Repository: slimtoolkit/slim Branch: master Commit: 2da84ff25aca Files: 5488 Total size: 59.5 MB Directory structure: gitextract_oai82ui8/ ├── .deepsource.toml ├── .dockerignore ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE.md │ ├── PULL_REQUEST_TEMPLATE.md │ ├── dependabot.yml │ └── workflows/ │ ├── acceptance-testing-bats.yml │ ├── acceptance-testing-e2e.yml │ ├── codesee-arch-diagram.yml │ └── unit-testing.yml ├── .gitignore ├── .gitmodules ├── .gitpod.yml ├── .markdownlint.json ├── .vscode/ │ └── launch.json ├── ADOPTERS.md ├── CHANGELOG.md ├── COMMUNITY_ACTIVITY_LOG.md ├── CONTRIBUTING.md ├── GOVERNANCE.md ├── LICENSE ├── MAINTAINERS.md ├── Makefile ├── README.md ├── ROADMAP.md ├── SECURITY.md ├── WISHLIST.md ├── assets/ │ └── images/ │ └── docs/ │ └── SlimHow.excalidraw ├── build/ │ └── package/ │ ├── docker/ │ │ ├── .dockerignore │ │ ├── .ds.container.d3e2c84f976743bdb92a7044ef12e381 │ │ ├── Dockerfile │ │ ├── Dockerfile.arm │ │ ├── build.sh │ │ ├── build_arm.sh │ │ ├── dockerhub_publish.sh │ │ ├── dockerhub_publish_arm.sh │ │ └── mac/ │ │ ├── build.command │ │ ├── build_arm.command │ │ ├── dockerhub_login.command │ │ ├── dockerhub_publish.command │ │ └── dockerhub_publish_arm.command │ └── rpm/ │ └── slim.spec ├── cmd/ │ ├── slim/ │ │ └── main.go │ └── slim-sensor/ │ └── main.go ├── examples/ │ ├── README.md │ └── k8s_nginx_cgr/ │ └── manifest.yaml ├── go.mod ├── go.sum ├── pkg/ │ ├── acounter/ │ │ └── acounter.go │ ├── aflag/ │ │ └── aflag.go │ ├── app/ │ │ ├── constants.go │ │ ├── execontext.go │ │ ├── master/ │ │ │ ├── app.go │ │ │ ├── builder/ │ │ │ │ └── image_builder.go │ │ │ ├── cli.go │ │ │ ├── command/ │ │ │ │ ├── appbom/ │ │ │ │ │ ├── cli.go │ │ │ │ │ ├── handler.go │ │ │ │ │ ├── init/ │ │ │ │ │ │ └── init.go │ │ │ │ │ ├── prompt.go │ │ │ │ │ └── register.go │ │ │ │ ├── build/ │ │ │ │ │ ├── cli.go │ │ │ │ │ ├── flags.go │ │ │ │ │ ├── handler.go │ │ │ │ │ ├── image.go │ │ │ │ │ ├── init/ │ │ │ │ │ │ └── init.go │ │ │ │ │ ├── kubernetes.go │ │ │ │ │ ├── prompt.go │ │ │ │ │ └── register.go │ │ │ │ ├── cliflags.go │ │ │ │ ├── clifvgetter.go │ │ │ │ ├── clifvparser.go │ │ │ │ ├── cliprompt.go │ │ │ │ ├── common.go │ │ │ │ ├── containerize/ │ │ │ │ │ ├── cli.go │ │ │ │ │ ├── handler.go │ │ │ │ │ ├── init/ │ │ │ │ │ │ └── init.go │ │ │ │ │ ├── prompt.go │ │ │ │ │ └── register.go │ │ │ │ ├── convert/ │ │ │ │ │ ├── cli.go │ │ │ │ │ ├── handler.go │ │ │ │ │ ├── init/ │ │ │ │ │ │ └── init.go │ │ │ │ │ ├── prompt.go │ │ │ │ │ └── register.go │ │ │ │ ├── debug/ │ │ │ │ │ ├── cli.go │ │ │ │ │ ├── debug_images.go │ │ │ │ │ ├── flags.go │ │ │ │ │ ├── handle_docker_runtime.go │ │ │ │ │ ├── handle_kubernetes_runtime.go │ │ │ │ │ ├── handler.go │ │ │ │ │ ├── init/ │ │ │ │ │ │ └── init.go │ │ │ │ │ ├── prompt.go │ │ │ │ │ ├── register.go │ │ │ │ │ ├── session.go │ │ │ │ │ └── shell.go │ │ │ │ ├── dockerclipm/ │ │ │ │ │ ├── cli.go │ │ │ │ │ └── register.go │ │ │ │ ├── edit/ │ │ │ │ │ ├── cli.go │ │ │ │ │ ├── handler.go │ │ │ │ │ ├── init/ │ │ │ │ │ │ └── init.go │ │ │ │ │ ├── prompt.go │ │ │ │ │ └── register.go │ │ │ │ ├── help/ │ │ │ │ │ ├── cli.go │ │ │ │ │ ├── init/ │ │ │ │ │ │ └── init.go │ │ │ │ │ ├── prompt.go │ │ │ │ │ └── register.go │ │ │ │ ├── images/ │ │ │ │ │ ├── cli.go │ │ │ │ │ ├── handler.go │ │ │ │ │ ├── init/ │ │ │ │ │ │ └── init.go │ │ │ │ │ ├── prompt.go │ │ │ │ │ └── register.go │ │ │ │ ├── install/ │ │ │ │ │ ├── cli.go │ │ │ │ │ ├── handler.go │ │ │ │ │ ├── init/ │ │ │ │ │ │ └── init.go │ │ │ │ │ ├── prompt.go │ │ │ │ │ └── register.go │ │ │ │ ├── lint/ │ │ │ │ │ ├── cli.go │ │ │ │ │ ├── flags.go │ │ │ │ │ ├── handler.go │ │ │ │ │ ├── init/ │ │ │ │ │ │ └── init.go │ │ │ │ │ ├── prompt.go │ │ │ │ │ └── register.go │ │ │ │ ├── merge/ │ │ │ │ │ ├── cli.go │ │ │ │ │ ├── flags.go │ │ │ │ │ ├── handler.go │ │ │ │ │ ├── init/ │ │ │ │ │ │ └── init.go │ │ │ │ │ ├── prompt.go │ │ │ │ │ └── register.go │ │ │ │ ├── probe/ │ │ │ │ │ ├── cli.go │ │ │ │ │ ├── flags.go │ │ │ │ │ ├── handler.go │ │ │ │ │ ├── init/ │ │ │ │ │ │ └── init.go │ │ │ │ │ ├── prompt.go │ │ │ │ │ └── register.go │ │ │ │ ├── profile/ │ │ │ │ │ ├── cli.go │ │ │ │ │ ├── handler.go │ │ │ │ │ ├── init/ │ │ │ │ │ │ └── init.go │ │ │ │ │ ├── prompt.go │ │ │ │ │ └── register.go │ │ │ │ ├── registry/ │ │ │ │ │ ├── auth.go │ │ │ │ │ ├── cli.go │ │ │ │ │ ├── flags.go │ │ │ │ │ ├── handler_copy.go │ │ │ │ │ ├── handler_image_index.go │ │ │ │ │ ├── handler_pull.go │ │ │ │ │ ├── handler_push.go │ │ │ │ │ ├── handler_server.go │ │ │ │ │ ├── init/ │ │ │ │ │ │ └── init.go │ │ │ │ │ ├── prompt.go │ │ │ │ │ └── register.go │ │ │ │ ├── run/ │ │ │ │ │ ├── cli.go │ │ │ │ │ ├── flags.go │ │ │ │ │ ├── handler.go │ │ │ │ │ ├── init/ │ │ │ │ │ │ └── init.go │ │ │ │ │ ├── prompt.go │ │ │ │ │ └── register.go │ │ │ │ ├── server/ │ │ │ │ │ ├── cli.go │ │ │ │ │ ├── handler.go │ │ │ │ │ ├── init/ │ │ │ │ │ │ └── init.go │ │ │ │ │ ├── prompt.go │ │ │ │ │ └── register.go │ │ │ │ ├── update/ │ │ │ │ │ ├── cli.go │ │ │ │ │ ├── handler.go │ │ │ │ │ ├── init/ │ │ │ │ │ │ └── init.go │ │ │ │ │ ├── prompt.go │ │ │ │ │ └── register.go │ │ │ │ ├── version/ │ │ │ │ │ ├── cli.go │ │ │ │ │ ├── handler.go │ │ │ │ │ ├── init/ │ │ │ │ │ │ └── init.go │ │ │ │ │ ├── prompt.go │ │ │ │ │ └── register.go │ │ │ │ ├── vulnerability/ │ │ │ │ │ ├── cli.go │ │ │ │ │ ├── flags.go │ │ │ │ │ ├── handler_tool_epss.go │ │ │ │ │ ├── init/ │ │ │ │ │ │ └── init.go │ │ │ │ │ ├── prompt.go │ │ │ │ │ └── register.go │ │ │ │ └── xray/ │ │ │ │ ├── cli.go │ │ │ │ ├── flags.go │ │ │ │ ├── handler.go │ │ │ │ ├── init/ │ │ │ │ │ └── init.go │ │ │ │ ├── prompt.go │ │ │ │ └── register.go │ │ │ ├── compose/ │ │ │ │ └── execution.go │ │ │ ├── config/ │ │ │ │ └── config.go │ │ │ ├── container/ │ │ │ │ └── execution.go │ │ │ ├── docker/ │ │ │ │ └── dockerhost/ │ │ │ │ └── host.go │ │ │ ├── inspectors/ │ │ │ │ ├── container/ │ │ │ │ │ ├── container_inspector.go │ │ │ │ │ └── container_startup.go │ │ │ │ ├── image/ │ │ │ │ │ ├── extract_registry_test.go │ │ │ │ │ └── image_inspector.go │ │ │ │ ├── ipc/ │ │ │ │ │ └── ipc.go │ │ │ │ ├── pod/ │ │ │ │ │ └── pod_inspector.go │ │ │ │ └── sensor/ │ │ │ │ └── sensor.go │ │ │ ├── kubernetes/ │ │ │ │ ├── client.go │ │ │ │ ├── kubectl.go │ │ │ │ ├── manifests.go │ │ │ │ └── workload.go │ │ │ ├── probe/ │ │ │ │ └── http/ │ │ │ │ ├── crawler.go │ │ │ │ ├── custom_probe.go │ │ │ │ ├── httpclient.go │ │ │ │ ├── internal/ │ │ │ │ │ ├── client.go │ │ │ │ │ └── fastcgi.go │ │ │ │ ├── swagger.go │ │ │ │ └── wsclient.go │ │ │ ├── security/ │ │ │ │ ├── apparmor/ │ │ │ │ │ └── apparmor.go │ │ │ │ └── seccomp/ │ │ │ │ └── seccomp.go │ │ │ ├── signals/ │ │ │ │ └── signals.go │ │ │ ├── update/ │ │ │ │ └── update.go │ │ │ └── version/ │ │ │ └── version.go │ │ └── sensor/ │ │ ├── app.go │ │ ├── app_test.go │ │ ├── artifact/ │ │ │ └── artifact.go │ │ ├── controlled/ │ │ │ ├── controlled.go │ │ │ └── controlled_test.go │ │ ├── detector/ │ │ │ ├── binfile/ │ │ │ │ └── binfile.go │ │ │ └── filetype/ │ │ │ └── filetype.go │ │ ├── execution/ │ │ │ ├── controlled.go │ │ │ ├── hook.go │ │ │ ├── interface.go │ │ │ └── standalone.go │ │ ├── inspector/ │ │ │ └── sodeps/ │ │ │ └── sodeps.go │ │ ├── ipc/ │ │ │ └── ipc.go │ │ ├── logger.go │ │ ├── monitor/ │ │ │ ├── composite.go │ │ │ ├── composite_test.go │ │ │ ├── fanotify/ │ │ │ │ └── monitor.go │ │ │ ├── pevent/ │ │ │ │ └── monitor.go │ │ │ └── ptrace/ │ │ │ ├── interface.go │ │ │ ├── monitor.go │ │ │ └── monitor_arm64.go │ │ ├── signals.go │ │ └── standalone/ │ │ ├── control/ │ │ │ ├── commands.go │ │ │ ├── queue.go │ │ │ ├── stop.go │ │ │ └── wait.go │ │ └── standalone.go │ ├── appbom/ │ │ ├── .gitignore │ │ ├── appbom.go │ │ ├── gobinhash.go │ │ ├── gobinhash_noembed.go │ │ └── gobinhasher.go │ ├── artifact/ │ │ └── artifact.go │ ├── certdiscover/ │ │ └── certdiscover.go │ ├── command/ │ │ └── command.go │ ├── consts/ │ │ ├── community.go │ │ ├── external.go │ │ └── version.go │ ├── docker/ │ │ ├── buildpackinfo/ │ │ │ └── buildpackinfo.go │ │ ├── dockerclient/ │ │ │ └── client.go │ │ ├── dockerfile/ │ │ │ ├── ast/ │ │ │ │ ├── line_parsers.go │ │ │ │ ├── parser.go │ │ │ │ └── split_command.go │ │ │ ├── dockerfile.go │ │ │ ├── parser/ │ │ │ │ └── parser.go │ │ │ ├── reverse/ │ │ │ │ ├── reverse.go │ │ │ │ └── reverse_test.go │ │ │ └── spec/ │ │ │ └── spec.go │ │ ├── dockerignore/ │ │ │ └── dockerignore.go │ │ ├── dockerimage/ │ │ │ ├── dockerimage.go │ │ │ ├── imagelayout.go │ │ │ ├── metadata.go │ │ │ ├── packagefiles.go │ │ │ └── topobjects.go │ │ ├── dockerutil/ │ │ │ └── dockerutil.go │ │ ├── instruction/ │ │ │ └── instruction.go │ │ └── linter/ │ │ ├── check/ │ │ │ ├── check.go │ │ │ ├── id10001.go │ │ │ ├── id10002.go │ │ │ ├── id20000.go │ │ │ ├── id20001.go │ │ │ ├── id20002.go │ │ │ ├── id20003.go │ │ │ ├── id20004.go │ │ │ ├── id20005.go │ │ │ ├── id20006.go │ │ │ ├── id20007.go │ │ │ ├── id20008.go │ │ │ ├── id20009.go │ │ │ ├── id20010.go │ │ │ ├── id20011.go │ │ │ ├── id20012.go │ │ │ ├── id20013.go │ │ │ ├── id20014.go │ │ │ ├── id20015.go │ │ │ ├── id20016.go │ │ │ ├── id20017.go │ │ │ ├── id20018.go │ │ │ ├── id20019.go │ │ │ ├── id20020.go │ │ │ ├── id20021.go │ │ │ └── id20022.go │ │ └── linter.go │ ├── errors/ │ │ └── errors.go │ ├── imagebuilder/ │ │ ├── imagebuilder.go │ │ └── internalbuilder/ │ │ └── engine.go │ ├── imagereader/ │ │ └── imagereader.go │ ├── ipc/ │ │ ├── channel/ │ │ │ └── channel.go │ │ ├── command/ │ │ │ └── command.go │ │ └── event/ │ │ └── event.go │ ├── lambdaproxy/ │ │ ├── lambdaproxy.go │ │ └── lambdaproxy_test.go │ ├── launcher/ │ │ └── launcher.go │ ├── mondel/ │ │ └── mondel.go │ ├── monitor/ │ │ └── ptrace/ │ │ ├── ptrace.go │ │ └── types.go │ ├── pdiscover/ │ │ ├── pevents.go │ │ ├── pevents_darwin.go │ │ ├── pevents_linux.go │ │ ├── pinfo.go │ │ ├── pinfo_darwin.go │ │ └── pinfo_linux.go │ ├── report/ │ │ ├── command_report.go │ │ ├── container_report.go │ │ ├── mondel_report.go │ │ └── report.go │ ├── sysenv/ │ │ ├── sysenv_darwin.go │ │ └── sysenv_linux.go │ ├── sysidentity/ │ │ └── sysidentity.go │ ├── system/ │ │ ├── architecture.go │ │ ├── errors.go │ │ ├── kernel_linux.go │ │ ├── nstring_int8.go │ │ ├── nstring_uint8.go │ │ ├── os_release.go │ │ ├── os_shells.go │ │ ├── syscalls.go │ │ ├── syscalls_armf32.go │ │ ├── syscalls_armf64.go │ │ ├── syscalls_x86f32.go │ │ ├── syscalls_x86f64.go │ │ ├── system.go │ │ ├── system_darwin.go │ │ ├── system_linux.go │ │ ├── system_linux_amd64.go │ │ ├── system_linux_arm.go │ │ └── system_linux_arm64.go │ ├── test/ │ │ ├── e2e/ │ │ │ └── sensor/ │ │ │ ├── docker.go │ │ │ ├── monitor.go │ │ │ └── sensor.go │ │ ├── stub/ │ │ │ └── sensor/ │ │ │ ├── execution/ │ │ │ │ └── execution.go │ │ │ └── monitor/ │ │ │ └── monitor.go │ │ └── util/ │ │ └── time.go │ ├── third_party/ │ │ ├── compose-go/ │ │ │ ├── .github/ │ │ │ │ ├── CODEOWNERS │ │ │ │ ├── dependabot.yml │ │ │ │ └── workflows/ │ │ │ │ ├── ci.yml │ │ │ │ └── release.yml │ │ │ ├── .gitignore │ │ │ ├── .pre-commit-config.yaml │ │ │ ├── CODE_OF_CONDUCT.md │ │ │ ├── CONTRIBUTING.md │ │ │ ├── LICENSE │ │ │ ├── MAINTENANCE.md │ │ │ ├── Makefile │ │ │ ├── NOTICE │ │ │ ├── README.md │ │ │ ├── ci/ │ │ │ │ └── Dockerfile │ │ │ ├── cli/ │ │ │ │ ├── options.go │ │ │ │ ├── options_test.go │ │ │ │ └── testdata/ │ │ │ │ ├── env-file/ │ │ │ │ │ ├── compose-with-env-file.yaml │ │ │ │ │ └── simple-env │ │ │ │ └── simple/ │ │ │ │ ├── compose-with-overrides.yaml │ │ │ │ ├── compose-with-variables.yaml │ │ │ │ └── compose.yaml │ │ │ ├── compatibility/ │ │ │ │ ├── allowlist.go │ │ │ │ ├── allowlist_test.go │ │ │ │ ├── build.go │ │ │ │ ├── checker.go │ │ │ │ ├── configs.go │ │ │ │ ├── deploy.go │ │ │ │ ├── networks.go │ │ │ │ ├── services.go │ │ │ │ └── volumes.go │ │ │ ├── errdefs/ │ │ │ │ └── errors.go │ │ │ ├── go.mod │ │ │ ├── go.sum │ │ │ ├── golangci.yml │ │ │ ├── interpolation/ │ │ │ │ ├── interpolation.go │ │ │ │ └── interpolation_test.go │ │ │ ├── loader/ │ │ │ │ ├── example1.env │ │ │ │ ├── example2.env │ │ │ │ ├── full-example.yml │ │ │ │ ├── full-struct_test.go │ │ │ │ ├── interpolate.go │ │ │ │ ├── loader.go │ │ │ │ ├── loader_test.go │ │ │ │ ├── merge.go │ │ │ │ ├── merge_test.go │ │ │ │ ├── normalize.go │ │ │ │ ├── normalize_test.go │ │ │ │ ├── testdata/ │ │ │ │ │ ├── Dockerfile │ │ │ │ │ ├── compose-test-extends-imported.yaml │ │ │ │ │ ├── compose-test-extends.yaml │ │ │ │ │ └── compose-test-with-version.yaml │ │ │ │ ├── types_test.go │ │ │ │ ├── validate.go │ │ │ │ ├── validate_test.go │ │ │ │ ├── volume.go │ │ │ │ ├── volume_test.go │ │ │ │ ├── windows_path.go │ │ │ │ ├── windows_path_test.go │ │ │ │ └── with-version-struct_test.go │ │ │ ├── schema/ │ │ │ │ ├── compose-spec.json │ │ │ │ ├── schema.go │ │ │ │ └── schema_test.go │ │ │ ├── scripts/ │ │ │ │ └── validate/ │ │ │ │ ├── fileheader │ │ │ │ └── template/ │ │ │ │ ├── bash.txt │ │ │ │ ├── dockerfile.txt │ │ │ │ ├── go.txt │ │ │ │ └── makefile.txt │ │ │ ├── template/ │ │ │ │ ├── template.go │ │ │ │ └── template_test.go │ │ │ └── types/ │ │ │ ├── config.go │ │ │ ├── config_test.go │ │ │ ├── project.go │ │ │ ├── project_test.go │ │ │ ├── types.go │ │ │ └── types_test.go │ │ ├── madmo/ │ │ │ └── fanotify/ │ │ │ ├── fanotify.go │ │ │ ├── fanotify_386.go │ │ │ ├── fanotify_amd64.go │ │ │ ├── fanotify_arm.go │ │ │ └── fanotify_arm64.go │ │ └── opencontainers/ │ │ └── specs/ │ │ └── seccomp.go │ ├── util/ │ │ ├── errutil/ │ │ │ └── errutil.go │ │ ├── fsutil/ │ │ │ ├── fsutil.go │ │ │ ├── sysstat.go │ │ │ ├── sysstat_darwin.go │ │ │ └── sysstat_linux.go │ │ ├── jsonutil/ │ │ │ └── jsonutil.go │ │ └── printbuffer/ │ │ └── printbuffer.go │ ├── version/ │ │ └── version.go │ └── vulnerability/ │ └── epss/ │ ├── api/ │ │ └── api.go │ ├── client/ │ │ └── client.go │ └── data.go ├── scripts/ │ ├── docker-builder-m1.run.sh │ ├── docker-builder.run.sh │ ├── govulncheck.sh │ ├── install-slim.sh │ ├── mac/ │ │ ├── bom.gen.command │ │ ├── docker-builder-m1.run.command │ │ ├── docker-builder.run.command │ │ ├── govulncheck.command │ │ ├── src.build.command │ │ ├── src.cleanup.command │ │ ├── src.fmt.command │ │ ├── src.inspect.command │ │ ├── src.test.command │ │ └── tools.get.command │ ├── src.build.m1.sh │ ├── src.build.quick.sh │ ├── src.build.sh │ ├── src.cleanup.sh │ ├── src.fmt.sh │ ├── src.inspect.sh │ ├── src.test.sh │ ├── tools.get.sh │ └── uninstall-slim.sh ├── test/ │ ├── debug.bats │ └── e2e-tests.mk └── vendor/ ├── github.com/ │ ├── Azure/ │ │ └── go-ansiterm/ │ │ ├── LICENSE │ │ ├── README.md │ │ ├── SECURITY.md │ │ ├── constants.go │ │ ├── context.go │ │ ├── csi_entry_state.go │ │ ├── csi_param_state.go │ │ ├── escape_intermediate_state.go │ │ ├── escape_state.go │ │ ├── event_handler.go │ │ ├── ground_state.go │ │ ├── osc_string_state.go │ │ ├── parser.go │ │ ├── parser_action_helpers.go │ │ ├── parser_actions.go │ │ ├── states.go │ │ ├── utilities.go │ │ └── winterm/ │ │ ├── ansi.go │ │ ├── api.go │ │ ├── attr_translation.go │ │ ├── cursor_helpers.go │ │ ├── erase_helpers.go │ │ ├── scroll_helper.go │ │ ├── utilities.go │ │ └── win_event_handler.go │ ├── Microsoft/ │ │ └── go-winio/ │ │ ├── .gitattributes │ │ ├── .gitignore │ │ ├── .golangci.yml │ │ ├── CODEOWNERS │ │ ├── LICENSE │ │ ├── README.md │ │ ├── SECURITY.md │ │ ├── backup.go │ │ ├── doc.go │ │ ├── ea.go │ │ ├── file.go │ │ ├── fileinfo.go │ │ ├── hvsock.go │ │ ├── internal/ │ │ │ ├── fs/ │ │ │ │ ├── doc.go │ │ │ │ ├── fs.go │ │ │ │ ├── security.go │ │ │ │ └── zsyscall_windows.go │ │ │ ├── socket/ │ │ │ │ ├── rawaddr.go │ │ │ │ ├── socket.go │ │ │ │ └── zsyscall_windows.go │ │ │ └── stringbuffer/ │ │ │ └── wstring.go │ │ ├── pipe.go │ │ ├── pkg/ │ │ │ └── guid/ │ │ │ ├── guid.go │ │ │ ├── guid_nonwindows.go │ │ │ ├── guid_windows.go │ │ │ └── variant_string.go │ │ ├── privilege.go │ │ ├── reparse.go │ │ ├── sd.go │ │ ├── syscall.go │ │ ├── tools.go │ │ └── zsyscall_windows.go │ ├── PuerkitoBio/ │ │ └── goquery/ │ │ ├── .gitattributes │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── array.go │ │ ├── doc.go │ │ ├── expand.go │ │ ├── filter.go │ │ ├── iteration.go │ │ ├── manipulation.go │ │ ├── property.go │ │ ├── query.go │ │ ├── traversal.go │ │ ├── type.go │ │ └── utilities.go │ ├── andybalholm/ │ │ └── cascadia/ │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ ├── parser.go │ │ ├── pseudo_classes.go │ │ ├── selector.go │ │ ├── serialize.go │ │ └── specificity.go │ ├── antchfx/ │ │ ├── htmlquery/ │ │ │ ├── .gitignore │ │ │ ├── .travis.yml │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── cache.go │ │ │ └── query.go │ │ ├── xmlquery/ │ │ │ ├── .gitignore │ │ │ ├── .travis.yml │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── books.xml │ │ │ ├── cache.go │ │ │ ├── cached_reader.go │ │ │ ├── node.go │ │ │ ├── options.go │ │ │ ├── parse.go │ │ │ └── query.go │ │ └── xpath/ │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ ├── build.go │ │ ├── cache.go │ │ ├── func.go │ │ ├── func_go110.go │ │ ├── func_pre_go110.go │ │ ├── operator.go │ │ ├── parse.go │ │ ├── query.go │ │ └── xpath.go │ ├── armon/ │ │ └── go-radix/ │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ └── radix.go │ ├── bmatcuk/ │ │ └── doublestar/ │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ ├── doublestar.go │ │ └── v3/ │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ ├── UPGRADING.md │ │ └── doublestar.go │ ├── c4milo/ │ │ └── unpackit/ │ │ ├── .editorconfig │ │ ├── .travis.yml │ │ ├── AUTHORS │ │ ├── CONTRIBUTING.md │ │ ├── LICENSE │ │ ├── README.md │ │ └── unpackit.go │ ├── cespare/ │ │ └── xxhash/ │ │ └── v2/ │ │ ├── LICENSE.txt │ │ ├── README.md │ │ ├── testall.sh │ │ ├── xxhash.go │ │ ├── xxhash_amd64.s │ │ ├── xxhash_arm64.s │ │ ├── xxhash_asm.go │ │ ├── xxhash_other.go │ │ ├── xxhash_safe.go │ │ └── xxhash_unsafe.go │ ├── compose-spec/ │ │ └── compose-go/ │ │ ├── LICENSE │ │ ├── NOTICE │ │ ├── errdefs/ │ │ │ └── errors.go │ │ ├── interpolation/ │ │ │ └── interpolation.go │ │ ├── loader/ │ │ │ ├── example1.env │ │ │ ├── example2.env │ │ │ ├── full-example.yml │ │ │ ├── interpolate.go │ │ │ ├── loader.go │ │ │ ├── merge.go │ │ │ ├── normalize.go │ │ │ ├── validate.go │ │ │ ├── volume.go │ │ │ └── windows_path.go │ │ ├── template/ │ │ │ └── template.go │ │ └── types/ │ │ ├── config.go │ │ ├── project.go │ │ └── types.go │ ├── containerd/ │ │ ├── containerd/ │ │ │ ├── LICENSE │ │ │ ├── NOTICE │ │ │ └── pkg/ │ │ │ └── userns/ │ │ │ ├── userns_linux.go │ │ │ └── userns_unsupported.go │ │ ├── log/ │ │ │ ├── .golangci.yml │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ └── context.go │ │ └── stargz-snapshotter/ │ │ └── estargz/ │ │ ├── LICENSE │ │ ├── build.go │ │ ├── errorutil/ │ │ │ └── errors.go │ │ ├── estargz.go │ │ ├── gzip.go │ │ ├── testutil.go │ │ └── types.go │ ├── cpuguy83/ │ │ └── go-md2man/ │ │ └── v2/ │ │ ├── LICENSE.md │ │ └── md2man/ │ │ ├── md2man.go │ │ └── roff.go │ ├── davecgh/ │ │ └── go-spew/ │ │ ├── LICENSE │ │ └── spew/ │ │ ├── bypass.go │ │ ├── bypasssafe.go │ │ ├── common.go │ │ ├── config.go │ │ ├── doc.go │ │ ├── dump.go │ │ ├── format.go │ │ └── spew.go │ ├── distribution/ │ │ ├── distribution/ │ │ │ └── v3/ │ │ │ ├── LICENSE │ │ │ ├── digestset/ │ │ │ │ └── set.go │ │ │ └── reference/ │ │ │ ├── helpers.go │ │ │ ├── normalize.go │ │ │ ├── reference.go │ │ │ └── regexp.go │ │ └── reference/ │ │ ├── .gitattributes │ │ ├── .gitignore │ │ ├── .golangci.yml │ │ ├── CODE-OF-CONDUCT.md │ │ ├── CONTRIBUTING.md │ │ ├── GOVERNANCE.md │ │ ├── LICENSE │ │ ├── MAINTAINERS │ │ ├── Makefile │ │ ├── README.md │ │ ├── SECURITY.md │ │ ├── helpers.go │ │ ├── normalize.go │ │ ├── reference.go │ │ ├── regexp.go │ │ └── sort.go │ ├── docker/ │ │ ├── cli/ │ │ │ ├── AUTHORS │ │ │ ├── LICENSE │ │ │ ├── NOTICE │ │ │ └── cli/ │ │ │ └── config/ │ │ │ ├── config.go │ │ │ ├── configfile/ │ │ │ │ ├── file.go │ │ │ │ ├── file_unix.go │ │ │ │ └── file_windows.go │ │ │ ├── credentials/ │ │ │ │ ├── credentials.go │ │ │ │ ├── default_store.go │ │ │ │ ├── default_store_darwin.go │ │ │ │ ├── default_store_linux.go │ │ │ │ ├── default_store_unsupported.go │ │ │ │ ├── default_store_windows.go │ │ │ │ ├── file_store.go │ │ │ │ └── native_store.go │ │ │ └── types/ │ │ │ └── authconfig.go │ │ ├── distribution/ │ │ │ ├── LICENSE │ │ │ └── registry/ │ │ │ └── client/ │ │ │ └── auth/ │ │ │ └── challenge/ │ │ │ ├── addr.go │ │ │ └── authchallenge.go │ │ ├── docker/ │ │ │ ├── AUTHORS │ │ │ ├── LICENSE │ │ │ ├── NOTICE │ │ │ ├── api/ │ │ │ │ ├── README.md │ │ │ │ ├── common.go │ │ │ │ ├── swagger-gen.yaml │ │ │ │ ├── swagger.yaml │ │ │ │ └── types/ │ │ │ │ ├── blkiodev/ │ │ │ │ │ └── blkio.go │ │ │ │ ├── checkpoint/ │ │ │ │ │ ├── list.go │ │ │ │ │ └── options.go │ │ │ │ ├── client.go │ │ │ │ ├── configs.go │ │ │ │ ├── container/ │ │ │ │ │ ├── change_type.go │ │ │ │ │ ├── change_types.go │ │ │ │ │ ├── config.go │ │ │ │ │ ├── container_top.go │ │ │ │ │ ├── container_update.go │ │ │ │ │ ├── create_response.go │ │ │ │ │ ├── errors.go │ │ │ │ │ ├── filesystem_change.go │ │ │ │ │ ├── hostconfig.go │ │ │ │ │ ├── hostconfig_unix.go │ │ │ │ │ ├── hostconfig_windows.go │ │ │ │ │ ├── options.go │ │ │ │ │ ├── wait_exit_error.go │ │ │ │ │ ├── wait_response.go │ │ │ │ │ └── waitcondition.go │ │ │ │ ├── error_response.go │ │ │ │ ├── error_response_ext.go │ │ │ │ ├── events/ │ │ │ │ │ └── events.go │ │ │ │ ├── filters/ │ │ │ │ │ ├── errors.go │ │ │ │ │ └── parse.go │ │ │ │ ├── graph_driver_data.go │ │ │ │ ├── id_response.go │ │ │ │ ├── image/ │ │ │ │ │ ├── delete_response.go │ │ │ │ │ ├── image.go │ │ │ │ │ ├── image_history.go │ │ │ │ │ ├── opts.go │ │ │ │ │ └── summary.go │ │ │ │ ├── mount/ │ │ │ │ │ └── mount.go │ │ │ │ ├── network/ │ │ │ │ │ ├── endpoint.go │ │ │ │ │ ├── ipam.go │ │ │ │ │ └── network.go │ │ │ │ ├── plugin.go │ │ │ │ ├── plugin_device.go │ │ │ │ ├── plugin_env.go │ │ │ │ ├── plugin_interface_type.go │ │ │ │ ├── plugin_mount.go │ │ │ │ ├── plugin_responses.go │ │ │ │ ├── port.go │ │ │ │ ├── registry/ │ │ │ │ │ ├── authconfig.go │ │ │ │ │ ├── authenticate.go │ │ │ │ │ └── registry.go │ │ │ │ ├── stats.go │ │ │ │ ├── strslice/ │ │ │ │ │ └── strslice.go │ │ │ │ ├── swarm/ │ │ │ │ │ ├── common.go │ │ │ │ │ ├── config.go │ │ │ │ │ ├── container.go │ │ │ │ │ ├── network.go │ │ │ │ │ ├── node.go │ │ │ │ │ ├── runtime/ │ │ │ │ │ │ ├── gen.go │ │ │ │ │ │ ├── plugin.pb.go │ │ │ │ │ │ └── plugin.proto │ │ │ │ │ ├── runtime.go │ │ │ │ │ ├── secret.go │ │ │ │ │ ├── service.go │ │ │ │ │ ├── service_create_response.go │ │ │ │ │ ├── service_update_response.go │ │ │ │ │ ├── swarm.go │ │ │ │ │ └── task.go │ │ │ │ ├── system/ │ │ │ │ │ ├── info.go │ │ │ │ │ ├── runtime.go │ │ │ │ │ └── security_opts.go │ │ │ │ ├── time/ │ │ │ │ │ └── timestamp.go │ │ │ │ ├── types.go │ │ │ │ ├── types_deprecated.go │ │ │ │ ├── versions/ │ │ │ │ │ ├── README.md │ │ │ │ │ └── compare.go │ │ │ │ └── volume/ │ │ │ │ ├── cluster_volume.go │ │ │ │ ├── create_options.go │ │ │ │ ├── list_response.go │ │ │ │ ├── options.go │ │ │ │ ├── volume.go │ │ │ │ └── volume_update.go │ │ │ ├── client/ │ │ │ │ ├── README.md │ │ │ │ ├── build_cancel.go │ │ │ │ ├── build_prune.go │ │ │ │ ├── checkpoint_create.go │ │ │ │ ├── checkpoint_delete.go │ │ │ │ ├── checkpoint_list.go │ │ │ │ ├── client.go │ │ │ │ ├── client_deprecated.go │ │ │ │ ├── client_unix.go │ │ │ │ ├── client_windows.go │ │ │ │ ├── config_create.go │ │ │ │ ├── config_inspect.go │ │ │ │ ├── config_list.go │ │ │ │ ├── config_remove.go │ │ │ │ ├── config_update.go │ │ │ │ ├── container_attach.go │ │ │ │ ├── container_commit.go │ │ │ │ ├── container_copy.go │ │ │ │ ├── container_create.go │ │ │ │ ├── container_diff.go │ │ │ │ ├── container_exec.go │ │ │ │ ├── container_export.go │ │ │ │ ├── container_inspect.go │ │ │ │ ├── container_kill.go │ │ │ │ ├── container_list.go │ │ │ │ ├── container_logs.go │ │ │ │ ├── container_pause.go │ │ │ │ ├── container_prune.go │ │ │ │ ├── container_remove.go │ │ │ │ ├── container_rename.go │ │ │ │ ├── container_resize.go │ │ │ │ ├── container_restart.go │ │ │ │ ├── container_start.go │ │ │ │ ├── container_stats.go │ │ │ │ ├── container_stop.go │ │ │ │ ├── container_top.go │ │ │ │ ├── container_unpause.go │ │ │ │ ├── container_update.go │ │ │ │ ├── container_wait.go │ │ │ │ ├── disk_usage.go │ │ │ │ ├── distribution_inspect.go │ │ │ │ ├── envvars.go │ │ │ │ ├── errors.go │ │ │ │ ├── events.go │ │ │ │ ├── hijack.go │ │ │ │ ├── image_build.go │ │ │ │ ├── image_create.go │ │ │ │ ├── image_history.go │ │ │ │ ├── image_import.go │ │ │ │ ├── image_inspect.go │ │ │ │ ├── image_list.go │ │ │ │ ├── image_load.go │ │ │ │ ├── image_prune.go │ │ │ │ ├── image_pull.go │ │ │ │ ├── image_push.go │ │ │ │ ├── image_remove.go │ │ │ │ ├── image_save.go │ │ │ │ ├── image_search.go │ │ │ │ ├── image_tag.go │ │ │ │ ├── info.go │ │ │ │ ├── interface.go │ │ │ │ ├── interface_experimental.go │ │ │ │ ├── interface_stable.go │ │ │ │ ├── login.go │ │ │ │ ├── network_connect.go │ │ │ │ ├── network_create.go │ │ │ │ ├── network_disconnect.go │ │ │ │ ├── network_inspect.go │ │ │ │ ├── network_list.go │ │ │ │ ├── network_prune.go │ │ │ │ ├── network_remove.go │ │ │ │ ├── node_inspect.go │ │ │ │ ├── node_list.go │ │ │ │ ├── node_remove.go │ │ │ │ ├── node_update.go │ │ │ │ ├── options.go │ │ │ │ ├── ping.go │ │ │ │ ├── plugin_create.go │ │ │ │ ├── plugin_disable.go │ │ │ │ ├── plugin_enable.go │ │ │ │ ├── plugin_inspect.go │ │ │ │ ├── plugin_install.go │ │ │ │ ├── plugin_list.go │ │ │ │ ├── plugin_push.go │ │ │ │ ├── plugin_remove.go │ │ │ │ ├── plugin_set.go │ │ │ │ ├── plugin_upgrade.go │ │ │ │ ├── request.go │ │ │ │ ├── secret_create.go │ │ │ │ ├── secret_inspect.go │ │ │ │ ├── secret_list.go │ │ │ │ ├── secret_remove.go │ │ │ │ ├── secret_update.go │ │ │ │ ├── service_create.go │ │ │ │ ├── service_inspect.go │ │ │ │ ├── service_list.go │ │ │ │ ├── service_logs.go │ │ │ │ ├── service_remove.go │ │ │ │ ├── service_update.go │ │ │ │ ├── swarm_get_unlock_key.go │ │ │ │ ├── swarm_init.go │ │ │ │ ├── swarm_inspect.go │ │ │ │ ├── swarm_join.go │ │ │ │ ├── swarm_leave.go │ │ │ │ ├── swarm_unlock.go │ │ │ │ ├── swarm_update.go │ │ │ │ ├── task_inspect.go │ │ │ │ ├── task_list.go │ │ │ │ ├── task_logs.go │ │ │ │ ├── utils.go │ │ │ │ ├── version.go │ │ │ │ ├── volume_create.go │ │ │ │ ├── volume_inspect.go │ │ │ │ ├── volume_list.go │ │ │ │ ├── volume_prune.go │ │ │ │ ├── volume_remove.go │ │ │ │ └── volume_update.go │ │ │ ├── errdefs/ │ │ │ │ ├── defs.go │ │ │ │ ├── doc.go │ │ │ │ ├── helpers.go │ │ │ │ ├── http_helpers.go │ │ │ │ └── is.go │ │ │ ├── image/ │ │ │ │ └── spec/ │ │ │ │ └── specs-go/ │ │ │ │ └── v1/ │ │ │ │ └── image.go │ │ │ ├── internal/ │ │ │ │ └── multierror/ │ │ │ │ └── multierror.go │ │ │ └── pkg/ │ │ │ ├── archive/ │ │ │ │ ├── archive.go │ │ │ │ ├── archive_linux.go │ │ │ │ ├── archive_other.go │ │ │ │ ├── archive_unix.go │ │ │ │ ├── archive_windows.go │ │ │ │ ├── changes.go │ │ │ │ ├── changes_linux.go │ │ │ │ ├── changes_other.go │ │ │ │ ├── changes_unix.go │ │ │ │ ├── changes_windows.go │ │ │ │ ├── copy.go │ │ │ │ ├── copy_unix.go │ │ │ │ ├── copy_windows.go │ │ │ │ ├── diff.go │ │ │ │ ├── diff_unix.go │ │ │ │ ├── diff_windows.go │ │ │ │ ├── path.go │ │ │ │ ├── path_unix.go │ │ │ │ ├── path_windows.go │ │ │ │ ├── time_linux.go │ │ │ │ ├── time_unsupported.go │ │ │ │ ├── whiteouts.go │ │ │ │ └── wrap.go │ │ │ ├── homedir/ │ │ │ │ ├── homedir.go │ │ │ │ ├── homedir_linux.go │ │ │ │ ├── homedir_others.go │ │ │ │ ├── homedir_unix.go │ │ │ │ └── homedir_windows.go │ │ │ ├── idtools/ │ │ │ │ ├── idtools.go │ │ │ │ ├── idtools_unix.go │ │ │ │ ├── idtools_windows.go │ │ │ │ ├── usergroupadd_linux.go │ │ │ │ ├── usergroupadd_unsupported.go │ │ │ │ └── utils_unix.go │ │ │ ├── ioutils/ │ │ │ │ ├── buffer.go │ │ │ │ ├── bytespipe.go │ │ │ │ ├── fswriters.go │ │ │ │ ├── readers.go │ │ │ │ ├── writeflusher.go │ │ │ │ └── writers.go │ │ │ ├── jsonmessage/ │ │ │ │ └── jsonmessage.go │ │ │ ├── longpath/ │ │ │ │ └── longpath.go │ │ │ ├── pools/ │ │ │ │ └── pools.go │ │ │ ├── stdcopy/ │ │ │ │ └── stdcopy.go │ │ │ └── system/ │ │ │ ├── args_windows.go │ │ │ ├── chtimes.go │ │ │ ├── chtimes_nowindows.go │ │ │ ├── chtimes_windows.go │ │ │ ├── errors.go │ │ │ ├── filesys.go │ │ │ ├── filesys_unix.go │ │ │ ├── filesys_windows.go │ │ │ ├── image_os_deprecated.go │ │ │ ├── init_windows.go │ │ │ ├── lstat_unix.go │ │ │ ├── lstat_windows.go │ │ │ ├── mknod.go │ │ │ ├── mknod_freebsd.go │ │ │ ├── mknod_unix.go │ │ │ ├── stat_bsd.go │ │ │ ├── stat_darwin.go │ │ │ ├── stat_linux.go │ │ │ ├── stat_openbsd.go │ │ │ ├── stat_unix.go │ │ │ ├── stat_windows.go │ │ │ ├── utimes_unix.go │ │ │ ├── utimes_unsupported.go │ │ │ ├── xattrs.go │ │ │ ├── xattrs_linux.go │ │ │ └── xattrs_unsupported.go │ │ ├── docker-credential-helpers/ │ │ │ ├── LICENSE │ │ │ ├── client/ │ │ │ │ ├── client.go │ │ │ │ └── command.go │ │ │ └── credentials/ │ │ │ ├── credentials.go │ │ │ ├── error.go │ │ │ ├── helper.go │ │ │ └── version.go │ │ ├── go-connections/ │ │ │ ├── LICENSE │ │ │ ├── nat/ │ │ │ │ ├── nat.go │ │ │ │ ├── parse.go │ │ │ │ └── sort.go │ │ │ ├── sockets/ │ │ │ │ ├── README.md │ │ │ │ ├── inmem_socket.go │ │ │ │ ├── proxy.go │ │ │ │ ├── sockets.go │ │ │ │ ├── sockets_unix.go │ │ │ │ ├── sockets_windows.go │ │ │ │ ├── tcp_socket.go │ │ │ │ └── unix_socket.go │ │ │ └── tlsconfig/ │ │ │ ├── certpool_go17.go │ │ │ ├── certpool_other.go │ │ │ ├── config.go │ │ │ ├── config_client_ciphers.go │ │ │ └── config_legacy_client_ciphers.go │ │ └── go-units/ │ │ ├── CONTRIBUTING.md │ │ ├── LICENSE │ │ ├── MAINTAINERS │ │ ├── README.md │ │ ├── circle.yml │ │ ├── duration.go │ │ ├── size.go │ │ └── ulimit.go │ ├── dsnet/ │ │ └── compress/ │ │ ├── .travis.yml │ │ ├── LICENSE.md │ │ ├── README.md │ │ ├── api.go │ │ ├── bzip2/ │ │ │ ├── bwt.go │ │ │ ├── common.go │ │ │ ├── fuzz_off.go │ │ │ ├── fuzz_on.go │ │ │ ├── internal/ │ │ │ │ └── sais/ │ │ │ │ ├── common.go │ │ │ │ ├── sais_byte.go │ │ │ │ └── sais_int.go │ │ │ ├── mtf_rle2.go │ │ │ ├── prefix.go │ │ │ ├── reader.go │ │ │ ├── rle1.go │ │ │ └── writer.go │ │ ├── internal/ │ │ │ ├── common.go │ │ │ ├── debug.go │ │ │ ├── errors/ │ │ │ │ └── errors.go │ │ │ ├── gofuzz.go │ │ │ ├── prefix/ │ │ │ │ ├── debug.go │ │ │ │ ├── decoder.go │ │ │ │ ├── encoder.go │ │ │ │ ├── prefix.go │ │ │ │ ├── range.go │ │ │ │ ├── reader.go │ │ │ │ ├── wrap.go │ │ │ │ └── writer.go │ │ │ └── release.go │ │ ├── zbench.sh │ │ ├── zfuzz.sh │ │ ├── zprof.sh │ │ └── ztest.sh │ ├── dustin/ │ │ └── go-humanize/ │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.markdown │ │ ├── big.go │ │ ├── bigbytes.go │ │ ├── bytes.go │ │ ├── comma.go │ │ ├── commaf.go │ │ ├── ftoa.go │ │ ├── humanize.go │ │ ├── number.go │ │ ├── ordinals.go │ │ ├── si.go │ │ └── times.go │ ├── emicklei/ │ │ └── go-restful/ │ │ └── v3/ │ │ ├── .gitignore │ │ ├── .goconvey │ │ ├── .travis.yml │ │ ├── CHANGES.md │ │ ├── LICENSE │ │ ├── Makefile │ │ ├── README.md │ │ ├── SECURITY.md │ │ ├── Srcfile │ │ ├── bench_test.sh │ │ ├── compress.go │ │ ├── compressor_cache.go │ │ ├── compressor_pools.go │ │ ├── compressors.go │ │ ├── constants.go │ │ ├── container.go │ │ ├── cors_filter.go │ │ ├── coverage.sh │ │ ├── curly.go │ │ ├── curly_route.go │ │ ├── custom_verb.go │ │ ├── doc.go │ │ ├── entity_accessors.go │ │ ├── extensions.go │ │ ├── filter.go │ │ ├── filter_adapter.go │ │ ├── json.go │ │ ├── jsoniter.go │ │ ├── jsr311.go │ │ ├── log/ │ │ │ └── log.go │ │ ├── logger.go │ │ ├── mime.go │ │ ├── options_filter.go │ │ ├── parameter.go │ │ ├── path_expression.go │ │ ├── path_processor.go │ │ ├── request.go │ │ ├── response.go │ │ ├── route.go │ │ ├── route_builder.go │ │ ├── route_reader.go │ │ ├── router.go │ │ ├── service_error.go │ │ ├── web_service.go │ │ └── web_service_container.go │ ├── evanphx/ │ │ └── json-patch/ │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── errors.go │ │ ├── merge.go │ │ └── patch.go │ ├── fatih/ │ │ └── color/ │ │ ├── LICENSE.md │ │ ├── README.md │ │ ├── color.go │ │ └── doc.go │ ├── felixge/ │ │ └── httpsnoop/ │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── LICENSE.txt │ │ ├── Makefile │ │ ├── README.md │ │ ├── capture_metrics.go │ │ ├── docs.go │ │ ├── wrap_generated_gteq_1.8.go │ │ └── wrap_generated_lt_1.8.go │ ├── fsouza/ │ │ └── go-dockerclient/ │ │ ├── .gitattributes │ │ ├── .gitignore │ │ ├── .golangci.yaml │ │ ├── DOCKER-LICENSE │ │ ├── LICENSE │ │ ├── Makefile │ │ ├── README.md │ │ ├── auth.go │ │ ├── change.go │ │ ├── client.go │ │ ├── client_unix.go │ │ ├── client_windows.go │ │ ├── container.go │ │ ├── container_archive.go │ │ ├── container_attach.go │ │ ├── container_changes.go │ │ ├── container_commit.go │ │ ├── container_copy.go │ │ ├── container_create.go │ │ ├── container_export.go │ │ ├── container_inspect.go │ │ ├── container_kill.go │ │ ├── container_list.go │ │ ├── container_logs.go │ │ ├── container_pause.go │ │ ├── container_prune.go │ │ ├── container_remove.go │ │ ├── container_rename.go │ │ ├── container_resize.go │ │ ├── container_restart.go │ │ ├── container_start.go │ │ ├── container_stats.go │ │ ├── container_stop.go │ │ ├── container_top.go │ │ ├── container_unpause.go │ │ ├── container_update.go │ │ ├── container_wait.go │ │ ├── distribution.go │ │ ├── env.go │ │ ├── event.go │ │ ├── exec.go │ │ ├── image.go │ │ ├── misc.go │ │ ├── network.go │ │ ├── plugin.go │ │ ├── registry_auth.go │ │ ├── signal.go │ │ ├── swarm.go │ │ ├── swarm_configs.go │ │ ├── swarm_node.go │ │ ├── swarm_secrets.go │ │ ├── swarm_service.go │ │ ├── swarm_task.go │ │ ├── system.go │ │ ├── tar.go │ │ ├── tls.go │ │ └── volume.go │ ├── getkin/ │ │ └── kin-openapi/ │ │ ├── LICENSE │ │ ├── openapi2/ │ │ │ ├── doc.go │ │ │ ├── header.go │ │ │ ├── helpers.go │ │ │ ├── marsh.go │ │ │ ├── openapi2.go │ │ │ ├── operation.go │ │ │ ├── parameter.go │ │ │ ├── path_item.go │ │ │ ├── ref.go │ │ │ ├── refs.go │ │ │ ├── response.go │ │ │ ├── schema.go │ │ │ └── security_scheme.go │ │ ├── openapi2conv/ │ │ │ ├── doc.go │ │ │ └── openapi2_conv.go │ │ └── openapi3/ │ │ ├── callback.go │ │ ├── components.go │ │ ├── contact.go │ │ ├── content.go │ │ ├── discriminator.go │ │ ├── doc.go │ │ ├── encoding.go │ │ ├── errors.go │ │ ├── example.go │ │ ├── example_validation.go │ │ ├── extension.go │ │ ├── external_docs.go │ │ ├── header.go │ │ ├── helpers.go │ │ ├── info.go │ │ ├── internalize_refs.go │ │ ├── license.go │ │ ├── link.go │ │ ├── loader.go │ │ ├── loader_uri_reader.go │ │ ├── maplike.go │ │ ├── marsh.go │ │ ├── media_type.go │ │ ├── openapi3.go │ │ ├── operation.go │ │ ├── origin.go │ │ ├── parameter.go │ │ ├── path_item.go │ │ ├── paths.go │ │ ├── ref.go │ │ ├── refs.go │ │ ├── refs.tmpl │ │ ├── refs_test.tmpl │ │ ├── request_body.go │ │ ├── response.go │ │ ├── schema.go │ │ ├── schema_formats.go │ │ ├── schema_pattern.go │ │ ├── schema_validation_settings.go │ │ ├── security_requirements.go │ │ ├── security_scheme.go │ │ ├── serialization_method.go │ │ ├── server.go │ │ ├── stringmap.go │ │ ├── tag.go │ │ ├── validation_options.go │ │ ├── visited.go │ │ └── xml.go │ ├── ghodss/ │ │ └── yaml/ │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ ├── fields.go │ │ └── yaml.go │ ├── go-errors/ │ │ └── errors/ │ │ ├── .travis.yml │ │ ├── LICENSE.MIT │ │ ├── README.md │ │ ├── error.go │ │ ├── error_1_13.go │ │ ├── error_backward.go │ │ ├── parse_panic.go │ │ └── stackframe.go │ ├── go-logr/ │ │ ├── logr/ │ │ │ ├── .golangci.yaml │ │ │ ├── CHANGELOG.md │ │ │ ├── CONTRIBUTING.md │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── discard.go │ │ │ ├── funcr/ │ │ │ │ └── funcr.go │ │ │ └── logr.go │ │ └── stdr/ │ │ ├── LICENSE │ │ ├── README.md │ │ └── stdr.go │ ├── go-openapi/ │ │ ├── jsonpointer/ │ │ │ ├── .editorconfig │ │ │ ├── .gitignore │ │ │ ├── .golangci.yml │ │ │ ├── CODE_OF_CONDUCT.md │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ └── pointer.go │ │ └── jsonreference/ │ │ ├── .gitignore │ │ ├── .golangci.yml │ │ ├── CODE_OF_CONDUCT.md │ │ ├── LICENSE │ │ ├── README.md │ │ ├── internal/ │ │ │ └── normalize_url.go │ │ └── reference.go │ ├── gobwas/ │ │ └── glob/ │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── bench.sh │ │ ├── compiler/ │ │ │ └── compiler.go │ │ ├── glob.go │ │ ├── match/ │ │ │ ├── any.go │ │ │ ├── any_of.go │ │ │ ├── btree.go │ │ │ ├── contains.go │ │ │ ├── every_of.go │ │ │ ├── list.go │ │ │ ├── match.go │ │ │ ├── max.go │ │ │ ├── min.go │ │ │ ├── nothing.go │ │ │ ├── prefix.go │ │ │ ├── prefix_any.go │ │ │ ├── prefix_suffix.go │ │ │ ├── range.go │ │ │ ├── row.go │ │ │ ├── segments.go │ │ │ ├── single.go │ │ │ ├── suffix.go │ │ │ ├── suffix_any.go │ │ │ ├── super.go │ │ │ └── text.go │ │ ├── readme.md │ │ ├── syntax/ │ │ │ ├── ast/ │ │ │ │ ├── ast.go │ │ │ │ └── parser.go │ │ │ ├── lexer/ │ │ │ │ ├── lexer.go │ │ │ │ └── token.go │ │ │ └── syntax.go │ │ └── util/ │ │ ├── runes/ │ │ │ └── runes.go │ │ └── strings/ │ │ └── strings.go │ ├── gocolly/ │ │ └── colly/ │ │ └── v2/ │ │ ├── .codecov.yml │ │ ├── .travis.yml │ │ ├── CHANGELOG.md │ │ ├── CONTRIBUTING.md │ │ ├── LICENSE.txt │ │ ├── README.md │ │ ├── VERSION │ │ ├── colly.go │ │ ├── context.go │ │ ├── debug/ │ │ │ ├── debug.go │ │ │ ├── logdebugger.go │ │ │ └── webdebugger.go │ │ ├── htmlelement.go │ │ ├── http_backend.go │ │ ├── http_trace.go │ │ ├── request.go │ │ ├── response.go │ │ ├── storage/ │ │ │ └── storage.go │ │ ├── unmarshal.go │ │ └── xmlelement.go │ ├── gogo/ │ │ └── protobuf/ │ │ ├── AUTHORS │ │ ├── CONTRIBUTORS │ │ ├── LICENSE │ │ ├── proto/ │ │ │ ├── Makefile │ │ │ ├── clone.go │ │ │ ├── custom_gogo.go │ │ │ ├── decode.go │ │ │ ├── deprecated.go │ │ │ ├── discard.go │ │ │ ├── duration.go │ │ │ ├── duration_gogo.go │ │ │ ├── encode.go │ │ │ ├── encode_gogo.go │ │ │ ├── equal.go │ │ │ ├── extensions.go │ │ │ ├── extensions_gogo.go │ │ │ ├── lib.go │ │ │ ├── lib_gogo.go │ │ │ ├── message_set.go │ │ │ ├── pointer_reflect.go │ │ │ ├── pointer_reflect_gogo.go │ │ │ ├── pointer_unsafe.go │ │ │ ├── pointer_unsafe_gogo.go │ │ │ ├── properties.go │ │ │ ├── properties_gogo.go │ │ │ ├── skip_gogo.go │ │ │ ├── table_marshal.go │ │ │ ├── table_marshal_gogo.go │ │ │ ├── table_merge.go │ │ │ ├── table_unmarshal.go │ │ │ ├── table_unmarshal_gogo.go │ │ │ ├── text.go │ │ │ ├── text_gogo.go │ │ │ ├── text_parser.go │ │ │ ├── timestamp.go │ │ │ ├── timestamp_gogo.go │ │ │ ├── wrappers.go │ │ │ └── wrappers_gogo.go │ │ └── sortkeys/ │ │ └── sortkeys.go │ ├── golang/ │ │ ├── groupcache/ │ │ │ ├── LICENSE │ │ │ └── lru/ │ │ │ └── lru.go │ │ └── protobuf/ │ │ ├── AUTHORS │ │ ├── CONTRIBUTORS │ │ ├── LICENSE │ │ ├── proto/ │ │ │ ├── buffer.go │ │ │ ├── defaults.go │ │ │ ├── deprecated.go │ │ │ ├── discard.go │ │ │ ├── extensions.go │ │ │ ├── properties.go │ │ │ ├── proto.go │ │ │ ├── registry.go │ │ │ ├── text_decode.go │ │ │ ├── text_encode.go │ │ │ ├── wire.go │ │ │ └── wrappers.go │ │ └── ptypes/ │ │ ├── any/ │ │ │ └── any.pb.go │ │ ├── any.go │ │ ├── doc.go │ │ ├── duration/ │ │ │ └── duration.pb.go │ │ ├── duration.go │ │ ├── timestamp/ │ │ │ └── timestamp.pb.go │ │ └── timestamp.go │ ├── google/ │ │ ├── btree/ │ │ │ ├── .travis.yml │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ └── btree.go │ │ ├── gnostic/ │ │ │ ├── LICENSE │ │ │ ├── compiler/ │ │ │ │ ├── README.md │ │ │ │ ├── context.go │ │ │ │ ├── error.go │ │ │ │ ├── extensions.go │ │ │ │ ├── helpers.go │ │ │ │ ├── main.go │ │ │ │ └── reader.go │ │ │ ├── extensions/ │ │ │ │ ├── README.md │ │ │ │ ├── extension.pb.go │ │ │ │ ├── extension.proto │ │ │ │ └── extensions.go │ │ │ ├── jsonschema/ │ │ │ │ ├── README.md │ │ │ │ ├── base.go │ │ │ │ ├── display.go │ │ │ │ ├── models.go │ │ │ │ ├── operations.go │ │ │ │ ├── reader.go │ │ │ │ ├── schema.json │ │ │ │ └── writer.go │ │ │ ├── openapiv2/ │ │ │ │ ├── OpenAPIv2.go │ │ │ │ ├── OpenAPIv2.pb.go │ │ │ │ ├── OpenAPIv2.proto │ │ │ │ ├── README.md │ │ │ │ ├── document.go │ │ │ │ └── openapi-2.0.json │ │ │ └── openapiv3/ │ │ │ ├── OpenAPIv3.go │ │ │ ├── OpenAPIv3.pb.go │ │ │ ├── OpenAPIv3.proto │ │ │ ├── README.md │ │ │ ├── document.go │ │ │ ├── openapi-3.0.json │ │ │ └── openapi-3.1.json │ │ ├── go-cmp/ │ │ │ ├── LICENSE │ │ │ └── cmp/ │ │ │ ├── compare.go │ │ │ ├── export.go │ │ │ ├── internal/ │ │ │ │ ├── diff/ │ │ │ │ │ ├── debug_disable.go │ │ │ │ │ ├── debug_enable.go │ │ │ │ │ └── diff.go │ │ │ │ ├── flags/ │ │ │ │ │ └── flags.go │ │ │ │ ├── function/ │ │ │ │ │ └── func.go │ │ │ │ └── value/ │ │ │ │ ├── name.go │ │ │ │ ├── pointer.go │ │ │ │ └── sort.go │ │ │ ├── options.go │ │ │ ├── path.go │ │ │ ├── report.go │ │ │ ├── report_compare.go │ │ │ ├── report_references.go │ │ │ ├── report_reflect.go │ │ │ ├── report_slices.go │ │ │ ├── report_text.go │ │ │ └── report_value.go │ │ ├── go-containerregistry/ │ │ │ ├── LICENSE │ │ │ ├── internal/ │ │ │ │ ├── and/ │ │ │ │ │ └── and_closer.go │ │ │ │ ├── compression/ │ │ │ │ │ └── compression.go │ │ │ │ ├── estargz/ │ │ │ │ │ └── estargz.go │ │ │ │ ├── gzip/ │ │ │ │ │ └── zip.go │ │ │ │ ├── httptest/ │ │ │ │ │ └── httptest.go │ │ │ │ ├── redact/ │ │ │ │ │ └── redact.go │ │ │ │ ├── retry/ │ │ │ │ │ ├── retry.go │ │ │ │ │ └── wait/ │ │ │ │ │ └── kubernetes_apimachinery_wait.go │ │ │ │ ├── verify/ │ │ │ │ │ └── verify.go │ │ │ │ ├── windows/ │ │ │ │ │ └── windows.go │ │ │ │ └── zstd/ │ │ │ │ └── zstd.go │ │ │ └── pkg/ │ │ │ ├── authn/ │ │ │ │ ├── README.md │ │ │ │ ├── anon.go │ │ │ │ ├── auth.go │ │ │ │ ├── authn.go │ │ │ │ ├── basic.go │ │ │ │ ├── bearer.go │ │ │ │ ├── doc.go │ │ │ │ ├── keychain.go │ │ │ │ └── multikeychain.go │ │ │ ├── compression/ │ │ │ │ └── compression.go │ │ │ ├── crane/ │ │ │ │ ├── append.go │ │ │ │ ├── catalog.go │ │ │ │ ├── config.go │ │ │ │ ├── copy.go │ │ │ │ ├── delete.go │ │ │ │ ├── digest.go │ │ │ │ ├── doc.go │ │ │ │ ├── export.go │ │ │ │ ├── filemap.go │ │ │ │ ├── get.go │ │ │ │ ├── list.go │ │ │ │ ├── manifest.go │ │ │ │ ├── options.go │ │ │ │ ├── pull.go │ │ │ │ ├── push.go │ │ │ │ └── tag.go │ │ │ ├── legacy/ │ │ │ │ ├── config.go │ │ │ │ ├── doc.go │ │ │ │ └── tarball/ │ │ │ │ ├── README.md │ │ │ │ ├── doc.go │ │ │ │ └── write.go │ │ │ ├── logs/ │ │ │ │ └── logs.go │ │ │ ├── name/ │ │ │ │ ├── README.md │ │ │ │ ├── check.go │ │ │ │ ├── digest.go │ │ │ │ ├── doc.go │ │ │ │ ├── errors.go │ │ │ │ ├── options.go │ │ │ │ ├── ref.go │ │ │ │ ├── registry.go │ │ │ │ ├── repository.go │ │ │ │ └── tag.go │ │ │ ├── registry/ │ │ │ │ ├── README.md │ │ │ │ ├── blobs.go │ │ │ │ ├── blobs_disk.go │ │ │ │ ├── error.go │ │ │ │ ├── manifest.go │ │ │ │ ├── registry.go │ │ │ │ └── tls.go │ │ │ └── v1/ │ │ │ ├── config.go │ │ │ ├── daemon/ │ │ │ │ ├── README.md │ │ │ │ ├── doc.go │ │ │ │ ├── image.go │ │ │ │ ├── options.go │ │ │ │ └── write.go │ │ │ ├── doc.go │ │ │ ├── empty/ │ │ │ │ ├── README.md │ │ │ │ ├── doc.go │ │ │ │ ├── image.go │ │ │ │ └── index.go │ │ │ ├── hash.go │ │ │ ├── image.go │ │ │ ├── index.go │ │ │ ├── layer.go │ │ │ ├── layout/ │ │ │ │ ├── README.md │ │ │ │ ├── blob.go │ │ │ │ ├── doc.go │ │ │ │ ├── gc.go │ │ │ │ ├── image.go │ │ │ │ ├── index.go │ │ │ │ ├── layoutpath.go │ │ │ │ ├── options.go │ │ │ │ ├── read.go │ │ │ │ └── write.go │ │ │ ├── manifest.go │ │ │ ├── match/ │ │ │ │ └── match.go │ │ │ ├── mutate/ │ │ │ │ ├── README.md │ │ │ │ ├── doc.go │ │ │ │ ├── image.go │ │ │ │ ├── index.go │ │ │ │ ├── mutate.go │ │ │ │ └── rebase.go │ │ │ ├── partial/ │ │ │ │ ├── README.md │ │ │ │ ├── compressed.go │ │ │ │ ├── doc.go │ │ │ │ ├── image.go │ │ │ │ ├── index.go │ │ │ │ ├── uncompressed.go │ │ │ │ └── with.go │ │ │ ├── platform.go │ │ │ ├── progress.go │ │ │ ├── remote/ │ │ │ │ ├── README.md │ │ │ │ ├── catalog.go │ │ │ │ ├── check.go │ │ │ │ ├── delete.go │ │ │ │ ├── descriptor.go │ │ │ │ ├── doc.go │ │ │ │ ├── fetcher.go │ │ │ │ ├── image.go │ │ │ │ ├── index.go │ │ │ │ ├── layer.go │ │ │ │ ├── list.go │ │ │ │ ├── mount.go │ │ │ │ ├── multi_write.go │ │ │ │ ├── options.go │ │ │ │ ├── progress.go │ │ │ │ ├── puller.go │ │ │ │ ├── pusher.go │ │ │ │ ├── referrers.go │ │ │ │ ├── schema1.go │ │ │ │ ├── transport/ │ │ │ │ │ ├── README.md │ │ │ │ │ ├── basic.go │ │ │ │ │ ├── bearer.go │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── error.go │ │ │ │ │ ├── logger.go │ │ │ │ │ ├── ping.go │ │ │ │ │ ├── retry.go │ │ │ │ │ ├── schemer.go │ │ │ │ │ ├── scope.go │ │ │ │ │ ├── transport.go │ │ │ │ │ └── useragent.go │ │ │ │ └── write.go │ │ │ ├── stream/ │ │ │ │ ├── README.md │ │ │ │ └── layer.go │ │ │ ├── tarball/ │ │ │ │ ├── README.md │ │ │ │ ├── doc.go │ │ │ │ ├── image.go │ │ │ │ ├── layer.go │ │ │ │ └── write.go │ │ │ ├── types/ │ │ │ │ └── types.go │ │ │ └── zz_deepcopy_generated.go │ │ ├── gofuzz/ │ │ │ ├── .travis.yml │ │ │ ├── CONTRIBUTING.md │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── bytesource/ │ │ │ │ └── bytesource.go │ │ │ ├── doc.go │ │ │ └── fuzz.go │ │ ├── shlex/ │ │ │ ├── COPYING │ │ │ ├── README │ │ │ └── shlex.go │ │ └── uuid/ │ │ ├── .travis.yml │ │ ├── CONTRIBUTING.md │ │ ├── CONTRIBUTORS │ │ ├── LICENSE │ │ ├── README.md │ │ ├── dce.go │ │ ├── doc.go │ │ ├── hash.go │ │ ├── marshal.go │ │ ├── node.go │ │ ├── node_js.go │ │ ├── node_net.go │ │ ├── null.go │ │ ├── sql.go │ │ ├── time.go │ │ ├── util.go │ │ ├── uuid.go │ │ ├── version1.go │ │ └── version4.go │ ├── gorilla/ │ │ └── websocket/ │ │ ├── .gitignore │ │ ├── AUTHORS │ │ ├── LICENSE │ │ ├── README.md │ │ ├── client.go │ │ ├── client_clone.go │ │ ├── client_clone_legacy.go │ │ ├── compression.go │ │ ├── conn.go │ │ ├── conn_write.go │ │ ├── conn_write_legacy.go │ │ ├── doc.go │ │ ├── join.go │ │ ├── json.go │ │ ├── mask.go │ │ ├── mask_safe.go │ │ ├── prepared.go │ │ ├── proxy.go │ │ ├── server.go │ │ ├── trace.go │ │ ├── trace_17.go │ │ ├── util.go │ │ └── x_net_proxy.go │ ├── gregjones/ │ │ └── httpcache/ │ │ ├── .travis.yml │ │ ├── LICENSE.txt │ │ ├── README.md │ │ └── httpcache.go │ ├── imdario/ │ │ └── mergo/ │ │ ├── .deepsource.toml │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── CODE_OF_CONDUCT.md │ │ ├── LICENSE │ │ ├── README.md │ │ ├── doc.go │ │ ├── map.go │ │ ├── merge.go │ │ └── mergo.go │ ├── inconshreveable/ │ │ └── mousetrap/ │ │ ├── LICENSE │ │ ├── README.md │ │ ├── trap_others.go │ │ └── trap_windows.go │ ├── jedib0t/ │ │ └── go-pretty/ │ │ └── v6/ │ │ ├── LICENSE │ │ ├── table/ │ │ │ ├── README.md │ │ │ ├── config.go │ │ │ ├── render.go │ │ │ ├── render_csv.go │ │ │ ├── render_hint.go │ │ │ ├── render_html.go │ │ │ ├── render_init.go │ │ │ ├── render_markdown.go │ │ │ ├── render_tsv.go │ │ │ ├── sort.go │ │ │ ├── style.go │ │ │ ├── table.go │ │ │ ├── util.go │ │ │ └── writer.go │ │ └── text/ │ │ ├── README.md │ │ ├── align.go │ │ ├── ansi.go │ │ ├── ansi_unix.go │ │ ├── ansi_windows.go │ │ ├── color.go │ │ ├── color_html.go │ │ ├── cursor.go │ │ ├── direction.go │ │ ├── escape.go │ │ ├── filter.go │ │ ├── format.go │ │ ├── hyperlink.go │ │ ├── string.go │ │ ├── transformer.go │ │ ├── valign.go │ │ └── wrap.go │ ├── josharian/ │ │ └── intern/ │ │ ├── README.md │ │ ├── intern.go │ │ └── license.md │ ├── kennygrant/ │ │ └── sanitize/ │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ └── sanitize.go │ ├── klauspost/ │ │ ├── compress/ │ │ │ ├── .gitattributes │ │ │ ├── .gitignore │ │ │ ├── .goreleaser.yml │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── SECURITY.md │ │ │ ├── compressible.go │ │ │ ├── flate/ │ │ │ │ ├── deflate.go │ │ │ │ ├── dict_decoder.go │ │ │ │ ├── fast_encoder.go │ │ │ │ ├── huffman_bit_writer.go │ │ │ │ ├── huffman_code.go │ │ │ │ ├── huffman_sortByFreq.go │ │ │ │ ├── huffman_sortByLiteral.go │ │ │ │ ├── inflate.go │ │ │ │ ├── inflate_gen.go │ │ │ │ ├── level1.go │ │ │ │ ├── level2.go │ │ │ │ ├── level3.go │ │ │ │ ├── level4.go │ │ │ │ ├── level5.go │ │ │ │ ├── level6.go │ │ │ │ ├── matchlen_amd64.go │ │ │ │ ├── matchlen_amd64.s │ │ │ │ ├── matchlen_generic.go │ │ │ │ ├── regmask_amd64.go │ │ │ │ ├── regmask_other.go │ │ │ │ ├── stateless.go │ │ │ │ └── token.go │ │ │ ├── fse/ │ │ │ │ ├── README.md │ │ │ │ ├── bitreader.go │ │ │ │ ├── bitwriter.go │ │ │ │ ├── bytereader.go │ │ │ │ ├── compress.go │ │ │ │ ├── decompress.go │ │ │ │ └── fse.go │ │ │ ├── gen.sh │ │ │ ├── huff0/ │ │ │ │ ├── .gitignore │ │ │ │ ├── README.md │ │ │ │ ├── bitreader.go │ │ │ │ ├── bitwriter.go │ │ │ │ ├── bytereader.go │ │ │ │ ├── compress.go │ │ │ │ ├── decompress.go │ │ │ │ ├── decompress_amd64.go │ │ │ │ ├── decompress_amd64.s │ │ │ │ ├── decompress_generic.go │ │ │ │ └── huff0.go │ │ │ ├── internal/ │ │ │ │ ├── cpuinfo/ │ │ │ │ │ ├── cpuinfo.go │ │ │ │ │ ├── cpuinfo_amd64.go │ │ │ │ │ └── cpuinfo_amd64.s │ │ │ │ └── snapref/ │ │ │ │ ├── LICENSE │ │ │ │ ├── decode.go │ │ │ │ ├── decode_other.go │ │ │ │ ├── encode.go │ │ │ │ ├── encode_other.go │ │ │ │ └── snappy.go │ │ │ ├── s2sx.mod │ │ │ ├── s2sx.sum │ │ │ └── zstd/ │ │ │ ├── README.md │ │ │ ├── bitreader.go │ │ │ ├── bitwriter.go │ │ │ ├── blockdec.go │ │ │ ├── blockenc.go │ │ │ ├── blocktype_string.go │ │ │ ├── bytebuf.go │ │ │ ├── bytereader.go │ │ │ ├── decodeheader.go │ │ │ ├── decoder.go │ │ │ ├── decoder_options.go │ │ │ ├── dict.go │ │ │ ├── enc_base.go │ │ │ ├── enc_best.go │ │ │ ├── enc_better.go │ │ │ ├── enc_dfast.go │ │ │ ├── enc_fast.go │ │ │ ├── encoder.go │ │ │ ├── encoder_options.go │ │ │ ├── framedec.go │ │ │ ├── frameenc.go │ │ │ ├── fse_decoder.go │ │ │ ├── fse_decoder_amd64.go │ │ │ ├── fse_decoder_amd64.s │ │ │ ├── fse_decoder_generic.go │ │ │ ├── fse_encoder.go │ │ │ ├── fse_predefined.go │ │ │ ├── hash.go │ │ │ ├── history.go │ │ │ ├── internal/ │ │ │ │ └── xxhash/ │ │ │ │ ├── LICENSE.txt │ │ │ │ ├── README.md │ │ │ │ ├── xxhash.go │ │ │ │ ├── xxhash_amd64.s │ │ │ │ ├── xxhash_arm64.s │ │ │ │ ├── xxhash_asm.go │ │ │ │ ├── xxhash_other.go │ │ │ │ └── xxhash_safe.go │ │ │ ├── matchlen_amd64.go │ │ │ ├── matchlen_amd64.s │ │ │ ├── matchlen_generic.go │ │ │ ├── seqdec.go │ │ │ ├── seqdec_amd64.go │ │ │ ├── seqdec_amd64.s │ │ │ ├── seqdec_generic.go │ │ │ ├── seqenc.go │ │ │ ├── snappy.go │ │ │ ├── zip.go │ │ │ └── zstd.go │ │ └── pgzip/ │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── GO_LICENSE │ │ ├── LICENSE │ │ ├── README.md │ │ ├── gunzip.go │ │ └── gzip.go │ ├── liggitt/ │ │ └── tabwriter/ │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ └── tabwriter.go │ ├── mailru/ │ │ └── easyjson/ │ │ ├── LICENSE │ │ ├── buffer/ │ │ │ └── pool.go │ │ ├── jlexer/ │ │ │ ├── bytestostr.go │ │ │ ├── bytestostr_nounsafe.go │ │ │ ├── error.go │ │ │ └── lexer.go │ │ └── jwriter/ │ │ └── writer.go │ ├── mattn/ │ │ ├── go-colorable/ │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── colorable_appengine.go │ │ │ ├── colorable_others.go │ │ │ ├── colorable_windows.go │ │ │ ├── go.test.sh │ │ │ └── noncolorable.go │ │ ├── go-isatty/ │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── doc.go │ │ │ ├── go.test.sh │ │ │ ├── isatty_bsd.go │ │ │ ├── isatty_others.go │ │ │ ├── isatty_plan9.go │ │ │ ├── isatty_solaris.go │ │ │ ├── isatty_tcgets.go │ │ │ └── isatty_windows.go │ │ ├── go-runewidth/ │ │ │ ├── .travis.yml │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── go.test.sh │ │ │ ├── runewidth.go │ │ │ ├── runewidth_appengine.go │ │ │ ├── runewidth_js.go │ │ │ ├── runewidth_posix.go │ │ │ ├── runewidth_table.go │ │ │ └── runewidth_windows.go │ │ ├── go-shellwords/ │ │ │ ├── .travis.yml │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── go.test.sh │ │ │ ├── shellwords.go │ │ │ ├── util_posix.go │ │ │ └── util_windows.go │ │ └── go-tty/ │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ ├── tty.go │ │ ├── tty_bsd.go │ │ ├── tty_linux.go │ │ ├── tty_plan9.go │ │ ├── tty_sys5.go │ │ ├── tty_unix.go │ │ └── tty_windows.go │ ├── mitchellh/ │ │ ├── go-homedir/ │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ └── homedir.go │ │ └── mapstructure/ │ │ ├── CHANGELOG.md │ │ ├── LICENSE │ │ ├── README.md │ │ ├── decode_hooks.go │ │ ├── error.go │ │ └── mapstructure.go │ ├── moby/ │ │ ├── patternmatcher/ │ │ │ ├── LICENSE │ │ │ ├── NOTICE │ │ │ └── patternmatcher.go │ │ ├── spdystream/ │ │ │ ├── CONTRIBUTING.md │ │ │ ├── LICENSE │ │ │ ├── MAINTAINERS │ │ │ ├── NOTICE │ │ │ ├── README.md │ │ │ ├── connection.go │ │ │ ├── handlers.go │ │ │ ├── priority.go │ │ │ ├── spdy/ │ │ │ │ ├── dictionary.go │ │ │ │ ├── read.go │ │ │ │ ├── types.go │ │ │ │ └── write.go │ │ │ ├── stream.go │ │ │ └── utils.go │ │ └── sys/ │ │ ├── sequential/ │ │ │ ├── LICENSE │ │ │ ├── doc.go │ │ │ ├── sequential_unix.go │ │ │ └── sequential_windows.go │ │ └── user/ │ │ ├── LICENSE │ │ ├── lookup_unix.go │ │ ├── user.go │ │ └── user_fuzzer.go │ ├── modern-go/ │ │ └── concurrent/ │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ ├── executor.go │ │ ├── go_above_19.go │ │ ├── go_below_19.go │ │ ├── log.go │ │ ├── test.sh │ │ └── unbounded_executor.go │ ├── mohae/ │ │ └── deepcopy/ │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ └── deepcopy.go │ ├── monochromegane/ │ │ └── go-gitignore/ │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ ├── depth_holder.go │ │ ├── full_scan_patterns.go │ │ ├── gitignore.go │ │ ├── index_scan_patterns.go │ │ ├── initial_holder.go │ │ ├── match.go │ │ ├── pattern.go │ │ ├── patterns.go │ │ └── util.go │ ├── morikuni/ │ │ └── aec/ │ │ ├── LICENSE │ │ ├── README.md │ │ ├── aec.go │ │ ├── ansi.go │ │ ├── builder.go │ │ └── sgr.go │ ├── munnerz/ │ │ └── goautoneg/ │ │ ├── LICENSE │ │ ├── Makefile │ │ ├── README.txt │ │ └── autoneg.go │ ├── oasdiff/ │ │ ├── yaml/ │ │ │ ├── .gitignore │ │ │ ├── .golangci.toml │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── fields.go │ │ │ └── yaml.go │ │ └── yaml3/ │ │ ├── LICENSE │ │ ├── NOTICE │ │ ├── README.md │ │ ├── apic.go │ │ ├── decode.go │ │ ├── emitterc.go │ │ ├── encode.go │ │ ├── origin.go │ │ ├── parserc.go │ │ ├── readerc.go │ │ ├── resolve.go │ │ ├── scannerc.go │ │ ├── sorter.go │ │ ├── writerc.go │ │ ├── yaml.go │ │ ├── yamlh.go │ │ └── yamlprivateh.go │ ├── opencontainers/ │ │ ├── go-digest/ │ │ │ ├── .mailmap │ │ │ ├── .pullapprove.yml │ │ │ ├── .travis.yml │ │ │ ├── CONTRIBUTING.md │ │ │ ├── LICENSE │ │ │ ├── LICENSE.docs │ │ │ ├── MAINTAINERS │ │ │ ├── README.md │ │ │ ├── algorithm.go │ │ │ ├── digest.go │ │ │ ├── digester.go │ │ │ ├── doc.go │ │ │ └── verifiers.go │ │ └── image-spec/ │ │ ├── LICENSE │ │ └── specs-go/ │ │ ├── v1/ │ │ │ ├── annotations.go │ │ │ ├── config.go │ │ │ ├── descriptor.go │ │ │ ├── index.go │ │ │ ├── layout.go │ │ │ ├── manifest.go │ │ │ └── mediatype.go │ │ ├── version.go │ │ └── versioned.go │ ├── perimeterx/ │ │ └── marshmallow/ │ │ ├── .gitignore │ │ ├── CHANGELOG.md │ │ ├── CODE_OF_CONDUCT.md │ │ ├── CONTRIBUTING.md │ │ ├── LICENSE │ │ ├── README.md │ │ ├── cache.go │ │ ├── doc.go │ │ ├── errors.go │ │ ├── options.go │ │ ├── reflection.go │ │ ├── unmarshal.go │ │ └── unmarshal_from_json_map.go │ ├── peterbourgon/ │ │ └── diskv/ │ │ ├── LICENSE │ │ ├── README.md │ │ ├── compression.go │ │ ├── diskv.go │ │ └── index.go │ ├── pkg/ │ │ ├── errors/ │ │ │ ├── .gitignore │ │ │ ├── .travis.yml │ │ │ ├── LICENSE │ │ │ ├── Makefile │ │ │ ├── README.md │ │ │ ├── appveyor.yml │ │ │ ├── errors.go │ │ │ ├── go113.go │ │ │ └── stack.go │ │ └── term/ │ │ ├── LICENSE │ │ └── termios/ │ │ ├── doc.go │ │ ├── ioctl.go │ │ ├── ioctl_darwin.go │ │ ├── ioctl_solaris.go │ │ ├── pty.go │ │ ├── pty_bsd.go │ │ ├── pty_darwin.go │ │ ├── pty_freebsd.go │ │ ├── pty_linux.go │ │ ├── pty_netbsd.go │ │ ├── pty_solaris.go │ │ ├── termios.go │ │ ├── termios_bsd.go │ │ ├── termios_const.go │ │ ├── termios_const_solaris.go │ │ ├── termios_linux.go │ │ ├── termios_solaris.go │ │ └── termios_windows.go │ ├── pmezard/ │ │ └── go-difflib/ │ │ ├── LICENSE │ │ └── difflib/ │ │ └── difflib.go │ ├── rivo/ │ │ └── uniseg/ │ │ ├── LICENSE.txt │ │ ├── README.md │ │ ├── doc.go │ │ ├── grapheme.go │ │ └── properties.go │ ├── russross/ │ │ └── blackfriday/ │ │ └── v2/ │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── LICENSE.txt │ │ ├── README.md │ │ ├── block.go │ │ ├── doc.go │ │ ├── entities.go │ │ ├── esc.go │ │ ├── html.go │ │ ├── inline.go │ │ ├── markdown.go │ │ ├── node.go │ │ └── smartypants.go │ ├── saintfish/ │ │ └── chardet/ │ │ ├── 2022.go │ │ ├── AUTHORS │ │ ├── LICENSE │ │ ├── README.md │ │ ├── detector.go │ │ ├── icu-license.html │ │ ├── multi_byte.go │ │ ├── recognizer.go │ │ ├── single_byte.go │ │ ├── unicode.go │ │ └── utf8.go │ ├── slimtoolkit/ │ │ ├── go-update/ │ │ │ ├── .gitignore │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── apply.go │ │ │ ├── doc.go │ │ │ ├── hide_noop.go │ │ │ ├── hide_windows.go │ │ │ ├── internal/ │ │ │ │ ├── binarydist/ │ │ │ │ │ ├── License │ │ │ │ │ ├── Readme.md │ │ │ │ │ ├── bzip2.go │ │ │ │ │ ├── diff.go │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── encoding.go │ │ │ │ │ ├── patch.go │ │ │ │ │ └── seek.go │ │ │ │ └── osext/ │ │ │ │ ├── LICENSE │ │ │ │ ├── README.md │ │ │ │ ├── osext.go │ │ │ │ ├── osext_plan9.go │ │ │ │ ├── osext_procfs.go │ │ │ │ ├── osext_sysctl.go │ │ │ │ └── osext_windows.go │ │ │ ├── patcher.go │ │ │ └── verifier.go │ │ ├── uilive/ │ │ │ ├── .travis.yml │ │ │ ├── LICENSE │ │ │ ├── Makefile │ │ │ ├── README.md │ │ │ ├── doc.go │ │ │ ├── terminal_size.go │ │ │ ├── writer.go │ │ │ ├── writer_posix.go │ │ │ └── writer_windows.go │ │ └── uiprogress/ │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── Makefile │ │ ├── README.md │ │ ├── bar.go │ │ ├── doc.go │ │ ├── progress.go │ │ └── util/ │ │ └── strutil/ │ │ └── strutil.go │ ├── spf13/ │ │ ├── cobra/ │ │ │ ├── .gitignore │ │ │ ├── .golangci.yml │ │ │ ├── .mailmap │ │ │ ├── CONDUCT.md │ │ │ ├── CONTRIBUTING.md │ │ │ ├── LICENSE.txt │ │ │ ├── MAINTAINERS │ │ │ ├── Makefile │ │ │ ├── README.md │ │ │ ├── active_help.go │ │ │ ├── active_help.md │ │ │ ├── args.go │ │ │ ├── bash_completions.go │ │ │ ├── bash_completions.md │ │ │ ├── bash_completionsV2.go │ │ │ ├── cobra.go │ │ │ ├── command.go │ │ │ ├── command_notwin.go │ │ │ ├── command_win.go │ │ │ ├── completions.go │ │ │ ├── fish_completions.go │ │ │ ├── fish_completions.md │ │ │ ├── flag_groups.go │ │ │ ├── powershell_completions.go │ │ │ ├── powershell_completions.md │ │ │ ├── projects_using_cobra.md │ │ │ ├── shell_completions.go │ │ │ ├── shell_completions.md │ │ │ ├── user_guide.md │ │ │ ├── zsh_completions.go │ │ │ └── zsh_completions.md │ │ └── pflag/ │ │ ├── .gitignore │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.md │ │ ├── bool.go │ │ ├── bool_slice.go │ │ ├── bytes.go │ │ ├── count.go │ │ ├── duration.go │ │ ├── duration_slice.go │ │ ├── flag.go │ │ ├── float32.go │ │ ├── float32_slice.go │ │ ├── float64.go │ │ ├── float64_slice.go │ │ ├── golangflag.go │ │ ├── int.go │ │ ├── int16.go │ │ ├── int32.go │ │ ├── int32_slice.go │ │ ├── int64.go │ │ ├── int64_slice.go │ │ ├── int8.go │ │ ├── int_slice.go │ │ ├── ip.go │ │ ├── ip_slice.go │ │ ├── ipmask.go │ │ ├── ipnet.go │ │ ├── string.go │ │ ├── string_array.go │ │ ├── string_slice.go │ │ ├── string_to_int.go │ │ ├── string_to_int64.go │ │ ├── string_to_string.go │ │ ├── uint.go │ │ ├── uint16.go │ │ ├── uint32.go │ │ ├── uint64.go │ │ ├── uint8.go │ │ └── uint_slice.go │ ├── stretchr/ │ │ └── testify/ │ │ ├── LICENSE │ │ ├── assert/ │ │ │ ├── assertion_compare.go │ │ │ ├── assertion_format.go │ │ │ ├── assertion_format.go.tmpl │ │ │ ├── assertion_forward.go │ │ │ ├── assertion_forward.go.tmpl │ │ │ ├── assertion_order.go │ │ │ ├── assertions.go │ │ │ ├── doc.go │ │ │ ├── errors.go │ │ │ ├── forward_assertions.go │ │ │ └── http_assertions.go │ │ └── require/ │ │ ├── doc.go │ │ ├── forward_requirements.go │ │ ├── require.go │ │ ├── require.go.tmpl │ │ ├── require_forward.go │ │ ├── require_forward.go.tmpl │ │ └── requirements.go │ ├── syndtr/ │ │ └── gocapability/ │ │ ├── LICENSE │ │ └── capability/ │ │ ├── capability.go │ │ ├── capability_linux.go │ │ ├── capability_noop.go │ │ ├── enum.go │ │ ├── enum_gen.go │ │ └── syscall_linux.go │ ├── temoto/ │ │ └── robotstxt/ │ │ ├── .gitignore │ │ ├── .golangci.yml │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── README.rst │ │ ├── codecov.yml │ │ ├── fuzz.go │ │ ├── parser.go │ │ ├── robotstxt.go │ │ └── scanner.go │ ├── ulikunitz/ │ │ └── xz/ │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── TODO.md │ │ ├── bits.go │ │ ├── crc.go │ │ ├── format.go │ │ ├── fox-check-none.xz │ │ ├── fox.xz │ │ ├── internal/ │ │ │ ├── hash/ │ │ │ │ ├── cyclic_poly.go │ │ │ │ ├── doc.go │ │ │ │ ├── rabin_karp.go │ │ │ │ └── roller.go │ │ │ └── xlog/ │ │ │ └── xlog.go │ │ ├── lzma/ │ │ │ ├── bintree.go │ │ │ ├── bitops.go │ │ │ ├── breader.go │ │ │ ├── buffer.go │ │ │ ├── bytewriter.go │ │ │ ├── decoder.go │ │ │ ├── decoderdict.go │ │ │ ├── directcodec.go │ │ │ ├── distcodec.go │ │ │ ├── encoder.go │ │ │ ├── encoderdict.go │ │ │ ├── fox.lzma │ │ │ ├── hashtable.go │ │ │ ├── header.go │ │ │ ├── header2.go │ │ │ ├── lengthcodec.go │ │ │ ├── literalcodec.go │ │ │ ├── matchalgorithm.go │ │ │ ├── operation.go │ │ │ ├── prob.go │ │ │ ├── properties.go │ │ │ ├── rangecodec.go │ │ │ ├── reader.go │ │ │ ├── reader2.go │ │ │ ├── state.go │ │ │ ├── treecodecs.go │ │ │ ├── writer.go │ │ │ └── writer2.go │ │ ├── lzmafilter.go │ │ ├── make-docs │ │ ├── none-check.go │ │ ├── reader.go │ │ └── writer.go │ ├── ulyssessouza/ │ │ └── godotenv/ │ │ ├── .gitignore │ │ ├── LICENCE │ │ ├── README.md │ │ ├── godotenv.go │ │ └── renovate.json │ ├── vbatts/ │ │ └── tar-split/ │ │ ├── LICENSE │ │ └── archive/ │ │ └── tar/ │ │ ├── common.go │ │ ├── format.go │ │ ├── reader.go │ │ ├── stat_actime1.go │ │ ├── stat_actime2.go │ │ ├── stat_unix.go │ │ ├── strconv.go │ │ └── writer.go │ ├── xlab/ │ │ └── treeprint/ │ │ ├── LICENSE │ │ ├── README.md │ │ ├── helpers.go │ │ ├── struct.go │ │ └── treeprint.go │ └── xrash/ │ └── smetrics/ │ ├── .travis.yml │ ├── LICENSE │ ├── README.md │ ├── doc.go │ ├── hamming.go │ ├── jaro-winkler.go │ ├── jaro.go │ ├── soundex.go │ ├── ukkonen.go │ └── wagner-fischer.go ├── go.opentelemetry.io/ │ ├── contrib/ │ │ └── instrumentation/ │ │ └── net/ │ │ └── http/ │ │ └── otelhttp/ │ │ ├── LICENSE │ │ ├── client.go │ │ ├── common.go │ │ ├── config.go │ │ ├── doc.go │ │ ├── handler.go │ │ ├── internal/ │ │ │ └── semconvutil/ │ │ │ ├── gen.go │ │ │ ├── httpconv.go │ │ │ └── netconv.go │ │ ├── labeler.go │ │ ├── transport.go │ │ ├── version.go │ │ └── wrap.go │ └── otel/ │ ├── .codespellignore │ ├── .codespellrc │ ├── .gitattributes │ ├── .gitignore │ ├── .gitmodules │ ├── .golangci.yml │ ├── .lycheeignore │ ├── .markdownlint.yaml │ ├── CHANGELOG.md │ ├── CODEOWNERS │ ├── CONTRIBUTING.md │ ├── LICENSE │ ├── Makefile │ ├── README.md │ ├── RELEASING.md │ ├── VERSIONING.md │ ├── attribute/ │ │ ├── doc.go │ │ ├── encoder.go │ │ ├── filter.go │ │ ├── iterator.go │ │ ├── key.go │ │ ├── kv.go │ │ ├── set.go │ │ ├── type_string.go │ │ └── value.go │ ├── baggage/ │ │ ├── baggage.go │ │ ├── context.go │ │ └── doc.go │ ├── codes/ │ │ ├── codes.go │ │ └── doc.go │ ├── doc.go │ ├── error_handler.go │ ├── get_main_pkgs.sh │ ├── handler.go │ ├── internal/ │ │ ├── attribute/ │ │ │ └── attribute.go │ │ ├── baggage/ │ │ │ ├── baggage.go │ │ │ └── context.go │ │ ├── gen.go │ │ ├── global/ │ │ │ ├── handler.go │ │ │ ├── instruments.go │ │ │ ├── internal_logging.go │ │ │ ├── meter.go │ │ │ ├── propagator.go │ │ │ ├── state.go │ │ │ └── trace.go │ │ └── rawhelpers.go │ ├── internal_logging.go │ ├── metric/ │ │ ├── LICENSE │ │ ├── asyncfloat64.go │ │ ├── asyncint64.go │ │ ├── config.go │ │ ├── doc.go │ │ ├── embedded/ │ │ │ └── embedded.go │ │ ├── instrument.go │ │ ├── meter.go │ │ ├── syncfloat64.go │ │ └── syncint64.go │ ├── metric.go │ ├── propagation/ │ │ ├── baggage.go │ │ ├── doc.go │ │ ├── propagation.go │ │ └── trace_context.go │ ├── propagation.go │ ├── requirements.txt │ ├── semconv/ │ │ └── v1.17.0/ │ │ ├── doc.go │ │ ├── event.go │ │ ├── exception.go │ │ ├── http.go │ │ ├── resource.go │ │ ├── schema.go │ │ └── trace.go │ ├── trace/ │ │ ├── LICENSE │ │ ├── config.go │ │ ├── context.go │ │ ├── doc.go │ │ ├── nonrecording.go │ │ ├── noop.go │ │ ├── trace.go │ │ └── tracestate.go │ ├── trace.go │ ├── verify_examples.sh │ ├── version.go │ └── versions.yaml ├── go.starlark.net/ │ ├── LICENSE │ ├── internal/ │ │ ├── compile/ │ │ │ ├── compile.go │ │ │ └── serial.go │ │ └── spell/ │ │ └── spell.go │ ├── resolve/ │ │ ├── binding.go │ │ └── resolve.go │ ├── starlark/ │ │ ├── debug.go │ │ ├── empty.s │ │ ├── eval.go │ │ ├── hashtable.go │ │ ├── int.go │ │ ├── interp.go │ │ ├── library.go │ │ ├── profile.go │ │ ├── unpack.go │ │ └── value.go │ ├── starlarkstruct/ │ │ ├── module.go │ │ └── struct.go │ └── syntax/ │ ├── grammar.txt │ ├── parse.go │ ├── quote.go │ ├── scan.go │ ├── syntax.go │ └── walk.go ├── golang.org/ │ └── x/ │ ├── crypto/ │ │ ├── LICENSE │ │ ├── PATENTS │ │ └── acme/ │ │ ├── acme.go │ │ ├── autocert/ │ │ │ ├── autocert.go │ │ │ ├── cache.go │ │ │ ├── listener.go │ │ │ └── renewal.go │ │ ├── http.go │ │ ├── jws.go │ │ ├── rfc8555.go │ │ └── types.go │ ├── mod/ │ │ ├── LICENSE │ │ ├── PATENTS │ │ └── semver/ │ │ └── semver.go │ ├── net/ │ │ ├── LICENSE │ │ ├── PATENTS │ │ ├── context/ │ │ │ └── context.go │ │ ├── html/ │ │ │ ├── atom/ │ │ │ │ ├── atom.go │ │ │ │ └── table.go │ │ │ ├── charset/ │ │ │ │ └── charset.go │ │ │ ├── const.go │ │ │ ├── doc.go │ │ │ ├── doctype.go │ │ │ ├── entity.go │ │ │ ├── escape.go │ │ │ ├── foreign.go │ │ │ ├── iter.go │ │ │ ├── node.go │ │ │ ├── parse.go │ │ │ ├── render.go │ │ │ └── token.go │ │ ├── http/ │ │ │ └── httpguts/ │ │ │ ├── guts.go │ │ │ └── httplex.go │ │ ├── http2/ │ │ │ ├── .gitignore │ │ │ ├── ascii.go │ │ │ ├── ciphers.go │ │ │ ├── client_conn_pool.go │ │ │ ├── config.go │ │ │ ├── config_go125.go │ │ │ ├── config_go126.go │ │ │ ├── databuffer.go │ │ │ ├── errors.go │ │ │ ├── flow.go │ │ │ ├── frame.go │ │ │ ├── gotrack.go │ │ │ ├── hpack/ │ │ │ │ ├── encode.go │ │ │ │ ├── hpack.go │ │ │ │ ├── huffman.go │ │ │ │ ├── static_table.go │ │ │ │ └── tables.go │ │ │ ├── http2.go │ │ │ ├── pipe.go │ │ │ ├── server.go │ │ │ ├── transport.go │ │ │ ├── unencrypted.go │ │ │ ├── write.go │ │ │ ├── writesched.go │ │ │ ├── writesched_priority_rfc7540.go │ │ │ ├── writesched_priority_rfc9218.go │ │ │ ├── writesched_random.go │ │ │ └── writesched_roundrobin.go │ │ ├── idna/ │ │ │ ├── go118.go │ │ │ ├── idna10.0.0.go │ │ │ ├── idna9.0.0.go │ │ │ ├── pre_go118.go │ │ │ ├── punycode.go │ │ │ ├── tables10.0.0.go │ │ │ ├── tables11.0.0.go │ │ │ ├── tables12.0.0.go │ │ │ ├── tables13.0.0.go │ │ │ ├── tables15.0.0.go │ │ │ ├── tables9.0.0.go │ │ │ ├── trie.go │ │ │ ├── trie12.0.0.go │ │ │ ├── trie13.0.0.go │ │ │ └── trieval.go │ │ ├── internal/ │ │ │ ├── httpcommon/ │ │ │ │ ├── ascii.go │ │ │ │ ├── headermap.go │ │ │ │ └── request.go │ │ │ └── socks/ │ │ │ ├── client.go │ │ │ └── socks.go │ │ └── proxy/ │ │ ├── dial.go │ │ ├── direct.go │ │ ├── per_host.go │ │ ├── proxy.go │ │ └── socks5.go │ ├── oauth2/ │ │ ├── .travis.yml │ │ ├── CONTRIBUTING.md │ │ ├── LICENSE │ │ ├── README.md │ │ ├── internal/ │ │ │ ├── client_appengine.go │ │ │ ├── doc.go │ │ │ ├── oauth2.go │ │ │ ├── token.go │ │ │ └── transport.go │ │ ├── oauth2.go │ │ ├── token.go │ │ └── transport.go │ ├── sync/ │ │ ├── LICENSE │ │ ├── PATENTS │ │ └── errgroup/ │ │ └── errgroup.go │ ├── sys/ │ │ ├── LICENSE │ │ ├── PATENTS │ │ ├── execabs/ │ │ │ ├── execabs.go │ │ │ ├── execabs_go118.go │ │ │ └── execabs_go119.go │ │ ├── plan9/ │ │ │ ├── asm.s │ │ │ ├── asm_plan9_386.s │ │ │ ├── asm_plan9_amd64.s │ │ │ ├── asm_plan9_arm.s │ │ │ ├── const_plan9.go │ │ │ ├── dir_plan9.go │ │ │ ├── env_plan9.go │ │ │ ├── errors_plan9.go │ │ │ ├── mkall.sh │ │ │ ├── mkerrors.sh │ │ │ ├── mksysnum_plan9.sh │ │ │ ├── pwd_plan9.go │ │ │ ├── race.go │ │ │ ├── race0.go │ │ │ ├── str.go │ │ │ ├── syscall.go │ │ │ ├── syscall_plan9.go │ │ │ ├── zsyscall_plan9_386.go │ │ │ ├── zsyscall_plan9_amd64.go │ │ │ ├── zsyscall_plan9_arm.go │ │ │ └── zsysnum_plan9.go │ │ ├── unix/ │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── affinity_linux.go │ │ │ ├── aliases.go │ │ │ ├── asm_aix_ppc64.s │ │ │ ├── asm_bsd_386.s │ │ │ ├── asm_bsd_amd64.s │ │ │ ├── asm_bsd_arm.s │ │ │ ├── asm_bsd_arm64.s │ │ │ ├── asm_bsd_ppc64.s │ │ │ ├── asm_bsd_riscv64.s │ │ │ ├── asm_linux_386.s │ │ │ ├── asm_linux_amd64.s │ │ │ ├── asm_linux_arm.s │ │ │ ├── asm_linux_arm64.s │ │ │ ├── asm_linux_loong64.s │ │ │ ├── asm_linux_mips64x.s │ │ │ ├── asm_linux_mipsx.s │ │ │ ├── asm_linux_ppc64x.s │ │ │ ├── asm_linux_riscv64.s │ │ │ ├── asm_linux_s390x.s │ │ │ ├── asm_openbsd_mips64.s │ │ │ ├── asm_solaris_amd64.s │ │ │ ├── asm_zos_s390x.s │ │ │ ├── auxv.go │ │ │ ├── auxv_unsupported.go │ │ │ ├── bluetooth_linux.go │ │ │ ├── bpxsvc_zos.go │ │ │ ├── bpxsvc_zos.s │ │ │ ├── cap_freebsd.go │ │ │ ├── constants.go │ │ │ ├── dev_aix_ppc.go │ │ │ ├── dev_aix_ppc64.go │ │ │ ├── dev_darwin.go │ │ │ ├── dev_dragonfly.go │ │ │ ├── dev_freebsd.go │ │ │ ├── dev_linux.go │ │ │ ├── dev_netbsd.go │ │ │ ├── dev_openbsd.go │ │ │ ├── dev_zos.go │ │ │ ├── dirent.go │ │ │ ├── endian_big.go │ │ │ ├── endian_little.go │ │ │ ├── env_unix.go │ │ │ ├── fcntl.go │ │ │ ├── fcntl_darwin.go │ │ │ ├── fcntl_linux_32bit.go │ │ │ ├── fdset.go │ │ │ ├── gccgo.go │ │ │ ├── gccgo_c.c │ │ │ ├── gccgo_linux_amd64.go │ │ │ ├── ifreq_linux.go │ │ │ ├── ioctl_linux.go │ │ │ ├── ioctl_signed.go │ │ │ ├── ioctl_unsigned.go │ │ │ ├── ioctl_zos.go │ │ │ ├── mkall.sh │ │ │ ├── mkerrors.sh │ │ │ ├── mmap_nomremap.go │ │ │ ├── mremap.go │ │ │ ├── pagesize_unix.go │ │ │ ├── pledge_openbsd.go │ │ │ ├── ptrace_darwin.go │ │ │ ├── ptrace_ios.go │ │ │ ├── race.go │ │ │ ├── race0.go │ │ │ ├── readdirent_getdents.go │ │ │ ├── readdirent_getdirentries.go │ │ │ ├── sockcmsg_dragonfly.go │ │ │ ├── sockcmsg_linux.go │ │ │ ├── sockcmsg_unix.go │ │ │ ├── sockcmsg_unix_other.go │ │ │ ├── sockcmsg_zos.go │ │ │ ├── symaddr_zos_s390x.s │ │ │ ├── syscall.go │ │ │ ├── syscall_aix.go │ │ │ ├── syscall_aix_ppc.go │ │ │ ├── syscall_aix_ppc64.go │ │ │ ├── syscall_bsd.go │ │ │ ├── syscall_darwin.go │ │ │ ├── syscall_darwin_amd64.go │ │ │ ├── syscall_darwin_arm64.go │ │ │ ├── syscall_darwin_libSystem.go │ │ │ ├── syscall_dragonfly.go │ │ │ ├── syscall_dragonfly_amd64.go │ │ │ ├── syscall_freebsd.go │ │ │ ├── syscall_freebsd_386.go │ │ │ ├── syscall_freebsd_amd64.go │ │ │ ├── syscall_freebsd_arm.go │ │ │ ├── syscall_freebsd_arm64.go │ │ │ ├── syscall_freebsd_riscv64.go │ │ │ ├── syscall_hurd.go │ │ │ ├── syscall_hurd_386.go │ │ │ ├── syscall_illumos.go │ │ │ ├── syscall_linux.go │ │ │ ├── syscall_linux_386.go │ │ │ ├── syscall_linux_alarm.go │ │ │ ├── syscall_linux_amd64.go │ │ │ ├── syscall_linux_amd64_gc.go │ │ │ ├── syscall_linux_arm.go │ │ │ ├── syscall_linux_arm64.go │ │ │ ├── syscall_linux_gc.go │ │ │ ├── syscall_linux_gc_386.go │ │ │ ├── syscall_linux_gc_arm.go │ │ │ ├── syscall_linux_gccgo_386.go │ │ │ ├── syscall_linux_gccgo_arm.go │ │ │ ├── syscall_linux_loong64.go │ │ │ ├── syscall_linux_mips64x.go │ │ │ ├── syscall_linux_mipsx.go │ │ │ ├── syscall_linux_ppc.go │ │ │ ├── syscall_linux_ppc64x.go │ │ │ ├── syscall_linux_riscv64.go │ │ │ ├── syscall_linux_s390x.go │ │ │ ├── syscall_linux_sparc64.go │ │ │ ├── syscall_netbsd.go │ │ │ ├── syscall_netbsd_386.go │ │ │ ├── syscall_netbsd_amd64.go │ │ │ ├── syscall_netbsd_arm.go │ │ │ ├── syscall_netbsd_arm64.go │ │ │ ├── syscall_openbsd.go │ │ │ ├── syscall_openbsd_386.go │ │ │ ├── syscall_openbsd_amd64.go │ │ │ ├── syscall_openbsd_arm.go │ │ │ ├── syscall_openbsd_arm64.go │ │ │ ├── syscall_openbsd_libc.go │ │ │ ├── syscall_openbsd_mips64.go │ │ │ ├── syscall_openbsd_ppc64.go │ │ │ ├── syscall_openbsd_riscv64.go │ │ │ ├── syscall_solaris.go │ │ │ ├── syscall_solaris_amd64.go │ │ │ ├── syscall_unix.go │ │ │ ├── syscall_unix_gc.go │ │ │ ├── syscall_unix_gc_ppc64x.go │ │ │ ├── syscall_zos_s390x.go │ │ │ ├── sysvshm_linux.go │ │ │ ├── sysvshm_unix.go │ │ │ ├── sysvshm_unix_other.go │ │ │ ├── timestruct.go │ │ │ ├── unveil_openbsd.go │ │ │ ├── vgetrandom_linux.go │ │ │ ├── vgetrandom_unsupported.go │ │ │ ├── xattr_bsd.go │ │ │ ├── zerrors_aix_ppc.go │ │ │ ├── zerrors_aix_ppc64.go │ │ │ ├── zerrors_darwin_amd64.go │ │ │ ├── zerrors_darwin_arm64.go │ │ │ ├── zerrors_dragonfly_amd64.go │ │ │ ├── zerrors_freebsd_386.go │ │ │ ├── zerrors_freebsd_amd64.go │ │ │ ├── zerrors_freebsd_arm.go │ │ │ ├── zerrors_freebsd_arm64.go │ │ │ ├── zerrors_freebsd_riscv64.go │ │ │ ├── zerrors_linux.go │ │ │ ├── zerrors_linux_386.go │ │ │ ├── zerrors_linux_amd64.go │ │ │ ├── zerrors_linux_arm.go │ │ │ ├── zerrors_linux_arm64.go │ │ │ ├── zerrors_linux_loong64.go │ │ │ ├── zerrors_linux_mips.go │ │ │ ├── zerrors_linux_mips64.go │ │ │ ├── zerrors_linux_mips64le.go │ │ │ ├── zerrors_linux_mipsle.go │ │ │ ├── zerrors_linux_ppc.go │ │ │ ├── zerrors_linux_ppc64.go │ │ │ ├── zerrors_linux_ppc64le.go │ │ │ ├── zerrors_linux_riscv64.go │ │ │ ├── zerrors_linux_s390x.go │ │ │ ├── zerrors_linux_sparc64.go │ │ │ ├── zerrors_netbsd_386.go │ │ │ ├── zerrors_netbsd_amd64.go │ │ │ ├── zerrors_netbsd_arm.go │ │ │ ├── zerrors_netbsd_arm64.go │ │ │ ├── zerrors_openbsd_386.go │ │ │ ├── zerrors_openbsd_amd64.go │ │ │ ├── zerrors_openbsd_arm.go │ │ │ ├── zerrors_openbsd_arm64.go │ │ │ ├── zerrors_openbsd_mips64.go │ │ │ ├── zerrors_openbsd_ppc64.go │ │ │ ├── zerrors_openbsd_riscv64.go │ │ │ ├── zerrors_solaris_amd64.go │ │ │ ├── zerrors_zos_s390x.go │ │ │ ├── zptrace_armnn_linux.go │ │ │ ├── zptrace_linux_arm64.go │ │ │ ├── zptrace_mipsnn_linux.go │ │ │ ├── zptrace_mipsnnle_linux.go │ │ │ ├── zptrace_x86_linux.go │ │ │ ├── zsymaddr_zos_s390x.s │ │ │ ├── zsyscall_aix_ppc.go │ │ │ ├── zsyscall_aix_ppc64.go │ │ │ ├── zsyscall_aix_ppc64_gc.go │ │ │ ├── zsyscall_aix_ppc64_gccgo.go │ │ │ ├── zsyscall_darwin_amd64.go │ │ │ ├── zsyscall_darwin_amd64.s │ │ │ ├── zsyscall_darwin_arm64.go │ │ │ ├── zsyscall_darwin_arm64.s │ │ │ ├── zsyscall_dragonfly_amd64.go │ │ │ ├── zsyscall_freebsd_386.go │ │ │ ├── zsyscall_freebsd_amd64.go │ │ │ ├── zsyscall_freebsd_arm.go │ │ │ ├── zsyscall_freebsd_arm64.go │ │ │ ├── zsyscall_freebsd_riscv64.go │ │ │ ├── zsyscall_illumos_amd64.go │ │ │ ├── zsyscall_linux.go │ │ │ ├── zsyscall_linux_386.go │ │ │ ├── zsyscall_linux_amd64.go │ │ │ ├── zsyscall_linux_arm.go │ │ │ ├── zsyscall_linux_arm64.go │ │ │ ├── zsyscall_linux_loong64.go │ │ │ ├── zsyscall_linux_mips.go │ │ │ ├── zsyscall_linux_mips64.go │ │ │ ├── zsyscall_linux_mips64le.go │ │ │ ├── zsyscall_linux_mipsle.go │ │ │ ├── zsyscall_linux_ppc.go │ │ │ ├── zsyscall_linux_ppc64.go │ │ │ ├── zsyscall_linux_ppc64le.go │ │ │ ├── zsyscall_linux_riscv64.go │ │ │ ├── zsyscall_linux_s390x.go │ │ │ ├── zsyscall_linux_sparc64.go │ │ │ ├── zsyscall_netbsd_386.go │ │ │ ├── zsyscall_netbsd_amd64.go │ │ │ ├── zsyscall_netbsd_arm.go │ │ │ ├── zsyscall_netbsd_arm64.go │ │ │ ├── zsyscall_openbsd_386.go │ │ │ ├── zsyscall_openbsd_386.s │ │ │ ├── zsyscall_openbsd_amd64.go │ │ │ ├── zsyscall_openbsd_amd64.s │ │ │ ├── zsyscall_openbsd_arm.go │ │ │ ├── zsyscall_openbsd_arm.s │ │ │ ├── zsyscall_openbsd_arm64.go │ │ │ ├── zsyscall_openbsd_arm64.s │ │ │ ├── zsyscall_openbsd_mips64.go │ │ │ ├── zsyscall_openbsd_mips64.s │ │ │ ├── zsyscall_openbsd_ppc64.go │ │ │ ├── zsyscall_openbsd_ppc64.s │ │ │ ├── zsyscall_openbsd_riscv64.go │ │ │ ├── zsyscall_openbsd_riscv64.s │ │ │ ├── zsyscall_solaris_amd64.go │ │ │ ├── zsyscall_zos_s390x.go │ │ │ ├── zsysctl_openbsd_386.go │ │ │ ├── zsysctl_openbsd_amd64.go │ │ │ ├── zsysctl_openbsd_arm.go │ │ │ ├── zsysctl_openbsd_arm64.go │ │ │ ├── zsysctl_openbsd_mips64.go │ │ │ ├── zsysctl_openbsd_ppc64.go │ │ │ ├── zsysctl_openbsd_riscv64.go │ │ │ ├── zsysnum_darwin_amd64.go │ │ │ ├── zsysnum_darwin_arm64.go │ │ │ ├── zsysnum_dragonfly_amd64.go │ │ │ ├── zsysnum_freebsd_386.go │ │ │ ├── zsysnum_freebsd_amd64.go │ │ │ ├── zsysnum_freebsd_arm.go │ │ │ ├── zsysnum_freebsd_arm64.go │ │ │ ├── zsysnum_freebsd_riscv64.go │ │ │ ├── zsysnum_linux_386.go │ │ │ ├── zsysnum_linux_amd64.go │ │ │ ├── zsysnum_linux_arm.go │ │ │ ├── zsysnum_linux_arm64.go │ │ │ ├── zsysnum_linux_loong64.go │ │ │ ├── zsysnum_linux_mips.go │ │ │ ├── zsysnum_linux_mips64.go │ │ │ ├── zsysnum_linux_mips64le.go │ │ │ ├── zsysnum_linux_mipsle.go │ │ │ ├── zsysnum_linux_ppc.go │ │ │ ├── zsysnum_linux_ppc64.go │ │ │ ├── zsysnum_linux_ppc64le.go │ │ │ ├── zsysnum_linux_riscv64.go │ │ │ ├── zsysnum_linux_s390x.go │ │ │ ├── zsysnum_linux_sparc64.go │ │ │ ├── zsysnum_netbsd_386.go │ │ │ ├── zsysnum_netbsd_amd64.go │ │ │ ├── zsysnum_netbsd_arm.go │ │ │ ├── zsysnum_netbsd_arm64.go │ │ │ ├── zsysnum_openbsd_386.go │ │ │ ├── zsysnum_openbsd_amd64.go │ │ │ ├── zsysnum_openbsd_arm.go │ │ │ ├── zsysnum_openbsd_arm64.go │ │ │ ├── zsysnum_openbsd_mips64.go │ │ │ ├── zsysnum_openbsd_ppc64.go │ │ │ ├── zsysnum_openbsd_riscv64.go │ │ │ ├── zsysnum_zos_s390x.go │ │ │ ├── ztypes_aix_ppc.go │ │ │ ├── ztypes_aix_ppc64.go │ │ │ ├── ztypes_darwin_amd64.go │ │ │ ├── ztypes_darwin_arm64.go │ │ │ ├── ztypes_dragonfly_amd64.go │ │ │ ├── ztypes_freebsd_386.go │ │ │ ├── ztypes_freebsd_amd64.go │ │ │ ├── ztypes_freebsd_arm.go │ │ │ ├── ztypes_freebsd_arm64.go │ │ │ ├── ztypes_freebsd_riscv64.go │ │ │ ├── ztypes_linux.go │ │ │ ├── ztypes_linux_386.go │ │ │ ├── ztypes_linux_amd64.go │ │ │ ├── ztypes_linux_arm.go │ │ │ ├── ztypes_linux_arm64.go │ │ │ ├── ztypes_linux_loong64.go │ │ │ ├── ztypes_linux_mips.go │ │ │ ├── ztypes_linux_mips64.go │ │ │ ├── ztypes_linux_mips64le.go │ │ │ ├── ztypes_linux_mipsle.go │ │ │ ├── ztypes_linux_ppc.go │ │ │ ├── ztypes_linux_ppc64.go │ │ │ ├── ztypes_linux_ppc64le.go │ │ │ ├── ztypes_linux_riscv64.go │ │ │ ├── ztypes_linux_s390x.go │ │ │ ├── ztypes_linux_sparc64.go │ │ │ ├── ztypes_netbsd_386.go │ │ │ ├── ztypes_netbsd_amd64.go │ │ │ ├── ztypes_netbsd_arm.go │ │ │ ├── ztypes_netbsd_arm64.go │ │ │ ├── ztypes_openbsd_386.go │ │ │ ├── ztypes_openbsd_amd64.go │ │ │ ├── ztypes_openbsd_arm.go │ │ │ ├── ztypes_openbsd_arm64.go │ │ │ ├── ztypes_openbsd_mips64.go │ │ │ ├── ztypes_openbsd_ppc64.go │ │ │ ├── ztypes_openbsd_riscv64.go │ │ │ ├── ztypes_solaris_amd64.go │ │ │ └── ztypes_zos_s390x.go │ │ └── windows/ │ │ ├── aliases.go │ │ ├── dll_windows.go │ │ ├── env_windows.go │ │ ├── eventlog.go │ │ ├── exec_windows.go │ │ ├── memory_windows.go │ │ ├── mkerrors.bash │ │ ├── mkknownfolderids.bash │ │ ├── mksyscall.go │ │ ├── race.go │ │ ├── race0.go │ │ ├── security_windows.go │ │ ├── service.go │ │ ├── setupapi_windows.go │ │ ├── str.go │ │ ├── syscall.go │ │ ├── syscall_windows.go │ │ ├── types_windows.go │ │ ├── types_windows_386.go │ │ ├── types_windows_amd64.go │ │ ├── types_windows_arm.go │ │ ├── types_windows_arm64.go │ │ ├── zerrors_windows.go │ │ ├── zknownfolderids_windows.go │ │ └── zsyscall_windows.go │ ├── term/ │ │ ├── CONTRIBUTING.md │ │ ├── LICENSE │ │ ├── PATENTS │ │ ├── README.md │ │ ├── codereview.cfg │ │ ├── term.go │ │ ├── term_plan9.go │ │ ├── term_unix.go │ │ ├── term_unix_bsd.go │ │ ├── term_unix_other.go │ │ ├── term_unsupported.go │ │ ├── term_windows.go │ │ └── terminal.go │ ├── text/ │ │ ├── LICENSE │ │ ├── PATENTS │ │ ├── encoding/ │ │ │ ├── charmap/ │ │ │ │ ├── charmap.go │ │ │ │ └── tables.go │ │ │ ├── encoding.go │ │ │ ├── htmlindex/ │ │ │ │ ├── htmlindex.go │ │ │ │ ├── map.go │ │ │ │ └── tables.go │ │ │ ├── internal/ │ │ │ │ ├── identifier/ │ │ │ │ │ ├── identifier.go │ │ │ │ │ └── mib.go │ │ │ │ └── internal.go │ │ │ ├── japanese/ │ │ │ │ ├── all.go │ │ │ │ ├── eucjp.go │ │ │ │ ├── iso2022jp.go │ │ │ │ ├── shiftjis.go │ │ │ │ └── tables.go │ │ │ ├── korean/ │ │ │ │ ├── euckr.go │ │ │ │ └── tables.go │ │ │ ├── simplifiedchinese/ │ │ │ │ ├── all.go │ │ │ │ ├── gbk.go │ │ │ │ ├── hzgb2312.go │ │ │ │ └── tables.go │ │ │ ├── traditionalchinese/ │ │ │ │ ├── big5.go │ │ │ │ └── tables.go │ │ │ └── unicode/ │ │ │ ├── override.go │ │ │ └── unicode.go │ │ ├── internal/ │ │ │ ├── language/ │ │ │ │ ├── common.go │ │ │ │ ├── compact/ │ │ │ │ │ ├── compact.go │ │ │ │ │ ├── language.go │ │ │ │ │ ├── parents.go │ │ │ │ │ ├── tables.go │ │ │ │ │ └── tags.go │ │ │ │ ├── compact.go │ │ │ │ ├── compose.go │ │ │ │ ├── coverage.go │ │ │ │ ├── language.go │ │ │ │ ├── lookup.go │ │ │ │ ├── match.go │ │ │ │ ├── parse.go │ │ │ │ ├── tables.go │ │ │ │ └── tags.go │ │ │ ├── tag/ │ │ │ │ └── tag.go │ │ │ └── utf8internal/ │ │ │ └── utf8internal.go │ │ ├── language/ │ │ │ ├── coverage.go │ │ │ ├── doc.go │ │ │ ├── language.go │ │ │ ├── match.go │ │ │ ├── parse.go │ │ │ ├── tables.go │ │ │ └── tags.go │ │ ├── runes/ │ │ │ ├── cond.go │ │ │ └── runes.go │ │ ├── secure/ │ │ │ └── bidirule/ │ │ │ ├── bidirule.go │ │ │ ├── bidirule10.0.0.go │ │ │ └── bidirule9.0.0.go │ │ ├── transform/ │ │ │ └── transform.go │ │ └── unicode/ │ │ ├── bidi/ │ │ │ ├── bidi.go │ │ │ ├── bracket.go │ │ │ ├── core.go │ │ │ ├── prop.go │ │ │ ├── tables10.0.0.go │ │ │ ├── tables11.0.0.go │ │ │ ├── tables12.0.0.go │ │ │ ├── tables13.0.0.go │ │ │ ├── tables15.0.0.go │ │ │ ├── tables9.0.0.go │ │ │ └── trieval.go │ │ └── norm/ │ │ ├── composition.go │ │ ├── forminfo.go │ │ ├── input.go │ │ ├── iter.go │ │ ├── normalize.go │ │ ├── readwriter.go │ │ ├── tables10.0.0.go │ │ ├── tables11.0.0.go │ │ ├── tables12.0.0.go │ │ ├── tables13.0.0.go │ │ ├── tables15.0.0.go │ │ ├── tables9.0.0.go │ │ ├── transform.go │ │ └── trie.go │ ├── time/ │ │ ├── AUTHORS │ │ ├── CONTRIBUTORS │ │ ├── LICENSE │ │ ├── PATENTS │ │ └── rate/ │ │ └── rate.go │ └── tools/ │ ├── LICENSE │ ├── PATENTS │ ├── cmd/ │ │ └── stringer/ │ │ └── stringer.go │ ├── go/ │ │ ├── ast/ │ │ │ ├── edge/ │ │ │ │ └── edge.go │ │ │ └── inspector/ │ │ │ ├── cursor.go │ │ │ ├── inspector.go │ │ │ ├── iter.go │ │ │ ├── typeof.go │ │ │ └── walk.go │ │ ├── gcexportdata/ │ │ │ ├── gcexportdata.go │ │ │ └── importer.go │ │ ├── packages/ │ │ │ ├── doc.go │ │ │ ├── external.go │ │ │ ├── golist.go │ │ │ ├── golist_overlay.go │ │ │ ├── loadmode_string.go │ │ │ ├── packages.go │ │ │ └── visit.go │ │ └── types/ │ │ ├── objectpath/ │ │ │ └── objectpath.go │ │ └── typeutil/ │ │ ├── callee.go │ │ ├── imports.go │ │ ├── map.go │ │ ├── methodsetcache.go │ │ └── ui.go │ └── internal/ │ ├── aliases/ │ │ ├── aliases.go │ │ └── aliases_go122.go │ ├── event/ │ │ ├── core/ │ │ │ ├── event.go │ │ │ ├── export.go │ │ │ └── fast.go │ │ ├── doc.go │ │ ├── event.go │ │ ├── keys/ │ │ │ ├── keys.go │ │ │ ├── standard.go │ │ │ └── util.go │ │ └── label/ │ │ └── label.go │ ├── gcimporter/ │ │ ├── bimport.go │ │ ├── exportdata.go │ │ ├── gcimporter.go │ │ ├── iexport.go │ │ ├── iimport.go │ │ ├── predeclared.go │ │ ├── support.go │ │ └── ureader_yes.go │ ├── gocommand/ │ │ ├── invoke.go │ │ ├── invoke_notunix.go │ │ ├── invoke_unix.go │ │ ├── vendor.go │ │ └── version.go │ ├── packagesinternal/ │ │ └── packages.go │ ├── pkgbits/ │ │ ├── codes.go │ │ ├── decoder.go │ │ ├── doc.go │ │ ├── encoder.go │ │ ├── flags.go │ │ ├── reloc.go │ │ ├── support.go │ │ ├── sync.go │ │ ├── syncmarker_string.go │ │ └── version.go │ ├── stdlib/ │ │ ├── deps.go │ │ ├── import.go │ │ ├── manifest.go │ │ └── stdlib.go │ ├── typeparams/ │ │ ├── common.go │ │ ├── coretype.go │ │ ├── free.go │ │ ├── normalize.go │ │ ├── termlist.go │ │ └── typeterm.go │ ├── typesinternal/ │ │ ├── classify_call.go │ │ ├── element.go │ │ ├── errorcode.go │ │ ├── errorcode_string.go │ │ ├── fx.go │ │ ├── isnamed.go │ │ ├── qualifier.go │ │ ├── recv.go │ │ ├── toonew.go │ │ ├── types.go │ │ ├── varkind.go │ │ └── zerovalue.go │ └── versions/ │ ├── features.go │ ├── gover.go │ ├── types.go │ └── versions.go ├── google.golang.org/ │ ├── appengine/ │ │ ├── LICENSE │ │ ├── internal/ │ │ │ ├── api.go │ │ │ ├── api_classic.go │ │ │ ├── api_common.go │ │ │ ├── app_id.go │ │ │ ├── base/ │ │ │ │ ├── api_base.pb.go │ │ │ │ └── api_base.proto │ │ │ ├── datastore/ │ │ │ │ ├── datastore_v3.pb.go │ │ │ │ └── datastore_v3.proto │ │ │ ├── identity.go │ │ │ ├── identity_classic.go │ │ │ ├── identity_flex.go │ │ │ ├── identity_vm.go │ │ │ ├── internal.go │ │ │ ├── log/ │ │ │ │ ├── log_service.pb.go │ │ │ │ └── log_service.proto │ │ │ ├── main.go │ │ │ ├── main_common.go │ │ │ ├── main_vm.go │ │ │ ├── metadata.go │ │ │ ├── net.go │ │ │ ├── regen.sh │ │ │ ├── remote_api/ │ │ │ │ ├── remote_api.pb.go │ │ │ │ └── remote_api.proto │ │ │ ├── transaction.go │ │ │ └── urlfetch/ │ │ │ ├── urlfetch_service.pb.go │ │ │ └── urlfetch_service.proto │ │ └── urlfetch/ │ │ └── urlfetch.go │ └── protobuf/ │ ├── LICENSE │ ├── PATENTS │ ├── encoding/ │ │ ├── prototext/ │ │ │ ├── decode.go │ │ │ ├── doc.go │ │ │ └── encode.go │ │ └── protowire/ │ │ └── wire.go │ ├── internal/ │ │ ├── descfmt/ │ │ │ └── stringer.go │ │ ├── descopts/ │ │ │ └── options.go │ │ ├── detrand/ │ │ │ └── rand.go │ │ ├── editiondefaults/ │ │ │ ├── defaults.go │ │ │ └── editions_defaults.binpb │ │ ├── encoding/ │ │ │ ├── defval/ │ │ │ │ └── default.go │ │ │ ├── messageset/ │ │ │ │ └── messageset.go │ │ │ ├── tag/ │ │ │ │ └── tag.go │ │ │ └── text/ │ │ │ ├── decode.go │ │ │ ├── decode_number.go │ │ │ ├── decode_string.go │ │ │ ├── decode_token.go │ │ │ ├── doc.go │ │ │ └── encode.go │ │ ├── errors/ │ │ │ ├── errors.go │ │ │ ├── is_go112.go │ │ │ └── is_go113.go │ │ ├── filedesc/ │ │ │ ├── build.go │ │ │ ├── desc.go │ │ │ ├── desc_init.go │ │ │ ├── desc_lazy.go │ │ │ ├── desc_list.go │ │ │ ├── desc_list_gen.go │ │ │ ├── editions.go │ │ │ └── placeholder.go │ │ ├── filetype/ │ │ │ └── build.go │ │ ├── flags/ │ │ │ ├── flags.go │ │ │ ├── proto_legacy_disable.go │ │ │ └── proto_legacy_enable.go │ │ ├── genid/ │ │ │ ├── any_gen.go │ │ │ ├── api_gen.go │ │ │ ├── descriptor_gen.go │ │ │ ├── doc.go │ │ │ ├── duration_gen.go │ │ │ ├── empty_gen.go │ │ │ ├── field_mask_gen.go │ │ │ ├── go_features_gen.go │ │ │ ├── goname.go │ │ │ ├── map_entry.go │ │ │ ├── source_context_gen.go │ │ │ ├── struct_gen.go │ │ │ ├── timestamp_gen.go │ │ │ ├── type_gen.go │ │ │ ├── wrappers.go │ │ │ └── wrappers_gen.go │ │ ├── impl/ │ │ │ ├── api_export.go │ │ │ ├── checkinit.go │ │ │ ├── codec_extension.go │ │ │ ├── codec_field.go │ │ │ ├── codec_gen.go │ │ │ ├── codec_map.go │ │ │ ├── codec_map_go111.go │ │ │ ├── codec_map_go112.go │ │ │ ├── codec_message.go │ │ │ ├── codec_messageset.go │ │ │ ├── codec_reflect.go │ │ │ ├── codec_tables.go │ │ │ ├── codec_unsafe.go │ │ │ ├── convert.go │ │ │ ├── convert_list.go │ │ │ ├── convert_map.go │ │ │ ├── decode.go │ │ │ ├── encode.go │ │ │ ├── enum.go │ │ │ ├── extension.go │ │ │ ├── legacy_enum.go │ │ │ ├── legacy_export.go │ │ │ ├── legacy_extension.go │ │ │ ├── legacy_file.go │ │ │ ├── legacy_message.go │ │ │ ├── merge.go │ │ │ ├── merge_gen.go │ │ │ ├── message.go │ │ │ ├── message_reflect.go │ │ │ ├── message_reflect_field.go │ │ │ ├── message_reflect_gen.go │ │ │ ├── pointer_reflect.go │ │ │ ├── pointer_unsafe.go │ │ │ ├── validate.go │ │ │ └── weak.go │ │ ├── order/ │ │ │ ├── order.go │ │ │ └── range.go │ │ ├── pragma/ │ │ │ └── pragma.go │ │ ├── set/ │ │ │ └── ints.go │ │ ├── strs/ │ │ │ ├── strings.go │ │ │ ├── strings_pure.go │ │ │ ├── strings_unsafe_go120.go │ │ │ └── strings_unsafe_go121.go │ │ └── version/ │ │ └── version.go │ ├── proto/ │ │ ├── checkinit.go │ │ ├── decode.go │ │ ├── decode_gen.go │ │ ├── doc.go │ │ ├── encode.go │ │ ├── encode_gen.go │ │ ├── equal.go │ │ ├── extension.go │ │ ├── merge.go │ │ ├── messageset.go │ │ ├── proto.go │ │ ├── proto_methods.go │ │ ├── proto_reflect.go │ │ ├── reset.go │ │ ├── size.go │ │ ├── size_gen.go │ │ └── wrappers.go │ ├── reflect/ │ │ ├── protodesc/ │ │ │ ├── desc.go │ │ │ ├── desc_init.go │ │ │ ├── desc_resolve.go │ │ │ ├── desc_validate.go │ │ │ ├── editions.go │ │ │ └── proto.go │ │ ├── protoreflect/ │ │ │ ├── methods.go │ │ │ ├── proto.go │ │ │ ├── source.go │ │ │ ├── source_gen.go │ │ │ ├── type.go │ │ │ ├── value.go │ │ │ ├── value_equal.go │ │ │ ├── value_pure.go │ │ │ ├── value_union.go │ │ │ ├── value_unsafe_go120.go │ │ │ └── value_unsafe_go121.go │ │ └── protoregistry/ │ │ └── registry.go │ ├── runtime/ │ │ ├── protoiface/ │ │ │ ├── legacy.go │ │ │ └── methods.go │ │ └── protoimpl/ │ │ ├── impl.go │ │ └── version.go │ └── types/ │ ├── descriptorpb/ │ │ └── descriptor.pb.go │ ├── gofeaturespb/ │ │ ├── go_features.pb.go │ │ └── go_features.proto │ └── known/ │ ├── anypb/ │ │ └── any.pb.go │ ├── durationpb/ │ │ └── duration.pb.go │ └── timestamppb/ │ └── timestamp.pb.go ├── gopkg.in/ │ ├── inf.v0/ │ │ ├── LICENSE │ │ ├── dec.go │ │ └── rounder.go │ ├── yaml.v2/ │ │ ├── .travis.yml │ │ ├── LICENSE │ │ ├── LICENSE.libyaml │ │ ├── NOTICE │ │ ├── README.md │ │ ├── apic.go │ │ ├── decode.go │ │ ├── emitterc.go │ │ ├── encode.go │ │ ├── parserc.go │ │ ├── readerc.go │ │ ├── resolve.go │ │ ├── scannerc.go │ │ ├── sorter.go │ │ ├── writerc.go │ │ ├── yaml.go │ │ ├── yamlh.go │ │ └── yamlprivateh.go │ └── yaml.v3/ │ ├── LICENSE │ ├── NOTICE │ ├── README.md │ ├── apic.go │ ├── decode.go │ ├── emitterc.go │ ├── encode.go │ ├── parserc.go │ ├── readerc.go │ ├── resolve.go │ ├── scannerc.go │ ├── sorter.go │ ├── writerc.go │ ├── yaml.go │ ├── yamlh.go │ └── yamlprivateh.go ├── k8s.io/ │ ├── api/ │ │ ├── LICENSE │ │ ├── admissionregistration/ │ │ │ ├── v1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ └── zz_generated.deepcopy.go │ │ │ ├── v1alpha1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ └── zz_generated.deepcopy.go │ │ │ └── v1beta1/ │ │ │ ├── doc.go │ │ │ ├── generated.pb.go │ │ │ ├── generated.proto │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ ├── types_swagger_doc_generated.go │ │ │ ├── zz_generated.deepcopy.go │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ ├── apidiscovery/ │ │ │ └── v2beta1/ │ │ │ ├── doc.go │ │ │ ├── generated.pb.go │ │ │ ├── generated.proto │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ ├── zz_generated.deepcopy.go │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ ├── apiserverinternal/ │ │ │ └── v1alpha1/ │ │ │ ├── doc.go │ │ │ ├── generated.pb.go │ │ │ ├── generated.proto │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ ├── types_swagger_doc_generated.go │ │ │ └── zz_generated.deepcopy.go │ │ ├── apps/ │ │ │ ├── v1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ └── zz_generated.deepcopy.go │ │ │ ├── v1beta1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ ├── zz_generated.deepcopy.go │ │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ │ └── v1beta2/ │ │ │ ├── doc.go │ │ │ ├── generated.pb.go │ │ │ ├── generated.proto │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ ├── types_swagger_doc_generated.go │ │ │ ├── zz_generated.deepcopy.go │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ ├── authentication/ │ │ │ ├── v1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ └── zz_generated.deepcopy.go │ │ │ ├── v1alpha1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ ├── zz_generated.deepcopy.go │ │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ │ └── v1beta1/ │ │ │ ├── doc.go │ │ │ ├── generated.pb.go │ │ │ ├── generated.proto │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ ├── types_swagger_doc_generated.go │ │ │ ├── zz_generated.deepcopy.go │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ ├── authorization/ │ │ │ ├── v1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ └── zz_generated.deepcopy.go │ │ │ └── v1beta1/ │ │ │ ├── doc.go │ │ │ ├── generated.pb.go │ │ │ ├── generated.proto │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ ├── types_swagger_doc_generated.go │ │ │ ├── zz_generated.deepcopy.go │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ ├── autoscaling/ │ │ │ ├── v1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ └── zz_generated.deepcopy.go │ │ │ ├── v2/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ └── zz_generated.deepcopy.go │ │ │ ├── v2beta1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ ├── zz_generated.deepcopy.go │ │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ │ └── v2beta2/ │ │ │ ├── doc.go │ │ │ ├── generated.pb.go │ │ │ ├── generated.proto │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ ├── types_swagger_doc_generated.go │ │ │ ├── zz_generated.deepcopy.go │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ ├── batch/ │ │ │ ├── v1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ └── zz_generated.deepcopy.go │ │ │ └── v1beta1/ │ │ │ ├── doc.go │ │ │ ├── generated.pb.go │ │ │ ├── generated.proto │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ ├── types_swagger_doc_generated.go │ │ │ ├── zz_generated.deepcopy.go │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ ├── certificates/ │ │ │ ├── v1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ └── zz_generated.deepcopy.go │ │ │ ├── v1alpha1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ ├── zz_generated.deepcopy.go │ │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ │ └── v1beta1/ │ │ │ ├── doc.go │ │ │ ├── generated.pb.go │ │ │ ├── generated.proto │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ ├── types_swagger_doc_generated.go │ │ │ ├── zz_generated.deepcopy.go │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ ├── coordination/ │ │ │ ├── v1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ └── zz_generated.deepcopy.go │ │ │ └── v1beta1/ │ │ │ ├── doc.go │ │ │ ├── generated.pb.go │ │ │ ├── generated.proto │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ ├── types_swagger_doc_generated.go │ │ │ ├── zz_generated.deepcopy.go │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ ├── core/ │ │ │ └── v1/ │ │ │ ├── annotation_key_constants.go │ │ │ ├── doc.go │ │ │ ├── generated.pb.go │ │ │ ├── generated.proto │ │ │ ├── lifecycle.go │ │ │ ├── objectreference.go │ │ │ ├── register.go │ │ │ ├── resource.go │ │ │ ├── taint.go │ │ │ ├── toleration.go │ │ │ ├── types.go │ │ │ ├── types_swagger_doc_generated.go │ │ │ ├── well_known_labels.go │ │ │ ├── well_known_taints.go │ │ │ └── zz_generated.deepcopy.go │ │ ├── discovery/ │ │ │ ├── v1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ ├── well_known_labels.go │ │ │ │ └── zz_generated.deepcopy.go │ │ │ └── v1beta1/ │ │ │ ├── doc.go │ │ │ ├── generated.pb.go │ │ │ ├── generated.proto │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ ├── types_swagger_doc_generated.go │ │ │ ├── well_known_labels.go │ │ │ ├── zz_generated.deepcopy.go │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ ├── events/ │ │ │ ├── v1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ └── zz_generated.deepcopy.go │ │ │ └── v1beta1/ │ │ │ ├── doc.go │ │ │ ├── generated.pb.go │ │ │ ├── generated.proto │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ ├── types_swagger_doc_generated.go │ │ │ ├── zz_generated.deepcopy.go │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ ├── extensions/ │ │ │ └── v1beta1/ │ │ │ ├── doc.go │ │ │ ├── generated.pb.go │ │ │ ├── generated.proto │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ ├── types_swagger_doc_generated.go │ │ │ ├── zz_generated.deepcopy.go │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ ├── flowcontrol/ │ │ │ ├── v1alpha1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ ├── zz_generated.deepcopy.go │ │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ │ ├── v1beta1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ ├── zz_generated.deepcopy.go │ │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ │ ├── v1beta2/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ ├── zz_generated.deepcopy.go │ │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ │ └── v1beta3/ │ │ │ ├── doc.go │ │ │ ├── generated.pb.go │ │ │ ├── generated.proto │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ ├── types_swagger_doc_generated.go │ │ │ ├── zz_generated.deepcopy.go │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ ├── networking/ │ │ │ ├── v1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ ├── well_known_annotations.go │ │ │ │ └── zz_generated.deepcopy.go │ │ │ ├── v1alpha1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ ├── well_known_labels.go │ │ │ │ ├── zz_generated.deepcopy.go │ │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ │ └── v1beta1/ │ │ │ ├── doc.go │ │ │ ├── generated.pb.go │ │ │ ├── generated.proto │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ ├── types_swagger_doc_generated.go │ │ │ ├── well_known_annotations.go │ │ │ ├── zz_generated.deepcopy.go │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ ├── node/ │ │ │ ├── v1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ └── zz_generated.deepcopy.go │ │ │ ├── v1alpha1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ └── zz_generated.deepcopy.go │ │ │ └── v1beta1/ │ │ │ ├── doc.go │ │ │ ├── generated.pb.go │ │ │ ├── generated.proto │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ ├── types_swagger_doc_generated.go │ │ │ ├── zz_generated.deepcopy.go │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ ├── policy/ │ │ │ ├── v1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ └── zz_generated.deepcopy.go │ │ │ └── v1beta1/ │ │ │ ├── doc.go │ │ │ ├── generated.pb.go │ │ │ ├── generated.proto │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ ├── types_swagger_doc_generated.go │ │ │ ├── zz_generated.deepcopy.go │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ ├── rbac/ │ │ │ ├── v1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ └── zz_generated.deepcopy.go │ │ │ ├── v1alpha1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ └── zz_generated.deepcopy.go │ │ │ └── v1beta1/ │ │ │ ├── doc.go │ │ │ ├── generated.pb.go │ │ │ ├── generated.proto │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ ├── types_swagger_doc_generated.go │ │ │ ├── zz_generated.deepcopy.go │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ ├── resource/ │ │ │ └── v1alpha2/ │ │ │ ├── doc.go │ │ │ ├── generated.pb.go │ │ │ ├── generated.proto │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ ├── types_swagger_doc_generated.go │ │ │ └── zz_generated.deepcopy.go │ │ ├── scheduling/ │ │ │ ├── v1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ └── zz_generated.deepcopy.go │ │ │ ├── v1alpha1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ └── zz_generated.deepcopy.go │ │ │ └── v1beta1/ │ │ │ ├── doc.go │ │ │ ├── generated.pb.go │ │ │ ├── generated.proto │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ ├── types_swagger_doc_generated.go │ │ │ ├── zz_generated.deepcopy.go │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ └── storage/ │ │ ├── v1/ │ │ │ ├── doc.go │ │ │ ├── generated.pb.go │ │ │ ├── generated.proto │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ ├── types_swagger_doc_generated.go │ │ │ └── zz_generated.deepcopy.go │ │ ├── v1alpha1/ │ │ │ ├── doc.go │ │ │ ├── generated.pb.go │ │ │ ├── generated.proto │ │ │ ├── register.go │ │ │ ├── types.go │ │ │ ├── types_swagger_doc_generated.go │ │ │ ├── zz_generated.deepcopy.go │ │ │ └── zz_generated.prerelease-lifecycle.go │ │ └── v1beta1/ │ │ ├── doc.go │ │ ├── generated.pb.go │ │ ├── generated.proto │ │ ├── register.go │ │ ├── types.go │ │ ├── types_swagger_doc_generated.go │ │ ├── zz_generated.deepcopy.go │ │ └── zz_generated.prerelease-lifecycle.go │ ├── apimachinery/ │ │ ├── LICENSE │ │ ├── pkg/ │ │ │ ├── api/ │ │ │ │ ├── equality/ │ │ │ │ │ └── semantic.go │ │ │ │ ├── errors/ │ │ │ │ │ ├── OWNERS │ │ │ │ │ ├── doc.go │ │ │ │ │ └── errors.go │ │ │ │ ├── meta/ │ │ │ │ │ ├── OWNERS │ │ │ │ │ ├── conditions.go │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── errors.go │ │ │ │ │ ├── firsthit_restmapper.go │ │ │ │ │ ├── help.go │ │ │ │ │ ├── interfaces.go │ │ │ │ │ ├── lazy.go │ │ │ │ │ ├── meta.go │ │ │ │ │ ├── multirestmapper.go │ │ │ │ │ ├── priority.go │ │ │ │ │ └── restmapper.go │ │ │ │ ├── resource/ │ │ │ │ │ ├── OWNERS │ │ │ │ │ ├── amount.go │ │ │ │ │ ├── generated.pb.go │ │ │ │ │ ├── generated.proto │ │ │ │ │ ├── math.go │ │ │ │ │ ├── quantity.go │ │ │ │ │ ├── quantity_proto.go │ │ │ │ │ ├── scale_int.go │ │ │ │ │ ├── suffix.go │ │ │ │ │ └── zz_generated.deepcopy.go │ │ │ │ └── validation/ │ │ │ │ ├── doc.go │ │ │ │ ├── generic.go │ │ │ │ └── objectmeta.go │ │ │ ├── apis/ │ │ │ │ └── meta/ │ │ │ │ └── v1/ │ │ │ │ ├── OWNERS │ │ │ │ ├── controller_ref.go │ │ │ │ ├── conversion.go │ │ │ │ ├── deepcopy.go │ │ │ │ ├── doc.go │ │ │ │ ├── duration.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── group_version.go │ │ │ │ ├── helpers.go │ │ │ │ ├── labels.go │ │ │ │ ├── meta.go │ │ │ │ ├── micro_time.go │ │ │ │ ├── micro_time_fuzz.go │ │ │ │ ├── micro_time_proto.go │ │ │ │ ├── register.go │ │ │ │ ├── time.go │ │ │ │ ├── time_fuzz.go │ │ │ │ ├── time_proto.go │ │ │ │ ├── types.go │ │ │ │ ├── types_swagger_doc_generated.go │ │ │ │ ├── unstructured/ │ │ │ │ │ ├── helpers.go │ │ │ │ │ ├── unstructured.go │ │ │ │ │ ├── unstructured_list.go │ │ │ │ │ ├── unstructuredscheme/ │ │ │ │ │ │ └── scheme.go │ │ │ │ │ └── zz_generated.deepcopy.go │ │ │ │ ├── validation/ │ │ │ │ │ └── validation.go │ │ │ │ ├── watch.go │ │ │ │ ├── zz_generated.conversion.go │ │ │ │ ├── zz_generated.deepcopy.go │ │ │ │ └── zz_generated.defaults.go │ │ │ ├── conversion/ │ │ │ │ ├── converter.go │ │ │ │ ├── deep_equal.go │ │ │ │ ├── doc.go │ │ │ │ ├── helper.go │ │ │ │ └── queryparams/ │ │ │ │ ├── convert.go │ │ │ │ └── doc.go │ │ │ ├── fields/ │ │ │ │ ├── doc.go │ │ │ │ ├── fields.go │ │ │ │ ├── requirements.go │ │ │ │ └── selector.go │ │ │ ├── labels/ │ │ │ │ ├── doc.go │ │ │ │ ├── labels.go │ │ │ │ ├── selector.go │ │ │ │ └── zz_generated.deepcopy.go │ │ │ ├── runtime/ │ │ │ │ ├── allocator.go │ │ │ │ ├── codec.go │ │ │ │ ├── codec_check.go │ │ │ │ ├── conversion.go │ │ │ │ ├── converter.go │ │ │ │ ├── doc.go │ │ │ │ ├── embedded.go │ │ │ │ ├── error.go │ │ │ │ ├── extension.go │ │ │ │ ├── generated.pb.go │ │ │ │ ├── generated.proto │ │ │ │ ├── helper.go │ │ │ │ ├── interfaces.go │ │ │ │ ├── mapper.go │ │ │ │ ├── negotiate.go │ │ │ │ ├── register.go │ │ │ │ ├── schema/ │ │ │ │ │ ├── generated.pb.go │ │ │ │ │ ├── generated.proto │ │ │ │ │ ├── group_version.go │ │ │ │ │ └── interfaces.go │ │ │ │ ├── scheme.go │ │ │ │ ├── scheme_builder.go │ │ │ │ ├── serializer/ │ │ │ │ │ ├── codec_factory.go │ │ │ │ │ ├── json/ │ │ │ │ │ │ ├── json.go │ │ │ │ │ │ └── meta.go │ │ │ │ │ ├── negotiated_codec.go │ │ │ │ │ ├── protobuf/ │ │ │ │ │ │ ├── doc.go │ │ │ │ │ │ └── protobuf.go │ │ │ │ │ ├── recognizer/ │ │ │ │ │ │ └── recognizer.go │ │ │ │ │ ├── streaming/ │ │ │ │ │ │ └── streaming.go │ │ │ │ │ └── versioning/ │ │ │ │ │ └── versioning.go │ │ │ │ ├── swagger_doc_generator.go │ │ │ │ ├── types.go │ │ │ │ ├── types_proto.go │ │ │ │ └── zz_generated.deepcopy.go │ │ │ ├── selection/ │ │ │ │ └── operator.go │ │ │ ├── types/ │ │ │ │ ├── doc.go │ │ │ │ ├── namespacedname.go │ │ │ │ ├── nodename.go │ │ │ │ ├── patch.go │ │ │ │ └── uid.go │ │ │ ├── util/ │ │ │ │ ├── duration/ │ │ │ │ │ └── duration.go │ │ │ │ ├── errors/ │ │ │ │ │ ├── doc.go │ │ │ │ │ └── errors.go │ │ │ │ ├── framer/ │ │ │ │ │ └── framer.go │ │ │ │ ├── httpstream/ │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── httpstream.go │ │ │ │ │ └── spdy/ │ │ │ │ │ ├── connection.go │ │ │ │ │ ├── roundtripper.go │ │ │ │ │ └── upgrade.go │ │ │ │ ├── intstr/ │ │ │ │ │ ├── generated.pb.go │ │ │ │ │ ├── generated.proto │ │ │ │ │ ├── instr_fuzz.go │ │ │ │ │ └── intstr.go │ │ │ │ ├── json/ │ │ │ │ │ └── json.go │ │ │ │ ├── managedfields/ │ │ │ │ │ ├── endpoints.yaml │ │ │ │ │ ├── extract.go │ │ │ │ │ ├── fieldmanager.go │ │ │ │ │ ├── gvkparser.go │ │ │ │ │ ├── internal/ │ │ │ │ │ │ ├── atmostevery.go │ │ │ │ │ │ ├── buildmanagerinfo.go │ │ │ │ │ │ ├── capmanagers.go │ │ │ │ │ │ ├── conflict.go │ │ │ │ │ │ ├── fieldmanager.go │ │ │ │ │ │ ├── fields.go │ │ │ │ │ │ ├── lastapplied.go │ │ │ │ │ │ ├── lastappliedmanager.go │ │ │ │ │ │ ├── lastappliedupdater.go │ │ │ │ │ │ ├── managedfields.go │ │ │ │ │ │ ├── managedfieldsupdater.go │ │ │ │ │ │ ├── manager.go │ │ │ │ │ │ ├── pathelement.go │ │ │ │ │ │ ├── skipnonapplied.go │ │ │ │ │ │ ├── stripmeta.go │ │ │ │ │ │ ├── structuredmerge.go │ │ │ │ │ │ ├── typeconverter.go │ │ │ │ │ │ └── versionconverter.go │ │ │ │ │ ├── node.yaml │ │ │ │ │ ├── pod.yaml │ │ │ │ │ ├── scalehandler.go │ │ │ │ │ └── typeconverter.go │ │ │ │ ├── naming/ │ │ │ │ │ └── from_stack.go │ │ │ │ ├── net/ │ │ │ │ │ ├── http.go │ │ │ │ │ ├── interface.go │ │ │ │ │ ├── port_range.go │ │ │ │ │ ├── port_split.go │ │ │ │ │ └── util.go │ │ │ │ ├── remotecommand/ │ │ │ │ │ └── constants.go │ │ │ │ ├── runtime/ │ │ │ │ │ └── runtime.go │ │ │ │ ├── sets/ │ │ │ │ │ ├── byte.go │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── empty.go │ │ │ │ │ ├── int.go │ │ │ │ │ ├── int32.go │ │ │ │ │ ├── int64.go │ │ │ │ │ ├── ordered.go │ │ │ │ │ ├── set.go │ │ │ │ │ └── string.go │ │ │ │ ├── validation/ │ │ │ │ │ ├── field/ │ │ │ │ │ │ ├── errors.go │ │ │ │ │ │ └── path.go │ │ │ │ │ └── validation.go │ │ │ │ ├── wait/ │ │ │ │ │ ├── backoff.go │ │ │ │ │ ├── delay.go │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── error.go │ │ │ │ │ ├── loop.go │ │ │ │ │ ├── poll.go │ │ │ │ │ ├── timer.go │ │ │ │ │ └── wait.go │ │ │ │ └── yaml/ │ │ │ │ └── decoder.go │ │ │ ├── version/ │ │ │ │ ├── doc.go │ │ │ │ ├── helpers.go │ │ │ │ └── types.go │ │ │ └── watch/ │ │ │ ├── doc.go │ │ │ ├── filter.go │ │ │ ├── mux.go │ │ │ ├── streamwatcher.go │ │ │ ├── watch.go │ │ │ └── zz_generated.deepcopy.go │ │ └── third_party/ │ │ └── forked/ │ │ └── golang/ │ │ ├── LICENSE │ │ ├── PATENTS │ │ ├── netutil/ │ │ │ └── addr.go │ │ └── reflect/ │ │ └── deep_equal.go │ ├── cli-runtime/ │ │ ├── LICENSE │ │ └── pkg/ │ │ ├── genericclioptions/ │ │ │ ├── builder_flags.go │ │ │ ├── builder_flags_fake.go │ │ │ ├── client_config.go │ │ │ ├── command_headers.go │ │ │ ├── config_flags.go │ │ │ ├── config_flags_fake.go │ │ │ ├── doc.go │ │ │ ├── filename_flags.go │ │ │ ├── io_options.go │ │ │ ├── json_yaml_flags.go │ │ │ ├── jsonpath_flags.go │ │ │ ├── kube_template_flags.go │ │ │ ├── name_flags.go │ │ │ ├── print_flags.go │ │ │ ├── record_flags.go │ │ │ └── template_flags.go │ │ ├── printers/ │ │ │ ├── discard.go │ │ │ ├── doc.go │ │ │ ├── interface.go │ │ │ ├── json.go │ │ │ ├── jsonpath.go │ │ │ ├── managedfields.go │ │ │ ├── name.go │ │ │ ├── sourcechecker.go │ │ │ ├── tableprinter.go │ │ │ ├── tabwriter.go │ │ │ ├── template.go │ │ │ ├── terminal.go │ │ │ ├── typesetter.go │ │ │ ├── warningprinter.go │ │ │ └── yaml.go │ │ └── resource/ │ │ ├── builder.go │ │ ├── client.go │ │ ├── crd_finder.go │ │ ├── doc.go │ │ ├── fake.go │ │ ├── fallback_query_param_verifier.go │ │ ├── helper.go │ │ ├── interfaces.go │ │ ├── kustomizevisitor.go │ │ ├── mapper.go │ │ ├── metadata_decoder.go │ │ ├── query_param_verifier.go │ │ ├── query_param_verifier_v3.go │ │ ├── result.go │ │ ├── scheme.go │ │ ├── selector.go │ │ └── visitor.go │ ├── client-go/ │ │ ├── LICENSE │ │ ├── applyconfigurations/ │ │ │ ├── admissionregistration/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── matchcondition.go │ │ │ │ │ ├── mutatingwebhook.go │ │ │ │ │ ├── mutatingwebhookconfiguration.go │ │ │ │ │ ├── rule.go │ │ │ │ │ ├── rulewithoperations.go │ │ │ │ │ ├── servicereference.go │ │ │ │ │ ├── validatingwebhook.go │ │ │ │ │ ├── validatingwebhookconfiguration.go │ │ │ │ │ └── webhookclientconfig.go │ │ │ │ ├── v1alpha1/ │ │ │ │ │ ├── auditannotation.go │ │ │ │ │ ├── expressionwarning.go │ │ │ │ │ ├── matchcondition.go │ │ │ │ │ ├── matchresources.go │ │ │ │ │ ├── namedrulewithoperations.go │ │ │ │ │ ├── paramkind.go │ │ │ │ │ ├── paramref.go │ │ │ │ │ ├── typechecking.go │ │ │ │ │ ├── validatingadmissionpolicy.go │ │ │ │ │ ├── validatingadmissionpolicybinding.go │ │ │ │ │ ├── validatingadmissionpolicybindingspec.go │ │ │ │ │ ├── validatingadmissionpolicyspec.go │ │ │ │ │ ├── validatingadmissionpolicystatus.go │ │ │ │ │ └── validation.go │ │ │ │ └── v1beta1/ │ │ │ │ ├── matchcondition.go │ │ │ │ ├── mutatingwebhook.go │ │ │ │ ├── mutatingwebhookconfiguration.go │ │ │ │ ├── servicereference.go │ │ │ │ ├── validatingwebhook.go │ │ │ │ ├── validatingwebhookconfiguration.go │ │ │ │ └── webhookclientconfig.go │ │ │ ├── apiserverinternal/ │ │ │ │ └── v1alpha1/ │ │ │ │ ├── serverstorageversion.go │ │ │ │ ├── storageversion.go │ │ │ │ ├── storageversioncondition.go │ │ │ │ └── storageversionstatus.go │ │ │ ├── apps/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── controllerrevision.go │ │ │ │ │ ├── daemonset.go │ │ │ │ │ ├── daemonsetcondition.go │ │ │ │ │ ├── daemonsetspec.go │ │ │ │ │ ├── daemonsetstatus.go │ │ │ │ │ ├── daemonsetupdatestrategy.go │ │ │ │ │ ├── deployment.go │ │ │ │ │ ├── deploymentcondition.go │ │ │ │ │ ├── deploymentspec.go │ │ │ │ │ ├── deploymentstatus.go │ │ │ │ │ ├── deploymentstrategy.go │ │ │ │ │ ├── replicaset.go │ │ │ │ │ ├── replicasetcondition.go │ │ │ │ │ ├── replicasetspec.go │ │ │ │ │ ├── replicasetstatus.go │ │ │ │ │ ├── rollingupdatedaemonset.go │ │ │ │ │ ├── rollingupdatedeployment.go │ │ │ │ │ ├── rollingupdatestatefulsetstrategy.go │ │ │ │ │ ├── statefulset.go │ │ │ │ │ ├── statefulsetcondition.go │ │ │ │ │ ├── statefulsetordinals.go │ │ │ │ │ ├── statefulsetpersistentvolumeclaimretentionpolicy.go │ │ │ │ │ ├── statefulsetspec.go │ │ │ │ │ ├── statefulsetstatus.go │ │ │ │ │ └── statefulsetupdatestrategy.go │ │ │ │ ├── v1beta1/ │ │ │ │ │ ├── controllerrevision.go │ │ │ │ │ ├── deployment.go │ │ │ │ │ ├── deploymentcondition.go │ │ │ │ │ ├── deploymentspec.go │ │ │ │ │ ├── deploymentstatus.go │ │ │ │ │ ├── deploymentstrategy.go │ │ │ │ │ ├── rollbackconfig.go │ │ │ │ │ ├── rollingupdatedeployment.go │ │ │ │ │ ├── rollingupdatestatefulsetstrategy.go │ │ │ │ │ ├── statefulset.go │ │ │ │ │ ├── statefulsetcondition.go │ │ │ │ │ ├── statefulsetordinals.go │ │ │ │ │ ├── statefulsetpersistentvolumeclaimretentionpolicy.go │ │ │ │ │ ├── statefulsetspec.go │ │ │ │ │ ├── statefulsetstatus.go │ │ │ │ │ └── statefulsetupdatestrategy.go │ │ │ │ └── v1beta2/ │ │ │ │ ├── controllerrevision.go │ │ │ │ ├── daemonset.go │ │ │ │ ├── daemonsetcondition.go │ │ │ │ ├── daemonsetspec.go │ │ │ │ ├── daemonsetstatus.go │ │ │ │ ├── daemonsetupdatestrategy.go │ │ │ │ ├── deployment.go │ │ │ │ ├── deploymentcondition.go │ │ │ │ ├── deploymentspec.go │ │ │ │ ├── deploymentstatus.go │ │ │ │ ├── deploymentstrategy.go │ │ │ │ ├── replicaset.go │ │ │ │ ├── replicasetcondition.go │ │ │ │ ├── replicasetspec.go │ │ │ │ ├── replicasetstatus.go │ │ │ │ ├── rollingupdatedaemonset.go │ │ │ │ ├── rollingupdatedeployment.go │ │ │ │ ├── rollingupdatestatefulsetstrategy.go │ │ │ │ ├── scale.go │ │ │ │ ├── statefulset.go │ │ │ │ ├── statefulsetcondition.go │ │ │ │ ├── statefulsetordinals.go │ │ │ │ ├── statefulsetpersistentvolumeclaimretentionpolicy.go │ │ │ │ ├── statefulsetspec.go │ │ │ │ ├── statefulsetstatus.go │ │ │ │ └── statefulsetupdatestrategy.go │ │ │ ├── autoscaling/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── crossversionobjectreference.go │ │ │ │ │ ├── horizontalpodautoscaler.go │ │ │ │ │ ├── horizontalpodautoscalerspec.go │ │ │ │ │ ├── horizontalpodautoscalerstatus.go │ │ │ │ │ ├── scale.go │ │ │ │ │ ├── scalespec.go │ │ │ │ │ └── scalestatus.go │ │ │ │ ├── v2/ │ │ │ │ │ ├── containerresourcemetricsource.go │ │ │ │ │ ├── containerresourcemetricstatus.go │ │ │ │ │ ├── crossversionobjectreference.go │ │ │ │ │ ├── externalmetricsource.go │ │ │ │ │ ├── externalmetricstatus.go │ │ │ │ │ ├── horizontalpodautoscaler.go │ │ │ │ │ ├── horizontalpodautoscalerbehavior.go │ │ │ │ │ ├── horizontalpodautoscalercondition.go │ │ │ │ │ ├── horizontalpodautoscalerspec.go │ │ │ │ │ ├── horizontalpodautoscalerstatus.go │ │ │ │ │ ├── hpascalingpolicy.go │ │ │ │ │ ├── hpascalingrules.go │ │ │ │ │ ├── metricidentifier.go │ │ │ │ │ ├── metricspec.go │ │ │ │ │ ├── metricstatus.go │ │ │ │ │ ├── metrictarget.go │ │ │ │ │ ├── metricvaluestatus.go │ │ │ │ │ ├── objectmetricsource.go │ │ │ │ │ ├── objectmetricstatus.go │ │ │ │ │ ├── podsmetricsource.go │ │ │ │ │ ├── podsmetricstatus.go │ │ │ │ │ ├── resourcemetricsource.go │ │ │ │ │ └── resourcemetricstatus.go │ │ │ │ ├── v2beta1/ │ │ │ │ │ ├── containerresourcemetricsource.go │ │ │ │ │ ├── containerresourcemetricstatus.go │ │ │ │ │ ├── crossversionobjectreference.go │ │ │ │ │ ├── externalmetricsource.go │ │ │ │ │ ├── externalmetricstatus.go │ │ │ │ │ ├── horizontalpodautoscaler.go │ │ │ │ │ ├── horizontalpodautoscalercondition.go │ │ │ │ │ ├── horizontalpodautoscalerspec.go │ │ │ │ │ ├── horizontalpodautoscalerstatus.go │ │ │ │ │ ├── metricspec.go │ │ │ │ │ ├── metricstatus.go │ │ │ │ │ ├── objectmetricsource.go │ │ │ │ │ ├── objectmetricstatus.go │ │ │ │ │ ├── podsmetricsource.go │ │ │ │ │ ├── podsmetricstatus.go │ │ │ │ │ ├── resourcemetricsource.go │ │ │ │ │ └── resourcemetricstatus.go │ │ │ │ └── v2beta2/ │ │ │ │ ├── containerresourcemetricsource.go │ │ │ │ ├── containerresourcemetricstatus.go │ │ │ │ ├── crossversionobjectreference.go │ │ │ │ ├── externalmetricsource.go │ │ │ │ ├── externalmetricstatus.go │ │ │ │ ├── horizontalpodautoscaler.go │ │ │ │ ├── horizontalpodautoscalerbehavior.go │ │ │ │ ├── horizontalpodautoscalercondition.go │ │ │ │ ├── horizontalpodautoscalerspec.go │ │ │ │ ├── horizontalpodautoscalerstatus.go │ │ │ │ ├── hpascalingpolicy.go │ │ │ │ ├── hpascalingrules.go │ │ │ │ ├── metricidentifier.go │ │ │ │ ├── metricspec.go │ │ │ │ ├── metricstatus.go │ │ │ │ ├── metrictarget.go │ │ │ │ ├── metricvaluestatus.go │ │ │ │ ├── objectmetricsource.go │ │ │ │ ├── objectmetricstatus.go │ │ │ │ ├── podsmetricsource.go │ │ │ │ ├── podsmetricstatus.go │ │ │ │ ├── resourcemetricsource.go │ │ │ │ └── resourcemetricstatus.go │ │ │ ├── batch/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── cronjob.go │ │ │ │ │ ├── cronjobspec.go │ │ │ │ │ ├── cronjobstatus.go │ │ │ │ │ ├── job.go │ │ │ │ │ ├── jobcondition.go │ │ │ │ │ ├── jobspec.go │ │ │ │ │ ├── jobstatus.go │ │ │ │ │ ├── jobtemplatespec.go │ │ │ │ │ ├── podfailurepolicy.go │ │ │ │ │ ├── podfailurepolicyonexitcodesrequirement.go │ │ │ │ │ ├── podfailurepolicyonpodconditionspattern.go │ │ │ │ │ ├── podfailurepolicyrule.go │ │ │ │ │ └── uncountedterminatedpods.go │ │ │ │ └── v1beta1/ │ │ │ │ ├── cronjob.go │ │ │ │ ├── cronjobspec.go │ │ │ │ ├── cronjobstatus.go │ │ │ │ └── jobtemplatespec.go │ │ │ ├── certificates/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── certificatesigningrequest.go │ │ │ │ │ ├── certificatesigningrequestcondition.go │ │ │ │ │ ├── certificatesigningrequestspec.go │ │ │ │ │ └── certificatesigningrequeststatus.go │ │ │ │ ├── v1alpha1/ │ │ │ │ │ ├── clustertrustbundle.go │ │ │ │ │ └── clustertrustbundlespec.go │ │ │ │ └── v1beta1/ │ │ │ │ ├── certificatesigningrequest.go │ │ │ │ ├── certificatesigningrequestcondition.go │ │ │ │ ├── certificatesigningrequestspec.go │ │ │ │ └── certificatesigningrequeststatus.go │ │ │ ├── coordination/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── lease.go │ │ │ │ │ └── leasespec.go │ │ │ │ └── v1beta1/ │ │ │ │ ├── lease.go │ │ │ │ └── leasespec.go │ │ │ ├── core/ │ │ │ │ └── v1/ │ │ │ │ ├── affinity.go │ │ │ │ ├── attachedvolume.go │ │ │ │ ├── awselasticblockstorevolumesource.go │ │ │ │ ├── azurediskvolumesource.go │ │ │ │ ├── azurefilepersistentvolumesource.go │ │ │ │ ├── azurefilevolumesource.go │ │ │ │ ├── capabilities.go │ │ │ │ ├── cephfspersistentvolumesource.go │ │ │ │ ├── cephfsvolumesource.go │ │ │ │ ├── cinderpersistentvolumesource.go │ │ │ │ ├── cindervolumesource.go │ │ │ │ ├── claimsource.go │ │ │ │ ├── clientipconfig.go │ │ │ │ ├── componentcondition.go │ │ │ │ ├── componentstatus.go │ │ │ │ ├── configmap.go │ │ │ │ ├── configmapenvsource.go │ │ │ │ ├── configmapkeyselector.go │ │ │ │ ├── configmapnodeconfigsource.go │ │ │ │ ├── configmapprojection.go │ │ │ │ ├── configmapvolumesource.go │ │ │ │ ├── container.go │ │ │ │ ├── containerimage.go │ │ │ │ ├── containerport.go │ │ │ │ ├── containerresizepolicy.go │ │ │ │ ├── containerstate.go │ │ │ │ ├── containerstaterunning.go │ │ │ │ ├── containerstateterminated.go │ │ │ │ ├── containerstatewaiting.go │ │ │ │ ├── containerstatus.go │ │ │ │ ├── csipersistentvolumesource.go │ │ │ │ ├── csivolumesource.go │ │ │ │ ├── daemonendpoint.go │ │ │ │ ├── downwardapiprojection.go │ │ │ │ ├── downwardapivolumefile.go │ │ │ │ ├── downwardapivolumesource.go │ │ │ │ ├── emptydirvolumesource.go │ │ │ │ ├── endpointaddress.go │ │ │ │ ├── endpointport.go │ │ │ │ ├── endpoints.go │ │ │ │ ├── endpointsubset.go │ │ │ │ ├── envfromsource.go │ │ │ │ ├── envvar.go │ │ │ │ ├── envvarsource.go │ │ │ │ ├── ephemeralcontainer.go │ │ │ │ ├── ephemeralcontainercommon.go │ │ │ │ ├── ephemeralvolumesource.go │ │ │ │ ├── event.go │ │ │ │ ├── eventseries.go │ │ │ │ ├── eventsource.go │ │ │ │ ├── execaction.go │ │ │ │ ├── fcvolumesource.go │ │ │ │ ├── flexpersistentvolumesource.go │ │ │ │ ├── flexvolumesource.go │ │ │ │ ├── flockervolumesource.go │ │ │ │ ├── gcepersistentdiskvolumesource.go │ │ │ │ ├── gitrepovolumesource.go │ │ │ │ ├── glusterfspersistentvolumesource.go │ │ │ │ ├── glusterfsvolumesource.go │ │ │ │ ├── grpcaction.go │ │ │ │ ├── hostalias.go │ │ │ │ ├── hostpathvolumesource.go │ │ │ │ ├── httpgetaction.go │ │ │ │ ├── httpheader.go │ │ │ │ ├── iscsipersistentvolumesource.go │ │ │ │ ├── iscsivolumesource.go │ │ │ │ ├── keytopath.go │ │ │ │ ├── lifecycle.go │ │ │ │ ├── lifecyclehandler.go │ │ │ │ ├── limitrange.go │ │ │ │ ├── limitrangeitem.go │ │ │ │ ├── limitrangespec.go │ │ │ │ ├── loadbalanceringress.go │ │ │ │ ├── loadbalancerstatus.go │ │ │ │ ├── localobjectreference.go │ │ │ │ ├── localvolumesource.go │ │ │ │ ├── namespace.go │ │ │ │ ├── namespacecondition.go │ │ │ │ ├── namespacespec.go │ │ │ │ ├── namespacestatus.go │ │ │ │ ├── nfsvolumesource.go │ │ │ │ ├── node.go │ │ │ │ ├── nodeaddress.go │ │ │ │ ├── nodeaffinity.go │ │ │ │ ├── nodecondition.go │ │ │ │ ├── nodeconfigsource.go │ │ │ │ ├── nodeconfigstatus.go │ │ │ │ ├── nodedaemonendpoints.go │ │ │ │ ├── nodeselector.go │ │ │ │ ├── nodeselectorrequirement.go │ │ │ │ ├── nodeselectorterm.go │ │ │ │ ├── nodespec.go │ │ │ │ ├── nodestatus.go │ │ │ │ ├── nodesysteminfo.go │ │ │ │ ├── objectfieldselector.go │ │ │ │ ├── objectreference.go │ │ │ │ ├── persistentvolume.go │ │ │ │ ├── persistentvolumeclaim.go │ │ │ │ ├── persistentvolumeclaimcondition.go │ │ │ │ ├── persistentvolumeclaimspec.go │ │ │ │ ├── persistentvolumeclaimstatus.go │ │ │ │ ├── persistentvolumeclaimtemplate.go │ │ │ │ ├── persistentvolumeclaimvolumesource.go │ │ │ │ ├── persistentvolumesource.go │ │ │ │ ├── persistentvolumespec.go │ │ │ │ ├── persistentvolumestatus.go │ │ │ │ ├── photonpersistentdiskvolumesource.go │ │ │ │ ├── pod.go │ │ │ │ ├── podaffinity.go │ │ │ │ ├── podaffinityterm.go │ │ │ │ ├── podantiaffinity.go │ │ │ │ ├── podcondition.go │ │ │ │ ├── poddnsconfig.go │ │ │ │ ├── poddnsconfigoption.go │ │ │ │ ├── podip.go │ │ │ │ ├── podos.go │ │ │ │ ├── podreadinessgate.go │ │ │ │ ├── podresourceclaim.go │ │ │ │ ├── podschedulinggate.go │ │ │ │ ├── podsecuritycontext.go │ │ │ │ ├── podspec.go │ │ │ │ ├── podstatus.go │ │ │ │ ├── podtemplate.go │ │ │ │ ├── podtemplatespec.go │ │ │ │ ├── portstatus.go │ │ │ │ ├── portworxvolumesource.go │ │ │ │ ├── preferredschedulingterm.go │ │ │ │ ├── probe.go │ │ │ │ ├── probehandler.go │ │ │ │ ├── projectedvolumesource.go │ │ │ │ ├── quobytevolumesource.go │ │ │ │ ├── rbdpersistentvolumesource.go │ │ │ │ ├── rbdvolumesource.go │ │ │ │ ├── replicationcontroller.go │ │ │ │ ├── replicationcontrollercondition.go │ │ │ │ ├── replicationcontrollerspec.go │ │ │ │ ├── replicationcontrollerstatus.go │ │ │ │ ├── resourceclaim.go │ │ │ │ ├── resourcefieldselector.go │ │ │ │ ├── resourcequota.go │ │ │ │ ├── resourcequotaspec.go │ │ │ │ ├── resourcequotastatus.go │ │ │ │ ├── resourcerequirements.go │ │ │ │ ├── scaleiopersistentvolumesource.go │ │ │ │ ├── scaleiovolumesource.go │ │ │ │ ├── scopedresourceselectorrequirement.go │ │ │ │ ├── scopeselector.go │ │ │ │ ├── seccompprofile.go │ │ │ │ ├── secret.go │ │ │ │ ├── secretenvsource.go │ │ │ │ ├── secretkeyselector.go │ │ │ │ ├── secretprojection.go │ │ │ │ ├── secretreference.go │ │ │ │ ├── secretvolumesource.go │ │ │ │ ├── securitycontext.go │ │ │ │ ├── selinuxoptions.go │ │ │ │ ├── service.go │ │ │ │ ├── serviceaccount.go │ │ │ │ ├── serviceaccounttokenprojection.go │ │ │ │ ├── serviceport.go │ │ │ │ ├── servicespec.go │ │ │ │ ├── servicestatus.go │ │ │ │ ├── sessionaffinityconfig.go │ │ │ │ ├── storageospersistentvolumesource.go │ │ │ │ ├── storageosvolumesource.go │ │ │ │ ├── sysctl.go │ │ │ │ ├── taint.go │ │ │ │ ├── tcpsocketaction.go │ │ │ │ ├── toleration.go │ │ │ │ ├── topologyselectorlabelrequirement.go │ │ │ │ ├── topologyselectorterm.go │ │ │ │ ├── topologyspreadconstraint.go │ │ │ │ ├── typedlocalobjectreference.go │ │ │ │ ├── typedobjectreference.go │ │ │ │ ├── volume.go │ │ │ │ ├── volumedevice.go │ │ │ │ ├── volumemount.go │ │ │ │ ├── volumenodeaffinity.go │ │ │ │ ├── volumeprojection.go │ │ │ │ ├── volumesource.go │ │ │ │ ├── vspherevirtualdiskvolumesource.go │ │ │ │ ├── weightedpodaffinityterm.go │ │ │ │ └── windowssecuritycontextoptions.go │ │ │ ├── discovery/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── endpoint.go │ │ │ │ │ ├── endpointconditions.go │ │ │ │ │ ├── endpointhints.go │ │ │ │ │ ├── endpointport.go │ │ │ │ │ ├── endpointslice.go │ │ │ │ │ └── forzone.go │ │ │ │ └── v1beta1/ │ │ │ │ ├── endpoint.go │ │ │ │ ├── endpointconditions.go │ │ │ │ ├── endpointhints.go │ │ │ │ ├── endpointport.go │ │ │ │ ├── endpointslice.go │ │ │ │ └── forzone.go │ │ │ ├── events/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── event.go │ │ │ │ │ └── eventseries.go │ │ │ │ └── v1beta1/ │ │ │ │ ├── event.go │ │ │ │ └── eventseries.go │ │ │ ├── extensions/ │ │ │ │ └── v1beta1/ │ │ │ │ ├── daemonset.go │ │ │ │ ├── daemonsetcondition.go │ │ │ │ ├── daemonsetspec.go │ │ │ │ ├── daemonsetstatus.go │ │ │ │ ├── daemonsetupdatestrategy.go │ │ │ │ ├── deployment.go │ │ │ │ ├── deploymentcondition.go │ │ │ │ ├── deploymentspec.go │ │ │ │ ├── deploymentstatus.go │ │ │ │ ├── deploymentstrategy.go │ │ │ │ ├── httpingresspath.go │ │ │ │ ├── httpingressrulevalue.go │ │ │ │ ├── ingress.go │ │ │ │ ├── ingressbackend.go │ │ │ │ ├── ingressloadbalanceringress.go │ │ │ │ ├── ingressloadbalancerstatus.go │ │ │ │ ├── ingressportstatus.go │ │ │ │ ├── ingressrule.go │ │ │ │ ├── ingressrulevalue.go │ │ │ │ ├── ingressspec.go │ │ │ │ ├── ingressstatus.go │ │ │ │ ├── ingresstls.go │ │ │ │ ├── ipblock.go │ │ │ │ ├── networkpolicy.go │ │ │ │ ├── networkpolicyegressrule.go │ │ │ │ ├── networkpolicyingressrule.go │ │ │ │ ├── networkpolicypeer.go │ │ │ │ ├── networkpolicyport.go │ │ │ │ ├── networkpolicyspec.go │ │ │ │ ├── networkpolicystatus.go │ │ │ │ ├── replicaset.go │ │ │ │ ├── replicasetcondition.go │ │ │ │ ├── replicasetspec.go │ │ │ │ ├── replicasetstatus.go │ │ │ │ ├── rollbackconfig.go │ │ │ │ ├── rollingupdatedaemonset.go │ │ │ │ ├── rollingupdatedeployment.go │ │ │ │ └── scale.go │ │ │ ├── flowcontrol/ │ │ │ │ ├── v1alpha1/ │ │ │ │ │ ├── flowdistinguishermethod.go │ │ │ │ │ ├── flowschema.go │ │ │ │ │ ├── flowschemacondition.go │ │ │ │ │ ├── flowschemaspec.go │ │ │ │ │ ├── flowschemastatus.go │ │ │ │ │ ├── groupsubject.go │ │ │ │ │ ├── limitedprioritylevelconfiguration.go │ │ │ │ │ ├── limitresponse.go │ │ │ │ │ ├── nonresourcepolicyrule.go │ │ │ │ │ ├── policyruleswithsubjects.go │ │ │ │ │ ├── prioritylevelconfiguration.go │ │ │ │ │ ├── prioritylevelconfigurationcondition.go │ │ │ │ │ ├── prioritylevelconfigurationreference.go │ │ │ │ │ ├── prioritylevelconfigurationspec.go │ │ │ │ │ ├── prioritylevelconfigurationstatus.go │ │ │ │ │ ├── queuingconfiguration.go │ │ │ │ │ ├── resourcepolicyrule.go │ │ │ │ │ ├── serviceaccountsubject.go │ │ │ │ │ ├── subject.go │ │ │ │ │ └── usersubject.go │ │ │ │ ├── v1beta1/ │ │ │ │ │ ├── flowdistinguishermethod.go │ │ │ │ │ ├── flowschema.go │ │ │ │ │ ├── flowschemacondition.go │ │ │ │ │ ├── flowschemaspec.go │ │ │ │ │ ├── flowschemastatus.go │ │ │ │ │ ├── groupsubject.go │ │ │ │ │ ├── limitedprioritylevelconfiguration.go │ │ │ │ │ ├── limitresponse.go │ │ │ │ │ ├── nonresourcepolicyrule.go │ │ │ │ │ ├── policyruleswithsubjects.go │ │ │ │ │ ├── prioritylevelconfiguration.go │ │ │ │ │ ├── prioritylevelconfigurationcondition.go │ │ │ │ │ ├── prioritylevelconfigurationreference.go │ │ │ │ │ ├── prioritylevelconfigurationspec.go │ │ │ │ │ ├── prioritylevelconfigurationstatus.go │ │ │ │ │ ├── queuingconfiguration.go │ │ │ │ │ ├── resourcepolicyrule.go │ │ │ │ │ ├── serviceaccountsubject.go │ │ │ │ │ ├── subject.go │ │ │ │ │ └── usersubject.go │ │ │ │ ├── v1beta2/ │ │ │ │ │ ├── flowdistinguishermethod.go │ │ │ │ │ ├── flowschema.go │ │ │ │ │ ├── flowschemacondition.go │ │ │ │ │ ├── flowschemaspec.go │ │ │ │ │ ├── flowschemastatus.go │ │ │ │ │ ├── groupsubject.go │ │ │ │ │ ├── limitedprioritylevelconfiguration.go │ │ │ │ │ ├── limitresponse.go │ │ │ │ │ ├── nonresourcepolicyrule.go │ │ │ │ │ ├── policyruleswithsubjects.go │ │ │ │ │ ├── prioritylevelconfiguration.go │ │ │ │ │ ├── prioritylevelconfigurationcondition.go │ │ │ │ │ ├── prioritylevelconfigurationreference.go │ │ │ │ │ ├── prioritylevelconfigurationspec.go │ │ │ │ │ ├── prioritylevelconfigurationstatus.go │ │ │ │ │ ├── queuingconfiguration.go │ │ │ │ │ ├── resourcepolicyrule.go │ │ │ │ │ ├── serviceaccountsubject.go │ │ │ │ │ ├── subject.go │ │ │ │ │ └── usersubject.go │ │ │ │ └── v1beta3/ │ │ │ │ ├── flowdistinguishermethod.go │ │ │ │ ├── flowschema.go │ │ │ │ ├── flowschemacondition.go │ │ │ │ ├── flowschemaspec.go │ │ │ │ ├── flowschemastatus.go │ │ │ │ ├── groupsubject.go │ │ │ │ ├── limitedprioritylevelconfiguration.go │ │ │ │ ├── limitresponse.go │ │ │ │ ├── nonresourcepolicyrule.go │ │ │ │ ├── policyruleswithsubjects.go │ │ │ │ ├── prioritylevelconfiguration.go │ │ │ │ ├── prioritylevelconfigurationcondition.go │ │ │ │ ├── prioritylevelconfigurationreference.go │ │ │ │ ├── prioritylevelconfigurationspec.go │ │ │ │ ├── prioritylevelconfigurationstatus.go │ │ │ │ ├── queuingconfiguration.go │ │ │ │ ├── resourcepolicyrule.go │ │ │ │ ├── serviceaccountsubject.go │ │ │ │ ├── subject.go │ │ │ │ └── usersubject.go │ │ │ ├── internal/ │ │ │ │ └── internal.go │ │ │ ├── meta/ │ │ │ │ └── v1/ │ │ │ │ ├── condition.go │ │ │ │ ├── deleteoptions.go │ │ │ │ ├── labelselector.go │ │ │ │ ├── labelselectorrequirement.go │ │ │ │ ├── managedfieldsentry.go │ │ │ │ ├── objectmeta.go │ │ │ │ ├── ownerreference.go │ │ │ │ ├── preconditions.go │ │ │ │ ├── typemeta.go │ │ │ │ └── unstructured.go │ │ │ ├── networking/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── httpingresspath.go │ │ │ │ │ ├── httpingressrulevalue.go │ │ │ │ │ ├── ingress.go │ │ │ │ │ ├── ingressbackend.go │ │ │ │ │ ├── ingressclass.go │ │ │ │ │ ├── ingressclassparametersreference.go │ │ │ │ │ ├── ingressclassspec.go │ │ │ │ │ ├── ingressloadbalanceringress.go │ │ │ │ │ ├── ingressloadbalancerstatus.go │ │ │ │ │ ├── ingressportstatus.go │ │ │ │ │ ├── ingressrule.go │ │ │ │ │ ├── ingressrulevalue.go │ │ │ │ │ ├── ingressservicebackend.go │ │ │ │ │ ├── ingressspec.go │ │ │ │ │ ├── ingressstatus.go │ │ │ │ │ ├── ingresstls.go │ │ │ │ │ ├── ipblock.go │ │ │ │ │ ├── networkpolicy.go │ │ │ │ │ ├── networkpolicyegressrule.go │ │ │ │ │ ├── networkpolicyingressrule.go │ │ │ │ │ ├── networkpolicypeer.go │ │ │ │ │ ├── networkpolicyport.go │ │ │ │ │ ├── networkpolicyspec.go │ │ │ │ │ ├── networkpolicystatus.go │ │ │ │ │ └── servicebackendport.go │ │ │ │ ├── v1alpha1/ │ │ │ │ │ ├── clustercidr.go │ │ │ │ │ ├── clustercidrspec.go │ │ │ │ │ ├── ipaddress.go │ │ │ │ │ ├── ipaddressspec.go │ │ │ │ │ └── parentreference.go │ │ │ │ └── v1beta1/ │ │ │ │ ├── httpingresspath.go │ │ │ │ ├── httpingressrulevalue.go │ │ │ │ ├── ingress.go │ │ │ │ ├── ingressbackend.go │ │ │ │ ├── ingressclass.go │ │ │ │ ├── ingressclassparametersreference.go │ │ │ │ ├── ingressclassspec.go │ │ │ │ ├── ingressloadbalanceringress.go │ │ │ │ ├── ingressloadbalancerstatus.go │ │ │ │ ├── ingressportstatus.go │ │ │ │ ├── ingressrule.go │ │ │ │ ├── ingressrulevalue.go │ │ │ │ ├── ingressspec.go │ │ │ │ ├── ingressstatus.go │ │ │ │ └── ingresstls.go │ │ │ ├── node/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── overhead.go │ │ │ │ │ ├── runtimeclass.go │ │ │ │ │ └── scheduling.go │ │ │ │ ├── v1alpha1/ │ │ │ │ │ ├── overhead.go │ │ │ │ │ ├── runtimeclass.go │ │ │ │ │ ├── runtimeclassspec.go │ │ │ │ │ └── scheduling.go │ │ │ │ └── v1beta1/ │ │ │ │ ├── overhead.go │ │ │ │ ├── runtimeclass.go │ │ │ │ └── scheduling.go │ │ │ ├── policy/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── eviction.go │ │ │ │ │ ├── poddisruptionbudget.go │ │ │ │ │ ├── poddisruptionbudgetspec.go │ │ │ │ │ └── poddisruptionbudgetstatus.go │ │ │ │ └── v1beta1/ │ │ │ │ ├── allowedcsidriver.go │ │ │ │ ├── allowedflexvolume.go │ │ │ │ ├── allowedhostpath.go │ │ │ │ ├── eviction.go │ │ │ │ ├── fsgroupstrategyoptions.go │ │ │ │ ├── hostportrange.go │ │ │ │ ├── idrange.go │ │ │ │ ├── poddisruptionbudget.go │ │ │ │ ├── poddisruptionbudgetspec.go │ │ │ │ ├── poddisruptionbudgetstatus.go │ │ │ │ ├── podsecuritypolicy.go │ │ │ │ ├── podsecuritypolicyspec.go │ │ │ │ ├── runasgroupstrategyoptions.go │ │ │ │ ├── runasuserstrategyoptions.go │ │ │ │ ├── runtimeclassstrategyoptions.go │ │ │ │ ├── selinuxstrategyoptions.go │ │ │ │ └── supplementalgroupsstrategyoptions.go │ │ │ ├── rbac/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── aggregationrule.go │ │ │ │ │ ├── clusterrole.go │ │ │ │ │ ├── clusterrolebinding.go │ │ │ │ │ ├── policyrule.go │ │ │ │ │ ├── role.go │ │ │ │ │ ├── rolebinding.go │ │ │ │ │ ├── roleref.go │ │ │ │ │ └── subject.go │ │ │ │ ├── v1alpha1/ │ │ │ │ │ ├── aggregationrule.go │ │ │ │ │ ├── clusterrole.go │ │ │ │ │ ├── clusterrolebinding.go │ │ │ │ │ ├── policyrule.go │ │ │ │ │ ├── role.go │ │ │ │ │ ├── rolebinding.go │ │ │ │ │ ├── roleref.go │ │ │ │ │ └── subject.go │ │ │ │ └── v1beta1/ │ │ │ │ ├── aggregationrule.go │ │ │ │ ├── clusterrole.go │ │ │ │ ├── clusterrolebinding.go │ │ │ │ ├── policyrule.go │ │ │ │ ├── role.go │ │ │ │ ├── rolebinding.go │ │ │ │ ├── roleref.go │ │ │ │ └── subject.go │ │ │ ├── resource/ │ │ │ │ └── v1alpha2/ │ │ │ │ ├── allocationresult.go │ │ │ │ ├── podschedulingcontext.go │ │ │ │ ├── podschedulingcontextspec.go │ │ │ │ ├── podschedulingcontextstatus.go │ │ │ │ ├── resourceclaim.go │ │ │ │ ├── resourceclaimconsumerreference.go │ │ │ │ ├── resourceclaimparametersreference.go │ │ │ │ ├── resourceclaimschedulingstatus.go │ │ │ │ ├── resourceclaimspec.go │ │ │ │ ├── resourceclaimstatus.go │ │ │ │ ├── resourceclaimtemplate.go │ │ │ │ ├── resourceclaimtemplatespec.go │ │ │ │ ├── resourceclass.go │ │ │ │ ├── resourceclassparametersreference.go │ │ │ │ └── resourcehandle.go │ │ │ ├── scheduling/ │ │ │ │ ├── v1/ │ │ │ │ │ └── priorityclass.go │ │ │ │ ├── v1alpha1/ │ │ │ │ │ └── priorityclass.go │ │ │ │ └── v1beta1/ │ │ │ │ └── priorityclass.go │ │ │ └── storage/ │ │ │ ├── v1/ │ │ │ │ ├── csidriver.go │ │ │ │ ├── csidriverspec.go │ │ │ │ ├── csinode.go │ │ │ │ ├── csinodedriver.go │ │ │ │ ├── csinodespec.go │ │ │ │ ├── csistoragecapacity.go │ │ │ │ ├── storageclass.go │ │ │ │ ├── tokenrequest.go │ │ │ │ ├── volumeattachment.go │ │ │ │ ├── volumeattachmentsource.go │ │ │ │ ├── volumeattachmentspec.go │ │ │ │ ├── volumeattachmentstatus.go │ │ │ │ ├── volumeerror.go │ │ │ │ └── volumenoderesources.go │ │ │ ├── v1alpha1/ │ │ │ │ ├── csistoragecapacity.go │ │ │ │ ├── volumeattachment.go │ │ │ │ ├── volumeattachmentsource.go │ │ │ │ ├── volumeattachmentspec.go │ │ │ │ ├── volumeattachmentstatus.go │ │ │ │ └── volumeerror.go │ │ │ └── v1beta1/ │ │ │ ├── csidriver.go │ │ │ ├── csidriverspec.go │ │ │ ├── csinode.go │ │ │ ├── csinodedriver.go │ │ │ ├── csinodespec.go │ │ │ ├── csistoragecapacity.go │ │ │ ├── storageclass.go │ │ │ ├── tokenrequest.go │ │ │ ├── volumeattachment.go │ │ │ ├── volumeattachmentsource.go │ │ │ ├── volumeattachmentspec.go │ │ │ ├── volumeattachmentstatus.go │ │ │ ├── volumeerror.go │ │ │ └── volumenoderesources.go │ │ ├── discovery/ │ │ │ ├── aggregated_discovery.go │ │ │ ├── cached/ │ │ │ │ ├── disk/ │ │ │ │ │ ├── cached_discovery.go │ │ │ │ │ └── round_tripper.go │ │ │ │ └── memory/ │ │ │ │ └── memcache.go │ │ │ ├── discovery_client.go │ │ │ ├── doc.go │ │ │ └── helper.go │ │ ├── dynamic/ │ │ │ ├── interface.go │ │ │ ├── scheme.go │ │ │ └── simple.go │ │ ├── kubernetes/ │ │ │ ├── clientset.go │ │ │ ├── doc.go │ │ │ ├── import.go │ │ │ ├── scheme/ │ │ │ │ ├── doc.go │ │ │ │ └── register.go │ │ │ └── typed/ │ │ │ ├── admissionregistration/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── admissionregistration_client.go │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── generated_expansion.go │ │ │ │ │ ├── mutatingwebhookconfiguration.go │ │ │ │ │ └── validatingwebhookconfiguration.go │ │ │ │ ├── v1alpha1/ │ │ │ │ │ ├── admissionregistration_client.go │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── generated_expansion.go │ │ │ │ │ ├── validatingadmissionpolicy.go │ │ │ │ │ └── validatingadmissionpolicybinding.go │ │ │ │ └── v1beta1/ │ │ │ │ ├── admissionregistration_client.go │ │ │ │ ├── doc.go │ │ │ │ ├── generated_expansion.go │ │ │ │ ├── mutatingwebhookconfiguration.go │ │ │ │ └── validatingwebhookconfiguration.go │ │ │ ├── apiserverinternal/ │ │ │ │ └── v1alpha1/ │ │ │ │ ├── apiserverinternal_client.go │ │ │ │ ├── doc.go │ │ │ │ ├── generated_expansion.go │ │ │ │ └── storageversion.go │ │ │ ├── apps/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── apps_client.go │ │ │ │ │ ├── controllerrevision.go │ │ │ │ │ ├── daemonset.go │ │ │ │ │ ├── deployment.go │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── generated_expansion.go │ │ │ │ │ ├── replicaset.go │ │ │ │ │ └── statefulset.go │ │ │ │ ├── v1beta1/ │ │ │ │ │ ├── apps_client.go │ │ │ │ │ ├── controllerrevision.go │ │ │ │ │ ├── deployment.go │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── generated_expansion.go │ │ │ │ │ └── statefulset.go │ │ │ │ └── v1beta2/ │ │ │ │ ├── apps_client.go │ │ │ │ ├── controllerrevision.go │ │ │ │ ├── daemonset.go │ │ │ │ ├── deployment.go │ │ │ │ ├── doc.go │ │ │ │ ├── generated_expansion.go │ │ │ │ ├── replicaset.go │ │ │ │ └── statefulset.go │ │ │ ├── authentication/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── authentication_client.go │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── generated_expansion.go │ │ │ │ │ └── tokenreview.go │ │ │ │ ├── v1alpha1/ │ │ │ │ │ ├── authentication_client.go │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── generated_expansion.go │ │ │ │ │ └── selfsubjectreview.go │ │ │ │ └── v1beta1/ │ │ │ │ ├── authentication_client.go │ │ │ │ ├── doc.go │ │ │ │ ├── generated_expansion.go │ │ │ │ ├── selfsubjectreview.go │ │ │ │ └── tokenreview.go │ │ │ ├── authorization/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── authorization_client.go │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── generated_expansion.go │ │ │ │ │ ├── localsubjectaccessreview.go │ │ │ │ │ ├── selfsubjectaccessreview.go │ │ │ │ │ ├── selfsubjectrulesreview.go │ │ │ │ │ └── subjectaccessreview.go │ │ │ │ └── v1beta1/ │ │ │ │ ├── authorization_client.go │ │ │ │ ├── doc.go │ │ │ │ ├── generated_expansion.go │ │ │ │ ├── localsubjectaccessreview.go │ │ │ │ ├── selfsubjectaccessreview.go │ │ │ │ ├── selfsubjectrulesreview.go │ │ │ │ └── subjectaccessreview.go │ │ │ ├── autoscaling/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── autoscaling_client.go │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── generated_expansion.go │ │ │ │ │ └── horizontalpodautoscaler.go │ │ │ │ ├── v2/ │ │ │ │ │ ├── autoscaling_client.go │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── generated_expansion.go │ │ │ │ │ └── horizontalpodautoscaler.go │ │ │ │ ├── v2beta1/ │ │ │ │ │ ├── autoscaling_client.go │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── generated_expansion.go │ │ │ │ │ └── horizontalpodautoscaler.go │ │ │ │ └── v2beta2/ │ │ │ │ ├── autoscaling_client.go │ │ │ │ ├── doc.go │ │ │ │ ├── generated_expansion.go │ │ │ │ └── horizontalpodautoscaler.go │ │ │ ├── batch/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── batch_client.go │ │ │ │ │ ├── cronjob.go │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── generated_expansion.go │ │ │ │ │ └── job.go │ │ │ │ └── v1beta1/ │ │ │ │ ├── batch_client.go │ │ │ │ ├── cronjob.go │ │ │ │ ├── doc.go │ │ │ │ └── generated_expansion.go │ │ │ ├── certificates/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── certificates_client.go │ │ │ │ │ ├── certificatesigningrequest.go │ │ │ │ │ ├── doc.go │ │ │ │ │ └── generated_expansion.go │ │ │ │ ├── v1alpha1/ │ │ │ │ │ ├── certificates_client.go │ │ │ │ │ ├── clustertrustbundle.go │ │ │ │ │ ├── doc.go │ │ │ │ │ └── generated_expansion.go │ │ │ │ └── v1beta1/ │ │ │ │ ├── certificates_client.go │ │ │ │ ├── certificatesigningrequest.go │ │ │ │ ├── certificatesigningrequest_expansion.go │ │ │ │ ├── doc.go │ │ │ │ └── generated_expansion.go │ │ │ ├── coordination/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── coordination_client.go │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── generated_expansion.go │ │ │ │ │ └── lease.go │ │ │ │ └── v1beta1/ │ │ │ │ ├── coordination_client.go │ │ │ │ ├── doc.go │ │ │ │ ├── generated_expansion.go │ │ │ │ └── lease.go │ │ │ ├── core/ │ │ │ │ └── v1/ │ │ │ │ ├── componentstatus.go │ │ │ │ ├── configmap.go │ │ │ │ ├── core_client.go │ │ │ │ ├── doc.go │ │ │ │ ├── endpoints.go │ │ │ │ ├── event.go │ │ │ │ ├── event_expansion.go │ │ │ │ ├── generated_expansion.go │ │ │ │ ├── limitrange.go │ │ │ │ ├── namespace.go │ │ │ │ ├── namespace_expansion.go │ │ │ │ ├── node.go │ │ │ │ ├── node_expansion.go │ │ │ │ ├── persistentvolume.go │ │ │ │ ├── persistentvolumeclaim.go │ │ │ │ ├── pod.go │ │ │ │ ├── pod_expansion.go │ │ │ │ ├── podtemplate.go │ │ │ │ ├── replicationcontroller.go │ │ │ │ ├── resourcequota.go │ │ │ │ ├── secret.go │ │ │ │ ├── service.go │ │ │ │ ├── service_expansion.go │ │ │ │ └── serviceaccount.go │ │ │ ├── discovery/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── discovery_client.go │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── endpointslice.go │ │ │ │ │ └── generated_expansion.go │ │ │ │ └── v1beta1/ │ │ │ │ ├── discovery_client.go │ │ │ │ ├── doc.go │ │ │ │ ├── endpointslice.go │ │ │ │ └── generated_expansion.go │ │ │ ├── events/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── event.go │ │ │ │ │ ├── events_client.go │ │ │ │ │ └── generated_expansion.go │ │ │ │ └── v1beta1/ │ │ │ │ ├── doc.go │ │ │ │ ├── event.go │ │ │ │ ├── event_expansion.go │ │ │ │ ├── events_client.go │ │ │ │ └── generated_expansion.go │ │ │ ├── extensions/ │ │ │ │ └── v1beta1/ │ │ │ │ ├── daemonset.go │ │ │ │ ├── deployment.go │ │ │ │ ├── deployment_expansion.go │ │ │ │ ├── doc.go │ │ │ │ ├── extensions_client.go │ │ │ │ ├── generated_expansion.go │ │ │ │ ├── ingress.go │ │ │ │ ├── networkpolicy.go │ │ │ │ └── replicaset.go │ │ │ ├── flowcontrol/ │ │ │ │ ├── v1alpha1/ │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── flowcontrol_client.go │ │ │ │ │ ├── flowschema.go │ │ │ │ │ ├── generated_expansion.go │ │ │ │ │ └── prioritylevelconfiguration.go │ │ │ │ ├── v1beta1/ │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── flowcontrol_client.go │ │ │ │ │ ├── flowschema.go │ │ │ │ │ ├── generated_expansion.go │ │ │ │ │ └── prioritylevelconfiguration.go │ │ │ │ ├── v1beta2/ │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── flowcontrol_client.go │ │ │ │ │ ├── flowschema.go │ │ │ │ │ ├── generated_expansion.go │ │ │ │ │ └── prioritylevelconfiguration.go │ │ │ │ └── v1beta3/ │ │ │ │ ├── doc.go │ │ │ │ ├── flowcontrol_client.go │ │ │ │ ├── flowschema.go │ │ │ │ ├── generated_expansion.go │ │ │ │ └── prioritylevelconfiguration.go │ │ │ ├── networking/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── generated_expansion.go │ │ │ │ │ ├── ingress.go │ │ │ │ │ ├── ingressclass.go │ │ │ │ │ ├── networking_client.go │ │ │ │ │ └── networkpolicy.go │ │ │ │ ├── v1alpha1/ │ │ │ │ │ ├── clustercidr.go │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── generated_expansion.go │ │ │ │ │ ├── ipaddress.go │ │ │ │ │ └── networking_client.go │ │ │ │ └── v1beta1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated_expansion.go │ │ │ │ ├── ingress.go │ │ │ │ ├── ingressclass.go │ │ │ │ └── networking_client.go │ │ │ ├── node/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── generated_expansion.go │ │ │ │ │ ├── node_client.go │ │ │ │ │ └── runtimeclass.go │ │ │ │ ├── v1alpha1/ │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── generated_expansion.go │ │ │ │ │ ├── node_client.go │ │ │ │ │ └── runtimeclass.go │ │ │ │ └── v1beta1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated_expansion.go │ │ │ │ ├── node_client.go │ │ │ │ └── runtimeclass.go │ │ │ ├── policy/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── eviction.go │ │ │ │ │ ├── eviction_expansion.go │ │ │ │ │ ├── generated_expansion.go │ │ │ │ │ ├── poddisruptionbudget.go │ │ │ │ │ └── policy_client.go │ │ │ │ └── v1beta1/ │ │ │ │ ├── doc.go │ │ │ │ ├── eviction.go │ │ │ │ ├── eviction_expansion.go │ │ │ │ ├── generated_expansion.go │ │ │ │ ├── poddisruptionbudget.go │ │ │ │ ├── podsecuritypolicy.go │ │ │ │ └── policy_client.go │ │ │ ├── rbac/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── clusterrole.go │ │ │ │ │ ├── clusterrolebinding.go │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── generated_expansion.go │ │ │ │ │ ├── rbac_client.go │ │ │ │ │ ├── role.go │ │ │ │ │ └── rolebinding.go │ │ │ │ ├── v1alpha1/ │ │ │ │ │ ├── clusterrole.go │ │ │ │ │ ├── clusterrolebinding.go │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── generated_expansion.go │ │ │ │ │ ├── rbac_client.go │ │ │ │ │ ├── role.go │ │ │ │ │ └── rolebinding.go │ │ │ │ └── v1beta1/ │ │ │ │ ├── clusterrole.go │ │ │ │ ├── clusterrolebinding.go │ │ │ │ ├── doc.go │ │ │ │ ├── generated_expansion.go │ │ │ │ ├── rbac_client.go │ │ │ │ ├── role.go │ │ │ │ └── rolebinding.go │ │ │ ├── resource/ │ │ │ │ └── v1alpha2/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated_expansion.go │ │ │ │ ├── podschedulingcontext.go │ │ │ │ ├── resource_client.go │ │ │ │ ├── resourceclaim.go │ │ │ │ ├── resourceclaimtemplate.go │ │ │ │ └── resourceclass.go │ │ │ ├── scheduling/ │ │ │ │ ├── v1/ │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── generated_expansion.go │ │ │ │ │ ├── priorityclass.go │ │ │ │ │ └── scheduling_client.go │ │ │ │ ├── v1alpha1/ │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── generated_expansion.go │ │ │ │ │ ├── priorityclass.go │ │ │ │ │ └── scheduling_client.go │ │ │ │ └── v1beta1/ │ │ │ │ ├── doc.go │ │ │ │ ├── generated_expansion.go │ │ │ │ ├── priorityclass.go │ │ │ │ └── scheduling_client.go │ │ │ └── storage/ │ │ │ ├── v1/ │ │ │ │ ├── csidriver.go │ │ │ │ ├── csinode.go │ │ │ │ ├── csistoragecapacity.go │ │ │ │ ├── doc.go │ │ │ │ ├── generated_expansion.go │ │ │ │ ├── storage_client.go │ │ │ │ ├── storageclass.go │ │ │ │ └── volumeattachment.go │ │ │ ├── v1alpha1/ │ │ │ │ ├── csistoragecapacity.go │ │ │ │ ├── doc.go │ │ │ │ ├── generated_expansion.go │ │ │ │ ├── storage_client.go │ │ │ │ └── volumeattachment.go │ │ │ └── v1beta1/ │ │ │ ├── csidriver.go │ │ │ ├── csinode.go │ │ │ ├── csistoragecapacity.go │ │ │ ├── doc.go │ │ │ ├── generated_expansion.go │ │ │ ├── storage_client.go │ │ │ ├── storageclass.go │ │ │ └── volumeattachment.go │ │ ├── openapi/ │ │ │ ├── OWNERS │ │ │ ├── cached/ │ │ │ │ ├── client.go │ │ │ │ └── groupversion.go │ │ │ ├── client.go │ │ │ └── groupversion.go │ │ ├── openapi3/ │ │ │ └── root.go │ │ ├── pkg/ │ │ │ ├── apis/ │ │ │ │ └── clientauthentication/ │ │ │ │ ├── OWNERS │ │ │ │ ├── doc.go │ │ │ │ ├── install/ │ │ │ │ │ └── install.go │ │ │ │ ├── register.go │ │ │ │ ├── types.go │ │ │ │ ├── v1/ │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── register.go │ │ │ │ │ ├── types.go │ │ │ │ │ ├── zz_generated.conversion.go │ │ │ │ │ ├── zz_generated.deepcopy.go │ │ │ │ │ └── zz_generated.defaults.go │ │ │ │ ├── v1beta1/ │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── register.go │ │ │ │ │ ├── types.go │ │ │ │ │ ├── zz_generated.conversion.go │ │ │ │ │ ├── zz_generated.deepcopy.go │ │ │ │ │ └── zz_generated.defaults.go │ │ │ │ └── zz_generated.deepcopy.go │ │ │ └── version/ │ │ │ ├── base.go │ │ │ ├── doc.go │ │ │ └── version.go │ │ ├── plugin/ │ │ │ └── pkg/ │ │ │ └── client/ │ │ │ └── auth/ │ │ │ └── exec/ │ │ │ ├── exec.go │ │ │ └── metrics.go │ │ ├── rest/ │ │ │ ├── OWNERS │ │ │ ├── client.go │ │ │ ├── config.go │ │ │ ├── exec.go │ │ │ ├── plugin.go │ │ │ ├── request.go │ │ │ ├── transport.go │ │ │ ├── url_utils.go │ │ │ ├── urlbackoff.go │ │ │ ├── warnings.go │ │ │ ├── watch/ │ │ │ │ ├── decoder.go │ │ │ │ └── encoder.go │ │ │ ├── with_retry.go │ │ │ └── zz_generated.deepcopy.go │ │ ├── restmapper/ │ │ │ ├── category_expansion.go │ │ │ ├── discovery.go │ │ │ └── shortcut.go │ │ ├── third_party/ │ │ │ └── forked/ │ │ │ └── golang/ │ │ │ ├── LICENSE │ │ │ ├── PATENTS │ │ │ └── template/ │ │ │ ├── exec.go │ │ │ └── funcs.go │ │ ├── tools/ │ │ │ ├── auth/ │ │ │ │ ├── OWNERS │ │ │ │ └── clientauth.go │ │ │ ├── clientcmd/ │ │ │ │ ├── api/ │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── helpers.go │ │ │ │ │ ├── latest/ │ │ │ │ │ │ └── latest.go │ │ │ │ │ ├── register.go │ │ │ │ │ ├── types.go │ │ │ │ │ ├── v1/ │ │ │ │ │ │ ├── conversion.go │ │ │ │ │ │ ├── defaults.go │ │ │ │ │ │ ├── doc.go │ │ │ │ │ │ ├── register.go │ │ │ │ │ │ ├── types.go │ │ │ │ │ │ ├── zz_generated.conversion.go │ │ │ │ │ │ ├── zz_generated.deepcopy.go │ │ │ │ │ │ └── zz_generated.defaults.go │ │ │ │ │ └── zz_generated.deepcopy.go │ │ │ │ ├── auth_loaders.go │ │ │ │ ├── client_config.go │ │ │ │ ├── config.go │ │ │ │ ├── doc.go │ │ │ │ ├── flag.go │ │ │ │ ├── helpers.go │ │ │ │ ├── loader.go │ │ │ │ ├── merged_client_builder.go │ │ │ │ ├── overrides.go │ │ │ │ └── validation.go │ │ │ ├── metrics/ │ │ │ │ ├── OWNERS │ │ │ │ └── metrics.go │ │ │ ├── reference/ │ │ │ │ └── ref.go │ │ │ └── remotecommand/ │ │ │ ├── doc.go │ │ │ ├── errorstream.go │ │ │ ├── reader.go │ │ │ ├── remotecommand.go │ │ │ ├── resize.go │ │ │ ├── v1.go │ │ │ ├── v2.go │ │ │ ├── v3.go │ │ │ └── v4.go │ │ ├── transport/ │ │ │ ├── OWNERS │ │ │ ├── cache.go │ │ │ ├── cache_go118.go │ │ │ ├── cert_rotation.go │ │ │ ├── config.go │ │ │ ├── round_trippers.go │ │ │ ├── spdy/ │ │ │ │ └── spdy.go │ │ │ ├── token_source.go │ │ │ └── transport.go │ │ └── util/ │ │ ├── cert/ │ │ │ ├── OWNERS │ │ │ ├── cert.go │ │ │ ├── csr.go │ │ │ ├── io.go │ │ │ ├── pem.go │ │ │ └── server_inspection.go │ │ ├── connrotation/ │ │ │ └── connrotation.go │ │ ├── exec/ │ │ │ └── exec.go │ │ ├── flowcontrol/ │ │ │ ├── backoff.go │ │ │ └── throttle.go │ │ ├── homedir/ │ │ │ └── homedir.go │ │ ├── jsonpath/ │ │ │ ├── doc.go │ │ │ ├── jsonpath.go │ │ │ ├── node.go │ │ │ └── parser.go │ │ ├── keyutil/ │ │ │ ├── OWNERS │ │ │ └── key.go │ │ └── workqueue/ │ │ ├── default_rate_limiters.go │ │ ├── delaying_queue.go │ │ ├── doc.go │ │ ├── metrics.go │ │ ├── parallelizer.go │ │ ├── queue.go │ │ └── rate_limiting_queue.go │ ├── klog/ │ │ └── v2/ │ │ ├── .gitignore │ │ ├── CONTRIBUTING.md │ │ ├── LICENSE │ │ ├── OWNERS │ │ ├── README.md │ │ ├── RELEASE.md │ │ ├── SECURITY.md │ │ ├── SECURITY_CONTACTS │ │ ├── code-of-conduct.md │ │ ├── contextual.go │ │ ├── exit.go │ │ ├── imports.go │ │ ├── internal/ │ │ │ ├── buffer/ │ │ │ │ └── buffer.go │ │ │ ├── clock/ │ │ │ │ ├── README.md │ │ │ │ └── clock.go │ │ │ ├── dbg/ │ │ │ │ └── dbg.go │ │ │ ├── serialize/ │ │ │ │ └── keyvalues.go │ │ │ └── severity/ │ │ │ └── severity.go │ │ ├── k8s_references.go │ │ ├── klog.go │ │ ├── klog_file.go │ │ ├── klog_file_others.go │ │ ├── klog_file_windows.go │ │ └── klogr.go │ ├── kube-openapi/ │ │ ├── LICENSE │ │ └── pkg/ │ │ ├── builder3/ │ │ │ └── util/ │ │ │ └── util.go │ │ ├── cached/ │ │ │ └── cache.go │ │ ├── common/ │ │ │ ├── common.go │ │ │ ├── doc.go │ │ │ └── interfaces.go │ │ ├── handler3/ │ │ │ └── handler.go │ │ ├── internal/ │ │ │ ├── flags.go │ │ │ ├── serialization.go │ │ │ └── third_party/ │ │ │ └── go-json-experiment/ │ │ │ └── json/ │ │ │ ├── AUTHORS │ │ │ ├── CONTRIBUTORS │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── arshal.go │ │ │ ├── arshal_any.go │ │ │ ├── arshal_default.go │ │ │ ├── arshal_funcs.go │ │ │ ├── arshal_inlined.go │ │ │ ├── arshal_methods.go │ │ │ ├── arshal_time.go │ │ │ ├── decode.go │ │ │ ├── doc.go │ │ │ ├── encode.go │ │ │ ├── errors.go │ │ │ ├── fields.go │ │ │ ├── fold.go │ │ │ ├── intern.go │ │ │ ├── pools.go │ │ │ ├── state.go │ │ │ ├── token.go │ │ │ └── value.go │ │ ├── openapiconv/ │ │ │ └── convert.go │ │ ├── schemaconv/ │ │ │ ├── openapi.go │ │ │ ├── proto_models.go │ │ │ └── smd.go │ │ ├── schemamutation/ │ │ │ └── walker.go │ │ ├── spec3/ │ │ │ ├── component.go │ │ │ ├── encoding.go │ │ │ ├── example.go │ │ │ ├── external_documentation.go │ │ │ ├── fuzz.go │ │ │ ├── header.go │ │ │ ├── media_type.go │ │ │ ├── operation.go │ │ │ ├── parameter.go │ │ │ ├── path.go │ │ │ ├── request_body.go │ │ │ ├── response.go │ │ │ ├── security_scheme.go │ │ │ ├── server.go │ │ │ └── spec.go │ │ ├── util/ │ │ │ └── proto/ │ │ │ ├── OWNERS │ │ │ ├── doc.go │ │ │ ├── document.go │ │ │ ├── document_v3.go │ │ │ └── openapi.go │ │ └── validation/ │ │ └── spec/ │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── contact_info.go │ │ ├── external_docs.go │ │ ├── fuzz.go │ │ ├── gnostic.go │ │ ├── header.go │ │ ├── info.go │ │ ├── items.go │ │ ├── license.go │ │ ├── operation.go │ │ ├── parameter.go │ │ ├── path_item.go │ │ ├── paths.go │ │ ├── ref.go │ │ ├── response.go │ │ ├── responses.go │ │ ├── schema.go │ │ ├── security_scheme.go │ │ ├── swagger.go │ │ └── tag.go │ └── utils/ │ ├── LICENSE │ ├── clock/ │ │ ├── README.md │ │ ├── clock.go │ │ └── testing/ │ │ ├── fake_clock.go │ │ └── simple_interval_clock.go │ ├── integer/ │ │ └── integer.go │ ├── internal/ │ │ └── third_party/ │ │ └── forked/ │ │ └── golang/ │ │ ├── LICENSE │ │ ├── PATENTS │ │ └── net/ │ │ ├── ip.go │ │ └── parse.go │ ├── net/ │ │ ├── ipfamily.go │ │ ├── ipnet.go │ │ ├── net.go │ │ ├── parse.go │ │ └── port.go │ ├── pointer/ │ │ ├── OWNERS │ │ ├── README.md │ │ └── pointer.go │ └── strings/ │ └── slices/ │ └── slices.go ├── modules.txt └── sigs.k8s.io/ ├── json/ │ ├── CONTRIBUTING.md │ ├── LICENSE │ ├── Makefile │ ├── OWNERS │ ├── README.md │ ├── SECURITY.md │ ├── SECURITY_CONTACTS │ ├── code-of-conduct.md │ ├── doc.go │ ├── internal/ │ │ └── golang/ │ │ └── encoding/ │ │ └── json/ │ │ ├── decode.go │ │ ├── encode.go │ │ ├── fold.go │ │ ├── fuzz.go │ │ ├── indent.go │ │ ├── kubernetes_patch.go │ │ ├── scanner.go │ │ ├── stream.go │ │ ├── tables.go │ │ └── tags.go │ └── json.go ├── kustomize/ │ ├── api/ │ │ ├── LICENSE │ │ ├── filters/ │ │ │ ├── annotations/ │ │ │ │ ├── annotations.go │ │ │ │ └── doc.go │ │ │ ├── fieldspec/ │ │ │ │ ├── doc.go │ │ │ │ └── fieldspec.go │ │ │ ├── filtersutil/ │ │ │ │ └── setters.go │ │ │ ├── fsslice/ │ │ │ │ ├── doc.go │ │ │ │ └── fsslice.go │ │ │ ├── iampolicygenerator/ │ │ │ │ ├── doc.go │ │ │ │ └── iampolicygenerator.go │ │ │ ├── imagetag/ │ │ │ │ ├── doc.go │ │ │ │ ├── imagetag.go │ │ │ │ ├── legacy.go │ │ │ │ └── updater.go │ │ │ ├── labels/ │ │ │ │ ├── doc.go │ │ │ │ └── labels.go │ │ │ ├── nameref/ │ │ │ │ ├── doc.go │ │ │ │ ├── nameref.go │ │ │ │ └── seqfilter.go │ │ │ ├── namespace/ │ │ │ │ ├── doc.go │ │ │ │ └── namespace.go │ │ │ ├── patchjson6902/ │ │ │ │ ├── doc.go │ │ │ │ └── patchjson6902.go │ │ │ ├── patchstrategicmerge/ │ │ │ │ ├── doc.go │ │ │ │ └── patchstrategicmerge.go │ │ │ ├── prefix/ │ │ │ │ ├── doc.go │ │ │ │ └── prefix.go │ │ │ ├── refvar/ │ │ │ │ ├── doc.go │ │ │ │ ├── expand.go │ │ │ │ └── refvar.go │ │ │ ├── replacement/ │ │ │ │ ├── doc.go │ │ │ │ └── replacement.go │ │ │ ├── replicacount/ │ │ │ │ ├── doc.go │ │ │ │ └── replicacount.go │ │ │ ├── suffix/ │ │ │ │ ├── doc.go │ │ │ │ └── suffix.go │ │ │ └── valueadd/ │ │ │ └── valueadd.go │ │ ├── hasher/ │ │ │ └── hasher.go │ │ ├── ifc/ │ │ │ └── ifc.go │ │ ├── image/ │ │ │ └── image.go │ │ ├── internal/ │ │ │ ├── accumulator/ │ │ │ │ ├── loadconfigfromcrds.go │ │ │ │ ├── namereferencetransformer.go │ │ │ │ ├── refvartransformer.go │ │ │ │ └── resaccumulator.go │ │ │ ├── builtins/ │ │ │ │ ├── AnnotationsTransformer.go │ │ │ │ ├── ConfigMapGenerator.go │ │ │ │ ├── HashTransformer.go │ │ │ │ ├── HelmChartInflationGenerator.go │ │ │ │ ├── IAMPolicyGenerator.go │ │ │ │ ├── ImageTagTransformer.go │ │ │ │ ├── LabelTransformer.go │ │ │ │ ├── NamespaceTransformer.go │ │ │ │ ├── PatchJson6902Transformer.go │ │ │ │ ├── PatchStrategicMergeTransformer.go │ │ │ │ ├── PatchTransformer.go │ │ │ │ ├── PrefixTransformer.go │ │ │ │ ├── ReplacementTransformer.go │ │ │ │ ├── ReplicaCountTransformer.go │ │ │ │ ├── SecretGenerator.go │ │ │ │ ├── SortOrderTransformer.go │ │ │ │ ├── SuffixTransformer.go │ │ │ │ ├── ValueAddTransformer.go │ │ │ │ └── doc.go │ │ │ ├── generators/ │ │ │ │ ├── configmap.go │ │ │ │ ├── secret.go │ │ │ │ └── utils.go │ │ │ ├── git/ │ │ │ │ ├── cloner.go │ │ │ │ ├── gitrunner.go │ │ │ │ └── repospec.go │ │ │ ├── kusterr/ │ │ │ │ └── yamlformaterror.go │ │ │ ├── plugins/ │ │ │ │ ├── builtinconfig/ │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── loaddefaultconfig.go │ │ │ │ │ ├── namebackreferences.go │ │ │ │ │ └── transformerconfig.go │ │ │ │ ├── builtinhelpers/ │ │ │ │ │ ├── builtinplugintype_string.go │ │ │ │ │ └── builtins.go │ │ │ │ ├── execplugin/ │ │ │ │ │ └── execplugin.go │ │ │ │ ├── fnplugin/ │ │ │ │ │ └── fnplugin.go │ │ │ │ ├── loader/ │ │ │ │ │ └── loader.go │ │ │ │ └── utils/ │ │ │ │ └── utils.go │ │ │ ├── target/ │ │ │ │ ├── errmissingkustomization.go │ │ │ │ ├── kusttarget.go │ │ │ │ ├── kusttarget_configplugin.go │ │ │ │ └── multitransformer.go │ │ │ ├── utils/ │ │ │ │ ├── annotations.go │ │ │ │ ├── errtimeout.go │ │ │ │ ├── makeResIds.go │ │ │ │ ├── stringslice.go │ │ │ │ └── timedcall.go │ │ │ └── validate/ │ │ │ └── fieldvalidator.go │ │ ├── konfig/ │ │ │ ├── builtinpluginconsts/ │ │ │ │ ├── commonannotations.go │ │ │ │ ├── commonlabels.go │ │ │ │ ├── defaultconfig.go │ │ │ │ ├── doc.go │ │ │ │ ├── images.go │ │ │ │ ├── metadatalabels.go │ │ │ │ ├── nameprefix.go │ │ │ │ ├── namereference.go │ │ │ │ ├── namespace.go │ │ │ │ ├── namesuffix.go │ │ │ │ ├── replicas.go │ │ │ │ ├── templatelabels.go │ │ │ │ └── varreference.go │ │ │ ├── doc.go │ │ │ ├── general.go │ │ │ └── plugins.go │ │ ├── krusty/ │ │ │ ├── doc.go │ │ │ ├── kustomizer.go │ │ │ └── options.go │ │ ├── kv/ │ │ │ └── kv.go │ │ ├── loader/ │ │ │ ├── errors.go │ │ │ ├── fileloader.go │ │ │ ├── loader.go │ │ │ └── loadrestrictions.go │ │ ├── provenance/ │ │ │ └── provenance.go │ │ ├── provider/ │ │ │ └── depprovider.go │ │ ├── resmap/ │ │ │ ├── factory.go │ │ │ ├── resmap.go │ │ │ └── reswrangler.go │ │ ├── resource/ │ │ │ ├── doc.go │ │ │ ├── factory.go │ │ │ ├── idset.go │ │ │ ├── origin.go │ │ │ └── resource.go │ │ └── types/ │ │ ├── builtinpluginloadingoptions_string.go │ │ ├── configmapargs.go │ │ ├── doc.go │ │ ├── erronlybuiltinpluginsallowed.go │ │ ├── errunabletofind.go │ │ ├── fieldspec.go │ │ ├── generationbehavior.go │ │ ├── generatorargs.go │ │ ├── generatoroptions.go │ │ ├── helmchartargs.go │ │ ├── iampolicygenerator.go │ │ ├── image.go │ │ ├── kustomization.go │ │ ├── kvpairsources.go │ │ ├── labels.go │ │ ├── loadrestrictions.go │ │ ├── loadrestrictions_string.go │ │ ├── objectmeta.go │ │ ├── pair.go │ │ ├── patch.go │ │ ├── patchstrategicmerge.go │ │ ├── pluginconfig.go │ │ ├── pluginrestrictions.go │ │ ├── pluginrestrictions_string.go │ │ ├── replacement.go │ │ ├── replacementfield.go │ │ ├── replica.go │ │ ├── secretargs.go │ │ ├── selector.go │ │ ├── sortoptions.go │ │ ├── typemeta.go │ │ └── var.go │ └── kyaml/ │ ├── LICENSE │ ├── comments/ │ │ └── comments.go │ ├── errors/ │ │ └── errors.go │ ├── ext/ │ │ └── ext.go │ ├── fieldmeta/ │ │ └── fieldmeta.go │ ├── filesys/ │ │ ├── confirmeddir.go │ │ ├── doc.go │ │ ├── file.go │ │ ├── fileinfo.go │ │ ├── fileondisk.go │ │ ├── filesystem.go │ │ ├── fsnode.go │ │ ├── fsondisk.go │ │ ├── fsondisk_unix.go │ │ ├── fsondisk_windows.go │ │ └── util.go │ ├── fn/ │ │ └── runtime/ │ │ ├── container/ │ │ │ └── container.go │ │ ├── exec/ │ │ │ ├── doc.go │ │ │ └── exec.go │ │ ├── runtimeutil/ │ │ │ ├── doc.go │ │ │ ├── functiontypes.go │ │ │ ├── runtimeutil.go │ │ │ └── types.go │ │ └── starlark/ │ │ ├── context.go │ │ ├── doc.go │ │ └── starlark.go │ ├── internal/ │ │ └── forked/ │ │ └── github.com/ │ │ ├── go-yaml/ │ │ │ └── yaml/ │ │ │ ├── LICENSE │ │ │ ├── NOTICE │ │ │ ├── README.md │ │ │ ├── apic.go │ │ │ ├── decode.go │ │ │ ├── emitterc.go │ │ │ ├── encode.go │ │ │ ├── parserc.go │ │ │ ├── readerc.go │ │ │ ├── resolve.go │ │ │ ├── scannerc.go │ │ │ ├── sorter.go │ │ │ ├── writerc.go │ │ │ ├── yaml.go │ │ │ ├── yamlh.go │ │ │ └── yamlprivateh.go │ │ └── qri-io/ │ │ └── starlib/ │ │ └── util/ │ │ ├── LICENSE │ │ ├── doc.go │ │ └── util.go │ ├── kio/ │ │ ├── byteio_reader.go │ │ ├── byteio_writer.go │ │ ├── doc.go │ │ ├── filters/ │ │ │ ├── filters.go │ │ │ ├── fmtr.go │ │ │ ├── grep.go │ │ │ ├── local.go │ │ │ ├── merge.go │ │ │ ├── merge3.go │ │ │ ├── modify.go │ │ │ └── stripcomments.go │ │ ├── ignorefilesmatcher.go │ │ ├── kio.go │ │ ├── kioutil/ │ │ │ └── kioutil.go │ │ ├── pkgio_reader.go │ │ ├── pkgio_writer.go │ │ └── tree.go │ ├── openapi/ │ │ ├── Makefile │ │ ├── README.md │ │ ├── kubernetesapi/ │ │ │ ├── openapiinfo.go │ │ │ └── v1_21_2/ │ │ │ ├── swagger.go │ │ │ └── swagger.pb │ │ ├── kustomizationapi/ │ │ │ ├── swagger.go │ │ │ └── swagger.json │ │ └── openapi.go │ ├── order/ │ │ └── syncorder.go │ ├── resid/ │ │ ├── gvk.go │ │ └── resid.go │ ├── runfn/ │ │ └── runfn.go │ ├── sets/ │ │ ├── string.go │ │ └── stringlist.go │ ├── sliceutil/ │ │ └── slice.go │ ├── utils/ │ │ └── pathsplitter.go │ └── yaml/ │ ├── alias.go │ ├── compatibility.go │ ├── const.go │ ├── datamap.go │ ├── doc.go │ ├── filters.go │ ├── fns.go │ ├── internal/ │ │ └── k8sgen/ │ │ └── pkg/ │ │ ├── labels/ │ │ │ ├── copied.deepcopy.go │ │ │ ├── labels.go │ │ │ └── selector.go │ │ ├── selection/ │ │ │ └── operator.go │ │ └── util/ │ │ ├── errors/ │ │ │ └── errors.go │ │ ├── sets/ │ │ │ ├── empty.go │ │ │ └── string.go │ │ └── validation/ │ │ ├── field/ │ │ │ ├── errors.go │ │ │ └── path.go │ │ └── validation.go │ ├── kfns.go │ ├── mapnode.go │ ├── match.go │ ├── merge2/ │ │ ├── merge2.go │ │ ├── smpdirective.go │ │ └── smpdirective_string.go │ ├── merge3/ │ │ ├── merge3.go │ │ └── visitor.go │ ├── order.go │ ├── rnode.go │ ├── schema/ │ │ └── schema.go │ ├── types.go │ ├── util.go │ └── walk/ │ ├── associative_sequence.go │ ├── map.go │ ├── nonassociative_sequence.go │ ├── scalar.go │ ├── visitor.go │ └── walk.go ├── structured-merge-diff/ │ └── v4/ │ ├── LICENSE │ ├── fieldpath/ │ │ ├── doc.go │ │ ├── element.go │ │ ├── fromvalue.go │ │ ├── managers.go │ │ ├── path.go │ │ ├── pathelementmap.go │ │ ├── serialize-pe.go │ │ ├── serialize.go │ │ └── set.go │ ├── merge/ │ │ ├── conflict.go │ │ └── update.go │ ├── schema/ │ │ ├── doc.go │ │ ├── elements.go │ │ ├── equals.go │ │ └── schemaschema.go │ ├── typed/ │ │ ├── doc.go │ │ ├── helpers.go │ │ ├── merge.go │ │ ├── parser.go │ │ ├── reconcile_schema.go │ │ ├── remove.go │ │ ├── tofieldset.go │ │ ├── typed.go │ │ ├── union.go │ │ └── validate.go │ └── value/ │ ├── allocator.go │ ├── doc.go │ ├── fields.go │ ├── jsontagutil.go │ ├── list.go │ ├── listreflect.go │ ├── listunstructured.go │ ├── map.go │ ├── mapreflect.go │ ├── mapunstructured.go │ ├── reflectcache.go │ ├── scalar.go │ ├── structreflect.go │ ├── value.go │ ├── valuereflect.go │ └── valueunstructured.go └── yaml/ ├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── OWNERS ├── README.md ├── RELEASE.md ├── SECURITY_CONTACTS ├── code-of-conduct.md ├── fields.go ├── yaml.go └── yaml_go110.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .deepsource.toml ================================================ version = 1 [[analyzers]] name = "go" enabled = true [analyzers.meta] import_paths = ["github.com/docker-slim/docker-slim"] ================================================ FILE: .dockerignore ================================================ ** !dist_linux/** !dist_linux_arm64/** !build/package/docker/.ds.container.d3e2c84f976743bdb92a7044ef12e381 **/.DS_Store **/*.command ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: dockerslim open_collective: docker-slim ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: docker-slim liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] ================================================ FILE: .github/ISSUE_TEMPLATE.md ================================================ Expected Behavior ================= --- Actual Behavior ================= --- Steps to Reproduce the Problem =============================== 1. 1. 1. --- Specifications ================= - Version: - Platform: ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ [Fixes-###](https://github.com/docker-slim/docker-slim/issues/###) ================================================================== What =============== Why =============== How Tested =============== ================================================ FILE: .github/dependabot.yml ================================================ --- version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "daily" - package-ecosystem: "gitsubmodule" directory: "/" schedule: interval: "daily" ================================================ FILE: .github/workflows/acceptance-testing-bats.yml ================================================ name: Acceptance testing (using bats tests) on: push: branches: - master pull_request: branches: - master types: [opened, synchronize, closed] jobs: test: strategy: matrix: go: ['1.21'] runs-on: 'ubuntu-latest' steps: - uses: actions/checkout@v6.0.2 with: fetch-depth: 0 submodules: recursive - uses: actions/setup-go@v6.3.0 with: go-version: ${{ matrix.go }} - name: Build Slim (quick dev) run: make build_dev - name: Run the bats test suite run: ./test/bats/bin/bats test/debug.bats ================================================ FILE: .github/workflows/acceptance-testing-e2e.yml ================================================ name: Acceptance testing (using e2e framework and examples) on: push: branches: - master pull_request: branches: - master types: [opened, synchronize, closed] jobs: build: runs-on: ubuntu-latest steps: - name: Check out slimtoolkit/slim repo uses: actions/checkout@v6.0.2 - name: Set up Go development environment uses: actions/setup-go@v6.3.0 with: go-version: '1.21.4' - name: Build Slim (multi-arch) run: make build - name: Upload Slim app binaries (Linux, amd64) uses: actions/upload-artifact@v7.0.0 with: name: dist_linux path: dist_linux retention-days: 1 - name: Upload Slim app binaries (Linux, arm64) uses: actions/upload-artifact@v7.0.0 with: name: dist_linux_arm64 path: dist_linux_arm64 retention-days: 1 - name: Upload Slim app binaries (Linux, arm) uses: actions/upload-artifact@v7.0.0 with: name: dist_linux_arm path: dist_linux_arm retention-days: 1 - name: Upload Slim app binaries (macOS, amd64) uses: actions/upload-artifact@v7.0.0 with: name: dist_mac path: dist_mac retention-days: 1 # Run sensor tests using the e2e testing framework. test-sensor-e2e: needs: build strategy: matrix: os: # macos runners don't support docker yet - ubuntu-latest include: - os: ubuntu-latest bin_artifacts: dist_linux runs-on: ${{ matrix.os }} steps: - name: Check out slimtoolkit/slim repo uses: actions/checkout@v6.0.2 - name: Set up Go development environment uses: actions/setup-go@v6.3.0 with: go-version: '1.21.4' - name: Download Slim app binaries uses: actions/download-artifact@v8.0.0 with: name: ${{ matrix.bin_artifacts }} path: bin - name: Fix Slim app binaries permissions run: chmod a+x bin/* - name: Add Slim bin folder to $PATH run: echo "${GITHUB_WORKSPACE}/bin" >> $GITHUB_PATH - name: Run sensor e2e tests timeout-minutes: 60 run: make test-e2e-sensor # Run full-cycle tests (ab)using the slimtoolkit/examples repository. test-full-e2e: needs: build strategy: matrix: os: # macos runners don't support docker yet - ubuntu-latest suite: - compose - distroless - dotnet - elixir - golang - haskell - http-probe - image-edit - java - node - php - python - ruby - rust include: - os: ubuntu-latest bin_artifacts: dist_linux runs-on: ${{ matrix.os }} steps: - name: Check out slimtoolkit/slim repo uses: actions/checkout@v6.0.2 - name: Check out slimtoolkit/examples repo uses: actions/checkout@v6.0.2 with: repository: slimtoolkit/examples path: e2e - name: Download Slim app binaries uses: actions/download-artifact@v8.0.0 with: name: ${{ matrix.bin_artifacts }} path: bin - name: Fix Slim app binaries permissions run: chmod a+x bin/* - name: Add Slim app bin folder to $PATH run: echo "${GITHUB_WORKSPACE}/bin" >> $GITHUB_PATH - name: Run all e2e tests from slimtoolkit/examples timeout-minutes: 60 run: | export DSLIM_LOG_LEVEL=debug export DSLIM_SHOW_CLOGS=1 export DSLIM_EXAMPLES_DIR=e2e make test-e2e-${{ matrix.suite }} ================================================ FILE: .github/workflows/codesee-arch-diagram.yml ================================================ name: CodeSee permissions: read-all on: push: branches: - master pull_request_target: types: [opened, synchronize, reopened] jobs: codesee: runs-on: ubuntu-latest continue-on-error: true name: Analyze the repo with CodeSee steps: - uses: Codesee-io/codesee-action@v2 with: codesee-token: ${{ secrets.CODESEE_ARCH_DIAG_API_TOKEN }} ================================================ FILE: .github/workflows/unit-testing.yml ================================================ name: Unit testing on: push: branches: - master pull_request: branches: - master types: [opened, synchronize, closed] jobs: test: runs-on: ubuntu-latest steps: - name: Check out slimtoolkit/slim repo uses: actions/checkout@v6.0.2 - name: Set up Go development environment uses: actions/setup-go@v6.3.0 with: go-version: '1.21.4' - name: Run tests run: make test ================================================ FILE: .gitignore ================================================ .DS_Store /docker-slim /slim /docker-slim-sensor /slim-sensor /dist_linux*/ /dist_mac/ /dist_mac_m1/ /bin/ /_gopath/ slim.report.json dist_linux*.tar.gz dist_mac.zip dist_mac_m1.zip .idea *.swp *.swo slim.report.json ================================================ FILE: .gitmodules ================================================ [submodule "test/bats"] path = test/bats url = https://github.com/bats-core/bats-core.git [submodule "test/test_helper/bats-support"] path = test/test_helper/bats-support url = https://github.com/bats-core/bats-support.git [submodule "test/test_helper/bats-assert"] path = test/test_helper/bats-assert url = https://github.com/bats-core/bats-assert.git ================================================ FILE: .gitpod.yml ================================================ # List the start up tasks. You can start them in parallel in multiple terminals. # https://www.gitpod.io/docs/config-start-tasks/ tasks: - init: > make tools build command: make inspect # Enable prebuilds of your project to enable faster workspace start times. # https://www.gitpod.io/docs/prebuilds/#configure-the-github-app github: prebuilds: master: true branches: true pullRequests: true pullRequestsFromForks: true addCheck: true ================================================ FILE: .markdownlint.json ================================================ { "MD024": false, "MD013": false } ================================================ FILE: .vscode/launch.json ================================================ { // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "Launch Package", "type": "go", "request": "launch", "mode": "auto", "program": "${fileDirname}", "args": ["registry", "image-index-create", "--image-index-name", "dslim/slim-multi-test:latest", "--image-name", "dslim/slim-arm:latest", "--image-name", "dslim/slim:latest" ] } ] } ================================================ FILE: ADOPTERS.md ================================================ # Adopters TBD ================================================ FILE: CHANGELOG.md ================================================ # Releases ## 1.40.11 (2/2/2024) ### New Features - New `build` command flags (`--include-dir-bins` and `--include-ssh-client`). - Simple `images` command to list container images. ### Improvements - OCI image format support in `xray`. - Improved `xray` command reports to include object type information. ### Bug Fixes - Fixes and dependency updates to support the new Docker Engine version (25.x). ## 1.40.10 (1/17/2024) ### Bug Fixes - Sensor artifact (post-)processing bug fix for additional PT generated artifacts. ## 1.40.9 (1/15/2024) ### Improvements - Added command parameter information to process events in `mondel`. - Enhanced `mondel` event capture to prevent event data loss on sensor shutdown. ## 1.40.8 (1/7/2024) ### New Features - New `vulnerability` command and the `epss` subcommand to lookup EPSS scores for vulnerabilities. - Simple `registry server` command to have a local OCI registry (thank you Sarvesh Raj, @sarveshraj, for your contribution!). - Simple `registry push` command to push local images to a registry. - Simple `images` command to list container images. - RPM packaging for the apps (thank you Rohan Jamadagni, @Rohansjamadagni, for your contribution!) ### Improvements - Enhanced `registry pull` command to pull images from authenticated registries. - `quiet` mode improvements (WIP) to hide the standard execution context output when it's enabled. - `quiet` mode for the `images` command. - Interactive prompt updates to include the `images`, `registry` and `vulnerability` commands and a couple of global flags. - Monitor Data Event Log (mondel) enhancement to improve the write path. ## 1.40.7 (12/9/2023) ### New Features - Simple `registry image-index-create` command to create multi-architecture images. - Simple `images` command to list container images. ### Improvements - Improved ptmon syscall handling. - Enhanced `mondel` events with timestamps and sequence numbers. - Extra docker socket validation checks. - Version info on exit/failure. - Temp container cleanup improvements. - ARM image build scripts for the containerized distribution. ### Bug Fixes - Websocket http probe bug fix. - Various ptmod bug fixes. ## 1.40.5/1.40.6 (11/2/2023) ### New Features - Sensor `control` commands to control sensor execution when running in the standalone mode (first command: `stop-target-app`). - `xray` - detect system identities (users, groups) and their properties (`--detect-identities` flag, enabled by default). - `build` - Keep the OS/libc zoneinfo data (`--include-zoneinfo` flag, disabled by default). - `build`/`profile` - Mon(itor) Data Event Log (aka `mondel`) - optional data event log for sensor monitors to log/stream monitor events (`--enable-mondel` main app flag, `--mondel`/`-n` sensor flag(s)). ### Improvements - `target-app-running` sensor lifecycle hook. - `build`/`profile`: `--env-file` to load env vars from a file. - `build`/`profile`: basic input validation to ignore malformed env var data for the `--env` flag. - `build`: Using internal output image builder by default (`--image-build-engine` flag) - Renamed the reverse engineered Dockerfile from `Dockerfile.fat` to `Dockerfile.reversed` ### Bug Fixes - Various bug fixes ## 1.40.4 (8/25/2023) ### Improvements - Auto-complete in the interactive `prompt` mode for the target, namespace, pod and session flags - Interactive `debug` command terminal that runs as if you are connected directly to the target image you are debugging (enabled by default) - Basic sessions for `debug` command - Ability to show logs for the existing `debug` command sessions - More `debug` command flags (see README) - README docs updates for the `debug` command ### Bug Fixes - Many `debug` command bug fixes ## 1.40.3 (7/13/2023) ### New Features - Kubernetes runtime support for the `debug` command - `appbom` command in the main app and `--appbom` flag in the sensor - `merge` command to merge two container images (optimized to merge two minified images). ### Improvements - More `debug` command flags - README docs for the `debug` command - Ability to detect the Docker Desktop unix socket - Code and logging cleanup ### Bug Fixes - Sensor volume fix for sensor symlinks (to address the Homebrew installed problems with sensor) - Various dependency updates to get security fixes ## 1.40.2 (5/12/2023) ### Improvements - New experimental `build` command flag to prevent the vulnerability scanners from discovering the metadata they need to identify the vulnerabilities (`--obfuscate-metadata`) inspired by the [`Malicious Compliance`](https://kccnceu2023.sched.com/event/1Hybu/malicious-compliance-reflections-on-trusting-container-scanners-ian-coldwater-independent-duffie-cooley-isovalent-brad-geesaman-ghost-security-rory-mccune-datadog) KubeCon EU 2023 talk ### Bug Fixes - HEALTHCHECK instruction decoding enhancements to handle the data generated by buildah - fsutil format string bug fix ## 1.40.1 (4/5/2023) ### Improvements - New include flags for the `build` command (`--include-workdir`) - Debug/trace logging improvements ### Bug Fixes - todo: add info ## 1.40.0 (1/15/2023) ### New Features - Base image metadata for xray - Basic support for multiple image build engines (`--image-build-engine`, `--image-build-arch` parameters) ### Improvements - dockerfile reverse engineering updates - buildkit dockerfile instruction support - name change ### Bug Fixes - todo: add info ## 1.39.1 (11/12/2022) ## 1.39.0 (10/24/2022) ## 1.38.0 (8/27/2022) ### New Features - Experimental 'debug' command - JSON console output format ### Improvements - refactored http-probe-exec and http-probe-exec-file to be host-exec and host-exec-file (breaking change) ### Bug Fixes - todo: add info ## 1.37.6 (4/22/2022) ### Improvements - Source image label in minified images - Full image path enhancements for container entry info ### Bug Fixes - Traced application signal handling bugfix - Healthcheck instruction parsing bugfix ## 1.37.5 (3/20/2022) ### New Features - Experimental Node.js package include flag - Experimental Next.js(React.js) app include flags - Experimental Nuxt.js(Vue.js) app include flags - Ability to disable the ptrace data source ## 1.37.4 (2/27/2022) ### New Features - Container probe feature to use one of the compose services to test/probe the target container (`--container-probe-compose-svc` flag and `container.probe` continue-after mode) - Ability to override the container image name and/or tag when targetting a compose service (`--target-compose-svc-image` flag) - Ability to wait before executing the HTTP probes (`--http-probe-start-wait` flag) - Ability to wait before starting each compose service (`--compose-svc-start-wait` flag) - Basic FastCGI protocol support in HTTP probes (docs TBD) - New `registry` command and a basic `pull` subcommand - `--include-new` build flag to keep new files created by target during dynamic analysis - Supprot for stored global param in `slim.config.json` ### Improvements - Improved containerized CI/CD environments support (`sensor-ipc-mode` and `sensor-ipc-endpoint` flags for `build` and `profile`) - Docker host detection improvements - Target container IP detection improvements - Not minifying onbuild base images by default - Not minifying already minified images - Cleanup container resources on exit - `include-cert-all` build flag enabled by default - Propagate logging flags to sensor - Not using default http probe if custom probes are already defined - Many compose related enhancements (volume lookup enhancements, compose image detection and error handling, etc) - Various monitoring engine enhancements - Migrate from urfave/cli/v1 to urfave/cli/v2 - Dockerfile reverse engineering enhancements (HEALTHCHECK instruction support, improved RUN instruction reversing when ARGs are also used) ## 1.37.3 (12/10/2021) ### New Features - Install command / docker cli plugin install option (preview version) ### Improvements - Container and compose link handling enhancements - Volume mounting enhancements - Static analysis improvements - Symlink handling improvements for builds - Collecting file check filesystem activity - Entrypoint/cmd override handling improvements ### Bug Fixes - Volume mounting bug fixes for compose ## 1.37.1/1.37.2 (11/7/2021) ### New Features - Ability to pull images from private registries (`--registry-account`, `--registry-secret`, `--docker-config-path` flags) ### Improvements - Additional flags for compose (`dep-include-target-compose-svc-deps`, `compose-env-nohost`, `compose-env-file`, `compose-workdir`, `compose-project-name`) - Variable substitution support in compose - Detect duplicates by default in xray - Resource cleanup when the build command exits - `delete-generated-fat-image` flag to cleanup the non-optimized images when `docker-slim` builds images from source/Dockerfile - Improved `maintainer` info collection for xray ### Bug Fixes - Volume mounting bug fixes for compose ## 1.37.0 (9/23/2021) ### New Features - Experimental docker-compose support for the build command - Include cert flags to make it easier to keep certificate data in the optimized images ### Improvements - Install script ## 1.36.4 ## 1.36.3 (8/30/2021) ## 1.36.2 (8/5/2021) ## 1.36.1 (6/20/2021) ### Improvements - `--cro-host-config-file`, `--cro-sysctl` and `--cro-shm-size` flags. - M1 builds. ### Bug Fixes - xray and sensor volume detection bug fixes. ### Improvements - Ability to detect additional shells. - Saving command report to /tmp directory if it's not possible to save it in the current working directory. - Printing tag information for build command. ### Bug Fixes - Default `continue-after` value handling fix (remove `probe` mode if http probing is disabled). - Sensor not exiting when it's trying to copy a directory it already copied. ## 1.36.0 (6/12/2021) ### New Features - Ability to find duplicate files for xray (`--detect-duplicates`, `--show-duplicates`). - Ability to find all utf8 encoded files for xray using the `--detect-utf8` flag (optionally dumping them to console, directory or tar file). - Ability to find the files with special permissions (`--show-special-perms`). - Ability to find all installed shells for xray. - Container entry information for xray with file detection. - Inherited image instructions (aka ONBUILD instructions) for xray. - More image level stats for xray. ### Improvements - Multiple tags for the build command. - `--http-probe-off` flag for the build command to provide a shortcut to disable HTTP probing. - Flexible target image handling to use non-default tags if the `latest` tag doesn't exist and no explicit tag is provided. ## 1.35.2 (5/2/2021) ### New Features - `change-match-layers-only` xray flag to print only the layers that contain the matches. ### Improvements - xray enhancement: printing to console by default for pattern or data matches. ### Bug Fixes - Various xray command bug fixes. ## 1.35.1 (4/27/2021) ### Improvements - Ability to combine `probe` and `exec` `continue-after` modes ### Bug Fixes - Various xray command bug fixes ## 1.35.0 (4/14/2021) ### New Features - Console color output (on by default; disable with `no-color`) - Loading http probe request data from separate files - Ability to execute external probe commands (`--http-probe-exec` and `--http-probe-exec-file` flags) - Ability to preserve original files in the target container discarding its test runtime data (`--preserve-path` and `--preserve-path-file`) - Ability to pull container images if they don't exist locally yet (`--pull` and `--show-plogs`) - File hashing for xray (`--hash-data`) - Additional flags to control the xray command executions (`--top-changes-max`, `--reuse-saved-image`) - Ability to match by file path, file data and file hash for xray (`--change-path value`, `--change-data value`, `--change-data-hash value`) ### Improvements - Lots of additional container build flags (`--tag-fat`, `--cbo-add-host`, `--cbo-build-arg`, `--cbo-label`, `--cbo-target`, `--cbo-network`, `--cbo-cache-from`). - Additional container runtime flags (`--cro-runtime`) - `sigint` should kill the running container (#186) ### Bug Fixes - Various xray image layer inspection bug fixes ## 1.34.0 (1/29/2021) ### New Features - New `xray` flags to control what layer change data to include in the generated reports (`layer-changes-max`, `all-changes-max`, `add-changes-max`, `modify-changes-max`, `delete-changes-max`) ### Improvements - `host` network flag handling enhancements. - Returning non-zero exit codes on failures - Additional image checks to catch missing ENTRYPOINT/CMD instructions ### Bug Fixes - Fixed container image listing bug that broke the `--target` value suggestions in the interactive prompt mode. ## 1.33.0 (12/12/2020) ### New Features - Ability to interact with the temporary containers using the `--exec` and `--exec-file` flags ### Improvements - `npm` support enhancements (makes it possible to use `npm start` in Dockerfiles, which isn't recommended though) ### Bug Fixes - Various bug fixes. ## 1.32.0 (8/23/2020) ### New Features - Mapping container ports to specific host ports analyzing image at runtime (`--publish-port` and `--publish-exposed-ports` flags) ### Improvements - `seccomp` security profile generation capability updates - User namespace handling improvements (thanks to `@solarnz`) ## 1.31.0 (8/13/2020) ### New Features - Experimental HTTP probe command generation based on the API descriptions from the Swagger and OpenAPI specs (`--http-probe-apispec` and `--http-probe-apispec-file` flags) - Image metadata editing capabilities to add, remove and update the LABEL, VOLUME, EXPOSE, ENV and WORKDIR instructions (`--new-workdir`, `--new-expose`, `--new-label`, `--new-volume`, `--remove-volume`, `--remove-env`, `--remove-label`, `--remove-expose` and `--image-overrides` combined with `--expose`, `--workdir`, `--env`, `--volume`, `--label`, `--env`) ### Improvements - Layer change details available in the `xray` command reports when the `--changes` flag is set. - System and engine information in the command reports to improve debugging - Ability to enable crawling for the HTTP probes specified using the `--http-probe-cmd` flag - Improved HTTP probe crawler documentation ## 1.30.0 (7/27/2020) ### New Features - `lint` command (initial Dockerfile linting capabilities with a basic set of checks) - HTTP probe crawler (automatically probes additional endpoints referenced in the processed targets; see the `--http-probe-crawl` and related flags) ### Improvements - ARM64 support (need more people to test!) - `--http-probe-exit-on-failure` flag to exit execution when all HTTP probe calls fail - `--include-bin-file` and `--include-exe-file` flags to make it easier to specify multiple binaries and executables loading them from files - `xray` command report enhancements ## 1.29.0 (3/18/2020) ### New Features - Interactive CLI prompt ### Improvements - `xray` command output improvements - Additional image data saved with the `xray` command reports (`--add-image-manifest` and `--add-image-config` flags) ## 1.28.1 (3/9/2020) ### Improvements - New `xray` parameters to control how much to show when it's printing the layer details (`--changes value` and `--layer value`) - Image history enhancements and more data saved in the xray command reports ## 1.28.0 (3/6/2020) ### New Features - `xray` command enhancements to show the detailed container image information including its layers and their files and directories (initial version). ### Improvements - The `--exclude-pattern` `build` parameter to filter/exclude the artifacts in the optimized container. ## 1.27.0 (2/28/2020) ### New Features - Option to set permissions, user and group information for the artifacts included with the `--include-*` parameters. - Option to overwrite the permissions and ownership info in the optimized image using the new `--path-perms` and `path-perms-file` parameters. ### Improvements - Option to run the containerized application using user and group information from the USER instruction. - Filter leftover PID files. - UX enhancements for the containers created using Dockerfiles. - Additional debugging information. ### Bug Fixes - Support for special install directories on Linux (to prevent failures when `docker-slim` is trying to save its state). ## 1.26.1 (11/28/2019) ### Improvements - Saving command execution report, by default (`slim.report.json`). - CLI output UX enhancements. - Docker connect info checks. ### Bug Fixes - Version check fixes when running in containers. ## 1.26 (11/16/2019) ### New Features - Run `docker-slim` in containers. - New distribution option ([`dslim/docker-slim`](https://hub.docker.com/r/dslim/docker-slim) image available in Docker Hub). - Archive `docker-slim` state into a separate Docker volume. ### Improvements - Default to continuing `docker-slim` execution after the http probing step is done when http probing is enabled. - Improved IPC. - Improved seccomp and metadata artifact copy option. - Improved execution report. ## 1.25.3 (8/4/2019) ### New Features - Build minified images from `source` using the new `--from-dockerfile` build flag (see `README.md` for details). ### Improvements - Custom HTTP POST probes support request bodies ## 1.25.2 (7/21/2019) ### New Features - Enhanced build command reports with additional container image metadata (using the global `--report` flag) - Ability to update the minified image Dockerfile instructions (using the --new-cmd, --new-entrypoint, --new-expose, --new-workdir, --new-env and --image-overrides flags) - Dockerfile volume support ### Improvements - HTTP probes by default (you will have to disable HTTP probes if you don't need them) - Various UX enhancements to provide better CLI feedback and to avoid generating minified images that might not work ### Bug Fixes - TTY bug fix caused by an external dependency (used to track update download progress) ## 1.25.0 (4/23/2019) ### New Features - Experimental ARM32 support - Easy way to keep a shell in your image (just pass `--include-shell` to the `build` command) - Easy way to include additional executables (`--include-exe` flag) and binary objects (`--include-bin` flag), which will also include their binary dependencies, so you don't have to explicitly include them all yourself - `update` command - now you can update `docker-slim` from `docker-slim`! - Current version checks to know if the installed release is out of date ### Improvements - Improvements to handle complex `--entrypoint` and `--cmd` parameters ## Previous Releases - Better Mac OS X support - when you install `docker-slim` to /usr/local/bin or other special/non-shared directories docker-slim will detect it and use the /temp directory to save its artifacts and to mount its sensor - HTTP Probing enhancements and new flags to control the probing process - Better Nginx support - Support for non-default users - Improved symlink handling - Better failure monitoring and reporting - The `--include-path-file` option to make it easier to load extra files you want to keep in your image - CentOS support - Enhancements for ruby applications with extensions - Save the docker-slim command results in a JSON file using the `--report` flag - Better support for applications with dynamic libraries (e.g., python compiled with `--enable-shared`) - Additional network related Docker parameters - Extended version information - Alpine image support - Ability to override ENV variables analyzing target image - Docker 1.12 support - User selected location to store DockerSlim state (global `--state-path` parameter). - Auto-generated seccomp profiles for Docker 1.10. - Python 3 support - Docker connect options - HTTP probe commands - Include extra directories and files in minified images ================================================ FILE: COMMUNITY_ACTIVITY_LOG.md ================================================ # Community Activity Log ## Meetings/Events TBD ## Engaging with CNCF TAGs TBD ## Collaborating/Integrating with Other Projects (CNCF or Not) TBD ## Conferences TBD ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing to SlimToolkit If you want to contribute submit a GitHub pull request or open an issue. Thank you! ## TL;DR Any contribution is better than no contribution :-) Submit a [pull request](https://help.github.com/articles/using-pull-requests) or open an issue even if you are not sure or if you feel the contribution is not significant enough. If you want to accelerate the process you can follow the guidelines below. There's a number of already open issues with the `good first issue` tag. Start there if you are new to the project. Explore the issues with the `help wanted` tag too. Help with any open issue is highly appreciated, of course. You can also take a look at the [`ROADMAP`](ROADMAP.md) to get ideas for your contribution. Also take a look at the [`WISHLIST`](WISHLIST.md) doc, which includes even more potential improvements (that are not a part of the roadmap yet, but they are still valuable). ## Code To learn more about the code take a look at the `SlimToolkit Code` videos on ['YouTube'](https://www.youtube.com/channel/UCy7RHjJlaBhpCCbChrd8POA?sub_confirmation=1) ## Guidelines ### Reporting Issues Open an issue and provide enough cotext information to repro the condition. Don't worry if it's not possible to reproduce (reliably). Submit an issue anyways including the information you do have. The version information should be provided in all bug reports. ### Environment Setup See the `BUILD PROCESS` section in the `README.md` file. ### Enhancements Take a look at the open issues or feel free to create a new issue if what you'd like to do doesn't have an issue already. This includes documentation enhancements and design proposals too. When you submit your [pull request](https://help.github.com/articles/using-pull-requests) make sure to reference the issue. ### Coding Standards The coding standards are based on the [Golang community standards](https://github.com/golang/go/wiki/CodeReviewComments). Make sure to leverage the `scripts/src.fmt.sh` and `scripts/src.inspect.sh` helper scripts or their equivalent. ### Dependencies TBD - information about adding dependencies ### Testing Yes :-) ### Documentation Yes :-) ### Pull Requests Standard guidelines for [pull requests](https://help.github.com/articles/using-pull-requests) ### Sign Your Code Please certify your [Developer Certificate of Origin (DCO)](https://developercertificate.org/), by signing off your commit with `git commit -s` (use your real name). ================================================ FILE: GOVERNANCE.md ================================================ # Governance TBD ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS Copyright 2015-2022 SlimToolkit. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: MAINTAINERS.md ================================================ # Maintainers - [Kyle Quest](https://github.com/kcq) (@kcq) - [Ivan Velichko](https://github.com/iximiuz) (@iximiuz) ================================================ FILE: Makefile ================================================ default: build_in_docker ## build docker-slim in docker by default build_in_docker: ## build docker-slim in docker rm -rfv bin '$(CURDIR)/scripts/docker-builder.run.sh' build_m1_in_docker: rm -rfv bin '$(CURDIR)/scripts/docker-builder-m1.run.sh' build: ## build docker-slim '$(CURDIR)/scripts/src.build.sh' build_m1: ## build docker-slim '$(CURDIR)/scripts/src.build.m1.sh' build_dev: ## build docker-slim for development (quickly), in bin/ '$(CURDIR)/scripts/src.build.quick.sh' fmt: ## format all golang files '$(CURDIR)/scripts/src.fmt.sh' help: ## prints out the menu of command options @awk -F ':.*?## ' '/^[a-zA-Z0-9_-]+:.*?## / {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) inspect: ## report suspicious constructs and linting errors '$(CURDIR)/scripts/src.inspect.sh' tools: ## install necessary tools '$(CURDIR)/scripts/tools.get.sh' ## run unit tests test: export GO_TEST_FLAGS ?= test: '$(CURDIR)/scripts/src.test.sh' clean: ## clean up '$(CURDIR)/scripts/src.cleanup.sh' include $(CURDIR)/test/e2e-tests.mk .PHONY: default help build_in_docker build_m1_in_docker build build_m1 build_dev fmt inspect tools test clean ================================================ FILE: README.md ================================================ ![SK](assets/images/dslim/logo.png) [![Gitter chat](https://img.shields.io/badge/chat-on%20gitter-brightgreen.svg?style=for-the-badge)](https://gitter.im/docker-slim/community) [![Discord chat](https://img.shields.io/static/v1.svg?label=chat&message=on%20discord&color=7389D8&style=for-the-badge)](https://discord.gg/9tDyxYS) [![Follow](https://img.shields.io/badge/follow-on%20twitter-%231DA1F2.svg?style=for-the-badge&logoColor=white)](https://twitter.com/DockerSlim) [![Youtube](https://img.shields.io/badge/-YouTube-red?style=for-the-badge&logo=youtube&logoColor=white)](https://www.youtube.com/channel/UCy7RHjJlaBhpCCbChrd8POA?sub_confirmation=1) [![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-908a85?logo=gitpod&style=for-the-badge)](https://gitpod.io/#https://github.com/slimtoolkit/slim) [![Install SlimToolkit](https://img.shields.io/badge/install-slim-blue?style=for-the-badge)](https://github.com/slimtoolkit/slim#installation) [![Get Examples](https://img.shields.io/badge/slim-app%20examples-green?style=for-the-badge)](https://github.com/slimtoolkit/examples) [![Try Slim.AI SaaS](https://img.shields.io/badge/try-Slim.AI%20SaaS-red?style=for-the-badge)](https://portal.slim.dev/login?invitecode=invite.1s85zlfnYX0p5TT1XKja49pAHbL) # Optimize Your Experience with Containers. Make Your Containers Better, Smaller, More Secure and Do Less to Get There (free and open source!) Note that **DockerSlim** is now just **Slim** (**SlimToolkit** is the full name, so it's easier to find it online) to show its growing support for additional container tools and runtimes in the cloud native ecosystem. **Slim** is now a CNCF Sandbox project. It was created by [Kyle](https://github.com/kcq) [Quest](https://twitter.com/kcqon) and it's been improved by many [contributors](https://github.com/slimtoolkit/slim/graphs/contributors). The project is supported by Root.io (formerly known as Slim.AI). Here's how Slim and Root work together: Slim helps you build optimized containers, while Root.io automatically fixes vulnerabilities without disrupting your workflows. Use Slim's open source toolkit to optimize containers, then keep them secure with Root's automated vulnerability remediation – from optimization to continuous security in one seamless journey. Learn more at www.root.io. ## Overview SlimToolkit allows developers to inspect, optimize and debug their containers using its `xray`, `lint`, `build`, `debug`, `run`, `images`, `merge`, `registry`, `vulnerability` (and other) commands. It simplifies and improves your developer experience building, customizing and using containers. It makes your containers better, smaller and more secure while providing advanced visibility and improved usability working with the original and minified containers. Don't change anything in your container image and minify it by up to 30x making it secure too! Optimizing images isn't the only thing it can do though. It can also help you understand and author better container images. Keep doing what you are doing. No need to change anything. Use the base image you want. Use the package manager you want. Don't worry about hand optimizing your Dockerfile. You shouldn't have to throw away your tools and your workflow to have small container images. Don't worry about manually creating Seccomp and AppArmor security profiles. You shouldn't have to become an expert in Linux syscalls, Seccomp and AppArmor to have secure containers. Even if you do know enough about it wasting time reverse engineering your application behavior can be time-consuming. SlimToolkit will optimize and secure your containers by understanding your application and what it needs using various analysis techniques. It will throw away what you don't need, reducing the attack surface of your container. What if you need some of those extra things to debug your container? You can use dedicated debugging side-car containers for that (more details below). SlimToolkit has been used with Node.js, Python, Ruby, Java, Go, Rust, Elixir and PHP (some app types) running on Ubuntu, Debian, CentOS, Alpine and even Distroless. Note that some application stacks do require advanced container probing to make sure that all dynamically loaded components are detected. See the `--http-probe*` flags for more details to know how you can define custom probe commands. In some cases you might also need to use the `--include-path` flag to make sure everything your application needs is included (e.g., `ubuntu.com` python SPA app container image [example](https://github.com/slimtoolkit/examples/tree/master/3rdparty/ubuntu-com) where the client side template files are explicitly included). It's also a good idea to use your app/environment tests when you run the Slim app. See the `--continue-after` flag for more details about integrating your tests with the temporary container Slim creates when it's doing its dynamic analysis. Running tests in the target container is also an option, but it does require you to specify a custom ENTRYPOINT/CMD with a custom wrapper to start your app and to execute your tests. ![Slim How](assets/images/docs/SlimHow.jpeg) Interactive CLI prompt screencast: [![asciicast](assets/images/dslim/DockerSlimIntPromptDemo.gif)](https://asciinema.org/a/311513) Watch this screencast to see how an application image is minified by more than 30x. [![asciicast](https://asciinema.org/a/rHqW8cbr3vXe0WxorHsD36n7V.png)](https://asciinema.org/a/rHqW8cbr3vXe0WxorHsD36n7V) When you run the `build` or `profile` commands in Slim it gives you an opportunity to interact with the temporary container it creates. By default, it will pause and wait for your input before it continues its execution. You can change this behavior using the `--continue-after` flag. If your application exposes any web interfaces (e.g., when you have a web server or an HTTP API), you'll see the port numbers on the host machine you will need to use to interact with your application (look for the `port.list` and `target.port.info` messages on the screen). For example, in the screencast above you'll see that the internal application port 8000 is mapped to port 32911 on your host. Note that Slim will interact with your application for you when HTTP probing is enabled (enabled by default; see the `--http-probe*` flag docs for more details). Some web applications built with scripting languages like Python or Ruby require service interactions to load everything in the application. Enable HTTP probing unless it gets in your way. You can also interact with the temporary container via a shell script or snippet using `--exec-file` or `--exec`. For example, you can create a container which is only capable of using curl. ```bash >> docker pull archlinux:latest ... >> slim build --target archlinux:latest --tag archlinux:curl --http-probe=false --exec "curl checkip.amazonaws.com" ... >> docker run archlinux:curl curl checkip.amazonaws.com ... >> docker images archlinux curl ... ... 17.4MB archlinux latest ... ... 467MB ... ``` ## Community Feel free to join any of these channels or just open a new [`Github issue`](https://github.com/slimtoolkit/slim/issues) if you want to chat or if you need help. * [`CNCF Slack channel`](https://cloud-native.slack.com/archives/C059QP1RH1S) * [`Discord server`](https://discord.gg/9tDyxYS) * [`Discussions`](https://github.com/slimtoolkit/slim/discussions) * [`Twitter`](https://twitter.com/SlimToolkit) ## SlimToolkit on the Internet ##### Books: * [`Everyone's Docker/Kubernetes`](https://www.amazon.co.jp/dp/429710461X) (Japanese) * [`Docker in Practice (2nd edition)`](https://www.amazon.com/Docker-Practice-Ian-Miell/dp/1617294802) * [`Docker/Kubernetes Security Practice Guide`](https://www.amazon.co.jp/dp/4839970505) (Japanese) ## Minification Examples You can find the examples in a separate repository: [https://github.com/slimtoolkit/examples](https://github.com/slimtoolkit/examples) Node.js application images: - from ubuntu:14.04 - 432MB => 14MB (minified by **30.85X**) - from debian:jessie - 406MB => 25.1MB (minified by **16.21X**) - from node:alpine - 66.7MB => 34.7MB (minified by **1.92X**) - from node:distroless - 72.7MB => 39.7MB (minified by **1.83X**) Python application images: - from ubuntu:14.04 - 438MB => 16.8MB (minified by **25.99X**) - from python:2.7-alpine - 84.3MB => 23.1MB (minified by **3.65X**) - from python:2.7.15 - 916MB => 27.5MB (minified by **33.29X**) - from centos:7 - 647MB => 23MB (minified by **28.57X**) - from centos/python-27-centos7 - 700MB => 24MB (minified by **29.01X**) - from python2.7:distroless - 60.7MB => 18.3MB (minified by **3.32X**) Ruby application images: - from ubuntu:14.04 - 433MB => 13.8MB (minified by **31.31X**) - from ruby:2.2-alpine - 319MB => 27MB (minified by **11.88X**) - from ruby:2.5.3 - 978MB => 30MB (minified by **32.74X**) Go application images: - from golang:latest - 700MB => 1.56MB (minified by **448.76X**) - from ubuntu:14.04 - 531MB => 1.87MB (minified by **284.10X**) - from golang:alpine - 258MB => 1.56MB (minified by **165.61X**) - from centos:7 - 615MB => 1.87MB (minified by **329.14X**) Rust application images: - from rust:1.31 - 2GB => 14MB (minified by **147.16X**) Java application images: - from ubuntu:14.04 - 743.6 MB => 100.3 MB PHP application images: - from php:7.0-cli - 368MB => 26.6MB (minified by **13.85X**) Haskell application images: - (Scotty service) from haskell:8 - 2.09GB => 16.6MB (minified by **125.32X**) - (Scotty service) from haskell:7 - 1.5GB => 21MB (minified by 71X) Elixir application images: - (Phoenix service) from elixir:1.6 - 1.1 GB => 37 MB (minified by **29.25X**) --- - [RECENT UPDATES](#recent-updates) - [INSTALLATION](#installation) - [BASIC USAGE INFO](#basic-usage-info) - [COMMANDS](#commands) - [USAGE DETAILS](#usage-details) - [`LINT` COMMAND OPTIONS](#lint-command-options) - [`XRAY` COMMAND OPTIONS](#xray-command-options) - [`BUILD` COMMAND OPTIONS](#build-command-options) - [`DEBUG` COMMAND OPTIONS](#debug-command-options) - [`RUN` COMMAND OPTIONS](#run-command-options) - [`REGISTRY` COMMAND OPTIONS](#registry-command-options) - [`VULNERABILITY` COMMAND OPTIONS](#vulnerability-command-options) - [RUNNING CONTAINERIZED](#running-containerized) - [DOCKER CONNECT OPTIONS](#docker-connect-options) - [HTTP PROBE COMMANDS](#http-probe-commands) - [DEBUGGING MINIFIED CONTAINERS](#debugging-minified-containers) - [MINIFYING COMMAND LINE TOOLS](#minifying-command-line-tools) - [QUICK SECCOMP EXAMPLE](#quick-seccomp-example) - [USING AUTO-GENERATED SECCOMP PROFILES](#using-auto-generated-seccomp-profiles) - [ORIGINAL DEMO VIDEO](#original-demo-video) - [DEMO STEPS](#demo-steps) - [FAQ](#faq) - [Is it safe for production use?](#is-it-safe-for-production-use) - [How can I contribute if I don't know Go?](#how-can-i-contribute-if-i-dont-know-go) - [What's the best application for Slim?](#whats-the-best-application-for-slim) - [Can I use Slim with dockerized command line tools?](#can-i-use-slim-with-dockerized-command-line-tools) - [What if my Docker images uses the USER command?](#what-if-my-docker-images-uses-the-user-command) - [Nginx fails in my minified image](#nginx-fails-in-my-minified-image) - [Slim fails with a 'no permission to read from' error](#slim-fails-with-a-no-permission-to-read-from-error) - [BUILD PROCESS](#build-process) - [Build Steps](#build-steps) - [CONTRIBUTING](#contributing) - [DESIGN](#design) - [CORE CONCEPTS](#core-concepts) - [DYNAMIC ANALYSIS OPTIONS](#dynamic-analysis-options) - [SECURITY](#security) - [CHALLENGES](#challenges) - [ORIGINS](#origins) - [MINIFIED IMAGES ON DOCKER HUB](#minified-images-on-docker-hub) - [LICENSE](#license) ## RECENT UPDATES Latest version: `1.40.11` (`2/2/2024`) The 1.40.11 version adds support for the latest Docker Engine version, improves `xray` reports and adds new `build` command flags (`--include-dir-bins` and `--include-ssh-client`). For more info about the latest release see the [`CHANGELOG`](CHANGELOG.md). ## INSTALLATION If you already have Slim installed use the `update` command to get the latest version: ``` slim update ``` ### Downloads 1. Download the zip package for your platform. - [Latest Mac binaries](https://github.com/slimtoolkit/slim/releases/download/1.40.11/dist_mac.zip) (`curl -L -o ds.zip https://github.com/slimtoolkit/slim/releases/download/1.40.11/dist_mac.zip`) - [Latest Mac M1 binaries](https://github.com/slimtoolkit/slim/releases/download/1.40.11/dist_mac_m1.zip) (`curl -L -o ds.zip https://github.com/slimtoolkit/slim/releases/download/1.40.11/dist_mac_m1.zip)`) - [Latest Linux binaries](https://github.com/slimtoolkit/slim/releases/download/1.40.11/dist_linux.tar.gz) (`curl -L -o ds.tar.gz https://github.com/slimtoolkit/slim/releases/download/1.40.11/dist_linux.tar.gz`) - [Latest Linux ARM binaries](https://github.com/slimtoolkit/slim/releases/download/1.40.11/dist_linux_arm.tar.gz) (`curl -L -o ds.tar.gz https://github.com/slimtoolkit/slim/releases/download/1.40.11/dist_linux_arm.tar.gz`) - [Latest Linux ARM64 binaries](https://github.com/slimtoolkit/slim/releases/download/1.40.11/dist_linux_arm64.tar.gz) (`curl -L -o ds.tar.gz https://github.com/slimtoolkit/slim/releases/download/1.40.11/dist_linux_arm64.tar.gz`) 2. Unzip the package and optionally move it to your bin directory. Linux (for non-intel replace `dist_linux` with the platform-specific extracted path): ``` tar -xvf ds.tar.gz mv dist_linux/slim /usr/local/bin/ mv dist_linux/slim-sensor /usr/local/bin/ ``` Mac: ``` unzip ds.zip mv dist_mac/slim /usr/local/bin/ mv dist_mac/slim-sensor /usr/local/bin/ ``` 3. Add the location where you unzipped the package to your PATH environment variable (optional). If the directory where you extracted the binaries is not in your PATH then you'll need to run your Slim app binary from that directory. #### Scripted Install You can also use this script to install the current release of Slim on Linux (x86 and ARM) and macOS (x86 and Apple Silicon) ```bash curl -sL https://raw.githubusercontent.com/slimtoolkit/slim/master/scripts/install-slim.sh | sudo -E bash - ``` ### Homebrew ``` brew install docker-slim ``` The Homebrew installer: https://formulae.brew.sh/formula/docker-slim ### Docker ``` docker pull dslim/slim ``` See the [RUNNING CONTAINERIZED](#running-containerized) section for more usage info. ### SaaS Powered by Slim. It will help you understand and troubleshoot your application containers and a lot more. If you use the `xray` command you'll want to try the SaaS. Understanding image changes is easy with its container diff capabilities. Connect your own registry and you can do the same with your own containers. Try it [here](https://portal.slim.dev/login?invitecode=invite.1s85zlfnYX0p5TT1XKja49pAHbL) without installing anything locally. ## BASIC USAGE INFO `slim [global flags] [xray|build|profile|run|debug|lint|merge|images|registry|vulnerability|update|version|appbom|help] [command-specific flags] ` If you don't specify any command `slim` will start in the interactive prompt mode. ### COMMANDS - `xray` - Performs static analysis for the target container image (including 'reverse engineering' the Dockerfile for the image). Use this command if you want to know what's inside of your container image and what makes it fat. - `lint` - Analyzes container instructions in Dockerfiles (Docker image support is WIP) - `build` - Analyzes, profiles and optimizes your container image generating the supported security profiles. This is the most popular command. - `debug` - Debug the running target container. This command is useful for troubleshooting running containers created from minimal/minified or regular container images. - `registry` - Execute registry operations (`pull`, `push`, `copy`, `server`). - `profile` - Performs basic container image analysis and dynamic container analysis, but it doesn't generate an optimized image. - `run` - Runs one or more containers (for now runs a single container similar to `docker run`) - `merge` - Merge two container images (optimized to merge minified images). - `images` - Get information about container images (example: `slim --quiet images`). - `vulnerability` - Execute vulnerability related tools and operations (`epss`). - `version` - Shows the version information. - `appbom` - Shows the application BOM (app composition/dependencies). - `update` - Updates Slim to the latest version. - `help` - Show the available commands and global flags Example: `slim build my/sample-app` See the `USAGE DETAILS` section for more details. Run `slim help` to get a high level overview of the available commands. Run `slim COMMAND_NAME` without any parameters and you'll get more information about that command (e.g., `slim build`). If you run `slim` without any parameters you'll get an interactive prompt that will provide suggestions about the available commands and flags. `Tabs` are used to show the available options, to autocomplete the parameters and to navigate the option menu (which you can also do with Up and Down arrows). `Spaces` are used to move to the next parameter and `Enter` is used to run the command. For more info about the interactive prompt see [`go-prompt`](https://github.com/c-bata/go-prompt). ## USAGE DETAILS `slim [global options] command [command options] ` Commands: - `xray` - Show what's in the container image and reverse engineer its Dockerfile - `lint` - Lint the target Dockerfile (or image, in the future) - `build` - Analyze the target container image along with its application and build an optimized image from it - `debug` - Debug the running target container. This command is useful for troubleshooting running containers created from minimal/minified or regular container images. - `registry` - Execute registry operations (`pull`, `push`, `copy`, `server`). - `profile` - Collect fat image information and generate a fat container report - `merge` - Merge two container images (optimized to merge minified images) - `images` - Get information about container images. - `vulnerability` - Execute vulnerability related tools and operations (`epss`). - `appbom` - Shows the application BOM (app composition/dependencies) - `version` - Show app and docker version information - `update` - Update the app - `help` - Show help info Global options: - `--report` - command report location (target location where to save the executed command results; `slim.report.json` by default; set it to `off` to disable) - `--check-version` - check if the current version is outdated - `--version` - print the version - `--debug` - enable debug logs - `--verbose` - enable info logs - `--log-level` - set the logging level ('debug', 'info', 'warn' (default), 'error', 'fatal', 'panic') - `--log-format` - set the format used by logs ('text' (default), or 'json') - `--crt-api-version` - Container runtime API version - `--quiet` - less verbose CLI execution mode - `--output-format` - set the output format to use ('text' (default), or 'json') - `--log` - log file to store logs - `--host` - Docker host address or socket (prefix with `tcp://` or `unix://`) - `--tls` - use TLS connecting to Docker - `--tls-verify` - do TLS verification - `--tls-cert-path` - path to TLS cert files - `--state-path value` - Slim state base path (must set it if the Slim binaries are not in a writable directory!) - `--archive-state` - Archives Slim state to the selected Docker volume (default volume - `slim-state`). By default, enabled when Slim is running in a container (disabled otherwise). Set it to `off` to disable explicitly. - `--in-container` - Set it to true to explicitly indicate that Slim is running in a container (if it's not set Slim will try to analyze the environment where it's running to determine if it's containerized) To disable the version checks set the global `--check-version` flag to `false` (e.g., `--check-version=false`) or you can use the `DSLIM_CHECK_VERSION` environment variable. ### `LINT` COMMAND OPTIONS - `--target` - target Dockerfile path (or Docker image, in the future; if you don't use this flag you must specify the target as the argument to the command) - `--target-type` - explicitly specify the command target type (values: dockerfile, image) - `--skip-build-context` - don't try to analyze build context - `--build-context-dir` - explicitly specify the build context directory - `--skip-dockerignore` - don't try to analyze .dockerignore - `--include-check-label` - include checks with the selected label key:value - `--exclude-check-label` - exclude checks with the selected label key:value - `--include-check-id` - check ID to include - `--include-check-id-file` - file with check IDs to include - `--exclude-check-id` - check ID to exclude - `--exclude-check-id-file` - file with check IDs to exclude - `--show-nohits` - show checks with no matches - `--show-snippet` - show check match snippet (default value: true) - `--list-checks` - list available checks (don't need to specify the target flag if you just want to list the available checks) ### `XRAY` COMMAND OPTIONS - `--target` - Target container image (name or ID) - `--pull` - Try pulling target if it's not available locally (default: false). - `--docker-config-path` - Set the docker config path used to fetch registry credentials (used with the `--pull` flag). - `--registry-account` - Account to be used when pulling images from private registries (used with the `--pull` flag). - `--registry-secret` - Account secret to be used when pulling images from private registries (used with the `--pull` and `--registry-account` flags). - `--show-plogs` - Show image pull logs (default: false). - `--changes value` - Show layer change details for the selected change type (values: none, all, delete, modify, add). - `--changes-output value` - Where to show the changes (values: all, report, console). - `--layer value` - Show details for the selected layer (using layer index or ID) - `--add-image-manifest` - Add raw image manifest to the command execution report file - `--add-image-config` - Add raw image config object to the command execution report file - `--layer-changes-max` - Maximum number of changes to show for each layer - `--all-changes-max` - Maximum number of changes to show for all layers - `--add-changes-max` - Maximum number of `add` changes to show for all layers - `--modify-changes-max` - Maximum number of `modify` changes to show for all layers - `--delete-changes-max` - Maximum number of `delete` changes to show for all layers - `--change-path value` - Include changes for the files that match the path pattern (Glob/Match in Go and **). Value formats: `` | `dump::` | `::` where `output type` is `console` or a directory name. If `value` starts with `dump:` the match will be 'dumped' to the selected `output type`. [can use this flag multiple times] - `--change-data value` - Include changes for the files that match the data pattern (regex). Value formats: `` | `dump:::` | `:::` | `:::` where `output type` is `console` or a directory name. If `value` starts with `dump:` the match will be 'dumped' to the selected `output type`. [can use this flag multiple times] - `--change-data-hash value` - Include changes for the files that match the provided data hashes (sha1). Value formats: `` | `dump::` | `::` where `output type` is `console` or a directory name. If `value` starts with `dump:` the match will be 'dumped' to the selected `output type`. [can use this flag multiple times] - `--reuse-saved-image` - Reuse saved container image (default: true). - `--top-changes-max` - Maximum number of top changes to track (default: 20). - `--hash-data` - Generate file data hashes (default: false). - `--detect-duplicates` - Detect duplicate files based on their hashes (default: true). - `--show-duplicates` - Show all discovered duplicate file paths (default: false). - `--show-special-perms` - Show files with special permissions (setuid,setgid,sticky) (default: true) - `--detect-utf8` - Detect utf8 files and optionally extract the discovered utf8 file content (possible values: "true" or "dump" or "dump:output_target.tgz" or "dump:output_target.tgz::max_size_bytes" or "dump:output_target.tgz:::max_size_bytes"). - `--detect-all-certs` - Detect all certificate files - `--detect-all-cert-pks` - Detect all certificate private key files - `--detect-identities` - Detect system identities (users, groups) and their properties (default: true) - `--change-match-layers-only` - Show only layers with change matches (default: false). - `--export-all-data-artifacts` - TAR archive file path to export all text data artifacts (if value is set to `.` then the archive file path defaults to `./data-artifacts.tar`) - `--remove-file-artifacts` - Remove file artifacts when command is done (note: you'll loose the reverse engineered Dockerfile) Change Types: - `none` - Don't show any file system change details in image layers (the top changes from the corresponding layer are still shown) - `all` - Show all file system change details in image layers - `delete` - Show only `delete` file system change details in image layers - `modify` - Show only `modify` file system change details in image layers - `add` - Show only 'add' file system change details in image layers In the interactive CLI prompt mode you must specify the target image using the `--target` flag while in the traditional CLI mode you can use the `--target` flag or you can specify the target image as the last value in the command. ### `BUILD` COMMAND OPTIONS - `--target` - Target container image (name or ID). It's an alternative way to provide the target information. The standard way to provide the target information is by putting as the last value in the `build` command CLI call. - `--pull` - Try pulling target if it's not available locally (default: false). - `--docker-config-path` - Set the docker config path used to fetch registry credentials (used with the `--pull` flag). - `--registry-account` - Account to be used when pulling images from private registries (used with the `--pull` flag). - `--registry-secret` - Account secret to be used when pulling images from private registries (used with the `--pull` and `--registry-account` flags). - `--show-plogs` - Show image pull logs (default: false). - `--compose-file` - Load container info from selected compose file - `--target-compose-svc` - Target service from compose file - `--target-compose-svc-image` - Override the container image name and/or tag when targeting a compose service using the target-compose-svc parameter (format: tag_name or image_name:tag_name) - `--target-compose-svc-no-ports` - Do not publish ports for target service from compose file - `--dep-exclude-compose-svc-all` - Do not start any compose services as target dependencies - `--dep-include-compose-svc` - Include specific compose service as a target dependency (only selected services will be started) - `--dep-exclude-compose-svc` - Exclude specific service from the compose services that will be started as target dependencies - `--dep-include-compose-svc-deps` - Include all dependencies for the selected compose service (excluding the service itself) as target dependencies - `--dep-include-target-compose-svc-deps` - Include all dependencies for the target compose service (excluding the service itself) as target dependencies. This is a shortcut flag to avoid repeating the service name (it's a pretty long flag name though :-)) - `--compose-svc-start-wait` - Number of seconds to wait before starting each compose service - `--compose-net` - Attach target to the selected compose network(s) otherwise all networks will be attached - `--compose-env-nohost` - Don't include the env vars from the host to compose - `--compose-env-file` - Load compose env vars from file (host env vars override the values loaded from this file) - `--compose-workdir` - Set custom work directory for compose - `--compose-project-name` - Use custom project name for compose - `--container-probe-compose-svc` - Container test/probe service from compose file - `--prestart-compose-svc` - placeholder for now - `--poststart-compose-svc` - placeholder for now - `--http-probe` - Enables/disables HTTP probing (ENABLED by default; you have to disable the probe if you don't need it by setting the flag to `false`: `--http-probe=false`) - `--http-probe-off` - Alternative way to disable HTTP probing - `--http-probe-cmd` - Additional HTTP probe command [can use this flag multiple times] - `--http-probe-cmd-file` - File with user defined HTTP probe commands - `--http-probe-start-wait` - Number of seconds to wait before starting HTTP probing - `--http-probe-retry-count` - Number of retries for each HTTP probe (default value: 5) - `--http-probe-retry-wait` - Number of seconds to wait before retrying HTTP probe (doubles when target is not ready; default value: 8) - `--http-probe-ports` - Explicit list of ports to probe (in the order you want them to be probed; excluded ports are not probed!) - `--http-probe-full` - Do full HTTP probe for all selected ports (if false, finish after first successful scan; default value: false) - `--http-probe-exit-on-failure` - Exit when all HTTP probe commands fail (default value: true) - `--http-probe-crawl` - Enable crawling for the default HTTP probe command (default value: true) - `--http-crawl-max-depth` - Max depth to use for the HTTP probe crawler (default value: 3) - `--http-crawl-max-page-count` - Max number of pages to visit for the HTTP probe crawler (default value: 1000) - `--http-crawl-concurrency` - Number of concurrent workers when crawling an HTTP target (default value: 10) - `--http-max-concurrent-crawlers` - Number of concurrent crawlers in the HTTP probe (default value: 1) - `--http-probe-apispec` - Run HTTP probes for API spec where the value represents the target path where the spec is available (supports Swagger 2.x and OpenAPI 3.x) [can use this flag multiple times] - `--http-probe-apispec-file` - Run HTTP probes for API spec from file (supports Swagger 2.x and OpenAPI 3.x) [can use this flag multiple times] - `--http-probe-exec` - App to execute when running HTTP probes. [can use this flag multiple times] - `--http-probe-exec-file` - Apps to execute when running HTTP probes loaded from file. - `--publish-port` - Map container port to host port analyzing image at runtime to make it easier to integrate external tests (format => port | hostPort:containerPort | hostIP:hostPort:containerPort | hostIP::containerPort )[can use this flag multiple times] - `--publish-exposed-ports` - Map all exposed ports to the same host ports analyzing image at runtime (default value: false) - `--show-clogs` - Show container logs (from the container used to perform dynamic inspection) - `--show-blogs` - Show build logs (when the minified container is built) - `--copy-meta-artifacts` - Copy meta artifacts to the provided location - `--remove-file-artifacts` - Remove file artifacts when command is done (note: you'll loose autogenerated Seccomp and Apparmor profiles unless you copy them with the `copy-meta-artifacts` flag or if you archive the state) - `--tag` - Use a custom tag for the generated image (instead of the default value: `.slim`) [can use this flag multiple times if you need to create additional tags for the optimized image] - `--entrypoint` - Override ENTRYPOINT analyzing image at runtime - `--cmd` - Override CMD analyzing image at runtime - `--mount` - Mount volume analyzing image (the mount parameter format is identical to the `-v` mount command in Docker) [can use this flag multiple times] - `--include-path` - Include directory (and what's in it) or file from image [can use this flag multiple times] (optionally overwriting the artifact's permissions, user and group information; full format: `targetPath:octalPermFlags#uid#gid`, minimal format: `targetPath` ; see the non-default USER FAQ section for more details) - `--include-path-file` - Load directory or file includes from a file (optionally overwriting the artifact's permissions, user and group information; full format: `targetPath:octalPermFlags#uid#gid`, minimal format: `targetPath` ; see the non-default USER FAQ section for more details) - `--include-paths-creport-file` - Keep files from the referenced creport - `--include-bin value` - Include binary from image (executable or shared object using its absolute path) - `--include-bin-file` - Load shared binary file includes from a file (similar to `--include-path-file`) - `--include-dir-bins value` - Include binaries in the target directory and include their dependencies, which could be in other locations (executables or shared objects using its absolute path) - `--include-exe value` - Include executable from image (by executable name) - `--include-exe-file` - Load executable file includes from a file (similar to `--include-path-file`) - `--include-shell` - Include basic shell functionality (default value: false) - `--include-workdir` - Keep files in working directory (default value: false) - `--include-cert-all` - Keep all discovered cert files (default: true) - `--include-cert-bundles-only` - Keep only cert bundles - `--include-cert-dirs` - Keep known cert directories and all files in them - `--include-cert-pk-all` - Keep all discovered cert private keys - `--include-cert-pk-dirs` - Keep known cert private key directories and all files in them - `--include-new` - Keep new files created by target during dynamic analysis (default value: true) - `--include-oslibs-net` - Keep the common networking OS libraries (default value: true) - `--include-ssh-client` - Keep the common SSH client components and configs - `--include-zoneinfo` - Keep the OS/libc zoneinfo data (default value: false) - `--include-app-nuxt-dir` - Keep the root Nuxt.js app directory (default value: false) - `--include-app-nuxt-build-dir` - Keep the build Nuxt.js app directory (default value: false) - `--include-app-nuxt-dist-dir` - Keep the dist Nuxt.js app directory (default value: false) - `--include-app-nuxt-static-dir` - Keep the static asset directory for Nuxt.js apps (default value: false) - `--include-app-nuxt-nodemodules-dir` - Keep the node modules directory for Nuxt.js apps (default value: false) - `--include-app-next-dir` - Keep the root Next.js app directory (default value: false) - `--include-app-next-build-dir` - Keep the build directory for Next.js app (default value: false) - `--include-app-next-dist-dir` - Keep the static SPA directory for Next.js apps (default value: false) - `--include-app-next-static-dir` - Keep the static public asset directory for Next.js apps (default value: false) - `--include-app-next-nodemodules-dir` - Keep the node modules directory for Next.js apps (default value: false) - `--include-node-package` - Keep node.js package by name [can use this flag multiple times] - `--preserve-path` - Keep path from original image in its initial state (changes to the selected container image files when it runs will be discarded). [can use this flag multiple times] - `--preserve-path-file` - File with paths to keep from original image in their original state (changes to the selected container image files when it runs will be discarded). - `--path-perms` - Set path permissions/user/group in optimized image (format: `target:octalPermFlags#uid#gid` ; see the non-default USER FAQ section for more details) - `--path-perms-file` - File with path permissions to set (format: `target:octalPermFlags#uid#gid` ; see the non-default USER FAQ section for more details) - `--exclude-pattern` - Exclude path pattern ([Glob/Match in Go](https://golang.org/pkg/path/filepath/#Match) and `**`) from image (useful when `--include-path` keeps a directory and you need to exclude / filter out some of the files in that directory) - `--exclude-varlock-files` - Exclude the files in the var and run lock directory (default value: true) - `--exclude-mounts` - Exclude mounted volumes from image (default value: true) - `--label` - Override or add LABEL analyzing image at runtime [can use this flag multiple times] - `--volume` - Add VOLUME analyzing image at runtime [can use this flag multiple times] - `--env` - Add ENV analyzing target image at runtime [can use this flag multiple times] - `--env-file` - Load multiple environment variables from a file when analyzing target image at runtime. - `--workdir` - Override WORKDIR analyzing image at runtime - `--network` - Override default container network settings analyzing image at runtime - `--expose` - Use additional EXPOSE instructions analyzing image at runtime [can use this flag multiple times] - `--link` - Add link to another container analyzing image at runtime [can use this flag multiple times] - `--hostname` - Override default container hostname analyzing image at runtime - `--etc-hosts-map` - Add a host to IP mapping to /etc/hosts analyzing image at runtime [can use this flag multiple times] - `--container-dns` - Add a dns server analyzing image at runtime [can use this flag multiple times] - `--container-dns-search` - Add a dns search domain for unqualified hostnames analyzing image at runtime [can use this flag multiple times] - `--image-overrides` - Save runtime overrides in generated image (values is `all` or a comma delimited list of override types: `entrypoint`, `cmd`, `workdir`, `env`, `expose`, `volume`, `label`). Use this flag if you need to set a runtime value and you want to persist it in the optimized image. If you only want to add, edit or delete an image value in the optimized image use one of the `--new-*` or `--remove-*` flags (define below). - `--continue-after` - Select continue mode: `enter` | `signal` | `probe` | `exec` | `timeout-number-in-seconds` | `container.probe` (default value if http probes are disabled: `enter`). You can also select `probe` and `exec` together: `'probe&exec'` (make sure to use quotes around the two modes or the `&` will break the shell command). - `--dockerfile` - The source Dockerfile name to build the fat image before it's optimized. - `--tag-fat` - Custom tag for the fat image built from Dockerfile. - `--cbo-add-host` - Add an extra host-to-IP mapping in /etc/hosts to use when building an image (Container Build Option). - `--cbo-build-arg` - Add a build-time variable (Container Build Option). - `--cbo-label` - Add a label when building from Dockerfiles (Container Build Option). - `--cbo-target` - Target stage to build for multi-stage Dockerfiles (Container Build Option). - `--cbo-network` - Networking mode to use for the RUN instructions at build-time (Container Build Option). - `--cbo-cache-from` - Add an image to the build cache (Container Build Option). - `--cro-runtime` - Runtime to use with the created containers (Container Runtime Option). - `--cro-host-config-file` - File to load the Docker host configuration data (JSON format) to use when running the container. See the [HostConfig](https://pkg.go.dev/github.com/fsouza/go-dockerclient#HostConfig) struct definition from the `go-dockerclient` package for configuration details. Note that Slim will automatically add `SYS_ADMIN` to the list of capabilities and run the container in privileged mode, which are required to generate the seccomp profiles. The host config parameters specified using their standalone build or profile command flags overwrite the values in the host config file (volume binds are merged). - `--cro-sysctl` - Set namespaced kernel parameters in the created container (Container Runtime Option). - `--cro-shm-size` - Shared memory size for /dev/shm in the created container (Container Runtime Option). - `--use-local-mounts` - Mount local paths for target container artifact input and output (off, by default) - `--use-sensor-volume` - Sensor volume name to use (set it to your Docker volume name if you manage your own Slim sensor volume). - `--keep-tmp-artifacts` - Keep temporary artifacts when command is done (off, by default). - `--keep-perms` - Keep artifact permissions as-is (default: true) - `--run-target-as-user` - Run target app (in the temporary container) as USER from Dockerfile (true, by default) - `--new-entrypoint` - New ENTRYPOINT instruction for the optimized image - `--new-cmd` - New CMD instruction for the optimized image - `--new-expose` - New EXPOSE instructions for the optimized image - `--new-workdir` - New WORKDIR instruction for the optimized image - `--new-env` - New ENV instructions for the optimized image - `--new-label` - New LABEL instructions for the optimized image - `--new-volume` - New VOLUME instructions for the optimized image - `--remove-volume` - Remove VOLUME instructions for the optimized image - `--remove-env` - Remove ENV instructions for the optimized image - `--remove-label` - Remove LABEL instructions for the optimized image - `--remove-expose` - Remove EXPOSE instructions for the optimized image - `--exec` - A shell script snippet to run via Docker exec - `--exec-file` - A shell script file to run via Docker exec - `--sensor-ipc-mode` - Select sensor IPC mode: proxy | direct (useful for containerized CI/CD environments) - `--sensor-ipc-endpoint` - Override sensor IPC endpoint - `--rta-onbuild-base-image` - Enable runtime analysis for onbuild base images (default: false) - `--rta-source-ptrace` - Enable PTRACE runtime analysis source (default: true) - `--image-build-engine` - Select image build engine: `internal` | `docker` | `none` (`internal` - build the output image without using Docker [default behavior], `docker` - build the output image with Docker, `none` - don't build the output image, allows you to do your own build with the tools you want to use, which you'll be able to do by pointing to the artifact directory where the `files.tar` and `Dockerfile` artifacts are located for the output image) - `--image-build-arch` - Select output image build architecture (use the standard container image names for the architectures without the OS part) - `--obfuscate-metadata` - Obfuscate the standard system and application metadata to make it more challenging to identify the image components (experimental flag, first version of obfuscation; inspired by the [`Malicious Compliance`](https://kccnceu2023.sched.com/event/1Hybu/malicious-compliance-reflections-on-trusting-container-scanners-ian-coldwater-independent-duffie-cooley-isovalent-brad-geesaman-ghost-security-rory-mccune-datadog) KubeCon EU 2023 talk) - `--enable-mondel` - Enable monitor data event log for sensor monitors to log/stream the events captured by those monitors (default: false) In the interactive CLI prompt mode you must specify the target image using the `--target` flag while in the traditional CLI mode you can use the `--target` flag or you can specify the target image as the last value in the command. The `--include-path` option is useful if you want to customize your minified image adding extra files and directories. The `--include-path-file` option allows you to load multiple includes from a newline delimited file. Use this option if you have a lot of includes. The includes from `--include-path` and `--include-path-file` are combined together. You can also use the `--exclude-pattern` flag to control what shouldn't be included. The `--continue-after` option is useful if you need to script the Slim app. If you pick the `probe` option then Slim will continue executing the build command after the HTTP probe is done executing. If you pick the `exec` options then Slim will continue executing the build command after the container exec shell commands (specified using the `--exec-file` or `--exec` flags) are done executing. If you pick the `timeout` option Slim will allow the target container to run for 60 seconds before it will attempt to collect the artifacts. You can specify a custom timeout value by passing a number of seconds you need instead of the `timeout` string. If you pick the `signal` option you'll need to send a `USR1` signal to the Slim app process. The `signal` option is useful when you want to run your own tests against the temporary container Slim creates. Your test automation / CI/CD pipeline will be able to notify the Slim app that it's done running its test by sending the `USR1` to it. You can also combine multiple `continue-after` modes. For now only combining `probe` and `exec` is supported (using either `probe&exec` or `exec&probe` as the `--continue-after` flag value). Other combinations may work too. Combining `probe` and `signal` is not supported. The `--include-shell` option provides a simple way to keep a basic shell in the minified container. Not all shell commands are included. To get additional shell commands or other command line utilities use the `--include-exe` and/or `--include-bin` options. Note that the extra apps and binaries might missed some of the non-binary dependencies (which don't get picked up during static analysis). For those additional dependencies use the `--include-path` and `--include-path-file` options. The `--dockerfile` option makes it possible to build a new minified image directly from source Dockerfile. Pass the Dockerfile name as the value for this flag and pass the build context directory or URL instead of the docker image name as the last parameter for the `build` command: `slim build --dockerfile Dockerfile --tag my/custom_minified_image_name .` If you want to see the console output from the build stages (when the fat and slim images are built) add the `--show-blogs` build flag. Note that the build console output is not interactive and it's printed only after the corresponding build step is done. The fat image created during the build process has the `.fat` suffix in its name. If you specify a custom image tag (with the `--tag` flag) the `.fat` suffix is added to the name part of the tag. If you don't provide a custom tag the generated fat image name will have the following format: `slim-tmp-fat-image..`. The minified image name will have the `.slim` suffix added to that auto-generated container image name (`slim-tmp-fat-image...slim`). Take a look at this [python examples](https://github.com/slimtoolkit/examples/tree/master/python_ubuntu_18_py27_from_dockerfile) to see how it's using the `--dockerfile` flag. The `--use-local-mounts` option is used to choose how the Slim sensor is added to the target container and how the sensor artifacts are delivered back to the master. If you enable this option you'll get the original Slim app behavior where it uses local file system volume mounts to add the sensor executable and to extract the artifacts from the target container. This option doesn't always work as expected in the dockerized environment where Slim itself is running in a Docker container. When this option is disabled (default behavior) then a separate Docker volume is used to mount the sensor and the sensor artifacts are explicitly copied from the target container. ### `DEBUG` COMMAND OPTIONS - `--runtime` - Runtime environment type (values: `docker`, `k8s`; defaults to `docker`) - `--debug-image` - Debug image to use for the debug side-car container (default value for this flag is `busybox`). - `--list-debug-images` - List possible debug images to use for the debug side-car container (for the `--debug-image` flag). This list is a ready to use set of debug images. You can use other images too. - `--target` - Target container name or ID (this can also be provided as the last param in the command line invocation of the `debug` command). Note that the target container must be running. You can use the `docker run` command to start the target container (or the kubernetes equivalent). - `--namespace` - Namespace to target [k8s runtime] (defaults to `default`) - `--pod` - Pod to target [k8s runtime] - `--cmd` - (Optional) custom CMD to use for the debug side-car container (alternatively pass custom CMD params after '--'). - `--entrypoint` - (Optional) custom ENTRYPOINT to use for the debug side-car container. - `--terminal` - Attach interactive terminal to the debug container (default: true). When the interactive terminal is not enabled the debug container output will be printed out to the screen when the `debug` command exits. - `--kubeconfig` - Kubeconfig file location [k8s runtime] - `--workdir` - Custom WORKDIR to use for the debug side-car container. - `--env` - Environment variable to add to the debug side-car container. - `--run-as-target-shell` - Attach interactive terminal to the debug container and run shell as if it's running in the target container environment. - `--list-sessions` - List all debug sessions for the selected target (pod and optionally selected container for k8s or container for other runtimes). - `--show-session-logs` - Show logs for the selected debug session (using namespace, pod, target container or debug session container name for k8s or debug session container name for other runtimes). - `--session` - Debug session container name (used for debug sessoin actions). - `--connect-session` - Connect to existing debug session. - `--list-namespaces` - List names for available namespaces (use this flag by itself) [k8s runtime]. - `--list-pods` - List names for running pods in the selected namespace (use this flag by itself) [k8s runtime]. - `--list-debuggable-containers` - List container names for active containers that can be debugged (use this flag by itself). - `--list-debug-images` - List possible debug images to use for the debug side-car container (use this flag by itself). - `--help` show help (default: false) See the "Debugging Using the `debug` Command" section for more information about this command. ### `RUN` COMMAND OPTIONS Run one or more containers USAGE: `slim [GLOBAL FLAGS] run [FLAGS] [IMAGE]` Flags: - `--target` - Target container image to run. Same as specifying the target container image as the last value for the command. Used mostly for the interactive prompt mode where you need to select flag names. - `--pull` - Pull the target image before trying to run it. - `--docker-config-path` - Docker config path (used to fetch registry credentials). - `--registry-account` - Target registry account used when pulling images from private registries. - `--registry-secret` - Target registry secret used when pulling images from private registries. - `--show-plogs` - Show image pull logs. - `--entrypoint` - Override ENTRYPOINT running the target image. - `--cmd` - Override CMD running the target image. - `--live-logs` - Show live logs for the container (can't use with --terminal). - `--terminal` - Attach interactive terminal to the container. - `--publish` - Map container port to host port (format => port | hostPort:containerPort | hostIP:hostPort:containerPort | hostIP::containerPort ). - `--rm` - Remove the container when it exits. - `--detach` - Start the container and do not wait for it to exit. ### `MERGE` COMMAND OPTIONS Merge two container images. Optimized to merge minified images. Flags: - `--image` - Image to merge. Flag instance position determines the merge order. The command supports two instances of this flag. - `--use-last-image-metadata` - Use only the last image metadata for the merged image. - `--tag` - Custom tags for the output image (multiple instances). ### `REGISTRY` COMMAND OPTIONS For the operations that require authentication you can reuse the registry credentials from Docker (do `docker login` first and then use the `--use-docker-credentials` flag with the `registry` command) or you can specify the auth info using the `--account` and `--secret` flags). Current sub-commands: `pull`, `push`, `image-index-create`, `server`. There's also a placeholder for `copy`, but it doesn't do anything yet. Great opportunity to contribute ;-) Shared Command Level Flags: - `--use-docker-credentials` - Use the registry credentials from the default Docker config file. - `--account` - Registry credentials account. - `--secret` - Registry credentials secret. #### `PULL` SUBCOMMAND OPTIONS USAGE: `slim [GLOBAL FLAGS] registry [SHARED FLAGS] pull [FLAGS] [IMAGE]` Flags: - `--target value` - Target container image (name or ID) [$DSLIM_TARGET] - `--save-to-docker`- Save pulled image to docker (default: true) [$DSLIM_REG_PULL_SAVE_TO_DOCKER] #### `PUSH` SUBCOMMAND OPTIONS USAGE: `slim [GLOBAL FLAGS] registry [SHARED FLAGS] push [FLAGS] [IMAGE]` Flags: - `--docker` -- Push local docker image. - `--tar` -- Push image from a local tar file. - `--as` -- Tag the selected image with the specified name before pushing. Note that `slim registry push LOCAL_DOCKER_IMAGE_NAME` is a shortcut for `slim registry push --docker LOCAL_DOCKER_IMAGE_NAME`. Normally you have to explicitly tag the target image to have a name that's appropriate for the destination registry. The `--as` flag is a convenient way to tag the image while you are pushing it. Here's an example pushing a local Docker `nginx` image to a local registry: `slim registry push --docker nginx --as localhost:5000/nginx` You can create a local registry using the `server` subcommand. See the `server` sub-command section below for more details. #### `COPY` SUBCOMMAND OPTIONS USAGE: `slim registry copy [SRC_IMAGE] [DST_IMAGE]` NOTE: Just a placeholder for now (TBD) #### `IMAGE-INDEX-CREATE` SUBCOMMAND OPTIONS USAGE: `slim registry image-index-create --image-index-name [MULTI-ARCH_IMAGE_TAG] --image-name [IMAGE_ONE] --image-name [IMAGE_TWO]` Flags: - `--image-index-name` - Image index name to use. - `--image-name` - Target image name to include in image index. - `--as-manifest-list` - Create image index with the manifest list media type instead of the default OCI image index type. - `--dump-raw-manifest` - Dump raw manifest for the created image index. - `--insecure-refs` - Allow the referenced images from insecure registry connections. #### `SERVER` SUBCOMMAND OPTIONS Starts a server which implements the [OCI API spec](https://github.com/opencontainers/distribution-spec/blob/v1.0.1/spec.md) on port 5000 by default. USAGE: `slim [GLOBAL FLAGS] registry server [FLAGS]` Flags: - `--address` - Registry server address to listen on (default: `0.0.0.0`) - `--port` - Registry server port (default: 5000) - `--https` - Use HTTPS. - `--cert-path` - Cert path for use with HTTPS (for use when not using autocert). - `--key-path` - Key path for use with HTTPS (for use when not using autocert). - `--domain` - Domain to use for registry server (to get certs). Only works if the registry is internet accessible (see `autocert` Go docs for more details). - `--referrers-api` - Enables the [referrers API endpoint](https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc3/spec.md#enabling-the-referrers-api) (OCI 1.1+). Enabled by default (set to `false` to disable). ### `VULNERABILITY` COMMAND OPTIONS USAGE: `slim [GLOBAL FLAGS] vulnerability [SHARED FLAGS] [SUBCOMMAND] [FLAGS]` Current sub-commands: * `epss` - Gets EPPS information for the target vulnerabilities or based on the selected vulnerability filters. Shared Command Level Flags: - `--cve` - Target vulnerability CVE ID (can specify multiple times to target multiple vulnerabilities). #### `EPSS` SUBCOMMAND OPTIONS USAGE: `slim [GLOBAL FLAGS] vulnerability [SHARED FLAGS] epss [FLAGS]` Flags: - `--op` - EPSS operation (`lookup` | `list`). - `--date` - Date for the EPSS information (YYYY-MM-DD format). Works with the `lookup` and `list` operations. - `--with-history` - Return EPSS results with historical data. Works with the `lookup` and `list` operations. - `--limit` - Limit the number of returned records. - `offset` - Offset where to start returning records. - `filter-cve-id-pattern` - 'CVE ID pattern' ESPP list operation filter. - `filter-days-since-added` - 'days since added' ESPP list operation filter. - `filter-score-gt` - 'score is greater than' ESPP list operation filter. - `filter-score-lt` - 'score is less than' ESPP list operation filter. - `filter-percentile-gt` - 'percentile is greater than' ESPP list operation filter. - `filter-percentile-lt` - 'percentile is less than' ESPP list operation filter. - `filter-order-records` - 'order returned records' ESPP list operation filter ('score-desc' | 'score-asc' | 'percentile-desc' | 'percentile-asc'). Examples: * `slim --quiet vulnerability --cve CVE-2021-21315 epss` * `slim --output-format=json vulnerability --cve CVE-2021-21315 epss` * `slim --quiet --output-format=json vulnerability --cve CVE-2021-21315 --cve CVE-2023-49070 epss` * `slim --quiet vulnerability --cve CVE-2021-21315 epss --with-history --date 2022-12-13` * `slim --quiet vulnerability epss --op list --date 2024-01-05` * `slim --quiet vulnerability epss --op list --filter-cve-id-pattern 2023 --filter-score-gt 0.92 --limit 2 --offset 3` ## RUNNING CONTAINERIZED The current version of Slim is able to run in containers. It will try to detect if it's running in a containerized environment, but you can also tell Slim explicitly using the `--in-container` global flag. You can run Slim in your container directly or you can use the Slim container image in your containerized environment. If you are using the Slim container image make sure you run it configured with the Docker IPC information, so it can communicate with the Docker daemon. The most common way to do it is by mounting the Docker unix socket to the Slim app container. Some containerized environments (like Gitlab and their `dind` service) might not expose the Docker unix socket to you, so you'll need to make sure the environment variables used to communicate with Docker (e.g., `DOCKER_HOST`) are passed to the Slim app container. Note that if those environment variables reference any kind of local host names those names need to be replaced or you need to tell the Slim app about them using the `--etc-hosts-map` flag. If those environment variables reference local files those local files (e.g., files for TLS cert validation) will need to be copied to a temporary container, so that temporary container can be used as a data container to make those files accessible by the Slim app container. When Slim app runs in a container it will attempt to save its execution state in a separate Docker volume. If the volume doesn't exist it will try to create it (`slim-state`, by default). You can pick a different state volume or disable this behavior completely by using the global `--archive-state` flag. If you do want to persist the Slim app execution state (which includes the `seccomp` and `AppArmor` profiles) without using the state archiving feature you can mount your own volume that maps to the `/bin/.slim-state` directory in the Slim app container. By default, the Slim app will try to create a Docker volume for its sensor unless one already exists. If this behavior is not supported by your containerized environment you can create a volume separately and pass its name to the Slim app using the `--use-sensor-volume` flag. Here's a basic example of how to use the containerized version of the Slim app: `docker run -it --rm -v /var/run/docker.sock:/var/run/docker.sock dslim/slim build your-docker-image-name` Here's a GitLab example for their `dind` `.gitlab-ci.yml` config file: `docker run -e DOCKER_HOST=tcp://$(grep docker /etc/hosts | cut -f1):2375 dslim/slim build your-docker-image-name` Here's a CircleCI example for their `remote docker` `.circleci/config.yml` config file (used after the `setup_remote_docker` step): ```bash docker create -v /dcert_path --name dcert alpine:latest /bin/true docker cp $DOCKER_CERT_PATH/. dcert:/dcert_path docker run --volumes-from dcert -e DOCKER_HOST=$DOCKER_HOST -e DOCKER_TLS_VERIFY=$DOCKER_TLS_VERIFY -e DOCKER_CERT_PATH=/dcert_path dslim/slim build your-docker-image-name ``` Different CI/CD services have different containerized environment designs that impose various restrictions that may impact the ability of the main app to communicate with the sensor app embedded in the temporary container Slim creates. Try adjusting the values for the `--sensor-ipc-mode` and `--sensor-ipc-endpoint` flags. This [`Google Cloud Build`](https://medium.com/google-cloud/integrating-dockerslim-container-minify-step-on-cloud-build-64da29fd58d1) blog post by Márton Kodok is a good reference for both of those flags. ### Using `*-file` Flags - There are several flags that accept file paths (`--include-path-file`, `--compose-file`, `--http-probe-cmd-file`, etc). You need volume mount the location of the referenced paths or the file paths themselves when you use the containerized version of Slim because the Slim app container won't have accept to the referenced files otherwise. ## CI/CD INTEGRATIONS ### Integrating Slim in Jenkins #### Prerequisites: - Spin up a virtual machine(e.g.EC2 Instance, Azure VM, GCE) which has an Ubuntu OS via your desired cloud platform(AWS, Azure, GCP), SSH into the machine, update the machine packages and install docker. An example of this step is highlighted below given you are running an AWS EC2 Instance. ``` sudo apt update -y ``` ``` sudo apt install docker -y ``` ``` sudo systemctl start docker ``` ``` sudo usermod -aG docker ec2-user ``` - Install Jenkins on the virtual machine using docker as stipulated by the [Jenkins Documentation](https://github.com/jenkinsci/docker/blob/master/README.md), this step pulls [Jenkins Image from DockerHub](https://hub.docker.com/r/jenkins/jenkins), runs Jenkins as a container via port 8080 and creates an explicit docker volume on the host machine to retain Jenkins data. Given you are running an AWS EC2 Instance, create a TCP rule with port 8080 in the Instance security group rules which allows only your Internet Protocol(IP) address to access the Jenkins server. ``` docker run -p 8080:8080 -p 50000:50000 -d -v jenkins_home:/var/jenkins_home jenkins/jenkins:lts ``` - Given Jenkins is now running as a containerized environment in the virtual machine, you need to make docker available in the Jenkins container, you can do this by bind mounting the virtual machine docker unix socket onto the jenkins container, note that to carry out this step you need to stop the running jenkins container, you can find the jenkins container ID by using the docker ps command, the commands to execute are highlighted below. This step is essential as it makes docker available in the Jenkins container, and with docker you can pull Slim Image which is to be used in further steps. ``` docker ps ``` ``` docker stop [jenkins_container_id] ``` ``` docker run -p 8080:8080 -p 50000:50000 -d \ -v jenkins_home:/var/jenkins_home \ -v /var/run/docker.sock:/var/run/docker.sock \ -v $(which docker):/usr/bin/docker jenkins/jenkins:lts ``` - Enable Docker permissions in the new jenkins container, such that Jenkins can perform docker commands and pull the [Slim Official Image](https://hub.docker.com/r/dslim/docker-slim) in the container. To do this, you need to get into the Jenkins container as a root user, you can find the jenkins container ID by using the docker ps command, the commands to execute are highlighted below: ``` docker exec -u 0 -it [jenkins_container_id] bash ``` ``` chmod 666 /var/run/docker.sock ``` ``` docker pull dslim/slim ``` #### Jenkinsfile Slim Stage Given you have completed the prerequisite steps above, you can build a docker image and minify the image size using Slim via the snippet stage below which should be highlighted in your Jenkinsfile stages. ``` stage("Build and Slim Docker Image") { steps { script { echo "building and slimming docker image..." sh 'docker build -t IMAGE_NAME:$BUILD_NUMBER .' sh 'docker run --rm -v /var/run/docker.sock:/var/run/docker.sock dslim/slim \ build --target IMAGE_NAME:$BUILD_NUMBER --tag IMAGE_NAME:slim-$BUILD_NUMBER \ exit' } } } ``` - The snippet stage above allows for customization, you should replace the image name--IMAGE_NAME with your desired image name, the environment variable tag--$BUILD_NUMBER represents a unique incremental number allocated by Jenkins each time your jenkins pipeline runs. - The docker build command builds a Docker Image of your application from a Dockerfile. - The docker run command runs Slim in a non-interactive mode via the docker unix socket, minifies the built(target) image--IMAGE_NAME:$BUILD_NUMBER, and adjusting it to a new slimmed image with the image/tag--IMAGE_NAME:slim-$BUILD_NUMBER. - You should put the Slim stage before a docker tag/push stage and after a build/test artifact in your Jenkinsfile, an example pipeline is highlighted below for a sample nodejs application; The first stage test and builds an artifact of the application; The second stage builds a docker image and a slimmed version of the docker image; The third stage tags the slimmed docker image with a DockerHub account remote repository and pushes the image to the remote repository. ``` pipeline { agent any stages { stage("building nodejs app") { steps{ script { echo "building nodejs app..." sh 'npm run test' sh 'npm pack' } } } stage("Build and Slim Docker Image") { steps { script { echo "building and slimming docker image..." sh 'docker build -t node_alpine:$BUILD_NUMBER .' sh 'docker run --rm -v /var/run/docker.sock:/var/run/docker.sock dslim/slim \ build --target node_alpine:$BUILD_NUMBER --tag node_alpine:slim-$BUILD_NUMBER \ exit' } } } stage("Push Slim Image to Regristy") { steps { script { echo 'pushing image to docker regristry...' withCredentials([usernamePassword(credentialsId: 'docker-hub-credentials', passwordVariable: 'PASS', usernameVariable: 'USER')]) { sh 'docker tag node_alpine:slim-$BUILD_NUMBER $USER/node_alpine:slim-$BUILD_NUMBER' sh 'echo $PASS | docker login -u $USER --password-stdin' sh 'docker push $USER/node_alpine:slim-$BUILD_NUMBER' } } } } } } ``` ### Integrating Slimtoolkit in Github Actions #### Github Action Integrating SlimToolkit in Github Actions in your CI/CD workflow involves using the [Docker-Slim Github Action](https://github.com/marketplace/actions/docker-slim-github-action), this Action(snippet below) minifies a target docker image--IMAGE_NAME:latest in your workflow, making it smaller and adjusting the new slimmed image as IMAGE_NAME:slim. ``` # Slim it! - uses: kitabisa/docker-slim-action@v1 env: DSLIM_HTTP_PROBE: false with: target: IMAGE_NAME:latest tag: "slim" ``` #### Github Actions Slim Workflow You can integrate the Docker-Slim Github Action in your workflow by inserting the Action after a [Docker Build/Push Github Action](https://github.com/docker/build-push-action), before [Docker Login Github Action](https://github.com/docker/login-action) and docker tag/push commands, a customized example workflow is highlighted below. Note that the environment variable tag--{{github.run_number}} in the workflow represents a unique incremental number allocated by Github Actions each time your workflow runs. ``` # Build the Docker image first - uses: docker/build-push-action@v4 with: push: false tags: IMAGE_NAME:{{github.run_number}} # Slim the Image - uses: kitabisa/docker-slim-action@v1 env: DSLIM_HTTP_PROBE: false with: target: IMAGE_NAME:{{github.run_number}} tag: "slim-{{github.run_number}}" # Docker Hub Login uses: docker/login-action@v2 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} # Push to the registry - run: | docker tag IMAGE_NAME:slim-{{github.run_number}} ${{ secrets.DOCKERHUB_USERNAME }}/IMAGE_NAME:slim-{{github.run_number}} docker push ${{ secrets.DOCKERHUB_USERNAME }}/IMAGE_NAME:slim-{{github.run_number}} ``` The workflow above indicates four steps: - A [Docker Build/Push Github Action](https://github.com/docker/build-push-action) for building a docker image with the image name/tag--IMAGE_NAME:{{github.run_number}}, you should give replace IMAGE_NAME with your desired image name. Note that this Action must have a false option to push the built image--given that you need the image slimmed/minified before pushing it to a container registry. - A Docker-Slim Github Action which minifies the target image--IMAGE_NAME:{{github.run_number}}, this Action has the "slim-{{github.run_number}}" tag and adds this tag to the slimmed/minified docker image such that the image name/tag becomes IMAGE_NAME:slim-{{github.run_number}}. - A Docker Login Github Action which logs into your DockerHub container regristry account, you should store your DockerHub username and personal access token as secrets in the github repository meant for the workflow. Suppose your container registry is not DockerHub, you can check the [Docker Login Github Action documentation](https://github.com/docker/login-action) for the use case of logging into your desired container registry. - A docker tag command for naming/tagging the slimmed image with your DockerHub account remote repository name which could be the same name(IMAGE_NAME) as the slimmed image; A docker push command to push the slimmed image to your Dockerhub account remote repository. ## DOCKER CONNECT OPTIONS If you don't specify any Docker connect options the Slim app expects to find the Docker Unix socket (`/var/run/docker.sock`) or the following environment variables: `DOCKER_HOST`, `DOCKER_TLS_VERIFY` (optional), `DOCKER_CERT_PATH` (required if `DOCKER_TLS_VERIFY` is set to `"1"`). Note that the `DOCKER_HOST` environment variable can be used to point to a Unix socket address (in case the default Unix socket isn't there). This is useful when you use Docker Desktop and you haven't configured Docker Desktop to create the default Unix socket. If the Docker environment variables are configured to use TLS and to verify the Docker cert (default behavior), but you want to disable the TLS verification you can override the TLS verification behavior by setting the `--tls-verify` to false: `slim --tls-verify=false build my/sample-node-app-multi` You can override all Docker connection options using these flags: `--host`, `--tls`, `--tls-verify`, `--tls-cert-path`. These flags correspond to the standard Docker options (and the environment variables). Note that you can also use the `--host` flag (similar to `DOCKER_HOST`) to point to a Unix socket (e.g., `--host=unix:///var/run/docker.sock`). If you want to use TLS with verification: `slim --host=tcp://192.168.99.100:2376 --tls-cert-path=/Users/youruser/.docker/machine/machines/default --tls=true --tls-verify=true build my/sample-node-app-multi` If you want to use TLS without verification: `slim --host=tcp://192.168.99.100:2376 --tls-cert-path=/Users/youruser/.docker/machine/machines/default --tls=true --tls-verify=false build my/sample-node-app-multi` If the Docker environment variables are not set and if you don't specify any Docker connect options Slim will try to use the default unix socket. ### DOCKER DESKTOP You may not have the default Unix socket (`/var/run/docker.sock`) configured if you use Docker Desktop. By default, Docker Desktop uses `~/.docker/run/docker.sock` as the Unix socket. You can either use `--host` or `DOCKER_HOST` to point to the Docker Desktop's Unix socket or you can configure Docker Desktop to create the default/traditional Unix socket (creating the `/var/run/docker.sock` symlink manually is an option too). To configure Docker Desktop to create the default Unix socket open its UI and go to `Settings -> Advanced` where you need to check the `Enable default Docker socket (Requires password)` option. ### COLIMA Similar to with Docker Desktop, but the socked will need to be configured to use `unix://${HOME}/.colima//docker.sock`. ## HTTP PROBE COMMANDS If the HTTP probe is enabled (note: it is enabled by default) it will default to running `GET /` with HTTP and then HTTPS on every exposed port. You can add additional commands using the `--http-probe-cmd` and `--http-probe-cmd-file` options. If you want to disable HTTP probing set the `--http-probe` flag to false (e.g., `--http-probe=false`). You can also use the `--http-probe-off` flag to do the same (simply use the flag without any parameters). The `--http-probe-cmd` option is good when you want to specify a small number of simple commands where you select some or all of these HTTP command options: crawling (defaults to false), protocol, method (defaults to GET), resource (path and query string). If you only want to use custom HTTP probe command and you don't want the default `GET /` command added to the command list you explicitly provided you'll need to set `--http-probe` to false when you specify your custom HTTP probe command. Note that this inconsistency will be addressed in the future releases to make it less confusing. Possible field combinations: * `/path` - runs `GET /path` * `crawl:/path` - runs `GET /path` and then crawls the pages referenced by the target page * `post:/path` - runs `POST /path` * `crawl:get:/path` - runs `GET /path` and then crawls the pages referenced by the target page * `https:get:/path` runs `GET /path` only on https * `crawl:http:get:/path` - runs `GET /path` and then crawls the pages referenced by the target page Here are a couple of examples: Adds two extra probe commands: `GET /api/info` and `POST /submit` (tries http first, then tries https): `slim build --show-clogs --http-probe-cmd /api/info --http-probe-cmd POST:/submit my/sample-node-app-multi` Adds one extra probe command: `POST /submit` (using only http): `slim build --show-clogs --http-probe-cmd http:POST:/submit my/sample-node-app-multi` The `--http-probe-cmd-file` option is good when you have a lot of commands and/or you want to select additional HTTP command options. Available HTTP command options: * `method` - HTTP method to use * `resource` - target resource URL * `port` - port number * `protocol` - `http`, `https`, `http2`, `http2c` (cleartext version of http2), `ws`, `wss` (secure websocket) * `headers` - array of strings with column delimited key/value pairs (e.g., "Content-Type: application/json") * `body` - request body as a string * `body_file` - request body loaded from the provided file * `username` - username to use for basic auth * `password` - password to use for basic auth * `crawl` - boolean to indicate if you want to crawl the target (to visit all referenced resources) Here's a probe command file example: `slim build --show-clogs --http-probe-cmd-file probeCmds.json my/sample-node-app-multi` Commands in `probeCmds.json`: ``` { "commands": [ { "resource": "/api/info" }, { "method": "POST", "resource": "/submit" }, { "procotol": "http", "resource": "/api/call?arg=one" }, { "protocol": "http", "method": "POST", "resource": "/submit2", "body": "key=value" }, { "protocol": "http", "method": "POST", "resource": "/submit3", "body_file": "mydata.json", "headers": ["Content-Type: application/json"] } ] } ``` The HTTP probe command file path can be a relative path (relative to the current working directory) or it can be an absolute path. For each HTTP probe call Slim will print the call status. Example: `info=http.probe.call status=200 method=GET target=http://127.0.0.1:32899/ attempt=1 error=none`. You can execute your own external HTTP requests using the `target.port.list` field in the container info message Slim prints when it starts its test container: `slim[build]: info=container name= id= target.port.list=[] target.port.info=[]`. Example: `slim[build]: info=container name=slimk_42861_20190203084955 id=aa44c43bcf4dd0dae78e2a8b3ac011e7beb6f098a65b09c8bce4a91dc2ff8427 target.port.list=[32899] target.port.info=[9000/tcp => 0.0.0.0:32899]`. With this information you can run `curl` or other HTTP request generating tools: `curl http://localhost:32899`. The current version also includes an experimental `crawling` capability. To enable it for the default HTTP probe use the `--http-probe-crawl` flag. You can also enable it for the HTTP probe commands in your command file using the `crawl` boolean field. When `crawling` is enabled the HTTP probe will act like a web crawler following the links it finds in the target endpoint. Probing based on the Swagger/OpenAPI spec is another experimental capability. This feature introduces two new flags: * `http-probe-apispec` - value: `:` * `http-probe-apispec-file` - value: `` You can use the `--http-probe-exec` and `--http-probe-exec-file` options to run the user provided commands when the http probes are executed. This example shows how you can run `curl` against the temporary container created by Slim when the http probes are executed. `slim build --http-probe-exec 'curl http://localhost:YOUR_CONTAINER_PORT_NUM/some/path' --publish-port YOUR_CONTAINER_PORT_NUM your-container-image-name` ## DEBUGGING MINIFIED CONTAINERS ### Debugging Using the `debug` Command The current version of the `debug` command is pretty basic and it lacks a number of useful capabilities. It will help you debug containers running in Docker or Kubernetes (use the `--runtime` flag and set it to `k8s` if you need to debug a container in Kubernetes). By default the `debug` command will provide you with an interactive terminal when it attaches the debugger side-car image to the debugged target container. Future versions will allow you to have different interaction modes with the target. #### The Debug Images You can use any container image as a debug image, but there's a list of pre-selected debug images you can choose. You can list all pre-selected debug images with the `--list-debug-images` and if you are using the interactive prompt mode there'll be an auto-complete dropdown menu for the `--debug-image` flag. Here's the current list of debug images: * `cgr.dev/chainguard/slim-toolkit-debug:latest` - a general purpose SlimToolkit debug image created by Chainguard * `cgr.dev/chainguard/wolfi-base:latest` - a basic lightweight Wolfi image * `busybox:latest` - a lightweight image with common unix utilities * `nicolaka/netshoot` - a network trouble-shooting swiss-army container * `lightruncom/koolkits:node` - a debug image for Node.js applications * `lightruncom/koolkits:python` - a debug image for Python applications * `lightruncom/koolkits:golang` - a debug image for Go applications * `lightruncom/koolkits:jvm` - a debug image for Java applications * `digitalocean/doks-debug:latest` - a kubernetes troubleshooting debug image * `public.ecr.aws/zinclabs/debug-ubuntu-base:latest` - an image with common debugging utilities #### Steps to Debug Your Container (Kubernetes Runtime) 1. Make sure the target environment you want to debug is up (the example k8s manifest creates a pod with the minimal nginx image from Chainguard and it has no shell): ```bash >> kubectl apply -f examples/k8s_nginx_cgr/manifest.yaml ``` 2. Run the debug command: ```bash >> slim debug --runtime=k8s --pod=example-pod example-container ``` or ```bash >> slim debug --runtime=k8s --pod=example-pod --target=example-container ``` Now you should have an interactive shell into the debug container started by `slim` and you can type your regular shell commands. By default the `debug` command will connect the interactive terminal to the debugged container and it will run a shell as if it's running in the target container environment, so you will see the file system of the target container as if you are directly connected (you won't have to go through the `proc` file system). You can change this behavior by using the `--run-as-target-shell` (which is true by default). For example, this call will connect you to the debug container in a more traditional way: `slim debug --runtime=k8s --run-as-target-shell=false example-container` Also note that if you use the interactive `prompt` mode (when you run `slim` with no command line parameters) you will get auto-complete behavior for a number of flags: `--target`, `--namespace`, `--pod`, `--session`. Each time you try to debug an image `slim` will have a session that represents it. You'll be able to reconnect to the existing active debug sessions and you'll be able to get logs from all available sessions. #### Steps to Debug Your Container (Docker Runtime) 1. Start the target container you want to debug: ```bash >> docker run -it --rm -p 80:80 --name mycontainer nginx ``` 2. Run the debug command: ```bash >> slim debug mycontainer ``` or ```bash >> slim debug --target=mycontainer ``` Now you should have an interactive shell into the debug container started by `slim` and you can type your regular shell commands. By default the `debug` command will connect the interactive terminal to the debugged container and it will run a shell as if it's running in the target container environment, so you will see the file system of the target container as if you are directly connected (you won't have to go through the `proc` file system). You can change this behavior by using the `--run-as-target-shell` (which is true by default). For example, this call will connect you to the debug container in a more traditional way: `slim debug --run-as-target-shell=false mycontainer` Also note that if you use the interactive `prompt` mode (when you run `slim` with no command line parameters) you will get auto-complete behavior for a number of flags: `--target`, `--session`. Each time you try to debug an image `slim` will have a session that represents it. You'll be able to reconnect to the existing active debug sessions and you'll be able to get logs from all available sessions. ### Debugging the "Hard Way" (Docker Runtime) You can create dedicated debugging side-car container images loaded with the tools you need for debugging target containers. This allows you to keep your production container images small. The debugging side-car containers attach to the running target containers. Assuming you have a running container named `node_app_alpine` you can attach your debugging side-car with a command like this: `docker run --rm -it --pid=container:node_app_alpine --net=container:node_app_alpine --cap-add sys_admin alpine sh`. In this example, the debugging side-car is a regular alpine image. This is exactly what happens with the `node_alpine` app sample (located in the `node_alpine` directory of the `examples` repo) and the `run_debug_sidecar.command` helper script. If you run the `ps` command in the side-car you'll see the application from the target container: ``` # ps PID USER TIME COMMAND 1 root 0:00 node /opt/my/service/server.js 13 root 0:00 sh 38 root 0:00 ps ``` You can access the target container file system through `/proc//root`: ``` # ls -lh /proc/1/root/opt/my/service total 8 drwxr-xr-x 3 root root 4.0K Sep 2 15:51 node_modules -rwxr-xr-x 1 root root 415 Sep 8 00:52 server.js ``` Some of the useful debugging commands include `cat /proc//cmdline`, `ls -l /proc//cwd`, `cat /proc/1/environ`, `cat /proc//limits`, `cat /proc//status` and `ls -l /proc//fd`. ## MINIFYING COMMAND LINE TOOLS Unless the default CMD instruction in your Dockerfile is sufficient you'll have to specify command line parameters when you execute the `build` command in Slim. This can be done with the `--cmd` option. Other useful command line parameters: - `--show-clogs` - use it if you want to see the output of your container. - `--mount` - use it to mount a volume when Slim inspects your image. - `--entrypoint` - use it if you want to override the ENTRYPOINT instruction when Slim inspects your image. Note that the `--entrypoint` and `--cmd` options don't override the `ENTRYPOINT` and `CMD` instructions in the final minified image. Here's a sample `build` command: `slim build --show-clogs=true --cmd docker-compose.yml --mount $(pwd)/data/:/data/ dslim/container-transform` It's used to minify the `container-transform` tool. You can get the minified image from [`Docker Hub`](https://hub.docker.com/r/dslim/container-transform.slim/). ## QUICK SECCOMP EXAMPLE If you want to auto-generate a Seccomp profile AND minify your image use the `build` command. If you only want to auto-generate a Seccomp profile (along with other interesting image metadata) use the `profile` command. Step one: run Slim `slim build your-name/your-app` Step two: use the generated Seccomp profile `docker run --security-opt seccomp:/.images//artifacts/your-name-your-app-seccomp.json your-name/your-app` Feel free to copy the generated profile :-) You can use the generated Seccomp profile with your original image or with the minified image. ## USING AUTO-GENERATED SECCOMP PROFILES You can use the generated profile with your original image or with the minified image Slim created: `docker run -it --rm --security-opt seccomp:path_to/my-sample-node-app-seccomp.json -p 8000:8000 my/sample-node-app.slim` ## ORIGINAL DEMO VIDEO [![DockerSlim demo](http://img.youtube.com/vi/uKdHnfEbc-E/0.jpg)](https://www.youtube.com/watch?v=uKdHnfEbc-E) [Demo video on YouTube](https://youtu.be/uKdHnfEbc-E) ## DEMO STEPS The demo runs on Mac OS X, but you can build a linux version. Note that these steps are different from the steps in the demo video. 1. Get the Slim app binaries: * [Mac](https://github.com/slimtoolkit/slim/releases/download/1.40.11/dist_mac.zip), * [Mac M1](https://github.com/slimtoolkit/slim/releases/download/1.40.11/dist_mac_m1.zip), * [Linux](https://github.com/slimtoolkit/slim/releases/download/1.40.11/dist_linux.tar.gz), * [Linux ARM](https://github.com/slimtoolkit/slim/releases/download/1.40.11/dist_linux_arm.tar.gz), * [Linux ARM64](https://github.com/slimtoolkit/slim/releases/download/1.40.11/dist_linux_arm64.tar.gz) Unzip them and optionally add their directory to your `PATH` environment variable if you want to use the app from other locations. The extracted directory contains two binaries (and now it also contains a symlink for the old name): - `slim` <- the main Slim application binary - `slim-sensor` <- the sensor application used to collect information from running containers - `docker-slim` <- the symlink to `slim`, the new main app binary (useful if you are still using the old name in your scripts) 2. Clone the `examples` repo to use the sample apps (note: the examples have been moved to a separate repo). You can skip this step if you have your own app. `git clone https://github.com/slimtoolkit/examples.git` 3. Create a Docker image for the sample node.js app in `examples/node_ubuntu`. You can skip this step if you have your own app. `cd examples/node_ubuntu` `docker build -t my/sample-node-app .` 4. Run the Slim app: `./slim build my/sample-node-app` <- run it from the location where you extracted the Slim app binaries (or update your `PATH` env var to include the directory where the Slim app binaries are located) Slim creates a special container based on the target image you provided. It also creates a resource directory where it stores the information it discovers about your image: `/.images/`. By default, the Slim app will run its http probe against the temporary container. If you are minifying a command line tool that doesn't expose any web service interface you'll need to explicitly disable http probing (by setting `--http-probe=false`). 5. Use curl (or other tools) to call the sample app (optional) `curl http://:` This is an optional step to make sure the target app container is doing something. Depending on the application it's an optional step. For some applications it's required if it loads new application resources dynamically based on the requests it's processing (e.g., Ruby or Python). You'll see the mapped ports printed to the console when the Slim app starts the target container. You can also get the port number either from the `docker ps` or `docker port ` commands. The current version of DockerSlim doesn't allow you to map exposed network ports (it works like `docker run … -P`). 6. Press and wait until the Slim app says it's done By default or when http probing is enabled explicitly the Slim app will continue its execution once the http probe is done running. If you explicitly picked a different `continue-after` option follow the expected steps. For example, for the `enter` `continue-after` option you must press the `enter` button on your keyboard. If http probing is enabled (when `http-probe` is set) and if `continue-after` is set to `enter` and you press the `enter` key before the built-in HTTP probe is done the probe might produce an EOF error because the Slim app will shut down the target container before all probe commands are done executing. It's ok to ignore it unless you really need the probe to finish. 7. Once Slim is done check that the new minified image is there `docker images` You should see `my/sample-node-app.slim` in the list of images. Right now all generated images have `.slim` at the end of its name. 8. Use the minified image `docker run -it --rm --name="slim_node_app" -p 8000:8000 my/sample-node-app.slim` ## FAQ ### Is it safe for production use? Yes! Either way, you should test your Docker images. ### How can I contribute if I don't know Go? You don't need to read the language spec and lots of books :-) Go through the [Tour of Go](https://tour.golang.org/welcome/1) and optionally read [50 Shades of Go](https://golang50shades.com/) and you'll be ready to contribute! ### What's the best application for Slim? SlimRToolkit will work for any containerized application; however, Slim automates app interactions for applications with an HTTP API. You can use Slim even if your app doesn't have an HTTP API. You'll need to interact with your application manually to make sure Slim can observe your application behavior. ### Can I use Slim with dockerized command line tools? Yes. The `--cmd`, `--entrypoint`, and `--mount` options will help you minify your image. The `container-transform` tool is a good example. Notes: You can explore the artifacts Slim generates when it's creating a slim image. You'll find those in `/.images//artifacts`. One of the artifacts is a "reverse engineered" Dockerfile for the original image. It'll be called `Dockerfile.reversed`. If you don't want to create a minified image and only want to "reverse engineer" the Dockerfile you can use the `info` command. ### What if my Docker images uses the USER command? The current version of Slim does include support for non-default users (take a look at the non-default user examples (including the ElasticSearch example located in the `3rdparty` directory) in the [`examples`](https://github.com/slimtoolkit/examples) repo. Please open tickets if something doesn't work for you. Everything should work as-is, but for the special cases where the current behavior don't work as expected you can adjust what Slim does using various `build` command parameters: `--run-target-as-user`, `--keep-perms`, `--path-perms`, `--path-perms-file` (along with the `--include-*` parameters). The `--run-target-as-user` parameter is enabled by default and it controls if the application in the temporary container is started using the identity from the USER instruction in the container's Dockerfile. The `--keep-perms` parameter is also enabled by default. It tells Slim to retain the permissions and the ownership information for the files and directories copied to the optimized container image. The `--path-perms` and `--path-perms-file` parameters are similar to the `--include-path` and `--include-path-file` parameters. They are used to overwrite the permission and the user/group information for the target files and directories. Note that the target files/directories are expected to be in the optimized container image. If you don't know if the target files/directories will be in the optimized container you'll need to use one of the `--include-*` parameters (e.g., `--include-path-file`) to explicitly require those artifacts to be included. You can specify the permissions and the ownership information in the `--include-*` parameters too (so you don't need to have the `--path-*` parameters just to set the permissions). The `--path-*` and `--include-*` params use the same format to communicate the permission/owernship info: `TARGET_PATH_OR_NAME:PERMS_IN_OCTAL_FORMAT#USER_ID#GROUP_ID`. You don't have to specify the user and group IDs if you don't want to change them. Here's an example using these parameters to minify the standard `nginx` image adding extra artifacts and changing their permissions: `slim build --include-path='/opt:770#104#107' --include-path='/bin/uname:710' --path-perms='/tmp:700' nginx`. This is what you'll see in the optimized container image: ``` drwx------ 0 0 0 0 Feb 28 22:15 tmp/ -rwx--x--- 0 0 0 31240 Mar 14 2015 bin/uname drwxrwx--- 0 104 107 0 Feb 28 22:13 opt/ ``` The `uname` binary isn't used by nginx, so the `--include-path` parameter is used to keep it in the optimized image changing its permissions to `710`. The `/tmp` directory will be included in the optimized image on its own, so the `--path-perms` parameter is used to change its permissions to `700`. When you set permissions/user/group on a directory the settings are only applied to that directory and not to the artifacts inside. The future versions will allow you to apply the same settings to everything inside the target directory too. Also note that for now you have to use numeric user and group IDs. The future versions will allow you to use user and group names too. ### Nginx fails in my minified image If you see `nginx: [emerg] mkdir() "/var/lib/nginx/body" failed` it means your nginx setup uses a non-standard temporary directory. Nginx will fail if the base directory for its temporary folders doesn't exist (they won't create the missing intermediate directories). Normally it's `/var/lib/nginx`, but if you have a custom config that points to something else you'll need to add an `--include-path` flag as an extra flag when you run the Slim app. ### Slim fails with a 'no permission to read from' error This problem shouldn't happen anymore because the exported artifacts are saved in a tar file and the master app doesn't need to access the files directly anymore. If you run older versions of Slim you can get around this problem by running Slim from a root shell. That way it will have access to all exported files. Slim copies the relevant image artifacts trying to preserve their permissions. If the permissions are too restrictive the master app might not have sufficient privilege to access these files when it's building the new minified image. ## BUILD PROCESS #### Build Options Pick one of the build options that works best for you. ##### Containerized Run `make build_in_docker` on linux or `make build_m1_in_docker` on Macs (or `./scripts/docker-builder.run.sh` or click on `./scripts/mac/docker-builder.run.command` on Macs) from the project directory (builds Slim in a Docker container; great if you don't want to install Go on your local machine and if you already have Docker). ##### Native Run `make build` on linux or `make build_m1` on Macs (or `./scripts/src.build.sh` or click on `./scripts/mac/src.build.command` on Macs) to build Slim natively (requires Go installed locally). Note: Try using the latest version of Go building the Slim app. The current version of Go used to build the Slim app is 1.21. ##### Gitpod If you have a web browser, you can get a fully pre-configured development environment in one click: [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/slimtoolkit/slim) ##### Additional Tools - `license-bill-of-materials` - Optional tool to track dependencies and their licenses. - `golint` - Optional tool for code analysis. See `https://github.com/golang/lint` for more details. You can install these tools using the `tools.get.sh` shell script in the `scripts` directory. Notes: - Make sure you have `golint` if you intend to run the `src.inspect.sh` or `mac.src.inspect.command` scripts. ## CONTRIBUTING If the project sounds interesting or if you found a bug see [`CONTRIBUTING.md`](CONTRIBUTING.md) and submit a PR or open an issue! Non-code contributions including docs are highly appreciated! Open an issue even if you have a question or something is not clear. ### CORE CONCEPTS 1. Inspect container metadata (static analysis) 2. Inspect container data (static analysis) 3. Inspect running application (dynamic analysis) 4. Build an application artifact graph 5. Use the collected application data to build small images 6. Use the collected application data to auto-generate various security framework configurations. ### DYNAMIC ANALYSIS OPTIONS 1. Instrument the container image (and replace the entrypoint/cmd) to collect application activity data 2. Use kernel-level tools that provide visibility into running containers (without instrumenting the containers) 3. Disable relevant namespaces in the target container to gain container visibility (can be done with runC) ### SECURITY The goal is to auto-generate Seccomp, AppArmor, (and potentially SELinux) profiles based on the collected information. - AppArmor profiles - Seccomp profiles ### CHALLENGES Some of the advanced analysis options require a number of Linux kernel features that are not always included. The kernel you get with Docker Machine / Boot2docker is a great example of that. ## ORIGINS DockerSlim was a `Docker Global Hack Day` \#`dockerhackday` project. It barely worked at the time, but it did get a win in Seattle and it took the second place in the `Plumbing` category overall :-) ![DHD3](assets/images/dhd/docker_global_hackday3_red.png) Since then it's been improved and it works pretty well for its core use cases. It can be better though. That's why the project needs your help! You don't need to know much about the container internals, container runtimes and you don't need to know anything about Go. You can contribute in many different ways. For example, use Slim on your images and open Github issues documenting your experience even if it worked just fine :-) ## LICENSE Apache License v2, see [LICENSE](https://github.com/slimtoolkit/slim/blob/master/LICENSE) for details. ## CODE OF CONDUCT The project follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). --- **We are a [Cloud Native Computing Foundation](https://cncf.io/) sandbox project.** --- [![Go Report Card](https://goreportcard.com/badge/github.com/slimtoolkit/slim)](https://goreportcard.com/report/github.com/slimtoolkit/slim) ================================================ FILE: ROADMAP.md ================================================ # High Level Project Roadmap This is a high level roadmap that identies the current areas of focus. Note that it's not a detailed list of every possible enhancement. * Community * Collaborate with other CNCF projects to achieve mutually benefitial outcomes * Talks, outreach, community training * Engage with the community to increase project contributions * Documentation * Improve system design documentations to make it easier for new contributors to contribute to the project * User docs (v1) * Non-docker runtime support * Direct ContainerD support * Finch integration * Podman support * Kubernetes support vNext * Container debugging * Ephemeral container based debugging for Kubernetes * Build/Optimize engine * Error and logging enhancements to improve debuggability * Improved build flag documentation with examples * Improved CI/build tool integration documentation (including Github Actions) * Integrations * Consign integrations for `xray` (reporting) and `build` (signing) * Plugins * Plugin subsystem design * Sample plugins * Container image build plugin for BuildKit * System sensor * System sensor subsystem design * External sensor integrations for Tetragon, Falco and Tracee as plugins * Installers for all major platforms/package managers and publishing the packages to the official package manager distribution repos * Homebrew (official tap), Mac Ports * Apt * Yum/Dnf/Rpm * Apk * Aur * Nix * Example * More build/optimize/minify examples * Documenting examples including the configs used to produce the minified images ================================================ FILE: SECURITY.md ================================================ # All Things Security ## Reporting Security Issues To report a security issue in the project itself, please use the GitHub Security Advisory ["Report a Vulnerability"](https://github.com/slimtoolkit/slim/security/advisories/new) tab. The project members will send a response indicating the next steps in handling your report. After the initial reply to your report, the project members will keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance. To report a known security issue in one of the project dependencies, please open a regular project issue and/or submit a PR with the updated version of the impacted dependency. ================================================ FILE: WISHLIST.md ================================================ # Enhancements Wishlist If you want to help feel free to submit a PR! You can also open an issue to track the work and to have a place to talk about it. ## AppArmor profile improvements ## Better support for command line applications (e.g., ability to specify multiple executions) ## Discover HTTP endpoints to make the HTTP probe more intelligent. ## Scripting language dependency discovery in the "scanner" app. ## Explore additional dependency discovery methods. ## "Live" image create mode - to create new images from containers where users install their applications interactively. ## Classic container image optimizations (aka ability to disable minification based on dynamic/static analysis) - Docker image flattening - OS specific cleanup commands (optional) ## Native Windows Support Original issue: - ## Provide an Audit Log for the Removed Files Original issue: - ## Dockerizing Local Applications (Linux Only) Dockerizing local applications and creating minified images for them. ## Minifying Local Applications and Saving to Directory (Linux Only) Original issue: - ## Podman Support Original issues: - - ## Windows Container Support Original issues: - ================================================ FILE: assets/images/docs/SlimHow.excalidraw ================================================ { "type": "excalidraw", "version": 2, "source": "https://excalidraw.com", "elements": [ { "type": "rectangle", "version": 315, "versionNonce": 1437085463, "isDeleted": false, "id": "z5FJM2AF322l6jPcAbRUT", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 60, "angle": 0, "x": 433.236328125, "y": 462.1044921875, "strokeColor": "#000000", "backgroundColor": "#ced4da", "width": 119.07421875, "height": 60.2578125, "seed": 2130074937, "groupIds": [], "roundness": null, "boundElements": [ { "id": "t5q3DU5ObK_JRfQ6w1xnQ", "type": "arrow" }, { "id": "V5vFMqArwYWyLiCq52rz3", "type": "arrow" } ], "updated": 1652277748492, "link": null, "locked": false }, { "type": "rectangle", "version": 231, "versionNonce": 1785310969, "isDeleted": false, "id": "CPRJJsoQCL9gIWgMlqRSW", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 70, "angle": 0, "x": 610.5283203125, "y": 575.9296875, "strokeColor": "#343a40", "backgroundColor": "#ced4da", "width": 257, "height": 85, "seed": 1618919993, "groupIds": [], "roundness": { "type": 1 }, "boundElements": [ { "id": "t5q3DU5ObK_JRfQ6w1xnQ", "type": "arrow" }, { "id": "RVgkHQgXZXIxCj76R427q", "type": "arrow" }, { "id": "BfrdlP1Cm8qndUsOb9Uyr", "type": "arrow" }, { "id": "sM8K4HhcbMtur8rrMNU7R", "type": "arrow" }, { "id": "uFyv2pSPIcyBia8PNAfgX", "type": "arrow" } ], "updated": 1652277590802, "link": null, "locked": false }, { "type": "rectangle", "version": 448, "versionNonce": 1587188953, "isDeleted": false, "id": "FYTFj4-mnR4zFfjpeWS5f", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 40, "angle": 0, "x": 121.69189453125, "y": 821.80224609375, "strokeColor": "#495057", "backgroundColor": "#15aabf", "width": 267.2802734375, "height": 105.56640624999999, "seed": 521273, "groupIds": [], "roundness": { "type": 1 }, "boundElements": [ { "id": "uFyv2pSPIcyBia8PNAfgX", "type": "arrow" }, { "id": "_rYpNumYsf_s6iMScQHjX", "type": "arrow" }, { "id": "sM8K4HhcbMtur8rrMNU7R", "type": "arrow" } ], "updated": 1652277053785, "link": null, "locked": false }, { "type": "rectangle", "version": 136, "versionNonce": 1620316665, "isDeleted": false, "id": "QOHGSkDnHQWYI-H2QmJiR", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 70, "angle": 0, "x": 614.451171875, "y": 399.01953125, "strokeColor": "#000000", "backgroundColor": "#15aabf", "width": 252.7470703125, "height": 84.02734375, "seed": 1780101943, "groupIds": [], "roundness": null, "boundElements": [ { "id": "RVgkHQgXZXIxCj76R427q", "type": "arrow" } ], "updated": 1652277165337, "link": null, "locked": false }, { "type": "text", "version": 171, "versionNonce": 1703457160, "isDeleted": false, "id": "DG1lDpjssUskLgYXdBntC", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 640.5009765625, "y": 425.1005859375, "strokeColor": "#000000", "backgroundColor": "transparent", "width": 205, "height": 30, "seed": 491250873, "groupIds": [], "roundness": null, "boundElements": [], "updated": 1673285239485, "link": null, "locked": false, "fontSize": 24.855468749999993, "fontFamily": 3, "text": "original image", "baseline": 24, "textAlign": "left", "verticalAlign": "top", "containerId": null, "originalText": "original image" }, { "type": "text", "version": 1333, "versionNonce": 1149242248, "isDeleted": false, "id": "dM4b1D2w11licRcaXwKCI", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 406.2029128389365, "y": 241.02862936877142, "strokeColor": "#364fc7", "backgroundColor": "transparent", "width": 806, "height": 106, "seed": 1982017081, "groupIds": [], "roundness": null, "boundElements": [], "updated": 1673285172173, "link": null, "locked": false, "fontSize": 46.19083069132936, "fontFamily": 2, "text": "How Slim makes images smaller, faster\n...and more secure", "baseline": 96, "textAlign": "right", "verticalAlign": "top", "containerId": null, "originalText": "How Slim makes images smaller, faster\n...and more secure" }, { "type": "rectangle", "version": 301, "versionNonce": 2047994489, "isDeleted": false, "id": "PqX1zxQsw9VE86_SzdHQt", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 20, "angle": 0, "x": 611.859375, "y": 752.9091796875, "strokeColor": "#000000", "backgroundColor": "#ced4da", "width": 253.396484375, "height": 117.71484374999994, "seed": 293155001, "groupIds": [], "roundness": null, "boundElements": [], "updated": 1652277601970, "link": null, "locked": false }, { "type": "rectangle", "version": 396, "versionNonce": 401807193, "isDeleted": false, "id": "crQQzA8TS6rb5RWykT26t", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 70, "angle": 0, "x": 610.505859375, "y": 962.2109375, "strokeColor": "#000000", "backgroundColor": "#15aabf", "width": 254, "height": 48, "seed": 1079428281, "groupIds": [], "roundness": null, "boundElements": [ { "id": "wz_CJ_GfqheVtN5tUFmax", "type": "arrow" }, { "id": "_rYpNumYsf_s6iMScQHjX", "type": "arrow" } ], "updated": 1652277239768, "link": null, "locked": false }, { "type": "text", "version": 736, "versionNonce": 1589654117, "isDeleted": false, "id": "GbjLgg--QdFAwXGlfgW_T", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 85.2552083333334, "y": 333.7072405133929, "strokeColor": "#343a40", "backgroundColor": "transparent", "width": 568, "height": 34, "seed": 538091993, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [], "updated": 1673284487993, "link": null, "locked": false, "fontSize": 28.461916095800987, "fontFamily": 3, "text": "$ slim build --target nginx:latest", "baseline": 28, "textAlign": "left", "verticalAlign": "top", "containerId": null, "originalText": "$ slim build --target nginx:latest" }, { "type": "rectangle", "version": 855, "versionNonce": 2079903609, "isDeleted": false, "id": "vBMcaaFI9kuqkow1X67Yi", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 80, "angle": 0, "x": 611.2001953125, "y": 1020.8388671875, "strokeColor": "#000000", "backgroundColor": "#fab005", "width": 251, "height": 41, "seed": 787876665, "groupIds": [], "roundness": null, "boundElements": [ { "id": "QeTboF3pareoNleVc6BRi", "type": "arrow" } ], "updated": 1652277256179, "link": null, "locked": false }, { "type": "arrow", "version": 2170, "versionNonce": 896420249, "isDeleted": false, "id": "sM8K4HhcbMtur8rrMNU7R", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 1, "opacity": 50, "angle": 0, "x": 385.2291230938969, "y": 602.1984828376064, "strokeColor": "#495057", "backgroundColor": "transparent", "width": 221.72107221860313, "height": 19.770614981174276, "seed": 1331821687, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [], "updated": 1652277951776, "link": null, "locked": false, "startBinding": { "elementId": "zasfCRJFIwlKmgpkmVLKv", "focus": -0.4186831681048361, "gap": 14.42712114077176 }, "endBinding": { "elementId": "CPRJJsoQCL9gIWgMlqRSW", "focus": -0.283861701539277, "gap": 3.578125 }, "lastCommittedPoint": null, "startArrowhead": null, "endArrowhead": null, "points": [ [ 0, 0 ], [ 221.72107221860313, 19.770614981174276 ] ] }, { "type": "arrow", "version": 714, "versionNonce": 391364375, "isDeleted": false, "id": "t5q3DU5ObK_JRfQ6w1xnQ", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 1, "opacity": 100, "angle": 0, "x": 547.7092635363069, "y": 526.7060994337498, "strokeColor": "#343a40", "backgroundColor": "transparent", "width": 61.5425314704446, "height": 49.59285764199774, "seed": 515474615, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [], "updated": 1652277889185, "link": null, "locked": false, "startBinding": { "elementId": "z5FJM2AF322l6jPcAbRUT", "focus": -0.12542319940551994, "gap": 4.343794746249841 }, "endBinding": { "elementId": "CPRJJsoQCL9gIWgMlqRSW", "focus": -0.4275761472351255, "gap": 1.2765253057484642 }, "lastCommittedPoint": null, "startArrowhead": null, "endArrowhead": "arrow", "points": [ [ 0, 0 ], [ 61.5425314704446, 49.59285764199774 ] ] }, { "type": "text", "version": 463, "versionNonce": 1622338649, "isDeleted": false, "id": "bqnQdIHAkULBIug1XcmPG", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 441.8232421875, "y": 420.7783203125, "strokeColor": "#d9480f", "backgroundColor": "transparent", "width": 110, "height": 30, "seed": 1243474103, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [ { "id": "V5vFMqArwYWyLiCq52rz3", "type": "arrow" } ], "updated": 1652278037947, "link": null, "locked": false, "fontSize": 23.804687500000046, "fontFamily": 1, "text": "...injected", "baseline": 21, "textAlign": "left", "verticalAlign": "top", "containerId": null, "originalText": "...injected" }, { "type": "arrow", "version": 462, "versionNonce": 443190231, "isDeleted": false, "id": "RVgkHQgXZXIxCj76R427q", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 743.4718635056754, "y": 488.46550802076536, "strokeColor": "#495057", "backgroundColor": "transparent", "width": 0, "height": 80.0842839888669, "seed": 280942169, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [], "updated": 1652278050690, "link": null, "locked": false, "startBinding": { "elementId": "QOHGSkDnHQWYI-H2QmJiR", "focus": -0.020947079395637863, "gap": 5.4186330207653555 }, "endBinding": { "elementId": "CPRJJsoQCL9gIWgMlqRSW", "focus": 0.03458010267062569, "gap": 7.379895490367744 }, "lastCommittedPoint": null, "startArrowhead": null, "endArrowhead": "arrow", "points": [ [ 0, 0 ], [ 0, 80.0842839888669 ] ] }, { "type": "arrow", "version": 2012, "versionNonce": 81754937, "isDeleted": false, "id": "uFyv2pSPIcyBia8PNAfgX", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 1, "opacity": 50, "angle": 0, "x": 391.6142700172496, "y": 724.4170629482254, "strokeColor": "#495057", "backgroundColor": "transparent", "width": 218.19024224766747, "height": 73.20081820558028, "seed": 1434877081, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [], "updated": 1652278291361, "link": null, "locked": false, "startBinding": { "elementId": "i_JDhedICwAdWdxkiudrF", "focus": 0.34566114961327016, "gap": 5.454602048499623 }, "endBinding": { "elementId": "CPRJJsoQCL9gIWgMlqRSW", "focus": 0.12342916764409954, "gap": 1 }, "lastCommittedPoint": null, "startArrowhead": null, "endArrowhead": null, "points": [ [ 0, 0 ], [ 218.19024224766747, -73.20081820558028 ] ] }, { "type": "text", "version": 208, "versionNonce": 113407577, "isDeleted": false, "id": "JzjVz4GXgYEQjEGYJmImJ", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 622.744140625, "y": 762.8055462861181, "strokeColor": "#000000", "backgroundColor": "transparent", "width": 203, "height": 98, "seed": 853002329, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [ { "id": "BfrdlP1Cm8qndUsOb9Uyr", "type": "arrow" }, { "id": "wz_CJ_GfqheVtN5tUFmax", "type": "arrow" } ], "updated": 1652277508373, "link": null, "locked": false, "fontSize": 20.395532349246235, "fontFamily": 3, "text": " Usage report\n- files accessed\n- syscalls issued\n- certs detected", "baseline": 93, "textAlign": "left", "verticalAlign": "top", "containerId": null, "originalText": " Usage report\n- files accessed\n- syscalls issued\n- certs detected" }, { "type": "text", "version": 1201, "versionNonce": 246888101, "isDeleted": false, "id": "WrWXFw3ZUzBc8EWTQaN9a", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 139.72831864567638, "y": 833.4317599335407, "strokeColor": "#343a40", "backgroundColor": "transparent", "width": 231, "height": 86, "seed": 622664953, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [ { "id": "_rYpNumYsf_s6iMScQHjX", "type": "arrow" } ], "updated": 1673284433631, "link": null, "locked": false, "fontSize": 22.383996272638267, "fontFamily": 1, "text": "...contains only files\nthat have been used\nor allow-listed!", "baseline": 77, "textAlign": "left", "verticalAlign": "top", "containerId": null, "originalText": "...contains only files\nthat have been used\nor allow-listed!" }, { "type": "text", "version": 289, "versionNonce": 101505911, "isDeleted": false, "id": "_oadoJngWdG6I-ukx4lVQ", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 1012.060546875, "y": 423.767578125, "strokeColor": "#495057", "backgroundColor": "transparent", "width": 96, "height": 46, "seed": 401330327, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [], "updated": 1652277522957, "link": null, "locked": false, "fontSize": 36.01924189814814, "fontFamily": 1, "text": "Input", "baseline": 32, "textAlign": "left", "verticalAlign": "top", "containerId": null, "originalText": "Input" }, { "type": "text", "version": 533, "versionNonce": 1599961145, "isDeleted": false, "id": "SrROoMHa6yZE1bPrQlyug", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 1007.0869140625, "y": 993.3746925636574, "strokeColor": "#495057", "backgroundColor": "transparent", "width": 127, "height": 46, "seed": 561191607, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [], "updated": 1652277086720, "link": null, "locked": false, "fontSize": 36.01924189814814, "fontFamily": 1, "text": "Output", "baseline": 32, "textAlign": "left", "verticalAlign": "top", "containerId": null, "originalText": "Output" }, { "type": "text", "version": 419, "versionNonce": 110228951, "isDeleted": false, "id": "1Wyoi3jBu6hHSKrc54wuX", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 1001.08984375, "y": 786.3388671875, "strokeColor": "#495057", "backgroundColor": "transparent", "width": 168, "height": 46, "seed": 940889113, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [], "updated": 1652277089165, "link": null, "locked": false, "fontSize": 36.01924189814814, "fontFamily": 1, "text": "Artifacts", "baseline": 32, "textAlign": "left", "verticalAlign": "top", "containerId": null, "originalText": "Artifacts" }, { "type": "text", "version": 437, "versionNonce": 692570841, "isDeleted": false, "id": "R9CCEm5odcJmVEN_Hv3WD", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 1007.5380859375, "y": 595.416015625, "strokeColor": "#495057", "backgroundColor": "transparent", "width": 172, "height": 46, "seed": 886007447, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [], "updated": 1652277848435, "link": null, "locked": false, "fontSize": 36.01924189814814, "fontFamily": 1, "text": "Monitoring", "baseline": 32, "textAlign": "left", "verticalAlign": "top", "containerId": null, "originalText": "Monitoring" }, { "type": "arrow", "version": 407, "versionNonce": 217048889, "isDeleted": false, "id": "BfrdlP1Cm8qndUsOb9Uyr", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 744.9128571963805, "y": 669.0392584528588, "strokeColor": "#495057", "backgroundColor": "transparent", "width": 0.8645066131217618, "height": 79.2116003332593, "seed": 492680345, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [], "updated": 1652278054270, "link": null, "locked": false, "startBinding": { "elementId": "CPRJJsoQCL9gIWgMlqRSW", "focus": -0.049912317353729085, "gap": 8.109570952858803 }, "endBinding": { "elementId": "JzjVz4GXgYEQjEGYJmImJ", "focus": 0.18729477388763419, "gap": 14.5546875 }, "lastCommittedPoint": null, "startArrowhead": null, "endArrowhead": "arrow", "points": [ [ 0, 0 ], [ -0.8645066131217618, 79.2116003332593 ] ] }, { "type": "arrow", "version": 343, "versionNonce": 512939959, "isDeleted": false, "id": "wz_CJ_GfqheVtN5tUFmax", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 744.6362904671561, "y": 876.8055462861182, "strokeColor": "#495057", "backgroundColor": "transparent", "width": 0.22841240668788032, "height": 77.80103853531045, "seed": 428109753, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [], "updated": 1652277881130, "link": null, "locked": false, "startBinding": { "elementId": "JzjVz4GXgYEQjEGYJmImJ", "focus": -0.19872764374547802, "gap": 16 }, "endBinding": { "elementId": "crQQzA8TS6rb5RWykT26t", "focus": 0.058641711083579316, "gap": 7.604352678571331 }, "lastCommittedPoint": null, "startArrowhead": null, "endArrowhead": "arrow", "points": [ [ 0, 0 ], [ 0.22841240668788032, 77.80103853531045 ] ] }, { "type": "text", "version": 486, "versionNonce": 1852162952, "isDeleted": false, "id": "V00LcEEQ_gK9fHP0y5o1T", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 818.8847656249998, "y": 514.744140625, "strokeColor": "#d9480f", "backgroundColor": "transparent", "width": 192, "height": 31, "seed": 309311449, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [], "updated": 1673285330321, "link": null, "locked": false, "fontSize": 24.60546875000003, "fontFamily": 1, "text": "Start container", "baseline": 22, "textAlign": "left", "verticalAlign": "top", "containerId": null, "originalText": "Start container" }, { "type": "text", "version": 431, "versionNonce": 342087928, "isDeleted": false, "id": "3l0CLOhz3zEXXW6HtE8ZQ", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 811.546875, "y": 694.6171875, "strokeColor": "#d9480f", "backgroundColor": "transparent", "width": 229, "height": 32, "seed": 945625687, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [], "updated": 1673285206328, "link": null, "locked": false, "fontSize": 25.542968750000032, "fontFamily": 1, "text": "Collect intelligence", "baseline": 23, "textAlign": "left", "verticalAlign": "top", "containerId": null, "originalText": "Collect intelligence" }, { "type": "text", "version": 885, "versionNonce": 311163640, "isDeleted": false, "id": "v3pcwqTE_okdLy0SnTK-u", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 811.8203125, "y": 887.5185856894842, "strokeColor": "#d9480f", "backgroundColor": "transparent", "width": 310, "height": 60, "seed": 376023255, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [], "updated": 1673285210447, "link": null, "locked": false, "fontSize": 23.871651785714267, "fontFamily": 1, "text": "Build slim image &\nGenerate security profiles", "baseline": 51, "textAlign": "left", "verticalAlign": "top", "containerId": null, "originalText": "Build slim image &\nGenerate security profiles" }, { "type": "arrow", "version": 1708, "versionNonce": 1315138071, "isDeleted": false, "id": "_rYpNumYsf_s6iMScQHjX", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 1, "opacity": 50, "angle": 0, "x": 389.8753776410778, "y": 910.4155513824027, "strokeColor": "#495057", "backgroundColor": "transparent", "width": 216.6888320638979, "height": 57.55225935236285, "seed": 388971671, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [], "updated": 1652277933826, "link": null, "locked": false, "startBinding": { "elementId": "WrWXFw3ZUzBc8EWTQaN9a", "focus": -0.01603557213069114, "gap": 19.6931103884736 }, "endBinding": { "elementId": "crQQzA8TS6rb5RWykT26t", "focus": -0.2864109335297447, "gap": 3.941649670024276 }, "lastCommittedPoint": null, "startArrowhead": null, "endArrowhead": null, "points": [ [ 0, 0 ], [ 216.6888320638979, 57.55225935236285 ] ] }, { "type": "text", "version": 853, "versionNonce": 769412325, "isDeleted": false, "id": "rILfc2fIcaTQXZMawymNt", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 85.2490234375, "y": 260.3280741373698, "strokeColor": "#d9480f", "backgroundColor": "transparent", "width": 122, "height": 60, "seed": 2106685079, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [], "updated": 1673284473184, "link": null, "locked": false, "fontSize": 23.489067485367045, "fontFamily": 1, "text": "Turn-key\nexperience!", "baseline": 51, "textAlign": "left", "verticalAlign": "top", "containerId": null, "originalText": "Turn-key\nexperience!" }, { "type": "rectangle", "version": 502, "versionNonce": 1521493625, "isDeleted": false, "id": "gfNwGmVCEp-4jZWpPJdmi", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 40, "angle": 0, "x": 120.34521484375, "y": 958.528076171875, "strokeColor": "#495057", "backgroundColor": "#15aabf", "width": 267.2802734375, "height": 88.59375, "seed": 598563481, "groupIds": [], "roundness": { "type": 1 }, "boundElements": [ { "id": "uFyv2pSPIcyBia8PNAfgX", "type": "arrow" }, { "id": "_rYpNumYsf_s6iMScQHjX", "type": "arrow" }, { "id": "QeTboF3pareoNleVc6BRi", "type": "arrow" } ], "updated": 1652277075868, "link": null, "locked": false }, { "type": "text", "version": 1317, "versionNonce": 1429967205, "isDeleted": false, "id": "BIbK5nClxTKKeZyQVy-th", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 138.04616477272685, "y": 973.9619140625, "strokeColor": "#343a40", "backgroundColor": "transparent", "width": 236, "height": 56, "seed": 2000031479, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [ { "id": "QeTboF3pareoNleVc6BRi", "type": "arrow" } ], "updated": 1673284433633, "link": null, "locked": false, "fontSize": 22.237859080018172, "fontFamily": 1, "text": "...can be used for\nfuture container runs", "baseline": 48, "textAlign": "left", "verticalAlign": "top", "containerId": null, "originalText": "...can be used for\nfuture container runs" }, { "type": "arrow", "version": 1798, "versionNonce": 690402521, "isDeleted": false, "id": "QeTboF3pareoNleVc6BRi", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 1, "opacity": 50, "angle": 0, "x": 389.64943041233084, "y": 1012.8135126541206, "strokeColor": "#495057", "backgroundColor": "transparent", "width": 218.37225484115527, "height": 32.670948771896064, "seed": 1016723929, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [], "updated": 1652278282558, "link": null, "locked": false, "startBinding": { "elementId": "BIbK5nClxTKKeZyQVy-th", "focus": -0.19757405030399774, "gap": 15.562516349830872 }, "endBinding": { "elementId": "dTwf5rZDEkp-V27Ai9lU3", "focus": -0.7613862562065846, "gap": 14.963666309013888 }, "lastCommittedPoint": null, "startArrowhead": null, "endArrowhead": null, "points": [ [ 0, 0 ], [ 218.37225484115527, 32.670948771896064 ] ] }, { "type": "rectangle", "version": 456, "versionNonce": 1790881081, "isDeleted": false, "id": "KhmJMkd5HKY0qq1lceBTL", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 40, "angle": 0, "x": 118.63525390625, "y": 559.05322265625, "strokeColor": "#495057", "backgroundColor": "#15aabf", "width": 267.2802734375, "height": 99.20410156250006, "seed": 1801822617, "groupIds": [], "roundness": { "type": 1 }, "boundElements": [ { "id": "sM8K4HhcbMtur8rrMNU7R", "type": "arrow" }, { "id": "uFyv2pSPIcyBia8PNAfgX", "type": "arrow" } ], "updated": 1652277942774, "link": null, "locked": false }, { "type": "text", "version": 782, "versionNonce": 97896633, "isDeleted": false, "id": "zasfCRJFIwlKmgpkmVLKv", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 129.8020019531251, "y": 571.1163736979165, "strokeColor": "#343a40", "backgroundColor": "transparent", "width": 241, "height": 81, "seed": 544317559, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [ { "id": "sM8K4HhcbMtur8rrMNU7R", "type": "arrow" } ], "updated": 1652277951775, "link": null, "locked": false, "fontSize": 21.447541316350293, "fontFamily": 1, "text": "Probe running container\n- Send HTTP requests\n- Exec commands", "baseline": 73, "textAlign": "left", "verticalAlign": "top", "containerId": null, "originalText": "Probe running container\n- Send HTTP requests\n- Exec commands" }, { "type": "rectangle", "version": 566, "versionNonce": 1169826359, "isDeleted": false, "id": "i_JDhedICwAdWdxkiudrF", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 40, "angle": 0, "x": 118.87939453125, "y": 689.099609375, "strokeColor": "#495057", "backgroundColor": "#15aabf", "width": 267.2802734375, "height": 98.8134765625, "seed": 655355703, "groupIds": [], "roundness": { "type": 1 }, "boundElements": [ { "id": "uFyv2pSPIcyBia8PNAfgX", "type": "arrow" } ], "updated": 1652277965303, "link": null, "locked": false }, { "type": "text", "version": 900, "versionNonce": 1527538539, "isDeleted": false, "id": "6-9VXYHfqETW1jPuxlEkR", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 136.07952277861432, "y": 697.47119140625, "strokeColor": "#343a40", "backgroundColor": "transparent", "width": 223, "height": 84, "seed": 93520825, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [], "updated": 1673284433634, "link": null, "locked": false, "fontSize": 22.15852204288347, "fontFamily": 1, "text": "Apply heuristics\n- find SSL certs\n- detect shells, etc.", "baseline": 76, "textAlign": "left", "verticalAlign": "top", "containerId": null, "originalText": "Apply heuristics\n- find SSL certs\n- detect shells, etc." }, { "type": "text", "version": 252, "versionNonce": 2057015800, "isDeleted": false, "id": "pEd0fds1ka7qENQ9DIWVu", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 661.8134765625, "y": 971.4794921875, "strokeColor": "#000000", "backgroundColor": "transparent", "width": 147, "height": 30, "seed": 1189050327, "groupIds": [], "roundness": null, "boundElements": [], "updated": 1673285246587, "link": null, "locked": false, "fontSize": 24.855468749999993, "fontFamily": 3, "text": "slim image", "baseline": 24, "textAlign": "left", "verticalAlign": "top", "containerId": null, "originalText": "slim image" }, { "type": "text", "version": 87, "versionNonce": 1961761989, "isDeleted": false, "id": "dTwf5rZDEkp-V27Ai9lU3", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 1, "opacity": 100, "angle": 0, "x": 623.0654296875005, "y": 1028.97265625, "strokeColor": "#000000", "backgroundColor": "#fab005", "width": 229, "height": 26, "seed": 1400126743, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [ { "id": "QeTboF3pareoNleVc6BRi", "type": "arrow" } ], "updated": 1673284433634, "link": null, "locked": false, "fontSize": 21.611328125000043, "fontFamily": 3, "text": "AppArmor & seccomp", "baseline": 21, "textAlign": "right", "verticalAlign": "top", "containerId": null, "originalText": "AppArmor & seccomp" }, { "type": "text", "version": 217, "versionNonce": 1876461561, "isDeleted": false, "id": "oRPMpJJXx9jtmBzX17fXM", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 679.515625, "y": 589.7587890625, "strokeColor": "#000000", "backgroundColor": "#ced4da", "width": 124, "height": 56, "seed": 321048313, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [], "updated": 1652277479950, "link": null, "locked": false, "fontSize": 23.352864583333275, "fontFamily": 3, "text": "tmp\ncontainer", "baseline": 51, "textAlign": "center", "verticalAlign": "top", "containerId": null, "originalText": "tmp\ncontainer" }, { "type": "text", "version": 288, "versionNonce": 543842327, "isDeleted": false, "id": "cgn0SQz_n5ih5Kfi34k8T", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 450.4482421875, "y": 476.158203125, "strokeColor": "#000000", "backgroundColor": "#ced4da", "width": 83, "height": 28, "seed": 1989060951, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [], "updated": 1652277570463, "link": null, "locked": false, "fontSize": 23.352864583333275, "fontFamily": 3, "text": "sensor", "baseline": 23, "textAlign": "center", "verticalAlign": "top", "containerId": null, "originalText": "sensor" }, { "type": "rectangle", "version": 553, "versionNonce": 449118233, "isDeleted": false, "id": "FBg8KHuxXgjqikZHW44mF", "fillStyle": "hachure", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 1, "opacity": 40, "angle": 0, "x": 117.75146484375, "y": 428.31591796875, "strokeColor": "#495057", "backgroundColor": "#15aabf", "width": 267.2802734375, "height": 96.1962890625, "seed": 825056697, "groupIds": [], "roundness": { "type": 1 }, "boundElements": [ { "id": "sM8K4HhcbMtur8rrMNU7R", "type": "arrow" }, { "id": "uFyv2pSPIcyBia8PNAfgX", "type": "arrow" }, { "id": "V5vFMqArwYWyLiCq52rz3", "type": "arrow" } ], "updated": 1652277753742, "link": null, "locked": false }, { "type": "text", "version": 973, "versionNonce": 774028811, "isDeleted": false, "id": "8hru3Ov7Y2SQk8-ogQPD1", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "angle": 0, "x": 147.1025390624999, "y": 436.326171875, "strokeColor": "#343a40", "backgroundColor": "transparent", "width": 198, "height": 83, "seed": 2001103255, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [ { "id": "sM8K4HhcbMtur8rrMNU7R", "type": "arrow" } ], "updated": 1673284433634, "link": null, "locked": false, "fontSize": 21.80801497565377, "fontFamily": 1, "text": "...supports multiple\nmonitors: ptrace,\nfanotify, etc.", "baseline": 75, "textAlign": "left", "verticalAlign": "top", "containerId": null, "originalText": "...supports multiple\nmonitors: ptrace,\nfanotify, etc." }, { "type": "arrow", "version": 2334, "versionNonce": 398947705, "isDeleted": false, "id": "V5vFMqArwYWyLiCq52rz3", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 1, "opacity": 50, "angle": 0, "x": 387.0132970958768, "y": 483.00853630125755, "strokeColor": "#495057", "backgroundColor": "transparent", "width": 40.42427847351763, "height": 5.393809927460552, "seed": 14150681, "groupIds": [], "roundness": { "type": 2 }, "boundElements": [], "updated": 1652277766342, "link": null, "locked": false, "startBinding": { "elementId": "FBg8KHuxXgjqikZHW44mF", "focus": -0.1744512163789095, "gap": 1.9815588146267942 }, "endBinding": { "elementId": "z5FJM2AF322l6jPcAbRUT", "focus": -0.12835124589854846, "gap": 5.798752555605574 }, "lastCommittedPoint": null, "startArrowhead": null, "endArrowhead": null, "points": [ [ 0, 0 ], [ 40.42427847351763, 5.393809927460552 ] ] } ], "appState": { "gridSize": null, "viewBackgroundColor": "#ffffff" }, "files": {} } ================================================ FILE: build/package/docker/.dockerignore ================================================ ** !dist_linux/** !build/package/docker/.ds.container.d3e2c84f976743bdb92a7044ef12e381 **/.DS_Store **/*.command ================================================ FILE: build/package/docker/.ds.container.d3e2c84f976743bdb92a7044ef12e381 ================================================ ================================================ FILE: build/package/docker/Dockerfile ================================================ FROM alpine:latest as ca-certs LABEL build-role=ca-certs RUN apk update && apk upgrade && apk add --no-cache ca-certificates && update-ca-certificates 2>/dev/null || true FROM scratch LABEL app=slim COPY --from=ca-certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt COPY dist_linux /bin COPY build/package/docker/.ds.container.d3e2c84f976743bdb92a7044ef12e381 /.ds.container.d3e2c84f976743bdb92a7044ef12e381 VOLUME /bin/.slim-state ENTRYPOINT ["/bin/slim"] ================================================ FILE: build/package/docker/Dockerfile.arm ================================================ FROM alpine:latest as ca-certs LABEL build-role=ca-certs RUN apk update && apk upgrade && apk add --no-cache ca-certificates && update-ca-certificates 2>/dev/null || true FROM scratch LABEL app=slim COPY --from=ca-certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt COPY dist_linux_arm64 /bin COPY build/package/docker/.ds.container.d3e2c84f976743bdb92a7044ef12e381 /.ds.container.d3e2c84f976743bdb92a7044ef12e381 VOLUME /bin/.slim-state ENTRYPOINT ["/bin/slim"] ================================================ FILE: build/package/docker/build.sh ================================================ #!/usr/bin/env bash set -e docker build --squash --rm -t slim -f Dockerfile ../../.. docker image prune --filter label=build-role=ca-certs -f docker image prune --filter label=app=slim -f ================================================ FILE: build/package/docker/build_arm.sh ================================================ #!/usr/bin/env bash set -e docker build --platform linux/arm64 -t slim-arm -f Dockerfile.arm ../../.. docker image prune --filter label=build-role=ca-certs -f docker image prune --filter label=app=slim -f ================================================ FILE: build/package/docker/dockerhub_publish.sh ================================================ #!/usr/bin/env bash set -e SOURCE="${BASH_SOURCE[0]}" while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done BDIR="$( cd -P "$( dirname "$SOURCE" )/../../.." && pwd )" TAG="current" pushd $BDIR if hash git 2>/dev/null && [ -e $BDIR/.git ]; then TAG="$(git describe --tags)" fi popd docker tag slim dslim/slim:$TAG docker tag slim dslim/slim docker push dslim/slim:$TAG docker push dslim/slim ================================================ FILE: build/package/docker/dockerhub_publish_arm.sh ================================================ #!/usr/bin/env bash set -e SOURCE="${BASH_SOURCE[0]}" while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done BDIR="$( cd -P "$( dirname "$SOURCE" )/../../.." && pwd )" TAG="current" pushd $BDIR if hash git 2>/dev/null && [ -e $BDIR/.git ]; then TAG="$(git describe --tags)" fi popd docker tag slim-arm dslim/slim-arm:$TAG docker tag slim-arm dslim/slim-arm docker push dslim/slim-arm:$TAG docker push dslim/slim-arm ================================================ FILE: build/package/docker/mac/build.command ================================================ here="$(dirname "$BASH_SOURCE")" cd $here/.. ./build.sh ================================================ FILE: build/package/docker/mac/build_arm.command ================================================ here="$(dirname "$BASH_SOURCE")" cd $here/.. ./build_arm.sh ================================================ FILE: build/package/docker/mac/dockerhub_login.command ================================================ here="$(dirname "$BASH_SOURCE")" cd $here docker login --username dslim ================================================ FILE: build/package/docker/mac/dockerhub_publish.command ================================================ here="$(dirname "$BASH_SOURCE")" cd $here/.. ./dockerhub_publish.sh ================================================ FILE: build/package/docker/mac/dockerhub_publish_arm.command ================================================ here="$(dirname "$BASH_SOURCE")" cd $here/.. ./dockerhub_publish_arm.sh ================================================ FILE: build/package/rpm/slim.spec ================================================ %global go_version 1.18.2 Name: slim Version: 1.40.6 Release: 1%{?dist} Summary: Slim Toolkit helps you make your containers better, smaller, and secure License: Apache-2.0 BuildRequires: golang >= %{go_version} URL: https://github.com/slimtoolkit/slim Source0: https://github.com/slimtoolkit/slim/archive/refs/tags/%{version}.tar.gz %define debug_package %{nil} %prep %autosetup %description Slim Toolkit helps you make your containers better, smaller, and secure %ifarch x86_64 %define goarch amd64 %endif %ifarch aarch64 %define goarch arm64 %endif %ifarch arm %define goarch arm %endif %global slim_version %(git describe --tags --always) %global slim_revision %(git rev-parse HEAD) %global slim_buildtime %(date '+%Y-%m-%d_%I:%M:%''S') %global slim_ldflags -s -w -X github.com/docker-slim/docker-slim/pkg/version.appVersionTag=%{slim_version} -X github.com/docker-slim/docker-slim/pkg/version.appVersionRev=%{slim_revision} -X github.com/docker-slim/docker-slim/pkg/version.appVersionTime=%{slim_buildtime} %build export CGO_ENABLED=0 go generate github.com/docker-slim/docker-slim/pkg/appbom mkdir dist_linux GOOS=linux GOARCH=%{goarch} go build -mod=vendor -trimpath -ldflags="%{slim_ldflags}" -a -tags 'netgo osusergo' -o "dist_linux/" ./cmd/slim/... GOOS=linux GOARCH=%{goarch} go build -mod=vendor -trimpath -ldflags="%{slim_ldflags}" -a -tags 'netgo osusergo' -o "dist_linux/" ./cmd/slim-sensor/... %install install -d -m 755 %{buildroot}%{_bindir} install -d -m 755 %{buildroot}%{_bindir} install -d -m 755 %{buildroot}/usr/share/doc/slim/ install -d -m 755 %{buildroot}/usr/share/licenses/slim/ install -m 755 dist_linux/%{name} %{buildroot}%{_bindir} install -m 755 dist_linux/%{name}-sensor %{buildroot}%{_bindir} install -m 644 README.md %{buildroot}/usr/share/doc/slim/README.md install -m 644 LICENSE %{buildroot}/usr/share/licenses/slim/LICENSE %post %{__ln_s} -f %{_bindir}/%{name} %{_bindir}/docker-slim chmod a+x %{_bindir}/%{name} chmod a+x %{_bindir}/%{name}-sensor %files %{_bindir}/%{name} %{_bindir}/%{name}-sensor %doc /usr/share/doc/slim/README.md %license /usr/share/licenses/slim/LICENSE ================================================ FILE: cmd/slim/main.go ================================================ package main import ( "os" "github.com/slimtoolkit/slim/pkg/app/master" ) func main() { if len(os.Args) > 1 && os.Args[1] == "slim" { //hack to handle plugin invocations os.Args = append([]string{os.Args[0]}, os.Args[2:]...) } app.Run() } ================================================ FILE: cmd/slim-sensor/main.go ================================================ package main import ( "github.com/slimtoolkit/slim/pkg/app/sensor" ) func main() { sensor.Run() } ================================================ FILE: examples/README.md ================================================ # Examples The containerized application examples have been moved to a separate repo: https://github.com/slimtoolkit/examples The examples cover many different base images and programming languages (and language frameworks). Pick one that looks close enough to your app and use it as a starting point if you want to minify your application container image. ================================================ FILE: examples/k8s_nginx_cgr/manifest.yaml ================================================ apiVersion: v1 kind: Pod metadata: name: example-pod labels: name: nginx spec: containers: - name: example-container image: cgr.dev/chainguard/nginx:latest ports: - containerPort: 8080 hostPort: 8080 ================================================ FILE: go.mod ================================================ module github.com/slimtoolkit/slim go 1.24.0 require ( github.com/armon/go-radix v1.0.0 github.com/bmatcuk/doublestar v1.3.4 github.com/bmatcuk/doublestar/v3 v3.0.0 github.com/c-bata/go-prompt v0.2.3 github.com/c4milo/unpackit v0.0.0-20170704181138-4ed373e9ef1c github.com/cespare/xxhash/v2 v2.2.0 github.com/compose-spec/compose-go v0.0.0-20210916141509-a7e1bc322970 github.com/docker/docker v25.0.6+incompatible github.com/docker/go-connections v0.4.0 github.com/dustin/go-humanize v1.0.0 github.com/fatih/color v1.13.0 github.com/fsouza/go-dockerclient v1.10.0 github.com/getkin/kin-openapi v0.131.0 github.com/ghodss/yaml v1.0.0 github.com/gocolly/colly/v2 v2.1.0 github.com/google/go-containerregistry v0.19.0 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/gorilla/websocket v1.4.2 github.com/jedib0t/go-pretty/v6 v6.4.9 github.com/moby/term v0.5.0 github.com/opencontainers/image-spec v1.1.0-rc5 github.com/pkg/errors v0.9.1 github.com/segmentio/ksuid v1.0.4 github.com/sirupsen/logrus v1.9.3 github.com/slimtoolkit/go-update v0.0.0-20231119011834-99945ebd76f7 github.com/slimtoolkit/uiprogress v0.0.0-20231119012247-4a052fb12f37 github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 github.com/urfave/cli/v2 v2.27.1 golang.org/x/crypto v0.45.0 golang.org/x/net v0.47.0 golang.org/x/sys v0.38.0 k8s.io/api v0.27.3 k8s.io/apimachinery v0.27.3 k8s.io/cli-runtime v0.27.3 k8s.io/client-go v0.27.3 ) require ( github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 // indirect github.com/containerd/log v0.1.0 // indirect github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e // indirect github.com/distribution/reference v0.6.0 // indirect github.com/docker/cli v24.0.0+incompatible // indirect github.com/docker/distribution v2.8.2+incompatible // indirect github.com/docker/docker-credential-helpers v0.7.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/emicklei/go-restful/v3 v3.10.1 // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/felixge/httpsnoop v1.0.3 // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.20.1 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/gobwas/glob v0.2.3 // 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.3 // indirect github.com/google/btree v1.0.1 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/gosuri/uilive v0.0.4 // indirect github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect github.com/hooklift/assert v0.1.0 // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kennygrant/sanitize v1.2.4 // indirect github.com/klauspost/compress v1.17.3 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-shellwords v1.0.12 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/moby/spdystream v0.2.0 // indirect github.com/moby/sys/user v0.2.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect github.com/slimtoolkit/uilive v0.0.2 // indirect github.com/spf13/cobra v1.7.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/temoto/robotstxt v1.1.2 // indirect github.com/ulyssessouza/godotenv v1.3.1-0.20210806120901-e417b721114e // indirect github.com/vbatts/tar-split v0.11.3 // indirect github.com/xlab/treeprint v1.1.0 // indirect github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 // indirect go.opentelemetry.io/otel v1.19.0 // indirect go.opentelemetry.io/otel/metric v1.19.0 // indirect go.opentelemetry.io/otel/trace v1.19.0 // indirect go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect golang.org/x/mod v0.29.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect golang.org/x/sync v0.18.0 // indirect golang.org/x/term v0.37.0 // indirect golang.org/x/text v0.31.0 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect golang.org/x/tools v0.38.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.33.0 // 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 sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/kustomize/api v0.13.2 // indirect sigs.k8s.io/kustomize/kyaml v0.14.1 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) require ( github.com/PuerkitoBio/goquery v1.8.1 // indirect github.com/andybalholm/cascadia v1.3.2 // indirect github.com/antchfx/htmlquery v1.3.0 // indirect github.com/antchfx/xmlquery v1.3.17 // indirect github.com/antchfx/xpath v1.2.4 // indirect github.com/containerd/containerd v1.7.11 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dsnet/compress v0.0.1 // indirect github.com/google/uuid v1.3.0 github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/pgzip v1.2.4 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mattn/go-tty v0.0.3 // indirect github.com/mitchellh/mapstructure v1.4.3 // indirect github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/sys/sequential v0.5.0 // indirect github.com/pkg/term v0.0.0-20200520122047-c3ffed290a03 // indirect github.com/stretchr/testify v1.9.0 github.com/ulikunitz/xz v0.5.7 // indirect k8s.io/klog/v2 v2.90.1 // indirect k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 // indirect ) replace github.com/compose-spec/compose-go => ./pkg/third_party/compose-go ================================================ FILE: go.sum ================================================ 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= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8= github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY= github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= github.com/antchfx/htmlquery v1.2.3/go.mod h1:B0ABL+F5irhhMWg54ymEZinzMSi0Kt3I2if0BLYa3V0= github.com/antchfx/htmlquery v1.3.0 h1:5I5yNFOVI+egyia5F2s/5Do2nFWxJz41Tr3DyfKD25E= github.com/antchfx/htmlquery v1.3.0/go.mod h1:zKPDVTMhfOmcwxheXUsx4rKJy8KEY/PU6eXr/2SebQ8= github.com/antchfx/xmlquery v1.2.4/go.mod h1:KQQuESaxSlqugE2ZBcM/qn+ebIpt+d+4Xx7YcSGAIrM= github.com/antchfx/xmlquery v1.3.17 h1:d0qWjPp/D+vtRw7ivCwT5ApH/3CkQU8JOeo3245PpTk= github.com/antchfx/xmlquery v1.3.17/go.mod h1:Afkq4JIeXut75taLSuI31ISJ/zeq+3jG7TunF7noreA= github.com/antchfx/xpath v1.1.6/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk= github.com/antchfx/xpath v1.1.8/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk= github.com/antchfx/xpath v1.2.3/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= github.com/antchfx/xpath v1.2.4 h1:dW1HB/JxKvGtJ9WyVGJ0sIoEcqftV3SqIstujI+B9XY= github.com/antchfx/xpath v1.2.4/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aws/aws-sdk-go v1.34.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0= github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= github.com/bmatcuk/doublestar/v3 v3.0.0 h1:TQtVPlDnAYwcrVNB2JiGuMc++H5qzWZd9PhkNo5WyHI= github.com/bmatcuk/doublestar/v3 v3.0.0/go.mod h1:6PcTVMw80pCY1RVuoqu3V++99uQB3vsSYKPTd8AWA0k= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 h1:GKTyiRCL6zVf5wWaqKnf+7Qs6GbEPfd4iMOitWzXJx8= github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8/go.mod h1:spo1JLcs67NmW1aVLEgtA8Yy1elc+X8y5SRW1sFW4Og= github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/c-bata/go-prompt v0.2.3 h1:jjCS+QhG/sULBhAaBdjb2PlMRVaKXQgn+4yzaauvs2s= github.com/c-bata/go-prompt v0.2.3/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= github.com/c4milo/unpackit v0.0.0-20170704181138-4ed373e9ef1c h1:aprLqMn7gSPT+vdDSl+/E6NLEuArwD/J7IWd8bJt5lQ= github.com/c4milo/unpackit v0.0.0-20170704181138-4ed373e9ef1c/go.mod h1:Ie6SubJv/NTO9Q0UBH0QCl3Ve50lu9hjbi5YJUw03TE= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/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/containerd/containerd v1.7.11 h1:lfGKw3eU35sjV0aG2eYZTiwFEY1pCzxdzicHP3SZILw= github.com/containerd/containerd v1.7.11/go.mod h1:5UluHxHTX2rdvYuZ5OJTC5m/KJNs0Zs9wVoJm9zf5ZE= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k= github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= 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/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e h1:n81KvOMrLZa+VWHwST7dun9f0G98X3zREHS1ztYzZKU= github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e/go.mod h1:xpWTC2KnJMiDLkoawhsPQcXjvwATEBcbq0xevG2YR9M= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/docker/cli v24.0.0+incompatible h1:0+1VshNwBQzQAx9lOl+OYCTCEAD8fKs/qeXMx3O0wqM= github.com/docker/cli v24.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v25.0.6+incompatible h1:5cPwbwriIcsua2REJe8HqQV+6WlWc1byg2QSXzBxBGg= github.com/docker/docker v25.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q= github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ= github.com/emicklei/go-restful/v3 v3.10.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsouza/go-dockerclient v1.10.0 h1:ppSBsbR60I1DFbV4Ag7LlHlHakHFRNLk9XakATW1yVQ= github.com/fsouza/go-dockerclient v1.10.0/go.mod h1:+iNzAW78AzClIBTZ6WFjkaMvOgz68GyCJ236b1opLTs= github.com/getkin/kin-openapi v0.131.0 h1:NO2UeHnFKRYhZ8wg6Nyh5Cq7dHk4suQQr72a4pMrDxE= github.com/getkin/kin-openapi v0.131.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gocolly/colly v1.2.0/go.mod h1:Hof5T3ZswNVsOHYmba1u03W65HDWgpV5HifSuueE0EA= github.com/gocolly/colly/v2 v2.1.0 h1:k0DuZkDoCsx51bKpRJNEmcxcp+W5N8ziuwGaSDuFoGs= github.com/gocolly/colly/v2 v2.1.0/go.mod h1:I2MuhsLjQ+Ex+IzK3afNS8/1qP3AedHOusRPcRdC5o0= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 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-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/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.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.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= 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.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-containerregistry v0.19.0 h1:uIsMRBV7m/HDkDxE/nXMnv1q+lOOSPlQ/ywc5JbB8Ic= github.com/google/go-containerregistry v0.19.0/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ= github.com/google/gofuzz v1.0.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/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= 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/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gosuri/uilive v0.0.4 h1:hUEBpQDj8D8jXgtCdBu7sWsy5sbW/5GhuO8KBwJ2jyY= github.com/gosuri/uilive v0.0.4/go.mod h1:V/epo5LjjlDE5RJUcqx8dbw+zc93y5Ya3yg8tfZ74VI= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/hooklift/assert v0.1.0 h1:UZzFxx5dSb9aBtvMHTtnPuvFnBvcEhHTPb9+0+jpEjs= github.com/hooklift/assert v0.1.0/go.mod h1:pfexfvIHnKCdjh6CkkIZv5ic6dQ6aU2jhKghBlXuwwY= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= 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/jawher/mow.cli v1.1.0/go.mod h1:aNaQlc7ozF3vw6IJ2dHjp2ZFiA4ozMIYY6PyuRJwlUg= github.com/jedib0t/go-pretty/v6 v6.4.9 h1:vZ6bjGg2eBSrJn365qlxGcaWu09Id+LHtrfDWlB2Usc= github.com/jedib0t/go-pretty/v6 v6.4.9/go.mod h1:Ndk3ase2CkQbXLLNf5QDHoYb6J9WtVfmHZu9n8rk2xs= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= 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.7/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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kennygrant/sanitize v1.2.4 h1:gN25/otpP5vAsO2djbMhF/LQX6R7+O1TB4yv8NzpJ3o= github.com/kennygrant/sanitize v1.2.4/go.mod h1:LGsjYYtgxbetdg5owWB2mpgUL6e2nfw2eObZ0u0qvak= 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/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.17.3 h1:qkRjuerhUU1EmXLYGkSH6EZL+vPSxIrYjLNAK4slzwA= github.com/klauspost/compress v1.17.3/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/pgzip v1.2.4 h1:TQ7CNpYKovDOmqzRHKxJh0BeaBI7UdQZYc6p7pMQh1A= github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 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/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 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/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-tty v0.0.3 h1:5OfyWorkyO7xP52Mq7tB36ajHDG5OHrmBGIS/DtakQI= github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 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/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= github.com/moby/sys/user v0.2.0 h1:OnpapJsRp25vkhw8TFG6OLJODNh/3rEwRWtJ3kakwRM= github.com/moby/sys/user v0.2.0/go.mod h1:RYstrcWOJpVh+6qzUqp2bU3eaRpdiQeKGlKitaH0PM8= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= 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/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= 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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY= github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw= github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c= github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk= github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo= github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= github.com/pkg/term v0.0.0-20200520122047-c3ffed290a03 h1:pd4YKIqCB0U7O2I4gWHgEUA2mCEOENmco0l/bM957bU= github.com/pkg/term v0.0.0-20200520122047-c3ffed290a03/go.mod h1:Z9+Ul5bCbBKnbCvdOWbLqTHhJiYV414CURZJba6L8qA= 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/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA= github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c= github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/slimtoolkit/go-update v0.0.0-20231119011834-99945ebd76f7 h1:uM8XWZNInZ8e7o9p9YrQLwoazAiBNhHzElPuUVMSYaQ= github.com/slimtoolkit/go-update v0.0.0-20231119011834-99945ebd76f7/go.mod h1:3uSkRs/BunnCLdBx0k3cyWDPAnYNFnDaioawCN3g9NA= github.com/slimtoolkit/uilive v0.0.2 h1:JZiBu3VMea13z6PLuuOfH/At32HUnfCqDjSWD0pfJHA= github.com/slimtoolkit/uilive v0.0.2/go.mod h1:r9dTgnFgOfeEYT+gQwP8rLGQXBZbT/K7IgjWckJiaxc= github.com/slimtoolkit/uiprogress v0.0.0-20231119012247-4a052fb12f37 h1:ai8Sj4lIpZURxtXhMYQiRaOxTR1+VvPAXDGpz2aJfXE= github.com/slimtoolkit/uiprogress v0.0.0-20231119012247-4a052fb12f37/go.mod h1:8mt8XhLNN4ZywNc2bYadbIApGVkDhgkhCdMnZYgTYJU= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 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/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.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 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.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/temoto/robotstxt v1.1.1/go.mod h1:+1AmkuG3IYkh1kv0d2qEB9Le88ehNO0zwOr3ujewlOo= github.com/temoto/robotstxt v1.1.2 h1:W2pOjSJ6SWvldyEuiFXNxz3xZ8aiWX5LbfDiOFd7Fxg= github.com/temoto/robotstxt v1.1.2/go.mod h1:+1AmkuG3IYkh1kv0d2qEB9Le88ehNO0zwOr3ujewlOo= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/ulikunitz/xz v0.5.7 h1:YvTNdFzX6+W5m9msiYg/zpkSURPPtOlzbqYjrFn7Yt4= github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulyssessouza/godotenv v1.3.1-0.20210806120901-e417b721114e h1:byEYm3QADv5mDUesYKstWwRodf2RoxxC/YuGOxtdqJw= github.com/ulyssessouza/godotenv v1.3.1-0.20210806120901-e417b721114e/go.mod h1:9JN/BuU6Agy5aHyEoA5EIPkBsYbk0+2R42zJgYi/SlI= github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RVck= github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e h1:+SOyEddqYF09QP7vr7CgJ1eti3pY9Fn3LHO1M1r/0sI= github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZx8cOF0+Kkazoc7lwUNMGy0LrzEMxTm4BbTxg= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0/go.mod h1:62CPTSry9QZtOaSsE3tOzhx6LzDhHnXJ6xHeMNNiM6Q= go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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-20181114220301-adae6a3d119a/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-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/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-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= 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-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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/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-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.3.0/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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= 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-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-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= 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.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/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 h1:Z0hjGZePRE0ZBWotvtrwxFNrNE9CUAGtplaDK5NNI/g= google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw= google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U= google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= 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.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.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 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.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/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= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.27.3 h1:yR6oQXXnUEBWEWcvPWS0jQL575KoAboQPfJAuKNrw5Y= k8s.io/api v0.27.3/go.mod h1:C4BNvZnQOF7JA/0Xed2S+aUyJSfTGkGFxLXz9MnpIpg= k8s.io/apimachinery v0.27.3 h1:Ubye8oBufD04l9QnNtW05idcOe9Z3GQN8+7PqmuVcUM= k8s.io/apimachinery v0.27.3/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= k8s.io/cli-runtime v0.27.3 h1:h592I+2eJfXj/4jVYM+tu9Rv8FEc/dyCoD80UJlMW2Y= k8s.io/cli-runtime v0.27.3/go.mod h1:LzXud3vFFuDFXn2LIrWnscPgUiEj7gQQcYZE2UPn9Kw= k8s.io/client-go v0.27.3 h1:7dnEGHZEJld3lYwxvLl7WoehK6lAq7GvgjxpA3nv1E8= k8s.io/client-go v0.27.3/go.mod h1:2MBEKuTo6V1lbKy3z1euEGnhPfGZLKTS9tiJ2xodM48= k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 h1:kmDqav+P+/5e1i9tFfHq1qcF3sOrDp+YEkVDAHu7Jwk= k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/kustomize/api v0.13.2 h1:kejWfLeJhUsTGioDoFNJET5LQe/ajzXhJGYoU+pJsiA= sigs.k8s.io/kustomize/api v0.13.2/go.mod h1:DUp325VVMFVcQSq+ZxyDisA8wtldwHxLZbr1g94UHsw= sigs.k8s.io/kustomize/kyaml v0.14.1 h1:c8iibius7l24G2wVAGZn/Va2wNys03GXLjYVIcFVxKA= sigs.k8s.io/kustomize/kyaml v0.14.1/go.mod h1:AN1/IpawKilWD7V+YvQwRGUvuUOOWpjsHu6uHwonSF4= sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= ================================================ FILE: pkg/acounter/acounter.go ================================================ package acounter import ( "sync/atomic" ) type Type struct { val uint64 } func (self *Type) Value() uint64 { return atomic.LoadUint64(&self.val) } func (self *Type) Inc() uint64 { return self.Add(1) } func (self *Type) Add(val uint64) uint64 { return atomic.AddUint64(&self.val, val) } ================================================ FILE: pkg/aflag/aflag.go ================================================ package aflag import ( "sync/atomic" ) const ( None uint32 = iota Off On ) type Type struct { val uint32 } func (self *Type) Value() uint32 { return atomic.LoadUint32(&self.val) } func (self *Type) On() { self.Set(On) } func (self *Type) Off() { self.Set(Off) } func (self *Type) Set(val uint32) { atomic.StoreUint32(&self.val, val) } func (self *Type) IsOn() bool { return self.Is(On) } func (self *Type) IsOff() bool { return self.Is(Off) } func (self *Type) IsNone() bool { return self.Is(None) } func (self *Type) Is(val uint32) bool { return atomic.LoadUint32(&self.val) == val } func (self *Type) Has(val uint32) bool { return (atomic.LoadUint32(&self.val) & val) == val } ================================================ FILE: pkg/app/constants.go ================================================ package app const ( DefaultArtifactsDirPath = "/opt/_slim/artifacts" ArtifactFilesDirName = "files" ) ================================================ FILE: pkg/app/execontext.go ================================================ package app import ( "encoding/json" "fmt" "os" "runtime/debug" "strings" "github.com/fatih/color" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/consts" "github.com/slimtoolkit/slim/pkg/util/fsutil" v "github.com/slimtoolkit/slim/pkg/version" ) const ( ofJSON = "json" ofText = "text" ) type ExecutionContext struct { Out *Output cleanupHandlers []func() } func (ref *ExecutionContext) Exit(exitCode int) { ref.doCleanup() ref.exit(exitCode) } func (ref *ExecutionContext) AddCleanupHandler(handler func()) { if handler != nil { ref.cleanupHandlers = append(ref.cleanupHandlers, handler) } } func (ref *ExecutionContext) doCleanup() { if len(ref.cleanupHandlers) == 0 { return } //call cleanup handlers in reverse order for i := len(ref.cleanupHandlers) - 1; i >= 0; i-- { cleanup := ref.cleanupHandlers[i] if cleanup != nil { cleanup() } } } func (ref *ExecutionContext) FailOn(err error) { if err != nil { ref.doCleanup() //not using FailOn from errutil to control the flow/output better stackData := debug.Stack() log.WithError(err).WithFields(log.Fields{ "stack": string(stackData), }).Error("terminating") if ref.Out != nil { ref.Out.Info("fail.on", OutVars{"version": v.Current()}) } ref.exit(-1) } } func (ref *ExecutionContext) Fail(reason string) { ref.doCleanup() //not using FailOn from errutil to control the flow/output better stackData := debug.Stack() log.WithFields(log.Fields{ "stack": string(stackData), "reason": reason, }).Error("terminating") if ref.Out != nil { ref.Out.Info("fail.on", OutVars{"version": v.Current()}) } ShowCommunityInfo(ref.Out.OutputFormat) ref.exit(-1) } func (ref *ExecutionContext) exit(exitCode int) { if ref.Out != nil { ref.Out.Info("exit", OutVars{ "code": exitCode, "version": v.Current(), "location": fsutil.ExeDir()}) } ShowCommunityInfo(ref.Out.OutputFormat) os.Exit(exitCode) } func NewExecutionContext( cmdName string, quiet bool, outputFormat string) *ExecutionContext { ref := &ExecutionContext{ Out: NewOutput(cmdName, quiet, outputFormat), } return ref } type Output struct { CmdName string Quiet bool OutputFormat string } func NewOutput(cmdName string, quiet bool, outputFormat string) *Output { ref := &Output{ CmdName: cmdName, Quiet: quiet, OutputFormat: outputFormat, } return ref } func NoColor() { color.NoColor = true } type OutVars map[string]interface{} func (ref *Output) LogDump(logType string, data string, params ...OutVars) { if ref.Quiet { return } var info string msg := map[string]string{} var jsonData []byte msg["cmd"] = ref.CmdName msg["log"] = logType msg["data"] = data if len(params) > 0 { kvSet := params[0] if len(kvSet) > 0 { var builder strings.Builder for k, v := range kvSet { msg[k] = fmt.Sprintf("%v", v) builder.WriteString(kcolor(k)) builder.WriteString("=") builder.WriteString(fmt.Sprintf("'%s'", vcolor("%v", v))) builder.WriteString(" ") } info = builder.String() } } switch ref.OutputFormat { case ofJSON: jsonData, _ = json.Marshal(msg) fmt.Println(string(jsonData)) case ofText: fmt.Printf("cmd=%s log='%s' event=LOG.START %s ====================\n", ref.CmdName, logType, info) fmt.Println(data) fmt.Printf("cmd=%s log='%s' event=LOG.END %s ====================\n", ref.CmdName, logType, info) default: log.Fatalf("Unknown console output flag: %s\n. It should be either 'text' or 'json", ref.OutputFormat) } } func (ref *Output) Prompt(data string) { if ref.Quiet { return } switch ref.OutputFormat { case ofJSON: //marshal data to json var jsonData []byte if len(data) > 0 { msg := map[string]string{ "cmd": ref.CmdName, "prompt": data, } jsonData, _ = json.Marshal(msg) fmt.Println(string(jsonData)) } case ofText: color.Set(color.FgHiRed) defer color.Unset() fmt.Printf("cmd=%s prompt='%s'\n", ref.CmdName, data) default: log.Fatalf("Unknown console output flag: %s\n. It should be either 'text' or 'json", ref.OutputFormat) } } func (ref *Output) Error(errType string, data string) { if ref.Quiet { return } switch ref.OutputFormat { case ofJSON: //marshal data to json var jsonData []byte if len(data) > 0 { msg := map[string]string{ "cmd": ref.CmdName, "error": errType, "message": data, } jsonData, _ = json.Marshal(msg) fmt.Println(string(jsonData)) } case ofText: color.Set(color.FgHiRed) defer color.Unset() fmt.Printf("cmd=%s error=%s message='%s'\n", ref.CmdName, errType, data) default: log.Fatalf("Unknown console output flag: %s\n. It should be either 'text' or 'json", ref.OutputFormat) } } func (ref *Output) Message(data string) { if ref.Quiet { return } switch ref.OutputFormat { case ofJSON: //marshal data to json var jsonData []byte if len(data) > 0 { msg := map[string]string{ "cmd": ref.CmdName, "message": data, } jsonData, _ = json.Marshal(msg) fmt.Println(string(jsonData)) } case ofText: color.Set(color.FgHiMagenta) defer color.Unset() fmt.Printf("cmd=%s message='%s'\n", ref.CmdName, data) default: log.Fatalf("Unknown console output flag: %s\n. It should be either 'text' or 'json", ref.OutputFormat) } } func (ref *Output) State(state string, params ...OutVars) { if ref.Quiet { return } var exitInfo string var info string var sep string msg := map[string]string{} var jsonData []byte msg["cmd"] = ref.CmdName msg["state"] = state if len(params) > 0 { var minCount int kvSet := params[0] if exitCode, ok := kvSet["exit.code"]; ok { minCount = 1 exitInfo = fmt.Sprintf(" code=%d", exitCode) } if len(kvSet) > minCount { var builder strings.Builder sep = " " for k, v := range kvSet { if k == "exit.code" { continue } msg["exit.info"] = exitInfo msg[k] = fmt.Sprintf("%v", v) builder.WriteString(k) builder.WriteString("=") val := fmt.Sprintf("%v", v) if strings.Contains(val, " ") && !strings.HasPrefix(val, `"`) { val = fmt.Sprintf("\"%s\"", val) } builder.WriteString(val) builder.WriteString(" ") } info = builder.String() } } switch ref.OutputFormat { case ofJSON: jsonData, _ = json.Marshal(msg) fmt.Println(string(jsonData)) case ofText: if state == "exited" || strings.Contains(state, "error") { color.Set(color.FgHiRed, color.Bold) } else { color.Set(color.FgCyan, color.Bold) } defer color.Unset() fmt.Printf("cmd=%s state=%s%s%s%s\n", ref.CmdName, state, exitInfo, sep, info) default: log.Fatalf("Unknown console output flag: %s\n. It should be either 'text' or 'json", ref.OutputFormat) } } var ( itcolor = color.New(color.FgMagenta, color.Bold).SprintFunc() kcolor = color.New(color.FgHiGreen, color.Bold).SprintFunc() vcolor = color.New(color.FgHiBlue).SprintfFunc() ) func (ref *Output) Info(infoType string, params ...OutVars) { if ref.Quiet { return } var data string var sep string msg := map[string]string{} var jsonData []byte msg["cmd"] = ref.CmdName msg["info"] = infoType if len(params) > 0 { kvSet := params[0] if len(kvSet) > 0 { var builder strings.Builder sep = " " for k, v := range kvSet { msg[k] = fmt.Sprintf("%v", v) builder.WriteString(kcolor(k)) builder.WriteString("=") builder.WriteString(fmt.Sprintf("'%s'", vcolor("%v", v))) builder.WriteString(" ") } data = builder.String() } } switch ref.OutputFormat { case ofJSON: jsonData, _ = json.Marshal(msg) fmt.Println(string(jsonData)) case ofText: fmt.Printf("cmd=%s info=%s%s%s\n", ref.CmdName, itcolor(infoType), sep, data) default: log.Fatalf("Unknown console output flag: %s\n. It should be either 'text' or 'json", ref.OutputFormat) } } func ShowCommunityInfo(outputFormat string) { lines := []struct { App string `json:"app"` Message string `json:"message"` Info string `json:"info"` }{ { App: consts.AppName, Message: "GitHub Discussions", Info: consts.CommunityDiscussions, }, { App: consts.AppName, Message: "Join the CNCF Slack channel to ask questions or to share your feedback", Info: consts.CommunityCNCFSlack, }, { App: consts.AppName, Message: "Join the Discord server to ask questions or to share your feedback", Info: consts.CommunityDiscord, }, { App: consts.AppName, Message: "Join the Gitter channel to ask questions or to share your feedback", Info: consts.CommunityGitter, }, } switch outputFormat { case ofJSON: for _, v := range lines { jsonData, _ := json.Marshal(v) fmt.Println(string(jsonData)) } default: color.Set(color.FgHiMagenta) defer color.Unset() for _, v := range lines { fmt.Printf("app='%s' message='%s' info='%s'\n", v.App, v.Message, v.Info) } } } ================================================ FILE: pkg/app/master/app.go ================================================ package app import ( "os" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app/master/signals" ) // Run starts the master app func Run() { signals.InitHandlers() cli := newCLI() if err := cli.Run(os.Args); err != nil { log.Fatal(err) } } ================================================ FILE: pkg/app/master/builder/image_builder.go ================================================ package builder import ( "bytes" "errors" "fmt" "path/filepath" "strings" "github.com/slimtoolkit/slim/pkg/app/master/config" "github.com/slimtoolkit/slim/pkg/consts" "github.com/slimtoolkit/slim/pkg/docker/dockerfile" "github.com/slimtoolkit/slim/pkg/util/fsutil" docker "github.com/fsouza/go-dockerclient" log "github.com/sirupsen/logrus" ) //todo: move/refactor this to be a "pkg/imagebuilder" engine var ( ErrInvalidContextDir = errors.New("invalid context directory") ) // BasicImageBuilder creates regular container images type BasicImageBuilder struct { ShowBuildLogs bool BuildOptions docker.BuildImageOptions APIClient *docker.Client BuildLog bytes.Buffer } // ImageBuilder creates new optimized container images type ImageBuilder struct { BasicImageBuilder RepoName string AdditionalTags []string ID string Entrypoint []string Cmd []string WorkingDir string Env []string Labels map[string]string ExposedPorts map[docker.Port]struct{} Volumes map[string]struct{} OnBuild []string User string HasData bool TarData bool } const ( dsCmdPortInfo = "65501/tcp" dsEvtPortInfo = "65502/tcp" ) // NewBasicImageBuilder creates a new BasicImageBuilder instances func NewBasicImageBuilder(client *docker.Client, //imageRepoNameTag string, //dockerfileName string, cbOpts *config.ContainerBuildOptions, buildContext string, showBuildLogs bool) (*BasicImageBuilder, error) { var buildArgs []docker.BuildArg for _, ba := range cbOpts.BuildArgs { buildArgs = append(buildArgs, docker.BuildArg{Name: ba.Name, Value: ba.Value}) } labels := map[string]string{} //cleanup non-standard labels from buildpacks for k, v := range cbOpts.Labels { lineLen := len(k) + len(v) + 7 if lineLen > 65535 { //TODO: improve JSON data splitting valueLen := len(v) parts := valueLen / 50000 parts++ offset := 0 for i := 0; i < parts && offset < valueLen; i++ { chunkSize := 50000 if (offset + chunkSize) > valueLen { chunkSize = valueLen - offset } value := v[offset:(offset + chunkSize)] offset += chunkSize key := fmt.Sprintf("%s.%d", k, i) labels[key] = value } } else { labels[k] = v } } builder := BasicImageBuilder{ ShowBuildLogs: showBuildLogs, BuildOptions: docker.BuildImageOptions{ Name: cbOpts.Tag, Dockerfile: cbOpts.Dockerfile, Target: cbOpts.Target, NetworkMode: cbOpts.NetworkMode, ExtraHosts: cbOpts.ExtraHosts, CacheFrom: cbOpts.CacheFrom, Labels: labels, BuildArgs: buildArgs, RmTmpContainer: true, }, APIClient: client, } if strings.HasPrefix(buildContext, "http://") || strings.HasPrefix(buildContext, "https://") { builder.BuildOptions.Remote = buildContext } else { if exists := fsutil.DirExists(buildContext); exists { builder.BuildOptions.ContextDir = buildContext fullDockerfileName := filepath.Join(buildContext, cbOpts.Dockerfile) if !fsutil.Exists(fullDockerfileName) || !fsutil.IsRegularFile(fullDockerfileName) { return nil, fmt.Errorf("invalid dockerfile reference - %s", fullDockerfileName) } } else { return nil, ErrInvalidContextDir } } builder.BuildOptions.OutputStream = &builder.BuildLog return &builder, nil } // Build creates a new container image func (b *BasicImageBuilder) Build() error { return b.APIClient.BuildImage(b.BuildOptions) } // Remove deletes the configured container image func (b *BasicImageBuilder) Remove() error { return nil } // NewImageBuilder creates a new ImageBuilder instances func NewImageBuilder( client *docker.Client, imageRepoNameTag string, additionalTags []string, imageInfo *docker.Image, artifactLocation string, showBuildLogs bool, overrideSelectors map[string]bool, overrides *config.ContainerOverrides, instructions *config.ImageNewInstructions, sourceImage string) (*ImageBuilder, error) { labels := map[string]string{} if imageInfo.Config.Labels != nil { //cleanup non-standard labels from buildpacks for k, v := range imageInfo.Config.Labels { lineLen := len(k) + len(v) + 7 if lineLen > 65535 { //TODO: improve JSON data splitting valueLen := len(v) parts := valueLen / 50000 parts++ offset := 0 for i := 0; i < parts && offset < valueLen; i++ { chunkSize := 50000 if (offset + chunkSize) > valueLen { chunkSize = valueLen - offset } value := v[offset:(offset + chunkSize)] offset += chunkSize key := fmt.Sprintf("%s.%d", k, i) labels[key] = value } } else { labels[k] = v } } } var platform string if imageInfo != nil && imageInfo.OS != "" && imageInfo.Architecture != "" { platform = fmt.Sprintf("%s/%s", imageInfo.OS, imageInfo.Architecture) } // omitempty will remove platform if empty on marshalled request to engine builder := &ImageBuilder{ BasicImageBuilder: BasicImageBuilder{ ShowBuildLogs: showBuildLogs, APIClient: client, // extract platform - from image inspector BuildOptions: docker.BuildImageOptions{ Name: imageRepoNameTag, RmTmpContainer: true, ContextDir: artifactLocation, Dockerfile: "Dockerfile", Platform: platform, //SuppressOutput: true, }, }, RepoName: imageRepoNameTag, AdditionalTags: additionalTags, ID: imageInfo.ID, Entrypoint: imageInfo.Config.Entrypoint, Cmd: imageInfo.Config.Cmd, WorkingDir: imageInfo.Config.WorkingDir, Env: imageInfo.Config.Env, Labels: labels, ExposedPorts: imageInfo.Config.ExposedPorts, Volumes: imageInfo.Config.Volumes, OnBuild: imageInfo.Config.OnBuild, User: imageInfo.Config.User, } if builder.ExposedPorts == nil { builder.ExposedPorts = map[docker.Port]struct{}{} } if builder.Volumes == nil { builder.Volumes = map[string]struct{}{} } if builder.Labels == nil { builder.Labels = map[string]string{} } if overrides != nil && len(overrideSelectors) > 0 { log.Debugf("NewImageBuilder: Using container runtime overrides => %+v", overrideSelectors) for k := range overrideSelectors { switch k { case "entrypoint": if len(overrides.Entrypoint) > 0 { builder.Entrypoint = overrides.Entrypoint } case "cmd": if len(overrides.Cmd) > 0 { builder.Cmd = overrides.Cmd } case "workdir": if overrides.Workdir != "" { builder.WorkingDir = overrides.Workdir } case "env": if len(overrides.Env) > 0 { builder.Env = append(builder.Env, overrides.Env...) } case "label": for k, v := range overrides.Labels { builder.Labels[k] = v } case "volume": for k, v := range overrides.Volumes { builder.Volumes[k] = v } case "expose": dsCmdPort := docker.Port(dsCmdPortInfo) dsEvtPort := docker.Port(dsEvtPortInfo) for k, v := range overrides.ExposedPorts { if k == dsCmdPort || k == dsEvtPort { continue } builder.ExposedPorts[k] = v } } } } //instructions have higher value precedence over the runtime overrides if instructions != nil { log.Debugf("NewImageBuilder: Using new image instructions => %+v", instructions) if instructions.Workdir != "" { builder.WorkingDir = instructions.Workdir } if len(instructions.Env) > 0 { builder.Env = append(builder.Env, instructions.Env...) } for k, v := range instructions.ExposedPorts { builder.ExposedPorts[k] = v } for k, v := range instructions.Volumes { builder.Volumes[k] = v } for k, v := range instructions.Labels { builder.Labels[k] = v } if len(instructions.Entrypoint) > 0 { builder.Entrypoint = instructions.Entrypoint } if len(instructions.Cmd) > 0 { builder.Cmd = instructions.Cmd } if len(builder.ExposedPorts) > 0 && len(instructions.RemoveExposedPorts) > 0 { for k := range instructions.RemoveExposedPorts { if _, ok := builder.ExposedPorts[k]; ok { delete(builder.ExposedPorts, k) } } } if len(builder.Volumes) > 0 && len(instructions.RemoveVolumes) > 0 { for k := range instructions.RemoveVolumes { if _, ok := builder.Volumes[k]; ok { delete(builder.Volumes, k) } } } if len(builder.Labels) > 0 && len(instructions.RemoveLabels) > 0 { for k := range instructions.RemoveLabels { if _, ok := builder.Labels[k]; ok { delete(builder.Labels, k) } } } if len(instructions.RemoveEnvs) > 0 && len(builder.Env) > 0 { var newEnv []string for _, envPair := range builder.Env { envParts := strings.SplitN(envPair, "=", 2) if len(envParts) > 0 && envParts[0] != "" { if _, ok := instructions.RemoveEnvs[envParts[0]]; !ok { newEnv = append(newEnv, envPair) } } } builder.Env = newEnv } } if sourceImage != "" { builder.Labels[consts.DSLabelSourceImage] = sourceImage } if imageInfo != nil && imageInfo.ID != "" { builder.Labels[consts.DSLabelSourceImageID] = imageInfo.ID } builder.BuildOptions.OutputStream = &builder.BuildLog dataTar := filepath.Join(artifactLocation, "files.tar") builder.TarData = fsutil.IsRegularFile(dataTar) if builder.TarData { builder.HasData = true } else { dataDir := filepath.Join(artifactLocation, "files") builder.HasData = fsutil.IsDir(dataDir) } return builder, nil } // Build creates a new container image func (b *ImageBuilder) Build() error { if err := b.GenerateDockerfile(); err != nil { return err } err := b.APIClient.BuildImage(b.BuildOptions) if err != nil { return err } for _, fullTag := range b.AdditionalTags { fullTag := strings.TrimSpace(fullTag) if len(fullTag) == 0 { log.Debug("ImageBuilder.Build: Skipping empty tag") continue } var options docker.TagImageOptions parts := strings.Split(fullTag, ":") if len(parts) > 2 { log.Debugf("ImageBuilder.Build: Skipping malformed tag - '%s'", fullTag) continue } if len(parts) == 2 { options.Repo = parts[0] options.Tag = parts[1] } else { options.Repo = parts[0] } targetImage := b.BuildOptions.Name if err := b.APIClient.TagImage(targetImage, options); err != nil { //not failing on tagging errors log.Debugf("ImageBuilder.Build: Error tagging image '%s' with tag - '%s' (error - %v)", targetImage, fullTag, err) } } return nil } // GenerateDockerfile creates a Dockerfile file func (b *ImageBuilder) GenerateDockerfile() error { return dockerfile.GenerateFromInfo(b.BuildOptions.ContextDir, b.Volumes, b.WorkingDir, b.Env, b.Labels, b.User, b.ExposedPorts, b.Entrypoint, b.Cmd, b.HasData, b.TarData) } ================================================ FILE: pkg/app/master/cli.go ================================================ package app import ( "fmt" "os" "strings" log "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/app/master/command/appbom" "github.com/slimtoolkit/slim/pkg/app/master/command/build" //"github.com/slimtoolkit/slim/pkg/app/master/command/containerize" //"github.com/slimtoolkit/slim/pkg/app/master/command/convert" "github.com/slimtoolkit/slim/pkg/app/master/command/debug" "github.com/slimtoolkit/slim/pkg/app/master/command/dockerclipm" //"github.com/slimtoolkit/slim/pkg/app/master/command/edit" "github.com/slimtoolkit/slim/pkg/app/master/command/help" "github.com/slimtoolkit/slim/pkg/app/master/command/images" "github.com/slimtoolkit/slim/pkg/app/master/command/install" "github.com/slimtoolkit/slim/pkg/app/master/command/lint" "github.com/slimtoolkit/slim/pkg/app/master/command/merge" "github.com/slimtoolkit/slim/pkg/app/master/command/probe" "github.com/slimtoolkit/slim/pkg/app/master/command/profile" "github.com/slimtoolkit/slim/pkg/app/master/command/registry" "github.com/slimtoolkit/slim/pkg/app/master/command/run" //"github.com/slimtoolkit/slim/pkg/app/master/command/server" "github.com/slimtoolkit/slim/pkg/app/master/command/update" "github.com/slimtoolkit/slim/pkg/app/master/command/version" "github.com/slimtoolkit/slim/pkg/app/master/command/vulnerability" "github.com/slimtoolkit/slim/pkg/app/master/command/xray" "github.com/slimtoolkit/slim/pkg/app/master/config" "github.com/slimtoolkit/slim/pkg/system" "github.com/slimtoolkit/slim/pkg/util/fsutil" v "github.com/slimtoolkit/slim/pkg/version" ) // Main/driver app CLI constants const ( AppName = "slim" AppUsage = "inspect, optimize and debug your containers!" ) func registerCommands() { //registering commands explicitly instead of relying on init() //also get to control the order of the commands in the interactive prompt debug.RegisterCommand() build.RegisterCommand() xray.RegisterCommand() lint.RegisterCommand() merge.RegisterCommand() images.RegisterCommand() registry.RegisterCommand() vulnerability.RegisterCommand() profile.RegisterCommand() version.RegisterCommand() appbom.RegisterCommand() help.RegisterCommand() update.RegisterCommand() install.RegisterCommand() //edit.RegisterCommand() - not doing anything yet probe.RegisterCommand() //convert.RegisterCommand() - not doing anything yet run.RegisterCommand() //server.RegisterCommand() - not doing anything yet //containerize.RegisterCommand() - not doing anything yet dockerclipm.RegisterCommand() } func newCLI() *cli.App { registerCommands() doShowCommunityInfo := true cliApp := cli.NewApp() cliApp.Version = v.Current() cliApp.Name = AppName cliApp.Usage = AppUsage cliApp.CommandNotFound = func(ctx *cli.Context, command string) { fmt.Printf("unknown command - %v \n\n", command) cli.ShowAppHelp(ctx) } cliApp.Flags = command.GlobalFlags() cliApp.Before = func(ctx *cli.Context) error { gparams := command.GlobalFlagValues(ctx) appParams, err := config.NewAppOptionsFromFile(fsutil.ResolveImageStateBasePath(gparams.StatePath)) if err != nil { log.Errorf("config.NewAppOptionsFromFile error - %v", err) return err } gparams = command.UpdateGlobalFlagValues(appParams, gparams) ctx.Context = command.CLIContextSave(ctx.Context, command.GlobalParams, gparams) ctx.Context = command.CLIContextSave(ctx.Context, command.AppParams, appParams) if gparams.NoColor { app.NoColor() } if gparams.Debug { log.SetLevel(log.DebugLevel) } else { if gparams.Verbose { log.SetLevel(log.InfoLevel) } else { logLevel := log.WarnLevel switch gparams.LogLevel { case "trace": logLevel = log.TraceLevel case "debug": logLevel = log.DebugLevel case "info": logLevel = log.InfoLevel case "warn": logLevel = log.WarnLevel case "error": logLevel = log.ErrorLevel case "fatal": logLevel = log.FatalLevel case "panic": logLevel = log.PanicLevel default: log.Fatalf("unknown log-level %q", gparams.LogLevel) } log.SetLevel(logLevel) } } if gparams.Log != "" { f, err := os.Create(gparams.Log) if err != nil { return err } log.SetOutput(f) } switch gparams.LogFormat { case "text": log.SetFormatter(&log.TextFormatter{DisableColors: true}) case "json": log.SetFormatter(new(log.JSONFormatter)) default: log.Fatalf("unknown log-format %q", gparams.LogFormat) } log.Debugf("sysinfo => %#v", system.GetSystemInfo()) //NOTE: not displaying the community info here to reduce noise //tmp hack //if !strings.Contains(strings.Join(os.Args, " "), " docker-cli-plugin-metadata") { // app.ShowCommunityInfo(gparams.OutputFormat) //} return nil } cliApp.After = func(ctx *cli.Context) error { //todo: get already fetched gcvalues from ctx.Context gcvalues := command.GlobalFlagValues(ctx) if gcvalues.QuietCLIMode { return nil } //tmp hack if !strings.Contains(strings.Join(os.Args, " "), " docker-cli-plugin-metadata") { if doShowCommunityInfo { app.ShowCommunityInfo(ctx.String(command.FlagOutputFormat)) } } return nil } cliApp.Action = func(ctx *cli.Context) error { //todo: get already fetched gcvalues from ctx.Context gcvalues := command.GlobalFlagValues(ctx) //disable community info in interactive mode (too noisy) doShowCommunityInfo = false ia := command.NewInteractiveApp(cliApp, gcvalues) ia.Run() return nil } cliApp.Commands = command.GetCommands() return cliApp } ================================================ FILE: pkg/app/master/command/appbom/cli.go ================================================ package appbom import ( "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/urfave/cli/v2" ) const ( Name = "appbom" Usage = "Show application BOM" Alias = "a" ) var CLI = &cli.Command{ Name: Name, Aliases: []string{Alias}, Usage: Usage, Action: func(ctx *cli.Context) error { gcvalues := command.GlobalFlagValues(ctx) xc := app.NewExecutionContext( Name, gcvalues.QuietCLIMode, gcvalues.OutputFormat) OnCommand( xc, gcvalues) return nil }, } ================================================ FILE: pkg/app/master/command/appbom/handler.go ================================================ package appbom import ( //log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/appbom" ) const appName = command.AppName type ovars = app.OutVars // OnCommand implements the 'server' command func OnCommand( xc *app.ExecutionContext, gparams *command.GenericParams) { //logger := log.WithFields(log.Fields{"app": appName, "command": Name}) info := appbom.Get() if info == nil { xc.Out.Error("appbom.info", "missing") xc.Exit(0) } if info.BuilderHash != "" { xc.Out.Info("appbom", ovars{"builder_hash": info.BuilderHash}) } xc.Out.Info("appbom", ovars{"runtime": info.Runtime}) xc.Out.Info("appbom.entrypoint", ovars{ "path": info.Entrypoint.Path, "version": info.Entrypoint.Version, }) if info.SourceControl != nil { xc.Out.Info("appbom.source_control", ovars{ "type": info.SourceControl.Type, "revision": info.SourceControl.Revision, "revision_time": info.SourceControl.RevisionTime, "has_local_changes": info.SourceControl.HasLocalChanges, }) } outputParam := func(param *appbom.ParamInfo, header string) { if param != nil { xc.Out.Info(header, ovars{ "name": param.Name, "type": param.Type, "value": param.Value, }) } } outputParam(info.BuildParams.Os, "appbom.build_params.os") outputParam(info.BuildParams.Arch, "appbom.build_params.arch") outputParam(info.BuildParams.ArchFeature, "appbom.build_params.arch_feature") outputParam(info.BuildParams.BuildMode, "appbom.build_params.build_mode") outputParam(info.BuildParams.Compiler, "appbom.build_params.compiler") outputParam(info.BuildParams.CgoEnabled, "appbom.build_params.cgo_enabled") outputParam(info.BuildParams.CgoCFlags, "appbom.build_params.cgo_cflags") outputParam(info.BuildParams.CgoCppFlags, "appbom.build_params.cgo_cppflags") outputParam(info.BuildParams.CgoCxxFlags, "appbom.build_params.cgo_cxxflags") outputParam(info.BuildParams.CgoLdFlags, "appbom.build_params.cgo_ldflags") if len(info.OtherParams) > 0 { for k, v := range info.OtherParams { xc.Out.Info("appbom.other_params", ovars{"key": k, "value": v}) } } if len(info.Includes) > 0 { for _, v := range info.Includes { vals := ovars{ "name": v.Name, "version": v.Version, "path": v.Path, "hash": v.Hash, } if v.ReplacedBy != "" { vals["replaced_by"] = v.ReplacedBy } xc.Out.Info("appbom.includes", vals) } } } ================================================ FILE: pkg/app/master/command/appbom/init/init.go ================================================ package init import ( "github.com/slimtoolkit/slim/pkg/app/master/command/appbom" ) func init() { appbom.RegisterCommand() } ================================================ FILE: pkg/app/master/command/appbom/prompt.go ================================================ package appbom import ( "github.com/c-bata/go-prompt" ) var CommandSuggestion = prompt.Suggest{ Text: Name, Description: Usage, } ================================================ FILE: pkg/app/master/command/appbom/register.go ================================================ package appbom import ( "github.com/slimtoolkit/slim/pkg/app/master/command" ) func RegisterCommand() { command.AddCLICommand( Name, CLI, CommandSuggestion, nil) } ================================================ FILE: pkg/app/master/command/build/cli.go ================================================ package build import ( "fmt" "os" "strings" log "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/app/master/config" "github.com/slimtoolkit/slim/pkg/artifact" ) const ( Name = "build" Usage = "Analyzes, profiles and optimizes your container image auto-generating Seccomp and AppArmor security profiles" Alias = "b" ) var CLI = &cli.Command{ Name: Name, Aliases: []string{Alias}, Usage: Usage, Flags: append([]cli.Flag{ command.Cflag(command.FlagCommandParamsFile), command.Cflag(command.FlagTarget), command.Cflag(command.FlagPull), command.Cflag(command.FlagDockerConfigPath), command.Cflag(command.FlagRegistryAccount), command.Cflag(command.FlagRegistrySecret), command.Cflag(command.FlagShowPullLogs), command.Cflag(command.FlagComposeFile), command.Cflag(command.FlagTargetComposeSvc), command.Cflag(command.FlagTargetComposeSvcImage), command.Cflag(command.FlagComposeSvcStartWait), command.Cflag(command.FlagComposeSvcNoPorts), command.Cflag(command.FlagDepExcludeComposeSvcAll), command.Cflag(command.FlagDepIncludeComposeSvc), command.Cflag(command.FlagDepExcludeComposeSvc), command.Cflag(command.FlagDepIncludeComposeSvcDeps), command.Cflag(command.FlagDepIncludeTargetComposeSvcDeps), command.Cflag(command.FlagComposeNet), command.Cflag(command.FlagComposeEnvNoHost), command.Cflag(command.FlagComposeEnvFile), command.Cflag(command.FlagComposeProjectName), command.Cflag(command.FlagComposeWorkdir), command.Cflag(command.FlagContainerProbeComposeSvc), command.Cflag(command.FlagHostExec), command.Cflag(command.FlagHostExecFile), command.Cflag(command.FlagTargetKubeWorkload), command.Cflag(command.FlagTargetKubeWorkloadNamespace), command.Cflag(command.FlagTargetKubeWorkloadContainer), command.Cflag(command.FlagTargetKubeWorkloadImage), command.Cflag(command.FlagKubeManifestFile), command.Cflag(command.FlagKubeKubeconfigFile), command.Cflag(command.FlagPublishPort), command.Cflag(command.FlagPublishExposedPorts), command.Cflag(command.FlagRunTargetAsUser), command.Cflag(command.FlagShowContainerLogs), command.Cflag(command.FlagEnableMondelLogs), cflag(FlagShowBuildLogs), command.Cflag(command.FlagCopyMetaArtifacts), command.Cflag(command.FlagRemoveFileArtifacts), command.Cflag(command.FlagExec), command.Cflag(command.FlagExecFile), // cflag(FlagTag), cflag(FlagImageOverrides), //Container Run Options command.Cflag(command.FlagCRORuntime), command.Cflag(command.FlagCROHostConfigFile), command.Cflag(command.FlagCROSysctl), command.Cflag(command.FlagCROShmSize), command.Cflag(command.FlagUser), command.Cflag(command.FlagEntrypoint), command.Cflag(command.FlagCmd), command.Cflag(command.FlagWorkdir), command.Cflag(command.FlagEnv), command.Cflag(command.FlagEnvFile), command.Cflag(command.FlagLabel), command.Cflag(command.FlagVolume), command.Cflag(command.FlagLink), command.Cflag(command.FlagEtcHostsMap), command.Cflag(command.FlagContainerDNS), command.Cflag(command.FlagContainerDNSSearch), command.Cflag(command.FlagNetwork), command.Cflag(command.FlagHostname), command.Cflag(command.FlagExpose), command.Cflag(command.FlagMount), //Container Build Options cflag(FlagImageBuildEngine), cflag(FlagImageBuildArch), cflag(FlagBuildFromDockerfile), cflag(FlagDockerfileContext), cflag(FlagTagFat), cflag(FlagCBOAddHost), cflag(FlagCBOBuildArg), cflag(FlagCBOCacheFrom), cflag(FlagCBOLabel), cflag(FlagCBOTarget), cflag(FlagCBONetwork), cflag(FlagDeleteFatImage), //New/Optimized Build Options cflag(FlagNewEntrypoint), cflag(FlagNewCmd), cflag(FlagNewExpose), cflag(FlagNewWorkdir), cflag(FlagNewEnv), cflag(FlagNewVolume), cflag(FlagNewLabel), cflag(FlagRemoveExpose), cflag(FlagRemoveEnv), cflag(FlagRemoveLabel), cflag(FlagRemoveVolume), cflag(FlagPreservePath), cflag(FlagPreservePathFile), cflag(FlagIncludePath), cflag(FlagIncludePathFile), cflag(FlagIncludeDirBins), cflag(FlagIncludeBin), cflag(FlagIncludeBinFile), cflag(FlagIncludeExeFile), cflag(FlagIncludeExe), cflag(FlagIncludeShell), cflag(FlagIncludeWorkdir), cflag(FlagIncludeAppImageAll), cflag(FlagAppImageStartInstGroup), cflag(FlagAppImageStartInst), cflag(FlagAppImageDockerfile), cflag(FlagIncludePathsCreportFile), cflag(FlagIncludeOSLibsNet), cflag(FlagIncludeSSHClient), cflag(FlagIncludeZoneInfo), cflag(FlagIncludeCertAll), cflag(FlagIncludeCertBundles), cflag(FlagIncludeCertDirs), cflag(FlagIncludeCertPKAll), cflag(FlagIncludeCertPKDirs), cflag(FlagIncludeNew), cflag(FlagKeepTmpArtifacts), cflag(FlagIncludeAppNuxtDir), cflag(FlagIncludeAppNuxtBuildDir), cflag(FlagIncludeAppNuxtDistDir), cflag(FlagIncludeAppNuxtStaticDir), cflag(FlagIncludeAppNuxtNodeModulesDir), cflag(FlagIncludeAppNextDir), cflag(FlagIncludeAppNextBuildDir), cflag(FlagIncludeAppNextDistDir), cflag(FlagIncludeAppNextStaticDir), cflag(FlagIncludeAppNextNodeModulesDir), cflag(FlagIncludeNodePackage), cflag(FlagKeepPerms), cflag(FlagPathPerms), cflag(FlagPathPermsFile), //"EXCLUDE" FLAGS - START cflag(FlagExcludePattern), cflag(FlagExcludeVarLockFiles), cflag(FlagExcludeMounts), //"EXCLUDE" FLAGS - END cflag(FlagObfuscateMetadata), command.Cflag(command.FlagContinueAfter), command.Cflag(command.FlagUseLocalMounts), command.Cflag(command.FlagUseSensorVolume), command.Cflag(command.FlagRTAOnbuildBaseImage), command.Cflag(command.FlagRTASourcePT), //Sensor flags: command.Cflag(command.FlagSensorIPCEndpoint), command.Cflag(command.FlagSensorIPCMode), }, command.HTTPProbeFlags()...), Action: func(ctx *cli.Context) error { gparams, ok := command.CLIContextGet(ctx.Context, command.GlobalParams).(*command.GenericParams) if !ok || gparams == nil { return command.ErrNoGlobalParams } xc := app.NewExecutionContext( Name, gparams.QuietCLIMode, gparams.OutputFormat) //NOTE: this is a placeholder to load all command params from a file _ = ctx.String(command.FlagCommandParamsFile) cbOpts, err := GetContainerBuildOptions(ctx) if err != nil { xc.Out.Error("param.error.container.build.options", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } deleteFatImage := ctx.Bool(command.FlagDeleteFatImage) if cbOpts.Dockerfile == "" { deleteFatImage = false } composeFiles := ctx.StringSlice(command.FlagComposeFile) //todo: load/parse compose file and then use it to validate the related compose params targetComposeSvc := ctx.String(command.FlagTargetComposeSvc) targetComposeSvcImage := ctx.String(command.FlagTargetComposeSvcImage) composeSvcNoPorts := ctx.Bool(command.FlagComposeSvcNoPorts) depExcludeComposeSvcAll := ctx.Bool(command.FlagDepExcludeComposeSvcAll) depIncludeComposeSvcDeps := ctx.String(command.FlagDepIncludeComposeSvcDeps) depIncludeTargetComposeSvcDeps := ctx.Bool(command.FlagDepIncludeTargetComposeSvcDeps) depIncludeComposeSvcs := ctx.StringSlice(command.FlagDepIncludeComposeSvc) depExcludeComposeSvcs := ctx.StringSlice(command.FlagDepExcludeComposeSvc) composeNets := ctx.StringSlice(command.FlagComposeNet) composeSvcStartWait := ctx.Int(command.FlagComposeSvcStartWait) composeEnvNoHost := ctx.Bool(command.FlagComposeEnvNoHost) composeEnvVars, err := command.ParseLinesWithCommentsFile(ctx.String(command.FlagComposeEnvFile)) if err != nil { xc.Out.Error("param.error.compose.env.file", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } composeProjectName := ctx.String(command.FlagComposeProjectName) composeWorkdir := ctx.String(command.FlagComposeWorkdir) containerProbeComposeSvc := ctx.String(command.FlagContainerProbeComposeSvc) kubeOpts, err := GetKubernetesOptions(ctx) if err != nil { xc.Out.Error("param.error.kubernetes.options", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } var targetRef string if kubeOpts.HasTargetSet() { targetRef = kubeOpts.Target.Workload } else if len(composeFiles) > 0 && targetComposeSvc != "" { targetRef = targetComposeSvc } else if cbOpts.Dockerfile == "" { targetRef = ctx.String(command.FlagTarget) if targetRef == "" { if ctx.Args().Len() < 1 { xc.Out.Error("param.target", "missing image ID/name") cli.ShowCommandHelp(ctx, Name) return nil } else { targetRef = ctx.Args().First() } } } else { targetRef = cbOpts.DockerfileContext if targetRef == "" { if ctx.Args().Len() < 1 { xc.Out.Error("param.target", "missing Dockerfile build context directory") cli.ShowCommandHelp(ctx, Name) return nil } else { targetRef = ctx.Args().First() } } } if targetRef == "" { xc.Out.Error("param.target", "missing target - make sure to set one of the target params") cli.ShowCommandHelp(ctx, Name) return nil } appOpts, ok := command.CLIContextGet(ctx.Context, command.AppParams).(*config.AppOptions) if !kubeOpts.HasTargetSet() && (!ok || appOpts == nil) { log.Debug("param.error.app.options - no app params") } crOpts, err := command.GetContainerRunOptions(ctx) if err != nil { xc.Out.Error("param.error.container.run.options", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } doPull := ctx.Bool(command.FlagPull) dockerConfigPath := ctx.String(command.FlagDockerConfigPath) registryAccount := ctx.String(command.FlagRegistryAccount) registrySecret := ctx.String(command.FlagRegistrySecret) doShowPullLogs := ctx.Bool(command.FlagShowPullLogs) doRmFileArtifacts := ctx.Bool(command.FlagRemoveFileArtifacts) doCopyMetaArtifacts := ctx.String(command.FlagCopyMetaArtifacts) portBindings, err := command.ParsePortBindings(ctx.StringSlice(command.FlagPublishPort)) if err != nil { xc.Out.Error("param.publish.port", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } doPublishExposedPorts := ctx.Bool(command.FlagPublishExposedPorts) httpProbeOpts := command.GetHTTPProbeOptions(xc, ctx, false) continueAfter, err := command.GetContinueAfter(ctx) if err != nil { xc.Out.Error("param.error.continue.after", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } if continueAfter.Mode == config.CAMProbe && !httpProbeOpts.Do { continueAfter.Mode = "" xc.Out.Info("exec", ovars{ "message": "changing continue-after from probe to nothing because http-probe is disabled", }) } execCmd := ctx.String(command.FlagExec) execFile := ctx.String(command.FlagExecFile) if strings.Contains(continueAfter.Mode, config.CAMExec) && len(execCmd) == 0 && len(execFile) == 0 { continueAfter.Mode = config.CAMEnter xc.Out.Info("exec", ovars{ "message": "changing continue-after from exec to enter because there are no exec flags", }) } if len(execCmd) != 0 && len(execFile) != 0 { xc.Out.Error("param.error.exec", "fatal: cannot use both --exec and --exec-file") xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } var execFileCmd []byte if len(execFile) > 0 { execFileCmd, err = os.ReadFile(execFile) xc.FailOn(err) if !strings.Contains(continueAfter.Mode, config.CAMExec) { if continueAfter.Mode == "" { continueAfter.Mode = config.CAMExec } else { continueAfter.Mode = fmt.Sprintf("%s&%s", continueAfter.Mode, config.CAMExec) } xc.Out.Info("exec", ovars{ "message": fmt.Sprintf("updating continue-after mode to %s", continueAfter.Mode), }) } } else if len(execCmd) > 0 { if !strings.Contains(continueAfter.Mode, config.CAMExec) { if continueAfter.Mode == "" { continueAfter.Mode = config.CAMExec } else { continueAfter.Mode = fmt.Sprintf("%s&%s", continueAfter.Mode, config.CAMExec) } xc.Out.Info("exec", ovars{ "message": fmt.Sprintf("updating continue-after mode to %s", continueAfter.Mode), }) } } if containerProbeComposeSvc != "" { if !strings.Contains(continueAfter.Mode, config.CAMContainerProbe) { if continueAfter.Mode == "" { continueAfter.Mode = config.CAMContainerProbe } else { continueAfter.Mode = fmt.Sprintf("%s&%s", continueAfter.Mode, config.CAMContainerProbe) } xc.Out.Info("continue.after", ovars{ "message": fmt.Sprintf("updating mode to %s", continueAfter.Mode), }) } } if continueAfter.Mode == "" { continueAfter.Mode = config.CAMEnter xc.Out.Info("exec", ovars{ "message": "changing continue-after to enter", }) } hostExecProbes := ctx.StringSlice(command.FlagHostExec) moreHostExecProbes, err := command.ParseHTTPProbeExecFile(ctx.String(command.FlagHostExecFile)) if err != nil { xc.Out.Error("param.host.exec.file", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } if len(moreHostExecProbes) > 0 { hostExecProbes = append(hostExecProbes, moreHostExecProbes...) } if strings.Contains(continueAfter.Mode, config.CAMHostExec) && len(hostExecProbes) == 0 { if continueAfter.Mode == config.CAMHostExec { continueAfter.Mode = config.CAMEnter xc.Out.Info("host-exec", ovars{ "message": "changing continue-after from host-exec to enter because there are no host-exec commands", }) } else { continueAfter.Mode = command.RemoveContinueAfterMode(continueAfter.Mode, config.CAMHostExec) xc.Out.Info("host-exec", ovars{ "message": "removing host-exec continue-after mode because there are no host-exec commands", }) } } if len(hostExecProbes) > 0 { if !strings.Contains(continueAfter.Mode, config.CAMHostExec) { if continueAfter.Mode == "" { continueAfter.Mode = config.CAMHostExec } else { continueAfter.Mode = fmt.Sprintf("%s&%s", continueAfter.Mode, config.CAMHostExec) } xc.Out.Info("exec", ovars{ "message": fmt.Sprintf("updating continue-after mode to %s", continueAfter.Mode), }) } } doKeepPerms := ctx.Bool(FlagKeepPerms) doRunTargetAsUser := ctx.Bool(command.FlagRunTargetAsUser) doShowContainerLogs := ctx.Bool(command.FlagShowContainerLogs) doEnableMondel := ctx.Bool(command.FlagEnableMondelLogs) doShowBuildLogs := ctx.Bool(FlagShowBuildLogs) outputTags := ctx.StringSlice(FlagTag) doImageOverrides := ctx.String(FlagImageOverrides) overrides, err := command.GetContainerOverrides(xc, ctx) if err != nil { xc.Out.Error("param.error.image.overrides", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } instructions, err := GetImageInstructions(ctx) if err != nil { xc.Out.Error("param.error.image.instructions", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } volumeMounts, err := command.ParseVolumeMounts(ctx.StringSlice(command.FlagMount)) if err != nil { xc.Out.Error("param.error.mount", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } excludePatterns := command.ParsePaths(ctx.StringSlice(FlagExcludePattern)) preservePaths := command.ParsePaths(ctx.StringSlice(FlagPreservePath)) morePreservePaths, err := command.ParsePathsFile(ctx.String(FlagPreservePathFile)) if err != nil { xc.Out.Error("param.error.preserve.path.file", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } else { for k, v := range morePreservePaths { preservePaths[k] = v } } if len(preservePaths) > 0 { for filtered := range artifact.FilteredPaths { if _, found := preservePaths[filtered]; found { delete(preservePaths, filtered) xc.Out.Info("params", ovars{ "preserve.path": filtered, "message": "ignoring", }) } } var toDelete []string for ip := range preservePaths { if artifact.IsFilteredPath(ip) { toDelete = append(toDelete, ip) } } for _, dp := range toDelete { delete(preservePaths, dp) xc.Out.Info("params", ovars{ "preserve.path": dp, "message": "ignoring", }) } } includePaths := command.ParsePaths(ctx.StringSlice(FlagIncludePath)) moreIncludePaths, err := command.ParsePathsFile(ctx.String(FlagIncludePathFile)) if err != nil { xc.Out.Error("param.error.include.path.file", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } else { for k, v := range moreIncludePaths { includePaths[k] = v } } if len(includePaths) > 0 { for filtered := range artifact.FilteredPaths { if _, found := includePaths[filtered]; found { delete(includePaths, filtered) xc.Out.Info("params", ovars{ "include.path": filtered, "message": "ignoring", }) } } var toDelete []string for ip := range includePaths { if artifact.IsFilteredPath(ip) { toDelete = append(toDelete, ip) } } for _, dp := range toDelete { delete(includePaths, dp) xc.Out.Info("params", ovars{ "include.path": dp, "message": "ignoring", }) } } creportIncludePaths, err := command.ParsePathsCreportFile(ctx.String(FlagIncludePathsCreportFile)) if err != nil { xc.Out.Error("param.error.include.paths.creport.file", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } else { for k, v := range creportIncludePaths { includePaths[k] = v } } pathPerms := command.ParsePaths(ctx.StringSlice(FlagPathPerms)) morePathPerms, err := command.ParsePathsFile(ctx.String(FlagPathPermsFile)) if err != nil { xc.Out.Error("param.error.path.perms.file", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } else { for k, v := range morePathPerms { pathPerms[k] = v } } includeBins := command.ParsePaths(ctx.StringSlice(FlagIncludeBin)) moreIncludeBins, err := command.ParsePathsFile(ctx.String(FlagIncludeBinFile)) if err != nil { xc.Out.Error("param.error.include.bin.file", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } else { for k, v := range moreIncludeBins { includeBins[k] = v } } if len(includeBins) > 0 { //shouldn't happen, but filtering either way for filtered := range artifact.FilteredPaths { if _, found := includeBins[filtered]; found { delete(includeBins, filtered) xc.Out.Info("params", ovars{ "include.bin": filtered, "message": "ignoring", }) } } var toDelete []string for ip := range includeBins { if artifact.IsFilteredPath(ip) { toDelete = append(toDelete, ip) } } for _, dp := range toDelete { delete(includeBins, dp) xc.Out.Info("params", ovars{ "include.bin": dp, "message": "ignoring", }) } } //note: if path perms, ID change are provided they are applied to all matching binaries includeDirBinsList := command.ParsePaths(ctx.StringSlice(FlagIncludeDirBins)) includeExes := command.ParsePaths(ctx.StringSlice(FlagIncludeExe)) moreIncludeExes, err := command.ParsePathsFile(ctx.String(FlagIncludeExeFile)) if err != nil { xc.Out.Error("param.error.include.exe.file", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } else { for k, v := range moreIncludeExes { includeExes[k] = v } } doIncludeShell := ctx.Bool(FlagIncludeShell) doIncludeWorkdir := ctx.Bool(FlagIncludeWorkdir) includeLastImageLayers := uint(0) doIncludeAppImageAll := ctx.Bool(FlagIncludeAppImageAll) appImageStartInstGroup := ctx.Int(FlagAppImageStartInstGroup) appImageStartInst := ctx.String(FlagAppImageStartInst) appImageDockerfilePath := ctx.String(FlagAppImageDockerfile) appImageDockerfileInsts, err := command.ParseLinesWithCommentsFile(appImageDockerfilePath) if err != nil { xc.Out.Error("param.error.app.image.dockerfile", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } doIncludeSSHClient := ctx.Bool(FlagIncludeSSHClient) doIncludeOSLibsNet := ctx.Bool(FlagIncludeOSLibsNet) doIncludeZoneInfo := ctx.Bool(FlagIncludeZoneInfo) doIncludeCertAll := ctx.Bool(FlagIncludeCertAll) doIncludeCertBundles := ctx.Bool(FlagIncludeCertBundles) doIncludeCertDirs := ctx.Bool(FlagIncludeCertDirs) doIncludeCertPKAll := ctx.Bool(FlagIncludeCertPKAll) doIncludeCertPKDirs := ctx.Bool(FlagIncludeCertPKDirs) doIncludeNew := ctx.Bool(FlagIncludeNew) doUseLocalMounts := ctx.Bool(command.FlagUseLocalMounts) doUseSensorVolume := ctx.String(command.FlagUseSensorVolume) doKeepTmpArtifacts := ctx.Bool(FlagKeepTmpArtifacts) doExcludeVarLockFiles := ctx.Bool(FlagExcludeVarLockFiles) doExcludeMounts := ctx.Bool(FlagExcludeMounts) if doExcludeMounts { for mpath := range volumeMounts { excludePatterns[mpath] = nil mpattern := fmt.Sprintf("%s/**", mpath) excludePatterns[mpattern] = nil } } commandReport := ctx.String(command.FlagCommandReport) if commandReport == "off" { commandReport = "" } rtaOnbuildBaseImage := ctx.Bool(command.FlagRTAOnbuildBaseImage) rtaSourcePT := ctx.Bool(command.FlagRTASourcePT) doObfuscateMetadata := ctx.Bool(FlagObfuscateMetadata) imageBuildEngine, err := getImageBuildEngine(ctx) if err != nil { xc.Out.Error("param.error.image-build-engine", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } imageBuildArch, err := getImageBuildArch(ctx) if err != nil { xc.Out.Error("param.error.image-build-arch", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } OnCommand( xc, gparams, targetRef, doPull, dockerConfigPath, registryAccount, registrySecret, doShowPullLogs, composeFiles, targetComposeSvc, targetComposeSvcImage, composeSvcStartWait, composeSvcNoPorts, depExcludeComposeSvcAll, depIncludeComposeSvcDeps, depIncludeTargetComposeSvcDeps, depIncludeComposeSvcs, depExcludeComposeSvcs, composeNets, composeEnvVars, composeEnvNoHost, composeWorkdir, composeProjectName, containerProbeComposeSvc, cbOpts, crOpts, outputTags, httpProbeOpts, portBindings, doPublishExposedPorts, hostExecProbes, doRmFileArtifacts, doCopyMetaArtifacts, doRunTargetAsUser, doShowContainerLogs, doEnableMondel, doShowBuildLogs, command.ParseImageOverrides(doImageOverrides), overrides, instructions, ctx.StringSlice(command.FlagLink), ctx.StringSlice(command.FlagEtcHostsMap), ctx.StringSlice(command.FlagContainerDNS), ctx.StringSlice(command.FlagContainerDNSSearch), volumeMounts, doKeepPerms, pathPerms, excludePatterns, doExcludeVarLockFiles, preservePaths, includePaths, includeBins, includeDirBinsList, includeExes, doIncludeShell, doIncludeWorkdir, includeLastImageLayers, doIncludeAppImageAll, appImageStartInstGroup, appImageStartInst, appImageDockerfileInsts, doIncludeSSHClient, doIncludeOSLibsNet, doIncludeZoneInfo, doIncludeCertAll, doIncludeCertBundles, doIncludeCertDirs, doIncludeCertPKAll, doIncludeCertPKDirs, doIncludeNew, doUseLocalMounts, doUseSensorVolume, doKeepTmpArtifacts, continueAfter, execCmd, string(execFileCmd), deleteFatImage, rtaOnbuildBaseImage, rtaSourcePT, doObfuscateMetadata, ctx.String(command.FlagSensorIPCEndpoint), ctx.String(command.FlagSensorIPCMode), kubeOpts, GetAppNodejsInspectOptions(ctx), imageBuildEngine, imageBuildArch) return nil }, } ================================================ FILE: pkg/app/master/command/build/flags.go ================================================ package build import ( "errors" "fmt" "os" "strings" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/app/master/config" log "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" ) // Build command flag names const ( FlagImageBuildEngine = "image-build-engine" FlagImageBuildArch = "image-build-arch" FlagDeleteFatImage = "delete-generated-fat-image" FlagShowBuildLogs = "show-blogs" FlagPathPerms = "path-perms" FlagPathPermsFile = "path-perms-file" FlagPreservePath = "preserve-path" FlagPreservePathFile = "preserve-path-file" FlagIncludePath = "include-path" FlagIncludePathFile = "include-path-file" FlagIncludeBin = "include-bin" FlagIncludeBinFile = "include-bin-file" FlagIncludeExe = "include-exe" FlagIncludeExeFile = "include-exe-file" FlagIncludeShell = "include-shell" FlagIncludeDirBins = "include-dir-bins" FlagIncludeDirBinsUsage = "Keep binaries in the target directory (executables or shared objects) and their dependencies, which could be in other locations" FlagIncludeWorkdir = "include-workdir" FlagIncludeWorkdirUsage = "Keep files in working directory" //TBD FlagWorkdirExclude = "workdir-exclude" FlagWorkdirExcludeUsage = "Exclude filter for artifacts when working directory is included" FlagIncludeAppImageAddCopyAll = "include-app-image-addcopy-all" //TBD FlagIncludeAppImageRun = "include-app-image-run" //TBD FlagIncludeAppImageAll = "include-app-image-all" FlagIncludeAppImageAllUsage = "Keep everything in the app part of the container image" FlagAppImageStartInst = "app-image-start-instruction" FlagAppImageStartInstUsage = "Instruction (prefix) that indicates where the app starts in the container image" FlagAppImageStartLayerCount = "app-image-start-layer-count" //TBD FlagAppImageStartInstGroup = "app-image-start-instruction-group" FlagAppImageStartInstGroupUsage = "Instruction group (reverse) index that indicates where the app starts in the container image" FlagAppImageStartDetect = "app-image-start-detect" //TBD FlagAppImageDockerfile = "app-image-dockerfile" //TODO: make it work with FlagBuildFromDockerfile too FlagAppImageDockerfileUsage = "Path to app image Dockerfile (used to determine where the application part of the image starts)" FlagIncludePathsCreportFile = "include-paths-creport-file" FlagIncludePathsCreportFileUsage = "Keep files from the referenced creport" FlagIncludeOSLibsNet = "include-oslibs-net" FlagIncludeOSLibsNetUsage = "Keep the common networking OS libraries" FlagIncludeSSHClient = "include-ssh-client" FlagIncludeSSHClientUsage = "Keep the common SSH client components and configs" FlagIncludeSSHClientAll = "include-ssh-client-all" FlagIncludeSSHClientAllUsage = "Keep all SSH client components and configs" FlagIncludeSSHClientBasic = "include-ssh-client-basic" FlagIncludeSSHClientBasicUsage = "Keep the basic SSH client components and configs" FlagIncludeZoneInfo = "include-zoneinfo" FlagIncludeCertAll = "include-cert-all" FlagIncludeCertBundles = "include-cert-bundles-only" FlagIncludeCertDirs = "include-cert-dirs" FlagIncludeCertPKAll = "include-cert-pk-all" FlagIncludeCertPKDirs = "include-cert-pk-dirs" FlagIncludeNew = "include-new" //FlagIncludeLicenses = "include-licenses" FlagKeepTmpArtifacts = "keep-tmp-artifacts" FlagIncludeAppNuxtDir = "include-app-nuxt-dir" FlagIncludeAppNuxtBuildDir = "include-app-nuxt-build-dir" FlagIncludeAppNuxtDistDir = "include-app-nuxt-dist-dir" FlagIncludeAppNuxtStaticDir = "include-app-nuxt-static-dir" FlagIncludeAppNuxtNodeModulesDir = "include-app-nuxt-nodemodules-dir" FlagIncludeAppNextDir = "include-app-next-dir" FlagIncludeAppNextBuildDir = "include-app-next-build-dir" FlagIncludeAppNextDistDir = "include-app-next-dist-dir" FlagIncludeAppNextStaticDir = "include-app-next-static-dir" FlagIncludeAppNextNodeModulesDir = "include-app-next-nodemodules-dir" FlagIncludeNodePackage = "include-node-package" FlagKeepPerms = "keep-perms" //"EXCLUDE" FLAGS: FlagExcludePattern = "exclude-pattern" FlagExcludePatternUsage = "Exclude path pattern (Glob/Match in Go and **) from image" FlagExcludeVarLockFiles = "exclude-varlock-files" FlagExcludeVarLockFilesUsage = "Exclude the files in the var and run lock directory" //NOTES: // also "exclude-varlock-new-files" <- related to "include-new" FlagExcludeMounts = "exclude-mounts" FlagExcludeMountsUsage = "Exclude mounted volumes from image" //Flags to edit (modify, add and remove) image metadata FlagNewEntrypoint = "new-entrypoint" FlagNewCmd = "new-cmd" FlagNewLabel = "new-label" FlagNewVolume = "new-volume" FlagNewExpose = "new-expose" FlagNewWorkdir = "new-workdir" FlagNewEnv = "new-env" FlagRemoveVolume = "remove-volume" FlagRemoveExpose = "remove-expose" FlagRemoveEnv = "remove-env" FlagRemoveLabel = "remove-label" FlagTag = "tag" FlagImageOverrides = "image-overrides" //Flags to build fat images from Dockerfile FlagTagFat = "tag-fat" FlagBuildFromDockerfile = "dockerfile" FlagDockerfileContext = "dockerfile-context" FlagCBOAddHost = "cbo-add-host" FlagCBOBuildArg = "cbo-build-arg" FlagCBOLabel = "cbo-label" FlagCBOTarget = "cbo-target" FlagCBONetwork = "cbo-network" FlagCBOCacheFrom = "cbo-cache-from" //Experimenal flags FlagObfuscateMetadata = "obfuscate-metadata" ) // Build command flag usage info const ( FlagImageBuildEngineUsage = "Select image build engine: internal | docker | none" FlagImageBuildArchUsage = "Select output image build architecture" FlagDeleteFatImageUsage = "Delete generated fat image requires --dockerfile flag" FlagShowBuildLogsUsage = "Show image build logs" FlagPathPermsUsage = "Set path permissions in optimized image" FlagPathPermsFileUsage = "File with path permissions to set" FlagPreservePathUsage = "Keep path from orignal image in its initial state (changes to the selected container image files when it runs will be discarded)" FlagPreservePathFileUsage = "File with paths to keep from original image in their original state (changes to the selected container image files when it runs will be discarded)" FlagIncludePathUsage = "Keep path from original image" FlagIncludePathFileUsage = "File with paths to keep from original image" FlagIncludeBinUsage = "Keep binary from original image (executable or shared object using its absolute path)" FlagIncludeExeUsage = "Keep executable from original image (by executable name)" FlagIncludeShellUsage = "Keep basic shell functionality" FlagIncludeZoneInfoUsage = "Keep the OS/libc zoneinfo data" FlagIncludeCertAllUsage = "Keep all discovered cert files" FlagIncludeCertBundlesUsage = "Keep only cert bundles" FlagIncludeCertDirsUsage = "Keep known cert directories and all files in them" FlagIncludeCertPKAllUsage = "Keep all discovered cert private keys" FlagIncludeCertPKDirsUsage = "Keep known cert private key directories and all files in them" FlagIncludeNewUsage = "Keep new files created by target during dynamic analysis" FlagKeepTmpArtifactsUsage = "Keep temporary artifacts when command is done" FlagIncludeAppNuxtDirUsage = "Keep the root Nuxt.js app directory" FlagIncludeAppNuxtBuildDirUsage = "Keep the build Nuxt.js app directory" FlagIncludeAppNuxtDistDirUsage = "Keep the dist Nuxt.js app directory" FlagIncludeAppNuxtStaticDirUsage = "Keep the static asset directory for Nuxt.js apps" FlagIncludeAppNuxtNodeModulesDirUsage = "Keep the node modules directory for Nuxt.js apps" FlagIncludeAppNextDirUsage = "Keep the root Next.js app directory" FlagIncludeAppNextBuildDirUsage = "Keep the build directory for Next.js app" FlagIncludeAppNextDistDirUsage = "Keep the static SPA directory for Next.js apps" FlagIncludeAppNextStaticDirUsage = "Keep the static public asset directory for Next.js apps" FlagIncludeAppNextNodeModulesDirUsage = "Keep the node modules directory for Next.js apps" FlagIncludeNodePackageUsage = "Keep node.js package by name" FlagKeepPermsUsage = "Keep artifact permissions as-is" FlagNewEntrypointUsage = "New ENTRYPOINT instruction for the optimized image" FlagNewCmdUsage = "New CMD instruction for the optimized image" FlagNewVolumeUsage = "New VOLUME instructions for the optimized image" FlagNewLabelUsage = "New LABEL instructions for the optimized image" FlagNewExposeUsage = "New EXPOSE instructions for the optimized image" FlagNewWorkdirUsage = "New WORKDIR instruction for the optimized image" FlagNewEnvUsage = "New ENV instructions for the optimized image" FlagRemoveExposeUsage = "Remove EXPOSE instructions for the optimized image" FlagRemoveEnvUsage = "Remove ENV instructions for the optimized image" FlagRemoveLabelUsage = "Remove LABEL instructions for the optimized image" FlagRemoveVolumeUsage = "Remove VOLUME instructions for the optimized image" FlagTagUsage = "Custom tags for the generated image" FlagImageOverridesUsage = "Save runtime overrides in generated image (values is 'all' or a comma delimited list of override types: 'entrypoint', 'cmd', 'workdir', 'env', 'expose', 'volume', 'label')" FlagIncludeBinFileUsage = "File with shared binary file names to include from image" FlagIncludeExeFileUsage = "File with executable file names to include from image" FlagTagFatUsage = "Custom tag for the fat image built from Dockerfile" FlagBuildFromDockerfileUsage = "The source Dockerfile name to build the fat image before it's optimized" FlagDockerfileContextUsage = "The build context directory when building source Dockerfile" FlagCBOAddHostUsage = "Add an extra host-to-IP mapping in /etc/hosts to use when building an image" FlagCBOBuildArgUsage = "Add a build-time variable" FlagCBOLabelUsage = "Add a label when building from Dockerfiles" FlagCBOTargetUsage = "Target stage to build for multi-stage Dockerfiles" FlagCBONetworkUsage = "Networking mode to use for the RUN instructions at build-time" FlagCBOCacheFromUsage = "Add an image to the build cache" FlagObfuscateMetadataUsage = "Obfuscate the standard system and application metadata to make it more challenging to identify the image components" ) var Flags = map[string]cli.Flag{ FlagShowBuildLogs: &cli.BoolFlag{ Name: FlagShowBuildLogs, Usage: FlagShowBuildLogsUsage, EnvVars: []string{"DSLIM_SHOW_BLOGS"}, }, FlagPathPerms: &cli.StringSliceFlag{ Name: FlagPathPerms, Value: cli.NewStringSlice(), Usage: FlagPathPermsUsage, EnvVars: []string{"DSLIM_PATH_PERMS"}, }, FlagPathPermsFile: &cli.StringFlag{ Name: FlagPathPermsFile, Value: "", Usage: FlagPathPermsFileUsage, EnvVars: []string{"DSLIM_PATH_PERMS_FILE"}, }, FlagPreservePath: &cli.StringSliceFlag{ Name: FlagPreservePath, Value: cli.NewStringSlice(), Usage: FlagPreservePathUsage, EnvVars: []string{"DSLIM_PRESERVE_PATH"}, }, FlagPreservePathFile: &cli.StringFlag{ Name: FlagPreservePathFile, Value: "", Usage: FlagPreservePathFileUsage, EnvVars: []string{"DSLIM_PRESERVE_PATH_FILE"}, }, FlagIncludePath: &cli.StringSliceFlag{ Name: FlagIncludePath, Value: cli.NewStringSlice(), Usage: FlagIncludePathUsage, EnvVars: []string{"DSLIM_INCLUDE_PATH"}, }, FlagIncludePathFile: &cli.StringFlag{ Name: FlagIncludePathFile, Value: "", Usage: FlagIncludePathFileUsage, EnvVars: []string{"DSLIM_INCLUDE_PATH_FILE"}, }, FlagIncludeBin: &cli.StringSliceFlag{ Name: FlagIncludeBin, Value: cli.NewStringSlice(), Usage: FlagIncludeBinUsage, EnvVars: []string{"DSLIM_INCLUDE_BIN"}, }, FlagIncludeDirBins: &cli.StringSliceFlag{ Name: FlagIncludeDirBins, Value: cli.NewStringSlice(), Usage: FlagIncludeDirBinsUsage, EnvVars: []string{"DSLIM_INCLUDE_DIR_BINS"}, }, FlagIncludeExe: &cli.StringSliceFlag{ Name: FlagIncludeExe, Value: cli.NewStringSlice(), Usage: FlagIncludeExeUsage, EnvVars: []string{"DSLIM_INCLUDE_EXE"}, }, FlagIncludeShell: &cli.BoolFlag{ Name: FlagIncludeShell, Usage: FlagIncludeShellUsage, EnvVars: []string{"DSLIM_INCLUDE_SHELL"}, }, //// FlagIncludeWorkdir: &cli.BoolFlag{ Name: FlagIncludeWorkdir, Usage: FlagIncludeWorkdirUsage, EnvVars: []string{"DSLIM_INCLUDE_WORKDIR"}, }, FlagIncludeAppImageAll: &cli.BoolFlag{ Name: FlagIncludeAppImageAll, Usage: FlagIncludeAppImageAllUsage, EnvVars: []string{"DSLIM_INCLUDE_APP_IMAGE_ALL"}, }, FlagAppImageStartInstGroup: &cli.IntFlag{ Name: FlagAppImageStartInstGroup, Value: -1, Usage: FlagAppImageStartInstGroupUsage, EnvVars: []string{"DSLIM_APP_IMAGE_START_INST_GROUP"}, }, FlagAppImageStartInst: &cli.StringFlag{ Name: FlagAppImageStartInst, Usage: FlagAppImageStartInstUsage, EnvVars: []string{"DSLIM_APP_IMAGE_START_INST"}, }, FlagAppImageDockerfile: &cli.StringFlag{ Name: FlagAppImageDockerfile, Usage: FlagAppImageDockerfileUsage, EnvVars: []string{"DSLIM_APP_IMAGE_DOCKERFILE"}, }, //// FlagIncludePathsCreportFile: &cli.StringFlag{ Name: FlagIncludePathsCreportFile, Value: "", Usage: FlagIncludePathsCreportFileUsage, EnvVars: []string{"DSLIM_INCLUDE_PATHS_CREPORT_FILE"}, }, //// FlagIncludeOSLibsNet: &cli.BoolFlag{ Name: FlagIncludeOSLibsNet, Value: true, //enabled by default Usage: FlagIncludeOSLibsNetUsage, EnvVars: []string{"DSLIM_INCLUDE_OSLIBS_NET"}, }, //// FlagIncludeSSHClient: &cli.BoolFlag{ Name: FlagIncludeSSHClient, Value: false, //disabled by default (for now) Usage: FlagIncludeSSHClientUsage, EnvVars: []string{"DSLIM_INCLUDE_SSH_CLIENT"}, }, //// FlagIncludeZoneInfo: &cli.BoolFlag{ Name: FlagIncludeZoneInfo, Value: false, Usage: FlagIncludeZoneInfoUsage, EnvVars: []string{"DSLIM_INCLUDE_ZONEINFO"}, }, //// FlagIncludeCertAll: &cli.BoolFlag{ Name: FlagIncludeCertAll, Value: true, //enabled by default Usage: FlagIncludeCertAllUsage, EnvVars: []string{"DSLIM_INCLUDE_CERT_ALL"}, }, FlagIncludeCertBundles: &cli.BoolFlag{ Name: FlagIncludeCertBundles, Usage: FlagIncludeCertBundlesUsage, EnvVars: []string{"DSLIM_INCLUDE_CERT_BUNDLES"}, }, FlagIncludeCertDirs: &cli.BoolFlag{ Name: FlagIncludeCertDirs, Usage: FlagIncludeCertDirsUsage, EnvVars: []string{"DSLIM_INCLUDE_CERT_DIRS"}, }, FlagIncludeCertPKAll: &cli.BoolFlag{ Name: FlagIncludeCertPKAll, Usage: FlagIncludeCertPKAllUsage, EnvVars: []string{"DSLIM_INCLUDE_CERT_PK_ALL"}, }, FlagIncludeCertPKDirs: &cli.BoolFlag{ Name: FlagIncludeCertPKDirs, Usage: FlagIncludeCertPKDirsUsage, EnvVars: []string{"DSLIM_INCLUDE_CERT_PK_DIRS"}, }, FlagIncludeNew: &cli.BoolFlag{ Name: FlagIncludeNew, Value: true, //enabled by default for now to keep the original behavior until minification works the same Usage: FlagIncludeNewUsage, EnvVars: []string{"DSLIM_INCLUDE_NEW"}, }, //// FlagKeepTmpArtifacts: &cli.BoolFlag{ Name: FlagKeepTmpArtifacts, Usage: FlagKeepTmpArtifactsUsage, EnvVars: []string{"DSLIM_KEEP_TMP_ARTIFACTS"}, }, FlagIncludeAppNuxtDir: &cli.BoolFlag{ Name: FlagIncludeAppNuxtDir, Usage: FlagIncludeAppNuxtDirUsage, EnvVars: []string{"DSLIM_INCLUDE_APP_NUXT_DIR"}, }, FlagIncludeAppNuxtBuildDir: &cli.BoolFlag{ Name: FlagIncludeAppNuxtBuildDir, Usage: FlagIncludeAppNuxtBuildDirUsage, EnvVars: []string{"DSLIM_INCLUDE_APP_NUXT_BUILD_DIR"}, }, FlagIncludeAppNuxtDistDir: &cli.BoolFlag{ Name: FlagIncludeAppNuxtDistDir, Usage: FlagIncludeAppNuxtDistDirUsage, EnvVars: []string{"DSLIM_INCLUDE_APP_NUXT_DIST_DIR"}, }, FlagIncludeAppNuxtStaticDir: &cli.BoolFlag{ Name: FlagIncludeAppNuxtStaticDir, Usage: FlagIncludeAppNuxtStaticDirUsage, EnvVars: []string{"DSLIM_INCLUDE_APP_NUXT_STATIC_DIR"}, }, FlagIncludeAppNuxtNodeModulesDir: &cli.BoolFlag{ Name: FlagIncludeAppNuxtNodeModulesDir, Usage: FlagIncludeAppNuxtNodeModulesDirUsage, EnvVars: []string{"DSLIM_INCLUDE_APP_NUXT_NM_DIR"}, }, FlagIncludeAppNextDir: &cli.BoolFlag{ Name: FlagIncludeAppNextDir, Usage: FlagIncludeAppNextDirUsage, EnvVars: []string{"DSLIM_INCLUDE_APP_NEXT_DIR"}, }, FlagIncludeAppNextBuildDir: &cli.BoolFlag{ Name: FlagIncludeAppNextBuildDir, Usage: FlagIncludeAppNextBuildDirUsage, EnvVars: []string{"DSLIM_INCLUDE_APP_NEXT_BUILD_DIR"}, }, FlagIncludeAppNextDistDir: &cli.BoolFlag{ Name: FlagIncludeAppNextDistDir, Usage: FlagIncludeAppNextDistDirUsage, EnvVars: []string{"DSLIM_INCLUDE_APP_NEXT_DIST_DIR"}, }, FlagIncludeAppNextStaticDir: &cli.BoolFlag{ Name: FlagIncludeAppNextStaticDir, Usage: FlagIncludeAppNextStaticDirUsage, EnvVars: []string{"DSLIM_INCLUDE_APP_NEXT_STATIC_DIR"}, }, FlagIncludeAppNextNodeModulesDir: &cli.BoolFlag{ Name: FlagIncludeAppNextNodeModulesDir, Usage: FlagIncludeAppNextNodeModulesDirUsage, EnvVars: []string{"DSLIM_INCLUDE_APP_NEXT_NM_DIR"}, }, FlagIncludeNodePackage: &cli.StringSliceFlag{ Name: FlagIncludeNodePackage, Value: cli.NewStringSlice(), Usage: FlagIncludeNodePackageUsage, EnvVars: []string{"DSLIM_INCLUDE_NODE_PKG"}, }, FlagKeepPerms: &cli.BoolFlag{ Name: FlagKeepPerms, Value: true, //enabled by default Usage: FlagKeepPermsUsage, EnvVars: []string{"DSLIM_KEEP_PERMS"}, }, //"EXCLUDE" FLAGS - START FlagExcludePattern: &cli.StringSliceFlag{ Name: FlagExcludePattern, Value: cli.NewStringSlice(), Usage: FlagExcludePatternUsage, EnvVars: []string{"DSLIM_EXCLUDE_PATTERN"}, }, FlagExcludeVarLockFiles: &cli.BoolFlag{ Name: FlagExcludeVarLockFiles, //true by default Value: true, Usage: FlagExcludeVarLockFilesUsage, EnvVars: []string{"DSLIM_EXCLUDE_VARLOCK"}, }, FlagExcludeMounts: &cli.BoolFlag{ Name: FlagExcludeMounts, //true by default Value: true, Usage: FlagExcludeMountsUsage, EnvVars: []string{"DSLIM_EXCLUDE_MOUNTS"}, }, //"EXCLUDE" FLAGS - END FlagNewEntrypoint: &cli.StringFlag{ Name: FlagNewEntrypoint, Value: "", Usage: FlagNewEntrypointUsage, EnvVars: []string{"DSLIM_NEW_ENTRYPOINT"}, }, FlagNewCmd: &cli.StringFlag{ Name: FlagNewCmd, Value: "", Usage: FlagNewCmdUsage, EnvVars: []string{"DSLIM_NEW_CMD"}, }, FlagNewExpose: &cli.StringSliceFlag{ Name: FlagNewExpose, Value: cli.NewStringSlice(), Usage: FlagNewExposeUsage, EnvVars: []string{"DSLIM_NEW_EXPOSE"}, }, FlagNewWorkdir: &cli.StringFlag{ Name: FlagNewWorkdir, Value: "", Usage: FlagNewWorkdirUsage, EnvVars: []string{"DSLIM_NEW_WORKDIR"}, }, FlagNewEnv: &cli.StringSliceFlag{ Name: FlagNewEnv, Value: cli.NewStringSlice(), Usage: FlagNewEnvUsage, EnvVars: []string{"DSLIM_NEW_ENV"}, }, FlagNewVolume: &cli.StringSliceFlag{ Name: FlagNewVolume, Value: cli.NewStringSlice(), Usage: FlagNewVolumeUsage, EnvVars: []string{"DSLIM_NEW_VOLUME"}, }, FlagNewLabel: &cli.StringSliceFlag{ Name: FlagNewLabel, Value: cli.NewStringSlice(), Usage: FlagNewLabelUsage, EnvVars: []string{"DSLIM_NEW_LABEL"}, }, FlagTag: &cli.StringSliceFlag{ Name: FlagTag, Value: cli.NewStringSlice(), Usage: FlagTagUsage, EnvVars: []string{"DSLIM_TARGET_TAG"}, }, FlagImageOverrides: &cli.StringFlag{ Name: FlagImageOverrides, Value: "", Usage: FlagImageOverridesUsage, EnvVars: []string{"DSLIM_TARGET_OVERRIDES"}, }, //Container Build Options FlagBuildFromDockerfile: &cli.StringFlag{ Name: FlagBuildFromDockerfile, Value: "", Usage: FlagBuildFromDockerfileUsage, EnvVars: []string{"DSLIM_BUILD_DOCKERFILE"}, }, FlagDockerfileContext: &cli.StringFlag{ Name: FlagDockerfileContext, Value: "", Usage: FlagDockerfileContextUsage, EnvVars: []string{"DSLIM_BUILD_DOCKERFILE_CTX"}, }, FlagTagFat: &cli.StringFlag{ Name: FlagTagFat, Value: "", Usage: FlagTagFatUsage, EnvVars: []string{"DSLIM_TARGET_TAG_FAT"}, }, FlagCBOAddHost: &cli.StringSliceFlag{ Name: FlagCBOAddHost, Value: cli.NewStringSlice(), Usage: FlagCBOAddHostUsage, EnvVars: []string{"DSLIM_CBO_ADD_HOST"}, }, FlagCBOBuildArg: &cli.StringSliceFlag{ Name: FlagCBOBuildArg, Value: cli.NewStringSlice(), Usage: FlagCBOBuildArgUsage, EnvVars: []string{"DSLIM_CBO_BUILD_ARG"}, }, FlagCBOCacheFrom: &cli.StringSliceFlag{ Name: FlagCBOCacheFrom, Value: cli.NewStringSlice(), Usage: FlagCBOCacheFromUsage, EnvVars: []string{"DSLIM_CBO_CACHE_FROM"}, }, FlagCBOLabel: &cli.StringSliceFlag{ Name: FlagCBOLabel, Value: cli.NewStringSlice(), Usage: FlagCBOLabelUsage, EnvVars: []string{"DSLIM_CBO_LABEL"}, }, FlagCBOTarget: &cli.StringFlag{ Name: FlagCBOTarget, Value: "", Usage: FlagCBOTargetUsage, EnvVars: []string{"DSLIM_CBO_TARGET"}, }, FlagCBONetwork: &cli.StringFlag{ Name: FlagCBONetwork, Value: "", Usage: FlagCBONetworkUsage, EnvVars: []string{"DSLIM_CBO_NETWORK"}, }, FlagImageBuildEngine: &cli.StringFlag{ Name: FlagImageBuildEngine, Value: IBEInternal, Usage: FlagImageBuildEngineUsage, EnvVars: []string{"DSLIM_IMAGE_BUILD_ENG"}, }, FlagImageBuildArch: &cli.StringFlag{ Name: FlagImageBuildArch, Usage: FlagImageBuildArchUsage, EnvVars: []string{"DSLIM_IMAGE_BUILD_ARCH"}, }, FlagDeleteFatImage: &cli.BoolFlag{ Name: FlagDeleteFatImage, Usage: FlagDeleteFatImageUsage, EnvVars: []string{"DSLIM_DELETE_FAT"}, }, FlagRemoveExpose: &cli.StringSliceFlag{ Name: FlagRemoveExpose, Value: cli.NewStringSlice(), Usage: FlagRemoveExposeUsage, EnvVars: []string{"DSLIM_RM_EXPOSE"}, }, FlagRemoveEnv: &cli.StringSliceFlag{ Name: FlagRemoveEnv, Value: cli.NewStringSlice(), Usage: FlagRemoveEnvUsage, EnvVars: []string{"DSLIM_RM_ENV"}, }, FlagRemoveLabel: &cli.StringSliceFlag{ Name: FlagRemoveLabel, Value: cli.NewStringSlice(), Usage: FlagRemoveLabelUsage, EnvVars: []string{"DSLIM_RM_LABEL"}, }, FlagRemoveVolume: &cli.StringSliceFlag{ Name: FlagRemoveVolume, Value: cli.NewStringSlice(), Usage: FlagRemoveVolumeUsage, EnvVars: []string{"DSLIM_RM_VOLUME"}, }, FlagIncludeBinFile: &cli.StringFlag{ Name: FlagIncludeBinFile, Value: "", Usage: FlagIncludeBinFileUsage, EnvVars: []string{"DSLIM_INCLUDE_BIN_FILE"}, }, FlagIncludeExeFile: &cli.StringFlag{ Name: FlagIncludeExeFile, Value: "", Usage: FlagIncludeExeFileUsage, EnvVars: []string{"DSLIM_INCLUDE_EXE_FILE"}, }, //// FlagObfuscateMetadata: &cli.BoolFlag{ Name: FlagObfuscateMetadata, Usage: FlagObfuscateMetadataUsage, EnvVars: []string{"DSLIM_OBFUSCATE_METADATA"}, }, } func cflag(name string) cli.Flag { cf, ok := Flags[name] if !ok { log.Fatalf("unknown flag='%s'", name) } return cf } func GetContainerBuildOptions(ctx *cli.Context) (*config.ContainerBuildOptions, error) { cbo := &config.ContainerBuildOptions{ Labels: map[string]string{}, } cbo.Dockerfile = ctx.String(FlagBuildFromDockerfile) cbo.DockerfileContext = ctx.String(FlagDockerfileContext) cbo.Tag = ctx.String(FlagTagFat) cbo.Target = ctx.String(FlagCBOTarget) cbo.NetworkMode = ctx.String(FlagCBONetwork) cbo.CacheFrom = ctx.StringSlice(FlagCBOCacheFrom) hosts := ctx.StringSlice(FlagCBOAddHost) //TODO: figure out how to encode multiple host entries to a string (docs are not helpful) cbo.ExtraHosts = strings.Join(hosts, ",") rawBuildArgs := ctx.StringSlice(FlagCBOBuildArg) for _, rba := range rawBuildArgs { //need to handle: //NAME=VALUE //"NAME"="VALUE" //NAME <- value is copied from the env var with the same name parts := strings.SplitN(rba, "=", 2) switch len(parts) { case 2: if strings.HasPrefix(parts[0], "\"") { parts[0] = strings.Trim(parts[0], "\"") parts[1] = strings.Trim(parts[1], "\"") } else { parts[0] = strings.Trim(parts[0], "'") parts[1] = strings.Trim(parts[1], "'") } ba := config.CBOBuildArg{ Name: parts[0], Value: parts[1], } cbo.BuildArgs = append(cbo.BuildArgs, ba) case 1: if envVal := os.Getenv(parts[0]); envVal != "" { ba := config.CBOBuildArg{ Name: parts[0], Value: envVal, } cbo.BuildArgs = append(cbo.BuildArgs, ba) } default: fmt.Printf("GetContainerBuildOptions(): unexpected build arg format - '%v'\n", rba) } } rawLabels := ctx.StringSlice(FlagCBOLabel) for _, rlabel := range rawLabels { parts := strings.SplitN(rlabel, "=", 2) switch len(parts) { case 2: if strings.HasPrefix(parts[0], "\"") { parts[0] = strings.Trim(parts[0], "\"") parts[1] = strings.Trim(parts[1], "\"") } else { parts[0] = strings.Trim(parts[0], "'") parts[1] = strings.Trim(parts[1], "'") } cbo.Labels[parts[0]] = parts[1] case 1: if envVal := os.Getenv(parts[0]); envVal != "" { cbo.Labels[parts[0]] = envVal } default: fmt.Printf("GetContainerBuildOptions(): unexpected label format - '%v'\n", rlabel) } } return cbo, nil } //TODO: move/share when the 'edit' command needs these flags too func GetImageInstructions(ctx *cli.Context) (*config.ImageNewInstructions, error) { entrypoint := ctx.String(FlagNewEntrypoint) cmd := ctx.String(FlagNewCmd) expose := ctx.StringSlice(FlagNewExpose) removeExpose := ctx.StringSlice(FlagRemoveExpose) instructions := &config.ImageNewInstructions{ Workdir: ctx.String(FlagNewWorkdir), Env: ctx.StringSlice(FlagNewEnv), } volumes, err := command.ParseTokenSet(ctx.StringSlice(FlagNewVolume)) if err != nil { fmt.Printf("getImageInstructions(): invalid new volume options %v\n", err) return nil, err } instructions.Volumes = volumes labels, err := command.ParseTokenMap(ctx.StringSlice(FlagNewLabel)) if err != nil { fmt.Printf("getImageInstructions(): invalid new label options %v\n", err) return nil, err } instructions.Labels = labels removeLabels, err := command.ParseTokenSet(ctx.StringSlice(FlagRemoveLabel)) if err != nil { fmt.Printf("getImageInstructions(): invalid remove label options %v\n", err) return nil, err } instructions.RemoveLabels = removeLabels removeEnvs, err := command.ParseTokenSet(ctx.StringSlice(FlagRemoveEnv)) if err != nil { fmt.Printf("getImageInstructions(): invalid remove env options %v\n", err) return nil, err } instructions.RemoveEnvs = removeEnvs removeVolumes, err := command.ParseTokenSet(ctx.StringSlice(FlagRemoveVolume)) if err != nil { fmt.Printf("getImageInstructions(): invalid remove volume options %v\n", err) return nil, err } instructions.RemoveVolumes = removeVolumes //TODO(future): also load instructions from a file if len(expose) > 0 { instructions.ExposedPorts, err = command.ParseDockerExposeOpt(expose) if err != nil { log.Errorf("getImageInstructions(): invalid expose options => %v", err) return nil, err } } if len(removeExpose) > 0 { instructions.RemoveExposedPorts, err = command.ParseDockerExposeOpt(removeExpose) if err != nil { log.Errorf("getImageInstructions(): invalid remove-expose options => %v", err) return nil, err } } instructions.Entrypoint, err = command.ParseExec(entrypoint) if err != nil { log.Errorf("getImageInstructions(): invalid entrypoint option => %v", err) return nil, err } //one space is a hacky way to indicate that you want to remove this instruction from the image instructions.ClearEntrypoint = command.IsOneSpace(entrypoint) instructions.Cmd, err = command.ParseExec(cmd) if err != nil { log.Errorf("getImageInstructions(): invalid cmd option => %v", err) return nil, err } //same hack to indicate you want to remove this instruction instructions.ClearCmd = command.IsOneSpace(cmd) return instructions, nil } func GetAppNodejsInspectOptions(ctx *cli.Context) config.AppNodejsInspectOptions { return config.AppNodejsInspectOptions{ IncludePackages: ctx.StringSlice(FlagIncludeNodePackage), NextOpts: getAppNextInspectOptions(ctx), NuxtOpts: getAppNuxtInspectOptions(ctx), } } func getAppNextInspectOptions(ctx *cli.Context) config.NodejsWebFrameworkInspectOptions { return config.NodejsWebFrameworkInspectOptions{ IncludeAppDir: ctx.Bool(FlagIncludeAppNextDir), IncludeBuildDir: ctx.Bool(FlagIncludeAppNextBuildDir), IncludeDistDir: ctx.Bool(FlagIncludeAppNextDistDir), IncludeStaticDir: ctx.Bool(FlagIncludeAppNextStaticDir), IncludeNodeModulesDir: ctx.Bool(FlagIncludeAppNextNodeModulesDir), } } func getAppNuxtInspectOptions(ctx *cli.Context) config.NodejsWebFrameworkInspectOptions { return config.NodejsWebFrameworkInspectOptions{ IncludeAppDir: ctx.Bool(FlagIncludeAppNuxtDir), IncludeBuildDir: ctx.Bool(FlagIncludeAppNuxtBuildDir), IncludeDistDir: ctx.Bool(FlagIncludeAppNuxtDistDir), IncludeStaticDir: ctx.Bool(FlagIncludeAppNuxtStaticDir), IncludeNodeModulesDir: ctx.Bool(FlagIncludeAppNuxtNodeModulesDir), } } func GetKubernetesOptions(ctx *cli.Context) (config.KubernetesOptions, error) { cfg := config.KubernetesOptions{ Target: config.KubernetesTarget{ Workload: ctx.String(command.FlagTargetKubeWorkload), Namespace: ctx.String(command.FlagTargetKubeWorkloadNamespace), Container: ctx.String(command.FlagTargetKubeWorkloadContainer), }, TargetOverride: config.KubernetesTargetOverride{ Image: ctx.String(command.FlagTargetKubeWorkloadImage), }, Manifests: ctx.StringSlice(command.FlagKubeManifestFile), Kubeconfig: ctx.String(command.FlagKubeKubeconfigFile), } if len(cfg.Target.Namespace)+len(cfg.Target.Container)+len(cfg.TargetOverride.Image)+len(cfg.Manifests) > 0 && cfg.Target.Workload == "" { return cfg, errors.New("--target-kube-workload flag must be provided") } return cfg, nil } const ( IBENone = "none" IBEInternal = "internal" IBEDocker = "docker" IBEBuildKit = "buildkit" ) func getImageBuildEngine(ctx *cli.Context) (string, error) { value := ctx.String(FlagImageBuildEngine) switch value { case IBENone, IBEInternal, IBEDocker, IBEBuildKit: return value, nil default: return "", fmt.Errorf("bad value") } } const ( ArchEmpty = "" ArchAmd64 = "amd64" ArchArm64 = "arm64" ) func getImageBuildArch(ctx *cli.Context) (string, error) { value := ctx.String(FlagImageBuildArch) switch value { case ArchEmpty, ArchAmd64, ArchArm64: return value, nil default: return "", fmt.Errorf("bad value") } } ================================================ FILE: pkg/app/master/command/build/handler.go ================================================ package build import ( "bufio" "bytes" "context" "encoding/json" "errors" "fmt" "io" "os" "path/filepath" "runtime" "strings" "time" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/app/master/compose" "github.com/slimtoolkit/slim/pkg/app/master/config" "github.com/slimtoolkit/slim/pkg/app/master/inspectors/container" "github.com/slimtoolkit/slim/pkg/app/master/inspectors/image" "github.com/slimtoolkit/slim/pkg/app/master/kubernetes" "github.com/slimtoolkit/slim/pkg/app/master/probe/http" "github.com/slimtoolkit/slim/pkg/app/master/version" cmd "github.com/slimtoolkit/slim/pkg/command" "github.com/slimtoolkit/slim/pkg/consts" "github.com/slimtoolkit/slim/pkg/docker/dockerclient" "github.com/slimtoolkit/slim/pkg/docker/dockerimage" "github.com/slimtoolkit/slim/pkg/docker/dockerutil" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/util/errutil" "github.com/slimtoolkit/slim/pkg/util/fsutil" "github.com/slimtoolkit/slim/pkg/util/printbuffer" v "github.com/slimtoolkit/slim/pkg/version" "github.com/dustin/go-humanize" dockerapi "github.com/fsouza/go-dockerclient" log "github.com/sirupsen/logrus" ) const appName = command.AppName const composeProjectNamePat = "dsbuild_%v_%v" // Build command exit codes const ( ecbOther = iota + 1 ecbBadCustomImageTag ecbImageBuildError ecbImageAlreadyOptimized ecbOnbuildBaseImage ecbNoEntrypoint ecbBadTargetComposeSvc ecbComposeSvcNoImage ecbComposeSvcUnknownImage ecbKubernetesNoWorkload ecbKubernetesNoWorkloadContainer ecbNotImplementedYet ) type ovars = app.OutVars // OnCommand implements the 'build' command func OnCommand( xc *app.ExecutionContext, gparams *command.GenericParams, targetRef string, doPull bool, dockerConfigPath string, registryAccount string, registrySecret string, doShowPullLogs bool, composeFiles []string, targetComposeSvc string, targetComposeSvcImage string, composeSvcStartWait int, composeSvcNoPorts bool, depExcludeComposeSvcAll bool, depIncludeComposeSvcDeps string, depIncludeTargetComposeSvcDeps bool, depIncludeComposeSvcs []string, depExcludeComposeSvcs []string, composeNets []string, composeEnvVars []string, composeEnvNoHost bool, composeWorkdir string, composeProjectName string, containerProbeComposeSvc string, cbOpts *config.ContainerBuildOptions, crOpts *config.ContainerRunOptions, outputTags []string, httpProbeOpts config.HTTPProbeOptions, portBindings map[dockerapi.Port][]dockerapi.PortBinding, doPublishExposedPorts bool, hostExecProbes []string, doRmFileArtifacts bool, copyMetaArtifactsLocation string, doRunTargetAsUser bool, doShowContainerLogs bool, doEnableMondel bool, doShowBuildLogs bool, imageOverrideSelectors map[string]bool, overrides *config.ContainerOverrides, instructions *config.ImageNewInstructions, links []string, etcHostsMaps []string, dnsServers []string, dnsSearchDomains []string, explicitVolumeMounts map[string]config.VolumeMount, doKeepPerms bool, pathPerms map[string]*fsutil.AccessInfo, excludePatterns map[string]*fsutil.AccessInfo, doExcludeVarLockFiles bool, preservePaths map[string]*fsutil.AccessInfo, includePaths map[string]*fsutil.AccessInfo, includeBins map[string]*fsutil.AccessInfo, includeDirBinsList map[string]*fsutil.AccessInfo, includeExes map[string]*fsutil.AccessInfo, doIncludeShell bool, doIncludeWorkdir bool, includeLastImageLayers uint, doIncludeAppImageAll bool, appImageStartInstGroup int, appImageStartInst string, appImageDockerfileInsts []string, doIncludeSSHClient bool, doIncludeOSLibsNet bool, doIncludeZoneInfo bool, doIncludeCertAll bool, doIncludeCertBundles bool, doIncludeCertDirs bool, doIncludeCertPKAll bool, doIncludeCertPKDirs bool, doIncludeNew bool, doUseLocalMounts bool, doUseSensorVolume string, doKeepTmpArtifacts bool, continueAfter *config.ContinueAfter, execCmd string, execFileCmd string, doDeleteFatImage bool, rtaOnbuildBaseImage bool, rtaSourcePT bool, doObfuscateMetadata bool, sensorIPCEndpoint string, sensorIPCMode string, kubeOpts config.KubernetesOptions, appNodejsInspectOpts config.AppNodejsInspectOptions, imageBuildEngine string, imageBuildArch string, ) { printState := true logger := log.WithFields(log.Fields{"app": appName, "cmd": Name}) viChan := version.CheckAsync(gparams.CheckVersion, gparams.InContainer, gparams.IsDSImage) cmdReport := report.NewBuildCommand(gparams.ReportLocation, gparams.InContainer) cmdReport.State = cmd.StateStarted cmdReport.TargetReference = targetRef cmdReportOnExit := func() { cmdReport.State = cmd.StateError if cmdReport.Save() { xc.Out.Info("report", ovars{ "file": cmdReport.ReportLocation(), }) } } xc.AddCleanupHandler(cmdReportOnExit) var customImageTag string var additionalTags []string if len(outputTags) > 0 { customImageTag = outputTags[0] if len(outputTags) > 1 { additionalTags = outputTags[1:] } } logger.Debugf("customImageTag='%s', additionalTags=%#v", customImageTag, additionalTags) client, err := dockerclient.New(gparams.ClientConfig) if err == dockerclient.ErrNoDockerInfo { exitMsg := "missing Docker connection info" if gparams.InContainer && gparams.IsDSImage { exitMsg = "make sure to pass the Docker connect parameters to the slim container" } xc.Out.Error("docker.connect.error", exitMsg) exitCode := command.ECTCommon | command.ECCNoDockerConnectInfo xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) cmdReport.Error = "docker.connect.error" xc.Exit(exitCode) } xc.FailOn(err) xc.Out.State("started") if kubeOpts.HasTargetSet() { xc.Out.Info("params", ovars{ "target.type": "kubernetes.workload", "target": kubeOpts.Target.Workload, "target.namespace": kubeOpts.Target.Namespace, "target.container": kubeOpts.Target.Container, "target.image": kubeOpts.TargetOverride.Image, "continue.mode": continueAfter.Mode, "image-build-engine": imageBuildEngine, }) kubeClient, err := kubernetes.NewClient(kubeOpts) xc.FailOn(err) var manifests *kubernetes.Manifests if len(kubeOpts.Manifests) > 0 { manifests, err = kubernetes.ManifestsFromFiles( kubeOpts, kubeClient, kubernetes.NewResourceBuilderFunc(kubeOpts)) xc.FailOn(err) } h := newKubeHandler( xc, context.TODO(), cmdReport, logger, client, kubeClient, kubernetes.NewKubectl(kubeOpts), kubernetes.NewWorkloadFinder(manifests, kubernetes.NewResourceBuilderFunc(kubeOpts))) h.Handle( kubeOpts.Target, kubeOpts.TargetOverride, manifests, kubeHandleOptions{ DoPull: doPull, DoShowPullLogs: doShowPullLogs, DoShowBuildLogs: doShowBuildLogs, DoShowContainerLogs: doShowContainerLogs, DoEnableMondel: doEnableMondel, DoDeleteFatImage: doDeleteFatImage, DoRmFileArtifacts: doRmFileArtifacts, CBOpts: cbOpts, RtaOnbuildBaseImage: rtaOnbuildBaseImage, RtaSourcePT: rtaSourcePT, DockerConfigPath: dockerConfigPath, RegistryAccount: registryAccount, RegistrySecret: registrySecret, KeepPerms: doKeepPerms, PathPerms: pathPerms, ArchiveState: gparams.ArchiveState, StatePath: gparams.StatePath, CopyMetaArtifactsLocation: copyMetaArtifactsLocation, Debug: gparams.Debug, LogLevel: gparams.LogLevel, LogFormat: gparams.LogFormat, SensorIPCEndpoint: sensorIPCEndpoint, CustomImageTag: customImageTag, AdditionalTags: additionalTags, httpProbeOpts: httpProbeOpts, continueAfter: continueAfter, execCmd: execCmd, imageBuildEngine: imageBuildEngine, imageBuildArch: imageBuildArch, }) vinfo := <-viChan version.PrintCheckVersion(xc, "", vinfo) return } if len(composeFiles) > 0 && targetComposeSvc != "" { xc.Out.Info("params", ovars{ "target.type": "compose.service", "target": targetRef, "continue.mode": continueAfter.Mode, "rt.as.user": doRunTargetAsUser, "keep.perms": doKeepPerms, "tags": strings.Join(outputTags, ","), "image-build-engine": imageBuildEngine, }) } else if cbOpts.Dockerfile != "" { xc.Out.Info("params", ovars{ "target.type": "dockerfile", "context": targetRef, "file": cbOpts.Dockerfile, "continue.mode": continueAfter.Mode, "rt.as.user": doRunTargetAsUser, "keep.perms": doKeepPerms, "image-build-engine": imageBuildEngine, }) } else { xc.Out.Info("params", ovars{ "target.type": "image", "target.image": targetRef, "continue.mode": continueAfter.Mode, "rt.as.user": doRunTargetAsUser, "keep.perms": doKeepPerms, "tags": strings.Join(outputTags, ","), "image-build-engine": imageBuildEngine, }) } if cbOpts.Dockerfile != "" { targetRef = buildFatImage(xc, targetRef, customImageTag, cbOpts, doShowBuildLogs, client, cmdReport) } var serviceAliases []string var depServicesExe *compose.Execution var baseVolumesFrom []string var baseMounts []dockerapi.HostMount if len(composeFiles) > 0 { if targetComposeSvc != "" && depIncludeComposeSvcDeps != targetComposeSvc { var found bool for _, svcName := range depExcludeComposeSvcs { if svcName == targetComposeSvc { found = true break } } if !found { //exclude the target service if the target service is not excluded already depExcludeComposeSvcs = append(depExcludeComposeSvcs, targetComposeSvc) } } if depIncludeTargetComposeSvcDeps { depIncludeComposeSvcDeps = targetComposeSvc } selectors := compose.NewServiceSelectors( depIncludeComposeSvcDeps, depIncludeComposeSvcs, depExcludeComposeSvcs) //todo: move compose flags to options options := &compose.ExecutionOptions{ SvcStartWait: composeSvcStartWait, } logger.Debugf("compose: file(s)='%s' selectors='%+v'\n", strings.Join(composeFiles, ","), selectors) if composeProjectName == "" { composeProjectName = fmt.Sprintf(composeProjectNamePat, os.Getpid(), time.Now().UTC().Format("20060102150405")) } exe, err := compose.NewExecution(xc, logger, client, composeFiles, selectors, composeProjectName, composeWorkdir, composeEnvVars, composeEnvNoHost, containerProbeComposeSvc, false, //buildImages doPull, nil, //pullExcludes (todo: add a flag) true, //ownAllResources options, nil, //eventCh printState) xc.FailOn(err) if !depExcludeComposeSvcAll && !exe.SelectedHaveImages() { xc.Out.Info("compose.file.error", ovars{ "status": "service.no.image", "files": strings.Join(composeFiles, ","), }) exitCode := command.ECTBuild | ecbComposeSvcNoImage xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Exit(exitCode) } if targetComposeSvc != "" { targetSvcInfo := exe.Service(targetComposeSvc) if targetSvcInfo == nil { xc.Out.Info("target.compose.svc.error", ovars{ "status": "unknown.compose.service", "target": targetComposeSvc, "files": strings.Join(composeFiles, ","), }) exitCode := command.ECTBuild | ecbBadTargetComposeSvc xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Exit(exitCode) } serviceAliases = append(serviceAliases, targetSvcInfo.Config.Name) targetRef = targetSvcInfo.Config.Image if targetComposeSvcImage != "" { targetRef = command.UpdateImageRef(logger, targetRef, targetComposeSvcImage) //shouldn't need to targetSvcInfo.Config.Image = targetRef logger.Debugf("using target service override '%s' -> '%s' ", targetComposeSvcImage, targetRef) } if len(targetSvcInfo.Config.Entrypoint) > 0 { logger.Debug("using targetSvcInfo.Config.Entrypoint") overrides.Entrypoint = targetSvcInfo.Config.Entrypoint } if len(targetSvcInfo.Config.Command) > 0 { logger.Debug("using targetSvcInfo.Config.Command") overrides.Cmd = targetSvcInfo.Config.Command } if overrides.Workdir == "" { overrides.Workdir = targetSvcInfo.Config.WorkingDir } if overrides.Hostname == "" { overrides.Hostname = targetSvcInfo.Config.Hostname } labelMap := map[string]string{} for k, v := range targetSvcInfo.Config.Labels { labelMap[k] = v } for k, v := range overrides.Labels { labelMap[k] = v } overrides.Labels = labelMap if overrides.User != "" { overrides.User = targetSvcInfo.Config.User } //todo: add command flags for these fields too //targetSvcInfo.Config.DomainName //env vars //the env vars from compose are already "resolved" and must be "k=v" svcEnvVars := compose.EnvVarsFromService( targetSvcInfo.Config.Environment, targetSvcInfo.Config.EnvFile) emap := map[string]string{} //start with compose env vars for _, env := range svcEnvVars { envComponents := strings.SplitN(env, "=", 2) if len(envComponents) == 2 { emap[envComponents[0]] = envComponents[1] } else { logger.Debugf("svcEnvVars - unexpected env var: '%s'", env) } } //then use env vars from overrides for _, env := range overrides.Env { envComponents := strings.SplitN(env, "=", 2) if len(envComponents) == 2 { emap[envComponents[0]] = envComponents[1] } else { logger.Debugf("overrides.Env - unexpected env var: '%s'", env) } } // combine into overrides var combineEnv []string for key, val := range emap { variable := fmt.Sprintf("%s=%s", key, val) combineEnv = append(combineEnv, variable) } overrides.Env = combineEnv logger.Debugf("compose: Environment_Variables='%v'\n", overrides.Env) //expose ports svcExposedPorts := compose.ExposedPorts(targetSvcInfo.Config.Expose, targetSvcInfo.Config.Ports) if len(svcExposedPorts) > 0 && overrides.ExposedPorts == nil { overrides.ExposedPorts = map[dockerapi.Port]struct{}{} } for k, v := range svcExposedPorts { overrides.ExposedPorts[k] = v } //publish ports if !composeSvcNoPorts { logger.Debug("using targetSvcInfo.Config.Ports") for _, p := range targetSvcInfo.Config.Ports { portKey := fmt.Sprintf("%v/%v", p.Target, p.Protocol) pbSet, found := portBindings[dockerapi.Port(portKey)] if found { found := false hostPort := fmt.Sprintf("%v", p.Published) for _, pinfo := range pbSet { if pinfo.HostIP == p.HostIP && pinfo.HostPort == hostPort { found = true break } } if !found { pbSet = append(pbSet, dockerapi.PortBinding{ HostIP: p.HostIP, HostPort: hostPort, }) portBindings[dockerapi.Port(portKey)] = pbSet } } else { portBindings[dockerapi.Port(portKey)] = []dockerapi.PortBinding{{ HostIP: p.HostIP, HostPort: fmt.Sprintf("%v", p.Published), }} } } } //make sure not to shadow baseMounts baseMounts, err = compose.MountsFromVolumeConfigs( exe.BaseComposeDir, targetSvcInfo.Config.Volumes, targetSvcInfo.Config.Tmpfs, exe.ActiveVolumes) xc.FailOn(err) logger.Debugf("compose targetSvcInfo - baseMounts(%d)", len(baseMounts)) baseVolumesFrom = compose.VolumesFrom(exe.AllServiceNames, targetSvcInfo.Config.VolumesFrom) logger.Debugf("compose targetSvcInfo - baseVolumesFrom(%d)", len(baseVolumesFrom)) } if !depExcludeComposeSvcAll { depServicesExe = exe } } logger.Infof("image=%v http-probe=%v remove-file-artifacts=%v image-overrides=%+v entrypoint=%+v (%v) cmd=%+v (%v) workdir='%v' env=%+v expose=%+v", targetRef, httpProbeOpts.Do, doRmFileArtifacts, imageOverrideSelectors, overrides.Entrypoint, overrides.ClearEntrypoint, overrides.Cmd, overrides.ClearCmd, overrides.Workdir, overrides.Env, overrides.ExposedPorts) if gparams.Debug { version.Print(xc, Name, logger, client, false, gparams.InContainer, gparams.IsDSImage) } if overrides.Network == "host" && runtime.GOOS == "darwin" { xc.Out.Info("param.error", ovars{ "status": "unsupported.network.mac", "value": overrides.Network, }) exitCode := command.ECTCommon | command.ECCBadNetworkName xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Exit(exitCode) } if !command.ConfirmNetwork(logger, client, overrides.Network) { xc.Out.Info("param.error", ovars{ "status": "unknown.network", "value": overrides.Network, }) exitCode := command.ECTCommon | command.ECCBadNetworkName xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Exit(exitCode) } imageInspector, localVolumePath, statePath, stateKey := inspectFatImage( xc, targetRef, doPull, doShowPullLogs, rtaOnbuildBaseImage, dockerConfigPath, registryAccount, registrySecret, gparams.StatePath, client, logger, cmdReport) loadExtraIncludePaths := func() { if (includeLastImageLayers > 0) || (appImageStartInstGroup > -1) || (appImageStartInst != "") || (len(appImageDockerfileInsts) > 0) { logger.Debugf("loadExtraIncludePaths: includeLastImageLayers=%v appImageStartInstGroup=%v appImageStartInst='%v' len.appImageDockerfileInsts=%v", includeLastImageLayers, appImageStartInstGroup, appImageStartInst, len(appImageDockerfileInsts)) includeLayerPaths := map[string]*fsutil.AccessInfo{} imageID := dockerutil.CleanImageID(imageInspector.ImageInfo.ID) iaName := fmt.Sprintf("%s.tar", imageID) iaPath := filepath.Join(localVolumePath, "image", iaName) iaPathReady := fmt.Sprintf("%s.ready", iaPath) var doSave bool if fsutil.IsRegularFile(iaPath) { //if !doReuseSavedImage { // doSave = true //} if !fsutil.Exists(iaPathReady) { doSave = true } } else { doSave = true } if doSave { if fsutil.Exists(iaPathReady) { fsutil.Remove(iaPathReady) } xc.Out.Info("image.data.inspection.save.image.start") err = dockerutil.SaveImage(client, imageID, iaPath, false, false) errutil.FailOn(err) err = fsutil.Touch(iaPathReady) errutil.WarnOn(err) xc.Out.Info("image.data.inspection.save.image.end") } else { logger.Debugf("exported image already exists - %s", iaPath) } xc.Out.Info("image.data.inspection.list.files.start") imgFiles, err := dockerimage.NewPackageFiles(iaPath) if err != nil { logger.Errorf("loadExtraIncludePaths: dockerimage.NewPackageFiles(%v) error - %v", iaPath, err) } else { layerCount := imgFiles.LayerCount() if (layerCount > 0) && ((appImageStartInstGroup > -1) || (appImageStartInst != "") || (len(appImageDockerfileInsts) > 0)) && doIncludeAppImageAll { //TODO: refactor the condition logic //appImageStartInstGroup - reverse index history, err := imgFiles.ListImageHistory() if err != nil { logger.Errorf("loadExtraIncludePaths: imgFiles.ListImageHistory() - error - %v", err) return } layers, err := imgFiles.ListLayerMetadata() if err != nil { logger.Errorf("loadExtraIncludePaths: imgFiles.ListLayerMetadata() - error - %v", err) return } if len(imageInspector.DockerfileInfo.AllInstructions) != len(history) { logger.Errorf("loadExtraIncludePaths: instruction count (%d) != history count (%d)", len(imageInspector.DockerfileInfo.AllInstructions), len(history)) return } appImageStartInstIndex := -1 appImageStartLayerIndex := -1 //var appImageStartLayerInfo *dockerimage.LayerMetadata //calculate the start instruction index by iterating over df instruction list var instCount int if len(appImageDockerfileInsts) > 0 { //use the instructions from the last 'stage' (first 'FROM' in reverse) for i := len(appImageDockerfileInsts) - 1; i > -1; i-- { if strings.HasPrefix(appImageDockerfileInsts[i], "FROM ") { logger.Tracef("loadExtraIncludePaths: app image dockerfile (reverse) instruction count - [%v] %v", i, instCount) break } if strings.HasPrefix(appImageDockerfileInsts[i], "ARG ") { continue } instCount++ } } if instCount > 0 { //NOTE: prefer reverse instruction count from the app image Dockerfile for current, idx := 0, len(imageInspector.DockerfileInfo.AllInstructions)-1; idx > -1; idx-- { current++ if current == instCount { appImageStartInstIndex = idx logger.Tracef("loadExtraIncludePaths: app image start instruction from app dockerfile - [%v][%v] %#v", instCount, idx, imageInspector.DockerfileInfo.AllInstructions[idx]) break } } } else { for idx, instInfo := range imageInspector.DockerfileInfo.AllInstructions { if appImageStartInst != "" { //NOTE: prefer to use the start instruction prefix if it's provided if strings.HasPrefix(instInfo.CommandAll, appImageStartInst) { //use the fist match appImageStartInstIndex = idx logger.Tracef("loadExtraIncludePaths: app image start instruction match - [%v] %#v", idx, instInfo) break } } else { if instInfo.InstSetTimeReverseIndex == appImageStartInstGroup { appImageStartInstIndex = idx logger.Tracef("loadExtraIncludePaths: app image start instruction group match - [%v] => [%v] %#v", appImageStartInstGroup, idx, instInfo) break } } } } if appImageStartInstIndex > -1 { rawLayerCount := 0 for hidx, record := range history { if hidx == appImageStartInstIndex { //start layer index is the layer that follows //the last layer we've seen already appImageStartLayerIndex = rawLayerCount break } if !record.EmptyLayer { rawLayerCount++ } if rawLayerCount >= layerCount { break } } if appImageStartLayerIndex > -1 { startLayerInfo := layers[appImageStartLayerIndex] logger.Tracef("loadExtraIncludePaths: app image start layer - %#v", startLayerInfo) if doIncludeAppImageAll { selectors := []dockerimage.FileSelector{ { Type: dockerimage.FSTIndexRange, IndexKey: appImageStartLayerIndex, IndexEndKey: layerCount - 1, NoDirs: true, Deleted: false, }, } if layerFiles, err := imgFiles.ListLayerFiles(selectors); err != nil { logger.Errorf("loadExtraIncludePaths: imgFiles.ListLayerFiles() error - %v", err) } else { for _, lf := range layerFiles { logger.Tracef("loadExtraIncludePaths: [ALS] layerFiles=%v/%v/%v fileCount=%v", lf.Layer.Index, lf.Layer.Digest, lf.Layer.DiffID, len(lf.Files)) for _, fileInfo := range lf.Files { logger.Tracef("loadExtraIncludePaths: [ALS] layerFiles.File=%v", fileInfo.Name) includeLayerPaths[fileInfo.Name] = nil } } } } else { logger.Debugf("loadExtraIncludePaths: doIncludeAppImageAll=false") } } else { logger.Debug("loadExtraIncludePaths: no layer instructions found") } } else { logger.Debug("loadExtraIncludePaths: no appImageStartInstIndex") } } if includeLastImageLayers > 0 { if includeLastImageLayers > uint(layerCount) { logger.Debugf("includeLastImageLayers(%v) > layerCount(%v)", includeLastImageLayers, layerCount) includeLastImageLayers = uint(layerCount) } selectors := []dockerimage.FileSelector{ { Type: dockerimage.FSTIndexRange, IndexKey: 0, IndexEndKey: int(includeLastImageLayers) - 1, ReverseIndexRange: true, NoDirs: true, Deleted: false, }, } if layerFiles, err := imgFiles.ListLayerFiles(selectors); err != nil { logger.Errorf("imgFiles.ListLayerFiles() error - %v", err) } else { for _, lf := range layerFiles { logger.Tracef("layerFiles=%v/%v/%v fileCount=%v", lf.Layer.Index, lf.Layer.Digest, lf.Layer.DiffID, len(lf.Files)) for _, fileInfo := range lf.Files { logger.Tracef("layerFiles.File=%v", fileInfo.Name) includeLayerPaths[fileInfo.Name] = nil } } } } } xc.Out.Info("image.data.inspection.list.files.end") for k := range includeLayerPaths { includePaths[k] = nil } } } loadExtraIncludePaths() //refresh the target refs targetRef = imageInspector.ImageRef //validate links (check if target container exists, ignore&log if not) svcLinkMap := map[string]struct{}{} for _, linkInfo := range links { svcLinkMap[linkInfo] = struct{}{} } selectedNetNames := map[string]compose.NetNameInfo{} if depServicesExe != nil { xc.Out.State("container.dependencies.init.start") err = depServicesExe.Prepare() if err != nil { var svcErr *compose.ServiceError if errors.As(err, &svcErr) { xc.Out.Info("compose.file.error", ovars{ "status": "deps.unknown.image", "files": strings.Join(composeFiles, ","), "service": svcErr.Service, "pull.enabled": doPull, "message": "Unknown dependency image (make sure to pull or build the images for your dependencies in compose)", }) exitCode := command.ECTBuild | ecbComposeSvcUnknownImage xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Exit(exitCode) } xc.FailOn(err) } err = depServicesExe.Start() if err != nil { depServicesExe.Stop() depServicesExe.Cleanup() } xc.FailOn(err) exeCleanup := func() { if depServicesExe != nil { xc.Out.State("container.dependencies.shutdown.start") err = depServicesExe.Stop() errutil.WarnOn(err) err = depServicesExe.Cleanup() errutil.WarnOn(err) xc.Out.State("container.dependencies.shutdown.done") } } xc.AddCleanupHandler(exeCleanup) //todo: //need a better way to make sure the dependencies are ready //monitor docker events //use basic application level checks (probing) time.Sleep(3 * time.Second) xc.Out.State("container.dependencies.init.done") //might need more info (including alias info) when targeting compose services allNetNames := depServicesExe.ActiveNetworkNames() if targetComposeSvc != "" { //if we are targeting a compose service, and we //have explicitly selected compose networks (composeNets) //we use the selected subset of the configured networks for the target service composeNetsSet := map[string]struct{}{} for _, key := range composeNets { composeNetsSet[key] = struct{}{} } svcNets := depServicesExe.ActiveServiceNetworks(targetComposeSvc) for key, netNameInfo := range svcNets { if len(composeNets) > 0 { if _, found := composeNetsSet[key]; !found { continue } } selectedNetNames[key] = netNameInfo } } else { //we are not targeting a compose service, //but we do want to connect to the networks in compose if len(composeNets) > 0 { for _, key := range composeNets { if net, found := allNetNames[key]; found { selectedNetNames[key] = compose.NetNameInfo{ FullName: net, //Aliases: serviceAliases, - we merge serviceAliases later } } } } else { //select/use all networks if specific networks are not selected for key, fullName := range allNetNames { selectedNetNames[key] = compose.NetNameInfo{ FullName: fullName, //Aliases: serviceAliases, - we merge serviceAliases later } } } } } links = []string{} //reset&reuse if targetComposeSvc != "" && depServicesExe != nil { targetSvcInfo := depServicesExe.Service(targetComposeSvc) //convert service links to container links (after deps are started) targetSvcLinkMap := map[string]struct{}{} for _, linkInfo := range targetSvcInfo.Config.Links { var linkTarget string var linkName string parts := strings.Split(linkInfo, ":") switch len(parts) { case 1: linkTarget = parts[0] linkName = parts[0] case 2: linkTarget = parts[0] linkName = parts[1] default: logger.Debugf("targetSvcInfo.Config.Links: malformed link - %s", linkInfo) continue } linkSvcInfo := depServicesExe.Service(linkTarget) if linkSvcInfo == nil { logger.Debugf("targetSvcInfo.Config.Links: unknown service in link - %s", linkInfo) continue } logger.Debugf("targetSvcInfo.Config.Links: linkInfo=%s linkSvcInfo=%#v", linkInfo, linkSvcInfo) if linkSvcInfo.ContainerName == "" { logger.Debugf("targetSvcInfo.Config.Links: no container name - linkInfo=%s", linkInfo) continue } clink := fmt.Sprintf("%s:%s", linkSvcInfo.ContainerName, linkSvcInfo.ContainerName) targetSvcLinkMap[clink] = struct{}{} clink = fmt.Sprintf("%s:%s", linkSvcInfo.ContainerName, linkName) targetSvcLinkMap[clink] = struct{}{} } for k := range targetSvcLinkMap { links = append(links, k) } } for k := range svcLinkMap { links = append(links, k) } selectedNetworks := map[string]container.NetNameInfo{} for key, info := range selectedNetNames { aset := map[string]struct{}{} for _, a := range info.Aliases { aset[a] = struct{}{} } //merge serviceAliases with the main set of aliases for _, a := range serviceAliases { aset[a] = struct{}{} } var alist []string for a := range aset { alist = append(alist, a) } selectedNetworks[key] = container.NetNameInfo{ Name: key, FullName: info.FullName, Aliases: alist, } } xc.Out.State("container.inspection.start") hasClassicLinks := true if targetComposeSvc != "" || len(composeNets) > 0 || overrides.Network != "" { hasClassicLinks = false } containerInspector, err := container.NewInspector( xc, crOpts, logger, client, statePath, imageInspector, localVolumePath, doUseLocalMounts, doUseSensorVolume, doKeepTmpArtifacts, overrides, explicitVolumeMounts, baseMounts, baseVolumesFrom, portBindings, doPublishExposedPorts, hasClassicLinks, links, etcHostsMaps, dnsServers, dnsSearchDomains, doShowContainerLogs, doEnableMondel, doRunTargetAsUser, doKeepPerms, pathPerms, excludePatterns, doExcludeVarLockFiles, preservePaths, includePaths, includeBins, includeDirBinsList, includeExes, doIncludeShell, doIncludeWorkdir, doIncludeCertAll, doIncludeCertBundles, doIncludeCertDirs, doIncludeCertPKAll, doIncludeCertPKDirs, doIncludeNew, doIncludeSSHClient, doIncludeOSLibsNet, doIncludeZoneInfo, selectedNetworks, gparams.Debug, gparams.LogLevel, gparams.LogFormat, gparams.InContainer, rtaSourcePT, doObfuscateMetadata, sensorIPCEndpoint, sensorIPCMode, printState, appNodejsInspectOpts) xc.FailOn(err) if len(containerInspector.FatContainerCmd) == 0 { xc.Out.Info("target.image.error", ovars{ "status": "no.entrypoint.cmd", "image": targetRef, "message": "no ENTRYPOINT/CMD", }) exitCode := command.ECTBuild | ecbNoEntrypoint xc.Out.State("exited", ovars{"exit.code": exitCode}) cmdReport.Error = "no.entrypoint.cmd" xc.Exit(exitCode) } logger.Info("starting instrumented 'fat' container...") err = containerInspector.RunContainer() if err != nil { containerInspector.ShowContainerLogs() containerInspector.ShutdownContainer(true) xc.FailOn(err) } containerName := containerInspector.ContainerName containerID := containerInspector.ContainerID inspectorCleanup := func() { xc.Out.Info("container.inspector.cleanup", ovars{ "name": containerName, "id": containerID, }) if containerInspector != nil { xc.Out.State("container.target.shutdown.start") containerInspector.FinishMonitoring() _ = containerInspector.ShutdownContainer(false) xc.Out.State("container.target.shutdown.done") } } xc.AddCleanupHandler(inspectorCleanup) xc.Out.Info("container", ovars{ "name": containerInspector.ContainerName, "id": containerInspector.ContainerID, "target.port.list": containerInspector.ContainerPortList, "target.port.info": containerInspector.ContainerPortsInfo, "message": "YOU CAN USE THESE PORTS TO INTERACT WITH THE CONTAINER", }) logger.Info("watching container monitor...") monitorContainer( xc, targetRef, continueAfter, execCmd, execFileCmd, httpProbeOpts, hostExecProbes, depServicesExe, containerProbeComposeSvc, containerInspector, client, cmdReport, printState) xc.Out.State("container.inspection.finishing") containerInspector.FinishMonitoring() logger.Info("shutting down 'fat' container...") err = containerInspector.ShutdownContainer(false) errutil.WarnOn(err) if depServicesExe != nil { xc.Out.State("container.dependencies.shutdown.start") err = depServicesExe.Stop() errutil.WarnOn(err) err = depServicesExe.Cleanup() errutil.WarnOn(err) xc.Out.State("container.dependencies.shutdown.done") } xc.Out.State("container.inspection.artifact.processing") if !containerInspector.HasCollectedData() { imageInspector.ShowFatImageDockerInstructions() xc.Out.Info("results", ovars{ "status": "no data collected (no minified image generated)", "version": v.Current(), "location": fsutil.ExeDir(), }) exitCode := command.ECTBuild | ecbImageBuildError xc.Out.State("exited", ovars{ "exit.code": exitCode, }) cmdReport.Error = "no.data.collected" xc.Exit(exitCode) } logger.Info("processing instrumented 'fat' container info...") err = containerInspector.ProcessCollectedData() xc.FailOn(err) xc.Out.State("container.inspection.done") minifiedImageName := buildOutputImage( xc, customImageTag, additionalTags, cbOpts, overrides, imageOverrideSelectors, instructions, doDeleteFatImage, doShowBuildLogs, imageInspector, client, logger, cmdReport, imageBuildEngine, imageBuildArch) finishCommand( xc, minifiedImageName, copyMetaArtifactsLocation, doRmFileArtifacts, gparams.ArchiveState, stateKey, imageInspector, client, logger, cmdReport, imageBuildEngine) vinfo := <-viChan version.PrintCheckVersion(xc, "", vinfo) } func monitorContainer( xc *app.ExecutionContext, targetRef string, continueAfter *config.ContinueAfter, execCmd string, execFileCmd string, httpProbeOpts config.HTTPProbeOptions, hostExecProbes []string, depServicesExe *compose.Execution, containerProbeComposeSvc string, containerInspector *container.Inspector, client *dockerapi.Client, cmdReport *report.BuildCommand, printState bool, ) { if hasContinueAfterMode(continueAfter.Mode, config.CAMProbe) { httpProbeOpts.Do = true } var probe *http.CustomProbe if httpProbeOpts.Do { var err error probe, err = http.NewContainerProbe(xc, containerInspector, httpProbeOpts, printState) xc.FailOn(err) if len(probe.Ports()) == 0 { xc.Out.State("http.probe.error", ovars{ "error": "NO EXPOSED PORTS", "message": "expose your service port with --expose or disable HTTP probing with --http-probe=false if your containerized application doesnt expose any network services", }) //note: should be handled by inspectorCleanup //logger.Info("shutting down 'fat' container...") //containerInspector.FinishMonitoring() //_ = containerInspector.ShutdownContainer(false) exitCode := command.ECTBuild | ecbImageBuildError xc.Out.State("exited", ovars{ "exit.code": exitCode, }) cmdReport.Error = "no.exposed.ports" xc.Exit(exitCode) } probe.Start() continueAfter.ContinueChan = probe.DoneChan() } continueAfterMsg := "provide the expected input to allow the container inspector to continue its execution" if continueAfter.Mode == config.CAMTimeout { continueAfterMsg = "no input required, execution will resume after the timeout" } if hasContinueAfterMode(continueAfter.Mode, config.CAMProbe) { continueAfterMsg = "no input required, execution will resume when HTTP probing is completed" } xc.Out.Info("continue.after", ovars{ "mode": continueAfter.Mode, "message": continueAfterMsg, }) execFail := false modes := command.GetContinueAfterModeNames(continueAfter.Mode) for _, mode := range modes { //should work for the most parts except //when probe and signal are combined //because both need channels (TODO: fix) switch mode { case config.CAMContainerProbe: idsToLog := map[string]string{} idsToLog[targetRef] = containerInspector.ContainerID for name, svc := range depServicesExe.RunningServices { idsToLog[name] = svc.ID } //TODO: //need a flag to control logs for dep services //also good to leverage the logging capabilities in compose (TBD) for name, id := range idsToLog { name := name id := id go func() { err := client.Logs(dockerapi.LogsOptions{ Container: id, OutputStream: NewLogWriter(name + "-stdout"), ErrorStream: NewLogWriter(name + "-stderr"), Follow: true, Stdout: true, Stderr: true, }) xc.FailOn(err) }() } svc, ok := depServicesExe.RunningServices[containerProbeComposeSvc] if !ok { xc.Out.State("error", ovars{"message": "container-prove-compose-svc not found in running services"}) xc.Exit(1) } for { c, err := client.InspectContainerWithOptions(dockerapi.InspectContainerOptions{ ID: svc.ID, }) xc.FailOn(err) if c.State.Running { xc.Out.Info("wait for container.probe to finish") } else { if c.State.ExitCode != 0 { xc.Out.State("exited", ovars{"container.probe exit.code": c.State.ExitCode}) xc.Exit(1) } break } time.Sleep(1 * time.Second) } case config.CAMEnter: xc.Out.Prompt("USER INPUT REQUIRED, PRESS WHEN YOU ARE DONE USING THE CONTAINER") creader := bufio.NewReader(os.Stdin) _, _, _ = creader.ReadLine() case config.CAMExec: var input *bytes.Buffer var cmd []string if len(execFileCmd) != 0 { input = bytes.NewBufferString(execFileCmd) cmd = []string{"sh", "-s"} for _, line := range strings.Split(string(execFileCmd), "\n") { xc.Out.Info("continue.after", ovars{ "mode": config.CAMExec, "shell": line, }) } } else { input = bytes.NewBufferString("") cmd = []string{"sh", "-c", execCmd} xc.Out.Info("continue.after", ovars{ "mode": config.CAMExec, "shell": execCmd, }) } exec, err := containerInspector.APIClient.CreateExec(dockerapi.CreateExecOptions{ Container: containerInspector.ContainerID, Cmd: cmd, AttachStdin: true, AttachStdout: true, AttachStderr: true, }) xc.FailOn(err) buffer := &printbuffer.PrintBuffer{Prefix: fmt.Sprintf("%s[%s][exec]: output:", appName, Name)} xc.FailOn(containerInspector.APIClient.StartExec(exec.ID, dockerapi.StartExecOptions{ InputStream: input, OutputStream: buffer, ErrorStream: buffer, })) inspect, err := containerInspector.APIClient.InspectExec(exec.ID) xc.FailOn(err) errutil.FailWhen(inspect.Running, "still running") if inspect.ExitCode != 0 { execFail = true } xc.Out.Info("continue.after", ovars{ "mode": config.CAMExec, "exitcode": inspect.ExitCode, }) case config.CAMSignal: xc.Out.Prompt("send SIGUSR1 when you are done using the container") <-continueAfter.ContinueChan xc.Out.Info("event", ovars{ "message": "got SIGUSR1", }) case config.CAMTimeout: xc.Out.Prompt(fmt.Sprintf("waiting for the target container (%v seconds)", int(continueAfter.Timeout))) <-time.After(time.Second * continueAfter.Timeout) xc.Out.Info("event", ovars{ "message": "done waiting for the target container", }) case config.CAMProbe: xc.Out.Prompt("waiting for the HTTP probe to finish") <-continueAfter.ContinueChan xc.Out.Info("event", ovars{ "message": "HTTP probe is done", }) if probe != nil && probe.CallCount > 0 && probe.OkCount == 0 && httpProbeOpts.ExitOnFailure { xc.Out.Error("probe.error", "no.successful.calls") containerInspector.ShowContainerLogs() xc.Out.State("exited", ovars{"exit.code": -1}) xc.Exit(-1) } case config.CAMHostExec: command.RunHostExecProbes(printState, xc, hostExecProbes) case config.CAMAppExit: xc.Out.Prompt("waiting for the target app to exit") //TBD default: errutil.Fail("unknown continue-after mode") } } if execFail { xc.Out.Info("continue.after", ovars{ "mode": config.CAMExec, "message": "fatal: exec cmd failure", }) exitCode := 1 xc.Out.State("exited", ovars{ "exit.code": exitCode, }) cmdReport.Error = "exec.cmd.failure" xc.Exit(exitCode) } } func finishCommand( xc *app.ExecutionContext, minifiedImageName string, copyMetaArtifactsLocation string, doRmFileArtifacts bool, archiveState string, stateKey string, imageInspector *image.Inspector, client *dockerapi.Client, logger *log.Entry, cmdReport *report.BuildCommand, imageBuildEngine string, ) { newImageInspector, err := image.NewInspector(client, minifiedImageName) xc.FailOn(err) noImage, err := imageInspector.NoImage() errutil.FailOn(err) if noImage { xc.Out.Info("results", ovars{ "message": "minified image not found", "image": minifiedImageName, }) exitCode := command.ECTBuild | ecbImageBuildError xc.Out.State("exited", ovars{ "exit.code": exitCode, }) cmdReport.Error = "minified.image.not.found" xc.Exit(exitCode) } err = newImageInspector.Inspect() errutil.WarnOn(err) if err == nil { cmdReport.MinifiedBy = float64(imageInspector.ImageInfo.VirtualSize) / float64(newImageInspector.ImageInfo.VirtualSize) imgIdentity := dockerutil.ImageToIdentity(imageInspector.ImageInfo) cmdReport.SourceImage = report.ImageMetadata{ Identity: report.ImageIdentity{ ID: imgIdentity.ID, Tags: imgIdentity.ShortTags, Names: imgIdentity.RepoTags, Digests: imgIdentity.ShortDigests, FullDigests: imgIdentity.RepoDigests, }, Size: imageInspector.ImageInfo.VirtualSize, SizeHuman: humanize.Bytes(uint64(imageInspector.ImageInfo.VirtualSize)), CreateTime: imageInspector.ImageInfo.Created.UTC().Format(time.RFC3339), Author: imageInspector.ImageInfo.Author, DockerVersion: imageInspector.ImageInfo.DockerVersion, Architecture: imageInspector.ImageInfo.Architecture, User: imageInspector.ImageInfo.Config.User, OS: imageInspector.ImageInfo.OS, } if cmdReport.MinifiedImageID != newImageInspector.ImageInfo.ID { logger.Errorf("finishCommand: output image ID mismatch '%s' != '%s'", cmdReport.MinifiedImageID, newImageInspector.ImageInfo.ID) } for k := range imageInspector.ImageInfo.Config.ExposedPorts { cmdReport.SourceImage.ExposedPorts = append(cmdReport.SourceImage.ExposedPorts, string(k)) } for k := range imageInspector.ImageInfo.Config.Volumes { cmdReport.SourceImage.Volumes = append(cmdReport.SourceImage.Volumes, k) } cmdReport.SourceImage.Labels = imageInspector.ImageInfo.Config.Labels cmdReport.SourceImage.EnvVars = imageInspector.ImageInfo.Config.Env cmdReport.MinifiedImageSize = newImageInspector.ImageInfo.VirtualSize cmdReport.MinifiedImageSizeHuman = humanize.Bytes(uint64(newImageInspector.ImageInfo.VirtualSize)) xc.Out.Info("results", ovars{ "status": "MINIFIED", "by": fmt.Sprintf("%.2fX", cmdReport.MinifiedBy), "size.original": cmdReport.SourceImage.SizeHuman, "size.optimized": cmdReport.MinifiedImageSizeHuman, }) } else { cmdReport.State = cmd.StateError cmdReport.Error = err.Error() } cmdReport.ArtifactLocation = imageInspector.ArtifactLocation cmdReport.ContainerReportName = report.DefaultContainerReportFileName cmdReport.SeccompProfileName = imageInspector.SeccompProfileName cmdReport.AppArmorProfileName = imageInspector.AppArmorProfileName //todo: //need to enhance the 'docker' image builder to provide //the output image metadata (until then we have this quick 'fix') if cmdReport.MinifiedImageID == "" { cmdReport.MinifiedImageID = newImageInspector.ImageInfo.ID //also need the digest... } xc.Out.Info("results", ovars{ "image-build-engine": imageBuildEngine, "image.name": cmdReport.MinifiedImage, "image.size": cmdReport.MinifiedImageSizeHuman, "image.id": cmdReport.MinifiedImageID, "image.digest": cmdReport.MinifiedImageDigest, "has.data": cmdReport.MinifiedImageHasData, }) xc.Out.Info("results", ovars{ "artifacts.location": cmdReport.ArtifactLocation, }) xc.Out.Info("results", ovars{ "artifacts.report": cmdReport.ContainerReportName, }) xc.Out.Info("results", ovars{ "artifacts.dockerfile.reversed": consts.ReversedDockerfile, }) if imageBuildEngine == IBEDocker || imageBuildEngine == IBEBuildKit { //no minified Dockerfile when using IBEInternal (or IBENone) xc.Out.Info("results", ovars{ "artifacts.dockerfile.minified": "Dockerfile", }) } xc.Out.Info("results", ovars{ "artifacts.seccomp": cmdReport.SeccompProfileName, }) xc.Out.Info("results", ovars{ "artifacts.apparmor": cmdReport.AppArmorProfileName, }) if cmdReport.ArtifactLocation != "" { creportPath := filepath.Join(cmdReport.ArtifactLocation, cmdReport.ContainerReportName) if creportData, err := os.ReadFile(creportPath); err == nil { var creport report.ContainerReport if err := json.Unmarshal(creportData, &creport); err == nil { cmdReport.System = report.SystemMetadata{ Type: creport.System.Type, Release: creport.System.Release, Distro: creport.System.Distro, } } else { logger.Infof("could not read container report - json parsing error - %v", err) } } else { logger.Infof("could not read container report - %v", err) } } ///////////////////////////// if copyMetaArtifactsLocation != "" { toCopy := []string{ report.DefaultContainerReportFileName, imageInspector.SeccompProfileName, imageInspector.AppArmorProfileName, } if !command.CopyMetaArtifacts(logger, toCopy, imageInspector.ArtifactLocation, copyMetaArtifactsLocation) { xc.Out.Info("artifacts", ovars{ "message": "could not copy meta artifacts", }) } } if err := command.DoArchiveState(logger, client, imageInspector.ArtifactLocation, archiveState, stateKey); err != nil { xc.Out.Info("state", ovars{ "message": "could not archive state", }) logger.Errorf("error archiving state - %v", err) } if doRmFileArtifacts { logger.Info("removing temporary artifacts...") err = fsutil.Remove(imageInspector.ArtifactLocation) errutil.WarnOn(err) } xc.Out.State("done") xc.Out.Info("commands", ovars{ "message": "use the xray command to learn more about the optimize image", }) cmdReport.State = cmd.StateDone if cmdReport.Save() { xc.Out.Info("report", ovars{ "file": cmdReport.ReportLocation(), }) } } func hasContinueAfterMode(modeSet, mode string) bool { for _, current := range strings.Split(modeSet, "&") { if current == mode { return true } } return false } func NewLogWriter(name string) *chanWriter { r, w := io.Pipe() cw := &chanWriter{ Name: name, r: r, w: w, } go func() { s := bufio.NewScanner(cw.r) for s.Scan() { fmt.Println(name + ": " + string(s.Bytes())) } }() return cw } type chanWriter struct { Name string Chan chan string w *io.PipeWriter r *io.PipeReader } func (w *chanWriter) Write(p []byte) (n int, err error) { return w.w.Write(p) } ================================================ FILE: pkg/app/master/command/build/image.go ================================================ package build import ( "fmt" "os" "path/filepath" "strings" "time" "github.com/dustin/go-humanize" dockerapi "github.com/fsouza/go-dockerclient" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/builder" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/app/master/config" "github.com/slimtoolkit/slim/pkg/app/master/inspectors/image" cmd "github.com/slimtoolkit/slim/pkg/command" "github.com/slimtoolkit/slim/pkg/consts" "github.com/slimtoolkit/slim/pkg/imagebuilder" "github.com/slimtoolkit/slim/pkg/imagebuilder/internalbuilder" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/util/errutil" "github.com/slimtoolkit/slim/pkg/util/fsutil" v "github.com/slimtoolkit/slim/pkg/version" ) func inspectFatImage( xc *app.ExecutionContext, targetRef string, doPull bool, doShowPullLogs bool, rtaOnbuildBaseImage bool, dockerConfigPath string, registryAccount string, registrySecret string, paramsStatePath string, client *dockerapi.Client, logger *log.Entry, cmdReport *report.BuildCommand, ) (*image.Inspector, string, string, string) { imageInspector, err := image.NewInspector(client, targetRef) xc.FailOn(err) noImage, err := imageInspector.NoImage() errutil.FailOn(err) if noImage { if doPull { xc.Out.Info("target.image", ovars{ "status": "image.not.found", "image": targetRef, "message": "trying to pull target image", }) err := imageInspector.Pull(doShowPullLogs, dockerConfigPath, registryAccount, registrySecret) xc.FailOn(err) } else { xc.Out.Info("target.image.error", ovars{ "status": "image.not.found", "image": targetRef, "message": "make sure the target image already exists locally (use --pull flag to auto-download it from registry)", }) exitCode := command.ECTCommon | command.ECCImageNotFound xc.Out.State("exited", ovars{ "exit.code": exitCode, }) xc.Exit(exitCode) } } logger.Tracef("targetRef=%s ii.ImageRef=%s", targetRef, imageInspector.ImageRef) cmdReport.TargetReference = imageInspector.ImageRef xc.Out.State("image.inspection.start") logger.Info("inspecting 'fat' image metadata...") err = imageInspector.Inspect() xc.FailOn(err) localVolumePath, artifactLocation, statePath, stateKey := fsutil.PrepareImageStateDirs(paramsStatePath, imageInspector.ImageInfo.ID) imageInspector.ArtifactLocation = artifactLocation logger.Debugf("localVolumePath=%v, artifactLocation=%v, statePath=%v, stateKey=%v", localVolumePath, artifactLocation, statePath, stateKey) xc.Out.Info("image", ovars{ "id": imageInspector.ImageInfo.ID, "size.bytes": imageInspector.ImageInfo.VirtualSize, "size.human": humanize.Bytes(uint64(imageInspector.ImageInfo.VirtualSize)), }) if imageInspector.ImageInfo.Config != nil && len(imageInspector.ImageInfo.Config.Labels) > 0 { for labelName := range imageInspector.ImageInfo.Config.Labels { if labelName == consts.DSLabelVersion { xc.Out.Info("target.image.error", ovars{ "status": "image.already.optimized", "image": targetRef, "message": "the target image is already optimized", }) exitCode := command.ECTBuild | ecbImageAlreadyOptimized xc.Out.State("exited", ovars{ "exit.code": exitCode, }) cmdReport.Error = "image.already.optimized" xc.Exit(exitCode) } } } logger.Info("processing 'fat' image info...") err = imageInspector.ProcessCollectedData() xc.FailOn(err) if imageInspector.DockerfileInfo != nil { if imageInspector.DockerfileInfo.ExeUser != "" { xc.Out.Info("image.users", ovars{ "exe": imageInspector.DockerfileInfo.ExeUser, "all": strings.Join(imageInspector.DockerfileInfo.AllUsers, ","), }) } if len(imageInspector.DockerfileInfo.ImageStack) > 0 { cmdReport.ImageStack = imageInspector.DockerfileInfo.ImageStack for idx, layerInfo := range imageInspector.DockerfileInfo.ImageStack { xc.Out.Info("image.stack", ovars{ "index": idx, "name": layerInfo.FullName, "id": layerInfo.ID, }) } } if len(imageInspector.DockerfileInfo.ExposedPorts) > 0 { xc.Out.Info("image.exposed_ports", ovars{ "list": strings.Join(imageInspector.DockerfileInfo.ExposedPorts, ","), }) } if !rtaOnbuildBaseImage && imageInspector.DockerfileInfo.HasOnbuild { xc.Out.Info("target.image.error", ovars{ "status": "onbuild.base.image", "image": targetRef, "message": "Runtime analysis for onbuild base images is not supported", }) exitCode := command.ECTBuild | ecbOnbuildBaseImage xc.Out.State("exited", ovars{ "exit.code": exitCode, }) cmdReport.Error = "onbuild.base.image" xc.Exit(exitCode) } } xc.Out.State("image.inspection.done") return imageInspector, localVolumePath, statePath, stateKey } func buildFatImage( xc *app.ExecutionContext, targetRef string, customImageTag string, cbOpts *config.ContainerBuildOptions, doShowBuildLogs bool, client *dockerapi.Client, cmdReport *report.BuildCommand, ) (fatImageRepoNameTag string) { xc.Out.State("building", ovars{ "message": "building basic image", }) //create a fat image name: //* use the explicit fat image tag if provided //* or create one based on the user provided (slim image) custom tag if it's available //* otherwise auto-generate a name if cbOpts.Tag != "" { fatImageRepoNameTag = cbOpts.Tag } else if customImageTag != "" { citParts := strings.Split(customImageTag, ":") switch len(citParts) { case 1: fatImageRepoNameTag = fmt.Sprintf("%s.fat", customImageTag) case 2: fatImageRepoNameTag = fmt.Sprintf("%s.fat:%s", citParts[0], citParts[1]) default: xc.Out.Info("param.error", ovars{ "status": "malformed.custom.image.tag", "value": customImageTag, }) exitCode := command.ECTBuild | ecbBadCustomImageTag xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) cmdReport.Error = "malformed.custom.image.tag" xc.Exit(exitCode) } } else { fatImageRepoNameTag = fmt.Sprintf("slim-tmp-fat-image.%v.%v", os.Getpid(), time.Now().UTC().Format("20060102150405")) } cbOpts.Tag = fatImageRepoNameTag xc.Out.Info("basic.image.info", ovars{ "tag": cbOpts.Tag, "dockerfile": cbOpts.Dockerfile, "context": targetRef, }) fatBuilder, err := builder.NewBasicImageBuilder( client, cbOpts, targetRef, doShowBuildLogs) xc.FailOn(err) err = fatBuilder.Build() if doShowBuildLogs || err != nil { xc.Out.LogDump("regular.image.build", fatBuilder.BuildLog.String(), ovars{ "tag": cbOpts.Tag, }) } if err != nil { xc.Out.Info("build.error", ovars{ "status": "standard.image.build.error", "value": err, }) exitCode := command.ECTBuild | ecbImageBuildError xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Exit(exitCode) } xc.Out.State("basic.image.build.completed") return fatImageRepoNameTag } func buildOutputImage( xc *app.ExecutionContext, customImageTag string, additionalTags []string, cbOpts *config.ContainerBuildOptions, overrides *config.ContainerOverrides, imageOverrideSelectors map[string]bool, instructions *config.ImageNewInstructions, doDeleteFatImage bool, doShowBuildLogs bool, imageInspector *image.Inspector, client *dockerapi.Client, logger *log.Entry, cmdReport *report.BuildCommand, imageBuildEngine string, imageBuildArch string, ) string { onError := func(e error) { xc.Out.Info("build.error", ovars{ "status": "optimized.image.build.error", "error": e, }) exitCode := command.ECTBuild | ecbImageBuildError xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) cmdReport.Error = "optimized.image.build.error" xc.Exit(exitCode) } if customImageTag == "" { customImageTag = imageInspector.SlimImageRepo } cmdReport.ImageBuildEngine = imageBuildEngine logger.Debugf("image build engine - %v", imageBuildEngine) xc.Out.State("building", ovars{ "message": "building optimized image", "engine": imageBuildEngine, }) var outputImageName string var hasData bool var imageCreated bool switch imageBuildEngine { case IBENone: case IBEInternal: engine, err := internalbuilder.New(doShowBuildLogs, true, //pushToDaemon - TODO: have a param to control this & //output image tar (if not 'saving' to daemon) false) xc.FailOn(err) opts := imagebuilder.SimpleBuildOptions{ ImageConfig: imagebuilder.ImageConfig{ Architecture: imageBuildArch, Config: imagebuilder.RunConfig{ ExposedPorts: map[string]struct{}{}, Volumes: map[string]struct{}{}, Labels: map[string]string{}, }, }, } if customImageTag != "" { //must be first opts.Tags = append(opts.Tags, customImageTag) } if len(additionalTags) > 0 { opts.Tags = append(opts.Tags, additionalTags...) } UpdateBuildOptionsWithSrcImageInfo(&opts, imageInspector.ImageInfo) UpdateBuildOptionsWithOverrides(&opts, imageOverrideSelectors, overrides) if imageInspector.ImageRef != "" { opts.ImageConfig.Config.Labels[consts.DSLabelSourceImage] = imageInspector.ImageRef } var sourceImageID string if imageInspector.ImageInfo != nil && imageInspector.ImageInfo.ID != "" { sourceImageID = imageInspector.ImageInfo.ID } if sourceImageID == "" && imageInspector.ImageRecordInfo.ID != "" { sourceImageID = imageInspector.ImageRecordInfo.ID } if sourceImageID != "" { opts.ImageConfig.Config.Labels[consts.DSLabelSourceImageID] = sourceImageID } opts.ImageConfig.Config.Labels[consts.DSLabelVersion] = v.Current() //(new) instructions have higher value precedence over the runtime overrides UpdateBuildOptionsWithNewInstructions(&opts, instructions) dataTar := filepath.Join(imageInspector.ArtifactLocation, "files.tar") if fsutil.Exists(dataTar) && fsutil.IsRegularFile(dataTar) && fsutil.IsTarFile(dataTar) { layerInfo := imagebuilder.LayerDataInfo{ Type: imagebuilder.TarSource, Source: dataTar, Params: &imagebuilder.DataParams{ TargetPath: "/", }, } opts.Layers = append(opts.Layers, layerInfo) hasData = true } else { dataDir := filepath.Join(imageInspector.ArtifactLocation, "files") if fsutil.Exists(dataTar) && fsutil.IsDir(dataDir) { layerInfo := imagebuilder.LayerDataInfo{ Type: imagebuilder.DirSource, Source: dataDir, Params: &imagebuilder.DataParams{ TargetPath: "/", }, } opts.Layers = append(opts.Layers, layerInfo) hasData = true } else { logger.Info("WARNING - no data artifacts") } } imageResult, err := engine.Build(opts) if err != nil { onError(err) } outputImageName = imageResult.Name // customImageTag // engine.RepoName cmdReport.MinifiedImageID = imageResult.ID cmdReport.MinifiedImageDigest = imageResult.Digest imageCreated = true case IBEBuildKit: case IBEDocker: engine, err := builder.NewImageBuilder( client, customImageTag, additionalTags, imageInspector.ImageInfo, imageInspector.ArtifactLocation, doShowBuildLogs, imageOverrideSelectors, overrides, instructions, imageInspector.ImageRef) xc.FailOn(err) if !engine.HasData { logger.Info("WARNING - no data artifacts") } err = engine.Build() if doShowBuildLogs || err != nil { xc.Out.LogDump("optimized.image.build", engine.BuildLog.String(), ovars{ "tag": customImageTag, }) } if err != nil { onError(err) } if cbOpts.Dockerfile != "" { if doDeleteFatImage { xc.Out.Info("Dockerfile", ovars{ "image.name": cbOpts.Tag, "image.fat.deleted": "true", }) var err = client.RemoveImage(cbOpts.Tag) errutil.WarnOn(err) } else { xc.Out.Info("Dockerfile", ovars{ "image.name": cbOpts.Tag, "image.fat.deleted": "false", }) } } outputImageName = engine.RepoName hasData = engine.HasData imageCreated = true default: logger.Errorf("bad image build engine - %v", imageBuildEngine) onError(fmt.Errorf("bad image build engine - %v", imageBuildEngine)) } cmdReport.State = cmd.StateCompleted cmdReport.ImageCreated = imageCreated cmdReport.MinifiedImage = outputImageName cmdReport.MinifiedImageHasData = hasData xc.Out.State("completed") return outputImageName } // NOTE: lots of C&P from image_builder (TODO: refactor) const ( dsCmdPortInfo = "65501/tcp" dsEvtPortInfo = "65502/tcp" ) func UpdateBuildOptionsWithNewInstructions( options *imagebuilder.SimpleBuildOptions, instructions *config.ImageNewInstructions) { if instructions != nil { log.Debugf("NewImageBuilder: Using new image instructions => %+v", instructions) if instructions.Workdir != "" { options.ImageConfig.Config.WorkingDir = instructions.Workdir } if len(instructions.Env) > 0 { options.ImageConfig.Config.Env = append(options.ImageConfig.Config.Env, instructions.Env...) } for k, v := range instructions.ExposedPorts { options.ImageConfig.Config.ExposedPorts[string(k)] = v } for k, v := range instructions.Volumes { options.ImageConfig.Config.Volumes[k] = v } for k, v := range instructions.Labels { options.ImageConfig.Config.Labels[k] = v } if len(instructions.Entrypoint) > 0 { options.ImageConfig.Config.Entrypoint = instructions.Entrypoint } if len(instructions.Cmd) > 0 { options.ImageConfig.Config.Cmd = instructions.Cmd } if len(options.ImageConfig.Config.ExposedPorts) > 0 && len(instructions.RemoveExposedPorts) > 0 { for k := range instructions.RemoveExposedPorts { if _, ok := options.ImageConfig.Config.ExposedPorts[string(k)]; ok { delete(options.ImageConfig.Config.ExposedPorts, string(k)) } } } if len(options.ImageConfig.Config.Volumes) > 0 && len(instructions.RemoveVolumes) > 0 { for k := range instructions.RemoveVolumes { if _, ok := options.ImageConfig.Config.Volumes[k]; ok { delete(options.ImageConfig.Config.Volumes, k) } } } if len(options.ImageConfig.Config.Labels) > 0 && len(instructions.RemoveLabels) > 0 { for k := range instructions.RemoveLabels { if _, ok := options.ImageConfig.Config.Labels[k]; ok { delete(options.ImageConfig.Config.Labels, k) } } } if len(instructions.RemoveEnvs) > 0 && len(options.ImageConfig.Config.Env) > 0 { var newEnv []string for _, envPair := range options.ImageConfig.Config.Env { envParts := strings.SplitN(envPair, "=", 2) if len(envParts) > 0 && envParts[0] != "" { if _, ok := instructions.RemoveEnvs[envParts[0]]; !ok { newEnv = append(newEnv, envPair) } } } options.ImageConfig.Config.Env = newEnv } } } func UpdateBuildOptionsWithOverrides( options *imagebuilder.SimpleBuildOptions, overrideSelectors map[string]bool, overrides *config.ContainerOverrides) { if overrides != nil && len(overrideSelectors) > 0 { log.Debugf("UpdateBuildOptionsWithOverrides: Using container runtime overrides => %+v", overrideSelectors) for k := range overrideSelectors { switch k { case "entrypoint": if len(overrides.Entrypoint) > 0 { options.ImageConfig.Config.Entrypoint = overrides.Entrypoint } case "cmd": if len(overrides.Cmd) > 0 { options.ImageConfig.Config.Cmd = overrides.Cmd } case "workdir": if overrides.Workdir != "" { options.ImageConfig.Config.WorkingDir = overrides.Workdir } case "env": if len(overrides.Env) > 0 { options.ImageConfig.Config.Env = append(options.ImageConfig.Config.Env, overrides.Env...) } case "label": for k, v := range overrides.Labels { options.ImageConfig.Config.Labels[k] = v } case "volume": for k, v := range overrides.Volumes { options.ImageConfig.Config.Volumes[k] = v } case "expose": dsCmdPort := dockerapi.Port(dsCmdPortInfo) dsEvtPort := dockerapi.Port(dsEvtPortInfo) for k, v := range overrides.ExposedPorts { if k == dsCmdPort || k == dsEvtPort { continue } options.ImageConfig.Config.ExposedPorts[string(k)] = v } } } } } func UpdateBuildOptionsWithSrcImageInfo( options *imagebuilder.SimpleBuildOptions, imageInfo *dockerapi.Image) { labels := SourceToOutputImageLabels(imageInfo.Config.Labels) for k, v := range labels { options.ImageConfig.Config.Labels[k] = v } //note: not passing imageInfo.OS explicitly //because it gets "hardcoded" to "linux" internally //(other OS types are not supported) if options.ImageConfig.Architecture == "" { options.ImageConfig.Architecture = imageInfo.Architecture } options.ImageConfig.Config.User = imageInfo.Config.User options.ImageConfig.Config.Entrypoint = imageInfo.Config.Entrypoint options.ImageConfig.Config.Cmd = imageInfo.Config.Cmd options.ImageConfig.Config.WorkingDir = imageInfo.Config.WorkingDir options.ImageConfig.Config.Env = imageInfo.Config.Env options.ImageConfig.Config.Volumes = imageInfo.Config.Volumes options.ImageConfig.Config.OnBuild = imageInfo.Config.OnBuild options.ImageConfig.Config.StopSignal = imageInfo.Config.StopSignal for k, v := range imageInfo.Config.ExposedPorts { options.ImageConfig.Config.ExposedPorts[string(k)] = v } if options.ImageConfig.Config.ExposedPorts == nil { options.ImageConfig.Config.ExposedPorts = map[string]struct{}{} } if options.ImageConfig.Config.Volumes == nil { options.ImageConfig.Config.Volumes = map[string]struct{}{} } if options.ImageConfig.Config.Labels == nil { options.ImageConfig.Config.Labels = map[string]string{} } } func SourceToOutputImageLabels(srcLabels map[string]string) map[string]string { labels := map[string]string{} if srcLabels != nil { //cleanup non-standard labels from buildpacks for k, v := range srcLabels { lineLen := len(k) + len(v) + 7 if lineLen > 65535 { //TODO: improve JSON data splitting valueLen := len(v) parts := valueLen / 50000 parts++ offset := 0 for i := 0; i < parts && offset < valueLen; i++ { chunkSize := 50000 if (offset + chunkSize) > valueLen { chunkSize = valueLen - offset } value := v[offset:(offset + chunkSize)] offset += chunkSize key := fmt.Sprintf("%s.%d", k, i) labels[key] = value } } else { labels[k] = v } } } return labels } ================================================ FILE: pkg/app/master/command/build/init/init.go ================================================ package init import ( "github.com/slimtoolkit/slim/pkg/app/master/command/build" ) func init() { build.RegisterCommand() } ================================================ FILE: pkg/app/master/command/build/kubernetes.go ================================================ package build import ( "bufio" "context" "encoding/json" "fmt" "os" "strings" "time" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/app/master/config" "github.com/slimtoolkit/slim/pkg/app/master/inspectors/image" "github.com/slimtoolkit/slim/pkg/app/master/inspectors/pod" "github.com/slimtoolkit/slim/pkg/app/master/kubernetes" "github.com/slimtoolkit/slim/pkg/app/master/probe/http" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/util/errutil" "github.com/slimtoolkit/slim/pkg/util/fsutil" v "github.com/slimtoolkit/slim/pkg/version" dockerapi "github.com/fsouza/go-dockerclient" log "github.com/sirupsen/logrus" "k8s.io/cli-runtime/pkg/resource" ) type kubeHandler struct { *app.ExecutionContext ctx context.Context report *report.BuildCommand logger *log.Entry dockerClient *dockerapi.Client kubeClient *kubernetes.Client kubectl kubernetes.Kubectl finder *kubernetes.WorkloadFinder } func newKubeHandler( xc *app.ExecutionContext, ctx context.Context, cmdReport *report.BuildCommand, logger *log.Entry, dockerClient *dockerapi.Client, kubeClient *kubernetes.Client, kubectl kubernetes.Kubectl, finder *kubernetes.WorkloadFinder, ) *kubeHandler { return &kubeHandler{ ctx: ctx, ExecutionContext: xc, report: cmdReport, logger: logger, dockerClient: dockerClient, kubeClient: kubeClient, kubectl: kubectl, finder: finder, } } type kubeHandleOptions struct { DoPull bool DoShowPullLogs bool DoShowBuildLogs bool DoShowContainerLogs bool DoEnableMondel bool DoDeleteFatImage bool DoRmFileArtifacts bool RtaOnbuildBaseImage bool RtaSourcePT bool DockerConfigPath string RegistryAccount string RegistrySecret string KeepPerms bool PathPerms map[string]*fsutil.AccessInfo ArchiveState string StatePath string CopyMetaArtifactsLocation string Debug bool LogLevel string LogFormat string SensorIPCEndpoint string CBOpts *config.ContainerBuildOptions CustomImageTag string AdditionalTags []string PortBindings map[dockerapi.Port][]dockerapi.PortBinding DoPublishExposedPorts bool httpProbeOpts config.HTTPProbeOptions continueAfter *config.ContinueAfter execCmd string imageBuildEngine string imageBuildArch string } func (h *kubeHandler) Handle( target config.KubernetesTarget, targetOverride config.KubernetesTargetOverride, manifests *kubernetes.Manifests, opts kubeHandleOptions, ) { // 1. Pre-process the workload // - find the running workload in the cluster // - ...or in the supplied manifest workload := h.findWorkloadOrFail(target) if manifests != nil { h.applyManifestsOrFail(manifests, workload) } // 2. Inspect the workload's original image. if targetOverride.Image != "" { workload.TargetContainer().Image = command.UpdateImageRef( h.logger, workload.TargetContainer().Image, targetOverride.Image) } imageInspector, _, statePath, stateKey := inspectFatImage( h.ExecutionContext, workload.TargetContainer().Image, opts.DoPull, opts.DoShowPullLogs, opts.RtaOnbuildBaseImage, opts.DockerConfigPath, opts.RegistryAccount, opts.RegistrySecret, opts.StatePath, h.dockerClient, h.logger, h.report) workload.TargetContainer().Image = imageInspector.ImageRef // 3. Patch and run the workload // - patch: add the init container, the volume, replace the entrypoint // - copy sensor to the volume via the init container // - terminate (successfully) the init container // - send StartCommand to the sensor podInspector, err := pod.NewInspector( h.ctx, h.ExecutionContext, h.logger, workload, h.kubectl, h.kubeClient, imageInspector, opts.KeepPerms, opts.PathPerms, opts.Debug, opts.LogLevel, opts.LogFormat, opts.RtaSourcePT, statePath, nil, opts.SensorIPCEndpoint, opts.PortBindings, opts.DoPublishExposedPorts, ) h.FailOn(err) h.AddCleanupHandler(func() { podInspector.FinishMonitoring() podInspector.ShutdownPod(manifests == nil) }) h.logger.Info("starting instrumented 'fat' workload...") err = podInspector.RunPod() if err != nil && opts.DoShowContainerLogs { podInspector.ShowPodLogs() } h.FailOn(err) h.Out.Info("pod", ovars{ "name": podInspector.PodName(), "target.port.list": podInspector.PodPortList(), "target.port.info": podInspector.PodPortsInfo(), "message": "YOU CAN USE THESE PORTS TO INTERACT WITH THE POD", }) // 4. Monitor the workload. h.logger.Info("watching pod monitor...") h.monitorPod(opts, podInspector) // 5. Copy the artifact from the workload. h.Out.State("pod.inspection.finishing") podInspector.FinishMonitoring() // 6. Shut down the workload. h.logger.Info("shutting down 'fat' pod...") podInspector.ShutdownPod(manifests == nil) if manifests != nil { errutil.WarnOn(manifests.Delete(h.ctx)) } // 7. Build the slim image & create AppArmor and seccomp profiles h.processCollectedDataOrFail(podInspector, imageInspector) minifiedImageName := buildOutputImage( h.ExecutionContext, opts.CustomImageTag, opts.AdditionalTags, opts.CBOpts, nil, // TODO: overrrides nil, // TODO: imageOverrideSelectors, nil, // TODO: instructions, opts.DoDeleteFatImage, opts.DoShowBuildLogs, imageInspector, h.dockerClient, h.logger, h.report, opts.imageBuildEngine, opts.imageBuildArch) finishCommand( h.ExecutionContext, minifiedImageName, opts.CopyMetaArtifactsLocation, opts.DoRmFileArtifacts, opts.ArchiveState, stateKey, imageInspector, h.dockerClient, h.logger, h.report, opts.imageBuildEngine) } func (h *kubeHandler) findWorkloadOrFail(target config.KubernetesTarget) *kubernetes.Workload { workload, err := h.finder.Find(target) h.FailOn(err) if workload == nil { h.Out.Info("kubernetes.workload.error", ovars{ "status": "workload.not.found", "target": target.Workload, }) exitCode := command.ECTBuild | ecbKubernetesNoWorkload h.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) h.Exit(exitCode) } if workload.TargetContainer() == nil { h.Out.Info("kubernetes.workload.error", ovars{ "status": "container.not.found", "target.container": target.Container, }) exitCode := command.ECTBuild | ecbKubernetesNoWorkloadContainer h.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) h.Exit(exitCode) } h.Out.Info("kubernetes.workload", ovars{ "namespace": workload.Namespace(), "name": workload.Name(), "template": asJSON(workload.Template()), "target": asJSON(workload.TargetContainer()), }) return workload } func (h *kubeHandler) applyManifestsOrFail( manifests *kubernetes.Manifests, workload *kubernetes.Workload, ) { h.AddCleanupHandler(func() { errutil.WarnOn(manifests.Delete(h.ctx)) }) // PodInspector will be applying the workload manifest separately. err := manifests.Apply(h.ctx, func(info *resource.Info) bool { return workload.Info().Mapping.GroupVersionKind.GroupKind() != info.Mapping.GroupVersionKind.GroupKind() || workload.Name() != info.Name || workload.Namespace() != info.Namespace }) h.FailOn(err) } func (h *kubeHandler) monitorPod( opts kubeHandleOptions, podInspector *pod.Inspector, ) { var probe *http.CustomProbe if opts.httpProbeOpts.Do { var err error probe, err = http.NewPodProbe(h.ExecutionContext, podInspector, opts.httpProbeOpts, true) h.FailOn(err) if len(probe.Ports()) == 0 { h.Out.State("http.probe.error", ovars{ "error": "NO EXPOSED PORTS", "message": "make sure target container spec has `ports` field filled or disable HTTP probing with --http-probe=false if your Kubernetes workload doesn't expose any network services", }) exitCode := command.ECTBuild | ecbImageBuildError h.Out.State("exited", ovars{"exit.code": exitCode}) h.report.Error = "no.exposed.ports" h.Exit(exitCode) } probe.Start() opts.continueAfter.ContinueChan = probe.DoneChan() } continueAfterMsg := "provide the expected input to allow the container inspector to continue its execution" if opts.continueAfter.Mode == config.CAMTimeout { continueAfterMsg = "no input required, execution will resume after the timeout" } if hasContinueAfterMode(opts.continueAfter.Mode, config.CAMProbe) { continueAfterMsg = "no input required, execution will resume when HTTP probing is completed" } h.Out.Info("continue.after", ovars{ "mode": opts.continueAfter.Mode, "message": continueAfterMsg, }) modes := strings.Split(opts.continueAfter.Mode, "&") for _, mode := range modes { switch mode { case config.CAMEnter: h.Out.Prompt("USER INPUT REQUIRED, PRESS WHEN YOU ARE DONE USING THE KUBERNETES WORKLOAD") creader := bufio.NewReader(os.Stdin) _, _, _ = creader.ReadLine() case config.CAMExec: // Use execCmd h.Out.Info("continue.after", ovars{"mode": config.CAMExec, "shell": opts.execCmd}) out, err := podInspector.Exec("sh", "-c", opts.execCmd) errutil.WarnOn(err) h.Out.Info("continue.after", ovars{"mode": config.CAMExec, "output": string(out)}) case config.CAMSignal: h.Out.Prompt("send SIGUSR1 when you are done using the container") <-opts.continueAfter.ContinueChan h.Out.Info("event", ovars{"message": "got SIGUSR1"}) case config.CAMTimeout: h.Out.Prompt(fmt.Sprintf("waiting for the target container (%v seconds)", int(opts.continueAfter.Timeout))) <-time.After(time.Second * opts.continueAfter.Timeout) h.Out.Info("event", ovars{"message": "done waiting for the target container"}) case config.CAMProbe: h.Out.Prompt("waiting for the HTTP probe to finish") <-opts.continueAfter.ContinueChan h.Out.Info("event", ovars{"message": "HTTP probe is done"}) if probe.CallCount > 0 && probe.OkCount == 0 && opts.httpProbeOpts.ExitOnFailure { h.Out.Error("probe.error", "no.successful.calls") podInspector.ShowPodLogs() h.Out.State("exited", ovars{"exit.code": -1}) h.Exit(-1) } default: errutil.Fail("unknown continue-after mode") } } } func (h *kubeHandler) processCollectedDataOrFail(podInspector *pod.Inspector, imageInspector *image.Inspector) { if !podInspector.HasCollectedData() { imageInspector.ShowFatImageDockerInstructions() h.Out.Info("results", ovars{ "status": "no data collected (no minified image generated)", "version": v.Current(), "location": fsutil.ExeDir(), }) exitCode := command.ECTBuild | ecbImageBuildError h.Out.State("exited", ovars{ "exit.code": exitCode, }) h.report.Error = "no.data.collected" h.Exit(exitCode) } h.logger.Info("processing instrumented 'fat' container info...") h.FailOn(podInspector.ProcessCollectedData()) } func asJSON(val interface{}) string { bytes, err := json.Marshal(val) if err != nil { panic("json.Marshal failed: " + err.Error()) } return string(bytes) } ================================================ FILE: pkg/app/master/command/build/prompt.go ================================================ package build import ( "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/c-bata/go-prompt" ) var CommandSuggestion = prompt.Suggest{ Text: Name, Description: Usage, } var CommandFlagSuggestions = &command.FlagSuggestions{ Names: []prompt.Suggest{ {Text: command.FullFlagName(command.FlagCommandParamsFile), Description: command.FlagCommandParamsFileUsage}, {Text: command.FullFlagName(command.FlagTarget), Description: command.FlagTargetUsage}, {Text: command.FullFlagName(command.FlagComposeFile), Description: command.FlagComposeFileUsage}, {Text: command.FullFlagName(command.FlagTargetComposeSvc), Description: command.FlagTargetComposeSvcUsage}, {Text: command.FullFlagName(command.FlagTargetComposeSvcImage), Description: command.FlagTargetComposeSvcImageUsage}, {Text: command.FullFlagName(command.FlagComposeSvcStartWait), Description: command.FlagComposeSvcStartWaitUsage}, {Text: command.FullFlagName(command.FlagDepIncludeComposeSvc), Description: command.FlagDepIncludeComposeSvcUsage}, {Text: command.FullFlagName(command.FlagDepExcludeComposeSvc), Description: command.FlagDepExcludeComposeSvcUsage}, {Text: command.FullFlagName(command.FlagDepIncludeComposeSvcDeps), Description: command.FlagDepIncludeComposeSvcDepsUsage}, {Text: command.FullFlagName(command.FlagComposeNet), Description: command.FlagComposeNetUsage}, {Text: command.FullFlagName(command.FlagDepIncludeTargetComposeSvcDeps), Description: command.FlagDepIncludeTargetComposeSvcDepsUsage}, {Text: command.FullFlagName(command.FlagComposeEnvNoHost), Description: command.FlagComposeEnvNoHostUsage}, {Text: command.FullFlagName(command.FlagComposeEnvFile), Description: command.FlagComposeEnvFileUsage}, {Text: command.FullFlagName(command.FlagComposeProjectName), Description: command.FlagComposeProjectNameUsage}, {Text: command.FullFlagName(command.FlagComposeWorkdir), Description: command.FlagComposeWorkdirUsage}, {Text: command.FullFlagName(command.FlagContainerProbeComposeSvc), Description: command.FlagContainerProbeComposeSvcUsage}, {Text: command.FullFlagName(command.FlagTargetKubeWorkload), Description: command.FlagTargetKubeWorkloadUsage}, {Text: command.FullFlagName(command.FlagTargetKubeWorkloadNamespace), Description: command.FlagTargetKubeWorkloadNamespaceUsage}, {Text: command.FullFlagName(command.FlagTargetKubeWorkloadContainer), Description: command.FlagTargetKubeWorkloadContainerUsage}, {Text: command.FullFlagName(command.FlagTargetKubeWorkloadImage), Description: command.FlagTargetKubeWorkloadImageUsage}, {Text: command.FullFlagName(command.FlagKubeManifestFile), Description: command.FlagKubeManifestFileUsage}, {Text: command.FullFlagName(command.FlagKubeKubeconfigFile), Description: command.FlagKubeKubeconfigFileUsage}, {Text: command.FullFlagName(command.FlagPull), Description: command.FlagPullUsage}, {Text: command.FullFlagName(command.FlagShowPullLogs), Description: command.FlagShowPullLogsUsage}, {Text: command.FullFlagName(command.FlagRegistryAccount), Description: command.FlagRegistryAccountUsage}, {Text: command.FullFlagName(command.FlagRegistrySecret), Description: command.FlagRegistrySecretUsage}, {Text: command.FullFlagName(command.FlagDockerConfigPath), Description: command.FlagDockerConfigPathUsage}, {Text: command.FullFlagName(FlagShowBuildLogs), Description: FlagShowBuildLogsUsage}, {Text: command.FullFlagName(command.FlagShowContainerLogs), Description: command.FlagShowContainerLogsUsage}, {Text: command.FullFlagName(command.FlagEnableMondelLogs), Description: command.FlagEnableMondelLogsUsage}, {Text: command.FullFlagName(command.FlagCRORuntime), Description: command.FlagCRORuntimeUsage}, {Text: command.FullFlagName(command.FlagCROHostConfigFile), Description: command.FlagCROHostConfigFileUsage}, {Text: command.FullFlagName(command.FlagCROSysctl), Description: command.FlagCROSysctlUsage}, {Text: command.FullFlagName(command.FlagCROShmSize), Description: command.FlagCROShmSizeUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeOff), Description: command.FlagHTTPProbeOffUsage}, {Text: command.FullFlagName(command.FlagHTTPProbe), Description: command.FlagHTTPProbeUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeCmd), Description: command.FlagHTTPProbeCmdUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeCmdFile), Description: command.FlagHTTPProbeCmdFileUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeStartWait), Description: command.FlagHTTPProbeStartWaitUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeRetryCount), Description: command.FlagHTTPProbeRetryCountUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeRetryWait), Description: command.FlagHTTPProbeRetryWaitUsage}, {Text: command.FullFlagName(command.FlagHTTPProbePorts), Description: command.FlagHTTPProbePortsUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeFull), Description: command.FlagHTTPProbeFullUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeExitOnFailure), Description: command.FlagHTTPProbeExitOnFailureUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeCrawl), Description: command.FlagHTTPProbeCrawlUsage}, {Text: command.FullFlagName(command.FlagHTTPCrawlMaxDepth), Description: command.FlagHTTPCrawlMaxDepthUsage}, {Text: command.FullFlagName(command.FlagHTTPCrawlMaxPageCount), Description: command.FlagHTTPCrawlMaxPageCountUsage}, {Text: command.FullFlagName(command.FlagHTTPCrawlConcurrency), Description: command.FlagHTTPCrawlConcurrencyUsage}, {Text: command.FullFlagName(command.FlagHTTPMaxConcurrentCrawlers), Description: command.FlagHTTPMaxConcurrentCrawlersUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeAPISpec), Description: command.FlagHTTPProbeAPISpecUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeAPISpecFile), Description: command.FlagHTTPProbeAPISpecFileUsage}, {Text: command.FullFlagName(command.FlagPublishPort), Description: command.FlagPublishPortUsage}, {Text: command.FullFlagName(command.FlagPublishExposedPorts), Description: command.FlagPublishExposedPortsUsage}, {Text: command.FullFlagName(command.FlagHostExec), Description: command.FlagHostExecUsage}, {Text: command.FullFlagName(command.FlagHostExecFile), Description: command.FlagHostExecFileUsage}, {Text: command.FullFlagName(FlagKeepPerms), Description: FlagKeepPermsUsage}, {Text: command.FullFlagName(command.FlagRunTargetAsUser), Description: command.FlagRunTargetAsUserUsage}, {Text: command.FullFlagName(command.FlagCopyMetaArtifacts), Description: command.FlagCopyMetaArtifactsUsage}, {Text: command.FullFlagName(command.FlagRemoveFileArtifacts), Description: command.FlagRemoveFileArtifactsUsage}, {Text: command.FullFlagName(FlagTag), Description: FlagTagUsage}, {Text: command.FullFlagName(FlagImageOverrides), Description: FlagImageOverridesUsage}, {Text: command.FullFlagName(command.FlagUser), Description: command.FlagUserUsage}, {Text: command.FullFlagName(command.FlagEntrypoint), Description: command.FlagEntrypointUsage}, {Text: command.FullFlagName(command.FlagCmd), Description: command.FlagCmdUsage}, {Text: command.FullFlagName(command.FlagWorkdir), Description: command.FlagWorkdirUsage}, {Text: command.FullFlagName(command.FlagEnv), Description: command.FlagEnvUsage}, {Text: command.FullFlagName(command.FlagEnvFile), Description: command.FlagEnvFileUsage}, {Text: command.FullFlagName(command.FlagLabel), Description: command.FlagLabelUsage}, {Text: command.FullFlagName(command.FlagVolume), Description: command.FlagVolumeUsage}, {Text: command.FullFlagName(command.FlagLink), Description: command.FlagLinkUsage}, {Text: command.FullFlagName(command.FlagEtcHostsMap), Description: command.FlagEtcHostsMapUsage}, {Text: command.FullFlagName(command.FlagContainerDNS), Description: command.FlagContainerDNSUsage}, {Text: command.FullFlagName(command.FlagContainerDNSSearch), Description: command.FlagContainerDNSSearchUsage}, {Text: command.FullFlagName(command.FlagNetwork), Description: command.FlagNetworkUsage}, {Text: command.FullFlagName(command.FlagHostname), Description: command.FlagHostnameUsage}, {Text: command.FullFlagName(command.FlagExpose), Description: command.FlagExposeUsage}, {Text: command.FullFlagName(FlagNewEntrypoint), Description: FlagNewEntrypointUsage}, {Text: command.FullFlagName(FlagNewCmd), Description: FlagNewCmdUsage}, {Text: command.FullFlagName(FlagNewExpose), Description: FlagNewExposeUsage}, {Text: command.FullFlagName(FlagNewWorkdir), Description: FlagNewWorkdirUsage}, {Text: command.FullFlagName(FlagNewEnv), Description: FlagNewEnvUsage}, {Text: command.FullFlagName(FlagNewVolume), Description: FlagNewVolumeUsage}, {Text: command.FullFlagName(FlagNewLabel), Description: FlagNewLabelUsage}, {Text: command.FullFlagName(FlagRemoveExpose), Description: FlagRemoveExposeUsage}, {Text: command.FullFlagName(FlagRemoveEnv), Description: FlagRemoveEnvUsage}, {Text: command.FullFlagName(FlagRemoveLabel), Description: FlagRemoveLabelUsage}, {Text: command.FullFlagName(FlagRemoveVolume), Description: FlagRemoveVolumeUsage}, {Text: command.FullFlagName(FlagExcludeMounts), Description: FlagExcludeMountsUsage}, {Text: command.FullFlagName(FlagExcludeVarLockFiles), Description: FlagExcludeVarLockFilesUsage}, {Text: command.FullFlagName(FlagExcludePattern), Description: FlagExcludePatternUsage}, {Text: command.FullFlagName(FlagPathPerms), Description: FlagPathPermsUsage}, {Text: command.FullFlagName(FlagPathPermsFile), Description: FlagPathPermsFileUsage}, {Text: command.FullFlagName(FlagPreservePath), Description: FlagPreservePathUsage}, {Text: command.FullFlagName(FlagPreservePathFile), Description: FlagPreservePathFileUsage}, {Text: command.FullFlagName(FlagIncludePath), Description: FlagIncludePathUsage}, {Text: command.FullFlagName(FlagIncludePathFile), Description: FlagIncludePathFileUsage}, {Text: command.FullFlagName(FlagIncludeBin), Description: FlagIncludeBinUsage}, {Text: command.FullFlagName(FlagIncludeBinFile), Description: FlagIncludeBinFileUsage}, {Text: command.FullFlagName(FlagIncludeExe), Description: FlagIncludeExeUsage}, {Text: command.FullFlagName(FlagIncludeExeFile), Description: FlagIncludeExeFileUsage}, {Text: command.FullFlagName(FlagIncludeShell), Description: FlagIncludeShellUsage}, {Text: command.FullFlagName(FlagIncludeWorkdir), Description: FlagIncludeWorkdirUsage}, {Text: command.FullFlagName(FlagIncludeAppImageAll), Description: FlagIncludeAppImageAllUsage}, {Text: command.FullFlagName(FlagAppImageStartInstGroup), Description: FlagAppImageStartInstGroupUsage}, {Text: command.FullFlagName(FlagAppImageStartInst), Description: FlagAppImageStartInstUsage}, {Text: command.FullFlagName(FlagAppImageDockerfile), Description: FlagAppImageDockerfileUsage}, {Text: command.FullFlagName(FlagIncludePathsCreportFile), Description: FlagIncludePathsCreportFileUsage}, {Text: command.FullFlagName(FlagIncludeSSHClient), Description: FlagIncludeSSHClientUsage}, {Text: command.FullFlagName(FlagIncludeOSLibsNet), Description: FlagIncludeOSLibsNetUsage}, {Text: command.FullFlagName(FlagIncludeCertAll), Description: FlagIncludeCertAllUsage}, {Text: command.FullFlagName(FlagIncludeCertBundles), Description: FlagIncludeCertBundlesUsage}, {Text: command.FullFlagName(FlagIncludeCertDirs), Description: FlagIncludeCertDirsUsage}, {Text: command.FullFlagName(FlagIncludeCertPKAll), Description: FlagIncludeCertPKAllUsage}, {Text: command.FullFlagName(FlagIncludeCertPKDirs), Description: FlagIncludeCertPKDirsUsage}, {Text: command.FullFlagName(FlagIncludeNew), Description: FlagIncludeNewUsage}, {Text: command.FullFlagName(command.FlagMount), Description: command.FlagMountUsage}, {Text: command.FullFlagName(command.FlagContinueAfter), Description: command.FlagContinueAfterUsage}, {Text: command.FullFlagName(command.FlagUseLocalMounts), Description: command.FlagUseLocalMountsUsage}, {Text: command.FullFlagName(command.FlagUseSensorVolume), Description: command.FlagUseSensorVolumeUsage}, {Text: command.FullFlagName(FlagKeepTmpArtifacts), Description: FlagKeepTmpArtifactsUsage}, {Text: command.FullFlagName(FlagIncludeAppNuxtDir), Description: FlagIncludeAppNuxtDirUsage}, {Text: command.FullFlagName(FlagIncludeAppNuxtBuildDir), Description: FlagIncludeAppNuxtBuildDirUsage}, {Text: command.FullFlagName(FlagIncludeAppNuxtDistDir), Description: FlagIncludeAppNuxtDistDirUsage}, {Text: command.FullFlagName(FlagIncludeAppNuxtStaticDir), Description: FlagIncludeAppNuxtStaticDirUsage}, {Text: command.FullFlagName(FlagIncludeAppNuxtNodeModulesDir), Description: FlagIncludeAppNuxtNodeModulesDirUsage}, {Text: command.FullFlagName(FlagIncludeAppNextDir), Description: FlagIncludeAppNextDirUsage}, {Text: command.FullFlagName(FlagIncludeAppNextBuildDir), Description: FlagIncludeAppNextBuildDirUsage}, {Text: command.FullFlagName(FlagIncludeAppNextDistDir), Description: FlagIncludeAppNextDistDirUsage}, {Text: command.FullFlagName(FlagIncludeAppNextStaticDir), Description: FlagIncludeAppNextStaticDirUsage}, {Text: command.FullFlagName(FlagIncludeAppNextNodeModulesDir), Description: FlagIncludeAppNextNodeModulesDirUsage}, {Text: command.FullFlagName(FlagIncludeNodePackage), Description: FlagIncludeNodePackageUsage}, {Text: command.FullFlagName(FlagBuildFromDockerfile), Description: FlagBuildFromDockerfileUsage}, {Text: command.FullFlagName(FlagDockerfileContext), Description: FlagDockerfileContextUsage}, {Text: command.FullFlagName(FlagTagFat), Description: FlagTagFatUsage}, {Text: command.FullFlagName(FlagCBOAddHost), Description: FlagCBOAddHostUsage}, {Text: command.FullFlagName(FlagCBOBuildArg), Description: FlagCBOBuildArgUsage}, {Text: command.FullFlagName(FlagCBOLabel), Description: FlagCBOLabelUsage}, {Text: command.FullFlagName(FlagCBOTarget), Description: FlagCBOTargetUsage}, {Text: command.FullFlagName(FlagCBONetwork), Description: FlagCBONetworkUsage}, {Text: command.FullFlagName(FlagCBOCacheFrom), Description: FlagCBOCacheFromUsage}, {Text: command.FullFlagName(command.FlagDeleteFatImage), Description: command.FlagDeleteFatImageUsage}, {Text: command.FullFlagName(command.FlagRTAOnbuildBaseImage), Description: command.FlagRTAOnbuildBaseImageUsage}, {Text: command.FullFlagName(command.FlagRTASourcePT), Description: command.FlagRTASourcePTUsage}, {Text: command.FullFlagName(command.FlagSensorIPCMode), Description: command.FlagSensorIPCModeUsage}, {Text: command.FullFlagName(command.FlagSensorIPCEndpoint), Description: command.FlagSensorIPCEndpointUsage}, {Text: command.FullFlagName(FlagImageBuildEngine), Description: FlagImageBuildEngineUsage}, {Text: command.FullFlagName(FlagImageBuildArch), Description: FlagImageBuildArchUsage}, {Text: command.FullFlagName(FlagObfuscateMetadata), Description: FlagObfuscateMetadataUsage}, }, Values: map[string]command.CompleteValue{ command.FullFlagName(command.FlagCommandParamsFile): command.CompleteFile, //NOTE: with FlagPull target complete needs to check remote registries too command.FullFlagName(command.FlagPull): command.CompleteTBool, command.FullFlagName(command.FlagShowPullLogs): command.CompleteBool, command.FullFlagName(command.FlagDockerConfigPath): command.CompleteFile, command.FullFlagName(command.FlagTarget): command.CompleteImage, command.FullFlagName(command.FlagComposeFile): command.CompleteFile, command.FullFlagName(command.FlagDepIncludeTargetComposeSvcDeps): command.CompleteBool, command.FullFlagName(command.FlagComposeEnvNoHost): command.CompleteBool, command.FullFlagName(command.FlagComposeEnvFile): command.CompleteFile, command.FullFlagName(command.FlagComposeWorkdir): command.CompleteFile, command.FullFlagName(command.FlagKubeManifestFile): command.CompleteFile, command.FullFlagName(command.FlagKubeKubeconfigFile): command.CompleteFile, command.FullFlagName(FlagShowBuildLogs): command.CompleteBool, command.FullFlagName(command.FlagShowContainerLogs): command.CompleteBool, command.FullFlagName(command.FlagEnableMondelLogs): command.CompleteBool, command.FullFlagName(command.FlagPublishExposedPorts): command.CompleteBool, command.FullFlagName(command.FlagHTTPProbeOff): command.CompleteBool, command.FullFlagName(command.FlagHTTPProbe): command.CompleteTBool, command.FullFlagName(command.FlagHTTPProbeCmdFile): command.CompleteFile, command.FullFlagName(command.FlagHTTPProbeFull): command.CompleteBool, command.FullFlagName(command.FlagHTTPProbeExitOnFailure): command.CompleteBool, command.FullFlagName(command.FlagHTTPProbeCrawl): command.CompleteTBool, command.FullFlagName(command.FlagHTTPProbeAPISpecFile): command.CompleteFile, command.FullFlagName(command.FlagHostExecFile): command.CompleteFile, command.FullFlagName(FlagKeepPerms): command.CompleteTBool, command.FullFlagName(command.FlagRunTargetAsUser): command.CompleteTBool, command.FullFlagName(command.FlagRemoveFileArtifacts): command.CompleteBool, command.FullFlagName(command.FlagNetwork): command.CompleteNetwork, command.FullFlagName(FlagExcludeVarLockFiles): command.CompleteTBool, command.FullFlagName(FlagExcludeMounts): command.CompleteTBool, command.FullFlagName(FlagPathPermsFile): command.CompleteFile, command.FullFlagName(FlagPreservePathFile): command.CompleteFile, command.FullFlagName(FlagIncludePathFile): command.CompleteFile, command.FullFlagName(FlagIncludeBinFile): command.CompleteFile, command.FullFlagName(FlagIncludeExeFile): command.CompleteFile, command.FullFlagName(FlagIncludePathsCreportFile): command.CompleteFile, command.FullFlagName(FlagIncludeShell): command.CompleteBool, command.FullFlagName(FlagIncludeWorkdir): command.CompleteBool, command.FullFlagName(FlagIncludeAppImageAll): command.CompleteBool, command.FullFlagName(FlagIncludeSSHClient): command.CompleteBool, command.FullFlagName(FlagIncludeOSLibsNet): command.CompleteBool, command.FullFlagName(FlagIncludeCertAll): command.CompleteBool, command.FullFlagName(FlagIncludeCertBundles): command.CompleteBool, command.FullFlagName(FlagIncludeCertDirs): command.CompleteBool, command.FullFlagName(FlagIncludeCertPKAll): command.CompleteBool, command.FullFlagName(FlagIncludeCertPKDirs): command.CompleteBool, command.FullFlagName(FlagIncludeNew): command.CompleteBool, command.FullFlagName(command.FlagContinueAfter): command.CompleteContinueAfter, //command.FullFlagName(command.FlagConsoleFormat): command.CompleteConsoleOutput, command.FullFlagName(command.FlagUseLocalMounts): command.CompleteBool, command.FullFlagName(command.FlagUseSensorVolume): command.CompleteVolume, command.FullFlagName(FlagKeepTmpArtifacts): command.CompleteBool, command.FullFlagName(FlagIncludeAppNuxtDir): command.CompleteBool, command.FullFlagName(FlagIncludeAppNuxtBuildDir): command.CompleteBool, command.FullFlagName(FlagIncludeAppNuxtDistDir): command.CompleteBool, command.FullFlagName(FlagIncludeAppNuxtStaticDir): command.CompleteBool, command.FullFlagName(FlagIncludeAppNuxtNodeModulesDir): command.CompleteBool, command.FullFlagName(FlagIncludeAppNextDir): command.CompleteBool, command.FullFlagName(FlagIncludeAppNextBuildDir): command.CompleteBool, command.FullFlagName(FlagIncludeAppNextDistDir): command.CompleteBool, command.FullFlagName(FlagIncludeAppNextStaticDir): command.CompleteBool, command.FullFlagName(FlagIncludeAppNextNodeModulesDir): command.CompleteBool, command.FullFlagName(command.FlagCROHostConfigFile): command.CompleteFile, command.FullFlagName(FlagDockerfileContext): command.CompleteFile, command.FullFlagName(FlagDeleteFatImage): command.CompleteBool, command.FullFlagName(command.FlagRTAOnbuildBaseImage): command.CompleteBool, command.FullFlagName(command.FlagRTASourcePT): command.CompleteBool, command.FullFlagName(command.FlagSensorIPCMode): command.CompleteIPCMode, command.FullFlagName(FlagImageBuildEngine): CompleteImageBuildEngine, command.FullFlagName(FlagImageBuildArch): CompleteImageBuildArch, command.FullFlagName(FlagAppImageDockerfile): command.CompleteFile, command.FullFlagName(FlagObfuscateMetadata): command.CompleteBool, }, } var imageBuildEngineValues = []prompt.Suggest{ {Text: IBENone, Description: "no image build engine (output image is not built)"}, {Text: IBEInternal, Description: "internal image build engine"}, {Text: IBEDocker, Description: "standard Docker image build engine"}, {Text: IBEBuildKit, Description: "BuildKit image build engine"}, } func CompleteImageBuildEngine(ia *command.InteractiveApp, token string, params prompt.Document) []prompt.Suggest { return prompt.FilterHasPrefix(imageBuildEngineValues, token, true) } var imageBuildArchValues = []prompt.Suggest{ {Text: ArchAmd64, Description: "amd64 architecture"}, {Text: ArchArm64, Description: "arm64 architecture"}, } func CompleteImageBuildArch(ia *command.InteractiveApp, token string, params prompt.Document) []prompt.Suggest { return prompt.FilterHasPrefix(imageBuildArchValues, token, true) } ================================================ FILE: pkg/app/master/command/build/register.go ================================================ package build import ( "github.com/slimtoolkit/slim/pkg/app/master/command" ) func RegisterCommand() { command.AddCLICommand( Name, CLI, CommandSuggestion, CommandFlagSuggestions) } ================================================ FILE: pkg/app/master/command/cliflags.go ================================================ package command import ( log "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" "k8s.io/client-go/tools/clientcmd" ) ///////////////////////////////////////////////////////// //Flags ///////////////////////////////////////////////////////// // Global flag names const ( FlagCommandReport = "report" FlagCheckVersion = "check-version" FlagDebug = "debug" FlagVerbose = "verbose" FlagQuietCLIMode = "quiet" FlagLogLevel = "log-level" FlagLog = "log" FlagLogFormat = "log-format" FlagAPIVersion = "crt-api-version" FlagUseTLS = "tls" FlagVerifyTLS = "tls-verify" FlagTLSCertPath = "tls-cert-path" FlagHost = "host" FlagStatePath = "state-path" FlagInContainer = "in-container" FlagArchiveState = "archive-state" FlagNoColor = "no-color" FlagOutputFormat = "output-format" ) const ( OutputFormatJSON = "json" OutputFormatText = "text" ) // Global flag usage info const ( FlagCommandReportUsage = "command report location (enabled by default; set it to \"off\" to disable it)" FlagCheckVersionUsage = "check if the current version is outdated" FlagDebugUsage = "enable debug logs" FlagVerboseUsage = "enable info logs" FlagQuietCLIModeUsage = "Quiet CLI execution mode" FlagLogLevelUsage = "set the logging level ('trace', 'debug', 'info', 'warn' (default), 'error', 'fatal', 'panic')" FlagLogUsage = "log file to store logs" FlagLogFormatUsage = "set the format used by logs ('text' (default), or 'json')" FlagOutputFormatUsage = "set the output format to use ('text' (default), or 'json')" FlagUseTLSUsage = "use TLS" FlagVerifyTLSUsage = "verify TLS" FlagTLSCertPathUsage = "path to TLS cert files" FlagAPIVersionUsage = "Container runtime API version" FlagHostUsage = "Docker host address or socket (prefix with 'tcp://' or 'unix://')" FlagStatePathUsage = "app state base path" FlagInContainerUsage = "app is running in a container" FlagArchiveStateUsage = "archive app state to the selected Docker volume (default volume - slim-state). By default, enabled when app is running in a container (disabled otherwise). Set it to \"off\" to disable explicitly." FlagNoColorUsage = "disable color output" ) // Shared command flag names const ( FlagCommandParamsFile = "command-params-file" FlagTarget = "target" FlagPull = "pull" FlagDockerConfigPath = "docker-config-path" FlagRegistryAccount = "registry-account" FlagRegistrySecret = "registry-secret" FlagShowPullLogs = "show-plogs" //Compose-related flags FlagComposeFile = "compose-file" FlagTargetComposeSvc = "target-compose-svc" FlagTargetComposeSvcImage = "target-compose-svc-image" FlagComposeSvcStartWait = "compose-svc-start-wait" FlagComposeSvcNoPorts = "target-compose-svc-no-ports" FlagDepExcludeComposeSvcAll = "dep-exclude-compose-svc-all" FlagDepIncludeComposeSvc = "dep-include-compose-svc" FlagDepExcludeComposeSvc = "dep-exclude-compose-svc" FlagDepIncludeComposeSvcDeps = "dep-include-compose-svc-deps" FlagDepIncludeTargetComposeSvcDeps = "dep-include-target-compose-svc-deps" FlagComposeNet = "compose-net" FlagComposeEnvNoHost = "compose-env-nohost" FlagComposeEnvFile = "compose-env-file" FlagComposeWorkdir = "compose-workdir" FlagComposeProjectName = "compose-project-name" FlagPrestartComposeSvc = "prestart-compose-svc" FlagPoststartComposeSvc = "poststart-compose-svc" FlagPrestartComposeWaitExit = "prestart-compose-wait-exit" FlagContainerProbeComposeSvc = "container-probe-compose-svc" //Kubernetes-related flags FlagTargetKubeWorkload = "target-kube-workload" // / e.g: deployment/foo, job/bar FlagTargetKubeWorkloadNamespace = "target-kube-workload-namespace" FlagTargetKubeWorkloadContainer = "target-kube-workload-container" FlagTargetKubeWorkloadImage = "target-kube-workload-image" FlagKubeManifestFile = "kube-manifest-file" FlagKubeKubeconfigFile = "kube-kubeconfig-file" // TODO: FlagKubeContext = "kube-context" // FlagKubeCluster =" kube-cluster" // etc. // Naming convention: keep the well known kubectl flag names as-is and prefix them with `--kube-` FlagRemoveFileArtifacts = "remove-file-artifacts" FlagCopyMetaArtifacts = "copy-meta-artifacts" FlagHTTPProbe = "http-probe" FlagHTTPProbeOff = "http-probe-off" //alternative way to disable http probing FlagHTTPProbeCmd = "http-probe-cmd" FlagHTTPProbeCmdFile = "http-probe-cmd-file" FlagHTTPProbeStartWait = "http-probe-start-wait" FlagHTTPProbeRetryCount = "http-probe-retry-count" FlagHTTPProbeRetryWait = "http-probe-retry-wait" FlagHTTPProbePorts = "http-probe-ports" FlagHTTPProbeFull = "http-probe-full" FlagHTTPProbeExitOnFailure = "http-probe-exit-on-failure" FlagHTTPProbeCrawl = "http-probe-crawl" FlagHTTPCrawlMaxDepth = "http-crawl-max-depth" FlagHTTPCrawlMaxPageCount = "http-crawl-max-page-count" FlagHTTPCrawlConcurrency = "http-crawl-concurrency" FlagHTTPMaxConcurrentCrawlers = "http-max-concurrent-crawlers" FlagHTTPProbeAPISpec = "http-probe-apispec" FlagHTTPProbeAPISpecFile = "http-probe-apispec-file" FlagHTTPProbeProxyEndpoint = "http-probe-proxy-endpoint" FlagHTTPProbeProxyPort = "http-probe-proxy-port" FlagHostExec = "host-exec" FlagHostExecFile = "host-exec-file" FlagPublishPort = "publish-port" FlagPublishExposedPorts = "publish-exposed-ports" FlagRunTargetAsUser = "run-target-as-user" FlagShowContainerLogs = "show-clogs" FlagEnableMondelLogs = "enable-mondel" //Mon(itor) Data Event Log FlagUseLocalMounts = "use-local-mounts" FlagUseSensorVolume = "use-sensor-volume" FlagContinueAfter = "continue-after" //RunTime Analysis Options FlagRTAOnbuildBaseImage = "rta-onbuild-base-image" FlagRTASourcePT = "rta-source-ptrace" //Sensor IPC Options (for build and profile commands) FlagSensorIPCEndpoint = "sensor-ipc-endpoint" FlagSensorIPCMode = "sensor-ipc-mode" FlagExec = "exec" FlagExecFile = "exec-file" //Container Run Options (for build, profile and run commands) FlagCRORuntime = "cro-runtime" FlagCROHostConfigFile = "cro-host-config-file" FlagCROSysctl = "cro-sysctl" FlagCROShmSize = "cro-shm-size" //Original Container Runtime Options (without cro- prefix) FlagUser = "user" FlagEntrypoint = "entrypoint" FlagCmd = "cmd" FlagWorkdir = "workdir" FlagEnv = "env" FlagEnvFile = "env-file" FlagLabel = "label" FlagVolume = "volume" FlagExpose = "expose" FlagLink = "link" FlagNetwork = "network" FlagHostname = "hostname" FlagEtcHostsMap = "etc-hosts-map" FlagContainerDNS = "container-dns" FlagContainerDNSSearch = "container-dns-search" FlagMount = "mount" FlagDeleteFatImage = "delete-generated-fat-image" ) // Shared command flag usage info const ( FlagCommandParamsFileUsage = "JSON file with all command parameters" FlagTargetUsage = "Target container image (name or ID)" FlagPullUsage = "Try pulling target if it's not available locally" FlagDockerConfigPathUsage = "Docker config path (used to fetch registry credentials)" FlagRegistryAccountUsage = "Target registry account used when pulling images from private registries" FlagRegistrySecretUsage = "Target registry secret used when pulling images from private registries" FlagShowPullLogsUsage = "Show image pull logs" //Compose-related flags FlagComposeFileUsage = "Load container info from selected compose file(s)" FlagTargetComposeSvcUsage = "Target service from compose file" FlagTargetComposeSvcImageUsage = "Override the container image name and/or tag when targeting a compose service using the target-compose-svc parameter (format: tag_name or image_name:tag_name)" FlagComposeSvcStartWaitUsage = "Number of seconds to wait before starting each compose service" FlagComposeSvcNoPortsUsage = "Do not publish ports for target service from compose file" FlagDepExcludeComposeSvcAllUsage = "Do not start any compose services as target dependencies" FlagDepIncludeComposeSvcUsage = "Include specific compose service as a target dependency (only selected services will be started)" FlagDepExcludeComposeSvcUsage = "Exclude specific service from the compose services that will be started as target dependencies" FlagDepIncludeComposeSvcDepsUsage = "Include all dependencies for the selected compose service (excluding the service itself) as target dependencies" FlagDepIncludeTargetComposeSvcDepsUsage = "Include all dependencies for the target compose service (excluding the service itself) as target dependencies" FlagComposeNetUsage = "Attach target to the selected compose network(s) otherwise all networks will be attached" FlagComposeEnvNoHostUsage = "Don't include the env vars from the host to compose" FlagComposeEnvFileUsage = "Load compose env vars from file (host env vars override the values loaded from this file)" FlagComposeWorkdirUsage = "Set custom work directory for compose" FlagContainerProbeComposeSvcUsage = "Container test/probe service from compose file" FlagComposeProjectNameUsage = "Use custom project name for compose" FlagPrestartComposeSvcUsage = "Run selected compose service(s) before any other compose services or target container" FlagPoststartComposeSvcUsage = "Run selected compose service(s) after the target container is running (need a new continue after mode too)" FlagPrestartComposeWaitExitUsage = "Wait for selected prestart compose services to exit before starting other compose services or target container" //Kubernetes-related flags FlagTargetKubeWorkloadUsage = "[Experimental] Target Kubernetes workload from the manifests (if --kube-manifest-file is provided) or in the default kubeconfig cluster (format: /, e.g., deployments/foobar)" FlagTargetKubeWorkloadNamespaceUsage = "[Experimental] Target Kubernetes workload namespace (if not set, the value from the manifest is used if provided, otherwise - \"default\")" FlagTargetKubeWorkloadContainerUsage = "[Experimental] Target container in the Kubernetes workload's pod template spec" FlagTargetKubeWorkloadImageUsage = "[Experimental] Override the container image name and/or tag when targeting a Kubernetes workload (format: tag_name or image_name:tag_name)" FlagKubeManifestFileUsage = "[Experimental] Kubernetes manifest(s) to apply before run" FlagKubeKubeconfigFileUsage = "[Experimental] Path to the kubeconfig file" FlagRemoveFileArtifactsUsage = "remove file artifacts when command is done" FlagCopyMetaArtifactsUsage = "copy metadata artifacts to the selected location when command is done" FlagHTTPProbeUsage = "Enable or disable HTTP probing" FlagHTTPProbeOffUsage = "Alternative way to disable HTTP probing" FlagHTTPProbeCmdUsage = "User defined HTTP probe(s) as [[[[\"crawl\":]PROTO:]METHOD:]PATH]" FlagHTTPProbeCmdFileUsage = "File with user defined HTTP probes" FlagHTTPProbeStartWaitUsage = "Number of seconds to wait before starting HTTP probing" FlagHTTPProbeRetryCountUsage = "Number of retries for each HTTP probe" FlagHTTPProbeRetryWaitUsage = "Number of seconds to wait before retrying HTTP probe (doubles when target is not ready)" FlagHTTPProbePortsUsage = "Explicit list of ports to probe (in the order you want them to be probed)" FlagHTTPProbeFullUsage = "Do full HTTP probe for all selected ports (if false, finish after first successful scan)" FlagHTTPProbeExitOnFailureUsage = "Exit when all HTTP probe commands fail" FlagHTTPProbeCrawlUsage = "Enable crawling for the default HTTP probe command" FlagHTTPCrawlMaxDepthUsage = "Max depth to use for the HTTP probe crawler" FlagHTTPCrawlMaxPageCountUsage = "Max number of pages to visit for the HTTP probe crawler" FlagHTTPCrawlConcurrencyUsage = "Number of concurrent workers when crawling an HTTP target" FlagHTTPMaxConcurrentCrawlersUsage = "Number of concurrent crawlers in the HTTP probe" FlagHTTPProbeAPISpecUsage = "Run HTTP probes for API spec" FlagHTTPProbeAPISpecFileUsage = "Run HTTP probes for API spec from file" FlagHTTPProbeProxyEndpointUsage = "Endpoint to proxy HTTP probes" FlagHTTPProbeProxyPortUsage = "Port to proxy HTTP probes (used with HTTP probe proxy endpoint)" FlagHostExecUsage = "Host commands to execute (aka host commands probes)" FlagHostExecFileUsage = "Host commands to execute loaded from file (aka host commands probes)" FlagPublishPortUsage = "Map container port to host port (format => port | hostPort:containerPort | hostIP:hostPort:containerPort | hostIP::containerPort )" FlagPublishExposedPortsUsage = "Map all exposed ports to the same host ports" FlagRunTargetAsUserUsage = "Run target app as USER" FlagShowContainerLogsUsage = "Show container logs" FlagEnableMondelLogsUsage = "Enable data event log for sensor monitors" FlagUseLocalMountsUsage = "Mount local paths for target container artifact input and output" FlagUseSensorVolumeUsage = "Sensor volume name to use" FlagContinueAfterUsage = "Select continue mode: enter | signal | probe | timeout-number-in-seconds | container.probe" FlagRTAOnbuildBaseImageUsage = "Enable runtime analysis for onbuild base images" FlagRTASourcePTUsage = "Enable PTRACE runtime analysis source" FlagSensorIPCEndpointUsage = "Override sensor IPC endpoint" FlagSensorIPCModeUsage = "Select sensor IPC mode: proxy | direct" FlagExecUsage = "A shell script snippet to run via Docker exec" FlagExecFileUsage = "A shell script file to run via Docker exec" //Container Run Options (for build, profile and run commands) FlagCRORuntimeUsage = "Runtime to use with the created containers" FlagCROHostConfigFileUsage = "Base Docker host configuration file (JSON format) to use when running the container" FlagCROSysctlUsage = "Set namespaced kernel parameters in the created container" FlagCROShmSizeUsage = "Shared memory size for /dev/shm in the created container" FlagUserUsage = "Override USER analyzing image at runtime" FlagEntrypointUsage = "Override ENTRYPOINT analyzing image at runtime. To persist ENTRYPOINT changes in the output image, pass the --image-overrides=entrypoint or --image-overrides=all flag as well." FlagCmdUsage = "Override CMD analyzing image at runtime. To persist CMD changes in the output image, pass the --image-overrides=cmd or --image-overrides=all flag as well." FlagWorkdirUsage = "Override WORKDIR analyzing image at runtime. To persist WORKDIR changes in the output image, pass the --image-overrides=workdir or --image-overrides=all flag as well." FlagEnvUsage = "Override or add ENV only during runtime. To persist ENV additions or changes in the output image, pass the --image-overrides=env or --image-overrides=all flag as well." FlagEnvFileUsage = "File to override or add ENV only during runtime. To persist ENV additions or changes in the output image, pass the --image-overrides=env or --image-overrides=all flag as well." FlagLabelUsage = "Override or add LABEL analyzing image at runtime. To persist LABEL additions or changes in the output image, pass the --image-overrides=label or --image-overrides=all flag as well." FlagVolumeUsage = "Add VOLUME analyzing image at runtime. To persist VOLUME additions in the output image, pass the --image-overrides=volume or --image-overrides=all flag as well." FlagExposeUsage = "Use additional EXPOSE instructions analyzing image at runtime. To persist EXPOSE additions in the output image, pass the --image-overrides=expose or --image-overrides=all flag as well." FlagLinkUsage = "Add link to another container analyzing image at runtime" FlagNetworkUsage = "Override default container network settings analyzing image at runtime" FlagHostnameUsage = "Override default container hostname analyzing image at runtime" FlagEtcHostsMapUsage = "Add a host to IP mapping to /etc/hosts analyzing image at runtime" FlagContainerDNSUsage = "Add a dns server analyzing image at runtime" FlagContainerDNSSearchUsage = "Add a dns search domain for unqualified hostnames analyzing image at runtime" FlagMountUsage = "Mount volume analyzing image" FlagDeleteFatImageUsage = "Delete generated fat image requires --dockerfile flag" ) /////////////////////////////////// func GlobalFlags() []cli.Flag { return []cli.Flag{ &cli.StringFlag{ Name: FlagCommandReport, Value: "slim.report.json", Usage: "command report location (enabled by default; set it to \"off\" to disable it)", }, &cli.BoolFlag{ Name: FlagCheckVersion, Value: true, Usage: "check if the current version is outdated", EnvVars: []string{"DSLIM_CHECK_VERSION"}, }, &cli.BoolFlag{ Name: FlagDebug, Usage: FlagDebugUsage, EnvVars: []string{"DSLIM_DEBUG"}, }, &cli.BoolFlag{ Name: FlagVerbose, Usage: "enable info logs", EnvVars: []string{"DSLIM_VERBOSE"}, }, &cli.BoolFlag{ Name: FlagQuietCLIMode, Usage: FlagQuietCLIModeUsage, EnvVars: []string{"DSLIM_QUIET"}, }, &cli.StringFlag{ Name: FlagLogLevel, Value: "warn", Usage: "set the logging level ('debug', 'info', 'warn' (default), 'error', 'fatal', 'panic')", EnvVars: []string{"DSLIM_LOG_LEVEL"}, }, &cli.StringFlag{ Name: FlagLog, Usage: "log file to store logs", }, &cli.StringFlag{ Name: FlagLogFormat, Value: "text", Usage: "set the format used by logs ('text' (default), or 'json')", }, &cli.StringFlag{ Name: FlagOutputFormat, Value: "text", Usage: FlagOutputFormatUsage, }, &cli.BoolFlag{ Name: FlagUseTLS, Value: true, Usage: "use TLS", }, &cli.BoolFlag{ Name: FlagVerifyTLS, Value: true, Usage: "verify TLS", }, &cli.StringFlag{ Name: FlagTLSCertPath, Value: "", Usage: FlagTLSCertPathUsage, }, &cli.StringFlag{ Name: FlagAPIVersion, Value: "1.25", // We need at least 1.25 for to support builds from Dockerfile. Usage: FlagAPIVersionUsage, EnvVars: []string{"DSLIM_CRT_API_VER"}, }, &cli.StringFlag{ Name: FlagHost, Value: "", Usage: "Docker host address", }, &cli.StringFlag{ Name: FlagStatePath, Value: "", Usage: "app state base path", }, &cli.BoolFlag{ Name: FlagInContainer, Usage: "app is running in a container", }, &cli.StringFlag{ Name: FlagArchiveState, Value: "", Usage: "archive app state to the selected Docker volume (default volume - slim-state). By default, enabled when app is running in a container (disabled otherwise). Set it to \"off\" to disable explicitly.", }, &cli.BoolFlag{ Name: FlagNoColor, Usage: FlagNoColorUsage, }, } } var CommonFlags = map[string]cli.Flag{ FlagCommandParamsFile: &cli.StringFlag{ Name: FlagCommandParamsFile, Value: "", Usage: FlagCommandParamsFileUsage, EnvVars: []string{"DSLIM_COMMAND_PARAMS_FILE"}, }, FlagTarget: &cli.StringFlag{ Name: FlagTarget, Value: "", Usage: FlagTargetUsage, EnvVars: []string{"DSLIM_TARGET"}, }, FlagPull: &cli.BoolFlag{ Name: FlagPull, Value: true, //enabled by default Usage: FlagPullUsage, EnvVars: []string{"DSLIM_PULL"}, }, FlagDockerConfigPath: &cli.StringFlag{ Name: FlagDockerConfigPath, Usage: FlagDockerConfigPathUsage, EnvVars: []string{"DSLIM_DOCKER_CONFIG_PATH"}, }, FlagRegistryAccount: &cli.StringFlag{ Name: FlagRegistryAccount, Usage: FlagRegistryAccountUsage, EnvVars: []string{"DSLIM_REGISTRY_ACCOUNT"}, }, FlagRegistrySecret: &cli.StringFlag{ Name: FlagRegistrySecret, Usage: FlagRegistrySecretUsage, EnvVars: []string{"DSLIM_REGISTRY_SECRET"}, }, FlagShowPullLogs: &cli.BoolFlag{ Name: FlagShowPullLogs, Usage: FlagShowPullLogsUsage, EnvVars: []string{"DSLIM_PLOG"}, }, // FlagComposeFile: &cli.StringSliceFlag{ Name: FlagComposeFile, Value: cli.NewStringSlice(), Usage: FlagComposeFileUsage, EnvVars: []string{"DSLIM_COMPOSE_FILE"}, }, FlagTargetComposeSvc: &cli.StringFlag{ Name: FlagTargetComposeSvc, Value: "", Usage: FlagTargetComposeSvcUsage, EnvVars: []string{"DSLIM_TARGET_COMPOSE_SVC"}, }, FlagTargetComposeSvcImage: &cli.StringFlag{ Name: FlagTargetComposeSvcImage, Value: "", Usage: FlagTargetComposeSvcImageUsage, EnvVars: []string{"DSLIM_TARGET_COMPOSE_SVC_IMAGE"}, }, FlagComposeSvcStartWait: &cli.IntFlag{ Name: FlagComposeSvcStartWait, Value: 0, Usage: FlagComposeSvcStartWaitUsage, EnvVars: []string{"DSLIM_COMPOSE_SVC_START_WAIT"}, }, FlagComposeSvcNoPorts: &cli.BoolFlag{ Name: FlagComposeSvcNoPorts, Usage: FlagComposeSvcNoPortsUsage, EnvVars: []string{"DSLIM_COMPOSE_SVC_NO_PORTS"}, }, FlagDepExcludeComposeSvcAll: &cli.BoolFlag{ Name: FlagDepExcludeComposeSvcAll, Usage: FlagDepExcludeComposeSvcAllUsage, EnvVars: []string{"DSLIM_DEP_INCLUDE_COMPOSE_SVC_ALL"}, }, FlagDepIncludeComposeSvcDeps: &cli.StringFlag{ Name: FlagDepIncludeComposeSvcDeps, Value: "", Usage: FlagDepIncludeComposeSvcDepsUsage, EnvVars: []string{"DSLIM_DEP_INCLUDE_COMPOSE_SVC_DEPS"}, }, FlagDepIncludeComposeSvc: &cli.StringSliceFlag{ Name: FlagDepIncludeComposeSvc, Value: cli.NewStringSlice(), Usage: FlagDepIncludeComposeSvcUsage, EnvVars: []string{"DSLIM_DEP_INCLUDE_COMPOSE_SVC"}, }, FlagDepExcludeComposeSvc: &cli.StringSliceFlag{ Name: FlagDepExcludeComposeSvc, Value: cli.NewStringSlice(), Usage: FlagDepExcludeComposeSvcUsage, EnvVars: []string{"DSLIM_DEP_EXCLUDE_COMPOSE_SVC"}, }, FlagComposeNet: &cli.StringSliceFlag{ Name: FlagComposeNet, Value: cli.NewStringSlice(), Usage: FlagComposeNetUsage, EnvVars: []string{"DSLIM_COMPOSE_NET"}, }, FlagDepIncludeTargetComposeSvcDeps: &cli.BoolFlag{ Name: FlagDepIncludeTargetComposeSvcDeps, Usage: FlagDepIncludeTargetComposeSvcDepsUsage, EnvVars: []string{"DSLIM_DEP_INCLUDE_TARGET_COMPOSE_SVC_DEPS"}, }, FlagComposeEnvNoHost: &cli.BoolFlag{ Name: FlagComposeEnvNoHost, Usage: FlagComposeEnvNoHostUsage, EnvVars: []string{"DSLIM_COMPOSE_ENV_NOHOST"}, }, FlagComposeEnvFile: &cli.StringFlag{ Name: FlagComposeEnvFile, Value: "", Usage: FlagComposeEnvFileUsage, EnvVars: []string{"DSLIM_COMPOSE_ENV_FILE"}, }, FlagComposeProjectName: &cli.StringFlag{ Name: FlagComposeProjectName, Value: "", Usage: FlagComposeProjectNameUsage, EnvVars: []string{"DSLIM_COMPOSE_PROJECT_NAME"}, }, FlagComposeWorkdir: &cli.StringFlag{ Name: FlagComposeWorkdir, Value: "", Usage: FlagComposeWorkdirUsage, EnvVars: []string{"DSLIM_COMPOSE_WORKDIR"}, }, FlagContainerProbeComposeSvc: &cli.StringFlag{ Name: FlagContainerProbeComposeSvc, Value: "", Usage: FlagContainerProbeComposeSvcUsage, EnvVars: []string{"DSLIM_CONTAINER_PROBE_COMPOSE_SVC"}, }, FlagPrestartComposeSvc: &cli.StringSliceFlag{ Name: FlagPrestartComposeSvc, Value: cli.NewStringSlice(), Usage: FlagPrestartComposeSvcUsage, EnvVars: []string{"DSLIM_PRESTART_COMPOSE_SVC"}, }, FlagPrestartComposeWaitExit: &cli.BoolFlag{ Name: FlagPrestartComposeWaitExit, Usage: FlagPrestartComposeWaitExitUsage, EnvVars: []string{"DSLIM_PRESTART_COMPOSE_WAIT"}, }, FlagPoststartComposeSvc: &cli.StringSliceFlag{ Name: FlagPoststartComposeSvc, Value: cli.NewStringSlice(), Usage: FlagPoststartComposeSvcUsage, EnvVars: []string{"DSLIM_POSTSTART_COMPOSE_SVC"}, }, // FlagTargetKubeWorkload: &cli.StringFlag{ Name: FlagTargetKubeWorkload, Value: "", Usage: FlagTargetKubeWorkloadUsage, EnvVars: []string{"DSLIM_TARGET_KUBE_WORKLOAD"}, }, FlagTargetKubeWorkloadNamespace: &cli.StringFlag{ Name: FlagTargetKubeWorkloadNamespace, Value: "", Usage: FlagTargetKubeWorkloadNamespaceUsage, EnvVars: []string{"DSLIM_TARGET_KUBE_WORKLOAD_NAMESPACE"}, }, FlagTargetKubeWorkloadContainer: &cli.StringFlag{ Name: FlagTargetKubeWorkloadContainer, Value: "", Usage: FlagTargetKubeWorkloadContainerUsage, EnvVars: []string{"DSLIM_TARGET_KUBE_WORKLOAD_CONTAINER"}, }, FlagTargetKubeWorkloadImage: &cli.StringFlag{ Name: FlagTargetKubeWorkloadImage, Value: "", Usage: FlagTargetKubeWorkloadImageUsage, EnvVars: []string{"DSLIM_TARGET_KUBE_WORKLOAD_IMAGE"}, }, FlagKubeManifestFile: &cli.StringSliceFlag{ Name: FlagKubeManifestFile, Value: cli.NewStringSlice(), Usage: FlagKubeManifestFileUsage, EnvVars: []string{"DSLIM_KUBE_MANIFEST_FILE"}, }, FlagKubeKubeconfigFile: &cli.StringFlag{ Name: FlagKubeKubeconfigFile, Value: clientcmd.RecommendedHomeFile, Usage: FlagKubeKubeconfigFileUsage, EnvVars: []string{ "DSLIM_KUBE_KUBECONFIG_FILE", "KUBECONFIG", // subject to an industry-wide convention }, }, // FlagRemoveFileArtifacts: &cli.BoolFlag{ Name: FlagRemoveFileArtifacts, Usage: FlagRemoveFileArtifactsUsage, EnvVars: []string{"DSLIM_RM_FILE_ARTIFACTS"}, }, FlagCopyMetaArtifacts: &cli.StringFlag{ Name: FlagCopyMetaArtifacts, Usage: FlagCopyMetaArtifactsUsage, EnvVars: []string{"DSLIM_CP_META_ARTIFACTS"}, }, // FlagHTTPProbe: &cli.BoolFlag{ //true by default Name: FlagHTTPProbe, Value: true, Usage: FlagHTTPProbeUsage, EnvVars: []string{"DSLIM_HTTP_PROBE"}, }, FlagHTTPProbeOff: &cli.BoolFlag{ Name: FlagHTTPProbeOff, Usage: FlagHTTPProbeOffUsage, EnvVars: []string{"DSLIM_HTTP_PROBE_OFF"}, }, FlagHTTPProbeCmd: &cli.StringSliceFlag{ Name: FlagHTTPProbeCmd, Value: cli.NewStringSlice(), Usage: FlagHTTPProbeCmdUsage, EnvVars: []string{"DSLIM_HTTP_PROBE_CMD"}, }, FlagHTTPProbeCmdFile: &cli.StringFlag{ Name: FlagHTTPProbeCmdFile, Value: "", Usage: FlagHTTPProbeCmdFileUsage, EnvVars: []string{"DSLIM_HTTP_PROBE_CMD_FILE"}, }, FlagHTTPProbeAPISpec: &cli.StringSliceFlag{ Name: FlagHTTPProbeAPISpec, Value: cli.NewStringSlice(), Usage: FlagHTTPProbeAPISpecUsage, EnvVars: []string{"DSLIM_HTTP_PROBE_API_SPEC"}, }, FlagHTTPProbeAPISpecFile: &cli.StringSliceFlag{ Name: FlagHTTPProbeAPISpecFile, Value: cli.NewStringSlice(), Usage: FlagHTTPProbeAPISpecFileUsage, EnvVars: []string{"DSLIM_HTTP_PROBE_API_SPEC_FILE"}, }, FlagHTTPProbeStartWait: &cli.IntFlag{ Name: FlagHTTPProbeStartWait, Value: 0, Usage: FlagHTTPProbeStartWaitUsage, EnvVars: []string{"DSLIM_HTTP_PROBE_START_WAIT"}, }, FlagHTTPProbeRetryCount: &cli.IntFlag{ Name: FlagHTTPProbeRetryCount, Value: 5, Usage: FlagHTTPProbeRetryCountUsage, EnvVars: []string{"DSLIM_HTTP_PROBE_RETRY_COUNT"}, }, FlagHTTPProbeRetryWait: &cli.IntFlag{ Name: FlagHTTPProbeRetryWait, Value: 8, Usage: FlagHTTPProbeRetryWaitUsage, EnvVars: []string{"DSLIM_HTTP_PROBE_RETRY_WAIT"}, }, FlagHTTPProbePorts: &cli.StringFlag{ Name: FlagHTTPProbePorts, Value: "", Usage: FlagHTTPProbePortsUsage, EnvVars: []string{"DSLIM_HTTP_PROBE_PORTS"}, }, FlagHTTPProbeFull: &cli.BoolFlag{ Name: FlagHTTPProbeFull, Usage: FlagHTTPProbeFullUsage, EnvVars: []string{"DSLIM_HTTP_PROBE_FULL"}, }, FlagHTTPProbeExitOnFailure: &cli.BoolFlag{ //true by default now Name: FlagHTTPProbeExitOnFailure, Value: true, Usage: FlagHTTPProbeExitOnFailureUsage, EnvVars: []string{"DSLIM_HTTP_PROBE_EXIT_ON_FAILURE"}, }, FlagHTTPProbeCrawl: &cli.BoolFlag{ Name: FlagHTTPProbeCrawl, Value: true, Usage: FlagHTTPProbeCrawl, EnvVars: []string{"DSLIM_HTTP_PROBE_CRAWL"}, }, FlagHTTPCrawlMaxDepth: &cli.IntFlag{ Name: FlagHTTPCrawlMaxDepth, Value: 3, Usage: FlagHTTPCrawlMaxDepthUsage, EnvVars: []string{"DSLIM_HTTP_CRAWL_MAX_DEPTH"}, }, FlagHTTPCrawlMaxPageCount: &cli.IntFlag{ Name: FlagHTTPCrawlMaxPageCount, Value: 1000, Usage: FlagHTTPCrawlMaxPageCountUsage, EnvVars: []string{"DSLIM_HTTP_CRAWL_MAX_PAGE_COUNT"}, }, FlagHTTPCrawlConcurrency: &cli.IntFlag{ Name: FlagHTTPCrawlConcurrency, Value: 10, Usage: FlagHTTPCrawlConcurrencyUsage, EnvVars: []string{"DSLIM_HTTP_CRAWL_CONCURRENCY"}, }, FlagHTTPMaxConcurrentCrawlers: &cli.IntFlag{ Name: FlagHTTPMaxConcurrentCrawlers, Value: 1, Usage: FlagHTTPMaxConcurrentCrawlersUsage, EnvVars: []string{"DSLIM_HTTP_MAX_CONCURRENT_CRAWLERS"}, }, FlagHTTPProbeProxyEndpoint: &cli.StringFlag{ Name: FlagHTTPProbeProxyEndpoint, Value: "", Usage: FlagHTTPProbeProxyEndpointUsage, EnvVars: []string{"DSLIM_HTTP_PROBE_PROXY_ENDPOINT"}, }, FlagHTTPProbeProxyPort: &cli.IntFlag{ Name: FlagHTTPProbeProxyPort, Value: 0, Usage: FlagHTTPProbeProxyPortUsage, EnvVars: []string{"DSLIM_HTTP_PROBE_PROXY_PORT"}, }, FlagHostExec: &cli.StringSliceFlag{ Name: FlagHostExec, Value: cli.NewStringSlice(), Usage: FlagHostExecUsage, EnvVars: []string{"DSLIM_HOST_EXEC"}, }, FlagHostExecFile: &cli.StringFlag{ Name: FlagHostExecFile, Value: "", Usage: FlagHostExecFileUsage, EnvVars: []string{"DSLIM_HOST_EXEC_FILE"}, }, FlagPublishPort: &cli.StringSliceFlag{ Name: FlagPublishPort, Value: cli.NewStringSlice(), Usage: FlagPublishPortUsage, EnvVars: []string{"DSLIM_PUBLISH_PORT"}, }, FlagPublishExposedPorts: &cli.BoolFlag{ Name: FlagPublishExposedPorts, Usage: FlagPublishExposedPortsUsage, EnvVars: []string{"DSLIM_PUBLISH_EXPOSED"}, }, FlagRunTargetAsUser: &cli.BoolFlag{ Name: FlagRunTargetAsUser, Value: true, Usage: FlagRunTargetAsUserUsage, EnvVars: []string{"DSLIM_RUN_TAS_USER"}, }, FlagShowContainerLogs: &cli.BoolFlag{ Name: FlagShowContainerLogs, Usage: FlagShowContainerLogsUsage, EnvVars: []string{"DSLIM_SHOW_CLOGS"}, }, FlagEnableMondelLogs: &cli.BoolFlag{ Name: FlagEnableMondelLogs, Usage: FlagEnableMondelLogsUsage, EnvVars: []string{"DSLIM_ENABLE_MONDEL"}, }, FlagSensorIPCMode: &cli.StringFlag{ Name: FlagSensorIPCMode, Value: "", Usage: FlagSensorIPCModeUsage, EnvVars: []string{"DSLIM_SENSOR_IPC_MODE"}, }, FlagSensorIPCEndpoint: &cli.StringFlag{ Name: FlagSensorIPCEndpoint, Value: "", Usage: FlagSensorIPCEndpointUsage, EnvVars: []string{"DSLIM_SENSOR_IPC_ENDPOINT"}, }, FlagExec: &cli.StringFlag{ Name: FlagExec, Value: "", Usage: FlagExecUsage, EnvVars: []string{"DSLIM_RC_EXE"}, }, FlagExecFile: &cli.StringFlag{ Name: FlagExecFile, Value: "", Usage: FlagExecFileUsage, EnvVars: []string{"DSLIM_RC_EXE_FILE"}, }, FlagUseLocalMounts: &cli.BoolFlag{ Name: FlagUseLocalMounts, Usage: FlagUseLocalMountsUsage, EnvVars: []string{"DSLIM_USE_LOCAL_MOUNTS"}, }, FlagUseSensorVolume: &cli.StringFlag{ Name: FlagUseSensorVolume, Value: "", Usage: FlagUseSensorVolumeUsage, EnvVars: []string{"DSLIM_USE_SENSOR_VOLUME"}, }, FlagContinueAfter: &cli.StringFlag{ Name: FlagContinueAfter, Value: "probe", Usage: FlagContinueAfterUsage, EnvVars: []string{"DSLIM_CONTINUE_AFTER"}, }, //Container Run Options FlagCRORuntime: &cli.StringFlag{ Name: FlagCRORuntime, Value: "", Usage: FlagCRORuntimeUsage, EnvVars: []string{"DSLIM_CRO_RUNTIME"}, }, FlagCROHostConfigFile: &cli.StringFlag{ Name: FlagCROHostConfigFile, Value: "", Usage: FlagCROHostConfigFileUsage, EnvVars: []string{"DSLIM_CRO_HOST_CONFIG_FILE"}, }, FlagCROSysctl: &cli.StringSliceFlag{ Name: FlagCROSysctl, Value: cli.NewStringSlice(), Usage: FlagCROSysctlUsage, EnvVars: []string{"DSLIM_CRO_SYSCTL"}, }, FlagCROShmSize: &cli.Int64Flag{ Name: FlagCROShmSize, Value: -1, Usage: FlagCROShmSizeUsage, EnvVars: []string{"DSLIM_CRO_SHM_SIZE"}, }, FlagUser: &cli.StringFlag{ Name: FlagUser, Value: "", Usage: FlagUserUsage, EnvVars: []string{"DSLIM_RC_USER"}, }, FlagEntrypoint: &cli.StringFlag{ Name: FlagEntrypoint, Value: "", Usage: FlagEntrypointUsage, EnvVars: []string{"DSLIM_RC_ENTRYPOINT"}, }, FlagCmd: &cli.StringFlag{ Name: FlagCmd, Value: "", Usage: FlagCmdUsage, EnvVars: []string{"DSLIM_RC_CMD"}, }, FlagWorkdir: &cli.StringFlag{ Name: FlagWorkdir, Value: "", Usage: FlagWorkdirUsage, EnvVars: []string{"DSLIM_RC_WORKDIR"}, }, FlagEnv: &cli.StringSliceFlag{ Name: FlagEnv, Value: cli.NewStringSlice(), Usage: FlagEnvUsage, EnvVars: []string{"DSLIM_RC_ENV"}, }, FlagEnvFile: &cli.StringFlag{ Name: FlagEnvFile, Value: "", Usage: FlagEnvFileUsage, EnvVars: []string{"DSLIM_RC_ENV_FILE"}, }, FlagLabel: &cli.StringSliceFlag{ Name: FlagLabel, Value: cli.NewStringSlice(), Usage: FlagLabelUsage, EnvVars: []string{"DSLIM_RC_LABEL"}, }, FlagVolume: &cli.StringSliceFlag{ Name: FlagVolume, Value: cli.NewStringSlice(), Usage: FlagVolumeUsage, EnvVars: []string{"DSLIM_RC_VOLUME"}, }, FlagLink: &cli.StringSliceFlag{ Name: FlagLink, Value: cli.NewStringSlice(), Usage: FlagLinkUsage, EnvVars: []string{"DSLIM_RC_LINK"}, }, FlagEtcHostsMap: &cli.StringSliceFlag{ Name: FlagEtcHostsMap, Value: cli.NewStringSlice(), Usage: FlagEtcHostsMapUsage, EnvVars: []string{"DSLIM_RC_ETC_HOSTS_MAP"}, }, FlagContainerDNS: &cli.StringSliceFlag{ Name: FlagContainerDNS, Value: cli.NewStringSlice(), Usage: FlagContainerDNSUsage, EnvVars: []string{"DSLIM_RC_DNS"}, }, FlagContainerDNSSearch: &cli.StringSliceFlag{ Name: FlagContainerDNSSearch, Value: cli.NewStringSlice(), Usage: FlagContainerDNSSearchUsage, EnvVars: []string{"DSLIM_RC_DNS_SEARCH"}, }, FlagHostname: &cli.StringFlag{ Name: FlagHostname, Value: "", Usage: FlagHostnameUsage, EnvVars: []string{"DSLIM_RC_HOSTNAME"}, }, FlagNetwork: &cli.StringFlag{ Name: FlagNetwork, Value: "", Usage: FlagNetworkUsage, EnvVars: []string{"DSLIM_RC_NET"}, }, FlagExpose: &cli.StringSliceFlag{ Name: FlagExpose, Value: cli.NewStringSlice(), Usage: FlagExposeUsage, EnvVars: []string{"DSLIM_RC_EXPOSE"}, }, FlagMount: &cli.StringSliceFlag{ Name: FlagMount, Value: cli.NewStringSlice(), Usage: FlagMountUsage, EnvVars: []string{"DSLIM_MOUNT"}, }, FlagDeleteFatImage: &cli.BoolFlag{ Name: FlagDeleteFatImage, Usage: FlagDeleteFatImageUsage, EnvVars: []string{"DSLIM_DELETE_FAT"}, }, FlagRTAOnbuildBaseImage: &cli.BoolFlag{ //should be disabled by default Name: FlagRTAOnbuildBaseImage, Usage: FlagRTAOnbuildBaseImageUsage, EnvVars: []string{"DSLIM_RTA_ONBUILD_BI"}, }, FlagRTASourcePT: &cli.BoolFlag{ Name: FlagRTASourcePT, Value: true, //all sources are enabled by default Usage: FlagRTASourcePTUsage, EnvVars: []string{"DSLIM_RTA_SRC_PT"}, }, } //var CommonFlags func Cflag(name string) cli.Flag { cf, ok := CommonFlags[name] if !ok { log.Fatalf("commands.Cflag: unknown flag='%s'", name) } return cf } func HTTPProbeFlags() []cli.Flag { return append([]cli.Flag{ Cflag(FlagHTTPProbeOff), Cflag(FlagHTTPProbe), Cflag(FlagHTTPProbeExitOnFailure), }, HTTPProbeFlagsBasic()...) } func HTTPProbeFlagsBasic() []cli.Flag { return []cli.Flag{ Cflag(FlagHTTPProbeCmd), Cflag(FlagHTTPProbeCmdFile), Cflag(FlagHTTPProbeStartWait), Cflag(FlagHTTPProbeRetryCount), Cflag(FlagHTTPProbeRetryWait), Cflag(FlagHTTPProbePorts), Cflag(FlagHTTPProbeFull), Cflag(FlagHTTPProbeCrawl), Cflag(FlagHTTPCrawlMaxDepth), Cflag(FlagHTTPCrawlMaxPageCount), Cflag(FlagHTTPCrawlConcurrency), Cflag(FlagHTTPMaxConcurrentCrawlers), Cflag(FlagHTTPProbeAPISpec), Cflag(FlagHTTPProbeAPISpecFile), } } /////////////////////////////////// // Update command flag names const ( FlagShowProgress = "show-progress" ) // Update command flag usage info const ( FlagShowProgressUsage = "show progress when the release package is downloaded" ) ================================================ FILE: pkg/app/master/command/clifvgetter.go ================================================ package command //Flag value getters import ( "encoding/json" "fmt" "os" "strconv" "strings" "time" log "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/config" "github.com/slimtoolkit/slim/pkg/app/master/signals" "github.com/slimtoolkit/slim/pkg/docker/dockerclient" ) func GetContainerRunOptions(ctx *cli.Context) (*config.ContainerRunOptions, error) { const op = "commands.GetContainerRunOptions" var cro config.ContainerRunOptions cro.Runtime = ctx.String(FlagCRORuntime) sysctlList := ctx.StringSlice(FlagCROSysctl) if len(sysctlList) > 0 { params, err := ParseTokenMap(sysctlList) if err != nil { log.WithFields(log.Fields{ "op": op, "error": err, }).Error("invalid sysctl options") return nil, err } cro.SysctlParams = params } hostConfigFileName := ctx.String(FlagCROHostConfigFile) if len(hostConfigFileName) > 0 { hostConfigBytes, err := os.ReadFile(hostConfigFileName) if err != nil { log.WithFields(log.Fields{ "op": op, "file.name": hostConfigFileName, "error": err, }).Error("could not read host config file") return nil, err } json.Unmarshal(hostConfigBytes, &cro.HostConfig) } cro.ShmSize = ctx.Int64(FlagCROShmSize) return &cro, nil } func GetHTTPProbeOptions(xc *app.ExecutionContext, ctx *cli.Context, doProbe bool) config.HTTPProbeOptions { opts := config.HTTPProbeOptions{ Full: ctx.Bool(FlagHTTPProbeFull), StartWait: ctx.Int(FlagHTTPProbeStartWait), RetryCount: ctx.Int(FlagHTTPProbeRetryCount), RetryWait: ctx.Int(FlagHTTPProbeRetryWait), CrawlMaxDepth: ctx.Int(FlagHTTPCrawlMaxDepth), CrawlMaxPageCount: ctx.Int(FlagHTTPCrawlMaxPageCount), CrawlConcurrency: ctx.Int(FlagHTTPCrawlConcurrency), CrawlConcurrencyMax: ctx.Int(FlagHTTPMaxConcurrentCrawlers), } if doProbe { opts.Do = true } else { opts.Do = ctx.Bool(FlagHTTPProbe) && !ctx.Bool(FlagHTTPProbeOff) opts.ExitOnFailure = ctx.Bool(FlagHTTPProbeExitOnFailure) } cmds, err := GetHTTPProbes(ctx) if err != nil { xc.Out.Error("param.http.probe", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } opts.Cmds = cmds if opts.Do && len(opts.Cmds) == 0 { //add default probe cmd if the "http-probe" flag is set //but only if there are no custom http probe commands xc.Out.Info("param.http.probe", ovars{ "message": "using default probe", }) opts.Cmds = append(opts.Cmds, GetDefaultHTTPProbe()) if ctx.Bool(FlagHTTPProbeCrawl) { opts.Cmds[0].Crawl = true } } if len(opts.Cmds) > 0 { opts.Do = true } ports, err := ParseHTTPProbesPorts(ctx.String(FlagHTTPProbePorts)) if err != nil { xc.Out.Error("param.http.probe.ports", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } opts.Ports = ports opts.APISpecs = ctx.StringSlice(FlagHTTPProbeAPISpec) apiSpecFiles, fileErrors := ValidateFiles(ctx.StringSlice(FlagHTTPProbeAPISpecFile)) if len(fileErrors) > 0 { for k, v := range fileErrors { err = v xc.Out.Info("error", ovars{ "file": k, "error": err, }) xc.Out.Error("param.error.http.api.spec.file", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } } opts.APISpecFiles = apiSpecFiles if len(opts.APISpecs)+len(opts.APISpecFiles) > 0 { opts.Do = true } return opts } func GetDefaultHTTPProbe() config.HTTPProbeCmd { return config.HTTPProbeCmd{ Protocol: "http", Method: "GET", Resource: "/", } } func GetHTTPProbes(ctx *cli.Context) ([]config.HTTPProbeCmd, error) { httpProbeCmds, err := ParseHTTPProbes(ctx.StringSlice(FlagHTTPProbeCmd)) if err != nil { return nil, err } moreHTTPProbeCmds, err := ParseHTTPProbesFile(ctx.String(FlagHTTPProbeCmdFile)) if err != nil { return nil, err } if moreHTTPProbeCmds != nil { httpProbeCmds = append(httpProbeCmds, moreHTTPProbeCmds...) } return httpProbeCmds, nil } func GetContinueAfter(ctx *cli.Context) (*config.ContinueAfter, error) { info := &config.ContinueAfter{ Mode: config.CAMEnter, } doContinueAfter := ctx.String(FlagContinueAfter) switch doContinueAfter { case config.CAMEnter: info.Mode = config.CAMEnter case config.CAMSignal: info.Mode = config.CAMSignal info.ContinueChan = signals.AppContinueChan case config.CAMProbe: info.Mode = config.CAMProbe case config.CAMExec: info.Mode = config.CAMExec case config.CAMContainerProbe: info.Mode = config.CAMContainerProbe case config.CAMHostExec: info.Mode = config.CAMHostExec case config.CAMAppExit: info.Mode = config.CAMAppExit case config.CAMTimeout: info.Mode = config.CAMTimeout info.Timeout = 60 default: modes := strings.Split(doContinueAfter, "&") if len(modes) > 1 { //not supporting combining signal or custom timeout modes with other modes info.Mode = doContinueAfter } else { if waitTime, err := strconv.Atoi(doContinueAfter); err == nil && waitTime > 0 { info.Mode = config.CAMTimeout info.Timeout = time.Duration(waitTime) } } } return info, nil } func RemoveContinueAfterMode(continueAfter, mode string) string { if continueAfter == mode { return "" } var result []string modes := strings.Split(continueAfter, "&") for _, current := range modes { if current != mode { result = append(result, mode) } } return strings.Join(modes, "&") } func GetContinueAfterModeNames(continueAfter string) []string { return strings.Split(continueAfter, "&") } func GetContainerOverrides(xc *app.ExecutionContext, ctx *cli.Context) (*config.ContainerOverrides, error) { const op = "commands.GetContainerOverrides" doUseEntrypoint := ctx.String(FlagEntrypoint) doUseCmd := ctx.String(FlagCmd) exposePortList := ctx.StringSlice(FlagExpose) volumesList := ctx.StringSlice(FlagVolume) labelsList := ctx.StringSlice(FlagLabel) envList, envErr := ParseEnvFile(ctx.String(FlagEnvFile)) if envErr != nil { return nil, envErr } envList = validateAndCleanEnvVariables(xc, envList, "param.env-file.value") env := validateAndCleanEnvVariables(xc, ctx.StringSlice(FlagEnv), "param.env") envList = append(envList, env...) overrides := &config.ContainerOverrides{ User: ctx.String(FlagUser), Workdir: ctx.String(FlagWorkdir), Env: envList, Network: ctx.String(FlagNetwork), Hostname: ctx.String(FlagHostname), } var err error if len(exposePortList) > 0 { overrides.ExposedPorts, err = ParseDockerExposeOpt(exposePortList) if err != nil { log.WithFields(log.Fields{ "op": op, "error": err, }).Error("invalid expose options") return nil, err } } if len(volumesList) > 0 { volumes, err := ParseTokenSet(volumesList) if err != nil { log.WithFields(log.Fields{ "op": op, "error": err, }).Error("invalid volume options") return nil, err } overrides.Volumes = volumes } if len(labelsList) > 0 { labels, err := ParseTokenMap(labelsList) if err != nil { log.WithFields(log.Fields{ "op": op, "error": err, }).Error("invalid label options") return nil, err } overrides.Labels = labels } overrides.Entrypoint, err = ParseExec(doUseEntrypoint) if err != nil { log.WithFields(log.Fields{ "op": op, "error": err, }).Error("invalid entrypoint option") return nil, err } //TODO: use a '--no-entrypoint' flag instead of this one space hack overrides.ClearEntrypoint = IsOneSpace(doUseEntrypoint) overrides.Cmd, err = ParseExec(doUseCmd) if err != nil { log.WithFields(log.Fields{ "op": op, "error": err, }).Error("invalid cmd option") return nil, err } overrides.ClearCmd = IsOneSpace(doUseCmd) return overrides, nil } func UpdateGlobalFlagValues(appOpts *config.AppOptions, values *GenericParams) *GenericParams { if appOpts == nil || appOpts.Global == nil || values == nil { return values } if appOpts.Global.NoColor != nil { values.NoColor = *appOpts.Global.NoColor } if appOpts.Global.Debug != nil { values.Debug = *appOpts.Global.Debug } if appOpts.Global.Verbose != nil { values.Verbose = *appOpts.Global.Verbose } if appOpts.Global.Quiet != nil { values.QuietCLIMode = *appOpts.Global.Quiet } if appOpts.Global.OutputFormat != nil { values.OutputFormat = *appOpts.Global.OutputFormat } if appOpts.Global.LogLevel != nil { values.LogLevel = *appOpts.Global.LogLevel } if appOpts.Global.LogFormat != nil { values.LogFormat = *appOpts.Global.LogFormat } if appOpts.Global.Log != nil { values.Log = *appOpts.Global.Log } if appOpts.Global.UseTLS != nil { values.ClientConfig.UseTLS = *appOpts.Global.UseTLS } if appOpts.Global.VerifyTLS != nil { values.ClientConfig.VerifyTLS = *appOpts.Global.VerifyTLS } if appOpts.Global.TLSCertPath != nil { values.ClientConfig.TLSCertPath = *appOpts.Global.TLSCertPath } if appOpts.Global.Host != nil { values.ClientConfig.Host = *appOpts.Global.Host } if appOpts.Global.APIVersion != nil { values.ClientConfig.APIVersion = *appOpts.Global.APIVersion } return values } func GlobalFlagValues(ctx *cli.Context) *GenericParams { values := GenericParams{ CheckVersion: ctx.Bool(FlagCheckVersion), Debug: ctx.Bool(FlagDebug), Verbose: ctx.Bool(FlagVerbose), QuietCLIMode: ctx.Bool(FlagQuietCLIMode), LogLevel: ctx.String(FlagLogLevel), LogFormat: ctx.String(FlagLogFormat), OutputFormat: ctx.String(FlagOutputFormat), Log: ctx.String(FlagLog), StatePath: ctx.String(FlagStatePath), ReportLocation: ctx.String(FlagCommandReport), } if values.ReportLocation == "off" { values.ReportLocation = "" } values.InContainer, values.IsDSImage = IsInContainer(ctx.Bool(FlagInContainer)) values.ArchiveState = ArchiveState(ctx.String(FlagArchiveState), values.InContainer) values.ClientConfig = GetDockerClientConfig(ctx) return &values } func GetDockerClientConfig(ctx *cli.Context) *config.DockerClient { config := &config.DockerClient{ APIVersion: ctx.String(FlagAPIVersion), UseTLS: ctx.Bool(FlagUseTLS), VerifyTLS: ctx.Bool(FlagVerifyTLS), TLSCertPath: ctx.String(FlagTLSCertPath), Host: ctx.String(FlagHost), Env: map[string]string{}, } getEnv := func(name string) { if value, exists := os.LookupEnv(name); exists { config.Env[name] = value } } for _, ev := range dockerclient.EnvVarNames { getEnv(ev) } return config } func validateAndCleanEnvVariables(xc *app.ExecutionContext, envList []string, errType string) []string { var envStaging []string if len(envList) == 0 { return envStaging } for i, kv := range envList { kv = strings.TrimSpace(kv) if len(kv) == 0 { continue } if !strings.ContainsAny(kv, "=") { xc.Out.Error(errType, fmt.Sprintf("skipping malformed env var - (index=%d data='%s')", i, kv)) continue } envKeyValue := strings.SplitN(kv, "=", 2) if len(envKeyValue) != 2 { xc.Out.Error(errType, fmt.Sprintf("skipping malformed env var - (index=%d data='%s')", i, kv)) continue } keyIsEmpty := len(strings.TrimSpace(envKeyValue[0])) == 0 //no need to trim value (it may have spaces intentionally) valIsEmpty := len(envKeyValue[1]) == 0 if !keyIsEmpty && !valIsEmpty { envStaging = append(envStaging, kv) } else { xc.Out.Error(errType, fmt.Sprintf("skipping malformed env var - (index=%d data='%s')", i, kv)) } } return envStaging } ================================================ FILE: pkg/app/master/command/clifvparser.go ================================================ package command //Flag value parsers import ( "bytes" "encoding/json" "fmt" "os" "path/filepath" "strconv" "strings" "unicode" "unicode/utf8" "github.com/docker/go-connections/nat" docker "github.com/fsouza/go-dockerclient" "github.com/google/shlex" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app/master/config" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/sysenv" "github.com/slimtoolkit/slim/pkg/util/fsutil" ) const ( DefaultStateArchiveVolumeName = "slim-state" ) func IsInContainer(flag bool) (bool, bool) { if flag { return true, sysenv.HasDSImageFlag() } return sysenv.InDSContainer() } func ArchiveState(flag string, inContainer bool) string { switch flag { case "": switch inContainer { case true: return DefaultStateArchiveVolumeName default: return "" } case "off": return "" default: return flag //should validate if it can be a Docker volume name } } // based on expose opt parsing in Docker func ParseDockerExposeOpt(values []string) (map[docker.Port]struct{}, error) { exposedPorts := map[docker.Port]struct{}{} for _, raw := range values { if strings.Contains(raw, ":") { return nil, fmt.Errorf("invalid EXPOSE format: %s", raw) } proto, ports := nat.SplitProtoPort(raw) startPort, endPort, err := nat.ParsePortRange(ports) if err != nil { return nil, fmt.Errorf("invalid port range in EXPOSE: %s / error: %s", raw, err) } for i := startPort; i <= endPort; i++ { portInfo, err := nat.NewPort(proto, strconv.FormatUint(i, 10)) if err != nil { return nil, err } exposedPorts[docker.Port(portInfo)] = struct{}{} } } return exposedPorts, nil } func ParsePortBindings(values []string) (map[docker.Port][]docker.PortBinding, error) { portBindings := map[docker.Port][]docker.PortBinding{} for _, raw := range values { var ( hostIP = "" hostPort = "" portKey = "" ) parts := strings.Split(raw, ":") //format: // port // hostPort:containerPort // hostIP:hostPort:containerPort // hostIP::containerPort switch len(parts) { case 1: portKey = fmt.Sprintf("%s/tcp", parts[0]) hostPort = parts[0] case 2: hostPort = parts[0] if strings.Contains(parts[1], "/") { portKey = parts[1] } else { portKey = fmt.Sprintf("%s/tcp", parts[1]) } case 3: hostIP = parts[0] if len(parts[1]) > 0 { hostPort = parts[1] } else { hostPort = parts[2] } if strings.Contains(parts[2], "/") { portKey = parts[2] } else { portKey = fmt.Sprintf("%s/tcp", parts[2]) } default: return nil, fmt.Errorf("invalid publish-port: %s", raw) } portBindings[docker.Port(portKey)] = []docker.PortBinding{{ HostIP: hostIP, HostPort: hostPort, }} } return portBindings, nil } func IsOneSpace(value string) bool { if len(value) > 0 && utf8.RuneCountInString(value) == 1 { r, _ := utf8.DecodeRuneInString(value) if r != utf8.RuneError && unicode.IsSpace(r) { return true } } return false } var AllImageOverrides = map[string]bool{ "entrypoint": true, "cmd": true, "workdir": true, "env": true, "expose": true, "volume": true, "label": true, } func ParseImageOverrides(value string) map[string]bool { switch value { case "": return map[string]bool{} case "all": return AllImageOverrides default: parts := strings.Split(value, ",") overrides := map[string]bool{} for _, part := range parts { part = strings.ToLower(part) if _, ok := AllImageOverrides[part]; ok { overrides[part] = true } } return overrides } } func ParseExec(value string) ([]string, error) { if value == "" { return []string{}, nil } if value[0] != '[' { return shlex.Split(value) } var parts []string if err := json.Unmarshal([]byte(value), &parts); err != nil { return nil, err } return parts, nil } func ParseTokenSet(values []string) (map[string]struct{}, error) { tokens := map[string]struct{}{} for _, token := range values { token = strings.TrimSpace(token) if token == "" { continue } tokens[token] = struct{}{} } return tokens, nil } func ParseTokenMap(values []string) (map[string]string, error) { tokens := map[string]string{} for _, token := range values { token = strings.TrimSpace(token) if token == "" { continue } parts := strings.SplitN(token, "=", 2) if len(parts) != 2 { continue } tokens[parts[0]] = parts[1] } return tokens, nil } func ParseCheckTags(values []string) (map[string]string, error) { tags := map[string]string{} for _, raw := range values { raw = strings.TrimSpace(raw) if raw == "" { continue } if !strings.Contains(raw, ":") { return nil, fmt.Errorf("invalid check tag format: %s", raw) } parts := strings.Split(raw, ":") if len(parts) != 2 { return nil, fmt.Errorf("invalid check tag format: %s", raw) } tags[parts[0]] = parts[1] } return tags, nil } func ParseTokenSetFile(filePath string) (map[string]struct{}, error) { tokens := map[string]struct{}{} if filePath == "" { return tokens, nil } fullPath, err := filepath.Abs(filePath) if err != nil { return tokens, err } _, err = os.Stat(fullPath) if err != nil { return tokens, err } fileData, err := os.ReadFile(fullPath) //[]byte if err != nil { return tokens, err } if len(fileData) == 0 { return tokens, nil } lines := strings.Split(string(fileData), "\n") for _, token := range lines { token = strings.TrimSpace(token) if len(token) != 0 { tokens[token] = struct{}{} } } return tokens, nil } func ParseVolumeMounts(values []string) (map[string]config.VolumeMount, error) { volumeMounts := map[string]config.VolumeMount{} for _, raw := range values { if !strings.Contains(raw, ":") { return nil, fmt.Errorf("invalid volume mount format: %s", raw) } parts := strings.Split(raw, ":") if (len(parts) > 3) || (len(parts[0]) < 1) || (len(parts[1]) < 1) || ((len(parts) == 3) && (len(parts[2]) < 1)) { return nil, fmt.Errorf("invalid volume mount format: %s", raw) } mount := config.VolumeMount{ Source: parts[0], Destination: parts[1], Options: "rw", } if len(parts) == 3 { mount.Options = parts[2] } //NOTE: also need to support volume bindings //with the same source, but different destinations volumeMounts[mount.Source] = mount } return volumeMounts, nil } func ParseVolumeMountsAsList(values []string) ([]config.VolumeMount, error) { volumeMounts := map[string]config.VolumeMount{} for _, raw := range values { if !strings.Contains(raw, ":") { return nil, fmt.Errorf("invalid volume mount format: %s", raw) } parts := strings.Split(raw, ":") if (len(parts) > 3) || (len(parts[0]) < 1) || (len(parts[1]) < 1) || ((len(parts) == 3) && (len(parts[2]) < 1)) { return nil, fmt.Errorf("invalid volume mount format: %s", raw) } mount := config.VolumeMount{ Source: parts[0], Destination: parts[1], Options: "rw", } if len(parts) == 3 { mount.Options = parts[2] } key := fmt.Sprintf("%s:%s", mount.Source, mount.Destination) volumeMounts[key] = mount } var volumeList []config.VolumeMount for _, m := range volumeMounts { volumeList = append(volumeList, m) } return volumeList, nil } func ParsePathPerms(raw string) (string, *fsutil.AccessInfo, error) { access := fsutil.NewAccessInfo() //note: will work for ASCII (todo: make it work for unicode) // //DATA FORMAT: // // filePath // filePath:octalFilemodeFlags#uid // filePath:octalFilemodeFlags#uid#gid // //Filemode bits: perms and extra bits (sticky, setuid, setgid) sepIdx := strings.LastIndex(raw, ":") if sepIdx == -1 || sepIdx == (len(raw)-1) { return raw, nil, nil } pathStr := raw[0:sepIdx] metaStr := raw[sepIdx+1:] metaParts := strings.Split(metaStr, "#") var permBitsStr string var extraBitsStr string fileModeStr := metaParts[0] if len(fileModeStr) > 3 { access.PermsOnly = false if len(fileModeStr) > 4 { fileModeStr = fileModeStr[len(fileModeStr)-4:] } extraBitsStr = fileModeStr[0:1] permBitsStr = fileModeStr[1:] } else { access.PermsOnly = true permBitsStr = fileModeStr } permsNum, err := strconv.ParseUint(permBitsStr, 8, 32) if err != nil { return "", nil, err } access.Flags = os.FileMode(permsNum) if len(extraBitsStr) > 0 { extraBits, err := strconv.ParseUint(extraBitsStr, 8, 32) if err != nil { return "", nil, err } access.Flags |= fsutil.FileModeExtraBitsUnix2Go(uint32(extraBits)) } if len(metaParts) > 1 { uidNum, err := strconv.ParseInt(metaParts[1], 10, 32) if err == nil && uidNum > -1 { access.UID = int(uidNum) } } if len(metaParts) > 2 { gidNum, err := strconv.ParseInt(metaParts[2], 10, 32) if err == nil && gidNum > -1 { access.GID = int(gidNum) } } return pathStr, access, nil } func ParsePaths(values []string) map[string]*fsutil.AccessInfo { const op = "commands.ParsePaths" paths := map[string]*fsutil.AccessInfo{} for _, raw := range values { pathStr, access, err := ParsePathPerms(raw) if err != nil { log.WithFields(log.Fields{ "op": op, "line": raw, "error": err, }).Debug("skipping.line") continue } paths[pathStr] = access } return paths } func ValidateFiles(names []string) ([]string, map[string]error) { found := []string{} errors := map[string]error{} for _, name := range names { if name == "" { continue } fullPath, err := filepath.Abs(name) if err != nil { errors[name] = err continue } _, err = os.Stat(fullPath) if err != nil { errors[name] = err continue } found = append(found, name) } return found, errors } func ParsePathsFile(filePath string) (map[string]*fsutil.AccessInfo, error) { const op = "commands.ParsePathsFile" paths := map[string]*fsutil.AccessInfo{} if filePath == "" { return paths, nil } fullPath, err := filepath.Abs(filePath) if err != nil { return paths, err } _, err = os.Stat(fullPath) if err != nil { return paths, err } fileData, err := os.ReadFile(fullPath) //[]byte if err != nil { return paths, err } if len(fileData) == 0 { return paths, nil } lines := strings.Split(string(fileData), "\n") log.WithFields(log.Fields{ "op": op, "file.path": filePath, "full.path": fullPath, "lines.count": len(lines), }).Trace("data") for _, line := range lines { line = strings.TrimSpace(line) if len(line) != 0 { pathStr, access, err := ParsePathPerms(line) if err != nil { log.WithFields(log.Fields{ "op": op, "file.path": filePath, "full.path": fullPath, "line": line, "error": err, }).Debug("skipping.line") continue } paths[pathStr] = access } } return paths, nil } // /// func ParsePathsCreportFile(filePath string) (map[string]*fsutil.AccessInfo, error) { const op = "commands.ParsePathsCreportFile" paths := map[string]*fsutil.AccessInfo{} if filePath == "" { return paths, nil } fullPath, err := filepath.Abs(filePath) if err != nil { return paths, err } _, err = os.Stat(fullPath) if err != nil { return paths, err } fileData, err := os.ReadFile(fullPath) //[]byte if err != nil { return paths, err } if len(fileData) == 0 { return paths, nil } var creport report.ContainerReport if err = json.NewDecoder(bytes.NewReader(fileData)).Decode(&creport); err != nil { return paths, err } for _, finfo := range creport.Image.Files { if finfo == nil || finfo.FilePath == "" { continue } paths[finfo.FilePath] = nil } return paths, nil } ///// func ParseHTTPProbes(values []string) ([]config.HTTPProbeCmd, error) { probes := []config.HTTPProbeCmd{} for _, raw := range values { var crawl bool parts := strings.Split(raw, ":") if parts[0] == "crawl" { crawl = true parts = parts[1:] } proto := "http" method := "GET" resource := "/" //sepCount := strings.Count(raw, ":") switch len(parts) { case 0: case 1: if parts[0] == "" || !isResource(parts[0]) { return nil, fmt.Errorf("invalid HTTP probe command resource: %+v", raw) } resource = parts[0] case 2: if parts[0] != "" && !isMethod(parts[0]) { return nil, fmt.Errorf("invalid HTTP probe command method: %+v", raw) } method = strings.ToUpper(parts[0]) if parts[1] == "" || !isResource(parts[1]) { return nil, fmt.Errorf("invalid HTTP probe command resource: %+v", raw) } resource = parts[1] case 3: if parts[0] != "" && !config.IsProto(parts[0]) { return nil, fmt.Errorf("invalid HTTP probe command protocol: %+v", raw) } proto = strings.ToLower(parts[0]) if parts[1] != "" && !isMethod(parts[1]) { return nil, fmt.Errorf("invalid HTTP probe command method: %+v", raw) } method = strings.ToUpper(parts[1]) if parts[2] == "" || !isResource(parts[2]) { return nil, fmt.Errorf("invalid HTTP probe command resource: %+v", raw) } resource = parts[2] default: return nil, fmt.Errorf("invalid HTTP probe command: %s", raw) } cmd := config.HTTPProbeCmd{ Protocol: proto, Method: method, Resource: resource, Crawl: crawl, } probes = append(probes, cmd) } return probes, nil } func ParseHTTPProbesFile(filePath string) ([]config.HTTPProbeCmd, error) { probes := []config.HTTPProbeCmd{} if filePath != "" { fullPath, err := filepath.Abs(filePath) if err != nil { return nil, err } _, err = os.Stat(fullPath) if err != nil { return nil, err } configFile, err := os.Open(fullPath) if err != nil { return nil, err } defer configFile.Close() var configs config.HTTPProbeCmds if err = json.NewDecoder(configFile).Decode(&configs); err != nil { return nil, err } for _, cmd := range configs.Commands { if cmd.Protocol != "" && !config.IsProto(cmd.Protocol) { return nil, fmt.Errorf("invalid HTTP probe command protocol: %+v", cmd) } if cmd.Method != "" && !isMethod(cmd.Method) { return nil, fmt.Errorf("invalid HTTP probe command method: %+v", cmd) } if cmd.Method == "" { cmd.Method = "GET" } cmd.Method = strings.ToUpper(cmd.Method) if cmd.Resource == "" || !isResource(cmd.Resource) { return nil, fmt.Errorf("invalid HTTP probe command resource: %+v", cmd) } if cmd.Port != 0 && !isPortNum(cmd.Port) { return nil, fmt.Errorf("invalid HTTP probe command port: %v", cmd) } if cmd.BodyFile != "" { bfFullPath, err := filepath.Abs(cmd.BodyFile) if err != nil { return nil, err } _, err = os.Stat(bfFullPath) if err != nil { return nil, err } cmd.BodyFile = bfFullPath //the body data file should be ok to load //will load the data at runtime } probes = append(probes, cmd) } } return probes, nil } func isMethod(value string) bool { switch strings.ToUpper(value) { case "HEAD", "GET", "POST", "PUT", "DELETE", "PATCH": return true default: return false } } func isResource(value string) bool { if value != "" && value[0] == '/' { return true } return false } func isPortNum(value int) bool { if 1 <= value && value <= 65535 { return true } return false } func ParseHTTPProbesPorts(portList string) ([]uint16, error) { var ports []uint16 if portList == "" { return ports, nil } parts := strings.Split(portList, ",") for _, part := range parts { port, err := strconv.ParseUint(part, 10, 16) if err != nil { return nil, err } ports = append(ports, uint16(port)) } return ports, nil } func ParseHTTPProbeExecFile(filePath string) ([]string, error) { var appCalls []string if filePath == "" { return appCalls, nil } fullPath, err := filepath.Abs(filePath) if err != nil { return appCalls, err } _, err = os.Stat(fullPath) if err != nil { return appCalls, err } fileData, err := os.ReadFile(fullPath) if err != nil { return appCalls, err } if len(fileData) == 0 { return appCalls, nil } lines := strings.Split(string(fileData), "\n") for _, appCall := range lines { appCall = strings.TrimSpace(appCall) if len(appCall) != 0 { appCalls = append(appCalls, appCall) } } return appCalls, nil } func ParseLinesWithCommentsFile(filePath string) ([]string, error) { var output []string if filePath == "" { return output, nil } fullPath, err := filepath.Abs(filePath) if err != nil { return output, err } _, err = os.Stat(fullPath) if err != nil { return output, err } fileData, err := os.ReadFile(fullPath) if err != nil { return output, err } if len(fileData) == 0 { return output, nil } lines := strings.Split(string(fileData), "\n") for _, line := range lines { line = strings.TrimSpace(line) if len(line) != 0 && !strings.HasPrefix(line, "#") { output = append(output, line) } } return output, nil } func IsTrueStr(value string) bool { if strings.ToLower(value) == "true" { return true } return false } func ParseEnvFile(filePath string) ([]string, error) { var output []string if filePath == "" { return output, nil } fullPath, err := filepath.Abs(filePath) if err != nil { return output, err } _, err = os.Stat(fullPath) if err != nil { return output, err } fileData, err := os.ReadFile(fullPath) if err != nil { return output, err } if len(fileData) == 0 { return output, nil } lines := strings.Split(string(fileData), "\n") for _, line := range lines { line = strings.TrimSpace(line) if len(line) > 0 { //env var format validation is done separately output = append(output, line) } } return output, nil } ================================================ FILE: pkg/app/master/command/cliprompt.go ================================================ package command //CLI prompts import ( "fmt" "os" "runtime" "strings" "time" "github.com/c-bata/go-prompt" "github.com/c-bata/go-prompt/completer" "github.com/dustin/go-humanize" dockerapi "github.com/fsouza/go-dockerclient" "github.com/google/shlex" log "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/config" "github.com/slimtoolkit/slim/pkg/docker/dockerclient" "github.com/slimtoolkit/slim/pkg/docker/dockerutil" "github.com/slimtoolkit/slim/pkg/util/errutil" "github.com/slimtoolkit/slim/pkg/util/fsutil" "github.com/slimtoolkit/slim/pkg/version" ) type InteractiveApp struct { appPrompt *prompt.Prompt fpCompleter completer.FilePathCompleter app *cli.App dclient *dockerapi.Client } func NewInteractiveApp(app *cli.App, gparams *GenericParams) *InteractiveApp { ia := InteractiveApp{ app: app, fpCompleter: completer.FilePathCompleter{ IgnoreCase: true, }, } client, err := dockerclient.New(gparams.ClientConfig) if err == dockerclient.ErrNoDockerInfo { exitMsg := "missing Docker connection info" if gparams.InContainer && gparams.IsDSImage { exitMsg = "make sure to pass the Docker connect parameters to the slim app container" } fmt.Printf("slim: info=docker.connect.error message='%s'\n", exitMsg) fmt.Printf("slim: state=exited version=%s location='%s'\n", version.Current(), fsutil.ExeDir()) os.Exit(-777) } errutil.FailOn(err) ia.dclient = client ia.appPrompt = prompt.New( ia.execute, ia.complete, prompt.OptionTitle(fmt.Sprintf("%s: interactive prompt", AppName)), prompt.OptionPrefix(">>> "), prompt.OptionInputTextColor(prompt.Red), prompt.OptionCompletionWordSeparator(completer.FilePathCompletionSeparator), ) return &ia } func (ia *InteractiveApp) execute(command string) { command = strings.TrimSpace(command) parts, err := shlex.Split(command) if err != nil { log.Fatal(err) } if len(parts) == 0 { return } if parts[0] == "exit" { app.ShowCommunityInfo(OutputFormatText) os.Exit(0) } partsCount := len(parts) for i := 0; i < partsCount; i++ { if parts[i] == "" { continue } if strings.HasPrefix(parts[i], "--") && (i+1) < partsCount && (parts[i+1] == "true" || parts[i+1] == "false") { parts[i] = fmt.Sprintf("%s=%s", parts[i], parts[i+1]) parts[i+1] = "" } } args := []string{AppName} for _, val := range parts { if val == "" { continue } args = append(args, val) } if err := ia.app.Run(args); err != nil { log.Fatal(err) } } func (ia *InteractiveApp) complete(params prompt.Document) []prompt.Suggest { allParamsLine := params.TextBeforeCursor() allParamsLine = strings.TrimSpace(allParamsLine) if allParamsLine == "" { return append(CommandSuggestions, GlobalFlagSuggestions...) } commandState := newCurrentCommandState() commandState.Dclient = ia.dclient currentToken := params.GetWordBeforeCursor() commandState.CurrentToken = currentToken allTokens := strings.Split(allParamsLine, " ") commandState.AllTokensList = allTokens var prevToken string prevTokenIdx := -1 tokenCount := len(allTokens) if tokenCount > 0 { if currentToken == "" { //currentToken 'points' past allTokens[last] prevTokenIdx = tokenCount - 1 prevToken = allTokens[prevTokenIdx] } else { //currentToken 'points' to allTokens[last] if tokenCount >= 2 { prevTokenIdx = tokenCount - 2 prevToken = allTokens[prevTokenIdx] } } } commandState.PrevToken = prevToken commandState.PrevTokenIdx = prevTokenIdx commandState.State = InputStateCommand if prevToken == "" { saveCurrentCommandState(commandState) suggestions := append(CommandSuggestions, GlobalFlagSuggestions...) return prompt.FilterHasPrefix(suggestions, currentToken, true) } commandTokenIdx := -1 lastValueIdx := -1 var lastFlagName string for i := 0; i <= prevTokenIdx; i++ { if strings.HasPrefix(allTokens[i], "--") { lastFlagName = allTokens[i] lastValueIdx = -1 } else { if lastFlagName == "" { //non-flag name token //command token if the previous token was not a flag name commandTokenIdx = i break } if lastFlagName == "" { lastValueIdx = i } else { lastFlagName = "" } } } if commandTokenIdx == -1 && lastValueIdx > -1 { commandTokenIdx = lastValueIdx } if commandTokenIdx == -1 { saveCurrentCommandState(commandState) if strings.HasPrefix(prevToken, "--") { if completeValue, ok := GlobalFlagValueSuggestions[prevToken]; ok && completeValue != nil { return completeValue(ia, currentToken, params) } } else { suggestions := append(CommandSuggestions, GlobalFlagSuggestions...) return prompt.FilterHasPrefix(suggestions, currentToken, true) } return []prompt.Suggest{} } commandToken := allTokens[commandTokenIdx] commandState.CommandTokenIdx = commandTokenIdx commandState.Command = commandToken if strings.HasPrefix(commandState.CurrentToken, "--") { commandState.State = InputStateCommandFlag } else { commandState.State = InputStateCommandFlagValue } lastFlagName = "" for i := 0; i < commandTokenIdx; i++ { if strings.HasPrefix(allTokens[i], "--") { lastFlagName = allTokens[i] } else { if lastFlagName != "" { commandState.GlobalFlags[lastFlagName] = allTokens[i] lastFlagName = "" } } } if commandTokenIdx == (tokenCount - 1) { saveCurrentCommandState(commandState) if currentToken != "" { //currentToken still points to the command token return prompt.FilterHasPrefix(CommandSuggestions, currentToken, true) } else { //need to return the command flag suggestions if cmdFlagSuggestions, ok := CommandFlagSuggestions[commandToken]; ok && cmdFlagSuggestions != nil { return prompt.FilterHasPrefix(cmdFlagSuggestions.Names, currentToken, true) } else { return []prompt.Suggest{} } } } else { lastFlagName = "" for i := commandTokenIdx + 1; i < tokenCount; i++ { if strings.HasPrefix(allTokens[i], "--") { lastFlagName = allTokens[i] } else { if lastFlagName != "" { valList := commandState.CommandFlags[lastFlagName] valList = append(valList, allTokens[i]) commandState.CommandFlags[lastFlagName] = valList lastFlagName = "" } } } saveCurrentCommandState(commandState) } cmdFlagSuggestions, ok := CommandFlagSuggestions[commandToken] if !ok { return []prompt.Suggest{} } if strings.HasPrefix(prevToken, "--") { if completeValue, ok := cmdFlagSuggestions.Values[prevToken]; ok && completeValue != nil { return completeValue(ia, currentToken, params) } } else { return prompt.FilterHasPrefix(cmdFlagSuggestions.Names, currentToken, true) } return []prompt.Suggest{} } func (ia *InteractiveApp) Run() { ia.appPrompt.Run() } ///////////////////////////////////////////// type CompleteValue func(ia *InteractiveApp, token string, params prompt.Document) []prompt.Suggest type FlagSuggestions struct { Names []prompt.Suggest Values map[string]CompleteValue } var CommandFlagSuggestions = map[string]*FlagSuggestions{} var CommandSuggestions = []prompt.Suggest{ {Text: "exit", Description: "Exit app"}, } const ( InputStateEmpty = "empty" InputStateGlobalFlag = "global.flag" InputStateGlobalFlagValue = "global.flag.value" InputStateCommand = "command" InputStateCommandFlag = "command.flag" InputStateCommandFlagValue = "command.flag.value" ) type CurrentCommandState struct { Dclient *dockerapi.Client State string AllTokensList []string CurrentToken string PrevToken string PrevTokenIdx int CommandTokenIdx int GlobalFlags map[string]string Command string CommandFlags map[string][]string } func newCurrentCommandState() *CurrentCommandState { return &CurrentCommandState{ State: InputStateEmpty, PrevTokenIdx: -1, CommandTokenIdx: -1, GlobalFlags: map[string]string{}, CommandFlags: map[string][]string{}, } } func (ref *CurrentCommandState) GetCFValue(name string) string { return ref.GetCFValueWithDefault(name, "") } func (ref *CurrentCommandState) GetCFValueWithDefault(name string, dvalue string) string { fullFlag := FullFlagName(name) vals, found := ref.CommandFlags[fullFlag] if found && len(vals) > 0 && vals[0] != "" { return vals[0] } return dvalue } func saveCurrentCommandState(value *CurrentCommandState) { //fmt.Printf("\nsaveCurrentCommandState: %#v\n\n",value) gCurrentCommandState = value } var gCurrentCommandState *CurrentCommandState func GetCurrentCommandState() *CurrentCommandState { return gCurrentCommandState } ///////////////////////////////////////////// //NOTE: command packages will add their prompt command suggestion in their init() var GlobalFlagSuggestions = []prompt.Suggest{ {Text: FullFlagName(FlagStatePath), Description: FlagStatePathUsage}, {Text: FullFlagName(FlagCommandReport), Description: FlagCommandReportUsage}, {Text: FullFlagName(FlagDebug), Description: FlagDebugUsage}, {Text: FullFlagName(FlagVerbose), Description: FlagVerboseUsage}, {Text: FullFlagName(FlagLogLevel), Description: FlagLogLevelUsage}, {Text: FullFlagName(FlagLog), Description: FlagLogUsage}, {Text: FullFlagName(FlagLogFormat), Description: FlagLogFormatUsage}, {Text: FullFlagName(FlagQuietCLIMode), Description: FlagQuietCLIModeUsage}, {Text: FullFlagName(FlagOutputFormat), Description: FlagOutputFormatUsage}, {Text: FullFlagName(FlagUseTLS), Description: FlagUseTLSUsage}, {Text: FullFlagName(FlagVerifyTLS), Description: FlagVerifyTLSUsage}, {Text: FullFlagName(FlagTLSCertPath), Description: FlagTLSCertPathUsage}, {Text: FullFlagName(FlagHost), Description: FlagHostUsage}, {Text: FullFlagName(FlagArchiveState), Description: FlagArchiveStateUsage}, {Text: FullFlagName(FlagInContainer), Description: FlagInContainerUsage}, {Text: FullFlagName(FlagCheckVersion), Description: FlagCheckVersionUsage}, {Text: FullFlagName(FlagNoColor), Description: FlagNoColorUsage}, } var GlobalFlagValueSuggestions = map[string]CompleteValue{ FullFlagName(FlagQuietCLIMode): CompleteBool, FullFlagName(FlagOutputFormat): CompleteOutputFormat, FullFlagName(FlagDebug): CompleteBool, FullFlagName(FlagVerbose): CompleteBool, FullFlagName(FlagNoColor): CompleteBool, FullFlagName(FlagCheckVersion): CompleteTBool, } func FullFlagName(name string) string { return fmt.Sprintf("--%s", name) } var boolValues = []prompt.Suggest{ {Text: "false", Description: "default"}, {Text: "true"}, } var tboolValues = []prompt.Suggest{ {Text: "true", Description: "default"}, {Text: "false"}, } var continueAfterValues = []prompt.Suggest{ {Text: config.CAMAppExit, Description: "Continue after the target app exits"}, {Text: config.CAMHostExec, Description: "Continue after host command execution is finished running"}, {Text: config.CAMExec, Description: "Continue after container command execution is finished running"}, {Text: config.CAMProbe, Description: "Continue after the HTTP probe is finished running"}, {Text: config.CAMEnter, Description: "Use the key to indicate you that you are done using the container"}, {Text: config.CAMSignal, Description: "Use SIGUSR1 to signal that you are done using the container"}, {Text: config.CAMTimeout, Description: "Continue after the default timeout (60 seconds)"}, {Text: config.CAMContainerProbe, Description: "Continue after the probed container exits"}, {Text: "", Description: "Enter the number of seconds to wait instead of "}, } var consoleOutputValues = []prompt.Suggest{ {Text: OutputFormatText, Description: "Default, output in text format (as a table in quiet CLI mode)"}, {Text: OutputFormatJSON, Description: "JSON output format"}, } var ipcModeValues = []prompt.Suggest{ {Text: "proxy", Description: "Proxy sensor ipc mode"}, {Text: "direct", Description: "Direct sensor ipc mode"}, } func CompleteProgress(ia *InteractiveApp, token string, params prompt.Document) []prompt.Suggest { switch runtime.GOOS { case "darwin": return CompleteTBool(ia, token, params) default: return CompleteBool(ia, token, params) } } func CompleteBool(ia *InteractiveApp, token string, params prompt.Document) []prompt.Suggest { return prompt.FilterHasPrefix(boolValues, token, true) } func CompleteTBool(ia *InteractiveApp, token string, params prompt.Document) []prompt.Suggest { return prompt.FilterHasPrefix(tboolValues, token, true) } func CompleteContinueAfter(ia *InteractiveApp, token string, params prompt.Document) []prompt.Suggest { return prompt.FilterHasPrefix(continueAfterValues, token, true) } func CompleteOutputFormat(ia *InteractiveApp, token string, params prompt.Document) []prompt.Suggest { return prompt.FilterHasPrefix(consoleOutputValues, token, true) } func CompleteIPCMode(ia *InteractiveApp, token string, params prompt.Document) []prompt.Suggest { return prompt.FilterHasPrefix(ipcModeValues, token, true) } func CompleteImage(ia *InteractiveApp, token string, params prompt.Document) []prompt.Suggest { images, err := dockerutil.ListImages(ia.dclient, "") if err != nil { log.Errorf("CompleteImage(%q): error - %v", token, err) return []prompt.Suggest{} } var values []prompt.Suggest for name, info := range images { description := fmt.Sprintf("size=%v created=%v id=%v", humanize.Bytes(uint64(info.Size)), time.Unix(info.Created, 0).Format(time.RFC3339), info.ID) entry := prompt.Suggest{ Text: name, Description: description, } values = append(values, entry) } return prompt.FilterContains(values, token, true) } func CompleteVolume(ia *InteractiveApp, token string, params prompt.Document) []prompt.Suggest { names, err := dockerutil.ListVolumes(ia.dclient, token) if err != nil { log.Errorf("completeVolume(%q): error - %v", token, err) return []prompt.Suggest{} } var values []prompt.Suggest for _, name := range names { entry := prompt.Suggest{ Text: name, } values = append(values, entry) } return prompt.FilterContains(values, token, true) } func CompleteNetwork(ia *InteractiveApp, token string, params prompt.Document) []prompt.Suggest { names, err := dockerutil.ListNetworks(ia.dclient, token) if err != nil { log.Errorf("completeNetwork(%q): error - %v", token, err) return []prompt.Suggest{} } var values []prompt.Suggest for _, name := range names { entry := prompt.Suggest{ Text: name, } values = append(values, entry) } return prompt.FilterContains(values, token, true) } func CompleteFile(ia *InteractiveApp, token string, params prompt.Document) []prompt.Suggest { return ia.fpCompleter.Complete(params) } ================================================ FILE: pkg/app/master/command/common.go ================================================ package command import ( "context" "errors" "fmt" "os" "os/exec" "path/filepath" "strings" "time" "github.com/c-bata/go-prompt" docker "github.com/fsouza/go-dockerclient" "github.com/google/shlex" log "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/config" "github.com/slimtoolkit/slim/pkg/docker/dockerutil" "github.com/slimtoolkit/slim/pkg/util/fsutil" ) const ( ImagesStateRootPath = "images" ) var ( ErrNoGlobalParams = errors.New("No global params") ) type ovars = app.OutVars ///////////////////////////////////////////////////////// type CLIContextKey int const ( GlobalParams CLIContextKey = 1 AppParams CLIContextKey = 2 ) func CLIContextSave(ctx context.Context, key CLIContextKey, data interface{}) context.Context { return context.WithValue(ctx, key, data) } func CLIContextGet(ctx context.Context, key CLIContextKey) interface{} { if ctx == nil { return nil } return ctx.Value(key) } ///////////////////////////////////////////////////////// type GenericParams struct { NoColor bool CheckVersion bool Debug bool Verbose bool QuietCLIMode bool LogLevel string LogFormat string OutputFormat string Log string StatePath string ReportLocation string InContainer bool IsDSImage bool ArchiveState string ClientConfig *config.DockerClient } // TODO: spread these code types across all command definition, so it's not all defined here // Exit Code Types const ( ECTCommon = 0x01000000 ECTBuild = 0x02000000 ectProfile = 0x03000000 ectInfo = 0x04000000 ectUpdate = 0x05000000 ectVersion = 0x06000000 ECTXray = 0x07000000 ECTRun = 0x08000000 ECTMerge = 0x09000000 ) // Build command exit codes const ( ECCOther = iota + 1 ECCImageNotFound ECCNoDockerConnectInfo ECCBadNetworkName ) const ( AppName = "slim" appName = "slim" ) //Common command handler code func DoArchiveState(logger *log.Entry, client *docker.Client, localStatePath, volumeName, stateKey string) error { if volumeName == "" { return nil } err := dockerutil.HasVolume(client, volumeName) switch { case err == nil: logger.Debugf("archiveState: already have volume = %v", volumeName) case err == dockerutil.ErrNotFound: logger.Debugf("archiveState: no volume yet = %v", volumeName) if dockerutil.HasEmptyImage(client) == dockerutil.ErrNotFound { err := dockerutil.BuildEmptyImage(client) if err != nil { logger.Debugf("archiveState: dockerutil.BuildEmptyImage() - error = %v", err) return err } } err = dockerutil.CreateVolumeWithData(client, "", volumeName, nil) if err != nil { logger.Debugf("archiveState: dockerutil.CreateVolumeWithData() - error = %v", err) return err } default: logger.Debugf("archiveState: dockerutil.HasVolume() - error = %v", err) return err } return dockerutil.CopyToVolume(client, volumeName, localStatePath, ImagesStateRootPath, stateKey) } func CopyMetaArtifacts(logger *log.Entry, names []string, artifactLocation, targetLocation string) bool { if targetLocation != "" { if !fsutil.Exists(artifactLocation) { logger.Debugf("copyMetaArtifacts() - bad artifact location (%v)\n", artifactLocation) return false } if len(names) == 0 { logger.Debug("copyMetaArtifacts() - no artifact names") return false } for _, name := range names { srcPath := filepath.Join(artifactLocation, name) if fsutil.Exists(srcPath) && fsutil.IsRegularFile(srcPath) { dstPath := filepath.Join(targetLocation, name) err := fsutil.CopyRegularFile(false, srcPath, dstPath, true) if err != nil { logger.Debugf("copyMetaArtifacts() - error saving file: %v\n", err) return false } } } return true } logger.Debug("copyMetaArtifacts() - no target location") return false } func ConfirmNetwork(logger *log.Entry, client *docker.Client, network string) bool { if network == "" { return true } if networks, err := client.ListNetworks(); err == nil { for _, n := range networks { if n.Name == network { return true } } } else { logger.Debugf("confirmNetwork() - error getting networks = %v", err) } return false } // / func UpdateImageRef(logger *log.Entry, ref, override string) string { logger.Debugf("UpdateImageRef() - ref='%s' override='%s'", ref, override) if override == "" { return ref } refParts := strings.SplitN(ref, ":", 2) refImage := refParts[0] refTag := "" if len(refParts) > 1 { refTag = refParts[1] } overrideParts := strings.SplitN(override, ":", 2) switch len(overrideParts) { case 2: refImage = overrideParts[0] refTag = overrideParts[1] case 1: refTag = overrideParts[0] } if refTag == "" { //shouldn't happen refTag = "latest" } return fmt.Sprintf("%s:%s", refImage, refTag) } func RunHostExecProbes(printState bool, xc *app.ExecutionContext, hostExecProbes []string) { if len(hostExecProbes) > 0 { var callCount uint var okCount uint var errCount uint if printState { xc.Out.Info("host.exec.probes", ovars{ "count": len(hostExecProbes), }) } for idx, appCall := range hostExecProbes { if printState { xc.Out.Info("host.exec.probes", ovars{ "idx": idx, "app": appCall, }) } xc.Out.Info("host.exec.probe.output.start") //TODO LATER: //add more parameters and outputs for more advanced execution control capabilities err := exeAppCall(appCall) xc.Out.Info("host.exec.probe.output.end") callCount++ statusCode := "error" callErrorStr := "none" if err == nil { okCount++ statusCode = "ok" } else { errCount++ callErrorStr = err.Error() } if printState { xc.Out.Info("host.exec.probes", ovars{ "idx": idx, "app": appCall, "status": statusCode, "error": callErrorStr, "time": time.Now().UTC().Format(time.RFC3339), }) } } } } func exeAppCall(appCall string) error { ctx, cancel := context.WithTimeout(context.Background(), 200*time.Second) defer cancel() appCall = strings.TrimSpace(appCall) args, err := shlex.Split(appCall) if err != nil { log.Errorf("exeAppCall(%s): call parse error: %v", appCall, err) return err } if len(args) == 0 { return fmt.Errorf("empty appCall") } cmd := exec.CommandContext(ctx, args[0], args[1:]...) //cmd.Dir = "." cmd.Stdin = os.Stdin //var outBuf, errBuf bytes.Buffer //cmd.Stdout = io.MultiWriter(os.Stdout, &outBuf) //cmd.Stderr = io.MultiWriter(os.Stderr, &errBuf) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Start(); err != nil { log.Errorf("exeAppCall(%s): command start error: %v", appCall, err) return err } err = cmd.Wait() fmt.Printf("\n") if err != nil { log.Fatalf("exeAppCall(%s): command exited with error: %v", appCall, err) return err } //TODO: process outBuf and errBuf here return nil } /////////////////////////////////////// // var CLI []*cli.Command var cliCommands []*cli.Command func AddCLICommand( name string, cmd *cli.Command, cmdSuggestion prompt.Suggest, flagSuggestions *FlagSuggestions) { cliCommands = append(cliCommands, cmd) if flagSuggestions != nil { CommandFlagSuggestions[name] = flagSuggestions } if cmdSuggestion.Text != "" { CommandSuggestions = append(CommandSuggestions, cmdSuggestion) } } func GetCommands() []*cli.Command { return cliCommands } ================================================ FILE: pkg/app/master/command/containerize/cli.go ================================================ package containerize import ( "github.com/urfave/cli/v2" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" ) const ( Name = "containerize" Usage = "Containerize the target app" Alias = "c" ) var CLI = &cli.Command{ Name: Name, Aliases: []string{Alias}, Usage: Usage, Action: func(ctx *cli.Context) error { gcvalues := command.GlobalFlagValues(ctx) xc := app.NewExecutionContext( Name, gcvalues.QuietCLIMode, gcvalues.OutputFormat) targetRef := ctx.String(command.FlagTarget) if targetRef == "" { if ctx.Args().Len() < 1 { xc.Out.Error("param.target", "missing target") cli.ShowCommandHelp(ctx, Name) return nil } else { targetRef = ctx.Args().First() } } OnCommand( xc, gcvalues, targetRef) return nil }, } ================================================ FILE: pkg/app/master/command/containerize/handler.go ================================================ package containerize import ( "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/app/master/version" cmd "github.com/slimtoolkit/slim/pkg/command" "github.com/slimtoolkit/slim/pkg/docker/dockerclient" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/util/errutil" "github.com/slimtoolkit/slim/pkg/util/fsutil" v "github.com/slimtoolkit/slim/pkg/version" log "github.com/sirupsen/logrus" ) const appName = command.AppName type ovars = app.OutVars // OnCommand implements the 'containerize' command func OnCommand( xc *app.ExecutionContext, gparams *command.GenericParams, targetRef string) { const cmdName = Name logger := log.WithFields(log.Fields{"app": appName, "cmd": cmdName}) viChan := version.CheckAsync(gparams.CheckVersion, gparams.InContainer, gparams.IsDSImage) cmdReport := report.NewContainerizeCommand(gparams.ReportLocation, gparams.InContainer) cmdReport.State = cmd.StateStarted xc.Out.State("started") xc.Out.Info("params", ovars{ "target": targetRef, }) client, err := dockerclient.New(gparams.ClientConfig) if err == dockerclient.ErrNoDockerInfo { exitMsg := "missing Docker connection info" if gparams.InContainer && gparams.IsDSImage { exitMsg = "make sure to pass the Docker connect parameters to the slim app container" } xc.Out.Info("docker.connect.error", ovars{ "message": exitMsg, }) exitCode := command.ECTCommon | command.ECCNoDockerConnectInfo xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Exit(exitCode) } errutil.FailOn(err) if gparams.Debug { version.Print(xc, cmdName, logger, client, false, gparams.InContainer, gparams.IsDSImage) } xc.Out.State("completed") cmdReport.State = cmd.StateCompleted xc.Out.State("done") vinfo := <-viChan version.PrintCheckVersion(xc, "", vinfo) cmdReport.State = cmd.StateDone if cmdReport.Save() { xc.Out.Info("report", ovars{ "file": cmdReport.ReportLocation(), }) } } ================================================ FILE: pkg/app/master/command/containerize/init/init.go ================================================ package init import ( "github.com/slimtoolkit/slim/pkg/app/master/command/containerize" ) func init() { containerize.RegisterCommand() } ================================================ FILE: pkg/app/master/command/containerize/prompt.go ================================================ package containerize import ( "github.com/c-bata/go-prompt" ) var CommandSuggestion = prompt.Suggest{ Text: Name, Description: Usage, } ================================================ FILE: pkg/app/master/command/containerize/register.go ================================================ package containerize import ( "github.com/slimtoolkit/slim/pkg/app/master/command" ) func RegisterCommand() { command.AddCLICommand( Name, CLI, CommandSuggestion, nil) } ================================================ FILE: pkg/app/master/command/convert/cli.go ================================================ package convert import ( "github.com/urfave/cli/v2" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" ) const ( Name = "convert" Usage = "Convert container image" Alias = "k" ) var CLI = &cli.Command{ Name: Name, Aliases: []string{Alias}, Usage: Usage, Action: func(ctx *cli.Context) error { gcvalues := command.GlobalFlagValues(ctx) xc := app.NewExecutionContext( Name, gcvalues.QuietCLIMode, gcvalues.OutputFormat) targetRef := ctx.String(command.FlagTarget) if targetRef == "" { if ctx.Args().Len() < 1 { xc.Out.Error("param.target", "missing target") cli.ShowCommandHelp(ctx, Name) return nil } else { targetRef = ctx.Args().First() } } OnCommand( xc, gcvalues, targetRef) return nil }, } ================================================ FILE: pkg/app/master/command/convert/handler.go ================================================ package convert import ( "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/app/master/version" cmd "github.com/slimtoolkit/slim/pkg/command" "github.com/slimtoolkit/slim/pkg/docker/dockerclient" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/util/errutil" "github.com/slimtoolkit/slim/pkg/util/fsutil" v "github.com/slimtoolkit/slim/pkg/version" log "github.com/sirupsen/logrus" ) const appName = command.AppName type ovars = app.OutVars // OnCommand implements the 'convert' command func OnCommand( xc *app.ExecutionContext, gparams *command.GenericParams, targetRef string) { const cmdName = Name logger := log.WithFields(log.Fields{"app": appName, "cmd": cmdName}) viChan := version.CheckAsync(gparams.CheckVersion, gparams.InContainer, gparams.IsDSImage) cmdReport := report.NewConvertCommand(gparams.ReportLocation, gparams.InContainer) cmdReport.State = cmd.StateStarted xc.Out.State("started") xc.Out.Info("params", ovars{ "target": targetRef, }) client, err := dockerclient.New(gparams.ClientConfig) if err == dockerclient.ErrNoDockerInfo { exitMsg := "missing Docker connection info" if gparams.InContainer && gparams.IsDSImage { exitMsg = "make sure to pass the Docker connect parameters to the slim app container" } xc.Out.Info("docker.connect.error", ovars{ "message": exitMsg, }) exitCode := command.ECTCommon | command.ECCNoDockerConnectInfo xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Exit(exitCode) } errutil.FailOn(err) if gparams.Debug { version.Print(xc, cmdName, logger, client, false, gparams.InContainer, gparams.IsDSImage) } xc.Out.State("completed") cmdReport.State = cmd.StateCompleted xc.Out.State("done") vinfo := <-viChan version.PrintCheckVersion(xc, "", vinfo) cmdReport.State = cmd.StateDone if cmdReport.Save() { xc.Out.Info("report", ovars{ "file": cmdReport.ReportLocation(), }) } } ================================================ FILE: pkg/app/master/command/convert/init/init.go ================================================ package init import ( "github.com/slimtoolkit/slim/pkg/app/master/command/convert" ) func init() { convert.RegisterCommand() } ================================================ FILE: pkg/app/master/command/convert/prompt.go ================================================ package convert import ( "github.com/c-bata/go-prompt" ) var CommandSuggestion = prompt.Suggest{ Text: Name, Description: Usage, } ================================================ FILE: pkg/app/master/command/convert/register.go ================================================ package convert import ( "github.com/slimtoolkit/slim/pkg/app/master/command" ) func RegisterCommand() { command.AddCLICommand( Name, CLI, CommandSuggestion, nil) } ================================================ FILE: pkg/app/master/command/debug/cli.go ================================================ package debug import ( "strings" "github.com/urfave/cli/v2" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" ) //Debug container const ( Name = "debug" Usage = "Debug the target container from a debug (side-car) container" Alias = "dbg" ) const ( DockerRuntime = "docker" KubernetesRuntime = "k8s" KubeconfigDefault = "${HOME}/.kube/config" NamespaceDefault = "default" ) type NVPair struct { Name string Value string } type CommandParams struct { /// the runtime environment type Runtime string /// the running container which we want to attach to TargetRef string /// the target namespace (k8s runtime) TargetNamespace string /// the target pod (k8s runtime) TargetPod string /// the name/id of the container image used for debugging DebugContainerImage string /// ENTRYPOINT used launching the debugging container Entrypoint []string /// CMD used launching the debugging container Cmd []string /// WORKDIR used launching the debugging container Workdir string /// Environment variables used launching the debugging container EnvVars []NVPair /// launch the debug container with an interactive terminal attached (like '--it' in docker) DoTerminal bool /// make it look like shell is running in the target container DoRunAsTargetShell bool /// Kubeconfig file path (k8s runtime) Kubeconfig string /// Debug session container name Session string /// Simple (non-debug) action - list namespaces ActionListNamespaces bool /// Simple (non-debug) action - list pods ActionListPods bool /// Simple (non-debug) action - list debuggable container ActionListDebuggableContainers bool /// Simple (non-debug) action - list debug sessions ActionListSessions bool /// Simple (non-debug) action - show debug sessions logs ActionShowSessionLogs bool /// Simple (non-debug) action - connect to an existing debug session ActionConnectSession bool } func ParseNameValueList(list []string) []NVPair { var pairs []NVPair for _, val := range list { val = strings.TrimSpace(val) if val == "" { continue } parts := strings.SplitN(val, "=", 2) if len(parts) != 2 { continue } nv := NVPair{Name: parts[0], Value: parts[1]} pairs = append(pairs, nv) } return pairs } var CLI = &cli.Command{ Name: Name, Aliases: []string{Alias}, Usage: Usage, Flags: []cli.Flag{ cflag(FlagRuntime), cflag(FlagTarget), cflag(FlagNamespace), cflag(FlagPod), cflag(FlagDebugImage), cflag(FlagEntrypoint), cflag(FlagCmd), cflag(FlagWorkdir), cflag(FlagEnv), cflag(FlagTerminal), cflag(FlagRunAsTargetShell), cflag(FlagListSessions), cflag(FlagShowSessionLogs), cflag(FlagConnectSession), cflag(FlagSession), cflag(FlagListNamespaces), cflag(FlagListPods), cflag(FlagListDebuggableContainers), cflag(FlagListDebugImage), cflag(FlagKubeconfig), }, Action: func(ctx *cli.Context) error { gcvalues := command.GlobalFlagValues(ctx) xc := app.NewExecutionContext( Name, gcvalues.QuietCLIMode, gcvalues.OutputFormat) if ctx.Bool(FlagListDebugImage) { xc.Out.State("action.list_debug_images") for k, v := range debugImages { xc.Out.Info("debug.image", ovars{"name": k, "description": v}) } return nil } commandParams := &CommandParams{ Runtime: ctx.String(FlagRuntime), TargetRef: ctx.String(FlagTarget), TargetNamespace: ctx.String(FlagNamespace), TargetPod: ctx.String(FlagPod), DebugContainerImage: ctx.String(FlagDebugImage), DoTerminal: ctx.Bool(FlagTerminal), DoRunAsTargetShell: ctx.Bool(FlagRunAsTargetShell), Kubeconfig: ctx.String(FlagKubeconfig), Workdir: ctx.String(FlagWorkdir), EnvVars: ParseNameValueList(ctx.StringSlice(FlagEnv)), Session: ctx.String(FlagSession), ActionListNamespaces: ctx.Bool(FlagListNamespaces), ActionListPods: ctx.Bool(FlagListPods), ActionListDebuggableContainers: ctx.Bool(FlagListDebuggableContainers), ActionListSessions: ctx.Bool(FlagListSessions), ActionShowSessionLogs: ctx.Bool(FlagShowSessionLogs), ActionConnectSession: ctx.Bool(FlagConnectSession), } if commandParams.Runtime != KubernetesRuntime && (commandParams.ActionListNamespaces || commandParams.ActionListPods) { var actionName string if commandParams.ActionListNamespaces { actionName = FlagListNamespaces } if commandParams.ActionListPods { actionName = FlagListPods } xc.Out.Error("param", "unsupported runtime flag") xc.Out.State("exited", ovars{ "runtime.provided": commandParams.Runtime, "runtime.required": KubernetesRuntime, "action": actionName, "exit.code": -1, }) xc.Exit(-1) } var err error if rawEntrypoint := ctx.String(FlagEntrypoint); rawEntrypoint != "" { commandParams.Entrypoint, err = command.ParseExec(rawEntrypoint) if err != nil { return err } } if rawCmd := ctx.String(FlagCmd); rawCmd != "" { commandParams.Cmd, err = command.ParseExec(rawCmd) if err != nil { return err } } //explicitly setting the entrypoint and/or cmd clears //implies a custom debug session where the 'RATS' setting should be ignored if len(commandParams.Entrypoint) > 0 || len(commandParams.Cmd) > 0 { commandParams.DoRunAsTargetShell = false } if commandParams.DoRunAsTargetShell { commandParams.DoTerminal = true } if !commandParams.ActionListNamespaces && !commandParams.ActionListPods && !commandParams.ActionListDebuggableContainers && !commandParams.ActionListSessions && !commandParams.ActionShowSessionLogs && !commandParams.ActionConnectSession && commandParams.TargetRef == "" { if ctx.Args().Len() < 1 { if commandParams.Runtime != KubernetesRuntime { xc.Out.Error("param.target", "missing target") cli.ShowCommandHelp(ctx, Name) return nil } //NOTE: //It's ok to not specify the target container for k8s //We'll pick the default or first container in the target pod } else { commandParams.TargetRef = ctx.Args().First() if ctx.Args().Len() > 1 && ctx.Args().Slice()[1] == "--" { //NOTE: //Keep the original 'no terminal' behavior //use this shortcut mode as a way to quickly //run one off commands in the debugged container //When there's 'no terminal' we show //the debugger container log at the end. //TODO: revisit the behavior later... cmdSlice := ctx.Args().Slice()[2:] var cmdClean []string for _, v := range cmdSlice { v = strings.TrimSpace(v) if v != "" { cmdClean = append(cmdClean, v) } } if len(cmdClean) > 0 { commandParams.Cmd = cmdClean commandParams.DoTerminal = false commandParams.DoRunAsTargetShell = false } } } } if commandParams.DebugContainerImage == "" { commandParams.DebugContainerImage = BusyboxImage } OnCommand( xc, gcvalues, commandParams) return nil }, } ================================================ FILE: pkg/app/master/command/debug/debug_images.go ================================================ package debug import ( // "strings" ) const ( CgrSlimToolkitDebugImage = "cgr.dev/chainguard/slim-toolkit-debug:latest" WolfiBaseImage = "cgr.dev/chainguard/wolfi-base:latest" BusyboxImage = "busybox:latest" NicolakaNetshootImage = "nicolaka/netshoot" KoolkitsNodeImage = "lightruncom/koolkits:node" KoolkitsPythonImage = "lightruncom/koolkits:python" KoolkitsGolangImage = "lightruncom/koolkits:golang" KoolkitsJVMImage = "lightruncom/koolkits:jvm" DigitaloceanDoksImage = "digitalocean/doks-debug:latest" ZinclabsUbuntuImage = "public.ecr.aws/zinclabs/debug-ubuntu-base:latest" InfuserImage = "ghcr.io/teaxyz/infuser:latest" ) var debugImages = map[string]string{ CgrSlimToolkitDebugImage: "Chainguard SlimToolkit debug image - https://edu.chainguard.dev/chainguard/chainguard-images/reference/slim-toolkit-debug", WolfiBaseImage: "A lightweight Wolfi base image - https://github.com/chainguard-images/images/tree/main/images/wolfi-base", BusyboxImage: "A lightweight image with common unix utilities - https://busybox.net/about.html", NicolakaNetshootImage: "Network trouble-shooting swiss-army container - https://github.com/nicolaka/netshoot", KoolkitsNodeImage: "Node.js KoolKit - https://github.com/lightrun-platform/koolkits/tree/main/nodejs", KoolkitsPythonImage: "Python KoolKit - https://github.com/lightrun-platform/koolkits/tree/main/python", KoolkitsGolangImage: "Go KoolKit - https://github.com/lightrun-platform/koolkits/tree/main/golang", KoolkitsJVMImage: "JVM KoolKit - https://github.com/lightrun-platform/koolkits/blob/main/jvm/README.md", DigitaloceanDoksImage: "Kubernetes manifests for investigation and troubleshooting - https://github.com/digitalocean/doks-debug", ZinclabsUbuntuImage: "Common utilities for debugging your cluster - https://github.com/openobserve/debug-container", InfuserImage: "Tea package manager image - https://github.com/teaxyz/infuser", } func ShellCommandPrefix(imageName string) []string { shellName := defaultShellName //TODO: //Need to have a reliable solution to deal with //the dynamic library dependencies for bash //before we default to it in interactive debug shells //Need to work out the compat issues linking the shared //object dir(s) from the debugging container //if strings.Contains(imageName, "lightruncom/koolkits") || // strings.Contains(imageName, "ubuntu") || // strings.Contains(imageName, "debian") { // shellName = bashShellName // //debian/ubuntu-based images link 'sh' to 'dash', which doesn't support 'set -o pipefail' //} return []string{shellName, "-c"} } ================================================ FILE: pkg/app/master/command/debug/flags.go ================================================ package debug import ( log "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" ) // Debug command flag names and usage descriptions const ( FlagRuntime = "runtime" FlagRuntimeUsage = "Runtime environment type" FlagTarget = "target" FlagTargetUsage = "Target container (name or ID)" FlagNamespace = "namespace" FlagNamespaceUsage = "Namespace to target (k8s runtime)" FlagPod = "pod" FlagPodUsage = "Pod to target (k8s runtime)" FlagDebugImage = "debug-image" FlagDebugImageUsage = "Debug image to use for the debug side-car container" FlagEntrypoint = "entrypoint" FlagEntrypointUsage = "Custom ENTRYPOINT to use for the debug side-car container." FlagCmd = "cmd" FlagCmdUsage = "Custom CMD to use for the debug side-car container (alternatively pass custom CMD params after '--')." FlagWorkdir = "workdir" FlagWorkdirUsage = "Custom WORKDIR to use for the debug side-car container." //value expected to be "name=value" FlagEnv = "env" FlagEnvUsage = "Environment variable to add to the debug side-car container." //TBD FlagMount = "mount" FlagMountUsage = "Volume mount to create in the debug side-car container." //TBD FlagLoadAllTargetEnvVars = "load-all-target-env-vars" FlagLoadAllTargetEnvVarsUsage = "Load all (known) environment variables from the target container" FlagTerminal = "terminal" FlagTerminalUsage = "Attach interactive terminal to the debug container" FlagRunAsTargetShell = "run-as-target-shell" FlagRunAsTargetShellUsage = "Attach interactive terminal to the debug container and run shell as if it's running in the target container environment." FlagListSessions = "list-sessions" FlagListSessionsUsage = "List all debug sessions for the selected target (pod and optionally selected container for k8s or container for other runtimes)." FlagShowSessionLogs = "show-session-logs" FlagShowSessionLogsUsage = "Show logs for the selected debug session (using namespace, pod, target container or debug session container name for k8s or debug session container name for other runtimes)." FlagSession = "session" FlagSessionUsage = "Debug session container name (used for debug sessoin actions)." FlagConnectSession = "connect-session" FlagConnectSessionUsage = "Connect to existing debug session." //TBD FlagConnectLastSession = "connect-last-session" FlagConnectLastSessionUsage = "Connect to last debug session" FlagListNamespaces = "list-namespaces" FlagListNamespacesUsage = "List names for available namespaces (use this flag by itself)." FlagListPods = "list-pods" FlagListPodsUsage = "List names for running pods in the selected namespace (use this flag by itself)." FlagListDebuggableContainers = "list-debuggable-containers" FlagListDebuggableContainersUsage = "List container names for active containers that can be debugged (use this flag by itself)." FlagListDebugImage = "list-debug-images" FlagListDebugImageUsage = "List possible debug images to use for the debug side-car container (use this flag by itself)." FlagKubeconfig = "kubeconfig" FlagKubeconfigUsage = "Kubeconfig file location (k8s runtime)" ) var Flags = map[string]cli.Flag{ FlagRuntime: &cli.StringFlag{ Name: FlagRuntime, Value: DockerRuntime, Usage: FlagRuntimeUsage, EnvVars: []string{"DSLIM_DBG_RT"}, }, FlagTarget: &cli.StringFlag{ Name: FlagTarget, Value: "", Usage: FlagTargetUsage, EnvVars: []string{"DSLIM_DBG_TARGET"}, }, FlagNamespace: &cli.StringFlag{ Name: FlagNamespace, Value: NamespaceDefault, Usage: FlagNamespaceUsage, EnvVars: []string{"DSLIM_DBG_TARGET_NS"}, }, FlagPod: &cli.StringFlag{ Name: FlagPod, Value: "", Usage: FlagPodUsage, EnvVars: []string{"DSLIM_DBG_TARGET_POD"}, }, FlagDebugImage: &cli.StringFlag{ Name: FlagDebugImage, Value: BusyboxImage, Usage: FlagDebugImageUsage, EnvVars: []string{"DSLIM_DBG_IMAGE"}, }, FlagEntrypoint: &cli.StringFlag{ Name: FlagEntrypoint, Value: "", Usage: FlagEntrypointUsage, EnvVars: []string{"DSLIM_DBG_ENTRYPOINT"}, }, FlagCmd: &cli.StringFlag{ Name: FlagCmd, Value: "", Usage: FlagCmdUsage, EnvVars: []string{"DSLIM_DBG_CMD"}, }, FlagWorkdir: &cli.StringFlag{ Name: FlagWorkdir, Value: "", Usage: FlagWorkdirUsage, EnvVars: []string{"DSLIM_DBG_WDIR"}, }, FlagEnv: &cli.StringSliceFlag{ Name: FlagEnv, Value: cli.NewStringSlice(), Usage: FlagEnvUsage, EnvVars: []string{"DSLIM_DBG_ENV"}, }, FlagTerminal: &cli.BoolFlag{ Name: FlagTerminal, Value: true, //attach interactive terminal by default Usage: FlagTerminalUsage, EnvVars: []string{"DSLIM_DBG_TERMINAL"}, }, FlagRunAsTargetShell: &cli.BoolFlag{ Name: FlagRunAsTargetShell, Value: true, //do it by default (FlagTerminal will be ignored, assumed to be true) Usage: FlagRunAsTargetShellUsage, EnvVars: []string{"DSLIM_DBG_RATS"}, }, FlagListSessions: &cli.BoolFlag{ Name: FlagListSessions, Value: false, Usage: FlagListSessionsUsage, EnvVars: []string{"DSLIM_DBG_LIST_SESSIONS"}, }, FlagShowSessionLogs: &cli.BoolFlag{ Name: FlagShowSessionLogs, Value: false, Usage: FlagShowSessionLogsUsage, EnvVars: []string{"DSLIM_DBG_SHOW_SESSION_LOGS"}, }, FlagConnectSession: &cli.BoolFlag{ Name: FlagConnectSession, Value: false, Usage: FlagConnectSessionUsage, EnvVars: []string{"DSLIM_DBG_CONNECT_SESSION"}, }, FlagSession: &cli.StringFlag{ Name: FlagSession, Value: "", Usage: FlagSessionUsage, EnvVars: []string{"DSLIM_DBG_SESSION"}, }, FlagListNamespaces: &cli.BoolFlag{ Name: FlagListNamespaces, Value: false, Usage: FlagListNamespacesUsage, EnvVars: []string{"DSLIM_DBG_LIST_NAMESPACES"}, }, FlagListPods: &cli.BoolFlag{ Name: FlagListPods, Value: false, Usage: FlagListPodsUsage, EnvVars: []string{"DSLIM_DBG_LIST_PODS"}, }, FlagListDebuggableContainers: &cli.BoolFlag{ Name: FlagListDebuggableContainers, Value: false, Usage: FlagListDebuggableContainersUsage, EnvVars: []string{"DSLIM_DBG_LIST_CONTAINERS"}, }, FlagListDebugImage: &cli.BoolFlag{ Name: FlagListDebugImage, Value: false, Usage: FlagListDebugImageUsage, EnvVars: []string{"DSLIM_DBG_LIST_IMAGES"}, }, FlagKubeconfig: &cli.StringFlag{ Name: FlagKubeconfig, Value: KubeconfigDefault, Usage: FlagKubeconfigUsage, EnvVars: []string{"DSLIM_DBG_KUBECONFIG"}, }, } func cflag(name string) cli.Flag { cf, ok := Flags[name] if !ok { log.Fatalf("unknown flag='%s'", name) } return cf } ================================================ FILE: pkg/app/master/command/debug/handle_docker_runtime.go ================================================ package debug import ( "fmt" "io" "os" "strings" "time" dockerapi "github.com/fsouza/go-dockerclient" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/app/master/container" "github.com/slimtoolkit/slim/pkg/app/master/inspectors/image" "github.com/slimtoolkit/slim/pkg/docker/dockerutil" "github.com/slimtoolkit/slim/pkg/util/errutil" ) // HandleDockerRuntime implements support for the docker runtime func HandleDockerRuntime( logger *log.Entry, xc *app.ExecutionContext, gparams *command.GenericParams, commandParams *CommandParams, client *dockerapi.Client, sid string, debugContainerName string) { if commandParams.ActionListDebuggableContainers { xc.Out.State("action.list_debuggable_containers") result, err := listDockerDebuggableContainers(client) if err != nil { logger.WithError(err).Error("listDockerDebuggableContainers") xc.FailOn(err) } xc.Out.Info("debuggable.containers", ovars{"count": len(result)}) for cname, iname := range result { xc.Out.Info("debuggable.container", ovars{"name": cname, "image": iname}) } return } //todo: need to check that if targetRef is not empty it is valid if commandParams.ActionListSessions { xc.Out.State("action.list_sessions", ovars{"target": commandParams.TargetRef}) //later will track/show additional debug session info result, err := listDockerDebugContainers(client, commandParams.TargetRef, false) if err != nil { logger.WithError(err).Error("listDockerDebugContainers") xc.FailOn(err) } var waitingCount int var runningCount int var terminatedCount int for _, info := range result { switch info.State { case CSWaiting: waitingCount++ case CSRunning: runningCount++ case CSTerminated: terminatedCount++ } } xc.Out.Info("debug.session.count", ovars{ "total": len(result), "running": runningCount, "waiting": waitingCount, "terminated": terminatedCount, }) for name, info := range result { outParams := ovars{ "target": info.TargetContainerName, "name": name, "image": info.SpecImage, "state": info.State, "start.time": info.StartTime, } /* if info.State == CSTerminated { outParams["exit.code"] = info.ExitCode outParams["finish.time"] = info.FinishTime if info.ExitReason != "" { outParams["exit.reason"] = info.ExitReason } if info.ExitMessage != "" { outParams["exit.message"] = info.ExitMessage } } */ xc.Out.Info("debug.session", outParams) } return } if commandParams.ActionShowSessionLogs { xc.Out.State("action.show_session_logs", ovars{ "target": commandParams.TargetRef, "session": commandParams.Session}) result, err := listDockerDebugContainers(client, commandParams.TargetRef, false) if err != nil { logger.WithError(err).Error("listDockerDebugContainers") xc.FailOn(err) } if len(result) < 1 { xc.Out.Info("no.debug.session") return } //todo: need to pick the last session if commandParams.Session is empty var containerID string for _, info := range result { if commandParams.Session == "" { commandParams.Session = info.Name } if commandParams.Session == info.Name { containerID = info.ContainerID } break } xc.Out.Info("container.logs.target", ovars{ "container.name": commandParams.Session, "container.id": containerID}) if err := dumpDockerContainerLogs(logger, xc, client, containerID); err != nil { logger.WithError(err).Error("dumpDockerContainerLogs") } return } if commandParams.ActionConnectSession { xc.Out.State("action.connect_session", ovars{ "target": commandParams.TargetRef, "session": commandParams.Session}) result, err := listDockerDebugContainers(client, commandParams.TargetRef, true) if err != nil { logger.WithError(err).Error("listDockerDebugContainers") xc.FailOn(err) } if len(result) < 1 { xc.Out.Info("no.debug.session") return } //todo: need to pick the last session if commandParams.Session is empty var containerID string for _, info := range result { if commandParams.Session == "" { commandParams.Session = info.Name } if commandParams.Session == info.Name { containerID = info.ContainerID } break } //todo: need to validate that the session container exists and it's running r, w := io.Pipe() go io.Copy(w, os.Stdin) options := dockerapi.AttachToContainerOptions{ Container: containerID, InputStream: r, OutputStream: os.Stdout, ErrorStream: os.Stderr, Stdin: true, Stdout: true, Stderr: true, Stream: true, RawTerminal: true, Logs: true, } err = client.AttachToContainer(options) xc.FailOn(err) return } imageInspector, err := image.NewInspector(client, commandParams.DebugContainerImage) errutil.FailOn(err) noImage, err := imageInspector.NoImage() errutil.FailOn(err) if noImage { err := imageInspector.Pull(true, "", "", "") xc.FailOn(err) } targetContainerInfo, err := client.InspectContainer(commandParams.TargetRef) if err != nil { xc.Out.Error("target.container.inspect", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } if commandParams.DoRunAsTargetShell { logger.Trace("doRunAsTargetShell") commandParams.Entrypoint = ShellCommandPrefix(commandParams.DebugContainerImage) shellConfig := configShell(sid, false) if CgrSlimToolkitDebugImage == commandParams.DebugContainerImage { shellConfig = configShellAlt(sid, false) } commandParams.Cmd = []string{shellConfig} } else { if len(commandParams.Cmd) == 0 && CgrSlimToolkitDebugImage == commandParams.DebugContainerImage { commandParams.Cmd = []string{bashShellName} } } options := container.ExecutionOptions{ ContainerName: debugContainerName, Entrypoint: commandParams.Entrypoint, Cmd: commandParams.Cmd, Terminal: commandParams.DoTerminal, } exe, err := container.NewExecution( xc, logger, client, commandParams.DebugContainerImage, &options, nil, true, true) // attach network, IPC & PIDs, essentially this is run --network container:golang_service --pid container:golang_service --ipc container:golang_service mode := fmt.Sprintf("container:%s", commandParams.TargetRef) if targetContainerInfo.HostConfig.IpcMode == "shareable" { exe.IpcMode = mode } exe.NetworkMode = mode exe.PidMode = mode xc.FailOn(err) err = exe.Start() xc.FailOn(err) _, err = exe.Wait() xc.FailOn(err) defer func() { err = exe.Cleanup() errutil.WarnOn(err) }() if !commandParams.DoTerminal { exe.ShowContainerLogs() } } func listDockerDebuggableContainers(client *dockerapi.Client) (map[string]string, error) { const op = "debug.listDockerDebuggableContainers" containers, err := dockerutil.ListContainers(client, "", false) if err != nil { log.WithFields(log.Fields{ "op": op, "containers.count": len(containers), }).Error("dockerutil.ListContainers") return nil, err } activeContainers := map[string]string{} for name, info := range containers { if info.State != dockerutil.CSRunning { log.WithFields(log.Fields{ "op": "listDockerDebuggableContainers", "container": name, "state": info.State, }).Trace("ignoring.nonrunning.container") continue } if strings.HasPrefix(name, containerNamePrefix) { log.WithFields(log.Fields{ "op": "listDockerDebuggableContainers", "container": name, }).Trace("ignoring.debug.container") continue } activeContainers[name] = info.Image } return activeContainers, nil } func listDebuggableDockerContainersWithConfig(client *dockerapi.Client) (map[string]string, error) { //todo: pass the docker client config params instead of the existing client return listDockerDebuggableContainers(client) } func listDockerDebugContainers( client *dockerapi.Client, targetContainer string, onlyActive bool) (map[string]*DebugContainerInfo, error) { containers, err := dockerutil.ListContainers(client, "", true) if err != nil { return nil, err } result := map[string]*DebugContainerInfo{} for name, container := range containers { if !strings.HasPrefix(name, containerNamePrefix) { log.WithFields(log.Fields{ "op": "listDockerDebugContainers", "container": name, }).Trace("ignoring.nondebug.container") continue } //todo: filter by targetContainer (when info.TargetContainerName is populated) t := time.Unix(container.Created, 0) //UnixMilli, UnixMicro info := &DebugContainerInfo{ //TargetContainerName: info.TargetContainerName, Name: container.Name, SpecImage: container.Image, ContainerID: container.ID, //Command: info.Command, //Args: info.Args, //WorkingDir: info.WorkingDir, //TTY: info.TTY, StartTime: fmt.Sprintf("%v", t), } switch container.State { case dockerutil.CSCreated, dockerutil.CSRestarting, dockerutil.CSPaused: info.State = CSWaiting case dockerutil.CSRunning: info.State = CSRunning case dockerutil.CSRemoving, dockerutil.CSExited, dockerutil.CSDead: info.State = CSTerminated } if onlyActive { if info.State == CSRunning { result[info.Name] = info } } else { result[info.Name] = info } } return result, nil } func listDockerDebugContainersWithConfig( client *dockerapi.Client, targetContainer string, onlyActive bool) (map[string]*DebugContainerInfo, error) { //todo: pass the docker client config params instead of the existing client return listDockerDebugContainers(client, targetContainer, onlyActive) } func dumpDockerContainerLogs( logger *log.Entry, xc *app.ExecutionContext, client *dockerapi.Client, containerID string) error { logger.Tracef("dumpDockerContainerLogs(%s)", containerID) outData, errData, err := dockerutil.GetContainerLogs(client, containerID, true) if err != nil { logger.WithError(err).Error("error reading container logs") return err } xc.Out.Info("container.logs.start") xc.Out.LogDump("debug.container.logs.stdout", string(outData)) xc.Out.LogDump("debug.container.logs.stderr", string(errData)) xc.Out.Info("container.logs.end") return nil } ================================================ FILE: pkg/app/master/command/debug/handle_kubernetes_runtime.go ================================================ package debug import ( "context" "encoding/json" "errors" "fmt" "io/ioutil" "net/http" "os" "strings" "time" log "github.com/sirupsen/logrus" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" restclient "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/remotecommand" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" ) // HandleKubernetesRuntime implements support for the k8s runtime func HandleKubernetesRuntime( logger *log.Entry, xc *app.ExecutionContext, gparams *command.GenericParams, commandParams *CommandParams, sid string, debugContainerName string) { logger = logger.WithFields( log.Fields{ "op": "debug.HandleKubernetesRuntime", }) cpJson, _ := json.Marshal(commandParams) logger.WithField("cparams", string(cpJson)).Trace("call") defer logger.Trace("exit") ctx := context.Background() api, restConfig, err := apiClientFromConfig(commandParams.Kubeconfig) if err != nil { logger.WithError(err).Error("apiClientFromConfig") xc.FailOn(err) } if commandParams.ActionListNamespaces { xc.Out.State("action.list_namespaces") names, err := listNamespaces(ctx, api) if err != nil { logger.WithError(err).Error("listNamespaces") xc.FailOn(err) } for _, name := range names { xc.Out.Info("namespace", ovars{"name": name}) } return } nsName, err := ensureNamespace(ctx, api, commandParams.TargetNamespace) if err != nil { logger.WithError(err).Error("ensureNamespace") xc.FailOn(err) } if commandParams.ActionListPods { xc.Out.State("action.list_pods", ovars{"namespace": nsName}) names, err := listActivePods(ctx, api, nsName) if err != nil { logger.WithError(err).Error("listActivePods") xc.FailOn(err) } for _, name := range names { xc.Out.Info("pod", ovars{"name": name}) } return } pod, podName, err := ensurePod(ctx, api, nsName, commandParams.TargetPod) if apierrors.IsNotFound(err) { logger.WithError(err). WithFields(log.Fields{ "ns": nsName, "pod": podName, }).Error("ensurePod - not found") xc.FailOn(err) } else if statusError, isStatus := err.(*apierrors.StatusError); isStatus { logger.WithError(err). WithFields(log.Fields{ "ns": nsName, "pod": podName, "status": statusError.ErrStatus.Message, }).Error("ensurePod - status error") xc.FailOn(err) } else if err != nil { logger.WithError(err). WithFields(log.Fields{ "ns": nsName, "pod": podName, "status": statusError.ErrStatus.Message, }).Error("ensurePod - other error") xc.FailOn(err) } logger.WithField("phase", pod.Status.Phase).Debug("target pod status") if pod.Status.Phase != corev1.PodRunning { logger.Error("target pod is not running") xc.FailOn(fmt.Errorf("target pod is not running")) } logger.WithFields( log.Fields{ "ns": nsName, "pod": podName, "ec.count": len(pod.Spec.EphemeralContainers), }).Debug("target pod info") if commandParams.ActionListDebuggableContainers { xc.Out.State("action.list_debuggable_containers", ovars{"namespace": nsName, "pod": podName}) result, err := listK8sDebuggableContainers(ctx, api, nsName, podName) if err != nil { logger.WithError(err).Error("listK8sDebuggableContainers") xc.FailOn(err) } for cname, iname := range result { xc.Out.Info("debuggable.container", ovars{"name": cname, "image": iname}) } return } //todo: need to check that if targetRef is not empty it is valid if commandParams.ActionListSessions { //list sessions before we pick a target container, //so we can list all debug session for the selected pod xc.Out.State("action.list_sessions", ovars{ "namespace": nsName, "pod": podName, "target": commandParams.TargetRef}) //later will track/show additional debug session info result, err := listK8sDebugContainers(ctx, api, nsName, podName, commandParams.TargetRef, false) if err != nil { logger.WithError(err).Error("listK8sDebugContainers") xc.FailOn(err) } var waitingCount int var runningCount int var terminatedCount int for _, info := range result { switch info.State { case CSWaiting: waitingCount++ case CSRunning: runningCount++ case CSTerminated: terminatedCount++ } } xc.Out.Info("debug.session.count", ovars{ "total": len(result), "running": runningCount, "waiting": waitingCount, "terminated": terminatedCount, }) for name, info := range result { outParams := ovars{ "target": info.TargetContainerName, "name": name, "image": info.SpecImage, "state": info.State, "start.time": info.StartTime, } if info.State == CSTerminated { outParams["exit.code"] = info.ExitCode outParams["finish.time"] = info.FinishTime if info.ExitReason != "" { outParams["exit.reason"] = info.ExitReason } if info.ExitMessage != "" { outParams["exit.message"] = info.ExitMessage } } xc.Out.Info("debug.session", outParams) } return } if commandParams.TargetRef == "" { logger.Debug("no explicit target container... pick one") //TODO: improve this logic (to also check for the default container) if len(pod.Spec.Containers) > 0 { commandParams.TargetRef = pod.Spec.Containers[0].Name } else { xc.FailOn(fmt.Errorf("no containers")) } } if commandParams.ActionShowSessionLogs { //list sessions before we pick a target container, //so we can list all debug session for the selected pod xc.Out.State("action.show_session_logs", ovars{ "namespace": nsName, "pod": podName, "target": commandParams.TargetRef, "session": commandParams.Session}) if commandParams.Session == "" { result, err := listK8sDebugContainers(ctx, api, nsName, podName, commandParams.TargetRef, false) if err != nil { logger.WithError(err).Error("listK8sDebugContainers") xc.FailOn(err) } if len(result) < 1 { xc.Out.Info("no.debug.session") return } //todo: need to pick the last session for _, info := range result { commandParams.Session = info.Name break } } if err := dumpK8sContainerLogs(logger, xc, ctx, api, nsName, podName, commandParams.Session); err != nil { logger.WithError(err).Error("dumpK8sContainerLogs") } return } if commandParams.ActionConnectSession { xc.Out.State("action.connect_session", ovars{ "namespace": nsName, "pod": podName, "target": commandParams.TargetRef, "session": commandParams.Session}) if commandParams.Session == "" { result, err := listK8sDebugContainers(ctx, api, nsName, podName, commandParams.TargetRef, true) if err != nil { logger.WithError(err).Error("listK8sDebugContainers") xc.FailOn(err) } if len(result) < 1 { xc.Out.Info("no.debug.session") return } //todo: need to pick the last session for _, info := range result { commandParams.Session = info.Name break } } //todo: need to validate that the debug session container exists and it's running //note: tty should be controlled by the 'terminal' flag //and connecting would not be interactive if it's not true doTTY := true req := api.CoreV1().RESTClient().Post(). Resource("pods"). Name(podName). Namespace(nsName). SubResource("attach"). VersionedParams(&corev1.PodAttachOptions{ Container: commandParams.Session, Stdin: true, Stdout: true, Stderr: true, TTY: doTTY, }, scheme.ParameterCodec) logger.Tracef("(connect to session) pod attach request URL: %s", req.URL()) attach, err := remotecommand.NewSPDYExecutor(restConfig, http.MethodPost, req.URL()) if err != nil { logger.WithError(err).Error("remotecommand.NewSPDYExecutor") xc.FailOn(err) } xc.Out.Info("terminal.start", ovars{ "mode": "connecting to existing debug session", "note": "press enter if you dont see any output", }) logger.Trace("starting stream...") //TODO: //use commandParams.DoTerminal to conditionally enable the interactive terminal //if false configure stream to do a one off command execution //and dump the container logs //similar to how it's done with the docker runtime fmt.Printf("\n") //note: blocks until done streaming or failure... err = attach.StreamWithContext( ctx, remotecommand.StreamOptions{ Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr, Tty: doTTY, }) if err != nil { if apierrors.IsNotFound(err) { logger.WithError(err). Error("attach.StreamWithContext - not found") } else if statusError, isStatus := err.(*apierrors.StatusError); isStatus { logger.WithError(err). WithFields(log.Fields{ "status": statusError.ErrStatus.Message, }).Error("attach.StreamWithContext - status error") } else { logger.WithError(err). Error("attach.StreamWithContext - other error") } xc.FailOn(err) } return } logger.WithField("target", commandParams.TargetRef).Debug("locating container") targetContainerIndex := -1 targetContainerIsRunning := false var targetContainer *corev1.Container for i, c := range pod.Spec.Containers { if c.Name == commandParams.TargetRef { targetContainerIndex = i targetContainer = &c logger.WithFields( log.Fields{ "index": targetContainerIndex, "ns": nsName, "pod": podName, "target": commandParams.TargetRef, }).Trace("found container") break } } if targetContainer != nil { //doTTY = targetContainer.TTY logger.WithField("data", fmt.Sprintf("%#v", targetContainer)).Trace("target container info") } containerFound := false for _, containerStatus := range pod.Status.ContainerStatuses { if containerStatus.Name == commandParams.TargetRef { containerFound = true if containerStatus.State.Running != nil { targetContainerIsRunning = true logger.Trace("target container is running") } break } } if !containerFound { logger.Errorf("Container %s not found in pod %s", commandParams.TargetRef, podName) xc.FailOn(fmt.Errorf("target container not found")) } if !targetContainerIsRunning { xc.Out.Info("wait.for.target.container", ovars{ "name": commandParams.TargetRef, "pod": podName, "namespace": nsName, }) err = waitForContainer(logger, xc, ctx, api, nsName, podName, commandParams.TargetRef, ctStandard) if err != nil { logger.WithError(err).Error("waitForContainer") xc.FailOn(err) } } //'tty' config needs to be the same when creating & attaching doTTY := true isEcPrivileged := true if commandParams.DoRunAsTargetShell { logger.Trace("doRunAsTargetShell") commandParams.Entrypoint = ShellCommandPrefix(commandParams.DebugContainerImage) shellConfig := configShell(sid, true) if CgrSlimToolkitDebugImage == commandParams.DebugContainerImage { shellConfig = configShellAlt(sid, true) } commandParams.Cmd = []string{shellConfig} } else { if len(commandParams.Cmd) == 0 && CgrSlimToolkitDebugImage == commandParams.DebugContainerImage { commandParams.Cmd = []string{bashShellName} } } logger.WithFields( log.Fields{ "work.dir": commandParams.Workdir, "params": fmt.Sprintf("%#v", commandParams), }).Trace("newEphemeralContainerInfo") //TODO: pass commandParams.DoTerminal ecInfo := newEphemeralContainerInfo( commandParams.TargetRef, debugContainerName, commandParams.DebugContainerImage, commandParams.Entrypoint, commandParams.Cmd, commandParams.Workdir, commandParams.EnvVars, isEcPrivileged, doTTY) pod.Spec.EphemeralContainers = append(pod.Spec.EphemeralContainers, ecInfo) _, err = api.CoreV1(). Pods(pod.Namespace). UpdateEphemeralContainers(ctx, pod.Name, pod, metav1.UpdateOptions{}) if err != nil { logger.WithError(err).Error("error adding the ephemeral container to target pod") xc.FailOn(err) } updatedPod, err := api.CoreV1(). Pods(pod.Namespace). Get(ctx, pod.Name, metav1.GetOptions{}) if err != nil { logger.WithError(err).Error("error getting the ephemeral container from target pod") xc.FailOn(err) } logger.WithFields( log.Fields{ "ns": nsName, "pod": podName, "target": commandParams.TargetRef, "image": commandParams.DebugContainerImage, }).Debug("attached ephemeral container") ec := ephemeralContainerFromPod(updatedPod, commandParams.TargetRef, debugContainerName) if ec == nil { logger.Errorf("ephemeral container not found in pod") xc.FailOn(fmt.Errorf("ephemeral container not found")) } ecData, _ := json.Marshal(ec) logger.WithField("data", string(ecData)).Trace("ephemeral container") var ecContainerIsRunning bool for _, ecStatus := range updatedPod.Status.EphemeralContainerStatuses { if ecStatus.Name == debugContainerName { if ecStatus.State.Running != nil { ecContainerIsRunning = true } break } } if !ecContainerIsRunning { xc.Out.Info("wait.for.debug.container", ovars{ "name": debugContainerName, "pod": podName, "namespace": nsName, }) err = waitForContainer(logger, xc, ctx, api, nsName, podName, debugContainerName, ctEphemeral) if err != nil { logger.WithError(err).Error("waitForContainer") if err == ErrContainerTerminated { xc.Out.Error("debug.container.error", "terminated") if err := dumpK8sContainerLogs(logger, xc, ctx, api, nsName, podName, debugContainerName); err != nil { logger.WithError(err).Error("dumpK8sContainerLogs") } xc.Out.State("debug.container.error", ovars{ "exit.code": -1, }) xc.Exit(-1) } else { xc.FailOn(err) } } } xc.Out.State("debug.container.running") req := api.CoreV1().RESTClient().Post(). Resource("pods"). Name(podName). Namespace(nsName). SubResource("attach"). VersionedParams(&corev1.PodAttachOptions{ Container: debugContainerName, Stdin: true, Stdout: true, Stderr: true, TTY: doTTY, }, scheme.ParameterCodec) logger.Tracef("pod attach request URL: %s", req.URL()) attach, err := remotecommand.NewSPDYExecutor(restConfig, http.MethodPost, req.URL()) if err != nil { logger.WithError(err).Error("remotecommand.NewSPDYExecutor") xc.FailOn(err) } xc.Out.Info("terminal.start", ovars{ "note": "press enter if you dont see any output", }) logger.Trace("starting stream...") //TODO: //use commandParams.DoTerminal to conditionally enable the interactive terminal //if false configure stream to do a one off command execution //and dump the container logs //similar to how it's done with the docker runtime fmt.Printf("\n") //note: blocks until done streaming or failure... err = attach.StreamWithContext( ctx, remotecommand.StreamOptions{ Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr, Tty: doTTY, }) if err != nil { if apierrors.IsNotFound(err) { logger.WithError(err). Error("attach.StreamWithContext - not found") } else if statusError, isStatus := err.(*apierrors.StatusError); isStatus { logger.WithError(err). WithFields(log.Fields{ "status": statusError.ErrStatus.Message, }).Error("attach.StreamWithContext - status error") } else { logger.WithError(err). Error("attach.StreamWithContext - other error") } xc.FailOn(err) } } func listNamespaces(ctx context.Context, api *kubernetes.Clientset) ([]string, error) { namespaces, err := api.CoreV1().Namespaces().List(ctx, metav1.ListOptions{}) if err != nil { return nil, err } if len(namespaces.Items) == 0 { return []string{}, nil } var names []string for _, nsInfo := range namespaces.Items { names = append(names, nsInfo.Name) } return names, nil } func listNamespacesWithConfig(kubeconfig string) ([]string, error) { ctx := context.Background() api, _, err := apiClientFromConfig(kubeconfig) if err != nil { log.WithError(err).Error("apiClientFromConfig") return nil, err } names, err := listNamespaces(ctx, api) if err != nil { log.WithError(err).Error("listNamespaces") return nil, err } return names, nil } func ensureNamespace(ctx context.Context, api *kubernetes.Clientset, name string) (string, error) { if name == "" { namespaces, err := api.CoreV1().Namespaces().List(ctx, metav1.ListOptions{}) if err != nil { return "", err } if len(namespaces.Items) == 0 { return "", fmt.Errorf("no namespaces") } return namespaces.Items[0].Name, nil } _, err := api.CoreV1().Namespaces().Get(ctx, name, metav1.GetOptions{}) if err != nil { if apierrors.IsNotFound(err) { log.Debugf("ensureNamespace: %s namespace is not found", name) } return "", err } return name, nil } func listActivePods(ctx context.Context, api *kubernetes.Clientset, nsName string) ([]string, error) { pods, err := api.CoreV1().Pods(nsName).List(ctx, metav1.ListOptions{}) if err != nil { return nil, err } if len(pods.Items) == 0 { return []string{}, nil } var names []string for _, podInfo := range pods.Items { switch podInfo.Status.Phase { case corev1.PodRunning, corev1.PodPending: names = append(names, podInfo.Name) } } return names, nil } func listActivePodsWithConfig(kubeconfig string, nsName string) ([]string, error) { ctx := context.Background() api, _, err := apiClientFromConfig(kubeconfig) if err != nil { log.WithError(err).Error("apiClientFromConfig") return nil, err } names, err := listActivePods(ctx, api, nsName) if err != nil { log.WithError(err).Error("listActivePods") return nil, err } return names, nil } func listAllActiveContainers( ctx context.Context, api *kubernetes.Clientset, nsName string, podName string) ([]string, error) { pod, err := api.CoreV1().Pods(nsName).Get(ctx, podName, metav1.GetOptions{}) if err != nil { return nil, err } if pod.Status.Phase != corev1.PodRunning { return nil, ErrPodNotRunning } var names []string cnl := getActiveContainerNames(pod.Status.ContainerStatuses) names = append(names, cnl...) icnl := getActiveContainerNames(pod.Status.InitContainerStatuses) names = append(names, icnl...) ecnl := getActiveContainerNames(pod.Status.EphemeralContainerStatuses) names = append(names, ecnl...) return names, nil } func listK8sDebuggableContainers( ctx context.Context, api *kubernetes.Clientset, nsName string, podName string) (map[string]string, error) { pod, err := api.CoreV1().Pods(nsName).Get(ctx, podName, metav1.GetOptions{}) if err != nil { return nil, err } if pod.Status.Phase != corev1.PodRunning { return nil, ErrPodNotRunning } activeNames := getActiveContainerNames(pod.Status.ContainerStatuses) activeContainers := map[string]string{} for _, name := range activeNames { activeContainers[name] = "" } for _, c := range pod.Spec.Containers { _, found := activeContainers[c.Name] if found { activeContainers[c.Name] = c.Image } } return activeContainers, nil } func listDebuggableK8sContainersWithConfig( kubeconfig string, nsName string, podName string) (map[string]string, error) { ctx := context.Background() api, _, err := apiClientFromConfig(kubeconfig) if err != nil { log.WithError(err).Error("apiClientFromConfig") return nil, err } _, podName, err = ensurePod(ctx, api, nsName, podName) if err != nil { if apierrors.IsNotFound(err) { log.WithError(err). WithFields(log.Fields{ "ns": nsName, "pod": podName, }).Error("ensurePod - not found") } else if statusError, isStatus := err.(*apierrors.StatusError); isStatus { log.WithError(err). WithFields(log.Fields{ "ns": nsName, "pod": podName, "status": statusError.ErrStatus.Message, }).Error("ensurePod - status error") } else if err != nil { log.WithError(err). WithFields(log.Fields{ "ns": nsName, "pod": podName, }).Error("ensurePod - other error") } return nil, err } result, err := listK8sDebuggableContainers(ctx, api, nsName, podName) if err != nil { log.WithError(err).Error("listK8sDebuggableContainers") return nil, err } return result, nil } func listK8sDebugContainers( ctx context.Context, api *kubernetes.Clientset, nsName string, podName string, targetContainer string, onlyActive bool) (map[string]*DebugContainerInfo, error) { pod, err := api.CoreV1().Pods(nsName).Get(ctx, podName, metav1.GetOptions{}) if err != nil { return nil, err } if pod.Status.Phase != corev1.PodRunning { return nil, ErrPodNotRunning } all := map[string]*DebugContainerInfo{} for _, ec := range pod.Spec.EphemeralContainers { if !strings.HasPrefix(ec.Name, containerNamePrefix) { log.WithFields(log.Fields{ "op": "listK8sDebugContainers", "ns": nsName, "pod": podName, "container": ec.Name, }).Trace("ignoring.other.ec") continue } if targetContainer != "" && ec.TargetContainerName != targetContainer { log.WithFields(log.Fields{ "op": "listK8sDebugContainers", "ns": nsName, "pod": podName, "container": ec.Name, "target.selected": targetContainer, "target": ec.TargetContainerName, }).Trace("ignoring.ec") continue } info := &DebugContainerInfo{ TargetContainerName: ec.TargetContainerName, Name: ec.Name, SpecImage: ec.Image, Command: ec.Command, Args: ec.Args, WorkingDir: ec.WorkingDir, TTY: ec.TTY, } all[info.Name] = info } result := map[string]*DebugContainerInfo{} for _, status := range pod.Status.EphemeralContainerStatuses { info, found := all[status.Name] if !found { continue } info.ContainerID = status.ContainerID info.RunningImage = status.Image info.RunningImageID = status.ImageID if status.State.Waiting != nil { info.State = CSWaiting info.WaitReason = status.State.Waiting.Reason info.WaitMessage = status.State.Waiting.Message } if status.State.Running != nil { info.State = CSRunning info.StartTime = fmt.Sprintf("%v", status.State.Running.StartedAt) } if status.State.Terminated != nil { info.State = CSTerminated info.ExitCode = status.State.Terminated.ExitCode info.ExitReason = status.State.Terminated.Reason info.ExitMessage = status.State.Terminated.Message info.StartTime = fmt.Sprintf("%v", status.State.Terminated.StartedAt) info.FinishTime = fmt.Sprintf("%v", status.State.Terminated.FinishedAt) } if onlyActive { if info.State == CSRunning { result[info.Name] = info } } else { result[info.Name] = info } } return result, nil } func listK8sDebugContainersWithConfig( kubeconfig string, nsName string, podName string, targetContainer string, onlyActive bool) (map[string]*DebugContainerInfo, error) { ctx := context.Background() api, _, err := apiClientFromConfig(kubeconfig) if err != nil { log.WithError(err).Error("apiClientFromConfig") return nil, err } _, podName, err = ensurePod(ctx, api, nsName, podName) if err != nil { if apierrors.IsNotFound(err) { log.WithError(err). WithFields(log.Fields{ "ns": nsName, "pod": podName, }).Error("ensurePod - not found") } else if statusError, isStatus := err.(*apierrors.StatusError); isStatus { log.WithError(err). WithFields(log.Fields{ "ns": nsName, "pod": podName, "status": statusError.ErrStatus.Message, }).Error("ensurePod - status error") } else if err != nil { log.WithError(err). WithFields(log.Fields{ "ns": nsName, "pod": podName, "status": statusError.ErrStatus.Message, }).Error("ensurePod - other error") } return nil, err } result, err := listK8sDebugContainers(ctx, api, nsName, podName, targetContainer, onlyActive) if err != nil { log.WithError(err).Error("listK8sDebugContainers") return nil, err } return result, nil } func getActiveContainerNames(input []corev1.ContainerStatus) []string { var list []string for _, status := range input { if status.State.Running != nil || status.State.Waiting != nil { list = append(list, status.Name) } } return list } func ensurePod(ctx context.Context, api *kubernetes.Clientset, nsName string, podName string) (*corev1.Pod, string, error) { if podName == "" { pods, err := api.CoreV1().Pods(nsName).List(ctx, metav1.ListOptions{}) if err != nil { return nil, "", err } if len(pods.Items) == 0 { return nil, "", fmt.Errorf("no pods") } podName = pods.Items[0].Name } var outputPod *corev1.Pod isPodRunning := func() (bool, error) { pod, err := api.CoreV1().Pods(nsName).Get(ctx, podName, metav1.GetOptions{}) if err != nil { return false, err } switch pod.Status.Phase { case corev1.PodRunning: outputPod = pod return true, nil case corev1.PodFailed, corev1.PodSucceeded: return false, fmt.Errorf("pod is done") } return false, nil } err := wait.PollImmediate(2*time.Second, 2*time.Minute, isPodRunning) if err != nil { return nil, "", err } return outputPod, podName, nil } const ( ctInit = "init" ctStandard = "standard" ctEphemeral = "ephemeral" ) var ( ErrPodTerminated = errors.New("Pod terminated") ErrPodNotRunning = errors.New("Pod not running") ErrContainerTerminated = errors.New("Container terminated") ) func waitForContainer( logger *log.Entry, xc *app.ExecutionContext, ctx context.Context, api *kubernetes.Clientset, nsName string, podName string, containerName string, containerType string) error { logger.Tracef("waitForContainer(%s,%s,%s,%s)", nsName, podName, containerName, containerType) isContainerRunning := func() (bool, error) { pod, err := api.CoreV1().Pods(nsName).Get(ctx, podName, metav1.GetOptions{}) if err != nil { return false, err } switch pod.Status.Phase { case corev1.PodRunning: var statuses []corev1.ContainerStatus switch containerType { case ctInit: statuses = pod.Status.InitContainerStatuses case ctStandard: statuses = pod.Status.ContainerStatuses case ctEphemeral: statuses = pod.Status.EphemeralContainerStatuses default: return false, fmt.Errorf("unknown container type") } logger.Tracef("waitForContainer: statuses (%d)", len(statuses)) for _, status := range statuses { if status.Name == containerName { if status.State.Running != nil { logger.Tracef("waitForContainer: RUNNING - %s/%s/%s[%s]", nsName, podName, containerName, containerType) if xc != nil { xc.Out.Info("wait.for.container.done", ovars{ "state": "RUNNING", "name": containerName, "pod": podName, "namespace": nsName, "type": containerType, "start_time": fmt.Sprintf("%v", status.State.Running.StartedAt), "id": status.ContainerID, }) } return true, nil } else { logger.Trace("waitForContainer: target is not running yet...") if xc != nil { paramVars := ovars{ "name": containerName, "pod": podName, "namespace": nsName, "type": containerType, "id": status.ContainerID, } if status.Started != nil && *status.Started { paramVars["is_started"] = true } if status.State.Waiting != nil { paramVars["state"] = "WAITING" if status.State.Waiting.Reason != "" { paramVars["reason"] = status.State.Waiting.Reason } if status.State.Waiting.Message != "" { paramVars["message"] = status.State.Waiting.Message } } if status.State.Terminated != nil { paramVars["state"] = "TERMINATED" paramVars["exit_code"] = status.State.Terminated.ExitCode if status.State.Terminated.Reason != "" { paramVars["reason"] = status.State.Terminated.Reason } if status.State.Terminated.Message != "" { paramVars["message"] = status.State.Terminated.Message } } xc.Out.Info("wait.for.container", paramVars) if status.State.Terminated != nil { return false, ErrContainerTerminated } } } } } //don't fail right away, let it time out... return false, nil case corev1.PodFailed, corev1.PodSucceeded: return false, ErrPodTerminated } return false, nil } return wait.PollImmediate(2*time.Second, 4*time.Minute, isContainerRunning) } func dumpK8sContainerLogs( logger *log.Entry, xc *app.ExecutionContext, ctx context.Context, api *kubernetes.Clientset, nsName string, podName string, containerName string) error { logger.Tracef("dumpK8sContainerLogs(%s,%s,%s)", nsName, podName, containerName) options := &corev1.PodLogOptions{ Container: containerName, } req := api.CoreV1(). Pods(nsName). GetLogs(podName, options) containerLogs, err := req.Stream(ctx) if err != nil { logger.WithError(err).Error("error streaming container logs") return err } defer containerLogs.Close() /* var outData bytes.Buffer _, err = io.Copy(&outData, containerLogs) if err != nil { logger.WithError(err).Error("error copying container logs") return err } fmt.Printf("%s\n", outData.String()) //_, _ = outData.WriteTo(os.Stdout) */ outData, err := ioutil.ReadAll(containerLogs) if err != nil { logger.WithError(err).Error("error reading container logs") return err } xc.Out.Info("container.logs.start") xc.Out.LogDump("debug.container.logs", string(outData)) xc.Out.Info("container.logs.end") return nil } func ephemeralContainerFromPod( pod *corev1.Pod, target string, name string) *corev1.EphemeralContainer { for _, ec := range pod.Spec.EphemeralContainers { if ec.TargetContainerName == target && ec.Name == name { return &ec } } return nil } func newEphemeralContainerInfo( target string, // target container in the pod name string, // name to use for the ephemeral container (must be unique) image string, // image to use for the ephemeral container command []string, // custom ENTRYPOINT to use for the ephemeral container (yes, it's not CMD :-)) args []string, // custom CMD to use workingDir string, envVars []NVPair, isPrivileged bool, // true if it should be a privileged container doTTY bool, ) corev1.EphemeralContainer { isTrue := true out := corev1.EphemeralContainer{ TargetContainerName: target, EphemeralContainerCommon: corev1.EphemeralContainerCommon{ TTY: doTTY, Stdin: true, Name: name, Image: image, Command: command, Args: args, WorkingDir: workingDir, //TODO: add support for more params: //EnvFrom //VolumeMounts //maybe: //ImagePullPolicy }, } if len(envVars) > 0 { for _, val := range envVars { if val.Name == "" { continue } nv := corev1.EnvVar{Name: val.Name, Value: val.Value} out.Env = append(out.Env, nv) } } if isPrivileged { out.EphemeralContainerCommon.SecurityContext = &corev1.SecurityContext{ Privileged: &isTrue, } } return out } func apiClientFromConfig(kubeconfig string) (*kubernetes.Clientset, *restclient.Config, error) { kubeconfig = os.ExpandEnv(kubeconfig) config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) if err != nil { return nil, nil, err } clientset, err := kubernetes.NewForConfig(config) if err != nil { return nil, nil, err } return clientset, config, nil } ================================================ FILE: pkg/app/master/command/debug/handler.go ================================================ package debug import ( log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" //"github.com/slimtoolkit/slim/pkg/app/master/container" //"github.com/slimtoolkit/slim/pkg/app/master/inspectors/image" "github.com/slimtoolkit/slim/pkg/app/master/version" cmd "github.com/slimtoolkit/slim/pkg/command" "github.com/slimtoolkit/slim/pkg/docker/dockerclient" "github.com/slimtoolkit/slim/pkg/report" //"github.com/slimtoolkit/slim/pkg/util/errutil" "github.com/slimtoolkit/slim/pkg/util/fsutil" v "github.com/slimtoolkit/slim/pkg/version" ) const appName = command.AppName type ovars = app.OutVars // OnCommand implements the 'debug' command func OnCommand( xc *app.ExecutionContext, gparams *command.GenericParams, commandParams *CommandParams) { logger := log.WithFields(log.Fields{"app": appName, "cmd": Name}) viChan := version.CheckAsync(gparams.CheckVersion, gparams.InContainer, gparams.IsDSImage) cmdReport := report.NewDebugCommand(gparams.ReportLocation, gparams.InContainer) cmdReport.State = cmd.StateStarted xc.Out.State("started") paramVars := ovars{ "runtime": commandParams.Runtime, "target": commandParams.TargetRef, "debug-image": commandParams.DebugContainerImage, "entrypoint": commandParams.Entrypoint, "cmd": commandParams.Cmd, "terminal": commandParams.DoTerminal, } if commandParams.Runtime == KubernetesRuntime { paramVars["namespace"] = commandParams.TargetNamespace paramVars["pod"] = commandParams.TargetPod } xc.Out.Info("params", paramVars) sid := generateSessionID() debugContainerName := generateContainerName(sid) logger = logger.WithFields( log.Fields{ "sid": sid, "debug.container.name": debugContainerName, }) switch commandParams.Runtime { case DockerRuntime: client, err := dockerclient.New(gparams.ClientConfig) if err == dockerclient.ErrNoDockerInfo { exitMsg := "missing Docker connection info" if gparams.InContainer && gparams.IsDSImage { exitMsg = "make sure to pass the Docker connect parameters to the slim app container" } xc.Out.Info("docker.connect.error", ovars{ "message": exitMsg, }) exitCode := command.ECTCommon | command.ECCNoDockerConnectInfo xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Exit(exitCode) } xc.FailOn(err) if gparams.Debug { version.Print(xc, Name, logger, client, false, gparams.InContainer, gparams.IsDSImage) } HandleDockerRuntime(logger, xc, gparams, commandParams, client, sid, debugContainerName) case KubernetesRuntime: if gparams.Debug { version.Print(xc, Name, logger, nil, false, gparams.InContainer, gparams.IsDSImage) } HandleKubernetesRuntime(logger, xc, gparams, commandParams, sid, debugContainerName) default: xc.Out.Error("runtime", "unsupported runtime") xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } xc.Out.State("completed") cmdReport.State = cmd.StateCompleted xc.Out.State("done") vinfo := <-viChan version.PrintCheckVersion(xc, "", vinfo) cmdReport.State = cmd.StateDone if cmdReport.Save() { xc.Out.Info("report", ovars{ "file": cmdReport.ReportLocation(), }) } } ================================================ FILE: pkg/app/master/command/debug/init/init.go ================================================ package init import ( "github.com/slimtoolkit/slim/pkg/app/master/command/debug" ) func init() { debug.RegisterCommand() } ================================================ FILE: pkg/app/master/command/debug/prompt.go ================================================ package debug import ( "fmt" "github.com/c-bata/go-prompt" "github.com/slimtoolkit/slim/pkg/app/master/command" ) var CommandSuggestion = prompt.Suggest{ Text: Name, Description: Usage, } var CommandFlagSuggestions = &command.FlagSuggestions{ Names: []prompt.Suggest{ {Text: command.FullFlagName(FlagRuntime), Description: FlagRuntimeUsage}, {Text: command.FullFlagName(FlagTarget), Description: FlagTargetUsage}, {Text: command.FullFlagName(FlagNamespace), Description: FlagNamespaceUsage}, {Text: command.FullFlagName(FlagPod), Description: FlagPodUsage}, {Text: command.FullFlagName(FlagDebugImage), Description: FlagDebugImageUsage}, {Text: command.FullFlagName(FlagEntrypoint), Description: FlagEntrypointUsage}, {Text: command.FullFlagName(FlagCmd), Description: FlagCmdUsage}, {Text: command.FullFlagName(FlagWorkdir), Description: FlagWorkdirUsage}, {Text: command.FullFlagName(FlagEnv), Description: FlagEnvUsage}, {Text: command.FullFlagName(FlagTerminal), Description: FlagTerminalUsage}, {Text: command.FullFlagName(FlagRunAsTargetShell), Description: FlagRunAsTargetShellUsage}, {Text: command.FullFlagName(FlagListSessions), Description: FlagListSessionsUsage}, {Text: command.FullFlagName(FlagShowSessionLogs), Description: FlagShowSessionLogsUsage}, {Text: command.FullFlagName(FlagConnectSession), Description: FlagConnectSessionUsage}, {Text: command.FullFlagName(FlagSession), Description: FlagSessionUsage}, {Text: command.FullFlagName(FlagListNamespaces), Description: FlagListNamespacesUsage}, {Text: command.FullFlagName(FlagListPods), Description: FlagListPodsUsage}, {Text: command.FullFlagName(FlagListDebuggableContainers), Description: FlagListDebuggableContainersUsage}, {Text: command.FullFlagName(FlagListDebugImage), Description: FlagListDebugImageUsage}, {Text: command.FullFlagName(FlagKubeconfig), Description: FlagKubeconfigUsage}, }, Values: map[string]command.CompleteValue{ command.FullFlagName(FlagRuntime): completeRuntime, command.FullFlagName(FlagTarget): completeTarget, command.FullFlagName(FlagDebugImage): completeDebugImage, command.FullFlagName(FlagTerminal): command.CompleteTBool, command.FullFlagName(FlagRunAsTargetShell): command.CompleteTBool, command.FullFlagName(FlagListSessions): command.CompleteBool, command.FullFlagName(FlagShowSessionLogs): command.CompleteBool, command.FullFlagName(FlagConnectSession): command.CompleteBool, command.FullFlagName(FlagSession): completeSession, command.FullFlagName(FlagListNamespaces): command.CompleteBool, command.FullFlagName(FlagListPods): command.CompleteBool, command.FullFlagName(FlagListDebuggableContainers): command.CompleteBool, command.FullFlagName(FlagListDebugImage): command.CompleteBool, command.FullFlagName(FlagNamespace): completeNamespace, command.FullFlagName(FlagPod): completePod, }, } func getDebugImageValues() []prompt.Suggest { var values []prompt.Suggest for k, v := range debugImages { value := prompt.Suggest{Text: k, Description: v} values = append(values, value) } return values } func completeDebugImage(ia *command.InteractiveApp, token string, params prompt.Document) []prompt.Suggest { return prompt.FilterHasPrefix(getDebugImageValues(), token, true) } var runtimeValues = []prompt.Suggest{ {Text: DockerRuntime, Description: "Docker runtime - debug a container running in Docker"}, {Text: KubernetesRuntime, Description: "Kubernetes runtime - debug a container running in Kubernetes"}, } func completeRuntime(ia *command.InteractiveApp, token string, params prompt.Document) []prompt.Suggest { return prompt.FilterHasPrefix(runtimeValues, token, true) } func completeNamespace(ia *command.InteractiveApp, token string, params prompt.Document) []prompt.Suggest { var values []prompt.Suggest ccs := command.GetCurrentCommandState() if ccs != nil && ccs.Command == Name { runtimeFlag := command.FullFlagName(FlagRuntime) if rtFlagVals, found := ccs.CommandFlags[runtimeFlag]; found { if len(rtFlagVals) > 0 && rtFlagVals[0] == KubernetesRuntime { kubeconfig := KubeconfigDefault kubeconfigFlag := command.FullFlagName(FlagKubeconfig) kcFlagVals, found := ccs.CommandFlags[kubeconfigFlag] if found && len(kcFlagVals) > 0 { kubeconfig = kcFlagVals[0] } names, _ := listNamespacesWithConfig(kubeconfig) for _, name := range names { value := prompt.Suggest{Text: name} values = append(values, value) } } } } return prompt.FilterHasPrefix(values, token, true) } func completePod(ia *command.InteractiveApp, token string, params prompt.Document) []prompt.Suggest { var values []prompt.Suggest ccs := command.GetCurrentCommandState() if ccs != nil && ccs.Command == Name { runtimeFlag := command.FullFlagName(FlagRuntime) if rtFlagVals, found := ccs.CommandFlags[runtimeFlag]; found { if len(rtFlagVals) > 0 && rtFlagVals[0] == KubernetesRuntime { kubeconfig := KubeconfigDefault kubeconfigFlag := command.FullFlagName(FlagKubeconfig) kcFlagVals, found := ccs.CommandFlags[kubeconfigFlag] if found && len(kcFlagVals) > 0 { kubeconfig = kcFlagVals[0] } namespace := NamespaceDefault namespaceFlag := command.FullFlagName(FlagNamespace) nsFlagVals, found := ccs.CommandFlags[namespaceFlag] if found && len(nsFlagVals) > 0 { namespace = nsFlagVals[0] } names, _ := listActivePodsWithConfig(kubeconfig, namespace) for _, name := range names { value := prompt.Suggest{Text: name} values = append(values, value) } } } } return prompt.FilterHasPrefix(values, token, true) } func completeTarget(ia *command.InteractiveApp, token string, params prompt.Document) []prompt.Suggest { var values []prompt.Suggest ccs := command.GetCurrentCommandState() if ccs != nil && ccs.Command == Name { runtimeFlag := command.FullFlagName(FlagRuntime) rtFlagVals, found := ccs.CommandFlags[runtimeFlag] if found && len(rtFlagVals) > 0 && rtFlagVals[0] == KubernetesRuntime { kubeconfig := KubeconfigDefault kubeconfigFlag := command.FullFlagName(FlagKubeconfig) kcFlagVals, found := ccs.CommandFlags[kubeconfigFlag] if found && len(kcFlagVals) > 0 { kubeconfig = kcFlagVals[0] } namespace := NamespaceDefault namespaceFlag := command.FullFlagName(FlagNamespace) nsFlagVals, found := ccs.CommandFlags[namespaceFlag] if found && len(nsFlagVals) > 0 { namespace = nsFlagVals[0] } var pod string podFlag := command.FullFlagName(FlagPod) podFlagVals, found := ccs.CommandFlags[podFlag] if found && len(podFlagVals) > 0 { pod = podFlagVals[0] } result, err := listDebuggableK8sContainersWithConfig(kubeconfig, namespace, pod) if err == nil { for cname, iname := range result { value := prompt.Suggest{ Text: cname, Description: fmt.Sprintf("image: %s", iname), } values = append(values, value) } } } else { //either no explicit 'runtime' param or other/docker runtime //todo: need a way to access/pass the docker client struct (or just pass the connect params) result, err := listDebuggableDockerContainersWithConfig(ccs.Dclient) if err == nil { for cname, iname := range result { value := prompt.Suggest{ Text: cname, Description: fmt.Sprintf("image: %s", iname), } values = append(values, value) } } } } return prompt.FilterHasPrefix(values, token, true) } func completeSession(ia *command.InteractiveApp, token string, params prompt.Document) []prompt.Suggest { var values []prompt.Suggest ccs := command.GetCurrentCommandState() if ccs != nil && ccs.Command == Name { csessValStr := ccs.GetCFValue(FlagConnectSession) runtimeFlag := command.FullFlagName(FlagRuntime) rtFlagVals, found := ccs.CommandFlags[runtimeFlag] if found && len(rtFlagVals) > 0 && rtFlagVals[0] == KubernetesRuntime { kubeconfig := KubeconfigDefault kubeconfigFlag := command.FullFlagName(FlagKubeconfig) kcFlagVals, found := ccs.CommandFlags[kubeconfigFlag] if found && len(kcFlagVals) > 0 { kubeconfig = kcFlagVals[0] } namespace := ccs.GetCFValueWithDefault(FlagNamespace, NamespaceDefault) var pod string podFlag := command.FullFlagName(FlagPod) podFlagVals, found := ccs.CommandFlags[podFlag] if found && len(podFlagVals) > 0 { pod = podFlagVals[0] } target := ccs.GetCFValue(FlagTarget) result, err := listK8sDebugContainersWithConfig( kubeconfig, namespace, pod, target, command.IsTrueStr(csessValStr)) if err == nil { for _, info := range result { desc := fmt.Sprintf("state: %s / start_time: %s / target: %s / image: %s", info.State, info.StartTime, info.TargetContainerName, info.SpecImage) value := prompt.Suggest{ Text: info.Name, Description: desc, } values = append(values, value) } } } else { //either no explicit 'runtime' param or other/docker runtime //todo: need a way to access/pass the docker client struct (or just pass the connect params) var target string targetFlag := command.FullFlagName(FlagTarget) targetFlagVals, found := ccs.CommandFlags[targetFlag] if found && len(targetFlagVals) > 0 { target = targetFlagVals[0] } result, err := listDockerDebugContainersWithConfig(ccs.Dclient, target, command.IsTrueStr(csessValStr)) if err == nil { for _, info := range result { desc := fmt.Sprintf("state: %s / start_time: %s / target: %s / image: %s", info.State, info.StartTime, info.TargetContainerName, info.SpecImage) value := prompt.Suggest{ Text: info.Name, Description: desc, } values = append(values, value) } } } } return prompt.FilterHasPrefix(values, token, true) } ================================================ FILE: pkg/app/master/command/debug/register.go ================================================ package debug import ( "github.com/slimtoolkit/slim/pkg/app/master/command" ) func RegisterCommand() { command.AddCLICommand( Name, CLI, CommandSuggestion, CommandFlagSuggestions) } ================================================ FILE: pkg/app/master/command/debug/session.go ================================================ package debug import ( "encoding/hex" "fmt" "os" "time" "github.com/segmentio/ksuid" log "github.com/sirupsen/logrus" ) const ( containerNamePrefix = "mint-debugger-" containerNamePat = "mint-debugger-%v" ) const ( CSWaiting = "WAITING" CSRunning = "RUNNING" CSTerminated = "TERMINATED" ) type DebugContainerInfo struct { TargetContainerName string Name string SpecImage string Command []string Args []string WorkingDir string TTY bool ContainerID string RunningImage string RunningImageID string StartTime string FinishTime string State string ExitCode int32 ExitReason string ExitMessage string WaitReason string WaitMessage string } func generateSessionID() string { id, err := ksuid.NewRandom() if err != nil { log.WithField("op", "debug.generateSessionID").WithError(err).Error("ksuid.NewRandom") return fmt.Sprintf("%v%v", time.Now().UTC().UnixNano(), os.Getpid()) } return hex.EncodeToString(id.Bytes()) } func generateContainerName(sid string) string { if sid == "" { sid = generateSessionID() } return fmt.Sprintf(containerNamePat, sid) } ================================================ FILE: pkg/app/master/command/debug/shell.go ================================================ package debug import ( "strings" //log "github.com/sirupsen/logrus" ) const ( sidKey = "_SESSION_ID_" shellKey = "_SHELL_NAME_" ) const ( defaultShellName = "sh" bashShellName = "bash" ) // NOTES: Mitigating variable expansion done by kubernetes & shell/heredoc var shellConfig = ` set -eu cat << 'EOF' > /.mint_debugger_shell_config.sh #!/bin/sh if [ -d "/proc/$$$/root/usr/local/sbin/" ]; then ln -s /proc/$$$/root/usr/local/sbin/ /proc/1/root/.mint_debugger_ulsbin__SESSION_ID_ export PATH=$PATH:/.mint_debugger_ulsbin__SESSION_ID_ fi if [ -d "/proc/$$$/root/usr/local/bin/" ]; then ln -s /proc/$$$/root/usr/local/bin/ /proc/1/root/.mint_debugger_ulbin__SESSION_ID_ export PATH=$PATH:/.mint_debugger_ulbin__SESSION_ID_ fi if [ -d "/proc/$$$/root/usr/sbin/" ]; then ln -s /proc/$$$/root/usr/sbin/ /proc/1/root/.mint_debugger_usbin__SESSION_ID_ export PATH=$PATH:/.mint_debugger_usbin__SESSION_ID_ fi if [ -d "/proc/$$$/root/usr/bin/" ]; then ln -s /proc/$$$/root/usr/bin/ /proc/1/root/.mint_debugger_ubin__SESSION_ID_ export PATH=$PATH:/.mint_debugger_ubin__SESSION_ID_ fi if [ -d "/proc/$$$/root/sbin/" ]; then ln -s /proc/$$$/root/sbin/ /proc/1/root/.mint_debugger_sbin__SESSION_ID_ export PATH=$PATH:/.mint_debugger_sbin__SESSION_ID_ fi if [ -d "/proc/$$$/root/bin/" ]; then ln -s /proc/$$$/root/bin/ /proc/1/root/.mint_debugger_bin__SESSION_ID_ export PATH=$PATH:/.mint_debugger_bin__SESSION_ID_ fi chroot /proc/1/root sh EOF sh /.mint_debugger_shell_config.sh ` //exec sh... func configShell(sessionID string, isK8s bool) string { result := strings.ReplaceAll(shellConfig, sidKey, sessionID) if isK8s { return result } return strings.ReplaceAll(result, "/$$$/", "/$$/") } var shellConfigAlt = ` set -eu cat << 'EOF' > /.mint_debugger_shell_config.sh #!/bin/sh if [ -d "/proc/$$$/root/usr/local/sbin/" ]; then ln -s /proc/$$$/root/usr/local/sbin/ /proc/1/root/.mint_debugger_ulsbin__SESSION_ID_ export PATH=$PATH:/.mint_debugger_ulsbin__SESSION_ID_ fi if [ -d "/proc/$$$/root/usr/local/bin/" ]; then ln -s /proc/$$$/root/usr/local/bin/ /proc/1/root/.mint_debugger_ulbin__SESSION_ID_ export PATH=$PATH:/.mint_debugger_ulbin__SESSION_ID_ fi if [ -d "/proc/$$$/root/usr/sbin/" ]; then ln -s /proc/$$$/root/usr/sbin/ /proc/1/root/.mint_debugger_usbin__SESSION_ID_ export PATH=$PATH:/.mint_debugger_usbin__SESSION_ID_ fi if [ -d "/proc/$$$/root/usr/bin/" ]; then ln -s /proc/$$$/root/usr/bin/ /proc/1/root/.mint_debugger_ubin__SESSION_ID_ export PATH=$PATH:/.mint_debugger_ubin__SESSION_ID_ fi if [ -d "/proc/$$$/root/sbin/" ]; then ln -s /proc/$$$/root/sbin/ /proc/1/root/.mint_debugger_sbin__SESSION_ID_ export PATH=$PATH:/.mint_debugger_sbin__SESSION_ID_ fi if [ -d "/proc/$$$/root/bin/" ]; then ln -s /proc/$$$/root/bin/ /proc/1/root/.mint_debugger_bin__SESSION_ID_ export PATH=$PATH:/.mint_debugger_bin__SESSION_ID_ fi if [ -f "/proc/$$$/root/bin/busybox" ] && [ ! -f "/proc/1/root/bin/busybox" ]; then ln -s /proc/$$$/root/bin/busybox /proc/1/root/bin/busybox fi ln -s /proc/$$$/root/lib/ /proc/1/root/.mint_debugger_lib__SESSION_ID_ ln -s /proc/$$$/root/usr/lib/ /proc/1/root/.mint_debugger_ulib__SESSION_ID_ ln -s /proc/$$$/root/usr/lib/libncursesw.so.6 /proc/1/root/usr/lib/libncursesw.so.6 ln -s /proc/$$$/root/usr/lib/libncursesw.so.6.4 /proc/1/root/usr/lib/libncursesw.so.6.4 #if [ -n "$LD_LIBRARY_PATH" ]; then #export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/.mint_debugger_ulib__SESSION_ID_ # export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/.mint_debugger_lib__SESSION_ID_:/.mint_debugger_ulib__SESSION_ID_ #else #export LD_LIBRARY_PATH=/.mint_debugger_ulib__SESSION_ID_ # export LD_LIBRARY_PATH=/.mint_debugger_lib__SESSION_ID_:/.mint_debugger_ulib__SESSION_ID_ #fi chroot /proc/1/root bash EOF sh /.mint_debugger_shell_config.sh ` func configShellAlt(sessionID string, isK8s bool) string { result := strings.ReplaceAll(shellConfigAlt, sidKey, sessionID) if isK8s { return result } return strings.ReplaceAll(result, "/$$$/", "/$$/") } ================================================ FILE: pkg/app/master/command/dockerclipm/cli.go ================================================ package dockerclipm import ( "encoding/json" "os" "github.com/urfave/cli/v2" "github.com/slimtoolkit/slim/pkg/version" ) const ( Name = "docker-cli-plugin-metadata" Usage = "Plugin metadata for the docker cli" ) type pluginMetadata struct { SchemaVersion string Vendor string Version string ShortDescription string URL string } var CLI = &cli.Command{ Category: "internal.metadata", Name: Name, Usage: Usage, Action: func(ctx *cli.Context) error { metadata := pluginMetadata{ SchemaVersion: "0.1.0", Vendor: "SlimToolkit", Version: version.Current(), ShortDescription: "SlimToolkit commands (build=minify, xray=static analyze, profile=dynamic analyze, lint=validate, more)", URL: "https://dockersl.im", } encoder := json.NewEncoder(os.Stdout) encoder.SetIndent("", " ") encoder.Encode(metadata) return nil }, } ================================================ FILE: pkg/app/master/command/dockerclipm/register.go ================================================ package dockerclipm import ( "github.com/c-bata/go-prompt" "github.com/slimtoolkit/slim/pkg/app/master/command" ) func RegisterCommand() { command.AddCLICommand( Name, CLI, prompt.Suggest{}, nil) } ================================================ FILE: pkg/app/master/command/edit/cli.go ================================================ package edit import ( "github.com/urfave/cli/v2" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" ) const ( Name = "edit" Usage = "Edit container image" Alias = "e" ) var CLI = &cli.Command{ Name: Name, Aliases: []string{Alias}, Usage: Usage, Action: func(ctx *cli.Context) error { gcvalues := command.GlobalFlagValues(ctx) xc := app.NewExecutionContext( Name, gcvalues.QuietCLIMode, gcvalues.OutputFormat) targetRef := ctx.String(command.FlagTarget) if targetRef == "" { if ctx.Args().Len() < 1 { xc.Out.Error("param.target", "missing target image ID/name") cli.ShowCommandHelp(ctx, Name) return nil } else { targetRef = ctx.Args().First() } } OnCommand( xc, gcvalues, targetRef) return nil }, } ================================================ FILE: pkg/app/master/command/edit/handler.go ================================================ package edit import ( log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/app/master/version" cmd "github.com/slimtoolkit/slim/pkg/command" "github.com/slimtoolkit/slim/pkg/docker/dockerclient" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/util/errutil" "github.com/slimtoolkit/slim/pkg/util/fsutil" v "github.com/slimtoolkit/slim/pkg/version" ) const appName = command.AppName type ovars = app.OutVars // OnCommand implements the 'edit' command func OnCommand( xc *app.ExecutionContext, gparams *command.GenericParams, targetRef string) { const cmdName = Name logger := log.WithFields(log.Fields{"app": appName, "cmd": cmdName}) viChan := version.CheckAsync(gparams.CheckVersion, gparams.InContainer, gparams.IsDSImage) cmdReport := report.NewEditCommand(gparams.ReportLocation, gparams.InContainer) cmdReport.State = cmd.StateStarted xc.Out.State("started") xc.Out.Info("params", ovars{ "target": targetRef, }) client, err := dockerclient.New(gparams.ClientConfig) if err == dockerclient.ErrNoDockerInfo { exitMsg := "missing Docker connection info" if gparams.InContainer && gparams.IsDSImage { exitMsg = "make sure to pass the Docker connect parameters to the slim app container" } xc.Out.Info("docker.connect.error", ovars{ "message": exitMsg, }) exitCode := command.ECTCommon | command.ECCNoDockerConnectInfo xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Exit(exitCode) } errutil.FailOn(err) if gparams.Debug { version.Print(xc, cmdName, logger, client, false, gparams.InContainer, gparams.IsDSImage) } xc.Out.State("completed") cmdReport.State = cmd.StateCompleted xc.Out.State("done") vinfo := <-viChan version.PrintCheckVersion(xc, "", vinfo) cmdReport.State = cmd.StateDone if cmdReport.Save() { xc.Out.Info("report", ovars{ "file": cmdReport.ReportLocation(), }) } } ================================================ FILE: pkg/app/master/command/edit/init/init.go ================================================ package init import ( "github.com/slimtoolkit/slim/pkg/app/master/command/edit" ) func init() { edit.RegisterCommand() } ================================================ FILE: pkg/app/master/command/edit/prompt.go ================================================ package edit import ( "github.com/c-bata/go-prompt" ) var CommandSuggestion = prompt.Suggest{ Text: Name, Description: Usage, } ================================================ FILE: pkg/app/master/command/edit/register.go ================================================ package edit import ( "github.com/slimtoolkit/slim/pkg/app/master/command" ) func RegisterCommand() { command.AddCLICommand( Name, CLI, CommandSuggestion, nil) } ================================================ FILE: pkg/app/master/command/help/cli.go ================================================ package help import ( "github.com/urfave/cli/v2" ) const ( Name = "help" Usage = "Show help info" Alias = "h" ) var CLI = &cli.Command{ Name: Name, Aliases: []string{Alias}, Usage: Usage, Action: func(ctx *cli.Context) error { cli.ShowAppHelp(ctx) return nil }, } ================================================ FILE: pkg/app/master/command/help/init/init.go ================================================ package init import ( "github.com/slimtoolkit/slim/pkg/app/master/command/help" ) func init() { help.RegisterCommand() } ================================================ FILE: pkg/app/master/command/help/prompt.go ================================================ package help import ( "github.com/c-bata/go-prompt" ) var CommandSuggestion = prompt.Suggest{ Text: Name, Description: Usage, } ================================================ FILE: pkg/app/master/command/help/register.go ================================================ package help import ( "github.com/slimtoolkit/slim/pkg/app/master/command" ) func RegisterCommand() { command.AddCLICommand( Name, CLI, CommandSuggestion, nil) } ================================================ FILE: pkg/app/master/command/images/cli.go ================================================ package images import ( "github.com/urfave/cli/v2" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" cmd "github.com/slimtoolkit/slim/pkg/command" ) const ( Name = string(cmd.Images) Usage = "Get information about container images" Alias = "i" ) //todo soon: add a lot of useful filtering flags // (to show new images from last hour, to show images in use, by size, with details, etc) var CLI = &cli.Command{ Name: Name, Aliases: []string{Alias}, Usage: Usage, Action: func(ctx *cli.Context) error { gcvalues := command.GlobalFlagValues(ctx) xc := app.NewExecutionContext( Name, gcvalues.QuietCLIMode, gcvalues.OutputFormat) OnCommand( xc, gcvalues) return nil }, } ================================================ FILE: pkg/app/master/command/images/handler.go ================================================ package images import ( "fmt" "time" "github.com/dustin/go-humanize" "github.com/jedib0t/go-pretty/v6/table" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/app/master/version" cmd "github.com/slimtoolkit/slim/pkg/command" "github.com/slimtoolkit/slim/pkg/docker/dockerclient" "github.com/slimtoolkit/slim/pkg/docker/dockerutil" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/util/fsutil" "github.com/slimtoolkit/slim/pkg/util/jsonutil" v "github.com/slimtoolkit/slim/pkg/version" ) const appName = command.AppName type ovars = app.OutVars // OnCommand implements the 'images' command func OnCommand( xc *app.ExecutionContext, gparams *command.GenericParams) { const cmdName = Name logger := log.WithFields(log.Fields{"app": appName, "cmd": cmdName}) viChan := version.CheckAsync(gparams.CheckVersion, gparams.InContainer, gparams.IsDSImage) cmdReport := report.NewImagesCommand(gparams.ReportLocation, gparams.InContainer) cmdReport.State = cmd.StateStarted xc.Out.State("started") xc.Out.Info("params", ovars{ //"target": targetRef, - todo: add command params here when added }) client, err := dockerclient.New(gparams.ClientConfig) if err == dockerclient.ErrNoDockerInfo { exitMsg := "missing Docker connection info" if gparams.InContainer && gparams.IsDSImage { exitMsg = "make sure to pass the Docker connect parameters to the slim app container" } xc.Out.Info("docker.connect.error", ovars{ "message": exitMsg, }) exitCode := command.ECTCommon | command.ECCNoDockerConnectInfo xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Exit(exitCode) } xc.FailOn(err) if gparams.Debug { version.Print(xc, cmdName, logger, client, false, gparams.InContainer, gparams.IsDSImage) } images, err := dockerutil.ListImages(client, "") xc.FailOn(err) if xc.Out.Quiet { if xc.Out.OutputFormat == command.OutputFormatJSON { fmt.Printf("%s\n", jsonutil.ToPretty(images)) return } printImagesTable(images) return } else { for name, info := range images { fields := ovars{ "name": name, "id": info.ID, "size": humanize.Bytes(uint64(info.Size)), "created": time.Unix(info.Created, 0).Format(time.RFC3339), } xc.Out.Info("image", fields) } } xc.Out.State("completed") cmdReport.State = cmd.StateCompleted xc.Out.State("done") vinfo := <-viChan version.PrintCheckVersion(xc, "", vinfo) cmdReport.State = cmd.StateDone if cmdReport.Save() { xc.Out.Info("report", ovars{ "file": cmdReport.ReportLocation(), }) } } func printImagesTable(images map[string]dockerutil.BasicImageProps) { tw := table.NewWriter() tw.AppendHeader(table.Row{"Name", "ID", "Size", "Created"}) for name, info := range images { tw.AppendRow(table.Row{ name, info.ID, humanize.Bytes(uint64(info.Size)), time.Unix(info.Created, 0).Format(time.RFC3339), }) } tw.SetStyle(table.StyleLight) tw.Style().Options.DrawBorder = false fmt.Printf("%s\n", tw.Render()) } ================================================ FILE: pkg/app/master/command/images/init/init.go ================================================ package init import ( "github.com/slimtoolkit/slim/pkg/app/master/command/images" ) func init() { images.RegisterCommand() } ================================================ FILE: pkg/app/master/command/images/prompt.go ================================================ package images import ( "github.com/c-bata/go-prompt" ) var CommandSuggestion = prompt.Suggest{ Text: Name, Description: Usage, } ================================================ FILE: pkg/app/master/command/images/register.go ================================================ package images import ( "github.com/slimtoolkit/slim/pkg/app/master/command" ) func RegisterCommand() { command.AddCLICommand( Name, CLI, CommandSuggestion, nil) } ================================================ FILE: pkg/app/master/command/install/cli.go ================================================ package install import ( "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/urfave/cli/v2" ) const ( Name = "install" Usage = "Installs slim" Alias = "in" ) const ( FlagBinDir = "bin-dir" FlagBinDirUsage = "Install binaries to the standard user app bin directory (/usr/local/bin)" FlagDockerCLIPlugin = "docker-cli-plugin" FlagDockerCLIPluginUsage = "Install as Docker CLI plugin" ) var CLI = &cli.Command{ Name: Name, Aliases: []string{Alias}, Usage: Usage, Flags: []cli.Flag{ &cli.BoolFlag{ Name: FlagBinDir, Usage: FlagBinDirUsage, EnvVars: []string{"DSLIM_INSTALL_BIN_DIR"}, }, &cli.BoolFlag{ Name: FlagDockerCLIPlugin, Usage: FlagDockerCLIPluginUsage, EnvVars: []string{"DSLIM_INSTALL_DOCKER_CLI_PLUGIN"}, }, }, Action: func(ctx *cli.Context) error { doDebug := ctx.Bool(command.FlagDebug) statePath := ctx.String(command.FlagStatePath) inContainer, isDSImage := command.IsInContainer(ctx.Bool(command.FlagInContainer)) archiveState := command.ArchiveState(ctx.String(command.FlagArchiveState), inContainer) binDir := ctx.Bool(FlagBinDir) dockerCLIPlugin := ctx.Bool(FlagDockerCLIPlugin) OnCommand(doDebug, statePath, archiveState, inContainer, isDSImage, binDir, dockerCLIPlugin) return nil }, } ================================================ FILE: pkg/app/master/command/install/handler.go ================================================ package install import ( "fmt" "os" "path/filepath" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/go-update" "github.com/slimtoolkit/slim/pkg/util/errutil" "github.com/slimtoolkit/slim/pkg/util/fsutil" vinfo "github.com/slimtoolkit/slim/pkg/version" ) const ( dockerCLIPluginDirSuffx = "/.docker/cli-plugins" masterAppName = "slim" sensorAppName = "slim-sensor" binDirName = "/usr/local/bin" ) // OnCommand implements the 'install' command func OnCommand( doDebug bool, statePath string, archiveState string, inContainer bool, isDSImage bool, binDir bool, dockerCLIPlugin bool) { logger := log.WithFields(log.Fields{"app": "slim", "cmd": "install"}) appPath, err := os.Executable() errutil.FailOn(err) appDirPath := filepath.Dir(appPath) if binDir { err := installToBinDir(logger, statePath, inContainer, isDSImage, appDirPath) if err != nil { fmt.Printf("slim[install]: info=status message='error installing to bin dir'\n") fmt.Printf("slim[install]: state=exited version=%s\n", vinfo.Current()) return } fmt.Printf("slim[install]: state=bin.dir.installed\n") //use the path from the bin dir, so installing docker CLI plugin symlinks to the right binaries appDirPath = binDirName } if dockerCLIPlugin { //create a symlink err := installDockerCLIPlugin(logger, statePath, inContainer, isDSImage, appDirPath) if err != nil { fmt.Printf("slim[install]: info=status message='error installing as Docker CLI plugin'\n") fmt.Printf("slim[install]: state=exited version=%s\n", vinfo.Current()) return } fmt.Printf("slim[install]: state=docker.cli.plugin.installed\n") } } func installToBinDir(logger *log.Entry, statePath string, inContainer, isDSImage bool, appDirPath string) error { if err := installRelease(logger, appDirPath, statePath, binDirName); err != nil { logger.Debugf("installToBinDir error: %v", err) return err } return nil } func symlinkBinaries(logger *log.Entry, appRootPath, symlinkRootPath string) error { symlinkMasterAppPath := filepath.Join(symlinkRootPath, masterAppName) symlinkSensorAppPath := filepath.Join(symlinkRootPath, sensorAppName) targetSensorAppPath := filepath.Join(appRootPath, sensorAppName) targetMasterAppPath := filepath.Join(appRootPath, masterAppName) //todo: //should not symlink the sensor because Docker CLI will treat it as an invalid plugin //need to improve sensor bin discovery from master app symlink err := os.Symlink(targetSensorAppPath, symlinkSensorAppPath) if err != nil { return err } err = os.Symlink(targetMasterAppPath, symlinkMasterAppPath) if err != nil { return err } return nil } func installDockerCLIPlugin(logger *log.Entry, statePath string, inContainer, isDSImage bool, appDirPath string) error { hd, _ := os.UserHomeDir() dockerCLIPluginDir := filepath.Join(hd, dockerCLIPluginDirSuffx) if !fsutil.Exists(dockerCLIPluginDir) { var dirMode os.FileMode = 0755 err := os.MkdirAll(dockerCLIPluginDir, dirMode) if err != nil { return err } } if err := symlinkBinaries(logger, appDirPath, dockerCLIPluginDir); err != nil { logger.Debugf("installDockerCLIPlugin error: %v", err) return err } return nil } func installRelease(logger *log.Entry, appRootPath, statePath, targetRootPath string) error { targetMasterAppPath := filepath.Join(targetRootPath, masterAppName) targetSensorAppPath := filepath.Join(targetRootPath, sensorAppName) srcSensorAppPath := filepath.Join(appRootPath, sensorAppName) srcMasterAppPath := filepath.Join(appRootPath, masterAppName) err := updateFile(logger, srcSensorAppPath, targetSensorAppPath) if err != nil { return err } //will copy the sensor to the state dir if DS is installed in a bad non-shared location on Macs fsutil.PreparePostUpdateStateDir(statePath) err = updateFile(logger, srcMasterAppPath, targetMasterAppPath) if err != nil { return err } return nil } // copied from updater func updateFile(logger *log.Entry, sourcePath, targetPath string) error { file, err := os.Open(sourcePath) if err != nil { return err } defer file.Close() options := update.Options{} if targetPath != "" { options.TargetPath = targetPath } err = update.Apply(file, options) if err != nil { if rerr := update.RollbackError(err); rerr != nil { logger.Debugf("install.updateFile(%s,%s): Failed to rollback from bad update: %v", sourcePath, targetPath, rerr) } } return err } ================================================ FILE: pkg/app/master/command/install/init/init.go ================================================ package init import ( "github.com/slimtoolkit/slim/pkg/app/master/command/install" ) func init() { install.RegisterCommand() } ================================================ FILE: pkg/app/master/command/install/prompt.go ================================================ package install import ( "github.com/c-bata/go-prompt" "github.com/slimtoolkit/slim/pkg/app/master/command" ) var CommandSuggestion = prompt.Suggest{ Text: Name, Description: Usage, } var CommandFlagSuggestions = &command.FlagSuggestions{ Names: []prompt.Suggest{ {Text: command.FullFlagName(FlagDockerCLIPlugin), Description: FlagDockerCLIPluginUsage}, }, Values: map[string]command.CompleteValue{ command.FullFlagName(FlagDockerCLIPlugin): command.CompleteBool, }, } ================================================ FILE: pkg/app/master/command/install/register.go ================================================ package install import ( "github.com/slimtoolkit/slim/pkg/app/master/command" ) func RegisterCommand() { command.AddCLICommand( Name, CLI, CommandSuggestion, CommandFlagSuggestions) } ================================================ FILE: pkg/app/master/command/lint/cli.go ================================================ package lint import ( "github.com/urfave/cli/v2" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" ) const ( Name = "lint" Usage = "Analyzes container instructions in Dockerfiles" Alias = "l" ) var CLI = &cli.Command{ Name: Name, Aliases: []string{Alias}, Usage: Usage, Flags: []cli.Flag{ cflag(command.FlagTarget), cflag(FlagTargetType), cflag(FlagSkipBuildContext), cflag(FlagBuildContextDir), cflag(FlagSkipDockerignore), cflag(FlagIncludeCheckLabel), cflag(FlagExcludeCheckLabel), cflag(FlagIncludeCheckID), cflag(FlagIncludeCheckIDFile), cflag(FlagExcludeCheckID), cflag(FlagExcludeCheckIDFile), cflag(FlagShowNoHits), cflag(FlagShowSnippet), cflag(FlagListChecks), }, Action: func(ctx *cli.Context) error { gcvalues := command.GlobalFlagValues(ctx) xc := app.NewExecutionContext( Name, gcvalues.QuietCLIMode, gcvalues.OutputFormat) doListChecks := ctx.Bool(FlagListChecks) targetRef := ctx.String(command.FlagTarget) if !doListChecks { if targetRef == "" { if ctx.Args().Len() < 1 { xc.Out.Error("param.target", "missing target Dockerfile") cli.ShowCommandHelp(ctx, Name) return nil } else { targetRef = ctx.Args().First() } } } targetType := ctx.String(FlagTargetType) doSkipBuildContext := ctx.Bool(FlagSkipBuildContext) buildContextDir := ctx.String(FlagBuildContextDir) doSkipDockerignore := ctx.Bool(FlagSkipDockerignore) includeCheckLabels, err := command.ParseCheckTags(ctx.StringSlice(FlagIncludeCheckLabel)) if err != nil { xc.Out.Error("param.error.invalid.include.check.labels", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } excludeCheckLabels, err := command.ParseCheckTags(ctx.StringSlice(FlagExcludeCheckLabel)) if err != nil { xc.Out.Error("param.error.invalid.exclude.check.labels", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } includeCheckIDs, err := command.ParseTokenSet(ctx.StringSlice(FlagIncludeCheckID)) if err != nil { xc.Out.Error("param.error.invalid.include.check.ids", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } includeCheckIDFile := ctx.String(FlagIncludeCheckIDFile) moreIncludeCheckIDs, err := command.ParseTokenSetFile(includeCheckIDFile) if err != nil { xc.Out.Error("param.error.invalid.include.check.ids.from.file", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } for k, v := range moreIncludeCheckIDs { includeCheckIDs[k] = v } excludeCheckIDs, err := command.ParseTokenSet(ctx.StringSlice(FlagExcludeCheckID)) if err != nil { xc.Out.Error("param.error.invalid.exclude.check.ids", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } excludeCheckIDFile := ctx.String(FlagExcludeCheckIDFile) moreExcludeCheckIDs, err := command.ParseTokenSetFile(excludeCheckIDFile) if err != nil { xc.Out.Error("param.error.invalid.exclude.check.ids.from.file", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } for k, v := range moreExcludeCheckIDs { excludeCheckIDs[k] = v } doShowNoHits := ctx.Bool(FlagShowNoHits) doShowSnippet := ctx.Bool(FlagShowSnippet) OnCommand( xc, gcvalues, targetRef, targetType, doSkipBuildContext, buildContextDir, doSkipDockerignore, includeCheckLabels, excludeCheckLabels, includeCheckIDs, excludeCheckIDs, doShowNoHits, doShowSnippet, doListChecks) return nil }, } ================================================ FILE: pkg/app/master/command/lint/flags.go ================================================ package lint import ( log "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" "github.com/slimtoolkit/slim/pkg/app/master/command" ) // Lint command flag names const ( FlagTargetType = "target-type" FlagSkipBuildContext = "skip-build-context" FlagBuildContextDir = "build-context-dir" FlagSkipDockerignore = "skip-dockerignore" FlagIncludeCheckLabel = "include-check-label" FlagExcludeCheckLabel = "exclude-check-label" FlagIncludeCheckID = "include-check-id" FlagIncludeCheckIDFile = "include-check-id-file" FlagExcludeCheckID = "exclude-check-id" FlagExcludeCheckIDFile = "exclude-check-id-file" FlagShowNoHits = "show-nohits" FlagShowSnippet = "show-snippet" FlagListChecks = "list-checks" ) // Lint command flag usage info const ( FlagLintTargetUsage = "Target Dockerfile path (or container image)" FlagTargetTypeUsage = "Explicitly specify the command target type (values: dockerfile, image)" FlagSkipBuildContextUsage = "Don't try to analyze build context" FlagBuildContextDirUsage = "Explicitly specify the build context directory" FlagSkipDockerignoreUsage = "Don't try to analyze .dockerignore" FlagIncludeCheckLabelUsage = "Include checks with the selected label key:value" FlagExcludeCheckLabelUsage = "Exclude checks with the selected label key:value" FlagIncludeCheckIDUsage = "Check ID to include" FlagIncludeCheckIDFileUsage = "File with check IDs to include" FlagExcludeCheckIDUsage = "Check ID to exclude" FlagExcludeCheckIDFileUsage = "File with check IDs to exclude" FlagShowNoHitsUsage = "Show checks with no matches" FlagShowSnippetUsage = "Show check match snippet" FlagListChecksUsage = "List available checks" ) var Flags = map[string]cli.Flag{ command.FlagTarget: &cli.StringFlag{ Name: command.FlagTarget, Value: "", Usage: FlagLintTargetUsage, EnvVars: []string{"DSLIM_TARGET"}, }, FlagTargetType: &cli.StringFlag{ Name: FlagTargetType, Value: "", Usage: FlagTargetTypeUsage, EnvVars: []string{"DSLIM_LINT_TARGET_TYPE"}, }, FlagSkipBuildContext: &cli.BoolFlag{ Name: FlagSkipBuildContext, Usage: FlagSkipBuildContextUsage, EnvVars: []string{"DSLIM_LINT_SKIP_BC"}, }, FlagBuildContextDir: &cli.StringFlag{ Name: FlagBuildContextDir, Value: "", Usage: FlagBuildContextDirUsage, EnvVars: []string{"DSLIM_LINT_BC_DIR"}, }, FlagSkipDockerignore: &cli.BoolFlag{ Name: FlagSkipDockerignore, Usage: FlagSkipDockerignoreUsage, EnvVars: []string{"DSLIM_LINT_SKIP_DI"}, }, FlagIncludeCheckLabel: &cli.StringSliceFlag{ Name: FlagIncludeCheckLabel, Value: cli.NewStringSlice(""), Usage: FlagIncludeCheckLabelUsage, EnvVars: []string{"DSLIM_LINT_INCLUDE_LABEL"}, }, FlagExcludeCheckLabel: &cli.StringSliceFlag{ Name: FlagExcludeCheckLabel, Value: cli.NewStringSlice(""), Usage: FlagExcludeCheckLabelUsage, EnvVars: []string{"DSLIM_LINT_EXCLUDE_LABEL"}, }, FlagIncludeCheckID: &cli.StringSliceFlag{ Name: FlagIncludeCheckID, Value: cli.NewStringSlice(""), Usage: FlagIncludeCheckIDUsage, EnvVars: []string{"DSLIM_LINT_INCLUDE_CID"}, }, FlagIncludeCheckIDFile: &cli.StringFlag{ Name: FlagIncludeCheckIDFile, Value: "", Usage: FlagIncludeCheckIDFileUsage, EnvVars: []string{"DSLIM_LINT_INCLUDE_CID_FILE"}, }, FlagExcludeCheckID: &cli.StringSliceFlag{ Name: FlagExcludeCheckID, Value: cli.NewStringSlice(""), Usage: FlagExcludeCheckIDUsage, EnvVars: []string{"DSLIM_LINT_EXCLUDE_CID"}, }, FlagExcludeCheckIDFile: &cli.StringFlag{ Name: FlagExcludeCheckIDFile, Value: "", Usage: FlagExcludeCheckIDFileUsage, EnvVars: []string{"DSLIM_LINT_EXCLUDE_CID_FILE"}, }, FlagShowNoHits: &cli.BoolFlag{ Name: FlagShowNoHits, Usage: FlagShowNoHitsUsage, EnvVars: []string{"DSLIM_LINT_SHOW_NOHITS"}, }, FlagShowSnippet: &cli.BoolFlag{ Name: FlagShowSnippet, Value: true, Usage: FlagShowSnippetUsage, EnvVars: []string{"DSLIM_LINT_SHOW_SNIPPET"}, }, FlagListChecks: &cli.BoolFlag{ Name: FlagListChecks, Usage: FlagListChecksUsage, EnvVars: []string{"DSLIM_LINT_LIST_CHECKS"}, }, } func cflag(name string) cli.Flag { cf, ok := Flags[name] if !ok { log.Fatalf("unknown flag='%s'", name) } return cf } ================================================ FILE: pkg/app/master/command/lint/handler.go ================================================ package lint import ( "fmt" "strings" dockerapi "github.com/fsouza/go-dockerclient" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/app/master/version" cmd "github.com/slimtoolkit/slim/pkg/command" "github.com/slimtoolkit/slim/pkg/docker/linter" "github.com/slimtoolkit/slim/pkg/docker/linter/check" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/util/errutil" ) const appName = command.AppName type ovars = app.OutVars // OnCommand implements the 'lint' command func OnCommand( xc *app.ExecutionContext, gparams *command.GenericParams, targetRef string, targetType string, doSkipBuildContext bool, buildContextDir string, doSkipDockerignore bool, includeCheckLabels map[string]string, excludeCheckLabels map[string]string, includeCheckIDs map[string]struct{}, excludeCheckIDs map[string]struct{}, doShowNoHits bool, doShowSnippet bool, doListChecks bool) { const cmdName = Name logger := log.WithFields(log.Fields{"app": appName, "cmd": cmdName}) viChan := version.CheckAsync(gparams.CheckVersion, gparams.InContainer, gparams.IsDSImage) cmdReport := report.NewLintCommand(gparams.ReportLocation, gparams.InContainer) cmdReport.State = cmd.StateStarted xc.Out.State("started") xc.Out.Info("params", ovars{ "target": targetRef, "list.checks": doListChecks, }) /* do it only when targeting images client, err := dockerclient.New(gparams.ClientConfig) if err == dockerclient.ErrNoDockerInfo { exitMsg := "missing Docker connection info" if gparams.InContainer && gparams.IsDSImage { exitMsg = "make sure to pass the Docker connect parameters to the 'slim' container" } fmt.Printf("cmd=%s info=docker.connect.error message='%s'\n", cmdName, exitMsg) fmt.Printf("cmd=%s state=exited version=%s location='%s'\n", cmdName, v.Current(), fsutil.ExeDir()) os.Exit(ectCommon | ecNoDockerConnectInfo) } errutil.FailOn(err) */ var client *dockerapi.Client if gparams.Debug { version.Print(xc, cmdName, logger, client, false, gparams.InContainer, gparams.IsDSImage) } if doListChecks { checks := linter.ListChecks() printLintChecks(xc, checks, appName, cmdName) } else { cmdReport.TargetType = linter.DockerfileTargetType cmdReport.TargetReference = targetRef options := linter.Options{ DockerfilePath: targetRef, SkipBuildContext: doSkipBuildContext, BuildContextDir: buildContextDir, SkipDockerignore: doSkipDockerignore, Selector: linter.CheckSelector{ IncludeCheckLabels: includeCheckLabels, IncludeCheckIDs: includeCheckIDs, ExcludeCheckLabels: excludeCheckLabels, ExcludeCheckIDs: excludeCheckIDs, }, } lintResults, err := linter.Execute(options) errutil.FailOn(err) cmdReport.BuildContextDir = lintResults.BuildContextDir cmdReport.Hits = lintResults.Hits cmdReport.Errors = lintResults.Errors printLintResults(xc, lintResults, appName, cmdName, cmdReport, doShowNoHits, doShowSnippet) } xc.Out.State("completed") cmdReport.State = cmd.StateCompleted xc.Out.State("done") vinfo := <-viChan version.PrintCheckVersion(xc, "", vinfo) cmdReport.State = cmd.StateDone if cmdReport.Save() { xc.Out.Info("report", ovars{ "file": cmdReport.ReportLocation(), }) } } func printLintChecks( xc *app.ExecutionContext, checks []*check.Info, appName string, cmdName cmd.Type) { xc.Out.Info("lint.checks", ovars{ "count": len(checks), }) for _, info := range checks { xc.Out.Info("lint.check.info", ovars{ "id": info.ID, "name": info.Name, "labels": kvMapString(info.Labels), "description": info.Description, "url": info.DetailsURL, }) } } func kvMapString(m map[string]string) string { var pairs []string for k, v := range m { pairs = append(pairs, fmt.Sprintf("%s=%s", k, v)) } return strings.Join(pairs, ",") } func printLintResults( xc *app.ExecutionContext, lintResults *linter.Report, appName string, cmdName cmd.Type, cmdReport *report.LintCommand, doShowNoHits bool, doShowSnippet bool) { cmdReport.HitsCount = len(lintResults.Hits) cmdReport.NoHitsCount = len(lintResults.NoHits) cmdReport.ErrorsCount = len(lintResults.Errors) xc.Out.Info("lint.results", ovars{ "hits": cmdReport.HitsCount, "nohits": cmdReport.NoHitsCount, "errors": cmdReport.ErrorsCount, }) if cmdReport.HitsCount > 0 { xc.Out.Info("lint.check.hits", ovars{ "count": cmdReport.HitsCount, }) for id, result := range lintResults.Hits { xc.Out.Info("lint.check.hit", ovars{ "id": id, "name": result.Source.Name, "level": result.Source.Labels[check.LabelLevel], "message": result.Message, }) if len(result.Matches) > 0 { xc.Out.Info("lint.check.hit.matches", ovars{ "count": len(result.Matches), }) for _, m := range result.Matches { //var instructionInfo string //the match message has the instruction info already //if m.Instruction != nil { // instructionInfo = fmt.Sprintf(" instruction(start=%d end=%d name=%s gindex=%d sindex=%d)", // m.Instruction.StartLine, // m.Instruction.EndLine, // m.Instruction.Name, // m.Instruction.GlobalIndex, // m.Instruction.StageIndex) //} minfo := ovars{} if m.Stage != nil { minfo["stage"] = fmt.Sprintf("%d:%s", m.Stage.Index, m.Stage.Name) } minfo["message"] = m.Message xc.Out.Info("lint.check.hit.match", minfo) if m.Instruction != nil && len(m.Instruction.RawLines) > 0 && doShowSnippet { for idx, data := range m.Instruction.RawLines { xc.Out.Info("lint.check.hit.match.snippet", ovars{ "line": idx + m.Instruction.StartLine, "data": data, }) } } } } } } if doShowNoHits && cmdReport.NoHitsCount > 0 { xc.Out.Info("lint.check.nohits", ovars{ "count": cmdReport.NoHitsCount, }) for id, result := range lintResults.NoHits { xc.Out.Info("lint.check.nohit", ovars{ "id": id, "name": result.Source.Name, }) } } if cmdReport.ErrorsCount > 0 { xc.Out.Info("lint.check.errors", ovars{ "count": cmdReport.ErrorsCount, }) for id, err := range lintResults.Errors { xc.Out.Info("lint.check.error", ovars{ "id": id, "message": err, }) } } } ================================================ FILE: pkg/app/master/command/lint/init/init.go ================================================ package init import ( "github.com/slimtoolkit/slim/pkg/app/master/command/lint" ) func init() { lint.RegisterCommand() } ================================================ FILE: pkg/app/master/command/lint/prompt.go ================================================ package lint import ( "github.com/c-bata/go-prompt" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/docker/linter/check" ) var CommandSuggestion = prompt.Suggest{ Text: Name, Description: Usage, } var CommandFlagSuggestions = &command.FlagSuggestions{ Names: []prompt.Suggest{ {Text: command.FullFlagName(command.FlagTarget), Description: FlagLintTargetUsage}, {Text: command.FullFlagName(FlagTargetType), Description: FlagTargetTypeUsage}, {Text: command.FullFlagName(FlagSkipBuildContext), Description: FlagSkipBuildContextUsage}, {Text: command.FullFlagName(FlagBuildContextDir), Description: FlagBuildContextDirUsage}, {Text: command.FullFlagName(FlagSkipDockerignore), Description: FlagSkipDockerignoreUsage}, {Text: command.FullFlagName(FlagIncludeCheckLabel), Description: FlagIncludeCheckLabelUsage}, {Text: command.FullFlagName(FlagExcludeCheckLabel), Description: FlagExcludeCheckLabelUsage}, {Text: command.FullFlagName(FlagIncludeCheckID), Description: FlagIncludeCheckIDUsage}, {Text: command.FullFlagName(FlagIncludeCheckIDFile), Description: FlagIncludeCheckIDFileUsage}, {Text: command.FullFlagName(FlagExcludeCheckID), Description: FlagExcludeCheckIDUsage}, {Text: command.FullFlagName(FlagExcludeCheckIDFile), Description: FlagExcludeCheckIDFileUsage}, {Text: command.FullFlagName(FlagShowNoHits), Description: FlagShowNoHitsUsage}, {Text: command.FullFlagName(FlagShowSnippet), Description: FlagShowSnippetUsage}, {Text: command.FullFlagName(FlagListChecks), Description: FlagListChecksUsage}, }, Values: map[string]command.CompleteValue{ command.FullFlagName(command.FlagTarget): completeLintTarget, command.FullFlagName(FlagTargetType): completeLintTargetType, command.FullFlagName(FlagSkipBuildContext): command.CompleteBool, command.FullFlagName(FlagBuildContextDir): command.CompleteFile, command.FullFlagName(FlagSkipDockerignore): command.CompleteBool, command.FullFlagName(FlagIncludeCheckID): completeLintCheckID, command.FullFlagName(FlagIncludeCheckIDFile): command.CompleteFile, command.FullFlagName(FlagExcludeCheckID): completeLintCheckID, command.FullFlagName(FlagExcludeCheckIDFile): command.CompleteFile, command.FullFlagName(FlagShowNoHits): command.CompleteBool, command.FullFlagName(FlagShowSnippet): command.CompleteTBool, command.FullFlagName(FlagListChecks): command.CompleteBool, }, } func completeLintTarget(ia *command.InteractiveApp, token string, params prompt.Document) []prompt.Suggest { //for now only support selecting Dockerfiles //later add an ability to choose (files or images) //based on the target-type parameter return command.CompleteFile(ia, token, params) } var lintTargetTypeValues = []prompt.Suggest{ {Text: "dockerfile", Description: "Dockerfile target type"}, {Text: "image", Description: "Docker image target type"}, } func completeLintTargetType(ia *command.InteractiveApp, token string, params prompt.Document) []prompt.Suggest { return prompt.FilterHasPrefix(lintTargetTypeValues, token, true) } func completeLintCheckID(ia *command.InteractiveApp, token string, params prompt.Document) []prompt.Suggest { var values []prompt.Suggest for _, check := range check.AllChecks { info := check.Get() entry := prompt.Suggest{ Text: info.ID, Description: info.Name, } values = append(values, entry) } return prompt.FilterContains(values, token, true) } ================================================ FILE: pkg/app/master/command/lint/register.go ================================================ package lint import ( "github.com/slimtoolkit/slim/pkg/app/master/command" ) func RegisterCommand() { command.AddCLICommand( Name, CLI, CommandSuggestion, CommandFlagSuggestions) } ================================================ FILE: pkg/app/master/command/merge/cli.go ================================================ package merge import ( "fmt" "github.com/urfave/cli/v2" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" ) const ( Name = "merge" Usage = "Merge two container images (optimized to merge minified images)" Alias = "m" ) //FUTURE/TODO: extend it to be a generic merge function not limited to minified images var CLI = &cli.Command{ Name: Name, Aliases: []string{Alias}, Usage: Usage, Flags: []cli.Flag{ cflag(FlagImage), cflag(FlagUseLastImageMetadata), cflag(FlagTag), }, Action: func(ctx *cli.Context) error { gfvalues := command.GlobalFlagValues(ctx) xc := app.NewExecutionContext( Name, gfvalues.QuietCLIMode, gfvalues.OutputFormat) if ctx.Args().Len() < 1 { xc.Out.Error("param.target", "missing target image ID/name") cli.ShowCommandHelp(ctx, Name) return nil } cfvalues, err := CommandFlagValues(xc, ctx) if err != nil { //CommandFlagValues() outputs the error messages already return nil } OnCommand( xc, gfvalues, cfvalues) return nil }, } type CommandParams struct { FirstImage string `json:"first_image"` LastImage string `json:"last_image"` UseLastImageMetadata bool `json:"use_last_image_metadata"` OutputTags []string `json:"output_tags"` } func CommandFlagValues(xc *app.ExecutionContext, ctx *cli.Context) (*CommandParams, error) { values := &CommandParams{ UseLastImageMetadata: ctx.Bool(FlagUseLastImageMetadata), OutputTags: ctx.StringSlice(FlagTag), } images := ctx.StringSlice(FlagImage) if len(images) > 0 { if len(images) < 2 { xc.Out.Error("param.image", "must have two image references") cli.ShowCommandHelp(ctx, Name) return nil, fmt.Errorf("must have two image references") } values.FirstImage = images[0] values.LastImage = images[1] } if ctx.Args().Len() > 0 { if ctx.Args().Len() < 2 { xc.Out.Error("param.image", "must have two image references") cli.ShowCommandHelp(ctx, Name) return nil, fmt.Errorf("must have two image references") } values.FirstImage = ctx.Args().Get(0) values.LastImage = ctx.Args().Get(1) } if values.FirstImage == "" || values.LastImage == "" { xc.Out.Error("param.image", "not enough image references") cli.ShowCommandHelp(ctx, Name) return nil, fmt.Errorf("not enough image references") } return values, nil } ================================================ FILE: pkg/app/master/command/merge/flags.go ================================================ package merge import ( log "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" ) // Merge command flag names const ( FlagImage = "image" FlagUseLastImageMetadata = "use-last-image-metadata" FlagTag = "tag" ) // Merge command flag usage info const ( FlagImageUsage = "Image to merge (flag instance position determines the merge order)" FlagUseLastImageMetadataUsage = "Use only the last image metadata for the merged image" FlagTagUsage = "Custom tags for the output image" ) var Flags = map[string]cli.Flag{ FlagImage: &cli.StringSliceFlag{ Name: FlagImage, Value: cli.NewStringSlice(), Usage: FlagImageUsage, EnvVars: []string{"DSLIM_MERGE_IMAGE"}, }, FlagUseLastImageMetadata: &cli.BoolFlag{ Name: FlagUseLastImageMetadata, Value: false, //defaults to false Usage: FlagUseLastImageMetadataUsage, EnvVars: []string{"DSLIM_MERGE_USE_LAST_IMAGE_META"}, }, FlagTag: &cli.StringSliceFlag{ Name: FlagTag, Value: cli.NewStringSlice(), Usage: FlagTagUsage, EnvVars: []string{"DSLIM_TARGET_TAG"}, }, } func cflag(name string) cli.Flag { cf, ok := Flags[name] if !ok { log.Fatalf("unknown flag='%s'", name) } return cf } ================================================ FILE: pkg/app/master/command/merge/handler.go ================================================ package merge import ( "archive/tar" "errors" "fmt" "io" "os" "strings" "github.com/cespare/xxhash/v2" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/app/master/inspectors/image" "github.com/slimtoolkit/slim/pkg/app/master/version" cmd "github.com/slimtoolkit/slim/pkg/command" "github.com/slimtoolkit/slim/pkg/docker/dockerclient" "github.com/slimtoolkit/slim/pkg/imagebuilder" "github.com/slimtoolkit/slim/pkg/imagebuilder/internalbuilder" "github.com/slimtoolkit/slim/pkg/imagereader" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/util/errutil" "github.com/slimtoolkit/slim/pkg/util/fsutil" v "github.com/slimtoolkit/slim/pkg/version" ) const appName = command.AppName type ovars = app.OutVars // OnCommand implements the 'merge' command func OnCommand( xc *app.ExecutionContext, gparams *command.GenericParams, cparams *CommandParams) { const cmdName = Name logger := log.WithFields(log.Fields{"app": appName, "cmd": cmdName}) viChan := version.CheckAsync(gparams.CheckVersion, gparams.InContainer, gparams.IsDSImage) cmdReport := report.NewMergeCommand(gparams.ReportLocation, gparams.InContainer) cmdReport.State = cmd.StateStarted cmdReport.FirstImage = cparams.FirstImage cmdReport.LastImage = cparams.LastImage cmdReport.UseLastImageMetadata = cparams.UseLastImageMetadata xc.Out.State("started") xc.Out.Info("params", ovars{ "image.first": cparams.FirstImage, "image.last": cparams.LastImage, "use.last.image.metadata": cparams.UseLastImageMetadata, "output.tags": cparams.OutputTags, }) client, err := dockerclient.New(gparams.ClientConfig) if err == dockerclient.ErrNoDockerInfo { exitMsg := "missing Docker connection info" if gparams.InContainer && gparams.IsDSImage { exitMsg = "make sure to pass the Docker connect parameters to the slim app container" } xc.Out.Info("docker.connect.error", ovars{ "message": exitMsg, }) exitCode := command.ECTCommon | command.ECCNoDockerConnectInfo xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Exit(exitCode) } xc.FailOn(err) if gparams.Debug { version.Print(xc, cmdName, logger, client, false, gparams.InContainer, gparams.IsDSImage) } ////////////////////////////////////////////////// ensureImage := func(name string, imageRef string, cr *report.MergeCommand) string { imageInspector, err := image.NewInspector(client, imageRef) xc.FailOn(err) noImage, err := imageInspector.NoImage() errutil.FailOn(err) if noImage { xc.Out.Error(fmt.Sprintf("%s.image.not.found", name), "make sure the target image already exists locally") cmdReport.State = cmd.StateError exitCode := command.ECTCommon | command.ECCImageNotFound xc.Out.State("exited", ovars{ "exit.code": exitCode, }) xc.Exit(exitCode) } return imageInspector.ImageRef } //and refresh the image refs cparams.FirstImage = ensureImage("first", cmdReport.FirstImage, cmdReport) cmdReport.FirstImage = cparams.FirstImage //and refresh the image refs cparams.LastImage = ensureImage("last", cmdReport.LastImage, cmdReport) cmdReport.LastImage = cparams.LastImage outputTags := cparams.OutputTags if len(outputTags) == 0 { var outputName string if strings.Contains(cparams.LastImage, ":") { parts := strings.SplitN(cparams.LastImage, ":", 2) outputName = fmt.Sprintf("%s.merged:%s", parts[0], parts[1]) } else { outputName = fmt.Sprintf("%s.merged", cparams.LastImage) } outputTags = append(outputTags, outputName) } fiReader, err := imagereader.New(cparams.FirstImage) xc.FailOn(err) liReader, err := imagereader.New(cparams.LastImage) xc.FailOn(err) xc.Out.State("image.metadata.merge.start") fiImageConfig, err := fiReader.ImageConfig() xc.FailOn(err) liImageConfig, err := liReader.ImageConfig() xc.FailOn(err) var outImageConfig *imagebuilder.ImageConfig if cparams.UseLastImageMetadata { outImageConfig = liImageConfig } else { imageConfig := *liImageConfig //merge environment variables (todo: do a better job merging envs, need to parse k/v) envMap := map[string]struct{}{} for _, v := range fiImageConfig.Config.Env { envMap[v] = struct{}{} } for _, v := range liImageConfig.Config.Env { envMap[v] = struct{}{} } imageConfig.Config.Env = []string{} for k := range envMap { imageConfig.Config.Env = append(imageConfig.Config.Env, k) } //merge labels labelMap := map[string]string{} for k, v := range fiImageConfig.Config.Labels { labelMap[k] = v } for k, v := range liImageConfig.Config.Labels { labelMap[k] = v } imageConfig.Config.Labels = labelMap //merge exposed ports portMap := map[string]struct{}{} for k := range fiImageConfig.Config.ExposedPorts { portMap[k] = struct{}{} } for k := range liImageConfig.Config.ExposedPorts { portMap[k] = struct{}{} } imageConfig.Config.ExposedPorts = portMap //merge volumes volumeMap := map[string]struct{}{} for k := range fiImageConfig.Config.Volumes { volumeMap[k] = struct{}{} } for k := range liImageConfig.Config.Volumes { volumeMap[k] = struct{}{} } imageConfig.Config.Volumes = volumeMap //Merging OnBuild requires the instruction order to be preserved //Auto-merging OnBuild instructions is not always ideal because //of the potential side effects if the merged images are not very compatible. //Merging minified images of the same source image should have no side effects //because the OnBuild instructions will be identical. sameLists := func(first, second []string) bool { if len(first) != len(second) { return false } for idx := range first { if first[idx] != second[idx] { return false } } return true } if !sameLists(fiImageConfig.Config.OnBuild, liImageConfig.Config.OnBuild) { var onBuild []string onBuild = append(onBuild, fiImageConfig.Config.OnBuild...) onBuild = append(onBuild, liImageConfig.Config.OnBuild...) imageConfig.Config.OnBuild = onBuild } outImageConfig = &imageConfig } xc.Out.State("image.metadata.merge.done") xc.Out.State("image.data.merge.start") fiDataTarName, err := fiReader.ExportFilesystem() xc.FailOn(err) liDataTarName, err := liReader.ExportFilesystem() xc.FailOn(err) f1, err := os.Open(fiDataTarName) xc.FailOn(err) defer f1.Close() index, err := tarMapFromFile(f1) xc.FailOn(err) f2, err := os.Open(liDataTarName) xc.FailOn(err) defer f2.Close() index2, err := tarMapFromFile(f2) xc.FailOn(err) logger.Debug("updating tar map with first tar data...") for p, info := range index2 { other, found := index[p] if !found { index[p] = info continue } if info.Header.Typeflag == other.Header.Typeflag && info.Header.Size == other.Header.Size && info.Hash == other.Hash { //can/should also check info.Header.Mode and info.Header.ModTime //if info.Header.ModTime.After(other.Header.ModTime) { // info.Replaced = append(other.Replaced, other) // index[p] = info // continue //} other.Dups++ continue } info.Replaced = append(other.Replaced, other) index[p] = info } outTarFileName, err := tarFromMap(logger, "", index) if !fsutil.Exists(outTarFileName) || !fsutil.IsRegularFile(outTarFileName) || !fsutil.IsTarFile(outTarFileName) { xc.FailOn(fmt.Errorf("bad output tar - %s", outTarFileName)) } xc.Out.State("image.data.merge.done") xc.Out.State("output.image.generate.start") ibo, err := imagebuilder.SimpleBuildOptionsFromImageConfig(outImageConfig) xc.FailOn(err) ibo.Tags = outputTags layerInfo := imagebuilder.LayerDataInfo{ Type: imagebuilder.TarSource, Source: outTarFileName, Params: &imagebuilder.DataParams{ TargetPath: "/", }, } ibo.Layers = append(ibo.Layers, layerInfo) engine, err := internalbuilder.New( false, //show build logs doShowBuildLogs, true, //push to daemon - TODO: have a param to control this later //output image tar (if not 'saving' to daemon) false) xc.FailOn(err) imageResult, err := engine.Build(*ibo) xc.FailOn(err) xc.Out.Info("results.output", ovars{ "image.name": imageResult.Name, "image.id": imageResult.ID, "image.digest": imageResult.Digest, }) ensureImage("output", outputTags[0], cmdReport) xc.Out.State("output.image.generate.done") ////////////////////////////////////////////////// xc.Out.State(cmd.StateCompleted) cmdReport.State = cmd.StateCompleted xc.Out.State(cmd.StateDone) vinfo := <-viChan version.PrintCheckVersion(xc, "", vinfo) cmdReport.State = cmd.StateDone if cmdReport.Save() { xc.Out.Info("report", ovars{ "file": cmdReport.ReportLocation(), }) } } type tfInfo struct { FileIndex uint32 Header *tar.Header Hash uint64 File *os.File DataOffset int64 Dups uint32 //to count duplicates (can have extra field to track tar file metadata later) Replaced []*tfInfo } func tarMapFromFile(f *os.File) (map[string]*tfInfo, error) { tr := tar.NewReader(f) tarMap := map[string]*tfInfo{} var fileIndex uint32 for { th, err := tr.Next() if err != nil { if errors.Is(err, io.EOF) { break } fmt.Println(err) return tarMap, err } if th == nil { fmt.Println("skipping empty tar header...") continue } offset, err := f.Seek(0, os.SEEK_CUR) if err != nil { fmt.Println(err) return tarMap, err } sr := io.NewSectionReader(f, offset, th.Size) hash := xxhash.New() //if _, err := io.Copy(hash, tr); err != nil { if _, err := io.Copy(hash, sr); err != nil { //_, err = io.CopyN(hash, sr, th.Size) log.Fatalf("Failed to compute hash: %v", err) } hashValue := hash.Sum64() //NOTE: //Not exposing the archived file data right now //because it'll require to read/load the data into memory //and for big images it'll be a lot of data. //For now just re-read the data when needed. tarMap[th.Name] = &tfInfo{ FileIndex: fileIndex, Header: th, Hash: hashValue, File: f, //tar file ref (not the file inside tar) DataOffset: offset, //offset in tar file } fileIndex++ } return tarMap, nil } func tarFromMap(logger *log.Entry, outputPath string, tarMap map[string]*tfInfo) (string, error) { var out *os.File if outputPath == "" { tarFile, err := os.CreateTemp("", "image-output-*.tar") if err != nil { return "", err } out = tarFile } else { tarFile, err := os.Create(outputPath) if err != nil { return "", err } out = tarFile } defer out.Close() // Create a new tar archive tw := tar.NewWriter(out) defer tw.Close() // Iterate over the input files for filePath, info := range tarMap { logger.Tracef("%s -> %+v\n", filePath, info) if err := tw.WriteHeader(info.Header); err != nil { panic(err) } if info.Header.Size == 0 { continue } if info.DataOffset < 0 { continue } sr := io.NewSectionReader(info.File, info.DataOffset, info.Header.Size) if _, err := io.Copy(tw, sr); err != nil { return "", err } } return out.Name(), nil } ================================================ FILE: pkg/app/master/command/merge/init/init.go ================================================ package init import ( "github.com/slimtoolkit/slim/pkg/app/master/command/merge" ) func init() { merge.RegisterCommand() } ================================================ FILE: pkg/app/master/command/merge/prompt.go ================================================ package merge import ( "github.com/c-bata/go-prompt" "github.com/slimtoolkit/slim/pkg/app/master/command" ) var CommandSuggestion = prompt.Suggest{ Text: Name, Description: Usage, } var CommandFlagSuggestions = &command.FlagSuggestions{ Names: []prompt.Suggest{ {Text: command.FullFlagName(FlagImage), Description: FlagImageUsage}, {Text: command.FullFlagName(FlagUseLastImageMetadata), Description: FlagUseLastImageMetadataUsage}, {Text: command.FullFlagName(FlagTag), Description: FlagTagUsage}, }, Values: map[string]command.CompleteValue{ command.FullFlagName(FlagUseLastImageMetadata): command.CompleteBool, }, } ================================================ FILE: pkg/app/master/command/merge/register.go ================================================ package merge import ( "github.com/slimtoolkit/slim/pkg/app/master/command" ) func RegisterCommand() { command.AddCLICommand( Name, CLI, CommandSuggestion, CommandFlagSuggestions) } ================================================ FILE: pkg/app/master/command/probe/cli.go ================================================ package probe import ( "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/urfave/cli/v2" ) //Standalone probing const ( Name = "probe" Usage = "Probe target endpoint" Alias = "prb" ) var CLI = &cli.Command{ Name: Name, Aliases: []string{Alias}, Usage: Usage, Flags: append([]cli.Flag{ cflag(FlagTarget), cflag(FlagPort), }, command.HTTPProbeFlagsBasic()...), Action: func(ctx *cli.Context) error { gparams, ok := command.CLIContextGet(ctx.Context, command.GlobalParams).(*command.GenericParams) if !ok || gparams == nil { return command.ErrNoGlobalParams } xc := app.NewExecutionContext( Name, gparams.QuietCLIMode, gparams.OutputFormat) targetEndpoint := ctx.String(FlagTarget) if targetEndpoint == "" { if ctx.Args().Len() < 1 { xc.Out.Error("param.target", "missing target") cli.ShowCommandHelp(ctx, Name) return nil } else { targetEndpoint = ctx.Args().First() } } httpProbeOpts := command.GetHTTPProbeOptions(xc, ctx, true) targetPorts := ctx.UintSlice(FlagPort) OnCommand( xc, gparams, targetEndpoint, targetPorts, httpProbeOpts) return nil }, } ================================================ FILE: pkg/app/master/command/probe/flags.go ================================================ package probe import ( log "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" ) // Probe command flag names and usage descriptions const ( FlagTarget = "target" FlagTargetUsage = "Target endpoint to probe" //for now just TCP ports (so no FlagProto for now) FlagPort = "port" FlagPortUsage = "Endpoint port to probe" ) var Flags = map[string]cli.Flag{ FlagTarget: &cli.StringFlag{ Name: FlagTarget, Value: "", Usage: FlagTargetUsage, EnvVars: []string{"DSLIM_PRB_TARGET"}, }, FlagPort: &cli.UintSliceFlag{ Name: FlagPort, Value: cli.NewUintSlice(), Usage: FlagPortUsage, EnvVars: []string{"DSLIM_PRB_PORT"}, }, } func cflag(name string) cli.Flag { cf, ok := Flags[name] if !ok { log.Fatalf("unknown flag='%s'", name) } return cf } ================================================ FILE: pkg/app/master/command/probe/handler.go ================================================ package probe import ( "fmt" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/app/master/config" "github.com/slimtoolkit/slim/pkg/app/master/probe/http" "github.com/slimtoolkit/slim/pkg/app/master/version" cmd "github.com/slimtoolkit/slim/pkg/command" "github.com/slimtoolkit/slim/pkg/docker/dockerclient" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/util/errutil" "github.com/slimtoolkit/slim/pkg/util/fsutil" v "github.com/slimtoolkit/slim/pkg/version" ) const appName = command.AppName type ovars = app.OutVars // OnCommand implements the 'probe' command func OnCommand( xc *app.ExecutionContext, gparams *command.GenericParams, targetEndpoint string, targetPorts []uint, httpProbeOpts config.HTTPProbeOptions) { printState := true logger := log.WithFields(log.Fields{"app": appName, "cmd": Name}) cmdName := fmt.Sprintf("cmd=%s", Name) viChan := version.CheckAsync(gparams.CheckVersion, gparams.InContainer, gparams.IsDSImage) cmdReport := report.NewProbeCommand(gparams.ReportLocation, gparams.InContainer) cmdReport.State = cmd.StateStarted xc.Out.State(cmd.StateStarted) xc.Out.Info("params", ovars{ "target": targetEndpoint, }) client, err := dockerclient.New(gparams.ClientConfig) if err == dockerclient.ErrNoDockerInfo { exitMsg := "missing Docker connection info" if gparams.InContainer && gparams.IsDSImage { exitMsg = "make sure to pass the Docker connect parameters to the slim app container" } xc.Out.Info("docker.connect.error", ovars{ "message": exitMsg, }) exitCode := -222 xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Exit(exitCode) } errutil.FailOn(err) if gparams.Debug { version.Print(xc, cmdName, logger, client, false, gparams.InContainer, gparams.IsDSImage) } probe, err := http.NewEndpointProbe(xc, targetEndpoint, targetPorts, httpProbeOpts, printState) xc.FailOn(err) probe.Start() xc.Out.Prompt("waiting for the HTTP probe to finish") <-probe.DoneChan() xc.Out.Info("event", ovars{ "message": "HTTP probe is done", }) if probe != nil && probe.CallCount > 0 && probe.OkCount == 0 { xc.Out.Error("probe.error", "no.successful.calls") } xc.Out.State(cmd.StateCompleted) cmdReport.State = cmd.StateCompleted xc.Out.State(cmd.StateDone) vinfo := <-viChan version.PrintCheckVersion(xc, "", vinfo) cmdReport.State = cmd.StateDone if cmdReport.Save() { xc.Out.Info("report", ovars{ "file": cmdReport.ReportLocation(), }) } } ================================================ FILE: pkg/app/master/command/probe/init/init.go ================================================ package init import ( "github.com/slimtoolkit/slim/pkg/app/master/command/probe" ) func init() { probe.RegisterCommand() } ================================================ FILE: pkg/app/master/command/probe/prompt.go ================================================ package probe import ( "github.com/c-bata/go-prompt" "github.com/slimtoolkit/slim/pkg/app/master/command" ) var CommandSuggestion = prompt.Suggest{ Text: Name, Description: Usage, } var CommandFlagSuggestions = &command.FlagSuggestions{ Names: []prompt.Suggest{ {Text: command.FullFlagName(FlagTarget), Description: FlagTargetUsage}, {Text: command.FullFlagName(FlagPort), Description: FlagPortUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeCmd), Description: command.FlagHTTPProbeCmdUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeCmdFile), Description: command.FlagHTTPProbeCmdFileUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeStartWait), Description: command.FlagHTTPProbeStartWaitUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeRetryCount), Description: command.FlagHTTPProbeRetryCountUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeRetryWait), Description: command.FlagHTTPProbeRetryWaitUsage}, {Text: command.FullFlagName(command.FlagHTTPProbePorts), Description: command.FlagHTTPProbePortsUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeFull), Description: command.FlagHTTPProbeFullUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeExitOnFailure), Description: command.FlagHTTPProbeExitOnFailureUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeCrawl), Description: command.FlagHTTPProbeCrawlUsage}, {Text: command.FullFlagName(command.FlagHTTPCrawlMaxDepth), Description: command.FlagHTTPCrawlMaxDepthUsage}, {Text: command.FullFlagName(command.FlagHTTPCrawlMaxPageCount), Description: command.FlagHTTPCrawlMaxPageCountUsage}, {Text: command.FullFlagName(command.FlagHTTPCrawlConcurrency), Description: command.FlagHTTPCrawlConcurrencyUsage}, {Text: command.FullFlagName(command.FlagHTTPMaxConcurrentCrawlers), Description: command.FlagHTTPMaxConcurrentCrawlersUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeAPISpec), Description: command.FlagHTTPProbeAPISpecUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeAPISpecFile), Description: command.FlagHTTPProbeAPISpecFileUsage}, }, Values: map[string]command.CompleteValue{ command.FullFlagName(command.FlagHTTPProbeCmdFile): command.CompleteFile, command.FullFlagName(command.FlagHTTPProbeFull): command.CompleteBool, command.FullFlagName(command.FlagHTTPProbeCrawl): command.CompleteTBool, command.FullFlagName(command.FlagHTTPProbeAPISpecFile): command.CompleteFile, }, } ================================================ FILE: pkg/app/master/command/probe/register.go ================================================ package probe import ( "github.com/slimtoolkit/slim/pkg/app/master/command" ) func RegisterCommand() { command.AddCLICommand( Name, CLI, CommandSuggestion, CommandFlagSuggestions) } ================================================ FILE: pkg/app/master/command/profile/cli.go ================================================ package profile import ( "fmt" "strings" "github.com/urfave/cli/v2" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/app/master/config" ) const ( Name = "profile" Usage = "Collects fat image information and generates a fat container report" Alias = "p" ) var CLI = &cli.Command{ Name: Name, Aliases: []string{Alias}, Usage: Usage, Flags: append([]cli.Flag{ command.Cflag(command.FlagTarget), command.Cflag(command.FlagPull), command.Cflag(command.FlagDockerConfigPath), command.Cflag(command.FlagRegistryAccount), command.Cflag(command.FlagRegistrySecret), command.Cflag(command.FlagShowPullLogs), //Compose support command.Cflag(command.FlagComposeFile), //not used yet command.Cflag(command.FlagTargetComposeSvc), //not used yet command.Cflag(command.FlagComposeSvcStartWait), //not used yet command.Cflag(command.FlagTargetComposeSvcImage), //not used yet command.Cflag(command.FlagComposeSvcNoPorts), //not used yet command.Cflag(command.FlagDepExcludeComposeSvcAll), //not used yet command.Cflag(command.FlagDepIncludeComposeSvc), //not used yet command.Cflag(command.FlagDepExcludeComposeSvc), //not used yet command.Cflag(command.FlagDepIncludeComposeSvcDeps), //not used yet command.Cflag(command.FlagDepIncludeTargetComposeSvcDeps), //not used yet command.Cflag(command.FlagComposeNet), //not used yet command.Cflag(command.FlagComposeEnvNoHost), //not used yet command.Cflag(command.FlagComposeEnvFile), //not used yet command.Cflag(command.FlagComposeProjectName), //not used yet command.Cflag(command.FlagComposeWorkdir), //not used yet command.Cflag(command.FlagPublishPort), command.Cflag(command.FlagPublishExposedPorts), command.Cflag(command.FlagHostExec), command.Cflag(command.FlagHostExecFile), //command.Cflag(command.FlagKeepPerms), command.Cflag(command.FlagRunTargetAsUser), command.Cflag(command.FlagShowContainerLogs), command.Cflag(command.FlagEnableMondelLogs), command.Cflag(command.FlagCopyMetaArtifacts), command.Cflag(command.FlagRemoveFileArtifacts), command.Cflag(command.FlagExec), command.Cflag(command.FlagExecFile), //Container Run Options command.Cflag(command.FlagCRORuntime), command.Cflag(command.FlagCROHostConfigFile), command.Cflag(command.FlagCROSysctl), command.Cflag(command.FlagCROShmSize), command.Cflag(command.FlagUser), command.Cflag(command.FlagEntrypoint), command.Cflag(command.FlagCmd), command.Cflag(command.FlagWorkdir), command.Cflag(command.FlagEnv), command.Cflag(command.FlagEnvFile), command.Cflag(command.FlagLabel), command.Cflag(command.FlagVolume), command.Cflag(command.FlagLink), command.Cflag(command.FlagEtcHostsMap), command.Cflag(command.FlagContainerDNS), command.Cflag(command.FlagContainerDNSSearch), command.Cflag(command.FlagNetwork), command.Cflag(command.FlagHostname), command.Cflag(command.FlagExpose), //command.Cflag(command.FlagExcludeMounts), //command.Cflag(command.FlagExcludePattern), //should remove too (no need) command.Cflag(command.FlagMount), command.Cflag(command.FlagContinueAfter), command.Cflag(command.FlagUseLocalMounts), command.Cflag(command.FlagUseSensorVolume), //Sensor flags: command.Cflag(command.FlagSensorIPCEndpoint), command.Cflag(command.FlagSensorIPCMode), }, command.HTTPProbeFlags()...), Action: func(ctx *cli.Context) error { gcvalues := command.GlobalFlagValues(ctx) xc := app.NewExecutionContext( Name, gcvalues.QuietCLIMode, gcvalues.OutputFormat) targetRef := ctx.String(command.FlagTarget) if targetRef == "" { if ctx.Args().Len() < 1 { xc.Out.Error("param.target", "missing target image ID/name") cli.ShowCommandHelp(ctx, Name) return nil } else { targetRef = ctx.Args().First() } } crOpts, err := command.GetContainerRunOptions(ctx) if err != nil { xc.Out.Error("param.error.container.run.options", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } doPull := ctx.Bool(command.FlagPull) dockerConfigPath := ctx.String(command.FlagDockerConfigPath) registryAccount := ctx.String(command.FlagRegistryAccount) registrySecret := ctx.String(command.FlagRegistrySecret) doShowPullLogs := ctx.Bool(command.FlagShowPullLogs) doRmFileArtifacts := ctx.Bool(command.FlagRemoveFileArtifacts) doCopyMetaArtifacts := ctx.String(command.FlagCopyMetaArtifacts) portBindings, err := command.ParsePortBindings(ctx.StringSlice(command.FlagPublishPort)) if err != nil { xc.Out.Error("param.publish.port", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } doPublishExposedPorts := ctx.Bool(command.FlagPublishExposedPorts) httpProbeOpts := command.GetHTTPProbeOptions(xc, ctx, false) continueAfter, err := command.GetContinueAfter(ctx) if err != nil { xc.Out.Error("param.error.continue.after", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } if !httpProbeOpts.Do && continueAfter.Mode == "probe" { continueAfter.Mode = "enter" xc.Out.Info("enter", ovars{ "message": "changing continue-after from probe to enter because http-probe is disabled", }) } hostExecProbes := ctx.StringSlice(command.FlagHostExec) moreHostExecProbes, err := command.ParseHTTPProbeExecFile(ctx.String(command.FlagHostExecFile)) if err != nil { xc.Out.Error("param.host.exec.file", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } if len(moreHostExecProbes) > 0 { hostExecProbes = append(hostExecProbes, moreHostExecProbes...) } if strings.Contains(continueAfter.Mode, config.CAMHostExec) && len(hostExecProbes) == 0 { if continueAfter.Mode == config.CAMHostExec { continueAfter.Mode = config.CAMEnter xc.Out.Info("host-exec", ovars{ "message": "changing continue-after from host-exec to enter because there are no host-exec commands", }) } else { continueAfter.Mode = command.RemoveContinueAfterMode(continueAfter.Mode, config.CAMHostExec) xc.Out.Info("host-exec", ovars{ "message": "removing host-exec continue-after mode because there are no host-exec commands", }) } } if len(hostExecProbes) > 0 { if !strings.Contains(continueAfter.Mode, config.CAMHostExec) { if continueAfter.Mode == "" { continueAfter.Mode = config.CAMHostExec } else { continueAfter.Mode = fmt.Sprintf("%s&%s", continueAfter.Mode, config.CAMHostExec) } xc.Out.Info("exec", ovars{ "message": fmt.Sprintf("updating continue-after mode to %s", continueAfter.Mode), }) } } //doKeepPerms := ctx.Bool(command.FlagKeepPerms) doRunTargetAsUser := ctx.Bool(command.FlagRunTargetAsUser) doShowContainerLogs := ctx.Bool(command.FlagShowContainerLogs) doEnableMondel := ctx.Bool(command.FlagEnableMondelLogs) overrides, err := command.GetContainerOverrides(xc, ctx) if err != nil { xc.Out.Error("param.error.container.overrides", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } volumeMounts, err := command.ParseVolumeMounts(ctx.StringSlice(command.FlagMount)) if err != nil { xc.Out.Error("param.error.mount", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } //excludePatterns := command.ParsePaths(ctx.StringSlice(command.FlagExcludePattern)) //includePaths := command.ParsePaths(ctx.StringSlice(command.FlagIncludePath)) //moreIncludePaths, err := command.ParsePathsFile(ctx.String(command.FlagIncludePathFile)) //if err != nil { // xc.Out.Error("param.error.include.path.file", err.Error()) // xc.Out.State("exited", // ovars{ // "exit.code": -1, // }) // xc.Exit(-1) //} else { // for k, v := range moreIncludePaths { // includePaths[k] = v // } //} //pathPerms := command.ParsePaths(ctx.StringSlice(command.FlagPathPerms)) //morePathPerms, err := command.ParsePathsFile(ctx.String(command.FlagPathPermsFile)) //if err != nil { // xc.Out.Error("param.error.path.perms.file", err.Error()) // xc.Out.State("exited", // ovars{ // "exit.code": -1, // }) // xc.Exit(-1) //} else { // for k, v := range morePathPerms { // pathPerms[k] = v // } //} //includeBins := command.ParsePaths(ctx.StringSlice(command.FlagIncludeBin)) //includeExes := command.ParsePaths(ctx.StringSlice(command.FlagIncludeExe)) //doIncludeShell := ctx.Bool(command.FlagIncludeShell) doUseLocalMounts := ctx.Bool(command.FlagUseLocalMounts) doUseSensorVolume := ctx.String(command.FlagUseSensorVolume) //doKeepTmpArtifacts := ctx.Bool(command.FlagKeepTmpArtifacts) //doExcludeMounts := ctx.Bool(command.FlagExcludeMounts) //if doExcludeMounts { // for mpath := range volumeMounts { // excludePatterns[mpath] = nil // mpattern := fmt.Sprintf("%s/**", mpath) // excludePatterns[mpattern] = nil // } //} commandReport := ctx.String(command.FlagCommandReport) if commandReport == "off" { commandReport = "" } OnCommand( xc, gcvalues, targetRef, doPull, dockerConfigPath, registryAccount, registrySecret, doShowPullLogs, crOpts, httpProbeOpts, portBindings, doPublishExposedPorts, hostExecProbes, doRmFileArtifacts, doCopyMetaArtifacts, doRunTargetAsUser, doShowContainerLogs, doEnableMondel, overrides, ctx.StringSlice(command.FlagLink), ctx.StringSlice(command.FlagEtcHostsMap), ctx.StringSlice(command.FlagContainerDNS), ctx.StringSlice(command.FlagContainerDNSSearch), volumeMounts, //doKeepPerms, //pathPerms, //excludePatterns, //includePaths, //includeBins, //includeExes, //doIncludeShell, doUseLocalMounts, doUseSensorVolume, //doKeepTmpArtifacts, continueAfter, ctx.String(command.FlagSensorIPCEndpoint), ctx.String(command.FlagSensorIPCMode), ctx.String(command.FlagLogLevel), ctx.String(command.FlagLogFormat)) return nil }, } ================================================ FILE: pkg/app/master/command/profile/handler.go ================================================ package profile import ( "bufio" "fmt" "os" "runtime" "time" "github.com/dustin/go-humanize" docker "github.com/fsouza/go-dockerclient" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/app/master/config" "github.com/slimtoolkit/slim/pkg/app/master/inspectors/container" "github.com/slimtoolkit/slim/pkg/app/master/inspectors/image" "github.com/slimtoolkit/slim/pkg/app/master/probe/http" "github.com/slimtoolkit/slim/pkg/app/master/version" cmd "github.com/slimtoolkit/slim/pkg/command" "github.com/slimtoolkit/slim/pkg/docker/dockerclient" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/util/errutil" "github.com/slimtoolkit/slim/pkg/util/fsutil" v "github.com/slimtoolkit/slim/pkg/version" ) const appName = command.AppName // Profile command exit codes const ( ecpOther = iota + 1 ecpNoEntrypoint ecpImageNotFound ) type ovars = app.OutVars //note: the runtime part of the 'profile' logic is a bit behind 'build' //todo: refactor 'xray', 'profile' and 'build' to compose and reuse common logic // OnCommand implements the 'profile' command func OnCommand( xc *app.ExecutionContext, gparams *command.GenericParams, targetRef string, doPull bool, dockerConfigPath string, registryAccount string, registrySecret string, doShowPullLogs bool, crOpts *config.ContainerRunOptions, httpProbeOpts config.HTTPProbeOptions, portBindings map[docker.Port][]docker.PortBinding, doPublishExposedPorts bool, hostExecProbes []string, doRmFileArtifacts bool, copyMetaArtifactsLocation string, doRunTargetAsUser bool, doShowContainerLogs bool, doEnableMondel bool, overrides *config.ContainerOverrides, links []string, etcHostsMaps []string, dnsServers []string, dnsSearchDomains []string, explicitVolumeMounts map[string]config.VolumeMount, //doKeepPerms bool, //pathPerms map[string]*fsutil.AccessInfo, //excludePatterns map[string]*fsutil.AccessInfo, //includePaths map[string]*fsutil.AccessInfo, //includeBins map[string]*fsutil.AccessInfo, //includeExes map[string]*fsutil.AccessInfo, //doIncludeShell bool, doUseLocalMounts bool, doUseSensorVolume string, //doKeepTmpArtifacts bool, continueAfter *config.ContinueAfter, sensorIPCEndpoint string, sensorIPCMode string, logLevel string, logFormat string) { printState := true logger := log.WithFields(log.Fields{"app": appName, "cmd": Name}) viChan := version.CheckAsync(gparams.CheckVersion, gparams.InContainer, gparams.IsDSImage) cmdReport := report.NewProfileCommand(gparams.ReportLocation, gparams.InContainer) cmdReport.State = cmd.StateStarted cmdReport.OriginalImage = targetRef xc.Out.State(cmd.StateStarted) xc.Out.Info("params", ovars{ "target": targetRef, }) client, err := dockerclient.New(gparams.ClientConfig) if err == dockerclient.ErrNoDockerInfo { exitMsg := "missing Docker connection info" if gparams.InContainer && gparams.IsDSImage { exitMsg = "make sure to pass the Docker connect parameters to the 'slim' container" } xc.Out.Info("docker.connect.error", ovars{ "message": exitMsg, }) exitCode := command.ECTCommon | command.ECCNoDockerConnectInfo xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Exit(exitCode) } errutil.FailOn(err) if gparams.Debug { version.Print(xc, Name, logger, client, false, gparams.InContainer, gparams.IsDSImage) } if overrides.Network == "host" && runtime.GOOS == "darwin" { xc.Out.Info("param.error", ovars{ "status": "unsupported.network.mac", "value": overrides.Network, }) exitCode := command.ECTCommon | command.ECCBadNetworkName xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Exit(exitCode) } if !command.ConfirmNetwork(logger, client, overrides.Network) { xc.Out.Info("param.error", ovars{ "status": "unknown.network", "value": overrides.Network, }) exitCode := command.ECTCommon | command.ECCBadNetworkName xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Exit(exitCode) } imageInspector, err := image.NewInspector(client, targetRef) errutil.FailOn(err) noImage, err := imageInspector.NoImage() errutil.FailOn(err) if noImage { if doPull { xc.Out.Info("target.image", ovars{ "status": "image.not.found", "image": targetRef, "message": "trying to pull target image", }) err := imageInspector.Pull(doShowPullLogs, dockerConfigPath, registryAccount, registrySecret) errutil.FailOn(err) } else { xc.Out.Info("target.image.error", ovars{ "status": "image.not.found", "image": targetRef, "message": "make sure the target image already exists locally", }) exitCode := command.ECTCommon | ecpImageNotFound xc.Out.State("exited", ovars{"exit.code": exitCode}) xc.Exit(exitCode) } } //refresh the target refs targetRef = imageInspector.ImageRef xc.Out.State("image.inspection.start") logger.Info("inspecting 'fat' image metadata...") err = imageInspector.Inspect() errutil.FailOn(err) localVolumePath, artifactLocation, statePath, stateKey := fsutil.PrepareImageStateDirs(gparams.StatePath, imageInspector.ImageInfo.ID) imageInspector.ArtifactLocation = artifactLocation logger.Debugf("localVolumePath=%v, artifactLocation=%v, statePath=%v, stateKey=%v", localVolumePath, artifactLocation, statePath, stateKey) xc.Out.Info("image", ovars{ "id": imageInspector.ImageInfo.ID, "size.bytes": imageInspector.ImageInfo.VirtualSize, "size.human": humanize.Bytes(uint64(imageInspector.ImageInfo.VirtualSize)), "architecture": imageInspector.ImageInfo.Architecture, }) logger.Info("processing 'fat' image info...") err = imageInspector.ProcessCollectedData() errutil.FailOn(err) xc.Out.State("image.inspection.done") xc.Out.State("container.inspection.start") //note: //not pre-processing links for 'profile' yet //need to copy the logic from 'build' //(better yet refactor to share code) hasClassicLinks := true //placeholder for now containerInspector, err := container.NewInspector( xc, crOpts, logger, client, statePath, imageInspector, localVolumePath, doUseLocalMounts, doUseSensorVolume, false, //doKeepTmpArtifacts, overrides, explicitVolumeMounts, nil, //baseMounts, nil, //baseVolumesFrom, portBindings, doPublishExposedPorts, hasClassicLinks, links, etcHostsMaps, dnsServers, dnsSearchDomains, doShowContainerLogs, doEnableMondel, doRunTargetAsUser, false, //doKeepPerms, nil, //pathPerms, nil, //excludePatterns, false, //doExcludeVarLockFiles nil, //preservePaths, nil, //includePaths, nil, //includeBins, nil, //includeDirBinsList, nil, //includeExes, false, //doIncludeShell, false, //doIncludeWorkdir, false, //doIncludeCertAll false, //doIncludeCertBundles false, //doIncludeCertDirs false, //doIncludeCertPKAll false, //doIncludeCertPKDirs false, //doIncludeNew false, //doIncludeSSHClient false, //doIncludeOSLibsNet false, //doIncludeZoneInfo nil, //selectedNetNames gparams.Debug, logLevel, logFormat, gparams.InContainer, true, //rtaSourcePT false, //doObfuscateMetadata sensorIPCEndpoint, sensorIPCMode, printState, config.AppNodejsInspectOptions{}) errutil.FailOn(err) if len(containerInspector.FatContainerCmd) == 0 { xc.Out.Info("target.image.error", ovars{ "status": "no.entrypoint.cmd", "image": targetRef, "message": "no ENTRYPOINT/CMD", }) exitCode := command.ECTBuild | ecpNoEntrypoint xc.Out.State("exited", ovars{"exit.code": exitCode}) xc.Exit(exitCode) } logger.Info("starting instrumented 'fat' container...") err = containerInspector.RunContainer() if err != nil { containerInspector.ShowContainerLogs() containerInspector.ShutdownContainer(true) xc.FailOn(err) } xc.Out.Info("container", ovars{ "name": containerInspector.ContainerName, "id": containerInspector.ContainerID, "target.port.list": containerInspector.ContainerPortList, "target.port.info": containerInspector.ContainerPortsInfo, "message": "YOU CAN USE THESE PORTS TO INTERACT WITH THE CONTAINER", }) logger.Info("watching container monitor...") if config.CAMProbe == continueAfter.Mode { httpProbeOpts.Do = true } var probe *http.CustomProbe if httpProbeOpts.Do { var err error probe, err = http.NewContainerProbe(xc, containerInspector, httpProbeOpts, printState) errutil.FailOn(err) if len(probe.Ports()) == 0 { xc.Out.State("http.probe.error", ovars{ "error": "NO EXPOSED PORTS", "message": "expose your service port with --expose or disable HTTP probing with --http-probe=false if your containerized application doesnt expose any network services", }) logger.Info("shutting down 'fat' container...") containerInspector.FinishMonitoring() _ = containerInspector.ShutdownContainer(false) xc.Out.State("exited", ovars{"exit.code": -1}) xc.Exit(-1) } probe.Start() continueAfter.ContinueChan = probe.DoneChan() } continueAfterMsg := "provide the expected input to allow the container inspector to continue its execution" switch continueAfter.Mode { case config.CAMTimeout: continueAfterMsg = "no input required, execution will resume after the timeout" case config.CAMProbe: continueAfterMsg = "no input required, execution will resume when HTTP probing is completed" } xc.Out.Info("continue.after", ovars{ "mode": continueAfter.Mode, "message": continueAfterMsg, }) execFail := false modes := command.GetContinueAfterModeNames(continueAfter.Mode) for _, mode := range modes { switch mode { //case config.CAMContainerProbe: /* case config.CAMExec: var input *bytes.Buffer var cmd []string if len(execFileCmd) != 0 { input = bytes.NewBufferString(execFileCmd) cmd = []string{"sh", "-s"} for _, line := range strings.Split(string(execFileCmd), "\n") { xc.Out.Info("continue.after", ovars{ "mode": config.CAMExec, "shell": line, }) } } else { input = bytes.NewBufferString("") cmd = []string{"sh", "-c", execCmd} xc.Out.Info("continue.after", ovars{ "mode": config.CAMExec, "shell": execCmd, }) } exec, err := containerInspector.APIClient.CreateExec(dockerapi.CreateExecOptions{ Container: containerInspector.ContainerID, Cmd: cmd, AttachStdin: true, AttachStdout: true, AttachStderr: true, }) xc.FailOn(err) buffer := &printbuffer.PrintBuffer{Prefix: fmt.Sprintf("%s[%s][exec]: output:", appName, Name)} xc.FailOn(containerInspector.APIClient.StartExec(exec.ID, dockerapi.StartExecOptions{ InputStream: input, OutputStream: buffer, ErrorStream: buffer, })) inspect, err := containerInspector.APIClient.InspectExec(exec.ID) xc.FailOn(err) errutil.FailWhen(inspect.Running, "still running") if inspect.ExitCode != 0 { execFail = true } xc.Out.Info("continue.after", ovars{ "mode": config.CAMExec, "exitcode": inspect.ExitCode, }) */ case config.CAMEnter: xc.Out.Prompt("USER INPUT REQUIRED, PRESS WHEN YOU ARE DONE USING THE CONTAINER") creader := bufio.NewReader(os.Stdin) _, _, _ = creader.ReadLine() case config.CAMSignal: xc.Out.Prompt("send SIGUSR1 when you are done using the container") <-continueAfter.ContinueChan xc.Out.Info("event", ovars{ "message": "got SIGUSR1", }) case config.CAMTimeout: xc.Out.Prompt(fmt.Sprintf("waiting for the target container (%v seconds)", int(continueAfter.Timeout))) <-time.After(time.Second * continueAfter.Timeout) xc.Out.Info("event", ovars{ "message": "done waiting for the target container", }) case config.CAMProbe: xc.Out.Prompt("waiting for the HTTP probe to finish") <-continueAfter.ContinueChan xc.Out.Info("event", ovars{ "message": "HTTP probe is done", }) if probe != nil && probe.CallCount > 0 && probe.OkCount == 0 && httpProbeOpts.ExitOnFailure { xc.Out.Error("probe.error", "no.successful.calls") containerInspector.ShowContainerLogs() xc.Out.State("exited", ovars{"exit.code": -1}) xc.Exit(-1) } case config.CAMHostExec: command.RunHostExecProbes(printState, xc, hostExecProbes) case config.CAMAppExit: xc.Out.Prompt("waiting for the target app to exit") //TBD default: errutil.Fail("unknown continue-after mode") } } xc.Out.State("container.inspection.finishing") containerInspector.FinishMonitoring() logger.Info("shutting down 'fat' container...") err = containerInspector.ShutdownContainer(false) errutil.WarnOn(err) if execFail { xc.Out.Info("continue.after", ovars{ "mode": config.CAMExec, "message": "fatal: exec cmd failure", }) exitCode := 1 xc.Out.State("exited", ovars{ "exit.code": exitCode, }) cmdReport.Error = "exec.cmd.failure" xc.Exit(exitCode) } xc.Out.State("container.inspection.artifact.processing") if !containerInspector.HasCollectedData() { imageInspector.ShowFatImageDockerInstructions() xc.Out.Info("results", ovars{ "status": "no data collected (no minified image generated)", "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Out.State("exited", ovars{"exit.code": -1}) xc.Exit(-1) } logger.Info("processing instrumented 'fat' container info...") err = containerInspector.ProcessCollectedData() errutil.FailOn(err) xc.Out.State("container.inspection.done") xc.Out.State(cmd.StateCompleted) cmdReport.State = cmd.StateCompleted cmdReport.SeccompProfileName = imageInspector.SeccompProfileName cmdReport.AppArmorProfileName = imageInspector.AppArmorProfileName xc.Out.Info("results", ovars{ "artifacts.seccomp": cmdReport.SeccompProfileName, }) xc.Out.Info("results", ovars{ "artifacts.apparmor": cmdReport.AppArmorProfileName, }) if copyMetaArtifactsLocation != "" { toCopy := []string{ report.DefaultContainerReportFileName, imageInspector.SeccompProfileName, imageInspector.AppArmorProfileName, } if !command.CopyMetaArtifacts(logger, toCopy, artifactLocation, copyMetaArtifactsLocation) { xc.Out.Info("artifacts", ovars{ "message": "could not copy meta artifacts", }) } } if err := command.DoArchiveState(logger, client, artifactLocation, gparams.ArchiveState, stateKey); err != nil { xc.Out.Info("state", ovars{ "message": "could not archive state", }) logger.Errorf("error archiving state - %v", err) } if doRmFileArtifacts { logger.Info("removing temporary artifacts...") err = fsutil.Remove(artifactLocation) errutil.WarnOn(err) } xc.Out.State(cmd.StateDone) vinfo := <-viChan version.PrintCheckVersion(xc, "", vinfo) cmdReport.State = cmd.StateDone if cmdReport.Save() { xc.Out.Info("report", ovars{ "file": cmdReport.ReportLocation(), }) } } ================================================ FILE: pkg/app/master/command/profile/init/init.go ================================================ package init import ( "github.com/slimtoolkit/slim/pkg/app/master/command/profile" ) func init() { profile.RegisterCommand() } ================================================ FILE: pkg/app/master/command/profile/prompt.go ================================================ package profile import ( "github.com/c-bata/go-prompt" "github.com/slimtoolkit/slim/pkg/app/master/command" ) var CommandSuggestion = prompt.Suggest{ Text: Name, Description: Usage, } var CommandFlagSuggestions = &command.FlagSuggestions{ Names: []prompt.Suggest{ {Text: command.FullFlagName(command.FlagTarget), Description: command.FlagTargetUsage}, {Text: command.FullFlagName(command.FlagPull), Description: command.FlagPullUsage}, {Text: command.FullFlagName(command.FlagShowPullLogs), Description: command.FlagShowPullLogsUsage}, {Text: command.FullFlagName(command.FlagShowContainerLogs), Description: command.FlagShowContainerLogsUsage}, {Text: command.FullFlagName(command.FlagEnableMondelLogs), Description: command.FlagEnableMondelLogsUsage}, {Text: command.FullFlagName(command.FlagCRORuntime), Description: command.FlagCRORuntimeUsage}, {Text: command.FullFlagName(command.FlagCROHostConfigFile), Description: command.FlagCROHostConfigFileUsage}, {Text: command.FullFlagName(command.FlagCROSysctl), Description: command.FlagCROSysctlUsage}, {Text: command.FullFlagName(command.FlagCROShmSize), Description: command.FlagCROShmSizeUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeOff), Description: command.FlagHTTPProbeOffUsage}, {Text: command.FullFlagName(command.FlagHTTPProbe), Description: command.FlagHTTPProbeUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeCmd), Description: command.FlagHTTPProbeCmdUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeCmdFile), Description: command.FlagHTTPProbeCmdFileUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeStartWait), Description: command.FlagHTTPProbeStartWaitUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeRetryCount), Description: command.FlagHTTPProbeRetryCountUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeRetryWait), Description: command.FlagHTTPProbeRetryWaitUsage}, {Text: command.FullFlagName(command.FlagHTTPProbePorts), Description: command.FlagHTTPProbePortsUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeFull), Description: command.FlagHTTPProbeFullUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeExitOnFailure), Description: command.FlagHTTPProbeExitOnFailureUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeCrawl), Description: command.FlagHTTPProbeCrawlUsage}, {Text: command.FullFlagName(command.FlagHTTPCrawlMaxDepth), Description: command.FlagHTTPCrawlMaxDepthUsage}, {Text: command.FullFlagName(command.FlagHTTPCrawlMaxPageCount), Description: command.FlagHTTPCrawlMaxPageCountUsage}, {Text: command.FullFlagName(command.FlagHTTPCrawlConcurrency), Description: command.FlagHTTPCrawlConcurrencyUsage}, {Text: command.FullFlagName(command.FlagHTTPMaxConcurrentCrawlers), Description: command.FlagHTTPMaxConcurrentCrawlersUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeAPISpec), Description: command.FlagHTTPProbeAPISpecUsage}, {Text: command.FullFlagName(command.FlagHTTPProbeAPISpecFile), Description: command.FlagHTTPProbeAPISpecFileUsage}, {Text: command.FullFlagName(command.FlagPublishPort), Description: command.FlagPublishPortUsage}, {Text: command.FullFlagName(command.FlagPublishExposedPorts), Description: command.FlagPublishExposedPortsUsage}, {Text: command.FullFlagName(command.FlagHostExec), Description: command.FlagHostExecUsage}, {Text: command.FullFlagName(command.FlagHostExecFile), Description: command.FlagHostExecFileUsage}, //{Text: command.FullFlagName(command.FlagKeepPerms), Description: command.FlagKeepPermsUsage}, {Text: command.FullFlagName(command.FlagRunTargetAsUser), Description: command.FlagRunTargetAsUserUsage}, {Text: command.FullFlagName(command.FlagCopyMetaArtifacts), Description: command.FlagCopyMetaArtifactsUsage}, {Text: command.FullFlagName(command.FlagRemoveFileArtifacts), Description: command.FlagRemoveFileArtifactsUsage}, {Text: command.FullFlagName(command.FlagUser), Description: command.FlagUserUsage}, {Text: command.FullFlagName(command.FlagEntrypoint), Description: command.FlagEntrypointUsage}, {Text: command.FullFlagName(command.FlagCmd), Description: command.FlagCmdUsage}, {Text: command.FullFlagName(command.FlagWorkdir), Description: command.FlagWorkdirUsage}, {Text: command.FullFlagName(command.FlagEnv), Description: command.FlagEnvUsage}, {Text: command.FullFlagName(command.FlagEnvFile), Description: command.FlagEnvFileUsage}, {Text: command.FullFlagName(command.FlagLabel), Description: command.FlagLabelUsage}, {Text: command.FullFlagName(command.FlagVolume), Description: command.FlagVolumeUsage}, {Text: command.FullFlagName(command.FlagLink), Description: command.FlagLinkUsage}, {Text: command.FullFlagName(command.FlagEtcHostsMap), Description: command.FlagEtcHostsMapUsage}, {Text: command.FullFlagName(command.FlagContainerDNS), Description: command.FlagContainerDNSUsage}, {Text: command.FullFlagName(command.FlagContainerDNSSearch), Description: command.FlagContainerDNSSearchUsage}, {Text: command.FullFlagName(command.FlagNetwork), Description: command.FlagNetworkUsage}, {Text: command.FullFlagName(command.FlagHostname), Description: command.FlagHostnameUsage}, {Text: command.FullFlagName(command.FlagExpose), Description: command.FlagExposeUsage}, //{Text: command.FullFlagName(command.FlagExcludeMounts), Description: command.FlagExcludeMountsUsage}, //{Text: command.FullFlagName(command.FlagExcludePattern), Description: command.FlagExcludePatternUsage}, {Text: command.FullFlagName(command.FlagMount), Description: command.FlagMountUsage}, {Text: command.FullFlagName(command.FlagContinueAfter), Description: command.FlagContinueAfterUsage}, {Text: command.FullFlagName(command.FlagUseLocalMounts), Description: command.FlagUseLocalMountsUsage}, {Text: command.FullFlagName(command.FlagUseSensorVolume), Description: command.FlagUseSensorVolumeUsage}, {Text: command.FullFlagName(command.FlagSensorIPCMode), Description: command.FlagSensorIPCModeUsage}, {Text: command.FullFlagName(command.FlagSensorIPCEndpoint), Description: command.FlagSensorIPCEndpointUsage}, }, Values: map[string]command.CompleteValue{ command.FullFlagName(command.FlagPull): command.CompleteTBool, command.FullFlagName(command.FlagShowPullLogs): command.CompleteBool, command.FullFlagName(command.FlagTarget): command.CompleteImage, command.FullFlagName(command.FlagShowContainerLogs): command.CompleteBool, command.FullFlagName(command.FlagEnableMondelLogs): command.CompleteBool, command.FullFlagName(command.FlagPublishExposedPorts): command.CompleteBool, command.FullFlagName(command.FlagHTTPProbeOff): command.CompleteBool, command.FullFlagName(command.FlagHTTPProbe): command.CompleteTBool, command.FullFlagName(command.FlagHTTPProbeCmdFile): command.CompleteFile, command.FullFlagName(command.FlagHTTPProbeFull): command.CompleteBool, command.FullFlagName(command.FlagHTTPProbeExitOnFailure): command.CompleteTBool, command.FullFlagName(command.FlagHTTPProbeCrawl): command.CompleteTBool, command.FullFlagName(command.FlagHTTPProbeAPISpecFile): command.CompleteFile, command.FullFlagName(command.FlagHostExecFile): command.CompleteFile, //command.FullFlagName(command.FlagKeepPerms): command.CompleteTBool, command.FullFlagName(command.FlagRunTargetAsUser): command.CompleteTBool, command.FullFlagName(command.FlagRemoveFileArtifacts): command.CompleteBool, command.FullFlagName(command.FlagNetwork): command.CompleteNetwork, //command.FullFlagName(command.FlagExcludeMounts): command.CompleteTBool, //command.FullFlagName(command.FlagPathPermsFile): command.CompleteFile, //command.FullFlagName(command.FlagIncludePathFile): command.CompleteFile, //command.FullFlagName(command.FlagIncludeShell): command.CompleteBool, command.FullFlagName(command.FlagContinueAfter): command.CompleteContinueAfter, //command.FullFlagName(command.FlagConsoleOutput): command.CompleteConsoleOutput, command.FullFlagName(command.FlagUseLocalMounts): command.CompleteBool, command.FullFlagName(command.FlagUseSensorVolume): command.CompleteVolume, //command.FullFlagName(command.FlagKeepTmpArtifacts): command.CompleteBool, command.FullFlagName(command.FlagCROHostConfigFile): command.CompleteFile, command.FullFlagName(command.FlagSensorIPCMode): command.CompleteIPCMode, }, } ================================================ FILE: pkg/app/master/command/profile/register.go ================================================ package profile import ( "github.com/slimtoolkit/slim/pkg/app/master/command" ) func RegisterCommand() { command.AddCLICommand( Name, CLI, CommandSuggestion, CommandFlagSuggestions) } ================================================ FILE: pkg/app/master/command/registry/auth.go ================================================ package registry import ( "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/v1/remote" //log "github.com/sirupsen/logrus" ) func ConfigureAuth(cparams *CommonCommandParams, remoteOpts []remote.Option) ([]remote.Option, error) { if cparams.UseDockerCreds { remoteOpts = append(remoteOpts, remote.WithAuthFromKeychain(authn.DefaultKeychain)) return remoteOpts, nil } if cparams.CredsAccount != "" && cparams.CredsSecret != "" { remoteOpts = append(remoteOpts, remote.WithAuth(&authn.Basic{ Username: cparams.CredsAccount, Password: cparams.CredsSecret, })) return remoteOpts, nil } //it's authn.Anonymous by default, but good to be explicit return append(remoteOpts, remote.WithAuth(authn.Anonymous)), nil } ================================================ FILE: pkg/app/master/command/registry/cli.go ================================================ package registry import ( "fmt" "github.com/urfave/cli/v2" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" ) const ( Name = "registry" Usage = "Execute registry operations" Alias = "reg" PullCmdName = "pull" PullCmdNameUsage = "Pull a container image from registry" PushCmdName = "push" PushCmdNameUsage = "Push a container image to a registry" CopyCmdName = "copy" CopyCmdNameUsage = "Copy a container image from one registry to another" ImageIndexCreateCmdName = "image-index-create" ImageIndexCreateCmdNameUsage = "Create an image index (aka manifest list) with the referenced images (already in the target registry)" ServerCmdName = "server" ServerCmdNameUsage = "Start a registry server" ) func fullCmdName(subCmdName string) string { return fmt.Sprintf("%s.%s", Name, subCmdName) } type CommonCommandParams struct { UseDockerCreds bool CredsAccount string CredsSecret string } func CommonCommandFlagValues(ctx *cli.Context) (*CommonCommandParams, error) { values := &CommonCommandParams{ UseDockerCreds: ctx.Bool(FlagUseDockerCreds), CredsAccount: ctx.String(FlagCredsAccount), //prefer env var for secret (todo: add interactive and file read modes) CredsSecret: ctx.String(FlagCredsSecret), } return values, nil } type PullCommandParams struct { *CommonCommandParams TargetRef string SaveToDocker bool } func PullCommandFlagValues(ctx *cli.Context) (*PullCommandParams, error) { common, err := CommonCommandFlagValues(ctx) if err != nil { return nil, err } values := &PullCommandParams{ CommonCommandParams: common, TargetRef: ctx.String(command.FlagTarget), SaveToDocker: ctx.Bool(FlagSaveToDocker), } return values, nil } type PushCommandParams struct { *CommonCommandParams TargetRef string TargetType string AsTag string } const ( ttDocker = "tt.docker" ttTar = "tt.tar" ttOCI = "tt.oci" ) func PushCommandFlagValues(ctx *cli.Context) (*PushCommandParams, error) { common, err := CommonCommandFlagValues(ctx) if err != nil { return nil, err } values := &PushCommandParams{ CommonCommandParams: common, AsTag: ctx.String(FlagAs), } if val := ctx.String(FlagDocker); val != "" { //todo: validate that this local docker image exists values.TargetRef = val values.TargetType = ttDocker } else if val := ctx.String(FlagTar); val != "" { //todo: validate that this local tar file exists values.TargetRef = val values.TargetType = ttTar } else if val := ctx.String(FlagOCI); val != "" { //todo: validate that this local directory exists values.TargetRef = val values.TargetType = ttOCI } return values, nil } type ImageIndexCreateCommandParams struct { *CommonCommandParams ImageIndexName string ImageNames []string AsManifestList bool InsecureRefs bool DumpRawManifest bool } func ImageIndexCreateCommandFlagValues(ctx *cli.Context) (*ImageIndexCreateCommandParams, error) { common, err := CommonCommandFlagValues(ctx) if err != nil { return nil, err } values := &ImageIndexCreateCommandParams{ CommonCommandParams: common, ImageIndexName: ctx.String(FlagImageIndexName), ImageNames: ctx.StringSlice(FlagImageName), AsManifestList: ctx.Bool(FlagAsManifestList), InsecureRefs: ctx.Bool(FlagInsecureRefs), DumpRawManifest: ctx.Bool(FlagDumpRawManifest), } return values, nil } type ServerCommandParams struct { *CommonCommandParams Domain string Address string Port uint UseHTTPS bool CertPath string KeyPath string ReferrersAPI bool StorePath string UseMemStore bool } func ServerCommandFlagValues(ctx *cli.Context) (*ServerCommandParams, error) { common, err := CommonCommandFlagValues(ctx) if err != nil { return nil, err } values := &ServerCommandParams{ CommonCommandParams: common, Domain: ctx.String(FlagDomain), Address: ctx.String(FlagAddress), Port: ctx.Uint(FlagPort), UseHTTPS: ctx.Bool(FlagHTTPS), CertPath: ctx.String(FlagCertPath), KeyPath: ctx.String(FlagKeyPath), ReferrersAPI: ctx.Bool(FlagReferrersAPI), StorePath: ctx.String(FlagStorePath), UseMemStore: ctx.Bool(FlagMemStore), } return values, nil } var CLI = &cli.Command{ Name: Name, Aliases: []string{Alias}, Usage: Usage, Flags: []cli.Flag{ cflag(FlagUseDockerCreds), cflag(FlagCredsAccount), cflag(FlagCredsSecret), }, Subcommands: []*cli.Command{ { Name: PullCmdName, Usage: PullCmdNameUsage, Flags: []cli.Flag{ command.Cflag(command.FlagTarget), cflag(FlagSaveToDocker), }, Action: func(ctx *cli.Context) error { gcvalues := command.GlobalFlagValues(ctx) xc := app.NewExecutionContext( fullCmdName(PullCmdName), gcvalues.QuietCLIMode, gcvalues.OutputFormat) cparams, err := PullCommandFlagValues(ctx) if err != nil { return err } if cparams.TargetRef == "" { if ctx.Args().Len() < 1 { xc.Out.Error("param.target", "missing target") cli.ShowCommandHelp(ctx, Name) return nil } else { cparams.TargetRef = ctx.Args().First() } } OnPullCommand(xc, gcvalues, cparams) return nil }, }, { Name: PushCmdName, Usage: PushCmdNameUsage, Flags: []cli.Flag{ cflag(FlagAs), cflag(FlagDocker), cflag(FlagTar), cflag(FlagOCI), }, Action: func(ctx *cli.Context) error { gcvalues := command.GlobalFlagValues(ctx) xc := app.NewExecutionContext( fullCmdName(PushCmdName), gcvalues.QuietCLIMode, gcvalues.OutputFormat) cparams, err := PushCommandFlagValues(ctx) if err != nil { xc.Out.Error("params", err.Error()) return err } if cparams.TargetType == "" { if ctx.Args().Len() < 1 { xc.Out.Error("params.target", "missing pull target") return fmt.Errorf("no target selected") } cparams.TargetRef = ctx.Args().First() cparams.TargetType = ttDocker } OnPushCommand(xc, gcvalues, cparams) return nil }, }, { Name: CopyCmdName, Usage: CopyCmdNameUsage, Action: func(ctx *cli.Context) error { gcvalues := command.GlobalFlagValues(ctx) xc := app.NewExecutionContext( fullCmdName(CopyCmdName), gcvalues.QuietCLIMode, gcvalues.OutputFormat) OnCopyCommand(xc, gcvalues) return nil }, }, { Name: ImageIndexCreateCmdName, Usage: ImageIndexCreateCmdNameUsage, Flags: []cli.Flag{ cflag(FlagImageIndexName), cflag(FlagImageName), cflag(FlagAsManifestList), cflag(FlagInsecureRefs), cflag(FlagDumpRawManifest), }, Action: func(ctx *cli.Context) error { gcvalues := command.GlobalFlagValues(ctx) xc := app.NewExecutionContext( fullCmdName(ImageIndexCreateCmdName), gcvalues.QuietCLIMode, gcvalues.OutputFormat) cparams, err := ImageIndexCreateCommandFlagValues(ctx) if err != nil { return err } OnImageIndexCreateCommand(xc, gcvalues, cparams) return nil }, }, { Name: ServerCmdName, Usage: ServerCmdNameUsage, Flags: []cli.Flag{ cflag(FlagDomain), cflag(FlagAddress), cflag(FlagPort), cflag(FlagHTTPS), cflag(FlagCertPath), cflag(FlagKeyPath), cflag(FlagReferrersAPI), }, Action: func(ctx *cli.Context) error { gcvalues := command.GlobalFlagValues(ctx) xc := app.NewExecutionContext( fullCmdName(ServerCmdName), gcvalues.QuietCLIMode, gcvalues.OutputFormat) cparams, err := ServerCommandFlagValues(ctx) if err != nil { return err } OnServerCommand(xc, gcvalues, cparams) return nil }, }, }, } ================================================ FILE: pkg/app/master/command/registry/flags.go ================================================ package registry import ( log "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" ) // Registry command flag names const ( FlagUseDockerCreds = "use-docker-credentials" FlagUseDockerCredsUsage = "Use the registry credentials from the default Docker config file" FlagCredsAccount = "account" FlagCredsAccountUsage = "Registry credentials account" FlagCredsSecret = "secret" FlagCredsSecretUsage = "Registry credentials secret" // Pull Flags FlagSaveToDocker = "save-to-docker" FlagSaveToDockerUsage = "Save pulled image to docker" // Push Flags FlagDocker = "docker" FlagDockerUsage = "Push local docker image" FlagTar = "tar" FlagTarUsage = "Push image from a local tar file" FlagOCI = "oci" FlagOCIUsage = "Push image from a local OCI Image Layout directory" FlagAs = "as" FlagAsUsage = "Tag the selected image with the specified name before pushing" // Image Index Flags FlagImageIndexName = "image-index-name" FlagImageIndexNameUsage = "Image index name to use" FlagImageName = "image-name" FlagImageNameUsage = "Target image name to include in image index" FlagAsManifestList = "as-manifest-list" FlagAsManifestListUsage = "Create image index with the manifest list media type instead of the default OCI image index type" FlagInsecureRefs = "insecure-refs" FlagInsecureRefsUsage = "Allow the referenced images from insecure registry connections" FlagDumpRawManifest = "dump-raw-manifest" FlagDumpRawManifestUsage = "Dump raw manifest for the created image index" // Registry Server Flags FlagAddress = "address" FlagAddressUsage = "Registry server address" FlagPort = "port" FlagPortUsage = "Registry server port" FlagDomain = "domain" FlagDomainUsage = "Domain to use for registry server (to get certs)" FlagHTTPS = "https" FlagHTTPSUsage = "Use HTTPS" FlagCertPath = "cert-path" FlagCertPathUsage = "Cert path for use with HTTPS" FlagKeyPath = "key-path" FlagKeyPathUsage = "Key path for use with HTTPS" FlagReferrersAPI = "referrers-api" FlagReferrersAPIUsage = "Enables the referrers API endpoint (OCI 1.1+) for the registry server" FlagStorePath = "store-path" FlagStorePathUsage = "Directory to store registry blobs" FlagMemStore = "mem-store" FlagMemStoreUsage = "Use memory registry blob store" ) var Flags = map[string]cli.Flag{ FlagUseDockerCreds: &cli.BoolFlag{ Name: FlagUseDockerCreds, Value: false, //defaults to false Usage: FlagUseDockerCredsUsage, EnvVars: []string{"DSLIM_REG_DOCKER_CREDS"}, }, FlagCredsAccount: &cli.StringFlag{ Name: FlagCredsAccount, Value: "", Usage: FlagCredsAccountUsage, EnvVars: []string{"DSLIM_REG_ACCOUNT"}, }, FlagCredsSecret: &cli.StringFlag{ Name: FlagCredsSecret, Value: "", Usage: FlagCredsSecretUsage, EnvVars: []string{"DSLIM_REG_SECRET"}, }, // Pull Flags: FlagSaveToDocker: &cli.BoolFlag{ Name: FlagSaveToDocker, Value: true, //defaults to true Usage: FlagSaveToDockerUsage, EnvVars: []string{"DSLIM_REG_PULL_SAVE_TO_DOCKER"}, }, // Push Flags: FlagDocker: &cli.StringFlag{ Name: FlagDocker, Value: "", Usage: FlagDockerUsage, EnvVars: []string{"DSLIM_REG_PUSH_DOCKER"}, }, FlagTar: &cli.StringFlag{ Name: FlagTar, Value: "", Usage: FlagTarUsage, EnvVars: []string{"DSLIM_REG_PUSH_TAR"}, }, FlagOCI: &cli.StringFlag{ Name: FlagOCI, Value: "", Usage: FlagOCIUsage, EnvVars: []string{"DSLIM_REG_PUSH_OCI"}, }, FlagAs: &cli.StringFlag{ Name: FlagAs, Value: "", Usage: FlagAsUsage, EnvVars: []string{"DSLIM_REG_PUSH_AS"}, }, // Image Index Flags: FlagImageIndexName: &cli.StringFlag{ Name: FlagImageIndexName, Value: "", Usage: FlagImageIndexNameUsage, EnvVars: []string{"DSLIM_REG_IIC_INDEX_NAME"}, }, FlagImageName: &cli.StringSliceFlag{ Name: FlagImageName, Value: cli.NewStringSlice(), Usage: FlagImageNameUsage, EnvVars: []string{"DSLIM_REG_IIC_IMAGE_NAME"}, }, FlagAsManifestList: &cli.BoolFlag{ Name: FlagAsManifestList, Value: false, //defaults to false Usage: FlagAsManifestListUsage, EnvVars: []string{"DSLIM_REG_IIC_AS_MLIST"}, }, FlagInsecureRefs: &cli.BoolFlag{ Name: FlagInsecureRefs, Value: false, //defaults to false Usage: FlagInsecureRefsUsage, EnvVars: []string{"DSLIM_REG_IIC_INSECURE_REFS"}, }, FlagDumpRawManifest: &cli.BoolFlag{ Name: FlagDumpRawManifest, Value: false, //defaults to false Usage: FlagDumpRawManifestUsage, EnvVars: []string{"DSLIM_REG_IIC_DUMP_MANIFEST"}, }, // Registry Server Flags: FlagReferrersAPI: &cli.BoolFlag{ Name: FlagReferrersAPI, Value: true, //defaults to true Usage: FlagReferrersAPIUsage, EnvVars: []string{"DSLIM_REG_SRV_REFERRERS_API"}, }, FlagDomain: &cli.StringFlag{ Name: FlagDomain, Value: "", Usage: FlagDomainUsage, EnvVars: []string{"DSLIM_REG_SRV_DOMAIN"}, }, FlagAddress: &cli.StringFlag{ Name: FlagAddress, Value: "0.0.0.0", Usage: FlagAddressUsage, EnvVars: []string{"DSLIM_REG_SRV_ADDR"}, }, FlagPort: &cli.UintFlag{ Name: FlagPort, Value: 5000, Usage: FlagPortUsage, EnvVars: []string{"DSLIM_REG_SRV_PORT"}, }, FlagHTTPS: &cli.BoolFlag{ Name: FlagHTTPS, Value: false, //defaults to false Usage: FlagHTTPSUsage, EnvVars: []string{"DSLIM_REG_SRV_HTTPS"}, }, FlagCertPath: &cli.StringFlag{ Name: FlagCertPath, Value: "", Usage: FlagCertPathUsage, EnvVars: []string{"DSLIM_REG_SRV_CERT"}, }, FlagKeyPath: &cli.StringFlag{ Name: FlagKeyPath, Value: "", Usage: FlagKeyPathUsage, EnvVars: []string{"DSLIM_REG_SRV_KEY"}, }, FlagStorePath: &cli.StringFlag{ Name: FlagStorePath, Value: "registry_server_data", Usage: FlagStorePathUsage, EnvVars: []string{"DSLIM_REG_SRV_STORE_PATH"}, }, FlagMemStore: &cli.BoolFlag{ Name: FlagMemStore, Value: false, //defaults to false Usage: FlagMemStoreUsage, EnvVars: []string{"DSLIM_REG_SRV_MEM_STORE"}, }, } func cflag(name string) cli.Flag { cf, ok := Flags[name] if !ok { log.Fatalf("unknown flag='%s'", name) } return cf } ================================================ FILE: pkg/app/master/command/registry/handler_copy.go ================================================ package registry import ( //"github.com/google/go-containerregistry/pkg/crane" //"github.com/google/go-containerregistry/pkg/name" //gocrv1 "github.com/google/go-containerregistry/pkg/v1" //"github.com/google/go-containerregistry/pkg/v1/daemon" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/app/master/version" cmd "github.com/slimtoolkit/slim/pkg/command" "github.com/slimtoolkit/slim/pkg/docker/dockerclient" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/util/errutil" "github.com/slimtoolkit/slim/pkg/util/fsutil" v "github.com/slimtoolkit/slim/pkg/version" ) // OnCopyCommand implements the 'registry copy' command func OnCopyCommand( xc *app.ExecutionContext, gparams *command.GenericParams) { cmdName := fullCmdName(CopyCmdName) logger := log.WithFields(log.Fields{ "app": appName, "cmd": cmdName, "sub": CopyCmdName}) viChan := version.CheckAsync(gparams.CheckVersion, gparams.InContainer, gparams.IsDSImage) cmdReport := report.NewRegistryCommand(gparams.ReportLocation, gparams.InContainer) cmdReport.State = cmd.StateStarted xc.Out.State(cmd.StateStarted) client, err := dockerclient.New(gparams.ClientConfig) if err == dockerclient.ErrNoDockerInfo { exitMsg := "missing Docker connection info" if gparams.InContainer && gparams.IsDSImage { exitMsg = "make sure to pass the Docker connect parameters to the docker-slim container" } xc.Out.Info("docker.connect.error", ovars{ "message": exitMsg, }) exitCode := command.ECTCommon | command.ECCNoDockerConnectInfo xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Exit(exitCode) } errutil.FailOn(err) if gparams.Debug { version.Print(xc, cmdName, logger, client, false, gparams.InContainer, gparams.IsDSImage) } xc.Out.State(cmd.StateCompleted) cmdReport.State = cmd.StateCompleted xc.Out.State(cmd.StateDone) vinfo := <-viChan version.PrintCheckVersion(xc, "", vinfo) cmdReport.State = cmd.StateDone if cmdReport.Save() { xc.Out.Info("report", ovars{ "file": cmdReport.ReportLocation(), }) } } ================================================ FILE: pkg/app/master/command/registry/handler_image_index.go ================================================ package registry import ( "context" "errors" "fmt" "net/http" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/empty" "github.com/google/go-containerregistry/pkg/v1/mutate" "github.com/google/go-containerregistry/pkg/v1/partial" "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/google/go-containerregistry/pkg/v1/remote/transport" "github.com/google/go-containerregistry/pkg/v1/types" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/app/master/version" cmd "github.com/slimtoolkit/slim/pkg/command" "github.com/slimtoolkit/slim/pkg/docker/dockerclient" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/util/fsutil" v "github.com/slimtoolkit/slim/pkg/version" ) // OnImageIndexCreateCommand implements the 'registry image-index-create' command func OnImageIndexCreateCommand( xc *app.ExecutionContext, gparams *command.GenericParams, cparams *ImageIndexCreateCommandParams) { cmdName := fullCmdName(CopyCmdName) logger := log.WithFields(log.Fields{ "app": appName, "cmd": cmdName, "sub": ImageIndexCreateCmdName}) viChan := version.CheckAsync(gparams.CheckVersion, gparams.InContainer, gparams.IsDSImage) cmdReport := report.NewRegistryCommand(gparams.ReportLocation, gparams.InContainer) cmdReport.State = cmd.StateStarted xc.Out.State(cmd.StateStarted) client, err := dockerclient.New(gparams.ClientConfig) if err == dockerclient.ErrNoDockerInfo { exitMsg := "missing Docker connection info" if gparams.InContainer && gparams.IsDSImage { exitMsg = "make sure to pass the Docker connect parameters to the docker-slim container" } xc.Out.Info("docker.connect.error", ovars{ "message": exitMsg, }) exitCode := command.ECTCommon | command.ECCNoDockerConnectInfo xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Exit(exitCode) } xc.FailOn(err) if gparams.Debug { version.Print(xc, cmdName, logger, client, false, gparams.InContainer, gparams.IsDSImage) } if len(cparams.ImageNames) == 0 { xc.FailOn(fmt.Errorf("no image references for image index")) } if !cparams.UseDockerCreds && !(cparams.CredsAccount != "" && cparams.CredsSecret != "") { xc.FailOn(fmt.Errorf("missing auth params")) } remoteOpts := []remote.Option{ remote.WithContext(context.Background()), } remoteOpts, err = ConfigureAuth(cparams.CommonCommandParams, remoteOpts) xc.FailOn(err) nameOpts := []name.Option{ name.WeakValidation, } if cparams.InsecureRefs { nameOpts = append(nameOpts, name.Insecure) } imageIndexRef, err := name.ParseReference(cparams.ImageIndexName, nameOpts...) if err != nil { xc.FailOn(fmt.Errorf("malformed image index reference - %s (%v)", cparams.ImageIndexName, err)) } if _, err := remote.Head(imageIndexRef, remoteOpts...); err != nil { if _, ok := err.(*transport.Error); ok { logger.Debug("no image index in registry (ok)") } else { logger.Debugf("error checking image index in registry - %v", err) } } else { logger.Debug("image index is already in the registry") } imageIndex := v1.ImageIndex(empty.Index) indexImageImgRefs := make([]mutate.IndexAddendum, 0, len(cparams.ImageNames)) for _, imageName := range cparams.ImageNames { imgRef, err := name.ParseReference(imageName, nameOpts...) if err != nil { xc.FailOn(fmt.Errorf("malformed image reference - %s (%v)", imageName, err)) } meta, err := remote.Get(imgRef, remoteOpts...) if err != nil { xc.FailOn(fmt.Errorf("image reference metadata get error - %s (%v)", imageName, err)) } if meta.MediaType.IsImage() { imgMeta, err := meta.Image() xc.FailOn(err) basicImageInfo(xc, imgMeta) imgConfig, err := imgMeta.ConfigFile() if err != nil { xc.FailOn(err) } imgRefMeta, err := partial.Descriptor(imgMeta) if err != nil { xc.FailOn(err) } imgRefMeta.Platform = imgConfig.Platform() indexImageImgRefs = append(indexImageImgRefs, mutate.IndexAddendum{ Add: imgMeta, Descriptor: *imgRefMeta, }) } else { xc.FailOn(fmt.Errorf("unexpected target image type - %s (%v)", imageName, meta.MediaType)) } } if cparams.AsManifestList { imageIndex = mutate.IndexMediaType(imageIndex, types.DockerManifestList) } imageIndex = mutate.AppendManifests(imageIndex, indexImageImgRefs...) if err := remote.WriteIndex(imageIndexRef, imageIndex, remoteOpts...); err != nil { var terr *transport.Error if errors.As(err, &terr) && terr.StatusCode == http.StatusUnauthorized { xc.Out.Info("registry.auth.error", ovars{ "message": "need to authenticate", }) exitCode := -111 xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), }) xc.Exit(exitCode) } else { xc.FailOn(fmt.Errorf("saving image index error - %s (%v)", cparams.ImageIndexName, err)) } } indexMeta, err := remote.Index(imageIndexRef, remoteOpts...) if err != nil { xc.FailOn(fmt.Errorf("index reference metadata get error - %s (%v)", cparams.ImageIndexName, err)) } indexMediaType, err := indexMeta.MediaType() xc.FailOn(err) if !indexMediaType.IsIndex() { xc.FailOn(fmt.Errorf("unexpected media type for index")) } indexDigest, err := indexMeta.Digest() xc.FailOn(err) indexManifest, err := indexMeta.IndexManifest() xc.FailOn(err) xc.Out.Info("index.info", ovars{ "reference": imageIndexRef, "digest": indexDigest.String(), "manifest.schema": indexManifest.SchemaVersion, "manifest.media_type": indexManifest.MediaType, "manifest.image.ref.count": len(indexManifest.Manifests), }) if cparams.DumpRawManifest { if rm, err := indexMeta.RawManifest(); err == nil { //todo: reformat to pretty print fmt.Printf("\n\n%s\n\n", string(rm)) } } xc.Out.State(cmd.StateCompleted) cmdReport.State = cmd.StateCompleted xc.Out.State(cmd.StateDone) vinfo := <-viChan version.PrintCheckVersion(xc, "", vinfo) cmdReport.State = cmd.StateDone if cmdReport.Save() { xc.Out.Info("report", ovars{ "file": cmdReport.ReportLocation(), }) } } func basicImageInfo( xc *app.ExecutionContext, targetImage v1.Image) { cn, err := targetImage.ConfigName() xc.FailOn(err) d, err := targetImage.Digest() xc.FailOn(err) cf, err := targetImage.ConfigFile() xc.FailOn(err) m, err := targetImage.Manifest() xc.FailOn(err) xc.Out.Info("image.info", ovars{ "id": fmt.Sprintf("%s:%s", cn.Algorithm, cn.Hex), "digest": fmt.Sprintf("%s:%s", d.Algorithm, d.Hex), "architecture": cf.Architecture, "os": cf.OS, "manifest.schema": m.SchemaVersion, "manifest.media_type": m.MediaType, "manifest.config.media_type": m.Config.MediaType, "manifest.config.size": fmt.Sprintf("%v", m.Config.Size), "manifest.config.digest": fmt.Sprintf("%s:%s", m.Config.Digest.Algorithm, m.Config.Digest.Hex), "manifest.layers.count": fmt.Sprintf("%v", len(m.Layers)), }) } ================================================ FILE: pkg/app/master/command/registry/handler_pull.go ================================================ package registry import ( "context" "fmt" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/daemon" "github.com/google/go-containerregistry/pkg/v1/remote" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/app/master/version" cmd "github.com/slimtoolkit/slim/pkg/command" "github.com/slimtoolkit/slim/pkg/docker/dockerclient" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/util/errutil" "github.com/slimtoolkit/slim/pkg/util/fsutil" v "github.com/slimtoolkit/slim/pkg/version" ) const appName = command.AppName type ovars = app.OutVars // OnPullCommand implements the 'registry pull' command func OnPullCommand( xc *app.ExecutionContext, gparams *command.GenericParams, cparams *PullCommandParams) { cmdName := fullCmdName(PullCmdName) logger := log.WithFields(log.Fields{ "app": appName, "cmd": cmdName, "sub": PullCmdName}) viChan := version.CheckAsync(gparams.CheckVersion, gparams.InContainer, gparams.IsDSImage) cmdReport := report.NewRegistryCommand(gparams.ReportLocation, gparams.InContainer) cmdReport.State = cmd.StateStarted cmdReport.TargetReference = cparams.TargetRef xc.Out.State(cmd.StateStarted) xc.Out.Info("params", ovars{ "cmd.params": fmt.Sprintf("%+v", cparams), }) client, err := dockerclient.New(gparams.ClientConfig) if err == dockerclient.ErrNoDockerInfo { exitMsg := "missing Docker connection info" if gparams.InContainer && gparams.IsDSImage { exitMsg = "make sure to pass the Docker connect parameters to the docker-slim container" } xc.Out.Info("docker.connect.error", ovars{ "message": exitMsg, }) exitCode := command.ECTCommon | command.ECCNoDockerConnectInfo xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Exit(exitCode) } errutil.FailOn(err) if gparams.Debug { version.Print(xc, cmdName, logger, client, false, gparams.InContainer, gparams.IsDSImage) } remoteOpts := []remote.Option{ remote.WithContext(context.Background()), } remoteOpts, err = ConfigureAuth(cparams.CommonCommandParams, remoteOpts) xc.FailOn(err) nameOpts := []name.Option{ name.WeakValidation, name.Insecure, } ref, err := name.ParseReference(cparams.TargetRef, nameOpts...) errutil.FailOn(err) //todo: pass a custom client to Pull (based on `client` above) targetImage, err := remote.Image(ref, remoteOpts...) errutil.FailOn(err) outImageInfo(xc, targetImage) if cparams.SaveToDocker { xc.Out.State("save.docker.start") tag, err := name.NewTag(cparams.TargetRef) errutil.FailOn(err) rawResponse, err := daemon.Write(tag, targetImage) errutil.FailOn(err) logger.Tracef("Image save to Docker response: %v", rawResponse) xc.Out.State("save.docker.done") } xc.Out.State(cmd.StateCompleted) cmdReport.State = cmd.StateCompleted xc.Out.State(cmd.StateDone) vinfo := <-viChan version.PrintCheckVersion(xc, "", vinfo) cmdReport.State = cmd.StateDone if cmdReport.Save() { xc.Out.Info("report", ovars{ "file": cmdReport.ReportLocation(), }) } } func outImageInfo( xc *app.ExecutionContext, targetImage v1.Image) { cn, err := targetImage.ConfigName() xc.FailOn(err) d, err := targetImage.Digest() xc.FailOn(err) cf, err := targetImage.ConfigFile() xc.FailOn(err) m, err := targetImage.Manifest() xc.FailOn(err) xc.Out.Info("image.info", ovars{ "id": fmt.Sprintf("%s:%s", cn.Algorithm, cn.Hex), "digest": fmt.Sprintf("%s:%s", d.Algorithm, d.Hex), "architecture": cf.Architecture, "os": cf.OS, "manifest.schema": m.SchemaVersion, "manifest.media_type": m.MediaType, "manifest.config.media_type": m.Config.MediaType, "manifest.config.size": fmt.Sprintf("%v", m.Config.Size), "manifest.config.digest": fmt.Sprintf("%s:%s", m.Config.Digest.Algorithm, m.Config.Digest.Hex), "manifest.layers.count": fmt.Sprintf("%v", len(m.Layers)), }) } ================================================ FILE: pkg/app/master/command/registry/handler_push.go ================================================ package registry import ( "context" "os" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/daemon" "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/google/go-containerregistry/pkg/v1/tarball" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/app/master/version" cmd "github.com/slimtoolkit/slim/pkg/command" "github.com/slimtoolkit/slim/pkg/docker/dockerclient" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/util/fsutil" v "github.com/slimtoolkit/slim/pkg/version" ) // OnPushCommand implements the 'registry push' command func OnPushCommand( xc *app.ExecutionContext, gparams *command.GenericParams, cparams *PushCommandParams) { cmdName := fullCmdName(PushCmdName) logger := log.WithFields(log.Fields{ "app": appName, "cmd": cmdName, "sub": PushCmdName}) viChan := version.CheckAsync(gparams.CheckVersion, gparams.InContainer, gparams.IsDSImage) cmdReport := report.NewRegistryCommand(gparams.ReportLocation, gparams.InContainer) cmdReport.State = cmd.StateStarted xc.Out.State(cmd.StateStarted) client, err := dockerclient.New(gparams.ClientConfig) if err == dockerclient.ErrNoDockerInfo { exitMsg := "missing Docker connection info" if gparams.InContainer && gparams.IsDSImage { exitMsg = "make sure to pass the Docker connect parameters to the docker-slim container" } xc.Out.Info("docker.connect.error", ovars{ "message": exitMsg, }) exitCode := command.ECTCommon | command.ECCNoDockerConnectInfo xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Exit(exitCode) } xc.FailOn(err) if gparams.Debug { version.Print(xc, cmdName, logger, client, false, gparams.InContainer, gparams.IsDSImage) } remoteOpts := []remote.Option{ remote.WithContext(context.Background()), } remoteOpts, err = ConfigureAuth(cparams.CommonCommandParams, remoteOpts) xc.FailOn(err) nameOpts := []name.Option{ name.WeakValidation, name.Insecure, } //todo: add support for other target types too if cparams.TargetType == ttDocker { tarPath, err := uniqueTarFilePath() xc.FailOn(err) err = saveDockerImage(logger, cparams.TargetRef, tarPath, nameOpts) xc.FailOn(err) remoteImageName := cparams.TargetRef if cparams.AsTag != "" { remoteImageName = cparams.AsTag } err = pushImageFromTar(logger, tarPath, remoteImageName, nameOpts, remoteOpts) xc.FailOn(err) } xc.Out.State(cmd.StateCompleted) cmdReport.State = cmd.StateCompleted xc.Out.State(cmd.StateDone) vinfo := <-viChan version.PrintCheckVersion(xc, "", vinfo) cmdReport.State = cmd.StateDone if cmdReport.Save() { xc.Out.Info("report", ovars{ "file": cmdReport.ReportLocation(), }) } } func uniqueTarFilePath() (string, error) { f, err := os.CreateTemp("", "saved-image-*.tar") if err != nil { return "", err } defer f.Close() defer os.Remove(f.Name()) return f.Name(), nil } func saveDockerImage( logger *log.Entry, localImageName string, tarPath string, nameOpts []name.Option) error { logger = logger.WithField("op", "registry.saveDockerImage") logger.Trace("call") defer logger.Trace("exit") ref, err := name.ParseReference(localImageName, nameOpts...) if err != nil { logger.WithError(err).Errorf("name.ParseReference(%s)", localImageName) return err } img, err := daemon.Image(ref) if err != nil { logger.WithError(err).Errorf("daemon.Image(%s)", localImageName) return err } if err := tarball.WriteToFile(tarPath, ref, img); err != nil { logger.WithError(err).Errorf("tarball.WriteToFile(%s, %s)", tarPath, localImageName) return err } return nil } func pushImageFromTar( logger *log.Entry, tarPath string, remoteImageName string, nameOpts []name.Option, remoteOpts []remote.Option) error { logger = logger.WithField("op", "registry.saveDockerImage") logger.Trace("call") defer logger.Trace("exit") ref, err := name.ParseReference(remoteImageName, nameOpts...) if err != nil { logger.WithError(err).Errorf("name.ParseReference(%s)", remoteImageName) return err } img, err := tarball.ImageFromPath(tarPath, nil) if err != nil { logger.WithError(err).Errorf("tarball.ImageFromPath(%s)", tarPath) return err } err = remote.Write(ref, img, remoteOpts...) if err != nil { logger.WithError(err).Errorf("tarball.ImageFromPath(%s, %s)", tarPath, remoteImageName) return err } return nil } ================================================ FILE: pkg/app/master/command/registry/handler_server.go ================================================ package registry import ( "crypto/tls" "errors" "fmt" "golang.org/x/crypto/acme/autocert" stdlog "log" "net/http" "time" docker "github.com/fsouza/go-dockerclient" log "github.com/sirupsen/logrus" "github.com/google/go-containerregistry/pkg/registry" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/app/master/version" cmd "github.com/slimtoolkit/slim/pkg/command" //"github.com/slimtoolkit/slim/pkg/docker/dockerclient" "github.com/slimtoolkit/slim/pkg/report" //"github.com/slimtoolkit/slim/pkg/util/fsutil" //v "github.com/slimtoolkit/slim/pkg/version" ) // OnServerCommand implements the 'registry server' command func OnServerCommand( xc *app.ExecutionContext, gparams *command.GenericParams, cparams *ServerCommandParams) { cmdName := fullCmdName(ServerCmdName) logger := log.WithFields(log.Fields{ "app": appName, "cmd": cmdName, "sub": ServerCmdName}) viChan := version.CheckAsync(gparams.CheckVersion, gparams.InContainer, gparams.IsDSImage) cmdReport := report.NewRegistryCommand(gparams.ReportLocation, gparams.InContainer) cmdReport.State = cmd.StateStarted xc.Out.State(cmd.StateStarted) var client *docker.Client /* NOTE: don't really need a docker client for the server... client, err := dockerclient.New(gparams.ClientConfig) if err == dockerclient.ErrNoDockerInfo { exitMsg := "missing Docker connection info" if gparams.InContainer && gparams.IsDSImage { exitMsg = "make sure to pass the Docker connect parameters to the docker-slim container" } xc.Out.Info("docker.connect.error", ovars{ "message": exitMsg, }) exitCode := command.ECTCommon | command.ECCNoDockerConnectInfo xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Exit(exitCode) } xc.FailOn(err) */ if gparams.Debug { version.Print(xc, cmdName, logger, client, false, gparams.InContainer, gparams.IsDSImage) } opts := []registry.Option{registry.Logger(stdlog.New(logger.Logger.Out, "", stdlog.LstdFlags))} if cparams.ReferrersAPI { opts = append(opts, registry.WithReferrersSupport(true)) } //TODO: add the custom blob handler logic if cparams.UseMemStore { //bh := registry.NewInMemoryBlobHandler() } else { //cparams.StorePath //bh = registry.NewDiskBlobHandler(diskp) //opts = append(opts, registry.WithBlobHandler(bh)) } //TODO: wrap http server to record the calls and save them in the report go func() { time.Sleep(3 * time.Second) xc.Out.State(cmd.StateCompleted) cmdReport.State = cmd.StateCompleted xc.Out.State(cmd.StateDone) vinfo := <-viChan version.PrintCheckVersion(xc, "", vinfo) cmdReport.State = cmd.StateDone if cmdReport.Save() { xc.Out.Info("report", ovars{ "file": cmdReport.ReportLocation(), }) } }() server := &http.Server{ Addr: fmt.Sprintf("%s:%d", cparams.Address, cparams.Port), ReadTimeout: 5 * time.Second, ReadHeaderTimeout: 4 * time.Second, WriteTimeout: 10 * time.Second, IdleTimeout: 120 * time.Second, Handler: registry.New(opts...), } var err error if cparams.UseHTTPS { server.TLSConfig = &tls.Config{ MinVersion: tls.VersionTLS12, PreferServerCipherSuites: true, CurvePreferences: []tls.CurveID{ tls.CurveP256, tls.X25519, }, } if cparams.Domain != "" && cparams.CertPath == "" && cparams.KeyPath == "" { certManager := autocert.Manager{ Prompt: autocert.AcceptTOS, //TODO: needs to put it as a sub-dir in the state path Cache: autocert.DirCache(".mint_certs"), HostPolicy: autocert.HostWhitelist(cparams.Domain), } server.TLSConfig.GetCertificate = certManager.GetCertificate } err = server.ListenAndServeTLS(cparams.CertPath, cparams.KeyPath) } else { err = server.ListenAndServe() } if errors.Is(err, http.ErrServerClosed) { xc.Out.Message("Server is done...") } else { xc.FailOn(err) } } ================================================ FILE: pkg/app/master/command/registry/init/init.go ================================================ package init import ( "github.com/slimtoolkit/slim/pkg/app/master/command/registry" ) func init() { registry.RegisterCommand() } ================================================ FILE: pkg/app/master/command/registry/prompt.go ================================================ package registry import ( "github.com/c-bata/go-prompt" "github.com/slimtoolkit/slim/pkg/app/master/command" ) var CommandSuggestion = prompt.Suggest{ Text: Name, Description: Usage, } var CommandFlagSuggestions = &command.FlagSuggestions{ Names: []prompt.Suggest{ {Text: command.FullFlagName(FlagUseDockerCreds), Description: FlagUseDockerCredsUsage}, {Text: command.FullFlagName(FlagCredsAccount), Description: FlagCredsAccountUsage}, {Text: command.FullFlagName(FlagCredsSecret), Description: FlagCredsSecretUsage}, //including sub-commands here too {Text: PullCmdName, Description: PullCmdNameUsage}, {Text: PushCmdName, Description: PushCmdNameUsage}, {Text: ImageIndexCreateCmdName, Description: ImageIndexCreateCmdNameUsage}, {Text: ServerCmdName, Description: ServerCmdNameUsage}, }, Values: map[string]command.CompleteValue{}, } ================================================ FILE: pkg/app/master/command/registry/register.go ================================================ package registry import ( "github.com/slimtoolkit/slim/pkg/app/master/command" ) func RegisterCommand() { command.AddCLICommand( Name, CLI, CommandSuggestion, CommandFlagSuggestions) } ================================================ FILE: pkg/app/master/command/run/cli.go ================================================ package run import ( dockerapi "github.com/fsouza/go-dockerclient" "github.com/urfave/cli/v2" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/app/master/config" ) const ( Name = "run" Usage = "Run one or more containers" Alias = "r" ) type CommandParams struct { TargetRef string DoPull bool DockerConfigPath string RegistryAccount string RegistrySecret string DoShowPullLogs bool Entrypoint []string Cmd []string DoLiveLogs bool DoTerminal bool PublishPorts map[dockerapi.Port][]dockerapi.PortBinding EnvVars []string Volumes []config.VolumeMount DoRemoveOnExit bool DoDetach bool } func CommandFlagValues(ctx *cli.Context) (*CommandParams, error) { values := &CommandParams{ TargetRef: ctx.String(command.FlagTarget), DoPull: ctx.Bool(command.FlagPull), DockerConfigPath: ctx.String(command.FlagDockerConfigPath), RegistryAccount: ctx.String(command.FlagRegistryAccount), RegistrySecret: ctx.String(command.FlagRegistrySecret), DoShowPullLogs: ctx.Bool(command.FlagShowPullLogs), DoLiveLogs: ctx.Bool(FlagLiveLogs), DoTerminal: ctx.Bool(FlagTerminal), EnvVars: ctx.StringSlice(command.FlagEnv), DoRemoveOnExit: ctx.Bool(FlagRemove), DoDetach: ctx.Bool(FlagDetach), } var err error if rawEntrypoint := ctx.String(command.FlagEntrypoint); rawEntrypoint != "" { values.Entrypoint, err = command.ParseExec(rawEntrypoint) if err != nil { return nil, err } } if rawCmd := ctx.String(command.FlagCmd); rawCmd != "" { values.Cmd, err = command.ParseExec(rawCmd) if err != nil { return nil, err } } if rawVolumes := ctx.StringSlice(command.FlagVolume); len(rawVolumes) > 0 { values.Volumes, err = command.ParseVolumeMountsAsList(rawVolumes) if err != nil { return nil, err } } if rawPorts := ctx.StringSlice(FlagPublishPort); len(rawPorts) > 0 { values.PublishPorts, err = command.ParsePortBindings(rawPorts) } return values, nil } var CLI = &cli.Command{ Name: Name, Aliases: []string{Alias}, Usage: Usage, Flags: []cli.Flag{ command.Cflag(command.FlagTarget), command.Cflag(command.FlagPull), command.Cflag(command.FlagDockerConfigPath), command.Cflag(command.FlagRegistryAccount), command.Cflag(command.FlagRegistrySecret), command.Cflag(command.FlagShowPullLogs), command.Cflag(command.FlagEntrypoint), command.Cflag(command.FlagCmd), cflag(FlagLiveLogs), cflag(FlagTerminal), cflag(FlagPublishPort), command.Cflag(command.FlagEnv), command.Cflag(command.FlagVolume), cflag(FlagRemove), cflag(FlagDetach), }, Action: func(ctx *cli.Context) error { gparams := command.GlobalFlagValues(ctx) xc := app.NewExecutionContext( Name, gparams.QuietCLIMode, gparams.OutputFormat) cparams, err := CommandFlagValues(ctx) if err != nil { return err } if cparams.TargetRef == "" { if ctx.Args().Len() < 1 { xc.Out.Error("param.target", "missing target") cli.ShowCommandHelp(ctx, Name) return nil } else { cparams.TargetRef = ctx.Args().First() } } OnCommand( xc, gparams, cparams) return nil }, } ================================================ FILE: pkg/app/master/command/run/flags.go ================================================ package run import ( log "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" ) // Run command flag names const ( FlagLiveLogs = "live-logs" FlagTerminal = "terminal" FlagPublishPort = "publish" FlagRemove = "rm" FlagDetach = "detach" ) // Run command flag usage info const ( FlagLiveLogsUsage = "Show live logs for the container (cant use with --terminal)" FlagTerminalUsage = "Attach interactive terminal to the container" FlagPublishPortUsage = "Map container port to host port (format => port | hostPort:containerPort | hostIP:hostPort:containerPort | hostIP::containerPort )" FlagRemoveUsage = "Remove the container when it exits" FlagDetachUsage = "Start the container and do not wait for it to exit" ) var Flags = map[string]cli.Flag{ FlagLiveLogs: &cli.BoolFlag{ Name: FlagLiveLogs, Usage: FlagLiveLogsUsage, EnvVars: []string{"DSLIM_RUN_LIVE_LOGS"}, }, FlagTerminal: &cli.BoolFlag{ Name: FlagTerminal, Usage: FlagTerminalUsage, EnvVars: []string{"DSLIM_RUN_TERMINAL"}, }, FlagPublishPort: &cli.StringSliceFlag{ Name: FlagPublishPort, Value: &cli.StringSlice{}, Usage: FlagPublishPortUsage, EnvVars: []string{"DSLIM_RUN_PUBLISH_PORT"}, }, FlagRemove: &cli.BoolFlag{ Name: FlagRemove, Usage: FlagRemoveUsage, EnvVars: []string{"DSLIM_RUN_RM"}, }, FlagDetach: &cli.BoolFlag{ Name: FlagDetach, Usage: FlagDetachUsage, EnvVars: []string{"DSLIM_RUN_DETACH"}, }, } func cflag(name string) cli.Flag { cf, ok := Flags[name] if !ok { log.Fatalf("unknown flag='%s'", name) } return cf } ================================================ FILE: pkg/app/master/command/run/handler.go ================================================ package run import ( "fmt" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/app/master/container" "github.com/slimtoolkit/slim/pkg/app/master/inspectors/image" "github.com/slimtoolkit/slim/pkg/app/master/signals" "github.com/slimtoolkit/slim/pkg/app/master/version" cmd "github.com/slimtoolkit/slim/pkg/command" "github.com/slimtoolkit/slim/pkg/docker/dockerclient" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/util/errutil" "github.com/slimtoolkit/slim/pkg/util/fsutil" v "github.com/slimtoolkit/slim/pkg/version" ) const appName = command.AppName // Run command exit codes const ( ecbOther = iota + 1 ecbTarget ) type ovars = app.OutVars // OnCommand implements the 'run' command func OnCommand( xc *app.ExecutionContext, gparams *command.GenericParams, cparams *CommandParams) { logger := log.WithFields(log.Fields{"app": appName, "cmd": Name}) viChan := version.CheckAsync(gparams.CheckVersion, gparams.InContainer, gparams.IsDSImage) cmdReport := report.NewRunCommand(gparams.ReportLocation, gparams.InContainer) cmdReport.State = cmd.StateStarted cmdReport.TargetReference = cparams.TargetRef xc.Out.State(cmd.StateStarted) xc.Out.Info("params", ovars{ "cmd.params": fmt.Sprintf("%+v", cparams), }) client, err := dockerclient.New(gparams.ClientConfig) if err == dockerclient.ErrNoDockerInfo { exitMsg := "missing Docker connection info" if gparams.InContainer && gparams.IsDSImage { exitMsg = "make sure to pass the Docker connect parameters to the slim app container" } xc.Out.Info("docker.connect.error", ovars{ "message": exitMsg, }) exitCode := command.ECTCommon | command.ECCNoDockerConnectInfo xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Exit(exitCode) } errutil.FailOn(err) if gparams.Debug { version.Print(xc, Name, logger, client, false, gparams.InContainer, gparams.IsDSImage) } imageInspector, err := image.NewInspector(client, cparams.TargetRef) errutil.FailOn(err) noImage, err := imageInspector.NoImage() errutil.FailOn(err) if noImage { if cparams.DoPull { xc.Out.Info("target.image", ovars{ "status": "image.not.found", "image": cparams.TargetRef, "message": "trying to pull target image", }) err := imageInspector.Pull(cparams.DoShowPullLogs, cparams.DockerConfigPath, cparams.RegistryAccount, cparams.RegistrySecret) errutil.FailOn(err) } else { xc.Out.Info("target.image.error", ovars{ "status": "image.not.found", "image": cparams.TargetRef, "message": "make sure the target image already exists locally (use --pull flag to auto-download it from registry)", }) exitCode := command.ECTRun | ecbTarget xc.Out.State("exited", ovars{ "exit.code": exitCode, }) xc.Exit(exitCode) } } //refresh the target refs cparams.TargetRef = imageInspector.ImageRef cmdReport.TargetReference = imageInspector.ImageRef xc.Out.State("container.run.start") options := &container.ExecutionOptions{ Entrypoint: cparams.Entrypoint, Cmd: cparams.Cmd, PublishPorts: cparams.PublishPorts, EnvVars: cparams.EnvVars, Volumes: cparams.Volumes, LiveLogs: cparams.DoLiveLogs, Terminal: cparams.DoTerminal, } if options.Terminal { options.LiveLogs = false } containerEventCh := make(chan *container.ExecutionEvenInfo, 10) exe, err := container.NewExecution( xc, logger, client, cparams.TargetRef, options, containerEventCh, true, true) errutil.FailOn(err) continueCh := make(chan struct{}) go func() { for { select { case evt := <-containerEventCh: logger.Tracef("Exection Event: name=%s", evt.Event) switch evt.Event { case container.XEExitedCrash: xc.Out.Info("target.container.event", ovars{ "event": evt.Event, "image": cparams.TargetRef, }) exe.ShowContainerLogs() close(continueCh) return case container.XEExited: close(continueCh) return } case <-signals.AppContinueChan: err = exe.Stop() if err != nil { errutil.WarnOn(err) } close(continueCh) return } } }() err = exe.Start() errutil.FailOn(err) <-continueCh if cparams.DoRemoveOnExit { exe.Cleanup() } xc.Out.State("container.run.done") xc.Out.State(cmd.StateCompleted) cmdReport.State = cmd.StateCompleted xc.Out.State(cmd.StateDone) vinfo := <-viChan version.PrintCheckVersion(xc, "", vinfo) cmdReport.State = cmd.StateDone if cmdReport.Save() { xc.Out.Info("report", ovars{ "file": cmdReport.ReportLocation(), }) } } ================================================ FILE: pkg/app/master/command/run/init/init.go ================================================ package init import ( "github.com/slimtoolkit/slim/pkg/app/master/command/run" ) func init() { run.RegisterCommand() } ================================================ FILE: pkg/app/master/command/run/prompt.go ================================================ package run import ( "github.com/c-bata/go-prompt" ) var CommandSuggestion = prompt.Suggest{ Text: Name, Description: Usage, } ================================================ FILE: pkg/app/master/command/run/register.go ================================================ package run import ( "github.com/slimtoolkit/slim/pkg/app/master/command" ) func RegisterCommand() { command.AddCLICommand( Name, CLI, CommandSuggestion, nil) } ================================================ FILE: pkg/app/master/command/server/cli.go ================================================ package server import ( "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/urfave/cli/v2" ) const ( Name = "server" Usage = "Run as an HTTP server" Alias = "s" ) var CLI = &cli.Command{ Name: Name, Aliases: []string{Alias}, Usage: Usage, Action: func(ctx *cli.Context) error { gcvalues := command.GlobalFlagValues(ctx) xc := app.NewExecutionContext( Name, gcvalues.QuietCLIMode, gcvalues.OutputFormat) OnCommand( xc, gcvalues) return nil }, } ================================================ FILE: pkg/app/master/command/server/handler.go ================================================ package server import ( "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/app/master/version" cmd "github.com/slimtoolkit/slim/pkg/command" "github.com/slimtoolkit/slim/pkg/docker/dockerclient" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/util/errutil" "github.com/slimtoolkit/slim/pkg/util/fsutil" v "github.com/slimtoolkit/slim/pkg/version" log "github.com/sirupsen/logrus" ) const appName = command.AppName type ovars = app.OutVars // OnCommand implements the 'server' command func OnCommand( xc *app.ExecutionContext, gparams *command.GenericParams) { logger := log.WithFields(log.Fields{"app": appName, "cmd": Name}) viChan := version.CheckAsync(gparams.CheckVersion, gparams.InContainer, gparams.IsDSImage) cmdReport := report.NewServerCommand(gparams.ReportLocation, gparams.InContainer) cmdReport.State = cmd.StateStarted xc.Out.State(cmd.StateStarted) client, err := dockerclient.New(gparams.ClientConfig) if err == dockerclient.ErrNoDockerInfo { exitMsg := "missing Docker connection info" if gparams.InContainer && gparams.IsDSImage { exitMsg = "make sure to pass the Docker connect parameters to the slim app container" } xc.Out.Info("docker.connect.error", ovars{ "message": exitMsg, }) exitCode := command.ECTCommon | command.ECCNoDockerConnectInfo xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Exit(exitCode) } errutil.FailOn(err) if gparams.Debug { version.Print(xc, Name, logger, client, false, gparams.InContainer, gparams.IsDSImage) } xc.Out.State(cmd.StateCompleted) cmdReport.State = cmd.StateCompleted xc.Out.State(cmd.StateDone) vinfo := <-viChan version.PrintCheckVersion(xc, "", vinfo) cmdReport.State = cmd.StateDone if cmdReport.Save() { xc.Out.Info("report", ovars{ "file": cmdReport.ReportLocation(), }) } } ================================================ FILE: pkg/app/master/command/server/init/init.go ================================================ package init import ( "github.com/slimtoolkit/slim/pkg/app/master/command/server" ) func init() { server.RegisterCommand() } ================================================ FILE: pkg/app/master/command/server/prompt.go ================================================ package server import ( "github.com/c-bata/go-prompt" ) var CommandSuggestion = prompt.Suggest{ Text: Name, Description: Usage, } ================================================ FILE: pkg/app/master/command/server/register.go ================================================ package server import ( "github.com/slimtoolkit/slim/pkg/app/master/command" ) func RegisterCommand() { command.AddCLICommand( Name, CLI, CommandSuggestion, nil) } ================================================ FILE: pkg/app/master/command/update/cli.go ================================================ package update import ( "fmt" "runtime" "github.com/urfave/cli/v2" "github.com/slimtoolkit/slim/pkg/app/master/command" ) const ( Name = "update" Usage = "Updates slim" Alias = "u" ) var CLI = &cli.Command{ Name: Name, Aliases: []string{Alias}, Usage: Usage, Flags: []cli.Flag{ initFlagShowProgress(), }, Action: func(ctx *cli.Context) error { doDebug := ctx.Bool(command.FlagDebug) statePath := ctx.String(command.FlagStatePath) inContainer, isDSImage := command.IsInContainer(ctx.Bool(command.FlagInContainer)) archiveState := command.ArchiveState(ctx.String(command.FlagArchiveState), inContainer) doShowProgress := ctx.Bool(command.FlagShowProgress) OnCommand(doDebug, statePath, archiveState, inContainer, isDSImage, doShowProgress) return nil }, } func initFlagShowProgress() cli.Flag { //enable 'show-progress' by default only on Mac OS X var doShowProgressFlag cli.Flag switch runtime.GOOS { case "darwin": doShowProgressFlag = &cli.BoolFlag{ Name: command.FlagShowProgress, Value: true, Usage: fmt.Sprintf("%s (default: true)", command.FlagShowProgressUsage), EnvVars: []string{"DSLIM_UPDATE_SHOW_PROGRESS"}, } default: doShowProgressFlag = &cli.BoolFlag{ Name: command.FlagShowProgress, Usage: fmt.Sprintf("%s (default: false)", command.FlagShowProgressUsage), EnvVars: []string{"DSLIM_UPDATE_SHOW_PROGRESS"}, } } return doShowProgressFlag } ================================================ FILE: pkg/app/master/command/update/handler.go ================================================ package update import ( "github.com/slimtoolkit/slim/pkg/app/master/update" ) // OnCommand implements the 'update' command func OnCommand(doDebug bool, statePath, archiveState string, inContainer, isDSImage bool, doShowProgress bool) { update.Run(doDebug, statePath, inContainer, isDSImage, doShowProgress) } ================================================ FILE: pkg/app/master/command/update/init/init.go ================================================ package init import ( "github.com/slimtoolkit/slim/pkg/app/master/command/update" ) func init() { update.RegisterCommand() } ================================================ FILE: pkg/app/master/command/update/prompt.go ================================================ package update import ( "github.com/c-bata/go-prompt" "github.com/slimtoolkit/slim/pkg/app/master/command" ) var CommandSuggestion = prompt.Suggest{ Text: Name, Description: Usage, } var CommandFlagSuggestions = &command.FlagSuggestions{ Names: []prompt.Suggest{ {Text: command.FullFlagName(command.FlagShowProgress), Description: command.FlagShowProgressUsage}, }, Values: map[string]command.CompleteValue{ command.FullFlagName(command.FlagShowProgress): command.CompleteProgress, }, } ================================================ FILE: pkg/app/master/command/update/register.go ================================================ package update import ( "github.com/slimtoolkit/slim/pkg/app/master/command" ) func RegisterCommand() { command.AddCLICommand( Name, CLI, CommandSuggestion, CommandFlagSuggestions) } ================================================ FILE: pkg/app/master/command/version/cli.go ================================================ package version import ( "github.com/urfave/cli/v2" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" ) const ( Name = "version" Usage = "Shows slim and docker version information" Alias = "v" ) var CLI = &cli.Command{ Name: Name, Aliases: []string{Alias}, Usage: Usage, Action: func(ctx *cli.Context) error { gcvalues := command.GlobalFlagValues(ctx) xc := app.NewExecutionContext( Name, gcvalues.QuietCLIMode, gcvalues.OutputFormat) OnCommand(xc, gcvalues.Debug, gcvalues.InContainer, gcvalues.IsDSImage, gcvalues.ClientConfig) return nil }, } ================================================ FILE: pkg/app/master/command/version/handler.go ================================================ package version import ( log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app" //"github.com/slimtoolkit/slim/pkg/app/master/commands" "github.com/slimtoolkit/slim/pkg/app/master/config" "github.com/slimtoolkit/slim/pkg/app/master/version" cmd "github.com/slimtoolkit/slim/pkg/command" "github.com/slimtoolkit/slim/pkg/docker/dockerclient" "github.com/slimtoolkit/slim/pkg/util/errutil" "github.com/slimtoolkit/slim/pkg/util/fsutil" v "github.com/slimtoolkit/slim/pkg/version" ) type ovars = app.OutVars // OnCommand implements the 'version' command func OnCommand( xc *app.ExecutionContext, doDebug, inContainer, isDSImage bool, clientConfig *config.DockerClient) { logger := log.WithFields(log.Fields{"app": "slim", "cmd": cmd.Version}) client, err := dockerclient.New(clientConfig) if err == dockerclient.ErrNoDockerInfo { exitMsg := "missing Docker connection info" if inContainer && isDSImage { exitMsg = "make sure to pass the Docker connect parameters to the slim app container" } xc.Out.Info("docker.connect.error", ovars{ "message": exitMsg, }) exitCode := -777 xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Exit(exitCode) } errutil.FailOn(err) version.Print(xc, Name, logger, client, true, inContainer, isDSImage) } ================================================ FILE: pkg/app/master/command/version/init/init.go ================================================ package init import ( "github.com/slimtoolkit/slim/pkg/app/master/command/version" ) func init() { version.RegisterCommand() } ================================================ FILE: pkg/app/master/command/version/prompt.go ================================================ package version import ( "github.com/c-bata/go-prompt" ) var CommandSuggestion = prompt.Suggest{ Text: Name, Description: Usage, } ================================================ FILE: pkg/app/master/command/version/register.go ================================================ package version import ( "github.com/slimtoolkit/slim/pkg/app/master/command" ) func RegisterCommand() { command.AddCLICommand( Name, CLI, CommandSuggestion, nil) } ================================================ FILE: pkg/app/master/command/vulnerability/cli.go ================================================ package vulnerability import ( "fmt" "time" "github.com/urfave/cli/v2" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/vulnerability/epss" ) const ( Name = "vulnerability" Usage = "Execute vulnerability related tools and operations" Alias = "vuln" EpssCmdName = "epss" EpssCmdNameUsage = "Get EPPS information for the target vulnerabilities" ) const ( EpssOrderRecordsScoreDesc = "score-desc" EpssOrderRecordsScoreAsc = "score-asc" EpssOrderRecordsPercentileDesc = "percentile-desc" EpssOrderRecordsPercentileAsc = "percentile-asc" ) func IsValidOp(input string) bool { switch input { case EpssOpLookup, EpssOpList: return true } return false } func IsValidOrderRecordsValue(input string) bool { switch input { case EpssOrderRecordsScoreDesc, EpssOrderRecordsScoreAsc, EpssOrderRecordsPercentileDesc, EpssOrderRecordsPercentileAsc: return true } return false } func OrderType(input string) epss.OrderType { switch input { case EpssOrderRecordsScoreDesc: return epss.ScoreDescOrder case EpssOrderRecordsScoreAsc: return epss.ScoreAscOrder case EpssOrderRecordsPercentileDesc: return epss.PercentileDescOrder case EpssOrderRecordsPercentileAsc: return epss.PercentileAscOrder } return epss.NoOrder } func fullCmdName(subCmdName string) string { return fmt.Sprintf("%s.%s", Name, subCmdName) } type CommonCommandParams struct { CVEList []string } func CommonCommandFlagValues(ctx *cli.Context) (*CommonCommandParams, error) { values := &CommonCommandParams{ CVEList: ctx.StringSlice(FlagCVE), } return values, nil } type EpssCommandParams struct { *CommonCommandParams Date time.Time Op string WithHistory bool Limit uint64 Offset uint64 FilterCveIDPattern string FilterDaysSinceAdded uint FilterScoreGt float64 FilterScoreLt float64 FilterPercentileGt float64 FilterPercentileLt float64 FilterOrderRecords epss.OrderType } func EpssCommandFlagValues(ctx *cli.Context) (*EpssCommandParams, error) { common, err := CommonCommandFlagValues(ctx) if err != nil { return nil, err } values := &EpssCommandParams{ CommonCommandParams: common, Op: ctx.String(FlagOp), WithHistory: ctx.Bool(FlagWithHistory), Limit: ctx.Uint64(FlagLimit), Offset: ctx.Uint64(FlagOffset), FilterCveIDPattern: ctx.String(FlagFilterCveIDPattern), FilterDaysSinceAdded: ctx.Uint(FlagFilterDaysSinceAdded), FilterScoreGt: ctx.Float64(FlagFilterScoreGt), FilterScoreLt: ctx.Float64(FlagFilterScoreLt), FilterPercentileGt: ctx.Float64(FlagFilterPercentileGt), FilterPercentileLt: ctx.Float64(FlagFilterPercentileLt), } if !IsValidOp(values.Op) { return nil, fmt.Errorf("invalid operation - %s", values.Op) } if orderStr := ctx.String(FlagFilterOrderRecords); orderStr != "" { if !IsValidOrderRecordsValue(orderStr) { return nil, fmt.Errorf("invalid order records value - %s", orderStr) } values.FilterOrderRecords = OrderType(orderStr) } if dateStr := ctx.String(FlagDate); dateStr != "" { date, err := epss.DateFromString(dateStr) if err != nil { return nil, err } values.Date = date } return values, nil } var CLI = &cli.Command{ Name: Name, Aliases: []string{Alias}, Usage: Usage, Flags: []cli.Flag{ cflag(FlagCVE), }, Subcommands: []*cli.Command{ { Name: EpssCmdName, Usage: EpssCmdNameUsage, Flags: []cli.Flag{ cflag(FlagDate), cflag(FlagOp), cflag(FlagWithHistory), cflag(FlagLimit), cflag(FlagOffset), cflag(FlagFilterCveIDPattern), cflag(FlagFilterDaysSinceAdded), cflag(FlagFilterScoreGt), cflag(FlagFilterScoreLt), cflag(FlagFilterPercentileGt), cflag(FlagFilterPercentileLt), cflag(FlagFilterOrderRecords), }, Action: func(ctx *cli.Context) error { gcvalues, ok := command.CLIContextGet(ctx.Context, command.GlobalParams).(*command.GenericParams) if !ok || gcvalues == nil { return command.ErrNoGlobalParams } xc := app.NewExecutionContext( fullCmdName(EpssCmdName), gcvalues.QuietCLIMode, gcvalues.OutputFormat) cparams, err := EpssCommandFlagValues(ctx) xc.FailOn(err) if len(cparams.CVEList) == 0 && cparams.Op == EpssOpLookup { xc.Fail("EPSS lookup requires, at least, one CVE") } OnEpssCommand(xc, gcvalues, cparams) return nil }, }, }, } ================================================ FILE: pkg/app/master/command/vulnerability/flags.go ================================================ package vulnerability import ( log "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" ) // Vulnerability command flags const ( // Shared Flags FlagCVE = "cve" FlagCVEUsage = "Target vulnerability CVE ID" // EPSS Flags FlagDate = "date" FlagDateUsage = "Date for the EPSS information (YYYY-MM-DD format)" FlagOp = "op" FlagOpUsage = "EPSS operation ('lookup' | 'list')" FlagWithHistory = "with-history" FlagWithHistoryUsage = "Return EPSS results with historical data" FlagLimit = "limit" FlagLimitUsage = "Limit the number of returned records" FlagOffset = "offset" FlagOffsetUsage = "Offset where to start returning records" FlagFilterCveIDPattern = "filter-cve-id-pattern" FlagFilterCveIDPatternUsage = "'CVE ID pattern' ESPP list operation filter" FlagFilterDaysSinceAdded = "filter-days-since-added" FlagFilterDaysSinceAddedUsage = "'days since added' ESPP list operation filter" FlagFilterScoreGt = "filter-score-gt" FlagFilterScoreGtUsage = "'score is greater than' ESPP list operation filter" FlagFilterScoreLt = "filter-score-lt" FlagFilterScoreLtUsage = "'score is less than' ESPP list operation filter" FlagFilterPercentileGt = "filter-percentile-gt" FlagFilterPercentileGtUsage = "'percentile is greater than' ESPP list operation filter" FlagFilterPercentileLt = "filter-percentile-lt" FlagFilterPercentileLtUsage = "'percentile is less than' ESPP list operation filter" FlagFilterOrderRecords = "filter-order-records" FlagFilterOrderRecordsUsage = "'order returned records' ESPP list operation filter ('score-desc' | 'score-asc' | 'percentile-desc' | 'percentile-asc')" ) const ( EpssOpLookup = "lookup" EpssOpList = "list" ) var Flags = map[string]cli.Flag{ FlagCVE: &cli.StringSliceFlag{ Name: FlagCVE, Value: cli.NewStringSlice(), Usage: FlagCVEUsage, EnvVars: []string{"DSLIM_VULN_CVE"}, }, FlagDate: &cli.StringFlag{ Name: FlagDate, Value: "", Usage: FlagDateUsage, EnvVars: []string{"DSLIM_VULN_EPSS_DATE"}, }, FlagOp: &cli.StringFlag{ Name: FlagOp, Value: EpssOpLookup, Usage: FlagOpUsage, EnvVars: []string{"DSLIM_VULN_EPSS_OP"}, }, FlagWithHistory: &cli.BoolFlag{ Name: FlagWithHistory, Value: false, //defaults to false Usage: FlagWithHistoryUsage, EnvVars: []string{"DSLIM_VULN_EPSS_HISTORY"}, }, FlagLimit: &cli.Uint64Flag{ Name: FlagLimit, Value: 10, Usage: FlagLimitUsage, EnvVars: []string{"DSLIM_VULN_LIMIT"}, }, FlagOffset: &cli.Uint64Flag{ Name: FlagOffset, Value: 0, Usage: FlagOffsetUsage, EnvVars: []string{"DSLIM_VULN_OFFSET"}, }, FlagFilterCveIDPattern: &cli.StringFlag{ Name: FlagFilterCveIDPattern, Value: "", Usage: FlagFilterCveIDPatternUsage, EnvVars: []string{"DSLIM_VULN_EPSS_FILTER_CVEID_PAT"}, }, FlagFilterDaysSinceAdded: &cli.UintFlag{ Name: FlagFilterDaysSinceAdded, Value: 0, Usage: FlagFilterDaysSinceAddedUsage, EnvVars: []string{"DSLIM_VULN_EPSS_FILTER_DAYS_SINCE"}, }, FlagFilterScoreGt: &cli.Float64Flag{ Name: FlagFilterScoreGt, Value: 0, Usage: FlagFilterScoreGtUsage, EnvVars: []string{"DSLIM_VULN_EPSS_FILTER_SCORE_GT"}, }, FlagFilterScoreLt: &cli.Float64Flag{ Name: FlagFilterScoreLt, Value: 0, Usage: FlagFilterScoreLtUsage, EnvVars: []string{"DSLIM_VULN_EPSS_FILTER_SCORE_LT"}, }, FlagFilterPercentileGt: &cli.Float64Flag{ Name: FlagFilterPercentileGt, Value: 0, Usage: FlagFilterPercentileGtUsage, EnvVars: []string{"DSLIM_VULN_EPSS_FILTER_PERC_GT"}, }, FlagFilterPercentileLt: &cli.Float64Flag{ Name: FlagFilterPercentileLt, Value: 0, Usage: FlagFilterPercentileLtUsage, EnvVars: []string{"DSLIM_VULN_EPSS_FILTER_PERC_LT"}, }, FlagFilterOrderRecords: &cli.StringFlag{ Name: FlagFilterOrderRecords, Value: "", Usage: FlagFilterOrderRecordsUsage, EnvVars: []string{"DSLIM_VULN_EPSS_FILTER_ORDER"}, }, } func cflag(name string) cli.Flag { cf, ok := Flags[name] if !ok { log.Fatalf("unknown flag='%s'", name) } return cf } ================================================ FILE: pkg/app/master/command/vulnerability/handler_tool_epss.go ================================================ package vulnerability import ( "context" "fmt" "time" "github.com/jedib0t/go-pretty/v6/table" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/app/master/version" cmd "github.com/slimtoolkit/slim/pkg/command" //"github.com/slimtoolkit/slim/pkg/docker/dockerclient" "github.com/slimtoolkit/slim/pkg/report" //"github.com/slimtoolkit/slim/pkg/util/fsutil" "github.com/slimtoolkit/slim/pkg/util/jsonutil" //v "github.com/slimtoolkit/slim/pkg/version" "github.com/slimtoolkit/slim/pkg/vulnerability/epss" "github.com/slimtoolkit/slim/pkg/vulnerability/epss/client" ) const appName = command.AppName type ovars = app.OutVars // OnEpssCommand implements the 'vulnerability epss' command func OnEpssCommand( xc *app.ExecutionContext, gparams *command.GenericParams, cparams *EpssCommandParams) { cmdName := fullCmdName(EpssCmdName) logger := log.WithFields(log.Fields{ "app": appName, "cmd": cmdName, "sub": EpssCmdName}) viChan := version.CheckAsync(gparams.CheckVersion, gparams.InContainer, gparams.IsDSImage) cmdReport := report.NewVulnerabilityCommand(gparams.ReportLocation, gparams.InContainer) cmdReport.State = cmd.StateStarted cmdReport.Operation = cparams.Op xc.Out.State(cmd.StateStarted) xc.Out.Info("params", ovars{ "cmd.params": jsonutil.ToString(cparams), }) /* client, err := dockerclient.New(gparams.ClientConfig) if err == dockerclient.ErrNoDockerInfo { exitMsg := "missing Docker connection info" if gparams.InContainer && gparams.IsDSImage { exitMsg = "make sure to pass the Docker connect parameters to the docker-slim container" } xc.Out.Info("docker.connect.error", ovars{ "message": exitMsg, }) exitCode := command.ECTCommon | command.ECCNoDockerConnectInfo xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Exit(exitCode) } xc.FailOn(err) */ if gparams.Debug { version.Print(xc, cmdName, logger, nil /*client*/, false, gparams.InContainer, gparams.IsDSImage) } epssClient := client.New(client.Options{Debug: gparams.Debug}) switch cparams.Op { case EpssOpList: callOptions := client.FilteredCallOptions{ CallOptions: client.CallOptions{ Date: cparams.Date, PageSize: cparams.Limit, Offset: cparams.Offset, }, CveIDPattern: cparams.FilterCveIDPattern, ScoreGt: cparams.FilterScoreGt, ScoreLt: cparams.FilterScoreLt, PercentileGt: cparams.FilterPercentileGt, PercentileLt: cparams.FilterPercentileLt, DaysSinceAdded: cparams.FilterDaysSinceAdded, OrderRecords: cparams.FilterOrderRecords, } //note: not fetching all pages/records (should have a special flag for it) if cparams.WithHistory { scores, _, err := epssClient.ListScoresWithHistory( context.Background(), callOptions, ) xc.FailOn(err) showScoresWithHistory(xc, scores) } else { scores, _, err := epssClient.ListScores( context.Background(), callOptions, ) xc.FailOn(err) showScores(xc, scores) } default: callOptions := client.CallOptions{ Date: cparams.Date, } if cparams.WithHistory { scores, _, err := epssClient.LookupScoresWithHistory( context.Background(), cparams.CVEList, callOptions) xc.FailOn(err) showScoresWithHistory(xc, scores) } else { scores, _, err := epssClient.LookupScores( context.Background(), cparams.CVEList, callOptions) xc.FailOn(err) showScores(xc, scores) } } xc.Out.State(cmd.StateCompleted) cmdReport.State = cmd.StateCompleted xc.Out.State(cmd.StateDone) vinfo := <-viChan version.PrintCheckVersion(xc, "", vinfo) cmdReport.State = cmd.StateDone if cmdReport.Save() { xc.Out.Info("report", ovars{ "file": cmdReport.ReportLocation(), }) } } func showScores(xc *app.ExecutionContext, scores []*epss.Score) { if xc.Out.Quiet { if xc.Out.OutputFormat == command.OutputFormatJSON { fmt.Printf("%s\n", jsonutil.ToPretty(scores)) return } printScoresTable(scores) return } xc.Out.Info("epss.scores.summary", ovars{"count": len(scores)}) for idx, score := range scores { fields := baseScoreFields(score) fields["index"] = idx xc.Out.Info("epss.score", fields) } } func printScoresTable(scores []*epss.Score) { tw := table.NewWriter() tw.AppendHeader(table.Row{"CVE ID", "Value", "Percentile", "Date"}) for _, score := range scores { tw.AppendRow(table.Row{ score.CVE, score.EPSS, score.Percentile, score.Date.Format(time.DateOnly), }) } tw.SetStyle(table.StyleLight) tw.Style().Options.DrawBorder = false fmt.Printf("%s\n", tw.Render()) } func printScoresWithHistoryTable(scores []*epss.ScoreWithHistory) { tw := table.NewWriter() tw.AppendHeader(table.Row{"CVE ID", "Value", "Percentile", "Date", "History"}) for _, score := range scores { htw := table.NewWriter() htw.AppendHeader(table.Row{"Value", "Percentile", "Date"}) for _, data := range score.History { htw.AppendRow(table.Row{ data.EPSS, data.Percentile, data.Date.Format(time.DateOnly), }) } htw.SetStyle(table.StyleLight) htw.Style().Options.DrawBorder = false tw.AppendRow(table.Row{ score.CVE, score.EPSS, score.Percentile, score.Date.Format(time.DateOnly), htw.Render(), }) } tw.SetStyle(table.StyleLight) tw.Style().Options.DrawBorder = false fmt.Printf("%s\n", tw.Render()) } func showScoresWithHistory(xc *app.ExecutionContext, scores []*epss.ScoreWithHistory) { if xc.Out.Quiet { if xc.Out.OutputFormat == command.OutputFormatJSON { fmt.Printf("%s\n", jsonutil.ToPretty(scores)) return } printScoresWithHistoryTable(scores) return } xc.Out.Info("epss.scores.summary", ovars{"count": len(scores)}) for idx, score := range scores { fields := baseScoreFields(&score.Score) fields["index"] = idx for k, v := range scoreHistoryFields(score.History) { fields[k] = v } xc.Out.Info("epss.score", fields) } } func baseScoreFields(score *epss.Score) app.OutVars { return app.OutVars{ "cve": score.CVE, "value": score.EPSS, "percentile": score.Percentile, "date": score.Date.Format(time.DateOnly), } } func scoreHistoryFields(scoreHistory []epss.ScoreData) app.OutVars { fields := app.OutVars{} for idx, data := range scoreHistory { fields[fmt.Sprintf("history.%d", idx)] = fmt.Sprintf("%s/%f/%f", data.Date.Format(time.DateOnly), data.EPSS, data.Percentile) } return fields } ================================================ FILE: pkg/app/master/command/vulnerability/init/init.go ================================================ package init import ( "github.com/slimtoolkit/slim/pkg/app/master/command/vulnerability" ) func init() { vulnerability.RegisterCommand() } ================================================ FILE: pkg/app/master/command/vulnerability/prompt.go ================================================ package vulnerability import ( "github.com/c-bata/go-prompt" "github.com/slimtoolkit/slim/pkg/app/master/command" ) var CommandSuggestion = prompt.Suggest{ Text: Name, Description: Usage, } var CommandFlagSuggestions = &command.FlagSuggestions{ Names: []prompt.Suggest{ {Text: command.FullFlagName(FlagCVE), Description: FlagCVEUsage}, //including sub-commands here too {Text: EpssCmdName, Description: EpssCmdNameUsage}, }, Values: map[string]command.CompleteValue{}, } ================================================ FILE: pkg/app/master/command/vulnerability/register.go ================================================ package vulnerability import ( "github.com/slimtoolkit/slim/pkg/app/master/command" ) func RegisterCommand() { command.AddCLICommand( Name, CLI, CommandSuggestion, CommandFlagSuggestions) } ================================================ FILE: pkg/app/master/command/xray/cli.go ================================================ package xray import ( "fmt" "strconv" "strings" "github.com/urfave/cli/v2" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/docker/dockerimage" ) const ( Name = "xray" Usage = "Shows what's inside of your container image and reverse engineers its Dockerfile" Alias = "x" ) type DetectOpParam struct { /// Operation is enabled Enabled bool /// Dump/save raw data DumpRaw bool /// Dump raw data to console IsConsoleOut bool /// Dump raw data to directory (otherwise save to an archive file) IsDirOut bool /// Output path (directory or archive path) OutputPath string /// Input parameters for the operation InputParams map[string]string } type CommandParams struct { DetectIdentities *DetectOpParam `json:"detect_identities,omitempty"` DetectScheduledTasks *DetectOpParam `json:"detect_scheduled_tasks,omitempty"` DetectServices *DetectOpParam `json:"detect_services,omitempty"` DetectSystemHooks *DetectOpParam `json:"detect_system_hooks,omitempty"` //todo: migrate simple bool param to DetectOpParam DetectAllCertFiles bool `json:"detect_all_cert_files,omitempty"` DetectAllCertPKFiles bool `json:"detect_all_cert_pks,omitempty"` } var CLI = &cli.Command{ Name: Name, Aliases: []string{Alias}, Usage: Usage, Flags: []cli.Flag{ command.Cflag(command.FlagCommandParamsFile), command.Cflag(command.FlagTarget), command.Cflag(command.FlagPull), command.Cflag(command.FlagDockerConfigPath), command.Cflag(command.FlagRegistryAccount), command.Cflag(command.FlagRegistrySecret), command.Cflag(command.FlagShowPullLogs), cflag(FlagChanges), cflag(FlagChangesOutput), cflag(FlagLayer), cflag(FlagAddImageManifest), cflag(FlagAddImageConfig), cflag(FlagLayerChangesMax), cflag(FlagAllChangesMax), cflag(FlagAddChangesMax), cflag(FlagModifyChangesMax), cflag(FlagDeleteChangesMax), cflag(FlagChangePath), cflag(FlagChangeData), cflag(FlagReuseSavedImage), cflag(FlagTopChangesMax), cflag(FlagChangeMatchLayersOnly), cflag(FlagHashData), cflag(FlagDetectUTF8), cflag(FlagDetectAllCertFiles), cflag(FlagDetectAllCertPKFiles), cflag(FlagDetectDuplicates), cflag(FlagShowDuplicates), cflag(FlagShowSpecialPerms), cflag(FlagChangeDataHash), cflag(FlagExportAllDataArtifacts), cflag(FlagDetectIdentities), cflag(FlagDetectIdentitiesParam), cflag(FlagDetectIdentitiesDumpRaw), cflag(FlagDetectScheduledTasks), cflag(FlagDetectScheduledTasksParam), cflag(FlagDetectScheduledTasksDumpRaw), cflag(FlagDetectServices), cflag(FlagDetectServicesParam), cflag(FlagDetectServicesDumpRaw), cflag(FlagDetectSystemHooks), cflag(FlagDetectSystemHooksParam), cflag(FlagDetectSystemHooksDumpRaw), command.Cflag(command.FlagRemoveFileArtifacts), }, Action: func(ctx *cli.Context) error { gcvalues, ok := command.CLIContextGet(ctx.Context, command.GlobalParams).(*command.GenericParams) if !ok || gcvalues == nil { return command.ErrNoGlobalParams } xc := app.NewExecutionContext( Name, gcvalues.QuietCLIMode, gcvalues.OutputFormat) //NOTE: this is a placeholder to load all command params from a file _ = ctx.String(command.FlagCommandParamsFile) targetRef := ctx.String(command.FlagTarget) if targetRef == "" { if ctx.Args().Len() < 1 { xc.Out.Error("param.target", "missing image ID/name") cli.ShowCommandHelp(ctx, Name) return nil } else { targetRef = ctx.Args().First() } } detectIdentities, err := getDetectOpParam(ctx, FlagDetectIdentities, FlagDetectIdentitiesParam, FlagDetectIdentitiesDumpRaw, detectIdentitiesDumpRawDefault, detectIdentitiesOpParamKeys) if err != nil { xc.Out.Error("param.detect_identities", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } detectScheduledTasks, err := getDetectOpParam(ctx, FlagDetectScheduledTasks, FlagDetectScheduledTasksParam, FlagDetectScheduledTasksDumpRaw, detectScheduledTasksDumpRawDefault, detectScheduledTasksOpParamKeys) if err != nil { xc.Out.Error("param.detect_scheduled_tasks", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } detectServices, err := getDetectOpParam(ctx, FlagDetectServices, FlagDetectServicesParam, FlagDetectServicesDumpRaw, detectServicesDumpRawDefault, detectServicesOpParamKeys) if err != nil { xc.Out.Error("param.detect_services", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } detectSystemHooks, err := getDetectOpParam(ctx, FlagDetectSystemHooks, FlagDetectSystemHooksParam, FlagDetectSystemHooksDumpRaw, detectSystemHooksDumpRawDefault, detectSystemHooksOpParamKeys) if err != nil { xc.Out.Error("param.detect_system_hooks", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } //todo: //1. migrate all param fields to CommandParams //2. load command params from file if command.FlagCommandParamsFile is provided cparams := &CommandParams{ DetectAllCertFiles: ctx.Bool(FlagDetectAllCertFiles), DetectAllCertPKFiles: ctx.Bool(FlagDetectAllCertPKFiles), DetectIdentities: detectIdentities, DetectScheduledTasks: detectScheduledTasks, DetectServices: detectServices, DetectSystemHooks: detectSystemHooks, } xdArtifactsPath := ctx.String(FlagExportAllDataArtifacts) doPull := ctx.Bool(command.FlagPull) dockerConfigPath := ctx.String(command.FlagDockerConfigPath) registryAccount := ctx.String(command.FlagRegistryAccount) registrySecret := ctx.String(command.FlagRegistrySecret) doShowPullLogs := ctx.Bool(command.FlagShowPullLogs) changes, err := parseChangeTypes(ctx.StringSlice(FlagChanges)) if err != nil { xc.Out.Error("param.error.change.types", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } if xdArtifactsPath != "" { //need 'changes all' when exporting all data artifacts changes["delete"] = struct{}{} changes["modify"] = struct{}{} changes["add"] = struct{}{} } rawChangesOutputs := ctx.StringSlice(FlagChangesOutput) if xdArtifactsPath != "" && len(rawChangesOutputs) == 0 { rawChangesOutputs = append(rawChangesOutputs, "report") } changesOutputs, err := parseChangeOutputTypes(rawChangesOutputs) if err != nil { xc.Out.Error("param.error.change.output", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } if xdArtifactsPath != "" { changesOutputs["report"] = struct{}{} } layers, err := command.ParseTokenSet(ctx.StringSlice(FlagLayer)) if err != nil { xc.Out.Error("param.error.layer", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } layerChangesMax := ctx.Int(FlagLayerChangesMax) allChangesMax := ctx.Int(FlagAllChangesMax) addChangesMax := ctx.Int(FlagAddChangesMax) modifyChangesMax := ctx.Int(FlagModifyChangesMax) deleteChangesMax := ctx.Int(FlagDeleteChangesMax) topChangesMax := ctx.Int(FlagTopChangesMax) changePathMatchers, err := parseChangePathMatchers(ctx.StringSlice(FlagChangePath)) if err != nil { xc.Out.Error("param.error.change.path", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } changeDataMatchers, err := parseChangeDataMatchers(ctx.StringSlice(FlagChangeData)) if err != nil { xc.Out.Error("param.error.change.data", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } doAddImageManifest := ctx.Bool(FlagAddImageManifest) if xdArtifactsPath != "" { doAddImageManifest = true } doAddImageConfig := ctx.Bool(FlagAddImageConfig) if xdArtifactsPath != "" { doAddImageConfig = true } doRmFileArtifacts := ctx.Bool(command.FlagRemoveFileArtifacts) doReuseSavedImage := ctx.Bool(FlagReuseSavedImage) doHashData := ctx.Bool(FlagHashData) if xdArtifactsPath != "" { doHashData = true } doDetectDuplicates := ctx.Bool(FlagDetectDuplicates) if doDetectDuplicates { doHashData = true } rawDetectUTF8 := ctx.String(FlagDetectUTF8) if xdArtifactsPath != "" && rawDetectUTF8 == "" { rawDetectUTF8 = "dump:utf8.tgz::10000000" } utf8Detector, err := parseDetectUTF8(rawDetectUTF8) if err != nil { xc.Out.Error("param.error.detect.utf8", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } if utf8Detector != nil && !doHashData { xc.Out.Error("param.error.detect.utf8", "--detect-utf8 requires option --hash-data") xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } doShowDuplicates := ctx.Bool(FlagShowDuplicates) doShowSpecialPerms := ctx.Bool(FlagShowSpecialPerms) changeDataHashMatchers, err := parseChangeDataHashMatchers(ctx.StringSlice(FlagChangeDataHash)) if err != nil { xc.Out.Error("param.error.change.data.hash", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } changeMatchLayersOnly := ctx.Bool(FlagChangeMatchLayersOnly) OnCommand( xc, gcvalues, cparams, targetRef, doPull, dockerConfigPath, registryAccount, registrySecret, doShowPullLogs, changes, changesOutputs, layers, layerChangesMax, allChangesMax, addChangesMax, modifyChangesMax, deleteChangesMax, topChangesMax, changePathMatchers, changeDataMatchers, changeDataHashMatchers, doHashData, doDetectDuplicates, doShowDuplicates, doShowSpecialPerms, changeMatchLayersOnly, doAddImageManifest, doAddImageConfig, doReuseSavedImage, doRmFileArtifacts, utf8Detector, xdArtifactsPath, ) return nil }, } func parseChangeTypes(values []string) (map[string]struct{}, error) { changes := map[string]struct{}{} if len(values) == 0 { values = append(values, "all") } for _, item := range values { switch item { case "none": return nil, nil case "all": changes["delete"] = struct{}{} changes["modify"] = struct{}{} changes["add"] = struct{}{} case "delete": changes["delete"] = struct{}{} case "modify": changes["modify"] = struct{}{} case "add": changes["add"] = struct{}{} } } return changes, nil } func parseChangeOutputTypes(values []string) (map[string]struct{}, error) { outputs := map[string]struct{}{} if len(values) == 0 { values = append(values, "all") } for _, item := range values { switch item { case "all": outputs["report"] = struct{}{} outputs["console"] = struct{}{} case "report": outputs["report"] = struct{}{} case "console": outputs["console"] = struct{}{} } } return outputs, nil } func parseChangeDataMatchers(values []string) ([]*dockerimage.ChangeDataMatcher, error) { var matchers []*dockerimage.ChangeDataMatcher for _, raw := range values { var m dockerimage.ChangeDataMatcher if strings.HasPrefix(raw, "dump:") { parts := strings.SplitN(raw, ":", 4) if len(parts) != 4 { return nil, fmt.Errorf("malformed change data matcher: %s", raw) } m.Dump = true outTarget := strings.TrimSpace(parts[1]) if len(outTarget) == 0 || outTarget == dockerimage.CDMDumpToConsole { m.DumpConsole = true } else { m.DumpDir = outTarget } m.PathPattern = parts[2] m.DataPattern = parts[3] //"dump:output:path_ptrn:data_regex" //"::path_ptrn:data_regex" //":::data_regex" //"data_regex" } else { if !strings.HasPrefix(raw, ":") { m.DataPattern = raw } else { parts := strings.SplitN(raw, ":", 4) if len(parts) != 4 { return nil, fmt.Errorf("malformed change data matcher: %s", raw) } m.PathPattern = parts[2] m.DataPattern = parts[3] } } matchers = append(matchers, &m) } return matchers, nil } func parseChangePathMatchers(values []string) ([]*dockerimage.ChangePathMatcher, error) { var matchers []*dockerimage.ChangePathMatcher for _, raw := range values { var m dockerimage.ChangePathMatcher if strings.HasPrefix(raw, "dump:") { parts := strings.SplitN(raw, ":", 3) if len(parts) != 3 { return nil, fmt.Errorf("malformed change path matcher: %s", raw) } m.Dump = true outTarget := strings.TrimSpace(parts[1]) if len(outTarget) == 0 || outTarget == dockerimage.CDMDumpToConsole { m.DumpConsole = true } else { m.DumpDir = outTarget } m.PathPattern = parts[2] //"dump:output:path_ptrn" //"::path_ptrn" //"path_ptrn" } else { if !strings.HasPrefix(raw, ":") { m.PathPattern = raw } else { parts := strings.SplitN(raw, ":", 3) if len(parts) != 3 { return nil, fmt.Errorf("malformed change path matcher: %s", raw) } m.PathPattern = parts[2] } } matchers = append(matchers, &m) } return matchers, nil } func parseChangeDataHashMatchers(values []string) ([]*dockerimage.ChangeDataHashMatcher, error) { var matchers []*dockerimage.ChangeDataHashMatcher for _, raw := range values { var m dockerimage.ChangeDataHashMatcher if strings.HasPrefix(raw, "dump:") { parts := strings.SplitN(raw, ":", 3) if len(parts) != 3 { return nil, fmt.Errorf("malformed change data hash matcher: %s", raw) } m.Dump = true outTarget := strings.TrimSpace(parts[1]) if len(outTarget) == 0 || outTarget == dockerimage.CDMDumpToConsole { m.DumpConsole = true } else { m.DumpDir = outTarget } m.Hash = strings.ToLower(strings.TrimSpace(parts[2])) //"dump:output:hash" //"::hash" //"hash" } else { if !strings.HasPrefix(raw, ":") { m.Hash = strings.ToLower(strings.TrimSpace(raw)) } else { parts := strings.SplitN(raw, ":", 3) if len(parts) != 3 { return nil, fmt.Errorf("malformed change data hash matcher: %s", raw) } m.Hash = strings.ToLower(strings.TrimSpace(parts[2])) } } matchers = append(matchers, &m) } return matchers, nil } func parseDetectUTF8(raw string) (*dockerimage.UTF8Detector, error) { if raw == "" { return nil, nil } var detector dockerimage.UTF8Detector if raw == "dump" { detector.Dump = true detector.DumpConsole = true } else if strings.HasPrefix(raw, "dump:") { parts := strings.SplitN(raw, ":", 2) if len(parts) != 2 { return nil, fmt.Errorf("malformed find utf8: %s", raw) } detector.Dump = true outTarget := strings.TrimSpace(parts[1]) if len(outTarget) == 0 || outTarget == dockerimage.CDMDumpToConsole { detector.DumpConsole = true } else { if strings.Count(outTarget, ":") == 2 { parts = strings.SplitN(outTarget, ":", 3) if len(parts) != 3 { return nil, fmt.Errorf("malformed find utf8: %s", raw) } outTarget = parts[0] _ = parts[1] // TODO implemement path pattern matcher maxSizeBytes := parts[2] var err error detector.MaxSizeBytes, err = strconv.Atoi(maxSizeBytes) if err != nil { return nil, err } } else if strings.Count(outTarget, ":") == 3 { parts = strings.SplitN(outTarget, ":", 4) if len(parts) != 4 { return nil, fmt.Errorf("malformed find utf8: %s", raw) } outTarget = parts[0] _ = parts[1] // TODO implemement path pattern matcher _ = parts[2] // TODO implemement data regex matcher maxSizeBytes := parts[3] var err error detector.MaxSizeBytes, err = strconv.Atoi(maxSizeBytes) if err != nil { return nil, err } } if strings.HasSuffix(outTarget, ".tgz") || strings.HasSuffix(outTarget, ".tar.gz") { detector.DumpArchive = outTarget dar, err := dockerimage.NewTarWriter(outTarget) if err != nil { return nil, err } detector.Archive = dar } else { detector.DumpDir = outTarget } } } else { if raw != "true" { return nil, nil } } //TODO: //get detector filters if we need to find/extract only a subset of the utf8 return &detector, nil } func getDetectOpParam( ctx *cli.Context, enableFlag string, kvListFlag string, dumpRawFlag string, defaultDumpRawFile string, validOpParamKeys map[string]struct{}) (*DetectOpParam, error) { var param DetectOpParam enable := ctx.Bool(enableFlag) kvList := ctx.StringSlice(kvListFlag) dumpRaw := ctx.String(dumpRawFlag) if enable == false && len(kvList) == 0 && dumpRaw == "" { return ¶m, nil } param.Enabled = true param.InputParams = map[string]string{} for _, raw := range kvList { raw = strings.TrimSpace(raw) parts := strings.SplitN(raw, "=", 2) if len(parts) != 2 { continue } if parts[0] == "" || parts[1] == "" { continue } if _, found := validOpParamKeys[parts[0]]; !found { continue } param.InputParams[parts[0]] = parts[1] } if dumpRaw != "" { param.DumpRaw = true switch dumpRaw { case ".": param.OutputPath = defaultDumpRawFile return ¶m, nil case "console": param.IsConsoleOut = true return ¶m, nil case "no": return ¶m, nil } parts := strings.SplitN(dumpRaw, ":", 2) if parts[1] == "" { param.IsConsoleOut = true return ¶m, nil } switch parts[0] { case "dir": param.IsDirOut = true default: //"file" param.OutputPath = parts[1] } } return ¶m, nil } const ( detectIdentitiesDumpRawDefault = "./raw-identities.tar" detectScheduledTasksDumpRawDefault = "./raw-scheduled-tasks.tar" detectServicesDumpRawDefault = "./raw-services.tar" detectSystemHooksDumpRawDefault = "./raw-system-hooks.tar" ) var ( detectIdentitiesOpParamKeys = map[string]struct{}{} //no keys yet detectScheduledTasksOpParamKeys = map[string]struct{}{} //no keys yet detectServicesOpParamKeys = map[string]struct{}{} //no keys yet detectSystemHooksOpParamKeys = map[string]struct{}{} //no keys yet ) ================================================ FILE: pkg/app/master/command/xray/flags.go ================================================ package xray import ( log "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" ) // Xray command flag names const ( FlagChanges = "changes" FlagChangesOutput = "changes-output" FlagLayer = "layer" FlagAddImageManifest = "add-image-manifest" FlagAddImageConfig = "add-image-config" FlagLayerChangesMax = "layer-changes-max" FlagAllChangesMax = "all-changes-max" FlagAddChangesMax = "add-changes-max" FlagModifyChangesMax = "modify-changes-max" FlagDeleteChangesMax = "delete-changes-max" FlagChangePath = "change-path" FlagChangeData = "change-data" FlagChangeDataHash = "change-data-hash" FlagReuseSavedImage = "reuse-saved-image" FlagTopChangesMax = "top-changes-max" FlagHashData = "hash-data" FlagDetectUTF8 = "detect-utf8" FlagDetectDuplicates = "detect-duplicates" FlagShowDuplicates = "show-duplicates" FlagShowSpecialPerms = "show-special-perms" FlagChangeMatchLayersOnly = "change-match-layers-only" FlagExportAllDataArtifacts = "export-all-data-artifacts" FlagDetectAllCertFiles = "detect-all-certs" FlagDetectAllCertPKFiles = "detect-all-cert-pks" FlagDetectIdentities = "detect-identities" FlagDetectIdentitiesParam = "detect-identities-param" FlagDetectIdentitiesDumpRaw = "detect-identities-dump-raw" FlagDetectScheduledTasks = "detect-scheduled-tasks" FlagDetectScheduledTasksParam = "detect-scheduled-tasks-param" FlagDetectScheduledTasksDumpRaw = "detect-scheduled-tasks-dump-raw" FlagDetectServices = "detect-services" FlagDetectServicesParam = "detect-services-param" FlagDetectServicesDumpRaw = "detect-services-dump-raw" FlagDetectSystemHooks = "detect-system-hooks" FlagDetectSystemHooksParam = "detect-system-hooks-param" FlagDetectSystemHooksDumpRaw = "detect-system-hooks-dump-raw" ) // Xray command flag usage info const ( FlagChangesUsage = "Show layer change details for the selected change type (values: none, all, delete, modify, add)" FlagChangesOutputUsage = "Where to show the changes (values: all, report, console)" FlagLayerUsage = "Show details for the selected layer (using layer index or ID)" FlagAddImageManifestUsage = "Add raw image manifest to the command execution report file" FlagAddImageConfigUsage = "Add raw image config object to the command execution report file" FlagLayerChangesMaxUsage = "Maximum number of changes to show for each layer" FlagAllChangesMaxUsage = "Maximum number of changes to show for all layers" FlagAddChangesMaxUsage = "Maximum number of 'add' changes to show for all layers" FlagModifyChangesMaxUsage = "Maximum number of 'modify' changes to show for all layers" FlagDeleteChangesMaxUsage = "Maximum number of 'delete' changes to show for all layers" FlagChangePathUsage = "Include changes for the files that match the path pattern (Glob/Match in Go and **)" FlagChangeDataUsage = "Include changes for the files that match the data pattern (regex)" FlagReuseSavedImageUsage = "Reuse saved container image" FlagTopChangesMaxUsage = "Maximum number of top changes to track" FlagChangeDataHashUsage = "Include changes for the files that match the provided data hashes (sha1)" FlagHashDataUsage = "Generate file data hashes" FlagDetectUTF8Usage = "Detect utf8 files and optionally extract the discovered utf8 file content" FlagDetectDuplicatesUsage = "Detect duplicate files based on their hashes" FlagShowDuplicatesUsage = "Show discovered duplicate file paths" FlagShowSpecialPermsUsage = "Show files with special permissions (setuid,setgid,sticky)" FlagChangeMatchLayersOnlyUsage = "Show only layers with change matches" FlagExportAllDataArtifactsUsage = "TAR archive file path to export all text data artifacts (if value is set to `.` then the archive file path defaults to `./data-artifacts.tar`)" FlagDetectAllCertFilesUsage = "Detect all certificate files" FlagDetectAllCertPKFilesUsage = "Detect all certificate private key files" FlagDetectIdentitiesUsage = "Detect system identities (users, groups) and their properties" FlagDetectIdentitiesParamUsage = "Input parameters for system identities detection" FlagDetectIdentitiesDumpRawUsage = "Raw data dump options for system identities detection (values: no, console, directory or a tar archive file path where setting value to `.` defaults tar file path to `./raw-identities.tar`" FlagDetectScheduledTasksUsage = "Detect scheduled tasks and their properties" FlagDetectScheduledTasksParamUsage = "Input parameters for scheduled tasks detection" FlagDetectScheduledTasksDumpRawUsage = "Raw data dump options for scheduled tasks detection (values: `no`, `console`, directory or a tar archive file path where setting value to `.` defaults tar file path to `./raw-scheduled-tasks.tar`" FlagDetectServicesUsage = "Detect services and their properties" FlagDetectServicesParamUsage = "Input parameters for services detection" FlagDetectServicesDumpRawUsage = "Raw data dump options for services detection (values: no, console, directory or a tar archive file path where setting value to `.` defaults tar file path to `./raw-services.tar`" FlagDetectSystemHooksUsage = "Detect system hooks and their properties" FlagDetectSystemHooksParamUsage = "Input parameters for system hooks detection" FlagDetectSystemHooksDumpRawUsage = "Raw data dump options for system hooks detection (values: no, console, directory or a tar archive file path where setting value to `.` defaults tar file path to `./raw-system-hooks.tar`" ) var Flags = map[string]cli.Flag{ FlagChanges: &cli.StringSliceFlag{ Name: FlagChanges, Value: cli.NewStringSlice(""), Usage: FlagChangesUsage, EnvVars: []string{"DSLIM_CHANGES"}, }, FlagChangesOutput: &cli.StringSliceFlag{ Name: FlagChangesOutput, Value: cli.NewStringSlice("all"), Usage: FlagChangesOutputUsage, EnvVars: []string{"DSLIM_CHANGES_OUTPUT"}, }, FlagLayer: &cli.StringSliceFlag{ Name: FlagLayer, Value: cli.NewStringSlice(), Usage: FlagLayerUsage, EnvVars: []string{"DSLIM_LAYER"}, }, FlagAddImageManifest: &cli.BoolFlag{ Name: FlagAddImageManifest, Usage: FlagAddImageManifestUsage, EnvVars: []string{"DSLIM_XRAY_IMAGE_MANIFEST"}, }, FlagAddImageConfig: &cli.BoolFlag{ Name: FlagAddImageConfig, Usage: FlagAddImageConfigUsage, EnvVars: []string{"DSLIM_XRAY_IMAGE_CONFIG"}, }, FlagLayerChangesMax: &cli.IntFlag{ Name: FlagLayerChangesMax, Value: -1, Usage: FlagLayerChangesMaxUsage, EnvVars: []string{"DSLIM_XRAY_LAYER_CHANGES_MAX"}, }, FlagAllChangesMax: &cli.IntFlag{ Name: FlagAllChangesMax, Value: -1, Usage: FlagAllChangesMaxUsage, EnvVars: []string{"DSLIM_XRAY_ALL_CHANGES_MAX"}, }, FlagAddChangesMax: &cli.IntFlag{ Name: FlagAddChangesMax, Value: -1, Usage: FlagAddChangesMaxUsage, EnvVars: []string{"DSLIM_XRAY_ADD_CHANGES_MAX"}, }, FlagModifyChangesMax: &cli.IntFlag{ Name: FlagModifyChangesMax, Value: -1, Usage: FlagModifyChangesMaxUsage, EnvVars: []string{"DSLIM_XRAY_MODIFY_CHANGES_MAX"}, }, FlagDeleteChangesMax: &cli.IntFlag{ Name: FlagDeleteChangesMax, Value: -1, Usage: FlagDeleteChangesMaxUsage, EnvVars: []string{"DSLIM_XRAY_DELETE_CHANGES_MAX"}, }, FlagChangePath: &cli.StringSliceFlag{ Name: FlagChangePath, Value: cli.NewStringSlice(), Usage: FlagChangePathUsage, EnvVars: []string{"DSLIM_XRAY_CHANGE_PATH"}, }, FlagChangeData: &cli.StringSliceFlag{ Name: FlagChangeData, Value: cli.NewStringSlice(), Usage: FlagChangeDataUsage, EnvVars: []string{"DSLIM_XRAY_CHANGE_DATA"}, }, FlagReuseSavedImage: &cli.BoolFlag{ Name: FlagReuseSavedImage, Value: true, //enabled by default Usage: FlagReuseSavedImageUsage, EnvVars: []string{"DSLIM_XRAY_REUSE_SAVED"}, }, FlagTopChangesMax: &cli.IntFlag{ Name: FlagTopChangesMax, Value: 20, Usage: FlagTopChangesMaxUsage, EnvVars: []string{"DSLIM_XRAY_TOP_CHANGES_MAX"}, }, FlagHashData: &cli.BoolFlag{ Name: FlagHashData, Usage: FlagHashDataUsage, EnvVars: []string{"DSLIM_XRAY_HASH_DATA"}, }, FlagDetectUTF8: &cli.StringFlag{ Name: FlagDetectUTF8, Usage: FlagDetectUTF8Usage, EnvVars: []string{"DSLIM_XRAY_DETECT_UTF8"}, }, FlagDetectDuplicates: &cli.BoolFlag{ Name: FlagDetectDuplicates, Value: true, //enabled by default Usage: FlagDetectDuplicatesUsage, EnvVars: []string{"DSLIM_XRAY_DETECT_DUP"}, }, FlagShowDuplicates: &cli.BoolFlag{ Name: FlagShowDuplicates, Usage: FlagShowDuplicatesUsage, EnvVars: []string{"DSLIM_XRAY_SHOW_DUP"}, }, FlagShowSpecialPerms: &cli.BoolFlag{ Name: FlagShowSpecialPerms, Value: true, //enabled by default Usage: FlagShowSpecialPermsUsage, EnvVars: []string{"DSLIM_XRAY_SHOW_SPECIAL"}, }, FlagChangeDataHash: &cli.StringSliceFlag{ Name: FlagChangeDataHash, Value: cli.NewStringSlice(), Usage: FlagChangeDataHashUsage, EnvVars: []string{"DSLIM_XRAY_CHANGE_DATA_HASH"}, }, FlagChangeMatchLayersOnly: &cli.BoolFlag{ Name: FlagChangeMatchLayersOnly, Usage: FlagChangeMatchLayersOnlyUsage, EnvVars: []string{"DSLIM_XRAY_CHANGE_MATCH_LAYERS_ONLY"}, }, FlagExportAllDataArtifacts: &cli.StringFlag{ Name: FlagExportAllDataArtifacts, Usage: FlagExportAllDataArtifactsUsage, EnvVars: []string{"DSLIM_XRAY_EXPORT_ALL_DARTIFACTS"}, }, FlagDetectAllCertFiles: &cli.BoolFlag{ Name: FlagDetectAllCertFiles, Usage: FlagDetectAllCertFilesUsage, EnvVars: []string{"DSLIM_XRAY_DETECT_ALL_CERTS"}, }, FlagDetectAllCertPKFiles: &cli.BoolFlag{ Name: FlagDetectAllCertPKFiles, Usage: FlagDetectAllCertPKFilesUsage, EnvVars: []string{"DSLIM_XRAY_DETECT_ALL_CERT_PKS"}, }, FlagDetectIdentities: &cli.BoolFlag{ Name: FlagDetectIdentities, Value: true, //enabled by default Usage: FlagDetectIdentitiesUsage, EnvVars: []string{"DSLIM_XRAY_DETECT_IDENTITIES"}, }, FlagDetectIdentitiesParam: &cli.StringSliceFlag{ Name: FlagDetectIdentitiesParam, Value: cli.NewStringSlice(), Usage: FlagDetectIdentitiesParamUsage, EnvVars: []string{"DSLIM_XRAY_DETECT_IDENTITIES_PARAM"}, }, FlagDetectIdentitiesDumpRaw: &cli.StringFlag{ Name: FlagDetectIdentitiesDumpRaw, Usage: FlagDetectIdentitiesDumpRawUsage, EnvVars: []string{"DSLIM_XRAY_DETECT_IDENTITIES_DUMP_RAW"}, }, FlagDetectScheduledTasks: &cli.BoolFlag{ Name: FlagDetectScheduledTasks, Value: true, //enabled by default Usage: FlagDetectScheduledTasksUsage, EnvVars: []string{"DSLIM_XRAY_DETECT_SCHEDULED_TASKS"}, }, FlagDetectScheduledTasksParam: &cli.StringSliceFlag{ Name: FlagDetectScheduledTasksParam, Value: cli.NewStringSlice(), Usage: FlagDetectScheduledTasksParamUsage, EnvVars: []string{"DSLIM_XRAY_DETECT_SCHEDULED_TASKS_PARAM"}, }, FlagDetectScheduledTasksDumpRaw: &cli.StringFlag{ Name: FlagDetectScheduledTasksDumpRaw, Usage: FlagDetectScheduledTasksDumpRawUsage, EnvVars: []string{"DSLIM_XRAY_DETECT_SCHEDULED_TASKS_DUMP_RAW"}, }, FlagDetectServices: &cli.BoolFlag{ Name: FlagDetectServices, Value: true, //enabled by default Usage: FlagDetectServicesUsage, EnvVars: []string{"DSLIM_XRAY_DETECT_SERVICES"}, }, FlagDetectServicesParam: &cli.StringSliceFlag{ Name: FlagDetectServicesParam, Value: cli.NewStringSlice(), Usage: FlagDetectServicesParamUsage, EnvVars: []string{"DSLIM_XRAY_DETECT_SERVICES_PARAM"}, }, FlagDetectServicesDumpRaw: &cli.StringFlag{ Name: FlagDetectServicesDumpRaw, Usage: FlagDetectServicesDumpRawUsage, EnvVars: []string{"DSLIM_XRAY_DETECT_SERVICES_DUMP_RAW"}, }, FlagDetectSystemHooks: &cli.BoolFlag{ Name: FlagDetectSystemHooks, Value: true, //enabled by default Usage: FlagDetectSystemHooksUsage, EnvVars: []string{"DSLIM_XRAY_DETECT_SYSTEM_HOOKS"}, }, FlagDetectSystemHooksParam: &cli.StringSliceFlag{ Name: FlagDetectSystemHooksParam, Value: cli.NewStringSlice(), Usage: FlagDetectSystemHooksParamUsage, EnvVars: []string{"DSLIM_XRAY_DETECT_SYSTEM_HOOKS_PARAM"}, }, FlagDetectSystemHooksDumpRaw: &cli.StringFlag{ Name: FlagDetectSystemHooksDumpRaw, Usage: FlagDetectSystemHooksDumpRawUsage, EnvVars: []string{"DSLIM_XRAY_DETECT_SYSTEM_HOOKS_DUMP_RAW"}, }, } func cflag(name string) cli.Flag { cf, ok := Flags[name] if !ok { log.Fatalf("unknown flag='%s'", name) } return cf } ================================================ FILE: pkg/app/master/command/xray/handler.go ================================================ package xray import ( "fmt" "path/filepath" "regexp" "strings" "time" //"github.com/bmatcuk/doublestar/v3" "github.com/dustin/go-humanize" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/app/master/inspectors/image" "github.com/slimtoolkit/slim/pkg/app/master/version" cmd "github.com/slimtoolkit/slim/pkg/command" "github.com/slimtoolkit/slim/pkg/docker/buildpackinfo" "github.com/slimtoolkit/slim/pkg/docker/dockerclient" "github.com/slimtoolkit/slim/pkg/docker/dockerimage" "github.com/slimtoolkit/slim/pkg/docker/dockerutil" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/util/errutil" "github.com/slimtoolkit/slim/pkg/util/fsutil" v "github.com/slimtoolkit/slim/pkg/version" ) const appName = command.AppName type ovars = app.OutVars // Xray command exit codes const ( ecxOther = iota + 1 ) const ( fatDockerfileName = "Dockerfile.fat" ) const ( // image.ref.name and image.version are supposed to represent // the corresponding target image identity properties // but sometimes it's base image properties // (when the builder doesn't set its own values inheriting the base image tags) ociLabelImageName = "org.opencontainers.image.ref.name" ociLabelImageVersion = "org.opencontainers.image.version" lsLabelImageVersion = "org.label-schema.version" // image build source info ociLabelImageSource = "org.opencontainers.image.source" lsLabelImageSource = "org.label-schema.vcs-url" //SCM repo revision info (could be commit hash, tag, branch) ociLabelImageRevision = "org.opencontainers.image.revision" lsLabelImageRevision = "org.label-schema.vcs-ref" ociLabelImageURL = "org.opencontainers.image.url" lsLabelImageURL = "org.label-schema.url" ociLabelImageTitle = "org.opencontainers.image.title" lsLabelImageTitle = "org.label-schema.name" ociLabelImageDesc = "org.opencontainers.image.description" lsLabelImageDesc = "org.label-schema.description" ociLabelImageDocs = "org.opencontainers.image.documentation" lsLabelImageDocs = "org.label-schema.usage" ociLabelImageVendor = "org.opencontainers.image.vendor" //high level 'author' info lsLabelImageVendor = "org.label-schema.vendor" ociLabelImageAuthors = "org.opencontainers.image.authors" ociLabelBaseImageDigest = "org.opencontainers.image.base.digest" ociLabelBaseImageName = "org.opencontainers.image.base.name" azureLabelBaseImageName = "image.base.ref.name" azureLabelBaseImageDigest = "image.base.digest" lsLabelDockerCmd = "org.label-schema.docker.cmd" lsLabelDockerCmdDevel = "org.label-schema.docker.cmd.devel" lsLabelDockerCmdTest = "org.label-schema.docker.cmd.test" lsLabelDockerCmdDebug = "org.label-schema.docker.debug" lsLabelDockerCmdHelp = "org.label-schema.docker.cmd.help" lsLabelDockerParams = "org.label-schema.docker.params" ) // OCI label info: // https://specs.opencontainers.org/image-spec/annotations/ // OnCommand implements the 'xray' command func OnCommand( xc *app.ExecutionContext, gparams *command.GenericParams, cparams *CommandParams, targetRef string, doPull bool, dockerConfigPath string, registryAccount string, registrySecret string, doShowPullLogs bool, changes map[string]struct{}, changesOutputs map[string]struct{}, layers map[string]struct{}, layerChangesMax int, allChangesMax int, addChangesMax int, modifyChangesMax int, deleteChangesMax int, topChangesMax int, changePathMatchers []*dockerimage.ChangePathMatcher, changeDataMatcherList []*dockerimage.ChangeDataMatcher, changeDataHashMatcherList []*dockerimage.ChangeDataHashMatcher, doHashData bool, doDetectDuplicates bool, doShowDuplicates bool, doShowSpecialPerms bool, changeMatchLayersOnly bool, doAddImageManifest bool, doAddImageConfig bool, doReuseSavedImage bool, doRmFileArtifacts bool, utf8Detector *dockerimage.UTF8Detector, xdArtifactsPath string, ) { const cmdName = Name logger := log.WithFields(log.Fields{"app": appName, "cmd": cmdName}) changeDataMatchers := map[string]*dockerimage.ChangeDataMatcher{} for _, cdm := range changeDataMatcherList { matcher, err := regexp.Compile(cdm.DataPattern) errutil.FailOn(err) cdm.Matcher = matcher changeDataMatchers[cdm.DataPattern] = cdm } changeDataHashMatchers := map[string]*dockerimage.ChangeDataHashMatcher{} for _, cdhm := range changeDataHashMatcherList { changeDataHashMatchers[cdhm.Hash] = cdhm } viChan := version.CheckAsync(gparams.CheckVersion, gparams.InContainer, gparams.IsDSImage) cmdReport := report.NewXrayCommand(gparams.ReportLocation, gparams.InContainer) cmdReport.State = cmd.StateStarted cmdReport.TargetReference = targetRef xc.Out.State(cmd.StateStarted) xc.Out.Info("params", ovars{ "target": targetRef, "add-image-manifest": doAddImageManifest, "add-image-config": doAddImageConfig, "rm-file-artifacts": doRmFileArtifacts, }) client, err := dockerclient.New(gparams.ClientConfig) if err == dockerclient.ErrNoDockerInfo { exitMsg := "missing Docker connection info" if gparams.InContainer && gparams.IsDSImage { exitMsg = "make sure to pass the Docker connect parameters to the docker-slim container" } xc.Out.Error("docker.connect.error", exitMsg) exitCode := command.ECTCommon | command.ECCNoDockerConnectInfo xc.Out.State("exited", ovars{ "exit.code": exitCode, "version": v.Current(), "location": fsutil.ExeDir(), }) xc.Exit(exitCode) } errutil.FailOn(err) if gparams.Debug { version.Print(xc, cmdName, logger, client, false, gparams.InContainer, gparams.IsDSImage) } imageInspector, err := image.NewInspector(client, targetRef) errutil.FailOn(err) noImage, err := imageInspector.NoImage() errutil.FailOn(err) if noImage { if doPull { xc.Out.Info("target.image", ovars{ "status": "not.found", "image": targetRef, "message": "trying to pull target image", }) err := imageInspector.Pull(doShowPullLogs, dockerConfigPath, registryAccount, registrySecret) errutil.FailOn(err) } else { xc.Out.Error("image.not.found", "make sure the target image already exists locally (use --pull flag to auto-download it from registry)") exitCode := command.ECTCommon | command.ECCImageNotFound xc.Out.State("exited", ovars{ "exit.code": exitCode, }) xc.Exit(exitCode) } } //refresh the target refs targetRef = imageInspector.ImageRef cmdReport.TargetReference = imageInspector.ImageRef xc.Out.State("image.api.inspection.start") logger.Info("inspecting 'fat' image metadata...") err = imageInspector.Inspect() errutil.FailOn(err) localVolumePath, artifactLocation, statePath, stateKey := fsutil.PrepareImageStateDirs(gparams.StatePath, imageInspector.ImageInfo.ID) imageInspector.ArtifactLocation = artifactLocation logger.Debugf("localVolumePath=%v, artifactLocation=%v, statePath=%v, stateKey=%v", localVolumePath, artifactLocation, statePath, stateKey) xc.Out.Info("image", ovars{ "id": imageInspector.ImageInfo.ID, "size.bytes": imageInspector.ImageInfo.VirtualSize, "size.human": humanize.Bytes(uint64(imageInspector.ImageInfo.VirtualSize)), "architecture": imageInspector.ImageInfo.Architecture, }) logger.Info("processing 'fat' image info...") err = imageInspector.ProcessCollectedData() errutil.FailOn(err) if imageInspector.DockerfileInfo != nil { if imageInspector.DockerfileInfo.ExeUser != "" { xc.Out.Info("image.users", ovars{ "exe": imageInspector.DockerfileInfo.ExeUser, "all": strings.Join(imageInspector.DockerfileInfo.AllUsers, ","), }) } if len(imageInspector.DockerfileInfo.ImageStack) > 0 { cmdReport.ImageStack = imageInspector.DockerfileInfo.ImageStack for idx, imageInfo := range imageInspector.DockerfileInfo.ImageStack { xc.Out.Info("image.stack", ovars{ "index": idx, "name": imageInfo.FullName, "id": imageInfo.ID, "instructions": len(imageInfo.Instructions), "message": "see report file for details", }) } } if len(imageInspector.DockerfileInfo.ExposedPorts) > 0 { xc.Out.Info("image.exposed_ports", ovars{ "list": strings.Join(imageInspector.DockerfileInfo.ExposedPorts, ","), }) } } imgIdentity := dockerutil.ImageToIdentity(imageInspector.ImageInfo) cmdReport.SourceImage = report.ImageMetadata{ Identity: report.ImageIdentity{ ID: imgIdentity.ID, Tags: imgIdentity.ShortTags, Names: imgIdentity.RepoTags, Digests: imgIdentity.ShortDigests, FullDigests: imgIdentity.RepoDigests, }, Size: imageInspector.ImageInfo.VirtualSize, SizeHuman: humanize.Bytes(uint64(imageInspector.ImageInfo.VirtualSize)), CreateTime: imageInspector.ImageInfo.Created.UTC().Format(time.RFC3339), Author: imageInspector.ImageInfo.Author, DockerVersion: imageInspector.ImageInfo.DockerVersion, Architecture: imageInspector.ImageInfo.Architecture, User: imageInspector.ImageInfo.Config.User, OS: imageInspector.ImageInfo.OS, WorkDir: imageInspector.ImageInfo.Config.WorkingDir, ContainerEntry: report.ContainerEntryInfo{ Entrypoint: imageInspector.ImageInfo.Config.Entrypoint, Cmd: imageInspector.ImageInfo.Config.Cmd, }, InheritedInstructions: imageInspector.ImageInfo.Config.OnBuild, } cmdReport.SourceImage.EnvVars = imageInspector.ImageInfo.Config.Env for k := range imageInspector.ImageInfo.Config.ExposedPorts { cmdReport.SourceImage.ExposedPorts = append(cmdReport.SourceImage.ExposedPorts, string(k)) } for k := range imageInspector.ImageInfo.Config.Volumes { cmdReport.SourceImage.Volumes = append(cmdReport.SourceImage.Volumes, k) } cmdReport.SourceImage.Labels = imageInspector.ImageInfo.Config.Labels maintainers := map[string]struct{}{} if buildpackinfo.HasBuildbackLabels(imageInspector.ImageInfo.Config.Labels) { for k, v := range imageInspector.ImageInfo.Config.Labels { if k == "io.buildpacks.stack.maintainer" { buildpackMaintainer := v + " (buildpack)" maintainers[buildpackMaintainer] = struct{}{} } } bpStack := imageInspector.ImageInfo.Config.Labels[buildpackinfo.LabelKeyStackID] cmdReport.SourceImage.Buildpack = &report.BuildpackInfo{ Stack: bpStack, } } for _, m := range imageInspector.DockerfileInfo.Maintainers { maintainers[m] = struct{}{} } for k, v := range cmdReport.SourceImage.Labels { if strings.ToLower(k) == "maintainer" || strings.ToLower(k) == "author" || strings.ToLower(k) == "authors" || k == ociLabelImageAuthors { maintainers[v] = struct{}{} } else { switch k { case ociLabelBaseImageDigest, azureLabelBaseImageDigest: cmdReport.SourceImage.BaseImageDigest = v case ociLabelBaseImageName, azureLabelBaseImageName: cmdReport.SourceImage.BaseImageName = v } } } for m := range maintainers { cmdReport.SourceImage.Maintainers = append(cmdReport.SourceImage.Maintainers, m) } cmdReport.ArtifactLocation = imageInspector.ArtifactLocation xc.Out.State("image.api.inspection.done") xc.Out.State("image.data.inspection.start") imageID := dockerutil.CleanImageID(imageInspector.ImageInfo.ID) iaName := fmt.Sprintf("%s.tar", imageID) iaPath := filepath.Join(localVolumePath, "image", iaName) iaPathReady := fmt.Sprintf("%s.ready", iaPath) var doSave bool if fsutil.IsRegularFile(iaPath) { if !doReuseSavedImage { doSave = true } if !fsutil.Exists(iaPathReady) { doSave = true } } else { doSave = true } if doSave { if fsutil.Exists(iaPathReady) { fsutil.Remove(iaPathReady) } xc.Out.Info("image.data.inspection.save.image.start") err = dockerutil.SaveImage(client, imageID, iaPath, false, false) errutil.FailOn(err) err = fsutil.Touch(iaPathReady) errutil.WarnOn(err) xc.Out.Info("image.data.inspection.save.image.end") } else { logger.Debugf("exported image already exists - %s", iaPath) } pp := &dockerimage.ProcessorParams{ DetectIdentities: &dockerimage.DetectOpParam{ Enabled: cparams.DetectIdentities.Enabled, DumpRaw: cparams.DetectIdentities.DumpRaw, IsConsoleOut: cparams.DetectIdentities.IsConsoleOut, IsDirOut: cparams.DetectIdentities.IsDirOut, OutputPath: cparams.DetectIdentities.OutputPath, InputParams: cparams.DetectIdentities.InputParams, }, DetectAllCertFiles: cparams.DetectAllCertFiles, DetectAllCertPKFiles: cparams.DetectAllCertPKFiles, } xc.Out.Info("image.data.inspection.process.image.start") imagePkg, err := dockerimage.LoadPackage( iaPath, imageID, false, topChangesMax, doHashData, doDetectDuplicates, changeDataHashMatchers, changePathMatchers, changeDataMatchers, utf8Detector, pp) errutil.FailOn(err) xc.Out.Info("image.data.inspection.process.image.end") if utf8Detector != nil { errutil.FailOn(utf8Detector.Close()) } xc.Out.State("image.data.inspection.done") if len(imageInspector.DockerfileInfo.AllInstructions) == len(imagePkg.Config.History) { for instIdx, instInfo := range imageInspector.DockerfileInfo.AllInstructions { instInfo.Author = imagePkg.Config.History[instIdx].Author instInfo.EmptyLayer = imagePkg.Config.History[instIdx].EmptyLayer instInfo.LayerID = imagePkg.Config.History[instIdx].LayerID instInfo.LayerIndex = imagePkg.Config.History[instIdx].LayerIndex instInfo.LayerFSDiffID = imagePkg.Config.History[instIdx].LayerFSDiffID } } else { logger.Debugf("history instruction set size mismatch - %v/%v ", len(imageInspector.DockerfileInfo.AllInstructions), len(imagePkg.Config.History)) } allEntryParams := append(cmdReport.SourceImage.ContainerEntry.Entrypoint, cmdReport.SourceImage.ContainerEntry.Cmd...) if len(allEntryParams) > 0 { cmdReport.SourceImage.ContainerEntry.ExePath = allEntryParams[0] cmdReport.SourceImage.ContainerEntry.ExeArgs = allEntryParams[1:] //fix up exe path if relative if !strings.HasPrefix(cmdReport.SourceImage.ContainerEntry.ExePath, "/") { //check relative path if strings.HasPrefix(cmdReport.SourceImage.ContainerEntry.ExePath, "./") || strings.HasPrefix(cmdReport.SourceImage.ContainerEntry.ExePath, "../") { fullExePath := filepath.Join(cmdReport.SourceImage.WorkDir, cmdReport.SourceImage.ContainerEntry.ExePath) object := findChange(imagePkg, fullExePath) if object != nil { cmdReport.SourceImage.ContainerEntry.FullExePath = &report.ContainerFileInfo{ Name: fullExePath, Layer: object.LayerIndex, } } } else { //check env paths var envPaths []string for _, envInfo := range cmdReport.SourceImage.EnvVars { if strings.HasPrefix(envInfo, "PATH=") { envInfo = strings.TrimPrefix(envInfo, "PATH=") envPaths = strings.Split(envInfo, ":") break } } for _, envPath := range envPaths { fullExePath := fmt.Sprintf("%s/%s", envPath, cmdReport.SourceImage.ContainerEntry.ExePath) object := findChange(imagePkg, fullExePath) if object != nil { cmdReport.SourceImage.ContainerEntry.FullExePath = &report.ContainerFileInfo{ Name: fullExePath, Layer: object.LayerIndex, } break } } } } else { object := findChange(imagePkg, cmdReport.SourceImage.ContainerEntry.ExePath) if object != nil { cmdReport.SourceImage.ContainerEntry.FullExePath = &report.ContainerFileInfo{ Name: cmdReport.SourceImage.ContainerEntry.ExePath, Layer: object.LayerIndex, } } } //find files in exe args for _, exeArg := range cmdReport.SourceImage.ContainerEntry.ExeArgs { //if starts with / assume a full path and lookup/find in the layer references //otherwise try to use workdir and lookup/find in the layer references if strings.HasPrefix(exeArg, "-") { //skip flag names (might have false positives) continue } var filePath string if strings.HasPrefix(exeArg, "/") { filePath = exeArg } else { //not a perfect way to find potential files //but better than nothing filePath = fmt.Sprintf("%s/%s", cmdReport.SourceImage.WorkDir, exeArg) } object := findChange(imagePkg, filePath) if object != nil { cmdReport.SourceImage.ContainerEntry.ArgFiles = append(cmdReport.SourceImage.ContainerEntry.ArgFiles, &report.ContainerFileInfo{ Name: filePath, Layer: object.LayerIndex, }) break } } } printImagePackage( xc, imagePkg, appName, cmdName, changes, changesOutputs, layers, layerChangesMax, allChangesMax, addChangesMax, modifyChangesMax, deleteChangesMax, doHashData, doDetectDuplicates, doShowDuplicates, doShowSpecialPerms, changeMatchLayersOnly, changeDataHashMatchers, changePathMatchers, changeDataMatchers, cparams, cmdReport) if doAddImageManifest { cmdReport.RawImageManifest = imagePkg.Manifest } if doAddImageConfig { cmdReport.RawImageConfig = imagePkg.Config } cmdReport.ImageReport.BuildInfo = imagePkg.Config.BuildInfoDecoded if (cmdReport.SourceImage.BaseImageDigest == "" || cmdReport.SourceImage.BaseImageName == "") && cmdReport.ImageReport.BuildInfo != nil && len(cmdReport.ImageReport.BuildInfo.Sources) > 0 { var lastImageSource *dockerimage.BuildSource for _, s := range cmdReport.ImageReport.BuildInfo.Sources { if s.Type == dockerimage.SourceTypeDockerImage { lastImageSource = s } } if lastImageSource != nil { if lastImageSource.Ref != "" && cmdReport.SourceImage.BaseImageName == "" { cmdReport.SourceImage.BaseImageName = lastImageSource.Ref } if lastImageSource.Pin != "" && cmdReport.SourceImage.BaseImageDigest == "" { cmdReport.SourceImage.BaseImageDigest = lastImageSource.Pin } } } xc.Out.State(cmd.StateCompleted) cmdReport.State = cmd.StateCompleted if doRmFileArtifacts { logger.Info("removing temporary artifacts...") err = fsutil.Remove(iaPath) errutil.WarnOn(err) } else { cmdReport.ImageArchiveLocation = iaPath } xc.Out.State(cmd.StateDone) xc.Out.Info("results", ovars{ "artifacts.location": cmdReport.ArtifactLocation, }) xc.Out.Info("results", ovars{ "artifacts.dockerfile.original": "Dockerfile.fat", }) vinfo := <-viChan version.PrintCheckVersion(xc, "", vinfo) cmdReport.State = cmd.StateDone if cmdReport.Save() { xc.Out.Info("report", ovars{ "file": cmdReport.ReportLocation(), }) } if xdArtifactsPath != "" { var filesToExport []string filesToExport = append(filesToExport, cmdReport.ReportLocation()) filesToExport = append(filesToExport, filepath.Join(cmdReport.ArtifactLocation, fatDockerfileName)) if utf8Detector.DumpArchive != "" { filesToExport = append(filesToExport, utf8Detector.DumpArchive) } if xdArtifactsPath == "." { xdArtifactsPath = "data-artifacts.tar" } if err := fsutil.ArchiveFiles(xdArtifactsPath, filesToExport, true, ""); err == nil { xc.Out.Info("exported-data-artifacts", ovars{ "file": xdArtifactsPath, }) } else { logger.Errorf("error exporting data artifacts (%s) - %v", xdArtifactsPath, err) } } } func findChange(pkg *dockerimage.Package, filepath string) *dockerimage.ObjectMetadata { for _, layer := range pkg.Layers { if object, found := layer.References[filepath]; found { return object } } return nil } func printImagePackage( xc *app.ExecutionContext, pkg *dockerimage.Package, appName string, cmdName cmd.Type, changes map[string]struct{}, changesOutputs map[string]struct{}, layers map[string]struct{}, layerChangesMax int, allChangesMax int, addChangesMax int, modifyChangesMax int, deleteChangesMax int, doHashData bool, doDetectDuplicates bool, doShowDuplicates bool, doShowSpecialPerms bool, changeMatchLayersOnly bool, changeDataHashMatchers map[string]*dockerimage.ChangeDataHashMatcher, changePathMatchers []*dockerimage.ChangePathMatcher, changeDataMatchers map[string]*dockerimage.ChangeDataMatcher, cparams *CommandParams, cmdReport *report.XrayCommand) { var allChangesCount int var addChangesCount int var modifyChangesCount int var deleteChangesCount int xc.Out.Info("image.package.details") xc.Out.Info("layers.count", ovars{ "value": len(pkg.Layers), }) //NOTE: all this cmdReport.ImageReport logic should be moved outside of this function cmdReport.ImageReport = &dockerimage.ImageReport{ Stats: pkg.Stats, } if cparams.DetectIdentities.Enabled { cmdReport.ImageReport.Identities = pkg.ProcessIdentityData() if cmdReport.ImageReport.Identities != nil { xc.Out.Info("image.identities.stats", ovars{ "user_count": len(cmdReport.ImageReport.Identities.Users), "group_count": len(cmdReport.ImageReport.Identities.Groups), }) for username, userInfo := range cmdReport.ImageReport.Identities.Users { xc.Out.Info("image.identities.user", ovars{ "username": username, "uid": userInfo.UID, "home": userInfo.Home, "shell": userInfo.Shell, "no_login_shell": userInfo.NoLoginShell, "no_password_login": userInfo.ShadowPassword.NoPasswordLogin, }) } } } for k := range pkg.Certs.Bundles { cmdReport.ImageReport.Certs.Bundles = append(cmdReport.ImageReport.Certs.Bundles, k) } for k := range pkg.Certs.Files { cmdReport.ImageReport.Certs.Files = append(cmdReport.ImageReport.Certs.Files, k) } cmdReport.ImageReport.Certs.Links = pkg.Certs.Links cmdReport.ImageReport.Certs.Hashes = pkg.Certs.Hashes for k := range pkg.Certs.PrivateKeys { cmdReport.ImageReport.Certs.PrivateKeys = append(cmdReport.ImageReport.Certs.PrivateKeys, k) } cmdReport.ImageReport.Certs.PrivateKeyLinks = pkg.Certs.PrivateKeyLinks for k := range pkg.CACerts.Bundles { cmdReport.ImageReport.CACerts.Bundles = append(cmdReport.ImageReport.CACerts.Bundles, k) } for k := range pkg.CACerts.Files { cmdReport.ImageReport.CACerts.Files = append(cmdReport.ImageReport.CACerts.Files, k) } cmdReport.ImageReport.CACerts.Links = pkg.CACerts.Links cmdReport.ImageReport.CACerts.Hashes = pkg.CACerts.Hashes for k := range pkg.CACerts.PrivateKeys { cmdReport.ImageReport.CACerts.PrivateKeys = append(cmdReport.ImageReport.CACerts.PrivateKeys, k) } cmdReport.ImageReport.CACerts.PrivateKeyLinks = pkg.CACerts.PrivateKeyLinks if doDetectDuplicates && pkg.Stats.DuplicateFileCount > 0 { xc.Out.Info("image.stats.duplicates", ovars{ "file_count": pkg.Stats.DuplicateFileCount, "file_total_count": pkg.Stats.DuplicateFileTotalCount, "file_size.bytes": pkg.Stats.DuplicateFileSize, "file_size.human": humanize.Bytes(pkg.Stats.DuplicateFileSize), "file_total_size.bytes": pkg.Stats.DuplicateFileTotalSize, "file_total_size.human": humanize.Bytes(pkg.Stats.DuplicateFileTotalSize), "wasted.bytes": pkg.Stats.DuplicateFileWastedSize, "wasted.human": humanize.Bytes(pkg.Stats.DuplicateFileWastedSize), }) } doShow := func(changeMatchLayersOnly bool, layer *dockerimage.Layer) bool { if !changeMatchLayersOnly || (changeMatchLayersOnly && layer.HasMatches()) { return true } return false } for _, layer := range pkg.Layers { layerInfo := ovars{ "index": layer.Index, "id": layer.ID, "path": layer.Path, } if layer.MetadataChangesOnly { layerInfo["metadata_change_only"] = true } if layer.LayerDataSource != "" { layerInfo["layer_data_source"] = layer.LayerDataSource } if doShow(changeMatchLayersOnly, layer) { xc.Out.Info("layer.start") xc.Out.Info("layer", layerInfo) } var layerChangesCount int if layer.Distro != nil { distro := &report.DistroInfo{ Name: layer.Distro.Name, Version: layer.Distro.Version, DisplayName: layer.Distro.DisplayName, } xc.Out.Info("distro", ovars{ "name": distro.Name, "version": distro.Version, "display": distro.DisplayName, }) cmdReport.SourceImage.Distro = distro } topList := layer.Top.List() layerReport := dockerimage.LayerReport{ ID: layer.ID, Index: layer.Index, Path: layer.Path, LayerDataSource: layer.LayerDataSource, MetadataChangesOnly: layer.MetadataChangesOnly, FSDiffID: layer.FSDiffID, Stats: layer.Stats, } layerReport.Changes.Deleted = uint64(len(layer.Changes.Deleted)) layerReport.Changes.Modified = uint64(len(layer.Changes.Modified)) layerReport.Changes.Added = uint64(len(layer.Changes.Added)) layerReport.Top = topList for imgIdx, imgInfo := range cmdReport.ImageStack { for instIdx, instInfo := range imgInfo.Instructions { if layerReport.ID == instInfo.LayerID { if !instInfo.EmptyLayer { if layerReport.ChangeInstruction != nil { log.Debugf("overwriting existing layerReport.ChangeInstruction = %#v", layerReport.ChangeInstruction) } layerReport.ChangeInstruction = &dockerimage.InstructionSummary{ Index: instIdx, ImageIndex: imgIdx, Type: instInfo.Type, All: instInfo.CommandAll, Snippet: instInfo.CommandSnippet, } } else { extraInst := &dockerimage.InstructionSummary{ Index: instIdx, ImageIndex: imgIdx, Type: instInfo.Type, All: instInfo.CommandAll, Snippet: instInfo.CommandSnippet, } layerReport.OtherInstructions = append(layerReport.OtherInstructions, extraInst) } } } } cmdReport.ImageLayers = append(cmdReport.ImageLayers, &layerReport) if layerReport.ChangeInstruction != nil { if doShow(changeMatchLayersOnly, layer) { xc.Out.Info("change.instruction", ovars{ "index": fmt.Sprintf("%d:%d", layerReport.ChangeInstruction.ImageIndex, layerReport.ChangeInstruction.Index), "type": layerReport.ChangeInstruction.Type, "snippet": layerReport.ChangeInstruction.Snippet, "all": layerReport.ChangeInstruction.All, }) } } if doShow(changeMatchLayersOnly, layer) { if layerReport.OtherInstructions != nil { xc.Out.Info("other.instructions", ovars{ "count": len(layerReport.OtherInstructions), }) for idx, info := range layerReport.OtherInstructions { xc.Out.Info("other.instruction", ovars{ "pos": idx, "index": fmt.Sprintf("%d:%d", info.ImageIndex, info.Index), "type": info.Type, "snippet": info.Snippet, "all": info.All, }) } } if layer.Stats.AllSize != 0 { xc.Out.Info("layer.stats", ovars{ "all_size.human": humanize.Bytes(uint64(layer.Stats.AllSize)), "all_size.bytes": layer.Stats.AllSize, }) } if layer.Stats.ObjectCount != 0 { xc.Out.Info("layer.stats", ovars{ "object_count": layer.Stats.ObjectCount, }) } if layer.Stats.DirCount != 0 { xc.Out.Info("layer.stats", ovars{ "dir_count": layer.Stats.DirCount, }) } if layer.Stats.FileCount != 0 { xc.Out.Info("layer.stats", ovars{ "file_count": layer.Stats.FileCount, }) } if layer.Stats.LinkCount != 0 { xc.Out.Info("layer.stats", ovars{ "link_count": layer.Stats.LinkCount, }) } if layer.Stats.MaxFileSize != 0 { xc.Out.Info("layer.stats", ovars{ "max_file_size.human": humanize.Bytes(uint64(layer.Stats.MaxFileSize)), "max_file_size.bytes": layer.Stats.MaxFileSize, }) } if layer.Stats.DeletedCount != 0 { xc.Out.Info("layer.stats", ovars{ "deleted_count": layer.Stats.DeletedCount, }) } if layer.Stats.DeletedDirCount != 0 { xc.Out.Info("layer.stats", ovars{ "deleted_dir_count": layer.Stats.DeletedDirCount, }) } if layer.Stats.DeletedFileCount != 0 { xc.Out.Info("layer.stats", ovars{ "deleted_file_count": layer.Stats.DeletedFileCount, }) } if layer.Stats.DeletedLinkCount != 0 { xc.Out.Info("layer.stats", ovars{ "deleted_link_count": layer.Stats.DeletedLinkCount, }) } if layer.Stats.DeletedSize != 0 { xc.Out.Info("layer.stats", ovars{ "deleted_size": layer.Stats.DeletedSize, }) } if layer.Stats.AddedSize != 0 { xc.Out.Info("layer.stats", ovars{ "added_size.human": humanize.Bytes(uint64(layer.Stats.AddedSize)), "added_size.bytes": layer.Stats.AddedSize, }) } if layer.Stats.ModifiedSize != 0 { xc.Out.Info("layer.stats", ovars{ "modified_size.human": humanize.Bytes(uint64(layer.Stats.ModifiedSize)), "modified_size.bytes": layer.Stats.ModifiedSize, }) } } changeCount := len(layer.Changes.Deleted) + len(layer.Changes.Modified) + len(layer.Changes.Added) if doShow(changeMatchLayersOnly, layer) { xc.Out.Info("layer.change.summary", ovars{ "deleted": len(layer.Changes.Deleted), "modified": len(layer.Changes.Modified), "added": len(layer.Changes.Added), "all": changeCount, }) xc.Out.Info("layer.objects.count", ovars{ "value": len(layer.Objects), }) if len(topList) > 0 { xc.Out.Info("layer.objects.top.start") for _, topObject := range topList { match := topObject.PathMatch if !match && len(changePathMatchers) > 0 { log.Tracef("Change path patterns, no match. skipping 'top' change ['%s']", topObject.Name) continue } else { if len(changeDataMatchers) > 0 { matchedPatterns, found := layer.DataMatches[topObject.Name] if !found { log.Tracef("Change data patterns, no match. skipping 'top' change ['%s']", topObject.Name) continue } log.Tracef("'%s' ('top' change) matched data patterns - %d", topObject.Name, len(matchedPatterns)) for _, cdm := range matchedPatterns { log.Tracef("matched => PP='%s' DP='%s'", cdm.PathPattern, cdm.DataPattern) } } else { if len(changeDataHashMatchers) > 0 { matched, found := layer.DataHashMatches[topObject.Name] if !found { log.Trace("Change data hash patterns, no match. skipping 'top' change...") continue } log.Tracef("'%s' ('top' change) matched data hash pattern - %s", topObject.Name, matched.Hash) } } } printObject(xc, topObject) } xc.Out.Info("layer.objects.top.end") } } showLayer := true if len(layers) > 0 { showLayer = false _, hasID := layers[layer.ID] layerIdx := fmt.Sprintf("%v", layer.Index) _, hasIndex := layers[layerIdx] if hasID || hasIndex { showLayer = true } } if doShow(changeMatchLayersOnly, layer) && showLayer { if _, ok := changes["delete"]; ok && len(layer.Changes.Deleted) > 0 { xc.Out.Info("layer.objects.deleted.start") for _, objectIdx := range layer.Changes.Deleted { allChangesCount++ deleteChangesCount++ layerChangesCount++ objectInfo := layer.Objects[objectIdx] //TODO: add a flag to select change type to apply path patterns match := objectInfo.PathMatch if !match && len(changePathMatchers) > 0 { log.Tracef("Change path patterns, no match. skipping 'delete' change ['%s']", objectInfo.Name) continue } //NOTE: not checking change data pattern matches for deletes if allChangesMax > -1 && allChangesCount > allChangesMax { break } if deleteChangesMax > -1 && deleteChangesCount > deleteChangesMax { break } if layerChangesMax > -1 && layerChangesCount > layerChangesMax { break } if _, ok := changesOutputs["report"]; ok { layerReport.Deleted = append(layerReport.Deleted, objectInfo) } if _, ok := changesOutputs["console"]; ok { printObject(xc, objectInfo) } } xc.Out.Info("layer.objects.deleted.end") } if _, ok := changes["modify"]; ok && len(layer.Changes.Modified) > 0 { xc.Out.Info("layer.objects.modified.start") for _, objectIdx := range layer.Changes.Modified { allChangesCount++ modifyChangesCount++ layerChangesCount++ objectInfo := layer.Objects[objectIdx] //TODO: add a flag to select change type to apply path patterns match := objectInfo.PathMatch if !match && len(changePathMatchers) > 0 { log.Tracef("Change path patterns, no match. skipping 'modify' change ['%s']", objectInfo.Name) continue } else { if len(changeDataMatchers) > 0 { matchedPatterns, found := layer.DataMatches[objectInfo.Name] if !found { log.Tracef("Change data patterns, no match. skipping change ['%s']", objectInfo.Name) continue } log.Tracef("'%s' ('modify' change) matched data patterns - %d", objectInfo.Name, len(matchedPatterns)) for _, cdm := range matchedPatterns { log.Tracef("matched => PP='%s' DP='%s'", cdm.PathPattern, cdm.DataPattern) } } else { if len(changeDataHashMatchers) > 0 { matched, found := layer.DataHashMatches[objectInfo.Name] if !found { log.Trace("Change data hash patterns, no match. skipping 'modify' change...") continue } log.Tracef("'%s' ('modify' change) matched data hash pattern - %s", objectInfo.Name, matched.Hash) } } } if allChangesMax > -1 && allChangesCount > allChangesMax { break } if modifyChangesMax > -1 && modifyChangesCount > modifyChangesMax { break } if layerChangesMax > -1 && layerChangesCount > layerChangesMax { break } if _, ok := changesOutputs["report"]; ok { layerReport.Modified = append(layerReport.Modified, objectInfo) } if _, ok := changesOutputs["console"]; ok { printObject(xc, objectInfo) } } xc.Out.Info("layer.objects.modified.end") } if _, ok := changes["add"]; ok && len(layer.Changes.Added) > 0 { xc.Out.Info("layer.objects.added.start") for _, objectIdx := range layer.Changes.Added { allChangesCount++ addChangesCount++ layerChangesCount++ objectInfo := layer.Objects[objectIdx] //TODO: add a flag to select change type to apply path patterns match := objectInfo.PathMatch if !match && len(changePathMatchers) > 0 { log.Tracef("Change path patterns, no match. skipping 'add' change ['%s']", objectInfo.Name) continue } else { if len(changeDataMatchers) > 0 { matchedPatterns, found := layer.DataMatches[objectInfo.Name] if !found { log.Tracef("change data patterns, no match. skipping change ['%s']", objectInfo.Name) continue } log.Tracef("'%s' ('add' change) matched data patterns - %d", objectInfo.Name, len(matchedPatterns)) for _, cdm := range matchedPatterns { log.Tracef("matched => PP='%s' DP='%s'", cdm.PathPattern, cdm.DataPattern) } } else { if len(changeDataHashMatchers) > 0 { matched, found := layer.DataHashMatches[objectInfo.Name] if !found { log.Trace("Change data hash patterns, no match. skipping 'add' change...") continue } log.Tracef("'%s' ('add' change) matched data hash pattern - %s", objectInfo.Name, matched.Hash) } } } if allChangesMax > -1 && allChangesCount > allChangesMax { break } if addChangesMax > -1 && addChangesCount > addChangesMax { break } if layerChangesMax > -1 && layerChangesCount > layerChangesMax { break } if _, ok := changesOutputs["report"]; ok { layerReport.Added = append(layerReport.Added, layer.Objects[objectIdx]) } if _, ok := changesOutputs["console"]; ok { printObject(xc, layer.Objects[objectIdx]) } } xc.Out.Info("layer.objects.added.end") } } if doShow(changeMatchLayersOnly, layer) { xc.Out.Info("layer.end") } } for _, info := range pkg.OSShells { xc.Out.Info("image.shells", ovars{ "full_name": info.FullName, "short_name": info.ShortName, "exe_path": info.ExePath, "link_path": info.LinkPath, "reference": info.Reference, "verified": info.Verified, }) cmdReport.ImageReport.OSShells = append(cmdReport.ImageReport.OSShells, info) } xc.Out.Info("image.entry", ovars{ "exe_path": cmdReport.SourceImage.ContainerEntry.ExePath, "exe_args": strings.Join(cmdReport.SourceImage.ContainerEntry.ExeArgs, ","), }) if cmdReport.SourceImage.ContainerEntry.FullExePath != nil { xc.Out.Info("image.entry.full_exe_path", ovars{ "name": cmdReport.SourceImage.ContainerEntry.FullExePath.Name, "layer": cmdReport.SourceImage.ContainerEntry.FullExePath.Layer, }) } if len(cmdReport.SourceImage.ContainerEntry.ArgFiles) > 0 { for _, argFile := range cmdReport.SourceImage.ContainerEntry.ArgFiles { xc.Out.Info("image.entry.arg_file", ovars{ "name": argFile.Name, "layer": argFile.Layer, }) } } if doShowSpecialPerms && (len(pkg.SpecialPermRefs.Setuid) > 0 || len(pkg.SpecialPermRefs.Setgid) > 0 || len(pkg.SpecialPermRefs.Sticky) > 0) { cmdReport.ImageReport.SpecialPerms = &dockerimage.SpecialPermsInfo{} for name := range pkg.SpecialPermRefs.Setuid { xc.Out.Info("image.special_perms.setuid", ovars{ "name": name, }) cmdReport.ImageReport.SpecialPerms.Setuid = append(cmdReport.ImageReport.SpecialPerms.Setuid, name) } for name := range pkg.SpecialPermRefs.Setgid { xc.Out.Info("image.special_perms.setgid", ovars{ "name": name, }) cmdReport.ImageReport.SpecialPerms.Setgid = append(cmdReport.ImageReport.SpecialPerms.Setgid, name) } for name := range pkg.SpecialPermRefs.Sticky { xc.Out.Info("image.special_perms.sticky", ovars{ "name": name, }) cmdReport.ImageReport.SpecialPerms.Sticky = append(cmdReport.ImageReport.SpecialPerms.Sticky, name) } } if doDetectDuplicates && len(pkg.HashReferences) > 0 { cmdReport.ImageReport.Duplicates = map[string]*dockerimage.DuplicateFilesReport{} //TODO: show duplicates by duplicate total size (biggest waste first) for hash, hobjects := range pkg.HashReferences { var dfr *dockerimage.DuplicateFilesReport showStart := true for fpath, info := range hobjects { if showStart { dfr = &dockerimage.DuplicateFilesReport{ Files: map[string]int{}, FileCount: uint64(len(hobjects)), FileSize: uint64(info.Size), AllFileSize: uint64(info.Size * int64(len(hobjects))), } dfr.WastedSize = dfr.AllFileSize - dfr.FileSize cmdReport.ImageReport.Duplicates[hash] = dfr if doShowDuplicates { xc.Out.Info("image.duplicates.set.start", ovars{ "hash": hash, "count": dfr.FileCount, "size.bytes": dfr.FileSize, "size.human": humanize.Bytes(uint64(dfr.FileSize)), "all_size.bytes": dfr.AllFileSize, "size_total.human": humanize.Bytes(dfr.AllFileSize), "wasted.bytes": dfr.WastedSize, "wasted.human": humanize.Bytes(dfr.WastedSize), }) } showStart = false } dfr.Files[fpath] = info.LayerIndex if doShowDuplicates { xc.Out.Info("image.duplicates.object", ovars{ "name": fpath, "layer": info.LayerIndex, }) } } if doShowDuplicates { xc.Out.Info("image.duplicates.set.end") } } } } func objectHistoryString(history *dockerimage.ObjectHistory) string { if history == nil { return "H=[]" } var builder strings.Builder builder.WriteString("H=[") if history.Add != nil { builder.WriteString(fmt.Sprintf("A:%d", history.Add.Layer)) } if history.Add != nil { var idxList []string for _, mod := range history.Modifies { idxList = append(idxList, fmt.Sprintf("%d", mod.Layer)) } if len(idxList) > 0 { builder.WriteString(fmt.Sprintf("/M:%s", strings.Join(idxList, ","))) } } if history.Delete != nil { builder.WriteString(fmt.Sprintf("/D:%d", history.Delete.Layer)) } builder.WriteString("]") return builder.String() } func printObject(xc *app.ExecutionContext, object *dockerimage.ObjectMetadata) { var hashInfo string if object.Hash != "" { hashInfo = fmt.Sprintf(" hash=%s", object.Hash) } ov := ovars{ "mode": object.Mode, "size.human": humanize.Bytes(uint64(object.Size)), "size.bytes": object.Size, "uid": object.UID, "gid": object.GID, "mtime": object.ModTime.UTC().Format(time.RFC3339), "H": objectHistoryString(object.History), "hash": hashInfo, "object.name": object.Name, } if object.LinkTarget != "" { ov["link.target"] = object.LinkTarget } xc.Out.Info("object", ov) } ================================================ FILE: pkg/app/master/command/xray/init/init.go ================================================ package init import ( "github.com/slimtoolkit/slim/pkg/app/master/command/xray" ) func init() { xray.RegisterCommand() } ================================================ FILE: pkg/app/master/command/xray/prompt.go ================================================ package xray import ( "github.com/c-bata/go-prompt" "github.com/slimtoolkit/slim/pkg/app/master/command" ) var CommandSuggestion = prompt.Suggest{ Text: Name, Description: Usage, } var CommandFlagSuggestions = &command.FlagSuggestions{ Names: []prompt.Suggest{ {Text: command.FullFlagName(command.FlagCommandParamsFile), Description: command.FlagCommandParamsFileUsage}, {Text: command.FullFlagName(command.FlagTarget), Description: command.FlagTargetUsage}, {Text: command.FullFlagName(command.FlagPull), Description: command.FlagPullUsage}, {Text: command.FullFlagName(command.FlagShowPullLogs), Description: command.FlagShowPullLogsUsage}, {Text: command.FullFlagName(command.FlagRegistryAccount), Description: command.FlagRegistryAccountUsage}, {Text: command.FullFlagName(command.FlagRegistrySecret), Description: command.FlagRegistrySecretUsage}, {Text: command.FullFlagName(command.FlagDockerConfigPath), Description: command.FlagDockerConfigPathUsage}, {Text: command.FullFlagName(FlagChanges), Description: FlagChangesUsage}, {Text: command.FullFlagName(FlagChangesOutput), Description: FlagChangesOutputUsage}, {Text: command.FullFlagName(FlagLayer), Description: FlagLayerUsage}, {Text: command.FullFlagName(FlagAddImageManifest), Description: FlagAddImageManifestUsage}, {Text: command.FullFlagName(FlagAddImageConfig), Description: FlagAddImageConfigUsage}, {Text: command.FullFlagName(FlagLayerChangesMax), Description: FlagLayerChangesMaxUsage}, {Text: command.FullFlagName(FlagAllChangesMax), Description: FlagAllChangesMaxUsage}, {Text: command.FullFlagName(FlagAddChangesMax), Description: FlagAddChangesMaxUsage}, {Text: command.FullFlagName(FlagModifyChangesMax), Description: FlagModifyChangesMaxUsage}, {Text: command.FullFlagName(FlagDeleteChangesMax), Description: FlagDeleteChangesMaxUsage}, {Text: command.FullFlagName(FlagChangePath), Description: FlagChangePathUsage}, {Text: command.FullFlagName(FlagChangeData), Description: FlagChangeDataUsage}, {Text: command.FullFlagName(FlagReuseSavedImage), Description: FlagReuseSavedImageUsage}, {Text: command.FullFlagName(FlagHashData), Description: FlagHashDataUsage}, {Text: command.FullFlagName(FlagDetectUTF8), Description: FlagDetectUTF8Usage}, {Text: command.FullFlagName(FlagDetectDuplicates), Description: FlagDetectDuplicatesUsage}, {Text: command.FullFlagName(FlagShowDuplicates), Description: FlagShowDuplicatesUsage}, {Text: command.FullFlagName(FlagShowSpecialPerms), Description: FlagShowSpecialPermsUsage}, {Text: command.FullFlagName(FlagChangeDataHash), Description: FlagChangeDataHashUsage}, {Text: command.FullFlagName(FlagTopChangesMax), Description: FlagTopChangesMaxUsage}, {Text: command.FullFlagName(FlagDetectAllCertFiles), Description: FlagDetectAllCertFilesUsage}, {Text: command.FullFlagName(FlagDetectAllCertPKFiles), Description: FlagDetectAllCertPKFilesUsage}, {Text: command.FullFlagName(FlagDetectIdentities), Description: FlagDetectIdentitiesUsage}, {Text: command.FullFlagName(FlagDetectIdentitiesParam), Description: FlagDetectIdentitiesParamUsage}, {Text: command.FullFlagName(FlagDetectIdentitiesDumpRaw), Description: FlagDetectIdentitiesDumpRawUsage}, {Text: command.FullFlagName(FlagExportAllDataArtifacts), Description: FlagExportAllDataArtifactsUsage}, {Text: command.FullFlagName(command.FlagRemoveFileArtifacts), Description: command.FlagRemoveFileArtifactsUsage}, }, Values: map[string]command.CompleteValue{ command.FullFlagName(command.FlagCommandParamsFile): command.CompleteFile, command.FullFlagName(command.FlagPull): command.CompleteTBool, command.FullFlagName(command.FlagShowPullLogs): command.CompleteBool, command.FullFlagName(command.FlagDockerConfigPath): command.CompleteFile, command.FullFlagName(command.FlagTarget): command.CompleteImage, command.FullFlagName(FlagChanges): completeLayerChanges, command.FullFlagName(FlagChangesOutput): completeOutputs, command.FullFlagName(FlagAddImageManifest): command.CompleteBool, command.FullFlagName(FlagAddImageConfig): command.CompleteBool, command.FullFlagName(FlagHashData): command.CompleteBool, command.FullFlagName(FlagDetectDuplicates): command.CompleteBool, command.FullFlagName(FlagShowDuplicates): command.CompleteTBool, command.FullFlagName(FlagShowSpecialPerms): command.CompleteTBool, command.FullFlagName(FlagReuseSavedImage): command.CompleteTBool, command.FullFlagName(FlagDetectAllCertFiles): command.CompleteBool, command.FullFlagName(FlagDetectAllCertPKFiles): command.CompleteBool, command.FullFlagName(FlagDetectIdentities): command.CompleteTBool, command.FullFlagName(command.FlagRemoveFileArtifacts): command.CompleteBool, }, } var layerChangeValues = []prompt.Suggest{ {Text: "none", Description: "Don't show any file system change details in image layers"}, {Text: "all", Description: "Show all file system change details in image layers"}, {Text: "delete", Description: "Show only 'delete' file system change details in image layers"}, {Text: "modify", Description: "Show only 'modify' file system change details in image layers"}, {Text: "add", Description: "Show only 'add' file system change details in image layers"}, } func completeLayerChanges(ia *command.InteractiveApp, token string, params prompt.Document) []prompt.Suggest { return prompt.FilterHasPrefix(layerChangeValues, token, true) } var outputsValues = []prompt.Suggest{ {Text: "all", Description: "Show changes in all outputs"}, {Text: "report", Description: "Show changes in command report"}, {Text: "console", Description: "Show changes in console"}, } func completeOutputs(ia *command.InteractiveApp, token string, params prompt.Document) []prompt.Suggest { return prompt.FilterHasPrefix(outputsValues, token, true) } ================================================ FILE: pkg/app/master/command/xray/register.go ================================================ package xray import ( "github.com/slimtoolkit/slim/pkg/app/master/command" ) func RegisterCommand() { command.AddCLICommand( Name, CLI, CommandSuggestion, CommandFlagSuggestions) } ================================================ FILE: pkg/app/master/compose/execution.go ================================================ package compose import ( "bytes" "context" "encoding/json" "errors" "fmt" "os" "path/filepath" "strings" "sync" "time" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/docker/dockerutil" "github.com/compose-spec/compose-go/loader" "github.com/compose-spec/compose-go/types" dockerapi "github.com/fsouza/go-dockerclient" log "github.com/sirupsen/logrus" ) var ErrNoServiceImage = errors.New("no service image") type ServiceError struct { Service string Op string Info string } func (e *ServiceError) Error() string { return fmt.Sprintf("compose.ServiceError: service=%s op=%s info='%s'", e.Service, e.Op, e.Info) } type ovars = app.OutVars type ExecutionState string const ( XSNone ExecutionState = "xs.none" XSCreated = "xs.created" XSStarted = "xs.started" XSStopping = "xs.stopping" XSStopped = "xs.stopped" XSRemoved = "xs.removed" XSExited = "xs.exited" XSExitedCrash = "xs.exited.crash" XSError = "xs.error" ) type ExecutionEvent string const ( XECreated ExecutionEvent = "xe.container.created" XEStarted = "xe.container.started" XEStopping = "xe.container.stopping" XEStopped = "xe.container.stopped" XERemoved = "xe.container.removed" XEExited = "xe.container.exited" XEExitedCrash = "xe.container.exited.crash" XEAPIError = "xe.api.error" XEInterrupt = "xe.interrupt" ) type ExecutionEventInfo struct { Event ExecutionEvent Data map[string]string } const ( ComposeVerUnknown = 0 ComposeVerOne = 1 ComposeVerTwo = 2 ComposeVerThree = 3 ) const ( ComposeVerOneStr = "1" ComposeVerTwoStr = "2" ComposeVerThreeStr = "3" ) type ExecutionOptions struct { SvcStartWait int } type Execution struct { *ConfigInfo State ExecutionState Selectors *ServiceSelectors BuildImages bool PullImages bool OwnAllResources bool AllServiceNames map[string]struct{} AllServices map[string]*ServiceInfo AllNetworks map[string]*NetworkInfo PendingServices map[string]struct{} RunningServices map[string]*RunningService ActiveVolumes map[string]*ActiveVolume ActiveNetworks map[string]*ActiveNetwork StopTimeout uint ContainerProbeSvc string options *ExecutionOptions eventCh chan *ExecutionEventInfo printState bool xc *app.ExecutionContext logger *log.Entry apiClient *dockerapi.Client } type ConfigInfo struct { BaseComposeDir string Version uint FullVersion string Project *types.Project Raw map[string]interface{} RawList []map[string]interface{} } type ServiceSelectors struct { Includes map[string]struct{} Excludes map[string]struct{} ServiceAllDeps string } func NewServiceSelectors(serviceAllDeps string, includes []string, excludes []string) *ServiceSelectors { selectors := &ServiceSelectors{ Includes: map[string]struct{}{}, Excludes: map[string]struct{}{}, ServiceAllDeps: serviceAllDeps, } for _, val := range includes { selectors.Includes[val] = struct{}{} } for _, val := range excludes { selectors.Excludes[val] = struct{}{} } return selectors } type ServiceInfo struct { Selected bool ShortName string Name string ContainerName string AllDependencies []string Config types.ServiceConfig } type NetworkInfo struct { Name string Config types.NetworkConfig } type RunningService struct { Name string ID string ContainerName string } type ActiveVolume struct { ShortName string FullName string ID string } type ActiveNetwork struct { Name string //full network name ID string Created bool } const defaultStopTimeout = 7 //7 seconds func NewConfigInfo( composeFiles []string, projectName string, workingDir string, envVars []string, environmentNoHost bool, ) (*ConfigInfo, error) { //not supporting compose profiles for now cv := &ConfigInfo{} var pcConfigFiles []types.ConfigFile for idx, composeFile := range composeFiles { fullComposeFilePath, err := filepath.Abs(composeFile) if err != nil { panic(err) } if idx == 0 { baseComposeDir := filepath.Dir(fullComposeFilePath) if projectName == "" { //default project name to the dir name for compose file projectName = filepath.Base(baseComposeDir) } if workingDir == "" { //all paths in the compose files are relative //to the base path of the first compose file //unless there's an explicitly provided working directory workingDir = baseComposeDir } cv.BaseComposeDir = workingDir } b, err := os.ReadFile(fullComposeFilePath) if err != nil { return nil, err } rawConfig, err := loader.ParseYAML(b) if err != nil { return nil, err } if idx == 0 { cv.Raw = rawConfig } cv.RawList = append(cv.RawList, rawConfig) cf := types.ConfigFile{ Filename: composeFile, Content: b, //pass raw bytes, so loader parses and interpolates } pcConfigFiles = append(pcConfigFiles, cf) } projectConfig := types.ConfigDetails{ WorkingDir: workingDir, ConfigFiles: pcConfigFiles, } if len(envVars) > 0 { projectConfig.Environment = map[string]string{} for _, evar := range envVars { parts := strings.SplitN(evar, "=", 2) if len(parts) == 2 { projectConfig.Environment[parts[0]] = parts[1] } } } if !environmentNoHost { if projectConfig.Environment == nil { projectConfig.Environment = map[string]string{} } //host env vars override explicit vars for _, evar := range os.Environ() { parts := strings.SplitN(evar, "=", 2) if len(parts) == 2 { projectConfig.Environment[parts[0]] = parts[1] } } } project, err := loader.Load(projectConfig, withProjectName(projectName)) if err != nil { return nil, err } if project == nil { return nil, fmt.Errorf("no project info") } cv.Project = project return cv, nil } func NewExecution( xc *app.ExecutionContext, logger *log.Entry, apiClient *dockerapi.Client, composeFiles []string, selectors *ServiceSelectors, projectName string, workingDir string, envVars []string, environmentNoHost bool, containerProbeComposeSvc string, buildImages bool, pullImages bool, pullExcludes []string, ownAllResources bool, options *ExecutionOptions, eventCh chan *ExecutionEventInfo, printState bool) (*Execution, error) { if logger != nil { logger = logger.WithFields(log.Fields{"com": "compose.execution"}) } configInfo, err := NewConfigInfo(composeFiles, projectName, workingDir, envVars, environmentNoHost) if err != nil { return nil, err } //todo: explicitly check for invalid/unknown service dependencies //todo: have an option to ignore/cleanup invalid/unknown service dependencies //not supporting compose profiles for now exe := &Execution{ ConfigInfo: configInfo, State: XSNone, Selectors: selectors, OwnAllResources: ownAllResources, BuildImages: buildImages, PullImages: pullImages, AllServiceNames: map[string]struct{}{}, AllServices: map[string]*ServiceInfo{}, AllNetworks: map[string]*NetworkInfo{}, PendingServices: map[string]struct{}{}, RunningServices: map[string]*RunningService{}, ActiveVolumes: map[string]*ActiveVolume{}, ActiveNetworks: map[string]*ActiveNetwork{}, ContainerProbeSvc: containerProbeComposeSvc, StopTimeout: defaultStopTimeout, apiClient: apiClient, options: options, eventCh: eventCh, printState: printState, xc: xc, logger: logger, } exe.initVersion() err = exe.initServices() if err != nil { return nil, err } exe.initNetworks() return exe, nil } type LoaderOptionsFn func(opts *loader.Options) func withProjectName(name string) LoaderOptionsFn { return func(opts *loader.Options) { opts.Name = name } } func withResolvePaths(resolve bool) LoaderOptionsFn { return func(opts *loader.Options) { opts.ResolvePaths = resolve } } func withInterpolation(interpolation bool) LoaderOptionsFn { return func(opts *loader.Options) { opts.SkipInterpolation = !interpolation } } //loader.WithDiscardEnvFiles //loader.WithSkipValidation func (ref *Execution) ProjectName() string { return ref.Project.Name } func (ref *Execution) ProjectWorkingDir() string { return ref.Project.WorkingDir } func (ref *Execution) Service(name string) *ServiceInfo { return ref.AllServices[name] } func (ref *Execution) SelectedHaveImages() bool { for _, svc := range ref.AllServices { if svc.Selected && svc.Config.Image == "" { return false } } return true } type NetNameInfo struct { FullName string Aliases []string } func (ref *Execution) ActiveServiceNetworks(svcName string) map[string]NetNameInfo { networks := map[string]NetNameInfo{} if svc, found := ref.AllServices[svcName]; found { for netKey, netConfig := range svc.Config.Networks { if netInfo, found := ref.AllNetworks[netKey]; found && netInfo != nil { info := NetNameInfo{ FullName: netInfo.Name, } if netConfig != nil { info.Aliases = netConfig.Aliases } networks[netKey] = info } } } return networks } func (ref *Execution) ActiveNetworkNames() map[string]string { networks := map[string]string{} for netKey, netInfo := range ref.AllNetworks { if netInfo != nil { networks[netKey] = netInfo.Name } } return networks } func (ref *Execution) initServices() error { for _, service := range ref.Project.Services { name := service.Name info := &ServiceInfo{ ShortName: name, Name: fullServiceName(ref.Project.Name, name), Config: service, } if err := ref.Project.WithServices([]string{name}, func(svc types.ServiceConfig) error { if svc.Name != name { info.AllDependencies = append(info.AllDependencies, svc.Name) } return nil }); err != nil { return err } if ref.Selectors != nil { if ref.Selectors.ServiceAllDeps != "" { if ref.Selectors.ServiceAllDeps == name { info.Selected = false if len(info.AllDependencies) > 0 { ref.Selectors.Includes = map[string]struct{}{} for _, sname := range info.AllDependencies { ref.Selectors.Includes[sname] = struct{}{} } } } } } else { info.Selected = true } ref.AllServices[name] = info ref.AllServiceNames[name] = struct{}{} } if ref.Selectors != nil { for name := range ref.AllServices { if len(ref.Selectors.Includes) > 0 { if _, found := ref.Selectors.Includes[name]; found { ref.AllServices[name].Selected = true } } else if len(ref.Selectors.Excludes) > 0 { if _, found := ref.Selectors.Excludes[name]; !found { ref.AllServices[name].Selected = true } } else { ref.AllServices[name].Selected = true } } } if ref.ContainerProbeSvc != "" { ref.AllServices[ref.ContainerProbeSvc].Selected = true ref.Selectors.Includes[ref.ContainerProbeSvc] = struct{}{} } ref.logger.Debug("Execution.initServices: checking ref.AllServices[x].Selected") for shortName, svc := range ref.AllServices { ref.logger.Debugf("sname=%s/%s name=%s SELECTED?=%v\n", shortName, svc.ShortName, svc.Name, svc.Selected) } return nil } func (ref *Execution) initNetworks() { for key, network := range ref.Project.Networks { ref.AllNetworks[key] = &NetworkInfo{ Name: fullNetworkName(ref.Project.Name, key, network.Name), Config: network, } } if _, ok := ref.AllNetworks[defaultNetName]; !ok { ref.AllNetworks[defaultNetName] = &NetworkInfo{ Name: fullNetworkName(ref.Project.Name, defaultNetName, defaultNetName), Config: types.NetworkConfig{}, } } } func (ref *Execution) initVersion() { ref.Version = ComposeVerUnknown fullVersion, ok := ref.Raw["version"].(string) if !ok { return } ref.FullVersion = fullVersion if ref.FullVersion != "" { switch ref.FullVersion[0:1] { case ComposeVerOneStr: ref.Version = ComposeVerOne case ComposeVerTwoStr: ref.Version = ComposeVerTwo case ComposeVerThreeStr: ref.Version = ComposeVerThree } } } func fullServiceName(project, service string) string { //todo: lower case return fmt.Sprintf("%s_%s", project, service) } func fullNetworkName(project, networkKey, networkName string) string { //todo: lower case fullName := fmt.Sprintf("%s_%s", project, networkKey) if networkName != "" && networkName != defaultNetName { fullName = networkName } return fullName } func (ref *Execution) Prepare() error { ref.logger.Debug("Execution.Prepare") if err := ref.PrepareServices(); err != nil { return err } if err := ref.CreateNetworks(); err != nil { return err } if err := ref.CreateVolumes(); err != nil { return err } return nil } func (ref *Execution) PrepareServices() error { errCh := make(chan error, len(ref.AllServices)) var wg sync.WaitGroup ctx, cancel := context.WithCancel(context.Background()) for name := range ref.AllServices { wg.Add(1) go func(svcName string) { defer wg.Done() ref.logger.Debugf("Execution.Prepare: service=%s", svcName) if err := ref.PrepareService(ctx, svcName); err != nil { ref.logger.Debugf("Execution.Prepare: PrepareService(%s) error - %v", svcName, err) //errCh <- fmt.Errorf("error preparing service - %s (%s)", svcName, err.Error()) errCh <- &ServiceError{Service: svcName, Op: "Execution.PrepareService", Info: err.Error()} ref.logger.Debugf("Execution.Prepare: PrepareService(%s) - CANCEL ALL PREPARE SVC", svcName) cancel() } }(name) } wg.Wait() select { case err := <-errCh: return err default: return nil } return nil } func (ref *Execution) PrepareService(ctx context.Context, name string) error { ref.logger.Debugf("Execution.PrepareService(%s)", name) serviceInfo, ok := ref.AllServices[name] if !ok { return fmt.Errorf("unknown service - %s", name) } service := serviceInfo.Config ref.logger.Debugf("Execution.PrepareService(%s): image=%s", name, service.Image) if service.Image == "" { imageName := fmt.Sprintf("%s_%s", ref.Project.Name, name) if ref.BuildImages && service.Build != nil { if err := buildImage(ctx, ref.apiClient, ref.BaseComposeDir, imageName, service.Build); err != nil { return err } } else { return fmt.Errorf("cant build service image - %s", name) } } else { found, err := HasImage(ref.apiClient, service.Image) if err != nil { return err } if found { return nil } //building image as if service.PullPolicy == types.PullPolicyBuild //todo: have an explicit flag for this behavior if ref.BuildImages && service.Build != nil { if err := buildImage(ctx, ref.apiClient, ref.BaseComposeDir, service.Image, service.Build); err != nil { return err } } if ref.PullImages { //building image as if //service.PullPolicy == types.PullPolicyIfNotPresent || types.PullPolicyMissing if err := pullImage(ctx, ref.apiClient, service.Image); err != nil { return err } //note: might need a better image check (already in DS?) found, err := HasImage(ref.apiClient, service.Image) if err != nil { return err } if !found { ref.logger.Debugf("Execution.PrepareService(%s): image=%s - no image and image pull did not pull image", name, service.Image) return ErrNoServiceImage } } else { ref.logger.Debugf("Execution.PrepareService(%s): image=%s - no image and image pull is not enabled", name, service.Image) return ErrNoServiceImage } } return nil } func (ref *Execution) DiscoverResources() error { ref.logger.Debug("Execution.DiscoverResources") //discover existing containers, volumes, networks for project return nil } func (ref *Execution) Start() error { ref.logger.Debug("Execution.Start") if err := ref.StartServices(); err != nil { return err } return nil } func (ref *Execution) StartServices() error { ref.logger.Debug("Execution.StartServices") if err := ref.Project.WithServices(nil, func(service types.ServiceConfig) error { ref.logger.Debugf("Execution.StartServices: service.Name='%s'", service.Name) fullSvcInfo, found := ref.AllServices[service.Name] if !found { return fmt.Errorf("unknown service - %s", service.Name) } ref.logger.Debugf("Execution.StartServices: starting service=%s (image=%s)", service.Name, fullSvcInfo.Config.Image) if ref.options.SvcStartWait > 0 { ref.logger.Debugf("Execution.StartServices: waiting %v seconds before starting service=%s (image=%s)", ref.options.SvcStartWait, service.Name, fullSvcInfo.Config.Image) time.Sleep(time.Duration(ref.options.SvcStartWait) * time.Second) } err := ref.StartService(service.Name) if err != nil { ref.logger.Debugf("Execution.StartServices: ref.StartService() error = %v", err) return err } return nil }); err != nil { ref.logger.Debugf("Execution.StartServices: ref.Project.WithServices error = %v", err) return err } return nil } func (ref *Execution) StartService(name string) error { ref.logger.Debugf("Execution.StartService(%s)", name) _, running := ref.RunningServices[name] if running { ref.logger.Debugf("Execution.StartService(%s) - already running", name) return nil } _, pending := ref.PendingServices[name] if pending { ref.logger.Debugf("Execution.StartService(%s) - already starting", name) return nil } serviceInfo, found := ref.AllServices[name] if !found { return fmt.Errorf("unknown service - %s", name) } if !serviceInfo.Selected { ref.logger.Debugf("Execution.StartService(%s) - NOT SELECTED", name) return nil } else { ref.logger.Debugf("Execution.StartService(%s) - selected", name) } service := serviceInfo.Config ref.PendingServices[name] = struct{}{} //note: //don't really need this because StartService() //is called in reverse dependency order dependencies := service.GetDependencies() for _, dep := range dependencies { ref.logger.Debugf("Execution.StartService(%s): start dependency=%s", name, dep) err := ref.StartService(dep) if err != nil { return err } } delete(ref.PendingServices, name) //todo: need to refactor to use container.Execution id, err := startContainer( ref.apiClient, ref.Project.Name, //serviceInfo.Name, //full service name ref.AllServiceNames, ref.BaseComposeDir, ref.ActiveVolumes, ref.ActiveNetworks, serviceInfo) //service) if err != nil { ref.logger.Debugf("Execution.StartService(%s): startContainer() error - %v", name, err) return err } //if fullName != serviceInfo.Name { // return fmt.Errorf("mismatching service name: %s/%s", fullName, serviceInfo.Name) //} rsvc := &RunningService{ Name: serviceInfo.Name, ID: id, ContainerName: serviceInfo.ContainerName, } ref.RunningServices[name] = rsvc ref.logger.Debugf("Execution.StartService(%s): runningService=%#v", name, rsvc) return nil } func (ref *Execution) StopServices() error { ref.logger.Debug("Execution.StopServices") for key := range ref.RunningServices { err := ref.StopService(key) if err != nil && key != ref.ContainerProbeSvc { return err } } return nil } func (ref *Execution) CleanupServices() error { ref.logger.Debug("Execution.CleanupServices") for key := range ref.RunningServices { err := ref.CleanupService(key) if err != nil && key != ref.ContainerProbeSvc { return err } } return nil } func (ref *Execution) StopService(key string) error { ref.logger.Debugf("Execution.StopService(%s)\n", key) service, running := ref.RunningServices[key] if !running { ref.logger.Debugf("Execution.StopService(%s) - no running service", key) if serviceInfo, found := ref.AllServices[key]; found && serviceInfo != nil { if !serviceInfo.Selected { ref.logger.Debugf("Execution.StopService(%s) - service not selected", key) return nil } } return nil } ref.logger.Debugf("Execution.StopService(%s): service=%+v", key, service) timeout := uint(defaultStopTimeout) if err := ref.apiClient.StopContainer(service.ID, timeout); err != nil { return err } return nil } func (ref *Execution) CleanupService(key string) error { ref.logger.Debugf("Execution.CleanupService(%s)", key) service, running := ref.RunningServices[key] if !running { ref.logger.Debugf("Execution.CleanupService(%s) - no running service", key) if serviceInfo, found := ref.AllServices[key]; found && serviceInfo != nil { if !serviceInfo.Selected { ref.logger.Debugf("Execution.CleanupService(%s) - service not selected", key) return nil } } return nil } ref.logger.Debugf("Execution.CleanupService(%s): service=%+v", key, service) options := dockerapi.RemoveContainerOptions{ ID: service.ID, Force: true, } if err := ref.apiClient.RemoveContainer(options); err != nil { return err } return nil } const ( rtLabelAppVersion = "tmp.version" rtLabelApp = "ds.runtime.container.type" rtLabelProject = "ds.engine.compose.project" rtLabelService = "ds.engine.compose.service" rtLabelVolumeName = "ds.engine.compose.volume.name" rtLabelVolumeKey = "ds.engine.compose.volume.key" rtLabelNetwork = "ds.engine.compose.network" ) func ExposedPorts(expose types.StringOrNumberList, ports []types.ServicePortConfig) map[dockerapi.Port]struct{} { exposed := map[dockerapi.Port]struct{}{} for _, key := range expose { exposed[dockerapi.Port(key)] = struct{}{} } for _, portInfo := range ports { key := fmt.Sprintf("%d/%s", portInfo.Target, portInfo.Protocol) exposed[dockerapi.Port(key)] = struct{}{} } return exposed } func MountsFromVolumeConfigs( baseComposeDir string, configs []types.ServiceVolumeConfig, tmpfsConfigs []string, activeVolumes map[string]*ActiveVolume) ([]dockerapi.HostMount, error) { mounts := []dockerapi.HostMount{} for _, c := range configs { log.Debugf("compose.MountsFromVolumeConfigs(): volumeConfig=%#v", c) mount := dockerapi.HostMount{ Type: c.Type, Target: c.Target, ReadOnly: c.ReadOnly, } if c.Source == "" { mount.Type = types.VolumeTypeVolume } else { source := c.Source _, found := activeVolumes[source] if !found { if !filepath.IsAbs(source) { if strings.HasPrefix(source, "~/") { hd, _ := os.UserHomeDir() source = filepath.Join(hd, source[2:]) } else { source = filepath.Join(baseComposeDir, source) } } log.Debugf("compose.MountsFromVolumeConfigs(): no active volume (orig.source='%s' source='%s' activeVolumes=%#v)", c.Source, source, activeVolumes) mount.Type = types.VolumeTypeBind } else { log.Debugf("compose.MountsFromVolumeConfigs(): activeVolume='%s'", source) mount.Type = types.VolumeTypeVolume } mount.Source = source } if c.Bind != nil { mount.BindOptions = &dockerapi.BindOptions{ Propagation: c.Bind.Propagation, } } if c.Volume != nil { mount.VolumeOptions = &dockerapi.VolumeOptions{ NoCopy: c.Volume.NoCopy, } } if c.Tmpfs != nil { mount.TempfsOptions = &dockerapi.TempfsOptions{ SizeBytes: c.Tmpfs.Size, } } mounts = append(mounts, mount) } for _, tc := range tmpfsConfigs { mount := dockerapi.HostMount{ Type: types.VolumeTypeTmpfs, Target: tc, } mounts = append(mounts, mount) } return mounts, nil } func EnvVarsFromService(varMap types.MappingWithEquals, varFiles types.StringList) []string { var result []string for k, v := range varMap { record := k if v != nil { record = fmt.Sprintf("%s=%s", record, *v) } result = append(result, record) } for _, file := range varFiles { data, err := os.ReadFile(file) if err != nil { log.Debugf("compose.EnvVarsFromService: error reading '%s' - %v", file, err) continue } lines := strings.Split(string(data), "\n") for _, line := range lines { line = strings.TrimSpace(line) if len(line) == 0 { continue } if strings.HasPrefix(line, "#") { continue } result = append(result, line) } } return result } func HasImage(dclient *dockerapi.Client, imageRef string) (bool, error) { if imageRef == "" || imageRef == "." || imageRef == ".." { return false, fmt.Errorf("bad image reference") } info, err := dockerutil.HasImage(dclient, imageRef) if err != nil { if err == dockerapi.ErrNoSuchImage || err == dockerutil.ErrNotFound { return false, nil } return false, err } log.Debugf("compose.HasImage(%s): image identity - %#v", imageRef, info) var repo string var tag string if strings.Contains(imageRef, "@") { parts := strings.SplitN(imageRef, "@", 2) repo = parts[0] tag = parts[1] } else { if strings.Contains(imageRef, ":") { parts := strings.SplitN(imageRef, ":", 2) repo = parts[0] tag = parts[1] } else { repo = imageRef tag = "latest" } } for _, t := range info.ShortTags { if t == tag { log.Debugf("compose.HasImage(%s): FOUND IT - %s (%s)", imageRef, t, repo) return true, nil } } log.Debugf("compose.HasImage(%s): NOT FOUND IT", imageRef) return false, nil } type ImageIdentity struct { ID string ShortTags []string RepoTags []string ShortDigests []string RepoDigests []string } func ImageToIdentity(info *dockerapi.Image) *ImageIdentity { result := &ImageIdentity{ ID: info.ID, RepoTags: info.RepoTags, RepoDigests: info.RepoDigests, } for _, tag := range result.RepoTags { parts := strings.Split(tag, ":") if len(parts) == 2 { result.ShortTags = append(result.ShortTags, parts[1]) } } for _, digest := range result.RepoDigests { parts := strings.Split(digest, "@") if len(parts) == 2 { result.ShortDigests = append(result.ShortDigests, parts[1]) } } return result } func pullImage(ctx context.Context, apiClient *dockerapi.Client, imageRef string) error { log.Debugf("pullImage(%s)", imageRef) var repo string var tag string if strings.Contains(imageRef, "@") { parts := strings.SplitN(imageRef, "@", 2) repo = parts[0] tag = parts[1] } else { if strings.Contains(imageRef, ":") { parts := strings.SplitN(imageRef, ":", 2) repo = parts[0] tag = parts[1] } else { repo = imageRef tag = "latest" } } var output bytes.Buffer options := dockerapi.PullImageOptions{ //Registry: Repository: repo, Tag: tag, OutputStream: &output, RawJSONStream: true, Context: ctx, } /* {"status":"","progressDetail":{"current":NUM,"total":NUM},progress:"","id":""} */ //TODO: add support for auth auth := dockerapi.AuthConfiguration{} log.Debugf("pullImage(%s) repo=%s tag=%s", imageRef, repo, tag) if err := apiClient.PullImage(options, auth); err != nil { log.Debugf("pullImage: dockerapi.PullImage() error = %v", err) return err } fmt.Println("pull output:") fmt.Println(output.String()) fmt.Println("pull output [DONE]") return nil } // TODO: move builder into pkg func buildImage(ctx context.Context, apiClient *dockerapi.Client, basePath, imageName string, config *types.BuildConfig) error { log.Debugf("buildImage(%s,%s)", basePath, imageName) var output bytes.Buffer buildOptions := dockerapi.BuildImageOptions{ Context: ctx, Name: imageName, Dockerfile: config.Dockerfile, OutputStream: &output, RmTmpContainer: true, ForceRmTmpContainer: true, Target: config.Target, RawJSONStream: true, } /* {"stream":""} */ //buildOptions.NetworkMode = config.Network for key, val := range config.Args { if val == nil { arg := dockerapi.BuildArg{ Name: key, } buildOptions.BuildArgs = append(buildOptions.BuildArgs, arg) continue } arg := dockerapi.BuildArg{ Name: key, Value: *val, } buildOptions.BuildArgs = append(buildOptions.BuildArgs, arg) } for key, val := range config.Labels { buildOptions.Labels[key] = val } for _, val := range config.CacheFrom { buildOptions.CacheFrom = append(buildOptions.CacheFrom, val) } //TODO: investigate []string to string if len(config.ExtraHosts) > 0 { buildOptions.ExtraHosts = config.ExtraHosts[0] } if strings.HasPrefix(config.Context, "http://") || strings.HasPrefix(config.Context, "https://") { buildOptions.Remote = config.Context } else { contextDir := config.Context if !strings.HasPrefix(contextDir, "/") { contextDir = filepath.Join(basePath, contextDir) } if info, err := os.Stat(contextDir); err == nil && info.IsDir() { buildOptions.ContextDir = contextDir } else { return fmt.Errorf("invalid context directory - %s", contextDir) } } if err := apiClient.BuildImage(buildOptions); err != nil { log.Debugf("buildImage: dockerapi.BuildImage() error = %v", err) return err } fmt.Println("build output:") fmt.Println(output.String()) fmt.Println("build output [DONE]") return nil } func durationToSeconds(d *types.Duration) int { if d == nil { return 0 } return int(time.Duration(*d).Seconds()) } func VolumesFrom(serviceNames map[string]struct{}, volumesFrom []string) []string { var vfList []string for _, vf := range volumesFrom { //service_name //service_name:ro //container:container_name //container:container_name:rw if strings.HasPrefix(vf, "container:") { vfList = append(vfList, vf[len("container:"):]) } if len(serviceNames) > 0 { if strings.Contains(vf, ":") { parts := strings.Split(vf, ":") vf = parts[0] } //todo: check that we get the right names here if _, found := serviceNames[vf]; found { vfList = append(vfList, vf) } } } return vfList } func startContainer( apiClient *dockerapi.Client, projectName string, //fullServiceName string, serviceNames map[string]struct{}, baseComposeDir string, activeVolumes map[string]*ActiveVolume, activeNetworks map[string]*ActiveNetwork, //service types.ServiceConfig serviceInfo *ServiceInfo) (string, error) { service := serviceInfo.Config //todo - cleanup log.Debugf("startContainer(%s/%s/%s)", serviceInfo.Name, service.Name, service.ContainerName) log.Debugf("service.Image=%s", service.Image) log.Debugf("service.Entrypoint=%s", service.Entrypoint) log.Debugf("service.Command=%s", service.Command) log.Debugf("service.Ports=%#v", service.Ports) log.Debugf("service.Environment=%#v", service.Environment) labels := map[string]string{} for name, val := range service.Labels { labels[name] = val } getServiceImage := func() string { return service.Image /* todo: test first with building images if service.Image != "" { return service.Image } return projectName + "_" + service.Name */ } labels[rtLabelApp] = rtLabelAppVersion labels[rtLabelProject] = projectName labels[rtLabelService] = service.Name var netModeKey string netMode := service.NetworkMode if netMode == "" { log.Debugf("startContainer(): activeNetworks[%d]=%+v", len(activeNetworks), activeNetworks) if len(activeNetworks) > 0 { serviceNetworks := service.Networks log.Debugf("startContainer(): configured service network count - %d", len(serviceNetworks)) if len(serviceNetworks) == 0 { log.Debug("startContainer(): adding default service network config") defaultNet := fmt.Sprintf("%s_default", projectName) serviceNetworks = map[string]*types.ServiceNetworkConfig{ defaultNet: nil, } } for name := range serviceNetworks { log.Debugf("startContainer(): service network config - %s", name) if _, ok := activeNetworks[name]; ok { netMode = activeNetworks[name].Name netModeKey = name log.Debugf("startContainer(): found active network config - %s (netMode=%s)", name, netMode) break } } } if netMode == "" { netMode = "none" log.Debug("startContainer(): default netMode to none") } } if (netMode == "none" || netMode == "host") && len(service.Networks) > 0 { log.Debugf("startContainer(%s): incompatible network_mode and networks config", service.Name) return "", fmt.Errorf("startContainer(%s): incompatible network_mode and networks config", service.Name) } netAliases := []string{ service.Name, } if len(service.Networks) != 0 { netConfig, ok := service.Networks[netModeKey] if ok && netConfig != nil { netAliases = append(netAliases, netConfig.Aliases...) } } endpointsConfig := map[string]*dockerapi.EndpointConfig{ netMode: { Aliases: netAliases, }, } log.Debugf("startContainer(%s): endpointsConfig=%+v", service.Name, endpointsConfig) mounts, err := MountsFromVolumeConfigs(baseComposeDir, service.Volumes, service.Tmpfs, activeVolumes) if err != nil { return "", err } //need to make it work with all container name checks serviceInfo.ContainerName = serviceInfo.Name if service.ContainerName != "" { serviceInfo.ContainerName = service.ContainerName } envVars := EnvVarsFromService(service.Environment, service.EnvFile) containerOptions := dockerapi.CreateContainerOptions{ Name: serviceInfo.ContainerName, Config: &dockerapi.Config{ Image: getServiceImage(), Entrypoint: []string(service.Entrypoint), Cmd: []string(service.Command), WorkingDir: service.WorkingDir, Env: envVars, Hostname: service.Hostname, Domainname: service.DomainName, DNS: []string(service.DNS), User: service.User, ExposedPorts: ExposedPorts(service.Expose, service.Ports), Labels: labels, //Volumes: - covered by "volume" HostConfig.Mounts, StopSignal: service.StopSignal, StopTimeout: durationToSeconds(service.StopGracePeriod), //Healthcheck *HealthConfig -> service.HealthCheck *HealthCheckConfig - todo SecurityOpts: service.SecurityOpt, //AttachStdout: true, //todo: revisit //AttachStderr: true, //todo: revisit AttachStdin: false, //for termnal Tty: service.Tty, //for termnal OpenStdin: service.StdinOpen, //for termnal StdinOnce: false, NetworkDisabled: netMode == "none", //service.NetworkMode == "disabled" MacAddress: service.MacAddress, }, HostConfig: &dockerapi.HostConfig{ Mounts: mounts, VolumesFrom: VolumesFrom(serviceNames, service.VolumesFrom), VolumeDriver: service.VolumeDriver, ReadonlyRootfs: service.ReadOnly, //Binds: - covered by "bind" HostConfig.Mounts CapAdd: []string(service.CapAdd), CapDrop: []string(service.CapDrop), PortBindings: portBindingsFromServicePortConfigs(service.Ports), //map[Port][]PortBinding -> Ports []ServicePortConfig //Links: service.Links, - not the same links DNS: service.DNS, DNSOptions: service.DNSOpts, DNSSearch: service.DNSSearch, ExtraHosts: service.ExtraHosts, UsernsMode: service.UserNSMode, NetworkMode: netMode, IpcMode: service.Ipc, Isolation: service.Isolation, PidMode: service.Pid, //RestartPolicy: dockerapi.RestartPolicy{Name: service.Restart},//RestartPolicy - todo: handle it the right way //Devices: ,//[]Device <- service.Devices //LogConfig: ,//LogConfig <- service.Logging SecurityOpt: service.SecurityOpt, Privileged: service.Privileged, CgroupParent: service.CgroupParent, //Memory: ,//int64 <- service.MemLimit //MemoryReservation: ,//int64 //KernelMemory: ,//int64 //MemorySwap: ,//int64 CPUShares: service.CPUShares, CPUSet: service.CPUSet, //Ulimits: ,//[]ULimit <- service.Ulimits OomScoreAdj: int(service.OomScoreAdj), //MemorySwappiness: ,//*int64 //OOMKillDisable: ,//*bool ShmSize: int64(service.ShmSize), //Tmpfs: - covered by "tmpfs" HostConfig.Mounts Sysctls: service.Sysctls, CPUCount: service.CPUCount, //CPUPercent: ,///int64 <- service.CPUPercent Runtime: service.Runtime, //string }, NetworkingConfig: &dockerapi.NetworkingConfig{ EndpointsConfig: endpointsConfig, }, } if service.Init != nil { containerOptions.HostConfig.Init = *service.Init } if service.PidsLimit > 0 { containerOptions.HostConfig.PidsLimit = &service.PidsLimit } if service.Init != nil { containerOptions.HostConfig.Init = *service.Init } log.Debugf("startContainer(%s): creating container...", service.Name) containerInfo, err := apiClient.CreateContainer(containerOptions) if err != nil { log.Debugf("startContainer(%s): create container error - %v", service.Name, err) return "", err } log.Debugf("startContainer(%s): connecting container to networks...", service.Name) for key, serviceNet := range service.Networks { log.Debugf("startContainer(%s): service network key=%s info=%v", service.Name, key, serviceNet) netInfo, found := activeNetworks[key] if !found || netInfo == nil { log.Debugf("startContainer(%s): skipping network = '%s'", service.Name, key) continue } log.Debugf("startContainer(%s): found network key=%s netInfo=%#v", service.Name, key, netInfo) options := dockerapi.NetworkConnectionOptions{ Container: containerInfo.ID, EndpointConfig: &dockerapi.EndpointConfig{ Aliases: []string{ service.Name, }, }, } if serviceNet != nil && len(serviceNet.Aliases) != 0 { options.EndpointConfig.Aliases = append(options.EndpointConfig.Aliases, serviceNet.Aliases...) } options.EndpointConfig.Aliases = append(options.EndpointConfig.Aliases, containerInfo.ID[:12]) if len(service.Links) > 0 { var networkLinks []string for _, linkInfo := range service.Links { var linkTarget string var linkName string parts := strings.Split(linkInfo, ":") switch len(parts) { case 1: linkTarget = parts[0] linkName = parts[0] case 2: linkTarget = parts[0] linkName = parts[1] default: log.Debugf("startContainer(%s): service.Links: malformed link - %s", service.Name, linkInfo) continue } networkLinks = append(networkLinks, fmt.Sprintf("%s:%s", linkTarget, linkName)) } options.EndpointConfig.Links = networkLinks } if err := apiClient.ConnectNetwork(netInfo.ID, options); err != nil { log.Debugf("startContainer(%s): container network connect error - %v", service.Name, err) log.Debugf("startContainer(%s): netInfo.ID=%s options=%#v", service.Name, netInfo.ID, options) return "", err } } log.Debugf("startContainer(%s): starting container id='%s' cn='%s'", service.Name, containerInfo.ID, serviceInfo.ContainerName) if err := apiClient.StartContainer(containerInfo.ID, nil); err != nil { log.Debugf("startContainer(%s): start container error - %v", service.Name, err) return "", err } return containerInfo.ID, nil } func portBindingsFromServicePortConfigs(configs []types.ServicePortConfig) map[dockerapi.Port][]dockerapi.PortBinding { result := map[dockerapi.Port][]dockerapi.PortBinding{} for _, config := range configs { var bindings []dockerapi.PortBinding var binding dockerapi.PortBinding if config.Published > 0 { binding.HostPort = fmt.Sprint(config.Published) } portKey := dockerapi.Port(fmt.Sprintf("%d/%s", config.Target, config.Protocol)) result[portKey] = append(bindings, binding) } return result } func createVolume(apiClient *dockerapi.Client, projectName, volKey, volFullName string, config types.VolumeConfig) (string, error) { labels := config.Labels if labels == nil { labels = map[string]string{} } labels[rtLabelApp] = rtLabelAppVersion labels[rtLabelProject] = projectName labels[rtLabelVolumeName] = volFullName labels[rtLabelVolumeKey] = volKey volumeOptions := dockerapi.CreateVolumeOptions{ Name: volFullName, //already includes the project prefix Driver: config.Driver, DriverOpts: config.DriverOpts, Labels: labels, } log.Debugf("createVolume(%s, %s, %s) volumeOptions=%#v", projectName, volKey, volFullName, volumeOptions) volumeInfo, err := apiClient.CreateVolume(volumeOptions) if err != nil { log.Debugf("apiClient.CreateVolume() error = %v", err) return "", err } return volumeInfo.Name, nil } func (ref *Execution) CreateVolumes() error { projectName := strings.Trim(ref.Project.Name, "-_") for key, volume := range ref.Project.Volumes { name := fmt.Sprintf("%s_%s", projectName, key) ref.logger.Debugf("CreateVolumes: key=%s gen.Name=%s volume.Name=%s", key, name, volume.Name) if volume.Name != "" { name = volume.Name } id, err := createVolume(ref.apiClient, projectName, key, name, volume) if err != nil { return err } ref.ActiveVolumes[key] = &ActiveVolume{ ShortName: key, FullName: name, ID: id, } } return nil } func (ref *Execution) DeleteVolumes() error { for key, volume := range ref.ActiveVolumes { ref.logger.Debugf("DeleteVolumes: key/name=%s ID=%s", key, volume.ID) err := deleteVolume(ref.apiClient, volume.ID) if err != nil && key != ref.ContainerProbeSvc { return err } delete(ref.ActiveVolumes, key) } return nil } func deleteVolume(apiClient *dockerapi.Client, id string) error { removeOptions := dockerapi.RemoveVolumeOptions{ Name: id, Force: true, } err := apiClient.RemoveVolumeWithOptions(removeOptions) if err != nil { log.Debugf("dclient.RemoveVolumeWithOptions() error = %v", err) return err } return nil } const defaultNetName = "default" func (ref *Execution) CreateNetworks() error { ref.logger.Debugf("Execution.CreateNetworks") projectName := strings.Trim(ref.Project.Name, "-_") for key, networkInfo := range ref.AllNetworks { network := networkInfo.Config ref.logger.Debugf("Execution.CreateNetworks: key=%s name=%s", key, network.Name) created, id, err := createNetwork(ref.apiClient, projectName, key, networkInfo.Name, network) if err != nil { return err } ref.ActiveNetworks[key] = &ActiveNetwork{ Name: networkInfo.Name, ID: id, Created: created, } } //need to create the 'default' network if it's not created yet if _, ok := ref.ActiveNetworks[defaultNetName]; !ok { //var defaultNetConfig types.NetworkConfig defaultNetworkInfo := ref.AllNetworks[defaultNetName] created, id, err := createNetwork(ref.apiClient, projectName, defaultNetName, defaultNetworkInfo.Name, defaultNetworkInfo.Config) if err != nil { return err } ref.ActiveNetworks[defaultNetName] = &ActiveNetwork{ Name: defaultNetworkInfo.Name, ID: id, Created: created, } } return nil } func createNetwork(apiClient *dockerapi.Client, projectName, name, fullName string, config types.NetworkConfig) (bool, string, error) { log.Debugf("createNetwork(%s,%s)", projectName, name) //fullName := fullNetworkName(projectName, name, config.Name) //fullName := fmt.Sprintf("%s_%s", projectName, name) //if config.Name != "" && config.Name != defaultNetName { // fullName = config.Name //} labels := map[string]string{ rtLabelApp: rtLabelAppVersion, rtLabelProject: projectName, rtLabelNetwork: name, } driverOpts := map[string]interface{}{} for k, v := range config.DriverOpts { driverOpts[k] = v } options := dockerapi.CreateNetworkOptions{ Name: fullName, Driver: config.Driver, Options: driverOpts, Labels: labels, Internal: config.Internal, Attachable: config.Attachable, } if options.Driver == "" { options.Driver = "bridge" } mustFind := false if config.External.External { mustFind = true fullName = name if config.External.Name != "" { fullName = config.External.Name } } //not using config.Ipam for now filter := dockerapi.NetworkFilterOpts{ "name": map[string]bool{ fullName: true, }, } log.Debugf("createNetwork(%s,%s): lookup '%s'", projectName, name, fullName) networkList, err := apiClient.FilteredListNetworks(filter) if err != nil { log.Debugf("listNetworks(%s): dockerapi.FilteredListNetworks() error = %v", name, err) return false, "", err } if len(networkList) == 0 || networkList[0].Name != fullName { if mustFind { return false, "", fmt.Errorf("no external network - %s", fullName) } log.Debugf("createNetwork(%s,%s): create '%s'", projectName, name, options.Name) networkInfo, err := apiClient.CreateNetwork(options) if err != nil { log.Debugf("apiClient.CreateNetwork() error = %v", err) return false, "", err } return true, networkInfo.ID, nil } log.Debugf("createNetwork(%s,%s): found network '%s' (id=%s)", projectName, name, fullName, networkList[0].ID) return false, networkList[0].ID, nil } func (ref *Execution) DeleteNetworks() error { for key, network := range ref.ActiveNetworks { ref.logger.Debugf("DeleteNetworks: key=%s name=%s ID=%s", key, network.Name, network.ID) if !network.Created { ref.logger.Debug("DeleteNetworks: skipping...") continue } err := deleteNetwork(ref.apiClient, network.ID) if err != nil && key != ref.ContainerProbeSvc { return err } delete(ref.ActiveNetworks, key) } return nil } func deleteNetwork(apiClient *dockerapi.Client, id string) error { err := apiClient.RemoveNetwork(id) if err != nil { log.Debugf("dclient.RemoveNetwork() error = %v", err) return err } return nil } func dumpComposeJSON(data *types.Project) { if pretty, err := json.MarshalIndent(data, "", " "); err == nil { fmt.Printf("%s\n", string(pretty)) } } func dumpRawJSON(data map[string]interface{}) { if pretty, err := json.MarshalIndent(data, "", " "); err == nil { fmt.Printf("%s\n", string(pretty)) } } func dumpConfig(config *types.Config) { fmt.Printf("\n\n") fmt.Printf("types.Config:\n%#v\n", config) } func (ref *Execution) Stop() error { ref.logger.Debugf("Execution.Stop") if err := ref.StopServices(); err != nil { return err } return nil } func (ref *Execution) Cleanup() error { ref.logger.Debugf("Execution.Cleanup") //todo: //pass 'force'/'all' param to clean the spec resources //not created by this instance (except external resources) if err := ref.CleanupServices(); err != nil { return err } if err := ref.DeleteVolumes(); err != nil { return err } if err := ref.DeleteNetworks(); err != nil { return err } return nil } ================================================ FILE: pkg/app/master/config/config.go ================================================ package config import ( "errors" "os" "path/filepath" "strings" "time" docker "github.com/fsouza/go-dockerclient" "github.com/slimtoolkit/slim/pkg/util/fsutil" ) // AppOptionsFilename is the default name for the app configs const AppOptionsFilename = "slim.config.json" // AppOptions provides a set of global application parameters and command-specific defaults // AppOptions values override the default flag values if they are set // AppOptions is loaded from the "slim.config.json" file stored in the state path directory type AppOptions struct { Global *GlobalAppOptions `json:"global,omitempty"` } // GlobalAppOptions provides a set of global application parameters type GlobalAppOptions struct { NoColor *bool `json:"no_color,omitempty"` Debug *bool `json:"debug,omitempty"` Verbose *bool `json:"verbose,omitempty"` Quiet *bool `json:"quiet,omitempty"` OutputFormat *string `json:"output_format,omitempty"` LogLevel *string `json:"log_level,omitempty"` Log *string `json:"log,omitempty"` LogFormat *string `json:"log_format,omitempty"` UseTLS *bool `json:"tls,omitempty"` VerifyTLS *bool `json:"tls_verify,omitempty"` TLSCertPath *string `json:"tls_cert_path,omitempty"` APIVersion *string `json:"api_version,omitempty"` Host *string `json:"host,omitempty"` ArchiveState *string `json:"archive_state,omitempty"` } func NewAppOptionsFromFile(dir string) (*AppOptions, error) { filePath := filepath.Join(dir, AppOptionsFilename) var result AppOptions err := fsutil.LoadStructFromFile(filePath, &result) if err != nil { if os.IsNotExist(err) { return nil, nil } if err == fsutil.ErrNoFileData { return nil, nil } return nil, err } return &result, nil } // TODO: robustly parse `--network`/Network at the CLI level to avoid ambiguity. // https://github.com/docker/cli/blob/cf8c4bab6477ef62122bda875f80d8472005010d/opts/network.go#L35 // ContainerOverrides provides a set of container field overrides // It can also be used to update the image instructions when // the "image-overrides" flag is provided type ContainerOverrides struct { User string Entrypoint []string ClearEntrypoint bool Cmd []string ClearCmd bool Workdir string Env []string Hostname string Network string ExposedPorts map[docker.Port]struct{} Volumes map[string]struct{} Labels map[string]string } // ImageNewInstructions provides a set new image instructions type ImageNewInstructions struct { Entrypoint []string ClearEntrypoint bool Cmd []string ClearCmd bool Workdir string Env []string Volumes map[string]struct{} ExposedPorts map[docker.Port]struct{} Labels map[string]string RemoveEnvs map[string]struct{} RemoveVolumes map[string]struct{} RemoveExposedPorts map[docker.Port]struct{} RemoveLabels map[string]struct{} } // ContainerBuildOptions provides the options to use when // building container images from Dockerfiles type ContainerBuildOptions struct { Dockerfile string DockerfileContext string Tag string ExtraHosts string BuildArgs []CBOBuildArg Labels map[string]string CacheFrom []string Target string NetworkMode string } type CBOBuildArg struct { Name string Value string } // ContainerRunOptions provides the options to use running a container type ContainerRunOptions struct { HostConfig *docker.HostConfig //Explicit overrides for the base and host config fields //Host config field override are applied //on top of the fields in the HostConfig struct if it's provided (volume mounts are merged though) Runtime string SysctlParams map[string]string ShmSize int64 } // VolumeMount provides the volume mount configuration information type VolumeMount struct { Source string Destination string Options string } const ( ProtoHTTP = "http" ProtoHTTPS = "https" ProtoHTTP2 = "http2" ProtoHTTP2C = "http2c" ProtoWS = "ws" ProtoWSS = "wss" ) func IsProto(value string) bool { switch strings.ToLower(value) { case ProtoHTTP, ProtoHTTPS, ProtoHTTP2, ProtoHTTP2C, ProtoWS, ProtoWSS: return true default: return false } } // HTTPProbeCmd provides the HTTP probe parameters type HTTPProbeCmd struct { Method string `json:"method"` Resource string `json:"resource"` Port int `json:"port"` Protocol string `json:"protocol"` Headers []string `json:"headers"` Body string `json:"body"` BodyFile string `json:"body_file"` Username string `json:"username"` Password string `json:"password"` Crawl bool `json:"crawl"` FastCGI *FastCGIProbeWrapperConfig `json:"fastcgi,omitempty"` } // FastCGI permits fine-grained configuration of the fastcgi RoundTripper. type FastCGIProbeWrapperConfig struct { // Root is the fastcgi root directory. // Defaults to the root directory of the container. Root string `json:"root,omitempty"` // The path in the URL will be split into two, with the first piece ending // with the value of SplitPath. The first piece will be assumed as the // actual resource (CGI script) name, and the second piece will be set to // PATH_INFO for the CGI script to use. SplitPath []string `json:"split_path,omitempty"` // Extra environment variables. EnvVars map[string]string `json:"env,omitempty"` // The duration used to set a deadline when connecting to an upstream. DialTimeout time.Duration `json:"dial_timeout,omitempty"` // The duration used to set a deadline when reading from the FastCGI server. ReadTimeout time.Duration `json:"read_timeout,omitempty"` // The duration used to set a deadline when sending to the FastCGI server. WriteTimeout time.Duration `json:"write_timeout,omitempty"` } // HTTPProbeCmds is a list of HTTPProbeCmd instances type HTTPProbeCmds struct { Commands []HTTPProbeCmd `json:"commands"` } // DockerClient provides Docker client parameters type DockerClient struct { UseTLS bool VerifyTLS bool TLSCertPath string Host string APIVersion string Env map[string]string } const ( CAMContainerProbe = "container-probe" CAMProbe = "probe" CAMEnter = "enter" CAMTimeout = "timeout" CAMSignal = "signal" CAMExec = "exec" CAMHostExec = "host-exec" CAMAppExit = "app-exit" ) // ContinueAfter provides the command execution mode parameters type ContinueAfter struct { Mode string Timeout time.Duration ContinueChan <-chan struct{} } type HTTPProbeOptions struct { Do bool Full bool ExitOnFailure bool Cmds []HTTPProbeCmd Ports []uint16 StartWait int RetryCount int RetryWait int CrawlMaxDepth int CrawlMaxPageCount int CrawlConcurrency int CrawlConcurrencyMax int APISpecs []string APISpecFiles []string ProxyEndpoint string ProxyPort int } type AppNodejsInspectOptions struct { IncludePackages []string NextOpts NodejsWebFrameworkInspectOptions NuxtOpts NodejsWebFrameworkInspectOptions } type NodejsWebFrameworkInspectOptions struct { IncludeAppDir bool IncludeBuildDir bool IncludeDistDir bool IncludeStaticDir bool IncludeNodeModulesDir bool } type KubernetesOptions struct { Target KubernetesTarget TargetOverride KubernetesTargetOverride Manifests []string Kubeconfig string } type KubernetesTarget struct { Workload string Namespace string Container string } func (t *KubernetesTarget) WorkloadName() (string, error) { parts := strings.Split(t.Workload, "/") if len(parts) != 2 || len(parts[1]) == 0 { return "", errors.New("malformed Kubernetes workload name") } return parts[1], nil } type KubernetesTargetOverride struct { Image string } func (ko KubernetesOptions) HasTargetSet() bool { return ko.Target.Workload != "" } ================================================ FILE: pkg/app/master/container/execution.go ================================================ package container import ( "bufio" "bytes" "errors" "fmt" "io" "os" "os/signal" "path/filepath" "strings" "syscall" "time" dockerapi "github.com/fsouza/go-dockerclient" "github.com/moby/term" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/config" ) //Custom I/O for terminal later const ( ContainerNamePat = "ds.run_%v_%v" ) type ovars = app.OutVars type ExecutionState string const ( XSNone ExecutionState = "xs.none" XSCreated = "xs.created" XSStarted = "xs.started" XSStopping = "xs.stopping" XSStopped = "xs.stopped" XSRemoved = "xs.removed" XSExited = "xs.exited" XSExitedCrash = "xs.exited.crash" XSError = "xs.error" ) type ExecutionEvent string const ( XECreated ExecutionEvent = "xe.container.created" XEStarted = "xe.container.started" XEStopping = "xe.container.stopping" XEStopped = "xe.container.stopped" XERemoved = "xe.container.removed" XEExited = "xe.container.exited" XEExitedCrash = "xe.container.exited.crash" XEAPIError = "xe.api.error" XEInterrupt = "xe.interrupt" ) type ExecutionEvenInfo struct { Event ExecutionEvent Data map[string]string } type VolumeInfo struct { Source string Destination string Options string } type ExecutionOptions struct { ContainerName string Entrypoint []string Cmd []string PublishPorts map[dockerapi.Port][]dockerapi.PortBinding EnvVars []string Volumes []config.VolumeMount LiveLogs bool Terminal bool IO ExecutionIO } type ExecutionIO struct { Input io.Reader Output io.Writer Error io.Writer } type Execution struct { ContainerInfo *dockerapi.Container ContainerName string ContainerID string State ExecutionState Crashed bool StopTimeout uint /// the following fields are forwarded to dockerapi.HostConfig and take /// the same parameters as docker's `--pid`, `--network` and `--ipc` CLI /// flags PidMode string NetworkMode string IpcMode string imageRef string APIClient *dockerapi.Client options *ExecutionOptions cleanupOnSysExit bool eventCh chan *ExecutionEvenInfo printState bool dockerEventCh chan *dockerapi.APIEvents dockerEventStopCh chan struct{} xc *app.ExecutionContext logger *log.Entry terminalExitChan chan error termFd uintptr isInterrupted bool } const defaultStopTimeout = 7 //7 seconds func NewExecution( xc *app.ExecutionContext, logger *log.Entry, client *dockerapi.Client, imageRef string, options *ExecutionOptions, eventCh chan *ExecutionEvenInfo, cleanupOnSysExit bool, printState bool) (*Execution, error) { if logger != nil { logger = logger.WithFields(log.Fields{"com": "container.execution"}) } exe := &Execution{ State: XSNone, StopTimeout: defaultStopTimeout, imageRef: imageRef, APIClient: client, options: options, eventCh: eventCh, cleanupOnSysExit: cleanupOnSysExit, printState: printState, xc: xc, logger: logger, dockerEventCh: make(chan *dockerapi.APIEvents), dockerEventStopCh: make(chan struct{}), } //allow liveLogs only if terminal is not enabled if exe.options != nil && exe.options.Terminal { exe.options.LiveLogs = false } return exe, nil } // Start starts a new container execution func (ref *Execution) Start() error { if ref.options.ContainerName != "" { //we have an explicitly provided container name to use //ideally we first validate that this name can be used... ref.ContainerName = ref.options.ContainerName } else { ref.ContainerName = fmt.Sprintf(ContainerNamePat, os.Getpid(), time.Now().UTC().Format("20060102150405")) } hostConfig := &dockerapi.HostConfig{ NetworkMode: ref.NetworkMode, PidMode: ref.PidMode, IpcMode: ref.IpcMode, } containerOptions := dockerapi.CreateContainerOptions{ Name: ref.ContainerName, Config: &dockerapi.Config{ Image: ref.imageRef, }, HostConfig: hostConfig, } if ref.options != nil { if len(ref.options.Entrypoint) > 0 { containerOptions.Config.Entrypoint = ref.options.Entrypoint } if len(ref.options.Cmd) > 0 { containerOptions.Config.Cmd = ref.options.Cmd } if len(ref.options.EnvVars) > 0 { containerOptions.Config.Env = ref.options.EnvVars } if len(ref.options.Volumes) > 0 { mounts := []dockerapi.HostMount{} for _, vol := range ref.options.Volumes { mount := dockerapi.HostMount{ Target: vol.Destination, } if vol.Source == "" { mount.Type = "volume" } else { if strings.HasPrefix(vol.Source, "/") { mount.Source = vol.Source mount.Type = "bind" } else if strings.HasPrefix(vol.Source, "~/") { hd, _ := os.UserHomeDir() mount.Source = filepath.Join(hd, vol.Source[2:]) mount.Type = "bind" } else if strings.HasPrefix(vol.Source, "./") || strings.HasPrefix(vol.Source, "../") || (vol.Source == "..") || (vol.Source == ".") { mount.Source, _ = filepath.Abs(vol.Source) mount.Type = "bind" } else { //todo: list volumes and check vol.Source instead of defaulting to named volume mount.Source = vol.Source mount.Type = "volume" } } if vol.Options == "ro" { mount.ReadOnly = true } mounts = append(mounts, mount) } containerOptions.HostConfig.Mounts = mounts } if len(ref.options.PublishPorts) > 0 { containerOptions.HostConfig.PortBindings = ref.options.PublishPorts } } if ref.options != nil && ref.options.Terminal { fmt.Println("adding more container params for Terminal") containerOptions.Config.OpenStdin = true //containerOptions.Config.StdinOnce = true containerOptions.Config.AttachStdin = true containerOptions.Config.Tty = true } containerInfo, err := ref.APIClient.CreateContainer(containerOptions) if err != nil { return err } ref.State = XSCreated if ref.ContainerName != containerInfo.Name { if ref.logger != nil { ref.logger.Debugf("RunContainer: Container name mismatch expected=%v got=%v", ref.ContainerName, ref.ContainerName) } } ref.ContainerID = containerInfo.ID if ref.printState && ref.xc != nil { ref.xc.Out.Info("container", ovars{ "status": "created", "name": ref.ContainerName, "id": ref.ContainerID, }) if ref.logger != nil { ref.logger.Tracef("container created: name=%s id=%s", ref.ContainerName, ref.ContainerID) } } if ref.eventCh != nil { ref.eventCh <- &ExecutionEvenInfo{ Event: XECreated, } } go ref.monitorContainerExitSync() if ref.cleanupOnSysExit { go ref.monitorSysExitSync() } if ref.options != nil { if ref.options.Terminal { var oldState *term.State var isTerminal bool ref.termFd, isTerminal = term.GetFdInfo(os.Stdout) if !isTerminal { return errors.New("not a terminal") } oldState, err = term.SetRawTerminal(ref.termFd) if err != nil { return err } defer term.RestoreTerminal(ref.termFd, oldState) ref.terminalExitChan = make(chan error) go ref.startTerminal() } else if ref.options.LiveLogs { go ref.startLiveLogs() } } if err := ref.APIClient.StartContainer(ref.ContainerID, nil); err != nil { ref.State = "error" return err } ref.State = XSStarted if ref.ContainerInfo, err = ref.APIClient.InspectContainer(ref.ContainerID); err != nil { ref.State = XSError if ref.eventCh != nil { ref.eventCh <- &ExecutionEvenInfo{ Event: XEAPIError, Data: map[string]string{ "error": err.Error(), }, } } return err } if ref.printState && ref.xc != nil { ref.xc.Out.Info("container", ovars{ "status": "started", "name": containerInfo.Name, "id": ref.ContainerID, }) if ref.logger != nil { ref.logger.Tracef("container started = name=%s id=%s\n", ref.ContainerName, ref.ContainerID) } } if ref.eventCh != nil { ref.eventCh <- &ExecutionEvenInfo{ Event: XEStarted, } } if ref.options != nil && ref.options.Terminal { go ref.monitorTerminalSizeSync() if ref.terminalExitChan != nil { attachErr := <-ref.terminalExitChan return attachErr } } return nil } // Stop stops the container execution func (ref *Execution) Stop() error { ref.State = XSStopping if ref.eventCh != nil { ref.eventCh <- &ExecutionEvenInfo{ Event: XEStopping, } } err := ref.APIClient.StopContainer(ref.ContainerID, ref.StopTimeout) if err != nil { if _, ok := err.(*dockerapi.ContainerNotRunning); ok { if ref.logger != nil { ref.logger.Info("can't stop the 'slim' container (container is not running)...") } } else { if ref.logger != nil { ref.logger.Infof("Execution.Stop: apiClient.StopContainer error - %v", err) } } } ref.State = XSStopped if ref.eventCh != nil { ref.eventCh <- &ExecutionEvenInfo{ Event: XEStopped, } } return err } // Cleanup removes stopped container for the execution func (ref *Execution) Cleanup() error { removeOption := dockerapi.RemoveContainerOptions{ ID: ref.ContainerID, RemoveVolumes: true, Force: true, } err := ref.APIClient.RemoveContainer(removeOption) if err != nil { if ref.logger != nil { ref.logger.Info("error removing container =>", err) } } ref.State = XSRemoved if ref.eventCh != nil { ref.eventCh <- &ExecutionEvenInfo{ Event: XERemoved, } } return err } // Wait waits the container execution func (ref *Execution) Wait() (int, error) { //TODO: use WaitContainerWithContext return ref.APIClient.WaitContainer(ref.ContainerID) } func (ref *Execution) monitorContainerExitSync() { ref.APIClient.AddEventListener(ref.dockerEventCh) for { select { case devent := <-ref.dockerEventCh: if devent == nil || devent.ID == "" || devent.Status == "" { break } if devent.ID == ref.ContainerID { if devent.Status == "die" { ref.State = XSExited exitEvent := &ExecutionEvenInfo{ Event: XEExited, } nonZeroExitCode := false exitCodeStr, ok := devent.Actor.Attributes["exitCode"] if ok && exitCodeStr != "" && exitCodeStr != "0" { nonZeroExitCode = true } if nonZeroExitCode { if ref.isInterrupted && exitCodeStr == "137" { if ref.logger != nil { ref.logger.Tracef("container interrupted (expected) = %s", ref.ContainerID) } } else { ref.State = XSExitedCrash ref.Crashed = true if ref.printState && ref.xc != nil { ref.xc.Out.Info("container", ovars{ "status": "crashed", "id": ref.ContainerID, "exit.code": exitCodeStr, }) if ref.logger != nil { ref.logger.Tracef("container crashed = %s", ref.ContainerID) } } exitEvent.Event = XEExitedCrash } } if exitEvent.Event == XEExited { if ref.printState && ref.xc != nil { ref.xc.Out.Info("container", ovars{ "status": "exited", "id": ref.ContainerID, "exit.code": exitCodeStr, }) if ref.logger != nil { ref.logger.Tracef("container exited = %s", ref.ContainerID) } } } if ref.eventCh != nil { ref.eventCh <- exitEvent } } } case <-ref.dockerEventStopCh: if ref.logger != nil { ref.logger.Debug("container.Execution.monitorContainerExitSync: Docker event monitor stopped") } return } } } func (ref *Execution) monitorSysExitSync() { signals := make(chan os.Signal, 1) signal.Notify(signals, syscall.SIGINT) <-signals ref.isInterrupted = true //_ = ref.APIClient.KillContainer(dockerapi.KillContainerOptions{ID: ref.ContainerID}) //if ref.logger != nil { // ref.logger.Debugf("Execution.monitorSysExitSync: received SIGINT, killing container %s", ref.ContainerID) //} if ref.eventCh != nil { ref.eventCh <- &ExecutionEvenInfo{ Event: XEInterrupt, } } err := ref.Stop() if err != nil { if ref.logger != nil { ref.logger.Debugf("ref.Stop error: id=%s err=%v", ref.ContainerID, err) } } } func (ref *Execution) startTerminal() { r, w := io.Pipe() go io.Copy(w, os.Stdin) options := dockerapi.AttachToContainerOptions{ Container: ref.ContainerID, InputStream: r, OutputStream: os.Stdout, ErrorStream: os.Stderr, Stdin: true, Stdout: true, Stderr: true, Stream: true, RawTerminal: true, Logs: true, } err := ref.APIClient.AttachToContainer(options) ref.terminalExitChan <- err } func (ref *Execution) startLiveLogs() { options := dockerapi.AttachToContainerOptions{ Container: ref.ContainerID, OutputStream: os.Stdout, ErrorStream: os.Stderr, Stdin: false, Stdout: true, Stderr: true, Logs: true, Stream: true, RawTerminal: true, } if ref.options != nil { if ref.options.IO.Output != nil { options.OutputStream = ref.options.IO.Output } if ref.options.IO.Error != nil { options.ErrorStream = ref.options.IO.Error } } err := ref.APIClient.AttachToContainer(options) if err != nil { panic(err) } } func (ref *Execution) ShowContainerLogs() { var outData bytes.Buffer outw := bufio.NewWriter(&outData) var errData bytes.Buffer errw := bufio.NewWriter(&errData) if ref.logger != nil { ref.logger.Debug("getting container logs => ", ref.ContainerID) } logsOptions := dockerapi.LogsOptions{ Container: ref.ContainerID, OutputStream: outw, ErrorStream: errw, Stdout: true, Stderr: true, } err := ref.APIClient.Logs(logsOptions) if err != nil { if ref.logger != nil { ref.logger.Infof("error getting container logs => %v - %v", ref.ContainerID, err) } } else { outw.Flush() errw.Flush() fmt.Printf("[%s] CONTAINER STDOUT:\n", ref.ContainerID) outData.WriteTo(os.Stdout) fmt.Printf("[%s] CONTAINER STDERR:\n", ref.ContainerID) errData.WriteTo(os.Stdout) fmt.Printf("[%s] END OF CONTAINER LOGS =============\n", ref.ContainerID) } } func (ref *Execution) monitorTerminalSizeSync() { ref.updateTerminalSize() winchCh := make(chan os.Signal, 1) signal.Notify(winchCh, syscall.SIGWINCH) defer signal.Stop(winchCh) for range winchCh { ref.updateTerminalSize() } } func (ref *Execution) updateTerminalSize() error { height, width := terminalSize(ref.termFd) if height == 0 && width == 0 { return nil } return ref.APIClient.ResizeContainerTTY(ref.ContainerID, height, width) } func terminalSize(fd uintptr) (int, int) { ws, err := term.GetWinsize(fd) if err != nil { if ws == nil { return 0, 0 } } return int(ws.Height), int(ws.Width) } ================================================ FILE: pkg/app/master/docker/dockerhost/host.go ================================================ package dockerhost import ( "net" "net/url" "os" dockerapi "github.com/fsouza/go-dockerclient" log "github.com/sirupsen/logrus" ) const ( localHostIP = "127.0.0.1" ) // GetIP returns the Docker host IP address func GetIP(apiClient *dockerapi.Client) string { dockerHost := os.Getenv("DOCKER_HOST") if dockerHost == "" { if apiClient != nil { netInfo, err := apiClient.NetworkInfo("bridge") if err != nil { log.WithFields(log.Fields{ "op": "dockerhost.GetIP", "error": err, }).Debug("apiClient.NetworkInfo") } else { if netInfo != nil && netInfo.Name == "bridge" { if len(netInfo.IPAM.Config) > 0 { return netInfo.IPAM.Config[0].Gateway } } } } return localHostIP } u, err := url.Parse(dockerHost) if err != nil { return localHostIP } switch u.Scheme { case "unix": return localHostIP default: host, _, err := net.SplitHostPort(u.Host) if err != nil { return localHostIP } return host } } ================================================ FILE: pkg/app/master/inspectors/container/container_inspector.go ================================================ package container import ( "bufio" "bytes" "errors" "fmt" "os" "os/signal" "path/filepath" "strings" "syscall" "time" "github.com/slimtoolkit/slim/pkg/aflag" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/config" "github.com/slimtoolkit/slim/pkg/app/master/docker/dockerhost" "github.com/slimtoolkit/slim/pkg/app/master/inspectors/image" "github.com/slimtoolkit/slim/pkg/app/master/inspectors/ipc" "github.com/slimtoolkit/slim/pkg/app/master/inspectors/sensor" "github.com/slimtoolkit/slim/pkg/app/master/security/apparmor" "github.com/slimtoolkit/slim/pkg/app/master/security/seccomp" "github.com/slimtoolkit/slim/pkg/docker/dockerutil" "github.com/slimtoolkit/slim/pkg/ipc/channel" "github.com/slimtoolkit/slim/pkg/ipc/command" "github.com/slimtoolkit/slim/pkg/ipc/event" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/util/errutil" "github.com/slimtoolkit/slim/pkg/util/fsutil" "github.com/slimtoolkit/slim/pkg/util/jsonutil" v "github.com/slimtoolkit/slim/pkg/version" containertypes "github.com/docker/docker/api/types/container" dockerapi "github.com/fsouza/go-dockerclient" log "github.com/sirupsen/logrus" ) // Container inspector constants const ( SensorIPCModeDirect = "direct" SensorIPCModeProxy = "proxy" SensorBinPath = "/opt/_slim/bin/slim-sensor" ContainerNamePat = "slimk_%v_%v" ArtifactsDir = "artifacts" ReportArtifactTar = "creport.tar" fileArtifactsTar = "files.tar" FileArtifactsOutTar = "files_out.tar" // FileArtifactsArchiveTar = "files_archive.tar" SensorMountPat = "%s:/opt/_slim/bin/slim-sensor:ro" VolumeSensorMountPat = "%s:/opt/_slim/bin:ro" LabelName = "_slim" MondelArtifactTar = "mondel.tar" ) type ovars = app.OutVars var ( cmdPortStrDefault = fmt.Sprintf("%d", channel.CmdPort) cmdPortSpecDefault = dockerapi.Port(fmt.Sprintf("%d/tcp", channel.CmdPort)) evtPortStrDefault = fmt.Sprintf("%d", channel.EvtPort) evtPortSpecDefault = dockerapi.Port(fmt.Sprintf("%d/tcp", channel.EvtPort)) ) var ErrStartMonitorTimeout = errors.New("start monitor timeout") const ( sensorVolumeBaseName = "slim-sensor" ) type NetNameInfo struct { Name string FullName string Aliases []string } // TODO(estroz): move all fields configured only after RunContainer is called // to a InspectorRunResponse struct returned by RunContainer. // Inspector is a container execution inspector type Inspector struct { ContainerInfo *dockerapi.Container ContainerPortsInfo string ContainerPortList string AvailablePorts map[dockerapi.Port]dockerapi.PortBinding // Ports found to be available for probing. ContainerID string ContainerName string FatContainerCmd []string LocalVolumePath string DoUseLocalMounts bool SensorVolumeName string DoKeepTmpArtifacts bool StatePath string CmdPort dockerapi.Port EvtPort dockerapi.Port DockerHostIP string ImageInspector *image.Inspector APIClient *dockerapi.Client Overrides *config.ContainerOverrides ExplicitVolumeMounts map[string]config.VolumeMount BaseMounts []dockerapi.HostMount BaseVolumesFrom []string DoPublishExposedPorts bool HasClassicLinks bool Links []string EtcHostsMaps []string DNSServers []string DNSSearchDomains []string DoShowContainerLogs bool DoEnableMondel bool RunTargetAsUser bool KeepPerms bool PathPerms map[string]*fsutil.AccessInfo ExcludePatterns map[string]*fsutil.AccessInfo DoExcludeVarLockFiles bool PreservePaths map[string]*fsutil.AccessInfo IncludePaths map[string]*fsutil.AccessInfo IncludeBins map[string]*fsutil.AccessInfo IncludeDirBinsList map[string]*fsutil.AccessInfo IncludeExes map[string]*fsutil.AccessInfo DoIncludeShell bool DoIncludeWorkdir bool DoIncludeCertAll bool DoIncludeCertBundles bool DoIncludeCertDirs bool DoIncludeCertPKAll bool DoIncludeCertPKDirs bool DoIncludeNew bool DoIncludeSSHClient bool DoIncludeOSLibsNet bool DoIncludeZoneInfo bool SelectedNetworks map[string]NetNameInfo DoDebug bool LogLevel string LogFormat string PrintState bool InContainer bool RTASourcePT bool DoObfuscateMetadata bool SensorIPCEndpoint string SensorIPCMode string TargetHost string dockerEventCh chan *dockerapi.APIEvents dockerEventStopCh chan struct{} isDone aflag.Type ipcClient *ipc.Client logger *log.Entry xc *app.ExecutionContext crOpts *config.ContainerRunOptions portBindings map[dockerapi.Port][]dockerapi.PortBinding appNodejsInspectOpts config.AppNodejsInspectOptions } func pathMapKeys(m map[string]*fsutil.AccessInfo) []string { if len(m) == 0 { return nil } keys := make([]string, 0, len(m)) for k := range m { keys = append(keys, k) } return keys } // NewInspector creates a new container execution inspector func NewInspector( xc *app.ExecutionContext, crOpts *config.ContainerRunOptions, logger *log.Entry, client *dockerapi.Client, statePath string, imageInspector *image.Inspector, localVolumePath string, doUseLocalMounts bool, sensorVolumeName string, doKeepTmpArtifacts bool, overrides *config.ContainerOverrides, explicitVolumeMounts map[string]config.VolumeMount, baseMounts []dockerapi.HostMount, baseVolumesFrom []string, portBindings map[dockerapi.Port][]dockerapi.PortBinding, doPublishExposedPorts bool, hasClassicLinks bool, links []string, etcHostsMaps []string, dnsServers []string, dnsSearchDomains []string, showContainerLogs bool, doEnableMondel bool, runTargetAsUser bool, keepPerms bool, pathPerms map[string]*fsutil.AccessInfo, excludePatterns map[string]*fsutil.AccessInfo, doExcludeVarLockFiles bool, preservePaths map[string]*fsutil.AccessInfo, includePaths map[string]*fsutil.AccessInfo, includeBins map[string]*fsutil.AccessInfo, includeDirBinsList map[string]*fsutil.AccessInfo, includeExes map[string]*fsutil.AccessInfo, doIncludeShell bool, doIncludeWorkdir bool, doIncludeCertAll bool, doIncludeCertBundles bool, doIncludeCertDirs bool, doIncludeCertPKAll bool, doIncludeCertPKDirs bool, doIncludeNew bool, doIncludeSSHClient bool, doIncludeOSLibsNet bool, doIncludeZoneInfo bool, selectedNetworks map[string]NetNameInfo, //serviceAliases []string, doDebug bool, logLevel string, logFormat string, inContainer bool, rtaSourcePT bool, doObfuscateMetadata bool, sensorIPCEndpoint string, sensorIPCMode string, printState bool, appNodejsInspectOpts config.AppNodejsInspectOptions) (*Inspector, error) { logger = logger.WithFields(log.Fields{"component": "container.inspector"}) inspector := &Inspector{ logger: logger, StatePath: statePath, LocalVolumePath: localVolumePath, DoUseLocalMounts: doUseLocalMounts, SensorVolumeName: sensorVolumeName, DoKeepTmpArtifacts: doKeepTmpArtifacts, CmdPort: cmdPortSpecDefault, EvtPort: evtPortSpecDefault, ImageInspector: imageInspector, APIClient: client, Overrides: overrides, ExplicitVolumeMounts: explicitVolumeMounts, BaseMounts: baseMounts, BaseVolumesFrom: baseVolumesFrom, DoPublishExposedPorts: doPublishExposedPorts, HasClassicLinks: hasClassicLinks, Links: links, EtcHostsMaps: etcHostsMaps, DNSServers: dnsServers, DNSSearchDomains: dnsSearchDomains, DoShowContainerLogs: showContainerLogs, DoEnableMondel: doEnableMondel, RunTargetAsUser: runTargetAsUser, KeepPerms: keepPerms, PathPerms: pathPerms, ExcludePatterns: excludePatterns, DoExcludeVarLockFiles: doExcludeVarLockFiles, PreservePaths: preservePaths, IncludePaths: includePaths, IncludeBins: includeBins, IncludeDirBinsList: includeDirBinsList, IncludeExes: includeExes, DoIncludeShell: doIncludeShell, DoIncludeWorkdir: doIncludeWorkdir, DoIncludeCertAll: doIncludeCertAll, DoIncludeCertBundles: doIncludeCertBundles, DoIncludeCertDirs: doIncludeCertDirs, DoIncludeCertPKAll: doIncludeCertPKAll, DoIncludeCertPKDirs: doIncludeCertPKDirs, DoIncludeNew: doIncludeNew, DoIncludeSSHClient: doIncludeSSHClient, DoIncludeOSLibsNet: doIncludeOSLibsNet, DoIncludeZoneInfo: doIncludeZoneInfo, SelectedNetworks: selectedNetworks, DoDebug: doDebug, LogLevel: logLevel, LogFormat: logFormat, PrintState: printState, InContainer: inContainer, RTASourcePT: rtaSourcePT, DoObfuscateMetadata: doObfuscateMetadata, SensorIPCEndpoint: sensorIPCEndpoint, SensorIPCMode: sensorIPCMode, xc: xc, crOpts: crOpts, portBindings: portBindings, appNodejsInspectOpts: appNodejsInspectOpts, } if overrides == nil { inspector.FatContainerCmd = BuildStartupCommand( imageInspector.ImageInfo.Config.Entrypoint, imageInspector.ImageInfo.Config.Cmd, imageInspector.ImageInfo.Config.Shell, false, nil, false, nil, ) } else { inspector.FatContainerCmd = BuildStartupCommand( imageInspector.ImageInfo.Config.Entrypoint, imageInspector.ImageInfo.Config.Cmd, imageInspector.ImageInfo.Config.Shell, overrides.ClearEntrypoint, overrides.Entrypoint, overrides.ClearCmd, overrides.Cmd, ) } logger.Debugf("FatContainerCmd - %+v", inspector.FatContainerCmd) inspector.dockerEventCh = make(chan *dockerapi.APIEvents) inspector.dockerEventStopCh = make(chan struct{}) return inspector, nil } // RunContainer starts the container inspector instance execution func (i *Inspector) RunContainer() error { logger := i.logger.WithField("op", "container.Inspector.RunContainer") artifactsPath := filepath.Join(i.LocalVolumePath, ArtifactsDir) sensorPath := sensor.EnsureLocalBinary(i.xc, i.logger, i.StatePath, i.PrintState) allMountsMap := map[string]dockerapi.HostMount{} //start with the base mounts (usually come from compose) if len(i.BaseMounts) > 0 { for _, m := range i.BaseMounts { mkey := fmt.Sprintf("%s:%s:%s", m.Type, m.Source, m.Target) allMountsMap[mkey] = m } } //var volumeBinds []string //then add binds and mounts from the host config param if i.crOpts != nil && i.crOpts.HostConfig != nil { //volumeBinds = i.crOpts.HostConfig.Binds for _, vb := range i.crOpts.HostConfig.Binds { parts := strings.Split(vb, ":") if len(parts) < 2 { logger.Errorf("invalid bind format in crOpts.HostConfig.Binds => %s", vb) continue } vm := dockerapi.HostMount{ Type: "bind", Source: parts[0], Target: parts[1], } if strings.HasPrefix(vm.Source, "~/") { hd, _ := os.UserHomeDir() vm.Source = filepath.Join(hd, vm.Source[2:]) } else if strings.HasPrefix(vm.Source, "./") || strings.HasPrefix(vm.Source, "../") || (vm.Source == "..") || (vm.Source == ".") { vm.Source, _ = filepath.Abs(vm.Source) } if len(parts) == 3 && parts[2] == "ro" { vm.ReadOnly = true } mkey := fmt.Sprintf("%s:%s:%s", vm.Type, vm.Source, vm.Target) allMountsMap[mkey] = vm } for _, vm := range i.crOpts.HostConfig.Mounts { mkey := fmt.Sprintf("%s:%s:%s", vm.Type, vm.Source, vm.Target) allMountsMap[mkey] = vm } } //configVolumes := i.Overrides.Volumes //if configVolumes == nil { // configVolumes = map[string]struct{}{} //} //then add volumes from overrides if i.Overrides != nil && len(i.Overrides.Volumes) > 0 { for vol := range i.Overrides.Volumes { vm := dockerapi.HostMount{ Type: "volume", Target: vol, } mkey := fmt.Sprintf("%s:%s:%s", vm.Type, vm.Source, vm.Target) allMountsMap[mkey] = vm } } //now handle the explicit volume mounts for _, vol := range i.ExplicitVolumeMounts { //mountInfo := fmt.Sprintf("%s:%s:%s", volumeMount.Source, volumeMount.Destination, volumeMount.Options) //volumeBinds = append(volumeBinds, mountInfo) vm := dockerapi.HostMount{ Target: vol.Destination, } if strings.HasPrefix(vol.Source, "/") { vm.Source = vol.Source vm.Type = "bind" } else if strings.HasPrefix(vol.Source, "~/") { hd, _ := os.UserHomeDir() vm.Source = filepath.Join(hd, vol.Source[2:]) vm.Type = "bind" } else if strings.HasPrefix(vol.Source, "./") || strings.HasPrefix(vol.Source, "../") || (vol.Source == "..") || (vol.Source == ".") { vm.Source, _ = filepath.Abs(vol.Source) vm.Type = "bind" } else { //todo: list volumes and check vol.Source instead of defaulting to named volume vm.Source = vol.Source vm.Type = "volume" } if vol.Options == "ro" { vm.ReadOnly = true } mkey := fmt.Sprintf("%s:%s:%s", vm.Type, vm.Source, vm.Target) allMountsMap[mkey] = vm } var err error var volumeName string if !i.DoUseLocalMounts { volumeName, err = ensureSensorVolume(i.logger, i.APIClient, sensorPath, i.SensorVolumeName) errutil.FailOn(err) } //var artifactsMountInfo string if i.DoUseLocalMounts { //"%s:/opt/_slim/artifacts" //artifactsMountInfo = fmt.Sprintf(ArtifactsMountPat, artifactsPath) //volumeBinds = append(volumeBinds, artifactsMountInfo) vm := dockerapi.HostMount{ Type: "bind", Source: artifactsPath, Target: app.DefaultArtifactsDirPath, } mkey := fmt.Sprintf("%s:%s:%s", vm.Type, vm.Source, vm.Target) allMountsMap[mkey] = vm } else { //artifactsMountInfo = app.DefaultArtifactsDirPath //configVolumes[artifactsMountInfo] = struct{}{} vm := dockerapi.HostMount{ Type: "volume", Target: app.DefaultArtifactsDirPath, } mkey := fmt.Sprintf("%s:%s:%s", vm.Type, vm.Source, vm.Target) allMountsMap[mkey] = vm } //var sensorMountInfo string if i.DoUseLocalMounts { //sensorMountInfo = fmt.Sprintf(SensorMountPat, sensorPath) vm := dockerapi.HostMount{ Type: "bind", Source: sensorPath, Target: SensorBinPath, ReadOnly: true, } mkey := fmt.Sprintf("%s:%s:%s", vm.Type, vm.Source, vm.Target) allMountsMap[mkey] = vm } else { //sensorMountInfo = fmt.Sprintf(VolumeSensorMountPat, volumeName) vm := dockerapi.HostMount{ Type: "volume", Source: volumeName, Target: "/opt/_slim/bin", ReadOnly: true, } mkey := fmt.Sprintf("%s:%s:%s", vm.Type, vm.Source, vm.Target) allMountsMap[mkey] = vm } //volumeBinds = append(volumeBinds, sensorMountInfo) var containerCmd []string if i.DoDebug { containerCmd = append(containerCmd, "-d") } if i.LogLevel != "" { containerCmd = append(containerCmd, "-log-level", i.LogLevel) } if i.LogFormat != "" { containerCmd = append(containerCmd, "-log-format", i.LogFormat) } if i.DoEnableMondel { containerCmd = append(containerCmd, "-n") } i.ContainerName = fmt.Sprintf(ContainerNamePat, os.Getpid(), time.Now().UTC().Format("20060102150405")) labels := i.Overrides.Labels if labels == nil { labels = map[string]string{} } labels["runtime.container.type"] = LabelName var hostConfig *dockerapi.HostConfig if i.crOpts != nil && i.crOpts.HostConfig != nil { hostConfig = i.crOpts.HostConfig } if hostConfig == nil { hostConfig = &dockerapi.HostConfig{} } //hostConfig.Binds = volumeBinds var mountsList []dockerapi.HostMount for _, m := range allMountsMap { mountsList = append(mountsList, m) } hostConfig.Mounts = mountsList hostConfig.Privileged = true hostConfig.UsernsMode = "host" hasSysAdminCap := false for _, c := range hostConfig.CapAdd { if c == "SYS_ADMIN" { hasSysAdminCap = true } } if !hasSysAdminCap { hostConfig.CapAdd = append(hostConfig.CapAdd, "SYS_ADMIN") } containerOptions := dockerapi.CreateContainerOptions{ Name: i.ContainerName, Config: &dockerapi.Config{ Image: i.ImageInspector.ImageRef, Entrypoint: []string{SensorBinPath}, Cmd: containerCmd, Env: i.Overrides.Env, Labels: labels, Hostname: i.Overrides.Hostname, WorkingDir: i.Overrides.Workdir, }, HostConfig: hostConfig, NetworkingConfig: &dockerapi.NetworkingConfig{}, } if i.crOpts != nil { if i.crOpts.Runtime != "" { containerOptions.HostConfig.Runtime = i.crOpts.Runtime logger.Debugf("using custom runtime => %s", containerOptions.HostConfig.Runtime) } if len(i.crOpts.SysctlParams) > 0 { containerOptions.HostConfig.Sysctls = i.crOpts.SysctlParams logger.Debugf("using sysctl params => %#v", containerOptions.HostConfig.Sysctls) } if i.crOpts.ShmSize > -1 { containerOptions.HostConfig.ShmSize = i.crOpts.ShmSize logger.Debugf("using shm-size params => %#v", containerOptions.HostConfig.ShmSize) } } //if len(configVolumes) > 0 { // containerOptions.Config.Volumes = configVolumes //} runAsUser := i.ImageInspector.ImageInfo.Config.User if i.Overrides.User != "" { runAsUser = i.Overrides.User } containerOptions.Config.User = "0:0" if runAsUser != "" && strings.ToLower(runAsUser) != "root" { //containerOptions.Config.Tty = true //containerOptions.Config.OpenStdin = true //NOTE: //when enabling TTY need to add extra params getting logs //or the client.Logs() call will fail with an //"Unrecognized input header" error } hostProbePorts := i.setPorts(&containerOptions) commsExposedPorts := containerOptions.Config.ExposedPorts if i.Overrides.Network != "" { // Non-user defined networks are *probably* a mode, ex. "host". if !containertypes.NetworkMode(i.Overrides.Network).IsUserDefined() { containerOptions.HostConfig.NetworkMode = i.Overrides.Network logger.Debugf("HostConfig.NetworkMode => %v", i.Overrides.Network) } if containerOptions.NetworkingConfig.EndpointsConfig == nil { containerOptions.NetworkingConfig.EndpointsConfig = map[string]*dockerapi.EndpointConfig{} } containerOptions.NetworkingConfig.EndpointsConfig[i.Overrides.Network] = &dockerapi.EndpointConfig{} logger.Debugf("NetworkingConfig.EndpointsConfig => %v", i.Overrides.Network) } // adding this separately for better visibility... if i.HasClassicLinks && len(i.Links) > 0 { containerOptions.HostConfig.Links = i.Links logger.Debugf("HostConfig.Links => %v", i.Links) } if len(i.EtcHostsMaps) > 0 { containerOptions.HostConfig.ExtraHosts = i.EtcHostsMaps logger.Debugf("HostConfig.ExtraHosts => %v", i.EtcHostsMaps) } if len(i.DNSServers) > 0 { containerOptions.HostConfig.DNS = i.DNSServers //for newer versions of Docker containerOptions.Config.DNS = i.DNSServers //for older versions of Docker logger.Debugf("HostConfig.DNS/Config.DNS => %v", i.DNSServers) } if len(i.DNSSearchDomains) > 0 { containerOptions.HostConfig.DNSSearch = i.DNSSearchDomains logger.Debugf("HostConfig.DNSSearch => %v", i.DNSSearchDomains) } containerInfo, err := i.APIClient.CreateContainer(containerOptions) if err != nil { return err } // note: now need to cleanup the created container if there's an error if i.ContainerName != containerInfo.Name { logger.Debugf("Container name mismatch expected=%v got=%v", i.ContainerName, containerInfo.Name) } i.ContainerID = containerInfo.ID if i.PrintState { i.xc.Out.Info("container", ovars{ "status": "created", "name": containerInfo.Name, "id": i.ContainerID, }) } if len(i.SelectedNetworks) > 0 { var networkLinks []string if !i.HasClassicLinks && len(i.Links) > 0 { networkLinks = i.Links } logger.Debugf("SelectedNetworks => %#v", i.SelectedNetworks) for key, netNameInfo := range i.SelectedNetworks { err = attachContainerToNetwork(i.logger, i.APIClient, i.ContainerID, netNameInfo, networkLinks) if err != nil { logger.Debugf("AttachContainerToNetwork(%s,%+v) key=%s error => %#v", i.ContainerID, netNameInfo, key, err) return err } } } if err := i.APIClient.AddEventListener(i.dockerEventCh); err != nil { logger.Debugf("i.APIClient.AddEventListener error => %v", err) return err } go func() { for { select { case devent := <-i.dockerEventCh: if devent == nil || devent.ID == "" || devent.Status == "" { break } if devent.ID == i.ContainerID { if devent.Status == "die" { nonZeroExitCode := false exitCodeStr, ok := devent.Actor.Attributes["exitCode"] if ok && exitCodeStr != "" && exitCodeStr != "0" { nonZeroExitCode = true } if nonZeroExitCode { if i.PrintState { i.xc.Out.Info("container", ovars{ "status": "crashed", "id": i.ContainerID, "exit.code": exitCodeStr, }) } i.ShowContainerLogs() if i.PrintState { i.xc.Out.State("exited", ovars{ "exit.code": -999, "version": v.Current(), "location.exe": fsutil.ExeDir(), "location.sensor": sensorPath, "sensor.filemode": fsutil.FileMode(sensorPath), "sensor.volume": volumeName, }) } i.xc.Exit(-999) } } } case <-i.dockerEventStopCh: logger.Debug("Docker event monitor stopped") return } } }() signals := make(chan os.Signal, 1) signal.Notify(signals, syscall.SIGINT) go func() { select { case <-signals: _ = i.APIClient.KillContainer(dockerapi.KillContainerOptions{ID: i.ContainerID}) logger.Fatalf("[SIGMON] received SIGINT, killing container %s", i.ContainerID) case <-i.dockerEventStopCh: logger.Debug("[SIGMON] Docker event monitor stopped") //not killing target because we are going through a graceful shutdown //where we sent the StopMonitor and ShutdownSensor ipc commands } }() if err := i.APIClient.StartContainer(i.ContainerID, nil); err != nil { return err } inspectContainerOpts := dockerapi.InspectContainerOptions{ID: i.ContainerID, Size: true} if i.ContainerInfo, err = i.APIClient.InspectContainerWithOptions(inspectContainerOpts); err != nil { return err } if i.ContainerInfo.NetworkSettings == nil { return fmt.Errorf("slim: error => no network info") } if hCfg := i.ContainerInfo.HostConfig; hCfg != nil && !i.isHostNetworked() { logger.Debugf("container HostConfig.NetworkMode => %s len(ports)=%d", hCfg.NetworkMode, len(i.ContainerInfo.NetworkSettings.Ports)) if len(i.ContainerInfo.NetworkSettings.Ports) < len(commsExposedPorts) { return fmt.Errorf("slim: error => missing comms ports") } } logger.Debugf("container NetworkSettings.Ports => %#v", i.ContainerInfo.NetworkSettings.Ports) i.setAvailablePorts(hostProbePorts) if i.PrintState { i.xc.Out.Info("container", ovars{ "status": "running", "name": containerInfo.Name, "id": i.ContainerID, }) } if err = i.initContainerChannels(); err != nil { return err } cmd := &command.StartMonitor{ RTASourcePT: i.RTASourcePT, AppName: i.FatContainerCmd[0], } if len(i.FatContainerCmd) > 1 { cmd.AppArgs = i.FatContainerCmd[1:] } if len(i.ExcludePatterns) > 0 { cmd.Excludes = pathMapKeys(i.ExcludePatterns) } cmd.ExcludeVarLockFiles = i.DoExcludeVarLockFiles if len(i.PreservePaths) > 0 { cmd.Preserves = i.PreservePaths } if len(i.IncludePaths) > 0 { cmd.Includes = i.IncludePaths } cmd.KeepPerms = i.KeepPerms if len(i.PathPerms) > 0 { cmd.Perms = i.PathPerms } if len(i.IncludeBins) > 0 { cmd.IncludeBins = pathMapKeys(i.IncludeBins) } if len(i.IncludeDirBinsList) > 0 { cmd.IncludeDirBinsList = i.IncludeDirBinsList } if len(i.IncludeExes) > 0 { cmd.IncludeExes = pathMapKeys(i.IncludeExes) } cmd.IncludeShell = i.DoIncludeShell if i.DoIncludeWorkdir { cmd.IncludeWorkdir = i.ImageInspector.ImageInfo.Config.WorkingDir } cmd.IncludeCertAll = i.DoIncludeCertAll cmd.IncludeCertBundles = i.DoIncludeCertBundles cmd.IncludeCertDirs = i.DoIncludeCertDirs cmd.IncludeCertPKAll = i.DoIncludeCertPKAll cmd.IncludeCertPKDirs = i.DoIncludeCertPKDirs cmd.IncludeNew = i.DoIncludeNew cmd.IncludeSSHClient = i.DoIncludeSSHClient cmd.IncludeOSLibsNet = i.DoIncludeOSLibsNet cmd.IncludeZoneInfo = i.DoIncludeZoneInfo if runAsUser != "" { cmd.AppUser = runAsUser if strings.ToLower(runAsUser) != "root" { cmd.RunTargetAsUser = i.RunTargetAsUser } } cmd.IncludeAppNextDir = i.appNodejsInspectOpts.NextOpts.IncludeAppDir cmd.IncludeAppNextBuildDir = i.appNodejsInspectOpts.NextOpts.IncludeBuildDir cmd.IncludeAppNextDistDir = i.appNodejsInspectOpts.NextOpts.IncludeDistDir cmd.IncludeAppNextStaticDir = i.appNodejsInspectOpts.NextOpts.IncludeStaticDir cmd.IncludeAppNextNodeModulesDir = i.appNodejsInspectOpts.NextOpts.IncludeNodeModulesDir cmd.IncludeAppNuxtDir = i.appNodejsInspectOpts.NuxtOpts.IncludeAppDir cmd.IncludeAppNuxtBuildDir = i.appNodejsInspectOpts.NuxtOpts.IncludeBuildDir cmd.IncludeAppNuxtDistDir = i.appNodejsInspectOpts.NuxtOpts.IncludeDistDir cmd.IncludeAppNuxtStaticDir = i.appNodejsInspectOpts.NuxtOpts.IncludeStaticDir cmd.IncludeAppNuxtNodeModulesDir = i.appNodejsInspectOpts.NuxtOpts.IncludeNodeModulesDir cmd.IncludeNodePackages = i.appNodejsInspectOpts.IncludePackages cmd.ObfuscateMetadata = i.DoObfuscateMetadata _, err = i.ipcClient.SendCommand(cmd) if err != nil { return err } if i.PrintState { i.xc.Out.Info("cmd.startmonitor", ovars{ "status": "sent", }) } // We really need this code block to produce conclusive // outcomes. Hence, many retries to prevent (most of) the // premature terminations of the master process with the // sensor process (in a container) remaining (semi-)started. for idx := 0; idx < 16; idx++ { evt, err := i.ipcClient.GetEvent() if err != nil { if os.IsTimeout(err) || err == channel.ErrWaitTimeout { if i.PrintState { i.xc.Out.Info("event.startmonitor.done", ovars{ "status": "receive.timeout", }) } logger.Debug("timeout waiting for the slim container to start...") continue } return err } if evt == nil || evt.Name == "" { logger.Warn("empty event waiting for the slim container to start (trying again)...") continue } if evt.Name == event.StartMonitorDone { if i.PrintState { i.xc.Out.Info("event.startmonitor.done", ovars{ "status": "received", }) } return nil } if evt.Name == event.Error { if i.PrintState { i.xc.Out.Info("event.error", ovars{ "status": "received", "data": evt.Data, }) i.xc.Out.State("exited", ovars{ "exit.code": -124, "component": "container.inspector", "version": v.Current(), }) } //not returning an error, exiting, need to clean up the container i.ShutdownContainer(true) i.xc.Exit(-124) } if evt.Name != event.StartMonitorDone { if i.PrintState { i.xc.Out.Info("event.startmonitor.done", ovars{ "status": "received.unexpected", "data": jsonutil.ToString(evt), }) } return event.ErrUnexpectedEvent } } return ErrStartMonitorTimeout } // isHostNetworked returns true if either the created container's network mode is "host" // or if the Inspector is configured with a network "host". func (i *Inspector) isHostNetworked() bool { if i.ContainerInfo != nil { return containertypes.NetworkMode(i.ContainerInfo.HostConfig.NetworkMode).IsHost() } return containertypes.NetworkMode(i.Overrides.Network).IsHost() } const localHostIP = "127.0.0.1" // setPorts sets all port fields in CreateContainerOptions from user input and defaults. // Exposed tcp ports are returned as hostProbePorts for containers configured with host networks, // as those ports are exposed directly by the contained application on the loopback interface, // and will not be surfaced in network settings. func (i *Inspector) setPorts(ctrOpts *dockerapi.CreateContainerOptions) (hostProbePorts map[dockerapi.Port][]dockerapi.PortBinding) { // This is the minimal set of ports to either expose or directly use. commsExposedPorts := map[dockerapi.Port]struct{}{ i.CmdPort: {}, i.EvtPort: {}, } //add comms ports to the exposed ports in the container if len(i.Overrides.ExposedPorts) > 0 { ctrOpts.Config.ExposedPorts = i.Overrides.ExposedPorts for k, v := range commsExposedPorts { if _, ok := ctrOpts.Config.ExposedPorts[k]; ok { i.logger.Errorf("RunContainer: comms port conflict => %v", k) } ctrOpts.Config.ExposedPorts[k] = v } i.logger.Debugf("RunContainer: Config.ExposedPorts => %#v", ctrOpts.Config.ExposedPorts) } else { ctrOpts.Config.ExposedPorts = commsExposedPorts i.logger.Debugf("RunContainer: default exposed ports => %#v", ctrOpts.Config.ExposedPorts) } if len(i.portBindings) > 0 { //need to add the IPC ports too cmdPort := dockerapi.Port(i.CmdPort) evtPort := dockerapi.Port(i.EvtPort) if pbInfo, ok := i.portBindings[cmdPort]; ok { i.exitIPCPortConflict(pbInfo, "cmd", -126) } if pbInfo, ok := i.portBindings[evtPort]; ok { i.exitIPCPortConflict(pbInfo, "evt", -127) } i.portBindings[cmdPort] = []dockerapi.PortBinding{{HostPort: cmdPortStrDefault}} i.portBindings[evtPort] = []dockerapi.PortBinding{{HostPort: evtPortStrDefault}} ctrOpts.HostConfig.PortBindings = i.portBindings } else if i.DoPublishExposedPorts { portBindings := map[dockerapi.Port][]dockerapi.PortBinding{} if i.ImageInspector.ImageInfo.Config != nil { for p := range i.ImageInspector.ImageInfo.Config.ExposedPorts { portBindings[p] = []dockerapi.PortBinding{{ HostPort: p.Port(), }} } } for p := range ctrOpts.Config.ExposedPorts { portBindings[p] = []dockerapi.PortBinding{{ HostPort: p.Port(), }} } ctrOpts.HostConfig.PortBindings = portBindings i.logger.Debugf("RunContainer: publishExposedPorts/portBindings => %+v", portBindings) } else { ctrOpts.HostConfig.PublishAllPorts = true i.logger.Debugf("RunContainer: HostConfig.PublishAllPorts => %v", ctrOpts.HostConfig.PublishAllPorts) } if i.isHostNetworked() { portMap := map[dockerapi.Port][]dockerapi.PortBinding{} if ctrOpts.HostConfig.PublishAllPorts { for p := range ctrOpts.Config.ExposedPorts { portMap[p] = []dockerapi.PortBinding{{HostPort: p.Port()}} } } else { portMap = ctrOpts.HostConfig.PortBindings } hostProbePorts = map[dockerapi.Port][]dockerapi.PortBinding{} for p, pbindings := range portMap { if p == i.CmdPort || p == i.EvtPort || p.Proto() != "tcp" { continue } if len(pbindings) == 0 { pbindings = []dockerapi.PortBinding{{HostPort: p.Port()}} } // Ensure all bindings at least have the loopback IP since this interface is // where host TCP ports are exposed by default. for i, pbinding := range pbindings { if pbinding.HostIP == "" { pbindings[i].HostIP = localHostIP } } hostProbePorts[p] = pbindings } i.logger.Debugf("RunContainer: host network loopback ports => %v", hostProbePorts) } return hostProbePorts } func (i *Inspector) setAvailablePorts(hostProbePorts map[dockerapi.Port][]dockerapi.PortBinding) { i.AvailablePorts = map[dockerapi.Port]dockerapi.PortBinding{} addPorts := func(keys, list []string, pk dockerapi.Port, pbinding []dockerapi.PortBinding) ([]string, []string) { if len(pbinding) > 0 { keys = append(keys, fmt.Sprintf("%v => %v:%v", pk, pbinding[0].HostIP, pbinding[0].HostPort)) list = append(list, string(pbinding[0].HostPort)) } else { keys = append(keys, string(pk)) } return keys, list } // These may be empty if host networking is used. var portKeys, portList []string for pk, pbinding := range i.ContainerInfo.NetworkSettings.Ports { if pk == i.CmdPort || pk == i.EvtPort { continue } if len(pbinding) == 0 { i.logger.Debugf("setAvailablePorts: skipping empty port bindings => pk=%v", pk) continue } i.AvailablePorts[pk] = pbinding[0] portKeys, portList = addPorts(portKeys, portList, pk, pbinding) } if i.isHostNetworked() { for pk, pbinding := range hostProbePorts { if pk == i.CmdPort || pk == i.EvtPort { continue } // The above loop handled this key/binding. if b, added := i.AvailablePorts[pk]; added && b == (dockerapi.PortBinding{}) { continue } i.AvailablePorts[pk] = pbinding[0] portKeys, portList = addPorts(portKeys, portList, pk, pbinding) } } i.ContainerPortList = strings.Join(portList, ",") i.ContainerPortsInfo = strings.Join(portKeys, ",") if i.isHostNetworked() { const hostMsg = "(ports on host loopback)" if i.ContainerPortList != "" { i.ContainerPortList = fmt.Sprintf("%s %s", i.ContainerPortList, hostMsg) } if i.ContainerPortsInfo != "" { i.ContainerPortsInfo = fmt.Sprintf("%s %s", i.ContainerPortsInfo, hostMsg) } } } func (i *Inspector) exitIPCPortConflict(port []dockerapi.PortBinding, typ string, code int) { i.logger.Errorf("RunContainer: port bindings comms port conflict (%s) = %#v", typ, port) if i.PrintState { i.xc.Out.Info("sensor.error", ovars{ "message": "port binding ipc port conflict", "type": typ, }) i.xc.Out.State("exited", ovars{ "exit.code": code, "component": "container.inspector", "version": v.Current(), }) } i.xc.Exit(code) } func (i *Inspector) ShowContainerLogs() { var outData bytes.Buffer outw := bufio.NewWriter(&outData) var errData bytes.Buffer errw := bufio.NewWriter(&errData) i.logger.Debug("getting container logs => ", i.ContainerID) logsOptions := dockerapi.LogsOptions{ Container: i.ContainerID, OutputStream: outw, ErrorStream: errw, Stdout: true, Stderr: true, } err := i.APIClient.Logs(logsOptions) if err != nil { i.logger.Infof("error getting container logs => %v - %v", i.ContainerID, err) } else { outw.Flush() errw.Flush() fmt.Println("slim: container stdout:") _, _ = outData.WriteTo(os.Stdout) fmt.Println("slim: container stderr:") _, _ = errData.WriteTo(os.Stdout) fmt.Println("slim: end of container logs =============") } } // ShutdownContainer terminates the container inspector instance execution func (i *Inspector) ShutdownContainer(terminateOnly bool) error { if i.ContainerID == "" { //no container to shutdown... return nil } if i.isDone.IsOn() { return nil } logger := i.logger.WithField("op", "container.Inspector.ShutdownContainer") i.isDone.On() defer func() { i.shutdownContainerChannels() if i.DoShowContainerLogs { i.ShowContainerLogs() } err := i.APIClient.StopContainer(i.ContainerID, 9) if _, ok := err.(*dockerapi.ContainerNotRunning); ok { logger.Info("can't stop the slim container (container is not running)...") } else { errutil.WarnOn(err) } removeOption := dockerapi.RemoveContainerOptions{ ID: i.ContainerID, RemoveVolumes: true, Force: true, } if err := i.APIClient.RemoveContainer(removeOption); err != nil { logger.Infof("error removing container ('%v')... terminating container", err) _ = i.APIClient.KillContainer(dockerapi.KillContainerOptions{ID: i.ContainerID}) } }() if !terminateOnly { if !i.DoUseLocalMounts { deleteOrig := true if i.DoKeepTmpArtifacts { deleteOrig = false } //copy the container report reportLocalPath := filepath.Join(i.LocalVolumePath, ArtifactsDir, ReportArtifactTar) reportRemotePath := filepath.Join(app.DefaultArtifactsDirPath, report.DefaultContainerReportFileName) err := dockerutil.CopyFromContainer(i.APIClient, i.ContainerID, reportRemotePath, reportLocalPath, true, deleteOrig) if err != nil { logger.WithError(err).WithField("container", i.ContainerID).Error("dockerutil.CopyFromContainer") //can't call errutil.FailOn() because we won't cleanup the target container return err } if i.DoEnableMondel { //copy the monitor data event log (if available) mondelLocalPath := filepath.Join(i.LocalVolumePath, ArtifactsDir, MondelArtifactTar) mondelRemotePath := filepath.Join(app.DefaultArtifactsDirPath, report.DefaultMonDelFileName) err = dockerutil.CopyFromContainer(i.APIClient, i.ContainerID, mondelRemotePath, mondelLocalPath, true, deleteOrig) if err != nil { //not a failure because the log might not be there (just log it) logger.WithFields(log.Fields{ "artifact.type": "mondel", "local.path": mondelLocalPath, "remote.path": mondelRemotePath, "err": err, }).Debug("dockerutil.CopyFromContainer") } } /* //ALTERNATIVE WAY TO XFER THE FILE ARTIFACTS filesOutLocalPath := filepath.Join(i.LocalVolumePath, ArtifactsDir, FileArtifactsArchiveTar) filesTarRemotePath := filepath.Join(app.DefaultArtifactsDirPath, fileArtifactsTar) err = dockerutil.CopyFromContainer(i.APIClient, i.ContainerID, filesTarRemotePath, filesOutLocalPath, true, false) //make it 'true' once tested/debugged if err != nil { errutil.FailOn(err) } */ filesOutLocalPath := filepath.Join(i.LocalVolumePath, ArtifactsDir, FileArtifactsOutTar) filesRemotePath := filepath.Join(app.DefaultArtifactsDirPath, app.ArtifactFilesDirName) err = dockerutil.CopyFromContainer(i.APIClient, i.ContainerID, filesRemotePath, filesOutLocalPath, false, false) if err != nil { logger.WithError(err).WithField("container", i.ContainerID).Error("dockerutil.CopyFromContainer") //can't call errutil.FailOn() because we won't cleanup the target container return err } //NOTE: possible enhancement (if the original filemode bits still get lost) //(alternative to archiving files in the container to preserve filemodes) //Rewrite the filemode bits using the data from creport.json, //but creport.json also needs to be enhanced to use //octal filemodes for the file records err = dockerutil.PrepareContainerDataArchive(filesOutLocalPath, fileArtifactsTar, app.ArtifactFilesDirName+"/", deleteOrig) if err != nil { logger.WithError(err).WithField("container", i.ContainerID).Error("dockerutil.PrepareContainerDataArchive") //can't call errutil.FailOn() because we won't cleanup the target container return err } } } return nil } // FinishMonitoring ends the target container monitoring activities func (i *Inspector) FinishMonitoring() { if i.dockerEventStopCh == nil { if i.PrintState { i.xc.Out.Info("container.inspector", ovars{ "message": "already finished monitoring", }) } return } close(i.dockerEventStopCh) i.dockerEventStopCh = nil cmdResponse, err := i.ipcClient.SendCommand(&command.StopMonitor{}) errutil.WarnOn(err) //_ = cmdResponse i.logger.Debugf("'stop' monitor response => '%v'", cmdResponse) i.logger.Info("waiting for the container to finish its work...") evt, err := i.ipcClient.GetEvent() i.logger.Debugf("sensor event => '%v'", evt) errutil.WarnOn(err) _ = evt i.logger.Debugf("sensor event => '%v'", evt) cmdResponse, err = i.ipcClient.SendCommand(&command.ShutdownSensor{}) if err != nil { i.logger.Debugf("error sending 'shutdown' => '%v'", err) } i.logger.Debugf("'shutdown' sensor response => '%v'", cmdResponse) } func (i *Inspector) initContainerChannels() error { const op = "container.Inspector.initContainerChannels" var cn string if i.Overrides != nil { cn = i.Overrides.Network } // Top level IP info will not be populated when not using "bridge", // which is only set for backwards compatibility. // // https://github.com/moby/moby/issues/21658#issuecomment-203527083 ipAddr := i.ContainerInfo.NetworkSettings.IPAddress if cn != "" { network, found := i.ContainerInfo.NetworkSettings.Networks[cn] errutil.FailWhen(!found, fmt.Sprintf("slim: error => expected NetworkSettings.Networks to contain %s: %v", cn, i.ContainerInfo.NetworkSettings.Networks)) ipAddr = network.IPAddress } // If running in host mode, no IP may be set but the contained application // is listening on the exposed ports on localhost. if ipAddr == "" && i.isHostNetworked() { ipAddr = localHostIP } if i.PrintState { i.xc.Out.Info("container", ovars{ "message": "obtained IP address", "ip": ipAddr, }) } var ipcMode string switch i.SensorIPCMode { case SensorIPCModeDirect, SensorIPCModeProxy: ipcMode = i.SensorIPCMode default: if i.InContainer || i.isHostNetworked() { ipcMode = SensorIPCModeDirect } else { ipcMode = SensorIPCModeProxy } } var cmdPort, evtPort string switch ipcMode { case SensorIPCModeDirect: i.TargetHost = ipAddr cmdPort = i.CmdPort.Port() evtPort = i.EvtPort.Port() case SensorIPCModeProxy: i.DockerHostIP = dockerhost.GetIP(i.APIClient) i.TargetHost = i.DockerHostIP cmdPortBindings := i.ContainerInfo.NetworkSettings.Ports[i.CmdPort] evtPortBindings := i.ContainerInfo.NetworkSettings.Ports[i.EvtPort] cmdPort = cmdPortBindings[0].HostPort evtPort = evtPortBindings[0].HostPort } i.SensorIPCMode = ipcMode if i.SensorIPCEndpoint != "" { i.TargetHost = i.SensorIPCEndpoint } i.logger.WithFields(log.Fields{ "op": op, "in.container": i.InContainer, "container.network": cn, "ipc.mode": ipcMode, "target": i.TargetHost, "port.cmd": cmdPort, "port.evt": evtPort, }).Debugf("target.container.ipc.connect") ipcClient, err := ipc.NewClient(i.TargetHost, cmdPort, evtPort, sensor.DefaultConnectWait) if err != nil { return err } i.ipcClient = ipcClient return nil } func (i *Inspector) shutdownContainerChannels() { const op = "container.Inspector.shutdownContainerChannels" if i.ipcClient != nil { if err := i.ipcClient.Stop(); err != nil { i.logger.WithFields(log.Fields{ "op": op, "error": err, }).Debug("shutting down channels") } i.ipcClient = nil } } // HasCollectedData returns true if any data was produced monitoring the target container func (i *Inspector) HasCollectedData() bool { return fsutil.Exists(filepath.Join(i.ImageInspector.ArtifactLocation, report.DefaultContainerReportFileName)) } // ProcessCollectedData performs post-processing on the collected container data func (i *Inspector) ProcessCollectedData() error { i.logger.Info("generating AppArmor profile...") err := apparmor.GenProfile(i.ImageInspector.ArtifactLocation, i.ImageInspector.AppArmorProfileName) if err != nil { return err } return seccomp.GenProfile(i.ImageInspector.ArtifactLocation, i.ImageInspector.SeccompProfileName) } ///////////////////////////////////////////////////////////////////////////////// func sensorVolumeName() string { return fmt.Sprintf("%s.%s", sensorVolumeBaseName, v.Tag()) } func ensureSensorVolume(logger *log.Entry, client *dockerapi.Client, localSensorPath, volumeName string) (string, error) { if volumeName == "" { volumeName = sensorVolumeName() } err := dockerutil.HasVolume(client, volumeName) switch { case err == nil: logger.Debugf("ensureSensorVolume: already have volume = %v", volumeName) //TODO: need to check if the volume has the sensor (otherwise delete and recreate) case err == dockerutil.ErrNotFound: logger.Debugf("ensureSensorVolume: no volume yet = %v", volumeName) if dockerutil.HasEmptyImage(client) == dockerutil.ErrNotFound { err := dockerutil.BuildEmptyImage(client) if err != nil { logger.Debugf("ensureSensorVolume: dockerutil.BuildEmptyImage() - error = %v", err) return "", err } } err = dockerutil.CreateVolumeWithData(client, localSensorPath, volumeName, nil) if err != nil { logger.Debugf("ensureSensorVolume: dockerutil.CreateVolumeWithData() - error = %v", err) return "", err } default: logger.Debugf("ensureSensorVolume: dockerutil.HasVolume() - error = %v", err) return "", err } return volumeName, nil } func attachContainerToNetwork( logger *log.Entry, apiClient *dockerapi.Client, containerID string, netNameInfo NetNameInfo, networkLinks []string) error { //network names seem to work ok (no need to use need network IDs) options := dockerapi.NetworkConnectionOptions{ Container: containerID, EndpointConfig: &dockerapi.EndpointConfig{ Aliases: netNameInfo.Aliases, }, } if len(networkLinks) > 0 { options.EndpointConfig.Links = networkLinks } if err := apiClient.ConnectNetwork(netNameInfo.FullName, options); err != nil { logger.Debugf("attachContainerToNetwork(%s,%s,%s): container network connect error - %v", containerID, netNameInfo.FullName, networkLinks, err) return err } return nil } ================================================ FILE: pkg/app/master/inspectors/container/container_startup.go ================================================ package container import ( "strings" ) var defaultShellFormPrefix = []string{"/bin/sh", "-c"} func hasPrefixSlice(input []string, prefix []string) bool { if len(prefix) > len(input) { return false } for idx, val := range prefix { if input[idx] != val { return false } } return true } func BuildStartupCommand( entrypoint []string, cmd []string, shell []string, clearEntrypoint bool, newEntrypoint []string, clearCmd bool, newCmd []string) []string { var output []string if len(shell) == 0 { shell = defaultShellFormPrefix } // Dockerfile CMD and runtime cmd params // are ignored when entrypoint is in shell form entryIsShellForm := hasPrefixSlice(entrypoint, shell) //note: need to refactor this messy algorithm (keeping it as-is for now)... if !clearEntrypoint && !clearCmd && len(newEntrypoint) == 0 && len(newCmd) == 0 { //if entrypoint is in shell format then ignore cmd output = append(output, entrypoint...) if !entryIsShellForm { output = append(output, cmd...) } } else { if len(newEntrypoint) > 0 || clearEntrypoint { output = append(output, newEntrypoint...) if len(newCmd) > 0 { output = append(output, newCmd...) } //note: not using CMD from image if there's an override for ENTRYPOINT } else { output = append(output, entrypoint...) if len(newCmd) > 0 || clearCmd { output = append(output, newCmd...) } else { if !entryIsShellForm { output = append(output, cmd...) } } } } emptyIdx := -1 for idx, val := range output { val = strings.TrimSpace(val) if val != "" { break } emptyIdx = idx } if emptyIdx > -1 { output = output[emptyIdx+1:] } return output } ================================================ FILE: pkg/app/master/inspectors/image/extract_registry_test.go ================================================ package image import ( "reflect" "testing" ) func TestRegistryExtraction(t *testing.T) { tt := []struct { in string expected string }{ {in: "https://gcr.io/nginx/nginx:3.9.11", expected: "https://gcr.io"}, {in: "https://www.gcr.io/nginx/nginx:3.9.11", expected: "https://www.gcr.io"}, {in: "eu.gcr.io/nginx/nginx:3.9.11", expected: "eu.gcr.io"}, {in: "https://mcr.com/puppet/nginx:1.1.11", expected: "https://mcr.com"}, {in: "http://192.168.10.11/nginx/nginx:3.9.11", expected: "http://192.168.10.11"}, {in: "http://192.158.1.10:2678/nginx/nginx:3.9.11", expected: "http://192.158.1.10:2678"}, {in: "https://192.158.1.10:2678/nginx/nginx:3.9.11", expected: "https://192.158.1.10:2678"}, {in: "https://2001:0db8:85a3:0000:0000:8a2e:0370:7334/nginx/nginx:3.9.11", expected: "https://2001:0db8:85a3:0000:0000:8a2e:0370:7334"}, {in: "https://2001:0db8:85a3:0000:0000:8a2e:0370:7334/nginx/nginx:3.9.11", expected: "https://2001:0db8:85a3:0000:0000:8a2e:0370:7334"}, {in: "http://[2001:db8:1f70::999:de8:7648:6e8]:1000/nginx/rad:76.9", expected: "http://[2001:db8:1f70::999:de8:7648:6e8]:1000"}, {in: "http://127.0.0.1/ops/scrap:latest", expected: "http://127.0.0.1"}, {in: "127.0.0.1:4000/ops/scrap:latest", expected: "127.0.0.1:4000"}, {in: "https://127.0.0.1:4000/ops/scrap:latest", expected: "https://127.0.0.1:4000"}, {in: "http://localhost/ops/scrap:latest", expected: "http://localhost"}, {in: "slim/docker-slim:latest", expected: "https://index.docker.io"}, //{in: "local-registry/ops/scrap:latest", expected: "local-registry"}, //{in: "local-registry:9000/ops/scrap:latest", expected: "local-registry:9000"}, } for _, test := range tt { registry := extractRegistry(test.in) if !equal(registry, test.expected) { t.Errorf("got %s expected %s", registry, test.expected) } } } func equal(res, expected interface{}) bool { return reflect.DeepEqual(res, expected) } ================================================ FILE: pkg/app/master/inspectors/image/image_inspector.go ================================================ package image import ( "bytes" "fmt" "path/filepath" "regexp" "strings" docker "github.com/fsouza/go-dockerclient" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/consts" "github.com/slimtoolkit/slim/pkg/docker/dockerfile/reverse" "github.com/slimtoolkit/slim/pkg/docker/dockerutil" "github.com/slimtoolkit/slim/pkg/util/errutil" ) const ( slimImageRepo = "slim" appArmorProfileName = "apparmor-profile" seccompProfileName = "seccomp-profile" appArmorProfileNamePat = "%s-apparmor-profile" seccompProfileNamePat = "%s-seccomp.json" https = "https://" http = "http://" ) // Inspector is a container image inspector type Inspector struct { ImageRef string ArtifactLocation string SlimImageRepo string AppArmorProfileName string SeccompProfileName string ImageInfo *docker.Image ImageRecordInfo docker.APIImages APIClient *docker.Client //fatImageDockerInstructions []string DockerfileInfo *reverse.Dockerfile } // NewInspector creates a new container image inspector func NewInspector(client *docker.Client, imageRef string /*, artifactLocation string*/) (*Inspector, error) { inspector := &Inspector{ ImageRef: imageRef, SlimImageRepo: slimImageRepo, AppArmorProfileName: appArmorProfileName, SeccompProfileName: seccompProfileName, //ArtifactLocation: artifactLocation, APIClient: client, } return inspector, nil } // NoImage returns true if the target image doesn't exist func (i *Inspector) NoImage() (bool, error) { //first, do a simple exact match lookup ii, err := dockerutil.HasImage(i.APIClient, i.ImageRef) if err == nil { log.Tracef("image.inspector.NoImage: ImageRef=%v ImageIdentity=%#v", i.ImageRef, ii) return false, nil } if err != dockerutil.ErrNotFound { log.Errorf("image.inspector.NoImage: err=%v", err) return true, err } //second, try to find something close enough //handle the case where there's no tag in the target image reference //and there are no default 'latest' tag //this will return/save the first available tag if err == dockerutil.ErrNotFound && !strings.Contains(i.ImageRef, ":") { //check if there are any tags for the target image matches, err := dockerutil.ListImages(i.APIClient, i.ImageRef) if err != nil { log.Errorf("image.inspector.NoImage: err=%v", err) return true, err } for ref, props := range matches { log.Debugf("image.inspector.NoImage: match.ref=%s match.props=%#v", ref, props) i.ImageRef = ref return false, nil } } return true, nil } // Pull tries to download the target image func (i *Inspector) Pull(showPullLog bool, dockerConfigPath, registryAccount, registrySecret string) error { var pullLog bytes.Buffer var repo string var tag string if strings.Contains(i.ImageRef, ":") { parts := strings.SplitN(i.ImageRef, ":", 2) repo = parts[0] tag = parts[1] } else { repo = i.ImageRef tag = "latest" } input := docker.PullImageOptions{ Repository: repo, Tag: tag, } if showPullLog { input.OutputStream = &pullLog } var err error var authConfig *docker.AuthConfiguration registry := extractRegistry(repo) authConfig, err = getRegistryCredential(registryAccount, registrySecret, dockerConfigPath, registry) if err != nil { log.Warnf("image.inspector.Pull: failed to get registry credential for registry=%s with err=%v", registry, err) //warn, attempt pull anyway, needs to work for public registries } if authConfig == nil { authConfig = &docker.AuthConfiguration{} } err = i.APIClient.PullImage(input, *authConfig) if err != nil { log.Debugf("image.inspector.Pull: client.PullImage err=%v", err) return err } if showPullLog { fmt.Printf("pull logs ====================\n") fmt.Println(pullLog.String()) fmt.Printf("end of pull logs =============\n") } return nil } func getRegistryCredential(registryAccount, registrySecret, dockerConfigPath, registry string) (cred *docker.AuthConfiguration, err error) { if registryAccount != "" && registrySecret != "" { cred = &docker.AuthConfiguration{ Username: registryAccount, Password: registrySecret, } return } missingAuthConfigErr := fmt.Errorf("could not find an auth config for registry - %s", registry) if dockerConfigPath != "" { dAuthConfigs, err := docker.NewAuthConfigurationsFromFile(dockerConfigPath) if err != nil { log.Warnf( "image.inspector.Pull: getDockerCredential - failed to acquire local docker config path=%s err=%s", dockerConfigPath, err.Error(), ) return nil, err } r, found := dAuthConfigs.Configs[registry] if !found { return nil, missingAuthConfigErr } cred = &r return cred, nil } cred, err = docker.NewAuthConfigurationsFromCredsHelpers(registry) if err != nil { log.Warnf( "image.inspector.Pull: failed to acquire local docker credential helpers for %s err=%s", registry, err.Error(), ) return nil, err } // could not find a credentials' helper, check auth configs if cred == nil { dConfigs, err := docker.NewAuthConfigurationsFromDockerCfg() if err != nil { log.Debugf("image.inspector.Pull: getDockerCredential err extracting docker auth configs - %s", err.Error()) return nil, err } r, found := dConfigs.Configs[registry] if !found { return nil, missingAuthConfigErr } cred = &r } log.Debugf("loaded registry auth config %+v", cred) return cred, nil } func extractRegistry(repo string) string { var scheme string if strings.Contains(repo, https) { scheme = https repo = strings.TrimPrefix(repo, https) } if strings.Contains(repo, http) { scheme = http repo = strings.TrimPrefix(repo, http) } registry := strings.Split(repo, "/")[0] domain := `((?:[a-z\d](?:[a-z\d-]{0,63}[a-z\d])?|\*)\.)+[a-z\d][a-z\d-]{0,63}[a-z\d]` ipv6 := `^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$` ipv4 := `^(((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4})` ipv4Port := `([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\:?([0-9]{1,5})?` ipv6Port := `(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))` if registry == "localhost" || strings.Contains(registry, "localhost:") { return scheme + registry } validDomain := regexp.MustCompile(domain) validIpv4 := regexp.MustCompile(ipv4) validIpv6 := regexp.MustCompile(ipv6) validIpv4WithPort := regexp.MustCompile(ipv4Port) validIpv6WithPort := regexp.MustCompile(ipv6Port) if validIpv6WithPort.MatchString(registry) { return scheme + registry } if validIpv4WithPort.MatchString(registry) { return scheme + registry } if validIpv6.MatchString(registry) { return scheme + registry } if validIpv4.MatchString(registry) { return scheme + registry } if !validDomain.MatchString(registry) { return https + "index.docker.io" } return scheme + registry } // Inspect starts the target image inspection func (i *Inspector) Inspect() error { var err error i.ImageInfo, err = i.APIClient.InspectImage(i.ImageRef) if err != nil { if err == docker.ErrNoSuchImage { log.Info("could not find target image") } return err } log.Tracef("image.Inspector.Inspect: ImageInfo=%#v", i.ImageInfo) imageList, err := i.APIClient.ListImages(docker.ListImagesOptions{All: true}) if err != nil { return err } log.Tracef("image.Inspector.Inspect: imageList.size=%v", len(imageList)) for _, r := range imageList { log.Tracef("image.Inspector.Inspect: target=%v record=%#v", i.ImageInfo.ID, r) if r.ID == i.ImageInfo.ID { i.ImageRecordInfo = r break } } if i.ImageRecordInfo.ID == "" { log.Info("could not find target image in the image list") return docker.ErrNoSuchImage } return nil } func (i *Inspector) processImageName() { if len(i.ImageRecordInfo.RepoTags) > 0 { //try to find the repo/tag that matches the image ref (if it's not an image ID) //then pick the first available repo/tag if we can't imageName := i.ImageRecordInfo.RepoTags[0] for _, current := range i.ImageRecordInfo.RepoTags { if strings.HasPrefix(current, i.ImageRef) { imageName = current break } } if rtInfo := strings.Split(imageName, ":"); len(rtInfo) > 1 { if rtInfo[0] == "" { rtInfo[0] = strings.TrimLeft(i.ImageRecordInfo.ID, "sha256:")[0:8] } i.SlimImageRepo = fmt.Sprintf("%s.slim", rtInfo[0]) if nameParts := strings.Split(rtInfo[0], "/"); len(nameParts) > 1 { i.AppArmorProfileName = strings.Join(nameParts, "-") i.SeccompProfileName = strings.Join(nameParts, "-") } else { i.AppArmorProfileName = rtInfo[0] i.SeccompProfileName = rtInfo[0] } i.AppArmorProfileName = fmt.Sprintf(appArmorProfileNamePat, i.AppArmorProfileName) i.SeccompProfileName = fmt.Sprintf(seccompProfileNamePat, i.SeccompProfileName) } } } // ProcessCollectedData performs post-processing on the collected image data func (i *Inspector) ProcessCollectedData() error { i.processImageName() var err error i.DockerfileInfo, err = reverse.DockerfileFromHistory(i.APIClient, i.ImageRef) if err != nil { return err } fatImageDockerfileLocation := filepath.Join(i.ArtifactLocation, consts.ReversedDockerfile) err = reverse.SaveDockerfileData(fatImageDockerfileLocation, i.DockerfileInfo.Lines) errutil.FailOn(err) //save the reversed Dockerfile with the old name too (tmp compat) fatImageDockerfileLocationOld := filepath.Join(i.ArtifactLocation, consts.ReversedDockerfileOldName) err = reverse.SaveDockerfileData(fatImageDockerfileLocationOld, i.DockerfileInfo.Lines) errutil.WarnOn(err) return nil } // ShowFatImageDockerInstructions prints the original target image Dockerfile instructions func (i *Inspector) ShowFatImageDockerInstructions() { if i.DockerfileInfo != nil && i.DockerfileInfo.Lines != nil { fmt.Println("slim: Fat image - Dockerfile instructures: start ====") fmt.Println(strings.Join(i.DockerfileInfo.Lines, "\n")) fmt.Println("slim: Fat image - Dockerfile instructures: end ======") } } ================================================ FILE: pkg/app/master/inspectors/ipc/ipc.go ================================================ package ipc import ( "encoding/json" "fmt" "os" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/ipc/channel" "github.com/slimtoolkit/slim/pkg/ipc/command" "github.com/slimtoolkit/slim/pkg/ipc/event" ) const ( connectTimeout = 15 readTimeout = 30 writeTimeout = 30 ) type Client struct { connectWait int target string cmdPort string evtPort string evtChannel *channel.EventClient cmdChannel *channel.CommandClient } func NewClient(target, cmdChannelPort, evtChannelPort string, connectWait int) (*Client, error) { log.Debugf("ipc.NewClient(%s,%s,%s)", target, cmdChannelPort, evtChannelPort) client := Client{ target: target, cmdPort: cmdChannelPort, evtPort: evtChannelPort, connectWait: connectWait, } if client.cmdPort == "" { client.cmdPort = fmt.Sprintf("%d", channel.CmdPort) } if client.evtPort == "" { client.evtPort = fmt.Sprintf("%d", channel.EvtPort) } if err := client.initChannels(); err != nil { log.Errorf("ipc.NewClient init error = %v", err) return nil, err } return &client, nil } func (c *Client) initChannels() error { cmdChannelAddr := fmt.Sprintf("%s:%s", c.target, c.cmdPort) cmdChannel, err := channel.NewCommandClient(cmdChannelAddr, c.connectWait, connectTimeout, readTimeout, writeTimeout) if os.IsTimeout(err) { log.Debug("ipc.initChannels(): connect timeout...") return err } else if err != nil { return err } c.cmdChannel = cmdChannel evtChannelAddr := fmt.Sprintf("%s:%s", c.target, c.evtPort) evtChannel, err := channel.NewEventClient(evtChannelAddr, c.connectWait, connectTimeout, -1) if os.IsTimeout(err) { log.Debug("ipc.initChannels(): connect timeout...") return err } else if err != nil { return err } c.evtChannel = evtChannel return nil } func (c *Client) shutdownChannels() error { if c.cmdChannel != nil { c.cmdChannel.Close() c.cmdChannel = nil } if c.evtChannel != nil { c.evtChannel.Close() c.evtChannel = nil } return nil } func (c *Client) Stop() error { return c.shutdownChannels() } func (c *Client) SendCommand(cmd command.Message) (*command.Response, error) { reqData, err := command.Encode(cmd) if err != nil { log.Error("ipc.Client.SendCommand(): malformed cmd - ", err) return nil, err } log.Debugf("ipc.Client.SendCommand() cmd channel call data='%s'\n", string(reqData)) respData, err := c.cmdChannel.Call(reqData, 3) if err != nil { log.Errorf("ipc.Client.SendCommand() cmd channel call error=%v\n", err) return nil, err } log.Debugf("ipc.Client.SendCommand() cmd channel call response='%s'\n", string(respData)) if len(respData) == 0 { log.Info("ipc.Client.SendCommand() no cmd channel call response (closed connection)") return nil, nil } var resp command.Response err = json.Unmarshal(respData, &resp) if err != nil { log.Error("ipc.Client.SendCommand(): malformed cmd response - ", err) return nil, err } return &resp, nil } func (c *Client) GetEvent() (*event.Message, error) { raw, id, err := c.evtChannel.Next(3) if err != nil { log.Errorf("ipc.Client.GetEvent(): event channel error = %v\n", err) return nil, err } log.Debugf("ipc.Client.GetEvent(): channel.Recv() - done [tid=%s evt=%v]\n", id, string(raw)) if len(raw) == 0 { log.Info("ipc.Client.GetEvent() no event channel data (closed connection)") return nil, nil } var evt event.Message if err := json.Unmarshal(raw, &evt); err != nil { log.Errorf("ipc.Client.GetEvent(): malformed event = %v\n", err) return nil, err } return &evt, nil } ================================================ FILE: pkg/app/master/inspectors/pod/pod_inspector.go ================================================ package pod import ( "context" "errors" "fmt" "os" "path/filepath" "strconv" "strings" "sync" "time" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/config" "github.com/slimtoolkit/slim/pkg/app/master/inspectors/image" "github.com/slimtoolkit/slim/pkg/app/master/inspectors/ipc" "github.com/slimtoolkit/slim/pkg/app/master/inspectors/sensor" "github.com/slimtoolkit/slim/pkg/app/master/kubernetes" "github.com/slimtoolkit/slim/pkg/app/master/security/apparmor" "github.com/slimtoolkit/slim/pkg/app/master/security/seccomp" "github.com/slimtoolkit/slim/pkg/ipc/channel" "github.com/slimtoolkit/slim/pkg/ipc/command" "github.com/slimtoolkit/slim/pkg/ipc/event" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/util/errutil" "github.com/slimtoolkit/slim/pkg/util/fsutil" v "github.com/slimtoolkit/slim/pkg/version" dockerapi "github.com/fsouza/go-dockerclient" log "github.com/sirupsen/logrus" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" ) type ovars = app.OutVars // TODO: unify with similar constants in container_inspector.go const ( sensorVolumeName = "slim-sensor" sensorVolumeMountPath = "/opt/_slim/bin" sensorBinFileAbs = sensorVolumeMountPath + "/" + sensor.LocalBinFile sensorLoaderContainer = "slim-sensor-loader" artifactsVolumeName = "slim-artifacts" targetPodLabelName = "dockersl.im/target-pod" targetPodLabelPat = "slimk_%v_%v" ) type portInfo struct { // Ports found to be available for probing. availablePorts map[dockerapi.Port]dockerapi.PortBinding podPortsInfo []string podPortList []string // Needed only to populate the struct during the RunPod() call. mux sync.Mutex } type Inspector struct { ctx context.Context ctxCancelFn context.CancelFunc xc *app.ExecutionContext logger *log.Entry kubectl kubernetes.Kubectl kubeClient *kubernetes.Client workload *kubernetes.Workload imageInspector *image.Inspector fatContainerCmd []string keepPerms bool pathPerms map[string]*fsutil.AccessInfo // ExcludePatterns map[string]*fsutil.AccessInfo // PreservePaths map[string]*fsutil.AccessInfo // IncludePaths map[string]*fsutil.AccessInfo // IncludeBins map[string]*fsutil.AccessInfo // IncludeExes map[string]*fsutil.AccessInfo // DoIncludeShell bool // DoIncludeCertAll bool // DoIncludeCertBundles bool // DoIncludeCertDirs bool // DoIncludeCertPKAll bool // DoIncludeCertPKDirs bool // DoIncludeNew bool doDebug bool logLevel string logFormat string rtaSourcePT bool sensorIPCEndpoint string statePath string portBindings map[dockerapi.Port][]dockerapi.PortBinding doPublishExposedPorts bool portInfo portInfo pod *corev1.Pod sensorIPCClient *ipc.Client } func NewInspector( ctx context.Context, xc *app.ExecutionContext, logger *log.Entry, workload *kubernetes.Workload, kubectl kubernetes.Kubectl, kubeClient *kubernetes.Client, imageInspector *image.Inspector, keepPerms bool, pathPerms map[string]*fsutil.AccessInfo, // TODO: pass these params // ExcludePatterns map[string]*fsutil.AccessInfo // PreservePaths map[string]*fsutil.AccessInfo // IncludePaths map[string]*fsutil.AccessInfo // IncludeBins map[string]*fsutil.AccessInfo // IncludeExes map[string]*fsutil.AccessInfo // DoIncludeShell bool // DoIncludeCertAll bool // DoIncludeCertBundles bool // DoIncludeCertDirs bool // DoIncludeCertPKAll bool // DoIncludeCertPKDirs bool // DoIncludeNew bool doDebug bool, logLevel string, logFormat string, rtaSourcePT bool, statePath string, contOverrides *config.ContainerOverrides, sensorIPCEndpoint string, portBindings map[dockerapi.Port][]dockerapi.PortBinding, doPublishExposedPorts bool, ) (*Inspector, error) { ctx, cancelFn := context.WithCancel(ctx) return &Inspector{ ctx: ctx, ctxCancelFn: cancelFn, xc: xc, logger: logger, workload: workload, kubectl: kubectl, kubeClient: kubeClient, imageInspector: imageInspector, fatContainerCmd: fatContainerCmd(workload, imageInspector, contOverrides), keepPerms: keepPerms, pathPerms: pathPerms, doDebug: doDebug, logLevel: logLevel, logFormat: logFormat, rtaSourcePT: rtaSourcePT, statePath: statePath, sensorIPCEndpoint: sensorIPCEndpoint, portBindings: portBindings, doPublishExposedPorts: doPublishExposedPorts, portInfo: portInfo{ availablePorts: map[dockerapi.Port]dockerapi.PortBinding{}, }, }, nil } func (i *Inspector) TargetHost() string { // Since at the moment we rely only on `kubectl port-forward` return "127.0.0.1" } func (i *Inspector) RunPod() error { if err := i.prepareWorkload(); err != nil { return err } if err := i.applyWorkload(); err != nil { return err } if err := waitForContainer(i.ctx, i.kubeClient, i.pod.Namespace, i.pod.Name, sensorLoaderContainer, true); err != nil { return err } localSensorPath := sensor.EnsureLocalBinary(i.xc, i.logger, i.statePath, true) i.logger.Debugf("RunPod: detected sensor at %q", localSensorPath) if err := i.injectSensor(localSensorPath); err != nil { return err } if err := waitForContainer(i.ctx, i.kubeClient, i.pod.Namespace, i.pod.Name, i.workload.TargetContainer().Name, false); err != nil { return err } if err := i.sensorConnect(); err != nil { return err } if err := i.sensorCommandStart(); err != nil { return err } return i.publishPorts() } func (i *Inspector) PodName() string { return i.pod.Namespace + "/" + i.pod.Name } func (i *Inspector) PodPortsInfo() string { return strings.Join(i.portInfo.podPortsInfo, ",") } func (i *Inspector) PodPortList() string { return strings.Join(i.portInfo.podPortList, ",") } func (i *Inspector) AvailablePorts() map[dockerapi.Port]dockerapi.PortBinding { return i.portInfo.availablePorts } func (i *Inspector) FinishMonitoring() { if i.sensorIPCClient == nil { return } errutil.WarnOn(i.sensorCommandStop()) out, err := i.kubectl.CpFrom( i.ctx, i.pod.Namespace, i.pod.Name, i.workload.TargetContainer().Name, filepath.Join(app.DefaultArtifactsDirPath, report.DefaultContainerReportFileName), filepath.Join(i.imageInspector.ArtifactLocation, report.DefaultContainerReportFileName), ) if err != nil { errutil.WarnOn(err) i.logger.Debugf("RunPod: kubectl cp pod:artifacts:creport -> localArtifactLocation failed with %q: %s", err, string(out)) } out, err = i.kubectl.CpFrom( i.ctx, i.pod.Namespace, i.pod.Name, i.workload.TargetContainer().Name, filepath.Join(app.DefaultArtifactsDirPath, app.ArtifactFilesDirName), filepath.Join(i.imageInspector.ArtifactLocation, app.ArtifactFilesDirName+"/"), ) if err != nil { errutil.WarnOn(err) i.logger.Debugf("RunPod: kubectl cp pod:artifacts:files -> localArtifactLocation failed with %q: %s", err, string(out)) } } func (i *Inspector) ShowPodLogs() { // TODO: Implement me! fmt.Println("slim: pod stdout:") fmt.Println("slim: pod stderr:") fmt.Println("slim: end of pod logs =============") } func (i *Inspector) ShutdownPod(resetChanges bool) { if i.sensorIPCClient == nil { return } resp, err := i.sensorIPCClient.SendCommand(&command.ShutdownSensor{}) if err != nil { i.logger.Debugf("error sending 'shutdown' => '%v'", err) } i.logger.Debugf("'shutdown' sensor response => '%v'", resp) if resetChanges { i.workload.ResetChanges() if err := i.kubeClient.CreateOrUpdate(i.ctx, i.workload.Info()); err != nil { i.logger.Debugf("error resetting workload changes => '%v'", err) } } else if i.workload.SetReplicasIfApplicable(0) { if err := i.kubeClient.CreateOrUpdate(i.ctx, i.workload.Info()); err != nil { i.logger.Debugf("error scaling down the workload => '%v'", err) } } i.sensorDisconnect() i.ctxCancelFn() } func (i *Inspector) HasCollectedData() bool { return fsutil.Exists(filepath.Join(i.imageInspector.ArtifactLocation, report.DefaultContainerReportFileName)) } func (i *Inspector) ProcessCollectedData() error { i.logger.Info("generating AppArmor profile...") err := apparmor.GenProfile(i.imageInspector.ArtifactLocation, i.imageInspector.AppArmorProfileName) if err != nil { return err } return seccomp.GenProfile(i.imageInspector.ArtifactLocation, i.imageInspector.SeccompProfileName) } func (i *Inspector) Exec(cmd string, args ...string) ([]byte, error) { return i.kubectl.Exec( i.ctx, i.pod.Namespace, i.pod.Name, i.workload.TargetContainer().Name, cmd, args..., ) } func (i *Inspector) prepareWorkload() error { i.workload.Template().Labels[targetPodLabelName] = fmt.Sprintf( targetPodLabelPat, os.Getpid(), time.Now().UTC().Format("20060102150405")) i.workload.AddEmptyDirVolume(sensorVolumeName) i.workload.AddEmptyDirVolume(artifactsVolumeName) i.workload.AddInitContainer(corev1.Container{ Name: sensorLoaderContainer, Image: "alpine", Command: []string{ "sh", "-c", fmt.Sprintf( `until [ -f %s ]; do echo "Waiting for sensor to appear..."; sleep 1; done; echo "Sensor found! Exiting..."`, sensorBinFileAbs, ), }, VolumeMounts: []corev1.VolumeMount{ {Name: sensorVolumeName, MountPath: sensorVolumeMountPath}, }, }) i.workload.SetReplicasIfApplicable(1) targetCont := i.workload.TargetContainer() targetCont.VolumeMounts = append( targetCont.VolumeMounts, corev1.VolumeMount{ Name: sensorVolumeName, MountPath: sensorVolumeMountPath, }, corev1.VolumeMount{ Name: artifactsVolumeName, MountPath: app.DefaultArtifactsDirPath, }, ) targetCont.Command = []string{sensorBinFileAbs} if targetCont.SecurityContext == nil { targetCont.SecurityContext = &corev1.SecurityContext{} } targetCont.SecurityContext.Privileged = boolPtr(true) // TODO: check if it's already there if targetCont.SecurityContext.Capabilities == nil { targetCont.SecurityContext.Capabilities = &corev1.Capabilities{} } targetCont.SecurityContext.Capabilities.Add = append( targetCont.SecurityContext.Capabilities.Add, "SYS_ADMIN", ) return nil } func (i *Inspector) applyWorkload() error { if err := i.kubeClient.CreateOrUpdate(i.ctx, i.workload.Info()); err != nil { return err } i.logger.Debugf("RunPod: workload (re)applied. Waiting for pod to start up...") pod, err := findPod(i.ctx, i.kubeClient, i.workload.Namespace(), i.workload.Template().Labels[targetPodLabelName]) if err != nil { return err } i.logger.Debugf("RunPod: found workload pod '%s/%s'. Waiting for sensor-loader container to start up...", pod.Namespace, pod.Name) i.xc.Out.Info("pod", ovars{ "status": "created", "namespace": pod.Namespace, "name": pod.Name, }) i.pod = &pod return nil } func (i *Inspector) publishPorts() error { toPublish := map[dockerapi.Port][]dockerapi.PortBinding{} if len(i.portBindings) > 0 { for contPort, hostPorts := range i.portBindings { if contPort.Port() == toStringPort(channel.CmdPort) { i.exitIPCPortConflict(hostPorts, "cmd", -126) } if contPort.Port() == toStringPort(channel.EvtPort) { i.exitIPCPortConflict(hostPorts, "evt", -127) } toPublish[contPort] = hostPorts } } else { for _, portInfo := range i.workload.TargetContainer().Ports { if portInfo.Protocol != "" && portInfo.Protocol != corev1.ProtocolTCP { continue } port := toDockerPort(portInfo.ContainerPort) toPublish[port] = []dockerapi.PortBinding{} if i.doPublishExposedPorts { toPublish[port] = append(toPublish[port], dockerapi.PortBinding{ HostPort: string(port), // same port number HostIP: i.TargetHost(), }) } } } var wg sync.WaitGroup for cp, hps := range toPublish { wg.Add(1) go func(contPort dockerapi.Port, hostPorts []dockerapi.PortBinding) { hostIP := "127.0.0.1" hostPort := "" if len(hostPorts) > 0 { hostIP = hostPorts[0].HostIP hostPort = hostPorts[0].HostPort } cmd, hostPort, err := i.kubectl.PortForward( i.ctx, i.pod.Namespace, i.pod.Name, hostIP, hostPort, contPort.Port(), ) if err == nil { i.portInfo.mux.Lock() i.portInfo.availablePorts[contPort] = dockerapi.PortBinding{HostIP: i.TargetHost(), HostPort: hostPort} i.portInfo.podPortsInfo = append( i.portInfo.podPortsInfo, fmt.Sprintf("%v => %v:%v", contPort, hostIP, hostPort), ) i.portInfo.podPortList = append(i.portInfo.podPortList, hostPort) i.portInfo.mux.Unlock() } wg.Done() if err == nil { err = cmd.Wait() } if err != nil { i.logger.Warnf("RunPod: kubectl port-forward container port failed. err=%q", err) } }(cp, hps) } wg.Wait() return nil } func (i *Inspector) exitIPCPortConflict(port []dockerapi.PortBinding, typ string, code int) { i.logger.Errorf("RunPod: port bindings comms port conflict (%s) = %#v", typ, port) i.xc.Out.Info("sensor.error", ovars{ "message": "port binding ipc port conflict", "type": typ, }) i.xc.Out.State("exited", ovars{ "exit.code": code, "component": "pod.inspector", "version": v.Current(), }) i.xc.Exit(code) } func (i *Inspector) injectSensor(localSensorPath string) error { // Trying to inject the sensor binary atomically using a "cp then mv" trick. i.logger.Debugf("RunPod: sending sensor to pod") out, err := i.kubectl.CpTo( i.ctx, i.pod.Namespace, i.pod.Name, sensorLoaderContainer, localSensorPath, sensorBinFileAbs+".uploading") if err != nil { i.logger.Debugf("RunPod: kubectl cp sensor.uploading -> pod failed with %q: %s", err, string(out)) return err } out, err = i.kubectl.Exec( i.ctx, i.pod.Namespace, i.pod.Name, sensorLoaderContainer, "mv", sensorBinFileAbs+".uploading", sensorBinFileAbs) if err != nil { i.logger.Debugf("RunPod: kubectl exec sensor.uploading -> sensor failed with %q: %s", err, string(out)) return err } return nil } func (i *Inspector) sensorConnect() error { sensorListenIP := "127.0.0.1" for _, p := range []int32{channel.CmdPort, channel.EvtPort} { go func(port int32) { cmd, _, err := i.kubectl.PortForward( i.ctx, i.pod.Namespace, i.pod.Name, sensorListenIP, toStringPort(port), toStringPort(port), ) if err == nil { err = cmd.Wait() } if err != nil { // TODO: Make it fatal! i.logger.Debugf("RunPod: kubectl port-forward sensor port %d failed. err=%q", port, err) } }(p) } ipcClient, err := ipc.NewClient( sensorListenIP, strconv.Itoa(channel.CmdPort), strconv.Itoa(channel.EvtPort), sensor.DefaultConnectWait) if err != nil { return err } i.sensorIPCClient = ipcClient return nil } func (i *Inspector) sensorCommandStart() error { cmd := &command.StartMonitor{ RTASourcePT: i.rtaSourcePT, AppName: i.fatContainerCmd[0], KeepPerms: i.keepPerms, } if len(i.fatContainerCmd) > 1 { cmd.AppArgs = i.fatContainerCmd[1:] } // if len(i.ExcludePatterns) > 0 { // cmd.Excludes = pathMapKeys(i.ExcludePatterns) // } // if len(i.PreservePaths) > 0 { // cmd.Preserves = i.PreservePaths // } // if len(i.IncludePaths) > 0 { // cmd.Includes = i.IncludePaths // } if len(i.pathPerms) > 0 { cmd.Perms = i.pathPerms } // if len(i.IncludeBins) > 0 { // cmd.IncludeBins = pathMapKeys(i.IncludeBins) // } // if len(i.IncludeExes) > 0 { // cmd.IncludeExes = pathMapKeys(i.IncludeExes) // } // cmd.IncludeShell = i.DoIncludeShell // cmd.IncludeCertAll = i.DoIncludeCertAll // cmd.IncludeCertBundles = i.DoIncludeCertBundles // cmd.IncludeCertDirs = i.DoIncludeCertDirs // cmd.IncludeCertPKAll = i.DoIncludeCertPKAll // cmd.IncludeCertPKDirs = i.DoIncludeCertPKDirs // cmd.IncludeNew = i.DoIncludeNew // if runAsUser != "" { // cmd.AppUser = runAsUser // if strings.ToLower(runAsUser) != "root" { // cmd.RunTargetAsUser = i.RunTargetAsUser // } // } // cmd.IncludeAppNextDir = i.appNodejsInspectOpts.NextOpts.IncludeAppDir // cmd.IncludeAppNextBuildDir = i.appNodejsInspectOpts.NextOpts.IncludeBuildDir // cmd.IncludeAppNextDistDir = i.appNodejsInspectOpts.NextOpts.IncludeDistDir // cmd.IncludeAppNextStaticDir = i.appNodejsInspectOpts.NextOpts.IncludeStaticDir // cmd.IncludeAppNextNodeModulesDir = i.appNodejsInspectOpts.NextOpts.IncludeNodeModulesDir // cmd.IncludeAppNuxtDir = i.appNodejsInspectOpts.NuxtOpts.IncludeAppDir // cmd.IncludeAppNuxtBuildDir = i.appNodejsInspectOpts.NuxtOpts.IncludeBuildDir // cmd.IncludeAppNuxtDistDir = i.appNodejsInspectOpts.NuxtOpts.IncludeDistDir // cmd.IncludeAppNuxtStaticDir = i.appNodejsInspectOpts.NuxtOpts.IncludeStaticDir // cmd.IncludeAppNuxtNodeModulesDir = i.appNodejsInspectOpts.NuxtOpts.IncludeNodeModulesDir // cmd.IncludeNodePackages = i.appNodejsInspectOpts.IncludePackages if _, err := i.sensorIPCClient.SendCommand(cmd); err != nil { return err } i.xc.Out.Info("cmd.startmonitor", ovars{"status": "sent"}) for idx := 0; idx < 3; idx++ { evt, err := i.sensorIPCClient.GetEvent() if err != nil { if os.IsTimeout(err) || err == channel.ErrWaitTimeout { i.xc.Out.Info("event.startmonitor.done", ovars{ "status": "receive.timeout", }) i.logger.Debug("timeout waiting for the slim container to start...") continue } return err } if evt == nil || evt.Name == "" { i.logger.Debug("empty event waiting for the slim container to start (trying again)...") continue } if evt.Name == event.StartMonitorDone { i.xc.Out.Info("event.startmonitor.done", ovars{ "status": "received", }) return nil } if evt.Name == event.Error { return fmt.Errorf("start monitor error: %v", evt.Data) } if evt.Name != event.StartMonitorDone { i.xc.Out.Info("event.startmonitor.done", ovars{ "status": "received.unexpected", "data": fmt.Sprintf("%+v", evt), }) //TODO: dump temp container logs return event.ErrUnexpectedEvent } } return errors.New("start monitor timeout") } func (i *Inspector) sensorCommandStop() error { resp, err := i.sensorIPCClient.SendCommand(&command.StopMonitor{}) if err != nil { return err } i.logger.Debugf("'stop' monitor response => '%v'", resp) i.logger.Info("waiting for the pod to finish its work...") evt, err := i.sensorIPCClient.GetEvent() if err != nil { return err } i.logger.Debugf("sensor event => '%v'", evt) return nil } func (i *Inspector) sensorDisconnect() { const op = "container.Inspector.shutdownContainerChannels" if i.sensorIPCClient != nil { if err := i.sensorIPCClient.Stop(); err != nil { i.logger.WithFields(log.Fields{ "op": op, "error": err, }).Debug("shutting down channels") } i.sensorIPCClient = nil } } func findPod( ctx context.Context, client *kubernetes.Client, namespace string, targetPodLabelValue string, ) (corev1.Pod, error) { var pod corev1.Pod err := wait.PollImmediateWithContext(ctx, 1*time.Second, 5*time.Minute, func(ctx context.Context) (bool, error) { pods, err := client. Static(). CoreV1(). Pods(namespace). List(context.TODO(), metav1.ListOptions{ LabelSelector: targetPodLabelName + "=" + targetPodLabelValue, }) if err != nil { return false, err } if len(pods.Items) == 0 { return false, nil // Keep waiting } if len(pods.Items) == 1 { pod = pods.Items[0] return true, nil // Done } return false, errors.New("unexpected - more than one target pod found") }) return pod, err } func waitForContainer( ctx context.Context, client *kubernetes.Client, namespace string, podName string, contName string, isInit bool, ) error { return wait.PollImmediateWithContext(ctx, 1*time.Second, 5*time.Minute, func(ctx context.Context) (bool, error) { pod, err := client.Static().CoreV1().Pods(namespace).Get(ctx, podName, metav1.GetOptions{}) if err != nil { return false, err } statuses := pod.Status.ContainerStatuses if isInit { statuses = pod.Status.InitContainerStatuses } for _, contStatus := range statuses { if contStatus.Name == contName && contStatus.State.Running != nil { return true, nil } } return false, nil }) } func fatContainerCmd( workload *kubernetes.Workload, imageInspector *image.Inspector, overrides *config.ContainerOverrides, ) []string { entrypoint := workload.TargetContainer().Command if len(entrypoint) == 0 { entrypoint = imageInspector.ImageInfo.Config.Entrypoint } cmd := workload.TargetContainer().Args if len(cmd) == 0 { cmd = imageInspector.ImageInfo.Config.Cmd } fullCmd := append(entrypoint, cmd...) if overrides != nil { if len(overrides.Entrypoint) > 0 || overrides.ClearEntrypoint { fullCmd = overrides.Entrypoint if len(overrides.Cmd) > 0 || overrides.ClearCmd { fullCmd = append(fullCmd, overrides.Cmd...) } //note: not using Args from PodTemplateSpec if there's an override for ENTRYPOINT } else { fullCmd = entrypoint if len(overrides.Cmd) > 0 || overrides.ClearCmd { fullCmd = append(fullCmd, overrides.Cmd...) } else { fullCmd = append(fullCmd, cmd...) } } } return fullCmd } func boolPtr(v bool) *bool { return &v } func toDockerPort(p int32) dockerapi.Port { return dockerapi.Port(fmt.Sprintf("%d/tcp", p)) } func toStringPort(p int32) string { return fmt.Sprintf("%d", p) } ================================================ FILE: pkg/app/master/inspectors/sensor/sensor.go ================================================ package sensor import ( "os" "path/filepath" "runtime" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/util/fsutil" v "github.com/slimtoolkit/slim/pkg/version" log "github.com/sirupsen/logrus" ) type ovars = app.OutVars const ( LocalBinFile = "slim-sensor" DefaultConnectWait = 60 ) func EnsureLocalBinary(xc *app.ExecutionContext, logger *log.Entry, statePath string, printState bool) string { sensorPath := filepath.Join(fsutil.ExeDir(), LocalBinFile) if runtime.GOOS == "darwin" { stateSensorPath := filepath.Join(statePath, LocalBinFile) if fsutil.Exists(stateSensorPath) { sensorPath = stateSensorPath } } if !fsutil.Exists(sensorPath) { if printState { xc.Out.Info("sensor.error", ovars{ "message": "sensor binary not found", "location": sensorPath, }) xc.Out.State("exited", ovars{ "exit.code": -999, "component": "container.inspector", "version": v.Current(), "location.exe": fsutil.ExeDir(), }) } xc.Exit(-999) } if finfo, err := os.Lstat(sensorPath); err == nil { logger.Debugf("sensor.EnsureLocalBinary: sensor (%s) perms => %#o", sensorPath, finfo.Mode().Perm()) if finfo.Mode().Perm()&fsutil.FilePermUserExe == 0 { if printState { xc.Out.Info("sensor.perms", ovars{ "message": "sensor missing execute permission", "location": sensorPath, "mode": finfo.Mode().String(), "perm": finfo.Mode().Perm().String(), }) } logger.Debugf("sensor.EnsureLocalBinary: sensor (%s) missing execute permission", sensorPath) updatedMode := finfo.Mode() | fsutil.FilePermUserExe | fsutil.FilePermGroupExe | fsutil.FilePermOtherExe if err = os.Chmod(sensorPath, updatedMode); err != nil { logger.Errorf("sensor.EnsureLocalBinary: error updating sensor (%s) perms (%#o -> %#o) => %v", sensorPath, finfo.Mode().Perm(), updatedMode.Perm(), err) } } } else { logger.Errorf("sensor.EnsureLocalBinary: error getting sensor (%s) info => %#v", sensorPath, err) } return sensorPath } ================================================ FILE: pkg/app/master/kubernetes/client.go ================================================ package kubernetes import ( "context" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/resource" "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" "github.com/slimtoolkit/slim/pkg/app/master/config" ) type Client struct { dynamic dynamic.Interface static kubernetes.Interface } func NewClient(kubeOpts config.KubernetesOptions) (*Client, error) { config, err := clientcmd.BuildConfigFromFlags("", kubeOpts.Kubeconfig) if err != nil { return nil, err } client := Client{} client.dynamic, err = dynamic.NewForConfig(config) if err != nil { return nil, err } client.static, err = kubernetes.NewForConfig(config) if err != nil { return nil, err } return &client, nil } func (c *Client) Dynamic() dynamic.Interface { return c.dynamic } func (c *Client) Static() kubernetes.Interface { return c.static } func (c *Client) CreateOrUpdate(ctx context.Context, info *resource.Info) (err error) { dto := unstructured.Unstructured{} dto.Object, err = runtime.DefaultUnstructuredConverter.ToUnstructured(info.Object) if err != nil { return } // Disable the version check since we don't expect concurrent changes. dto.SetResourceVersion("") var obj *unstructured.Unstructured obj, err = c.dynamic. Resource(info.Mapping.Resource). Namespace(info.Namespace). Create(ctx, &dto, metav1.CreateOptions{}) if apierrors.IsAlreadyExists(err) { obj, err = c.dynamic. Resource(info.Mapping.Resource). Namespace(info.Namespace). Update(ctx, &dto, metav1.UpdateOptions{}) } if err != nil { return } err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, info.Object) if err == nil { info.ResourceVersion = obj.GetResourceVersion() } return } func (c *Client) Delete(ctx context.Context, info *resource.Info) error { return c.dynamic. Resource(info.Mapping.Resource). Namespace(info.Namespace). Delete( ctx, info.Name, metav1.DeleteOptions{}) } // ResourceBuilderFunc is a helper function to avoid // passing KuberneteOptions through unrelated code. type ResourceBuilderFunc func() *resource.Builder func NewResourceBuilder(kubeOpts config.KubernetesOptions) *resource.Builder { configFlags := genericclioptions.NewConfigFlags(true) configFlags.KubeConfig = &kubeOpts.Kubeconfig return resource.NewBuilder(configFlags) } func NewResourceBuilderFunc(kubeOpts config.KubernetesOptions) ResourceBuilderFunc { return func() *resource.Builder { return NewResourceBuilder(kubeOpts) } } ================================================ FILE: pkg/app/master/kubernetes/kubectl.go ================================================ package kubernetes import ( "bufio" "context" "errors" "fmt" "os/exec" "path/filepath" "strings" "github.com/slimtoolkit/slim/pkg/app/master/config" ) type Kubectl interface { CpFrom(ctx context.Context, namespace, pod, container, srcPath, dstPath string) ([]byte, error) CpTo(ctx context.Context, namespace, pod, container, srcPath, dstPath string) ([]byte, error) Exec(ctx context.Context, namespace, pod, container, cmd string, args ...string) ([]byte, error) PortForward(ctx context.Context, namespace, pod, address, hostPort, podPort string) (*exec.Cmd, string, error) } type kubectl struct { kubeconfig string } var _ Kubectl = &kubectl{} func NewKubectl(opts config.KubernetesOptions) Kubectl { return &kubectl{ kubeconfig: opts.Kubeconfig, } } func (k *kubectl) CpFrom( ctx context.Context, namespace string, pod string, container string, srcPath string, dstPath string, ) ([]byte, error) { cmd1 := []string{ "kubectl", "exec", pod, "--kubeconfig", k.kubeconfig, "--namespace", namespace, "--container", container, "--", "tar", "cf", "-", "-C", filepath.Dir(srcPath), filepath.Base(srcPath), } cmd2 := []string{"tar", "xf", "-", "-C", filepath.Dir(dstPath), filepath.Base(dstPath)} cmd := exec.CommandContext( ctx, "bash", "-c", strings.Join(cmd1, " ")+"|"+strings.Join(cmd2, " "), ) return cmd.CombinedOutput() // r, w, err := os.Pipe() // if err != nil { // return nil, err // } // c1.Stdout = w // c2.Stdin = io.TeeReader(r, os.Stdout) // var out bytes.Buffer // c2.Stdout = &out // c2.Stderr = &out // if err := c1.Start(); err != nil { // return nil, err // } // if err := c2.Start(); err != nil { // return nil, err // } // if err := c1.Wait(); err != nil { // return nil, err // } // if err := w.Close(); err != nil { // return nil, err // } // if err := c2.Wait(); err != nil { // return nil, err // } // return out.Bytes(), nil } func (k *kubectl) CpTo( ctx context.Context, namespace string, pod string, container string, srcPath string, dstPath string, ) ([]byte, error) { return exec.CommandContext( ctx, "kubectl", "cp", srcPath, pod+":"+dstPath, "--kubeconfig", k.kubeconfig, "--namespace", namespace, "--container", container, ).CombinedOutput() } func (k *kubectl) Exec( ctx context.Context, namespace string, pod string, container string, cmd string, args ...string, ) ([]byte, error) { args = append([]string{ "kubectl", "exec", pod, "--kubeconfig", k.kubeconfig, "--namespace", namespace, "--container", container, "--", cmd, }, args...) return exec.Command(args[0], args[1:]...).CombinedOutput() } func (k *kubectl) PortForward( ctx context.Context, namespace string, pod string, address string, hostPort string, podPort string, ) (*exec.Cmd, string, error) { if podPort == "" { return nil, "", errors.New("podPort cannot be empty") } mapping := ":" + podPort if hostPort != "" { mapping = hostPort + mapping } cmd := exec.CommandContext( ctx, "kubectl", "--kubeconfig", k.kubeconfig, "--namespace", namespace, "--address", address, "port-forward", "pod/"+pod, mapping, ) out, err := cmd.StdoutPipe() if err != nil { return cmd, "", err } if err := cmd.Start(); err != nil { return cmd, "", err } var actualHostPort int pattern := fmt.Sprintf("Forwarding from %s:%s -> %s", address, "%d", podPort) scanner := bufio.NewScanner(out) for scanner.Scan() { n, err := fmt.Sscanf(scanner.Text(), pattern, &actualHostPort) if err == nil && n == 1 { return cmd, fmt.Sprintf("%d", actualHostPort), nil } } return cmd, "", errors.New("cannot detect host port") } ================================================ FILE: pkg/app/master/kubernetes/manifests.go ================================================ package kubernetes import ( "context" "fmt" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/cli-runtime/pkg/resource" "k8s.io/client-go/kubernetes/scheme" "github.com/slimtoolkit/slim/pkg/app/master/config" ) // TODO: Start supporting CRDs - currently the hardcoded client-go scheme usage // will likely make the manifests containing CRDs fail. type Manifests struct { infos []*resource.Info client *Client resourceBuilderFn ResourceBuilderFunc } func ManifestsFromFiles( opts config.KubernetesOptions, client *Client, resourceBuilderFn ResourceBuilderFunc, ) (*Manifests, error) { namespace := opts.Target.Namespace if namespace == "" { namespace = namespaceDefault } infos, err := resourceBuilderFn(). WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...). NamespaceParam(namespace). DefaultNamespace(). FilenameParam( false, &resource.FilenameOptions{ Filenames: opts.Manifests, Recursive: true, }). Do(). Infos() if err != nil { return nil, err } return &Manifests{ infos: infos, client: client, resourceBuilderFn: resourceBuilderFn, }, nil } func (ms *Manifests) Find(target config.KubernetesTarget) (*resource.Info, error) { mapping, err := ms.resourceBuilderFn(). Local(). WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...). NamespaceParam("not-really-used"). ResourceTypeOrNameArgs(true, target.Workload). SingleResourceType(). Do(). ResourceMapping() if err != nil { return nil, err } name, err := target.WorkloadName() if err != nil { return nil, err } matches := []*resource.Info{} for _, info := range ms.infos { if info.Mapping.GroupVersionKind.GroupKind() != mapping.GroupVersionKind.GroupKind() { continue } if info.Name != name { continue } if target.Namespace != "" && target.Namespace != info.Namespace { continue } matches = append(matches, info) } if len(matches) == 0 { return nil, nil } if len(matches) > 1 { return nil, fmt.Errorf("found more than 1 workload '%s/%s' match in the supplied manifests", target.Namespace, name) } return matches[0], nil } func (ms *Manifests) Apply(ctx context.Context, predicate func(info *resource.Info) bool) error { for _, info := range ms.infos { if !predicate(info) { continue } if err := ms.client.CreateOrUpdate(ctx, info); err != nil { // TODO: consider partial applying return err } } return nil } func (ms *Manifests) Delete(ctx context.Context) error { for _, info := range ms.infos { if err := ms.client.Delete(ctx, info); err != nil && !apierrors.IsNotFound(err) { // TODO: consider partial deletion return err } } return nil } ================================================ FILE: pkg/app/master/kubernetes/workload.go ================================================ package kubernetes import ( "errors" appsv1 "k8s.io/api/apps/v1" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/cli-runtime/pkg/resource" "k8s.io/client-go/kubernetes/scheme" "github.com/slimtoolkit/slim/pkg/app/master/config" ) const ( namespaceDefault = "default" annotationDefaultContainer = "kubectl.kubernetes.io/default-container" ) type Workload struct { info *resource.Info orig runtime.Object targetContainerName string } func newWorkload(info *resource.Info, targetContainerName string) *Workload { return &Workload{ info: info, orig: info.Object.DeepCopyObject(), targetContainerName: targetContainerName, } } func (w *Workload) Namespace() string { return w.info.Namespace } func (w *Workload) Name() string { return w.info.Name } func (w *Workload) Info() *resource.Info { return w.info } func (w *Workload) Template() *corev1.PodTemplateSpec { switch obj := w.info.Object.(type) { case *appsv1.DaemonSet: return &obj.Spec.Template case *appsv1.Deployment: return &obj.Spec.Template case *appsv1.ReplicaSet: return &obj.Spec.Template case *appsv1.StatefulSet: return &obj.Spec.Template case *batchv1.Job: return &obj.Spec.Template case *batchv1.CronJob: return &obj.Spec.JobTemplate.Spec.Template default: // Shouldn't really happen... return nil } } func (w *Workload) Container(name string) *corev1.Container { for _, c := range w.Template().Spec.Containers { if c.Name == name { return &c } } return nil } func (w *Workload) DefaultContainer() *corev1.Container { as := w.Template().Annotations if as != nil && as[annotationDefaultContainer] != "" { return w.Container(as[annotationDefaultContainer]) } if len(w.Template().Spec.Containers) == 1 { return &w.Template().Spec.Containers[0] } // No default return nil } func (w *Workload) TargetContainer() *corev1.Container { if w.targetContainerName == "" { return w.DefaultContainer() } return w.Container(w.targetContainerName) } func (w *Workload) AddEmptyDirVolume(name string) { w.Template().Spec.Volumes = append(w.Template().Spec.Volumes, corev1.Volume{ Name: name, VolumeSource: corev1.VolumeSource{ EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }) } func (w *Workload) AddInitContainer(cont corev1.Container) { w.Template().Spec.InitContainers = append(w.Template().Spec.InitContainers, cont) } func (w *Workload) SetReplicasIfApplicable(n int32) bool { switch obj := w.info.Object.(type) { case *appsv1.Deployment: obj.Spec.Replicas = &n case *appsv1.ReplicaSet: obj.Spec.Replicas = &n case *appsv1.StatefulSet: obj.Spec.Replicas = &n default: return false } return true } func (w *Workload) ResetChanges() { w.info.Object = w.orig.DeepCopyObject() w.info.ResourceVersion = "" } // WorkloadFinder searches for the workload object in: // - supplied manifest files (if any) // - default cluster type WorkloadFinder struct { manifests *Manifests resourceBuilderFn ResourceBuilderFunc } func NewWorkloadFinder(manifests *Manifests, resourceBuilderFn ResourceBuilderFunc) *WorkloadFinder { return &WorkloadFinder{ manifests: manifests, resourceBuilderFn: resourceBuilderFn, } } func (f *WorkloadFinder) Find(target config.KubernetesTarget) (*Workload, error) { info, err := func() (*resource.Info, error) { if f.manifests != nil { return f.manifests.Find(target) } return f.findInCluster(target) }() if err != nil { return nil, err } if info == nil { return nil, nil } return newWorkload(info, target.Container), nil } func (f *WorkloadFinder) findInCluster(target config.KubernetesTarget) (*resource.Info, error) { namespace := target.Namespace if namespace == "" { namespace = namespaceDefault } infos, err := f.resourceBuilderFn(). WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...). NamespaceParam(namespace). SingleResourceType(). ResourceTypeOrNameArgs(true, target.Workload). Do(). Infos() if err != nil { return nil, err } if len(infos) == 0 { return nil, errors.New("couldn't find workload in cluster") } if len(infos) > 1 { return nil, errors.New("couldn't unambiguously identify workload in cluster") } return infos[0], nil } ================================================ FILE: pkg/app/master/probe/http/crawler.go ================================================ package http import ( "net/http" "net/http/cookiejar" "strings" "time" "github.com/gocolly/colly/v2" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app/master/config" ) const ( defaultCrawlMaxDepth = 3 defaultCrawlMaxPageCount = 1000 defaultCrawlConcurrency = 10 defaultMaxConcurrentCrawlers = 1 ) func (p *CustomProbe) crawl(proto, domain, addr string) { var httpClient *http.Client if strings.HasPrefix(proto, config.ProtoHTTP2) { var err error if httpClient, err = getHTTPClient(proto); err != nil { p.xc.Out.Error("HTTP probe - construct client error - %v", err.Error()) return } httpClient.Timeout = 10 * time.Second //matches the timeout used by Colly jar, _ := cookiejar.New(nil) httpClient.Jar = jar } if p.opts.CrawlConcurrencyMax > 0 && p.concurrentCrawlers != nil { p.concurrentCrawlers <- struct{}{} } p.workers.Add(1) go func() { defer func() { if p.opts.CrawlConcurrencyMax > 0 && p.concurrentCrawlers != nil { <-p.concurrentCrawlers } p.workers.Done() }() var pageCount int c := colly.NewCollector() c.UserAgent = "ds.crawler" c.IgnoreRobotsTxt = true c.Async = true c.AllowedDomains = []string{domain} c.AllowURLRevisit = false if httpClient != nil { c.SetClient(httpClient) } if p.opts.CrawlMaxDepth > 0 { c.MaxDepth = p.opts.CrawlMaxDepth } if p.opts.CrawlConcurrency > 0 { c.Limit(&colly.LimitRule{ DomainGlob: "*", Parallelism: p.opts.CrawlConcurrency, }) } c.OnHTML("a[href]", func(e *colly.HTMLElement) { if p.opts.CrawlMaxPageCount > 0 && pageCount > p.opts.CrawlMaxPageCount { log.Debugf("http.CustomProbe.crawl.OnHTML(a[href]) - reached max page count, ignoring link (%v)", p.opts.CrawlMaxPageCount) return } e.Request.Visit(e.Attr("href")) }) c.OnHTML("link[href]", func(e *colly.HTMLElement) { if p.opts.CrawlMaxPageCount > 0 && pageCount > p.opts.CrawlMaxPageCount { log.Debugf("http.CustomProbe.crawl.OnHTML(link[href]) - reached max page count, ignoring link (%v)", p.opts.CrawlMaxPageCount) return } switch e.Attr("rel") { case "dns-prefetch", "preconnect", "alternate": return } e.Request.Visit(e.Attr("href")) }) c.OnHTML("script[src], source[src], img[src]", func(e *colly.HTMLElement) { if p.opts.CrawlMaxPageCount > 0 && pageCount > p.opts.CrawlMaxPageCount { log.Debugf("http.CustomProbe.crawl.OnHTML(script/source/img) - reached max page count, ignoring link (%v)", p.opts.CrawlMaxPageCount) return } e.Request.Visit(e.Attr("src")) }) c.OnHTML("source[srcset]", func(e *colly.HTMLElement) { if p.opts.CrawlMaxPageCount > 0 && pageCount > p.opts.CrawlMaxPageCount { log.Debugf("http.CustomProbe.crawl.OnHTML(source[srcset]) - reached max page count, ignoring link (%v)", p.opts.CrawlMaxPageCount) return } e.Request.Visit(e.Attr("srcset")) }) c.OnHTML("[data-src]", func(e *colly.HTMLElement) { if p.opts.CrawlMaxPageCount > 0 && pageCount > p.opts.CrawlMaxPageCount { log.Debugf("http.CustomProbe.crawl.OnHTML([data-src]) - reached max page count, ignoring link (%v)", p.opts.CrawlMaxPageCount) return } e.Request.Visit(e.Attr("data-src")) }) c.OnRequest(func(r *colly.Request) { p.xc.Out.Info("http.probe.crawler", ovars{ "page": pageCount, "url": r.URL, }) if p.opts.CrawlMaxPageCount > 0 && pageCount > p.opts.CrawlMaxPageCount { p.xc.Out.Info("http.probe.crawler.stop", ovars{ "reason": "reached max visits", }) log.Debugf("http.CustomProbe.crawl.OnRequest - reached max page count (%v)", p.opts.CrawlMaxPageCount) r.Abort() return } pageCount++ }) c.OnError(func(_ *colly.Response, err error) { log.Tracef("http.CustomProbe.crawl - error=%v", err) }) c.Visit(addr) c.Wait() p.xc.Out.Info("probe.crawler.done", ovars{ "addr": addr, }) }() } ================================================ FILE: pkg/app/master/probe/http/custom_probe.go ================================================ package http import ( "context" "errors" "fmt" "io" "net/http" "net/url" "os" "strings" "sync" "time" dockerapi "github.com/fsouza/go-dockerclient" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/config" "github.com/slimtoolkit/slim/pkg/app/master/inspectors/container" "github.com/slimtoolkit/slim/pkg/app/master/inspectors/pod" ) const ( probeRetryCount = 5 defaultHTTPPortStr = "80" defaultHTTPSPortStr = "443" defaultFastCGIPortStr = "9000" ) type ovars = app.OutVars // CustomProbe is a custom HTTP probe type CustomProbe struct { xc *app.ExecutionContext opts config.HTTPProbeOptions ports []string targetHost string APISpecProbes []apiSpecInfo printState bool CallCount uint64 ErrCount uint64 OkCount uint64 doneChan chan struct{} workers sync.WaitGroup concurrentCrawlers chan struct{} } // NewEndpointProbe creates a new custom HTTP probe for an endpoint func NewEndpointProbe( xc *app.ExecutionContext, targetEndpoint string, ports []uint, opts config.HTTPProbeOptions, printState bool, ) (*CustomProbe, error) { probe := newCustomProbe(xc, targetEndpoint, opts, printState) if len(ports) == 0 { ports = []uint{80} } for _, pnum := range ports { probe.ports = append(probe.ports, fmt.Sprintf("%d", pnum)) } if len(probe.opts.APISpecFiles) > 0 { probe.loadAPISpecFiles() } return probe, nil } // NewContainerProbe creates a new custom HTTP probe func NewContainerProbe( xc *app.ExecutionContext, inspector *container.Inspector, opts config.HTTPProbeOptions, printState bool, ) (*CustomProbe, error) { probe := newCustomProbe(xc, inspector.TargetHost, opts, printState) availableHostPorts := map[string]string{} for nsPortKey, nsPortData := range inspector.AvailablePorts { log.Debugf("HTTP probe - target's network port key='%s' data='%#v'", nsPortKey, nsPortData) if nsPortKey.Proto() != "tcp" { log.Debugf("HTTP probe - skipping non-tcp port => %v", nsPortKey) continue } if nsPortData.HostPort == "" { log.Debugf("HTTP probe - skipping network setting without port data => %v", nsPortKey) continue } availableHostPorts[nsPortData.HostPort] = nsPortKey.Port() } log.Debugf("HTTP probe - available host ports => %+v", availableHostPorts) if len(probe.opts.Ports) > 0 { for _, pnum := range probe.opts.Ports { pspec := dockerapi.Port(fmt.Sprintf("%v/tcp", pnum)) if _, ok := inspector.AvailablePorts[pspec]; ok { if inspector.SensorIPCMode == container.SensorIPCModeDirect { probe.ports = append(probe.ports, fmt.Sprintf("%d", pnum)) } else { probe.ports = append(probe.ports, inspector.AvailablePorts[pspec].HostPort) } } else { log.Debugf("HTTP probe - ignoring port => %v", pspec) } } log.Debugf("HTTP probe - filtered ports => %+v", probe.ports) } else { //order the port list based on the order of the 'EXPOSE' instructions if len(inspector.ImageInspector.DockerfileInfo.ExposedPorts) > 0 { for epi := len(inspector.ImageInspector.DockerfileInfo.ExposedPorts) - 1; epi >= 0; epi-- { portInfo := inspector.ImageInspector.DockerfileInfo.ExposedPorts[epi] if !strings.Contains(portInfo, "/") { portInfo = fmt.Sprintf("%v/tcp", portInfo) } pspec := dockerapi.Port(portInfo) if _, ok := inspector.AvailablePorts[pspec]; ok { hostPort := inspector.AvailablePorts[pspec].HostPort if inspector.SensorIPCMode == container.SensorIPCModeDirect { if containerPort := availableHostPorts[hostPort]; containerPort != "" { probe.ports = append(probe.ports, containerPort) } else { log.Debugf("HTTP probe - could not find container port from host port => %v", hostPort) } } else { probe.ports = append(probe.ports, hostPort) } if _, ok := availableHostPorts[hostPort]; ok { log.Debugf("HTTP probe - delete exposed port from the available host ports => %v (%v)", hostPort, portInfo) delete(availableHostPorts, hostPort) } } else { log.Debugf("HTTP probe - Unknown exposed port - %v", portInfo) } } } for hostPort, containerPort := range availableHostPorts { if inspector.SensorIPCMode == container.SensorIPCModeDirect { probe.ports = append(probe.ports, containerPort) } else { probe.ports = append(probe.ports, hostPort) } } log.Debugf("HTTP probe - probe.Ports => %+v", probe.ports) } if len(probe.opts.APISpecFiles) > 0 { probe.loadAPISpecFiles() } return probe, nil } func NewPodProbe( xc *app.ExecutionContext, inspector *pod.Inspector, opts config.HTTPProbeOptions, printState bool, ) (*CustomProbe, error) { probe := newCustomProbe(xc, inspector.TargetHost(), opts, printState) availableHostPorts := map[string]string{} for nsPortKey, nsPortData := range inspector.AvailablePorts() { log.Debugf("HTTP probe - target's network port key='%s' data='%#v'", nsPortKey, nsPortData) availableHostPorts[nsPortData.HostPort] = nsPortKey.Port() } log.Debugf("HTTP probe - available host ports => %+v", availableHostPorts) if len(probe.opts.Ports) > 0 { for _, pnum := range probe.opts.Ports { pspec := dockerapi.Port(fmt.Sprintf("%v/tcp", pnum)) if port, ok := inspector.AvailablePorts()[pspec]; ok { probe.ports = append(probe.ports, port.HostPort) } else { log.Debugf("HTTP probe - ignoring port => %v", pspec) } } log.Debugf("HTTP probe - filtered ports => %+v", probe.ports) } else { for hostPort := range availableHostPorts { probe.ports = append(probe.ports, hostPort) } log.Debugf("HTTP probe - probe.Ports => %+v", probe.ports) } if len(probe.opts.APISpecFiles) > 0 { probe.loadAPISpecFiles() } return probe, nil } func newCustomProbe( xc *app.ExecutionContext, targetHost string, opts config.HTTPProbeOptions, printState bool, ) *CustomProbe { //note: the default probe should already be there if the user asked for it //-1 means disabled if opts.CrawlMaxDepth == 0 { opts.CrawlMaxDepth = defaultCrawlMaxDepth } //-1 means disabled if opts.CrawlMaxPageCount == 0 { opts.CrawlMaxPageCount = defaultCrawlMaxPageCount } //-1 means disabled if opts.CrawlConcurrency == 0 { opts.CrawlConcurrency = defaultCrawlConcurrency } //-1 means disabled if opts.CrawlConcurrencyMax == 0 { opts.CrawlConcurrencyMax = defaultMaxConcurrentCrawlers } probe := &CustomProbe{ xc: xc, opts: opts, printState: printState, targetHost: targetHost, doneChan: make(chan struct{}), } if opts.CrawlConcurrencyMax > 0 { probe.concurrentCrawlers = make(chan struct{}, opts.CrawlConcurrencyMax) } return probe } func (p *CustomProbe) Ports() []string { return p.ports } // Start starts the HTTP probe instance execution func (p *CustomProbe) Start() { if p.printState { p.xc.Out.State("http.probe.starting", ovars{ "message": "WAIT FOR HTTP PROBE TO FINISH", }) } go func() { //TODO: need to do a better job figuring out if the target app is ready to accept connections time.Sleep(9 * time.Second) //base start wait time if p.opts.StartWait > 0 { if p.printState { p.xc.Out.State("http.probe.start.wait", ovars{"time": p.opts.StartWait}) } //additional wait time time.Sleep(time.Duration(p.opts.StartWait) * time.Second) if p.printState { p.xc.Out.State("http.probe.start.wait.done") } } if p.printState { p.xc.Out.State("http.probe.running") } log.Info("HTTP probe started...") findIdx := func(ports []string, target string) int { for idx, val := range ports { if val == target { return idx } } return -1 } httpIdx := findIdx(p.ports, defaultHTTPPortStr) httpsIdx := findIdx(p.ports, defaultHTTPSPortStr) if httpIdx != -1 && httpsIdx != -1 && httpsIdx < httpIdx { //want to probe http first log.Debugf("http.probe - swapping http and https ports (http=%v <-> https=%v)", httpIdx, httpsIdx) p.ports[httpIdx], p.ports[httpsIdx] = p.ports[httpsIdx], p.ports[httpIdx] } if p.printState { p.xc.Out.Info("http.probe.ports", ovars{ "count": len(p.ports), "targets": strings.Join(p.ports, ","), }) var cmdListPreview []string var cmdListTail string for idx, c := range p.opts.Cmds { cmdListPreview = append(cmdListPreview, fmt.Sprintf("%s %s", c.Method, c.Resource)) if idx == 2 { cmdListTail = ",..." break } } cmdInfo := fmt.Sprintf("%s%s", strings.Join(cmdListPreview, ","), cmdListTail) p.xc.Out.Info("http.probe.commands", ovars{ "count": len(p.opts.Cmds), "commands": cmdInfo, }) } for _, port := range p.ports { //If it's ok stop after the first successful probe pass if p.OkCount > 0 && !p.opts.Full { break } for _, cmd := range p.opts.Cmds { var reqBody io.Reader var rbSeeker io.Seeker if cmd.BodyFile != "" { _, err := os.Stat(cmd.BodyFile) if err != nil { log.Errorf("http.probe - cmd.BodyFile (%s) check error: %v", cmd.BodyFile, err) } else { bodyFile, err := os.Open(cmd.BodyFile) if err != nil { log.Errorf("http.probe - cmd.BodyFile (%s) read error: %v", cmd.BodyFile, err) } else { reqBody = bodyFile rbSeeker = bodyFile //the file will be closed only when the function exits defer bodyFile.Close() } } } else { strBody := strings.NewReader(cmd.Body) reqBody = strBody rbSeeker = strBody } // TODO: need a smarter and more dynamic way to determine the actual protocol type // Set up FastCGI defaults if the default CGI port is used without a FastCGI config. if port == defaultFastCGIPortStr && cmd.FastCGI == nil { log.Debugf("HTTP probe - FastCGI default port (%s) used, setting up HTTP probe FastCGI wrapper defaults", port) // Typicall the entrypoint into a PHP app. if cmd.Resource == "/" { cmd.Resource = "/index.php" } // SplitPath is typically on the first .php path element. var splitPath []string if phpIdx := strings.Index(cmd.Resource, ".php"); phpIdx != -1 { splitPath = []string{cmd.Resource[:phpIdx+4]} } cmd.FastCGI = &config.FastCGIProbeWrapperConfig{ // /var/www is a typical root for PHP indices. Root: "/var/www", SplitPath: splitPath, } } var protocols []string if cmd.Protocol == "" { switch port { case defaultHTTPPortStr: protocols = []string{config.ProtoHTTP} case defaultHTTPSPortStr: protocols = []string{config.ProtoHTTPS} default: protocols = []string{config.ProtoHTTP, config.ProtoHTTPS} } } else { protocols = []string{cmd.Protocol} } for _, proto := range protocols { maxRetryCount := probeRetryCount if p.opts.RetryCount > 0 { maxRetryCount = p.opts.RetryCount } notReadyErrorWait := time.Duration(16) webErrorWait := time.Duration(8) otherErrorWait := time.Duration(4) if p.opts.RetryWait > 0 { webErrorWait = time.Duration(p.opts.RetryWait) notReadyErrorWait = time.Duration(p.opts.RetryWait * 2) otherErrorWait = time.Duration(p.opts.RetryWait / 2) } if IsValidWSProto(proto) { wc, err := NewWebsocketClient(proto, p.targetHost, port) if err != nil { log.Debugf("HTTP probe - new websocket error - %v", err) continue } wc.ReadCh = make(chan WebsocketMessage, 10) for i := 0; i < maxRetryCount; i++ { err = wc.Connect() if err != nil { log.Debugf("HTTP probe - ws target not ready yet (retry again later) [err=%v]...", err) time.Sleep(notReadyErrorWait * time.Second) continue } wc.CheckConnection() //TODO: prep data to write from the HTTPProbeCmd fields err = wc.WriteString("ws.data") p.CallCount++ if p.printState { statusCode := "error" callErrorStr := "none" if err == nil { statusCode = "ok" } else { callErrorStr = err.Error() } p.xc.Out.Info("http.probe.call.ws", ovars{ "status": statusCode, "stats.rc": wc.ReadCount, "stats.pic": wc.PingCount, "stats.poc": wc.PongCount, "target": wc.Addr, "attempt": i + 1, "error": callErrorStr, "time": time.Now().UTC().Format(time.RFC3339), }) } if err != nil { p.ErrCount++ log.Debugf("HTTP probe - websocket write error - %v", err) time.Sleep(notReadyErrorWait * time.Second) } else { p.OkCount++ //try to read something from the socket select { case wsMsg := <-wc.ReadCh: log.Debugf("HTTP probe - websocket read - [type=%v data=%s]", wsMsg.Type, string(wsMsg.Data)) case <-time.After(time.Second * 5): log.Debugf("HTTP probe - websocket read time out") } break } } wc.Disconnect() continue } var client *http.Client switch { case cmd.FastCGI != nil: log.Debug("HTTP probe - FastCGI embedded proxy configured") client = getFastCGIClient(cmd.FastCGI) default: var err error if client, err = getHTTPClient(proto); err != nil { p.xc.Out.Error("HTTP probe - construct client error - %v", err.Error()) continue } } baseAddr := getHTTPAddr(proto, p.targetHost, port) // TODO: cmd.Resource may need to be a part of cmd.FastCGI instead. addr := fmt.Sprintf("%s%s", baseAddr, cmd.Resource) req, err := newHTTPRequestFromCmd(cmd, addr, reqBody) if err != nil { p.xc.Out.Error("HTTP probe - construct request error - %v", err.Error()) continue } for i := 0; i < maxRetryCount; i++ { res, err := client.Do(req.Clone(context.Background())) p.CallCount++ rbSeeker.Seek(0, 0) if res != nil { if res.Body != nil { io.Copy(io.Discard, res.Body) } res.Body.Close() } statusCode := "error" callErrorStr := "none" if err == nil { statusCode = fmt.Sprintf("%v", res.StatusCode) } else { callErrorStr = err.Error() } if p.printState { p.xc.Out.Info("http.probe.call", ovars{ "status": statusCode, "method": cmd.Method, "target": addr, "attempt": i + 1, "error": callErrorStr, "time": time.Now().UTC().Format(time.RFC3339), }) } if err == nil { p.OkCount++ if p.OkCount == 1 { if len(p.opts.APISpecs) != 0 && len(p.opts.APISpecFiles) != 0 && cmd.FastCGI != nil { p.xc.Out.Info("HTTP probe - API spec probing not implemented for fastcgi") } else { p.probeAPISpecs(proto, p.targetHost, port) } } if cmd.Crawl { if cmd.FastCGI != nil { p.xc.Out.Info("HTTP probe - crawling not implemented for fastcgi") } else { p.crawl(proto, p.targetHost, addr) } } break } else { p.ErrCount++ urlErr := &url.Error{} if errors.As(err, &urlErr) { if errors.Is(urlErr.Err, io.EOF) { log.Debugf("HTTP probe - target not ready yet (retry again later)...") time.Sleep(notReadyErrorWait * time.Second) } else { log.Debugf("HTTP probe - web error... retry again later...") time.Sleep(webErrorWait * time.Second) } } else { log.Debugf("HTTP probe - other error... retry again later...") time.Sleep(otherErrorWait * time.Second) } } } } } } log.Info("HTTP probe done.") if p.printState { p.xc.Out.Info("http.probe.summary", ovars{ "total": p.CallCount, "failures": p.ErrCount, "successful": p.OkCount, }) outVars := ovars{} //warning := "" switch { case p.CallCount == 0: outVars["warning"] = "no.calls" //warning = "warning=no.calls" case p.OkCount == 0: //warning = "warning=no.successful.calls" outVars["warning"] = "no.successful.calls" } p.xc.Out.State("http.probe.done", outVars) } p.workers.Wait() close(p.doneChan) }() } func (p *CustomProbe) probeAPISpecs(proto, targetHost, port string) { //fetch the API spec when we know the target is reachable p.loadAPISpecs(proto, targetHost, port) //ideally api spec probes should work without http probe commands //for now trigger the api spec probes after the first successful http probe command //and once the api specs are loaded for _, specInfo := range p.APISpecProbes { var apiPrefix string if specInfo.prefixOverride != "" { apiPrefix = specInfo.prefixOverride } else { var err error apiPrefix, err = apiSpecPrefix(specInfo.spec) if err != nil { p.xc.Out.Error("http.probe.api-spec.error.prefix", err.Error()) continue } } p.probeAPISpecEndpoints(proto, targetHost, port, apiPrefix, specInfo.spec) } } // DoneChan returns the 'done' channel for the HTTP probe instance func (p *CustomProbe) DoneChan() <-chan struct{} { return p.doneChan } func newHTTPRequestFromCmd(cmd config.HTTPProbeCmd, addr string, reqBody io.Reader) (*http.Request, error) { req, err := http.NewRequestWithContext(context.Background(), cmd.Method, addr, reqBody) if err != nil { return nil, err } for _, hline := range cmd.Headers { hparts := strings.SplitN(hline, ":", 2) if len(hparts) != 2 { log.Debugf("ignoring malformed header (%v)", hline) continue } hname := strings.TrimSpace(hparts[0]) hvalue := strings.TrimSpace(hparts[1]) req.Header.Add(hname, hvalue) } if (cmd.Username != "") || (cmd.Password != "") { req.SetBasicAuth(cmd.Username, cmd.Password) } return req, nil } ================================================ FILE: pkg/app/master/probe/http/httpclient.go ================================================ package http import ( "crypto/tls" "fmt" "net" "net/http" "time" "golang.org/x/net/http2" "github.com/slimtoolkit/slim/pkg/app/master/config" "github.com/slimtoolkit/slim/pkg/app/master/probe/http/internal" ) func getHTTP1Client() *http.Client { client := &http.Client{ Timeout: time.Second * 30, Transport: &http.Transport{ MaxIdleConns: 10, IdleConnTimeout: 30 * time.Second, TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, }, }, } return client } func getHTTP2Client(h2c bool) *http.Client { transport := &http2.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, }, } client := &http.Client{ Timeout: time.Second * 30, Transport: transport, } if h2c { transport.AllowHTTP = true transport.DialTLS = func(network, addr string, cfg *tls.Config) (net.Conn, error) { return net.Dial(network, addr) } } return client } func getHTTPClient(proto string) (*http.Client, error) { switch proto { case config.ProtoHTTP2: return getHTTP2Client(false), nil case config.ProtoHTTP2C: return getHTTP2Client(true), nil default: return getHTTP1Client(), nil } return nil, fmt.Errorf("unsupported HTTP-family protocol %s", proto) } func getHTTPAddr(proto, targetHost, port string) string { scheme := getHTTPScheme(proto) return fmt.Sprintf("%s://%s:%s", scheme, targetHost, port) } func getHTTPScheme(proto string) string { var scheme string switch proto { case config.ProtoHTTP: scheme = proto case config.ProtoHTTPS: scheme = proto case config.ProtoHTTP2: scheme = config.ProtoHTTPS case config.ProtoHTTP2C: scheme = config.ProtoHTTP } return scheme } func getFastCGIClient(cfg *config.FastCGIProbeWrapperConfig) *http.Client { genericTimeout := time.Second * 30 var dialTimeout, readTimeout, writeTimeout time.Duration if dialTimeout = cfg.DialTimeout; dialTimeout == 0 { dialTimeout = genericTimeout } if readTimeout = cfg.ReadTimeout; readTimeout == 0 { readTimeout = genericTimeout } if writeTimeout = cfg.WriteTimeout; writeTimeout == 0 { writeTimeout = genericTimeout } return &http.Client{ Timeout: genericTimeout, Transport: &internal.FastCGITransport{ Root: cfg.Root, SplitPath: cfg.SplitPath, EnvVars: cfg.EnvVars, DialTimeout: dialTimeout, ReadTimeout: readTimeout, WriteTimeout: writeTimeout, }, } } ================================================ FILE: pkg/app/master/probe/http/internal/client.go ================================================ // This file is a modified version of // https://github.com/caddyserver/caddy/blob/44e5e9e/modules/caddyhttp/reverseproxy/fastcgi/client.go // Modifications made permit targeting containerized PHP scripts // and decouple Caddy dependencies. // Copyright 2015 Matthew Holt and The Caddy Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Forked Jan. 2015 from http://bitbucket.org/PinIdea/fcgi_client // (which is forked from https://code.google.com/p/go-fastcgi-client/). // This fork contains several fixes and improvements by Matt Holt and // other contributors to the Caddy project. // Copyright 2012 Junqing Tan and The Go Authors // Use of this source code is governed by a BSD-style // Part of source code is from Go fcgi package package internal import ( "bufio" "bytes" "context" "encoding/binary" "errors" "io" "mime/multipart" "net" "net/http" "net/http/httputil" "net/textproto" "net/url" "os" "path/filepath" "strconv" "strings" "sync" "time" ) // FCGIListenSockFileno describes listen socket file number. const FCGIListenSockFileno uint8 = 0 // FCGIHeaderLen describes header length. const FCGIHeaderLen uint8 = 8 // Version1 describes the version. const Version1 uint8 = 1 // FCGINullRequestID describes the null request ID. const FCGINullRequestID uint8 = 0 // FCGIKeepConn describes keep connection mode. const FCGIKeepConn uint8 = 1 const ( // BeginRequest is the begin request flag. BeginRequest uint8 = iota + 1 // AbortRequest is the abort request flag. AbortRequest // EndRequest is the end request flag. EndRequest // Params is the parameters flag. Params // Stdin is the standard input flag. Stdin // Stdout is the standard output flag. Stdout // Stderr is the standard error flag. Stderr // Data is the data flag. Data // GetValues is the get values flag. GetValues // GetValuesResult is the get values result flag. GetValuesResult // UnknownType is the unknown type flag. UnknownType // MaxType is the maximum type flag. MaxType = UnknownType ) const ( // Responder is the responder flag. Responder uint8 = iota + 1 // Authorizer is the authorizer flag. Authorizer // Filter is the filter flag. Filter ) const ( // RequestComplete is the completed request flag. RequestComplete uint8 = iota // CantMultiplexConns is the multiplexed connections flag. CantMultiplexConns // Overloaded is the overloaded flag. Overloaded // UnknownRole is the unknown role flag. UnknownRole ) const ( // MaxConns is the maximum connections flag. MaxConns string = "MAX_CONNS" // MaxRequests is the maximum requests flag. MaxRequests string = "MAX_REQS" // MultiplexConns is the multiplex connections flag. MultiplexConns string = "MPXS_CONNS" ) const ( maxWrite = 65500 // 65530 may work, but for compatibility maxPad = 255 ) type header struct { Version uint8 Type uint8 ID uint16 ContentLength uint16 PaddingLength uint8 Reserved uint8 } // for padding so we don't have to allocate all the time // not synchronized because we don't care what the contents are var pad [maxPad]byte func (h *header) init(recType uint8, reqID uint16, contentLength int) { h.Version = 1 h.Type = recType h.ID = reqID h.ContentLength = uint16(contentLength) h.PaddingLength = uint8(-contentLength & 7) } type record struct { h header rbuf []byte } func (rec *record) read(r io.Reader) (buf []byte, err error) { if err = binary.Read(r, binary.BigEndian, &rec.h); err != nil { return } if rec.h.Version != 1 { err = errors.New("fcgi: invalid header version") return } if rec.h.Type == EndRequest { err = io.EOF return } n := int(rec.h.ContentLength) + int(rec.h.PaddingLength) if len(rec.rbuf) < n { rec.rbuf = make([]byte, n) } if _, err = io.ReadFull(r, rec.rbuf[:n]); err != nil { return } buf = rec.rbuf[:int(rec.h.ContentLength)] return } // FCGIClient implements a FastCGI client, which is a standard for // interfacing external applications with Web servers. type FCGIClient struct { mutex sync.Mutex rwc io.ReadWriteCloser h header buf bytes.Buffer stderr bytes.Buffer keepAlive bool reqID uint16 } // DialWithDialerContext connects to the fcgi responder at the specified network address, using custom net.Dialer // and a context. // See func net.Dial for a description of the network and address parameters. func DialWithDialerContext(ctx context.Context, network, address string, dialer net.Dialer) (fcgi *FCGIClient, err error) { var conn net.Conn conn, err = dialer.DialContext(ctx, network, address) if err != nil { return } fcgi = &FCGIClient{ rwc: conn, keepAlive: false, reqID: 1, } return } // Close closes fcgi connection func (c *FCGIClient) Close() { c.rwc.Close() } func (c *FCGIClient) writeRecord(recType uint8, content []byte) (err error) { c.mutex.Lock() defer c.mutex.Unlock() c.buf.Reset() c.h.init(recType, c.reqID, len(content)) if err := binary.Write(&c.buf, binary.BigEndian, c.h); err != nil { return err } if _, err := c.buf.Write(content); err != nil { return err } if _, err := c.buf.Write(pad[:c.h.PaddingLength]); err != nil { return err } _, err = c.rwc.Write(c.buf.Bytes()) return err } func (c *FCGIClient) writeBeginRequest(role uint16, flags uint8) error { b := [8]byte{byte(role >> 8), byte(role), flags} return c.writeRecord(BeginRequest, b[:]) } func (c *FCGIClient) writePairs(recType uint8, pairs map[string]string) error { w := newWriter(c, recType) b := make([]byte, 8) nn := 0 for k, v := range pairs { m := 8 + len(k) + len(v) if m > maxWrite { // param data size exceed 65535 bytes" vl := maxWrite - 8 - len(k) v = v[:vl] } n := encodeSize(b, uint32(len(k))) n += encodeSize(b[n:], uint32(len(v))) m = n + len(k) + len(v) if (nn + m) > maxWrite { w.Flush() nn = 0 } nn += m if _, err := w.Write(b[:n]); err != nil { return err } if _, err := w.WriteString(k); err != nil { return err } if _, err := w.WriteString(v); err != nil { return err } } w.Close() return nil } func encodeSize(b []byte, size uint32) int { if size > 127 { size |= 1 << 31 binary.BigEndian.PutUint32(b, size) return 4 } b[0] = byte(size) return 1 } // bufWriter encapsulates bufio.Writer but also closes the underlying stream when // Closed. type bufWriter struct { closer io.Closer *bufio.Writer } func (w *bufWriter) Close() error { if err := w.Writer.Flush(); err != nil { w.closer.Close() return err } return w.closer.Close() } func newWriter(c *FCGIClient, recType uint8) *bufWriter { s := &streamWriter{c: c, recType: recType} w := bufio.NewWriterSize(s, maxWrite) return &bufWriter{s, w} } // streamWriter abstracts out the separation of a stream into discrete records. // It only writes maxWrite bytes at a time. type streamWriter struct { c *FCGIClient recType uint8 } func (w *streamWriter) Write(p []byte) (int, error) { nn := 0 for len(p) > 0 { n := len(p) if n > maxWrite { n = maxWrite } if err := w.c.writeRecord(w.recType, p[:n]); err != nil { return nn, err } nn += n p = p[n:] } return nn, nil } func (w *streamWriter) Close() error { // send empty record to close the stream return w.c.writeRecord(w.recType, nil) } type streamReader struct { c *FCGIClient buf []byte } func (w *streamReader) Read(p []byte) (n int, err error) { if len(p) > 0 { if len(w.buf) == 0 { // filter outputs for error log for { rec := &record{} var buf []byte buf, err = rec.read(w.c.rwc) if err != nil { return } // standard error output if rec.h.Type == Stderr { w.c.stderr.Write(buf) continue } w.buf = buf break } } n = len(p) if n > len(w.buf) { n = len(w.buf) } copy(p, w.buf[:n]) w.buf = w.buf[n:] } return } // Do made the request and returns a io.Reader that translates the data read // from fcgi responder out of fcgi packet before returning it. func (c *FCGIClient) Do(p map[string]string, req io.Reader) (r io.Reader, err error) { err = c.writeBeginRequest(uint16(Responder), 0) if err != nil { return } err = c.writePairs(Params, p) if err != nil { return } body := newWriter(c, Stdin) if req != nil { _, _ = io.Copy(body, req) } body.Close() r = &streamReader{c: c} return } // clientCloser is a io.ReadCloser. It wraps a io.Reader with a Closer // that closes FCGIClient connection. type clientCloser struct { *FCGIClient io.Reader } func (f clientCloser) Close() error { return f.rwc.Close() } // Request returns a HTTP Response with Header and Body // from fcgi responder func (c *FCGIClient) Request(p map[string]string, req io.Reader) (resp *http.Response, err error) { r, err := c.Do(p, req) if err != nil { return } rb := bufio.NewReader(r) tp := textproto.NewReader(rb) resp = new(http.Response) // Parse the response headers. mimeHeader, err := tp.ReadMIMEHeader() if err != nil && err != io.EOF { return } resp.Header = http.Header(mimeHeader) if resp.Header.Get("Status") != "" { statusParts := strings.SplitN(resp.Header.Get("Status"), " ", 2) resp.StatusCode, err = strconv.Atoi(statusParts[0]) if err != nil { return } if len(statusParts) > 1 { resp.Status = statusParts[1] } } else { resp.StatusCode = http.StatusOK } // TODO: fixTransferEncoding ? resp.TransferEncoding = resp.Header["Transfer-Encoding"] resp.ContentLength, _ = strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 64) if chunked(resp.TransferEncoding) { resp.Body = clientCloser{c, httputil.NewChunkedReader(rb)} } else { resp.Body = clientCloser{c, io.NopCloser(rb)} } return } // Get issues a GET request to the fcgi responder. func (c *FCGIClient) Get(p map[string]string, body io.Reader, l int64) (resp *http.Response, err error) { p["REQUEST_METHOD"] = "GET" p["CONTENT_LENGTH"] = strconv.FormatInt(l, 10) return c.Request(p, body) } // Head issues a HEAD request to the fcgi responder. func (c *FCGIClient) Head(p map[string]string) (resp *http.Response, err error) { p["REQUEST_METHOD"] = "HEAD" p["CONTENT_LENGTH"] = "0" return c.Request(p, nil) } // Options issues an OPTIONS request to the fcgi responder. func (c *FCGIClient) Options(p map[string]string) (resp *http.Response, err error) { p["REQUEST_METHOD"] = "OPTIONS" p["CONTENT_LENGTH"] = "0" return c.Request(p, nil) } // Post issues a POST request to the fcgi responder. with request body // in the format that bodyType specified func (c *FCGIClient) Post(p map[string]string, method string, bodyType string, body io.Reader, l int64) (resp *http.Response, err error) { if p == nil { p = map[string]string{} } p["REQUEST_METHOD"] = strings.ToUpper(method) if len(p["REQUEST_METHOD"]) == 0 || p["REQUEST_METHOD"] == "GET" { p["REQUEST_METHOD"] = "POST" } p["CONTENT_LENGTH"] = strconv.FormatInt(l, 10) if len(bodyType) > 0 { p["CONTENT_TYPE"] = bodyType } else { p["CONTENT_TYPE"] = "application/x-www-form-urlencoded" } return c.Request(p, body) } // PostForm issues a POST to the fcgi responder, with form // as a string key to a list values (url.Values) func (c *FCGIClient) PostForm(p map[string]string, data url.Values) (resp *http.Response, err error) { body := bytes.NewReader([]byte(data.Encode())) return c.Post(p, "POST", "application/x-www-form-urlencoded", body, int64(body.Len())) } // PostFile issues a POST to the fcgi responder in multipart(RFC 2046) standard, // with form as a string key to a list values (url.Values), // and/or with file as a string key to a list file path. func (c *FCGIClient) PostFile(p map[string]string, data url.Values, file map[string]string) (resp *http.Response, err error) { buf := &bytes.Buffer{} writer := multipart.NewWriter(buf) bodyType := writer.FormDataContentType() for key, val := range data { for _, v0 := range val { err = writer.WriteField(key, v0) if err != nil { return } } } for key, val := range file { fd, e := os.Open(val) if e != nil { return nil, e } defer fd.Close() part, e := writer.CreateFormFile(key, filepath.Base(val)) if e != nil { return nil, e } _, err = io.Copy(part, fd) if err != nil { return } } err = writer.Close() if err != nil { return } return c.Post(p, "POST", bodyType, buf, int64(buf.Len())) } // SetReadTimeout sets the read timeout for future calls that read from the // fcgi responder. A zero value for t means no timeout will be set. func (c *FCGIClient) SetReadTimeout(t time.Duration) error { if conn, ok := c.rwc.(net.Conn); ok && t != 0 { return conn.SetReadDeadline(time.Now().Add(t)) } return nil } // SetWriteTimeout sets the write timeout for future calls that send data to // the fcgi responder. A zero value for t means no timeout will be set. func (c *FCGIClient) SetWriteTimeout(t time.Duration) error { if conn, ok := c.rwc.(net.Conn); ok && t != 0 { return conn.SetWriteDeadline(time.Now().Add(t)) } return nil } // Checks whether chunked is part of the encodings stack func chunked(te []string) bool { return len(te) > 0 && te[0] == "chunked" } ================================================ FILE: pkg/app/master/probe/http/internal/fastcgi.go ================================================ // This file is a modified version of // https://github.com/caddyserver/caddy/blob/44e5e9e/modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go // Modifications made permit targeting containerized PHP scripts // and decouple Caddy dependencies. // Copyright 2015 Matthew Holt and The Caddy Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package internal import ( "crypto/tls" "encoding/json" "fmt" "net" "net/http" "path" "strconv" "strings" "time" log "github.com/sirupsen/logrus" ) var _ http.RoundTripper = &FastCGITransport{} // FastCGITransport facilitates FastCGI communication. type FastCGITransport struct { // Root is the fastcgi root directory. // Defaults to the root directory of the container. Root string // The path in the URL will be split into two, with the first piece ending // with the value of SplitPath. The first piece will be assumed as the // actual resource (CGI script) name, and the second piece will be set to // PATH_INFO for the CGI script to use. SplitPath []string // Extra environment variables. EnvVars map[string]string // The duration used to set a deadline when connecting to an upstream. DialTimeout time.Duration // The duration used to set a deadline when reading from the FastCGI server. ReadTimeout time.Duration // The duration used to set a deadline when sending to the FastCGI server. WriteTimeout time.Duration } // init sets up t. func (t *FastCGITransport) init() { if t.Root == "" { t.Root = "/" } } // RoundTrip implements http.RoundTripper. func (t FastCGITransport) RoundTrip(r *http.Request) (*http.Response, error) { t.init() env, err := t.buildEnv(r) if err != nil { return nil, fmt.Errorf("building environment: %v", err) } network, address := "tcp", r.URL.Host if log.IsLevelEnabled(log.DebugLevel) { envJSON, _ := json.Marshal(env) log.Debugf("HTTP probe - FastCGI env - %s", string(envJSON)) } ctx := r.Context() dialer := net.Dialer{Timeout: t.DialTimeout} fcgiBackend, err := DialWithDialerContext(ctx, network, address, dialer) if err != nil { // TODO: wrap in a special error type if the dial failed, so retries can happen if enabled return nil, fmt.Errorf("dialing backend: %v", err) } // fcgiBackend gets closed when response body is closed (see clientCloser) // read/write timeouts if err := fcgiBackend.SetReadTimeout(t.ReadTimeout); err != nil { return nil, fmt.Errorf("setting read timeout: %v", err) } if err := fcgiBackend.SetWriteTimeout(t.WriteTimeout); err != nil { return nil, fmt.Errorf("setting write timeout: %v", err) } contentLength := r.ContentLength if contentLength == 0 { contentLength, _ = strconv.ParseInt(r.Header.Get("Content-Length"), 10, 64) } var resp *http.Response switch r.Method { case http.MethodHead: resp, err = fcgiBackend.Head(env) case http.MethodGet: resp, err = fcgiBackend.Get(env, r.Body, contentLength) case http.MethodOptions: resp, err = fcgiBackend.Options(env) default: resp, err = fcgiBackend.Post(env, r.Method, r.Header.Get("Content-Type"), r.Body, contentLength) } return resp, err } // buildEnv returns a set of CGI environment variables for the request. func (t FastCGITransport) buildEnv(r *http.Request) (map[string]string, error) { // Separate remote IP and port; more lenient than net.SplitHostPort var ip, port string if idx := strings.LastIndex(r.RemoteAddr, ":"); idx > -1 { ip = r.RemoteAddr[:idx] port = r.RemoteAddr[idx+1:] } else { ip = r.RemoteAddr } // Remove [] from IPv6 addresses ip = strings.Replace(ip, "[", "", 1) ip = strings.Replace(ip, "]", "", 1) // make sure file root is absolute if !path.IsAbs(t.Root) { return nil, fmt.Errorf("root %q is not absolute", t.Root) } root := t.Root fpath := r.URL.Path scriptName := fpath docURI := fpath // split "actual path" from "path info" if configured var pathInfo string if splitPos := t.splitPos(fpath); splitPos > -1 { docURI = fpath[:splitPos] pathInfo = fpath[splitPos:] // Strip PATH_INFO from SCRIPT_NAME scriptName = strings.TrimSuffix(scriptName, pathInfo) } // SCRIPT_FILENAME is the absolute path of SCRIPT_NAME scriptFilename := path.Join(root, scriptName) // Ensure the SCRIPT_NAME has a leading slash for compliance with RFC3875 // Info: https://tools.ietf.org/html/rfc3875#section-4.1.13 if scriptName != "" && !path.IsAbs(scriptName) { scriptName = "/" + scriptName } requestScheme := "http" if r.TLS != nil { requestScheme = "https" } reqHost, reqPort, err := net.SplitHostPort(r.Host) if err != nil { // whatever, just assume there was no port reqHost = r.Host } authUser, _, _ := r.BasicAuth() // Some variables are unused but cleared explicitly to prevent // the parent environment from interfering. env := map[string]string{ // Variables defined in CGI 1.1 spec "AUTH_TYPE": "", // Not used "CONTENT_LENGTH": r.Header.Get("Content-Length"), "CONTENT_TYPE": r.Header.Get("Content-Type"), "GATEWAY_INTERFACE": "CGI/1.1", "PATH_INFO": pathInfo, "QUERY_STRING": r.URL.RawQuery, "REMOTE_ADDR": ip, "REMOTE_HOST": ip, // For speed, remote host lookups disabled "REMOTE_PORT": port, "REMOTE_IDENT": "", // Not used "REMOTE_USER": authUser, "REQUEST_METHOD": r.Method, "REQUEST_SCHEME": requestScheme, "SERVER_NAME": reqHost, "SERVER_PROTOCOL": r.Proto, "SERVER_SOFTWARE": "slimtoolkit/v0.0.0", // Other variables "DOCUMENT_ROOT": root, "DOCUMENT_URI": docURI, "HTTP_HOST": r.Host, // added here, since not always part of headers // "REQUEST_URI": "", "SCRIPT_FILENAME": scriptFilename, "SCRIPT_NAME": scriptName, } // compliance with the CGI specification requires that // PATH_TRANSLATED should only exist if PATH_INFO is defined. // Info: https://www.ietf.org/rfc/rfc3875 Page 14 if pathInfo != "" { env["PATH_TRANSLATED"] = path.Join(root, pathInfo) } // compliance with the CGI specification requires that // SERVER_PORT should only exist if it's a valid numeric value. // Info: https://www.ietf.org/rfc/rfc3875 Page 18 if reqPort != "" { env["SERVER_PORT"] = reqPort } // Some web apps rely on knowing HTTPS or not if r.TLS != nil { env["HTTPS"] = "on" // and pass the protocol details in a manner compatible with apache's mod_ssl // (which is why these have a SSL_ prefix and not TLS_). v, ok := tlsProtocolStrings[r.TLS.Version] if ok { env["SSL_PROTOCOL"] = v } // and pass the cipher suite in a manner compatible with apache's mod_ssl for _, cs := range tls.CipherSuites() { if cs.ID == r.TLS.CipherSuite { env["SSL_CIPHER"] = cs.Name break } } } // Add env variables from config (with support for placeholders in values) for key, value := range t.EnvVars { env[key] = value } // Add all HTTP headers to env variables for field, val := range r.Header { header := strings.ToUpper(field) header = headerNameReplacer.Replace(header) env["HTTP_"+header] = strings.Join(val, ", ") } return env, nil } // splitPos returns the index where path should // be split based on t.SplitPath. func (t FastCGITransport) splitPos(path string) int { if len(t.SplitPath) == 0 { return 0 } lowerPath := strings.ToLower(path) for _, split := range t.SplitPath { if idx := strings.Index(lowerPath, strings.ToLower(split)); idx > -1 { return idx + len(split) } } return -1 } // Map of supported protocols to Apache ssl_mod format // Note that these are slightly different from SupportedProtocols in caddytls/config.go var tlsProtocolStrings = map[uint16]string{ tls.VersionTLS10: "TLSv1", tls.VersionTLS11: "TLSv1.1", tls.VersionTLS12: "TLSv1.2", tls.VersionTLS13: "TLSv1.3", } var headerNameReplacer = strings.NewReplacer(" ", "_", "-", "_") ================================================ FILE: pkg/app/master/probe/http/swagger.go ================================================ package http import ( "bytes" "fmt" "io" "net/http" "net/url" "os" "strings" "time" "github.com/getkin/kin-openapi/openapi2" "github.com/getkin/kin-openapi/openapi2conv" "github.com/getkin/kin-openapi/openapi3" "github.com/ghodss/yaml" log "github.com/sirupsen/logrus" ) type apiSpecInfo struct { spec *openapi3.T prefixOverride string } func (p *CustomProbe) loadAPISpecFiles() { for _, info := range p.opts.APISpecFiles { fileName := info prefixOverride := "" if strings.Contains(info, ":") { parts := strings.SplitN(info, ":", 2) fileName = parts[0] prefixOverride = parts[1] } spec, err := loadAPISpecFromFile(fileName) if err != nil { p.xc.Out.Info("http.probe.apispec.error", ovars{ "message": "error loading api spec file", "error": err, }) continue } if spec == nil { p.xc.Out.Info("http.probe.apispec", ovars{ "message": "unsupported spec type", }) continue } info := apiSpecInfo{ spec: spec, prefixOverride: prefixOverride, } p.APISpecProbes = append(p.APISpecProbes, info) } } func parseAPISpec(rdata []byte) (*openapi3.T, error) { if isOpenAPI(rdata) { log.Debug("http.CustomProbe.parseAPISpec - is openapi") loader := openapi3.NewLoader() loader.IsExternalRefsAllowed = true spec, err := loader.LoadFromData(rdata) if err != nil { log.Debugf("http.CustomProbe.parseAPISpec.LoadFromData - error=%v", err) return nil, err } return spec, nil } if isSwagger(rdata) { log.Debug("http.CustomProbe.parseAPISpec - is swagger") spec2 := &openapi2.T{} if err := yaml.Unmarshal(rdata, spec2); err != nil { log.Debugf("http.CustomProbe.parseAPISpec.yaml.Unmarshal - error=%v", err) return nil, err } spec, err := openapi2conv.ToV3(spec2) if err != nil { log.Debugf("http.CustomProbe.parseAPISpec.ToV3 - error=%v", err) return nil, err } return spec, nil } log.Debugf("http.CustomProbe.parseAPISpec - unsupported api spec type (%d): %s", len(rdata), string(rdata)) return nil, nil } func loadAPISpecFromEndpoint(client *http.Client, endpoint string) (*openapi3.T, error) { log.Debugf("http.CustomProbe.loadAPISpecFromEndpoint(%s)", endpoint) req, err := http.NewRequest("GET", endpoint, nil) if err != nil { log.Debugf("http.CustomProbe.loadAPISpecFromEndpoint.http.NewRequest - error=%v", err) return nil, err } res, err := client.Do(req) if err != nil { log.Debugf("http.CustomProbe.loadAPISpecFromEndpoint.httpClient.Do - error=%v", err) return nil, err } if res != nil && res.Body != nil { defer res.Body.Close() } if res.Body != nil { rdata, err := io.ReadAll(res.Body) if err != nil { log.Debugf("http.CustomProbe.loadAPISpecFromEndpoint.response.read - error=%v", err) return nil, err } return parseAPISpec(rdata) } log.Debug("http.CustomProbe.loadAPISpecFromEndpoint.response - no body") return nil, nil } func loadAPISpecFromFile(name string) (*openapi3.T, error) { rdata, err := os.ReadFile(name) if err != nil { log.Debugf("http.CustomProbe.loadAPISpecFromFile.ReadFile - error=%v", err) return nil, err } return parseAPISpec(rdata) } func isSwagger(data []byte) bool { if (bytes.Contains(data, []byte(`"swagger":`)) || bytes.Contains(data, []byte(`swagger:`))) && bytes.Contains(data, []byte(`paths`)) { return true } return false } func isOpenAPI(data []byte) bool { if (bytes.Contains(data, []byte(`"openapi":`)) || bytes.Contains(data, []byte(`openapi:`))) && bytes.Contains(data, []byte(`paths`)) { return true } return false } func apiSpecPrefix(spec *openapi3.T) (string, error) { //for now get the api prefix from the first server struc //later, support multiple prefixes if there's more than one server struct var prefix string for _, sinfo := range spec.Servers { xurl := sinfo.URL if strings.Contains(xurl, "{") { for k, vinfo := range sinfo.Variables { varStr := fmt.Sprintf("{%s}", k) if strings.Contains(xurl, varStr) { valStr := "var" if vinfo.Default != "" { valStr = fmt.Sprintf("%v", vinfo.Default) } else if len(vinfo.Enum) > 0 { valStr = fmt.Sprintf("%v", vinfo.Enum[0]) } xurl = strings.ReplaceAll(xurl, varStr, valStr) } } } if strings.Contains(xurl, "{") { xurl = strings.ReplaceAll(xurl, "{", "") if strings.Contains(xurl, "}") { xurl = strings.ReplaceAll(xurl, "}", "") } } parsed, err := url.Parse(xurl) if err != nil { return "", err } if parsed.Path != "" && parsed.Path != "/" { prefix = parsed.Path } } return prefix, nil } func (p *CustomProbe) loadAPISpecs(proto, targetHost, port string) { baseAddr := getHTTPAddr(proto, targetHost, port) client, err := getHTTPClient(proto) if err != nil { p.xc.Out.Error("HTTP probe - construct client error - %v", err.Error()) return } //TODO: //Need to support user provided target port for the spec, //but these need to be mapped to the actual port at runtime //Need to support user provided target proto for the spec for _, info := range p.opts.APISpecs { specPath := info prefixOverride := "" if strings.Contains(info, ":") { parts := strings.SplitN(info, ":", 2) specPath = parts[0] prefixOverride = parts[1] } addr := fmt.Sprintf("%s%s", baseAddr, specPath) spec, err := loadAPISpecFromEndpoint(client, addr) if err != nil { p.xc.Out.Info("http.probe.apispec.error", ovars{ "message": "error loading api spec from endpoint", "error": err, }) continue } if spec == nil { p.xc.Out.Info("http.probe.apispec", ovars{ "message": "unsupported spec type", }) continue } info := apiSpecInfo{ spec: spec, prefixOverride: prefixOverride, } p.APISpecProbes = append(p.APISpecProbes, info) } } func pathOps(pinfo *openapi3.PathItem) map[string]*openapi3.Operation { ops := map[string]*openapi3.Operation{} addPathOp(&ops, pinfo.Connect, "connect") addPathOp(&ops, pinfo.Delete, "delete") addPathOp(&ops, pinfo.Get, "get") addPathOp(&ops, pinfo.Head, "head") addPathOp(&ops, pinfo.Options, "options") addPathOp(&ops, pinfo.Patch, "patch") addPathOp(&ops, pinfo.Post, "post") addPathOp(&ops, pinfo.Put, "put") addPathOp(&ops, pinfo.Trace, "trace") return ops } func addPathOp(m *map[string]*openapi3.Operation, op *openapi3.Operation, name string) { if op != nil { (*m)[name] = op } } func (p *CustomProbe) probeAPISpecEndpoints(proto, targetHost, port, prefix string, spec *openapi3.T) { addr := getHTTPAddr(proto, targetHost, port) if p.printState { p.xc.Out.State("http.probe.api-spec.probe.endpoint.starting", ovars{ "addr": addr, "prefix": prefix, "endpoints": len(spec.Paths), }) } httpClient, err := getHTTPClient(proto) if err != nil { p.xc.Out.Error("HTTP probe - construct client error - %v", err.Error()) return } for apiPath, pathInfo := range spec.Paths { //very primitive way to set the path params (will break for numeric values) if strings.Contains(apiPath, "{") { apiPath = strings.ReplaceAll(apiPath, "{", "") if strings.Contains(apiPath, "}") { apiPath = strings.ReplaceAll(apiPath, "}", "") } } endpoint := fmt.Sprintf("%s%s%s", addr, prefix, apiPath) ops := pathOps(pathInfo) for apiMethod := range ops { //make a call (no params for now) p.apiSpecEndpointCall(httpClient, endpoint, apiMethod) } } } func (p *CustomProbe) apiSpecEndpointCall(client *http.Client, endpoint, method string) { maxRetryCount := probeRetryCount if p.opts.RetryCount > 0 { maxRetryCount = p.opts.RetryCount } notReadyErrorWait := time.Duration(16) webErrorWait := time.Duration(8) otherErrorWait := time.Duration(4) if p.opts.RetryWait > 0 { webErrorWait = time.Duration(p.opts.RetryWait) notReadyErrorWait = time.Duration(p.opts.RetryWait * 2) otherErrorWait = time.Duration(p.opts.RetryWait / 2) } method = strings.ToUpper(method) for i := 0; i < maxRetryCount; i++ { req, err := http.NewRequest(method, endpoint, nil) if err != nil { p.xc.Out.Error("HTTP probe - construct request error - %v", err.Error()) // Break since the same args are passed to NewRequest() on each loop. break } //no body, no request headers and no credentials for now res, err := client.Do(req) p.CallCount++ if res != nil { if res.Body != nil { io.Copy(io.Discard, res.Body) } defer res.Body.Close() } statusCode := "error" callErrorStr := "none" if err == nil { statusCode = fmt.Sprintf("%v", res.StatusCode) } else { callErrorStr = err.Error() } if p.printState { p.xc.Out.Info("http.probe.api-spec.probe.endpoint.call", ovars{ "status": statusCode, "method": method, "endpoint": endpoint, "attempt": i + 1, "error": callErrorStr, "time": time.Now().UTC().Format(time.RFC3339), }) } if err == nil { p.OkCount++ break } else { p.ErrCount++ if urlErr, ok := err.(*url.Error); ok { if urlErr.Err == io.EOF { log.Debugf("HTTP probe - target not ready yet (retry again later)...") time.Sleep(notReadyErrorWait * time.Second) } else { log.Debugf("HTTP probe - web error... retry again later...") time.Sleep(webErrorWait * time.Second) } } else { log.Debugf("HTTP probe - other error... retry again later...") time.Sleep(otherErrorWait * time.Second) } } } } ================================================ FILE: pkg/app/master/probe/http/wsclient.go ================================================ package http import ( "fmt" "time" "github.com/gorilla/websocket" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/acounter" ) const ( ProtoWS = "ws" ProtoWSS = "wss" ) type WebsocketClient struct { OnRead func(mtype int, mdata []byte) ReadCh chan WebsocketMessage Conn *websocket.Conn ReadCount acounter.Type PongCount acounter.Type PingCount acounter.Type Addr string pongCh chan string doneCh chan struct{} } type WebsocketMessage struct { Type int Data []byte } func NewWebsocketClient(proto, host, port string) (*WebsocketClient, error) { if proto == "" { proto = ProtoWS } if !IsValidWSProto(proto) { return nil, fmt.Errorf("invalid ws proto - %s", proto) } wsclient := &WebsocketClient{ Addr: fmt.Sprintf("%s://%s:%s", proto, host, port), doneCh: make(chan struct{}), pongCh: make(chan string, 10), } return wsclient, nil } func IsValidWSProto(proto string) bool { switch proto { case ProtoWS, ProtoWSS: return true default: return false } } func (wc *WebsocketClient) Connect() error { conn, _, err := websocket.DefaultDialer.Dial(wc.Addr, nil) if err != nil { log.Debugf("WebsocketClient.Connect: ws.Dial error=%v", err) return err } pongHandler := func(data string) error { wc.PongCount.Inc() log.Debugf("WebsocketClient: Pong Handler - data='%s'", data) wc.pongCh <- data return nil } conn.SetPongHandler(pongHandler) defaultPingHandler := conn.PingHandler() pingHandler := func(data string) error { wc.PingCount.Inc() log.Debugf("WebsocketClient: Ping Handler - data='%s'", data) return defaultPingHandler(data) } conn.SetPingHandler(pingHandler) go func() { for { log.Debug("WebsocketClient: reader - waiting for errors...") select { case <-wc.doneCh: log.Debug("WebsocketClient: reader - error collector - done...") return default: mtype, message, err := conn.ReadMessage() if err != nil { log.Debugf("WebsocketClient: reader - read error=%v", err) //read err: websocket: close 1000 (normal) return } log.Debugf("WebsocketClient: reader - read={type=%s(%d) len=%d data='%s'}\n", wsMessageType(mtype), mtype, len(message), message) wc.ReadCount.Inc() if wc.OnRead != nil { wc.OnRead(mtype, message) } if wc.ReadCh != nil { select { case wc.ReadCh <- WebsocketMessage{Type: mtype, Data: message}: log.Debug("WebsocketClient: reader - posted message to wc.ReadCh") case <-time.After(time.Second * 5): log.Debug("WebsocketClient: reader - timed out posting message to wc.ReadCh") } } } } }() wc.Conn = conn return nil } func wsMessageType(val int) string { switch val { case websocket.TextMessage: return "text" case websocket.BinaryMessage: return "binary" case websocket.CloseMessage: return "close" case websocket.PingMessage: return "ping" case websocket.PongMessage: return "pong" default: return fmt.Sprintf("unknown(%d)", val) } } func (wc *WebsocketClient) CheckConnection() error { log.Debugf("WebsocketClient.CheckConnection: conn.WriteControl(websocket.PingMessage)") err := wc.Conn.WriteControl(websocket.PingMessage, []byte("wc.ping"), time.Now().Add(3*time.Second)) if err != nil { log.Debugf("WebsocketClient.CheckConnection: conn.WriteControl(websocket.PingMessage) error=%v", err) return err } select { case pongData := <-wc.pongCh: log.Debugf("WebsocketClient.CheckConnection: pong data = %v", pongData) case <-time.After(time.Second * 5): log.Debug("WebsocketClient.CheckConnection: pong data timeout") } return nil } func (wc *WebsocketClient) WriteString(data string) error { err := wc.Conn.WriteMessage(websocket.TextMessage, []byte(data)) if err != nil { log.Debugf("WebsocketClient.WriteString: conn.WriteMessage(websocket.TextMessage) error=%v", err) return err } return nil } func (wc *WebsocketClient) WriteBinary(data []byte) error { err := wc.Conn.WriteMessage(websocket.BinaryMessage, data) if err != nil { log.Debugf("WebsocketClient.WriteString: conn.WriteMessage(websocket.BinaryMessage) error=%v", err) return err } return nil } func (wc *WebsocketClient) Disconnect() error { if wc.Conn != nil { close(wc.doneCh) wc.doneCh = nil err := wc.Conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) if err != nil { log.Debugf("WebsocketClient.Disconnect: conn.WriteMessage(websocket.CloseMessage) error=%v", err) return err } //TODO: should wait for the 'websocket: close 1000 (normal)' read error time.Sleep(2 * time.Second) wc.Conn.Close() wc.Conn = nil } return nil } ================================================ FILE: pkg/app/master/security/apparmor/apparmor.go ================================================ package apparmor import ( "encoding/json" "os" "path/filepath" "text/template" "github.com/slimtoolkit/slim/pkg/report" ) const appArmorTemplate = ` profile {{.ProfileName}} flags=(attach_disconnected,mediate_deleted) { network, {{range $value := .ExeFileRules}} {{$value.FilePath}} {{$value.PermSet}}, {{end}} {{range $value := .WriteFileRules}} {{$value.FilePath}} {{$value.PermSet}}, {{end}} {{range $value := .ReadFileRules}} {{$value.FilePath}} {{$value.PermSet}}, {{end}} } ` type appArmorFileRule struct { FilePath string PermSet string } type appArmorProfileData struct { ProfileName string ExeFileRules []appArmorFileRule WriteFileRules []appArmorFileRule ReadFileRules []appArmorFileRule } //TODO: //need to safe more metadata about the artifacts in the monitor data //1. exe bit //2. w/r operation info (so we can add useful write rules) // GenProfile creates an AppArmor profile func GenProfile(artifactLocation string, profileName string) error { containerReportFilePath := filepath.Join(artifactLocation, report.DefaultContainerReportFileName) if _, err := os.Stat(containerReportFilePath); err != nil { return err } reportFile, err := os.Open(containerReportFilePath) if err != nil { return err } defer reportFile.Close() var creport report.ContainerReport if err = json.NewDecoder(reportFile).Decode(&creport); err != nil { return err } profilePath := filepath.Join(artifactLocation, profileName) profileFile, err := os.OpenFile(profilePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY|os.O_APPEND, 0644) if err != nil { return err } defer profileFile.Close() profileData := appArmorProfileData{ProfileName: profileName} for _, aprops := range creport.Image.Files { if aprops == nil { continue } if aprops.Flags == nil { //default to "R" (todo: double check flag creation...) profileData.ReadFileRules = append(profileData.ReadFileRules, appArmorFileRule{ FilePath: aprops.FilePath, PermSet: "r", }) } else { switch { case aprops.Flags["X"]: profileData.ExeFileRules = append(profileData.ExeFileRules, appArmorFileRule{ FilePath: aprops.FilePath, PermSet: report.PermSetFromFlags(aprops.Flags), }) case aprops.Flags["W"]: profileData.WriteFileRules = append(profileData.WriteFileRules, appArmorFileRule{ FilePath: aprops.FilePath, PermSet: report.PermSetFromFlags(aprops.Flags), }) case aprops.Flags["R"]: profileData.ReadFileRules = append(profileData.ReadFileRules, appArmorFileRule{ FilePath: aprops.FilePath, PermSet: report.PermSetFromFlags(aprops.Flags), }) default: //logrus.Printf("slim: genAppArmorProfile - other artifact => %v\n", aprops) //note: most are Symlinks } } } t, err := template.New("profile").Parse(appArmorTemplate) if err != nil { return err } if err := t.Execute(profileFile, profileData); err != nil { return err } return nil } ================================================ FILE: pkg/app/master/security/seccomp/seccomp.go ================================================ package seccomp import ( "encoding/json" "fmt" "os" "path/filepath" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/system" "github.com/slimtoolkit/slim/pkg/third_party/opencontainers/specs" log "github.com/sirupsen/logrus" ) var archMap = map[system.ArchName]specs.Arch{ system.ArchName386: specs.ArchX86, system.ArchNameAmd64: specs.ArchX86_64, system.ArchNameArm32: specs.ArchARM, system.ArchNameArm64: specs.ArchAARCH64, } func archNameToSeccompArch(name string) specs.Arch { if arch, ok := archMap[system.ArchName(name)]; ok { return arch } return "unknown" } var extraCalls = []string{ "openat", "getdents64", "capget", "capset", "chdir", "setuid", "setgroups", "setgid", "prctl", "fchown", "getppid", "getpid", "getuid", "getgid", "epoll_pwait", "newfstatat", "exit", "stat", "lstat", "write", "futex", "execve", //always detected, but it's still one of the syscalls Docker itself needs "rt_sigreturn", "rt_sigsuspend", "exit_group", "kill", //extra calls "sendmsg", "wait4", "setitimer", "unlink", "dup3", //needed for some tty detection/setup logic (doesn't always get picked up) "getcwd", //safe to add } // GenProfile creates a SecComp profile func GenProfile(artifactLocation string, profileName string) error { containerReportFilePath := filepath.Join(artifactLocation, report.DefaultContainerReportFileName) if _, err := os.Stat(containerReportFilePath); err != nil { return err } reportFile, err := os.Open(containerReportFilePath) if err != nil { return err } defer reportFile.Close() var creport report.ContainerReport if err = json.NewDecoder(reportFile).Decode(&creport); err != nil { return err } if !creport.Monitors.Pt.Enabled { log.Debug("seccomp.GenProfile: not generating seccomp profile (PT mon disabled, no syscall info)") return nil } profilePath := filepath.Join(artifactLocation, profileName) log.Debug("seccomp.GenProfile: saving seccomp profile to ", profilePath) profile := &specs.Seccomp{ DefaultAction: specs.ActErrno, Architectures: []specs.Arch{ archNameToSeccompArch(creport.Monitors.Pt.ArchName), }, } nameResolver := system.CallNameResolver(system.ArchName(creport.Monitors.Pt.ArchName)) if nameResolver != nil { for _, xcall := range extraCalls { if cnum, ok := nameResolver(xcall); ok { cnKey := fmt.Sprintf("%d", cnum) if _, ok := creport.Monitors.Pt.SyscallStats[cnKey]; !ok { creport.Monitors.Pt.SyscallStats[cnKey] = report.SyscallStatInfo{Name: xcall} } } } } scSpec := specs.Syscall{ Action: specs.ActAllow, } for _, scInfo := range creport.Monitors.Pt.SyscallStats { scSpec.Names = append(scSpec.Names, scInfo.Name) } profile.Syscalls = append(profile.Syscalls, &scSpec) profileData, err := json.MarshalIndent(profile, "", " ") if err != nil { return err } err = os.WriteFile(profilePath, profileData, 0644) if err != nil { return err } return nil } ================================================ FILE: pkg/app/master/signals/signals.go ================================================ package signals import ( "os" "os/signal" "syscall" log "github.com/sirupsen/logrus" ) var AppContinueChan = make(chan struct{}) var appDoneChan = make(chan struct{}) var signals = []os.Signal{ syscall.SIGUSR1, } func InitHandlers() { sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, signals...) log.Debugf("slim: listening for signals - %+v", signals) go func() { for { select { case <-appDoneChan: return case sig := <-sigChan: switch sig { case syscall.SIGUSR1: log.Debug("slim: continue signal") AppContinueChan <- struct{}{} default: log.Debugf("slim: other signal (%v)...", sig) } } } }() } ================================================ FILE: pkg/app/master/update/update.go ================================================ package update import ( "errors" "fmt" "io" "net/http" "net/url" "os" "path/filepath" "runtime" "time" vchecker "github.com/slimtoolkit/slim/pkg/app/master/version" "github.com/slimtoolkit/slim/pkg/util/errutil" "github.com/slimtoolkit/slim/pkg/util/fsutil" vinfo "github.com/slimtoolkit/slim/pkg/version" "github.com/c4milo/unpackit" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/go-update" "github.com/slimtoolkit/uiprogress" ) const ( hdrUserAgent = "User-Agent" hdrContentLength = "Content-Length" downloadEndpoint = "https://downloads.dockerslim.com/releases" masterAppName = "slim" sensorAppName = "slim-sensor" distDirName = "dist" artifactsPerms = 0740 ) var ( errUnexpectedHTTPStatus = errors.New("unexpected HTTP status code") ) // Run checks the current version and updates it if it doesn't match the latest available version func Run(doDebug bool, statePath string, inContainer, isDSImage, doShowProgress bool) { logger := log.WithFields(log.Fields{"app": "slim", "command": "update"}) appPath, err := os.Executable() errutil.FailOn(err) appDirPath := filepath.Dir(appPath) vstatus := vchecker.Check(inContainer, isDSImage) logger.Debugf("Version Status => %+v", vstatus) if vstatus == nil || vstatus.Status != "success" { fmt.Printf("slim[update]: info=status message='version check was not successful'\n") fmt.Printf("slim[update]: state=exited version=%s\n", vinfo.Current()) return } if !vstatus.Outdated { fmt.Printf("slim[update]: info=status message='already using the current version'\n") fmt.Printf("slim[update]: state=exited version=%s\n", vinfo.Current()) return } fmt.Printf("slim[update]: info=version local=%s current=%s\n", vinfo.Tag(), vstatus.Current) blobNameBase, blobNameExt := getReleaseBlobInfo() errutil.FailWhen(blobNameBase == "", "could not discover platform-specific release package name") releaseDirPath, statePath := fsutil.PrepareReleaseStateDirs(statePath, vstatus.Current) errutil.FailOn(err) blobName := fmt.Sprintf("%s.%s", blobNameBase, blobNameExt) blobPath := filepath.Join(releaseDirPath, blobName) logger.Debugf("release package blob: %v", blobPath) if fsutil.Exists(blobPath) { //feature: not removing/replacing the existing release package blob if it's already there fmt.Printf("slim[update]: info=status message='release package already downloaded'\n") fmt.Printf("slim[update]: state=exited version=%s\n", vinfo.Current()) return } fmt.Println("slim[update]: state=update.download.started") releaseDownloadPath := fmt.Sprintf("%s/%s/%s", downloadEndpoint, vstatus.Current, blobName) logger.Debugf("release download path: %v", releaseDownloadPath) if !isGoodDownloadSource(logger, releaseDownloadPath) { fmt.Printf("slim[update]: info=status message='release package download location is not accessible'\n") fmt.Printf("slim[update]: state=exited version=%s\n", vinfo.Current()) return } var brConstructor BlobReaderConstructor = newPassThroughReader if doShowProgress { logger.Debug("show download progress") brConstructor = newProgressReader } err = downloadRelease(logger, blobPath, releaseDownloadPath, brConstructor) if err != nil { logger.Debugf("error downloading release: %v", err) fmt.Printf("slim[update]: info=status message='error downloading release package'\n") fmt.Printf("slim[update]: state=exited version=%s\n", vinfo.Current()) return } fmt.Println("slim[update]: state=update.download.completed") if err := unpackRelease(logger, blobPath, releaseDirPath, blobNameBase); err != nil { logger.Debugf("error unpacking release package: %v", err) fmt.Printf("slim[update]: info=status message='error unpacking release package'\n") fmt.Printf("slim[update]: state=exited version=%s\n", vinfo.Current()) return } fmt.Println("slim[update]: state=update.unpacked") if err := installRelease(logger, appDirPath, statePath, releaseDirPath); err != nil { logger.Debugf("error installing release: %v", err) fmt.Printf("slim[update]: info=status message='error installing release'\n") fmt.Printf("slim[update]: state=exited version=%s\n", vinfo.Current()) return } fmt.Println("slim[update]: state=update.installed") fmt.Printf("slim[update]: state=exited version=%s\n", vinfo.Current()) } func getReleaseBlobInfo() (base string, ext string) { switch runtime.GOOS { case "darwin": return "dist_mac", "zip" case "linux": switch runtime.GOARCH { case "amd64": return "dist_linux", "tar.gz" case "arm": return "dist_linux_arm", "tar.gz" case "arm64": return "dist_linux_arm64", "tar.gz" default: return "", "" } default: return "", "" } } func downloadRelease(logger *log.Entry, localBlobPath, downloadSource string, c BlobReaderConstructor) error { d, err := os.Create(localBlobPath) if err != nil { return err } client := http.Client{ Timeout: 13 * time.Second, } req, err := http.NewRequest("GET", downloadSource, nil) if err != nil { panic(err) } req.Header.Set(hdrUserAgent, fmt.Sprintf("DockerSlimApp/%s", vinfo.Current())) qs := url.Values{} qs.Add("cv", vinfo.Tag()) req.URL.RawQuery = qs.Encode() resp, err := client.Do(req) if err != nil { if resp != nil && resp.Body != nil { io.Copy(io.Discard, resp.Body) resp.Body.Close() } d.Close() os.Remove(localBlobPath) return err } if resp.StatusCode != http.StatusOK { logger.Debugf("downloadRelease: unexpected status code - %v", resp.StatusCode) io.Copy(io.Discard, resp.Body) resp.Body.Close() d.Close() os.Remove(localBlobPath) return errUnexpectedHTTPStatus } if resp.ContentLength == -1 { logger.Debug("downloadRelease: content length is unknown") } blobSize := int(resp.ContentLength) blobReader := c(blobSize, resp.Body) copied, err := io.Copy(d, blobReader) if err != nil { blobReader.Close() d.Close() os.Remove(localBlobPath) return err } logger.Debugf("downloadRelease: data size = %v (content length = %v)", copied, blobSize) if err := blobReader.Close(); err != nil { logger.Debugf("downloadRelease: error closing downloaded reader - %v", err) } d.Chmod(artifactsPerms) if err := d.Close(); err != nil { logger.Debugf("downloadRelease: error closing downloaded release package - %v", err) } return nil } func isGoodDownloadSource(logger *log.Entry, location string) bool { client := http.Client{ Timeout: 13 * time.Second, CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }, } req, err := http.NewRequest("HEAD", location, nil) if err != nil { panic(err) } req.Header.Set(hdrUserAgent, fmt.Sprintf("DockerSlimApp/%s", vinfo.Current())) resp, err := client.Do(req) if resp != nil && resp.Body != nil { defer func() { io.Copy(io.Discard, resp.Body) resp.Body.Close() }() } if err != nil { logger.Debugf("isGoodDownloadSource: error - %v", err) return false } if resp.StatusCode >= 200 && resp.StatusCode < 400 { return true } return false } func unpackRelease(logger *log.Entry, blobPath, releaseRootPath, blobNameBase string) error { unpackedBlobDir := filepath.Join(releaseRootPath, blobNameBase) commonBlobDir := filepath.Join(releaseRootPath, distDirName) if fsutil.DirExists(unpackedBlobDir) { //feature: not removing the unpacked directory if it's already there (todo: revisit the feature later) logger.Debug("unpackRelease: error - unpacked package blob dir already exists") return nil } //todo: should check if it exists before we download the release package... if fsutil.DirExists(commonBlobDir) { //feature: not removing the unpacked dist directory if it's already there (todo: revisit the feature later) logger.Debug("unpackRelease: error - release package dir already exists") return nil } file, err := os.Open(blobPath) if err != nil { return err } defer file.Close() destPath, err := unpackit.Unpack(file, releaseRootPath) if err != nil { return err } logger.Debugf("unpackRelease: unpacked package directory - %v", destPath) if err := os.Remove(blobPath); err != nil { logger.Debugf("unpackRelease: could not remove the release package blob - %v", err) } if err := os.Rename(unpackedBlobDir, commonBlobDir); err != nil { return err } return nil } func installRelease(logger *log.Entry, appRootPath, statePath, releaseRootPath string) error { newMasterAppPath := filepath.Join(releaseRootPath, distDirName, masterAppName) newSensorAppPath := filepath.Join(releaseRootPath, distDirName, sensorAppName) sensorAppPath := filepath.Join(appRootPath, sensorAppName) err := updateFile(logger, newSensorAppPath, sensorAppPath) if err != nil { return err } //will copy the sensor to the state dir if DS is installed in a bad non-shared location on Macs fsutil.PreparePostUpdateStateDir(statePath) err = updateFile(logger, newMasterAppPath, "") if err != nil { return err } return nil } func updateFile(logger *log.Entry, sourcePath, targetPath string) error { file, err := os.Open(sourcePath) if err != nil { return err } defer file.Close() options := update.Options{} if targetPath != "" { options.TargetPath = targetPath } err = update.Apply(file, options) if err != nil { if rerr := update.RollbackError(err); rerr != nil { logger.Debugf("updateFile(%s,%s): Failed to rollback from bad update: %v", sourcePath, targetPath, rerr) } } return err } type BlobReaderConstructor func(size int, rc io.ReadCloser) io.ReadCloser func newPassThroughReader(size int, rc io.ReadCloser) io.ReadCloser { return rc } func newProgressReader(size int, rc io.ReadCloser) io.ReadCloser { pr := progressReader{ rc: rc, size: size, progress: uiprogress.New(), } if pr.progress != nil { pr.progress.SetRefreshInterval(time.Millisecond * 100) pr.progress.Start() pr.bar = pr.progress.AddBar(pr.size).AppendCompleted().PrependElapsed() pr.bar.Width = 50 } return &pr } type progressReader struct { rc io.ReadCloser size int current int progress *uiprogress.Progress bar *uiprogress.Bar } func (pr *progressReader) Read(b []byte) (int, error) { count, err := pr.rc.Read(b) if err == nil { pr.current += count if pr.bar != nil { pr.bar.Set(pr.current) } } return count, err } func (pr *progressReader) Close() error { if pr.progress != nil { pr.progress.Stop() } io.Copy(io.Discard, pr.rc) return pr.rc.Close() } ================================================ FILE: pkg/app/master/version/version.go ================================================ package version import ( "bytes" "encoding/json" "fmt" "io" "net/http" "time" docker "github.com/fsouza/go-dockerclient" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app" //"github.com/slimtoolkit/slim/pkg/app/master/commands" "github.com/slimtoolkit/slim/pkg/system" "github.com/slimtoolkit/slim/pkg/util/fsutil" v "github.com/slimtoolkit/slim/pkg/version" ) const ( versionCheckEndpoint = "https://versions.api.dockerslim.com/check" jsonContentType = "application/json" versionAuthKey = "1JZg1RXvS6mZ0ANgf7p9PoYWQ9q.1JZg3zytWMmBVH50c0RvtBvVpq8" ) type ovars = app.OutVars type CheckVersionRequest struct { AppVersion string `json:"app_version"` } type CheckVersionInfo struct { Status string `json:"status"` Outdated bool `json:"outdated,omitempty"` Current string `json:"current,omitempty"` } // PrintCheckVersion shows if the current version is outdated func PrintCheckVersion( xc *app.ExecutionContext, printPrefix string, info *CheckVersionInfo) { if info != nil && info.Status == "success" && info.Outdated { msg := "Your version of SlimToolkit is out of date! Use `slim update` to get the latest version." if xc == nil { fmt.Printf("%s info=version status=OUTDATED local=%s current=%s\n", printPrefix, v.Tag(), info.Current) fmt.Printf("%s info=message message='%s'\n", printPrefix, msg) } else { xc.Out.Info("version", app.OutVars{ "status": "OUTDATED", "local": v.Tag(), "current": info.Current, }) xc.Out.Message(msg) } } } // GetCheckVersionVerdict returns the version status of the locally installed package func GetCheckVersionVerdict(info *CheckVersionInfo) string { if info != nil && info.Status == "success" { if info.Outdated { return fmt.Sprintf("your installed version is OUTDATED (local=%s current=%s)", v.Tag(), info.Current) } else { return "you have the latest version" } } return "version status information is not available" } // Print shows the master app version information func Print(xc *app.ExecutionContext, cmdNameParam string, logger *log.Entry, client *docker.Client, checkVersion, inContainer, isDSImage bool) { ovApp := ovars{ "cmd": cmdNameParam, "version": v.Current(), "container": inContainer, "dsimage": isDSImage, "location": fsutil.ExeDir(), } if checkVersion { vinfo := Check(inContainer, isDSImage) current := "unknown" if vinfo != nil && vinfo.Status == "success" { if vinfo.Outdated { ovApp["status"] = "OUTDATED" } current = vinfo.Current } ovApp["current"] = current ovApp["verdict"] = GetCheckVersionVerdict(vinfo) } xc.Out.Info("app", ovApp) hostInfo := system.GetSystemInfo() ovHost := ovars{ "cmd": cmdNameParam, "osname": hostInfo.Distro.DisplayName, "osbuild": hostInfo.OsBuild, "version": hostInfo.Version, "release": hostInfo.Release, "sysname": hostInfo.Sysname, } xc.Out.Info("host", ovHost) if client != nil { info, err := client.Info() if err != nil { xc.Out.Error("error getting docker info", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } ovDocker := ovars{ "cmd": cmdNameParam, "name": info.Name, "kernel.version": info.KernelVersion, "operating.system": info.OperatingSystem, "ostype": info.OSType, "server.version": info.ServerVersion, "architecture": info.Architecture, } xc.Out.Info("docker", ovDocker) ver, err := client.Version() if err != nil { xc.Out.Error("error getting docker client version", err.Error()) xc.Out.State("exited", ovars{ "exit.code": -1, }) xc.Exit(-1) } ovDockerClient := ovars{ "cmd": cmdNameParam, "api.version": ver.Get("ApiVersion"), "min.api.version": ver.Get("MinAPIVersion"), "build.time": ver.Get("BuildTime"), "git.commit": ver.Get("GitCommit"), } xc.Out.Info("dclient", ovDockerClient) } else { xc.Out.Info("no.docker.client", ovars{}) } } // Check checks the app version func Check(inContainer, isDSImage bool) *CheckVersionInfo { logger := log.WithFields(log.Fields{"app": "slim"}) client := http.Client{ Timeout: 13 * time.Second, } data := CheckVersionRequest{ AppVersion: v.Current(), } var b bytes.Buffer encoder := json.NewEncoder(&b) encoder.SetEscapeHTML(false) if err := encoder.Encode(&data); err != nil { logger.Debugf("Check - error encoding data => %v", err) return nil } req, err := http.NewRequest("POST", versionCheckEndpoint, &b) if err != nil { logger.Debugf("Check - error creating version check request => %v", err) return nil } hinfo := system.GetSystemInfo() req.Header.Set("User-Agent", fmt.Sprintf("DockerSlimApp/%v/%v/%v/%v", v.Current(), inContainer, isDSImage, hinfo.Distro.DisplayName)) req.Header.Set("Content-Type", jsonContentType) req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", versionAuthKey)) resp, err := client.Do(req) if resp != nil && resp.Body != nil { defer func() { io.Copy(io.Discard, resp.Body) resp.Body.Close() }() } if err != nil { logger.Debugf("Check - error checking version => %v", err) return nil } logger.Debug("version.Check: http status = ", resp.Status) if resp.StatusCode != http.StatusOK { return nil } var checkInfo CheckVersionInfo if err := json.NewDecoder(resp.Body).Decode(&checkInfo); err != nil { logger.Debugf("Check - error decoding response => %v", err) return nil } return &checkInfo } // CheckAsync checks the app version without blocking func CheckAsync(doCheckVersion, inContainer, isDSImage bool) <-chan *CheckVersionInfo { resultCh := make(chan *CheckVersionInfo, 1) if doCheckVersion { go func() { resultCh <- Check(inContainer, isDSImage) }() } else { close(resultCh) } return resultCh } // CheckAndPrintAsync check the app version and prints the results func CheckAndPrintAsync(printPrefix string, inContainer, isDSImage bool) { go func() { PrintCheckVersion(nil, printPrefix, Check(inContainer, isDSImage)) }() } ================================================ FILE: pkg/app/sensor/app.go ================================================ //go:build linux // +build linux package sensor import ( "bytes" "context" "encoding/json" "errors" "flag" "fmt" "os" "path/filepath" "time" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/sensor/artifact" "github.com/slimtoolkit/slim/pkg/app/sensor/controlled" "github.com/slimtoolkit/slim/pkg/app/sensor/execution" "github.com/slimtoolkit/slim/pkg/app/sensor/monitor" "github.com/slimtoolkit/slim/pkg/app/sensor/standalone" "github.com/slimtoolkit/slim/pkg/app/sensor/standalone/control" "github.com/slimtoolkit/slim/pkg/appbom" "github.com/slimtoolkit/slim/pkg/ipc/event" "github.com/slimtoolkit/slim/pkg/mondel" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/sysenv" "github.com/slimtoolkit/slim/pkg/system" "github.com/slimtoolkit/slim/pkg/util/errutil" "github.com/slimtoolkit/slim/pkg/version" ) const ( // Execution modes sensorModeControlled = "controlled" sensorModeStandalone = "standalone" // Flags getAppBomFlagUsage = "get sensor application BOM" getAppBomFlagDefault = false enableDebugFlagUsage = "enable debug logging" enableDebugFlagDefault = false logLevelFlagUsage = "set the logging level ('debug', 'info', 'warn', 'error', 'fatal', 'panic')" logLevelFlagDefault = "info" logFormatFlagUsage = "set the logging format ('text', or 'json')" logFormatFlagDefault = "text" logFileFlagUsage = "enable logging redirection to a file (allowing to keep sensor's output separate from the target app's output)" logFileFlagDefault = "" sensorModeFlagUsage = "set the sensor execution mode ('controlled' when sensor expect the driver 'slim' app to manipulate its lifecycle; or 'standalone' when sensor depends on nothing but the target app" sensorModeFlagDefault = sensorModeControlled commandsFileFlagUsage = "provide a JSONL-encoded file with one ore more sensor commands (standalone mode only)" commandsFileFlagDefault = "/opt/_slim/commands.json" lifecycleHookCommandFlagUsage = "set path to an executable that'll be invoked at various sensor lifecycle events (post-start, pre-shutdown, etc)" lifecycleHookCommandFlagDefault = "" // Should stopSignal and stopGracePeriod become StartMonitor // command's fields instead? Hypothetically, in a multi-command // monitoring run, these two params may have different values. stopSignalFlagUsage = "set the signal to stop the target app (and, eventually, the sensor)" stopSignalFlagDefault = "TERM" stopGracePeriodFlagUsage = "set the time to wait for the graceful termination of the target app (before sensor SIGKILL's it)" stopGracePeriodFlagDefault = 5 * time.Second artifactsDirFlagUsage = "output director for all sensor artifacts" artifactsDirFlagDefault = app.DefaultArtifactsDirPath enableMondelFlagUsage = "enable monitor data event logging" enableMondelFlagDefault = false ) var ( getAppBom *bool = flag.Bool("appbom", getAppBomFlagDefault, getAppBomFlagUsage) enableDebug *bool = flag.Bool("debug", enableDebugFlagDefault, enableDebugFlagUsage) logLevel *string = flag.String("log-level", logLevelFlagDefault, logLevelFlagUsage) logFormat *string = flag.String("log-format", logFormatFlagDefault, logFormatFlagUsage) logFile *string = flag.String("log-file", logFileFlagDefault, logFileFlagUsage) sensorMode *string = flag.String("mode", sensorModeFlagDefault, sensorModeFlagUsage) commandsFile *string = flag.String("command-file", commandsFileFlagDefault, commandsFileFlagUsage) artifactsDir *string = flag.String("artifacts-dir", artifactsDirFlagDefault, artifactsDirFlagUsage) lifecycleHookCommand *string = flag.String("lifecycle-hook", lifecycleHookCommandFlagDefault, lifecycleHookCommandFlagUsage) stopSignal *string = flag.String("stop-signal", stopSignalFlagDefault, stopSignalFlagUsage) stopGracePeriod *time.Duration = flag.Duration("stop-grace-period", stopGracePeriodFlagDefault, stopGracePeriodFlagUsage) enableMondel *bool = flag.Bool("mondel", enableMondelFlagDefault, enableMondelFlagUsage) errUnknownMode = errors.New("unknown sensor mode") ) func eventsFilePath() string { return filepath.Join(*artifactsDir, "events.json") } func init() { flag.BoolVar(getAppBom, "b", getAppBomFlagDefault, getAppBomFlagUsage) flag.BoolVar(enableDebug, "d", enableDebugFlagDefault, enableDebugFlagUsage) flag.StringVar(logLevel, "l", logLevelFlagDefault, logLevelFlagUsage) flag.StringVar(logFormat, "f", logFormatFlagDefault, logFormatFlagUsage) flag.StringVar(logFile, "o", logFileFlagDefault, logFileFlagUsage) flag.StringVar(sensorMode, "m", sensorModeFlagDefault, sensorModeFlagUsage) flag.StringVar(commandsFile, "c", commandsFileFlagDefault, commandsFileFlagUsage) flag.StringVar(artifactsDir, "e", artifactsDirFlagDefault, artifactsDirFlagUsage) flag.StringVar(lifecycleHookCommand, "a", lifecycleHookCommandFlagDefault, lifecycleHookCommandFlagUsage) flag.StringVar(stopSignal, "s", stopSignalFlagDefault, stopSignalFlagUsage) flag.DurationVar(stopGracePeriod, "w", stopGracePeriodFlagDefault, stopGracePeriodFlagUsage) flag.BoolVar(enableMondel, "n", enableMondelFlagDefault, enableMondelFlagUsage) } // Run starts the sensor app func Run() { flag.Parse() if *getAppBom { dumpAppBom() return } errutil.FailOn(configureLogger(*enableDebug, *logLevel, *logFormat, *logFile)) ctx := context.Background() if len(os.Args) > 1 && os.Args[1] == "control" { if err := runControlCommand(ctx); err != nil { fmt.Fprintln(os.Stderr, "Control command failed: "+err.Error()) os.Exit(1) } return } activeCaps, maxCaps, err := sysenv.Capabilities(0) errutil.WarnOn(err) sr := &report.SensorReport{ Version: version.Current(), Args: os.Args, } log.Infof("sensor: ver=%v", sr.Version) log.Debugf("sensor: args => %#v", sr.Args) log.Tracef("sensor: uid=%v euid=%v", os.Getuid(), os.Geteuid()) log.Tracef("sensor: privileged => %v", sysenv.IsPrivileged()) log.Tracef("sensor: active capabilities => %#v", activeCaps) log.Tracef("sensor: max capabilities => %#v", maxCaps) log.Tracef("sensor: sysinfo => %#v", system.GetSystemInfo()) log.Tracef("sensor: kernel flags => %#v", system.DefaultKernelFeatures.Raw) var artifactsExtra []string if len(*commandsFile) > 0 { artifactsExtra = append(artifactsExtra, *commandsFile) } if len(*logFile) > 0 { artifactsExtra = append(artifactsExtra, *logFile) } artifactor := artifact.NewProcessor(sr, *artifactsDir, artifactsExtra) exe, err := newExecution( ctx, *sensorMode, *commandsFile, eventsFilePath(), *lifecycleHookCommand, ) if err != nil { errutil.WarnOn(artifactor.Archive()) errutil.FailOn(err) // calls os.Exit(1) } mondelFile := filepath.Join(*artifactsDir, report.DefaultMonDelFileName) del := mondel.NewPublisher(ctx, *enableMondel, mondelFile) sen, err := newSensor(ctx, exe, *sensorMode, artifactor, del, *artifactsDir) if err != nil { exe.Close() errutil.WarnOn(artifactor.Archive()) errutil.FailOn(err) // calls os.Exit(1) } if err := sen.Run(); err != nil { log.WithError(err).Error("sensor: run finished with error") if errors.Is(err, monitor.ErrInsufficientPermissions) { log.Info("sensor: Instrumented containers require root and ALL capabilities enabled. Example: `docker run --user root --cap-add ALL app:v1-instrumented`") } } else { log.Info("sensor: run finished succesfully") } exe.Close() log.Info("sensor: exiting...") } func newExecution( ctx context.Context, mode string, commandsFile string, eventsFile string, lifecycleHookCommand string, ) (execution.Interface, error) { switch mode { case sensorModeControlled: return execution.NewControlled(ctx, lifecycleHookCommand) case sensorModeStandalone: return execution.NewStandalone( ctx, commandsFile, eventsFile, lifecycleHookCommand, ) } return nil, errUnknownMode } type sensor interface { Run() error } func newSensor( ctx context.Context, exe execution.Interface, mode string, artifactor artifact.Processor, del mondel.Publisher, artifactsDir string, ) (sensor, error) { workDir, err := os.Getwd() errutil.WarnOn(err) log.Debugf("sensor: cwd => %s", workDir) mountPoint := "/" log.Debugf("sensor: mount point => %s", mountPoint) switch mode { case sensorModeControlled: ctx, cancel := context.WithCancel(ctx) // To preserve the backward compatibility, don't forward // signals to the target app in the default (controlled) mode. startSystemSignalsMonitor(func() { cancel() time.Sleep(2 * time.Second) }) return controlled.NewSensor( ctx, exe, monitor.NewCompositeMonitor, del, artifactor, workDir, mountPoint, ), nil case sensorModeStandalone: return standalone.NewSensor( ctx, exe, monitor.NewCompositeMonitor, del, artifactor, workDir, mountPoint, signalFromString(*stopSignal), *stopGracePeriod, ), nil } exe.PubEvent(event.StartMonitorFailed, &event.StartMonitorFailedData{ Component: event.ComSensorConstructor, State: event.StateSensorTypeCreating, Context: map[string]string{ event.CtxSensorType: mode, }, Errors: []string{errUnknownMode.Error()}, }) return nil, errUnknownMode } func dumpAppBom() { info := appbom.Get() if info == nil { return } var out bytes.Buffer encoder := json.NewEncoder(&out) encoder.SetEscapeHTML(false) encoder.SetIndent(" ", " ") _ = encoder.Encode(info) fmt.Printf("%s\n", out.String()) } // sensor control func runControlCommand(ctx context.Context) error { if len(os.Args) < 3 { return errors.New("missing command") } cmd := control.Command(os.Args[2]) switch cmd { case control.StopTargetAppCommand: if err := control.ExecuteStopTargetAppCommand(ctx, *commandsFile); err != nil { return fmt.Errorf("error stopping target app: %w", err) } case control.WaitForEventCommand: if len(os.Args) < 4 { return errors.New("missing event name") } if err := control.ExecuteWaitEvenCommand(ctx, eventsFilePath(), event.Type(os.Args[3])); err != nil { return fmt.Errorf("error waiting for sensor event: %w", err) } default: return fmt.Errorf("unknown command: %s", cmd) } return nil } ================================================ FILE: pkg/app/sensor/app_test.go ================================================ //go:build e2e // +build e2e package sensor_test import ( "context" "strings" "syscall" "testing" "time" "github.com/google/uuid" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app/sensor/standalone/control" "github.com/slimtoolkit/slim/pkg/ipc/event" "github.com/slimtoolkit/slim/pkg/report" testsensor "github.com/slimtoolkit/slim/pkg/test/e2e/sensor" testutil "github.com/slimtoolkit/slim/pkg/test/util" ) const ( imageSimpleService = "docker.io/library/nginx:1.21" imageSimpleCLI = "docker.io/library/alpine:3.16.2" ) var ( sensorFullLifecycleSequence = []string{ "sensor: ver=", "sensor: creating monitors...", "sensor: starting monitors...", "sensor: run finished succesfully", } sensorLifecycleHookSequence = []string{ "sensor-post-start", "monitor-pre-start", "monitor-post-shutdown", "sensor-pre-shutdown", } ) func init() { log.SetLevel(log.DebugLevel) } func TestSimpleSensorRun_Controlled_CLI(t *testing.T) { runID := newTestRun(t) ctx := context.Background() sensor := testsensor.NewSensorOrFail(t, ctx, t.TempDir(), runID, imageSimpleCLI) defer sensor.Cleanup(t, ctx) sensor.StartControlledOrFail(t, ctx) sensor.SendStartCommandOrFail(t, ctx, testsensor.NewMonitorStartCommand( testsensor.WithSaneDefaults(), testsensor.WithAppNameArgs("cat", "/etc/alpine-release"), ), ) sensor.ExpectEvent(t, event.StartMonitorDone) time.Sleep(1 * time.Second) sensor.SendStopCommandOrFail(t, ctx) sensor.ExpectEvent(t, event.StopMonitorDone) sensor.ShutdownOrFail(t, ctx) sensor.WaitOrFail(t, ctx) sensor.AssertSensorLogsContain(t, ctx, sensorFullLifecycleSequence...) sensor.AssertTargetAppLogsContain(t, ctx, "3.16.2") sensor.DownloadArtifactsOrFail(t, ctx) sensor.AssertReportIncludesFiles(t, "/bin/cat", "/bin/busybox", "/etc/alpine-release") sensor.AssertReportNotIncludesFiles(t, "/bin/echo2", "/etc/resolve.conf") } func TestSimpleSensorRun_Controlled_Service(t *testing.T) { runID := newTestRun(t) ctx := context.Background() sensor := testsensor.NewSensorOrFail(t, ctx, t.TempDir(), runID, imageSimpleService) defer sensor.Cleanup(t, ctx) sensor.StartControlledOrFail(t, ctx) sensor.SendStartCommandOrFail(t, ctx) sensor.ExpectEvent(t, event.StartMonitorDone) time.Sleep(5 * time.Second) sensor.SendStopCommandOrFail(t, ctx) sensor.ExpectEvent(t, event.StopMonitorDone) sensor.ShutdownOrFail(t, ctx) sensor.WaitOrFail(t, ctx) sensor.AssertSensorLogsContain(t, ctx, sensorFullLifecycleSequence...) sensor.AssertTargetAppLogsContain(t, ctx, "nginx/1.21", "start worker processes", ) sensor.DownloadArtifactsOrFail(t, ctx) sensor.AssertReportIncludesFiles(t, "/bin/sh", "/etc/nginx/nginx.conf", "/etc/nginx/conf.d/default.conf", "/var/cache/nginx", "/var/run", // Here is an interesting one - in the controlled (default) mode, sensor doesn't // await the target process termination. Hence, no cleanup on the nginx side // happens, and the pid file remains in the report. "/run/nginx.pid", ) sensor.AssertReportNotIncludesFiles(t, "/bin/bash", "/bin/cat", "/etc/apt/sources.list", ) } func TestSimpleSensorRun_Standalone_CLI(t *testing.T) { runID := newTestRun(t) ctx := context.Background() sensor := testsensor.NewSensorOrFail(t, ctx, t.TempDir(), runID, imageSimpleCLI) defer sensor.Cleanup(t, ctx) sensor.StartStandaloneOrFail(t, ctx, []string{"cat", "/etc/alpine-release"}) sensor.WaitOrFail(t, ctx) sensor.AssertSensorLogsContain(t, ctx, sensorFullLifecycleSequence...) sensor.AssertTargetAppLogsContain(t, ctx, "3.16.2") sensor.DownloadArtifactsOrFail(t, ctx) sensor.AssertReportIncludesFiles(t, "/bin/cat", "/bin/busybox", "/etc/alpine-release") sensor.AssertReportNotIncludesFiles(t, "/bin/echo2", "/etc/resolve.conf") sensor.AssertSensorEventsFileContains(t, ctx, event.StartMonitorDone, event.StopMonitorDone, event.ShutdownSensorDone, ) } func TestSimpleSensorRun_Standalone_Service(t *testing.T) { runID := newTestRun(t) ctx := context.Background() sensor := testsensor.NewSensorOrFail(t, ctx, t.TempDir(), runID, imageSimpleService) defer sensor.Cleanup(t, ctx) sensor.StartStandaloneOrFail(t, ctx, nil) go testutil.Delayed(ctx, 5*time.Second, func() { sensor.SignalOrFail(t, ctx, syscall.SIGTERM) }) sensor.WaitOrFail(t, ctx) sensor.AssertSensorLogsContain(t, ctx, sensorFullLifecycleSequence...) sensor.AssertTargetAppLogsContain(t, ctx, "nginx/1.21", "start worker processes", "(SIGTERM) received from 1, exiting", ) sensor.DownloadArtifactsOrFail(t, ctx) sensor.AssertReportIncludesFiles(t, "/bin/sh", "/etc/nginx/nginx.conf", "/etc/nginx/conf.d/default.conf", "/var/cache/nginx", "/var/run", ) sensor.AssertReportNotIncludesFiles(t, "/bin/bash", "/bin/cat", "/etc/apt/sources.list", // Here is an interesting one - in the standalone mode sensor // tries gracefully terminate the target process by forwarding // it the StopSignal from it receives from the runtime. Nginx // exits and cleans up its pid file. "/run/nginx.pid", ) } func TestSensorLogsGoToFile(t *testing.T) { runID := newTestRun(t) ctx := context.Background() sensor := testsensor.NewSensorOrFail( t, ctx, t.TempDir(), runID, imageSimpleCLI, testsensor.WithSensorLogsToFile(), ) defer sensor.Cleanup(t, ctx) sensor.StartStandaloneOrFail(t, ctx, []string{"echo", "123456879"}) sensor.WaitOrFail(t, ctx) sensor.AssertTargetAppLogsEqualTo(t, ctx, "123456879") // When WithSensorLogsToFile() is used, sensor's logs become part of artifacts. sensor.DownloadArtifactsOrFail(t, ctx) sensor.AssertSensorLogsContain(t, ctx, sensorFullLifecycleSequence...) } func TestAppStdoutToFile(t *testing.T) { runID := newTestRun(t) ctx := context.Background() sensor := testsensor.NewSensorOrFail( t, ctx, t.TempDir(), runID, imageSimpleCLI, testsensor.WithSensorLogsToFile(), ) defer sensor.Cleanup(t, ctx) sensor.StartStandaloneOrFail( t, ctx, []string{"sh", "-c", "echo 123456789; echo 987654321 >&2"}, testsensor.NewMonitorStartCommand( testsensor.WithSaneDefaults(), testsensor.WithAppStdoutToFile(), ), ) sensor.WaitOrFail(t, ctx) sensor.DownloadArtifactsOrFail(t, ctx) sensor.AssertSensorLogsContain(t, ctx, sensorFullLifecycleSequence...) sensor.AssertTargetAppLogsContain(t, ctx, "123456789") sensor.AssertTargetAppLogsContain(t, ctx, "987654321") sensor.AssertTargetAppStdoutFileEqualsTo(t, ctx, "123456789") } func TestAppStderrToFile(t *testing.T) { runID := newTestRun(t) ctx := context.Background() sensor := testsensor.NewSensorOrFail( t, ctx, t.TempDir(), runID, imageSimpleCLI, testsensor.WithSensorLogsToFile(), ) defer sensor.Cleanup(t, ctx) sensor.StartStandaloneOrFail( t, ctx, []string{"sh", "-c", "echo 123456789; echo 987654321 >&2"}, testsensor.NewMonitorStartCommand( testsensor.WithSaneDefaults(), testsensor.WithAppStderrToFile(), ), ) sensor.WaitOrFail(t, ctx) sensor.DownloadArtifactsOrFail(t, ctx) sensor.AssertSensorLogsContain(t, ctx, sensorFullLifecycleSequence...) sensor.AssertTargetAppLogsContain(t, ctx, "123456789") sensor.AssertTargetAppLogsContain(t, ctx, "987654321") sensor.AssertTargetAppStderrFileEqualsTo(t, ctx, "987654321") } func TestAccessedButThenDeletedFilesShouldBeReported(t *testing.T) { runID := newTestRun(t) ctx := context.Background() t.Skip("Fix for the sensor's logic is required!") sensor := testsensor.NewSensorOrFail(t, ctx, t.TempDir(), runID, imageSimpleCLI) defer sensor.Cleanup(t, ctx) sensor.StartStandaloneOrFail(t, ctx, []string{ "sh", "-c", "cat /etc/alpine-release; rm /etc/alpine-release", }) sensor.WaitOrFail(t, ctx) sensor.AssertSensorLogsContain(t, ctx, sensorFullLifecycleSequence...) sensor.AssertTargetAppLogsContain(t, ctx, "3.16.2") sensor.DownloadArtifactsOrFail(t, ctx) sensor.AssertReportIncludesFiles(t, "/etc/alpine-release") } func TestPreservedPathsWorkWithFilesDeletedDuringProbing(t *testing.T) { runID := newTestRun(t) ctx := context.Background() t.Skip("Fix for the sensor's logic is required!") sensor := testsensor.NewSensorOrFail(t, ctx, t.TempDir(), runID, imageSimpleCLI) defer sensor.Cleanup(t, ctx) sensor.StartStandaloneOrFail(t, ctx, []string{"sh", "-c", "cat /etc/alpine-release; rm /etc/alpine-release"}, testsensor.NewMonitorStartCommand( testsensor.WithPreserves("/etc/alpine-release"), ), ) sensor.WaitOrFail(t, ctx) sensor.AssertSensorLogsContain(t, ctx, sensorFullLifecycleSequence...) sensor.AssertTargetAppLogsContain(t, ctx, "3.16.2") sensor.DownloadArtifactsOrFail(t, ctx) sensor.AssertReportIncludesFiles(t, "/etc/alpine-release") } func newTestRun(t *testing.T) string { runID := t.Name() + "-" + strings.SplitN(uuid.New().String(), "-", 2)[0] log.Debugf("New test run %s", runID) return runID } func TestLifecycleHook_Controlled_CLI(t *testing.T) { runID := newTestRun(t) ctx := context.Background() sensor := testsensor.NewSensorOrFail( t, ctx, t.TempDir(), runID, imageSimpleCLI, testsensor.WithSensorLifecycleHook("echo"), ) defer sensor.Cleanup(t, ctx) sensor.StartControlledOrFail(t, ctx) sensor.SendStartCommandOrFail(t, ctx, testsensor.NewMonitorStartCommand( testsensor.WithSaneDefaults(), testsensor.WithAppNameArgs("cat", "/etc/alpine-release"), ), ) time.Sleep(1 * time.Second) sensor.SendStopCommandOrFail(t, ctx) sensor.ShutdownOrFail(t, ctx) sensor.WaitOrFail(t, ctx) sensor.AssertTargetAppLogsContain(t, ctx, "3.16.2") sensor.AssertSensorLogsContain(t, ctx, sensorFullLifecycleSequence...) sensor.AssertSensorLogsContain(t, ctx, sensorLifecycleHookSequence...) } func TestLifecycleHook_Standalone_CLI(t *testing.T) { runID := newTestRun(t) ctx := context.Background() sensor := testsensor.NewSensorOrFail( t, ctx, t.TempDir(), runID, imageSimpleCLI, testsensor.WithSensorLifecycleHook("echo"), ) defer sensor.Cleanup(t, ctx) sensor.StartStandaloneOrFail(t, ctx, []string{"cat", "/etc/alpine-release"}) sensor.WaitOrFail(t, ctx) sensor.AssertTargetAppLogsContain(t, ctx, "3.16.2") sensor.AssertSensorLogsContain(t, ctx, sensorFullLifecycleSequence...) sensor.AssertSensorLogsContain(t, ctx, sensorLifecycleHookSequence...) } func TestRunTargetAsUser(t *testing.T) { runID := newTestRun(t) ctx := context.Background() sensor := testsensor.NewSensorOrFail(t, ctx, t.TempDir(), runID, imageSimpleCLI) defer sensor.Cleanup(t, ctx) sensor.StartStandaloneOrFail( t, ctx, []string{"whoami"}, testsensor.NewMonitorStartCommand( testsensor.WithAppUser("daemon"), ), ) sensor.WaitOrFail(t, ctx) sensor.AssertSensorLogsContain(t, ctx, sensorFullLifecycleSequence...) sensor.AssertTargetAppLogsContain(t, ctx, "daemon") } func TestTargetAppEnvVars(t *testing.T) { cases := []struct { image string user string home string }{ // nixery.dev/shell lacks the /etc/passwd file: all UIDs should end up with HOME=/ {image: "nixery.dev/shell", user: "0", home: "/"}, {image: "nixery.dev/shell", user: "65534", home: "/"}, // Alpine {image: imageSimpleCLI, home: "/root"}, {image: imageSimpleCLI, user: "root", home: "/root"}, {image: imageSimpleCLI, user: "0", home: "/root"}, {image: imageSimpleCLI, user: "nobody", home: "/"}, {image: imageSimpleCLI, user: "65534", home: "/"}, // nobody's UID {image: imageSimpleCLI, user: "bin", home: "/bin"}, {image: imageSimpleCLI, user: "1", home: "/bin"}, // bin's UID {image: imageSimpleCLI, user: "daemon", home: "/sbin"}, {image: imageSimpleCLI, user: "2", home: "/sbin"}, // daemon's UID {image: imageSimpleCLI, user: "nosuchuser", home: "/"}, {image: imageSimpleCLI, user: "14567", home: "/"}, // hopefully, no such UID // Nginx {image: imageSimpleService, user: "nginx", home: "/nonexistent"}, {image: imageSimpleService, user: "101", home: "/nonexistent"}, // nginx's UID {image: imageSimpleService, user: "nobody", home: "/"}, {image: imageSimpleService, user: "65534", home: "/"}, // nobody's UID {image: imageSimpleService, user: "daemon", home: "/usr/sbin"}, {image: imageSimpleService, user: "1", home: "/usr/sbin"}, // daemon's UID {image: imageSimpleService, user: "nosuchuser", home: "/"}, {image: imageSimpleService, user: "14567", home: "/"}, // hopefully, no such UID } for _, tcase := range cases { func() { runID := newTestRun(t) ctx := context.Background() sensor := testsensor.NewSensorOrFail(t, ctx, t.TempDir(), runID, tcase.image) defer sensor.Cleanup(t, ctx) var startOpts []testsensor.StartMonitorOpt if len(tcase.user) > 0 { startOpts = append(startOpts, testsensor.WithAppUser(tcase.user)) } sensor.StartStandaloneOrFail( t, ctx, []string{"env"}, testsensor.NewMonitorStartCommand(startOpts...), ) sensor.WaitOrFail(t, ctx) sensor.AssertSensorLogsContain(t, ctx, sensorFullLifecycleSequence...) sensor.AssertTargetAppLogsContain(t, ctx, "HOME="+tcase.home) }() } } func TestArchiveArtifacts_HappyPath(t *testing.T) { runID := newTestRun(t) ctx := context.Background() sensor := testsensor.NewSensorOrFail( t, ctx, t.TempDir(), runID, imageSimpleCLI, testsensor.WithSensorLogsToFile(), ) defer sensor.Cleanup(t, ctx) sensor.StartStandaloneOrFail( t, ctx, []string{"cat", "/etc/alpine-release"}, testsensor.NewMonitorStartCommand( testsensor.WithSaneDefaults(), testsensor.WithAppStdoutToFile(), testsensor.WithAppStderrToFile(), ), ) sensor.WaitOrFail(t, ctx) sensor.DownloadArtifactsOrFail(t, ctx) sensor.AssertArtifactsArchiveContains(t, ctx, report.DefaultContainerReportFileName, testsensor.EventsFileName, testsensor.CommandsFileName, testsensor.SensorLogFileName, testsensor.AppStdoutFileName, testsensor.AppStderrFileName, ) } func TestArchiveArtifacts_CustomLocation(t *testing.T) { runID := newTestRun(t) ctx := context.Background() sensor := testsensor.NewSensorOrFail( t, ctx, t.TempDir(), runID, imageSimpleCLI, testsensor.WithSensorLogsToFile(), testsensor.WithSensorArtifactsDir("/opt/not-dockerslim-at-all/files"), ) defer sensor.Cleanup(t, ctx) sensor.StartStandaloneOrFail( t, ctx, []string{"cat", "/etc/alpine-release"}, testsensor.NewMonitorStartCommand( testsensor.WithSaneDefaults(), testsensor.WithAppStdoutToFile(), testsensor.WithAppStderrToFile(), ), ) sensor.WaitOrFail(t, ctx) sensor.DownloadArtifactsOrFail(t, ctx) sensor.AssertArtifactsArchiveContains(t, ctx, report.DefaultContainerReportFileName, testsensor.EventsFileName, testsensor.CommandsFileName, testsensor.SensorLogFileName, testsensor.AppStdoutFileName, testsensor.AppStderrFileName, ) } func TestArchiveArtifacts_SensorFailure_NoCaps(t *testing.T) { runID := newTestRun(t) ctx := context.Background() sensor := testsensor.NewSensorOrFail( t, ctx, t.TempDir(), runID, imageSimpleCLI, testsensor.WithSensorLogsToFile(), testsensor.WithSensorCapabilities(), // Cancels out the default --cap-add=ALL. ) defer sensor.Cleanup(t, ctx) sensor.StartStandaloneOrFail( t, ctx, []string{"cat", "/etc/alpine-release"}, testsensor.NewMonitorStartCommand( testsensor.WithSaneDefaults(), testsensor.WithAppStdoutToFile(), testsensor.WithAppStderrToFile(), testsensor.WithAppStderrToFile(), ), ) sensor.WaitOrFail(t, ctx) sensor.DownloadArtifactsOrFail(t, ctx) sensor.AssertSensorLogsContain(t, ctx, []string{ "sensor: creating monitors...", "sensor: starting monitors...", "sensor: composite monitor - FAN failed to start running", // <-- failure! "sensor: run finished with error", }...) sensor.AssertArtifactsArchiveContains(t, ctx, testsensor.EventsFileName, testsensor.CommandsFileName, testsensor.SensorLogFileName, ) } func TestArchiveArtifacts_SensorFailure_NoRoot(t *testing.T) { // It's a fairly common failure scenario. t.Skip("Implement me!") } func TestStopSignal_ForceKill(t *testing.T) { runID := newTestRun(t) ctx := context.Background() wrongStopSignal := syscall.SIGUSR1 // This signal isn't going to make Nginx exit. sensor := testsensor.NewSensorOrFail( t, ctx, t.TempDir(), runID, imageSimpleService, testsensor.WithStopSignal(wrongStopSignal), // Emulate misconfiguration. ) defer sensor.Cleanup(t, ctx) sensor.StartStandaloneOrFail(t, ctx, nil) go testutil.Delayed(ctx, 5*time.Second, func() { // However, the sensor will terminate the target app // anyway, when the grace period after receiving the // stop signal is over. sensor.SignalOrFail(t, ctx, wrongStopSignal) }) sensor.WaitOrFail(t, ctx) sensor.AssertSensorLogsContain(t, ctx, sensorFullLifecycleSequence...) sensor.AssertTargetAppLogsContain(t, ctx, "nginx/1.21", "start worker processes", "sensor: stop signal was sent to target app - starting grace period", "sensor: grace timeout expired - SIGKILL goes to target app", ) sensor.DownloadArtifactsOrFail(t, ctx) sensor.AssertReportIncludesFiles(t, "/etc/nginx/nginx.conf", "/etc/nginx/conf.d/default.conf", "/var/cache/nginx", "/var/run", // Because the target app was terminated by SIGKILL, // the pid file is not cleaned up. "/run/nginx.pid", ) sensor.AssertReportNotIncludesFiles(t, "/bin/bash", "/bin/cat", "/etc/apt/sources.list", ) } func TestControlCommands_StopTargetApp(t *testing.T) { runID := newTestRun(t) ctx := context.Background() sensor := testsensor.NewSensorOrFail(t, ctx, t.TempDir(), runID, imageSimpleService) defer sensor.Cleanup(t, ctx) sensor.StartStandaloneOrFail(t, ctx, nil) go testutil.Delayed(ctx, 5*time.Second, func() { sensor.ExecuteControlCommandOrFail(t, ctx, control.StopTargetAppCommand) sensor.WaitForEventOrFail(t, ctx, event.StopMonitorDone) sensor.WaitForEventOrFail(t, ctx, event.ShutdownSensorDone) // In the real world, there might be some (long) time between // the stop command and the target app signalling - maybe // we need to simulate that here? sensor.SignalOrFail(t, ctx, syscall.SIGQUIT) }) sensor.WaitOrFail(t, ctx) sensor.AssertSensorLogsContain(t, ctx, sensorFullLifecycleSequence...) } func TestEnableMondel(t *testing.T) { runID := newTestRun(t) ctx := context.Background() sensor := testsensor.NewSensorOrFail( t, ctx, t.TempDir(), runID, imageSimpleService, testsensor.WithEnableMondel(), ) defer sensor.Cleanup(t, ctx) sensor.StartStandaloneOrFail(t, ctx, nil) go testutil.Delayed(ctx, 5*time.Second, func() { sensor.SignalOrFail(t, ctx, syscall.SIGTERM) }) sensor.WaitOrFail(t, ctx) sensor.AssertSensorLogsContain(t, ctx, sensorFullLifecycleSequence...) sensor.DownloadArtifactsOrFail(t, ctx) sensor.AssertMondelIncludesFiles(t, "/etc/nginx/nginx.conf", "/etc/nginx/conf.d/default.conf", // TODO: investigate why these files are not included in the mondel (but are in the creport). // "/bin/sh", // "/var/cache/nginx", // "/var/run", ) sensor.AssertMondelNotIncludesFiles(t, "/bin/bash", "/bin/cat", "/etc/apt/sources.list", // TODO: investigate why this file is included in the mondel (but not in the creport). // "/run/nginx.pid", ) // Uncomment when the mondel and creport file sets are synced. // sensor.AssertReportAndMondelFileListsMatch(t) } ================================================ FILE: pkg/app/sensor/artifact/artifact.go ================================================ //go:build linux // +build linux package artifact import ( "bytes" "crypto/sha1" "encoding/hex" "encoding/json" "fmt" "os" "os/exec" "path" "path/filepath" "sort" "strings" "syscall" "github.com/armon/go-radix" "github.com/bmatcuk/doublestar/v3" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/sensor/detector/binfile" "github.com/slimtoolkit/slim/pkg/app/sensor/inspector/sodeps" "github.com/slimtoolkit/slim/pkg/artifact" "github.com/slimtoolkit/slim/pkg/certdiscover" "github.com/slimtoolkit/slim/pkg/ipc/command" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/sysidentity" "github.com/slimtoolkit/slim/pkg/system" "github.com/slimtoolkit/slim/pkg/util/fsutil" ) const ( pidFileSuffix = ".pid" varRunDir = "/var/run/" fileTypeCmdName = "file" filesArchiveName = "files.tar" runArchiveName = "run.tar" preservedDirName = "preserved" ) //TODO: extract these app, framework and language specific login into separate packages // Nginx related consts const ( ngxBinName = "/nginx" ngxSubDir = "/nginx/" ngxCommonTemp = "/var/lib/nginx" ngxLogTemp = "/var/log/nginx" ngxCacheTemp = "/var/cache/nginx" ) // Ruby related consts const ( rbBinName = "/ruby" rbIrbBinName = "/irb" rbGemBinName = "/gem" rbBundleBinName = "/bundle" rbRbenvBinName = "/rbenv" rbSrcFileExt = ".rb" rbGemSpecExt = ".gemspec" rbGemsSubDir = "/gems/" rbGemfile = "Gemfile" rbGemfileLockFile = "Gemfile.lock" rbDefaultSpecSubDir = "/specifications/default/" rbSpecSubDir = "/specifications/" rgExtSibDir = "extensions" rbGemBuildFlag = "gem.build_complete" ) // Python related consts const ( pyBinName = "/python" py2BinName = "/python2" py3BinName = "/python3" pyPipBinName = "/pip" pyPip2BinName = "/pip2" pyPip3BinName = "/pip3" pyPoetryBinName = "/poetry" pyCondaBinName = "/conda" pyPipEnvBinName = "/pipenv" pyEasyInstallBinName = "/easy_install" pyPipxBinName = "/pipx" pyVirtEnvBinName = "/virtualenv" pySrcFileExt = ".py" pycExt = ".pyc" pyoExt = ".pyo" pycacheDir = "/__pycache__/" pycache = "__pycache__" pyReqsFile = "requirements.txt" pyPoetryProjectFile = "pyproject.toml" pyPipEnvProjectFile = "Pipfile" pyPipEnvLockFile = "Pipfile.lock" pyDistPkgDir = "/dist-packages/" pySitePkgDir = "/site-packages/" ) // Node.js related consts const ( nodeBinName = "/node" nodeNpmBinName = "/npm" nodeYarnBinName = "/yarn" nodePnpmBinName = "/pnpm" nodeRushBinName = "/rush" nodeLernaBinName = "/lerna" nodeSrcFileExt = ".js" nodePackageFile = "package.json" nodePackageLockFile = "package-lock.json" nodeNpmShrinkwrapFile = "npm-shrinkwrap.json" nodeYarnLockFile = "yarn.lock" nodePackageDirPath = "/node_modules/" nodePackageDirName = "node_modules" nodeNPMNodeGypPackage = "/npm/node_modules/node-gyp/package.json" nodeNPMNodeGypFile = "bin/node-gyp.js" ) // nuxt.js related consts const ( nuxtConfigFile = "nuxt.config.js" nuxtBuildDirKey = "buildDir" nuxtSrcDirKey = "srcDir" //defaults to rootDir nuxtDistDirKey = "dir" //in 'generate' nuxtDefaultDistDir = "dist" nuxtDefaultBuildDir = ".nuxt" nuxtStaticDir = "static" ) // next.js related consts const ( nextConfigFile = "next.config.js" nextConfigFileAlt = "next.config.mjs" nextDefaultBuildDir = ".next" nextDefaultBuildStandaloneDir = ".next/standalone" nextDefaultBuildStaticDir = ".next/static" nextStaticDir = "public" nextDefaultStaticSpaDir = "out" nextDefaultStaticSpaDirPath = "/out/_next/" ) type NodePackageConfigSimple struct { Name string `json:"name"` Version string `json:"version"` Dependencies map[string]string `json:"dependencies"` } type appStackInfo struct { language string //will be reusing language consts from certdiscover (todo: replace it later) codeFiles uint packageDirs map[string]struct{} } // later: each language pack will register its metadata files var appMetadataFiles = map[string]struct{}{ //python: pyReqsFile: {}, pyPoetryProjectFile: {}, pyPipEnvProjectFile: {}, pyPipEnvLockFile: {}, //ruby: rbGemfile: {}, rbGemfileLockFile: {}, //node: nodePackageFile: {}, nodePackageLockFile: {}, nodeNpmShrinkwrapFile: {}, nodeYarnLockFile: {}, nuxtConfigFile: {}, nextConfigFile: {}, nextConfigFileAlt: {}, } func isAppMetadataFile(filePath string) bool { target := filepath.Base(filePath) for name := range appMetadataFiles { if target == name { return true } } return false } var binDataReplace = []fsutil.ReplaceInfo{ { PathSuffix: "/node", Match: "node.js/v", Replace: "done,xu/v", }, } var appMetadataFileUpdate = map[string]fsutil.DataUpdaterFn{ nodePackageFile: nodePackageJSONVerUpdater, } func appMetadataFileUpdater(filePath string) error { target := filepath.Base(filePath) updater, found := appMetadataFileUpdate[target] if !found { log.Tracef("appMetadataFileUpdater - no updater") return nil } return fsutil.UpdateFileData(filePath, updater, true) } func nodePackageJSONVerUpdater(target string, data []byte) ([]byte, error) { var info map[string]interface{} err := json.Unmarshal(data, &info) if err != nil { return nil, err } version, ok := info["version"].(string) if !ok { log.Tracef("nodePackageJSONVerUpdater - no version field, return as-is") return data, nil } version = fmt.Sprintf("1%s", version) log.Tracef("nodePackageJSONVerUpdater(%s) - version='%v'->'%v')\n", target, info["version"], version) info["version"] = version var b bytes.Buffer enc := json.NewEncoder(&b) enc.SetEscapeHTML(false) enc.SetIndent(" ", " ") err = enc.Encode(info) if err != nil { return nil, fmt.Errorf("error encoding updated package data") } return b.Bytes(), nil } var fileTypeCmd string func init() { findFileTypeCmd() } func findFileTypeCmd() { fileTypeCmd, err := exec.LookPath(fileTypeCmdName) if err != nil { log.Debugf("findFileTypeCmd - cmd not found: %v", err) return } log.Debugf("findFileTypeCmd - cmd found: %v", fileTypeCmd) } // Needed mostly to be able to mock it in the sensor tests. type Processor interface { // Current location of the artifacts folder. ArtifactsDir() string // Enumerate all files under a given root (used later on to tell the files // that were created during probing and the existed files appart). GetCurrentPaths(root string, excludes []string) (map[string]struct{}, error) // Create the artifacts folder, preserve some files, etc. PrepareEnv(cmd *command.StartMonitor) error // Dump the creport and the files to the artifacts folder. Process( cmd *command.StartMonitor, mountPoint string, peReport *report.PeMonitorReport, fanReport *report.FanMonitorReport, ptReport *report.PtMonitorReport, ) error // Archives commands.json, creport.json, events.json, sensor.log, etc // to a tar ball. Archive() error } type processor struct { seReport *report.SensorReport artifactsDirName string // Extra files to put into the artifacts archive before exiting. artifactsExtra []string origPathMap map[string]struct{} } func NewProcessor(seReport *report.SensorReport, artifactsDirName string, artifactsExtra []string) Processor { return &processor{ seReport: seReport, artifactsDirName: artifactsDirName, artifactsExtra: artifactsExtra, } } func (a *processor) ArtifactsDir() string { return a.artifactsDirName } func (a *processor) GetCurrentPaths(root string, excludes []string) (map[string]struct{}, error) { logger := log.WithField("op", "processor.GetCurrentPaths") logger.Trace("call") defer logger.Trace("exit") pathMap := map[string]struct{}{} err := filepath.Walk(root, func(pth string, info os.FileInfo, err error) error { if strings.HasPrefix(pth, "/proc/") { logger.Debugf("skipping /proc file system objects... - '%s'", pth) return filepath.SkipDir } if strings.HasPrefix(pth, "/sys/") { logger.Debugf("skipping /sys file system objects... - '%s'", pth) return filepath.SkipDir } if strings.HasPrefix(pth, "/dev/") { logger.Debugf("skipping /dev file system objects... - '%s'", pth) return filepath.SkipDir } // Optimization: Exclude folders early on to prevent slow enumerat // Can help with mounting big folders from the host. // TODO: Combine this logic with the similar logic in findSymlinks(). for _, xpattern := range excludes { if match, _ := doublestar.Match(xpattern, pth); match { if info.Mode().IsDir() { return filepath.SkipDir } return nil } } if err != nil { logger.Debugf("skipping %s with error: %v", pth, err) return nil } if !(info.Mode().IsRegular() || (info.Mode()&os.ModeSymlink) != 0) { //need symlinks too return nil } pth, err = filepath.Abs(pth) if err != nil { return nil } if strings.HasPrefix(pth, "/proc/") || strings.HasPrefix(pth, "/sys/") || strings.HasPrefix(pth, "/dev/") { return nil } pathMap[pth] = struct{}{} return nil }) if err != nil { return nil, err } a.origPathMap = pathMap return pathMap, nil } func (a *processor) PrepareEnv(cmd *command.StartMonitor) error { logger := log.WithField("op", "processor.PrepareEnv") logger.Trace("call") defer logger.Trace("exit") dstRootPath := filepath.Join(a.artifactsDirName, app.ArtifactFilesDirName) logger.Debugf("prep file artifacts root dir - '%s'", dstRootPath) if err := os.MkdirAll(dstRootPath, 0777); err != nil { return err } if cmd != nil && len(cmd.Preserves) > 0 { logger.Debugf("preserving paths - %d", len(cmd.Preserves)) preservedDirPath := filepath.Join(a.artifactsDirName, preservedDirName) logger.Debugf("prep preserved artifacts root dir - '%s'", preservedDirPath) if err := os.MkdirAll(preservedDirPath, 0777); err != nil { return err } preservePaths := preparePaths(getKeys(cmd.Preserves)) logger.Debugf("preservePaths(%v): %+v", len(preservePaths), preservePaths) newPerms := getRecordsWithPerms(cmd.Preserves) logger.Debugf("newPerms(%v): %+v", len(newPerms), newPerms) for inPath, isDir := range preservePaths { if artifact.IsFilteredPath(inPath) { logger.Debugf("skipping filtered path [isDir=%v] %s", isDir, inPath) continue } dstPath := fmt.Sprintf("%s%s", preservedDirPath, inPath) logger.Debugf("[isDir=%v] %s", isDir, dstPath) if isDir { err, errs := fsutil.CopyDir(cmd.KeepPerms, inPath, dstPath, true, true, nil, nil, nil) if err != nil { logger.Debugf("fsutil.CopyDir(%v,%v) error: %v", inPath, dstPath, err) } if len(errs) > 0 { logger.Debugf("fsutil.CopyDir(%v,%v) copy errors: %+v", inPath, dstPath, errs) } } else { if err := fsutil.CopyFile(cmd.KeepPerms, inPath, dstPath, true); err != nil { logger.Debugf("fsutil.CopyFile(%v,%v) error: %v", inPath, dstPath, err) } } } for inPath, perms := range newPerms { dstPath := fmt.Sprintf("%s%s", preservedDirPath, inPath) if fsutil.Exists(dstPath) { if err := fsutil.SetAccess(dstPath, perms); err != nil { logger.Debugf("fsutil.SetAccess(%v,%v) error: %v", dstPath, perms, err) } } } } return nil } func (a *processor) Process( cmd *command.StartMonitor, mountPoint string, peReport *report.PeMonitorReport, fanReport *report.FanMonitorReport, ptReport *report.PtMonitorReport, ) error { //TODO: when peReport is available filter file events from fanReport logger := log.WithField("op", "processor.Process") logger.Trace("call") defer logger.Trace("exit") logger.Debug("processing data...") fileCount := 0 fileList := make([]string, 0, fileCount) for _, processFileMap := range fanReport.ProcessFiles { fileCount += len(processFileMap) for fpath := range processFileMap { fileList = append(fileList, fpath) } } logger.Debugf("len(fanReport.ProcessFiles)=%v / fileCount=%v", len(fanReport.ProcessFiles), fileCount) allFilesMap := findSymlinks(fileList, mountPoint, cmd.Excludes) return saveResults(a.origPathMap, a.artifactsDirName, cmd, allFilesMap, fanReport, ptReport, peReport, a.seReport) } func (a *processor) Archive() error { toArchive := map[string]struct{}{} for _, f := range a.artifactsExtra { if fsutil.Exists(f) { toArchive[f] = struct{}{} } } artifacts, err := os.ReadDir(a.artifactsDirName) if err != nil { return err } // We archive everything in the /opt/_slim/artifacts folder // except (potentially large data) `files` and `files.tar` entries. // and the monitor data event log // (which is used for local debugging or it should be streamed out of band) // In particular, this may include: // - creport.json // - events.json // - app_stdout.log // - app_stderr.log for _, f := range artifacts { if f.Name() != app.ArtifactFilesDirName && f.Name() != filesArchiveName && f.Name() != report.DefaultMonDelFileName { toArchive[filepath.Join(a.artifactsDirName, f.Name())] = struct{}{} } } var toArchiveList []string for name := range toArchive { toArchiveList = append(toArchiveList, name) } return fsutil.ArchiveFiles( filepath.Join(a.artifactsDirName, runArchiveName), toArchiveList, false, "") } func saveResults( origPathMap map[string]struct{}, artifactsDirName string, cmd *command.StartMonitor, fileNames map[string]*report.ArtifactProps, fanMonReport *report.FanMonitorReport, ptMonReport *report.PtMonitorReport, peReport *report.PeMonitorReport, seReport *report.SensorReport, ) error { log.Debugf("saveResults(%v,...)", len(fileNames)) artifactStore := newStore(origPathMap, artifactsDirName, fileNames, fanMonReport, ptMonReport, peReport, seReport, cmd) artifactStore.prepareArtifacts() artifactStore.saveArtifacts() artifactStore.enumerateArtifacts() //artifactStore.archiveArtifacts() //alternative way to xfer artifacts return artifactStore.saveReport() } // NOTE: // the 'store' is supposed to only store/save/copy the artifacts we identified, // but overtime a lot of artifact processing and post-processing logic // ended up there too (which belongs in the artifact 'processor'). // TODO: refactor 'processor' and 'store' to have the right logic in the right places type store struct { origPathMap map[string]struct{} storeLocation string fanMonReport *report.FanMonitorReport ptMonReport *report.PtMonitorReport peMonReport *report.PeMonitorReport seReport *report.SensorReport rawNames map[string]*report.ArtifactProps nameList []string resolve map[string]struct{} linkMap map[string]*report.ArtifactProps fileMap map[string]*report.ArtifactProps saFileMap map[string]*report.ArtifactProps cmd *command.StartMonitor appStacks map[string]*appStackInfo } func newStore( origPathMap map[string]struct{}, storeLocation string, rawNames map[string]*report.ArtifactProps, fanMonReport *report.FanMonitorReport, ptMonReport *report.PtMonitorReport, peMonReport *report.PeMonitorReport, seReport *report.SensorReport, cmd *command.StartMonitor) *store { store := &store{ origPathMap: origPathMap, storeLocation: storeLocation, fanMonReport: fanMonReport, ptMonReport: ptMonReport, peMonReport: peMonReport, seReport: seReport, rawNames: rawNames, nameList: make([]string, 0, len(rawNames)), resolve: map[string]struct{}{}, linkMap: map[string]*report.ArtifactProps{}, fileMap: map[string]*report.ArtifactProps{}, saFileMap: map[string]*report.ArtifactProps{}, cmd: cmd, appStacks: map[string]*appStackInfo{}, } return store } func (p *store) getArtifactFlags(artifactFileName string) map[string]bool { flags := map[string]bool{} for _, processFileMap := range p.fanMonReport.ProcessFiles { if finfo, ok := processFileMap[artifactFileName]; ok { if finfo.ReadCount > 0 { flags["R"] = true } if finfo.WriteCount > 0 { flags["W"] = true } if finfo.ExeCount > 0 { flags["X"] = true } } } if len(flags) < 1 { return nil } return flags } func (p *store) prepareArtifact(artifactFileName string) { srcLinkFileInfo, err := os.Lstat(artifactFileName) if err != nil { log.Debugf("prepareArtifact - artifact don't exist: %v (%v)", artifactFileName, os.IsNotExist(err)) return } p.nameList = append(p.nameList, artifactFileName) props := &report.ArtifactProps{ FilePath: artifactFileName, Mode: srcLinkFileInfo.Mode(), ModeText: srcLinkFileInfo.Mode().String(), FileSize: srcLinkFileInfo.Size(), } props.Flags = p.getArtifactFlags(artifactFileName) log.Tracef("prepareArtifact - file mode:%v", srcLinkFileInfo.Mode()) switch { case srcLinkFileInfo.Mode().IsRegular(): props.FileType = report.FileArtifactType props.Sha1Hash, _ = getFileHash(artifactFileName) if fileTypeCmd != "" { props.DataType, _ = getDataType(artifactFileName) } p.fileMap[artifactFileName] = props p.rawNames[artifactFileName] = props case (srcLinkFileInfo.Mode() & os.ModeSymlink) != 0: linkRef, err := os.Readlink(artifactFileName) if err != nil { log.Debugf("prepareArtifact - error getting reference for symlink (%v) -> %v", err, artifactFileName) return } props.FileType = report.SymlinkArtifactType props.LinkRef = linkRef //props.LinkRefAbs, err := filepath.Abs(linkRef) //if err != nil { // log.Warnf("prepareArtifact - error getting absolute path for symlink reference (%v) -> %v => %v", // err, artifactFileName, linkRef) //} //build absolute and evaluated symlink target paths var absLinkRef string if !filepath.IsAbs(linkRef) { linkDir := filepath.Dir(artifactFileName) fullLinkRef := filepath.Join(linkDir, linkRef) absLinkRef, err = filepath.Abs(fullLinkRef) if err != nil { log.Debugf("prepareArtifact - error getting absolute path for symlink ref (%v) -> %v => %v", err, artifactFileName, fullLinkRef) } } else { absLinkRef, err = filepath.Abs(linkRef) if err != nil { log.Debugf("prepareArtifact - error getting absolute path for symlink ref 2 (%v) -> %v => %v", err, artifactFileName, linkRef) } } if absLinkRef != "" { evalLinkRef, err := filepath.EvalSymlinks(absLinkRef) if err != nil { log.Debugf("prepareArtifact - error evaluating symlink (%v) -> %v => %v", err, artifactFileName, absLinkRef) } else { if evalLinkRef != absLinkRef { if _, ok := p.rawNames[evalLinkRef]; !ok { p.resolve[evalLinkRef] = struct{}{} } } } if _, ok := p.rawNames[absLinkRef]; !ok { p.resolve[absLinkRef] = struct{}{} } } p.linkMap[artifactFileName] = props p.rawNames[artifactFileName] = props case srcLinkFileInfo.Mode().IsDir(): log.Debugf("prepareArtifact - is a directory (shouldn't see it) - %v", artifactFileName) props.FileType = report.DirArtifactType p.rawNames[artifactFileName] = props default: log.Debugf("prepareArtifact - other type (shouldn't see it) - %v", artifactFileName) p.rawNames[artifactFileName] = props } } func (p *store) prepareArtifacts() { log.Debugf("p.prepareArtifacts() p.rawNames=%v", len(p.rawNames)) for artifactFileName := range p.rawNames { log.Debugf("prepareArtifacts - artifact => %v", artifactFileName) p.prepareArtifact(artifactFileName) } if p.ptMonReport.Enabled { log.Debug("prepareArtifacts - ptMonReport.Enabled") for artifactFileName, fsaInfo := range p.ptMonReport.FSActivity { artifactInfo, found := p.rawNames[artifactFileName] if found && artifactInfo != nil { artifactInfo.FSActivity = fsaInfo } else { log.Debugf("prepareArtifacts [%v] - fsa artifact => %v", found, artifactFileName) if found && artifactInfo == nil { log.Debugf("prepareArtifacts - fsa artifact (found, but no info) => %v", artifactFileName) } p.prepareArtifact(artifactFileName) artifactInfo, found := p.rawNames[artifactFileName] if found && artifactInfo != nil { artifactInfo.FSActivity = fsaInfo } else { log.Debugf("[warn] prepareArtifacts - fsa artifact - missing in rawNames => %v", artifactFileName) } //TMP: //fsa might include directories, which we'll need to copy (dir only) //but p.prepareArtifact() doesn't do anything with dirs for now } } } for artifactFileName := range p.fileMap { //TODO: conditionally detect binary files and their deps if binProps, _ := binfile.Detected(artifactFileName); binProps == nil || !binProps.IsBin { continue } binArtifacts, err := sodeps.AllDependencies(artifactFileName) if err != nil { if err == sodeps.ErrDepResolverNotFound { log.Debug("prepareArtifacts.binArtifacts[bsa] - no static bin dep resolver") } else { log.Debugf("prepareArtifacts.binArtifacts[bsa] - %v - error getting bin artifacts => %v\n", artifactFileName, err) } continue } for idx, bpath := range binArtifacts { if artifactFileName == bpath { continue } _, found := p.rawNames[bpath] if found { log.Debugf("prepareArtifacts.binArtifacts[bsa] - known file path (%s)", bpath) continue } bpathFileInfo, err := os.Lstat(bpath) if err != nil { log.Debugf("prepareArtifacts.binArtifacts[bsa] - artifact doesn't exist: %v (%v)", bpath, os.IsNotExist(err)) continue } bprops := &report.ArtifactProps{ FilePath: bpath, Mode: bpathFileInfo.Mode(), ModeText: bpathFileInfo.Mode().String(), FileSize: bpathFileInfo.Size(), } bprops.Flags = p.getArtifactFlags(bpath) fsType := report.UnknownArtifactTypeName switch { case bpathFileInfo.Mode().IsRegular(): fsType = report.FileArtifactTypeName p.rawNames[bpath] = bprops //use a separate file map, so we can save them last //in case we are dealing with intermediate symlinks //and to better track what bin deps are not covered by dynamic analysis p.saFileMap[bpath] = bprops case (bpathFileInfo.Mode() & os.ModeSymlink) != 0: fsType = report.SymlinkArtifactTypeName p.linkMap[bpath] = bprops p.rawNames[bpath] = bprops default: fsType = report.UnexpectedArtifactTypeName log.Debugf("prepareArtifacts.binArtifacts[bsa] - unexpected ft - %s", bpath) } log.Debugf("prepareArtifacts.binArtifacts[bsa] - bin artifact (%s) fsType=%s [%d]bdep=%s", artifactFileName, fsType, idx, bpath) } } p.resolveLinks() } func (p *store) resolveLinks() { //note: //the links should be resolved in findSymlinks, but //the current design needs to be improved to catch all symlinks //this is a backup to catch the root level symlinks files, err := os.ReadDir("/") if err != nil { log.Debug("resolveLinks - os.ReadDir error: ", err) return } for _, file := range files { fpath := fmt.Sprintf("/%s", file.Name()) log.Debugf("resolveLinks.files - fpath='%s'", fpath) if fpath == "/proc" || fpath == "/sys" || fpath == "/dev" { continue } fileInfo, err := os.Lstat(fpath) if err != nil { log.Debugf("resolveLinks.files - os.Lstat(%s) error: %v", fpath, err) continue } if fileInfo.Mode()&os.ModeSymlink == 0 { log.Debug("resolveLinks.files - skipping non-symlink") continue } linkRef, err := os.Readlink(fpath) if err != nil { log.Debugf("resolveLinks.files - os.Readlink(%s) error: %v", fpath, err) continue } var absLinkRef string if !filepath.IsAbs(linkRef) { linkDir := filepath.Dir(fpath) log.Debugf("resolveLinks.files - relative linkRef %v -> %v +/+ %v", fpath, linkDir, linkRef) fullLinkRef := filepath.Join(linkDir, linkRef) var err error absLinkRef, err = filepath.Abs(fullLinkRef) if err != nil { log.Debugf("resolveLinks.files - error getting absolute path for symlink ref (1) (%v) -> %v => %v", err, fpath, fullLinkRef) continue } } else { var err error absLinkRef, err = filepath.Abs(linkRef) if err != nil { log.Debugf("resolveLinks.files - error getting absolute path for symlink ref (2) (%v) -> %v => %v", err, fpath, linkRef) continue } } //todo: skip "/proc/..." references evalLinkRef, err := filepath.EvalSymlinks(absLinkRef) if err != nil { log.Debugf("resolveLinks.files - error evaluating symlink (%v) -> %v => %v", err, fpath, absLinkRef) } //detecting intermediate dir symlinks symlinkPrefix := fmt.Sprintf("%s/", fpath) absPrefix := fmt.Sprintf("%s/", absLinkRef) evalPrefix := fmt.Sprintf("%s/", evalLinkRef) for rawName := range p.rawNames { if strings.HasPrefix(rawName, symlinkPrefix) { if _, found := p.rawNames[fpath]; found { log.Debugf("resolveLinks.files - rawNames - known symlink: name=%s target=%s", fpath, symlinkPrefix) } else { p.rawNames[fpath] = nil log.Debugf("resolveLinks.files - added path symlink to p.rawNames (0) -> %v", fpath) p.prepareArtifact(fpath) } break } if strings.HasPrefix(rawName, absPrefix) { if _, found := p.rawNames[fpath]; found { log.Debugf("resolveLinks.files - rawNames - known symlink: name=%s target=%s", fpath, absPrefix) } else { p.rawNames[fpath] = nil log.Debugf("resolveLinks.files - added path symlink to p.rawNames (1) -> %v", fpath) p.prepareArtifact(fpath) } break } if evalLinkRef != "" && absPrefix != evalPrefix && strings.HasPrefix(rawName, evalPrefix) { if _, found := p.rawNames[fpath]; found { log.Debugf("resolveLinks.files - rawNames - known symlink: name=%s target=%s", fpath, evalPrefix) } else { p.rawNames[fpath] = nil log.Debugf("resolveLinks.files - added path symlink to p.rawNames (2) -> %v", fpath) p.prepareArtifact(fpath) } break } } } //note: resolve these extra symlinks after the root level symlinks for name := range p.resolve { log.Debug("resolveLinks - resolving: ", name) p.prepareArtifact(name) } } func preparePaths(pathList []string) map[string]bool { if len(pathList) < 1 { return nil } paths := map[string]bool{} for _, pathValue := range pathList { pathInfo, err := os.Stat(pathValue) if err != nil { log.WithError(err).Debug("preparePaths(): skipping path = ", pathValue) continue } if pathInfo.IsDir() { paths[pathValue] = true } else { paths[pathValue] = false } } return paths } func getKeys(m map[string]*fsutil.AccessInfo) []string { if len(m) == 0 { return nil } keys := make([]string, 0, len(m)) for k := range m { keys = append(keys, k) } return keys } func getRecordsWithPerms(m map[string]*fsutil.AccessInfo) map[string]*fsutil.AccessInfo { perms := map[string]*fsutil.AccessInfo{} for k, v := range m { if v != nil { perms[k] = v } } return perms } // copied from dockerimage.go func linkTargetToFullPath(fullPath, target string) string { if filepath.IsAbs(target) { return target } if target == "." { return "" } d := filepath.Dir(fullPath) return filepath.Clean(filepath.Join(d, target)) } func (p *store) saveWorkdir(excludePatterns []string) { if p.cmd.IncludeWorkdir == "" { return } if artifact.IsFilteredPath(p.cmd.IncludeWorkdir) { log.Debug("sensor.store.saveWorkdir(): skipping filtered workdir") return } if !fsutil.DirExists(p.cmd.IncludeWorkdir) { log.Debugf("sensor.store.saveWorkdir: workdir does not exist %s", p.cmd.IncludeWorkdir) return } dstPath := fmt.Sprintf("%s/files%s", p.storeLocation, p.cmd.IncludeWorkdir) if fsutil.Exists(dstPath) { log.Debug("sensor.store.saveWorkdir: workdir dst path already exists") //it's possible that some of the files in the work dir are already copied //the copy logic will improve when we copy the files separately //for now just copy the whole workdir } log.Debugf("sensor.store.saveWorkdir: workdir=%s", p.cmd.IncludeWorkdir) err, errs := fsutil.CopyDir(p.cmd.KeepPerms, p.cmd.IncludeWorkdir, dstPath, true, true, excludePatterns, nil, nil) if err != nil { log.Debugf("sensor.store.saveWorkdir: CopyDir(%v,%v) error: %v", p.cmd.IncludeWorkdir, dstPath, err) } if len(errs) > 0 { log.Debugf("sensor.store.saveWorkdir: CopyDir(%v,%v) copy errors: %+v", p.cmd.IncludeWorkdir, dstPath, errs) } //todo: //copy files separately and //apply 'workdir-exclude' patterns in addition to the global excludes (excludePatterns) //resolve symlinks } ///////////////////////////////////////////////////////// const ( ziDirOne = "/usr/lib/zoneinfo" ziDirTwo = "/usr/share/zoneinfo" ziDirThree = "/usr/share/zoneinfo-icu" ziEnv = "TZDIR" //TODO: lookup zoneinfo data path from TZDIR ziTimezone = "/etc/timezone" ziLocaltime = "/etc/localtime" ) var ziDirs = []string{ ziDirOne, ziDirTwo, ziDirThree, } var ziFiles = []string{ ziTimezone, ziLocaltime, } func (p *store) saveZoneInfo() { if !p.cmd.IncludeZoneInfo { return } log.Trace("sensor.store.saveZoneInfo") for _, fp := range ziFiles { if !fsutil.Exists(fp) { log.Debugf("sensor.store.saveZoneInfo: no target file '%s' (skipping...)", fp) continue } log.Tracef("sensor.store.saveZoneInfo: copy %s", fp) dstPath := fmt.Sprintf("%s/files%s", p.storeLocation, fp) if fsutil.Exists(dstPath) { log.Debugf("sensor.store.saveZoneInfo: already copied target file '%s' (skipping...)", dstPath) continue } if err := fsutil.CopyFile(p.cmd.KeepPerms, fp, dstPath, true); err != nil { log.Debugf("sensor.store.saveZoneInfo: fsutil.CopyFile(%v,%v) error - %v", fp, dstPath, err) } } for _, dp := range ziDirs { if !fsutil.DirExists(dp) { log.Debugf("sensor.store.saveZoneInfo: no target directory '%s' (skipping...)", dp) continue } log.Tracef("sensor.store.saveZoneInfo: copy dir %s", dp) dstPath := fmt.Sprintf("%s/files%s", p.storeLocation, dp) err, errs := fsutil.CopyDir(p.cmd.KeepPerms, dp, dstPath, true, true, nil, nil, nil) if err != nil { log.Debugf("sensor.store.saveZoneInfo: fsutil.CopyDir(%s,%s) error: %v", dp, dstPath, err) } if len(errs) > 0 { log.Debugf("sensor.store.saveZoneInfo: fsutil.CopyDir(%v,%v) copy errors: %+v", dp, dstPath, errs) } } } ///////////////////////////////////////////////////////// const ( sshUserSSHDir = ".ssh" sshUserSSHDirPat = "/.ssh/" sshEtc = "/etc/ssh" sshLibOpenSSH = "/usr/lib/openssh" sshDefaultExeDir = "/usr/bin" sshExeName = "ssh" sshAddExeName = "ssh-add" sshAgentExeName = "ssh-agent" sshKeygenExeName = "ssh-keygen" sshKeyscanExeName = "ssh-keyscan" sshArgv0ExeName = "ssh-argv0" sshCopyIDExeName = "ssh-copy-id" ) var sshConfigDirs = []string{ sshEtc, } var sshBinDirs = []string{ sshLibOpenSSH, } var sshExeNames = []string{ sshExeName, sshAddExeName, sshAgentExeName, sshKeygenExeName, sshKeyscanExeName, sshArgv0ExeName, sshCopyIDExeName, } func homeDirs() []string { dirMap := map[string]struct{}{} var done bool if fsutil.Exists(sysidentity.PasswdFilePath) { info, err := sysidentity.ReadPasswdFile(sysidentity.PasswdFilePath) if err != nil { log.Debugf("sensor.store.homeDirs: error processing passwd: %v", err) } else { for _, pr := range info.Records { if pr.NoLoginShell || pr.Home == "" { continue } dirMap[pr.Home] = struct{}{} } done = true } } if !done { // hacky way to get the home directories for users... rootDir := "/root" if !fsutil.DirExists(rootDir) { dirMap[rootDir] = struct{}{} } homeBaseDir := "/home" hdFiles, err := os.ReadDir(homeBaseDir) if err == nil { for _, file := range hdFiles { fullPath := filepath.Join(homeBaseDir, file.Name()) if fsutil.IsDir(fullPath) { dirMap[fullPath] = struct{}{} } } } else { log.Debugf("sensor.store.homeDirs: error enumerating %s: %v", homeBaseDir, err) } } var dirList []string for dp := range dirMap { dirList = append(dirList, dp) } return dirList } func (ref *store) saveSSHClient() { if !ref.cmd.IncludeSSHClient { return } log.Trace("sensor.store.saveSSHClient") configDirs := append([]string{}, sshConfigDirs...) // copy user config dirs for _, dir := range homeDirs() { dp := filepath.Join(dir, sshUserSSHDir) if !fsutil.DirExists(dp) { continue } configDirs = append(configDirs, dp) } // copy config dirs for _, dp := range configDirs { if !fsutil.DirExists(dp) { log.Debugf("sensor.store.saveSSHClient: no target directory '%s' (skipping...)", dp) continue } log.Tracef("sensor.store.saveSSHClient: copy dir %s", dp) dstPath := fmt.Sprintf("%s/files%s", ref.storeLocation, dp) err, errs := fsutil.CopyDir(ref.cmd.KeepPerms, dp, dstPath, true, true, nil, nil, nil) if err != nil { log.Debugf("sensor.store.saveSSHClient: fsutil.CopyDir(%s,%s) error: %v", dp, dstPath, err) } if len(errs) > 0 { log.Debugf("sensor.store.saveSSHClient: fsutil.CopyDir(%v,%v) copy errors: %+v", dp, dstPath, errs) } } // locate/resolve exes to full bin paths allDepsMap := map[string]struct{}{} for _, name := range sshExeNames { exePath, err := exec.LookPath(name) if err != nil { log.Debugf("sensor.store.saveSSHClient - checking '%s' exe (not found: %s)", name, err) exePath = filepath.Join(sshDefaultExeDir, name) } if !fsutil.Exists(exePath) { log.Debugf("sensor.store.saveSSHClient - exe bin file not found - '%s' (skipping)", exePath) continue } artifacts, err := sodeps.AllDependencies(exePath) if err != nil { log.Debugf("sensor.store.saveSSHClient - %s - error getting bin artifacts => %v", exePath, err) // still add the bin path itself even if we had problems locating its deps allDepsMap[exePath] = struct{}{} continue } // artifacts includes exePath for _, an := range artifacts { allDepsMap[an] = struct{}{} } } // copy bin dirs and identify bin deps for _, dp := range sshBinDirs { if !fsutil.DirExists(dp) { log.Debugf("sensor.store.saveSSHClient: no target directory '%s' (skipping...)", dp) continue } log.Tracef("sensor.store.saveSSHClient: copy dir %s", dp) dstPath := fmt.Sprintf("%s/files%s", ref.storeLocation, dp) err, errs := fsutil.CopyDir(ref.cmd.KeepPerms, dp, dstPath, true, true, nil, nil, nil) if err != nil { log.Debugf("sensor.store.saveSSHClient: fsutil.CopyDir(%s,%s) error: %v", dp, dstPath, err) } if len(errs) > 0 { log.Debugf("sensor.store.saveSSHClient: fsutil.CopyDir(%v,%v) copy errors: %+v", dp, dstPath, errs) } dirFiles := map[string]struct{}{} err = filepath.Walk(dp, func(p string, info os.FileInfo, err error) error { if err != nil { log.Debugf("sensor.store.saveSSHClient: [bin dir path - %s] skipping %s with error: %v", dp, p, err) return nil } p, err = filepath.Abs(p) if err != nil { return nil } dirFiles[p] = struct{}{} return nil }) if err != nil { log.Debugf("sensor.store.saveSSHClient: error enumerating %s: %v", dp, err) } for fp := range dirFiles { if !fsutil.Exists(fp) { log.Debugf("sensor.store.saveSSHClient - bin dir (%s) file not found - '%s' (skipping)", dp, fp) continue } if binProps, _ := binfile.Detected(fp); binProps != nil && binProps.IsBin { binArtifacts, err := sodeps.AllDependencies(fp) if err != nil { // still add the bin path itself even if we had problems locating its deps allDepsMap[fp] = struct{}{} continue } for _, bpath := range binArtifacts { bfpaths, err := resloveLink(bpath) if err != nil { log.Debugf("sensor.store.saveSSHClient: error resolving link - %s (%v)", bpath, err) // still add the path... allDepsMap[bpath] = struct{}{} continue } for _, bfp := range bfpaths { if bfp == "" { continue } if !fsutil.Exists(bfp) { continue } allDepsMap[bfp] = struct{}{} } } } else { allDepsMap[fp] = struct{}{} } } } // copy bin files and their deps log.Tracef("sensor.store.saveSSHClient: - paths.len(%d) = %+v", len(allDepsMap), allDepsMap) for fp := range allDepsMap { if !fsutil.Exists(fp) { continue } dstPath := fmt.Sprintf("%s/files%s", ref.storeLocation, fp) if fsutil.Exists(dstPath) { continue } if err := fsutil.CopyFile(ref.cmd.KeepPerms, fp, dstPath, true); err != nil { log.Debugf("sensor.store.saveSSHClient: fsutil.CopyFile(%v,%v) error - %v", fp, dstPath, err) } } } const ( osLibDir = "/lib/" osUsrLibDir = "/usr/lib/" osUsrLib64Dir = "/usr/lib64/" osLibNssDns = "/libnss_dns" osLibNssResolv = "/libresolv" osLibNssFiles = "/libnss_files" osLibSO = ".so" osLibResolveConf = "/etc/resolv.conf" osLibNsswitchConf = "/etc/nsswitch.conf" osLibHostConf = "/etc/host.conf" ) var osLibsNetFiles = []string{ osLibResolveConf, osLibNsswitchConf, osLibHostConf, } func (p *store) saveOSLibsNetwork() { if !p.cmd.IncludeOSLibsNet { return } log.Trace("sensor.store.saveOSLibsNetwork") for _, fp := range osLibsNetFiles { if !fsutil.Exists(fp) { continue } log.Debugf("sensor.store.saveOSLibsNetwork: copy %s", fp) dstPath := fmt.Sprintf("%s/files%s", p.storeLocation, fp) if fsutil.Exists(dstPath) { continue } if err := fsutil.CopyFile(p.cmd.KeepPerms, fp, dstPath, true); err != nil { log.Debugf("sensor.store.saveOSLibsNetwork: fsutil.CopyFile(%v,%v) error - %v", fp, dstPath, err) } } if len(p.origPathMap) == 0 { log.Debug("sensor.store.saveOSLibsNetwork: no origPathMap") return } pathMap := map[string]struct{}{} for fileName := range p.origPathMap { if (strings.Contains(fileName, osLibNssDns) || strings.Contains(fileName, osLibNssResolv) || strings.Contains(fileName, osLibNssFiles)) && (strings.Contains(fileName, osLibDir) || strings.Contains(fileName, osUsrLibDir) || strings.Contains(fileName, osUsrLib64Dir)) && strings.Contains(fileName, osLibSO) { log.Debugf("sensor.store.saveOSLibsNetwork: match - %s", fileName) pathMap[fileName] = struct{}{} } } allPathMap := map[string]struct{}{} for fpath := range pathMap { if !fsutil.Exists(fpath) { continue } fpaths, err := resloveLink(fpath) if err != nil { log.Debugf("sensor.store.saveOSLibsNetwork: error resolving link - %s", fpath) continue } fpaths = append(fpaths, fpath) for _, fp := range fpaths { if fp == "" { continue } if !fsutil.Exists(fp) { continue } allPathMap[fp] = struct{}{} if binProps, _ := binfile.Detected(fp); binProps != nil && binProps.IsBin { binArtifacts, err := sodeps.AllDependencies(fp) if err != nil { if err == sodeps.ErrDepResolverNotFound { log.Debug("sensor.store.saveOSLibsNetwork[bsa] - no static bin dep resolver") } else { log.Debugf("sensor.store.saveOSLibsNetwork[bsa] - %v - error getting bin artifacts => %v\n", fp, err) } continue } for _, bpath := range binArtifacts { bfpaths, err := resloveLink(bpath) if err != nil { log.Debugf("sensor.store.saveOSLibsNetwork: error resolving link - %s", bpath) continue } for _, bfp := range bfpaths { if bfp == "" { continue } if !fsutil.Exists(bfp) { continue } allPathMap[bfp] = struct{}{} } } } } } log.Debugf("sensor.store.saveOSLibsNetwork: - allPathMap(%v) = %+v", len(allPathMap), allPathMap) for fp := range allPathMap { if !fsutil.Exists(fp) { continue } dstPath := fmt.Sprintf("%s/files%s", p.storeLocation, fp) if fsutil.Exists(dstPath) { continue } if err := fsutil.CopyFile(p.cmd.KeepPerms, fp, dstPath, true); err != nil { log.Debugf("sensor.store.saveOSLibsNetwork: fsutil.CopyFile(%v,%v) error - %v", fp, dstPath, err) } } } func resloveLink(fpath string) ([]string, error) { finfo, err := os.Lstat(fpath) if err != nil { return nil, err } if finfo.Mode()&os.ModeSymlink == 0 { return nil, nil } linkRef, err := os.Readlink(fpath) if err != nil { return nil, err } var out []string var target string if filepath.IsAbs(linkRef) { target = linkRef } else { linkDir := filepath.Dir(fpath) fullLinkRef := filepath.Clean(filepath.Join(linkDir, linkRef)) if fullLinkRef != "." { target = fullLinkRef } } if target != "" { out = append(out, target) if evalLinkRef, err := filepath.EvalSymlinks(target); err == nil { if evalLinkRef != target { out = append(out, evalLinkRef) } } } return out, nil } func (p *store) saveCertsData() { copyCertFiles := func(list []string) { log.Debugf("sensor.store.saveCertsData.copyCertFiles(list=%+v)", list) for _, fname := range list { if fsutil.Exists(fname) { dstPath := fmt.Sprintf("%s/files%s", p.storeLocation, fname) if err := fsutil.CopyFile(p.cmd.KeepPerms, fname, dstPath, true); err != nil { log.Debugf("sensor.store.saveCertsData.copyCertFiles: fsutil.CopyFile(%v,%v) error - %v", fname, dstPath, err) } } } } copyDirs := func(list []string, copyLinkTargets bool) { log.Debugf("sensor.store.saveCertsData.copyDirs(list=%+v,copyLinkTargets=%v)", list, copyLinkTargets) for _, fname := range list { if fsutil.Exists(fname) { dstPath := fmt.Sprintf("%s/files%s", p.storeLocation, fname) if fsutil.IsDir(fname) { err, errs := fsutil.CopyDir(p.cmd.KeepPerms, fname, dstPath, true, true, nil, nil, nil) if err != nil { log.Debugf("sensor.store.saveCertsData.copyDirs: fsutil.CopyDir(%v,%v) error: %v", fname, dstPath, err) } else if copyLinkTargets { foList, err := os.ReadDir(fname) if err == nil { log.Debugf("sensor.store.saveCertsData.copyDirs(): dir=%v fcount=%v", fname, len(foList)) for _, fo := range foList { fullPath := filepath.Join(fname, fo.Name()) log.Debugf("sensor.store.saveCertsData.copyDirs(): dir=%v fullPath=%v", fname, fullPath) if fsutil.IsSymlink(fullPath) { linkRef, err := os.Readlink(fullPath) if err != nil { log.Debugf("sensor.store.saveCertsData.copyDirs: os.Readlink(%v) error - %v", fullPath, err) continue } log.Debugf("sensor.store.saveCertsData.copyDirs(): dir=%v fullPath=%v linkRef=%v", fname, fullPath, linkRef) if strings.Contains(linkRef, "/") { targetFilePath := linkTargetToFullPath(fullPath, linkRef) if targetFilePath != "" && fsutil.Exists(targetFilePath) { log.Debugf("sensor.store.saveCertsData.copyDirs(): dir=%v fullPath=%v linkRef=%v targetFilePath=%v", fname, fullPath, linkRef, targetFilePath) dstPath := fmt.Sprintf("%s/files%s", p.storeLocation, targetFilePath) if err := fsutil.CopyFile(p.cmd.KeepPerms, targetFilePath, dstPath, true); err != nil { log.Debugf("sensor.store.saveCertsData.copyDirs: fsutil.CopyFile(%v,%v) error - %v", targetFilePath, dstPath, err) } } else { log.Debugf("sensor.store.saveCertsData.copyDirs: targetFilePath does not exist - %v", targetFilePath) } } } } } else { log.Debugf("sensor.store.saveCertsData.copyDirs: os.ReadDir(%v) error - %v", fname, err) } } if len(errs) > 0 { log.Debugf("sensor.store.saveCertsData.copyDirs: fsutil.CopyDir(%v,%v) copy errors: %+v", fname, dstPath, errs) } } else if fsutil.IsSymlink(fname) { if err := fsutil.CopySymlinkFile(p.cmd.KeepPerms, fname, dstPath, true); err != nil { log.Debugf("sensor.store.saveCertsData.copyDirs: fsutil.CopySymlinkFile(%v,%v) error - %v", fname, dstPath, err) } } else { log.Debugf("store.saveCertsData.copyDir: unexpected obect type - %s", fname) } } } } copyAppCertFiles := func(suffix string, dirs []string, subdirPrefix string) { //NOTE: dirs end with "/" (need to revisit the formatting to make it consistent) log.Debugf("sensor.store.saveCertsData.copyAppCertFiles(suffix=%v,dirs=%+v,subdirPrefix=%v)", suffix, dirs, subdirPrefix) for _, dirName := range dirs { if subdirPrefix != "" { foList, err := os.ReadDir(dirName) if err != nil { log.Debugf("sensor.store.saveCertsData.copyAppCertFiles: os.ReadDir(%v) error - %v", dirName, err) continue } for _, fo := range foList { if strings.HasPrefix(fo.Name(), subdirPrefix) { dirName = fmt.Sprintf("%s%s/", dirName, fo.Name()) break } } } srcFilePath := fmt.Sprintf("%s%s", dirName, suffix) if fsutil.Exists(srcFilePath) { dstPath := fmt.Sprintf("%s/files%s", p.storeLocation, srcFilePath) if err := fsutil.CopyFile(p.cmd.KeepPerms, srcFilePath, dstPath, true); err != nil { log.Debugf("sensor.store.saveCertsData.copyAppCertFiles: fsutil.CopyFile(%v,%v) error - %v", srcFilePath, dstPath, err) } } } } setToList := func(in map[string]struct{}) []string { var out []string for k := range in { out = append(out, k) } return out } if p.cmd.IncludeCertAll { copyCertFiles(certdiscover.CertFileList()) copyCertFiles(certdiscover.CACertFileList()) //TODO: //need to 'walk' these directories detecting cert files //and only copying those files instead of copying all files copyDirs(certdiscover.CertDirList(), true) copyDirs(certdiscover.CACertDirList(), true) //shouldn't copy the extra dirs explicitly here //the actual cert files should be copied through links above copyDirs(certdiscover.CertExtraDirList(), false) for _, appStack := range p.appStacks { switch appStack.language { case certdiscover.LanguagePython: copyAppCertFiles(certdiscover.AppCertPathSuffixPython, setToList(appStack.packageDirs), "") case certdiscover.LanguageNode: copyAppCertFiles(certdiscover.AppCertPathSuffixNode, setToList(appStack.packageDirs), "") case certdiscover.LanguageRuby: //ruby needs the versioned package name too certifi-zzzzz/ copyAppCertFiles(certdiscover.AppCertPathSuffixRuby, setToList(appStack.packageDirs), certdiscover.AppCertPackageName) //case certdiscover.LanguageJava: } } } if !p.cmd.IncludeCertAll && p.cmd.IncludeCertBundles { copyCertFiles(certdiscover.CertFileList()) copyCertFiles(certdiscover.CACertFileList()) for _, appStack := range p.appStacks { switch appStack.language { case certdiscover.LanguagePython: copyAppCertFiles(certdiscover.AppCertPathSuffixPython, setToList(appStack.packageDirs), "") case certdiscover.LanguageNode: copyAppCertFiles(certdiscover.AppCertPathSuffixNode, setToList(appStack.packageDirs), "") case certdiscover.LanguageRuby: //ruby needs the versioned package name too certifi-zzzzz/ copyAppCertFiles(certdiscover.AppCertPathSuffixRuby, setToList(appStack.packageDirs), certdiscover.AppCertPackageName) //case certdiscover.LanguageJava: } } } if !p.cmd.IncludeCertAll && p.cmd.IncludeCertDirs { copyDirs(certdiscover.CertDirList(), true) copyDirs(certdiscover.CACertDirList(), true) copyDirs(certdiscover.CertExtraDirList(), false) } if p.cmd.IncludeCertPKAll { copyCertFiles(certdiscover.CACertPKFileList()) //TODO: //need to 'walk' these directories detecting cert PK files //and only copying those files instead of copying all files copyDirs(certdiscover.CertPKDirList(), true) copyDirs(certdiscover.CACertPKDirList(), true) } if !p.cmd.IncludeCertPKAll && p.cmd.IncludeCertPKDirs { copyDirs(certdiscover.CertPKDirList(), true) copyDirs(certdiscover.CACertPKDirList(), true) } } func (p *store) saveArtifacts() { var includePaths map[string]bool var includeDirBinsList map[string]bool var newPerms map[string]*fsutil.AccessInfo syscall.Umask(0) excludePatterns := p.cmd.Excludes excludePatterns = append(excludePatterns, "/opt/_slim") excludePatterns = append(excludePatterns, "/opt/_slim/**") if p.cmd.ExcludeVarLockFiles { excludePatterns = append(excludePatterns, "/var/lock/**") excludePatterns = append(excludePatterns, "/run/lock/**") } log.Debugf("saveArtifacts - excludePatterns(%v): %+v", len(excludePatterns), excludePatterns) includePaths = preparePaths(getKeys(p.cmd.Includes)) log.Debugf("saveArtifacts - includePaths(%v): %+v", len(includePaths), includePaths) if includePaths == nil { includePaths = map[string]bool{} } includeDirBinsList = preparePaths(getKeys(p.cmd.IncludeDirBinsList)) log.Debugf("saveArtifacts - includeDirBinsList(%d): %+v", len(includeDirBinsList), includeDirBinsList) if includeDirBinsList == nil { includeDirBinsList = map[string]bool{} } newPerms = getRecordsWithPerms(p.cmd.Includes) log.Debugf("saveArtifacts - newPerms(%v): %+v", len(newPerms), newPerms) for pk, pv := range p.cmd.Perms { newPerms[pk] = pv } log.Debugf("saveArtifacts - merged newPerms(%v): %+v", len(newPerms), newPerms) //moved to prepareEnv //dstRootPath := filepath.Join(p.storeLocation, app.ArtifactFilesDirName) //log.Debugf("saveArtifacts - prep file artifacts root dir - '%s'", dstRootPath) //err := os.MkdirAll(dstRootPath, 0777) //errutil.FailOn(err) extraDirs := map[string]struct{}{} symlinkFailed := map[string]*report.ArtifactProps{} log.Debugf("saveArtifacts - copy links (%v)", len(p.linkMap)) //copyLinks: //NOTE: MUST copy the links FIRST, so the dir symlinks get created before their files are copied symlinkMap := radix.New() for linkName, linkProps := range p.linkMap { symlinkMap.Insert(linkName, linkProps) } symlinkWalk := func(linkName string, val interface{}) bool { linkProps, ok := val.(*report.ArtifactProps) if !ok { log.Debugf("saveArtifacts.symlinkWalk: could not convert data - %s\n", linkName) return false } for _, xpattern := range excludePatterns { found, err := doublestar.Match(xpattern, linkName) if err != nil { log.Debugf("saveArtifacts.symlinkWalk - copy links - [%v] excludePatterns Match error - %v\n", linkName, err) //should only happen when the pattern is malformed return false } if found { log.Debugf("saveArtifacts.symlinkWalk - copy links - [%v] - excluding (%s) ", linkName, xpattern) return false } } //TODO: review linkPath := fmt.Sprintf("%s/files%s", p.storeLocation, linkName) linkDir := fsutil.FileDir(linkPath) //NOTE: //The symlink target dir might not exist, which means //the dir create calls that start with the current symlink prefix will fail. //We'll save the failed links to try again //later when the symlink target is already created. //Another option is to create the symlink targets, //but it might be tricky if the target is a symlink (potentially to another symlink, etc) //log.Debugf("saveArtifacts.symlinkWalk - saving symlink - create subdir: linkName=%s linkDir=%s linkPath=%s", linkName, linkDir, linkPath) err := os.MkdirAll(linkDir, 0777) if err != nil { log.Debugf("saveArtifacts.symlinkWalk - dir error (linkName=%s linkDir=%s linkPath=%s) => error=%v", linkName, linkDir, linkPath, err) //save it and try again later symlinkFailed[linkName] = linkProps return false } if linkProps != nil && linkProps.FSActivity != nil && linkProps.FSActivity.OpsCheckFile > 0 { log.Debug("saveArtifacts.symlinkWalk - saving 'checked' symlink => ", linkName) } //log.Debugf("saveArtifacts.symlinkWalk - saving symlink: name=%s target=%s", linkName, linkProps.LinkRef) err = os.Symlink(linkProps.LinkRef, linkPath) if err != nil { if os.IsExist(err) { log.Debug("saveArtifacts.symlinkWalk - symlink already exists") } else { log.Debugf("saveArtifacts.symlinkWalk - symlink create error: %v", err) } } return false } symlinkMap.Walk(symlinkWalk) for linkName, linkProps := range symlinkFailed { linkPath := fmt.Sprintf("%s/files%s", p.storeLocation, linkName) linkDir := fsutil.FileDir(linkPath) //log.Debugf("saveArtifacts.symlinkFailed - saving symlink - create subdir: linkName=%s linkDir=%s linkPath=%s", linkName, linkDir, linkPath) err := os.MkdirAll(linkDir, 0777) if err != nil { log.Debugf("saveArtifacts.symlinkFailed - dir error (linkName=%s linkDir=%s linkPath=%s) => error=%v", linkName, linkDir, linkPath, err) continue } if linkProps != nil && linkProps.FSActivity != nil && linkProps.FSActivity.OpsCheckFile > 0 { log.Debug("saveArtifacts.symlinkFailed - saving 'checked' symlink => ", linkName) } //log.Debugf("saveArtifacts.symlinkFailed - saving symlink: name=%s target=%s", linkName, linkProps.LinkRef) err = os.Symlink(linkProps.LinkRef, linkPath) if err != nil { if os.IsExist(err) { log.Debug("saveArtifacts.symlinkFailed - symlink already exists") } else { log.Debugf("saveArtifacts.symlinkFailed - symlink create error ==> %v", err) } } } //NOTE: need to copy the files after the links are copied log.Debugf("saveArtifacts - copy files (%v) and copy additional files checked at runtime...", len(p.fileMap)) ngxEnsured := false copyFiles: for srcFileName, artifactInfo := range p.fileMap { //need to make sure we don't filter out something we need if artifact.IsFilteredPath(srcFileName) { log.Debugf("saveArtifacts - skipping filtered copy file - %s", srcFileName) continue } for _, xpattern := range excludePatterns { found, err := doublestar.Match(xpattern, srcFileName) if err != nil { log.Debugf("saveArtifacts - copy files - [%v] excludePatterns Match error - %v\n", srcFileName, err) //should only happen when the pattern is malformed continue } if found { log.Debugf("saveArtifacts - copy files - [%v] - excluding (%s) ", srcFileName, xpattern) continue copyFiles } } //filter out pid files (todo: have a flag to enable/disable these capabilities) if isKnownPidFilePath(srcFileName) { log.Debugf("saveArtifacts - copy files - skipping known pid file (%v)", srcFileName) extraDirs[fsutil.FileDir(srcFileName)] = struct{}{} continue } if hasPidFileSuffix(srcFileName) { log.Debugf("saveArtifacts - copy files - skipping a pid file (%v)", srcFileName) extraDirs[fsutil.FileDir(srcFileName)] = struct{}{} continue } filePath := fmt.Sprintf("%s/files%s", p.storeLocation, srcFileName) log.Debug("saveArtifacts - saving file data => ", filePath) if artifactInfo != nil && artifactInfo.FSActivity != nil && artifactInfo.FSActivity.OpsCheckFile > 0 { log.Debugf("saveArtifacts - saving 'checked' file => %v", srcFileName) //NOTE: later have an option to save 'checked' only files without data } if p.cmd.ObfuscateMetadata { if isAppMetadataFile(srcFileName) { log.Tracef("saveArtifacts - isAppMetadataFile - src(%s)->dst(%s)", srcFileName, filePath) err := fsutil.CopyAndObfuscateFile(p.cmd.KeepPerms, srcFileName, filePath, true) if err != nil { log.Debugf("saveArtifacts [%s,%s] - error saving file => %v", srcFileName, filePath, err) } if err := appMetadataFileUpdater(filePath); err != nil { log.Debugf("saveArtifacts [%s,%s] - appMetadataFileUpdater => not updated / err = %v", srcFileName, filePath, err) } } else { err := fsutil.CopyRegularFile(p.cmd.KeepPerms, srcFileName, filePath, true) if err != nil { log.Debugf("saveArtifacts [%s,%s] - error saving file => %v", srcFileName, filePath, err) } else { //NOTE: this covers the main file set (doesn't cover the extra includes) binProps, err := binfile.Detected(filePath) if err == nil && binProps != nil && binProps.IsBin && binProps.IsExe { if err := fsutil.AppendToFile(filePath, []byte("KCQ"), true); err != nil { log.Debugf("saveArtifacts [%s,%s] - fsutil.AppendToFile error => %v", srcFileName, filePath, err) } else { log.Tracef("saveArtifacts - binfile.Detected[IsExe]/fsutil.AppendToFile - %s", filePath) err := fsutil.ReplaceFileData(filePath, binDataReplace, true) if err != nil { log.Debugf("saveArtifacts [%s,%s] - fsutil.ReplaceFileData error => %v", srcFileName, filePath, err) } } } } } } else { err := fsutil.CopyRegularFile(p.cmd.KeepPerms, srcFileName, filePath, true) if err != nil { log.Debugf("saveArtifacts - error saving file => %v", err) } } /////////////////// fileName := srcFileName p.detectAppStack(fileName) if p.cmd.IncludeAppNuxtDir || p.cmd.IncludeAppNuxtBuildDir || p.cmd.IncludeAppNuxtDistDir || p.cmd.IncludeAppNuxtStaticDir || p.cmd.IncludeAppNuxtNodeModulesDir { if isNuxtConfigFile(fileName) { nuxtConfig, err := getNuxtConfig(fileName) if err != nil { log.Debugf("saveArtifacts: failed to get nuxt config: %v", err) continue } if nuxtConfig == nil { log.Debugf("saveArtifacts: nuxt config not found: %v", fileName) continue } //note: //Nuxt config file is usually in the app directory, but not always //cust app path is defined with the "srcDir" field in the Nuxt config file nuxtAppDir := filepath.Dir(fileName) nuxtAppDirPrefix := fmt.Sprintf("%s/", nuxtAppDir) if p.cmd.IncludeAppNuxtDir { includePaths[nuxtAppDir] = true log.Tracef("saveArtifacts[nuxt] - including app dir - %s", nuxtAppDir) } if p.cmd.IncludeAppNuxtStaticDir { srcPath := filepath.Join(nuxtAppDir, nuxtStaticDir) if fsutil.DirExists(srcPath) { if p.cmd.IncludeAppNuxtDir && strings.HasPrefix(srcPath, nuxtAppDirPrefix) { log.Debugf("saveArtifacts[nuxt] - static dir is already included (%s)", srcPath) } else { includePaths[srcPath] = true log.Tracef("saveArtifacts[nuxt] - including static dir - %s", srcPath) } } else { log.Debugf("saveArtifacts[nuxt] - static dir does not exists (%s)", srcPath) } } if p.cmd.IncludeAppNuxtBuildDir && nuxtConfig.Build != "" { basePath := nuxtAppDir if strings.HasPrefix(nuxtConfig.Build, "/") { basePath = "" } srcPath := filepath.Join(basePath, nuxtConfig.Build) if fsutil.DirExists(srcPath) { if p.cmd.IncludeAppNuxtDir && strings.HasPrefix(srcPath, nuxtAppDirPrefix) { log.Debugf("saveArtifacts[nuxt] - build dir is already included (%s)", srcPath) } else { includePaths[srcPath] = true log.Tracef("saveArtifacts[nuxt] - including build dir - %s", srcPath) } } else { log.Debugf("saveArtifacts[nuxt] - build dir does not exists (%s)", srcPath) } } if p.cmd.IncludeAppNuxtDistDir && nuxtConfig.Dist != "" { basePath := nuxtAppDir if strings.HasPrefix(nuxtConfig.Dist, "/") { basePath = "" } srcPath := filepath.Join(basePath, nuxtConfig.Dist) if fsutil.DirExists(srcPath) { if p.cmd.IncludeAppNuxtDir && strings.HasPrefix(srcPath, nuxtAppDirPrefix) { log.Debugf("saveArtifacts[nuxt] - dist dir is already included (%s)", srcPath) } else { includePaths[srcPath] = true log.Tracef("saveArtifacts[nuxt] - including dist dir - %s", srcPath) } } else { log.Debugf("saveArtifacts[nuxt] - dist dir does not exists (%s)", srcPath) } } if p.cmd.IncludeAppNuxtNodeModulesDir { srcPath := filepath.Join(nuxtAppDir, nodePackageDirName) if fsutil.DirExists(srcPath) { if p.cmd.IncludeAppNuxtDir && strings.HasPrefix(srcPath, nuxtAppDirPrefix) { log.Debugf("saveArtifacts[nuxt] - node_modules dir is already included (%s)", srcPath) } else { includePaths[srcPath] = true log.Tracef("saveArtifacts[nuxt] - including node_modules dir - %s", srcPath) } } else { log.Debugf("saveArtifacts[nuxt] - node_modules dir does not exists (%s)", srcPath) } } continue } } if p.cmd.IncludeAppNextDir || p.cmd.IncludeAppNextBuildDir || p.cmd.IncludeAppNextDistDir || p.cmd.IncludeAppNextStaticDir || p.cmd.IncludeAppNextNodeModulesDir { if isNextConfigFile(fileName) { nextAppDir := filepath.Dir(fileName) nextAppDirPrefix := fmt.Sprintf("%s/", nextAppDir) if p.cmd.IncludeAppNextDir { includePaths[nextAppDir] = true log.Tracef("saveArtifacts[next] - including app dir - %s", nextAppDir) } if p.cmd.IncludeAppNextStaticDir { srcPath := filepath.Join(nextAppDir, nextStaticDir) if fsutil.DirExists(srcPath) { if p.cmd.IncludeAppNextDir && strings.HasPrefix(srcPath, nextAppDirPrefix) { log.Debugf("saveArtifacts[next] - static public dir is already included (%s)", srcPath) } else { includePaths[srcPath] = true log.Tracef("saveArtifacts[next] - including static public dir - %s", srcPath) } } else { log.Debugf("saveArtifacts[next] - static public dir does not exists (%s)", srcPath) } } if p.cmd.IncludeAppNextBuildDir { srcPath := filepath.Join(nextAppDir, nextDefaultBuildDir) if fsutil.DirExists(srcPath) { if p.cmd.IncludeAppNextDir && strings.HasPrefix(srcPath, nextAppDirPrefix) { log.Debugf("saveArtifacts[next] - build dir is already included (%s)", srcPath) } else { includePaths[srcPath] = true log.Tracef("saveArtifacts[next] - including build dir - %s", srcPath) } } else { log.Debugf("saveArtifacts[next] - build dir does not exists (%s)", srcPath) } } if p.cmd.IncludeAppNextDistDir { srcPath := filepath.Join(nextAppDir, nextDefaultStaticSpaDir) if fsutil.DirExists(srcPath) { if p.cmd.IncludeAppNextDir && strings.HasPrefix(srcPath, nextAppDirPrefix) { log.Debugf("saveArtifacts[next] - dist dir is already included (%s)", srcPath) } else { includePaths[srcPath] = true log.Tracef("saveArtifacts[next] - including dist dir - %s", srcPath) } } else { log.Debugf("saveArtifacts[next] - dist dir does not exists (%s)", srcPath) } } if p.cmd.IncludeAppNextNodeModulesDir { srcPath := filepath.Join(nextAppDir, nodePackageDirName) if fsutil.DirExists(srcPath) { if p.cmd.IncludeAppNextDir && strings.HasPrefix(srcPath, nextAppDirPrefix) { log.Debugf("saveArtifacts[next] - node_modules dir is already included (%s)", srcPath) } else { includePaths[srcPath] = true log.Tracef("saveArtifacts[next] - including node_modules dir - %s", srcPath) } } else { log.Debugf("saveArtifacts[next] - node_modules dir does not exists (%s)", srcPath) } } continue } } if isRbGemSpecFile(fileName) { log.Debug("saveArtifacts - processing ruby gem spec ==>", fileName) err := rbEnsureGemFiles(fileName, p.storeLocation, "/files") if err != nil { log.Debugf("saveArtifacts - error ensuring ruby gem files => %v", err) } } else if isNodePackageFile(fileName) { log.Debug("saveArtifacts - processing node package file ==>", fileName) err := nodeEnsurePackageFiles(p.cmd.KeepPerms, fileName, p.storeLocation, "/files") if err != nil { log.Debugf("saveArtifacts - error ensuring node package files => %v", err) } if len(p.cmd.IncludeNodePackages) > 0 { nodePackageInfo, err := getNodePackageFileData(fileName) if err == nil && nodePackageInfo != nil { for _, pkgName := range p.cmd.IncludeNodePackages { //note: use a better match lookup and include package version match later (":" as separator) if pkgName != "" && pkgName == nodePackageInfo.Name { nodeAppDir := filepath.Dir(fileName) includePaths[nodeAppDir] = true log.Tracef("saveArtifacts[node] - including app(%s) dir - %s", nodePackageInfo.Name, nodeAppDir) break } } } else { log.Debugf("saveArtifacts - error getting node package config file => %v", err) } } } else if isNgxArtifact(fileName) && !ngxEnsured { log.Debug("saveArtifacts - ensuring ngx artifacts....") ngxEnsure(p.storeLocation) ngxEnsured = true } else { err := fixPy3CacheFile(fileName, filePath) if err != nil { log.Debugf("saveArtifacts - error fixing py3 cache file => %v", err) } } /////////////////// } log.Debugf("saveArtifacts[bsa] - copy files (%v)", len(p.saFileMap)) copyBsaFiles: for srcFileName := range p.saFileMap { for _, xpattern := range excludePatterns { found, err := doublestar.Match(xpattern, srcFileName) if err != nil { log.Debugf("saveArtifacts[bsa] - copy files - [%v] excludePatterns Match error - %v\n", srcFileName, err) //should only happen when the pattern is malformed continue } if found { log.Debugf("saveArtifacts[bsa] - copy files - [%v] - excluding (%s) ", srcFileName, xpattern) continue copyBsaFiles } } dstFilePath := fmt.Sprintf("%s/files%s", p.storeLocation, srcFileName) log.Debug("saveArtifacts[bsa] - saving file data => ", dstFilePath) if fsutil.Exists(dstFilePath) { //we might already have the target file //when we have intermediate symlinks in the path log.Debugf("saveArtifacts[bsa] - target file already exists (%s)", dstFilePath) } else { if p.cmd.ObfuscateMetadata && isAppMetadataFile(srcFileName) { err := fsutil.CopyAndObfuscateFile(p.cmd.KeepPerms, srcFileName, dstFilePath, true) if err != nil { log.Debugf("saveArtifacts[bsa] - error saving file => %v", err) } else { log.Debugf("saveArtifacts[bsa] - saved file (%s)", dstFilePath) } } else { err := fsutil.CopyRegularFile(p.cmd.KeepPerms, srcFileName, dstFilePath, true) if err != nil { log.Debugf("saveArtifacts[bsa] - error saving file => %v", err) } else { log.Debugf("saveArtifacts[bsa] - saved file (%s)", dstFilePath) } } } } //was conditional: if p.cmd.AppUser != "" //NOTE: //we may need the user info even if the caller didn't explicitly indicated it //makes this conditional again when/if we can fully analyze the target app(s) //to understand if it really needs the user info from the system copyBasicUserInfo := func() { //always copy the '/etc/passwd' file when we have a user //later: do it only when AppUser is a name (not UID) dstPasswdFilePath := fmt.Sprintf("%s/files%s", p.storeLocation, sysidentity.PasswdFilePath) if _, err := os.Stat(sysidentity.PasswdFilePath); err == nil { //if err := cpFile(passwdFilePath, passwdFileTargetPath); err != nil { if err := fsutil.CopyRegularFile(p.cmd.KeepPerms, sysidentity.PasswdFilePath, dstPasswdFilePath, true); err != nil { log.Debugf("sensor: monitor - error copying user info file => %v", err) } } else { if os.IsNotExist(err) { log.Debug("sensor: monitor - no user info file") } else { log.Debug("sensor: monitor - could not save user info file =>", err) } } } copyBasicUserInfo() copyIncludes: for inPath, isDir := range includePaths { if artifact.IsFilteredPath(inPath) { log.Debugf("saveArtifacts - skipping filtered include path [isDir=%v] %s", isDir, inPath) continue } for _, xpattern := range excludePatterns { found, err := doublestar.Match(xpattern, inPath) if err != nil { log.Debugf("saveArtifacts - copy includes - [%v] excludePatterns Match error - %v\n", inPath, err) //should only happen when the pattern is malformed continue } if found { log.Debugf("saveArtifacts - copy includes - [%v] - excluding (%s) ", inPath, xpattern) continue copyIncludes } } dstPath := fmt.Sprintf("%s/files%s", p.storeLocation, inPath) if isDir { err, errs := fsutil.CopyDir(p.cmd.KeepPerms, inPath, dstPath, true, true, excludePatterns, nil, nil) if err != nil { log.Debugf("CopyDir(%v,%v) error: %v", inPath, dstPath, err) } if len(errs) > 0 { log.Debugf("CopyDir(%v,%v) copy errors: %+v", inPath, dstPath, errs) } } else { if err := fsutil.CopyFile(p.cmd.KeepPerms, inPath, dstPath, true); err != nil { log.Debugf("CopyFile(%v,%v) error: %v", inPath, dstPath, err) } } } for _, exePath := range p.cmd.IncludeExes { exeArtifacts, err := sodeps.AllExeDependencies(exePath, true) if err != nil { log.Debugf("saveArtifacts - %v - error getting exe artifacts => %v", exePath, err) continue } log.Debugf("saveArtifacts - include exe [%s]: artifacts (%d):\n%v\n", exePath, len(exeArtifacts), strings.Join(exeArtifacts, "\n")) for _, apath := range exeArtifacts { dstPath := fmt.Sprintf("%s/files%s", p.storeLocation, apath) if err := fsutil.CopyFile(p.cmd.KeepPerms, apath, dstPath, true); err != nil { log.Debugf("CopyFile(%v,%v) error: %v", apath, dstPath, err) } } } binPathMap := map[string]struct{}{} for _, binPath := range p.cmd.IncludeBins { binPathMap[binPath] = struct{}{} } addExtraBinIncludes: for inPath, isDir := range includeDirBinsList { if !isDir { log.Debugf("saveArtifacts - skipping non-directory in includeDirBinsList - %s", inPath) continue } if artifact.IsFilteredPath(inPath) { log.Debugf("saveArtifacts - skipping filtered path in includeDirBinsList - %s", inPath) continue } for _, xpattern := range excludePatterns { found, err := doublestar.Match(xpattern, inPath) if err != nil { log.Debugf("saveArtifacts - includeDirBinsList - [%s] excludePatterns Match error - %v\n", inPath, err) //should only happen when the pattern is malformed continue } if found { log.Debugf("saveArtifacts - includeDirBinsList - [%s] - excluding (%s) ", inPath, xpattern) continue addExtraBinIncludes } } err := filepath.Walk(inPath, func(pth string, info os.FileInfo, err error) error { if strings.HasPrefix(pth, "/proc/") { log.Debugf("skipping /proc file system objects... - '%s'", pth) return filepath.SkipDir } if strings.HasPrefix(pth, "/sys/") { log.Debugf("skipping /sys file system objects... - '%s'", pth) return filepath.SkipDir } if strings.HasPrefix(pth, "/dev/") { log.Debugf("skipping /dev file system objects... - '%s'", pth) return filepath.SkipDir } // Optimization: Exclude folders early on to prevent slow enumerat // Can help with mounting big folders from the host. // TODO: Combine this logic with the similar logic in findSymlinks(). for _, xpattern := range excludePatterns { if match, _ := doublestar.Match(xpattern, pth); match { if info.Mode().IsDir() { return filepath.SkipDir } return nil } } if err != nil { log.Debugf("skipping %s with error: %v", pth, err) return nil } if !info.Mode().IsRegular() { return nil } pth, err = filepath.Abs(pth) if err != nil { return nil } if strings.HasPrefix(pth, "/proc/") || strings.HasPrefix(pth, "/sys/") || strings.HasPrefix(pth, "/dev/") { return nil } if binProps, _ := binfile.Detected(pth); binProps != nil && binProps.IsBin { binPathMap[pth] = struct{}{} } return nil }) if err != nil { log.Errorf("saveArtifacts - error enumerating includeDirBinsList dir (%s) - %v", inPath, err) } } copyBinIncludes: for binPath := range binPathMap { if artifact.IsFilteredPath(binPath) { log.Debugf("saveArtifacts - skipping filtered include bin - %s", binPath) continue } for _, xpattern := range excludePatterns { found, err := doublestar.Match(xpattern, binPath) if err != nil { log.Debugf("saveArtifacts - copy bin includes - [%v] excludePatterns Match error - %v\n", binPath, err) //should only happen when the pattern is malformed continue } if found { log.Debugf("saveArtifacts - copy bin includes - [%v] - excluding (%s) ", binPath, xpattern) continue copyBinIncludes } } binArtifacts, err := sodeps.AllDependencies(binPath) if err != nil { log.Debugf("saveArtifacts - %v - error getting bin artifacts => %v", binPath, err) continue } log.Debugf("saveArtifacts - include bin [%s]: artifacts (%d):\n%v", binPath, len(binArtifacts), strings.Join(binArtifacts, "\n")) for _, bpath := range binArtifacts { dstPath := fmt.Sprintf("%s/files%s", p.storeLocation, bpath) if err := fsutil.CopyFile(p.cmd.KeepPerms, bpath, dstPath, true); err != nil { log.Debugf("CopyFile(%v,%v) error: %v", bpath, dstPath, err) } } } if p.cmd.IncludeShell { shellArtifacts, err := shellDependencies() if err == nil { log.Debugf("saveArtifacts - include shell: artifacts (%d):\n%v\n", len(shellArtifacts), strings.Join(shellArtifacts, "\n")) for _, spath := range shellArtifacts { dstPath := fmt.Sprintf("%s/files%s", p.storeLocation, spath) if err := fsutil.CopyFile(p.cmd.KeepPerms, spath, dstPath, true); err != nil { log.Debugf("CopyFile(%v,%v) error: %v", spath, dstPath, err) } } } else { log.Debugf("saveArtifacts - error getting shell artifacts => %v", err) } } p.saveWorkdir(excludePatterns) p.saveSSHClient() p.saveOSLibsNetwork() p.saveCertsData() p.saveZoneInfo() if fsutil.DirExists("/tmp") { tdTargetPath := fmt.Sprintf("%s/files/tmp", p.storeLocation) if !fsutil.DirExists(tdTargetPath) { if err := os.MkdirAll(tdTargetPath, os.ModeSticky|os.ModeDir|0777); err != nil { log.Debugf("saveArtifacts - error creating tmp directory => %v", err) } } else { if err := os.Chmod(tdTargetPath, os.ModeSticky|os.ModeDir|0777); err != nil { log.Debugf("saveArtifacts - error setting tmp directory permission ==> %v", err) } } } if fsutil.DirExists("/run") { tdTargetPath := fmt.Sprintf("%s/files/run", p.storeLocation) if !fsutil.DirExists(tdTargetPath) { //should use perms from source if err := os.MkdirAll(tdTargetPath, 0755); err != nil { log.Debugf("saveArtifacts - error creating run directory => %v", err) } } } for extraDir := range extraDirs { tdTargetPath := fmt.Sprintf("%s/files%s", p.storeLocation, extraDir) if fsutil.DirExists(extraDir) && !fsutil.DirExists(tdTargetPath) { if err := fsutil.CopyDirOnly(p.cmd.KeepPerms, extraDir, tdTargetPath); err != nil { log.Debugf("CopyDirOnly(%v,%v) error: %v", extraDir, tdTargetPath, err) } } } for inPath, perms := range newPerms { dstPath := fmt.Sprintf("%s/files%s", p.storeLocation, inPath) if fsutil.Exists(dstPath) { if err := fsutil.SetAccess(dstPath, perms); err != nil { log.Debugf("SetPerms(%v,%v) error: %v", dstPath, perms, err) } } } if len(p.cmd.Preserves) > 0 { log.Debugf("saveArtifacts: restoring preserved paths - %d", len(p.cmd.Preserves)) preservedDirPath := filepath.Join(p.storeLocation, preservedDirName) if fsutil.Exists(preservedDirPath) { filesDirPath := filepath.Join(p.storeLocation, app.ArtifactFilesDirName) preservePaths := preparePaths(getKeys(p.cmd.Preserves)) for inPath, isDir := range preservePaths { if artifact.IsFilteredPath(inPath) { log.Debugf("saveArtifacts: skipping filtered preserved path [isDir=%v] %s", isDir, inPath) continue } srcPath := fmt.Sprintf("%s%s", preservedDirPath, inPath) dstPath := fmt.Sprintf("%s%s", filesDirPath, inPath) if isDir { err, errs := fsutil.CopyDir(p.cmd.KeepPerms, srcPath, dstPath, true, true, nil, nil, nil) if err != nil { log.Debugf("saveArtifacts.CopyDir(%v,%v) error: %v", srcPath, dstPath, err) } if len(errs) > 0 { log.Debugf("saveArtifacts.CopyDir(%v,%v) copy errors: %+v", srcPath, dstPath, errs) } } else { if err := fsutil.CopyFile(p.cmd.KeepPerms, srcPath, dstPath, true); err != nil { log.Debugf("saveArtifacts.CopyFile(%v,%v) error: %v", srcPath, dstPath, err) } } } } else { log.Debug("saveArtifacts(): preserved root path doesnt exist") } } } func (p *store) detectAppStack(fileName string) { isPython := detectPythonCodeFile(fileName) if isPython { appStack, ok := p.appStacks[certdiscover.LanguagePython] if !ok { appStack = &appStackInfo{ language: certdiscover.LanguagePython, packageDirs: map[string]struct{}{}, } p.appStacks[certdiscover.LanguagePython] = appStack } appStack.codeFiles++ } pyPkgDir := detectPythonPkgDir(fileName) if pyPkgDir != "" { appStack, ok := p.appStacks[certdiscover.LanguagePython] if !ok { appStack = &appStackInfo{ language: certdiscover.LanguagePython, packageDirs: map[string]struct{}{}, } p.appStacks[certdiscover.LanguagePython] = appStack } appStack.packageDirs[pyPkgDir] = struct{}{} } if isPython || pyPkgDir != "" { return } isRuby := detectRubyCodeFile(fileName) if isRuby { appStack, ok := p.appStacks[certdiscover.LanguageRuby] if !ok { appStack = &appStackInfo{ language: certdiscover.LanguageRuby, packageDirs: map[string]struct{}{}, } p.appStacks[certdiscover.LanguageRuby] = appStack } appStack.codeFiles++ } rbPkgDir := detectRubyPkgDir(fileName) if rbPkgDir != "" { appStack, ok := p.appStacks[certdiscover.LanguageRuby] if !ok { appStack = &appStackInfo{ language: certdiscover.LanguageRuby, packageDirs: map[string]struct{}{}, } p.appStacks[certdiscover.LanguageRuby] = appStack } appStack.packageDirs[rbPkgDir] = struct{}{} } if isRuby || rbPkgDir != "" { return } isNode := detectNodeCodeFile(fileName) if isNode { appStack, ok := p.appStacks[certdiscover.LanguageNode] if !ok { appStack = &appStackInfo{ language: certdiscover.LanguageNode, packageDirs: map[string]struct{}{}, } p.appStacks[certdiscover.LanguageNode] = appStack } appStack.codeFiles++ } nodePkgDir := detectNodePkgDir(fileName) if nodePkgDir != "" { appStack, ok := p.appStacks[certdiscover.LanguageNode] if !ok { appStack = &appStackInfo{ language: certdiscover.LanguageNode, packageDirs: map[string]struct{}{}, } p.appStacks[certdiscover.LanguageNode] = appStack } appStack.packageDirs[nodePkgDir] = struct{}{} } } func isFileExt(filePath, match string) bool { fileExt := filepath.Ext(filePath) return fileExt == match } func getPathElementPrefix(filePath, match string) string { if !strings.Contains(filePath, match) { return "" } parts := strings.Split(filePath, match) if len(parts) > 0 { return parts[0] } return "" } func getPathElementPrefixLast(filePath, match string) string { if !strings.Contains(filePath, match) { return "" } if idx := strings.LastIndex(filePath, match); idx != -1 { return filePath[0:idx] } return "" } func detectPythonCodeFile(fileName string) bool { return isFileExt(fileName, pySrcFileExt) } func detectPythonPkgDir(fileName string) string { dpPrefix := getPathElementPrefix(fileName, pyDistPkgDir) if dpPrefix != "" { return fmt.Sprintf("%s%s", dpPrefix, pyDistPkgDir) } spPrefix := getPathElementPrefix(fileName, pySitePkgDir) if spPrefix != "" { return fmt.Sprintf("%s%s", spPrefix, pySitePkgDir) } return "" } func detectRubyCodeFile(fileName string) bool { return isFileExt(fileName, rbSrcFileExt) } func detectRubyPkgDir(fileName string) string { prefix := getPathElementPrefixLast(fileName, rbGemsSubDir) if prefix != "" { return fmt.Sprintf("%s%s", prefix, rbGemsSubDir) } return "" } func detectNodeCodeFile(fileName string) bool { return isFileExt(fileName, nodeSrcFileExt) } func detectNodePkgDir(fileName string) string { prefix := getPathElementPrefix(fileName, nodePackageDirPath) if prefix != "" { return fmt.Sprintf("%s%s", prefix, nodePackageDirPath) } return "" } func (p *store) archiveArtifacts() error { logger := log.WithField("op", "store.archiveArtifacts") logger.Trace("call") defer logger.Trace("exit") src := filepath.Join(p.storeLocation, app.ArtifactFilesDirName) dst := filepath.Join(p.storeLocation, filesArchiveName) logger.Debugf("src='%s' dst='%s'", src, dst) trimPrefix := fmt.Sprintf("%s/", src) return fsutil.ArchiveDir(dst, src, trimPrefix, "") } // Go over all saved artifacts and update the name list to make // sure all the files & folders are reflected in the final report. // Hopefully, just a temporary workaround until a proper refactoring. func (p *store) enumerateArtifacts() { logger := log.WithField("op", "store.enumerateArtifacts") logger.Trace("call") defer logger.Trace("exit") knownFiles := list2map(p.nameList) artifactFilesDir := filepath.Join(p.storeLocation, app.ArtifactFilesDirName) var curpath string dirqueue := []string{artifactFilesDir} for len(dirqueue) > 0 { curpath, dirqueue = dirqueue[0], dirqueue[1:] entries, err := os.ReadDir(curpath) if err != nil { logger.WithError(err).Debugf("os.ReadDir(%s)", curpath) // Keep processing though since it might have been a partial result. } // Leaf element - empty dir. if len(entries) == 0 { // Trim /opt/_slim/artifacts/files prefix from the dirpath. curpath = strings.TrimPrefix(curpath, artifactFilesDir) if knownFiles[curpath] { continue } if props, err := artifactProps(curpath); err == nil { p.nameList = append(p.nameList, curpath) p.rawNames[curpath] = props knownFiles[curpath] = true } else { logger.WithError(err). WithField("path", curpath). Debugf("artifactProps(%s): failed computing dir artifact props", curpath) } continue } for _, child := range entries { childpath := filepath.Join(curpath, child.Name()) if child.IsDir() { dirqueue = append(dirqueue, childpath) continue } // Trim /opt/_slim/artifacts/files prefix from the filepath. childpath = strings.TrimPrefix(childpath, artifactFilesDir) // Leaf element - regular file or symlink. if knownFiles[childpath] { continue } if props, err := artifactProps(childpath); err == nil { p.nameList = append(p.nameList, childpath) p.rawNames[childpath] = props knownFiles[childpath] = true } else { logger.WithError(err). WithField("path", childpath). Debugf("artifactProps(%s): failed computing artifact props", childpath) } } } } func (p *store) saveReport() error { logger := log.WithField("op", "store.saveReport") logger.Trace("call") defer logger.Trace("exit") creport := report.ContainerReport{ Sensor: p.seReport, Monitors: report.MonitorReports{ Pt: p.ptMonReport, Fan: p.fanMonReport, }, } if p.cmd != nil { creport.StartCommand = &report.StartCommandReport{ AppName: p.cmd.AppName, AppArgs: p.cmd.AppArgs, AppUser: p.cmd.AppUser, AppEntrypoint: p.cmd.AppEntrypoint, AppCmd: p.cmd.AppCmd, } } sinfo := system.GetSystemInfo() creport.System = report.SystemReport{ Type: sinfo.Sysname, Release: sinfo.Release, Distro: report.DistroInfo{ Name: sinfo.Distro.Name, Version: sinfo.Distro.Version, DisplayName: sinfo.Distro.DisplayName, }, } sort.Strings(p.nameList) for _, fname := range p.nameList { rawNameRecord, found := p.rawNames[fname] if found { creport.Image.Files = append(creport.Image.Files, rawNameRecord) } else { logger.Debugf("nameList file name (%s) not found in rawNames map", fname) } } _, err := os.Stat(p.storeLocation) if os.IsNotExist(err) { os.MkdirAll(p.storeLocation, 0777) if _, err := os.Stat(p.storeLocation); err != nil { return err } } reportFilePath := filepath.Join(p.storeLocation, report.DefaultContainerReportFileName) logger.Debugf("saving report to '%s'", reportFilePath) var reportData bytes.Buffer encoder := json.NewEncoder(&reportData) encoder.SetEscapeHTML(false) encoder.SetIndent("", " ") if err := encoder.Encode(creport); err != nil { return err } return os.WriteFile(reportFilePath, reportData.Bytes(), 0644) } func getFileHash(artifactFileName string) (string, error) { fileData, err := os.ReadFile(artifactFileName) if err != nil { return "", err } hash := sha1.Sum(fileData) return hex.EncodeToString(hash[:]), nil } func getDataType(artifactFileName string) (string, error) { //TODO: use libmagic (pure impl) var cerr bytes.Buffer var cout bytes.Buffer cmd := exec.Command(fileTypeCmd, artifactFileName) cmd.Stderr = &cerr cmd.Stdout = &cout if err := cmd.Start(); err != nil { return "", err } if err := cmd.Wait(); err != nil { err = fmt.Errorf("getDataType - error getting data type: %s / stderr: %s", err, cerr.String()) return "", err } if typeInfo := strings.Split(strings.TrimSpace(cout.String()), ":"); len(typeInfo) > 1 { return strings.TrimSpace(typeInfo[1]), nil } return "unknown", nil } /* func cpFile(src, dst string) error { s, err := os.Open(src) if err != nil { log.Warnln("sensor: monitor - cp - error opening source file =>", src) return err } defer s.Close() dstDir := fsutil.FileDir(dst) err = os.MkdirAll(dstDir, 0777) if err != nil { log.Warnln("sensor: monitor - dir error =>", err) } d, err := os.Create(dst) if err != nil { log.Warnln("sensor: monitor - cp - error opening dst file =>", dst) return err } //todo: copy owner info... srcFileInfo, err := s.Stat() if err == nil { if err := d.Chmod(srcFileInfo.Mode()); err != nil { log.Warnln("sensor: cpFile - unable to set mode =>", dst) } } if _, err := io.Copy(d, s); err != nil { d.Close() return err } if err := d.Close(); err != nil { return err } sysStat, ok := srcFileInfo.Sys().(*syscall.Stat_t) if !ok { log.Warnln("sensor: cpFile - unable to get Stat_t =>", src) return nil } //note: cpFile() is only for regular files if srcFileInfo.Mode()&os.ModeSymlink != 0 { log.Warnln("sensor: cpFile - source is a symlink =>", src) return nil } //note: need to do the same for symlinks too if err := fsutil.UpdateFileTimes(dst, sysStat.Atim, sysStat.Mtim); err != nil { log.Warnln("sensor: cpFile - UpdateFileTimes error =>", dst) return err } return nil } */ func py3FileNameFromCache(p string) string { ext := path.Ext(p) if !(((ext == pycExt) || (ext == pyoExt)) && strings.Contains(p, pycacheDir)) { return "" } pathParts := strings.Split(p, "/") if !((len(pathParts) > 1) && (pycache == pathParts[len(pathParts)-2])) { return "" } pycFileName := path.Base(p) nameParts := strings.Split(pycFileName, ".") if !(len(nameParts) > 2) { return "" } var pyFileName string if len(nameParts) == 3 { pyFileName = fmt.Sprintf("%v.py", nameParts[0]) } else { pyFileName = fmt.Sprintf("%v.py", strings.Join(nameParts[0:len(nameParts)-2], ".")) } return path.Join(path.Dir(path.Dir(p)), pyFileName) } func fixPy3CacheFile(src, dst string) error { dstPyFilePath := py3FileNameFromCache(dst) if dstPyFilePath == "" { return nil } srcPyFilePath := py3FileNameFromCache(src) if srcPyFilePath == "" { return nil } if _, err := os.Stat(dstPyFilePath); err != nil && os.IsNotExist(err) { //if err := cpFile(srcPyFilePath, dstPyFilePath); err != nil { if err := fsutil.CopyRegularFile(true, srcPyFilePath, dstPyFilePath, true); err != nil { log.Debugf("sensor: monitor - fixPy3CacheFile - error copying file => %v", dstPyFilePath) return err } } return nil } func rbEnsureGemFiles(src, storeLocation, prefix string) error { if strings.Contains(src, rbDefaultSpecSubDir) { return nil } dir, file := path.Split(src) base := strings.TrimSuffix(dir, rbSpecSubDir) gemName := strings.TrimSuffix(file, rbGemSpecExt) extBasePath := filepath.Join(base, rgExtSibDir) foList, err := os.ReadDir(extBasePath) if err != nil { return err } for _, fo := range foList { if fo.IsDir() { platform := fo.Name() extPlatformPath := filepath.Join(extBasePath, platform) foVerList, err := os.ReadDir(extPlatformPath) if err != nil { return err } for _, foVer := range foVerList { if foVer.IsDir() { rversion := foVer.Name() extBuildFlagFilePath := filepath.Join(base, rgExtSibDir, platform, rversion, gemName, rbGemBuildFlag) if _, err := os.Stat(extBuildFlagFilePath); err != nil && os.IsNotExist(err) { log.Debug("sensor: monitor - rbEnsureGemFiles - no native extensions for gem =>", gemName) continue } extBuildFlagFilePathDst := fmt.Sprintf("%s%s%s", storeLocation, prefix, extBuildFlagFilePath) if _, err := os.Stat(extBuildFlagFilePathDst); err != nil && os.IsNotExist(err) { //if err := cpFile(extBuildFlagFilePath, extBuildFlagFilePathDst); err != nil { if err := fsutil.CopyRegularFile(true, extBuildFlagFilePath, extBuildFlagFilePathDst, true); err != nil { log.Debugf("sensor: monitor - rbEnsureGemFiles - error copying file => %v", extBuildFlagFilePathDst) return err } } } } } } return nil } type nuxtDirs struct { Build string Dist string } func getNuxtConfig(path string) (*nuxtDirs, error) { if _, err := os.Stat(path); err != nil && os.IsNotExist(err) { log.Debugf("sensor: monitor - getNuxtConfig - err stat => %s - %s", path, err.Error()) return nil, fmt.Errorf("sensor: artifact - getNuxtConfig - error getting file => %s", path) } dat, err := os.ReadFile(path) if err != nil { log.Debugf("sensor: monitor - getNuxtConfig - err reading file => %s - %s", path, err.Error()) return nil, fmt.Errorf("sensor: artifact - getNuxtConfig - error reading file => %s", path) } log.Tracef("sensor: monitor - getNuxtConfig(%s) - %s", path, string(dat)) nuxt := nuxtDirs{ Build: nuxtDefaultBuildDir, Dist: fmt.Sprintf("%s/%s", nuxtDefaultBuildDir, nuxtDefaultDistDir), } /* todo: need more test apps to verify this part of the code vm := otto.New() vm.Run(dat) if value, err := vm.Get(nuxtBuildDirKey); err == nil { if v, err := value.ToString(); err == nil { nuxt.Build = v } else { log.Debugf("saveArtifacts - using build default => %s", err.Error()) } } else { log.Debug("saveArtifacts - error reading nuxt.config.js file => ", err.Error()) return nil, fmt.Errorf("sensor: artifact - getNuxtConfig - error getting buildDir => %s", path) } if value, err := vm.Get(nuxtDistDirKey); err == nil { if v, err := value.ToString(); err == nil { nuxt.Dist = fmt.Sprintf("%s/%s", nuxt.Build, v) } else { log.Debugf("saveArtifacts - using dist default => %s", err.Error()) } } else { log.Debug("saveArtifacts - reading nuxt.config.js file => ", err.Error()) return nil, fmt.Errorf("sensor: artifact - getNuxtConfig - error getting distDir => %s", path) } */ return &nuxt, nil } func isNuxtConfigFile(filePath string) bool { fileName := filepath.Base(filePath) if fileName == nuxtConfigFile { return true } //TODO: read the file and verify that it's a real nuxt config file return false } ///// func isNextConfigFile(filePath string) bool { fileName := filepath.Base(filePath) if fileName == nextConfigFile || fileName == nextConfigFileAlt { return true } //TODO: read the file and verify that it's a real next config file return false } ///// func isRbGemSpecFile(filePath string) bool { ext := path.Ext(filePath) if ext == rbGemSpecExt && strings.Contains(filePath, rbSpecSubDir) { return true } return false } func isNodePackageFile(filePath string) bool { fileName := filepath.Base(filePath) if fileName == nodePackageFile { return true } //TODO: read the file and verify that it's a real package file return false } func getNodePackageFileData(filePath string) (*NodePackageConfigSimple, error) { fileName := filepath.Base(filePath) if fileName != nodePackageFile { return nil, nil } var result NodePackageConfigSimple err := fsutil.LoadStructFromFile(filePath, &result) if err != nil { log.Debugf("sensor: getNodePackageFileData(%s) - error loading data => %v", filePath, err) return nil, err } return &result, nil } func nodeEnsurePackageFiles(keepPerms bool, src, storeLocation, prefix string) error { if strings.HasSuffix(src, nodeNPMNodeGypPackage) { //for now only ensure that we have node-gyp for npm //npm requires it to be there even though it won't use it //'check if exists' condition (not picked up by the FAN monitor, but picked up by the PT monitor) nodeGypFilePath := path.Join(filepath.Dir(src), nodeNPMNodeGypFile) if _, err := os.Stat(nodeGypFilePath); err == nil { nodeGypFilePathDst := fmt.Sprintf("%s%s%s", storeLocation, prefix, nodeGypFilePath) if err := fsutil.CopyRegularFile(keepPerms, nodeGypFilePath, nodeGypFilePathDst, true); err != nil { log.Debugf("sensor: nodeEnsurePackageFiles - error copying %s => %v", nodeGypFilePath, err) } } } //NOTE: can also read the dependencies and confirm/ensure that we copied everything we need return nil } var pidFilePathSuffixes = []string{ "/var/run/nginx.pid", "/run/nginx.pid", "/tmp/nginx.pid", "/tmp/pids/server.pid", } func isKnownPidFilePath(filePath string) bool { for _, suffix := range pidFilePathSuffixes { if strings.HasSuffix(filePath, suffix) { return true } } return false } func hasPidFileSuffix(filePath string) bool { if strings.HasSuffix(filePath, pidFileSuffix) { return true } return false } func isNgxArtifact(filePath string) bool { if strings.Contains(filePath, ngxSubDir) || strings.HasSuffix(filePath, ngxBinName) { return true } return false } func ngxEnsure(prefix string) { //ensure common temp paths (note: full implementation needs mkdir syscall info) if info, err := os.Stat(ngxCommonTemp); err == nil { if info.IsDir() { dstPath := fmt.Sprintf("%s/files%s", prefix, ngxCommonTemp) if !fsutil.DirExists(dstPath) { err := os.MkdirAll(dstPath, 0777) //err, errs := fsutil.CopyDir(true, ngxCommonTemp, dstPath, true, true, nil, nil, nil) if err != nil { log.Debugf("ngxEnsure - MkdirAll(%v) error: %v", dstPath, err) } //if len(errs) > 0 { // log.Warnf("ngxEnsure - CopyDir copy error: %+v", errs) //} } } else { log.Debugf("ngxEnsure - %v should be a directory", ngxCommonTemp) } } else { if !os.IsNotExist(err) { log.Debugf("ngxEnsure - error checking %v => %v", ngxCommonTemp, err) } } if info, err := os.Stat(ngxLogTemp); err == nil { if info.IsDir() { dstPath := fmt.Sprintf("%s/files%s", prefix, ngxLogTemp) if !fsutil.DirExists(dstPath) { err := os.MkdirAll(dstPath, 0777) if err != nil { log.Debugf("ngxEnsure - MkdirAll(%v) error: %v", dstPath, err) } } } else { log.Debugf("ngxEnsure - %v should be a directory", ngxLogTemp) } } else { if !os.IsNotExist(err) { log.Debugf("ngxEnsure - error checking %v => %v", ngxLogTemp, err) } } if info, err := os.Stat(ngxCacheTemp); err == nil { if info.IsDir() { dstPath := fmt.Sprintf("%s/files%s", prefix, ngxCacheTemp) if !fsutil.DirExists(dstPath) { err := os.MkdirAll(dstPath, 0777) if err != nil { log.Debugf("ngxEnsure - MkdirAll(%v) error: %v", dstPath, err) } } } else { log.Debugf("ngxEnsure - %v should be a directory", ngxCacheTemp) } } else { if !os.IsNotExist(err) { log.Debugf("ngxEnsure - error checking %v => %v", ngxCacheTemp, err) } } } func shellDependencies() ([]string, error) { var allDeps []string for _, name := range artifact.ShellNames { shellPath, err := exec.LookPath(name) if err != nil { log.Debugf("shellDependencies - checking '%s' shell (not found: %s)", name, err) continue } exeArtifacts, err := sodeps.AllExeDependencies(shellPath, true) if err != nil { log.Debugf("shellDependencies - %v - error getting shell artifacts => %v", shellPath, err) return nil, err } allDeps = append(allDeps, exeArtifacts...) break } if len(allDeps) == 0 { log.Debug("shellDependencies - no shell found") return nil, nil } for _, name := range artifact.ShellCommands { cmdPath, err := exec.LookPath(name) if err != nil { log.Debugf("shellDependencies - checking '%s' cmd (not found: %s)", name, err) continue } cmdArtifacts, err := sodeps.AllExeDependencies(cmdPath, true) if err != nil { log.Debugf("shellDependencies - %v - error getting cmd artifacts => %v", cmdPath, err) return nil, err } allDeps = append(allDeps, cmdArtifacts...) } return allDeps, nil } // TODO: Merge it with prepareArtifact(). func artifactProps(filename string) (*report.ArtifactProps, error) { fileInfo, err := os.Lstat(filename) if err != nil { return nil, err } fileType := report.UnknownArtifactType switch true { case fileInfo.Mode().IsRegular(): fileType = report.FileArtifactType case (fileInfo.Mode() & os.ModeSymlink) != 0: fileType = report.SymlinkArtifactType case fileInfo.IsDir(): fileType = report.DirArtifactType } return &report.ArtifactProps{ FileType: fileType, FilePath: filename, Mode: fileInfo.Mode(), ModeText: fileInfo.Mode().String(), FileSize: fileInfo.Size(), }, nil } func list2map(l []string) map[string]bool { m := map[string]bool{} for _, v := range l { m[v] = true } return m } func findSymlinks(files []string, mountPoint string, excludes []string) map[string]*report.ArtifactProps { log.Debugf("findSymlinks(%v,%v)", len(files), mountPoint) result := map[string]*report.ArtifactProps{} symlinks := map[string]string{} checkPathSymlinks := func(symlinkFileName string) { if _, ok := result[symlinkFileName]; ok { log.Tracef("findSymlinks.checkPathSymlinks - symlink already in files -> %v", symlinkFileName) return } linkRef, err := os.Readlink(symlinkFileName) if err != nil { log.Debugf("findSymlinks.checkPathSymlinks - error getting reference for symlink (%v) -> %v", err, symlinkFileName) return } var absLinkRef string if !filepath.IsAbs(linkRef) { linkDir := filepath.Dir(symlinkFileName) log.Tracef("findSymlinks.checkPathSymlinks - relative linkRef %v -> %v +/+ %v", symlinkFileName, linkDir, linkRef) fullLinkRef := filepath.Join(linkDir, linkRef) var err error absLinkRef, err = filepath.Abs(fullLinkRef) if err != nil { log.Debugf("findSymlinks.checkPathSymlinks - error getting absolute path for symlink ref (1) (%v) -> %v => %v", err, symlinkFileName, fullLinkRef) return } } else { var err error absLinkRef, err = filepath.Abs(linkRef) if err != nil { log.Debugf("findSymlinks.checkPathSymlinks - error getting absolute path for symlink ref (2) (%v) -> %v => %v", err, symlinkFileName, linkRef) return } } //todo: skip "/proc/..." references evalLinkRef, err := filepath.EvalSymlinks(absLinkRef) if err != nil { log.Debugf("findSymlinks.checkPathSymlinks - error evaluating symlink (%v) -> %v => %v", err, symlinkFileName, absLinkRef) } //detecting intermediate dir symlinks symlinkPrefix := fmt.Sprintf("%s/", symlinkFileName) absPrefix := fmt.Sprintf("%s/", absLinkRef) evalPrefix := fmt.Sprintf("%s/", evalLinkRef) //TODO: //have an option not to resolve intermediate dir symlinks //it'll result in file duplication, but the symlinks //resolution logic will be less complicated and faster for _, fname := range files { added := false if strings.HasPrefix(fname, symlinkPrefix) { result[symlinkFileName] = nil log.Tracef("findSymlinks.checkPathSymlinks - added path symlink to files (0) -> %v", symlinkFileName) added = true } if strings.HasPrefix(fname, absPrefix) { result[symlinkFileName] = nil log.Tracef("findSymlinks.checkPathSymlinks - added path symlink to files (1) -> %v", symlinkFileName) added = true } if evalLinkRef != "" && absPrefix != evalPrefix && strings.HasPrefix(fname, evalPrefix) { result[symlinkFileName] = nil log.Tracef("findSymlinks.checkPathSymlinks - added path symlink to files (2) -> %v", symlinkFileName) added = true } if added { return } } symlinks[symlinkFileName] = linkRef } inodes, devices := filesToInodesNative(files) log.Debugf("findSymlinks - len(inodes)=%v len(devices)=%v", len(inodes), len(devices)) inodeToFiles := map[uint64][]string{} //native filepath.Walk is a bit slow (compared to the "find" command) //but it's fast enough for now filepath.Walk(mountPoint, func(fullName string, fileInfo os.FileInfo, err error) error { if strings.HasPrefix(fullName, "/proc/") { log.Debugf("findSymlinks: skipping /proc file system objects...") return filepath.SkipDir } if strings.HasPrefix(fullName, "/sys/") { log.Debugf("findSymlinks: skipping /sys file system objects...") return filepath.SkipDir } if strings.HasPrefix(fullName, "/dev/") { log.Debugf("findSymlinks: skipping /dev file system objects...") return filepath.SkipDir } // Optimization: Avoid walking excluded folders. Supposed to help with // mounting big folders from the host (they should be explicitly // excluded). // TODO: Combine this logic with the similar logic in GetCurrentPaths(). for _, xpattern := range excludes { if match, _ := doublestar.Match(xpattern, fullName); match { if fileInfo.Mode().IsDir() { return filepath.SkipDir } return nil } } if err != nil { log.Debugf("findSymlinks: error accessing %q: %v\n", fullName, err) //just ignore the error and keep going return nil } if fileInfo.Sys() == nil { log.Debugf("findSymlinks: fileInfo.Sys() is nil (ignoring)") return nil } sysStatInfo, ok := fileInfo.Sys().(*syscall.Stat_t) if !ok { return fmt.Errorf("findSymlinks - could not convert fileInfo to Stat_t for %s", fullName) } if _, ok := devices[uint64(sysStatInfo.Dev)]; !ok { log.Debugf("findSymlinks: ignoring %v (by device id - %v)", fullName, sysStatInfo.Dev) //NOTE: //don't return filepath.SkipDir for everything //because we might still need other files in the dir //return filepath.SkipDir //example: "/etc/hostname" Docker mounts from another device //NOTE: //can move the checks for /dev, /sys and /proc here too return nil } if fileInfo.Mode()&os.ModeSymlink != 0 { checkPathSymlinks(fullName) if info, err := getFileSysStats(fullName); err == nil { if _, ok := inodes[info.Ino]; ok { //not using the inode for the link (using the target inode instead) inodeToFiles[info.Ino] = append(inodeToFiles[info.Ino], fullName) } else { //log.Debugf("findSymlinks - don't care about this symlink (%s)",fullName) } } else { log.Infof("findSymlinks - could not get target stats info for file (%v) -> %v", err, fullName) } } else { if _, ok := inodes[sysStatInfo.Ino]; ok { inodeToFiles[sysStatInfo.Ino] = append(inodeToFiles[sysStatInfo.Ino], fullName) } else { //log.Debugf("findSymlinks - don't care about this file (%s)",fullName) } } return nil }) log.Debugf("findSymlinks - len(inodeToFiles)=%v", len(inodeToFiles)) for inodeID := range inodes { v := inodeToFiles[inodeID] for _, f := range v { //result[f] = inodeID result[f] = nil } } //NOTE/TODO: //Might need multiple passes until no new symlinks are added to result //(with the current approach) //Should REDESIGN to use a reverse/target radix and a radix-based result for symlinkFileName, linkRef := range symlinks { var absLinkRef string if !filepath.IsAbs(linkRef) { linkDir := filepath.Dir(symlinkFileName) log.Debugf("findSymlinks.walkSymlinks - relative linkRef %v -> %v +/+ %v", symlinkFileName, linkDir, linkRef) fullLinkRef := filepath.Join(linkDir, linkRef) var err error absLinkRef, err = filepath.Abs(fullLinkRef) if err != nil { log.Debugf("findSymlinks.walkSymlinks - error getting absolute path for symlink ref (1) (%v) -> %v => %v", err, symlinkFileName, fullLinkRef) break } } else { var err error absLinkRef, err = filepath.Abs(linkRef) if err != nil { log.Debugf("findSymlinks.walkSymlinks - error getting absolute path for symlink ref (2) (%v) -> %v => %v", err, symlinkFileName, linkRef) break } } //todo: skip "/proc/..." references evalLinkRef, err := filepath.EvalSymlinks(absLinkRef) if err != nil { log.Debugf("findSymlinks.walkSymlinks - error evaluating symlink (%v) -> %v => %v", err, symlinkFileName, absLinkRef) } //detecting intermediate dir symlinks symlinkPrefix := fmt.Sprintf("%s/", symlinkFileName) absPrefix := fmt.Sprintf("%s/", absLinkRef) evalPrefix := fmt.Sprintf("%s/", evalLinkRef) for fname := range result { added := false if strings.HasPrefix(fname, symlinkPrefix) { result[symlinkFileName] = nil log.Debugf("findSymlinks.walkSymlinks - added path symlink to files (0) -> %v", symlinkFileName) added = true } if strings.HasPrefix(fname, absPrefix) { result[symlinkFileName] = nil log.Debugf("findSymlinks.walkSymlinks - added path symlink to files (1) -> %v", symlinkFileName) added = true } if evalLinkRef != "" && absPrefix != evalPrefix && strings.HasPrefix(fname, evalPrefix) { result[symlinkFileName] = nil log.Debugf("findSymlinks.walkSymlinks - added path symlink to files (2) -> %v", symlinkFileName) added = true } if added { break } } } return result } func filesToInodesNative(files []string) (map[uint64]struct{}, map[uint64]struct{}) { inodes := map[uint64]struct{}{} devices := map[uint64]struct{}{} for _, fullName := range files { info, err := getFileSysStats(fullName) if err != nil { log.Debugf("filesToInodesNative - could not get inode for %s", fullName) continue } inodes[info.Ino] = struct{}{} devices[uint64(info.Dev)] = struct{}{} } return inodes, devices } func getFileSysStats(fullName string) (*syscall.Stat_t, error) { statInfo, err := os.Stat(fullName) if err != nil { return nil, err } sysStatInfo, ok := statInfo.Sys().(*syscall.Stat_t) if !ok { return nil, fmt.Errorf("failed to get system stat info for %s", fullName) } return sysStatInfo, nil } func getFileDevice(fullName string) (uint64, error) { info, err := getFileSysStats(fullName) if err != nil { return 0, err } return uint64(info.Dev), nil } ================================================ FILE: pkg/app/sensor/controlled/controlled.go ================================================ package controlled import ( "context" "errors" "fmt" "time" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app/sensor/artifact" "github.com/slimtoolkit/slim/pkg/app/sensor/execution" "github.com/slimtoolkit/slim/pkg/app/sensor/monitor" "github.com/slimtoolkit/slim/pkg/ipc/command" "github.com/slimtoolkit/slim/pkg/ipc/event" "github.com/slimtoolkit/slim/pkg/mondel" "github.com/slimtoolkit/slim/pkg/util/errutil" ) var ErrPrematureShutdown = errors.New("sensor shutdown before monitor stop") type Sensor struct { ctx context.Context exe execution.Interface newMonitor monitor.NewCompositeMonitorFunc del mondel.Publisher artifactor artifact.Processor workDir string mountPoint string } func NewSensor( ctx context.Context, exe execution.Interface, newMonitor monitor.NewCompositeMonitorFunc, del mondel.Publisher, artifactor artifact.Processor, workDir string, mountPoint string, ) *Sensor { return &Sensor{ ctx: ctx, exe: exe, newMonitor: newMonitor, del: del, artifactor: artifactor, workDir: workDir, mountPoint: mountPoint, } } // Sensor can be in two interchanging (and mutually exclusive) "states": // // - (I) No monitor is running // -> ShutdownSensor command arrives => clean exit // -> StartMonitor command arrives => go to state II. // -> Any other command => grumble but keep waiting // // - (II) Monitor is running // -> StopMonitor command arrives => stop the mon, dump the report, and go to state I. // -> ShutdownSensor command arrives => cancel monitoring, grumble, and exit // -> Any other command => grumble but keep waiting func (s *Sensor) Run() error { s.exe.HookSensorPostStart() err := s.run() if err != nil { s.exe.PubEvent(event.Error, err.Error()) } // We have to dump the artifacts before invokin the pre-shutdown // hook - it may want to upload the artifacts somewhere. errutil.WarnOn(s.artifactor.Archive()) s.exe.HookSensorPreShutdown() s.exe.PubEvent(event.ShutdownSensorDone) return err } func (s *Sensor) run() error { log.Info("sensor: waiting for commands...") for { mon, err := s.runWithoutMonitor() if err != nil { s.exe.HookMonitorFailed() s.exe.PubEvent(event.StartMonitorFailed, &event.StartMonitorFailedData{ Component: event.ComMonitorRunner, //TODO: need to get to the real component State: s.exe.State(), Errors: []string{err.Error()}, }) return fmt.Errorf("run sensor without monitor failed: %w", err) } if mon == nil { return nil } s.exe.PubEvent(event.StartMonitorDone) if err := s.runWithMonitor(mon); err != nil { return fmt.Errorf("run sensor with monitor failed: %w", err) } s.exe.HookMonitorPostShutdown() s.exe.PubEvent(event.StopMonitorDone) } } func (s *Sensor) runWithoutMonitor() (monitor.CompositeMonitor, error) { ticker := time.NewTicker(5 * time.Second) defer ticker.Stop() for { select { case cmd := <-s.exe.Commands(): log.Debugf("sensor: new command => %+v", cmd) switch typedCmd := cmd.(type) { case *command.StartMonitor: return s.startMonitor(typedCmd) case *command.ShutdownSensor: return nil, nil // Clean exit default: log.Warn("sensor: ignoring unknown or unexpected command => ", cmd) } // eof: type switch case <-ticker.C: log.Debug(".") } // eof: select } } func (s *Sensor) startMonitor(cmd *command.StartMonitor) (monitor.CompositeMonitor, error) { if err := s.artifactor.PrepareEnv(cmd); err != nil { log.WithError(err).Error("sensor: artifactor.PrepareEnv() failed") return nil, fmt.Errorf("failed to prepare artifacts env: %w", err) } origPaths, err := s.artifactor.GetCurrentPaths(s.mountPoint, cmd.Excludes) if err != nil { log.WithError(err).Error("sensor: artifactor.GetCurrentPaths() failed") return nil, fmt.Errorf("failed to enumerate current paths: %w", err) } s.exe.HookMonitorPreStart() mon, err := s.newMonitor( s.ctx, cmd, s.workDir, s.del, s.artifactor.ArtifactsDir(), s.mountPoint, origPaths, ) if err != nil { log.WithError(err).Error("sensor: failed to create composite monitor") return nil, err } if err := mon.Start(); err != nil { log.WithError(err).Error("sensor: failed to start composite monitor") return nil, err } log.Info("sensor: monitor started...") return mon, nil } func (s *Sensor) runWithMonitor(mon monitor.CompositeMonitor) error { log.Debug("sensor: monitor.worker - waiting to stop monitoring...") log.Debug("sensor: error collector - waiting for errors...") ticker := time.NewTicker(time.Second * 5) defer ticker.Stop() // Only two ways out of this: either StopMonitor or ShutdownSensor. stopCommandReceived := false loop: for { select { case <-mon.Done(): break loop case cmd := <-s.exe.Commands(): switch cmd.(type) { case *command.StopMonitor: stopCommandReceived = true mon.Cancel() break loop case *command.ShutdownSensor: mon.Cancel() // Dirty exit - abandoning the results. return ErrPrematureShutdown default: log.Info("sensor: ignoring unknown or unexpected command => ", cmd) } // eof: type switch case err := <-mon.Errors(): log.WithError(err).Warn("sensor: non-critical monitor error condition") s.exe.PubEvent(event.Error, monitor.NonCriticalError(err).Error()) case <-ticker.C: s.exe.HookTargetAppRunning() log.Debug(".") } // eof: select } if !stopCommandReceived { // Monitor can finish before the stop command is received. // In such case, we have to await the explicit stop. if cmd, ok := (<-s.exe.Commands()).(*command.StopMonitor); !ok { return fmt.Errorf("sensor received unepxected command: %#+v", cmd) } } return s.processMonitoringResults(mon) } func (s *Sensor) processMonitoringResults(mon monitor.CompositeMonitor) error { // A bit of code duplication to avoid starting a goroutine // for error event handling - keeping the control flow // "single-threaded" keeps reasoning about the logic. for _, err := range mon.DrainErrors() { log.WithError(err).Warn("sensor: non-critical monitor error condition (drained)") s.exe.PubEvent(event.Error, monitor.NonCriticalError(err).Error()) } log.Info("sensor: composite monitor is done, checking status...") report, err := mon.Status() if err != nil { log.WithError(err).Error("sensor: composite monitor failed") return fmt.Errorf("composite monitor failed: %w", err) } if err := s.artifactor.Process( mon.StartCommand(), s.mountPoint, report.PeReport, report.FanReport, report.PtReport, ); err != nil { log.WithError(err).Error("sensor: artifact.Process() failed") return fmt.Errorf("saving reports failed: %w", err) } return nil // Clean exit } ================================================ FILE: pkg/app/sensor/controlled/controlled_test.go ================================================ package controlled_test import ( "context" "errors" "testing" "time" "github.com/slimtoolkit/slim/pkg/app/sensor/artifact" "github.com/slimtoolkit/slim/pkg/app/sensor/controlled" "github.com/slimtoolkit/slim/pkg/app/sensor/monitor" "github.com/slimtoolkit/slim/pkg/app/sensor/monitor/fanotify" "github.com/slimtoolkit/slim/pkg/app/sensor/monitor/ptrace" "github.com/slimtoolkit/slim/pkg/ipc/command" "github.com/slimtoolkit/slim/pkg/mondel" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/test/stub/sensor/execution" stubmonitor "github.com/slimtoolkit/slim/pkg/test/stub/sensor/monitor" ) // Stubs func newStubMonitorFunc( ctx context.Context, fanMon fanotify.Monitor, ptMon ptrace.Monitor, ) monitor.NewCompositeMonitorFunc { if fanMon == nil { fanMon = stubmonitor.NewFanMonitor(ctx) } if ptMon == nil { ptMon = stubmonitor.NewPtMonitor(ctx) } return func( ctx context.Context, cmd *command.StartMonitor, workDir string, del mondel.Publisher, artifactsDir string, mountPoint string, origPaths map[string]struct{}, ) (monitor.CompositeMonitor, error) { return monitor.Compose( cmd, nil, fanMon, ptMon, nil, nil, ), nil } } type artifactorStub struct{} var _ artifact.Processor = &artifactorStub{} func (a *artifactorStub) ArtifactsDir() string { return "" } func (a *artifactorStub) GetCurrentPaths(root string, excludes []string) (map[string]struct{}, error) { return map[string]struct{}{}, nil } func (a *artifactorStub) PrepareEnv(cmd *command.StartMonitor) error { return nil } func (a *artifactorStub) Archive() error { return nil } func (a *artifactorStub) Process( cmd *command.StartMonitor, mountPoint string, peReport *report.PeMonitorReport, fanReport *report.FanMonitorReport, ptReport *report.PtMonitorReport, ) error { return nil } // Tests func TestStartStopShutdown(t *testing.T) { ctx := context.Background() exe := execution.NewExecution() sen := controlled.NewSensor( ctx, exe, newStubMonitorFunc(ctx, nil, nil), nil, //Monitor Data Event Log &artifactorStub{}, "", "", ) go func() { time.Sleep(100 * time.Millisecond) exe.SendCommand(&command.StartMonitor{}) time.Sleep(100 * time.Millisecond) exe.SendCommand(&command.StopMonitor{}) time.Sleep(100 * time.Millisecond) exe.SendCommand(&command.ShutdownSensor{}) }() if err := sen.Run(); err != nil { t.Fatal("Unexpected sensor run error:", err) } } func TestShutdownBeforeStart(t *testing.T) { ctx := context.Background() exe := execution.NewExecution() sen := controlled.NewSensor( ctx, exe, newStubMonitorFunc(ctx, nil, nil), nil, //Monitor Data Event Log &artifactorStub{}, "", "", ) go func() { exe.SendCommand(&command.ShutdownSensor{}) }() if err := sen.Run(); err != nil { t.Fatal("Unexpected sensor run error:", err) } } func TestStartFollowedByShutdown(t *testing.T) { ctx := context.Background() exe := execution.NewExecution() sen := controlled.NewSensor( ctx, exe, newStubMonitorFunc(ctx, nil, nil), nil, //Monitor Data Event Log &artifactorStub{}, "", "", ) go func() { exe.SendCommand(&command.StartMonitor{}) exe.SendCommand(&command.ShutdownSensor{}) }() if err := sen.Run(); !errors.Is(err, controlled.ErrPrematureShutdown) { t.Fatal("Unexpected sensor run error:", err) } } func TestStopNonStartedMonitor(t *testing.T) { ctx := context.Background() exe := execution.NewExecution() sen := controlled.NewSensor( ctx, exe, newStubMonitorFunc(ctx, nil, nil), nil, //Monitor Data Event Log &artifactorStub{}, "", "", ) go func() { exe.SendCommand(&command.StopMonitor{}) exe.SendCommand(&command.ShutdownSensor{}) }() if err := sen.Run(); err != nil { t.Fatal("Unexpected sensor run error:", err) } } ================================================ FILE: pkg/app/sensor/detector/binfile/binfile.go ================================================ package binfile import ( "debug/elf" "strings" log "github.com/sirupsen/logrus" ) type BinProps struct { IsBin bool IsSO bool IsExe bool } func Detected(filePath string) (*BinProps, error) { binFile, err := elf.Open(filePath) if err == nil { binProps := &BinProps{ IsBin: true, } switch binFile.Type { case elf.ET_EXEC: binProps.IsExe = true case elf.ET_DYN: binProps.IsSO = true } binFile.Close() return binProps, nil } log.Debugf("binfile.Detected(%v) - elf.Open error: %v", filePath, err) if elfErr, ok := err.(*elf.FormatError); ok { if strings.Contains(elfErr.Error(), "bad magic number") { return nil, nil } log.Debugf("binfile.Detected(%v) - malformed binary file", filePath) return nil, err } return nil, err } ================================================ FILE: pkg/app/sensor/detector/filetype/filetype.go ================================================ package filetype import ( "bytes" "fmt" "os/exec" "strings" log "github.com/sirupsen/logrus" ) const ( fileTypeCmdName = "file" ) var fileTypeCmd string func init() { findFileTypeCmd() } func findFileTypeCmd() { fileTypeCmd, err := exec.LookPath(fileTypeCmdName) if err != nil { log.Debugf("findFileTypeCmd - cmd not found: %v", err) return } log.Debugf("findFileTypeCmd - cmd found: %v", fileTypeCmd) } func Detect(filePath string) (string, error) { //TODO: use libmagic (pure impl) var cerr bytes.Buffer var cout bytes.Buffer if fileTypeCmd != "" { return "", nil } cmd := exec.Command(fileTypeCmd, filePath) cmd.Stderr = &cerr cmd.Stdout = &cout if err := cmd.Start(); err != nil { return "", err } if err := cmd.Wait(); err != nil { err = fmt.Errorf("Detect - error getting data type: %s / stderr: %s", err, cerr.String()) return "", err } if typeInfo := strings.Split(strings.TrimSpace(cout.String()), ":"); len(typeInfo) > 1 { return strings.TrimSpace(typeInfo[1]), nil } return "unknown", nil } ================================================ FILE: pkg/app/sensor/execution/controlled.go ================================================ package execution import ( "context" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app/sensor/ipc" "github.com/slimtoolkit/slim/pkg/ipc/command" "github.com/slimtoolkit/slim/pkg/ipc/event" ) type controlledExe struct { hookExecutor ipcServer *ipc.Server } func NewControlled( ctx context.Context, lifecycleHookCommand string, ) (Interface, error) { log.Debug("sensor: starting IPC server...") ipcServer, err := ipc.NewServer(ctx.Done()) if err != nil { return nil, err } if err := ipcServer.Run(); err != nil { return nil, err } return &controlledExe{ hookExecutor: hookExecutor{ ctx: ctx, cmd: lifecycleHookCommand, }, ipcServer: ipcServer, }, nil } func (e *controlledExe) Close() { e.ipcServer.Stop() } func (e *controlledExe) Commands() <-chan command.Message { return e.ipcServer.CommandChan() } func (e *controlledExe) PubEvent(etype event.Type, data ...interface{}) { evt := &event.Message{Name: etype} if len(data) > 0 { evt.Data = data[0] } e.ipcServer.TryPublishEvt(evt, 3) } ================================================ FILE: pkg/app/sensor/execution/hook.go ================================================ package execution import ( "context" "fmt" "os/exec" "time" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/acounter" "github.com/slimtoolkit/slim/pkg/util/errutil" ) type kind string const ( sensorPostStart kind = "sensor-post-start" sensorPreShutdown kind = "sensor-pre-shutdown" monitorPreStart kind = "monitor-pre-start" targetAppRunning kind = "target-app-running" monitorPostShutdown kind = "monitor-post-shutdown" monitorFailed kind = "monitor-failed" ) // todo: // add 'kind' to the lifecycle event and // pass the whole event to the lifecycle hook command type LifecycleEvent struct { Timestamp int64 `json:"ts"` SeqNumber uint64 `json:"sn"` } type hookExecutor struct { ctx context.Context cmd string lastHook kind seqNumber acounter.Type } func (h *hookExecutor) State() string { return fmt.Sprintf("%s/%s", h.cmd, string(h.lastHook)) } func (h *hookExecutor) HookSensorPostStart() { h.doHook(sensorPostStart) } func (h *hookExecutor) HookSensorPreShutdown() { h.doHook(sensorPreShutdown) } func (h *hookExecutor) HookMonitorPreStart() { h.doHook(monitorPreStart) } func (h *hookExecutor) HookTargetAppRunning() { h.doHook(targetAppRunning) } func (h *hookExecutor) HookMonitorPostShutdown() { h.doHook(monitorPostShutdown) } func (h *hookExecutor) HookMonitorFailed() { h.doHook(monitorFailed) } func (h *hookExecutor) doHook(k kind) { if len(h.cmd) == 0 { return } event := LifecycleEvent{ Timestamp: time.Now().UTC().UnixNano(), SeqNumber: h.seqNumber.Inc(), } h.lastHook = k //todo: pass event as a base64 encoded json string as an extra param to 'cmd' cmd := exec.CommandContext(h.ctx, h.cmd, string(k)) out, err := cmd.CombinedOutput() logger := log. WithField("kind", string(k)). WithField("event", fmt.Sprintf("%+v", event)). WithField("command", h.cmd). WithField("exit_code", cmd.ProcessState.ExitCode()). WithField("output", string(out)) // Some lifecycle hooks are really fast - hence, the IsNoChildProcesses() check. if err == nil || errutil.IsNoChildProcesses(err) { logger.Debugf("sensor: %s hook succeeded", k) } else { logger.WithError(err).Warnf("sensor: %s hook failed", k) } } ================================================ FILE: pkg/app/sensor/execution/interface.go ================================================ package execution import ( "github.com/slimtoolkit/slim/pkg/ipc/command" "github.com/slimtoolkit/slim/pkg/ipc/event" ) type Interface interface { State() string Commands() <-chan command.Message PubEvent(etype event.Type, data ...interface{}) Close() // Lifecycle hooks (extension points) HookSensorPostStart() HookSensorPreShutdown() HookMonitorPreStart() HookTargetAppRunning() HookMonitorPostShutdown() HookMonitorFailed() } ================================================ FILE: pkg/app/sensor/execution/standalone.go ================================================ package execution import ( "bytes" "context" "encoding/json" "errors" "flag" "fmt" "io" "os" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app/sensor/standalone/control" "github.com/slimtoolkit/slim/pkg/ipc/command" "github.com/slimtoolkit/slim/pkg/ipc/event" "github.com/slimtoolkit/slim/pkg/util/fsutil" ) type standaloneExe struct { hookExecutor commandCh chan command.Message eventFile io.WriteCloser } func NewStandalone( ctx context.Context, commandFileName string, eventFileName string, lifecycleHookCommand string, ) (Interface, error) { // fsutil.Touch() creates (potentially missing) folder(s). if err := fsutil.Touch(eventFileName); err != nil { return nil, fmt.Errorf( "cannot create execution - touch event file %q failed: %w", eventFileName, err, ) } eventFile, err := os.OpenFile(eventFileName, os.O_APPEND|os.O_WRONLY|os.O_SYNC, 0644) if err != nil { return nil, fmt.Errorf( "cannot create execution - open event file %q failed: %w", eventFileName, err, ) } cmd, err := readCommandFile(commandFileName) if err != nil { return nil, fmt.Errorf( "cannot create execution - cannot read command file %q: %w", commandFileName, err, ) } commandCh := make(chan command.Message, 10) commandCh <- &cmd go control.HandleControlCommandQueue(ctx, commandFileName, commandCh) return &standaloneExe{ hookExecutor: hookExecutor{ ctx: ctx, cmd: lifecycleHookCommand, }, commandCh: commandCh, eventFile: eventFile, }, nil } func (e *standaloneExe) Close() { e.eventFile.Close() close(e.commandCh) } func (e *standaloneExe) Commands() <-chan command.Message { return e.commandCh } func (e *standaloneExe) PubEvent(name event.Type, data ...interface{}) { encoder := json.NewEncoder(e.eventFile) encoder.SetEscapeHTML(false) evt := event.Message{Name: name} if len(data) > 0 { evt.Data = data[0] } if err := encoder.Encode(evt); err != nil { log.WithError(err).Warn("sensor: failed dumping event") } } // TODO: Make this function return a list of commands. func readCommandFile(filename string) (command.StartMonitor, error) { var cmd command.StartMonitor data, err := os.ReadFile(filename) if err != nil { return cmd, fmt.Errorf("could not read command file %q: %w", filename, err) } data = bytes.Split(data, []byte("\n"))[0] if err := json.Unmarshal(data, &cmd); err != nil { return cmd, fmt.Errorf("could not decode command %q: %w", string(data), err) } // The instrumented image will always have the ENTRYPOINT overwritten // by the instrumentor to make the sensor the PID1 process in the monitored // container. // The original ENTRYPOINT & CMD will be preserved as part of the // `commands.json` file. However, it's also possible to override the // CMD at runtime by supplying extra args to the `docker run` (or alike) // command. Sensor needs to be able to detect this and replace the // baked in CMD with the new list of args. For that, the instrumented image's // ENTRYPOINT has to contain a special separator value `--` denoting the end // of the sensor's flags sequence. Example: // // ENTRYPOINT ["/path/to/sensor", "-m=standalone", "-c=/path/to/commands.json", "--" ] // Note on CMD & ENTRYPOINT override: Historically, sensor used // AppName + AppArgs[] to start the target process. With the addition // of the standalone mode, the need for supporting Docker's original // CMD[] + ENTRYPOINT[] approach arose. However, to keep things simple: // // - These two "modes" of starting the target app will be mutually exclusive // (from the sensor's users standpoint). // - The CMD + ENTRYPOINT mode will be converted back to the AppName + AppArgs // early on in the sensor's processing (to prevent cascading changes). // First, check if there is a run-time override of the CMD[] part // (i.e., anything after the `--` separator). // If there is some, it'll essentially "activate" the CMD + ENTRYPOINT mode. if args := flag.Args(); len(args) > 0 { cmd.AppCmd = args } // If it's ENTRYPOINT + CMD mode, converting back to AppName + AppArgs. if len(cmd.AppEntrypoint)+len(cmd.AppCmd) > 0 { if len(cmd.AppName)+len(cmd.AppArgs) > 0 { return cmd, errors.New("ambiguous start command: cannot use [app_name,app_args] and [app_entrypoint,app_cmd] simultaneously") } if len(cmd.AppEntrypoint) > 0 { cmd.AppName = cmd.AppEntrypoint[0] cmd.AppArgs = append(cmd.AppEntrypoint[1:], cmd.AppCmd...) } else { cmd.AppName = cmd.AppCmd[0] cmd.AppArgs = cmd.AppCmd[1:] } } return cmd, nil } ================================================ FILE: pkg/app/sensor/inspector/sodeps/sodeps.go ================================================ package sodeps import ( "bytes" "errors" "os" "os/exec" "path/filepath" "strings" "github.com/slimtoolkit/slim/pkg/app/sensor/detector/binfile" log "github.com/sirupsen/logrus" ) // Inspector errors var ( ErrFilePathNotAbs = errors.New("file path is not absolute") ErrFileNotBin = errors.New("file is not a binary") ErrDepResolverNotFound = errors.New("dependency resolver not found") ) const ( resolverExeName = "ldd" ) func AllExeDependencies(exeFileName string, find bool) ([]string, error) { if !strings.HasPrefix(exeFileName, "/") { if !find { return nil, ErrFilePathNotAbs } exePath, err := exec.LookPath(exeFileName) if err != nil { return nil, err } exeFileName = exePath } return AllDependencies(exeFileName) } const ( strExitStatus = "exit status 127" strErrorReloc = "Error relocating" strErrorSymNotFound = "symbol not found" ) func AllDependencies(binFilePath string) ([]string, error) { //binFilePath could point to an executable or a shared object if !strings.HasPrefix(binFilePath, "/") { return nil, ErrFilePathNotAbs } if _, err := os.Stat(binFilePath); err != nil { if os.IsNotExist(err) { log.Debugf("sodeps.AllDependencies(%v): missing target - %v", binFilePath, err) } return nil, err } if binProps, _ := binfile.Detected(binFilePath); binProps == nil || !binProps.IsBin { return nil, ErrFileNotBin } resolverExePath, err := exec.LookPath(resolverExeName) if err != nil { log.Debugf("sodeps.AllDependencies(%v): resolver not found - %v", binFilePath, err) return nil, ErrDepResolverNotFound } var cerr bytes.Buffer var cout bytes.Buffer cmd := exec.Command(resolverExePath, binFilePath) cmd.Stderr = &cerr cmd.Stdout = &cout if err := cmd.Start(); err != nil { log.Debugf("sodeps.AllDependencies(%v): exe run error - %v", binFilePath, err) return nil, err } depMap := map[string]struct{}{} var deps []string deps = append(deps, binFilePath) if err := cmd.Wait(); err != nil { if strings.Contains(err.Error(), strExitStatus) && strings.Contains(cerr.String(), strErrorReloc) && strings.Contains(cerr.String(), strErrorSymNotFound) { elines := strings.Split(cerr.String(), "\n") for _, line := range elines { clean := strings.TrimSpace(line) if len(clean) == 0 { continue } if !strings.Contains(clean, strErrorReloc) { log.Debugf("sodeps.AllDependencies(%v): skipping '%s'", binFilePath, line) continue } parts := strings.Split(clean, " ") //Error relocating __LIB_NAME__: __SYMBOL_NAME__: symbol not found if len(parts) == 7 { if strings.HasSuffix(parts[2], ":") { dep := strings.Trim(parts[2], ":") depMap[dep] = struct{}{} } } else { log.Debugf("sodeps.AllDependencies(%v): skipping '%s'", binFilePath, line) } } } else { log.Debugf("sodeps.AllDependencies(%v): exe error result - %v (stderr: '%v')", binFilePath, err, cerr.String()) return nil, err } } for dep := range depMap { deps = append(deps, dep) } lines := strings.Split(cout.String(), "\n") for _, line := range lines { clean := strings.TrimSpace(line) if len(clean) == 0 { continue } if strings.Contains(clean, "statically linked") { log.Debugf("sodeps.AllDependencies(%v): statically linked binary file", binFilePath) continue } parts := strings.Split(clean, " ") if len(parts) == 4 { if parts[1] != "=>" { log.Debugf("sodeps.AllDependencies(%v): unexpected line format - '%v'", binFilePath, clean) continue } if parts[2] == resolverExeName { log.Debugf("sodeps.AllDependencies(%v): ignore non-lib dependency - '%v'", binFilePath, clean) continue } deps = append(deps, parts[2]) continue } if len(parts) == 2 { if strings.HasPrefix(parts[0], "/") { //full path (dynamic linker) deps = append(deps, parts[0]) continue } if strings.HasPrefix(parts[0], "linux-vdso") { continue } if strings.HasPrefix(parts[0], resolverExeName) { log.Debugf("sodeps.AllDependencies(%v): ignore resolver dependency - '%v'", binFilePath, clean) continue } log.Debugf("sodeps.AllDependencies(%v): unexpected line - '%v'", binFilePath, clean) continue } } var allDeps []string for depth := 0; len(deps) > 0; depth++ { var fileDeps []string fileDeps, deps = resolveDepArtifacts(deps) allDeps = append(allDeps, fileDeps...) if depth > 5 { log.Debugf("sodeps.AllDependencies(%v): link ref too deep - breaking", binFilePath) break } } return allDeps, nil } func resolveDepArtifacts(names []string) (files, links []string) { for _, name := range names { if info, err := os.Lstat(name); err == nil { files = append(files, name) if info.Mode()&os.ModeSymlink != 0 { linkRef, err := os.Readlink(name) if err != nil { log.Debugf("sodeps.resolveDepArtifacts: %v - error reading link (%v)\n", name, err) continue } var absLinkRef string if !filepath.IsAbs(linkRef) { linkDir := filepath.Dir(name) fullLinkRef := filepath.Join(linkDir, linkRef) var err error absLinkRef, err = filepath.Abs(fullLinkRef) if err != nil { log.Debugf("sodeps.resolveDepArtifacts: %v - error getting absolute path for symlink ref (1) %v - (%v)\n", name, fullLinkRef, err) continue } } else { var err error absLinkRef, err = filepath.Abs(linkRef) if err != nil { log.Debugf("sodeps.resolveDepArtifacts: %v - error getting absolute path for symlink ref (2) %v - (%v)\n", name, linkRef, err) continue } } links = append(links, absLinkRef) } } else { if os.IsNotExist(err) { log.Debugf("sodeps.resolveDepArtifacts: %v - missing dep (%v)\n", name, err) } else { log.Debugf("sodeps.resolveDepArtifacts: %v - error checking dep (%v)\n", name, err) } } } return files, links } ================================================ FILE: pkg/app/sensor/ipc/ipc.go ================================================ package ipc import ( "encoding/json" "fmt" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/ipc/channel" "github.com/slimtoolkit/slim/pkg/ipc/command" "github.com/slimtoolkit/slim/pkg/ipc/event" ) type Server struct { evtChannel *channel.EventServer cmdChannel *channel.CommandServer cmdChan chan command.Message doneChan <-chan struct{} } func NewServer(doneChan <-chan struct{}) (*Server, error) { server := Server{ doneChan: doneChan, cmdChan: make(chan command.Message, 10), } if err := server.initChannels(); err != nil { log.Errorf("sensor: ipc.NewServer error = %v", err) return nil, err } return &server, nil } func (s *Server) initChannels() error { evtChannelAddr := fmt.Sprintf("0.0.0.0:%d", channel.EvtPort) evtChannel := channel.NewEventServer(evtChannelAddr) s.evtChannel = evtChannel cmdChannelAddr := fmt.Sprintf("0.0.0.0:%d", channel.CmdPort) cmdChannel := channel.NewCommandServer(cmdChannelAddr, s) s.cmdChannel = cmdChannel return nil } func (s *Server) shutdownChannels() error { if s.cmdChannel != nil { s.cmdChannel.Stop() s.cmdChannel = nil } if s.evtChannel != nil { s.evtChannel.Stop() s.evtChannel = nil } return nil } func (s *Server) Stop() { s.shutdownChannels() } func (s *Server) CommandChan() <-chan command.Message { return s.cmdChan } func (s *Server) OnRequest(data []byte) ([]byte, error) { resp := command.Response{ Status: command.ResponseStatusError, } if cmd, err := command.Decode(data); err == nil { s.cmdChan <- cmd resp.Status = command.ResponseStatusOk } else { log.Errorf("ipc.Server.OnRequest: error decoding request = %v", err) } respData, err := json.Marshal(&resp) if err != nil { log.Errorf("ipc.Server.OnRequest: error encoding response = %v", err) return nil, err } return respData, nil } func (s *Server) Run() error { log.Debug("sensor: ipc.Server.Run()") err := s.evtChannel.Start(true) if err != nil { log.Errorf("sensor: ipc.Server.Run() - evtChannel.Start error = %v\n", err) return err } err = s.cmdChannel.Start(true) if err != nil { log.Errorf("sensor: ipc.Server.Run() - cmdChannel.Start error = %v\n", err) return err } s.cmdChannel.WaitForConnection() s.evtChannel.WaitForConnection() go func() { for { log.Debug("sensor: ipc.Server.Run - waiting for done signal...") select { case <-s.doneChan: log.Debug("sensor: ipc.Server.Run - done...") s.Stop() return } } }() return nil } func (s *Server) TryPublishEvt(evt *event.Message, retries uint) error { log.Debugf("ipc.Server.TryPublishEvt(%+v)", evt) if s.evtChannel == nil { log.Warnf("ipc.Server.TryPublishEvt(): skipped - server has already been stopped") return nil } data, err := json.Marshal(evt) if err != nil { log.Errorf("app.TryPublishEvt(): failed to encode event - %v", err) return err } return s.evtChannel.Publish(data, retries) } ================================================ FILE: pkg/app/sensor/logger.go ================================================ //go:build linux // +build linux package sensor import ( "fmt" "os" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/util/fsutil" ) func configureLogger( enableDebug bool, levelName string, format string, logFile string, ) error { if err := setLogLevel(enableDebug, levelName); err != nil { return fmt.Errorf("failed to set log-level: %v", err) } if err := setLogFormat(format); err != nil { return fmt.Errorf("failed to set log format: %v", err) } if len(logFile) > 0 { // This touch is not ideal - need to understand how to merge this logic with artifacts.PrepareEnv(). if err := fsutil.Touch(logFile); err != nil { return fmt.Errorf("failed to set log output destination to %q, touch failed with: %v", logFile, err) } f, err := os.OpenFile(logFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) if err != nil { return fmt.Errorf("failed to set log output destination to %q: %w", logFile, err) } log.SetOutput(f) } return nil } func setLogFormat(format string) error { switch format { case "text": log.SetFormatter(&log.TextFormatter{DisableColors: true}) case "json": log.SetFormatter(&log.JSONFormatter{}) default: return fmt.Errorf("unknown log-format %q", format) } return nil } func setLogLevel(enableDebug bool, levelName string) error { if enableDebug { log.SetLevel(log.DebugLevel) return nil } var logLevel log.Level switch levelName { case "trace": logLevel = log.TraceLevel case "debug": logLevel = log.DebugLevel case "info": logLevel = log.InfoLevel case "warn": logLevel = log.WarnLevel case "error": logLevel = log.ErrorLevel case "fatal": logLevel = log.FatalLevel case "panic": logLevel = log.PanicLevel default: return fmt.Errorf("unknown log-level %q", levelName) } log.SetLevel(logLevel) return nil } ================================================ FILE: pkg/app/sensor/monitor/composite.go ================================================ package monitor import ( "context" "errors" "fmt" "io" "os" "path/filepath" "strings" "sync" "time" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app/sensor/monitor/fanotify" "github.com/slimtoolkit/slim/pkg/app/sensor/monitor/ptrace" "github.com/slimtoolkit/slim/pkg/ipc/command" "github.com/slimtoolkit/slim/pkg/mondel" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/util/errutil" ) const ( signalChanBufSize = 10 errorChanBufSize = 100 errorChanDrainTime = 200 * time.Millisecond // Some monitors are passive. If the driving monitor // is too fast, the passive ones may not have a chance // to track all the needed events (used to happen often // between the driving ptrace and observing fanotify mons). minPassiveMonitoring = 1 * time.Second ) var ( ErrInsufficientPermissions = errors.New("insufficient permissions") ) type CompositeReport struct { PeReport *report.PeMonitorReport FanReport *report.FanMonitorReport PtReport *report.PtMonitorReport } type CompositeMonitor interface { // Start() is not reentrant! Start() error // Just a helper getter. StartCommand() *command.StartMonitor SignalTargetApp(s os.Signal) Cancel() // Done() is reentrant. Every invocation returns the same instance of the channel. Done() <-chan struct{} // Errors() method is a way to communicate non-fatal monitor's error // conditions back to the caller. The method is reentrant. Every invocation // returns the same instance of the channel. The error channel is never closed, // but ideally it should stop returning eny errors after the monitor is done // (the actual behavior might differ due to the subordinate monitors, especially // the one at pkg/monitors/ptrace). Errors() <-chan error // Helper method to read left-over non-critical error events after the monitor // is done. DrainErrors() []error // TODO: Consider adding the Files() method for the incremental // report storing. // Files() <-chan *ArtifactProp Status() (*CompositeReport, error) } // The sensor is designed to be capable of processing multiple // StartMonitor commands (sequentially?). However, monitors are // supposed to be one-shot entities. Every time the sensor receives // a new start command, it should initialize a new instance of the // composite monitor using fresh instances of the underlying mons. type monitor struct { cmd *command.StartMonitor del mondel.Publisher startedAt time.Time // peMon *pevent.Monitor fanMon fanotify.Monitor ptMon ptrace.Monitor // Inspired by os/exec.Cmd closeAfterDone []io.Closer signalCh chan os.Signal doneCh chan struct{} doneOnce sync.Once errorCh chan error } var _ CompositeMonitor = (*monitor)(nil) type NewCompositeMonitorFunc func( ctx context.Context, cmd *command.StartMonitor, workDir string, del mondel.Publisher, artifactsDir string, mountPoint string, origPaths map[string]struct{}, ) (CompositeMonitor, error) func NewCompositeMonitor( ctx context.Context, cmd *command.StartMonitor, workDir string, del mondel.Publisher, artifactsDir string, mountPoint string, origPaths map[string]struct{}, ) (CompositeMonitor, error) { log.Info("sensor: creating monitors...") errorCh := make(chan error, errorChanBufSize) fanMon := fanotify.NewMonitor( ctx, del, artifactsDir, mountPoint, cmd.IncludeNew, origPaths, errorCh, ) var closeAfterDone []io.Closer appStdout := os.Stdout if cmd.AppStdoutToFile { sink, file, err := dupAppStdStream(artifactsDir, os.Stdout, "stdout") if err != nil { return nil, err } closeAfterDone = append(closeAfterDone, sink, file) appStdout = sink } appStderr := os.Stderr if cmd.AppStderrToFile { sink, file, err := dupAppStdStream(artifactsDir, os.Stderr, "stderr") if err != nil { closeAll(closeAfterDone) return nil, err } closeAfterDone = append(closeAfterDone, sink, file) appStderr = sink } signalCh := make(chan os.Signal, signalChanBufSize) ptMon := ptrace.NewMonitor( ctx, del, artifactsDir, ptrace.AppRunOpt{ Cmd: cmd.AppName, Args: cmd.AppArgs, AppStdout: appStdout, AppStderr: appStderr, WorkDir: workDir, User: cmd.AppUser, RunAsUser: cmd.RunTargetAsUser, RTASourcePT: cmd.RTASourcePT, ReportOnMainPidExit: cmd.ReportOnMainPidExit, }, cmd.IncludeNew, origPaths, signalCh, errorCh, ) m := Compose(cmd, del, fanMon, ptMon, signalCh, errorCh) m.closeAfterDone = closeAfterDone return m, nil } func Compose( cmd *command.StartMonitor, del mondel.Publisher, fanMon fanotify.Monitor, ptMon ptrace.Monitor, signalCh chan os.Signal, errorCh chan error, ) *monitor { return &monitor{ cmd: cmd, del: del, // TODO: peMon: peMon, fanMon: fanMon, ptMon: ptMon, signalCh: signalCh, errorCh: errorCh, } } // TODO: Consider adding an option to make fanotify // // and pevent monitor errors non-fatal. func (m *monitor) Start() error { log.Info("sensor: starting monitors...") // if err := m.peMon.Start(); err != nil { // return err // } if err := m.fanMon.Start(); err != nil { log.WithError(err).Debug("sensor: composite monitor - FAN error") log.Error("sensor: composite monitor - FAN failed to start running") if strings.Contains(err.Error(), "operation not permitted") { return ErrInsufficientPermissions } closeAll(m.closeAfterDone) return err } if err := m.ptMon.Start(); err != nil { log.WithError(err).Debug("sensor: composite monitor - PTAN error") log.Error("sensor: composite monitor - PTAN failed to start running") closeAll(m.closeAfterDone) return err } m.startedAt = time.Now() return nil } func (m *monitor) StartCommand() *command.StartMonitor { return m.cmd } func (m *monitor) SignalTargetApp(s os.Signal) { m.signalCh <- s } func (m *monitor) Cancel() { // m.peMon.Cancel() m.fanMon.Cancel() m.ptMon.Cancel() } func (m *monitor) Done() <-chan struct{} { m.doneOnce.Do(func() { m.doneCh = make(chan struct{}) go func() { log.Debug("sensor: composite monitor - waiting for sub-monitors...") // The order matters here because the ptrace monitor is the // driving one while the others are rather passive observers. <-m.ptMon.Done() log.Debug("sensor: composite monitor - ptmon is done") // This code smells... If the ptrace monitor finished too quickly, // we have to give the other monitors more time to increase their // chances to track the needed events. But we only do this if the // driving monitor finished successfully. elapsed := time.Since(m.startedAt) if _, err := m.ptMon.Status(); err == nil && elapsed < minPassiveMonitoring { time.Sleep(minPassiveMonitoring - elapsed) } // m.peMon.Cancel() m.fanMon.Cancel() // <-m.peMon.Done() <-m.fanMon.Done() log.Debug("sensor: composite monitor - fanmon is done") closeAll(m.closeAfterDone) //need to call del.Stop here to make sure we get all drained monitor events if m.del != nil { m.del.Stop() } // The composite is done when all its subordinates are done. close(m.doneCh) }() }) return m.doneCh } func (m *monitor) Errors() <-chan error { return m.errorCh } func (m *monitor) DrainErrors() (errors []error) { timer := time.After(errorChanDrainTime) for { select { case <-timer: return errors case err := <-m.errorCh: errors = append(errors, err) } } } func (m *monitor) Status() (*CompositeReport, error) { // peReport, peErr := m.peMon.Status() fanReport, fanErr := m.fanMon.Status() ptReport, ptErr := m.ptMon.Status() if fanErr != nil || ptErr != nil { return nil, fmt.Errorf( "one or more monitors failed: fanotify.error=%q, ptrace.error=%q", fanErr, ptErr, ) } return &CompositeReport{ // PeReport: peReport, FanReport: fanReport, PtReport: ptReport, }, nil } func NonCriticalError(err error) error { return fmt.Errorf("non-critical monitor error: %w", err) } // Using simple io.MultiWriter(os.Stdout, os.File) would make cmd.Wait() // block until either the cmd's stdout is closed or the multi-writer is closed. // However, both are impossible. We need the Wait() to return much earlier // than the process termination (see pkg/monitors/ptrace logic), and multi-writer // cannot be closed at all. Hence, the pipe trick. func dupAppStdStream(artifactsDir string, w io.Writer, kind string) (*os.File, *os.File, error) { filename := filepath.Join(artifactsDir, "app_"+kind+".log") f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, 0o644) if err != nil { return nil, nil, fmt.Errorf("cannot open file %q to duplicate app's %s stream: %w", filename, kind, err) } pr, pw, err := os.Pipe() if err != nil { f.Close() return nil, nil, fmt.Errorf("cannot create pipe for app %s stream: %w", kind, err) } go func() { n, err := io.Copy(io.MultiWriter(w, f), pr) log.Debugf("dupAppStdStream: io.Copy() finished; written=%d error=%v", n, err) }() return pw, f, nil } func closeAll(cs []io.Closer) { for _, c := range cs { errutil.WarnOn(c.Close()) } } ================================================ FILE: pkg/app/sensor/monitor/composite_test.go ================================================ package monitor import ( "context" "errors" "testing" "time" log "github.com/sirupsen/logrus" stubmonitor "github.com/slimtoolkit/slim/pkg/test/stub/sensor/monitor" ) func init() { log.SetLevel(log.DebugLevel) } func TestCompositeMonitor_Lifecycle(t *testing.T) { mon := &monitor{ fanMon: stubmonitor.NewFanMonitor(context.Background()), ptMon: stubmonitor.NewPtMonitor(context.Background()), } mon.Start() select { case <-mon.Done(): t.Fatal("composite monitor must not be done yet") default: } mon.Cancel() select { case <-mon.Done(): break case <-time.After(1*time.Second + minPassiveMonitoring): t.Fatal("composite monitor must be done by this time") } select { case <-mon.Done(): break default: t.Fatal("composite monitor Done() method must be reentrant") } // TODO: Check the status (reports & final error). } func TestCompositeMonitor_DrainErrors(t *testing.T) { mon := &monitor{ errorCh: make(chan error, errorChanBufSize), } go func() { // Definitely within the drain window. mon.errorCh <- errors.New("err1") time.Sleep(errorChanDrainTime / 2) // Still within the drain window. mon.errorCh <- errors.New("err2") time.Sleep(errorChanDrainTime * 2) // Definitely outside of the drain window. mon.errorCh <- errors.New("err3") }() drained := mon.DrainErrors() if len(drained) != 2 { t.Errorf("Unexpected number of drained errors: %d", len(drained)) } if drained[0].Error() != "err1" { t.Errorf("Unexpected drained error: %q", drained[0]) } if drained[1].Error() != "err2" { t.Errorf("Unexpected drained error: %q", drained[1]) } } ================================================ FILE: pkg/app/sensor/monitor/fanotify/monitor.go ================================================ //go:build linux // +build linux package fanotify import ( "bytes" "context" "fmt" "os" "strconv" "strings" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/errors" "github.com/slimtoolkit/slim/pkg/mondel" "github.com/slimtoolkit/slim/pkg/report" fanapi "github.com/slimtoolkit/slim/pkg/third_party/madmo/fanotify" ) const ( errorBufSize = 10 eventBufSize = 1000 procFsFdInfo = "/proc/self/fd/%d" procFsFilePath = "/proc/%v/%v" ) // Event is file operation event type Event struct { ID uint32 Pid int32 File string IsRead bool IsWrite bool } type Monitor interface { // Starts the long running monitoring. The method itself is not // blocking and not reentrant! Start() error // Cancels the underlying ptrace execution context but doesn't // make the current monitor done immediately. You still need to await // the final cleanup with <-mon.Done() before accessing the status. Cancel() // With Done clients can await for the monitoring completion. // The method is reentrant - every invocation returns the same // instance of the channel. Done() <-chan struct{} Status() (*report.FanMonitorReport, error) } type status struct { report *report.FanMonitorReport err error } type monitor struct { ctx context.Context cancel context.CancelFunc del mondel.Publisher artifactsDir string mountPoint string // TODO: Move the logic behind these two fields to the artifact processig stage. includeNew bool origPaths map[string]struct{} status status doneCh chan struct{} errorCh chan<- error logger *log.Entry } func NewMonitor( ctx context.Context, del mondel.Publisher, artifactsDir string, mountPoint string, includeNew bool, origPaths map[string]struct{}, errorCh chan<- error, ) Monitor { logger := log.WithFields(log.Fields{ "app": "sensor", "com": "fanmon", }) ctx, cancel := context.WithCancel(ctx) return &monitor{ ctx: ctx, cancel: cancel, del: del, artifactsDir: artifactsDir, mountPoint: mountPoint, includeNew: includeNew, origPaths: origPaths, doneCh: make(chan struct{}), errorCh: errorCh, logger: logger, } } func (m *monitor) Start() error { logger := m.logger.WithField("op", "Start") logger.Info("call") defer logger.Info("exit") nd, err := fanapi.Initialize(fanapi.FAN_CLASS_NOTIF, os.O_RDONLY) if err != nil { return errors.SE("sensor.fanotify.Run/fanapi.Initialize", "call.error", err) } if err = nd.Mark( fanapi.FAN_MARK_ADD|fanapi.FAN_MARK_MOUNT, fanapi.FAN_MODIFY|fanapi.FAN_ACCESS|fanapi.FAN_OPEN, -1, m.mountPoint, ); err != nil { return errors.SE("sensor.fanotify.Run/nd.Mark", "call.error", err) } // Sync part of the start was successful. // Tracking the completetion of the monitor.... go func() { logger := m.logger.WithField("op", "collector") logger.Info("call") defer logger.Info("exit") var eventID uint32 eventCh := make(chan Event, eventBufSize) go func() { logger := m.logger.WithField("op", "processor") logger.Info("call") fanReport := &report.FanMonitorReport{ MonitorPid: os.Getpid(), MonitorParentPid: os.Getppid(), ProcessFiles: map[string]map[string]*report.FileInfo{}, Processes: map[string]*report.ProcessInfo{}, } process: for { select { case <-m.ctx.Done(): logger.Info("process - done") break process case e := <-eventCh: m.processEvent(e, fanReport) } } logger.Debug("done, drain - starting...") drain: for { select { case e := <-eventCh: m.processEvent(e, fanReport) default: logger.Debug("draining - done") break drain } } logger.Debugf("sending report (processed %v events)...", fanReport.EventCount) m.status.report = fanReport close(m.doneCh) logger.Info("exit") }() for { select { case <-m.ctx.Done(): logger.Info("done - returning") return default: } //TODO: enhance FA Notify to return the original file handle too data, err := nd.GetEvent() if err != nil { m.errorCh <- errors.SE("sensor.fanotify.Run/nd.GetEvent", "call.error", err) continue } logger.Debugf("data.Mask => %x", data.Mask) if (data.Mask & fanapi.FAN_Q_OVERFLOW) == fanapi.FAN_Q_OVERFLOW { logger.Debug("overflow event") continue } doNotify := false isRead := false isWrite := false if (data.Mask & fanapi.FAN_OPEN) == fanapi.FAN_OPEN { logger.Trace("FAN.E - file open") doNotify = true } if (data.Mask & fanapi.FAN_ACCESS) == fanapi.FAN_ACCESS { logger.Trace("FAN.E - file read") isRead = true doNotify = true } if (data.Mask & fanapi.FAN_MODIFY) == fanapi.FAN_MODIFY { logger.Trace("FAN.E - file write") isWrite = true doNotify = true } // Ivan: It might be a good idea to move this from collector to processor. // Probably, the fanotify events should be read as quick as just possible. path, err := os.Readlink(fmt.Sprintf(procFsFdInfo, data.File.Fd())) if err != nil { m.errorCh <- errors.SE("sensor.fanotify.Run/os.Readlink", "call.error", err) continue } data.File.Close() logger.Debugf("file path => %v", path) if strings.HasPrefix(path, m.artifactsDir) { logger.Trace("skipping artifacts dir op...") continue } if doNotify { eventID++ e := Event{ID: eventID, Pid: data.Pid, File: path, IsRead: isRead, IsWrite: isWrite} select { case eventCh <- e: case <-m.ctx.Done(): logger.Info("stopping....") return } } } }() return nil } func (m *monitor) Cancel() { m.cancel() } func (m *monitor) Done() <-chan struct{} { return m.doneCh } func (m *monitor) Status() (*report.FanMonitorReport, error) { return m.status.report, m.status.err } func (m *monitor) processEvent(e Event, fanReport *report.FanMonitorReport) { fanReport.EventCount++ logger := m.logger.WithField("op", "processEvent") logger.Debugf("[%v] handling event %v", fanReport.EventCount, e) if m.del != nil { delEvent := &report.MonitorDataEvent{ Source: report.MDESourceFan, Type: report.MDETypeArtifact, Pid: e.Pid, Artifact: e.File, } if e.IsRead { delEvent.OpType = report.OpTypeRead } if e.IsWrite { delEvent.OpType = report.OpTypeWrite } if err := m.del.Publish(delEvent); err != nil { logger.Errorf( "mondel publish event failed - source=%v type=%v: %v", delEvent.Source, delEvent.Type, err, ) } } if _, ok := m.origPaths[e.File]; !ok && !m.includeNew { return } var newProcess *report.ProcessInfo if e.ID == 1 { //first event represents the main process if pinfo, err := getProcessInfo(e.Pid); (err == nil) && (pinfo != nil) { fanReport.MainProcess = pinfo fanReport.Processes[strconv.Itoa(int(e.Pid))] = pinfo newProcess = pinfo } } else { if _, ok := fanReport.Processes[strconv.Itoa(int(e.Pid))]; !ok { if pinfo, err := getProcessInfo(e.Pid); (err == nil) && (pinfo != nil) { // Ivan: PIDs can be reused, so we might be overwriting pinfo here. // But if we consider this probability as too low to care about, // then we should probably start caching getProcessInfo() calls :) fanReport.Processes[strconv.Itoa(int(e.Pid))] = pinfo newProcess = pinfo } } } if newProcess != nil && m.del != nil { delEvent := &report.MonitorDataEvent{ Source: report.MDESourceFan, Type: report.MDETypeProcess, Pid: newProcess.Pid, ParentPid: newProcess.ParentPid, Artifact: newProcess.Path, Cmd: newProcess.Cmd, WorkDir: newProcess.Cwd, Root: newProcess.Root, } if err := m.del.Publish(delEvent); err != nil { logger.Errorf( "mondel publish event failed - source=%v type=%v: %v", delEvent.Source, delEvent.Type, err, ) } } if _, ok := fanReport.ProcessFiles[strconv.Itoa(int(e.Pid))]; !ok { fanReport.ProcessFiles[strconv.Itoa(int(e.Pid))] = map[string]*report.FileInfo{} } if existingFi, ok := fanReport.ProcessFiles[strconv.Itoa(int(e.Pid))][e.File]; !ok { fi := &report.FileInfo{ EventCount: 1, Name: e.File, FirstEventID: e.ID, } if e.IsRead { fi.ReadCount = 1 } if e.IsWrite { fi.WriteCount = 1 } if pi, ok := fanReport.Processes[strconv.Itoa(int(e.Pid))]; ok && (e.File == pi.Path) { fi.ExeCount = 1 } fanReport.ProcessFiles[strconv.Itoa(int(e.Pid))][e.File] = fi } else { existingFi.EventCount++ if e.IsRead { existingFi.ReadCount++ } if e.IsWrite { existingFi.WriteCount++ } if pi, ok := fanReport.Processes[strconv.Itoa(int(e.Pid))]; ok && (e.File == pi.Path) { existingFi.ExeCount++ } } } func procFilePath(pid int, key string) string { return fmt.Sprintf(procFsFilePath, pid, key) } func getProcessInfo(pid int32) (*report.ProcessInfo, error) { info := &report.ProcessInfo{Pid: pid} var err error info.Path, err = os.Readlink(procFilePath(int(pid), "exe")) if err != nil { return nil, err } info.Cwd, err = os.Readlink(procFilePath(int(pid), "cwd")) if err != nil { return nil, err } info.Root, err = os.Readlink(procFilePath(int(pid), "root")) if err != nil { return nil, err } rawCmdline, err := os.ReadFile(procFilePath(int(pid), "cmdline")) if err != nil { return nil, err } if len(rawCmdline) > 0 { rawCmdline = bytes.TrimRight(rawCmdline, "\x00") //NOTE: later/future (when we do more app analytics) //split rawCmdline and resolve the "entry point" (exe or cmd param) info.Cmd = string(bytes.Replace(rawCmdline, []byte("\x00"), []byte(" "), -1)) } //note: will need to get "environ" at some point :) //rawEnviron, err := os.ReadFile(procFilePath(int(pid), "environ")) //if err != nil { // return nil, err //} //if len(rawEnviron) > 0 { // rawEnviron = bytes.TrimRight(rawEnviron,"\x00") // info.Env = strings.Split(string(rawEnviron),"\x00") //} info.Name = "unknown" info.ParentPid = -1 stat, err := os.ReadFile(procFilePath(int(pid), "stat")) if err == nil { var procPid int var procName string var procStatus string var procPpid int fmt.Sscanf(string(stat), "%d %s %s %d", &procPid, &procName, &procStatus, &procPpid) info.Name = procName[1 : len(procName)-1] info.ParentPid = int32(procPpid) } return info, nil } ================================================ FILE: pkg/app/sensor/monitor/pevent/monitor.go ================================================ //go:build linux // +build linux package pevent import ( "github.com/slimtoolkit/slim/pkg/pdiscover" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/util/errutil" log "github.com/sirupsen/logrus" ) //Process Event Monitor goal: //Watch the processes to separate the activity we care about from unrelated stuff running in the background. // Run starts the PEVENT monitor func Run(stopChan <-chan struct{}) <-chan *report.PeMonitorReport { log.Info("pemon: starting...") //"connection refused" with boot2docker... watcher, err := pdiscover.NewAllWatcher(pdiscover.PROC_EVENT_ALL) errutil.FailOn(err) reportChan := make(chan *report.PeMonitorReport, 1) go func() { peReport := &report.PeMonitorReport{ Children: map[int][]int{}, Parents: map[int]int{}, } done: for { select { case <-stopChan: log.Info("pemon: stopping...") break done case ev := <-watcher.Fork: peReport.Children[ev.ParentPid] = append(peReport.Children[ev.ParentPid], ev.ChildPid) peReport.Parents[ev.ChildPid] = ev.ParentPid case <-watcher.Exec: case <-watcher.Exit: case err := <-watcher.Error: errutil.FailOn(err) } } reportChan <- peReport watcher.Close() }() return reportChan } ================================================ FILE: pkg/app/sensor/monitor/ptrace/interface.go ================================================ package ptrace import ( "github.com/slimtoolkit/slim/pkg/monitor/ptrace" "github.com/slimtoolkit/slim/pkg/report" ) type AppRunOpt = ptrace.AppRunOpt type Monitor interface { // Starts the long running monitoring. The method itself is not // blocking and not reentrant! Start() error // Cancels the underlying ptrace execution context but doesn't // make the current monitor done immediately. You still need to await // the final cleanup with <-mon.Done() before accessing the status. Cancel() // With Done clients can await for the monitoring completion. // The method is reentrant - every invocation returns the same // instance of the channel. Done() <-chan struct{} Status() (*report.PtMonitorReport, error) } ================================================ FILE: pkg/app/sensor/monitor/ptrace/monitor.go ================================================ //go:build !arm64 // +build !arm64 package ptrace import ( "context" "fmt" "os" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/errors" "github.com/slimtoolkit/slim/pkg/mondel" "github.com/slimtoolkit/slim/pkg/monitor/ptrace" "github.com/slimtoolkit/slim/pkg/report" ) type status struct { report *report.PtMonitorReport err error } type monitor struct { ctx context.Context cancel context.CancelFunc del mondel.Publisher artifactsDir string runOpt AppRunOpt // TODO: Move the logic behind these two fields to the artifact processig stage. includeNew bool origPaths map[string]struct{} // To receive signals that should be delivered to the target app. signalCh <-chan os.Signal errorCh chan<- error app *ptrace.App status status doneCh chan struct{} logger *log.Entry } func NewMonitor( ctx context.Context, del mondel.Publisher, artifactsDir string, runOpt AppRunOpt, includeNew bool, origPaths map[string]struct{}, signalCh <-chan os.Signal, errorCh chan<- error, ) Monitor { logger := log.WithFields(log.Fields{ "app": "sensor", "com": "ptmon", }) ctx, cancel := context.WithCancel(ctx) return &monitor{ ctx: ctx, cancel: cancel, del: del, artifactsDir: artifactsDir, runOpt: runOpt, includeNew: includeNew, origPaths: origPaths, signalCh: signalCh, errorCh: errorCh, doneCh: make(chan struct{}), logger: logger, } } func (m *monitor) Start() error { logger := m.logger.WithField("op", "sensor.pt.monitor.Start") logger.Info("call") defer logger.Info("exit") logger.WithFields(log.Fields{ "name": m.runOpt.Cmd, "args": m.runOpt.Args, }).Debug("starting target app...") // Despite the name, ptrace.Run() is not blocking. app, err := ptrace.Run( m.ctx, m.del, m.runOpt, m.includeNew, m.origPaths, m.signalCh, m.errorCh, ) if err != nil { return errors.SE("sensor.ptrace.Run/ptrace.Run", "call.error", err) } m.app = app appState := <-app.StateCh logger. WithField("state", appState). Debugf("pta state watcher - new target app state") if appState == ptrace.AppFailed { // Don't need to wait for the 'done' state. logger.Error("pta state watcher - target app failed") return fmt.Errorf("ptmon: target app startup failed: %q", appState) } if appState != ptrace.AppStarted { // Cannot really happen. logger.Error("pta state watcher - unexpected target app state") return fmt.Errorf("ptmon: unexpected target app state %q", appState) } // The sync part of the start was successful. // Tracking the completetion of the monitor. go func() { logger := m.logger.WithField("op", "sensor.pt.monitor.completetion.monitor") logger.Info("call") defer logger.Info("exit") appState := <-app.StateCh if appState == ptrace.AppDone { m.status.report = <-app.ReportCh } else { m.status.err = fmt.Errorf("ptmon: target app failed with state %q", appState) } // Monitor is done. close(m.doneCh) }() return nil } func (m *monitor) Cancel() { m.cancel() } func (m *monitor) Done() <-chan struct{} { return m.doneCh } func (m *monitor) Status() (*report.PtMonitorReport, error) { return m.status.report, m.status.err } ================================================ FILE: pkg/app/sensor/monitor/ptrace/monitor_arm64.go ================================================ //go:build arm64 // +build arm64 package ptrace import ( "context" "os" "os/exec" "runtime" "strconv" "syscall" log "github.com/sirupsen/logrus" "golang.org/x/sys/unix" "github.com/slimtoolkit/slim/pkg/errors" "github.com/slimtoolkit/slim/pkg/launcher" "github.com/slimtoolkit/slim/pkg/mondel" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/system" ) type syscallEvent struct { callNum uint32 retVal uint64 } const ( eventBufSize = 500 ptOptions = unix.PTRACE_O_TRACECLONE | unix.PTRACE_O_TRACEFORK | unix.PTRACE_O_TRACEVFORK ) /* unix.PTRACE_O_EXITKILL| unix.PTRACE_O_TRACESECCOMP| syscall.PTRACE_O_TRACESYSGOOD| syscall.PTRACE_O_TRACEEXEC| syscall.PTRACE_O_TRACECLONE| syscall.PTRACE_O_TRACEFORK| syscall.PTRACE_O_TRACEVFORK syscall.PTRACE_O_TRACECLONE|syscall.PTRACE_O_TRACEEXIT */ type status struct { report *report.PtMonitorReport err error } type monitor struct { ctx context.Context cancel context.CancelFunc del mondel.Publisher artifactsDir string runOpt AppRunOpt // TODO: Move the logic behind these two fields to the artifact processig stage. includeNew bool origPaths map[string]struct{} // To receive signals that should be delivered to the target app. signalCh <-chan os.Signal status status doneCh chan struct{} } func NewMonitor( ctx context.Context, del mondel.Publisher, artifactsDir string, runOpt AppRunOpt, includeNew bool, origPaths map[string]struct{}, signalCh <-chan os.Signal, errorCh chan<- error, ) Monitor { ctx, cancel := context.WithCancel(ctx) return &monitor{ ctx: ctx, cancel: cancel, del: del, artifactsDir: artifactsDir, runOpt: runOpt, includeNew: includeNew, origPaths: origPaths, signalCh: signalCh, doneCh: make(chan struct{}), } } func (m *monitor) Start() error { logger := log.WithField("op", "sensor.pt.monitor.Start") logger.Info("call") defer logger.Info("exit") logger.WithFields(log.Fields{ "name": m.runOpt.Cmd, "args": m.runOpt.Args, }).Debug("starting target app...") sysInfo := system.GetSystemInfo() archName := system.MachineToArchName(sysInfo.Machine) syscallResolver := system.CallNumberResolver(archName) appName := m.runOpt.Cmd appArgs := m.runOpt.Args workDir := m.runOpt.WorkDir appUser := m.runOpt.User runTargetAsUser := m.runOpt.RunAsUser rtaSourcePT := m.runOpt.RTASourcePT // TODO(ivan): Implement the runOpt.ReportOnMainPidExit handling. // The sync part of the start was successful. // Starting the async part... go func() { logger := log.WithField("op", "sensor.pt.monitor.processor") logger.Debug("call") defer logger.Debug("exit") ptReport := &report.PtMonitorReport{ ArchName: string(archName), SyscallStats: map[string]report.SyscallStatInfo{}, } syscallStats := map[uint32]uint64{} eventChan := make(chan syscallEvent, eventBufSize) collectorDoneChan := make(chan int, 1) var app *exec.Cmd go func() { logger := log.WithField("op", "sensor.pt.monitor.collector") logger.Debug("call") defer logger.Debug("exit") //IMPORTANT: //Ptrace is not pretty... and it requires that you do all ptrace calls from the same thread runtime.LockOSThread() var err error app, err = launcher.Start( appName, appArgs, workDir, appUser, runTargetAsUser, rtaSourcePT, m.runOpt.AppStdout, m.runOpt.AppStderr, ) if err != nil { m.status.err = errors.SE("sensor.ptrace.Run/launcher.Start", "call.error", err) close(m.doneCh) return } // TODO: Apparently, rtaSourcePT is ignored by this below code. // The x86-64 version of it has an alternative code branch // to run the target app w/o tracing. cancelSignalForwarding := startSignalForwarding(m.ctx, app, m.signalCh) defer cancelSignalForwarding() targetPid := app.Process.Pid //pgid, err := syscall.Getpgid(targetPid) //if err != nil { // log.Warnf("ptmon: collector - getpgid error %d: %v", targetPid, err) // collectorDoneChan <- 1 // return //} logger.Debugf("target PID ==> %d", targetPid) var wstat unix.WaitStatus //pid, err := syscall.Wait4(-1, &wstat, syscall.WALL, nil) - WIP pid, err := unix.Wait4(targetPid, &wstat, 0, nil) if err != nil { logger.Warnf("unix.Wait4 - error waiting for %d: %v", targetPid, err) collectorDoneChan <- 2 return } //err = syscall.PtraceSetOptions(targetPid, ptOptions) //if err != nil { // log.Warnf("ptmon: collector - error setting trace options %d: %v", targetPid, err) // collectorDoneChan <- 3 // return //} logger.Debugf("initial process status = %v (pid=%d)\n", wstat, pid) if wstat.Exited() { logger.Warn("app exited (unexpected)") collectorDoneChan <- 4 return } if wstat.Signaled() { logger.Warn("app signalled (unexpected)") collectorDoneChan <- 5 return } syscallReturn := false gotCallNum := false gotRetVal := false var callNum uint64 var retVal uint64 for wstat.Stopped() { var regs unix.PtraceRegsArm64 switch syscallReturn { case false: logger.Infof("target pid is %d", targetPid) if err := unix.PtraceGetRegSetArm64(targetPid, 1, ®s); err != nil { //if err := syscall.PtraceGetRegs(pid, ®s); err != nil { logger.Fatalf("unix.PtraceGetRegsArm64(call): %v", err) } callNum = system.CallNumber(regs) syscallReturn = true gotCallNum = true case true: if err := unix.PtraceGetRegSetArm64(targetPid, 1, ®s); err != nil { //if err := syscall.PtraceGetRegs(pid, ®s); err != nil { logger.Fatalf("unix.PtraceGetRegsArm64(return): %v", err) } retVal = system.CallReturnValue(regs) syscallReturn = false gotRetVal = true } //err = syscall.PtraceSyscall(pid, 0) err = unix.PtraceSyscall(targetPid, 0) if err != nil { logger.Warnf("unix.PtraceSyscall error: %v", err) break } //pid, err = syscall.Wait4(-1, &wstat, syscall.WALL, nil) pid, err = unix.Wait4(targetPid, &wstat, 0, nil) if err != nil { logger.Warnf("unix.Wait4 - error waiting 4 %d: %v", targetPid, err) break } if gotCallNum && gotRetVal { gotCallNum = false gotRetVal = false select { case eventChan <- syscallEvent{ callNum: uint32(callNum), retVal: retVal, }: case <-m.ctx.Done(): logger.Info("stopping...") return } } } logger.Infof("exiting... status=%v", wstat) collectorDoneChan <- 0 }() done: for { select { case rc := <-collectorDoneChan: logger.Info("collector finished =>", rc) break done case <-m.ctx.Done(): logger.Info("stopping...") //NOTE: need a better way to stop the target app... if err := app.Process.Signal(unix.SIGTERM); err != nil { logger.Warnf("app.Process.Signal(unix.SIGTERM) - error stopping target app => %v", err) if err := app.Process.Kill(); err != nil { logger.Warnf("app.Process.Kill - error killing target app => %v") } } break done case e := <-eventChan: ptReport.SyscallCount++ logger.Tracef("syscall ==> %d", e.callNum) if _, ok := syscallStats[e.callNum]; ok { syscallStats[e.callNum]++ } else { syscallStats[e.callNum] = 1 } } } logger.Debugf("executed syscall count = %d", ptReport.SyscallCount) logger.Debugf("number of syscalls: %v", len(syscallStats)) for scNum, scCount := range syscallStats { logger.Tracef("%v", syscallResolver(scNum)) logger.Tracef("[%v] %v = %v", scNum, syscallResolver(scNum), scCount) ptReport.SyscallStats[strconv.FormatInt(int64(scNum), 10)] = report.SyscallStatInfo{ Number: scNum, Name: syscallResolver(scNum), Count: scCount, } } ptReport.SyscallNum = uint32(len(ptReport.SyscallStats)) m.status.report = ptReport close(m.doneCh) }() return nil } func (m *monitor) Cancel() { m.cancel() } func (m *monitor) Done() <-chan struct{} { return m.doneCh } func (m *monitor) Status() (*report.PtMonitorReport, error) { return m.status.report, m.status.err } func startSignalForwarding( ctx context.Context, app *exec.Cmd, signalCh <-chan os.Signal, ) context.CancelFunc { log.Debug("ptmon: signal forwarder - starting...") ctx, cancel := context.WithCancel(ctx) go func() { for { select { case <-ctx.Done(): return case s := <-signalCh: log.WithField("signal", s).Debug("ptmon: signal forwarder - received signal") if s == syscall.SIGCHLD { continue } log.WithField("signal", s).Debug("ptmon: signal forwarder - forwarding signal") if err := app.Process.Signal(s); err != nil { log. WithError(err). WithField("signal", s). Debug("ptmon: signal forwarder - failed to signal target app") } } } }() return cancel } ================================================ FILE: pkg/app/sensor/signals.go ================================================ //go:build linux // +build linux package sensor import ( "os" "os/signal" "strings" "syscall" log "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) var signals = []os.Signal{ os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGHUP, syscall.SIGSTOP, syscall.SIGCONT, } func startSystemSignalsMonitor(cleanup func()) { sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, signals...) go func() { sig := <-sigChan log.Debugf("sensor: cleanup on signal (%v)...", sig) cleanup() os.Exit(0) }() } func signalFromString(s string) syscall.Signal { if !strings.HasPrefix(s, "SIG") { s = "SIG" + s } return unix.SignalNum(s) } ================================================ FILE: pkg/app/sensor/standalone/control/commands.go ================================================ package control type Command string const ( StopTargetAppCommand Command = "stop-target-app" WaitForEventCommand Command = "wait-for-event" ) ================================================ FILE: pkg/app/sensor/standalone/control/queue.go ================================================ package control import ( "bufio" "context" "io" "os" "syscall" "time" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/ipc/command" ) func HandleControlCommandQueue(ctx context.Context, commandsFile string, commandCh chan command.Message) { fifoPath := getFIFOPath(commandsFile) if !createFIFOIfNeeded(fifoPath) { return } go func() { <-ctx.Done() os.Remove(fifoPath) }() processCommandsFromFIFO(ctx, fifoPath, commandCh) } func getFIFOPath(commandsFile string) string { return commandsFile + ".fifo" } func createFIFOIfNeeded(fifoPath string) bool { if _, err := os.Stat(fifoPath); os.IsNotExist(err) { if err = syscall.Mkfifo(fifoPath, 0600); err != nil { log.Warnf("sensor: control commands not activated - cannot create %s FIFO file: %s", fifoPath, err) return false } log.Info("sensor: control commands activated") } return true } func processCommandsFromFIFO(ctx context.Context, fifoPath string, commandCh chan command.Message) { for ctx.Err() == nil { fifo, err := os.Open(fifoPath) if err != nil { log.Debugf("sensor: control commands - cannot open %s FIFO file: %s", fifoPath, err) time.Sleep(1 * time.Second) continue } readAndHandleCommands(fifo, commandCh) fifo.Close() } } func readAndHandleCommands(fifo *os.File, commandCh chan command.Message) { reader := bufio.NewReader(fifo) for { line, err := reader.ReadBytes('\n') if len(line) > 0 { handleCommand(line, commandCh) } if err == io.EOF { return } if err != nil { log.Warnf("sensor: error reading control command: %s", err) time.Sleep(1 * time.Second) } } } func handleCommand(line []byte, commandCh chan command.Message) { msg, err := command.Decode(line) if err == nil { commandCh <- msg } else { log.Warnf("sensor: cannot decode control command %#q: %s", line, err) } } ================================================ FILE: pkg/app/sensor/standalone/control/stop.go ================================================ package control import ( "context" "fmt" "github.com/slimtoolkit/slim/pkg/ipc/command" "github.com/slimtoolkit/slim/pkg/util/fsutil" ) func ExecuteStopTargetAppCommand(ctx context.Context, commandsFile string) error { msg, err := command.Encode(&command.StopMonitor{}) if err != nil { return fmt.Errorf("cannot encode stop command: %w", err) } if err := fsutil.AppendToFile(getFIFOPath(commandsFile), msg, false); err != nil { return fmt.Errorf("cannot append stop command to FIFO file: %w", err) } return nil } ================================================ FILE: pkg/app/sensor/standalone/control/wait.go ================================================ package control import ( "bufio" "context" "fmt" "os" "strings" "time" "github.com/slimtoolkit/slim/pkg/ipc/event" ) func ExecuteWaitEvenCommand( ctx context.Context, eventsFile string, evt event.Type, ) error { if err := waitForEvent(ctx, eventsFile, evt); err != nil { return fmt.Errorf("waiting for %v event: %w", evt, err) } return nil } func waitForEvent(ctx context.Context, eventsFile string, target event.Type) error { for ctx.Err() == nil { found, err := findEvent(eventsFile, target) if err != nil { return err } if found { return nil } time.Sleep(1 * time.Second) } return ctx.Err() } func findEvent(eventsFile string, target event.Type) (bool, error) { file, err := os.Open(eventsFile) if err != nil { return false, err } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() // A bit hacky - we probably need to parse the event struct properly. if strings.Contains(line, string(target)) { return true, nil } } if scanner.Err() != nil { return false, scanner.Err() } return false, nil } ================================================ FILE: pkg/app/sensor/standalone/standalone.go ================================================ package standalone import ( "context" "fmt" "os" "os/signal" "syscall" "time" log "github.com/sirupsen/logrus" "golang.org/x/sys/unix" "github.com/slimtoolkit/slim/pkg/app/sensor/artifact" "github.com/slimtoolkit/slim/pkg/app/sensor/execution" "github.com/slimtoolkit/slim/pkg/app/sensor/monitor" "github.com/slimtoolkit/slim/pkg/ipc/command" "github.com/slimtoolkit/slim/pkg/ipc/event" "github.com/slimtoolkit/slim/pkg/mondel" "github.com/slimtoolkit/slim/pkg/util/errutil" ) type Sensor struct { ctx context.Context exe execution.Interface newMonitor monitor.NewCompositeMonitorFunc del mondel.Publisher artifactor artifact.Processor workDir string mountPoint string signalCh chan os.Signal stopSignal os.Signal stopGracePeriod time.Duration stopCommandReceived bool } func NewSensor( ctx context.Context, exe execution.Interface, newMonitor monitor.NewCompositeMonitorFunc, del mondel.Publisher, artifactor artifact.Processor, workDir string, mountPoint string, stopSignal os.Signal, stopGracePeriod time.Duration, ) *Sensor { return &Sensor{ ctx: ctx, exe: exe, newMonitor: newMonitor, del: del, artifactor: artifactor, workDir: workDir, mountPoint: mountPoint, stopSignal: stopSignal, stopGracePeriod: stopGracePeriod, } } func (s *Sensor) Run() error { s.exe.HookSensorPostStart() err := s.run() if err != nil { s.exe.PubEvent(event.Error, err.Error()) } // We have to dump the artifacts before invokin the pre-shutdown // hook - it may want to upload the artifacts somewhere. errutil.WarnOn(s.artifactor.Archive()) s.exe.HookSensorPreShutdown() s.exe.PubEvent(event.ShutdownSensorDone) // The target app can be done before the stop signal is received // because of two reasons: // // - App terminated on its own (e.g., a typical CLI use case) // // - The "stop monitor" control command was received. // // In the latter case, sensor needs to wait for the stop signal // before proceeding with the shutdown because otherwise the container // runtime may restart the container which is not desirable. if s.stopCommandReceived { select { case <-s.ctx.Done(): case <-s.signalCh: } } return err } func (s *Sensor) run() error { raw := <-s.exe.Commands() cmd, ok := (raw).(*command.StartMonitor) if !ok { log. WithField("cmd", fmt.Sprintf("%+v", cmd)). Error("sensor: unexpected start monitor command") s.exe.HookMonitorFailed() s.exe.PubEvent(event.StartMonitorFailed, &event.StartMonitorFailedData{ Component: event.ComSensorCmdServer, State: event.StateCmdStartMonCmdWaiting, Context: map[string]string{ event.CtxCmdType: string(raw.GetName()), }, }) return fmt.Errorf("unexpected start monitor command: %+v", cmd) } if err := s.artifactor.PrepareEnv(cmd); err != nil { log.WithError(err).Error("sensor: artifactor.PrepareEnv() failed") s.exe.HookMonitorFailed() s.exe.PubEvent(event.StartMonitorFailed, &event.StartMonitorFailedData{ Component: event.ComSensorCmdServer, State: event.StateEnvPreparing, Errors: []string{err.Error()}, }) return fmt.Errorf("failed to prepare artifacts env: %w", err) } origPaths, err := s.artifactor.GetCurrentPaths(s.mountPoint, cmd.Excludes) if err != nil { log.WithError(err).Error("sensor: artifactor.GetCurrentPaths() failed") return fmt.Errorf("failed to enumerate current paths: %w", err) } s.exe.HookMonitorPreStart() mon, err := s.newMonitor( s.ctx, cmd, s.workDir, s.del, s.artifactor.ArtifactsDir(), s.mountPoint, origPaths, ) if err != nil { log.WithError(err).Error("sensor: failed to create composite monitor") s.exe.HookMonitorFailed() s.exe.PubEvent(event.StartMonitorFailed, &event.StartMonitorFailedData{ Component: event.ComSensorCmdServer, State: event.StateMonCreating, Errors: []string{err.Error()}, }) return err } s.signalCh = make(chan os.Signal, 1024) go s.runSignalForwarder(mon) if err := mon.Start(); err != nil { log.WithError(err).Error("sensor: failed to start composite monitor") s.exe.HookMonitorFailed() s.exe.PubEvent(event.StartMonitorFailed, &event.StartMonitorFailedData{ Component: event.ComSensorCmdServer, State: event.StateMonStarting, Errors: []string{err.Error()}, }) return err } s.exe.PubEvent(event.StartMonitorDone) s.runMonitor(mon) report, err := mon.Status() if err != nil { log.WithError(err).Error("sensor: target app failed") return err } log.Info("sensor: target app is done") s.exe.HookMonitorPostShutdown() s.exe.PubEvent(event.StopMonitorDone) if err := s.artifactor.Process( cmd, s.mountPoint, report.PeReport, report.FanReport, report.PtReport, ); err != nil { log.WithError(err).Error("sensor: artifact.Process() failed") return fmt.Errorf("saving reports failed: %w", err) } return nil } func (s *Sensor) runMonitor(mon monitor.CompositeMonitor) { ticker := time.NewTicker(time.Second * 5) defer ticker.Stop() loop: for { select { case <-mon.Done(): break loop case cmd := <-s.exe.Commands(): log.Infof("sensor: recieved control command => %s", cmd.GetName()) if cmd.GetName() == command.StopMonitorName { s.stopCommandReceived = true s.signalTargetApp(mon, s.stopSignal) } else { log.Warnf("sensor: unsupported control command => %s", cmd.GetName()) } case err := <-mon.Errors(): log.WithError(err).Warn("sensor: non-critical monitor error condition") s.exe.PubEvent(event.Error, monitor.NonCriticalError(err).Error()) case <-ticker.C: s.exe.HookTargetAppRunning() log.Debug(".") } // TODO: Implement me! // case file := <-mon.Files(): // Stub for the incremental artifact storing. } // A bit of code duplication to avoid starting a goroutine // for error event handling - keeping the control flow // "single-threaded" keeps reasoning about the logic. for _, err := range mon.DrainErrors() { log.WithError(err).Warn("sensor: non-critical monitor error condition (drained)") s.exe.PubEvent(event.Error, monitor.NonCriticalError(err).Error()) } } // TODO: Combine the signal forwarder loop with the run monitor loop // // to avoid competting event loops - this will simplify the code // and let us avoid subtle race conditions when the stop signal // arrives while the app is being stoped due to the stop control command. func (s *Sensor) runSignalForwarder(mon monitor.CompositeMonitor) { log.Debug("sensor: starting forwarding signals to target app...") signal.Notify(s.signalCh) for { select { case <-s.ctx.Done(): log.Debug("sensor: forwarding signal to target app no more - sensor is done") return case sig := <-s.signalCh: if s.stopCommandReceived && sig == s.stopSignal { signal.Stop(s.signalCh) close(s.signalCh) } select { case <-mon.Done(): log.Debug("sensor: skipping signal forwarding - target app is done") default: if sig != syscall.SIGCHLD { // Due to ptrace, SIGCHLDs flood the output. // TODO: Log SIGCHLD if ptrace-ing is off. log.Debugf("sensor: forwarding signal %s to target app", unix.SignalName(sig.(syscall.Signal))) } s.signalTargetApp(mon, sig) } } } } func (s *Sensor) signalTargetApp(mon monitor.CompositeMonitor, sig os.Signal) { mon.SignalTargetApp(sig) if sig == s.stopSignal { log.Debug("sensor: stop signal was sent to target app - starting grace period") go func() { // Starting the grace period. timer := time.NewTimer(s.stopGracePeriod) defer func() { if !timer.Stop() { <-timer.C } }() select { case <-mon.Done(): log.Debug("sensor: target app finished before grace timeout - dismantling SIGKILL") case <-timer.C: log.Debug("sensor: grace timeout expired - SIGKILL goes to target app") mon.SignalTargetApp(syscall.SIGKILL) } }() } } ================================================ FILE: pkg/appbom/.gitignore ================================================ /gobinhash ================================================ FILE: pkg/appbom/appbom.go ================================================ /* There are two ways to provide the Go executable hash to "appbom": 1. "go generate" and "embed" 2. "-ldflags" Using "go generate" to hash the Go binary: go generate ./... go generate github.com/slimtoolkit/slim/pkg/appbom With "go generate" you also need to use embedding (enabled by default). If you can't use "embed" you can disable it with the "appbom_noembed" tag: go build -tags appbom_noembed If you disable embedding then you'll need to pass the Go executable hash using "-ldflags": Mac: go build -ldflags "-X github.com/slimtoolkit/slim/pkg/appbom.GoBinHash=sha256:$(shasum -a 256 $(go env GOROOT)/bin/go | head -c 64)" Linux: go build -ldflags "-X github.com/slimtoolkit/slim/pkg/appbom.GoBinHash=sha256:$(sha256sum $(go env GOROOT)/bin/go | head -c 64)" You can use "-ldflags" instead of go generate/embed if that approach works better for you. */ package appbom //go:generate go run gobinhasher.go import ( "fmt" "path/filepath" "runtime/debug" ) // Known Settings key names const ( SettingBuildMode = "-buildmode" // the buildmode flag used SettingCompiler = "-compiler" // the compiler toolchain flag used SettingTags = "-tags" SettingTrimPath = "-trimpath" SettingLdFlags = "-ldflags" SettingMod = "-mod" SettingEnvVarCgoEnabled = "CGO_ENABLED" // the effective CGO_ENABLED environment variable SettingEnvVarCgoCFlags = "CGO_CFLAGS" // the effective CGO_CFLAGS environment variable SettingEnvVarCgoCppFlags = "CGO_CPPFLAGS" // the effective CGO_CPPFLAGS environment variable SettingEnvVarCgoCxxFlags = "CGO_CXXFLAGS" // the effective CGO_CXXFLAGS environment variable SettingEnvVarCgoLdFlags = "CGO_LDFLAGS" // the effective CGO_LDFLAGS environment variable SettingEnvVarGoOs = "GOOS" // the operating system target SettingEnvVarGoArch = "GOARCH" // the architecture target // the architecture feature level for GOARCH SettingEnvVarGoAmd64 = "GOAMD64" SettingEnvVarGoArm64 = "GOARM64" SettingEnvVarGoArm = "GOARM" SettingEnvVarGo386 = "GO386" SettingEnvVarGoPpc64 = "GOPPC64" SettingEnvVarGoMips = "GOMIPS" SettingEnvVarGoMips64 = "GOMIPS64" SettingEnvVarGoWasm = "GOWASM" SettingVcsType = "vcs" // the version control system for the source tree where the build ran SettingVcsRevision = "vcs.revision" // the revision identifier for the current commit or checkout SettingVcsTime = "vcs.time" // the modification time associated with vcs.revision, in RFC3339 format SettingVcsModified = "vcs.modified" // true or false indicating whether the source tree had local modifications ) //todo: expose the setting description as a 'details'/'summary' field type MainPackageInfo struct { Path string `json:"path"` //todo: add 'main' Version string `json:"version"` } type PackageInfo struct { Name string `json:"name"` Version string `json:"version"` Hash string `json:"hash"` // 'h1' algo is sha256: https://go.dev/ref/mod#go-sum-files Path string `json:"path"` ReplacedBy string `json:"replaced_by,omitempty"` } type SourceControlInfo struct { Type string `json:"type"` Revision string `json:"revision"` RevisionTime string `json:"revision_time"` HasLocalChanges bool `json:"has_local_changes"` } type ParamType string const ( PTFlag ParamType = "flag" PTEnvVar = "envvar" ) type ParamName string const ( PNBuildMode = ParamName(SettingBuildMode) PNCompiler = ParamName(SettingCompiler) PNTags = ParamName(SettingTags) PNTrimPath = ParamName(SettingTrimPath) PNLdFlags = ParamName(SettingLdFlags) PNMod = ParamName(SettingMod) PNCgoEnabled = ParamName(SettingEnvVarCgoEnabled) PNCgoCFlags = ParamName(SettingEnvVarCgoCFlags) PNCgoCppFlags = ParamName(SettingEnvVarCgoCppFlags) PNCgoCxxFlags = ParamName(SettingEnvVarCgoCxxFlags) PNCgoLdFlags = ParamName(SettingEnvVarCgoLdFlags) PNGoOs = ParamName(SettingEnvVarGoOs) PNGoArch = ParamName(SettingEnvVarGoArch) PNGoAmd64 = ParamName(SettingEnvVarGoAmd64) PNGoArm64 = ParamName(SettingEnvVarGoArm64) PNGoArm = ParamName(SettingEnvVarGoArm) PNGo386 = ParamName(SettingEnvVarGo386) PNGoPpc64 = ParamName(SettingEnvVarGoPpc64) PNGoMips = ParamName(SettingEnvVarGoMips) PNGoMips64 = ParamName(SettingEnvVarGoMips64) PNGoWasm = ParamName(SettingEnvVarGoWasm) ) type ParamInfo struct { Name ParamName `json:"name"` Type ParamType `json:"type"` Value string `json:"value"` Description string `json:"description,omitempty"` } type BuildParams struct { BuildMode *ParamInfo `json:"build_mode,omitempty"` Compiler *ParamInfo `json:"compiler,omitempty"` CgoEnabled *ParamInfo `json:"cgo_enabled,omitempty"` CgoCFlags *ParamInfo `json:"cgo_cflags,omitempty"` CgoCppFlags *ParamInfo `json:"cgo_cppflags,omitempty"` CgoCxxFlags *ParamInfo `json:"cgo_cxxflags,omitempty"` CgoLdFlags *ParamInfo `json:"cgo_ldflags,omitempty"` Os *ParamInfo `json:"os,omitempty"` Arch *ParamInfo `json:"arch,omitempty"` ArchFeature *ParamInfo `json:"arch_feature,omitempty"` } type Info struct { BuilderHash string `json:"builder_hash,omitempty"` Runtime string `json:"runtime"` Entrypoint MainPackageInfo `json:"entrypoint"` BuildParams BuildParams `json:"build_params"` OtherParams map[string]string `json:"other_params,omitempty"` SourceControl *SourceControlInfo `json:"source_control,omitempty"` Includes []*PackageInfo `json:"includes,omitempty"` } func Get() *Info { raw, ok := debug.ReadBuildInfo() if !ok { return nil } info := &Info{ BuilderHash: goBinHash, Runtime: raw.GoVersion, OtherParams: map[string]string{}, } if raw.Path != "" { if raw.Path == "command-line-arguments" { info.Entrypoint.Path = "unknown.prefix/main" } else { info.Entrypoint.Path = fmt.Sprintf("%s/main", raw.Path) } } if raw.Main.Path != "" { info.Entrypoint.Path = fmt.Sprintf("%s/main", raw.Main.Path) } info.Entrypoint.Version = raw.Main.Version for _, kv := range raw.Settings { switch kv.Key { case SettingVcsType: if info.SourceControl == nil { info.SourceControl = &SourceControlInfo{} } info.SourceControl.Type = kv.Value case SettingVcsRevision: if info.SourceControl == nil { info.SourceControl = &SourceControlInfo{} } info.SourceControl.Revision = kv.Value case SettingVcsTime: if info.SourceControl == nil { info.SourceControl = &SourceControlInfo{} } info.SourceControl.RevisionTime = kv.Value case SettingVcsModified: if info.SourceControl == nil { info.SourceControl = &SourceControlInfo{} } switch kv.Value { case "true", "TRUE": info.SourceControl.HasLocalChanges = true } case SettingBuildMode: info.BuildParams.BuildMode = &ParamInfo{ Name: PNBuildMode, Type: PTFlag, Value: kv.Value, } case SettingCompiler: info.BuildParams.Compiler = &ParamInfo{ Name: PNCompiler, Type: PTFlag, Value: kv.Value, } case SettingTags: info.BuildParams.Compiler = &ParamInfo{ Name: PNTags, Type: PTFlag, Value: kv.Value, } case SettingTrimPath: info.BuildParams.Compiler = &ParamInfo{ Name: PNTrimPath, Type: PTFlag, Value: kv.Value, } case SettingLdFlags: info.BuildParams.Compiler = &ParamInfo{ Name: PNLdFlags, Type: PTFlag, Value: kv.Value, } case SettingMod: info.BuildParams.Compiler = &ParamInfo{ Name: PNMod, Type: PTFlag, Value: kv.Value, } case SettingEnvVarCgoEnabled: info.BuildParams.CgoEnabled = &ParamInfo{ Name: PNCgoEnabled, Type: PTEnvVar, Value: kv.Value, } case SettingEnvVarCgoCFlags: info.BuildParams.CgoCFlags = &ParamInfo{ Name: PNCgoCFlags, Type: PTEnvVar, Value: kv.Value, } case SettingEnvVarCgoCppFlags: info.BuildParams.CgoCppFlags = &ParamInfo{ Name: PNCgoCppFlags, Type: PTEnvVar, Value: kv.Value, } case SettingEnvVarCgoCxxFlags: info.BuildParams.CgoCxxFlags = &ParamInfo{ Name: PNCgoCxxFlags, Type: PTEnvVar, Value: kv.Value, } case SettingEnvVarCgoLdFlags: info.BuildParams.CgoLdFlags = &ParamInfo{ Name: PNCgoLdFlags, Type: PTEnvVar, Value: kv.Value, } case SettingEnvVarGoOs: info.BuildParams.Os = &ParamInfo{ Name: PNGoOs, Type: PTEnvVar, Value: kv.Value, } case SettingEnvVarGoArch: info.BuildParams.Arch = &ParamInfo{ Name: PNGoArch, Type: PTEnvVar, Value: kv.Value, } // the architecture feature level for GOARCH case SettingEnvVarGoAmd64: info.BuildParams.ArchFeature = &ParamInfo{ Name: PNGoAmd64, Type: PTEnvVar, Value: kv.Value, } case SettingEnvVarGoArm64: info.BuildParams.ArchFeature = &ParamInfo{ Name: PNGoArm64, Type: PTEnvVar, Value: kv.Value, } case SettingEnvVarGoArm: info.BuildParams.ArchFeature = &ParamInfo{ Name: PNGoArm, Type: PTEnvVar, Value: kv.Value, } case SettingEnvVarGo386: info.BuildParams.ArchFeature = &ParamInfo{ Name: PNGo386, Type: PTEnvVar, Value: kv.Value, } case SettingEnvVarGoPpc64: info.BuildParams.ArchFeature = &ParamInfo{ Name: PNGoPpc64, Type: PTEnvVar, Value: kv.Value, } case SettingEnvVarGoMips: info.BuildParams.ArchFeature = &ParamInfo{ Name: PNGoMips, Type: PTEnvVar, Value: kv.Value, } case SettingEnvVarGoMips64: info.BuildParams.ArchFeature = &ParamInfo{ Name: PNGoMips64, Type: PTEnvVar, Value: kv.Value, } case SettingEnvVarGoWasm: info.BuildParams.ArchFeature = &ParamInfo{ Name: PNGoWasm, Type: PTEnvVar, Value: kv.Value, } default: info.OtherParams[kv.Key] = kv.Value } } includeMap := map[string]*PackageInfo{} for _, depData := range raw.Deps { if _, found := includeMap[depData.Path]; found { continue } pkg := &PackageInfo{ Name: filepath.Base(depData.Path), Path: depData.Path, Version: depData.Version, Hash: depData.Sum, } includeMap[depData.Path] = pkg for depData.Replace != nil { pkg.ReplacedBy = depData.Replace.Path if _, found := includeMap[depData.Replace.Path]; found { break } pkg = &PackageInfo{ Name: filepath.Base(depData.Replace.Path), Path: depData.Replace.Path, Version: depData.Replace.Version, Hash: depData.Replace.Sum, } includeMap[depData.Replace.Path] = pkg depData.Replace = depData.Replace.Replace } } for _, pkg := range includeMap { info.Includes = append(info.Includes, pkg) } return info } //TODO: have an option to add package metadata (from deps.dev and osv.dev) ================================================ FILE: pkg/appbom/gobinhash.go ================================================ //go:build !appbom_noembed // +build !appbom_noembed package appbom import ( _ "embed" ) //go:embed gobinhash var goBinHash string ================================================ FILE: pkg/appbom/gobinhash_noembed.go ================================================ //go:build appbom_noembed // +build appbom_noembed package appbom var goBinHash string ================================================ FILE: pkg/appbom/gobinhasher.go ================================================ //go:build ignore // +build ignore package main import ( "crypto/sha256" "fmt" "io" "os" "os/exec" ) func main() { fullPath, err := getGoExeFullPath() if err != nil { fmt.Println("Error:", err) return } fmt.Printf("found go binary: %s\n", fullPath) hash, err := hashFile(fullPath) if err != nil { fmt.Println("Error:", err) return } data := fmt.Sprintf("sha256:%s", hash) fmt.Printf("saving go binary hash: %s\n", data) err = os.WriteFile("gobinhash", []byte(data), 0644) if err != nil { fmt.Println("Error:", err) return } } const ( goBinName = "go" goEnvCmd = "env" goRootEnvVar = "GOROOT" goBinPathPat = "%s/bin/%s" ) func getGoExeFullPath() (string, error) { output, err := exec.Command(goBinName, goEnvCmd, goRootEnvVar).Output() if err != nil { return "", err } goRoot := string(output[:len(output)-1]) // removing the newline fullPath := fmt.Sprintf(goBinPathPat, goRoot, goBinName) if _, err := os.Stat(fullPath); err == nil { return fullPath, nil } return exec.LookPath(goBinName) } func hashFile(fullPath string) (string, error) { file, err := os.Open(fullPath) if err != nil { return "", err } defer file.Close() hasher := sha256.New() if _, err := io.Copy(hasher, file); err != nil { return "", err } hash := hasher.Sum(nil) return fmt.Sprintf("%x", hash), nil } ================================================ FILE: pkg/artifact/artifact.go ================================================ package artifact import ( "strings" ) var ShellNames = []string{ "bash", "sh", } var ShellCommands = []string{ "ls", "pwd", "cd", "ps", "head", "tail", "cat", "more", "find", "grep", "awk", "env", } var FilteredPaths = map[string]struct{}{ "/": {}, "/proc": {}, "/sys": {}, "/dev": {}, } var FileteredPathPrefixList = []string{ "/proc/", "/sys/", "/dev/", } func IsFilteredPath(name string) bool { switch name { case "/", "/proc", "/sys", "/dev": return true } for _, prefix := range FileteredPathPrefixList { if strings.HasPrefix(name, prefix) { return true } } return false } ================================================ FILE: pkg/certdiscover/certdiscover.go ================================================ package certdiscover import ( "bytes" "fmt" "strings" "unicode/utf8" ) // Cert Files (standard system cert bundles) and Java Keystore var certFiles = []string{ "/etc/ssl/certs/ca-certificates.crt", // Debian / Ubuntu / Gentoo / etc. "/etc/ssl/cert.pem", // Alpine / Arch / RHEL 9 "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", // CentOS / RHEL 7 "/etc/ssl/certs/ca-bundle.crt", // RHEL / Fedora "/etc/pki/tls/certs/ca-bundle.crt", // Fedora / RHEL 6 "/etc/pki/tls/cert.pem", // CentOS 7, 8 / RHEL 7, 8 / Fedora <= 33 (can be a symlnk to /etc/pki/tls/certs/ca-bundle.crt) "/etc/ssl/ca-bundle.pem", // OpenSUSE "/etc/pki/tls/cacert.pem", // OpenELEC "/etc/ssl/certs/java/cacerts", // Java Keystore Alpine / Ubuntu "/etc/pki/java/cacerts", // Java Keystore RHEL } var certFilesSet map[string]struct{} // Cert Directories var certDirectories = []string{ "/etc/ssl/certs", // Debian/Ubuntu/OpenSUSE "/etc/pki/tls/certs", // Fedora/RHEL/CentOS/Amazon Linux "/usr/lib/ssl/certs", // OpenSSL, usually linked to the OS-specific location (e.g., /etc/ssl/certs) "/usr/local/ssl/certs", // OpenSSL } var certDirsSet map[string]struct{} // Cert Extra Directories (directories that container the actual standalone certs) var certExtraDirectories = []string{ "/usr/share/ca-certificates", "/usr/local/share/ca-certificates", "/usr/lib/ca-certificates", "/usr/share/pki/trust/anchors", } // Cert File Env Vars (TODO: use these) var certFileEnvVars = []string{ "SSL_CERT_FILE", "PIP_CERT", "NODE_EXTRA_CA_CERTS", "CURL_CA_BUNDLE", } var certFileEnvVarsSet map[string]struct{} // Cert Dir Env Vars var certDirEnvVars = []string{ "SSL_CERT_DIR", // ":" separated list of directories } var certDirEnvVarsSet map[string]struct{} // Cert Private Key Directories var certPKDirectories = []string{ "/etc/ssl/private", // Debian/Ubuntu/OpenSUSE "/etc/pki/tls/private", // Fedora/RHEL/CentOS/Amazon Linux "/usr/lib/ssl/private", // OpenSSL, usually linked to the OS-specific location (e.g., /etc/ssl/private) "/usr/local/ssl/private", // OpenSSL } var certPKDirsSet map[string]struct{} // CA Cert Files (standard CA cert bundles) var caCertFiles = []string{ "/etc/ssl/ca/certs/ca.crt", "/etc/pki/CA/certs/ca.crt", "/etc/ssl/ca/cacert.pem", //"/etc/letsencrypt/live//fullchain.pem" (also: cert.pem, chain.pem) } var caCertFilesSet map[string]struct{} // CA Cert Directories var caCertDirectories = []string{ "/etc/ssl/ca/certs", "/etc/pki/CA/certs", "/etc/letsencrypt/live", } var caCertDirsSet map[string]struct{} // CA Private Key Files var caCertPKFiles = []string{ "/etc/ssl/ca/private/ca.key", "/etc/pki/CA/private/ca.key", "/etc/ssl/ca/private/cakey.pem", //"/etc/letsencrypt/live//privkey.pem" } var caCertPKFilesSet map[string]struct{} // CA Private Key Directories var caCertPKDirectories = []string{ "/etc/ssl/ca/private", "/etc/pki/CA/private", "/etc/letsencrypt/live", } var caCertPKDirsSet map[string]struct{} func CertFileList() []string { return certFiles } func IsCertFile(name string) bool { _, found := certFilesSet[name] return found } func IsCertDirPath(name string) bool { for _, dir := range certDirectories { dir := fmt.Sprintf("%s/", dir) if strings.HasPrefix(name, dir) { return true } } return false } func CertDirList() []string { return certDirectories } func IsCertDir(name string) bool { _, found := certDirsSet[name] return found } func CertExtraDirList() []string { return certExtraDirectories } func CertPKDirList() []string { return certPKDirectories } func IsCertPKDir(name string) bool { _, found := certPKDirsSet[name] return found } func IsCertPKDirPath(name string) bool { for _, dir := range certPKDirectories { dir := fmt.Sprintf("%s/", dir) if strings.HasPrefix(name, dir) { return true } } return false } func CACertFileList() []string { return caCertFiles } func IsCACertFile(name string) bool { _, found := caCertFilesSet[name] return found } func CACertDirList() []string { return caCertDirectories } func IsCACertDir(name string) bool { _, found := caCertDirsSet[name] return found } func IsCACertDirPath(name string) bool { for _, dir := range caCertDirectories { dir := fmt.Sprintf("%s/", dir) if strings.HasPrefix(name, dir) { return true } } return false } func CACertPKFileList() []string { return caCertPKFiles } func IsCACertPKFile(name string) bool { _, found := caCertPKFilesSet[name] return found } func IsCACertPKDir(name string) bool { _, found := caCertPKDirsSet[name] return found } func IsCACertPKDirPath(name string) bool { for _, dir := range caCertPKDirectories { dir := fmt.Sprintf("%s/", dir) if strings.HasPrefix(name, dir) { return true } } return false } func CACertPKDirList() []string { return caCertPKDirectories } const ( LanguageNomatch = "nomatch" LanguagePython = "python" LanguageNode = "node.js" LanguageRuby = "ruby" LanguageJava = "java" ) const AppCertPackageName = "certifi" const ( AppCertPathSuffixPython = "certifi/cacert.pem" AppCertPathSuffixNode = "certifi/cacert.pem" AppCertPathSuffixRuby = "lib/certifi/vendor/cacert.pem" AppCertPathSuffixJava = "security/cacerts" ) const ( AppCertPathMatcherPython = "-packages/certifi/cacert.pem" AppCertPathMatcherNode = "/node_modules/certifi/cacert.pem" AppCertPathMatcherRuby = "/lib/certifi/vendor/cacert.pem" AppCertPathMatcherJava = "/jre/lib/security/cacerts" ) // Certifi package cert file (bundle) path matchers (+ Java Keystore) var certifiCertPathMatchers = map[string]string{ AppCertPathMatcherPython: LanguagePython, AppCertPathMatcherNode: LanguageNode, AppCertPathMatcherRuby: LanguageRuby, //also should include "gems/" AppCertPathMatcherJava: LanguageJava, //Java Keystore } func IsAppCertFile(name string) bool { for pat := range certifiCertPathMatchers { if strings.HasSuffix(name, pat) { return true } } return false } func IsAppCertFileWithInfo(name string) string { for pat, info := range certifiCertPathMatchers { if strings.HasSuffix(name, pat) { return info } } return LanguageNomatch } const ( beginCert = "-----BEGIN CERTIFICATE-----" endCert = "-----END CERTIFICATE-----" ) func IsCertData(data []byte) bool { if !utf8.Valid(data) { return false } if bytes.Contains(data, []byte(beginCert)) && bytes.Contains(data, []byte(endCert)) { return true } return false } const ( beginRSAPK = "-----BEGIN RSA PRIVATE KEY-----" endRSAPK = "-----END RSA PRIVATE KEY-----" beginEncPK = "-----BEGIN ENCRYPTED PRIVATE KEY-----" endEncPK = "-----END ENCRYPTED PRIVATE KEY-----" beginPK = "-----BEGIN PRIVATE KEY-----" endPK = "-----END PRIVATE KEY-----" pkPart = " PRIVATE KEY-----" ) func IsPrivateKeyData(data []byte) bool { if !utf8.Valid(data) { return false } //Basic PEM detection (TODO: detect other formats like DER) if bytes.Contains(data, []byte(pkPart)) { if (bytes.Contains(data, []byte(beginPK)) && bytes.Contains(data, []byte(endPK))) || (bytes.Contains(data, []byte(beginRSAPK)) && bytes.Contains(data, []byte(endRSAPK))) || (bytes.Contains(data, []byte(beginEncPK)) && bytes.Contains(data, []byte(endEncPK))) { return true } } return false } func IsCertHashName(name string) bool { if len(name) == 10 && name[8] == '.' && (name[9] >= '0' && name[9] <= '9') { return true } return false } func init() { certFilesSet = initItemSet(certFiles) certDirsSet = initItemSet(certDirectories) certPKDirsSet = initItemSet(certPKDirectories) certFileEnvVarsSet = initItemSet(certFileEnvVars) certDirEnvVarsSet = initItemSet(certDirEnvVars) caCertFilesSet = initItemSet(caCertFiles) caCertDirsSet = initItemSet(caCertDirectories) caCertPKFilesSet = initItemSet(caCertPKFiles) caCertPKDirsSet = initItemSet(caCertPKDirectories) } func initItemSet(items []string) map[string]struct{} { set := map[string]struct{}{} for _, item := range items { set[item] = struct{}{} } return set } //todo: handle symlinks ================================================ FILE: pkg/command/command.go ================================================ package command // Command type constants const ( Build Type = "build" Profile Type = "profile" Xray Type = "xray" Lint Type = "lint" Images Type = "images" Containerize Type = "containerize" Convert Type = "convert" Merge Type = "merge" Edit Type = "edit" Debug Type = "debug" Probe Type = "probe" Run Type = "run" Server Type = "server" Registry Type = "registry" Vulnerability Type = "vulnerability" Version Type = "version" Update Type = "update" ) // Type is the command type name type Type string // Command state constants const ( StateUnknown = "unknown" StateError = "error" StateStarted = "started" StateCompleted = "completed" StateExited = "exited" StateDone = "done" ) // State is the command state type type State string ================================================ FILE: pkg/consts/community.go ================================================ package consts // Community communication forums const ( CommunityCNCFSlack = "https://cloud-native.slack.com/archives/C059QP1RH1S" CommunityGitter = "https://gitter.im/docker-slim/community" CommunityDiscord = "https://discord.gg/9tDyxYS" CommunityDiscussions = "https://github.com/slimtoolkit/slim/discussions" ) ================================================ FILE: pkg/consts/external.go ================================================ package consts // Labels added to optimized container images const ( DSLabelVersion = "slimtoolkit.version" DSLabelSourceImage = "slimtoolkit.source.image" DSLabelSourceImageID = "slimtoolkit.source.image.id" DSLabelSourceImageDigest = "slimtoolkit.source.image.digest" ) // Other constants that external users/consumers will see const ( //reverse engineered Dockerfile for the target container image ReversedDockerfile = "Dockerfile.reversed" ReversedDockerfileOldName = "Dockerfile.fat" //tmp compat ) ================================================ FILE: pkg/consts/version.go ================================================ package consts // App version constants const ( AppName = "slim" AppVersionName = "Transformer" ) ================================================ FILE: pkg/docker/buildpackinfo/buildpackinfo.go ================================================ // Package buildpackinfo contains buildpack metadata extraction code package buildpackinfo const ( LabelKeyStackID = "io.buildpacks.stack.id" LabelKeyProjectMetadata = "io.buildpacks.project.metadata" LabelKeyBuildMetadata = "io.buildpacks.build.metadata" LabelKeyLifecycleMetadata = "io.buildpacks.lifecycle.metadata" LabelKeyStackMaintainer = "io.buildpacks.stack.maintainer" ) const ( StackHeroku18 = "heroku-18" StackGoogle = "google" StackPaketo = "io.buildpacks.stacks.bionic" ) const ( VendorHeroku = "heroku" VendorGoogle = "google" VendorPaketo = "paketo" ) const Entrypoint = "/cnb/process/web" var Labels = map[string]struct{}{ LabelKeyStackID: {}, LabelKeyProjectMetadata: {}, LabelKeyBuildMetadata: {}, LabelKeyLifecycleMetadata: {}, LabelKeyStackMaintainer: {}, } func HasBuildbackLabels(labels map[string]string) bool { for k := range labels { if _, found := Labels[k]; found { return true } } return false } ================================================ FILE: pkg/docker/dockerclient/client.go ================================================ package dockerclient import ( "errors" "fmt" "os" "path/filepath" "github.com/fsouza/go-dockerclient" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app/master/config" "github.com/slimtoolkit/slim/pkg/util/errutil" "github.com/slimtoolkit/slim/pkg/util/fsutil" "github.com/slimtoolkit/slim/pkg/util/jsonutil" ) const ( EnvDockerAPIVer = "DOCKER_API_VERSION" EnvDockerHost = "DOCKER_HOST" EnvDockerTLSVerify = "DOCKER_TLS_VERIFY" EnvDockerCertPath = "DOCKER_CERT_PATH" UnixSocketPath = "/var/run/docker.sock" UnixSocketAddr = "unix:///var/run/docker.sock" unixUserSocketSuffix = ".docker/run/docker.sock" ) var EnvVarNames = []string{ EnvDockerHost, EnvDockerTLSVerify, EnvDockerCertPath, EnvDockerAPIVer, } var ( ErrNoDockerInfo = errors.New("no docker info") ) func UserDockerSocket() string { home, _ := os.UserHomeDir() return filepath.Join(home, unixUserSocketSuffix) } type SocketInfo struct { Address string `json:"address"` FilePath string `json:"file_path"` FileType string `json:"type"` FilePerms string `json:"perms"` SymlinkTarget string `json:"symlink_target,omitempty"` TargetPerms string `json:"target_perms,omitempty"` TargetType string `json:"target_type,omitempty"` CanRead bool `json:"can_read"` CanWrite bool `json:"can_write"` } func getSocketInfo(filePath string) (*SocketInfo, error) { info := &SocketInfo{ FileType: "file", FilePath: filePath, } fi, err := os.Lstat(info.FilePath) if err != nil { log.Errorf("dockerclient.getSocketInfo.os.Lstat(%s): error - %v", filePath, err) return nil, err } if fi.Mode()&os.ModeSymlink != 0 { info.SymlinkTarget, err = os.Readlink(info.FilePath) if err != nil { log.Errorf("dockerclient.getSocketInfo.os.Readlink(%s): error - %v", filePath, err) return nil, err } info.FileType = "symlink" info.FilePerms = fmt.Sprintf("%#o", fi.Mode().Perm()) if info.SymlinkTarget != "" { tfi, err := os.Lstat(info.SymlinkTarget) if err != nil { log.Errorf("dockerclient.getSocketInfo.os.Lstat(%s): error - %v", info.SymlinkTarget, err) return nil, err } info.TargetPerms = fmt.Sprintf("%#o", tfi.Mode().Perm()) if tfi.Mode()&os.ModeSymlink != 0 { info.TargetType = "symlink" } } } info.CanRead, err = fsutil.HasReadAccess(info.FilePath) if err != nil { log.Errorf("dockerclient.getSocketInfo.fsutil.HasReadAccess(%s): error - %v", info.FilePath, err) return nil, err } info.CanWrite, err = fsutil.HasWriteAccess(info.FilePath) if err != nil { log.Errorf("dockerclient.getSocketInfo.fsutil.HasWriteAccess(%s): error - %v", info.FilePath, err) return nil, err } return info, nil } func GetUnixSocketAddr() (*SocketInfo, error) { //note: may move this to dockerutil if _, err := os.Stat(UnixSocketPath); err == nil { socketInfo, err := getSocketInfo(UnixSocketPath) if err != nil { return nil, err } socketInfo.Address = UnixSocketAddr log.Debugf("dockerclient.GetUnixSocketAddr(): found => %s", jsonutil.ToString(socketInfo)) return socketInfo, nil } userDockerSocket := UserDockerSocket() if _, err := os.Stat(userDockerSocket); err == nil { socketInfo, err := getSocketInfo(userDockerSocket) if err != nil { return nil, err } socketInfo.Address = fmt.Sprintf("unix://%s", userDockerSocket) log.Debugf("dockerclient.GetUnixSocketAddr(): found => %s", jsonutil.ToString(socketInfo)) return socketInfo, nil } return nil, fmt.Errorf("docker socket not found") } // New creates a new Docker client instance func New(config *config.DockerClient) (*docker.Client, error) { var client *docker.Client var err error newTLSClient := func(host string, certPath string, verify bool, apiVersion string) (*docker.Client, error) { var ca []byte cert, err := os.ReadFile(filepath.Join(certPath, "cert.pem")) if err != nil { return nil, err } key, err := os.ReadFile(filepath.Join(certPath, "key.pem")) if err != nil { return nil, err } if verify { var err error ca, err = os.ReadFile(filepath.Join(certPath, "ca.pem")) if err != nil { return nil, err } } return docker.NewVersionedTLSClientFromBytes(host, cert, key, ca, apiVersion) } switch { case config.Host != "" && config.UseTLS && config.VerifyTLS && config.TLSCertPath != "": client, err = newTLSClient(config.Host, config.TLSCertPath, true, config.APIVersion) if err != nil { return nil, err } log.Debug("dockerclient.New: new Docker client (TLS,verify) [1]") case config.Host != "" && config.UseTLS && !config.VerifyTLS && config.TLSCertPath != "": client, err = newTLSClient(config.Host, config.TLSCertPath, false, config.APIVersion) if err != nil { return nil, err } log.Debug("dockerclient.New: new Docker client (TLS,no verify) [2]") case config.Host != "" && !config.UseTLS: client, err = docker.NewVersionedClient(config.Host, config.APIVersion) if err != nil { return nil, err } if config.APIVersion != "" { client.SkipServerVersionCheck = true } log.Debug("dockerclient.New: new Docker client [3]") case config.Host == "" && !config.VerifyTLS && config.Env[EnvDockerTLSVerify] == "1" && config.Env[EnvDockerCertPath] != "" && config.Env[EnvDockerHost] != "": client, err = newTLSClient(config.Env[EnvDockerHost], config.Env[EnvDockerCertPath], false, config.APIVersion) if err != nil { return nil, err } log.Debug("dockerclient.New: new Docker client (TLS,no verify) [4]") case config.Env[EnvDockerHost] != "": client, err = docker.NewClientFromEnv() if err != nil { return nil, err } log.Debug("dockerclient.New: new Docker client (env) [5]") case config.Host == "" && config.Env[EnvDockerHost] == "": socketInfo, err := GetUnixSocketAddr() if err != nil { return nil, err } if socketInfo == nil || socketInfo.Address == "" { return nil, fmt.Errorf("no unix socket found") } if socketInfo.CanRead == false || socketInfo.CanWrite == false { return nil, fmt.Errorf("insufficient socket permissions (can_read=%v can_write=%v)", socketInfo.CanRead, socketInfo.CanWrite) } config.Host = socketInfo.Address client, err = docker.NewVersionedClient(config.Host, config.APIVersion) if err != nil { return nil, err } if config.APIVersion != "" { client.SkipServerVersionCheck = true } log.Debug("dockerclient.New: new Docker client (default) [6]") default: return nil, ErrNoDockerInfo } if config.Env[EnvDockerHost] == "" { if err := os.Setenv(EnvDockerHost, config.Host); err != nil { errutil.WarnOn(err) } log.Debug("dockerclient.New: configured DOCKER_HOST env var") } if config.APIVersion != "" && config.Env[EnvDockerAPIVer] == "" { if err := os.Setenv(EnvDockerAPIVer, config.APIVersion); err != nil { errutil.WarnOn(err) } log.Debug("dockerclient.New: configured DOCKER_API_VERSION env var") } return client, nil } ================================================ FILE: pkg/docker/dockerfile/ast/line_parsers.go ================================================ package ast // line parsers are dispatch calls that parse a single unit of text into a // Node object which contains the whole statement. Dockerfiles have varied // (but not usually unique, see ONBUILD for a unique example) parsing rules // per-command, and these unify the processing in a way that makes it // manageable. // Augmented BuildKit parser code to support linting import ( "encoding/json" "errors" "fmt" "strings" "unicode" "unicode/utf8" "github.com/slimtoolkit/slim/pkg/docker/instruction" ) type NameValError struct { ParseError } func nve(context, data, message string) NameValError { return NameValError{ ParseError: pe(context, data, message), } } type OldFormatNameValError struct { NameValError } func ofnve(context, message string) OldFormatNameValError { return OldFormatNameValError{ NameValError: nve(context, "", message), } } type NewFormatNameValError struct { NameValError } func nfnve(context, data, message string) NewFormatNameValError { return NewFormatNameValError{ NameValError: nve(context, data, message), } } var ( ErrDockerfileNotStringArray = errors.New("when using JSON array syntax, arrays must be comprised of strings only") ) // ignore the current argument. This will still leave a command parsed, but // will not incorporate the arguments into the ast. func parseIgnore(rest string, d *Directive) (*Node, map[string]bool, error) { return &Node{}, nil, nil } // used for onbuild. Could potentially be used for anything that represents a // statement with sub-statements. // // ONBUILD RUN foo bar -> (onbuild (run foo bar)) func parseSubCommand(rest string, d *Directive) (*Node, map[string]bool, error) { if rest == "" { return nil, nil, nil } child, err := newNodeFromLine(rest, d) if err != nil { return nil, nil, err } return &Node{Children: []*Node{child}}, nil, nil } // helper to parse words (i.e space delimited or quoted strings) in a statement. // The quotes are preserved as part of this function and they are stripped later // as part of processWords(). func parseWords(rest string, d *Directive) []string { const ( inSpaces = iota // looking for start of a word inWord inQuote ) words := []string{} phase := inSpaces word := "" quote := '\000' blankOK := false var ch rune var chWidth int for pos := 0; pos <= len(rest); pos += chWidth { if pos != len(rest) { ch, chWidth = utf8.DecodeRuneInString(rest[pos:]) } if phase == inSpaces { // Looking for start of word if pos == len(rest) { // end of input break } if unicode.IsSpace(ch) { // skip spaces continue } phase = inWord // found it, fall through } if (phase == inWord || phase == inQuote) && (pos == len(rest)) { if blankOK || len(word) > 0 { words = append(words, word) } break } if phase == inWord { if unicode.IsSpace(ch) { phase = inSpaces if blankOK || len(word) > 0 { words = append(words, word) } word = "" blankOK = false continue } if ch == '\'' || ch == '"' { quote = ch blankOK = true phase = inQuote } if ch == d.escapeToken { if pos+chWidth == len(rest) { continue // just skip an escape token at end of line } // If we're not quoted and we see an escape token, then always just // add the escape token plus the char to the word, even if the char // is a quote. word += string(ch) pos += chWidth ch, chWidth = utf8.DecodeRuneInString(rest[pos:]) } word += string(ch) continue } if phase == inQuote { if ch == quote { phase = inWord } // The escape token is special except for ' quotes - can't escape anything for ' if ch == d.escapeToken && quote != '\'' { if pos+chWidth == len(rest) { phase = inWord continue // just skip the escape token at end } pos += chWidth word += string(ch) ch, chWidth = utf8.DecodeRuneInString(rest[pos:]) } word += string(ch) } } return words } // parse environment like statements. Note that this does *not* handle // variable interpolation, which will be handled in the evaluator. func parseNameVal(rest string, key string, d *Directive) (*Node, error) { // This is kind of tricky because we need to support the old // variant: KEY name value // as well as the new one: KEY name=value ... // The trigger to know which one is being used will be whether we hit // a space or = first. space ==> old, "=" ==> new words := parseWords(rest, d) if len(words) == 0 { return nil, nil } // Old format (KEY name value) if !strings.Contains(words[0], "=") { parts := tokenWhitespace.Split(rest, 2) if len(parts) < 2 { return nil, ofnve(key, fmt.Sprintf(key+" must have two arguments")) } return newKeyValueNode(parts[0], parts[1]), nil } var rootNode *Node var prevNode *Node for _, word := range words { if !strings.Contains(word, "=") { return nil, nfnve(key, word, fmt.Sprintf("Syntax error - can't find = in %q. Must be of the form: name=value", word)) } parts := strings.SplitN(word, "=", 2) node := newKeyValueNode(parts[0], parts[1]) rootNode, prevNode = appendKeyValueNode(node, rootNode, prevNode) } return rootNode, nil } func newKeyValueNode(key, value string) *Node { return &Node{ Value: key, Next: &Node{Value: value}, } } func appendKeyValueNode(node, rootNode, prevNode *Node) (*Node, *Node) { if rootNode == nil { rootNode = node } if prevNode != nil { prevNode.Next = node } prevNode = node.Next return rootNode, prevNode } func parseEnv(rest string, d *Directive) (*Node, map[string]bool, error) { node, err := parseNameVal(rest, instruction.Env, d) return node, nil, err } func parseLabel(rest string, d *Directive) (*Node, map[string]bool, error) { node, err := parseNameVal(rest, instruction.Label, d) return node, nil, err } // parses a statement containing one or more keyword definition(s) and/or // value assignments, like `name1 name2= name3="" name4=value`. // Note that this is a stricter format than the old format of assignment, // allowed by parseNameVal(), in a way that this only allows assignment of the // form `keyword=[]` like `name2=`, `name3=""`, and `name4=value` above. // In addition, a keyword definition alone is of the form `keyword` like `name1` // above. And the assignments `name2=` and `name3=""` are equivalent and // assign an empty value to the respective keywords. func parseNameOrNameVal(rest string, d *Directive) (*Node, map[string]bool, error) { words := parseWords(rest, d) if len(words) == 0 { return nil, nil, nil } var ( rootnode *Node prevNode *Node ) for i, word := range words { node := &Node{} node.Value = word if i == 0 { rootnode = node } else { prevNode.Next = node } prevNode = node } return rootnode, nil, nil } // parses a whitespace-delimited set of arguments. The result is effectively a // linked list of string arguments. func parseStringsWhitespaceDelimited(rest string, d *Directive) (*Node, map[string]bool, error) { if rest == "" { return nil, nil, nil } node := &Node{} rootnode := node prevnode := node for _, str := range tokenWhitespace.Split(rest, -1) { // use regexp prevnode = node node.Value = str node.Next = &Node{} node = node.Next } // XXX to get around regexp.Split *always* providing an empty string at the // end due to how our loop is constructed, nil out the last node in the // chain. prevnode.Next = nil return rootnode, nil, nil } // parseString just wraps the string in quotes and returns a working node. func parseString(rest string, d *Directive) (*Node, map[string]bool, error) { if rest == "" { return nil, nil, nil } n := &Node{} n.Value = rest return n, nil, nil } // parseJSON converts JSON arrays to an AST. func parseJSON(rest string, d *Directive) (*Node, map[string]bool, error) { rest = strings.TrimLeftFunc(rest, unicode.IsSpace) if !strings.HasPrefix(rest, "[") { return nil, nil, ParseError{ Data: rest, Message: fmt.Sprintf(`Error parsing "%s" as a JSON array`, rest), } } var myJSON []interface{} if err := json.NewDecoder(strings.NewReader(rest)).Decode(&myJSON); err != nil { return nil, nil, err } var top, prev *Node for _, str := range myJSON { s, ok := str.(string) if !ok { return nil, nil, ErrDockerfileNotStringArray } node := &Node{Value: s} if prev == nil { top = node } else { prev.Next = node } prev = node } return top, map[string]bool{"json": true}, nil } // parseMaybeJSON determines if the argument appears to be a JSON array. If // so, passes to parseJSON; if not, quotes the result and returns a single // node. func parseMaybeJSON(rest string, d *Directive) (*Node, map[string]bool, error) { if rest == "" { return nil, nil, nil } node, attrs, err := parseJSON(rest, d) if err == nil { return node, attrs, nil } if err == ErrDockerfileNotStringArray { return nil, nil, err } node = &Node{} node.Value = rest return node, nil, nil } // parseMaybeJSONToList determines if the argument appears to be a JSON array. If // so, passes to parseJSON; if not, attempts to parse it as a whitespace // delimited string. func parseMaybeJSONToList(rest string, d *Directive) (*Node, map[string]bool, error) { node, attrs, err := parseJSON(rest, d) if err == nil { return node, attrs, nil } if err == ErrDockerfileNotStringArray { return nil, nil, err } return parseStringsWhitespaceDelimited(rest, d) } // The HEALTHCHECK command is like parseMaybeJSON, but has an extra type argument. func parseHealthConfig(rest string, d *Directive) (*Node, map[string]bool, error) { // Find end of first argument var sep int for ; sep < len(rest); sep++ { if unicode.IsSpace(rune(rest[sep])) { break } } next := sep for ; next < len(rest); next++ { if !unicode.IsSpace(rune(rest[next])) { break } } if sep == 0 { return nil, nil, nil } typ := rest[:sep] cmd, attrs, err := parseMaybeJSON(rest[next:], d) if err != nil { return nil, nil, err } return &Node{Value: typ, Next: cmd}, attrs, err } ================================================ FILE: pkg/docker/dockerfile/ast/parser.go ================================================ // Package ast implements a (low level) parser and parse tree dumper for Dockerfiles. package ast // Augmented BuildKit parser code to support linting import ( "bufio" "bytes" "fmt" "io" "regexp" "strconv" "strings" "unicode" "github.com/pkg/errors" "github.com/slimtoolkit/slim/pkg/docker/instruction" ) const ( UnknownInstMsg = "unknown instruction" ) var ( ErrDockerfileUnknownInst = errors.New(UnknownInstMsg) ) type ParseError struct { Context string Data string Message string } func (e ParseError) Error() string { return e.Message } func pe(context, data, message string) ParseError { return ParseError{ Context: context, Data: data, Message: message, } } // Node is a structure used to represent a parse tree. // // In the node there are three fields, Value, Next, and Children. Value is the // current token's string value. Next is always the next non-child token, and // children contains all the children. Here's an example: // // (value next (child child-next child-next-next) next-next) // // This data structure is frankly pretty lousy for handling complex languages, // but lucky for us the Dockerfile isn't very complicated. This structure // works a little more effectively than a "proper" parse tree for our needs. type Node struct { IsValid bool Errors []string Value string // actual content ArgsRaw string Next *Node // the next item in the current sexp Children []*Node // the children of this sexp Attributes map[string]bool // special attributes for this node Original string // original line used before parsing Flags []string // only top Node should have this set StartLine int // the line in the original dockerfile where the node begins EndLine int // the line in the original dockerfile where the node ends } // Dump dumps the AST defined by `node` as a list of sexps. // Returns a string suitable for printing. func (node *Node) Dump() string { str := "" if !node.IsValid { str += fmt.Sprintf("is_invalid errors='%+v' ", node.Errors) } str += fmt.Sprintf("value=%q", node.Value) if len(node.Flags) > 0 { str += fmt.Sprintf(" flags=%q", node.Flags) } if len(node.Attributes) > 0 { str += fmt.Sprintf(" attributes=%+v", node.Attributes) } if len(node.Children) > 0 { str += " children start:\n" for _, n := range node.Children { str += "(" + n.Dump() + ")\n" } str += ":children done\n" } for n := node.Next; n != nil; n = n.Next { if len(n.Children) > 0 { str += " next=" + n.Dump() } else { str += " nextv=" + strconv.Quote(n.Value) } } return strings.TrimSpace(str) } func (node *Node) lines(start, end int) { node.StartLine = start node.EndLine = end } // AddChild adds a new child node, and updates line information func (node *Node) AddChild(child *Node, startLine, endLine int) { child.lines(startLine, endLine) if node.StartLine < 0 { node.StartLine = startLine } node.EndLine = endLine node.Children = append(node.Children, child) } var ( dispatch map[string]func(string, *Directive) (*Node, map[string]bool, error) tokenWhitespace = regexp.MustCompile(`[\t\v\f\r ]+`) tokenEscapeCommand = regexp.MustCompile(`^#[ \t]*escape[ \t]*=[ \t]*(?P.).*$`) tokenComment = regexp.MustCompile(`^#.*$`) ) // DefaultEscapeToken is the default escape token const DefaultEscapeToken = '\\' // Directive is the structure used during a build run to hold the state of // parsing directives. type Directive struct { escapeToken rune // Current escape token lineContinuationRegex *regexp.Regexp // Current line continuation regex processingComplete bool // Whether we are done looking for directives escapeSeen bool // Whether the escape directive has been seen } // setEscapeToken sets the default token for escaping characters in a Dockerfile. func (d *Directive) setEscapeToken(s string) error { if s != "`" && s != "\\" { return fmt.Errorf("invalid ESCAPE '%s'. Must be ` or \\", s) } d.escapeToken = rune(s[0]) d.lineContinuationRegex = regexp.MustCompile(`\` + s + `[ \t]*$`) return nil } // possibleParserDirective looks for parser directives, eg '# escapeToken='. // Parser directives must precede any builder instruction or other comments, // and cannot be repeated. func (d *Directive) possibleParserDirective(line string) error { if d.processingComplete { return nil } tecMatch := tokenEscapeCommand.FindStringSubmatch(strings.ToLower(line)) if len(tecMatch) != 0 { for i, n := range tokenEscapeCommand.SubexpNames() { if n == "escapechar" { if d.escapeSeen { return errors.New("only one escape parser directive can be used") } d.escapeSeen = true return d.setEscapeToken(tecMatch[i]) } } } d.processingComplete = true return nil } // NewDefaultDirective returns a new Directive with the default escapeToken token func NewDefaultDirective() *Directive { directive := Directive{} directive.setEscapeToken(string(DefaultEscapeToken)) return &directive } func init() { // Dispatch Table. see line_parsers.go for the parse functions. // The command is parsed and mapped to the line parser. The line parser // receives the arguments but not the command, and returns an AST after // reformulating the arguments according to the rules in the parser // functions. Errors are propagated up by Parse() and the resulting AST can // be incorporated directly into the existing AST as a next. dispatch = map[string]func(string, *Directive) (*Node, map[string]bool, error){ instruction.Add: parseMaybeJSONToList, instruction.Arg: parseNameOrNameVal, instruction.Cmd: parseMaybeJSON, instruction.Copy: parseMaybeJSONToList, instruction.Entrypoint: parseMaybeJSON, instruction.Env: parseEnv, instruction.Expose: parseStringsWhitespaceDelimited, instruction.From: parseStringsWhitespaceDelimited, instruction.Healthcheck: parseHealthConfig, instruction.Label: parseLabel, instruction.Maintainer: parseString, instruction.Onbuild: parseSubCommand, instruction.Run: parseMaybeJSON, instruction.Shell: parseMaybeJSON, instruction.StopSignal: parseString, instruction.User: parseString, instruction.Volume: parseMaybeJSONToList, instruction.Workdir: parseString, } } // newNodeFromLine splits the line into parts, and dispatches to a function // based on the command and command arguments. A Node is created from the // result of the dispatch. func newNodeFromLine(line string, directive *Directive) (*Node, error) { cmd, flags, args, err := splitCommand(line) if err != nil { return nil, err } fn, found := dispatch[cmd] // Ignore invalid Dockerfile instructions if !found { fn = parseIgnore } var isValid bool var errors []string next, attrs, err := fn(args, directive) if err != nil { errors = append(errors, err.Error()) next = &Node{ Value: args, } } else { if !found { errors = append(errors, UnknownInstMsg) next.Value = args } else { isValid = true } } return &Node{ IsValid: isValid, Errors: errors, Value: cmd, Original: line, Flags: flags, Next: next, Attributes: attrs, ArgsRaw: args, }, nil } // Result is the result of parsing a Dockerfile type Result struct { Lines []string AST *Node EscapeToken rune Warnings []string } // PrintWarnings to the writer func (r *Result) PrintWarnings(out io.Writer) { if len(r.Warnings) == 0 { return } fmt.Fprintf(out, strings.Join(r.Warnings, "\n")+"\n") } // Parse reads lines from a Reader, parses the lines into an AST and returns // the AST and escape token func Parse(rwc io.Reader) (*Result, error) { var lines []string d := NewDefaultDirective() currentLine := 0 root := &Node{IsValid: true, StartLine: -1} scanner := bufio.NewScanner(rwc) warnings := []string{} var err error for scanner.Scan() { bytesRead := scanner.Bytes() if currentLine == 0 { // First line, strip the byte-order-marker if present bytesRead = bytes.TrimPrefix(bytesRead, utf8bom) } lines = append(lines, string(bytesRead)) bytesRead, err = processLine(d, bytesRead, true) if err != nil { return nil, err } currentLine++ startLine := currentLine line, isEndOfLine := trimContinuationCharacter(string(bytesRead), d) if isEndOfLine && line == "" { continue } var hasEmptyContinuationLine bool for !isEndOfLine && scanner.Scan() { bytesRead, err := processLine(d, scanner.Bytes(), false) if err != nil { return nil, err } currentLine++ if isComment(scanner.Bytes()) { // original line was a comment (processLine strips comments) continue } if isEmptyContinuationLine(bytesRead) { hasEmptyContinuationLine = true continue } continuationLine := string(bytesRead) continuationLine, isEndOfLine = trimContinuationCharacter(continuationLine, d) line += continuationLine } if hasEmptyContinuationLine { warnings = append(warnings, "[WARNING]: Empty continuation line found in:\n "+line) } child, err := newNodeFromLine(line, d) if err != nil { return nil, err } root.AddChild(child, startLine, currentLine) } if len(warnings) > 0 { warnings = append(warnings, "[WARNING]: Empty continuation lines will become errors in a future release.") } if root.StartLine < 0 { warnings = append(warnings, "[WARNING]: File with no instructions.") //return nil, errors.New("file with no instructions.") } return &Result{ Lines: lines, AST: root, Warnings: warnings, EscapeToken: d.escapeToken, }, handleScannerError(scanner.Err()) } func trimComments(src []byte) []byte { return tokenComment.ReplaceAll(src, []byte{}) } func trimWhitespace(src []byte) []byte { return bytes.TrimLeftFunc(src, unicode.IsSpace) } func isComment(line []byte) bool { return tokenComment.Match(trimWhitespace(line)) } func isEmptyContinuationLine(line []byte) bool { return len(trimWhitespace(line)) == 0 } var utf8bom = []byte{0xEF, 0xBB, 0xBF} func trimContinuationCharacter(line string, d *Directive) (string, bool) { if d.lineContinuationRegex.MatchString(line) { line = d.lineContinuationRegex.ReplaceAllString(line, "") return line, false } return line, true } // TODO: remove stripLeftWhitespace after deprecation period. It seems silly // to preserve whitespace on continuation lines. Why is that done? func processLine(d *Directive, token []byte, stripLeftWhitespace bool) ([]byte, error) { if stripLeftWhitespace { token = trimWhitespace(token) } return trimComments(token), d.possibleParserDirective(string(token)) } func handleScannerError(err error) error { switch err { case bufio.ErrTooLong: return errors.Errorf("dockerfile line greater than max allowed size of %d", bufio.MaxScanTokenSize-1) default: return err } } ================================================ FILE: pkg/docker/dockerfile/ast/split_command.go ================================================ package ast // Augmented BuildKit parser code to support linting import ( "strings" "unicode" ) // splitCommand takes a single line of text and parses out the cmd and args, // which are used for dispatching to more exact parsing functions. func splitCommand(line string) (string, []string, string, error) { var args string var flags []string // Make sure we get the same results irrespective of leading/trailing spaces cmdline := tokenWhitespace.Split(strings.TrimSpace(line), 2) cmd := strings.ToLower(cmdline[0]) if len(cmdline) == 2 { var err error args, flags, err = extractBuilderFlags(cmdline[1]) if err != nil { return "", nil, "", err } } return cmd, flags, strings.TrimSpace(args), nil } func extractBuilderFlags(line string) (string, []string, error) { // Parses the BuilderFlags and returns the remaining part of the line const ( inSpaces = iota // looking for start of a word inWord inQuote ) words := []string{} phase := inSpaces word := "" quote := '\000' blankOK := false var ch rune for pos := 0; pos <= len(line); pos++ { if pos != len(line) { ch = rune(line[pos]) } if phase == inSpaces { // Looking for start of word if pos == len(line) { // end of input break } if unicode.IsSpace(ch) { // skip spaces continue } // Only keep going if the next word starts with -- if ch != '-' || pos+1 == len(line) || rune(line[pos+1]) != '-' { return line[pos:], words, nil } phase = inWord // found something with "--", fall through } if (phase == inWord || phase == inQuote) && (pos == len(line)) { if word != "--" && (blankOK || len(word) > 0) { words = append(words, word) } break } if phase == inWord { if unicode.IsSpace(ch) { phase = inSpaces if word == "--" { return line[pos:], words, nil } if blankOK || len(word) > 0 { words = append(words, word) } word = "" blankOK = false continue } if ch == '\'' || ch == '"' { quote = ch blankOK = true phase = inQuote continue } if ch == '\\' { if pos+1 == len(line) { continue // just skip \ at end } pos++ ch = rune(line[pos]) } word += string(ch) continue } if phase == inQuote { if ch == quote { phase = inWord continue } if ch == '\\' { if pos+1 == len(line) { phase = inWord continue // just skip \ at end } pos++ ch = rune(line[pos]) } word += string(ch) } } return "", words, nil } ================================================ FILE: pkg/docker/dockerfile/dockerfile.go ================================================ package dockerfile import ( "bytes" "encoding/json" "fmt" "os" "path/filepath" "strconv" "strings" docker "github.com/fsouza/go-dockerclient" "github.com/slimtoolkit/slim/pkg/consts" v "github.com/slimtoolkit/slim/pkg/version" ) // note: dup (todo: refactor) const ( //MAINTAINER: instPrefixMaintainer = "MAINTAINER " //ENTRYPOINT: instTypeEntrypoint = "ENTRYPOINT" instPrefixEntrypoint = "ENTRYPOINT " //CMD: instTypeCmd = "CMD" instPrefixCmd = "CMD " //USER: instTypeUser = "USER" instPrefixUser = "USER " //EXPOSE: instTypeExpose = "EXPOSE" instPrefixExpose = "EXPOSE " //WORKDIR: instTypeWorkdir = "WORKDIR" instPrefixWorkdir = "WORKDIR " //HEALTHCHECK: instTypeHealthcheck = "HEALTHCHECK" instPrefixHealthcheck = "HEALTHCHECK " //ONBUILD: instTypeOnbuild = "ONBUILD" //RUN: instTypeRun = "RUN" instPrefixRun = "RUN " //ADD: instTypeAdd = "ADD" //COPY: instTypeCopy = "COPY" ) // GenerateFromInfo builds and saves a Dockerfile file object func GenerateFromInfo( location string, volumes map[string]struct{}, workingDir string, env []string, labels map[string]string, user string, exposedPorts map[docker.Port]struct{}, entrypoint []string, cmd []string, hasData bool, tarData bool) error { dockerfileLocation := filepath.Join(location, "Dockerfile") var dfData bytes.Buffer dfData.WriteString("FROM scratch\n") dsInfoLabel := fmt.Sprintf("LABEL %s=\"%s\"\n", consts.DSLabelVersion, v.Current()) dfData.WriteString(dsInfoLabel) if len(labels) > 0 { for name, value := range labels { var encoded bytes.Buffer encoder := json.NewEncoder(&encoded) encoder.SetEscapeHTML(false) encoder.Encode(value) labelInfo := fmt.Sprintf("LABEL %s=%s\n", name, encoded.String()) dfData.WriteString(labelInfo) } dfData.WriteByte('\n') } if len(env) > 0 { for _, envInfo := range env { if envParts := strings.SplitN(envInfo, "=", 2); len(envParts) > 1 { dfData.WriteString("ENV ") envLine := fmt.Sprintf("%s \"%s\"", envParts[0], envParts[1]) dfData.WriteString(envLine) dfData.WriteByte('\n') } } dfData.WriteByte('\n') } if len(volumes) > 0 { var volumeList []string for volumeName := range volumes { volumeList = append(volumeList, strconv.Quote(volumeName)) } volumeInst := fmt.Sprintf("VOLUME [%s]", strings.Join(volumeList, ",")) dfData.WriteString(volumeInst) dfData.WriteByte('\n') } if hasData { addData := "COPY files /\n" if tarData { addData = "ADD files.tar /\n" } dfData.WriteString(addData) } if workingDir != "" { dfData.WriteString(instPrefixWorkdir) dfData.WriteString(workingDir) dfData.WriteByte('\n') } if user != "" { dfData.WriteString(instPrefixUser) dfData.WriteString(user) dfData.WriteByte('\n') } if len(exposedPorts) > 0 { for portInfo := range exposedPorts { dfData.WriteString(instPrefixExpose) dfData.WriteString(string(portInfo)) dfData.WriteByte('\n') } } if len(entrypoint) > 0 { //TODO: need to make sure the generated ENTRYPOINT is compatible with the original behavior var quotedEntryPoint []string for idx := range entrypoint { quotedEntryPoint = append(quotedEntryPoint, strconv.Quote(entrypoint[idx])) } dfData.WriteString("ENTRYPOINT [") dfData.WriteString(strings.Join(quotedEntryPoint, ",")) dfData.WriteByte(']') dfData.WriteByte('\n') } if len(cmd) > 0 { //TODO: need to make sure the generated CMD is compatible with the original behavior var quotedCmd []string for idx := range cmd { quotedCmd = append(quotedCmd, strconv.Quote(cmd[idx])) } dfData.WriteString("CMD [") dfData.WriteString(strings.Join(quotedCmd, ",")) dfData.WriteByte(']') dfData.WriteByte('\n') } return os.WriteFile(dockerfileLocation, dfData.Bytes(), 0644) } ================================================ FILE: pkg/docker/dockerfile/parser/parser.go ================================================ // Package parser implements a Dockerfile parser package parser import ( "errors" "os" "path/filepath" "strconv" "strings" "github.com/slimtoolkit/slim/pkg/docker/dockerfile/ast" "github.com/slimtoolkit/slim/pkg/docker/dockerfile/spec" "github.com/slimtoolkit/slim/pkg/docker/instruction" ) var ( ErrInvalidDockerfile = errors.New("invalid Dockerfile") ) //TODO: //* support incremental, partial and instruction level parsing //* support parsing from reader and from string func FromFile(fpath string) (*spec.Dockerfile, error) { fo, err := os.Open(fpath) if err != nil { return nil, err } defer fo.Close() astParsed, err := ast.Parse(fo) if err != nil { return nil, err } if !astParsed.AST.IsValid { return nil, ErrInvalidDockerfile } dockerfile := spec.NewDockerfile() dockerfile.Name = filepath.Base(fpath) dockerfile.Location = filepath.Dir(fpath) dockerfile.Lines = astParsed.Lines if astParsed.AST.StartLine > -1 && len(astParsed.AST.Children) > 0 { var currentStage *spec.BuildStage instStageIndex := -1 for idx, node := range astParsed.AST.Children { inst := &instruction.Field{ GlobalIndex: idx, StageIndex: instStageIndex, IsValid: node.IsValid, RawData: node.Original, StartLine: node.StartLine, EndLine: node.EndLine, Name: node.Value, Flags: node.Flags, ArgsRaw: node.ArgsRaw, } if len(dockerfile.Lines) > 0 && inst.StartLine > 0 && inst.StartLine <= len(dockerfile.Lines) && inst.EndLine <= len(dockerfile.Lines) && inst.EndLine >= inst.StartLine { inst.RawLines = dockerfile.Lines[inst.StartLine-1 : inst.EndLine] } if !inst.IsValid { inst.Errors = append(inst.Errors, node.Errors...) } if inst.Name == instruction.Onbuild && node.Next != nil && len(node.Next.Children) > 0 { inst.IsOnBuild = true node = node.Next.Children[0] inst.Name = node.Value inst.ArgsRaw = node.ArgsRaw inst.Flags = node.Flags } for n := node.Next; n != nil; n = n.Next { inst.Args = append(inst.Args, n.Value) } if _, ok := node.Attributes["json"]; ok { inst.IsJSONForm = true } if inst.Name == instruction.From { currentStage = spec.NewBuildStage() currentStage.FromInstruction = inst currentStage.Index = len(dockerfile.Stages) instStageIndex = -1 currentStage.StartLine = inst.StartLine if len(inst.Args) == 3 && strings.ToLower(inst.Args[1]) == "as" { currentStage.Name = inst.Args[2] dockerfile.StagesByName[currentStage.Name] = currentStage } if len(inst.Args) > 0 { var parts []string var hasDigest bool switch { case strings.Contains(inst.Args[0], ":"): parts = strings.Split(inst.Args[0], ":") case strings.Contains(inst.Args[0], "@"): parts = strings.Split(inst.Args[0], "@") hasDigest = true default: parts = append(parts, inst.Args[0]) } if len(parts) > 0 { if len(parts[0]) > 0 { if strings.HasPrefix(parts[0], "$") { argName := GetRefName(parts[0]) argVal, ok := dockerfile.FromArgs[argName] if !ok { currentStage.UnknownFromArgs[argName] = struct{}{} } else { currentStage.FromArgs[argName] = argVal } if len(parts) == 1 { if len(argVal) > 0 { switch { case strings.Contains(argVal, ":"): parts = strings.Split(argVal, ":") case strings.Contains(argVal, "@"): parts = strings.Split(argVal, "@") hasDigest = true default: parts = nil parts = append(parts, argVal) } if len(parts) == 1 { currentStage.Parent.Name = parts[0] } } currentStage.Parent.BuildArgAll = argName } else { currentStage.Parent.BuildArgName = argName currentStage.Parent.Name = argVal } } else { currentStage.Parent.Name = parts[0] } } else { currentStage.Parent.HasEmptyName = true } } if len(parts) == 2 { if len(parts[1]) > 0 { if strings.HasPrefix(parts[1], "$") { argName := GetRefName(parts[1]) argVal, ok := dockerfile.FromArgs[argName] if !ok { currentStage.UnknownFromArgs[argName] = struct{}{} } else { currentStage.FromArgs[argName] = argVal } if hasDigest { currentStage.Parent.BuildArgDigest = argName currentStage.Parent.Digest = argVal } else { currentStage.Parent.BuildArgTag = argName currentStage.Parent.Tag = argVal } } else { if hasDigest { currentStage.Parent.Digest = parts[1] } else { currentStage.Parent.Tag = parts[1] } } } else { if hasDigest { currentStage.Parent.HasEmptyDigest = true } else { currentStage.Parent.HasEmptyTag = true } } } if currentStage.Parent.Name != "" { if parentStage, ok := dockerfile.StagesByName[currentStage.Parent.Name]; ok { parentStage.IsUsed = true currentStage.Parent.ParentStage = parentStage currentStage.StageReferences[currentStage.Parent.Name] = parentStage } } } else { currentStage.Parent.HasEmptyName = true currentStage.Parent.HasEmptyTag = true currentStage.Parent.HasEmptyDigest = true } dockerfile.Stages = append(dockerfile.Stages, currentStage) dockerfile.LastStage = currentStage } dockerfile.AllInstructions = append(dockerfile.AllInstructions, inst) dockerfile.InstructionsByType[inst.Name] = append( dockerfile.InstructionsByType[inst.Name], inst) if currentStage != nil { currentStage.EndLine = inst.EndLine inst.StageID = currentStage.Index instStageIndex++ inst.StageIndex = instStageIndex currentStage.AllInstructions = append(currentStage.AllInstructions, inst) if inst.Name == instruction.Onbuild { currentStage.OnBuildInstructions = append(currentStage.OnBuildInstructions, inst) } else if inst.Name == instruction.Copy { for _, flag := range inst.Flags { if strings.HasPrefix(flag, "--from=") { fparts := strings.SplitN(flag, "=", 2) //possible values: stage index, stage name, image name var stageRef *spec.BuildStage refIdx, err := strconv.Atoi(fparts[1]) if err == nil { if refIdx >= 0 && refIdx < len(dockerfile.Stages) { stageRef = dockerfile.Stages[refIdx] } } else { stageRef = dockerfile.StagesByName[fparts[1]] } if stageRef != nil { stageRef.IsUsed = true currentStage.StageReferences[fparts[1]] = stageRef } else { currentStage.ExternalReferences[fparts[1]] = struct{}{} //todo: check if it references a local (or remote) image } } } } else if instruction.IsKnown(inst.Name) { currentStage.CurrentInstructions = append(currentStage.CurrentInstructions, inst) currentStage.CurrentInstructionsByType[inst.Name] = append( currentStage.CurrentInstructionsByType[inst.Name], inst) if inst.IsValid { switch inst.Name { case instruction.Arg: for _, iarg := range inst.Args { if iarg == "" { continue } if strings.Contains(iarg, "=") { iaparts := strings.SplitN(iarg, "=", 2) currentStage.BuildArgs[iaparts[0]] = iaparts[1] } else { currentStage.BuildArgs[iarg] = "" } } //only one ARG is supposed to be defined, but we'll use all //the 'ARG' value count lint check will detect the extra values //the k=v ARG values are also not parsed (unlike ENV k=v values) case instruction.Env: for i := 0; i < len(inst.Args) && (i+1) < len(inst.Args); i += 2 { if len(inst.Args[i]) == 0 { continue } currentStage.EnvVars[inst.Args[i]] = inst.Args[i+1] } } } } } else { if inst.Name == instruction.Arg { if inst.IsValid && len(inst.Args) > 0 { dockerfile.ArgInstructions = append(dockerfile.ArgInstructions, inst) parts := strings.Split(inst.Args[0], "=") argVal := "" if len(parts) == 2 { argVal = parts[1] } dockerfile.FromArgs[parts[0]] = argVal } //note: should have a lint check for ARG without 'args' } else { dockerfile.StagelessInstructions = append(dockerfile.StagelessInstructions, inst) } } if !inst.IsValid { if !instruction.IsKnown(inst.Name) { dockerfile.UnknownInstructions = append(dockerfile.UnknownInstructions, inst) if currentStage != nil { currentStage.UnknownInstructions = append(currentStage.UnknownInstructions, inst) } } else { dockerfile.InvalidInstructions = append(dockerfile.InvalidInstructions, inst) if currentStage != nil { currentStage.InvalidInstructions = append(currentStage.InvalidInstructions, inst) } } } } if dockerfile.LastStage != nil { dockerfile.LastStage.IsUsed = true } } dockerfile.Warnings = astParsed.Warnings return dockerfile, nil } func GetRefName(ref string) string { ref = strings.TrimPrefix(ref, "$") if strings.HasPrefix(ref, "{") && strings.HasSuffix(ref, "}") { ref = strings.TrimPrefix(ref, "{") ref = strings.TrimSuffix(ref, "}") } return ref } ================================================ FILE: pkg/docker/dockerfile/reverse/reverse.go ================================================ package reverse import ( "bytes" "encoding/json" "errors" "fmt" "os" "sort" "strconv" "strings" "time" "github.com/dustin/go-humanize" docker "github.com/fsouza/go-dockerclient" "github.com/google/shlex" log "github.com/sirupsen/logrus" ) var ( ErrBadInstPrefix = errors.New("bad instruction prefix") ) // Dockerfile represents the reverse engineered Dockerfile info type Dockerfile struct { Lines []string `json:"lines,omitempty"` Maintainers []string `json:"maintainers,omitempty"` AllUsers []string `json:"all_users,omitempty"` ExeUser string `json:"exe_user,omitempty"` ExposedPorts []string `json:"exposed_ports,omitempty"` ImageStack []*ImageInfo `json:"image_stack"` AllInstructions []*InstructionInfo `json:"all_instructions"` InstructionGroups [][]*InstructionInfo `json:"instruction_groups"` InstructionGroupsReverse [][]*InstructionInfo `json:"instruction_groups_reverse"` HasOnbuild bool `json:"has_onbuild"` } type ImageInfo struct { IsTopImage bool `json:"is_top_image"` ID string `json:"id"` FullName string `json:"full_name"` RepoName string `json:"repo_name"` VersionTag string `json:"version_tag"` RawTags []string `json:"raw_tags,omitempty"` CreateTime string `json:"create_time"` NewSize int64 `json:"new_size"` NewSizeHuman string `json:"new_size_human"` BaseImageID string `json:"base_image_id,omitempty"` Instructions []*InstructionInfo `json:"instructions"` } type InstructionInfo struct { Type string `json:"type"` Time string `json:"time"` //Time time.Time `json:"time"` IsLastInstruction bool `json:"is_last_instruction,omitempty"` IsNop bool `json:"is_nop"` IsExecForm bool `json:"is_exec_form,omitempty"` //is exec/json format (a valid field for RUN, ENTRYPOINT, CMD) LocalImageExists bool `json:"local_image_exists"` IntermediateImageID string `json:"intermediate_image_id,omitempty"` LayerIndex int `json:"layer_index"` //-1 for an empty layer LayerID string `json:"layer_id,omitempty"` LayerFSDiffID string `json:"layer_fsdiff_id,omitempty"` Size int64 `json:"size"` SizeHuman string `json:"size_human,omitempty"` Params string `json:"params,omitempty"` CommandSnippet string `json:"command_snippet"` CommandAll string `json:"command_all"` SystemCommands []string `json:"system_commands,omitempty"` Comment string `json:"comment,omitempty"` Author string `json:"author,omitempty"` EmptyLayer bool `json:"empty_layer,omitempty"` instPosition string imageFullName string RawTags []string `json:"raw_tags,omitempty"` Target string `json:"target,omitempty"` //for ADD and COPY SourceType string `json:"source_type,omitempty"` //for ADD and COPY IsBuildKitInstruction bool `json:"is_buildkit_instruction,omitempty"` BuildKitInfo string `json:"buildkit_info,omitempty"` TimeValue time.Time `json:"-"` InstSetTimeBucket time.Time `json:"inst_set_time_bucket,omitempty"` InstSetTimeIndex int `json:"inst_set_time_index"` InstSetTimeReverseIndex int `json:"inst_set_time_reverse_index"` } const ( buildkitCreatedBySuffix = "# buildkit" buildkitPrefix = "buildkit." buildkitDockerfilePrefix = "buildkit.dockerfile." buildkitDockerfileV0 = "buildkit.dockerfile.v0" ) //The 'History' API doesn't expose the 'author' in the records it returns //The 'author' field is useful in detecting if it's a Dockerfile instruction //or if it's created with something else. //One option is to combine the 'History' API data with the history data //from the image config JSON embedded in the image. //Another option is to rely on '#(nop)'. const ( defaultRunInstShell = "/bin/sh" notRunInstPrefix = "/bin/sh -c #(nop) " runInstShellPrefix = "/bin/sh -c " //without any ARG params runInstArgsPrefix = "|" ) const ( //MAINTAINER: instTypeMaintainer = "MAINTAINER" instPrefixMaintainer = "MAINTAINER " //ENTRYPOINT: instTypeEntrypoint = "ENTRYPOINT" instPrefixEntrypoint = "ENTRYPOINT " //CMD: instTypeCmd = "CMD" instPrefixCmd = "CMD " //USER: instTypeUser = "USER" instPrefixUser = "USER " //EXPOSE: instTypeExpose = "EXPOSE" instPrefixExpose = "EXPOSE " //WORKDIR: instTypeWorkdir = "WORKDIR" instPrefixWorkdir = "WORKDIR " //HEALTHCHECK: instTypeHealthcheck = "HEALTHCHECK" instPrefixHealthcheck = "HEALTHCHECK " instPrefixBasicEncHealthcheck = "HEALTHCHECK --" //ONBUILD: instTypeOnbuild = "ONBUILD" //RUN: instTypeRun = "RUN" instPrefixRun = "RUN " //ADD: instTypeAdd = "ADD" //COPY: instTypeCopy = "COPY" instTypeVolume = "VOLUME" instTypeEnv = "ENV" instTypeLabel = "LABEL" instTypeStopSignal = "STOPSIGNAL" instTypeShell = "SHELL" instTypeArg = "ARG" //shouldn't see it as an standalone instruction ) var instructionTypes = map[string]struct{}{ instTypeRun: {}, instTypeEntrypoint: {}, instTypeCmd: {}, instTypeUser: {}, instTypeExpose: {}, instTypeWorkdir: {}, instTypeHealthcheck: {}, instTypeOnbuild: {}, instTypeAdd: {}, instTypeCopy: {}, instTypeMaintainer: {}, instTypeVolume: {}, instTypeEnv: {}, instTypeLabel: {}, instTypeStopSignal: {}, instTypeShell: {}, instTypeArg: {}, } func isInstructionType(input string) bool { _, found := instructionTypes[input] return found } func hasInstructionPrefix(input string) bool { if !strings.Contains(input, " ") { return false } parts := strings.SplitN(input, " ", 2) return isInstructionType(parts[0]) } const ( mapPrefix = "map[" portMapKeySuffix = ":{}]" ) type tbrecord struct { index int instruction *InstructionInfo tb time.Time } const tbDuration = (15 * time.Minute) // DockerfileFromHistoryData recreates Dockerfile information from container image history func DockerfileFromHistoryData(data string) (*Dockerfile, error) { var imageHistory []docker.ImageHistory if err := json.NewDecoder(strings.NewReader(data)).Decode(&imageHistory); err != nil { return nil, err } return DockerfileFromHistoryStruct(imageHistory) } // DockerfileFromHistory recreates Dockerfile information from container image history func DockerfileFromHistory(apiClient *docker.Client, imageID string) (*Dockerfile, error) { //TODO: make it possible to pass the history information as a param //TODO: pass the other image metadata (including OCI and buildkit base image info) imageHistory, err := apiClient.ImageHistory(imageID) if err != nil { return nil, err } return DockerfileFromHistoryStruct(imageHistory) } // DockerfileFromHistoryStruct recreates Dockerfile information from container image history func DockerfileFromHistoryStruct(imageHistory []docker.ImageHistory) (*Dockerfile, error) { var out Dockerfile log.Tracef("\n\nreverse.DockerfileFromHistoryStruct - IMAGE HISTORY:\n%#v\n\n", imageHistory) var timeBuckets = map[time.Time][]tbrecord{} var reversedInstructions []*InstructionInfo var currentImageInfo *ImageInfo var prevImageID string imageLayerCount := len(imageHistory) imageLayerStart := imageLayerCount - 1 startNewImage := true if imageLayerCount > 0 { for idx := imageLayerStart; idx >= 0; idx-- { rawLine := imageHistory[idx].CreatedBy var isNop bool var inst string var isBuildKitInstruction bool if strings.HasSuffix(rawLine, buildkitCreatedBySuffix) { isBuildKitInstruction = true rawLine = strings.TrimSuffix(rawLine, buildkitCreatedBySuffix) } var rawInst string isRunInst := strings.HasPrefix(rawLine, instPrefixRun) if isRunInst { parts := strings.SplitN(rawLine, " ", 2) rawInst = parts[1] } else { rawInst = rawLine } if strings.Contains(rawLine, "#(nop)") { isNop = true } isExecForm := false switch { case len(rawInst) == 0: inst = "" //NOTE: //still keeping a placeholder for the empty instructions in history //because not all builders populate all history record fields (e.g., buildkits) case strings.HasPrefix(rawInst, notRunInstPrefix): //Instructions that are not RUN inst = strings.TrimPrefix(rawInst, notRunInstPrefix) case strings.HasPrefix(rawInst, runInstShellPrefix): //RUN instruction in shell form runData := strings.TrimPrefix(rawInst, runInstShellPrefix) if strings.Contains(runData, "&&") { parts := strings.Split(runData, "&&") for i := range parts { partPrefix := "" if i != 0 { partPrefix = "\t" } parts[i] = partPrefix + strings.TrimSpace(parts[i]) } runDataFormatted := strings.Join(parts, " && \\\n") inst = instPrefixRun + runDataFormatted } else { inst = instPrefixRun + runData } default: //TODO: need to refactor processed := false //rawInst := rawLine if strings.HasPrefix(rawInst, runInstArgsPrefix) { var err error inst, processed, isExecForm, err = stripRunInstArgs(rawInst) //should not be ':=' if err != nil { log.Debugf("stripRunInstArgs: err -> %v\n", err) } } if hasInstructionPrefix(rawInst) { inst = rawInst } else { if !processed { //default to RUN instruction in exec form isExecForm = true inst = instPrefixRun + rawInst if outArray, err := shlex.Split(rawInst); err == nil { var outJson bytes.Buffer encoder := json.NewEncoder(&outJson) encoder.SetEscapeHTML(false) err := encoder.Encode(outArray) if err == nil { inst = fmt.Sprintf("RUN %s", outJson.String()) } } } } } //NOTE: Dockerfile instructions can be any case, but the instructions from history are always uppercase cleanInst := strings.TrimSpace(inst) if strings.HasPrefix(cleanInst, instPrefixEntrypoint) { cleanInst = strings.Replace(cleanInst, "&{[", "[", -1) cleanInst = strings.Replace(cleanInst, "]}", "]", -1) entrypointShellFormPrefix := `ENTRYPOINT ["/bin/sh" "-c" "` if strings.HasPrefix(cleanInst, entrypointShellFormPrefix) { instData := strings.TrimPrefix(cleanInst, entrypointShellFormPrefix) instData = strings.TrimSuffix(instData, `"]`) cleanInst = instPrefixEntrypoint + instData } else { isExecForm = true instData := strings.TrimPrefix(cleanInst, instPrefixEntrypoint) instData = fixJSONArray(instData) cleanInst = instPrefixEntrypoint + instData } } if strings.HasPrefix(cleanInst, instPrefixCmd) { cmdShellFormPrefix := `CMD ["/bin/sh" "-c" "` if strings.HasPrefix(cleanInst, cmdShellFormPrefix) { instData := strings.TrimPrefix(cleanInst, cmdShellFormPrefix) instData = strings.TrimSuffix(instData, `"]`) cleanInst = instPrefixCmd + instData } else { isExecForm = true instData := strings.TrimPrefix(cleanInst, instPrefixCmd) instData = fixJSONArray(instData) cleanInst = instPrefixCmd + instData } } if strings.HasPrefix(cleanInst, instPrefixMaintainer) { parts := strings.SplitN(cleanInst, " ", 2) if len(parts) == 2 { maintainer := strings.TrimSpace(parts[1]) out.Maintainers = append(out.Maintainers, maintainer) } else { log.Infof("ReverseDockerfileFromHistory - MAINTAINER - unexpected number of user parts - %v", len(parts)) } } if strings.HasPrefix(cleanInst, instPrefixUser) { parts := strings.SplitN(cleanInst, " ", 2) if len(parts) == 2 { userName := strings.TrimSpace(parts[1]) out.AllUsers = append(out.AllUsers, userName) out.ExeUser = userName } else { log.Infof("ReverseDockerfileFromHistory - unexpected number of user parts - %v", len(parts)) } } if strings.HasPrefix(cleanInst, instPrefixExpose) { parts := strings.SplitN(cleanInst, " ", 2) if len(parts) == 2 { portInfo := strings.TrimSpace(parts[1]) if strings.HasPrefix(portInfo, mapPrefix) && strings.HasSuffix(portInfo, portMapKeySuffix) { portInfo = strings.TrimPrefix(portInfo, mapPrefix) portInfo = strings.TrimSuffix(portInfo, portMapKeySuffix) cleanInst = fmt.Sprintf("EXPOSE %s", portInfo) } out.ExposedPorts = append(out.ExposedPorts, portInfo) } else { log.Infof("ReverseDockerfileFromHistory - unexpected number of expose parts - %v", len(parts)) } } instInfo := InstructionInfo{ IsNop: isNop, IsExecForm: isExecForm, CommandAll: cleanInst, Time: time.Unix(imageHistory[idx].Created, 0).UTC().Format(time.RFC3339), TimeValue: time.Unix(imageHistory[idx].Created, 0), Comment: imageHistory[idx].Comment, RawTags: imageHistory[idx].Tags, Size: imageHistory[idx].Size, IsBuildKitInstruction: isBuildKitInstruction, InstSetTimeIndex: -1, InstSetTimeReverseIndex: -1, } instInfo.InstSetTimeBucket = instInfo.TimeValue.Truncate(tbDuration) if strings.HasPrefix(instInfo.Comment, buildkitPrefix) { instInfo.IsBuildKitInstruction = true } instParts := strings.SplitN(cleanInst, " ", 2) if len(instParts) == 2 { instInfo.Type = instParts[0] } if instInfo.Type == instTypeOnbuild { out.HasOnbuild = true } if instInfo.CommandAll == "" { instInfo.Type = "NONE" instInfo.CommandAll = "# no instruction info" } if instInfo.Type == instTypeRun { var cmdParts []string cmds := strings.Replace(instParts[1], "\\", "", -1) if strings.Contains(cmds, "&&") { cmdParts = strings.Split(cmds, "&&") } else { cmdParts = strings.Split(cmds, ";") } for _, cmd := range cmdParts { cmd = strings.TrimSpace(cmd) cmd = strings.Replace(cmd, "\t", "", -1) cmd = strings.Replace(cmd, "\n", "", -1) instInfo.SystemCommands = append(instInfo.SystemCommands, cmd) } } else { if len(instParts) == 2 { instInfo.Params = instParts[1] } } if instInfo.Type == instTypeWorkdir { instInfo.SystemCommands = append(instInfo.SystemCommands, fmt.Sprintf("mkdir -p %s", instParts[1])) } switch instInfo.Type { case instTypeAdd, instTypeCopy: if strings.Contains(instInfo.Params, ":") && strings.Contains(instInfo.Params, " in ") { pparts := strings.SplitN(instInfo.Params, ":", 2) if len(pparts) == 2 { instInfo.SourceType = pparts[0] tparts := strings.SplitN(pparts[1], " in ", 2) if len(tparts) == 2 { instInfo.Target = tparts[1] instInfo.CommandAll = fmt.Sprintf("%s %s:%s %s", instInfo.Type, instInfo.SourceType, tparts[0], instInfo.Target) } } } } if instInfo.Type == instTypeHealthcheck { healthInst, _, err := deserialiseHealtheckInstruction(instInfo.CommandAll) if err != nil { log.Errorf("ReverseDockerfileFromHistory - HEALTHCHECK - deserialiseHealtheckInstruction - %v", err) } instInfo.CommandAll = healthInst } if len(instInfo.CommandAll) > 44 { instInfo.CommandSnippet = fmt.Sprintf("%s...", instInfo.CommandAll[0:44]) } else { instInfo.CommandSnippet = instInfo.CommandAll } if instInfo.Size > 0 { instInfo.SizeHuman = humanize.Bytes(uint64(instInfo.Size)) } if imageHistory[idx].ID != "" { instInfo.LocalImageExists = true instInfo.IntermediateImageID = imageHistory[idx].ID } if startNewImage { startNewImage = false currentImageInfo = &ImageInfo{ BaseImageID: prevImageID, NewSize: 0, } } currentImageInfo.NewSize += imageHistory[idx].Size currentImageInfo.Instructions = append(currentImageInfo.Instructions, &instInfo) out.AllInstructions = append(out.AllInstructions, &instInfo) instPosition := "intermediate" if idx == imageLayerStart { instPosition = "first" //first instruction in the list } if idx == 0 || (len(imageHistory[idx].Tags) > 0) { instPosition = "last" //last in an image currentImageInfo.ID = imageHistory[idx].ID prevImageID = currentImageInfo.ID if instInfo.IntermediateImageID == currentImageInfo.ID { instInfo.IntermediateImageID = "" instInfo.IsLastInstruction = true } currentImageInfo.CreateTime = instInfo.Time currentImageInfo.RawTags = imageHistory[idx].Tags if len(imageHistory[idx].Tags) > 0 { instInfo.imageFullName = imageHistory[idx].Tags[0] currentImageInfo.FullName = imageHistory[idx].Tags[0] if tagInfo := strings.Split(imageHistory[idx].Tags[0], ":"); len(tagInfo) > 1 { currentImageInfo.RepoName = tagInfo[0] currentImageInfo.VersionTag = tagInfo[1] } } currentImageInfo.NewSizeHuman = humanize.Bytes(uint64(currentImageInfo.NewSize)) out.ImageStack = append(out.ImageStack, currentImageInfo) startNewImage = true } instInfo.instPosition = instPosition reversedInstructions = append(reversedInstructions, &instInfo) tbr := tbrecord{ index: len(reversedInstructions) - 1, instruction: &instInfo, tb: instInfo.InstSetTimeBucket, } timeBuckets[instInfo.InstSetTimeBucket] = append(timeBuckets[instInfo.InstSetTimeBucket], tbr) } if currentImageInfo != nil { currentImageInfo.IsTopImage = true } } tkeys := make([]time.Time, 0, len(timeBuckets)) for k := range timeBuckets { tkeys = append(tkeys, k) } sort.SliceStable(tkeys, func(i, j int) bool { return tkeys[i].Before(tkeys[j]) }) tkListLen := len(tkeys) for i, k := range tkeys { tbrList := timeBuckets[k] for _, tbr := range tbrList { tbr.instruction.InstSetTimeIndex = i tbr.instruction.InstSetTimeReverseIndex = tkListLen - 1 - i } } out.InstructionGroups = make([][]*InstructionInfo, tkListLen) out.InstructionGroupsReverse = make([][]*InstructionInfo, tkListLen) //Always adding "FROM scratch" as the first line //GOAL: to have a reversed Dockerfile that can be used to build a new image out.Lines = append(out.Lines, "FROM scratch") prevInstSetTimeIndex := -1 for idx, instInfo := range reversedInstructions { if instInfo.instPosition == "first" { out.Lines = append(out.Lines, "# new image") } if instInfo.InstSetTimeIndex != prevInstSetTimeIndex { out.Lines = append(out.Lines, fmt.Sprintf("\n# instruction set group %d\n", instInfo.InstSetTimeIndex+1)) prevInstSetTimeIndex = instInfo.InstSetTimeIndex } out.InstructionGroups[instInfo.InstSetTimeIndex] = append(out.InstructionGroups[instInfo.InstSetTimeIndex], instInfo) out.InstructionGroupsReverse[instInfo.InstSetTimeReverseIndex] = append(out.InstructionGroupsReverse[instInfo.InstSetTimeReverseIndex], instInfo) if instInfo.Comment != "" { outComment := fmt.Sprintf("# %s", instInfo.Comment) if instInfo.IsBuildKitInstruction { outComment = fmt.Sprintf("%s (a buildkit instruction)", outComment) } out.Lines = append(out.Lines, outComment) } else if instInfo.IsBuildKitInstruction { out.Lines = append(out.Lines, "# a buildkit instruction") } out.Lines = append(out.Lines, instInfo.CommandAll) if instInfo.instPosition == "last" { commentText := fmt.Sprintf("# end of image: %s (id: %s tags: %s)", instInfo.imageFullName, instInfo.IntermediateImageID, strings.Join(instInfo.RawTags, ",")) out.Lines = append(out.Lines, commentText) out.Lines = append(out.Lines, "") if idx < (len(reversedInstructions) - 1) { out.Lines = append(out.Lines, "# new image") } } } log.Debugf("IMAGE INSTRUCTIONS:") for _, iiLine := range out.Lines { log.Debug(iiLine) } return &out, nil //BASE LAYER IDENTIFICATION: //* tags from the instruction history //* instruction time-based clustering //* instruction patterns (e.g., base images often have their own ENTRYPOINT/CMD instructions) //* base image metadata from the image labels (e.g., "org.opencontainers.image.base.digest" OCI label) //* database with pre-indexed common base image digests (will require a network lookup) } func stripRunInstArgs(rawInst string) (string, bool, bool, error) { parts := strings.SplitN(rawInst, " ", 2) if len(parts) == 2 { withArgs := strings.TrimSpace(parts[1]) argNumStr := parts[0][1:] argNum, err := strconv.Atoi(argNumStr) if err == nil { if withArgsArray, err := shlex.Split(withArgs); err == nil { if len(withArgsArray) > argNum { rawInstParts := withArgsArray[argNum:] isExecForm := false processed := true inst := "" if len(rawInstParts) > 2 && rawInstParts[0] == defaultRunInstShell && rawInstParts[1] == "-c" { isExecForm = false rawInstParts = rawInstParts[2:] inst = fmt.Sprintf("RUN %s", strings.Join(rawInstParts, " ")) inst = strings.TrimSpace(inst) } else { isExecForm = true var outJson bytes.Buffer encoder := json.NewEncoder(&outJson) encoder.SetEscapeHTML(false) err = encoder.Encode(rawInstParts) if err == nil { inst = fmt.Sprintf("RUN %s", outJson.String()) } } return inst, processed, isExecForm, err } else { log.Infof("reverse.stripRunInstArgs - RUN with ARGs - malformed - %v (%v)", rawInst, err) } } else { log.Infof("reverse.stripRunInstArgs - RUN with ARGs - malformed - %v (%v)", rawInst, err) } } else { log.Infof("reverse.stripRunInstArgs - RUN with ARGs - malformed number of ARGs - %v (%v)", rawInst, err) } } else { log.Infof("reverse.stripRunInstArgs - RUN with ARGs - unexpected number of parts - %v", rawInst) } return "", false, false, nil } // SaveDockerfileData saves the Dockerfile information to a file func SaveDockerfileData(fatImageDockerfileLocation string, fatImageDockerfileLines []string) error { var data bytes.Buffer data.WriteString(strings.Join(fatImageDockerfileLines, "\n")) return os.WriteFile(fatImageDockerfileLocation, data.Bytes(), 0644) } func fixJSONArray(in string) string { data := in if data[0] == '[' { data = data[1 : len(data)-1] } outArray, err := shlex.Split(data) if err != nil { return in } var out bytes.Buffer encoder := json.NewEncoder(&out) encoder.SetEscapeHTML(false) err = encoder.Encode(outArray) if err != nil { return in } return out.String() } func deserialiseHealtheckInstruction(data string) (string, *docker.HealthConfig, error) { //Example: // HEALTHCHECK &{["CMD" "/healthcheck" "8080"] "5s" "10s" "0s" '\x03'} // HEALTHCHECK --interval=5s --timeout=10s --retries=3 CMD [ "/healthcheck", "8080" ] //Note: CMD can be specified with both formats (shell and json) //Buildah example (raw/full): // /bin/sh -c #(nop) HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost/ || exit 1 cleanInst := strings.TrimSpace(data) if !strings.HasPrefix(cleanInst, instPrefixHealthcheck) { return "", nil, ErrBadInstPrefix } var config docker.HealthConfig var strTest string if strings.HasPrefix(cleanInst, instPrefixBasicEncHealthcheck) || !strings.Contains(cleanInst, "&{[") { //handling the basic Buildah encoding var err error if strings.Contains(cleanInst, "--interval=") { vparts := strings.SplitN(cleanInst, "--interval=", 2) vparts = strings.SplitN(vparts[1], " ", 2) config.Interval, err = time.ParseDuration(vparts[0]) if err != nil { log.Errorf("[%s] config.Interval err = %v", vparts[0], err) } } if strings.Contains(cleanInst, "--timeout=") { vparts := strings.SplitN(cleanInst, "--timeout=", 2) vparts = strings.SplitN(vparts[1], " ", 2) config.Timeout, err = time.ParseDuration(vparts[0]) if err != nil { log.Errorf("[%s] config.Timeout err = %v", vparts[0], err) } } if strings.Contains(cleanInst, "--start-period=") { vparts := strings.SplitN(cleanInst, "--start-period=", 2) vparts = strings.SplitN(vparts[1], " ", 2) config.StartPeriod, err = time.ParseDuration(vparts[0]) if err != nil { log.Errorf("[%s] config.StartPeriod err = %v", vparts[0], err) } } if strings.Contains(cleanInst, "--retries=") { vparts := strings.SplitN(cleanInst, "--retries=", 2) vparts = strings.SplitN(vparts[1], " ", 2) retries, err := strconv.ParseInt(vparts[0], 16, 64) if err != nil { log.Errorf("[%s] config.Retries err = %v", vparts[0], err) } else { config.Retries = int(retries) } } if strings.Contains(cleanInst, " CMD ") { parts := strings.SplitN(cleanInst, " CMD ", 2) strTest = fmt.Sprintf("CMD %s", parts[1]) config.Test = []string{"CMD", parts[1]} } } else { cleanInst = strings.Replace(cleanInst, "&{[", "", -1) //Splits the string into two parts - first part pointer to array of string and rest of the string with } in end. instParts := strings.SplitN(cleanInst, "]", 2) // Cleans HEALTHCHECK part and splits the first part further parts := strings.SplitN(instParts[0], " ", 2) // joins the first part of the string instPart1 := strings.Join(parts[1:], " ") // removes quotes from the first part of the string instPart1 = strings.ReplaceAll(instPart1, "\"", "") // cleans it to assign it to the pointer config.Test config.Test = strings.Split(instPart1, " ") // removes the } from the second part of the string instPart2 := strings.Replace(instParts[1], "}", "", -1) // removes extra spaces from string instPart2 = strings.TrimSpace(instPart2) paramParts := strings.SplitN(instPart2, " ", 4) for i, param := range paramParts { paramParts[i] = strings.Trim(param, "\"'") } var err error config.Interval, err = time.ParseDuration(paramParts[0]) if err != nil { log.Errorf("[%s] config.Interval err = %v", paramParts[0], err) } config.Timeout, err = time.ParseDuration(paramParts[1]) if err != nil { log.Errorf("[%s] config.Timeout err = %v", paramParts[1], err) } config.StartPeriod, err = time.ParseDuration(paramParts[2]) if err != nil { log.Errorf("[%s] config.StartPeriod err = %v", paramParts[2], err) } var retries int64 if strings.Index(paramParts[3], `\x`) != -1 { // retries are hex encoded retries, err = strconv.ParseInt(strings.TrimPrefix(paramParts[3], `\x`), 16, 64) } else if strings.Index(paramParts[3], `\U`) != -1 { // retries are a unicode string retries, err = strconv.ParseInt(strings.TrimPrefix(paramParts[3], `\U`), 16, 64) } else if strings.Index(paramParts[3], `\`) == 0 { // retries is printed as a C-escape if len(paramParts[3]) != 2 { err = fmt.Errorf("expected retries (%s) to be an escape sequence", paramParts[3]) } else { escapeCodes := map[byte]int64{ byte('a'): 7, byte('b'): 8, byte('t'): 9, byte('n'): 10, byte('v'): 11, byte('f'): 12, byte('r'): 13, } var ok bool if retries, ok = escapeCodes[(paramParts[3])[1]]; !ok { err = fmt.Errorf("got an invalid escape sequence: %s", paramParts[3]) } } } else { retries = int64((paramParts[3])[0]) } if err != nil { log.Errorf("[%s] config.Retries err = %v", paramParts[3], err) } else { config.Retries = int(retries) } var testType string if len(config.Test) > 0 { testType = config.Test[0] } switch testType { case "NONE": strTest = "NONE" case "CMD": if len(config.Test) == 1 { strTest = "CMD []" } else { strTest = fmt.Sprintf(`CMD ["%s"]`, strings.Join(config.Test[1:], `", "`)) } case "CMD-SHELL": cmdShell := strings.Join(config.Test[1:], " ") strTest = fmt.Sprintf("CMD %s", cmdShell) config.Test = []string{config.Test[0], cmdShell} } } defaultTimeout := false defaultInterval := false defaultRetries := false defaultStartPeriod := false if config.Timeout == 0 { defaultTimeout = true config.Timeout = 30 * time.Second } if config.Interval == 0 { defaultInterval = true config.Interval = 30 * time.Second } if config.Retries == 0 { defaultRetries = true config.Retries = 3 } if config.StartPeriod == 0 { defaultStartPeriod = true } type HealthCheckFlag struct { flagFmtStr string isDefault bool value interface{} } healthInst := "HEALTHCHECK" for _, flag := range []HealthCheckFlag{ {flagFmtStr: "--interval=%v", isDefault: defaultInterval, value: config.Interval}, {flagFmtStr: "--timeout=%v", isDefault: defaultTimeout, value: config.Timeout}, {flagFmtStr: "--start-period=%v", isDefault: defaultStartPeriod, value: config.StartPeriod}, {flagFmtStr: "--retries=%d", isDefault: defaultRetries, value: config.Retries}, } { if !flag.isDefault { healthInst = healthInst + " " + fmt.Sprintf(flag.flagFmtStr, flag.value) } } healthInst += " " + strTest if strTest == "NONE" { healthInst = "HEALTHCHECK NONE" } return healthInst, &config, nil } // // https://docs.docker.com/engine/reference/builder/ // ================================================ FILE: pkg/docker/dockerfile/reverse/reverse_test.go ================================================ package reverse import ( "fmt" "testing" "time" dockerclient "github.com/fsouza/go-dockerclient" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestHealthCheck(t *testing.T) { type TestData struct { input string reconstructedHealthcheck string expectedHealthConf dockerclient.HealthConfig } testHealthCheckData := []TestData{ { input: `HEALTHCHECK &{["CMD" "curl" "--fail-with-body" "127.0.0.1:5000"] "15s" "1s" "20s" '\x0A'}`, reconstructedHealthcheck: `HEALTHCHECK --interval=15s --timeout=1s --start-period=20s --retries=10 CMD ["curl", "--fail-with-body", "127.0.0.1:5000"]`, expectedHealthConf: dockerclient.HealthConfig{ Interval: 15 * time.Second, Timeout: 1 * time.Second, StartPeriod: 20 * time.Second, Retries: 10, Test: []string{"CMD", "curl", "--fail-with-body", "127.0.0.1:5000"}, }, }, { input: `HEALTHCHECK &{["CMD-SHELL" "cat /etc/resolv.conf"] "20s" "10s" "5s" '\x02'}`, reconstructedHealthcheck: "HEALTHCHECK --interval=20s --timeout=10s --start-period=5s --retries=2 CMD cat /etc/resolv.conf", expectedHealthConf: dockerclient.HealthConfig{ Interval: 20 * time.Second, Timeout: 10 * time.Second, StartPeriod: 5 * time.Second, Retries: 2, Test: []string{"CMD-SHELL", "cat /etc/resolv.conf"}, }, }, { input: `HEALTHCHECK &{["CMD-SHELL" "/opt/up-yet.sh"] "0s" "0s" "0s" '\x00'}`, reconstructedHealthcheck: "HEALTHCHECK CMD /opt/up-yet.sh", expectedHealthConf: dockerclient.HealthConfig{ Test: []string{"CMD-SHELL", "/opt/up-yet.sh"}, Interval: 30 * time.Second, Timeout: 30 * time.Second, StartPeriod: 0 * time.Second, Retries: 3, }, }, { input: `HEALTHCHECK &{["CMD" "cat" "/etc/resolv.conf"] "0s" "0s" "0s" '!'}`, reconstructedHealthcheck: `HEALTHCHECK --retries=33 CMD ["cat", "/etc/resolv.conf"]`, expectedHealthConf: dockerclient.HealthConfig{ Test: []string{"CMD", "cat", "/etc/resolv.conf"}, Interval: 30 * time.Second, Timeout: 30 * time.Second, StartPeriod: 0 * time.Second, Retries: 33, }, }, { input: `HEALTHCHECK &{["CMD-SHELL" "ss -tulpn|grep 22"] "2s" "0s" "0s" '\U00051615'}`, reconstructedHealthcheck: `HEALTHCHECK --interval=2s --retries=333333 CMD ss -tulpn|grep 22`, expectedHealthConf: dockerclient.HealthConfig{ Test: []string{"CMD-SHELL", "ss -tulpn|grep 22"}, Interval: 2 * time.Second, Timeout: 30 * time.Second, StartPeriod: 0 * time.Second, Retries: 333333, }, }, { input: `HEALTHCHECK &{["CMD" "/bin/uptime"] "0s" "1h23m20s" "0s" '\n'}`, reconstructedHealthcheck: `HEALTHCHECK --timeout=1h23m20s --retries=10 CMD ["/bin/uptime"]`, expectedHealthConf: dockerclient.HealthConfig{ Test: []string{"CMD", "/bin/uptime"}, Interval: 30 * time.Second, Timeout: 5000 * time.Second, StartPeriod: 0 * time.Second, Retries: 10, }, }, } for retries := 1; retries <= 31; retries++ { testHealthCheckData = append(testHealthCheckData, TestData{ input: fmt.Sprintf(`HEALTHCHECK &{["CMD" "/bin/uptime"] "0s" "1h23m20s" "0s" '%q'}`, retries), reconstructedHealthcheck: fmt.Sprintf(`HEALTHCHECK --timeout=1h23m20s --retries=%d CMD ["/bin/uptime"]`, retries), expectedHealthConf: dockerclient.HealthConfig{ Test: []string{"CMD", "/bin/uptime"}, Interval: 30 * time.Second, Timeout: 5000 * time.Second, StartPeriod: 0 * time.Second, Retries: retries, }, }) } for _, testData := range testHealthCheckData { res, healthConf, err := deserialiseHealtheckInstruction(testData.input) require.NoError(t, err, "No error should occur") assert.Equal(t, &testData.expectedHealthConf, healthConf) assert.Equal(t, testData.reconstructedHealthcheck, res) } } ================================================ FILE: pkg/docker/dockerfile/spec/spec.go ================================================ // Package spec describes the Dockerfile data model. package spec import ( "github.com/slimtoolkit/slim/pkg/docker/instruction" ) type ParentImage struct { Name string `json:"name,omitempty"` Tag string `json:"tag,omitempty"` Digest string `json:"digest,omitempty"` BuildArgAll string `json:"-"` BuildArgName string `json:"-"` BuildArgTag string `json:"-"` BuildArgDigest string `json:"-"` HasEmptyName bool `json:"-"` HasEmptyTag bool `json:"-"` HasEmptyDigest bool `json:"-"` ParentStage *BuildStage `json:"-"` } type BuildStage struct { Index int `json:"index"` StartLine int `json:"start_line"` EndLine int `json:"end_line"` Name string `json:"name,omitempty"` Parent ParentImage `json:"parent"` AllInstructions []*instruction.Field `json:"-"` CurrentInstructions []*instruction.Field `json:"-"` OnBuildInstructions []*instruction.Field `json:"-"` UnknownInstructions []*instruction.Field `json:"-"` InvalidInstructions []*instruction.Field `json:"-"` //not including unknown instructions CurrentInstructionsByType map[string][]*instruction.Field `json:"-"` FromInstruction *instruction.Field `json:"-"` ArgInstructions []*instruction.Field `json:"-"` EnvInstructions []*instruction.Field `json:"-"` EnvVars map[string]string `json:"-"` BuildArgs map[string]string `json:"-"` FromArgs map[string]string `json:"-"` //"FROM" ARGs used by the stage UnknownFromArgs map[string]struct{} `json:"-"` IsUsed bool `json:"-"` StageReferences map[string]*BuildStage `json:"-"` ExternalReferences map[string]struct{} `json:"-"` } func NewBuildStage() *BuildStage { return &BuildStage{ CurrentInstructionsByType: map[string][]*instruction.Field{}, EnvVars: map[string]string{}, BuildArgs: map[string]string{}, FromArgs: map[string]string{}, UnknownFromArgs: map[string]struct{}{}, StageReferences: map[string]*BuildStage{}, ExternalReferences: map[string]struct{}{}, } } type Dockerfile struct { Name string Location string Lines []string FromArgs map[string]string //all "FROM" ARGs Stages []*BuildStage StagesByName map[string]*BuildStage LastStage *BuildStage StagelessInstructions []*instruction.Field ArgInstructions []*instruction.Field AllInstructions []*instruction.Field InstructionsByType map[string][]*instruction.Field UnknownInstructions []*instruction.Field InvalidInstructions []*instruction.Field //not including unknown instructions Warnings []string } func NewDockerfile() *Dockerfile { return &Dockerfile{ FromArgs: map[string]string{}, StagesByName: map[string]*BuildStage{}, InstructionsByType: map[string][]*instruction.Field{}, } } ================================================ FILE: pkg/docker/dockerignore/dockerignore.go ================================================ // Package dockerignore contains the code to deal with the .dockerignore files. package dockerignore import ( "bufio" "bytes" "errors" "fmt" "io" "os" "path/filepath" "regexp" "strings" "text/scanner" ) const ( filename = ".dockerignore" ) type Matcher struct { Location string Exists bool Patterns []string } func (m *Matcher) Match(fpath string) (bool, error) { //Docker's dockerignore matching (including the '.' exception) pm, err := newPatternMatcher(m.Patterns) if err != nil { return false, err } fpath = filepath.Clean(fpath) if fpath == "." { return false, nil } return pm.Matches(fpath) } func Load(location string) (*Matcher, error) { location = filepath.Clean(location) if _, err := os.Stat(location); err != nil { return nil, err } matcher := &Matcher{ Location: location, } fpath := filepath.Join(location, filename) fo, err := os.Open(fpath) if err != nil { if os.IsNotExist(err) { return matcher, nil } else { return nil, err } } defer fo.Close() matcher.Exists = true patterns, err := readPatterns(fo) if err != nil { return nil, err } matcher.Patterns = patterns return matcher, nil } func readPatterns(reader io.Reader) ([]string, error) { // Docker's dockerignore function to read the patterns if reader == nil { return nil, nil } scanner := bufio.NewScanner(reader) var excludes []string currentLine := 0 utf8bom := []byte{0xEF, 0xBB, 0xBF} for scanner.Scan() { scannedBytes := scanner.Bytes() // We trim UTF8 BOM if currentLine == 0 { scannedBytes = bytes.TrimPrefix(scannedBytes, utf8bom) } pattern := string(scannedBytes) currentLine++ // Lines starting with # (comments) are ignored before processing if strings.HasPrefix(pattern, "#") { continue } pattern = strings.TrimSpace(pattern) if pattern == "" { continue } // normalize absolute paths to paths relative to the context // (taking care of '!' prefix) invert := pattern[0] == '!' if invert { pattern = strings.TrimSpace(pattern[1:]) } if len(pattern) > 0 { pattern = filepath.Clean(pattern) pattern = filepath.ToSlash(pattern) if len(pattern) > 1 && pattern[0] == '/' { pattern = pattern[1:] } } if invert { pattern = "!" + pattern } excludes = append(excludes, pattern) } if err := scanner.Err(); err != nil { return nil, fmt.Errorf("Error reading .dockerignore: %v", err) } return excludes, nil } //Docker's pattern matching type patternMatcher struct { patterns []*pattern exclusions bool } func newPatternMatcher(patterns []string) (*patternMatcher, error) { pm := &patternMatcher{ patterns: make([]*pattern, 0, len(patterns)), } for _, p := range patterns { // Eliminate leading and trailing whitespace. p = strings.TrimSpace(p) if p == "" { continue } p = filepath.Clean(p) newp := &pattern{} if p[0] == '!' { if len(p) == 1 { return nil, errors.New("illegal exclusion pattern: \"!\"") } newp.exclusion = true p = p[1:] pm.exclusions = true } // Do some syntax checking on the pattern. // filepath's Match() has some really weird rules that are inconsistent // so instead of trying to dup their logic, just call Match() for its // error state and if there is an error in the pattern return it. // If this becomes an issue we can remove this since its really only // needed in the error (syntax) case - which isn't really critical. if _, err := filepath.Match(p, "."); err != nil { return nil, err } newp.cleanedPattern = p newp.dirs = strings.Split(p, string(os.PathSeparator)) pm.patterns = append(pm.patterns, newp) } return pm, nil } func (pm *patternMatcher) Matches(file string) (bool, error) { matched := false file = filepath.FromSlash(file) parentPath := filepath.Dir(file) parentPathDirs := strings.Split(parentPath, string(os.PathSeparator)) for _, p := range pm.patterns { negative := false if p.exclusion { negative = true } match, err := p.match(file) if err != nil { return false, err } if !match && parentPath != "." { // Check to see if the pattern matches one of our parent dirs. if len(p.dirs) <= len(parentPathDirs) { match, _ = p.match(strings.Join(parentPathDirs[:len(p.dirs)], string(os.PathSeparator))) } } if match { matched = !negative } } return matched, nil } type pattern struct { cleanedPattern string dirs []string regexp *regexp.Regexp exclusion bool } func (p *pattern) match(path string) (bool, error) { if p.regexp == nil { if err := p.compile(); err != nil { return false, filepath.ErrBadPattern } } b := p.regexp.MatchString(path) return b, nil } func (p *pattern) compile() error { regStr := "^" patternStr := p.cleanedPattern // Go through the pattern and convert it to a regexp. // We use a scanner so we can support utf-8 chars. var scan scanner.Scanner scan.Init(strings.NewReader(patternStr)) sl := string(os.PathSeparator) escSL := sl if sl == `\` { escSL += `\` } for scan.Peek() != scanner.EOF { ch := scan.Next() if ch == '*' { if scan.Peek() == '*' { // is some flavor of "**" scan.Next() // Treat **/ as ** so eat the "/" if string(scan.Peek()) == sl { scan.Next() } if scan.Peek() == scanner.EOF { // is "**EOF" - to align with .gitignore just accept all regStr += ".*" } else { // is "**" // Note that this allows for any # of /'s (even 0) because // the .* will eat everything, even /'s regStr += "(.*" + escSL + ")?" } } else { // is "*" so map it to anything but "/" regStr += "[^" + escSL + "]*" } } else if ch == '?' { // "?" is any char except "/" regStr += "[^" + escSL + "]" } else if ch == '.' || ch == '$' { // Escape some regexp special chars that have no meaning // in golang's filepath.Match regStr += `\` + string(ch) } else if ch == '\\' { // escape next char. Note that a trailing \ in the pattern // will be left alone (but need to escape it) if sl == `\` { // On windows map "\" to "\\", meaning an escaped backslash, // and then just continue because filepath.Match on // Windows doesn't allow escaping at all regStr += escSL continue } if scan.Peek() != scanner.EOF { regStr += `\` + string(scan.Next()) } else { regStr += `\` } } else { regStr += string(ch) } } regStr += "$" re, err := regexp.Compile(regStr) if err != nil { return err } p.regexp = re return nil } ================================================ FILE: pkg/docker/dockerimage/dockerimage.go ================================================ package dockerimage import ( "archive/tar" "bytes" "compress/gzip" "container/heap" "crypto/sha1" "encoding/hex" "encoding/json" "errors" "fmt" "io" "os" "path/filepath" "regexp" "strings" "time" "unicode/utf8" "github.com/bmatcuk/doublestar/v3" "github.com/dustin/go-humanize" oci "github.com/opencontainers/image-spec/specs-go/v1" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/certdiscover" "github.com/slimtoolkit/slim/pkg/docker/dockerutil" "github.com/slimtoolkit/slim/pkg/sysidentity" "github.com/slimtoolkit/slim/pkg/system" "github.com/slimtoolkit/slim/pkg/util/fsutil" "github.com/slimtoolkit/slim/pkg/util/jsonutil" ) const ( defaultTopObjectMax = 10 ) type ImageFormatType string const ( FormatUnknown ImageFormatType = "unknown" FormatDockerV1 = "docker.v1" FormatDockerOCI = "docker.oci" FormatOCI = "oci" ) // todo: rename 'Package' struct type Package struct { Format ImageFormatType IsOCI bool ManifestOCI *oci.Manifest Manifest *DockerManifestObject Config *ConfigObject Layers []*Layer LayerIDRefs map[string]*Layer HashReferences map[string]map[string]*ObjectMetadata Stats PackageStats OSShells map[string]*system.OSShell SpecialPermRefs SpecialPermsRefsInfo Certs CertsRefInfo CACerts CertsRefInfo IdentityData *sysidentity.DataSet } type CertsRefInfo struct { Bundles map[string]struct{} `json:"bundles,omitempty"` Files map[string]struct{} `json:"files,omitempty"` Links map[string]string `json:"links,omitempty"` Hashes map[string]string `json:"hashes,omitempty"` PrivateKeys map[string]struct{} `json:"private_keys,omitempty"` PrivateKeyLinks map[string]string `json:"private_key_links,omitempty"` } type CertsInfo struct { Bundles []string `json:"bundles,omitempty"` Files []string `json:"files,omitempty"` Links map[string]string `json:"links,omitempty"` Hashes map[string]string `json:"hashes,omitempty"` PrivateKeys []string `json:"private_keys,omitempty"` PrivateKeyLinks map[string]string `json:"private_key_links,omitempty"` } type SpecialPermsRefsInfo struct { Setuid map[string]*ObjectMetadata Setgid map[string]*ObjectMetadata Sticky map[string]*ObjectMetadata } type SpecialPermsInfo struct { Setuid []string `json:"setuid,omitempty"` Setgid []string `json:"setgid,omitempty"` Sticky []string `json:"sticky,omitempty"` } type ImageReport struct { Stats PackageStats `json:"stats"` Duplicates map[string]*DuplicateFilesReport `json:"duplicates,omitempty"` SpecialPerms *SpecialPermsInfo `json:"special_perms,omitempty"` OSShells []*system.OSShell `json:"shells,omitempty"` Certs CertsInfo `json:"certs"` CACerts CertsInfo `json:"ca_certs"` BuildInfo *BuildKitBuildInfo `json:"build_info,omitempty"` Identities *sysidentity.Report `json:"identities,omitempty"` } type DuplicateFilesReport struct { FileCount uint64 `json:"file_count"` FileSize uint64 `json:"file_size"` AllFileSize uint64 `json:"all_file_size"` WastedSize uint64 `json:"wasted_size"` Files map[string]int `json:"files"` } type LayerReport struct { ID string `json:"id"` Index int `json:"index"` Path string `json:"path,omitempty"` LayerDataSource string `json:"layer_data_source,omitempty"` MetadataChangesOnly bool `json:"metadata_changes_only,omitempty"` FSDiffID string `json:"fsdiff_id,omitempty"` Stats LayerStats `json:"stats"` Changes ChangesetSummary `json:"changes"` Top []*ObjectMetadata `json:"top"` Deleted []*ObjectMetadata `json:"deleted,omitempty"` Added []*ObjectMetadata `json:"added,omitempty"` Modified []*ObjectMetadata `json:"modified,omitempty"` ChangeInstruction *InstructionSummary `json:"change_instruction,omitempty"` OtherInstructions []*InstructionSummary `json:"other_instructions,omitempty"` } type InstructionSummary struct { Index int `json:"index"` ImageIndex int `json:"image_index"` Type string `json:"type"` All string `json:"all"` Snippet string `json:"snippet"` } type ChangesetSummary struct { Deleted uint64 `json:"deleted"` Added uint64 `json:"added"` Modified uint64 `json:"modified"` } type Layer struct { ID string Index int Path string LayerDataSource string MetadataChangesOnly bool FSDiffID string Stats LayerStats Changes Changeset Objects []*ObjectMetadata References map[string]*ObjectMetadata Top TopObjects Distro *system.DistroInfo DataMatches map[string][]*ChangeDataMatcher //object.Name -> matched CDM DataHashMatches map[string]*ChangeDataHashMatcher //object.Name -> matched CDHM pathMatches bool } func (ref *Layer) HasMatches() bool { if len(ref.DataMatches) > 0 { return true } if len(ref.DataHashMatches) > 0 { return true } if ref.pathMatches { return true } return false } type LayerStats struct { //BlobSize uint64 `json:"blob_size"` AllSize uint64 `json:"all_size"` ObjectCount uint64 `json:"object_count"` DirCount uint64 `json:"dir_count"` FileCount uint64 `json:"file_count"` LinkCount uint64 `json:"link_count"` MaxFileSize uint64 `json:"max_file_size"` MaxDirSize uint64 `json:"max_dir_size"` DeletedCount uint64 `json:"deleted_count"` DeletedDirContentCount uint64 `json:"deleted_dir_content_count"` DeletedDirCount uint64 `json:"deleted_dir_count"` DeletedFileCount uint64 `json:"deleted_file_count"` DeletedLinkCount uint64 `json:"deleted_link_count"` DeletedSize uint64 `json:"deleted_size"` AddedSize uint64 `json:"added_size"` ModifiedSize uint64 `json:"modified_size"` UTF8Count uint64 `json:"utf8_count,omitempty"` UTF8Size uint64 `json:"utf8_size,omitempty"` UTF8SizeHuman string `json:"utf8_size_human,omitempty"` BinaryCount uint64 `json:"binary_count,omitempty"` BinarySize uint64 `json:"binary_size,omitempty"` BinarySizeHuman string `json:"binary_size_human,omitempty"` SetuidCount uint64 `json:"setuid_count,omitempty"` SetgidCount uint64 `json:"setgid_count,omitempty"` StickyCount uint64 `json:"sticky_count,omitempty"` } type PackageStats struct { DuplicateFileCount uint64 `json:"duplicate_file_count"` DuplicateFileTotalCount uint64 `json:"duplicate_file_total_count"` DuplicateFileSize uint64 `json:"duplicate_file_size"` DuplicateFileTotalSize uint64 `json:"duplicate_file_total_size"` DuplicateFileWastedSize uint64 `json:"duplicate_file_wasted_size"` DeletedCount uint64 `json:"deleted_count"` DeletedDirContentCount uint64 `json:"deleted_dir_content_count"` DeletedDirCount uint64 `json:"deleted_dir_count"` DeletedFileCount uint64 `json:"deleted_file_count"` DeletedLinkCount uint64 `json:"deleted_link_count"` DeletedFileSize uint64 `json:"deleted_file_size"` UTF8Count uint64 `json:"utf8_count,omitempty"` UTF8Size uint64 `json:"utf8_size,omitempty"` UTF8SizeHuman string `json:"utf8_size_human,omitempty"` BinaryCount uint64 `json:"binary_count,omitempty"` BinarySize uint64 `json:"binary_size,omitempty"` BinarySizeHuman string `json:"binary_size_human,omitempty"` SetuidCount uint64 `json:"setuid_count,omitempty"` SetgidCount uint64 `json:"setgid_count,omitempty"` StickyCount uint64 `json:"sticky_count,omitempty"` } type ChangeType int const ( ChangeUnknown ChangeType = iota ChangeDelete ChangeAdd ChangeModify ) var changeTypeToStrings = map[ChangeType]string{ ChangeUnknown: "U", ChangeDelete: "D", ChangeAdd: "A", ChangeModify: "M", } var changeTypeFromStrings = map[string]ChangeType{ "U": ChangeUnknown, "D": ChangeDelete, "A": ChangeAdd, "M": ChangeModify, } const CDMDumpToConsole = "console" type ChangeDataMatcher struct { Dump bool DumpConsole bool DumpDir string PathPattern string DataPattern string Matcher *regexp.Regexp } type ChangeDataHashMatcher struct { Dump bool DumpConsole bool DumpDir string Hash string //lowercase } type ChangePathMatcher struct { Dump bool DumpConsole bool DumpDir string PathPattern string } type UTF8Detector struct { Dump bool DumpConsole bool DumpDir string DumpArchive string MaxSizeBytes int Archive *TarWriter Filters []UTF8DetectorMatcher } type UTF8DetectorMatcher struct { PathPattern string DataPattern string Matcher *regexp.Regexp } type TarWriter struct { file *os.File bufferGzip *gzip.Writer Writer *tar.Writer } func NewTarWriter(name string) (*TarWriter, error) { file, err := os.Create(name) if err != nil { return nil, err } bufferGzip := gzip.NewWriter(file) writer := tar.NewWriter(bufferGzip) tw := &TarWriter{ file: file, bufferGzip: bufferGzip, Writer: writer, } return tw, nil } func (w *TarWriter) Close() error { if err := w.Writer.Close(); err != nil { return err } if err := w.bufferGzip.Close(); err != nil { return err } if err := w.file.Close(); err != nil { return err } return nil } func (d *UTF8Detector) Close() error { if d.Archive != nil { return d.Archive.Close() } return nil } func (ct ChangeType) String() string { if v, ok := changeTypeToStrings[ct]; ok { return v } return "U" } func (ct ChangeType) MarshalJSON() ([]byte, error) { v, ok := changeTypeToStrings[ct] if !ok { v = "O" } d := bytes.NewBufferString(`"`) d.WriteString(v) d.WriteString(`"`) return d.Bytes(), nil } func (ct *ChangeType) UnmarshalJSON(b []byte) error { var d string err := json.Unmarshal(b, &d) if err != nil { return err } if v, ok := changeTypeFromStrings[d]; ok { *ct = v } else { *ct = ChangeUnknown } return nil } const ( ContentTypeUTF8 = "utf8" ContentTypeBinary = "binary" ) type ObjectMetadata struct { Change ChangeType `json:"change"` DirContentDelete bool `json:"dir_content_delete,omitempty"` Name string `json:"name"` Size int64 `json:"size,omitempty"` SizeHuman string `json:"size_human,omitempty"` Mode os.FileMode `json:"mode,omitempty"` ModeHuman string `json:"mode_human,omitempty"` UID int `json:"uid"` //don't omit uid 0 GID int `json:"gid"` //don't omit gid 0 ModTime time.Time `json:"mod_time,omitempty"` ChangeTime time.Time `json:"change_time,omitempty"` LinkTarget string `json:"link_target,omitempty"` History *ObjectHistory `json:"history,omitempty"` Hash string `json:"hash,omitempty"` PathMatch bool `json:"-"` LayerIndex int `json:"-"` TypeFlag byte `json:"-"` Type string `json:"type"` ContentType string `json:"content_type,omitempty"` } type ObjectHistory struct { Add *ChangeInfo `json:"A,omitempty"` Modifies []*ChangeInfo `json:"M,omitempty"` Delete *ChangeInfo `json:"D,omitempty"` } type ChangeInfo struct { Layer int `json:"layer"` Object *ObjectMetadata `json:"-"` } type Changeset struct { Deleted []int DeletedDirContent []int Added []int Modified []int } func newPackage() *Package { pkg := Package{ Format: FormatUnknown, LayerIDRefs: map[string]*Layer{}, HashReferences: map[string]map[string]*ObjectMetadata{}, OSShells: map[string]*system.OSShell{}, SpecialPermRefs: SpecialPermsRefsInfo{ Setuid: map[string]*ObjectMetadata{}, Setgid: map[string]*ObjectMetadata{}, Sticky: map[string]*ObjectMetadata{}, }, Certs: CertsRefInfo{ Bundles: map[string]struct{}{}, Files: map[string]struct{}{}, Links: map[string]string{}, Hashes: map[string]string{}, PrivateKeys: map[string]struct{}{}, PrivateKeyLinks: map[string]string{}, }, CACerts: CertsRefInfo{ Bundles: map[string]struct{}{}, Files: map[string]struct{}{}, Links: map[string]string{}, Hashes: map[string]string{}, PrivateKeys: map[string]struct{}{}, PrivateKeyLinks: map[string]string{}, }, IdentityData: sysidentity.NewDataSet(), } return &pkg } func newLayer(id string, topChangesMax int) *Layer { topChangesCount := defaultTopObjectMax if topChangesMax > -1 { topChangesCount = topChangesMax } layer := Layer{ ID: id, Index: -1, References: map[string]*ObjectMetadata{}, Top: NewTopObjects(topChangesCount), DataMatches: map[string][]*ChangeDataMatcher{}, DataHashMatches: map[string]*ChangeDataHashMatcher{}, } heap.Init(&(layer.Top)) return &layer } //todo: refactor, so we don't have duplicated structures type DetectOpParam struct { /// Operation is enabled Enabled bool /// Dump/save raw data DumpRaw bool /// Dump raw data to console IsConsoleOut bool /// Dump raw data to directory (otherwise save to an archive file) IsDirOut bool /// Output path (directory or archive path) OutputPath string /// Input parameters for the operation InputParams map[string]string } // todo: add other processor params (passed separately for now) type ProcessorParams struct { DetectIdentities *DetectOpParam DetectScheduledTasks *DetectOpParam DetectServices *DetectOpParam DetectSystemHooks *DetectOpParam DetectAllCertFiles bool DetectAllCertPKFiles bool } type LayerLocation struct { Position int Path string LayerID string } type LayerLocationSource string const ( DockerManifestLayersLocation = "ll.dm.layers" OCIImageManifestLocation = "ll.oci.imagemanifest" ) func LoadPackage(archivePath string, imageID string, skipObjects bool, topChangesMax int, doHashData bool, doDetectDuplicates bool, changeDataHashMatchers map[string]*ChangeDataHashMatcher, changePathMatchers []*ChangePathMatcher, changeDataMatchers map[string]*ChangeDataMatcher, utf8Detector *UTF8Detector, processorParams *ProcessorParams, ) (*Package, error) { imageID = dockerutil.CleanImageID(imageID) cpmDumps := hasChangePathMatcherDumps(changePathMatchers) dv1ConfigObjectFileName := fmt.Sprintf("%s.json", imageID) afile, err := os.Open(archivePath) if err != nil { log.Errorf("dockerimage.LoadPackage: os.Open error - %v", err) return nil, err } defer afile.Close() pkg := newPackage() layers := map[string]*Layer{} archiveFiles := map[string]struct{}{} var tarFileCount uint var foundOCILayout bool var foundBlobsDir bool var foundIndex bool var foundDockerManifest bool var foundDockerV1Config bool var foundDockerV1Layer bool var ociImageManifestDesc *oci.Descriptor tr := tar.NewReader(afile) for { hdr, err := tr.Next() if err != nil { if errors.Is(err, io.EOF) { break } log.Errorf("dockerimage.LoadPackage: error reading archive(%v) enumerating files - %v", archivePath, err) return nil, err } tarFileCount++ if hdr == nil || hdr.Name == "" { log.Debugf("dockerimage.LoadPackage: ignoring bad tar header (%d)", tarFileCount) continue } hdr.Name = filepath.Clean(hdr.Name) archiveFiles[hdr.Name] = struct{}{} if hdr.Name == ociBlobDirName || strings.HasPrefix(hdr.Name, ociBlobDirPrefix) { foundBlobsDir = true } switch { case hdr.Name == ociLayoutFileName: var ociLayout OCILayout if err := jsonFromStream(archivePath, hdr.Name, tr, &ociLayout); err != nil { log.Debugf("dockerimage.LoadPackage: error reading oci-layout from archive(%v/%v) - %v", archivePath, ociLayoutFileName, err) //not erroring right away (ok if we don't have a good oci layout if the Docker Manifest info is still there) } else { if ociLayout.Version != ociLayoutVersion { log.Debugf("dockerimage.LoadPackage: unexpected version in oci-layout from archive(%v/%v) - %#v", archivePath, ociLayoutFileName, ociLayout) } foundOCILayout = true } case hdr.Name == ociIndexFileName: var ociIndex oci.Index if err := jsonFromStream(archivePath, hdr.Name, tr, &ociIndex); err != nil { log.Debugf("dockerimage.LoadPackage: error reading oci index from archive(%v/%v) - %v", archivePath, ociIndexFileName, err) //not erroring right away (ok if we don't have a good oci index if the Docker Manifest info is still there) } else { if ociIndex.MediaType == oci.MediaTypeImageIndex { // Docker bug (ociIndex.Manifests is null when image is saved by ID, not by name) if len(ociIndex.Manifests) != 0 { // picking the first usable manifest descriptor (for now) // make it selectable later for _, md := range ociIndex.Manifests { md := md if md.MediaType == oci.MediaTypeImageManifest && md.Platform != nil && md.Platform.OS == DefaultOS && md.Platform.Architecture == DefaultRuntimeArch() { ociImageManifestDesc = &md break } } if ociImageManifestDesc == nil { for _, md := range ociIndex.Manifests { md := md if md.MediaType == oci.MediaTypeImageManifest && md.Platform != nil && md.Platform.OS == DefaultOS { ociImageManifestDesc = &md break } } } if ociImageManifestDesc == nil { for _, md := range ociIndex.Manifests { md := md if md.MediaType == oci.MediaTypeImageManifest { ociImageManifestDesc = &md break } } if ociImageManifestDesc == nil { log.Debugf("dockerimage.LoadPackage: oci index from archive(%s/%s) has no image manifest references - '%s'", archivePath, ociIndexFileName, jsonutil.ToString(ociIndex)) } } if ociImageManifestDesc != nil { log.Tracef("dockerimage.LoadPackage: archive(%v) - OCI Index - found image manifest = '%#v'", archivePath, ociImageManifestDesc) } //index file counts only if it's somewhat well structured foundIndex = true } else { log.Debugf("dockerimage.LoadPackage: malformed index file - no image manifests") //still ok if the Docker Manifest file has the data we need } } } log.Tracef("dockerimage.LoadPackage: oci index from archive(%v/%v) [found oci image manifest desc - '%v'] = %s", archivePath, ociIndexFileName, ociImageManifestDesc != nil, jsonutil.ToString(ociIndex)) case hdr.Name == dockerManifestFileName: var manifests []DockerManifestObject if err := jsonFromStream(archivePath, hdr.Name, tr, &manifests); err != nil { log.Debugf("dockerimage.LoadPackage: error reading manifest file from archive(%v/%v) - %v", archivePath, dockerManifestFileName, err) //not erroring right away (ok if we don't have a good Docker Manifest if the OCI Index and OCI Image Manifest info is still there) } else { if len(manifests) != 0 { pkg.Manifest = &manifests[0] /* TODO: REFACTOR use either v1 config name or the config name from the OCI manifest for _, m := range manifests { if m.Config == dv1ConfigObjectFileName { manifest := m pkg.Manifest = &manifest break } } */ foundDockerManifest = true } else { log.Debugf("dockerimage.LoadPackage: malformed manifest file - no manifests") //still ok if we have the OCI image manifest (referenced in the OCI index) } } log.Tracef("dockerimage.LoadPackage: Docker manifest from archive(%v/%v) = %s", archivePath, dockerManifestFileName, jsonutil.ToString(manifests)) case hdr.Name == dv1ConfigObjectFileName: var imageConfig ConfigObject if err := jsonFromStream(archivePath, hdr.Name, tr, &imageConfig); err != nil { log.Errorf("dockerimage.LoadPackage: error reading config object from archive(%v/%v) - %v", archivePath, dv1ConfigObjectFileName, err) return nil, err } if imageConfig.BuildInfoRaw != "" { imageConfig.BuildInfoDecoded, err = buildInfoDecode(imageConfig.BuildInfoRaw) } pkg.Config = &imageConfig foundDockerV1Config = true case strings.HasSuffix(hdr.Name, dockerV1LayerSuffix): foundDockerV1Layer = true } } log.Tracef("dockerimage.LoadPackage: archive(%v) - tfc=%d, archiveFiles=%d [dm=%v/dv1config=%v/dv1layer=%v/ocilayout=%v/ociindex=%v/ociblobs=%v]", archivePath, tarFileCount, len(archiveFiles), foundDockerManifest, foundDockerV1Config, foundDockerV1Layer, foundOCILayout, foundIndex, foundBlobsDir) if foundOCILayout && foundBlobsDir && foundIndex { if foundDockerManifest { pkg.Format = FormatDockerOCI pkg.IsOCI = true } else { pkg.Format = FormatOCI pkg.IsOCI = true } } else if foundDockerManifest && foundDockerV1Config { pkg.Format = FormatDockerV1 } log.Debugf("dockerimage.LoadPackage: pkg.Format=%s pkg.IsOCI=%v archive - (%s)", pkg.Format, pkg.IsOCI, archivePath) var configObjectFileName string //list of layer IDs based on their order in Docker Manifest or OCI Image Manifest var layerSequence []LayerLocation var ociLayerSequence []LayerLocation var layerLocationSource LayerLocationSource layerFileNames := map[string]struct{}{} ociLayerFileNames := map[string]struct{}{} nonLayerFileNames := map[string]string{} if pkg.Manifest != nil { if pkg.Manifest.Config != "" && pkg.Manifest.Config != dv1ConfigObjectFileName { configObjectFileName = pkg.Manifest.Config log.Tracef("dockerimage.LoadPackage: archive(%v) - DM - config object path = '%s'", archivePath, configObjectFileName) } if len(pkg.Manifest.Layers) > 0 { layerLocationSource = DockerManifestLayersLocation for idx, layerPath := range pkg.Manifest.Layers { parts := strings.Split(layerPath, "/") var layerID string if parts[0] == ociBlobDirName { layerID = parts[2] } else { layerID = parts[0] } layerSequence = append(layerSequence, LayerLocation{ Position: idx, Path: layerPath, LayerID: layerID, }) layerFileNames[layerPath] = struct{}{} } log.Tracef("dockerimage.LoadPackage: archive(%v) - DM - layer paths [%d] = '%#v'", archivePath, len(layerFileNames), layerFileNames) } else { // todo: figure out the layer file names from pkg.Manifest.LayerSources } } // note: for a 'Docker OCI' image loading the OCI image manifest is not a "must have" var ociImageManifestPath string if ociImageManifestDesc != nil && ociImageManifestDesc.Digest.String() != "" { parts := strings.Split(ociImageManifestDesc.Digest.String(), ":") if len(parts) == 2 { ociImageManifestPath = filepath.Join(ociBlobDirName, parts[0], parts[1]) log.Tracef("dockerimage.LoadPackage: archive(%v) - found OCI image manifest path = '%s'", archivePath, ociImageManifestPath) if _, found := archiveFiles[ociImageManifestPath]; !found { //show an error, but don't exit log.Errorf("dockerimage.LoadPackage: malformed oci image manifest path from archive(%s) - %s", archivePath, ociImageManifestPath) ociImageManifestPath = "" } } else { log.Errorf("dockerimage.LoadPackage: malformed oci image manifest digest from archive(%s) - %s", archivePath, ociImageManifestDesc.Digest.String()) } afile.Seek(0, 0) tr = tar.NewReader(afile) for { hdr, err := tr.Next() if err != nil { if errors.Is(err, io.EOF) { break } log.Errorf("dockerimage.LoadPackage: error reading archive(%v) enumerating files - %v", archivePath, err) return nil, err } if hdr == nil || hdr.Name == "" { log.Debugf("dockerimage.LoadPackage: ignoring bad tar header (%d)", tarFileCount) continue } hdr.Name = filepath.Clean(hdr.Name) if configObjectFileName != "" && hdr.Name == configObjectFileName { var imageConfig ConfigObject if err := jsonFromStream(archivePath, hdr.Name, tr, &imageConfig); err != nil { log.Errorf("dockerimage.LoadPackage: error reading config object from archive(%v/%v) - %v", archivePath, configObjectFileName, err) return nil, err } if imageConfig.BuildInfoRaw != "" { imageConfig.BuildInfoDecoded, err = buildInfoDecode(imageConfig.BuildInfoRaw) } log.Tracef("dockerimage.LoadPackage: archive(%v) - loaded config object from path = '%s'", archivePath, configObjectFileName) //possible to have a v1 pkg.Config already //good to check if they are different pkg.Config = &imageConfig configObjectFileName = "" continue } if pkg.ManifestOCI == nil && ociImageManifestPath != "" && hdr.Name == ociImageManifestPath { var ociImageManifest oci.Manifest if err := jsonFromStream(archivePath, hdr.Name, tr, &ociImageManifest); err != nil { log.Errorf("dockerimage.LoadPackage: error reading oci image manifest from archive(%v/%v) - %v", archivePath, ociImageManifestPath, err) return nil, err } log.Tracef("dockerimage.LoadPackage: archive(%v) - loaded OCI Image Manifest from path = '%s'", archivePath, ociImageManifestPath) pkg.ManifestOCI = &ociImageManifest ociImageManifestPath = "" } if configObjectFileName == "" && ociImageManifestPath == "" { break } } } if pkg.ManifestOCI != nil && pkg.ManifestOCI.Config.Digest.String() != "" { // when we don't have a Docker manifest parts := strings.Split(pkg.ManifestOCI.Config.Digest.String(), ":") if len(parts) == 2 { ociConfigObjectFileName := filepath.Join(ociBlobDirName, parts[0], parts[1]) log.Tracef("dockerimage.LoadPackage: archive(%v) - config object path from OCI Image Manifest = '%s'", archivePath, ociConfigObjectFileName) if _, found := archiveFiles[ociConfigObjectFileName]; !found { //show the error, but don't exit log.Errorf("dockerimage.LoadPackage: malformed oci config object path from archive(%s) - %s", archivePath, ociConfigObjectFileName) //make it empty,so we don't attempt to extract the config again configObjectFileName = "" } else { if ociConfigObjectFileName != configObjectFileName { log.Debugf("dockerimage.LoadPackage: config object path mismatch (OCI='%s' != DM='%s') from archive(%s)", ociConfigObjectFileName, configObjectFileName, archivePath) //make sure we fetch and save the OCI config configObjectFileName = ociConfigObjectFileName } else { configObjectFileName = "" } } } else { log.Errorf("dockerimage.LoadPackage: malformed oci config object digest from archive(%s) - %s", archivePath, pkg.ManifestOCI.Config.Digest.String()) } } if pkg.ManifestOCI != nil { // get layers from oci image manifest for idx, layerInfo := range pkg.ManifestOCI.Layers { // todo: add support for oci.MediaTypeImageLayerGzip and oci.MediaTypeImageLayerZstd if layerInfo.MediaType == oci.MediaTypeImageLayer && layerInfo.Digest.String() != "" { parts := strings.Split(layerInfo.Digest.String(), ":") if len(parts) == 2 { layerFileName := filepath.Join(ociBlobDirName, parts[0], parts[1]) if _, found := archiveFiles[layerFileName]; !found { //show error, but don't exit (exit if we have --strict-image-format) log.Errorf("dockerimage.LoadPackage: malformed oci layer path from archive(%s) - %s", archivePath, layerFileName) } else { ociLayerSequence = append(ociLayerSequence, LayerLocation{ Position: idx, Path: layerFileName, LayerID: parts[1], }) ociLayerFileNames[layerFileName] = struct{}{} //cross reference the OCI layers with the Docker Manifest layers (if we have them) if len(layerFileNames) > 0 { if _, layerFound := layerFileNames[layerFileName]; !layerFound { log.Debugf("dockerimage.LoadPackage: oci layer path (%s) should be known already - archive(%s)", layerFileName, archivePath) } } if len(layerSequence) >= len(ociLayerSequence) { lsIdx := len(ociLayerSequence) - 1 if layerSequence[lsIdx].Path != ociLayerSequence[lsIdx].Path || layerSequence[lsIdx].Position != ociLayerSequence[lsIdx].Position || layerSequence[lsIdx].LayerID != ociLayerSequence[lsIdx].LayerID { log.Debugf("dockerimage.LoadPackage: layerFileName=%s / OCI layer and DM layer sequence record mismatch lsIdx=%d ('%+v' / '%+v') - archive(%s)", layerFileName, lsIdx, layerSequence[lsIdx], ociLayerSequence[lsIdx], archivePath) } } else { log.Debugf("dockerimage.LoadPackage: layerFileName=%s / OCI layer and DM layer sequence mismatch (%d / %d) - archive(%s)", layerFileName, len(layerSequence), len(ociLayerSequence), archivePath) } } } else { log.Errorf("dockerimage.LoadPackage: malformed oci layer digest from archive(%s) - %s", archivePath, layerInfo.Digest.String()) } } else if layerInfo.MediaType != oci.MediaTypeImageLayer { if layerInfo.Digest.String() != "" { parts := strings.Split(layerInfo.Digest.String(), ":") if len(parts) == 2 { layerFileName := filepath.Join(ociBlobDirName, parts[0], parts[1]) nonLayerFileNames[layerFileName] = layerInfo.MediaType } else { log.Errorf("dockerimage.LoadPackage: malformed oci layer digest from archive(%s) - %s", archivePath, layerInfo.Digest.String()) } } else { log.Debugf("dockerimage.LoadPackage: non-image layer, no digest '%s' - archive(%s)", jsonutil.ToString(layerInfo), archivePath) } } } } // a 'simple' way to decide when to use the OCI layer metadata (todo: review later) if len(ociLayerFileNames) > len(layerFileNames) { layerFileNames = ociLayerFileNames layerSequence = ociLayerSequence layerLocationSource = OCIImageManifestLocation } // now load the layers afile.Seek(0, 0) tr = tar.NewReader(afile) for { hdr, err := tr.Next() if err == io.EOF { break } if err != nil { log.Errorf("dockerimage.LoadPackage: error reading archive(%v) - %v", archivePath, err) return nil, err } if hdr == nil || hdr.Name == "" { log.Debugf("dockerimage.LoadPackage: ignoring bad tar header") continue } hdr.Name = filepath.Clean(hdr.Name) if configObjectFileName != "" && hdr.Name == configObjectFileName { var imageConfig ConfigObject if err := jsonFromStream(archivePath, hdr.Name, tr, &imageConfig); err != nil { log.Errorf("dockerimage.LoadPackage: error reading oci config object from archive(%v/%v) - %v", archivePath, configObjectFileName, err) return nil, err } if imageConfig.BuildInfoRaw != "" { imageConfig.BuildInfoDecoded, err = buildInfoDecode(imageConfig.BuildInfoRaw) } log.Tracef("dockerimage.LoadPackage: archive(%v) - loaded config object from path = '%s'", archivePath, configObjectFileName) //pkg.Config might alread point to a config (v1 or DM) //todo: check the difference pkg.Config = &imageConfig configObjectFileName = "" continue } switch hdr.Typeflag { case tar.TypeReg, tar.TypeSymlink: switch { case strings.HasSuffix(hdr.Name, dockerV1LayerSuffix): parts := strings.Split(hdr.Name, "/") layerID := parts[0] var layer *Layer if hdr.Typeflag == tar.TypeSymlink { layer = newLayer(layerID, topChangesMax) layer.Path = hdr.Name layer.MetadataChangesOnly = true parts := strings.Split(hdr.Linkname, "/") if len(parts) == 3 && parts[2] == "layer.tar" { layer.LayerDataSource = parts[1] if srcLayer, ok := layers[layer.LayerDataSource]; ok { for _, srcObj := range srcLayer.Objects { if srcObj.Change != ChangeDelete { newObj := *srcObj newObj.Change = ChangeUnknown layer.Objects = append(layer.Objects, &newObj) layer.References[srcObj.Name] = &newObj layer.Stats.ObjectCount++ } } layer.Stats.LinkCount = srcLayer.Stats.LinkCount - srcLayer.Stats.DeletedLinkCount layer.Stats.FileCount = srcLayer.Stats.FileCount - srcLayer.Stats.DeletedFileCount layer.Stats.DirCount = srcLayer.Stats.DirCount - srcLayer.Stats.DeletedDirCount } else { log.Debugf("dockerimage.LoadPackage: could not find source layer - %v", layer.LayerDataSource) } } } else { layer, err = layerFromStream( pkg, hdr.Name, tar.NewReader(tr), layerID, topChangesMax, doHashData, doDetectDuplicates, changeDataHashMatchers, changePathMatchers, cpmDumps, changeDataMatchers, utf8Detector, processorParams, ) if err != nil { log.Errorf("dockerimage.LoadPackage: error reading layer from archive(%v/%v) - %v", archivePath, hdr.Name, err) return nil, err } } layers[layerID] = layer log.Debugf("dockerimage.LoadPackage: saved v1 layer '%s' from archive - %s", layerID, archivePath) default: if _, found := layerFileNames[hdr.Name]; found { // todo: test if we can get symlinks... parts := strings.SplitN(hdr.Name, "/", 3) layerID := parts[2] layer, err := layerFromStream( pkg, hdr.Name, tar.NewReader(tr), layerID, topChangesMax, doHashData, doDetectDuplicates, changeDataHashMatchers, changePathMatchers, cpmDumps, changeDataMatchers, utf8Detector, processorParams, ) if err != nil { log.Errorf("dockerimage.LoadPackage: error reading oci layer from archive(%s/%s) - %v", archivePath, hdr.Name, err) return nil, err } layers[layerID] = layer log.Debugf("dockerimage.LoadPackage: saved layer '%s' from archive - %s", layerID, archivePath) } } } } if pkg.Format == FormatOCI { if pkg.ManifestOCI == nil { return nil, fmt.Errorf("dockerimage.LoadPackage: missing OCI Image Manifest object for image ID=%s / archive='%s' / files=%+v", imageID, archivePath, archiveFiles) } } else { if pkg.Manifest == nil { return nil, fmt.Errorf("dockerimage.LoadPackage: missing manifest object for image ID=%s / archive='%s' / files=%+v", imageID, archivePath, archiveFiles) } } if pkg.Config == nil { return nil, fmt.Errorf("dockerimage.LoadPackage: missing image config object for image ID=%s / archive='%s' / files=%+v", imageID, archivePath, archiveFiles) } if len(layers) == 0 { //todo: display non-image layer metadata if there are any //todo: save non-image layer metadata/data log.Debugf("dockerimage.LoadPackage: no image layers in archive - %s (%+v)", archivePath, layerFileNames) } if len(nonLayerFileNames) > 0 { log.Debugf("dockerimage.LoadPackage: non-image layers in archive - %s (%s)", archivePath, jsonutil.ToString(nonLayerFileNames)) } log.Debugf("dockerimage.LoadPackage: layerLocationSource=%v layerSequence='%#v' - archive='%s'", layerLocationSource, layerSequence, archivePath) for idx, layerLocationInfo := range layerSequence { layer, ok := layers[layerLocationInfo.LayerID] if !ok { log.Errorf("dockerimage.LoadPackage: error missing layer (idx=%d layerPath=%s layerID=%s) archive=%s", idx, layerLocationInfo.Path, layerLocationInfo.LayerID, archivePath) return nil, fmt.Errorf("dockerimage.LoadPackage: missing layer (%v) for image ID - %v", layerLocationInfo.Path, imageID) } layer.Index = idx //adding layers based on their manifest order pkg.Layers = append(pkg.Layers, layer) if len(pkg.Layers)-1 != layer.Index { return nil, fmt.Errorf("dockerimage.LoadPackage: layer index mismatch - %v / %v", len(pkg.Layers)-1, layer.Index) } if layerLocationInfo.Path != layer.Path { return nil, fmt.Errorf("dockerimage.LoadPackage: layer path mismatch - %v / %v", layerLocationInfo.Path, layer.Path) } if idx == 0 { for oidx, object := range layer.Objects { object.LayerIndex = idx if utf8Detector != nil { switch object.ContentType { case ContentTypeUTF8: layer.Stats.UTF8Count++ layer.Stats.UTF8Size += uint64(object.Size) pkg.Stats.UTF8Count++ pkg.Stats.UTF8Size += uint64(object.Size) case ContentTypeBinary: layer.Stats.BinaryCount++ layer.Stats.BinarySize += uint64(object.Size) pkg.Stats.BinaryCount++ pkg.Stats.BinarySize += uint64(object.Size) } } if object.Change == ChangeUnknown { object.Change = ChangeAdd layer.Changes.Added = append(layer.Changes.Added, oidx) layer.Stats.AddedSize += uint64(object.Size) } if object.History == nil { object.History = &ObjectHistory{} } changeInfo := ChangeInfo{ Layer: layer.Index, Object: object, } switch object.Change { case ChangeAdd: object.History.Add = &changeInfo case ChangeDelete: object.History.Delete = &changeInfo } if object.Change == ChangeAdd || object.Change == ChangeModify { if shellInfo, found := pkg.OSShells[object.Name]; found { if exeInfo, rfound := layer.References[shellInfo.ExePath]; rfound { shellInfo.Verified = true if exeInfo.LinkTarget != "" { shellInfo.LinkPath = exeInfo.LinkTarget } } } } } if utf8Detector != nil { layer.Stats.UTF8SizeHuman = humanize.Bytes(layer.Stats.UTF8Size) layer.Stats.BinarySizeHuman = humanize.Bytes(layer.Stats.BinarySize) } } else { for oidx, object := range layer.Objects { object.LayerIndex = idx if utf8Detector != nil { switch object.ContentType { case ContentTypeUTF8: layer.Stats.UTF8Count++ layer.Stats.UTF8Size += uint64(object.Size) pkg.Stats.UTF8Count++ pkg.Stats.UTF8Size += uint64(object.Size) case ContentTypeBinary: layer.Stats.BinaryCount++ layer.Stats.BinarySize += uint64(object.Size) pkg.Stats.BinaryCount++ pkg.Stats.BinarySize += uint64(object.Size) } } if object.Change == ChangeUnknown { for prevIdx := 0; prevIdx < idx; prevIdx++ { prevLayer := pkg.Layers[prevIdx] if om, ok := prevLayer.References[object.Name]; ok { object.Change = ChangeModify layer.Changes.Modified = append(layer.Changes.Modified, oidx) layer.Stats.ModifiedSize += uint64(object.Size) if om.History != nil { object.History = om.History } else { object.History = &ObjectHistory{} om.History = object.History } changeInfo := ChangeInfo{ Layer: layer.Index, Object: object, } object.History.Modifies = append(object.History.Modifies, &changeInfo) break } } if object.Change == ChangeUnknown { if object.History == nil { object.History = &ObjectHistory{} } object.History.Add = &ChangeInfo{ Layer: layer.Index, Object: object, } object.Change = ChangeAdd layer.Changes.Added = append(layer.Changes.Added, oidx) layer.Stats.AddedSize += uint64(object.Size) } } if object.Change == ChangeDelete { for prevIdx := 0; prevIdx < idx; prevIdx++ { prevLayer := pkg.Layers[prevIdx] if om, ok := prevLayer.References[object.Name]; ok { if om.History != nil { object.History = om.History } else { object.History = &ObjectHistory{} om.History = object.History } object.History.Delete = &ChangeInfo{ Layer: layer.Index, Object: object, } switch object.TypeFlag { case tar.TypeReg: //NOTE: counting the file size of the first instance layer.Stats.DeletedSize += uint64(om.Size) pkg.Stats.DeletedFileSize += uint64(om.Size) case tar.TypeDir: //TODO: need to count all file sizes in the dir } break } } } if object.Change == ChangeAdd || object.Change == ChangeModify { if shellInfo, found := pkg.OSShells[object.Name]; found { if exeInfo, rfound := layer.References[shellInfo.ExePath]; rfound { shellInfo.Verified = true if exeInfo.LinkTarget != "" { shellInfo.LinkPath = exeInfo.LinkTarget } } } } } if utf8Detector != nil { layer.Stats.UTF8SizeHuman = humanize.Bytes(layer.Stats.UTF8Size) layer.Stats.BinarySizeHuman = humanize.Bytes(layer.Stats.BinarySize) } } pkg.LayerIDRefs[layerLocationInfo.LayerID] = layer if pkg.Config.RootFS != nil && idx < len(pkg.Config.RootFS.DiffIDs) { diffID := pkg.Config.RootFS.DiffIDs[idx] layer.FSDiffID = diffID } else { log.Debugf("dockerimage.LoadPackage: no FS diff for layer index %v", idx) } } if utf8Detector != nil { pkg.Stats.UTF8SizeHuman = humanize.Bytes(pkg.Stats.UTF8Size) pkg.Stats.BinarySizeHuman = humanize.Bytes(pkg.Stats.BinarySize) } if len(pkg.Layers) > 0 { currentLayerIndex := 0 isFirstLayer := true for hidx := range pkg.Config.History { if !pkg.Config.History[hidx].EmptyLayer { if currentLayerIndex < (len(pkg.Layers)-1) && !isFirstLayer { currentLayerIndex++ } } currentLayer := pkg.Layers[currentLayerIndex] if currentLayer != nil { pkg.Config.History[hidx].LayerFSDiffID = currentLayer.FSDiffID pkg.Config.History[hidx].LayerID = currentLayer.ID pkg.Config.History[hidx].LayerIndex = currentLayer.Index } if !pkg.Config.History[hidx].EmptyLayer && isFirstLayer { isFirstLayer = false } } } if doDetectDuplicates { for hash, hr := range pkg.HashReferences { dupCount := len(hr) if dupCount > 1 { pkg.Stats.DuplicateFileCount++ pkg.Stats.DuplicateFileTotalCount += uint64(dupCount) for _, om := range hr { pkg.Stats.DuplicateFileSize += uint64(om.Size) pkg.Stats.DuplicateFileTotalSize += uint64(om.Size * int64(dupCount)) break } } else { delete(pkg.HashReferences, hash) } } if pkg.Stats.DuplicateFileCount > 0 { pkg.Stats.DuplicateFileWastedSize = uint64(pkg.Stats.DuplicateFileTotalSize - pkg.Stats.DuplicateFileSize) } } return pkg, nil } func (ref *Package) ProcessIdentityData() *sysidentity.Report { if ref.IdentityData == nil { return nil } report, err := sysidentity.NewReportFromData(ref.IdentityData) if err != nil { log.Errorf("dockerimage.Package.ProcessIdentityData: error - %v", err) return nil } return report } func hasChangePathMatcherDumps(changePathMatchers []*ChangePathMatcher) bool { for _, cpm := range changePathMatchers { if cpm.PathPattern != "" && cpm.Dump { return true } } return false } func linkTargetToFullPath(fullPath, target string) string { if filepath.IsAbs(target) { return target } if target == "." { return "" } d := filepath.Dir(fullPath) return filepath.Clean(filepath.Join(d, target)) } func layerFromStream( pkg *Package, layerPath string, tr *tar.Reader, layerID string, topChangesMax int, doHashData bool, doDetectDuplicates bool, changeDataHashMatchers map[string]*ChangeDataHashMatcher, changePathMatchers []*ChangePathMatcher, cpmDumps bool, changeDataMatchers map[string]*ChangeDataMatcher, utf8Detector *UTF8Detector, processorParams *ProcessorParams, ) (*Layer, error) { layer := newLayer(layerID, topChangesMax) layer.Path = layerPath topChangesCount := defaultTopObjectMax if topChangesMax > -1 { topChangesCount = topChangesMax } for { hdr, err := tr.Next() if err == io.EOF { break } if err != nil { log.Errorf("layerFromStream: error reading layer(%v) - %v", layerID, err) return nil, err } if hdr == nil || hdr.Name == "" { log.Debug("layerFromStream: ignoring bad tar header") continue } hdr.Name = filepath.Clean(hdr.Name) object := &ObjectMetadata{ Name: hdr.Name, Size: hdr.Size, SizeHuman: humanize.Bytes(uint64(hdr.Size)), Mode: hdr.FileInfo().Mode(), UID: hdr.Uid, GID: hdr.Gid, ModTime: hdr.ModTime, ChangeTime: hdr.ChangeTime, TypeFlag: hdr.Typeflag, Type: ObjectTypeFromTarType(hdr.Typeflag), } normalized, isDeleted, isDeletedDirContent, err := NormalizeFileObjectLayerPath(object.Name) if err == nil && len(object.Name) > 0 && object.Name[0] != '/' { object.Name = fmt.Sprintf("/%s", normalized) } object.Name = strings.ReplaceAll(object.Name, "//", "/") object.LinkTarget = strings.ReplaceAll(object.LinkTarget, "//", "/") if isDeletedDirContent { object.DirContentDelete = true } layer.Objects = append(layer.Objects, object) layer.References[object.Name] = object heap.Push(&(layer.Top), object) if layer.Top.Len() > topChangesCount { _ = heap.Pop(&(layer.Top)) } layer.Stats.AllSize += uint64(object.Size) layer.Stats.ObjectCount++ if isDeletedDirContent { object.Change = ChangeDelete idx := len(layer.Objects) - 1 layer.Changes.Deleted = append(layer.Changes.Deleted, idx) layer.Changes.DeletedDirContent = append(layer.Changes.DeletedDirContent, idx) layer.Stats.DeletedDirContentCount++ pkg.Stats.DeletedDirContentCount++ } else { if isDeleted { object.Change = ChangeDelete idx := len(layer.Objects) - 1 layer.Changes.Deleted = append(layer.Changes.Deleted, idx) layer.Stats.DeletedCount++ pkg.Stats.DeletedCount++ //layer.Stats.DeletedSize += uint64(object.Size) //NOTE: //This is not the real deleted size. //Need to find the actual object in a previous layer to know the actual size. } } switch hdr.Typeflag { case tar.TypeSymlink, tar.TypeLink: object.LinkTarget = hdr.Linkname layer.Stats.LinkCount++ if isDeleted { layer.Stats.DeletedLinkCount++ pkg.Stats.DeletedLinkCount++ } nameOnly := filepath.Base(object.Name) if certdiscover.IsCertHashName(nameOnly) { if certdiscover.IsCertPKDirPath(object.Name) { pkg.CACerts.Hashes[object.Name] = object.LinkTarget } else if certdiscover.IsCertDirPath(object.Name) { pkg.Certs.Hashes[object.Name] = object.LinkTarget } } else { linkFullPath := linkTargetToFullPath(object.Name, object.LinkTarget) if certdiscover.IsCertFile(object.Name) { pkg.Certs.Bundles[object.Name] = struct{}{} if linkFullPath != "" { pkg.Certs.Bundles[linkFullPath] = struct{}{} } } else if certdiscover.IsAppCertFile(object.Name) { pkg.Certs.Bundles[object.Name] = struct{}{} if linkFullPath != "" { pkg.Certs.Bundles[linkFullPath] = struct{}{} } } else if certdiscover.IsCACertFile(object.Name) { pkg.CACerts.Bundles[object.Name] = struct{}{} if linkFullPath != "" { pkg.CACerts.Bundles[linkFullPath] = struct{}{} } } else if certdiscover.IsCACertPKFile(object.Name) { pkg.CACerts.PrivateKeys[object.Name] = struct{}{} if linkFullPath != "" { pkg.CACerts.PrivateKeys[linkFullPath] = struct{}{} } } else { //NOTE: Links require post-processing if certdiscover.IsCertDir(object.Name) { pkg.Certs.Links[object.Name] = linkFullPath } else if certdiscover.IsCertPKDir(object.Name) { pkg.Certs.PrivateKeyLinks[object.Name] = linkFullPath } else if certdiscover.IsCACertDir(object.Name) { pkg.CACerts.Links[object.Name] = linkFullPath } else if certdiscover.IsCACertPKDir(object.Name) { pkg.CACerts.PrivateKeyLinks[object.Name] = linkFullPath } else if certdiscover.IsCertDirPath(object.Name) { pkg.Certs.Links[object.Name] = linkFullPath } else if certdiscover.IsCACertDirPath(object.Name) { pkg.CACerts.Links[object.Name] = linkFullPath } else if certdiscover.IsCertPKDirPath(object.Name) { pkg.Certs.PrivateKeyLinks[object.Name] = linkFullPath } else if certdiscover.IsCACertPKDirPath(object.Name) { pkg.CACerts.PrivateKeyLinks[object.Name] = linkFullPath } } } case tar.TypeReg: layer.Stats.FileCount++ if uint64(object.Size) > layer.Stats.MaxFileSize { layer.Stats.MaxFileSize = uint64(object.Size) } if isDeleted { layer.Stats.DeletedFileCount++ pkg.Stats.DeletedFileCount++ } else { //TODO: //do unique pkg stats counts, //so we don't count the same thing multiple times //across different layers if fsutil.FileModeIsSetuid(object.Mode) { layer.Stats.SetuidCount++ pkg.Stats.SetuidCount++ pkg.SpecialPermRefs.Setuid[object.Name] = object } if fsutil.FileModeIsSetgid(object.Mode) { layer.Stats.SetgidCount++ pkg.Stats.SetgidCount++ pkg.SpecialPermRefs.Setgid[object.Name] = object } if fsutil.FileModeIsSticky(object.Mode) { layer.Stats.StickyCount++ pkg.Stats.StickyCount++ pkg.SpecialPermRefs.Sticky[object.Name] = object } err = inspectFile( object, tr, pkg, layer, doHashData, changeDataHashMatchers, changePathMatchers, cpmDumps, changeDataMatchers, utf8Detector, processorParams, ) if err != nil { log.Errorf("layerFromStream: error inspecting layer file (%s) - (%v) - %v", object.Name, layerID, err) } else { if doDetectDuplicates && len(object.Hash) != 0 { hr, found := pkg.HashReferences[object.Hash] if !found { hr = map[string]*ObjectMetadata{} pkg.HashReferences[object.Hash] = hr } hr[object.Name] = object } } } case tar.TypeDir: layer.Stats.DirCount++ if uint64(object.Size) > layer.Stats.MaxDirSize { layer.Stats.MaxDirSize = uint64(object.Size) } if isDeleted { layer.Stats.DeletedDirCount++ pkg.Stats.DeletedDirCount++ } else { //TODO: //do unique pkg stats counts, //so we don't count the same thing multiple times //across different layers if fsutil.FileModeIsSticky(object.Mode) { layer.Stats.StickyCount++ pkg.Stats.StickyCount++ pkg.SpecialPermRefs.Sticky[object.Name] = object } } } } return layer, nil } func getStreamHash(reader io.Reader) (string, error) { hasher := sha1.New() _, err := io.Copy(hasher, reader) if err != nil { log.Errorf("getStreamHash: error=%v", err) return "", err } hash := hasher.Sum(nil) return hex.EncodeToString(hash[:]), nil } func getBytesHash(data []byte) string { hash := sha1.Sum(data) return hex.EncodeToString(hash[:]) } type utf8FileInfo struct { name string size int64 modtime time.Time } func (f *utf8FileInfo) Name() string { return f.name } func (f *utf8FileInfo) Size() int64 { return f.size } func (f *utf8FileInfo) Mode() os.FileMode { return os.ModePerm } func (f *utf8FileInfo) ModTime() time.Time { return f.modtime } func (f *utf8FileInfo) IsDir() bool { return false } func (f *utf8FileInfo) Sys() interface{} { return nil } func inspectFile( object *ObjectMetadata, reader io.Reader, pkg *Package, layer *Layer, doHashData bool, changeDataHashMatchers map[string]*ChangeDataHashMatcher, changePathMatchers []*ChangePathMatcher, cpmDumps bool, changeDataMatchers map[string]*ChangeDataMatcher, utf8Detector *UTF8Detector, processorParams *ProcessorParams, ) error { //TODO: refactor and enhance the OS Distro detection logic fullPath := object.Name var cdhmDumps bool for _, dhm := range changeDataHashMatchers { if dhm.Dump { cdhmDumps = true break } } var isKnownCertFile bool if certdiscover.IsCertFile(fullPath) { pkg.Certs.Bundles[fullPath] = struct{}{} isKnownCertFile = true } else if certdiscover.IsAppCertFile(fullPath) { pkg.Certs.Bundles[fullPath] = struct{}{} isKnownCertFile = true } else if certdiscover.IsCACertFile(fullPath) { pkg.CACerts.Bundles[fullPath] = struct{}{} isKnownCertFile = true } else if certdiscover.IsCACertPKFile(fullPath) { pkg.CACerts.PrivateKeys[fullPath] = struct{}{} isKnownCertFile = true } if (processorParams.DetectIdentities.Enabled && sysidentity.IsSourceFile(fullPath)) || system.IsOSReleaseFile(fullPath) || system.IsOSShellsFile(fullPath) || len(changeDataMatchers) > 0 || cpmDumps || cdhmDumps || utf8Detector != nil || (!isKnownCertFile && processorParams.DetectAllCertFiles) || (!isKnownCertFile && processorParams.DetectAllCertPKFiles) { data, err := io.ReadAll(reader) if err != nil { return err } if processorParams.DetectIdentities.Enabled && sysidentity.IsSourceFile(fullPath) { pkg.IdentityData.AddData(fullPath, data) } if !isKnownCertFile { if processorParams.DetectAllCertFiles { //NOTE: //not limiting detection to the main cert directories, //but checking the CA cert dir prefix to know where to put it if certdiscover.IsCertData(data) { if certdiscover.IsCACertDirPath(fullPath) { pkg.CACerts.Files[fullPath] = struct{}{} } else { pkg.Certs.Files[fullPath] = struct{}{} } } } if processorParams.DetectAllCertPKFiles { //NOTE: not limiting detection to the main cert private key directories if certdiscover.IsPrivateKeyData(data) { if certdiscover.IsCACertPKDirPath(fullPath) { pkg.CACerts.PrivateKeys[fullPath] = struct{}{} } else { pkg.Certs.PrivateKeys[fullPath] = struct{}{} } } } } if system.IsOSShellsFile(fullPath) { shellsList, _ := system.NewOSShellsFromData(data) for _, shellInfo := range shellsList { if shellInfo.Reference != "" { pkg.OSShells[shellInfo.Reference] = shellInfo } else { pkg.OSShells[shellInfo.ExePath] = shellInfo } } } if system.IsOSReleaseFile(fullPath) { osr, err := system.NewOsRelease(data) if err != nil { return err } distro := &system.DistroInfo{ Name: osr.Name, Version: osr.VersionID, DisplayName: osr.PrettyName, } if distro.Version == "" { distro.Version = osr.Version } if distro.DisplayName == "" { nameMain := osr.Name nameVersion := osr.Version if nameVersion == "" { nameVersion = osr.VersionID } distro.DisplayName = fmt.Sprintf("%v %v", nameMain, nameVersion) } layer.Distro = distro } var hash string if doHashData || len(changeDataHashMatchers) > 0 || utf8Detector != nil { hash = getBytesHash(data) } if doHashData { object.Hash = hash } if len(hash) > 0 && utf8Detector != nil { if utf8.Valid(data) { object.ContentType = ContentTypeUTF8 if utf8Detector.Dump { if utf8Detector.Archive != nil { if utf8Detector.MaxSizeBytes != 0 && object.Size > int64(utf8Detector.MaxSizeBytes) { fileInfo := &utf8FileInfo{ name: hash, size: int64(utf8Detector.MaxSizeBytes), modtime: object.ModTime, } header, err := tar.FileInfoHeader(fileInfo, hash) if err != nil { return err } header.Name = hash err = utf8Detector.Archive.Writer.WriteHeader(header) if err != nil { return err } _, err = utf8Detector.Archive.Writer.Write(data[:utf8Detector.MaxSizeBytes]) if err != nil { return err } } else { fileInfo := &utf8FileInfo{ name: hash, size: object.Size, modtime: object.ModTime, } header, err := tar.FileInfoHeader(fileInfo, hash) if err != nil { return err } header.Name = hash err = utf8Detector.Archive.Writer.WriteHeader(header) if err != nil { return err } _, err = utf8Detector.Archive.Writer.Write(data) if err != nil { return err } } } if utf8Detector.DumpConsole { fmt.Printf("cmd=xray info=detect.utf8.match.start\n") fmt.Printf("cmd=xray info=detect.utf8.match file='%s')\n", fullPath) fmt.Printf("%s\n", string(data)) fmt.Printf("cmd=xray info=detect.utf8.match.end\n") } if utf8Detector.DumpDir != "" { dumpPath := filepath.Join(utf8Detector.DumpDir, fullPath) dirPath := fsutil.FileDir(dumpPath) if !fsutil.DirExists(dirPath) { err := os.MkdirAll(dirPath, 0755) if err != nil { fmt.Printf("cmd=xray info=detect.utf8.match.dump.error file='%s' target='%s' error='%s'):\n", fullPath, dumpPath, err) return err } } err := os.WriteFile(dumpPath, data, 0644) if err != nil { fmt.Printf("cmd=xray info=detect.utf8.match.dump.error file='%s' target='%s' error='%s'):\n", fullPath, dumpPath, err) return err } fmt.Printf("cmd=xray info=detect.utf8.match.dump file='%s' target='%s'):\n", fullPath, dumpPath) } } } else { object.ContentType = ContentTypeBinary } } if len(hash) > 0 && len(changeDataHashMatchers) > 0 { if dhm, found := changeDataHashMatchers[hash]; found { //need to save to DataHashMatches to make it work without generating/saving hashes for all objects layer.DataHashMatches[fullPath] = dhm if dhm.DumpConsole { fmt.Printf("cmd=xray info=change.data.hash.match.start\n") fmt.Printf("cmd=xray info=change.data.hash.match file='%s' hash='%s')\n", fullPath, hash) fmt.Printf("%s\n", string(data)) fmt.Printf("cmd=xray info=change.data.hash.match.end\n") } if dhm.DumpDir != "" { dumpPath := filepath.Join(dhm.DumpDir, fullPath) dirPath := fsutil.FileDir(dumpPath) if !fsutil.DirExists(dirPath) { err := os.MkdirAll(dirPath, 0755) if err != nil { fmt.Printf("cmd=xray info=change.data.hash.match.dump.error file='%s' hash='%s' target='%s' error='%s'):\n", fullPath, hash, dumpPath, err) return err } } err := os.WriteFile(dumpPath, data, 0644) if err != nil { fmt.Printf("cmd=xray info=change.data.hash.match.dump.error file='%s' hash='%s' target='%s' error='%s'):\n", fullPath, hash, dumpPath, err) return err } fmt.Printf("cmd=xray info=change.data.hash.match.dump file='%s' hash='%s' target='%s'):\n", fullPath, hash, dumpPath) } } } for _, cpm := range changePathMatchers { if cpm.PathPattern != "" { pmatch, err := doublestar.Match(cpm.PathPattern, fullPath) if err != nil { log.Errorf("doublestar.Match name='%s' error=%v", fullPath, err) continue } if !pmatch { continue } object.PathMatch = true layer.pathMatches = true if cpm.Dump && cpm.DumpConsole { fmt.Printf("cmd=xray info=change.path.match.start\n") fmt.Printf("cmd=xray info=change.path.match file='%s' ppattern='%s')\n", fullPath, cpm.PathPattern) fmt.Printf("%s\n", string(data)) fmt.Printf("cmd=xray info=change.path.match.end\n") } if cpm.Dump && cpm.DumpDir != "" { dumpPath := filepath.Join(cpm.DumpDir, fullPath) dirPath := fsutil.FileDir(dumpPath) if !fsutil.DirExists(dirPath) { err := os.MkdirAll(dirPath, 0755) if err != nil { fmt.Printf("cmd=xray info=change.path.match.dump.error file='%s' ppattern='%s' target='%s' error='%s'):\n", fullPath, cpm.PathPattern, dumpPath, err) continue } } err := os.WriteFile(dumpPath, data, 0644) if err != nil { fmt.Printf("cmd=xray info=change.path.match.dump.error file='%s' ppattern='%s' target='%s' error='%s'):\n", fullPath, cpm.PathPattern, dumpPath, err) continue } fmt.Printf("cmd=xray info=change.path.match.dump file='%s' ppattern='%s' target='%s'):\n", fullPath, cpm.PathPattern, dumpPath) } break } } for _, cdm := range changeDataMatchers { if cdm.PathPattern != "" { pmatch, err := doublestar.Match(cdm.PathPattern, fullPath) if err != nil { log.Errorf("doublestar.Match name='%s' error=%v", fullPath, err) continue } if !pmatch { continue } } if cdm.Matcher.Match(data) { layer.DataMatches[fullPath] = append(layer.DataMatches[fullPath], cdm) if cdm.Dump { if cdm.DumpConsole { fmt.Printf("cmd=xray info=change.data.match.start\n") fmt.Printf("cmd=xray info=change.data.match file='%s' ppattern='%s' dpattern='%s')\n", fullPath, cdm.PathPattern, cdm.DataPattern) fmt.Printf("%s\n", string(data)) fmt.Printf("cmd=xray info=change.data.match.end\n") } if cdm.DumpDir != "" { dumpPath := filepath.Join(cdm.DumpDir, fullPath) dirPath := fsutil.FileDir(dumpPath) if !fsutil.DirExists(dirPath) { err := os.MkdirAll(dirPath, 0755) if err != nil { fmt.Printf("cmd=xray info=change.data.match.dump.error file='%s' ppattern='%s' dpattern='%s' target='%s' error='%s'):\n", fullPath, cdm.PathPattern, cdm.DataPattern, dumpPath, err) continue } } err := os.WriteFile(dumpPath, data, 0644) if err != nil { fmt.Printf("cmd=xray info=change.data.match.dump.error file='%s' ppattern='%s' dpattern='%s' target='%s' error='%s'):\n", fullPath, cdm.PathPattern, cdm.DataPattern, dumpPath, err) continue } fmt.Printf("cmd=xray info=change.data.match.dump file='%s' ppattern='%s' dpattern='%s' target='%s'):\n", fullPath, cdm.PathPattern, cdm.DataPattern, dumpPath) } } //TODO: //add a flag to do first match only //now we'll try to match all data patterns } } } else { for _, cpm := range changePathMatchers { if cpm.PathPattern != "" { pmatch, err := doublestar.Match(cpm.PathPattern, fullPath) if err != nil { log.Errorf("doublestar.Match name='%s' error=%v", fullPath, err) continue } if pmatch { object.PathMatch = true layer.pathMatches = true break } } } var hash string if doHashData || len(changeDataHashMatchers) > 0 { var err error hash, err = getStreamHash(reader) if err != nil { log.Errorf("inspectFile: getStreamHash error - name='%s' error=%v", fullPath, err) return err } } if doHashData { object.Hash = hash } if len(hash) > 0 && len(changeDataHashMatchers) > 0 { if dhm, found := changeDataHashMatchers[hash]; found { //need to save to DataHashMatches to make it work without generating/saving hashes for all objects layer.DataHashMatches[fullPath] = dhm if dhm.Dump { log.Errorf("inspectFile: should not dump - %#v", dhm) } } } } return nil } func jsonFromStream(source string, name string, reader io.Reader, data interface{}) error { raw, err := io.ReadAll(reader) if err != nil { return err } sr := strings.NewReader(string(raw)) log.Tracef("dockerimage.LoadPackage.jsonFromStream: name='%s' data[%d]='%s' source='%s'", name, len(raw), string(raw), source) return json.NewDecoder(sr).Decode(data) } type TarReadCloser struct { io.Reader io.Closer } func FileReaderFromTar(tarPath, filePath string) (io.ReadCloser, error) { tfile, err := os.Open(tarPath) if err != nil { log.Errorf("dockerimage.FileReaderFromTar: os.Open error - %v", err) return nil, err } defer tfile.Close() tr := tar.NewReader(tfile) for { hdr, err := tr.Next() if err == io.EOF { break } if err != nil { return nil, err } if hdr == nil || hdr.Name == "" { continue } hdr.Name = filepath.Clean(hdr.Name) if hdr.Name == filePath { switch hdr.Typeflag { case tar.TypeReg, tar.TypeSymlink, tar.TypeLink: return TarReadCloser{ Reader: tr, Closer: tfile, }, nil } } } return nil, fmt.Errorf("no file - %s", filePath) } func FileDataFromTar(tarPath, filePath string) ([]byte, error) { tfile, err := os.Open(tarPath) if err != nil { log.Errorf("dockerimage.FileDataFromTar: os.Open error - %v", err) return nil, err } defer tfile.Close() tr := tar.NewReader(tfile) for { hdr, err := tr.Next() if err == io.EOF { break } if err != nil { return nil, err } if hdr == nil || hdr.Name == "" { continue } hdr.Name = filepath.Clean(hdr.Name) if hdr.Name == filePath { switch hdr.Typeflag { case tar.TypeReg, tar.TypeSymlink, tar.TypeLink: return io.ReadAll(tr) } } } return nil, fmt.Errorf("no file - %s", filePath) } func LoadManifestObject(archivePath, imageID string) (*DockerManifestObject, error) { return nil, nil } func LoadConfigObject(archivePath, imageID string) (*ConfigObject, error) { return nil, nil } func LoadLayer(archivePath, imageID, layerID string) (*Layer, error) { return nil, nil } ================================================ FILE: pkg/docker/dockerimage/imagelayout.go ================================================ package dockerimage const ( dockerManifestFileName = "manifest.json" dockerReposFileName = "repositories" //dockerV1 config object file name: .json dockerV1LayerSuffix = "/layer.tar" ) const ( ociLayoutFileName = "oci-layout" ociLayoutVersion = "1.0.0" ociIndexFileName = "index.json" ociBlobDirName = "blobs" ociBlobDirPrefix = "blobs/" ) type OCILayout struct { Version string `json:"imageLayoutVersion"` } ================================================ FILE: pkg/docker/dockerimage/metadata.go ================================================ package dockerimage import ( "encoding/base64" "encoding/json" "path/filepath" "runtime" "strings" "time" ) // pkg/system/architecture.go has architecture enums, // but the enums are not always identical const ( OSLinux = "linux" DefaultOS = OSLinux ArchARM = "arm" ArchARM64 = "arm64" ArchAMD64 = "amd64" DefaultArch = ArchAMD64 ) func DefaultRuntimeArch() string { return runtime.GOARCH } // focusing on the primary OS and architectures (ignoring the rest) var ArchVarMap = map[string][]string{ ArchARM: {"", "v6", "v7", "v8"}, ArchARM64: {"", "v8"}, ArchAMD64: {""}, } func IsValidArchitecture(arch string, variant string) bool { for varch, vvariants := range ArchVarMap { if varch == arch { for _, vvariant := range vvariants { if vvariant == variant { return true } } return false } } return false } var OSArchMap = map[string][]string{ OSLinux: { "386", ArchAMD64, ArchARM, ArchARM64, "ppc64", "ppc64le", "mips64", "mips64le", "s390x", "riscv64"}, } func IsValidPlatform(os string, arch string) bool { for vos, varchs := range OSArchMap { if vos == os { for _, varch := range varchs { if varch == arch { return true } } return false } } return false } // v1 and oci-based Manifest structure is the same* / compatible // but config and layer paths are different type DockerManifestObject struct { // v1 format: "IMAGE_ID.json" (no sha256 prefix in IMAGE_ID) // oci format: "blobs/sha256/DIGEST_NO_PREFIX" Config string RepoTags []string `json:",omitempty"` //["user/repo:tag"] // v1 format: "LAYER_ID/layer.tar" (no sha256 prefix in LAYER_ID) // oci format: "blobs/sha256/DIGEST" (no sha256 prefix in DIGEST) Layers []string // newer fields Parent string `json:",omitempty"` // DiffID map (where DiffID does have the "sha256:" prefix) LayerSources map[string]BlobDescriptor `json:",omitempty"` } // can reuse the 'Descriptor' for 'BlobDescriptor' from github.com/opencontainers/image-spec/specs-go/v1 type BlobDescriptor struct { MediaType string `json:"mediaType,omitempty"` Size int64 `json:"size,omitempty"` Digest string `json:"digest,omitempty"` // extra fields Annotations map[string]string `json:"annotations,omitempty"` URLs []string `json:"urls,omitempty"` Platform *Platform `json:"platform,omitempty"` } // todo: can reuse 'Platform' from github.com/opencontainers/image-spec/specs-go/v1 type Platform struct { Architecture string `json:"architecture"` OS string `json:"os"` OSVersion string `json:"os.version,omitempty"` OSFeatures []string `json:"os.features,omitempty"` Variant string `json:"variant,omitempty"` } // data structures from https://github.com/moby/moby/blob/master/image type V1ConfigObject struct { // ID is a unique 64 character identifier of the image ID string `json:"id,omitempty"` // Parent is the ID of the parent image Parent string `json:"parent,omitempty"` // Comment is the commit message that was set when committing the image Comment string `json:"comment,omitempty"` // Created is the timestamp at which the image was created Created time.Time `json:"created"` // Container is the id of the container used to commit Container string `json:"container,omitempty"` // ContainerConfig is the configuration of the container that is committed into the image ContainerConfig ContainerConfig `json:"container_config,omitempty"` // DockerVersion specifies the version of Docker that was used to build the image DockerVersion string `json:"docker_version,omitempty"` // Author is the name of the author that was specified when committing the image Author string `json:"author,omitempty"` // Config is the configuration of the container received from the client Config *ContainerConfig `json:"config,omitempty"` // Architecture is the hardware that the image is built and runs on Architecture string `json:"architecture,omitempty"` // Variant is the CPU architecture variant (presently ARM-only) Variant string `json:"variant,omitempty"` // OS is the operating system used to build and run the image OS string `json:"os,omitempty"` // Size is the total size of the image including all layers it is composed of Size int64 `json:",omitempty"` } type ConfigObject struct { V1ConfigObject Parent string `json:"parent,omitempty"` //nolint:govet RootFS *RootFS `json:"rootfs,omitempty"` History []XHistory `json:"history,omitempty"` OSVersion string `json:"os.version,omitempty"` OSFeatures []string `json:"os.features,omitempty"` //buildkit build info BuildInfoRaw string `json:"moby.buildkit.buildinfo.v1,omitempty"` BuildInfoDecoded *BuildKitBuildInfo } //data structures from https://github.com/moby/moby/blob/master/image/rootfs.go const TypeLayers = "layers" type RootFS struct { Type string `json:"type"` DiffIDs []string `json:"diff_ids,omitempty"` } //data structures from https://github.com/moby/moby/blob/master/image/image.go // XHistory augments the standard History struct with extra layer info type XHistory struct { // Created is the timestamp at which the image was created Created time.Time `json:"created"` // Author is the name of the author that was specified when committing the image Author string `json:"author,omitempty"` // CreatedBy keeps the Dockerfile command used while building the image CreatedBy string `json:"created_by,omitempty"` // Comment is the commit message that was set when committing the image Comment string `json:"comment,omitempty"` // EmptyLayer is set to true if this history item did not generate a // layer. Otherwise, the history item is associated with the next // layer in the RootFS section. EmptyLayer bool `json:"empty_layer,omitempty"` //extra fields LayerID string `json:"layer_id,omitempty"` LayerIndex int `json:"layer_index"` LayerFSDiffID string `json:"layer_fsdiff_id,omitempty"` } //data structures from https://github.com/moby/moby/blob/master/api/types/container/config.go type ContainerConfig struct { Hostname string // Hostname Domainname string // Domainname User string // User that will run the command(s) inside the container, also support user:group AttachStdin bool // Attach the standard input, makes possible user interaction AttachStdout bool // Attach the standard output AttachStderr bool // Attach the standard error ExposedPorts map[string]struct{} `json:",omitempty"` // List of exposed ports Tty bool // Attach standard streams to a tty, including stdin if it is not closed. OpenStdin bool // Open stdin StdinOnce bool // If true, close stdin after the 1 attached client disconnects. Env []string // List of environment variable to set in the container Cmd []string // Command to run when starting the container Healthcheck *HealthConfig `json:",omitempty"` // Healthcheck describes how to check the container is healthy ArgsEscaped bool `json:",omitempty"` // True if command is already escaped (meaning treat as a command line) (Windows specific). Image string // Name of the image as it was passed by the operator (e.g. could be symbolic) Volumes map[string]struct{} // List of volumes (mounts) used for the container WorkingDir string // Current directory (PWD) in the command will be launched Entrypoint []string // Entrypoint to run when starting the container NetworkDisabled bool `json:",omitempty"` // Is network disabled MacAddress string `json:",omitempty"` // Mac Address of the container OnBuild []string // ONBUILD metadata that were defined on the image Dockerfile Labels map[string]string // List of labels set to this container StopSignal string `json:",omitempty"` // Signal to stop a container StopTimeout *int `json:",omitempty"` // Timeout (in seconds) to stop a container Shell []string `json:",omitempty"` // Shell for shell-form of RUN, CMD, ENTRYPOINT } //data structures from https://github.com/moby/moby/blob/master/api/types/container/config.go type HealthConfig struct { // Test is the test to perform to check that the container is healthy. // An empty slice means to inherit the default. // The options are: // {} : inherit healthcheck // {"NONE"} : disable healthcheck // {"CMD", args...} : exec arguments directly // {"CMD-SHELL", command} : run command with system's default shell Test []string `json:",omitempty"` // Zero means to inherit. Durations are expressed as integer nanoseconds. Interval time.Duration `json:",omitempty"` // Interval is the time to wait between checks. Timeout time.Duration `json:",omitempty"` // Timeout is the time to wait before considering the check to have hung. StartPeriod time.Duration `json:",omitempty"` // The start period for the container to initialize before the retries starts to count down. // Retries is the number of consecutive failures needed to consider a container as unhealthy. // Zero means inherit. Retries int `json:",omitempty"` } //data structures from https://github.com/moby/buildkit/blob/master/util/buildinfo/types/types.go type BuildKitBuildInfo struct { // Frontend defines the frontend used to build. Frontend string `json:"frontend,omitempty"` // Attrs defines build request attributes. Attrs map[string]*string `json:"attrs,omitempty"` // Sources defines build dependencies. Sources []*BuildSource `json:"sources,omitempty"` // Deps defines context dependencies. Deps map[string]BuildKitBuildInfo `json:"deps,omitempty"` } type BuildSource struct { // Type defines the SourceType source type (docker-image, git, http). Type SourceType `json:"type,omitempty"` // Ref is the reference of the source. Ref string `json:"ref,omitempty"` // Alias is a special field used to match with the actual source ref // because frontend might have already transformed a string user typed // before generating LLB. Alias string `json:"alias,omitempty"` // Pin is the source digest. Pin string `json:"pin,omitempty"` } type SourceType string const ( SourceTypeDockerImage SourceType = "docker-image" SourceTypeGit SourceType = "git" SourceTypeHTTP SourceType = "http" ) func buildInfoDecode(encoded string) (*BuildKitBuildInfo, error) { if encoded == "" { return nil, nil } raw, err := base64.StdEncoding.DecodeString(encoded) if err != nil { return nil, err } var info BuildKitBuildInfo if err = json.Unmarshal(raw, &info); err != nil { return nil, err } return &info, nil } // consts from https://github.com/moby/moby/blob/master/pkg/archive/whiteouts.go // WhiteoutPrefix prefix means file is a whiteout. If this is followed by a // filename this means that file has been removed from the base layer. const WhiteoutPrefix = ".wh." // WhiteoutMetaPrefix prefix means whiteout has a special meaning and is not // for removing an actual file. Normally these files are excluded from exported // archives. const WhiteoutMetaPrefix = WhiteoutPrefix + WhiteoutPrefix // WhiteoutLinkDir is a directory AUFS uses for storing hardlink links to other // layers. Normally these should not go into exported archives and all changed // hardlinks should be copied to the top layer. const WhiteoutLinkDir = WhiteoutMetaPrefix + "plnk" // WhiteoutOpaqueDir file means directory has been made opaque - meaning // readdir calls to this directory do not follow to lower layers. const WhiteoutOpaqueDir = WhiteoutMetaPrefix + ".opq" func IsDeletedFileObject(path string) bool { name := filepath.Base(path) return strings.HasPrefix(name, WhiteoutPrefix) } func NormalizeFileObjectLayerPath(path string) (string, bool, bool, error) { isDeleted := false name := filepath.Base(path) if name == WhiteoutOpaqueDir { //return a fake wildcard delete path for now (to make it easy to detect) return filepath.Join(filepath.Dir(path), "*"), true, true, nil } if strings.HasPrefix(name, WhiteoutPrefix) { restored := name[len(WhiteoutPrefix):] dir := filepath.Dir(path) isDeleted = true path = filepath.Join(dir, restored) } return path, isDeleted, false, nil } ================================================ FILE: pkg/docker/dockerimage/packagefiles.go ================================================ package dockerimage import ( "archive/tar" "errors" "fmt" "io" "os" "path/filepath" "strings" "time" "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/tarball" "github.com/google/go-containerregistry/pkg/v1/types" log "github.com/sirupsen/logrus" ) const ( DirType = "dir" FileType = "file" SymlinkType = "symlink" HardlinkType = "hardlink" OtherType = "other" UnknownType = "unknown" UnexpectedType = "unexpected" ) func ObjectTypeFromTarType(flag byte) string { switch flag { case tar.TypeDir: return DirType case tar.TypeReg, tar.TypeRegA: return FileType case tar.TypeSymlink: return SymlinkType case tar.TypeLink: return HardlinkType default: return fmt.Sprintf("other(%v)", flag) } } func TarHeaderTypeName(flag byte) string { switch flag { case tar.TypeReg: return "tar.TypeReg" case tar.TypeRegA: return "tar.TypeRegA" case tar.TypeLink: return "tar.TypeLink" case tar.TypeSymlink: return "tar.TypeSymlink" case tar.TypeChar: return "tar.TypeChar" case tar.TypeBlock: return "tar.TypeBlock" case tar.TypeDir: return "tar.TypeDir" case tar.TypeFifo: return "tar.TypeFifo" default: return fmt.Sprintf("other(%v)", flag) } } type PackageFiles struct { img v1.Image imgDigest string imgID string } type LayerMetadata struct { Index int `json:"index,omitempty"` Digest string `json:"digest,omitempty"` DiffID string `json:"diff_id,omitempty"` MediaType string `json:"media_type,omitempty"` Size int64 `json:"size,omitempty"` } type FileSelectorType string const ( FSTAll FileSelectorType = "fst.all" FSTDigest FileSelectorType = "fst.digest" FSTDiffID FileSelectorType = "fst.diffid" FSTIndex FileSelectorType = "fst.index" FSTIndexRange FileSelectorType = "fst.index.range" ) type FileSelector struct { Type FileSelectorType Key string IndexKey int IndexEndKey int ReverseIndexRange bool RawNames bool NoDirs bool Deleted bool } type FileMetadata struct { Type string `json:"type,omitempty"` IsDir bool `json:"is_dir,omitempty"` IsDelete bool `json:"is_delete,omitempty"` IsOpq bool `json:"is_opq,omitempty"` Name string `json:"name,omitempty"` RawName string `json:"raw_name,omitempty"` Size int64 `json:"size,omitempty"` Mode os.FileMode `json:"mode,omitempty"` UID int `json:"uid"` GID int `json:"gid"` ModTime time.Time `json:"mod_time,omitempty"` ChangeTime time.Time `json:"change_time,omitempty"` } type LayerFiles struct { Layer *LayerMetadata `json:"layer"` Files []*FileMetadata `json:"files"` } func NewPackageFiles(archivePath string) (*PackageFiles, error) { img, err := tarball.ImageFromPath(archivePath, nil) if err != nil { return nil, err } digest, err := img.Digest() if err != nil { return nil, err } cfgName, err := img.ConfigName() if err != nil { return nil, err } ref := &PackageFiles{ img: img, imgDigest: digest.String(), imgID: cfgName.String(), } return ref, nil } func (ref *PackageFiles) ImageDigest() string { return ref.imgDigest } func (ref *PackageFiles) ImageID() string { return ref.imgID } func (ref *PackageFiles) ListImageHistory() ([]XHistory, error) { configFile, err := ref.img.ConfigFile() if err != nil { return nil, err } var list []XHistory for _, record := range configFile.History { info := XHistory{ Created: record.Created.Time, CreatedBy: record.CreatedBy, Comment: record.Comment, EmptyLayer: record.EmptyLayer, Author: record.Author, } list = append(list, info) } return list, nil } func (ref *PackageFiles) LayerCount() int { layers, _ := ref.img.Layers() return len(layers) } func (ref *PackageFiles) ListLayerMetadata() ([]*LayerMetadata, error) { layers, err := ref.img.Layers() if err != nil { return nil, err } var list []*LayerMetadata for idx, layer := range layers { digest, _ := layer.Digest() diffID, _ := layer.DiffID() size, _ := layer.Size() mediaType, _ := layer.MediaType() layerInfo := &LayerMetadata{ Index: idx, Digest: digest.String(), DiffID: diffID.String(), MediaType: string(mediaType), Size: size, } list = append(list, layerInfo) } return list, nil } func (ref *PackageFiles) ListLayerFiles(selectors []FileSelector) ([]*LayerFiles, error) { //layersMetadata (by index) layersMetadata, err := ref.ListLayerMetadata() if err != nil { return nil, err } layers, err := ref.img.Layers() if err != nil { return nil, err } layersMetadataByDigest := map[string]*LayerMetadata{} layersMetadataByDiffID := map[string]*LayerMetadata{} for _, lmInfo := range layersMetadata { if lmInfo.Digest != "" { layersMetadataByDigest[lmInfo.Digest] = lmInfo } if lmInfo.DiffID != "" { layersMetadataByDiffID[lmInfo.DiffID] = lmInfo } } layerSelectors := map[string]FileSelector{} for _, selector := range selectors { switch selector.Type { case FSTAll: for k := range layersMetadataByDiffID { //todo: create a deep selector copy layerSelectors[k] = selector } case FSTDigest: if selector.Key != "" { if info, found := layersMetadataByDigest[selector.Key]; found { layerSelectors[info.DiffID] = selector } } case FSTDiffID: if selector.Key != "" { if info, found := layersMetadataByDiffID[selector.Key]; found { layerSelectors[info.DiffID] = selector } } case FSTIndex: if (selector.IndexKey >= 0) && (selector.IndexKey < len(layersMetadata)) { layerSelectors[layersMetadata[selector.IndexKey].DiffID] = selector } case FSTIndexRange: if selector.ReverseIndexRange { if (selector.IndexKey >= 0) && (selector.IndexKey < len(layersMetadata)) && (selector.IndexEndKey >= 0) && (selector.IndexEndKey < len(layersMetadata)) { if selector.IndexEndKey > selector.IndexKey { for i := selector.IndexKey; i <= selector.IndexEndKey; i++ { rindex := len(layersMetadata) - 1 - i if rindex >= 0 { layerSelectors[layersMetadata[rindex].DiffID] = selector } } } } } else { if (selector.IndexKey >= 0) && (selector.IndexKey < len(layersMetadata)) { if selector.IndexEndKey < 0 { //means 'until the last index' selector.IndexEndKey = len(layersMetadata) - 1 } if (selector.IndexEndKey >= 0) && (selector.IndexEndKey < len(layersMetadata)) { if selector.IndexEndKey > selector.IndexKey { for i := selector.IndexKey; i <= selector.IndexEndKey; i++ { layerSelectors[layersMetadata[i].DiffID] = selector } } } } } } } layerFilesByDiff := map[string]*LayerFiles{} for _, layer := range layers { diffID, _ := layer.DiffID() diffIDStr := diffID.String() layerSelector, found := layerSelectors[diffIDStr] if !found { continue } layerInfo, found := layersMetadataByDiffID[diffIDStr] if !found { continue } currentLayerFiles := &LayerFiles{ Layer: layerInfo, } ucr, err := layer.Uncompressed() if err != nil { return nil, err } defer ucr.Close() ltr := tar.NewReader(ucr) for { layerHeader, err := ltr.Next() if err != nil { if errors.Is(err, io.EOF) { break } return nil, err } info := getFileMetadata(layerHeader) if info == nil { continue } if layerSelector.NoDirs && info.IsDir { continue } if !layerSelector.Deleted && (info.IsDelete || info.IsOpq) { continue } currentLayerFiles.Files = append(currentLayerFiles.Files, info) } layerFilesByDiff[diffIDStr] = currentLayerFiles } var layerFiles []*LayerFiles for _, v := range layerFilesByDiff { layerFiles = append(layerFiles, v) } return layerFiles, nil } func getFileMetadata(header *tar.Header) *FileMetadata { if header == nil || header.Name == "" { return nil } var isDir bool if header.Typeflag == tar.TypeDir { isDir = true } fileName := filepath.Clean(header.Name) if fileName != "" && fileName[0] != '/' { fileName = fmt.Sprintf("/%s", fileName) } rawFileName := fileName bname := filepath.Base(fileName) dname := filepath.Dir(fileName) isDelete := strings.HasPrefix(bname, whPrefix) var isOpq bool if isDelete { if bname == whOpaqueDir { isd := header.Typeflag == tar.TypeDir log.Debugf("dockerimage.getFileMetadata - special name: %s (header.Typeflag=%v/%s) / isd=%v", bname, header.Typeflag, TarHeaderTypeName(header.Typeflag), isd) isOpq = true isDelete = false fileName = dname } else { bname = bname[len(whPrefix):] if header.Typeflag != tar.TypeDir { fileName = filepath.Join(dname, bname) } } } result := &FileMetadata{ Type: ObjectTypeFromTarType(header.Typeflag), IsDir: isDir, IsDelete: isDelete, IsOpq: isOpq, Name: fileName, Size: header.Size, Mode: header.FileInfo().Mode(), UID: header.Uid, GID: header.Gid, ModTime: header.ModTime, ChangeTime: header.ChangeTime, } if isDelete || isOpq { result.RawName = rawFileName } return result } // TODO: use already defined constants const ( whPrefix = ".wh." whOpaqueDirSuffix = ".wh..opq" whOpaqueDir = whPrefix + whOpaqueDirSuffix ) //FileDataFromTar //FileReaderFromTar func IsLayerMediaType(value types.MediaType) bool { switch value { case types.DockerLayer, types.DockerUncompressedLayer, types.OCILayer, types.OCIUncompressedLayer, types.OCILayerZStd: return true } return false } /* NOTE: TAR FILE LIST INCLUDES EXTRAS LIKE INTERMEDIATE DIRECTORIES, WHITEOUT FILES, DIRECTORY WHITEOUT FILES NEED TO CLEAN UP THOSE THINGS WHEN GENERATING THE FILE LIST ALSO HAVE A MODE TO INCLUDE/EXCLUDE INTERMEDIATE DIRECTORIES INPUT: IMAGE TAR FILE PATH OPTIONS/FLAGS (WHAT I WANT? / CAPABILITIES: * List files for the last X layers (note: this is a "range" call) - MVP (should i retain any extra meta data for layers, etc?) * List files for one specific layer (how to select? options: index, digest, fsdiffid) * List files for a set of layers * List all files - MAYBE (return a map of layers with lists/maps of files in each???) OR (return a global map of files with layer as a property???) OR (both or configurable to have either of these three options???) */ ================================================ FILE: pkg/docker/dockerimage/topobjects.go ================================================ package dockerimage import ( "container/heap" ) type TopObjects []*ObjectMetadata func NewTopObjects(n int) TopObjects { if n < 1 { n = 1 } n++ return make(TopObjects, 0, n) } func (to TopObjects) Len() int { return len(to) } func (to TopObjects) Less(i, j int) bool { return to[i].Size < to[j].Size } func (to TopObjects) Swap(i, j int) { to[i], to[j] = to[j], to[i] } func (to *TopObjects) Push(x interface{}) { item := x.(*ObjectMetadata) *to = append(*to, item) } func (to *TopObjects) Pop() interface{} { old := *to n := len(old) item := old[n-1] old[n-1] = nil *to = old[0 : n-1] return item } func (to TopObjects) List() []*ObjectMetadata { list := []*ObjectMetadata{} for len(to) > 0 { item := heap.Pop(&to).(*ObjectMetadata) list = append([]*ObjectMetadata{item}, list...) } return list } ================================================ FILE: pkg/docker/dockerutil/dockerutil.go ================================================ package dockerutil import ( "archive/tar" "bufio" "bytes" "errors" "fmt" "io" "os" "path/filepath" "strings" "time" "github.com/docker/docker/pkg/archive" dockerapi "github.com/fsouza/go-dockerclient" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/docker/dockerclient" "github.com/slimtoolkit/slim/pkg/util/fsutil" ) var ( ErrBadParam = errors.New("bad parameter") ErrNotFound = errors.New("not found") ) const ( volumeMountPat = "%s:/data" volumeBasePath = "/data" emptyImageName = "docker-slim-empty-image:latest" emptyImageDockerfile = "FROM scratch\nCMD\n" ) type BasicImageProps struct { ID string Size int64 Created int64 } type ImageIdentity struct { ID string ShortTags []string RepoTags []string ShortDigests []string RepoDigests []string } func APIImagesToIdentity(info *dockerapi.APIImages) *ImageIdentity { imageInfo := &dockerapi.Image{ ID: info.ID, RepoTags: info.RepoTags, RepoDigests: info.RepoDigests, } return ImageToIdentity(imageInfo) } func ImageToIdentity(info *dockerapi.Image) *ImageIdentity { result := &ImageIdentity{ ID: info.ID, RepoTags: info.RepoTags, RepoDigests: info.RepoDigests, } uniqueTags := map[string]struct{}{} for _, tag := range result.RepoTags { parts := strings.Split(tag, ":") if len(parts) == 2 { uniqueTags[parts[1]] = struct{}{} } } for k := range uniqueTags { result.ShortTags = append(result.ShortTags, k) } uniqueDigests := map[string]struct{}{} for _, digest := range result.RepoDigests { parts := strings.Split(digest, "@") if len(parts) == 2 { uniqueDigests[parts[1]] = struct{}{} } } for k := range uniqueDigests { result.ShortDigests = append(result.ShortDigests, k) } return result } func CleanImageID(id string) string { if strings.HasPrefix(id, "sha256:") { id = strings.TrimPrefix(id, "sha256:") } return id } func HasEmptyImage(dclient *dockerapi.Client) error { _, err := HasImage(dclient, emptyImageName) return err } func HasImage(dclient *dockerapi.Client, imageRef string) (*ImageIdentity, error) { //NOTES: //ListImages doesn't filter by image ID (must use ImageInspect instead) //Check images by name:tag, full or partial image ID or name@digest if imageRef == "" || imageRef == "." || imageRef == ".." { return nil, ErrBadParam } var err error if dclient == nil { socketInfo, err := dockerclient.GetUnixSocketAddr() if err != nil { return nil, err } if socketInfo == nil || socketInfo.Address == "" { return nil, fmt.Errorf("no unix socket found") } dclient, err = dockerapi.NewClient(socketInfo.Address) if err != nil { log.Errorf("dockerutil.HasImage(%s): dockerapi.NewClient() error = %v", imageRef, err) return nil, err } } imageInfo, err := dclient.InspectImage(imageRef) if err != nil { if err == dockerapi.ErrNoSuchImage { return nil, ErrNotFound } return nil, err } return ImageToIdentity(imageInfo), nil } func ListImages(dclient *dockerapi.Client, imageNameFilter string) (map[string]BasicImageProps, error) { // python <- exact match only // py* <- all image names starting with 'py' (no/default namespace) // dslimexamples/* <- all image names in the 'dslimexamples' namespace // dslimexamples/ser* <- all image names starting with 'ser' in the 'dslimexamples' namespace // dslimexamples/*python* <- all image names with 'python' in them in the 'dslimexamples' namespace // */*python* <- all image names with 'python' in them in all namesapces (except the default namespace) // */*alpine <- all image names ending with 'alpine' in all namesapces (except the default namespace) // * <- all image names with no/default namespace. note that no images with namespaces will be returned var err error if dclient == nil { socketInfo, err := dockerclient.GetUnixSocketAddr() if err != nil { return nil, err } if socketInfo == nil || socketInfo.Address == "" { return nil, fmt.Errorf("no unix socket found") } dclient, err = dockerapi.NewClient(socketInfo.Address) if err != nil { log.Errorf("dockerutil.ListImages(%s): dockerapi.NewClient() error = %v", imageNameFilter, err) return nil, err } } listOptions := dockerapi.ListImagesOptions{ All: false, } if imageNameFilter != "" { listOptions.Filters = map[string][]string{ "reference": {imageNameFilter}, } } imageList, err := dclient.ListImages(listOptions) if err != nil { log.Errorf("dockerutil.ListImages(%s): dockerapi.ListImages() error = %v", imageNameFilter, err) return nil, err } log.Debugf("dockerutil.ListImages(%s): matching images - %+v", imageNameFilter, imageList) images := map[string]BasicImageProps{} for _, imageInfo := range imageList { for _, repo := range imageInfo.RepoTags { info := BasicImageProps{ ID: strings.TrimPrefix(imageInfo.ID, "sha256:"), Size: imageInfo.Size, Created: imageInfo.Created, } if repo == ":" { repo = strings.TrimPrefix(imageInfo.ID, "sha256:") images[repo] = info break } images[repo] = info } } return images, nil } func BuildEmptyImage(dclient *dockerapi.Client) error { //TODO: use the 'internal' build engine that doesn't need Docker if dclient == nil { socketInfo, err := dockerclient.GetUnixSocketAddr() if err != nil { return err } if socketInfo == nil || socketInfo.Address == "" { return fmt.Errorf("no unix socket found") } //note: shouldn't use ":=" here to avoid var shadowing dclient, err = dockerapi.NewClient(socketInfo.Address) if err != nil { log.Errorf("dockerutil.BuildEmptyImage: dockerapi.NewClient() error = %v", err) return err } } var input bytes.Buffer tw := tar.NewWriter(&input) header := tar.Header{ Name: "Dockerfile", Size: int64(len(emptyImageDockerfile)), } if err := tw.WriteHeader(&header); err != nil { return err } if _, err := tw.Write([]byte(emptyImageDockerfile)); err != nil { return err } if err := tw.Close(); err != nil { return err } var output bytes.Buffer buildOptions := dockerapi.BuildImageOptions{ Name: emptyImageName, InputStream: &input, OutputStream: &output, RmTmpContainer: true, ForceRmTmpContainer: true, } if err := dclient.BuildImage(buildOptions); err != nil { log.Errorf("dockerutil.BuildEmptyImage: dockerapi.BuildImage() error = %v / output: %s", err, output.String()) return err } return nil } func SaveImage(dclient *dockerapi.Client, imageRef, local string, extract, removeOrig bool) error { if local == "" { return ErrBadParam } var err error if dclient == nil { socketInfo, err := dockerclient.GetUnixSocketAddr() if err != nil { return err } if socketInfo == nil || socketInfo.Address == "" { return fmt.Errorf("no unix socket found") } dclient, err = dockerapi.NewClient(socketInfo.Address) if err != nil { log.Errorf("dockerutil.SaveImage: dockerapi.NewClient() error = %v", err) return err } } imageRef = CleanImageID(imageRef) //todo: 'pull' the image if it's not available locally yet //note: HasImage() doesn't work with image IDs dir := fsutil.FileDir(local) if !fsutil.DirExists(dir) { err := os.MkdirAll(dir, 0755) if err != nil { return err } } dfile, err := os.Create(local) if err != nil { return err } options := dockerapi.ExportImageOptions{ Name: imageRef, OutputStream: dfile, InactivityTimeout: 20 * time.Second, } err = dclient.ExportImage(options) if err != nil { log.Errorf("dockerutil.SaveImage: dclient.ExportImage() error = %v", err) dfile.Close() return err } dfile.Close() if extract { dstDir := filepath.Dir(local) arc := archive.NewDefaultArchiver() afile, err := os.Open(local) if err != nil { log.Errorf("dockerutil.SaveImage: os.Open error - %v", err) return err } tarOptions := &archive.TarOptions{ NoLchown: true, //UIDMaps: arc.IDMapping.UIDs(), //GIDMaps: arc.IDMapping.GIDs(), } tarOptions.IDMap.UIDMaps = arc.IDMapping.UIDMaps tarOptions.IDMap.GIDMaps = arc.IDMapping.GIDMaps err = arc.Untar(afile, dstDir, tarOptions) if err != nil { log.Errorf("dockerutil.SaveImage: error unpacking tar - %v", err) afile.Close() return err } afile.Close() if removeOrig { os.Remove(local) } } return nil } type VolumeInfo struct { Created time.Time Mountpoint string Size uint64 FileCount uint64 } func GetVolumeInfo(dclient *dockerapi.Client, name string, fileCount bool) (*VolumeInfo, error) { if name == "" { return nil, ErrBadParam } var err error if dclient == nil { socketInfo, err := dockerclient.GetUnixSocketAddr() if err != nil { return nil, err } if socketInfo == nil || socketInfo.Address == "" { return nil, fmt.Errorf("no unix socket found") } dclient, err = dockerapi.NewClient(socketInfo.Address) if err != nil { log.Errorf("dockerutil.GetVolumeInfo: dockerapi.NewClient() error = %v", err) return nil, err } } volume, err := dclient.InspectVolume(name) if err != nil { log.Errorf("dockerutil.GetVolumeInfo: dclient.InspectVolume() error = %v", err) return nil, err } info := &VolumeInfo{ Created: volume.CreatedAt, Mountpoint: volume.Mountpoint, } return info, nil } func ListVolumeFiles(dclient *dockerapi.Client, name string) ([]string, error) { if name == "" { return nil, ErrBadParam } return nil, nil } func VolumePathExists(dclient *dockerapi.Client, volume string, pth string) (bool, error) { if volume == "" || pth == "" { return false, ErrBadParam } return false, nil } func HasVolume(dclient *dockerapi.Client, name string) error { if name == "" { return ErrBadParam } var err error if dclient == nil { socketInfo, err := dockerclient.GetUnixSocketAddr() if err != nil { return err } if socketInfo == nil || socketInfo.Address == "" { return fmt.Errorf("no unix socket found") } dclient, err = dockerapi.NewClient(socketInfo.Address) if err != nil { log.Errorf("dockerutil.HasVolume: dockerapi.NewClient() error = %v", err) return err } } listOptions := dockerapi.ListVolumesOptions{ Filters: map[string][]string{"name": {name}}, } volumes, err := dclient.ListVolumes(listOptions) if err != nil { log.Errorf("dockerutil.HasVolume: dclient.ListVolumes() error = %v", err) return err } if len(volumes) == 0 { log.Debugf("dockerutil.HasVolume: volume not found - %v", name) return ErrNotFound } for _, info := range volumes { if info.Name == name { return nil } } return ErrNotFound } func DeleteVolume(dclient *dockerapi.Client, name string) error { if name == "" { return ErrBadParam } if dclient == nil { socketInfo, err := dockerclient.GetUnixSocketAddr() if err != nil { return err } if socketInfo == nil || socketInfo.Address == "" { return fmt.Errorf("no unix socket found") } //note: shouldn't use ":=" here to avoid var shadowing dclient, err = dockerapi.NewClient(socketInfo.Address) if err != nil { log.Errorf("dockerutil.DeleteVolume: dockerapi.NewClient() error = %v", err) return err } } if err := HasVolume(dclient, name); err == nil { removeOptions := dockerapi.RemoveVolumeOptions{ Name: name, Force: true, } //ok to call remove even if the volume isn't there err = dclient.RemoveVolumeWithOptions(removeOptions) if err != nil { fmt.Printf("dockerutil.DeleteVolume: dclient.RemoveVolumeWithOptions() error = %v\n", err) return err } } return nil } func CopyToVolume( dclient *dockerapi.Client, volumeName string, source string, dstRootDir string, dstTargetDir string) error { var err error if dclient == nil { socketInfo, err := dockerclient.GetUnixSocketAddr() if err != nil { return err } if socketInfo == nil || socketInfo.Address == "" { return fmt.Errorf("no unix socket found") } dclient, err = dockerapi.NewClient(socketInfo.Address) if err != nil { log.Errorf("dockerutil.CopyToVolume: dockerapi.NewClient() error = %v", err) return err } } volumeBinds := []string{fmt.Sprintf(volumeMountPat, volumeName)} containerOptions := dockerapi.CreateContainerOptions{ Name: volumeName, //todo: might be good to make it unique (to support concurrent copy op) Config: &dockerapi.Config{ Image: emptyImageName, Labels: map[string]string{"owner": "docker-slim"}, }, HostConfig: &dockerapi.HostConfig{ Binds: volumeBinds, }, } containerInfo, err := dclient.CreateContainer(containerOptions) if err != nil { log.Errorf("dockerutil.CopyToVolume: dclient.CreateContainer() error = %v", err) return err } containerID := containerInfo.ID log.Debugf("dockerutil.CopyToVolume: containerID - %v", containerID) rmContainer := func() { removeOptions := dockerapi.RemoveContainerOptions{ ID: containerID, Force: true, } err = dclient.RemoveContainer(removeOptions) if err != nil { fmt.Printf("dockerutil.CopyToVolume: dclient.RemoveContainer() error = %v\n", err) } } cleanSource, err := filepath.EvalSymlinks(source) if err != nil { log.Errorf("dockerutil.CopyToVolume: filepath.EvalSymlinks(%s) error = %v", source, err) rmContainer() return err } if fsutil.IsSymlink(cleanSource) { log.Errorf("dockerutil.CopyToVolume: source is a symlink = %s", cleanSource) rmContainer() return fmt.Errorf("source is symlink") } tarData, err := archive.Tar(cleanSource, archive.Uncompressed) if err != nil { log.Errorf("dockerutil.CopyToVolume: archive.Tar() error = %v", err) rmContainer() return err } targetPath := volumeBasePath if dstRootDir != "" { dirData, err := GenStateDirsTar(dstRootDir, dstTargetDir) if err != nil { log.Errorf("dockerutil.CopyToVolume: GenStateDirsTar() error = %v", err) rmContainer() return err } dirUploadOptions := dockerapi.UploadToContainerOptions{ InputStream: dirData, Path: targetPath, } err = dclient.UploadToContainer(containerID, dirUploadOptions) if err != nil { log.Errorf("dockerutil.CopyToVolume: copy dirs - dclient.UploadToContainer() error = %v", err) rmContainer() return err } targetPath = filepath.Join(volumeBasePath, dstRootDir, dstTargetDir) } uploadOptions := dockerapi.UploadToContainerOptions{ InputStream: tarData, Path: targetPath, } err = dclient.UploadToContainer(containerID, uploadOptions) if err != nil { log.Errorf("dockerutil.CopyToVolume: dclient.UploadToContainer() error = %v", err) tarData.Close() rmContainer() return err } tarData.Close() rmContainer() return nil } func GenStateDirsTar(rootDir, stateDir string) (io.Reader, error) { if rootDir == "" || stateDir == "" { return nil, ErrBadParam } var b bytes.Buffer tw := tar.NewWriter(&b) baseDirHdr := tar.Header{ Typeflag: tar.TypeDir, Name: fmt.Sprintf("%s/", rootDir), Mode: 16877, } if err := tw.WriteHeader(&baseDirHdr); err != nil { log.Errorf("dockerutil.GenStateDirsTar: error writing base dir header to archive - %v", err) return nil, err } stateDirHdr := tar.Header{ Typeflag: tar.TypeDir, Name: fmt.Sprintf("%s/%s/", rootDir, stateDir), Mode: 16877, } if err := tw.WriteHeader(&stateDirHdr); err != nil { log.Errorf("dockerutil.GenStateDirsTar: error writing state dir header to archive - %v", err) return nil, err } if err := tw.Close(); err != nil { log.Errorf("dockerutil.GenStateDirsTar: error closing archive - %v", err) return nil, err } return &b, nil } func CreateVolumeWithData( dclient *dockerapi.Client, source string, name string, labels map[string]string) error { if name == "" { return ErrBadParam } if source != "" { if _, err := os.Stat(source); err != nil { log.Errorf("dockerutil.CreateVolumeWithData: bad source (%v) = %v", source, err) return err } } var err error if dclient == nil { socketInfo, err := dockerclient.GetUnixSocketAddr() if err != nil { return err } if socketInfo == nil || socketInfo.Address == "" { return fmt.Errorf("no unix socket found") } dclient, err = dockerapi.NewClient(socketInfo.Address) if err != nil { log.Errorf("dockerutil.CreateVolumeWithData: dockerapi.NewClient() error = %v", err) return err } } volumeOptions := dockerapi.CreateVolumeOptions{ Name: name, Labels: labels, } volumeInfo, err := dclient.CreateVolume(volumeOptions) if err != nil { log.Errorf("dockerutil.CreateVolumeWithData: dclient.CreateVolume() error = %v", err) return err } log.Debugf("dockerutil.CreateVolumeWithData: volumeInfo = %+v", volumeInfo) if source != "" { return CopyToVolume(dclient, name, source, "", "") } return nil } func CopyFromContainer(dclient *dockerapi.Client, containerID, remote, local string, extract, removeOrig bool) error { if containerID == "" || remote == "" || local == "" { return ErrBadParam } var err error if dclient == nil { socketInfo, err := dockerclient.GetUnixSocketAddr() if err != nil { return err } if socketInfo == nil || socketInfo.Address == "" { return fmt.Errorf("no unix socket found") } dclient, err = dockerapi.NewClient(socketInfo.Address) if err != nil { log.Errorf("dockerutil.CopyFromContainer: dockerapi.NewClient() error = %v", err) return err } } dfile, err := os.Create(local) if err != nil { return err } downloadOptions := dockerapi.DownloadFromContainerOptions{ Path: remote, OutputStream: dfile, InactivityTimeout: 20 * time.Second, } err = dclient.DownloadFromContainer(containerID, downloadOptions) if err != nil { log.Errorf("dockerutil.CopyFromContainer: dclient.DownloadFromContainer() error = %v", err) dfile.Close() return err } dfile.Close() if extract { dstDir := filepath.Dir(local) arc := archive.NewDefaultArchiver() afile, err := os.Open(local) if err != nil { log.Errorf("dockerutil.CopyFromContainer: os.Open error - %v", err) return err } tarOptions := &archive.TarOptions{ NoLchown: true, //UIDMaps: arc.IDMapping.UIDs(), //GIDMaps: arc.IDMapping.GIDs(), } tarOptions.IDMap.UIDMaps = arc.IDMapping.UIDMaps tarOptions.IDMap.GIDMaps = arc.IDMapping.GIDMaps err = arc.Untar(afile, dstDir, tarOptions) if err != nil { log.Errorf("dockerutil.CopyFromContainer: error unpacking tar - %v", err) afile.Close() return err } afile.Close() if removeOrig { os.Remove(local) } } return nil } func PrepareContainerDataArchive(fullPath, newName, removePrefix string, removeOrig bool) error { if fullPath == "" || newName == "" || removePrefix == "" { return ErrBadParam } dirName := filepath.Dir(fullPath) dstPath := filepath.Join(dirName, newName) inFile, err := os.Open(fullPath) if err != nil { log.Errorf("dockerutil.PrepareContainerDataArchive: os.Open(%s) error - %v", fullPath, err) return err } outFile, err := os.Create(dstPath) if err != nil { log.Errorf("dockerutil.PrepareContainerDataArchive: os.Open(%s) error - %v", dstPath, err) inFile.Close() return err } tw := tar.NewWriter(outFile) tr := tar.NewReader(inFile) for { hdr, err := tr.Next() if err == io.EOF { break } if err != nil { log.Errorf("dockerutil.PrepareContainerDataArchive: error reading archive(%v) - %v", fullPath, err) inFile.Close() return err } if hdr == nil || hdr.Name == "" { log.Debugf("dockerutil.PrepareContainerDataArchive: ignoring bad tar header") continue } if hdr.Name == removePrefix { log.Debugf("dockerutil.PrepareContainerDataArchive: ignoring tar object: %v", hdr.Name) continue } if hdr.Name != "" && strings.HasPrefix(hdr.Name, removePrefix) { hdr.Name = strings.TrimPrefix(hdr.Name, removePrefix) } if err := tw.WriteHeader(hdr); err != nil { log.Errorf("dockerutil.PrepareContainerDataArchive: error writing header to archive(%v) - %v", dstPath, err) inFile.Close() outFile.Close() return err } if _, err := io.Copy(tw, tr); err != nil { log.Errorf("dockerutil.PrepareContainerDataArchive: error copying data to archive(%v) - %v", dstPath, err) inFile.Close() outFile.Close() return err } } if err := tw.Close(); err != nil { log.Errorf("dockerutil.PrepareContainerDataArchive: error closing archive(%v) - %v", dstPath, err) } outFile.Close() inFile.Close() if removeOrig { os.Remove(fullPath) } return nil } func ListNetworks(dclient *dockerapi.Client, nameFilter string) ([]string, error) { var err error if dclient == nil { socketInfo, err := dockerclient.GetUnixSocketAddr() if err != nil { return nil, err } if socketInfo == nil || socketInfo.Address == "" { return nil, fmt.Errorf("no unix socket found") } dclient, err = dockerapi.NewClient(socketInfo.Address) if err != nil { log.Errorf("dockerutil.ListNetworks(%s): dockerapi.NewClient() error = %v", nameFilter, err) return nil, err } } filter := dockerapi.NetworkFilterOpts{ "name": map[string]bool{ nameFilter: true, }, } networkList, err := dclient.FilteredListNetworks(filter) if err != nil { log.Errorf("dockerutil.ListNetworks(%s): dockerapi.FilteredListNetworks() error = %v", nameFilter, err) return nil, err } var names []string for _, networkInfo := range networkList { names = append(names, networkInfo.Name) } return names, nil } func ListVolumes(dclient *dockerapi.Client, nameFilter string) ([]string, error) { var err error if dclient == nil { socketInfo, err := dockerclient.GetUnixSocketAddr() if err != nil { return nil, err } if socketInfo == nil || socketInfo.Address == "" { return nil, fmt.Errorf("no unix socket found") } dclient, err = dockerapi.NewClient(socketInfo.Address) if err != nil { log.Errorf("dockerutil.ListVolumes(%s): dockerapi.NewClient() error = %v", nameFilter, err) return nil, err } } listOptions := dockerapi.ListVolumesOptions{ Filters: map[string][]string{ "name": {nameFilter}, }, } volumeList, err := dclient.ListVolumes(listOptions) if err != nil { log.Errorf("dockerutil.ListVolumes(%s): dockerapi.ListVolumes() error = %v", nameFilter, err) return nil, err } var names []string for _, volumeInfo := range volumeList { names = append(names, volumeInfo.Name) } return names, nil } //////// const ( CSCreated = "created" CSRestarting = "restarting" CSRunning = "running" CSRemoving = "removing" CSPaused = "paused" CSExited = "exited" CSDead = "dead" ) type BasicContainerProps struct { Name string Names []string //names have "/" as the prefix ID string Image string //'spec' image ref (inspect to get the exact image ID) Created int64 State string Status string //e.g., "Up X seconds" Command string //Entrypoint+Cmd in the shell format } func ListContainers(dclient *dockerapi.Client, nameFilter string, all bool) (map[string]BasicContainerProps, error) { var err error if dclient == nil { socketInfo, err := dockerclient.GetUnixSocketAddr() if err != nil { return nil, err } if socketInfo == nil || socketInfo.Address == "" { return nil, fmt.Errorf("no unix socket found") } dclient, err = dockerapi.NewClient(socketInfo.Address) if err != nil { log.Errorf("dockerutil.ListContainers(%s): dockerapi.NewClient() error = %v", nameFilter, err) return nil, err } } listOptions := dockerapi.ListContainersOptions{ All: all, //if 'all' then also non-running containers are returned } if nameFilter != "" { listOptions.Filters = map[string][]string{ "name": {nameFilter}, } } //note: will need to call 'inspect' to get additional container fields containerList, err := dclient.ListContainers(listOptions) if err != nil { log.Errorf("dockerutil.ListContainers(%s): dockerapi.ListContainers() error = %v", nameFilter, err) return nil, err } containers := map[string]BasicContainerProps{} for _, containerInfo := range containerList { var name string if len(containerInfo.Names) > 0 { name = strings.TrimPrefix(containerInfo.Names[0], "/") } info := BasicContainerProps{ Name: name, Names: containerInfo.Names, ID: containerInfo.ID, Created: containerInfo.Created, Image: containerInfo.Image, State: containerInfo.State, Status: containerInfo.Status, Command: containerInfo.Command, } containers[name] = info } return containers, nil } func GetContainerLogs(dclient *dockerapi.Client, containerID string, rawTerminal bool) (string, string, error) { var err error if dclient == nil { socketInfo, err := dockerclient.GetUnixSocketAddr() if err != nil { return "", "", err } if socketInfo == nil || socketInfo.Address == "" { return "", "", fmt.Errorf("no unix socket found") } dclient, err = dockerapi.NewClient(socketInfo.Address) if err != nil { log.Errorf("dockerutil.GetContainerLogs(%s): dockerapi.NewClient() error = %v", containerID, err) return "", "", err } } var outData bytes.Buffer outw := bufio.NewWriter(&outData) var errData bytes.Buffer errw := bufio.NewWriter(&errData) options := dockerapi.LogsOptions{ Container: containerID, OutputStream: outw, ErrorStream: errw, Stdout: true, Stderr: true, RawTerminal: rawTerminal, } err = dclient.Logs(options) if err != nil { log.Errorf("error getting container logs => %v - %v", containerID, err) return "", "", err } return outData.String(), errData.String(), nil } ================================================ FILE: pkg/docker/instruction/instruction.go ================================================ // Package instruction describes the Docker instruction data model. package instruction import ( "strings" ) // All supported instruction names const ( Add = "add" Arg = "arg" Cmd = "cmd" Copy = "copy" Entrypoint = "entrypoint" Env = "env" Expose = "expose" From = "from" Healthcheck = "healthcheck" Label = "label" Maintainer = "maintainer" Onbuild = "onbuild" Run = "run" Shell = "shell" StopSignal = "stopsignal" User = "user" Volume = "volume" Workdir = "workdir" ) type Field struct { GlobalIndex int `json:"start_index"` StageIndex int `json:"stage_index"` StageID int `json:"stage_id"` RawData string `json:"-"` RawLines []string `json:"raw_lines"` StartLine int `json:"start_line"` EndLine int `json:"end_line"` Name string `json:"name"` Flags []string `json:"flags,omitempty"` Args []string `json:"args,omitempty"` ArgsRaw string `json:"args_raw,omitempty"` IsJSONForm bool `json:"is_json"` IsOnBuild bool `json:"is_onbuild,omitempty"` IsValid bool `json:"is_valid"` Errors []string `json:"errors,omitempty"` } type Format struct { Name string SupportsFlags bool //todo: add a list allowed flags SupportsJSONForm bool SupportsNameValues bool RequiresNameValues bool SupportsSubInst bool IsDepricated bool } // Specs is a map of all available instructions and their format info (by name) var Specs = map[string]Format{ Add: { Name: Add, SupportsFlags: true, SupportsJSONForm: true, }, Arg: { Name: Arg, SupportsNameValues: true, }, Cmd: { Name: Cmd, SupportsJSONForm: true, }, Copy: { Name: Copy, SupportsFlags: true, SupportsJSONForm: true, }, Entrypoint: { Name: Entrypoint, SupportsJSONForm: true, }, Env: { Name: Env, RequiresNameValues: true, }, Expose: { Name: Expose, }, From: { Name: From, SupportsFlags: true, }, Healthcheck: { Name: Healthcheck, SupportsJSONForm: true, }, Label: { Name: Label, RequiresNameValues: true, }, Maintainer: { Name: Maintainer, IsDepricated: true, }, Onbuild: { Name: Label, SupportsSubInst: true, }, Run: { Name: Run, SupportsJSONForm: true, }, Shell: { Name: Shell, SupportsJSONForm: true, }, StopSignal: { Name: StopSignal, }, User: { Name: User, }, Volume: { Name: Volume, SupportsJSONForm: true, }, Workdir: { Name: Workdir, }, } func IsKnown(name string) bool { name = strings.ToLower(name) _, ok := Specs[name] return ok } func SupportsJSONForm() []string { var names []string for _, spec := range Specs { names = append(names, spec.Name) } return names } ================================================ FILE: pkg/docker/linter/check/check.go ================================================ // Package check contains the linter checks package check import ( "github.com/slimtoolkit/slim/pkg/docker/dockerfile/spec" "github.com/slimtoolkit/slim/pkg/docker/dockerignore" "github.com/slimtoolkit/slim/pkg/docker/instruction" ) type Context struct { DockerfilePath string Dockerfile *spec.Dockerfile BuildContextDir string Dockerignore *dockerignore.Matcher } type Options struct { } type Info struct { ID string `json:"id"` Name string `json:"name"` Description string `json:"-"` MainMessage string `json:"-"` //can be a template with a format string MatchMessage string `json:"-"` //can be a template with a format string DetailsURL string `json:"-"` Labels map[string]string `json:"-"` } const ( LabelLevel = "level" LabelScope = "scope" LabelInstruction = "instruction" LabelApp = "app" LabelShell = "shell" ) const ( LevelAny = "any" LevelFatal = "fatal" //parse or other errors that will result in image build failures LevelError = "error" LevelWarn = "warn" LevelInfo = "info" LevelStyle = "style" ) const ( ScopeAll = "all" ScopeDockerfile = "dockerfile" ScopeStage = "stage" ScopeInstruction = "instruction" ScopeDockerignore = "dockerignore" ScopeData = "data" ScopeApp = "app" ScopeShell = "shell" ) //Possible labels: //"level" -> "info", "warn", "error", "style" //"scope" -> "app", "shell", "instruction", "stage", "dockerfile", "all", "dockerignore", "data" //"instruction" -> "list,of,instructions" (negative with !instruction) //"app" -> "list,of,app names" //"shell" -> "general or specific shell name" func (i *Info) Get() *Info { return i } type Result struct { Source *Info `json:"source"` Hit bool `json:"-"` Message string `json:"message,omitempty"` Matches []*Match `json:"matches,omitempty"` DetailsURL string `json:"-"` } type Match struct { Stage *spec.BuildStage `json:"stage,omitempty"` Instruction *instruction.Field `json:"instruction,omitempty"` Message string `json:"message,omitempty"` } type Runner interface { Get() *Info Run(opts *Options, ctx *Context) (*Result, error) } //regex-based (with rules) //type PolicyCheck struct { // Info //} var AllChecks = []Runner{} ================================================ FILE: pkg/docker/linter/check/id10001.go ================================================ // Package check contains the linter checks package check import ( log "github.com/sirupsen/logrus" ) func init() { check := &NoDockerignore{ Info: Info{ ID: "ID.10001", Name: "Missing .dockerignore", Description: "Missing .dockerignore", DetailsURL: "https://lint.dockersl.im/check/ID.10001", MainMessage: "No .dockerignore", Labels: map[string]string{ LabelLevel: LevelWarn, LabelScope: ScopeDockerignore, }, }, } AllChecks = append(AllChecks, check) } type NoDockerignore struct { Info } func (c *NoDockerignore) Run(opts *Options, ctx *Context) (*Result, error) { log.Debugf("linter.check[%s:'%s']", c.ID, c.Name) result := &Result{ Source: &c.Info, } if ctx.Dockerignore == nil { return result, nil } if !ctx.Dockerignore.Exists { result.Hit = true result.Message = c.MainMessage } return result, nil } ================================================ FILE: pkg/docker/linter/check/id10002.go ================================================ // Package check contains the linter checks package check import ( log "github.com/sirupsen/logrus" ) func init() { check := &EmptyDockerignore{ Info: Info{ ID: "ID.10002", Name: "Empty .dockerignore", Description: "Empty .dockerignore", DetailsURL: "https://lint.dockersl.im/check/ID.10002", MainMessage: "No exclude patterns in .dockerignore", Labels: map[string]string{ LabelLevel: LevelWarn, LabelScope: ScopeDockerignore, }, }, } AllChecks = append(AllChecks, check) } type EmptyDockerignore struct { Info } func (c *EmptyDockerignore) Run(opts *Options, ctx *Context) (*Result, error) { log.Debugf("linter.check[%s:'%s']", c.ID, c.Name) result := &Result{ Source: &c.Info, } if ctx.Dockerignore == nil { return result, nil } if ctx.Dockerignore.Exists && len(ctx.Dockerignore.Patterns) == 0 { result.Hit = true result.Message = c.MainMessage } return result, nil } ================================================ FILE: pkg/docker/linter/check/id20000.go ================================================ // Package check contains the linter checks package check import ( "fmt" log "github.com/sirupsen/logrus" ) func init() { check := &InvalidInstruction{ Info: Info{ ID: "ID.20000", Name: "Invalid instruction", Description: "Invalid instruction", DetailsURL: "https://lint.dockersl.im/check/ID.20000", MainMessage: "Invalid instruction in Dockerfile", MatchMessage: "Instruction: start=%d end=%d global_index=%d stage_id=%d stage_index=%d", Labels: map[string]string{ LabelLevel: LevelFatal, LabelScope: ScopeDockerfile, }, }, } AllChecks = append(AllChecks, check) } type InvalidInstruction struct { Info } func (c *InvalidInstruction) Run(opts *Options, ctx *Context) (*Result, error) { log.Debugf("linter.check[%s:'%s']", c.ID, c.Name) result := &Result{ Source: &c.Info, } for _, inst := range ctx.Dockerfile.InvalidInstructions { if !inst.IsValid { if !result.Hit { result.Hit = true result.Message = c.MainMessage } match := &Match{ Instruction: inst, Message: fmt.Sprintf(c.MatchMessage, inst.StartLine, inst.EndLine, inst.GlobalIndex, inst.StageID, inst.StageIndex), } result.Matches = append(result.Matches, match) } } return result, nil } ================================================ FILE: pkg/docker/linter/check/id20001.go ================================================ // Package check contains the linter checks package check import ( log "github.com/sirupsen/logrus" ) func init() { check := &EmptyDockerfile{ Info: Info{ ID: "ID.20001", Name: "Empty Dockerfile", Description: "Empty Dockerfile", DetailsURL: "https://lint.dockersl.im/check/ID.20001", MainMessage: "No instructions in Dockerfile", Labels: map[string]string{ LabelLevel: LevelError, LabelScope: ScopeDockerfile, }, }, } AllChecks = append(AllChecks, check) } type EmptyDockerfile struct { Info } func (c *EmptyDockerfile) Run(opts *Options, ctx *Context) (*Result, error) { log.Debugf("linter.check[%s:'%s']", c.ID, c.Name) result := &Result{ Source: &c.Info, } if len(ctx.Dockerfile.AllInstructions) == 0 { result.Hit = true result.Message = c.MainMessage } return result, nil } ================================================ FILE: pkg/docker/linter/check/id20002.go ================================================ // Package check contains the linter checks package check import ( log "github.com/sirupsen/logrus" ) func init() { check := &NoStages{ Info: Info{ ID: "ID.20002", Name: "No stages", Description: "No stages", DetailsURL: "https://lint.dockersl.im/check/ID.20002", MainMessage: "No stages in Dockerfile", Labels: map[string]string{ LabelLevel: LevelError, LabelScope: ScopeStage, }, }, } AllChecks = append(AllChecks, check) } type NoStages struct { Info } func (c *NoStages) Run(opts *Options, ctx *Context) (*Result, error) { log.Debugf("linter.check[%s:'%s']", c.ID, c.Name) result := &Result{ Source: &c.Info, } if len(ctx.Dockerfile.Stages) == 0 { result.Hit = true result.Message = c.MainMessage } return result, nil } ================================================ FILE: pkg/docker/linter/check/id20003.go ================================================ // Package check contains the linter checks package check import ( "fmt" log "github.com/sirupsen/logrus" ) func init() { check := &EmptyStage{ Info: Info{ ID: "ID.20003", Name: "Empty stage", Description: "Empty stage", DetailsURL: "https://lint.dockersl.im/check/ID.20003", MainMessage: "Empty stage in Dockerfile", MatchMessage: "Stage: index=%d name='%s' start=%d end=%d", Labels: map[string]string{ LabelLevel: LevelError, LabelScope: ScopeStage, }, }, } AllChecks = append(AllChecks, check) } type EmptyStage struct { Info } func (c *EmptyStage) Run(opts *Options, ctx *Context) (*Result, error) { log.Debugf("linter.check[%s:'%s']", c.ID, c.Name) result := &Result{ Source: &c.Info, } for _, stage := range ctx.Dockerfile.Stages { if len(stage.AllInstructions) == 1 { if !result.Hit { result.Hit = true result.Message = c.MainMessage } match := &Match{ Stage: stage, Message: fmt.Sprintf(c.MatchMessage, stage.Index, stage.Name, stage.StartLine, stage.EndLine), } result.Matches = append(result.Matches, match) } } return result, nil } ================================================ FILE: pkg/docker/linter/check/id20004.go ================================================ // Package check contains the linter checks package check import ( "fmt" log "github.com/sirupsen/logrus" ) func init() { check := &NoStageArgs{ Info: Info{ ID: "ID.20004", Name: "No stage arguments", Description: "No stage arguments", DetailsURL: "https://lint.dockersl.im/check/ID.20004", MainMessage: "Stage without arguments in Dockerfile", MatchMessage: "Stage: index=%d start=%d end=%d", Labels: map[string]string{ LabelLevel: LevelFatal, LabelScope: ScopeStage, }, }, } AllChecks = append(AllChecks, check) } type NoStageArgs struct { Info } func (c *NoStageArgs) Run(opts *Options, ctx *Context) (*Result, error) { log.Debugf("linter.check[%s:'%s']", c.ID, c.Name) result := &Result{ Source: &c.Info, } for _, stage := range ctx.Dockerfile.Stages { if stage.FromInstruction == nil { continue } if len(stage.FromInstruction.ArgsRaw) == 0 { if !result.Hit { result.Hit = true result.Message = c.MainMessage } match := &Match{ Stage: stage, Message: fmt.Sprintf(c.MatchMessage, stage.Index, stage.StartLine, stage.EndLine), } result.Matches = append(result.Matches, match) } } return result, nil } ================================================ FILE: pkg/docker/linter/check/id20005.go ================================================ // Package check contains the linter checks package check import ( "fmt" "strings" log "github.com/sirupsen/logrus" ) func init() { check := &InvalidStageArgs{ Info: Info{ ID: "ID.20005", Name: "Invalid stage arguments", Description: "Invalid stage arguments", DetailsURL: "https://lint.dockersl.im/check/ID.20005", MainMessage: "Stage with invalid arguments in Dockerfile", MatchMessage: "Stage: reason='%s' index=%d name='%s' start=%d end=%d", Labels: map[string]string{ LabelLevel: LevelFatal, LabelScope: ScopeStage, }, }, } AllChecks = append(AllChecks, check) } type InvalidStageArgs struct { Info } func (c *InvalidStageArgs) Run(opts *Options, ctx *Context) (*Result, error) { log.Debugf("linter.check[%s:'%s']", c.ID, c.Name) result := &Result{ Source: &c.Info, } for _, stage := range ctx.Dockerfile.Stages { if stage.FromInstruction == nil { continue } if !stage.FromInstruction.IsValid { if !result.Hit { result.Hit = true result.Message = c.MainMessage } match := &Match{ Stage: stage, Message: fmt.Sprintf(c.MatchMessage, "invalid from instruction", stage.Index, stage.Name, stage.StartLine, stage.EndLine), } result.Matches = append(result.Matches, match) } //FROM args are always parsed if len(stage.FromInstruction.Args) == 2 || len(stage.FromInstruction.Args) > 3 { if !result.Hit { result.Hit = true result.Message = c.MainMessage } match := &Match{ Stage: stage, Message: fmt.Sprintf(c.MatchMessage, "incorrect number of arguments", stage.Index, stage.Name, stage.StartLine, stage.EndLine), } result.Matches = append(result.Matches, match) } if len(stage.FromInstruction.Args) == 3 && strings.ToLower(stage.FromInstruction.Args[1]) != "as" { if !result.Hit { result.Hit = true result.Message = c.MainMessage } match := &Match{ Stage: stage, Message: fmt.Sprintf(c.MatchMessage, "malformed arguments", stage.Index, stage.Name, stage.StartLine, stage.EndLine), } result.Matches = append(result.Matches, match) } } for name, stageByName := range ctx.Dockerfile.StagesByName { for _, stageByIdx := range ctx.Dockerfile.Stages { if stageByIdx.Name != "" { if stageByIdx.Name == name && stageByIdx != stageByName { if !result.Hit { result.Hit = true result.Message = c.MainMessage } match := &Match{ Stage: stageByIdx, Message: fmt.Sprintf(c.MatchMessage, "duplicate name", stageByIdx.Index, stageByIdx.Name, stageByIdx.StartLine, stageByIdx.EndLine), } result.Matches = append(result.Matches, match) } } } } return result, nil } ================================================ FILE: pkg/docker/linter/check/id20006.go ================================================ // Package check contains the linter checks package check import ( "fmt" "strings" log "github.com/sirupsen/logrus" ) func init() { check := &StageFromLatest{ Info: Info{ ID: "ID.20006", Name: "Stage from latest tag", Description: "Stage from latest tag", DetailsURL: "https://lint.dockersl.im/check/ID.20006", MainMessage: "Stage from latest tag in Dockerfile", MatchMessage: "Stage: index=%d name='%s' start=%d end=%d parent='%s'", Labels: map[string]string{ LabelLevel: LevelError, LabelScope: ScopeStage, }, }, } AllChecks = append(AllChecks, check) } type StageFromLatest struct { Info } func (c *StageFromLatest) Run(opts *Options, ctx *Context) (*Result, error) { log.Debugf("linter.check[%s:'%s']", c.ID, c.Name) result := &Result{ Source: &c.Info, } for _, stage := range ctx.Dockerfile.Stages { if stage.Parent.Name != "" { if (stage.Parent.Tag == "" || strings.ToLower(stage.Parent.Tag) == "latest") && stage.Parent.Digest == "" { if !result.Hit { result.Hit = true result.Message = c.MainMessage } match := &Match{ Stage: stage, Message: fmt.Sprintf(c.MatchMessage, stage.Index, stage.Name, stage.StartLine, stage.EndLine, stage.Parent.Name), } result.Matches = append(result.Matches, match) } } } return result, nil } ================================================ FILE: pkg/docker/linter/check/id20007.go ================================================ // Package check contains the linter checks package check import ( "fmt" log "github.com/sirupsen/logrus" ) func init() { check := &UnknownInstruction{ Info: Info{ ID: "ID.20007", Name: "Unknown instruction", Description: "Unknown instruction", DetailsURL: "https://lint.dockersl.im/check/ID.20007", MainMessage: "Unknown instruction in Dockerfile", MatchMessage: "Instruction: start=%d end=%d name='%s' global_index=%d stage_id=%d stage_index=%d", Labels: map[string]string{ LabelLevel: LevelInfo, LabelScope: ScopeInstruction, }, }, } AllChecks = append(AllChecks, check) } type UnknownInstruction struct { Info } func (c *UnknownInstruction) Run(opts *Options, ctx *Context) (*Result, error) { log.Debugf("linter.check[%s:'%s']", c.ID, c.Name) result := &Result{ Source: &c.Info, } if len(ctx.Dockerfile.UnknownInstructions) > 0 { result.Hit = true result.Message = c.MainMessage for _, inst := range ctx.Dockerfile.UnknownInstructions { match := &Match{ Instruction: inst, Message: fmt.Sprintf(c.MatchMessage, inst.StartLine, inst.EndLine, inst.Name, inst.GlobalIndex, inst.StageID, inst.StageIndex), } result.Matches = append(result.Matches, match) } } return result, nil } ================================================ FILE: pkg/docker/linter/check/id20008.go ================================================ // Package check contains the linter checks package check import ( "fmt" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/docker/instruction" ) func init() { check := &DeprecatedInstruction{ Info: Info{ ID: "ID.20008", Name: "Deprecated instruction", Description: "Deprecated instruction", DetailsURL: "https://lint.dockersl.im/check/ID.20008", MainMessage: "Deprecated instruction", MatchMessage: "Instruction: start=%d end=%d name='%s' global_index=%d", Labels: map[string]string{ LabelLevel: LevelInfo, LabelScope: ScopeDockerfile, }, }, } AllChecks = append(AllChecks, check) } type DeprecatedInstruction struct { Info } func (c *DeprecatedInstruction) Run(opts *Options, ctx *Context) (*Result, error) { log.Debugf("linter.check[%s:'%s']", c.ID, c.Name) result := &Result{ Source: &c.Info, } if instructions, ok := ctx.Dockerfile.InstructionsByType[instruction.Maintainer]; ok { result.Hit = true result.Message = c.MainMessage for _, inst := range instructions { match := &Match{ Instruction: inst, Message: fmt.Sprintf(c.MatchMessage, inst.StartLine, inst.EndLine, inst.Name, inst.GlobalIndex), } result.Matches = append(result.Matches, match) } } return result, nil } ================================================ FILE: pkg/docker/linter/check/id20009.go ================================================ // Package check contains the linter checks package check import ( "fmt" log "github.com/sirupsen/logrus" ) func init() { check := &StagelessInstruction{ Info: Info{ ID: "ID.20009", Name: "Stageless instruction", Description: "Stageless instruction", DetailsURL: "https://lint.dockersl.im/check/ID.20009", MainMessage: "Non-arg instruction outside of a Dockerfile stage", MatchMessage: "Instruction: start=%d end=%d name='%s' global_index=%d", Labels: map[string]string{ LabelLevel: LevelWarn, LabelScope: ScopeDockerfile, }, }, } AllChecks = append(AllChecks, check) } type StagelessInstruction struct { Info } func (c *StagelessInstruction) Run(opts *Options, ctx *Context) (*Result, error) { log.Debugf("linter.check[%s:'%s']", c.ID, c.Name) result := &Result{ Source: &c.Info, } if len(ctx.Dockerfile.StagelessInstructions) > 0 { result.Hit = true result.Message = c.MainMessage for _, inst := range ctx.Dockerfile.StagelessInstructions { match := &Match{ Instruction: inst, Message: fmt.Sprintf(c.MatchMessage, inst.StartLine, inst.EndLine, inst.Name, inst.GlobalIndex), } result.Matches = append(result.Matches, match) } } return result, nil } ================================================ FILE: pkg/docker/linter/check/id20010.go ================================================ // Package check contains the linter checks package check import ( "fmt" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/docker/instruction" ) func init() { check := &MultipleEntrypointInstructions{ Info: Info{ ID: "ID.20010", Name: "Multiple ENTRYPOINT instructions", Description: "Multiple ENTRYPOINT instructions", DetailsURL: "https://lint.dockersl.im/check/ID.20010", MainMessage: "Multiple ENTRYPOINT instructions in stage", MatchMessage: "Instruction: start=%d end=%d global_index=%d stage_id=%d stage_index=%d", Labels: map[string]string{ LabelLevel: LevelError, LabelScope: ScopeStage, }, }, } AllChecks = append(AllChecks, check) } type MultipleEntrypointInstructions struct { Info } func (c *MultipleEntrypointInstructions) Run(opts *Options, ctx *Context) (*Result, error) { log.Debugf("linter.check[%s:'%s']", c.ID, c.Name) result := &Result{ Source: &c.Info, } for _, stage := range ctx.Dockerfile.Stages { if instructions, ok := stage.CurrentInstructionsByType[instruction.Entrypoint]; ok { if len(instructions) > 1 { if !result.Hit { result.Hit = true result.Message = c.MainMessage } for _, inst := range instructions { match := &Match{ Stage: stage, Instruction: inst, Message: fmt.Sprintf(c.MatchMessage, inst.StartLine, inst.EndLine, inst.GlobalIndex, inst.StageID, inst.StageIndex), } result.Matches = append(result.Matches, match) } } } } return result, nil } ================================================ FILE: pkg/docker/linter/check/id20011.go ================================================ // Package check contains the linter checks package check import ( "fmt" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/docker/instruction" ) func init() { check := &MultipleCmdInstructions{ Info: Info{ ID: "ID.20011", Name: "Multiple CMD instructions", Description: "Multiple CMD instructions", DetailsURL: "https://lint.dockersl.im/check/ID.20011", MainMessage: "Multiple CMD instructions in stage", MatchMessage: "Instruction: start=%d end=%d global_index=%d stage_id=%d stage_index=%d", Labels: map[string]string{ LabelLevel: LevelError, LabelScope: ScopeStage, }, }, } AllChecks = append(AllChecks, check) } type MultipleCmdInstructions struct { Info } func (c *MultipleCmdInstructions) Run(opts *Options, ctx *Context) (*Result, error) { log.Debugf("linter.check[%s:'%s']", c.ID, c.Name) result := &Result{ Source: &c.Info, } for _, stage := range ctx.Dockerfile.Stages { if instructions, ok := stage.CurrentInstructionsByType[instruction.Cmd]; ok { if len(instructions) > 1 { if !result.Hit { result.Hit = true result.Message = c.MainMessage } for _, inst := range instructions { match := &Match{ Stage: stage, Instruction: inst, Message: fmt.Sprintf(c.MatchMessage, inst.StartLine, inst.EndLine, inst.GlobalIndex, inst.StageID, inst.StageIndex), } result.Matches = append(result.Matches, match) } } } } return result, nil } ================================================ FILE: pkg/docker/linter/check/id20012.go ================================================ // Package check contains the linter checks package check import ( "fmt" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/docker/instruction" ) func init() { check := &EntrypointCmdShellForm{ Info: Info{ ID: "ID.20012", Name: "ENTRYPOINT or CMD in shell form", Description: "ENTRYPOINT or CMD should use the exec/JSON form", DetailsURL: "https://lint.dockersl.im/check/ID.20012", MainMessage: "Instruction in shell form", MatchMessage: "Instruction: start=%d end=%d name='%s' global_index=%d stage_id=%d stage_index=%d", Labels: map[string]string{ LabelLevel: LevelWarn, LabelScope: ScopeStage, }, }, Names: []string{ instruction.Entrypoint, instruction.Cmd, }, } AllChecks = append(AllChecks, check) } type EntrypointCmdShellForm struct { Info Names []string } func (c *EntrypointCmdShellForm) Run(opts *Options, ctx *Context) (*Result, error) { log.Debugf("linter.check[%s:'%s']", c.ID, c.Name) result := &Result{ Source: &c.Info, } for _, stage := range ctx.Dockerfile.Stages { for _, name := range c.Names { if instructions, ok := stage.CurrentInstructionsByType[name]; ok { for _, inst := range instructions { if !inst.IsJSONForm { if !result.Hit { result.Hit = true result.Message = c.MainMessage } for _, inst := range instructions { match := &Match{ Stage: stage, Instruction: inst, Message: fmt.Sprintf(c.MatchMessage, inst.StartLine, inst.EndLine, inst.Name, inst.GlobalIndex, inst.StageID, inst.StageIndex), } result.Matches = append(result.Matches, match) } } } } } } return result, nil } ================================================ FILE: pkg/docker/linter/check/id20013.go ================================================ // Package check contains the linter checks package check import ( "fmt" "strings" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/docker/instruction" ) func init() { check := &MalformedInstExecForm{ Info: Info{ ID: "ID.20013", Name: "Malformed instruction in exec/JSON form", Description: "Malformed instruction in exec/JSON form", DetailsURL: "https://lint.dockersl.im/check/ID.20013", MainMessage: "Malformed instruction in exec/JSON form", MatchMessage: "Instruction: start=%d end=%d name='%s' global_index=%d stage_id=%d stage_index=%d", Labels: map[string]string{ LabelLevel: LevelError, LabelScope: ScopeStage, }, }, } AllChecks = append(AllChecks, check) } type MalformedInstExecForm struct { Info } func (c *MalformedInstExecForm) Run(opts *Options, ctx *Context) (*Result, error) { log.Debugf("linter.check[%s:'%s']", c.ID, c.Name) result := &Result{ Source: &c.Info, } for _, stage := range ctx.Dockerfile.Stages { for _, name := range instruction.SupportsJSONForm() { if instructions, ok := stage.CurrentInstructionsByType[name]; ok { for _, inst := range instructions { if inst.IsJSONForm { continue } if strings.HasPrefix(inst.ArgsRaw, "[") || strings.HasSuffix(inst.ArgsRaw, "]") { if !result.Hit { result.Hit = true result.Message = c.MainMessage } for _, inst := range instructions { match := &Match{ Stage: stage, Instruction: inst, Message: fmt.Sprintf(c.MatchMessage, inst.StartLine, inst.EndLine, inst.Name, inst.GlobalIndex, inst.StageID, inst.StageIndex), } result.Matches = append(result.Matches, match) } } } } } } return result, nil } ================================================ FILE: pkg/docker/linter/check/id20014.go ================================================ // Package check contains the linter checks package check import ( "fmt" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/docker/instruction" ) func init() { //related to ID.20016 (but fatal because it'll result in failed container builds) check := &NoWorkdirPath{ Info: Info{ ID: "ID.20014", Name: "No WORKDIR path", Description: "No WORKDIR path", DetailsURL: "https://lint.dockersl.im/check/ID.20014", MainMessage: "No WORKDIR path in stage", MatchMessage: "Instruction: start=%d end=%d global_index=%d stage_id=%d stage_index=%d", Labels: map[string]string{ LabelLevel: LevelFatal, LabelScope: ScopeStage, }, }, } AllChecks = append(AllChecks, check) } type NoWorkdirPath struct { Info } func (c *NoWorkdirPath) Run(opts *Options, ctx *Context) (*Result, error) { log.Debugf("linter.check[%s:'%s']", c.ID, c.Name) result := &Result{ Source: &c.Info, } for _, stage := range ctx.Dockerfile.Stages { if instructions, ok := stage.CurrentInstructionsByType[instruction.Workdir]; ok { for _, inst := range instructions { if len(inst.ArgsRaw) == 0 { if !result.Hit { result.Hit = true result.Message = c.MainMessage } for _, inst := range instructions { match := &Match{ Stage: stage, Instruction: inst, Message: fmt.Sprintf(c.MatchMessage, inst.StartLine, inst.EndLine, inst.GlobalIndex, inst.StageID, inst.StageIndex), } result.Matches = append(result.Matches, match) } } } } } return result, nil } ================================================ FILE: pkg/docker/linter/check/id20015.go ================================================ // Package check contains the linter checks package check import ( "fmt" "strings" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/docker/instruction" ) func init() { check := &RelativeWorkdir{ Info: Info{ ID: "ID.20015", Name: "Relative WORKDIR path", Description: "Relative WORKDIR path", DetailsURL: "https://lint.dockersl.im/check/ID.20015", MainMessage: "Relative WORKDIR path in stage", MatchMessage: "Instruction: start=%d end=%d global_index=%d stage_id=%d stage_index=%d", Labels: map[string]string{ LabelLevel: LevelWarn, LabelScope: ScopeStage, }, }, } AllChecks = append(AllChecks, check) } type RelativeWorkdir struct { Info } func (c *RelativeWorkdir) Run(opts *Options, ctx *Context) (*Result, error) { log.Debugf("linter.check[%s:'%s']", c.ID, c.Name) result := &Result{ Source: &c.Info, } for _, stage := range ctx.Dockerfile.Stages { if instructions, ok := stage.CurrentInstructionsByType[instruction.Workdir]; ok { for _, inst := range instructions { if len(inst.ArgsRaw) > 0 { workdirPath := inst.ArgsRaw if strings.Contains(workdirPath, "$") { workdirPath = expandEnvVars(workdirPath, stage.EnvVars) } if strings.HasPrefix(workdirPath, "/") { continue } if !result.Hit { result.Hit = true result.Message = c.MainMessage } for _, inst := range instructions { match := &Match{ Stage: stage, Instruction: inst, Message: fmt.Sprintf(c.MatchMessage, inst.StartLine, inst.EndLine, inst.GlobalIndex, inst.StageID, inst.StageIndex), } result.Matches = append(result.Matches, match) } } } } } return result, nil } func expandEnvVars(data string, vars map[string]string) string { //todo: do it the right way if strings.HasPrefix(data, "$") { name := data[1:] if val, ok := vars[name]; ok { return val } } return data } ================================================ FILE: pkg/docker/linter/check/id20016.go ================================================ // Package check contains the linter checks package check import ( "fmt" log "github.com/sirupsen/logrus" ) func init() { check := &NoEnvArgs{ Info: Info{ ID: "ID.20016", Name: "No instruction args", Description: "No instruction args", DetailsURL: "https://lint.dockersl.im/check/ID.20016", MainMessage: "No instruction args in stage", MatchMessage: "Instruction: start=%d end=%d name='%s' global_index=%d stage_id=%d stage_index=%d", Labels: map[string]string{ LabelLevel: LevelError, LabelScope: ScopeStage, }, }, } AllChecks = append(AllChecks, check) } type NoEnvArgs struct { Info } func (c *NoEnvArgs) Run(opts *Options, ctx *Context) (*Result, error) { log.Debugf("linter.check[%s:'%s']", c.ID, c.Name) result := &Result{ Source: &c.Info, } for _, stage := range ctx.Dockerfile.Stages { for _, inst := range stage.CurrentInstructions { if len(inst.ArgsRaw) == 0 { if !result.Hit { result.Hit = true result.Message = c.MainMessage } match := &Match{ Stage: stage, Instruction: inst, Message: fmt.Sprintf(c.MatchMessage, inst.StartLine, inst.EndLine, inst.Name, inst.GlobalIndex, inst.StageID, inst.StageIndex), } result.Matches = append(result.Matches, match) } } } return result, nil } ================================================ FILE: pkg/docker/linter/check/id20017.go ================================================ // Package check contains the linter checks package check import ( "fmt" "strings" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/docker/instruction" ) func init() { check := &LastUserRoot{ Info: Info{ ID: "ID.20017", Name: "Last USER instruction with root", Description: "Last USER instruction with root", DetailsURL: "https://lint.dockersl.im/check/ID.20017", MainMessage: "Last USER instruction with root", MatchMessage: "Instruction: start=%d end=%d global_index=%d stage_id=%d stage_index=%d", Labels: map[string]string{ LabelLevel: LevelInfo, LabelScope: ScopeStage, }, }, } AllChecks = append(AllChecks, check) } type LastUserRoot struct { Info } func (c *LastUserRoot) Run(opts *Options, ctx *Context) (*Result, error) { log.Debugf("linter.check[%s:'%s']", c.ID, c.Name) result := &Result{ Source: &c.Info, } lastStageIdx := len(ctx.Dockerfile.Stages) - 1 if lastStageIdx > -1 { stage := ctx.Dockerfile.Stages[lastStageIdx] if instructions, ok := stage.CurrentInstructionsByType[instruction.User]; ok { lastUserIdx := len(instructions) - 1 if lastUserIdx > -1 { inst := instructions[lastUserIdx] argsRaw := strings.ToLower(inst.ArgsRaw) if argsRaw == "0" || argsRaw == "root" || strings.HasPrefix(argsRaw, "0:") || strings.HasPrefix(argsRaw, "root:") { result.Hit = true result.Message = c.MainMessage match := &Match{ Stage: stage, Instruction: inst, Message: fmt.Sprintf(c.MatchMessage, inst.StartLine, inst.EndLine, inst.GlobalIndex, inst.StageID, inst.StageIndex), } result.Matches = append(result.Matches, match) } } } //if no explicit USER instruction (parent's USER is inherited) } return result, nil } ================================================ FILE: pkg/docker/linter/check/id20018.go ================================================ // Package check contains the linter checks package check import ( "fmt" "strings" "github.com/google/shlex" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/docker/instruction" ) func init() { check := &PyPipInstallLatest{ Info: Info{ ID: "ID.20018", Name: "Python Pip installs latest package version", Description: "Python Pip installs latest package version", DetailsURL: "https://lint.dockersl.im/check/ID.20018", MainMessage: "Python Pip installs latest package version", MatchMessage: "Instruction: start=%d end=%d global_index=%d stage_id=%d stage_index=%d package=%s", Labels: map[string]string{ LabelLevel: LevelWarn, LabelScope: ScopeStage, }, }, } AllChecks = append(AllChecks, check) } type PyPipInstallLatest struct { Info } func (c *PyPipInstallLatest) Run(opts *Options, ctx *Context) (*Result, error) { log.Debugf("linter.check[%s:'%s']", c.ID, c.Name) result := &Result{ Source: &c.Info, } for _, stage := range ctx.Dockerfile.Stages { if instructions, ok := stage.CurrentInstructionsByType[instruction.Run]; ok { for _, inst := range instructions { if len(inst.ArgsRaw) == 0 { continue } var args []string if inst.IsJSONForm { args = inst.Args } else { var err error args, err = shlex.Split(inst.ArgsRaw) if err != nil { log.Fatal(err) } } if len(args) > 1 { cmdName := strings.ToLower(args[0]) if strings.HasPrefix(cmdName, "python") && len(args) > 3 && args[1] == "-m" { cmdName = strings.ToLower(args[2]) args = args[2:] //handle 'pythonX -m pip install pkgname' } //pip install -x 'pkgname>=x.y.z,' || c == '!' }) pkgName := pkgInfoParts[0] if pkgName[0] == '\'' || pkgName[0] == '"' { pkgName = pkgName[1:] } if strings.Contains(args[i], "==") || strings.Contains(args[i], "<=") || strings.Contains(args[i], "<") { continue } if !result.Hit { result.Hit = true result.Message = c.MainMessage } match := &Match{ Stage: stage, Instruction: inst, Message: fmt.Sprintf(c.MatchMessage, inst.StartLine, inst.EndLine, inst.GlobalIndex, inst.StageID, inst.StageIndex, pkgInfoParts[0]), } result.Matches = append(result.Matches, match) } } } } } } return result, nil } ================================================ FILE: pkg/docker/linter/check/id20019.go ================================================ // Package check contains the linter checks package check import ( "fmt" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/docker/instruction" ) func init() { check := &UnnecessaryLayer{ Info: Info{ ID: "ID.20019", Name: "Unnecessary Layer", Description: "RUN instruction will result in unnecessary layer (combine command with previous RUN instruciton)", DetailsURL: "https://lint.dockersl.im/check/ID.20019", MainMessage: "RUN instruction will result in unnecessary layer", MatchMessage: "Instruction: start=%d end=%d global_index=%d stage_id=%d stage_index=%d prev_start=%d prev_end=%d prev_global_index=%d prev_stage_index=%d", Labels: map[string]string{ LabelLevel: LevelWarn, LabelScope: ScopeStage, }, }, } AllChecks = append(AllChecks, check) } type UnnecessaryLayer struct { Info } func (c *UnnecessaryLayer) Run(opts *Options, ctx *Context) (*Result, error) { log.Debugf("linter.check[%s:'%s']", c.ID, c.Name) result := &Result{ Source: &c.Info, } for _, stage := range ctx.Dockerfile.Stages { var prevInst *instruction.Field for _, inst := range stage.CurrentInstructions { if inst.Name == instruction.Run && prevInst != nil && prevInst.Name == instruction.Run { //very primitive unnecessary layer that only checks the previous RUN instruction //should have a separate check with more advanced unnecessary layer detection if !result.Hit { result.Hit = true result.Message = c.MainMessage } match := &Match{ Stage: stage, Instruction: inst, Message: fmt.Sprintf(c.MatchMessage, inst.StartLine, inst.EndLine, inst.GlobalIndex, inst.StageID, inst.StageIndex, prevInst.StartLine, prevInst.EndLine, prevInst.GlobalIndex, prevInst.StageIndex), } result.Matches = append(result.Matches, match) } prevInst = inst } } return result, nil } ================================================ FILE: pkg/docker/linter/check/id20020.go ================================================ // Package check contains the linter checks package check import ( "fmt" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/docker/instruction" ) const ( maxLayerCount = 100 ) func init() { check := &TooManyLayers{ Info: Info{ ID: "ID.20020", Name: "Too Many Layers", Description: "Too many image layers", DetailsURL: "https://lint.dockersl.im/check/ID.20020", MainMessage: "Too many image layers in stage: count=%d", Labels: map[string]string{ LabelLevel: LevelWarn, LabelScope: ScopeStage, }, }, } AllChecks = append(AllChecks, check) } type TooManyLayers struct { Info } func (c *TooManyLayers) Run(opts *Options, ctx *Context) (*Result, error) { log.Debugf("linter.check[%s:'%s']", c.ID, c.Name) result := &Result{ Source: &c.Info, } //NOTES: //A regular Dockerfile lint check won't be very accurate because: //* the check doesn't know how many layers exist in the base image //* it doesn't know the storage driver and its exact limit //* not all RUN instructions generate new layers (e.g., "RUN python -V") //* not all WORKDIR instructions generate new layers (new layer only when dir doesn't exist) for _, stage := range ctx.Dockerfile.Stages { layerCount := 0 for _, inst := range stage.CurrentInstructions { switch inst.Name { case instruction.Run, instruction.Workdir, instruction.Copy, instruction.Add: layerCount++ } } if layerCount > maxLayerCount { result.Hit = true result.Message = fmt.Sprintf(c.MainMessage, layerCount) } } return result, nil } ================================================ FILE: pkg/docker/linter/check/id20021.go ================================================ // Package check contains the linter checks package check import ( "fmt" "strings" "github.com/google/shlex" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/docker/instruction" ) func init() { check := &SeparateRemove{ Info: Info{ ID: "ID.20021", Name: "Separate rm Command", Description: "Separate rm command only hides data", DetailsURL: "https://lint.dockersl.im/check/ID.20021", MainMessage: "Separate rm command only hides data in the image (also creates a new layer)", MatchMessage: "Instruction: start=%d end=%d global_index=%d stage_id=%d stage_index=%d", Labels: map[string]string{ LabelLevel: LevelWarn, LabelScope: ScopeStage, }, }, } AllChecks = append(AllChecks, check) } type SeparateRemove struct { Info } func (c *SeparateRemove) Run(opts *Options, ctx *Context) (*Result, error) { log.Debugf("linter.check[%s:'%s']", c.ID, c.Name) result := &Result{ Source: &c.Info, } for _, stage := range ctx.Dockerfile.Stages { if instructions, ok := stage.CurrentInstructionsByType[instruction.Run]; ok { for _, inst := range instructions { if len(inst.ArgsRaw) == 0 { continue } var args []string if inst.IsJSONForm { args = inst.Args } else { var err error args, err = shlex.Split(inst.ArgsRaw) if err != nil { log.Fatal(err) } } if len(args) > 1 { if strings.ToLower(args[0]) == "rm" { //can also check/track if it's the only RUN instruction if !result.Hit { result.Hit = true result.Message = c.MainMessage } match := &Match{ Stage: stage, Instruction: inst, Message: fmt.Sprintf(c.MatchMessage, inst.StartLine, inst.EndLine, inst.GlobalIndex, inst.StageID, inst.StageIndex), } result.Matches = append(result.Matches, match) } } } } } return result, nil } ================================================ FILE: pkg/docker/linter/check/id20022.go ================================================ // Package check contains the linter checks package check import ( "fmt" "strings" "github.com/google/shlex" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/docker/instruction" ) func init() { check := &BadContainerCommands{ Info: Info{ ID: "ID.20022", Name: "Bad Container Commands", Description: "Bad container commands", DetailsURL: "https://lint.dockersl.im/check/ID.20022", MainMessage: "Bad container commands", MatchMessage: "Instruction: start=%d end=%d global_index=%d stage_id=%d stage_index=%d cmd=%s", Labels: map[string]string{ LabelLevel: LevelInfo, LabelScope: ScopeStage, }, }, Names: []string{ "vim", "shutdown", "mount", "kill", "top", "free", "service", "ssh", "ps", }, } AllChecks = append(AllChecks, check) } type BadContainerCommands struct { Info Names []string } func (check *BadContainerCommands) Run(opts *Options, ctx *Context) (*Result, error) { log.Debugf("linter.check[%s:'%s']", check.ID, check.Name) result := &Result{ Source: &check.Info, } for _, stage := range ctx.Dockerfile.Stages { if instructions, ok := stage.CurrentInstructionsByType[instruction.Run]; ok { for _, inst := range instructions { if len(inst.ArgsRaw) == 0 { continue } var args []string if inst.IsJSONForm { args = inst.Args } else { var err error args, err = shlex.Split(inst.ArgsRaw) if err != nil { log.Fatal(err) } } if len(args) > 1 { for _, cmdName := range check.Names { //TODO: //need to make it work with multiple commands in instruction //need better shell parsing first if strings.ToLower(args[0]) == cmdName { if !result.Hit { result.Hit = true result.Message = check.MainMessage } match := &Match{ Stage: stage, Instruction: inst, Message: fmt.Sprintf(check.MatchMessage, inst.StartLine, inst.EndLine, inst.GlobalIndex, inst.StageID, inst.StageIndex, cmdName), } result.Matches = append(result.Matches, match) } } } } } } return result, nil } ================================================ FILE: pkg/docker/linter/linter.go ================================================ // Package linter implements a Dockerfile linter package linter import ( "errors" "sync" "time" "github.com/slimtoolkit/slim/pkg/docker/dockerfile/parser" "github.com/slimtoolkit/slim/pkg/docker/dockerfile/spec" "github.com/slimtoolkit/slim/pkg/docker/dockerignore" "github.com/slimtoolkit/slim/pkg/docker/linter/check" log "github.com/sirupsen/logrus" ) var ( ErrBadParams = errors.New("bad params") ) const ( DockerfileTargetType = "dockerfile" ImageTargetType = "image" ) //TODO: //* support incremental, partial and instruction level linting //* support linting from string type Options struct { DockerfilePath string Dockerfile *spec.Dockerfile SkipBuildContext bool BuildContextDir string SkipDockerignore bool //to disable .dockerignore parsing Dockerignore *dockerignore.Matcher Selector CheckSelector Config map[string]*check.Options } type CheckContext struct { DockerfilePath string Dockerfile *spec.Dockerfile BuildContextDir string Dockerignore *dockerignore.Matcher } type CheckSelector struct { IncludeCheckLabels map[string]string IncludeCheckIDs map[string]struct{} ExcludeCheckLabels map[string]string ExcludeCheckIDs map[string]struct{} } const ( StatusUnknown = "unknown" StatusRunning = "running" StatusComplete = "complete" StatusTimedOut = "timeout" StatusFailed = "failed" ) // TODO: add report name, report time, etc type Report struct { Status string BuildContextDir string Dockerfile *spec.Dockerfile Dockerignore *dockerignore.Matcher Hits map[string]*check.Result NoHits map[string]*check.Result Errors map[string]error } func NewReport() *Report { return &Report{ Status: StatusUnknown, Hits: map[string]*check.Result{}, NoHits: map[string]*check.Result{}, Errors: map[string]error{}, } } type CheckState struct { Check check.Runner Options *check.Options Context *check.Context Result *check.Result Error error } func Execute(options Options) (*Report, error) { df := options.Dockerfile if df == nil { if options.DockerfilePath == "" { return nil, ErrBadParams } var err error df, err = parser.FromFile(options.DockerfilePath) if err != nil { return nil, err } } var buildContextDir string if !options.SkipBuildContext { if df != nil { buildContextDir = df.Location } if options.BuildContextDir != "" { buildContextDir = options.BuildContextDir } } di := options.Dockerignore if di == nil && !options.SkipDockerignore { var err error di, err = dockerignore.Load(buildContextDir) if err != nil { return nil, err } } report := NewReport() report.BuildContextDir = options.BuildContextDir report.Dockerfile = df report.Dockerignore = di var selectedChecks []check.Runner for _, check := range check.AllChecks { info := check.Get() if len(options.Selector.IncludeCheckIDs) > 0 { if _, ok := options.Selector.IncludeCheckIDs[info.ID]; ok { selectedChecks = append(selectedChecks, check) log.Debugf("linter.Execute: selected check - id=%v (IncludeCheckIDs)", info.ID) } continue } if len(options.Selector.IncludeCheckLabels) > 0 { for k, v := range info.Labels { if inval := options.Selector.IncludeCheckLabels[k]; inval == v { if len(options.Selector.ExcludeCheckIDs) == 0 { selectedChecks = append(selectedChecks, check) log.Debugf("linter.Execute: selected check - id=%v label=%v:%v (IncludeCheckLabels)", info.ID, k, v) } else if _, ok := options.Selector.ExcludeCheckIDs[info.ID]; !ok { selectedChecks = append(selectedChecks, check) log.Debugf("linter.Execute: selected check - id=%v label=%v:%v (IncludeCheckLabels/ExcludeCheckIDs)", info.ID, k, v) } continue } } continue } if len(options.Selector.ExcludeCheckLabels) > 0 { for k, v := range info.Labels { if inval := options.Selector.ExcludeCheckLabels[k]; inval == v { continue } } } if len(options.Selector.ExcludeCheckIDs) > 0 { if _, ok := options.Selector.ExcludeCheckIDs[info.ID]; ok { continue } } selectedChecks = append(selectedChecks, check) log.Debugf("linter.Execute: selected check - id=%v", info.ID) } if len(selectedChecks) == 0 { report.Status = StatusComplete return report, nil } report.Status = StatusRunning checkContext := &check.Context{ DockerfilePath: options.DockerfilePath, Dockerfile: df, BuildContextDir: options.BuildContextDir, Dockerignore: di, } stateCh := make(chan *CheckState, len(selectedChecks)) var workers sync.WaitGroup workers.Add(len(selectedChecks)) for _, c := range selectedChecks { go func(c check.Runner) { defer workers.Done() info := c.Get() var checkOptions *check.Options if len(options.Config) > 0 { checkOptions = options.Config[info.ID] } state := &CheckState{ Check: c, Options: checkOptions, Context: checkContext, } result, err := c.Run(checkOptions, checkContext) state.Result = result state.Error = err stateCh <- state }(c) } go func() { workers.Wait() close(stateCh) }() timeout := time.After(120 * time.Second) done: for { select { case checkState, ok := <-stateCh: if !ok { report.Status = StatusComplete break done } if checkState != nil { info := checkState.Check.Get() if checkState.Error != nil { report.Errors[info.ID] = checkState.Error } else { if checkState.Result.Hit { report.Hits[info.ID] = checkState.Result } else { report.NoHits[info.ID] = checkState.Result } } } case <-timeout: report.Status = StatusTimedOut break done } } return report, nil } func ListChecks() []*check.Info { var list []*check.Info for _, check := range check.AllChecks { info := check.Get() list = append(list, info) } return list } ================================================ FILE: pkg/errors/errors.go ================================================ package errors import ( "fmt" "runtime" ) type SensorError struct { Op string `json:"op"` Kind string `json:"kind"` Next *SensorError `json:"next,omitempty"` Wrapped *WrappedError `json:"wrapped,omitempty"` } type WrappedError struct { Type string `json:"type"` Info string `json:"info"` File string `json:"file"` Line int `json:"line"` } func (e *SensorError) Error() string { errStr := "" if e.Next != nil { errStr = fmt.Sprintf(",Next:%s", e.Next.Error()) } if e.Wrapped != nil { if errStr == "" { errStr = fmt.Sprintf(",Wrapped:{Type=%s,Info=%s,Line:%d,File:%s}", e.Wrapped.Type, e.Wrapped.Info, e.Wrapped.Line, e.Wrapped.File) } else { errStr = fmt.Sprintf("%s,Wrapped:{Type=%s,Info=%s,Line:%d,File:%s}", errStr, e.Wrapped.Type, e.Wrapped.Info, e.Wrapped.Line, e.Wrapped.File) } } return fmt.Sprintf("SensorError{Op:%s,Kind:%s%s}", e.Op, e.Kind, errStr) } func SE(op string, kind string, err error) *SensorError { e := &SensorError{ Op: op, Kind: kind, } if next, ok := err.(*SensorError); ok { e.Next = next } else { e.Wrapped = &WrappedError{ Type: fmt.Sprintf("%T", err), Info: err.Error(), } if _, file, line, ok := runtime.Caller(1); ok { e.Wrapped.File = file e.Wrapped.Line = line } } return e } func Drain(ch <-chan error) (arr []error) { for { select { case e := <-ch: arr = append(arr, e) default: return arr } } } ================================================ FILE: pkg/imagebuilder/imagebuilder.go ================================================ package imagebuilder import ( "fmt" "os" "strings" "time" "github.com/slimtoolkit/slim/pkg/docker/instruction" ) // ImageConfig describes the container image configurations (aka ConfigFile or V1Image/Image in other libraries) // Fields (ordered according to spec): // * https://github.com/opencontainers/image-spec/blob/main/config.md#properties // * https://github.com/moby/moby/blob/e1c92184f08153456ecbf5e302a851afd6f28e1c/image/image.go#LL40C6-L40C13 // Note: related to pkg/docker/dockerimage/V1ConfigObject|ConfigObject // TODO: refactor into one set of common structs later type ImageConfig struct { Created time.Time `json:"created,omitempty"` Author string `json:"author,omitempty"` Architecture string `json:"architecture"` OS string `json:"os"` OSVersion string `json:"os.version,omitempty"` OSFeatures []string `json:"os.features,omitempty"` Variant string `json:"variant,omitempty"` Config RunConfig `json:"config"` RootFS *RootFS `json:"rootfs"` //not used building images History []History `json:"history,omitempty"` //not used building images //Extra fields Container string `json:"container,omitempty"` DockerVersion string `json:"docker_version,omitempty"` //More extra fields ID string `json:"id,omitempty"` Comment string `json:"comment,omitempty"` } type RootFS struct { Type string `json:"type"` DiffIDs []string `json:"diff_ids,omitempty"` } type History struct { Created string `json:"created,omitempty"` Author string `json:"author,omitempty"` CreatedBy string `json:"created_by,omitempty"` Comment string `json:"comment,omitempty"` EmptyLayer bool `json:"empty_layer,omitempty"` } // RunConfig describes the runtime config parameters for container instances (aka Config in other libraries) // Fields (ordered according to spec): Memory, MemorySwap, CpuShares aren't necessary // * https://github.com/opencontainers/image-spec/blob/main/config.md#properties (Config field) // * https://github.com/moby/moby/blob/master/api/types/container/config.go#L70 // Note: related to pkg/docker/dockerimage/ContainerConfig // TODO: refactor into one set of common structs later type RunConfig struct { User string `json:"User,omitempty"` ExposedPorts map[string]struct{} `json:"ExposedPorts,omitempty"` Env []string `json:"Env,omitempty"` Entrypoint []string `json:"Entrypoint,omitempty"` Cmd []string `json:"Cmd,omitempty"` Volumes map[string]struct{} `json:"Volumes,omitempty"` WorkingDir string `json:"WorkingDir,omitempty"` Labels map[string]string `json:"Labels,omitempty"` StopSignal string `json:"StopSignal,omitempty"` ArgsEscaped bool `json:"ArgsEscaped,omitempty"` Healthcheck *HealthConfig `json:"Healthcheck,omitempty"` //Extra fields AttachStderr bool `json:"AttachStderr,omitempty"` AttachStdin bool `json:"AttachStdin,omitempty"` AttachStdout bool `json:"AttachStdout,omitempty"` Domainname string `json:"Domainname,omitempty"` Hostname string `json:"Hostname,omitempty"` Image string `json:"Image,omitempty"` OnBuild []string `json:"OnBuild,omitempty"` OpenStdin bool `json:"OpenStdin,omitempty"` StdinOnce bool `json:"StdinOnce,omitempty"` Tty bool `json:"Tty,omitempty"` NetworkDisabled bool `json:"NetworkDisabled,omitempty"` MacAddress string `json:"MacAddress,omitempty"` StopTimeout *int `json:"StopTimeout,omitempty"` Shell []string `json:"Shell,omitempty"` } type HealthConfig struct { Test []string `json:",omitempty"` Interval time.Duration `json:",omitempty"` Timeout time.Duration `json:",omitempty"` StartPeriod time.Duration `json:",omitempty"` Retries int `json:",omitempty"` } type SimpleBuildOptions struct { From string Tags []string Layers []LayerDataInfo ImageConfig ImageConfig /* //todo: add 'Healthcheck' Entrypoint []string Cmd []string WorkDir string User string StopSignal string OnBuild []string Volumes map[string]struct{} EnvVars []string ExposedPorts map[string]struct{} Labels map[string]string Architecture string */ } type LayerSourceType string const ( TarSource LayerSourceType = "lst.tar" DirSource LayerSourceType = "lst.dir" ) type LayerDataInfo struct { Type LayerSourceType Source string Params *DataParams //TODO: add other common layer metadata... } type DataParams struct { TargetPath string //TODO: add useful fields (e.g., to filter directory files or to use specific file perms, etc) } type ImageResult struct { ID string `json:"id,omitempty"` Digest string `json:"digest,omitempty"` Name string `json:"name,omitempty"` OtherTags []string `json:"other_tags,omitempty"` } type SimpleBuildEngine interface { Name() string Build(options SimpleBuildOptions) (*ImageResult, error) } func SimpleBuildOptionsFromDockerfileData(data string, ignoreExeInstructions bool) (*SimpleBuildOptions, error) { var options SimpleBuildOptions lines := strings.Split(data, "\n") for _, line := range lines { line = strings.TrimSpace(line) if strings.HasPrefix(line, "#") { continue } parts := strings.SplitN(line, " ", 2) if len(parts) != 2 { continue } instName := strings.ToLower(parts[0]) switch instName { case instruction.Entrypoint: //options.Entrypoint []string case instruction.Cmd: //options.Cmd []string case instruction.Env: //options.EnvVars []string case instruction.Expose: //options.ExposedPorts map[string]struct{} case instruction.Label: //options.Labels map[string]string case instruction.User: //options.User = parts[1] options.ImageConfig.Config.User = parts[1] case instruction.Volume: //options.Volumes map[string]struct{} case instruction.Workdir: //options.WorkDir = parts[1] options.ImageConfig.Config.WorkingDir = parts[1] case instruction.Add: //support tar files (ignore other things, at leas, for now) //options.Layers []LayerDataInfo case instruction.Copy: //options.Layers []LayerDataInfo case instruction.Maintainer: //TBD case instruction.Healthcheck: //TBD case instruction.From: //options.From string case instruction.Arg: //TODO case instruction.Run: if !ignoreExeInstructions { return nil, fmt.Errorf("RUN instructions are not supported") } case instruction.Onbuild: //IGNORE case instruction.Shell: //IGNORE case instruction.StopSignal: //IGNORE } } return &options, nil } func SimpleBuildOptionsFromDockerfile(path string, ignoreExeInstructions bool) (*SimpleBuildOptions, error) { data, err := os.ReadFile(path) if err != nil { return nil, err } return SimpleBuildOptionsFromDockerfileData(string(data), ignoreExeInstructions) } func SimpleBuildOptionsFromImageConfig(data *ImageConfig) (*SimpleBuildOptions, error) { return &SimpleBuildOptions{ImageConfig: *data}, nil } ================================================ FILE: pkg/imagebuilder/internalbuilder/engine.go ================================================ package internalbuilder import ( "archive/tar" "bytes" "fmt" "io" "os" "path" "path/filepath" "time" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/daemon" "github.com/google/go-containerregistry/pkg/v1/empty" "github.com/google/go-containerregistry/pkg/v1/mutate" "github.com/google/go-containerregistry/pkg/v1/tarball" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/imagebuilder" "github.com/slimtoolkit/slim/pkg/util/fsutil" ) const ( Name = "internal.container.build.engine" ) // Engine is the default simple build engine type Engine struct { ShowBuildLogs bool PushToDaemon bool PushToRegistry bool } // New creates new Engine instances func New( showBuildLogs bool, pushToDaemon bool, pushToRegistry bool) (*Engine, error) { engine := &Engine{ ShowBuildLogs: showBuildLogs, PushToDaemon: pushToDaemon, PushToRegistry: pushToRegistry, } return engine, nil } func (ref *Engine) Build(options imagebuilder.SimpleBuildOptions) (*imagebuilder.ImageResult, error) { if len(options.ImageConfig.Config.Entrypoint) == 0 && len(options.ImageConfig.Config.Cmd) == 0 { return nil, fmt.Errorf("missing startup info") } if len(options.Layers) == 0 { return nil, fmt.Errorf("no layers") } if len(options.Layers) > 255 { return nil, fmt.Errorf("too many layers") } switch options.ImageConfig.Architecture { case "": options.ImageConfig.Architecture = "amd64" case "arm64", "amd64": default: return nil, fmt.Errorf("bad architecture value") } var img v1.Image if options.From == "" { //same as FROM scratch img = empty.Image } else { return nil, fmt.Errorf("custom base images are not supported yet") } imgRunConfig := v1.Config{ User: options.ImageConfig.Config.User, ExposedPorts: options.ImageConfig.Config.ExposedPorts, Env: options.ImageConfig.Config.Env, Entrypoint: options.ImageConfig.Config.Entrypoint, Cmd: options.ImageConfig.Config.Cmd, Volumes: options.ImageConfig.Config.Volumes, WorkingDir: options.ImageConfig.Config.WorkingDir, Labels: options.ImageConfig.Config.Labels, StopSignal: options.ImageConfig.Config.StopSignal, ArgsEscaped: options.ImageConfig.Config.ArgsEscaped, AttachStderr: options.ImageConfig.Config.AttachStderr, AttachStdin: options.ImageConfig.Config.AttachStdin, AttachStdout: options.ImageConfig.Config.AttachStdout, Domainname: options.ImageConfig.Config.Domainname, Hostname: options.ImageConfig.Config.Hostname, Image: options.ImageConfig.Config.Image, OnBuild: options.ImageConfig.Config.OnBuild, OpenStdin: options.ImageConfig.Config.OpenStdin, StdinOnce: options.ImageConfig.Config.StdinOnce, Tty: options.ImageConfig.Config.Tty, NetworkDisabled: options.ImageConfig.Config.NetworkDisabled, MacAddress: options.ImageConfig.Config.MacAddress, Shell: options.ImageConfig.Config.Shell, } if options.ImageConfig.Config.Healthcheck != nil { imgRunConfig.Healthcheck = &v1.HealthConfig{ Test: options.ImageConfig.Config.Healthcheck.Test, Interval: options.ImageConfig.Config.Healthcheck.Interval, Timeout: options.ImageConfig.Config.Healthcheck.Timeout, StartPeriod: options.ImageConfig.Config.Healthcheck.StartPeriod, Retries: options.ImageConfig.Config.Healthcheck.Retries, } } imgConfig := &v1.ConfigFile{ Created: v1.Time{Time: time.Now()}, Author: options.ImageConfig.Author, Architecture: options.ImageConfig.Architecture, OS: options.ImageConfig.OS, OSVersion: options.ImageConfig.OSVersion, OSFeatures: options.ImageConfig.OSFeatures, Variant: options.ImageConfig.Variant, Config: imgRunConfig, //History - not setting for now (actual history needs to match the added layers) Container: options.ImageConfig.Container, DockerVersion: options.ImageConfig.DockerVersion, } if imgConfig.OS == "" { imgConfig.OS = "linux" } if imgConfig.Author == "" { imgConfig.Author = "slimtoolkit" } if !options.ImageConfig.Created.IsZero() { imgConfig.Created = v1.Time{Time: options.ImageConfig.Created} } log.Debug("DefaultSimpleBuilder.Build: config image") img, err := mutate.ConfigFile(img, imgConfig) if err != nil { return nil, err } var layersToAdd []v1.Layer for i, layerInfo := range options.Layers { log.Debugf("DefaultSimpleBuilder.Build: [%d] create image layer (type=%v source=%s)", i, layerInfo.Type, layerInfo.Source) if layerInfo.Source == "" { return nil, fmt.Errorf("empty image layer data source") } if !fsutil.Exists(layerInfo.Source) { return nil, fmt.Errorf("image layer data source path doesnt exist - %s", layerInfo.Source) } switch layerInfo.Type { case imagebuilder.TarSource: if !fsutil.IsRegularFile(layerInfo.Source) { return nil, fmt.Errorf("image layer data source path is not a file - %s", layerInfo.Source) } if !fsutil.IsTarFile(layerInfo.Source) { return nil, fmt.Errorf("image layer data source path is not a tar file - %s", layerInfo.Source) } layer, err := layerFromTar(layerInfo) if err != nil { return nil, err } layersToAdd = append(layersToAdd, layer) case imagebuilder.DirSource: if !fsutil.IsDir(layerInfo.Source) { return nil, fmt.Errorf("image layer data source path is not a directory - %s", layerInfo.Source) } layer, err := layerFromDir(layerInfo) if err != nil { return nil, err } layersToAdd = append(layersToAdd, layer) default: return nil, fmt.Errorf("unknown image data source - %v", layerInfo.Source) } } log.Debug("DefaultSimpleBuilder.Build: adding layers to image") newImg, err := mutate.AppendLayers(img, layersToAdd...) if err != nil { return nil, err } if len(options.Tags) == 0 { return nil, fmt.Errorf("missing tags") } tag, err := name.NewTag(options.Tags[0]) if err != nil { return nil, err } otherTags := options.Tags[1:] if ref.PushToDaemon { log.Debug("DefaultSimpleBuilder.Build: saving image to Docker") imageLoadResponseStr, err := daemon.Write(tag, newImg) if err != nil { return nil, err } log.Debugf("DefaultSimpleBuilder.Build: pushed image to daemon - %s", imageLoadResponseStr) if ref.ShowBuildLogs { //TBD (need execution context to display the build logs) } if len(otherTags) > 0 { log.Debug("DefaultSimpleBuilder.Build: adding other tags") for _, tagName := range otherTags { ntag, err := name.NewTag(tagName) if err != nil { log.Errorf("DefaultSimpleBuilder.Build: error creating tag: %v", err) continue } if err := daemon.Tag(tag, ntag); err != nil { log.Errorf("DefaultSimpleBuilder.Build: error tagging: %v", err) } } } } if ref.PushToRegistry { //TBD } id, _ := newImg.ConfigName() digest, _ := newImg.Digest() result := &imagebuilder.ImageResult{ Name: options.Tags[0], OtherTags: otherTags, ID: fmt.Sprintf("%s:%s", id.Algorithm, id.Hex), Digest: fmt.Sprintf("%s:%s", digest.Algorithm, digest.Hex), } return result, nil } func layerFromTar(input imagebuilder.LayerDataInfo) (v1.Layer, error) { if !fsutil.Exists(input.Source) || !fsutil.IsRegularFile(input.Source) { return nil, fmt.Errorf("bad input data") } return tarball.LayerFromFile(input.Source) } func layerFromDir(input imagebuilder.LayerDataInfo) (v1.Layer, error) { if !fsutil.Exists(input.Source) || !fsutil.IsDir(input.Source) { return nil, fmt.Errorf("bad input data") } var b bytes.Buffer tw := tar.NewWriter(&b) layerBasePath := "/" if input.Params != nil && input.Params.TargetPath != "" { layerBasePath = input.Params.TargetPath } err := filepath.Walk(input.Source, func(fp string, info os.FileInfo, err error) error { if err != nil { return nil } rel, err := filepath.Rel(input.Source, fp) if err != nil { return fmt.Errorf("failed to calculate relative path: %w", err) } hdr := &tar.Header{ Name: path.Join(layerBasePath, filepath.ToSlash(rel)), Mode: int64(info.Mode()), } if !info.IsDir() { hdr.Size = info.Size() } if info.Mode().IsDir() { hdr.Typeflag = tar.TypeDir } else if info.Mode().IsRegular() { hdr.Typeflag = tar.TypeReg } else { return fmt.Errorf("not implemented archiving file type %s (%s)", info.Mode(), rel) } if err := tw.WriteHeader(hdr); err != nil { return fmt.Errorf("failed to write tar header: %w", err) } if !info.IsDir() { f, err := os.Open(fp) if err != nil { return err } if _, err := io.Copy(tw, f); err != nil { return fmt.Errorf("failed to read file into the tar: %w", err) } f.Close() } return nil }) if err != nil { return nil, fmt.Errorf("failed to scan files: %w", err) } if err := tw.Close(); err != nil { return nil, fmt.Errorf("failed to finish tar: %w", err) } return tarball.LayerFromReader(&b) } ================================================ FILE: pkg/imagereader/imagereader.go ================================================ package imagereader import ( "os" "github.com/google/go-containerregistry/pkg/crane" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/daemon" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/imagebuilder" ) type Instance struct { imageName string nameRef name.Reference imageRef v1.Image exportedTarPath string imageConfig *imagebuilder.ImageConfig } func New(imageName string) (*Instance, error) { logger := log.WithFields(log.Fields{ "op": "imagereader.New", "image.name": imageName, }) logger.Trace("call") defer logger.Trace("exit") ref, err := name.ParseReference(imageName) //, name.WeakValidation) if err != nil { logger.WithError(err).Error("name.ParseReference") return nil, err } //TODO/FUTURE: add other image source options (not just local Docker daemon) //TODO/ASAP: need to pass the 'daemon' client otherwise it'll fail if the default client isn't enough img, err := daemon.Image(ref) if err != nil { logger.WithError(err).Error("daemon.Image") return nil, err } instance := &Instance{ imageName: imageName, nameRef: ref, imageRef: img, } return instance, nil } func (ref *Instance) ImageConfig() (*imagebuilder.ImageConfig, error) { logger := log.WithFields(log.Fields{ "op": "imagereader.Instance.ImageConfig", "image.name": ref.imageName, }) logger.Trace("call") defer logger.Trace("exit") if ref.imageConfig != nil { return ref.imageConfig, nil } cf, err := ref.imageRef.ConfigFile() if err != nil { logger.WithError(err).Error("v1.Image.ConfigFile") return nil, err } ref.imageConfig = &imagebuilder.ImageConfig{ Created: cf.Created.Time, Author: cf.Author, Architecture: cf.Architecture, OS: cf.OS, OSVersion: cf.OSVersion, OSFeatures: cf.OSFeatures, Variant: cf.Variant, //RootFS *RootFS `json:"rootfs"` //not used building images //History []History `json:"history,omitempty"` //not used building images Container: cf.Container, DockerVersion: cf.DockerVersion, Config: imagebuilder.RunConfig{ User: cf.Config.User, ExposedPorts: cf.Config.ExposedPorts, Env: cf.Config.Env, Entrypoint: cf.Config.Entrypoint, Cmd: cf.Config.Cmd, Volumes: cf.Config.Volumes, WorkingDir: cf.Config.WorkingDir, Labels: cf.Config.Labels, StopSignal: cf.Config.StopSignal, ArgsEscaped: cf.Config.ArgsEscaped, AttachStderr: cf.Config.AttachStderr, AttachStdin: cf.Config.AttachStdin, AttachStdout: cf.Config.AttachStdout, Domainname: cf.Config.Domainname, Hostname: cf.Config.Hostname, Image: cf.Config.Image, OnBuild: cf.Config.OnBuild, OpenStdin: cf.Config.OpenStdin, StdinOnce: cf.Config.StdinOnce, Tty: cf.Config.Tty, NetworkDisabled: cf.Config.NetworkDisabled, MacAddress: cf.Config.MacAddress, Shell: cf.Config.Shell, //?? //Healthcheck *HealthConfig `json:"Healthcheck,omitempty"` }, } return ref.imageConfig, nil } func (ref *Instance) FreeExportedFilesystem() error { logger := log.WithFields(log.Fields{ "op": "imagereader.Instance.FreeExportedFilesystem", "image.name": ref.imageName, }) logger.Trace("call") defer logger.Trace("exit") if ref.exportedTarPath == "" { return nil } err := os.Remove(ref.exportedTarPath) ref.exportedTarPath = "" return err } func (ref *Instance) ExportFilesystem() (string, error) { logger := log.WithFields(log.Fields{ "op": "imagereader.Instance.ExportFilesystem", "image.name": ref.imageName, }) logger.Trace("call") defer logger.Trace("exit") if ref.exportedTarPath != "" { return ref.exportedTarPath, nil } tarFile, err := os.CreateTemp("", "image-exported-fs-*.tar") if err != nil { return "", err } defer tarFile.Close() err = crane.Export(ref.imageRef, tarFile) if err != nil { return "", err } if _, err := os.Stat(tarFile.Name()); err != nil { return "", err } ref.exportedTarPath = tarFile.Name() return ref.exportedTarPath, nil } func (ref *Instance) ExportedTarPath() string { return ref.exportedTarPath } ================================================ FILE: pkg/ipc/channel/channel.go ================================================ package channel import ( "bufio" "bytes" "encoding/hex" "encoding/json" "errors" "fmt" "io" "math/rand" "net" "os" "sync" "time" log "github.com/sirupsen/logrus" ) var ( ErrNoData = errors.New("no data") ErrFrameTIDMismatch = errors.New("frame TID mismatch") ErrFrameUnexpected = errors.New("unexpected frame type") ErrRemoteError = errors.New("remote error") ErrWaitTimeout = errors.New("wait timeout") ErrFrameMalformed = errors.New("malformed frame") ) // Channel ports const ( CmdPort = 65501 EvtPort = 65502 ) const ( proto = "tcp" defaultConnectTimeoutDuration = 11 * time.Second defaultReadTimeoutDuration = 11 * time.Second defaultWriteTimeoutDuration = 11 * time.Second msgEndByte = '\n' ) var ( frameHeader = []byte("[<|]") frameTrailer = []byte("[|>]\n") ) // FrameType is a Frame struct type type FrameType string // Supported frame types const ( RequestFrameType FrameType = "ft.request" ResponseFrameType FrameType = "ft.response" EventFrameType FrameType = "ft.event" ErrorFrameType FrameType = "ft.error" ControlFrameType FrameType = "ft.control" ) type Frame struct { TID string `json:"tid"` Type FrameType `json:"type"` Body json.RawMessage `json:"body,omitempty"` } func newFrame(ftype FrameType, data []byte, tid string) *Frame { frame := Frame{ Type: ftype, Body: data, } if tid != "" { frame.TID = tid } else { frame.TID = GenerateTID() } return &frame } func createFrameBytes(frame *Frame) ([]byte, error) { raw, err := json.Marshal(frame) if err != nil { return nil, err } var b bytes.Buffer b.Write(frameHeader) b.Write(raw) b.Write(frameTrailer) return b.Bytes(), nil } func createFrameBytesFromFields(ftype FrameType, data []byte, tid string) ([]byte, error) { frame := newFrame(ftype, data, tid) return createFrameBytes(frame) } func GenerateTID() string { now := time.Now().UnixNano() random := make([]byte, 8) rand.Read(random) return fmt.Sprintf("%d.%s", now, hex.EncodeToString(random)) } func getFrame(raw []byte) (*Frame, error) { if len(raw) > (len(frameHeader)+len(frameTrailer)) && bytes.HasPrefix(raw, frameHeader) && bytes.HasSuffix(raw, frameTrailer) { data := raw[len(frameHeader) : len(raw)-len(frameTrailer)] var frame Frame if err := json.Unmarshal(data, &frame); err != nil { return nil, err } return &frame, nil } return nil, ErrFrameMalformed } type ConnectionHandler interface { OnConnection(conn net.Conn) } type Server struct { addr string listener net.Listener handler ConnectionHandler } func NewServer(addr string) *Server { server := Server{ addr: addr, } return &server } func (s *Server) SetConnHandler(handler ConnectionHandler) { s.handler = handler } func (s *Server) Start(async bool) error { var err error log.Debugf("channel.Server.Start() - addr=%v [time=%v]", s.addr, time.Now().UnixNano()) s.listener, err = net.Listen(proto, s.addr) if err != nil { log.Debugf("channel:Server.Start() - net.Listen error = %v", err) return err } loop := func() { log.Debugf("channel.Server.Start.loop()... [time=%v]", time.Now().UnixNano()) for { conn, err := s.listener.Accept() log.Debugf("channel.Server.Start.loop.Accept - new connection... [time=%v]", time.Now().UnixNano()) if err != nil { log.Errorf("channel.Server.Start() - loop.Accept error = %v", err) return } log.Debugf("channel.Server.Start.loop(): new connection = %s -> %s", conn.RemoteAddr(), conn.LocalAddr()) if s.handler != nil { log.Debug("channel.Server.Start.loop(): new connection - call handler...") s.handler.OnConnection(conn) } } } if async { go loop() } else { loop() } return nil } func (s *Server) Stop() { if s.listener != nil { s.listener.Close() } } type EventServer struct { *Server mu sync.Mutex links []net.Conn pending chan struct{} } func NewEventServer(addr string) *EventServer { server := &EventServer{ Server: NewServer(addr), pending: make(chan struct{}), } server.SetConnHandler(server) return server } func (s *EventServer) OnConnection(conn net.Conn) { s.mu.Lock() defer s.mu.Unlock() s.links = append(s.links, conn) select { case <-s.pending: // Has already been closed break default: close(s.pending) } } func (s *EventServer) WaitForConnection() { <-s.pending } func (s *EventServer) Publish(data []byte, retries uint) error { if len(data) == 0 { return nil } frame, err := createFrameBytesFromFields(EventFrameType, data, "") if err != nil { return err } for _, conn := range s.links { timeouts := uint(0) for { conn.SetWriteDeadline(time.Now().Add(defaultWriteTimeoutDuration)) n, err := conn.Write(frame) log.Debugf("channel.Broadcast.Write: %s -> %s - conn.Write wc=%v err=%v", conn.RemoteAddr(), conn.LocalAddr(), n, err) if err == nil { break } if os.IsTimeout(err) { log.Debugf("channel.Broadcast.Write: %s -> %s - write timeout...", conn.RemoteAddr(), conn.LocalAddr()) timeouts++ if retries > 0 && timeouts > retries { break } } else if err != nil { log.Errorf("channel.Broadcast.Write: %s -> %s - write error = %v", conn.RemoteAddr(), conn.LocalAddr(), err) break } } } return nil } type Client struct { addr string conn net.Conn reader *bufio.Reader readTimeout time.Duration writeTimeout time.Duration } func durationValue(vparam int, vdefault time.Duration) time.Duration { val := vdefault switch { case vparam > 0: val = time.Duration(vparam) * time.Second case vparam < 0: val = 0 } return val } func NewClient(addr string, connectWait, connectTimeout, readTimeout, writeTimeout int) (*Client, error) { cwd := durationValue(connectWait, 0) ctd := durationValue(connectTimeout, defaultConnectTimeoutDuration) //todo: use non-timeout net.Dial or net.DialContext rtd := durationValue(readTimeout, defaultReadTimeoutDuration) wtd := durationValue(writeTimeout, defaultWriteTimeoutDuration) client := Client{ addr: addr, readTimeout: rtd, writeTimeout: wtd, } var timeout <-chan time.Time if cwd > 0 { log.Debugf("channel.NewClient: connect wait timeout - %v", cwd) timeout = time.After(cwd) } connectStart := time.Now() done: for { select { case <-timeout: log.Debugf("channel.NewClient: connect wait timeout (waited=%v)", time.Since(connectStart)) return nil, ErrWaitTimeout default: start := time.Now() var err error if ctd != 0 { log.Debugf("channel.NewClient: net.DialTimeout(%v,%v,%v) [time=%v]", proto, addr, ctd, time.Now().UnixNano()) client.conn, err = net.DialTimeout(proto, addr, ctd) } else { log.Debugf("channel.NewClient: net.Dial(%v,%v)", proto, addr) client.conn, err = net.Dial(proto, addr) } if err == nil { break done } log.Debugf("channel.NewClient: (dial time = %v) - connect error = %v", time.Since(start), err) if connectWait < 1 { return nil, err } log.Debug("channel.NewClient: waiting before trying to connect again...") time.Sleep(2 * time.Second) } } client.reader = bufio.NewReader(client.conn) return &client, nil } func (c *Client) Write(frame *Frame, retries uint) (n int, err error) { frameBytes, e := createFrameBytes(frame) if err != nil { return 0, e } timeouts := uint(0) for { if c.writeTimeout != 0 { c.conn.SetWriteDeadline(time.Now().Add(c.writeTimeout)) } n, err = c.conn.Write(frameBytes) log.Debugf("channel.Client.Write: remote=%s -> local=%s - conn.Write wc=%v err=%v [time=%v]", c.conn.RemoteAddr(), c.conn.LocalAddr(), n, err, time.Now().UnixNano()) if err == nil { return n, nil } if os.IsTimeout(err) { log.Debugf("channel.Client.Write: %s -> %s - write timeout...", c.conn.RemoteAddr(), c.conn.LocalAddr()) timeouts++ if retries > 0 && timeouts > retries { return } } else if err != nil { log.Errorf("channel.Client.Write: %s -> %s - write error = %v", c.conn.RemoteAddr(), c.conn.LocalAddr(), err) return } } } func (c *Client) Read(retries uint) (*Frame, error) { timeouts := uint(0) var raw string for { var err error if c.readTimeout != 0 { c.conn.SetReadDeadline(time.Now().Add(c.readTimeout)) } raw, err = c.reader.ReadString(msgEndByte) log.Debugf("channel.Client.Read() - (%p)reader.ReadString => err=%v raw=%#v", c.reader, err, raw) if err != nil { if err == io.EOF { log.Debug("channel.Client.Read: connection done...") return nil, err } else if os.IsTimeout(err) { log.Debug("channel.Client.Read: read timeout...") timeouts++ if retries > 0 && timeouts > retries { return nil, err } continue } else { log.Errorf("channel.Client.Read: read error (%v), exiting...", err) return nil, err } } log.Debugf("channel.Client.Read: got raw frame ='%s'", raw) break } frame, err := getFrame([]byte(raw)) if err != nil { log.Infof("channel.Client.Read: malformed frame (%v) ='%s'", len(raw), raw) return nil, err } if frame != nil { log.Debugf("channel.Client.Read: frame data => tid=%s type=%s body='%s'", frame.TID, frame.Type, string(frame.Body)) } else { log.Infof("channel.Client.Read: malformed frame (%v) ='%s'", len(raw), raw) } return frame, nil } func (c *Client) Close() error { return c.conn.Close() } type EventClient struct { *Client } func NewEventClient(addr string, connectWait, connectTimeout, readTimeout int) (*EventClient, error) { client, err := NewClient(addr, connectWait, connectTimeout, readTimeout, -1) if err != nil { log.Errorf("channel.NewSubscriber: NewClient error = %v", err) return nil, err } eventClient := &EventClient{ Client: client, } return eventClient, nil } func (c *EventClient) Next(retries uint) ([]byte, string, error) { frame, err := c.Read(retries) if err != nil { return nil, "", err } if frame == nil { log.Info("channel.EventClient.Next: c.Read() - no data, closed connection...") return nil, "", nil } return frame.Body, frame.TID, nil } type CommandClient struct { *Client } func NewCommandClient(addr string, connectWait, connectTimeout, readTimeout, writeTimeout int) (*CommandClient, error) { cwd := durationValue(connectWait, 0) var timeout <-chan time.Time if cwd > 0 { timeout = time.After(cwd) } connectStart := time.Now() for { select { case <-timeout: log.Debugf("channel.NewCommandClient: connect wait timeout (waited=%v)...", time.Since(connectStart)) return nil, ErrWaitTimeout default: client, err := NewClient(addr, connectWait, connectTimeout, readTimeout, writeTimeout) if err != nil { log.Errorf("channel.NewCommandClient: NewClient error = %v", err) return nil, err } err = verifyCommandChannel(client, 3) if err == nil { cmdClient := &CommandClient{ Client: client, } return cmdClient, nil } if errors.Is(err, io.EOF) { log.Debug("channel.NewCommandClient: closed connection.") } else { log.Errorf("channel.NewCommandClient: channel verify error = %v", err) } if connectWait < 1 { return nil, err } log.Debugf("channel.NewCommandClient: waiting before trying to connect again...") time.Sleep(5 * time.Second) } } } func verifyCommandChannel(client *Client, retries uint) error { reqFrame := newFrame(ControlFrameType, nil, "") _, err := client.Write(reqFrame, retries) if err != nil { log.Errorf("verifyCommandChannel: client.Write error = %v", err) return err } replyFrame, err := client.Read(retries) if err != nil { log.Debugf("verifyCommandChannel: client.Read error = %v", err) return err } if replyFrame == nil { log.Info("verifyCommandChannel: client.Read() - no data") return ErrFrameUnexpected } log.Debugf("verifyCommandChannel: client.Read() (tid=%v type=%v) result.data='%s'", replyFrame.TID, replyFrame.Type, string(replyFrame.Body)) if replyFrame.Type == ErrorFrameType { return ErrRemoteError } if replyFrame.Type != ControlFrameType { return ErrFrameUnexpected } if reqFrame.TID != replyFrame.TID { log.Errorf("verifyCommandChannel: frame TID mismatch %s = %s", reqFrame.TID, replyFrame.TID) return ErrFrameTIDMismatch } return nil } func (c *CommandClient) Call(data []byte, retries uint) ([]byte, error) { log.Debug("channel.CommandClient.Call: calling c.Write()") if len(data) == 0 { return nil, ErrNoData } reqFrame := newFrame(RequestFrameType, data, "") _, err := c.Write(reqFrame, retries) if err != nil { log.Errorf("channel.CommandClient.Call: c.Write error = %v", err) return nil, err } log.Debugf("channel.CommandClient.Call: calling c.Read()") replyFrame, err := c.Read(retries) if err != nil { log.Errorf("channel.CommandClient.Call: c.Read error = %v", err) return nil, err } if replyFrame == nil { log.Info("channel.CommandClient.Call: c.Read() - no data, closed connection...") return nil, nil } log.Debugf("channel.CommandClient.Call: c.Read() (tid=%v type=%v) result.data='%s'", replyFrame.TID, replyFrame.Type, string(replyFrame.Body)) if replyFrame.Type == ErrorFrameType { return nil, ErrRemoteError } if replyFrame.Type != ResponseFrameType { return nil, ErrFrameUnexpected } if reqFrame.TID != replyFrame.TID { log.Errorf("channel.CommandClient.Call: frame TID mismatch %s = %s", reqFrame.TID, replyFrame.TID) return nil, ErrFrameTIDMismatch } return replyFrame.Body, nil } type RequestHandler interface { OnRequest(data []byte) ([]byte, error) } type CommandServer struct { *Server handler RequestHandler pending chan struct{} } func NewCommandServer(addr string, handler RequestHandler) *CommandServer { server := &CommandServer{ Server: NewServer(addr), pending: make(chan struct{}), } server.SetConnHandler(server) server.SetReqHandler(handler) return server } func (s *CommandServer) SetReqHandler(handler RequestHandler) { s.handler = handler } func (s *CommandServer) WaitForConnection() { <-s.pending } func (s *CommandServer) OnConnection(conn net.Conn) { log.Debugf("channel.CommandServer.OnConnection: %s -> %s", conn.RemoteAddr(), conn.LocalAddr()) select { case <-s.pending: // Has already been closed break default: close(s.pending) } go func() { defer func() { log.Debug("channel.CommandServer.OnConnection.worker: closing connection...") conn.Close() }() reader := bufio.NewReader(conn) for { conn.SetReadDeadline(time.Now().Add(defaultReadTimeoutDuration)) inRaw, err := reader.ReadString(msgEndByte) if err == io.EOF { log.Debugf("channel.CommandServer.OnConnection.worker: %s -> %s - connection done...", conn.RemoteAddr(), conn.LocalAddr()) return } else if os.IsTimeout(err) { log.Debugf("channel.CommandServer.OnConnection.worker: %s -> %s - read timeout...", conn.RemoteAddr(), conn.LocalAddr()) continue } else if err != nil { log.Errorf("channel.CommandServer.OnConnection.worker: %s -> %s - read error (%v)", conn.RemoteAddr(), conn.LocalAddr(), err) return } if s.handler != nil { log.Debugf("channel.CommandServer.OnConnection.worker: raw frame => '%s'", inRaw) inFrame, err := getFrame([]byte(inRaw)) if err != nil { log.Errorf("channel.CommandServer.OnConnection.worker: %s -> %s - error getting frame (%v) [raw(%v)='%s']", conn.RemoteAddr(), conn.LocalAddr(), err, len(inRaw), inRaw) return } if inFrame == nil { log.Errorf("channel.CommandServer.OnConnection.worker: %s -> %s - no frame", conn.RemoteAddr(), conn.LocalAddr()) continue } log.Debugf("channel.CommandServer.OnConnection.worker: in frame => tid=%v type=%v body='%s'", inFrame.TID, inFrame.Type, string(inFrame.Body)) var outFrame []byte if inFrame.Type == ControlFrameType { outFrame, err = createFrameBytesFromFields(ControlFrameType, nil, inFrame.TID) if err != nil { log.Debugf("channel.CommandServer.OnConnection.worker: %s -> %s - error creating control out frame (%v)", conn.RemoteAddr(), conn.LocalAddr(), err) return } } else { outData, err := s.handler.OnRequest(inFrame.Body) if err != nil { log.Errorf("channel.CommandServer.OnConnection.worker: handler.OnRequest error => %v", err) outFrame, err = createFrameBytesFromFields(ErrorFrameType, nil, inFrame.TID) if err != nil { log.Errorf("channel.CommandServer.OnConnection.worker: %s -> %s - error creating out frame (%v)", conn.RemoteAddr(), conn.LocalAddr(), err) return } } else { outFrame, err = createFrameBytesFromFields(ResponseFrameType, outData, inFrame.TID) if err != nil { log.Debugf("channel.CommandServer.OnConnection.worker: %s -> %s - error creating out frame (%v)", conn.RemoteAddr(), conn.LocalAddr(), err) return } } } for { conn.SetWriteDeadline(time.Now().Add(defaultWriteTimeoutDuration)) wc, err := conn.Write(outFrame) //todo handle timeouts properly log.Debugf("channel.CommandServer.OnConnection.worker: %s -> %s - conn.Write wc=%v err=%v", conn.RemoteAddr(), conn.LocalAddr(), wc, err) if err == nil { log.Debugf("channel.CommandServer.OnConnection.worker: %s -> %s - replied with frame='%s'", conn.RemoteAddr(), conn.LocalAddr(), string(outFrame)) break } if os.IsTimeout(err) { log.Debugf("channel.CommandServer.OnConnection.worker: %s -> %s - write timeout (trying again)...", conn.RemoteAddr(), conn.LocalAddr()) } else if err == io.EOF { log.Debugf("channel.CommandServer.OnConnection.worker: %s -> %s - connection done (worker exiting)...", conn.RemoteAddr(), conn.LocalAddr()) return } else if err != nil { log.Errorf("channel.CommandServer.OnConnection.worker: %s -> %s - write error = %v (worker exiting)", conn.RemoteAddr(), conn.LocalAddr(), err) return } } } } }() } ================================================ FILE: pkg/ipc/command/command.go ================================================ package command import ( "bytes" "encoding/json" "errors" "github.com/slimtoolkit/slim/pkg/util/fsutil" ) // Message errors var ( ErrUnknownMessage = errors.New("unknown command type") ) const ( ResponseStatusOk = "ok" ResponseStatusError = "error" ) // Response contains the command response status information type Response struct { Status string `json:"status"` } // MessageName is a message ID type type MessageName string // Supported messages const ( StartMonitorName MessageName = "cmd.monitor.start" StopMonitorName MessageName = "cmd.monitor.stop" ShutdownSensorName MessageName = "cmd.sensor.shutdown" ) // Message represents the message interface type Message interface { GetName() MessageName } // StartMonitor contains the start monitor command fields type StartMonitor struct { ObfuscateMetadata bool `json:"obfuscate_metadata"` RTASourcePT bool `json:"rta_source_ptrace"` AppName string `json:"app_name"` AppArgs []string `json:"app_args,omitempty"` AppEntrypoint []string `json:"app_entrypoint,omitempty"` AppCmd []string `json:"app_cmd,omitempty"` AppUser string `json:"app_user,omitempty"` AppStdoutToFile bool `json:"app_stdout_to_file"` AppStderrToFile bool `json:"app_stderr_to_file"` RunTargetAsUser bool `json:"run_tas_user,omitempty"` ReportOnMainPidExit bool `json:"report_on_main_pid_exit"` KeepPerms bool `json:"keep_perms,omitempty"` Perms map[string]*fsutil.AccessInfo `json:"perms,omitempty"` Excludes []string `json:"excludes,omitempty"` ExcludeVarLockFiles bool `json:"exclude_varlock_files,omitempty"` Preserves map[string]*fsutil.AccessInfo `json:"preserves,omitempty"` Includes map[string]*fsutil.AccessInfo `json:"includes,omitempty"` IncludeBins []string `json:"include_bins,omitempty"` IncludeDirBinsList map[string]*fsutil.AccessInfo `json:"include_dir_bins_list,omitempty"` IncludeExes []string `json:"include_exes,omitempty"` IncludeShell bool `json:"include_shell,omitempty"` IncludeWorkdir string `json:"include_workdir,omitempty"` IncludeCertAll bool `json:"include_cert_all,omitempty"` IncludeCertBundles bool `json:"include_cert_bundles,omitempty"` IncludeCertDirs bool `json:"include_cert_dirs,omitempty"` IncludeCertPKAll bool `json:"include_cert_pk_all,omitempty"` IncludeCertPKDirs bool `json:"include_cert_pk_dirs,omitempty"` IncludeNew bool `json:"include_new,omitempty"` IncludeSSHClient bool `json:"include_ssh_client,omitempty"` IncludeOSLibsNet bool `json:"include_oslibs_net,omitempty"` IncludeZoneInfo bool `json:"include_zoneinfo,omitempty"` IncludeAppNuxtDir bool `json:"include_app_nuxt_dir,omitempty"` IncludeAppNuxtBuildDir bool `json:"include_app_nuxt_build,omitempty"` IncludeAppNuxtDistDir bool `json:"include_app_nuxt_dist,omitempty"` IncludeAppNuxtStaticDir bool `json:"include_app_nuxt_static,omitempty"` IncludeAppNuxtNodeModulesDir bool `json:"include_app_nuxt_nm,omitempty"` IncludeAppNextDir bool `json:"include_app_next_dir,omitempty"` IncludeAppNextBuildDir bool `json:"include_app_next_build,omitempty"` IncludeAppNextDistDir bool `json:"include_app_next_dist,omitempty"` IncludeAppNextStaticDir bool `json:"include_app_next_static,omitempty"` IncludeAppNextNodeModulesDir bool `json:"include_app_next_nm,omitempty"` IncludeNodePackages []string `json:"include_node_packages,omitempty"` } // GetName returns the command message ID for the start monitor command func (m *StartMonitor) GetName() MessageName { return StartMonitorName } // StopMonitor contains the stop monitor command fields type StopMonitor struct { } // GetName returns the command message ID for the stop monitor command func (m *StopMonitor) GetName() MessageName { return StopMonitorName } // ShutdownSensor contains the 'shutdown sensor' command fields type ShutdownSensor struct{} // GetName returns the command message ID for the 'shutdown sensor' command func (m *ShutdownSensor) GetName() MessageName { return ShutdownSensorName } type messageWrapper struct { Name MessageName `json:"name"` Data json.RawMessage `json:"data,omitempty"` } // Encode encodes the message instance to a JSON buffer object func Encode(m Message) ([]byte, error) { obj := messageWrapper{ Name: m.GetName(), } switch v := m.(type) { case *StartMonitor: var b bytes.Buffer encoder := json.NewEncoder(&b) encoder.SetEscapeHTML(false) if err := encoder.Encode(v); err != nil { return nil, err } obj.Data = b.Bytes() case *StopMonitor: case *ShutdownSensor: default: return nil, ErrUnknownMessage } return json.Marshal(&obj) } // Decode decodes JSON data into a message instance func Decode(data []byte) (Message, error) { var wrapper messageWrapper if err := json.Unmarshal(data, &wrapper); err != nil { return nil, err } switch wrapper.Name { case StartMonitorName: var cmd StartMonitor if err := json.Unmarshal(wrapper.Data, &cmd); err != nil { return nil, err } return &cmd, nil case StopMonitorName: return &StopMonitor{}, nil case ShutdownSensorName: return &ShutdownSensor{}, nil default: return nil, ErrUnknownMessage } } ================================================ FILE: pkg/ipc/event/event.go ================================================ package event import ( "encoding/json" goerr "errors" "github.com/slimtoolkit/slim/pkg/errors" ) // Event errors var ( ErrUnknownEvent = goerr.New("unknown event type") ErrUnexpectedEvent = goerr.New("unexpected event type") ) // Type is an event ID type type Type string // Supported events const ( StartMonitorDone Type = "event.monitor.start.done" StartMonitorFailed Type = "event.monitor.start.failed" StopMonitorDone Type = "event.monitor.stop.done" ShutdownSensorDone Type = "event.sensor.shutdown.done" Error Type = "event.error" ) type Message struct { Name Type `json:"name"` Data interface{} `json:"data,omitempty"` } // StartMonitorFailedData contains additional context for the failure type StartMonitorFailedData struct { Component string `json:"component,omitempty"` State string `json:"state,omitempty"` Context map[string]string `json:"context,omitempty"` Errors []string `json:"errors,omitempty"` } const ( StateSensorTypeCreating = "sensor.type.creating" StateCmdStartMonCmdWaiting = "cmd.monitor.start.waiting" StateEnvPreparing = "env.preparing" StateMonCreating = "monitor.creating" StateMonStarting = "monitor.starting" ) const ( ComSensorConstructor = "sensor.constructor" ComSensorCmdServer = "sensor.cmd.server" ComMonitorRunner = "monitor.runner" ) const ( CtxSensorType = "sensor.type" CtxCmdType = "cmd.type" ) func (m *Message) UnmarshalJSON(data []byte) error { var tmp struct { Name Type `json:"name"` Data json.RawMessage `json:"data,omitempty"` } if err := json.Unmarshal(data, &tmp); err != nil { return err } m.Name = tmp.Name switch tmp.Name { case Error: var data errors.SensorError if err := json.Unmarshal(tmp.Data, &data); err != nil { return err } m.Data = &data case StartMonitorFailed: var data StartMonitorFailedData if err := json.Unmarshal(tmp.Data, &data); err != nil { return err } m.Data = &data default: if len(tmp.Data) > 0 { return json.Unmarshal(tmp.Data, &m.Data) } } return nil } ================================================ FILE: pkg/lambdaproxy/lambdaproxy.go ================================================ package lambdaproxy import ( "bytes" "context" "encoding/base64" "encoding/json" "fmt" "log" "net/http" "os" "regexp" "sort" "strings" ) var isbnRegexp = regexp.MustCompile(`[0-9]{3}\-[0-9]{10}`) var errorLogger = log.New(os.Stderr, "ERROR ", log.Llongfile) // HTTPProbeCmd provides the HTTP probe parameters type HTTPProbeCmd struct { Method string `json:"method"` Resource string `json:"resource"` Port int `json:"port"` Protocol string `json:"protocol"` Headers []string `json:"headers"` Body string `json:"body"` BodyFile string `json:"body_file"` Username string `json:"username"` Password string `json:"password"` Crawl bool `json:"crawl"` } type apiGatewayProxyRequest struct { Path string `json:"path,omitempty"` // The url path for the caller HTTPMethod string `json:"httpMethod,omitempty"` Headers map[string]string `json:"headers,omitempty"` QueryStringParameters map[string]string `json:"queryStringParameters,omitempty"` Body string `json:"body,omitempty"` IsBase64Encoded bool `json:"isBase64Encoded,omitempty"` Resource string `json:"resource,omitempty"` // The resource path defined in API Gateway } type apiGatewayProxyResponse struct { StatusCode int `json:"statusCode"` Headers map[string]string `json:"headers"` Body string `json:"body"` IsBase64Encoded bool `json:"isBase64Encoded,omitempty"` } type HTTPRequest struct { Method string `json:"method"` Resource string `json:"resource"` Headers []string `json:"headers"` Body string `json:"body"` Protocol string `json:"protocol"` Username string `json:"username"` Password string `json:"password"` } type HTTPResponse struct { StatusCode int `json:"statusCode"` Headers []string `json:"headers"` Body string `json:"body"` } // encode and decode options - future placeholder for v1 and v2 options of lambda results type EncodeOptions struct { version string `json:"version"` } type DecodeOptions struct { version string `json:"version"` } // Add a helper for handling errors. This logs any error to os.Stderr // and returns a 500 Internal Server Error response that the AWS API // Gateway understands. func serverError(err error) (apiGatewayProxyResponse, error) { errorLogger.Println(err.Error()) return apiGatewayProxyResponse{ StatusCode: http.StatusInternalServerError, Body: http.StatusText(http.StatusInternalServerError), }, nil } // Similarly add a helper for send responses relating to client errors. func clientError(status int) (apiGatewayProxyResponse, error) { return apiGatewayProxyResponse{ StatusCode: status, Body: http.StatusText(status), }, nil } func handleRequest(ctx context.Context, request *HTTPProbeCmd) (*HTTPRequest, error) { return &HTTPRequest{Method: request.Method, Resource: request.Resource, Headers: request.Headers, Body: request.Body}, nil } func EncodeRequest(input *HTTPRequest, options *EncodeOptions) ([]byte, error) { // encode http request to api gateway proxy request // to do matching of parsing of both structs encodeapiGatewayStruct := apiGatewayProxyRequest{ Resource: input.Resource, Body: input.Body, Headers: convertSliceToMap(input.Headers), } var b bytes.Buffer encoder := json.NewEncoder(&b) encoder.SetEscapeHTML(false) if err := encoder.Encode(&encodeapiGatewayStruct); err != nil { fmt.Errorf("Error encoding apiGatewayProxyRequest: %s", err) return nil, err } return b.Bytes(), nil } func DecodeResponse(input []byte, options *DecodeOptions) (HTTPResponse, error) { var response apiGatewayProxyResponse if err := json.NewDecoder(bytes.NewBuffer(input)).Decode(&response); err != nil { return HTTPResponse{}, err } //decode the response.Body if base64 encoded if response.IsBase64Encoded { responseBodyBytes, err := base64.StdEncoding.DecodeString(string(response.Body)) if err != nil { log.Fatalf("Some error occured during base64 decode. Error %s", err.Error()) } response.Body = string(responseBodyBytes) } return HTTPResponse{StatusCode: response.StatusCode, Body: response.Body, Headers: convertMapToSlice(response.Headers)}, nil } func convertSliceToMap(pairs []string) map[string]string { m := map[string]string{} for _, pair := range pairs { parts := strings.SplitN(pair, ":", 2) parts[0] = strings.TrimSpace(parts[0]) parts[1] = strings.TrimSpace(parts[1]) if val, found := m[parts[0]]; !found { m[parts[0]] = parts[1] } else { m[parts[0]] = fmt.Sprintf("%s,%s", val, parts[1]) } } return m } func convertMapToSlice(input map[string]string) []string { // Convert map to slice of keys. //since map is unordered we need to sort the keys. keys := make([]string, 0, len(input)) for key := range input { keys = append(keys, key) } sort.Strings(keys) // Convert map to slice of values. values := []string{} for _, key := range keys { values = append(values, input[key]) } // Convert map to slice of key-value pairs. pairs := []string{} for key, value := range input { splitvalue := strings.Split(value, ",") if len(splitvalue) > 1 { for _, v := range splitvalue { pairs = append(pairs, fmt.Sprintf("%s: %s", key, strings.TrimSpace(v))) } } else { pairs = append(pairs, fmt.Sprintf("%s: %s", key, value)) } } return pairs } ================================================ FILE: pkg/lambdaproxy/lambdaproxy_test.go ================================================ package lambdaproxy import ( "context" "encoding/json" "testing" "github.com/stretchr/testify/assert" ) var ( cmd *HTTPProbeCmd request *HTTPRequest ) // /WIP Test func TestHandleRequest(t *testing.T) { // Create a new HTTPProbeCmd err := json.Unmarshal([]byte(`{ "resource": "/2015-03-31/functions/function/invocations", "method": "POST", "body": "{\"httpMethod\":\"POST\", \"path\":\"/stuff\", \"body\":\"{\"key\":\"val data\"}\", \"headers\": {\"Content-Type\": \"application/json\"}}", "protocol": "http" }`), &cmd) if err != nil { t.Fatal(err) } // Call HandleRequest request, err = handleRequest(context.Background(), cmd) if err != nil { t.Fatal(err) } // Check that the request is correct assert.Equal(t, request.Method, "POST") assert.Equal(t, request.Body, "{\"httpMethod\":\"POST\", \"path\":\"/stuff\", \"body\":\"{\"key\":\"val data\"}\", \"headers\": {\"Content-Type\": \"application/json\"}}") assert.Equal(t, request.Resource, "/2015-03-31/functions/function/invocations") } func TestEncodeRequest(t *testing.T) { var testrequest *HTTPRequest err := json.Unmarshal([]byte(`{ "headers": [ "header1: value1", "header2: value1,value2" ], "body": "Hello from Lambda", "resource": "/2015-03-31/functions/function/invocations" }`), &testrequest) if err != nil { t.Fatal(err) } // Call EncodeRequest encodedRequest, err := EncodeRequest(testrequest, nil) if err != nil { t.Fatal(err) } // Check that the encoded request is correct //to-fix-this-test assert.Equal(t, string(encodedRequest), `{"headers":{"header1":"value1","header2":"value1,value2"},"body":"Hello from Lambda","resource":"/2015-03-31/functions/function/invocations"} `) } // function to decode response from lambda proxy func TestDecodeResponse(t *testing.T) { // Call DecodeResponse response, err := DecodeResponse([]byte(`{"statusCode":200,"headers":{"x-powered-by":"Express","content-type":"application/json; charset=utf-8","content-length":"68","etag":"W/\"44-ef4gTsYXxI2SCuYqkSK9GcCRgKo\""},"isBase64Encoded":false,"body":"{\"status\":\"ok\",\"call\":\"POST /stuff\",\"data\":\"{\\\"key\\\":\\\"val data\\\"}\"}"}`), &DecodeOptions{}) if err != nil { t.Fatal(err) } // Check that the response is correct assert.Equal(t, response.StatusCode, 200) assert.Equal(t, response.Body, "{\"status\":\"ok\",\"call\":\"POST /stuff\",\"data\":\"{\\\"key\\\":\\\"val data\\\"}\"}") assert.ElementsMatch(t, response.Headers, []string{"x-powered-by: Express", "content-type: application/json; charset=utf-8", "content-length: 68", "etag: W/\"44-ef4gTsYXxI2SCuYqkSK9GcCRgKo\""}) } ================================================ FILE: pkg/launcher/launcher.go ================================================ //go:build linux // +build linux package launcher import ( "io" "os" "os/exec" "os/user" "strings" "syscall" log "github.com/sirupsen/logrus" "golang.org/x/sys/unix" "github.com/slimtoolkit/slim/pkg/system" ) // copied from libcontainer func fixStdioPermissions(uid int) error { var null unix.Stat_t if err := unix.Stat("/dev/null", &null); err != nil { return err } for _, fd := range []uintptr{ os.Stdin.Fd(), os.Stderr.Fd(), os.Stdout.Fd(), } { var s unix.Stat_t if err := unix.Fstat(int(fd), &s); err != nil { return err } if s.Rdev == null.Rdev { continue } if err := unix.Fchown(int(fd), uid, int(s.Gid)); err != nil { if err == unix.EINVAL || err == unix.EPERM { continue } return err } } return nil } func fixStdioPermissionsAlt(uid int) error { var null unix.Stat_t if err := unix.Stat("/dev/null", &null); err != nil { return err } for _, fd := range []uintptr{ os.Stdin.Fd(), os.Stderr.Fd(), os.Stdout.Fd(), } { var s unix.Stat_t if err := unix.Fstat(int(fd), &s); err != nil { return err } if s.Rdev == null.Rdev { continue } if err := unix.Fchmod(int(fd), 0777); err != nil { if err == unix.EINVAL || err == unix.EPERM { continue } return err } } return nil } // Start starts the target application in the container func Start( appName string, appArgs []string, appDir string, appUser string, runTargetAsUser bool, doPtrace bool, appStdout io.Writer, appStderr io.Writer, ) (*exec.Cmd, error) { log.Debugf("launcher.Start(%v,%v,%v,%v)", appName, appArgs, appDir, appUser) if !runTargetAsUser { appUser = "" } app := exec.Command(appName, appArgs...) if doPtrace { app.SysProcAttr = &syscall.SysProcAttr{ Ptrace: true, Pdeathsig: syscall.SIGKILL, } } app.Dir = appDir app.Stdin = os.Stdin app.Stdout = appStdout app.Stderr = appStderr if appUser != "" { if app.SysProcAttr == nil { app.SysProcAttr = &syscall.SysProcAttr{} } appUserParts := strings.Split(appUser, ":") if len(appUserParts) > 0 { uid, gid, home, err := system.ResolveUser(appUserParts[0]) if err == nil { if len(appUserParts) > 1 { xgid, err := system.ResolveGroup(appUserParts[1]) if err == nil { gid = xgid } else { log.Errorf("launcher.Start: error resolving group identity (%v/%v) - %v", appUser, appUserParts[1], err) } } app.SysProcAttr.Credential = &syscall.Credential{ Uid: uid, Gid: gid, } log.Debugf("launcher.Start: start target as user (%s) - (uid=%d,gid=%d)", appUser, uid, gid) if err = fixStdioPermissions(int(uid)); err != nil { log.Errorf("launcher.Start: error fixing i/o perms for user (%v/%v) - %v", appUser, uid, err) } app.Env = appEnv(home) } else { log.Errorf("launcher.Start: error resolving user identity (%v/%v) - %v", appUser, appUserParts[0], err) } } } else { // This is not exactly the same as leaving app.Env unset. // When cmd.Env == nil, Go stdlib takes the os.Environ() list // **AND** adds the PWD=`cmd.Dir` unless Dir is empty. This doesn't // match the Docker's standard behavior - even when an image has // the WORKDIR set, there is no PWD env var unless it's set // explicitly. Thus, before this change was made to the sensor // logic, instrumented containers would have non-identical ENV list. app.Env = os.Environ() } err := app.Start() if err != nil { log.Errorf("launcher.Start: error - %v", err) return nil, err } log.Debugf("launcher.Start: started target app --> PID=%d", app.Process.Pid) return app, nil } func appEnv(appUserHome string) (appEnv []string) { // Another attempt to make the sensor's presence invisible to the target app. // Instrumented containers must be started as "root". But it makes Docker // (and other container runtimes) setting the HOME env var accordingly // (typically, using "/root" if /etc/passwd record exists or defaulting to "/" // otherwise to stay POSIX-conformant). However, when the target app needs // to be run as `appUser` != "root", the HOME env var may very well be different. // So we need to restore it by reading the corresponding /etc/passwd record. // // Note that the above logic is applicable only to the HOME env var. Other // typical env vars like USER or PATH don't need to be restored. Container // runtimes typically don't touch the USER var and almost always do set // the PATH var explicitly (during image building). So we just (implicitly) // propagate these values to app.Env from os.Environ(). sensorUser, err := user.Current() if err != nil { log.WithError(err).Error("launcher.Start: couldn't get current user") return os.Environ() } for _, e := range os.Environ() { if !strings.HasPrefix(e, "HOME=") { appEnv = append(appEnv, e) // Just copy everything... except HOME. continue } if "HOME="+sensorUser.HomeDir == e { // Since current HOME var is equal to the sensor's user HomeDir, // it's highly likely it wasn't set explicitly in the `docker run` // command (or alike) and instead was "computed" by the runtime upon // launching the container. Since the target app user != sensor's user, // we need to "recompute" it. appEnv = append(appEnv, "HOME="+appUserHome) } else { appEnv = append(appEnv, e) // Likely the HOME var was set explicitly - don't mess with it. } } return appEnv } ================================================ FILE: pkg/mondel/mondel.go ================================================ package mondel //Monitor Data Event Logger import ( "bytes" "context" "encoding/json" "errors" "fmt" "os" "sync" "time" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/acounter" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/util/fsutil" ) const eventBufSize = 10000 var ( ErrEventDropped = errors.New("event dropped") ) type Publisher interface { Publish(event *report.MonitorDataEvent) error Stop() } type publisher struct { stopped chan struct{} ctx context.Context enable bool output *os.File eventCh chan *report.MonitorDataEvent seqNumber acounter.Type } func NewPublisher(ctx context.Context, enable bool, outputFile string) *publisher { logger := log.WithField("op", "NewPublisher") logger.WithFields(log.Fields{ "enable": enable, "output.file": outputFile, }).Trace("call") defer logger.Trace("exit") ref := &publisher{ stopped: make(chan struct{}), ctx: ctx, enable: enable, } if !ref.enable { return ref } // fsutil.Touch() creates potentially missing folder(s). if err := fsutil.Touch(outputFile); err != nil { log.WithError(err).Errorf("cannot create mondel file %q - fsutil.Touch() failed", outputFile) ref.enable = false return ref } // Using O_SYNC because there is another process (art_collector) that is // reading from this file. If we don't use O_SYNC, then the file may not // be flushed to disk for too long. f, err := os.OpenFile(outputFile, os.O_APPEND|os.O_WRONLY|os.O_SYNC, 0644) if err != nil { log.WithError(err).Errorf("os.OpenFile(%v)", outputFile) ref.enable = false return ref } ref.output = f ref.eventCh = make(chan *report.MonitorDataEvent, eventBufSize) go ref.process() return ref } func (ref *publisher) Stop() { close(ref.stopped) } func (ref *publisher) Publish(event *report.MonitorDataEvent) error { if !ref.enable || event == nil { return nil } event.Timestamp = time.Now().UTC().UnixNano() event.SeqNumber = ref.seqNumber.Inc() logger := log.WithField("op", "mondel.publisher.Publish") select { case <-ref.stopped: logger.Debugf("publisher stopped - dropped event (%#v)", event) return ErrEventDropped return nil case <-ref.ctx.Done(): return ref.ctx.Err() case ref.eventCh <- event: return nil default: logger.Debugf("dropped event (%#v)", event) return ErrEventDropped } } func (ref *publisher) process() { logger := log.WithField("op", "mondel.publisher.process") logger.Trace("call") defer logger.Trace("exit") defer func() { err := ref.output.Close() logger.Tracef("closed ref.output (e=%v)", err) }() var buf bytes.Buffer ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() var once sync.Once finish := func() { once.Do(func() { close(ref.eventCh) logger.Debugf("draining eventCh - %d", len(ref.eventCh)) for evt := range ref.eventCh { encoded, err := encodeEvent(evt) if err != nil { logger.Debugf("could not encode - %v", encoded) continue } buf.WriteString(encoded) } if buf.Len() > 0 { logger.Debugf("saving leftover data - %d", buf.Len()) if _, err := ref.output.Write(buf.Bytes()); err != nil { logger.Errorf("Error writing batch: %v", err) } } logger.Debug("finish(): done") }) } for { select { case <-ref.stopped: logger.Debug("done - finishing....") finish() return case <-ref.ctx.Done(): logger.Debug("done - finishing....") finish() return case evt := <-ref.eventCh: encoded, err := encodeEvent(evt) if err != nil { logger.Debugf("could not encode - %v", encoded) continue } buf.WriteString(encoded) case <-ticker.C: // Flush the buffer every second if buf.Len() > 0 { if _, err := ref.output.Write(buf.Bytes()); err != nil { logger.Errorf("Error writing batch: %v", err) } buf.Reset() } } } } func encodeEvent(event *report.MonitorDataEvent) (string, error) { var b bytes.Buffer enc := json.NewEncoder(&b) enc.SetEscapeHTML(false) if err := enc.Encode(event); err != nil { return "", fmt.Errorf("error encoding data - %v", err) } return b.String(), nil } ================================================ FILE: pkg/monitor/ptrace/ptrace.go ================================================ //go:build !arm64 // +build !arm64 package ptrace import ( "bytes" "context" "fmt" "io" "os" "os/exec" "path" "path/filepath" "runtime" "strconv" "strings" "syscall" "github.com/armon/go-radix" log "github.com/sirupsen/logrus" "golang.org/x/sys/unix" "github.com/slimtoolkit/slim/pkg/errors" "github.com/slimtoolkit/slim/pkg/launcher" "github.com/slimtoolkit/slim/pkg/mondel" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/system" ) type AppState string const ( AppStarted AppState = "app.started" AppFailed AppState = "app.failed" AppDone AppState = "app.done" ) const ( cwdFD = unix.AT_FDCWD // AT_FDCWD = -0x64 ) func Run( ctx context.Context, del mondel.Publisher, runOpt AppRunOpt, // TODO(ivan): includeNew & origPaths logic should be applied at the artifact dumping stage. includeNew bool, origPaths map[string]struct{}, signalCh <-chan os.Signal, errorCh chan<- error, ) (*App, error) { logger := log.WithFields(log.Fields{ "com": "pt", "op": "Run", }) logger.Debug("call") defer logger.Debug("exit") app, err := newApp(ctx, del, runOpt, includeNew, origPaths, signalCh, errorCh) if err != nil { app.StateCh <- AppFailed return nil, err } if runOpt.RTASourcePT { logger.Debug("tracing target app") app.Report.Enabled = true go app.process() go app.trace() } else { logger.Debug("not tracing target app...") go func() { logger.Debug("not tracing target app - start app") if err := app.start(); err != nil { logger.Debugf("not tracing target app - start app error - %v", err) app.errorCh <- errors.SE("ptrace.App.trace.app.start", "call.error", err) app.StateCh <- AppFailed return } cancelSignalForwarding := app.startSignalForwarding() defer cancelSignalForwarding() app.StateCh <- AppStarted if err := app.cmd.Wait(); err != nil { logger.WithError(err).Debug("not tracing target app - state<-AppFailed") app.StateCh <- AppFailed } else { logger.Debug("not tracing target app - state<-AppDone") app.StateCh <- AppDone } app.ReportCh <- &app.Report }() } return app, nil } const ptOptions = syscall.PTRACE_O_TRACECLONE | syscall.PTRACE_O_TRACEFORK | syscall.PTRACE_O_TRACEVFORK | syscall.PTRACE_O_TRACEEXEC | syscall.PTRACE_O_TRACESYSGOOD | syscall.PTRACE_O_TRACEEXIT | unix.PTRACE_O_EXITKILL type syscallState struct { pid int callNum uint64 retVal uint64 expectReturn bool gotCallNum bool gotRetVal bool started bool exiting bool pathParam string pathParamErr error } type App struct { ctx context.Context Cmd string Args []string WorkDir string User string RunAsUser bool del mondel.Publisher appStdout io.Writer appStderr io.Writer RTASourcePT bool reportOnMainPidExit bool includeNew bool origPaths map[string]struct{} signalCh <-chan os.Signal errorCh chan<- error StateCh chan AppState ReportCh chan *report.PtMonitorReport cmd *exec.Cmd pgid int Report report.PtMonitorReport fsActivity map[string]*report.FSActivityInfo syscallActivity map[uint32]uint64 //syscallResolver system.NumberResolverFunc eventCh chan syscallEvent collectorDoneCh chan int logger *log.Entry } func (a *App) MainPID() int { return a.cmd.Process.Pid } func (a *App) PGID() int { return a.pgid } const eventBufSize = 2000 type syscallEvent struct { returned bool pid int callNum uint32 retVal uint64 pathParam string } func newApp( ctx context.Context, del mondel.Publisher, runOpt AppRunOpt, includeNew bool, origPaths map[string]struct{}, signalCh <-chan os.Signal, errorCh chan<- error, ) (*App, error) { logger := log.WithFields(log.Fields{ "com": "pt.App", }) sysInfo := system.GetSystemInfo() archName := system.MachineToArchName(sysInfo.Machine) a := App{ ctx: ctx, del: del, Cmd: runOpt.Cmd, Args: runOpt.Args, WorkDir: runOpt.WorkDir, User: runOpt.User, RunAsUser: runOpt.RunAsUser, appStdout: runOpt.AppStdout, appStderr: runOpt.AppStderr, RTASourcePT: runOpt.RTASourcePT, reportOnMainPidExit: runOpt.ReportOnMainPidExit, includeNew: includeNew, origPaths: origPaths, signalCh: signalCh, errorCh: errorCh, StateCh: make(chan AppState, 5), ReportCh: make(chan *report.PtMonitorReport), Report: report.PtMonitorReport{ ArchName: string(archName), SyscallStats: map[string]report.SyscallStatInfo{}, FSActivity: map[string]*report.FSActivityInfo{}, }, fsActivity: map[string]*report.FSActivityInfo{}, syscallActivity: map[uint32]uint64{}, eventCh: make(chan syscallEvent, eventBufSize), collectorDoneCh: make(chan int, 2), logger: logger, } return &a, nil } func (app *App) trace() { logger := app.logger.WithField("op", "trace") logger.Debug("call") defer logger.Debug("exit") runtime.LockOSThread() if err := app.start(); err != nil { app.collectorDoneCh <- 1 app.errorCh <- errors.SE("ptrace.App.trace.app.start", "call.error", err) app.StateCh <- AppFailed return } cancelSignalForwarding := app.startSignalForwarding() defer cancelSignalForwarding() app.StateCh <- AppStarted app.collect() } func (app *App) processSyscallActivity(e *syscallEvent) { app.syscallActivity[e.callNum]++ } func (app *App) processFileActivity(e *syscallEvent) { if e.pathParam != "" { logger := app.logger.WithField("op", "processFileActivity") p, found := syscallProcessors[int(e.callNum)] if !found { logger.Debugf("no syscall processor - %#v", e) //shouldn't happen return } if (p.SyscallType() == CheckFileType || p.SyscallType() == OpenFileType) && p.OKReturnStatus(e.retVal) { //todo: filter "/proc/", "/sys/", "/dev/" externally if e.pathParam != "." && e.pathParam != "/proc" && !strings.HasPrefix(e.pathParam, "/proc/") && !strings.HasPrefix(e.pathParam, "/sys/") && !strings.HasPrefix(e.pathParam, "/dev/") { if fsa, ok := app.fsActivity[e.pathParam]; ok { fsa.OpsAll++ fsa.Pids[e.pid] = struct{}{} fsa.Syscalls[int(e.callNum)] = struct{}{} if processor, found := syscallProcessors[int(e.callNum)]; found { switch processor.SyscallType() { case CheckFileType: fsa.OpsCheckFile++ } } } else { fsa := &report.FSActivityInfo{ OpsAll: 1, OpsCheckFile: 1, Pids: map[int]struct{}{}, Syscalls: map[int]struct{}{}, } fsa.Pids[e.pid] = struct{}{} fsa.Syscalls[int(e.callNum)] = struct{}{} app.fsActivity[e.pathParam] = fsa } if app.del != nil { //NOTE: //not capturing the 'dirfd' syscall params necessary //to reconstruct relative paths for some syscalls (todo: improve later) delEvent := &report.MonitorDataEvent{ Source: report.MDESourcePT, Type: report.MDETypeArtifact, Pid: int32(e.pid), Artifact: e.pathParam, //note: might not be full path OpNum: e.callNum, Op: p.SyscallName(), } switch p.SyscallType() { case CheckFileType: delEvent.OpType = report.OpTypeCheck case OpenFileType: delEvent.OpType = report.OpTypeRead } if err := app.del.Publish(delEvent); err != nil { logger.Errorf( "mondel publish event failed - source=%v type=%v: %v", delEvent.Source, delEvent.Type, err, ) } } } } if p.SyscallType() == ExecType && (!e.returned || //need to catch exec calls that haven't completed yet (e.returned && p.OKReturnStatus(e.retVal))) { if fsa, ok := app.fsActivity[e.pathParam]; ok { fsa.OpsAll++ fsa.Pids[e.pid] = struct{}{} fsa.Syscalls[int(e.callNum)] = struct{}{} } else { fsa := &report.FSActivityInfo{ OpsAll: 1, OpsCheckFile: 0, Pids: map[int]struct{}{}, Syscalls: map[int]struct{}{}, } fsa.Pids[e.pid] = struct{}{} fsa.Syscalls[int(e.callNum)] = struct{}{} app.fsActivity[e.pathParam] = fsa } if app.del != nil { //NOTE: //not capturing the 'dirfd' syscall params necessary //to reconstruct relative paths for some syscalls (todo: improve later) delEvent := &report.MonitorDataEvent{ Source: report.MDESourcePT, Type: report.MDETypeArtifact, Pid: int32(e.pid), Artifact: e.pathParam, //note: might not be full path OpNum: e.callNum, Op: p.SyscallName(), OpType: report.OpTypeExec, } if err := app.del.Publish(delEvent); err != nil { logger.Errorf( "mondel publish event failed - source=%v type=%v op_type=%v: %v", delEvent.Source, delEvent.Type, delEvent.OpType, err, ) } } } } } func (app *App) process() { logger := app.logger.WithField("op", "process") logger.Debug("call") defer logger.Debug("exit") state := AppDone done: for { select { // Highest priority - monitor's context has been cancelled from the outside. case <-app.ctx.Done(): logger.Debug("done - stopping...") //NOTE: need a better way to stop the target app... //"os: process already finished" error is ok if err := app.cmd.Process.Signal(syscall.SIGTERM); err != nil { logger.Debug("error stopping target app => ", err) if err := app.cmd.Process.Kill(); err != nil { logger.Debug("error killing target app => ", err) } } break done case e := <-app.eventCh: app.Report.SyscallCount++ logger.Tracef("event ==> {pid=%v cn=%d}", e.pid, e.callNum) app.processSyscallActivity(&e) app.processFileActivity(&e) case rc := <-app.collectorDoneCh: logger.Debugf("collector finished => %v", rc) if rc > 0 { state = AppFailed } break done } } // Drainign the remaining events after the collection is done. // Note that it likely introduces a race when ReportOnMainPidExit is true. // Events might be generated by the child processes (indefinitely) long after // the main process' exit, and this could make the sensor hang. However, the // alternative race (when events aren't drained) is even worse - it leads to // loosing files in the report. drain: for { select { case e := <-app.eventCh: app.Report.SyscallCount++ logger.Tracef("event (drained) ==> {pid=%v cn=%d}", e.pid, e.callNum) app.processSyscallActivity(&e) app.processFileActivity(&e) default: logger.Trace("event draining is finished") break drain } } logger.Tracef("executed syscall count = %d", app.Report.SyscallCount) logger.Tracef("number of syscalls: %v", len(app.syscallActivity)) for scNum, scCount := range app.syscallActivity { //syscallName := app.syscallResolver(scNum) syscallName := system.LookupCallName(scNum) log.Debugf("[%v] %v = %v", scNum, syscallName, scCount) scKey := strconv.FormatInt(int64(scNum), 10) app.Report.SyscallStats[scKey] = report.SyscallStatInfo{ Number: scNum, Name: syscallName, Count: scCount, } } app.Report.SyscallNum = uint32(len(app.Report.SyscallStats)) app.Report.FSActivity = app.FileActivity() app.StateCh <- state app.ReportCh <- &app.Report } func (app *App) FileActivity() map[string]*report.FSActivityInfo { logger := app.logger.WithField("op", "FileActivity") logger.Debugf("call - [all records - %d]", len(app.fsActivity)) //get the file activity info (ignore intermediate directories) t := radix.New() for k, v := range app.fsActivity { t.Insert(k, v) } walk := func(wkey string, wv interface{}) bool { wdata, ok := wv.(*report.FSActivityInfo) if !ok { return false } walkAfter := func(akey string, av interface{}) bool { //adata, ok := av.(*report.FSActivityInfo) //if !ok { // return false //} if wkey == akey { return false } wdata.IsSubdir = true return true } t.WalkPrefix(wkey, walkAfter) return false } t.Walk(walk) result := map[string]*report.FSActivityInfo{} for k, v := range app.fsActivity { if v.IsSubdir { continue } result[k] = v } logger.Debugf("exit - [file records - %d]", len(result)) return result } func (app *App) start() error { logger := app.logger.WithField("op", "start") logger.Debug("call") defer logger.Debug("exit") var err error app.cmd, err = launcher.Start( app.Cmd, app.Args, app.WorkDir, app.User, app.RunAsUser, app.RTASourcePT, app.appStdout, app.appStderr, ) if err != nil { logger.WithError(err).Errorf( "cmd='%v' args='%+v' dir='%v'", app.Cmd, app.Args, app.WorkDir, ) return err } if !app.RTASourcePT { app.pgid, err = syscall.Getpgid(app.cmd.Process.Pid) if err != nil { return err } return nil } err = app.cmd.Wait() logger.Debugf("app.cmd.Wait err - %v", err) logger.Debugf("Target process state info - Exited=%v ExitCode=%v SysWaitStatus=%v", app.cmd.ProcessState.Exited(), app.cmd.ProcessState.ExitCode(), app.cmd.ProcessState.Sys()) waitStatus, ok := app.cmd.ProcessState.Sys().(syscall.WaitStatus) if ok { logger.Debugf("Target process wait status - %v (Exited=%v Signaled=%v Signal='%v' Stopped=%v StopSignal='%v' TrapCause=%v)", waitStatus, waitStatus.Exited(), waitStatus.Signaled(), waitStatus.Signal(), waitStatus.Stopped(), waitStatus.StopSignal(), waitStatus.TrapCause()) if waitStatus.Exited() { logger.Debug("unexpected app exit") return fmt.Errorf("unexpected app exit") } if waitStatus.Signaled() { logger.Debug("unexpected app signalled") return fmt.Errorf("unexpected app signalled") } //we should be in the Stopped state if waitStatus.Stopped() { sigEnum := SignalEnum(int(waitStatus.StopSignal())) logger.Debugf("Process Stop Signal - code=%d enum=%s str=%s", waitStatus.StopSignal(), sigEnum, waitStatus.StopSignal()) } else { //TODO: //check for Exited or Signaled process state (shouldn't happen) //do it for context indicating that we are in a failed state } } else { logger.WithError(err).Error("process status error") return fmt.Errorf("process status error") } app.pgid, err = syscall.Getpgid(app.cmd.Process.Pid) if err != nil { return err } logger.Debugf("started target app --> PID=%d PGID=%d", app.cmd.Process.Pid, app.pgid) err = syscall.PtraceSetOptions(app.cmd.Process.Pid, ptOptions) if err != nil { return err } return nil } const traceSysGoodStatusBit = 0x80 func StopSignalInfo(sig syscall.Signal) string { sigNum := int(sig) if sigNum == -1 { return fmt.Sprintf("(code=%d)", sigNum) } sigEnum := SignalEnum(sigNum) sigStr := sig.String() if sig&traceSysGoodStatusBit == traceSysGoodStatusBit { msig := sig &^ traceSysGoodStatusBit sigEnum = fmt.Sprintf("%s|0x%04x", SignalEnum(int(msig)), traceSysGoodStatusBit) sigStr = fmt.Sprintf("%s|0x%04x", msig, traceSysGoodStatusBit) } info := fmt.Sprintf("(code=%d/0x%04x enum='%s' str='%s')", sigNum, sigNum, sigEnum, sigStr) return info } func SigTrapCauseInfo(cause int) string { if cause == -1 { return fmt.Sprintf("(code=%d)", cause) } causeEnum := PtraceEvenEnum(cause) info := fmt.Sprintf("(code=%d enum=%s)", cause, causeEnum) return info } func PtraceEvenEnum(data int) string { if enum, ok := ptEventMap[data]; ok { return enum } else { return fmt.Sprintf("(%d)", data) } } var ptEventMap = map[int]string{ syscall.PTRACE_EVENT_CLONE: "PTRACE_EVENT_CLONE", syscall.PTRACE_EVENT_EXEC: "PTRACE_EVENT_EXEC", syscall.PTRACE_EVENT_EXIT: "PTRACE_EVENT_EXIT", syscall.PTRACE_EVENT_FORK: "PTRACE_EVENT_FORK", unix.PTRACE_EVENT_SECCOMP: "PTRACE_EVENT_SECCOMP", unix.PTRACE_EVENT_STOP: "PTRACE_EVENT_STOP", syscall.PTRACE_EVENT_VFORK: "PTRACE_EVENT_VFORK", syscall.PTRACE_EVENT_VFORK_DONE: "PTRACE_EVENT_VFORK_DONE", } func (app *App) collect() { logger := app.logger.WithField("op", "collect") logger.Debug("call") defer logger.Debug("exit") callPid := app.MainPID() prevPid := callPid logger.Debugf("trace syscall mainPID=%v", callPid) pidSyscallState := map[int]*syscallState{} pidSyscallState[callPid] = &syscallState{pid: callPid} mainExiting := false waitFor := -1 doSyscall := true callSig := 0 for { select { case <-app.ctx.Done(): logger.Debug("done - stop (exiting)") return default: } if doSyscall { logger.Tracef("trace syscall (pid=%v sig=%v)", callPid, callSig) err := syscall.PtraceSyscall(callPid, callSig) if err != nil { if strings.Contains(strings.ToLower(err.Error()), "no such process") { // This is kinda-sorta normal situtaion when ptrace-ing: // - The tracee process might have been KILL-led // - A group-stop event in a multi-threaded program can have // a similar effect (see also https://linux.die.net/man/2/ptrace). // // We'd been observing this behavior a lot with short&fast Go programs, // and regular `strace -f ./app` would produce similar results. // Sending this error event back to the master process would make // the `slim build` command fail with the exit code -124. logger.Debugf("trace syscall - (likely) tracee terminated pid=%v sig=%v error - %v (errno=%d)", callPid, callSig, err, err.(syscall.Errno)) } else { logger.Errorf("trace syscall pid=%v sig=%v error - %v (errno=%d)", callPid, callSig, err, err.(syscall.Errno)) app.errorCh <- errors.SE("ptrace.App.collect.ptsyscall", "call.error", err) } //keep waiting for other syscalls } } logger.Trace("waiting for syscall...") var ws syscall.WaitStatus wpid, err := syscall.Wait4(waitFor, &ws, syscall.WALL, nil) if err != nil { if err.(syscall.Errno) == syscall.ECHILD { logger.Debug("wait4 ECHILD error (ignoring)") doSyscall = false continue } logger.Debugf("wait4 error - %v (errno=%d)", err, err.(syscall.Errno)) app.errorCh <- errors.SE("ptrace.App.collect.wait4", "call.error", err) app.StateCh <- AppFailed app.collectorDoneCh <- 2 return } logger.Tracef("wait4 -> wpid=%v wstatus=%v (Exited=%v Signaled=%v Signal='%v' Stopped=%v StopSignalInfo=%s TrapCause=%s)", wpid, ws, ws.Exited(), ws.Signaled(), ws.Signal(), ws.Stopped(), StopSignalInfo(ws.StopSignal()), SigTrapCauseInfo(ws.TrapCause())) if wpid == -1 { logger.Error("wpid = -1") app.StateCh <- AppFailed app.errorCh <- errors.SE("ptrace.App.collect.wpid", "call.error", fmt.Errorf("wpid is -1")) // TODO(ivan): Investigate if this code branch leads to sensor becoming stuck. // Should we collectorDoneCh <- 42? return } callSig = 0 // reset terminated := false eventStop := false handleCall := false eventCode := 0 statusCode := 0 switch { case ws.Exited(): terminated = true statusCode = ws.ExitStatus() case ws.Signaled(): terminated = true statusCode = int(ws.Signal()) case ws.Stopped(): statusCode = int(ws.StopSignal()) if statusCode == int(syscall.SIGTRAP|traceSysGoodStatusBit) { handleCall = true } else if statusCode == int(syscall.SIGTRAP) { eventStop = true eventCode = ws.TrapCause() } else { callSig = statusCode } } if terminated { if _, ok := pidSyscallState[wpid]; !ok { logger.Debugf("[%d/%d]: unknown process is terminated (pid=%v)", app.cmd.Process.Pid, app.pgid, wpid) } else { if !pidSyscallState[wpid].exiting { logger.Debugf("[%d/%d]: unexpected process termination (pid=%v)", app.cmd.Process.Pid, app.pgid, wpid) } } delete(pidSyscallState, wpid) if app.MainPID() == wpid { logger.Debugf("[%d/%d]: wpid(%v) is main PID and terminated...", app.cmd.Process.Pid, app.pgid, wpid) if !mainExiting { logger.Debug("unexpected main PID termination...") } if len(pidSyscallState) > 0 && app.reportOnMainPidExit { // Announce the end of event collection but don't stop the tracing loop. // // This makes the monitor report the tracing results unblocking the sensor // and allowing it to start dumping the artifacts (while giving an extra // chance for the child processes to terminate gracefully). app.collectorDoneCh <- 0 } } if len(pidSyscallState) == 0 { logger.Debugf("[%d/%d]: all processes terminated...", app.cmd.Process.Pid, app.pgid) app.collectorDoneCh <- 0 // TODO(ivan): Should it be collectorDoneCh <- statusCode instead? return } doSyscall = false continue } if handleCall { var cstate *syscallState if _, ok := pidSyscallState[wpid]; ok { cstate = pidSyscallState[wpid] } else { logger.Debugf("[%d/%d]: collector loop - new pid - mainPid=%v pid=%v (prevPid=%v) - add state", app.cmd.Process.Pid, app.pgid, app.MainPID(), wpid, prevPid) //TODO: create new process records from clones/forks cstate = &syscallState{pid: wpid} pidSyscallState[wpid] = cstate } if !cstate.expectReturn { genEvent, err := onSyscall(wpid, cstate) if err != nil { logger.Debugf("[%d/%d]: wpid=%v onSyscall error - %v", app.cmd.Process.Pid, app.pgid, wpid, err) continue } if genEvent { //not waiting for the return call for the 'exec' syscalls (mostly) evt := syscallEvent{ returned: false, pid: wpid, callNum: uint32(cstate.callNum), pathParam: cstate.pathParam, } logger.Debugf("[%d/%d]: event.onSyscall - wpid=%v (evt=%#v)", app.cmd.Process.Pid, app.pgid, wpid, evt) select { case app.eventCh <- evt: default: logger.Warnf("[%d/%d]: event.onSyscall - wpid=%v app.eventCh send error (evt=%#v)", app.cmd.Process.Pid, app.pgid, wpid, evt) } } } else { if err := onSyscallReturn(wpid, cstate); err != nil { logger.Debugf("[%d/%d]: wpid=%v onSyscallReturn error - %v", app.cmd.Process.Pid, app.pgid, wpid, err) continue } } if cstate.gotCallNum && cstate.gotRetVal { evt := syscallEvent{ returned: true, pid: wpid, callNum: uint32(cstate.callNum), retVal: cstate.retVal, pathParam: cstate.pathParam, } cstate.gotCallNum = false cstate.gotRetVal = false cstate.pathParam = "" cstate.pathParamErr = nil _, ok := app.origPaths[evt.pathParam] if app.includeNew { ok = true } if ok { select { case app.eventCh <- evt: default: logger.Warnf("[%d/%d]: wpid=%v app.eventCh send error (evt=%#v)", app.cmd.Process.Pid, app.pgid, wpid, evt) } } } } if eventStop { logger.Debugf("[%d/%d]: eventStop eventCode=%d(0x%04x)", app.cmd.Process.Pid, app.pgid, eventCode, eventCode) switch eventCode { case syscall.PTRACE_EVENT_CLONE, syscall.PTRACE_EVENT_FORK, syscall.PTRACE_EVENT_VFORK, syscall.PTRACE_EVENT_VFORK_DONE: newPid, err := syscall.PtraceGetEventMsg(wpid) if err != nil { logger.Debugf("[%d/%d]: PTRACE_EVENT_CLONE/[V]FORK[_DONE] - error getting cloned pid - %v", app.cmd.Process.Pid, app.pgid, err) } else { logger.Debugf("[%d/%d]: PTRACE_EVENT_CLONE/[V]FORK[_DONE] - cloned pid - %v", app.cmd.Process.Pid, app.pgid, newPid) if _, ok := pidSyscallState[int(newPid)]; ok { logger.Debugf("[%d/%d]: PTRACE_EVENT_CLONE/[V]FORK[_DONE] - pid already exists - %v", app.cmd.Process.Pid, app.pgid, newPid) pidSyscallState[int(newPid)].started = true } else { pidSyscallState[int(newPid)] = &syscallState{pid: int(newPid), started: true} } } case syscall.PTRACE_EVENT_EXEC: oldPid, err := syscall.PtraceGetEventMsg(wpid) if err != nil { logger.Debugf("[%d/%d]: PTRACE_EVENT_EXEC - error getting old pid - %v", app.cmd.Process.Pid, app.pgid, err) } else { logger.Debugf("[%d/%d]: PTRACE_EVENT_EXEC - old pid - %v", app.cmd.Process.Pid, app.pgid, oldPid) } case syscall.PTRACE_EVENT_EXIT: logger.Debugf("[%d/%d]: PTRACE_EVENT_EXIT - process exiting pid=%v", app.cmd.Process.Pid, app.pgid, wpid) if app.MainPID() == wpid { mainExiting = true logger.Debugf("[%d/%d]: main process is exiting (%v)", app.cmd.Process.Pid, app.pgid, wpid) } if _, ok := pidSyscallState[wpid]; ok { pidSyscallState[wpid].exiting = true } else { logger.Debugf("[%d/%d]: unknown process is exiting (pid=%v)", app.cmd.Process.Pid, app.pgid, wpid) } } } doSyscall = true callPid = wpid } // eof: main for loop } func (app *App) startSignalForwarding() context.CancelFunc { logger := app.logger.WithField("op", "startSignalForwarding") logger.Debug("call") defer logger.Debug("exit") ctx, cancel := context.WithCancel(app.ctx) go func() { logger := app.logger.WithField("op", "startSignalForwarding.worker") logger.Debug("call") defer logger.Debug("exit") for { select { case <-ctx.Done(): return case s := <-app.signalCh: if s == syscall.SIGCHLD { continue } logger.WithField("signal", s).Debug("forwarding signal") ss, ok := s.(syscall.Signal) if !ok { logger.WithField("signal", s).Debug("unsupported signal type") continue } // app.cmd.Process.Signal(s) can't be used because due to ptrace-ing // the target processes' status becomes set before the actual // process termination making Signal() think the process exited and // not even trying to deliver the signal. if err := syscall.Kill(app.cmd.Process.Pid, ss); err != nil { logger. WithError(err). WithField("signal", ss). Debug("failed to signal target app") } } } }() return cancel } func onSyscall(pid int, cstate *syscallState) (bool, error) { var regs syscall.PtraceRegs if err := syscall.PtraceGetRegs(pid, ®s); err != nil { return false, err } cstate.callNum = system.CallNumber(regs) cstate.expectReturn = true cstate.gotCallNum = true if processor, found := syscallProcessors[int(cstate.callNum)]; found && processor != nil { processor.OnCall(pid, regs, cstate) doGenEvent := processor.EventOnCall() return doGenEvent, nil } return false, nil } func onSyscallReturn(pid int, cstate *syscallState) error { var regs syscall.PtraceRegs if err := syscall.PtraceGetRegs(pid, ®s); err != nil { return err } cstate.retVal = system.CallReturnValue(regs) cstate.expectReturn = false cstate.gotRetVal = true if processor, found := syscallProcessors[int(cstate.callNum)]; found { processor.OnReturn(pid, regs, cstate) } return nil } /////////////////////////////////// func fdName(fd int) string { switch fd { case 0: return "STDIN" case 1: return "STDOUT" case 2: return "STDERR" case cwdFD: return "AT_FDCWD" } return "" } func getIntVal(ptr uint64) int { return int(int32(ptr)) } func errnoName(ptr uint64) string { val := int(int32(ptr)) if val >= 0 { return "" } val *= -1 return unix.ErrnoName(syscall.Errno(val)) } func getIntParam(pid int, ptr uint64) int { return int(int32(ptr)) } func getStringParam(pid int, ptr uint64) string { var out [256]byte var data []byte for { count, err := syscall.PtracePeekData(pid, uintptr(ptr), out[:]) if err != nil && err != syscall.EIO { fmt.Printf("readString: syscall.PtracePeekData error - '%v'\v", err) } idx := bytes.IndexByte(out[:count], 0) var foundNull bool if idx == -1 { idx = count ptr += uint64(count) } else { foundNull = true } data = append(data, out[:idx]...) if foundNull { return string(data) } } } type SyscallTypeName string const ( CheckFileType SyscallTypeName = "type.checkfile" OpenFileType SyscallTypeName = "type.openfile" ExecType SyscallTypeName = "type.exec" ) type SyscallProcessor interface { SyscallNumber() uint64 SetSyscallNumber(uint64) SyscallType() SyscallTypeName SyscallName() string EventOnCall() bool OnCall(pid int, regs syscall.PtraceRegs, cstate *syscallState) OnReturn(pid int, regs syscall.PtraceRegs, cstate *syscallState) FailedCall(cstate *syscallState) bool FailedReturnStatus(retVal uint64) bool OKCall(cstate *syscallState) bool OKReturnStatus(retVal uint64) bool } type StringParamPos int type syscallProcessorCore struct { Num uint64 Name string Type SyscallTypeName StringParam StringParamPos } const ( SPPNo StringParamPos = 0 SPPOne StringParamPos = 1 SPPTwo StringParamPos = 2 ) func (ref *syscallProcessorCore) SyscallNumber() uint64 { return ref.Num } func (ref *syscallProcessorCore) SetSyscallNumber(num uint64) { ref.Num = num } func (ref *syscallProcessorCore) SyscallType() SyscallTypeName { return ref.Type } func (ref *syscallProcessorCore) SyscallName() string { return ref.Name } func (ref *syscallProcessorCore) OnCall(pid int, regs syscall.PtraceRegs, cstate *syscallState) { pth := "" dir := "" var fd int switch ref.StringParam { case SPPNo: log.Tracef("syscallProcessorCore.OnCall[%s/%d]: pid=%d - no string param", ref.Name, ref.Num, pid) return case SPPOne: pth = getStringParam(pid, system.CallFirstParam(regs)) case SPPTwo: fd = getIntParam(pid, system.CallFirstParam(regs)) //cwdFD < 0 / (stdin=0/stdout=1/stderr=2) if fd > 2 { dir, _ = os.Readlink(fmt.Sprintf("/proc/%d/fd/%d", pid, fd)) } pth = getStringParam(pid, system.CallSecondParam(regs)) default: panic("unreachable") } //fmt.Printf("[pid=%d][1]ptrace.SPC.OnCall[%v/%v](fd=[%x/%d/%s]/pth:'%s')\n", // pid, ref.Num, ref.Name, fd, fd, fdName(fd), pth) if len(pth) == 0 || (len(pth) > 0 && pth[0] != '/') { if dir != "" { pth = path.Join(dir, pth) } else if fd == cwdFD { //todo: track cwd with process/pid (so we don't need to look it up later for each call) cwd, _ := os.Readlink(fmt.Sprintf("/proc/%d/cwd", pid)) if cwd != "" { pth = path.Join(cwd, pth) } } } cstate.pathParam = filepath.Clean(pth) //fmt.Printf("[pid=%d][2]ptrace.SPC.OnCall[%v/%v](fd=[%x/%d/%s]/pp:'%s')\n", // pid, ref.Num, ref.Name, fd, fd, fdName(fd), cstate.pathParam) } func (ref *syscallProcessorCore) OnReturn(pid int, regs syscall.PtraceRegs, cstate *syscallState) { //for stat/check syscalls: //-2 => -ENOENT (No such file or directory) intRetVal := getIntVal(cstate.retVal) errnoStr := errnoName(cstate.retVal) if cstate.pathParamErr == nil { //fmt.Printf("[pid=%d][3]ptrace.SPC.OnReturn[%v/%v](pp:'%s') = %x/%d/%s\n", // pid, cstate.callNum, ref.Name, cstate.pathParam, cstate.retVal, intRetVal, errnoStr) log.Tracef("checkFileSyscallProcessor.OnReturn: [%d] {%d}%s('%s') = %x/%d/%s", pid, cstate.callNum, ref.Name, cstate.pathParam, cstate.retVal, intRetVal, errnoStr) } else { log.Debugf("checkFileSyscallProcessor.OnReturn: [%d] {%d}%s(/'%s') = %x/%d/%s [pp err => %v]", pid, cstate.callNum, ref.Name, cstate.pathParam, cstate.retVal, intRetVal, errnoStr, cstate.pathParamErr) //fmt.Printf("[pid=%d][3err]ptrace.SPC.OnReturn[%v/%v](pp:'%s'/ppe: '%v') = %x/%d/%s\n", // pid, cstate.callNum, ref.Name, cstate.pathParam, cstate.pathParamErr, cstate.retVal, intRetVal, errnoStr) } } type checkFileSyscallProcessor struct { *syscallProcessorCore } func (ref *checkFileSyscallProcessor) FailedCall(cstate *syscallState) bool { return cstate.retVal != 0 } func (ref *checkFileSyscallProcessor) FailedReturnStatus(retVal uint64) bool { return retVal != 0 } func (ref *checkFileSyscallProcessor) OKCall(cstate *syscallState) bool { return cstate.retVal == 0 } func (ref *checkFileSyscallProcessor) OKReturnStatus(retVal uint64) bool { return retVal == 0 } func (ref *checkFileSyscallProcessor) EventOnCall() bool { return false } type openFileSyscallProcessor struct { *syscallProcessorCore } func (ref *openFileSyscallProcessor) FailedCall(cstate *syscallState) bool { fd := getIntVal(cstate.retVal) return fd < 0 } func (ref *openFileSyscallProcessor) FailedReturnStatus(retVal uint64) bool { fd := getIntVal(retVal) return fd < 0 } func (ref *openFileSyscallProcessor) OKCall(cstate *syscallState) bool { fd := getIntVal(cstate.retVal) //need to make sure it works for readlink which returns the number of read bytes //(later: has its own processor) return fd >= 0 } func (ref *openFileSyscallProcessor) OKReturnStatus(retVal uint64) bool { fd := getIntVal(retVal) //need to make sure it works for readlink which returns the number of read bytes //(later: has its own processor) return fd >= 0 } func (ref *openFileSyscallProcessor) EventOnCall() bool { return false } type execSyscallProcessor struct { *syscallProcessorCore } func (ref *execSyscallProcessor) FailedCall(cstate *syscallState) bool { fd := getIntVal(cstate.retVal) return fd < 0 } func (ref *execSyscallProcessor) FailedReturnStatus(retVal uint64) bool { fd := getIntVal(retVal) return fd < 0 } func (ref *execSyscallProcessor) OKCall(cstate *syscallState) bool { fd := getIntVal(cstate.retVal) return fd >= 0 } func (ref *execSyscallProcessor) OKReturnStatus(retVal uint64) bool { fd := getIntVal(retVal) return fd >= 0 } func (ref *execSyscallProcessor) EventOnCall() bool { return true } // TODO: introduce syscall num and name consts to use instead of liternal values var syscallProcessors = map[int]SyscallProcessor{} func init() { //readlink(const char *path, char *buf, int bufsiz) //on success, returns the number of bytes placed in buf. On error, returns -1 //reusing "open" processor because the call error return values are similar addSyscallProcessor(&openFileSyscallProcessor{ syscallProcessorCore: &syscallProcessorCore{ Name: "readlink", Type: OpenFileType, StringParam: SPPOne, }, }) //utime(char *filename, struct utimbuf *times) addSyscallProcessor(&checkFileSyscallProcessor{ syscallProcessorCore: &syscallProcessorCore{ Name: "utime", Type: CheckFileType, StringParam: SPPOne, }, }) //utimes(char *filename, struct timeval *utimes) addSyscallProcessor(&checkFileSyscallProcessor{ syscallProcessorCore: &syscallProcessorCore{ Name: "utimes", Type: CheckFileType, StringParam: SPPOne, }, }) //chdir(const char *filename) addSyscallProcessor(&checkFileSyscallProcessor{ syscallProcessorCore: &syscallProcessorCore{ Name: "chdir", Type: CheckFileType, StringParam: SPPOne, }, }) //open(const char *filename, int flags, int mode) addSyscallProcessor(&openFileSyscallProcessor{ syscallProcessorCore: &syscallProcessorCore{ Name: "open", Type: OpenFileType, StringParam: SPPOne, }, }) //readlinkat(int dfd, const char *pathname, char *buf, int bufsiz) //on success, returns the number of bytes placed in buf. On error, returns -1 //reusing "open" processor because the call error return values are similar addSyscallProcessor(&openFileSyscallProcessor{ syscallProcessorCore: &syscallProcessorCore{ Name: "readlinkat", Type: OpenFileType, StringParam: SPPTwo, }, }) //openat(int dfd, const char *filename, int flags, int mode) addSyscallProcessor(&openFileSyscallProcessor{ syscallProcessorCore: &syscallProcessorCore{ Name: "openat", Type: OpenFileType, StringParam: SPPTwo, }, }) //futimesat(int dfd, const char *filename, struct timeval *utimes) addSyscallProcessor(&checkFileSyscallProcessor{ syscallProcessorCore: &syscallProcessorCore{ Name: "futimesat", Type: CheckFileType, StringParam: SPPTwo, }, }) //"check" system calls with file paths: //access(const char *filename, int mode) addSyscallProcessor(&checkFileSyscallProcessor{ syscallProcessorCore: &syscallProcessorCore{ Name: "access", Type: CheckFileType, StringParam: SPPOne, }, }) //faccessat(int dfd, const char *filename, int mode) addSyscallProcessor(&checkFileSyscallProcessor{ syscallProcessorCore: &syscallProcessorCore{ Name: "faccessat", Type: CheckFileType, StringParam: SPPTwo, }, }) //stat(const char *filename,struct stat *statbuf) addSyscallProcessor(&checkFileSyscallProcessor{ syscallProcessorCore: &syscallProcessorCore{ Name: "stat", Type: CheckFileType, StringParam: SPPOne, }, }) //lstat(fconst char *filename, struct stat *statbuf) addSyscallProcessor(&checkFileSyscallProcessor{ syscallProcessorCore: &syscallProcessorCore{ Name: "lstat", Type: CheckFileType, StringParam: SPPOne, }, }) //statfs(const char *pathname, struct statfs *buf) addSyscallProcessor(&checkFileSyscallProcessor{ syscallProcessorCore: &syscallProcessorCore{ Name: "statfs", Type: CheckFileType, StringParam: SPPOne, }, }) //statx(int dirfd, const char *pathname, int flags, unsigned int mask, struct statx *statxbuf) //dirfd: AT_FDCWD //flags: AT_EMPTY_PATH addSyscallProcessor(&checkFileSyscallProcessor{ syscallProcessorCore: &syscallProcessorCore{ Name: "statx", Type: CheckFileType, StringParam: SPPTwo, }, }) //newfstatat/fstatat(int dirfd, const char *restrict pathname, struct stat *restrict statbuf, int flags) //dirfd: AT_FDCWD addSyscallProcessor(&checkFileSyscallProcessor{ syscallProcessorCore: &syscallProcessorCore{ Name: "newfstatat", Type: CheckFileType, StringParam: SPPTwo, }, }) //execve(const char *pathname, char *const argv[], char *const envp[]) addSyscallProcessor(&execSyscallProcessor{ syscallProcessorCore: &syscallProcessorCore{ Name: "execve", Type: ExecType, StringParam: SPPOne, }, }) //execveat(int dirfd, const char *pathname, const char *const argv[], const char *const envp[], int flags) addSyscallProcessor(&execSyscallProcessor{ syscallProcessorCore: &syscallProcessorCore{ Name: "execveat", Type: ExecType, StringParam: SPPTwo, }, }) } func addSyscallProcessor(p SyscallProcessor) { num, found := system.LookupCallNumber(p.SyscallName()) if !found { log.Errorf("addSyscallProcessor: unknown syscall='%s'", p.SyscallName()) return } p.SetSyscallNumber(uint64(num)) syscallProcessors[int(p.SyscallNumber())] = p } /////////////////////////////////// func SignalEnum(sigNum int) string { if sigNum >= len(sigEnums) || sigNum < 0 { return fmt.Sprintf("BAD(%d)", sigNum) } e := sigEnums[sigNum] if e == "" { e = fmt.Sprintf("UNKNOWN(%d)", sigNum) } return e } var sigEnums = [...]string{ 0: "(NOSIGNAL)", syscall.SIGABRT: "SIGABRT/SIGIOT", syscall.SIGALRM: "SIGALRM", syscall.SIGBUS: "SIGBUS", syscall.SIGCHLD: "SIGCHLD", syscall.SIGCONT: "SIGCONT", syscall.SIGFPE: "SIGFPE", syscall.SIGHUP: "SIGHUP", syscall.SIGILL: "SIGILL", syscall.SIGINT: "SIGINT", syscall.SIGKILL: "SIGKILL", syscall.SIGPIPE: "SIGPIPE", syscall.SIGPOLL: "SIGIO/SIGPOLL", syscall.SIGPROF: "SIGPROF", syscall.SIGPWR: "SIGPWR", syscall.SIGQUIT: "SIGQUIT", syscall.SIGSEGV: "SIGSEGV", syscall.SIGSTKFLT: "SIGSTKFLT", syscall.SIGSTOP: "SIGSTOP", syscall.SIGSYS: "SIGSYS", syscall.SIGTERM: "SIGTERM", syscall.SIGTRAP: "SIGTRAP", syscall.SIGTSTP: "SIGTSTP", syscall.SIGTTIN: "SIGTTIN", syscall.SIGTTOU: "SIGTTOU", syscall.SIGURG: "SIGURG", syscall.SIGUSR1: "SIGUSR1", syscall.SIGUSR2: "SIGUSR2", syscall.SIGVTALRM: "SIGVTALRM", syscall.SIGWINCH: "SIGWINCH", syscall.SIGXCPU: "SIGXCPU", syscall.SIGXFSZ: "SIGXFSZ", } ================================================ FILE: pkg/monitor/ptrace/types.go ================================================ package ptrace import ( "io" ) type AppRunOpt struct { Cmd string Args []string AppStdout io.Writer AppStderr io.Writer WorkDir string User string RunAsUser bool RTASourcePT bool ReportOnMainPidExit bool } ================================================ FILE: pkg/pdiscover/pevents.go ================================================ // hacked up code from CloudFoundry package pdiscover import ( "errors" "fmt" ) type ProcEventFork struct { ParentPid int // Pid of the process that called fork() ChildPid int // Child process pid created by fork() } type ProcEventExec struct { Pid int // Pid of the process that called exec() } type ProcEventExit struct { Pid int // Pid of the process that called exit() } type watch struct { flags uint32 // Saved value of Watch() flags param } type eventListener interface { close() error // Watch.Close() closes the OS specific listener } type Watcher struct { allFlags uint32 listener eventListener // OS specifics (kqueue or netlink) watches map[int]*watch // Map of watched process ids Error chan error // Errors are sent on this channel Fork chan *ProcEventFork // Fork events are sent on this channel Exec chan *ProcEventExec // Exec events are sent on this channel Exit chan *ProcEventExit // Exit events are sent on this channel done chan bool // Used to stop the readEvents() goroutine isClosed bool // Set to true when Close() is first called } func NewAllWatcher(flags uint32) (*Watcher, error) { listener, err := createListener() if err != nil { return nil, err } w := &Watcher{ allFlags: flags, listener: listener, watches: map[int]*watch{}, Fork: make(chan *ProcEventFork), Exec: make(chan *ProcEventExec), Exit: make(chan *ProcEventExit), Error: make(chan error), done: make(chan bool, 1), } go w.readAllEvents() return w, nil } // Initialize event listener and channels func NewWatcher() (*Watcher, error) { listener, err := createListener() if err != nil { return nil, err } w := &Watcher{ listener: listener, watches: map[int]*watch{}, Fork: make(chan *ProcEventFork), Exec: make(chan *ProcEventExec), Exit: make(chan *ProcEventExit), Error: make(chan error), done: make(chan bool, 1), } go w.readEvents() return w, nil } // Close event channels when done message is received func (w *Watcher) finish() { close(w.Fork) close(w.Exec) close(w.Exit) close(w.Error) } // Closes the OS specific event listener, // removes all watches and closes all event channels. func (w *Watcher) Close() error { if w.isClosed { return nil } w.isClosed = true for pid := range w.watches { w.RemoveWatch(pid) } w.done <- true w.listener.close() return nil } // Add pid to the watched process set. // The flags param is a bitmask of process events to capture, // must be one or more of: PROC_EVENT_FORK, PROC_EVENT_EXEC, PROC_EVENT_EXIT func (w *Watcher) Watch(pid int, flags uint32) error { if w.isClosed { return errors.New("psnotify watcher is closed") } watchEntry, found := w.watches[pid] if found { watchEntry.flags |= flags } else { if err := w.register(pid, flags); err != nil { return err } w.watches[pid] = &watch{flags: flags} } return nil } func (w *Watcher) WatchAll() error { if w.isClosed { return errors.New("psnotify watcher is closed") } return nil } // Remove pid from the watched process set. func (w *Watcher) RemoveWatch(pid int) error { _, ok := w.watches[pid] if !ok { msg := fmt.Sprintf("watch for pid=%d does not exist", pid) return errors.New(msg) } delete(w.watches, pid) return w.unregister(pid) } // Internal helper to check if there is a message on the "done" channel. // The "done" message is sent by the Close() method; when received here, // the Watcher.finish method is called to close all channels and return // true - in which case the caller should break from the readEvents loop. func (w *Watcher) isDone() bool { var done bool select { case done = <-w.done: w.finish() default: } return done } ================================================ FILE: pkg/pdiscover/pevents_darwin.go ================================================ package pdiscover // stubs, so 'go get' doesn't show errors on Macs... func createListener() (eventListener, error) { return nil, nil } func (w *Watcher) unregister(pid int) error { return nil } func (w *Watcher) register(pid int, flags uint32) error { return nil } func (w *Watcher) readEvents() { } func (w *Watcher) readAllEvents() { } func (w *Watcher) isWatching(pid int, event uint32) bool { return false } ================================================ FILE: pkg/pdiscover/pevents_linux.go ================================================ // hacked up code from CloudFoundry // Go interface to the Linux netlink process connector. // See Documentation/connector/connector.txt in the linux kernel source tree. package pdiscover import ( "bytes" "encoding/binary" "os" "syscall" ) const ( // internal flags (from ) _CN_IDX_PROC = 0x1 _CN_VAL_PROC = 0x1 // internal flags (from ) _PROC_CN_MCAST_LISTEN = 1 _PROC_CN_MCAST_IGNORE = 2 // Flags (from ) PROC_EVENT_FORK = 0x00000001 // fork() events PROC_EVENT_EXEC = 0x00000002 // exec() events PROC_EVENT_EXIT = 0x80000000 // exit() events //KCQ: //NEED TO ADD MORE EVENTS (TBD) // Watch for all process events PROC_EVENT_ALL = PROC_EVENT_FORK | PROC_EVENT_EXEC | PROC_EVENT_EXIT ) var ( byteOrder = binary.LittleEndian ) // linux/connector.h: struct cb_id type cbId struct { Idx uint32 Val uint32 } // linux/connector.h: struct cb_msg type cnMsg struct { Id cbId Seq uint32 Ack uint32 Len uint16 Flags uint16 } // linux/cn_proc.h: struct proc_event.{what,cpu,timestamp_ns} type procEventHeader struct { What uint32 Cpu uint32 Timestamp uint64 } // linux/cn_proc.h: struct proc_event.fork type forkProcEvent struct { ParentPid uint32 ParentTgid uint32 ChildPid uint32 ChildTgid uint32 } // linux/cn_proc.h: struct proc_event.exec type execProcEvent struct { ProcessPid uint32 ProcessTgid uint32 } // linux/cn_proc.h: struct proc_event.exit type exitProcEvent struct { ProcessPid uint32 ProcessTgid uint32 ExitCode uint32 ExitSignal uint32 } // standard netlink header + connector header type netlinkProcMessage struct { Header syscall.NlMsghdr Data cnMsg } type netlinkListener struct { addr *syscall.SockaddrNetlink // Netlink socket address sock int // The syscall.Socket() file descriptor seq uint32 // struct cn_msg.seq } // Initialize linux implementation of the eventListener interface func createListener() (eventListener, error) { listener := &netlinkListener{} err := listener.bind() return listener, err } // noop on linux func (w *Watcher) unregister(pid int) error { return nil } // noop on linux func (w *Watcher) register(pid int, flags uint32) error { return nil } // Read events from the netlink socket func (w *Watcher) readEvents() { buf := make([]byte, syscall.Getpagesize()) listener, _ := w.listener.(*netlinkListener) for { if w.isDone() { return } nr, _, err := syscall.Recvfrom(listener.sock, buf, 0) if err != nil { w.Error <- err continue } if nr < syscall.NLMSG_HDRLEN { w.Error <- syscall.EINVAL continue } msgs, _ := syscall.ParseNetlinkMessage(buf[:nr]) for _, m := range msgs { if m.Header.Type == syscall.NLMSG_DONE { w.handleEvent(m.Data) } } } } func (w *Watcher) readAllEvents() { buf := make([]byte, syscall.Getpagesize()) listener, _ := w.listener.(*netlinkListener) for { if w.isDone() { return } nr, _, err := syscall.Recvfrom(listener.sock, buf, 0) if err != nil { w.Error <- err continue } if nr < syscall.NLMSG_HDRLEN { w.Error <- syscall.EINVAL continue } msgs, _ := syscall.ParseNetlinkMessage(buf[:nr]) for _, m := range msgs { if m.Header.Type == syscall.NLMSG_DONE { w.handleEventAll(m.Data) } } } } // Internal helper to check if pid && event is being watched func (w *Watcher) isWatching(pid int, event uint32) bool { if watch, ok := w.watches[pid]; ok { return (watch.flags & event) == event } return false } // Dispatch events from the netlink socket to the Event channels. // Unlike bsd kqueue, netlink receives events for all pids, // so we apply filtering based on the watch table via isWatching() func (w *Watcher) handleEvent(data []byte) { buf := bytes.NewBuffer(data) msg := &cnMsg{} hdr := &procEventHeader{} binary.Read(buf, byteOrder, msg) binary.Read(buf, byteOrder, hdr) switch hdr.What { case PROC_EVENT_FORK: event := &forkProcEvent{} binary.Read(buf, byteOrder, event) ppid := int(event.ParentTgid) pid := int(event.ChildTgid) if w.isWatching(ppid, PROC_EVENT_EXEC) { // follow forks watch, _ := w.watches[ppid] w.Watch(pid, watch.flags) } if w.isWatching(ppid, PROC_EVENT_FORK) { w.Fork <- &ProcEventFork{ParentPid: ppid, ChildPid: pid} } case PROC_EVENT_EXEC: event := &execProcEvent{} binary.Read(buf, byteOrder, event) pid := int(event.ProcessTgid) if w.isWatching(pid, PROC_EVENT_EXEC) { w.Exec <- &ProcEventExec{Pid: pid} } case PROC_EVENT_EXIT: event := &exitProcEvent{} binary.Read(buf, byteOrder, event) pid := int(event.ProcessTgid) if w.isWatching(pid, PROC_EVENT_EXIT) { w.RemoveWatch(pid) w.Exit <- &ProcEventExit{Pid: pid} } } } func (w *Watcher) handleEventAll(data []byte) { buf := bytes.NewBuffer(data) msg := &cnMsg{} hdr := &procEventHeader{} binary.Read(buf, byteOrder, msg) binary.Read(buf, byteOrder, hdr) switch hdr.What { case PROC_EVENT_FORK: event := &forkProcEvent{} binary.Read(buf, byteOrder, event) ppid := int(event.ParentTgid) pid := int(event.ChildTgid) w.Fork <- &ProcEventFork{ParentPid: ppid, ChildPid: pid} case PROC_EVENT_EXEC: event := &execProcEvent{} binary.Read(buf, byteOrder, event) pid := int(event.ProcessTgid) w.Exec <- &ProcEventExec{Pid: pid} case PROC_EVENT_EXIT: event := &exitProcEvent{} binary.Read(buf, byteOrder, event) pid := int(event.ProcessTgid) w.Exit <- &ProcEventExit{Pid: pid} } } // Bind our netlink socket and // send a listen control message to the connector driver. func (listener *netlinkListener) bind() error { sock, err := syscall.Socket( syscall.AF_NETLINK, syscall.SOCK_DGRAM, syscall.NETLINK_CONNECTOR) if err != nil { return err } listener.sock = sock listener.addr = &syscall.SockaddrNetlink{ Family: syscall.AF_NETLINK, Groups: _CN_IDX_PROC, } err = syscall.Bind(listener.sock, listener.addr) if err != nil { return err } return listener.send(_PROC_CN_MCAST_LISTEN) } // Send an ignore control message to the connector driver // and close our netlink socket. func (listener *netlinkListener) close() error { err := listener.send(_PROC_CN_MCAST_IGNORE) syscall.Close(listener.sock) return err } // Generic method for sending control messages to the connector // driver; where op is one of PROC_CN_MCAST_{LISTEN,IGNORE} func (listener *netlinkListener) send(op uint32) error { listener.seq++ pr := &netlinkProcMessage{} plen := binary.Size(pr.Data) + binary.Size(op) pr.Header.Len = syscall.NLMSG_HDRLEN + uint32(plen) pr.Header.Type = uint16(syscall.NLMSG_DONE) pr.Header.Flags = 0 pr.Header.Seq = listener.seq pr.Header.Pid = uint32(os.Getpid()) pr.Data.Id.Idx = _CN_IDX_PROC pr.Data.Id.Val = _CN_VAL_PROC pr.Data.Len = uint16(binary.Size(op)) buf := bytes.NewBuffer(make([]byte, 0, pr.Header.Len)) binary.Write(buf, byteOrder, pr) binary.Write(buf, byteOrder, op) return syscall.Sendto(listener.sock, buf.Bytes(), 0, listener.addr) } ================================================ FILE: pkg/pdiscover/pinfo.go ================================================ package pdiscover import ( "errors" ) var ( ErrInvalidProcArgsLen = errors.New("invalid ProcArgs length") ErrInvalidProcInfo = errors.New("invalid proc info") ) ================================================ FILE: pkg/pdiscover/pinfo_darwin.go ================================================ package pdiscover import ( "os" "path/filepath" "syscall" "unsafe" ) const ( CTLKern = 1 KernProcArgs = 38 ) func GetOwnProcPath() (string, error) { return GetProcPath(os.Getpid()) } func GetProcPath(pid int) (string, error) { nameMib := []int32{CTLKern, KernProcArgs, int32(pid), -1} procArgsLen := uintptr(0) if err := getSysCtlInfo(nameMib, nil, &procArgsLen); err != nil { return "", err } if procArgsLen == 0 { return "", ErrInvalidProcArgsLen } procArgs := make([]byte, procArgsLen) if err := getSysCtlInfo(nameMib, &procArgs[0], &procArgsLen); err != nil { return "", err } if procArgsLen == 0 { return "", ErrInvalidProcArgsLen } exePath := exePathFromProcArgs(procArgs) exePath, err := filepath.Abs(exePath) if err != nil { return exePath, err } if exePath, err := filepath.EvalSymlinks(exePath); err != nil { return exePath, err } return exePath, nil } func getSysCtlInfo(name []int32, value *byte, valueLen *uintptr) error { _, _, errNum := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&name[0])), uintptr(len(name)), uintptr(unsafe.Pointer(value)), uintptr(unsafe.Pointer(valueLen)), 0, 0) if errNum != 0 { return errNum } return nil } func GetProcInfo(pid int) map[string]string { return nil } func exePathFromProcArgs(raw []byte) string { for i, byteVal := range raw { if byteVal == 0 { return string(raw[:i]) } } return "" } ================================================ FILE: pkg/pdiscover/pinfo_linux.go ================================================ package pdiscover import ( "os" "strconv" ) func procFileName(pid int, name string) string { return "/proc/" + strconv.Itoa(pid) + "/" + name } func GetProcInfo(pid int) map[string]string { linkFields := []string{"exe", "cwd", "root"} valFields := []string{"cmdline", "environ"} fields := map[string]string{} for _, name := range linkFields { val, err := os.Readlink(procFileName(pid, name)) if err != nil { return nil } fields[name] = val } for _, name := range valFields { val, err := os.ReadFile(procFileName(pid, name)) if err != nil { return nil } fields[name] = string(val) } return fields } func GetOwnProcPath() (string, error) { return os.Readlink("/proc/self/exe") } func GetProcPath(pid int) (string, error) { procInfo := GetProcInfo(pid) if procInfo == nil { return "", ErrInvalidProcInfo } return procInfo["exe"], nil } ================================================ FILE: pkg/report/command_report.go ================================================ package report import ( "bytes" "encoding/json" "fmt" "os" "path/filepath" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/command" "github.com/slimtoolkit/slim/pkg/docker/dockerfile/reverse" "github.com/slimtoolkit/slim/pkg/docker/dockerimage" "github.com/slimtoolkit/slim/pkg/docker/linter/check" "github.com/slimtoolkit/slim/pkg/system" "github.com/slimtoolkit/slim/pkg/util/errutil" "github.com/slimtoolkit/slim/pkg/version" ) // DefaultFilename is the default name for the command report const DefaultFilename = "slim.report.json" const tmpPath = "/tmp" // Command is the common command report data type Command struct { reportLocation string Version string `json:"version"` Engine string `json:"engine"` Containerized bool `json:"containerized"` HostDistro DistroInfo `json:"host_distro"` //Docker string `json:"docker,omitempty"` Type command.Type `json:"type"` State command.State `json:"state"` Error string `json:"error,omitempty"` } // ImageIdentity includes the container image identity fields type ImageIdentity struct { ID string `json:"id"` Tags []string `json:"tags,omitempty"` Names []string `json:"names,omitempty"` Digests []string `json:"digests,omitempty"` FullDigests []string `json:"full_digests,omitempty"` } // ImageMetadata provides basic image metadata type ImageMetadata struct { Identity ImageIdentity `json:"identity"` Size int64 `json:"size"` SizeHuman string `json:"size_human"` CreateTime string `json:"create_time"` Author string `json:"author,omitempty"` Maintainers []string `json:"maintainers,omitempty"` DockerVersion string `json:"docker_version"` Architecture string `json:"architecture"` User string `json:"user,omitempty"` ExposedPorts []string `json:"exposed_ports,omitempty"` OS string `json:"os,omitempty"` Volumes []string `json:"volumes,omitempty"` Labels map[string]string `json:"labels,omitempty"` EnvVars []string `json:"env_vars,omitempty"` WorkDir string `json:"workdir,omitempty"` InheritedInstructions []string `json:"inherited_instructions,omitempty"` //TODO: //Should be in ImageReport dockerimage.ImageReport //because it's additional info discovered during analysis //BUT also need to find a way to make it available //for the 'build' command (at least, distro) Distro *DistroInfo `json:"distro,omitempty"` Buildpack *BuildpackInfo `json:"buildpack,omitempty"` ContainerEntry ContainerEntryInfo `json:"container_entry"` //Base image info BaseImageDigest string `json:"base_image_digest,omitempty"` BaseImageName string `json:"base_image_name,omitempty"` } type ContainerEntryInfo struct { Entrypoint []string `json:"entrypoint,omitempty"` Cmd []string `json:"cmd,omitempty"` ExePath string `json:"exe_path"` FullExePath *ContainerFileInfo `json:"full_exe_path,omitempty"` ExeArgs []string `json:"exe_args,omitempty"` ArgFiles []*ContainerFileInfo `json:"arg_files,omitempty"` } type ContainerFileInfo struct { Name string `json:"name"` Layer int `json:"layer"` } type BuildpackInfo struct { Stack string `json:"stack"` Vendor string `json:"vendor,omitempty"` Buildpack string `json:"buildpack,omitempty"` } // SystemMetadata provides basic system metadata type SystemMetadata struct { Type string `json:"type"` Release string `json:"release"` Distro DistroInfo `json:"distro"` } // Output Version for 'build' const OVBuildCommand = "1.1" // BuildCommand is the 'build' command report data type BuildCommand struct { Command TargetReference string `json:"target_reference"` System SystemMetadata `json:"system"` SourceImage ImageMetadata `json:"source_image"` MinifiedImageSize int64 `json:"minified_image_size"` MinifiedImageSizeHuman string `json:"minified_image_size_human"` MinifiedImage string `json:"minified_image"` MinifiedImageID string `json:"minified_image_id"` MinifiedImageDigest string `json:"minified_image_digest"` MinifiedImageHasData bool `json:"minified_image_has_data"` MinifiedBy float64 `json:"minified_by"` ArtifactLocation string `json:"artifact_location"` ContainerReportName string `json:"container_report_name"` SeccompProfileName string `json:"seccomp_profile_name"` AppArmorProfileName string `json:"apparmor_profile_name"` ImageStack []*reverse.ImageInfo `json:"image_stack"` ImageCreated bool `json:"image_created"` ImageBuildEngine string `json:"image_build_engine"` } // Output Version for 'profile' const OVProfileCommand = "1.0" // ProfileCommand is the 'profile' command report data type ProfileCommand struct { Command OriginalImage string `json:"original_image"` OriginalImageSize int64 `json:"original_image_size"` OriginalImageSizeHuman string `json:"original_image_size_human"` MinifiedImageSize int64 `json:"minified_image_size"` MinifiedImageSizeHuman string `json:"minified_image_size_human"` MinifiedImage string `json:"minified_image"` MinifiedImageHasData bool `json:"minified_image_has_data"` MinifiedBy float64 `json:"minified_by"` ArtifactLocation string `json:"artifact_location"` ContainerReportName string `json:"container_report_name"` SeccompProfileName string `json:"seccomp_profile_name"` AppArmorProfileName string `json:"apparmor_profile_name"` } // Output Version for 'xray' const OVXrayCommand = "1.2.3" // XrayCommand is the 'xray' command report data type XrayCommand struct { Command TargetReference string `json:"target_reference"` SourceImage ImageMetadata `json:"source_image"` ArtifactLocation string `json:"artifact_location"` ImageReport *dockerimage.ImageReport `json:"image_report,omitempty"` ImageStack []*reverse.ImageInfo `json:"image_stack"` ImageLayers []*dockerimage.LayerReport `json:"image_layers"` ImageArchiveLocation string `json:"image_archive_location"` RawImageManifest *dockerimage.DockerManifestObject `json:"raw_image_manifest,omitempty"` RawImageConfig *dockerimage.ConfigObject `json:"raw_image_config,omitempty"` } // Output Version for 'lint' const OVLintCommand = "1.0" // LintCommand is the 'lint' command report data type LintCommand struct { Command TargetType string `json:"target_type"` TargetReference string `json:"target_reference"` BuildContextDir string `json:"build_context_dir,omitempty"` HitsCount int `json:"hits_count"` NoHitsCount int `json:"nohits_count"` ErrorsCount int `json:"errors_count"` Hits map[string]*check.Result `json:"hits,omitempty"` //map[CHECK_ID]CHECK_RESULT Errors map[string]error `json:"errors,omitempty"` //map[CHECK_ID]ERROR_INFO } // Output Version for 'images' const OVImagesCommand = "1.0" // ImagesCommand is the 'images' command report data type ImagesCommand struct { Command } // Output Version for 'containerize' const OVContainerizeCommand = "1.0" // ContainerizeCommand is the 'containerize' command report data type ContainerizeCommand struct { Command } // Output Version for 'convert' const OVConvertCommand = "1.0" // ConvertCommand is the 'convert' command report data type ConvertCommand struct { Command } // Output Version for 'merge' const OVMergeCommand = "1.0" // MergeCommand is the 'merge' command report data type MergeCommand struct { Command FirstImage string `json:"first_image"` LastImage string `json:"last_image"` UseLastImageMetadata bool `json:"use_last_image_metadata"` } // Output Version for 'edit' const OVEditCommand = "1.0" // EditCommand is the 'edit' command report data type EditCommand struct { Command } // Output Version for 'debug' const OVDebugCommand = "1.0" // DebugCommand is the 'debug' command report data type DebugCommand struct { Command } // Output Version for 'probe' const OVProbeCommand = "1.0" // ProbeCommand is the 'probe' command report data type ProbeCommand struct { Command } // Output Version for 'server' const OVServerCommand = "1.0" // ServerCommand is the 'server' command report data type ServerCommand struct { Command } // Output Version for 'run' const OVRunCommand = "1.0" // RunCommand is the 'run' command report data type RunCommand struct { Command TargetReference string `json:"target_reference"` } // Output Version for 'registry' const OVRegistryCommand = "1.0" // RegistryCommand is the 'registry' command report data type RegistryCommand struct { Command TargetReference string `json:"target_reference"` } // Output Version for 'vulnerability' const OVVulnerabilityCommand = "1.0" // VulnerabilityCommand is the 'vulnerability' command report data type VulnerabilityCommand struct { Command Operation string `json:"operation"` } func (cmd *Command) init(containerized bool) { cmd.Containerized = containerized cmd.Engine = version.Current() hinfo := system.GetSystemInfo() cmd.HostDistro = DistroInfo{ Name: hinfo.Distro.Name, Version: hinfo.Distro.Version, DisplayName: hinfo.Distro.DisplayName, } } // NewBuildCommand creates a new 'build' command report func NewBuildCommand(reportLocation string, containerized bool) *BuildCommand { cmd := &BuildCommand{ Command: Command{ reportLocation: reportLocation, Version: OVBuildCommand, //build command 'results' version (report and artifacts) Type: command.Build, State: command.StateUnknown, }, } cmd.Command.init(containerized) return cmd } // NewProfileCommand creates a new 'profile' command report func NewProfileCommand(reportLocation string, containerized bool) *ProfileCommand { cmd := &ProfileCommand{ Command: Command{ reportLocation: reportLocation, Version: OVProfileCommand, //profile command 'results' version (report and artifacts) Type: command.Profile, State: command.StateUnknown, }, } cmd.Command.init(containerized) return cmd } // NewXrayCommand creates a new 'xray' command report func NewXrayCommand(reportLocation string, containerized bool) *XrayCommand { cmd := &XrayCommand{ Command: Command{ reportLocation: reportLocation, Version: OVXrayCommand, //xray command 'results' version (report and artifacts) Type: command.Xray, State: command.StateUnknown, }, } cmd.Command.init(containerized) return cmd } // NewLintCommand creates a new 'lint' command report func NewLintCommand(reportLocation string, containerized bool) *LintCommand { cmd := &LintCommand{ Command: Command{ reportLocation: reportLocation, Version: OVLintCommand, //lint command 'results' version (report and artifacts) Type: command.Lint, State: command.StateUnknown, }, } cmd.Command.init(containerized) return cmd } // NewImagesCommand creates a new 'images' command report func NewImagesCommand(reportLocation string, containerized bool) *ImagesCommand { cmd := &ImagesCommand{ Command: Command{ reportLocation: reportLocation, Version: OVImagesCommand, //images command 'results' version (report and artifacts) Type: command.Images, State: command.StateUnknown, }, } cmd.Command.init(containerized) return cmd } // NewContainerizeCommand creates a new 'containerize' command report func NewContainerizeCommand(reportLocation string, containerized bool) *ContainerizeCommand { cmd := &ContainerizeCommand{ Command: Command{ reportLocation: reportLocation, Version: OVContainerizeCommand, //containerize command 'results' version (report and artifacts) Type: command.Containerize, State: command.StateUnknown, }, } cmd.Command.init(containerized) return cmd } // NewConvertCommand creates a new 'convert' command report func NewConvertCommand(reportLocation string, containerized bool) *ConvertCommand { cmd := &ConvertCommand{ Command: Command{ reportLocation: reportLocation, Version: OVConvertCommand, //convert command 'results' version (report and artifacts) Type: command.Convert, State: command.StateUnknown, }, } cmd.Command.init(containerized) return cmd } // NewMergeCommand creates a new 'edit' command report func NewMergeCommand(reportLocation string, containerized bool) *MergeCommand { cmd := &MergeCommand{ Command: Command{ reportLocation: reportLocation, Version: OVMergeCommand, //edit command 'results' version (report and artifacts) Type: command.Merge, State: command.StateUnknown, }, } cmd.Command.init(containerized) return cmd } // NewEditCommand creates a new 'edit' command report func NewEditCommand(reportLocation string, containerized bool) *EditCommand { cmd := &EditCommand{ Command: Command{ reportLocation: reportLocation, Version: OVEditCommand, //edit command 'results' version (report and artifacts) Type: command.Edit, State: command.StateUnknown, }, } cmd.Command.init(containerized) return cmd } // NewDebugCommand creates a new 'debug' command report func NewDebugCommand(reportLocation string, containerized bool) *DebugCommand { cmd := &DebugCommand{ Command: Command{ reportLocation: reportLocation, Version: OVDebugCommand, //debug command 'results' version (report and artifacts) Type: command.Debug, State: command.StateUnknown, }, } cmd.Command.init(containerized) return cmd } // NewProbeCommand creates a new 'probe' command report func NewProbeCommand(reportLocation string, containerized bool) *ProbeCommand { cmd := &ProbeCommand{ Command: Command{ reportLocation: reportLocation, Version: OVProbeCommand, //probe command 'results' version (report and artifacts) Type: command.Probe, State: command.StateUnknown, }, } cmd.Command.init(containerized) return cmd } // NewServerCommand creates a new 'server' command report func NewServerCommand(reportLocation string, containerized bool) *ServerCommand { cmd := &ServerCommand{ Command: Command{ reportLocation: reportLocation, Version: OVServerCommand, //server command 'results' version (report and artifacts) Type: command.Server, State: command.StateUnknown, }, } cmd.Command.init(containerized) return cmd } // NewRunCommand creates a new 'run' command report func NewRunCommand(reportLocation string, containerized bool) *RunCommand { cmd := &RunCommand{ Command: Command{ reportLocation: reportLocation, Version: OVRunCommand, //run command 'results' version (report and artifacts) Type: command.Run, State: command.StateUnknown, }, } cmd.Command.init(containerized) return cmd } // NewRegistryCommand creates a new 'registry' command report func NewRegistryCommand(reportLocation string, containerized bool) *RegistryCommand { cmd := &RegistryCommand{ Command: Command{ reportLocation: reportLocation, Version: OVRegistryCommand, //registry command 'results' version (report and artifacts) Type: command.Registry, State: command.StateUnknown, }, } cmd.Command.init(containerized) return cmd } // NewVulnerabilityCommand creates a new 'registry' command report func NewVulnerabilityCommand(reportLocation string, containerized bool) *VulnerabilityCommand { cmd := &VulnerabilityCommand{ Command: Command{ reportLocation: reportLocation, Version: OVVulnerabilityCommand, Type: command.Vulnerability, State: command.StateUnknown, }, } cmd.Command.init(containerized) return cmd } func (p *Command) ReportLocation() string { return p.reportLocation } func (p *Command) saveInfo(info interface{}) bool { if p.reportLocation != "" { dirName := filepath.Dir(p.reportLocation) baseName := filepath.Base(p.reportLocation) if baseName == "." { fmt.Printf("no build command report location: %v\n", p.reportLocation) return false } if dirName != "." { _, err := os.Stat(dirName) if os.IsNotExist(err) { os.MkdirAll(dirName, 0777) _, err = os.Stat(dirName) errutil.FailOn(err) } } var reportData bytes.Buffer encoder := json.NewEncoder(&reportData) encoder.SetEscapeHTML(false) encoder.SetIndent("", " ") err := encoder.Encode(info) errutil.FailOn(err) err = os.WriteFile(p.reportLocation, reportData.Bytes(), 0644) if err != nil && os.IsPermission(err) { if pinfo, tmpErr := os.Stat(tmpPath); tmpErr == nil && pinfo.IsDir() { p.reportLocation = filepath.Join(tmpPath, DefaultFilename) log.Debugf("report.saveInfo - overriding command report file path to %v", p.reportLocation) err = os.WriteFile(p.reportLocation, reportData.Bytes(), 0644) } else { fmt.Printf("report.Command.saveInfo: not saving report file - '%s'\n", p.reportLocation) return false } } errutil.FailOn(err) return true } return false } // Save saves the report data to the configured location func (p *Command) Save() bool { return p.saveInfo(p) } // Save saves the Build command report data to the configured location func (p *BuildCommand) Save() bool { return p.saveInfo(p) } // Save saves the Profile command report data to the configured location func (p *ProfileCommand) Save() bool { return p.saveInfo(p) } // Save saves the Xray command report data to the configured location func (p *XrayCommand) Save() bool { return p.saveInfo(p) } // Save saves the Lint command report data to the configured location func (p *LintCommand) Save() bool { return p.saveInfo(p) } ================================================ FILE: pkg/report/container_report.go ================================================ package report import ( "bytes" "encoding/json" "os" ) // ArtifactType is an artifact type ID type ArtifactType int // Artifact type ID constants const ( DirArtifactType ArtifactType = 1 FileArtifactType ArtifactType = 2 SymlinkArtifactType ArtifactType = 3 UnknownArtifactType ArtifactType = 99 ) const ( DirArtifactTypeName = "dir" FileArtifactTypeName = "file" SymlinkArtifactTypeName = "symlink" HardlinkArtifactTypeName = "hardlink" UnknownArtifactTypeName = "unknown" UnexpectedArtifactTypeName = "unexpected" ) // DefaultContainerReportFileName is the default container report file name const DefaultContainerReportFileName = "creport.json" var artifactTypeNames = map[ArtifactType]string{ DirArtifactType: DirArtifactTypeName, FileArtifactType: FileArtifactTypeName, SymlinkArtifactType: SymlinkArtifactTypeName, UnknownArtifactType: UnknownArtifactTypeName, } // String converts the artifact type ID to a string func (t ArtifactType) String() string { return artifactTypeNames[t] } var artifactTypeValues = map[string]ArtifactType{ DirArtifactTypeName: DirArtifactType, FileArtifactTypeName: FileArtifactType, SymlinkArtifactTypeName: SymlinkArtifactType, UnknownArtifactTypeName: UnknownArtifactType, } // GetArtifactTypeValue maps an artifact type name to an artifact type ID func GetArtifactTypeValue(s string) ArtifactType { return artifactTypeValues[s] } // ProcessInfo contains various process object metadata type ProcessInfo struct { Pid int32 `json:"pid"` Name string `json:"name"` Path string `json:"path"` Cmd string `json:"cmd"` Cwd string `json:"cwd"` Root string `json:"root"` ParentPid int32 `json:"ppid"` } // FileInfo contains various file object and activity metadata type FileInfo struct { EventCount uint32 `json:"event_count"` FirstEventID uint32 `json:"first_eid"` Name string `json:"-"` ReadCount uint32 `json:"reads,omitempty"` WriteCount uint32 `json:"writes,omitempty"` ExeCount uint32 `json:"execs,omitempty"` } // FanMonitorReport is a file monitoring report type FanMonitorReport struct { MonitorPid int `json:"monitor_pid"` MonitorParentPid int `json:"monitor_ppid"` EventCount uint32 `json:"event_count"` MainProcess *ProcessInfo `json:"main_process"` Processes map[string]*ProcessInfo `json:"processes"` ProcessFiles map[string]map[string]*FileInfo `json:"process_files"` } // PeMonitorReport is a processing monitoring report type PeMonitorReport struct { Children map[int][]int Parents map[int]int } // SyscallStatInfo contains various system call activity metadata type SyscallStatInfo struct { Number uint32 `json:"num"` Name string `json:"name"` Count uint64 `json:"count"` } // PtMonitorReport contains various process execution metadata type PtMonitorReport struct { Enabled bool `json:"enabled"` ArchName string `json:"arch_name"` SyscallCount uint64 `json:"syscall_count"` SyscallNum uint32 `json:"syscall_num"` SyscallStats map[string]SyscallStatInfo `json:"syscall_stats"` FSActivity map[string]*FSActivityInfo `json:"fs_activity"` } type FSActivityInfo struct { OpsAll uint64 `json:"ops_all"` OpsCheckFile uint64 `json:"ops_checkfile"` Syscalls map[int]struct{} `json:"syscalls"` Pids map[int]struct{} `json:"pids"` IsSubdir bool `json:"is_subdir"` } // ArtifactProps contains various file system artifact properties type ArtifactProps struct { FileType ArtifactType `json:"-"` //todo FilePath string `json:"file_path"` Mode os.FileMode `json:"-"` //todo ModeText string `json:"mode"` LinkRef string `json:"link_ref,omitempty"` Flags map[string]bool `json:"flags,omitempty"` DataType string `json:"data_type,omitempty"` FileSize int64 `json:"file_size"` Sha1Hash string `json:"sha1_hash,omitempty"` AppType string `json:"app_type,omitempty"` FileInode uint64 `json:"-"` //todo FSActivity *FSActivityInfo `json:"-"` } // UnmarshalJSON decodes artifact property data func (p *ArtifactProps) UnmarshalJSON(data []byte) error { type artifactPropsType ArtifactProps props := &struct { FileTypeStr string `json:"file_type"` *artifactPropsType }{ artifactPropsType: (*artifactPropsType)(p), } if err := json.Unmarshal(data, &props); err != nil { return err } p.FileType = GetArtifactTypeValue(props.FileTypeStr) return nil } // MarshalJSON encodes artifact property data func (p *ArtifactProps) MarshalJSON() ([]byte, error) { type artifactPropsType ArtifactProps var out bytes.Buffer encoder := json.NewEncoder(&out) encoder.SetEscapeHTML(false) err := encoder.Encode( &struct { FileTypeStr string `json:"file_type"` *artifactPropsType }{ FileTypeStr: p.FileType.String(), artifactPropsType: (*artifactPropsType)(p), }) return out.Bytes(), err } // ImageReport contains image report fields type ImageReport struct { Files []*ArtifactProps `json:"files"` } // MonitorReports contains monitoring report fields type MonitorReports struct { Fan *FanMonitorReport `json:"fan"` Pt *PtMonitorReport `json:"pt"` } // SystemReport provides a basic system report for the container environment type SystemReport struct { Type string `json:"type"` Release string `json:"release"` Distro DistroInfo `json:"distro"` } // SensorReport provides a basic sensor report for the container environment type SensorReport struct { Version string `json:"version"` Args []string `json:"args"` } // StartCommandReport provides a basic start command report for the container environment type StartCommandReport struct { AppName string `json:"app_name"` AppArgs []string `json:"app_args,omitempty"` AppEntrypoint []string `json:"app_entrypoint,omitempty"` AppCmd []string `json:"app_cmd,omitempty"` AppUser string `json:"app_user,omitempty"` } // ContainerReport contains container report fields type ContainerReport struct { StartCommand *StartCommandReport `json:"start_command"` Sensor *SensorReport `json:"sensor"` System SystemReport `json:"system"` Monitors MonitorReports `json:"monitors"` Image ImageReport `json:"image"` } // PermSetFromFlags maps artifact flags to permissions func PermSetFromFlags(flags map[string]bool) string { var b bytes.Buffer if flags["R"] { b.WriteString("r") } if flags["W"] { b.WriteString("w") } if flags["X"] { b.WriteString("ix") } return b.String() } ================================================ FILE: pkg/report/mondel_report.go ================================================ package report // DefaultContainerReportFileName is the default Monitor Data Event Log file name const DefaultMonDelFileName = "mondel.ndjson" // Event source const ( MDESourceDel = ".del" //Data Event Logger event MDESourceFan = "m.fa" //FaNotify monitor event MDESourcePT = "m.pt" //PTrace monitor event ) // Event types const ( MDETypeArtifact = "a" //Artifact event type MDETypeProcess = "p" //Process event type MDETypeState = "s" //State event ) // Operation types const ( OpTypeRead = "r" OpTypeWrite = "w" OpTypeExec = "x" OpTypeCheck = "c" ) type MonitorDataEvent struct { Timestamp int64 `json:"ts"` SeqNumber uint64 `json:"sn"` Source string `json:"s"` Type string `json:"t"` Pid int32 `json:"p,omitempty"` ParentPid int32 `json:"pp,omitempty"` Artifact string `json:"a,omitempty"` // used for exe path for process events OpType string `json:"o,omitempty"` // operation type Op string `json:"op,omitempty"` // operation OpNum uint32 `json:"n,omitempty"` WorkDir string `json:"w,omitempty"` Root string `json:"r,omitempty"` Cmd string `json:"c,omitempty"` State string `json:"st,omitempty"` } ================================================ FILE: pkg/report/report.go ================================================ package report type DistroInfo struct { Name string `json:"name"` Version string `json:"version"` DisplayName string `json:"display_name"` } //copy from system.DistroInfo ================================================ FILE: pkg/sysenv/sysenv_darwin.go ================================================ package sysenv func HasDSImageFlag() bool { return false } func InDSContainer() (bool, bool) { return false, false } ================================================ FILE: pkg/sysenv/sysenv_linux.go ================================================ package sysenv import ( "fmt" "golang.org/x/sys/unix" "os" "regexp" "strings" "github.com/syndtr/gocapability/capability" ) const ( procSelfCgroup = "/proc/self/cgroup" dockerEnvPath = "/.dockerenv" dsImageFlagPath = "/.ds.container.d3e2c84f976743bdb92a7044ef12e381" ) func HasDSImageFlag() bool { _, err := os.Stat(dsImageFlagPath) return err == nil } func HasDockerEnvPath() bool { _, err := os.Stat(dockerEnvPath) return err == nil } func HasContainerCgroups() bool { if bdata, err := os.ReadFile(procSelfCgroup); err == nil { return strings.Contains(string(bdata), ":/docker/") } return false } func InDSContainer() (bool, bool) { isDSImage := HasDSImageFlag() inContainer := InContainer() return inContainer, isDSImage } func InContainer() bool { if HasDockerEnvPath() { return true } if HasContainerCgroups() { return true } return false } func IsPrivileged() bool { mode, err := SeccompMode(0) if err != nil { fmt.Printf("sysenv.IsPrivileged: error - %v\n", err) return false } if WithAllCapabilities() && mode == SeccompMNDisabled { return true } return false } func WithAllCapabilities() bool { caps, err := capability.NewPid(0) if err != nil { fmt.Printf("sysenv.WithAllCapabilities: error - %v\n", err) return false } if caps.Full(capability.PERMITTED) && caps.Full(capability.EFFECTIVE) { return true } return false } func Capabilities(pid int) (map[string]struct{}, map[string]struct{}, error) { caps, err := capability.NewPid(pid) if err != nil { return nil, nil, err } all := capability.List() active := map[string]struct{}{} for _, cap := range all { if caps.Get(capability.EFFECTIVE, cap) { active[cap.String()] = struct{}{} } } max := map[string]struct{}{} for _, cap := range all { if caps.Get(capability.PERMITTED, cap) { max[cap.String()] = struct{}{} } } return active, max, nil } func IsDefaultCapSet(set map[string]struct{}) bool { if len(set) != len(DefaultCapStrings) { return false } for k := range set { if _, ok := DefaultCapStrings[k]; !ok { return false } } return true } //default Docker container capabilities: 14 //https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities var DefaultCapStrings = map[string]struct{}{ capability.CAP_SETPCAP.String(): {}, capability.CAP_MKNOD.String(): {}, capability.CAP_AUDIT_WRITE.String(): {}, capability.CAP_CHOWN.String(): {}, capability.CAP_NET_RAW.String(): {}, capability.CAP_DAC_OVERRIDE.String(): {}, capability.CAP_FOWNER.String(): {}, capability.CAP_FSETID.String(): {}, capability.CAP_KILL.String(): {}, capability.CAP_SETGID.String(): {}, capability.CAP_SETUID.String(): {}, capability.CAP_NET_BIND_SERVICE.String(): {}, capability.CAP_SYS_CHROOT.String(): {}, capability.CAP_SETFCAP.String(): {}, } var DefaultCapNums = map[capability.Cap]string{ capability.CAP_SETPCAP: capability.CAP_SETPCAP.String(), capability.CAP_MKNOD: capability.CAP_MKNOD.String(), capability.CAP_AUDIT_WRITE: capability.CAP_AUDIT_WRITE.String(), capability.CAP_CHOWN: capability.CAP_CHOWN.String(), capability.CAP_NET_RAW: capability.CAP_NET_RAW.String(), capability.CAP_DAC_OVERRIDE: capability.CAP_DAC_OVERRIDE.String(), capability.CAP_FOWNER: capability.CAP_FOWNER.String(), capability.CAP_FSETID: capability.CAP_FSETID.String(), capability.CAP_KILL: capability.CAP_KILL.String(), capability.CAP_SETGID: capability.CAP_SETGID.String(), capability.CAP_SETUID: capability.CAP_SETUID.String(), capability.CAP_NET_BIND_SERVICE: capability.CAP_NET_BIND_SERVICE.String(), capability.CAP_SYS_CHROOT: capability.CAP_SYS_CHROOT.String(), capability.CAP_SETFCAP: capability.CAP_SETFCAP.String(), } type SeccompModeName string const ( SMDisabled string = "0" SeccompMNDisabled SeccompModeName = "disabled" SMStrict string = "1" SeccompMNStrict SeccompModeName = "strict" SMFiltering string = "2" SeccompMFiltering SeccompModeName = "filtering" ) var seccompModes = map[string]SeccompModeName{ SMDisabled: SeccompMNDisabled, SMStrict: SeccompMNStrict, SMFiltering: SeccompMFiltering, } const procStatusPat = "/proc/%s/status" func SeccompMode(pid int) (SeccompModeName, error) { fname := procFileName(pid, "status") fdata, err := fileData(fname) if err != nil { return "", err } mode := procSeccompMode(fdata) if mode != "" { return mode, nil } mode = sysSeccompMode() return mode, nil } func procSeccompMode(data string) SeccompModeName { modeNum := procStatusField(data, "Seccomp:") if mode, ok := seccompModes[modeNum]; ok { return mode } return "" } const procStatusFieldValueRegexStr = ":(.*)" var procStatusFieldValueMatcher = regexp.MustCompile(procStatusFieldValueRegexStr) func procStatusField(data, fname string) string { if !strings.HasSuffix(fname, ":") { fname = fmt.Sprintf("%s:", fname) } lines := strings.Split(data, "\n") for _, line := range lines { line := cleanLine(line) if line == "" { continue } if strings.HasPrefix(line, fname) { matches := procStatusFieldValueMatcher.FindStringSubmatch(line) if len(matches) > 1 { return strings.TrimSpace(matches[1]) } } } return "" } func cleanLine(line string) string { return strings.TrimSpace(line) } func sysSeccompMode() SeccompModeName { if err := unix.Prctl(unix.PR_GET_SECCOMP, 0, 0, 0, 0); err != unix.EINVAL { if err := unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0); err != unix.EINVAL { return SeccompMNStrict } } return SeccompMNDisabled } func fileData(fname string) (string, error) { bdata, err := os.ReadFile(fname) if err != nil { return "", err } data := strings.TrimSpace(string(bdata)) return data, nil } func procFileName(pid int, name string) string { target := "self" if pid > 0 { target = fmt.Sprintf("%d", pid) } return fmt.Sprintf("/proc/%s/%s", target, name) } ================================================ FILE: pkg/sysidentity/sysidentity.go ================================================ package sysidentity import ( "bufio" "bytes" "encoding/json" "errors" "fmt" "os" "path/filepath" "strconv" "strings" "time" ) const ( PasswdFilePath = "/etc/passwd" ShadowFilePath = "/etc/shadow" GshadowFilePath = "/etc/gshadow" GroupFilePath = "/etc/group" LoginDefsFilePath = "/etc/login.defs" SudoersFilePath = "/etc/sudoers" AuthKeysFileName = "authorized_keys" //todo: move to one of the runtime detection packages AuthLogFilePath = "/var/log/auth.log" ) func IsSourceFile(fullPath string) bool { if IsAuthKeyFile(fullPath) { return true } switch fullPath { case PasswdFilePath, ShadowFilePath, GroupFilePath: return true } return false } func IsAuthKeyFile(fullPath string) bool { if filepath.Base(fullPath) == AuthKeysFileName { return true } return false } func NewDataSet() *DataSet { ref := &DataSet{ AuthKeysData: map[string][]byte{}, } return ref } type DataSet struct { PasswdFilePath string PasswdData []byte ShadowFilePath string ShadowData []byte GroupFilePath string GroupData []byte AuthKeysData map[string][]byte } func (ref *DataSet) AddData(filePath string, data []byte) bool { switch filePath { case PasswdFilePath: ref.PasswdFilePath = filePath ref.PasswdData = data return true case ShadowFilePath: ref.ShadowFilePath = filePath ref.ShadowData = data return true case GroupFilePath: ref.GroupFilePath = filePath ref.GroupData = data return true default: if IsAuthKeyFile(filePath) { if ref.AuthKeysData == nil { ref.AuthKeysData = map[string][]byte{} } ref.AuthKeysData[filePath] = data return true } } return false } func NewReportFromData(data *DataSet) (*Report, error) { pinfo, err := ReadPasswdData(data.PasswdData) if err != nil { return nil, err } sinfo, err := ReadShadowData(data.ShadowData) if err != nil { return nil, err } ginfo, err := ReadGroupData(data.GroupData) if err != nil { return nil, err } report := Report{ Sources: []*DataSource{ {FilePath: data.PasswdFilePath}, {FilePath: data.ShadowFilePath}, {FilePath: data.GroupFilePath}, }, Users: map[string]*UserInfo{}, Groups: map[string]*GroupInfo{}, } for _, record := range pinfo.Records { userInfo := &UserInfo{ Username: record.Username, PasswdPassword: record.Password, UID: record.UID, GID: record.GID, ExtraInfo: record.Info, Home: record.Home, Shell: record.Shell, NoLoginShell: record.NoLoginShell, } report.Users[userInfo.Username] = userInfo } for _, record := range sinfo.Records { userInfo, found := report.Users[record.Username] if !found { userInfo := &UserInfo{ Username: record.Username, } report.Users[record.Username] = userInfo } userInfo.ShadowPassword = record.Password userInfo.LastChangeRaw = record.LastChangeRaw userInfo.LastChangeDate = record.LastChangeDate userInfo.MinimumAge = record.MinimumAge userInfo.MaximumAge = record.MaximumAge userInfo.WarningPeriod = record.WarningPeriod userInfo.InactiveDays = record.InactiveDays userInfo.ExpirationRaw = record.ExpirationRaw userInfo.ExpirationDate = record.ExpirationDate } for _, record := range ginfo.Records { groupInfo := &GroupInfo{ Name: record.Group, ID: record.GID, Members: record.Members, Password: record.Password, //todo: get the actual password infor from gshadow (if any) } report.Groups[groupInfo.Name] = groupInfo } for fileName, data := range data.AuthKeysData { //tmp fmt.Printf("%s -> %v\n", fileName, data) } return &report, nil } type Report struct { Users map[string]*UserInfo `json:"users"` Groups map[string]*GroupInfo `json:"groups"` Sources []*DataSource `json:"sources"` } type DataSource struct { FilePath string `json:"file_path"` Metadata DataSourceMetadata `json:"metadata"` } type DataSourceMetadata struct { Sha1Hash string `json:"sha1_hash,omitempty"` FileSize int64 `json:"file_size"` ModeText string `json:"mode"` //ModTime time.Time `json:"mod_time,omitempty"` //ChangeTime time.Time `json:"change_time,omitempty"` //CreateTime time.Time `json:"create_time,omitempty"` } func (ref *Report) StringJSONPretty() string { var out bytes.Buffer encoder := json.NewEncoder(&out) encoder.SetEscapeHTML(false) encoder.SetIndent(" ", " ") _ = encoder.Encode(ref) return out.String() } type GroupInfo struct { Name string `json:"name"` ID int `json:"id"` Members []string `json:"members"` Password string `json:"password,omitempty"` } type UserInfo struct { Username string `json:"username"` PasswdPassword string `json:"passwd_password"` ShadowPassword PasswordHash `json:"shadow_password"` UID int `json:"uid"` GID int `json:"gid"` ExtraInfo string `json:"extra_info"` Home string `json:"home"` Shell string `json:"shell"` NoLoginShell bool `json:"no_login_shell"` LastChangeRaw int `json:"last_change_raw"` LastChangeDate time.Time `json:"last_change_date"` MinimumAge int `json:"minimum_age"` MaximumAge int `json:"maximum_age"` WarningPeriod int `json:"warning_period"` InactiveDays int `json:"inactive_days"` ExpirationRaw int `json:"expiration_raw"` ExpirationDate time.Time `json:"expiration_date"` SshKeys []*SshKeyRecord `json:"ssh_keys,omitempty"` } type AuthorizedKeysFileInfo struct { Records []SshKeyRecord `json:"records"` } type SshKeyRecord struct { KeyType string `json:"key_type"` Key string `json:"key"` //base64 encoded Comment string `json:"comment"` Command string `json:"command,omitempty"` Environments []string `json:"environments,omitempty"` OtherOptions []string `json:"other_options,omitempty"` RawData string `json:"raw_data"` FilePath string `json:"file_path"` } /* "golang.org/x/crypto/ssh" func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []string, rest []byte, err error) ssh.ParseAuthorizedKey([]byte(foreverUserCertString)) */ //Raw data structs type PasswdFileInfo struct { Records []PasswdRecord `json:"records"` } type PasswdRecord struct { Username string `json:"username"` Password string `json:"password"` //password hash, "x" if the actual password hash is in the shadow file UID int `json:"uid"` GID int `json:"gid"` Info string `json:"info"` //additional user identity info / GECOS Home string `json:"home"` //home directory Shell string `json:"shell"` //shell exected when user logs in RawData string `json:"raw_data"` NoLoginShell bool `json:"no_login_shell"` } func ReadPasswdFile(filePath string) (*PasswdFileInfo, error) { //todo: have an option to skip bad records data, err := os.ReadFile(filePath) if err != nil { return nil, err } return ReadPasswdData(data) } func ReadPasswdData(data []byte) (*PasswdFileInfo, error) { scanner := bufio.NewScanner(bytes.NewReader(data)) var info PasswdFileInfo for scanner.Scan() { record, err := ParsePasswdRecord(scanner.Text()) if err != nil { return nil, err } info.Records = append(info.Records, record) } if err := scanner.Err(); err != nil { return nil, err } return &info, nil } func ParsePasswdRecord(line string) (PasswdRecord, error) { line = strings.TrimSpace(line) record := PasswdRecord{ RawData: line, } parts := strings.Split(line, ":") if len(parts) != 7 { return record, errors.New("unexpected field count") } record.Username = parts[0] record.Password = parts[1] record.Info = parts[4] record.Home = parts[5] record.Shell = strings.TrimSpace(parts[6]) if _, found := NoLoginShells[record.Shell]; found { record.NoLoginShell = true } var err error record.UID, err = strconv.Atoi(parts[2]) if err != nil { return record, fmt.Errorf("error parsing UID field - %s (%v)", parts[2], err) } record.GID, err = strconv.Atoi(parts[3]) if err != nil { return record, fmt.Errorf("error parsing GID field - %s (%v)", parts[3], err) } return record, nil } const ( HasShadowFileRecord = "x" ) func (ref PasswdRecord) UsesShadow() bool { if ref.Password == HasShadowFileRecord { return true } return false } var NoLoginShells = map[string]struct{}{ "/sbin/nologin": {}, "/usr/sbin/nologin": {}, "/bin/false": {}, } const ( HashTypeDES = "" //yes, it's empty HashTypeMD5 = "1" HashTypeBlowfish = "2a" HashTypeBcrypt = "2b" HashTypeEksblowfish = "2y" HashTypeSHA256 = "5" HashTypeSHA512 = "6" HashTypeYescrypt = "y" HashTypeGostYescrypt = "gy" HashTypeScrypt = "7" ) var HashTypes = map[string]string{ HashTypeDES: "DES", HashTypeMD5: "MD5", HashTypeBlowfish: "blowfish", HashTypeBcrypt: "bcrypt", HashTypeEksblowfish: "eksblowfish", HashTypeSHA256: "SHA256", HashTypeSHA512: "SHA512", HashTypeYescrypt: "yescrypt", HashTypeGostYescrypt: "gost-yescrypt", HashTypeScrypt: "scrypt", } const ( NoPasswordLoginUser = "!" NoPasswordLoginService = "*" ) func NewPasswordHash(data string) PasswordHash { var out PasswordHash switch data { case NoPasswordLoginUser, NoPasswordLoginService: out.NoPasswordLogin = true return out } parts := strings.Split(data, "$") if len(parts) < 4 { return out } out.AlgoTypeRaw = parts[1] out.Salt = parts[len(parts)-2] out.Hash = parts[len(parts)-1] out.AlgoType = AlgoNameFromType(out.AlgoTypeRaw) if len(parts) > 4 { out.AlgoParam = parts[2] } return out } func AlgoNameFromType(data string) string { name, found := HashTypes[data] if found { return name } return "other" } type PasswordHash struct { AlgoTypeRaw string `json:"algo_type_raw,omitempty"` AlgoType string `json:"algo_type,omitempty"` AlgoParam string `json:"algo_param,omitempty"` //encoded (need to decode) Salt string `json:"salt,omitempty"` Hash string `json:"hash,omitempty"` NoPasswordLogin bool `json:"no_password_login"` } func (ref PasswordHash) UsesWeakAlgo() bool { switch ref.AlgoTypeRaw { case HashTypeDES, HashTypeMD5: return true } return false } func (ref ShadowRecord) LoginWithoutPassword() bool { if ref.PasswordRaw == "" { return true } return false } type ShadowFileInfo struct { Records []ShadowRecord `json:"records"` } const FieldNotSet = -1 type ShadowRecord struct { Username string PasswordRaw string Password PasswordHash LastChangeRaw int LastChangeDate time.Time MinimumAge int MaximumAge int WarningPeriod int InactiveDays int ExpirationRaw int ExpirationDate time.Time Reserved string RawData string } func ReadShadowFile(filePath string) (*ShadowFileInfo, error) { //todo: have an option to skip bad records data, err := os.ReadFile(filePath) if err != nil { return nil, err } return ReadShadowData(data) } func ReadShadowData(data []byte) (*ShadowFileInfo, error) { scanner := bufio.NewScanner(bytes.NewReader(data)) var info ShadowFileInfo for scanner.Scan() { record, err := ParseShadowRecord(scanner.Text()) if err != nil { return nil, err } info.Records = append(info.Records, record) } if err := scanner.Err(); err != nil { return nil, err } return &info, nil } func ParseShadowRecord(line string) (ShadowRecord, error) { line = strings.TrimSpace(line) record := ShadowRecord{ RawData: line, } parts := strings.Split(line, ":") if len(parts) != 9 { return record, errors.New("unexpected field count") } record.Username = parts[0] record.PasswordRaw = parts[1] record.Reserved = parts[8] intFields := [...]*int{ &record.LastChangeRaw, &record.MinimumAge, &record.MaximumAge, &record.WarningPeriod, &record.InactiveDays, &record.ExpirationRaw, } for idx, val := range intFields { field := parts[idx+2] if field == "" { *val = FieldNotSet } else { var err error *val, err = strconv.Atoi(field) if err != nil { return record, fmt.Errorf("error parsing field - %s (%v)", field, err) } } } record.Password = NewPasswordHash(record.PasswordRaw) var err error record.LastChangeDate, err = daysToDate(record.LastChangeRaw) if err != nil { return record, err } record.ExpirationDate, err = daysToDate(record.ExpirationRaw) if err != nil { return record, err } return record, nil } func daysToDate(days int) (time.Time, error) { if days == FieldNotSet { return time.Time{}, nil } return time.Date(1970, 0, 0, 0, 0, 0, 0, time.UTC).AddDate(0, 0, days), nil } //////////// type GroupRecord struct { Group string `json:"gid"` //group name Password string `json:"password"` //password hash, usually empty / unused (actual password hashes are in gshadow) GID int `json:"gid"` MembersRaw string `json:"members_raw"` Members []string `json:"members"` RawData string `json:"raw_data"` } type GroupFileInfo struct { Records []GroupRecord `json:"records"` } func ReadGroupFile(filePath string) (*GroupFileInfo, error) { //todo: have an option to skip bad records data, err := os.ReadFile(filePath) if err != nil { return nil, err } return ReadGroupData(data) } func ReadGroupData(data []byte) (*GroupFileInfo, error) { scanner := bufio.NewScanner(bytes.NewReader(data)) var info GroupFileInfo for scanner.Scan() { record, err := ParseGroupRecord(scanner.Text()) if err != nil { return nil, err } info.Records = append(info.Records, record) } if err := scanner.Err(); err != nil { return nil, err } return &info, nil } func ParseGroupRecord(line string) (GroupRecord, error) { line = strings.TrimSpace(line) record := GroupRecord{ RawData: line, } parts := strings.Split(line, ":") if len(parts) != 4 { return record, errors.New("unexpected field count") } record.Group = parts[0] record.Password = parts[1] record.MembersRaw = parts[3] if record.MembersRaw != "" { record.Members = strings.Split(record.MembersRaw, ",") } var err error record.GID, err = strconv.Atoi(parts[2]) if err != nil { return record, fmt.Errorf("error parsing GID field - %s (%v)", parts[2], err) } return record, nil } ================================================ FILE: pkg/system/architecture.go ================================================ package system type ArchName string const ( ArchNameUnknown ArchName = "unknown" ArchNameUnsupported ArchName = "unsupported" ArchName386 ArchName = "386" ArchNameAmd64 ArchName = "amd64" ArchNameArm32 ArchName = "armhf" ArchNameArm64 ArchName = "aarch64" ) type MachineName string const ( MachineNameNamei386 MachineName = "i386" MachineNameNamei586 MachineName = "i586" MachineNameNamei686 MachineName = "i686" MachineNameNamex86_64 MachineName = "x86_64" MachineNameNameArm MachineName = "armv7l" MachineNameNameArm64 MachineName = "aarch64" ) type ArchBits uint8 const ( ArchBits32 ArchBits = 32 ArchBits64 ArchBits = 64 ) type ArchFamily string const ( ArchFamilyX86 ArchFamily = "x86" ArchFamilyArm ArchFamily = "arm" ArchFamilyArm64 ArchFamily = "arm64" ) type ArchInfo struct { Name ArchName Family ArchFamily Bits ArchBits } var x86Family64Arch = ArchInfo{ Name: ArchNameAmd64, Family: ArchFamilyX86, Bits: ArchBits64, } var x86Family32Arch = ArchInfo{ Name: ArchName386, Family: ArchFamilyX86, Bits: ArchBits32, } var ArmFamily32Arch = ArchInfo{ Name: ArchNameArm32, Family: ArchFamilyArm, Bits: ArchBits32, } var ArmFamily64Arch = ArchInfo{ Name: ArchNameArm64, Family: ArchFamilyArm, Bits: ArchBits64, } var unsupportedArch = ArchInfo{ Name: ArchNameUnsupported, } var unknownArch = ArchInfo{ Name: ArchNameUnknown, } var archMap = map[MachineName]*ArchInfo{ MachineNameNamei386: &x86Family32Arch, MachineNameNamei586: &x86Family32Arch, MachineNameNamei686: &x86Family32Arch, MachineNameNamex86_64: &x86Family64Arch, MachineNameNameArm: &ArmFamily32Arch, MachineNameNameArm64: &ArmFamily64Arch, } func MachineToArchName(mtype string) ArchName { if archInfo, ok := archMap[MachineName(mtype)]; ok { return archInfo.Name } return ArchNameUnknown } func MachineToArch(mtype string) *ArchInfo { if archInfo, ok := archMap[MachineName(mtype)]; ok { return archInfo } return &unknownArch } ================================================ FILE: pkg/system/errors.go ================================================ package system import ( "errors" ) var ( ErrNotConfigured = errors.New("feature is not configured") ErrNoConfigs = errors.New("no kernel configs") ErrArchNotSupported = errors.New("unsupported architecture") ) ================================================ FILE: pkg/system/kernel_linux.go ================================================ package system import ( "bufio" "compress/gzip" "fmt" "os" "strings" ) type KernelFeatures struct { Raw map[string]string Error string } func (p *KernelFeatures) IsConfigured(name string) bool { if _, ok := p.Raw[name]; ok { return true } return false } func (p *KernelFeatures) RawValue(name string) (string, error) { if val, ok := p.Raw[name]; ok { return val, nil } return "", ErrNotConfigured } func (p *KernelFeatures) IsFlag(name string) (bool, error) { if val, ok := p.Raw[name]; ok { if (val == "y") || (val == "m") { return true, nil } return false, nil } return false, ErrNotConfigured } func (p *KernelFeatures) isFlagSet(name string, flag string) (bool, error) { if val, ok := p.Raw[name]; ok { if val == flag { return true, nil } return false, nil } return false, ErrNotConfigured } func (p *KernelFeatures) IsCompiled(name string) (bool, error) { return p.isFlagSet(name, "y") } func (p *KernelFeatures) IsLoadable(name string) (bool, error) { return p.isFlagSet(name, "m") } func NewKernelFeatures() (KernelFeatures, error) { return NewKernelFeaturesWithProps("") } func NewKernelFeaturesWithProps(location string) (KernelFeatures, error) { var kfeatures KernelFeatures if location == "" { bootConfigWithVersion := fmt.Sprintf("/boot/config-%s", defaultSysInfo.Machine) kernelConfigLocations := []string{ "/proc/config.gz", "/boot/config", } kernelConfigLocations = append(kernelConfigLocations, bootConfigWithVersion) location = findKernelConfigs(kernelConfigLocations) if location == "" { kfeatures.Error = ErrNoConfigs.Error() return kfeatures, ErrNoConfigs } } else { if !fileExists(location) { kfeatures.Error = os.ErrNotExist.Error() return kfeatures, os.ErrNotExist } } rawFeatures, err := readKernelFeatures(location) if err != nil { kfeatures.Error = err.Error() return kfeatures, err } kfeatures.Raw = rawFeatures return kfeatures, nil } var DefaultKernelFeatures, _ = NewKernelFeatures() func findKernelConfigs(locations []string) string { for _, loc := range locations { if fileExists(loc) { return loc } } return "" } func fileExists(filePath string) bool { fileInfo, err := os.Stat(filePath) if err == nil && (fileInfo.Mode().IsRegular()) { return true } return false } func readKernelFeatures(filename string) (map[string]string, error) { freader, err := os.Open(filename) if err != nil { return nil, err } defer freader.Close() areader, err := gzip.NewReader(freader) if err != nil { return nil, err } defer areader.Close() var lines []string scanner := bufio.NewScanner(areader) kernelFeatures := map[string]string{} for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line == "" { continue } if line[0] == '#' { //todo: extract section metadata from comments continue } lineParts := strings.Split(line, "=") if len(lineParts) == 2 { flagKey := strings.TrimSpace(lineParts[0]) flagValue := strings.TrimSpace(lineParts[1]) flagValue = strings.Trim(flagValue, "\"") lines = append(lines, line) kernelFeatures[flagKey] = flagValue } } if err = scanner.Err(); err != nil { //if err == bufio.ErrTooLong { // log.Println("line length error:", err) //} else { // log.Println("other scanner error:", err) //} return kernelFeatures, err } return kernelFeatures, nil } ================================================ FILE: pkg/system/nstring_int8.go ================================================ //go:build (linux && 386) || (linux && amd64) || (linux && arm64) // +build linux,386 linux,amd64 linux,arm64 package system func nativeCharsToString(cstr [65]int8) string { max := len(cstr) data := make([]byte, max) var pos int for ; (pos < max) && (cstr[pos] != 0); pos++ { data[pos] = byte(cstr[pos]) } return string(data[0:pos]) } ================================================ FILE: pkg/system/nstring_uint8.go ================================================ //go:build (linux && arm) || (linux && ppc64) || (linux && ppc64le) || s390x // +build linux,arm linux,ppc64 linux,ppc64le s390x package system func nativeCharsToString(cstr [65]uint8) string { max := len(cstr) data := make([]byte, max) var pos int for ; (pos < max) && (cstr[pos] != 0); pos++ { data[pos] = cstr[pos] } return string(data[0:pos]) } ================================================ FILE: pkg/system/os_release.go ================================================ package system import ( "bufio" "bytes" "fmt" "reflect" "strings" "log" ) const ( OSReleaseFile = "/etc/os-release" OSReleaseFileNew = "/usr/lib/os-release" LSBReleaseFile = "/etc/lsb-release" IssueFile = "/etc/issue" IssueNetFile = "/etc/issue.net" ) func IsOSReleaseFile(name string) bool { switch name { case OSReleaseFile, OSReleaseFileNew: return true default: return false } } //NOTE: //copied from https://github.com/docker/machine/blob/master/libmachine/provision/os_release.go // The /etc/os-release file contains operating system identification data // See http://www.freedesktop.org/software/systemd/man/os-release.html for more details // OsRelease reflects values in /etc/os-release // Values in this struct must always be string // or the reflection will not work properly. type OsRelease struct { AnsiColor string `osr:"ANSI_COLOR"` Name string `osr:"NAME"` Version string `osr:"VERSION"` Variant string `osr:"VARIANT"` VariantID string `osr:"VARIANT_ID"` ID string `osr:"ID"` IDLike string `osr:"ID_LIKE"` PrettyName string `osr:"PRETTY_NAME"` VersionID string `osr:"VERSION_ID"` HomeURL string `osr:"HOME_URL"` SupportURL string `osr:"SUPPORT_URL"` BugReportURL string `osr:"BUG_REPORT_URL"` } func stripQuotes(val string) string { if len(val) > 0 && val[0] == '"' { return val[1 : len(val)-1] } return val } func (osr *OsRelease) setIfPossible(key, val string) error { v := reflect.ValueOf(osr).Elem() for i := 0; i < v.NumField(); i++ { fieldValue := v.Field(i) fieldType := v.Type().Field(i) originalName := fieldType.Tag.Get("osr") if key == originalName && fieldValue.Kind() == reflect.String { fieldValue.SetString(val) return nil } } return fmt.Errorf("Couldn't set key %s, no corresponding struct field found", key) } func parseLine(osrLine string) (string, string, error) { if osrLine == "" { return "", "", nil } vals := strings.Split(osrLine, "=") if len(vals) != 2 { return "", "", fmt.Errorf("Expected %s to split by '=' char into two strings, instead got %d strings", osrLine, len(vals)) } key := vals[0] val := stripQuotes(vals[1]) return key, val, nil } func (osr *OsRelease) ParseOsRelease(osReleaseContents []byte) error { r := bytes.NewReader(osReleaseContents) scanner := bufio.NewScanner(r) for scanner.Scan() { key, val, err := parseLine(scanner.Text()) if err != nil { log.Printf("Warning: got an invalid line error parsing /etc/os-release: %s", err) continue } if err := osr.setIfPossible(key, val); err != nil { //log.Printf("Info: %s\n",err) //note: ignore, printing these messages causes more confusion... } } return nil } func NewOsRelease(contents []byte) (*OsRelease, error) { osr := &OsRelease{} if err := osr.ParseOsRelease(contents); err != nil { return nil, err } return osr, nil } //TODO: Add LSB Release and Issue file parsers /* cat /etc/os-release NAME="Ubuntu" VERSION="14.04.5 LTS, Trusty Tahr" ID=ubuntu ID_LIKE=debian PRETTY_NAME="Ubuntu 14.04.5 LTS" VERSION_ID="14.04" HOME_URL="http://www.ubuntu.com/" SUPPORT_URL="http://help.ubuntu.com/" BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/" cat /etc/os-release NAME="Alpine Linux" ID=alpine VERSION_ID=3.5.2 PRETTY_NAME="Alpine Linux v3.5" HOME_URL="http://alpinelinux.org" BUG_REPORT_URL="http://bugs.alpinelinux.org" cat /etc/os-release NAME="CentOS Linux" VERSION="7 (Core)" ID="centos" ID_LIKE="rhel fedora" VERSION_ID="7" PRETTY_NAME="CentOS Linux 7 (Core)" ANSI_COLOR="0;31" CPE_NAME="cpe:/o:centos:centos:7" HOME_URL="https://www.centos.org/" BUG_REPORT_URL="https://bugs.centos.org/" CENTOS_MANTISBT_PROJECT="CentOS-7" CENTOS_MANTISBT_PROJECT_VERSION="7" REDHAT_SUPPORT_PRODUCT="centos" REDHAT_SUPPORT_PRODUCT_VERSION="7" cat /etc/os-release PRETTY_NAME="Debian GNU/Linux 9 (stretch)" NAME="Debian GNU/Linux" VERSION_ID="9" VERSION="9 (stretch)" ID=debian HOME_URL="https://www.debian.org/" SUPPORT_URL="https://www.debian.org/support" BUG_REPORT_URL="https://bugs.debian.org/" */ ================================================ FILE: pkg/system/os_shells.go ================================================ package system import ( "bufio" "bytes" "os" "strings" "github.com/slimtoolkit/slim/pkg/util/fsutil" ) const ( OSShellsFile = "/etc/shells" ) // Common linux shell exe paths const ( OSShellAshExePath = "/bin/ash" OSShellBourneExePath = "/bin/sh" OSShellKornExePath = "/bin/ksh93" //usually a link to /bin/ksh93 OSShellRestrictedKornExePath = "/bin/rksh93" //usually a link to /etc/alternatives/ksh //which links to /bin/ksh93 OSShellKorn2ExePath = "/bin/ksh" OSShellBashExePath = "/bin/bash" //usually a link to /bin/bash OSShellRestrictedBashExePath = "/bin/rbash" OSShellDashExePath = "/bin/dash" OSShellZExePath = "/bin/zsh" //usually a link to /bin/zsh OSShellZ2ExePath = "/usr/bin/zsh" OSShellCExePath = "/bin/csh" OSShellTCExePath = "/bin/tcsh" OSShellFishExePath = "/usr/bin/fish" OSShellElvishExePath = "/usr/bin/elvish" OSShellXonshExePath = "/usr/bin/xonsh" OSShellYashExePath = "/usr/bin/yash" OSShellGitShellExePath = "/usr/bin/git-shell" OSShellScreenExePath = "/usr/bin/screen" OSShellTmuxExePath = "/usr/bin/tmux" ) var OSShellReferences = map[string]string{ OSShellBourneExePath: OSShellBourneExePath, OSShellKornExePath: OSShellKornExePath, OSShellKorn2ExePath: OSShellKornExePath, OSShellRestrictedKornExePath: OSShellKornExePath, OSShellBashExePath: OSShellBashExePath, OSShellRestrictedBashExePath: OSShellBashExePath, OSShellDashExePath: OSShellDashExePath, OSShellZExePath: OSShellZExePath, OSShellZ2ExePath: OSShellZExePath, OSShellCExePath: OSShellCExePath, OSShellTCExePath: OSShellTCExePath, OSShellFishExePath: OSShellFishExePath, OSShellElvishExePath: OSShellElvishExePath, OSShellXonshExePath: OSShellXonshExePath, OSShellYashExePath: OSShellYashExePath, OSShellAshExePath: OSShellAshExePath, OSShellGitShellExePath: OSShellGitShellExePath, OSShellScreenExePath: OSShellScreenExePath, OSShellTmuxExePath: OSShellTmuxExePath, } var OSShells = map[string]OSShell{ OSShellBourneExePath: { FullName: "Bourne shell", ShortName: "sh", ExePath: OSShellBourneExePath, }, OSShellKornExePath: { FullName: "Korn shell", ShortName: "ksh", ExePath: OSShellKornExePath, }, OSShellBashExePath: { FullName: "Bash shell", ShortName: "bash", ExePath: OSShellBashExePath, }, OSShellDashExePath: { FullName: "Dash shell", ShortName: "dash", ExePath: OSShellDashExePath, }, OSShellZExePath: { FullName: "Z shell", ShortName: "zsh", ExePath: OSShellZExePath, }, OSShellCExePath: { FullName: "C shell", ShortName: "csh", ExePath: OSShellCExePath, }, OSShellTCExePath: { FullName: "TC shell", ShortName: "tcsh", ExePath: OSShellTCExePath, }, OSShellFishExePath: { FullName: "Fish shell", ShortName: "fish", ExePath: OSShellFishExePath, }, OSShellElvishExePath: { FullName: "Elvish shell", ShortName: "elvish", ExePath: OSShellElvishExePath, }, OSShellXonshExePath: { FullName: "Xonsh shell", ShortName: "xonsh", ExePath: OSShellXonshExePath, }, OSShellYashExePath: { FullName: "Yash shell", ShortName: "yash", ExePath: OSShellYashExePath, }, OSShellAshExePath: { FullName: "Ash shell", ShortName: "ash", ExePath: OSShellAshExePath, }, //Restricted login shell for Git-only SSH access //installed with git OSShellGitShellExePath: { FullName: "Git shell", ShortName: "git-shell", ExePath: OSShellGitShellExePath, }, OSShellScreenExePath: { FullName: "Screen", ShortName: "screen", ExePath: OSShellScreenExePath, }, OSShellTmuxExePath: { FullName: "Tmux", ShortName: "tmux", ExePath: OSShellTmuxExePath, }, } type OSShell struct { FullName string `json:"full_name"` ShortName string `json:"short_name,omitempty"` ExePath string `json:"exe_path"` LinkPath string `json:"link_path,omitempty"` Reference string `json:"reference,omitempty"` Verified bool `json:"verified,omitempty"` } func IsOSShellsFile(name string) bool { if name == OSShellsFile { return true } return false } func IsShellExePath(name string) bool { _, found := OSShellReferences[name] if found { return true } return false } func LookupShellByExePath(name string) *OSShell { mainPath, found := OSShellReferences[name] if !found { return nil } if info, found := OSShells[mainPath]; found { if name != mainPath { info.Reference = name } return &info } return nil } func parseOSShellsLine(line string) string { line = strings.TrimSpace(line) if strings.HasPrefix(line, "#") { return "" } return line } func ParseOSShells(raw []byte) []*OSShell { var shells []*OSShell r := bytes.NewReader(raw) scanner := bufio.NewScanner(r) for scanner.Scan() { exePath := parseOSShellsLine(scanner.Text()) if exePath != "" { shellInfo := LookupShellByExePath(exePath) if shellInfo != nil { info := *shellInfo shells = append(shells, &info) } else { unknown := &OSShell{ FullName: "Unknown shell", ExePath: exePath, } shells = append(shells, unknown) } } } return shells } func NewOSShellsFromData(raw []byte) ([]*OSShell, error) { shells := ParseOSShells(raw) return shells, nil } func NewOSShells(verify bool) ([]*OSShell, error) { raw, err := os.ReadFile(OSShellsFile) if err == nil { return nil, err } result, err := NewOSShellsFromData(raw) if err != nil { return nil, err } if verify { for _, info := range result { if fsutil.Exists(info.ExePath) && !fsutil.IsDir(info.ExePath) { info.Verified = true if fsutil.IsSymlink(info.ExePath) { if linkRef, err := os.Readlink(info.ExePath); err == nil { info.LinkPath = linkRef } } } } } return result, nil } ================================================ FILE: pkg/system/syscalls.go ================================================ package system //NOTES: //* syscall constants in the "syscall" package are nice, but some syscalls there are missing //* future versions will include more than just the syscall name //* 32bit (x86/i386) and 64bit (x86_64) syscall numbers are different const ( SyscallX86MinNum = 0 SyscallX86UnknownNum = -1 SyscallX86UnknownName = "unknown_syscall" ) type NumberResolverFunc func(uint32) string type NameResolverFunc func(string) (uint32, bool) func CallNumberResolver(arch ArchName) NumberResolverFunc { switch arch { case ArchName386: return callNameX86Family32 case ArchNameAmd64: return callNameX86Family64 case ArchNameArm32: return callNameArmFamily32 case ArchNameArm64: return callNameArmFamily64 default: return nil } } func CallNameResolver(arch ArchName) NameResolverFunc { switch arch { case ArchName386: return callNumberX86Family32 case ArchNameAmd64: return callNumberX86Family64 case ArchNameArm32: return callNumberArmFamily32 case ArchNameArm64: return callNumberArmFamily64 default: return nil } } ================================================ FILE: pkg/system/syscalls_armf32.go ================================================ package system const ( SyscallArmUnknownNum = -1 SyscallArmUnknownName = "unknown_syscall" SyscallArmMinNum32 = 0 SyscallArmMaxNum32 = 377 SyscallArmLastName32 = "process_vm_writev" ) var syscallNumTableArmFamily32 = map[uint32]string{ 0: "restart_syscall", 1: "exit", 2: "fork", 3: "read", 4: "write", 5: "open", 6: "close", 8: "creat", 9: "link", 10: "unlink", 11: "execve", 12: "chdir", 13: "time", 14: "mknod", 15: "chmod", 16: "lchown", 19: "lseek", 20: "getpid", 21: "mount", 22: "umount", 23: "setuid", 24: "getuid", 25: "stime", 26: "ptrace", 27: "alarm", 29: "pause", 30: "utime", 33: "access", 34: "nice", 36: "sync", 37: "kill", 38: "rename", 39: "mkdir", 40: "rmdir", 41: "dup", 42: "pipe", 43: "times", 45: "brk", 46: "setgid", 47: "getgid", 49: "geteuid", 50: "getegid", 51: "acct", 52: "umount2", 54: "ioctl", 55: "fcntl", 57: "setpgid", 60: "umask", 61: "chroot", 62: "ustat", 63: "dup2", 64: "getppid", 65: "getpgrp", 66: "setsid", 67: "sigaction", 70: "setreuid", 71: "setregid", 72: "sigsuspend", 73: "sigpending", 74: "sethostname", 75: "setrlimit", 76: "getrlimit", 77: "getrusage", 78: "gettimeofday", 79: "settimeofday", 80: "getgroups", 81: "setgroups", 82: "select", 83: "symlink", 85: "readlink", 86: "uselib", 87: "swapon", 88: "reboot", 89: "readdir", 90: "mmap", 91: "munmap", 92: "truncate", 93: "ftruncate", 94: "fchmod", 95: "fchown", 96: "getpriority", 97: "setpriority", 99: "statfs", 100: "fstatfs", 102: "socketcall", 103: "syslog", 104: "setitimer", 105: "getitimer", 106: "stat", 107: "lstat", 108: "fstat", 111: "vhangup", 113: "syscall", 114: "wait4", 115: "swapoff", 116: "sysinfo", 117: "ipc", 118: "fsync", 119: "sigreturn", 120: "clone", 121: "setdomainname", 122: "uname", 124: "adjtimex", 125: "mprotect", 126: "sigprocmask", 128: "init_module", 129: "delete_module", 131: "quotactl", 132: "getpgid", 133: "fchdir", 134: "bdflush", 135: "sysfs", 136: "personality", 138: "setfsuid", 139: "setfsgid", 140: "_llseek", 141: "getdents", 142: "_newselect", 143: "flock", 144: "msync", 145: "readv", 146: "writev", 147: "getsid", 148: "fdatasync", 149: "_sysctl", 150: "mlock", 151: "munlock", 152: "mlockall", 153: "munlockall", 154: "sched_setparam", 155: "sched_getparam", 156: "sched_setscheduler", 157: "sched_getscheduler", 158: "sched_yield", 159: "sched_get_priority_max", 160: "sched_get_priority_min", 161: "sched_rr_get_interval", 162: "nanosleep", 163: "mremap", 164: "setresuid", 165: "getresuid", 168: "poll", 169: "nfsservctl", 170: "setresgid", 171: "getresgid", 172: "prctl", 173: "rt_sigreturn", 174: "rt_sigaction", 175: "rt_sigprocmask", 176: "rt_sigpending", 177: "rt_sigtimedwait", 178: "rt_sigqueueinfo", 179: "rt_sigsuspend", 180: "pread64", 181: "pwrite64", 182: "chown", 183: "getcwd", 184: "capget", 185: "capset", 186: "sigaltstack", 187: "sendfile", 190: "vfork", 191: "ugetrlimit", 192: "mmap2", 193: "truncate64", 194: "ftruncate64", 195: "stat64", 196: "lstat64", 197: "fstat64", 198: "lchown32", 199: "getuid32", 200: "getgid32", 201: "geteuid32", 202: "getegid32", 203: "setreuid32", 204: "setregid32", 205: "getgroups32", 206: "setgroups32", 207: "fchown32", 208: "setresuid32", 209: "getresuid32", 210: "setresgid32", 211: "getresgid32", 212: "chown32", 213: "setuid32", 214: "setgid32", 215: "setfsuid32", 216: "setfsgid32", 217: "getdents64", 218: "pivot_root", 219: "mincore", 220: "madvise", 221: "fcntl64", 224: "gettid", 225: "readahead", 226: "setxattr", 227: "lsetxattr", 228: "fsetxattr", 229: "getxattr", 230: "lgetxattr", 231: "fgetxattr", 232: "listxattr", 233: "llistxattr", 234: "flistxattr", 235: "removexattr", 236: "lremovexattr", 237: "fremovexattr", 238: "tkill", 239: "sendfile64", 240: "futex", 241: "sched_setaffinity", 242: "sched_getaffinity", 243: "io_setup", 244: "io_destroy", 245: "io_getevents", 246: "io_submit", 247: "io_cancel", 248: "exit_group", 249: "lookup_dcookie", 250: "epoll_create", 251: "epoll_ctl", 252: "epoll_wait", 253: "remap_file_pages", 256: "set_tid_address", 257: "timer_create", 258: "timer_settime", 259: "timer_gettime", 260: "timer_getoverrun", 261: "timer_delete", 262: "clock_settime", 263: "clock_gettime", 264: "clock_getres", 265: "clock_nanosleep", 266: "statfs64", 267: "fstatfs64", 268: "tgkill", 269: "utimes", 270: "arm_fadvise64_64", 271: "pciconfig_iobase", 272: "pciconfig_read", 273: "pciconfig_write", 274: "mq_open", 275: "mq_unlink", 276: "mq_timedsend", 277: "mq_timedreceive", 278: "mq_notify", 279: "mq_getsetattr", 280: "waitid", 281: "socket", 282: "bind", 283: "connect", 284: "listen", 285: "accept", 286: "getsockname", 287: "getpeername", 288: "socketpair", 289: "send", 290: "sendto", 291: "recv", 292: "recvfrom", 293: "shutdown", 294: "setsockopt", 295: "getsockopt", 296: "sendmsg", 297: "recvmsg", 298: "semop", 299: "semget", 300: "semctl", 301: "msgsnd", 302: "msgrcv", 303: "msgget", 304: "msgctl", 305: "shmat", 306: "shmdt", 307: "shmget", 308: "shmctl", 309: "add_key", 310: "request_key", 311: "keyctl", 312: "semtimedop", 313: "vserver", 314: "ioprio_set", 315: "ioprio_get", 316: "inotify_init", 317: "inotify_add_watch", 318: "inotify_rm_watch", 319: "mbind", 320: "get_mempolicy", 321: "set_mempolicy", 322: "openat", 323: "mkdirat", 324: "mknodat", 325: "fchownat", 326: "futimesat", 327: "fstatat64", 328: "unlinkat", 329: "renameat", 330: "linkat", 331: "symlinkat", 332: "readlinkat", 333: "fchmodat", 334: "faccessat", 335: "pselect6", 336: "ppoll", 337: "unshare", 338: "set_robust_list", 339: "get_robust_list", 340: "splice", 341: "sync_file_range2", 342: "tee", 343: "vmsplice", 344: "move_pages", 345: "getcpu", 346: "epoll_pwait", 347: "kexec_load", 348: "utimensat", 349: "signalfd", 350: "timerfd_create", 351: "eventfd", 352: "fallocate", 353: "timerfd_settime", 354: "timerfd_gettime", 355: "signalfd4", 356: "eventfd2", 357: "epoll_create1", 358: "dup3", 359: "pipe2", 360: "inotify_init1", 361: "preadv", 362: "pwritev", 363: "rt_tgsigqueueinfo", 364: "perf_event_open", 365: "recvmmsg", 366: "accept4", 367: "fanotify_init", 368: "fanotify_mark", 369: "prlimit64", 370: "name_to_handle_at", 371: "open_by_handle_at", 372: "clock_adjtime", 373: "syncfs", 374: "sendmmsg", 375: "setns", 376: "process_vm_readv", 377: "process_vm_writev", } func callNameArmFamily32(num uint32) string { if num > SyscallArmMaxNum32 { return SyscallArmUnknownName } return syscallNumTableArmFamily32[num] } func callNumTableIsOkArmFamily32() bool { if (len(syscallNumTableArmFamily32) == SyscallArmMaxNum32+1) && syscallNumTableArmFamily32[SyscallArmMaxNum32] == SyscallArmLastName32 { return true } return false } func callNumberArmFamily32(name string) (uint32, bool) { num, ok := syscallNameTableArmFamily32[name] return num, ok } var syscallNameTableArmFamily32 map[string]uint32 func init() { syscallNameTableArmFamily32 = make(map[string]uint32, len(syscallNumTableArmFamily32)) for callNum, callName := range syscallNumTableArmFamily32 { syscallNameTableArmFamily32[callName] = uint32(callNum) } } ================================================ FILE: pkg/system/syscalls_armf64.go ================================================ package system const ( SyscallArmUnknownNum64 = -1 SyscallArmUnknownName64 = "unknown_syscall" SyscallArmMinNum64 = 0 SyscallArmMaxNum64 = 294 SyscallArmLastName64 = "kexec_file_load" ) var syscallNumTableArmFamily64 = map[uint32]string{ 0: "io_setup", 1: "io_destroy", 2: "io_submit", 3: "io_cancel", 4: "io_getevents", 5: "setxattr", 6: "lsetxattr", 7: "fsetxattr", 8: "getxattr", 9: "lgetxattr", 10: "fgetxattr", 11: "listxattr", 12: "llistxattr", 13: "flistxattr", 14: "removexattr", 15: "lremovexattr", 16: "fremovexattr", 17: "getcwd", 18: "lookup_dcookie", 19: "eventfd2", 20: "epoll_create1", 21: "epoll_ctl", 22: "epoll_pwait", 23: "dup", 24: "dup3", 25: "fcntl", 26: "inotify_init1", 27: "inotify_add_watch", 28: "inotify_rm_watch", 29: "ioctl", 30: "ioprio_set", 31: "ioprio_get", 32: "flock", 33: "mknodat", 34: "mkdirat", 35: "unlinkat", 36: "symlinkat", 37: "linkat", 38: "renameat", 39: "umount2", 40: "mount", 41: "pivot_root", 42: "nfsservctl", 43: "statfs", 44: "fstatfs", 45: "truncate", 46: "ftruncate", 47: "fallocate", 48: "faccessat", 49: "chdir", 50: "fchdir", 51: "chroot", 52: "fchmod", 53: "fchmodat", 54: "fchownat", 55: "fchown", 56: "openat", 57: "close", 58: "vhangup", 59: "pipe2", 60: "quotactl", 61: "getdents64", 62: "lseek", 63: "read", 64: "write", 65: "readv", 66: "writev", 67: "pread64", 68: "pwrite64", 69: "preadv", 70: "pwritev", 71: "sendfile", 72: "pselect6", 73: "ppoll", 74: "signalfd4", 75: "vmsplice", 76: "splice", 77: "tee", 78: "readlinkat", 79: "fstatat", 80: "fstat", 81: "sync", 82: "fsync", 83: "fdatasync", 84: "sync_file_range", 85: "timerfd_create", 86: "timerfd_settime", 87: "timerfd_gettime", 88: "utimensat", 89: "acct", 90: "capget", 91: "capset", 92: "personality", 93: "exit", 94: "exit_group", 95: "waitid", 96: "set_tid_address", 97: "unshare", 98: "futex", 99: "set_robust_list", 100: "get_robust_list", 101: "nanosleep", 102: "getitimer", 103: "setitimer", 104: "kexec_load", 105: "init_module", 106: "delete_module", 107: "timer_create", 108: "timer_gettime", 109: "timer_getoverrun", 110: "timer_settime", 111: "timer_delete", 112: "clock_settime", 113: "clock_gettime", 114: "clock_getres", 115: "clock_nanosleep", 116: "syslog", 117: "ptrace", 118: "sched_setparam", 119: "sched_setscheduler", 120: "sched_getscheduler", 121: "sched_getparam", 122: "sched_setaffinity", 123: "sched_getaffinity", 124: "sched_yield", 125: "sched_get_priority_max", 126: "sched_get_priority_min", 127: "sched_rr_get_interval", 128: "restart_syscall", 129: "kill", 130: "tkill", 131: "tgkill", 132: "sigaltstack", 133: "rt_sigsuspend", 134: "rt_sigaction", 135: "rt_sigprocmask", 136: "rt_sigpending", 137: "rt_sigtimedwait", 138: "rt_sigqueueinfo", 139: "rt_sigreturn", 140: "setpriority", 141: "getpriority", 142: "reboot", 143: "setregid", 144: "setgid", 145: "setreuid", 146: "setuid", 147: "setresuid", 148: "getresuid", 149: "setresgid", 150: "getresgid", 151: "setfsuid", 152: "setfsgid", 153: "times", 154: "setpgid", 155: "getpgid", 156: "getsid", 157: "setsid", 158: "getgroups", 159: "setgroups", 160: "uname", 161: "sethostname", 162: "setdomainname", 163: "getrlimit", 164: "setrlimit", 165: "getrusage", 166: "umask", 167: "prctl", 168: "getcpu", 169: "gettimeofday", 170: "settimeofday", 171: "adjtimex", 172: "getpid", 173: "getppid", 174: "getuid", 175: "geteuid", 176: "getgid", 177: "getegid", 178: "gettid", 179: "sysinfo", 180: "mq_open", 181: "mq_unlink", 182: "mq_timedsend", 183: "mq_timedreceive", 184: "mq_notify", 185: "mq_getsetattr", 186: "msgget", 187: "msgctl", 188: "msgrcv", 189: "msgsnd", 190: "semget", 191: "semctl", 192: "semtimedop", 193: "semop", 194: "shmget", 195: "shmctl", 196: "shmat", 197: "shmdt", 198: "socket", 199: "socketpair", 200: "bind", 201: "listen", 202: "accept", 203: "connect", 204: "getsockname", 205: "getpeername", 206: "sendto", 207: "recvfrom", 208: "setsockopt", 209: "getsockopt", 210: "shutdown", 211: "sendmsg", 212: "recvmsg", 213: "readahead", 214: "brk", 215: "munmap", 216: "mremap", 217: "add_key", 218: "request_key", 219: "keyctl", 220: "clone", 221: "execve", 222: "mmap", 223: "fadvise64", 224: "swapon", 225: "swapoff", 226: "mprotect", 227: "msync", 228: "mlock", 229: "munlock", 230: "mlockall", 231: "munlockall", 232: "mincore", 233: "madvise", 234: "remap_file_pages", 235: "mbind", 236: "get_mempolicy", 237: "set_mempolicy", 238: "migrate_pages", 239: "move_pages", 240: "rt_tgsigqueueinfo", 241: "perf_event_open", 242: "accept4", 243: "recvmmsg", 244: "arch_specific_syscall", 260: "wait4", 261: "prlimit64", 262: "fanotify_init", 263: "fanotify_mark", 264: "name_to_handle_at", 265: "open_by_handle_at", 266: "clock_adjtime", 267: "syncfs", 268: "setns", 269: "sendmmsg", 270: "process_vm_readv", 271: "process_vm_writev", 272: "kcmp", 273: "finit_module", 274: "sched_setattr", 275: "sched_getattr", 276: "renameat2", 277: "seccomp", 278: "getrandom", 279: "memfd_create", 280: "bpf", 281: "execveat", 282: "userfaultfd", 283: "membarrier", 284: "mlock2", 285: "copy_file_range", 286: "preadv2", 287: "pwritev2", 288: "pkey_mprotect", 289: "pkey_alloc", 290: "pkey_free", 291: "statx", 292: "io_pgetevents", 293: "rseq", 294: "kexec_file_load", } func callNameArmFamily64(num uint32) string { if num > SyscallArmMaxNum64 { return SyscallArmUnknownName64 } return syscallNumTableArmFamily64[num] } func callNumTableIsOkArmFamily64() bool { if (len(syscallNumTableArmFamily64) == SyscallArmMaxNum64+1) && syscallNumTableArmFamily64[SyscallArmMaxNum64] == SyscallArmLastName64 { return true } return false } func callNumberArmFamily64(name string) (uint32, bool) { num, ok := syscallNameTableArmFamily64[name] return num, ok } var syscallNameTableArmFamily64 map[string]uint32 func init() { syscallNameTableArmFamily64 = make(map[string]uint32, len(syscallNumTableArmFamily64)) for callNum, callName := range syscallNumTableArmFamily64 { syscallNameTableArmFamily64[callName] = uint32(callNum) } } ================================================ FILE: pkg/system/syscalls_x86f32.go ================================================ package system const ( SyscallX86MaxNum32 = 435 SyscallX86LastName32 = "clone3" ) // line numbers are aligned with the syscall number (-10) var syscallNumTableX86Family32 = [...]string{ "restart_syscall", "exit", "fork", "read", "write", "open", "close", "waitpid", "creat", "link", "unlink", "execve", "chdir", "time", "mknod", "chmod", "lchown", "break", "oldstat", "lseek", "getpid", "mount", "umount", "setuid", "getuid", "stime", "ptrace", "alarm", "oldfstat", "pause", "utime", "stty", "gtty", "access", "nice", "ftime", "sync", "kill", "rename", "mkdir", "rmdir", "dup", "pipe", "times", "prof", "brk", "setgid", "getgid", "signal", "geteuid", "getegid", "acct", "umount2", "lock", "ioctl", "fcntl", "mpx", "setpgid", "ulimit", "oldolduname", "umask", "chroot", "ustat", "dup2", "getppid", "getpgrp", "setsid", "sigaction", "sgetmask", "ssetmask", "setreuid", "setregid", "sigsuspend", "sigpending", "sethostname", "setrlimit", "getrlimit", "getrusage", "gettimeofday", "settimeofday", "getgroups", "setgroups", "select", "symlink", "oldlstat", "readlink", "uselib", "swapon", "reboot", "readdir", "mmap", "munmap", "truncate", "ftruncate", "fchmod", "fchown", "getpriority", "setpriority", "profil", "statfs", "fstatfs", "ioperm", "socketcall", "syslog", "setitimer", "getitimer", "stat", "lstat", "fstat", "olduname", "iopl", "vhangup", "idle", "vm86old", "wait4", "swapoff", "sysinfo", "ipc", "fsync", "sigreturn", "clone", "setdomainname", "uname", "modify_ldt", "adjtimex", "mprotect", "sigprocmask", "create_module", "init_module", "delete_module", "get_kernel_syms", "quotactl", "getpgid", "fchdir", "bdflush", "sysfs", "personality", "afs_syscall", "setfsuid", "setfsgid", "_llseek", "getdents", "_newselect", "flock", "msync", "readv", "writev", "getsid", "fdatasync", "_sysctl", "mlock", "munlock", "mlockall", "munlockall", "sched_setparam", "sched_getparam", "sched_setscheduler", "sched_getscheduler", "sched_yield", "sched_get_priority_max", "sched_get_priority_min", "sched_rr_get_interval", "nanosleep", "mremap", "setresuid", "getresuid", "vm86", "query_module", "poll", "nfsservctl", "setresgid", "getresgid", "prctl", "rt_sigreturn", "rt_sigaction", "rt_sigprocmask", "rt_sigpending", "rt_sigtimedwait", "rt_sigqueueinfo", "rt_sigsuspend", "pread64", "pwrite64", "chown", "getcwd", "capget", "capset", "sigaltstack", "sendfile", "getpmsg", "putpmsg", "vfork", "ugetrlimit", "mmap2", "truncate64", "ftruncate64", "stat64", "lstat64", "fstat64", "lchown32", "getuid32", "getgid32", "geteuid32", "getegid32", "setreuid32", "setregid32", "getgroups32", "setgroups32", "fchown32", "setresuid32", "getresuid32", "setresgid32", "getresgid32", "chown32", "setuid32", "setgid32", "setfsuid32", "setfsgid32", "pivot_root", "mincore", "madvise", "getdents64", "fcntl64", "unused.222", //222 - unused "unused.223", //223 - unused "gettid", "readahead", "setxattr", "lsetxattr", "fsetxattr", "getxattr", "lgetxattr", "fgetxattr", "listxattr", "llistxattr", "flistxattr", "removexattr", "lremovexattr", "fremovexattr", "tkill", "sendfile64", "futex", "sched_setaffinity", "sched_getaffinity", "set_thread_area", "get_thread_area", "io_setup", "io_destroy", "io_getevents", "io_submit", "io_cancel", "fadvise64", "unused.251", //251 - unused (available for reuse) was used by sys_set_zone_reclaim "exit_group", "lookup_dcookie", "epoll_create", "epoll_ctl", "epoll_wait", "remap_file_pages", "set_tid_address", "timer_create", "timer_settime", "timer_gettime", "timer_getoverrun", "timer_delete", "clock_settime", "clock_gettime", "clock_getres", "clock_nanosleep", "statfs64", "fstatfs64", "tgkill", "utimes", "fadvise64_64", "vserver", "mbind", "get_mempolicy", "set_mempolicy", "mq_open", "mq_unlink", "mq_timedsend", "mq_timedreceive", "mq_notify", "mq_getsetattr", "kexec_load", "waitid", "setaltroot", //285 - sys_setaltroot (sort of used :)) "add_key", "request_key", "keyctl", "ioprio_set", "ioprio_get", "inotify_init", "inotify_add_watch", "inotify_rm_watch", "migrate_pages", "openat", "mkdirat", "mknodat", "fchownat", "futimesat", "fstatat64", "unlinkat", "renameat", "linkat", "symlinkat", "readlinkat", "fchmodat", "faccessat", "pselect6", "ppoll", "unshare", "set_robust_list", "get_robust_list", "splice", "sync_file_range", "tee", "vmsplice", "move_pages", "getcpu", "epoll_pwait", "utimensat", "signalfd", "timerfd_create", "eventfd", "fallocate", "timerfd_settime", "timerfd_gettime", "signalfd4", "eventfd2", "epoll_create1", "dup3", "pipe2", "inotify_init1", "preadv", "pwritev", "rt_tgsigqueueinfo", "perf_event_open", "recvmmsg", "fanotify_init", "fanotify_mark", "prlimit64", "name_to_handle_at", "open_by_handle_at", "clock_adjtime", "syncfs", "sendmmsg", "setns", "process_vm_readv", "process_vm_writev", "kcmp", "finit_module", "sched_setattr", "sched_getattr", "renameat2", "seccomp", "getrandom", "memfd_create", "bpf", "execveat", //358 "socket", "socketpair", "bind", "connect", "listen", "accept4", "getsockopt", "setsockopt", "getsockname", "getpeername", "sendto", "sendmsg", "recvfrom", "recvmsg", "shutdown", "userfaultfd", "membarrier", "mlock2", "copy_file_range", "preadv2", "pwritev2", "pkey_mprotect", "pkey_alloc", "pkey_free", "statx", "arch_prctl", "io_pgetevents", "rseq", "unused.387", "unused.388", "unused.389", "unused.390", "unused.391", "unused.392", "semget", "semctl", "shmget", "shmctl", "shmat", "shmdt", "msgget", "msgsnd", "msgrcv", "msgctl", "clock_gettime64", "clock_settime64", "clock_adjtime64", "clock_getres_time64", "clock_nanosleep_time64", "timer_gettime64", "timer_settime64", "timerfd_gettime64", "timerfd_settime64", "utimensat_time64", "pselect6_time64", "ppoll_time64", "unused.415", "io_pgetevents_time64", "recvmmsg_time64", "mq_timedsend_time64", "mq_timedreceive_time64", "semtimedop_time64", "rt_sigtimedwait_time64", "futex_time64", "sched_rr_get_interval_time64", "pidfd_send_signal", "io_uring_setup", "io_uring_enter", "io_uring_register", "open_tree", "move_mount", "fsopen", "fsconfig", "fsmount", "fspick", "pidfd_open", "clone3", //435 } func callNameX86Family32(num uint32) string { if num > SyscallX86MaxNum32 { return SyscallX86UnknownName } return syscallNumTableX86Family32[num] } func callNumTableIsOkX86Family32() bool { if (len(syscallNumTableX86Family32) == SyscallX86MaxNum32+1) && syscallNumTableX86Family32[SyscallX86MaxNum32] == SyscallX86LastName32 { return true } return false } func callNumberX86Family32(name string) (uint32, bool) { num, ok := syscallNameTableX86Family32[name] return num, ok } var syscallNameTableX86Family32 map[string]uint32 func init() { syscallNameTableX86Family32 = make(map[string]uint32, len(syscallNumTableX86Family32)) for callNum, callName := range syscallNumTableX86Family32 { syscallNameTableX86Family32[callName] = uint32(callNum) } } ================================================ FILE: pkg/system/syscalls_x86f64.go ================================================ package system const ( SyscallX86MaxNum64 = 435 SyscallX86LastName64 = "clone3" ) // line numbers are aligned with the syscall number (-10) var syscallNumTableX86Family64 = [...]string{ "read", "write", "open", "close", "stat", "fstat", "lstat", "poll", "lseek", "mmap", "mprotect", "munmap", "brk", "rt_sigaction", "rt_sigprocmask", "rt_sigreturn", "ioctl", "pread64", "pwrite64", "readv", "writev", "access", "pipe", "select", "sched_yield", "mremap", "msync", "mincore", "madvise", "shmget", "shmat", "shmctl", "dup", "dup2", "pause", "nanosleep", "getitimer", "alarm", "setitimer", "getpid", "sendfile", "socket", "connect", "accept", "sendto", "recvfrom", "sendmsg", "recvmsg", "shutdown", "bind", "listen", "getsockname", "getpeername", "socketpair", "setsockopt", "getsockopt", "clone", "fork", "vfork", "execve", "exit", "wait4", "kill", "uname", "semget", "semop", "semctl", "shmdt", "msgget", "msgsnd", "msgrcv", "msgctl", "fcntl", "flock", "fsync", "fdatasync", "truncate", "ftruncate", "getdents", "getcwd", "chdir", "fchdir", "rename", "mkdir", "rmdir", "creat", "link", "unlink", "symlink", "readlink", "chmod", "fchmod", "chown", "fchown", "lchown", "umask", "gettimeofday", "getrlimit", "getrusage", "sysinfo", "times", "ptrace", "getuid", "syslog", "getgid", "setuid", "setgid", "geteuid", "getegid", "setpgid", "getppid", "getpgrp", "setsid", "setreuid", "setregid", "getgroups", "setgroups", "setresuid", "getresuid", "setresgid", "getresgid", "getpgid", "setfsuid", "setfsgid", "getsid", "capget", "capset", "rt_sigpending", "rt_sigtimedwait", "rt_sigqueueinfo", "rt_sigsuspend", "sigaltstack", "utime", "mknod", "uselib", "personality", "ustat", "statfs", "fstatfs", "sysfs", "getpriority", "setpriority", "sched_setparam", "sched_getparam", "sched_setscheduler", "sched_getscheduler", "sched_get_priority_max", "sched_get_priority_min", "sched_rr_get_interval", "mlock", "munlock", "mlockall", "munlockall", "vhangup", "modify_ldt", "pivot_root", "_sysctl", "prctl", "arch_prctl", "adjtimex", "setrlimit", "chroot", "sync", "acct", "settimeofday", "mount", "umount2", "swapon", "swapoff", "reboot", "sethostname", "setdomainname", "iopl", "ioperm", "create_module", "init_module", "delete_module", "get_kernel_syms", "query_module", "quotactl", "nfsservctl", "getpmsg", "putpmsg", "afs_syscall", "tuxcall", "security", "gettid", "readahead", "setxattr", "lsetxattr", "fsetxattr", "getxattr", "lgetxattr", "fgetxattr", "listxattr", "llistxattr", "flistxattr", "removexattr", "lremovexattr", "fremovexattr", "tkill", "time", "futex", "sched_setaffinity", "sched_getaffinity", "set_thread_area", "io_setup", "io_destroy", "io_getevents", "io_submit", "io_cancel", "get_thread_area", "lookup_dcookie", "epoll_create", "epoll_ctl_old", "epoll_wait_old", "remap_file_pages", "getdents64", "set_tid_address", "restart_syscall", "semtimedop", "fadvise64", "timer_create", "timer_settime", "timer_gettime", "timer_getoverrun", "timer_delete", "clock_settime", "clock_gettime", "clock_getres", "clock_nanosleep", "exit_group", "epoll_wait", "epoll_ctl", "tgkill", "utimes", "vserver", "mbind", "set_mempolicy", "get_mempolicy", "mq_open", "mq_unlink", "mq_timedsend", "mq_timedreceive", "mq_notify", "mq_getsetattr", "kexec_load", "waitid", "add_key", "request_key", "keyctl", "ioprio_set", "ioprio_get", "inotify_init", "inotify_add_watch", "inotify_rm_watch", "migrate_pages", "openat", "mkdirat", "mknodat", "fchownat", "futimesat", "newfstatat", "unlinkat", "renameat", "linkat", "symlinkat", "readlinkat", "fchmodat", "faccessat", "pselect6", "ppoll", "unshare", "set_robust_list", "get_robust_list", "splice", "tee", "sync_file_range", "vmsplice", "move_pages", "utimensat", "epoll_pwait", "signalfd", "timerfd_create", "eventfd", "fallocate", "timerfd_settime", "timerfd_gettime", "accept4", "signalfd4", "eventfd2", "epoll_create1", "dup3", "pipe2", "inotify_init1", "preadv", "pwritev", "rt_tgsigqueueinfo", "perf_event_open", "recvmmsg", "fanotify_init", "fanotify_mark", "prlimit64", "name_to_handle_at", "open_by_handle_at", "clock_adjtime", "syncfs", "sendmmsg", "setns", "getcpu", "process_vm_readv", "process_vm_writev", "kcmp", "finit_module", "sched_setattr", "sched_getattr", "renameat2", "seccomp", "getrandom", "memfd_create", "kexec_file_load", "bpf", "execveat", "userfaultfd", "membarrier", "mlock2", "copy_file_range", "preadv2", "pwritev2", "pkey_mprotect", "pkey_alloc", "pkey_free", "statx", "io_pgetevents", "rseq", "reserved.335", "reserved.336", "reserved.337", "reserved.338", "reserved.339", "reserved.340", "reserved.341", "reserved.342", "reserved.343", "reserved.344", "reserved.345", "reserved.346", "reserved.347", "reserved.348", "reserved.349", "reserved.350", "reserved.351", "reserved.352", "reserved.353", "reserved.354", "reserved.355", "reserved.356", "reserved.357", "reserved.358", "reserved.359", "reserved.360", "reserved.361", "reserved.362", "reserved.363", "reserved.364", "reserved.365", "reserved.366", "reserved.367", "reserved.368", "reserved.369", "reserved.370", "reserved.371", "reserved.372", "reserved.373", "reserved.374", "reserved.375", "reserved.376", "reserved.377", "reserved.378", "reserved.379", "reserved.380", "reserved.381", "reserved.382", "reserved.383", "reserved.384", "reserved.385", "reserved.386", "reserved.387", "reserved.388", "reserved.389", "reserved.390", "reserved.391", "reserved.392", "reserved.393", "reserved.394", "reserved.395", "reserved.396", "reserved.397", "reserved.398", "reserved.399", "reserved.400", "reserved.401", "reserved.402", "reserved.403", "reserved.404", "reserved.405", "reserved.406", "reserved.407", "reserved.408", "reserved.409", "reserved.410", "reserved.411", "reserved.412", "reserved.413", "reserved.414", "reserved.415", "reserved.416", "reserved.417", "reserved.418", "reserved.419", "reserved.420", "reserved.421", "reserved.422", "reserved.423", "pidfd_send_signal", "io_uring_setup", "io_uring_enter", "io_uring_register", "open_tree", "move_mount", "fsopen", "fsconfig", "fsmount", "fspick", "pidfd_open", "clone3", //435 } func callNameX86Family64(num uint32) string { if num > SyscallX86MaxNum64 { return SyscallX86UnknownName } return syscallNumTableX86Family64[num] } func callNumTableIsOkX86Family64() bool { if (len(syscallNumTableX86Family64) == SyscallX86MaxNum64+1) && syscallNumTableX86Family64[SyscallX86MaxNum64] == SyscallX86LastName64 { return true } return false } func callNumberX86Family64(name string) (uint32, bool) { num, ok := syscallNameTableX86Family64[name] return num, ok } var syscallNameTableX86Family64 map[string]uint32 func init() { syscallNameTableX86Family64 = make(map[string]uint32, len(syscallNumTableX86Family64)) for callNum, callName := range syscallNumTableX86Family64 { syscallNameTableX86Family64[callName] = uint32(callNum) } } ================================================ FILE: pkg/system/system.go ================================================ package system import ( "os/user" "strconv" ) type SystemInfo struct { Sysname string Nodename string Release string Version string Machine string Domainname string OsBuild string Distro DistroInfo } type DistroInfo struct { Name string `json:"name"` Version string `json:"version"` DisplayName string `json:"display_name"` } func ResolveUser(identity string) (uid uint32, gid uint32, home string, err error) { var userInfo *user.User if _, err := strconv.ParseUint(identity, 10, 32); err == nil { userInfo, err = user.LookupId(identity) if err != nil { return 0, 0, "", err } } else { userInfo, err = user.Lookup(identity) if err != nil { return 0, 0, "", err } } uid64, err := strconv.ParseUint(userInfo.Uid, 10, 32) if err != nil { return 0, 0, "", err } gid64, err := strconv.ParseUint(userInfo.Gid, 10, 32) if err != nil { return 0, 0, "", err } return uint32(uid64), uint32(gid64), userInfo.HomeDir, nil } func ResolveGroup(identity string) (uint32, error) { var groupInfo *user.Group if _, err := strconv.ParseUint(identity, 10, 32); err == nil { groupInfo, err = user.LookupGroupId(identity) if err != nil { return 0, err } } else { groupInfo, err = user.LookupGroup(identity) if err != nil { return 0, err } } gid, err := strconv.ParseUint(groupInfo.Gid, 10, 32) if err != nil { return 0, err } return uint32(gid), nil } ================================================ FILE: pkg/system/system_darwin.go ================================================ package system import ( "fmt" "os" "runtime" "strconv" "strings" "syscall" ) func newSystemInfo() SystemInfo { var sysInfo SystemInfo sysInfo.Sysname = runtime.GOOS sysInfo.Nodename, _ = os.Hostname() if machineInfo, err := syscall.Sysctl("hw.machine"); err == nil { sysInfo.Machine = machineInfo } if releaseInfo, err := syscall.Sysctl("kern.osrelease"); err == nil { rparts := strings.SplitN(releaseInfo, ".", 3) if len(rparts) == 3 { major, _ := strconv.ParseUint(rparts[0], 10, 64) minor, _ := strconv.ParseUint(rparts[1], 10, 64) sysInfo.Distro = DistroInfo{ DisplayName: osName(major, minor), } } sysInfo.Release = releaseInfo } if versionInfo, err := syscall.Sysctl("kern.version"); err == nil { vparts := strings.SplitN(versionInfo, ":", 2) if len(vparts) == 2 { sysInfo.Version = vparts[1] } } if buildInfo, err := syscall.Sysctl("kern.osversion"); err == nil { sysInfo.OsBuild = buildInfo } return sysInfo } var defaultSysInfo = newSystemInfo() func GetSystemInfo() SystemInfo { return defaultSysInfo } func osName(major, minor uint64) string { if info, ok := osNames[major]; ok { return fmt.Sprintf("%v (%v%v)", info.name, info.numPrefix, minor) } return "other" } // Mac OS X version names and numbers: // https://support.apple.com/en-us/HT201260 // https://en.wikipedia.org/wiki/MacOS_version_history var osNames = map[uint64]struct { name, numPrefix string }{ 4: {"Cheetah", "10.0."}, 5: {"Puma", "10.1."}, 6: {"Jaguar", "10.2."}, 7: {"Panther", "10.3."}, 8: {"Tiger", "10.4."}, 9: {"Leopard", "10.5."}, 10: {"Snow Leopard", "10.6."}, 11: {"Lion", "10.7."}, 12: {"Mountain Lion", "10.8."}, 13: {"Mavericks", "10.9."}, 14: {"Yosemite", "10.10."}, 15: {"El Capitan", "10.11."}, 16: {"Sierra", "10.12."}, 17: {"High Sierra", "10.13."}, 18: {"Mojave", "10.14."}, 19: {"Catalina", "10.15."}, 20: {"Big Sur", "11."}, 21: {"Monterey", "12."}, 22: {"Ventura", "13."}, 23: {"Sonoma", "14."}, } ================================================ FILE: pkg/system/system_linux.go ================================================ package system import ( "fmt" "os" "syscall" ) func newSystemInfo() SystemInfo { var sysInfo SystemInfo var unameInfo syscall.Utsname if err := syscall.Uname(&unameInfo); err != nil { return sysInfo } sysInfo.Sysname = nativeCharsToString(unameInfo.Sysname) sysInfo.Nodename = nativeCharsToString(unameInfo.Nodename) sysInfo.Machine = nativeCharsToString(unameInfo.Machine) sysInfo.Domainname = nativeCharsToString(unameInfo.Domainname) //kernel info sysInfo.Release = nativeCharsToString(unameInfo.Release) sysInfo.Version = nativeCharsToString(unameInfo.Version) //distro info sysInfo.Distro = distroInfo() return sysInfo } var defaultSysInfo = newSystemInfo() func GetSystemInfo() SystemInfo { return defaultSysInfo } func distroInfo() DistroInfo { distro := DistroInfo{ Name: "unknown", DisplayName: "unknown", } bdata, err := os.ReadFile(OSReleaseFile) if err != nil { return distro } if osr, err := NewOsRelease(bdata); err == nil { var nameMain, nameVersion string distro.Name = osr.Name distro.Version = osr.VersionID if distro.Version == "" { distro.Version = osr.Version } distro.DisplayName = osr.PrettyName if distro.DisplayName == "" { nameMain = osr.Name if len(osr.Version) > 0 { nameVersion = osr.Version } else { nameVersion = osr.VersionID } distro.DisplayName = fmt.Sprintf("%v %v", nameMain, nameVersion) } return distro } distro.Name = "other" distro.DisplayName = "other" return distro } /* func getOperatingSystem() string { bdata, err := os.ReadFile("/etc/os-release") if err != nil { print("error reading /etc/os-release") return "" } var nameMain, nameVersion string if i := bytes.Index(bdata, []byte("NAME")); i >= 0 { offset := i+ len("NAME") + 2 nameData = bdata[offset:] nameMain = string(nameData[:bytes.IndexByte(nameData, '"')]) } if i := bytes.Index(bdata, []byte("VERSION")); i >= 0 { offset := i+ len("VERSION") + 2 nameData = bdata[offset:] nameMain = string(nameData[:bytes.IndexByte(nameData, '"')]) } else { if i := bytes.Index(bdata, []byte("VERSION_ID")); i >= 0 { //version id could be with or without quotes offset := i+ len("VERSION_ID") + 2 nameData = bdata[offset:] nameMain = string(nameData[:bytes.IndexByte(nameData, '"')]) } } return fmt.Sprintf("%v %v",nameMain,nameVersion) } */ ================================================ FILE: pkg/system/system_linux_amd64.go ================================================ package system import ( "syscall" ) /* AMD64/X86_64 SYSCALL REGISTER USE: Syscall Number: rax Return Value: rax 1st Param (arg0): rdi 2nd Param (arg1): rsi 3rd Param (arg2): rdx 4th Param (arg3): r10 5th Param (arg4): r8 6th Param (arg5): r9 */ func LookupCallName(num uint32) string { return callNameX86Family64(num) } func LookupCallNumber(name string) (uint32, bool) { return callNumberX86Family64(name) } func CallNumber(regs syscall.PtraceRegs) uint64 { return regs.Orig_rax } func CallReturnValue(regs syscall.PtraceRegs) uint64 { return regs.Rax } func CallFirstParam(regs syscall.PtraceRegs) uint64 { return regs.Rdi } func CallSecondParam(regs syscall.PtraceRegs) uint64 { return regs.Rsi } func CallThirdParam(regs syscall.PtraceRegs) uint64 { return regs.Rdx } func CallFourthParam(regs syscall.PtraceRegs) uint64 { return regs.Rcx } /* X86_32 SYSCALL REGISTER USE: Syscall Number: eax Return Value: eax 1st Param (arg0): ebx 2nd Param (arg1): ecx 3rd Param (arg2): edx 4th Param (arg3): esi 5th Param (arg4): edi 6th Param (arg5): ebp */ ================================================ FILE: pkg/system/system_linux_arm.go ================================================ package system import ( "syscall" ) /* ARM SYSCALL REGISTER USE: Syscall Number: r7 Return Value: r0 1st Param (arg0): r0 2nd Param (arg1): r1 3rd Param (arg2): r2 4th Param (arg3): r3 5th Param (arg4): r4 6th Param (arg5): r5 */ func LookupCallName(num uint32) string { return callNameArmFamily32(num) } func LookupCallNumber(name string) (uint32, bool) { return callNumberArmFamily32(name) } func CallNumber(regs syscall.PtraceRegs) uint64 { return uint64(regs.Uregs[7]) } func CallReturnValue(regs syscall.PtraceRegs) uint64 { return uint64(regs.Uregs[0]) } func CallFirstParam(regs syscall.PtraceRegs) uint64 { return uint64(regs.Uregs[0]) } func CallSecondParam(regs syscall.PtraceRegs) uint64 { return uint64(regs.Uregs[1]) } ================================================ FILE: pkg/system/system_linux_arm64.go ================================================ package system import ( "golang.org/x/sys/unix" ) /* ARM64 SYSCALL REGISTER USE: Syscall Number: x8 Return Value: x0 1st Param (arg0): x0 2nd Param (arg1): x1 3rd Param (arg2): x2 4th Param (arg3): x3 5th Param (arg4): x4 6th Param (arg5): x5 */ func LookupCallName(num uint32) string { return callNameArmFamily64(num) } func LookupCallNumber(name string) (uint32, bool) { return callNumberArmFamily64(name) } func CallNumber(regs unix.PtraceRegsArm64) uint64 { return uint64(regs.Regs[8]) } func CallReturnValue(regs unix.PtraceRegsArm64) uint64 { return uint64(regs.Regs[0]) } func CallFirstParam(regs unix.PtraceRegsArm64) uint64 { return uint64(regs.Regs[0]) } func CallSecondParam(regs unix.PtraceRegsArm64) uint64 { return uint64(regs.Regs[1]) } ================================================ FILE: pkg/test/e2e/sensor/docker.go ================================================ package sensor import ( "bytes" "context" "encoding/json" "fmt" "os" "os/exec" "strconv" "syscall" dockerapi "github.com/fsouza/go-dockerclient" log "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) func imagePull(ctx context.Context, name string) error { _, err := docker(ctx, "image", "pull", name) return err } func imageInspect(ctx context.Context, name string) (dockerapi.Image, error) { out, err := docker(ctx, "image", "inspect", name) if err != nil { return dockerapi.Image{}, err } var images []dockerapi.Image if err := json.Unmarshal([]byte(out), &images); err != nil { return dockerapi.Image{}, fmt.Errorf("cannot decode docker command output %q: %w", out, err) } if len(images) > 1 { return dockerapi.Image{}, fmt.Errorf("ambiguous image name %q", name) } return images[0], nil } func containerCreate( ctx context.Context, flags []string, image string, arg ...string, ) (string, error) { tail := append([]string{"create"}, flags...) tail = append(tail, image) tail = append(tail, arg...) return docker(ctx, "container", tail...) } func containerStart(ctx context.Context, contID string) error { _, err := docker(ctx, "container", "start", contID) return err } func containerWait(ctx context.Context, contID string) (int, error) { out, err := docker(ctx, "container", "wait", contID) if err != nil { return -1, err } exitCode, err := strconv.Atoi(string(out)) if err != nil { return -1, fmt.Errorf("unexpected container wait output %q - expected number (exit code)", string(out)) } return exitCode, nil } func containerKill(ctx context.Context, contID string, sig syscall.Signal) error { _, err := docker(ctx, "container", "kill", "-s", unix.SignalName(sig), contID) return err } func containerRemove(ctx context.Context, contID string) error { _, err := docker(ctx, "container", "rm", contID) return err } func containerInspect(ctx context.Context, contID string) (dockerapi.Container, error) { out, err := docker(ctx, "container", "inspect", contID) if err != nil { return dockerapi.Container{}, err } var conts []dockerapi.Container if err := json.Unmarshal([]byte(out), &conts); err != nil { return dockerapi.Container{}, fmt.Errorf("cannot decode docker command output %q: %w", out, err) } if len(conts) > 1 { return dockerapi.Container{}, fmt.Errorf("ambiguous container id/name %q", contID) } return conts[0], nil } func containerExec(ctx context.Context, contID string, arg ...string) (string, error) { cmd := exec.CommandContext(ctx, "docker", append([]string{"container", "exec", contID}, arg...)...) log.Debug("Executing: ", cmd.String()) out, err := cmd.CombinedOutput() return string(out), err } func containerLogs(ctx context.Context, contID string) (string, error) { cmd := exec.CommandContext(ctx, "docker", "container", "logs", contID) log.Debug("Executing: ", cmd.String()) out, err := cmd.CombinedOutput() return string(out), err } func containerCopyFrom(ctx context.Context, contID, orig, dest string) error { _, err := docker(ctx, "container", "cp", contID+":"+orig, dest) return err } func docker(ctx context.Context, command string, arg ...string) (string, error) { cmd := exec.CommandContext(ctx, "docker", append([]string{command}, arg...)...) cmd.Stderr = os.Stderr log.Debug("Executing: ", cmd.String()) out, err := cmd.Output() if len(out) > 0 { return string(bytes.Trim(out, " \t\r\n")), err } return "", err } ================================================ FILE: pkg/test/e2e/sensor/monitor.go ================================================ package sensor import ( mastercommand "github.com/slimtoolkit/slim/pkg/app/master/command" "github.com/slimtoolkit/slim/pkg/ipc/command" ) type StartMonitorOpt func(*command.StartMonitor) func WithSaneDefaults() StartMonitorOpt { return func(cmd *command.StartMonitor) { cmd.RTASourcePT = true cmd.KeepPerms = true cmd.IncludeCertAll = true cmd.IncludeCertBundles = true cmd.IncludeCertDirs = true cmd.IncludeCertPKAll = true cmd.IncludeCertPKDirs = true cmd.IncludeNew = true } } func WithAppNameArgs(name string, arg ...string) StartMonitorOpt { return func(cmd *command.StartMonitor) { cmd.AppName = name cmd.AppArgs = arg } } func WithAppUser(user string) StartMonitorOpt { return func(cmd *command.StartMonitor) { cmd.AppUser = user cmd.RunTargetAsUser = true } } func WithAppStdoutToFile() StartMonitorOpt { return func(cmd *command.StartMonitor) { cmd.AppStdoutToFile = true } } func WithAppStderrToFile() StartMonitorOpt { return func(cmd *command.StartMonitor) { cmd.AppStderrToFile = true } } func WithPreserves(path ...string) StartMonitorOpt { return func(cmd *command.StartMonitor) { cmd.Preserves = mastercommand.ParsePaths(path) } } func NewMonitorStartCommand(opts ...StartMonitorOpt) command.StartMonitor { cmd := command.StartMonitor{} for _, opt := range opts { opt(&cmd) } return cmd } ================================================ FILE: pkg/test/e2e/sensor/sensor.go ================================================ package sensor import ( "archive/tar" "context" "encoding/json" "errors" "fmt" "io" "os" "os/exec" "path/filepath" "strings" "syscall" "testing" "time" dockerapi "github.com/fsouza/go-dockerclient" log "github.com/sirupsen/logrus" "golang.org/x/sys/unix" "github.com/slimtoolkit/slim/pkg/app" "github.com/slimtoolkit/slim/pkg/app/master/inspectors/ipc" "github.com/slimtoolkit/slim/pkg/app/master/inspectors/sensor" "github.com/slimtoolkit/slim/pkg/app/sensor/standalone/control" "github.com/slimtoolkit/slim/pkg/ipc/channel" "github.com/slimtoolkit/slim/pkg/ipc/command" "github.com/slimtoolkit/slim/pkg/ipc/event" "github.com/slimtoolkit/slim/pkg/report" "github.com/slimtoolkit/slim/pkg/util/fsutil" ) const ( // Intentionally duplicating values here since to make sure a refactoing // of the paths on the sensor side won't be unnoticed. CommandsFileName = "commands.json" SensorLogFileName = "sensor.log" MondelFileName = "mondel.ndjson" AppStdoutFileName = "app_stdout.log" AppStderrFileName = "app_stderr.log" EventsFileName = "events.json" runArchiveName = "run.tar" sensorExePath = "/opt/_slim/sensor" ) var ( errNotStarted error = errors.New("test sensor container hasn't been started yet") ) type sensorOpt func(*Sensor) func WithSensorLogsToFile() sensorOpt { return func(s *Sensor) { s.useLogFile = true } } func WithEnableMondel() sensorOpt { return func(s *Sensor) { s.enableMondel = true } } func WithSensorArtifactsDir(dir string) sensorOpt { return func(s *Sensor) { s.artifactsDirPath = dir } } func WithSensorLifecycleHook(cmd string) sensorOpt { return func(s *Sensor) { s.lifecycleHook = cmd } } func WithSensorCapabilities(caps ...string) sensorOpt { return func(s *Sensor) { s.capAdd = caps } } func WithoutSensorCapabilities(caps ...string) sensorOpt { return func(s *Sensor) { s.capDrop = caps } } func WithStopSignal(sig syscall.Signal) sensorOpt { return func(s *Sensor) { s.stopSignal = sig } } type Sensor struct { image dockerapi.Image contName string sensorExePath string contextDirPath string // "Opts" useLogFile bool enableMondel bool artifactsDirPath string lifecycleHook string capAdd []string capDrop []string user string stopSignal syscall.Signal // "Nullable" contID string rawCommands string client *ipc.Client stopped bool // "Artifacts" creport *report.ContainerReport rawCReport string rawEvents string mondel []report.MonitorDataEvent rawMondel string } func NewSensor( ctx context.Context, contextDirPath string, contName string, imageName string, opts ...sensorOpt, ) (*Sensor, error) { sensorExePath, err := exec.LookPath(sensor.LocalBinFile) if err != nil { return nil, fmt.Errorf("cannot locate %s executable on the host system", sensor.LocalBinFile) } if err := imagePull(ctx, imageName); err != nil { return nil, fmt.Errorf("cannot pull image %q: %w", imageName, err) } image, err := imageInspect(ctx, imageName) if err != nil { return nil, fmt.Errorf("cannot inspect image %q: %w", imageName, err) } log. WithField("image", imageName). WithField("context", contextDirPath). WithField("exe", sensorExePath). Debug("New test sensor created") s := &Sensor{ image: image, contName: strings.ToLower(contName), sensorExePath: sensorExePath, contextDirPath: contextDirPath, capAdd: []string{"ALL"}, user: "0", } for _, opt := range opts { opt(s) } return s, nil } func NewSensorOrFail( t *testing.T, ctx context.Context, contextDirPath string, contName string, imageName string, opts ...sensorOpt, ) *Sensor { s, err := NewSensor(ctx, contextDirPath, contName, imageName, opts...) if err != nil { t.Fatal("Cannot initialize sensor:", err) } return s } func (s *Sensor) StartControlled(ctx context.Context) error { log.Debug("Starting test sensor (controlled mode)...") contID, err := containerCreate( ctx, flatten( s.capabilities(), []string{ "--name", s.contName, "--user", s.user, "--volume", s.sensorExePath + ":" + sensorExePath, "--publish", fmt.Sprintf("%d", channel.CmdPort), "--publish", fmt.Sprintf("%d", channel.EvtPort), "--entrypoint", sensorExePath, }, ), s.image.ID, s.commonStartFlags()..., ) if err != nil { return fmt.Errorf("cannot create target container (controlled mode): %w", err) } log.WithField("containerId", contID).Debug("Test sensor container created (controlled mode)") s.contID = contID if err := containerStart(ctx, s.contID); err != nil { return fmt.Errorf("cannot start target container (controlled mode): %w", err) } log.WithField("containerId", contID).Debug("Test sensor container started (controlled mode)") cont, err := containerInspect(ctx, s.contID) if err != nil { return fmt.Errorf("cannot inspect container %q: %w", s.contID, err) } cmdPort, ok := hostPort(cont, channel.CmdPort) if !ok { return fmt.Errorf("container %q - no host port found for port %d", s.contID, channel.CmdPort) } evtPort, ok := hostPort(cont, channel.EvtPort) if !ok { return fmt.Errorf("container %q - no host port found for port %d", s.contID, channel.EvtPort) } // TODO: Refactor the IPC code to use context with a deadline. client, err := ipc.NewClient("127.0.0.1", cmdPort, evtPort, 10) // Seconds, I guess if err != nil { return fmt.Errorf("cannot start IPC client: %w", err) } log. WithField("containerId", contID). Debug("IPC client connected to the target container") s.client = client return nil } func (s *Sensor) StartControlledOrFail(t *testing.T, ctx context.Context) { if err := s.StartControlled(ctx); err != nil { t.Fatal("Cannot start sensor (controlled mode):", err) } } func (s *Sensor) StartStandalone( ctx context.Context, runArgs []string, cmdOverride ...command.StartMonitor, ) error { cmd := startCommandStandalone(s.image, cmdOverride...) log. WithField("command", fmt.Sprintf("%+v", cmd)). Debug("Starting test sensor (standalone mode)...") commandsFilePath := filepath.Join(s.contextDirPath, CommandsFileName) if err := jsonDump(commandsFilePath, cmd); err != nil { return fmt.Errorf("cannot create %s file: %w", CommandsFileName, err) } rawCommands, err := os.ReadFile(commandsFilePath) if err != nil { return fmt.Errorf("cannot re-read %s file: %w", CommandsFileName, err) } s.rawCommands = string(rawCommands) stopSignal := "SIGTERM" if s.stopSignal != 0 { stopSignal = unix.SignalName(s.stopSignal) } else if len(s.image.Config.StopSignal) > 0 { stopSignal = s.image.Config.StopSignal } stopTimeout := 5 * time.Second if s.image.Config.StopTimeout != 0 { // TODO: Make sure we never pass 0s. stopTimeout = time.Duration(s.image.Config.StopTimeout/2) * time.Second } contID, err := containerCreate( ctx, flatten( s.capabilities(), []string{ "--name", s.contName, "--user", s.user, "--volume", s.sensorExePath + ":" + sensorExePath, "--volume", commandsFilePath + ":/opt/_slim/commands.json", "--entrypoint", sensorExePath, }, ), s.image.ID, flatten( s.commonStartFlags(), // Standalone flags []string{ "-m", "standalone", "-c", "/opt/_slim/commands.json", "-s", stopSignal, "-w", stopTimeout.String(), "--", }, runArgs, )..., ) if err != nil { return fmt.Errorf("cannot create target container (standalone mode): %w", err) } log.WithField("containerId", contID).Debug("Test sensor container created (standalone mode)") s.contID = contID if err := containerStart(ctx, s.contID); err != nil { return fmt.Errorf("cannot start target container (standalone mode): %w", err) } log.WithField("containerId", contID).Debug("Test sensor container started (standalone mode)") return nil } func (s *Sensor) StartStandaloneOrFail( t *testing.T, ctx context.Context, runArgs []string, cmdOverride ...command.StartMonitor, ) { if err := s.StartStandalone(ctx, runArgs, cmdOverride...); err != nil { t.Fatal("Cannot start sensor (standalone mode):", err) } } func (s *Sensor) SendCommand(ctx context.Context, cmd command.Message) error { msg, err := command.Encode(cmd) if err != nil { return fmt.Errorf("cannot encode command %q: %w", cmd, err) } log.Debugf("Sending command to the test sensor: %s", string(msg)) if len(s.contID) == 0 { return errNotStarted } if s.client == nil { return errors.New("IPC client isn't initialized - is sensor running?") } // TODO: Use timeout from ctx. resp, err := s.client.SendCommand(cmd) if err != nil || resp.Status != command.ResponseStatusOk { return fmt.Errorf("IPC client.SendCommand() failed with response %q: %w", resp, err) } return nil } func (s *Sensor) SendStartCommand( ctx context.Context, cmdOverride ...command.StartMonitor, ) error { cmd := startCommandControlled(s.image, cmdOverride...) return s.SendCommand(ctx, &cmd) } func (s *Sensor) SendStartCommandOrFail( t *testing.T, ctx context.Context, cmdOverride ...command.StartMonitor, ) { if err := s.SendStartCommand(ctx, cmdOverride...); err != nil { t.Fatal("Failed sending StartMonitor command:", err) } } func (s *Sensor) SendStopCommand(ctx context.Context) error { return s.SendCommand(ctx, &command.StopMonitor{}) } func (s *Sensor) SendStopCommandOrFail(t *testing.T, ctx context.Context) { if err := s.SendStopCommand(ctx); err != nil { t.Fatal("Failed sending StopMonitor command:", err) } } func (s *Sensor) ExecuteControlCommand(ctx context.Context, cmd control.Command) error { if len(s.contID) == 0 { return errNotStarted } if s.client != nil { return fmt.Errorf("cannot execute control command - sensor is not in the standalone mode") } if out, err := containerExec( ctx, s.contID, sensorExePath, "control", string(cmd), ); err != nil { return fmt.Errorf("cannot execute control command: %w\n%s", err, string(out)) } return nil } func (s *Sensor) ExecuteControlCommandOrFail(t *testing.T, ctx context.Context, cmd control.Command) { if err := s.ExecuteControlCommand(ctx, cmd); err != nil { t.Fatalf("Failed executing control command %s: %v", cmd, err) } } func (s *Sensor) WaitForEvent(ctx context.Context, evt event.Type) error { if len(s.contID) == 0 { return errNotStarted } if out, err := containerExec( ctx, s.contID, sensorExePath, "control", string(control.WaitForEventCommand), string(evt), ); err != nil { return fmt.Errorf("cannot wait for sensor event %s: %w\n%s", evt, err, string(out)) } return nil } func (s *Sensor) WaitForEventOrFail(t *testing.T, ctx context.Context, evt event.Type) { if err := s.WaitForEvent(ctx, evt); err != nil { t.Fatalf("Failed waiting for sensor event %s: %v", evt, err) } } func (s *Sensor) Shutdown(ctx context.Context) error { if err := s.SendCommand(ctx, &command.ShutdownSensor{}); err != nil { return err } if err := s.client.Stop(); err != nil { return fmt.Errorf("IPC client.Stop() failed: %w", err) } return nil } func (s *Sensor) ShutdownOrFail(t *testing.T, ctx context.Context) { if err := s.Shutdown(ctx); err != nil { t.Fatal("Test sensor shutdown failed:", err) } } func (s *Sensor) Wait(ctx context.Context) (int, error) { if len(s.contID) == 0 { return -1, errNotStarted } exitCode, err := containerWait(ctx, s.contID) if err == nil { s.stopped = true return exitCode, nil } return -1, err } func (s *Sensor) WaitOrFail(t *testing.T, ctx context.Context) int { exitCode, err := s.Wait(ctx) if err != nil { t.Fatal("Failed waiting for test sensor container:", err) } return exitCode } func (s *Sensor) Signal(ctx context.Context, sig syscall.Signal) error { if len(s.contID) == 0 { return errNotStarted } return containerKill(ctx, s.contID, sig) } func (s *Sensor) SignalOrFail(t *testing.T, ctx context.Context, sig syscall.Signal) { if err := s.Signal(ctx, sig); err != nil { t.Fatal("Cannot signal test sensor container:", err) } } func (s *Sensor) DownloadArtifacts(ctx context.Context) error { if len(s.contID) == 0 { return errNotStarted } if err := containerCopyFrom( ctx, s.contID, s.remoteArtifactsDirPath(), s.localArtifactsDirPath(), ); err != nil { return fmt.Errorf("cannot download test sensor's artifacts: %w", err) } creportFilePath := filepath.Join(s.localArtifactsDirPath(), report.DefaultContainerReportFileName) rawCReport, err := os.ReadFile(creportFilePath) if err == nil { s.rawCReport = string(rawCReport) var creport report.ContainerReport if err := fsutil.LoadStructFromFile(creportFilePath, &creport); err != nil { return fmt.Errorf("cannot decode test sensor's report: %w", err) } s.creport = &creport } if s.enableMondel { mondelFilePath := filepath.Join(s.localArtifactsDirPath(), MondelFileName) rawMondel, err := os.ReadFile(mondelFilePath) if err != nil { return fmt.Errorf("cannot read %s file: %w", MondelFileName, err) } s.rawMondel = string(rawMondel) for _, line := range strings.Split(strings.TrimSpace(s.rawMondel), "\n") { var evt report.MonitorDataEvent if err := json.Unmarshal([]byte(line), &evt); err != nil { return fmt.Errorf("cannot decode test sensor's mondel line %#q: %w", line, err) } s.mondel = append(s.mondel, evt) } } if s.client == nil { rawEvents, err := os.ReadFile(filepath.Join(s.localArtifactsDirPath(), EventsFileName)) if err != nil { return fmt.Errorf("cannot read %s file: %w", EventsFileName, err) } s.rawEvents = string(rawEvents) } return nil } func (s *Sensor) DownloadArtifactsOrFail(t *testing.T, ctx context.Context) { if err := s.DownloadArtifacts(ctx); err != nil { t.Fatal("Cannot download test sensor's artifacts:", err) } } func (s *Sensor) Cleanup(t *testing.T, ctx context.Context) { if t.Failed() { s.PrintState(ctx) } if len(s.contID) == 0 || s.stopped { return } if err := s.Signal(ctx, syscall.SIGKILL); err != nil { log.WithError(err).Warnf("Sensor cleanup: cannot signal container %q", s.contID) } else { time.Sleep(2 * time.Second) } if err := containerRemove(ctx, s.contID); err != nil { log.WithError(err).Warnf("Sensor cleanup: cannot remove container %q", s.contID) } } func (s *Sensor) ContainerLogs(ctx context.Context) (string, error) { return containerLogs(ctx, s.contID) } func (s *Sensor) ContainerLogsOrFail(t *testing.T, ctx context.Context) string { logs, err := s.ContainerLogs(ctx) if err != nil { t.Fatal("Cannot retrieve target container logs:", err) } return logs } func (s *Sensor) SensorLogs(ctx context.Context) (string, error) { if s.useLogFile { bytes, err := os.ReadFile( filepath.Join(s.localArtifactsDirPath(), SensorLogFileName), ) return string(bytes), err } return s.ContainerLogs(ctx) } func (s *Sensor) SensorLogsOrFail(t *testing.T, ctx context.Context) string { logs, err := s.SensorLogs(ctx) if err != nil { t.Fatal("Cannot retrieve sensor logs:", err) } return logs } func (s *Sensor) PrintState(ctx context.Context) { log. WithField("image", s.image). WithField("container", s.contID). WithField("context", s.contextDirPath). WithField("exe", s.sensorExePath). WithField("creport downloaded", s.creport != nil). Info("Printing out test sensor state") if len(s.rawCReport) > 0 { fmt.Fprintln(os.Stderr, "-=== Container report ===-") fmt.Fprintln(os.Stderr, s.rawCReport) fmt.Fprintln(os.Stderr, "-=== eof: Container report ===-") } if len(s.rawMondel) > 0 { fmt.Fprintln(os.Stderr, "-=== Container MonDEL ===-") fmt.Fprintln(os.Stderr, s.rawMondel) fmt.Fprintln(os.Stderr, "-=== eof: Container MonDEL ===-") } if len(s.rawEvents) > 0 { fmt.Fprintln(os.Stderr, "-=== events.json ===-") fmt.Fprintln(os.Stderr, s.rawEvents) fmt.Fprintln(os.Stderr, "-=== eof: events.json ===-") } if len(s.contID) > 0 && s.useLogFile { fmt.Fprintln(os.Stderr, "-=== Sensor logs ===-") if sensorLogs, err := s.SensorLogs(ctx); err == nil { fmt.Fprintln(os.Stderr, sensorLogs) } else { log.WithError(err).Error("Cannot obtain sensor logs") } fmt.Fprintln(os.Stderr, "-=== eof: Sensor logs ===-") } if len(s.contID) > 0 { fmt.Fprintln(os.Stderr, "-=== Container logs ===-") if contLogs, err := s.ContainerLogs(ctx); err == nil { fmt.Fprintln(os.Stderr, contLogs) } else { log.WithError(err).Error("Cannot obtain target container logs") } fmt.Fprintln(os.Stderr, "-=== eof: Container logs ===-") } } func (s *Sensor) ExpectEvent(t *testing.T, name event.Type) { if s.client == nil { t.Fatal("IPC client isn't initialized - is sensor running?") } evt, err := s.client.GetEvent() if err != nil { t.Fatalf("IPC client.GetEvent() failed with response %q: %v", evt, err) } if evt.Name != name { t.Fatalf("Unexpected event type %q (expected %q)", evt.Name, name) } } func (s *Sensor) AssertSensorLogsContain(t *testing.T, ctx context.Context, what ...string) { if len(s.contID) == 0 { t.Fatal("Test sensor container hasn't been started yet") } contLogs := s.SensorLogsOrFail(t, ctx) for _, w := range what { if strings.Index(contLogs, w) == -1 { t.Errorf("Cannot find string %q in sensor logs", w) } } } func (s *Sensor) AssertTargetAppLogsContain(t *testing.T, ctx context.Context, what ...string) { if len(s.contID) == 0 { t.Fatal("Test sensor container hasn't been started yet") } contLogs := s.ContainerLogsOrFail(t, ctx) for _, w := range what { if strings.Index(contLogs, w) == -1 { t.Errorf("Cannot find string %q in container logs", w) } } } func (s *Sensor) AssertTargetAppLogsEqualTo(t *testing.T, ctx context.Context, what string) { if len(s.contID) == 0 { t.Fatal("Test sensor container hasn't been started yet") } contLogs := strings.TrimSpace(s.ContainerLogsOrFail(t, ctx)) if contLogs != what { t.Errorf("Unexpected container logs %q. Expected %q.", contLogs, what) } } func (s *Sensor) AssertTargetAppStdoutFileEqualsTo(t *testing.T, ctx context.Context, expected string) { s.assertTargetAppStdFileEqualsTo(t, ctx, AppStdoutFileName, expected) } func (s *Sensor) AssertTargetAppStderrFileEqualsTo(t *testing.T, ctx context.Context, expected string) { s.assertTargetAppStdFileEqualsTo(t, ctx, AppStderrFileName, expected) } func (s *Sensor) assertTargetAppStdFileEqualsTo( t *testing.T, ctx context.Context, kind string, expected string, ) { if len(s.contID) == 0 { t.Fatal("Test sensor container hasn't been started yet") } data, err := os.ReadFile(filepath.Join(s.localArtifactsDirPath(), kind)) if err != nil { t.Fatalf("cannot read %s file: %v", kind, err) } actual := strings.TrimSpace(string(data)) if actual != expected { t.Errorf("Unexpected target app %s content %q. Expected %q.", kind, actual, expected) } } // Checks the presense of the expected events AND the occurrence order. func (s *Sensor) AssertSensorEventsFileContains( t *testing.T, ctx context.Context, expected ...event.Type, ) { if len(s.rawEvents) == 0 { t.Fatal("No events found") } actual := parseEvents(s.rawEvents) // Orders matter! for len(expected) > 0 && len(actual) > 0 { if expected[0] == actual[0].Name { expected = expected[1:] } actual = actual[1:] } if len(expected) > 0 { t.Errorf("Some of the expected events weren't found in event file: %q", expected) } } func (s *Sensor) AssertReportIncludesFiles(t *testing.T, filepath ...string) { if s.creport == nil { t.Fatal("No sensor report found") } index := artifactsByFilePath(s.creport.Image.Files) for _, f := range filepath { if index[f] == nil { t.Errorf("Expected file %q not found in the container report", f) } } } func (s *Sensor) AssertReportNotIncludesFiles(t *testing.T, filepath ...string) { if s.creport == nil { t.Fatal("No sensor report found") } index := artifactsByFilePath(s.creport.Image.Files) for _, f := range filepath { if index[f] != nil { t.Errorf("Unexpected file %q found in the container report", f) } } } func (s *Sensor) AssertMondelIncludesFiles(t *testing.T, filepath ...string) { if s.mondel == nil { t.Fatal("No sensor mondel file found") } index := mondelEventByFilePath(s.mondel) for _, f := range filepath { if _, found := index[f]; !found { t.Errorf("Expected file %q not found in the mondel file", f) } } } func (s *Sensor) AssertMondelNotIncludesFiles(t *testing.T, filepath ...string) { if s.mondel == nil { t.Fatal("No sensor mondel file found") } index := mondelEventByFilePath(s.mondel) for _, f := range filepath { if _, found := index[f]; found { t.Errorf("Unexpected file %q found in the mondel file", f) } } } func (s *Sensor) AssertReportAndMondelFileListsMatch(t *testing.T) { if s.creport == nil { t.Fatal("No sensor report found") } if len(s.mondel) == 0 { t.Fatal("No sensor mondel file found") } uniqMondelFiles := mondelEventByFilePath(s.mondel) uniqCReportFiles := artifactsByFilePath(s.creport.Image.Files) for f := range uniqMondelFiles { if _, found := uniqCReportFiles[f]; !found { t.Errorf("File %q found in mondel but not in container report", f) } } for f := range uniqCReportFiles { if _, found := uniqMondelFiles[f]; !found { t.Errorf("File %q found in container report but not in mondel", f) } } } func (s *Sensor) AssertArtifactsArchiveContains( t *testing.T, ctx context.Context, filename ...string, ) { archiveFilePath := filepath.Join(s.localArtifactsDirPath(), runArchiveName) archiveFile, err := os.Open(archiveFilePath) if err != nil { t.Errorf("Cannot open report archive %q: %v", archiveFilePath, err) return } defer archiveFile.Close() found := map[string]bool{} for _, f := range filename { found[f] = false } reader := tar.NewReader(archiveFile) for { header, err := reader.Next() if err == io.EOF { break } if err != nil { t.Errorf("Failed reading tar archive header: %v", err) continue } if _, expected := found[header.Name]; !expected { continue } _, err = io.ReadAll(reader) if err != nil { t.Errorf("Failed reading expected tar archive entry %q: %v", header.Name, err) continue } found[header.Name] = true } for name := range found { if !found[name] { t.Errorf("Artifacts archive doesn't contain entry %q", name) } } } func (s *Sensor) commonStartFlags() []string { flags := []string{"-l", "debug", "-d"} if len(s.artifactsDirPath) > 0 { flags = append(flags, "-e", s.artifactsDirPath) } if s.useLogFile { flags = append(flags, "-o", filepath.Join(s.remoteArtifactsDirPath(), SensorLogFileName)) } if s.enableMondel { flags = append(flags, "-n") } if len(s.lifecycleHook) > 0 { flags = append(flags, "-a", s.lifecycleHook) } return flags } func (s *Sensor) capabilities() (caps []string) { for _, c := range s.capAdd { caps = append(caps, "--cap-add="+c) } for _, c := range s.capDrop { caps = append(caps, "--cap-drop="+c) } return caps } func (s *Sensor) localArtifactsDirPath() string { return filepath.Join(s.contextDirPath, filepath.Base(s.remoteArtifactsDirPath())) } func (s *Sensor) remoteArtifactsDirPath() string { if len(s.artifactsDirPath) > 0 { return s.artifactsDirPath } return app.DefaultArtifactsDirPath } func startCommandControlled( image dockerapi.Image, cmdOverride ...command.StartMonitor, ) command.StartMonitor { cmd := NewMonitorStartCommand(WithSaneDefaults()) if len(cmdOverride) > 0 { cmd = cmdOverride[0] } if len(cmd.AppName) == 0 { if len(image.Config.Entrypoint) > 0 { cmd.AppName = image.Config.Entrypoint[0] cmd.AppArgs = append(image.Config.Entrypoint[1:], image.Config.Cmd...) } else { cmd.AppName = image.Config.Cmd[0] cmd.AppArgs = image.Config.Cmd[1:] } } if len(image.Config.User) > 0 { cmd.AppUser = image.Config.User cmd.RunTargetAsUser = true } return cmd } func startCommandStandalone( image dockerapi.Image, cmdOverride ...command.StartMonitor, ) command.StartMonitor { cmd := NewMonitorStartCommand(WithSaneDefaults()) if len(cmdOverride) > 0 { cmd = cmdOverride[0] } cmd.AppEntrypoint = image.Config.Entrypoint cmd.AppCmd = image.Config.Cmd if len(image.Config.User) > 0 { cmd.AppUser = image.Config.User cmd.RunTargetAsUser = true } cmd.ReportOnMainPidExit = true return cmd } func parseEvents(rawEvents string) (events []event.Message) { for _, line := range strings.Split(rawEvents, "\n") { if len(line) == 0 { continue } var event event.Message if err := json.Unmarshal([]byte(line), &event); err != nil { log.WithError(err).Errorf("Cannot parse event file entry: %q", line) } events = append(events, event) } return events } func jsonDump(filename string, val interface{}) error { f, err := os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) if err != nil { return fmt.Errorf("JSON dump failed: cannot create|open file %q: %w", filename, err) } encoder := json.NewEncoder(f) encoder.SetEscapeHTML(false) if err := encoder.Encode(val); err != nil { return fmt.Errorf("JSON dump failed: encoding of value %q failed: %w", val, err) } return nil } func artifactsByFilePath(files []*report.ArtifactProps) map[string]*report.ArtifactProps { dict := make(map[string]*report.ArtifactProps) for _, props := range files { if props != nil { dict[props.FilePath] = props } } return dict } func mondelEventByFilePath(events []report.MonitorDataEvent) map[string]report.MonitorDataEvent { dict := make(map[string]report.MonitorDataEvent) for _, evt := range events { dict[evt.Artifact] = evt } return dict } func hostPort(cont dockerapi.Container, contPort int) (string, bool) { if cont.NetworkSettings != nil { for port, bindings := range cont.NetworkSettings.Ports { if port.Port() == fmt.Sprintf("%d", contPort) && len(bindings) > 0 { return bindings[0].HostPort, true } } } return "", false } func flatten(arrs ...[]string) []string { res := []string{} for _, arr := range arrs { res = append(res, arr...) } return res } ================================================ FILE: pkg/test/stub/sensor/execution/execution.go ================================================ package execution import ( log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/app/sensor/execution" "github.com/slimtoolkit/slim/pkg/ipc/command" "github.com/slimtoolkit/slim/pkg/ipc/event" ) type executionStub struct { commands chan command.Message } var _ execution.Interface = &executionStub{} func NewExecution() *executionStub { return &executionStub{ commands: make(chan command.Message), } } func (e *executionStub) State() string { return "" } func (e *executionStub) Commands() <-chan command.Message { return e.commands } func (e *executionStub) SendCommand(cmd command.Message) { e.commands <- cmd } func (e *executionStub) PubEvent(etype event.Type, data ...interface{}) { log. WithField("type", etype). WithField("data", data). Debug("execution stub - new event") } func (e *executionStub) Close() { close(e.commands) } func (e *executionStub) HookSensorPostStart() { // noop } func (e *executionStub) HookSensorPreShutdown() { // noop } func (e *executionStub) HookMonitorPreStart() { // noop } func (e *executionStub) HookTargetAppRunning() { // noop } func (e *executionStub) HookMonitorPostShutdown() { // noop } func (e *executionStub) HookMonitorFailed() { // noop } ================================================ FILE: pkg/test/stub/sensor/monitor/monitor.go ================================================ package monitor import ( "context" "github.com/slimtoolkit/slim/pkg/app/sensor/monitor/fanotify" "github.com/slimtoolkit/slim/pkg/app/sensor/monitor/ptrace" "github.com/slimtoolkit/slim/pkg/report" ) // Base monitor stub. type monitorStub struct { ctx context.Context cancelFn context.CancelFunc errorCh chan error } func newMonitorStub(ctx context.Context) *monitorStub { ctx, cancelFn := context.WithCancel(ctx) return &monitorStub{ ctx: ctx, cancelFn: cancelFn, errorCh: make(chan error), } } func (m *monitorStub) Start() error { return nil } func (m *monitorStub) Cancel() { select { case <-m.ctx.Done(): return default: } m.cancelFn() close(m.errorCh) } func (m *monitorStub) Done() <-chan struct{} { return m.ctx.Done() } // fan monitor stub implements fanotify.Monitor type FanMonitorStub struct { *monitorStub } var _ fanotify.Monitor = &FanMonitorStub{} func NewFanMonitor(ctx context.Context) *FanMonitorStub { return &FanMonitorStub{ monitorStub: newMonitorStub(ctx), } } func (m *FanMonitorStub) Status() (*report.FanMonitorReport, error) { return nil, nil } // ptrace monitor stub implements ptrace.Monitor type PtMonitorStub struct { *monitorStub } var _ ptrace.Monitor = &PtMonitorStub{} func NewPtMonitor(ctx context.Context) *PtMonitorStub { return &PtMonitorStub{ monitorStub: newMonitorStub(ctx), } } func (m *PtMonitorStub) Status() (*report.PtMonitorReport, error) { return nil, nil } ================================================ FILE: pkg/test/util/time.go ================================================ package util import ( "context" "time" ) func Delayed(ctx context.Context, delay time.Duration, fn func()) { timer := time.NewTimer(delay) select { case <-ctx.Done(): if !timer.Stop() { <-timer.C } return case <-timer.C: fn() } } ================================================ FILE: pkg/third_party/compose-go/.github/CODEOWNERS ================================================ * @ndeloof ================================================ FILE: pkg/third_party/compose-go/.github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: gomod directory: "/" schedule: interval: weekly open-pull-requests-limit: 10 ignore: - dependency-name: github.com/sirupsen/logrus versions: - 1.8.0 - 1.8.1 - dependency-name: github.com/google/go-cmp versions: - 0.5.5 ================================================ FILE: pkg/third_party/compose-go/.github/workflows/ci.yml ================================================ on: [push, pull_request] name: Continuous integration jobs: validate: name: validate runs-on: ubuntu-latest timeout-minutes: 5 steps: - name: Checkout uses: actions/checkout@v2 - name: Lint code run: DOCKER_BUILDKIT=1 make lint - name: Check license run: DOCKER_BUILDKIT=1 make check-license test: strategy: matrix: go-version: [1.16.x] platform: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} timeout-minutes: 5 steps: - name: Install Go uses: actions/setup-go@v2 with: go-version: ${{ matrix.go-version }} - name: Checkout code uses: actions/checkout@v2 - name: Test run: go test ./... ================================================ FILE: pkg/third_party/compose-go/.github/workflows/release.yml ================================================ name: Release on: push: tags: - "v1*" jobs: release: runs-on: ubuntu-latest steps: - uses: ncipollo/release-action@v1 with: token: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: pkg/third_party/compose-go/.gitignore ================================================ ### IDEs ### .idea/* ================================================ FILE: pkg/third_party/compose-go/.pre-commit-config.yaml ================================================ repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v2.4.0 hooks: - id: check-yaml exclude: '^vendor/' - id: end-of-file-fixer exclude: '^vendor/' - id: trailing-whitespace exclude: '^vendor/' - repo: https://github.com/dnephin/pre-commit-golang rev: v0.3.5 hooks: - id: go-fmt - id: golangci-lint - id: go-imports ================================================ FILE: pkg/third_party/compose-go/CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. ================================================ FILE: pkg/third_party/compose-go/CONTRIBUTING.md ================================================ # Contributing Contributions should be made via pull requests. Pull requests will be reviewed by one or more maintainers and merged when acceptable. The goal of the Compose Go library is to facilitate parsing and loading Compose files. ## Commit Messages Commit messages should follow best practices and explain the context of the problem and how it was solved– including any caveats or follow up changes required. They should tell the story of the change and provide readers an understanding of what led to it. [How to Write a Git Commit Message](http://chris.beams.io/posts/git-commit/) provides a good guide for how to do so. In practice, the best approach to maintaining a nice commit message is to leverage a `git add -p` and `git commit --amend` to formulate a solid change set. This allows one to piece together a change, as information becomes available. If you squash a series of commits, don't just submit that. Re-write the commit message, as if the series of commits was a single stroke of brilliance. That said, there is no requirement to have a single commit for a pull request, as long as each commit tells the story. For example, if there is a feature that requires a package, it might make sense to have the package in a separate commit then have a subsequent commit that uses it. Remember, you're telling part of the story with the commit message. Don't make your chapter weird. ## Sign your work The sign-off is a simple line at the end of the explanation for the patch. Your signature certifies that you wrote the patch or otherwise have the right to pass it on as an open-source patch. The rules are pretty simple: if you can certify the below (from [developercertificate.org](http://developercertificate.org/)): ``` Developer Certificate of Origin Version 1.1 Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 660 York Street, Suite 102, San Francisco, CA 94110 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. ``` Then you just add a line to every git commit message: Signed-off-by: Joe Smith Use your real name (sorry, no pseudonyms or anonymous contributions.) If you set your `user.name` and `user.email` git configs, you can sign your commit automatically with `git commit -s`. ================================================ FILE: pkg/third_party/compose-go/LICENSE ================================================ Apache License Version 2.0, January 2004 https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS Copyright 2013-2017 Docker, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: pkg/third_party/compose-go/MAINTENANCE.md ================================================ # Maintenance The compose-go library has to be kept up-to-date with approved changes in the [Compose specification](https://github.com/compose-spec/compose-spec). As we define new attributes to be added to the spec, this typically requires: 1. Updating `schema` to latest version from compose-spec 1. Creating the matching struct/field in `types` 1. Creating the matching `CheckXX` method in `compatibility` 1. If the new attribute replaces a legacy one we want to deprecate, creating the adequate logic in `normalize.go` ================================================ FILE: pkg/third_party/compose-go/Makefile ================================================ # Copyright 2020 The Compose Specification Authors. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. IMAGE_PREFIX=composespec/conformance-tests- .PHONY: build build: ## Run tests go build ./... .PHONY: test test: ## Run tests gotestsum ./... .PHONY: fmt fmt: ## Format go files go fmt ./... .PHONY: build-validate-image build-validate-image: docker build . -f ci/Dockerfile -t $(IMAGE_PREFIX)validate .PHONY: lint lint: build-validate-image docker run --rm $(IMAGE_PREFIX)validate bash -c "golangci-lint run --config ./golangci.yml ./..." .PHONY: check-license check-license: build-validate-image docker run --rm $(IMAGE_PREFIX)validate bash -c "./scripts/validate/fileheader" .PHONY: setup setup: ## Setup the precommit hook @which pre-commit > /dev/null 2>&1 || (echo "pre-commit not installed see README." && false) @pre-commit install ================================================ FILE: pkg/third_party/compose-go/NOTICE ================================================ The Compose Specification Copyright 2020 The Compose Specification Authors ================================================ FILE: pkg/third_party/compose-go/README.md ================================================ # compose-go ![Continuous integration](https://github.com/compose-spec/compose-go/workflows/Continuous%20integration/badge.svg) Go reference library for parsing and loading Compose files as specified by the [Compose specification](https://github.com/compose-spec/compose-spec). ## Used by * [compose-ref](https://github.com/compose-spec/compose-ref) * [containerd/nerdctl](https://github.com/containerd/nerdctl) * [compose-cli](https://github.com/docker/compose-cli) ================================================ FILE: pkg/third_party/compose-go/ci/Dockerfile ================================================ # Copyright 2020 The Compose Specification Authors. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. FROM golang:1.16 WORKDIR /go/src ARG GOLANGCILINT_VERSION=v1.24.0 RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin ${GOLANGCILINT_VERSION} RUN go get -v -u github.com/kunalkushwaha/ltag && rm -rf /go/src/github.com/kunalkushwaha COPY . . ================================================ FILE: pkg/third_party/compose-go/cli/options.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package cli import ( "fmt" "io/ioutil" "os" "path/filepath" "regexp" "strings" "github.com/compose-spec/compose-go/errdefs" "github.com/compose-spec/compose-go/loader" "github.com/compose-spec/compose-go/types" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/ulyssessouza/godotenv" ) // ProjectOptions groups the command line options recommended for a Compose implementation type ProjectOptions struct { Name string WorkingDir string ConfigPaths []string Environment map[string]string EnvFile string loadOptions []func(*loader.Options) } type ProjectOptionsFn func(*ProjectOptions) error // NewProjectOptions creates ProjectOptions func NewProjectOptions(configs []string, opts ...ProjectOptionsFn) (*ProjectOptions, error) { options := &ProjectOptions{ ConfigPaths: configs, Environment: map[string]string{}, } for _, o := range opts { err := o(options) if err != nil { return nil, err } } return options, nil } // WithName defines ProjectOptions' name func WithName(name string) ProjectOptionsFn { return func(o *ProjectOptions) error { o.Name = name return nil } } // WithWorkingDirectory defines ProjectOptions' working directory func WithWorkingDirectory(wd string) ProjectOptionsFn { return func(o *ProjectOptions) error { if wd == "" { return nil } abs, err := filepath.Abs(wd) if err != nil { return err } o.WorkingDir = abs return nil } } // WithConfigFileEnv allow to set compose config file paths by COMPOSE_FILE environment variable func WithConfigFileEnv(o *ProjectOptions) error { if len(o.ConfigPaths) > 0 { return nil } sep := o.Environment[ComposePathSeparator] if sep == "" { sep = string(os.PathListSeparator) } f, ok := o.Environment[ComposeFilePath] if ok { paths, err := absolutePaths(strings.Split(f, sep)) o.ConfigPaths = paths return err } return nil } // WithDefaultConfigPath searches for default config files from working directory func WithDefaultConfigPath(o *ProjectOptions) error { if len(o.ConfigPaths) > 0 { return nil } pwd, err := o.GetWorkingDir() if err != nil { return err } for { candidates := findFiles(DefaultFileNames, pwd) if len(candidates) > 0 { winner := candidates[0] if len(candidates) > 1 { logrus.Warnf("Found multiple config files with supported names: %s", strings.Join(candidates, ", ")) logrus.Warnf("Using %s", winner) } o.ConfigPaths = append(o.ConfigPaths, winner) overrides := findFiles(DefaultOverrideFileNames, pwd) if len(overrides) > 0 { if len(overrides) > 1 { logrus.Warnf("Found multiple override files with supported names: %s", strings.Join(overrides, ", ")) logrus.Warnf("Using %s", overrides[0]) } o.ConfigPaths = append(o.ConfigPaths, overrides[0]) } return nil } parent := filepath.Dir(pwd) if parent == pwd { return errors.Wrap(errdefs.ErrNotFound, "can't find a suitable configuration file in this directory or any parent") } pwd = parent } } // WithEnv defines a key=value set of variables used for compose file interpolation func WithEnv(env []string) ProjectOptionsFn { return func(o *ProjectOptions) error { for k, v := range getAsEqualsMap(env) { o.Environment[k] = v } return nil } } // WithDiscardEnvFile sets discards the `env_file` section after resolving to // the `environment` section func WithDiscardEnvFile(o *ProjectOptions) error { o.loadOptions = append(o.loadOptions, loader.WithDiscardEnvFiles) return nil } // WithLoadOptions provides a hook to control how compose files are loaded func WithLoadOptions(loadOptions ...func(*loader.Options)) ProjectOptionsFn { return func(o *ProjectOptions) error { o.loadOptions = append(o.loadOptions, loadOptions...) return nil } } // WithOsEnv imports environment variables from OS func WithOsEnv(o *ProjectOptions) error { for k, v := range getAsEqualsMap(os.Environ()) { o.Environment[k] = v } return nil } // WithEnvFile set an alternate env file func WithEnvFile(file string) ProjectOptionsFn { return func(options *ProjectOptions) error { options.EnvFile = file return nil } } // WithDotEnv imports environment variables from .env file func WithDotEnv(o *ProjectOptions) error { dotEnvFile := o.EnvFile if dotEnvFile == "" { wd, err := o.GetWorkingDir() if err != nil { return err } dotEnvFile = filepath.Join(wd, ".env") } abs, err := filepath.Abs(dotEnvFile) if err != nil { return err } dotEnvFile = abs s, err := os.Stat(dotEnvFile) if os.IsNotExist(err) { if o.EnvFile != "" { return errors.Errorf("Couldn't find env file: %s", o.EnvFile) } return nil } if err != nil { return err } if s.IsDir() { if o.EnvFile != "" { return errors.Errorf("%s is a directory", dotEnvFile) } } file, err := os.Open(dotEnvFile) if err != nil { return err } defer file.Close() notInEnvSet := make(map[string]interface{}) env, err := godotenv.ParseWithLookup(file, func(k string) (string, bool) { v, ok := os.LookupEnv(k) if !ok { notInEnvSet[k] = nil return "", true } return v, true }) if err != nil { return err } for k, v := range env { if _, ok := notInEnvSet[k]; ok { continue } o.Environment[k] = v } return nil } // WithInterpolation set ProjectOptions to enable/skip interpolation func WithInterpolation(interpolation bool) ProjectOptionsFn { return func(o *ProjectOptions) error { o.loadOptions = append(o.loadOptions, func(options *loader.Options) { options.SkipInterpolation = !interpolation }) return nil } } // WithResolvedPaths set ProjectOptions to enable paths resolution func WithResolvedPaths(resolve bool) ProjectOptionsFn { return func(o *ProjectOptions) error { o.loadOptions = append(o.loadOptions, func(options *loader.Options) { options.ResolvePaths = resolve }) return nil } } // DefaultFileNames defines the Compose file names for auto-discovery (in order of preference) var DefaultFileNames = []string{"compose.yaml", "compose.yml", "docker-compose.yml", "docker-compose.yaml"} // DefaultOverrideFileNames defines the Compose override file names for auto-discovery (in order of preference) var DefaultOverrideFileNames = []string{"compose.override.yml", "compose.override.yaml", "docker-compose.override.yml", "docker-compose.override.yaml"} const ( ComposeProjectName = "COMPOSE_PROJECT_NAME" ComposePathSeparator = "COMPOSE_PATH_SEPARATOR" ComposeFilePath = "COMPOSE_FILE" ) func (o ProjectOptions) GetWorkingDir() (string, error) { if o.WorkingDir != "" { return o.WorkingDir, nil } for _, path := range o.ConfigPaths { if path != "-" { absPath, err := filepath.Abs(path) if err != nil { return "", err } return filepath.Dir(absPath), nil } } return os.Getwd() } // ProjectFromOptions load a compose project based on command line options func ProjectFromOptions(options *ProjectOptions) (*types.Project, error) { configPaths, err := getConfigPathsFromOptions(options) if err != nil { return nil, err } var configs []types.ConfigFile for _, f := range configPaths { var b []byte if f == "-" { b, err = ioutil.ReadAll(os.Stdin) if err != nil { return nil, err } } else { f, err := filepath.Abs(f) if err != nil { return nil, err } b, err = ioutil.ReadFile(f) if err != nil { return nil, err } } configs = append(configs, types.ConfigFile{ Filename: f, Content: b, }) } workingDir, err := options.GetWorkingDir() if err != nil { return nil, err } absWorkingDir, err := filepath.Abs(workingDir) if err != nil { return nil, err } var nameLoadOpt = func(opts *loader.Options) { if options.Name != "" { opts.Name = options.Name } else if nameFromEnv, ok := options.Environment[ComposeProjectName]; ok && nameFromEnv != "" { opts.Name = nameFromEnv } else { opts.Name = regexp.MustCompile(`(?m)[a-z]+[-_a-z0-9]*`).FindString(strings.ToLower(filepath.Base(absWorkingDir))) } opts.Name = strings.ToLower(opts.Name) } options.loadOptions = append(options.loadOptions, nameLoadOpt) project, err := loader.Load(types.ConfigDetails{ ConfigFiles: configs, WorkingDir: workingDir, Environment: options.Environment, }, options.loadOptions...) if err != nil { return nil, err } project.ComposeFiles = configPaths return project, nil } // getConfigPathsFromOptions retrieves the config files for project based on project options func getConfigPathsFromOptions(options *ProjectOptions) ([]string, error) { if len(options.ConfigPaths) != 0 { return absolutePaths(options.ConfigPaths) } return nil, errors.New("no configuration file provided") } func findFiles(names []string, pwd string) []string { candidates := []string{} for _, n := range names { f := filepath.Join(pwd, n) if _, err := os.Stat(f); err == nil { candidates = append(candidates, f) } } return candidates } func absolutePaths(p []string) ([]string, error) { var paths []string for _, f := range p { if f == "-" { paths = append(paths, f) continue } abs, err := filepath.Abs(f) if err != nil { return nil, err } f = abs if _, err := os.Stat(f); err != nil { return nil, err } paths = append(paths, f) } return paths, nil } // getAsEqualsMap split key=value formatted strings into a key : value map func getAsEqualsMap(em []string) map[string]string { m := make(map[string]string) for _, v := range em { kv := strings.SplitN(v, "=", 2) m[kv[0]] = kv[1] } return m } // getAsStringList format a key : value map into key=value strings func getAsStringList(em map[string]string) []string { m := make([]string, 0, len(em)) for k, v := range em { m = append(m, fmt.Sprintf("%s=%s", k, v)) } return m } ================================================ FILE: pkg/third_party/compose-go/cli/options_test.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package cli import ( "os" "path/filepath" "testing" "gotest.tools/v3/assert" ) func TestProjectName(t *testing.T) { t.Run("by name", func(t *testing.T) { opts, err := NewProjectOptions([]string{"testdata/simple/compose.yaml"}, WithName("my_project")) assert.NilError(t, err) p, err := ProjectFromOptions(opts) assert.NilError(t, err) assert.Equal(t, p.Name, "my_project") }) t.Run("by working dir", func(t *testing.T) { opts, err := NewProjectOptions([]string{"testdata/simple/compose.yaml"}, WithWorkingDirectory(".")) assert.NilError(t, err) p, err := ProjectFromOptions(opts) assert.NilError(t, err) assert.Equal(t, p.Name, "cli") }) t.Run("by compose file parent dir", func(t *testing.T) { opts, err := NewProjectOptions([]string{"testdata/simple/compose.yaml"}) assert.NilError(t, err) p, err := ProjectFromOptions(opts) assert.NilError(t, err) assert.Equal(t, p.Name, "simple") }) t.Run("by COMPOSE_PROJECT_NAME", func(t *testing.T) { os.Setenv("COMPOSE_PROJECT_NAME", "my_project_from_env") defer os.Unsetenv("COMPOSE_PROJECT_NAME") opts, err := NewProjectOptions([]string{"testdata/simple/compose.yaml"}, WithOsEnv) assert.NilError(t, err) p, err := ProjectFromOptions(opts) assert.NilError(t, err) assert.Equal(t, p.Name, "my_project_from_env") }) t.Run("by .env", func(t *testing.T) { wd, err := os.Getwd() assert.NilError(t, err) err = os.Chdir("testdata/env-file") assert.NilError(t, err) defer os.Chdir(wd) opts, err := NewProjectOptions(nil, WithDotEnv, WithConfigFileEnv) assert.NilError(t, err) p, err := ProjectFromOptions(opts) assert.NilError(t, err) assert.Equal(t, p.Name, "my_project_from_dot_env") }) } func TestProjectFromSetOfFiles(t *testing.T) { opts, err := NewProjectOptions([]string{ "testdata/simple/compose.yaml", "testdata/simple/compose-with-overrides.yaml", }, WithName("my_project")) assert.NilError(t, err) p, err := ProjectFromOptions(opts) assert.NilError(t, err) service, err := p.GetService("simple") assert.NilError(t, err) assert.Equal(t, service.Image, "haproxy") } func TestProjectComposefilesFromSetOfFiles(t *testing.T) { opts, err := NewProjectOptions([]string{}, WithWorkingDirectory("testdata/simple/"), WithName("my_project"), WithDefaultConfigPath, ) assert.NilError(t, err) p, err := ProjectFromOptions(opts) assert.NilError(t, err) absPath, _ := filepath.Abs(filepath.Join("testdata", "simple", "compose.yaml")) assert.DeepEqual(t, p.ComposeFiles, []string{absPath}) } func TestProjectComposefilesFromWorkingDir(t *testing.T) { opts, err := NewProjectOptions([]string{ "testdata/simple/compose.yaml", "testdata/simple/compose-with-overrides.yaml", }, WithName("my_project")) assert.NilError(t, err) p, err := ProjectFromOptions(opts) assert.NilError(t, err) currentDir, _ := os.Getwd() assert.DeepEqual(t, p.ComposeFiles, []string{ filepath.Join(currentDir, "testdata/simple/compose.yaml"), filepath.Join(currentDir, "testdata/simple/compose-with-overrides.yaml"), }) } func TestProjectWithDotEnv(t *testing.T) { wd, err := os.Getwd() assert.NilError(t, err) err = os.Chdir("testdata/simple") assert.NilError(t, err) defer os.Chdir(wd) opts, err := NewProjectOptions([]string{ "compose-with-variables.yaml", }, WithName("my_project"), WithDotEnv) assert.NilError(t, err) p, err := ProjectFromOptions(opts) assert.NilError(t, err) service, err := p.GetService("simple") assert.NilError(t, err) assert.Equal(t, service.Ports[0].Published, uint32(8000)) } func TestProjectWithDiscardEnvFile(t *testing.T) { opts, err := NewProjectOptions([]string{ "testdata/env-file/compose-with-env-file.yaml", }, WithDiscardEnvFile) assert.NilError(t, err) p, err := ProjectFromOptions(opts) assert.NilError(t, err) service, err := p.GetService("simple") assert.NilError(t, err) assert.Equal(t, *service.Environment["DEFAULT_PORT"], "8080") assert.Assert(t, service.EnvFile == nil) } func TestProjectNameFromWorkingDir(t *testing.T) { opts, err := NewProjectOptions([]string{ "testdata/env-file/compose-with-env-file.yaml", }) assert.NilError(t, err) p, err := ProjectFromOptions(opts) assert.NilError(t, err) assert.Equal(t, p.Name, "env-file") } func TestEnvMap(t *testing.T) { m := map[string]string{} m["foo"] = "bar" l := getAsStringList(m) assert.Equal(t, l[0], "foo=bar") m = getAsEqualsMap(l) assert.Equal(t, m["foo"], "bar") } ================================================ FILE: pkg/third_party/compose-go/cli/testdata/env-file/compose-with-env-file.yaml ================================================ version: "3" services: simple: image: nginx env_file: - ./simple-env ports: - 8000:80 ================================================ FILE: pkg/third_party/compose-go/cli/testdata/env-file/simple-env ================================================ DEFAULT_PORT=8080 ================================================ FILE: pkg/third_party/compose-go/cli/testdata/simple/compose-with-overrides.yaml ================================================ services: simple: image: haproxy ================================================ FILE: pkg/third_party/compose-go/cli/testdata/simple/compose-with-variables.yaml ================================================ services: simple: image: nginx ports: - ${PUBLIC_PORT}:80 ================================================ FILE: pkg/third_party/compose-go/cli/testdata/simple/compose.yaml ================================================ services: simple: image: nginx ================================================ FILE: pkg/third_party/compose-go/compatibility/allowlist.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package compatibility import ( "fmt" "github.com/compose-spec/compose-go/errdefs" "github.com/pkg/errors" ) // AllowList implements the Checker interface by rejecting all attributes that are not listed as "supported". type AllowList struct { Supported []string errors []error } // Errors returns the list of errors encountered when checking against the allow list func (c *AllowList) Errors() []error { return c.errors } func (c *AllowList) supported(attributes ...string) bool { for _, a := range attributes { for _, s := range c.Supported { if s == a { return true } } } return false } func (c *AllowList) Unsupported(message string, args ...interface{}) { c.errors = append(c.errors, errors.Wrap(errdefs.ErrUnsupported, fmt.Sprintf(message, args...))) } func (c *AllowList) Incompatible(message string, args ...interface{}) { c.errors = append(c.errors, errors.Wrap(errdefs.ErrIncompatible, fmt.Sprintf(message, args...))) } ================================================ FILE: pkg/third_party/compose-go/compatibility/allowlist_test.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package compatibility import ( "testing" "github.com/compose-spec/compose-go/errdefs" "github.com/compose-spec/compose-go/loader" "github.com/compose-spec/compose-go/types" "gotest.tools/v3/assert" ) func TestAllowList(t *testing.T) { var checker Checker = customChecker{ &AllowList{ Supported: []string{ "services.image", "services.network_mode", "services.privileged", "services.networks", "services.scale", }, }, } dict := []byte(` services: foo: image: busybox network_mode: host privileged: true mac_address: "a:b:c:d" `) project, err := loader.Load(types.ConfigDetails{ ConfigFiles: []types.ConfigFile{ {Filename: "filename.yml", Content: dict}, }, }) assert.NilError(t, err) Check(project, checker) errors := checker.Errors() assert.Check(t, len(errors) == 2) assert.Check(t, errdefs.IsUnsupportedError(errors[0])) assert.Equal(t, errors[0].Error(), "services.mac_address: unsupported attribute") assert.Check(t, errdefs.IsUnsupportedError(errors[1])) assert.Equal(t, errors[1].Error(), "services.network_mode=host: unsupported attribute") service, err := project.GetService("foo") assert.NilError(t, err) assert.Check(t, service.MacAddress == "") } type customChecker struct { *AllowList } func (c customChecker) CheckNetworkMode(service *types.ServiceConfig) { if service.NetworkMode == "host" { c.Unsupported("services.network_mode=host") } } ================================================ FILE: pkg/third_party/compose-go/compatibility/build.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package compatibility import "github.com/compose-spec/compose-go/types" func (c *AllowList) CheckBuild(service *types.ServiceConfig) bool { if !c.supported("services.build") && service.Build != nil { service.Build = nil c.Unsupported("services.build") return false } return true } func (c *AllowList) CheckBuildArgs(build *types.BuildConfig) { if !c.supported("services.build.args") && len(build.Args) != 0 { build.Args = nil c.Unsupported("services.build.args") } } func (c *AllowList) CheckBuildLabels(build *types.BuildConfig) { if !c.supported("services.build.labels") && len(build.Labels) != 0 { build.Labels = nil c.Unsupported("services.build.labels") } } func (c *AllowList) CheckBuildCacheFrom(build *types.BuildConfig) { if !c.supported("services.build.cache_from") && len(build.CacheFrom) != 0 { build.CacheFrom = nil c.Unsupported("services.build.cache_from") } } func (c *AllowList) CheckBuildExtraHosts(build *types.BuildConfig) { if !c.supported("services.build.extra_hosts") && len(build.ExtraHosts) != 0 { build.ExtraHosts = nil c.Unsupported("services.build.extra_hosts") } } func (c *AllowList) CheckBuildIsolation(build *types.BuildConfig) { if !c.supported("services.build.isolation") && build.Isolation != "" { build.Isolation = "" c.Unsupported("services.build.isolation") } } func (c *AllowList) CheckBuildNetwork(build *types.BuildConfig) { if !c.supported("services.build.network") && build.Network != "" { build.Network = "" c.Unsupported("services.build.network") } } func (c *AllowList) CheckBuildTarget(build *types.BuildConfig) { if !c.supported("services.build.target") && build.Target != "" { build.Target = "" c.Unsupported("services.build.target") } } ================================================ FILE: pkg/third_party/compose-go/compatibility/checker.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package compatibility import ( "github.com/compose-spec/compose-go/errdefs" "github.com/compose-spec/compose-go/types" ) type Checker interface { Errors() []error CheckBlkioConfig(build *types.ServiceConfig) CheckBlkioWeight(build *types.BlkioConfig) CheckBlkioWeightDevice(build *types.BlkioConfig) CheckBlkioDeviceReadBps(build *types.BlkioConfig) CheckBlkioDeviceReadIOps(build *types.BlkioConfig) CheckBlkioDeviceWriteBps(build *types.BlkioConfig) CheckBlkioDeviceWriteIOps(build *types.BlkioConfig) CheckBuild(build *types.ServiceConfig) bool CheckBuildArgs(build *types.BuildConfig) CheckBuildLabels(build *types.BuildConfig) CheckBuildCacheFrom(build *types.BuildConfig) CheckBuildExtraHosts(build *types.BuildConfig) CheckBuildIsolation(build *types.BuildConfig) CheckBuildNetwork(build *types.BuildConfig) CheckBuildTarget(build *types.BuildConfig) CheckCapAdd(service *types.ServiceConfig) CheckCapDrop(service *types.ServiceConfig) CheckCgroupParent(service *types.ServiceConfig) CheckCPUCount(service *types.ServiceConfig) CheckCPUPercent(service *types.ServiceConfig) CheckCPUPeriod(service *types.ServiceConfig) CheckCPUQuota(service *types.ServiceConfig) CheckCPURTRuntime(service *types.ServiceConfig) CheckCPURTPeriod(service *types.ServiceConfig) CheckCPUs(service *types.ServiceConfig) CheckCPUSet(service *types.ServiceConfig) CheckCPUShares(service *types.ServiceConfig) CheckCommand(service *types.ServiceConfig) CheckConfigs(service *types.ServiceConfig) CheckContainerName(service *types.ServiceConfig) CheckCredentialSpec(service *types.ServiceConfig) CheckDependsOn(service *types.ServiceConfig) CheckDevices(service *types.ServiceConfig) CheckDNS(service *types.ServiceConfig) CheckDNSOpts(service *types.ServiceConfig) CheckDNSSearch(service *types.ServiceConfig) CheckDomainName(service *types.ServiceConfig) CheckEntrypoint(service *types.ServiceConfig) CheckEnvironment(service *types.ServiceConfig) CheckEnvFile(service *types.ServiceConfig) CheckExpose(service *types.ServiceConfig) CheckExtends(service *types.ServiceConfig) CheckExternalLinks(service *types.ServiceConfig) CheckExtraHosts(service *types.ServiceConfig) CheckGroupAdd(service *types.ServiceConfig) CheckHostname(service *types.ServiceConfig) CheckHealthCheckTest(h *types.HealthCheckConfig) CheckHealthCheckTimeout(h *types.HealthCheckConfig) CheckHealthCheckInterval(h *types.HealthCheckConfig) CheckHealthCheckRetries(h *types.HealthCheckConfig) CheckHealthCheckStartPeriod(h *types.HealthCheckConfig) CheckImage(service *types.ServiceConfig) CheckInit(service *types.ServiceConfig) CheckIpc(service *types.ServiceConfig) CheckIsolation(service *types.ServiceConfig) CheckLabels(service *types.ServiceConfig) CheckLinks(service *types.ServiceConfig) CheckLoggingDriver(logging *types.LoggingConfig) CheckLoggingOptions(logging *types.LoggingConfig) CheckMemLimit(service *types.ServiceConfig) CheckMemReservation(service *types.ServiceConfig) CheckMemSwapLimit(service *types.ServiceConfig) CheckMemSwappiness(service *types.ServiceConfig) CheckMacAddress(service *types.ServiceConfig) CheckNet(service *types.ServiceConfig) CheckNetworkMode(service *types.ServiceConfig) CheckNetworkAliases(n *types.ServiceNetworkConfig) CheckNetworkIpv4Address(n *types.ServiceNetworkConfig) CheckNetworkIpv6Address(n *types.ServiceNetworkConfig) CheckOomKillDisable(service *types.ServiceConfig) CheckOomScoreAdj(service *types.ServiceConfig) CheckPid(service *types.ServiceConfig) CheckPidsLimit(service *types.ServiceConfig) CheckPlatform(service *types.ServiceConfig) CheckPortsMode(p *types.ServicePortConfig) CheckPortsTarget(p *types.ServicePortConfig) CheckPortsPublished(p *types.ServicePortConfig) CheckPortsProtocol(p *types.ServicePortConfig) CheckPrivileged(service *types.ServiceConfig) CheckPullPolicy(service *types.ServiceConfig) CheckReadOnly(service *types.ServiceConfig) CheckRestart(service *types.ServiceConfig) CheckRuntime(service *types.ServiceConfig) CheckScale(service *types.ServiceConfig) CheckSecrets(service *types.ServiceConfig) CheckFileReferenceSource(s string, config *types.FileReferenceConfig) CheckFileReferenceTarget(s string, config *types.FileReferenceConfig) CheckFileReferenceUID(s string, config *types.FileReferenceConfig) CheckFileReferenceGID(s string, config *types.FileReferenceConfig) CheckFileReferenceMode(s string, config *types.FileReferenceConfig) CheckSecurityOpt(service *types.ServiceConfig) CheckShmSize(service *types.ServiceConfig) CheckStdinOpen(service *types.ServiceConfig) CheckStopGracePeriod(service *types.ServiceConfig) CheckStopSignal(service *types.ServiceConfig) CheckSysctls(service *types.ServiceConfig) CheckTmpfs(service *types.ServiceConfig) CheckTty(service *types.ServiceConfig) CheckUlimits(service *types.ServiceConfig) CheckUser(service *types.ServiceConfig) CheckUserNSMode(service *types.ServiceConfig) CheckUts(service *types.ServiceConfig) CheckVolumeDriver(service *types.ServiceConfig) CheckVolumesSource(config *types.ServiceVolumeConfig) CheckVolumesTarget(config *types.ServiceVolumeConfig) CheckVolumesReadOnly(config *types.ServiceVolumeConfig) CheckVolumesConsistency(config *types.ServiceVolumeConfig) CheckVolumesBind(config *types.ServiceVolumeBind) CheckVolumesVolume(config *types.ServiceVolumeVolume) CheckVolumesTmpfs(config *types.ServiceVolumeTmpfs) CheckVolumesFrom(service *types.ServiceConfig) CheckWorkingDir(service *types.ServiceConfig) CheckVolumeConfigDriver(config *types.VolumeConfig) CheckVolumeConfigDriverOpts(config *types.VolumeConfig) CheckVolumeConfigExternal(config *types.VolumeConfig) CheckVolumeConfigLabels(config *types.VolumeConfig) CheckFileObjectConfigFile(s string, config *types.FileObjectConfig) CheckFileObjectConfigExternal(s string, config *types.FileObjectConfig) CheckFileObjectConfigLabels(s string, config *types.FileObjectConfig) CheckFileObjectConfigDriver(s string, config *types.FileObjectConfig) CheckFileObjectConfigDriverOpts(s string, config *types.FileObjectConfig) CheckFileObjectConfigTemplateDriver(s string, config *types.FileObjectConfig) CheckDeploy(deploy *types.ServiceConfig) bool CheckDeployEndpointMode(deploy *types.DeployConfig) CheckDeployLabels(deploy *types.DeployConfig) CheckDeployMode(deploy *types.DeployConfig) CheckDeployReplicas(deploy *types.DeployConfig) CheckDeployRestartPolicy(deploy *types.DeployConfig) bool CheckDeployRollbackConfig(deploy *types.DeployConfig) bool CheckDeployUpdateConfig(deploy *types.DeployConfig) bool CheckPlacementConstraints(p *types.Placement) CheckPlacementMaxReplicas(p *types.Placement) CheckPlacementPreferences(p *types.Placement) CheckRestartPolicyDelay(policy *types.RestartPolicy) CheckRestartPolicyCondition(policy *types.RestartPolicy) CheckRestartPolicyMaxAttempts(policy *types.RestartPolicy) CheckRestartPolicyWindow(policy *types.RestartPolicy) CheckUpdateConfigDelay(rollback string, config *types.UpdateConfig) CheckUpdateConfigFailureAction(rollback string, config *types.UpdateConfig) CheckUpdateConfigMaxFailureRatio(rollback string, config *types.UpdateConfig) CheckUpdateConfigMonitor(rollback string, config *types.UpdateConfig) CheckUpdateConfigOrder(rollback string, config *types.UpdateConfig) CheckUpdateConfigParallelism(rollback string, config *types.UpdateConfig) CheckDeployResourcesNanoCPUs(s string, resource *types.Resource) CheckDeployResourcesMemoryBytes(s string, resource *types.Resource) CheckDeployResourcesDevices(s string, resource *types.Resource) CheckDeployResourcesDevicesCapabilities(s string, r types.DeviceRequest) CheckDeployResourcesDevicesCount(s string, r types.DeviceRequest) CheckDeployResourcesDevicesIDs(s string, r types.DeviceRequest) CheckDeployResourcesDevicesDriver(s string, r types.DeviceRequest) CheckDeployResourcesGenericResources(s string, resource *types.Resource) CheckDeployResourcesLimits(deploy *types.DeployConfig) bool CheckDeployResourcesReservations(deploy *types.DeployConfig) bool CheckHealthCheck(service *types.ServiceConfig) bool CheckLogging(service *types.ServiceConfig) bool CheckNetworks(service *types.ServiceConfig) bool CheckPorts(service *types.ServiceConfig) bool CheckServiceVolumes(service *types.ServiceConfig) bool CheckNetworkConfigIpam(network *types.NetworkConfig) CheckNetworkConfigIpamSubnet(config *types.IPAMPool) CheckNetworkConfigIpamGateway(config *types.IPAMPool) CheckNetworkConfigIpamIPRange(config *types.IPAMPool) CheckNetworkConfigIpamAuxiliaryAddresses(config *types.IPAMPool) CheckNetworkConfigDriver(network *types.NetworkConfig) CheckNetworkConfigDriverOpts(network *types.NetworkConfig) CheckNetworkConfigExternal(network *types.NetworkConfig) CheckNetworkConfigInternal(network *types.NetworkConfig) CheckNetworkConfigAttachable(network *types.NetworkConfig) CheckNetworkConfigLabels(network *types.NetworkConfig) } func Check(project *types.Project, c Checker) { for i, service := range project.Services { CheckServiceConfig(&service, c) project.Services[i] = service } for i, network := range project.Networks { CheckNetworkConfig(&network, c) project.Networks[i] = network } for i, volume := range project.Volumes { CheckVolumeConfig(&volume, c) project.Volumes[i] = volume } for i, config := range project.Configs { CheckConfigsConfig(&config, c) project.Configs[i] = config } for i, secret := range project.Secrets { CheckSecretsConfig(&secret, c) project.Secrets[i] = secret } } // IsCompatible return true if the checker didn't reported any incompatibility error func IsCompatible(c Checker) bool { for _, err := range c.Errors() { if errdefs.IsIncompatibleError(err) { return false } } return true } func CheckServiceConfig(service *types.ServiceConfig, c Checker) { c.CheckBlkioConfig(service) if service.Build != nil && c.CheckBuild(service) { c.CheckBuildArgs(service.Build) c.CheckBuildLabels(service.Build) c.CheckBuildCacheFrom(service.Build) c.CheckBuildNetwork(service.Build) c.CheckBuildTarget(service.Build) } c.CheckCapAdd(service) c.CheckCapDrop(service) c.CheckCgroupParent(service) c.CheckCPUCount(service) c.CheckCPUPercent(service) c.CheckCPUPeriod(service) c.CheckCPUQuota(service) c.CheckCPURTPeriod(service) c.CheckCPURTRuntime(service) c.CheckCPUs(service) c.CheckCPUSet(service) c.CheckCPUShares(service) c.CheckCommand(service) c.CheckConfigs(service) c.CheckContainerName(service) c.CheckCredentialSpec(service) c.CheckDependsOn(service) if service.Deploy != nil && c.CheckDeploy(service) { c.CheckDeployEndpointMode(service.Deploy) c.CheckDeployLabels(service.Deploy) c.CheckDeployMode(service.Deploy) c.CheckPlacementConstraints(&service.Deploy.Placement) c.CheckPlacementMaxReplicas(&service.Deploy.Placement) c.CheckPlacementPreferences(&service.Deploy.Placement) c.CheckDeployReplicas(service.Deploy) if service.Deploy.Resources.Limits != nil && c.CheckDeployResourcesLimits(service.Deploy) { c.CheckDeployResourcesNanoCPUs(ResourceLimits, service.Deploy.Resources.Limits) c.CheckDeployResourcesMemoryBytes(ResourceLimits, service.Deploy.Resources.Limits) c.CheckDeployResourcesGenericResources(ResourceLimits, service.Deploy.Resources.Limits) } if service.Deploy.Resources.Reservations != nil && c.CheckDeployResourcesReservations(service.Deploy) { c.CheckDeployResourcesNanoCPUs(ResourceReservations, service.Deploy.Resources.Reservations) c.CheckDeployResourcesMemoryBytes(ResourceReservations, service.Deploy.Resources.Reservations) c.CheckDeployResourcesGenericResources(ResourceReservations, service.Deploy.Resources.Reservations) c.CheckDeployResourcesDevices(ResourceReservations, service.Deploy.Resources.Reservations) } if service.Deploy.RestartPolicy != nil && c.CheckDeployRestartPolicy(service.Deploy) { c.CheckRestartPolicyCondition(service.Deploy.RestartPolicy) c.CheckRestartPolicyDelay(service.Deploy.RestartPolicy) c.CheckRestartPolicyMaxAttempts(service.Deploy.RestartPolicy) c.CheckRestartPolicyWindow(service.Deploy.RestartPolicy) } if service.Deploy.UpdateConfig != nil && c.CheckDeployUpdateConfig(service.Deploy) { c.CheckUpdateConfigDelay(UpdateConfigUpdate, service.Deploy.UpdateConfig) c.CheckUpdateConfigFailureAction(UpdateConfigUpdate, service.Deploy.UpdateConfig) c.CheckUpdateConfigMaxFailureRatio(UpdateConfigUpdate, service.Deploy.UpdateConfig) c.CheckUpdateConfigMonitor(UpdateConfigUpdate, service.Deploy.UpdateConfig) c.CheckUpdateConfigOrder(UpdateConfigUpdate, service.Deploy.UpdateConfig) c.CheckUpdateConfigParallelism(UpdateConfigUpdate, service.Deploy.UpdateConfig) } if service.Deploy.RollbackConfig != nil && c.CheckDeployRollbackConfig(service.Deploy) { c.CheckUpdateConfigDelay(UpdateConfigRollback, service.Deploy.RollbackConfig) c.CheckUpdateConfigFailureAction(UpdateConfigRollback, service.Deploy.RollbackConfig) c.CheckUpdateConfigMaxFailureRatio(UpdateConfigRollback, service.Deploy.RollbackConfig) c.CheckUpdateConfigMonitor(UpdateConfigRollback, service.Deploy.RollbackConfig) c.CheckUpdateConfigOrder(UpdateConfigRollback, service.Deploy.RollbackConfig) c.CheckUpdateConfigParallelism(UpdateConfigRollback, service.Deploy.RollbackConfig) } } c.CheckDevices(service) c.CheckDNS(service) c.CheckDNSOpts(service) c.CheckDNSSearch(service) c.CheckDomainName(service) c.CheckEntrypoint(service) c.CheckEnvironment(service) c.CheckEnvFile(service) c.CheckExpose(service) c.CheckExtends(service) c.CheckExternalLinks(service) c.CheckExtraHosts(service) c.CheckGroupAdd(service) c.CheckHostname(service) if service.HealthCheck != nil && c.CheckHealthCheck(service) { c.CheckHealthCheckInterval(service.HealthCheck) c.CheckHealthCheckRetries(service.HealthCheck) c.CheckHealthCheckStartPeriod(service.HealthCheck) c.CheckHealthCheckTest(service.HealthCheck) c.CheckHealthCheckTimeout(service.HealthCheck) } c.CheckImage(service) c.CheckInit(service) c.CheckIpc(service) c.CheckIsolation(service) c.CheckLabels(service) c.CheckLinks(service) if service.Logging != nil && c.CheckLogging(service) { c.CheckLoggingDriver(service.Logging) c.CheckLoggingOptions(service.Logging) } c.CheckMemLimit(service) c.CheckMemReservation(service) c.CheckMemSwapLimit(service) c.CheckMemSwappiness(service) c.CheckMacAddress(service) c.CheckNet(service) c.CheckNetworkMode(service) if len(service.Networks) > 0 && c.CheckNetworks(service) { for _, n := range service.Networks { if n != nil { c.CheckNetworkAliases(n) c.CheckNetworkIpv4Address(n) c.CheckNetworkIpv6Address(n) } } } c.CheckOomKillDisable(service) c.CheckOomScoreAdj(service) c.CheckPid(service) c.CheckPidsLimit(service) c.CheckPlatform(service) if len(service.Ports) > 0 && c.CheckPorts(service) { for i, p := range service.Ports { c.CheckPortsMode(&p) c.CheckPortsTarget(&p) c.CheckPortsProtocol(&p) c.CheckPortsPublished(&p) service.Ports[i] = p } } c.CheckPrivileged(service) c.CheckPullPolicy(service) c.CheckReadOnly(service) c.CheckRestart(service) c.CheckRuntime(service) c.CheckScale(service) c.CheckSecrets(service) c.CheckSecurityOpt(service) c.CheckShmSize(service) c.CheckStdinOpen(service) c.CheckStopGracePeriod(service) c.CheckStopSignal(service) c.CheckSysctls(service) c.CheckTmpfs(service) c.CheckTty(service) c.CheckUlimits(service) c.CheckUser(service) c.CheckUserNSMode(service) c.CheckUts(service) c.CheckVolumeDriver(service) if len(service.Volumes) > 0 && c.CheckServiceVolumes(service) { for i, v := range service.Volumes { c.CheckVolumesSource(&v) c.CheckVolumesTarget(&v) c.CheckVolumesReadOnly(&v) switch v.Type { case types.VolumeTypeBind: c.CheckVolumesBind(v.Bind) case types.VolumeTypeVolume: c.CheckVolumesVolume(v.Volume) case types.VolumeTypeTmpfs: c.CheckVolumesTmpfs(v.Tmpfs) } service.Volumes[i] = v } } c.CheckVolumesFrom(service) c.CheckWorkingDir(service) } func CheckNetworkConfig(network *types.NetworkConfig, c Checker) { c.CheckNetworkConfigDriver(network) c.CheckNetworkConfigDriverOpts(network) c.CheckNetworkConfigIpam(network) c.CheckNetworkConfigExternal(network) c.CheckNetworkConfigInternal(network) c.CheckNetworkConfigAttachable(network) c.CheckNetworkConfigLabels(network) } func CheckVolumeConfig(config *types.VolumeConfig, c Checker) { c.CheckVolumeConfigDriver(config) c.CheckVolumeConfigDriverOpts(config) c.CheckVolumeConfigExternal(config) c.CheckVolumeConfigLabels(config) } func CheckConfigsConfig(config *types.ConfigObjConfig, c Checker) { ref := types.FileObjectConfig(*config) CheckFileObjectConfig("configs", &ref, c) } func CheckSecretsConfig(config *types.SecretConfig, c Checker) { ref := types.FileObjectConfig(*config) CheckFileObjectConfig("secrets", &ref, c) } func CheckFileObjectConfig(s string, config *types.FileObjectConfig, c Checker) { c.CheckFileObjectConfigDriver(s, config) c.CheckFileObjectConfigDriverOpts(s, config) c.CheckFileObjectConfigExternal(s, config) c.CheckFileObjectConfigFile(s, config) c.CheckFileObjectConfigLabels(s, config) c.CheckFileObjectConfigTemplateDriver(s, config) } ================================================ FILE: pkg/third_party/compose-go/compatibility/configs.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package compatibility import ( "fmt" "github.com/compose-spec/compose-go/types" ) func (c *AllowList) CheckFileObjectConfigFile(s string, config *types.FileObjectConfig) { k := fmt.Sprintf("%s.file", s) if !c.supported(k) && config.File != "" { config.File = "" c.Unsupported(k) } } func (c *AllowList) CheckFileObjectConfigExternal(s string, config *types.FileObjectConfig) { k := fmt.Sprintf("%s.external", s) if !c.supported(k) && config.External.External { config.External.External = false c.Unsupported(k) } } func (c *AllowList) CheckFileObjectConfigLabels(s string, config *types.FileObjectConfig) { k := fmt.Sprintf("%s.labels", s) if !c.supported(k) && len(config.Labels) != 0 { config.Labels = nil c.Unsupported(k) } } func (c *AllowList) CheckFileObjectConfigDriver(s string, config *types.FileObjectConfig) { k := fmt.Sprintf("%s.driver", s) if !c.supported(k) && config.Driver != "" { config.Driver = "" c.Unsupported(k) } } func (c *AllowList) CheckFileObjectConfigDriverOpts(s string, config *types.FileObjectConfig) { k := fmt.Sprintf("%s.driver_opts", s) if !c.supported(k) && len(config.DriverOpts) != 0 { config.DriverOpts = nil c.Unsupported(k) } } func (c *AllowList) CheckFileObjectConfigTemplateDriver(s string, config *types.FileObjectConfig) { k := fmt.Sprintf("%s.template_driver", s) if !c.supported(k) && config.TemplateDriver != "" { config.TemplateDriver = "" c.Unsupported(k) } } ================================================ FILE: pkg/third_party/compose-go/compatibility/deploy.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package compatibility import ( "fmt" "github.com/compose-spec/compose-go/types" ) func (c *AllowList) CheckDeploy(service *types.ServiceConfig) bool { if !c.supported("services.deploy") && service.Deploy != nil { service.Deploy = nil c.Unsupported("services.deploy") return false } return true } func (c *AllowList) CheckDeployMode(config *types.DeployConfig) { if !c.supported("services.deploy.mode") && config.Mode != "" { config.Mode = "" c.Unsupported("services.deploy.mode") } } func (c *AllowList) CheckDeployReplicas(config *types.DeployConfig) { if !c.supported("services.deploy.replicas") && config.Replicas != nil { config.Replicas = nil c.Unsupported("services.deploy.replicas") } } func (c *AllowList) CheckDeployLabels(config *types.DeployConfig) { if !c.supported("services.deploy.labels") && len(config.Labels) != 0 { config.Labels = nil c.Unsupported("services.deploy.labels") } } const ( UpdateConfigUpdate = "update_config" UpdateConfigRollback = "rolback_config" ) func (c *AllowList) CheckDeployUpdateConfig(config *types.DeployConfig) bool { if !c.supported("services.deploy.update_config") { config.UpdateConfig = nil c.Unsupported("services.deploy.update_config") return false } return true } func (c *AllowList) CheckDeployRollbackConfig(config *types.DeployConfig) bool { if !c.supported("services.deploy.rollback_config") { config.RollbackConfig = nil c.Unsupported("services.deploy.rollback_config") return false } return true } func (c *AllowList) CheckUpdateConfigParallelism(s string, config *types.UpdateConfig) { k := fmt.Sprintf("services.deploy.%s.parallelism", s) if !c.supported(k) && config.Parallelism != nil { config.Parallelism = nil c.Unsupported(k) } } func (c *AllowList) CheckUpdateConfigDelay(s string, config *types.UpdateConfig) { k := fmt.Sprintf("services.deploy.%s.delay", s) if !c.supported(k) && config.Delay != 0 { config.Delay = 0 c.Unsupported(k) } } func (c *AllowList) CheckUpdateConfigFailureAction(s string, config *types.UpdateConfig) { k := fmt.Sprintf("services.deploy.%s.failure_action", s) if !c.supported(k) && config.FailureAction != "" { config.FailureAction = "" c.Unsupported(k) } } func (c *AllowList) CheckUpdateConfigMonitor(s string, config *types.UpdateConfig) { k := fmt.Sprintf("services.deploy.%s.monitor", s) if !c.supported(k) && config.Monitor != 0 { config.Monitor = 0 c.Unsupported(k) } } func (c *AllowList) CheckUpdateConfigMaxFailureRatio(s string, config *types.UpdateConfig) { k := fmt.Sprintf("services.deploy.%s.max_failure_ratio", s) if !c.supported(k) && config.MaxFailureRatio != 0 { config.MaxFailureRatio = 0 c.Unsupported(k) } } func (c *AllowList) CheckUpdateConfigOrder(s string, config *types.UpdateConfig) { k := fmt.Sprintf("services.deploy.%s.order", s) if !c.supported(k) && config.Order != "" { config.Order = "" c.Unsupported(k) } } const ( ResourceLimits = "limits" ResourceReservations = "reservations" ) func (c *AllowList) CheckDeployResourcesLimits(config *types.DeployConfig) bool { if !c.supported("services.deploy.resources.limits") { config.Resources.Limits = nil c.Unsupported("services.deploy.resources.limits") return false } return true } func (c *AllowList) CheckDeployResourcesReservations(config *types.DeployConfig) bool { if !c.supported("services.deploy.resources.reservations") { config.Resources.Reservations = nil c.Unsupported("services.deploy.resources.reservations") return false } return true } func (c *AllowList) CheckDeployResourcesNanoCPUs(s string, r *types.Resource) { k := fmt.Sprintf("services.deploy.resources.%s.cpus", s) if !c.supported(k) && r.NanoCPUs != "" { r.NanoCPUs = "" c.Unsupported(k) } } func (c *AllowList) CheckDeployResourcesMemoryBytes(s string, r *types.Resource) { k := fmt.Sprintf("services.deploy.resources.%s.memory", s) if !c.supported(k) && r.MemoryBytes != 0 { r.MemoryBytes = 0 c.Unsupported(k) } } func (c *AllowList) CheckDeployResourcesDevices(s string, r *types.Resource) { if len(r.Devices) == 0 { return } k := fmt.Sprintf("services.deploy.resources.%s.devices", s) if !c.supported(k) { r.Devices = nil c.Unsupported(k) return } for _, d := range r.Devices { c.CheckDeployResourcesDevicesCapabilities(s, d) c.CheckDeployResourcesDevicesCount(s, d) c.CheckDeployResourcesDevicesIDs(s, d) c.CheckDeployResourcesDevicesDriver(s, d) } } func (c *AllowList) CheckDeployResourcesDevicesCapabilities(s string, r types.DeviceRequest) { k := fmt.Sprintf("services.deploy.resources.%s.devices.capabilities", s) if !c.supported(k) && len(r.Capabilities) != 0 { r.Capabilities = nil c.Unsupported(k) } } func (c *AllowList) CheckDeployResourcesDevicesCount(s string, r types.DeviceRequest) { k := fmt.Sprintf("services.deploy.resources.%s.devices.count", s) if !c.supported(k) && r.Count != 0 { r.Count = 0 c.Unsupported(k) } } func (c *AllowList) CheckDeployResourcesDevicesIDs(s string, r types.DeviceRequest) { k := fmt.Sprintf("services.deploy.resources.%s.devices.device_ids", s) if !c.supported(k) && len(r.IDs) != 0 { r.IDs = nil c.Unsupported(k) } } func (c *AllowList) CheckDeployResourcesDevicesDriver(s string, r types.DeviceRequest) { k := fmt.Sprintf("services.deploy.resources.%s.devices.driver", s) if !c.supported(k) && r.Driver != "" { r.Driver = "" c.Unsupported(k) } } func (c *AllowList) CheckDeployResourcesGenericResources(s string, r *types.Resource) { k := fmt.Sprintf("services.deploy.resources.%s.generic_resources", s) if !c.supported(k) && len(r.GenericResources) != 0 { r.GenericResources = nil c.Unsupported(k) } } func (c *AllowList) CheckDeployRestartPolicy(config *types.DeployConfig) bool { if !c.supported("services.deploy.restart_policy") { config.RestartPolicy = nil c.Unsupported("services.deploy.restart_policy") return false } return true } func (c *AllowList) CheckRestartPolicyCondition(p *types.RestartPolicy) { if !c.supported("services.deploy.restart_policy.condition") && p.Condition != "" { p.Condition = "" c.Unsupported("services.deploy.restart_policy.condition") } } func (c *AllowList) CheckRestartPolicyDelay(p *types.RestartPolicy) { if !c.supported("services.deploy.restart_policy.delay") && p.Delay != nil { p.Delay = nil c.Unsupported("services.deploy.restart_policy.delay") } } func (c *AllowList) CheckRestartPolicyMaxAttempts(p *types.RestartPolicy) { if !c.supported("services.deploy.restart_policy.max_attempts") && p.MaxAttempts != nil { p.MaxAttempts = nil c.Unsupported("services.deploy.restart_policy.max_attempts") } } func (c *AllowList) CheckRestartPolicyWindow(p *types.RestartPolicy) { if !c.supported("services.deploy.restart_policy.window") && p.Window != nil { p.Window = nil c.Unsupported("services.deploy.restart_policy.window") } } func (c *AllowList) CheckPlacementConstraints(p *types.Placement) { if !c.supported("services.deploy.placement", "services.deploy.placement.constraints") && len(p.Constraints) != 0 { p.Constraints = nil c.Unsupported("services.deploy.restart_policy.constraints") } } func (c *AllowList) CheckPlacementPreferences(p *types.Placement) { if !c.supported("services.deploy.placement", "services.deploy.placement.preferences") && p.Preferences != nil { p.Preferences = nil c.Unsupported("services.deploy.restart_policy.preferences") } } func (c *AllowList) CheckPlacementMaxReplicas(p *types.Placement) { if !c.supported("services.deploy.placement", "services.deploy.placement.max_replicas_per_node") && p.MaxReplicas != 0 { p.MaxReplicas = 0 c.Unsupported("services.deploy.restart_policy.max_replicas_per_node") } } func (c *AllowList) CheckDeployEndpointMode(config *types.DeployConfig) { if !c.supported("services.deploy.endpoint_mode") && config.EndpointMode != "" { config.EndpointMode = "" c.Unsupported("services.deploy.endpoint_mode") } } ================================================ FILE: pkg/third_party/compose-go/compatibility/networks.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package compatibility import "github.com/compose-spec/compose-go/types" func (c *AllowList) CheckNetworkConfig(network *types.NetworkConfig) { c.CheckNetworkConfigDriver(network) c.CheckNetworkConfigDriverOpts(network) c.CheckNetworkConfigIpam(network) c.CheckNetworkConfigExternal(network) c.CheckNetworkConfigInternal(network) c.CheckNetworkConfigAttachable(network) c.CheckNetworkConfigLabels(network) } func (c *AllowList) CheckNetworkConfigDriver(config *types.NetworkConfig) { if !c.supported("networks.driver") && config.Driver != "" { config.Driver = "" c.Unsupported("networks.driver") } } func (c *AllowList) CheckNetworkConfigDriverOpts(config *types.NetworkConfig) { if !c.supported("networks.driver_opts") && len(config.DriverOpts) != 0 { config.DriverOpts = nil c.Unsupported("networks.driver_opts") } } func (c *AllowList) CheckNetworkConfigIpam(config *types.NetworkConfig) { c.CheckNetworkConfigIpamDriver(&config.Ipam) if len(config.Ipam.Config) != 0 { if !c.supported("networks.ipam.config") { c.Unsupported("networks.ipam.config") return } for _, p := range config.Ipam.Config { c.CheckNetworkConfigIpamSubnet(p) c.CheckNetworkConfigIpamGateway(p) c.CheckNetworkConfigIpamIPRange(p) c.CheckNetworkConfigIpamAuxiliaryAddresses(p) } } } func (c *AllowList) CheckNetworkConfigIpamDriver(config *types.IPAMConfig) { if !c.supported("networks.ipam.driver") && config.Driver != "" { config.Driver = "" c.Unsupported("networks.ipam.driver") } } func (c *AllowList) CheckNetworkConfigIpamSubnet(config *types.IPAMPool) { if !c.supported("networks.ipam.config.subnet") && config.Subnet != "" { config.Subnet = "" c.Unsupported("networks.ipam.config.subnet") } } func (c *AllowList) CheckNetworkConfigIpamGateway(config *types.IPAMPool) { if !c.supported("networks.ipam.config.gateway") && config.Gateway != "" { config.Gateway = "" c.Unsupported("networks.ipam.config.gateway") } } func (c *AllowList) CheckNetworkConfigIpamIPRange(config *types.IPAMPool) { if !c.supported("networks.ipam.config.ip_range") && config.IPRange != "" { config.IPRange = "" c.Unsupported("networks.ipam.config.ip_range") } } func (c *AllowList) CheckNetworkConfigIpamAuxiliaryAddresses(config *types.IPAMPool) { if !c.supported("networks.ipam.config.aux_addresses") && len(config.AuxiliaryAddresses) > 0 { config.AuxiliaryAddresses = nil c.Unsupported("networks.ipam.config.aux_addresses") } } func (c *AllowList) CheckNetworkConfigExternal(config *types.NetworkConfig) { if !c.supported("networks.external") && config.External.External { config.External.External = false c.Unsupported("networks.external") } } func (c *AllowList) CheckNetworkConfigInternal(config *types.NetworkConfig) { if !c.supported("networks.internal") && config.Internal { config.Internal = false c.Unsupported("networks.internal") } } func (c *AllowList) CheckNetworkConfigAttachable(config *types.NetworkConfig) { if !c.supported("networks.attachable") && config.Attachable { config.Attachable = false c.Unsupported("networks.attachable") } } func (c *AllowList) CheckNetworkConfigLabels(config *types.NetworkConfig) { if !c.supported("networks.labels") && len(config.Labels) != 0 { config.Labels = nil c.Unsupported("networks.labels") } } ================================================ FILE: pkg/third_party/compose-go/compatibility/services.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package compatibility import ( "fmt" "github.com/compose-spec/compose-go/types" ) func (c *AllowList) CheckBlkioConfig(service *types.ServiceConfig) { if !c.supported("services.blkio_config") && service.BlkioConfig != nil { service.BlkioConfig = nil c.Unsupported("services.blkio_config") } } func (c *AllowList) CheckBlkioWeight(config *types.BlkioConfig) { if !c.supported("services.blkio_config.weight") && config.Weight != 0 { config.Weight = 0 c.Unsupported("services.blkio_config.weight") } } func (c *AllowList) CheckBlkioWeightDevice(config *types.BlkioConfig) { if !c.supported("services.blkio_config.weight_device") && len(config.WeightDevice) != 0 { config.WeightDevice = nil c.Unsupported("services.blkio_config.weight_device") } } func (c *AllowList) CheckBlkioDeviceReadBps(config *types.BlkioConfig) { if !c.supported("services.blkio_config.device_read_bps") && len(config.DeviceWriteBps) != 0 { config.DeviceWriteBps = nil c.Unsupported("services.blkio_config.device_read_bps") } } func (c *AllowList) CheckBlkioDeviceReadIOps(config *types.BlkioConfig) { if !c.supported("services.blkio_config.device_read_iops") && len(config.DeviceReadIOps) != 0 { config.DeviceReadIOps = nil c.Unsupported("services.blkio_config.device_read_iops") } } func (c *AllowList) CheckBlkioDeviceWriteBps(config *types.BlkioConfig) { if !c.supported("services.blkio_config.device_write_bps") && len(config.DeviceWriteBps) != 0 { config.DeviceWriteBps = nil c.Unsupported("services.blkio_config.device_write_bps") } } func (c *AllowList) CheckBlkioDeviceWriteIOps(config *types.BlkioConfig) { if !c.supported("services.blkio_config.device_write_iops") && len(config.DeviceWriteIOps) != 0 { config.DeviceWriteIOps = nil c.Unsupported("services.blkio_config.device_write_iops") } } func (c *AllowList) CheckCapAdd(service *types.ServiceConfig) { if !c.supported("services.cap_add") && len(service.CapAdd) != 0 { service.CapAdd = nil c.Unsupported("services.cap_add") } } func (c *AllowList) CheckCapDrop(service *types.ServiceConfig) { if !c.supported("services.cap_drop") && len(service.CapDrop) != 0 { service.CapDrop = nil c.Unsupported("services.cap_drop") } } func (c *AllowList) CheckCgroupParent(service *types.ServiceConfig) { if !c.supported("services.cgroup_parent") && service.CgroupParent != "" { service.CgroupParent = "" c.Unsupported("services.cgroup_parent") } } func (c *AllowList) CheckCPUQuota(service *types.ServiceConfig) { if !c.supported("services.cpu_quota") && service.CPUQuota != 0 { service.CPUQuota = 0 c.Unsupported("services.cpu_quota") } } func (c *AllowList) CheckCPUCount(service *types.ServiceConfig) { if !c.supported("services.cpu_count") && service.CPUCount != 0 { service.CPUCount = 0 c.Unsupported("services.cpu_count") } } func (c *AllowList) CheckCPUPercent(service *types.ServiceConfig) { if !c.supported("services.cpu_percent") && service.CPUPercent != 0 { service.CPUPercent = 0 c.Unsupported("services.cpu_percent") } } func (c *AllowList) CheckCPUPeriod(service *types.ServiceConfig) { if !c.supported("services.cpu_period") && service.CPUPeriod != 0 { service.CPUPeriod = 0 c.Unsupported("services.cpu_period") } } func (c *AllowList) CheckCPURTRuntime(service *types.ServiceConfig) { if !c.supported("services.cpu_rt_runtime") && service.CPURTRuntime != 0 { service.CPURTRuntime = 0 c.Unsupported("services.cpu_rt_period") } } func (c *AllowList) CheckCPURTPeriod(service *types.ServiceConfig) { if !c.supported("services.cpu_rt_period") && service.CPURTPeriod != 0 { service.CPURTPeriod = 0 c.Unsupported("services.cpu_rt_period") } } func (c *AllowList) CheckCPUs(service *types.ServiceConfig) { if !c.supported("services.cpus") && service.CPUS != 0 { service.CPUS = 0 c.Unsupported("services.cpus") } } func (c *AllowList) CheckCPUSet(service *types.ServiceConfig) { if !c.supported("services.cpuset") && service.CPUSet != "" { service.CPUSet = "" c.Unsupported("services.cpuset") } } func (c *AllowList) CheckCPUShares(service *types.ServiceConfig) { if !c.supported("services.cpu_shares") && service.CPUShares != 0 { service.CPUShares = 0 c.Unsupported("services.cpu_shares") } } func (c *AllowList) CheckCommand(service *types.ServiceConfig) { if !c.supported("services.command") && len(service.Command) != 0 { service.Command = nil c.Unsupported("services.command") } } func (c *AllowList) CheckConfigs(service *types.ServiceConfig) { if len(service.Configs) != 0 { if !c.supported("services.configs") { service.Configs = nil c.Unsupported("services.configs") return } for i, s := range service.Secrets { ref := types.FileReferenceConfig(s) c.CheckFileReference("configs", &ref) service.Secrets[i] = s } } } func (c *AllowList) CheckContainerName(service *types.ServiceConfig) { if !c.supported("services.container_name") && service.ContainerName != "" { service.ContainerName = "" c.Unsupported("services.container_name") } } func (c *AllowList) CheckCredentialSpec(service *types.ServiceConfig) { if !c.supported("services.credential_spec") && service.CredentialSpec != nil { service.CredentialSpec = nil c.Unsupported("services.credential_spec") } } func (c *AllowList) CheckDependsOn(service *types.ServiceConfig) { if !c.supported("services.depends_on") && len(service.DependsOn) != 0 { service.DependsOn = nil c.Unsupported("services.depends_on") } } func (c *AllowList) CheckDevices(service *types.ServiceConfig) { if !c.supported("services.devices") && len(service.Devices) != 0 { service.Devices = nil c.Unsupported("services.devices") } } func (c *AllowList) CheckDNS(service *types.ServiceConfig) { if !c.supported("services.dns") && service.DNS != nil { service.DNS = nil c.Unsupported("services.dns") } } func (c *AllowList) CheckDNSOpts(service *types.ServiceConfig) { if !c.supported("services.dns_opt") && len(service.DNSOpts) != 0 { service.DNSOpts = nil c.Unsupported("services.dns_opt") } } func (c *AllowList) CheckDNSSearch(service *types.ServiceConfig) { if !c.supported("services.dns_search") && len(service.DNSSearch) != 0 { service.DNSSearch = nil c.Unsupported("services.dns_search") } } func (c *AllowList) CheckDomainName(service *types.ServiceConfig) { if !c.supported("services.domainname") && service.DomainName != "" { service.DomainName = "" c.Unsupported("services.domainname") } } func (c *AllowList) CheckEntrypoint(service *types.ServiceConfig) { if !c.supported("services.entrypoint") && len(service.Entrypoint) != 0 { service.Entrypoint = nil c.Unsupported("services.entrypoint") } } func (c *AllowList) CheckEnvironment(service *types.ServiceConfig) { if !c.supported("services.environment") && len(service.Environment) != 0 { service.Environment = nil c.Unsupported("services.environment") } } func (c *AllowList) CheckEnvFile(service *types.ServiceConfig) { if !c.supported("services.env_file") && len(service.EnvFile) != 0 { service.EnvFile = nil c.Unsupported("services.env_file") } } func (c *AllowList) CheckExpose(service *types.ServiceConfig) { if !c.supported("services.expose") && len(service.Expose) != 0 { service.Expose = nil c.Unsupported("services.expose") } } func (c *AllowList) CheckExtends(service *types.ServiceConfig) { if !c.supported("services.extends") && len(service.Extends) != 0 { service.Extends = nil c.Unsupported("services.extends") } } func (c *AllowList) CheckExternalLinks(service *types.ServiceConfig) { if !c.supported("services.external_links") && len(service.ExternalLinks) != 0 { service.ExternalLinks = nil c.Unsupported("services.external_links") } } func (c *AllowList) CheckExtraHosts(service *types.ServiceConfig) { if !c.supported("services.extra_hosts") && len(service.ExtraHosts) != 0 { service.ExtraHosts = nil c.Unsupported("services.extra_hosts") } } func (c *AllowList) CheckGroupAdd(service *types.ServiceConfig) { if !c.supported("services.group_app") && len(service.GroupAdd) != 0 { service.GroupAdd = nil c.Unsupported("services.group_app") } } func (c *AllowList) CheckHostname(service *types.ServiceConfig) { if !c.supported("services.hostname") && service.Hostname != "" { service.Hostname = "" c.Unsupported("services.hostname") } } func (c *AllowList) CheckHealthCheck(service *types.ServiceConfig) bool { if !c.supported("services.healthcheck") { service.HealthCheck = nil c.Unsupported("services.healthcheck") return false } return true } func (c *AllowList) CheckHealthCheckTest(h *types.HealthCheckConfig) { if !c.supported("services.healthcheck.test") && len(h.Test) != 0 { h.Test = nil c.Unsupported("services.healthcheck.test") } } func (c *AllowList) CheckHealthCheckTimeout(h *types.HealthCheckConfig) { if !c.supported("services.healthcheck.timeout") && h.Timeout != nil { h.Timeout = nil c.Unsupported("services.healthcheck.timeout") } } func (c *AllowList) CheckHealthCheckInterval(h *types.HealthCheckConfig) { if !c.supported("services.healthcheck.interval") && h.Interval != nil { h.Interval = nil c.Unsupported("services.healthcheck.interval") } } func (c *AllowList) CheckHealthCheckRetries(h *types.HealthCheckConfig) { if !c.supported("services.healthcheck.retries") && h.Retries != nil { h.Retries = nil c.Unsupported("services.healthcheck.retries") } } func (c *AllowList) CheckHealthCheckStartPeriod(h *types.HealthCheckConfig) { if !c.supported("services.healthcheck.start_period") && h.StartPeriod != nil { h.StartPeriod = nil c.Unsupported("services.healthcheck.start_period") } } func (c *AllowList) CheckImage(service *types.ServiceConfig) { if !c.supported("services.image") && service.Image != "" { service.Image = "" c.Unsupported("services.image") } } func (c *AllowList) CheckInit(service *types.ServiceConfig) { if !c.supported("services.init") && service.Init != nil { service.Init = nil c.Unsupported("services.init") } } func (c *AllowList) CheckIpc(service *types.ServiceConfig) { if !c.supported("services.ipc") && service.Ipc != "" { service.Ipc = "" c.Unsupported("services.ipc") } } func (c *AllowList) CheckIsolation(service *types.ServiceConfig) { if !c.supported("services.isolation") && service.Isolation != "" { service.Isolation = "" c.Unsupported("services.isolation") } } func (c *AllowList) CheckLabels(service *types.ServiceConfig) { if !c.supported("services.labels") && len(service.Labels) != 0 { service.Labels = nil c.Unsupported("services.labels") } } func (c *AllowList) CheckLinks(service *types.ServiceConfig) { if !c.supported("services.links") && len(service.Links) != 0 { service.Links = nil c.Unsupported("services.links") } } func (c *AllowList) CheckLogging(service *types.ServiceConfig) bool { if !c.supported("services.logging") { service.Logging = nil c.Unsupported("services.logging") return false } return true } func (c *AllowList) CheckLoggingDriver(logging *types.LoggingConfig) { if !c.supported("services.logging.driver") && logging.Driver != "" { logging.Driver = "" c.Unsupported("services.logging.driver") } } func (c *AllowList) CheckLoggingOptions(logging *types.LoggingConfig) { if !c.supported("services.logging.options") && len(logging.Options) != 0 { logging.Options = nil c.Unsupported("services.logging.options") } } func (c *AllowList) CheckMemLimit(service *types.ServiceConfig) { if !c.supported("services.mem_limit") && service.MemLimit != 0 { service.MemLimit = 0 c.Unsupported("services.mem_limit") } } func (c *AllowList) CheckMemReservation(service *types.ServiceConfig) { if !c.supported("services.mem_reservation") && service.MemReservation != 0 { service.MemReservation = 0 c.Unsupported("services.mem_reservation") } } func (c *AllowList) CheckMemSwapLimit(service *types.ServiceConfig) { if !c.supported("services.memswap_limit") && service.MemSwapLimit != 0 { service.MemSwapLimit = 0 c.Unsupported("services.memswap_limit") } } func (c *AllowList) CheckMemSwappiness(service *types.ServiceConfig) { if !c.supported("services.mem_swappiness") && service.MemSwappiness != 0 { service.MemSwappiness = 0 c.Unsupported("services.mem_swappiness") } } func (c *AllowList) CheckMacAddress(service *types.ServiceConfig) { if !c.supported("services.mac_address") && service.MacAddress != "" { service.MacAddress = "" c.Unsupported("services.mac_address") } } func (c *AllowList) CheckNet(service *types.ServiceConfig) { if !c.supported("services.net") && service.Net != "" { service.Net = "" c.Unsupported("services.net") } } func (c *AllowList) CheckNetworkMode(service *types.ServiceConfig) { if !c.supported("services.network_mode") && service.NetworkMode != "" { service.NetworkMode = "" c.Unsupported("services.network_mode") } } func (c *AllowList) CheckNetworks(service *types.ServiceConfig) bool { if !c.supported("services.networks") { service.Networks = nil c.Unsupported("services.networks") return false } return true } func (c *AllowList) CheckNetworkAliases(n *types.ServiceNetworkConfig) { if !c.supported("services.networks.aliases") && len(n.Aliases) != 0 { n.Aliases = nil c.Unsupported("services.networks.aliases") } } func (c *AllowList) CheckNetworkIpv4Address(n *types.ServiceNetworkConfig) { if !c.supported("services.networks.ipv4_address") && n.Ipv4Address != "" { n.Ipv4Address = "" c.Unsupported("services.networks.ipv4_address") } } func (c *AllowList) CheckNetworkIpv6Address(n *types.ServiceNetworkConfig) { if !c.supported("services.networks.ipv6_address") && n.Ipv6Address != "" { n.Ipv6Address = "" c.Unsupported("services.networks.ipv6_address") } } func (c *AllowList) CheckOomKillDisable(service *types.ServiceConfig) { if !c.supported("services.oom_kill_disable") && service.OomKillDisable { service.OomKillDisable = false c.Unsupported("services.oom_kill_disable") } } func (c *AllowList) CheckOomScoreAdj(service *types.ServiceConfig) { if !c.supported("services.oom_score_adj") && service.OomScoreAdj != 0 { service.OomScoreAdj = 0 c.Unsupported("services.oom_score_adj") } } func (c *AllowList) CheckPid(service *types.ServiceConfig) { if !c.supported("services.pid") && service.Pid != "" { service.Pid = "" c.Unsupported("services.pid") } } func (c *AllowList) CheckPidsLimit(service *types.ServiceConfig) { if !c.supported("services.pids_limit") && service.PidsLimit != 0 { service.PidsLimit = 0 c.Unsupported("services.pids_limit") } } func (c *AllowList) CheckPlatform(service *types.ServiceConfig) { if !c.supported("services.platform") && service.Platform != "" { service.Platform = "" c.Unsupported("services.platform") } } func (c *AllowList) CheckPorts(service *types.ServiceConfig) bool { if !c.supported("services.ports") { service.Ports = nil c.Unsupported("services.ports") return false } return true } func (c *AllowList) CheckPortsMode(p *types.ServicePortConfig) { if !c.supported("services.ports.mode") && p.Mode != "" { p.Mode = "" c.Unsupported("services.ports.mode") } } func (c *AllowList) CheckPortsTarget(p *types.ServicePortConfig) { if !c.supported("services.ports.target") && p.Target != 0 { p.Target = 0 c.Unsupported("services.ports.target") } } func (c *AllowList) CheckPortsPublished(p *types.ServicePortConfig) { if !c.supported("services.ports.published") && p.Published != 0 { p.Published = 0 c.Unsupported("services.ports.published") } } func (c *AllowList) CheckPortsProtocol(p *types.ServicePortConfig) { if !c.supported("services.ports.protocol") && p.Protocol != "" { p.Protocol = "" c.Unsupported("services.ports.protocol") } } func (c *AllowList) CheckPrivileged(service *types.ServiceConfig) { if !c.supported("services.privileged") && service.Privileged { service.Privileged = false c.Unsupported("services.privileged") } } func (c *AllowList) CheckPullPolicy(service *types.ServiceConfig) { if !c.supported("services.pull_policy") && service.PullPolicy != "" { service.PullPolicy = "false" c.Unsupported("services.pull_policy") } } func (c *AllowList) CheckReadOnly(service *types.ServiceConfig) { if !c.supported("services.read_only") && service.ReadOnly { service.ReadOnly = false c.Unsupported("services.read_only") } } func (c *AllowList) CheckRestart(service *types.ServiceConfig) { if !c.supported("services.restart") && service.Restart != "" { service.Restart = "" c.Unsupported("services.restart") } } func (c *AllowList) CheckRuntime(service *types.ServiceConfig) { if !c.supported("services.runtime") && service.Runtime != "" { service.Runtime = "" c.Unsupported("services.runtime") } } func (c *AllowList) CheckScale(service *types.ServiceConfig) { if !c.supported("services.scale") && service.Scale != 0 { service.Scale = 0 c.Unsupported("services.scale") } } func (c *AllowList) CheckSecrets(service *types.ServiceConfig) { if len(service.Secrets) != 0 { if !c.supported("services.secrets") { service.Secrets = nil c.Unsupported("services.secrets") } for i, s := range service.Secrets { ref := types.FileReferenceConfig(s) c.CheckFileReference("services.secrets", &ref) service.Secrets[i] = s } } } func (c *AllowList) CheckFileReference(s string, config *types.FileReferenceConfig) { c.CheckFileReferenceSource(s, config) c.CheckFileReferenceTarget(s, config) c.CheckFileReferenceGID(s, config) c.CheckFileReferenceUID(s, config) c.CheckFileReferenceMode(s, config) } func (c *AllowList) CheckFileReferenceSource(s string, config *types.FileReferenceConfig) { k := fmt.Sprintf("%s.source", s) if !c.supported(k) && config.Source != "" { config.Source = "" c.Unsupported(k) } } func (c *AllowList) CheckFileReferenceTarget(s string, config *types.FileReferenceConfig) { k := fmt.Sprintf("%s.target", s) if !c.supported(k) && config.Target == "" { config.Target = "" c.Unsupported(k) } } func (c *AllowList) CheckFileReferenceUID(s string, config *types.FileReferenceConfig) { k := fmt.Sprintf("%s.uid", s) if !c.supported(k) && config.UID != "" { config.UID = "" c.Unsupported(k) } } func (c *AllowList) CheckFileReferenceGID(s string, config *types.FileReferenceConfig) { k := fmt.Sprintf("%s.gid", s) if !c.supported(k) && config.GID != "" { config.GID = "" c.Unsupported(k) } } func (c *AllowList) CheckFileReferenceMode(s string, config *types.FileReferenceConfig) { k := fmt.Sprintf("%s.mode", s) if !c.supported(k) && config.Mode != nil { config.Mode = nil c.Unsupported(k) } } func (c *AllowList) CheckSecurityOpt(service *types.ServiceConfig) { if !c.supported("services.security_opt") && len(service.SecurityOpt) != 0 { service.SecurityOpt = nil c.Unsupported("services.security_opt") } } func (c *AllowList) CheckShmSize(service *types.ServiceConfig) { if !c.supported("services.shm_size") && service.ShmSize != 0 { service.ShmSize = 0 c.Unsupported("services.shm_size") } } func (c *AllowList) CheckStdinOpen(service *types.ServiceConfig) { if !c.supported("services.stdin_open") && service.StdinOpen { service.StdinOpen = true c.Unsupported("services.stdin_open") } } func (c *AllowList) CheckStopGracePeriod(service *types.ServiceConfig) { if !c.supported("services.stop_grace_period") && service.StopGracePeriod != nil { service.StopGracePeriod = nil c.Unsupported("services.stop_grace_period") } } func (c *AllowList) CheckStopSignal(service *types.ServiceConfig) { if !c.supported("services.stop_signal") && service.StopSignal != "" { service.StopSignal = "" c.Unsupported("services.stop_signal") } } func (c *AllowList) CheckSysctls(service *types.ServiceConfig) { if !c.supported("services.sysctls") && len(service.Sysctls) != 0 { service.Sysctls = nil c.Unsupported("services.sysctls") } } func (c *AllowList) CheckTmpfs(service *types.ServiceConfig) { if !c.supported("services.tmpfs") && len(service.Tmpfs) != 0 { service.Tmpfs = nil c.Unsupported("services.tmpfs") } } func (c *AllowList) CheckTty(service *types.ServiceConfig) { if !c.supported("services.tty") && service.Tty { service.Tty = false c.Unsupported("services.tty") } } func (c *AllowList) CheckUlimits(service *types.ServiceConfig) { if !c.supported("services.ulimits") && len(service.Ulimits) != 0 { service.Ulimits = nil c.Unsupported("services.ulimits") } } func (c *AllowList) CheckUser(service *types.ServiceConfig) { if !c.supported("services.user") && service.User != "" { service.User = "" c.Unsupported("services.user") } } func (c *AllowList) CheckUserNSMode(service *types.ServiceConfig) { if !c.supported("services.userns_mode") && service.UserNSMode != "" { service.UserNSMode = "" c.Unsupported("services.userns_mode") } } func (c *AllowList) CheckUts(service *types.ServiceConfig) { if !c.supported("services.build") && service.Uts != "" { service.Uts = "" c.Unsupported("services.uts") } } func (c *AllowList) CheckVolumeDriver(service *types.ServiceConfig) { if !c.supported("services.volume_driver") && service.VolumeDriver != "" { service.VolumeDriver = "" c.Unsupported("services.volume_driver") } } func (c *AllowList) CheckServiceVolumes(service *types.ServiceConfig) bool { if !c.supported("services.volumes") { service.Volumes = nil c.Unsupported("services.volumes") return false } return true } func (c *AllowList) CheckVolumesSource(config *types.ServiceVolumeConfig) { if !c.supported("services.volumes.source") && config.Source != "" { config.Source = "" c.Unsupported("services.volumes.source") } } func (c *AllowList) CheckVolumesTarget(config *types.ServiceVolumeConfig) { if !c.supported("services.volumes.target") && config.Target != "" { config.Target = "" c.Unsupported("services.volumes.target") } } func (c *AllowList) CheckVolumesReadOnly(config *types.ServiceVolumeConfig) { if !c.supported("services.volumes.read_only") && config.ReadOnly { config.ReadOnly = false c.Unsupported("services.volumes.read_only") } } func (c *AllowList) CheckVolumesConsistency(config *types.ServiceVolumeConfig) { if !c.supported("services.volumes.consistency") && config.Consistency != "" { config.Consistency = "" c.Unsupported("services.volumes.consistency") } } func (c *AllowList) CheckVolumesBind(config *types.ServiceVolumeBind) { if config == nil { return } if !c.supported("services.volumes.bind.propagation") && config.Propagation != "" { config.Propagation = "" c.Unsupported("services.volumes.bind.propagation") } } func (c *AllowList) CheckVolumesVolume(config *types.ServiceVolumeVolume) { if config == nil { return } if !c.supported("services.volumes.nocopy") && config.NoCopy { config.NoCopy = false c.Unsupported("services.volumes.nocopy") } } func (c *AllowList) CheckVolumesTmpfs(config *types.ServiceVolumeTmpfs) { if config == nil { return } if !c.supported("services.volumes.tmpfs.size") && config.Size != 0 { config.Size = 0 c.Unsupported("services.volumes.tmpfs.size") } } func (c *AllowList) CheckVolumesFrom(service *types.ServiceConfig) { if !c.supported("services.volumes_from") && len(service.VolumesFrom) != 0 { service.VolumesFrom = nil c.Unsupported("services.volumes_from") } } func (c *AllowList) CheckWorkingDir(service *types.ServiceConfig) { if !c.supported("services.working_dir") && service.WorkingDir != "" { service.WorkingDir = "" c.Unsupported("services.working_dir") } } ================================================ FILE: pkg/third_party/compose-go/compatibility/volumes.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package compatibility import "github.com/compose-spec/compose-go/types" func (c *AllowList) CheckVolumeConfigDriver(config *types.VolumeConfig) { if !c.supported("volumes.driver") && config.Driver != "" { config.Driver = "" c.Unsupported("volumes.driver") } } func (c *AllowList) CheckVolumeConfigDriverOpts(config *types.VolumeConfig) { if !c.supported("volumes.driver_opts") && len(config.DriverOpts) != 0 { config.DriverOpts = nil c.Unsupported("volumes.driver_opts") } } func (c *AllowList) CheckVolumeConfigExternal(config *types.VolumeConfig) { if !c.supported("volumes.external") && config.External.External { config.External.External = false c.Unsupported("volumes.external") } } func (c *AllowList) CheckVolumeConfigLabels(config *types.VolumeConfig) { if !c.supported("volumes.labels") && len(config.Labels) != 0 { config.Labels = nil c.Unsupported("volumes.labels") } } ================================================ FILE: pkg/third_party/compose-go/errdefs/errors.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package errdefs import "errors" var ( // ErrNotFound is returned when an object is not found ErrNotFound = errors.New("not found") // ErrInvalid is returned when a compose project is invalid ErrInvalid = errors.New("invalid compose project") // ErrUnsupported is returned when a compose project uses an unsupported attribute ErrUnsupported = errors.New("unsupported attribute") // ErrIncompatible is returned when a compose project uses an incompatible attribute ErrIncompatible = errors.New("incompatible attribute") ) // IsNotFoundError returns true if the unwrapped error is ErrNotFound func IsNotFoundError(err error) bool { return errors.Is(err, ErrNotFound) } // IsInvalidError returns true if the unwrapped error is ErrInvalid func IsInvalidError(err error) bool { return errors.Is(err, ErrInvalid) } // IsUnsupportedError returns true if the unwrapped error is ErrUnsupported func IsUnsupportedError(err error) bool { return errors.Is(err, ErrUnsupported) } // IsIncompatibleError returns true if the unwrapped error is ErrIncompatible func IsIncompatibleError(err error) bool { return errors.Is(err, ErrIncompatible) } ================================================ FILE: pkg/third_party/compose-go/go.mod ================================================ module github.com/compose-spec/compose-go go 1.16 require ( github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e github.com/docker/go-connections v0.4.0 github.com/docker/go-units v0.4.0 github.com/google/go-cmp v0.5.5 github.com/imdario/mergo v0.3.12 github.com/mattn/go-shellwords v1.0.12 github.com/mitchellh/mapstructure v1.4.1 github.com/opencontainers/go-digest v1.0.0 github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.8.1 github.com/ulyssessouza/godotenv v1.3.1-0.20210806120901-e417b721114e github.com/xeipuuv/gojsonschema v1.2.0 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd // indirect gopkg.in/yaml.v2 v2.4.0 gotest.tools/v3 v3.0.3 ) ================================================ FILE: pkg/third_party/compose-go/go.sum ================================================ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/aws/aws-sdk-go v1.34.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= 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/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e h1:n81KvOMrLZa+VWHwST7dun9f0G98X3zREHS1ztYzZKU= github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e/go.mod h1:xpWTC2KnJMiDLkoawhsPQcXjvwATEBcbq0xevG2YR9M= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 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/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= github.com/google/go-cmp v0.3.0/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.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/ulyssessouza/godotenv v1.3.1-0.20210806120901-e417b721114e h1:byEYm3QADv5mDUesYKstWwRodf2RoxxC/YuGOxtdqJw= github.com/ulyssessouza/godotenv v1.3.1-0.20210806120901-e417b721114e/go.mod h1:9JN/BuU6Agy5aHyEoA5EIPkBsYbk0+2R42zJgYi/SlI= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/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-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-20190613194153-d28f0bde5980/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/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789 h1:NMiUjDZiD6qDVeBOzpImftxXzQHCp2Y2QLdmaqU9MRk= gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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.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= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= ================================================ FILE: pkg/third_party/compose-go/golangci.yml ================================================ run: deadline: 2m linters: disable-all: true enable: - gofmt - goimports - golint - gosimple - ineffassign - misspell - govet ================================================ FILE: pkg/third_party/compose-go/interpolation/interpolation.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package interpolation import ( "os" "strings" "github.com/compose-spec/compose-go/template" "github.com/pkg/errors" ) // Options supported by Interpolate type Options struct { // LookupValue from a key LookupValue LookupValue // TypeCastMapping maps key paths to functions to cast to a type TypeCastMapping map[Path]Cast // Substitution function to use Substitute func(string, template.Mapping) (string, error) } // LookupValue is a function which maps from variable names to values. // Returns the value as a string and a bool indicating whether // the value is present, to distinguish between an empty string // and the absence of a value. type LookupValue func(key string) (string, bool) // Cast a value to a new type, or return an error if the value can't be cast type Cast func(value string) (interface{}, error) // Interpolate replaces variables in a string with the values from a mapping func Interpolate(config map[string]interface{}, opts Options) (map[string]interface{}, error) { if opts.LookupValue == nil { opts.LookupValue = os.LookupEnv } if opts.TypeCastMapping == nil { opts.TypeCastMapping = make(map[Path]Cast) } if opts.Substitute == nil { opts.Substitute = template.Substitute } out := map[string]interface{}{} for key, value := range config { interpolatedValue, err := recursiveInterpolate(value, NewPath(key), opts) if err != nil { return out, err } out[key] = interpolatedValue } return out, nil } func recursiveInterpolate(value interface{}, path Path, opts Options) (interface{}, error) { switch value := value.(type) { case string: newValue, err := opts.Substitute(value, template.Mapping(opts.LookupValue)) if err != nil || newValue == value { return value, newPathError(path, err) } caster, ok := opts.getCasterForPath(path) if !ok { return newValue, nil } casted, err := caster(newValue) return casted, newPathError(path, errors.Wrap(err, "failed to cast to expected type")) case map[string]interface{}: out := map[string]interface{}{} for key, elem := range value { interpolatedElem, err := recursiveInterpolate(elem, path.Next(key), opts) if err != nil { return nil, err } out[key] = interpolatedElem } return out, nil case []interface{}: out := make([]interface{}, len(value)) for i, elem := range value { interpolatedElem, err := recursiveInterpolate(elem, path.Next(PathMatchList), opts) if err != nil { return nil, err } out[i] = interpolatedElem } return out, nil default: return value, nil } } func newPathError(path Path, err error) error { switch err := err.(type) { case nil: return nil case *template.InvalidTemplateError: return errors.Errorf( "invalid interpolation format for %s: %#v. You may need to escape any $ with another $.", path, err.Template) default: return errors.Wrapf(err, "error while interpolating %s", path) } } const pathSeparator = "." // PathMatchAll is a token used as part of a Path to match any key at that level // in the nested structure const PathMatchAll = "*" // PathMatchList is a token used as part of a Path to match items in a list const PathMatchList = "[]" // Path is a dotted path of keys to a value in a nested mapping structure. A * // section in a path will match any key in the mapping structure. type Path string // NewPath returns a new Path func NewPath(items ...string) Path { return Path(strings.Join(items, pathSeparator)) } // Next returns a new path by append part to the current path func (p Path) Next(part string) Path { return Path(string(p) + pathSeparator + part) } func (p Path) parts() []string { return strings.Split(string(p), pathSeparator) } func (p Path) matches(pattern Path) bool { patternParts := pattern.parts() parts := p.parts() if len(patternParts) != len(parts) { return false } for index, part := range parts { switch patternParts[index] { case PathMatchAll, part: continue default: return false } } return true } func (o Options) getCasterForPath(path Path) (Cast, bool) { for pattern, caster := range o.TypeCastMapping { if path.matches(pattern) { return caster, true } } return nil, false } ================================================ FILE: pkg/third_party/compose-go/interpolation/interpolation_test.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package interpolation import ( "testing" "strconv" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" "gotest.tools/v3/env" ) var defaults = map[string]string{ "USER": "jenny", "FOO": "bar", "count": "5", } func defaultMapping(name string) (string, bool) { val, ok := defaults[name] return val, ok } func TestInterpolate(t *testing.T) { services := map[string]interface{}{ "servicea": map[string]interface{}{ "image": "example:${USER}", "volumes": []interface{}{"$FOO:/target"}, "logging": map[string]interface{}{ "driver": "${FOO}", "options": map[string]interface{}{ "user": "$USER", }, }, }, } expected := map[string]interface{}{ "servicea": map[string]interface{}{ "image": "example:jenny", "volumes": []interface{}{"bar:/target"}, "logging": map[string]interface{}{ "driver": "bar", "options": map[string]interface{}{ "user": "jenny", }, }, }, } result, err := Interpolate(services, Options{LookupValue: defaultMapping}) assert.NilError(t, err) assert.Check(t, is.DeepEqual(expected, result)) } func TestInvalidInterpolation(t *testing.T) { services := map[string]interface{}{ "servicea": map[string]interface{}{ "image": "${", }, } _, err := Interpolate(services, Options{LookupValue: defaultMapping}) assert.Error(t, err, `invalid interpolation format for servicea.image: "${". You may need to escape any $ with another $.`) } func TestInterpolateWithDefaults(t *testing.T) { defer env.Patch(t, "FOO", "BARZ")() config := map[string]interface{}{ "networks": map[string]interface{}{ "foo": "thing_${FOO}", }, } expected := map[string]interface{}{ "networks": map[string]interface{}{ "foo": "thing_BARZ", }, } result, err := Interpolate(config, Options{}) assert.NilError(t, err) assert.Check(t, is.DeepEqual(expected, result)) } func TestInterpolateWithCast(t *testing.T) { config := map[string]interface{}{ "foo": map[string]interface{}{ "replicas": "$count", }, } toInt := func(value string) (interface{}, error) { return strconv.Atoi(value) } result, err := Interpolate(config, Options{ LookupValue: defaultMapping, TypeCastMapping: map[Path]Cast{NewPath(PathMatchAll, "replicas"): toInt}, }) assert.NilError(t, err) expected := map[string]interface{}{ "foo": map[string]interface{}{ "replicas": 5, }, } assert.Check(t, is.DeepEqual(expected, result)) } func TestPathMatches(t *testing.T) { var testcases = []struct { doc string path Path pattern Path expected bool }{ { doc: "pattern too short", path: NewPath("one", "two", "three"), pattern: NewPath("one", "two"), }, { doc: "pattern too long", path: NewPath("one", "two"), pattern: NewPath("one", "two", "three"), }, { doc: "pattern mismatch", path: NewPath("one", "three", "two"), pattern: NewPath("one", "two", "three"), }, { doc: "pattern mismatch with match-all part", path: NewPath("one", "three", "two"), pattern: NewPath(PathMatchAll, "two", "three"), }, { doc: "pattern match with match-all part", path: NewPath("one", "two", "three"), pattern: NewPath("one", "*", "three"), expected: true, }, { doc: "pattern match", path: NewPath("one", "two", "three"), pattern: NewPath("one", "two", "three"), expected: true, }, } for _, testcase := range testcases { assert.Check(t, is.Equal(testcase.expected, testcase.path.matches(testcase.pattern))) } } ================================================ FILE: pkg/third_party/compose-go/loader/example1.env ================================================ # passed through FOO=foo_from_env_file # overridden in example2.env BAR=bar_from_env_file # overridden in full-example.yml BAZ=baz_from_env_file ================================================ FILE: pkg/third_party/compose-go/loader/example2.env ================================================ BAR=bar_from_env_file_2 # overridden in configDetails.Environment QUX=quz_from_env_file_2 ================================================ FILE: pkg/third_party/compose-go/loader/full-example.yml ================================================ services: foo: build: context: ./dir dockerfile: Dockerfile args: foo: bar target: foo network: foo cache_from: - foo - bar labels: [FOO=BAR] cap_add: - ALL cap_drop: - NET_ADMIN - SYS_ADMIN cgroup_parent: m-executor-abcd # String or list command: bundle exec thin -p 3000 # command: ["bundle", "exec", "thin", "-p", "3000"] configs: - config1 - source: config2 target: /my_config uid: '103' gid: '103' mode: 0440 container_name: my-web-container depends_on: - db - redis deploy: mode: replicated replicas: 6 labels: [FOO=BAR] rollback_config: parallelism: 3 delay: 10s failure_action: continue monitor: 60s max_failure_ratio: 0.3 order: start-first update_config: parallelism: 3 delay: 10s failure_action: continue monitor: 60s max_failure_ratio: 0.3 order: start-first resources: limits: cpus: '0.001' memory: 50M reservations: cpus: '0.0001' memory: 20M generic_resources: - discrete_resource_spec: kind: 'gpu' value: 2 - discrete_resource_spec: kind: 'ssd' value: 1 restart_policy: condition: on-failure delay: 5s max_attempts: 3 window: 120s placement: constraints: [node=foo] max_replicas_per_node: 5 preferences: - spread: node.labels.az endpoint_mode: dnsrr devices: - "/dev/ttyUSB0:/dev/ttyUSB0" # String or list # dns: 8.8.8.8 dns: - 8.8.8.8 - 9.9.9.9 # String or list # dns_search: example.com dns_search: - dc1.example.com - dc2.example.com domainname: foo.com # String or list # entrypoint: /code/entrypoint.sh -p 3000 entrypoint: ["/code/entrypoint.sh", "-p", "3000"] # String or list # env_file: .env env_file: - ./example1.env - ./example2.env # Mapping or list # Mapping values can be strings, numbers or null # Booleans are not allowed - must be quoted environment: BAZ: baz_from_service_def QUX: # environment: # - RACK_ENV=development # - SHOW=true # - SESSION_SECRET # Items can be strings or numbers expose: - "3000" - 8000 external_links: - redis_1 - project_db_1:mysql - project_db_1:postgresql # Mapping or list # Mapping values must be strings # extra_hosts: # somehost: "162.242.195.82" # otherhost: "50.31.209.229" extra_hosts: - "somehost:162.242.195.82" - "otherhost:50.31.209.229" hostname: foo healthcheck: test: echo "hello world" interval: 10s timeout: 1s retries: 5 start_period: 15s # Any valid image reference - repo, tag, id, sha image: redis # image: ubuntu:14.04 # image: tutum/influxdb # image: example-registry.com:4000/postgresql # image: a4bc65fd # image: busybox@sha256:38a203e1986cf79639cfb9b2e1d6e773de84002feea2d4eb006b52004ee8502d ipc: host # Mapping or list # Mapping values can be strings, numbers or null labels: com.example.description: "Accounting webapp" com.example.number: 42 com.example.empty-label: # labels: # - "com.example.description=Accounting webapp" # - "com.example.number=42" # - "com.example.empty-label" links: - db - db:database - redis logging: driver: syslog options: syslog-address: "tcp://192.168.0.42:123" mac_address: 02:42:ac:11:65:43 # network_mode: "bridge" # network_mode: "host" # network_mode: "none" # Use the network mode of an arbitrary container from another service # network_mode: "service:db" # Use the network mode of another container, specified by name or id # network_mode: "container:some-container" network_mode: "container:0cfeab0f748b9a743dc3da582046357c6ef497631c1a016d28d2bf9b4f899f7b" networks: some-network: aliases: - alias1 - alias3 other-network: ipv4_address: 172.16.238.10 ipv6_address: 2001:3984:3989::10 other-other-network: pid: "host" ports: - 3000 - "3001-3005" - "8000:8000" - "9090-9091:8080-8081" - "49100:22" - "127.0.0.1:8001:8001" - "127.0.0.1:5000-5010:5000-5010" privileged: true read_only: true restart: always secrets: - secret1 - source: secret2 target: my_secret uid: '103' gid: '103' mode: 0440 security_opt: - label=level:s0:c100,c200 - label=type:svirt_apache_t stdin_open: true stop_grace_period: 20s stop_signal: SIGUSR1 sysctls: net.core.somaxconn: 1024 net.ipv4.tcp_syncookies: 0 # String or list # tmpfs: /run tmpfs: - /run - /tmp tty: true ulimits: # Single number or mapping with soft + hard limits nproc: 65535 nofile: soft: 20000 hard: 40000 user: someone volumes: # Just specify a path and let the Engine create a volume - /var/lib/mysql # Specify an absolute path mapping - /opt/data:/var/lib/mysql # Path on the host, relative to the Compose file - .:/code - ./static:/var/www/html # User-relative path - ~/configs:/etc/configs/:ro # Named volume - datavolume:/var/lib/mysql - type: bind source: ./opt target: /opt consistency: cached - type: tmpfs target: /opt tmpfs: size: 10000 working_dir: /code x-bar: baz x-foo: bar networks: # Entries can be null, which specifies simply that a network # called "{project name}_some-network" should be created and # use the default driver some-network: other-network: driver: overlay driver_opts: # Values can be strings or numbers foo: "bar" baz: 1 ipam: driver: overlay # driver_opts: # # Values can be strings or numbers # com.docker.network.enable_ipv6: "true" # com.docker.network.numeric_value: 1 config: - subnet: 172.28.0.0/16 ip_range: 172.28.5.0/24 gateway: 172.28.5.254 aux_addresses: host1: 172.28.1.5 host2: 172.28.1.6 host3: 172.28.1.7 - subnet: 2001:3984:3989::/64 gateway: 2001:3984:3989::1 labels: foo: bar external-network: # Specifies that a pre-existing network called "external-network" # can be referred to within this file as "external-network" external: true other-external-network: # Specifies that a pre-existing network called "my-cool-network" # can be referred to within this file as "other-external-network" external: name: my-cool-network x-bar: baz x-foo: bar volumes: # Entries can be null, which specifies simply that a volume # called "{project name}_some-volume" should be created and # use the default driver some-volume: other-volume: driver: flocker driver_opts: # Values can be strings or numbers foo: "bar" baz: 1 labels: foo: bar another-volume: name: "user_specified_name" driver: vsphere driver_opts: # Values can be strings or numbers foo: "bar" baz: 1 external-volume: # Specifies that a pre-existing volume called "external-volume" # can be referred to within this file as "external-volume" external: true other-external-volume: # Specifies that a pre-existing volume called "my-cool-volume" # can be referred to within this file as "other-external-volume" # This example uses the deprecated "volume.external.name" (replaced by "volume.name") external: name: my-cool-volume external-volume3: # Specifies that a pre-existing volume called "this-is-volume3" # can be referred to within this file as "external-volume3" name: this-is-volume3 external: true x-bar: baz x-foo: bar configs: config1: file: ./config_data labels: foo: bar config2: external: name: my_config config3: external: true config4: name: foo x-bar: baz x-foo: bar secrets: secret1: file: ./secret_data labels: foo: bar secret2: external: name: my_secret secret3: external: true secret4: name: bar x-bar: baz x-foo: bar x-bar: baz x-foo: bar x-nested: bar: baz foo: bar ================================================ FILE: pkg/third_party/compose-go/loader/full-struct_test.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package loader import ( "fmt" "path/filepath" "strings" "time" "github.com/compose-spec/compose-go/types" ) func fullExampleConfig(workingDir, homeDir string) *types.Config { return &types.Config{ Services: services(workingDir, homeDir), Networks: networks(), Volumes: volumes(), Configs: configs(workingDir), Secrets: secrets(workingDir), Extensions: map[string]interface{}{ "x-foo": "bar", "x-bar": "baz", "x-nested": map[string]interface{}{ "foo": "bar", "bar": "baz", }, }, } } func services(workingDir, homeDir string) []types.ServiceConfig { return []types.ServiceConfig{ { Name: "foo", Build: &types.BuildConfig{ Context: "./dir", Dockerfile: "Dockerfile", Args: map[string]*string{"foo": strPtr("bar")}, Target: "foo", Network: "foo", CacheFrom: []string{"foo", "bar"}, Labels: map[string]string{"FOO": "BAR"}, }, CapAdd: []string{"ALL"}, CapDrop: []string{"NET_ADMIN", "SYS_ADMIN"}, CgroupParent: "m-executor-abcd", Command: []string{"bundle", "exec", "thin", "-p", "3000"}, Configs: []types.ServiceConfigObjConfig{ { Source: "config1", }, { Source: "config2", Target: "/my_config", UID: "103", GID: "103", Mode: uint32Ptr(0440), }, }, ContainerName: "my-web-container", DependsOn: types.DependsOnConfig{ "db": {Condition: types.ServiceConditionStarted}, "redis": {Condition: types.ServiceConditionStarted}, }, Deploy: &types.DeployConfig{ Mode: "replicated", Replicas: uint64Ptr(6), Labels: map[string]string{"FOO": "BAR"}, RollbackConfig: &types.UpdateConfig{ Parallelism: uint64Ptr(3), Delay: types.Duration(10 * time.Second), FailureAction: "continue", Monitor: types.Duration(60 * time.Second), MaxFailureRatio: 0.3, Order: "start-first", }, UpdateConfig: &types.UpdateConfig{ Parallelism: uint64Ptr(3), Delay: types.Duration(10 * time.Second), FailureAction: "continue", Monitor: types.Duration(60 * time.Second), MaxFailureRatio: 0.3, Order: "start-first", }, Resources: types.Resources{ Limits: &types.Resource{ NanoCPUs: "0.001", MemoryBytes: 50 * 1024 * 1024, }, Reservations: &types.Resource{ NanoCPUs: "0.0001", MemoryBytes: 20 * 1024 * 1024, GenericResources: []types.GenericResource{ { DiscreteResourceSpec: &types.DiscreteGenericResource{ Kind: "gpu", Value: 2, }, }, { DiscreteResourceSpec: &types.DiscreteGenericResource{ Kind: "ssd", Value: 1, }, }, }, }, }, RestartPolicy: &types.RestartPolicy{ Condition: types.RestartPolicyOnFailure, Delay: durationPtr(5 * time.Second), MaxAttempts: uint64Ptr(3), Window: durationPtr(2 * time.Minute), }, Placement: types.Placement{ Constraints: []string{"node=foo"}, MaxReplicas: uint64(5), Preferences: []types.PlacementPreferences{ { Spread: "node.labels.az", }, }, }, EndpointMode: "dnsrr", }, Devices: []string{"/dev/ttyUSB0:/dev/ttyUSB0"}, DNS: []string{"8.8.8.8", "9.9.9.9"}, DNSSearch: []string{"dc1.example.com", "dc2.example.com"}, DomainName: "foo.com", Entrypoint: []string{"/code/entrypoint.sh", "-p", "3000"}, Environment: map[string]*string{ "FOO": strPtr("foo_from_env_file"), "BAR": strPtr("bar_from_env_file_2"), "BAZ": strPtr("baz_from_service_def"), "QUX": strPtr("qux_from_environment"), }, EnvFile: []string{ "./example1.env", "./example2.env", }, Expose: []string{"3000", "8000"}, ExternalLinks: []string{ "redis_1", "project_db_1:mysql", "project_db_1:postgresql", }, ExtraHosts: []string{ "somehost:162.242.195.82", "otherhost:50.31.209.229", }, Extensions: map[string]interface{}{ "x-bar": "baz", "x-foo": "bar", }, HealthCheck: &types.HealthCheckConfig{ Test: types.HealthCheckTest([]string{"CMD-SHELL", "echo \"hello world\""}), Interval: durationPtr(10 * time.Second), Timeout: durationPtr(1 * time.Second), Retries: uint64Ptr(5), StartPeriod: durationPtr(15 * time.Second), }, Hostname: "foo", Image: "redis", Ipc: "host", Labels: map[string]string{ "com.example.description": "Accounting webapp", "com.example.number": "42", "com.example.empty-label": "", }, Links: []string{ "db", "db:database", "redis", }, Logging: &types.LoggingConfig{ Driver: "syslog", Options: map[string]string{ "syslog-address": "tcp://192.168.0.42:123", }, }, MacAddress: "02:42:ac:11:65:43", NetworkMode: "container:0cfeab0f748b9a743dc3da582046357c6ef497631c1a016d28d2bf9b4f899f7b", Networks: map[string]*types.ServiceNetworkConfig{ "some-network": { Aliases: []string{"alias1", "alias3"}, Ipv4Address: "", Ipv6Address: "", }, "other-network": { Ipv4Address: "172.16.238.10", Ipv6Address: "2001:3984:3989::10", }, "other-other-network": nil, }, Pid: "host", Ports: []types.ServicePortConfig{ //"3000", { Mode: "ingress", Target: 3000, Protocol: "tcp", }, { Mode: "ingress", Target: 3001, Protocol: "tcp", }, { Mode: "ingress", Target: 3002, Protocol: "tcp", }, { Mode: "ingress", Target: 3003, Protocol: "tcp", }, { Mode: "ingress", Target: 3004, Protocol: "tcp", }, { Mode: "ingress", Target: 3005, Protocol: "tcp", }, //"8000:8000", { Mode: "ingress", Target: 8000, Published: 8000, Protocol: "tcp", }, //"9090-9091:8080-8081", { Mode: "ingress", Target: 8080, Published: 9090, Protocol: "tcp", }, { Mode: "ingress", Target: 8081, Published: 9091, Protocol: "tcp", }, //"49100:22", { Mode: "ingress", Target: 22, Published: 49100, Protocol: "tcp", }, //"127.0.0.1:8001:8001", { Mode: "ingress", HostIP: "127.0.0.1", Target: 8001, Published: 8001, Protocol: "tcp", }, //"127.0.0.1:5000-5010:5000-5010", { Mode: "ingress", HostIP: "127.0.0.1", Target: 5000, Published: 5000, Protocol: "tcp", }, { Mode: "ingress", HostIP: "127.0.0.1", Target: 5001, Published: 5001, Protocol: "tcp", }, { Mode: "ingress", HostIP: "127.0.0.1", Target: 5002, Published: 5002, Protocol: "tcp", }, { Mode: "ingress", HostIP: "127.0.0.1", Target: 5003, Published: 5003, Protocol: "tcp", }, { Mode: "ingress", HostIP: "127.0.0.1", Target: 5004, Published: 5004, Protocol: "tcp", }, { Mode: "ingress", HostIP: "127.0.0.1", Target: 5005, Published: 5005, Protocol: "tcp", }, { Mode: "ingress", HostIP: "127.0.0.1", Target: 5006, Published: 5006, Protocol: "tcp", }, { Mode: "ingress", HostIP: "127.0.0.1", Target: 5007, Published: 5007, Protocol: "tcp", }, { Mode: "ingress", HostIP: "127.0.0.1", Target: 5008, Published: 5008, Protocol: "tcp", }, { Mode: "ingress", HostIP: "127.0.0.1", Target: 5009, Published: 5009, Protocol: "tcp", }, { Mode: "ingress", HostIP: "127.0.0.1", Target: 5010, Published: 5010, Protocol: "tcp", }, }, Privileged: true, ReadOnly: true, Restart: types.RestartPolicyAlways, Scale: 1, Secrets: []types.ServiceSecretConfig{ { Source: "secret1", }, { Source: "secret2", Target: "my_secret", UID: "103", GID: "103", Mode: uint32Ptr(0440), }, }, SecurityOpt: []string{ "label=level:s0:c100,c200", "label=type:svirt_apache_t", }, StdinOpen: true, StopSignal: "SIGUSR1", StopGracePeriod: durationPtr(20 * time.Second), Sysctls: map[string]string{ "net.core.somaxconn": "1024", "net.ipv4.tcp_syncookies": "0", }, Tmpfs: []string{"/run", "/tmp"}, Tty: true, Ulimits: map[string]*types.UlimitsConfig{ "nproc": { Single: 65535, }, "nofile": { Soft: 20000, Hard: 40000, }, }, User: "someone", Volumes: []types.ServiceVolumeConfig{ {Target: "/var/lib/mysql", Type: "volume", Volume: &types.ServiceVolumeVolume{}}, {Source: "/opt/data", Target: "/var/lib/mysql", Type: "bind", Bind: &types.ServiceVolumeBind{CreateHostPath: true}}, {Source: workingDir, Target: "/code", Type: "bind", Bind: &types.ServiceVolumeBind{CreateHostPath: true}}, {Source: filepath.Join(workingDir, "static"), Target: "/var/www/html", Type: "bind", Bind: &types.ServiceVolumeBind{CreateHostPath: true}}, {Source: filepath.Join(homeDir, "/configs"), Target: "/etc/configs/", Type: "bind", ReadOnly: true, Bind: &types.ServiceVolumeBind{CreateHostPath: true}}, {Source: "datavolume", Target: "/var/lib/mysql", Type: "volume", Volume: &types.ServiceVolumeVolume{}}, {Source: filepath.Join(workingDir, "opt"), Target: "/opt", Consistency: "cached", Type: "bind"}, {Target: "/opt", Type: "tmpfs", Tmpfs: &types.ServiceVolumeTmpfs{ Size: int64(10000), }}, }, WorkingDir: "/code", }, } } func networks() map[string]types.NetworkConfig { return map[string]types.NetworkConfig{ "some-network": {}, "other-network": { Driver: "overlay", DriverOpts: map[string]string{ "foo": "bar", "baz": "1", }, Ipam: types.IPAMConfig{ Driver: "overlay", Config: []*types.IPAMPool{ { Subnet: "172.28.0.0/16", IPRange: "172.28.5.0/24", Gateway: "172.28.5.254", AuxiliaryAddresses: map[string]string{ "host1": "172.28.1.5", "host2": "172.28.1.6", "host3": "172.28.1.7", }, }, { Subnet: "2001:3984:3989::/64", Gateway: "2001:3984:3989::1", }, }, }, Labels: map[string]string{ "foo": "bar", }, }, "external-network": { Name: "external-network", External: types.External{External: true}, }, "other-external-network": { Name: "my-cool-network", External: types.External{External: true}, Extensions: map[string]interface{}{ "x-bar": "baz", "x-foo": "bar", }, }, } } func volumes() map[string]types.VolumeConfig { return map[string]types.VolumeConfig{ "some-volume": {}, "other-volume": { Driver: "flocker", DriverOpts: map[string]string{ "foo": "bar", "baz": "1", }, Labels: map[string]string{ "foo": "bar", }, }, "another-volume": { Name: "user_specified_name", Driver: "vsphere", DriverOpts: map[string]string{ "foo": "bar", "baz": "1", }, }, "external-volume": { Name: "external-volume", External: types.External{External: true}, }, "other-external-volume": { Name: "my-cool-volume", External: types.External{External: true}, }, "external-volume3": { Name: "this-is-volume3", External: types.External{External: true}, Extensions: map[string]interface{}{ "x-bar": "baz", "x-foo": "bar", }, }, } } func configs(workingDir string) map[string]types.ConfigObjConfig { return map[string]types.ConfigObjConfig{ "config1": { File: filepath.Join(workingDir, "config_data"), Labels: map[string]string{ "foo": "bar", }, }, "config2": { Name: "my_config", External: types.External{External: true}, }, "config3": { Name: "config3", External: types.External{External: true}, }, "config4": { Name: "foo", File: workingDir, Extensions: map[string]interface{}{ "x-bar": "baz", "x-foo": "bar", }, }, } } func secrets(workingDir string) map[string]types.SecretConfig { return map[string]types.SecretConfig{ "secret1": { File: filepath.Join(workingDir, "secret_data"), Labels: map[string]string{ "foo": "bar", }, }, "secret2": { Name: "my_secret", External: types.External{External: true}, }, "secret3": { Name: "secret3", External: types.External{External: true}, }, "secret4": { Name: "bar", File: workingDir, Extensions: map[string]interface{}{ "x-bar": "baz", "x-foo": "bar", }, }, } } func fullExampleYAML(workingDir, homeDir string) string { return fmt.Sprintf(`services: foo: build: context: ./dir dockerfile: Dockerfile args: foo: bar labels: FOO: BAR cache_from: - foo - bar network: foo target: foo cap_add: - ALL cap_drop: - NET_ADMIN - SYS_ADMIN cgroup_parent: m-executor-abcd command: - bundle - exec - thin - -p - "3000" configs: - source: config1 - source: config2 target: /my_config uid: "103" gid: "103" mode: 288 container_name: my-web-container depends_on: db: condition: service_started redis: condition: service_started deploy: mode: replicated replicas: 6 labels: FOO: BAR update_config: parallelism: 3 delay: 10s failure_action: continue monitor: 1m0s max_failure_ratio: 0.3 order: start-first rollback_config: parallelism: 3 delay: 10s failure_action: continue monitor: 1m0s max_failure_ratio: 0.3 order: start-first resources: limits: cpus: "0.001" memory: "52428800" reservations: cpus: "0.0001" memory: "20971520" generic_resources: - discrete_resource_spec: kind: gpu value: 2 - discrete_resource_spec: kind: ssd value: 1 restart_policy: condition: on-failure delay: 5s max_attempts: 3 window: 2m0s placement: constraints: - node=foo preferences: - spread: node.labels.az max_replicas_per_node: 5 endpoint_mode: dnsrr devices: - /dev/ttyUSB0:/dev/ttyUSB0 dns: - 8.8.8.8 - 9.9.9.9 dns_search: - dc1.example.com - dc2.example.com domainname: foo.com entrypoint: - /code/entrypoint.sh - -p - "3000" environment: BAR: bar_from_env_file_2 BAZ: baz_from_service_def FOO: foo_from_env_file QUX: qux_from_environment env_file: - ./example1.env - ./example2.env expose: - "3000" - "8000" external_links: - redis_1 - project_db_1:mysql - project_db_1:postgresql extra_hosts: - somehost:162.242.195.82 - otherhost:50.31.209.229 hostname: foo healthcheck: test: - CMD-SHELL - echo "hello world" timeout: 1s interval: 10s retries: 5 start_period: 15s image: redis ipc: host labels: com.example.description: Accounting webapp com.example.empty-label: "" com.example.number: "42" links: - db - db:database - redis logging: driver: syslog options: syslog-address: tcp://192.168.0.42:123 mac_address: 02:42:ac:11:65:43 network_mode: container:0cfeab0f748b9a743dc3da582046357c6ef497631c1a016d28d2bf9b4f899f7b networks: other-network: ipv4_address: 172.16.238.10 ipv6_address: 2001:3984:3989::10 other-other-network: null some-network: aliases: - alias1 - alias3 pid: host ports: - mode: ingress target: 3000 protocol: tcp - mode: ingress target: 3001 protocol: tcp - mode: ingress target: 3002 protocol: tcp - mode: ingress target: 3003 protocol: tcp - mode: ingress target: 3004 protocol: tcp - mode: ingress target: 3005 protocol: tcp - mode: ingress target: 8000 published: 8000 protocol: tcp - mode: ingress target: 8080 published: 9090 protocol: tcp - mode: ingress target: 8081 published: 9091 protocol: tcp - mode: ingress target: 22 published: 49100 protocol: tcp - mode: ingress host_ip: 127.0.0.1 target: 8001 published: 8001 protocol: tcp - mode: ingress host_ip: 127.0.0.1 target: 5000 published: 5000 protocol: tcp - mode: ingress host_ip: 127.0.0.1 target: 5001 published: 5001 protocol: tcp - mode: ingress host_ip: 127.0.0.1 target: 5002 published: 5002 protocol: tcp - mode: ingress host_ip: 127.0.0.1 target: 5003 published: 5003 protocol: tcp - mode: ingress host_ip: 127.0.0.1 target: 5004 published: 5004 protocol: tcp - mode: ingress host_ip: 127.0.0.1 target: 5005 published: 5005 protocol: tcp - mode: ingress host_ip: 127.0.0.1 target: 5006 published: 5006 protocol: tcp - mode: ingress host_ip: 127.0.0.1 target: 5007 published: 5007 protocol: tcp - mode: ingress host_ip: 127.0.0.1 target: 5008 published: 5008 protocol: tcp - mode: ingress host_ip: 127.0.0.1 target: 5009 published: 5009 protocol: tcp - mode: ingress host_ip: 127.0.0.1 target: 5010 published: 5010 protocol: tcp privileged: true read_only: true restart: always secrets: - source: secret1 - source: secret2 target: my_secret uid: "103" gid: "103" mode: 288 security_opt: - label=level:s0:c100,c200 - label=type:svirt_apache_t stdin_open: true stop_grace_period: 20s stop_signal: SIGUSR1 sysctls: net.core.somaxconn: "1024" net.ipv4.tcp_syncookies: "0" tmpfs: - /run - /tmp tty: true ulimits: nofile: soft: 20000 hard: 40000 nproc: 65535 user: someone volumes: - type: volume target: /var/lib/mysql volume: {} - type: bind source: /opt/data target: /var/lib/mysql bind: create_host_path: true - type: bind source: %s target: /code bind: create_host_path: true - type: bind source: %s target: /var/www/html bind: create_host_path: true - type: bind source: %s target: /etc/configs/ read_only: true bind: create_host_path: true - type: volume source: datavolume target: /var/lib/mysql volume: {} - type: bind source: %s target: /opt consistency: cached - type: tmpfs target: /opt tmpfs: size: 10000 working_dir: /code x-bar: baz x-foo: bar networks: external-network: name: external-network external: true other-external-network: name: my-cool-network external: true x-bar: baz x-foo: bar other-network: driver: overlay driver_opts: baz: "1" foo: bar ipam: driver: overlay config: - subnet: 172.28.0.0/16 gateway: 172.28.5.254 ip_range: 172.28.5.0/24 aux_addresses: host1: 172.28.1.5 host2: 172.28.1.6 host3: 172.28.1.7 - subnet: 2001:3984:3989::/64 gateway: 2001:3984:3989::1 labels: foo: bar some-network: {} volumes: another-volume: name: user_specified_name driver: vsphere driver_opts: baz: "1" foo: bar external-volume: name: external-volume external: true external-volume3: name: this-is-volume3 external: true x-bar: baz x-foo: bar other-external-volume: name: my-cool-volume external: true other-volume: driver: flocker driver_opts: baz: "1" foo: bar labels: foo: bar some-volume: {} secrets: secret1: file: %s labels: foo: bar secret2: name: my_secret external: true secret3: name: secret3 external: true secret4: name: bar file: %s x-bar: baz x-foo: bar configs: config1: file: %s labels: foo: bar config2: name: my_config external: true config3: name: config3 external: true config4: name: foo file: %s x-bar: baz x-foo: bar x-bar: baz x-foo: bar x-nested: bar: baz foo: bar `, filepath.Join(workingDir), filepath.Join(workingDir, "static"), filepath.Join(homeDir, "configs"), filepath.Join(workingDir, "opt"), filepath.Join(workingDir, "secret_data"), filepath.Join(workingDir), filepath.Join(workingDir, "config_data"), filepath.Join(workingDir)) } func fullExampleJSON(workingDir, homeDir string) string { return fmt.Sprintf(`{ "configs": { "config1": { "file": "%s", "external": false, "labels": { "foo": "bar" } }, "config2": { "name": "my_config", "external": true }, "config3": { "name": "config3", "external": true }, "config4": { "name": "foo", "file": "%s", "external": false } }, "networks": { "external-network": { "name": "external-network", "ipam": {}, "external": true }, "other-external-network": { "name": "my-cool-network", "ipam": {}, "external": true }, "other-network": { "driver": "overlay", "driver_opts": { "baz": "1", "foo": "bar" }, "ipam": { "driver": "overlay", "config": [ { "subnet": "172.28.0.0/16", "gateway": "172.28.5.254", "ip_range": "172.28.5.0/24", "aux_addresses": { "host1": "172.28.1.5", "host2": "172.28.1.6", "host3": "172.28.1.7" } }, { "subnet": "2001:3984:3989::/64", "gateway": "2001:3984:3989::1" } ] }, "external": false, "labels": { "foo": "bar" } }, "some-network": { "ipam": {}, "external": false } }, "secrets": { "secret1": { "file": "%s", "external": false, "labels": { "foo": "bar" } }, "secret2": { "name": "my_secret", "external": true }, "secret3": { "name": "secret3", "external": true }, "secret4": { "name": "bar", "file": "%s", "external": false } }, "services": { "foo": { "build": { "context": "./dir", "dockerfile": "Dockerfile", "args": { "foo": "bar" }, "labels": { "FOO": "BAR" }, "cache_from": [ "foo", "bar" ], "network": "foo", "target": "foo" }, "cap_add": [ "ALL" ], "cap_drop": [ "NET_ADMIN", "SYS_ADMIN" ], "cgroup_parent": "m-executor-abcd", "command": [ "bundle", "exec", "thin", "-p", "3000" ], "configs": [ { "source": "config1" }, { "source": "config2", "target": "/my_config", "uid": "103", "gid": "103", "mode": 288 } ], "container_name": "my-web-container", "depends_on": { "db": { "condition": "service_started" }, "redis": { "condition": "service_started" } }, "deploy": { "mode": "replicated", "replicas": 6, "labels": { "FOO": "BAR" }, "update_config": { "parallelism": 3, "delay": "10s", "failure_action": "continue", "monitor": "1m0s", "max_failure_ratio": 0.3, "order": "start-first" }, "rollback_config": { "parallelism": 3, "delay": "10s", "failure_action": "continue", "monitor": "1m0s", "max_failure_ratio": 0.3, "order": "start-first" }, "resources": { "limits": { "cpus": "0.001", "memory": "52428800" }, "reservations": { "cpus": "0.0001", "memory": "20971520", "generic_resources": [ { "discrete_resource_spec": { "kind": "gpu", "value": 2 } }, { "discrete_resource_spec": { "kind": "ssd", "value": 1 } } ] } }, "restart_policy": { "condition": "on-failure", "delay": "5s", "max_attempts": 3, "window": "2m0s" }, "placement": { "constraints": [ "node=foo" ], "preferences": [ { "spread": "node.labels.az" } ], "max_replicas_per_node": 5 }, "endpoint_mode": "dnsrr" }, "devices": [ "/dev/ttyUSB0:/dev/ttyUSB0" ], "dns": [ "8.8.8.8", "9.9.9.9" ], "dns_search": [ "dc1.example.com", "dc2.example.com" ], "domainname": "foo.com", "entrypoint": [ "/code/entrypoint.sh", "-p", "3000" ], "environment": { "BAR": "bar_from_env_file_2", "BAZ": "baz_from_service_def", "FOO": "foo_from_env_file", "QUX": "qux_from_environment" }, "env_file": [ "./example1.env", "./example2.env" ], "expose": [ "3000", "8000" ], "external_links": [ "redis_1", "project_db_1:mysql", "project_db_1:postgresql" ], "extra_hosts": [ "somehost:162.242.195.82", "otherhost:50.31.209.229" ], "hostname": "foo", "healthcheck": { "test": [ "CMD-SHELL", "echo \"hello world\"" ], "timeout": "1s", "interval": "10s", "retries": 5, "start_period": "15s" }, "image": "redis", "ipc": "host", "labels": { "com.example.description": "Accounting webapp", "com.example.empty-label": "", "com.example.number": "42" }, "links": [ "db", "db:database", "redis" ], "logging": { "driver": "syslog", "options": { "syslog-address": "tcp://192.168.0.42:123" } }, "mac_address": "02:42:ac:11:65:43", "network_mode": "container:0cfeab0f748b9a743dc3da582046357c6ef497631c1a016d28d2bf9b4f899f7b", "networks": { "other-network": { "ipv4_address": "172.16.238.10", "ipv6_address": "2001:3984:3989::10" }, "other-other-network": null, "some-network": { "aliases": [ "alias1", "alias3" ] } }, "pid": "host", "ports": [ { "mode": "ingress", "target": 3000, "protocol": "tcp" }, { "mode": "ingress", "target": 3001, "protocol": "tcp" }, { "mode": "ingress", "target": 3002, "protocol": "tcp" }, { "mode": "ingress", "target": 3003, "protocol": "tcp" }, { "mode": "ingress", "target": 3004, "protocol": "tcp" }, { "mode": "ingress", "target": 3005, "protocol": "tcp" }, { "mode": "ingress", "target": 8000, "published": 8000, "protocol": "tcp" }, { "mode": "ingress", "target": 8080, "published": 9090, "protocol": "tcp" }, { "mode": "ingress", "target": 8081, "published": 9091, "protocol": "tcp" }, { "mode": "ingress", "target": 22, "published": 49100, "protocol": "tcp" }, { "mode": "ingress", "host_ip": "127.0.0.1", "target": 8001, "published": 8001, "protocol": "tcp" }, { "mode": "ingress", "host_ip": "127.0.0.1", "target": 5000, "published": 5000, "protocol": "tcp" }, { "mode": "ingress", "host_ip": "127.0.0.1", "target": 5001, "published": 5001, "protocol": "tcp" }, { "mode": "ingress", "host_ip": "127.0.0.1", "target": 5002, "published": 5002, "protocol": "tcp" }, { "mode": "ingress", "host_ip": "127.0.0.1", "target": 5003, "published": 5003, "protocol": "tcp" }, { "mode": "ingress", "host_ip": "127.0.0.1", "target": 5004, "published": 5004, "protocol": "tcp" }, { "mode": "ingress", "host_ip": "127.0.0.1", "target": 5005, "published": 5005, "protocol": "tcp" }, { "mode": "ingress", "host_ip": "127.0.0.1", "target": 5006, "published": 5006, "protocol": "tcp" }, { "mode": "ingress", "host_ip": "127.0.0.1", "target": 5007, "published": 5007, "protocol": "tcp" }, { "mode": "ingress", "host_ip": "127.0.0.1", "target": 5008, "published": 5008, "protocol": "tcp" }, { "mode": "ingress", "host_ip": "127.0.0.1", "target": 5009, "published": 5009, "protocol": "tcp" }, { "mode": "ingress", "host_ip": "127.0.0.1", "target": 5010, "published": 5010, "protocol": "tcp" } ], "privileged": true, "read_only": true, "restart": "always", "secrets": [ { "source": "secret1" }, { "source": "secret2", "target": "my_secret", "uid": "103", "gid": "103", "mode": 288 } ], "security_opt": [ "label=level:s0:c100,c200", "label=type:svirt_apache_t" ], "stdin_open": true, "stop_grace_period": "20s", "stop_signal": "SIGUSR1", "sysctls": { "net.core.somaxconn": "1024", "net.ipv4.tcp_syncookies": "0" }, "tmpfs": [ "/run", "/tmp" ], "tty": true, "ulimits": { "nofile": { "soft": 20000, "hard": 40000 }, "nproc": 65535 }, "user": "someone", "volumes": [ { "type": "volume", "target": "/var/lib/mysql", "volume": {} }, { "type": "bind", "source": "/opt/data", "target": "/var/lib/mysql", "bind": { "create_host_path": true } }, { "type": "bind", "source": "%s", "target": "/code", "bind": { "create_host_path": true } }, { "type": "bind", "source": "%s", "target": "/var/www/html", "bind": { "create_host_path": true } }, { "type": "bind", "source": "%s", "target": "/etc/configs/", "read_only": true, "bind": { "create_host_path": true } }, { "type": "volume", "source": "datavolume", "target": "/var/lib/mysql", "volume": {} }, { "type": "bind", "source": "%s", "target": "/opt", "consistency": "cached" }, { "type": "tmpfs", "target": "/opt", "tmpfs": { "size": 10000 } } ], "working_dir": "/code" } }, "volumes": { "another-volume": { "name": "user_specified_name", "driver": "vsphere", "driver_opts": { "baz": "1", "foo": "bar" }, "external": false }, "external-volume": { "name": "external-volume", "external": true }, "external-volume3": { "name": "this-is-volume3", "external": true }, "other-external-volume": { "name": "my-cool-volume", "external": true }, "other-volume": { "driver": "flocker", "driver_opts": { "baz": "1", "foo": "bar" }, "external": false, "labels": { "foo": "bar" } }, "some-volume": { "external": false } }, "x-bar": "baz", "x-foo": "bar", "x-nested": { "bar": "baz", "foo": "bar" } }`, toPath(workingDir, "config_data"), toPath(workingDir), toPath(workingDir, "secret_data"), toPath(workingDir), toPath(workingDir), toPath(workingDir, "static"), toPath(homeDir, "configs"), toPath(workingDir, "opt")) } func toPath(path ...string) string { return strings.ReplaceAll(filepath.Join(path...), "\\", "\\\\") } ================================================ FILE: pkg/third_party/compose-go/loader/interpolate.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package loader import ( "strconv" "strings" interp "github.com/compose-spec/compose-go/interpolation" "github.com/compose-spec/compose-go/types" "github.com/pkg/errors" ) var interpolateTypeCastMapping = map[interp.Path]interp.Cast{ servicePath("configs", interp.PathMatchList, "mode"): toInt, servicePath("cpu_count"): toInt64, servicePath("cpu_percent"): toFloat, servicePath("cpu_period"): toInt64, servicePath("cpu_quota"): toInt64, servicePath("cpu_rt_period"): toInt64, servicePath("cpu_rt_runtime"): toInt64, servicePath("cpus"): toFloat32, servicePath("cpu_shares"): toInt64, servicePath("init"): toBoolean, servicePath("deploy", "replicas"): toInt, servicePath("deploy", "update_config", "parallelism"): toInt, servicePath("deploy", "update_config", "max_failure_ratio"): toFloat, servicePath("deploy", "rollback_config", "parallelism"): toInt, servicePath("deploy", "rollback_config", "max_failure_ratio"): toFloat, servicePath("deploy", "restart_policy", "max_attempts"): toInt, servicePath("deploy", "placement", "max_replicas_per_node"): toInt, servicePath("healthcheck", "retries"): toInt, servicePath("healthcheck", "disable"): toBoolean, servicePath("mem_limit"): toUnitBytes, servicePath("mem_reservation"): toUnitBytes, servicePath("memswap_limit"): toUnitBytes, servicePath("mem_swappiness"): toUnitBytes, servicePath("oom_kill_disable"): toBoolean, servicePath("oom_score_adj"): toInt64, servicePath("pids_limit"): toInt64, servicePath("ports", interp.PathMatchList, "target"): toInt, servicePath("ports", interp.PathMatchList, "published"): toInt, servicePath("privileged"): toBoolean, servicePath("read_only"): toBoolean, servicePath("scale"): toInt, servicePath("secrets", interp.PathMatchList, "mode"): toInt, servicePath("shm_size"): toUnitBytes, servicePath("stdin_open"): toBoolean, servicePath("stop_grace_period"): toDuration, servicePath("tty"): toBoolean, servicePath("ulimits", interp.PathMatchAll): toInt, servicePath("ulimits", interp.PathMatchAll, "hard"): toInt, servicePath("ulimits", interp.PathMatchAll, "soft"): toInt, servicePath("volumes", interp.PathMatchList, "read_only"): toBoolean, servicePath("volumes", interp.PathMatchList, "volume", "nocopy"): toBoolean, iPath("networks", interp.PathMatchAll, "external"): toBoolean, iPath("networks", interp.PathMatchAll, "internal"): toBoolean, iPath("networks", interp.PathMatchAll, "attachable"): toBoolean, iPath("volumes", interp.PathMatchAll, "external"): toBoolean, iPath("secrets", interp.PathMatchAll, "external"): toBoolean, iPath("configs", interp.PathMatchAll, "external"): toBoolean, } func iPath(parts ...string) interp.Path { return interp.NewPath(parts...) } func servicePath(parts ...string) interp.Path { return iPath(append([]string{"services", interp.PathMatchAll}, parts...)...) } func toInt(value string) (interface{}, error) { return strconv.Atoi(value) } func toInt64(value string) (interface{}, error) { return strconv.ParseInt(value, 10, 64) } func toUnitBytes(value string) (interface{}, error) { i, err := strconv.ParseInt(value, 10, 64) if err != nil { return nil, err } return types.UnitBytes(i), nil } func toDuration(value string) (interface{}, error) { i, err := strconv.ParseInt(value, 10, 64) if err != nil { return nil, err } return types.Duration(i), nil } func toFloat(value string) (interface{}, error) { return strconv.ParseFloat(value, 64) } func toFloat32(value string) (interface{}, error) { f, err := strconv.ParseFloat(value, 32) if err != nil { return nil, err } return float32(f), nil } // should match http://yaml.org/type/bool.html func toBoolean(value string) (interface{}, error) { switch strings.ToLower(value) { case "y", "yes", "true", "on": return true, nil case "n", "no", "false", "off": return false, nil default: return nil, errors.Errorf("invalid boolean: %s", value) } } ================================================ FILE: pkg/third_party/compose-go/loader/loader.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package loader import ( "fmt" "io/ioutil" "os" "path" "path/filepath" "reflect" "sort" "strings" "time" interp "github.com/compose-spec/compose-go/interpolation" //"github.com/compose-spec/compose-go/schema" "github.com/compose-spec/compose-go/template" "github.com/compose-spec/compose-go/types" "github.com/docker/go-units" "github.com/mattn/go-shellwords" "github.com/mitchellh/mapstructure" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/ulyssessouza/godotenv" "gopkg.in/yaml.v2" ) // Options supported by Load type Options struct { // Skip schema validation SkipValidation bool // Skip interpolation SkipInterpolation bool // Skip normalization SkipNormalization bool // Resolve paths ResolvePaths bool // Skip consistency check SkipConsistencyCheck bool // Skip extends SkipExtends bool // Interpolation options Interpolate *interp.Options // Discard 'env_file' entries after resolving to 'environment' section discardEnvFiles bool // Set project name Name string } // serviceRef identifies a reference to a service. It's used to detect cyclic // references in "extends". type serviceRef struct { filename string service string } type cycleTracker struct { loaded []serviceRef } func (ct *cycleTracker) Add(filename, service string) error { toAdd := serviceRef{filename: filename, service: service} for _, loaded := range ct.loaded { if toAdd == loaded { // Create an error message of the form: // Circular reference: // service-a in docker-compose.yml // extends service-b in docker-compose.yml // extends service-a in docker-compose.yml errLines := []string{ "Circular reference:", fmt.Sprintf(" %s in %s", ct.loaded[0].service, ct.loaded[0].filename), } for _, service := range append(ct.loaded[1:], toAdd) { errLines = append(errLines, fmt.Sprintf(" extends %s in %s", service.service, service.filename)) } return errors.New(strings.Join(errLines, "\n")) } } ct.loaded = append(ct.loaded, toAdd) return nil } // WithDiscardEnvFiles sets the Options to discard the `env_file` section after resolving to // the `environment` section func WithDiscardEnvFiles(opts *Options) { opts.discardEnvFiles = true } // WithSkipValidation sets the Options to skip validation when loading sections func WithSkipValidation(opts *Options) { opts.SkipValidation = true } // ParseYAML reads the bytes from a file, parses the bytes into a mapping // structure, and returns it. func ParseYAML(source []byte) (map[string]interface{}, error) { var cfg interface{} if err := yaml.Unmarshal(source, &cfg); err != nil { return nil, err } cfgMap, ok := cfg.(map[interface{}]interface{}) if !ok { return nil, errors.Errorf("Top-level object must be a mapping") } converted, err := convertToStringKeysRecursive(cfgMap, "") if err != nil { return nil, err } return converted.(map[string]interface{}), nil } // Load reads a ConfigDetails and returns a fully loaded configuration func Load(configDetails types.ConfigDetails, options ...func(*Options)) (*types.Project, error) { if len(configDetails.ConfigFiles) < 1 { return nil, errors.Errorf("No files specified") } opts := &Options{ Interpolate: &interp.Options{ Substitute: template.Substitute, LookupValue: configDetails.LookupEnv, TypeCastMapping: interpolateTypeCastMapping, }, } for _, op := range options { op(opts) } configs := []*types.Config{} for i, file := range configDetails.ConfigFiles { configDict := file.Config if configDict == nil { dict, err := parseConfig(file.Content, opts) if err != nil { return nil, err } configDict = dict file.Config = dict configDetails.ConfigFiles[i] = file } //QTMP //if !opts.SkipValidation { // if err := schema.Validate(configDict); err != nil { // return nil, err // } //} configDict = groupXFieldsIntoExtensions(configDict) cfg, err := loadSections(file.Filename, configDict, configDetails, opts) if err != nil { return nil, err } if opts.discardEnvFiles { for i := range cfg.Services { cfg.Services[i].EnvFile = nil } } configs = append(configs, cfg) } model, err := merge(configs) if err != nil { return nil, err } for _, s := range model.Services { var newEnvFiles types.StringList for _, ef := range s.EnvFile { newEnvFiles = append(newEnvFiles, absPath(configDetails.WorkingDir, ef)) } s.EnvFile = newEnvFiles } project := &types.Project{ Name: opts.Name, WorkingDir: configDetails.WorkingDir, Services: model.Services, Networks: model.Networks, Volumes: model.Volumes, Secrets: model.Secrets, Configs: model.Configs, Environment: configDetails.Environment, Extensions: model.Extensions, } if !opts.SkipNormalization { err = normalize(project, opts.ResolvePaths) if err != nil { return nil, err } } if !opts.SkipConsistencyCheck { err = checkConsistency(project) if err != nil { return nil, err } } return project, nil } func parseConfig(b []byte, opts *Options) (map[string]interface{}, error) { yaml, err := ParseYAML(b) if err != nil { return nil, err } if !opts.SkipInterpolation { return interp.Interpolate(yaml, *opts.Interpolate) } return yaml, err } func groupXFieldsIntoExtensions(dict map[string]interface{}) map[string]interface{} { extras := map[string]interface{}{} for key, value := range dict { if strings.HasPrefix(key, "x-") { extras[key] = value delete(dict, key) } if d, ok := value.(map[string]interface{}); ok { dict[key] = groupXFieldsIntoExtensions(d) } } if len(extras) > 0 { dict["extensions"] = extras } return dict } func loadSections(filename string, config map[string]interface{}, configDetails types.ConfigDetails, opts *Options) (*types.Config, error) { var err error cfg := types.Config{ Filename: filename, } cfg.Services, err = LoadServices(filename, getSection(config, "services"), configDetails.WorkingDir, configDetails.LookupEnv, opts) if err != nil { return nil, err } cfg.Networks, err = LoadNetworks(getSection(config, "networks")) if err != nil { return nil, err } cfg.Volumes, err = LoadVolumes(getSection(config, "volumes")) if err != nil { return nil, err } cfg.Secrets, err = LoadSecrets(getSection(config, "secrets"), configDetails, opts.ResolvePaths) if err != nil { return nil, err } cfg.Configs, err = LoadConfigObjs(getSection(config, "configs"), configDetails, opts.ResolvePaths) if err != nil { return nil, err } extensions := getSection(config, "extensions") if len(extensions) > 0 { cfg.Extensions = extensions } if err != nil { return nil, err } return &cfg, nil } func getSection(config map[string]interface{}, key string) map[string]interface{} { section, ok := config[key] if !ok { return map[string]interface{}{} } return section.(map[string]interface{}) } // ForbiddenPropertiesError is returned when there are properties in the Compose // file that are forbidden. type ForbiddenPropertiesError struct { Properties map[string]string } func (e *ForbiddenPropertiesError) Error() string { return "Configuration contains forbidden properties" } // Transform converts the source into the target struct with compose types transformer // and the specified transformers if any. func Transform(source interface{}, target interface{}, additionalTransformers ...Transformer) error { data := mapstructure.Metadata{} config := &mapstructure.DecoderConfig{ DecodeHook: mapstructure.ComposeDecodeHookFunc( createTransformHook(additionalTransformers...), mapstructure.StringToTimeDurationHookFunc()), Result: target, Metadata: &data, } decoder, err := mapstructure.NewDecoder(config) if err != nil { return err } return decoder.Decode(source) } // TransformerFunc defines a function to perform the actual transformation type TransformerFunc func(interface{}) (interface{}, error) // Transformer defines a map to type transformer type Transformer struct { TypeOf reflect.Type Func TransformerFunc } func createTransformHook(additionalTransformers ...Transformer) mapstructure.DecodeHookFuncType { transforms := map[reflect.Type]func(interface{}) (interface{}, error){ reflect.TypeOf(types.External{}): transformExternal, reflect.TypeOf(types.HealthCheckTest{}): transformHealthCheckTest, reflect.TypeOf(types.ShellCommand{}): transformShellCommand, reflect.TypeOf(types.StringList{}): transformStringList, reflect.TypeOf(map[string]string{}): transformMapStringString, reflect.TypeOf(types.UlimitsConfig{}): transformUlimits, reflect.TypeOf(types.UnitBytes(0)): transformSize, reflect.TypeOf([]types.ServicePortConfig{}): transformServicePort, reflect.TypeOf(types.ServiceSecretConfig{}): transformStringSourceMap, reflect.TypeOf(types.ServiceConfigObjConfig{}): transformStringSourceMap, reflect.TypeOf(types.StringOrNumberList{}): transformStringOrNumberList, reflect.TypeOf(map[string]*types.ServiceNetworkConfig{}): transformServiceNetworkMap, reflect.TypeOf(types.Mapping{}): transformMappingOrListFunc("=", false), reflect.TypeOf(types.MappingWithEquals{}): transformMappingOrListFunc("=", true), reflect.TypeOf(types.Labels{}): transformMappingOrListFunc("=", false), reflect.TypeOf(types.MappingWithColon{}): transformMappingOrListFunc(":", false), reflect.TypeOf(types.HostsList{}): transformListOrMappingFunc(":", false), reflect.TypeOf(types.ServiceVolumeConfig{}): transformServiceVolumeConfig, reflect.TypeOf(types.BuildConfig{}): transformBuildConfig, reflect.TypeOf(types.Duration(0)): transformStringToDuration, reflect.TypeOf(types.DependsOnConfig{}): transformDependsOnConfig, reflect.TypeOf(types.ExtendsConfig{}): transformExtendsConfig, reflect.TypeOf(types.DeviceRequest{}): transformServiceDeviceRequest, } for _, transformer := range additionalTransformers { transforms[transformer.TypeOf] = transformer.Func } return func(_ reflect.Type, target reflect.Type, data interface{}) (interface{}, error) { transform, ok := transforms[target] if !ok { return data, nil } return transform(data) } } // keys needs to be converted to strings for jsonschema func convertToStringKeysRecursive(value interface{}, keyPrefix string) (interface{}, error) { if mapping, ok := value.(map[interface{}]interface{}); ok { dict := map[string]interface{}{} for key, entry := range mapping { str, ok := key.(string) if !ok { return nil, formatInvalidKeyError(keyPrefix, key) } var newKeyPrefix string if keyPrefix == "" { newKeyPrefix = str } else { newKeyPrefix = fmt.Sprintf("%s.%s", keyPrefix, str) } convertedEntry, err := convertToStringKeysRecursive(entry, newKeyPrefix) if err != nil { return nil, err } dict[str] = convertedEntry } return dict, nil } if list, ok := value.([]interface{}); ok { convertedList := []interface{}{} for index, entry := range list { newKeyPrefix := fmt.Sprintf("%s[%d]", keyPrefix, index) convertedEntry, err := convertToStringKeysRecursive(entry, newKeyPrefix) if err != nil { return nil, err } convertedList = append(convertedList, convertedEntry) } return convertedList, nil } return value, nil } func formatInvalidKeyError(keyPrefix string, key interface{}) error { var location string if keyPrefix == "" { location = "at top level" } else { location = fmt.Sprintf("in %s", keyPrefix) } return errors.Errorf("Non-string key %s: %#v", location, key) } // LoadServices produces a ServiceConfig map from a compose file Dict // the servicesDict is not validated if directly used. Use Load() to enable validation func LoadServices(filename string, servicesDict map[string]interface{}, workingDir string, lookupEnv template.Mapping, opts *Options) ([]types.ServiceConfig, error) { var services []types.ServiceConfig x, ok := servicesDict["extensions"] if ok { // as a top-level attribute, "services" doesn't support extensions, and a service can be named `x-foo` for k, v := range x.(map[string]interface{}) { servicesDict[k] = v } } for name := range servicesDict { serviceConfig, err := loadServiceWithExtends(filename, name, servicesDict, workingDir, lookupEnv, opts, &cycleTracker{}) if err != nil { return nil, err } services = append(services, *serviceConfig) } return services, nil } func loadServiceWithExtends(filename, name string, servicesDict map[string]interface{}, workingDir string, lookupEnv template.Mapping, opts *Options, ct *cycleTracker) (*types.ServiceConfig, error) { if err := ct.Add(filename, name); err != nil { return nil, err } serviceConfig, err := LoadService(name, servicesDict[name].(map[string]interface{}), workingDir, lookupEnv, opts.ResolvePaths) if err != nil { return nil, err } if serviceConfig.Extends != nil && !opts.SkipExtends { baseServiceName := *serviceConfig.Extends["service"] var baseService *types.ServiceConfig if file := serviceConfig.Extends["file"]; file == nil { baseService, err = loadServiceWithExtends(filename, baseServiceName, servicesDict, workingDir, lookupEnv, opts, ct) if err != nil { return nil, err } } else { // Resolve the path to the imported file, and load it. baseFilePath := absPath(workingDir, *file) bytes, err := ioutil.ReadFile(baseFilePath) if err != nil { return nil, err } baseFile, err := parseConfig(bytes, opts) if err != nil { return nil, err } baseFileServices := getSection(baseFile, "services") baseService, err = loadServiceWithExtends(baseFilePath, baseServiceName, baseFileServices, filepath.Dir(baseFilePath), lookupEnv, opts, ct) if err != nil { return nil, err } // Make paths relative to the importing Compose file. Note that we // make the paths relative to `*file` rather than `baseFilePath` so // that the resulting paths won't be absolute if `*file` isn't an // absolute path. baseFileParent := filepath.Dir(*file) if baseService.Build != nil { // Note that the Dockerfile is always defined relative to the // build context, so there's no need to update the Dockerfile field. baseService.Build.Context = absPath(baseFileParent, baseService.Build.Context) } for i, vol := range baseService.Volumes { if vol.Type != types.VolumeTypeBind { continue } baseService.Volumes[i].Source = absPath(baseFileParent, vol.Source) } } serviceConfig, err = _merge(baseService, serviceConfig) if err != nil { return nil, err } } return serviceConfig, nil } // LoadService produces a single ServiceConfig from a compose file Dict // the serviceDict is not validated if directly used. Use Load() to enable validation func LoadService(name string, serviceDict map[string]interface{}, workingDir string, lookupEnv template.Mapping, resolvePaths bool) (*types.ServiceConfig, error) { serviceConfig := &types.ServiceConfig{ Scale: 1, } if err := Transform(serviceDict, serviceConfig); err != nil { return nil, err } serviceConfig.Name = name if err := resolveEnvironment(serviceConfig, workingDir, lookupEnv); err != nil { return nil, err } for i, volume := range serviceConfig.Volumes { if volume.Type != "bind" { continue } if volume.Source == "" { return nil, errors.New(`invalid mount config for type "bind": field Source must not be empty`) } if resolvePaths { serviceConfig.Volumes[i] = resolveVolumePath(volume, workingDir, lookupEnv) } } return serviceConfig, nil } func resolveEnvironment(serviceConfig *types.ServiceConfig, workingDir string, lookupEnv template.Mapping) error { environment := types.MappingWithEquals{} if len(serviceConfig.EnvFile) > 0 { for _, file := range serviceConfig.EnvFile { filePath := absPath(workingDir, file) file, err := os.Open(filePath) if err != nil { return err } defer file.Close() fileVars, err := godotenv.Parse(file) if err != nil { return err } env := types.MappingWithEquals{} for k, v := range fileVars { v := v env[k] = &v } environment.OverrideBy(env.Resolve(lookupEnv).RemoveEmpty()) } } environment.OverrideBy(serviceConfig.Environment.Resolve(lookupEnv)) serviceConfig.Environment = environment return nil } func resolveVolumePath(volume types.ServiceVolumeConfig, workingDir string, lookupEnv template.Mapping) types.ServiceVolumeConfig { filePath := expandUser(volume.Source, lookupEnv) // Check if source is an absolute path (either Unix or Windows), to // handle a Windows client with a Unix daemon or vice-versa. // // Note that this is not required for Docker for Windows when specifying // a local Windows path, because Docker for Windows translates the Windows // path into a valid path within the VM. if !path.IsAbs(filePath) && !isAbs(filePath) { filePath = absPath(workingDir, filePath) } volume.Source = filePath return volume } // TODO: make this more robust func expandUser(path string, lookupEnv template.Mapping) string { if strings.HasPrefix(path, "~") { home, err := os.UserHomeDir() if err != nil { logrus.Warn("cannot expand '~', because the environment lacks HOME") return path } return filepath.Join(home, path[1:]) } return path } func transformUlimits(data interface{}) (interface{}, error) { switch value := data.(type) { case int: return types.UlimitsConfig{Single: value}, nil case map[string]interface{}: ulimit := types.UlimitsConfig{} if v, ok := value["soft"]; ok { ulimit.Soft = v.(int) } if v, ok := value["hard"]; ok { ulimit.Hard = v.(int) } return ulimit, nil default: return data, errors.Errorf("invalid type %T for ulimits", value) } } // LoadNetworks produces a NetworkConfig map from a compose file Dict // the source Dict is not validated if directly used. Use Load() to enable validation func LoadNetworks(source map[string]interface{}) (map[string]types.NetworkConfig, error) { networks := map[string]types.NetworkConfig{} err := Transform(source, &networks) if err != nil { return networks, err } for name, network := range networks { if !network.External.External { continue } switch { case network.External.Name != "": if network.Name != "" { return nil, errors.Errorf("network %s: network.external.name and network.name conflict; only use network.name", name) } logrus.Warnf("network %s: network.external.name is deprecated in favor of network.name", name) network.Name = network.External.Name network.External.Name = "" case network.Name == "": network.Name = name } networks[name] = network } return networks, nil } func externalVolumeError(volume, key string) error { return errors.Errorf( "conflicting parameters \"external\" and %q specified for volume %q", key, volume) } // LoadVolumes produces a VolumeConfig map from a compose file Dict // the source Dict is not validated if directly used. Use Load() to enable validation func LoadVolumes(source map[string]interface{}) (map[string]types.VolumeConfig, error) { volumes := make(map[string]types.VolumeConfig) if err := Transform(source, &volumes); err != nil { return volumes, err } for name, volume := range volumes { if !volume.External.External { continue } switch { case volume.Driver != "": return nil, externalVolumeError(name, "driver") case len(volume.DriverOpts) > 0: return nil, externalVolumeError(name, "driver_opts") case len(volume.Labels) > 0: return nil, externalVolumeError(name, "labels") case volume.External.Name != "": if volume.Name != "" { return nil, errors.Errorf("volume %s: volume.external.name and volume.name conflict; only use volume.name", name) } logrus.Warnf("volume %s: volume.external.name is deprecated in favor of volume.name", name) volume.Name = volume.External.Name volume.External.Name = "" case volume.Name == "": volume.Name = name } volumes[name] = volume } return volumes, nil } // LoadSecrets produces a SecretConfig map from a compose file Dict // the source Dict is not validated if directly used. Use Load() to enable validation func LoadSecrets(source map[string]interface{}, details types.ConfigDetails, resolvePaths bool) (map[string]types.SecretConfig, error) { secrets := make(map[string]types.SecretConfig) if err := Transform(source, &secrets); err != nil { return secrets, err } for name, secret := range secrets { obj, err := loadFileObjectConfig(name, "secret", types.FileObjectConfig(secret), details, resolvePaths) if err != nil { return nil, err } secretConfig := types.SecretConfig(obj) secrets[name] = secretConfig } return secrets, nil } // LoadConfigObjs produces a ConfigObjConfig map from a compose file Dict // the source Dict is not validated if directly used. Use Load() to enable validation func LoadConfigObjs(source map[string]interface{}, details types.ConfigDetails, resolvePaths bool) (map[string]types.ConfigObjConfig, error) { configs := make(map[string]types.ConfigObjConfig) if err := Transform(source, &configs); err != nil { return configs, err } for name, config := range configs { obj, err := loadFileObjectConfig(name, "config", types.FileObjectConfig(config), details, resolvePaths) if err != nil { return nil, err } configConfig := types.ConfigObjConfig(obj) configs[name] = configConfig } return configs, nil } func loadFileObjectConfig(name string, objType string, obj types.FileObjectConfig, details types.ConfigDetails, resolvePaths bool) (types.FileObjectConfig, error) { // if "external: true" switch { case obj.External.External: // handle deprecated external.name if obj.External.Name != "" { if obj.Name != "" { return obj, errors.Errorf("%[1]s %[2]s: %[1]s.external.name and %[1]s.name conflict; only use %[1]s.name", objType, name) } logrus.Warnf("%[1]s %[2]s: %[1]s.external.name is deprecated in favor of %[1]s.name", objType, name) obj.Name = obj.External.Name obj.External.Name = "" } else { if obj.Name == "" { obj.Name = name } } // if not "external: true" case obj.Driver != "": if obj.File != "" { return obj, errors.Errorf("%[1]s %[2]s: %[1]s.driver and %[1]s.file conflict; only use %[1]s.driver", objType, name) } default: if resolvePaths { obj.File = absPath(details.WorkingDir, obj.File) } } return obj, nil } func absPath(workingDir string, filePath string) string { if strings.HasPrefix(filePath, "~") { home, _ := os.UserHomeDir() return filepath.Join(home, filePath[1:]) } if filepath.IsAbs(filePath) { return filePath } return filepath.Join(workingDir, filePath) } var transformMapStringString TransformerFunc = func(data interface{}) (interface{}, error) { switch value := data.(type) { case map[string]interface{}: return toMapStringString(value, false), nil case map[string]string: return value, nil default: return data, errors.Errorf("invalid type %T for map[string]string", value) } } var transformExternal TransformerFunc = func(data interface{}) (interface{}, error) { switch value := data.(type) { case bool: return map[string]interface{}{"external": value}, nil case map[string]interface{}: return map[string]interface{}{"external": true, "name": value["name"]}, nil default: return data, errors.Errorf("invalid type %T for external", value) } } var transformServicePort TransformerFunc = func(data interface{}) (interface{}, error) { switch entries := data.(type) { case []interface{}: // We process the list instead of individual items here. // The reason is that one entry might be mapped to multiple ServicePortConfig. // Therefore we take an input of a list and return an output of a list. ports := []interface{}{} for _, entry := range entries { switch value := entry.(type) { case int: parsed, err := types.ParsePortConfig(fmt.Sprint(value)) if err != nil { return data, err } for _, v := range parsed { ports = append(ports, v) } case string: parsed, err := types.ParsePortConfig(value) if err != nil { return data, err } for _, v := range parsed { ports = append(ports, v) } case map[string]interface{}: ports = append(ports, groupXFieldsIntoExtensions(value)) default: return data, errors.Errorf("invalid type %T for port", value) } } return ports, nil default: return data, errors.Errorf("invalid type %T for port", entries) } } var transformServiceDeviceRequest TransformerFunc = func(data interface{}) (interface{}, error) { switch value := data.(type) { case map[string]interface{}: count, ok := value["count"] if ok { switch val := count.(type) { case int: return value, nil case string: if strings.ToLower(val) == "all" { value["count"] = -1 return value, nil } return data, errors.Errorf("invalid string value for 'count' (the only value allowed is 'all')") default: return data, errors.Errorf("invalid type %T for device count", val) } } return data, nil default: return data, errors.Errorf("invalid type %T for resource reservation", value) } } var transformStringSourceMap TransformerFunc = func(data interface{}) (interface{}, error) { switch value := data.(type) { case string: return map[string]interface{}{"source": value}, nil case map[string]interface{}: return groupXFieldsIntoExtensions(data.(map[string]interface{})), nil default: return data, errors.Errorf("invalid type %T for secret", value) } } var transformBuildConfig TransformerFunc = func(data interface{}) (interface{}, error) { switch value := data.(type) { case string: return map[string]interface{}{"context": value}, nil case map[string]interface{}: return groupXFieldsIntoExtensions(data.(map[string]interface{})), nil default: return data, errors.Errorf("invalid type %T for service build", value) } } var transformDependsOnConfig TransformerFunc = func(data interface{}) (interface{}, error) { switch value := data.(type) { case []interface{}: transformed := map[string]interface{}{} for _, serviceIntf := range value { service, ok := serviceIntf.(string) if !ok { return data, errors.Errorf("invalid type %T for service depends_on element. Expected string.", value) } transformed[service] = map[string]interface{}{"condition": types.ServiceConditionStarted} } return transformed, nil case map[string]interface{}: return groupXFieldsIntoExtensions(data.(map[string]interface{})), nil default: return data, errors.Errorf("invalid type %T for service depends_on", value) } } var transformExtendsConfig TransformerFunc = func(data interface{}) (interface{}, error) { switch data.(type) { case string: data = map[string]interface{}{ "service": data, } } return transformMappingOrListFunc("=", true)(data) } var transformServiceVolumeConfig TransformerFunc = func(data interface{}) (interface{}, error) { switch value := data.(type) { case string: return ParseVolume(value) case map[string]interface{}: return groupXFieldsIntoExtensions(data.(map[string]interface{})), nil default: return data, errors.Errorf("invalid type %T for service volume", value) } } var transformServiceNetworkMap TransformerFunc = func(value interface{}) (interface{}, error) { if list, ok := value.([]interface{}); ok { mapValue := map[interface{}]interface{}{} for _, name := range list { mapValue[name] = nil } return mapValue, nil } return value, nil } var transformStringOrNumberList TransformerFunc = func(value interface{}) (interface{}, error) { list := value.([]interface{}) result := make([]string, len(list)) for i, item := range list { result[i] = fmt.Sprint(item) } return result, nil } var transformStringList TransformerFunc = func(data interface{}) (interface{}, error) { switch value := data.(type) { case string: return []string{value}, nil case []interface{}: return value, nil default: return data, errors.Errorf("invalid type %T for string list", value) } } func transformMappingOrListFunc(sep string, allowNil bool) TransformerFunc { return func(data interface{}) (interface{}, error) { return transformMappingOrList(data, sep, allowNil), nil } } func transformListOrMappingFunc(sep string, allowNil bool) TransformerFunc { return func(data interface{}) (interface{}, error) { return transformListOrMapping(data, sep, allowNil), nil } } func transformListOrMapping(listOrMapping interface{}, sep string, allowNil bool) interface{} { switch value := listOrMapping.(type) { case map[string]interface{}: return toStringList(value, sep, allowNil) case []interface{}: return listOrMapping } panic(errors.Errorf("expected a map or a list, got %T: %#v", listOrMapping, listOrMapping)) } func transformMappingOrList(mappingOrList interface{}, sep string, allowNil bool) interface{} { switch value := mappingOrList.(type) { case map[string]interface{}: return toMapStringString(value, allowNil) case ([]interface{}): result := make(map[string]interface{}) for _, value := range value { parts := strings.SplitN(value.(string), sep, 2) key := parts[0] switch { case len(parts) == 1 && allowNil: result[key] = nil case len(parts) == 1 && !allowNil: result[key] = "" default: result[key] = parts[1] } } return result } panic(errors.Errorf("expected a map or a list, got %T: %#v", mappingOrList, mappingOrList)) } var transformShellCommand TransformerFunc = func(value interface{}) (interface{}, error) { if str, ok := value.(string); ok { return shellwords.Parse(str) } return value, nil } var transformHealthCheckTest TransformerFunc = func(data interface{}) (interface{}, error) { switch value := data.(type) { case string: return append([]string{"CMD-SHELL"}, value), nil case []interface{}: return value, nil default: return value, errors.Errorf("invalid type %T for healthcheck.test", value) } } var transformSize TransformerFunc = func(value interface{}) (interface{}, error) { switch value := value.(type) { case int: return int64(value), nil case int64, types.UnitBytes: return value, nil case string: return units.RAMInBytes(value) default: return value, errors.Errorf("invalid type for size %T", value) } } var transformStringToDuration TransformerFunc = func(value interface{}) (interface{}, error) { switch value := value.(type) { case string: d, err := time.ParseDuration(value) if err != nil { return value, err } return types.Duration(d), nil default: return value, errors.Errorf("invalid type %T for duration", value) } } func toMapStringString(value map[string]interface{}, allowNil bool) map[string]interface{} { output := make(map[string]interface{}) for key, value := range value { output[key] = toString(value, allowNil) } return output } func toString(value interface{}, allowNil bool) interface{} { switch { case value != nil: return fmt.Sprint(value) case allowNil: return nil default: return "" } } func toStringList(value map[string]interface{}, separator string, allowNil bool) []string { output := []string{} for key, value := range value { if value == nil && !allowNil { continue } output = append(output, fmt.Sprintf("%s%s%s", key, separator, value)) } sort.Strings(output) return output } ================================================ FILE: pkg/third_party/compose-go/loader/loader_test.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package loader import ( "bytes" "io/ioutil" "os" "sort" "strings" "testing" "time" "github.com/compose-spec/compose-go/types" "github.com/google/go-cmp/cmp/cmpopts" "github.com/sirupsen/logrus" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) func buildConfigDetails(yaml string, env map[string]string) types.ConfigDetails { workingDir, err := os.Getwd() if err != nil { panic(err) } return types.ConfigDetails{ WorkingDir: workingDir, ConfigFiles: []types.ConfigFile{ {Filename: "filename.yml", Content: []byte(yaml)}, }, Environment: env, } } func loadYAML(yaml string) (*types.Project, error) { return loadYAMLWithEnv(yaml, nil) } func loadYAMLWithEnv(yaml string, env map[string]string) (*types.Project, error) { return Load(buildConfigDetails(yaml, env), func(options *Options) { options.SkipConsistencyCheck = true options.SkipNormalization = true options.ResolvePaths = true }) } var sampleYAML = ` services: foo: image: busybox networks: with_me: bar: image: busybox environment: - FOO=1 networks: - with_ipam volumes: hello: driver: default driver_opts: beep: boop networks: default: driver: bridge driver_opts: beep: boop with_ipam: ipam: driver: default config: - subnet: 172.28.0.0/16 ` var sampleDict = map[string]interface{}{ "services": map[string]interface{}{ "foo": map[string]interface{}{ "image": "busybox", "networks": map[string]interface{}{"with_me": nil}, }, "bar": map[string]interface{}{ "image": "busybox", "environment": []interface{}{"FOO=1"}, "networks": []interface{}{"with_ipam"}, }, }, "volumes": map[string]interface{}{ "hello": map[string]interface{}{ "driver": "default", "driver_opts": map[string]interface{}{ "beep": "boop", }, }, }, "networks": map[string]interface{}{ "default": map[string]interface{}{ "driver": "bridge", "driver_opts": map[string]interface{}{ "beep": "boop", }, }, "with_ipam": map[string]interface{}{ "ipam": map[string]interface{}{ "driver": "default", "config": []interface{}{ map[string]interface{}{ "subnet": "172.28.0.0/16", }, }, }, }, }, } var samplePortsConfig = []types.ServicePortConfig{ { Mode: "ingress", Target: 8080, Published: 80, Protocol: "tcp", }, { Mode: "ingress", Target: 8081, Published: 81, Protocol: "tcp", }, { Mode: "ingress", Target: 8082, Published: 82, Protocol: "tcp", }, { Mode: "ingress", Target: 8090, Published: 90, Protocol: "udp", }, { Mode: "ingress", Target: 8091, Published: 91, Protocol: "udp", }, { Mode: "ingress", Target: 8092, Published: 92, Protocol: "udp", }, { Mode: "ingress", Target: 8500, Published: 85, Protocol: "tcp", }, { Mode: "ingress", Target: 8600, Published: 0, Protocol: "tcp", }, { Target: 53, Published: 10053, Protocol: "udp", }, { Mode: "host", Target: 22, Published: 10022, }, } func strPtr(val string) *string { return &val } var sampleConfig = types.Config{ Services: []types.ServiceConfig{ { Name: "foo", Image: "busybox", Environment: map[string]*string{}, Networks: map[string]*types.ServiceNetworkConfig{ "with_me": nil, }, Scale: 1, }, { Name: "bar", Image: "busybox", Environment: map[string]*string{"FOO": strPtr("1")}, Networks: map[string]*types.ServiceNetworkConfig{ "with_ipam": nil, }, Scale: 1, }, }, Networks: map[string]types.NetworkConfig{ "default": { Driver: "bridge", DriverOpts: map[string]string{ "beep": "boop", }, }, "with_ipam": { Ipam: types.IPAMConfig{ Driver: "default", Config: []*types.IPAMPool{ { Subnet: "172.28.0.0/16", }, }, }, }, }, Volumes: map[string]types.VolumeConfig{ "hello": { Driver: "default", DriverOpts: map[string]string{ "beep": "boop", }, }, }, } func TestParseYAML(t *testing.T) { dict, err := ParseYAML([]byte(sampleYAML)) assert.NilError(t, err) assert.Check(t, is.DeepEqual(sampleDict, dict)) } func TestLoad(t *testing.T) { actual, err := Load(buildConfigDetails(sampleYAML, nil), func(options *Options) { options.SkipNormalization = true options.SkipConsistencyCheck = true }) assert.NilError(t, err) assert.Check(t, is.DeepEqual(serviceSort(sampleConfig.Services), serviceSort(actual.Services))) assert.Check(t, is.DeepEqual(sampleConfig.Networks, actual.Networks)) assert.Check(t, is.DeepEqual(sampleConfig.Volumes, actual.Volumes)) } func TestLoadExtensions(t *testing.T) { actual, err := loadYAML(` services: foo: image: busybox x-foo: bar`) assert.NilError(t, err) assert.Check(t, is.Len(actual.Services, 1)) service := actual.Services[0] assert.Check(t, is.Equal("busybox", service.Image)) extras := map[string]interface{}{ "x-foo": "bar", } assert.Check(t, is.DeepEqual(extras, service.Extensions)) } func TestLoadExtends(t *testing.T) { actual, err := loadYAML(` services: foo: image: busybox extends: service: bar bar: image: alpine command: echo`) assert.NilError(t, err) assert.Check(t, is.Len(actual.Services, 2)) service, err := actual.GetService("foo") assert.NilError(t, err) assert.Check(t, service.Image == "busybox") assert.Check(t, service.Command[0] == "echo") } func TestLoadExtendsOverrideCommand(t *testing.T) { actual, err := loadYAML(` services: foo: image: busybox extends: service: bar command: "/bin/ash -c \"rm -rf /tmp/might-not-exist\"" bar: image: alpine command: "/bin/ash -c \"echo Oh no...\""`) assert.NilError(t, err) assert.Check(t, is.Len(actual.Services, 2)) service, err := actual.GetService("foo") assert.NilError(t, err) assert.Check(t, service.Image == "busybox") assert.DeepEqual(t, service.Command, types.ShellCommand{"/bin/ash", "-c", "rm -rf /tmp/might-not-exist"}) } func TestLoadCredentialSpec(t *testing.T) { actual, err := loadYAML(` services: foo: image: busybox credential_spec: config: "0bt9dmxjvjiqermk6xrop3ekq" `) assert.NilError(t, err) assert.Assert(t, is.Len(actual.Services, 1)) assert.Check(t, is.Equal(actual.Services[0].CredentialSpec.Config, "0bt9dmxjvjiqermk6xrop3ekq")) } func TestParseAndLoad(t *testing.T) { actual, err := loadYAML(sampleYAML) assert.NilError(t, err) assert.Check(t, is.DeepEqual(serviceSort(sampleConfig.Services), serviceSort(actual.Services))) assert.Check(t, is.DeepEqual(sampleConfig.Networks, actual.Networks)) assert.Check(t, is.DeepEqual(sampleConfig.Volumes, actual.Volumes)) } func TestInvalidTopLevelObjectType(t *testing.T) { _, err := loadYAML("1") assert.ErrorContains(t, err, "Top-level object must be a mapping") _, err = loadYAML("\"hello\"") assert.ErrorContains(t, err, "Top-level object must be a mapping") _, err = loadYAML("[\"hello\"]") assert.ErrorContains(t, err, "Top-level object must be a mapping") } func TestNonStringKeys(t *testing.T) { _, err := loadYAML(` 123: foo: image: busybox `) assert.ErrorContains(t, err, "Non-string key at top level: 123") _, err = loadYAML(` services: foo: image: busybox 123: image: busybox `) assert.ErrorContains(t, err, "Non-string key in services: 123") _, err = loadYAML(` services: foo: image: busybox networks: default: ipam: config: - 123: oh dear `) assert.ErrorContains(t, err, "Non-string key in networks.default.ipam.config[0]: 123") _, err = loadYAML(` services: dict-env: image: busybox environment: 1: FOO `) assert.ErrorContains(t, err, "Non-string key in services.dict-env.environment: 1") } func TestV1Unsupported(t *testing.T) { _, err := loadYAML(` foo: image: busybox `) assert.Check(t, err != nil) } func TestNonMappingObject(t *testing.T) { _, err := loadYAML(` services: - foo: image: busybox `) assert.ErrorContains(t, err, "services must be a mapping") _, err = loadYAML(` services: foo: busybox `) assert.ErrorContains(t, err, "services.foo must be a mapping") _, err = loadYAML(` networks: - default: driver: bridge `) assert.ErrorContains(t, err, "networks must be a mapping") _, err = loadYAML(` networks: default: bridge `) assert.ErrorContains(t, err, "networks.default must be a mapping") _, err = loadYAML(` volumes: - data: driver: local `) assert.ErrorContains(t, err, "volumes must be a mapping") _, err = loadYAML(` volumes: data: local `) assert.ErrorContains(t, err, "volumes.data must be a mapping") } func TestNonStringImage(t *testing.T) { _, err := loadYAML(` services: foo: image: ["busybox", "latest"] `) assert.ErrorContains(t, err, "services.foo.image must be a string") } func TestLoadWithEnvironment(t *testing.T) { config, err := loadYAMLWithEnv(` services: dict-env: image: busybox environment: FOO: "1" BAR: 2 GA: 2.5 BU: "" ZO: MEU: list-env: image: busybox environment: - FOO=1 - BAR=2 - GA=2.5 - BU= - ZO - MEU `, map[string]string{"MEU": "Shadoks"}) assert.NilError(t, err) expected := types.MappingWithEquals{ "FOO": strPtr("1"), "BAR": strPtr("2"), "GA": strPtr("2.5"), "BU": strPtr(""), "ZO": nil, "MEU": strPtr("Shadoks"), } assert.Check(t, is.Equal(2, len(config.Services))) for _, service := range config.Services { assert.Check(t, is.DeepEqual(expected, service.Environment)) } } func TestLoadEnvironmentWithBoolean(t *testing.T) { config, err := loadYAML(` services: dict-env: image: busybox environment: FOO: true BAR: false `) assert.NilError(t, err) expected := types.MappingWithEquals{ "FOO": strPtr("true"), "BAR": strPtr("false"), } assert.Check(t, is.Equal(1, len(config.Services))) for _, service := range config.Services { assert.Check(t, is.DeepEqual(expected, service.Environment)) } } func TestInvalidEnvironmentValue(t *testing.T) { _, err := loadYAML(` services: dict-env: image: busybox environment: FOO: ["1"] `) assert.ErrorContains(t, err, "services.dict-env.environment.FOO must be a string, number, boolean or null") } func TestInvalidEnvironmentObject(t *testing.T) { _, err := loadYAML(` services: dict-env: image: busybox environment: "FOO=1" `) assert.ErrorContains(t, err, "services.dict-env.environment must be a mapping") } func TestLoadWithEnvironmentInterpolation(t *testing.T) { home := "/home/foo" config, err := loadYAMLWithEnv(` # This is a comment, so using variable syntax here ${SHOULD_NOT_BREAK} parsing services: test: image: busybox labels: - home1=$HOME - home2=${HOME} - nonexistent=$NONEXISTENT - default=${NONEXISTENT-default} networks: test: driver: $HOME volumes: test: driver: $HOME `, map[string]string{ "HOME": home, "FOO": "foo", }) assert.NilError(t, err) expectedLabels := types.Labels{ "home1": home, "home2": home, "nonexistent": "", "default": "default", } assert.Check(t, is.DeepEqual(expectedLabels, config.Services[0].Labels)) assert.Check(t, is.Equal(home, config.Networks["test"].Driver)) assert.Check(t, is.Equal(home, config.Volumes["test"].Driver)) } func TestLoadWithInterpolationCastFull(t *testing.T) { dict := ` services: web: configs: - source: appconfig mode: $theint secrets: - source: super mode: $theint healthcheck: retries: ${theint} disable: $thebool deploy: replicas: $theint update_config: parallelism: $theint max_failure_ratio: $thefloat rollback_config: parallelism: $theint max_failure_ratio: $thefloat restart_policy: max_attempts: $theint placement: max_replicas_per_node: $theint ports: - $theint - "34567" - target: $theint published: $theint x-foo-bar: true ulimits: nproc: $theint nofile: hard: $theint soft: $theint privileged: $thebool read_only: $thebool shm_size: 2gb stdin_open: ${thebool} tty: $thebool volumes: - source: data type: volume read_only: $thebool volume: nocopy: $thebool configs: appconfig: external: $thebool secrets: super: external: $thebool volumes: data: external: $thebool networks: front: external: $thebool internal: $thebool attachable: $thebool back: ` env := map[string]string{ "theint": "555", "thefloat": "3.14", "thebool": "true", } config, err := Load(buildConfigDetails(dict, env), func(options *Options) { options.SkipNormalization = true options.SkipConsistencyCheck = true }) assert.NilError(t, err) workingDir, err := os.Getwd() assert.NilError(t, err) expected := &types.Project{ Name: "", Environment: map[string]string{"thebool": "true", "thefloat": "3.14", "theint": "555"}, WorkingDir: workingDir, Services: []types.ServiceConfig{ { Name: "web", Configs: []types.ServiceConfigObjConfig{ { Source: "appconfig", Mode: uint32Ptr(555), }, }, Secrets: []types.ServiceSecretConfig{ { Source: "super", Mode: uint32Ptr(555), }, }, HealthCheck: &types.HealthCheckConfig{ Retries: uint64Ptr(555), Disable: true, }, Deploy: &types.DeployConfig{ Replicas: uint64Ptr(555), UpdateConfig: &types.UpdateConfig{ Parallelism: uint64Ptr(555), MaxFailureRatio: 3.14, }, RollbackConfig: &types.UpdateConfig{ Parallelism: uint64Ptr(555), MaxFailureRatio: 3.14, }, RestartPolicy: &types.RestartPolicy{ MaxAttempts: uint64Ptr(555), }, Placement: types.Placement{ MaxReplicas: 555, }, }, Ports: []types.ServicePortConfig{ {Target: 555, Mode: "ingress", Protocol: "tcp"}, {Target: 34567, Mode: "ingress", Protocol: "tcp"}, {Target: 555, Published: 555, Extensions: map[string]interface{}{"x-foo-bar": true}}, }, Ulimits: map[string]*types.UlimitsConfig{ "nproc": {Single: 555}, "nofile": {Hard: 555, Soft: 555}, }, Privileged: true, ReadOnly: true, Scale: 1, ShmSize: types.UnitBytes(2 * 1024 * 1024 * 1024), StdinOpen: true, Tty: true, Volumes: []types.ServiceVolumeConfig{ { Source: "data", Type: "volume", ReadOnly: true, Volume: &types.ServiceVolumeVolume{NoCopy: true}, }, }, Environment: types.MappingWithEquals{}, }, }, Configs: map[string]types.ConfigObjConfig{ "appconfig": {External: types.External{External: true}, Name: "appconfig"}, }, Secrets: map[string]types.SecretConfig{ "super": {External: types.External{External: true}, Name: "super"}, }, Volumes: map[string]types.VolumeConfig{ "data": {External: types.External{External: true}, Name: "data"}, }, Networks: map[string]types.NetworkConfig{ "back": {}, "front": { External: types.External{External: true}, Name: "front", Internal: true, Attachable: true, }, }, } assert.Check(t, is.DeepEqual(expected, config)) } func TestUnsupportedProperties(t *testing.T) { dict := ` services: web: image: web build: context: ./web links: - bar pid: host db: image: db build: context: ./db ` configDetails := buildConfigDetails(dict, nil) _, err := Load(configDetails) assert.NilError(t, err) } func TestDiscardEnvFileOption(t *testing.T) { dict := `services: web: image: nginx env_file: - example1.env - example2.env ` expectedEnvironmentMap := types.MappingWithEquals{ "FOO": strPtr("foo_from_env_file"), "BAZ": strPtr("baz_from_env_file"), "BAR": strPtr("bar_from_env_file_2"), // Original value is overwritten by example2.env "QUX": strPtr("quz_from_env_file_2"), } configDetails := buildConfigDetails(dict, nil) // Default behavior keeps the `env_file` entries configWithEnvFiles, err := Load(configDetails) assert.NilError(t, err) assert.DeepEqual(t, configWithEnvFiles.Services[0].EnvFile, types.StringList{"example1.env", "example2.env"}) assert.DeepEqual(t, configWithEnvFiles.Services[0].Environment, expectedEnvironmentMap) // Custom behavior removes the `env_file` entries configWithoutEnvFiles, err := Load(configDetails, WithDiscardEnvFiles) assert.NilError(t, err) assert.DeepEqual(t, configWithoutEnvFiles.Services[0].EnvFile, types.StringList(nil)) assert.DeepEqual(t, configWithoutEnvFiles.Services[0].Environment, expectedEnvironmentMap) } func TestBuildProperties(t *testing.T) { dict := ` services: web: image: web build: . links: - bar db: image: db build: context: ./db ` configDetails := buildConfigDetails(dict, nil) _, err := Load(configDetails) assert.NilError(t, err) } func TestDeprecatedProperties(t *testing.T) { dict := ` services: web: image: web container_name: web db: image: db container_name: db expose: ["5434"] ` configDetails := buildConfigDetails(dict, nil) _, err := Load(configDetails) assert.NilError(t, err) } func TestInvalidResource(t *testing.T) { _, err := loadYAML(` services: foo: image: busybox deploy: resources: impossible: x: 1 `) assert.ErrorContains(t, err, "Additional property impossible is not allowed") } func TestInvalidExternalAndDriverCombination(t *testing.T) { _, err := loadYAML(` volumes: external_volume: external: true driver: foobar `) assert.ErrorContains(t, err, "conflicting parameters \"external\" and \"driver\" specified for volume") assert.ErrorContains(t, err, "external_volume") } func TestInvalidExternalAndDirverOptsCombination(t *testing.T) { _, err := loadYAML(` volumes: external_volume: external: true driver_opts: beep: boop `) assert.ErrorContains(t, err, "conflicting parameters \"external\" and \"driver_opts\" specified for volume") assert.ErrorContains(t, err, "external_volume") } func TestInvalidExternalAndLabelsCombination(t *testing.T) { _, err := loadYAML(` volumes: external_volume: external: true labels: - beep=boop `) assert.ErrorContains(t, err, "conflicting parameters \"external\" and \"labels\" specified for volume") assert.ErrorContains(t, err, "external_volume") } func TestLoadVolumeInvalidExternalNameAndNameCombination(t *testing.T) { _, err := loadYAML(` volumes: external_volume: name: user_specified_name external: name: external_name `) assert.ErrorContains(t, err, "volume.external.name and volume.name conflict; only use volume.name") assert.ErrorContains(t, err, "external_volume") } func TestInterpolateInt(t *testing.T) { project, err := loadYAMLWithEnv(` services: foo: image: foo scale: ${FOO_SCALE} `, map[string]string{"FOO_SCALE": "2"}) assert.NilError(t, err) assert.Equal(t, project.Services[0].Scale, 2) } func durationPtr(value time.Duration) *types.Duration { result := types.Duration(value) return &result } func uint64Ptr(value uint64) *uint64 { return &value } func uint32Ptr(value uint32) *uint32 { return &value } func TestFullExample(t *testing.T) { bytes, err := ioutil.ReadFile("full-example.yml") assert.NilError(t, err) homeDir, err := os.UserHomeDir() assert.NilError(t, err) env := map[string]string{"HOME": homeDir, "QUX": "qux_from_environment"} config, err := loadYAMLWithEnv(string(bytes), env) assert.NilError(t, err) workingDir, err := os.Getwd() assert.NilError(t, err) expectedConfig := fullExampleConfig(workingDir, homeDir) assert.Check(t, is.DeepEqual(expectedConfig.Services, config.Services)) assert.Check(t, is.DeepEqual(expectedConfig.Networks, config.Networks)) assert.Check(t, is.DeepEqual(expectedConfig.Volumes, config.Volumes)) assert.Check(t, is.DeepEqual(expectedConfig.Secrets, config.Secrets)) assert.Check(t, is.DeepEqual(expectedConfig.Configs, config.Configs)) assert.Check(t, is.DeepEqual(expectedConfig.Extensions, config.Extensions)) } func TestLoadTmpfsVolume(t *testing.T) { config, err := loadYAML(` services: tmpfs: image: nginx:latest volumes: - type: tmpfs target: /app tmpfs: size: 10000 `) assert.NilError(t, err) expected := types.ServiceVolumeConfig{ Target: "/app", Type: "tmpfs", Tmpfs: &types.ServiceVolumeTmpfs{ Size: int64(10000), }, } assert.Assert(t, is.Len(config.Services, 1)) assert.Check(t, is.Len(config.Services[0].Volumes, 1)) assert.Check(t, is.DeepEqual(expected, config.Services[0].Volumes[0])) } func TestLoadTmpfsVolumeAdditionalPropertyNotAllowed(t *testing.T) { _, err := loadYAML(` services: tmpfs: image: nginx:latest volumes: - type: tmpfs target: /app foo: bar: zot `) assert.ErrorContains(t, err, "services.tmpfs.volumes.0 Additional property foo is not allowed") } func TestLoadBindMountSourceMustNotBeEmpty(t *testing.T) { _, err := loadYAML(` services: tmpfs: image: nginx:latest volumes: - type: bind target: /app `) assert.Error(t, err, `invalid mount config for type "bind": field Source must not be empty`) } func TestLoadBindMountSourceIsWindowsAbsolute(t *testing.T) { tests := []struct { doc string yaml string expected types.ServiceVolumeConfig }{ { doc: "Z-drive lowercase", yaml: ` services: windows: image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019 volumes: - type: bind source: z:\ target: c:\data `, expected: types.ServiceVolumeConfig{Type: "bind", Source: `z:\`, Target: `c:\data`}, }, { doc: "Z-drive uppercase", yaml: ` services: windows: image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019 volumes: - type: bind source: Z:\ target: C:\data `, expected: types.ServiceVolumeConfig{Type: "bind", Source: `Z:\`, Target: `C:\data`}, }, { doc: "Z-drive subdirectory", yaml: ` services: windows: image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019 volumes: - type: bind source: Z:\some-dir target: C:\data `, expected: types.ServiceVolumeConfig{Type: "bind", Source: `Z:\some-dir`, Target: `C:\data`}, }, { doc: "forward-slashes", yaml: ` services: app: image: app:latest volumes: - type: bind source: /z/some-dir target: /c/data `, expected: types.ServiceVolumeConfig{Type: "bind", Source: `/z/some-dir`, Target: `/c/data`}, }, } for _, tc := range tests { t.Run(tc.doc, func(t *testing.T) { config, err := loadYAML(tc.yaml) assert.NilError(t, err) assert.Check(t, is.Len(config.Services[0].Volumes, 1)) assert.Check(t, is.DeepEqual(tc.expected, config.Services[0].Volumes[0])) }) } } func TestLoadBindMountWithSource(t *testing.T) { config, err := loadYAML(` services: bind: image: nginx:latest volumes: - type: bind target: /app source: "." `) assert.NilError(t, err) workingDir, err := os.Getwd() assert.NilError(t, err) expected := types.ServiceVolumeConfig{ Type: "bind", Source: workingDir, Target: "/app", } assert.Assert(t, is.Len(config.Services, 1)) assert.Check(t, is.Len(config.Services[0].Volumes, 1)) assert.Check(t, is.DeepEqual(expected, config.Services[0].Volumes[0])) } func TestLoadTmpfsVolumeSizeCanBeZero(t *testing.T) { config, err := loadYAML(` services: tmpfs: image: nginx:latest volumes: - type: tmpfs target: /app tmpfs: size: 0 `) assert.NilError(t, err) expected := types.ServiceVolumeConfig{ Target: "/app", Type: "tmpfs", Tmpfs: &types.ServiceVolumeTmpfs{}, } assert.Assert(t, is.Len(config.Services, 1)) assert.Check(t, is.Len(config.Services[0].Volumes, 1)) assert.Check(t, is.DeepEqual(expected, config.Services[0].Volumes[0])) } func TestLoadTmpfsVolumeSizeMustBeGTEQZero(t *testing.T) { _, err := loadYAML(` services: tmpfs: image: nginx:latest volumes: - type: tmpfs target: /app tmpfs: size: -1 `) assert.ErrorContains(t, err, "services.tmpfs.volumes.0.tmpfs.size Must be greater than or equal to 0") } func TestLoadTmpfsVolumeSizeMustBeInteger(t *testing.T) { _, err := loadYAML(` services: tmpfs: image: nginx:latest volumes: - type: tmpfs target: /app tmpfs: size: 0.0001 `) assert.ErrorContains(t, err, "services.tmpfs.volumes.0.tmpfs.size must be a integer") } func serviceSort(services []types.ServiceConfig) []types.ServiceConfig { sort.Slice(services, func(i, j int) bool { return services[i].Name < services[j].Name }) return services } func TestLoadAttachableNetwork(t *testing.T) { config, err := loadYAML(` networks: mynet1: driver: overlay attachable: true mynet2: driver: bridge `) assert.NilError(t, err) expected := types.Networks{ "mynet1": { Driver: "overlay", Attachable: true, }, "mynet2": { Driver: "bridge", Attachable: false, }, } assert.Check(t, is.DeepEqual(expected, config.Networks)) } func TestLoadExpandedPortFormat(t *testing.T) { config, err := loadYAML(` services: web: image: busybox ports: - "80-82:8080-8082" - "90-92:8090-8092/udp" - "85:8500" - 8600 - protocol: udp target: 53 published: 10053 - mode: host target: 22 published: 10022 `) assert.NilError(t, err) assert.Check(t, is.Len(config.Services, 1)) assert.Check(t, is.DeepEqual(samplePortsConfig, config.Services[0].Ports)) } func TestLoadExpandedMountFormat(t *testing.T) { config, err := loadYAML(` services: web: image: busybox volumes: - type: volume source: foo target: /target read_only: true volumes: foo: {} `) assert.NilError(t, err) expected := types.ServiceVolumeConfig{ Type: "volume", Source: "foo", Target: "/target", ReadOnly: true, } assert.Assert(t, is.Len(config.Services, 1)) assert.Check(t, is.Len(config.Services[0].Volumes, 1)) assert.Check(t, is.DeepEqual(expected, config.Services[0].Volumes[0])) } func TestLoadExtraHostsMap(t *testing.T) { config, err := loadYAML(` services: web: image: busybox extra_hosts: "zulu": "162.242.195.82" "alpha": "50.31.209.229" `) assert.NilError(t, err) expected := types.HostsList{ "alpha:50.31.209.229", "zulu:162.242.195.82", } assert.Assert(t, is.Len(config.Services, 1)) assert.Check(t, is.DeepEqual(expected, config.Services[0].ExtraHosts)) } func TestLoadExtraHostsList(t *testing.T) { config, err := loadYAML(` services: web: image: busybox extra_hosts: - "zulu:162.242.195.82" - "alpha:50.31.209.229" - "zulu:ff02::1" `) assert.NilError(t, err) expected := types.HostsList{ "zulu:162.242.195.82", "alpha:50.31.209.229", "zulu:ff02::1", } assert.Assert(t, is.Len(config.Services, 1)) assert.Check(t, is.DeepEqual(expected, config.Services[0].ExtraHosts)) } func TestLoadVolumesWarnOnDeprecatedExternalNameVersion34(t *testing.T) { buf, cleanup := patchLogrus() defer cleanup() source := map[string]interface{}{ "foo": map[string]interface{}{ "external": map[string]interface{}{ "name": "oops", }, }, } volumes, err := LoadVolumes(source) assert.NilError(t, err) expected := map[string]types.VolumeConfig{ "foo": { Name: "oops", External: types.External{External: true}, }, } assert.Check(t, is.DeepEqual(expected, volumes)) assert.Check(t, is.Contains(buf.String(), "volume.external.name is deprecated")) } func patchLogrus() (*bytes.Buffer, func()) { buf := new(bytes.Buffer) out := logrus.StandardLogger().Out logrus.SetOutput(buf) return buf, func() { logrus.SetOutput(out) } } func TestLoadVolumesWarnOnDeprecatedExternalName(t *testing.T) { buf, cleanup := patchLogrus() defer cleanup() source := map[string]interface{}{ "foo": map[string]interface{}{ "external": map[string]interface{}{ "name": "oops", }, }, } volumes, err := LoadVolumes(source) assert.NilError(t, err) expected := map[string]types.VolumeConfig{ "foo": { Name: "oops", External: types.External{External: true}, }, } assert.Check(t, is.DeepEqual(expected, volumes)) assert.Check(t, strings.Contains(buf.String(), "volume foo: volume.external.name is deprecated in favor of volume.name")) } func TestLoadInvalidIsolation(t *testing.T) { // validation should be done only on the daemon side actual, err := loadYAML(` services: foo: image: busybox isolation: invalid configs: super: external: true `) assert.NilError(t, err) assert.Assert(t, is.Len(actual.Services, 1)) assert.Check(t, is.Equal("invalid", actual.Services[0].Isolation)) } func TestLoadSecretInvalidExternalNameAndNameCombination(t *testing.T) { _, err := loadYAML(` secrets: external_secret: name: user_specified_name external: name: external_name `) assert.ErrorContains(t, err, "secret.external.name and secret.name conflict; only use secret.name") assert.ErrorContains(t, err, "external_secret") } func TestLoadSecretsWarnOnDeprecatedExternalNameVersion35(t *testing.T) { buf, cleanup := patchLogrus() defer cleanup() source := map[string]interface{}{ "foo": map[string]interface{}{ "external": map[string]interface{}{ "name": "oops", }, }, } details := types.ConfigDetails{} secrets, err := LoadSecrets(source, details, true) assert.NilError(t, err) expected := map[string]types.SecretConfig{ "foo": { Name: "oops", External: types.External{External: true}, }, } assert.Check(t, is.DeepEqual(expected, secrets)) assert.Check(t, is.Contains(buf.String(), "secret.external.name is deprecated")) } func TestLoadNetworksWarnOnDeprecatedExternalNameVersion35(t *testing.T) { buf, cleanup := patchLogrus() defer cleanup() source := map[string]interface{}{ "foo": map[string]interface{}{ "external": map[string]interface{}{ "name": "oops", }, }, } networks, err := LoadNetworks(source) assert.NilError(t, err) expected := map[string]types.NetworkConfig{ "foo": { Name: "oops", External: types.External{External: true}, }, } assert.Check(t, is.DeepEqual(expected, networks)) assert.Check(t, is.Contains(buf.String(), "network.external.name is deprecated")) } func TestLoadNetworksWarnOnDeprecatedExternalName(t *testing.T) { buf, cleanup := patchLogrus() defer cleanup() source := map[string]interface{}{ "foo": map[string]interface{}{ "external": map[string]interface{}{ "name": "oops", }, }, } networks, err := LoadNetworks(source) assert.NilError(t, err) expected := map[string]types.NetworkConfig{ "foo": { Name: "oops", External: types.External{External: true}, }, } assert.Check(t, is.DeepEqual(expected, networks)) assert.Check(t, strings.Contains(buf.String(), "network foo: network.external.name is deprecated in favor of network.name")) } func TestLoadNetworkInvalidExternalNameAndNameCombination(t *testing.T) { _, err := loadYAML(` networks: foo: name: user_specified_name external: name: external_name `) assert.ErrorContains(t, err, "network.external.name and network.name conflict; only use network.name") assert.ErrorContains(t, err, "foo") } func TestLoadNetworkWithName(t *testing.T) { config, err := loadYAML(` services: hello-world: image: redis:alpine networks: - network1 - network3 networks: network1: name: network2 network3: `) assert.NilError(t, err) workingDir, err := os.Getwd() assert.NilError(t, err) expected := &types.Project{ Name: "", WorkingDir: workingDir, Services: types.Services{ { Name: "hello-world", Image: "redis:alpine", Scale: 1, Networks: map[string]*types.ServiceNetworkConfig{ "network1": nil, "network3": nil, }, }, }, Networks: map[string]types.NetworkConfig{ "network1": {Name: "network2"}, "network3": {}, }, } assert.DeepEqual(t, config, expected, cmpopts.EquateEmpty()) } func TestLoadInit(t *testing.T) { booleanTrue := true booleanFalse := false var testcases = []struct { doc string yaml string init *bool }{ { doc: "no init defined", yaml: ` services: foo: image: alpine`, }, { doc: "has true init", yaml: ` services: foo: image: alpine init: true`, init: &booleanTrue, }, { doc: "has false init", yaml: ` services: foo: image: alpine init: false`, init: &booleanFalse, }, } for _, testcase := range testcases { testcase := testcase t.Run(testcase.doc, func(t *testing.T) { config, err := loadYAML(testcase.yaml) assert.NilError(t, err) assert.Check(t, is.Len(config.Services, 1)) assert.Check(t, is.DeepEqual(config.Services[0].Init, testcase.init)) }) } } func TestLoadSysctls(t *testing.T) { config, err := loadYAML(` services: web: image: busybox sysctls: - net.core.somaxconn=1024 - net.ipv4.tcp_syncookies=0 - testing.one.one= - testing.one.two `) assert.NilError(t, err) expected := types.Mapping{ "net.core.somaxconn": "1024", "net.ipv4.tcp_syncookies": "0", "testing.one.one": "", "testing.one.two": "", } assert.Assert(t, is.Len(config.Services, 1)) assert.Check(t, is.DeepEqual(expected, config.Services[0].Sysctls)) config, err = loadYAML(` services: web: image: busybox sysctls: net.core.somaxconn: 1024 net.ipv4.tcp_syncookies: 0 testing.one.one: "" testing.one.two: `) assert.NilError(t, err) assert.Assert(t, is.Len(config.Services, 1)) assert.Check(t, is.DeepEqual(expected, config.Services[0].Sysctls)) } func TestTransform(t *testing.T) { var source = []interface{}{ "80-82:8080-8082", "90-92:8090-8092/udp", "85:8500", 8600, map[string]interface{}{ "protocol": "udp", "target": 53, "published": 10053, }, map[string]interface{}{ "mode": "host", "target": 22, "published": 10022, }, } var ports []types.ServicePortConfig err := Transform(source, &ports) assert.NilError(t, err) assert.Check(t, is.DeepEqual(samplePortsConfig, ports)) } func TestLoadTemplateDriver(t *testing.T) { config, err := loadYAML(` services: hello-world: image: redis:alpine secrets: - secret configs: - config configs: config: name: config external: true template_driver: config-driver secrets: secret: name: secret external: true template_driver: secret-driver `) assert.NilError(t, err) workingDir, err := os.Getwd() assert.NilError(t, err) expected := &types.Project{ Name: "", WorkingDir: workingDir, Services: types.Services{ { Name: "hello-world", Image: "redis:alpine", Configs: []types.ServiceConfigObjConfig{ { Source: "config", }, }, Scale: 1, Secrets: []types.ServiceSecretConfig{ { Source: "secret", }, }, }, }, Configs: map[string]types.ConfigObjConfig{ "config": { Name: "config", External: types.External{External: true}, TemplateDriver: "config-driver", }, }, Secrets: map[string]types.SecretConfig{ "secret": { Name: "secret", External: types.External{External: true}, TemplateDriver: "secret-driver", }, }, } assert.DeepEqual(t, config, expected, cmpopts.EquateEmpty()) } func TestLoadSecretDriver(t *testing.T) { config, err := loadYAML(` services: hello-world: image: redis:alpine secrets: - secret configs: - config configs: config: name: config external: true secrets: secret: name: secret driver: secret-bucket driver_opts: OptionA: value for driver option A OptionB: value for driver option B `) assert.NilError(t, err) workingDir, err := os.Getwd() assert.NilError(t, err) expected := &types.Project{ Name: "", WorkingDir: workingDir, Services: types.Services{ { Name: "hello-world", Image: "redis:alpine", Configs: []types.ServiceConfigObjConfig{ { Source: "config", }, }, Scale: 1, Secrets: []types.ServiceSecretConfig{ { Source: "secret", }, }, }, }, Configs: map[string]types.ConfigObjConfig{ "config": { Name: "config", External: types.External{External: true}, }, }, Secrets: map[string]types.SecretConfig{ "secret": { Name: "secret", Driver: "secret-bucket", DriverOpts: map[string]string{ "OptionA": "value for driver option A", "OptionB": "value for driver option B", }, }, }, } assert.DeepEqual(t, config, expected, cmpopts.EquateEmpty()) } func TestComposeFileWithVersion(t *testing.T) { bytes, err := ioutil.ReadFile("testdata/compose-test-with-version.yaml") assert.NilError(t, err) homeDir, err := os.UserHomeDir() assert.NilError(t, err) env := map[string]string{"HOME": homeDir, "QUX": "qux_from_environment"} config, err := loadYAMLWithEnv(string(bytes), env) assert.NilError(t, err) workingDir, err := os.Getwd() assert.NilError(t, err) expectedConfig := withVersionExampleConfig(workingDir, homeDir) sort.Slice(config.Services, func(i, j int) bool { return config.Services[i].Name > config.Services[j].Name }) assert.Check(t, is.DeepEqual(expectedConfig.Services, config.Services)) assert.Check(t, is.DeepEqual(expectedConfig.Networks, config.Networks)) assert.Check(t, is.DeepEqual(expectedConfig.Volumes, config.Volumes)) } func TestLoadWithExtends(t *testing.T) { bytes, err := ioutil.ReadFile("testdata/compose-test-extends.yaml") assert.NilError(t, err) configDetails := types.ConfigDetails{ WorkingDir: "testdata", ConfigFiles: []types.ConfigFile{ {Filename: "testdata/compose-test-extends.yaml", Content: bytes}, }, } actual, err := Load(configDetails) assert.NilError(t, err) expServices := types.Services{ { Name: "importer", Image: "nginx", Extends: types.ExtendsConfig{ "file": strPtr("compose-test-extends-imported.yaml"), "service": strPtr("imported"), }, Environment: types.MappingWithEquals{}, Networks: map[string]*types.ServiceNetworkConfig{"default": nil}, Scale: 1, }, } assert.Check(t, is.DeepEqual(expServices, actual.Services)) } func TestServiceDeviceRequestCount(t *testing.T) { _, err := loadYAML(` services: hello-world: image: redis:alpine deploy: resources: reservations: devices: - driver: nvidia capabilities: [gpu] count: all `) assert.NilError(t, err) } func TestServiceDeviceRequestCountType(t *testing.T) { _, err := loadYAML(` services: hello-world: image: redis:alpine deploy: resources: reservations: devices: - driver: nvidia capabilities: [gpu] count: somestring `) assert.ErrorContains(t, err, "invalid string value for 'count' (the only value allowed is 'all')") } func TestServicePullPolicy(t *testing.T) { actual, err := loadYAML(` services: hello-world: image: redis:alpine pull_policy: always `) assert.NilError(t, err) svc, err := actual.GetService("hello-world") assert.NilError(t, err) assert.Equal(t, "always", svc.PullPolicy) } func TestEmptyList(t *testing.T) { _, err := loadYAML(` services: test: image: nginx:latest ports: [] `) assert.NilError(t, err) } ================================================ FILE: pkg/third_party/compose-go/loader/merge.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package loader import ( "reflect" "sort" "github.com/compose-spec/compose-go/types" "github.com/imdario/mergo" "github.com/pkg/errors" ) type specials struct { m map[reflect.Type]func(dst, src reflect.Value) error } var serviceSpecials = &specials{ m: map[reflect.Type]func(dst, src reflect.Value) error{ reflect.TypeOf(&types.LoggingConfig{}): safelyMerge(mergeLoggingConfig), reflect.TypeOf(&types.UlimitsConfig{}): safelyMerge(mergeUlimitsConfig), reflect.TypeOf([]types.ServiceVolumeConfig{}): mergeSlice(toServiceVolumeConfigsMap, toServiceVolumeConfigsSlice), reflect.TypeOf([]types.ServicePortConfig{}): mergeSlice(toServicePortConfigsMap, toServicePortConfigsSlice), reflect.TypeOf([]types.ServiceSecretConfig{}): mergeSlice(toServiceSecretConfigsMap, toServiceSecretConfigsSlice), reflect.TypeOf([]types.ServiceConfigObjConfig{}): mergeSlice(toServiceConfigObjConfigsMap, toSServiceConfigObjConfigsSlice), reflect.TypeOf(&types.UlimitsConfig{}): mergeUlimitsConfig, reflect.TypeOf(&types.ServiceNetworkConfig{}): mergeServiceNetworkConfig, }, } func (s *specials) Transformer(t reflect.Type) func(dst, src reflect.Value) error { if fn, ok := s.m[t]; ok { return fn } return nil } func merge(configs []*types.Config) (*types.Config, error) { base := configs[0] for _, override := range configs[1:] { var err error base.Services, err = mergeServices(base.Services, override.Services) if err != nil { return base, errors.Wrapf(err, "cannot merge services from %s", override.Filename) } base.Volumes, err = mergeVolumes(base.Volumes, override.Volumes) if err != nil { return base, errors.Wrapf(err, "cannot merge volumes from %s", override.Filename) } base.Networks, err = mergeNetworks(base.Networks, override.Networks) if err != nil { return base, errors.Wrapf(err, "cannot merge networks from %s", override.Filename) } base.Secrets, err = mergeSecrets(base.Secrets, override.Secrets) if err != nil { return base, errors.Wrapf(err, "cannot merge secrets from %s", override.Filename) } base.Configs, err = mergeConfigs(base.Configs, override.Configs) if err != nil { return base, errors.Wrapf(err, "cannot merge configs from %s", override.Filename) } base.Extensions, err = mergeExtensions(base.Extensions, override.Extensions) if err != nil { return base, errors.Wrapf(err, "cannot merge extensions from %s", override.Filename) } } return base, nil } func mergeServices(base, override []types.ServiceConfig) ([]types.ServiceConfig, error) { baseServices := mapByName(base) overrideServices := mapByName(override) for name, overrideService := range overrideServices { overrideService := overrideService if baseService, ok := baseServices[name]; ok { merged, err := _merge(&baseService, &overrideService) if err != nil { return nil, errors.Wrapf(err, "cannot merge service %s", name) } baseServices[name] = *merged continue } baseServices[name] = overrideService } services := []types.ServiceConfig{} for _, baseService := range baseServices { services = append(services, baseService) } sort.Slice(services, func(i, j int) bool { return services[i].Name < services[j].Name }) return services, nil } func _merge(baseService *types.ServiceConfig, overrideService *types.ServiceConfig) (*types.ServiceConfig, error) { if err := mergo.Merge(baseService, overrideService, mergo.WithAppendSlice, mergo.WithOverride, mergo.WithTransformers(serviceSpecials)); err != nil { return nil, err } if overrideService.Command != nil { baseService.Command = overrideService.Command } if overrideService.Entrypoint != nil { baseService.Entrypoint = overrideService.Entrypoint } return baseService, nil } func toServiceSecretConfigsMap(s interface{}) (map[interface{}]interface{}, error) { secrets, ok := s.([]types.ServiceSecretConfig) if !ok { return nil, errors.Errorf("not a serviceSecretConfig: %v", s) } m := map[interface{}]interface{}{} for _, secret := range secrets { m[secret.Source] = secret } return m, nil } func toServiceConfigObjConfigsMap(s interface{}) (map[interface{}]interface{}, error) { secrets, ok := s.([]types.ServiceConfigObjConfig) if !ok { return nil, errors.Errorf("not a serviceSecretConfig: %v", s) } m := map[interface{}]interface{}{} for _, secret := range secrets { m[secret.Source] = secret } return m, nil } func toServicePortConfigsMap(s interface{}) (map[interface{}]interface{}, error) { ports, ok := s.([]types.ServicePortConfig) if !ok { return nil, errors.Errorf("not a servicePortConfig slice: %v", s) } m := map[interface{}]interface{}{} type port struct { target uint32 published uint32 ip string protocol string } for _, p := range ports { mergeKey := port{ target: p.Target, published: p.Published, ip: p.HostIP, protocol: p.Protocol, } m[mergeKey] = p } return m, nil } func toServiceVolumeConfigsMap(s interface{}) (map[interface{}]interface{}, error) { volumes, ok := s.([]types.ServiceVolumeConfig) if !ok { return nil, errors.Errorf("not a ServiceVolumeConfig slice: %v", s) } m := map[interface{}]interface{}{} for _, v := range volumes { m[v.Target] = v } return m, nil } func toServiceSecretConfigsSlice(dst reflect.Value, m map[interface{}]interface{}) error { s := []types.ServiceSecretConfig{} for _, v := range m { s = append(s, v.(types.ServiceSecretConfig)) } sort.Slice(s, func(i, j int) bool { return s[i].Source < s[j].Source }) dst.Set(reflect.ValueOf(s)) return nil } func toSServiceConfigObjConfigsSlice(dst reflect.Value, m map[interface{}]interface{}) error { s := []types.ServiceConfigObjConfig{} for _, v := range m { s = append(s, v.(types.ServiceConfigObjConfig)) } sort.Slice(s, func(i, j int) bool { return s[i].Source < s[j].Source }) dst.Set(reflect.ValueOf(s)) return nil } func toServicePortConfigsSlice(dst reflect.Value, m map[interface{}]interface{}) error { s := []types.ServicePortConfig{} for _, v := range m { s = append(s, v.(types.ServicePortConfig)) } sort.Slice(s, func(i, j int) bool { if s[i].Target != s[j].Target { return s[i].Target < s[j].Target } if s[i].Published != s[j].Published { return s[i].Published < s[j].Published } if s[i].HostIP != s[j].HostIP { return s[i].HostIP < s[j].HostIP } return s[i].Protocol < s[j].Protocol }) dst.Set(reflect.ValueOf(s)) return nil } func toServiceVolumeConfigsSlice(dst reflect.Value, m map[interface{}]interface{}) error { s := []types.ServiceVolumeConfig{} for _, v := range m { s = append(s, v.(types.ServiceVolumeConfig)) } sort.Slice(s, func(i, j int) bool { return s[i].Target < s[j].Target }) dst.Set(reflect.ValueOf(s)) return nil } type tomapFn func(s interface{}) (map[interface{}]interface{}, error) type writeValueFromMapFn func(reflect.Value, map[interface{}]interface{}) error func safelyMerge(mergeFn func(dst, src reflect.Value) error) func(dst, src reflect.Value) error { return func(dst, src reflect.Value) error { if src.IsNil() { return nil } if dst.IsNil() { dst.Set(src) return nil } return mergeFn(dst, src) } } func mergeSlice(tomap tomapFn, writeValue writeValueFromMapFn) func(dst, src reflect.Value) error { return func(dst, src reflect.Value) error { dstMap, err := sliceToMap(tomap, dst) if err != nil { return err } srcMap, err := sliceToMap(tomap, src) if err != nil { return err } if err := mergo.Map(&dstMap, srcMap, mergo.WithOverride); err != nil { return err } return writeValue(dst, dstMap) } } func sliceToMap(tomap tomapFn, v reflect.Value) (map[interface{}]interface{}, error) { // check if valid if !v.IsValid() { return nil, errors.Errorf("invalid value : %+v", v) } return tomap(v.Interface()) } func mergeLoggingConfig(dst, src reflect.Value) error { // Same driver, merging options if getLoggingDriver(dst.Elem()) == getLoggingDriver(src.Elem()) || getLoggingDriver(dst.Elem()) == "" || getLoggingDriver(src.Elem()) == "" { if getLoggingDriver(dst.Elem()) == "" { dst.Elem().FieldByName("Driver").SetString(getLoggingDriver(src.Elem())) } dstOptions := dst.Elem().FieldByName("Options").Interface().(map[string]string) srcOptions := src.Elem().FieldByName("Options").Interface().(map[string]string) return mergo.Merge(&dstOptions, srcOptions, mergo.WithOverride) } // Different driver, override with src dst.Set(src) return nil } // nolint: unparam func mergeUlimitsConfig(dst, src reflect.Value) error { if src.Interface() != reflect.Zero(reflect.TypeOf(src.Interface())).Interface() { dst.Elem().Set(src.Elem()) } return nil } // nolint: unparam func mergeServiceNetworkConfig(dst, src reflect.Value) error { if src.Interface() != reflect.Zero(reflect.TypeOf(src.Interface())).Interface() { dst.Elem().FieldByName("Aliases").Set(src.Elem().FieldByName("Aliases")) if ipv4 := src.Elem().FieldByName("Ipv4Address").Interface().(string); ipv4 != "" { dst.Elem().FieldByName("Ipv4Address").SetString(ipv4) } if ipv6 := src.Elem().FieldByName("Ipv6Address").Interface().(string); ipv6 != "" { dst.Elem().FieldByName("Ipv6Address").SetString(ipv6) } } return nil } func getLoggingDriver(v reflect.Value) string { return v.FieldByName("Driver").String() } func mapByName(services []types.ServiceConfig) map[string]types.ServiceConfig { m := map[string]types.ServiceConfig{} for _, service := range services { m[service.Name] = service } return m } func mergeVolumes(base, override map[string]types.VolumeConfig) (map[string]types.VolumeConfig, error) { err := mergo.Map(&base, &override, mergo.WithOverride) return base, err } func mergeNetworks(base, override map[string]types.NetworkConfig) (map[string]types.NetworkConfig, error) { err := mergo.Map(&base, &override, mergo.WithOverride) return base, err } func mergeSecrets(base, override map[string]types.SecretConfig) (map[string]types.SecretConfig, error) { err := mergo.Map(&base, &override, mergo.WithOverride) return base, err } func mergeConfigs(base, override map[string]types.ConfigObjConfig) (map[string]types.ConfigObjConfig, error) { err := mergo.Map(&base, &override, mergo.WithOverride) return base, err } func mergeExtensions(base, override map[string]interface{}) (map[string]interface{}, error) { if base == nil { base = map[string]interface{}{} } err := mergo.Map(&base, &override, mergo.WithOverride) return base, err } ================================================ FILE: pkg/third_party/compose-go/loader/merge_test.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package loader import ( "reflect" "testing" "github.com/imdario/mergo" "github.com/compose-spec/compose-go/types" "gotest.tools/v3/assert" ) func TestLoadLogging(t *testing.T) { loggingCases := []struct { name string loggingBase map[string]interface{} loggingOverride map[string]interface{} expected *types.LoggingConfig }{ { name: "no_override_driver", loggingBase: map[string]interface{}{ "logging": map[string]interface{}{ "driver": "json-file", "options": map[string]interface{}{ "frequency": "2000", "timeout": "23", }, }, }, loggingOverride: map[string]interface{}{ "logging": map[string]interface{}{ "options": map[string]interface{}{ "timeout": "360", "pretty-print": "on", }, }, }, expected: &types.LoggingConfig{ Driver: "json-file", Options: map[string]string{ "frequency": "2000", "timeout": "360", "pretty-print": "on", }, }, }, { name: "override_driver", loggingBase: map[string]interface{}{ "logging": map[string]interface{}{ "driver": "json-file", "options": map[string]interface{}{ "frequency": "2000", "timeout": "23", }, }, }, loggingOverride: map[string]interface{}{ "logging": map[string]interface{}{ "driver": "syslog", "options": map[string]interface{}{ "timeout": "360", "pretty-print": "on", }, }, }, expected: &types.LoggingConfig{ Driver: "syslog", Options: map[string]string{ "timeout": "360", "pretty-print": "on", }, }, }, { name: "no_base_driver", loggingBase: map[string]interface{}{ "logging": map[string]interface{}{ "options": map[string]interface{}{ "frequency": "2000", "timeout": "23", }, }, }, loggingOverride: map[string]interface{}{ "logging": map[string]interface{}{ "driver": "json-file", "options": map[string]interface{}{ "timeout": "360", "pretty-print": "on", }, }, }, expected: &types.LoggingConfig{ Driver: "json-file", Options: map[string]string{ "frequency": "2000", "timeout": "360", "pretty-print": "on", }, }, }, { name: "no_driver", loggingBase: map[string]interface{}{ "logging": map[string]interface{}{ "options": map[string]interface{}{ "frequency": "2000", "timeout": "23", }, }, }, loggingOverride: map[string]interface{}{ "logging": map[string]interface{}{ "options": map[string]interface{}{ "timeout": "360", "pretty-print": "on", }, }, }, expected: &types.LoggingConfig{ Options: map[string]string{ "frequency": "2000", "timeout": "360", "pretty-print": "on", }, }, }, { name: "no_override_options", loggingBase: map[string]interface{}{ "logging": map[string]interface{}{ "driver": "json-file", "options": map[string]interface{}{ "frequency": "2000", "timeout": "23", }, }, }, loggingOverride: map[string]interface{}{ "logging": map[string]interface{}{ "driver": "syslog", }, }, expected: &types.LoggingConfig{ Driver: "syslog", }, }, { name: "no_base", loggingBase: map[string]interface{}{}, loggingOverride: map[string]interface{}{ "logging": map[string]interface{}{ "driver": "json-file", "options": map[string]interface{}{ "frequency": "2000", }, }, }, expected: &types.LoggingConfig{ Driver: "json-file", Options: map[string]string{ "frequency": "2000", }, }, }, } for _, tc := range loggingCases { t.Run(tc.name, func(t *testing.T) { configDetails := types.ConfigDetails{ ConfigFiles: []types.ConfigFile{ { Filename: "base.yml", Config: map[string]interface{}{ "services": map[string]interface{}{ "foo": tc.loggingBase, }, }, }, { Filename: "override.yml", Config: map[string]interface{}{ "services": map[string]interface{}{ "foo": tc.loggingOverride, }, }, }, }, } config, err := loadTestProject(configDetails) assert.NilError(t, err) assert.DeepEqual(t, &types.Project{ Name: "", WorkingDir: "", Services: []types.ServiceConfig{ { Name: "foo", Logging: tc.expected, Environment: types.MappingWithEquals{}, Scale: 1, }, }, Networks: types.Networks{}, Volumes: types.Volumes{}, Secrets: types.Secrets{}, Configs: types.Configs{}, Extensions: types.Extensions{}, }, config) }) } } func loadTestProject(configDetails types.ConfigDetails) (*types.Project, error) { return Load(configDetails, func(options *Options) { options.SkipNormalization = true options.SkipConsistencyCheck = true }) } func TestLoadMultipleServicePorts(t *testing.T) { portsCases := []struct { name string portBase map[string]interface{} portOverride map[string]interface{} expected []types.ServicePortConfig }{ { name: "no_override", portBase: map[string]interface{}{ "ports": []interface{}{ "8080:80", }, }, portOverride: map[string]interface{}{}, expected: []types.ServicePortConfig{ { Mode: "ingress", Published: 8080, Target: 80, Protocol: "tcp", }, }, }, { name: "override_different_published", portBase: map[string]interface{}{ "ports": []interface{}{ "8080:80", }, }, portOverride: map[string]interface{}{ "ports": []interface{}{ "8081:80", }, }, expected: []types.ServicePortConfig{ { Mode: "ingress", Published: 8080, Target: 80, Protocol: "tcp", }, { Mode: "ingress", Published: 8081, Target: 80, Protocol: "tcp", }, }, }, { name: "override_distinct_protocols", portBase: map[string]interface{}{ "ports": []interface{}{ "8080:80/tcp", }, }, portOverride: map[string]interface{}{ "ports": []interface{}{ "8080:80/udp", }, }, expected: []types.ServicePortConfig{ { Mode: "ingress", Published: 8080, Target: 80, Protocol: "tcp", }, { Mode: "ingress", Published: 8080, Target: 80, Protocol: "udp", }, }, }, { name: "override_one_sided", portBase: map[string]interface{}{ "ports": []interface{}{ "5000", "6000", }, }, portOverride: map[string]interface{}{}, expected: []types.ServicePortConfig{ { Mode: "ingress", Published: 0, Target: 5000, Protocol: "tcp", }, { Mode: "ingress", Published: 0, Target: 6000, Protocol: "tcp", }, }, }, } for _, tc := range portsCases { t.Run(tc.name, func(t *testing.T) { configDetails := types.ConfigDetails{ ConfigFiles: []types.ConfigFile{ { Filename: "base.yml", Config: map[string]interface{}{ "services": map[string]interface{}{ "foo": tc.portBase, }, }, }, { Filename: "override.yml", Config: map[string]interface{}{ "services": map[string]interface{}{ "foo": tc.portOverride, }, }, }, }, } config, err := loadTestProject(configDetails) assert.NilError(t, err) assert.DeepEqual(t, &types.Project{ Name: "", WorkingDir: "", Services: []types.ServiceConfig{ { Name: "foo", Ports: tc.expected, Environment: types.MappingWithEquals{}, Scale: 1, }, }, Networks: types.Networks{}, Volumes: types.Volumes{}, Secrets: types.Secrets{}, Configs: types.Configs{}, Extensions: types.Extensions{}, }, config) }) } } func TestLoadMultipleSecretsConfig(t *testing.T) { portsCases := []struct { name string secretBase map[string]interface{} secretOverride map[string]interface{} expected []types.ServiceSecretConfig }{ { name: "no_override", secretBase: map[string]interface{}{ "secrets": []interface{}{ "my_secret", }, }, secretOverride: map[string]interface{}{}, expected: []types.ServiceSecretConfig{ { Source: "my_secret", }, }, }, { name: "override_simple", secretBase: map[string]interface{}{ "secrets": []interface{}{ "foo_secret", }, }, secretOverride: map[string]interface{}{ "secrets": []interface{}{ "bar_secret", }, }, expected: []types.ServiceSecretConfig{ { Source: "bar_secret", }, { Source: "foo_secret", }, }, }, { name: "override_same_source", secretBase: map[string]interface{}{ "secrets": []interface{}{ "foo_secret", map[string]interface{}{ "source": "bar_secret", "target": "waw_secret", }, }, }, secretOverride: map[string]interface{}{ "secrets": []interface{}{ map[string]interface{}{ "source": "bar_secret", "target": "bof_secret", }, map[string]interface{}{ "source": "baz_secret", "target": "waw_secret", }, }, }, expected: []types.ServiceSecretConfig{ { Source: "bar_secret", Target: "bof_secret", }, { Source: "baz_secret", Target: "waw_secret", }, { Source: "foo_secret", }, }, }, } for _, tc := range portsCases { t.Run(tc.name, func(t *testing.T) { configDetails := types.ConfigDetails{ ConfigFiles: []types.ConfigFile{ { Filename: "base.yml", Config: map[string]interface{}{ "services": map[string]interface{}{ "foo": tc.secretBase, }, }, }, { Filename: "override.yml", Config: map[string]interface{}{ "services": map[string]interface{}{ "foo": tc.secretOverride, }, }, }, }, } config, err := loadTestProject(configDetails) assert.NilError(t, err) assert.DeepEqual(t, &types.Project{ Name: "", WorkingDir: "", Services: []types.ServiceConfig{ { Name: "foo", Secrets: tc.expected, Environment: types.MappingWithEquals{}, Scale: 1, }, }, Networks: types.Networks{}, Volumes: types.Volumes{}, Secrets: types.Secrets{}, Configs: types.Configs{}, Extensions: types.Extensions{}, }, config) }) } } func TestLoadMultipleConfigobjsConfig(t *testing.T) { portsCases := []struct { name string configBase map[string]interface{} configOverride map[string]interface{} expected []types.ServiceConfigObjConfig }{ { name: "no_override", configBase: map[string]interface{}{ "configs": []interface{}{ "my_config", }, }, configOverride: map[string]interface{}{}, expected: []types.ServiceConfigObjConfig{ { Source: "my_config", }, }, }, { name: "override_simple", configBase: map[string]interface{}{ "configs": []interface{}{ "foo_config", }, }, configOverride: map[string]interface{}{ "configs": []interface{}{ "bar_config", }, }, expected: []types.ServiceConfigObjConfig{ { Source: "bar_config", }, { Source: "foo_config", }, }, }, { name: "override_same_source", configBase: map[string]interface{}{ "configs": []interface{}{ "foo_config", map[string]interface{}{ "source": "bar_config", "target": "waw_config", }, }, }, configOverride: map[string]interface{}{ "configs": []interface{}{ map[string]interface{}{ "source": "bar_config", "target": "bof_config", }, map[string]interface{}{ "source": "baz_config", "target": "waw_config", }, }, }, expected: []types.ServiceConfigObjConfig{ { Source: "bar_config", Target: "bof_config", }, { Source: "baz_config", Target: "waw_config", }, { Source: "foo_config", }, }, }, } for _, tc := range portsCases { t.Run(tc.name, func(t *testing.T) { configDetails := types.ConfigDetails{ ConfigFiles: []types.ConfigFile{ { Filename: "base.yml", Config: map[string]interface{}{ "services": map[string]interface{}{ "foo": tc.configBase, }, }, }, { Filename: "override.yml", Config: map[string]interface{}{ "services": map[string]interface{}{ "foo": tc.configOverride, }, }, }, }, } config, err := loadTestProject(configDetails) assert.NilError(t, err) assert.DeepEqual(t, &types.Project{ Name: "", WorkingDir: "", Services: []types.ServiceConfig{ { Name: "foo", Configs: tc.expected, Environment: types.MappingWithEquals{}, Scale: 1, }, }, Networks: types.Networks{}, Volumes: types.Volumes{}, Secrets: types.Secrets{}, Configs: types.Configs{}, Extensions: types.Extensions{}, }, config) }) } } func TestLoadMultipleUlimits(t *testing.T) { ulimitCases := []struct { name string ulimitBase map[string]interface{} ulimitOverride map[string]interface{} expected map[string]*types.UlimitsConfig }{ { name: "no_override", ulimitBase: map[string]interface{}{ "ulimits": map[string]interface{}{ "noproc": 65535, }, }, ulimitOverride: map[string]interface{}{}, expected: map[string]*types.UlimitsConfig{ "noproc": { Single: 65535, }, }, }, { name: "override_simple", ulimitBase: map[string]interface{}{ "ulimits": map[string]interface{}{ "noproc": 65535, }, }, ulimitOverride: map[string]interface{}{ "ulimits": map[string]interface{}{ "noproc": 44444, }, }, expected: map[string]*types.UlimitsConfig{ "noproc": { Single: 44444, }, }, }, { name: "override_different_notation", ulimitBase: map[string]interface{}{ "ulimits": map[string]interface{}{ "nofile": map[string]interface{}{ "soft": 11111, "hard": 99999, }, "noproc": 44444, }, }, ulimitOverride: map[string]interface{}{ "ulimits": map[string]interface{}{ "nofile": 55555, "noproc": map[string]interface{}{ "soft": 22222, "hard": 33333, }, }, }, expected: map[string]*types.UlimitsConfig{ "noproc": { Soft: 22222, Hard: 33333, }, "nofile": { Single: 55555, }, }, }, } for _, tc := range ulimitCases { t.Run(tc.name, func(t *testing.T) { configDetails := types.ConfigDetails{ ConfigFiles: []types.ConfigFile{ { Filename: "base.yml", Config: map[string]interface{}{ "services": map[string]interface{}{ "foo": tc.ulimitBase, }, }, }, { Filename: "override.yml", Config: map[string]interface{}{ "services": map[string]interface{}{ "foo": tc.ulimitOverride, }, }, }, }, } config, err := loadTestProject(configDetails) assert.NilError(t, err) assert.DeepEqual(t, &types.Project{ Name: "", WorkingDir: "", Services: []types.ServiceConfig{ { Name: "foo", Ulimits: tc.expected, Environment: types.MappingWithEquals{}, Scale: 1, }, }, Networks: types.Networks{}, Volumes: types.Volumes{}, Secrets: types.Secrets{}, Configs: types.Configs{}, Extensions: types.Extensions{}, }, config) }) } } func TestLoadMultipleServiceNetworks(t *testing.T) { networkCases := []struct { name string networkBase map[string]interface{} networkOverride map[string]interface{} expected map[string]*types.ServiceNetworkConfig }{ { name: "no_override", networkBase: map[string]interface{}{ "networks": []interface{}{ "net1", "net2", }, }, networkOverride: map[string]interface{}{}, expected: map[string]*types.ServiceNetworkConfig{ "net1": nil, "net2": nil, }, }, { name: "override_simple", networkBase: map[string]interface{}{ "networks": []interface{}{ "net1", "net2", }, }, networkOverride: map[string]interface{}{ "networks": []interface{}{ "net1", "net3", }, }, expected: map[string]*types.ServiceNetworkConfig{ "net1": nil, "net2": nil, "net3": nil, }, }, { name: "override_with_aliases", networkBase: map[string]interface{}{ "networks": map[string]interface{}{ "net1": map[string]interface{}{ "aliases": []interface{}{ "alias1", }, }, "net2": nil, }, }, networkOverride: map[string]interface{}{ "networks": map[string]interface{}{ "net1": map[string]interface{}{ "aliases": []interface{}{ "alias2", "alias3", }, }, "net3": map[string]interface{}{}, }, }, expected: map[string]*types.ServiceNetworkConfig{ "net1": { Aliases: []string{"alias2", "alias3"}, }, "net2": nil, "net3": {}, }, }, } for _, tc := range networkCases { t.Run(tc.name, func(t *testing.T) { configDetails := types.ConfigDetails{ ConfigFiles: []types.ConfigFile{ { Filename: "base.yml", Config: map[string]interface{}{ "services": map[string]interface{}{ "foo": tc.networkBase, }, }, }, { Filename: "override.yml", Config: map[string]interface{}{ "services": map[string]interface{}{ "foo": tc.networkOverride, }, }, }, }, } config, err := loadTestProject(configDetails) assert.NilError(t, err) assert.DeepEqual(t, &types.Project{ Name: "", WorkingDir: "", Services: []types.ServiceConfig{ { Name: "foo", Networks: tc.expected, Environment: types.MappingWithEquals{}, Scale: 1, }, }, Networks: types.Networks{}, Volumes: types.Volumes{}, Secrets: types.Secrets{}, Configs: types.Configs{}, Extensions: types.Extensions{}, }, config) }) } } func TestLoadMultipleConfigs(t *testing.T) { base := map[string]interface{}{ "services": map[string]interface{}{ "foo": map[string]interface{}{ "image": "foo", "entrypoint": "echo", "command": "hellow world", "build": map[string]interface{}{ "context": ".", "dockerfile": "bar.Dockerfile", }, "ports": []interface{}{ "8080:80", "9090:90", }, "labels": []interface{}{ "foo=bar", }, "cap_add": []interface{}{ "NET_ADMIN", }, }, }, "volumes": map[string]interface{}{}, "networks": map[string]interface{}{}, "secrets": map[string]interface{}{}, "configs": map[string]interface{}{}, } override := map[string]interface{}{ "services": map[string]interface{}{ "foo": map[string]interface{}{ "image": "baz", "entrypoint": "ping", "command": "localhost", "build": map[string]interface{}{ "dockerfile": "foo.Dockerfile", "args": []interface{}{ "buildno=1", "password=secret", }, }, "ports": []interface{}{ map[string]interface{}{ "target": 81, "published": 8080, }, }, "labels": map[string]interface{}{ "foo": "baz", }, "cap_add": []interface{}{ "SYS_ADMIN", }, }, "bar": map[string]interface{}{ "image": "bar", }, }, "volumes": map[string]interface{}{}, "networks": map[string]interface{}{}, "secrets": map[string]interface{}{}, "configs": map[string]interface{}{}, } configDetails := types.ConfigDetails{ ConfigFiles: []types.ConfigFile{ {Filename: "base.yml", Config: base}, {Filename: "override.yml", Config: override}, }, } config, err := loadTestProject(configDetails) assert.NilError(t, err) assert.DeepEqual(t, &types.Project{ Name: "", WorkingDir: "", Services: []types.ServiceConfig{ { Name: "bar", Image: "bar", Environment: types.MappingWithEquals{}, Scale: 1, }, { Name: "foo", Image: "baz", Entrypoint: types.ShellCommand{"ping"}, Command: types.ShellCommand{"localhost"}, Build: &types.BuildConfig{ Context: ".", Dockerfile: "foo.Dockerfile", Args: types.MappingWithEquals{ "buildno": strPtr("1"), "password": strPtr("secret"), }, }, Ports: []types.ServicePortConfig{ { Mode: "ingress", Target: 80, Published: 8080, Protocol: "tcp", }, { Target: 81, Published: 8080, }, { Mode: "ingress", Target: 90, Published: 9090, Protocol: "tcp", }, }, Labels: types.Labels{ "foo": "baz", }, CapAdd: []string{"NET_ADMIN", "SYS_ADMIN"}, Environment: types.MappingWithEquals{}, Scale: 1, }}, Networks: types.Networks{}, Volumes: types.Volumes{}, Secrets: types.Secrets{}, Configs: types.Configs{}, Extensions: types.Extensions{}, }, config) } // Issue#972 func TestLoadMultipleNetworks(t *testing.T) { base := map[string]interface{}{ "services": map[string]interface{}{ "foo": map[string]interface{}{ "image": "baz", }, }, "volumes": map[string]interface{}{}, "networks": map[string]interface{}{ "hostnet": map[string]interface{}{ "driver": "overlay", "ipam": map[string]interface{}{ "driver": "default", "config": []interface{}{ map[string]interface{}{ "subnet": "10.0.0.0/20", }, }, }, }, }, "secrets": map[string]interface{}{}, "configs": map[string]interface{}{}, } override := map[string]interface{}{ "services": map[string]interface{}{}, "volumes": map[string]interface{}{}, "networks": map[string]interface{}{ "hostnet": map[string]interface{}{ "external": map[string]interface{}{ "name": "host", }, }, }, "secrets": map[string]interface{}{}, "configs": map[string]interface{}{}, } configDetails := types.ConfigDetails{ ConfigFiles: []types.ConfigFile{ {Filename: "base.yml", Config: base}, {Filename: "override.yml", Config: override}, }, } config, err := loadTestProject(configDetails) assert.NilError(t, err) assert.DeepEqual(t, &types.Project{ Name: "", WorkingDir: "", Services: []types.ServiceConfig{ { Name: "foo", Image: "baz", Environment: types.MappingWithEquals{}, Scale: 1, }}, Networks: map[string]types.NetworkConfig{ "hostnet": { Name: "host", External: types.External{ External: true, }, }, }, Volumes: types.Volumes{}, Secrets: types.Secrets{}, Configs: types.Configs{}, Extensions: types.Extensions{}, }, config) } func TestMergeUlimitsConfig(t *testing.T) { specials := &specials{ m: map[reflect.Type]func(dst, src reflect.Value) error{ reflect.TypeOf(&types.UlimitsConfig{}): mergeUlimitsConfig, }, } base := map[string]*types.UlimitsConfig{ "override-single": {Single: 100}, "override-single-with-soft-hard": {Single: 200}, "override-soft-hard": {Soft: 300, Hard: 301}, "override-soft-hard-with-single": {Soft: 400, Hard: 401}, "dont-override": {Single: 500}, } override := map[string]*types.UlimitsConfig{ "override-single": {Single: 110}, "override-single-with-soft-hard": {Soft: 210, Hard: 211}, "override-soft-hard": {Soft: 310, Hard: 311}, "override-soft-hard-with-single": {Single: 410}, "add": {Single: 610}, } err := mergo.Merge(&base, &override, mergo.WithOverride, mergo.WithTransformers(specials)) assert.NilError(t, err) assert.DeepEqual( t, base, map[string]*types.UlimitsConfig{ "override-single": {Single: 110}, "override-single-with-soft-hard": {Soft: 210, Hard: 211}, "override-soft-hard": {Soft: 310, Hard: 311}, "override-soft-hard-with-single": {Single: 410}, "dont-override": {Single: 500}, "add": {Single: 610}, }, ) } func TestMergeServiceNetworkConfig(t *testing.T) { specials := &specials{ m: map[reflect.Type]func(dst, src reflect.Value) error{ reflect.TypeOf(&types.ServiceNetworkConfig{}): mergeServiceNetworkConfig, }, } base := map[string]*types.ServiceNetworkConfig{ "override-aliases": { Aliases: []string{"100", "101"}, Ipv4Address: "127.0.0.1", Ipv6Address: "0:0:0:0:0:0:0:1", }, "dont-override": { Aliases: []string{"200", "201"}, Ipv4Address: "127.0.0.2", Ipv6Address: "0:0:0:0:0:0:0:2", }, } override := map[string]*types.ServiceNetworkConfig{ "override-aliases": { Aliases: []string{"110", "111"}, Ipv4Address: "127.0.1.1", Ipv6Address: "0:0:0:0:0:0:1:1", }, "add": { Aliases: []string{"310", "311"}, Ipv4Address: "127.0.3.1", Ipv6Address: "0:0:0:0:0:0:3:1", }, } err := mergo.Merge(&base, &override, mergo.WithOverride, mergo.WithTransformers(specials)) assert.NilError(t, err) assert.DeepEqual( t, base, map[string]*types.ServiceNetworkConfig{ "override-aliases": { Aliases: []string{"110", "111"}, Ipv4Address: "127.0.1.1", Ipv6Address: "0:0:0:0:0:0:1:1", }, "dont-override": { Aliases: []string{"200", "201"}, Ipv4Address: "127.0.0.2", Ipv6Address: "0:0:0:0:0:0:0:2", }, "add": { Aliases: []string{"310", "311"}, Ipv4Address: "127.0.3.1", Ipv6Address: "0:0:0:0:0:0:3:1", }, }, ) } func TestMergeTopLevelExtensions(t *testing.T) { base := map[string]interface{}{ "x-foo": "foo", "x-bar": map[string]interface{}{ "base": map[string]interface{}{}, }, } override := map[string]interface{}{ "x-bar": map[string]interface{}{ "base": "qix", }, "x-zot": "zot", } configDetails := types.ConfigDetails{ ConfigFiles: []types.ConfigFile{ {Filename: "base.yml", Config: base}, {Filename: "override.yml", Config: override}, }, } config, err := loadTestProject(configDetails) assert.NilError(t, err) assert.DeepEqual(t, &types.Project{ Name: "", WorkingDir: "", Services: types.Services{}, Networks: types.Networks{}, Volumes: types.Volumes{}, Secrets: types.Secrets{}, Configs: types.Configs{}, Extensions: types.Extensions{ "x-foo": "foo", "x-bar": map[string]interface{}{ "base": "qix", }, "x-zot": "zot", }, }, config) } func TestMergeCommands(t *testing.T) { configDetails := types.ConfigDetails{ ConfigFiles: []types.ConfigFile{ {Filename: "base.yml", Config: map[string]interface{}{ "services": map[string]interface{}{ "foo": map[string]interface{}{ "image": "alpine", "command": "/bin/bash -c \"echo 'hello'\"", }, }, }}, {Filename: "override.yml", Config: map[string]interface{}{ "services": map[string]interface{}{ "foo": map[string]interface{}{ "image": "alpine", "command": "/bin/ash -c \"echo 'world'\"", }, }, }}, }, } merged, err := loadTestProject(configDetails) assert.NilError(t, err) assert.DeepEqual(t, merged.Services[0].Command, types.ShellCommand{"/bin/ash", "-c", "echo 'world'"}) } ================================================ FILE: pkg/third_party/compose-go/loader/normalize.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package loader import ( "fmt" "os" "path/filepath" "github.com/compose-spec/compose-go/errdefs" "github.com/compose-spec/compose-go/types" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) // normalize compose project by moving deprecated attributes to their canonical position and injecting implicit defaults func normalize(project *types.Project, resolvePaths bool) error { absWorkingDir, err := filepath.Abs(project.WorkingDir) if err != nil { return err } project.WorkingDir = absWorkingDir absComposeFiles, err := absComposeFiles(project.ComposeFiles) if err != nil { return err } project.ComposeFiles = absComposeFiles // If not declared explicitly, Compose model involves an implicit "default" network if _, ok := project.Networks["default"]; !ok { project.Networks["default"] = types.NetworkConfig{} } err = relocateExternalName(project) if err != nil { return err } for i, s := range project.Services { if len(s.Networks) == 0 && s.NetworkMode == "" { // Service without explicit network attachment are implicitly exposed on default network s.Networks = map[string]*types.ServiceNetworkConfig{"default": nil} } if s.PullPolicy == types.PullPolicyIfNotPresent { s.PullPolicy = types.PullPolicyMissing } fn := func(s string) (string, bool) { v, ok := project.Environment[s] return v, ok } if s.Build != nil { if s.Build.Dockerfile == "" { s.Build.Dockerfile = "Dockerfile" } localContext := absPath(project.WorkingDir, s.Build.Context) if _, err := os.Stat(localContext); err == nil { if resolvePaths { s.Build.Context = localContext s.Build.Dockerfile = absPath(localContext, s.Build.Dockerfile) } } else { // might be a remote http/git context. Unfortunately supported "remote" syntax is highly ambiguous // in moby/moby and not defined by compose-spec, so let's assume runtime will check } s.Build.Args = s.Build.Args.Resolve(fn) } s.Environment = s.Environment.Resolve(fn) err := relocateLogDriver(&s) if err != nil { return err } err = relocateLogOpt(&s) if err != nil { return err } err = relocateDockerfile(&s) if err != nil { return err } err = relocateScale(&s) if err != nil { return err } project.Services[i] = s } setNameFromKey(project) return nil } func relocateScale(s *types.ServiceConfig) error { scale := uint64(s.Scale) if scale != 1 { logrus.Warn("`scale` is deprecated. Use the `deploy.replicas` element") if s.Deploy == nil { s.Deploy = &types.DeployConfig{} } if s.Deploy.Replicas != nil && *s.Deploy.Replicas != scale { return errors.Wrap(errdefs.ErrInvalid, "can't use both 'scale' (deprecated) and 'deploy.replicas'") } s.Deploy.Replicas = &scale } return nil } func absComposeFiles(composeFiles []string) ([]string, error) { absComposeFiles := make([]string, len(composeFiles)) for i, composeFile := range composeFiles { absComposefile, err := filepath.Abs(composeFile) if err != nil { return nil, err } absComposeFiles[i] = absComposefile } return absComposeFiles, nil } // Resources with no explicit name are actually named by their key in map func setNameFromKey(project *types.Project) { for i, n := range project.Networks { if n.Name == "" { n.Name = fmt.Sprintf("%s_%s", project.Name, i) project.Networks[i] = n } } for i, v := range project.Volumes { if v.Name == "" { v.Name = fmt.Sprintf("%s_%s", project.Name, i) project.Volumes[i] = v } } for i, c := range project.Configs { if c.Name == "" { c.Name = fmt.Sprintf("%s_%s", project.Name, i) project.Configs[i] = c } } for i, s := range project.Secrets { if s.Name == "" { s.Name = fmt.Sprintf("%s_%s", project.Name, i) project.Secrets[i] = s } } } func relocateExternalName(project *types.Project) error { for i, n := range project.Networks { if n.External.Name != "" { if n.Name != "" { return errors.Wrap(errdefs.ErrInvalid, "can't use both 'networks.external.name' (deprecated) and 'networks.name'") } n.Name = n.External.Name } project.Networks[i] = n } for i, v := range project.Volumes { if v.External.Name != "" { if v.Name != "" { return errors.Wrap(errdefs.ErrInvalid, "can't use both 'volumes.external.name' (deprecated) and 'volumes.name'") } v.Name = v.External.Name } project.Volumes[i] = v } for i, s := range project.Secrets { if s.External.Name != "" { if s.Name != "" { return errors.Wrap(errdefs.ErrInvalid, "can't use both 'secrets.external.name' (deprecated) and 'secrets.name'") } s.Name = s.External.Name } project.Secrets[i] = s } for i, c := range project.Configs { if c.External.Name != "" { if c.Name != "" { return errors.Wrap(errdefs.ErrInvalid, "can't use both 'configs.external.name' (deprecated) and 'configs.name'") } c.Name = c.External.Name } project.Configs[i] = c } return nil } func relocateLogOpt(s *types.ServiceConfig) error { if len(s.LogOpt) != 0 { logrus.Warn("`log_opts` is deprecated. Use the `logging` element") if s.Logging == nil { s.Logging = &types.LoggingConfig{} } for k, v := range s.LogOpt { if _, ok := s.Logging.Options[k]; !ok { s.Logging.Options[k] = v } else { return errors.Wrap(errdefs.ErrInvalid, "can't use both 'log_opt' (deprecated) and 'logging.options'") } } } return nil } func relocateLogDriver(s *types.ServiceConfig) error { if s.LogDriver != "" { logrus.Warn("`log_driver` is deprecated. Use the `logging` element") if s.Logging == nil { s.Logging = &types.LoggingConfig{} } if s.Logging.Driver == "" { s.Logging.Driver = s.LogDriver } else { return errors.Wrap(errdefs.ErrInvalid, "can't use both 'log_driver' (deprecated) and 'logging.driver'") } } return nil } func relocateDockerfile(s *types.ServiceConfig) error { if s.Dockerfile != "" { logrus.Warn("`dockerfile` is deprecated. Use the `build` element") if s.Build == nil { s.Build = &types.BuildConfig{} } if s.Dockerfile == "" { s.Build.Dockerfile = s.Dockerfile } else { return errors.Wrap(errdefs.ErrInvalid, "can't use both 'dockerfile' (deprecated) and 'build.dockerfile'") } } return nil } ================================================ FILE: pkg/third_party/compose-go/loader/normalize_test.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package loader import ( "os" "path/filepath" "testing" "github.com/compose-spec/compose-go/types" "gopkg.in/yaml.v2" "gotest.tools/v3/assert" ) func TestNormalizeNetworkNames(t *testing.T) { wd, _ := os.Getwd() project := types.Project{ Name: "myProject", WorkingDir: wd, Environment: map[string]string{ "FOO": "BAR", }, Networks: types.Networks{ "myExternalnet": { Name: "myExternalnet", // this is automaticaly setup by loader for externa networks before reaching normalization External: types.External{External: true}, }, "mynet": {}, "myNamedNet": { Name: "CustomName", }, }, Services: []types.ServiceConfig{ { Name: "foo", Build: &types.BuildConfig{ Context: "./testdata", Args: map[string]*string{ "FOO": nil, "ZOT": nil, }, }, Scale: 1, }, }, } expected := `services: foo: build: context: ./testdata dockerfile: Dockerfile args: FOO: BAR ZOT: null networks: default: null networks: default: name: myProject_default myExternalnet: name: myExternalnet external: true myNamedNet: name: CustomName mynet: name: myProject_mynet ` err := normalize(&project, false) assert.NilError(t, err) marshal, err := yaml.Marshal(project) assert.NilError(t, err) assert.DeepEqual(t, expected, string(marshal)) } func TestNormalizeAbsolutePaths(t *testing.T) { project := types.Project{ Name: "myProject", WorkingDir: "testdata", Networks: types.Networks{}, ComposeFiles: []string{filepath.Join("testdata", "simple", "compose.yaml"), filepath.Join("testdata", "simple", "compose-with-overrides.yaml")}, } absWorkingDir, _ := filepath.Abs("testdata") absComposeFile, _ := filepath.Abs(filepath.Join("testdata", "simple", "compose.yaml")) absOverrideFile, _ := filepath.Abs(filepath.Join("testdata", "simple", "compose-with-overrides.yaml")) expected := types.Project{ Name: "myProject", Networks: types.Networks{"default": {Name: "myProject_default"}}, WorkingDir: absWorkingDir, ComposeFiles: []string{absComposeFile, absOverrideFile}, } err := normalize(&project, false) assert.NilError(t, err) assert.DeepEqual(t, expected, project) } func TestNormalizeVolumes(t *testing.T) { project := types.Project{ Name: "myProject", Networks: types.Networks{}, Volumes: types.Volumes{ "myExternalVol": { Name: "myExternalVol", // this is automaticaly setup by loader for externa networks before reaching normalization External: types.External{External: true}, }, "myvol": {}, "myNamedVol": { Name: "CustomName", }, }, } absCwd, _ := filepath.Abs(".") expected := types.Project{ Name: "myProject", Networks: types.Networks{"default": {Name: "myProject_default"}}, Volumes: types.Volumes{ "myExternalVol": { Name: "myExternalVol", External: types.External{External: true}, }, "myvol": {Name: "myProject_myvol"}, "myNamedVol": { Name: "CustomName", }, }, WorkingDir: absCwd, ComposeFiles: []string{}, } err := normalize(&project, false) assert.NilError(t, err) assert.DeepEqual(t, expected, project) } ================================================ FILE: pkg/third_party/compose-go/loader/testdata/Dockerfile ================================================ # Copyright 2020 The Compose Specification Authors. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. FROM busybox:1.31.0-uclibc RUN echo something CMD top ================================================ FILE: pkg/third_party/compose-go/loader/testdata/compose-test-extends-imported.yaml ================================================ services: imported: image: nginx ================================================ FILE: pkg/third_party/compose-go/loader/testdata/compose-test-extends.yaml ================================================ services: importer: extends: file: compose-test-extends-imported.yaml service: imported ================================================ FILE: pkg/third_party/compose-go/loader/testdata/compose-test-with-version.yaml ================================================ version: "2" volumes: data: driver: local networks: front: {} services: web: build: ./Dockerfile networks: - front - default volumes_from: - other other: image: busybox:1.31.0-uclibc command: top volumes: - /data ================================================ FILE: pkg/third_party/compose-go/loader/types_test.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package loader import ( "encoding/json" "os" "testing" yaml "gopkg.in/yaml.v2" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) func TestMarshallConfig(t *testing.T) { workingDir, err := os.Getwd() assert.NilError(t, err) homeDir, err := os.UserHomeDir() assert.NilError(t, err) cfg := fullExampleConfig(workingDir, homeDir) expected := fullExampleYAML(workingDir, homeDir) actual, err := yaml.Marshal(cfg) assert.NilError(t, err) assert.Check(t, is.Equal(expected, string(actual))) // Make sure the expected still _, err = Load(buildConfigDetails(expected, map[string]string{}), func(options *Options) { options.SkipNormalization = true options.SkipConsistencyCheck = true }) assert.NilError(t, err) } func TestJSONMarshallConfig(t *testing.T) { workingDir, err := os.Getwd() assert.NilError(t, err) homeDir, err := os.UserHomeDir() assert.NilError(t, err) cfg := fullExampleConfig(workingDir, homeDir) expected := fullExampleJSON(workingDir, homeDir) actual, err := json.MarshalIndent(cfg, "", " ") assert.NilError(t, err) assert.Check(t, is.Equal(expected, string(actual))) _, err = Load(buildConfigDetails(expected, map[string]string{}), func(options *Options) { options.SkipNormalization = true options.SkipConsistencyCheck = true }) assert.NilError(t, err) } ================================================ FILE: pkg/third_party/compose-go/loader/validate.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package loader import ( "fmt" "strings" "github.com/compose-spec/compose-go/errdefs" "github.com/compose-spec/compose-go/types" "github.com/pkg/errors" ) // checkConsistency validate a compose model is consistent func checkConsistency(project *types.Project) error { for _, s := range project.Services { if s.Build == nil && s.Image == "" { return errors.Wrapf(errdefs.ErrInvalid, "service %q has neither an image nor a build context specified", s.Name) } for network := range s.Networks { if _, ok := project.Networks[network]; !ok { return errors.Wrap(errdefs.ErrInvalid, fmt.Sprintf("service %q refers to undefined network %s", s.Name, network)) } } if strings.HasPrefix(s.NetworkMode, types.ServicePrefix) { serviceName := s.NetworkMode[len(types.ServicePrefix):] if _, err := project.GetServices(serviceName); err != nil { return fmt.Errorf("service %q not found for network_mode 'service:%s'", serviceName, serviceName) } } for _, volume := range s.Volumes { switch volume.Type { case types.VolumeTypeVolume: if volume.Source != "" { // non anonymous volumes if _, ok := project.Volumes[volume.Source]; !ok { return errors.Wrap(errdefs.ErrInvalid, fmt.Sprintf("service %q refers to undefined volume %s", s.Name, volume.Source)) } } } } for _, secret := range s.Secrets { if _, ok := project.Secrets[secret.Source]; !ok { return errors.Wrap(errdefs.ErrInvalid, fmt.Sprintf("service %q refers to undefined secret %s", s.Name, secret.Source)) } } for _, config := range s.Configs { if _, ok := project.Configs[config.Source]; !ok { return errors.Wrap(errdefs.ErrInvalid, fmt.Sprintf("service %q refers to undefined config %s", s.Name, config.Source)) } } } return nil } ================================================ FILE: pkg/third_party/compose-go/loader/validate_test.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package loader import ( "testing" "gotest.tools/v3/assert" "github.com/compose-spec/compose-go/types" ) func TestValidateAnonymousVolume(t *testing.T) { project := &types.Project{ Services: types.Services([]types.ServiceConfig{ { Name: "myservice", Image: "my/service", Volumes: []types.ServiceVolumeConfig{ { Type: types.VolumeTypeVolume, Target: "/use/local", }, }, }, }), } err := checkConsistency(project) assert.NilError(t, err) } func TestValidateNamedVolume(t *testing.T) { project := &types.Project{ Services: types.Services([]types.ServiceConfig{ { Name: "myservice", Image: "my/service", Volumes: []types.ServiceVolumeConfig{ { Type: types.VolumeTypeVolume, Source: "myVolume", Target: "/use/local", }, }, }, }), } err := checkConsistency(project) assert.Error(t, err, `service "myservice" refers to undefined volume myVolume: invalid compose project`) project.Volumes = types.Volumes(map[string]types.VolumeConfig{ "myVolume": { Name: "myVolume", }, }) err = checkConsistency(project) assert.NilError(t, err) } func TestValidateNoBuildNoImage(t *testing.T) { project := &types.Project{ Services: types.Services([]types.ServiceConfig{ { Name: "myservice", }, }), } err := checkConsistency(project) assert.Error(t, err, `service "myservice" has neither an image nor a build context specified: invalid compose project`) } func TestValidateNetworkMode(t *testing.T) { t.Run("network_mode service fail", func(t *testing.T) { project := &types.Project{ Services: types.Services([]types.ServiceConfig{ { Name: "myservice1", Image: "scratch", }, { Name: "myservice2", Image: "scratch", NetworkMode: "service:myservice1", }, }), } err := checkConsistency(project) assert.NilError(t, err) }) t.Run("network_mode service fail", func(t *testing.T) { project := &types.Project{ Services: types.Services([]types.ServiceConfig{ { Name: "myservice1", Image: "scratch", }, { Name: "myservice2", Image: "scratch", NetworkMode: "service:nonexistentservice", }, }), } err := checkConsistency(project) assert.Error(t, err, `service "nonexistentservice" not found for network_mode 'service:nonexistentservice'`) }) t.Run("network_mode container", func(t *testing.T) { project := &types.Project{ Services: types.Services([]types.ServiceConfig{ { Name: "myservice1", ContainerName: "mycontainer_name", Image: "scratch", }, { Name: "myservice2", Image: "scratch", NetworkMode: "container:mycontainer_name", }, }), } err := checkConsistency(project) assert.NilError(t, err) }) } ================================================ FILE: pkg/third_party/compose-go/loader/volume.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package loader import ( "strings" "unicode" "unicode/utf8" "github.com/compose-spec/compose-go/types" "github.com/pkg/errors" ) const endOfSpec = rune(0) // ParseVolume parses a volume spec without any knowledge of the target platform func ParseVolume(spec string) (types.ServiceVolumeConfig, error) { volume := types.ServiceVolumeConfig{} switch len(spec) { case 0: return volume, errors.New("invalid empty volume spec") case 1, 2: volume.Target = spec volume.Type = string(types.VolumeTypeVolume) return volume, nil } buffer := []rune{} for _, char := range spec + string(endOfSpec) { switch { case isWindowsDrive(buffer, char): buffer = append(buffer, char) case char == ':' || char == endOfSpec: if err := populateFieldFromBuffer(char, buffer, &volume); err != nil { populateType(&volume) return volume, errors.Wrapf(err, "invalid spec: %s", spec) } buffer = []rune{} default: buffer = append(buffer, char) } } populateType(&volume) return volume, nil } func isWindowsDrive(buffer []rune, char rune) bool { return char == ':' && len(buffer) == 1 && unicode.IsLetter(buffer[0]) } func populateFieldFromBuffer(char rune, buffer []rune, volume *types.ServiceVolumeConfig) error { strBuffer := string(buffer) switch { case len(buffer) == 0: return errors.New("empty section between colons") // Anonymous volume case volume.Source == "" && char == endOfSpec: volume.Target = strBuffer return nil case volume.Source == "": volume.Source = strBuffer return nil case volume.Target == "": volume.Target = strBuffer return nil case char == ':': return errors.New("too many colons") } for _, option := range strings.Split(strBuffer, ",") { switch option { case "ro": volume.ReadOnly = true case "rw": volume.ReadOnly = false case "nocopy": volume.Volume = &types.ServiceVolumeVolume{NoCopy: true} default: if isBindOption(option) { volume.Bind = &types.ServiceVolumeBind{Propagation: option} } // ignore unknown options } } return nil } var Propagations = []string{ types.PropagationRPrivate, types.PropagationPrivate, types.PropagationRShared, types.PropagationShared, types.PropagationRSlave, types.PropagationSlave, } func isBindOption(option string) bool { for _, propagation := range Propagations { if option == propagation { return true } } return false } func populateType(volume *types.ServiceVolumeConfig) { if isFilePath(volume.Source) { volume.Type = types.VolumeTypeBind if volume.Bind == nil { volume.Bind = &types.ServiceVolumeBind{} } // For backward compatibility with docker-compose legacy, using short notation involves // bind will create missing host path volume.Bind.CreateHostPath = true } else { volume.Type = types.VolumeTypeVolume if volume.Volume == nil { volume.Volume = &types.ServiceVolumeVolume{} } } } func isFilePath(source string) bool { if source == "" { return false } switch source[0] { case '.', '/', '~': return true } // windows named pipes if strings.HasPrefix(source, `\\`) { return true } first, nextIndex := utf8.DecodeRuneInString(source) return isWindowsDrive([]rune{first}, rune(source[nextIndex])) } ================================================ FILE: pkg/third_party/compose-go/loader/volume_test.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package loader import ( "fmt" "testing" "github.com/compose-spec/compose-go/types" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) func TestParseVolumeAnonymousVolume(t *testing.T) { for _, path := range []string{"/path", "/path/foo"} { volume, err := ParseVolume(path) expected := types.ServiceVolumeConfig{Type: "volume", Target: path, Volume: &types.ServiceVolumeVolume{}} assert.NilError(t, err) assert.Check(t, is.DeepEqual(expected, volume)) } } func TestParseVolumeAnonymousVolumeWindows(t *testing.T) { for _, path := range []string{"C:\\path", "Z:\\path\\foo"} { volume, err := ParseVolume(path) expected := types.ServiceVolumeConfig{Type: "volume", Target: path, Volume: &types.ServiceVolumeVolume{}} assert.NilError(t, err) assert.Check(t, is.DeepEqual(expected, volume)) } } func TestParseVolumeTooManyColons(t *testing.T) { _, err := ParseVolume("/foo:/foo:ro:foo") assert.Error(t, err, "invalid spec: /foo:/foo:ro:foo: too many colons") } func TestParseVolumeShortVolumes(t *testing.T) { for _, path := range []string{".", "/a"} { volume, err := ParseVolume(path) expected := types.ServiceVolumeConfig{Type: "volume", Target: path} assert.NilError(t, err) assert.Check(t, is.DeepEqual(expected, volume)) } } func TestParseVolumeMissingSource(t *testing.T) { for _, spec := range []string{":foo", "/foo::ro"} { _, err := ParseVolume(spec) assert.ErrorContains(t, err, "empty section between colons") } } func TestParseVolumeBindMount(t *testing.T) { for _, path := range []string{"./foo", "~/thing", "../other", "/foo", "/home/user"} { volume, err := ParseVolume(path + ":/target") expected := types.ServiceVolumeConfig{ Type: "bind", Source: path, Target: "/target", Bind: &types.ServiceVolumeBind{CreateHostPath: true}, } assert.NilError(t, err) assert.Check(t, is.DeepEqual(expected, volume)) } } func TestParseVolumeRelativeBindMountWindows(t *testing.T) { for _, path := range []string{ "./foo", "~/thing", "../other", "D:\\path", "/home/user", } { volume, err := ParseVolume(path + ":d:\\target") expected := types.ServiceVolumeConfig{ Type: "bind", Source: path, Target: "d:\\target", Bind: &types.ServiceVolumeBind{CreateHostPath: true}, } assert.NilError(t, err) assert.Check(t, is.DeepEqual(expected, volume)) } } func TestParseVolumeWithBindOptions(t *testing.T) { volume, err := ParseVolume("/source:/target:slave") expected := types.ServiceVolumeConfig{ Type: "bind", Source: "/source", Target: "/target", Bind: &types.ServiceVolumeBind{ CreateHostPath: true, Propagation: "slave", }, } assert.NilError(t, err) assert.Check(t, is.DeepEqual(expected, volume)) } func TestParseVolumeWithBindOptionsWindows(t *testing.T) { volume, err := ParseVolume("C:\\source\\foo:D:\\target:ro,rprivate") expected := types.ServiceVolumeConfig{ Type: "bind", Source: "C:\\source\\foo", Target: "D:\\target", ReadOnly: true, Bind: &types.ServiceVolumeBind{ CreateHostPath: true, Propagation: "rprivate", }, } assert.NilError(t, err) assert.Check(t, is.DeepEqual(expected, volume)) } func TestParseVolumeWithInvalidVolumeOptions(t *testing.T) { _, err := ParseVolume("name:/target:bogus") assert.NilError(t, err) } func TestParseVolumeWithVolumeOptions(t *testing.T) { volume, err := ParseVolume("name:/target:nocopy") expected := types.ServiceVolumeConfig{ Type: "volume", Source: "name", Target: "/target", Volume: &types.ServiceVolumeVolume{NoCopy: true}, } assert.NilError(t, err) assert.Check(t, is.DeepEqual(expected, volume)) } func TestParseVolumeWithReadOnly(t *testing.T) { for _, path := range []string{"./foo", "/home/user"} { volume, err := ParseVolume(path + ":/target:ro") expected := types.ServiceVolumeConfig{ Type: "bind", Source: path, Target: "/target", ReadOnly: true, Bind: &types.ServiceVolumeBind{CreateHostPath: true}, } assert.NilError(t, err) assert.Check(t, is.DeepEqual(expected, volume)) } } func TestParseVolumeWithRW(t *testing.T) { for _, path := range []string{"./foo", "/home/user"} { volume, err := ParseVolume(path + ":/target:rw") expected := types.ServiceVolumeConfig{ Type: "bind", Source: path, Target: "/target", ReadOnly: false, Bind: &types.ServiceVolumeBind{CreateHostPath: true}, } assert.NilError(t, err) assert.Check(t, is.DeepEqual(expected, volume)) } } func TestParseVolumeWindowsNamedPipe(t *testing.T) { volume, err := ParseVolume(`\\.\pipe\docker_engine:\\.\pipe\inside`) assert.NilError(t, err) expected := types.ServiceVolumeConfig{ Type: "bind", Source: `\\.\pipe\docker_engine`, Target: `\\.\pipe\inside`, Bind: &types.ServiceVolumeBind{CreateHostPath: true}, } assert.Check(t, is.DeepEqual(expected, volume)) } func TestIsFilePath(t *testing.T) { assert.Check(t, !isFilePath("a界")) } // Preserve the test cases for VolumeSplitN func TestParseVolumeSplitCases(t *testing.T) { for casenumber, x := range []struct { input string n int expected []string }{ {`C:\foo:d:`, -1, []string{`C:\foo`, `d:`}}, {`:C:\foo:d:`, -1, nil}, {`/foo:/bar:ro`, 3, []string{`/foo`, `/bar`, `ro`}}, {`/foo:/bar:ro`, 2, []string{`/foo`, `/bar:ro`}}, {`C:\foo\:/foo`, -1, []string{`C:\foo\`, `/foo`}}, {`d:\`, -1, []string{`d:\`}}, {`d:`, -1, []string{`d:`}}, {`d:\path`, -1, []string{`d:\path`}}, {`d:\path with space`, -1, []string{`d:\path with space`}}, {`d:\pathandmode:rw`, -1, []string{`d:\pathandmode`, `rw`}}, {`c:\:d:\`, -1, []string{`c:\`, `d:\`}}, {`c:\windows\:d:`, -1, []string{`c:\windows\`, `d:`}}, {`c:\windows:d:\s p a c e`, -1, []string{`c:\windows`, `d:\s p a c e`}}, {`c:\windows:d:\s p a c e:RW`, -1, []string{`c:\windows`, `d:\s p a c e`, `RW`}}, {`c:\program files:d:\s p a c e i n h o s t d i r`, -1, []string{`c:\program files`, `d:\s p a c e i n h o s t d i r`}}, {`0123456789name:d:`, -1, []string{`0123456789name`, `d:`}}, {`MiXeDcAsEnAmE:d:`, -1, []string{`MiXeDcAsEnAmE`, `d:`}}, {`name:D:`, -1, []string{`name`, `D:`}}, {`name:D::rW`, -1, []string{`name`, `D:`, `rW`}}, {`name:D::RW`, -1, []string{`name`, `D:`, `RW`}}, {`c:/:d:/forward/slashes/are/good/too`, -1, []string{`c:/`, `d:/forward/slashes/are/good/too`}}, {`c:\Windows`, -1, []string{`c:\Windows`}}, {`c:\Program Files (x86)`, -1, []string{`c:\Program Files (x86)`}}, {``, -1, nil}, {`.`, -1, []string{`.`}}, {`..\`, -1, []string{`..\`}}, {`c:\:..\`, -1, []string{`c:\`, `..\`}}, {`c:\:d:\:xyzzy`, -1, []string{`c:\`, `d:\`, `xyzzy`}}, // Cover directories with one-character name {`/tmp/x/y:/foo/x/y`, -1, []string{`/tmp/x/y`, `/foo/x/y`}}, } { parsed, _ := ParseVolume(x.input) expected := len(x.expected) > 1 msg := fmt.Sprintf("Case %d: %s", casenumber, x.input) assert.Check(t, is.Equal(expected, parsed.Source != ""), msg) } } func TestParseVolumeInvalidEmptySpec(t *testing.T) { _, err := ParseVolume("") assert.ErrorContains(t, err, "invalid empty volume spec") } func TestParseVolumeInvalidSections(t *testing.T) { _, err := ParseVolume("/foo::rw") assert.ErrorContains(t, err, "invalid spec") } ================================================ FILE: pkg/third_party/compose-go/loader/windows_path.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package loader // Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // https://github.com/golang/go/blob/master/LICENSE // This file contains utilities to check for Windows absolute paths on Linux. // The code in this file was largely copied from the Golang filepath package // https://github.com/golang/go/blob/1d0e94b1e13d5e8a323a63cd1cc1ef95290c9c36/src/path/filepath/path_windows.go#L12-L65 func isSlash(c uint8) bool { return c == '\\' || c == '/' } // isAbs reports whether the path is a Windows absolute path. func isAbs(path string) (b bool) { l := volumeNameLen(path) if l == 0 { return false } path = path[l:] if path == "" { return false } return isSlash(path[0]) } // volumeNameLen returns length of the leading volume name on Windows. // It returns 0 elsewhere. // nolint: gocyclo func volumeNameLen(path string) int { if len(path) < 2 { return 0 } // with drive letter c := path[0] if path[1] == ':' && ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') { return 2 } // is it UNC? https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx if l := len(path); l >= 5 && isSlash(path[0]) && isSlash(path[1]) && !isSlash(path[2]) && path[2] != '.' { // first, leading `\\` and next shouldn't be `\`. its server name. for n := 3; n < l-1; n++ { // second, next '\' shouldn't be repeated. if isSlash(path[n]) { n++ // third, following something characters. its share name. if !isSlash(path[n]) { if path[n] == '.' { break } for ; n < l; n++ { if isSlash(path[n]) { break } } return n } break } } } return 0 } ================================================ FILE: pkg/third_party/compose-go/loader/windows_path_test.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package loader // Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // https://github.com/golang/go/blob/master/LICENSE // The code in this file was copied from the Golang filepath package with some // small modifications to run it on non-Windows platforms. // https://github.com/golang/go/blob/1d0e94b1e13d5e8a323a63cd1cc1ef95290c9c36/src/path/filepath/path_test.go#L711-L763 import "testing" type IsAbsTest struct { path string isAbs bool } var isabstests = []IsAbsTest{ {"", false}, {"/", true}, {"/usr/bin/gcc", true}, {"..", false}, {"/a/../bb", true}, {".", false}, {"./", false}, {"lala", false}, } var winisabstests = []IsAbsTest{ {`C:\`, true}, {`c\`, false}, {`c::`, false}, {`c:`, false}, {`/`, false}, {`\`, false}, {`\Windows`, false}, {`c:a\b`, false}, {`c:\a\b`, true}, {`c:/a/b`, true}, {`\\host\share\foo`, true}, {`//host/share/foo/bar`, true}, } func TestIsAbs(t *testing.T) { tests := winisabstests // All non-windows tests should fail, because they have no volume letter. for _, test := range isabstests { tests = append(tests, IsAbsTest{test.path, false}) } // All non-windows test should work as intended if prefixed with volume letter. for _, test := range isabstests { tests = append(tests, IsAbsTest{"c:" + test.path, test.isAbs}) } for _, test := range tests { if r := isAbs(test.path); r != test.isAbs { t.Errorf("IsAbs(%q) = %v, want %v", test.path, r, test.isAbs) } } } ================================================ FILE: pkg/third_party/compose-go/loader/with-version-struct_test.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package loader import ( "github.com/compose-spec/compose-go/types" ) func withVersionExampleConfig(workingDir, homeDir string) *types.Config { return &types.Config{ Services: withVersionServices(workingDir, homeDir), Networks: withVersionNetworks(), Volumes: withVersionVolumes(), } } func withVersionServices(workingDir, homeDir string) []types.ServiceConfig { return []types.ServiceConfig{ { Name: "web", Build: &types.BuildConfig{ Context: "./Dockerfile", }, Environment: types.MappingWithEquals{}, Networks: map[string]*types.ServiceNetworkConfig{ "front": nil, "default": nil, }, VolumesFrom: []string{"other"}, Scale: 1, }, { Name: "other", Image: "busybox:1.31.0-uclibc", Command: []string{"top"}, Environment: types.MappingWithEquals{}, Volumes: []types.ServiceVolumeConfig{ {Target: "/data", Type: "volume", Volume: &types.ServiceVolumeVolume{}}, }, Scale: 1, }, } } func withVersionNetworks() map[string]types.NetworkConfig { return map[string]types.NetworkConfig{ "front": {}, } } func withVersionVolumes() map[string]types.VolumeConfig { return map[string]types.VolumeConfig{ "data": { Driver: "local", }, } } ================================================ FILE: pkg/third_party/compose-go/schema/compose-spec.json ================================================ { "$schema": "http://json-schema.org/draft/2019-09/schema#", "id": "compose_spec.json", "type": "object", "title": "Compose Specification", "description": "The Compose file is a YAML file defining a multi-containers based application.", "properties": { "version": { "type": "string", "description": "Version of the Compose specification used. Tools not implementing required version MUST reject the configuration file." }, "services": { "id": "#/properties/services", "type": "object", "patternProperties": { "^[a-zA-Z0-9._-]+$": { "$ref": "#/definitions/service" } }, "additionalProperties": false }, "networks": { "id": "#/properties/networks", "type": "object", "patternProperties": { "^[a-zA-Z0-9._-]+$": { "$ref": "#/definitions/network" } } }, "volumes": { "id": "#/properties/volumes", "type": "object", "patternProperties": { "^[a-zA-Z0-9._-]+$": { "$ref": "#/definitions/volume" } }, "additionalProperties": false }, "secrets": { "id": "#/properties/secrets", "type": "object", "patternProperties": { "^[a-zA-Z0-9._-]+$": { "$ref": "#/definitions/secret" } }, "additionalProperties": false }, "configs": { "id": "#/properties/configs", "type": "object", "patternProperties": { "^[a-zA-Z0-9._-]+$": { "$ref": "#/definitions/config" } }, "additionalProperties": false } }, "patternProperties": {"^x-": {}}, "additionalProperties": false, "definitions": { "service": { "id": "#/definitions/service", "type": "object", "properties": { "deploy": {"$ref": "#/definitions/deployment"}, "build": { "oneOf": [ {"type": "string"}, { "type": "object", "properties": { "context": {"type": "string"}, "dockerfile": {"type": "string"}, "args": {"$ref": "#/definitions/list_or_dict"}, "labels": {"$ref": "#/definitions/list_or_dict"}, "cache_from": {"type": "array", "items": {"type": "string"}}, "network": {"type": "string"}, "target": {"type": "string"}, "shm_size": {"type": ["integer", "string"]}, "extra_hosts": {"$ref": "#/definitions/list_or_dict"}, "isolation": {"type": "string"} }, "additionalProperties": false, "patternProperties": {"^x-": {}} } ] }, "blkio_config": { "type": "object", "properties": { "device_read_bps": { "type": "array", "items": {"$ref": "#/definitions/blkio_limit"} }, "device_read_iops": { "type": "array", "items": {"$ref": "#/definitions/blkio_limit"} }, "device_write_bps": { "type": "array", "items": {"$ref": "#/definitions/blkio_limit"} }, "device_write_iops": { "type": "array", "items": {"$ref": "#/definitions/blkio_limit"} }, "weight": {"type": "integer"}, "weight_device": { "type": "array", "items": {"$ref": "#/definitions/blkio_weight"} } }, "additionalProperties": false }, "cap_add": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, "cap_drop": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, "cgroup_parent": {"type": "string"}, "command": { "oneOf": [ {"type": "string"}, {"type": "array", "items": {"type": "string"}} ] }, "configs": { "type": "array", "items": { "oneOf": [ {"type": "string"}, { "type": "object", "properties": { "source": {"type": "string"}, "target": {"type": "string"}, "uid": {"type": "string"}, "gid": {"type": "string"}, "mode": {"type": "number"} }, "additionalProperties": false, "patternProperties": {"^x-": {}} } ] } }, "container_name": {"type": "string"}, "cpu_count": {"type": "integer", "minimum": 0}, "cpu_percent": {"type": "integer", "minimum": 0, "maximum": 100}, "cpu_shares": {"type": ["number", "string"]}, "cpu_quota": {"type": ["number", "string"]}, "cpu_period": {"type": ["number", "string"]}, "cpu_rt_period": {"type": ["number", "string"]}, "cpu_rt_runtime": {"type": ["number", "string"]}, "cpus": {"type": ["number", "string"]}, "cpuset": {"type": "string"}, "credential_spec": { "type": "object", "properties": { "config": {"type": "string"}, "file": {"type": "string"}, "registry": {"type": "string"} }, "additionalProperties": false, "patternProperties": {"^x-": {}} }, "depends_on": { "oneOf": [ {"$ref": "#/definitions/list_of_strings"}, { "type": "object", "additionalProperties": false, "patternProperties": { "^[a-zA-Z0-9._-]+$": { "type": "object", "additionalProperties": false, "properties": { "condition": { "type": "string", "enum": ["service_started", "service_healthy", "service_completed_successfully"] } }, "required": ["condition"] } } } ] }, "device_cgroup_rules": {"$ref": "#/definitions/list_of_strings"}, "devices": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, "dns": {"$ref": "#/definitions/string_or_list"}, "dns_opt": {"type": "array","items": {"type": "string"}, "uniqueItems": true}, "dns_search": {"$ref": "#/definitions/string_or_list"}, "domainname": {"type": "string"}, "entrypoint": { "oneOf": [ {"type": "string"}, {"type": "array", "items": {"type": "string"}} ] }, "env_file": {"$ref": "#/definitions/string_or_list"}, "environment": {"$ref": "#/definitions/list_or_dict"}, "expose": { "type": "array", "items": { "type": ["string", "number"], "format": "expose" }, "uniqueItems": true }, "extends": { "oneOf": [ {"type": "string"}, { "type": "object", "properties": { "service": {"type": "string"}, "file": {"type": "string"} }, "required": ["service"], "additionalProperties": false } ] }, "external_links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, "extra_hosts": {"$ref": "#/definitions/list_or_dict"}, "group_add": { "type": "array", "items": { "type": ["string", "number"] }, "uniqueItems": true }, "healthcheck": {"$ref": "#/definitions/healthcheck"}, "hostname": {"type": "string"}, "image": {"type": "string"}, "init": {"type": "boolean"}, "ipc": {"type": "string"}, "isolation": {"type": "string"}, "labels": {"$ref": "#/definitions/list_or_dict"}, "links": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, "logging": { "type": "object", "properties": { "driver": {"type": "string"}, "options": { "type": "object", "patternProperties": { "^.+$": {"type": ["string", "number", "null"]} } } }, "additionalProperties": false, "patternProperties": {"^x-": {}} }, "mac_address": {"type": "string"}, "mem_limit": {"type": ["number", "string"]}, "mem_reservation": {"type": ["string", "integer"]}, "mem_swappiness": {"type": "integer"}, "memswap_limit": {"type": ["number", "string"]}, "network_mode": {"type": "string"}, "networks": { "oneOf": [ {"$ref": "#/definitions/list_of_strings"}, { "type": "object", "patternProperties": { "^[a-zA-Z0-9._-]+$": { "oneOf": [ { "type": "object", "properties": { "aliases": {"$ref": "#/definitions/list_of_strings"}, "ipv4_address": {"type": "string"}, "ipv6_address": {"type": "string"}, "link_local_ips": {"$ref": "#/definitions/list_of_strings"}, "priority": {"type": "number"} }, "additionalProperties": false, "patternProperties": {"^x-": {}} }, {"type": "null"} ] } }, "additionalProperties": false } ] }, "oom_kill_disable": {"type": "boolean"}, "oom_score_adj": {"type": "integer", "minimum": -1000, "maximum": 1000}, "pid": {"type": ["string", "null"]}, "pids_limit": {"type": ["number", "string"]}, "platform": {"type": "string"}, "ports": { "type": "array", "items": { "oneOf": [ {"type": "number", "format": "ports"}, {"type": "string", "format": "ports"}, { "type": "object", "properties": { "mode": {"type": "string"}, "host_ip": {"type": "string"}, "target": {"type": "integer"}, "published": {"type": "integer"}, "protocol": {"type": "string"} }, "additionalProperties": false, "patternProperties": {"^x-": {}} } ] }, "uniqueItems": true }, "privileged": {"type": "boolean"}, "profiles": {"$ref": "#/definitions/list_of_strings"}, "pull_policy": {"type": "string", "enum": [ "always", "never", "if_not_present", "build" ]}, "read_only": {"type": "boolean"}, "restart": {"type": "string"}, "runtime": { "type": "string" }, "scale": { "type": "integer" }, "security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, "shm_size": {"type": ["number", "string"]}, "secrets": { "type": "array", "items": { "oneOf": [ {"type": "string"}, { "type": "object", "properties": { "source": {"type": "string"}, "target": {"type": "string"}, "uid": {"type": "string"}, "gid": {"type": "string"}, "mode": {"type": "number"} }, "additionalProperties": false, "patternProperties": {"^x-": {}} } ] } }, "sysctls": {"$ref": "#/definitions/list_or_dict"}, "stdin_open": {"type": "boolean"}, "stop_grace_period": {"type": "string", "format": "duration"}, "stop_signal": {"type": "string"}, "tmpfs": {"$ref": "#/definitions/string_or_list"}, "tty": {"type": "boolean"}, "ulimits": { "type": "object", "patternProperties": { "^[a-z]+$": { "oneOf": [ {"type": "integer"}, { "type": "object", "properties": { "hard": {"type": "integer"}, "soft": {"type": "integer"} }, "required": ["soft", "hard"], "additionalProperties": false, "patternProperties": {"^x-": {}} } ] } } }, "user": {"type": "string"}, "userns_mode": {"type": "string"}, "volumes": { "type": "array", "items": { "oneOf": [ {"type": "string"}, { "type": "object", "required": ["type"], "properties": { "type": {"type": "string"}, "source": {"type": "string"}, "target": {"type": "string"}, "read_only": {"type": "boolean"}, "consistency": {"type": "string"}, "bind": { "type": "object", "properties": { "propagation": {"type": "string"}, "create_host_path": {"type": "boolean"} }, "additionalProperties": false, "patternProperties": {"^x-": {}} }, "volume": { "type": "object", "properties": { "nocopy": {"type": "boolean"} }, "additionalProperties": false, "patternProperties": {"^x-": {}} }, "tmpfs": { "type": "object", "properties": { "size": { "type": "integer", "minimum": 0 } }, "additionalProperties": false, "patternProperties": {"^x-": {}} } }, "additionalProperties": false, "patternProperties": {"^x-": {}} } ] }, "uniqueItems": true }, "volumes_from": { "type": "array", "items": {"type": "string"}, "uniqueItems": true }, "working_dir": {"type": "string"} }, "patternProperties": {"^x-": {}}, "additionalProperties": false }, "healthcheck": { "id": "#/definitions/healthcheck", "type": "object", "properties": { "disable": {"type": "boolean"}, "interval": {"type": "string", "format": "duration"}, "retries": {"type": "number"}, "test": { "oneOf": [ {"type": "string"}, {"type": "array", "items": {"type": "string"}} ] }, "timeout": {"type": "string", "format": "duration"}, "start_period": {"type": "string", "format": "duration"} }, "additionalProperties": false, "patternProperties": {"^x-": {}} }, "deployment": { "id": "#/definitions/deployment", "type": ["object", "null"], "properties": { "mode": {"type": "string"}, "endpoint_mode": {"type": "string"}, "replicas": {"type": "integer"}, "labels": {"$ref": "#/definitions/list_or_dict"}, "rollback_config": { "type": "object", "properties": { "parallelism": {"type": "integer"}, "delay": {"type": "string", "format": "duration"}, "failure_action": {"type": "string"}, "monitor": {"type": "string", "format": "duration"}, "max_failure_ratio": {"type": "number"}, "order": {"type": "string", "enum": [ "start-first", "stop-first" ]} }, "additionalProperties": false, "patternProperties": {"^x-": {}} }, "update_config": { "type": "object", "properties": { "parallelism": {"type": "integer"}, "delay": {"type": "string", "format": "duration"}, "failure_action": {"type": "string"}, "monitor": {"type": "string", "format": "duration"}, "max_failure_ratio": {"type": "number"}, "order": {"type": "string", "enum": [ "start-first", "stop-first" ]} }, "additionalProperties": false, "patternProperties": {"^x-": {}} }, "resources": { "type": "object", "properties": { "limits": { "type": "object", "properties": { "cpus": {"type": ["number", "string"]}, "memory": {"type": "string"} }, "additionalProperties": false, "patternProperties": {"^x-": {}} }, "reservations": { "type": "object", "properties": { "cpus": {"type": ["number", "string"]}, "memory": {"type": "string"}, "generic_resources": {"$ref": "#/definitions/generic_resources"}, "devices": {"$ref": "#/definitions/devices"} }, "additionalProperties": false, "patternProperties": {"^x-": {}} } }, "additionalProperties": false, "patternProperties": {"^x-": {}} }, "restart_policy": { "type": "object", "properties": { "condition": {"type": "string"}, "delay": {"type": "string", "format": "duration"}, "max_attempts": {"type": "integer"}, "window": {"type": "string", "format": "duration"} }, "additionalProperties": false, "patternProperties": {"^x-": {}} }, "placement": { "type": "object", "properties": { "constraints": {"type": "array", "items": {"type": "string"}}, "preferences": { "type": "array", "items": { "type": "object", "properties": { "spread": {"type": "string"} }, "additionalProperties": false, "patternProperties": {"^x-": {}} } }, "max_replicas_per_node": {"type": "integer"} }, "additionalProperties": false, "patternProperties": {"^x-": {}} } }, "additionalProperties": false, "patternProperties": {"^x-": {}} }, "generic_resources": { "id": "#/definitions/generic_resources", "type": "array", "items": { "type": "object", "properties": { "discrete_resource_spec": { "type": "object", "properties": { "kind": {"type": "string"}, "value": {"type": "number"} }, "additionalProperties": false, "patternProperties": {"^x-": {}} } }, "additionalProperties": false, "patternProperties": {"^x-": {}} } }, "devices": { "id": "#/definitions/devices", "type": "array", "items": { "type": "object", "properties": { "capabilities": {"$ref": "#/definitions/list_of_strings"}, "count": {"type": ["string", "integer"]}, "device_ids": {"$ref": "#/definitions/list_of_strings"}, "driver":{"type": "string"}, "options":{"$ref": "#/definitions/list_or_dict"} }, "additionalProperties": false, "patternProperties": {"^x-": {}} } }, "network": { "id": "#/definitions/network", "type": ["object", "null"], "properties": { "name": {"type": "string"}, "driver": {"type": "string"}, "driver_opts": { "type": "object", "patternProperties": { "^.+$": {"type": ["string", "number"]} } }, "ipam": { "type": "object", "properties": { "driver": {"type": "string"}, "config": { "type": "array", "items": { "type": "object", "properties": { "subnet": {"type": "string", "format": "subnet_ip_address"}, "ip_range": {"type": "string"}, "gateway": {"type": "string"}, "aux_addresses": { "type": "object", "additionalProperties": false, "patternProperties": {"^.+$": {"type": "string"}} } }, "additionalProperties": false, "patternProperties": {"^x-": {}} } }, "options": { "type": "object", "additionalProperties": false, "patternProperties": {"^.+$": {"type": "string"}} } }, "additionalProperties": false, "patternProperties": {"^x-": {}} }, "external": { "type": ["boolean", "object"], "properties": { "name": { "deprecated": true, "type": "string" } }, "additionalProperties": false, "patternProperties": {"^x-": {}} }, "internal": {"type": "boolean"}, "enable_ipv6": {"type": "boolean"}, "attachable": {"type": "boolean"}, "labels": {"$ref": "#/definitions/list_or_dict"} }, "additionalProperties": false, "patternProperties": {"^x-": {}} }, "volume": { "id": "#/definitions/volume", "type": ["object", "null"], "properties": { "name": {"type": "string"}, "driver": {"type": "string"}, "driver_opts": { "type": "object", "patternProperties": { "^.+$": {"type": ["string", "number"]} } }, "external": { "type": ["boolean", "object"], "properties": { "name": { "deprecated": true, "type": "string" } }, "additionalProperties": false, "patternProperties": {"^x-": {}} }, "labels": {"$ref": "#/definitions/list_or_dict"} }, "additionalProperties": false, "patternProperties": {"^x-": {}} }, "secret": { "id": "#/definitions/secret", "type": "object", "properties": { "name": {"type": "string"}, "file": {"type": "string"}, "external": { "type": ["boolean", "object"], "properties": { "name": {"type": "string"} } }, "labels": {"$ref": "#/definitions/list_or_dict"}, "driver": {"type": "string"}, "driver_opts": { "type": "object", "patternProperties": { "^.+$": {"type": ["string", "number"]} } }, "template_driver": {"type": "string"} }, "additionalProperties": false, "patternProperties": {"^x-": {}} }, "config": { "id": "#/definitions/config", "type": "object", "properties": { "name": {"type": "string"}, "file": {"type": "string"}, "external": { "type": ["boolean", "object"], "properties": { "name": { "deprecated": true, "type": "string" } } }, "labels": {"$ref": "#/definitions/list_or_dict"}, "template_driver": {"type": "string"} }, "additionalProperties": false, "patternProperties": {"^x-": {}} }, "string_or_list": { "oneOf": [ {"type": "string"}, {"$ref": "#/definitions/list_of_strings"} ] }, "list_of_strings": { "type": "array", "items": {"type": "string"}, "uniqueItems": true }, "list_or_dict": { "oneOf": [ { "type": "object", "patternProperties": { ".+": { "type": ["string", "number", "boolean", "null"] } }, "additionalProperties": false }, {"type": "array", "items": {"type": "string"}, "uniqueItems": true} ] }, "blkio_limit": { "type": "object", "properties": { "path": {"type": "string"}, "rate": {"type": ["integer", "string"]} }, "additionalProperties": false }, "blkio_weight": { "type": "object", "properties": { "path": {"type": "string"}, "weight": {"type": "integer"} }, "additionalProperties": false }, "constraints": { "service": { "id": "#/definitions/constraints/service", "anyOf": [ {"required": ["build"]}, {"required": ["image"]} ], "properties": { "build": { "required": ["context"] } } } } } } ================================================ FILE: pkg/third_party/compose-go/schema/schema.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package schema import ( "fmt" "strings" "time" "github.com/xeipuuv/gojsonschema" // Enable support for embedded static resources _ "embed" ) const ( defaultVersion = "1.0" versionField = "version" ) type portsFormatChecker struct{} func (checker portsFormatChecker) IsFormat(input interface{}) bool { // TODO: implement this return true } type durationFormatChecker struct{} func (checker durationFormatChecker) IsFormat(input interface{}) bool { value, ok := input.(string) if !ok { return false } _, err := time.ParseDuration(value) return err == nil } func init() { gojsonschema.FormatCheckers.Add("expose", portsFormatChecker{}) gojsonschema.FormatCheckers.Add("ports", portsFormatChecker{}) gojsonschema.FormatCheckers.Add("duration", durationFormatChecker{}) } // Schema is the compose-spec JSON schema // //go:embed compose-spec.json var Schema string // Validate uses the jsonschema to validate the configuration func Validate(config map[string]interface{}) error { schemaLoader := gojsonschema.NewStringLoader(Schema) dataLoader := gojsonschema.NewGoLoader(config) result, err := gojsonschema.Validate(schemaLoader, dataLoader) if err != nil { return err } if !result.Valid() { return toError(result) } return nil } func toError(result *gojsonschema.Result) error { err := getMostSpecificError(result.Errors()) return err } const ( jsonschemaOneOf = "number_one_of" jsonschemaAnyOf = "number_any_of" ) func getDescription(err validationError) string { switch err.parent.Type() { case "invalid_type": if expectedType, ok := err.parent.Details()["expected"].(string); ok { return fmt.Sprintf("must be a %s", humanReadableType(expectedType)) } case jsonschemaOneOf, jsonschemaAnyOf: if err.child == nil { return err.parent.Description() } return err.child.Description() } return err.parent.Description() } func humanReadableType(definition string) string { if definition[0:1] == "[" { allTypes := strings.Split(definition[1:len(definition)-1], ",") for i, t := range allTypes { allTypes[i] = humanReadableType(t) } return fmt.Sprintf( "%s or %s", strings.Join(allTypes[0:len(allTypes)-1], ", "), allTypes[len(allTypes)-1], ) } if definition == "object" { return "mapping" } if definition == "array" { return "list" } return definition } type validationError struct { parent gojsonschema.ResultError child gojsonschema.ResultError } func (err validationError) Error() string { description := getDescription(err) return fmt.Sprintf("%s %s", err.parent.Field(), description) } func getMostSpecificError(errors []gojsonschema.ResultError) validationError { mostSpecificError := 0 for i, err := range errors { if specificity(err) > specificity(errors[mostSpecificError]) { mostSpecificError = i continue } if specificity(err) == specificity(errors[mostSpecificError]) { // Invalid type errors win in a tie-breaker for most specific field name if err.Type() == "invalid_type" && errors[mostSpecificError].Type() != "invalid_type" { mostSpecificError = i } } } if mostSpecificError+1 == len(errors) { return validationError{parent: errors[mostSpecificError]} } switch errors[mostSpecificError].Type() { case "number_one_of", "number_any_of": return validationError{ parent: errors[mostSpecificError], child: errors[mostSpecificError+1], } default: return validationError{parent: errors[mostSpecificError]} } } func specificity(err gojsonschema.ResultError) int { return len(strings.Split(err.Field(), ".")) } ================================================ FILE: pkg/third_party/compose-go/schema/schema_test.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package schema import ( "testing" "gotest.tools/v3/assert" ) type dict map[string]interface{} func TestValidate(t *testing.T) { config := dict{ "services": dict{ "foo": dict{ "image": "busybox", }, }, } assert.NilError(t, Validate(config)) } func TestValidateUndefinedTopLevelOption(t *testing.T) { config := dict{ "helicopters": dict{ "foo": dict{ "image": "busybox", }, }, } err := Validate(config) assert.ErrorContains(t, err, "Additional property helicopters is not allowed") } func TestValidateAllowsXTopLevelFields(t *testing.T) { config := dict{ "x-extra-stuff": dict{}, } assert.NilError(t, Validate(config)) assert.NilError(t, Validate(config)) } func TestValidateAllowsXFields(t *testing.T) { config := dict{ "services": dict{ "bar": dict{ "x-extra-stuff": dict{}, }, }, "volumes": dict{ "bar": dict{ "x-extra-stuff": dict{}, }, }, "networks": dict{ "bar": dict{ "x-extra-stuff": dict{}, }, }, "configs": dict{ "bar": dict{ "x-extra-stuff": dict{}, }, }, "secrets": dict{ "bar": dict{ "x-extra-stuff": dict{}, }, }, } assert.NilError(t, Validate(config)) assert.NilError(t, Validate(config)) } func TestValidateSecretConfigNames(t *testing.T) { config := dict{ "configs": dict{ "bar": dict{ "name": "foobar", }, }, "secrets": dict{ "baz": dict{ "name": "foobaz", }, }, } assert.NilError(t, Validate(config)) assert.NilError(t, Validate(config)) } type array []interface{} func TestValidatePlacement(t *testing.T) { config := dict{ "services": dict{ "foo": dict{ "image": "busybox", "deploy": dict{ "placement": dict{ "preferences": array{ dict{ "spread": "node.labels.az", }, }, }, }, }, }, } assert.NilError(t, Validate(config)) assert.NilError(t, Validate(config)) } func TestValidateIsolation(t *testing.T) { config := dict{ "services": dict{ "foo": dict{ "image": "busybox", "isolation": "some-isolation-value", }, }, } assert.NilError(t, Validate(config)) assert.NilError(t, Validate(config)) } func TestValidateRollbackConfig(t *testing.T) { config := dict{ "services": dict{ "foo": dict{ "image": "busybox", "deploy": dict{ "rollback_config": dict{ "parallelism": 1, }, }, }, }, } assert.NilError(t, Validate(config)) assert.NilError(t, Validate(config)) } func TestValidateRollbackConfigWithOrder(t *testing.T) { config := dict{ "services": dict{ "foo": dict{ "image": "busybox", "deploy": dict{ "rollback_config": dict{ "parallelism": 1, "order": "start-first", }, }, }, }, } assert.NilError(t, Validate(config)) assert.NilError(t, Validate(config)) } func TestValidateRollbackConfigWithUpdateConfig(t *testing.T) { config := dict{ "services": dict{ "foo": dict{ "image": "busybox", "deploy": dict{ "update_config": dict{ "parallelism": 1, "order": "start-first", }, "rollback_config": dict{ "parallelism": 1, "order": "start-first", }, }, }, }, } assert.NilError(t, Validate(config)) assert.NilError(t, Validate(config)) } func TestValidateRollbackConfigWithUpdateConfigFull(t *testing.T) { config := dict{ "services": dict{ "foo": dict{ "image": "busybox", "deploy": dict{ "update_config": dict{ "parallelism": 1, "order": "start-first", "delay": "10s", "failure_action": "pause", "monitor": "10s", }, "rollback_config": dict{ "parallelism": 1, "order": "start-first", "delay": "10s", "failure_action": "pause", "monitor": "10s", }, }, }, }, } assert.NilError(t, Validate(config)) assert.NilError(t, Validate(config)) } ================================================ FILE: pkg/third_party/compose-go/scripts/validate/fileheader ================================================ #!/usr/bin/env bash # Copyright The Compose Specification Authors. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -eu -o pipefail if ! command -v ltag; then >&2 echo "ERROR: ltag not found. Install with:" >&2 echo " go get -u github.com/kunalkushwaha/ltag" exit 1 fi BASEPATH="${1-}" ltag -t "${BASEPATH}scripts/validate/template" --excludes "validate" --check -v ================================================ FILE: pkg/third_party/compose-go/scripts/validate/template/bash.txt ================================================ # Copyright 2020 The Compose Specification Authors. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ================================================ FILE: pkg/third_party/compose-go/scripts/validate/template/dockerfile.txt ================================================ # Copyright 2020 The Compose Specification Authors. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ================================================ FILE: pkg/third_party/compose-go/scripts/validate/template/go.txt ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ ================================================ FILE: pkg/third_party/compose-go/scripts/validate/template/makefile.txt ================================================ # Copyright 2020 The Compose Specification Authors. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ================================================ FILE: pkg/third_party/compose-go/template/template.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package template import ( "fmt" "regexp" "strings" "github.com/sirupsen/logrus" ) var delimiter = "\\$" var substitutionNamed = "[_a-z][_a-z0-9]*" var substitutionBraced = "[_a-z][_a-z0-9]*(?::?[-?][^}]*)?" var patternString = fmt.Sprintf( "%s(?i:(?P%s)|(?P%s)|{(?P%s)}|(?P))", delimiter, delimiter, substitutionNamed, substitutionBraced, ) var defaultPattern = regexp.MustCompile(patternString) // DefaultSubstituteFuncs contains the default SubstituteFunc used by the docker cli var DefaultSubstituteFuncs = []SubstituteFunc{ softDefault, hardDefault, requiredNonEmpty, required, } // InvalidTemplateError is returned when a variable template is not in a valid // format type InvalidTemplateError struct { Template string } func (e InvalidTemplateError) Error() string { return fmt.Sprintf("Invalid template: %#v", e.Template) } // Mapping is a user-supplied function which maps from variable names to values. // Returns the value as a string and a bool indicating whether // the value is present, to distinguish between an empty string // and the absence of a value. type Mapping func(string) (string, bool) // SubstituteFunc is a user-supplied function that apply substitution. // Returns the value as a string, a bool indicating if the function could apply // the substitution and an error. type SubstituteFunc func(string, Mapping) (string, bool, error) // SubstituteWith substitute variables in the string with their values. // It accepts additional substitute function. func SubstituteWith(template string, mapping Mapping, pattern *regexp.Regexp, subsFuncs ...SubstituteFunc) (string, error) { var err error result := pattern.ReplaceAllStringFunc(template, func(substring string) string { matches := pattern.FindStringSubmatch(substring) groups := matchGroups(matches, pattern) if escaped := groups["escaped"]; escaped != "" { return escaped } braced := false substitution := groups["named"] if substitution == "" { substitution = groups["braced"] braced = true } if substitution == "" { err = &InvalidTemplateError{Template: template} return "" } if braced { for _, f := range subsFuncs { var ( value string applied bool ) value, applied, err = f(substitution, mapping) if err != nil { return "" } if !applied { continue } return value } } value, ok := mapping(substitution) if !ok { logrus.Warnf("The %q variable is not set. Defaulting to a blank string.", substitution) } return value }) return result, err } // Substitute variables in the string with their values func Substitute(template string, mapping Mapping) (string, error) { return SubstituteWith(template, mapping, defaultPattern, DefaultSubstituteFuncs...) } // ExtractVariables returns a map of all the variables defined in the specified // composefile (dict representation) and their default value if any. func ExtractVariables(configDict map[string]interface{}, pattern *regexp.Regexp) map[string]Variable { if pattern == nil { pattern = defaultPattern } return recurseExtract(configDict, pattern) } func recurseExtract(value interface{}, pattern *regexp.Regexp) map[string]Variable { m := map[string]Variable{} switch value := value.(type) { case string: if values, is := extractVariable(value, pattern); is { for _, v := range values { m[v.Name] = v } } case map[string]interface{}: for _, elem := range value { submap := recurseExtract(elem, pattern) for key, value := range submap { m[key] = value } } case []interface{}: for _, elem := range value { if values, is := extractVariable(elem, pattern); is { for _, v := range values { m[v.Name] = v } } } } return m } type Variable struct { Name string DefaultValue string Required bool } func extractVariable(value interface{}, pattern *regexp.Regexp) ([]Variable, bool) { sValue, ok := value.(string) if !ok { return []Variable{}, false } matches := pattern.FindAllStringSubmatch(sValue, -1) if len(matches) == 0 { return []Variable{}, false } values := []Variable{} for _, match := range matches { groups := matchGroups(match, pattern) if escaped := groups["escaped"]; escaped != "" { continue } val := groups["named"] if val == "" { val = groups["braced"] } name := val var defaultValue string var required bool switch { case strings.Contains(val, ":?"): name, _ = partition(val, ":?") required = true case strings.Contains(val, "?"): name, _ = partition(val, "?") required = true case strings.Contains(val, ":-"): name, defaultValue = partition(val, ":-") case strings.Contains(val, "-"): name, defaultValue = partition(val, "-") } values = append(values, Variable{ Name: name, DefaultValue: defaultValue, Required: required, }) } return values, len(values) > 0 } // Soft default (fall back if unset or empty) func softDefault(substitution string, mapping Mapping) (string, bool, error) { sep := ":-" if !strings.Contains(substitution, sep) { return "", false, nil } name, defaultValue := partition(substitution, sep) value, ok := mapping(name) if !ok || value == "" { return defaultValue, true, nil } return value, true, nil } // Hard default (fall back if-and-only-if empty) func hardDefault(substitution string, mapping Mapping) (string, bool, error) { sep := "-" if !strings.Contains(substitution, sep) { return "", false, nil } name, defaultValue := partition(substitution, sep) value, ok := mapping(name) if !ok { return defaultValue, true, nil } return value, true, nil } func requiredNonEmpty(substitution string, mapping Mapping) (string, bool, error) { return withRequired(substitution, mapping, ":?", func(v string) bool { return v != "" }) } func required(substitution string, mapping Mapping) (string, bool, error) { return withRequired(substitution, mapping, "?", func(_ string) bool { return true }) } func withRequired(substitution string, mapping Mapping, sep string, valid func(string) bool) (string, bool, error) { if !strings.Contains(substitution, sep) { return "", false, nil } name, errorMessage := partition(substitution, sep) value, ok := mapping(name) if !ok || !valid(value) { return "", true, &InvalidTemplateError{ Template: fmt.Sprintf("required variable %s is missing a value: %s", name, errorMessage), } } return value, true, nil } func matchGroups(matches []string, pattern *regexp.Regexp) map[string]string { groups := make(map[string]string) for i, name := range pattern.SubexpNames()[1:] { groups[name] = matches[i+1] } return groups } // Split the string at the first occurrence of sep, and return the part before the separator, // and the part after the separator. // // If the separator is not found, return the string itself, followed by an empty string. func partition(s, sep string) (string, string) { if strings.Contains(s, sep) { parts := strings.SplitN(s, sep, 2) return parts[0], parts[1] } return s, "" } ================================================ FILE: pkg/third_party/compose-go/template/template_test.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package template import ( "fmt" "reflect" "testing" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) var defaults = map[string]string{ "FOO": "first", "BAR": "", } func defaultMapping(name string) (string, bool) { val, ok := defaults[name] return val, ok } func TestEscaped(t *testing.T) { result, err := Substitute("$${foo}", defaultMapping) assert.NilError(t, err) assert.Check(t, is.Equal("${foo}", result)) } func TestSubstituteNoMatch(t *testing.T) { result, err := Substitute("foo", defaultMapping) assert.NilError(t, err) assert.Equal(t, "foo", result) } func TestInvalid(t *testing.T) { invalidTemplates := []string{ "${", "$}", "${}", "${ }", "${ foo}", "${foo }", "${foo!}", } for _, template := range invalidTemplates { _, err := Substitute(template, defaultMapping) assert.ErrorContains(t, err, "Invalid template") } } // see https://github.com/docker/compose/issues/8601 func TestNonBraced(t *testing.T) { substituted, err := Substitute("$FOO-bar", defaultMapping) assert.NilError(t, err) assert.Equal(t, substituted, "first-bar") } func TestNoValueNoDefault(t *testing.T) { for _, template := range []string{"This ${missing} var", "This ${BAR} var"} { result, err := Substitute(template, defaultMapping) assert.NilError(t, err) assert.Check(t, is.Equal("This var", result)) } } func TestValueNoDefault(t *testing.T) { for _, template := range []string{"This $FOO var", "This ${FOO} var"} { result, err := Substitute(template, defaultMapping) assert.NilError(t, err) assert.Check(t, is.Equal("This first var", result)) } } func TestNoValueWithDefault(t *testing.T) { for _, template := range []string{"ok ${missing:-def}", "ok ${missing-def}"} { result, err := Substitute(template, defaultMapping) assert.NilError(t, err) assert.Check(t, is.Equal("ok def", result)) } } func TestEmptyValueWithSoftDefault(t *testing.T) { result, err := Substitute("ok ${BAR:-def}", defaultMapping) assert.NilError(t, err) assert.Check(t, is.Equal("ok def", result)) } func TestValueWithSoftDefault(t *testing.T) { result, err := Substitute("ok ${FOO:-def}", defaultMapping) assert.NilError(t, err) assert.Check(t, is.Equal("ok first", result)) } func TestEmptyValueWithHardDefault(t *testing.T) { result, err := Substitute("ok ${BAR-def}", defaultMapping) assert.NilError(t, err) assert.Check(t, is.Equal("ok ", result)) } func TestNonAlphanumericDefault(t *testing.T) { result, err := Substitute("ok ${BAR:-/non:-alphanumeric}", defaultMapping) assert.NilError(t, err) assert.Check(t, is.Equal("ok /non:-alphanumeric", result)) } func TestMandatoryVariableErrors(t *testing.T) { testCases := []struct { template string expectedError string }{ { template: "not ok ${UNSET_VAR:?Mandatory Variable Unset}", expectedError: "required variable UNSET_VAR is missing a value: Mandatory Variable Unset", }, { template: "not ok ${BAR:?Mandatory Variable Empty}", expectedError: "required variable BAR is missing a value: Mandatory Variable Empty", }, { template: "not ok ${UNSET_VAR:?}", expectedError: "required variable UNSET_VAR is missing a value", }, { template: "not ok ${UNSET_VAR?Mandatory Variable Unset}", expectedError: "required variable UNSET_VAR is missing a value: Mandatory Variable Unset", }, { template: "not ok ${UNSET_VAR?}", expectedError: "required variable UNSET_VAR is missing a value", }, } for _, tc := range testCases { _, err := Substitute(tc.template, defaultMapping) assert.ErrorContains(t, err, tc.expectedError) assert.ErrorType(t, err, reflect.TypeOf(&InvalidTemplateError{})) } } func TestDefaultsForMandatoryVariables(t *testing.T) { testCases := []struct { template string expected string }{ { template: "ok ${FOO:?err}", expected: "ok first", }, { template: "ok ${FOO?err}", expected: "ok first", }, { template: "ok ${BAR?err}", expected: "ok ", }, } for _, tc := range testCases { result, err := Substitute(tc.template, defaultMapping) assert.NilError(t, err) assert.Check(t, is.Equal(tc.expected, result)) } } func TestSubstituteWithCustomFunc(t *testing.T) { errIsMissing := func(substitution string, mapping Mapping) (string, bool, error) { value, found := mapping(substitution) if !found { return "", true, &InvalidTemplateError{ Template: fmt.Sprintf("required variable %s is missing a value", substitution), } } return value, true, nil } result, err := SubstituteWith("ok ${FOO}", defaultMapping, defaultPattern, errIsMissing) assert.NilError(t, err) assert.Check(t, is.Equal("ok first", result)) result, err = SubstituteWith("ok ${BAR}", defaultMapping, defaultPattern, errIsMissing) assert.NilError(t, err) assert.Check(t, is.Equal("ok ", result)) _, err = SubstituteWith("ok ${NOTHERE}", defaultMapping, defaultPattern, errIsMissing) assert.Check(t, is.ErrorContains(err, "required variable")) } func TestExtractVariables(t *testing.T) { testCases := []struct { name string dict map[string]interface{} expected map[string]Variable }{ { name: "empty", dict: map[string]interface{}{}, expected: map[string]Variable{}, }, { name: "no-variables", dict: map[string]interface{}{ "foo": "bar", }, expected: map[string]Variable{}, }, { name: "variable-without-curly-braces", dict: map[string]interface{}{ "foo": "$bar", }, expected: map[string]Variable{ "bar": {Name: "bar"}, }, }, { name: "variable", dict: map[string]interface{}{ "foo": "${bar}", }, expected: map[string]Variable{ "bar": {Name: "bar", DefaultValue: ""}, }, }, { name: "required-variable", dict: map[string]interface{}{ "foo": "${bar?:foo}", }, expected: map[string]Variable{ "bar": {Name: "bar", DefaultValue: "", Required: true}, }, }, { name: "required-variable2", dict: map[string]interface{}{ "foo": "${bar?foo}", }, expected: map[string]Variable{ "bar": {Name: "bar", DefaultValue: "", Required: true}, }, }, { name: "default-variable", dict: map[string]interface{}{ "foo": "${bar:-foo}", }, expected: map[string]Variable{ "bar": {Name: "bar", DefaultValue: "foo"}, }, }, { name: "default-variable2", dict: map[string]interface{}{ "foo": "${bar-foo}", }, expected: map[string]Variable{ "bar": {Name: "bar", DefaultValue: "foo"}, }, }, { name: "multiple-values", dict: map[string]interface{}{ "foo": "${bar:-foo}", "bar": map[string]interface{}{ "foo": "${fruit:-banana}", "bar": "vegetable", }, "baz": []interface{}{ "foo", "$docker:${project:-cli}", "$toto", }, }, expected: map[string]Variable{ "bar": {Name: "bar", DefaultValue: "foo"}, "fruit": {Name: "fruit", DefaultValue: "banana"}, "toto": {Name: "toto", DefaultValue: ""}, "docker": {Name: "docker", DefaultValue: ""}, "project": {Name: "project", DefaultValue: "cli"}, }, }, } for _, tc := range testCases { tc := tc t.Run(tc.name, func(t *testing.T) { actual := ExtractVariables(tc.dict, defaultPattern) assert.Check(t, is.DeepEqual(actual, tc.expected)) }) } } ================================================ FILE: pkg/third_party/compose-go/types/config.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package types import ( "encoding/json" "github.com/mitchellh/mapstructure" ) // ConfigDetails are the details about a group of ConfigFiles type ConfigDetails struct { Version string WorkingDir string ConfigFiles []ConfigFile Environment map[string]string } // LookupEnv provides a lookup function for environment variables func (cd ConfigDetails) LookupEnv(key string) (string, bool) { v, ok := cd.Environment[key] return v, ok } // ConfigFile is a filename and the contents of the file as a Dict type ConfigFile struct { // Filename is the name of the yaml configuration file Filename string // Content is the raw yaml content. Will be loaded from Filename if not set Content []byte // Config if the yaml tree for this config file. Will be parsed from Content if not set Config map[string]interface{} } // Config is a full compose file configuration and model type Config struct { Filename string `yaml:"-" json:"-"` Services Services `json:"services"` Networks Networks `yaml:",omitempty" json:"networks,omitempty"` Volumes Volumes `yaml:",omitempty" json:"volumes,omitempty"` Secrets Secrets `yaml:",omitempty" json:"secrets,omitempty"` Configs Configs `yaml:",omitempty" json:"configs,omitempty"` Extensions Extensions `yaml:",inline" json:"-"` } // Volumes is a map of VolumeConfig type Volumes map[string]VolumeConfig // Networks is a map of NetworkConfig type Networks map[string]NetworkConfig // Secrets is a map of SecretConfig type Secrets map[string]SecretConfig // Configs is a map of ConfigObjConfig type Configs map[string]ConfigObjConfig // Extensions is a map of custom extension type Extensions map[string]interface{} // MarshalJSON makes Config implement json.Marshaler func (c Config) MarshalJSON() ([]byte, error) { m := map[string]interface{}{ "services": c.Services, } if len(c.Networks) > 0 { m["networks"] = c.Networks } if len(c.Volumes) > 0 { m["volumes"] = c.Volumes } if len(c.Secrets) > 0 { m["secrets"] = c.Secrets } if len(c.Configs) > 0 { m["configs"] = c.Configs } for k, v := range c.Extensions { m[k] = v } return json.Marshal(m) } func (e Extensions) Get(name string, target interface{}) (bool, error) { if v, ok := e[name]; ok { err := mapstructure.Decode(v, target) return true, err } return false, nil } ================================================ FILE: pkg/third_party/compose-go/types/config_test.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package types import ( "testing" "gotest.tools/v3/assert" ) func Test_WithServices(t *testing.T) { p := Project{ Services: append(Services{}, ServiceConfig{ Name: "service_1", DependsOn: map[string]ServiceDependency{ "service_3": { Condition: ServiceConditionStarted, }, }, }, ServiceConfig{ Name: "service_2", }, ServiceConfig{ Name: "service_3", DependsOn: map[string]ServiceDependency{ "service_2": { Condition: ServiceConditionStarted, }, }, }), } order := []string{} fn := func(service ServiceConfig) error { order = append(order, service.Name) return nil } err := p.WithServices(nil, fn) assert.NilError(t, err) assert.DeepEqual(t, order, []string{"service_2", "service_3", "service_1"}) } ================================================ FILE: pkg/third_party/compose-go/types/project.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package types import ( "fmt" "os" "path/filepath" "sort" "github.com/distribution/distribution/v3/reference" "github.com/opencontainers/go-digest" "golang.org/x/sync/errgroup" ) // Project is the result of loading a set of compose files type Project struct { Name string `yaml:"-" json:"-"` WorkingDir string `yaml:"-" json:"-"` Services Services `json:"services"` Networks Networks `yaml:",omitempty" json:"networks,omitempty"` Volumes Volumes `yaml:",omitempty" json:"volumes,omitempty"` Secrets Secrets `yaml:",omitempty" json:"secrets,omitempty"` Configs Configs `yaml:",omitempty" json:"configs,omitempty"` Extensions Extensions `yaml:",inline" json:"-"` // https://github.com/golang/go/issues/6213 ComposeFiles []string `yaml:"-" json:"-"` Environment map[string]string `yaml:"-" json:"-"` // DisabledServices track services which have been disable as profile is not active DisabledServices Services `yaml:"-" json:"-"` } // ServiceNames return names for all services in this Compose config func (p Project) ServiceNames() []string { names := []string{} for _, s := range p.Services { names = append(names, s.Name) } sort.Strings(names) return names } // VolumeNames return names for all volumes in this Compose config func (p Project) VolumeNames() []string { names := []string{} for k := range p.Volumes { names = append(names, k) } sort.Strings(names) return names } // NetworkNames return names for all volumes in this Compose config func (p Project) NetworkNames() []string { names := []string{} for k := range p.Networks { names = append(names, k) } sort.Strings(names) return names } // SecretNames return names for all secrets in this Compose config func (p Project) SecretNames() []string { names := []string{} for k := range p.Secrets { names = append(names, k) } sort.Strings(names) return names } // ConfigNames return names for all configs in this Compose config func (p Project) ConfigNames() []string { names := []string{} for k := range p.Configs { names = append(names, k) } sort.Strings(names) return names } // GetServices retrieve services by names, or return all services if no name specified func (p Project) GetServices(names ...string) (Services, error) { if len(names) == 0 { return p.Services, nil } services := Services{} for _, name := range names { var serviceConfig *ServiceConfig for _, s := range p.Services { if s.Name == name { serviceConfig = &s break } } if serviceConfig == nil { return services, fmt.Errorf("no such service: %s", name) } services = append(services, *serviceConfig) } return services, nil } // GetService retrieve a specific service by name func (p Project) GetService(name string) (ServiceConfig, error) { services, err := p.GetServices(name) if err != nil { return ServiceConfig{}, err } if len(services) == 0 { return ServiceConfig{}, fmt.Errorf("no such service: %s", name) } return services[0], nil } func (p Project) AllServices() Services { var all Services all = append(all, p.Services...) all = append(all, p.DisabledServices...) return all } type ServiceFunc func(service ServiceConfig) error // WithServices run ServiceFunc on each service and dependencies in dependency order func (p Project) WithServices(names []string, fn ServiceFunc) error { return p.withServices(names, fn, map[string]bool{}) } func (p Project) withServices(names []string, fn ServiceFunc, done map[string]bool) error { services, err := p.GetServices(names...) if err != nil { return err } for _, service := range services { if done[service.Name] { continue } //KCQ done[service.Name] = true dependencies := service.GetDependencies() if len(dependencies) > 0 { err := p.withServices(dependencies, fn, done) if err != nil { return err } } if err := fn(service); err != nil { return err } } return nil } // RelativePath resolve a relative path based project's working directory func (p *Project) RelativePath(path string) string { if path[0] == '~' { home, _ := os.UserHomeDir() path = filepath.Join(home, path[1:]) } if filepath.IsAbs(path) { return path } return filepath.Join(p.WorkingDir, path) } // HasProfile return true if service has no profile declared or has at least one profile matching func (service ServiceConfig) HasProfile(profiles []string) bool { if len(service.Profiles) == 0 { return true } for _, p := range profiles { for _, sp := range service.Profiles { if sp == p { return true } } } return false } // GetProfiles retrieve the profiles implicitly enabled by explicitly targeting selected services func (s Services) GetProfiles() []string { set := map[string]struct{}{} for _, service := range s { for _, p := range service.Profiles { set[p] = struct{}{} } } var profiles []string for k := range set { profiles = append(profiles, k) } return profiles } // ApplyProfiles disables service which don't match selected profiles func (p *Project) ApplyProfiles(profiles []string) { for _, p := range profiles { if p == "*" { return } } var enabled, disabled Services for _, service := range p.Services { if service.HasProfile(profiles) { enabled = append(enabled, service) } else { disabled = append(disabled, service) } } p.Services = enabled p.DisabledServices = disabled } // WithoutUnnecessaryResources drops networks/volumes/secrets/configs that are not referenced by active services func (p *Project) WithoutUnnecessaryResources() { requiredNetworks := map[string]struct{}{} requiredVolumes := map[string]struct{}{} requiredSecrets := map[string]struct{}{} requiredConfigs := map[string]struct{}{} for _, s := range p.Services { for k := range s.Networks { requiredNetworks[k] = struct{}{} } for _, v := range s.Volumes { if v.Type != VolumeTypeVolume || v.Source == "" { continue } requiredVolumes[v.Source] = struct{}{} } for _, v := range s.Secrets { requiredSecrets[v.Source] = struct{}{} } for _, v := range s.Configs { requiredConfigs[v.Source] = struct{}{} } } networks := Networks{} for k := range requiredNetworks { networks[k] = p.Networks[k] } p.Networks = networks volumes := Volumes{} for k := range requiredVolumes { volumes[k] = p.Volumes[k] } p.Volumes = volumes secrets := Secrets{} for k := range requiredSecrets { secrets[k] = p.Secrets[k] } p.Secrets = secrets configs := Configs{} for k := range requiredConfigs { configs[k] = p.Configs[k] } p.Configs = configs } // ForServices restrict the project model to a subset of services func (p *Project) ForServices(names []string) error { if len(names) == 0 { // All services return nil } set := map[string]struct{}{} err := p.WithServices(names, func(service ServiceConfig) error { set[service.Name] = struct{}{} return nil }) if err != nil { return err } // Disable all services which are not explicit target or dependencies var enabled Services for _, s := range p.Services { if _, ok := set[s.Name]; ok { enabled = append(enabled, s) } else { p.DisabledServices = append(p.DisabledServices, s) } } p.Services = enabled return nil } // ResolveImages updates services images to include digest computed by a resolver function func (p *Project) ResolveImages(resolver func(named reference.Named) (digest.Digest, error)) error { eg := errgroup.Group{} for i, s := range p.Services { idx := i service := s if service.Image == "" { continue } eg.Go(func() error { named, err := reference.ParseDockerRef(service.Image) if err != nil { return err } if _, ok := named.(reference.Canonical); !ok { // image is named but not digested reference digest, err := resolver(named) if err != nil { return err } named, err = reference.WithDigest(named, digest) if err != nil { return err } } service.Image = named.String() p.Services[idx] = service return nil }) } return eg.Wait() } ================================================ FILE: pkg/third_party/compose-go/types/project_test.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package types import ( _ "crypto/sha256" "testing" "github.com/distribution/distribution/v3/reference" "github.com/opencontainers/go-digest" "gotest.tools/v3/assert" ) func Test_ApplyProfiles(t *testing.T) { p := makeProject() p.ApplyProfiles([]string{"foo"}) assert.Equal(t, len(p.Services), 2) assert.Equal(t, p.Services[0].Name, "service_1") assert.Equal(t, p.Services[1].Name, "service_2") assert.Equal(t, len(p.DisabledServices), 1) assert.Equal(t, p.DisabledServices[0].Name, "service_3") } func Test_WithoutUnnecessaryResources(t *testing.T) { p := makeProject() p.Networks["unused"] = NetworkConfig{} p.Volumes["unused"] = VolumeConfig{} p.Secrets["unused"] = SecretConfig{} p.Configs["unused"] = ConfigObjConfig{} p.WithoutUnnecessaryResources() if _, ok := p.Networks["unused"]; ok { t.Fail() } if _, ok := p.Volumes["unused"]; ok { t.Fail() } if _, ok := p.Secrets["unused"]; ok { t.Fail() } if _, ok := p.Configs["unused"]; ok { t.Fail() } } func Test_NoProfiles(t *testing.T) { p := makeProject() p.ApplyProfiles(nil) assert.Equal(t, len(p.Services), 1) assert.Equal(t, len(p.DisabledServices), 2) assert.Equal(t, p.Services[0].Name, "service_1") } func Test_ServiceProfiles(t *testing.T) { p := makeProject() services, err := p.GetServices("service_1", "service_2") assert.NilError(t, err) profiles := services.GetProfiles() assert.Equal(t, len(profiles), 1) assert.Equal(t, profiles[0], "foo") } func Test_ForServices(t *testing.T) { p := makeProject() err := p.ForServices([]string{"service_2"}) assert.NilError(t, err) assert.Equal(t, len(p.DisabledServices), 1) assert.Equal(t, p.DisabledServices[0].Name, "service_3") } func makeProject() Project { return Project{ Services: append(Services{}, ServiceConfig{ Name: "service_1", }, ServiceConfig{ Name: "service_2", Profiles: []string{"foo"}, DependsOn: map[string]ServiceDependency{"service_1": {}}, }, ServiceConfig{ Name: "service_3", Profiles: []string{"bar"}, }), Networks: Networks{}, Volumes: Volumes{}, Secrets: Secrets{}, Configs: Configs{}, } } func Test_ResolveImages(t *testing.T) { p := makeProject() resolver := func(named reference.Named) (digest.Digest, error) { return "sha256:1234567890123456789012345678901234567890123456789012345678901234", nil } tests := []struct { image string resolved string }{ { image: "com.acme/long:tag", resolved: "com.acme/long:tag@sha256:1234567890123456789012345678901234567890123456789012345678901234", }, { image: "com.acme/long", resolved: "com.acme/long:latest@sha256:1234567890123456789012345678901234567890123456789012345678901234", }, { image: "short", resolved: "docker.io/library/short:latest@sha256:1234567890123456789012345678901234567890123456789012345678901234", }, { image: "com.acme/digested:tag@sha256:1234567890123456789012345678901234567890123456789012345678901234", resolved: "com.acme/digested@sha256:1234567890123456789012345678901234567890123456789012345678901234", }, { image: "com.acme/digested@sha256:1234567890123456789012345678901234567890123456789012345678901234", resolved: "com.acme/digested@sha256:1234567890123456789012345678901234567890123456789012345678901234", }, } for _, test := range tests { p.Services[0].Image = test.image err := p.ResolveImages(resolver) assert.NilError(t, err) assert.Equal(t, p.Services[0].Image, test.resolved) } } ================================================ FILE: pkg/third_party/compose-go/types/types.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package types import ( "encoding/json" "fmt" "sort" "strings" "time" "github.com/docker/go-connections/nat" ) // Duration is a thin wrapper around time.Duration with improved JSON marshalling type Duration time.Duration func (d Duration) String() string { return time.Duration(d).String() } // ConvertDurationPtr converts a typedefined Duration pointer to a time.Duration pointer with the same value. func ConvertDurationPtr(d *Duration) *time.Duration { if d == nil { return nil } res := time.Duration(*d) return &res } // MarshalJSON makes Duration implement json.Marshaler func (d Duration) MarshalJSON() ([]byte, error) { return json.Marshal(d.String()) } // MarshalYAML makes Duration implement yaml.Marshaler func (d Duration) MarshalYAML() (interface{}, error) { return d.String(), nil } func (d *Duration) UnmarshalJSON(b []byte) error { s := strings.Trim(string(b), "\"") timeDuration, err := time.ParseDuration(s) if err != nil { return err } *d = Duration(timeDuration) return nil } // Services is a list of ServiceConfig type Services []ServiceConfig // MarshalYAML makes Services implement yaml.Marshaller func (s Services) MarshalYAML() (interface{}, error) { services := map[string]ServiceConfig{} for _, service := range s { services[service.Name] = service } return services, nil } // MarshalJSON makes Services implement json.Marshaler func (s Services) MarshalJSON() ([]byte, error) { data, err := s.MarshalYAML() if err != nil { return nil, err } return json.MarshalIndent(data, "", " ") } // ServiceConfig is the configuration of one service type ServiceConfig struct { Name string `yaml:"-" json:"-"` Profiles []string `mapstructure:"profiles" yaml:"profiles,omitempty" json:"profiles,omitempty"` Build *BuildConfig `yaml:",omitempty" json:"build,omitempty"` BlkioConfig *BlkioConfig `yaml:",omitempty" json:"blkio_config,omitempty"` CapAdd []string `mapstructure:"cap_add" yaml:"cap_add,omitempty" json:"cap_add,omitempty"` CapDrop []string `mapstructure:"cap_drop" yaml:"cap_drop,omitempty" json:"cap_drop,omitempty"` CgroupParent string `mapstructure:"cgroup_parent" yaml:"cgroup_parent,omitempty" json:"cgroup_parent,omitempty"` CPUCount int64 `mapstructure:"cpu_count" yaml:"cpu_count,omitempty" json:"cpu_count,omitempty"` CPUPercent float32 `mapstructure:"cpu_percent" yaml:"cpu_percent,omitempty" json:"cpu_percent,omitempty"` CPUPeriod int64 `mapstructure:"cpu_period" yaml:"cpu_period,omitempty" json:"cpu_period,omitempty"` CPUQuota int64 `mapstructure:"cpu_quota" yaml:"cpu_quota,omitempty" json:"cpu_quota,omitempty"` CPURTPeriod int64 `mapstructure:"cpu_rt_period" yaml:"cpu_rt_period,omitempty" json:"cpu_rt_period,omitempty"` CPURTRuntime int64 `mapstructure:"cpu_rt_runtime" yaml:"cpu_rt_runtime,omitempty" json:"cpu_rt_runtime,omitempty"` CPUS float32 `mapstructure:"cpus" yaml:"cpus,omitempty" json:"cpus,omitempty"` CPUSet string `mapstructure:"cpuset" yaml:"cpuset,omitempty" json:"cpuset,omitempty"` CPUShares int64 `mapstructure:"cpu_shares" yaml:"cpu_shares,omitempty" json:"cpu_shares,omitempty"` Command ShellCommand `yaml:",omitempty" json:"command,omitempty"` Configs []ServiceConfigObjConfig `yaml:",omitempty" json:"configs,omitempty"` ContainerName string `mapstructure:"container_name" yaml:"container_name,omitempty" json:"container_name,omitempty"` CredentialSpec *CredentialSpecConfig `mapstructure:"credential_spec" yaml:"credential_spec,omitempty" json:"credential_spec,omitempty"` DependsOn DependsOnConfig `mapstructure:"depends_on" yaml:"depends_on,omitempty" json:"depends_on,omitempty"` Deploy *DeployConfig `yaml:",omitempty" json:"deploy,omitempty"` Devices []string `yaml:",omitempty" json:"devices,omitempty"` DNS StringList `yaml:",omitempty" json:"dns,omitempty"` DNSOpts []string `mapstructure:"dns_opt" yaml:"dns_opt,omitempty" json:"dns_opt,omitempty"` DNSSearch StringList `mapstructure:"dns_search" yaml:"dns_search,omitempty" json:"dns_search,omitempty"` Dockerfile string `yaml:"dockerfile,omitempty" json:"dockerfile,omitempty"` DomainName string `mapstructure:"domainname" yaml:"domainname,omitempty" json:"domainname,omitempty"` Entrypoint ShellCommand `yaml:",omitempty" json:"entrypoint,omitempty"` Environment MappingWithEquals `yaml:",omitempty" json:"environment,omitempty"` EnvFile StringList `mapstructure:"env_file" yaml:"env_file,omitempty" json:"env_file,omitempty"` Expose StringOrNumberList `yaml:",omitempty" json:"expose,omitempty"` Extends ExtendsConfig `yaml:"extends,omitempty" json:"extends,omitempty"` ExternalLinks []string `mapstructure:"external_links" yaml:"external_links,omitempty" json:"external_links,omitempty"` ExtraHosts HostsList `mapstructure:"extra_hosts" yaml:"extra_hosts,omitempty" json:"extra_hosts,omitempty"` GroupAdd []string `mapstructure:"group_app" yaml:"group_add,omitempty" json:"group_add,omitempty"` Hostname string `yaml:",omitempty" json:"hostname,omitempty"` HealthCheck *HealthCheckConfig `yaml:",omitempty" json:"healthcheck,omitempty"` Image string `yaml:",omitempty" json:"image,omitempty"` Init *bool `yaml:",omitempty" json:"init,omitempty"` Ipc string `yaml:",omitempty" json:"ipc,omitempty"` Isolation string `mapstructure:"isolation" yaml:"isolation,omitempty" json:"isolation,omitempty"` Labels Labels `yaml:",omitempty" json:"labels,omitempty"` Links []string `yaml:",omitempty" json:"links,omitempty"` Logging *LoggingConfig `yaml:",omitempty" json:"logging,omitempty"` LogDriver string `mapstructure:"log_driver" yaml:"log_driver,omitempty" json:"log_driver,omitempty"` LogOpt map[string]string `mapstructure:"log_opt" yaml:"log_opt,omitempty" json:"log_opt,omitempty"` MemLimit UnitBytes `mapstructure:"mem_limit" yaml:"mem_limit,omitempty" json:"mem_limit,omitempty"` MemReservation UnitBytes `mapstructure:"mem_reservation" yaml:"mem_reservation,omitempty" json:"mem_reservation,omitempty"` MemSwapLimit UnitBytes `mapstructure:"memswap_limit" yaml:"memswap_limit,omitempty" json:"memswap_limit,omitempty"` MemSwappiness UnitBytes `mapstructure:"mem_swappiness" yaml:"mem_swappiness,omitempty" json:"mem_swappiness,omitempty"` MacAddress string `mapstructure:"mac_address" yaml:"mac_address,omitempty" json:"mac_address,omitempty"` Net string `yaml:"net,omitempty" json:"net,omitempty"` NetworkMode string `mapstructure:"network_mode" yaml:"network_mode,omitempty" json:"network_mode,omitempty"` Networks map[string]*ServiceNetworkConfig `yaml:",omitempty" json:"networks,omitempty"` OomKillDisable bool `mapstructure:"oom_kill_disable" yaml:"oom_kill_disable,omitempty" json:"oom_kill_disable,omitempty"` OomScoreAdj int64 `mapstructure:"oom_score_adj" yaml:"oom_score_adj,omitempty" json:"oom_score_adj,omitempty"` Pid string `yaml:",omitempty" json:"pid,omitempty"` PidsLimit int64 `mapstructure:"pids_limit" yaml:"pids_limit,omitempty" json:"pids_limit,omitempty"` Platform string `yaml:",omitempty" json:"platform,omitempty"` Ports []ServicePortConfig `yaml:",omitempty" json:"ports,omitempty"` Privileged bool `yaml:",omitempty" json:"privileged,omitempty"` PullPolicy string `mapstructure:"pull_policy" yaml:"pull_policy,omitempty" json:"pull_policy,omitempty"` ReadOnly bool `mapstructure:"read_only" yaml:"read_only,omitempty" json:"read_only,omitempty"` Restart string `yaml:",omitempty" json:"restart,omitempty"` Runtime string `yaml:",omitempty" json:"runtime,omitempty"` Scale int `yaml:"-" json:"-"` Secrets []ServiceSecretConfig `yaml:",omitempty" json:"secrets,omitempty"` SecurityOpt []string `mapstructure:"security_opt" yaml:"security_opt,omitempty" json:"security_opt,omitempty"` ShmSize UnitBytes `mapstructure:"shm_size" yaml:"shm_size,omitempty" json:"shm_size,omitempty"` StdinOpen bool `mapstructure:"stdin_open" yaml:"stdin_open,omitempty" json:"stdin_open,omitempty"` StopGracePeriod *Duration `mapstructure:"stop_grace_period" yaml:"stop_grace_period,omitempty" json:"stop_grace_period,omitempty"` StopSignal string `mapstructure:"stop_signal" yaml:"stop_signal,omitempty" json:"stop_signal,omitempty"` Sysctls Mapping `yaml:",omitempty" json:"sysctls,omitempty"` Tmpfs StringList `yaml:",omitempty" json:"tmpfs,omitempty"` Tty bool `mapstructure:"tty" yaml:"tty,omitempty" json:"tty,omitempty"` Ulimits map[string]*UlimitsConfig `yaml:",omitempty" json:"ulimits,omitempty"` User string `yaml:",omitempty" json:"user,omitempty"` UserNSMode string `mapstructure:"userns_mode" yaml:"userns_mode,omitempty" json:"userns_mode,omitempty"` Uts string `yaml:"uts,omitempty" json:"uts,omitempty"` VolumeDriver string `mapstructure:"volume_driver" yaml:"volume_driver,omitempty" json:"volume_driver,omitempty"` Volumes []ServiceVolumeConfig `yaml:",omitempty" json:"volumes,omitempty"` VolumesFrom []string `mapstructure:"volumes_from" yaml:"volumes_from,omitempty" json:"volumes_from,omitempty"` WorkingDir string `mapstructure:"working_dir" yaml:"working_dir,omitempty" json:"working_dir,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // NetworksByPriority return the service networks IDs sorted according to Priority func (s *ServiceConfig) NetworksByPriority() []string { type key struct { name string priority int } var keys []key for k, v := range s.Networks { priority := 0 if v != nil { priority = v.Priority } keys = append(keys, key{ name: k, priority: priority, }) } sort.Slice(keys, func(i, j int) bool { return keys[i].priority > keys[j].priority }) var sorted []string for _, k := range keys { sorted = append(sorted, k.name) } return sorted } const ( //PullPolicyAlways always pull images PullPolicyAlways = "always" //PullPolicyNever never pull images PullPolicyNever = "never" //PullPolicyIfNotPresent pull missing images PullPolicyIfNotPresent = "if_not_present" //PullPolicyIfNotPresent pull missing images PullPolicyMissing = "missing" //PullPolicyBuild force building images PullPolicyBuild = "build" ) const ( //RestartPolicyAlways always restart the container if it stops RestartPolicyAlways = "always" //RestartPolicyOnFailure restart the container if it exits due to an error RestartPolicyOnFailure = "on-failure" //RestartPolicyNo do not automatically restart the container RestartPolicyNo = "no" //RestartPolicyUnlessStopped always restart the container unless the container is stopped (manually or otherwise) RestartPolicyUnlessStopped = "unless-stopped" ) const ( // ServicePrefix is the prefix for references pointing to a service ServicePrefix = "service:" // ContainerPrefix is the prefix for references pointing to a container ContainerPrefix = "container:" // NetworkModeServicePrefix is the prefix for network_mode pointing to a service // Deprecated prefer ServicePrefix NetworkModeServicePrefix = ServicePrefix // NetworkModeContainerPrefix is the prefix for network_mode pointing to a container // Deprecated prefer ContainerPrefix NetworkModeContainerPrefix = ContainerPrefix ) // GetDependencies retrieve all services this service depends on func (s ServiceConfig) GetDependencies() []string { dependencies := make(set) for dependency := range s.DependsOn { dependencies.append(dependency) } for _, link := range s.Links { parts := strings.Split(link, ":") if len(parts) == 2 { dependencies.append(parts[0]) } else { dependencies.append(link) } } if strings.HasPrefix(s.NetworkMode, ServicePrefix) { dependencies.append(s.NetworkMode[len(ServicePrefix):]) } if strings.HasPrefix(s.Ipc, ServicePrefix) { dependencies.append(s.Ipc[len(ServicePrefix):]) } if strings.HasPrefix(s.Pid, ServicePrefix) { dependencies.append(s.Pid[len(ServicePrefix):]) } for _, vol := range s.VolumesFrom { if !strings.HasPrefix(s.Pid, ContainerPrefix) { dependencies.append(vol) } } return dependencies.toSlice() } type set map[string]struct{} func (s set) append(strings ...string) { for _, str := range strings { s[str] = struct{}{} } } func (s set) toSlice() []string { slice := make([]string, 0, len(s)) for v := range s { slice = append(slice, v) } return slice } // BuildConfig is a type for build type BuildConfig struct { Context string `yaml:",omitempty" json:"context,omitempty"` Dockerfile string `yaml:",omitempty" json:"dockerfile,omitempty"` Args MappingWithEquals `yaml:",omitempty" json:"args,omitempty"` Labels Labels `yaml:",omitempty" json:"labels,omitempty"` CacheFrom StringList `mapstructure:"cache_from" yaml:"cache_from,omitempty" json:"cache_from,omitempty"` ExtraHosts HostsList `mapstructure:"extra_hosts" yaml:"extra_hosts,omitempty" json:"extra_hosts,omitempty"` Isolation string `yaml:",omitempty" json:"isolation,omitempty"` Network string `yaml:",omitempty" json:"network,omitempty"` Target string `yaml:",omitempty" json:"target,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // BlkioConfig define blkio config type BlkioConfig struct { Weight uint16 `yaml:",omitempty" json:"weight,omitempty"` WeightDevice []WeightDevice `yaml:",omitempty" json:"weight_device,omitempty"` DeviceReadBps []ThrottleDevice `yaml:",omitempty" json:"device_read_bps,omitempty"` DeviceReadIOps []ThrottleDevice `yaml:",omitempty" json:"device_read_iops,omitempty"` DeviceWriteBps []ThrottleDevice `yaml:",omitempty" json:"device_write_bps,omitempty"` DeviceWriteIOps []ThrottleDevice `yaml:",omitempty" json:"device_write_iops,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // WeightDevice is a structure that holds device:weight pair type WeightDevice struct { Path string Weight uint16 Extensions map[string]interface{} `yaml:",inline" json:"-"` } // ThrottleDevice is a structure that holds device:rate_per_second pair type ThrottleDevice struct { Path string Rate uint64 Extensions map[string]interface{} `yaml:",inline" json:"-"` } // ShellCommand is a string or list of string args type ShellCommand []string // StringList is a type for fields that can be a string or list of strings type StringList []string // StringOrNumberList is a type for fields that can be a list of strings or // numbers type StringOrNumberList []string // MappingWithEquals is a mapping type that can be converted from a list of // key[=value] strings. // For the key with an empty value (`key=`), the mapped value is set to a pointer to `""`. // For the key without value (`key`), the mapped value is set to nil. type MappingWithEquals map[string]*string // NewMappingWithEquals build a new Mapping from a set of KEY=VALUE strings func NewMappingWithEquals(values []string) MappingWithEquals { mapping := MappingWithEquals{} for _, env := range values { tokens := strings.SplitN(env, "=", 2) if len(tokens) > 1 { mapping[tokens[0]] = &tokens[1] } else { mapping[env] = nil } } return mapping } // OverrideBy update MappingWithEquals with values from another MappingWithEquals func (e MappingWithEquals) OverrideBy(other MappingWithEquals) MappingWithEquals { for k, v := range other { e[k] = v } return e } // Resolve update a MappingWithEquals for keys without value (`key`, but not `key=`) func (e MappingWithEquals) Resolve(lookupFn func(string) (string, bool)) MappingWithEquals { for k, v := range e { if v == nil { if value, ok := lookupFn(k); ok { e[k] = &value } } } return e } // RemoveEmpty excludes keys that are not associated with a value func (e MappingWithEquals) RemoveEmpty() MappingWithEquals { for k, v := range e { if v == nil { delete(e, k) } } return e } // Mapping is a mapping type that can be converted from a list of // key[=value] strings. // For the key with an empty value (`key=`), or key without value (`key`), the // mapped value is set to an empty string `""`. type Mapping map[string]string // NewMapping build a new Mapping from a set of KEY=VALUE strings func NewMapping(values []string) Mapping { mapping := Mapping{} for _, value := range values { parts := strings.SplitN(value, "=", 2) key := parts[0] switch { case len(parts) == 1: mapping[key] = "" default: mapping[key] = parts[1] } } return mapping } // Labels is a mapping type for labels type Labels map[string]string func (l Labels) Add(key, value string) Labels { if l == nil { l = Labels{} } l[key] = value return l } // MappingWithColon is a mapping type that can be converted from a list of // 'key: value' strings type MappingWithColon map[string]string // HostsList is a list of colon-separated host-ip mappings type HostsList []string // LoggingConfig the logging configuration for a service type LoggingConfig struct { Driver string `yaml:",omitempty" json:"driver,omitempty"` Options map[string]string `yaml:",omitempty" json:"options,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // DeployConfig the deployment configuration for a service type DeployConfig struct { Mode string `yaml:",omitempty" json:"mode,omitempty"` Replicas *uint64 `yaml:",omitempty" json:"replicas,omitempty"` Labels Labels `yaml:",omitempty" json:"labels,omitempty"` UpdateConfig *UpdateConfig `mapstructure:"update_config" yaml:"update_config,omitempty" json:"update_config,omitempty"` RollbackConfig *UpdateConfig `mapstructure:"rollback_config" yaml:"rollback_config,omitempty" json:"rollback_config,omitempty"` Resources Resources `yaml:",omitempty" json:"resources,omitempty"` RestartPolicy *RestartPolicy `mapstructure:"restart_policy" yaml:"restart_policy,omitempty" json:"restart_policy,omitempty"` Placement Placement `yaml:",omitempty" json:"placement,omitempty"` EndpointMode string `mapstructure:"endpoint_mode" yaml:"endpoint_mode,omitempty" json:"endpoint_mode,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // HealthCheckConfig the healthcheck configuration for a service type HealthCheckConfig struct { Test HealthCheckTest `yaml:",omitempty" json:"test,omitempty"` Timeout *Duration `yaml:",omitempty" json:"timeout,omitempty"` Interval *Duration `yaml:",omitempty" json:"interval,omitempty"` Retries *uint64 `yaml:",omitempty" json:"retries,omitempty"` StartPeriod *Duration `mapstructure:"start_period" yaml:"start_period,omitempty" json:"start_period,omitempty"` Disable bool `yaml:",omitempty" json:"disable,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // HealthCheckTest is the command run to test the health of a service type HealthCheckTest []string // UpdateConfig the service update configuration type UpdateConfig struct { Parallelism *uint64 `yaml:",omitempty" json:"parallelism,omitempty"` Delay Duration `yaml:",omitempty" json:"delay,omitempty"` FailureAction string `mapstructure:"failure_action" yaml:"failure_action,omitempty" json:"failure_action,omitempty"` Monitor Duration `yaml:",omitempty" json:"monitor,omitempty"` MaxFailureRatio float32 `mapstructure:"max_failure_ratio" yaml:"max_failure_ratio,omitempty" json:"max_failure_ratio,omitempty"` Order string `yaml:",omitempty" json:"order,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // Resources the resource limits and reservations type Resources struct { Limits *Resource `yaml:",omitempty" json:"limits,omitempty"` Reservations *Resource `yaml:",omitempty" json:"reservations,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // Resource is a resource to be limited or reserved type Resource struct { // TODO: types to convert from units and ratios NanoCPUs string `mapstructure:"cpus" yaml:"cpus,omitempty" json:"cpus,omitempty"` MemoryBytes UnitBytes `mapstructure:"memory" yaml:"memory,omitempty" json:"memory,omitempty"` Devices []DeviceRequest `mapstructure:"devices" yaml:"devices,omitempty" json:"devices,omitempty"` GenericResources []GenericResource `mapstructure:"generic_resources" yaml:"generic_resources,omitempty" json:"generic_resources,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } type DeviceRequest struct { Capabilities []string `mapstructure:"capabilities" yaml:"capabilities,omitempty" json:"capabilities,omitempty"` Driver string `mapstructure:"driver" yaml:"driver,omitempty" json:"driver,omitempty"` Count int64 `mapstructure:"count" yaml:"count,omitempty" json:"count,omitempty"` IDs []string `mapstructure:"device_ids" yaml:"device_ids,omitempty" json:"device_ids,omitempty"` } // GenericResource represents a "user defined" resource which can // only be an integer (e.g: SSD=3) for a service type GenericResource struct { DiscreteResourceSpec *DiscreteGenericResource `mapstructure:"discrete_resource_spec" yaml:"discrete_resource_spec,omitempty" json:"discrete_resource_spec,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // DiscreteGenericResource represents a "user defined" resource which is defined // as an integer // "Kind" is used to describe the Kind of a resource (e.g: "GPU", "FPGA", "SSD", ...) // Value is used to count the resource (SSD=5, HDD=3, ...) type DiscreteGenericResource struct { Kind string `json:"kind"` Value int64 `json:"value"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // UnitBytes is the bytes type type UnitBytes int64 // MarshalYAML makes UnitBytes implement yaml.Marshaller func (u UnitBytes) MarshalYAML() (interface{}, error) { return fmt.Sprintf("%d", u), nil } // MarshalJSON makes UnitBytes implement json.Marshaler func (u UnitBytes) MarshalJSON() ([]byte, error) { return []byte(fmt.Sprintf(`"%d"`, u)), nil } // RestartPolicy the service restart policy type RestartPolicy struct { Condition string `yaml:",omitempty" json:"condition,omitempty"` Delay *Duration `yaml:",omitempty" json:"delay,omitempty"` MaxAttempts *uint64 `mapstructure:"max_attempts" yaml:"max_attempts,omitempty" json:"max_attempts,omitempty"` Window *Duration `yaml:",omitempty" json:"window,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // Placement constraints for the service type Placement struct { Constraints []string `yaml:",omitempty" json:"constraints,omitempty"` Preferences []PlacementPreferences `yaml:",omitempty" json:"preferences,omitempty"` MaxReplicas uint64 `mapstructure:"max_replicas_per_node" yaml:"max_replicas_per_node,omitempty" json:"max_replicas_per_node,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // PlacementPreferences is the preferences for a service placement type PlacementPreferences struct { Spread string `yaml:",omitempty" json:"spread,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // ServiceNetworkConfig is the network configuration for a service type ServiceNetworkConfig struct { Priority int `yaml:",omitempty" json:"priotirt,omitempty"` Aliases []string `yaml:",omitempty" json:"aliases,omitempty"` Ipv4Address string `mapstructure:"ipv4_address" yaml:"ipv4_address,omitempty" json:"ipv4_address,omitempty"` Ipv6Address string `mapstructure:"ipv6_address" yaml:"ipv6_address,omitempty" json:"ipv6_address,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // ServicePortConfig is the port configuration for a service type ServicePortConfig struct { Mode string `yaml:",omitempty" json:"mode,omitempty"` HostIP string `mapstructure:"host_ip" yaml:"host_ip,omitempty" json:"host_ip,omitempty"` Target uint32 `yaml:",omitempty" json:"target,omitempty"` Published uint32 `yaml:",omitempty" json:"published,omitempty"` Protocol string `yaml:",omitempty" json:"protocol,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // ParsePortConfig parse short syntax for service port configuration func ParsePortConfig(value string) ([]ServicePortConfig, error) { var portConfigs []ServicePortConfig ports, portBindings, err := nat.ParsePortSpecs([]string{value}) if err != nil { return nil, err } // We need to sort the key of the ports to make sure it is consistent keys := []string{} for port := range ports { keys = append(keys, string(port)) } sort.Strings(keys) for _, key := range keys { port := nat.Port(key) converted, err := convertPortToPortConfig(port, portBindings) if err != nil { return nil, err } portConfigs = append(portConfigs, converted...) } return portConfigs, nil } func convertPortToPortConfig(port nat.Port, portBindings map[nat.Port][]nat.PortBinding) ([]ServicePortConfig, error) { portConfigs := []ServicePortConfig{} for _, binding := range portBindings[port] { startHostPort, endHostPort, err := nat.ParsePortRange(binding.HostPort) if err != nil && binding.HostPort != "" { return nil, fmt.Errorf("invalid hostport binding (%s) for port (%s)", binding.HostPort, port.Port()) } for i := startHostPort; i <= endHostPort; i++ { portConfigs = append(portConfigs, ServicePortConfig{ HostIP: binding.HostIP, Protocol: strings.ToLower(port.Proto()), Target: uint32(port.Int()), Published: uint32(i), Mode: "ingress", }) } } return portConfigs, nil } // ServiceVolumeConfig are references to a volume used by a service type ServiceVolumeConfig struct { Type string `yaml:",omitempty" json:"type,omitempty"` Source string `yaml:",omitempty" json:"source,omitempty"` Target string `yaml:",omitempty" json:"target,omitempty"` ReadOnly bool `mapstructure:"read_only" yaml:"read_only,omitempty" json:"read_only,omitempty"` Consistency string `yaml:",omitempty" json:"consistency,omitempty"` Bind *ServiceVolumeBind `yaml:",omitempty" json:"bind,omitempty"` Volume *ServiceVolumeVolume `yaml:",omitempty" json:"volume,omitempty"` Tmpfs *ServiceVolumeTmpfs `yaml:",omitempty" json:"tmpfs,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } const ( // TypeBind is the type for mounting host dir VolumeTypeBind = "bind" // TypeVolume is the type for remote storage volumes VolumeTypeVolume = "volume" // TypeTmpfs is the type for mounting tmpfs VolumeTypeTmpfs = "tmpfs" // TypeNamedPipe is the type for mounting Windows named pipes VolumeTypeNamedPipe = "npipe" ) // ServiceVolumeBind are options for a service volume of type bind type ServiceVolumeBind struct { Propagation string `yaml:",omitempty" json:"propagation,omitempty"` CreateHostPath bool `mapstructure:"create_host_path" yaml:"create_host_path,omitempty" json:"create_host_path,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // Propagation represents the propagation of a mount. const ( // PropagationRPrivate RPRIVATE PropagationRPrivate string = "rprivate" // PropagationPrivate PRIVATE PropagationPrivate string = "private" // PropagationRShared RSHARED PropagationRShared string = "rshared" // PropagationShared SHARED PropagationShared string = "shared" // PropagationRSlave RSLAVE PropagationRSlave string = "rslave" // PropagationSlave SLAVE PropagationSlave string = "slave" ) // ServiceVolumeVolume are options for a service volume of type volume type ServiceVolumeVolume struct { NoCopy bool `mapstructure:"nocopy" yaml:"nocopy,omitempty" json:"nocopy,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // ServiceVolumeTmpfs are options for a service volume of type tmpfs type ServiceVolumeTmpfs struct { Size int64 `yaml:",omitempty" json:"size,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // FileReferenceConfig for a reference to a swarm file object type FileReferenceConfig struct { Source string `yaml:",omitempty" json:"source,omitempty"` Target string `yaml:",omitempty" json:"target,omitempty"` UID string `yaml:",omitempty" json:"uid,omitempty"` GID string `yaml:",omitempty" json:"gid,omitempty"` Mode *uint32 `yaml:",omitempty" json:"mode,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // ServiceConfigObjConfig is the config obj configuration for a service type ServiceConfigObjConfig FileReferenceConfig // ServiceSecretConfig is the secret configuration for a service type ServiceSecretConfig FileReferenceConfig // UlimitsConfig the ulimit configuration type UlimitsConfig struct { Single int `yaml:",omitempty" json:"single,omitempty"` Soft int `yaml:",omitempty" json:"soft,omitempty"` Hard int `yaml:",omitempty" json:"hard,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // MarshalYAML makes UlimitsConfig implement yaml.Marshaller func (u *UlimitsConfig) MarshalYAML() (interface{}, error) { if u.Single != 0 { return u.Single, nil } return u, nil } // MarshalJSON makes UlimitsConfig implement json.Marshaller func (u *UlimitsConfig) MarshalJSON() ([]byte, error) { if u.Single != 0 { return json.Marshal(u.Single) } // Pass as a value to avoid re-entering this method and use the default implementation return json.Marshal(*u) } // NetworkConfig for a network type NetworkConfig struct { Name string `yaml:",omitempty" json:"name,omitempty"` Driver string `yaml:",omitempty" json:"driver,omitempty"` DriverOpts map[string]string `mapstructure:"driver_opts" yaml:"driver_opts,omitempty" json:"driver_opts,omitempty"` Ipam IPAMConfig `yaml:",omitempty" json:"ipam,omitempty"` External External `yaml:",omitempty" json:"external,omitempty"` Internal bool `yaml:",omitempty" json:"internal,omitempty"` Attachable bool `yaml:",omitempty" json:"attachable,omitempty"` Labels Labels `yaml:",omitempty" json:"labels,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // IPAMConfig for a network type IPAMConfig struct { Driver string `yaml:",omitempty" json:"driver,omitempty"` Config []*IPAMPool `yaml:",omitempty" json:"config,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // IPAMPool for a network type IPAMPool struct { Subnet string `yaml:",omitempty" json:"subnet,omitempty"` Gateway string `yaml:",omitempty" json:"gateway,omitempty"` IPRange string `mapstructure:"ip_range" yaml:"ip_range,omitempty" json:"ip_range,omitempty"` AuxiliaryAddresses map[string]string `mapstructure:"aux_addresses" yaml:"aux_addresses,omitempty" json:"aux_addresses,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // VolumeConfig for a volume type VolumeConfig struct { Name string `yaml:",omitempty" json:"name,omitempty"` Driver string `yaml:",omitempty" json:"driver,omitempty"` DriverOpts map[string]string `mapstructure:"driver_opts" yaml:"driver_opts,omitempty" json:"driver_opts,omitempty"` External External `yaml:",omitempty" json:"external,omitempty"` Labels Labels `yaml:",omitempty" json:"labels,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // External identifies a Volume or Network as a reference to a resource that is // not managed, and should already exist. // External.name is deprecated and replaced by Volume.name type External struct { Name string `yaml:",omitempty" json:"name,omitempty"` External bool `yaml:",omitempty" json:"external,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // MarshalYAML makes External implement yaml.Marshaller func (e External) MarshalYAML() (interface{}, error) { if e.Name == "" { return e.External, nil } return External{Name: e.Name}, nil } // MarshalJSON makes External implement json.Marshaller func (e External) MarshalJSON() ([]byte, error) { if e.Name == "" { return []byte(fmt.Sprintf("%v", e.External)), nil } return []byte(fmt.Sprintf(`{"name": %q}`, e.Name)), nil } // CredentialSpecConfig for credential spec on Windows type CredentialSpecConfig struct { Config string `yaml:",omitempty" json:"config,omitempty"` // Config was added in API v1.40 File string `yaml:",omitempty" json:"file,omitempty"` Registry string `yaml:",omitempty" json:"registry,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // FileObjectConfig is a config type for a file used by a service type FileObjectConfig struct { Name string `yaml:",omitempty" json:"name,omitempty"` File string `yaml:",omitempty" json:"file,omitempty"` External External `yaml:",omitempty" json:"external,omitempty"` Labels Labels `yaml:",omitempty" json:"labels,omitempty"` Driver string `yaml:",omitempty" json:"driver,omitempty"` DriverOpts map[string]string `mapstructure:"driver_opts" yaml:"driver_opts,omitempty" json:"driver_opts,omitempty"` TemplateDriver string `mapstructure:"template_driver" yaml:"template_driver,omitempty" json:"template_driver,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } const ( // ServiceConditionCompletedSuccessfully is the type for waiting until a service has completed successfully (exit code 0). ServiceConditionCompletedSuccessfully = "service_completed_successfully" // ServiceConditionHealthy is the type for waiting until a service is healthy. ServiceConditionHealthy = "service_healthy" // ServiceConditionStarted is the type for waiting until a service has started (default). ServiceConditionStarted = "service_started" ) type DependsOnConfig map[string]ServiceDependency type ServiceDependency struct { Condition string `yaml:",omitempty" json:"condition,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } type ExtendsConfig MappingWithEquals // SecretConfig for a secret type SecretConfig FileObjectConfig // ConfigObjConfig is the config for the swarm "Config" object type ConfigObjConfig FileObjectConfig ================================================ FILE: pkg/third_party/compose-go/types/types_test.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package types import ( "testing" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) func TestParsePortConfig(t *testing.T) { testCases := []struct { value string expectedError string expected []ServicePortConfig }{ { value: "80", expected: []ServicePortConfig{ { Protocol: "tcp", Target: 80, Mode: "ingress", }, }, }, { value: "80:8080", expected: []ServicePortConfig{ { Protocol: "tcp", Target: 8080, Published: 80, Mode: "ingress", }, }, }, { value: "8080:80/tcp", expected: []ServicePortConfig{ { Protocol: "tcp", Target: 80, Published: 8080, Mode: "ingress", }, }, }, { value: "80:8080/udp", expected: []ServicePortConfig{ { Protocol: "udp", Target: 8080, Published: 80, Mode: "ingress", }, }, }, { value: "80-81:8080-8081/tcp", expected: []ServicePortConfig{ { Protocol: "tcp", Target: 8080, Published: 80, Mode: "ingress", }, { Protocol: "tcp", Target: 8081, Published: 81, Mode: "ingress", }, }, }, { value: "80-82:8080-8082/udp", expected: []ServicePortConfig{ { Protocol: "udp", Target: 8080, Published: 80, Mode: "ingress", }, { Protocol: "udp", Target: 8081, Published: 81, Mode: "ingress", }, { Protocol: "udp", Target: 8082, Published: 82, Mode: "ingress", }, }, }, { value: "80-82:8080/udp", expected: []ServicePortConfig{ { Protocol: "udp", Target: 8080, Published: 80, Mode: "ingress", }, { Protocol: "udp", Target: 8080, Published: 81, Mode: "ingress", }, { Protocol: "udp", Target: 8080, Published: 82, Mode: "ingress", }, }, }, { value: "80-80:8080/tcp", expected: []ServicePortConfig{ { Protocol: "tcp", Target: 8080, Published: 80, Mode: "ingress", }, }, }, { value: "9999999", expectedError: "Invalid containerPort: 9999999", }, { value: "80/xyz", expectedError: "Invalid proto: xyz", }, { value: "tcp", expectedError: "Invalid containerPort: tcp", }, { value: "udp", expectedError: "Invalid containerPort: udp", }, { value: "", expectedError: "No port specified: ", }, { value: "1.1.1.1:80:80", expected: []ServicePortConfig{ { HostIP: "1.1.1.1", Protocol: "tcp", Target: 80, Published: 80, Mode: "ingress", }, }, }, } for _, tc := range testCases { ports, err := ParsePortConfig(tc.value) if tc.expectedError != "" { assert.Error(t, err, tc.expectedError) continue } assert.NilError(t, err) assert.Check(t, is.Len(ports, len(tc.expected))) for _, expectedPortConfig := range tc.expected { assertContains(t, ports, expectedPortConfig) } } } func assertContains(t *testing.T, portConfigs []ServicePortConfig, expected ServicePortConfig) { var contains = false for _, portConfig := range portConfigs { if is.DeepEqual(portConfig, expected)().Success() { contains = true break } } if !contains { t.Errorf("expected %v to contain %v, did not", portConfigs, expected) } } func TestSet(t *testing.T) { s := make(set) s.append("one") s.append("two") s.append("three") s.append("two") assert.Equal(t, len(s.toSlice()), 3) } type foo struct { Bar string } func TestExtension(t *testing.T) { x := Extensions{ "foo": map[string]interface{}{ "bar": "zot", }, } var foo foo ok, err := x.Get("foo", &foo) assert.NilError(t, err) assert.Check(t, ok == true) assert.Check(t, foo.Bar == "zot") ok, err = x.Get("qiz", &foo) assert.NilError(t, err) assert.Check(t, ok == false) } func TestNewMapping(t *testing.T) { m := NewMapping([]string{ "FOO=BAR", "ZOT=", "QIX", }) mw := NewMappingWithEquals([]string{ "FOO=BAR", "ZOT=", "QIX", }) assert.Check(t, m["FOO"] == "BAR") assert.Check(t, m["ZOT"] == "") assert.Check(t, m["QIX"] == "") assert.Check(t, *mw["FOO"] == "BAR") assert.Check(t, *mw["ZOT"] == "") assert.Check(t, mw["QIX"] == nil) } func TestNetworksByPriority(t *testing.T) { s := ServiceConfig{ Networks: map[string]*ServiceNetworkConfig{ "foo": nil, "bar": { Priority: 10, }, "zot": { Priority: 100, }, "qix": { Priority: 1000, }, }, } assert.DeepEqual(t, s.NetworksByPriority(), []string{"qix", "zot", "bar", "foo"}) } ================================================ FILE: pkg/third_party/madmo/fanotify/fanotify.go ================================================ // Copyright (c) 2012, Moritz Bitsch // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // The fanotify package provides a simple fanotify api package fanotify import ( "bufio" "encoding/binary" "os" "syscall" ) // Flags used as first parameter to Initiliaze const ( /* flags used for fanotify_init() */ FAN_CLOEXEC = 0x00000001 FAN_NONBLOCK = 0x00000002 /* These are NOT bitwise flags. Both bits are used togther. */ FAN_CLASS_NOTIF = 0x00000000 FAN_CLASS_CONTENT = 0x00000004 FAN_CLASS_PRE_CONTENT = 0x00000008 FAN_ALL_CLASS_BITS = FAN_CLASS_NOTIF | FAN_CLASS_CONTENT | FAN_CLASS_PRE_CONTENT FAN_UNLIMITED_QUEUE = 0x00000010 FAN_UNLIMITED_MARKS = 0x00000020 FAN_ALL_INIT_FLAGS = FAN_CLOEXEC | FAN_NONBLOCK | FAN_ALL_CLASS_BITS | FAN_UNLIMITED_QUEUE | FAN_UNLIMITED_MARKS ) // Flags used for the Mark Method const ( /* flags used for fanotify_modify_mark() */ FAN_MARK_ADD = 0x00000001 FAN_MARK_REMOVE = 0x00000002 FAN_MARK_DONT_FOLLOW = 0x00000004 FAN_MARK_ONLYDIR = 0x00000008 FAN_MARK_MOUNT = 0x00000010 FAN_MARK_IGNORED_MASK = 0x00000020 FAN_MARK_IGNORED_SURV_MODIFY = 0x00000040 FAN_MARK_FLUSH = 0x00000080 FAN_ALL_MARK_FLAGS = FAN_MARK_ADD | FAN_MARK_REMOVE | FAN_MARK_DONT_FOLLOW | FAN_MARK_ONLYDIR | FAN_MARK_MOUNT | FAN_MARK_IGNORED_MASK | FAN_MARK_IGNORED_SURV_MODIFY | FAN_MARK_FLUSH ) // Event types const ( FAN_ACCESS = 0x00000001 /* File was accessed */ FAN_MODIFY = 0x00000002 /* File was modified */ FAN_CLOSE_WRITE = 0x00000008 /* Writtable file closed */ FAN_CLOSE_NOWRITE = 0x00000010 /* Unwrittable file closed */ FAN_OPEN = 0x00000020 /* File was opened */ FAN_Q_OVERFLOW = 0x00004000 /* Event queued overflowed */ FAN_OPEN_PERM = 0x00010000 /* File open in perm check */ FAN_ACCESS_PERM = 0x00020000 /* File accessed in perm check */ FAN_ONDIR = 0x40000000 /* event occurred against dir */ FAN_EVENT_ON_CHILD = 0x08000000 /* interested in child events */ /* helper events */ FAN_CLOSE = FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE /* close */ /* * All of the events - we build the list by hand so that we can add flags in * the future and not break backward compatibility. Apps will get only the * events that they originally wanted. Be sure to add new events here! */ FAN_ALL_EVENTS = FAN_ACCESS | FAN_MODIFY | FAN_CLOSE | FAN_OPEN /* * All events which require a permission response from userspace */ FAN_ALL_PERM_EVENTS = FAN_OPEN_PERM | FAN_ACCESS_PERM FAN_ALL_OUTGOING_EVENTS = FAN_ALL_EVENTS | FAN_ALL_PERM_EVENTS | FAN_Q_OVERFLOW FANOTIFY_METADATA_VERSION = 3 FAN_ALLOW = 0x01 FAN_DENY = 0x02 FAN_NOFD = -1 ) // Internal eventMetadata struct, used for fanotify comm type eventMetadata struct { Len uint32 Version uint8 Reserved uint8 MetadataLen uint16 Mask uint64 Fd int32 Pid int32 } // Internal response struct, used for fanotify comm type response struct { Fd int32 Response uint32 } // Event struct returned from NotifyFD.GetEvent // // The File member needs to be Closed after usage, to prevent // an Fd leak type EventMetadata struct { Len uint32 Version uint8 Reserved uint8 MetadataLen uint16 Mask uint64 File *os.File Pid int32 } // A notify handle, used by all notify functions type NotifyFD struct { f *os.File r *bufio.Reader } // Initialize the notify support func Initialize(faflags, openflags int) (*NotifyFD, error) { fd, _, errno := syscall.Syscall(syscall.SYS_FANOTIFY_INIT, uintptr(faflags), uintptr(openflags), uintptr(0)) var err error if errno != 0 { err = errno } f := os.NewFile(fd, "") return &NotifyFD{f, bufio.NewReader(f)}, err } // Get an event from the fanotify handle func (nd *NotifyFD) GetEvent() (*EventMetadata, error) { ev := &eventMetadata{} err := binary.Read(nd.r, binary.LittleEndian, ev) if err != nil { return nil, err } res := &EventMetadata{ev.Len, ev.Version, ev.Reserved, ev.MetadataLen, ev.Mask, os.NewFile(uintptr(ev.Fd), ""), ev.Pid} return res, nil } // Send an allow message back to fanotify, used for permission checks // If allow is set to true, access is granted func (nd *NotifyFD) Response(ev *EventMetadata, allow bool) error { resp := &response{Fd: int32(ev.File.Fd())} if allow { resp.Response = FAN_ALLOW } else { resp.Response = FAN_DENY } return binary.Write(nd.f, binary.LittleEndian, resp) } ================================================ FILE: pkg/third_party/madmo/fanotify/fanotify_386.go ================================================ // Copyright (c) 2012, Moritz Bitsch // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. package fanotify import ( "syscall" "unsafe" ) // Add/Delete/Modify an Fanotify mark func (nd *NotifyFD) Mark(flags int, mask uint64, dfd int, path string) error { _, _, errno := syscall.Syscall6(syscall.SYS_FANOTIFY_MARK, uintptr(nd.f.Fd()), uintptr(flags), uintptr(mask), uintptr(mask>>32), uintptr(dfd), uintptr(unsafe.Pointer(syscall.StringBytePtr(path)))) var err error if errno != 0 { err = errno } return err } ================================================ FILE: pkg/third_party/madmo/fanotify/fanotify_amd64.go ================================================ // Copyright (c) 2012, Moritz Bitsch // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. package fanotify import ( "syscall" "unsafe" ) // Add/Delete/Modify an Fanotify mark func (nd *NotifyFD) Mark(flags int, mask uint64, dfd int, path string) error { _, _, errno := syscall.Syscall6(syscall.SYS_FANOTIFY_MARK, uintptr(nd.f.Fd()), uintptr(flags), uintptr(mask), uintptr(dfd), uintptr(unsafe.Pointer(syscall.StringBytePtr(path))), 0) var err error if errno != 0 { err = errno } return err } ================================================ FILE: pkg/third_party/madmo/fanotify/fanotify_arm.go ================================================ // Copyright (c) 2012, Moritz Bitsch // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. package fanotify import ( "syscall" "unsafe" ) // Add/Delete/Modify an Fanotify mark func (nd *NotifyFD) Mark(flags int, mask uint64, dfd int, path string) error { _, _, errno := syscall.Syscall6(syscall.SYS_FANOTIFY_MARK, uintptr(nd.f.Fd()), uintptr(flags), uintptr(mask), uintptr(mask>>32), uintptr(dfd), uintptr(unsafe.Pointer(syscall.StringBytePtr(path)))) var err error if errno != 0 { err = errno } return err } ================================================ FILE: pkg/third_party/madmo/fanotify/fanotify_arm64.go ================================================ // Copyright (c) 2012, Moritz Bitsch // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. package fanotify import ( "syscall" "unsafe" ) // Add/Delete/Modify an Fanotify mark func (nd *NotifyFD) Mark(flags int, mask uint64, dfd int, path string) error { _, _, errno := syscall.Syscall6(syscall.SYS_FANOTIFY_MARK, uintptr(nd.f.Fd()), uintptr(flags), uintptr(mask), uintptr(dfd), uintptr(unsafe.Pointer(syscall.StringBytePtr(path))), 0) var err error if errno != 0 { err = errno } return err } ================================================ FILE: pkg/third_party/opencontainers/specs/seccomp.go ================================================ package specs // Seccomp represents syscall restrictions type Seccomp struct { DefaultAction Action `json:"defaultAction"` Architectures []Arch `json:"architectures,omitempty"` ArchMap []Architecture `json:"archMap,omitempty"` Syscalls []*Syscall `json:"syscalls,omitempty"` } //ArchMap - in Docker, but not in the Opencontainers spec (yet) type Architecture struct { Arch Arch `json:"architecture"` SubArches []Arch `json:"subArchitectures"` } // Arch - architecture type // Additional architectures permitted to be used for system calls // By default only the native architecture of the kernel is permitted type Arch string // Architecture types const ( ArchX86 Arch = "SCMP_ARCH_X86" ArchX86_64 Arch = "SCMP_ARCH_X86_64" ArchX32 Arch = "SCMP_ARCH_X32" ArchARM Arch = "SCMP_ARCH_ARM" ArchAARCH64 Arch = "SCMP_ARCH_AARCH64" ) // Action taken upon Seccomp rule match type Action string // Action types const ( ActKill Action = "SCMP_ACT_KILL" ActTrap Action = "SCMP_ACT_TRAP" ActErrno Action = "SCMP_ACT_ERRNO" ActTrace Action = "SCMP_ACT_TRACE" ActAllow Action = "SCMP_ACT_ALLOW" ) // Operator used to match syscall arguments in Seccomp type Operator string // Operator types const ( OpNotEqual Operator = "SCMP_CMP_NE" OpLessThan Operator = "SCMP_CMP_LT" OpLessEqual Operator = "SCMP_CMP_LE" OpEqualTo Operator = "SCMP_CMP_EQ" OpGreaterEqual Operator = "SCMP_CMP_GE" OpGreaterThan Operator = "SCMP_CMP_GT" OpMaskedEqual Operator = "SCMP_CMP_MASKED_EQ" ) // Arg used for matching specific syscall arguments in Seccomp type Arg struct { Index uint `json:"index"` Value uint64 `json:"value"` ValueTwo uint64 `json:"valueTwo,omitempty"` Op Operator `json:"op"` } // Syscall is used to match a syscall in Seccomp type Syscall struct { Name string `json:"name,omitempty"` Names []string `json:"names,omitempty"` Action Action `json:"action"` Args []*Arg `json:"args,omitempty"` Comment string `json:"comment,omitempty"` Includes Filter `json:"includes,omitempty"` Excludes Filter `json:"excludes,omitempty"` } //Opencontainers spec only includes the 'Names' field //Docker also includes the old/original 'Name' field //Docker only: Comment, Includes, Excludes // Filter is used to conditionally apply Seccomp rules type Filter struct { Caps []string `json:"caps,omitempty"` Arches []string `json:"arches,omitempty"` MinKernel string `json:"minKernel,omitempty"` } ================================================ FILE: pkg/util/errutil/errutil.go ================================================ package errutil import ( "fmt" "runtime/debug" "strings" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/consts" "github.com/slimtoolkit/slim/pkg/version" ) // FailOnWithInfo logs the error information with additional context info (terminates the application) func FailOnWithInfo(err error, info map[string]string) { if err != nil { showInfo(info) stackData := debug.Stack() log.WithError(err).WithFields(log.Fields{ "version": version.Current(), "stack": string(stackData), }).Fatal("slim: failure") showCommunityInfo() } } // FailOn logs the error information (terminates the application) func FailOn(err error) { if err != nil { stackData := debug.Stack() log.WithError(err).WithFields(log.Fields{ "version": version.Current(), "stack": string(stackData), }).Fatal("slim: failure") showCommunityInfo() } } // WarnOn logs the error information as a warning func WarnOn(err error) { if err != nil { stackData := debug.Stack() log.WithError(err).WithFields(log.Fields{ "version": version.Current(), "stack": string(stackData), }).Warn("slim: warning") } } // FailWhen logs the given message if the condition is true (terminates the application) func FailWhen(cond bool, msg string) { if cond { stackData := debug.Stack() log.WithFields(log.Fields{ "version": version.Current(), "error": msg, "stack": string(stackData), }).Fatal("slim: failure") showCommunityInfo() } } // Fail logs the given messages and terminates the application func Fail(msg string) { stackData := debug.Stack() log.WithFields(log.Fields{ "version": version.Current(), "error": msg, "stack": string(stackData), }).Fatal("slim: failure") showCommunityInfo() } func showInfo(info map[string]string) { if len(info) > 0 { fmt.Println("Error Context Info:") for k, v := range info { fmt.Printf("'%s': '%s'\n", k, v) } } } func showCommunityInfo() { fmt.Printf("slim: message='Github discussions' info='%s'\n", consts.CommunityDiscussions) fmt.Printf("slim: message='CNCF Slack' info='%s'\n", consts.CommunityCNCFSlack) fmt.Printf("slim: message='join the Discord server to get help with this failure' info='%s'\n", consts.CommunityDiscord) fmt.Printf("slim: message='join the Gitter channel to get help with this failure' info='%s'\n", consts.CommunityGitter) } // exec.Command().Run() and its derivatives sometimes return // "wait: no child processes" or "waitid: no child processes" // even for successful runs. It's a race condition between the // Start() + Wait() calls and the actual underlying command // execution. The shorter the execution time, the higher are // the chances to get this error. // // Some examples from the wild: // - https://github.com/gitpod-io/gitpod/blob/405d44b74b5ac1dffe20e076d59c2b5986f18960/components/common-go/process/process.go#L18. func IsNoChildProcesses(err error) bool { if err == nil { return false } return strings.HasSuffix(err.Error(), ": no child processes") } ================================================ FILE: pkg/util/fsutil/fsutil.go ================================================ package fsutil import ( "archive/tar" "bytes" "encoding/json" "errors" "fmt" "io" "os" "path/filepath" "strings" "syscall" "time" "golang.org/x/sys/unix" "github.com/slimtoolkit/slim/pkg/pdiscover" "github.com/slimtoolkit/slim/pkg/util/errutil" "github.com/bmatcuk/doublestar" log "github.com/sirupsen/logrus" ) // File permission bits (execute bits only) const ( FilePermUserExe = 0100 FilePermGroupExe = 0010 FilePermOtherExe = 0001 ) // Native FileMode special bits mask const FMSpecialBits = os.ModeSticky | os.ModeSetgid | os.ModeSetuid // Native FileModes for extra flags const ( FMSticky = 01000 FMSetgid = 02000 FMSetuid = 04000 ) // Native FileMode bits for extra flags const ( BitSticky = 1 BitSetgid = 2 BitSetuid = 4 ) // Directory and file related errors var ( ErrNoFileData = errors.New("no file data") ErrNoSrcDir = errors.New("no source directory path") ErrNoDstDir = errors.New("no destination directory path") ErrSameDir = errors.New("source and destination directories are the same") ErrSrcDirNotExist = errors.New("source directory doesn't exist") ErrSrcNotDir = errors.New("source is not a directory") ErrSrcNotRegularFile = errors.New("source is not a regular file") ErrUnsupportedFileObjectType = errors.New("unsupported file object type") ) // FileModeExtraUnix2Go converts the standard unix filemode for the extra flags to the Go version func FileModeExtraUnix2Go(mode uint32) os.FileMode { switch mode { case FMSticky: return os.ModeSticky case FMSetgid: return os.ModeSetgid case FMSetuid: return os.ModeSetuid } return 0 } // FileModeExtraBitUnix2Go converts the standard unix filemode bit for the extra flags to the filemode in Go func FileModeExtraBitUnix2Go(bit uint32) os.FileMode { switch bit { case BitSticky: return os.ModeSticky case BitSetgid: return os.ModeSetgid case BitSetuid: return os.ModeSetuid } return 0 } // FileModeExtraBitsUnix2Go converts the standard unix filemode bits for the extra flags to the filemode flags in Go func FileModeExtraBitsUnix2Go(bits uint32) os.FileMode { var mode os.FileMode if bits&BitSticky != 0 { mode |= os.ModeSticky } if bits&BitSetgid != 0 { mode |= os.ModeSetgid } if bits&BitSetuid != 0 { mode |= os.ModeSetuid } return mode } // FileModeIsSticky checks if FileMode has the sticky bit set func FileModeIsSticky(mode os.FileMode) bool { if mode&os.ModeSticky != 0 { return true } return false } // FileModeIsSetgid checks if FileMode has the setgid bit set func FileModeIsSetgid(mode os.FileMode) bool { if mode&os.ModeSetgid != 0 { return true } return false } // FileModeIsSetuid checks if FileMode has the setuid bit set func FileModeIsSetuid(mode os.FileMode) bool { if mode&os.ModeSetuid != 0 { return true } return false } const ( rootStateKey = ".slim-state" releasesStateKey = "releases" imageStateBaseKey = "images" imageStateArtifactsKey = "artifacts" stateArtifactsPerms = 0777 releaseArtifactsPerms = 0740 ) var badInstallPaths = [...]string{ "/usr/local/bin", "/usr/local/sbin", "/usr/bin", "/usr/sbin", "/bin", "/sbin", } const ( tmpPath = "/tmp" stateTmpPath = "/tmp/slim-state" sensorFileName = "slim-sensor" ) // AccessInfo provides the file object access properties type AccessInfo struct { Flags os.FileMode PermsOnly bool UID int GID int } func NewAccessInfo() *AccessInfo { return &AccessInfo{ Flags: 0, UID: -1, GID: -1, } } // Remove removes the artifacts generated during the current application execution func Remove(artifactLocation string) error { return os.RemoveAll(artifactLocation) } // Touch creates the target file or updates its timestamp func Touch(target string) error { targetDirPath := FileDir(target) if _, err := os.Stat(targetDirPath); err != nil { if os.IsNotExist(err) { err = os.MkdirAll(targetDirPath, 0777) if err != nil { return err } } else { return err } } tf, err := os.OpenFile(target, os.O_RDONLY|os.O_CREATE, 0644) if err != nil { return err } tf.Close() tnow := time.Now().UTC() err = os.Chtimes(target, tnow, tnow) if err != nil { return err } return nil } // Exists returns true if the target file system object exists func Exists(target string) bool { if _, err := os.Stat(target); err != nil { return false } return true } // DirExists returns true if the target exists and it's a directory func DirExists(target string) bool { if info, err := os.Stat(target); err == nil && info.IsDir() { return true } return false } // IsDir returns true if the target file system object is a directory func IsDir(target string) bool { info, err := os.Stat(target) if err != nil { return false } return info.IsDir() } // IsRegularFile returns true if the target file system object is a regular file func IsRegularFile(target string) bool { info, err := os.Lstat(target) if err != nil { return false } return info.Mode().IsRegular() } // IsSymlink returns true if the target file system object is a symlink func IsSymlink(target string) bool { info, err := os.Lstat(target) if err != nil { return false } return (info.Mode() & os.ModeSymlink) == os.ModeSymlink } // IsTarFile returns true if the target file system object is a tar archive func IsTarFile(target string) bool { tf, err := os.Open(target) if err != nil { log.Debugf("fsutil.IsTarFile(%s): error - %v", target, err) return false } defer tf.Close() tr := tar.NewReader(tf) _, err = tr.Next() if err != nil { log.Debugf("fsutil.IsTarFile(%s): error - %v", target, err) return false } return true } func HasReadAccess(dst string) (bool, error) { err := unix.Access(dst, unix.R_OK) if err == nil { return true, nil } if err == unix.EACCES { return false, nil } return false, err } func HasWriteAccess(dst string) (bool, error) { err := unix.Access(dst, unix.W_OK) if err == nil { return true, nil } if err == unix.EACCES { return false, nil } return false, err } // SetAccess updates the access permissions on the destination func SetAccess(dst string, access *AccessInfo) error { if dst == "" || access == nil { return nil } if access.Flags != 0 { dstInfo, err := os.Stat(dst) if err != nil { return err } fmode := dstInfo.Mode() fmode = fmode &^ os.ModePerm fmode |= (access.Flags & os.ModePerm) if !access.PermsOnly { fmode = fmode &^ FMSpecialBits fmode |= (access.Flags & FMSpecialBits) } if err := os.Chmod(dst, fmode); err != nil { return err } } if access.UID > -1 || access.GID > -1 { if err := os.Chown(dst, access.UID, access.GID); err != nil { return err } } return nil } // CopyFile copies the source file system object to the desired destination func CopyFile(clone bool, src, dst string, makeDir bool) error { log.Debugf("CopyFile(%v,%v,%v,%v)", clone, src, dst, makeDir) info, err := os.Lstat(src) if err != nil { return err } switch { case info.Mode().IsRegular(): return CopyRegularFile(clone, src, dst, makeDir) case (info.Mode() & os.ModeSymlink) == os.ModeSymlink: return CopySymlinkFile(clone, src, dst, makeDir) default: return ErrUnsupportedFileObjectType } } // CopySymlinkFile copies a symlink file func CopySymlinkFile(clone bool, src, dst string, makeDir bool) error { log.Debugf("CopySymlinkFile(%v,%v,%v)", src, dst, makeDir) if makeDir { //srcDirName := FileDir(src) dstDirName := FileDir(dst) if _, err := os.Stat(dstDirName); err != nil { if os.IsNotExist(err) { var dirMode os.FileMode = 0777 //need to make it work for non-default user use cases //if clone { // srcDirInfo, err := os.Stat(srcDirName) // if err != nil { // return err // } // // dirMode = srcDirInfo.Mode() //} err = os.MkdirAll(dstDirName, dirMode) if err != nil { return err } } else { return err } } } linkRef, err := os.Readlink(src) if err != nil { return err } err = os.Symlink(linkRef, dst) if err != nil { return err } if clone { srcInfo, err := os.Lstat(src) if err != nil { return err } if sysStat, ok := srcInfo.Sys().(*syscall.Stat_t); ok { ssi := SysStatInfo(sysStat) if ssi.Ok { if err := UpdateSymlinkTimes(dst, ssi.Atime, ssi.Mtime); err != nil { log.Warnf("CopySymlinkFile(%v,%v) - UpdateSymlinkTimes error", src, dst) } if err := os.Lchown(dst, int(ssi.Uid), int(ssi.Gid)); err != nil { log.Warnf("CopySymlinkFile(%v,%v)- unable to change owner", src, dst) } } } else { log.Warnf("CopySymlinkFile(%v,%v)- unable to get Stat_t", src, dst) } } return nil } type dirInfo struct { src string dst string perms os.FileMode sys SysStat } func cloneDirPath(src, dst string) { src, err := filepath.Abs(src) if err != nil { errutil.FailOn(err) } dst, err = filepath.Abs(dst) if err != nil { errutil.FailOn(err) } var dirs []dirInfo for { if src == "/" { break } srcDirName := filepath.Base(src) dstDirName := filepath.Base(dst) if srcDirName != dstDirName { break } srcInfo, err := os.Stat(src) if err != nil { errutil.FailOn(err) } if !srcInfo.IsDir() { errutil.Fail("not a directory") } if Exists(dst) { break } di := dirInfo{ src: src, dst: dst, perms: srcInfo.Mode(), } if sysStat, ok := srcInfo.Sys().(*syscall.Stat_t); ok { di.sys = SysStatInfo(sysStat) } dirs = append([]dirInfo{di}, dirs...) src = FileDir(src) dst = FileDir(dst) } for _, dir := range dirs { if Exists(dir.dst) { log.Debugf("cloneDirPath() - dst dir exists - %v", dir.dst) continue } //using os.MkdirAll instead of os.Mkdir to make sure we don't miss any intermediate directories //need to research when we might miss intermediate directories err = os.MkdirAll(dir.dst, 0777) if err != nil { log.Errorf("cloneDirPath() - os.MkdirAll(%v) error - %v", dir.dst, err) } //if err != nil && !os.IsExist(err) { // errutil.FailOn(err) //} if err == nil { if err := os.Chmod(dir.dst, dir.perms); err != nil { log.Warnf("cloneDirPath() - unable to set perms (%v) - %v", dir.dst, err) } if dir.sys.Ok { if err := UpdateFileTimes(dir.dst, dir.sys.Atime, dir.sys.Mtime); err != nil { log.Warnf("cloneDirPath() - UpdateFileTimes error (%v) - %v", dir.dst, err) } if err := os.Chown(dir.dst, int(dir.sys.Uid), int(dir.sys.Gid)); err != nil { log.Warnf("cloneDirPath()- unable to change owner (%v) - %v", dir.dst, err) } } } } } // CopyRegularFile copies a regular file func CopyRegularFile(clone bool, src, dst string, makeDir bool) error { log.Debugf("CopyRegularFile(%v,%v,%v,%v)", clone, src, dst, makeDir) //'clone' should be true only for the dst files that need to clone the dir properties from src s, err := os.Open(src) if err != nil { return err } defer s.Close() srcFileInfo, err := s.Stat() if err != nil { return err } if !srcFileInfo.Mode().IsRegular() { return ErrSrcNotRegularFile } if makeDir { dstDirPath := FileDir(dst) if _, err := os.Stat(dstDirPath); err != nil { if os.IsNotExist(err) { srcDirPath := FileDir(src) if clone { cloneDirPath(srcDirPath, dstDirPath) } else { var dirMode os.FileMode = 0777 err = os.MkdirAll(dstDirPath, dirMode) if err != nil { return err } //try copying the timestamps too (even without cloning) srcDirInfo, err := os.Stat(srcDirPath) if err == nil { if sysStat, ok := srcDirInfo.Sys().(*syscall.Stat_t); ok { ssi := SysStatInfo(sysStat) if ssi.Ok { if err := UpdateFileTimes(dstDirPath, ssi.Atime, ssi.Mtime); err != nil { log.Warnf("CopyRegularFile() - UpdateFileTimes(%v) error - %v", dstDirPath, err) } } } } else { log.Warnf("CopyRegularFile() - os.Stat(%v) error - %v", srcDirPath, err) } } } else { return err } } } d, err := os.Create(dst) if err != nil { return err } if srcFileInfo.Size() > 0 { written, err := io.Copy(d, s) if err != nil { d.Close() return err } if written != srcFileInfo.Size() { log.Debugf("CopyRegularFile(%v,%v,%v) - copy data mismatch - %v/%v", src, dst, makeDir, written, srcFileInfo.Size()) d.Close() return fmt.Errorf("%s -> %s: partial copy - %d/%d", src, dst, written, srcFileInfo.Size()) } } //Need to close dst file before chmod works the right way if err := d.Close(); err != nil { log.Debugf("CopyRegularFile() - d.Close error - %v", err) return err } if clone { if err := os.Chmod(dst, srcFileInfo.Mode()); err != nil { log.Warnf("CopyRegularFile(%v,%v) - unable to set mode", src, dst) return err } if sysStat, ok := srcFileInfo.Sys().(*syscall.Stat_t); ok { ssi := SysStatInfo(sysStat) if ssi.Ok { if err := UpdateFileTimes(dst, ssi.Atime, ssi.Mtime); err != nil { log.Warnf("CopyRegularFile(%v,%v) - UpdateFileTimes error", src, dst) } if err := os.Chown(dst, int(ssi.Uid), int(ssi.Gid)); err != nil { log.Warnf("CopyRegularFile(%v,%v)- unable to change owner", src, dst) } } } else { log.Warnf("CopyRegularFile(%v,%v)- unable to get Stat_t", src, dst) } } else { if err := os.Chmod(dst, 0777); err != nil { log.Warnf("CopyRegularFile(%v,%v) - unable to set mode", src, dst) } if sysStat, ok := srcFileInfo.Sys().(*syscall.Stat_t); ok { ssi := SysStatInfo(sysStat) if ssi.Ok { if err := UpdateFileTimes(dst, ssi.Atime, ssi.Mtime); err != nil { log.Warnf("CopyRegularFile(%v,%v) - UpdateFileTimes error", src, dst) } } } else { log.Warnf("CopyRegularFile(%v,%v)- unable to get Stat_t", src, dst) } } return nil } // CopyAndObfuscateFile copies a regular file and performs basic file reference obfuscation func CopyAndObfuscateFile( clone bool, src string, dst string, makeDir bool) error { log.Debugf("CopyAndObfuscateFile(%v,%v,%v,%v)", clone, src, dst, makeDir) //need to preserve the extension because some of the app stacks //depend on it for its compile/run time behavior base := filepath.Base(dst) ext := filepath.Ext(base) base = strings.ReplaceAll(base, ".", "..") base = fmt.Sprintf(".d.%s", base) if ext != "" { base = fmt.Sprintf("%s%s", base, ext) } dirPart := filepath.Dir(dst) dstData := filepath.Join(dirPart, base) err := CopyRegularFile(clone, src, dstData, makeDir) if err != nil { return err } err = os.Symlink(base, dst) if err != nil { return err } return nil } // AppendToFile appends the provided data to the target file func AppendToFile(target string, data []byte, preserveTimes bool) error { if target == "" || len(data) == 0 { return nil } tfi, err := os.Stat(target) if err != nil { return err } var ssi SysStat if rawSysStat, ok := tfi.Sys().(*syscall.Stat_t); ok { ssi = SysStatInfo(rawSysStat) } tf, err := os.OpenFile(target, os.O_WRONLY|os.O_APPEND, 0644) if err != nil { return err } defer tf.Close() _, err = tf.Write(data) if err != nil { return err } if preserveTimes && ssi.Ok { if err := UpdateFileTimes(target, ssi.Atime, ssi.Mtime); err != nil { log.Warnf("AppendToFile(%v) - UpdateFileTimes error", target) } } return nil } type ReplaceInfo struct { PathSuffix string IsMatchRegex string Match string Replace string } // ReplaceFileData replaces the selected file bytes with the caller provided bytes func ReplaceFileData(target string, replace []ReplaceInfo, preserveTimes bool) error { if target == "" || len(replace) == 0 { return nil } tfi, err := os.Stat(target) if err != nil { return err } var ssi SysStat if rawSysStat, ok := tfi.Sys().(*syscall.Stat_t); ok { ssi = SysStatInfo(rawSysStat) } raw, err := os.ReadFile(target) if err != nil { return err } var replaced bool for _, r := range replace { if r.PathSuffix != "" { if !strings.HasSuffix(target, r.PathSuffix) { continue } } if r.Match == "" || r.Replace == "" { continue } raw = bytes.ReplaceAll(raw, []byte(r.Match), []byte(r.Replace)) replaced = true } if replaced { err = os.WriteFile(target, raw, 0644) if err != nil { return err } if preserveTimes && ssi.Ok { if err := UpdateFileTimes(target, ssi.Atime, ssi.Mtime); err != nil { log.Warnf("ReplaceFileData(%v) - UpdateFileTimes error", target) } } } return nil } type DataUpdaterFn func(target string, data []byte) ([]byte, error) // UpdateFileData updates all file data in target file using the updater function func UpdateFileData(target string, updater DataUpdaterFn, preserveTimes bool) error { if target == "" || updater == nil { return nil } tfi, err := os.Stat(target) if err != nil { return err } var ssi SysStat if rawSysStat, ok := tfi.Sys().(*syscall.Stat_t); ok { ssi = SysStatInfo(rawSysStat) } raw, err := os.ReadFile(target) if err != nil { return err } raw, err = updater(target, raw) if err != nil { return err } err = os.WriteFile(target, raw, 0644) if err != nil { return err } if preserveTimes && ssi.Ok { if err := UpdateFileTimes(target, ssi.Atime, ssi.Mtime); err != nil { log.Warnf("ReplaceFileData(%v) - UpdateFileTimes error", target) } } return nil } func copyFileObjectHandler( clone bool, srcBase, dstBase string, copyRelPath, skipErrors bool, excludePatterns []string, ignoreDirNames, ignoreFileNames map[string]struct{}, errs *[]error) filepath.WalkFunc { var foCount uint64 return func(path string, info os.FileInfo, err error) error { foCount++ if err != nil { if skipErrors { *errs = append(*errs, err) return nil } return err } foBase := filepath.Base(path) var isIgnored bool for _, xpattern := range excludePatterns { found, err := doublestar.Match(xpattern, path) if err != nil { log.Warnf("copyFileObjectHandler - [%v] excludePatterns Match error - %v\n", path, err) //should only happen when the pattern is malformed continue } if found { isIgnored = true break } } /* if _, ok := ignorePaths[path]; ok { isIgnored = true } for prefix := range ignorePrefixes { if strings.HasPrefix(path, prefix) { isIgnored = true break } } */ var targetPath string if copyRelPath { targetPath = filepath.Join(dstBase, strings.TrimPrefix(path, srcBase)) } else { targetPath = filepath.Join(dstBase, path) } switch { case info.Mode().IsDir(): if isIgnored { log.Debugf("dir path (%v) is ignored (skipping dir)...", path) return filepath.SkipDir } //todo: refactor if _, ok := ignoreDirNames[foBase]; ok { log.Debugf("dir name (%v) in ignoreDirNames list (skipping dir)...", foBase) return filepath.SkipDir } if _, err := os.Stat(targetPath); err != nil { if os.IsNotExist(err) { if clone { cloneDirPath(path, targetPath) } else { err = os.MkdirAll(targetPath, 0777) if err != nil { if skipErrors { *errs = append(*errs, err) return nil } return err } srcDirInfo, err := os.Stat(path) if err == nil { if sysStat, ok := srcDirInfo.Sys().(*syscall.Stat_t); ok { ssi := SysStatInfo(sysStat) if ssi.Ok { if err := UpdateFileTimes(targetPath, ssi.Atime, ssi.Mtime); err != nil { log.Warnf("copyFileObjectHandler() - UpdateFileTimes(%v) error - %v", targetPath, err) } } } } else { log.Warnf("copyFileObjectHandler() - os.Stat(%v) error - %v", path, err) } } } else { log.Warnf("copyFileObjectHandler() - os.Stat(%v) error - %v", targetPath, err) } } case info.Mode().IsRegular(): if isIgnored { log.Debugf("file path (%v) is ignored (skipping file)...", path) return nil } //todo: refactor if _, ok := ignoreFileNames[foBase]; ok { log.Debugf("file name (%v) in ignoreFileNames list (skipping file)...", foBase) return nil } err = CopyRegularFile(clone, path, targetPath, true) if err != nil { if skipErrors { *errs = append(*errs, err) return nil } return err } case (info.Mode() & os.ModeSymlink) == os.ModeSymlink: if isIgnored { log.Debugf("link path (%v) is ignored (skipping file)...", path) return nil } //todo: refactor if _, ok := ignoreFileNames[foBase]; ok { log.Debugf("link file name (%v) in ignoreFileNames list (skipping file)...", foBase) return nil } //TODO: should call CopySymlinkFile() here (instead of using os.Readlink/os.Symlink directly) //TODO: (add a flag) //to make it more generic need to support absolute path link rewriting //if they point to other copied file objects linkRef, err := os.Readlink(path) if err != nil { if skipErrors { *errs = append(*errs, err) return nil } return err } err = os.Symlink(linkRef, targetPath) if err != nil { if skipErrors { *errs = append(*errs, err) return nil } return err } default: log.Debug("is other file object type... (ignoring)") } return nil } } // CopyDirOnly copies a directory without any files func CopyDirOnly(clone bool, src, dst string) error { log.Debugf("CopyDirOnly(%v,%v,%v)", clone, src, dst) if src == "" { return ErrNoSrcDir } if dst == "" { return ErrNoDstDir } var err error if src, err = filepath.Abs(src); err != nil { return err } if dst, err = filepath.Abs(dst); err != nil { return err } if src == dst { return ErrSameDir } //TODO: better symlink support //when 'src' is a directory (need to better define the expected behavior) //should use Lstat() first srcInfo, err := os.Stat(src) if err != nil { if os.IsNotExist(err) { return ErrSrcDirNotExist } return err } if !srcInfo.IsDir() { return ErrSrcNotDir } if !DirExists(dst) { if clone { cloneDirPath(src, dst) } else { var dirMode os.FileMode = 0777 err = os.MkdirAll(dst, dirMode) if err != nil { return err } //try copying the timestamps too (even without cloning) if sysStat, ok := srcInfo.Sys().(*syscall.Stat_t); ok { ssi := SysStatInfo(sysStat) if ssi.Ok { if err := UpdateFileTimes(dst, ssi.Atime, ssi.Mtime); err != nil { log.Warnf("CopyDirOnly() - UpdateFileTimes(%v) error - %v", dst, err) } } } } } return nil } // CopyDir copies a directory func CopyDir(clone bool, src string, dst string, copyRelPath bool, skipErrors bool, excludePatterns []string, ignoreDirNames map[string]struct{}, ignoreFileNames map[string]struct{}) (error, []error) { log.Debugf("CopyDir(%v,%v,%v,%v,%#v,...)", src, dst, copyRelPath, skipErrors, excludePatterns) if src == "" { return ErrNoSrcDir, nil } if dst == "" { return ErrNoDstDir, nil } var err error if src, err = filepath.Abs(src); err != nil { return err, nil } if dst, err = filepath.Abs(dst); err != nil { return err, nil } if src == dst { return ErrSameDir, nil } //TODO: better symlink support //when 'src' is a directory (need to better define the expected behavior) //should use Lstat() first srcInfo, err := os.Stat(src) if err != nil { if os.IsNotExist(err) { return ErrSrcDirNotExist, nil } return err, nil } if !srcInfo.IsDir() { return ErrSrcNotDir, nil } //TODO: should clone directory permission, ownership and timestamps info var errs []error err = filepath.Walk(src, copyFileObjectHandler( clone, src, dst, copyRelPath, skipErrors, excludePatterns, ignoreDirNames, ignoreFileNames, &errs)) if err != nil { return err, nil } return nil, errs } /////////////////////////////////////////////////////////////////////////////// func FileMode(fileName string) string { finfo, err := os.Lstat(fileName) if err != nil { log.Errorf("fsutil.FileMode(%s) - os.Lstat error - %v", fileName, err) return "" } return finfo.Mode().String() } // ExeDir returns the directory information for the application func ExeDir() string { exePath, err := pdiscover.GetOwnProcPath() errutil.FailOn(err) return filepath.Dir(exePath) } // FileDir returns the directory information for the given file func FileDir(fileName string) string { abs, err := filepath.Abs(fileName) errutil.FailOn(err) return filepath.Dir(abs) } // PreparePostUpdateStateDir ensures that the updated sensor is copied to the state directory if necessary func PreparePostUpdateStateDir(statePrefix string) { log.Debugf("PreparePostUpdateStateDir(%v)", statePrefix) appDir := ExeDir() if statePrefix == "" { statePrefix = appDir } for _, badPath := range badInstallPaths { if appDir == badPath { if pinfo, err := os.Stat(tmpPath); err == nil && pinfo.IsDir() { log.Debugf("PreparePostUpdateStateDir - overriding state path to %v", stateTmpPath) srcSensorPath := filepath.Join(appDir, sensorFileName) dstSensorPath := filepath.Join(stateTmpPath, sensorFileName) if Exists(dstSensorPath) { log.Debugf("PreparePostUpdateStateDir - remove existing sensor binary - %v", dstSensorPath) if err := Remove(dstSensorPath); err != nil { log.Debugf("PreparePostUpdateStateDir - error removing existing sensor binary - %v", err) } } err = CopyRegularFile(false, srcSensorPath, dstSensorPath, true) errutil.FailOn(err) } else { log.Debugf("PreparePostUpdateStateDir - did not find tmp") } } } } // ResolveImageStateBasePath resolves the base path for the state path func ResolveImageStateBasePath(statePrefix string) string { log.Debugf("ResolveImageStateBasePath(%s)", statePrefix) appDir := ExeDir() if statePrefix == "" { statePrefix = appDir } for _, badPath := range badInstallPaths { if strings.HasPrefix(statePrefix, badPath) { log.Debugf("ResolveImageStateBasePath - statePrefix=%s appDir=%s badPath=%s", statePrefix, appDir, badPath) if pinfo, err := os.Stat(tmpPath); err == nil && pinfo.IsDir() { log.Debugf("ResolveImageStateBasePath - overriding state path to %v", stateTmpPath) statePrefix = stateTmpPath } else { log.Debugf("ResolveImageStateBasePath - did not find tmp") } break } } return statePrefix } // PrepareImageStateDirs ensures that the required application directories exist func PrepareImageStateDirs(statePrefix, imageID string) (string, string, string, string) { //prepares the image processing directories //creating the root state directory if it doesn't exist log.Debugf("PrepareImageStateDirs(%v,%v)", statePrefix, imageID) stateKey := imageID //images IDs in Docker 1.9+ are prefixed with a hash type... if strings.Contains(stateKey, ":") { parts := strings.Split(stateKey, ":") stateKey = parts[1] } appDir := ExeDir() if statePrefix == "" { statePrefix = appDir } for _, badPath := range badInstallPaths { //Note: //Should be a prefix check ideally //and should check if it's actually one of the 'shared' directories in Docker for Mac (on Macs) if statePrefix == badPath { log.Debugf("PrepareImageStateDirs - statePrefix=%s appDir=%s badPath=%s", statePrefix, appDir, badPath) if pinfo, err := os.Stat(tmpPath); err == nil && pinfo.IsDir() { log.Debugf("PrepareImageStateDirs - overriding state path to %v", stateTmpPath) statePrefix = stateTmpPath } else { log.Debugf("PrepareImageStateDirs - did not find tmp") } } if appDir == badPath { log.Debugf("PrepareImageStateDirs - statePrefix=%s appDir=%s badPath=%s", statePrefix, appDir, badPath) if pinfo, err := os.Stat(tmpPath); err == nil && pinfo.IsDir() { log.Debugf("PrepareImageStateDirs - copying sensor to state path (to %v)", stateTmpPath) srcSensorPath := filepath.Join(appDir, sensorFileName) dstSensorPath := filepath.Join(statePrefix, sensorFileName) err = CopyRegularFile(false, srcSensorPath, dstSensorPath, true) errInfo := map[string]string{ "op": "PrepareImageStateDirs", "call": "CopyRegularFile", "srcSensorPath": srcSensorPath, "dstSensorPath": dstSensorPath, } errutil.FailOnWithInfo(err, errInfo) } else { log.Debugf("PrepareImageStateDirs - did not find tmp") } } } localVolumePath := filepath.Join(statePrefix, rootStateKey, imageStateBaseKey, stateKey) artifactLocation := filepath.Join(localVolumePath, imageStateArtifactsKey) artifactDir, err := os.Stat(artifactLocation) switch { case err == nil: log.Debugf("PrepareImageStateDirs - removing existing state location: %v", artifactLocation) err = Remove(artifactLocation) if err != nil { log.Debugf("PrepareImageStateDirs - failed to remove existing state location: %v", artifactLocation) errutil.FailOn(err) } case os.IsNotExist(err): log.Debugf("PrepareImageStateDirs - will create new state location: %v", artifactLocation) default: errutil.FailOn(err) } err = os.MkdirAll(artifactLocation, stateArtifactsPerms) errutil.FailOn(err) artifactDir, err = os.Stat(artifactLocation) errutil.FailOn(err) log.Debug("PrepareImageStateDirs - created new image state location: ", artifactLocation) errutil.FailWhen(!artifactDir.IsDir(), "artifact location is not a directory") return localVolumePath, artifactLocation, statePrefix, stateKey } // PrepareReleaseStateDirs ensures that the required app release directories exist func PrepareReleaseStateDirs(statePrefix, version string) (string, string) { //prepares the app release directories (used to update the app binaries) //creating the root state directory if it doesn't exist log.Debugf("PrepareReleaseStateDirs(%v,%v)", statePrefix, version) if statePrefix == "" { statePrefix = ExeDir() } for _, badPath := range badInstallPaths { if statePrefix == badPath { if pinfo, err := os.Stat(tmpPath); err == nil && pinfo.IsDir() { log.Debugf("PrepareReleaseStateDirs - overriding state path to %v", stateTmpPath) statePrefix = stateTmpPath } else { log.Debugf("PrepareReleaseStateDirs - did not find tmp") } } } releaseDirPath := filepath.Join(statePrefix, rootStateKey, releasesStateKey, version) releaseDir, err := os.Stat(releaseDirPath) switch { case err == nil: log.Debugf("PrepareReleaseStateDirs - release state location already exists: %v", releaseDirPath) //not deleting existing release artifacts (todo: revisit this feature in the future) case os.IsNotExist(err): log.Debugf("PrepareReleaseStateDirs - will create new release state location: %v", releaseDirPath) default: errutil.FailOn(err) } err = os.MkdirAll(releaseDirPath, releaseArtifactsPerms) errutil.FailOn(err) releaseDir, err = os.Stat(releaseDirPath) errutil.FailOn(err) log.Debug("PrepareReleaseStateDirs - created new release state location: ", releaseDirPath) errutil.FailWhen(!releaseDir.IsDir(), "release state location is not a directory") return releaseDirPath, statePrefix } /* use - TBD func createDummyFile(src, dst string) error { _, err := os.Stat(dst) if err != nil && os.IsNotExist(err) { f, err := os.Create(dst) if err != nil { return err } defer f.Close() f.WriteString(" ") s, err := os.Open(src) if err != nil { return err } defer s.Close() srcFileInfo, err := s.Stat() if err != nil { return err } f.Chmod(srcFileInfo.Mode()) sysStat, ok := srcFileInfo.Sys().(*syscall.Stat_t) if !ok { log.Warnln("sensor: createDummyFile - unable to get Stat_t =>", src) return nil } //note: doing it only for regular files if srcFileInfo.Mode()&os.ModeSymlink != 0 { log.Warnln("sensor: createDummyFile - source is a symlink =>", src) return nil } //note: need to do the same for symlinks too if err := fsutil.UpdateFileTimes(dst, sysStat.Mtim, sysStat.Atim); err != nil { log.Warnln("sensor: createDummyFile - UpdateFileTimes error =>", dst) return err } } return nil } */ /////////////////////////////////////////////////////////////////////////////// func ArchiveFiles(afname string, files []string, removePath bool, addPrefix string) error { tf, err := os.Create(afname) if err != nil { return err } defer close(tf) tw := tar.NewWriter(tf) defer close(tw) fpRewrite := func(filePath string, removePath bool, addPrefix string) string { if removePath { filePath = filepath.Base(filePath) } if addPrefix != "" { filePath = fmt.Sprintf("%s%s", addPrefix, filePath) } return filePath } for _, fname := range files { if Exists(fname) && IsRegularFile(fname) { info, err := os.Stat(fname) if err != nil { log.Errorf("fsutil.ArchiveFiles: bad file - %s - %v", fname, err) return err } th, err := tar.FileInfoHeader(info, "") if err != nil { return err } th.Name = fpRewrite(fname, true, addPrefix) if err := tw.WriteHeader(th); err != nil { return err } f, err := os.Open(fname) if err != nil { return err } defer close(f) if _, err := io.CopyN(tw, f, th.Size); err != nil { return fmt.Errorf("cannot write %s file: %w", fname, err) } } else { log.Errorf("fsutil.ArchiveFiles: bad file - %s", fname) return fmt.Errorf("bad file - %s", fname) } } return nil } func ArchiveDir(afname string, d2aname string, trimPrefix string, addPrefix string) error { dirInfo, err := os.Stat(d2aname) if err != nil { return err } if !dirInfo.IsDir() { return fmt.Errorf("not a directory") } tf, err := os.Create(afname) if err != nil { return err } defer close(tf) tw := tar.NewWriter(tf) defer close(tw) onFSObject := func(path string, info os.FileInfo, err error) error { if err != nil { log.Errorf("fsutil.ArchiveDir.onFSObject: path=%q err=%q", path, err) return err } if info == nil { log.Errorf("fsutil.ArchiveDir.onFSObject: path=%q skipping... no file info", path) return nil } log.Debugf("fsutil.ArchiveDir.onFSObject: path=%q name=%v size=%v mode=%o isDir=%v isReg=%v", path, info.Name(), info.Size(), info.Mode(), info.IsDir(), info.Mode().IsRegular()) switch { case info.IsDir(): th, err := tar.FileInfoHeader(info, "") if err != nil { return err } th.Name = fmt.Sprintf("%s/", fpUpdate(path, trimPrefix, addPrefix)) if err := tw.WriteHeader(th); err != nil { return err } case info.Mode().IsRegular(): th, err := tar.FileInfoHeader(info, "") if err != nil { return err } th.Name = fpUpdate(path, trimPrefix, addPrefix) if err := tw.WriteHeader(th); err != nil { return err } f, err := os.Open(path) if err != nil { return err } defer close(f) if _, err := io.Copy(tw, f); err != nil { return err } case info.Mode()&os.ModeSymlink != 0: linkRef, err := os.Readlink(path) if err != nil { return err } th, err := tar.FileInfoHeader(info, linkRef) if err != nil { return err } th.Name = fpUpdate(path, trimPrefix, addPrefix) if err := tw.WriteHeader(th); err != nil { return err } default: log.Debugf("fsutil.ArchiveDir.onFSObject: ignoring other file object types - %q", path) return nil } return nil } filepath.Walk(d2aname, onFSObject) return nil } func close(ref io.Closer) { if err := ref.Close(); err != nil { log.Errorf("close - error closing: %v", err) } } func fpUpdate(filePath string, trimPrefix string, addPrefix string) string { if trimPrefix != "" { filePath = strings.TrimPrefix(filePath, trimPrefix) } if addPrefix != "" { filePath = fmt.Sprintf("%s%s", addPrefix, filePath) } return filePath } /////////////////////////////////////////////////////////////////////////////// // UpdateFileTimes updates the atime and mtime timestamps on the target file func UpdateFileTimes(target string, atime, mtime syscall.Timespec) error { ts := []syscall.Timespec{atime, mtime} return syscall.UtimesNano(target, ts) } // UpdateSymlinkTimes updates the atime and mtime timestamps on the target symlink func UpdateSymlinkTimes(target string, atime, mtime syscall.Timespec) error { ts := []unix.Timespec{unix.Timespec(atime), unix.Timespec(mtime)} return unix.UtimesNanoAt(unix.AT_FDCWD, target, ts, unix.AT_SYMLINK_NOFOLLOW) } // LoadStructFromFile creates a struct from a file func LoadStructFromFile(filePath string, out interface{}) error { if _, err := os.Stat(filePath); err != nil { return err } raw, err := os.ReadFile(filePath) if err != nil { return err } if len(raw) == 0 { return ErrNoFileData } decoder := json.NewDecoder(bytes.NewReader(raw)) err = decoder.Decode(out) if err != nil { return err } return nil } ================================================ FILE: pkg/util/fsutil/sysstat.go ================================================ package fsutil import ( "syscall" ) type SysStat struct { Ok bool Uid uint32 Gid uint32 Atime syscall.Timespec Mtime syscall.Timespec Ctime syscall.Timespec } /* Linux: Atim Timespec Mtim Timespec Mac: Atimespec Timespec Mtimespec Timespec MAC: type Stat_t struct { Dev int32 Mode uint16 Nlink uint16 Ino uint64 Uid uint32 Gid uint32 Rdev int32 Pad_cgo_0 [4]byte Atimespec Timespec Mtimespec Timespec Ctimespec Timespec Birthtimespec Timespec Size int64 Blocks int64 Blksize int32 Flags uint32 Gen uint32 Lspare int32 Qspare [2]int64 } LINUX: type Stat_t struct { Dev uint64 Ino uint64 Nlink uint64 Mode uint32 Uid uint32 Gid uint32 X__pad0 int32 Rdev uint64 Size int64 Blksize int64 Blocks int64 Atim Timespec Mtim Timespec Ctim Timespec X__unused [3]int64 } */ ================================================ FILE: pkg/util/fsutil/sysstat_darwin.go ================================================ package fsutil import ( "syscall" ) func SysStatInfo(raw *syscall.Stat_t) SysStat { return SysStat{ Ok: true, Uid: raw.Uid, Gid: raw.Gid, Atime: raw.Atimespec, Mtime: raw.Mtimespec, Ctime: raw.Ctimespec, } } ================================================ FILE: pkg/util/fsutil/sysstat_linux.go ================================================ package fsutil import ( "syscall" ) func SysStatInfo(raw *syscall.Stat_t) SysStat { return SysStat{ Ok: true, Uid: raw.Uid, Gid: raw.Gid, Atime: raw.Atim, Mtime: raw.Mtim, Ctime: raw.Ctim, } } ================================================ FILE: pkg/util/jsonutil/jsonutil.go ================================================ package jsonutil import ( "bytes" "encoding/json" ) func ToString(input interface{}) string { return toString(input, false) } func ToPretty(input interface{}) string { return toString(input, true) } func toString(input interface{}, pretty bool) string { var out bytes.Buffer encoder := json.NewEncoder(&out) encoder.SetEscapeHTML(false) if pretty { encoder.SetIndent(" ", " ") } _ = encoder.Encode(input) return out.String() } ================================================ FILE: pkg/util/printbuffer/printbuffer.go ================================================ package printbuffer import ( "bytes" "fmt" ) type PrintBuffer struct { Prefix string } func (b *PrintBuffer) Write(p []byte) (n int, err error) { for _, line := range bytes.Split(bytes.TrimRight(p, "\n"), []byte{'\n'}) { if len(line) > 0 { fmt.Println(b.Prefix, string(line)) } } return len(p), nil } ================================================ FILE: pkg/version/version.go ================================================ package version import ( "fmt" "runtime" "github.com/slimtoolkit/slim/pkg/consts" ) var ( appVersionTag = "latest" appVersionRev = "latest" appVersionTime = "latest" currentVersion = "v" ) func init() { currentVersion = fmt.Sprintf("%s/%s|%s|%s|%s|%s", runtime.GOOS, runtime.GOARCH, consts.AppVersionName, appVersionTag, appVersionRev, appVersionTime) } // Current returns the current version information func Current() string { return currentVersion } func Tag() string { return appVersionTag } ================================================ FILE: pkg/vulnerability/epss/api/api.go ================================================ package api import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "net/url" "strings" "time" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/vulnerability/epss" ) const ( callPath = "https://api.first.org/data/v1/epss" trueStr = "true" falseStr = "false" // GLOBAL PARAMETERS: // Comma-separated list of fieldnames to be retrieved. // Used only for limiting the available resultset. qsFields = "fields" // type: string // Limits the maximun number of records to be shown. // Should be a number between 1 and 100. qsLimit = "limit" // type: integer // Offsets the list of records by this number. // The first item is 0. qsOffset = "offset" // type: integer // Comma-separated list of fieldnames to be used to sort the resultset. // Fields starting with - (minus sign) indicate a descending order. // Each application should define its default sorting options. qsSort = "sort" // type: string // Use true, false, 0 or 1. If set to true will add an object wrapping // the resultset, with details on the status, total records found, // offset and limit. When false this information is returned //at the response header. Defaults to true. qsEnvelope = "envelope" // type: bool // Use true, false, 0 or 1. If the result should be // pretty-printed or no. Defaults to false. qsPretty = "pretty" // type: bool // Collection of fieldnames to retrieve. Affects the resultset and // the possible options for the parameter fields. Each data model // can specify multiple available scopes. qsScope = "scope" // type: string // Shows the last 30 days (or what is available) // of the EPSS score and percentile for any CVE. tsScopeVal = "time-series" // Shows the basic EPSS data (cve, epss, percentile, created). pubScopeVal = "public" // default scope value // EPSS CALL PARAMETERS: // Filters by EPSS CVE ID. Multiple values are supported separated by commas. // The maximum size accepted for this parameter is 2000 characters (including commas). qsCVE = "cve" // type: string maxCVEValueSize = 2000 // Date in the format YYYY-MM-DD (since April 14, 2021), // shows the historic values for epss and percentile attributes. qsDate = "date" // type: date // Number of days since the EPSS score was added to the database // (starting at 1, not affected by the date parameter). qsDays = "days" // type: int // Only display CVEs with EPSS score greater or equal than the parameter. qsEPSSGt = "epss-gt" // type: decimal // Only display CVEs with percentile greater or equal than the parameter. qsPctGt = "percentile-gt" // type: decimal // Only display CVEs with EPSS score lower or equal than the parameter. qsEPSSLt = "epss-lt" // type: decimal // Only display CVEs with percentile lower or equal than the parameter. qsPctLt = "percentile-lt" // type: decimal // Free text search at the CVE ID (allows partial matches). qsQuery = "q" // type: string // note: not fully documented qsOrder = "order" orderScoreDescVal = "!epss" orderScoreAscVal = "epss" orderPctDescVal = "!percentile" orderPctAscVal = "percentile" ) //reminder: //var _ connector.APIInt = (*API)(nil) type Instance struct { client *http.Client pretty bool pageSize uint64 debug bool logger *log.Entry } type Options struct { APITimeout int Pretty bool PageSize uint64 //will be used as the default 'limit' value Debug bool Logger *log.Entry } type CallOptions struct { Date string PageSize uint64 //will be used as the default 'limit' value Offset uint64 OutputFields []string WithHistory bool Output interface{} } type FilteredCallOptions struct { CallOptions CveIDPattern string DaysSinceAdded uint ScoreGt float64 ScoreLt float64 PercentileGt float64 PercentileLt float64 OrderRecords epss.OrderType } func New(options ...Options) *Instance { var ref Instance var logger *log.Entry timeoutSec := time.Duration(epss.APITimeout) if len(options) > 0 { ref.debug = options[0].Debug ref.pretty = options[0].Pretty if options[0].APITimeout > 0 { timeoutSec = time.Duration(options[0].APITimeout) } if options[0].PageSize > 0 { ref.pageSize = options[0].PageSize } else { ref.pageSize = epss.PageSize } logger = options[0].Logger } if logger == nil { logger = log.NewEntry(log.StandardLogger()) } ref.logger = logger.WithField("com", "epss.api") ref.client = &http.Client{ Timeout: timeoutSec * time.Second, } return &ref } func (ref *Instance) ListCall( ctx context.Context, options ...FilteredCallOptions) (epss.ReplyType, error) { var output epss.ReplyType if len(options) == 0 { options = append(options, FilteredCallOptions{}) } if options[0].Output == nil { output = allocOutput(options[0].WithHistory) options[0].Output = output } else { output = options[0].Output.(epss.ReplyType) } _, err := ref.GenericListCall(ctx, epss.OutJSON, options...) if err != nil { return nil, err } return output, nil } func (ref *Instance) GenericListCall( ctx context.Context, format string, options ...FilteredCallOptions) (string, error) { output := callOutput{ format: format, } input := &callInput{} if len(options) > 0 { if options[0].ScoreGt > 0 { input.scoreGt = options[0].ScoreGt } if options[0].ScoreLt > 0 { input.scoreLt = options[0].ScoreLt } if options[0].PercentileGt > 0 { input.percentileGt = options[0].PercentileGt } if options[0].PercentileLt > 0 { input.percentileLt = options[0].PercentileLt } if options[0].DaysSinceAdded > 0 { input.days = options[0].DaysSinceAdded } if options[0].CveIDPattern != "" { input.query = options[0].CveIDPattern } if options[0].OrderRecords != epss.NoOrder { switch options[0].OrderRecords { case epss.ScoreDescOrder: input.order = orderScoreDescVal case epss.ScoreAscOrder: input.order = orderScoreAscVal case epss.PercentileDescOrder: input.order = orderPctDescVal case epss.PercentileAscOrder: input.order = orderPctAscVal } } input.offset = options[0].Offset if options[0].PageSize > 0 { input.limit = options[0].PageSize } else { input.limit = ref.pageSize } input.date = options[0].Date input.fields = options[0].OutputFields input.history = options[0].WithHistory if options[0].Output != nil { output.decoded = options[0].Output } } _, err := ref.call(ctx, input, &output) if err != nil { return "", err } return output.raw, nil } func allocOutput(withHistory bool) epss.ReplyType { var output epss.ReplyType if withHistory { output = &epss.APIResultWithHistory{ Reply: epss.Reply{ ReplyMetadata: &epss.ReplyMetadata{}, }, } } else { output = &epss.APIResult{ Reply: epss.Reply{ ReplyMetadata: &epss.ReplyMetadata{}, }, } } return output } func (ref *Instance) LookupCall( ctx context.Context, cveIDs []string, options ...CallOptions) (epss.ReplyType, error) { var output epss.ReplyType if len(options) == 0 { options = append(options, CallOptions{}) } if options[0].Output == nil { output = allocOutput(options[0].WithHistory) options[0].Output = output } else { output = options[0].Output.(epss.ReplyType) } _, err := ref.GenericLookupCall(ctx, cveIDs, epss.OutJSON, options...) if err != nil { return nil, err } return output, nil } func (ref *Instance) GenericLookupCall( ctx context.Context, cveIDs []string, format string, options ...CallOptions) (string, error) { output := callOutput{ format: format, } input := &callInput{ cveList: cveIDs, } if len(options) > 0 { input.offset = options[0].Offset if options[0].PageSize > 0 { input.limit = options[0].PageSize } else { input.limit = ref.pageSize } input.date = options[0].Date input.fields = options[0].OutputFields input.history = options[0].WithHistory if options[0].Output != nil { output.decoded = options[0].Output } } _, err := ref.call(ctx, input, &output) if err != nil { return "", err } return output.raw, nil } type callInput struct { cve string cveList []string offset uint64 limit uint64 date string fields []string history bool //filter fields order string query string scoreGt float64 scoreLt float64 percentileGt float64 percentileLt float64 days uint } type callOutput struct { format string decoded interface{} decodedOnly bool raw string } func (ref *Instance) call( ctx context.Context, input *callInput, output *callOutput) (*http.Response, error) { logger := ref.logger.WithField("op", "epss.api.Instance.call") outFormat := epss.OutJSON if output != nil { if output.format == "" { output.format = outFormat } if !epss.IsValidOutput(output.format) { logger.WithFields(log.Fields{ "error": epss.ErrInvalidParams, "output.format": output.format, }).Error("epss.IsValidOutput") return nil, epss.ErrInvalidParams } outFormat = output.format } var body io.Reader //no call param to put in the body (yet) req, err := http.NewRequestWithContext(ctx, http.MethodGet, callPath, body) if err != nil { logger.WithError(err).Error("http.NewRequestWithContext") return nil, err } req.Header.Set("Accept", outFormat) qs := url.Values{} //always setting envelope because response headers don't include all fields qs.Set(qsEnvelope, trueStr) if ref.pretty { qs.Set(qsPretty, trueStr) } if input != nil { if input.query != "" { qs.Set(qsQuery, input.query) } if input.cve != "" { input.cveList = append(input.cveList, input.cve) } if len(input.cveList) > 0 { if err := epss.IsValidCveList(input.cveList); err != nil { logger.WithFields(log.Fields{ "error": err, "input.cveList": input.cveList, }).Error("epss.IsValidCveList") return nil, err } cveValue := strings.Join(input.cveList, ",") if len(cveValue) > maxCVEValueSize { logger.WithFields(log.Fields{ "error": epss.ErrTooManyCVEs, "input.cveList": input.cveList, }).Error("maxCVEValueSize") return nil, epss.ErrTooManyCVEs } qs.Set(qsCVE, cveValue) } if input.date != "" { date, err := epss.DateFromString(input.date) if err != nil { logger.WithFields(log.Fields{ "error": err, "input.date": input.date, }).Error("epss.DateFromString") return nil, err } if !date.IsZero() { if !epss.IsValidDate(date) { logger.WithFields(log.Fields{ "error": epss.ErrInvalidDateParam, "date": date, }).Error("epss.IsValidDate") return nil, epss.ErrInvalidDateParam } qs.Set(qsDate, input.date) } } if len(input.fields) > 0 { //todo: add field name validation qs.Set(qsFields, strings.Join(input.fields, ",")) } if input.history { qs.Set(qsScope, tsScopeVal) } if input.order != "" { qs.Set(qsOrder, input.order) } if input.scoreGt > 0 { qs.Set(qsEPSSGt, fmt.Sprintf("%v", input.scoreGt)) } if input.scoreLt > 0 { qs.Set(qsEPSSLt, fmt.Sprintf("%v", input.scoreLt)) } if input.percentileGt > 0 { qs.Set(qsPctGt, fmt.Sprintf("%v", input.percentileGt)) } if input.percentileLt > 0 { qs.Set(qsPctLt, fmt.Sprintf("%v", input.percentileLt)) } if input.days > 0 { qs.Set(qsDays, fmt.Sprintf("%v", input.days)) } if input.limit > 0 { qs.Set(qsLimit, fmt.Sprintf("%v", input.limit)) } if input.offset > 0 { qs.Set(qsOffset, fmt.Sprintf("%v", input.offset)) } } req.URL.RawQuery = qs.Encode() ref.logger.WithFields(log.Fields{ "path": callPath, "qs": req.URL.RawQuery, }).Trace("ref.client.Do") resp, err := ref.client.Do(req) if resp != nil && resp.Body != nil { if output != nil { defer resp.Body.Close() } if err != nil { logger.WithError(err).Error("ref.client.Do") return resp, err } if resp.StatusCode != http.StatusOK { logger.WithField("status.code", resp.StatusCode).Error("ref.client.Do") if resp.StatusCode == http.StatusNotFound { return resp, epss.ErrNotFound } if resp.StatusCode == http.StatusForbidden { return resp, epss.ErrNotAuthorized } return resp, fmt.Errorf("bad http status - %d", resp.StatusCode) } if output != nil { var b bytes.Buffer b.ReadFrom(resp.Body) if output.decoded != nil && outFormat == epss.OutJSON { //non-json responses are returned as raw strings decoder := json.NewDecoder(bytes.NewReader(b.Bytes())) err = decoder.Decode(output.decoded) if err != nil { logger.WithFields(log.Fields{ "error": err, "output.decoded": output.decoded, }).Error("decoder.Decode") return resp, err } } if output.decoded == nil || !output.decodedOnly { output.raw = b.String() } } return resp, nil } return resp, err } ================================================ FILE: pkg/vulnerability/epss/client/client.go ================================================ package client import ( "context" "time" log "github.com/sirupsen/logrus" "github.com/slimtoolkit/slim/pkg/vulnerability/epss" "github.com/slimtoolkit/slim/pkg/vulnerability/epss/api" ) type Instance struct { client *api.Instance debug bool logger *log.Entry } type Options struct { APITimeout int Pretty bool PageSize uint64 //will be used as the default 'limit' value Debug bool Logger *log.Entry } type CallOptions struct { Date time.Time PageSize uint64 //will be used as the default 'limit' value Offset uint64 OutputFields []string } type FilteredCallOptions struct { CallOptions CveIDPattern string DaysSinceAdded uint ScoreGt float64 ScoreLt float64 PercentileGt float64 PercentileLt float64 OrderRecords epss.OrderType } func New(options ...Options) *Instance { var apiOptions []api.Options if len(options) > 0 { apiOptions = append(apiOptions, api.Options{ APITimeout: options[0].APITimeout, Pretty: options[0].Pretty, PageSize: options[0].PageSize, Debug: options[0].Debug, Logger: options[0].Logger, }) } ref := Instance{ client: api.New(apiOptions...), } var logger *log.Entry if len(options) > 0 { ref.debug = options[0].Debug logger = options[0].Logger } if logger == nil { logger = log.NewEntry(log.StandardLogger()) } ref.logger = logger.WithField("com", "epss.client") return &ref } func apiCallOptions(input []CallOptions) []api.CallOptions { var output []api.CallOptions if len(input) > 0 { output = append(output, api.CallOptions{ Date: epss.DateToString(input[0].Date), PageSize: input[0].PageSize, Offset: input[0].Offset, OutputFields: input[0].OutputFields, }) } return output } func (ref *Instance) LookupScore( ctx context.Context, cveID string, options ...CallOptions) (*epss.Score, *epss.Result, error) { apiOptions := apiCallOptions(options) apiReply, err := ref.client.LookupCall(ctx, []string{cveID}, apiOptions...) if err != nil { return nil, nil, err } apiResult := apiReply.(*epss.APIResult) result, err := epss.NewResult(apiResult) if err != nil { return nil, nil, err } if len(apiResult.Data) == 0 { return nil, result, nil } return result.Data[0], result, nil } func (ref *Instance) LookupScoreWithHistory( ctx context.Context, cveID string, options ...CallOptions) (*epss.ScoreWithHistory, *epss.ResultWithHistory, error) { apiOptions := apiCallOptions(options) if len(options) == 0 { apiOptions = append(apiOptions, api.CallOptions{}) } apiOptions[0].WithHistory = true apiReply, err := ref.client.LookupCall(ctx, []string{cveID}, apiOptions...) if err != nil { return nil, nil, err } apiResult := apiReply.(*epss.APIResultWithHistory) if len(apiResult.Data) == 0 { return nil, nil, nil } result, err := epss.NewResultWithHistory(apiResult) if err != nil { return nil, nil, err } return result.Data[0], result, nil } func (ref *Instance) LookupScores( ctx context.Context, cveIDs []string, options ...CallOptions) ([]*epss.Score, *epss.Result, error) { apiOptions := apiCallOptions(options) apiReply, err := ref.client.LookupCall(ctx, cveIDs, apiOptions...) if err != nil { return nil, nil, err } result, err := epss.NewResult(apiReply.(*epss.APIResult)) if err != nil { return nil, nil, err } return result.Data, result, nil } func (ref *Instance) LookupScoresWithHistory( ctx context.Context, cveIDs []string, options ...CallOptions) ([]*epss.ScoreWithHistory, *epss.ResultWithHistory, error) { apiOptions := apiCallOptions(options) if len(options) == 0 { apiOptions = append(apiOptions, api.CallOptions{}) } apiOptions[0].WithHistory = true apiReply, err := ref.client.LookupCall(ctx, cveIDs, apiOptions...) if err != nil { return nil, nil, err } result, err := epss.NewResultWithHistory(apiReply.(*epss.APIResultWithHistory)) if err != nil { return nil, nil, err } return result.Data, result, nil } func (ref *Instance) ListScores( ctx context.Context, options ...FilteredCallOptions) ([]*epss.Score, *epss.Result, error) { apiOptions := apiFilteredCallOptions(options) apiReply, err := ref.client.ListCall(ctx, apiOptions...) if err != nil { return nil, nil, err } result, err := epss.NewResult(apiReply.(*epss.APIResult)) if err != nil { return nil, nil, err } return result.Data, result, nil } func (ref *Instance) ListScoresWithHistory( ctx context.Context, options ...FilteredCallOptions) ([]*epss.ScoreWithHistory, *epss.ResultWithHistory, error) { apiOptions := apiFilteredCallOptions(options) if len(options) == 0 { apiOptions = append(apiOptions, api.FilteredCallOptions{}) } apiOptions[0].WithHistory = true apiReply, err := ref.client.ListCall(ctx, apiOptions...) if err != nil { return nil, nil, err } result, err := epss.NewResultWithHistory(apiReply.(*epss.APIResultWithHistory)) if err != nil { return nil, nil, err } return result.Data, result, nil } func apiFilteredCallOptions(input []FilteredCallOptions) []api.FilteredCallOptions { var output []api.FilteredCallOptions if len(input) > 0 { output = append(output, api.FilteredCallOptions{ CallOptions: api.CallOptions{ Date: epss.DateToString(input[0].Date), PageSize: input[0].PageSize, Offset: input[0].Offset, OutputFields: input[0].OutputFields, }, CveIDPattern: input[0].CveIDPattern, DaysSinceAdded: input[0].DaysSinceAdded, ScoreGt: input[0].ScoreGt, ScoreLt: input[0].ScoreLt, PercentileGt: input[0].PercentileGt, PercentileLt: input[0].PercentileLt, OrderRecords: input[0].OrderRecords, }) } return output } ================================================ FILE: pkg/vulnerability/epss/data.go ================================================ package epss import ( "encoding/json" "errors" "fmt" "strconv" "strings" "time" ) ///////////////////////////////////////////////////////////// // // EPSS API DOCS: // // https://api.first.org/epss // https://www.first.org/epss/api // // EPSS OFFLINE DATA: // // ALL EPSS scores for all CVEs for a particular date (yyyy-mm-dd). // For this request, simply request the full csv directly as // https://epss.cyentia.com/epss_scores-YYYY-MM-DD.csv.gz // ///////////////////////////////////////////////////////////// type ReplyMetadata struct { Status string `json:"status"` StatusCode int `json:"status-code"` Version string `json:"version"` Access string `json:"access"` Total uint64 `json:"total"` Offset uint64 `json:"offset"` Limit uint64 `json:"limit"` } type ReplyType interface { Metadata() *ReplyMetadata } type Reply struct { *ReplyMetadata } func (ref *Reply) Metadata() *ReplyMetadata { return ref.ReplyMetadata } const ( APITimeout = 20 PageSize = uint64(100) ) const ( OutJSON = "application/json" OutYAML = "application/yaml" OutXML = "application/xml" OutCSV = "application/csv" ) var ( ErrInvalidParams = errors.New("invalid params") ErrInvalidDateParam = errors.New("invalid date param") ErrInvalidCVEParam = errors.New("invalid CVE param") ErrNotFound = errors.New("not found") ErrNotAuthorized = errors.New("not authorized") ErrTooManyCVEs = errors.New("too many CVEs") ) type OrderType string const ( NoOrder = "" ScoreDescOrder = "ot.score.desc" ScoreAscOrder = "ot.score.asc" PercentileDescOrder = "ot.percentile.desc" PercentileAscOrder = "ot.percentile.asc" ) type APIScoreData struct { Date string `json:"date"` EPSS string `json:"epss"` Percentile string `json:"percentile"` } type APIScore struct { APIScoreData CVE string `json:"cve"` } type APIScoreWithHistory struct { APIScore TimeSeries []APIScoreData `json:"time-series,omitempty"` } type APIResult struct { Reply Data []*APIScore `json:"data"` } type APIResultWithHistory struct { Reply Data []*APIScoreWithHistory `json:"data"` } type Result struct { Reply Data []*Score `json:"data"` } type ResultWithHistory struct { Reply Data []*ScoreWithHistory `json:"data"` } type ScoreData struct { Date time.Time `json:"date"` EPSS float64 `json:"epss"` Percentile float64 `json:"percentile"` } type Score struct { ScoreData CVE string `json:"cve"` } type ScoreWithHistory struct { Score History []ScoreData `json:"history,omitempty"` } func (ref *ScoreData) MarshalJSON() ([]byte, error) { type ScoreDataAlias ScoreData return json.Marshal(&struct { *ScoreDataAlias Date string `json:"date"` }{ ScoreDataAlias: (*ScoreDataAlias)(ref), Date: ref.Date.Format(time.DateOnly), }) } func (ref *Score) MarshalJSON() ([]byte, error) { //embedding gotcha workaround return json.Marshal(&struct { EPSS float64 `json:"epss"` Percentile float64 `json:"percentile"` Date string `json:"date"` CVE string `json:"cve"` }{ EPSS: ref.EPSS, Percentile: ref.Percentile, Date: ref.Date.Format(time.DateOnly), CVE: ref.CVE, }) } func (ref *ScoreWithHistory) MarshalJSON() ([]byte, error) { //embedding gotcha workaround return json.Marshal(&struct { EPSS float64 `json:"epss"` Percentile float64 `json:"percentile"` Date string `json:"date"` CVE string `json:"cve"` History []ScoreData `json:"history,omitempty"` }{ EPSS: ref.EPSS, Percentile: ref.Percentile, Date: ref.Date.Format(time.DateOnly), CVE: ref.CVE, History: ref.History, }) } func NewResult(input *APIResult) (*Result, error) { if input == nil { return nil, nil } output := Result{ Reply: Reply{ ReplyMetadata: input.ReplyMetadata, }, } if len(input.Data) == 0 { return &output, nil } output.Data = make([]*Score, 0, len(input.Data)) for _, rawScore := range input.Data { score := Score{ CVE: rawScore.CVE, } var err error score.Date, err = DateFromString(rawScore.Date) if err != nil { return nil, err } score.EPSS, err = strconv.ParseFloat(rawScore.EPSS, 64) if err != nil { return nil, err } score.Percentile, err = strconv.ParseFloat(rawScore.Percentile, 64) if err != nil { return nil, err } output.Data = append(output.Data, &score) } return &output, nil } func NewResultWithHistory(input *APIResultWithHistory) (*ResultWithHistory, error) { if input == nil { return nil, nil } output := ResultWithHistory{ Reply: Reply{ ReplyMetadata: input.ReplyMetadata, }, } if len(input.Data) == 0 { return &output, nil } output.Data = make([]*ScoreWithHistory, 0, len(input.Data)) for _, rawScore := range input.Data { score := ScoreWithHistory{ Score: Score{ CVE: rawScore.CVE, }, } var err error score.Date, err = DateFromString(rawScore.Date) if err != nil { return nil, err } score.EPSS, err = strconv.ParseFloat(rawScore.EPSS, 64) if err != nil { return nil, err } score.Percentile, err = strconv.ParseFloat(rawScore.Percentile, 64) if err != nil { return nil, err } if len(rawScore.TimeSeries) == 0 { continue } score.History = make([]ScoreData, 0, len(rawScore.TimeSeries)) for _, rawData := range rawScore.TimeSeries { var scoreData ScoreData scoreData.Date, err = DateFromString(rawData.Date) if err != nil { return nil, err } scoreData.EPSS, err = strconv.ParseFloat(rawData.EPSS, 64) if err != nil { return nil, err } scoreData.Percentile, err = strconv.ParseFloat(rawData.Percentile, 64) if err != nil { return nil, err } score.History = append(score.History, scoreData) } output.Data = append(output.Data, &score) } return &output, nil } func Date(year int, month time.Month, day int) time.Time { return time.Date(year, month, day, 0, 0, 0, 0, time.UTC) } func DateFromString(input string) (time.Time, error) { date, err := time.Parse(time.DateOnly, input) if err != nil { return time.Time{}, err } return date, nil } func DateFromStringOrNow(input string) time.Time { date, err := DateFromString(input) if err == nil { return date } return time.Now() } func DateToString(input time.Time) string { return input.Format(time.DateOnly) } func IsValidDate(input time.Time) bool { if input.Before(EarliestDate) || input.After(time.Now().UTC()) { return false } return true } var EarliestDate = Date(2021, 4, 14) func IsValidOutput(input string) bool { switch input { case OutJSON, OutYAML, OutXML, OutCSV: return true } return false } func IsValidCveID(input string) error { parts := strings.Split(input, "-") if len(parts) != 3 { return ErrInvalidCVEParam } if strings.ToUpper(parts[0]) != "CVE" { return ErrInvalidCVEParam } if len(parts[1]) != 4 { return ErrInvalidCVEParam } if len(parts[2]) < 4 { return ErrInvalidCVEParam } yr, err := strconv.Atoi(parts[1]) if err != nil { return err } if yr < 1999 || yr > time.Now().Year() { return ErrInvalidCVEParam } sn, err := strconv.Atoi(parts[2]) if err != nil { return err } if sn < 1 { return ErrInvalidCVEParam } return nil } func IsValidCveList(input []string) error { for idx, cve := range input { if err := IsValidCveID(cve); err != nil { return fmt.Errorf("invalid CVE ID: index=%d cve='%s' (%w)", idx, cve, err) } } return nil } var ( _ ReplyType = (*APIResult)(nil) _ ReplyType = (*APIResultWithHistory)(nil) _ ReplyType = (*Result)(nil) _ ReplyType = (*ResultWithHistory)(nil) ) ================================================ FILE: scripts/docker-builder-m1.run.sh ================================================ #!/usr/bin/env bash set -e SOURCE="${BASH_SOURCE[0]}" while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done BDIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )" pushd $BDIR docker run -v $(pwd):/go/src/github.com/slimtoolkit/slim -w /go/src/github.com/slimtoolkit/slim -it --rm --name="slim-builder" golang:1.21 make build_m1 if [ ! -f dist_mac_m1.zip ]; then if hash zip 2> /dev/null; then zip -r dist_mac_m1.zip dist_mac_m1 -x "*.DS_Store" fi fi ================================================ FILE: scripts/docker-builder.run.sh ================================================ #!/usr/bin/env bash set -e SOURCE="${BASH_SOURCE[0]}" while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done BDIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )" pushd $BDIR docker run -v $(pwd):/go/src/github.com/slimtoolkit/slim -w /go/src/github.com/slimtoolkit/slim -it --rm --name="slim-builder" golang:1.21 make build if [ ! -f dist_mac.zip ]; then if hash zip 2> /dev/null; then zip -r dist_mac.zip dist_mac -x "*.DS_Store" fi fi ================================================ FILE: scripts/govulncheck.sh ================================================ #!/usr/bin/env bash set -e SOURCE="${BASH_SOURCE[0]}" while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done BDIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )" GOBIN=${GOBIN:=$(go env GOBIN)} if [ -z "$GOBIN" ]; then GOBIN="$(go env GOPATH)/bin" fi GOVULNCHECK="${GOBIN}/govulncheck" if [ ! -f "$GOVULNCHECK" ]; then echo "Tools: No govulncheck. Installing...." go install golang.org/x/vuln/cmd/govulncheck@latest fi pushd $BDIR $GOVULNCHECK ./... popd ================================================ FILE: scripts/install-slim.sh ================================================ #!/usr/bin/env bash function get_mint() { local DIST="" local EXT="" local FILENAME="" local KERNEL="" local MACHINE="" local TMP_DIR="" local URL="" local VER="" if [ -n "$1" ]; then VER=$1 else # Get the current released tag_name VER=$(curl -sL https://api.github.com/repos/mintoolkit/mint/releases \ | grep tag_name | head -n1 | cut -d'"' -f4) fi if [ -n "${VER}" ]; then URL="https://github.com/mintoolkit/mint/releases/download/${VER}" else echo "ERROR! Could not retrieve the current Mint version number." exit 1 fi # Get kernel name and machine architecture. KERNEL=$(uname -s) MACHINE=$(uname -m) # Determine the target distrubution if [ "${KERNEL}" == "Linux" ]; then EXT="tar.gz" if [ "${MACHINE}" == "x86_64" ]; then DIST="linux" elif [ "${MACHINE}" == "armv7l" ]; then DIST="linux_arm" elif [ "${MACHINE}" == "aarch64" ]; then DIST="linux_arm64" fi elif [ "${KERNEL}" == "Darwin" ]; then EXT="zip" if [ "${MACHINE}" == "x86_64" ]; then DIST="mac" elif [ "${MACHINE}" == "arm64" ]; then DIST="mac_m1" fi else echo "ERROR! ${KERNEL} is not a supported platform." exit 1 fi # Was a known distribution detected? if [ -z "${DIST}" ]; then echo "ERROR! ${MACHINE} is not a supported architecture." exit 1 fi # Derive the filename FILENAME="dist_${DIST}.${EXT}" echo " - Downloading ${URL}/${FILENAME}" TMP_DIR=$(mktemp -d) curl -sLo "${TMP_DIR}/${FILENAME}" "${URL}/${FILENAME}" echo " - Unpacking ${FILENAME}" if [ "${EXT}" == "zip" ]; then unzip -qq -o "${TMP_DIR}/${FILENAME}" -d "${TMP_DIR}" elif [ "${EXT}" == "tar.gz" ]; then tar -xf "${TMP_DIR}/${FILENAME}" --directory "${TMP_DIR}" else echo "ERROR! Unexpected file extension." exit 1 fi # /usr/local/bin should be present on Linux and macOS hosts. Just be sure. if [ -d /usr/local/bin ]; then echo " - Placing mint in /usr/local/bin" mv "${TMP_DIR}/dist_${DIST}/mint" /usr/local/bin/ mv "${TMP_DIR}/dist_${DIST}/mint-sensor" /usr/local/bin/ chmod +x /usr/local/bin/mint chmod +x /usr/local/bin/mint-sensor mv "${TMP_DIR}/dist_${DIST}/slim" /usr/local/bin/ mv "${TMP_DIR}/dist_${DIST}/docker-slim" /usr/local/bin/ chmod +x /usr/local/bin/slim chmod +x /usr/local/bin/docker-slim echo " - Cleaning up" rm -rf "${TMP_DIR}" echo -en " - " mint --version else echo "ERROR! /usr/local/bin is not present. Install aborted." rm -rf "${TMP_DIR}" exit 1 fi } echo "Mint scripted install" if [ "$(id -u)" -ne 0 ]; then echo "ERROR! You must run this script as root." exit 1 fi get_mint $1 # You can pass a specific version to install otherwise the latest version will be installed ================================================ FILE: scripts/mac/bom.gen.command ================================================ here="$(dirname "$BASH_SOURCE")" cd $here/.. ./bom.gen.sh ================================================ FILE: scripts/mac/docker-builder-m1.run.command ================================================ #!/usr/bin/env bash here="$(dirname "$BASH_SOURCE")" cd $here/.. ./docker-builder-m1.run.sh ================================================ FILE: scripts/mac/docker-builder.run.command ================================================ #!/usr/bin/env bash here="$(dirname "$BASH_SOURCE")" cd $here/.. ./docker-builder.run.sh ================================================ FILE: scripts/mac/govulncheck.command ================================================ here="$(dirname "$BASH_SOURCE")" cd $here/.. ./govulncheck.sh ================================================ FILE: scripts/mac/src.build.command ================================================ here="$(dirname "$BASH_SOURCE")" cd $here/.. ./src.build.sh ================================================ FILE: scripts/mac/src.cleanup.command ================================================ here="$(dirname "$BASH_SOURCE")" cd $here/.. ./src.cleanup.sh ================================================ FILE: scripts/mac/src.fmt.command ================================================ here="$(dirname "$BASH_SOURCE")" cd $here/.. ./src.fmt.sh ================================================ FILE: scripts/mac/src.inspect.command ================================================ here="$(dirname "$BASH_SOURCE")" cd $here/.. ./src.inspect.sh ================================================ FILE: scripts/mac/src.test.command ================================================ here="$(dirname "$BASH_SOURCE")" cd $here/.. ./src.test.sh ================================================ FILE: scripts/mac/tools.get.command ================================================ here="$(dirname "$BASH_SOURCE")" cd $here/.. ./tools.get.sh ================================================ FILE: scripts/src.build.m1.sh ================================================ #!/usr/bin/env bash set -e SOURCE="${BASH_SOURCE[0]}" while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done BDIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )" export CGO_ENABLED=0 pushd $BDIR BUILD_TIME="$(date -u '+%Y-%m-%d_%I:%M:%S%p')" TAG="current" REVISION="current" if hash git 2>/dev/null && [ -e $BDIR/.git ]; then TAG="$(git describe --tags --always)" REVISION="$(git rev-parse HEAD)" fi LD_FLAGS="-s -w -X github.com/slimtoolkit/slim/pkg/version.appVersionTag=${TAG} -X github.com/slimtoolkit/slim/pkg/version.appVersionRev=${REVISION} -X github.com/slimtoolkit/slim/pkg/version.appVersionTime=${BUILD_TIME}" go generate github.com/slimtoolkit/slim/pkg/appbom pushd ${BDIR}/cmd/slim GOOS=darwin GOARCH=arm64 go build -mod=vendor -trimpath -ldflags="${LD_FLAGS}" -a -tags 'netgo osusergo' -o "${BDIR}/bin/mac_m1/slim" popd pushd ${BDIR}/cmd/slim-sensor GOOS=linux GOARCH=arm64 go build -mod=vendor -trimpath -ldflags="${LD_FLAGS}" -a -tags 'netgo osusergo' -o "$BDIR/bin/linux_arm64/slim-sensor" chmod a+x "$BDIR/bin/linux_arm64/slim-sensor" popd rm -rfv ${BDIR}/dist_mac_m1 mkdir ${BDIR}/dist_mac_m1 cp ${BDIR}/bin/mac_m1/slim ${BDIR}/dist_mac_m1/slim cp ${BDIR}/bin/linux_arm64/slim-sensor ${BDIR}/dist_mac_m1/slim-sensor pushd ${BDIR}/dist_mac_m1 ln -s slim docker-slim popd pushd ${BDIR} if hash zip 2> /dev/null; then zip -r dist_mac_m1.zip dist_mac_m1 -x "*.DS_Store" fi rm -rfv ${BDIR}/bin ================================================ FILE: scripts/src.build.quick.sh ================================================ #!/usr/bin/env bash set -e SOURCE="${BASH_SOURCE[0]}" while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done BDIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )" BUILD_TIME="$(date -u '+%Y-%m-%d_%I:%M:%S%p')" TAG="current" REVISION="current" if hash git 2>/dev/null && [ -e $BDIR/.git ]; then TAG="$(git describe --tags)" REVISION="$(git rev-parse HEAD)" fi LD_FLAGS="-s -w -X github.com/slimtoolkit/slim/pkg/version.appVersionTag=${TAG} -X github.com/slimtoolkit/slim/pkg/version.appVersionRev=${REVISION} -X github.com/slimtoolkit/slim/pkg/version.appVersionTime=${BUILD_TIME}" go generate github.com/slimtoolkit/slim/pkg/appbom BINDIR="${BDIR}/bin" mkdir -p "$BINDIR" rm -rf "${BINDIR}/"* CGO_ENABLED=0 go build -ldflags="${LD_FLAGS}" -mod=vendor -o "${BINDIR}/slim" "${BDIR}/cmd/slim/main.go" CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="${LD_FLAGS}" -mod=vendor -o "${BINDIR}/slim-sensor" "${BDIR}/cmd/slim-sensor/main.go" ================================================ FILE: scripts/src.build.sh ================================================ #!/usr/bin/env bash set -e SOURCE="${BASH_SOURCE[0]}" while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done BDIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )" export CGO_ENABLED=0 pushd $BDIR BUILD_TIME="$(date -u '+%Y-%m-%d_%I:%M:%S%p')" TAG="current" REVISION="current" if hash git 2>/dev/null && [ -e $BDIR/.git ]; then TAG="$(git describe --tags --always)" REVISION="$(git rev-parse HEAD)" fi LD_FLAGS="-s -w -X github.com/slimtoolkit/slim/pkg/version.appVersionTag=${TAG} -X github.com/slimtoolkit/slim/pkg/version.appVersionRev=${REVISION} -X github.com/slimtoolkit/slim/pkg/version.appVersionTime=${BUILD_TIME}" go generate github.com/slimtoolkit/slim/pkg/appbom pushd ${BDIR}/cmd/slim GOOS=linux GOARCH=amd64 go build -mod=vendor -trimpath -ldflags="${LD_FLAGS}" -a -tags 'netgo osusergo' -o "${BDIR}/bin/linux/slim" GOOS=darwin GOARCH=amd64 go build -mod=vendor -trimpath -ldflags="${LD_FLAGS}" -a -tags 'netgo osusergo' -o "${BDIR}/bin/mac/slim" GOOS=linux GOARCH=arm go build -mod=vendor -trimpath -ldflags="${LD_FLAGS}" -a -tags 'netgo osusergo' -o "$BDIR/bin/linux_arm/slim" GOOS=linux GOARCH=arm64 go build -mod=vendor -trimpath -ldflags="${LD_FLAGS}" -a -tags 'netgo osusergo' -o "$BDIR/bin/linux_arm64/slim" popd pushd ${BDIR}/cmd/slim-sensor GOOS=linux GOARCH=amd64 go build -mod=vendor -trimpath -ldflags="${LD_FLAGS}" -a -tags 'netgo osusergo' -o "${BDIR}/bin/linux/slim-sensor" GOOS=linux GOARCH=arm go build -mod=vendor -trimpath -ldflags="${LD_FLAGS}" -a -tags 'netgo osusergo' -o "$BDIR/bin/linux_arm/slim-sensor" GOOS=linux GOARCH=arm64 go build -mod=vendor -trimpath -ldflags="${LD_FLAGS}" -a -tags 'netgo osusergo' -o "$BDIR/bin/linux_arm64/slim-sensor" chmod a+x "${BDIR}/bin/linux/slim-sensor" chmod a+x "$BDIR/bin/linux_arm/slim-sensor" chmod a+x "$BDIR/bin/linux_arm64/slim-sensor" popd rm -rfv ${BDIR}/dist_mac mkdir ${BDIR}/dist_mac cp ${BDIR}/bin/mac/slim ${BDIR}/dist_mac/slim cp ${BDIR}/bin/linux/slim-sensor ${BDIR}/dist_mac/slim-sensor pushd ${BDIR}/dist_mac ln -s slim docker-slim popd pushd ${BDIR} if hash zip 2> /dev/null; then zip -r dist_mac.zip dist_mac -x "*.DS_Store" fi popd rm -rfv ${BDIR}/dist_linux mkdir ${BDIR}/dist_linux cp ${BDIR}/bin/linux/slim ${BDIR}/dist_linux/slim cp ${BDIR}/bin/linux/slim-sensor ${BDIR}/dist_linux/slim-sensor pushd ${BDIR}/dist_linux ln -s slim docker-slim popd pushd ${BDIR} tar -czvf dist_linux.tar.gz dist_linux popd rm -rfv $BDIR/dist_linux_arm mkdir $BDIR/dist_linux_arm cp $BDIR/bin/linux_arm/slim $BDIR/dist_linux_arm/slim cp $BDIR/bin/linux_arm/slim-sensor $BDIR/dist_linux_arm/slim-sensor pushd ${BDIR}/dist_linux_arm ln -s slim docker-slim popd pushd ${BDIR} tar -czvf dist_linux_arm.tar.gz dist_linux_arm popd rm -rfv $BDIR/dist_linux_arm64 mkdir $BDIR/dist_linux_arm64 cp $BDIR/bin/linux_arm64/slim $BDIR/dist_linux_arm64/slim cp $BDIR/bin/linux_arm64/slim-sensor $BDIR/dist_linux_arm64/slim-sensor pushd ${BDIR}/dist_linux_arm64 ln -s slim docker-slim popd pushd ${BDIR} tar -czvf dist_linux_arm64.tar.gz dist_linux_arm64 popd rm -rfv ${BDIR}/bin ================================================ FILE: scripts/src.cleanup.sh ================================================ #!/usr/bin/env bash set -e SOURCE="${BASH_SOURCE[0]}" while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done BDIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )" rm -rfv $BDIR/_gopath rm -rfv $BDIR/dist_linux rm -fv $BDIR/dist_linux.tar.gz rm -rfv $BDIR/dist_linux_arm rm -fv $BDIR/dist_linux_arm.tar.gz rm -rfv $BDIR/dist_linux_arm64 rm -fv $BDIR/dist_linux_arm64.tar.gz rm -rfv $BDIR/dist_mac rm -fv $BDIR/dist_mac.zip rm -rfv $BDIR/dist_mac_m1 rm -fv $BDIR/dist_mac_m1.zip rm -rfv $BDIR/bin ================================================ FILE: scripts/src.fmt.sh ================================================ #!/usr/bin/env bash set -e SOURCE="${BASH_SOURCE[0]}" while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done BDIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )" pushd ${BDIR}/cmd gofmt -l -w -s . popd pushd ${BDIR}/pkg gofmt -l -w -s . popd ================================================ FILE: scripts/src.inspect.sh ================================================ #!/usr/bin/env bash set -e SOURCE="${BASH_SOURCE[0]}" while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done BDIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )" export GOOS=linux export GOARCH=amd64 pushd ${BDIR}/cmd go vet ./... go vet -vettool=$(which shadow) ./... golint ./... popd pushd ${BDIR}/pkg go vet ./... go vet -vettool=$(which shadow) ./... golint ./... popd ================================================ FILE: scripts/src.test.sh ================================================ #!/usr/bin/env bash set -e SOURCE="${BASH_SOURCE[0]}" while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done BDIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )" pushd ${BDIR} set -x go generate ./... go test -v -count 10 ${GO_TEST_FLAGS} ./... popd ================================================ FILE: scripts/tools.get.sh ================================================ #!/usr/bin/env bash #tmp until Go has better support for installing tools with modules export GO111MODULE=off go get -u golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow if ! which golint > /dev/null; then echo "Tools - Installing golint..." go get -u golang.org/x/lint/golint fi if ! which license-bill-of-materials > /dev/null; then echo "Tools - Installing bom tool..." go get -v -u github.com/cloudimmunity/license-bill-of-materials fi ================================================ FILE: scripts/uninstall-slim.sh ================================================ #!/usr/bin/env bash function uninstall_slim() { local VER="" # /usr/local/bin should be present on Linux and macOS hosts. Just be sure. if [ -d /usr/local/bin ]; then VER=$(slim --version | cut -d'|' -f3) echo " - Uninstalling version - ${VER}" echo " - Removing slim slim binaries from /usr/local/bin" rm /usr/local/bin/slim rm /usr/local/bin/slim-sensor echo " - Removing local state directory" rm -rfv /tmp/slim-state echo " - Removing state volume" docker volume rm slim-state echo " - Removing sensor volume" docker volume rm slim-sensor.${VER} else echo "ERROR! /usr/local/bin is not present. Uninstall aborted." exit 1 fi } echo "Slim scripted uninstall" if [ "$(id -u)" -ne 0 ]; then echo "ERROR! You must run this script as root." exit 1 fi uninstall_slim ================================================ FILE: test/debug.bats ================================================ setup() { load 'test_helper/bats-support/load' load 'test_helper/bats-assert/load' DIR="$( cd "$( dirname "$BATS_TEST_FILENAME" )" >/dev/null 2>&1 && pwd )" PATH="$DIR/../bin:$PATH" docker run --ipc 'shareable' --name debug_me --rm -d alpine tail -f /dev/null } teardown() { docker rm -f debug_me } function debug_command_with_default_image_works { # @test run slim debug debug_me -- ps assert [ $status -eq 0 ] assert_output --partial '1 root 0:00 tail -f /dev/null' } function debug_command_with_custom_image_works { # @test run slim debug --debug-image busybox debug_me -- cat /proc/1/root/etc/os-release assert [ $status -eq 0 ] assert_output --partial 'NAME="Alpine Linux"' assert_output --partial 'ID=alpine' } ================================================ FILE: test/e2e-tests.mk ================================================ ARCH ?= $(shell uname -m) DSLIM_EXAMPLES_DIR ?= '$(CURDIR)/../examples' GO_TEST_FLAGS = # E.g.: make test-e2e-sensor GO_TEST_FLAGS='-run TestXyz' # run sensor only e2e tests test-e2e-sensor: go generate github.com/slimtoolkit/slim/pkg/appbom go test -v -tags e2e -count 5 -timeout 30m $(GO_TEST_FLAGS) $(CURDIR)/pkg/app/sensor # run all e2e tests at once .PHONY: test-e2e-all: test-e2e-compose test-e2e-all: test-e2e-distroless test-e2e-all: test-e2e-dotnet test-e2e-all: test-e2e-elixir test-e2e-all: test-e2e-golang test-e2e-all: test-e2e-haskell test-e2e-all: test-e2e-http-probe test-e2e-all: test-e2e-image-edit test-e2e-all: test-e2e-java test-e2e-all: test-e2e-node test-e2e-all: test-e2e-php test-e2e-all: test-e2e-python test-e2e-all: test-e2e-ruby test-e2e-all: test-e2e-rust test-e2e-all: @echo "OK" .PHONY: test-e2e-compose: make -f $(DSLIM_EXAMPLES_DIR)/node_compose/Makefile test-e2e make -f $(DSLIM_EXAMPLES_DIR)/node_redis_compose/Makefile test-e2e make -f $(DSLIM_EXAMPLES_DIR)/vuejs-compose/Makefile test-e2e .PHONY: test-e2e-distroless: make -f $(DSLIM_EXAMPLES_DIR)/distroless/nodejs/Makefile test-e2e make -f $(DSLIM_EXAMPLES_DIR)/distroless/python2.7/Makefile test-e2e .PHONY: test-e2e-dotnet: [ "${ARCH}" = "arm64" ] || make -f $(DSLIM_EXAMPLES_DIR)/dotnet_alpine/Makefile test-e2e [ "${ARCH}" = "arm64" ] || make -f $(DSLIM_EXAMPLES_DIR)/dotnet_aspnetcore_ubuntu/Makefile test-e2e [ "${ARCH}" = "arm64" ] || make -f $(DSLIM_EXAMPLES_DIR)/dotnet_debian/Makefile test-e2e [ "${ARCH}" = "arm64" ] || make -f $(DSLIM_EXAMPLES_DIR)/dotnet_ubuntu/Makefile test-e2e .PHONY: test-e2e-elixir: true || make -f $(DSLIM_EXAMPLES_DIR)/elixir_phx_standard/Makefile test-e2e # TODO: Broken one! .PHONY: test-e2e-golang: make -f $(DSLIM_EXAMPLES_DIR)/golang_alpine/Makefile test-e2e make -f $(DSLIM_EXAMPLES_DIR)/golang_centos/Makefile test-e2e make -f $(DSLIM_EXAMPLES_DIR)/golang_gin_standard/Makefile test-e2e make -f $(DSLIM_EXAMPLES_DIR)/golang_standard/Makefile test-e2e make -f $(DSLIM_EXAMPLES_DIR)/golang_ubuntu/Makefile test-e2e .PHONY: test-e2e-haskell: make -f $(DSLIM_EXAMPLES_DIR)/haskell_scotty_standard/Makefile test-e2e .PHONY: test-e2e-http-probe: make -f $(DSLIM_EXAMPLES_DIR)/http_probe_cmd_file/Makefile test-e2e make -f $(DSLIM_EXAMPLES_DIR)/http_probe_swagger/Makefile test-e2e make -f $(DSLIM_EXAMPLES_DIR)/http_probe_swagger_http2_https/Makefile test-e2e make -f $(DSLIM_EXAMPLES_DIR)/http_probe_swagger_http2_plain/Makefile test-e2e .PHONY: test-e2e-image-edit: make -f $(DSLIM_EXAMPLES_DIR)/image_edit_basic/Makefile test-e2e .PHONY: test-e2e-java: make -f $(DSLIM_EXAMPLES_DIR)/java_corretto/Makefile test-e2e make -f $(DSLIM_EXAMPLES_DIR)/java_standard/Makefile test-e2e .PHONY: test-e2e-node: make -f $(DSLIM_EXAMPLES_DIR)/node17_express_yarn_standard/Makefile test-e2e make -f $(DSLIM_EXAMPLES_DIR)/node_alpine/Makefile test-e2e make -f $(DSLIM_EXAMPLES_DIR)/node_ubuntu/Makefile test-e2e make -f $(DSLIM_EXAMPLES_DIR)/node_ubuntu_focal/Makefile test-e2e .PHONY: test-e2e-php: make -f $(DSLIM_EXAMPLES_DIR)/php7_fpm_fastcgi/Makefile test-e2e make -f $(DSLIM_EXAMPLES_DIR)/php7_fpm_nginx/Makefile test-e2e make -f $(DSLIM_EXAMPLES_DIR)/php7_builtin_web_server/Makefile test-e2e .PHONY: test-e2e-python: make -f $(DSLIM_EXAMPLES_DIR)/python2_flask_alpine/Makefile test-e2e make -f $(DSLIM_EXAMPLES_DIR)/python2_flask_standard/Makefile test-e2e make -f $(DSLIM_EXAMPLES_DIR)/python_ubuntu_18.04_user/Makefile test-e2e make -f $(DSLIM_EXAMPLES_DIR)/python_ubuntu_18_py27_from_dockerfile/Makefile test-e2e .PHONY: test-e2e-ruby: [ "${ARCH}" = "arm64" ] || make -f $(DSLIM_EXAMPLES_DIR)/ruby2_rails5_alpine/Makefile test-e2e [ "${ARCH}" = "arm64" ] || make -f $(DSLIM_EXAMPLES_DIR)/ruby2_rails5_alpine_puma/Makefile test-e2e [ "${ARCH}" = "arm64" ] || make -f $(DSLIM_EXAMPLES_DIR)/ruby2_rails5_alpine_puma_sh/Makefile test-e2e [ "${ARCH}" = "arm64" ] || make -f $(DSLIM_EXAMPLES_DIR)/ruby2_rails5_alpine_unicorn_rails/Makefile test-e2e [ "${ARCH}" = "arm64" ] || make -f $(DSLIM_EXAMPLES_DIR)/ruby2_rails5_standard/Makefile test-e2e [ "${ARCH}" = "arm64" ] || make -f $(DSLIM_EXAMPLES_DIR)/ruby2_rails5_standard_puma/Makefile test-e2e [ "${ARCH}" = "arm64" ] || make -f $(DSLIM_EXAMPLES_DIR)/ruby2_sinatra_ubuntu/Makefile test-e2e .PHONY: test-e2e-rust: [ "${GITHUB_ACTIONS}" = "true" ] || make -f $(DSLIM_EXAMPLES_DIR)/rust_standard/Makefile test-e2e # TODO: HTTP probe always fails on CI - need to investigate more. ================================================ FILE: vendor/github.com/Azure/go-ansiterm/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2015 Microsoft Corporation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/Azure/go-ansiterm/README.md ================================================ # go-ansiterm This is a cross platform Ansi Terminal Emulation library. It reads a stream of Ansi characters and produces the appropriate function calls. The results of the function calls are platform dependent. For example the parser might receive "ESC, [, A" as a stream of three characters. This is the code for Cursor Up (http://www.vt100.net/docs/vt510-rm/CUU). The parser then calls the cursor up function (CUU()) on an event handler. The event handler determines what platform specific work must be done to cause the cursor to move up one position. The parser (parser.go) is a partial implementation of this state machine (http://vt100.net/emu/vt500_parser.png). There are also two event handler implementations, one for tests (test_event_handler.go) to validate that the expected events are being produced and called, the other is a Windows implementation (winterm/win_event_handler.go). See parser_test.go for examples exercising the state machine and generating appropriate function calls. ----- This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. ================================================ FILE: vendor/github.com/Azure/go-ansiterm/SECURITY.md ================================================ ## Security Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. ## Reporting Security Issues **Please do not report security vulnerabilities through public GitHub issues.** Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) * Full paths of source file(s) related to the manifestation of the issue * The location of the affected source code (tag/branch/commit or direct URL) * Any special configuration required to reproduce the issue * Step-by-step instructions to reproduce the issue * Proof-of-concept or exploit code (if possible) * Impact of the issue, including how an attacker might exploit the issue This information will help us triage your report more quickly. If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. ## Preferred Languages We prefer all communications to be in English. ## Policy Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). ================================================ FILE: vendor/github.com/Azure/go-ansiterm/constants.go ================================================ package ansiterm const LogEnv = "DEBUG_TERMINAL" // ANSI constants // References: // -- http://www.ecma-international.org/publications/standards/Ecma-048.htm // -- http://man7.org/linux/man-pages/man4/console_codes.4.html // -- http://manpages.ubuntu.com/manpages/intrepid/man4/console_codes.4.html // -- http://en.wikipedia.org/wiki/ANSI_escape_code // -- http://vt100.net/emu/dec_ansi_parser // -- http://vt100.net/emu/vt500_parser.svg // -- http://invisible-island.net/xterm/ctlseqs/ctlseqs.html // -- http://www.inwap.com/pdp10/ansicode.txt const ( // ECMA-48 Set Graphics Rendition // Note: // -- Constants leading with an underscore (e.g., _ANSI_xxx) are unsupported or reserved // -- Fonts could possibly be supported via SetCurrentConsoleFontEx // -- Windows does not expose the per-window cursor (i.e., caret) blink times ANSI_SGR_RESET = 0 ANSI_SGR_BOLD = 1 ANSI_SGR_DIM = 2 _ANSI_SGR_ITALIC = 3 ANSI_SGR_UNDERLINE = 4 _ANSI_SGR_BLINKSLOW = 5 _ANSI_SGR_BLINKFAST = 6 ANSI_SGR_REVERSE = 7 _ANSI_SGR_INVISIBLE = 8 _ANSI_SGR_LINETHROUGH = 9 _ANSI_SGR_FONT_00 = 10 _ANSI_SGR_FONT_01 = 11 _ANSI_SGR_FONT_02 = 12 _ANSI_SGR_FONT_03 = 13 _ANSI_SGR_FONT_04 = 14 _ANSI_SGR_FONT_05 = 15 _ANSI_SGR_FONT_06 = 16 _ANSI_SGR_FONT_07 = 17 _ANSI_SGR_FONT_08 = 18 _ANSI_SGR_FONT_09 = 19 _ANSI_SGR_FONT_10 = 20 _ANSI_SGR_DOUBLEUNDERLINE = 21 ANSI_SGR_BOLD_DIM_OFF = 22 _ANSI_SGR_ITALIC_OFF = 23 ANSI_SGR_UNDERLINE_OFF = 24 _ANSI_SGR_BLINK_OFF = 25 _ANSI_SGR_RESERVED_00 = 26 ANSI_SGR_REVERSE_OFF = 27 _ANSI_SGR_INVISIBLE_OFF = 28 _ANSI_SGR_LINETHROUGH_OFF = 29 ANSI_SGR_FOREGROUND_BLACK = 30 ANSI_SGR_FOREGROUND_RED = 31 ANSI_SGR_FOREGROUND_GREEN = 32 ANSI_SGR_FOREGROUND_YELLOW = 33 ANSI_SGR_FOREGROUND_BLUE = 34 ANSI_SGR_FOREGROUND_MAGENTA = 35 ANSI_SGR_FOREGROUND_CYAN = 36 ANSI_SGR_FOREGROUND_WHITE = 37 _ANSI_SGR_RESERVED_01 = 38 ANSI_SGR_FOREGROUND_DEFAULT = 39 ANSI_SGR_BACKGROUND_BLACK = 40 ANSI_SGR_BACKGROUND_RED = 41 ANSI_SGR_BACKGROUND_GREEN = 42 ANSI_SGR_BACKGROUND_YELLOW = 43 ANSI_SGR_BACKGROUND_BLUE = 44 ANSI_SGR_BACKGROUND_MAGENTA = 45 ANSI_SGR_BACKGROUND_CYAN = 46 ANSI_SGR_BACKGROUND_WHITE = 47 _ANSI_SGR_RESERVED_02 = 48 ANSI_SGR_BACKGROUND_DEFAULT = 49 // 50 - 65: Unsupported ANSI_MAX_CMD_LENGTH = 4096 MAX_INPUT_EVENTS = 128 DEFAULT_WIDTH = 80 DEFAULT_HEIGHT = 24 ANSI_BEL = 0x07 ANSI_BACKSPACE = 0x08 ANSI_TAB = 0x09 ANSI_LINE_FEED = 0x0A ANSI_VERTICAL_TAB = 0x0B ANSI_FORM_FEED = 0x0C ANSI_CARRIAGE_RETURN = 0x0D ANSI_ESCAPE_PRIMARY = 0x1B ANSI_ESCAPE_SECONDARY = 0x5B ANSI_OSC_STRING_ENTRY = 0x5D ANSI_COMMAND_FIRST = 0x40 ANSI_COMMAND_LAST = 0x7E DCS_ENTRY = 0x90 CSI_ENTRY = 0x9B OSC_STRING = 0x9D ANSI_PARAMETER_SEP = ";" ANSI_CMD_G0 = '(' ANSI_CMD_G1 = ')' ANSI_CMD_G2 = '*' ANSI_CMD_G3 = '+' ANSI_CMD_DECPNM = '>' ANSI_CMD_DECPAM = '=' ANSI_CMD_OSC = ']' ANSI_CMD_STR_TERM = '\\' KEY_CONTROL_PARAM_2 = ";2" KEY_CONTROL_PARAM_3 = ";3" KEY_CONTROL_PARAM_4 = ";4" KEY_CONTROL_PARAM_5 = ";5" KEY_CONTROL_PARAM_6 = ";6" KEY_CONTROL_PARAM_7 = ";7" KEY_CONTROL_PARAM_8 = ";8" KEY_ESC_CSI = "\x1B[" KEY_ESC_N = "\x1BN" KEY_ESC_O = "\x1BO" FILL_CHARACTER = ' ' ) func getByteRange(start byte, end byte) []byte { bytes := make([]byte, 0, 32) for i := start; i <= end; i++ { bytes = append(bytes, byte(i)) } return bytes } var toGroundBytes = getToGroundBytes() var executors = getExecuteBytes() // SPACE 20+A0 hex Always and everywhere a blank space // Intermediate 20-2F hex !"#$%&'()*+,-./ var intermeds = getByteRange(0x20, 0x2F) // Parameters 30-3F hex 0123456789:;<=>? // CSI Parameters 30-39, 3B hex 0123456789; var csiParams = getByteRange(0x30, 0x3F) var csiCollectables = append(getByteRange(0x30, 0x39), getByteRange(0x3B, 0x3F)...) // Uppercase 40-5F hex @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ var upperCase = getByteRange(0x40, 0x5F) // Lowercase 60-7E hex `abcdefghijlkmnopqrstuvwxyz{|}~ var lowerCase = getByteRange(0x60, 0x7E) // Alphabetics 40-7E hex (all of upper and lower case) var alphabetics = append(upperCase, lowerCase...) var printables = getByteRange(0x20, 0x7F) var escapeIntermediateToGroundBytes = getByteRange(0x30, 0x7E) var escapeToGroundBytes = getEscapeToGroundBytes() // See http://www.vt100.net/emu/vt500_parser.png for description of the complex // byte ranges below func getEscapeToGroundBytes() []byte { escapeToGroundBytes := getByteRange(0x30, 0x4F) escapeToGroundBytes = append(escapeToGroundBytes, getByteRange(0x51, 0x57)...) escapeToGroundBytes = append(escapeToGroundBytes, 0x59) escapeToGroundBytes = append(escapeToGroundBytes, 0x5A) escapeToGroundBytes = append(escapeToGroundBytes, 0x5C) escapeToGroundBytes = append(escapeToGroundBytes, getByteRange(0x60, 0x7E)...) return escapeToGroundBytes } func getExecuteBytes() []byte { executeBytes := getByteRange(0x00, 0x17) executeBytes = append(executeBytes, 0x19) executeBytes = append(executeBytes, getByteRange(0x1C, 0x1F)...) return executeBytes } func getToGroundBytes() []byte { groundBytes := []byte{0x18} groundBytes = append(groundBytes, 0x1A) groundBytes = append(groundBytes, getByteRange(0x80, 0x8F)...) groundBytes = append(groundBytes, getByteRange(0x91, 0x97)...) groundBytes = append(groundBytes, 0x99) groundBytes = append(groundBytes, 0x9A) groundBytes = append(groundBytes, 0x9C) return groundBytes } // Delete 7F hex Always and everywhere ignored // C1 Control 80-9F hex 32 additional control characters // G1 Displayable A1-FE hex 94 additional displayable characters // Special A0+FF hex Same as SPACE and DELETE ================================================ FILE: vendor/github.com/Azure/go-ansiterm/context.go ================================================ package ansiterm type ansiContext struct { currentChar byte paramBuffer []byte interBuffer []byte } ================================================ FILE: vendor/github.com/Azure/go-ansiterm/csi_entry_state.go ================================================ package ansiterm type csiEntryState struct { baseState } func (csiState csiEntryState) Handle(b byte) (s state, e error) { csiState.parser.logf("CsiEntry::Handle %#x", b) nextState, err := csiState.baseState.Handle(b) if nextState != nil || err != nil { return nextState, err } switch { case sliceContains(alphabetics, b): return csiState.parser.ground, nil case sliceContains(csiCollectables, b): return csiState.parser.csiParam, nil case sliceContains(executors, b): return csiState, csiState.parser.execute() } return csiState, nil } func (csiState csiEntryState) Transition(s state) error { csiState.parser.logf("CsiEntry::Transition %s --> %s", csiState.Name(), s.Name()) csiState.baseState.Transition(s) switch s { case csiState.parser.ground: return csiState.parser.csiDispatch() case csiState.parser.csiParam: switch { case sliceContains(csiParams, csiState.parser.context.currentChar): csiState.parser.collectParam() case sliceContains(intermeds, csiState.parser.context.currentChar): csiState.parser.collectInter() } } return nil } func (csiState csiEntryState) Enter() error { csiState.parser.clear() return nil } ================================================ FILE: vendor/github.com/Azure/go-ansiterm/csi_param_state.go ================================================ package ansiterm type csiParamState struct { baseState } func (csiState csiParamState) Handle(b byte) (s state, e error) { csiState.parser.logf("CsiParam::Handle %#x", b) nextState, err := csiState.baseState.Handle(b) if nextState != nil || err != nil { return nextState, err } switch { case sliceContains(alphabetics, b): return csiState.parser.ground, nil case sliceContains(csiCollectables, b): csiState.parser.collectParam() return csiState, nil case sliceContains(executors, b): return csiState, csiState.parser.execute() } return csiState, nil } func (csiState csiParamState) Transition(s state) error { csiState.parser.logf("CsiParam::Transition %s --> %s", csiState.Name(), s.Name()) csiState.baseState.Transition(s) switch s { case csiState.parser.ground: return csiState.parser.csiDispatch() } return nil } ================================================ FILE: vendor/github.com/Azure/go-ansiterm/escape_intermediate_state.go ================================================ package ansiterm type escapeIntermediateState struct { baseState } func (escState escapeIntermediateState) Handle(b byte) (s state, e error) { escState.parser.logf("escapeIntermediateState::Handle %#x", b) nextState, err := escState.baseState.Handle(b) if nextState != nil || err != nil { return nextState, err } switch { case sliceContains(intermeds, b): return escState, escState.parser.collectInter() case sliceContains(executors, b): return escState, escState.parser.execute() case sliceContains(escapeIntermediateToGroundBytes, b): return escState.parser.ground, nil } return escState, nil } func (escState escapeIntermediateState) Transition(s state) error { escState.parser.logf("escapeIntermediateState::Transition %s --> %s", escState.Name(), s.Name()) escState.baseState.Transition(s) switch s { case escState.parser.ground: return escState.parser.escDispatch() } return nil } ================================================ FILE: vendor/github.com/Azure/go-ansiterm/escape_state.go ================================================ package ansiterm type escapeState struct { baseState } func (escState escapeState) Handle(b byte) (s state, e error) { escState.parser.logf("escapeState::Handle %#x", b) nextState, err := escState.baseState.Handle(b) if nextState != nil || err != nil { return nextState, err } switch { case b == ANSI_ESCAPE_SECONDARY: return escState.parser.csiEntry, nil case b == ANSI_OSC_STRING_ENTRY: return escState.parser.oscString, nil case sliceContains(executors, b): return escState, escState.parser.execute() case sliceContains(escapeToGroundBytes, b): return escState.parser.ground, nil case sliceContains(intermeds, b): return escState.parser.escapeIntermediate, nil } return escState, nil } func (escState escapeState) Transition(s state) error { escState.parser.logf("Escape::Transition %s --> %s", escState.Name(), s.Name()) escState.baseState.Transition(s) switch s { case escState.parser.ground: return escState.parser.escDispatch() case escState.parser.escapeIntermediate: return escState.parser.collectInter() } return nil } func (escState escapeState) Enter() error { escState.parser.clear() return nil } ================================================ FILE: vendor/github.com/Azure/go-ansiterm/event_handler.go ================================================ package ansiterm type AnsiEventHandler interface { // Print Print(b byte) error // Execute C0 commands Execute(b byte) error // CUrsor Up CUU(int) error // CUrsor Down CUD(int) error // CUrsor Forward CUF(int) error // CUrsor Backward CUB(int) error // Cursor to Next Line CNL(int) error // Cursor to Previous Line CPL(int) error // Cursor Horizontal position Absolute CHA(int) error // Vertical line Position Absolute VPA(int) error // CUrsor Position CUP(int, int) error // Horizontal and Vertical Position (depends on PUM) HVP(int, int) error // Text Cursor Enable Mode DECTCEM(bool) error // Origin Mode DECOM(bool) error // 132 Column Mode DECCOLM(bool) error // Erase in Display ED(int) error // Erase in Line EL(int) error // Insert Line IL(int) error // Delete Line DL(int) error // Insert Character ICH(int) error // Delete Character DCH(int) error // Set Graphics Rendition SGR([]int) error // Pan Down SU(int) error // Pan Up SD(int) error // Device Attributes DA([]string) error // Set Top and Bottom Margins DECSTBM(int, int) error // Index IND() error // Reverse Index RI() error // Flush updates from previous commands Flush() error } ================================================ FILE: vendor/github.com/Azure/go-ansiterm/ground_state.go ================================================ package ansiterm type groundState struct { baseState } func (gs groundState) Handle(b byte) (s state, e error) { gs.parser.context.currentChar = b nextState, err := gs.baseState.Handle(b) if nextState != nil || err != nil { return nextState, err } switch { case sliceContains(printables, b): return gs, gs.parser.print() case sliceContains(executors, b): return gs, gs.parser.execute() } return gs, nil } ================================================ FILE: vendor/github.com/Azure/go-ansiterm/osc_string_state.go ================================================ package ansiterm type oscStringState struct { baseState } func (oscState oscStringState) Handle(b byte) (s state, e error) { oscState.parser.logf("OscString::Handle %#x", b) nextState, err := oscState.baseState.Handle(b) if nextState != nil || err != nil { return nextState, err } switch { case isOscStringTerminator(b): return oscState.parser.ground, nil } return oscState, nil } // See below for OSC string terminators for linux // http://man7.org/linux/man-pages/man4/console_codes.4.html func isOscStringTerminator(b byte) bool { if b == ANSI_BEL || b == 0x5C { return true } return false } ================================================ FILE: vendor/github.com/Azure/go-ansiterm/parser.go ================================================ package ansiterm import ( "errors" "log" "os" ) type AnsiParser struct { currState state eventHandler AnsiEventHandler context *ansiContext csiEntry state csiParam state dcsEntry state escape state escapeIntermediate state error state ground state oscString state stateMap []state logf func(string, ...interface{}) } type Option func(*AnsiParser) func WithLogf(f func(string, ...interface{})) Option { return func(ap *AnsiParser) { ap.logf = f } } func CreateParser(initialState string, evtHandler AnsiEventHandler, opts ...Option) *AnsiParser { ap := &AnsiParser{ eventHandler: evtHandler, context: &ansiContext{}, } for _, o := range opts { o(ap) } if isDebugEnv := os.Getenv(LogEnv); isDebugEnv == "1" { logFile, _ := os.Create("ansiParser.log") logger := log.New(logFile, "", log.LstdFlags) if ap.logf != nil { l := ap.logf ap.logf = func(s string, v ...interface{}) { l(s, v...) logger.Printf(s, v...) } } else { ap.logf = logger.Printf } } if ap.logf == nil { ap.logf = func(string, ...interface{}) {} } ap.csiEntry = csiEntryState{baseState{name: "CsiEntry", parser: ap}} ap.csiParam = csiParamState{baseState{name: "CsiParam", parser: ap}} ap.dcsEntry = dcsEntryState{baseState{name: "DcsEntry", parser: ap}} ap.escape = escapeState{baseState{name: "Escape", parser: ap}} ap.escapeIntermediate = escapeIntermediateState{baseState{name: "EscapeIntermediate", parser: ap}} ap.error = errorState{baseState{name: "Error", parser: ap}} ap.ground = groundState{baseState{name: "Ground", parser: ap}} ap.oscString = oscStringState{baseState{name: "OscString", parser: ap}} ap.stateMap = []state{ ap.csiEntry, ap.csiParam, ap.dcsEntry, ap.escape, ap.escapeIntermediate, ap.error, ap.ground, ap.oscString, } ap.currState = getState(initialState, ap.stateMap) ap.logf("CreateParser: parser %p", ap) return ap } func getState(name string, states []state) state { for _, el := range states { if el.Name() == name { return el } } return nil } func (ap *AnsiParser) Parse(bytes []byte) (int, error) { for i, b := range bytes { if err := ap.handle(b); err != nil { return i, err } } return len(bytes), ap.eventHandler.Flush() } func (ap *AnsiParser) handle(b byte) error { ap.context.currentChar = b newState, err := ap.currState.Handle(b) if err != nil { return err } if newState == nil { ap.logf("WARNING: newState is nil") return errors.New("New state of 'nil' is invalid.") } if newState != ap.currState { if err := ap.changeState(newState); err != nil { return err } } return nil } func (ap *AnsiParser) changeState(newState state) error { ap.logf("ChangeState %s --> %s", ap.currState.Name(), newState.Name()) // Exit old state if err := ap.currState.Exit(); err != nil { ap.logf("Exit state '%s' failed with : '%v'", ap.currState.Name(), err) return err } // Perform transition action if err := ap.currState.Transition(newState); err != nil { ap.logf("Transition from '%s' to '%s' failed with: '%v'", ap.currState.Name(), newState.Name, err) return err } // Enter new state if err := newState.Enter(); err != nil { ap.logf("Enter state '%s' failed with: '%v'", newState.Name(), err) return err } ap.currState = newState return nil } ================================================ FILE: vendor/github.com/Azure/go-ansiterm/parser_action_helpers.go ================================================ package ansiterm import ( "strconv" ) func parseParams(bytes []byte) ([]string, error) { paramBuff := make([]byte, 0, 0) params := []string{} for _, v := range bytes { if v == ';' { if len(paramBuff) > 0 { // Completed parameter, append it to the list s := string(paramBuff) params = append(params, s) paramBuff = make([]byte, 0, 0) } } else { paramBuff = append(paramBuff, v) } } // Last parameter may not be terminated with ';' if len(paramBuff) > 0 { s := string(paramBuff) params = append(params, s) } return params, nil } func parseCmd(context ansiContext) (string, error) { return string(context.currentChar), nil } func getInt(params []string, dflt int) int { i := getInts(params, 1, dflt)[0] return i } func getInts(params []string, minCount int, dflt int) []int { ints := []int{} for _, v := range params { i, _ := strconv.Atoi(v) // Zero is mapped to the default value in VT100. if i == 0 { i = dflt } ints = append(ints, i) } if len(ints) < minCount { remaining := minCount - len(ints) for i := 0; i < remaining; i++ { ints = append(ints, dflt) } } return ints } func (ap *AnsiParser) modeDispatch(param string, set bool) error { switch param { case "?3": return ap.eventHandler.DECCOLM(set) case "?6": return ap.eventHandler.DECOM(set) case "?25": return ap.eventHandler.DECTCEM(set) } return nil } func (ap *AnsiParser) hDispatch(params []string) error { if len(params) == 1 { return ap.modeDispatch(params[0], true) } return nil } func (ap *AnsiParser) lDispatch(params []string) error { if len(params) == 1 { return ap.modeDispatch(params[0], false) } return nil } func getEraseParam(params []string) int { param := getInt(params, 0) if param < 0 || 3 < param { param = 0 } return param } ================================================ FILE: vendor/github.com/Azure/go-ansiterm/parser_actions.go ================================================ package ansiterm func (ap *AnsiParser) collectParam() error { currChar := ap.context.currentChar ap.logf("collectParam %#x", currChar) ap.context.paramBuffer = append(ap.context.paramBuffer, currChar) return nil } func (ap *AnsiParser) collectInter() error { currChar := ap.context.currentChar ap.logf("collectInter %#x", currChar) ap.context.paramBuffer = append(ap.context.interBuffer, currChar) return nil } func (ap *AnsiParser) escDispatch() error { cmd, _ := parseCmd(*ap.context) intermeds := ap.context.interBuffer ap.logf("escDispatch currentChar: %#x", ap.context.currentChar) ap.logf("escDispatch: %v(%v)", cmd, intermeds) switch cmd { case "D": // IND return ap.eventHandler.IND() case "E": // NEL, equivalent to CRLF err := ap.eventHandler.Execute(ANSI_CARRIAGE_RETURN) if err == nil { err = ap.eventHandler.Execute(ANSI_LINE_FEED) } return err case "M": // RI return ap.eventHandler.RI() } return nil } func (ap *AnsiParser) csiDispatch() error { cmd, _ := parseCmd(*ap.context) params, _ := parseParams(ap.context.paramBuffer) ap.logf("Parsed params: %v with length: %d", params, len(params)) ap.logf("csiDispatch: %v(%v)", cmd, params) switch cmd { case "@": return ap.eventHandler.ICH(getInt(params, 1)) case "A": return ap.eventHandler.CUU(getInt(params, 1)) case "B": return ap.eventHandler.CUD(getInt(params, 1)) case "C": return ap.eventHandler.CUF(getInt(params, 1)) case "D": return ap.eventHandler.CUB(getInt(params, 1)) case "E": return ap.eventHandler.CNL(getInt(params, 1)) case "F": return ap.eventHandler.CPL(getInt(params, 1)) case "G": return ap.eventHandler.CHA(getInt(params, 1)) case "H": ints := getInts(params, 2, 1) x, y := ints[0], ints[1] return ap.eventHandler.CUP(x, y) case "J": param := getEraseParam(params) return ap.eventHandler.ED(param) case "K": param := getEraseParam(params) return ap.eventHandler.EL(param) case "L": return ap.eventHandler.IL(getInt(params, 1)) case "M": return ap.eventHandler.DL(getInt(params, 1)) case "P": return ap.eventHandler.DCH(getInt(params, 1)) case "S": return ap.eventHandler.SU(getInt(params, 1)) case "T": return ap.eventHandler.SD(getInt(params, 1)) case "c": return ap.eventHandler.DA(params) case "d": return ap.eventHandler.VPA(getInt(params, 1)) case "f": ints := getInts(params, 2, 1) x, y := ints[0], ints[1] return ap.eventHandler.HVP(x, y) case "h": return ap.hDispatch(params) case "l": return ap.lDispatch(params) case "m": return ap.eventHandler.SGR(getInts(params, 1, 0)) case "r": ints := getInts(params, 2, 1) top, bottom := ints[0], ints[1] return ap.eventHandler.DECSTBM(top, bottom) default: ap.logf("ERROR: Unsupported CSI command: '%s', with full context: %v", cmd, ap.context) return nil } } func (ap *AnsiParser) print() error { return ap.eventHandler.Print(ap.context.currentChar) } func (ap *AnsiParser) clear() error { ap.context = &ansiContext{} return nil } func (ap *AnsiParser) execute() error { return ap.eventHandler.Execute(ap.context.currentChar) } ================================================ FILE: vendor/github.com/Azure/go-ansiterm/states.go ================================================ package ansiterm type stateID int type state interface { Enter() error Exit() error Handle(byte) (state, error) Name() string Transition(state) error } type baseState struct { name string parser *AnsiParser } func (base baseState) Enter() error { return nil } func (base baseState) Exit() error { return nil } func (base baseState) Handle(b byte) (s state, e error) { switch { case b == CSI_ENTRY: return base.parser.csiEntry, nil case b == DCS_ENTRY: return base.parser.dcsEntry, nil case b == ANSI_ESCAPE_PRIMARY: return base.parser.escape, nil case b == OSC_STRING: return base.parser.oscString, nil case sliceContains(toGroundBytes, b): return base.parser.ground, nil } return nil, nil } func (base baseState) Name() string { return base.name } func (base baseState) Transition(s state) error { if s == base.parser.ground { execBytes := []byte{0x18} execBytes = append(execBytes, 0x1A) execBytes = append(execBytes, getByteRange(0x80, 0x8F)...) execBytes = append(execBytes, getByteRange(0x91, 0x97)...) execBytes = append(execBytes, 0x99) execBytes = append(execBytes, 0x9A) if sliceContains(execBytes, base.parser.context.currentChar) { return base.parser.execute() } } return nil } type dcsEntryState struct { baseState } type errorState struct { baseState } ================================================ FILE: vendor/github.com/Azure/go-ansiterm/utilities.go ================================================ package ansiterm import ( "strconv" ) func sliceContains(bytes []byte, b byte) bool { for _, v := range bytes { if v == b { return true } } return false } func convertBytesToInteger(bytes []byte) int { s := string(bytes) i, _ := strconv.Atoi(s) return i } ================================================ FILE: vendor/github.com/Azure/go-ansiterm/winterm/ansi.go ================================================ // +build windows package winterm import ( "fmt" "os" "strconv" "strings" "syscall" "github.com/Azure/go-ansiterm" windows "golang.org/x/sys/windows" ) // Windows keyboard constants // See https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx. const ( VK_PRIOR = 0x21 // PAGE UP key VK_NEXT = 0x22 // PAGE DOWN key VK_END = 0x23 // END key VK_HOME = 0x24 // HOME key VK_LEFT = 0x25 // LEFT ARROW key VK_UP = 0x26 // UP ARROW key VK_RIGHT = 0x27 // RIGHT ARROW key VK_DOWN = 0x28 // DOWN ARROW key VK_SELECT = 0x29 // SELECT key VK_PRINT = 0x2A // PRINT key VK_EXECUTE = 0x2B // EXECUTE key VK_SNAPSHOT = 0x2C // PRINT SCREEN key VK_INSERT = 0x2D // INS key VK_DELETE = 0x2E // DEL key VK_HELP = 0x2F // HELP key VK_F1 = 0x70 // F1 key VK_F2 = 0x71 // F2 key VK_F3 = 0x72 // F3 key VK_F4 = 0x73 // F4 key VK_F5 = 0x74 // F5 key VK_F6 = 0x75 // F6 key VK_F7 = 0x76 // F7 key VK_F8 = 0x77 // F8 key VK_F9 = 0x78 // F9 key VK_F10 = 0x79 // F10 key VK_F11 = 0x7A // F11 key VK_F12 = 0x7B // F12 key RIGHT_ALT_PRESSED = 0x0001 LEFT_ALT_PRESSED = 0x0002 RIGHT_CTRL_PRESSED = 0x0004 LEFT_CTRL_PRESSED = 0x0008 SHIFT_PRESSED = 0x0010 NUMLOCK_ON = 0x0020 SCROLLLOCK_ON = 0x0040 CAPSLOCK_ON = 0x0080 ENHANCED_KEY = 0x0100 ) type ansiCommand struct { CommandBytes []byte Command string Parameters []string IsSpecial bool } func newAnsiCommand(command []byte) *ansiCommand { if isCharacterSelectionCmdChar(command[1]) { // Is Character Set Selection commands return &ansiCommand{ CommandBytes: command, Command: string(command), IsSpecial: true, } } // last char is command character lastCharIndex := len(command) - 1 ac := &ansiCommand{ CommandBytes: command, Command: string(command[lastCharIndex]), IsSpecial: false, } // more than a single escape if lastCharIndex != 0 { start := 1 // skip if double char escape sequence if command[0] == ansiterm.ANSI_ESCAPE_PRIMARY && command[1] == ansiterm.ANSI_ESCAPE_SECONDARY { start++ } // convert this to GetNextParam method ac.Parameters = strings.Split(string(command[start:lastCharIndex]), ansiterm.ANSI_PARAMETER_SEP) } return ac } func (ac *ansiCommand) paramAsSHORT(index int, defaultValue int16) int16 { if index < 0 || index >= len(ac.Parameters) { return defaultValue } param, err := strconv.ParseInt(ac.Parameters[index], 10, 16) if err != nil { return defaultValue } return int16(param) } func (ac *ansiCommand) String() string { return fmt.Sprintf("0x%v \"%v\" (\"%v\")", bytesToHex(ac.CommandBytes), ac.Command, strings.Join(ac.Parameters, "\",\"")) } // isAnsiCommandChar returns true if the passed byte falls within the range of ANSI commands. // See http://manpages.ubuntu.com/manpages/intrepid/man4/console_codes.4.html. func isAnsiCommandChar(b byte) bool { switch { case ansiterm.ANSI_COMMAND_FIRST <= b && b <= ansiterm.ANSI_COMMAND_LAST && b != ansiterm.ANSI_ESCAPE_SECONDARY: return true case b == ansiterm.ANSI_CMD_G1 || b == ansiterm.ANSI_CMD_OSC || b == ansiterm.ANSI_CMD_DECPAM || b == ansiterm.ANSI_CMD_DECPNM: // non-CSI escape sequence terminator return true case b == ansiterm.ANSI_CMD_STR_TERM || b == ansiterm.ANSI_BEL: // String escape sequence terminator return true } return false } func isXtermOscSequence(command []byte, current byte) bool { return (len(command) >= 2 && command[0] == ansiterm.ANSI_ESCAPE_PRIMARY && command[1] == ansiterm.ANSI_CMD_OSC && current != ansiterm.ANSI_BEL) } func isCharacterSelectionCmdChar(b byte) bool { return (b == ansiterm.ANSI_CMD_G0 || b == ansiterm.ANSI_CMD_G1 || b == ansiterm.ANSI_CMD_G2 || b == ansiterm.ANSI_CMD_G3) } // bytesToHex converts a slice of bytes to a human-readable string. func bytesToHex(b []byte) string { hex := make([]string, len(b)) for i, ch := range b { hex[i] = fmt.Sprintf("%X", ch) } return strings.Join(hex, "") } // ensureInRange adjusts the passed value, if necessary, to ensure it is within // the passed min / max range. func ensureInRange(n int16, min int16, max int16) int16 { if n < min { return min } else if n > max { return max } else { return n } } func GetStdFile(nFile int) (*os.File, uintptr) { var file *os.File // syscall uses negative numbers // windows package uses very big uint32 // Keep these switches split so we don't have to convert ints too much. switch uint32(nFile) { case windows.STD_INPUT_HANDLE: file = os.Stdin case windows.STD_OUTPUT_HANDLE: file = os.Stdout case windows.STD_ERROR_HANDLE: file = os.Stderr default: switch nFile { case syscall.STD_INPUT_HANDLE: file = os.Stdin case syscall.STD_OUTPUT_HANDLE: file = os.Stdout case syscall.STD_ERROR_HANDLE: file = os.Stderr default: panic(fmt.Errorf("Invalid standard handle identifier: %v", nFile)) } } fd, err := syscall.GetStdHandle(nFile) if err != nil { panic(fmt.Errorf("Invalid standard handle identifier: %v -- %v", nFile, err)) } return file, uintptr(fd) } ================================================ FILE: vendor/github.com/Azure/go-ansiterm/winterm/api.go ================================================ // +build windows package winterm import ( "fmt" "syscall" "unsafe" ) //=========================================================================================================== // IMPORTANT NOTE: // // The methods below make extensive use of the "unsafe" package to obtain the required pointers. // Beginning in Go 1.3, the garbage collector may release local variables (e.g., incoming arguments, stack // variables) the pointers reference *before* the API completes. // // As a result, in those cases, the code must hint that the variables remain in active by invoking the // dummy method "use" (see below). Newer versions of Go are planned to change the mechanism to no longer // require unsafe pointers. // // If you add or modify methods, ENSURE protection of local variables through the "use" builtin to inform // the garbage collector the variables remain in use if: // // -- The value is not a pointer (e.g., int32, struct) // -- The value is not referenced by the method after passing the pointer to Windows // // See http://golang.org/doc/go1.3. //=========================================================================================================== var ( kernel32DLL = syscall.NewLazyDLL("kernel32.dll") getConsoleCursorInfoProc = kernel32DLL.NewProc("GetConsoleCursorInfo") setConsoleCursorInfoProc = kernel32DLL.NewProc("SetConsoleCursorInfo") setConsoleCursorPositionProc = kernel32DLL.NewProc("SetConsoleCursorPosition") setConsoleModeProc = kernel32DLL.NewProc("SetConsoleMode") getConsoleScreenBufferInfoProc = kernel32DLL.NewProc("GetConsoleScreenBufferInfo") setConsoleScreenBufferSizeProc = kernel32DLL.NewProc("SetConsoleScreenBufferSize") scrollConsoleScreenBufferProc = kernel32DLL.NewProc("ScrollConsoleScreenBufferA") setConsoleTextAttributeProc = kernel32DLL.NewProc("SetConsoleTextAttribute") setConsoleWindowInfoProc = kernel32DLL.NewProc("SetConsoleWindowInfo") writeConsoleOutputProc = kernel32DLL.NewProc("WriteConsoleOutputW") readConsoleInputProc = kernel32DLL.NewProc("ReadConsoleInputW") waitForSingleObjectProc = kernel32DLL.NewProc("WaitForSingleObject") ) // Windows Console constants const ( // Console modes // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx. ENABLE_PROCESSED_INPUT = 0x0001 ENABLE_LINE_INPUT = 0x0002 ENABLE_ECHO_INPUT = 0x0004 ENABLE_WINDOW_INPUT = 0x0008 ENABLE_MOUSE_INPUT = 0x0010 ENABLE_INSERT_MODE = 0x0020 ENABLE_QUICK_EDIT_MODE = 0x0040 ENABLE_EXTENDED_FLAGS = 0x0080 ENABLE_AUTO_POSITION = 0x0100 ENABLE_VIRTUAL_TERMINAL_INPUT = 0x0200 ENABLE_PROCESSED_OUTPUT = 0x0001 ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002 ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 DISABLE_NEWLINE_AUTO_RETURN = 0x0008 ENABLE_LVB_GRID_WORLDWIDE = 0x0010 // Character attributes // Note: // -- The attributes are combined to produce various colors (e.g., Blue + Green will create Cyan). // Clearing all foreground or background colors results in black; setting all creates white. // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682088(v=vs.85).aspx#_win32_character_attributes. FOREGROUND_BLUE uint16 = 0x0001 FOREGROUND_GREEN uint16 = 0x0002 FOREGROUND_RED uint16 = 0x0004 FOREGROUND_INTENSITY uint16 = 0x0008 FOREGROUND_MASK uint16 = 0x000F BACKGROUND_BLUE uint16 = 0x0010 BACKGROUND_GREEN uint16 = 0x0020 BACKGROUND_RED uint16 = 0x0040 BACKGROUND_INTENSITY uint16 = 0x0080 BACKGROUND_MASK uint16 = 0x00F0 COMMON_LVB_MASK uint16 = 0xFF00 COMMON_LVB_REVERSE_VIDEO uint16 = 0x4000 COMMON_LVB_UNDERSCORE uint16 = 0x8000 // Input event types // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx. KEY_EVENT = 0x0001 MOUSE_EVENT = 0x0002 WINDOW_BUFFER_SIZE_EVENT = 0x0004 MENU_EVENT = 0x0008 FOCUS_EVENT = 0x0010 // WaitForSingleObject return codes WAIT_ABANDONED = 0x00000080 WAIT_FAILED = 0xFFFFFFFF WAIT_SIGNALED = 0x0000000 WAIT_TIMEOUT = 0x00000102 // WaitForSingleObject wait duration WAIT_INFINITE = 0xFFFFFFFF WAIT_ONE_SECOND = 1000 WAIT_HALF_SECOND = 500 WAIT_QUARTER_SECOND = 250 ) // Windows API Console types // -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682101(v=vs.85).aspx for Console specific types (e.g., COORD) // -- See https://msdn.microsoft.com/en-us/library/aa296569(v=vs.60).aspx for comments on alignment type ( CHAR_INFO struct { UnicodeChar uint16 Attributes uint16 } CONSOLE_CURSOR_INFO struct { Size uint32 Visible int32 } CONSOLE_SCREEN_BUFFER_INFO struct { Size COORD CursorPosition COORD Attributes uint16 Window SMALL_RECT MaximumWindowSize COORD } COORD struct { X int16 Y int16 } SMALL_RECT struct { Left int16 Top int16 Right int16 Bottom int16 } // INPUT_RECORD is a C/C++ union of which KEY_EVENT_RECORD is one case, it is also the largest // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx. INPUT_RECORD struct { EventType uint16 KeyEvent KEY_EVENT_RECORD } KEY_EVENT_RECORD struct { KeyDown int32 RepeatCount uint16 VirtualKeyCode uint16 VirtualScanCode uint16 UnicodeChar uint16 ControlKeyState uint32 } WINDOW_BUFFER_SIZE struct { Size COORD } ) // boolToBOOL converts a Go bool into a Windows int32. func boolToBOOL(f bool) int32 { if f { return int32(1) } else { return int32(0) } } // GetConsoleCursorInfo retrieves information about the size and visiblity of the console cursor. // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683163(v=vs.85).aspx. func GetConsoleCursorInfo(handle uintptr, cursorInfo *CONSOLE_CURSOR_INFO) error { r1, r2, err := getConsoleCursorInfoProc.Call(handle, uintptr(unsafe.Pointer(cursorInfo)), 0) return checkError(r1, r2, err) } // SetConsoleCursorInfo sets the size and visiblity of the console cursor. // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686019(v=vs.85).aspx. func SetConsoleCursorInfo(handle uintptr, cursorInfo *CONSOLE_CURSOR_INFO) error { r1, r2, err := setConsoleCursorInfoProc.Call(handle, uintptr(unsafe.Pointer(cursorInfo)), 0) return checkError(r1, r2, err) } // SetConsoleCursorPosition location of the console cursor. // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686025(v=vs.85).aspx. func SetConsoleCursorPosition(handle uintptr, coord COORD) error { r1, r2, err := setConsoleCursorPositionProc.Call(handle, coordToPointer(coord)) use(coord) return checkError(r1, r2, err) } // GetConsoleMode gets the console mode for given file descriptor // See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx. func GetConsoleMode(handle uintptr) (mode uint32, err error) { err = syscall.GetConsoleMode(syscall.Handle(handle), &mode) return mode, err } // SetConsoleMode sets the console mode for given file descriptor // See http://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx. func SetConsoleMode(handle uintptr, mode uint32) error { r1, r2, err := setConsoleModeProc.Call(handle, uintptr(mode), 0) use(mode) return checkError(r1, r2, err) } // GetConsoleScreenBufferInfo retrieves information about the specified console screen buffer. // See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683171(v=vs.85).aspx. func GetConsoleScreenBufferInfo(handle uintptr) (*CONSOLE_SCREEN_BUFFER_INFO, error) { info := CONSOLE_SCREEN_BUFFER_INFO{} err := checkError(getConsoleScreenBufferInfoProc.Call(handle, uintptr(unsafe.Pointer(&info)), 0)) if err != nil { return nil, err } return &info, nil } func ScrollConsoleScreenBuffer(handle uintptr, scrollRect SMALL_RECT, clipRect SMALL_RECT, destOrigin COORD, char CHAR_INFO) error { r1, r2, err := scrollConsoleScreenBufferProc.Call(handle, uintptr(unsafe.Pointer(&scrollRect)), uintptr(unsafe.Pointer(&clipRect)), coordToPointer(destOrigin), uintptr(unsafe.Pointer(&char))) use(scrollRect) use(clipRect) use(destOrigin) use(char) return checkError(r1, r2, err) } // SetConsoleScreenBufferSize sets the size of the console screen buffer. // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686044(v=vs.85).aspx. func SetConsoleScreenBufferSize(handle uintptr, coord COORD) error { r1, r2, err := setConsoleScreenBufferSizeProc.Call(handle, coordToPointer(coord)) use(coord) return checkError(r1, r2, err) } // SetConsoleTextAttribute sets the attributes of characters written to the // console screen buffer by the WriteFile or WriteConsole function. // See http://msdn.microsoft.com/en-us/library/windows/desktop/ms686047(v=vs.85).aspx. func SetConsoleTextAttribute(handle uintptr, attribute uint16) error { r1, r2, err := setConsoleTextAttributeProc.Call(handle, uintptr(attribute), 0) use(attribute) return checkError(r1, r2, err) } // SetConsoleWindowInfo sets the size and position of the console screen buffer's window. // Note that the size and location must be within and no larger than the backing console screen buffer. // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686125(v=vs.85).aspx. func SetConsoleWindowInfo(handle uintptr, isAbsolute bool, rect SMALL_RECT) error { r1, r2, err := setConsoleWindowInfoProc.Call(handle, uintptr(boolToBOOL(isAbsolute)), uintptr(unsafe.Pointer(&rect))) use(isAbsolute) use(rect) return checkError(r1, r2, err) } // WriteConsoleOutput writes the CHAR_INFOs from the provided buffer to the active console buffer. // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms687404(v=vs.85).aspx. func WriteConsoleOutput(handle uintptr, buffer []CHAR_INFO, bufferSize COORD, bufferCoord COORD, writeRegion *SMALL_RECT) error { r1, r2, err := writeConsoleOutputProc.Call(handle, uintptr(unsafe.Pointer(&buffer[0])), coordToPointer(bufferSize), coordToPointer(bufferCoord), uintptr(unsafe.Pointer(writeRegion))) use(buffer) use(bufferSize) use(bufferCoord) return checkError(r1, r2, err) } // ReadConsoleInput reads (and removes) data from the console input buffer. // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms684961(v=vs.85).aspx. func ReadConsoleInput(handle uintptr, buffer []INPUT_RECORD, count *uint32) error { r1, r2, err := readConsoleInputProc.Call(handle, uintptr(unsafe.Pointer(&buffer[0])), uintptr(len(buffer)), uintptr(unsafe.Pointer(count))) use(buffer) return checkError(r1, r2, err) } // WaitForSingleObject waits for the passed handle to be signaled. // It returns true if the handle was signaled; false otherwise. // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx. func WaitForSingleObject(handle uintptr, msWait uint32) (bool, error) { r1, _, err := waitForSingleObjectProc.Call(handle, uintptr(uint32(msWait))) switch r1 { case WAIT_ABANDONED, WAIT_TIMEOUT: return false, nil case WAIT_SIGNALED: return true, nil } use(msWait) return false, err } // String helpers func (info CONSOLE_SCREEN_BUFFER_INFO) String() string { return fmt.Sprintf("Size(%v) Cursor(%v) Window(%v) Max(%v)", info.Size, info.CursorPosition, info.Window, info.MaximumWindowSize) } func (coord COORD) String() string { return fmt.Sprintf("%v,%v", coord.X, coord.Y) } func (rect SMALL_RECT) String() string { return fmt.Sprintf("(%v,%v),(%v,%v)", rect.Left, rect.Top, rect.Right, rect.Bottom) } // checkError evaluates the results of a Windows API call and returns the error if it failed. func checkError(r1, r2 uintptr, err error) error { // Windows APIs return non-zero to indicate success if r1 != 0 { return nil } // Return the error if provided, otherwise default to EINVAL if err != nil { return err } return syscall.EINVAL } // coordToPointer converts a COORD into a uintptr (by fooling the type system). func coordToPointer(c COORD) uintptr { // Note: This code assumes the two SHORTs are correctly laid out; the "cast" to uint32 is just to get a pointer to pass. return uintptr(*((*uint32)(unsafe.Pointer(&c)))) } // use is a no-op, but the compiler cannot see that it is. // Calling use(p) ensures that p is kept live until that point. func use(p interface{}) {} ================================================ FILE: vendor/github.com/Azure/go-ansiterm/winterm/attr_translation.go ================================================ // +build windows package winterm import "github.com/Azure/go-ansiterm" const ( FOREGROUND_COLOR_MASK = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE BACKGROUND_COLOR_MASK = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE ) // collectAnsiIntoWindowsAttributes modifies the passed Windows text mode flags to reflect the // request represented by the passed ANSI mode. func collectAnsiIntoWindowsAttributes(windowsMode uint16, inverted bool, baseMode uint16, ansiMode int16) (uint16, bool) { switch ansiMode { // Mode styles case ansiterm.ANSI_SGR_BOLD: windowsMode = windowsMode | FOREGROUND_INTENSITY case ansiterm.ANSI_SGR_DIM, ansiterm.ANSI_SGR_BOLD_DIM_OFF: windowsMode &^= FOREGROUND_INTENSITY case ansiterm.ANSI_SGR_UNDERLINE: windowsMode = windowsMode | COMMON_LVB_UNDERSCORE case ansiterm.ANSI_SGR_REVERSE: inverted = true case ansiterm.ANSI_SGR_REVERSE_OFF: inverted = false case ansiterm.ANSI_SGR_UNDERLINE_OFF: windowsMode &^= COMMON_LVB_UNDERSCORE // Foreground colors case ansiterm.ANSI_SGR_FOREGROUND_DEFAULT: windowsMode = (windowsMode &^ FOREGROUND_MASK) | (baseMode & FOREGROUND_MASK) case ansiterm.ANSI_SGR_FOREGROUND_BLACK: windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) case ansiterm.ANSI_SGR_FOREGROUND_RED: windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED case ansiterm.ANSI_SGR_FOREGROUND_GREEN: windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_GREEN case ansiterm.ANSI_SGR_FOREGROUND_YELLOW: windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED | FOREGROUND_GREEN case ansiterm.ANSI_SGR_FOREGROUND_BLUE: windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_BLUE case ansiterm.ANSI_SGR_FOREGROUND_MAGENTA: windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED | FOREGROUND_BLUE case ansiterm.ANSI_SGR_FOREGROUND_CYAN: windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_GREEN | FOREGROUND_BLUE case ansiterm.ANSI_SGR_FOREGROUND_WHITE: windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE // Background colors case ansiterm.ANSI_SGR_BACKGROUND_DEFAULT: // Black with no intensity windowsMode = (windowsMode &^ BACKGROUND_MASK) | (baseMode & BACKGROUND_MASK) case ansiterm.ANSI_SGR_BACKGROUND_BLACK: windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) case ansiterm.ANSI_SGR_BACKGROUND_RED: windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED case ansiterm.ANSI_SGR_BACKGROUND_GREEN: windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_GREEN case ansiterm.ANSI_SGR_BACKGROUND_YELLOW: windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED | BACKGROUND_GREEN case ansiterm.ANSI_SGR_BACKGROUND_BLUE: windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_BLUE case ansiterm.ANSI_SGR_BACKGROUND_MAGENTA: windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED | BACKGROUND_BLUE case ansiterm.ANSI_SGR_BACKGROUND_CYAN: windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_GREEN | BACKGROUND_BLUE case ansiterm.ANSI_SGR_BACKGROUND_WHITE: windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE } return windowsMode, inverted } // invertAttributes inverts the foreground and background colors of a Windows attributes value func invertAttributes(windowsMode uint16) uint16 { return (COMMON_LVB_MASK & windowsMode) | ((FOREGROUND_MASK & windowsMode) << 4) | ((BACKGROUND_MASK & windowsMode) >> 4) } ================================================ FILE: vendor/github.com/Azure/go-ansiterm/winterm/cursor_helpers.go ================================================ // +build windows package winterm const ( horizontal = iota vertical ) func (h *windowsAnsiEventHandler) getCursorWindow(info *CONSOLE_SCREEN_BUFFER_INFO) SMALL_RECT { if h.originMode { sr := h.effectiveSr(info.Window) return SMALL_RECT{ Top: sr.top, Bottom: sr.bottom, Left: 0, Right: info.Size.X - 1, } } else { return SMALL_RECT{ Top: info.Window.Top, Bottom: info.Window.Bottom, Left: 0, Right: info.Size.X - 1, } } } // setCursorPosition sets the cursor to the specified position, bounded to the screen size func (h *windowsAnsiEventHandler) setCursorPosition(position COORD, window SMALL_RECT) error { position.X = ensureInRange(position.X, window.Left, window.Right) position.Y = ensureInRange(position.Y, window.Top, window.Bottom) err := SetConsoleCursorPosition(h.fd, position) if err != nil { return err } h.logf("Cursor position set: (%d, %d)", position.X, position.Y) return err } func (h *windowsAnsiEventHandler) moveCursorVertical(param int) error { return h.moveCursor(vertical, param) } func (h *windowsAnsiEventHandler) moveCursorHorizontal(param int) error { return h.moveCursor(horizontal, param) } func (h *windowsAnsiEventHandler) moveCursor(moveMode int, param int) error { info, err := GetConsoleScreenBufferInfo(h.fd) if err != nil { return err } position := info.CursorPosition switch moveMode { case horizontal: position.X += int16(param) case vertical: position.Y += int16(param) } if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil { return err } return nil } func (h *windowsAnsiEventHandler) moveCursorLine(param int) error { info, err := GetConsoleScreenBufferInfo(h.fd) if err != nil { return err } position := info.CursorPosition position.X = 0 position.Y += int16(param) if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil { return err } return nil } func (h *windowsAnsiEventHandler) moveCursorColumn(param int) error { info, err := GetConsoleScreenBufferInfo(h.fd) if err != nil { return err } position := info.CursorPosition position.X = int16(param) - 1 if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil { return err } return nil } ================================================ FILE: vendor/github.com/Azure/go-ansiterm/winterm/erase_helpers.go ================================================ // +build windows package winterm import "github.com/Azure/go-ansiterm" func (h *windowsAnsiEventHandler) clearRange(attributes uint16, fromCoord COORD, toCoord COORD) error { // Ignore an invalid (negative area) request if toCoord.Y < fromCoord.Y { return nil } var err error var coordStart = COORD{} var coordEnd = COORD{} xCurrent, yCurrent := fromCoord.X, fromCoord.Y xEnd, yEnd := toCoord.X, toCoord.Y // Clear any partial initial line if xCurrent > 0 { coordStart.X, coordStart.Y = xCurrent, yCurrent coordEnd.X, coordEnd.Y = xEnd, yCurrent err = h.clearRect(attributes, coordStart, coordEnd) if err != nil { return err } xCurrent = 0 yCurrent += 1 } // Clear intervening rectangular section if yCurrent < yEnd { coordStart.X, coordStart.Y = xCurrent, yCurrent coordEnd.X, coordEnd.Y = xEnd, yEnd-1 err = h.clearRect(attributes, coordStart, coordEnd) if err != nil { return err } xCurrent = 0 yCurrent = yEnd } // Clear remaining partial ending line coordStart.X, coordStart.Y = xCurrent, yCurrent coordEnd.X, coordEnd.Y = xEnd, yEnd err = h.clearRect(attributes, coordStart, coordEnd) if err != nil { return err } return nil } func (h *windowsAnsiEventHandler) clearRect(attributes uint16, fromCoord COORD, toCoord COORD) error { region := SMALL_RECT{Top: fromCoord.Y, Left: fromCoord.X, Bottom: toCoord.Y, Right: toCoord.X} width := toCoord.X - fromCoord.X + 1 height := toCoord.Y - fromCoord.Y + 1 size := uint32(width) * uint32(height) if size <= 0 { return nil } buffer := make([]CHAR_INFO, size) char := CHAR_INFO{ansiterm.FILL_CHARACTER, attributes} for i := 0; i < int(size); i++ { buffer[i] = char } err := WriteConsoleOutput(h.fd, buffer, COORD{X: width, Y: height}, COORD{X: 0, Y: 0}, ®ion) if err != nil { return err } return nil } ================================================ FILE: vendor/github.com/Azure/go-ansiterm/winterm/scroll_helper.go ================================================ // +build windows package winterm // effectiveSr gets the current effective scroll region in buffer coordinates func (h *windowsAnsiEventHandler) effectiveSr(window SMALL_RECT) scrollRegion { top := addInRange(window.Top, h.sr.top, window.Top, window.Bottom) bottom := addInRange(window.Top, h.sr.bottom, window.Top, window.Bottom) if top >= bottom { top = window.Top bottom = window.Bottom } return scrollRegion{top: top, bottom: bottom} } func (h *windowsAnsiEventHandler) scrollUp(param int) error { info, err := GetConsoleScreenBufferInfo(h.fd) if err != nil { return err } sr := h.effectiveSr(info.Window) return h.scroll(param, sr, info) } func (h *windowsAnsiEventHandler) scrollDown(param int) error { return h.scrollUp(-param) } func (h *windowsAnsiEventHandler) deleteLines(param int) error { info, err := GetConsoleScreenBufferInfo(h.fd) if err != nil { return err } start := info.CursorPosition.Y sr := h.effectiveSr(info.Window) // Lines cannot be inserted or deleted outside the scrolling region. if start >= sr.top && start <= sr.bottom { sr.top = start return h.scroll(param, sr, info) } else { return nil } } func (h *windowsAnsiEventHandler) insertLines(param int) error { return h.deleteLines(-param) } // scroll scrolls the provided scroll region by param lines. The scroll region is in buffer coordinates. func (h *windowsAnsiEventHandler) scroll(param int, sr scrollRegion, info *CONSOLE_SCREEN_BUFFER_INFO) error { h.logf("scroll: scrollTop: %d, scrollBottom: %d", sr.top, sr.bottom) h.logf("scroll: windowTop: %d, windowBottom: %d", info.Window.Top, info.Window.Bottom) // Copy from and clip to the scroll region (full buffer width) scrollRect := SMALL_RECT{ Top: sr.top, Bottom: sr.bottom, Left: 0, Right: info.Size.X - 1, } // Origin to which area should be copied destOrigin := COORD{ X: 0, Y: sr.top - int16(param), } char := CHAR_INFO{ UnicodeChar: ' ', Attributes: h.attributes, } if err := ScrollConsoleScreenBuffer(h.fd, scrollRect, scrollRect, destOrigin, char); err != nil { return err } return nil } func (h *windowsAnsiEventHandler) deleteCharacters(param int) error { info, err := GetConsoleScreenBufferInfo(h.fd) if err != nil { return err } return h.scrollLine(param, info.CursorPosition, info) } func (h *windowsAnsiEventHandler) insertCharacters(param int) error { return h.deleteCharacters(-param) } // scrollLine scrolls a line horizontally starting at the provided position by a number of columns. func (h *windowsAnsiEventHandler) scrollLine(columns int, position COORD, info *CONSOLE_SCREEN_BUFFER_INFO) error { // Copy from and clip to the scroll region (full buffer width) scrollRect := SMALL_RECT{ Top: position.Y, Bottom: position.Y, Left: position.X, Right: info.Size.X - 1, } // Origin to which area should be copied destOrigin := COORD{ X: position.X - int16(columns), Y: position.Y, } char := CHAR_INFO{ UnicodeChar: ' ', Attributes: h.attributes, } if err := ScrollConsoleScreenBuffer(h.fd, scrollRect, scrollRect, destOrigin, char); err != nil { return err } return nil } ================================================ FILE: vendor/github.com/Azure/go-ansiterm/winterm/utilities.go ================================================ // +build windows package winterm // AddInRange increments a value by the passed quantity while ensuring the values // always remain within the supplied min / max range. func addInRange(n int16, increment int16, min int16, max int16) int16 { return ensureInRange(n+increment, min, max) } ================================================ FILE: vendor/github.com/Azure/go-ansiterm/winterm/win_event_handler.go ================================================ // +build windows package winterm import ( "bytes" "log" "os" "strconv" "github.com/Azure/go-ansiterm" ) type windowsAnsiEventHandler struct { fd uintptr file *os.File infoReset *CONSOLE_SCREEN_BUFFER_INFO sr scrollRegion buffer bytes.Buffer attributes uint16 inverted bool wrapNext bool drewMarginByte bool originMode bool marginByte byte curInfo *CONSOLE_SCREEN_BUFFER_INFO curPos COORD logf func(string, ...interface{}) } type Option func(*windowsAnsiEventHandler) func WithLogf(f func(string, ...interface{})) Option { return func(w *windowsAnsiEventHandler) { w.logf = f } } func CreateWinEventHandler(fd uintptr, file *os.File, opts ...Option) ansiterm.AnsiEventHandler { infoReset, err := GetConsoleScreenBufferInfo(fd) if err != nil { return nil } h := &windowsAnsiEventHandler{ fd: fd, file: file, infoReset: infoReset, attributes: infoReset.Attributes, } for _, o := range opts { o(h) } if isDebugEnv := os.Getenv(ansiterm.LogEnv); isDebugEnv == "1" { logFile, _ := os.Create("winEventHandler.log") logger := log.New(logFile, "", log.LstdFlags) if h.logf != nil { l := h.logf h.logf = func(s string, v ...interface{}) { l(s, v...) logger.Printf(s, v...) } } else { h.logf = logger.Printf } } if h.logf == nil { h.logf = func(string, ...interface{}) {} } return h } type scrollRegion struct { top int16 bottom int16 } // simulateLF simulates a LF or CR+LF by scrolling if necessary to handle the // current cursor position and scroll region settings, in which case it returns // true. If no special handling is necessary, then it does nothing and returns // false. // // In the false case, the caller should ensure that a carriage return // and line feed are inserted or that the text is otherwise wrapped. func (h *windowsAnsiEventHandler) simulateLF(includeCR bool) (bool, error) { if h.wrapNext { if err := h.Flush(); err != nil { return false, err } h.clearWrap() } pos, info, err := h.getCurrentInfo() if err != nil { return false, err } sr := h.effectiveSr(info.Window) if pos.Y == sr.bottom { // Scrolling is necessary. Let Windows automatically scroll if the scrolling region // is the full window. if sr.top == info.Window.Top && sr.bottom == info.Window.Bottom { if includeCR { pos.X = 0 h.updatePos(pos) } return false, nil } // A custom scroll region is active. Scroll the window manually to simulate // the LF. if err := h.Flush(); err != nil { return false, err } h.logf("Simulating LF inside scroll region") if err := h.scrollUp(1); err != nil { return false, err } if includeCR { pos.X = 0 if err := SetConsoleCursorPosition(h.fd, pos); err != nil { return false, err } } return true, nil } else if pos.Y < info.Window.Bottom { // Let Windows handle the LF. pos.Y++ if includeCR { pos.X = 0 } h.updatePos(pos) return false, nil } else { // The cursor is at the bottom of the screen but outside the scroll // region. Skip the LF. h.logf("Simulating LF outside scroll region") if includeCR { if err := h.Flush(); err != nil { return false, err } pos.X = 0 if err := SetConsoleCursorPosition(h.fd, pos); err != nil { return false, err } } return true, nil } } // executeLF executes a LF without a CR. func (h *windowsAnsiEventHandler) executeLF() error { handled, err := h.simulateLF(false) if err != nil { return err } if !handled { // Windows LF will reset the cursor column position. Write the LF // and restore the cursor position. pos, _, err := h.getCurrentInfo() if err != nil { return err } h.buffer.WriteByte(ansiterm.ANSI_LINE_FEED) if pos.X != 0 { if err := h.Flush(); err != nil { return err } h.logf("Resetting cursor position for LF without CR") if err := SetConsoleCursorPosition(h.fd, pos); err != nil { return err } } } return nil } func (h *windowsAnsiEventHandler) Print(b byte) error { if h.wrapNext { h.buffer.WriteByte(h.marginByte) h.clearWrap() if _, err := h.simulateLF(true); err != nil { return err } } pos, info, err := h.getCurrentInfo() if err != nil { return err } if pos.X == info.Size.X-1 { h.wrapNext = true h.marginByte = b } else { pos.X++ h.updatePos(pos) h.buffer.WriteByte(b) } return nil } func (h *windowsAnsiEventHandler) Execute(b byte) error { switch b { case ansiterm.ANSI_TAB: h.logf("Execute(TAB)") // Move to the next tab stop, but preserve auto-wrap if already set. if !h.wrapNext { pos, info, err := h.getCurrentInfo() if err != nil { return err } pos.X = (pos.X + 8) - pos.X%8 if pos.X >= info.Size.X { pos.X = info.Size.X - 1 } if err := h.Flush(); err != nil { return err } if err := SetConsoleCursorPosition(h.fd, pos); err != nil { return err } } return nil case ansiterm.ANSI_BEL: h.buffer.WriteByte(ansiterm.ANSI_BEL) return nil case ansiterm.ANSI_BACKSPACE: if h.wrapNext { if err := h.Flush(); err != nil { return err } h.clearWrap() } pos, _, err := h.getCurrentInfo() if err != nil { return err } if pos.X > 0 { pos.X-- h.updatePos(pos) h.buffer.WriteByte(ansiterm.ANSI_BACKSPACE) } return nil case ansiterm.ANSI_VERTICAL_TAB, ansiterm.ANSI_FORM_FEED: // Treat as true LF. return h.executeLF() case ansiterm.ANSI_LINE_FEED: // Simulate a CR and LF for now since there is no way in go-ansiterm // to tell if the LF should include CR (and more things break when it's // missing than when it's incorrectly added). handled, err := h.simulateLF(true) if handled || err != nil { return err } return h.buffer.WriteByte(ansiterm.ANSI_LINE_FEED) case ansiterm.ANSI_CARRIAGE_RETURN: if h.wrapNext { if err := h.Flush(); err != nil { return err } h.clearWrap() } pos, _, err := h.getCurrentInfo() if err != nil { return err } if pos.X != 0 { pos.X = 0 h.updatePos(pos) h.buffer.WriteByte(ansiterm.ANSI_CARRIAGE_RETURN) } return nil default: return nil } } func (h *windowsAnsiEventHandler) CUU(param int) error { if err := h.Flush(); err != nil { return err } h.logf("CUU: [%v]", []string{strconv.Itoa(param)}) h.clearWrap() return h.moveCursorVertical(-param) } func (h *windowsAnsiEventHandler) CUD(param int) error { if err := h.Flush(); err != nil { return err } h.logf("CUD: [%v]", []string{strconv.Itoa(param)}) h.clearWrap() return h.moveCursorVertical(param) } func (h *windowsAnsiEventHandler) CUF(param int) error { if err := h.Flush(); err != nil { return err } h.logf("CUF: [%v]", []string{strconv.Itoa(param)}) h.clearWrap() return h.moveCursorHorizontal(param) } func (h *windowsAnsiEventHandler) CUB(param int) error { if err := h.Flush(); err != nil { return err } h.logf("CUB: [%v]", []string{strconv.Itoa(param)}) h.clearWrap() return h.moveCursorHorizontal(-param) } func (h *windowsAnsiEventHandler) CNL(param int) error { if err := h.Flush(); err != nil { return err } h.logf("CNL: [%v]", []string{strconv.Itoa(param)}) h.clearWrap() return h.moveCursorLine(param) } func (h *windowsAnsiEventHandler) CPL(param int) error { if err := h.Flush(); err != nil { return err } h.logf("CPL: [%v]", []string{strconv.Itoa(param)}) h.clearWrap() return h.moveCursorLine(-param) } func (h *windowsAnsiEventHandler) CHA(param int) error { if err := h.Flush(); err != nil { return err } h.logf("CHA: [%v]", []string{strconv.Itoa(param)}) h.clearWrap() return h.moveCursorColumn(param) } func (h *windowsAnsiEventHandler) VPA(param int) error { if err := h.Flush(); err != nil { return err } h.logf("VPA: [[%d]]", param) h.clearWrap() info, err := GetConsoleScreenBufferInfo(h.fd) if err != nil { return err } window := h.getCursorWindow(info) position := info.CursorPosition position.Y = window.Top + int16(param) - 1 return h.setCursorPosition(position, window) } func (h *windowsAnsiEventHandler) CUP(row int, col int) error { if err := h.Flush(); err != nil { return err } h.logf("CUP: [[%d %d]]", row, col) h.clearWrap() info, err := GetConsoleScreenBufferInfo(h.fd) if err != nil { return err } window := h.getCursorWindow(info) position := COORD{window.Left + int16(col) - 1, window.Top + int16(row) - 1} return h.setCursorPosition(position, window) } func (h *windowsAnsiEventHandler) HVP(row int, col int) error { if err := h.Flush(); err != nil { return err } h.logf("HVP: [[%d %d]]", row, col) h.clearWrap() return h.CUP(row, col) } func (h *windowsAnsiEventHandler) DECTCEM(visible bool) error { if err := h.Flush(); err != nil { return err } h.logf("DECTCEM: [%v]", []string{strconv.FormatBool(visible)}) h.clearWrap() return nil } func (h *windowsAnsiEventHandler) DECOM(enable bool) error { if err := h.Flush(); err != nil { return err } h.logf("DECOM: [%v]", []string{strconv.FormatBool(enable)}) h.clearWrap() h.originMode = enable return h.CUP(1, 1) } func (h *windowsAnsiEventHandler) DECCOLM(use132 bool) error { if err := h.Flush(); err != nil { return err } h.logf("DECCOLM: [%v]", []string{strconv.FormatBool(use132)}) h.clearWrap() if err := h.ED(2); err != nil { return err } info, err := GetConsoleScreenBufferInfo(h.fd) if err != nil { return err } targetWidth := int16(80) if use132 { targetWidth = 132 } if info.Size.X < targetWidth { if err := SetConsoleScreenBufferSize(h.fd, COORD{targetWidth, info.Size.Y}); err != nil { h.logf("set buffer failed: %v", err) return err } } window := info.Window window.Left = 0 window.Right = targetWidth - 1 if err := SetConsoleWindowInfo(h.fd, true, window); err != nil { h.logf("set window failed: %v", err) return err } if info.Size.X > targetWidth { if err := SetConsoleScreenBufferSize(h.fd, COORD{targetWidth, info.Size.Y}); err != nil { h.logf("set buffer failed: %v", err) return err } } return SetConsoleCursorPosition(h.fd, COORD{0, 0}) } func (h *windowsAnsiEventHandler) ED(param int) error { if err := h.Flush(); err != nil { return err } h.logf("ED: [%v]", []string{strconv.Itoa(param)}) h.clearWrap() // [J -- Erases from the cursor to the end of the screen, including the cursor position. // [1J -- Erases from the beginning of the screen to the cursor, including the cursor position. // [2J -- Erases the complete display. The cursor does not move. // Notes: // -- Clearing the entire buffer, versus just the Window, works best for Windows Consoles info, err := GetConsoleScreenBufferInfo(h.fd) if err != nil { return err } var start COORD var end COORD switch param { case 0: start = info.CursorPosition end = COORD{info.Size.X - 1, info.Size.Y - 1} case 1: start = COORD{0, 0} end = info.CursorPosition case 2: start = COORD{0, 0} end = COORD{info.Size.X - 1, info.Size.Y - 1} } err = h.clearRange(h.attributes, start, end) if err != nil { return err } // If the whole buffer was cleared, move the window to the top while preserving // the window-relative cursor position. if param == 2 { pos := info.CursorPosition window := info.Window pos.Y -= window.Top window.Bottom -= window.Top window.Top = 0 if err := SetConsoleCursorPosition(h.fd, pos); err != nil { return err } if err := SetConsoleWindowInfo(h.fd, true, window); err != nil { return err } } return nil } func (h *windowsAnsiEventHandler) EL(param int) error { if err := h.Flush(); err != nil { return err } h.logf("EL: [%v]", strconv.Itoa(param)) h.clearWrap() // [K -- Erases from the cursor to the end of the line, including the cursor position. // [1K -- Erases from the beginning of the line to the cursor, including the cursor position. // [2K -- Erases the complete line. info, err := GetConsoleScreenBufferInfo(h.fd) if err != nil { return err } var start COORD var end COORD switch param { case 0: start = info.CursorPosition end = COORD{info.Size.X, info.CursorPosition.Y} case 1: start = COORD{0, info.CursorPosition.Y} end = info.CursorPosition case 2: start = COORD{0, info.CursorPosition.Y} end = COORD{info.Size.X, info.CursorPosition.Y} } err = h.clearRange(h.attributes, start, end) if err != nil { return err } return nil } func (h *windowsAnsiEventHandler) IL(param int) error { if err := h.Flush(); err != nil { return err } h.logf("IL: [%v]", strconv.Itoa(param)) h.clearWrap() return h.insertLines(param) } func (h *windowsAnsiEventHandler) DL(param int) error { if err := h.Flush(); err != nil { return err } h.logf("DL: [%v]", strconv.Itoa(param)) h.clearWrap() return h.deleteLines(param) } func (h *windowsAnsiEventHandler) ICH(param int) error { if err := h.Flush(); err != nil { return err } h.logf("ICH: [%v]", strconv.Itoa(param)) h.clearWrap() return h.insertCharacters(param) } func (h *windowsAnsiEventHandler) DCH(param int) error { if err := h.Flush(); err != nil { return err } h.logf("DCH: [%v]", strconv.Itoa(param)) h.clearWrap() return h.deleteCharacters(param) } func (h *windowsAnsiEventHandler) SGR(params []int) error { if err := h.Flush(); err != nil { return err } strings := []string{} for _, v := range params { strings = append(strings, strconv.Itoa(v)) } h.logf("SGR: [%v]", strings) if len(params) <= 0 { h.attributes = h.infoReset.Attributes h.inverted = false } else { for _, attr := range params { if attr == ansiterm.ANSI_SGR_RESET { h.attributes = h.infoReset.Attributes h.inverted = false continue } h.attributes, h.inverted = collectAnsiIntoWindowsAttributes(h.attributes, h.inverted, h.infoReset.Attributes, int16(attr)) } } attributes := h.attributes if h.inverted { attributes = invertAttributes(attributes) } err := SetConsoleTextAttribute(h.fd, attributes) if err != nil { return err } return nil } func (h *windowsAnsiEventHandler) SU(param int) error { if err := h.Flush(); err != nil { return err } h.logf("SU: [%v]", []string{strconv.Itoa(param)}) h.clearWrap() return h.scrollUp(param) } func (h *windowsAnsiEventHandler) SD(param int) error { if err := h.Flush(); err != nil { return err } h.logf("SD: [%v]", []string{strconv.Itoa(param)}) h.clearWrap() return h.scrollDown(param) } func (h *windowsAnsiEventHandler) DA(params []string) error { h.logf("DA: [%v]", params) // DA cannot be implemented because it must send data on the VT100 input stream, // which is not available to go-ansiterm. return nil } func (h *windowsAnsiEventHandler) DECSTBM(top int, bottom int) error { if err := h.Flush(); err != nil { return err } h.logf("DECSTBM: [%d, %d]", top, bottom) // Windows is 0 indexed, Linux is 1 indexed h.sr.top = int16(top - 1) h.sr.bottom = int16(bottom - 1) // This command also moves the cursor to the origin. h.clearWrap() return h.CUP(1, 1) } func (h *windowsAnsiEventHandler) RI() error { if err := h.Flush(); err != nil { return err } h.logf("RI: []") h.clearWrap() info, err := GetConsoleScreenBufferInfo(h.fd) if err != nil { return err } sr := h.effectiveSr(info.Window) if info.CursorPosition.Y == sr.top { return h.scrollDown(1) } return h.moveCursorVertical(-1) } func (h *windowsAnsiEventHandler) IND() error { h.logf("IND: []") return h.executeLF() } func (h *windowsAnsiEventHandler) Flush() error { h.curInfo = nil if h.buffer.Len() > 0 { h.logf("Flush: [%s]", h.buffer.Bytes()) if _, err := h.buffer.WriteTo(h.file); err != nil { return err } } if h.wrapNext && !h.drewMarginByte { h.logf("Flush: drawing margin byte '%c'", h.marginByte) info, err := GetConsoleScreenBufferInfo(h.fd) if err != nil { return err } charInfo := []CHAR_INFO{{UnicodeChar: uint16(h.marginByte), Attributes: info.Attributes}} size := COORD{1, 1} position := COORD{0, 0} region := SMALL_RECT{Left: info.CursorPosition.X, Top: info.CursorPosition.Y, Right: info.CursorPosition.X, Bottom: info.CursorPosition.Y} if err := WriteConsoleOutput(h.fd, charInfo, size, position, ®ion); err != nil { return err } h.drewMarginByte = true } return nil } // cacheConsoleInfo ensures that the current console screen information has been queried // since the last call to Flush(). It must be called before accessing h.curInfo or h.curPos. func (h *windowsAnsiEventHandler) getCurrentInfo() (COORD, *CONSOLE_SCREEN_BUFFER_INFO, error) { if h.curInfo == nil { info, err := GetConsoleScreenBufferInfo(h.fd) if err != nil { return COORD{}, nil, err } h.curInfo = info h.curPos = info.CursorPosition } return h.curPos, h.curInfo, nil } func (h *windowsAnsiEventHandler) updatePos(pos COORD) { if h.curInfo == nil { panic("failed to call getCurrentInfo before calling updatePos") } h.curPos = pos } // clearWrap clears the state where the cursor is in the margin // waiting for the next character before wrapping the line. This must // be done before most operations that act on the cursor. func (h *windowsAnsiEventHandler) clearWrap() { h.wrapNext = false h.drewMarginByte = false } ================================================ FILE: vendor/github.com/Microsoft/go-winio/.gitattributes ================================================ * text=auto eol=lf ================================================ FILE: vendor/github.com/Microsoft/go-winio/.gitignore ================================================ .vscode/ *.exe # testing testdata # go workspaces go.work go.work.sum ================================================ FILE: vendor/github.com/Microsoft/go-winio/.golangci.yml ================================================ run: skip-dirs: - pkg/etw/sample linters: enable: # style - containedctx # struct contains a context - dupl # duplicate code - errname # erorrs are named correctly - nolintlint # "//nolint" directives are properly explained - revive # golint replacement - unconvert # unnecessary conversions - wastedassign # bugs, performance, unused, etc ... - contextcheck # function uses a non-inherited context - errorlint # errors not wrapped for 1.13 - exhaustive # check exhaustiveness of enum switch statements - gofmt # files are gofmt'ed - gosec # security - nilerr # returns nil even with non-nil error - unparam # unused function params issues: exclude-rules: # err is very often shadowed in nested scopes - linters: - govet text: '^shadow: declaration of "err" shadows declaration' # ignore long lines for skip autogen directives - linters: - revive text: "^line-length-limit: " source: "^//(go:generate|sys) " #TODO: remove after upgrading to go1.18 # ignore comment spacing for nolint and sys directives - linters: - revive text: "^comment-spacings: no space between comment delimiter and comment text" source: "//(cspell:|nolint:|sys |todo)" # not on go 1.18 yet, so no any - linters: - revive text: "^use-any: since GO 1.18 'interface{}' can be replaced by 'any'" # allow unjustified ignores of error checks in defer statements - linters: - nolintlint text: "^directive `//nolint:errcheck` should provide explanation" source: '^\s*defer ' # allow unjustified ignores of error lints for io.EOF - linters: - nolintlint text: "^directive `//nolint:errorlint` should provide explanation" source: '[=|!]= io.EOF' linters-settings: exhaustive: default-signifies-exhaustive: true govet: enable-all: true disable: # struct order is often for Win32 compat # also, ignore pointer bytes/GC issues for now until performance becomes an issue - fieldalignment check-shadowing: true nolintlint: allow-leading-space: false require-explanation: true require-specific: true revive: # revive is more configurable than static check, so likely the preferred alternative to static-check # (once the perf issue is solved: https://github.com/golangci/golangci-lint/issues/2997) enable-all-rules: true # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md rules: # rules with required arguments - name: argument-limit disabled: true - name: banned-characters disabled: true - name: cognitive-complexity disabled: true - name: cyclomatic disabled: true - name: file-header disabled: true - name: function-length disabled: true - name: function-result-limit disabled: true - name: max-public-structs disabled: true # geneally annoying rules - name: add-constant # complains about any and all strings and integers disabled: true - name: confusing-naming # we frequently use "Foo()" and "foo()" together disabled: true - name: flag-parameter # excessive, and a common idiom we use disabled: true - name: unhandled-error # warns over common fmt.Print* and io.Close; rely on errcheck instead disabled: true # general config - name: line-length-limit arguments: - 140 - name: var-naming arguments: - [] - - CID - CRI - CTRD - DACL - DLL - DOS - ETW - FSCTL - GCS - GMSA - HCS - HV - IO - LCOW - LDAP - LPAC - LTSC - MMIO - NT - OCI - PMEM - PWSH - RX - SACl - SID - SMB - TX - VHD - VHDX - VMID - VPCI - WCOW - WIM ================================================ FILE: vendor/github.com/Microsoft/go-winio/CODEOWNERS ================================================ * @microsoft/containerplat ================================================ FILE: vendor/github.com/Microsoft/go-winio/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2015 Microsoft Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/Microsoft/go-winio/README.md ================================================ # go-winio [![Build Status](https://github.com/microsoft/go-winio/actions/workflows/ci.yml/badge.svg)](https://github.com/microsoft/go-winio/actions/workflows/ci.yml) This repository contains utilities for efficiently performing Win32 IO operations in Go. Currently, this is focused on accessing named pipes and other file handles, and for using named pipes as a net transport. This code relies on IO completion ports to avoid blocking IO on system threads, allowing Go to reuse the thread to schedule another goroutine. This limits support to Windows Vista and newer operating systems. This is similar to the implementation of network sockets in Go's net package. Please see the LICENSE file for licensing information. ## Contributing This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit [Microsoft CLA](https://cla.microsoft.com). When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. Additionally, the pull request pipeline requires the following steps to be performed before mergining. ### Code Sign-Off We require that contributors sign their commits using [`git commit --signoff`][git-commit-s] to certify they either authored the work themselves or otherwise have permission to use it in this project. A range of commits can be signed off using [`git rebase --signoff`][git-rebase-s]. Please see [the developer certificate](https://developercertificate.org) for more info, as well as to make sure that you can attest to the rules listed. Our CI uses the DCO Github app to ensure that all commits in a given PR are signed-off. ### Linting Code must pass a linting stage, which uses [`golangci-lint`][lint]. The linting settings are stored in [`.golangci.yaml`](./.golangci.yaml), and can be run automatically with VSCode by adding the following to your workspace or folder settings: ```json "go.lintTool": "golangci-lint", "go.lintOnSave": "package", ``` Additional editor [integrations options are also available][lint-ide]. Alternatively, `golangci-lint` can be [installed locally][lint-install] and run from the repo root: ```shell # use . or specify a path to only lint a package # to show all lint errors, use flags "--max-issues-per-linter=0 --max-same-issues=0" > golangci-lint run ./... ``` ### Go Generate The pipeline checks that auto-generated code, via `go generate`, are up to date. This can be done for the entire repo: ```shell > go generate ./... ``` ## Code of Conduct This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. ## Special Thanks Thanks to [natefinch][natefinch] for the inspiration for this library. See [npipe](https://github.com/natefinch/npipe) for another named pipe implementation. [lint]: https://golangci-lint.run/ [lint-ide]: https://golangci-lint.run/usage/integrations/#editor-integration [lint-install]: https://golangci-lint.run/usage/install/#local-installation [git-commit-s]: https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s [git-rebase-s]: https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---signoff [natefinch]: https://github.com/natefinch ================================================ FILE: vendor/github.com/Microsoft/go-winio/SECURITY.md ================================================ ## Security Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. ## Reporting Security Issues **Please do not report security vulnerabilities through public GitHub issues.** Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) * Full paths of source file(s) related to the manifestation of the issue * The location of the affected source code (tag/branch/commit or direct URL) * Any special configuration required to reproduce the issue * Step-by-step instructions to reproduce the issue * Proof-of-concept or exploit code (if possible) * Impact of the issue, including how an attacker might exploit the issue This information will help us triage your report more quickly. If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. ## Preferred Languages We prefer all communications to be in English. ## Policy Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). ================================================ FILE: vendor/github.com/Microsoft/go-winio/backup.go ================================================ //go:build windows // +build windows package winio import ( "encoding/binary" "errors" "fmt" "io" "os" "runtime" "syscall" "unicode/utf16" "golang.org/x/sys/windows" ) //sys backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead //sys backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite const ( BackupData = uint32(iota + 1) BackupEaData BackupSecurity BackupAlternateData BackupLink BackupPropertyData BackupObjectId //revive:disable-line:var-naming ID, not Id BackupReparseData BackupSparseBlock BackupTxfsData ) const ( StreamSparseAttributes = uint32(8) ) //nolint:revive // var-naming: ALL_CAPS const ( WRITE_DAC = windows.WRITE_DAC WRITE_OWNER = windows.WRITE_OWNER ACCESS_SYSTEM_SECURITY = windows.ACCESS_SYSTEM_SECURITY ) // BackupHeader represents a backup stream of a file. type BackupHeader struct { //revive:disable-next-line:var-naming ID, not Id Id uint32 // The backup stream ID Attributes uint32 // Stream attributes Size int64 // The size of the stream in bytes Name string // The name of the stream (for BackupAlternateData only). Offset int64 // The offset of the stream in the file (for BackupSparseBlock only). } type win32StreamID struct { StreamID uint32 Attributes uint32 Size uint64 NameSize uint32 } // BackupStreamReader reads from a stream produced by the BackupRead Win32 API and produces a series // of BackupHeader values. type BackupStreamReader struct { r io.Reader bytesLeft int64 } // NewBackupStreamReader produces a BackupStreamReader from any io.Reader. func NewBackupStreamReader(r io.Reader) *BackupStreamReader { return &BackupStreamReader{r, 0} } // Next returns the next backup stream and prepares for calls to Read(). It skips the remainder of the current stream if // it was not completely read. func (r *BackupStreamReader) Next() (*BackupHeader, error) { if r.bytesLeft > 0 { //nolint:nestif // todo: flatten this if s, ok := r.r.(io.Seeker); ok { // Make sure Seek on io.SeekCurrent sometimes succeeds // before trying the actual seek. if _, err := s.Seek(0, io.SeekCurrent); err == nil { if _, err = s.Seek(r.bytesLeft, io.SeekCurrent); err != nil { return nil, err } r.bytesLeft = 0 } } if _, err := io.Copy(io.Discard, r); err != nil { return nil, err } } var wsi win32StreamID if err := binary.Read(r.r, binary.LittleEndian, &wsi); err != nil { return nil, err } hdr := &BackupHeader{ Id: wsi.StreamID, Attributes: wsi.Attributes, Size: int64(wsi.Size), } if wsi.NameSize != 0 { name := make([]uint16, int(wsi.NameSize/2)) if err := binary.Read(r.r, binary.LittleEndian, name); err != nil { return nil, err } hdr.Name = syscall.UTF16ToString(name) } if wsi.StreamID == BackupSparseBlock { if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil { return nil, err } hdr.Size -= 8 } r.bytesLeft = hdr.Size return hdr, nil } // Read reads from the current backup stream. func (r *BackupStreamReader) Read(b []byte) (int, error) { if r.bytesLeft == 0 { return 0, io.EOF } if int64(len(b)) > r.bytesLeft { b = b[:r.bytesLeft] } n, err := r.r.Read(b) r.bytesLeft -= int64(n) if err == io.EOF { err = io.ErrUnexpectedEOF } else if r.bytesLeft == 0 && err == nil { err = io.EOF } return n, err } // BackupStreamWriter writes a stream compatible with the BackupWrite Win32 API. type BackupStreamWriter struct { w io.Writer bytesLeft int64 } // NewBackupStreamWriter produces a BackupStreamWriter on top of an io.Writer. func NewBackupStreamWriter(w io.Writer) *BackupStreamWriter { return &BackupStreamWriter{w, 0} } // WriteHeader writes the next backup stream header and prepares for calls to Write(). func (w *BackupStreamWriter) WriteHeader(hdr *BackupHeader) error { if w.bytesLeft != 0 { return fmt.Errorf("missing %d bytes", w.bytesLeft) } name := utf16.Encode([]rune(hdr.Name)) wsi := win32StreamID{ StreamID: hdr.Id, Attributes: hdr.Attributes, Size: uint64(hdr.Size), NameSize: uint32(len(name) * 2), } if hdr.Id == BackupSparseBlock { // Include space for the int64 block offset wsi.Size += 8 } if err := binary.Write(w.w, binary.LittleEndian, &wsi); err != nil { return err } if len(name) != 0 { if err := binary.Write(w.w, binary.LittleEndian, name); err != nil { return err } } if hdr.Id == BackupSparseBlock { if err := binary.Write(w.w, binary.LittleEndian, hdr.Offset); err != nil { return err } } w.bytesLeft = hdr.Size return nil } // Write writes to the current backup stream. func (w *BackupStreamWriter) Write(b []byte) (int, error) { if w.bytesLeft < int64(len(b)) { return 0, fmt.Errorf("too many bytes by %d", int64(len(b))-w.bytesLeft) } n, err := w.w.Write(b) w.bytesLeft -= int64(n) return n, err } // BackupFileReader provides an io.ReadCloser interface on top of the BackupRead Win32 API. type BackupFileReader struct { f *os.File includeSecurity bool ctx uintptr } // NewBackupFileReader returns a new BackupFileReader from a file handle. If includeSecurity is true, // Read will attempt to read the security descriptor of the file. func NewBackupFileReader(f *os.File, includeSecurity bool) *BackupFileReader { r := &BackupFileReader{f, includeSecurity, 0} return r } // Read reads a backup stream from the file by calling the Win32 API BackupRead(). func (r *BackupFileReader) Read(b []byte) (int, error) { var bytesRead uint32 err := backupRead(syscall.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx) if err != nil { return 0, &os.PathError{Op: "BackupRead", Path: r.f.Name(), Err: err} } runtime.KeepAlive(r.f) if bytesRead == 0 { return 0, io.EOF } return int(bytesRead), nil } // Close frees Win32 resources associated with the BackupFileReader. It does not close // the underlying file. func (r *BackupFileReader) Close() error { if r.ctx != 0 { _ = backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx) runtime.KeepAlive(r.f) r.ctx = 0 } return nil } // BackupFileWriter provides an io.WriteCloser interface on top of the BackupWrite Win32 API. type BackupFileWriter struct { f *os.File includeSecurity bool ctx uintptr } // NewBackupFileWriter returns a new BackupFileWriter from a file handle. If includeSecurity is true, // Write() will attempt to restore the security descriptor from the stream. func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter { w := &BackupFileWriter{f, includeSecurity, 0} return w } // Write restores a portion of the file using the provided backup stream. func (w *BackupFileWriter) Write(b []byte) (int, error) { var bytesWritten uint32 err := backupWrite(syscall.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx) if err != nil { return 0, &os.PathError{Op: "BackupWrite", Path: w.f.Name(), Err: err} } runtime.KeepAlive(w.f) if int(bytesWritten) != len(b) { return int(bytesWritten), errors.New("not all bytes could be written") } return len(b), nil } // Close frees Win32 resources associated with the BackupFileWriter. It does not // close the underlying file. func (w *BackupFileWriter) Close() error { if w.ctx != 0 { _ = backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx) runtime.KeepAlive(w.f) w.ctx = 0 } return nil } // OpenForBackup opens a file or directory, potentially skipping access checks if the backup // or restore privileges have been acquired. // // If the file opened was a directory, it cannot be used with Readdir(). func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) { winPath, err := syscall.UTF16FromString(path) if err != nil { return nil, err } h, err := syscall.CreateFile(&winPath[0], access, share, nil, createmode, syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT, 0) if err != nil { err = &os.PathError{Op: "open", Path: path, Err: err} return nil, err } return os.NewFile(uintptr(h), path), nil } ================================================ FILE: vendor/github.com/Microsoft/go-winio/doc.go ================================================ // This package provides utilities for efficiently performing Win32 IO operations in Go. // Currently, this package is provides support for genreal IO and management of // - named pipes // - files // - [Hyper-V sockets] // // This code is similar to Go's [net] package, and uses IO completion ports to avoid // blocking IO on system threads, allowing Go to reuse the thread to schedule other goroutines. // // This limits support to Windows Vista and newer operating systems. // // Additionally, this package provides support for: // - creating and managing GUIDs // - writing to [ETW] // - opening and manageing VHDs // - parsing [Windows Image files] // - auto-generating Win32 API code // // [Hyper-V sockets]: https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/make-integration-service // [ETW]: https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/event-tracing-for-windows--etw- // [Windows Image files]: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/work-with-windows-images package winio ================================================ FILE: vendor/github.com/Microsoft/go-winio/ea.go ================================================ package winio import ( "bytes" "encoding/binary" "errors" ) type fileFullEaInformation struct { NextEntryOffset uint32 Flags uint8 NameLength uint8 ValueLength uint16 } var ( fileFullEaInformationSize = binary.Size(&fileFullEaInformation{}) errInvalidEaBuffer = errors.New("invalid extended attribute buffer") errEaNameTooLarge = errors.New("extended attribute name too large") errEaValueTooLarge = errors.New("extended attribute value too large") ) // ExtendedAttribute represents a single Windows EA. type ExtendedAttribute struct { Name string Value []byte Flags uint8 } func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) { var info fileFullEaInformation err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info) if err != nil { err = errInvalidEaBuffer return ea, nb, err } nameOffset := fileFullEaInformationSize nameLen := int(info.NameLength) valueOffset := nameOffset + int(info.NameLength) + 1 valueLen := int(info.ValueLength) nextOffset := int(info.NextEntryOffset) if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) { err = errInvalidEaBuffer return ea, nb, err } ea.Name = string(b[nameOffset : nameOffset+nameLen]) ea.Value = b[valueOffset : valueOffset+valueLen] ea.Flags = info.Flags if info.NextEntryOffset != 0 { nb = b[info.NextEntryOffset:] } return ea, nb, err } // DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION // buffer retrieved from BackupRead, ZwQueryEaFile, etc. func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) { for len(b) != 0 { ea, nb, err := parseEa(b) if err != nil { return nil, err } eas = append(eas, ea) b = nb } return eas, err } func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error { if int(uint8(len(ea.Name))) != len(ea.Name) { return errEaNameTooLarge } if int(uint16(len(ea.Value))) != len(ea.Value) { return errEaValueTooLarge } entrySize := uint32(fileFullEaInformationSize + len(ea.Name) + 1 + len(ea.Value)) withPadding := (entrySize + 3) &^ 3 nextOffset := uint32(0) if !last { nextOffset = withPadding } info := fileFullEaInformation{ NextEntryOffset: nextOffset, Flags: ea.Flags, NameLength: uint8(len(ea.Name)), ValueLength: uint16(len(ea.Value)), } err := binary.Write(buf, binary.LittleEndian, &info) if err != nil { return err } _, err = buf.Write([]byte(ea.Name)) if err != nil { return err } err = buf.WriteByte(0) if err != nil { return err } _, err = buf.Write(ea.Value) if err != nil { return err } _, err = buf.Write([]byte{0, 0, 0}[0 : withPadding-entrySize]) if err != nil { return err } return nil } // EncodeExtendedAttributes encodes a list of EAs into a FILE_FULL_EA_INFORMATION // buffer for use with BackupWrite, ZwSetEaFile, etc. func EncodeExtendedAttributes(eas []ExtendedAttribute) ([]byte, error) { var buf bytes.Buffer for i := range eas { last := false if i == len(eas)-1 { last = true } err := writeEa(&buf, &eas[i], last) if err != nil { return nil, err } } return buf.Bytes(), nil } ================================================ FILE: vendor/github.com/Microsoft/go-winio/file.go ================================================ //go:build windows // +build windows package winio import ( "errors" "io" "runtime" "sync" "sync/atomic" "syscall" "time" "golang.org/x/sys/windows" ) //sys cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) = CancelIoEx //sys createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) = CreateIoCompletionPort //sys getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus //sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes //sys wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult type atomicBool int32 func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 } func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) } func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) } //revive:disable-next-line:predeclared Keep "new" to maintain consistency with "atomic" pkg func (b *atomicBool) swap(new bool) bool { var newInt int32 if new { newInt = 1 } return atomic.SwapInt32((*int32)(b), newInt) == 1 } var ( ErrFileClosed = errors.New("file has already been closed") ErrTimeout = &timeoutError{} ) type timeoutError struct{} func (*timeoutError) Error() string { return "i/o timeout" } func (*timeoutError) Timeout() bool { return true } func (*timeoutError) Temporary() bool { return true } type timeoutChan chan struct{} var ioInitOnce sync.Once var ioCompletionPort syscall.Handle // ioResult contains the result of an asynchronous IO operation. type ioResult struct { bytes uint32 err error } // ioOperation represents an outstanding asynchronous Win32 IO. type ioOperation struct { o syscall.Overlapped ch chan ioResult } func initIO() { h, err := createIoCompletionPort(syscall.InvalidHandle, 0, 0, 0xffffffff) if err != nil { panic(err) } ioCompletionPort = h go ioCompletionProcessor(h) } // win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall. // It takes ownership of this handle and will close it if it is garbage collected. type win32File struct { handle syscall.Handle wg sync.WaitGroup wgLock sync.RWMutex closing atomicBool socket bool readDeadline deadlineHandler writeDeadline deadlineHandler } type deadlineHandler struct { setLock sync.Mutex channel timeoutChan channelLock sync.RWMutex timer *time.Timer timedout atomicBool } // makeWin32File makes a new win32File from an existing file handle. func makeWin32File(h syscall.Handle) (*win32File, error) { f := &win32File{handle: h} ioInitOnce.Do(initIO) _, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff) if err != nil { return nil, err } err = setFileCompletionNotificationModes(h, windows.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS|windows.FILE_SKIP_SET_EVENT_ON_HANDLE) if err != nil { return nil, err } f.readDeadline.channel = make(timeoutChan) f.writeDeadline.channel = make(timeoutChan) return f, nil } func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) { // If we return the result of makeWin32File directly, it can result in an // interface-wrapped nil, rather than a nil interface value. f, err := makeWin32File(h) if err != nil { return nil, err } return f, nil } // closeHandle closes the resources associated with a Win32 handle. func (f *win32File) closeHandle() { f.wgLock.Lock() // Atomically set that we are closing, releasing the resources only once. if !f.closing.swap(true) { f.wgLock.Unlock() // cancel all IO and wait for it to complete _ = cancelIoEx(f.handle, nil) f.wg.Wait() // at this point, no new IO can start syscall.Close(f.handle) f.handle = 0 } else { f.wgLock.Unlock() } } // Close closes a win32File. func (f *win32File) Close() error { f.closeHandle() return nil } // IsClosed checks if the file has been closed. func (f *win32File) IsClosed() bool { return f.closing.isSet() } // prepareIO prepares for a new IO operation. // The caller must call f.wg.Done() when the IO is finished, prior to Close() returning. func (f *win32File) prepareIO() (*ioOperation, error) { f.wgLock.RLock() if f.closing.isSet() { f.wgLock.RUnlock() return nil, ErrFileClosed } f.wg.Add(1) f.wgLock.RUnlock() c := &ioOperation{} c.ch = make(chan ioResult) return c, nil } // ioCompletionProcessor processes completed async IOs forever. func ioCompletionProcessor(h syscall.Handle) { for { var bytes uint32 var key uintptr var op *ioOperation err := getQueuedCompletionStatus(h, &bytes, &key, &op, syscall.INFINITE) if op == nil { panic(err) } op.ch <- ioResult{bytes, err} } } // todo: helsaawy - create an asyncIO version that takes a context // asyncIO processes the return value from ReadFile or WriteFile, blocking until // the operation has actually completed. func (f *win32File) asyncIO(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) { if err != syscall.ERROR_IO_PENDING { //nolint:errorlint // err is Errno return int(bytes), err } if f.closing.isSet() { _ = cancelIoEx(f.handle, &c.o) } var timeout timeoutChan if d != nil { d.channelLock.Lock() timeout = d.channel d.channelLock.Unlock() } var r ioResult select { case r = <-c.ch: err = r.err if err == syscall.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno if f.closing.isSet() { err = ErrFileClosed } } else if err != nil && f.socket { // err is from Win32. Query the overlapped structure to get the winsock error. var bytes, flags uint32 err = wsaGetOverlappedResult(f.handle, &c.o, &bytes, false, &flags) } case <-timeout: _ = cancelIoEx(f.handle, &c.o) r = <-c.ch err = r.err if err == syscall.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno err = ErrTimeout } } // runtime.KeepAlive is needed, as c is passed via native // code to ioCompletionProcessor, c must remain alive // until the channel read is complete. // todo: (de)allocate *ioOperation via win32 heap functions, instead of needing to KeepAlive? runtime.KeepAlive(c) return int(r.bytes), err } // Read reads from a file handle. func (f *win32File) Read(b []byte) (int, error) { c, err := f.prepareIO() if err != nil { return 0, err } defer f.wg.Done() if f.readDeadline.timedout.isSet() { return 0, ErrTimeout } var bytes uint32 err = syscall.ReadFile(f.handle, b, &bytes, &c.o) n, err := f.asyncIO(c, &f.readDeadline, bytes, err) runtime.KeepAlive(b) // Handle EOF conditions. if err == nil && n == 0 && len(b) != 0 { return 0, io.EOF } else if err == syscall.ERROR_BROKEN_PIPE { //nolint:errorlint // err is Errno return 0, io.EOF } else { return n, err } } // Write writes to a file handle. func (f *win32File) Write(b []byte) (int, error) { c, err := f.prepareIO() if err != nil { return 0, err } defer f.wg.Done() if f.writeDeadline.timedout.isSet() { return 0, ErrTimeout } var bytes uint32 err = syscall.WriteFile(f.handle, b, &bytes, &c.o) n, err := f.asyncIO(c, &f.writeDeadline, bytes, err) runtime.KeepAlive(b) return n, err } func (f *win32File) SetReadDeadline(deadline time.Time) error { return f.readDeadline.set(deadline) } func (f *win32File) SetWriteDeadline(deadline time.Time) error { return f.writeDeadline.set(deadline) } func (f *win32File) Flush() error { return syscall.FlushFileBuffers(f.handle) } func (f *win32File) Fd() uintptr { return uintptr(f.handle) } func (d *deadlineHandler) set(deadline time.Time) error { d.setLock.Lock() defer d.setLock.Unlock() if d.timer != nil { if !d.timer.Stop() { <-d.channel } d.timer = nil } d.timedout.setFalse() select { case <-d.channel: d.channelLock.Lock() d.channel = make(chan struct{}) d.channelLock.Unlock() default: } if deadline.IsZero() { return nil } timeoutIO := func() { d.timedout.setTrue() close(d.channel) } now := time.Now() duration := deadline.Sub(now) if deadline.After(now) { // Deadline is in the future, set a timer to wait d.timer = time.AfterFunc(duration, timeoutIO) } else { // Deadline is in the past. Cancel all pending IO now. timeoutIO() } return nil } ================================================ FILE: vendor/github.com/Microsoft/go-winio/fileinfo.go ================================================ //go:build windows // +build windows package winio import ( "os" "runtime" "unsafe" "golang.org/x/sys/windows" ) // FileBasicInfo contains file access time and file attributes information. type FileBasicInfo struct { CreationTime, LastAccessTime, LastWriteTime, ChangeTime windows.Filetime FileAttributes uint32 _ uint32 // padding } // GetFileBasicInfo retrieves times and attributes for a file. func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) { bi := &FileBasicInfo{} if err := windows.GetFileInformationByHandleEx( windows.Handle(f.Fd()), windows.FileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi)), ); err != nil { return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} } runtime.KeepAlive(f) return bi, nil } // SetFileBasicInfo sets times and attributes for a file. func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error { if err := windows.SetFileInformationByHandle( windows.Handle(f.Fd()), windows.FileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi)), ); err != nil { return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err} } runtime.KeepAlive(f) return nil } // FileStandardInfo contains extended information for the file. // FILE_STANDARD_INFO in WinBase.h // https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_standard_info type FileStandardInfo struct { AllocationSize, EndOfFile int64 NumberOfLinks uint32 DeletePending, Directory bool } // GetFileStandardInfo retrieves ended information for the file. func GetFileStandardInfo(f *os.File) (*FileStandardInfo, error) { si := &FileStandardInfo{} if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), windows.FileStandardInfo, (*byte)(unsafe.Pointer(si)), uint32(unsafe.Sizeof(*si))); err != nil { return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} } runtime.KeepAlive(f) return si, nil } // FileIDInfo contains the volume serial number and file ID for a file. This pair should be // unique on a system. type FileIDInfo struct { VolumeSerialNumber uint64 FileID [16]byte } // GetFileID retrieves the unique (volume, file ID) pair for a file. func GetFileID(f *os.File) (*FileIDInfo, error) { fileID := &FileIDInfo{} if err := windows.GetFileInformationByHandleEx( windows.Handle(f.Fd()), windows.FileIdInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID)), ); err != nil { return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} } runtime.KeepAlive(f) return fileID, nil } ================================================ FILE: vendor/github.com/Microsoft/go-winio/hvsock.go ================================================ //go:build windows // +build windows package winio import ( "context" "errors" "fmt" "io" "net" "os" "syscall" "time" "unsafe" "golang.org/x/sys/windows" "github.com/Microsoft/go-winio/internal/socket" "github.com/Microsoft/go-winio/pkg/guid" ) const afHVSock = 34 // AF_HYPERV // Well known Service and VM IDs // https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/make-integration-service#vmid-wildcards // HvsockGUIDWildcard is the wildcard VmId for accepting connections from all partitions. func HvsockGUIDWildcard() guid.GUID { // 00000000-0000-0000-0000-000000000000 return guid.GUID{} } // HvsockGUIDBroadcast is the wildcard VmId for broadcasting sends to all partitions. func HvsockGUIDBroadcast() guid.GUID { // ffffffff-ffff-ffff-ffff-ffffffffffff return guid.GUID{ Data1: 0xffffffff, Data2: 0xffff, Data3: 0xffff, Data4: [8]uint8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, } } // HvsockGUIDLoopback is the Loopback VmId for accepting connections to the same partition as the connector. func HvsockGUIDLoopback() guid.GUID { // e0e16197-dd56-4a10-9195-5ee7a155a838 return guid.GUID{ Data1: 0xe0e16197, Data2: 0xdd56, Data3: 0x4a10, Data4: [8]uint8{0x91, 0x95, 0x5e, 0xe7, 0xa1, 0x55, 0xa8, 0x38}, } } // HvsockGUIDSiloHost is the address of a silo's host partition: // - The silo host of a hosted silo is the utility VM. // - The silo host of a silo on a physical host is the physical host. func HvsockGUIDSiloHost() guid.GUID { // 36bd0c5c-7276-4223-88ba-7d03b654c568 return guid.GUID{ Data1: 0x36bd0c5c, Data2: 0x7276, Data3: 0x4223, Data4: [8]byte{0x88, 0xba, 0x7d, 0x03, 0xb6, 0x54, 0xc5, 0x68}, } } // HvsockGUIDChildren is the wildcard VmId for accepting connections from the connector's child partitions. func HvsockGUIDChildren() guid.GUID { // 90db8b89-0d35-4f79-8ce9-49ea0ac8b7cd return guid.GUID{ Data1: 0x90db8b89, Data2: 0xd35, Data3: 0x4f79, Data4: [8]uint8{0x8c, 0xe9, 0x49, 0xea, 0xa, 0xc8, 0xb7, 0xcd}, } } // HvsockGUIDParent is the wildcard VmId for accepting connections from the connector's parent partition. // Listening on this VmId accepts connection from: // - Inside silos: silo host partition. // - Inside hosted silo: host of the VM. // - Inside VM: VM host. // - Physical host: Not supported. func HvsockGUIDParent() guid.GUID { // a42e7cda-d03f-480c-9cc2-a4de20abb878 return guid.GUID{ Data1: 0xa42e7cda, Data2: 0xd03f, Data3: 0x480c, Data4: [8]uint8{0x9c, 0xc2, 0xa4, 0xde, 0x20, 0xab, 0xb8, 0x78}, } } // hvsockVsockServiceTemplate is the Service GUID used for the VSOCK protocol. func hvsockVsockServiceTemplate() guid.GUID { // 00000000-facb-11e6-bd58-64006a7986d3 return guid.GUID{ Data2: 0xfacb, Data3: 0x11e6, Data4: [8]uint8{0xbd, 0x58, 0x64, 0x00, 0x6a, 0x79, 0x86, 0xd3}, } } // An HvsockAddr is an address for a AF_HYPERV socket. type HvsockAddr struct { VMID guid.GUID ServiceID guid.GUID } type rawHvsockAddr struct { Family uint16 _ uint16 VMID guid.GUID ServiceID guid.GUID } var _ socket.RawSockaddr = &rawHvsockAddr{} // Network returns the address's network name, "hvsock". func (*HvsockAddr) Network() string { return "hvsock" } func (addr *HvsockAddr) String() string { return fmt.Sprintf("%s:%s", &addr.VMID, &addr.ServiceID) } // VsockServiceID returns an hvsock service ID corresponding to the specified AF_VSOCK port. func VsockServiceID(port uint32) guid.GUID { g := hvsockVsockServiceTemplate() // make a copy g.Data1 = port return g } func (addr *HvsockAddr) raw() rawHvsockAddr { return rawHvsockAddr{ Family: afHVSock, VMID: addr.VMID, ServiceID: addr.ServiceID, } } func (addr *HvsockAddr) fromRaw(raw *rawHvsockAddr) { addr.VMID = raw.VMID addr.ServiceID = raw.ServiceID } // Sockaddr returns a pointer to and the size of this struct. // // Implements the [socket.RawSockaddr] interface, and allows use in // [socket.Bind] and [socket.ConnectEx]. func (r *rawHvsockAddr) Sockaddr() (unsafe.Pointer, int32, error) { return unsafe.Pointer(r), int32(unsafe.Sizeof(rawHvsockAddr{})), nil } // Sockaddr interface allows use with `sockets.Bind()` and `.ConnectEx()`. func (r *rawHvsockAddr) FromBytes(b []byte) error { n := int(unsafe.Sizeof(rawHvsockAddr{})) if len(b) < n { return fmt.Errorf("got %d, want %d: %w", len(b), n, socket.ErrBufferSize) } copy(unsafe.Slice((*byte)(unsafe.Pointer(r)), n), b[:n]) if r.Family != afHVSock { return fmt.Errorf("got %d, want %d: %w", r.Family, afHVSock, socket.ErrAddrFamily) } return nil } // HvsockListener is a socket listener for the AF_HYPERV address family. type HvsockListener struct { sock *win32File addr HvsockAddr } var _ net.Listener = &HvsockListener{} // HvsockConn is a connected socket of the AF_HYPERV address family. type HvsockConn struct { sock *win32File local, remote HvsockAddr } var _ net.Conn = &HvsockConn{} func newHVSocket() (*win32File, error) { fd, err := syscall.Socket(afHVSock, syscall.SOCK_STREAM, 1) if err != nil { return nil, os.NewSyscallError("socket", err) } f, err := makeWin32File(fd) if err != nil { syscall.Close(fd) return nil, err } f.socket = true return f, nil } // ListenHvsock listens for connections on the specified hvsock address. func ListenHvsock(addr *HvsockAddr) (_ *HvsockListener, err error) { l := &HvsockListener{addr: *addr} sock, err := newHVSocket() if err != nil { return nil, l.opErr("listen", err) } sa := addr.raw() err = socket.Bind(windows.Handle(sock.handle), &sa) if err != nil { return nil, l.opErr("listen", os.NewSyscallError("socket", err)) } err = syscall.Listen(sock.handle, 16) if err != nil { return nil, l.opErr("listen", os.NewSyscallError("listen", err)) } return &HvsockListener{sock: sock, addr: *addr}, nil } func (l *HvsockListener) opErr(op string, err error) error { return &net.OpError{Op: op, Net: "hvsock", Addr: &l.addr, Err: err} } // Addr returns the listener's network address. func (l *HvsockListener) Addr() net.Addr { return &l.addr } // Accept waits for the next connection and returns it. func (l *HvsockListener) Accept() (_ net.Conn, err error) { sock, err := newHVSocket() if err != nil { return nil, l.opErr("accept", err) } defer func() { if sock != nil { sock.Close() } }() c, err := l.sock.prepareIO() if err != nil { return nil, l.opErr("accept", err) } defer l.sock.wg.Done() // AcceptEx, per documentation, requires an extra 16 bytes per address. // // https://docs.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-acceptex const addrlen = uint32(16 + unsafe.Sizeof(rawHvsockAddr{})) var addrbuf [addrlen * 2]byte var bytes uint32 err = syscall.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0 /* rxdatalen */, addrlen, addrlen, &bytes, &c.o) if _, err = l.sock.asyncIO(c, nil, bytes, err); err != nil { return nil, l.opErr("accept", os.NewSyscallError("acceptex", err)) } conn := &HvsockConn{ sock: sock, } // The local address returned in the AcceptEx buffer is the same as the Listener socket's // address. However, the service GUID reported by GetSockName is different from the Listeners // socket, and is sometimes the same as the local address of the socket that dialed the // address, with the service GUID.Data1 incremented, but othertimes is different. // todo: does the local address matter? is the listener's address or the actual address appropriate? conn.local.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[0]))) conn.remote.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[addrlen]))) // initialize the accepted socket and update its properties with those of the listening socket if err = windows.Setsockopt(windows.Handle(sock.handle), windows.SOL_SOCKET, windows.SO_UPDATE_ACCEPT_CONTEXT, (*byte)(unsafe.Pointer(&l.sock.handle)), int32(unsafe.Sizeof(l.sock.handle))); err != nil { return nil, conn.opErr("accept", os.NewSyscallError("setsockopt", err)) } sock = nil return conn, nil } // Close closes the listener, causing any pending Accept calls to fail. func (l *HvsockListener) Close() error { return l.sock.Close() } // HvsockDialer configures and dials a Hyper-V Socket (ie, [HvsockConn]). type HvsockDialer struct { // Deadline is the time the Dial operation must connect before erroring. Deadline time.Time // Retries is the number of additional connects to try if the connection times out, is refused, // or the host is unreachable Retries uint // RetryWait is the time to wait after a connection error to retry RetryWait time.Duration rt *time.Timer // redial wait timer } // Dial the Hyper-V socket at addr. // // See [HvsockDialer.Dial] for more information. func Dial(ctx context.Context, addr *HvsockAddr) (conn *HvsockConn, err error) { return (&HvsockDialer{}).Dial(ctx, addr) } // Dial attempts to connect to the Hyper-V socket at addr, and returns a connection if successful. // Will attempt (HvsockDialer).Retries if dialing fails, waiting (HvsockDialer).RetryWait between // retries. // // Dialing can be cancelled either by providing (HvsockDialer).Deadline, or cancelling ctx. func (d *HvsockDialer) Dial(ctx context.Context, addr *HvsockAddr) (conn *HvsockConn, err error) { op := "dial" // create the conn early to use opErr() conn = &HvsockConn{ remote: *addr, } if !d.Deadline.IsZero() { var cancel context.CancelFunc ctx, cancel = context.WithDeadline(ctx, d.Deadline) defer cancel() } // preemptive timeout/cancellation check if err = ctx.Err(); err != nil { return nil, conn.opErr(op, err) } sock, err := newHVSocket() if err != nil { return nil, conn.opErr(op, err) } defer func() { if sock != nil { sock.Close() } }() sa := addr.raw() err = socket.Bind(windows.Handle(sock.handle), &sa) if err != nil { return nil, conn.opErr(op, os.NewSyscallError("bind", err)) } c, err := sock.prepareIO() if err != nil { return nil, conn.opErr(op, err) } defer sock.wg.Done() var bytes uint32 for i := uint(0); i <= d.Retries; i++ { err = socket.ConnectEx( windows.Handle(sock.handle), &sa, nil, // sendBuf 0, // sendDataLen &bytes, (*windows.Overlapped)(unsafe.Pointer(&c.o))) _, err = sock.asyncIO(c, nil, bytes, err) if i < d.Retries && canRedial(err) { if err = d.redialWait(ctx); err == nil { continue } } break } if err != nil { return nil, conn.opErr(op, os.NewSyscallError("connectex", err)) } // update the connection properties, so shutdown can be used if err = windows.Setsockopt( windows.Handle(sock.handle), windows.SOL_SOCKET, windows.SO_UPDATE_CONNECT_CONTEXT, nil, // optvalue 0, // optlen ); err != nil { return nil, conn.opErr(op, os.NewSyscallError("setsockopt", err)) } // get the local name var sal rawHvsockAddr err = socket.GetSockName(windows.Handle(sock.handle), &sal) if err != nil { return nil, conn.opErr(op, os.NewSyscallError("getsockname", err)) } conn.local.fromRaw(&sal) // one last check for timeout, since asyncIO doesn't check the context if err = ctx.Err(); err != nil { return nil, conn.opErr(op, err) } conn.sock = sock sock = nil return conn, nil } // redialWait waits before attempting to redial, resetting the timer as appropriate. func (d *HvsockDialer) redialWait(ctx context.Context) (err error) { if d.RetryWait == 0 { return nil } if d.rt == nil { d.rt = time.NewTimer(d.RetryWait) } else { // should already be stopped and drained d.rt.Reset(d.RetryWait) } select { case <-ctx.Done(): case <-d.rt.C: return nil } // stop and drain the timer if !d.rt.Stop() { <-d.rt.C } return ctx.Err() } // assumes error is a plain, unwrapped syscall.Errno provided by direct syscall. func canRedial(err error) bool { //nolint:errorlint // guaranteed to be an Errno switch err { case windows.WSAECONNREFUSED, windows.WSAENETUNREACH, windows.WSAETIMEDOUT, windows.ERROR_CONNECTION_REFUSED, windows.ERROR_CONNECTION_UNAVAIL: return true default: return false } } func (conn *HvsockConn) opErr(op string, err error) error { // translate from "file closed" to "socket closed" if errors.Is(err, ErrFileClosed) { err = socket.ErrSocketClosed } return &net.OpError{Op: op, Net: "hvsock", Source: &conn.local, Addr: &conn.remote, Err: err} } func (conn *HvsockConn) Read(b []byte) (int, error) { c, err := conn.sock.prepareIO() if err != nil { return 0, conn.opErr("read", err) } defer conn.sock.wg.Done() buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))} var flags, bytes uint32 err = syscall.WSARecv(conn.sock.handle, &buf, 1, &bytes, &flags, &c.o, nil) n, err := conn.sock.asyncIO(c, &conn.sock.readDeadline, bytes, err) if err != nil { var eno windows.Errno if errors.As(err, &eno) { err = os.NewSyscallError("wsarecv", eno) } return 0, conn.opErr("read", err) } else if n == 0 { err = io.EOF } return n, err } func (conn *HvsockConn) Write(b []byte) (int, error) { t := 0 for len(b) != 0 { n, err := conn.write(b) if err != nil { return t + n, err } t += n b = b[n:] } return t, nil } func (conn *HvsockConn) write(b []byte) (int, error) { c, err := conn.sock.prepareIO() if err != nil { return 0, conn.opErr("write", err) } defer conn.sock.wg.Done() buf := syscall.WSABuf{Buf: &b[0], Len: uint32(len(b))} var bytes uint32 err = syscall.WSASend(conn.sock.handle, &buf, 1, &bytes, 0, &c.o, nil) n, err := conn.sock.asyncIO(c, &conn.sock.writeDeadline, bytes, err) if err != nil { var eno windows.Errno if errors.As(err, &eno) { err = os.NewSyscallError("wsasend", eno) } return 0, conn.opErr("write", err) } return n, err } // Close closes the socket connection, failing any pending read or write calls. func (conn *HvsockConn) Close() error { return conn.sock.Close() } func (conn *HvsockConn) IsClosed() bool { return conn.sock.IsClosed() } // shutdown disables sending or receiving on a socket. func (conn *HvsockConn) shutdown(how int) error { if conn.IsClosed() { return socket.ErrSocketClosed } err := syscall.Shutdown(conn.sock.handle, how) if err != nil { // If the connection was closed, shutdowns fail with "not connected" if errors.Is(err, windows.WSAENOTCONN) || errors.Is(err, windows.WSAESHUTDOWN) { err = socket.ErrSocketClosed } return os.NewSyscallError("shutdown", err) } return nil } // CloseRead shuts down the read end of the socket, preventing future read operations. func (conn *HvsockConn) CloseRead() error { err := conn.shutdown(syscall.SHUT_RD) if err != nil { return conn.opErr("closeread", err) } return nil } // CloseWrite shuts down the write end of the socket, preventing future write operations and // notifying the other endpoint that no more data will be written. func (conn *HvsockConn) CloseWrite() error { err := conn.shutdown(syscall.SHUT_WR) if err != nil { return conn.opErr("closewrite", err) } return nil } // LocalAddr returns the local address of the connection. func (conn *HvsockConn) LocalAddr() net.Addr { return &conn.local } // RemoteAddr returns the remote address of the connection. func (conn *HvsockConn) RemoteAddr() net.Addr { return &conn.remote } // SetDeadline implements the net.Conn SetDeadline method. func (conn *HvsockConn) SetDeadline(t time.Time) error { // todo: implement `SetDeadline` for `win32File` if err := conn.SetReadDeadline(t); err != nil { return fmt.Errorf("set read deadline: %w", err) } if err := conn.SetWriteDeadline(t); err != nil { return fmt.Errorf("set write deadline: %w", err) } return nil } // SetReadDeadline implements the net.Conn SetReadDeadline method. func (conn *HvsockConn) SetReadDeadline(t time.Time) error { return conn.sock.SetReadDeadline(t) } // SetWriteDeadline implements the net.Conn SetWriteDeadline method. func (conn *HvsockConn) SetWriteDeadline(t time.Time) error { return conn.sock.SetWriteDeadline(t) } ================================================ FILE: vendor/github.com/Microsoft/go-winio/internal/fs/doc.go ================================================ // This package contains Win32 filesystem functionality. package fs ================================================ FILE: vendor/github.com/Microsoft/go-winio/internal/fs/fs.go ================================================ //go:build windows package fs import ( "golang.org/x/sys/windows" "github.com/Microsoft/go-winio/internal/stringbuffer" ) //go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go fs.go // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew //sys CreateFile(name string, access AccessMask, mode FileShareMode, sa *syscall.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) [failretval==windows.InvalidHandle] = CreateFileW const NullHandle windows.Handle = 0 // AccessMask defines standard, specific, and generic rights. // // Bitmask: // 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 // 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 // +---------------+---------------+-------------------------------+ // |G|G|G|G|Resvd|A| StandardRights| SpecificRights | // |R|W|E|A| |S| | | // +-+-------------+---------------+-------------------------------+ // // GR Generic Read // GW Generic Write // GE Generic Exectue // GA Generic All // Resvd Reserved // AS Access Security System // // https://learn.microsoft.com/en-us/windows/win32/secauthz/access-mask // // https://learn.microsoft.com/en-us/windows/win32/secauthz/generic-access-rights // // https://learn.microsoft.com/en-us/windows/win32/fileio/file-access-rights-constants type AccessMask = windows.ACCESS_MASK //nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. const ( // Not actually any. // // For CreateFile: "query certain metadata such as file, directory, or device attributes without accessing that file or device" // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew#parameters FILE_ANY_ACCESS AccessMask = 0 // Specific Object Access // from ntioapi.h FILE_READ_DATA AccessMask = (0x0001) // file & pipe FILE_LIST_DIRECTORY AccessMask = (0x0001) // directory FILE_WRITE_DATA AccessMask = (0x0002) // file & pipe FILE_ADD_FILE AccessMask = (0x0002) // directory FILE_APPEND_DATA AccessMask = (0x0004) // file FILE_ADD_SUBDIRECTORY AccessMask = (0x0004) // directory FILE_CREATE_PIPE_INSTANCE AccessMask = (0x0004) // named pipe FILE_READ_EA AccessMask = (0x0008) // file & directory FILE_READ_PROPERTIES AccessMask = FILE_READ_EA FILE_WRITE_EA AccessMask = (0x0010) // file & directory FILE_WRITE_PROPERTIES AccessMask = FILE_WRITE_EA FILE_EXECUTE AccessMask = (0x0020) // file FILE_TRAVERSE AccessMask = (0x0020) // directory FILE_DELETE_CHILD AccessMask = (0x0040) // directory FILE_READ_ATTRIBUTES AccessMask = (0x0080) // all FILE_WRITE_ATTRIBUTES AccessMask = (0x0100) // all FILE_ALL_ACCESS AccessMask = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF) FILE_GENERIC_READ AccessMask = (STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE) FILE_GENERIC_WRITE AccessMask = (STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE) FILE_GENERIC_EXECUTE AccessMask = (STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE) SPECIFIC_RIGHTS_ALL AccessMask = 0x0000FFFF // Standard Access // from ntseapi.h DELETE AccessMask = 0x0001_0000 READ_CONTROL AccessMask = 0x0002_0000 WRITE_DAC AccessMask = 0x0004_0000 WRITE_OWNER AccessMask = 0x0008_0000 SYNCHRONIZE AccessMask = 0x0010_0000 STANDARD_RIGHTS_REQUIRED AccessMask = 0x000F_0000 STANDARD_RIGHTS_READ AccessMask = READ_CONTROL STANDARD_RIGHTS_WRITE AccessMask = READ_CONTROL STANDARD_RIGHTS_EXECUTE AccessMask = READ_CONTROL STANDARD_RIGHTS_ALL AccessMask = 0x001F_0000 ) type FileShareMode uint32 //nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. const ( FILE_SHARE_NONE FileShareMode = 0x00 FILE_SHARE_READ FileShareMode = 0x01 FILE_SHARE_WRITE FileShareMode = 0x02 FILE_SHARE_DELETE FileShareMode = 0x04 FILE_SHARE_VALID_FLAGS FileShareMode = 0x07 ) type FileCreationDisposition uint32 //nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. const ( // from winbase.h CREATE_NEW FileCreationDisposition = 0x01 CREATE_ALWAYS FileCreationDisposition = 0x02 OPEN_EXISTING FileCreationDisposition = 0x03 OPEN_ALWAYS FileCreationDisposition = 0x04 TRUNCATE_EXISTING FileCreationDisposition = 0x05 ) // CreateFile and co. take flags or attributes together as one parameter. // Define alias until we can use generics to allow both // https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants type FileFlagOrAttribute uint32 //nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. const ( // from winnt.h FILE_FLAG_WRITE_THROUGH FileFlagOrAttribute = 0x8000_0000 FILE_FLAG_OVERLAPPED FileFlagOrAttribute = 0x4000_0000 FILE_FLAG_NO_BUFFERING FileFlagOrAttribute = 0x2000_0000 FILE_FLAG_RANDOM_ACCESS FileFlagOrAttribute = 0x1000_0000 FILE_FLAG_SEQUENTIAL_SCAN FileFlagOrAttribute = 0x0800_0000 FILE_FLAG_DELETE_ON_CLOSE FileFlagOrAttribute = 0x0400_0000 FILE_FLAG_BACKUP_SEMANTICS FileFlagOrAttribute = 0x0200_0000 FILE_FLAG_POSIX_SEMANTICS FileFlagOrAttribute = 0x0100_0000 FILE_FLAG_OPEN_REPARSE_POINT FileFlagOrAttribute = 0x0020_0000 FILE_FLAG_OPEN_NO_RECALL FileFlagOrAttribute = 0x0010_0000 FILE_FLAG_FIRST_PIPE_INSTANCE FileFlagOrAttribute = 0x0008_0000 ) type FileSQSFlag = FileFlagOrAttribute //nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. const ( // from winbase.h SECURITY_ANONYMOUS FileSQSFlag = FileSQSFlag(SecurityAnonymous << 16) SECURITY_IDENTIFICATION FileSQSFlag = FileSQSFlag(SecurityIdentification << 16) SECURITY_IMPERSONATION FileSQSFlag = FileSQSFlag(SecurityImpersonation << 16) SECURITY_DELEGATION FileSQSFlag = FileSQSFlag(SecurityDelegation << 16) SECURITY_SQOS_PRESENT FileSQSFlag = 0x00100000 SECURITY_VALID_SQOS_FLAGS FileSQSFlag = 0x001F0000 ) // GetFinalPathNameByHandle flags // // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew#parameters type GetFinalPathFlag uint32 //nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. const ( GetFinalPathDefaultFlag GetFinalPathFlag = 0x0 FILE_NAME_NORMALIZED GetFinalPathFlag = 0x0 FILE_NAME_OPENED GetFinalPathFlag = 0x8 VOLUME_NAME_DOS GetFinalPathFlag = 0x0 VOLUME_NAME_GUID GetFinalPathFlag = 0x1 VOLUME_NAME_NT GetFinalPathFlag = 0x2 VOLUME_NAME_NONE GetFinalPathFlag = 0x4 ) // getFinalPathNameByHandle facilitates calling the Windows API GetFinalPathNameByHandle // with the given handle and flags. It transparently takes care of creating a buffer of the // correct size for the call. // // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew func GetFinalPathNameByHandle(h windows.Handle, flags GetFinalPathFlag) (string, error) { b := stringbuffer.NewWString() //TODO: can loop infinitely if Win32 keeps returning the same (or a larger) n? for { n, err := windows.GetFinalPathNameByHandle(h, b.Pointer(), b.Cap(), uint32(flags)) if err != nil { return "", err } // If the buffer wasn't large enough, n will be the total size needed (including null terminator). // Resize and try again. if n > b.Cap() { b.ResizeTo(n) continue } // If the buffer is large enough, n will be the size not including the null terminator. // Convert to a Go string and return. return b.String(), nil } } ================================================ FILE: vendor/github.com/Microsoft/go-winio/internal/fs/security.go ================================================ package fs // https://learn.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level type SecurityImpersonationLevel int32 // C default enums underlying type is `int`, which is Go `int32` // Impersonation levels const ( SecurityAnonymous SecurityImpersonationLevel = 0 SecurityIdentification SecurityImpersonationLevel = 1 SecurityImpersonation SecurityImpersonationLevel = 2 SecurityDelegation SecurityImpersonationLevel = 3 ) ================================================ FILE: vendor/github.com/Microsoft/go-winio/internal/fs/zsyscall_windows.go ================================================ //go:build windows // Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT. package fs import ( "syscall" "unsafe" "golang.org/x/sys/windows" ) var _ unsafe.Pointer // Do the interface allocations only once for common // Errno values. const ( errnoERROR_IO_PENDING = 997 ) var ( errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) errERROR_EINVAL error = syscall.EINVAL ) // errnoErr returns common boxed Errno values, to prevent // allocations at runtime. func errnoErr(e syscall.Errno) error { switch e { case 0: return errERROR_EINVAL case errnoERROR_IO_PENDING: return errERROR_IO_PENDING } // TODO: add more here, after collecting data on the common // error values see on Windows. (perhaps when running // all.bat?) return e } var ( modkernel32 = windows.NewLazySystemDLL("kernel32.dll") procCreateFileW = modkernel32.NewProc("CreateFileW") ) func CreateFile(name string, access AccessMask, mode FileShareMode, sa *syscall.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) { var _p0 *uint16 _p0, err = syscall.UTF16PtrFromString(name) if err != nil { return } return _CreateFile(_p0, access, mode, sa, createmode, attrs, templatefile) } func _CreateFile(name *uint16, access AccessMask, mode FileShareMode, sa *syscall.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) { r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0) handle = windows.Handle(r0) if handle == windows.InvalidHandle { err = errnoErr(e1) } return } ================================================ FILE: vendor/github.com/Microsoft/go-winio/internal/socket/rawaddr.go ================================================ package socket import ( "unsafe" ) // RawSockaddr allows structs to be used with [Bind] and [ConnectEx]. The // struct must meet the Win32 sockaddr requirements specified here: // https://docs.microsoft.com/en-us/windows/win32/winsock/sockaddr-2 // // Specifically, the struct size must be least larger than an int16 (unsigned short) // for the address family. type RawSockaddr interface { // Sockaddr returns a pointer to the RawSockaddr and its struct size, allowing // for the RawSockaddr's data to be overwritten by syscalls (if necessary). // // It is the callers responsibility to validate that the values are valid; invalid // pointers or size can cause a panic. Sockaddr() (unsafe.Pointer, int32, error) } ================================================ FILE: vendor/github.com/Microsoft/go-winio/internal/socket/socket.go ================================================ //go:build windows package socket import ( "errors" "fmt" "net" "sync" "syscall" "unsafe" "github.com/Microsoft/go-winio/pkg/guid" "golang.org/x/sys/windows" ) //go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go socket.go //sys getsockname(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) [failretval==socketError] = ws2_32.getsockname //sys getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) [failretval==socketError] = ws2_32.getpeername //sys bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) [failretval==socketError] = ws2_32.bind const socketError = uintptr(^uint32(0)) var ( // todo(helsaawy): create custom error types to store the desired vs actual size and addr family? ErrBufferSize = errors.New("buffer size") ErrAddrFamily = errors.New("address family") ErrInvalidPointer = errors.New("invalid pointer") ErrSocketClosed = fmt.Errorf("socket closed: %w", net.ErrClosed) ) // todo(helsaawy): replace these with generics, ie: GetSockName[S RawSockaddr](s windows.Handle) (S, error) // GetSockName writes the local address of socket s to the [RawSockaddr] rsa. // If rsa is not large enough, the [windows.WSAEFAULT] is returned. func GetSockName(s windows.Handle, rsa RawSockaddr) error { ptr, l, err := rsa.Sockaddr() if err != nil { return fmt.Errorf("could not retrieve socket pointer and size: %w", err) } // although getsockname returns WSAEFAULT if the buffer is too small, it does not set // &l to the correct size, so--apart from doubling the buffer repeatedly--there is no remedy return getsockname(s, ptr, &l) } // GetPeerName returns the remote address the socket is connected to. // // See [GetSockName] for more information. func GetPeerName(s windows.Handle, rsa RawSockaddr) error { ptr, l, err := rsa.Sockaddr() if err != nil { return fmt.Errorf("could not retrieve socket pointer and size: %w", err) } return getpeername(s, ptr, &l) } func Bind(s windows.Handle, rsa RawSockaddr) (err error) { ptr, l, err := rsa.Sockaddr() if err != nil { return fmt.Errorf("could not retrieve socket pointer and size: %w", err) } return bind(s, ptr, l) } // "golang.org/x/sys/windows".ConnectEx and .Bind only accept internal implementations of the // their sockaddr interface, so they cannot be used with HvsockAddr // Replicate functionality here from // https://cs.opensource.google/go/x/sys/+/master:windows/syscall_windows.go // The function pointers to `AcceptEx`, `ConnectEx` and `GetAcceptExSockaddrs` must be loaded at // runtime via a WSAIoctl call: // https://docs.microsoft.com/en-us/windows/win32/api/Mswsock/nc-mswsock-lpfn_connectex#remarks type runtimeFunc struct { id guid.GUID once sync.Once addr uintptr err error } func (f *runtimeFunc) Load() error { f.once.Do(func() { var s windows.Handle s, f.err = windows.Socket(windows.AF_INET, windows.SOCK_STREAM, windows.IPPROTO_TCP) if f.err != nil { return } defer windows.CloseHandle(s) //nolint:errcheck var n uint32 f.err = windows.WSAIoctl(s, windows.SIO_GET_EXTENSION_FUNCTION_POINTER, (*byte)(unsafe.Pointer(&f.id)), uint32(unsafe.Sizeof(f.id)), (*byte)(unsafe.Pointer(&f.addr)), uint32(unsafe.Sizeof(f.addr)), &n, nil, // overlapped 0, // completionRoutine ) }) return f.err } var ( // todo: add `AcceptEx` and `GetAcceptExSockaddrs` WSAID_CONNECTEX = guid.GUID{ //revive:disable-line:var-naming ALL_CAPS Data1: 0x25a207b9, Data2: 0xddf3, Data3: 0x4660, Data4: [8]byte{0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e}, } connectExFunc = runtimeFunc{id: WSAID_CONNECTEX} ) func ConnectEx( fd windows.Handle, rsa RawSockaddr, sendBuf *byte, sendDataLen uint32, bytesSent *uint32, overlapped *windows.Overlapped, ) error { if err := connectExFunc.Load(); err != nil { return fmt.Errorf("failed to load ConnectEx function pointer: %w", err) } ptr, n, err := rsa.Sockaddr() if err != nil { return err } return connectEx(fd, ptr, n, sendBuf, sendDataLen, bytesSent, overlapped) } // BOOL LpfnConnectex( // [in] SOCKET s, // [in] const sockaddr *name, // [in] int namelen, // [in, optional] PVOID lpSendBuffer, // [in] DWORD dwSendDataLength, // [out] LPDWORD lpdwBytesSent, // [in] LPOVERLAPPED lpOverlapped // ) func connectEx( s windows.Handle, name unsafe.Pointer, namelen int32, sendBuf *byte, sendDataLen uint32, bytesSent *uint32, overlapped *windows.Overlapped, ) (err error) { // todo: after upgrading to 1.18, switch from syscall.Syscall9 to syscall.SyscallN r1, _, e1 := syscall.Syscall9(connectExFunc.addr, 7, uintptr(s), uintptr(name), uintptr(namelen), uintptr(unsafe.Pointer(sendBuf)), uintptr(sendDataLen), uintptr(unsafe.Pointer(bytesSent)), uintptr(unsafe.Pointer(overlapped)), 0, 0) if r1 == 0 { if e1 != 0 { err = error(e1) } else { err = syscall.EINVAL } } return err } ================================================ FILE: vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go ================================================ //go:build windows // Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT. package socket import ( "syscall" "unsafe" "golang.org/x/sys/windows" ) var _ unsafe.Pointer // Do the interface allocations only once for common // Errno values. const ( errnoERROR_IO_PENDING = 997 ) var ( errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) errERROR_EINVAL error = syscall.EINVAL ) // errnoErr returns common boxed Errno values, to prevent // allocations at runtime. func errnoErr(e syscall.Errno) error { switch e { case 0: return errERROR_EINVAL case errnoERROR_IO_PENDING: return errERROR_IO_PENDING } // TODO: add more here, after collecting data on the common // error values see on Windows. (perhaps when running // all.bat?) return e } var ( modws2_32 = windows.NewLazySystemDLL("ws2_32.dll") procbind = modws2_32.NewProc("bind") procgetpeername = modws2_32.NewProc("getpeername") procgetsockname = modws2_32.NewProc("getsockname") ) func bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) { r1, _, e1 := syscall.Syscall(procbind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen)) if r1 == socketError { err = errnoErr(e1) } return } func getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) { r1, _, e1 := syscall.Syscall(procgetpeername.Addr(), 3, uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen))) if r1 == socketError { err = errnoErr(e1) } return } func getsockname(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) { r1, _, e1 := syscall.Syscall(procgetsockname.Addr(), 3, uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen))) if r1 == socketError { err = errnoErr(e1) } return } ================================================ FILE: vendor/github.com/Microsoft/go-winio/internal/stringbuffer/wstring.go ================================================ package stringbuffer import ( "sync" "unicode/utf16" ) // TODO: worth exporting and using in mkwinsyscall? // Uint16BufferSize is the buffer size in the pool, chosen somewhat arbitrarily to accommodate // large path strings: // MAX_PATH (260) + size of volume GUID prefix (49) + null terminator = 310. const MinWStringCap = 310 // use *[]uint16 since []uint16 creates an extra allocation where the slice header // is copied to heap and then referenced via pointer in the interface header that sync.Pool // stores. var pathPool = sync.Pool{ // if go1.18+ adds Pool[T], use that to store []uint16 directly New: func() interface{} { b := make([]uint16, MinWStringCap) return &b }, } func newBuffer() []uint16 { return *(pathPool.Get().(*[]uint16)) } // freeBuffer copies the slice header data, and puts a pointer to that in the pool. // This avoids taking a pointer to the slice header in WString, which can be set to nil. func freeBuffer(b []uint16) { pathPool.Put(&b) } // WString is a wide string buffer ([]uint16) meant for storing UTF-16 encoded strings // for interacting with Win32 APIs. // Sizes are specified as uint32 and not int. // // It is not thread safe. type WString struct { // type-def allows casting to []uint16 directly, use struct to prevent that and allow adding fields in the future. // raw buffer b []uint16 } // NewWString returns a [WString] allocated from a shared pool with an // initial capacity of at least [MinWStringCap]. // Since the buffer may have been previously used, its contents are not guaranteed to be empty. // // The buffer should be freed via [WString.Free] func NewWString() *WString { return &WString{ b: newBuffer(), } } func (b *WString) Free() { if b.empty() { return } freeBuffer(b.b) b.b = nil } // ResizeTo grows the buffer to at least c and returns the new capacity, freeing the // previous buffer back into pool. func (b *WString) ResizeTo(c uint32) uint32 { // allready sufficient (or n is 0) if c <= b.Cap() { return b.Cap() } if c <= MinWStringCap { c = MinWStringCap } // allocate at-least double buffer size, as is done in [bytes.Buffer] and other places if c <= 2*b.Cap() { c = 2 * b.Cap() } b2 := make([]uint16, c) if !b.empty() { copy(b2, b.b) freeBuffer(b.b) } b.b = b2 return c } // Buffer returns the underlying []uint16 buffer. func (b *WString) Buffer() []uint16 { if b.empty() { return nil } return b.b } // Pointer returns a pointer to the first uint16 in the buffer. // If the [WString.Free] has already been called, the pointer will be nil. func (b *WString) Pointer() *uint16 { if b.empty() { return nil } return &b.b[0] } // String returns the returns the UTF-8 encoding of the UTF-16 string in the buffer. // // It assumes that the data is null-terminated. func (b *WString) String() string { // Using [windows.UTF16ToString] would require importing "golang.org/x/sys/windows" // and would make this code Windows-only, which makes no sense. // So copy UTF16ToString code into here. // If other windows-specific code is added, switch to [windows.UTF16ToString] s := b.b for i, v := range s { if v == 0 { s = s[:i] break } } return string(utf16.Decode(s)) } // Cap returns the underlying buffer capacity. func (b *WString) Cap() uint32 { if b.empty() { return 0 } return b.cap() } func (b *WString) cap() uint32 { return uint32(cap(b.b)) } func (b *WString) empty() bool { return b == nil || b.cap() == 0 } ================================================ FILE: vendor/github.com/Microsoft/go-winio/pipe.go ================================================ //go:build windows // +build windows package winio import ( "context" "errors" "fmt" "io" "net" "os" "runtime" "syscall" "time" "unsafe" "golang.org/x/sys/windows" "github.com/Microsoft/go-winio/internal/fs" ) //sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe //sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW //sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo //sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW //sys localAlloc(uFlags uint32, length uint32) (ptr uintptr) = LocalAlloc //sys ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) = ntdll.NtCreateNamedPipeFile //sys rtlNtStatusToDosError(status ntStatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb //sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) = ntdll.RtlDosPathNameToNtPathName_U //sys rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) = ntdll.RtlDefaultNpAcl type ioStatusBlock struct { Status, Information uintptr } type objectAttributes struct { Length uintptr RootDirectory uintptr ObjectName *unicodeString Attributes uintptr SecurityDescriptor *securityDescriptor SecurityQoS uintptr } type unicodeString struct { Length uint16 MaximumLength uint16 Buffer uintptr } type securityDescriptor struct { Revision byte Sbz1 byte Control uint16 Owner uintptr Group uintptr Sacl uintptr //revive:disable-line:var-naming SACL, not Sacl Dacl uintptr //revive:disable-line:var-naming DACL, not Dacl } type ntStatus int32 func (status ntStatus) Err() error { if status >= 0 { return nil } return rtlNtStatusToDosError(status) } var ( // ErrPipeListenerClosed is returned for pipe operations on listeners that have been closed. ErrPipeListenerClosed = net.ErrClosed errPipeWriteClosed = errors.New("pipe has been closed for write") ) type win32Pipe struct { *win32File path string } type win32MessageBytePipe struct { win32Pipe writeClosed bool readEOF bool } type pipeAddress string func (f *win32Pipe) LocalAddr() net.Addr { return pipeAddress(f.path) } func (f *win32Pipe) RemoteAddr() net.Addr { return pipeAddress(f.path) } func (f *win32Pipe) SetDeadline(t time.Time) error { if err := f.SetReadDeadline(t); err != nil { return err } return f.SetWriteDeadline(t) } // CloseWrite closes the write side of a message pipe in byte mode. func (f *win32MessageBytePipe) CloseWrite() error { if f.writeClosed { return errPipeWriteClosed } err := f.win32File.Flush() if err != nil { return err } _, err = f.win32File.Write(nil) if err != nil { return err } f.writeClosed = true return nil } // Write writes bytes to a message pipe in byte mode. Zero-byte writes are ignored, since // they are used to implement CloseWrite(). func (f *win32MessageBytePipe) Write(b []byte) (int, error) { if f.writeClosed { return 0, errPipeWriteClosed } if len(b) == 0 { return 0, nil } return f.win32File.Write(b) } // Read reads bytes from a message pipe in byte mode. A read of a zero-byte message on a message // mode pipe will return io.EOF, as will all subsequent reads. func (f *win32MessageBytePipe) Read(b []byte) (int, error) { if f.readEOF { return 0, io.EOF } n, err := f.win32File.Read(b) if err == io.EOF { //nolint:errorlint // If this was the result of a zero-byte read, then // it is possible that the read was due to a zero-size // message. Since we are simulating CloseWrite with a // zero-byte message, ensure that all future Read() calls // also return EOF. f.readEOF = true } else if err == syscall.ERROR_MORE_DATA { //nolint:errorlint // err is Errno // ERROR_MORE_DATA indicates that the pipe's read mode is message mode // and the message still has more bytes. Treat this as a success, since // this package presents all named pipes as byte streams. err = nil } return n, err } func (pipeAddress) Network() string { return "pipe" } func (s pipeAddress) String() string { return string(s) } // tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout. func tryDialPipe(ctx context.Context, path *string, access fs.AccessMask) (syscall.Handle, error) { for { select { case <-ctx.Done(): return syscall.Handle(0), ctx.Err() default: wh, err := fs.CreateFile(*path, access, 0, // mode nil, // security attributes fs.OPEN_EXISTING, fs.FILE_FLAG_OVERLAPPED|fs.SECURITY_SQOS_PRESENT|fs.SECURITY_ANONYMOUS, 0, // template file handle ) h := syscall.Handle(wh) if err == nil { return h, nil } if err != windows.ERROR_PIPE_BUSY { //nolint:errorlint // err is Errno return h, &os.PathError{Err: err, Op: "open", Path: *path} } // Wait 10 msec and try again. This is a rather simplistic // view, as we always try each 10 milliseconds. time.Sleep(10 * time.Millisecond) } } } // DialPipe connects to a named pipe by path, timing out if the connection // takes longer than the specified duration. If timeout is nil, then we use // a default timeout of 2 seconds. (We do not use WaitNamedPipe.) func DialPipe(path string, timeout *time.Duration) (net.Conn, error) { var absTimeout time.Time if timeout != nil { absTimeout = time.Now().Add(*timeout) } else { absTimeout = time.Now().Add(2 * time.Second) } ctx, cancel := context.WithDeadline(context.Background(), absTimeout) defer cancel() conn, err := DialPipeContext(ctx, path) if errors.Is(err, context.DeadlineExceeded) { return nil, ErrTimeout } return conn, err } // DialPipeContext attempts to connect to a named pipe by `path` until `ctx` // cancellation or timeout. func DialPipeContext(ctx context.Context, path string) (net.Conn, error) { return DialPipeAccess(ctx, path, syscall.GENERIC_READ|syscall.GENERIC_WRITE) } // DialPipeAccess attempts to connect to a named pipe by `path` with `access` until `ctx` // cancellation or timeout. func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn, error) { var err error var h syscall.Handle h, err = tryDialPipe(ctx, &path, fs.AccessMask(access)) if err != nil { return nil, err } var flags uint32 err = getNamedPipeInfo(h, &flags, nil, nil, nil) if err != nil { return nil, err } f, err := makeWin32File(h) if err != nil { syscall.Close(h) return nil, err } // If the pipe is in message mode, return a message byte pipe, which // supports CloseWrite(). if flags&windows.PIPE_TYPE_MESSAGE != 0 { return &win32MessageBytePipe{ win32Pipe: win32Pipe{win32File: f, path: path}, }, nil } return &win32Pipe{win32File: f, path: path}, nil } type acceptResponse struct { f *win32File err error } type win32PipeListener struct { firstHandle syscall.Handle path string config PipeConfig acceptCh chan (chan acceptResponse) closeCh chan int doneCh chan int } func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (syscall.Handle, error) { path16, err := syscall.UTF16FromString(path) if err != nil { return 0, &os.PathError{Op: "open", Path: path, Err: err} } var oa objectAttributes oa.Length = unsafe.Sizeof(oa) var ntPath unicodeString if err := rtlDosPathNameToNtPathName(&path16[0], &ntPath, 0, 0, ).Err(); err != nil { return 0, &os.PathError{Op: "open", Path: path, Err: err} } defer localFree(ntPath.Buffer) oa.ObjectName = &ntPath oa.Attributes = windows.OBJ_CASE_INSENSITIVE // The security descriptor is only needed for the first pipe. if first { if sd != nil { l := uint32(len(sd)) sdb := localAlloc(0, l) defer localFree(sdb) copy((*[0xffff]byte)(unsafe.Pointer(sdb))[:], sd) oa.SecurityDescriptor = (*securityDescriptor)(unsafe.Pointer(sdb)) } else { // Construct the default named pipe security descriptor. var dacl uintptr if err := rtlDefaultNpAcl(&dacl).Err(); err != nil { return 0, fmt.Errorf("getting default named pipe ACL: %w", err) } defer localFree(dacl) sdb := &securityDescriptor{ Revision: 1, Control: windows.SE_DACL_PRESENT, Dacl: dacl, } oa.SecurityDescriptor = sdb } } typ := uint32(windows.FILE_PIPE_REJECT_REMOTE_CLIENTS) if c.MessageMode { typ |= windows.FILE_PIPE_MESSAGE_TYPE } disposition := uint32(windows.FILE_OPEN) access := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | syscall.SYNCHRONIZE) if first { disposition = windows.FILE_CREATE // By not asking for read or write access, the named pipe file system // will put this pipe into an initially disconnected state, blocking // client connections until the next call with first == false. access = syscall.SYNCHRONIZE } timeout := int64(-50 * 10000) // 50ms var ( h syscall.Handle iosb ioStatusBlock ) err = ntCreateNamedPipeFile(&h, access, &oa, &iosb, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE, disposition, 0, typ, 0, 0, 0xffffffff, uint32(c.InputBufferSize), uint32(c.OutputBufferSize), &timeout).Err() if err != nil { return 0, &os.PathError{Op: "open", Path: path, Err: err} } runtime.KeepAlive(ntPath) return h, nil } func (l *win32PipeListener) makeServerPipe() (*win32File, error) { h, err := makeServerPipeHandle(l.path, nil, &l.config, false) if err != nil { return nil, err } f, err := makeWin32File(h) if err != nil { syscall.Close(h) return nil, err } return f, nil } func (l *win32PipeListener) makeConnectedServerPipe() (*win32File, error) { p, err := l.makeServerPipe() if err != nil { return nil, err } // Wait for the client to connect. ch := make(chan error) go func(p *win32File) { ch <- connectPipe(p) }(p) select { case err = <-ch: if err != nil { p.Close() p = nil } case <-l.closeCh: // Abort the connect request by closing the handle. p.Close() p = nil err = <-ch if err == nil || err == ErrFileClosed { //nolint:errorlint // err is Errno err = ErrPipeListenerClosed } } return p, err } func (l *win32PipeListener) listenerRoutine() { closed := false for !closed { select { case <-l.closeCh: closed = true case responseCh := <-l.acceptCh: var ( p *win32File err error ) for { p, err = l.makeConnectedServerPipe() // If the connection was immediately closed by the client, try // again. if err != windows.ERROR_NO_DATA { //nolint:errorlint // err is Errno break } } responseCh <- acceptResponse{p, err} closed = err == ErrPipeListenerClosed //nolint:errorlint // err is Errno } } syscall.Close(l.firstHandle) l.firstHandle = 0 // Notify Close() and Accept() callers that the handle has been closed. close(l.doneCh) } // PipeConfig contain configuration for the pipe listener. type PipeConfig struct { // SecurityDescriptor contains a Windows security descriptor in SDDL format. SecurityDescriptor string // MessageMode determines whether the pipe is in byte or message mode. In either // case the pipe is read in byte mode by default. The only practical difference in // this implementation is that CloseWrite() is only supported for message mode pipes; // CloseWrite() is implemented as a zero-byte write, but zero-byte writes are only // transferred to the reader (and returned as io.EOF in this implementation) // when the pipe is in message mode. MessageMode bool // InputBufferSize specifies the size of the input buffer, in bytes. InputBufferSize int32 // OutputBufferSize specifies the size of the output buffer, in bytes. OutputBufferSize int32 } // ListenPipe creates a listener on a Windows named pipe path, e.g. \\.\pipe\mypipe. // The pipe must not already exist. func ListenPipe(path string, c *PipeConfig) (net.Listener, error) { var ( sd []byte err error ) if c == nil { c = &PipeConfig{} } if c.SecurityDescriptor != "" { sd, err = SddlToSecurityDescriptor(c.SecurityDescriptor) if err != nil { return nil, err } } h, err := makeServerPipeHandle(path, sd, c, true) if err != nil { return nil, err } l := &win32PipeListener{ firstHandle: h, path: path, config: *c, acceptCh: make(chan (chan acceptResponse)), closeCh: make(chan int), doneCh: make(chan int), } go l.listenerRoutine() return l, nil } func connectPipe(p *win32File) error { c, err := p.prepareIO() if err != nil { return err } defer p.wg.Done() err = connectNamedPipe(p.handle, &c.o) _, err = p.asyncIO(c, nil, 0, err) if err != nil && err != windows.ERROR_PIPE_CONNECTED { //nolint:errorlint // err is Errno return err } return nil } func (l *win32PipeListener) Accept() (net.Conn, error) { ch := make(chan acceptResponse) select { case l.acceptCh <- ch: response := <-ch err := response.err if err != nil { return nil, err } if l.config.MessageMode { return &win32MessageBytePipe{ win32Pipe: win32Pipe{win32File: response.f, path: l.path}, }, nil } return &win32Pipe{win32File: response.f, path: l.path}, nil case <-l.doneCh: return nil, ErrPipeListenerClosed } } func (l *win32PipeListener) Close() error { select { case l.closeCh <- 1: <-l.doneCh case <-l.doneCh: } return nil } func (l *win32PipeListener) Addr() net.Addr { return pipeAddress(l.path) } ================================================ FILE: vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go ================================================ // Package guid provides a GUID type. The backing structure for a GUID is // identical to that used by the golang.org/x/sys/windows GUID type. // There are two main binary encodings used for a GUID, the big-endian encoding, // and the Windows (mixed-endian) encoding. See here for details: // https://en.wikipedia.org/wiki/Universally_unique_identifier#Encoding package guid import ( "crypto/rand" "crypto/sha1" //nolint:gosec // not used for secure application "encoding" "encoding/binary" "fmt" "strconv" ) //go:generate go run golang.org/x/tools/cmd/stringer -type=Variant -trimprefix=Variant -linecomment // Variant specifies which GUID variant (or "type") of the GUID. It determines // how the entirety of the rest of the GUID is interpreted. type Variant uint8 // The variants specified by RFC 4122 section 4.1.1. const ( // VariantUnknown specifies a GUID variant which does not conform to one of // the variant encodings specified in RFC 4122. VariantUnknown Variant = iota VariantNCS VariantRFC4122 // RFC 4122 VariantMicrosoft VariantFuture ) // Version specifies how the bits in the GUID were generated. For instance, a // version 4 GUID is randomly generated, and a version 5 is generated from the // hash of an input string. type Version uint8 func (v Version) String() string { return strconv.FormatUint(uint64(v), 10) } var _ = (encoding.TextMarshaler)(GUID{}) var _ = (encoding.TextUnmarshaler)(&GUID{}) // NewV4 returns a new version 4 (pseudorandom) GUID, as defined by RFC 4122. func NewV4() (GUID, error) { var b [16]byte if _, err := rand.Read(b[:]); err != nil { return GUID{}, err } g := FromArray(b) g.setVersion(4) // Version 4 means randomly generated. g.setVariant(VariantRFC4122) return g, nil } // NewV5 returns a new version 5 (generated from a string via SHA-1 hashing) // GUID, as defined by RFC 4122. The RFC is unclear on the encoding of the name, // and the sample code treats it as a series of bytes, so we do the same here. // // Some implementations, such as those found on Windows, treat the name as a // big-endian UTF16 stream of bytes. If that is desired, the string can be // encoded as such before being passed to this function. func NewV5(namespace GUID, name []byte) (GUID, error) { b := sha1.New() //nolint:gosec // not used for secure application namespaceBytes := namespace.ToArray() b.Write(namespaceBytes[:]) b.Write(name) a := [16]byte{} copy(a[:], b.Sum(nil)) g := FromArray(a) g.setVersion(5) // Version 5 means generated from a string. g.setVariant(VariantRFC4122) return g, nil } func fromArray(b [16]byte, order binary.ByteOrder) GUID { var g GUID g.Data1 = order.Uint32(b[0:4]) g.Data2 = order.Uint16(b[4:6]) g.Data3 = order.Uint16(b[6:8]) copy(g.Data4[:], b[8:16]) return g } func (g GUID) toArray(order binary.ByteOrder) [16]byte { b := [16]byte{} order.PutUint32(b[0:4], g.Data1) order.PutUint16(b[4:6], g.Data2) order.PutUint16(b[6:8], g.Data3) copy(b[8:16], g.Data4[:]) return b } // FromArray constructs a GUID from a big-endian encoding array of 16 bytes. func FromArray(b [16]byte) GUID { return fromArray(b, binary.BigEndian) } // ToArray returns an array of 16 bytes representing the GUID in big-endian // encoding. func (g GUID) ToArray() [16]byte { return g.toArray(binary.BigEndian) } // FromWindowsArray constructs a GUID from a Windows encoding array of bytes. func FromWindowsArray(b [16]byte) GUID { return fromArray(b, binary.LittleEndian) } // ToWindowsArray returns an array of 16 bytes representing the GUID in Windows // encoding. func (g GUID) ToWindowsArray() [16]byte { return g.toArray(binary.LittleEndian) } func (g GUID) String() string { return fmt.Sprintf( "%08x-%04x-%04x-%04x-%012x", g.Data1, g.Data2, g.Data3, g.Data4[:2], g.Data4[2:]) } // FromString parses a string containing a GUID and returns the GUID. The only // format currently supported is the `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` // format. func FromString(s string) (GUID, error) { if len(s) != 36 { return GUID{}, fmt.Errorf("invalid GUID %q", s) } if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { return GUID{}, fmt.Errorf("invalid GUID %q", s) } var g GUID data1, err := strconv.ParseUint(s[0:8], 16, 32) if err != nil { return GUID{}, fmt.Errorf("invalid GUID %q", s) } g.Data1 = uint32(data1) data2, err := strconv.ParseUint(s[9:13], 16, 16) if err != nil { return GUID{}, fmt.Errorf("invalid GUID %q", s) } g.Data2 = uint16(data2) data3, err := strconv.ParseUint(s[14:18], 16, 16) if err != nil { return GUID{}, fmt.Errorf("invalid GUID %q", s) } g.Data3 = uint16(data3) for i, x := range []int{19, 21, 24, 26, 28, 30, 32, 34} { v, err := strconv.ParseUint(s[x:x+2], 16, 8) if err != nil { return GUID{}, fmt.Errorf("invalid GUID %q", s) } g.Data4[i] = uint8(v) } return g, nil } func (g *GUID) setVariant(v Variant) { d := g.Data4[0] switch v { case VariantNCS: d = (d & 0x7f) case VariantRFC4122: d = (d & 0x3f) | 0x80 case VariantMicrosoft: d = (d & 0x1f) | 0xc0 case VariantFuture: d = (d & 0x0f) | 0xe0 case VariantUnknown: fallthrough default: panic(fmt.Sprintf("invalid variant: %d", v)) } g.Data4[0] = d } // Variant returns the GUID variant, as defined in RFC 4122. func (g GUID) Variant() Variant { b := g.Data4[0] if b&0x80 == 0 { return VariantNCS } else if b&0xc0 == 0x80 { return VariantRFC4122 } else if b&0xe0 == 0xc0 { return VariantMicrosoft } else if b&0xe0 == 0xe0 { return VariantFuture } return VariantUnknown } func (g *GUID) setVersion(v Version) { g.Data3 = (g.Data3 & 0x0fff) | (uint16(v) << 12) } // Version returns the GUID version, as defined in RFC 4122. func (g GUID) Version() Version { return Version((g.Data3 & 0xF000) >> 12) } // MarshalText returns the textual representation of the GUID. func (g GUID) MarshalText() ([]byte, error) { return []byte(g.String()), nil } // UnmarshalText takes the textual representation of a GUID, and unmarhals it // into this GUID. func (g *GUID) UnmarshalText(text []byte) error { g2, err := FromString(string(text)) if err != nil { return err } *g = g2 return nil } ================================================ FILE: vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go ================================================ //go:build !windows // +build !windows package guid // GUID represents a GUID/UUID. It has the same structure as // golang.org/x/sys/windows.GUID so that it can be used with functions expecting // that type. It is defined as its own type as that is only available to builds // targeted at `windows`. The representation matches that used by native Windows // code. type GUID struct { Data1 uint32 Data2 uint16 Data3 uint16 Data4 [8]byte } ================================================ FILE: vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go ================================================ //go:build windows // +build windows package guid import "golang.org/x/sys/windows" // GUID represents a GUID/UUID. It has the same structure as // golang.org/x/sys/windows.GUID so that it can be used with functions expecting // that type. It is defined as its own type so that stringification and // marshaling can be supported. The representation matches that used by native // Windows code. type GUID windows.GUID ================================================ FILE: vendor/github.com/Microsoft/go-winio/pkg/guid/variant_string.go ================================================ // Code generated by "stringer -type=Variant -trimprefix=Variant -linecomment"; DO NOT EDIT. package guid import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[VariantUnknown-0] _ = x[VariantNCS-1] _ = x[VariantRFC4122-2] _ = x[VariantMicrosoft-3] _ = x[VariantFuture-4] } const _Variant_name = "UnknownNCSRFC 4122MicrosoftFuture" var _Variant_index = [...]uint8{0, 7, 10, 18, 27, 33} func (i Variant) String() string { if i >= Variant(len(_Variant_index)-1) { return "Variant(" + strconv.FormatInt(int64(i), 10) + ")" } return _Variant_name[_Variant_index[i]:_Variant_index[i+1]] } ================================================ FILE: vendor/github.com/Microsoft/go-winio/privilege.go ================================================ //go:build windows // +build windows package winio import ( "bytes" "encoding/binary" "fmt" "runtime" "sync" "syscall" "unicode/utf16" "golang.org/x/sys/windows" ) //sys adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges //sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf //sys revertToSelf() (err error) = advapi32.RevertToSelf //sys openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) = advapi32.OpenThreadToken //sys getCurrentThread() (h syscall.Handle) = GetCurrentThread //sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW //sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW //sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW const ( //revive:disable-next-line:var-naming ALL_CAPS SE_PRIVILEGE_ENABLED = windows.SE_PRIVILEGE_ENABLED //revive:disable-next-line:var-naming ALL_CAPS ERROR_NOT_ALL_ASSIGNED syscall.Errno = windows.ERROR_NOT_ALL_ASSIGNED SeBackupPrivilege = "SeBackupPrivilege" SeRestorePrivilege = "SeRestorePrivilege" SeSecurityPrivilege = "SeSecurityPrivilege" ) var ( privNames = make(map[string]uint64) privNameMutex sync.Mutex ) // PrivilegeError represents an error enabling privileges. type PrivilegeError struct { privileges []uint64 } func (e *PrivilegeError) Error() string { s := "Could not enable privilege " if len(e.privileges) > 1 { s = "Could not enable privileges " } for i, p := range e.privileges { if i != 0 { s += ", " } s += `"` s += getPrivilegeName(p) s += `"` } return s } // RunWithPrivilege enables a single privilege for a function call. func RunWithPrivilege(name string, fn func() error) error { return RunWithPrivileges([]string{name}, fn) } // RunWithPrivileges enables privileges for a function call. func RunWithPrivileges(names []string, fn func() error) error { privileges, err := mapPrivileges(names) if err != nil { return err } runtime.LockOSThread() defer runtime.UnlockOSThread() token, err := newThreadToken() if err != nil { return err } defer releaseThreadToken(token) err = adjustPrivileges(token, privileges, SE_PRIVILEGE_ENABLED) if err != nil { return err } return fn() } func mapPrivileges(names []string) ([]uint64, error) { privileges := make([]uint64, 0, len(names)) privNameMutex.Lock() defer privNameMutex.Unlock() for _, name := range names { p, ok := privNames[name] if !ok { err := lookupPrivilegeValue("", name, &p) if err != nil { return nil, err } privNames[name] = p } privileges = append(privileges, p) } return privileges, nil } // EnableProcessPrivileges enables privileges globally for the process. func EnableProcessPrivileges(names []string) error { return enableDisableProcessPrivilege(names, SE_PRIVILEGE_ENABLED) } // DisableProcessPrivileges disables privileges globally for the process. func DisableProcessPrivileges(names []string) error { return enableDisableProcessPrivilege(names, 0) } func enableDisableProcessPrivilege(names []string, action uint32) error { privileges, err := mapPrivileges(names) if err != nil { return err } p := windows.CurrentProcess() var token windows.Token err = windows.OpenProcessToken(p, windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, &token) if err != nil { return err } defer token.Close() return adjustPrivileges(token, privileges, action) } func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) error { var b bytes.Buffer _ = binary.Write(&b, binary.LittleEndian, uint32(len(privileges))) for _, p := range privileges { _ = binary.Write(&b, binary.LittleEndian, p) _ = binary.Write(&b, binary.LittleEndian, action) } prevState := make([]byte, b.Len()) reqSize := uint32(0) success, err := adjustTokenPrivileges(token, false, &b.Bytes()[0], uint32(len(prevState)), &prevState[0], &reqSize) if !success { return err } if err == ERROR_NOT_ALL_ASSIGNED { //nolint:errorlint // err is Errno return &PrivilegeError{privileges} } return nil } func getPrivilegeName(luid uint64) string { var nameBuffer [256]uint16 bufSize := uint32(len(nameBuffer)) err := lookupPrivilegeName("", &luid, &nameBuffer[0], &bufSize) if err != nil { return fmt.Sprintf("", luid) } var displayNameBuffer [256]uint16 displayBufSize := uint32(len(displayNameBuffer)) var langID uint32 err = lookupPrivilegeDisplayName("", &nameBuffer[0], &displayNameBuffer[0], &displayBufSize, &langID) if err != nil { return fmt.Sprintf("", string(utf16.Decode(nameBuffer[:bufSize]))) } return string(utf16.Decode(displayNameBuffer[:displayBufSize])) } func newThreadToken() (windows.Token, error) { err := impersonateSelf(windows.SecurityImpersonation) if err != nil { return 0, err } var token windows.Token err = openThreadToken(getCurrentThread(), syscall.TOKEN_ADJUST_PRIVILEGES|syscall.TOKEN_QUERY, false, &token) if err != nil { rerr := revertToSelf() if rerr != nil { panic(rerr) } return 0, err } return token, nil } func releaseThreadToken(h windows.Token) { err := revertToSelf() if err != nil { panic(err) } h.Close() } ================================================ FILE: vendor/github.com/Microsoft/go-winio/reparse.go ================================================ //go:build windows // +build windows package winio import ( "bytes" "encoding/binary" "fmt" "strings" "unicode/utf16" "unsafe" ) const ( reparseTagMountPoint = 0xA0000003 reparseTagSymlink = 0xA000000C ) type reparseDataBuffer struct { ReparseTag uint32 ReparseDataLength uint16 Reserved uint16 SubstituteNameOffset uint16 SubstituteNameLength uint16 PrintNameOffset uint16 PrintNameLength uint16 } // ReparsePoint describes a Win32 symlink or mount point. type ReparsePoint struct { Target string IsMountPoint bool } // UnsupportedReparsePointError is returned when trying to decode a non-symlink or // mount point reparse point. type UnsupportedReparsePointError struct { Tag uint32 } func (e *UnsupportedReparsePointError) Error() string { return fmt.Sprintf("unsupported reparse point %x", e.Tag) } // DecodeReparsePoint decodes a Win32 REPARSE_DATA_BUFFER structure containing either a symlink // or a mount point. func DecodeReparsePoint(b []byte) (*ReparsePoint, error) { tag := binary.LittleEndian.Uint32(b[0:4]) return DecodeReparsePointData(tag, b[8:]) } func DecodeReparsePointData(tag uint32, b []byte) (*ReparsePoint, error) { isMountPoint := false switch tag { case reparseTagMountPoint: isMountPoint = true case reparseTagSymlink: default: return nil, &UnsupportedReparsePointError{tag} } nameOffset := 8 + binary.LittleEndian.Uint16(b[4:6]) if !isMountPoint { nameOffset += 4 } nameLength := binary.LittleEndian.Uint16(b[6:8]) name := make([]uint16, nameLength/2) err := binary.Read(bytes.NewReader(b[nameOffset:nameOffset+nameLength]), binary.LittleEndian, &name) if err != nil { return nil, err } return &ReparsePoint{string(utf16.Decode(name)), isMountPoint}, nil } func isDriveLetter(c byte) bool { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') } // EncodeReparsePoint encodes a Win32 REPARSE_DATA_BUFFER structure describing a symlink or // mount point. func EncodeReparsePoint(rp *ReparsePoint) []byte { // Generate an NT path and determine if this is a relative path. var ntTarget string relative := false if strings.HasPrefix(rp.Target, `\\?\`) { ntTarget = `\??\` + rp.Target[4:] } else if strings.HasPrefix(rp.Target, `\\`) { ntTarget = `\??\UNC\` + rp.Target[2:] } else if len(rp.Target) >= 2 && isDriveLetter(rp.Target[0]) && rp.Target[1] == ':' { ntTarget = `\??\` + rp.Target } else { ntTarget = rp.Target relative = true } // The paths must be NUL-terminated even though they are counted strings. target16 := utf16.Encode([]rune(rp.Target + "\x00")) ntTarget16 := utf16.Encode([]rune(ntTarget + "\x00")) size := int(unsafe.Sizeof(reparseDataBuffer{})) - 8 size += len(ntTarget16)*2 + len(target16)*2 tag := uint32(reparseTagMountPoint) if !rp.IsMountPoint { tag = reparseTagSymlink size += 4 // Add room for symlink flags } data := reparseDataBuffer{ ReparseTag: tag, ReparseDataLength: uint16(size), SubstituteNameOffset: 0, SubstituteNameLength: uint16((len(ntTarget16) - 1) * 2), PrintNameOffset: uint16(len(ntTarget16) * 2), PrintNameLength: uint16((len(target16) - 1) * 2), } var b bytes.Buffer _ = binary.Write(&b, binary.LittleEndian, &data) if !rp.IsMountPoint { flags := uint32(0) if relative { flags |= 1 } _ = binary.Write(&b, binary.LittleEndian, flags) } _ = binary.Write(&b, binary.LittleEndian, ntTarget16) _ = binary.Write(&b, binary.LittleEndian, target16) return b.Bytes() } ================================================ FILE: vendor/github.com/Microsoft/go-winio/sd.go ================================================ //go:build windows // +build windows package winio import ( "errors" "syscall" "unsafe" "golang.org/x/sys/windows" ) //sys lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountNameW //sys lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountSidW //sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW //sys convertStringSidToSid(str *uint16, sid **byte) (err error) = advapi32.ConvertStringSidToSidW //sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW //sys convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) = advapi32.ConvertSecurityDescriptorToStringSecurityDescriptorW //sys localFree(mem uintptr) = LocalFree //sys getSecurityDescriptorLength(sd uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength type AccountLookupError struct { Name string Err error } func (e *AccountLookupError) Error() string { if e.Name == "" { return "lookup account: empty account name specified" } var s string switch { case errors.Is(e.Err, windows.ERROR_INVALID_SID): s = "the security ID structure is invalid" case errors.Is(e.Err, windows.ERROR_NONE_MAPPED): s = "not found" default: s = e.Err.Error() } return "lookup account " + e.Name + ": " + s } func (e *AccountLookupError) Unwrap() error { return e.Err } type SddlConversionError struct { Sddl string Err error } func (e *SddlConversionError) Error() string { return "convert " + e.Sddl + ": " + e.Err.Error() } func (e *SddlConversionError) Unwrap() error { return e.Err } // LookupSidByName looks up the SID of an account by name // //revive:disable-next-line:var-naming SID, not Sid func LookupSidByName(name string) (sid string, err error) { if name == "" { return "", &AccountLookupError{name, windows.ERROR_NONE_MAPPED} } var sidSize, sidNameUse, refDomainSize uint32 err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse) if err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // err is Errno return "", &AccountLookupError{name, err} } sidBuffer := make([]byte, sidSize) refDomainBuffer := make([]uint16, refDomainSize) err = lookupAccountName(nil, name, &sidBuffer[0], &sidSize, &refDomainBuffer[0], &refDomainSize, &sidNameUse) if err != nil { return "", &AccountLookupError{name, err} } var strBuffer *uint16 err = convertSidToStringSid(&sidBuffer[0], &strBuffer) if err != nil { return "", &AccountLookupError{name, err} } sid = syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:]) localFree(uintptr(unsafe.Pointer(strBuffer))) return sid, nil } // LookupNameBySid looks up the name of an account by SID // //revive:disable-next-line:var-naming SID, not Sid func LookupNameBySid(sid string) (name string, err error) { if sid == "" { return "", &AccountLookupError{sid, windows.ERROR_NONE_MAPPED} } sidBuffer, err := windows.UTF16PtrFromString(sid) if err != nil { return "", &AccountLookupError{sid, err} } var sidPtr *byte if err = convertStringSidToSid(sidBuffer, &sidPtr); err != nil { return "", &AccountLookupError{sid, err} } defer localFree(uintptr(unsafe.Pointer(sidPtr))) var nameSize, refDomainSize, sidNameUse uint32 err = lookupAccountSid(nil, sidPtr, nil, &nameSize, nil, &refDomainSize, &sidNameUse) if err != nil && err != windows.ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // err is Errno return "", &AccountLookupError{sid, err} } nameBuffer := make([]uint16, nameSize) refDomainBuffer := make([]uint16, refDomainSize) err = lookupAccountSid(nil, sidPtr, &nameBuffer[0], &nameSize, &refDomainBuffer[0], &refDomainSize, &sidNameUse) if err != nil { return "", &AccountLookupError{sid, err} } name = windows.UTF16ToString(nameBuffer) return name, nil } func SddlToSecurityDescriptor(sddl string) ([]byte, error) { var sdBuffer uintptr err := convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &sdBuffer, nil) if err != nil { return nil, &SddlConversionError{sddl, err} } defer localFree(sdBuffer) sd := make([]byte, getSecurityDescriptorLength(sdBuffer)) copy(sd, (*[0xffff]byte)(unsafe.Pointer(sdBuffer))[:len(sd)]) return sd, nil } func SecurityDescriptorToSddl(sd []byte) (string, error) { var sddl *uint16 // The returned string length seems to include an arbitrary number of terminating NULs. // Don't use it. err := convertSecurityDescriptorToStringSecurityDescriptor(&sd[0], 1, 0xff, &sddl, nil) if err != nil { return "", err } defer localFree(uintptr(unsafe.Pointer(sddl))) return syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(sddl))[:]), nil } ================================================ FILE: vendor/github.com/Microsoft/go-winio/syscall.go ================================================ //go:build windows package winio //go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go ./*.go ================================================ FILE: vendor/github.com/Microsoft/go-winio/tools.go ================================================ //go:build tools package winio import _ "golang.org/x/tools/cmd/stringer" ================================================ FILE: vendor/github.com/Microsoft/go-winio/zsyscall_windows.go ================================================ //go:build windows // Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT. package winio import ( "syscall" "unsafe" "golang.org/x/sys/windows" ) var _ unsafe.Pointer // Do the interface allocations only once for common // Errno values. const ( errnoERROR_IO_PENDING = 997 ) var ( errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) errERROR_EINVAL error = syscall.EINVAL ) // errnoErr returns common boxed Errno values, to prevent // allocations at runtime. func errnoErr(e syscall.Errno) error { switch e { case 0: return errERROR_EINVAL case errnoERROR_IO_PENDING: return errERROR_IO_PENDING } // TODO: add more here, after collecting data on the common // error values see on Windows. (perhaps when running // all.bat?) return e } var ( modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") modkernel32 = windows.NewLazySystemDLL("kernel32.dll") modntdll = windows.NewLazySystemDLL("ntdll.dll") modws2_32 = windows.NewLazySystemDLL("ws2_32.dll") procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges") procConvertSecurityDescriptorToStringSecurityDescriptorW = modadvapi32.NewProc("ConvertSecurityDescriptorToStringSecurityDescriptorW") procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW") procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW") procConvertStringSidToSidW = modadvapi32.NewProc("ConvertStringSidToSidW") procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength") procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf") procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW") procLookupAccountSidW = modadvapi32.NewProc("LookupAccountSidW") procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW") procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW") procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW") procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken") procRevertToSelf = modadvapi32.NewProc("RevertToSelf") procBackupRead = modkernel32.NewProc("BackupRead") procBackupWrite = modkernel32.NewProc("BackupWrite") procCancelIoEx = modkernel32.NewProc("CancelIoEx") procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe") procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort") procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW") procGetCurrentThread = modkernel32.NewProc("GetCurrentThread") procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW") procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo") procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus") procLocalAlloc = modkernel32.NewProc("LocalAlloc") procLocalFree = modkernel32.NewProc("LocalFree") procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes") procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile") procRtlDefaultNpAcl = modntdll.NewProc("RtlDefaultNpAcl") procRtlDosPathNameToNtPathName_U = modntdll.NewProc("RtlDosPathNameToNtPathName_U") procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb") procWSAGetOverlappedResult = modws2_32.NewProc("WSAGetOverlappedResult") ) func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) { var _p0 uint32 if releaseAll { _p0 = 1 } r0, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize))) success = r0 != 0 if true { err = errnoErr(e1) } return } func convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) { r1, _, e1 := syscall.Syscall6(procConvertSecurityDescriptorToStringSecurityDescriptorW.Addr(), 5, uintptr(unsafe.Pointer(sd)), uintptr(revision), uintptr(secInfo), uintptr(unsafe.Pointer(sddl)), uintptr(unsafe.Pointer(sddlSize)), 0) if r1 == 0 { err = errnoErr(e1) } return } func convertSidToStringSid(sid *byte, str **uint16) (err error) { r1, _, e1 := syscall.Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)), 0) if r1 == 0 { err = errnoErr(e1) } return } func convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) { var _p0 *uint16 _p0, err = syscall.UTF16PtrFromString(str) if err != nil { return } return _convertStringSecurityDescriptorToSecurityDescriptor(_p0, revision, sd, size) } func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision uint32, sd *uintptr, size *uint32) (err error) { r1, _, e1 := syscall.Syscall6(procConvertStringSecurityDescriptorToSecurityDescriptorW.Addr(), 4, uintptr(unsafe.Pointer(str)), uintptr(revision), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(size)), 0, 0) if r1 == 0 { err = errnoErr(e1) } return } func convertStringSidToSid(str *uint16, sid **byte) (err error) { r1, _, e1 := syscall.Syscall(procConvertStringSidToSidW.Addr(), 2, uintptr(unsafe.Pointer(str)), uintptr(unsafe.Pointer(sid)), 0) if r1 == 0 { err = errnoErr(e1) } return } func getSecurityDescriptorLength(sd uintptr) (len uint32) { r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0) len = uint32(r0) return } func impersonateSelf(level uint32) (err error) { r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(level), 0, 0) if r1 == 0 { err = errnoErr(e1) } return } func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) { var _p0 *uint16 _p0, err = syscall.UTF16PtrFromString(accountName) if err != nil { return } return _lookupAccountName(systemName, _p0, sid, sidSize, refDomain, refDomainSize, sidNameUse) } func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) { r1, _, e1 := syscall.Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0) if r1 == 0 { err = errnoErr(e1) } return } func lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) { r1, _, e1 := syscall.Syscall9(procLookupAccountSidW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0) if r1 == 0 { err = errnoErr(e1) } return } func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) { var _p0 *uint16 _p0, err = syscall.UTF16PtrFromString(systemName) if err != nil { return } return _lookupPrivilegeDisplayName(_p0, name, buffer, size, languageId) } func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) { r1, _, e1 := syscall.Syscall6(procLookupPrivilegeDisplayNameW.Addr(), 5, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)), 0) if r1 == 0 { err = errnoErr(e1) } return } func lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) { var _p0 *uint16 _p0, err = syscall.UTF16PtrFromString(systemName) if err != nil { return } return _lookupPrivilegeName(_p0, luid, buffer, size) } func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) { r1, _, e1 := syscall.Syscall6(procLookupPrivilegeNameW.Addr(), 4, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), 0, 0) if r1 == 0 { err = errnoErr(e1) } return } func lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) { var _p0 *uint16 _p0, err = syscall.UTF16PtrFromString(systemName) if err != nil { return } var _p1 *uint16 _p1, err = syscall.UTF16PtrFromString(name) if err != nil { return } return _lookupPrivilegeValue(_p0, _p1, luid) } func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err error) { r1, _, e1 := syscall.Syscall(procLookupPrivilegeValueW.Addr(), 3, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid))) if r1 == 0 { err = errnoErr(e1) } return } func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) { var _p0 uint32 if openAsSelf { _p0 = 1 } r1, _, e1 := syscall.Syscall6(procOpenThreadToken.Addr(), 4, uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0) if r1 == 0 { err = errnoErr(e1) } return } func revertToSelf() (err error) { r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0) if r1 == 0 { err = errnoErr(e1) } return } func backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) { var _p0 *byte if len(b) > 0 { _p0 = &b[0] } var _p1 uint32 if abort { _p1 = 1 } var _p2 uint32 if processSecurity { _p2 = 1 } r1, _, e1 := syscall.Syscall9(procBackupRead.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0) if r1 == 0 { err = errnoErr(e1) } return } func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) { var _p0 *byte if len(b) > 0 { _p0 = &b[0] } var _p1 uint32 if abort { _p1 = 1 } var _p2 uint32 if processSecurity { _p2 = 1 } r1, _, e1 := syscall.Syscall9(procBackupWrite.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0) if r1 == 0 { err = errnoErr(e1) } return } func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) { r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(file), uintptr(unsafe.Pointer(o)), 0) if r1 == 0 { err = errnoErr(e1) } return } func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) { r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(o)), 0) if r1 == 0 { err = errnoErr(e1) } return } func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) { r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0) newport = syscall.Handle(r0) if newport == 0 { err = errnoErr(e1) } return } func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) { var _p0 *uint16 _p0, err = syscall.UTF16PtrFromString(name) if err != nil { return } return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa) } func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) { r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0) handle = syscall.Handle(r0) if handle == syscall.InvalidHandle { err = errnoErr(e1) } return } func getCurrentThread() (h syscall.Handle) { r0, _, _ := syscall.Syscall(procGetCurrentThread.Addr(), 0, 0, 0, 0) h = syscall.Handle(r0) return } func getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) { r1, _, e1 := syscall.Syscall9(procGetNamedPipeHandleStateW.Addr(), 7, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize), 0, 0) if r1 == 0 { err = errnoErr(e1) } return } func getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) { r1, _, e1 := syscall.Syscall6(procGetNamedPipeInfo.Addr(), 5, uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)), 0) if r1 == 0 { err = errnoErr(e1) } return } func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) { r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout), 0) if r1 == 0 { err = errnoErr(e1) } return } func localAlloc(uFlags uint32, length uint32) (ptr uintptr) { r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(uFlags), uintptr(length), 0) ptr = uintptr(r0) return } func localFree(mem uintptr) { syscall.Syscall(procLocalFree.Addr(), 1, uintptr(mem), 0, 0) return } func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) { r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(h), uintptr(flags), 0) if r1 == 0 { err = errnoErr(e1) } return } func ntCreateNamedPipeFile(pipe *syscall.Handle, access uint32, oa *objectAttributes, iosb *ioStatusBlock, share uint32, disposition uint32, options uint32, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) { r0, _, _ := syscall.Syscall15(procNtCreateNamedPipeFile.Addr(), 14, uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout)), 0) status = ntStatus(r0) return } func rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) { r0, _, _ := syscall.Syscall(procRtlDefaultNpAcl.Addr(), 1, uintptr(unsafe.Pointer(dacl)), 0, 0) status = ntStatus(r0) return } func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) { r0, _, _ := syscall.Syscall6(procRtlDosPathNameToNtPathName_U.Addr(), 4, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(ntName)), uintptr(filePart), uintptr(reserved), 0, 0) status = ntStatus(r0) return } func rtlNtStatusToDosError(status ntStatus) (winerr error) { r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0) if r0 != 0 { winerr = syscall.Errno(r0) } return } func wsaGetOverlappedResult(h syscall.Handle, o *syscall.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) { var _p0 uint32 if wait { _p0 = 1 } r1, _, e1 := syscall.Syscall6(procWSAGetOverlappedResult.Addr(), 5, uintptr(h), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(bytes)), uintptr(_p0), uintptr(unsafe.Pointer(flags)), 0) if r1 == 0 { err = errnoErr(e1) } return } ================================================ FILE: vendor/github.com/PuerkitoBio/goquery/.gitattributes ================================================ testdata/* linguist-vendored ================================================ FILE: vendor/github.com/PuerkitoBio/goquery/.gitignore ================================================ # editor temporary files *.sublime-* .DS_Store *.swp #*.*# tags # direnv config .env* # test binaries *.test # coverage and profilte outputs *.out ================================================ FILE: vendor/github.com/PuerkitoBio/goquery/LICENSE ================================================ Copyright (c) 2012-2021, Martin Angers & Contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/PuerkitoBio/goquery/README.md ================================================ # goquery - a little like that j-thing, only in Go [![Build Status](https://github.com/PuerkitoBio/goquery/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/PuerkitoBio/goquery/actions) [![Go Reference](https://pkg.go.dev/badge/github.com/PuerkitoBio/goquery.svg)](https://pkg.go.dev/github.com/PuerkitoBio/goquery) [![Sourcegraph Badge](https://sourcegraph.com/github.com/PuerkitoBio/goquery/-/badge.svg)](https://sourcegraph.com/github.com/PuerkitoBio/goquery?badge) goquery brings a syntax and a set of features similar to [jQuery][] to the [Go language][go]. It is based on Go's [net/html package][html] and the CSS Selector library [cascadia][]. Since the net/html parser returns nodes, and not a full-featured DOM tree, jQuery's stateful manipulation functions (like height(), css(), detach()) have been left off. Also, because the net/html parser requires UTF-8 encoding, so does goquery: it is the caller's responsibility to ensure that the source document provides UTF-8 encoded HTML. See the [wiki][] for various options to do this. Syntax-wise, it is as close as possible to jQuery, with the same function names when possible, and that warm and fuzzy chainable interface. jQuery being the ultra-popular library that it is, I felt that writing a similar HTML-manipulating library was better to follow its API than to start anew (in the same spirit as Go's `fmt` package), even though some of its methods are less than intuitive (looking at you, [index()][index]...). ## Table of Contents * [Installation](#installation) * [Changelog](#changelog) * [API](#api) * [Examples](#examples) * [Related Projects](#related-projects) * [Support](#support) * [License](#license) ## Installation Please note that because of the net/html dependency, goquery requires Go1.1+ and is tested on Go1.7+. $ go get github.com/PuerkitoBio/goquery (optional) To run unit tests: $ cd $GOPATH/src/github.com/PuerkitoBio/goquery $ go test (optional) To run benchmarks (warning: it runs for a few minutes): $ cd $GOPATH/src/github.com/PuerkitoBio/goquery $ go test -bench=".*" ## Changelog **Note that goquery's API is now stable, and will not break.** * **2023-02-18 (v1.8.1)** : Update `go.mod` dependencies, update CI workflow. * **2021-10-25 (v1.8.0)** : Add `Render` function to render a `Selection` to an `io.Writer` (thanks [@anthonygedeon](https://github.com/anthonygedeon)). * **2021-07-11 (v1.7.1)** : Update go.mod dependencies and add dependabot config (thanks [@jauderho](https://github.com/jauderho)). * **2021-06-14 (v1.7.0)** : Add `Single` and `SingleMatcher` functions to optimize first-match selection (thanks [@gdollardollar](https://github.com/gdollardollar)). * **2021-01-11 (v1.6.1)** : Fix panic when calling `{Prepend,Append,Set}Html` on a `Selection` that contains non-Element nodes. * **2020-10-08 (v1.6.0)** : Parse html in context of the container node for all functions that deal with html strings (`AfterHtml`, `AppendHtml`, etc.). Thanks to [@thiemok][thiemok] and [@davidjwilkins][djw] for their work on this. * **2020-02-04 (v1.5.1)** : Update module dependencies. * **2018-11-15 (v1.5.0)** : Go module support (thanks @Zaba505). * **2018-06-07 (v1.4.1)** : Add `NewDocumentFromReader` examples. * **2018-03-24 (v1.4.0)** : Deprecate `NewDocument(url)` and `NewDocumentFromResponse(response)`. * **2018-01-28 (v1.3.0)** : Add `ToEnd` constant to `Slice` until the end of the selection (thanks to @davidjwilkins for raising the issue). * **2018-01-11 (v1.2.0)** : Add `AddBack*` and deprecate `AndSelf` (thanks to @davidjwilkins). * **2017-02-12 (v1.1.0)** : Add `SetHtml` and `SetText` (thanks to @glebtv). * **2016-12-29 (v1.0.2)** : Optimize allocations for `Selection.Text` (thanks to @radovskyb). * **2016-08-28 (v1.0.1)** : Optimize performance for large documents. * **2016-07-27 (v1.0.0)** : Tag version 1.0.0. * **2016-06-15** : Invalid selector strings internally compile to a `Matcher` implementation that never matches any node (instead of a panic). So for example, `doc.Find("~")` returns an empty `*Selection` object. * **2016-02-02** : Add `NodeName` utility function similar to the DOM's `nodeName` property. It returns the tag name of the first element in a selection, and other relevant values of non-element nodes (see [doc][] for details). Add `OuterHtml` utility function similar to the DOM's `outerHTML` property (named `OuterHtml` in small caps for consistency with the existing `Html` method on the `Selection`). * **2015-04-20** : Add `AttrOr` helper method to return the attribute's value or a default value if absent. Thanks to [piotrkowalczuk][piotr]. * **2015-02-04** : Add more manipulation functions - Prepend* - thanks again to [Andrew Stone][thatguystone]. * **2014-11-28** : Add more manipulation functions - ReplaceWith*, Wrap* and Unwrap - thanks again to [Andrew Stone][thatguystone]. * **2014-11-07** : Add manipulation functions (thanks to [Andrew Stone][thatguystone]) and `*Matcher` functions, that receive compiled cascadia selectors instead of selector strings, thus avoiding potential panics thrown by goquery via `cascadia.MustCompile` calls. This results in better performance (selectors can be compiled once and reused) and more idiomatic error handling (you can handle cascadia's compilation errors, instead of recovering from panics, which had been bugging me for a long time). Note that the actual type expected is a `Matcher` interface, that `cascadia.Selector` implements. Other matcher implementations could be used. * **2014-11-06** : Change import paths of net/html to golang.org/x/net/html (see https://groups.google.com/forum/#!topic/golang-nuts/eD8dh3T9yyA). Make sure to update your code to use the new import path too when you call goquery with `html.Node`s. * **v0.3.2** : Add `NewDocumentFromReader()` (thanks jweir) which allows creating a goquery document from an io.Reader. * **v0.3.1** : Add `NewDocumentFromResponse()` (thanks assassingj) which allows creating a goquery document from an http response. * **v0.3.0** : Add `EachWithBreak()` which allows to break out of an `Each()` loop by returning false. This function was added instead of changing the existing `Each()` to avoid breaking compatibility. * **v0.2.1** : Make go-getable, now that [go.net/html is Go1.0-compatible][gonet] (thanks to @matrixik for pointing this out). * **v0.2.0** : Add support for negative indices in Slice(). **BREAKING CHANGE** `Document.Root` is removed, `Document` is now a `Selection` itself (a selection of one, the root element, just like `Document.Root` was before). Add jQuery's Closest() method. * **v0.1.1** : Add benchmarks to use as baseline for refactorings, refactor Next...() and Prev...() methods to use the new html package's linked list features (Next/PrevSibling, FirstChild). Good performance boost (40+% in some cases). * **v0.1.0** : Initial release. ## API goquery exposes two structs, `Document` and `Selection`, and the `Matcher` interface. Unlike jQuery, which is loaded as part of a DOM document, and thus acts on its containing document, goquery doesn't know which HTML document to act upon. So it needs to be told, and that's what the `Document` type is for. It holds the root document node as the initial Selection value to manipulate. jQuery often has many variants for the same function (no argument, a selector string argument, a jQuery object argument, a DOM element argument, ...). Instead of exposing the same features in goquery as a single method with variadic empty interface arguments, statically-typed signatures are used following this naming convention: * When the jQuery equivalent can be called with no argument, it has the same name as jQuery for the no argument signature (e.g.: `Prev()`), and the version with a selector string argument is called `XxxFiltered()` (e.g.: `PrevFiltered()`) * When the jQuery equivalent **requires** one argument, the same name as jQuery is used for the selector string version (e.g.: `Is()`) * The signatures accepting a jQuery object as argument are defined in goquery as `XxxSelection()` and take a `*Selection` object as argument (e.g.: `FilterSelection()`) * The signatures accepting a DOM element as argument in jQuery are defined in goquery as `XxxNodes()` and take a variadic argument of type `*html.Node` (e.g.: `FilterNodes()`) * The signatures accepting a function as argument in jQuery are defined in goquery as `XxxFunction()` and take a function as argument (e.g.: `FilterFunction()`) * The goquery methods that can be called with a selector string have a corresponding version that take a `Matcher` interface and are defined as `XxxMatcher()` (e.g.: `IsMatcher()`) Utility functions that are not in jQuery but are useful in Go are implemented as functions (that take a `*Selection` as parameter), to avoid a potential naming clash on the `*Selection`'s methods (reserved for jQuery-equivalent behaviour). The complete [package reference documentation can be found here][doc]. Please note that Cascadia's selectors do not necessarily match all supported selectors of jQuery (Sizzle). See the [cascadia project][cascadia] for details. Invalid selector strings compile to a `Matcher` that fails to match any node. Behaviour of the various functions that take a selector string as argument follows from that fact, e.g. (where `~` is an invalid selector string): * `Find("~")` returns an empty selection because the selector string doesn't match anything. * `Add("~")` returns a new selection that holds the same nodes as the original selection, because it didn't add any node (selector string didn't match anything). * `ParentsFiltered("~")` returns an empty selection because the selector string doesn't match anything. * `ParentsUntil("~")` returns all parents of the selection because the selector string didn't match any element to stop before the top element. ## Examples See some tips and tricks in the [wiki][]. Adapted from example_test.go: ```Go package main import ( "fmt" "log" "net/http" "github.com/PuerkitoBio/goquery" ) func ExampleScrape() { // Request the HTML page. res, err := http.Get("http://metalsucks.net") if err != nil { log.Fatal(err) } defer res.Body.Close() if res.StatusCode != 200 { log.Fatalf("status code error: %d %s", res.StatusCode, res.Status) } // Load the HTML document doc, err := goquery.NewDocumentFromReader(res.Body) if err != nil { log.Fatal(err) } // Find the review items doc.Find(".left-content article .post-title").Each(func(i int, s *goquery.Selection) { // For each item found, get the title title := s.Find("a").Text() fmt.Printf("Review %d: %s\n", i, title) }) } func main() { ExampleScrape() } ``` ## Related Projects - [Goq][goq], an HTML deserialization and scraping library based on goquery and struct tags. - [andybalholm/cascadia][cascadia], the CSS selector library used by goquery. - [suntong/cascadia][cascadiacli], a command-line interface to the cascadia CSS selector library, useful to test selectors. - [gocolly/colly](https://github.com/gocolly/colly), a lightning fast and elegant Scraping Framework - [gnulnx/goperf](https://github.com/gnulnx/goperf), a website performance test tool that also fetches static assets. - [MontFerret/ferret](https://github.com/MontFerret/ferret), declarative web scraping. - [tacusci/berrycms](https://github.com/tacusci/berrycms), a modern simple to use CMS with easy to write plugins - [Dataflow kit](https://github.com/slotix/dataflowkit), Web Scraping framework for Gophers. - [Geziyor](https://github.com/geziyor/geziyor), a fast web crawling & scraping framework for Go. Supports JS rendering. - [Pagser](https://github.com/foolin/pagser), a simple, easy, extensible, configurable HTML parser to struct based on goquery and struct tags. - [stitcherd](https://github.com/vhodges/stitcherd), A server for doing server side includes using css selectors and DOM updates. - [goskyr](https://github.com/jakopako/goskyr), an easily configurable command-line scraper written in Go. - [goGetJS](https://github.com/davemolk/goGetJS), a tool for extracting, searching, and saving JavaScript files (with optional headless browser). ## Support There are a number of ways you can support the project: * Use it, star it, build something with it, spread the word! - If you do build something open-source or otherwise publicly-visible, let me know so I can add it to the [Related Projects](#related-projects) section! * Raise issues to improve the project (note: doc typos and clarifications are issues too!) - Please search existing issues before opening a new one - it may have already been adressed. * Pull requests: please discuss new code in an issue first, unless the fix is really trivial. - Make sure new code is tested. - Be mindful of existing code - PRs that break existing code have a high probability of being declined, unless it fixes a serious issue. * Sponsor the developer - See the Github Sponsor button at the top of the repo on github - or via BuyMeACoffee.com, below Buy Me A Coffee ## License The [BSD 3-Clause license][bsd], the same as the [Go language][golic]. Cascadia's license is [here][caslic]. [jquery]: http://jquery.com/ [go]: http://golang.org/ [cascadia]: https://github.com/andybalholm/cascadia [cascadiacli]: https://github.com/suntong/cascadia [bsd]: http://opensource.org/licenses/BSD-3-Clause [golic]: http://golang.org/LICENSE [caslic]: https://github.com/andybalholm/cascadia/blob/master/LICENSE [doc]: https://pkg.go.dev/github.com/PuerkitoBio/goquery [index]: http://api.jquery.com/index/ [gonet]: https://github.com/golang/net/ [html]: https://pkg.go.dev/golang.org/x/net/html [wiki]: https://github.com/PuerkitoBio/goquery/wiki/Tips-and-tricks [thatguystone]: https://github.com/thatguystone [piotr]: https://github.com/piotrkowalczuk [goq]: https://github.com/andrewstuart/goq [thiemok]: https://github.com/thiemok [djw]: https://github.com/davidjwilkins ================================================ FILE: vendor/github.com/PuerkitoBio/goquery/array.go ================================================ package goquery import ( "golang.org/x/net/html" ) const ( maxUint = ^uint(0) maxInt = int(maxUint >> 1) // ToEnd is a special index value that can be used as end index in a call // to Slice so that all elements are selected until the end of the Selection. // It is equivalent to passing (*Selection).Length(). ToEnd = maxInt ) // First reduces the set of matched elements to the first in the set. // It returns a new Selection object, and an empty Selection object if the // the selection is empty. func (s *Selection) First() *Selection { return s.Eq(0) } // Last reduces the set of matched elements to the last in the set. // It returns a new Selection object, and an empty Selection object if // the selection is empty. func (s *Selection) Last() *Selection { return s.Eq(-1) } // Eq reduces the set of matched elements to the one at the specified index. // If a negative index is given, it counts backwards starting at the end of the // set. It returns a new Selection object, and an empty Selection object if the // index is invalid. func (s *Selection) Eq(index int) *Selection { if index < 0 { index += len(s.Nodes) } if index >= len(s.Nodes) || index < 0 { return newEmptySelection(s.document) } return s.Slice(index, index+1) } // Slice reduces the set of matched elements to a subset specified by a range // of indices. The start index is 0-based and indicates the index of the first // element to select. The end index is 0-based and indicates the index at which // the elements stop being selected (the end index is not selected). // // The indices may be negative, in which case they represent an offset from the // end of the selection. // // The special value ToEnd may be specified as end index, in which case all elements // until the end are selected. This works both for a positive and negative start // index. func (s *Selection) Slice(start, end int) *Selection { if start < 0 { start += len(s.Nodes) } if end == ToEnd { end = len(s.Nodes) } else if end < 0 { end += len(s.Nodes) } return pushStack(s, s.Nodes[start:end]) } // Get retrieves the underlying node at the specified index. // Get without parameter is not implemented, since the node array is available // on the Selection object. func (s *Selection) Get(index int) *html.Node { if index < 0 { index += len(s.Nodes) // Negative index gets from the end } return s.Nodes[index] } // Index returns the position of the first element within the Selection object // relative to its sibling elements. func (s *Selection) Index() int { if len(s.Nodes) > 0 { return newSingleSelection(s.Nodes[0], s.document).PrevAll().Length() } return -1 } // IndexSelector returns the position of the first element within the // Selection object relative to the elements matched by the selector, or -1 if // not found. func (s *Selection) IndexSelector(selector string) int { if len(s.Nodes) > 0 { sel := s.document.Find(selector) return indexInSlice(sel.Nodes, s.Nodes[0]) } return -1 } // IndexMatcher returns the position of the first element within the // Selection object relative to the elements matched by the matcher, or -1 if // not found. func (s *Selection) IndexMatcher(m Matcher) int { if len(s.Nodes) > 0 { sel := s.document.FindMatcher(m) return indexInSlice(sel.Nodes, s.Nodes[0]) } return -1 } // IndexOfNode returns the position of the specified node within the Selection // object, or -1 if not found. func (s *Selection) IndexOfNode(node *html.Node) int { return indexInSlice(s.Nodes, node) } // IndexOfSelection returns the position of the first node in the specified // Selection object within this Selection object, or -1 if not found. func (s *Selection) IndexOfSelection(sel *Selection) int { if sel != nil && len(sel.Nodes) > 0 { return indexInSlice(s.Nodes, sel.Nodes[0]) } return -1 } ================================================ FILE: vendor/github.com/PuerkitoBio/goquery/doc.go ================================================ // Copyright (c) 2012-2016, Martin Angers & Contributors // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation and/or // other materials provided with the distribution. // * Neither the name of the author nor the names of its contributors may be used to // endorse or promote products derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY // WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /* Package goquery implements features similar to jQuery, including the chainable syntax, to manipulate and query an HTML document. It brings a syntax and a set of features similar to jQuery to the Go language. It is based on Go's net/html package and the CSS Selector library cascadia. Since the net/html parser returns nodes, and not a full-featured DOM tree, jQuery's stateful manipulation functions (like height(), css(), detach()) have been left off. Also, because the net/html parser requires UTF-8 encoding, so does goquery: it is the caller's responsibility to ensure that the source document provides UTF-8 encoded HTML. See the repository's wiki for various options on how to do this. Syntax-wise, it is as close as possible to jQuery, with the same method names when possible, and that warm and fuzzy chainable interface. jQuery being the ultra-popular library that it is, writing a similar HTML-manipulating library was better to follow its API than to start anew (in the same spirit as Go's fmt package), even though some of its methods are less than intuitive (looking at you, index()...). It is hosted on GitHub, along with additional documentation in the README.md file: https://github.com/puerkitobio/goquery Please note that because of the net/html dependency, goquery requires Go1.1+. The various methods are split into files based on the category of behavior. The three dots (...) indicate that various "overloads" are available. * array.go : array-like positional manipulation of the selection. - Eq() - First() - Get() - Index...() - Last() - Slice() * expand.go : methods that expand or augment the selection's set. - Add...() - AndSelf() - Union(), which is an alias for AddSelection() * filter.go : filtering methods, that reduce the selection's set. - End() - Filter...() - Has...() - Intersection(), which is an alias of FilterSelection() - Not...() * iteration.go : methods to loop over the selection's nodes. - Each() - EachWithBreak() - Map() * manipulation.go : methods for modifying the document - After...() - Append...() - Before...() - Clone() - Empty() - Prepend...() - Remove...() - ReplaceWith...() - Unwrap() - Wrap...() - WrapAll...() - WrapInner...() * property.go : methods that inspect and get the node's properties values. - Attr*(), RemoveAttr(), SetAttr() - AddClass(), HasClass(), RemoveClass(), ToggleClass() - Html() - Length() - Size(), which is an alias for Length() - Text() * query.go : methods that query, or reflect, a node's identity. - Contains() - Is...() * traversal.go : methods to traverse the HTML document tree. - Children...() - Contents() - Find...() - Next...() - Parent[s]...() - Prev...() - Siblings...() * type.go : definition of the types exposed by goquery. - Document - Selection - Matcher * utilities.go : definition of helper functions (and not methods on a *Selection) that are not part of jQuery, but are useful to goquery. - NodeName - OuterHtml */ package goquery ================================================ FILE: vendor/github.com/PuerkitoBio/goquery/expand.go ================================================ package goquery import "golang.org/x/net/html" // Add adds the selector string's matching nodes to those in the current // selection and returns a new Selection object. // The selector string is run in the context of the document of the current // Selection object. func (s *Selection) Add(selector string) *Selection { return s.AddNodes(findWithMatcher([]*html.Node{s.document.rootNode}, compileMatcher(selector))...) } // AddMatcher adds the matcher's matching nodes to those in the current // selection and returns a new Selection object. // The matcher is run in the context of the document of the current // Selection object. func (s *Selection) AddMatcher(m Matcher) *Selection { return s.AddNodes(findWithMatcher([]*html.Node{s.document.rootNode}, m)...) } // AddSelection adds the specified Selection object's nodes to those in the // current selection and returns a new Selection object. func (s *Selection) AddSelection(sel *Selection) *Selection { if sel == nil { return s.AddNodes() } return s.AddNodes(sel.Nodes...) } // Union is an alias for AddSelection. func (s *Selection) Union(sel *Selection) *Selection { return s.AddSelection(sel) } // AddNodes adds the specified nodes to those in the // current selection and returns a new Selection object. func (s *Selection) AddNodes(nodes ...*html.Node) *Selection { return pushStack(s, appendWithoutDuplicates(s.Nodes, nodes, nil)) } // AndSelf adds the previous set of elements on the stack to the current set. // It returns a new Selection object containing the current Selection combined // with the previous one. // Deprecated: This function has been deprecated and is now an alias for AddBack(). func (s *Selection) AndSelf() *Selection { return s.AddBack() } // AddBack adds the previous set of elements on the stack to the current set. // It returns a new Selection object containing the current Selection combined // with the previous one. func (s *Selection) AddBack() *Selection { return s.AddSelection(s.prevSel) } // AddBackFiltered reduces the previous set of elements on the stack to those that // match the selector string, and adds them to the current set. // It returns a new Selection object containing the current Selection combined // with the filtered previous one func (s *Selection) AddBackFiltered(selector string) *Selection { return s.AddSelection(s.prevSel.Filter(selector)) } // AddBackMatcher reduces the previous set of elements on the stack to those that match // the mateher, and adds them to the curernt set. // It returns a new Selection object containing the current Selection combined // with the filtered previous one func (s *Selection) AddBackMatcher(m Matcher) *Selection { return s.AddSelection(s.prevSel.FilterMatcher(m)) } ================================================ FILE: vendor/github.com/PuerkitoBio/goquery/filter.go ================================================ package goquery import "golang.org/x/net/html" // Filter reduces the set of matched elements to those that match the selector string. // It returns a new Selection object for this subset of matching elements. func (s *Selection) Filter(selector string) *Selection { return s.FilterMatcher(compileMatcher(selector)) } // FilterMatcher reduces the set of matched elements to those that match // the given matcher. It returns a new Selection object for this subset // of matching elements. func (s *Selection) FilterMatcher(m Matcher) *Selection { return pushStack(s, winnow(s, m, true)) } // Not removes elements from the Selection that match the selector string. // It returns a new Selection object with the matching elements removed. func (s *Selection) Not(selector string) *Selection { return s.NotMatcher(compileMatcher(selector)) } // NotMatcher removes elements from the Selection that match the given matcher. // It returns a new Selection object with the matching elements removed. func (s *Selection) NotMatcher(m Matcher) *Selection { return pushStack(s, winnow(s, m, false)) } // FilterFunction reduces the set of matched elements to those that pass the function's test. // It returns a new Selection object for this subset of elements. func (s *Selection) FilterFunction(f func(int, *Selection) bool) *Selection { return pushStack(s, winnowFunction(s, f, true)) } // NotFunction removes elements from the Selection that pass the function's test. // It returns a new Selection object with the matching elements removed. func (s *Selection) NotFunction(f func(int, *Selection) bool) *Selection { return pushStack(s, winnowFunction(s, f, false)) } // FilterNodes reduces the set of matched elements to those that match the specified nodes. // It returns a new Selection object for this subset of elements. func (s *Selection) FilterNodes(nodes ...*html.Node) *Selection { return pushStack(s, winnowNodes(s, nodes, true)) } // NotNodes removes elements from the Selection that match the specified nodes. // It returns a new Selection object with the matching elements removed. func (s *Selection) NotNodes(nodes ...*html.Node) *Selection { return pushStack(s, winnowNodes(s, nodes, false)) } // FilterSelection reduces the set of matched elements to those that match a // node in the specified Selection object. // It returns a new Selection object for this subset of elements. func (s *Selection) FilterSelection(sel *Selection) *Selection { if sel == nil { return pushStack(s, winnowNodes(s, nil, true)) } return pushStack(s, winnowNodes(s, sel.Nodes, true)) } // NotSelection removes elements from the Selection that match a node in the specified // Selection object. It returns a new Selection object with the matching elements removed. func (s *Selection) NotSelection(sel *Selection) *Selection { if sel == nil { return pushStack(s, winnowNodes(s, nil, false)) } return pushStack(s, winnowNodes(s, sel.Nodes, false)) } // Intersection is an alias for FilterSelection. func (s *Selection) Intersection(sel *Selection) *Selection { return s.FilterSelection(sel) } // Has reduces the set of matched elements to those that have a descendant // that matches the selector. // It returns a new Selection object with the matching elements. func (s *Selection) Has(selector string) *Selection { return s.HasSelection(s.document.Find(selector)) } // HasMatcher reduces the set of matched elements to those that have a descendant // that matches the matcher. // It returns a new Selection object with the matching elements. func (s *Selection) HasMatcher(m Matcher) *Selection { return s.HasSelection(s.document.FindMatcher(m)) } // HasNodes reduces the set of matched elements to those that have a // descendant that matches one of the nodes. // It returns a new Selection object with the matching elements. func (s *Selection) HasNodes(nodes ...*html.Node) *Selection { return s.FilterFunction(func(_ int, sel *Selection) bool { // Add all nodes that contain one of the specified nodes for _, n := range nodes { if sel.Contains(n) { return true } } return false }) } // HasSelection reduces the set of matched elements to those that have a // descendant that matches one of the nodes of the specified Selection object. // It returns a new Selection object with the matching elements. func (s *Selection) HasSelection(sel *Selection) *Selection { if sel == nil { return s.HasNodes() } return s.HasNodes(sel.Nodes...) } // End ends the most recent filtering operation in the current chain and // returns the set of matched elements to its previous state. func (s *Selection) End() *Selection { if s.prevSel != nil { return s.prevSel } return newEmptySelection(s.document) } // Filter based on the matcher, and the indicator to keep (Filter) or // to get rid of (Not) the matching elements. func winnow(sel *Selection, m Matcher, keep bool) []*html.Node { // Optimize if keep is requested if keep { return m.Filter(sel.Nodes) } // Use grep return grep(sel, func(i int, s *Selection) bool { return !m.Match(s.Get(0)) }) } // Filter based on an array of nodes, and the indicator to keep (Filter) or // to get rid of (Not) the matching elements. func winnowNodes(sel *Selection, nodes []*html.Node, keep bool) []*html.Node { if len(nodes)+len(sel.Nodes) < minNodesForSet { return grep(sel, func(i int, s *Selection) bool { return isInSlice(nodes, s.Get(0)) == keep }) } set := make(map[*html.Node]bool) for _, n := range nodes { set[n] = true } return grep(sel, func(i int, s *Selection) bool { return set[s.Get(0)] == keep }) } // Filter based on a function test, and the indicator to keep (Filter) or // to get rid of (Not) the matching elements. func winnowFunction(sel *Selection, f func(int, *Selection) bool, keep bool) []*html.Node { return grep(sel, func(i int, s *Selection) bool { return f(i, s) == keep }) } ================================================ FILE: vendor/github.com/PuerkitoBio/goquery/iteration.go ================================================ package goquery // Each iterates over a Selection object, executing a function for each // matched element. It returns the current Selection object. The function // f is called for each element in the selection with the index of the // element in that selection starting at 0, and a *Selection that contains // only that element. func (s *Selection) Each(f func(int, *Selection)) *Selection { for i, n := range s.Nodes { f(i, newSingleSelection(n, s.document)) } return s } // EachWithBreak iterates over a Selection object, executing a function for each // matched element. It is identical to Each except that it is possible to break // out of the loop by returning false in the callback function. It returns the // current Selection object. func (s *Selection) EachWithBreak(f func(int, *Selection) bool) *Selection { for i, n := range s.Nodes { if !f(i, newSingleSelection(n, s.document)) { return s } } return s } // Map passes each element in the current matched set through a function, // producing a slice of string holding the returned values. The function // f is called for each element in the selection with the index of the // element in that selection starting at 0, and a *Selection that contains // only that element. func (s *Selection) Map(f func(int, *Selection) string) (result []string) { for i, n := range s.Nodes { result = append(result, f(i, newSingleSelection(n, s.document))) } return result } ================================================ FILE: vendor/github.com/PuerkitoBio/goquery/manipulation.go ================================================ package goquery import ( "strings" "golang.org/x/net/html" ) // After applies the selector from the root document and inserts the matched elements // after the elements in the set of matched elements. // // If one of the matched elements in the selection is not currently in the // document, it's impossible to insert nodes after it, so it will be ignored. // // This follows the same rules as Selection.Append. func (s *Selection) After(selector string) *Selection { return s.AfterMatcher(compileMatcher(selector)) } // AfterMatcher applies the matcher from the root document and inserts the matched elements // after the elements in the set of matched elements. // // If one of the matched elements in the selection is not currently in the // document, it's impossible to insert nodes after it, so it will be ignored. // // This follows the same rules as Selection.Append. func (s *Selection) AfterMatcher(m Matcher) *Selection { return s.AfterNodes(m.MatchAll(s.document.rootNode)...) } // AfterSelection inserts the elements in the selection after each element in the set of matched // elements. // // This follows the same rules as Selection.Append. func (s *Selection) AfterSelection(sel *Selection) *Selection { return s.AfterNodes(sel.Nodes...) } // AfterHtml parses the html and inserts it after the set of matched elements. // // This follows the same rules as Selection.Append. func (s *Selection) AfterHtml(htmlStr string) *Selection { return s.eachNodeHtml(htmlStr, true, func(node *html.Node, nodes []*html.Node) { nextSibling := node.NextSibling for _, n := range nodes { if node.Parent != nil { node.Parent.InsertBefore(n, nextSibling) } } }) } // AfterNodes inserts the nodes after each element in the set of matched elements. // // This follows the same rules as Selection.Append. func (s *Selection) AfterNodes(ns ...*html.Node) *Selection { return s.manipulateNodes(ns, true, func(sn *html.Node, n *html.Node) { if sn.Parent != nil { sn.Parent.InsertBefore(n, sn.NextSibling) } }) } // Append appends the elements specified by the selector to the end of each element // in the set of matched elements, following those rules: // // 1) The selector is applied to the root document. // // 2) Elements that are part of the document will be moved to the new location. // // 3) If there are multiple locations to append to, cloned nodes will be // appended to all target locations except the last one, which will be moved // as noted in (2). func (s *Selection) Append(selector string) *Selection { return s.AppendMatcher(compileMatcher(selector)) } // AppendMatcher appends the elements specified by the matcher to the end of each element // in the set of matched elements. // // This follows the same rules as Selection.Append. func (s *Selection) AppendMatcher(m Matcher) *Selection { return s.AppendNodes(m.MatchAll(s.document.rootNode)...) } // AppendSelection appends the elements in the selection to the end of each element // in the set of matched elements. // // This follows the same rules as Selection.Append. func (s *Selection) AppendSelection(sel *Selection) *Selection { return s.AppendNodes(sel.Nodes...) } // AppendHtml parses the html and appends it to the set of matched elements. func (s *Selection) AppendHtml(htmlStr string) *Selection { return s.eachNodeHtml(htmlStr, false, func(node *html.Node, nodes []*html.Node) { for _, n := range nodes { node.AppendChild(n) } }) } // AppendNodes appends the specified nodes to each node in the set of matched elements. // // This follows the same rules as Selection.Append. func (s *Selection) AppendNodes(ns ...*html.Node) *Selection { return s.manipulateNodes(ns, false, func(sn *html.Node, n *html.Node) { sn.AppendChild(n) }) } // Before inserts the matched elements before each element in the set of matched elements. // // This follows the same rules as Selection.Append. func (s *Selection) Before(selector string) *Selection { return s.BeforeMatcher(compileMatcher(selector)) } // BeforeMatcher inserts the matched elements before each element in the set of matched elements. // // This follows the same rules as Selection.Append. func (s *Selection) BeforeMatcher(m Matcher) *Selection { return s.BeforeNodes(m.MatchAll(s.document.rootNode)...) } // BeforeSelection inserts the elements in the selection before each element in the set of matched // elements. // // This follows the same rules as Selection.Append. func (s *Selection) BeforeSelection(sel *Selection) *Selection { return s.BeforeNodes(sel.Nodes...) } // BeforeHtml parses the html and inserts it before the set of matched elements. // // This follows the same rules as Selection.Append. func (s *Selection) BeforeHtml(htmlStr string) *Selection { return s.eachNodeHtml(htmlStr, true, func(node *html.Node, nodes []*html.Node) { for _, n := range nodes { if node.Parent != nil { node.Parent.InsertBefore(n, node) } } }) } // BeforeNodes inserts the nodes before each element in the set of matched elements. // // This follows the same rules as Selection.Append. func (s *Selection) BeforeNodes(ns ...*html.Node) *Selection { return s.manipulateNodes(ns, false, func(sn *html.Node, n *html.Node) { if sn.Parent != nil { sn.Parent.InsertBefore(n, sn) } }) } // Clone creates a deep copy of the set of matched nodes. The new nodes will not be // attached to the document. func (s *Selection) Clone() *Selection { ns := newEmptySelection(s.document) ns.Nodes = cloneNodes(s.Nodes) return ns } // Empty removes all children nodes from the set of matched elements. // It returns the children nodes in a new Selection. func (s *Selection) Empty() *Selection { var nodes []*html.Node for _, n := range s.Nodes { for c := n.FirstChild; c != nil; c = n.FirstChild { n.RemoveChild(c) nodes = append(nodes, c) } } return pushStack(s, nodes) } // Prepend prepends the elements specified by the selector to each element in // the set of matched elements, following the same rules as Append. func (s *Selection) Prepend(selector string) *Selection { return s.PrependMatcher(compileMatcher(selector)) } // PrependMatcher prepends the elements specified by the matcher to each // element in the set of matched elements. // // This follows the same rules as Selection.Append. func (s *Selection) PrependMatcher(m Matcher) *Selection { return s.PrependNodes(m.MatchAll(s.document.rootNode)...) } // PrependSelection prepends the elements in the selection to each element in // the set of matched elements. // // This follows the same rules as Selection.Append. func (s *Selection) PrependSelection(sel *Selection) *Selection { return s.PrependNodes(sel.Nodes...) } // PrependHtml parses the html and prepends it to the set of matched elements. func (s *Selection) PrependHtml(htmlStr string) *Selection { return s.eachNodeHtml(htmlStr, false, func(node *html.Node, nodes []*html.Node) { firstChild := node.FirstChild for _, n := range nodes { node.InsertBefore(n, firstChild) } }) } // PrependNodes prepends the specified nodes to each node in the set of // matched elements. // // This follows the same rules as Selection.Append. func (s *Selection) PrependNodes(ns ...*html.Node) *Selection { return s.manipulateNodes(ns, true, func(sn *html.Node, n *html.Node) { // sn.FirstChild may be nil, in which case this functions like // sn.AppendChild() sn.InsertBefore(n, sn.FirstChild) }) } // Remove removes the set of matched elements from the document. // It returns the same selection, now consisting of nodes not in the document. func (s *Selection) Remove() *Selection { for _, n := range s.Nodes { if n.Parent != nil { n.Parent.RemoveChild(n) } } return s } // RemoveFiltered removes from the current set of matched elements those that // match the selector filter. It returns the Selection of removed nodes. // // For example if the selection s contains "

", "

" and "

" // and s.RemoveFiltered("h2") is called, only the "

" node is removed // (and returned), while "

" and "

" are kept in the document. func (s *Selection) RemoveFiltered(selector string) *Selection { return s.RemoveMatcher(compileMatcher(selector)) } // RemoveMatcher removes from the current set of matched elements those that // match the Matcher filter. It returns the Selection of removed nodes. // See RemoveFiltered for additional information. func (s *Selection) RemoveMatcher(m Matcher) *Selection { return s.FilterMatcher(m).Remove() } // ReplaceWith replaces each element in the set of matched elements with the // nodes matched by the given selector. // It returns the removed elements. // // This follows the same rules as Selection.Append. func (s *Selection) ReplaceWith(selector string) *Selection { return s.ReplaceWithMatcher(compileMatcher(selector)) } // ReplaceWithMatcher replaces each element in the set of matched elements with // the nodes matched by the given Matcher. // It returns the removed elements. // // This follows the same rules as Selection.Append. func (s *Selection) ReplaceWithMatcher(m Matcher) *Selection { return s.ReplaceWithNodes(m.MatchAll(s.document.rootNode)...) } // ReplaceWithSelection replaces each element in the set of matched elements with // the nodes from the given Selection. // It returns the removed elements. // // This follows the same rules as Selection.Append. func (s *Selection) ReplaceWithSelection(sel *Selection) *Selection { return s.ReplaceWithNodes(sel.Nodes...) } // ReplaceWithHtml replaces each element in the set of matched elements with // the parsed HTML. // It returns the removed elements. // // This follows the same rules as Selection.Append. func (s *Selection) ReplaceWithHtml(htmlStr string) *Selection { s.eachNodeHtml(htmlStr, true, func(node *html.Node, nodes []*html.Node) { nextSibling := node.NextSibling for _, n := range nodes { if node.Parent != nil { node.Parent.InsertBefore(n, nextSibling) } } }) return s.Remove() } // ReplaceWithNodes replaces each element in the set of matched elements with // the given nodes. // It returns the removed elements. // // This follows the same rules as Selection.Append. func (s *Selection) ReplaceWithNodes(ns ...*html.Node) *Selection { s.AfterNodes(ns...) return s.Remove() } // SetHtml sets the html content of each element in the selection to // specified html string. func (s *Selection) SetHtml(htmlStr string) *Selection { for _, context := range s.Nodes { for c := context.FirstChild; c != nil; c = context.FirstChild { context.RemoveChild(c) } } return s.eachNodeHtml(htmlStr, false, func(node *html.Node, nodes []*html.Node) { for _, n := range nodes { node.AppendChild(n) } }) } // SetText sets the content of each element in the selection to specified content. // The provided text string is escaped. func (s *Selection) SetText(text string) *Selection { return s.SetHtml(html.EscapeString(text)) } // Unwrap removes the parents of the set of matched elements, leaving the matched // elements (and their siblings, if any) in their place. // It returns the original selection. func (s *Selection) Unwrap() *Selection { s.Parent().Each(func(i int, ss *Selection) { // For some reason, jquery allows unwrap to remove the element, so // allowing it here too. Same for . Why it allows those elements to // be unwrapped while not allowing body is a mystery to me. if ss.Nodes[0].Data != "body" { ss.ReplaceWithSelection(ss.Contents()) } }) return s } // Wrap wraps each element in the set of matched elements inside the first // element matched by the given selector. The matched child is cloned before // being inserted into the document. // // It returns the original set of elements. func (s *Selection) Wrap(selector string) *Selection { return s.WrapMatcher(compileMatcher(selector)) } // WrapMatcher wraps each element in the set of matched elements inside the // first element matched by the given matcher. The matched child is cloned // before being inserted into the document. // // It returns the original set of elements. func (s *Selection) WrapMatcher(m Matcher) *Selection { return s.wrapNodes(m.MatchAll(s.document.rootNode)...) } // WrapSelection wraps each element in the set of matched elements inside the // first element in the given Selection. The element is cloned before being // inserted into the document. // // It returns the original set of elements. func (s *Selection) WrapSelection(sel *Selection) *Selection { return s.wrapNodes(sel.Nodes...) } // WrapHtml wraps each element in the set of matched elements inside the inner- // most child of the given HTML. // // It returns the original set of elements. func (s *Selection) WrapHtml(htmlStr string) *Selection { nodesMap := make(map[string][]*html.Node) for _, context := range s.Nodes { var parent *html.Node if context.Parent != nil { parent = context.Parent } else { parent = &html.Node{Type: html.ElementNode} } nodes, found := nodesMap[nodeName(parent)] if !found { nodes = parseHtmlWithContext(htmlStr, parent) nodesMap[nodeName(parent)] = nodes } newSingleSelection(context, s.document).wrapAllNodes(cloneNodes(nodes)...) } return s } // WrapNode wraps each element in the set of matched elements inside the inner- // most child of the given node. The given node is copied before being inserted // into the document. // // It returns the original set of elements. func (s *Selection) WrapNode(n *html.Node) *Selection { return s.wrapNodes(n) } func (s *Selection) wrapNodes(ns ...*html.Node) *Selection { s.Each(func(i int, ss *Selection) { ss.wrapAllNodes(ns...) }) return s } // WrapAll wraps a single HTML structure, matched by the given selector, around // all elements in the set of matched elements. The matched child is cloned // before being inserted into the document. // // It returns the original set of elements. func (s *Selection) WrapAll(selector string) *Selection { return s.WrapAllMatcher(compileMatcher(selector)) } // WrapAllMatcher wraps a single HTML structure, matched by the given Matcher, // around all elements in the set of matched elements. The matched child is // cloned before being inserted into the document. // // It returns the original set of elements. func (s *Selection) WrapAllMatcher(m Matcher) *Selection { return s.wrapAllNodes(m.MatchAll(s.document.rootNode)...) } // WrapAllSelection wraps a single HTML structure, the first node of the given // Selection, around all elements in the set of matched elements. The matched // child is cloned before being inserted into the document. // // It returns the original set of elements. func (s *Selection) WrapAllSelection(sel *Selection) *Selection { return s.wrapAllNodes(sel.Nodes...) } // WrapAllHtml wraps the given HTML structure around all elements in the set of // matched elements. The matched child is cloned before being inserted into the // document. // // It returns the original set of elements. func (s *Selection) WrapAllHtml(htmlStr string) *Selection { var context *html.Node var nodes []*html.Node if len(s.Nodes) > 0 { context = s.Nodes[0] if context.Parent != nil { nodes = parseHtmlWithContext(htmlStr, context) } else { nodes = parseHtml(htmlStr) } } return s.wrapAllNodes(nodes...) } func (s *Selection) wrapAllNodes(ns ...*html.Node) *Selection { if len(ns) > 0 { return s.WrapAllNode(ns[0]) } return s } // WrapAllNode wraps the given node around the first element in the Selection, // making all other nodes in the Selection children of the given node. The node // is cloned before being inserted into the document. // // It returns the original set of elements. func (s *Selection) WrapAllNode(n *html.Node) *Selection { if s.Size() == 0 { return s } wrap := cloneNode(n) first := s.Nodes[0] if first.Parent != nil { first.Parent.InsertBefore(wrap, first) first.Parent.RemoveChild(first) } for c := getFirstChildEl(wrap); c != nil; c = getFirstChildEl(wrap) { wrap = c } newSingleSelection(wrap, s.document).AppendSelection(s) return s } // WrapInner wraps an HTML structure, matched by the given selector, around the // content of element in the set of matched elements. The matched child is // cloned before being inserted into the document. // // It returns the original set of elements. func (s *Selection) WrapInner(selector string) *Selection { return s.WrapInnerMatcher(compileMatcher(selector)) } // WrapInnerMatcher wraps an HTML structure, matched by the given selector, // around the content of element in the set of matched elements. The matched // child is cloned before being inserted into the document. // // It returns the original set of elements. func (s *Selection) WrapInnerMatcher(m Matcher) *Selection { return s.wrapInnerNodes(m.MatchAll(s.document.rootNode)...) } // WrapInnerSelection wraps an HTML structure, matched by the given selector, // around the content of element in the set of matched elements. The matched // child is cloned before being inserted into the document. // // It returns the original set of elements. func (s *Selection) WrapInnerSelection(sel *Selection) *Selection { return s.wrapInnerNodes(sel.Nodes...) } // WrapInnerHtml wraps an HTML structure, matched by the given selector, around // the content of element in the set of matched elements. The matched child is // cloned before being inserted into the document. // // It returns the original set of elements. func (s *Selection) WrapInnerHtml(htmlStr string) *Selection { nodesMap := make(map[string][]*html.Node) for _, context := range s.Nodes { nodes, found := nodesMap[nodeName(context)] if !found { nodes = parseHtmlWithContext(htmlStr, context) nodesMap[nodeName(context)] = nodes } newSingleSelection(context, s.document).wrapInnerNodes(cloneNodes(nodes)...) } return s } // WrapInnerNode wraps an HTML structure, matched by the given selector, around // the content of element in the set of matched elements. The matched child is // cloned before being inserted into the document. // // It returns the original set of elements. func (s *Selection) WrapInnerNode(n *html.Node) *Selection { return s.wrapInnerNodes(n) } func (s *Selection) wrapInnerNodes(ns ...*html.Node) *Selection { if len(ns) == 0 { return s } s.Each(func(i int, s *Selection) { contents := s.Contents() if contents.Size() > 0 { contents.wrapAllNodes(ns...) } else { s.AppendNodes(cloneNode(ns[0])) } }) return s } func parseHtml(h string) []*html.Node { // Errors are only returned when the io.Reader returns any error besides // EOF, but strings.Reader never will nodes, err := html.ParseFragment(strings.NewReader(h), &html.Node{Type: html.ElementNode}) if err != nil { panic("goquery: failed to parse HTML: " + err.Error()) } return nodes } func parseHtmlWithContext(h string, context *html.Node) []*html.Node { // Errors are only returned when the io.Reader returns any error besides // EOF, but strings.Reader never will nodes, err := html.ParseFragment(strings.NewReader(h), context) if err != nil { panic("goquery: failed to parse HTML: " + err.Error()) } return nodes } // Get the first child that is an ElementNode func getFirstChildEl(n *html.Node) *html.Node { c := n.FirstChild for c != nil && c.Type != html.ElementNode { c = c.NextSibling } return c } // Deep copy a slice of nodes. func cloneNodes(ns []*html.Node) []*html.Node { cns := make([]*html.Node, 0, len(ns)) for _, n := range ns { cns = append(cns, cloneNode(n)) } return cns } // Deep copy a node. The new node has clones of all the original node's // children but none of its parents or siblings. func cloneNode(n *html.Node) *html.Node { nn := &html.Node{ Type: n.Type, DataAtom: n.DataAtom, Data: n.Data, Attr: make([]html.Attribute, len(n.Attr)), } copy(nn.Attr, n.Attr) for c := n.FirstChild; c != nil; c = c.NextSibling { nn.AppendChild(cloneNode(c)) } return nn } func (s *Selection) manipulateNodes(ns []*html.Node, reverse bool, f func(sn *html.Node, n *html.Node)) *Selection { lasti := s.Size() - 1 // net.Html doesn't provide document fragments for insertion, so to get // things in the correct order with After() and Prepend(), the callback // needs to be called on the reverse of the nodes. if reverse { for i, j := 0, len(ns)-1; i < j; i, j = i+1, j-1 { ns[i], ns[j] = ns[j], ns[i] } } for i, sn := range s.Nodes { for _, n := range ns { if i != lasti { f(sn, cloneNode(n)) } else { if n.Parent != nil { n.Parent.RemoveChild(n) } f(sn, n) } } } return s } // eachNodeHtml parses the given html string and inserts the resulting nodes in the dom with the mergeFn. // The parsed nodes are inserted for each element of the selection. // isParent can be used to indicate that the elements of the selection should be treated as the parent for the parsed html. // A cache is used to avoid parsing the html multiple times should the elements of the selection result in the same context. func (s *Selection) eachNodeHtml(htmlStr string, isParent bool, mergeFn func(n *html.Node, nodes []*html.Node)) *Selection { // cache to avoid parsing the html for the same context multiple times nodeCache := make(map[string][]*html.Node) var context *html.Node for _, n := range s.Nodes { if isParent { context = n.Parent } else { if n.Type != html.ElementNode { continue } context = n } if context != nil { nodes, found := nodeCache[nodeName(context)] if !found { nodes = parseHtmlWithContext(htmlStr, context) nodeCache[nodeName(context)] = nodes } mergeFn(n, cloneNodes(nodes)) } } return s } ================================================ FILE: vendor/github.com/PuerkitoBio/goquery/property.go ================================================ package goquery import ( "bytes" "regexp" "strings" "golang.org/x/net/html" ) var rxClassTrim = regexp.MustCompile("[\t\r\n]") // Attr gets the specified attribute's value for the first element in the // Selection. To get the value for each element individually, use a looping // construct such as Each or Map method. func (s *Selection) Attr(attrName string) (val string, exists bool) { if len(s.Nodes) == 0 { return } return getAttributeValue(attrName, s.Nodes[0]) } // AttrOr works like Attr but returns default value if attribute is not present. func (s *Selection) AttrOr(attrName, defaultValue string) string { if len(s.Nodes) == 0 { return defaultValue } val, exists := getAttributeValue(attrName, s.Nodes[0]) if !exists { return defaultValue } return val } // RemoveAttr removes the named attribute from each element in the set of matched elements. func (s *Selection) RemoveAttr(attrName string) *Selection { for _, n := range s.Nodes { removeAttr(n, attrName) } return s } // SetAttr sets the given attribute on each element in the set of matched elements. func (s *Selection) SetAttr(attrName, val string) *Selection { for _, n := range s.Nodes { attr := getAttributePtr(attrName, n) if attr == nil { n.Attr = append(n.Attr, html.Attribute{Key: attrName, Val: val}) } else { attr.Val = val } } return s } // Text gets the combined text contents of each element in the set of matched // elements, including their descendants. func (s *Selection) Text() string { var buf bytes.Buffer // Slightly optimized vs calling Each: no single selection object created var f func(*html.Node) f = func(n *html.Node) { if n.Type == html.TextNode { // Keep newlines and spaces, like jQuery buf.WriteString(n.Data) } if n.FirstChild != nil { for c := n.FirstChild; c != nil; c = c.NextSibling { f(c) } } } for _, n := range s.Nodes { f(n) } return buf.String() } // Size is an alias for Length. func (s *Selection) Size() int { return s.Length() } // Length returns the number of elements in the Selection object. func (s *Selection) Length() int { return len(s.Nodes) } // Html gets the HTML contents of the first element in the set of matched // elements. It includes text and comment nodes. func (s *Selection) Html() (ret string, e error) { // Since there is no .innerHtml, the HTML content must be re-created from // the nodes using html.Render. var buf bytes.Buffer if len(s.Nodes) > 0 { for c := s.Nodes[0].FirstChild; c != nil; c = c.NextSibling { e = html.Render(&buf, c) if e != nil { return } } ret = buf.String() } return } // AddClass adds the given class(es) to each element in the set of matched elements. // Multiple class names can be specified, separated by a space or via multiple arguments. func (s *Selection) AddClass(class ...string) *Selection { classStr := strings.TrimSpace(strings.Join(class, " ")) if classStr == "" { return s } tcls := getClassesSlice(classStr) for _, n := range s.Nodes { curClasses, attr := getClassesAndAttr(n, true) for _, newClass := range tcls { if !strings.Contains(curClasses, " "+newClass+" ") { curClasses += newClass + " " } } setClasses(n, attr, curClasses) } return s } // HasClass determines whether any of the matched elements are assigned the // given class. func (s *Selection) HasClass(class string) bool { class = " " + class + " " for _, n := range s.Nodes { classes, _ := getClassesAndAttr(n, false) if strings.Contains(classes, class) { return true } } return false } // RemoveClass removes the given class(es) from each element in the set of matched elements. // Multiple class names can be specified, separated by a space or via multiple arguments. // If no class name is provided, all classes are removed. func (s *Selection) RemoveClass(class ...string) *Selection { var rclasses []string classStr := strings.TrimSpace(strings.Join(class, " ")) remove := classStr == "" if !remove { rclasses = getClassesSlice(classStr) } for _, n := range s.Nodes { if remove { removeAttr(n, "class") } else { classes, attr := getClassesAndAttr(n, true) for _, rcl := range rclasses { classes = strings.Replace(classes, " "+rcl+" ", " ", -1) } setClasses(n, attr, classes) } } return s } // ToggleClass adds or removes the given class(es) for each element in the set of matched elements. // Multiple class names can be specified, separated by a space or via multiple arguments. func (s *Selection) ToggleClass(class ...string) *Selection { classStr := strings.TrimSpace(strings.Join(class, " ")) if classStr == "" { return s } tcls := getClassesSlice(classStr) for _, n := range s.Nodes { classes, attr := getClassesAndAttr(n, true) for _, tcl := range tcls { if strings.Contains(classes, " "+tcl+" ") { classes = strings.Replace(classes, " "+tcl+" ", " ", -1) } else { classes += tcl + " " } } setClasses(n, attr, classes) } return s } func getAttributePtr(attrName string, n *html.Node) *html.Attribute { if n == nil { return nil } for i, a := range n.Attr { if a.Key == attrName { return &n.Attr[i] } } return nil } // Private function to get the specified attribute's value from a node. func getAttributeValue(attrName string, n *html.Node) (val string, exists bool) { if a := getAttributePtr(attrName, n); a != nil { val = a.Val exists = true } return } // Get and normalize the "class" attribute from the node. func getClassesAndAttr(n *html.Node, create bool) (classes string, attr *html.Attribute) { // Applies only to element nodes if n.Type == html.ElementNode { attr = getAttributePtr("class", n) if attr == nil && create { n.Attr = append(n.Attr, html.Attribute{ Key: "class", Val: "", }) attr = &n.Attr[len(n.Attr)-1] } } if attr == nil { classes = " " } else { classes = rxClassTrim.ReplaceAllString(" "+attr.Val+" ", " ") } return } func getClassesSlice(classes string) []string { return strings.Split(rxClassTrim.ReplaceAllString(" "+classes+" ", " "), " ") } func removeAttr(n *html.Node, attrName string) { for i, a := range n.Attr { if a.Key == attrName { n.Attr[i], n.Attr[len(n.Attr)-1], n.Attr = n.Attr[len(n.Attr)-1], html.Attribute{}, n.Attr[:len(n.Attr)-1] return } } } func setClasses(n *html.Node, attr *html.Attribute, classes string) { classes = strings.TrimSpace(classes) if classes == "" { removeAttr(n, "class") return } attr.Val = classes } ================================================ FILE: vendor/github.com/PuerkitoBio/goquery/query.go ================================================ package goquery import "golang.org/x/net/html" // Is checks the current matched set of elements against a selector and // returns true if at least one of these elements matches. func (s *Selection) Is(selector string) bool { return s.IsMatcher(compileMatcher(selector)) } // IsMatcher checks the current matched set of elements against a matcher and // returns true if at least one of these elements matches. func (s *Selection) IsMatcher(m Matcher) bool { if len(s.Nodes) > 0 { if len(s.Nodes) == 1 { return m.Match(s.Nodes[0]) } return len(m.Filter(s.Nodes)) > 0 } return false } // IsFunction checks the current matched set of elements against a predicate and // returns true if at least one of these elements matches. func (s *Selection) IsFunction(f func(int, *Selection) bool) bool { return s.FilterFunction(f).Length() > 0 } // IsSelection checks the current matched set of elements against a Selection object // and returns true if at least one of these elements matches. func (s *Selection) IsSelection(sel *Selection) bool { return s.FilterSelection(sel).Length() > 0 } // IsNodes checks the current matched set of elements against the specified nodes // and returns true if at least one of these elements matches. func (s *Selection) IsNodes(nodes ...*html.Node) bool { return s.FilterNodes(nodes...).Length() > 0 } // Contains returns true if the specified Node is within, // at any depth, one of the nodes in the Selection object. // It is NOT inclusive, to behave like jQuery's implementation, and // unlike Javascript's .contains, so if the contained // node is itself in the selection, it returns false. func (s *Selection) Contains(n *html.Node) bool { return sliceContains(s.Nodes, n) } ================================================ FILE: vendor/github.com/PuerkitoBio/goquery/traversal.go ================================================ package goquery import "golang.org/x/net/html" type siblingType int // Sibling type, used internally when iterating over children at the same // level (siblings) to specify which nodes are requested. const ( siblingPrevUntil siblingType = iota - 3 siblingPrevAll siblingPrev siblingAll siblingNext siblingNextAll siblingNextUntil siblingAllIncludingNonElements ) // Find gets the descendants of each element in the current set of matched // elements, filtered by a selector. It returns a new Selection object // containing these matched elements. func (s *Selection) Find(selector string) *Selection { return pushStack(s, findWithMatcher(s.Nodes, compileMatcher(selector))) } // FindMatcher gets the descendants of each element in the current set of matched // elements, filtered by the matcher. It returns a new Selection object // containing these matched elements. func (s *Selection) FindMatcher(m Matcher) *Selection { return pushStack(s, findWithMatcher(s.Nodes, m)) } // FindSelection gets the descendants of each element in the current // Selection, filtered by a Selection. It returns a new Selection object // containing these matched elements. func (s *Selection) FindSelection(sel *Selection) *Selection { if sel == nil { return pushStack(s, nil) } return s.FindNodes(sel.Nodes...) } // FindNodes gets the descendants of each element in the current // Selection, filtered by some nodes. It returns a new Selection object // containing these matched elements. func (s *Selection) FindNodes(nodes ...*html.Node) *Selection { return pushStack(s, mapNodes(nodes, func(i int, n *html.Node) []*html.Node { if sliceContains(s.Nodes, n) { return []*html.Node{n} } return nil })) } // Contents gets the children of each element in the Selection, // including text and comment nodes. It returns a new Selection object // containing these elements. func (s *Selection) Contents() *Selection { return pushStack(s, getChildrenNodes(s.Nodes, siblingAllIncludingNonElements)) } // ContentsFiltered gets the children of each element in the Selection, // filtered by the specified selector. It returns a new Selection // object containing these elements. Since selectors only act on Element nodes, // this function is an alias to ChildrenFiltered unless the selector is empty, // in which case it is an alias to Contents. func (s *Selection) ContentsFiltered(selector string) *Selection { if selector != "" { return s.ChildrenFiltered(selector) } return s.Contents() } // ContentsMatcher gets the children of each element in the Selection, // filtered by the specified matcher. It returns a new Selection // object containing these elements. Since matchers only act on Element nodes, // this function is an alias to ChildrenMatcher. func (s *Selection) ContentsMatcher(m Matcher) *Selection { return s.ChildrenMatcher(m) } // Children gets the child elements of each element in the Selection. // It returns a new Selection object containing these elements. func (s *Selection) Children() *Selection { return pushStack(s, getChildrenNodes(s.Nodes, siblingAll)) } // ChildrenFiltered gets the child elements of each element in the Selection, // filtered by the specified selector. It returns a new // Selection object containing these elements. func (s *Selection) ChildrenFiltered(selector string) *Selection { return filterAndPush(s, getChildrenNodes(s.Nodes, siblingAll), compileMatcher(selector)) } // ChildrenMatcher gets the child elements of each element in the Selection, // filtered by the specified matcher. It returns a new // Selection object containing these elements. func (s *Selection) ChildrenMatcher(m Matcher) *Selection { return filterAndPush(s, getChildrenNodes(s.Nodes, siblingAll), m) } // Parent gets the parent of each element in the Selection. It returns a // new Selection object containing the matched elements. func (s *Selection) Parent() *Selection { return pushStack(s, getParentNodes(s.Nodes)) } // ParentFiltered gets the parent of each element in the Selection filtered by a // selector. It returns a new Selection object containing the matched elements. func (s *Selection) ParentFiltered(selector string) *Selection { return filterAndPush(s, getParentNodes(s.Nodes), compileMatcher(selector)) } // ParentMatcher gets the parent of each element in the Selection filtered by a // matcher. It returns a new Selection object containing the matched elements. func (s *Selection) ParentMatcher(m Matcher) *Selection { return filterAndPush(s, getParentNodes(s.Nodes), m) } // Closest gets the first element that matches the selector by testing the // element itself and traversing up through its ancestors in the DOM tree. func (s *Selection) Closest(selector string) *Selection { cs := compileMatcher(selector) return s.ClosestMatcher(cs) } // ClosestMatcher gets the first element that matches the matcher by testing the // element itself and traversing up through its ancestors in the DOM tree. func (s *Selection) ClosestMatcher(m Matcher) *Selection { return pushStack(s, mapNodes(s.Nodes, func(i int, n *html.Node) []*html.Node { // For each node in the selection, test the node itself, then each parent // until a match is found. for ; n != nil; n = n.Parent { if m.Match(n) { return []*html.Node{n} } } return nil })) } // ClosestNodes gets the first element that matches one of the nodes by testing the // element itself and traversing up through its ancestors in the DOM tree. func (s *Selection) ClosestNodes(nodes ...*html.Node) *Selection { set := make(map[*html.Node]bool) for _, n := range nodes { set[n] = true } return pushStack(s, mapNodes(s.Nodes, func(i int, n *html.Node) []*html.Node { // For each node in the selection, test the node itself, then each parent // until a match is found. for ; n != nil; n = n.Parent { if set[n] { return []*html.Node{n} } } return nil })) } // ClosestSelection gets the first element that matches one of the nodes in the // Selection by testing the element itself and traversing up through its ancestors // in the DOM tree. func (s *Selection) ClosestSelection(sel *Selection) *Selection { if sel == nil { return pushStack(s, nil) } return s.ClosestNodes(sel.Nodes...) } // Parents gets the ancestors of each element in the current Selection. It // returns a new Selection object with the matched elements. func (s *Selection) Parents() *Selection { return pushStack(s, getParentsNodes(s.Nodes, nil, nil)) } // ParentsFiltered gets the ancestors of each element in the current // Selection. It returns a new Selection object with the matched elements. func (s *Selection) ParentsFiltered(selector string) *Selection { return filterAndPush(s, getParentsNodes(s.Nodes, nil, nil), compileMatcher(selector)) } // ParentsMatcher gets the ancestors of each element in the current // Selection. It returns a new Selection object with the matched elements. func (s *Selection) ParentsMatcher(m Matcher) *Selection { return filterAndPush(s, getParentsNodes(s.Nodes, nil, nil), m) } // ParentsUntil gets the ancestors of each element in the Selection, up to but // not including the element matched by the selector. It returns a new Selection // object containing the matched elements. func (s *Selection) ParentsUntil(selector string) *Selection { return pushStack(s, getParentsNodes(s.Nodes, compileMatcher(selector), nil)) } // ParentsUntilMatcher gets the ancestors of each element in the Selection, up to but // not including the element matched by the matcher. It returns a new Selection // object containing the matched elements. func (s *Selection) ParentsUntilMatcher(m Matcher) *Selection { return pushStack(s, getParentsNodes(s.Nodes, m, nil)) } // ParentsUntilSelection gets the ancestors of each element in the Selection, // up to but not including the elements in the specified Selection. It returns a // new Selection object containing the matched elements. func (s *Selection) ParentsUntilSelection(sel *Selection) *Selection { if sel == nil { return s.Parents() } return s.ParentsUntilNodes(sel.Nodes...) } // ParentsUntilNodes gets the ancestors of each element in the Selection, // up to but not including the specified nodes. It returns a // new Selection object containing the matched elements. func (s *Selection) ParentsUntilNodes(nodes ...*html.Node) *Selection { return pushStack(s, getParentsNodes(s.Nodes, nil, nodes)) } // ParentsFilteredUntil is like ParentsUntil, with the option to filter the // results based on a selector string. It returns a new Selection // object containing the matched elements. func (s *Selection) ParentsFilteredUntil(filterSelector, untilSelector string) *Selection { return filterAndPush(s, getParentsNodes(s.Nodes, compileMatcher(untilSelector), nil), compileMatcher(filterSelector)) } // ParentsFilteredUntilMatcher is like ParentsUntilMatcher, with the option to filter the // results based on a matcher. It returns a new Selection object containing the matched elements. func (s *Selection) ParentsFilteredUntilMatcher(filter, until Matcher) *Selection { return filterAndPush(s, getParentsNodes(s.Nodes, until, nil), filter) } // ParentsFilteredUntilSelection is like ParentsUntilSelection, with the // option to filter the results based on a selector string. It returns a new // Selection object containing the matched elements. func (s *Selection) ParentsFilteredUntilSelection(filterSelector string, sel *Selection) *Selection { return s.ParentsMatcherUntilSelection(compileMatcher(filterSelector), sel) } // ParentsMatcherUntilSelection is like ParentsUntilSelection, with the // option to filter the results based on a matcher. It returns a new // Selection object containing the matched elements. func (s *Selection) ParentsMatcherUntilSelection(filter Matcher, sel *Selection) *Selection { if sel == nil { return s.ParentsMatcher(filter) } return s.ParentsMatcherUntilNodes(filter, sel.Nodes...) } // ParentsFilteredUntilNodes is like ParentsUntilNodes, with the // option to filter the results based on a selector string. It returns a new // Selection object containing the matched elements. func (s *Selection) ParentsFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection { return filterAndPush(s, getParentsNodes(s.Nodes, nil, nodes), compileMatcher(filterSelector)) } // ParentsMatcherUntilNodes is like ParentsUntilNodes, with the // option to filter the results based on a matcher. It returns a new // Selection object containing the matched elements. func (s *Selection) ParentsMatcherUntilNodes(filter Matcher, nodes ...*html.Node) *Selection { return filterAndPush(s, getParentsNodes(s.Nodes, nil, nodes), filter) } // Siblings gets the siblings of each element in the Selection. It returns // a new Selection object containing the matched elements. func (s *Selection) Siblings() *Selection { return pushStack(s, getSiblingNodes(s.Nodes, siblingAll, nil, nil)) } // SiblingsFiltered gets the siblings of each element in the Selection // filtered by a selector. It returns a new Selection object containing the // matched elements. func (s *Selection) SiblingsFiltered(selector string) *Selection { return filterAndPush(s, getSiblingNodes(s.Nodes, siblingAll, nil, nil), compileMatcher(selector)) } // SiblingsMatcher gets the siblings of each element in the Selection // filtered by a matcher. It returns a new Selection object containing the // matched elements. func (s *Selection) SiblingsMatcher(m Matcher) *Selection { return filterAndPush(s, getSiblingNodes(s.Nodes, siblingAll, nil, nil), m) } // Next gets the immediately following sibling of each element in the // Selection. It returns a new Selection object containing the matched elements. func (s *Selection) Next() *Selection { return pushStack(s, getSiblingNodes(s.Nodes, siblingNext, nil, nil)) } // NextFiltered gets the immediately following sibling of each element in the // Selection filtered by a selector. It returns a new Selection object // containing the matched elements. func (s *Selection) NextFiltered(selector string) *Selection { return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNext, nil, nil), compileMatcher(selector)) } // NextMatcher gets the immediately following sibling of each element in the // Selection filtered by a matcher. It returns a new Selection object // containing the matched elements. func (s *Selection) NextMatcher(m Matcher) *Selection { return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNext, nil, nil), m) } // NextAll gets all the following siblings of each element in the // Selection. It returns a new Selection object containing the matched elements. func (s *Selection) NextAll() *Selection { return pushStack(s, getSiblingNodes(s.Nodes, siblingNextAll, nil, nil)) } // NextAllFiltered gets all the following siblings of each element in the // Selection filtered by a selector. It returns a new Selection object // containing the matched elements. func (s *Selection) NextAllFiltered(selector string) *Selection { return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextAll, nil, nil), compileMatcher(selector)) } // NextAllMatcher gets all the following siblings of each element in the // Selection filtered by a matcher. It returns a new Selection object // containing the matched elements. func (s *Selection) NextAllMatcher(m Matcher) *Selection { return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextAll, nil, nil), m) } // Prev gets the immediately preceding sibling of each element in the // Selection. It returns a new Selection object containing the matched elements. func (s *Selection) Prev() *Selection { return pushStack(s, getSiblingNodes(s.Nodes, siblingPrev, nil, nil)) } // PrevFiltered gets the immediately preceding sibling of each element in the // Selection filtered by a selector. It returns a new Selection object // containing the matched elements. func (s *Selection) PrevFiltered(selector string) *Selection { return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrev, nil, nil), compileMatcher(selector)) } // PrevMatcher gets the immediately preceding sibling of each element in the // Selection filtered by a matcher. It returns a new Selection object // containing the matched elements. func (s *Selection) PrevMatcher(m Matcher) *Selection { return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrev, nil, nil), m) } // PrevAll gets all the preceding siblings of each element in the // Selection. It returns a new Selection object containing the matched elements. func (s *Selection) PrevAll() *Selection { return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevAll, nil, nil)) } // PrevAllFiltered gets all the preceding siblings of each element in the // Selection filtered by a selector. It returns a new Selection object // containing the matched elements. func (s *Selection) PrevAllFiltered(selector string) *Selection { return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevAll, nil, nil), compileMatcher(selector)) } // PrevAllMatcher gets all the preceding siblings of each element in the // Selection filtered by a matcher. It returns a new Selection object // containing the matched elements. func (s *Selection) PrevAllMatcher(m Matcher) *Selection { return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevAll, nil, nil), m) } // NextUntil gets all following siblings of each element up to but not // including the element matched by the selector. It returns a new Selection // object containing the matched elements. func (s *Selection) NextUntil(selector string) *Selection { return pushStack(s, getSiblingNodes(s.Nodes, siblingNextUntil, compileMatcher(selector), nil)) } // NextUntilMatcher gets all following siblings of each element up to but not // including the element matched by the matcher. It returns a new Selection // object containing the matched elements. func (s *Selection) NextUntilMatcher(m Matcher) *Selection { return pushStack(s, getSiblingNodes(s.Nodes, siblingNextUntil, m, nil)) } // NextUntilSelection gets all following siblings of each element up to but not // including the element matched by the Selection. It returns a new Selection // object containing the matched elements. func (s *Selection) NextUntilSelection(sel *Selection) *Selection { if sel == nil { return s.NextAll() } return s.NextUntilNodes(sel.Nodes...) } // NextUntilNodes gets all following siblings of each element up to but not // including the element matched by the nodes. It returns a new Selection // object containing the matched elements. func (s *Selection) NextUntilNodes(nodes ...*html.Node) *Selection { return pushStack(s, getSiblingNodes(s.Nodes, siblingNextUntil, nil, nodes)) } // PrevUntil gets all preceding siblings of each element up to but not // including the element matched by the selector. It returns a new Selection // object containing the matched elements. func (s *Selection) PrevUntil(selector string) *Selection { return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevUntil, compileMatcher(selector), nil)) } // PrevUntilMatcher gets all preceding siblings of each element up to but not // including the element matched by the matcher. It returns a new Selection // object containing the matched elements. func (s *Selection) PrevUntilMatcher(m Matcher) *Selection { return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevUntil, m, nil)) } // PrevUntilSelection gets all preceding siblings of each element up to but not // including the element matched by the Selection. It returns a new Selection // object containing the matched elements. func (s *Selection) PrevUntilSelection(sel *Selection) *Selection { if sel == nil { return s.PrevAll() } return s.PrevUntilNodes(sel.Nodes...) } // PrevUntilNodes gets all preceding siblings of each element up to but not // including the element matched by the nodes. It returns a new Selection // object containing the matched elements. func (s *Selection) PrevUntilNodes(nodes ...*html.Node) *Selection { return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevUntil, nil, nodes)) } // NextFilteredUntil is like NextUntil, with the option to filter // the results based on a selector string. // It returns a new Selection object containing the matched elements. func (s *Selection) NextFilteredUntil(filterSelector, untilSelector string) *Selection { return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil, compileMatcher(untilSelector), nil), compileMatcher(filterSelector)) } // NextFilteredUntilMatcher is like NextUntilMatcher, with the option to filter // the results based on a matcher. // It returns a new Selection object containing the matched elements. func (s *Selection) NextFilteredUntilMatcher(filter, until Matcher) *Selection { return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil, until, nil), filter) } // NextFilteredUntilSelection is like NextUntilSelection, with the // option to filter the results based on a selector string. It returns a new // Selection object containing the matched elements. func (s *Selection) NextFilteredUntilSelection(filterSelector string, sel *Selection) *Selection { return s.NextMatcherUntilSelection(compileMatcher(filterSelector), sel) } // NextMatcherUntilSelection is like NextUntilSelection, with the // option to filter the results based on a matcher. It returns a new // Selection object containing the matched elements. func (s *Selection) NextMatcherUntilSelection(filter Matcher, sel *Selection) *Selection { if sel == nil { return s.NextMatcher(filter) } return s.NextMatcherUntilNodes(filter, sel.Nodes...) } // NextFilteredUntilNodes is like NextUntilNodes, with the // option to filter the results based on a selector string. It returns a new // Selection object containing the matched elements. func (s *Selection) NextFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection { return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil, nil, nodes), compileMatcher(filterSelector)) } // NextMatcherUntilNodes is like NextUntilNodes, with the // option to filter the results based on a matcher. It returns a new // Selection object containing the matched elements. func (s *Selection) NextMatcherUntilNodes(filter Matcher, nodes ...*html.Node) *Selection { return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil, nil, nodes), filter) } // PrevFilteredUntil is like PrevUntil, with the option to filter // the results based on a selector string. // It returns a new Selection object containing the matched elements. func (s *Selection) PrevFilteredUntil(filterSelector, untilSelector string) *Selection { return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil, compileMatcher(untilSelector), nil), compileMatcher(filterSelector)) } // PrevFilteredUntilMatcher is like PrevUntilMatcher, with the option to filter // the results based on a matcher. // It returns a new Selection object containing the matched elements. func (s *Selection) PrevFilteredUntilMatcher(filter, until Matcher) *Selection { return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil, until, nil), filter) } // PrevFilteredUntilSelection is like PrevUntilSelection, with the // option to filter the results based on a selector string. It returns a new // Selection object containing the matched elements. func (s *Selection) PrevFilteredUntilSelection(filterSelector string, sel *Selection) *Selection { return s.PrevMatcherUntilSelection(compileMatcher(filterSelector), sel) } // PrevMatcherUntilSelection is like PrevUntilSelection, with the // option to filter the results based on a matcher. It returns a new // Selection object containing the matched elements. func (s *Selection) PrevMatcherUntilSelection(filter Matcher, sel *Selection) *Selection { if sel == nil { return s.PrevMatcher(filter) } return s.PrevMatcherUntilNodes(filter, sel.Nodes...) } // PrevFilteredUntilNodes is like PrevUntilNodes, with the // option to filter the results based on a selector string. It returns a new // Selection object containing the matched elements. func (s *Selection) PrevFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection { return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil, nil, nodes), compileMatcher(filterSelector)) } // PrevMatcherUntilNodes is like PrevUntilNodes, with the // option to filter the results based on a matcher. It returns a new // Selection object containing the matched elements. func (s *Selection) PrevMatcherUntilNodes(filter Matcher, nodes ...*html.Node) *Selection { return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil, nil, nodes), filter) } // Filter and push filters the nodes based on a matcher, and pushes the results // on the stack, with the srcSel as previous selection. func filterAndPush(srcSel *Selection, nodes []*html.Node, m Matcher) *Selection { // Create a temporary Selection with the specified nodes to filter using winnow sel := &Selection{nodes, srcSel.document, nil} // Filter based on matcher and push on stack return pushStack(srcSel, winnow(sel, m, true)) } // Internal implementation of Find that return raw nodes. func findWithMatcher(nodes []*html.Node, m Matcher) []*html.Node { // Map nodes to find the matches within the children of each node return mapNodes(nodes, func(i int, n *html.Node) (result []*html.Node) { // Go down one level, becausejQuery's Find selects only within descendants for c := n.FirstChild; c != nil; c = c.NextSibling { if c.Type == html.ElementNode { result = append(result, m.MatchAll(c)...) } } return }) } // Internal implementation to get all parent nodes, stopping at the specified // node (or nil if no stop). func getParentsNodes(nodes []*html.Node, stopm Matcher, stopNodes []*html.Node) []*html.Node { return mapNodes(nodes, func(i int, n *html.Node) (result []*html.Node) { for p := n.Parent; p != nil; p = p.Parent { sel := newSingleSelection(p, nil) if stopm != nil { if sel.IsMatcher(stopm) { break } } else if len(stopNodes) > 0 { if sel.IsNodes(stopNodes...) { break } } if p.Type == html.ElementNode { result = append(result, p) } } return }) } // Internal implementation of sibling nodes that return a raw slice of matches. func getSiblingNodes(nodes []*html.Node, st siblingType, untilm Matcher, untilNodes []*html.Node) []*html.Node { var f func(*html.Node) bool // If the requested siblings are ...Until, create the test function to // determine if the until condition is reached (returns true if it is) if st == siblingNextUntil || st == siblingPrevUntil { f = func(n *html.Node) bool { if untilm != nil { // Matcher-based condition sel := newSingleSelection(n, nil) return sel.IsMatcher(untilm) } else if len(untilNodes) > 0 { // Nodes-based condition sel := newSingleSelection(n, nil) return sel.IsNodes(untilNodes...) } return false } } return mapNodes(nodes, func(i int, n *html.Node) []*html.Node { return getChildrenWithSiblingType(n.Parent, st, n, f) }) } // Gets the children nodes of each node in the specified slice of nodes, // based on the sibling type request. func getChildrenNodes(nodes []*html.Node, st siblingType) []*html.Node { return mapNodes(nodes, func(i int, n *html.Node) []*html.Node { return getChildrenWithSiblingType(n, st, nil, nil) }) } // Gets the children of the specified parent, based on the requested sibling // type, skipping a specified node if required. func getChildrenWithSiblingType(parent *html.Node, st siblingType, skipNode *html.Node, untilFunc func(*html.Node) bool) (result []*html.Node) { // Create the iterator function var iter = func(cur *html.Node) (ret *html.Node) { // Based on the sibling type requested, iterate the right way for { switch st { case siblingAll, siblingAllIncludingNonElements: if cur == nil { // First iteration, start with first child of parent // Skip node if required if ret = parent.FirstChild; ret == skipNode && skipNode != nil { ret = skipNode.NextSibling } } else { // Skip node if required if ret = cur.NextSibling; ret == skipNode && skipNode != nil { ret = skipNode.NextSibling } } case siblingPrev, siblingPrevAll, siblingPrevUntil: if cur == nil { // Start with previous sibling of the skip node ret = skipNode.PrevSibling } else { ret = cur.PrevSibling } case siblingNext, siblingNextAll, siblingNextUntil: if cur == nil { // Start with next sibling of the skip node ret = skipNode.NextSibling } else { ret = cur.NextSibling } default: panic("Invalid sibling type.") } if ret == nil || ret.Type == html.ElementNode || st == siblingAllIncludingNonElements { return } // Not a valid node, try again from this one cur = ret } } for c := iter(nil); c != nil; c = iter(c) { // If this is an ...Until case, test before append (returns true // if the until condition is reached) if st == siblingNextUntil || st == siblingPrevUntil { if untilFunc(c) { return } } result = append(result, c) if st == siblingNext || st == siblingPrev { // Only one node was requested (immediate next or previous), so exit return } } return } // Internal implementation of parent nodes that return a raw slice of Nodes. func getParentNodes(nodes []*html.Node) []*html.Node { return mapNodes(nodes, func(i int, n *html.Node) []*html.Node { if n.Parent != nil && n.Parent.Type == html.ElementNode { return []*html.Node{n.Parent} } return nil }) } // Internal map function used by many traversing methods. Takes the source nodes // to iterate on and the mapping function that returns an array of nodes. // Returns an array of nodes mapped by calling the callback function once for // each node in the source nodes. func mapNodes(nodes []*html.Node, f func(int, *html.Node) []*html.Node) (result []*html.Node) { set := make(map[*html.Node]bool) for i, n := range nodes { if vals := f(i, n); len(vals) > 0 { result = appendWithoutDuplicates(result, vals, set) } } return result } ================================================ FILE: vendor/github.com/PuerkitoBio/goquery/type.go ================================================ package goquery import ( "errors" "io" "net/http" "net/url" "github.com/andybalholm/cascadia" "golang.org/x/net/html" ) // Document represents an HTML document to be manipulated. Unlike jQuery, which // is loaded as part of a DOM document, and thus acts upon its containing // document, GoQuery doesn't know which HTML document to act upon. So it needs // to be told, and that's what the Document class is for. It holds the root // document node to manipulate, and can make selections on this document. type Document struct { *Selection Url *url.URL rootNode *html.Node } // NewDocumentFromNode is a Document constructor that takes a root html Node // as argument. func NewDocumentFromNode(root *html.Node) *Document { return newDocument(root, nil) } // NewDocument is a Document constructor that takes a string URL as argument. // It loads the specified document, parses it, and stores the root Document // node, ready to be manipulated. // // Deprecated: Use the net/http standard library package to make the request // and validate the response before calling goquery.NewDocumentFromReader // with the response's body. func NewDocument(url string) (*Document, error) { // Load the URL res, e := http.Get(url) if e != nil { return nil, e } return NewDocumentFromResponse(res) } // NewDocumentFromReader returns a Document from an io.Reader. // It returns an error as second value if the reader's data cannot be parsed // as html. It does not check if the reader is also an io.Closer, the // provided reader is never closed by this call. It is the responsibility // of the caller to close it if required. func NewDocumentFromReader(r io.Reader) (*Document, error) { root, e := html.Parse(r) if e != nil { return nil, e } return newDocument(root, nil), nil } // NewDocumentFromResponse is another Document constructor that takes an http response as argument. // It loads the specified response's document, parses it, and stores the root Document // node, ready to be manipulated. The response's body is closed on return. // // Deprecated: Use goquery.NewDocumentFromReader with the response's body. func NewDocumentFromResponse(res *http.Response) (*Document, error) { if res == nil { return nil, errors.New("Response is nil") } defer res.Body.Close() if res.Request == nil { return nil, errors.New("Response.Request is nil") } // Parse the HTML into nodes root, e := html.Parse(res.Body) if e != nil { return nil, e } // Create and fill the document return newDocument(root, res.Request.URL), nil } // CloneDocument creates a deep-clone of a document. func CloneDocument(doc *Document) *Document { return newDocument(cloneNode(doc.rootNode), doc.Url) } // Private constructor, make sure all fields are correctly filled. func newDocument(root *html.Node, url *url.URL) *Document { // Create and fill the document d := &Document{nil, url, root} d.Selection = newSingleSelection(root, d) return d } // Selection represents a collection of nodes matching some criteria. The // initial Selection can be created by using Document.Find, and then // manipulated using the jQuery-like chainable syntax and methods. type Selection struct { Nodes []*html.Node document *Document prevSel *Selection } // Helper constructor to create an empty selection func newEmptySelection(doc *Document) *Selection { return &Selection{nil, doc, nil} } // Helper constructor to create a selection of only one node func newSingleSelection(node *html.Node, doc *Document) *Selection { return &Selection{[]*html.Node{node}, doc, nil} } // Matcher is an interface that defines the methods to match // HTML nodes against a compiled selector string. Cascadia's // Selector implements this interface. type Matcher interface { Match(*html.Node) bool MatchAll(*html.Node) []*html.Node Filter([]*html.Node) []*html.Node } // Single compiles a selector string to a Matcher that stops after the first // match is found. // // By default, Selection.Find and other functions that accept a selector string // to select nodes will use all matches corresponding to that selector. By // using the Matcher returned by Single, at most the first match will be // selected. // // For example, those two statements are semantically equivalent: // // sel1 := doc.Find("a").First() // sel2 := doc.FindMatcher(goquery.Single("a")) // // The one using Single is optimized to be potentially much faster on large // documents. // // Only the behaviour of the MatchAll method of the Matcher interface is // altered compared to standard Matchers. This means that the single-selection // property of the Matcher only applies for Selection methods where the Matcher // is used to select nodes, not to filter or check if a node matches the // Matcher - in those cases, the behaviour of the Matcher is unchanged (e.g. // FilterMatcher(Single("div")) will still result in a Selection with multiple // "div"s if there were many "div"s in the Selection to begin with). func Single(selector string) Matcher { return singleMatcher{compileMatcher(selector)} } // SingleMatcher returns a Matcher matches the same nodes as m, but that stops // after the first match is found. // // See the documentation of function Single for more details. func SingleMatcher(m Matcher) Matcher { if _, ok := m.(singleMatcher); ok { // m is already a singleMatcher return m } return singleMatcher{m} } // compileMatcher compiles the selector string s and returns // the corresponding Matcher. If s is an invalid selector string, // it returns a Matcher that fails all matches. func compileMatcher(s string) Matcher { cs, err := cascadia.Compile(s) if err != nil { return invalidMatcher{} } return cs } type singleMatcher struct { Matcher } func (m singleMatcher) MatchAll(n *html.Node) []*html.Node { // Optimized version - stops finding at the first match (cascadia-compiled // matchers all use this code path). if mm, ok := m.Matcher.(interface{ MatchFirst(*html.Node) *html.Node }); ok { node := mm.MatchFirst(n) if node == nil { return nil } return []*html.Node{node} } // Fallback version, for e.g. test mocks that don't provide the MatchFirst // method. nodes := m.Matcher.MatchAll(n) if len(nodes) > 0 { return nodes[:1:1] } return nil } // invalidMatcher is a Matcher that always fails to match. type invalidMatcher struct{} func (invalidMatcher) Match(n *html.Node) bool { return false } func (invalidMatcher) MatchAll(n *html.Node) []*html.Node { return nil } func (invalidMatcher) Filter(ns []*html.Node) []*html.Node { return nil } ================================================ FILE: vendor/github.com/PuerkitoBio/goquery/utilities.go ================================================ package goquery import ( "bytes" "io" "golang.org/x/net/html" ) // used to determine if a set (map[*html.Node]bool) should be used // instead of iterating over a slice. The set uses more memory and // is slower than slice iteration for small N. const minNodesForSet = 1000 var nodeNames = []string{ html.ErrorNode: "#error", html.TextNode: "#text", html.DocumentNode: "#document", html.CommentNode: "#comment", } // NodeName returns the node name of the first element in the selection. // It tries to behave in a similar way as the DOM's nodeName property // (https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeName). // // Go's net/html package defines the following node types, listed with // the corresponding returned value from this function: // // ErrorNode : #error // TextNode : #text // DocumentNode : #document // ElementNode : the element's tag name // CommentNode : #comment // DoctypeNode : the name of the document type // func NodeName(s *Selection) string { if s.Length() == 0 { return "" } return nodeName(s.Get(0)) } // nodeName returns the node name of the given html node. // See NodeName for additional details on behaviour. func nodeName(node *html.Node) string { if node == nil { return "" } switch node.Type { case html.ElementNode, html.DoctypeNode: return node.Data default: if int(node.Type) < len(nodeNames) { return nodeNames[node.Type] } return "" } } // Render renders the HTML of the first item in the selection and writes it to // the writer. It behaves the same as OuterHtml but writes to w instead of // returning the string. func Render(w io.Writer, s *Selection) error { if s.Length() == 0 { return nil } n := s.Get(0) return html.Render(w, n) } // OuterHtml returns the outer HTML rendering of the first item in // the selection - that is, the HTML including the first element's // tag and attributes. // // Unlike Html, this is a function and not a method on the Selection, // because this is not a jQuery method (in javascript-land, this is // a property provided by the DOM). func OuterHtml(s *Selection) (string, error) { var buf bytes.Buffer if err := Render(&buf, s); err != nil { return "", err } return buf.String(), nil } // Loop through all container nodes to search for the target node. func sliceContains(container []*html.Node, contained *html.Node) bool { for _, n := range container { if nodeContains(n, contained) { return true } } return false } // Checks if the contained node is within the container node. func nodeContains(container *html.Node, contained *html.Node) bool { // Check if the parent of the contained node is the container node, traversing // upward until the top is reached, or the container is found. for contained = contained.Parent; contained != nil; contained = contained.Parent { if container == contained { return true } } return false } // Checks if the target node is in the slice of nodes. func isInSlice(slice []*html.Node, node *html.Node) bool { return indexInSlice(slice, node) > -1 } // Returns the index of the target node in the slice, or -1. func indexInSlice(slice []*html.Node, node *html.Node) int { if node != nil { for i, n := range slice { if n == node { return i } } } return -1 } // Appends the new nodes to the target slice, making sure no duplicate is added. // There is no check to the original state of the target slice, so it may still // contain duplicates. The target slice is returned because append() may create // a new underlying array. If targetSet is nil, a local set is created with the // target if len(target) + len(nodes) is greater than minNodesForSet. func appendWithoutDuplicates(target []*html.Node, nodes []*html.Node, targetSet map[*html.Node]bool) []*html.Node { // if there are not that many nodes, don't use the map, faster to just use nested loops // (unless a non-nil targetSet is passed, in which case the caller knows better). if targetSet == nil && len(target)+len(nodes) < minNodesForSet { for _, n := range nodes { if !isInSlice(target, n) { target = append(target, n) } } return target } // if a targetSet is passed, then assume it is reliable, otherwise create one // and initialize it with the current target contents. if targetSet == nil { targetSet = make(map[*html.Node]bool, len(target)) for _, n := range target { targetSet[n] = true } } for _, n := range nodes { if !targetSet[n] { target = append(target, n) targetSet[n] = true } } return target } // Loop through a selection, returning only those nodes that pass the predicate // function. func grep(sel *Selection, predicate func(i int, s *Selection) bool) (result []*html.Node) { for i, n := range sel.Nodes { if predicate(i, newSingleSelection(n, sel.document)) { result = append(result, n) } } return result } // Creates a new Selection object based on the specified nodes, and keeps the // source Selection object on the stack (linked list). func pushStack(fromSel *Selection, nodes []*html.Node) *Selection { result := &Selection{nodes, fromSel.document, fromSel} return result } ================================================ FILE: vendor/github.com/andybalholm/cascadia/.travis.yml ================================================ language: go go: - 1.3 - 1.4 install: - go get github.com/andybalholm/cascadia script: - go test -v notifications: email: false ================================================ FILE: vendor/github.com/andybalholm/cascadia/LICENSE ================================================ Copyright (c) 2011 Andy Balholm. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/andybalholm/cascadia/README.md ================================================ # cascadia [![](https://travis-ci.org/andybalholm/cascadia.svg)](https://travis-ci.org/andybalholm/cascadia) The Cascadia package implements CSS selectors for use with the parse trees produced by the html package. To test CSS selectors without writing Go code, check out [cascadia](https://github.com/suntong/cascadia) the command line tool, a thin wrapper around this package. [Refer to godoc here](https://godoc.org/github.com/andybalholm/cascadia). ## Example The following is an example of how you can use Cascadia. ```go package main import ( "fmt" "log" "strings" "github.com/andybalholm/cascadia" "golang.org/x/net/html" ) var pricingHtml string = `

Free

$0/mo

  • 10 users included
  • 2 GB of storage
  • See more

Pro

$15/mo

  • 20 users included
  • 10 GB of storage
  • See more

Enterprise

$29/mo

  • 30 users included
  • 15 GB of storage
  • See more
` func Query(n *html.Node, query string) *html.Node { sel, err := cascadia.Parse(query) if err != nil { return &html.Node{} } return cascadia.Query(n, sel) } func QueryAll(n *html.Node, query string) []*html.Node { sel, err := cascadia.Parse(query) if err != nil { return []*html.Node{} } return cascadia.QueryAll(n, sel) } func AttrOr(n *html.Node, attrName, or string) string { for _, a := range n.Attr { if a.Key == attrName { return a.Val } } return or } func main() { doc, err := html.Parse(strings.NewReader(pricingHtml)) if err != nil { log.Fatal(err) } fmt.Printf("List of pricing plans:\n\n") for i, p := range QueryAll(doc, "div.card.mb-4.box-shadow") { planName := Query(p, "h4").FirstChild.Data price := Query(p, ".pricing-card-title").FirstChild.Data usersIncluded := Query(p, "li:first-child").FirstChild.Data storage := Query(p, "li:nth-child(2)").FirstChild.Data detailsUrl := AttrOr(Query(p, "li:last-child a"), "href", "(No link available)") fmt.Printf( "Plan #%d\nName: %s\nPrice: %s\nUsers: %s\nStorage: %s\nDetails: %s\n\n", i+1, planName, price, usersIncluded, storage, detailsUrl, ) } } ``` The output is: ``` List of pricing plans: Plan #1 Name: Free Price: $0/mo Users: 10 users included Storage: 2 GB of storage Details: https://example.com Plan #2 Name: Pro Price: $15/mo Users: 20 users included Storage: 10 GB of storage Details: https://example.com Plan #3 Name: Enterprise Price: $29/mo Users: 30 users included Storage: 15 GB of storage Details: (No link available) ``` ================================================ FILE: vendor/github.com/andybalholm/cascadia/parser.go ================================================ // Package cascadia is an implementation of CSS selectors. package cascadia import ( "errors" "fmt" "regexp" "strconv" "strings" ) // a parser for CSS selectors type parser struct { s string // the source text i int // the current position // if `false`, parsing a pseudo-element // returns an error. acceptPseudoElements bool } // parseEscape parses a backslash escape. func (p *parser) parseEscape() (result string, err error) { if len(p.s) < p.i+2 || p.s[p.i] != '\\' { return "", errors.New("invalid escape sequence") } start := p.i + 1 c := p.s[start] switch { case c == '\r' || c == '\n' || c == '\f': return "", errors.New("escaped line ending outside string") case hexDigit(c): // unicode escape (hex) var i int for i = start; i < start+6 && i < len(p.s) && hexDigit(p.s[i]); i++ { // empty } v, _ := strconv.ParseUint(p.s[start:i], 16, 64) if len(p.s) > i { switch p.s[i] { case '\r': i++ if len(p.s) > i && p.s[i] == '\n' { i++ } case ' ', '\t', '\n', '\f': i++ } } p.i = i return string(rune(v)), nil } // Return the literal character after the backslash. result = p.s[start : start+1] p.i += 2 return result, nil } // toLowerASCII returns s with all ASCII capital letters lowercased. func toLowerASCII(s string) string { var b []byte for i := 0; i < len(s); i++ { if c := s[i]; 'A' <= c && c <= 'Z' { if b == nil { b = make([]byte, len(s)) copy(b, s) } b[i] = s[i] + ('a' - 'A') } } if b == nil { return s } return string(b) } func hexDigit(c byte) bool { return '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' } // nameStart returns whether c can be the first character of an identifier // (not counting an initial hyphen, or an escape sequence). func nameStart(c byte) bool { return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_' || c > 127 } // nameChar returns whether c can be a character within an identifier // (not counting an escape sequence). func nameChar(c byte) bool { return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_' || c > 127 || c == '-' || '0' <= c && c <= '9' } // parseIdentifier parses an identifier. func (p *parser) parseIdentifier() (result string, err error) { const prefix = '-' var numPrefix int for len(p.s) > p.i && p.s[p.i] == prefix { p.i++ numPrefix++ } if len(p.s) <= p.i { return "", errors.New("expected identifier, found EOF instead") } if c := p.s[p.i]; !(nameStart(c) || c == '\\') { return "", fmt.Errorf("expected identifier, found %c instead", c) } result, err = p.parseName() if numPrefix > 0 && err == nil { result = strings.Repeat(string(prefix), numPrefix) + result } return } // parseName parses a name (which is like an identifier, but doesn't have // extra restrictions on the first character). func (p *parser) parseName() (result string, err error) { i := p.i loop: for i < len(p.s) { c := p.s[i] switch { case nameChar(c): start := i for i < len(p.s) && nameChar(p.s[i]) { i++ } result += p.s[start:i] case c == '\\': p.i = i val, err := p.parseEscape() if err != nil { return "", err } i = p.i result += val default: break loop } } if result == "" { return "", errors.New("expected name, found EOF instead") } p.i = i return result, nil } // parseString parses a single- or double-quoted string. func (p *parser) parseString() (result string, err error) { i := p.i if len(p.s) < i+2 { return "", errors.New("expected string, found EOF instead") } quote := p.s[i] i++ loop: for i < len(p.s) { switch p.s[i] { case '\\': if len(p.s) > i+1 { switch c := p.s[i+1]; c { case '\r': if len(p.s) > i+2 && p.s[i+2] == '\n' { i += 3 continue loop } fallthrough case '\n', '\f': i += 2 continue loop } } p.i = i val, err := p.parseEscape() if err != nil { return "", err } i = p.i result += val case quote: break loop case '\r', '\n', '\f': return "", errors.New("unexpected end of line in string") default: start := i for i < len(p.s) { if c := p.s[i]; c == quote || c == '\\' || c == '\r' || c == '\n' || c == '\f' { break } i++ } result += p.s[start:i] } } if i >= len(p.s) { return "", errors.New("EOF in string") } // Consume the final quote. i++ p.i = i return result, nil } // parseRegex parses a regular expression; the end is defined by encountering an // unmatched closing ')' or ']' which is not consumed func (p *parser) parseRegex() (rx *regexp.Regexp, err error) { i := p.i if len(p.s) < i+2 { return nil, errors.New("expected regular expression, found EOF instead") } // number of open parens or brackets; // when it becomes negative, finished parsing regex open := 0 loop: for i < len(p.s) { switch p.s[i] { case '(', '[': open++ case ')', ']': open-- if open < 0 { break loop } } i++ } if i >= len(p.s) { return nil, errors.New("EOF in regular expression") } rx, err = regexp.Compile(p.s[p.i:i]) p.i = i return rx, err } // skipWhitespace consumes whitespace characters and comments. // It returns true if there was actually anything to skip. func (p *parser) skipWhitespace() bool { i := p.i for i < len(p.s) { switch p.s[i] { case ' ', '\t', '\r', '\n', '\f': i++ continue case '/': if strings.HasPrefix(p.s[i:], "/*") { end := strings.Index(p.s[i+len("/*"):], "*/") if end != -1 { i += end + len("/**/") continue } } } break } if i > p.i { p.i = i return true } return false } // consumeParenthesis consumes an opening parenthesis and any following // whitespace. It returns true if there was actually a parenthesis to skip. func (p *parser) consumeParenthesis() bool { if p.i < len(p.s) && p.s[p.i] == '(' { p.i++ p.skipWhitespace() return true } return false } // consumeClosingParenthesis consumes a closing parenthesis and any preceding // whitespace. It returns true if there was actually a parenthesis to skip. func (p *parser) consumeClosingParenthesis() bool { i := p.i p.skipWhitespace() if p.i < len(p.s) && p.s[p.i] == ')' { p.i++ return true } p.i = i return false } // parseTypeSelector parses a type selector (one that matches by tag name). func (p *parser) parseTypeSelector() (result tagSelector, err error) { tag, err := p.parseIdentifier() if err != nil { return } return tagSelector{tag: toLowerASCII(tag)}, nil } // parseIDSelector parses a selector that matches by id attribute. func (p *parser) parseIDSelector() (idSelector, error) { if p.i >= len(p.s) { return idSelector{}, fmt.Errorf("expected id selector (#id), found EOF instead") } if p.s[p.i] != '#' { return idSelector{}, fmt.Errorf("expected id selector (#id), found '%c' instead", p.s[p.i]) } p.i++ id, err := p.parseName() if err != nil { return idSelector{}, err } return idSelector{id: id}, nil } // parseClassSelector parses a selector that matches by class attribute. func (p *parser) parseClassSelector() (classSelector, error) { if p.i >= len(p.s) { return classSelector{}, fmt.Errorf("expected class selector (.class), found EOF instead") } if p.s[p.i] != '.' { return classSelector{}, fmt.Errorf("expected class selector (.class), found '%c' instead", p.s[p.i]) } p.i++ class, err := p.parseIdentifier() if err != nil { return classSelector{}, err } return classSelector{class: class}, nil } // parseAttributeSelector parses a selector that matches by attribute value. func (p *parser) parseAttributeSelector() (attrSelector, error) { if p.i >= len(p.s) { return attrSelector{}, fmt.Errorf("expected attribute selector ([attribute]), found EOF instead") } if p.s[p.i] != '[' { return attrSelector{}, fmt.Errorf("expected attribute selector ([attribute]), found '%c' instead", p.s[p.i]) } p.i++ p.skipWhitespace() key, err := p.parseIdentifier() if err != nil { return attrSelector{}, err } key = toLowerASCII(key) p.skipWhitespace() if p.i >= len(p.s) { return attrSelector{}, errors.New("unexpected EOF in attribute selector") } if p.s[p.i] == ']' { p.i++ return attrSelector{key: key, operation: ""}, nil } if p.i+2 >= len(p.s) { return attrSelector{}, errors.New("unexpected EOF in attribute selector") } op := p.s[p.i : p.i+2] if op[0] == '=' { op = "=" } else if op[1] != '=' { return attrSelector{}, fmt.Errorf(`expected equality operator, found "%s" instead`, op) } p.i += len(op) p.skipWhitespace() if p.i >= len(p.s) { return attrSelector{}, errors.New("unexpected EOF in attribute selector") } var val string var rx *regexp.Regexp if op == "#=" { rx, err = p.parseRegex() } else { switch p.s[p.i] { case '\'', '"': val, err = p.parseString() default: val, err = p.parseIdentifier() } } if err != nil { return attrSelector{}, err } p.skipWhitespace() if p.i >= len(p.s) { return attrSelector{}, errors.New("unexpected EOF in attribute selector") } // check if the attribute contains an ignore case flag ignoreCase := false if p.s[p.i] == 'i' || p.s[p.i] == 'I' { ignoreCase = true p.i++ } p.skipWhitespace() if p.i >= len(p.s) { return attrSelector{}, errors.New("unexpected EOF in attribute selector") } if p.s[p.i] != ']' { return attrSelector{}, fmt.Errorf("expected ']', found '%c' instead", p.s[p.i]) } p.i++ switch op { case "=", "!=", "~=", "|=", "^=", "$=", "*=", "#=": return attrSelector{key: key, val: val, operation: op, regexp: rx, insensitive: ignoreCase}, nil default: return attrSelector{}, fmt.Errorf("attribute operator %q is not supported", op) } } var ( errExpectedParenthesis = errors.New("expected '(' but didn't find it") errExpectedClosingParenthesis = errors.New("expected ')' but didn't find it") errUnmatchedParenthesis = errors.New("unmatched '('") ) // parsePseudoclassSelector parses a pseudoclass selector like :not(p) or a pseudo-element // For backwards compatibility, both ':' and '::' prefix are allowed for pseudo-elements. // https://drafts.csswg.org/selectors-3/#pseudo-elements // Returning a nil `Sel` (and a nil `error`) means we found a pseudo-element. func (p *parser) parsePseudoclassSelector() (out Sel, pseudoElement string, err error) { if p.i >= len(p.s) { return nil, "", fmt.Errorf("expected pseudoclass selector (:pseudoclass), found EOF instead") } if p.s[p.i] != ':' { return nil, "", fmt.Errorf("expected attribute selector (:pseudoclass), found '%c' instead", p.s[p.i]) } p.i++ var mustBePseudoElement bool if p.i >= len(p.s) { return nil, "", fmt.Errorf("got empty pseudoclass (or pseudoelement)") } if p.s[p.i] == ':' { // we found a pseudo-element mustBePseudoElement = true p.i++ } name, err := p.parseIdentifier() if err != nil { return } name = toLowerASCII(name) if mustBePseudoElement && (name != "after" && name != "backdrop" && name != "before" && name != "cue" && name != "first-letter" && name != "first-line" && name != "grammar-error" && name != "marker" && name != "placeholder" && name != "selection" && name != "spelling-error") { return out, "", fmt.Errorf("unknown pseudoelement :%s", name) } switch name { case "not", "has", "haschild": if !p.consumeParenthesis() { return out, "", errExpectedParenthesis } sel, parseErr := p.parseSelectorGroup() if parseErr != nil { return out, "", parseErr } if !p.consumeClosingParenthesis() { return out, "", errExpectedClosingParenthesis } out = relativePseudoClassSelector{name: name, match: sel} case "contains", "containsown": if !p.consumeParenthesis() { return out, "", errExpectedParenthesis } if p.i == len(p.s) { return out, "", errUnmatchedParenthesis } var val string switch p.s[p.i] { case '\'', '"': val, err = p.parseString() default: val, err = p.parseIdentifier() } if err != nil { return out, "", err } val = strings.ToLower(val) p.skipWhitespace() if p.i >= len(p.s) { return out, "", errors.New("unexpected EOF in pseudo selector") } if !p.consumeClosingParenthesis() { return out, "", errExpectedClosingParenthesis } out = containsPseudoClassSelector{own: name == "containsown", value: val} case "matches", "matchesown": if !p.consumeParenthesis() { return out, "", errExpectedParenthesis } rx, err := p.parseRegex() if err != nil { return out, "", err } if p.i >= len(p.s) { return out, "", errors.New("unexpected EOF in pseudo selector") } if !p.consumeClosingParenthesis() { return out, "", errExpectedClosingParenthesis } out = regexpPseudoClassSelector{own: name == "matchesown", regexp: rx} case "nth-child", "nth-last-child", "nth-of-type", "nth-last-of-type": if !p.consumeParenthesis() { return out, "", errExpectedParenthesis } a, b, err := p.parseNth() if err != nil { return out, "", err } if !p.consumeClosingParenthesis() { return out, "", errExpectedClosingParenthesis } last := name == "nth-last-child" || name == "nth-last-of-type" ofType := name == "nth-of-type" || name == "nth-last-of-type" out = nthPseudoClassSelector{a: a, b: b, last: last, ofType: ofType} case "first-child": out = nthPseudoClassSelector{a: 0, b: 1, ofType: false, last: false} case "last-child": out = nthPseudoClassSelector{a: 0, b: 1, ofType: false, last: true} case "first-of-type": out = nthPseudoClassSelector{a: 0, b: 1, ofType: true, last: false} case "last-of-type": out = nthPseudoClassSelector{a: 0, b: 1, ofType: true, last: true} case "only-child": out = onlyChildPseudoClassSelector{ofType: false} case "only-of-type": out = onlyChildPseudoClassSelector{ofType: true} case "input": out = inputPseudoClassSelector{} case "empty": out = emptyElementPseudoClassSelector{} case "root": out = rootPseudoClassSelector{} case "link": out = linkPseudoClassSelector{} case "lang": if !p.consumeParenthesis() { return out, "", errExpectedParenthesis } if p.i == len(p.s) { return out, "", errUnmatchedParenthesis } val, err := p.parseIdentifier() if err != nil { return out, "", err } val = strings.ToLower(val) p.skipWhitespace() if p.i >= len(p.s) { return out, "", errors.New("unexpected EOF in pseudo selector") } if !p.consumeClosingParenthesis() { return out, "", errExpectedClosingParenthesis } out = langPseudoClassSelector{lang: val} case "enabled": out = enabledPseudoClassSelector{} case "disabled": out = disabledPseudoClassSelector{} case "checked": out = checkedPseudoClassSelector{} case "visited", "hover", "active", "focus", "target": // Not applicable in a static context: never match. out = neverMatchSelector{value: ":" + name} case "after", "backdrop", "before", "cue", "first-letter", "first-line", "grammar-error", "marker", "placeholder", "selection", "spelling-error": return nil, name, nil default: return out, "", fmt.Errorf("unknown pseudoclass or pseudoelement :%s", name) } return } // parseInteger parses a decimal integer. func (p *parser) parseInteger() (int, error) { i := p.i start := i for i < len(p.s) && '0' <= p.s[i] && p.s[i] <= '9' { i++ } if i == start { return 0, errors.New("expected integer, but didn't find it") } p.i = i val, err := strconv.Atoi(p.s[start:i]) if err != nil { return 0, err } return val, nil } // parseNth parses the argument for :nth-child (normally of the form an+b). func (p *parser) parseNth() (a, b int, err error) { // initial state if p.i >= len(p.s) { goto eof } switch p.s[p.i] { case '-': p.i++ goto negativeA case '+': p.i++ goto positiveA case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': goto positiveA case 'n', 'N': a = 1 p.i++ goto readN case 'o', 'O', 'e', 'E': id, nameErr := p.parseName() if nameErr != nil { return 0, 0, nameErr } id = toLowerASCII(id) if id == "odd" { return 2, 1, nil } if id == "even" { return 2, 0, nil } return 0, 0, fmt.Errorf("expected 'odd' or 'even', but found '%s' instead", id) default: goto invalid } positiveA: if p.i >= len(p.s) { goto eof } switch p.s[p.i] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': a, err = p.parseInteger() if err != nil { return 0, 0, err } goto readA case 'n', 'N': a = 1 p.i++ goto readN default: goto invalid } negativeA: if p.i >= len(p.s) { goto eof } switch p.s[p.i] { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': a, err = p.parseInteger() if err != nil { return 0, 0, err } a = -a goto readA case 'n', 'N': a = -1 p.i++ goto readN default: goto invalid } readA: if p.i >= len(p.s) { goto eof } switch p.s[p.i] { case 'n', 'N': p.i++ goto readN default: // The number we read as a is actually b. return 0, a, nil } readN: p.skipWhitespace() if p.i >= len(p.s) { goto eof } switch p.s[p.i] { case '+': p.i++ p.skipWhitespace() b, err = p.parseInteger() if err != nil { return 0, 0, err } return a, b, nil case '-': p.i++ p.skipWhitespace() b, err = p.parseInteger() if err != nil { return 0, 0, err } return a, -b, nil default: return a, 0, nil } eof: return 0, 0, errors.New("unexpected EOF while attempting to parse expression of form an+b") invalid: return 0, 0, errors.New("unexpected character while attempting to parse expression of form an+b") } // parseSimpleSelectorSequence parses a selector sequence that applies to // a single element. func (p *parser) parseSimpleSelectorSequence() (Sel, error) { var selectors []Sel if p.i >= len(p.s) { return nil, errors.New("expected selector, found EOF instead") } switch p.s[p.i] { case '*': // It's the universal selector. Just skip over it, since it doesn't affect the meaning. p.i++ if p.i+2 < len(p.s) && p.s[p.i:p.i+2] == "|*" { // other version of universal selector p.i += 2 } case '#', '.', '[', ':': // There's no type selector. Wait to process the other till the main loop. default: r, err := p.parseTypeSelector() if err != nil { return nil, err } selectors = append(selectors, r) } var pseudoElement string loop: for p.i < len(p.s) { var ( ns Sel newPseudoElement string err error ) switch p.s[p.i] { case '#': ns, err = p.parseIDSelector() case '.': ns, err = p.parseClassSelector() case '[': ns, err = p.parseAttributeSelector() case ':': ns, newPseudoElement, err = p.parsePseudoclassSelector() default: break loop } if err != nil { return nil, err } // From https://drafts.csswg.org/selectors-3/#pseudo-elements : // "Only one pseudo-element may appear per selector, and if present // it must appear after the sequence of simple selectors that // represents the subjects of the selector."" if ns == nil { // we found a pseudo-element if pseudoElement != "" { return nil, fmt.Errorf("only one pseudo-element is accepted per selector, got %s and %s", pseudoElement, newPseudoElement) } if !p.acceptPseudoElements { return nil, fmt.Errorf("pseudo-element %s found, but pseudo-elements support is disabled", newPseudoElement) } pseudoElement = newPseudoElement } else { if pseudoElement != "" { return nil, fmt.Errorf("pseudo-element %s must be at the end of selector", pseudoElement) } selectors = append(selectors, ns) } } if len(selectors) == 1 && pseudoElement == "" { // no need wrap the selectors in compoundSelector return selectors[0], nil } return compoundSelector{selectors: selectors, pseudoElement: pseudoElement}, nil } // parseSelector parses a selector that may include combinators. func (p *parser) parseSelector() (Sel, error) { p.skipWhitespace() result, err := p.parseSimpleSelectorSequence() if err != nil { return nil, err } for { var ( combinator byte c Sel ) if p.skipWhitespace() { combinator = ' ' } if p.i >= len(p.s) { return result, nil } switch p.s[p.i] { case '+', '>', '~': combinator = p.s[p.i] p.i++ p.skipWhitespace() case ',', ')': // These characters can't begin a selector, but they can legally occur after one. return result, nil } if combinator == 0 { return result, nil } c, err = p.parseSimpleSelectorSequence() if err != nil { return nil, err } result = combinedSelector{first: result, combinator: combinator, second: c} } } // parseSelectorGroup parses a group of selectors, separated by commas. func (p *parser) parseSelectorGroup() (SelectorGroup, error) { current, err := p.parseSelector() if err != nil { return nil, err } result := SelectorGroup{current} for p.i < len(p.s) { if p.s[p.i] != ',' { break } p.i++ c, err := p.parseSelector() if err != nil { return nil, err } result = append(result, c) } return result, nil } ================================================ FILE: vendor/github.com/andybalholm/cascadia/pseudo_classes.go ================================================ package cascadia import ( "bytes" "fmt" "regexp" "strings" "golang.org/x/net/html" "golang.org/x/net/html/atom" ) // This file implements the pseudo classes selectors, // which share the implementation of PseudoElement() and Specificity() type abstractPseudoClass struct{} func (s abstractPseudoClass) Specificity() Specificity { return Specificity{0, 1, 0} } func (c abstractPseudoClass) PseudoElement() string { return "" } type relativePseudoClassSelector struct { name string // one of "not", "has", "haschild" match SelectorGroup } func (s relativePseudoClassSelector) Match(n *html.Node) bool { if n.Type != html.ElementNode { return false } switch s.name { case "not": // matches elements that do not match a. return !s.match.Match(n) case "has": // matches elements with any descendant that matches a. return hasDescendantMatch(n, s.match) case "haschild": // matches elements with a child that matches a. return hasChildMatch(n, s.match) default: panic(fmt.Sprintf("unsupported relative pseudo class selector : %s", s.name)) } } // hasChildMatch returns whether n has any child that matches a. func hasChildMatch(n *html.Node, a Matcher) bool { for c := n.FirstChild; c != nil; c = c.NextSibling { if a.Match(c) { return true } } return false } // hasDescendantMatch performs a depth-first search of n's descendants, // testing whether any of them match a. It returns true as soon as a match is // found, or false if no match is found. func hasDescendantMatch(n *html.Node, a Matcher) bool { for c := n.FirstChild; c != nil; c = c.NextSibling { if a.Match(c) || (c.Type == html.ElementNode && hasDescendantMatch(c, a)) { return true } } return false } // Specificity returns the specificity of the most specific selectors // in the pseudo-class arguments. // See https://www.w3.org/TR/selectors/#specificity-rules func (s relativePseudoClassSelector) Specificity() Specificity { var max Specificity for _, sel := range s.match { newSpe := sel.Specificity() if max.Less(newSpe) { max = newSpe } } return max } func (c relativePseudoClassSelector) PseudoElement() string { return "" } type containsPseudoClassSelector struct { abstractPseudoClass value string own bool } func (s containsPseudoClassSelector) Match(n *html.Node) bool { var text string if s.own { // matches nodes that directly contain the given text text = strings.ToLower(nodeOwnText(n)) } else { // matches nodes that contain the given text. text = strings.ToLower(nodeText(n)) } return strings.Contains(text, s.value) } type regexpPseudoClassSelector struct { abstractPseudoClass regexp *regexp.Regexp own bool } func (s regexpPseudoClassSelector) Match(n *html.Node) bool { var text string if s.own { // matches nodes whose text directly matches the specified regular expression text = nodeOwnText(n) } else { // matches nodes whose text matches the specified regular expression text = nodeText(n) } return s.regexp.MatchString(text) } // writeNodeText writes the text contained in n and its descendants to b. func writeNodeText(n *html.Node, b *bytes.Buffer) { switch n.Type { case html.TextNode: b.WriteString(n.Data) case html.ElementNode: for c := n.FirstChild; c != nil; c = c.NextSibling { writeNodeText(c, b) } } } // nodeText returns the text contained in n and its descendants. func nodeText(n *html.Node) string { var b bytes.Buffer writeNodeText(n, &b) return b.String() } // nodeOwnText returns the contents of the text nodes that are direct // children of n. func nodeOwnText(n *html.Node) string { var b bytes.Buffer for c := n.FirstChild; c != nil; c = c.NextSibling { if c.Type == html.TextNode { b.WriteString(c.Data) } } return b.String() } type nthPseudoClassSelector struct { abstractPseudoClass a, b int last, ofType bool } func (s nthPseudoClassSelector) Match(n *html.Node) bool { if s.a == 0 { if s.last { return simpleNthLastChildMatch(s.b, s.ofType, n) } else { return simpleNthChildMatch(s.b, s.ofType, n) } } return nthChildMatch(s.a, s.b, s.last, s.ofType, n) } // nthChildMatch implements :nth-child(an+b). // If last is true, implements :nth-last-child instead. // If ofType is true, implements :nth-of-type instead. func nthChildMatch(a, b int, last, ofType bool, n *html.Node) bool { if n.Type != html.ElementNode { return false } parent := n.Parent if parent == nil { return false } i := -1 count := 0 for c := parent.FirstChild; c != nil; c = c.NextSibling { if (c.Type != html.ElementNode) || (ofType && c.Data != n.Data) { continue } count++ if c == n { i = count if !last { break } } } if i == -1 { // This shouldn't happen, since n should always be one of its parent's children. return false } if last { i = count - i + 1 } i -= b if a == 0 { return i == 0 } return i%a == 0 && i/a >= 0 } // simpleNthChildMatch implements :nth-child(b). // If ofType is true, implements :nth-of-type instead. func simpleNthChildMatch(b int, ofType bool, n *html.Node) bool { if n.Type != html.ElementNode { return false } parent := n.Parent if parent == nil { return false } count := 0 for c := parent.FirstChild; c != nil; c = c.NextSibling { if c.Type != html.ElementNode || (ofType && c.Data != n.Data) { continue } count++ if c == n { return count == b } if count >= b { return false } } return false } // simpleNthLastChildMatch implements :nth-last-child(b). // If ofType is true, implements :nth-last-of-type instead. func simpleNthLastChildMatch(b int, ofType bool, n *html.Node) bool { if n.Type != html.ElementNode { return false } parent := n.Parent if parent == nil { return false } count := 0 for c := parent.LastChild; c != nil; c = c.PrevSibling { if c.Type != html.ElementNode || (ofType && c.Data != n.Data) { continue } count++ if c == n { return count == b } if count >= b { return false } } return false } type onlyChildPseudoClassSelector struct { abstractPseudoClass ofType bool } // Match implements :only-child. // If `ofType` is true, it implements :only-of-type instead. func (s onlyChildPseudoClassSelector) Match(n *html.Node) bool { if n.Type != html.ElementNode { return false } parent := n.Parent if parent == nil { return false } count := 0 for c := parent.FirstChild; c != nil; c = c.NextSibling { if (c.Type != html.ElementNode) || (s.ofType && c.Data != n.Data) { continue } count++ if count > 1 { return false } } return count == 1 } type inputPseudoClassSelector struct { abstractPseudoClass } // Matches input, select, textarea and button elements. func (s inputPseudoClassSelector) Match(n *html.Node) bool { return n.Type == html.ElementNode && (n.Data == "input" || n.Data == "select" || n.Data == "textarea" || n.Data == "button") } type emptyElementPseudoClassSelector struct { abstractPseudoClass } // Matches empty elements. func (s emptyElementPseudoClassSelector) Match(n *html.Node) bool { if n.Type != html.ElementNode { return false } for c := n.FirstChild; c != nil; c = c.NextSibling { switch c.Type { case html.ElementNode: return false case html.TextNode: if strings.TrimSpace(nodeText(c)) == "" { continue } else { return false } } } return true } type rootPseudoClassSelector struct { abstractPseudoClass } // Match implements :root func (s rootPseudoClassSelector) Match(n *html.Node) bool { if n.Type != html.ElementNode { return false } if n.Parent == nil { return false } return n.Parent.Type == html.DocumentNode } func hasAttr(n *html.Node, attr string) bool { return matchAttribute(n, attr, func(string) bool { return true }) } type linkPseudoClassSelector struct { abstractPseudoClass } // Match implements :link func (s linkPseudoClassSelector) Match(n *html.Node) bool { return (n.DataAtom == atom.A || n.DataAtom == atom.Area || n.DataAtom == atom.Link) && hasAttr(n, "href") } type langPseudoClassSelector struct { abstractPseudoClass lang string } func (s langPseudoClassSelector) Match(n *html.Node) bool { own := matchAttribute(n, "lang", func(val string) bool { return val == s.lang || strings.HasPrefix(val, s.lang+"-") }) if n.Parent == nil { return own } return own || s.Match(n.Parent) } type enabledPseudoClassSelector struct { abstractPseudoClass } func (s enabledPseudoClassSelector) Match(n *html.Node) bool { if n.Type != html.ElementNode { return false } switch n.DataAtom { case atom.A, atom.Area, atom.Link: return hasAttr(n, "href") case atom.Optgroup, atom.Menuitem, atom.Fieldset: return !hasAttr(n, "disabled") case atom.Button, atom.Input, atom.Select, atom.Textarea, atom.Option: return !hasAttr(n, "disabled") && !inDisabledFieldset(n) } return false } type disabledPseudoClassSelector struct { abstractPseudoClass } func (s disabledPseudoClassSelector) Match(n *html.Node) bool { if n.Type != html.ElementNode { return false } switch n.DataAtom { case atom.Optgroup, atom.Menuitem, atom.Fieldset: return hasAttr(n, "disabled") case atom.Button, atom.Input, atom.Select, atom.Textarea, atom.Option: return hasAttr(n, "disabled") || inDisabledFieldset(n) } return false } func hasLegendInPreviousSiblings(n *html.Node) bool { for s := n.PrevSibling; s != nil; s = s.PrevSibling { if s.DataAtom == atom.Legend { return true } } return false } func inDisabledFieldset(n *html.Node) bool { if n.Parent == nil { return false } if n.Parent.DataAtom == atom.Fieldset && hasAttr(n.Parent, "disabled") && (n.DataAtom != atom.Legend || hasLegendInPreviousSiblings(n)) { return true } return inDisabledFieldset(n.Parent) } type checkedPseudoClassSelector struct { abstractPseudoClass } func (s checkedPseudoClassSelector) Match(n *html.Node) bool { if n.Type != html.ElementNode { return false } switch n.DataAtom { case atom.Input, atom.Menuitem: return hasAttr(n, "checked") && matchAttribute(n, "type", func(val string) bool { t := toLowerASCII(val) return t == "checkbox" || t == "radio" }) case atom.Option: return hasAttr(n, "selected") } return false } ================================================ FILE: vendor/github.com/andybalholm/cascadia/selector.go ================================================ package cascadia import ( "fmt" "regexp" "strings" "golang.org/x/net/html" ) // Matcher is the interface for basic selector functionality. // Match returns whether a selector matches n. type Matcher interface { Match(n *html.Node) bool } // Sel is the interface for all the functionality provided by selectors. type Sel interface { Matcher Specificity() Specificity // Returns a CSS input compiling to this selector. String() string // Returns a pseudo-element, or an empty string. PseudoElement() string } // Parse parses a selector. Use `ParseWithPseudoElement` // if you need support for pseudo-elements. func Parse(sel string) (Sel, error) { p := &parser{s: sel} compiled, err := p.parseSelector() if err != nil { return nil, err } if p.i < len(sel) { return nil, fmt.Errorf("parsing %q: %d bytes left over", sel, len(sel)-p.i) } return compiled, nil } // ParseWithPseudoElement parses a single selector, // with support for pseudo-element. func ParseWithPseudoElement(sel string) (Sel, error) { p := &parser{s: sel, acceptPseudoElements: true} compiled, err := p.parseSelector() if err != nil { return nil, err } if p.i < len(sel) { return nil, fmt.Errorf("parsing %q: %d bytes left over", sel, len(sel)-p.i) } return compiled, nil } // ParseGroup parses a selector, or a group of selectors separated by commas. // Use `ParseGroupWithPseudoElements` // if you need support for pseudo-elements. func ParseGroup(sel string) (SelectorGroup, error) { p := &parser{s: sel} compiled, err := p.parseSelectorGroup() if err != nil { return nil, err } if p.i < len(sel) { return nil, fmt.Errorf("parsing %q: %d bytes left over", sel, len(sel)-p.i) } return compiled, nil } // ParseGroupWithPseudoElements parses a selector, or a group of selectors separated by commas. // It supports pseudo-elements. func ParseGroupWithPseudoElements(sel string) (SelectorGroup, error) { p := &parser{s: sel, acceptPseudoElements: true} compiled, err := p.parseSelectorGroup() if err != nil { return nil, err } if p.i < len(sel) { return nil, fmt.Errorf("parsing %q: %d bytes left over", sel, len(sel)-p.i) } return compiled, nil } // A Selector is a function which tells whether a node matches or not. // // This type is maintained for compatibility; I recommend using the newer and // more idiomatic interfaces Sel and Matcher. type Selector func(*html.Node) bool // Compile parses a selector and returns, if successful, a Selector object // that can be used to match against html.Node objects. func Compile(sel string) (Selector, error) { compiled, err := ParseGroup(sel) if err != nil { return nil, err } return Selector(compiled.Match), nil } // MustCompile is like Compile, but panics instead of returning an error. func MustCompile(sel string) Selector { compiled, err := Compile(sel) if err != nil { panic(err) } return compiled } // MatchAll returns a slice of the nodes that match the selector, // from n and its children. func (s Selector) MatchAll(n *html.Node) []*html.Node { return s.matchAllInto(n, nil) } func (s Selector) matchAllInto(n *html.Node, storage []*html.Node) []*html.Node { if s(n) { storage = append(storage, n) } for child := n.FirstChild; child != nil; child = child.NextSibling { storage = s.matchAllInto(child, storage) } return storage } func queryInto(n *html.Node, m Matcher, storage []*html.Node) []*html.Node { for child := n.FirstChild; child != nil; child = child.NextSibling { if m.Match(child) { storage = append(storage, child) } storage = queryInto(child, m, storage) } return storage } // QueryAll returns a slice of all the nodes that match m, from the descendants // of n. func QueryAll(n *html.Node, m Matcher) []*html.Node { return queryInto(n, m, nil) } // Match returns true if the node matches the selector. func (s Selector) Match(n *html.Node) bool { return s(n) } // MatchFirst returns the first node that matches s, from n and its children. func (s Selector) MatchFirst(n *html.Node) *html.Node { if s.Match(n) { return n } for c := n.FirstChild; c != nil; c = c.NextSibling { m := s.MatchFirst(c) if m != nil { return m } } return nil } // Query returns the first node that matches m, from the descendants of n. // If none matches, it returns nil. func Query(n *html.Node, m Matcher) *html.Node { for c := n.FirstChild; c != nil; c = c.NextSibling { if m.Match(c) { return c } if matched := Query(c, m); matched != nil { return matched } } return nil } // Filter returns the nodes in nodes that match the selector. func (s Selector) Filter(nodes []*html.Node) (result []*html.Node) { for _, n := range nodes { if s(n) { result = append(result, n) } } return result } // Filter returns the nodes that match m. func Filter(nodes []*html.Node, m Matcher) (result []*html.Node) { for _, n := range nodes { if m.Match(n) { result = append(result, n) } } return result } type tagSelector struct { tag string } // Matches elements with a given tag name. func (t tagSelector) Match(n *html.Node) bool { return n.Type == html.ElementNode && n.Data == t.tag } func (c tagSelector) Specificity() Specificity { return Specificity{0, 0, 1} } func (c tagSelector) PseudoElement() string { return "" } type classSelector struct { class string } // Matches elements by class attribute. func (t classSelector) Match(n *html.Node) bool { return matchAttribute(n, "class", func(s string) bool { return matchInclude(t.class, s, false) }) } func (c classSelector) Specificity() Specificity { return Specificity{0, 1, 0} } func (c classSelector) PseudoElement() string { return "" } type idSelector struct { id string } // Matches elements by id attribute. func (t idSelector) Match(n *html.Node) bool { return matchAttribute(n, "id", func(s string) bool { return s == t.id }) } func (c idSelector) Specificity() Specificity { return Specificity{1, 0, 0} } func (c idSelector) PseudoElement() string { return "" } type attrSelector struct { key, val, operation string regexp *regexp.Regexp insensitive bool } // Matches elements by attribute value. func (t attrSelector) Match(n *html.Node) bool { switch t.operation { case "": return matchAttribute(n, t.key, func(string) bool { return true }) case "=": return matchAttribute(n, t.key, func(s string) bool { return matchInsensitiveValue(s, t.val, t.insensitive) }) case "!=": return attributeNotEqualMatch(t.key, t.val, n, t.insensitive) case "~=": // matches elements where the attribute named key is a whitespace-separated list that includes val. return matchAttribute(n, t.key, func(s string) bool { return matchInclude(t.val, s, t.insensitive) }) case "|=": return attributeDashMatch(t.key, t.val, n, t.insensitive) case "^=": return attributePrefixMatch(t.key, t.val, n, t.insensitive) case "$=": return attributeSuffixMatch(t.key, t.val, n, t.insensitive) case "*=": return attributeSubstringMatch(t.key, t.val, n, t.insensitive) case "#=": return attributeRegexMatch(t.key, t.regexp, n) default: panic(fmt.Sprintf("unsuported operation : %s", t.operation)) } } // matches elements where we ignore (or not) the case of the attribute value // the user attribute is the value set by the user to match elements // the real attribute is the attribute value found in the code parsed func matchInsensitiveValue(userAttr string, realAttr string, ignoreCase bool) bool { if ignoreCase { return strings.EqualFold(userAttr, realAttr) } return userAttr == realAttr } // matches elements where the attribute named key satisifes the function f. func matchAttribute(n *html.Node, key string, f func(string) bool) bool { if n.Type != html.ElementNode { return false } for _, a := range n.Attr { if a.Key == key && f(a.Val) { return true } } return false } // attributeNotEqualMatch matches elements where // the attribute named key does not have the value val. func attributeNotEqualMatch(key, val string, n *html.Node, ignoreCase bool) bool { if n.Type != html.ElementNode { return false } for _, a := range n.Attr { if a.Key == key && matchInsensitiveValue(a.Val, val, ignoreCase) { return false } } return true } // returns true if s is a whitespace-separated list that includes val. func matchInclude(val string, s string, ignoreCase bool) bool { for s != "" { i := strings.IndexAny(s, " \t\r\n\f") if i == -1 { return matchInsensitiveValue(s, val, ignoreCase) } if matchInsensitiveValue(s[:i], val, ignoreCase) { return true } s = s[i+1:] } return false } // matches elements where the attribute named key equals val or starts with val plus a hyphen. func attributeDashMatch(key, val string, n *html.Node, ignoreCase bool) bool { return matchAttribute(n, key, func(s string) bool { if matchInsensitiveValue(s, val, ignoreCase) { return true } if len(s) <= len(val) { return false } if matchInsensitiveValue(s[:len(val)], val, ignoreCase) && s[len(val)] == '-' { return true } return false }) } // attributePrefixMatch returns a Selector that matches elements where // the attribute named key starts with val. func attributePrefixMatch(key, val string, n *html.Node, ignoreCase bool) bool { return matchAttribute(n, key, func(s string) bool { if strings.TrimSpace(s) == "" { return false } if ignoreCase { return strings.HasPrefix(strings.ToLower(s), strings.ToLower(val)) } return strings.HasPrefix(s, val) }) } // attributeSuffixMatch matches elements where // the attribute named key ends with val. func attributeSuffixMatch(key, val string, n *html.Node, ignoreCase bool) bool { return matchAttribute(n, key, func(s string) bool { if strings.TrimSpace(s) == "" { return false } if ignoreCase { return strings.HasSuffix(strings.ToLower(s), strings.ToLower(val)) } return strings.HasSuffix(s, val) }) } // attributeSubstringMatch matches nodes where // the attribute named key contains val. func attributeSubstringMatch(key, val string, n *html.Node, ignoreCase bool) bool { return matchAttribute(n, key, func(s string) bool { if strings.TrimSpace(s) == "" { return false } if ignoreCase { return strings.Contains(strings.ToLower(s), strings.ToLower(val)) } return strings.Contains(s, val) }) } // attributeRegexMatch matches nodes where // the attribute named key matches the regular expression rx func attributeRegexMatch(key string, rx *regexp.Regexp, n *html.Node) bool { return matchAttribute(n, key, func(s string) bool { return rx.MatchString(s) }) } func (c attrSelector) Specificity() Specificity { return Specificity{0, 1, 0} } func (c attrSelector) PseudoElement() string { return "" } // see pseudo_classes.go for pseudo classes selectors // on a static context, some selectors can't match anything type neverMatchSelector struct { value string } func (s neverMatchSelector) Match(n *html.Node) bool { return false } func (s neverMatchSelector) Specificity() Specificity { return Specificity{0, 0, 0} } func (c neverMatchSelector) PseudoElement() string { return "" } type compoundSelector struct { selectors []Sel pseudoElement string } // Matches elements if each sub-selectors matches. func (t compoundSelector) Match(n *html.Node) bool { if len(t.selectors) == 0 { return n.Type == html.ElementNode } for _, sel := range t.selectors { if !sel.Match(n) { return false } } return true } func (s compoundSelector) Specificity() Specificity { var out Specificity for _, sel := range s.selectors { out = out.Add(sel.Specificity()) } if s.pseudoElement != "" { // https://drafts.csswg.org/selectors-3/#specificity out = out.Add(Specificity{0, 0, 1}) } return out } func (c compoundSelector) PseudoElement() string { return c.pseudoElement } type combinedSelector struct { first Sel combinator byte second Sel } func (t combinedSelector) Match(n *html.Node) bool { if t.first == nil { return false // maybe we should panic } switch t.combinator { case 0: return t.first.Match(n) case ' ': return descendantMatch(t.first, t.second, n) case '>': return childMatch(t.first, t.second, n) case '+': return siblingMatch(t.first, t.second, true, n) case '~': return siblingMatch(t.first, t.second, false, n) default: panic("unknown combinator") } } // matches an element if it matches d and has an ancestor that matches a. func descendantMatch(a, d Matcher, n *html.Node) bool { if !d.Match(n) { return false } for p := n.Parent; p != nil; p = p.Parent { if a.Match(p) { return true } } return false } // matches an element if it matches d and its parent matches a. func childMatch(a, d Matcher, n *html.Node) bool { return d.Match(n) && n.Parent != nil && a.Match(n.Parent) } // matches an element if it matches s2 and is preceded by an element that matches s1. // If adjacent is true, the sibling must be immediately before the element. func siblingMatch(s1, s2 Matcher, adjacent bool, n *html.Node) bool { if !s2.Match(n) { return false } if adjacent { for n = n.PrevSibling; n != nil; n = n.PrevSibling { if n.Type == html.TextNode || n.Type == html.CommentNode { continue } return s1.Match(n) } return false } // Walk backwards looking for element that matches s1 for c := n.PrevSibling; c != nil; c = c.PrevSibling { if s1.Match(c) { return true } } return false } func (s combinedSelector) Specificity() Specificity { spec := s.first.Specificity() if s.second != nil { spec = spec.Add(s.second.Specificity()) } return spec } // on combinedSelector, a pseudo-element only makes sens on the last // selector, although others increase specificity. func (c combinedSelector) PseudoElement() string { if c.second == nil { return "" } return c.second.PseudoElement() } // A SelectorGroup is a list of selectors, which matches if any of the // individual selectors matches. type SelectorGroup []Sel // Match returns true if the node matches one of the single selectors. func (s SelectorGroup) Match(n *html.Node) bool { for _, sel := range s { if sel.Match(n) { return true } } return false } ================================================ FILE: vendor/github.com/andybalholm/cascadia/serialize.go ================================================ package cascadia import ( "fmt" "strconv" "strings" ) // implements the reverse operation Sel -> string var specialCharReplacer *strings.Replacer func init() { var pairs []string for _, s := range ",!\"#$%&'()*+ -./:;<=>?@[\\]^`{|}~" { pairs = append(pairs, string(s), "\\"+string(s)) } specialCharReplacer = strings.NewReplacer(pairs...) } // espace special CSS char func escape(s string) string { return specialCharReplacer.Replace(s) } func (c tagSelector) String() string { return c.tag } func (c idSelector) String() string { return "#" + escape(c.id) } func (c classSelector) String() string { return "." + escape(c.class) } func (c attrSelector) String() string { val := c.val if c.operation == "#=" { val = c.regexp.String() } else if c.operation != "" { val = fmt.Sprintf(`"%s"`, val) } ignoreCase := "" if c.insensitive { ignoreCase = " i" } return fmt.Sprintf(`[%s%s%s%s]`, c.key, c.operation, val, ignoreCase) } func (c relativePseudoClassSelector) String() string { return fmt.Sprintf(":%s(%s)", c.name, c.match.String()) } func (c containsPseudoClassSelector) String() string { s := "contains" if c.own { s += "Own" } return fmt.Sprintf(`:%s("%s")`, s, c.value) } func (c regexpPseudoClassSelector) String() string { s := "matches" if c.own { s += "Own" } return fmt.Sprintf(":%s(%s)", s, c.regexp.String()) } func (c nthPseudoClassSelector) String() string { if c.a == 0 && c.b == 1 { // special cases s := ":first-" if c.last { s = ":last-" } if c.ofType { s += "of-type" } else { s += "child" } return s } var name string switch [2]bool{c.last, c.ofType} { case [2]bool{true, true}: name = "nth-last-of-type" case [2]bool{true, false}: name = "nth-last-child" case [2]bool{false, true}: name = "nth-of-type" case [2]bool{false, false}: name = "nth-child" } s := fmt.Sprintf("+%d", c.b) if c.b < 0 { // avoid +-8 invalid syntax s = strconv.Itoa(c.b) } return fmt.Sprintf(":%s(%dn%s)", name, c.a, s) } func (c onlyChildPseudoClassSelector) String() string { if c.ofType { return ":only-of-type" } return ":only-child" } func (c inputPseudoClassSelector) String() string { return ":input" } func (c emptyElementPseudoClassSelector) String() string { return ":empty" } func (c rootPseudoClassSelector) String() string { return ":root" } func (c linkPseudoClassSelector) String() string { return ":link" } func (c langPseudoClassSelector) String() string { return fmt.Sprintf(":lang(%s)", c.lang) } func (c neverMatchSelector) String() string { return c.value } func (c enabledPseudoClassSelector) String() string { return ":enabled" } func (c disabledPseudoClassSelector) String() string { return ":disabled" } func (c checkedPseudoClassSelector) String() string { return ":checked" } func (c compoundSelector) String() string { if len(c.selectors) == 0 && c.pseudoElement == "" { return "*" } chunks := make([]string, len(c.selectors)) for i, sel := range c.selectors { chunks[i] = sel.String() } s := strings.Join(chunks, "") if c.pseudoElement != "" { s += "::" + c.pseudoElement } return s } func (c combinedSelector) String() string { start := c.first.String() if c.second != nil { start += fmt.Sprintf(" %s %s", string(c.combinator), c.second.String()) } return start } func (c SelectorGroup) String() string { ck := make([]string, len(c)) for i, s := range c { ck[i] = s.String() } return strings.Join(ck, ", ") } ================================================ FILE: vendor/github.com/andybalholm/cascadia/specificity.go ================================================ package cascadia // Specificity is the CSS specificity as defined in // https://www.w3.org/TR/selectors/#specificity-rules // with the convention Specificity = [A,B,C]. type Specificity [3]int // returns `true` if s < other (strictly), false otherwise func (s Specificity) Less(other Specificity) bool { for i := range s { if s[i] < other[i] { return true } if s[i] > other[i] { return false } } return false } func (s Specificity) Add(other Specificity) Specificity { for i, sp := range other { s[i] += sp } return s } ================================================ FILE: vendor/github.com/antchfx/htmlquery/.gitignore ================================================ # vscode .vscode debug *.test ./build # Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof ================================================ FILE: vendor/github.com/antchfx/htmlquery/.travis.yml ================================================ language: go go: - 1.9.x - 1.12.x - 1.13.x install: - go get golang.org/x/net/html/charset - go get golang.org/x/net/html - go get github.com/antchfx/xpath - go get github.com/mattn/goveralls - go get github.com/golang/groupcache script: - $HOME/gopath/bin/goveralls -service=travis-ci ================================================ FILE: vendor/github.com/antchfx/htmlquery/LICENSE ================================================ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/antchfx/htmlquery/README.md ================================================ htmlquery ==== [![Build Status](https://travis-ci.org/antchfx/htmlquery.svg?branch=master)](https://travis-ci.org/antchfx/htmlquery) [![Coverage Status](https://coveralls.io/repos/github/antchfx/htmlquery/badge.svg?branch=master)](https://coveralls.io/github/antchfx/htmlquery?branch=master) [![GoDoc](https://godoc.org/github.com/antchfx/htmlquery?status.svg)](https://godoc.org/github.com/antchfx/htmlquery) [![Go Report Card](https://goreportcard.com/badge/github.com/antchfx/htmlquery)](https://goreportcard.com/report/github.com/antchfx/htmlquery) Overview ==== `htmlquery` is an XPath query package for HTML, lets you extract data or evaluate from HTML documents by an XPath expression. `htmlquery` built-in the query object caching feature based on [LRU](https://godoc.org/github.com/golang/groupcache/lru), this feature will caching the recently used XPATH query string. Enable query caching can avoid re-compile XPath expression each query. You can visit this page to learn about the supported XPath(1.0/2.0) syntax. https://github.com/antchfx/xpath XPath query packages for Go === | Name | Description | | ------------------------------------------------- | ----------------------------------------- | | [htmlquery](https://github.com/antchfx/htmlquery) | XPath query package for the HTML document | | [xmlquery](https://github.com/antchfx/xmlquery) | XPath query package for the XML document | | [jsonquery](https://github.com/antchfx/jsonquery) | XPath query package for the JSON document | Installation ==== ``` go get github.com/antchfx/htmlquery ``` Getting Started ==== #### Query, returns matched elements or error. ```go nodes, err := htmlquery.QueryAll(doc, "//a") if err != nil { panic(`not a valid XPath expression.`) } ``` #### Load HTML document from URL. ```go doc, err := htmlquery.LoadURL("http://example.com/") ``` #### Load HTML from document. ```go filePath := "/home/user/sample.html" doc, err := htmlquery.LoadDoc(filePath) ``` #### Load HTML document from string. ```go s := `....` doc, err := htmlquery.Parse(strings.NewReader(s)) ``` #### Find all A elements. ```go list := htmlquery.Find(doc, "//a") ``` #### Find all A elements that have `href` attribute. ```go list := htmlquery.Find(doc, "//a[@href]") ``` #### Find all A elements with `href` attribute and only return `href` value. ```go list := htmlquery.Find(doc, "//a/@href") for _ , n := range list{ fmt.Println(htmlquery.SelectAttr(n, "href")) // output @href value } ``` ### Find the third A element. ```go a := htmlquery.FindOne(doc, "//a[3]") ``` ### Find children element (img) under A `href` and print the source ```go a := htmlquery.FindOne(doc, "//a") img := htmlquery.FindOne(a, "//img") fmt.Prinln(htmlquery.SelectAttr(img, "src")) // output @src value ``` #### Evaluate the number of all IMG element. ```go expr, _ := xpath.Compile("count(//img)") v := expr.Evaluate(htmlquery.CreateXPathNavigator(doc)).(float64) fmt.Printf("total count is %f", v) ``` Quick Starts === ```go func main() { doc, err := htmlquery.LoadURL("https://www.bing.com/search?q=golang") if err != nil { panic(err) } // Find all news item. list, err := htmlquery.QueryAll(doc, "//ol/li") if err != nil { panic(err) } for i, n := range list { a := htmlquery.FindOne(n, "//a") if a != nil { fmt.Printf("%d %s(%s)\n", i, htmlquery.InnerText(a), htmlquery.SelectAttr(a, "href")) } } } ``` FAQ ==== #### `Find()` vs `QueryAll()`, which is better? `Find` and `QueryAll` both do the same things, searches all of matched html nodes. The `Find` will panics if you give an error XPath query, but `QueryAll` will return an error for you. #### Can I save my query expression object for the next query? Yes, you can. We offer the `QuerySelector` and `QuerySelectorAll` methods, It will accept your query expression object. Cache a query expression object(or reused) will avoid re-compile XPath query expression, improve your query performance. #### XPath query object cache performance ``` goos: windows goarch: amd64 pkg: github.com/antchfx/htmlquery BenchmarkSelectorCache-4 20000000 55.2 ns/op BenchmarkDisableSelectorCache-4 500000 3162 ns/op ``` #### How to disable caching? ``` htmlquery.DisableSelectorCache = true ``` Questions === Please let me know if you have any questions. ================================================ FILE: vendor/github.com/antchfx/htmlquery/cache.go ================================================ package htmlquery import ( "sync" "github.com/antchfx/xpath" "github.com/golang/groupcache/lru" ) // DisableSelectorCache will disable caching for the query selector if value is true. var DisableSelectorCache = false // SelectorCacheMaxEntries allows how many selector object can be caching. Default is 50. // Will disable caching if SelectorCacheMaxEntries <= 0. var SelectorCacheMaxEntries = 50 var ( cacheOnce sync.Once cache *lru.Cache cacheMutex sync.Mutex ) func getQuery(expr string) (*xpath.Expr, error) { if DisableSelectorCache || SelectorCacheMaxEntries <= 0 { return xpath.Compile(expr) } cacheOnce.Do(func() { cache = lru.New(SelectorCacheMaxEntries) }) cacheMutex.Lock() defer cacheMutex.Unlock() if v, ok := cache.Get(expr); ok { return v.(*xpath.Expr), nil } v, err := xpath.Compile(expr) if err != nil { return nil, err } cache.Add(expr, v) return v, nil } ================================================ FILE: vendor/github.com/antchfx/htmlquery/query.go ================================================ /* Package htmlquery provides extract data from HTML documents using XPath expression. */ package htmlquery import ( "bufio" "fmt" "io" "net/http" "os" "strings" "github.com/antchfx/xpath" "golang.org/x/net/html" "golang.org/x/net/html/charset" ) var _ xpath.NodeNavigator = &NodeNavigator{} // CreateXPathNavigator creates a new xpath.NodeNavigator for the specified html.Node. func CreateXPathNavigator(top *html.Node) *NodeNavigator { return &NodeNavigator{curr: top, root: top, attr: -1} } // Find is like QueryAll but Will panics if the expression `expr` cannot be parsed. // // See `QueryAll()` function. func Find(top *html.Node, expr string) []*html.Node { nodes, err := QueryAll(top, expr) if err != nil { panic(err) } return nodes } // FindOne is like Query but will panics if the expression `expr` cannot be parsed. // See `Query()` function. func FindOne(top *html.Node, expr string) *html.Node { node, err := Query(top, expr) if err != nil { panic(err) } return node } // QueryAll searches the html.Node that matches by the specified XPath expr. // Return an error if the expression `expr` cannot be parsed. func QueryAll(top *html.Node, expr string) ([]*html.Node, error) { exp, err := getQuery(expr) if err != nil { return nil, err } nodes := QuerySelectorAll(top, exp) return nodes, nil } // Query runs the given XPath expression against the given html.Node and // returns the first matching html.Node, or nil if no matches are found. // // Returns an error if the expression `expr` cannot be parsed. func Query(top *html.Node, expr string) (*html.Node, error) { exp, err := getQuery(expr) if err != nil { return nil, err } return QuerySelector(top, exp), nil } // QuerySelector returns the first matched html.Node by the specified XPath selector. func QuerySelector(top *html.Node, selector *xpath.Expr) *html.Node { t := selector.Select(CreateXPathNavigator(top)) if t.MoveNext() { return getCurrentNode(t.Current().(*NodeNavigator)) } return nil } // QuerySelectorAll searches all of the html.Node that matches the specified XPath selectors. func QuerySelectorAll(top *html.Node, selector *xpath.Expr) []*html.Node { var elems []*html.Node t := selector.Select(CreateXPathNavigator(top)) for t.MoveNext() { nav := t.Current().(*NodeNavigator) n := getCurrentNode(nav) elems = append(elems, n) } return elems } // LoadURL loads the HTML document from the specified URL. func LoadURL(url string) (*html.Node, error) { resp, err := http.Get(url) if err != nil { return nil, err } defer resp.Body.Close() r, err := charset.NewReader(resp.Body, resp.Header.Get("Content-Type")) if err != nil { return nil, err } return html.Parse(r) } // LoadDoc loads the HTML document from the specified file path. func LoadDoc(path string) (*html.Node, error) { f, err := os.Open(path) if err != nil { return nil, err } defer f.Close() return html.Parse(bufio.NewReader(f)) } func getCurrentNode(n *NodeNavigator) *html.Node { if n.NodeType() == xpath.AttributeNode { childNode := &html.Node{ Type: html.TextNode, Data: n.Value(), } return &html.Node{ Type: html.ElementNode, Data: n.LocalName(), FirstChild: childNode, LastChild: childNode, } } return n.curr } // Parse returns the parse tree for the HTML from the given Reader. func Parse(r io.Reader) (*html.Node, error) { return html.Parse(r) } // InnerText returns the text between the start and end tags of the object. func InnerText(n *html.Node) string { var output func(*strings.Builder, *html.Node) output = func(b *strings.Builder, n *html.Node) { switch n.Type { case html.TextNode: b.WriteString(n.Data) return case html.CommentNode: return } for child := n.FirstChild; child != nil; child = child.NextSibling { output(b, child) } } var b strings.Builder output(&b, n) return b.String() } // SelectAttr returns the attribute value with the specified name. func SelectAttr(n *html.Node, name string) (val string) { if n == nil { return } if n.Type == html.ElementNode && n.Parent == nil && name == n.Data { return InnerText(n) } for _, attr := range n.Attr { if attr.Key == name { val = attr.Val break } } return } // ExistsAttr returns whether attribute with specified name exists. func ExistsAttr(n *html.Node, name string) bool { if n == nil { return false } for _, attr := range n.Attr { if attr.Key == name { return true } } return false } // OutputHTML returns the text including tags name. func OutputHTML(n *html.Node, self bool) string { var b strings.Builder if self { html.Render(&b, n) } else { for n := n.FirstChild; n != nil; n = n.NextSibling { html.Render(&b, n) } } return b.String() } type NodeNavigator struct { root, curr *html.Node attr int } func (h *NodeNavigator) Current() *html.Node { return h.curr } func (h *NodeNavigator) NodeType() xpath.NodeType { switch h.curr.Type { case html.CommentNode: return xpath.CommentNode case html.TextNode: return xpath.TextNode case html.DocumentNode: return xpath.RootNode case html.ElementNode: if h.attr != -1 { return xpath.AttributeNode } return xpath.ElementNode case html.DoctypeNode: // ignored declare and as Root-Node type. return xpath.RootNode } panic(fmt.Sprintf("unknown HTML node type: %v", h.curr.Type)) } func (h *NodeNavigator) LocalName() string { if h.attr != -1 { return h.curr.Attr[h.attr].Key } return h.curr.Data } func (*NodeNavigator) Prefix() string { return "" } func (h *NodeNavigator) Value() string { switch h.curr.Type { case html.CommentNode: return h.curr.Data case html.ElementNode: if h.attr != -1 { return h.curr.Attr[h.attr].Val } return InnerText(h.curr) case html.TextNode: return h.curr.Data } return "" } func (h *NodeNavigator) Copy() xpath.NodeNavigator { n := *h return &n } func (h *NodeNavigator) MoveToRoot() { h.curr = h.root } func (h *NodeNavigator) MoveToParent() bool { if h.attr != -1 { h.attr = -1 return true } else if node := h.curr.Parent; node != nil { h.curr = node return true } return false } func (h *NodeNavigator) MoveToNextAttribute() bool { if h.attr >= len(h.curr.Attr)-1 { return false } h.attr++ return true } func (h *NodeNavigator) MoveToChild() bool { if h.attr != -1 { return false } if node := h.curr.FirstChild; node != nil { h.curr = node return true } return false } func (h *NodeNavigator) MoveToFirst() bool { if h.attr != -1 || h.curr.PrevSibling == nil { return false } for { node := h.curr.PrevSibling if node == nil { break } h.curr = node } return true } func (h *NodeNavigator) String() string { return h.Value() } func (h *NodeNavigator) MoveToNext() bool { if h.attr != -1 { return false } if node := h.curr.NextSibling; node != nil { h.curr = node return true } return false } func (h *NodeNavigator) MoveToPrevious() bool { if h.attr != -1 { return false } if node := h.curr.PrevSibling; node != nil { h.curr = node return true } return false } func (h *NodeNavigator) MoveTo(other xpath.NodeNavigator) bool { node, ok := other.(*NodeNavigator) if !ok || node.root != h.root { return false } h.curr = node.curr h.attr = node.attr return true } ================================================ FILE: vendor/github.com/antchfx/xmlquery/.gitignore ================================================ # vscode .vscode debug *.test ./build # Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof ================================================ FILE: vendor/github.com/antchfx/xmlquery/.travis.yml ================================================ language: go go: - 1.9.x - 1.12.x - 1.13.x - 1.14.x - 1.15.x install: - go get golang.org/x/net/html/charset - go get github.com/antchfx/xpath - go get github.com/mattn/goveralls - go get github.com/golang/groupcache script: - $HOME/gopath/bin/goveralls -service=travis-ci ================================================ FILE: vendor/github.com/antchfx/xmlquery/LICENSE ================================================ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/antchfx/xmlquery/README.md ================================================ xmlquery ==== [![Build Status](https://travis-ci.org/antchfx/xmlquery.svg?branch=master)](https://travis-ci.org/antchfx/xmlquery) [![Coverage Status](https://coveralls.io/repos/github/antchfx/xmlquery/badge.svg?branch=master)](https://coveralls.io/github/antchfx/xmlquery?branch=master) [![GoDoc](https://godoc.org/github.com/antchfx/xmlquery?status.svg)](https://godoc.org/github.com/antchfx/xmlquery) [![Go Report Card](https://goreportcard.com/badge/github.com/antchfx/xmlquery)](https://goreportcard.com/report/github.com/antchfx/xmlquery) Overview === `xmlquery` is an XPath query package for XML documents, allowing you to extract data or evaluate from XML documents with an XPath expression. `xmlquery` has a built-in query object caching feature that caches recently used XPATH query strings. Enabling caching can avoid recompile XPath expression for each query. You can visit this page to learn about the supported XPath(1.0/2.0) syntax. https://github.com/antchfx/xpath [htmlquery](https://github.com/antchfx/htmlquery) - Package for the HTML document query. [xmlquery](https://github.com/antchfx/xmlquery) - Package for the XML document query. [jsonquery](https://github.com/antchfx/jsonquery) - Package for the JSON document query. Installation ==== ``` $ go get github.com/antchfx/xmlquery ``` Quick Starts === ```go import ( "github.com/antchfx/xmlquery" ) func main(){ s := ` W3Schools Home Page https://www.w3schools.com Free web building tutorials RSS Tutorial https://www.w3schools.com/xml/xml_rss.asp New RSS tutorial on W3Schools XML Tutorial https://www.w3schools.com/xml New XML tutorial on W3Schools ` doc, err := xmlquery.Parse(strings.NewReader(s)) if err != nil { panic(err) } channel := xmlquery.FindOne(doc, "//channel") if n := channel.SelectElement("title"); n != nil { fmt.Printf("title: %s\n", n.InnerText()) } if n := channel.SelectElement("link"); n != nil { fmt.Printf("link: %s\n", n.InnerText()) } for i, n := range xmlquery.Find(doc, "//item/title") { fmt.Printf("#%d %s\n", i, n.InnerText()) } } ``` Getting Started === ### Find specified XPath query. ```go list, err := xmlquery.QueryAll(doc, "a") if err != nil { panic(err) } ``` #### Parse an XML from URL. ```go doc, err := xmlquery.LoadURL("http://www.example.com/sitemap.xml") ``` #### Parse an XML from string. ```go s := `` doc, err := xmlquery.Parse(strings.NewReader(s)) ``` #### Parse an XML from io.Reader. ```go f, err := os.Open("../books.xml") doc, err := xmlquery.Parse(f) ``` #### Parse an XML in a stream fashion (simple case without elements filtering). ```go f, _ := os.Open("../books.xml") p, err := xmlquery.CreateStreamParser(f, "/bookstore/book") for { n, err := p.Read() if err == io.EOF { break } if err != nil { panic(err) } fmt.Println(n) } ``` Notes: `CreateStreamParser()` used for saving memory if your had a large XML file to parse. #### Parse an XML in a stream fashion (simple case advanced element filtering). ```go f, _ := os.Open("../books.xml") p, err := xmlquery.CreateStreamParser(f, "/bookstore/book", "/bookstore/book[price>=10]") for { n, err := p.Read() if err == io.EOF { break } if err != nil { panic(err) } fmt.Println(n) } ``` #### Find authors of all books in the bookstore. ```go list := xmlquery.Find(doc, "//book//author") // or list := xmlquery.Find(doc, "//author") ``` #### Find the second book. ```go book := xmlquery.FindOne(doc, "//book[2]") ``` #### Find all book elements and only get `id` attribute. (New Feature) ```go list := xmlquery.Find(doc,"//book/@id") ``` #### Find all books with id `bk104`. ```go list := xmlquery.Find(doc, "//book[@id='bk104']") ``` #### Find all books with price less than 5. ```go list := xmlquery.Find(doc, "//book[price<5]") ``` #### Evaluate total price of all books. ```go expr, err := xpath.Compile("sum(//book/price)") price := expr.Evaluate(xmlquery.CreateXPathNavigator(doc)).(float64) fmt.Printf("total price: %f\n", price) ``` #### Evaluate number of all book elements. ```go expr, err := xpath.Compile("count(//book)") price := expr.Evaluate(xmlquery.CreateXPathNavigator(doc)).(float64) ``` Advanced Features ==== ### Parse `UTF-16` XML file with `ParseWithOptions()`. ```go f, _ := os.Open(`UTF-16.XML`) // Convert UTF-16 XML to UTF-8 utf16ToUtf8Transformer := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewDecoder() utf8Reader := transform.NewReader(f, utf16ToUtf8Transformer) // Sets `CharsetReader` options := xmlquery.ParserOptions{ Decoder: &xmlquery.DecoderOptions{ CharsetReader: func(charset string, input io.Reader) (io.Reader, error) { return input, nil }, }, } doc, err := xmlquery.ParseWithOptions(utf8Reader, options) ``` ### Query with custom namespace prefix. ```go s := ` RequestReplyActivity OpClientReqActivity 300 80 ` nsMap := map[string]string{ "q": "http://xmlns.xyz.com/process/2003", "r": "http://www.w3.org/1999/XSL/Transform", "s": "http://www.w3.org/2001/XMLSchema", } expr, _ := xpath.CompileWithNS("//q:activity", nsMap) node := xmlquery.QuerySelector(doc, expr) ``` #### Create XML document without call `xml.Marshal`. ```go doc := &xmlquery.Node{ Type: xmlquery.DeclarationNode, Data: "xml", Attr: []xml.Attr{ xml.Attr{Name: xml.Name{Local: "version"}, Value: "1.0"}, }, } root := &xmlquery.Node{ Data: "rss", Type: xmlquery.ElementNode, } doc.FirstChild = root channel := &xmlquery.Node{ Data: "channel", Type: xmlquery.ElementNode, } root.FirstChild = channel title := &xmlquery.Node{ Data: "title", Type: xmlquery.ElementNode, } title_text := &xmlquery.Node{ Data: "W3Schools Home Page", Type: xmlquery.TextNode, } title.FirstChild = title_text channel.FirstChild = title fmt.Println(doc.OutputXML(true)) fmt.Println(doc.OutputXMLWithOptions(WithOutputSelf())) ``` Output: ```xml W3Schools Home Page ``` FAQ ==== #### `Find()` vs `QueryAll()`, which is better? `Find` and `QueryAll` both do the same thing: searches all of matched XML nodes. `Find` panics if provided with an invalid XPath query, while `QueryAll` returns an error. #### Can I save my query expression object for the next query? Yes, you can. We provide `QuerySelector` and `QuerySelectorAll` methods; they accept your query expression object. Caching a query expression object avoids recompiling the XPath query expression, improving query performance. Questions === Please let me know if you have any questions ================================================ FILE: vendor/github.com/antchfx/xmlquery/books.xml ================================================ Gambardella, Matthew XML Developer's Guide Computer 44.95 2000-10-01 An in-depth look at creating applications with XML. Ralls, Kim Midnight Rain Fantasy 5.95 2000-12-16 A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world. Corets, Eva Maeve Ascendant Fantasy 5.95 2000-11-17 After the collapse of a nanotechnology society in England, the young survivors lay the foundation for a new society. Corets, Eva Oberon's Legacy Fantasy 5.95 2001-03-10 In post-apocalypse England, the mysterious agent known only as Oberon helps to create a new life for the inhabitants of London. Sequel to Maeve Ascendant. Corets, Eva The Sundered Grail Fantasy 5.95 2001-09-10 The two daughters of Maeve, half-sisters, battle one another for control of England. Sequel to Oberon's Legacy. Randall, Cynthia Lover Birds Romance 4.95 2000-09-02 When Carla meets Paul at an ornithology conference, tempers fly as feathers get ruffled. Thurman, Paula Splish Splash Romance 4.95 2000-11-02 A deep sea diver finds true love twenty thousand leagues beneath the sea. Knorr, Stefan Creepy Crawlies Horror 4.95 2000-12-06 An anthology of horror stories about roaches, centipedes, scorpions and other insects. Kress, Peter Paradox Lost Science Fiction 6.95 2000-11-02 After an inadvertant trip through a Heisenberg Uncertainty Device, James Salway discovers the problems of being quantum. O'Brien, Tim Microsoft .NET: The Programming Bible Computer 36.95 2000-12-09 Microsoft's .NET initiative is explored in detail in this deep programmer's reference. O'Brien, Tim MSXML3: A Comprehensive Guide Computer 36.95 2000-12-01 The Microsoft MSXML3 parser is covered in detail, with attention to XML DOM interfaces, XSLT processing, SAX and more. Galos, Mike Visual Studio 7: A Comprehensive Guide Computer 49.95 2001-04-16 Microsoft Visual Studio 7 is explored in depth, looking at how Visual Basic, Visual C++, C#, and ASP+ are integrated into a comprehensive development environment. ================================================ FILE: vendor/github.com/antchfx/xmlquery/cache.go ================================================ package xmlquery import ( "sync" "github.com/golang/groupcache/lru" "github.com/antchfx/xpath" ) // DisableSelectorCache will disable caching for the query selector if value is true. var DisableSelectorCache = false // SelectorCacheMaxEntries allows how many selector object can be caching. Default is 50. // Will disable caching if SelectorCacheMaxEntries <= 0. var SelectorCacheMaxEntries = 50 var ( cacheOnce sync.Once cache *lru.Cache cacheMutex sync.Mutex ) func getQuery(expr string) (*xpath.Expr, error) { if DisableSelectorCache || SelectorCacheMaxEntries <= 0 { return xpath.Compile(expr) } cacheOnce.Do(func() { cache = lru.New(SelectorCacheMaxEntries) }) cacheMutex.Lock() defer cacheMutex.Unlock() if v, ok := cache.Get(expr); ok { return v.(*xpath.Expr), nil } v, err := xpath.Compile(expr) if err != nil { return nil, err } cache.Add(expr, v) return v, nil } ================================================ FILE: vendor/github.com/antchfx/xmlquery/cached_reader.go ================================================ package xmlquery import ( "bufio" ) type cachedReader struct { buffer *bufio.Reader cache []byte cacheCap int cacheLen int caching bool } func newCachedReader(r *bufio.Reader) *cachedReader { return &cachedReader{ buffer: r, cache: make([]byte, 4096), cacheCap: 4096, cacheLen: 0, caching: false, } } func (c *cachedReader) StartCaching() { c.cacheLen = 0 c.caching = true } func (c *cachedReader) ReadByte() (byte, error) { if !c.caching { return c.buffer.ReadByte() } b, err := c.buffer.ReadByte() if err != nil { return b, err } if c.cacheLen < c.cacheCap { c.cache[c.cacheLen] = b c.cacheLen++ } return b, err } func (c *cachedReader) Cache() []byte { return c.cache[:c.cacheLen] } func (c *cachedReader) StopCaching() { c.caching = false } func (c *cachedReader) Read(p []byte) (int, error) { n, err := c.buffer.Read(p) if err != nil { return n, err } if c.caching && c.cacheLen < c.cacheCap { for i := 0; i < n; i++ { c.cache[c.cacheLen] = p[i] c.cacheLen++ if c.cacheLen >= c.cacheCap { break } } } return n, err } ================================================ FILE: vendor/github.com/antchfx/xmlquery/node.go ================================================ package xmlquery import ( "encoding/xml" "fmt" "html" "strings" ) // A NodeType is the type of a Node. type NodeType uint const ( // DocumentNode is a document object that, as the root of the document tree, // provides access to the entire XML document. DocumentNode NodeType = iota // DeclarationNode is the document type declaration, indicated by the // following tag (for example, ). DeclarationNode // ElementNode is an element (for example, ). ElementNode // TextNode is the text content of a node. TextNode // CharDataNode node CharDataNode // CommentNode a comment (for example, ). CommentNode // AttributeNode is an attribute of element. AttributeNode ) type Attr struct { Name xml.Name Value string NamespaceURI string } // A Node consists of a NodeType and some Data (tag name for // element nodes, content for text) and are part of a tree of Nodes. type Node struct { Parent, FirstChild, LastChild, PrevSibling, NextSibling *Node Type NodeType Data string Prefix string NamespaceURI string Attr []Attr level int // node level in the tree } type outputConfiguration struct { printSelf bool preserveSpaces bool emptyElementTagSupport bool skipComments bool } type OutputOption func(*outputConfiguration) // WithOutputSelf configures the Node to print the root node itself func WithOutputSelf() OutputOption { return func(oc *outputConfiguration) { oc.printSelf = true } } // WithEmptyTagSupport empty tags should be written as and // not as func WithEmptyTagSupport() OutputOption { return func(oc *outputConfiguration) { oc.emptyElementTagSupport = true } } // WithoutComments will skip comments in output func WithoutComments() OutputOption { return func(oc *outputConfiguration) { oc.skipComments = true } } func newXMLName(name string) xml.Name { if i := strings.IndexByte(name, ':'); i > 0 { return xml.Name{ Space: name[:i], Local: name[i+1:], } } return xml.Name{ Local: name, } } // InnerText returns the text between the start and end tags of the object. func (n *Node) InnerText() string { var output func(*strings.Builder, *Node) output = func(b *strings.Builder, n *Node) { switch n.Type { case TextNode, CharDataNode: b.WriteString(n.Data) case CommentNode: default: for child := n.FirstChild; child != nil; child = child.NextSibling { output(b, child) } } } var b strings.Builder output(&b, n) return b.String() } func (n *Node) sanitizedData(preserveSpaces bool) string { if preserveSpaces { return n.Data } return strings.TrimSpace(n.Data) } func calculatePreserveSpaces(n *Node, pastValue bool) bool { if attr := n.SelectAttr("xml:space"); attr == "preserve" { return true } else if attr == "default" { return false } return pastValue } func outputXML(b *strings.Builder, n *Node, preserveSpaces bool, config *outputConfiguration) { preserveSpaces = calculatePreserveSpaces(n, preserveSpaces) switch n.Type { case TextNode: b.WriteString(html.EscapeString(n.sanitizedData(preserveSpaces))) return case CharDataNode: b.WriteString("") return case CommentNode: if !config.skipComments { b.WriteString("") } return case DeclarationNode: b.WriteString("") } else { if n.FirstChild != nil || !config.emptyElementTagSupport { b.WriteString(">") } else { b.WriteString("/>") return } } for child := n.FirstChild; child != nil; child = child.NextSibling { outputXML(b, child, preserveSpaces, config) } if n.Type != DeclarationNode { if n.Prefix == "" { fmt.Fprintf(b, "", n.Data) } else { fmt.Fprintf(b, "", n.Prefix, n.Data) } } } // OutputXML returns the text that including tags name. func (n *Node) OutputXML(self bool) string { config := &outputConfiguration{ printSelf: true, emptyElementTagSupport: false, } preserveSpaces := calculatePreserveSpaces(n, false) var b strings.Builder if self && n.Type != DocumentNode { outputXML(&b, n, preserveSpaces, config) } else { for n := n.FirstChild; n != nil; n = n.NextSibling { outputXML(&b, n, preserveSpaces, config) } } return b.String() } // OutputXMLWithOptions returns the text that including tags name. func (n *Node) OutputXMLWithOptions(opts ...OutputOption) string { config := &outputConfiguration{} // Set the options for _, opt := range opts { opt(config) } preserveSpaces := calculatePreserveSpaces(n, false) var b strings.Builder if config.printSelf && n.Type != DocumentNode { outputXML(&b, n, preserveSpaces, config) } else { for n := n.FirstChild; n != nil; n = n.NextSibling { outputXML(&b, n, preserveSpaces, config) } } return b.String() } // AddAttr adds a new attribute specified by 'key' and 'val' to a node 'n'. func AddAttr(n *Node, key, val string) { attr := Attr{ Name: newXMLName(key), Value: val, } n.Attr = append(n.Attr, attr) } // SetAttr allows an attribute value with the specified name to be changed. // If the attribute did not previously exist, it will be created. func (n *Node) SetAttr(key, value string) { name := newXMLName(key) for i, attr := range n.Attr { if attr.Name == name { n.Attr[i].Value = value return } } AddAttr(n, key, value) } // RemoveAttr removes the attribute with the specified name. func (n *Node) RemoveAttr(key string) { name := newXMLName(key) for i, attr := range n.Attr { if attr.Name == name { n.Attr = append(n.Attr[:i], n.Attr[i+1:]...) return } } } // AddChild adds a new node 'n' to a node 'parent' as its last child. func AddChild(parent, n *Node) { n.Parent = parent n.NextSibling = nil if parent.FirstChild == nil { parent.FirstChild = n n.PrevSibling = nil } else { parent.LastChild.NextSibling = n n.PrevSibling = parent.LastChild } parent.LastChild = n } // AddSibling adds a new node 'n' as a sibling of a given node 'sibling'. // Note it is not necessarily true that the new node 'n' would be added // immediately after 'sibling'. If 'sibling' isn't the last child of its // parent, then the new node 'n' will be added at the end of the sibling // chain of their parent. func AddSibling(sibling, n *Node) { for t := sibling.NextSibling; t != nil; t = t.NextSibling { sibling = t } n.Parent = sibling.Parent sibling.NextSibling = n n.PrevSibling = sibling n.NextSibling = nil if sibling.Parent != nil { sibling.Parent.LastChild = n } } // RemoveFromTree removes a node and its subtree from the document // tree it is in. If the node is the root of the tree, then it's no-op. func RemoveFromTree(n *Node) { if n.Parent == nil { return } if n.Parent.FirstChild == n { if n.Parent.LastChild == n { n.Parent.FirstChild = nil n.Parent.LastChild = nil } else { n.Parent.FirstChild = n.NextSibling n.NextSibling.PrevSibling = nil } } else { if n.Parent.LastChild == n { n.Parent.LastChild = n.PrevSibling n.PrevSibling.NextSibling = nil } else { n.PrevSibling.NextSibling = n.NextSibling n.NextSibling.PrevSibling = n.PrevSibling } } n.Parent = nil n.PrevSibling = nil n.NextSibling = nil } ================================================ FILE: vendor/github.com/antchfx/xmlquery/options.go ================================================ package xmlquery import ( "encoding/xml" "io" ) type ParserOptions struct { Decoder *DecoderOptions } func (options ParserOptions) apply(parser *parser) { if options.Decoder != nil { (*options.Decoder).apply(parser.decoder) } } // DecoderOptions implement the very same options than the standard // encoding/xml package. Please refer to this documentation: // https://golang.org/pkg/encoding/xml/#Decoder type DecoderOptions struct { Strict bool AutoClose []string Entity map[string]string CharsetReader func(charset string, input io.Reader) (io.Reader, error) } func (options DecoderOptions) apply(decoder *xml.Decoder) { decoder.Strict = options.Strict decoder.AutoClose = options.AutoClose decoder.Entity = options.Entity decoder.CharsetReader = options.CharsetReader } ================================================ FILE: vendor/github.com/antchfx/xmlquery/parse.go ================================================ package xmlquery import ( "bufio" "encoding/xml" "fmt" "io" "net/http" "regexp" "strings" "github.com/antchfx/xpath" "golang.org/x/net/html/charset" ) var xmlMIMERegex = regexp.MustCompile(`(?i)((application|image|message|model)/((\w|\.|-)+\+?)?|text/)(wb)?xml`) // LoadURL loads the XML document from the specified URL. func LoadURL(url string) (*Node, error) { resp, err := http.Get(url) if err != nil { return nil, err } defer resp.Body.Close() // Make sure the Content-Type has a valid XML MIME type if xmlMIMERegex.MatchString(resp.Header.Get("Content-Type")) { return Parse(resp.Body) } return nil, fmt.Errorf("invalid XML document(%s)", resp.Header.Get("Content-Type")) } // Parse returns the parse tree for the XML from the given Reader. func Parse(r io.Reader) (*Node, error) { return ParseWithOptions(r, ParserOptions{}) } // ParseWithOptions is like parse, but with custom options func ParseWithOptions(r io.Reader, options ParserOptions) (*Node, error) { p := createParser(r) options.apply(p) for { _, err := p.parse() if err == io.EOF { return p.doc, nil } if err != nil { return nil, err } } } type parser struct { decoder *xml.Decoder doc *Node level int prev *Node streamElementXPath *xpath.Expr // Under streaming mode, this specifies the xpath to the target element node(s). streamElementFilter *xpath.Expr // If specified, it provides further filtering on the target element. streamNode *Node // Need to remember the last target node So we can clean it up upon next Read() call. streamNodePrev *Node // Need to remember target node's prev so upon target node removal, we can restore correct prev. reader *cachedReader // Need to maintain a reference to the reader, so we can determine whether a node contains CDATA. } func createParser(r io.Reader) *parser { reader := newCachedReader(bufio.NewReader(r)) p := &parser{ decoder: xml.NewDecoder(reader), doc: &Node{Type: DocumentNode}, level: 0, reader: reader, } if p.decoder.CharsetReader == nil { p.decoder.CharsetReader = charset.NewReaderLabel } p.prev = p.doc return p } func (p *parser) parse() (*Node, error) { var streamElementNodeCounter int space2prefix := map[string]string{"http://www.w3.org/XML/1998/namespace": "xml"} for { p.reader.StartCaching() tok, err := p.decoder.Token() p.reader.StopCaching() if err != nil { return nil, err } switch tok := tok.(type) { case xml.StartElement: if p.level == 0 { // mising XML declaration attributes := make([]Attr, 1) attributes[0].Name = xml.Name{Local: "version"} attributes[0].Value = "1.0" node := &Node{ Type: DeclarationNode, Data: "xml", Attr: attributes, level: 1, } AddChild(p.prev, node) p.level = 1 p.prev = node } for _, att := range tok.Attr { if att.Name.Local == "xmlns" { space2prefix[att.Value] = "" // reset empty if exist the default namespace // defaultNamespaceURL = att.Value } else if att.Name.Space == "xmlns" { // maybe there are have duplicate NamespaceURL? space2prefix[att.Value] = att.Name.Local } } if space := tok.Name.Space; space != "" { if _, found := space2prefix[space]; !found && p.decoder.Strict { return nil, fmt.Errorf("xmlquery: invalid XML document, namespace %s is missing", space) } } attributes := make([]Attr, len(tok.Attr)) for i, att := range tok.Attr { name := att.Name if prefix, ok := space2prefix[name.Space]; ok { name.Space = prefix } attributes[i] = Attr{ Name: name, Value: att.Value, NamespaceURI: att.Name.Space, } } node := &Node{ Type: ElementNode, Data: tok.Name.Local, NamespaceURI: tok.Name.Space, Attr: attributes, level: p.level, } if p.level == p.prev.level { AddSibling(p.prev, node) } else if p.level > p.prev.level { AddChild(p.prev, node) } else if p.level < p.prev.level { for i := p.prev.level - p.level; i > 1; i-- { p.prev = p.prev.Parent } AddSibling(p.prev.Parent, node) } if node.NamespaceURI != "" { if v, ok := space2prefix[node.NamespaceURI]; ok { cached := string(p.reader.Cache()) if strings.HasPrefix(cached, fmt.Sprintf("%s:%s", v, node.Data)) || strings.HasPrefix(cached, fmt.Sprintf("<%s:%s", v, node.Data)) { node.Prefix = v } } } // If we're in the streaming mode, we need to remember the node if it is the target node // so that when we finish processing the node's EndElement, we know how/what to return to // caller. Also we need to remove the target node from the tree upon next Read() call so // memory doesn't grow unbounded. if p.streamElementXPath != nil { if p.streamNode == nil { if QuerySelector(p.doc, p.streamElementXPath) != nil { p.streamNode = node p.streamNodePrev = p.prev streamElementNodeCounter = 1 } } else { streamElementNodeCounter++ } } p.prev = node p.level++ case xml.EndElement: p.level-- // If we're in streaming mode, and we already have a potential streaming // target node identified (p.streamNode != nil) then we need to check if // this is the real one we want to return to caller. if p.streamNode != nil { streamElementNodeCounter-- if streamElementNodeCounter == 0 { // Now we know this element node is the at least passing the initial // p.streamElementXPath check and is a potential target node candidate. // We need to have 1 more check with p.streamElementFilter (if given) to // ensure it is really the element node we want. // The reason we need a two-step check process is because the following // situation: // b1 // And say the p.streamElementXPath = "/AAA/BBB[. != 'b1']". Now during // xml.StartElement time, the node is still empty, so it will pass // the p.streamElementXPath check. However, eventually we know this // shouldn't be returned to the caller. Having a second more fine-grained // filter check ensures that. So in this case, the caller should really // setup the stream parser with: // streamElementXPath = "/AAA/BBB[" // streamElementFilter = "/AAA/BBB[. != 'b1']" if p.streamElementFilter == nil || QuerySelector(p.doc, p.streamElementFilter) != nil { return p.streamNode, nil } // otherwise, this isn't our target node, clean things up. // note we also remove the underlying *Node from the node tree, to prevent // future stream node candidate selection error. RemoveFromTree(p.streamNode) p.prev = p.streamNodePrev p.streamNode = nil p.streamNodePrev = nil } } case xml.CharData: // First, normalize the cache... cached := strings.ToUpper(string(p.reader.Cache())) nodeType := TextNode if strings.HasPrefix(cached, " p.prev.level { AddChild(p.prev, node) } else if p.level < p.prev.level { for i := p.prev.level - p.level; i > 1; i-- { p.prev = p.prev.Parent } AddSibling(p.prev.Parent, node) } case xml.Comment: node := &Node{Type: CommentNode, Data: string(tok), level: p.level} if p.level == p.prev.level { AddSibling(p.prev, node) } else if p.level > p.prev.level { AddChild(p.prev, node) } else if p.level < p.prev.level { for i := p.prev.level - p.level; i > 1; i-- { p.prev = p.prev.Parent } AddSibling(p.prev.Parent, node) } case xml.ProcInst: // Processing Instruction if p.prev.Type != DeclarationNode { p.level++ } node := &Node{Type: DeclarationNode, Data: tok.Target, level: p.level} pairs := strings.Split(string(tok.Inst), " ") for _, pair := range pairs { pair = strings.TrimSpace(pair) if i := strings.Index(pair, "="); i > 0 { AddAttr(node, pair[:i], strings.Trim(pair[i+1:], `"`)) } } if p.level == p.prev.level { AddSibling(p.prev, node) } else if p.level > p.prev.level { AddChild(p.prev, node) } else if p.level < p.prev.level { for i := p.prev.level - p.level; i > 1; i-- { p.prev = p.prev.Parent } AddSibling(p.prev.Parent, node) } p.prev = node case xml.Directive: } } } // StreamParser enables loading and parsing an XML document in a streaming // fashion. type StreamParser struct { p *parser } // CreateStreamParser creates a StreamParser. Argument streamElementXPath is // required. // Argument streamElementFilter is optional and should only be used in advanced // scenarios. // // Scenario 1: simple case: // xml := `b1b2` // sp, err := CreateStreamParser(strings.NewReader(xml), "/AAA/BBB") // if err != nil { // panic(err) // } // for { // n, err := sp.Read() // if err != nil { // break // } // fmt.Println(n.OutputXML(true)) // } // Output will be: // b1 // b2 // // Scenario 2: advanced case: // xml := `b1b2` // sp, err := CreateStreamParser(strings.NewReader(xml), "/AAA/BBB", "/AAA/BBB[. != 'b1']") // if err != nil { // panic(err) // } // for { // n, err := sp.Read() // if err != nil { // break // } // fmt.Println(n.OutputXML(true)) // } // Output will be: // b2 // // As the argument names indicate, streamElementXPath should be used for // providing xpath query pointing to the target element node only, no extra // filtering on the element itself or its children; while streamElementFilter, // if needed, can provide additional filtering on the target element and its // children. // // CreateStreamParser returns an error if either streamElementXPath or // streamElementFilter, if provided, cannot be successfully parsed and compiled // into a valid xpath query. func CreateStreamParser(r io.Reader, streamElementXPath string, streamElementFilter ...string) (*StreamParser, error) { return CreateStreamParserWithOptions(r, ParserOptions{}, streamElementXPath, streamElementFilter...) } // CreateStreamParserWithOptions is like CreateStreamParser, but with custom options func CreateStreamParserWithOptions( r io.Reader, options ParserOptions, streamElementXPath string, streamElementFilter ...string, ) (*StreamParser, error) { elemXPath, err := getQuery(streamElementXPath) if err != nil { return nil, fmt.Errorf("invalid streamElementXPath '%s', err: %s", streamElementXPath, err.Error()) } elemFilter := (*xpath.Expr)(nil) if len(streamElementFilter) > 0 { elemFilter, err = getQuery(streamElementFilter[0]) if err != nil { return nil, fmt.Errorf("invalid streamElementFilter '%s', err: %s", streamElementFilter[0], err.Error()) } } parser := createParser(r) options.apply(parser) sp := &StreamParser{ p: parser, } sp.p.streamElementXPath = elemXPath sp.p.streamElementFilter = elemFilter return sp, nil } // Read returns a target node that satisfies the XPath specified by caller at // StreamParser creation time. If there is no more satisfying target nodes after // reading the rest of the XML document, io.EOF will be returned. At any time, // any XML parsing error encountered will be returned, and the stream parsing // stopped. Calling Read() after an error is returned (including io.EOF) results // undefined behavior. Also note, due to the streaming nature, calling Read() // will automatically remove any previous target node(s) from the document tree. func (sp *StreamParser) Read() (*Node, error) { // Because this is a streaming read, we need to release/remove last // target node from the node tree to free up memory. if sp.p.streamNode != nil { // We need to remove all siblings before the current stream node, // because the document may contain unwanted nodes between the target // ones (for example new line text node), which would otherwise // accumulate as first childs, and slow down the stream over time for sp.p.streamNode.PrevSibling != nil { RemoveFromTree(sp.p.streamNode.PrevSibling) } sp.p.prev = sp.p.streamNode.Parent RemoveFromTree(sp.p.streamNode) sp.p.streamNode = nil sp.p.streamNodePrev = nil } return sp.p.parse() } ================================================ FILE: vendor/github.com/antchfx/xmlquery/query.go ================================================ /* Package xmlquery provides extract data from XML documents using XPath expression. */ package xmlquery import ( "fmt" "strings" "github.com/antchfx/xpath" ) // SelectElements finds child elements with the specified name. func (n *Node) SelectElements(name string) []*Node { return Find(n, name) } // SelectElement finds child elements with the specified name. func (n *Node) SelectElement(name string) *Node { return FindOne(n, name) } // SelectAttr returns the attribute value with the specified name. func (n *Node) SelectAttr(name string) string { if n.Type == AttributeNode { if n.Data == name { return n.InnerText() } return "" } xmlName := newXMLName(name) for _, attr := range n.Attr { if attr.Name == xmlName { return attr.Value } } return "" } var _ xpath.NodeNavigator = &NodeNavigator{} // CreateXPathNavigator creates a new xpath.NodeNavigator for the specified // XML Node. func CreateXPathNavigator(top *Node) *NodeNavigator { return &NodeNavigator{curr: top, root: top, attr: -1} } func getCurrentNode(it *xpath.NodeIterator) *Node { n := it.Current().(*NodeNavigator) if n.NodeType() == xpath.AttributeNode { childNode := &Node{ Type: TextNode, Data: n.Value(), } return &Node{ Parent: n.curr, Type: AttributeNode, Data: n.LocalName(), FirstChild: childNode, LastChild: childNode, } } return n.curr } // Find is like QueryAll but panics if `expr` is not a valid XPath expression. // See `QueryAll()` function. func Find(top *Node, expr string) []*Node { nodes, err := QueryAll(top, expr) if err != nil { panic(err) } return nodes } // FindOne is like Query but panics if `expr` is not a valid XPath expression. // See `Query()` function. func FindOne(top *Node, expr string) *Node { node, err := Query(top, expr) if err != nil { panic(err) } return node } // QueryAll searches the XML Node that matches by the specified XPath expr. // Returns an error if the expression `expr` cannot be parsed. func QueryAll(top *Node, expr string) ([]*Node, error) { exp, err := getQuery(expr) if err != nil { return nil, err } return QuerySelectorAll(top, exp), nil } // Query searches the XML Node that matches by the specified XPath expr, // and returns first matched element. func Query(top *Node, expr string) (*Node, error) { exp, err := getQuery(expr) if err != nil { return nil, err } return QuerySelector(top, exp), nil } // QuerySelectorAll searches all of the XML Node that matches the specified // XPath selectors. func QuerySelectorAll(top *Node, selector *xpath.Expr) []*Node { t := selector.Select(CreateXPathNavigator(top)) var elems []*Node for t.MoveNext() { elems = append(elems, getCurrentNode(t)) } return elems } // QuerySelector returns the first matched XML Node by the specified XPath // selector. func QuerySelector(top *Node, selector *xpath.Expr) *Node { t := selector.Select(CreateXPathNavigator(top)) if t.MoveNext() { return getCurrentNode(t) } return nil } // FindEach searches the html.Node and calls functions cb. // Important: this method is deprecated, instead, use for .. = range Find(){}. func FindEach(top *Node, expr string, cb func(int, *Node)) { for i, n := range Find(top, expr) { cb(i, n) } } // FindEachWithBreak functions the same as FindEach but allows to break the loop // by returning false from the callback function `cb`. // Important: this method is deprecated, instead, use .. = range Find(){}. func FindEachWithBreak(top *Node, expr string, cb func(int, *Node) bool) { for i, n := range Find(top, expr) { if !cb(i, n) { break } } } type NodeNavigator struct { root, curr *Node attr int } func (x *NodeNavigator) Current() *Node { return x.curr } func (x *NodeNavigator) NodeType() xpath.NodeType { switch x.curr.Type { case CommentNode: return xpath.CommentNode case TextNode, CharDataNode: return xpath.TextNode case DeclarationNode, DocumentNode: return xpath.RootNode case ElementNode: if x.attr != -1 { return xpath.AttributeNode } return xpath.ElementNode } panic(fmt.Sprintf("unknown XML node type: %v", x.curr.Type)) } func (x *NodeNavigator) LocalName() string { if x.attr != -1 { return x.curr.Attr[x.attr].Name.Local } return x.curr.Data } func (x *NodeNavigator) Prefix() string { if x.NodeType() == xpath.AttributeNode { if x.attr != -1 { return x.curr.Attr[x.attr].Name.Space } return "" } return x.curr.Prefix } func (x *NodeNavigator) NamespaceURL() string { if x.attr != -1 { return x.curr.Attr[x.attr].NamespaceURI } return x.curr.NamespaceURI } func (x *NodeNavigator) Value() string { switch x.curr.Type { case CommentNode: return x.curr.Data case ElementNode: if x.attr != -1 { return x.curr.Attr[x.attr].Value } return x.curr.InnerText() case TextNode: return x.curr.Data } return "" } func (x *NodeNavigator) Copy() xpath.NodeNavigator { n := *x return &n } func (x *NodeNavigator) MoveToRoot() { x.curr = x.root } func (x *NodeNavigator) MoveToParent() bool { if x.attr != -1 { x.attr = -1 return true } else if node := x.curr.Parent; node != nil { x.curr = node return true } return false } func (x *NodeNavigator) MoveToNextAttribute() bool { if x.attr >= len(x.curr.Attr)-1 { return false } x.attr++ return true } func (x *NodeNavigator) MoveToChild() bool { if x.attr != -1 { return false } if node := x.curr.FirstChild; node != nil { x.curr = node return true } return false } func (x *NodeNavigator) MoveToFirst() bool { if x.attr != -1 || x.curr.PrevSibling == nil { return false } for { node := x.curr.PrevSibling if node == nil { break } x.curr = node } return true } func (x *NodeNavigator) String() string { return x.Value() } func (x *NodeNavigator) MoveToNext() bool { if x.attr != -1 { return false } for node := x.curr.NextSibling; node != nil; node = x.curr.NextSibling { x.curr = node if x.curr.Type != TextNode || strings.TrimSpace(x.curr.Data) != "" { return true } } return false } func (x *NodeNavigator) MoveToPrevious() bool { if x.attr != -1 { return false } for node := x.curr.PrevSibling; node != nil; node = x.curr.PrevSibling { x.curr = node if x.curr.Type != TextNode || strings.TrimSpace(x.curr.Data) != "" { return true } } return false } func (x *NodeNavigator) MoveTo(other xpath.NodeNavigator) bool { node, ok := other.(*NodeNavigator) if !ok || node.root != x.root { return false } x.curr = node.curr x.attr = node.attr return true } ================================================ FILE: vendor/github.com/antchfx/xpath/.gitignore ================================================ # vscode .vscode debug *.test ./build # Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof ================================================ FILE: vendor/github.com/antchfx/xpath/.travis.yml ================================================ language: go go: - 1.6 - 1.9 - '1.10' install: - go get github.com/mattn/goveralls script: - $HOME/gopath/bin/goveralls -service=travis-ci ================================================ FILE: vendor/github.com/antchfx/xpath/LICENSE ================================================ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/antchfx/xpath/README.md ================================================ XPath ==== [![GoDoc](https://godoc.org/github.com/antchfx/xpath?status.svg)](https://godoc.org/github.com/antchfx/xpath) [![Coverage Status](https://coveralls.io/repos/github/antchfx/xpath/badge.svg?branch=master)](https://coveralls.io/github/antchfx/xpath?branch=master) [![Build Status](https://travis-ci.org/antchfx/xpath.svg?branch=master)](https://travis-ci.org/antchfx/xpath) [![Go Report Card](https://goreportcard.com/badge/github.com/antchfx/xpath)](https://goreportcard.com/report/github.com/antchfx/xpath) XPath is Go package provides selecting nodes from XML, HTML or other documents using XPath expression. Implementation === - [htmlquery](https://github.com/antchfx/htmlquery) - an XPath query package for HTML document - [xmlquery](https://github.com/antchfx/xmlquery) - an XPath query package for XML document. - [jsonquery](https://github.com/antchfx/jsonquery) - an XPath query package for JSON document Supported Features === #### The basic XPath patterns. > The basic XPath patterns cover 90% of the cases that most stylesheets will need. - `node` : Selects all child elements with nodeName of node. - `*` : Selects all child elements. - `@attr` : Selects the attribute attr. - `@*` : Selects all attributes. - `node()` : Matches an org.w3c.dom.Node. - `text()` : Matches a org.w3c.dom.Text node. - `comment()` : Matches a comment. - `.` : Selects the current node. - `..` : Selects the parent of current node. - `/` : Selects the document node. - `a[expr]` : Select only those nodes matching a which also satisfy the expression expr. - `a[n]` : Selects the nth matching node matching a When a filter's expression is a number, XPath selects based on position. - `a/b` : For each node matching a, add the nodes matching b to the result. - `a//b` : For each node matching a, add the descendant nodes matching b to the result. - `//b` : Returns elements in the entire document matching b. - `a|b` : All nodes matching a or b, union operation(not boolean or). - `(a, b, c)` : Evaluates each of its operands and concatenates the resulting sequences, in order, into a single result sequence - `(a/b)` : Selects all matches nodes as grouping set. #### Node Axes - `child::*` : The child axis selects children of the current node. - `descendant::*` : The descendant axis selects descendants of the current node. It is equivalent to '//'. - `descendant-or-self::*` : Selects descendants including the current node. - `attribute::*` : Selects attributes of the current element. It is equivalent to @* - `following-sibling::*` : Selects nodes after the current node. - `preceding-sibling::*` : Selects nodes before the current node. - `following::*` : Selects the first matching node following in document order, excluding descendants. - `preceding::*` : Selects the first matching node preceding in document order, excluding ancestors. - `parent::*` : Selects the parent if it matches. The '..' pattern from the core is equivalent to 'parent::node()'. - `ancestor::*` : Selects matching ancestors. - `ancestor-or-self::*` : Selects ancestors including the current node. - `self::*` : Selects the current node. '.' is equivalent to 'self::node()'. #### Expressions The gxpath supported three types: number, boolean, string. - `path` : Selects nodes based on the path. - `a = b` : Standard comparisons. * a = b True if a equals b. * a != b True if a is not equal to b. * a < b True if a is less than b. * a <= b True if a is less than or equal to b. * a > b True if a is greater than b. * a >= b True if a is greater than or equal to b. - `a + b` : Arithmetic expressions. * `- a` Unary minus * a + b Add * a - b Substract * a * b Multiply * a div b Divide * a mod b Floating point mod, like Java. - `a or b` : Boolean `or` operation. - `a and b` : Boolean `and` operation. - `(expr)` : Parenthesized expressions. - `fun(arg1, ..., argn)` : Function calls: | Function | Supported | | --- | --- | `boolean()`| ✓ | `ceiling()`| ✓ | `choose()`| ✗ | `concat()`| ✓ | `contains()`| ✓ | `count()`| ✓ | `current()`| ✗ | `document()`| ✗ | `element-available()`| ✗ | `ends-with()`| ✓ | `false()`| ✓ | `floor()`| ✓ | `format-number()`| ✗ | `function-available()`| ✗ | `generate-id()`| ✗ | `id()`| ✗ | `key()`| ✗ | `lang()`| ✗ | `last()`| ✓ | `local-name()`| ✓ | `matches()`| ✓ | `name()`| ✓ | `namespace-uri()`| ✓ | `normalize-space()`| ✓ | `not()`| ✓ | `number()`| ✓ | `position()`| ✓ | `replace()`| ✓ | `reverse()`| ✓ | `round()`| ✓ | `starts-with()`| ✓ | `string()`| ✓ | `string-length()`| ✓ | `substring()`| ✓ | `substring-after()`| ✓ | `substring-before()`| ✓ | `sum()`| ✓ | `system-property()`| ✗ | `translate()`| ✓ | `true()`| ✓ | `unparsed-entity-url()` | ✗ | ================================================ FILE: vendor/github.com/antchfx/xpath/build.go ================================================ package xpath import ( "errors" "fmt" ) type flag int const ( noneFlag flag = iota filterFlag ) // builder provides building an XPath expressions. type builder struct { depth int flag flag firstInput query } // axisPredicate creates a predicate to predicating for this axis node. func axisPredicate(root *axisNode) func(NodeNavigator) bool { // get current axix node type. typ := ElementNode switch root.AxeType { case "attribute": typ = AttributeNode case "self", "parent": typ = allNode default: switch root.Prop { case "comment": typ = CommentNode case "text": typ = TextNode // case "processing-instruction": // typ = ProcessingInstructionNode case "node": typ = allNode } } nametest := root.LocalName != "" || root.Prefix != "" predicate := func(n NodeNavigator) bool { if typ == n.NodeType() || typ == allNode { if nametest { type namespaceURL interface { NamespaceURL() string } if ns, ok := n.(namespaceURL); ok && root.hasNamespaceURI { return root.LocalName == n.LocalName() && root.namespaceURI == ns.NamespaceURL() } if root.LocalName == n.LocalName() && root.Prefix == n.Prefix() { return true } } else { return true } } return false } return predicate } // processAxisNode processes a query for the XPath axis node. func (b *builder) processAxisNode(root *axisNode) (query, error) { var ( err error qyInput query qyOutput query predicate = axisPredicate(root) ) if root.Input == nil { qyInput = &contextQuery{} } else { if root.AxeType == "child" && (root.Input.Type() == nodeAxis) { if input := root.Input.(*axisNode); input.AxeType == "descendant-or-self" { var qyGrandInput query if input.Input != nil { qyGrandInput, _ = b.processNode(input.Input) } else { qyGrandInput = &contextQuery{} } // fix #20: https://github.com/antchfx/htmlquery/issues/20 filter := func(n NodeNavigator) bool { v := predicate(n) switch root.Prop { case "text": v = v && n.NodeType() == TextNode case "comment": v = v && n.NodeType() == CommentNode } return v } // fix `//*[contains(@id,"food")]//*[contains(@id,"food")]`, see https://github.com/antchfx/htmlquery/issues/52 // Skip the current node(Self:false) for the next descendants nodes. _, ok := qyGrandInput.(*contextQuery) qyOutput = &descendantQuery{Input: qyGrandInput, Predicate: filter, Self: ok} return qyOutput, nil } } qyInput, err = b.processNode(root.Input) if err != nil { return nil, err } } switch root.AxeType { case "ancestor": qyOutput = &ancestorQuery{Input: qyInput, Predicate: predicate} case "ancestor-or-self": qyOutput = &ancestorQuery{Input: qyInput, Predicate: predicate, Self: true} case "attribute": qyOutput = &attributeQuery{Input: qyInput, Predicate: predicate} case "child": filter := func(n NodeNavigator) bool { v := predicate(n) switch root.Prop { case "text": v = v && n.NodeType() == TextNode case "node": v = v && (n.NodeType() == ElementNode || n.NodeType() == TextNode) case "comment": v = v && n.NodeType() == CommentNode } return v } qyOutput = &childQuery{Input: qyInput, Predicate: filter} case "descendant": qyOutput = &descendantQuery{Input: qyInput, Predicate: predicate} case "descendant-or-self": qyOutput = &descendantQuery{Input: qyInput, Predicate: predicate, Self: true} case "following": qyOutput = &followingQuery{Input: qyInput, Predicate: predicate} case "following-sibling": qyOutput = &followingQuery{Input: qyInput, Predicate: predicate, Sibling: true} case "parent": qyOutput = &parentQuery{Input: qyInput, Predicate: predicate} case "preceding": qyOutput = &precedingQuery{Input: qyInput, Predicate: predicate} case "preceding-sibling": qyOutput = &precedingQuery{Input: qyInput, Predicate: predicate, Sibling: true} case "self": qyOutput = &selfQuery{Input: qyInput, Predicate: predicate} case "namespace": // haha,what will you do someting?? default: err = fmt.Errorf("unknown axe type: %s", root.AxeType) return nil, err } return qyOutput, nil } // processFilterNode builds query for the XPath filter predicate. func (b *builder) processFilterNode(root *filterNode) (query, error) { b.flag |= filterFlag qyInput, err := b.processNode(root.Input) if err != nil { return nil, err } qyCond, err := b.processNode(root.Condition) if err != nil { return nil, err } qyOutput := &filterQuery{Input: qyInput, Predicate: qyCond} return qyOutput, nil } // processFunctionNode processes query for the XPath function node. func (b *builder) processFunctionNode(root *functionNode) (query, error) { var qyOutput query switch root.FuncName { case "starts-with": arg1, err := b.processNode(root.Args[0]) if err != nil { return nil, err } arg2, err := b.processNode(root.Args[1]) if err != nil { return nil, err } qyOutput = &functionQuery{Input: b.firstInput, Func: startwithFunc(arg1, arg2)} case "ends-with": arg1, err := b.processNode(root.Args[0]) if err != nil { return nil, err } arg2, err := b.processNode(root.Args[1]) if err != nil { return nil, err } qyOutput = &functionQuery{Input: b.firstInput, Func: endwithFunc(arg1, arg2)} case "contains": arg1, err := b.processNode(root.Args[0]) if err != nil { return nil, err } arg2, err := b.processNode(root.Args[1]) if err != nil { return nil, err } qyOutput = &functionQuery{Input: b.firstInput, Func: containsFunc(arg1, arg2)} case "matches": //matches(string , pattern) if len(root.Args) != 2 { return nil, errors.New("xpath: matches function must have two parameters") } var ( arg1, arg2 query err error ) if arg1, err = b.processNode(root.Args[0]); err != nil { return nil, err } if arg2, err = b.processNode(root.Args[1]); err != nil { return nil, err } qyOutput = &functionQuery{Input: b.firstInput, Func: matchesFunc(arg1, arg2)} case "substring": //substring( string , start [, length] ) if len(root.Args) < 2 { return nil, errors.New("xpath: substring function must have at least two parameter") } var ( arg1, arg2, arg3 query err error ) if arg1, err = b.processNode(root.Args[0]); err != nil { return nil, err } if arg2, err = b.processNode(root.Args[1]); err != nil { return nil, err } if len(root.Args) == 3 { if arg3, err = b.processNode(root.Args[2]); err != nil { return nil, err } } qyOutput = &functionQuery{Input: b.firstInput, Func: substringFunc(arg1, arg2, arg3)} case "substring-before", "substring-after": //substring-xxxx( haystack, needle ) if len(root.Args) != 2 { return nil, errors.New("xpath: substring-before function must have two parameters") } var ( arg1, arg2 query err error ) if arg1, err = b.processNode(root.Args[0]); err != nil { return nil, err } if arg2, err = b.processNode(root.Args[1]); err != nil { return nil, err } qyOutput = &functionQuery{ Input: b.firstInput, Func: substringIndFunc(arg1, arg2, root.FuncName == "substring-after"), } case "string-length": // string-length( [string] ) if len(root.Args) < 1 { return nil, errors.New("xpath: string-length function must have at least one parameter") } arg1, err := b.processNode(root.Args[0]) if err != nil { return nil, err } qyOutput = &functionQuery{Input: b.firstInput, Func: stringLengthFunc(arg1)} case "normalize-space": if len(root.Args) == 0 { return nil, errors.New("xpath: normalize-space function must have at least one parameter") } argQuery, err := b.processNode(root.Args[0]) if err != nil { return nil, err } qyOutput = &functionQuery{Input: argQuery, Func: normalizespaceFunc} case "replace": //replace( string , string, string ) if len(root.Args) != 3 { return nil, errors.New("xpath: replace function must have three parameters") } var ( arg1, arg2, arg3 query err error ) if arg1, err = b.processNode(root.Args[0]); err != nil { return nil, err } if arg2, err = b.processNode(root.Args[1]); err != nil { return nil, err } if arg3, err = b.processNode(root.Args[2]); err != nil { return nil, err } qyOutput = &functionQuery{Input: b.firstInput, Func: replaceFunc(arg1, arg2, arg3)} case "translate": //translate( string , string, string ) if len(root.Args) != 3 { return nil, errors.New("xpath: translate function must have three parameters") } var ( arg1, arg2, arg3 query err error ) if arg1, err = b.processNode(root.Args[0]); err != nil { return nil, err } if arg2, err = b.processNode(root.Args[1]); err != nil { return nil, err } if arg3, err = b.processNode(root.Args[2]); err != nil { return nil, err } qyOutput = &functionQuery{Input: b.firstInput, Func: translateFunc(arg1, arg2, arg3)} case "not": if len(root.Args) == 0 { return nil, errors.New("xpath: not function must have at least one parameter") } argQuery, err := b.processNode(root.Args[0]) if err != nil { return nil, err } qyOutput = &functionQuery{Input: argQuery, Func: notFunc} case "name", "local-name", "namespace-uri": if len(root.Args) > 1 { return nil, fmt.Errorf("xpath: %s function must have at most one parameter", root.FuncName) } var ( arg query err error ) if len(root.Args) == 1 { arg, err = b.processNode(root.Args[0]) if err != nil { return nil, err } } switch root.FuncName { case "name": qyOutput = &functionQuery{Input: b.firstInput, Func: nameFunc(arg)} case "local-name": qyOutput = &functionQuery{Input: b.firstInput, Func: localNameFunc(arg)} case "namespace-uri": qyOutput = &functionQuery{Input: b.firstInput, Func: namespaceFunc(arg)} } case "true", "false": val := root.FuncName == "true" qyOutput = &functionQuery{ Input: b.firstInput, Func: func(_ query, _ iterator) interface{} { return val }, } case "last": switch typ := b.firstInput.(type) { case *groupQuery, *filterQuery: // https://github.com/antchfx/xpath/issues/76 // https://github.com/antchfx/xpath/issues/78 qyOutput = &lastQuery{Input: typ} default: qyOutput = &functionQuery{Input: b.firstInput, Func: lastFunc} } case "position": qyOutput = &functionQuery{Input: b.firstInput, Func: positionFunc} case "boolean", "number", "string": inp := b.firstInput if len(root.Args) > 1 { return nil, fmt.Errorf("xpath: %s function must have at most one parameter", root.FuncName) } if len(root.Args) == 1 { argQuery, err := b.processNode(root.Args[0]) if err != nil { return nil, err } inp = argQuery } f := &functionQuery{Input: inp} switch root.FuncName { case "boolean": f.Func = booleanFunc case "string": f.Func = stringFunc case "number": f.Func = numberFunc } qyOutput = f case "count": //if b.firstInput == nil { // return nil, errors.New("xpath: expression must evaluate to node-set") //} if len(root.Args) == 0 { return nil, fmt.Errorf("xpath: count(node-sets) function must with have parameters node-sets") } argQuery, err := b.processNode(root.Args[0]) if err != nil { return nil, err } qyOutput = &functionQuery{Input: argQuery, Func: countFunc} case "sum": if len(root.Args) == 0 { return nil, fmt.Errorf("xpath: sum(node-sets) function must with have parameters node-sets") } argQuery, err := b.processNode(root.Args[0]) if err != nil { return nil, err } qyOutput = &functionQuery{Input: argQuery, Func: sumFunc} case "ceiling", "floor", "round": if len(root.Args) == 0 { return nil, fmt.Errorf("xpath: ceiling(node-sets) function must with have parameters node-sets") } argQuery, err := b.processNode(root.Args[0]) if err != nil { return nil, err } f := &functionQuery{Input: argQuery} switch root.FuncName { case "ceiling": f.Func = ceilingFunc case "floor": f.Func = floorFunc case "round": f.Func = roundFunc } qyOutput = f case "concat": if len(root.Args) < 2 { return nil, fmt.Errorf("xpath: concat() must have at least two arguments") } var args []query for _, v := range root.Args { q, err := b.processNode(v) if err != nil { return nil, err } args = append(args, q) } qyOutput = &functionQuery{Input: b.firstInput, Func: concatFunc(args...)} case "reverse": if len(root.Args) == 0 { return nil, fmt.Errorf("xpath: reverse(node-sets) function must with have parameters node-sets") } argQuery, err := b.processNode(root.Args[0]) if err != nil { return nil, err } qyOutput = &transformFunctionQuery{Input: argQuery, Func: reverseFunc} default: return nil, fmt.Errorf("not yet support this function %s()", root.FuncName) } return qyOutput, nil } func (b *builder) processOperatorNode(root *operatorNode) (query, error) { left, err := b.processNode(root.Left) if err != nil { return nil, err } right, err := b.processNode(root.Right) if err != nil { return nil, err } var qyOutput query switch root.Op { case "+", "-", "*", "div", "mod": // Numeric operator var exprFunc func(interface{}, interface{}) interface{} switch root.Op { case "+": exprFunc = plusFunc case "-": exprFunc = minusFunc case "*": exprFunc = mulFunc case "div": exprFunc = divFunc case "mod": exprFunc = modFunc } qyOutput = &numericQuery{Left: left, Right: right, Do: exprFunc} case "=", ">", ">=", "<", "<=", "!=": var exprFunc func(iterator, interface{}, interface{}) interface{} switch root.Op { case "=": exprFunc = eqFunc case ">": exprFunc = gtFunc case ">=": exprFunc = geFunc case "<": exprFunc = ltFunc case "<=": exprFunc = leFunc case "!=": exprFunc = neFunc } qyOutput = &logicalQuery{Left: left, Right: right, Do: exprFunc} case "or", "and": isOr := false if root.Op == "or" { isOr = true } qyOutput = &booleanQuery{Left: left, Right: right, IsOr: isOr} case "|": qyOutput = &unionQuery{Left: left, Right: right} } return qyOutput, nil } func (b *builder) processNode(root node) (q query, err error) { if b.depth = b.depth + 1; b.depth > 1024 { err = errors.New("the xpath expressions is too complex") return } switch root.Type() { case nodeConstantOperand: n := root.(*operandNode) q = &constantQuery{Val: n.Val} case nodeRoot: q = &contextQuery{Root: true} case nodeAxis: q, err = b.processAxisNode(root.(*axisNode)) b.firstInput = q case nodeFilter: q, err = b.processFilterNode(root.(*filterNode)) b.firstInput = q case nodeFunction: q, err = b.processFunctionNode(root.(*functionNode)) case nodeOperator: q, err = b.processOperatorNode(root.(*operatorNode)) case nodeGroup: q, err = b.processNode(root.(*groupNode).Input) if err != nil { return } q = &groupQuery{Input: q} b.firstInput = q } return } // build builds a specified XPath expressions expr. func build(expr string, namespaces map[string]string) (q query, err error) { defer func() { if e := recover(); e != nil { switch x := e.(type) { case string: err = errors.New(x) case error: err = x default: err = errors.New("unknown panic") } } }() root := parse(expr, namespaces) b := &builder{} return b.processNode(root) } ================================================ FILE: vendor/github.com/antchfx/xpath/cache.go ================================================ package xpath import ( "regexp" "sync" ) type loadFunc func(key interface{}) (interface{}, error) const ( defaultCap = 65536 ) // The reason we're building a simple capacity-resetting loading cache (when capacity reached) instead of using // something like github.com/hashicorp/golang-lru is primarily due to (not wanting to create) external dependency. // Currently this library has 0 external dep (other than go sdk), and supports go 1.6, 1.9, and 1.10 (and later). // Creating external lib dependencies (plus their transitive dependencies) would make things hard if not impossible. // We expect under most circumstances, the defaultCap is big enough for any long running services that use this // library if their xpath regexp cardinality is low. However, in extreme cases when the capacity is reached, we // simply reset the cache, taking a small subsequent perf hit (next to nothing considering amortization) in trade // of more complex and less performant LRU type of construct. type loadingCache struct { sync.RWMutex cap int load loadFunc m map[interface{}]interface{} reset int } // NewLoadingCache creates a new instance of a loading cache with capacity. Capacity must be >= 0, or // it will panic. Capacity == 0 means the cache growth is unbounded. func NewLoadingCache(load loadFunc, capacity int) *loadingCache { if capacity < 0 { panic("capacity must be >= 0") } return &loadingCache{cap: capacity, load: load, m: make(map[interface{}]interface{})} } func (c *loadingCache) get(key interface{}) (interface{}, error) { c.RLock() v, found := c.m[key] c.RUnlock() if found { return v, nil } v, err := c.load(key) if err != nil { return nil, err } c.Lock() if c.cap > 0 && len(c.m) >= c.cap { c.m = map[interface{}]interface{}{key: v} c.reset++ } else { c.m[key] = v } c.Unlock() return v, nil } var ( // RegexpCache is a loading cache for string -> *regexp.Regexp mapping. It is exported so that in rare cases // client can customize load func and/or capacity. RegexpCache = defaultRegexpCache() ) func defaultRegexpCache() *loadingCache { return NewLoadingCache( func(key interface{}) (interface{}, error) { return regexp.Compile(key.(string)) }, defaultCap) } func getRegexp(pattern string) (*regexp.Regexp, error) { exp, err := RegexpCache.get(pattern) if err != nil { return nil, err } return exp.(*regexp.Regexp), nil } ================================================ FILE: vendor/github.com/antchfx/xpath/func.go ================================================ package xpath import ( "errors" "fmt" "math" "strconv" "strings" "sync" "unicode" ) // Defined an interface of stringBuilder that compatible with // strings.Builder(go 1.10) and bytes.Buffer(< go 1.10) type stringBuilder interface { WriteRune(r rune) (n int, err error) WriteString(s string) (int, error) Reset() Grow(n int) String() string } var builderPool = sync.Pool{New: func() interface{} { return newStringBuilder() }} // The XPath function list. func predicate(q query) func(NodeNavigator) bool { type Predicater interface { Test(NodeNavigator) bool } if p, ok := q.(Predicater); ok { return p.Test } return func(NodeNavigator) bool { return true } } // positionFunc is a XPath Node Set functions position(). func positionFunc(q query, t iterator) interface{} { var ( count = 1 node = t.Current().Copy() ) test := predicate(q) for node.MoveToPrevious() { if test(node) { count++ } } return float64(count) } // lastFunc is a XPath Node Set functions last(). func lastFunc(q query, t iterator) interface{} { var ( count = 0 node = t.Current().Copy() ) node.MoveToFirst() test := predicate(q) for { if test(node) { count++ } if !node.MoveToNext() { break } } return float64(count) } // countFunc is a XPath Node Set functions count(node-set). func countFunc(q query, t iterator) interface{} { var count = 0 q = functionArgs(q) test := predicate(q) switch typ := q.Evaluate(t).(type) { case query: for node := typ.Select(t); node != nil; node = typ.Select(t) { if test(node) { count++ } } } return float64(count) } // sumFunc is a XPath Node Set functions sum(node-set). func sumFunc(q query, t iterator) interface{} { var sum float64 switch typ := functionArgs(q).Evaluate(t).(type) { case query: for node := typ.Select(t); node != nil; node = typ.Select(t) { if v, err := strconv.ParseFloat(node.Value(), 64); err == nil { sum += v } } case float64: sum = typ case string: v, err := strconv.ParseFloat(typ, 64) if err != nil { panic(errors.New("sum() function argument type must be a node-set or number")) } sum = v } return sum } func asNumber(t iterator, o interface{}) float64 { switch typ := o.(type) { case query: node := typ.Select(t) if node == nil { return float64(0) } if v, err := strconv.ParseFloat(node.Value(), 64); err == nil { return v } case float64: return typ case string: v, err := strconv.ParseFloat(typ, 64) if err == nil { return v } } return math.NaN() } // ceilingFunc is a XPath Node Set functions ceiling(node-set). func ceilingFunc(q query, t iterator) interface{} { val := asNumber(t, functionArgs(q).Evaluate(t)) // if math.IsNaN(val) { // panic(errors.New("ceiling() function argument type must be a valid number")) // } return math.Ceil(val) } // floorFunc is a XPath Node Set functions floor(node-set). func floorFunc(q query, t iterator) interface{} { val := asNumber(t, functionArgs(q).Evaluate(t)) return math.Floor(val) } // roundFunc is a XPath Node Set functions round(node-set). func roundFunc(q query, t iterator) interface{} { val := asNumber(t, functionArgs(q).Evaluate(t)) //return math.Round(val) return round(val) } // nameFunc is a XPath functions name([node-set]). func nameFunc(arg query) func(query, iterator) interface{} { return func(q query, t iterator) interface{} { var v NodeNavigator if arg == nil { v = t.Current() } else { v = arg.Clone().Select(t) if v == nil { return "" } } ns := v.Prefix() if ns == "" { return v.LocalName() } return ns + ":" + v.LocalName() } } // localNameFunc is a XPath functions local-name([node-set]). func localNameFunc(arg query) func(query, iterator) interface{} { return func(q query, t iterator) interface{} { var v NodeNavigator if arg == nil { v = t.Current() } else { v = arg.Clone().Select(t) if v == nil { return "" } } return v.LocalName() } } // namespaceFunc is a XPath functions namespace-uri([node-set]). func namespaceFunc(arg query) func(query, iterator) interface{} { return func(q query, t iterator) interface{} { var v NodeNavigator if arg == nil { v = t.Current() } else { // Get the first node in the node-set if specified. v = arg.Clone().Select(t) if v == nil { return "" } } // fix about namespace-uri() bug: https://github.com/antchfx/xmlquery/issues/22 // TODO: In the next version, add NamespaceURL() to the NodeNavigator interface. type namespaceURL interface { NamespaceURL() string } if f, ok := v.(namespaceURL); ok { return f.NamespaceURL() } return v.Prefix() } } func asBool(t iterator, v interface{}) bool { switch v := v.(type) { case nil: return false case *NodeIterator: return v.MoveNext() case bool: return v case float64: return v != 0 case string: return v != "" case query: return v.Select(t) != nil default: panic(fmt.Errorf("unexpected type: %T", v)) } } func asString(t iterator, v interface{}) string { switch v := v.(type) { case nil: return "" case bool: if v { return "true" } return "false" case float64: return strconv.FormatFloat(v, 'g', -1, 64) case string: return v case query: node := v.Select(t) if node == nil { return "" } return node.Value() default: panic(fmt.Errorf("unexpected type: %T", v)) } } // booleanFunc is a XPath functions boolean([node-set]). func booleanFunc(q query, t iterator) interface{} { v := functionArgs(q).Evaluate(t) return asBool(t, v) } // numberFunc is a XPath functions number([node-set]). func numberFunc(q query, t iterator) interface{} { v := functionArgs(q).Evaluate(t) return asNumber(t, v) } // stringFunc is a XPath functions string([node-set]). func stringFunc(q query, t iterator) interface{} { v := functionArgs(q).Evaluate(t) return asString(t, v) } // startwithFunc is a XPath functions starts-with(string, string). func startwithFunc(arg1, arg2 query) func(query, iterator) interface{} { return func(q query, t iterator) interface{} { var ( m, n string ok bool ) switch typ := functionArgs(arg1).Evaluate(t).(type) { case string: m = typ case query: node := typ.Select(t) if node == nil { return false } m = node.Value() default: panic(errors.New("starts-with() function argument type must be string")) } n, ok = functionArgs(arg2).Evaluate(t).(string) if !ok { panic(errors.New("starts-with() function argument type must be string")) } return strings.HasPrefix(m, n) } } // endwithFunc is a XPath functions ends-with(string, string). func endwithFunc(arg1, arg2 query) func(query, iterator) interface{} { return func(q query, t iterator) interface{} { var ( m, n string ok bool ) switch typ := functionArgs(arg1).Evaluate(t).(type) { case string: m = typ case query: node := typ.Select(t) if node == nil { return false } m = node.Value() default: panic(errors.New("ends-with() function argument type must be string")) } n, ok = functionArgs(arg2).Evaluate(t).(string) if !ok { panic(errors.New("ends-with() function argument type must be string")) } return strings.HasSuffix(m, n) } } // containsFunc is a XPath functions contains(string or @attr, string). func containsFunc(arg1, arg2 query) func(query, iterator) interface{} { return func(q query, t iterator) interface{} { var ( m, n string ok bool ) switch typ := functionArgs(arg1).Evaluate(t).(type) { case string: m = typ case query: node := typ.Select(t) if node == nil { return false } m = node.Value() default: panic(errors.New("contains() function argument type must be string")) } n, ok = functionArgs(arg2).Evaluate(t).(string) if !ok { panic(errors.New("contains() function argument type must be string")) } return strings.Contains(m, n) } } // matchesFunc is an XPath function that tests a given string against a regexp pattern. // Note: does not support https://www.w3.org/TR/xpath-functions-31/#func-matches 3rd optional `flags` argument; if // needed, directly put flags in the regexp pattern, such as `(?i)^pattern$` for `i` flag. func matchesFunc(arg1, arg2 query) func(query, iterator) interface{} { return func(q query, t iterator) interface{} { var s string switch typ := functionArgs(arg1).Evaluate(t).(type) { case string: s = typ case query: node := typ.Select(t) if node == nil { return "" } s = node.Value() } var pattern string var ok bool if pattern, ok = functionArgs(arg2).Evaluate(t).(string); !ok { panic(errors.New("matches() function second argument type must be string")) } re, err := getRegexp(pattern) if err != nil { panic(fmt.Errorf("matches() function second argument is not a valid regexp pattern, err: %s", err.Error())) } return re.MatchString(s) } } // normalizespaceFunc is XPath functions normalize-space(string?) func normalizespaceFunc(q query, t iterator) interface{} { var m string switch typ := functionArgs(q).Evaluate(t).(type) { case string: m = typ case query: node := typ.Select(t) if node == nil { return "" } m = node.Value() } var b = builderPool.Get().(stringBuilder) b.Grow(len(m)) runeStr := []rune(strings.TrimSpace(m)) l := len(runeStr) for i := range runeStr { r := runeStr[i] isSpace := unicode.IsSpace(r) if !(isSpace && (i+1 < l && unicode.IsSpace(runeStr[i+1]))) { if isSpace { r = ' ' } b.WriteRune(r) } } result := b.String() b.Reset() builderPool.Put(b) return result } // substringFunc is XPath functions substring function returns a part of a given string. func substringFunc(arg1, arg2, arg3 query) func(query, iterator) interface{} { return func(q query, t iterator) interface{} { var m string switch typ := functionArgs(arg1).Evaluate(t).(type) { case string: m = typ case query: node := typ.Select(t) if node == nil { return "" } m = node.Value() } var start, length float64 var ok bool if start, ok = functionArgs(arg2).Evaluate(t).(float64); !ok { panic(errors.New("substring() function first argument type must be int")) } else if start < 1 { panic(errors.New("substring() function first argument type must be >= 1")) } start-- if arg3 != nil { if length, ok = functionArgs(arg3).Evaluate(t).(float64); !ok { panic(errors.New("substring() function second argument type must be int")) } } if (len(m) - int(start)) < int(length) { panic(errors.New("substring() function start and length argument out of range")) } if length > 0 { return m[int(start):int(length+start)] } return m[int(start):] } } // substringIndFunc is XPath functions substring-before/substring-after function returns a part of a given string. func substringIndFunc(arg1, arg2 query, after bool) func(query, iterator) interface{} { return func(q query, t iterator) interface{} { var str string switch v := functionArgs(arg1).Evaluate(t).(type) { case string: str = v case query: node := v.Select(t) if node == nil { return "" } str = node.Value() } var word string switch v := functionArgs(arg2).Evaluate(t).(type) { case string: word = v case query: node := v.Select(t) if node == nil { return "" } word = node.Value() } if word == "" { return "" } i := strings.Index(str, word) if i < 0 { return "" } if after { return str[i+len(word):] } return str[:i] } } // stringLengthFunc is XPATH string-length( [string] ) function that returns a number // equal to the number of characters in a given string. func stringLengthFunc(arg1 query) func(query, iterator) interface{} { return func(q query, t iterator) interface{} { switch v := functionArgs(arg1).Evaluate(t).(type) { case string: return float64(len(v)) case query: node := v.Select(t) if node == nil { break } return float64(len(node.Value())) } return float64(0) } } // translateFunc is XPath functions translate() function returns a replaced string. func translateFunc(arg1, arg2, arg3 query) func(query, iterator) interface{} { return func(q query, t iterator) interface{} { str := asString(t, functionArgs(arg1).Evaluate(t)) src := asString(t, functionArgs(arg2).Evaluate(t)) dst := asString(t, functionArgs(arg3).Evaluate(t)) replace := make([]string, 0, len(src)) for i, s := range src { d := "" if i < len(dst) { d = string(dst[i]) } replace = append(replace, string(s), d) } return strings.NewReplacer(replace...).Replace(str) } } // replaceFunc is XPath functions replace() function returns a replaced string. func replaceFunc(arg1, arg2, arg3 query) func(query, iterator) interface{} { return func(q query, t iterator) interface{} { str := asString(t, functionArgs(arg1).Evaluate(t)) src := asString(t, functionArgs(arg2).Evaluate(t)) dst := asString(t, functionArgs(arg3).Evaluate(t)) return strings.Replace(str, src, dst, -1) } } // notFunc is XPATH functions not(expression) function operation. func notFunc(q query, t iterator) interface{} { switch v := functionArgs(q).Evaluate(t).(type) { case bool: return !v case query: node := v.Select(t) return node == nil default: return false } } // concatFunc is the concat function concatenates two or more // strings and returns the resulting string. // concat( string1 , string2 [, stringn]* ) func concatFunc(args ...query) func(query, iterator) interface{} { return func(q query, t iterator) interface{} { b := builderPool.Get().(stringBuilder) for _, v := range args { v = functionArgs(v) switch v := v.Evaluate(t).(type) { case string: b.WriteString(v) case query: node := v.Select(t) if node != nil { b.WriteString(node.Value()) } } } result := b.String() b.Reset() builderPool.Put(b) return result } } // https://github.com/antchfx/xpath/issues/43 func functionArgs(q query) query { if _, ok := q.(*functionQuery); ok { return q } return q.Clone() } func reverseFunc(q query, t iterator) func() NodeNavigator { var list []NodeNavigator for { node := q.Select(t) if node == nil { break } list = append(list, node.Copy()) } i := len(list) return func() NodeNavigator { if i <= 0 { return nil } i-- node := list[i] return node } } ================================================ FILE: vendor/github.com/antchfx/xpath/func_go110.go ================================================ // +build go1.10 package xpath import ( "math" "strings" ) func round(f float64) int { return int(math.Round(f)) } func newStringBuilder() stringBuilder { return &strings.Builder{} } ================================================ FILE: vendor/github.com/antchfx/xpath/func_pre_go110.go ================================================ // +build !go1.10 package xpath import ( "bytes" "math" ) // math.Round() is supported by Go 1.10+, // This method just compatible for version <1.10. // https://github.com/golang/go/issues/20100 func round(f float64) int { if math.Abs(f) < 0.5 { return 0 } return int(f + math.Copysign(0.5, f)) } func newStringBuilder() stringBuilder { return &bytes.Buffer{} } ================================================ FILE: vendor/github.com/antchfx/xpath/operator.go ================================================ package xpath import ( "fmt" "reflect" "strconv" ) // The XPath number operator function list. // valueType is a return value type. type valueType int const ( booleanType valueType = iota numberType stringType nodeSetType ) func getValueType(i interface{}) valueType { v := reflect.ValueOf(i) switch v.Kind() { case reflect.Float64: return numberType case reflect.String: return stringType case reflect.Bool: return booleanType default: if _, ok := i.(query); ok { return nodeSetType } } panic(fmt.Errorf("xpath unknown value type: %v", v.Kind())) } type logical func(iterator, string, interface{}, interface{}) bool var logicalFuncs = [][]logical{ {cmpBooleanBoolean, nil, nil, nil}, {nil, cmpNumericNumeric, cmpNumericString, cmpNumericNodeSet}, {nil, cmpStringNumeric, cmpStringString, cmpStringNodeSet}, {nil, cmpNodeSetNumeric, cmpNodeSetString, cmpNodeSetNodeSet}, } // number vs number func cmpNumberNumberF(op string, a, b float64) bool { switch op { case "=": return a == b case ">": return a > b case "<": return a < b case ">=": return a >= b case "<=": return a <= b case "!=": return a != b } return false } // string vs string func cmpStringStringF(op string, a, b string) bool { switch op { case "=": return a == b case ">": return a > b case "<": return a < b case ">=": return a >= b case "<=": return a <= b case "!=": return a != b } return false } func cmpBooleanBooleanF(op string, a, b bool) bool { switch op { case "or": return a || b case "and": return a && b } return false } func cmpNumericNumeric(t iterator, op string, m, n interface{}) bool { a := m.(float64) b := n.(float64) return cmpNumberNumberF(op, a, b) } func cmpNumericString(t iterator, op string, m, n interface{}) bool { a := m.(float64) b := n.(string) num, err := strconv.ParseFloat(b, 64) if err != nil { panic(err) } return cmpNumberNumberF(op, a, num) } func cmpNumericNodeSet(t iterator, op string, m, n interface{}) bool { a := m.(float64) b := n.(query) for { node := b.Select(t) if node == nil { break } num, err := strconv.ParseFloat(node.Value(), 64) if err != nil { panic(err) } if cmpNumberNumberF(op, a, num) { return true } } return false } func cmpNodeSetNumeric(t iterator, op string, m, n interface{}) bool { a := m.(query) b := n.(float64) for { node := a.Select(t) if node == nil { break } num, err := strconv.ParseFloat(node.Value(), 64) if err != nil { panic(err) } if cmpNumberNumberF(op, num, b) { return true } } return false } func cmpNodeSetString(t iterator, op string, m, n interface{}) bool { a := m.(query) b := n.(string) for { node := a.Select(t) if node == nil { break } if cmpStringStringF(op, b, node.Value()) { return true } } return false } func cmpNodeSetNodeSet(t iterator, op string, m, n interface{}) bool { a := m.(query) b := n.(query) for { x := a.Select(t) if x == nil { return false } y := b.Select(t) if y == nil { return false } for { if cmpStringStringF(op, x.Value(), y.Value()) { return true } if y = b.Select(t); y == nil { break } } // reset b.Evaluate(t) } } func cmpStringNumeric(t iterator, op string, m, n interface{}) bool { a := m.(string) b := n.(float64) num, err := strconv.ParseFloat(a, 64) if err != nil { panic(err) } return cmpNumberNumberF(op, b, num) } func cmpStringString(t iterator, op string, m, n interface{}) bool { a := m.(string) b := n.(string) return cmpStringStringF(op, a, b) } func cmpStringNodeSet(t iterator, op string, m, n interface{}) bool { a := m.(string) b := n.(query) for { node := b.Select(t) if node == nil { break } if cmpStringStringF(op, a, node.Value()) { return true } } return false } func cmpBooleanBoolean(t iterator, op string, m, n interface{}) bool { a := m.(bool) b := n.(bool) return cmpBooleanBooleanF(op, a, b) } // eqFunc is an `=` operator. func eqFunc(t iterator, m, n interface{}) interface{} { t1 := getValueType(m) t2 := getValueType(n) return logicalFuncs[t1][t2](t, "=", m, n) } // gtFunc is an `>` operator. func gtFunc(t iterator, m, n interface{}) interface{} { t1 := getValueType(m) t2 := getValueType(n) return logicalFuncs[t1][t2](t, ">", m, n) } // geFunc is an `>=` operator. func geFunc(t iterator, m, n interface{}) interface{} { t1 := getValueType(m) t2 := getValueType(n) return logicalFuncs[t1][t2](t, ">=", m, n) } // ltFunc is an `<` operator. func ltFunc(t iterator, m, n interface{}) interface{} { t1 := getValueType(m) t2 := getValueType(n) return logicalFuncs[t1][t2](t, "<", m, n) } // leFunc is an `<=` operator. func leFunc(t iterator, m, n interface{}) interface{} { t1 := getValueType(m) t2 := getValueType(n) return logicalFuncs[t1][t2](t, "<=", m, n) } // neFunc is an `!=` operator. func neFunc(t iterator, m, n interface{}) interface{} { t1 := getValueType(m) t2 := getValueType(n) return logicalFuncs[t1][t2](t, "!=", m, n) } // orFunc is an `or` operator. var orFunc = func(t iterator, m, n interface{}) interface{} { t1 := getValueType(m) t2 := getValueType(n) return logicalFuncs[t1][t2](t, "or", m, n) } func numericExpr(m, n interface{}, cb func(float64, float64) float64) float64 { typ := reflect.TypeOf(float64(0)) a := reflect.ValueOf(m).Convert(typ) b := reflect.ValueOf(n).Convert(typ) return cb(a.Float(), b.Float()) } // plusFunc is an `+` operator. var plusFunc = func(m, n interface{}) interface{} { return numericExpr(m, n, func(a, b float64) float64 { return a + b }) } // minusFunc is an `-` operator. var minusFunc = func(m, n interface{}) interface{} { return numericExpr(m, n, func(a, b float64) float64 { return a - b }) } // mulFunc is an `*` operator. var mulFunc = func(m, n interface{}) interface{} { return numericExpr(m, n, func(a, b float64) float64 { return a * b }) } // divFunc is an `DIV` operator. var divFunc = func(m, n interface{}) interface{} { return numericExpr(m, n, func(a, b float64) float64 { return a / b }) } // modFunc is an 'MOD' operator. var modFunc = func(m, n interface{}) interface{} { return numericExpr(m, n, func(a, b float64) float64 { return float64(int(a) % int(b)) }) } ================================================ FILE: vendor/github.com/antchfx/xpath/parse.go ================================================ package xpath import ( "bytes" "errors" "fmt" "strconv" "unicode" ) // A XPath expression token type. type itemType int const ( itemComma itemType = iota // ',' itemSlash // '/' itemAt // '@' itemDot // '.' itemLParens // '(' itemRParens // ')' itemLBracket // '[' itemRBracket // ']' itemStar // '*' itemPlus // '+' itemMinus // '-' itemEq // '=' itemLt // '<' itemGt // '>' itemBang // '!' itemDollar // '$' itemApos // '\'' itemQuote // '"' itemUnion // '|' itemNe // '!=' itemLe // '<=' itemGe // '>=' itemAnd // '&&' itemOr // '||' itemDotDot // '..' itemSlashSlash // '//' itemName // XML Name itemString // Quoted string constant itemNumber // Number constant itemAxe // Axe (like child::) itemEOF // END ) // A node is an XPath node in the parse tree. type node interface { Type() nodeType } // nodeType identifies the type of a parse tree node. type nodeType int func (t nodeType) Type() nodeType { return t } const ( nodeRoot nodeType = iota nodeAxis nodeFilter nodeFunction nodeOperator nodeVariable nodeConstantOperand nodeGroup ) type parser struct { r *scanner d int namespaces map[string]string } // newOperatorNode returns new operator node OperatorNode. func newOperatorNode(op string, left, right node) node { return &operatorNode{nodeType: nodeOperator, Op: op, Left: left, Right: right} } // newOperand returns new constant operand node OperandNode. func newOperandNode(v interface{}) node { return &operandNode{nodeType: nodeConstantOperand, Val: v} } // newAxisNode returns new axis node AxisNode. func newAxisNode(axeTyp, localName, prefix, prop string, n node, opts ...func(p *axisNode)) node { a := axisNode{ nodeType: nodeAxis, LocalName: localName, Prefix: prefix, AxeType: axeTyp, Prop: prop, Input: n, } for _, o := range opts { o(&a) } return &a } // newVariableNode returns new variable node VariableNode. func newVariableNode(prefix, name string) node { return &variableNode{nodeType: nodeVariable, Name: name, Prefix: prefix} } // newFilterNode returns a new filter node FilterNode. func newFilterNode(n, m node) node { return &filterNode{nodeType: nodeFilter, Input: n, Condition: m} } func newGroupNode(n node) node { return &groupNode{nodeType: nodeGroup, Input: n} } // newRootNode returns a root node. func newRootNode(s string) node { return &rootNode{nodeType: nodeRoot, slash: s} } // newFunctionNode returns function call node. func newFunctionNode(name, prefix string, args []node) node { return &functionNode{nodeType: nodeFunction, Prefix: prefix, FuncName: name, Args: args} } // testOp reports whether current item name is an operand op. func testOp(r *scanner, op string) bool { return r.typ == itemName && r.prefix == "" && r.name == op } func isPrimaryExpr(r *scanner) bool { switch r.typ { case itemString, itemNumber, itemDollar, itemLParens: return true case itemName: return r.canBeFunc && !isNodeType(r) } return false } func isNodeType(r *scanner) bool { switch r.name { case "node", "text", "processing-instruction", "comment": return r.prefix == "" } return false } func isStep(item itemType) bool { switch item { case itemDot, itemDotDot, itemAt, itemAxe, itemStar, itemName: return true } return false } func checkItem(r *scanner, typ itemType) { if r.typ != typ { panic(fmt.Sprintf("%s has an invalid token", r.text)) } } // parseExpression parsing the expression with input node n. func (p *parser) parseExpression(n node) node { if p.d = p.d + 1; p.d > 200 { panic("the xpath query is too complex(depth > 200)") } n = p.parseOrExpr(n) p.d-- return n } // next scanning next item on forward. func (p *parser) next() bool { return p.r.nextItem() } func (p *parser) skipItem(typ itemType) { checkItem(p.r, typ) p.next() } // OrExpr ::= AndExpr | OrExpr 'or' AndExpr func (p *parser) parseOrExpr(n node) node { opnd := p.parseAndExpr(n) for { if !testOp(p.r, "or") { break } p.next() opnd = newOperatorNode("or", opnd, p.parseAndExpr(n)) } return opnd } // AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr func (p *parser) parseAndExpr(n node) node { opnd := p.parseEqualityExpr(n) for { if !testOp(p.r, "and") { break } p.next() opnd = newOperatorNode("and", opnd, p.parseEqualityExpr(n)) } return opnd } // EqualityExpr ::= RelationalExpr | EqualityExpr '=' RelationalExpr | EqualityExpr '!=' RelationalExpr func (p *parser) parseEqualityExpr(n node) node { opnd := p.parseRelationalExpr(n) Loop: for { var op string switch p.r.typ { case itemEq: op = "=" case itemNe: op = "!=" default: break Loop } p.next() opnd = newOperatorNode(op, opnd, p.parseRelationalExpr(n)) } return opnd } // RelationalExpr ::= AdditiveExpr | RelationalExpr '<' AdditiveExpr | RelationalExpr '>' AdditiveExpr // | RelationalExpr '<=' AdditiveExpr // | RelationalExpr '>=' AdditiveExpr func (p *parser) parseRelationalExpr(n node) node { opnd := p.parseAdditiveExpr(n) Loop: for { var op string switch p.r.typ { case itemLt: op = "<" case itemGt: op = ">" case itemLe: op = "<=" case itemGe: op = ">=" default: break Loop } p.next() opnd = newOperatorNode(op, opnd, p.parseAdditiveExpr(n)) } return opnd } // AdditiveExpr ::= MultiplicativeExpr | AdditiveExpr '+' MultiplicativeExpr | AdditiveExpr '-' MultiplicativeExpr func (p *parser) parseAdditiveExpr(n node) node { opnd := p.parseMultiplicativeExpr(n) Loop: for { var op string switch p.r.typ { case itemPlus: op = "+" case itemMinus: op = "-" default: break Loop } p.next() opnd = newOperatorNode(op, opnd, p.parseMultiplicativeExpr(n)) } return opnd } // MultiplicativeExpr ::= UnaryExpr | MultiplicativeExpr MultiplyOperator(*) UnaryExpr // | MultiplicativeExpr 'div' UnaryExpr | MultiplicativeExpr 'mod' UnaryExpr func (p *parser) parseMultiplicativeExpr(n node) node { opnd := p.parseUnaryExpr(n) Loop: for { var op string if p.r.typ == itemStar { op = "*" } else if testOp(p.r, "div") || testOp(p.r, "mod") { op = p.r.name } else { break Loop } p.next() opnd = newOperatorNode(op, opnd, p.parseUnaryExpr(n)) } return opnd } // UnaryExpr ::= UnionExpr | '-' UnaryExpr func (p *parser) parseUnaryExpr(n node) node { minus := false // ignore '-' sequence for p.r.typ == itemMinus { p.next() minus = !minus } opnd := p.parseUnionExpr(n) if minus { opnd = newOperatorNode("*", opnd, newOperandNode(float64(-1))) } return opnd } // UnionExpr ::= PathExpr | UnionExpr '|' PathExpr func (p *parser) parseUnionExpr(n node) node { opnd := p.parsePathExpr(n) Loop: for { if p.r.typ != itemUnion { break Loop } p.next() opnd2 := p.parsePathExpr(n) // Checking the node type that must be is node set type? opnd = newOperatorNode("|", opnd, opnd2) } return opnd } // PathExpr ::= LocationPath | FilterExpr | FilterExpr '/' RelativeLocationPath | FilterExpr '//' RelativeLocationPath func (p *parser) parsePathExpr(n node) node { var opnd node if isPrimaryExpr(p.r) { opnd = p.parseFilterExpr(n) switch p.r.typ { case itemSlash: p.next() opnd = p.parseRelativeLocationPath(opnd) case itemSlashSlash: p.next() opnd = p.parseRelativeLocationPath(newAxisNode("descendant-or-self", "", "", "", opnd)) } } else { opnd = p.parseLocationPath(nil) } return opnd } // FilterExpr ::= PrimaryExpr | FilterExpr Predicate func (p *parser) parseFilterExpr(n node) node { opnd := p.parsePrimaryExpr(n) if p.r.typ == itemLBracket { opnd = newFilterNode(opnd, p.parsePredicate(opnd)) } return opnd } // Predicate ::= '[' PredicateExpr ']' func (p *parser) parsePredicate(n node) node { p.skipItem(itemLBracket) opnd := p.parseExpression(n) p.skipItem(itemRBracket) return opnd } // LocationPath ::= RelativeLocationPath | AbsoluteLocationPath func (p *parser) parseLocationPath(n node) (opnd node) { switch p.r.typ { case itemSlash: p.next() opnd = newRootNode("/") if isStep(p.r.typ) { opnd = p.parseRelativeLocationPath(opnd) // ?? child:: or self ?? } case itemSlashSlash: p.next() opnd = newRootNode("//") opnd = p.parseRelativeLocationPath(newAxisNode("descendant-or-self", "", "", "", opnd)) default: opnd = p.parseRelativeLocationPath(n) } return opnd } // RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | AbbreviatedRelativeLocationPath func (p *parser) parseRelativeLocationPath(n node) node { opnd := n Loop: for { opnd = p.parseStep(opnd) switch p.r.typ { case itemSlashSlash: p.next() opnd = newAxisNode("descendant-or-self", "", "", "", opnd) case itemSlash: p.next() default: break Loop } } return opnd } // Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep func (p *parser) parseStep(n node) (opnd node) { axeTyp := "child" // default axes value. if p.r.typ == itemDot || p.r.typ == itemDotDot { if p.r.typ == itemDot { axeTyp = "self" } else { axeTyp = "parent" } p.next() opnd = newAxisNode(axeTyp, "", "", "", n) if p.r.typ != itemLBracket { return opnd } } else { switch p.r.typ { case itemAt: p.next() axeTyp = "attribute" case itemAxe: axeTyp = p.r.name p.next() case itemLParens: return p.parseSequence(n) } opnd = p.parseNodeTest(n, axeTyp) } for p.r.typ == itemLBracket { opnd = newFilterNode(opnd, p.parsePredicate(opnd)) } return opnd } // Expr ::= '(' Step ("," Step)* ')' func (p *parser) parseSequence(n node) (opnd node) { p.skipItem(itemLParens) opnd = p.parseStep(n) for { if p.r.typ != itemComma { break } p.next() opnd2 := p.parseStep(n) opnd = newOperatorNode("|", opnd, opnd2) } p.skipItem(itemRParens) return opnd } // NodeTest ::= NameTest | nodeType '(' ')' | 'processing-instruction' '(' Literal ')' func (p *parser) parseNodeTest(n node, axeTyp string) (opnd node) { switch p.r.typ { case itemName: if p.r.canBeFunc && isNodeType(p.r) { var prop string switch p.r.name { case "comment", "text", "processing-instruction", "node": prop = p.r.name } var name string p.next() p.skipItem(itemLParens) if prop == "processing-instruction" && p.r.typ != itemRParens { checkItem(p.r, itemString) name = p.r.strval p.next() } p.skipItem(itemRParens) opnd = newAxisNode(axeTyp, name, "", prop, n) } else { prefix := p.r.prefix name := p.r.name p.next() if p.r.name == "*" { name = "" } opnd = newAxisNode(axeTyp, name, prefix, "", n, func(a *axisNode) { if prefix != "" && p.namespaces != nil { if ns, ok := p.namespaces[prefix]; ok { a.hasNamespaceURI = true a.namespaceURI = ns } else { panic(fmt.Sprintf("prefix %s not defined.", prefix)) } } }) } case itemStar: opnd = newAxisNode(axeTyp, "", "", "", n) p.next() default: panic("expression must evaluate to a node-set") } return opnd } // PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall func (p *parser) parsePrimaryExpr(n node) (opnd node) { switch p.r.typ { case itemString: opnd = newOperandNode(p.r.strval) p.next() case itemNumber: opnd = newOperandNode(p.r.numval) p.next() case itemDollar: p.next() checkItem(p.r, itemName) opnd = newVariableNode(p.r.prefix, p.r.name) p.next() case itemLParens: p.next() opnd = p.parseExpression(n) if opnd.Type() != nodeConstantOperand { opnd = newGroupNode(opnd) } p.skipItem(itemRParens) case itemName: if p.r.canBeFunc && !isNodeType(p.r) { opnd = p.parseMethod(nil) } } return opnd } // FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument )* )? ')' func (p *parser) parseMethod(n node) node { var args []node name := p.r.name prefix := p.r.prefix p.skipItem(itemName) p.skipItem(itemLParens) if p.r.typ != itemRParens { for { args = append(args, p.parseExpression(n)) if p.r.typ == itemRParens { break } p.skipItem(itemComma) } } p.skipItem(itemRParens) return newFunctionNode(name, prefix, args) } // Parse parsing the XPath express string expr and returns a tree node. func parse(expr string, namespaces map[string]string) node { r := &scanner{text: expr} r.nextChar() r.nextItem() p := &parser{r: r, namespaces: namespaces} return p.parseExpression(nil) } // rootNode holds a top-level node of tree. type rootNode struct { nodeType slash string } func (r *rootNode) String() string { return r.slash } // operatorNode holds two Nodes operator. type operatorNode struct { nodeType Op string Left, Right node } func (o *operatorNode) String() string { return fmt.Sprintf("%v%s%v", o.Left, o.Op, o.Right) } // axisNode holds a location step. type axisNode struct { nodeType Input node Prop string // node-test name.[comment|text|processing-instruction|node] AxeType string // name of the axes.[attribute|ancestor|child|....] LocalName string // local part name of node. Prefix string // prefix name of node. namespaceURI string // namespace URI of node hasNamespaceURI bool // if namespace URI is set (can be "") } func (a *axisNode) String() string { var b bytes.Buffer if a.AxeType != "" { b.Write([]byte(a.AxeType + "::")) } if a.Prefix != "" { b.Write([]byte(a.Prefix + ":")) } b.Write([]byte(a.LocalName)) if a.Prop != "" { b.Write([]byte("/" + a.Prop + "()")) } return b.String() } // operandNode holds a constant operand. type operandNode struct { nodeType Val interface{} } func (o *operandNode) String() string { return fmt.Sprintf("%v", o.Val) } // groupNode holds a set of node expression type groupNode struct { nodeType Input node } func (g *groupNode) String() string { return fmt.Sprintf("%s", g.Input) } // filterNode holds a condition filter. type filterNode struct { nodeType Input, Condition node } func (f *filterNode) String() string { return fmt.Sprintf("%s[%s]", f.Input, f.Condition) } // variableNode holds a variable. type variableNode struct { nodeType Name, Prefix string } func (v *variableNode) String() string { if v.Prefix == "" { return v.Name } return fmt.Sprintf("%s:%s", v.Prefix, v.Name) } // functionNode holds a function call. type functionNode struct { nodeType Args []node Prefix string FuncName string // function name } func (f *functionNode) String() string { var b bytes.Buffer // fun(arg1, ..., argn) b.Write([]byte(f.FuncName)) b.Write([]byte("(")) for i, arg := range f.Args { if i > 0 { b.Write([]byte(",")) } b.Write([]byte(fmt.Sprintf("%s", arg))) } b.Write([]byte(")")) return b.String() } type scanner struct { text, name, prefix string pos int curr rune typ itemType strval string // text value at current pos numval float64 // number value at current pos canBeFunc bool } func (s *scanner) nextChar() bool { if s.pos >= len(s.text) { s.curr = rune(0) return false } s.curr = rune(s.text[s.pos]) s.pos++ return true } func (s *scanner) nextItem() bool { s.skipSpace() switch s.curr { case 0: s.typ = itemEOF return false case ',', '@', '(', ')', '|', '*', '[', ']', '+', '-', '=', '#', '$': s.typ = asItemType(s.curr) s.nextChar() case '<': s.typ = itemLt s.nextChar() if s.curr == '=' { s.typ = itemLe s.nextChar() } case '>': s.typ = itemGt s.nextChar() if s.curr == '=' { s.typ = itemGe s.nextChar() } case '!': s.typ = itemBang s.nextChar() if s.curr == '=' { s.typ = itemNe s.nextChar() } case '.': s.typ = itemDot s.nextChar() if s.curr == '.' { s.typ = itemDotDot s.nextChar() } else if isDigit(s.curr) { s.typ = itemNumber s.numval = s.scanFraction() } case '/': s.typ = itemSlash s.nextChar() if s.curr == '/' { s.typ = itemSlashSlash s.nextChar() } case '"', '\'': s.typ = itemString s.strval = s.scanString() default: if isDigit(s.curr) { s.typ = itemNumber s.numval = s.scanNumber() } else if isName(s.curr) { s.typ = itemName s.name = s.scanName() s.prefix = "" // "foo:bar" is one itemem not three because it doesn't allow spaces in between // We should distinct it from "foo::" and need process "foo ::" as well if s.curr == ':' { s.nextChar() // can be "foo:bar" or "foo::" if s.curr == ':' { // "foo::" s.nextChar() s.typ = itemAxe } else { // "foo:*", "foo:bar" or "foo: " s.prefix = s.name if s.curr == '*' { s.nextChar() s.name = "*" } else if isName(s.curr) { s.name = s.scanName() } else { panic(fmt.Sprintf("%s has an invalid qualified name.", s.text)) } } } else { s.skipSpace() if s.curr == ':' { s.nextChar() // it can be "foo ::" or just "foo :" if s.curr == ':' { s.nextChar() s.typ = itemAxe } else { panic(fmt.Sprintf("%s has an invalid qualified name.", s.text)) } } } s.skipSpace() s.canBeFunc = s.curr == '(' } else { panic(fmt.Sprintf("%s has an invalid token.", s.text)) } } return true } func (s *scanner) skipSpace() { Loop: for { if !unicode.IsSpace(s.curr) || !s.nextChar() { break Loop } } } func (s *scanner) scanFraction() float64 { var ( i = s.pos - 2 c = 1 // '.' ) for isDigit(s.curr) { s.nextChar() c++ } v, err := strconv.ParseFloat(s.text[i:i+c], 64) if err != nil { panic(fmt.Errorf("xpath: scanFraction parse float got error: %v", err)) } return v } func (s *scanner) scanNumber() float64 { var ( c int i = s.pos - 1 ) for isDigit(s.curr) { s.nextChar() c++ } if s.curr == '.' { s.nextChar() c++ for isDigit(s.curr) { s.nextChar() c++ } } v, err := strconv.ParseFloat(s.text[i:i+c], 64) if err != nil { panic(fmt.Errorf("xpath: scanNumber parse float got error: %v", err)) } return v } func (s *scanner) scanString() string { var ( c = 0 end = s.curr ) s.nextChar() i := s.pos - 1 for s.curr != end { if !s.nextChar() { panic(errors.New("xpath: scanString got unclosed string")) } c++ } s.nextChar() return s.text[i : i+c] } func (s *scanner) scanName() string { var ( c int i = s.pos - 1 ) for isName(s.curr) { c++ if !s.nextChar() { break } } return s.text[i : i+c] } func isName(r rune) bool { return string(r) != ":" && string(r) != "/" && (unicode.Is(first, r) || unicode.Is(second, r) || string(r) == "*") } func isDigit(r rune) bool { return unicode.IsDigit(r) } func asItemType(r rune) itemType { switch r { case ',': return itemComma case '@': return itemAt case '(': return itemLParens case ')': return itemRParens case '|': return itemUnion case '*': return itemStar case '[': return itemLBracket case ']': return itemRBracket case '+': return itemPlus case '-': return itemMinus case '=': return itemEq case '$': return itemDollar } panic(fmt.Errorf("unknown item: %v", r)) } var first = &unicode.RangeTable{ R16: []unicode.Range16{ {0x003A, 0x003A, 1}, {0x0041, 0x005A, 1}, {0x005F, 0x005F, 1}, {0x0061, 0x007A, 1}, {0x00C0, 0x00D6, 1}, {0x00D8, 0x00F6, 1}, {0x00F8, 0x00FF, 1}, {0x0100, 0x0131, 1}, {0x0134, 0x013E, 1}, {0x0141, 0x0148, 1}, {0x014A, 0x017E, 1}, {0x0180, 0x01C3, 1}, {0x01CD, 0x01F0, 1}, {0x01F4, 0x01F5, 1}, {0x01FA, 0x0217, 1}, {0x0250, 0x02A8, 1}, {0x02BB, 0x02C1, 1}, {0x0386, 0x0386, 1}, {0x0388, 0x038A, 1}, {0x038C, 0x038C, 1}, {0x038E, 0x03A1, 1}, {0x03A3, 0x03CE, 1}, {0x03D0, 0x03D6, 1}, {0x03DA, 0x03E0, 2}, {0x03E2, 0x03F3, 1}, {0x0401, 0x040C, 1}, {0x040E, 0x044F, 1}, {0x0451, 0x045C, 1}, {0x045E, 0x0481, 1}, {0x0490, 0x04C4, 1}, {0x04C7, 0x04C8, 1}, {0x04CB, 0x04CC, 1}, {0x04D0, 0x04EB, 1}, {0x04EE, 0x04F5, 1}, {0x04F8, 0x04F9, 1}, {0x0531, 0x0556, 1}, {0x0559, 0x0559, 1}, {0x0561, 0x0586, 1}, {0x05D0, 0x05EA, 1}, {0x05F0, 0x05F2, 1}, {0x0621, 0x063A, 1}, {0x0641, 0x064A, 1}, {0x0671, 0x06B7, 1}, {0x06BA, 0x06BE, 1}, {0x06C0, 0x06CE, 1}, {0x06D0, 0x06D3, 1}, {0x06D5, 0x06D5, 1}, {0x06E5, 0x06E6, 1}, {0x0905, 0x0939, 1}, {0x093D, 0x093D, 1}, {0x0958, 0x0961, 1}, {0x0985, 0x098C, 1}, {0x098F, 0x0990, 1}, {0x0993, 0x09A8, 1}, {0x09AA, 0x09B0, 1}, {0x09B2, 0x09B2, 1}, {0x09B6, 0x09B9, 1}, {0x09DC, 0x09DD, 1}, {0x09DF, 0x09E1, 1}, {0x09F0, 0x09F1, 1}, {0x0A05, 0x0A0A, 1}, {0x0A0F, 0x0A10, 1}, {0x0A13, 0x0A28, 1}, {0x0A2A, 0x0A30, 1}, {0x0A32, 0x0A33, 1}, {0x0A35, 0x0A36, 1}, {0x0A38, 0x0A39, 1}, {0x0A59, 0x0A5C, 1}, {0x0A5E, 0x0A5E, 1}, {0x0A72, 0x0A74, 1}, {0x0A85, 0x0A8B, 1}, {0x0A8D, 0x0A8D, 1}, {0x0A8F, 0x0A91, 1}, {0x0A93, 0x0AA8, 1}, {0x0AAA, 0x0AB0, 1}, {0x0AB2, 0x0AB3, 1}, {0x0AB5, 0x0AB9, 1}, {0x0ABD, 0x0AE0, 0x23}, {0x0B05, 0x0B0C, 1}, {0x0B0F, 0x0B10, 1}, {0x0B13, 0x0B28, 1}, {0x0B2A, 0x0B30, 1}, {0x0B32, 0x0B33, 1}, {0x0B36, 0x0B39, 1}, {0x0B3D, 0x0B3D, 1}, {0x0B5C, 0x0B5D, 1}, {0x0B5F, 0x0B61, 1}, {0x0B85, 0x0B8A, 1}, {0x0B8E, 0x0B90, 1}, {0x0B92, 0x0B95, 1}, {0x0B99, 0x0B9A, 1}, {0x0B9C, 0x0B9C, 1}, {0x0B9E, 0x0B9F, 1}, {0x0BA3, 0x0BA4, 1}, {0x0BA8, 0x0BAA, 1}, {0x0BAE, 0x0BB5, 1}, {0x0BB7, 0x0BB9, 1}, {0x0C05, 0x0C0C, 1}, {0x0C0E, 0x0C10, 1}, {0x0C12, 0x0C28, 1}, {0x0C2A, 0x0C33, 1}, {0x0C35, 0x0C39, 1}, {0x0C60, 0x0C61, 1}, {0x0C85, 0x0C8C, 1}, {0x0C8E, 0x0C90, 1}, {0x0C92, 0x0CA8, 1}, {0x0CAA, 0x0CB3, 1}, {0x0CB5, 0x0CB9, 1}, {0x0CDE, 0x0CDE, 1}, {0x0CE0, 0x0CE1, 1}, {0x0D05, 0x0D0C, 1}, {0x0D0E, 0x0D10, 1}, {0x0D12, 0x0D28, 1}, {0x0D2A, 0x0D39, 1}, {0x0D60, 0x0D61, 1}, {0x0E01, 0x0E2E, 1}, {0x0E30, 0x0E30, 1}, {0x0E32, 0x0E33, 1}, {0x0E40, 0x0E45, 1}, {0x0E81, 0x0E82, 1}, {0x0E84, 0x0E84, 1}, {0x0E87, 0x0E88, 1}, {0x0E8A, 0x0E8D, 3}, {0x0E94, 0x0E97, 1}, {0x0E99, 0x0E9F, 1}, {0x0EA1, 0x0EA3, 1}, {0x0EA5, 0x0EA7, 2}, {0x0EAA, 0x0EAB, 1}, {0x0EAD, 0x0EAE, 1}, {0x0EB0, 0x0EB0, 1}, {0x0EB2, 0x0EB3, 1}, {0x0EBD, 0x0EBD, 1}, {0x0EC0, 0x0EC4, 1}, {0x0F40, 0x0F47, 1}, {0x0F49, 0x0F69, 1}, {0x10A0, 0x10C5, 1}, {0x10D0, 0x10F6, 1}, {0x1100, 0x1100, 1}, {0x1102, 0x1103, 1}, {0x1105, 0x1107, 1}, {0x1109, 0x1109, 1}, {0x110B, 0x110C, 1}, {0x110E, 0x1112, 1}, {0x113C, 0x1140, 2}, {0x114C, 0x1150, 2}, {0x1154, 0x1155, 1}, {0x1159, 0x1159, 1}, {0x115F, 0x1161, 1}, {0x1163, 0x1169, 2}, {0x116D, 0x116E, 1}, {0x1172, 0x1173, 1}, {0x1175, 0x119E, 0x119E - 0x1175}, {0x11A8, 0x11AB, 0x11AB - 0x11A8}, {0x11AE, 0x11AF, 1}, {0x11B7, 0x11B8, 1}, {0x11BA, 0x11BA, 1}, {0x11BC, 0x11C2, 1}, {0x11EB, 0x11F0, 0x11F0 - 0x11EB}, {0x11F9, 0x11F9, 1}, {0x1E00, 0x1E9B, 1}, {0x1EA0, 0x1EF9, 1}, {0x1F00, 0x1F15, 1}, {0x1F18, 0x1F1D, 1}, {0x1F20, 0x1F45, 1}, {0x1F48, 0x1F4D, 1}, {0x1F50, 0x1F57, 1}, {0x1F59, 0x1F5B, 0x1F5B - 0x1F59}, {0x1F5D, 0x1F5D, 1}, {0x1F5F, 0x1F7D, 1}, {0x1F80, 0x1FB4, 1}, {0x1FB6, 0x1FBC, 1}, {0x1FBE, 0x1FBE, 1}, {0x1FC2, 0x1FC4, 1}, {0x1FC6, 0x1FCC, 1}, {0x1FD0, 0x1FD3, 1}, {0x1FD6, 0x1FDB, 1}, {0x1FE0, 0x1FEC, 1}, {0x1FF2, 0x1FF4, 1}, {0x1FF6, 0x1FFC, 1}, {0x2126, 0x2126, 1}, {0x212A, 0x212B, 1}, {0x212E, 0x212E, 1}, {0x2180, 0x2182, 1}, {0x3007, 0x3007, 1}, {0x3021, 0x3029, 1}, {0x3041, 0x3094, 1}, {0x30A1, 0x30FA, 1}, {0x3105, 0x312C, 1}, {0x4E00, 0x9FA5, 1}, {0xAC00, 0xD7A3, 1}, }, } var second = &unicode.RangeTable{ R16: []unicode.Range16{ {0x002D, 0x002E, 1}, {0x0030, 0x0039, 1}, {0x00B7, 0x00B7, 1}, {0x02D0, 0x02D1, 1}, {0x0300, 0x0345, 1}, {0x0360, 0x0361, 1}, {0x0387, 0x0387, 1}, {0x0483, 0x0486, 1}, {0x0591, 0x05A1, 1}, {0x05A3, 0x05B9, 1}, {0x05BB, 0x05BD, 1}, {0x05BF, 0x05BF, 1}, {0x05C1, 0x05C2, 1}, {0x05C4, 0x0640, 0x0640 - 0x05C4}, {0x064B, 0x0652, 1}, {0x0660, 0x0669, 1}, {0x0670, 0x0670, 1}, {0x06D6, 0x06DC, 1}, {0x06DD, 0x06DF, 1}, {0x06E0, 0x06E4, 1}, {0x06E7, 0x06E8, 1}, {0x06EA, 0x06ED, 1}, {0x06F0, 0x06F9, 1}, {0x0901, 0x0903, 1}, {0x093C, 0x093C, 1}, {0x093E, 0x094C, 1}, {0x094D, 0x094D, 1}, {0x0951, 0x0954, 1}, {0x0962, 0x0963, 1}, {0x0966, 0x096F, 1}, {0x0981, 0x0983, 1}, {0x09BC, 0x09BC, 1}, {0x09BE, 0x09BF, 1}, {0x09C0, 0x09C4, 1}, {0x09C7, 0x09C8, 1}, {0x09CB, 0x09CD, 1}, {0x09D7, 0x09D7, 1}, {0x09E2, 0x09E3, 1}, {0x09E6, 0x09EF, 1}, {0x0A02, 0x0A3C, 0x3A}, {0x0A3E, 0x0A3F, 1}, {0x0A40, 0x0A42, 1}, {0x0A47, 0x0A48, 1}, {0x0A4B, 0x0A4D, 1}, {0x0A66, 0x0A6F, 1}, {0x0A70, 0x0A71, 1}, {0x0A81, 0x0A83, 1}, {0x0ABC, 0x0ABC, 1}, {0x0ABE, 0x0AC5, 1}, {0x0AC7, 0x0AC9, 1}, {0x0ACB, 0x0ACD, 1}, {0x0AE6, 0x0AEF, 1}, {0x0B01, 0x0B03, 1}, {0x0B3C, 0x0B3C, 1}, {0x0B3E, 0x0B43, 1}, {0x0B47, 0x0B48, 1}, {0x0B4B, 0x0B4D, 1}, {0x0B56, 0x0B57, 1}, {0x0B66, 0x0B6F, 1}, {0x0B82, 0x0B83, 1}, {0x0BBE, 0x0BC2, 1}, {0x0BC6, 0x0BC8, 1}, {0x0BCA, 0x0BCD, 1}, {0x0BD7, 0x0BD7, 1}, {0x0BE7, 0x0BEF, 1}, {0x0C01, 0x0C03, 1}, {0x0C3E, 0x0C44, 1}, {0x0C46, 0x0C48, 1}, {0x0C4A, 0x0C4D, 1}, {0x0C55, 0x0C56, 1}, {0x0C66, 0x0C6F, 1}, {0x0C82, 0x0C83, 1}, {0x0CBE, 0x0CC4, 1}, {0x0CC6, 0x0CC8, 1}, {0x0CCA, 0x0CCD, 1}, {0x0CD5, 0x0CD6, 1}, {0x0CE6, 0x0CEF, 1}, {0x0D02, 0x0D03, 1}, {0x0D3E, 0x0D43, 1}, {0x0D46, 0x0D48, 1}, {0x0D4A, 0x0D4D, 1}, {0x0D57, 0x0D57, 1}, {0x0D66, 0x0D6F, 1}, {0x0E31, 0x0E31, 1}, {0x0E34, 0x0E3A, 1}, {0x0E46, 0x0E46, 1}, {0x0E47, 0x0E4E, 1}, {0x0E50, 0x0E59, 1}, {0x0EB1, 0x0EB1, 1}, {0x0EB4, 0x0EB9, 1}, {0x0EBB, 0x0EBC, 1}, {0x0EC6, 0x0EC6, 1}, {0x0EC8, 0x0ECD, 1}, {0x0ED0, 0x0ED9, 1}, {0x0F18, 0x0F19, 1}, {0x0F20, 0x0F29, 1}, {0x0F35, 0x0F39, 2}, {0x0F3E, 0x0F3F, 1}, {0x0F71, 0x0F84, 1}, {0x0F86, 0x0F8B, 1}, {0x0F90, 0x0F95, 1}, {0x0F97, 0x0F97, 1}, {0x0F99, 0x0FAD, 1}, {0x0FB1, 0x0FB7, 1}, {0x0FB9, 0x0FB9, 1}, {0x20D0, 0x20DC, 1}, {0x20E1, 0x3005, 0x3005 - 0x20E1}, {0x302A, 0x302F, 1}, {0x3031, 0x3035, 1}, {0x3099, 0x309A, 1}, {0x309D, 0x309E, 1}, {0x30FC, 0x30FE, 1}, }, } ================================================ FILE: vendor/github.com/antchfx/xpath/query.go ================================================ package xpath import ( "bytes" "fmt" "hash/fnv" "reflect" ) type iterator interface { Current() NodeNavigator } // An XPath query interface. type query interface { // Select traversing iterator returns a query matched node NodeNavigator. Select(iterator) NodeNavigator // Evaluate evaluates query and returns values of the current query. Evaluate(iterator) interface{} Clone() query } // nopQuery is an empty query that always return nil for any query. type nopQuery struct { query } func (nopQuery) Select(iterator) NodeNavigator { return nil } func (nopQuery) Evaluate(iterator) interface{} { return nil } func (nopQuery) Clone() query { return nopQuery{} } // contextQuery is returns current node on the iterator object query. type contextQuery struct { count int Root bool // Moving to root-level node in the current context iterator. } func (c *contextQuery) Select(t iterator) (n NodeNavigator) { if c.count == 0 { c.count++ n = t.Current().Copy() if c.Root { n.MoveToRoot() } } return n } func (c *contextQuery) Evaluate(iterator) interface{} { c.count = 0 return c } func (c *contextQuery) Clone() query { return &contextQuery{Root: c.Root} } // ancestorQuery is an XPath ancestor node query.(ancestor::*|ancestor-self::*) type ancestorQuery struct { iterator func() NodeNavigator Self bool Input query Predicate func(NodeNavigator) bool } func (a *ancestorQuery) Select(t iterator) NodeNavigator { for { if a.iterator == nil { node := a.Input.Select(t) if node == nil { return nil } first := true node = node.Copy() a.iterator = func() NodeNavigator { if first && a.Self { first = false if a.Predicate(node) { return node } } for node.MoveToParent() { if !a.Predicate(node) { continue } return node } return nil } } if node := a.iterator(); node != nil { return node } a.iterator = nil } } func (a *ancestorQuery) Evaluate(t iterator) interface{} { a.Input.Evaluate(t) a.iterator = nil return a } func (a *ancestorQuery) Test(n NodeNavigator) bool { return a.Predicate(n) } func (a *ancestorQuery) Clone() query { return &ancestorQuery{Self: a.Self, Input: a.Input.Clone(), Predicate: a.Predicate} } // attributeQuery is an XPath attribute node query.(@*) type attributeQuery struct { iterator func() NodeNavigator Input query Predicate func(NodeNavigator) bool } func (a *attributeQuery) Select(t iterator) NodeNavigator { for { if a.iterator == nil { node := a.Input.Select(t) if node == nil { return nil } node = node.Copy() a.iterator = func() NodeNavigator { for { onAttr := node.MoveToNextAttribute() if !onAttr { return nil } if a.Predicate(node) { return node } } } } if node := a.iterator(); node != nil { return node } a.iterator = nil } } func (a *attributeQuery) Evaluate(t iterator) interface{} { a.Input.Evaluate(t) a.iterator = nil return a } func (a *attributeQuery) Test(n NodeNavigator) bool { return a.Predicate(n) } func (a *attributeQuery) Clone() query { return &attributeQuery{Input: a.Input.Clone(), Predicate: a.Predicate} } // childQuery is an XPath child node query.(child::*) type childQuery struct { posit int iterator func() NodeNavigator Input query Predicate func(NodeNavigator) bool } func (c *childQuery) Select(t iterator) NodeNavigator { for { if c.iterator == nil { c.posit = 0 node := c.Input.Select(t) if node == nil { return nil } node = node.Copy() first := true c.iterator = func() NodeNavigator { for { if (first && !node.MoveToChild()) || (!first && !node.MoveToNext()) { return nil } first = false if c.Predicate(node) { return node } } } } if node := c.iterator(); node != nil { c.posit++ return node } c.iterator = nil } } func (c *childQuery) Evaluate(t iterator) interface{} { c.Input.Evaluate(t) c.iterator = nil return c } func (c *childQuery) Test(n NodeNavigator) bool { return c.Predicate(n) } func (c *childQuery) Clone() query { return &childQuery{Input: c.Input.Clone(), Predicate: c.Predicate} } // position returns a position of current NodeNavigator. func (c *childQuery) position() int { return c.posit } // descendantQuery is an XPath descendant node query.(descendant::* | descendant-or-self::*) type descendantQuery struct { iterator func() NodeNavigator posit int level int Self bool Input query Predicate func(NodeNavigator) bool } func (d *descendantQuery) Select(t iterator) NodeNavigator { for { if d.iterator == nil { d.posit = 0 node := d.Input.Select(t) if node == nil { return nil } node = node.Copy() d.level = 0 positmap := make(map[int]int) first := true d.iterator = func() NodeNavigator { if first && d.Self { first = false if d.Predicate(node) { d.posit = 1 positmap[d.level] = 1 return node } } for { if node.MoveToChild() { d.level = d.level + 1 positmap[d.level] = 0 } else { for { if d.level == 0 { return nil } if node.MoveToNext() { break } node.MoveToParent() d.level = d.level - 1 } } if d.Predicate(node) { positmap[d.level]++ d.posit = positmap[d.level] return node } } } } if node := d.iterator(); node != nil { return node } d.iterator = nil } } func (d *descendantQuery) Evaluate(t iterator) interface{} { d.Input.Evaluate(t) d.iterator = nil return d } func (d *descendantQuery) Test(n NodeNavigator) bool { return d.Predicate(n) } // position returns a position of current NodeNavigator. func (d *descendantQuery) position() int { return d.posit } func (d *descendantQuery) depth() int { return d.level } func (d *descendantQuery) Clone() query { return &descendantQuery{Self: d.Self, Input: d.Input.Clone(), Predicate: d.Predicate} } // followingQuery is an XPath following node query.(following::*|following-sibling::*) type followingQuery struct { posit int iterator func() NodeNavigator Input query Sibling bool // The matching sibling node of current node. Predicate func(NodeNavigator) bool } func (f *followingQuery) Select(t iterator) NodeNavigator { for { if f.iterator == nil { f.posit = 0 node := f.Input.Select(t) if node == nil { return nil } node = node.Copy() if f.Sibling { f.iterator = func() NodeNavigator { for { if !node.MoveToNext() { return nil } if f.Predicate(node) { f.posit++ return node } } } } else { var q *descendantQuery // descendant query f.iterator = func() NodeNavigator { for { if q == nil { for !node.MoveToNext() { if !node.MoveToParent() { return nil } } q = &descendantQuery{ Self: true, Input: &contextQuery{}, Predicate: f.Predicate, } t.Current().MoveTo(node) } if node := q.Select(t); node != nil { f.posit = q.posit return node } q = nil } } } } if node := f.iterator(); node != nil { return node } f.iterator = nil } } func (f *followingQuery) Evaluate(t iterator) interface{} { f.Input.Evaluate(t) return f } func (f *followingQuery) Test(n NodeNavigator) bool { return f.Predicate(n) } func (f *followingQuery) Clone() query { return &followingQuery{Input: f.Input.Clone(), Sibling: f.Sibling, Predicate: f.Predicate} } func (f *followingQuery) position() int { return f.posit } // precedingQuery is an XPath preceding node query.(preceding::*) type precedingQuery struct { iterator func() NodeNavigator posit int Input query Sibling bool // The matching sibling node of current node. Predicate func(NodeNavigator) bool } func (p *precedingQuery) Select(t iterator) NodeNavigator { for { if p.iterator == nil { p.posit = 0 node := p.Input.Select(t) if node == nil { return nil } node = node.Copy() if p.Sibling { p.iterator = func() NodeNavigator { for { for !node.MoveToPrevious() { return nil } if p.Predicate(node) { p.posit++ return node } } } } else { var q query p.iterator = func() NodeNavigator { for { if q == nil { for !node.MoveToPrevious() { if !node.MoveToParent() { return nil } p.posit = 0 } q = &descendantQuery{ Self: true, Input: &contextQuery{}, Predicate: p.Predicate, } t.Current().MoveTo(node) } if node := q.Select(t); node != nil { p.posit++ return node } q = nil } } } } if node := p.iterator(); node != nil { return node } p.iterator = nil } } func (p *precedingQuery) Evaluate(t iterator) interface{} { p.Input.Evaluate(t) return p } func (p *precedingQuery) Test(n NodeNavigator) bool { return p.Predicate(n) } func (p *precedingQuery) Clone() query { return &precedingQuery{Input: p.Input.Clone(), Sibling: p.Sibling, Predicate: p.Predicate} } func (p *precedingQuery) position() int { return p.posit } // parentQuery is an XPath parent node query.(parent::*) type parentQuery struct { Input query Predicate func(NodeNavigator) bool } func (p *parentQuery) Select(t iterator) NodeNavigator { for { node := p.Input.Select(t) if node == nil { return nil } node = node.Copy() if node.MoveToParent() && p.Predicate(node) { return node } } } func (p *parentQuery) Evaluate(t iterator) interface{} { p.Input.Evaluate(t) return p } func (p *parentQuery) Clone() query { return &parentQuery{Input: p.Input.Clone(), Predicate: p.Predicate} } func (p *parentQuery) Test(n NodeNavigator) bool { return p.Predicate(n) } // selfQuery is an Self node query.(self::*) type selfQuery struct { Input query Predicate func(NodeNavigator) bool } func (s *selfQuery) Select(t iterator) NodeNavigator { for { node := s.Input.Select(t) if node == nil { return nil } if s.Predicate(node) { return node } } } func (s *selfQuery) Evaluate(t iterator) interface{} { s.Input.Evaluate(t) return s } func (s *selfQuery) Test(n NodeNavigator) bool { return s.Predicate(n) } func (s *selfQuery) Clone() query { return &selfQuery{Input: s.Input.Clone(), Predicate: s.Predicate} } // filterQuery is an XPath query for predicate filter. type filterQuery struct { Input query Predicate query posit int positmap map[int]int } func (f *filterQuery) do(t iterator) bool { val := reflect.ValueOf(f.Predicate.Evaluate(t)) switch val.Kind() { case reflect.Bool: return val.Bool() case reflect.String: return len(val.String()) > 0 case reflect.Float64: pt := getNodePosition(f.Input) return int(val.Float()) == pt default: if f.Predicate != nil { return f.Predicate.Select(t) != nil } } return false } func (f *filterQuery) position() int { return f.posit } func (f *filterQuery) Select(t iterator) NodeNavigator { if f.positmap == nil { f.positmap = make(map[int]int) } for { node := f.Input.Select(t) if node == nil { return nil } node = node.Copy() t.Current().MoveTo(node) if f.do(t) { // fix https://github.com/antchfx/htmlquery/issues/26 // Calculate and keep the each of matching node's position in the same depth. level := getNodeDepth(f.Input) f.positmap[level]++ f.posit = f.positmap[level] return node } } } func (f *filterQuery) Evaluate(t iterator) interface{} { f.Input.Evaluate(t) return f } func (f *filterQuery) Clone() query { return &filterQuery{Input: f.Input.Clone(), Predicate: f.Predicate.Clone()} } // functionQuery is an XPath function that returns a computed value for // the Evaluate call of the current NodeNavigator node. Select call isn't // applicable for functionQuery. type functionQuery struct { Input query // Node Set Func func(query, iterator) interface{} // The xpath function. } func (f *functionQuery) Select(t iterator) NodeNavigator { return nil } // Evaluate call a specified function that will returns the // following value type: number,string,boolean. func (f *functionQuery) Evaluate(t iterator) interface{} { return f.Func(f.Input, t) } func (f *functionQuery) Clone() query { return &functionQuery{Input: f.Input.Clone(), Func: f.Func} } // transformFunctionQuery diffs from functionQuery where the latter computes a scalar // value (number,string,boolean) for the current NodeNavigator node while the former // (transformFunctionQuery) performs a mapping or transform of the current NodeNavigator // and returns a new NodeNavigator. It is used for non-scalar XPath functions such as // reverse(), remove(), subsequence(), unordered(), etc. type transformFunctionQuery struct { Input query Func func(query, iterator) func() NodeNavigator iterator func() NodeNavigator } func (f *transformFunctionQuery) Select(t iterator) NodeNavigator { if f.iterator == nil { f.iterator = f.Func(f.Input, t) } return f.iterator() } func (f *transformFunctionQuery) Evaluate(t iterator) interface{} { f.Input.Evaluate(t) f.iterator = nil return f } func (f *transformFunctionQuery) Clone() query { return &transformFunctionQuery{Input: f.Input.Clone(), Func: f.Func} } // constantQuery is an XPath constant operand. type constantQuery struct { Val interface{} } func (c *constantQuery) Select(t iterator) NodeNavigator { return nil } func (c *constantQuery) Evaluate(t iterator) interface{} { return c.Val } func (c *constantQuery) Clone() query { return c } type groupQuery struct { posit int Input query } func (g *groupQuery) Select(t iterator) NodeNavigator { node := g.Input.Select(t) if node == nil { return nil } g.posit++ return node } func (g *groupQuery) Evaluate(t iterator) interface{} { return g.Input.Evaluate(t) } func (g *groupQuery) Clone() query { return &groupQuery{Input: g.Input.Clone()} } func (g *groupQuery) position() int { return g.posit } // logicalQuery is an XPath logical expression. type logicalQuery struct { Left, Right query Do func(iterator, interface{}, interface{}) interface{} } func (l *logicalQuery) Select(t iterator) NodeNavigator { // When a XPath expr is logical expression. node := t.Current().Copy() val := l.Evaluate(t) switch val.(type) { case bool: if val.(bool) == true { return node } } return nil } func (l *logicalQuery) Evaluate(t iterator) interface{} { m := l.Left.Evaluate(t) n := l.Right.Evaluate(t) return l.Do(t, m, n) } func (l *logicalQuery) Clone() query { return &logicalQuery{Left: l.Left.Clone(), Right: l.Right.Clone(), Do: l.Do} } // numericQuery is an XPath numeric operator expression. type numericQuery struct { Left, Right query Do func(interface{}, interface{}) interface{} } func (n *numericQuery) Select(t iterator) NodeNavigator { return nil } func (n *numericQuery) Evaluate(t iterator) interface{} { m := n.Left.Evaluate(t) k := n.Right.Evaluate(t) return n.Do(m, k) } func (n *numericQuery) Clone() query { return &numericQuery{Left: n.Left.Clone(), Right: n.Right.Clone(), Do: n.Do} } type booleanQuery struct { IsOr bool Left, Right query iterator func() NodeNavigator } func (b *booleanQuery) Select(t iterator) NodeNavigator { if b.iterator == nil { var list []NodeNavigator i := 0 root := t.Current().Copy() if b.IsOr { for { node := b.Left.Select(t) if node == nil { break } node = node.Copy() list = append(list, node) } t.Current().MoveTo(root) for { node := b.Right.Select(t) if node == nil { break } node = node.Copy() list = append(list, node) } } else { var m []NodeNavigator var n []NodeNavigator for { node := b.Left.Select(t) if node == nil { break } node = node.Copy() list = append(m, node) } t.Current().MoveTo(root) for { node := b.Right.Select(t) if node == nil { break } node = node.Copy() list = append(n, node) } for _, k := range m { for _, j := range n { if k == j { list = append(list, k) } } } } b.iterator = func() NodeNavigator { if i >= len(list) { return nil } node := list[i] i++ return node } } return b.iterator() } func (b *booleanQuery) Evaluate(t iterator) interface{} { n := t.Current().Copy() m := b.Left.Evaluate(t) left := asBool(t, m) if b.IsOr && left { return true } else if !b.IsOr && !left { return false } t.Current().MoveTo(n) m = b.Right.Evaluate(t) return asBool(t, m) } func (b *booleanQuery) Clone() query { return &booleanQuery{IsOr: b.IsOr, Left: b.Left.Clone(), Right: b.Right.Clone()} } type unionQuery struct { Left, Right query iterator func() NodeNavigator } func (u *unionQuery) Select(t iterator) NodeNavigator { if u.iterator == nil { var list []NodeNavigator var m = make(map[uint64]bool) root := t.Current().Copy() for { node := u.Left.Select(t) if node == nil { break } code := getHashCode(node.Copy()) if _, ok := m[code]; !ok { m[code] = true list = append(list, node.Copy()) } } t.Current().MoveTo(root) for { node := u.Right.Select(t) if node == nil { break } code := getHashCode(node.Copy()) if _, ok := m[code]; !ok { m[code] = true list = append(list, node.Copy()) } } var i int u.iterator = func() NodeNavigator { if i >= len(list) { return nil } node := list[i] i++ return node } } return u.iterator() } func (u *unionQuery) Evaluate(t iterator) interface{} { u.iterator = nil u.Left.Evaluate(t) u.Right.Evaluate(t) return u } func (u *unionQuery) Clone() query { return &unionQuery{Left: u.Left.Clone(), Right: u.Right.Clone()} } type lastQuery struct { buffer []NodeNavigator counted bool Input query } func (q *lastQuery) Select(t iterator) NodeNavigator { return nil } func (q *lastQuery) Evaluate(t iterator) interface{} { if !q.counted { for { node := q.Input.Select(t) if node == nil { break } q.buffer = append(q.buffer, node.Copy()) } q.counted = true } return float64(len(q.buffer)) } func (q *lastQuery) Clone() query { return &lastQuery{Input: q.Input.Clone()} } func getHashCode(n NodeNavigator) uint64 { var sb bytes.Buffer switch n.NodeType() { case AttributeNode, TextNode, CommentNode: sb.WriteString(fmt.Sprintf("%s=%s", n.LocalName(), n.Value())) // https://github.com/antchfx/htmlquery/issues/25 d := 1 for n.MoveToPrevious() { d++ } sb.WriteString(fmt.Sprintf("-%d", d)) for n.MoveToParent() { d = 1 for n.MoveToPrevious() { d++ } sb.WriteString(fmt.Sprintf("-%d", d)) } case ElementNode: sb.WriteString(n.Prefix() + n.LocalName()) d := 1 for n.MoveToPrevious() { d++ } sb.WriteString(fmt.Sprintf("-%d", d)) for n.MoveToParent() { d = 1 for n.MoveToPrevious() { d++ } sb.WriteString(fmt.Sprintf("-%d", d)) } } h := fnv.New64a() h.Write([]byte(sb.String())) return h.Sum64() } func getNodePosition(q query) int { type Position interface { position() int } if count, ok := q.(Position); ok { return count.position() } return 1 } func getNodeDepth(q query) int { type Depth interface { depth() int } if count, ok := q.(Depth); ok { return count.depth() } return 0 } ================================================ FILE: vendor/github.com/antchfx/xpath/xpath.go ================================================ package xpath import ( "errors" "fmt" ) // NodeType represents a type of XPath node. type NodeType int const ( // RootNode is a root node of the XML document or node tree. RootNode NodeType = iota // ElementNode is an element, such as . ElementNode // AttributeNode is an attribute, such as id='123'. AttributeNode // TextNode is the text content of a node. TextNode // CommentNode is a comment node, such as CommentNode // allNode is any types of node, used by xpath package only to predicate match. allNode ) // NodeNavigator provides cursor model for navigating XML data. type NodeNavigator interface { // NodeType returns the XPathNodeType of the current node. NodeType() NodeType // LocalName gets the Name of the current node. LocalName() string // Prefix returns namespace prefix associated with the current node. Prefix() string // Value gets the value of current node. Value() string // Copy does a deep copy of the NodeNavigator and all its components. Copy() NodeNavigator // MoveToRoot moves the NodeNavigator to the root node of the current node. MoveToRoot() // MoveToParent moves the NodeNavigator to the parent node of the current node. MoveToParent() bool // MoveToNextAttribute moves the NodeNavigator to the next attribute on current node. MoveToNextAttribute() bool // MoveToChild moves the NodeNavigator to the first child node of the current node. MoveToChild() bool // MoveToFirst moves the NodeNavigator to the first sibling node of the current node. MoveToFirst() bool // MoveToNext moves the NodeNavigator to the next sibling node of the current node. MoveToNext() bool // MoveToPrevious moves the NodeNavigator to the previous sibling node of the current node. MoveToPrevious() bool // MoveTo moves the NodeNavigator to the same position as the specified NodeNavigator. MoveTo(NodeNavigator) bool } // NodeIterator holds all matched Node object. type NodeIterator struct { node NodeNavigator query query } // Current returns current node which matched. func (t *NodeIterator) Current() NodeNavigator { return t.node } // MoveNext moves Navigator to the next match node. func (t *NodeIterator) MoveNext() bool { n := t.query.Select(t) if n != nil { if !t.node.MoveTo(n) { t.node = n.Copy() } return true } return false } // Select selects a node set using the specified XPath expression. // This method is deprecated, recommend using Expr.Select() method instead. func Select(root NodeNavigator, expr string) *NodeIterator { exp, err := Compile(expr) if err != nil { panic(err) } return exp.Select(root) } // Expr is an XPath expression for query. type Expr struct { s string q query } type iteratorFunc func() NodeNavigator func (f iteratorFunc) Current() NodeNavigator { return f() } // Evaluate returns the result of the expression. // The result type of the expression is one of the follow: bool,float64,string,NodeIterator). func (expr *Expr) Evaluate(root NodeNavigator) interface{} { val := expr.q.Evaluate(iteratorFunc(func() NodeNavigator { return root })) switch val.(type) { case query: return &NodeIterator{query: expr.q.Clone(), node: root} } return val } // Select selects a node set using the specified XPath expression. func (expr *Expr) Select(root NodeNavigator) *NodeIterator { return &NodeIterator{query: expr.q.Clone(), node: root} } // String returns XPath expression string. func (expr *Expr) String() string { return expr.s } // Compile compiles an XPath expression string. func Compile(expr string) (*Expr, error) { if expr == "" { return nil, errors.New("expr expression is nil") } qy, err := build(expr, nil) if err != nil { return nil, err } if qy == nil { return nil, fmt.Errorf(fmt.Sprintf("undeclared variable in XPath expression: %s", expr)) } return &Expr{s: expr, q: qy}, nil } // MustCompile compiles an XPath expression string and ignored error. func MustCompile(expr string) *Expr { exp, err := Compile(expr) if err != nil { return &Expr{s: expr, q: nopQuery{}} } return exp } // CompileWithNS compiles an XPath expression string, using given namespaces map. func CompileWithNS(expr string, namespaces map[string]string) (*Expr, error) { if expr == "" { return nil, errors.New("expr expression is nil") } qy, err := build(expr, namespaces) if err != nil { return nil, err } if qy == nil { return nil, fmt.Errorf(fmt.Sprintf("undeclared variable in XPath expression: %s", expr)) } return &Expr{s: expr, q: qy}, nil } ================================================ FILE: vendor/github.com/armon/go-radix/.gitignore ================================================ # Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe ================================================ FILE: vendor/github.com/armon/go-radix/.travis.yml ================================================ language: go go: - tip ================================================ FILE: vendor/github.com/armon/go-radix/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2014 Armon Dadgar Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/armon/go-radix/README.md ================================================ go-radix [![Build Status](https://travis-ci.org/armon/go-radix.png)](https://travis-ci.org/armon/go-radix) ========= Provides the `radix` package that implements a [radix tree](http://en.wikipedia.org/wiki/Radix_tree). The package only provides a single `Tree` implementation, optimized for sparse nodes. As a radix tree, it provides the following: * O(k) operations. In many cases, this can be faster than a hash table since the hash function is an O(k) operation, and hash tables have very poor cache locality. * Minimum / Maximum value lookups * Ordered iteration For an immutable variant, see [go-immutable-radix](https://github.com/hashicorp/go-immutable-radix). Documentation ============= The full documentation is available on [Godoc](http://godoc.org/github.com/armon/go-radix). Example ======= Below is a simple example of usage ```go // Create a tree r := radix.New() r.Insert("foo", 1) r.Insert("bar", 2) r.Insert("foobar", 2) // Find the longest prefix match m, _, _ := r.LongestPrefix("foozip") if m != "foo" { panic("should be foo") } ``` ================================================ FILE: vendor/github.com/armon/go-radix/radix.go ================================================ package radix import ( "sort" "strings" ) // WalkFn is used when walking the tree. Takes a // key and value, returning if iteration should // be terminated. type WalkFn func(s string, v interface{}) bool // leafNode is used to represent a value type leafNode struct { key string val interface{} } // edge is used to represent an edge node type edge struct { label byte node *node } type node struct { // leaf is used to store possible leaf leaf *leafNode // prefix is the common prefix we ignore prefix string // Edges should be stored in-order for iteration. // We avoid a fully materialized slice to save memory, // since in most cases we expect to be sparse edges edges } func (n *node) isLeaf() bool { return n.leaf != nil } func (n *node) addEdge(e edge) { n.edges = append(n.edges, e) n.edges.Sort() } func (n *node) updateEdge(label byte, node *node) { num := len(n.edges) idx := sort.Search(num, func(i int) bool { return n.edges[i].label >= label }) if idx < num && n.edges[idx].label == label { n.edges[idx].node = node return } panic("replacing missing edge") } func (n *node) getEdge(label byte) *node { num := len(n.edges) idx := sort.Search(num, func(i int) bool { return n.edges[i].label >= label }) if idx < num && n.edges[idx].label == label { return n.edges[idx].node } return nil } func (n *node) delEdge(label byte) { num := len(n.edges) idx := sort.Search(num, func(i int) bool { return n.edges[i].label >= label }) if idx < num && n.edges[idx].label == label { copy(n.edges[idx:], n.edges[idx+1:]) n.edges[len(n.edges)-1] = edge{} n.edges = n.edges[:len(n.edges)-1] } } type edges []edge func (e edges) Len() int { return len(e) } func (e edges) Less(i, j int) bool { return e[i].label < e[j].label } func (e edges) Swap(i, j int) { e[i], e[j] = e[j], e[i] } func (e edges) Sort() { sort.Sort(e) } // Tree implements a radix tree. This can be treated as a // Dictionary abstract data type. The main advantage over // a standard hash map is prefix-based lookups and // ordered iteration, type Tree struct { root *node size int } // New returns an empty Tree func New() *Tree { return NewFromMap(nil) } // NewFromMap returns a new tree containing the keys // from an existing map func NewFromMap(m map[string]interface{}) *Tree { t := &Tree{root: &node{}} for k, v := range m { t.Insert(k, v) } return t } // Len is used to return the number of elements in the tree func (t *Tree) Len() int { return t.size } // longestPrefix finds the length of the shared prefix // of two strings func longestPrefix(k1, k2 string) int { max := len(k1) if l := len(k2); l < max { max = l } var i int for i = 0; i < max; i++ { if k1[i] != k2[i] { break } } return i } // Insert is used to add a newentry or update // an existing entry. Returns if updated. func (t *Tree) Insert(s string, v interface{}) (interface{}, bool) { var parent *node n := t.root search := s for { // Handle key exhaution if len(search) == 0 { if n.isLeaf() { old := n.leaf.val n.leaf.val = v return old, true } n.leaf = &leafNode{ key: s, val: v, } t.size++ return nil, false } // Look for the edge parent = n n = n.getEdge(search[0]) // No edge, create one if n == nil { e := edge{ label: search[0], node: &node{ leaf: &leafNode{ key: s, val: v, }, prefix: search, }, } parent.addEdge(e) t.size++ return nil, false } // Determine longest prefix of the search key on match commonPrefix := longestPrefix(search, n.prefix) if commonPrefix == len(n.prefix) { search = search[commonPrefix:] continue } // Split the node t.size++ child := &node{ prefix: search[:commonPrefix], } parent.updateEdge(search[0], child) // Restore the existing node child.addEdge(edge{ label: n.prefix[commonPrefix], node: n, }) n.prefix = n.prefix[commonPrefix:] // Create a new leaf node leaf := &leafNode{ key: s, val: v, } // If the new key is a subset, add to to this node search = search[commonPrefix:] if len(search) == 0 { child.leaf = leaf return nil, false } // Create a new edge for the node child.addEdge(edge{ label: search[0], node: &node{ leaf: leaf, prefix: search, }, }) return nil, false } } // Delete is used to delete a key, returning the previous // value and if it was deleted func (t *Tree) Delete(s string) (interface{}, bool) { var parent *node var label byte n := t.root search := s for { // Check for key exhaution if len(search) == 0 { if !n.isLeaf() { break } goto DELETE } // Look for an edge parent = n label = search[0] n = n.getEdge(label) if n == nil { break } // Consume the search prefix if strings.HasPrefix(search, n.prefix) { search = search[len(n.prefix):] } else { break } } return nil, false DELETE: // Delete the leaf leaf := n.leaf n.leaf = nil t.size-- // Check if we should delete this node from the parent if parent != nil && len(n.edges) == 0 { parent.delEdge(label) } // Check if we should merge this node if n != t.root && len(n.edges) == 1 { n.mergeChild() } // Check if we should merge the parent's other child if parent != nil && parent != t.root && len(parent.edges) == 1 && !parent.isLeaf() { parent.mergeChild() } return leaf.val, true } // DeletePrefix is used to delete the subtree under a prefix // Returns how many nodes were deleted // Use this to delete large subtrees efficiently func (t *Tree) DeletePrefix(s string) int { return t.deletePrefix(nil, t.root, s) } // delete does a recursive deletion func (t *Tree) deletePrefix(parent, n *node, prefix string) int { // Check for key exhaustion if len(prefix) == 0 { // Remove the leaf node subTreeSize := 0 //recursively walk from all edges of the node to be deleted recursiveWalk(n, func(s string, v interface{}) bool { subTreeSize++ return false }) if n.isLeaf() { n.leaf = nil } n.edges = nil // deletes the entire subtree // Check if we should merge the parent's other child if parent != nil && parent != t.root && len(parent.edges) == 1 && !parent.isLeaf() { parent.mergeChild() } t.size -= subTreeSize return subTreeSize } // Look for an edge label := prefix[0] child := n.getEdge(label) if child == nil || (!strings.HasPrefix(child.prefix, prefix) && !strings.HasPrefix(prefix, child.prefix)) { return 0 } // Consume the search prefix if len(child.prefix) > len(prefix) { prefix = prefix[len(prefix):] } else { prefix = prefix[len(child.prefix):] } return t.deletePrefix(n, child, prefix) } func (n *node) mergeChild() { e := n.edges[0] child := e.node n.prefix = n.prefix + child.prefix n.leaf = child.leaf n.edges = child.edges } // Get is used to lookup a specific key, returning // the value and if it was found func (t *Tree) Get(s string) (interface{}, bool) { n := t.root search := s for { // Check for key exhaution if len(search) == 0 { if n.isLeaf() { return n.leaf.val, true } break } // Look for an edge n = n.getEdge(search[0]) if n == nil { break } // Consume the search prefix if strings.HasPrefix(search, n.prefix) { search = search[len(n.prefix):] } else { break } } return nil, false } // LongestPrefix is like Get, but instead of an // exact match, it will return the longest prefix match. func (t *Tree) LongestPrefix(s string) (string, interface{}, bool) { var last *leafNode n := t.root search := s for { // Look for a leaf node if n.isLeaf() { last = n.leaf } // Check for key exhaution if len(search) == 0 { break } // Look for an edge n = n.getEdge(search[0]) if n == nil { break } // Consume the search prefix if strings.HasPrefix(search, n.prefix) { search = search[len(n.prefix):] } else { break } } if last != nil { return last.key, last.val, true } return "", nil, false } // Minimum is used to return the minimum value in the tree func (t *Tree) Minimum() (string, interface{}, bool) { n := t.root for { if n.isLeaf() { return n.leaf.key, n.leaf.val, true } if len(n.edges) > 0 { n = n.edges[0].node } else { break } } return "", nil, false } // Maximum is used to return the maximum value in the tree func (t *Tree) Maximum() (string, interface{}, bool) { n := t.root for { if num := len(n.edges); num > 0 { n = n.edges[num-1].node continue } if n.isLeaf() { return n.leaf.key, n.leaf.val, true } break } return "", nil, false } // Walk is used to walk the tree func (t *Tree) Walk(fn WalkFn) { recursiveWalk(t.root, fn) } // WalkPrefix is used to walk the tree under a prefix func (t *Tree) WalkPrefix(prefix string, fn WalkFn) { n := t.root search := prefix for { // Check for key exhaution if len(search) == 0 { recursiveWalk(n, fn) return } // Look for an edge n = n.getEdge(search[0]) if n == nil { break } // Consume the search prefix if strings.HasPrefix(search, n.prefix) { search = search[len(n.prefix):] } else if strings.HasPrefix(n.prefix, search) { // Child may be under our search prefix recursiveWalk(n, fn) return } else { break } } } // WalkPath is used to walk the tree, but only visiting nodes // from the root down to a given leaf. Where WalkPrefix walks // all the entries *under* the given prefix, this walks the // entries *above* the given prefix. func (t *Tree) WalkPath(path string, fn WalkFn) { n := t.root search := path for { // Visit the leaf values if any if n.leaf != nil && fn(n.leaf.key, n.leaf.val) { return } // Check for key exhaution if len(search) == 0 { return } // Look for an edge n = n.getEdge(search[0]) if n == nil { return } // Consume the search prefix if strings.HasPrefix(search, n.prefix) { search = search[len(n.prefix):] } else { break } } } // recursiveWalk is used to do a pre-order walk of a node // recursively. Returns true if the walk should be aborted func recursiveWalk(n *node, fn WalkFn) bool { // Visit the leaf values if any if n.leaf != nil && fn(n.leaf.key, n.leaf.val) { return true } // Recurse on the children for _, e := range n.edges { if recursiveWalk(e.node, fn) { return true } } return false } // ToMap is used to walk the tree and convert it into a map func (t *Tree) ToMap() map[string]interface{} { out := make(map[string]interface{}, t.size) t.Walk(func(k string, v interface{}) bool { out[k] = v return false }) return out } ================================================ FILE: vendor/github.com/bmatcuk/doublestar/.gitignore ================================================ # vi *~ *.swp *.swo # Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof # test directory test/ ================================================ FILE: vendor/github.com/bmatcuk/doublestar/.travis.yml ================================================ language: go go: - 1.12 - 1.13 - 1.14 os: - linux - windows before_install: - go get -t -v ./... script: - go test -race -coverprofile=coverage.txt -covermode=atomic after_success: - bash <(curl -s https://codecov.io/bash) ================================================ FILE: vendor/github.com/bmatcuk/doublestar/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2014 Bob Matcuk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/bmatcuk/doublestar/README.md ================================================ # doublestar Path pattern matching and globbing supporting `doublestar` (`**`) patterns. ![Release](https://img.shields.io/github/release/bmatcuk/doublestar.svg?branch=master) [![Build Status](https://travis-ci.org/bmatcuk/doublestar.svg?branch=master)](https://travis-ci.org/bmatcuk/doublestar) [![codecov.io](https://img.shields.io/codecov/c/github/bmatcuk/doublestar.svg?branch=master)](https://codecov.io/github/bmatcuk/doublestar?branch=master) ## About **doublestar** is a [golang](http://golang.org/) implementation of path pattern matching and globbing with support for "doublestar" (aka globstar: `**`) patterns. doublestar patterns match files and directories recursively. For example, if you had the following directory structure: ```bash grandparent `-- parent |-- child1 `-- child2 ``` You could find the children with patterns such as: `**/child*`, `grandparent/**/child?`, `**/parent/*`, or even just `**` by itself (which will return all files and directories recursively). Bash's globstar is doublestar's inspiration and, as such, works similarly. Note that the doublestar must appear as a path component by itself. A pattern such as `/path**` is invalid and will be treated the same as `/path*`, but `/path*/**` should achieve the desired result. Additionally, `/path/**` will match all directories and files under the path directory, but `/path/**/` will only match directories. ## Installation **doublestar** can be installed via `go get`: ```bash go get github.com/bmatcuk/doublestar ``` To use it in your code, you must import it: ```go import "github.com/bmatcuk/doublestar" ``` ## Usage ### Match ```go func Match(pattern, name string) (bool, error) ``` Match returns true if `name` matches the file name `pattern` ([see below](#patterns)). `name` and `pattern` are split on forward slash (`/`) characters and may be relative or absolute. Note: `Match()` is meant to be a drop-in replacement for `path.Match()`. As such, it always uses `/` as the path separator. If you are writing code that will run on systems where `/` is not the path separator (such as Windows), you want to use `PathMatch()` (below) instead. ### PathMatch ```go func PathMatch(pattern, name string) (bool, error) ``` PathMatch returns true if `name` matches the file name `pattern` ([see below](#patterns)). The difference between Match and PathMatch is that PathMatch will automatically use your system's path separator to split `name` and `pattern`. `PathMatch()` is meant to be a drop-in replacement for `filepath.Match()`. ### Glob ```go func Glob(pattern string) ([]string, error) ``` Glob finds all files and directories in the filesystem that match `pattern` ([see below](#patterns)). `pattern` may be relative (to the current working directory), or absolute. `Glob()` is meant to be a drop-in replacement for `filepath.Glob()`. ### Patterns **doublestar** supports the following special terms in the patterns: Special Terms | Meaning ------------- | ------- `*` | matches any sequence of non-path-separators `**` | matches any sequence of characters, including path separators `?` | matches any single non-path-separator character `[class]` | matches any single non-path-separator character against a class of characters ([see below](#character-classes)) `{alt1,...}` | matches a sequence of characters if one of the comma-separated alternatives matches Any character with a special meaning can be escaped with a backslash (`\`). #### Character Classes Character classes support the following: Class | Meaning ---------- | ------- `[abc]` | matches any single character within the set `[a-z]` | matches any single character in the range `[^class]` | matches any single character which does *not* match the class ### Abstracting the `os` package **doublestar** by default uses the `Open`, `Stat`, and `Lstat`, functions and `PathSeparator` value from the standard library's `os` package. To abstract this, for example to be able to perform tests of Windows paths on Linux, or to interoperate with your own filesystem code, it includes the functions `GlobOS` and `PathMatchOS` which are identical to `Glob` and `PathMatch` except that they operate on an `OS` interface: ```go type OS interface { Lstat(name string) (os.FileInfo, error) Open(name string) (*os.File, error) PathSeparator() rune Stat(name string) (os.FileInfo, error) } ``` `StandardOS` is a value that implements this interface by calling functions in the standard library's `os` package. ## License [MIT License](LICENSE) ================================================ FILE: vendor/github.com/bmatcuk/doublestar/doublestar.go ================================================ package doublestar import ( "fmt" "os" "path" "path/filepath" "sort" "strings" "unicode/utf8" ) // An OS abstracts functions in the standard library's os package. type OS interface { Lstat(name string) (os.FileInfo, error) Open(name string) (*os.File, error) PathSeparator() rune Stat(name string) (os.FileInfo, error) } // StandardOS is a value that implements the OS interface by calling functions // in the standard libray's os package. var StandardOS OS = standardOS{} // A standardOS implements OS by calling functions in the standard library's os // package. type standardOS struct{} func (standardOS) Lstat(name string) (os.FileInfo, error) { return os.Lstat(name) } func (standardOS) Open(name string) (*os.File, error) { return os.Open(name) } func (standardOS) PathSeparator() rune { return os.PathSeparator } func (standardOS) Stat(name string) (os.FileInfo, error) { return os.Stat(name) } // ErrBadPattern indicates a pattern was malformed. var ErrBadPattern = path.ErrBadPattern // Split a path on the given separator, respecting escaping. func splitPathOnSeparator(path string, separator rune) (ret []string) { idx := 0 if separator == '\\' { // if the separator is '\\', then we can just split... ret = strings.Split(path, string(separator)) idx = len(ret) } else { // otherwise, we need to be careful of situations where the separator was escaped cnt := strings.Count(path, string(separator)) if cnt == 0 { return []string{path} } ret = make([]string, cnt+1) pathlen := len(path) separatorLen := utf8.RuneLen(separator) emptyEnd := false for start := 0; start < pathlen; { end := indexRuneWithEscaping(path[start:], separator) if end == -1 { emptyEnd = false end = pathlen } else { emptyEnd = true end += start } ret[idx] = path[start:end] start = end + separatorLen idx++ } // If the last rune is a path separator, we need to append an empty string to // represent the last, empty path component. By default, the strings from // make([]string, ...) will be empty, so we just need to icrement the count if emptyEnd { idx++ } } return ret[:idx] } // Find the first index of a rune in a string, // ignoring any times the rune is escaped using "\". func indexRuneWithEscaping(s string, r rune) int { end := strings.IndexRune(s, r) if end == -1 { return -1 } if end > 0 && s[end-1] == '\\' { start := end + utf8.RuneLen(r) end = indexRuneWithEscaping(s[start:], r) if end != -1 { end += start } } return end } // Find the last index of a rune in a string, // ignoring any times the rune is escaped using "\". func lastIndexRuneWithEscaping(s string, r rune) int { end := strings.LastIndex(s, string(r)) if end == -1 { return -1 } if end > 0 && s[end-1] == '\\' { end = lastIndexRuneWithEscaping(s[:end-1], r) } return end } // Find the index of the first instance of one of the unicode characters in // chars, ignoring any times those characters are escaped using "\". func indexAnyWithEscaping(s, chars string) int { end := strings.IndexAny(s, chars) if end == -1 { return -1 } if end > 0 && s[end-1] == '\\' { _, adj := utf8.DecodeRuneInString(s[end:]) start := end + adj end = indexAnyWithEscaping(s[start:], chars) if end != -1 { end += start } } return end } // Split a set of alternatives such as {alt1,alt2,...} and returns the index of // the rune after the closing curly brace. Respects nested alternatives and // escaped runes. func splitAlternatives(s string) (ret []string, idx int) { ret = make([]string, 0, 2) idx = 0 slen := len(s) braceCnt := 1 esc := false start := 0 for braceCnt > 0 { if idx >= slen { return nil, -1 } sRune, adj := utf8.DecodeRuneInString(s[idx:]) if esc { esc = false } else if sRune == '\\' { esc = true } else if sRune == '{' { braceCnt++ } else if sRune == '}' { braceCnt-- } else if sRune == ',' && braceCnt == 1 { ret = append(ret, s[start:idx]) start = idx + adj } idx += adj } ret = append(ret, s[start:idx-1]) return } // Returns true if the pattern is "zero length", meaning // it could match zero or more characters. func isZeroLengthPattern(pattern string) (ret bool, err error) { // * can match zero if pattern == "" || pattern == "*" || pattern == "**" { return true, nil } // an alternative with zero length can match zero, for example {,x} - the // first alternative has zero length r, adj := utf8.DecodeRuneInString(pattern) if r == '{' { options, endOptions := splitAlternatives(pattern[adj:]) if endOptions == -1 { return false, ErrBadPattern } if ret, err = isZeroLengthPattern(pattern[adj+endOptions:]); !ret || err != nil { return } for _, o := range options { if ret, err = isZeroLengthPattern(o); ret || err != nil { return } } } return false, nil } // Match returns true if name matches the shell file name pattern. // The pattern syntax is: // // pattern: // { term } // term: // '*' matches any sequence of non-path-separators // '**' matches any sequence of characters, including // path separators. // '?' matches any single non-path-separator character // '[' [ '^' ] { character-range } ']' // character class (must be non-empty) // '{' { term } [ ',' { term } ... ] '}' // c matches character c (c != '*', '?', '\\', '[') // '\\' c matches character c // // character-range: // c matches character c (c != '\\', '-', ']') // '\\' c matches character c // lo '-' hi matches character c for lo <= c <= hi // // Match requires pattern to match all of name, not just a substring. // The path-separator defaults to the '/' character. The only possible // returned error is ErrBadPattern, when pattern is malformed. // // Note: this is meant as a drop-in replacement for path.Match() which // always uses '/' as the path separator. If you want to support systems // which use a different path separator (such as Windows), what you want // is the PathMatch() function below. // func Match(pattern, name string) (bool, error) { return matchWithSeparator(pattern, name, '/') } // PathMatch is like Match except that it uses your system's path separator. // For most systems, this will be '/'. However, for Windows, it would be '\\'. // Note that for systems where the path separator is '\\', escaping is // disabled. // // Note: this is meant as a drop-in replacement for filepath.Match(). // func PathMatch(pattern, name string) (bool, error) { return PathMatchOS(StandardOS, pattern, name) } // PathMatchOS is like PathMatch except that it uses vos's path separator. func PathMatchOS(vos OS, pattern, name string) (bool, error) { pattern = filepath.ToSlash(pattern) return matchWithSeparator(pattern, name, vos.PathSeparator()) } // Match returns true if name matches the shell file name pattern. // The pattern syntax is: // // pattern: // { term } // term: // '*' matches any sequence of non-path-separators // '**' matches any sequence of characters, including // path separators. // '?' matches any single non-path-separator character // '[' [ '^' ] { character-range } ']' // character class (must be non-empty) // '{' { term } [ ',' { term } ... ] '}' // c matches character c (c != '*', '?', '\\', '[') // '\\' c matches character c // // character-range: // c matches character c (c != '\\', '-', ']') // '\\' c matches character c, unless separator is '\\' // lo '-' hi matches character c for lo <= c <= hi // // Match requires pattern to match all of name, not just a substring. // The only possible returned error is ErrBadPattern, when pattern // is malformed. // func matchWithSeparator(pattern, name string, separator rune) (bool, error) { nameComponents := splitPathOnSeparator(name, separator) return doMatching(pattern, nameComponents) } func doMatching(pattern string, nameComponents []string) (matched bool, err error) { // check for some base-cases patternLen, nameLen := len(pattern), len(nameComponents) if patternLen == 0 && nameLen == 0 { return true, nil } if patternLen == 0 { if nameLen == 1 && nameComponents[0] == "" { return true, nil } else if nameLen == 0 { return false, nil } } slashIdx := indexRuneWithEscaping(pattern, '/') lastComponent := slashIdx == -1 if lastComponent { slashIdx = len(pattern) } if pattern[:slashIdx] == "**" { // if our last pattern component is a doublestar, we're done - // doublestar will match any remaining name components, if any. if lastComponent { return true, nil } // otherwise, try matching remaining components for nameIdx := 0; nameIdx < nameLen; nameIdx++ { if m, _ := doMatching(pattern[slashIdx+1:], nameComponents[nameIdx:]); m { return true, nil } } return false, nil } var matches []string matches, err = matchComponent(pattern, nameComponents[0]) if matches == nil || err != nil { return } if len(matches) == 0 && nameLen == 1 { return true, nil } if nameLen > 1 { for _, alt := range matches { matched, err = doMatching(alt, nameComponents[1:]) if matched || err != nil { return } } } return false, nil } // Glob returns the names of all files matching pattern or nil // if there is no matching file. The syntax of pattern is the same // as in Match. The pattern may describe hierarchical names such as // /usr/*/bin/ed (assuming the Separator is '/'). // // Glob ignores file system errors such as I/O errors reading directories. // The only possible returned error is ErrBadPattern, when pattern // is malformed. // // Your system path separator is automatically used. This means on // systems where the separator is '\\' (Windows), escaping will be // disabled. // // Note: this is meant as a drop-in replacement for filepath.Glob(). // func Glob(pattern string) (matches []string, err error) { return GlobOS(StandardOS, pattern) } // GlobOS is like Glob except that it operates on vos. func GlobOS(vos OS, pattern string) (matches []string, err error) { if len(pattern) == 0 { return nil, nil } // if the pattern starts with alternatives, we need to handle that here - the // alternatives may be a mix of relative and absolute if pattern[0] == '{' { options, endOptions := splitAlternatives(pattern[1:]) if endOptions == -1 { return nil, ErrBadPattern } for _, o := range options { m, e := GlobOS(vos, o+pattern[endOptions+1:]) if e != nil { return nil, e } matches = append(matches, m...) } return matches, nil } // If the pattern is relative or absolute and we're on a non-Windows machine, // volumeName will be an empty string. If it is absolute and we're on a // Windows machine, volumeName will be a drive letter ("C:") for filesystem // paths or \\\ for UNC paths. isAbs := filepath.IsAbs(pattern) || pattern[0] == '\\' || pattern[0] == '/' volumeName := filepath.VolumeName(pattern) isWindowsUNC := strings.HasPrefix(volumeName, `\\`) if isWindowsUNC || isAbs { startIdx := len(volumeName) + 1 return doGlob(vos, fmt.Sprintf("%s%s", volumeName, string(vos.PathSeparator())), filepath.ToSlash(pattern[startIdx:]), matches) } // otherwise, it's a relative pattern return doGlob(vos, ".", filepath.ToSlash(pattern), matches) } // Perform a glob func doGlob(vos OS, basedir, pattern string, matches []string) (m []string, e error) { m = matches e = nil // if the pattern starts with any path components that aren't globbed (ie, // `path/to/glob*`), we can skip over the un-globbed components (`path/to` in // our example). globIdx := indexAnyWithEscaping(pattern, "*?[{\\") if globIdx > 0 { globIdx = lastIndexRuneWithEscaping(pattern[:globIdx], '/') } else if globIdx == -1 { globIdx = lastIndexRuneWithEscaping(pattern, '/') } if globIdx > 0 { basedir = filepath.Join(basedir, pattern[:globIdx]) pattern = pattern[globIdx+1:] } // Lstat will return an error if the file/directory doesn't exist fi, err := vos.Lstat(basedir) if err != nil { return } // if the pattern is empty, we've found a match if len(pattern) == 0 { m = append(m, basedir) return } // otherwise, we need to check each item in the directory... // first, if basedir is a symlink, follow it... if (fi.Mode() & os.ModeSymlink) != 0 { fi, err = vos.Stat(basedir) if err != nil { return } } // confirm it's a directory... if !fi.IsDir() { return } files, err := filesInDir(vos, basedir) if err != nil { return } sort.Slice(files, func(i, j int) bool { return files[i].Name() < files[j].Name() }) slashIdx := indexRuneWithEscaping(pattern, '/') lastComponent := slashIdx == -1 if lastComponent { slashIdx = len(pattern) } if pattern[:slashIdx] == "**" { // if the current component is a doublestar, we'll try depth-first for _, file := range files { // if symlink, we may want to follow if (file.Mode() & os.ModeSymlink) != 0 { file, err = vos.Stat(filepath.Join(basedir, file.Name())) if err != nil { continue } } if file.IsDir() { // recurse into directories if lastComponent { m = append(m, filepath.Join(basedir, file.Name())) } m, e = doGlob(vos, filepath.Join(basedir, file.Name()), pattern, m) } else if lastComponent { // if the pattern's last component is a doublestar, we match filenames, too m = append(m, filepath.Join(basedir, file.Name())) } } if lastComponent { return // we're done } pattern = pattern[slashIdx+1:] } // check items in current directory and recurse var match []string for _, file := range files { match, e = matchComponent(pattern, file.Name()) if e != nil { return } if match != nil { if len(match) == 0 { m = append(m, filepath.Join(basedir, file.Name())) } else { for _, alt := range match { m, e = doGlob(vos, filepath.Join(basedir, file.Name()), alt, m) } } } } return } func filesInDir(vos OS, dirPath string) (files []os.FileInfo, e error) { dir, err := vos.Open(dirPath) if err != nil { return nil, nil } defer func() { if err := dir.Close(); e == nil { e = err } }() files, err = dir.Readdir(-1) if err != nil { return nil, nil } return } // Attempt to match a single path component with a pattern. Note that the // pattern may include multiple components but that the "name" is just a single // path component. The return value is a slice of patterns that should be // checked against subsequent path components or nil, indicating that the // pattern does not match this path. It is assumed that pattern components are // separated by '/' func matchComponent(pattern, name string) ([]string, error) { // check for matches one rune at a time patternLen, nameLen := len(pattern), len(name) patIdx, nameIdx := 0, 0 for patIdx < patternLen && nameIdx < nameLen { patRune, patAdj := utf8.DecodeRuneInString(pattern[patIdx:]) nameRune, nameAdj := utf8.DecodeRuneInString(name[nameIdx:]) if patRune == '/' { patIdx++ break } else if patRune == '\\' { // handle escaped runes, only if separator isn't '\\' patIdx += patAdj patRune, patAdj = utf8.DecodeRuneInString(pattern[patIdx:]) if patRune == utf8.RuneError { return nil, ErrBadPattern } else if patRune == nameRune { patIdx += patAdj nameIdx += nameAdj } else { return nil, nil } } else if patRune == '*' { // handle stars - a star at the end of the pattern or before a separator // will always match the rest of the path component if patIdx += patAdj; patIdx >= patternLen { return []string{}, nil } if patRune, patAdj = utf8.DecodeRuneInString(pattern[patIdx:]); patRune == '/' { return []string{pattern[patIdx+patAdj:]}, nil } // check if we can make any matches for ; nameIdx < nameLen; nameIdx += nameAdj { if m, e := matchComponent(pattern[patIdx:], name[nameIdx:]); m != nil || e != nil { return m, e } _, nameAdj = utf8.DecodeRuneInString(name[nameIdx:]) } return nil, nil } else if patRune == '[' { // handle character sets patIdx += patAdj endClass := indexRuneWithEscaping(pattern[patIdx:], ']') if endClass == -1 { return nil, ErrBadPattern } endClass += patIdx classRunes := []rune(pattern[patIdx:endClass]) classRunesLen := len(classRunes) if classRunesLen > 0 { classIdx := 0 matchClass := false if classRunes[0] == '^' { classIdx++ } for classIdx < classRunesLen { low := classRunes[classIdx] if low == '-' { return nil, ErrBadPattern } classIdx++ if low == '\\' { if classIdx < classRunesLen { low = classRunes[classIdx] classIdx++ } else { return nil, ErrBadPattern } } high := low if classIdx < classRunesLen && classRunes[classIdx] == '-' { // we have a range of runes if classIdx++; classIdx >= classRunesLen { return nil, ErrBadPattern } high = classRunes[classIdx] if high == '-' { return nil, ErrBadPattern } classIdx++ if high == '\\' { if classIdx < classRunesLen { high = classRunes[classIdx] classIdx++ } else { return nil, ErrBadPattern } } } if low <= nameRune && nameRune <= high { matchClass = true } } if matchClass == (classRunes[0] == '^') { return nil, nil } } else { return nil, ErrBadPattern } patIdx = endClass + 1 nameIdx += nameAdj } else if patRune == '{' { // handle alternatives such as {alt1,alt2,...} patIdx += patAdj options, endOptions := splitAlternatives(pattern[patIdx:]) if endOptions == -1 { return nil, ErrBadPattern } patIdx += endOptions results := make([][]string, 0, len(options)) totalResults := 0 for _, o := range options { m, e := matchComponent(o+pattern[patIdx:], name[nameIdx:]) if e != nil { return nil, e } if m != nil { results = append(results, m) totalResults += len(m) } } if len(results) > 0 { lst := make([]string, 0, totalResults) for _, m := range results { lst = append(lst, m...) } return lst, nil } return nil, nil } else if patRune == '?' || patRune == nameRune { // handle single-rune wildcard patIdx += patAdj nameIdx += nameAdj } else { return nil, nil } } if nameIdx >= nameLen { if patIdx >= patternLen { return []string{}, nil } pattern = pattern[patIdx:] slashIdx := indexRuneWithEscaping(pattern, '/') testPattern := pattern if slashIdx >= 0 { testPattern = pattern[:slashIdx] } zeroLength, err := isZeroLengthPattern(testPattern) if err != nil { return nil, err } if zeroLength { if slashIdx == -1 { return []string{}, nil } else { return []string{pattern[slashIdx+1:]}, nil } } } return nil, nil } ================================================ FILE: vendor/github.com/bmatcuk/doublestar/v3/.gitignore ================================================ # vi *~ *.swp *.swo # Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof # test directory test/ ================================================ FILE: vendor/github.com/bmatcuk/doublestar/v3/.travis.yml ================================================ language: go go: - 1.13 - 1.14 - 1.15 os: - linux - windows before_install: - go get -t -v ./... script: - go test -race -coverprofile=coverage.txt -covermode=atomic after_success: - bash <(curl -s https://codecov.io/bash) ================================================ FILE: vendor/github.com/bmatcuk/doublestar/v3/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2014 Bob Matcuk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/bmatcuk/doublestar/v3/README.md ================================================ # doublestar Path pattern matching and globbing supporting `doublestar` (`**`) patterns. [![PkgGoDev](https://pkg.go.dev/badge/github.com/bmatcuk/doublestar)](https://pkg.go.dev/github.com/bmatcuk/doublestar/v2) [![Release](https://img.shields.io/github/release/bmatcuk/doublestar.svg?branch=master)](https://github.com/bmatcuk/doublestar/releases) [![Build Status](https://travis-ci.org/bmatcuk/doublestar.svg?branch=master)](https://travis-ci.org/bmatcuk/doublestar) [![codecov.io](https://img.shields.io/codecov/c/github/bmatcuk/doublestar.svg?branch=master)](https://codecov.io/github/bmatcuk/doublestar?branch=master) ## About #### [Upgrading to v2? To v3?](UPGRADING.md) **doublestar** is a [golang](http://golang.org/) implementation of path pattern matching and globbing with support for "doublestar" (aka globstar: `**`) patterns. doublestar patterns match files and directories recursively. For example, if you had the following directory structure: ```bash grandparent `-- parent |-- child1 `-- child2 ``` You could find the children with patterns such as: `**/child*`, `grandparent/**/child?`, `**/parent/*`, or even just `**` by itself (which will return all files and directories recursively). Bash's globstar is doublestar's inspiration and, as such, works similarly. Note that the doublestar must appear as a path component by itself. A pattern such as `/path**` is invalid and will be treated the same as `/path*`, but `/path*/**` should achieve the desired result. Additionally, `/path/**` will match all directories and files under the path directory, but `/path/**/` will only match directories. ## Installation **doublestar** can be installed via `go get`: ```bash go get github.com/bmatcuk/doublestar/v2 ``` To use it in your code, you must import it: ```go import "github.com/bmatcuk/doublestar/v2" ``` ## Usage ### Match ```go func Match(pattern, name string) (bool, error) ``` Match returns true if `name` matches the file name `pattern` ([see below](#patterns)). `name` and `pattern` are split on forward slash (`/`) characters and may be relative or absolute. Note: `Match()` is meant to be a drop-in replacement for `path.Match()`. As such, it always uses `/` as the path separator. If you are writing code that will run on systems where `/` is not the path separator (such as Windows), you want to use `PathMatch()` (below) instead. ### PathMatch ```go func PathMatch(pattern, name string) (bool, error) ``` PathMatch returns true if `name` matches the file name `pattern` ([see below](#patterns)). The difference between Match and PathMatch is that PathMatch will automatically use your system's path separator to split `name` and `pattern`. `PathMatch()` is meant to be a drop-in replacement for `filepath.Match()`. ### Glob ```go func Glob(pattern string) ([]string, error) ``` Glob finds all files and directories in the filesystem that match `pattern` ([see below](#patterns)). `pattern` may be relative (to the current working directory), or absolute. `Glob()` is meant to be a drop-in replacement for `filepath.Glob()`. ### Patterns **doublestar** supports the following special terms in the patterns: Special Terms | Meaning ------------- | ------- `*` | matches any sequence of non-path-separators `**` | matches any sequence of characters, including path separators `?` | matches any single non-path-separator character `[class]` | matches any single non-path-separator character against a class of characters ([see below](#character-classes)) `{alt1,...}` | matches a sequence of characters if one of the comma-separated alternatives matches Any character with a special meaning can be escaped with a backslash (`\`). A mid-pattern doublestar (`**`) behaves like bash's globstar option: a pattern such as `path/to/**.txt` would return the same results as `path/to/*.txt`. The pattern you're looking for is `path/to/**/*.txt`. #### Character Classes Character classes support the following: Class | Meaning ---------- | ------- `[abc]` | matches any single character within the set `[a-z]` | matches any single character in the range `[^class]` | matches any single character which does *not* match the class ### Abstracting the `os` package **doublestar** by default uses the `Open`, `Stat`, and `Lstat`, functions and `PathSeparator` value from the standard library's `os` package. To abstract this, for example to be able to perform tests of Windows paths on Linux, or to interoperate with your own filesystem code, it includes the functions `GlobOS` and `PathMatchOS` which are identical to `Glob` and `PathMatch` except that they operate on an `OS` interface: ```go type OS interface { Lstat(name string) (os.FileInfo, error) Open(name string) (*os.File, error) PathSeparator() rune Stat(name string) (os.FileInfo, error) } ``` `StandardOS` is a value that implements this interface by calling functions in the standard library's `os` package. ## License [MIT License](LICENSE) ================================================ FILE: vendor/github.com/bmatcuk/doublestar/v3/UPGRADING.md ================================================ # Upgrading from v2 to v3 v3 introduced using `!` to negate character classes, in addition to `^`. If any of your patterns include a character class that starts with an exclamation mark (ie, `[!...]`), you'll need to update the pattern to escape or move the exclamation mark. Note that, like the caret (`^`), it only negates the character class if it is the first character in the character class. # Upgrading from v1 to v2 The change from v1 to v2 was fairly minor: the return type of the `Open` method on the `OS` interface was changed from `*os.File` to `File`, a new interface exported by doublestar. The new `File` interface only defines the functionality doublestar actually needs (`io.Closer` and `Readdir`), making it easier to use doublestar with [go-billy](https://github.com/src-d/go-billy), [afero](https://github.com/spf13/afero), or something similar. If you were using this functionality, updating should be as easy as updating `Open's` return type, since `os.File` already implements `doublestar.File`. If you weren't using this functionality, updating should be as easy as changing your dependencies to point to v2. ================================================ FILE: vendor/github.com/bmatcuk/doublestar/v3/doublestar.go ================================================ package doublestar import ( "fmt" "io" "os" "path" "path/filepath" "sort" "strings" "unicode/utf8" ) // File defines a subset of file operations type File interface { io.Closer Readdir(count int) ([]os.FileInfo, error) } // An OS abstracts functions in the standard library's os package. type OS interface { Lstat(name string) (os.FileInfo, error) Open(name string) (File, error) PathSeparator() rune Stat(name string) (os.FileInfo, error) } // A standardOS implements OS by calling functions in the standard library's os // package. type standardOS struct{} func (standardOS) Lstat(name string) (os.FileInfo, error) { return os.Lstat(name) } func (standardOS) Open(name string) (File, error) { return os.Open(name) } func (standardOS) PathSeparator() rune { return os.PathSeparator } func (standardOS) Stat(name string) (os.FileInfo, error) { return os.Stat(name) } // StandardOS is a value that implements the OS interface by calling functions // in the standard libray's os package. var StandardOS OS = standardOS{} // ErrBadPattern indicates a pattern was malformed. var ErrBadPattern = path.ErrBadPattern // Find the first index of a rune in a string, // ignoring any times the rune is escaped using "\". func indexRuneWithEscaping(s string, r rune) int { end := strings.IndexRune(s, r) if end == -1 || r == '\\' { return end } if end > 0 && s[end-1] == '\\' { start := end + utf8.RuneLen(r) end = indexRuneWithEscaping(s[start:], r) if end != -1 { end += start } } return end } // Find the last index of a rune in a string, // ignoring any times the rune is escaped using "\". func lastIndexRuneWithEscaping(s string, r rune) int { end := strings.LastIndex(s, string(r)) if end == -1 { return -1 } if end > 0 && s[end-1] == '\\' { end = lastIndexRuneWithEscaping(s[:end-1], r) } return end } // Find the index of the first instance of one of the unicode characters in // chars, ignoring any times those characters are escaped using "\". func indexAnyWithEscaping(s, chars string) int { end := strings.IndexAny(s, chars) if end == -1 { return -1 } if end > 0 && s[end-1] == '\\' { _, adj := utf8.DecodeRuneInString(s[end:]) start := end + adj end = indexAnyWithEscaping(s[start:], chars) if end != -1 { end += start } } return end } // Split a set of alternatives such as {alt1,alt2,...} and returns the index of // the rune after the closing curly brace. Respects nested alternatives and // escaped runes. func splitAlternatives(s string) (ret []string, idx int) { ret = make([]string, 0, 2) idx = 0 slen := len(s) braceCnt := 1 esc := false start := 0 for braceCnt > 0 { if idx >= slen { return nil, -1 } sRune, adj := utf8.DecodeRuneInString(s[idx:]) if esc { esc = false } else if sRune == '\\' { esc = true } else if sRune == '{' { braceCnt++ } else if sRune == '}' { braceCnt-- } else if sRune == ',' && braceCnt == 1 { ret = append(ret, s[start:idx]) start = idx + adj } idx += adj } ret = append(ret, s[start:idx-1]) return } // Returns true if the pattern is "zero length", meaning // it could match zero or more characters. func isZeroLengthPattern(pattern string) (ret bool, err error) { // * can match zero if pattern == "" || pattern == "*" || pattern == "**" { return true, nil } // an alternative with zero length can match zero, for example {,x} - the // first alternative has zero length r, adj := utf8.DecodeRuneInString(pattern) if r == '{' { options, endOptions := splitAlternatives(pattern[adj:]) if endOptions == -1 { return false, ErrBadPattern } if ret, err = isZeroLengthPattern(pattern[adj+endOptions:]); !ret || err != nil { return } for _, o := range options { if ret, err = isZeroLengthPattern(o); ret || err != nil { return } } } return false, nil } // Match returns true if name matches the shell file name pattern. // The pattern syntax is: // // pattern: // { term } // term: // '*' matches any sequence of non-path-separators // '**' matches any sequence of characters, including // path separators. // '?' matches any single non-path-separator character // '[' [ '^' '!' ] { character-range } ']' // character class (must be non-empty) // '{' { term } [ ',' { term } ... ] '}' // c matches character c (c != '*', '?', '\\', '[') // '\\' c matches character c // // character-range: // c matches character c (c != '\\', '-', ']') // '\\' c matches character c // lo '-' hi matches character c for lo <= c <= hi // // Match requires pattern to match all of name, not just a substring. // The path-separator defaults to the '/' character. The only possible // returned error is ErrBadPattern, when pattern is malformed. // // Note: this is meant as a drop-in replacement for path.Match() which // always uses '/' as the path separator. If you want to support systems // which use a different path separator (such as Windows), what you want // is the PathMatch() function below. // func Match(pattern, name string) (bool, error) { return doMatching(pattern, name, '/') } // PathMatch is like Match except that it uses your system's path separator. // For most systems, this will be '/'. However, for Windows, it would be '\\'. // Note that for systems where the path separator is '\\', escaping is // disabled. // // Note: this is meant as a drop-in replacement for filepath.Match(). // func PathMatch(pattern, name string) (bool, error) { return PathMatchOS(StandardOS, pattern, name) } // PathMatchOS is like PathMatch except that it uses vos's path separator. func PathMatchOS(vos OS, pattern, name string) (bool, error) { pattern = filepath.ToSlash(pattern) return doMatching(pattern, name, vos.PathSeparator()) } func doMatching(pattern, name string, separator rune) (matched bool, err error) { // check for some base-cases patternLen, nameLen := len(pattern), len(name) if patternLen == 0 { return nameLen == 0, nil } else if nameLen == 0 { return isZeroLengthPattern(pattern) } separatorAdj := utf8.RuneLen(separator) patIdx := indexRuneWithEscaping(pattern, '/') lastPat := patIdx == -1 if lastPat { patIdx = len(pattern) } if pattern[:patIdx] == "**" { // if our last pattern component is a doublestar, we're done - // doublestar will match any remaining name components, if any. if lastPat { return true, nil } // otherwise, try matching remaining components nameIdx := 0 patIdx += 1 for { if m, _ := doMatching(pattern[patIdx:], name[nameIdx:], separator); m { return true, nil } nextNameIdx := 0 if nextNameIdx = indexRuneWithEscaping(name[nameIdx:], separator); nextNameIdx == -1 { break } nameIdx += separatorAdj + nextNameIdx } return false, nil } nameIdx := indexRuneWithEscaping(name, separator) lastName := nameIdx == -1 if lastName { nameIdx = nameLen } var matches []string matches, err = matchComponent(pattern, name[:nameIdx]) if matches == nil || err != nil { return } if len(matches) == 0 && lastName { return true, nil } if !lastName { nameIdx += separatorAdj for _, alt := range matches { matched, err = doMatching(alt, name[nameIdx:], separator) if matched || err != nil { return } } } return false, nil } // Glob returns the names of all files matching pattern or nil // if there is no matching file. The syntax of pattern is the same // as in Match. The pattern may describe hierarchical names such as // /usr/*/bin/ed (assuming the Separator is '/'). // // Glob ignores file system errors such as I/O errors reading directories. // The only possible returned error is ErrBadPattern, when pattern // is malformed. // // Your system path separator is automatically used. This means on // systems where the separator is '\\' (Windows), escaping will be // disabled. // // Note: this is meant as a drop-in replacement for filepath.Glob(). // func Glob(pattern string) (matches []string, err error) { return GlobOS(StandardOS, pattern) } // GlobOS is like Glob except that it operates on vos. func GlobOS(vos OS, pattern string) (matches []string, err error) { if len(pattern) == 0 { return nil, nil } // if the pattern starts with alternatives, we need to handle that here - the // alternatives may be a mix of relative and absolute if pattern[0] == '{' { options, endOptions := splitAlternatives(pattern[1:]) if endOptions == -1 { return nil, ErrBadPattern } for _, o := range options { m, e := GlobOS(vos, o+pattern[endOptions+1:]) if e != nil { return nil, e } matches = append(matches, m...) } return matches, nil } // If the pattern is relative or absolute and we're on a non-Windows machine, // volumeName will be an empty string. If it is absolute and we're on a // Windows machine, volumeName will be a drive letter ("C:") for filesystem // paths or \\\ for UNC paths. isAbs := filepath.IsAbs(pattern) || pattern[0] == '\\' || pattern[0] == '/' volumeName := filepath.VolumeName(pattern) isWindowsUNC := strings.HasPrefix(volumeName, `\\`) if isWindowsUNC || isAbs { startIdx := len(volumeName) + 1 return doGlob(vos, fmt.Sprintf("%s%s", volumeName, string(vos.PathSeparator())), filepath.ToSlash(pattern[startIdx:]), matches) } // otherwise, it's a relative pattern return doGlob(vos, ".", filepath.ToSlash(pattern), matches) } // Perform a glob func doGlob(vos OS, basedir, pattern string, matches []string) (m []string, e error) { m = matches e = nil // if the pattern starts with any path components that aren't globbed (ie, // `path/to/glob*`), we can skip over the un-globbed components (`path/to` in // our example). globIdx := indexAnyWithEscaping(pattern, "*?[{\\") if globIdx > 0 { globIdx = lastIndexRuneWithEscaping(pattern[:globIdx], '/') } else if globIdx == -1 { globIdx = lastIndexRuneWithEscaping(pattern, '/') } if globIdx > 0 { basedir = filepath.Join(basedir, pattern[:globIdx]) pattern = pattern[globIdx+1:] } // Lstat will return an error if the file/directory doesn't exist fi, err := vos.Lstat(basedir) if err != nil { return } // if the pattern is empty, we've found a match if len(pattern) == 0 { m = append(m, basedir) return } // otherwise, we need to check each item in the directory... // first, if basedir is a symlink, follow it... if (fi.Mode() & os.ModeSymlink) != 0 { fi, err = vos.Stat(basedir) if err != nil { return } } // confirm it's a directory... if !fi.IsDir() { return } files, err := filesInDir(vos, basedir) if err != nil { return } sort.Slice(files, func(i, j int) bool { return files[i].Name() < files[j].Name() }) slashIdx := indexRuneWithEscaping(pattern, '/') lastComponent := slashIdx == -1 if lastComponent { slashIdx = len(pattern) } if pattern[:slashIdx] == "**" { // if the current component is a doublestar, we'll try depth-first for _, file := range files { // if symlink, we may want to follow if (file.Mode() & os.ModeSymlink) != 0 { file, err = vos.Stat(filepath.Join(basedir, file.Name())) if err != nil { continue } } if file.IsDir() { // recurse into directories if lastComponent { m = append(m, filepath.Join(basedir, file.Name())) } m, e = doGlob(vos, filepath.Join(basedir, file.Name()), pattern, m) } else if lastComponent { // if the pattern's last component is a doublestar, we match filenames, too m = append(m, filepath.Join(basedir, file.Name())) } } if lastComponent { return // we're done } pattern = pattern[slashIdx+1:] } // check items in current directory and recurse var match []string for _, file := range files { match, e = matchComponent(pattern, file.Name()) if e != nil { return } if match != nil { if len(match) == 0 { m = append(m, filepath.Join(basedir, file.Name())) } else { for _, alt := range match { m, e = doGlob(vos, filepath.Join(basedir, file.Name()), alt, m) } } } } return } func filesInDir(vos OS, dirPath string) (files []os.FileInfo, e error) { dir, err := vos.Open(dirPath) if err != nil { return nil, nil } defer func() { if err := dir.Close(); e == nil { e = err } }() files, err = dir.Readdir(-1) if err != nil { return nil, nil } return } // Attempt to match a single path component with a pattern. Note that the // pattern may include multiple components but that the "name" is just a single // path component. The return value is a slice of patterns that should be // checked against subsequent path components or nil, indicating that the // pattern does not match this path. It is assumed that pattern components are // separated by '/' func matchComponent(pattern, name string) ([]string, error) { // check for matches one rune at a time patternLen, nameLen := len(pattern), len(name) patIdx, nameIdx := 0, 0 for patIdx < patternLen && nameIdx < nameLen { patRune, patAdj := utf8.DecodeRuneInString(pattern[patIdx:]) nameRune, nameAdj := utf8.DecodeRuneInString(name[nameIdx:]) if patRune == '/' { patIdx++ break } else if patRune == '\\' { // handle escaped runes, only if separator isn't '\\' patIdx += patAdj patRune, patAdj = utf8.DecodeRuneInString(pattern[patIdx:]) if patRune == utf8.RuneError { return nil, ErrBadPattern } else if patRune == nameRune { patIdx += patAdj nameIdx += nameAdj } else { return nil, nil } } else if patRune == '*' { // handle stars - a star at the end of the pattern or before a separator // will always match the rest of the path component if patIdx += patAdj; patIdx >= patternLen { return []string{}, nil } if patRune, patAdj = utf8.DecodeRuneInString(pattern[patIdx:]); patRune == '/' { return []string{pattern[patIdx+patAdj:]}, nil } // check if we can make any matches for ; nameIdx < nameLen; nameIdx += nameAdj { if m, e := matchComponent(pattern[patIdx:], name[nameIdx:]); m != nil || e != nil { return m, e } _, nameAdj = utf8.DecodeRuneInString(name[nameIdx:]) } return nil, nil } else if patRune == '[' { // handle character sets patIdx += patAdj endClass := indexRuneWithEscaping(pattern[patIdx:], ']') if endClass == -1 { return nil, ErrBadPattern } endClass += patIdx classRunes := []rune(pattern[patIdx:endClass]) classRunesLen := len(classRunes) if classRunesLen > 0 { classIdx := 0 matchClass := false negate := classRunes[0] == '^' || classRunes[0] == '!' if negate { classIdx++ } for classIdx < classRunesLen { low := classRunes[classIdx] if low == '-' { return nil, ErrBadPattern } classIdx++ if low == '\\' { if classIdx < classRunesLen { low = classRunes[classIdx] classIdx++ } else { return nil, ErrBadPattern } } high := low if classIdx < classRunesLen && classRunes[classIdx] == '-' { // we have a range of runes if classIdx++; classIdx >= classRunesLen { return nil, ErrBadPattern } high = classRunes[classIdx] if high == '-' { return nil, ErrBadPattern } classIdx++ if high == '\\' { if classIdx < classRunesLen { high = classRunes[classIdx] classIdx++ } else { return nil, ErrBadPattern } } } if low <= nameRune && nameRune <= high { matchClass = true } } if matchClass == negate { return nil, nil } } else { return nil, ErrBadPattern } patIdx = endClass + 1 nameIdx += nameAdj } else if patRune == '{' { // handle alternatives such as {alt1,alt2,...} patIdx += patAdj options, endOptions := splitAlternatives(pattern[patIdx:]) if endOptions == -1 { return nil, ErrBadPattern } patIdx += endOptions results := make([][]string, 0, len(options)) totalResults := 0 for _, o := range options { m, e := matchComponent(o+pattern[patIdx:], name[nameIdx:]) if e != nil { return nil, e } if m != nil { results = append(results, m) totalResults += len(m) } } if len(results) > 0 { lst := make([]string, 0, totalResults) for _, m := range results { lst = append(lst, m...) } return lst, nil } return nil, nil } else if patRune == '?' || patRune == nameRune { // handle single-rune wildcard patIdx += patAdj nameIdx += nameAdj } else { return nil, nil } } if nameIdx >= nameLen { if patIdx >= patternLen { return []string{}, nil } pattern = pattern[patIdx:] slashIdx := indexRuneWithEscaping(pattern, '/') testPattern := pattern if slashIdx >= 0 { testPattern = pattern[:slashIdx] } zeroLength, err := isZeroLengthPattern(testPattern) if err != nil { return nil, err } if zeroLength { if slashIdx == -1 { return []string{}, nil } else { return []string{pattern[slashIdx+1:]}, nil } } } return nil, nil } ================================================ FILE: vendor/github.com/c4milo/unpackit/.editorconfig ================================================ root = true ; Unix-style newlines with a newline ending every file [*] end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true charset = utf-8 ; Golang [*.go] indent_style = tab indent_size = 4 ; C [*.c] indent_style = space indent_size = 4 ; YAML [*.yaml] indent_style = space indent_size = 2 ================================================ FILE: vendor/github.com/c4milo/unpackit/.travis.yml ================================================ sudo: false language: go go: - 1.8 - tip ================================================ FILE: vendor/github.com/c4milo/unpackit/AUTHORS ================================================ 3 Alexandre Normand 48 Camilo Aguilar 2 David Siefert 1 Matthias Kalb 1 bogdannechyporenko ================================================ FILE: vendor/github.com/c4milo/unpackit/CONTRIBUTING.md ================================================ ### Thanks for your interest in contributing to this project and for taking the time to read this guide. ## Code of conduct *Taken from http://libvirt.org/governance.html with minor adjustments.* The open source community covers people from a wide variety of countries, backgrounds and positions. This global diversity is a great strength for this project, but can also lead to communication issues, which may in turn cause unhappiness. To maximise happiness of the project community taken as a whole, all members (whether users, contributors or committers) are expected to abide by the project's code of conduct. At a high level the code can be summarized as "be excellent to each other". Expanding on this: **Be respectful:** disagreements between people are to be expected and are usually the sign of healthy debate and engagement. Disagreements can lead to frustration and even anger for some members. Turning to personal insults, intimidation or threatening behaviour does not improve the situation. Participants should thus take care to ensure all communications / interactions stay professional at all times. **Be considerate:** remember that the community has members with a diverse background many of whom have English as a second language. What might appear impolite, may simply be a result of a lack of knowledge of the English language. Bear in mind that actions will have an impact on other community members and the project as a whole, so take potential consequences into account before pursuing a course of action. **Be forgiving:** humans are fallible and as such prone to make mistakes and inexplicably change their positions at times. Don't assume that other members are acting with malicious intent. Be prepared to forgive people who make mistakes and assist each other in learning from them. Playing a blame game doesn't help anyone. ## Issues * Before reporting an issue make sure you search first if anybody has already reported a similar issue and whether or not it has been fixed. * Make sure your issue report sufficiently details the problem. * Include code samples reproducing the issue. * Please do not derail or troll issues. Keep the discussion on topic and respect the Code of conduct. * If you want to tackle any open issue, make sure you let people know you are working on it. ## Development workflow Go is unlike any other language in that it forces a specific development workflow and project structure. Trying to fight it is useless, frustrating and time consuming. So, you better be prepare to adapt your workflow when contributing to Go projects. ### Prerequisites * **Go**: To install Go, please follow its installation guide at https://golang.org/doc/install * **Git:** * **Debian-based distros:** `apt-get install git-core` * **OSX:** `brew install git` ### Pull Requests * Please be generous describing your changes. * Although it is highly suggested to include tests, they are not a hard requirement in order to get your contributions accepted. * Keep pull requets small so core developers can review them quickly. * Unlike other projects, we are not going to nitpick your contributions. If your pull request is correct, we are going to merge and address the small details by ourselves. There is no point on delaying contributions due to little and unimportant details. ### Workflow for third-party code contributions * In Github, fork `https://github.com/c4milo/unpackit` to your own account * Get the package using "go get": `go get github.com/c4milo/unpackit` * Move to where the package was cloned: `cd $GOPATH/src/github.com/c4milo/unpackit` * Add a git remote pointing to your own fork: `git remote add downstream git@github.com:/unpackit.git` * Create a branch for making your changes, then commit them. * Push changes to downstream repository, this is your own fork: `git push downstream` * In Github, from your fork, create the Pull Request and send it upstream. * You are done. ### Workflow for core developers Since core developers usually have access to the upstream repository, there is no need for having a workflow like the one for thid-party contributors. * Get the package using "go get": `go get github.com/c4milo/unpackit` * Create a branch for making your changes, then commit them. * Push changes to the repository: `git push origin ` * In Github, create the Pull Request from your branch to master. * Before merging into master, wait for at least two developers to code review your contribution. ================================================ FILE: vendor/github.com/c4milo/unpackit/LICENSE ================================================ Mozilla Public License Version 2.0 ================================== 1. Definitions -------------- 1.1. "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. 1.2. "Contributor Version" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor's Contribution. 1.3. "Contribution" means Covered Software of a particular Contributor. 1.4. "Covered Software" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. 1.5. "Incompatible With Secondary Licenses" means (a) that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or (b) that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. 1.6. "Executable Form" means any form of the work other than Source Code Form. 1.7. "Larger Work" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" means this document. 1.9. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. 1.10. "Modifications" means any of the following: (a) any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or (b) any new file in Source Code Form that contains any Covered Software. 1.11. "Patent Claims" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. 1.12. "Secondary License" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. 1.13. "Source Code Form" means the form of the work preferred for making modifications. 1.14. "You" (or "Your") means an individual or a legal entity exercising rights under this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants and Conditions -------------------------------- 2.1. Grants Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and (b) under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. 2.2. Effective Date The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. 2.3. Limitations on Grant Scope The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: (a) for any code that a Contributor has removed from Covered Software; or (b) for infringements caused by: (i) Your and any other third party's modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or (c) under Patent Claims infringed by Covered Software in the absence of its Contributions. This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). 2.4. Subsequent Licenses No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). 2.5. Representation Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. 2.6. Fair Use This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. 2.7. Conditions Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. 3. Responsibilities ------------------- 3.1. Distribution of Source Form All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients' rights in the Source Code Form. 3.2. Distribution of Executable Form If You distribute Covered Software in Executable Form then: (a) such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and (b) You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients' rights in the Source Code Form under this License. 3.3. Distribution of a Larger Work You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). 3.4. Notices You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. 3.5. Application of Additional Terms You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. 4. Inability to Comply Due to Statute or Regulation --------------------------------------------------- If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Termination -------------- 5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. 5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. ************************************************************************ * * * 6. Disclaimer of Warranty * * ------------------------- * * * * Covered Software is provided under this License on an "as is" * * basis, without warranty of any kind, either expressed, implied, or * * statutory, including, without limitation, warranties that the * * Covered Software is free of defects, merchantable, fit for a * * particular purpose or non-infringing. The entire risk as to the * * quality and performance of the Covered Software is with You. * * Should any Covered Software prove defective in any respect, You * * (not any Contributor) assume the cost of any necessary servicing, * * repair, or correction. This disclaimer of warranty constitutes an * * essential part of this License. No use of any Covered Software is * * authorized under this License except under this disclaimer. * * * ************************************************************************ ************************************************************************ * * * 7. Limitation of Liability * * -------------------------- * * * * Under no circumstances and under no legal theory, whether tort * * (including negligence), contract, or otherwise, shall any * * Contributor, or anyone who distributes Covered Software as * * permitted above, be liable to You for any direct, indirect, * * special, incidental, or consequential damages of any character * * including, without limitation, damages for lost profits, loss of * * goodwill, work stoppage, computer failure or malfunction, or any * * and all other commercial damages or losses, even if such party * * shall have been informed of the possibility of such damages. This * * limitation of liability shall not apply to liability for death or * * personal injury resulting from such party's negligence to the * * extent applicable law prohibits such limitation. Some * * jurisdictions do not allow the exclusion or limitation of * * incidental or consequential damages, so this exclusion and * * limitation may not apply to You. * * * ************************************************************************ 8. Litigation ------------- Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party's ability to bring cross-claims or counter-claims. 9. Miscellaneous ---------------- This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. 10. Versions of the License --------------------------- 10.1. New Versions Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. 10.2. Effect of New Versions You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. 10.3. Modified Versions If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. Exhibit A - Source Code Form License Notice ------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. Exhibit B - "Incompatible With Secondary Licenses" Notice --------------------------------------------------------- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. ================================================ FILE: vendor/github.com/c4milo/unpackit/README.md ================================================ # UnpackIt [![GoDoc](https://godoc.org/github.com/c4milo/unpackit?status.svg)](https://godoc.org/github.com/c4milo/unpackit) [![Build Status](https://travis-ci.org/c4milo/unpackit.svg?branch=master)](https://travis-ci.org/c4milo/unpackit) This Go library allows you to easily unpack the following files using magic numbers: * tar.gz * tar.bzip2 * tar.xz * zip * tar ## Usage Unpack a file: ```go file, _ := os.Open(test.filepath) destPath, err := unpackit.Unpack(file, tempDir) ``` Unpack a stream (such as a http.Response): ```go res, err := http.Get(url) destPath, err := unpackit.Unpack(res.Body, tempDir) ``` ================================================ FILE: vendor/github.com/c4milo/unpackit/unpackit.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package unpackit allows you to easily unpack *.tar.gz, *.tar.bzip2, *.tar.xz, *.zip and *.tar files. // There are not CGO involved nor hard dependencies of any type. package unpackit import ( "archive/tar" "archive/zip" "bufio" "bytes" "fmt" "io" "io/ioutil" "log" "os" "path" "path/filepath" "runtime" "strings" "time" "github.com/dsnet/compress/bzip2" gzip "github.com/klauspost/pgzip" "github.com/pkg/errors" "github.com/ulikunitz/xz" ) var ( magicZIP = []byte{0x50, 0x4b, 0x03, 0x04} magicGZ = []byte{0x1f, 0x8b} magicBZIP = []byte{0x42, 0x5a} magicTAR = []byte{0x75, 0x73, 0x74, 0x61, 0x72} // at offset 257 magicXZ = []byte{0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00} ) // Check whether a file has the magic number for tar, gzip, bzip2 or zip files // // Note that this function does not advance the Reader. // // 50 4b 03 04 for pkzip format // 1f 8b for .gz format // 42 5a for .bzip format // 75 73 74 61 72 at offset 257 for tar files // fd 37 7a 58 5a 00 for .xz format func magicNumber(reader *bufio.Reader, offset int) (string, error) { headerBytes, err := reader.Peek(offset + 6) if err != nil { return "", err } magic := headerBytes[offset : offset+6] if bytes.Equal(magicTAR, magic[0:5]) { return "tar", nil } if bytes.Equal(magicZIP, magic[0:4]) { return "zip", nil } if bytes.Equal(magicGZ, magic[0:2]) { return "gzip", nil } else if bytes.Equal(magicBZIP, magic[0:2]) { return "bzip", nil } if bytes.Equal(magicXZ, magic) { return "xz", nil } return "", nil } // Unpack unpacks a compressed stream. Magic numbers are used to determine what // decompressor and/or unarchiver to use. func Unpack(reader io.Reader, destPath string) (string, error) { var err error if destPath == "" { destPath, err = ioutil.TempDir(os.TempDir(), "unpackit-") if err != nil { return "", err } } // Makes sure destPath exists if err := os.MkdirAll(destPath, 0740); err != nil { return "", err } r := bufio.NewReader(reader) // Reads magic number from the stream so we can better determine how to proceed ftype, err := magicNumber(r, 0) if err != nil { return "", err } var decompressingReader *bufio.Reader switch ftype { case "gzip": gzr, err := gzip.NewReader(r) if err != nil { return "", err } defer func() { if err := gzr.Close(); err != nil { fmt.Printf("%+v", errors.Wrapf(err, "unpackit: failed closing gzip reader")) } }() decompressingReader = bufio.NewReader(gzr) case "xz": xzr, err := xz.NewReader(r) if err != nil { return "", err } decompressingReader = bufio.NewReader(xzr) case "bzip": br, err := bzip2.NewReader(r, nil) if err != nil { return "", err } defer func() { if err := br.Close(); err != nil { fmt.Printf("%+v", errors.Wrapf(err, "unpackit: failed closing bzip2 reader")) } }() decompressingReader = bufio.NewReader(br) case "zip": // Like TAR, ZIP is also an archiving format, therefore we can just return // after it finishes return Unzip(r, destPath) default: // maybe it is a tarball file decompressingReader = r } // Check magic number in offset 257 too see if this is also a TAR file ftype, err = magicNumber(decompressingReader, 257) if err != nil { return "", err } if ftype == "tar" { return Untar(decompressingReader, destPath) } // If it's not a TAR archive then save it to disk as is. destRawFile := filepath.Join(destPath, sanitize(path.Base("unknown-pack"))) // Creates destination file destFile, err := os.Create(destRawFile) if err != nil { return "", err } defer func() { if err := destFile.Close(); err != nil { log.Println(err) } }() // Copies data to destination file if _, err := io.Copy(destFile, decompressingReader); err != nil { return "", err } return destPath, nil } // Unzip unpacks a ZIP stream. When given a os.File reader it will get its size without // reading the entire zip file in memory. func Unzip(r io.Reader, destPath string) (string, error) { var ( zr *zip.Reader err error ) if f, ok := r.(*os.File); ok { fstat, err := f.Stat() if err != nil { return "", err } zr, err = zip.NewReader(f, fstat.Size()) } else { data, err := ioutil.ReadAll(r) if err != nil { return "", err } memReader := bytes.NewReader(data) zr, err = zip.NewReader(memReader, memReader.Size()) } if err != nil { return "", err } return unpackZip(zr, destPath) } func unpackZip(zr *zip.Reader, destPath string) (string, error) { for _, f := range zr.File { err := unzipFile(f, destPath) if err != nil { return "", err } } return destPath, nil } func unzipFile(f *zip.File, destPath string) error { if f.FileInfo().IsDir() { if err := os.MkdirAll(filepath.Join(destPath, f.Name), f.Mode().Perm()); err != nil { return err } return nil } rc, err := f.Open() if err != nil { return err } defer func() { if err := rc.Close(); err != nil { log.Println(err) } }() filePath := sanitize(f.Name) destPath = filepath.Join(destPath, filePath) // If directories were not included in the archive but are part of the file name, // we create them relative to the destination path. fileDir := filepath.Dir(destPath) _, err = os.Lstat(fileDir) if err != nil { if err := os.MkdirAll(fileDir, 0700); err != nil { return err } } file, err := os.Create(destPath) if err != nil { return err } defer func() { if err := file.Close(); err != nil { log.Println(err) } }() if err := file.Chmod(f.Mode()); err != nil { log.Printf("warn: failed setting file permissions for %q: %#v", file.Name(), err) } if err := os.Chtimes(file.Name(), time.Now(), f.ModTime()); err != nil { log.Printf("warn: failed setting file atime and mtime for %q: %#v", file.Name(), err) } if _, err := io.CopyN(file, rc, int64(f.UncompressedSize64)); err != nil { return err } return nil } // Untar unarchives a TAR archive and returns the final destination path or an error func Untar(data io.Reader, destPath string) (string, error) { // Makes sure destPath exists if err := os.MkdirAll(destPath, 0740); err != nil { return "", err } tr := tar.NewReader(data) // Iterate through the files in the archive. rootdir := destPath for { hdr, err := tr.Next() if err == io.EOF { // end of tar archive break } if err != nil { return rootdir, err } // Skip pax_global_header with the commit ID this archive was created from if hdr.Name == "pax_global_header" { continue } fp := filepath.Join(destPath, sanitize(hdr.Name)) if hdr.FileInfo().IsDir() { if rootdir == destPath { rootdir = fp } if err := os.MkdirAll(fp, os.FileMode(hdr.Mode)); err != nil { return rootdir, err } continue } _, untarErr := untarFile(hdr, tr, fp, rootdir) if untarErr != nil { return rootdir, untarErr } } return rootdir, nil } func untarFile(hdr *tar.Header, tr *tar.Reader, fp, rootdir string) (string, error) { parentDir, _ := filepath.Split(fp) if err := os.MkdirAll(parentDir, 0740); err != nil { return rootdir, err } file, err := os.Create(fp) if err != nil { return rootdir, err } defer func() { if err := file.Close(); err != nil { log.Println(err) } }() if err := file.Chmod(os.FileMode(hdr.Mode)); err != nil { log.Printf("warn: failed setting file permissions for %q: %#v", file.Name(), err) } if err := os.Chtimes(file.Name(), time.Now(), hdr.ModTime); err != nil { log.Printf("warn: failed setting file atime and mtime for %q: %#v", file.Name(), err) } if _, err := io.Copy(file, tr); err != nil { return rootdir, err } return rootdir, nil } // Sanitizes name to avoid overwriting sensitive system files when unarchiving func sanitize(name string) string { // Gets rid of volume drive label in Windows if len(name) > 1 && name[1] == ':' && runtime.GOOS == "windows" { name = name[2:] } name = filepath.Clean(name) name = filepath.ToSlash(name) for strings.HasPrefix(name, "../") { name = name[3:] } return name } ================================================ FILE: vendor/github.com/cespare/xxhash/v2/LICENSE.txt ================================================ Copyright (c) 2016 Caleb Spare MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/cespare/xxhash/v2/README.md ================================================ # xxhash [![Go Reference](https://pkg.go.dev/badge/github.com/cespare/xxhash/v2.svg)](https://pkg.go.dev/github.com/cespare/xxhash/v2) [![Test](https://github.com/cespare/xxhash/actions/workflows/test.yml/badge.svg)](https://github.com/cespare/xxhash/actions/workflows/test.yml) xxhash is a Go implementation of the 64-bit [xxHash] algorithm, XXH64. This is a high-quality hashing algorithm that is much faster than anything in the Go standard library. This package provides a straightforward API: ``` func Sum64(b []byte) uint64 func Sum64String(s string) uint64 type Digest struct{ ... } func New() *Digest ``` The `Digest` type implements hash.Hash64. Its key methods are: ``` func (*Digest) Write([]byte) (int, error) func (*Digest) WriteString(string) (int, error) func (*Digest) Sum64() uint64 ``` The package is written with optimized pure Go and also contains even faster assembly implementations for amd64 and arm64. If desired, the `purego` build tag opts into using the Go code even on those architectures. [xxHash]: http://cyan4973.github.io/xxHash/ ## Compatibility This package is in a module and the latest code is in version 2 of the module. You need a version of Go with at least "minimal module compatibility" to use github.com/cespare/xxhash/v2: * 1.9.7+ for Go 1.9 * 1.10.3+ for Go 1.10 * Go 1.11 or later I recommend using the latest release of Go. ## Benchmarks Here are some quick benchmarks comparing the pure-Go and assembly implementations of Sum64. | input size | purego | asm | | ---------- | --------- | --------- | | 4 B | 1.3 GB/s | 1.2 GB/s | | 16 B | 2.9 GB/s | 3.5 GB/s | | 100 B | 6.9 GB/s | 8.1 GB/s | | 4 KB | 11.7 GB/s | 16.7 GB/s | | 10 MB | 12.0 GB/s | 17.3 GB/s | These numbers were generated on Ubuntu 20.04 with an Intel Xeon Platinum 8252C CPU using the following commands under Go 1.19.2: ``` benchstat <(go test -tags purego -benchtime 500ms -count 15 -bench 'Sum64$') benchstat <(go test -benchtime 500ms -count 15 -bench 'Sum64$') ``` ## Projects using this package - [InfluxDB](https://github.com/influxdata/influxdb) - [Prometheus](https://github.com/prometheus/prometheus) - [VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics) - [FreeCache](https://github.com/coocood/freecache) - [FastCache](https://github.com/VictoriaMetrics/fastcache) ================================================ FILE: vendor/github.com/cespare/xxhash/v2/testall.sh ================================================ #!/bin/bash set -eu -o pipefail # Small convenience script for running the tests with various combinations of # arch/tags. This assumes we're running on amd64 and have qemu available. go test ./... go test -tags purego ./... GOARCH=arm64 go test GOARCH=arm64 go test -tags purego ================================================ FILE: vendor/github.com/cespare/xxhash/v2/xxhash.go ================================================ // Package xxhash implements the 64-bit variant of xxHash (XXH64) as described // at http://cyan4973.github.io/xxHash/. package xxhash import ( "encoding/binary" "errors" "math/bits" ) const ( prime1 uint64 = 11400714785074694791 prime2 uint64 = 14029467366897019727 prime3 uint64 = 1609587929392839161 prime4 uint64 = 9650029242287828579 prime5 uint64 = 2870177450012600261 ) // Store the primes in an array as well. // // The consts are used when possible in Go code to avoid MOVs but we need a // contiguous array of the assembly code. var primes = [...]uint64{prime1, prime2, prime3, prime4, prime5} // Digest implements hash.Hash64. type Digest struct { v1 uint64 v2 uint64 v3 uint64 v4 uint64 total uint64 mem [32]byte n int // how much of mem is used } // New creates a new Digest that computes the 64-bit xxHash algorithm. func New() *Digest { var d Digest d.Reset() return &d } // Reset clears the Digest's state so that it can be reused. func (d *Digest) Reset() { d.v1 = primes[0] + prime2 d.v2 = prime2 d.v3 = 0 d.v4 = -primes[0] d.total = 0 d.n = 0 } // Size always returns 8 bytes. func (d *Digest) Size() int { return 8 } // BlockSize always returns 32 bytes. func (d *Digest) BlockSize() int { return 32 } // Write adds more data to d. It always returns len(b), nil. func (d *Digest) Write(b []byte) (n int, err error) { n = len(b) d.total += uint64(n) memleft := d.mem[d.n&(len(d.mem)-1):] if d.n+n < 32 { // This new data doesn't even fill the current block. copy(memleft, b) d.n += n return } if d.n > 0 { // Finish off the partial block. c := copy(memleft, b) d.v1 = round(d.v1, u64(d.mem[0:8])) d.v2 = round(d.v2, u64(d.mem[8:16])) d.v3 = round(d.v3, u64(d.mem[16:24])) d.v4 = round(d.v4, u64(d.mem[24:32])) b = b[c:] d.n = 0 } if len(b) >= 32 { // One or more full blocks left. nw := writeBlocks(d, b) b = b[nw:] } // Store any remaining partial block. copy(d.mem[:], b) d.n = len(b) return } // Sum appends the current hash to b and returns the resulting slice. func (d *Digest) Sum(b []byte) []byte { s := d.Sum64() return append( b, byte(s>>56), byte(s>>48), byte(s>>40), byte(s>>32), byte(s>>24), byte(s>>16), byte(s>>8), byte(s), ) } // Sum64 returns the current hash. func (d *Digest) Sum64() uint64 { var h uint64 if d.total >= 32 { v1, v2, v3, v4 := d.v1, d.v2, d.v3, d.v4 h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4) h = mergeRound(h, v1) h = mergeRound(h, v2) h = mergeRound(h, v3) h = mergeRound(h, v4) } else { h = d.v3 + prime5 } h += d.total b := d.mem[:d.n&(len(d.mem)-1)] for ; len(b) >= 8; b = b[8:] { k1 := round(0, u64(b[:8])) h ^= k1 h = rol27(h)*prime1 + prime4 } if len(b) >= 4 { h ^= uint64(u32(b[:4])) * prime1 h = rol23(h)*prime2 + prime3 b = b[4:] } for ; len(b) > 0; b = b[1:] { h ^= uint64(b[0]) * prime5 h = rol11(h) * prime1 } h ^= h >> 33 h *= prime2 h ^= h >> 29 h *= prime3 h ^= h >> 32 return h } const ( magic = "xxh\x06" marshaledSize = len(magic) + 8*5 + 32 ) // MarshalBinary implements the encoding.BinaryMarshaler interface. func (d *Digest) MarshalBinary() ([]byte, error) { b := make([]byte, 0, marshaledSize) b = append(b, magic...) b = appendUint64(b, d.v1) b = appendUint64(b, d.v2) b = appendUint64(b, d.v3) b = appendUint64(b, d.v4) b = appendUint64(b, d.total) b = append(b, d.mem[:d.n]...) b = b[:len(b)+len(d.mem)-d.n] return b, nil } // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. func (d *Digest) UnmarshalBinary(b []byte) error { if len(b) < len(magic) || string(b[:len(magic)]) != magic { return errors.New("xxhash: invalid hash state identifier") } if len(b) != marshaledSize { return errors.New("xxhash: invalid hash state size") } b = b[len(magic):] b, d.v1 = consumeUint64(b) b, d.v2 = consumeUint64(b) b, d.v3 = consumeUint64(b) b, d.v4 = consumeUint64(b) b, d.total = consumeUint64(b) copy(d.mem[:], b) d.n = int(d.total % uint64(len(d.mem))) return nil } func appendUint64(b []byte, x uint64) []byte { var a [8]byte binary.LittleEndian.PutUint64(a[:], x) return append(b, a[:]...) } func consumeUint64(b []byte) ([]byte, uint64) { x := u64(b) return b[8:], x } func u64(b []byte) uint64 { return binary.LittleEndian.Uint64(b) } func u32(b []byte) uint32 { return binary.LittleEndian.Uint32(b) } func round(acc, input uint64) uint64 { acc += input * prime2 acc = rol31(acc) acc *= prime1 return acc } func mergeRound(acc, val uint64) uint64 { val = round(0, val) acc ^= val acc = acc*prime1 + prime4 return acc } func rol1(x uint64) uint64 { return bits.RotateLeft64(x, 1) } func rol7(x uint64) uint64 { return bits.RotateLeft64(x, 7) } func rol11(x uint64) uint64 { return bits.RotateLeft64(x, 11) } func rol12(x uint64) uint64 { return bits.RotateLeft64(x, 12) } func rol18(x uint64) uint64 { return bits.RotateLeft64(x, 18) } func rol23(x uint64) uint64 { return bits.RotateLeft64(x, 23) } func rol27(x uint64) uint64 { return bits.RotateLeft64(x, 27) } func rol31(x uint64) uint64 { return bits.RotateLeft64(x, 31) } ================================================ FILE: vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s ================================================ //go:build !appengine && gc && !purego // +build !appengine // +build gc // +build !purego #include "textflag.h" // Registers: #define h AX #define d AX #define p SI // pointer to advance through b #define n DX #define end BX // loop end #define v1 R8 #define v2 R9 #define v3 R10 #define v4 R11 #define x R12 #define prime1 R13 #define prime2 R14 #define prime4 DI #define round(acc, x) \ IMULQ prime2, x \ ADDQ x, acc \ ROLQ $31, acc \ IMULQ prime1, acc // round0 performs the operation x = round(0, x). #define round0(x) \ IMULQ prime2, x \ ROLQ $31, x \ IMULQ prime1, x // mergeRound applies a merge round on the two registers acc and x. // It assumes that prime1, prime2, and prime4 have been loaded. #define mergeRound(acc, x) \ round0(x) \ XORQ x, acc \ IMULQ prime1, acc \ ADDQ prime4, acc // blockLoop processes as many 32-byte blocks as possible, // updating v1, v2, v3, and v4. It assumes that there is at least one block // to process. #define blockLoop() \ loop: \ MOVQ +0(p), x \ round(v1, x) \ MOVQ +8(p), x \ round(v2, x) \ MOVQ +16(p), x \ round(v3, x) \ MOVQ +24(p), x \ round(v4, x) \ ADDQ $32, p \ CMPQ p, end \ JLE loop // func Sum64(b []byte) uint64 TEXT ·Sum64(SB), NOSPLIT|NOFRAME, $0-32 // Load fixed primes. MOVQ ·primes+0(SB), prime1 MOVQ ·primes+8(SB), prime2 MOVQ ·primes+24(SB), prime4 // Load slice. MOVQ b_base+0(FP), p MOVQ b_len+8(FP), n LEAQ (p)(n*1), end // The first loop limit will be len(b)-32. SUBQ $32, end // Check whether we have at least one block. CMPQ n, $32 JLT noBlocks // Set up initial state (v1, v2, v3, v4). MOVQ prime1, v1 ADDQ prime2, v1 MOVQ prime2, v2 XORQ v3, v3 XORQ v4, v4 SUBQ prime1, v4 blockLoop() MOVQ v1, h ROLQ $1, h MOVQ v2, x ROLQ $7, x ADDQ x, h MOVQ v3, x ROLQ $12, x ADDQ x, h MOVQ v4, x ROLQ $18, x ADDQ x, h mergeRound(h, v1) mergeRound(h, v2) mergeRound(h, v3) mergeRound(h, v4) JMP afterBlocks noBlocks: MOVQ ·primes+32(SB), h afterBlocks: ADDQ n, h ADDQ $24, end CMPQ p, end JG try4 loop8: MOVQ (p), x ADDQ $8, p round0(x) XORQ x, h ROLQ $27, h IMULQ prime1, h ADDQ prime4, h CMPQ p, end JLE loop8 try4: ADDQ $4, end CMPQ p, end JG try1 MOVL (p), x ADDQ $4, p IMULQ prime1, x XORQ x, h ROLQ $23, h IMULQ prime2, h ADDQ ·primes+16(SB), h try1: ADDQ $4, end CMPQ p, end JGE finalize loop1: MOVBQZX (p), x ADDQ $1, p IMULQ ·primes+32(SB), x XORQ x, h ROLQ $11, h IMULQ prime1, h CMPQ p, end JL loop1 finalize: MOVQ h, x SHRQ $33, x XORQ x, h IMULQ prime2, h MOVQ h, x SHRQ $29, x XORQ x, h IMULQ ·primes+16(SB), h MOVQ h, x SHRQ $32, x XORQ x, h MOVQ h, ret+24(FP) RET // func writeBlocks(d *Digest, b []byte) int TEXT ·writeBlocks(SB), NOSPLIT|NOFRAME, $0-40 // Load fixed primes needed for round. MOVQ ·primes+0(SB), prime1 MOVQ ·primes+8(SB), prime2 // Load slice. MOVQ b_base+8(FP), p MOVQ b_len+16(FP), n LEAQ (p)(n*1), end SUBQ $32, end // Load vN from d. MOVQ s+0(FP), d MOVQ 0(d), v1 MOVQ 8(d), v2 MOVQ 16(d), v3 MOVQ 24(d), v4 // We don't need to check the loop condition here; this function is // always called with at least one block of data to process. blockLoop() // Copy vN back to d. MOVQ v1, 0(d) MOVQ v2, 8(d) MOVQ v3, 16(d) MOVQ v4, 24(d) // The number of bytes written is p minus the old base pointer. SUBQ b_base+8(FP), p MOVQ p, ret+32(FP) RET ================================================ FILE: vendor/github.com/cespare/xxhash/v2/xxhash_arm64.s ================================================ //go:build !appengine && gc && !purego // +build !appengine // +build gc // +build !purego #include "textflag.h" // Registers: #define digest R1 #define h R2 // return value #define p R3 // input pointer #define n R4 // input length #define nblocks R5 // n / 32 #define prime1 R7 #define prime2 R8 #define prime3 R9 #define prime4 R10 #define prime5 R11 #define v1 R12 #define v2 R13 #define v3 R14 #define v4 R15 #define x1 R20 #define x2 R21 #define x3 R22 #define x4 R23 #define round(acc, x) \ MADD prime2, acc, x, acc \ ROR $64-31, acc \ MUL prime1, acc // round0 performs the operation x = round(0, x). #define round0(x) \ MUL prime2, x \ ROR $64-31, x \ MUL prime1, x #define mergeRound(acc, x) \ round0(x) \ EOR x, acc \ MADD acc, prime4, prime1, acc // blockLoop processes as many 32-byte blocks as possible, // updating v1, v2, v3, and v4. It assumes that n >= 32. #define blockLoop() \ LSR $5, n, nblocks \ PCALIGN $16 \ loop: \ LDP.P 16(p), (x1, x2) \ LDP.P 16(p), (x3, x4) \ round(v1, x1) \ round(v2, x2) \ round(v3, x3) \ round(v4, x4) \ SUB $1, nblocks \ CBNZ nblocks, loop // func Sum64(b []byte) uint64 TEXT ·Sum64(SB), NOSPLIT|NOFRAME, $0-32 LDP b_base+0(FP), (p, n) LDP ·primes+0(SB), (prime1, prime2) LDP ·primes+16(SB), (prime3, prime4) MOVD ·primes+32(SB), prime5 CMP $32, n CSEL LT, prime5, ZR, h // if n < 32 { h = prime5 } else { h = 0 } BLT afterLoop ADD prime1, prime2, v1 MOVD prime2, v2 MOVD $0, v3 NEG prime1, v4 blockLoop() ROR $64-1, v1, x1 ROR $64-7, v2, x2 ADD x1, x2 ROR $64-12, v3, x3 ROR $64-18, v4, x4 ADD x3, x4 ADD x2, x4, h mergeRound(h, v1) mergeRound(h, v2) mergeRound(h, v3) mergeRound(h, v4) afterLoop: ADD n, h TBZ $4, n, try8 LDP.P 16(p), (x1, x2) round0(x1) // NOTE: here and below, sequencing the EOR after the ROR (using a // rotated register) is worth a small but measurable speedup for small // inputs. ROR $64-27, h EOR x1 @> 64-27, h, h MADD h, prime4, prime1, h round0(x2) ROR $64-27, h EOR x2 @> 64-27, h, h MADD h, prime4, prime1, h try8: TBZ $3, n, try4 MOVD.P 8(p), x1 round0(x1) ROR $64-27, h EOR x1 @> 64-27, h, h MADD h, prime4, prime1, h try4: TBZ $2, n, try2 MOVWU.P 4(p), x2 MUL prime1, x2 ROR $64-23, h EOR x2 @> 64-23, h, h MADD h, prime3, prime2, h try2: TBZ $1, n, try1 MOVHU.P 2(p), x3 AND $255, x3, x1 LSR $8, x3, x2 MUL prime5, x1 ROR $64-11, h EOR x1 @> 64-11, h, h MUL prime1, h MUL prime5, x2 ROR $64-11, h EOR x2 @> 64-11, h, h MUL prime1, h try1: TBZ $0, n, finalize MOVBU (p), x4 MUL prime5, x4 ROR $64-11, h EOR x4 @> 64-11, h, h MUL prime1, h finalize: EOR h >> 33, h MUL prime2, h EOR h >> 29, h MUL prime3, h EOR h >> 32, h MOVD h, ret+24(FP) RET // func writeBlocks(d *Digest, b []byte) int TEXT ·writeBlocks(SB), NOSPLIT|NOFRAME, $0-40 LDP ·primes+0(SB), (prime1, prime2) // Load state. Assume v[1-4] are stored contiguously. MOVD d+0(FP), digest LDP 0(digest), (v1, v2) LDP 16(digest), (v3, v4) LDP b_base+8(FP), (p, n) blockLoop() // Store updated state. STP (v1, v2), 0(digest) STP (v3, v4), 16(digest) BIC $31, n MOVD n, ret+32(FP) RET ================================================ FILE: vendor/github.com/cespare/xxhash/v2/xxhash_asm.go ================================================ //go:build (amd64 || arm64) && !appengine && gc && !purego // +build amd64 arm64 // +build !appengine // +build gc // +build !purego package xxhash // Sum64 computes the 64-bit xxHash digest of b. // //go:noescape func Sum64(b []byte) uint64 //go:noescape func writeBlocks(d *Digest, b []byte) int ================================================ FILE: vendor/github.com/cespare/xxhash/v2/xxhash_other.go ================================================ //go:build (!amd64 && !arm64) || appengine || !gc || purego // +build !amd64,!arm64 appengine !gc purego package xxhash // Sum64 computes the 64-bit xxHash digest of b. func Sum64(b []byte) uint64 { // A simpler version would be // d := New() // d.Write(b) // return d.Sum64() // but this is faster, particularly for small inputs. n := len(b) var h uint64 if n >= 32 { v1 := primes[0] + prime2 v2 := prime2 v3 := uint64(0) v4 := -primes[0] for len(b) >= 32 { v1 = round(v1, u64(b[0:8:len(b)])) v2 = round(v2, u64(b[8:16:len(b)])) v3 = round(v3, u64(b[16:24:len(b)])) v4 = round(v4, u64(b[24:32:len(b)])) b = b[32:len(b):len(b)] } h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4) h = mergeRound(h, v1) h = mergeRound(h, v2) h = mergeRound(h, v3) h = mergeRound(h, v4) } else { h = prime5 } h += uint64(n) for ; len(b) >= 8; b = b[8:] { k1 := round(0, u64(b[:8])) h ^= k1 h = rol27(h)*prime1 + prime4 } if len(b) >= 4 { h ^= uint64(u32(b[:4])) * prime1 h = rol23(h)*prime2 + prime3 b = b[4:] } for ; len(b) > 0; b = b[1:] { h ^= uint64(b[0]) * prime5 h = rol11(h) * prime1 } h ^= h >> 33 h *= prime2 h ^= h >> 29 h *= prime3 h ^= h >> 32 return h } func writeBlocks(d *Digest, b []byte) int { v1, v2, v3, v4 := d.v1, d.v2, d.v3, d.v4 n := len(b) for len(b) >= 32 { v1 = round(v1, u64(b[0:8:len(b)])) v2 = round(v2, u64(b[8:16:len(b)])) v3 = round(v3, u64(b[16:24:len(b)])) v4 = round(v4, u64(b[24:32:len(b)])) b = b[32:len(b):len(b)] } d.v1, d.v2, d.v3, d.v4 = v1, v2, v3, v4 return n - len(b) } ================================================ FILE: vendor/github.com/cespare/xxhash/v2/xxhash_safe.go ================================================ //go:build appengine // +build appengine // This file contains the safe implementations of otherwise unsafe-using code. package xxhash // Sum64String computes the 64-bit xxHash digest of s. func Sum64String(s string) uint64 { return Sum64([]byte(s)) } // WriteString adds more data to d. It always returns len(s), nil. func (d *Digest) WriteString(s string) (n int, err error) { return d.Write([]byte(s)) } ================================================ FILE: vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go ================================================ //go:build !appengine // +build !appengine // This file encapsulates usage of unsafe. // xxhash_safe.go contains the safe implementations. package xxhash import ( "unsafe" ) // In the future it's possible that compiler optimizations will make these // XxxString functions unnecessary by realizing that calls such as // Sum64([]byte(s)) don't need to copy s. See https://go.dev/issue/2205. // If that happens, even if we keep these functions they can be replaced with // the trivial safe code. // NOTE: The usual way of doing an unsafe string-to-[]byte conversion is: // // var b []byte // bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) // bh.Data = (*reflect.StringHeader)(unsafe.Pointer(&s)).Data // bh.Len = len(s) // bh.Cap = len(s) // // Unfortunately, as of Go 1.15.3 the inliner's cost model assigns a high enough // weight to this sequence of expressions that any function that uses it will // not be inlined. Instead, the functions below use a different unsafe // conversion designed to minimize the inliner weight and allow both to be // inlined. There is also a test (TestInlining) which verifies that these are // inlined. // // See https://github.com/golang/go/issues/42739 for discussion. // Sum64String computes the 64-bit xxHash digest of s. // It may be faster than Sum64([]byte(s)) by avoiding a copy. func Sum64String(s string) uint64 { b := *(*[]byte)(unsafe.Pointer(&sliceHeader{s, len(s)})) return Sum64(b) } // WriteString adds more data to d. It always returns len(s), nil. // It may be faster than Write([]byte(s)) by avoiding a copy. func (d *Digest) WriteString(s string) (n int, err error) { d.Write(*(*[]byte)(unsafe.Pointer(&sliceHeader{s, len(s)}))) // d.Write always returns len(s), nil. // Ignoring the return output and returning these fixed values buys a // savings of 6 in the inliner's cost model. return len(s), nil } // sliceHeader is similar to reflect.SliceHeader, but it assumes that the layout // of the first two words is the same as the layout of a string. type sliceHeader struct { s string cap int } ================================================ FILE: vendor/github.com/compose-spec/compose-go/LICENSE ================================================ Apache License Version 2.0, January 2004 https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS Copyright 2013-2017 Docker, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/compose-spec/compose-go/NOTICE ================================================ The Compose Specification Copyright 2020 The Compose Specification Authors ================================================ FILE: vendor/github.com/compose-spec/compose-go/errdefs/errors.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package errdefs import "errors" var ( // ErrNotFound is returned when an object is not found ErrNotFound = errors.New("not found") // ErrInvalid is returned when a compose project is invalid ErrInvalid = errors.New("invalid compose project") // ErrUnsupported is returned when a compose project uses an unsupported attribute ErrUnsupported = errors.New("unsupported attribute") // ErrIncompatible is returned when a compose project uses an incompatible attribute ErrIncompatible = errors.New("incompatible attribute") ) // IsNotFoundError returns true if the unwrapped error is ErrNotFound func IsNotFoundError(err error) bool { return errors.Is(err, ErrNotFound) } // IsInvalidError returns true if the unwrapped error is ErrInvalid func IsInvalidError(err error) bool { return errors.Is(err, ErrInvalid) } // IsUnsupportedError returns true if the unwrapped error is ErrUnsupported func IsUnsupportedError(err error) bool { return errors.Is(err, ErrUnsupported) } // IsIncompatibleError returns true if the unwrapped error is ErrIncompatible func IsIncompatibleError(err error) bool { return errors.Is(err, ErrIncompatible) } ================================================ FILE: vendor/github.com/compose-spec/compose-go/interpolation/interpolation.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package interpolation import ( "os" "strings" "github.com/compose-spec/compose-go/template" "github.com/pkg/errors" ) // Options supported by Interpolate type Options struct { // LookupValue from a key LookupValue LookupValue // TypeCastMapping maps key paths to functions to cast to a type TypeCastMapping map[Path]Cast // Substitution function to use Substitute func(string, template.Mapping) (string, error) } // LookupValue is a function which maps from variable names to values. // Returns the value as a string and a bool indicating whether // the value is present, to distinguish between an empty string // and the absence of a value. type LookupValue func(key string) (string, bool) // Cast a value to a new type, or return an error if the value can't be cast type Cast func(value string) (interface{}, error) // Interpolate replaces variables in a string with the values from a mapping func Interpolate(config map[string]interface{}, opts Options) (map[string]interface{}, error) { if opts.LookupValue == nil { opts.LookupValue = os.LookupEnv } if opts.TypeCastMapping == nil { opts.TypeCastMapping = make(map[Path]Cast) } if opts.Substitute == nil { opts.Substitute = template.Substitute } out := map[string]interface{}{} for key, value := range config { interpolatedValue, err := recursiveInterpolate(value, NewPath(key), opts) if err != nil { return out, err } out[key] = interpolatedValue } return out, nil } func recursiveInterpolate(value interface{}, path Path, opts Options) (interface{}, error) { switch value := value.(type) { case string: newValue, err := opts.Substitute(value, template.Mapping(opts.LookupValue)) if err != nil || newValue == value { return value, newPathError(path, err) } caster, ok := opts.getCasterForPath(path) if !ok { return newValue, nil } casted, err := caster(newValue) return casted, newPathError(path, errors.Wrap(err, "failed to cast to expected type")) case map[string]interface{}: out := map[string]interface{}{} for key, elem := range value { interpolatedElem, err := recursiveInterpolate(elem, path.Next(key), opts) if err != nil { return nil, err } out[key] = interpolatedElem } return out, nil case []interface{}: out := make([]interface{}, len(value)) for i, elem := range value { interpolatedElem, err := recursiveInterpolate(elem, path.Next(PathMatchList), opts) if err != nil { return nil, err } out[i] = interpolatedElem } return out, nil default: return value, nil } } func newPathError(path Path, err error) error { switch err := err.(type) { case nil: return nil case *template.InvalidTemplateError: return errors.Errorf( "invalid interpolation format for %s: %#v. You may need to escape any $ with another $.", path, err.Template) default: return errors.Wrapf(err, "error while interpolating %s", path) } } const pathSeparator = "." // PathMatchAll is a token used as part of a Path to match any key at that level // in the nested structure const PathMatchAll = "*" // PathMatchList is a token used as part of a Path to match items in a list const PathMatchList = "[]" // Path is a dotted path of keys to a value in a nested mapping structure. A * // section in a path will match any key in the mapping structure. type Path string // NewPath returns a new Path func NewPath(items ...string) Path { return Path(strings.Join(items, pathSeparator)) } // Next returns a new path by append part to the current path func (p Path) Next(part string) Path { return Path(string(p) + pathSeparator + part) } func (p Path) parts() []string { return strings.Split(string(p), pathSeparator) } func (p Path) matches(pattern Path) bool { patternParts := pattern.parts() parts := p.parts() if len(patternParts) != len(parts) { return false } for index, part := range parts { switch patternParts[index] { case PathMatchAll, part: continue default: return false } } return true } func (o Options) getCasterForPath(path Path) (Cast, bool) { for pattern, caster := range o.TypeCastMapping { if path.matches(pattern) { return caster, true } } return nil, false } ================================================ FILE: vendor/github.com/compose-spec/compose-go/loader/example1.env ================================================ # passed through FOO=foo_from_env_file # overridden in example2.env BAR=bar_from_env_file # overridden in full-example.yml BAZ=baz_from_env_file ================================================ FILE: vendor/github.com/compose-spec/compose-go/loader/example2.env ================================================ BAR=bar_from_env_file_2 # overridden in configDetails.Environment QUX=quz_from_env_file_2 ================================================ FILE: vendor/github.com/compose-spec/compose-go/loader/full-example.yml ================================================ services: foo: build: context: ./dir dockerfile: Dockerfile args: foo: bar target: foo network: foo cache_from: - foo - bar labels: [FOO=BAR] cap_add: - ALL cap_drop: - NET_ADMIN - SYS_ADMIN cgroup_parent: m-executor-abcd # String or list command: bundle exec thin -p 3000 # command: ["bundle", "exec", "thin", "-p", "3000"] configs: - config1 - source: config2 target: /my_config uid: '103' gid: '103' mode: 0440 container_name: my-web-container depends_on: - db - redis deploy: mode: replicated replicas: 6 labels: [FOO=BAR] rollback_config: parallelism: 3 delay: 10s failure_action: continue monitor: 60s max_failure_ratio: 0.3 order: start-first update_config: parallelism: 3 delay: 10s failure_action: continue monitor: 60s max_failure_ratio: 0.3 order: start-first resources: limits: cpus: '0.001' memory: 50M reservations: cpus: '0.0001' memory: 20M generic_resources: - discrete_resource_spec: kind: 'gpu' value: 2 - discrete_resource_spec: kind: 'ssd' value: 1 restart_policy: condition: on-failure delay: 5s max_attempts: 3 window: 120s placement: constraints: [node=foo] max_replicas_per_node: 5 preferences: - spread: node.labels.az endpoint_mode: dnsrr devices: - "/dev/ttyUSB0:/dev/ttyUSB0" # String or list # dns: 8.8.8.8 dns: - 8.8.8.8 - 9.9.9.9 # String or list # dns_search: example.com dns_search: - dc1.example.com - dc2.example.com domainname: foo.com # String or list # entrypoint: /code/entrypoint.sh -p 3000 entrypoint: ["/code/entrypoint.sh", "-p", "3000"] # String or list # env_file: .env env_file: - ./example1.env - ./example2.env # Mapping or list # Mapping values can be strings, numbers or null # Booleans are not allowed - must be quoted environment: BAZ: baz_from_service_def QUX: # environment: # - RACK_ENV=development # - SHOW=true # - SESSION_SECRET # Items can be strings or numbers expose: - "3000" - 8000 external_links: - redis_1 - project_db_1:mysql - project_db_1:postgresql # Mapping or list # Mapping values must be strings # extra_hosts: # somehost: "162.242.195.82" # otherhost: "50.31.209.229" extra_hosts: - "somehost:162.242.195.82" - "otherhost:50.31.209.229" hostname: foo healthcheck: test: echo "hello world" interval: 10s timeout: 1s retries: 5 start_period: 15s # Any valid image reference - repo, tag, id, sha image: redis # image: ubuntu:14.04 # image: tutum/influxdb # image: example-registry.com:4000/postgresql # image: a4bc65fd # image: busybox@sha256:38a203e1986cf79639cfb9b2e1d6e773de84002feea2d4eb006b52004ee8502d ipc: host # Mapping or list # Mapping values can be strings, numbers or null labels: com.example.description: "Accounting webapp" com.example.number: 42 com.example.empty-label: # labels: # - "com.example.description=Accounting webapp" # - "com.example.number=42" # - "com.example.empty-label" links: - db - db:database - redis logging: driver: syslog options: syslog-address: "tcp://192.168.0.42:123" mac_address: 02:42:ac:11:65:43 # network_mode: "bridge" # network_mode: "host" # network_mode: "none" # Use the network mode of an arbitrary container from another service # network_mode: "service:db" # Use the network mode of another container, specified by name or id # network_mode: "container:some-container" network_mode: "container:0cfeab0f748b9a743dc3da582046357c6ef497631c1a016d28d2bf9b4f899f7b" networks: some-network: aliases: - alias1 - alias3 other-network: ipv4_address: 172.16.238.10 ipv6_address: 2001:3984:3989::10 other-other-network: pid: "host" ports: - 3000 - "3001-3005" - "8000:8000" - "9090-9091:8080-8081" - "49100:22" - "127.0.0.1:8001:8001" - "127.0.0.1:5000-5010:5000-5010" privileged: true read_only: true restart: always secrets: - secret1 - source: secret2 target: my_secret uid: '103' gid: '103' mode: 0440 security_opt: - label=level:s0:c100,c200 - label=type:svirt_apache_t stdin_open: true stop_grace_period: 20s stop_signal: SIGUSR1 sysctls: net.core.somaxconn: 1024 net.ipv4.tcp_syncookies: 0 # String or list # tmpfs: /run tmpfs: - /run - /tmp tty: true ulimits: # Single number or mapping with soft + hard limits nproc: 65535 nofile: soft: 20000 hard: 40000 user: someone volumes: # Just specify a path and let the Engine create a volume - /var/lib/mysql # Specify an absolute path mapping - /opt/data:/var/lib/mysql # Path on the host, relative to the Compose file - .:/code - ./static:/var/www/html # User-relative path - ~/configs:/etc/configs/:ro # Named volume - datavolume:/var/lib/mysql - type: bind source: ./opt target: /opt consistency: cached - type: tmpfs target: /opt tmpfs: size: 10000 working_dir: /code x-bar: baz x-foo: bar networks: # Entries can be null, which specifies simply that a network # called "{project name}_some-network" should be created and # use the default driver some-network: other-network: driver: overlay driver_opts: # Values can be strings or numbers foo: "bar" baz: 1 ipam: driver: overlay # driver_opts: # # Values can be strings or numbers # com.docker.network.enable_ipv6: "true" # com.docker.network.numeric_value: 1 config: - subnet: 172.28.0.0/16 ip_range: 172.28.5.0/24 gateway: 172.28.5.254 aux_addresses: host1: 172.28.1.5 host2: 172.28.1.6 host3: 172.28.1.7 - subnet: 2001:3984:3989::/64 gateway: 2001:3984:3989::1 labels: foo: bar external-network: # Specifies that a pre-existing network called "external-network" # can be referred to within this file as "external-network" external: true other-external-network: # Specifies that a pre-existing network called "my-cool-network" # can be referred to within this file as "other-external-network" external: name: my-cool-network x-bar: baz x-foo: bar volumes: # Entries can be null, which specifies simply that a volume # called "{project name}_some-volume" should be created and # use the default driver some-volume: other-volume: driver: flocker driver_opts: # Values can be strings or numbers foo: "bar" baz: 1 labels: foo: bar another-volume: name: "user_specified_name" driver: vsphere driver_opts: # Values can be strings or numbers foo: "bar" baz: 1 external-volume: # Specifies that a pre-existing volume called "external-volume" # can be referred to within this file as "external-volume" external: true other-external-volume: # Specifies that a pre-existing volume called "my-cool-volume" # can be referred to within this file as "other-external-volume" # This example uses the deprecated "volume.external.name" (replaced by "volume.name") external: name: my-cool-volume external-volume3: # Specifies that a pre-existing volume called "this-is-volume3" # can be referred to within this file as "external-volume3" name: this-is-volume3 external: true x-bar: baz x-foo: bar configs: config1: file: ./config_data labels: foo: bar config2: external: name: my_config config3: external: true config4: name: foo x-bar: baz x-foo: bar secrets: secret1: file: ./secret_data labels: foo: bar secret2: external: name: my_secret secret3: external: true secret4: name: bar x-bar: baz x-foo: bar x-bar: baz x-foo: bar x-nested: bar: baz foo: bar ================================================ FILE: vendor/github.com/compose-spec/compose-go/loader/interpolate.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package loader import ( "strconv" "strings" interp "github.com/compose-spec/compose-go/interpolation" "github.com/compose-spec/compose-go/types" "github.com/pkg/errors" ) var interpolateTypeCastMapping = map[interp.Path]interp.Cast{ servicePath("configs", interp.PathMatchList, "mode"): toInt, servicePath("cpu_count"): toInt64, servicePath("cpu_percent"): toFloat, servicePath("cpu_period"): toInt64, servicePath("cpu_quota"): toInt64, servicePath("cpu_rt_period"): toInt64, servicePath("cpu_rt_runtime"): toInt64, servicePath("cpus"): toFloat32, servicePath("cpu_shares"): toInt64, servicePath("init"): toBoolean, servicePath("deploy", "replicas"): toInt, servicePath("deploy", "update_config", "parallelism"): toInt, servicePath("deploy", "update_config", "max_failure_ratio"): toFloat, servicePath("deploy", "rollback_config", "parallelism"): toInt, servicePath("deploy", "rollback_config", "max_failure_ratio"): toFloat, servicePath("deploy", "restart_policy", "max_attempts"): toInt, servicePath("deploy", "placement", "max_replicas_per_node"): toInt, servicePath("healthcheck", "retries"): toInt, servicePath("healthcheck", "disable"): toBoolean, servicePath("mem_limit"): toUnitBytes, servicePath("mem_reservation"): toUnitBytes, servicePath("memswap_limit"): toUnitBytes, servicePath("mem_swappiness"): toUnitBytes, servicePath("oom_kill_disable"): toBoolean, servicePath("oom_score_adj"): toInt64, servicePath("pids_limit"): toInt64, servicePath("ports", interp.PathMatchList, "target"): toInt, servicePath("ports", interp.PathMatchList, "published"): toInt, servicePath("privileged"): toBoolean, servicePath("read_only"): toBoolean, servicePath("scale"): toInt, servicePath("secrets", interp.PathMatchList, "mode"): toInt, servicePath("shm_size"): toUnitBytes, servicePath("stdin_open"): toBoolean, servicePath("stop_grace_period"): toDuration, servicePath("tty"): toBoolean, servicePath("ulimits", interp.PathMatchAll): toInt, servicePath("ulimits", interp.PathMatchAll, "hard"): toInt, servicePath("ulimits", interp.PathMatchAll, "soft"): toInt, servicePath("volumes", interp.PathMatchList, "read_only"): toBoolean, servicePath("volumes", interp.PathMatchList, "volume", "nocopy"): toBoolean, iPath("networks", interp.PathMatchAll, "external"): toBoolean, iPath("networks", interp.PathMatchAll, "internal"): toBoolean, iPath("networks", interp.PathMatchAll, "attachable"): toBoolean, iPath("volumes", interp.PathMatchAll, "external"): toBoolean, iPath("secrets", interp.PathMatchAll, "external"): toBoolean, iPath("configs", interp.PathMatchAll, "external"): toBoolean, } func iPath(parts ...string) interp.Path { return interp.NewPath(parts...) } func servicePath(parts ...string) interp.Path { return iPath(append([]string{"services", interp.PathMatchAll}, parts...)...) } func toInt(value string) (interface{}, error) { return strconv.Atoi(value) } func toInt64(value string) (interface{}, error) { return strconv.ParseInt(value, 10, 64) } func toUnitBytes(value string) (interface{}, error) { i, err := strconv.ParseInt(value, 10, 64) if err != nil { return nil, err } return types.UnitBytes(i), nil } func toDuration(value string) (interface{}, error) { i, err := strconv.ParseInt(value, 10, 64) if err != nil { return nil, err } return types.Duration(i), nil } func toFloat(value string) (interface{}, error) { return strconv.ParseFloat(value, 64) } func toFloat32(value string) (interface{}, error) { f, err := strconv.ParseFloat(value, 32) if err != nil { return nil, err } return float32(f), nil } // should match http://yaml.org/type/bool.html func toBoolean(value string) (interface{}, error) { switch strings.ToLower(value) { case "y", "yes", "true", "on": return true, nil case "n", "no", "false", "off": return false, nil default: return nil, errors.Errorf("invalid boolean: %s", value) } } ================================================ FILE: vendor/github.com/compose-spec/compose-go/loader/loader.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package loader import ( "fmt" "io/ioutil" "os" "path" "path/filepath" "reflect" "sort" "strings" "time" interp "github.com/compose-spec/compose-go/interpolation" //"github.com/compose-spec/compose-go/schema" "github.com/compose-spec/compose-go/template" "github.com/compose-spec/compose-go/types" "github.com/docker/go-units" "github.com/mattn/go-shellwords" "github.com/mitchellh/mapstructure" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/ulyssessouza/godotenv" "gopkg.in/yaml.v2" ) // Options supported by Load type Options struct { // Skip schema validation SkipValidation bool // Skip interpolation SkipInterpolation bool // Skip normalization SkipNormalization bool // Resolve paths ResolvePaths bool // Skip consistency check SkipConsistencyCheck bool // Skip extends SkipExtends bool // Interpolation options Interpolate *interp.Options // Discard 'env_file' entries after resolving to 'environment' section discardEnvFiles bool // Set project name Name string } // serviceRef identifies a reference to a service. It's used to detect cyclic // references in "extends". type serviceRef struct { filename string service string } type cycleTracker struct { loaded []serviceRef } func (ct *cycleTracker) Add(filename, service string) error { toAdd := serviceRef{filename: filename, service: service} for _, loaded := range ct.loaded { if toAdd == loaded { // Create an error message of the form: // Circular reference: // service-a in docker-compose.yml // extends service-b in docker-compose.yml // extends service-a in docker-compose.yml errLines := []string{ "Circular reference:", fmt.Sprintf(" %s in %s", ct.loaded[0].service, ct.loaded[0].filename), } for _, service := range append(ct.loaded[1:], toAdd) { errLines = append(errLines, fmt.Sprintf(" extends %s in %s", service.service, service.filename)) } return errors.New(strings.Join(errLines, "\n")) } } ct.loaded = append(ct.loaded, toAdd) return nil } // WithDiscardEnvFiles sets the Options to discard the `env_file` section after resolving to // the `environment` section func WithDiscardEnvFiles(opts *Options) { opts.discardEnvFiles = true } // WithSkipValidation sets the Options to skip validation when loading sections func WithSkipValidation(opts *Options) { opts.SkipValidation = true } // ParseYAML reads the bytes from a file, parses the bytes into a mapping // structure, and returns it. func ParseYAML(source []byte) (map[string]interface{}, error) { var cfg interface{} if err := yaml.Unmarshal(source, &cfg); err != nil { return nil, err } cfgMap, ok := cfg.(map[interface{}]interface{}) if !ok { return nil, errors.Errorf("Top-level object must be a mapping") } converted, err := convertToStringKeysRecursive(cfgMap, "") if err != nil { return nil, err } return converted.(map[string]interface{}), nil } // Load reads a ConfigDetails and returns a fully loaded configuration func Load(configDetails types.ConfigDetails, options ...func(*Options)) (*types.Project, error) { if len(configDetails.ConfigFiles) < 1 { return nil, errors.Errorf("No files specified") } opts := &Options{ Interpolate: &interp.Options{ Substitute: template.Substitute, LookupValue: configDetails.LookupEnv, TypeCastMapping: interpolateTypeCastMapping, }, } for _, op := range options { op(opts) } configs := []*types.Config{} for i, file := range configDetails.ConfigFiles { configDict := file.Config if configDict == nil { dict, err := parseConfig(file.Content, opts) if err != nil { return nil, err } configDict = dict file.Config = dict configDetails.ConfigFiles[i] = file } //QTMP //if !opts.SkipValidation { // if err := schema.Validate(configDict); err != nil { // return nil, err // } //} configDict = groupXFieldsIntoExtensions(configDict) cfg, err := loadSections(file.Filename, configDict, configDetails, opts) if err != nil { return nil, err } if opts.discardEnvFiles { for i := range cfg.Services { cfg.Services[i].EnvFile = nil } } configs = append(configs, cfg) } model, err := merge(configs) if err != nil { return nil, err } for _, s := range model.Services { var newEnvFiles types.StringList for _, ef := range s.EnvFile { newEnvFiles = append(newEnvFiles, absPath(configDetails.WorkingDir, ef)) } s.EnvFile = newEnvFiles } project := &types.Project{ Name: opts.Name, WorkingDir: configDetails.WorkingDir, Services: model.Services, Networks: model.Networks, Volumes: model.Volumes, Secrets: model.Secrets, Configs: model.Configs, Environment: configDetails.Environment, Extensions: model.Extensions, } if !opts.SkipNormalization { err = normalize(project, opts.ResolvePaths) if err != nil { return nil, err } } if !opts.SkipConsistencyCheck { err = checkConsistency(project) if err != nil { return nil, err } } return project, nil } func parseConfig(b []byte, opts *Options) (map[string]interface{}, error) { yaml, err := ParseYAML(b) if err != nil { return nil, err } if !opts.SkipInterpolation { return interp.Interpolate(yaml, *opts.Interpolate) } return yaml, err } func groupXFieldsIntoExtensions(dict map[string]interface{}) map[string]interface{} { extras := map[string]interface{}{} for key, value := range dict { if strings.HasPrefix(key, "x-") { extras[key] = value delete(dict, key) } if d, ok := value.(map[string]interface{}); ok { dict[key] = groupXFieldsIntoExtensions(d) } } if len(extras) > 0 { dict["extensions"] = extras } return dict } func loadSections(filename string, config map[string]interface{}, configDetails types.ConfigDetails, opts *Options) (*types.Config, error) { var err error cfg := types.Config{ Filename: filename, } cfg.Services, err = LoadServices(filename, getSection(config, "services"), configDetails.WorkingDir, configDetails.LookupEnv, opts) if err != nil { return nil, err } cfg.Networks, err = LoadNetworks(getSection(config, "networks")) if err != nil { return nil, err } cfg.Volumes, err = LoadVolumes(getSection(config, "volumes")) if err != nil { return nil, err } cfg.Secrets, err = LoadSecrets(getSection(config, "secrets"), configDetails, opts.ResolvePaths) if err != nil { return nil, err } cfg.Configs, err = LoadConfigObjs(getSection(config, "configs"), configDetails, opts.ResolvePaths) if err != nil { return nil, err } extensions := getSection(config, "extensions") if len(extensions) > 0 { cfg.Extensions = extensions } if err != nil { return nil, err } return &cfg, nil } func getSection(config map[string]interface{}, key string) map[string]interface{} { section, ok := config[key] if !ok { return map[string]interface{}{} } return section.(map[string]interface{}) } // ForbiddenPropertiesError is returned when there are properties in the Compose // file that are forbidden. type ForbiddenPropertiesError struct { Properties map[string]string } func (e *ForbiddenPropertiesError) Error() string { return "Configuration contains forbidden properties" } // Transform converts the source into the target struct with compose types transformer // and the specified transformers if any. func Transform(source interface{}, target interface{}, additionalTransformers ...Transformer) error { data := mapstructure.Metadata{} config := &mapstructure.DecoderConfig{ DecodeHook: mapstructure.ComposeDecodeHookFunc( createTransformHook(additionalTransformers...), mapstructure.StringToTimeDurationHookFunc()), Result: target, Metadata: &data, } decoder, err := mapstructure.NewDecoder(config) if err != nil { return err } return decoder.Decode(source) } // TransformerFunc defines a function to perform the actual transformation type TransformerFunc func(interface{}) (interface{}, error) // Transformer defines a map to type transformer type Transformer struct { TypeOf reflect.Type Func TransformerFunc } func createTransformHook(additionalTransformers ...Transformer) mapstructure.DecodeHookFuncType { transforms := map[reflect.Type]func(interface{}) (interface{}, error){ reflect.TypeOf(types.External{}): transformExternal, reflect.TypeOf(types.HealthCheckTest{}): transformHealthCheckTest, reflect.TypeOf(types.ShellCommand{}): transformShellCommand, reflect.TypeOf(types.StringList{}): transformStringList, reflect.TypeOf(map[string]string{}): transformMapStringString, reflect.TypeOf(types.UlimitsConfig{}): transformUlimits, reflect.TypeOf(types.UnitBytes(0)): transformSize, reflect.TypeOf([]types.ServicePortConfig{}): transformServicePort, reflect.TypeOf(types.ServiceSecretConfig{}): transformStringSourceMap, reflect.TypeOf(types.ServiceConfigObjConfig{}): transformStringSourceMap, reflect.TypeOf(types.StringOrNumberList{}): transformStringOrNumberList, reflect.TypeOf(map[string]*types.ServiceNetworkConfig{}): transformServiceNetworkMap, reflect.TypeOf(types.Mapping{}): transformMappingOrListFunc("=", false), reflect.TypeOf(types.MappingWithEquals{}): transformMappingOrListFunc("=", true), reflect.TypeOf(types.Labels{}): transformMappingOrListFunc("=", false), reflect.TypeOf(types.MappingWithColon{}): transformMappingOrListFunc(":", false), reflect.TypeOf(types.HostsList{}): transformListOrMappingFunc(":", false), reflect.TypeOf(types.ServiceVolumeConfig{}): transformServiceVolumeConfig, reflect.TypeOf(types.BuildConfig{}): transformBuildConfig, reflect.TypeOf(types.Duration(0)): transformStringToDuration, reflect.TypeOf(types.DependsOnConfig{}): transformDependsOnConfig, reflect.TypeOf(types.ExtendsConfig{}): transformExtendsConfig, reflect.TypeOf(types.DeviceRequest{}): transformServiceDeviceRequest, } for _, transformer := range additionalTransformers { transforms[transformer.TypeOf] = transformer.Func } return func(_ reflect.Type, target reflect.Type, data interface{}) (interface{}, error) { transform, ok := transforms[target] if !ok { return data, nil } return transform(data) } } // keys needs to be converted to strings for jsonschema func convertToStringKeysRecursive(value interface{}, keyPrefix string) (interface{}, error) { if mapping, ok := value.(map[interface{}]interface{}); ok { dict := map[string]interface{}{} for key, entry := range mapping { str, ok := key.(string) if !ok { return nil, formatInvalidKeyError(keyPrefix, key) } var newKeyPrefix string if keyPrefix == "" { newKeyPrefix = str } else { newKeyPrefix = fmt.Sprintf("%s.%s", keyPrefix, str) } convertedEntry, err := convertToStringKeysRecursive(entry, newKeyPrefix) if err != nil { return nil, err } dict[str] = convertedEntry } return dict, nil } if list, ok := value.([]interface{}); ok { convertedList := []interface{}{} for index, entry := range list { newKeyPrefix := fmt.Sprintf("%s[%d]", keyPrefix, index) convertedEntry, err := convertToStringKeysRecursive(entry, newKeyPrefix) if err != nil { return nil, err } convertedList = append(convertedList, convertedEntry) } return convertedList, nil } return value, nil } func formatInvalidKeyError(keyPrefix string, key interface{}) error { var location string if keyPrefix == "" { location = "at top level" } else { location = fmt.Sprintf("in %s", keyPrefix) } return errors.Errorf("Non-string key %s: %#v", location, key) } // LoadServices produces a ServiceConfig map from a compose file Dict // the servicesDict is not validated if directly used. Use Load() to enable validation func LoadServices(filename string, servicesDict map[string]interface{}, workingDir string, lookupEnv template.Mapping, opts *Options) ([]types.ServiceConfig, error) { var services []types.ServiceConfig x, ok := servicesDict["extensions"] if ok { // as a top-level attribute, "services" doesn't support extensions, and a service can be named `x-foo` for k, v := range x.(map[string]interface{}) { servicesDict[k] = v } } for name := range servicesDict { serviceConfig, err := loadServiceWithExtends(filename, name, servicesDict, workingDir, lookupEnv, opts, &cycleTracker{}) if err != nil { return nil, err } services = append(services, *serviceConfig) } return services, nil } func loadServiceWithExtends(filename, name string, servicesDict map[string]interface{}, workingDir string, lookupEnv template.Mapping, opts *Options, ct *cycleTracker) (*types.ServiceConfig, error) { if err := ct.Add(filename, name); err != nil { return nil, err } serviceConfig, err := LoadService(name, servicesDict[name].(map[string]interface{}), workingDir, lookupEnv, opts.ResolvePaths) if err != nil { return nil, err } if serviceConfig.Extends != nil && !opts.SkipExtends { baseServiceName := *serviceConfig.Extends["service"] var baseService *types.ServiceConfig if file := serviceConfig.Extends["file"]; file == nil { baseService, err = loadServiceWithExtends(filename, baseServiceName, servicesDict, workingDir, lookupEnv, opts, ct) if err != nil { return nil, err } } else { // Resolve the path to the imported file, and load it. baseFilePath := absPath(workingDir, *file) bytes, err := ioutil.ReadFile(baseFilePath) if err != nil { return nil, err } baseFile, err := parseConfig(bytes, opts) if err != nil { return nil, err } baseFileServices := getSection(baseFile, "services") baseService, err = loadServiceWithExtends(baseFilePath, baseServiceName, baseFileServices, filepath.Dir(baseFilePath), lookupEnv, opts, ct) if err != nil { return nil, err } // Make paths relative to the importing Compose file. Note that we // make the paths relative to `*file` rather than `baseFilePath` so // that the resulting paths won't be absolute if `*file` isn't an // absolute path. baseFileParent := filepath.Dir(*file) if baseService.Build != nil { // Note that the Dockerfile is always defined relative to the // build context, so there's no need to update the Dockerfile field. baseService.Build.Context = absPath(baseFileParent, baseService.Build.Context) } for i, vol := range baseService.Volumes { if vol.Type != types.VolumeTypeBind { continue } baseService.Volumes[i].Source = absPath(baseFileParent, vol.Source) } } serviceConfig, err = _merge(baseService, serviceConfig) if err != nil { return nil, err } } return serviceConfig, nil } // LoadService produces a single ServiceConfig from a compose file Dict // the serviceDict is not validated if directly used. Use Load() to enable validation func LoadService(name string, serviceDict map[string]interface{}, workingDir string, lookupEnv template.Mapping, resolvePaths bool) (*types.ServiceConfig, error) { serviceConfig := &types.ServiceConfig{ Scale: 1, } if err := Transform(serviceDict, serviceConfig); err != nil { return nil, err } serviceConfig.Name = name if err := resolveEnvironment(serviceConfig, workingDir, lookupEnv); err != nil { return nil, err } for i, volume := range serviceConfig.Volumes { if volume.Type != "bind" { continue } if volume.Source == "" { return nil, errors.New(`invalid mount config for type "bind": field Source must not be empty`) } if resolvePaths { serviceConfig.Volumes[i] = resolveVolumePath(volume, workingDir, lookupEnv) } } return serviceConfig, nil } func resolveEnvironment(serviceConfig *types.ServiceConfig, workingDir string, lookupEnv template.Mapping) error { environment := types.MappingWithEquals{} if len(serviceConfig.EnvFile) > 0 { for _, file := range serviceConfig.EnvFile { filePath := absPath(workingDir, file) file, err := os.Open(filePath) if err != nil { return err } defer file.Close() fileVars, err := godotenv.Parse(file) if err != nil { return err } env := types.MappingWithEquals{} for k, v := range fileVars { v := v env[k] = &v } environment.OverrideBy(env.Resolve(lookupEnv).RemoveEmpty()) } } environment.OverrideBy(serviceConfig.Environment.Resolve(lookupEnv)) serviceConfig.Environment = environment return nil } func resolveVolumePath(volume types.ServiceVolumeConfig, workingDir string, lookupEnv template.Mapping) types.ServiceVolumeConfig { filePath := expandUser(volume.Source, lookupEnv) // Check if source is an absolute path (either Unix or Windows), to // handle a Windows client with a Unix daemon or vice-versa. // // Note that this is not required for Docker for Windows when specifying // a local Windows path, because Docker for Windows translates the Windows // path into a valid path within the VM. if !path.IsAbs(filePath) && !isAbs(filePath) { filePath = absPath(workingDir, filePath) } volume.Source = filePath return volume } // TODO: make this more robust func expandUser(path string, lookupEnv template.Mapping) string { if strings.HasPrefix(path, "~") { home, err := os.UserHomeDir() if err != nil { logrus.Warn("cannot expand '~', because the environment lacks HOME") return path } return filepath.Join(home, path[1:]) } return path } func transformUlimits(data interface{}) (interface{}, error) { switch value := data.(type) { case int: return types.UlimitsConfig{Single: value}, nil case map[string]interface{}: ulimit := types.UlimitsConfig{} if v, ok := value["soft"]; ok { ulimit.Soft = v.(int) } if v, ok := value["hard"]; ok { ulimit.Hard = v.(int) } return ulimit, nil default: return data, errors.Errorf("invalid type %T for ulimits", value) } } // LoadNetworks produces a NetworkConfig map from a compose file Dict // the source Dict is not validated if directly used. Use Load() to enable validation func LoadNetworks(source map[string]interface{}) (map[string]types.NetworkConfig, error) { networks := map[string]types.NetworkConfig{} err := Transform(source, &networks) if err != nil { return networks, err } for name, network := range networks { if !network.External.External { continue } switch { case network.External.Name != "": if network.Name != "" { return nil, errors.Errorf("network %s: network.external.name and network.name conflict; only use network.name", name) } logrus.Warnf("network %s: network.external.name is deprecated in favor of network.name", name) network.Name = network.External.Name network.External.Name = "" case network.Name == "": network.Name = name } networks[name] = network } return networks, nil } func externalVolumeError(volume, key string) error { return errors.Errorf( "conflicting parameters \"external\" and %q specified for volume %q", key, volume) } // LoadVolumes produces a VolumeConfig map from a compose file Dict // the source Dict is not validated if directly used. Use Load() to enable validation func LoadVolumes(source map[string]interface{}) (map[string]types.VolumeConfig, error) { volumes := make(map[string]types.VolumeConfig) if err := Transform(source, &volumes); err != nil { return volumes, err } for name, volume := range volumes { if !volume.External.External { continue } switch { case volume.Driver != "": return nil, externalVolumeError(name, "driver") case len(volume.DriverOpts) > 0: return nil, externalVolumeError(name, "driver_opts") case len(volume.Labels) > 0: return nil, externalVolumeError(name, "labels") case volume.External.Name != "": if volume.Name != "" { return nil, errors.Errorf("volume %s: volume.external.name and volume.name conflict; only use volume.name", name) } logrus.Warnf("volume %s: volume.external.name is deprecated in favor of volume.name", name) volume.Name = volume.External.Name volume.External.Name = "" case volume.Name == "": volume.Name = name } volumes[name] = volume } return volumes, nil } // LoadSecrets produces a SecretConfig map from a compose file Dict // the source Dict is not validated if directly used. Use Load() to enable validation func LoadSecrets(source map[string]interface{}, details types.ConfigDetails, resolvePaths bool) (map[string]types.SecretConfig, error) { secrets := make(map[string]types.SecretConfig) if err := Transform(source, &secrets); err != nil { return secrets, err } for name, secret := range secrets { obj, err := loadFileObjectConfig(name, "secret", types.FileObjectConfig(secret), details, resolvePaths) if err != nil { return nil, err } secretConfig := types.SecretConfig(obj) secrets[name] = secretConfig } return secrets, nil } // LoadConfigObjs produces a ConfigObjConfig map from a compose file Dict // the source Dict is not validated if directly used. Use Load() to enable validation func LoadConfigObjs(source map[string]interface{}, details types.ConfigDetails, resolvePaths bool) (map[string]types.ConfigObjConfig, error) { configs := make(map[string]types.ConfigObjConfig) if err := Transform(source, &configs); err != nil { return configs, err } for name, config := range configs { obj, err := loadFileObjectConfig(name, "config", types.FileObjectConfig(config), details, resolvePaths) if err != nil { return nil, err } configConfig := types.ConfigObjConfig(obj) configs[name] = configConfig } return configs, nil } func loadFileObjectConfig(name string, objType string, obj types.FileObjectConfig, details types.ConfigDetails, resolvePaths bool) (types.FileObjectConfig, error) { // if "external: true" switch { case obj.External.External: // handle deprecated external.name if obj.External.Name != "" { if obj.Name != "" { return obj, errors.Errorf("%[1]s %[2]s: %[1]s.external.name and %[1]s.name conflict; only use %[1]s.name", objType, name) } logrus.Warnf("%[1]s %[2]s: %[1]s.external.name is deprecated in favor of %[1]s.name", objType, name) obj.Name = obj.External.Name obj.External.Name = "" } else { if obj.Name == "" { obj.Name = name } } // if not "external: true" case obj.Driver != "": if obj.File != "" { return obj, errors.Errorf("%[1]s %[2]s: %[1]s.driver and %[1]s.file conflict; only use %[1]s.driver", objType, name) } default: if resolvePaths { obj.File = absPath(details.WorkingDir, obj.File) } } return obj, nil } func absPath(workingDir string, filePath string) string { if strings.HasPrefix(filePath, "~") { home, _ := os.UserHomeDir() return filepath.Join(home, filePath[1:]) } if filepath.IsAbs(filePath) { return filePath } return filepath.Join(workingDir, filePath) } var transformMapStringString TransformerFunc = func(data interface{}) (interface{}, error) { switch value := data.(type) { case map[string]interface{}: return toMapStringString(value, false), nil case map[string]string: return value, nil default: return data, errors.Errorf("invalid type %T for map[string]string", value) } } var transformExternal TransformerFunc = func(data interface{}) (interface{}, error) { switch value := data.(type) { case bool: return map[string]interface{}{"external": value}, nil case map[string]interface{}: return map[string]interface{}{"external": true, "name": value["name"]}, nil default: return data, errors.Errorf("invalid type %T for external", value) } } var transformServicePort TransformerFunc = func(data interface{}) (interface{}, error) { switch entries := data.(type) { case []interface{}: // We process the list instead of individual items here. // The reason is that one entry might be mapped to multiple ServicePortConfig. // Therefore we take an input of a list and return an output of a list. ports := []interface{}{} for _, entry := range entries { switch value := entry.(type) { case int: parsed, err := types.ParsePortConfig(fmt.Sprint(value)) if err != nil { return data, err } for _, v := range parsed { ports = append(ports, v) } case string: parsed, err := types.ParsePortConfig(value) if err != nil { return data, err } for _, v := range parsed { ports = append(ports, v) } case map[string]interface{}: ports = append(ports, groupXFieldsIntoExtensions(value)) default: return data, errors.Errorf("invalid type %T for port", value) } } return ports, nil default: return data, errors.Errorf("invalid type %T for port", entries) } } var transformServiceDeviceRequest TransformerFunc = func(data interface{}) (interface{}, error) { switch value := data.(type) { case map[string]interface{}: count, ok := value["count"] if ok { switch val := count.(type) { case int: return value, nil case string: if strings.ToLower(val) == "all" { value["count"] = -1 return value, nil } return data, errors.Errorf("invalid string value for 'count' (the only value allowed is 'all')") default: return data, errors.Errorf("invalid type %T for device count", val) } } return data, nil default: return data, errors.Errorf("invalid type %T for resource reservation", value) } } var transformStringSourceMap TransformerFunc = func(data interface{}) (interface{}, error) { switch value := data.(type) { case string: return map[string]interface{}{"source": value}, nil case map[string]interface{}: return groupXFieldsIntoExtensions(data.(map[string]interface{})), nil default: return data, errors.Errorf("invalid type %T for secret", value) } } var transformBuildConfig TransformerFunc = func(data interface{}) (interface{}, error) { switch value := data.(type) { case string: return map[string]interface{}{"context": value}, nil case map[string]interface{}: return groupXFieldsIntoExtensions(data.(map[string]interface{})), nil default: return data, errors.Errorf("invalid type %T for service build", value) } } var transformDependsOnConfig TransformerFunc = func(data interface{}) (interface{}, error) { switch value := data.(type) { case []interface{}: transformed := map[string]interface{}{} for _, serviceIntf := range value { service, ok := serviceIntf.(string) if !ok { return data, errors.Errorf("invalid type %T for service depends_on element. Expected string.", value) } transformed[service] = map[string]interface{}{"condition": types.ServiceConditionStarted} } return transformed, nil case map[string]interface{}: return groupXFieldsIntoExtensions(data.(map[string]interface{})), nil default: return data, errors.Errorf("invalid type %T for service depends_on", value) } } var transformExtendsConfig TransformerFunc = func(data interface{}) (interface{}, error) { switch data.(type) { case string: data = map[string]interface{}{ "service": data, } } return transformMappingOrListFunc("=", true)(data) } var transformServiceVolumeConfig TransformerFunc = func(data interface{}) (interface{}, error) { switch value := data.(type) { case string: return ParseVolume(value) case map[string]interface{}: return groupXFieldsIntoExtensions(data.(map[string]interface{})), nil default: return data, errors.Errorf("invalid type %T for service volume", value) } } var transformServiceNetworkMap TransformerFunc = func(value interface{}) (interface{}, error) { if list, ok := value.([]interface{}); ok { mapValue := map[interface{}]interface{}{} for _, name := range list { mapValue[name] = nil } return mapValue, nil } return value, nil } var transformStringOrNumberList TransformerFunc = func(value interface{}) (interface{}, error) { list := value.([]interface{}) result := make([]string, len(list)) for i, item := range list { result[i] = fmt.Sprint(item) } return result, nil } var transformStringList TransformerFunc = func(data interface{}) (interface{}, error) { switch value := data.(type) { case string: return []string{value}, nil case []interface{}: return value, nil default: return data, errors.Errorf("invalid type %T for string list", value) } } func transformMappingOrListFunc(sep string, allowNil bool) TransformerFunc { return func(data interface{}) (interface{}, error) { return transformMappingOrList(data, sep, allowNil), nil } } func transformListOrMappingFunc(sep string, allowNil bool) TransformerFunc { return func(data interface{}) (interface{}, error) { return transformListOrMapping(data, sep, allowNil), nil } } func transformListOrMapping(listOrMapping interface{}, sep string, allowNil bool) interface{} { switch value := listOrMapping.(type) { case map[string]interface{}: return toStringList(value, sep, allowNil) case []interface{}: return listOrMapping } panic(errors.Errorf("expected a map or a list, got %T: %#v", listOrMapping, listOrMapping)) } func transformMappingOrList(mappingOrList interface{}, sep string, allowNil bool) interface{} { switch value := mappingOrList.(type) { case map[string]interface{}: return toMapStringString(value, allowNil) case ([]interface{}): result := make(map[string]interface{}) for _, value := range value { parts := strings.SplitN(value.(string), sep, 2) key := parts[0] switch { case len(parts) == 1 && allowNil: result[key] = nil case len(parts) == 1 && !allowNil: result[key] = "" default: result[key] = parts[1] } } return result } panic(errors.Errorf("expected a map or a list, got %T: %#v", mappingOrList, mappingOrList)) } var transformShellCommand TransformerFunc = func(value interface{}) (interface{}, error) { if str, ok := value.(string); ok { return shellwords.Parse(str) } return value, nil } var transformHealthCheckTest TransformerFunc = func(data interface{}) (interface{}, error) { switch value := data.(type) { case string: return append([]string{"CMD-SHELL"}, value), nil case []interface{}: return value, nil default: return value, errors.Errorf("invalid type %T for healthcheck.test", value) } } var transformSize TransformerFunc = func(value interface{}) (interface{}, error) { switch value := value.(type) { case int: return int64(value), nil case int64, types.UnitBytes: return value, nil case string: return units.RAMInBytes(value) default: return value, errors.Errorf("invalid type for size %T", value) } } var transformStringToDuration TransformerFunc = func(value interface{}) (interface{}, error) { switch value := value.(type) { case string: d, err := time.ParseDuration(value) if err != nil { return value, err } return types.Duration(d), nil default: return value, errors.Errorf("invalid type %T for duration", value) } } func toMapStringString(value map[string]interface{}, allowNil bool) map[string]interface{} { output := make(map[string]interface{}) for key, value := range value { output[key] = toString(value, allowNil) } return output } func toString(value interface{}, allowNil bool) interface{} { switch { case value != nil: return fmt.Sprint(value) case allowNil: return nil default: return "" } } func toStringList(value map[string]interface{}, separator string, allowNil bool) []string { output := []string{} for key, value := range value { if value == nil && !allowNil { continue } output = append(output, fmt.Sprintf("%s%s%s", key, separator, value)) } sort.Strings(output) return output } ================================================ FILE: vendor/github.com/compose-spec/compose-go/loader/merge.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package loader import ( "reflect" "sort" "github.com/compose-spec/compose-go/types" "github.com/imdario/mergo" "github.com/pkg/errors" ) type specials struct { m map[reflect.Type]func(dst, src reflect.Value) error } var serviceSpecials = &specials{ m: map[reflect.Type]func(dst, src reflect.Value) error{ reflect.TypeOf(&types.LoggingConfig{}): safelyMerge(mergeLoggingConfig), reflect.TypeOf(&types.UlimitsConfig{}): safelyMerge(mergeUlimitsConfig), reflect.TypeOf([]types.ServiceVolumeConfig{}): mergeSlice(toServiceVolumeConfigsMap, toServiceVolumeConfigsSlice), reflect.TypeOf([]types.ServicePortConfig{}): mergeSlice(toServicePortConfigsMap, toServicePortConfigsSlice), reflect.TypeOf([]types.ServiceSecretConfig{}): mergeSlice(toServiceSecretConfigsMap, toServiceSecretConfigsSlice), reflect.TypeOf([]types.ServiceConfigObjConfig{}): mergeSlice(toServiceConfigObjConfigsMap, toSServiceConfigObjConfigsSlice), reflect.TypeOf(&types.UlimitsConfig{}): mergeUlimitsConfig, reflect.TypeOf(&types.ServiceNetworkConfig{}): mergeServiceNetworkConfig, }, } func (s *specials) Transformer(t reflect.Type) func(dst, src reflect.Value) error { if fn, ok := s.m[t]; ok { return fn } return nil } func merge(configs []*types.Config) (*types.Config, error) { base := configs[0] for _, override := range configs[1:] { var err error base.Services, err = mergeServices(base.Services, override.Services) if err != nil { return base, errors.Wrapf(err, "cannot merge services from %s", override.Filename) } base.Volumes, err = mergeVolumes(base.Volumes, override.Volumes) if err != nil { return base, errors.Wrapf(err, "cannot merge volumes from %s", override.Filename) } base.Networks, err = mergeNetworks(base.Networks, override.Networks) if err != nil { return base, errors.Wrapf(err, "cannot merge networks from %s", override.Filename) } base.Secrets, err = mergeSecrets(base.Secrets, override.Secrets) if err != nil { return base, errors.Wrapf(err, "cannot merge secrets from %s", override.Filename) } base.Configs, err = mergeConfigs(base.Configs, override.Configs) if err != nil { return base, errors.Wrapf(err, "cannot merge configs from %s", override.Filename) } base.Extensions, err = mergeExtensions(base.Extensions, override.Extensions) if err != nil { return base, errors.Wrapf(err, "cannot merge extensions from %s", override.Filename) } } return base, nil } func mergeServices(base, override []types.ServiceConfig) ([]types.ServiceConfig, error) { baseServices := mapByName(base) overrideServices := mapByName(override) for name, overrideService := range overrideServices { overrideService := overrideService if baseService, ok := baseServices[name]; ok { merged, err := _merge(&baseService, &overrideService) if err != nil { return nil, errors.Wrapf(err, "cannot merge service %s", name) } baseServices[name] = *merged continue } baseServices[name] = overrideService } services := []types.ServiceConfig{} for _, baseService := range baseServices { services = append(services, baseService) } sort.Slice(services, func(i, j int) bool { return services[i].Name < services[j].Name }) return services, nil } func _merge(baseService *types.ServiceConfig, overrideService *types.ServiceConfig) (*types.ServiceConfig, error) { if err := mergo.Merge(baseService, overrideService, mergo.WithAppendSlice, mergo.WithOverride, mergo.WithTransformers(serviceSpecials)); err != nil { return nil, err } if overrideService.Command != nil { baseService.Command = overrideService.Command } if overrideService.Entrypoint != nil { baseService.Entrypoint = overrideService.Entrypoint } return baseService, nil } func toServiceSecretConfigsMap(s interface{}) (map[interface{}]interface{}, error) { secrets, ok := s.([]types.ServiceSecretConfig) if !ok { return nil, errors.Errorf("not a serviceSecretConfig: %v", s) } m := map[interface{}]interface{}{} for _, secret := range secrets { m[secret.Source] = secret } return m, nil } func toServiceConfigObjConfigsMap(s interface{}) (map[interface{}]interface{}, error) { secrets, ok := s.([]types.ServiceConfigObjConfig) if !ok { return nil, errors.Errorf("not a serviceSecretConfig: %v", s) } m := map[interface{}]interface{}{} for _, secret := range secrets { m[secret.Source] = secret } return m, nil } func toServicePortConfigsMap(s interface{}) (map[interface{}]interface{}, error) { ports, ok := s.([]types.ServicePortConfig) if !ok { return nil, errors.Errorf("not a servicePortConfig slice: %v", s) } m := map[interface{}]interface{}{} type port struct { target uint32 published uint32 ip string protocol string } for _, p := range ports { mergeKey := port{ target: p.Target, published: p.Published, ip: p.HostIP, protocol: p.Protocol, } m[mergeKey] = p } return m, nil } func toServiceVolumeConfigsMap(s interface{}) (map[interface{}]interface{}, error) { volumes, ok := s.([]types.ServiceVolumeConfig) if !ok { return nil, errors.Errorf("not a ServiceVolumeConfig slice: %v", s) } m := map[interface{}]interface{}{} for _, v := range volumes { m[v.Target] = v } return m, nil } func toServiceSecretConfigsSlice(dst reflect.Value, m map[interface{}]interface{}) error { s := []types.ServiceSecretConfig{} for _, v := range m { s = append(s, v.(types.ServiceSecretConfig)) } sort.Slice(s, func(i, j int) bool { return s[i].Source < s[j].Source }) dst.Set(reflect.ValueOf(s)) return nil } func toSServiceConfigObjConfigsSlice(dst reflect.Value, m map[interface{}]interface{}) error { s := []types.ServiceConfigObjConfig{} for _, v := range m { s = append(s, v.(types.ServiceConfigObjConfig)) } sort.Slice(s, func(i, j int) bool { return s[i].Source < s[j].Source }) dst.Set(reflect.ValueOf(s)) return nil } func toServicePortConfigsSlice(dst reflect.Value, m map[interface{}]interface{}) error { s := []types.ServicePortConfig{} for _, v := range m { s = append(s, v.(types.ServicePortConfig)) } sort.Slice(s, func(i, j int) bool { if s[i].Target != s[j].Target { return s[i].Target < s[j].Target } if s[i].Published != s[j].Published { return s[i].Published < s[j].Published } if s[i].HostIP != s[j].HostIP { return s[i].HostIP < s[j].HostIP } return s[i].Protocol < s[j].Protocol }) dst.Set(reflect.ValueOf(s)) return nil } func toServiceVolumeConfigsSlice(dst reflect.Value, m map[interface{}]interface{}) error { s := []types.ServiceVolumeConfig{} for _, v := range m { s = append(s, v.(types.ServiceVolumeConfig)) } sort.Slice(s, func(i, j int) bool { return s[i].Target < s[j].Target }) dst.Set(reflect.ValueOf(s)) return nil } type tomapFn func(s interface{}) (map[interface{}]interface{}, error) type writeValueFromMapFn func(reflect.Value, map[interface{}]interface{}) error func safelyMerge(mergeFn func(dst, src reflect.Value) error) func(dst, src reflect.Value) error { return func(dst, src reflect.Value) error { if src.IsNil() { return nil } if dst.IsNil() { dst.Set(src) return nil } return mergeFn(dst, src) } } func mergeSlice(tomap tomapFn, writeValue writeValueFromMapFn) func(dst, src reflect.Value) error { return func(dst, src reflect.Value) error { dstMap, err := sliceToMap(tomap, dst) if err != nil { return err } srcMap, err := sliceToMap(tomap, src) if err != nil { return err } if err := mergo.Map(&dstMap, srcMap, mergo.WithOverride); err != nil { return err } return writeValue(dst, dstMap) } } func sliceToMap(tomap tomapFn, v reflect.Value) (map[interface{}]interface{}, error) { // check if valid if !v.IsValid() { return nil, errors.Errorf("invalid value : %+v", v) } return tomap(v.Interface()) } func mergeLoggingConfig(dst, src reflect.Value) error { // Same driver, merging options if getLoggingDriver(dst.Elem()) == getLoggingDriver(src.Elem()) || getLoggingDriver(dst.Elem()) == "" || getLoggingDriver(src.Elem()) == "" { if getLoggingDriver(dst.Elem()) == "" { dst.Elem().FieldByName("Driver").SetString(getLoggingDriver(src.Elem())) } dstOptions := dst.Elem().FieldByName("Options").Interface().(map[string]string) srcOptions := src.Elem().FieldByName("Options").Interface().(map[string]string) return mergo.Merge(&dstOptions, srcOptions, mergo.WithOverride) } // Different driver, override with src dst.Set(src) return nil } // nolint: unparam func mergeUlimitsConfig(dst, src reflect.Value) error { if src.Interface() != reflect.Zero(reflect.TypeOf(src.Interface())).Interface() { dst.Elem().Set(src.Elem()) } return nil } // nolint: unparam func mergeServiceNetworkConfig(dst, src reflect.Value) error { if src.Interface() != reflect.Zero(reflect.TypeOf(src.Interface())).Interface() { dst.Elem().FieldByName("Aliases").Set(src.Elem().FieldByName("Aliases")) if ipv4 := src.Elem().FieldByName("Ipv4Address").Interface().(string); ipv4 != "" { dst.Elem().FieldByName("Ipv4Address").SetString(ipv4) } if ipv6 := src.Elem().FieldByName("Ipv6Address").Interface().(string); ipv6 != "" { dst.Elem().FieldByName("Ipv6Address").SetString(ipv6) } } return nil } func getLoggingDriver(v reflect.Value) string { return v.FieldByName("Driver").String() } func mapByName(services []types.ServiceConfig) map[string]types.ServiceConfig { m := map[string]types.ServiceConfig{} for _, service := range services { m[service.Name] = service } return m } func mergeVolumes(base, override map[string]types.VolumeConfig) (map[string]types.VolumeConfig, error) { err := mergo.Map(&base, &override, mergo.WithOverride) return base, err } func mergeNetworks(base, override map[string]types.NetworkConfig) (map[string]types.NetworkConfig, error) { err := mergo.Map(&base, &override, mergo.WithOverride) return base, err } func mergeSecrets(base, override map[string]types.SecretConfig) (map[string]types.SecretConfig, error) { err := mergo.Map(&base, &override, mergo.WithOverride) return base, err } func mergeConfigs(base, override map[string]types.ConfigObjConfig) (map[string]types.ConfigObjConfig, error) { err := mergo.Map(&base, &override, mergo.WithOverride) return base, err } func mergeExtensions(base, override map[string]interface{}) (map[string]interface{}, error) { if base == nil { base = map[string]interface{}{} } err := mergo.Map(&base, &override, mergo.WithOverride) return base, err } ================================================ FILE: vendor/github.com/compose-spec/compose-go/loader/normalize.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package loader import ( "fmt" "os" "path/filepath" "github.com/compose-spec/compose-go/errdefs" "github.com/compose-spec/compose-go/types" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) // normalize compose project by moving deprecated attributes to their canonical position and injecting implicit defaults func normalize(project *types.Project, resolvePaths bool) error { absWorkingDir, err := filepath.Abs(project.WorkingDir) if err != nil { return err } project.WorkingDir = absWorkingDir absComposeFiles, err := absComposeFiles(project.ComposeFiles) if err != nil { return err } project.ComposeFiles = absComposeFiles // If not declared explicitly, Compose model involves an implicit "default" network if _, ok := project.Networks["default"]; !ok { project.Networks["default"] = types.NetworkConfig{} } err = relocateExternalName(project) if err != nil { return err } for i, s := range project.Services { if len(s.Networks) == 0 && s.NetworkMode == "" { // Service without explicit network attachment are implicitly exposed on default network s.Networks = map[string]*types.ServiceNetworkConfig{"default": nil} } if s.PullPolicy == types.PullPolicyIfNotPresent { s.PullPolicy = types.PullPolicyMissing } fn := func(s string) (string, bool) { v, ok := project.Environment[s] return v, ok } if s.Build != nil { if s.Build.Dockerfile == "" { s.Build.Dockerfile = "Dockerfile" } localContext := absPath(project.WorkingDir, s.Build.Context) if _, err := os.Stat(localContext); err == nil { if resolvePaths { s.Build.Context = localContext s.Build.Dockerfile = absPath(localContext, s.Build.Dockerfile) } } else { // might be a remote http/git context. Unfortunately supported "remote" syntax is highly ambiguous // in moby/moby and not defined by compose-spec, so let's assume runtime will check } s.Build.Args = s.Build.Args.Resolve(fn) } s.Environment = s.Environment.Resolve(fn) err := relocateLogDriver(&s) if err != nil { return err } err = relocateLogOpt(&s) if err != nil { return err } err = relocateDockerfile(&s) if err != nil { return err } err = relocateScale(&s) if err != nil { return err } project.Services[i] = s } setNameFromKey(project) return nil } func relocateScale(s *types.ServiceConfig) error { scale := uint64(s.Scale) if scale != 1 { logrus.Warn("`scale` is deprecated. Use the `deploy.replicas` element") if s.Deploy == nil { s.Deploy = &types.DeployConfig{} } if s.Deploy.Replicas != nil && *s.Deploy.Replicas != scale { return errors.Wrap(errdefs.ErrInvalid, "can't use both 'scale' (deprecated) and 'deploy.replicas'") } s.Deploy.Replicas = &scale } return nil } func absComposeFiles(composeFiles []string) ([]string, error) { absComposeFiles := make([]string, len(composeFiles)) for i, composeFile := range composeFiles { absComposefile, err := filepath.Abs(composeFile) if err != nil { return nil, err } absComposeFiles[i] = absComposefile } return absComposeFiles, nil } // Resources with no explicit name are actually named by their key in map func setNameFromKey(project *types.Project) { for i, n := range project.Networks { if n.Name == "" { n.Name = fmt.Sprintf("%s_%s", project.Name, i) project.Networks[i] = n } } for i, v := range project.Volumes { if v.Name == "" { v.Name = fmt.Sprintf("%s_%s", project.Name, i) project.Volumes[i] = v } } for i, c := range project.Configs { if c.Name == "" { c.Name = fmt.Sprintf("%s_%s", project.Name, i) project.Configs[i] = c } } for i, s := range project.Secrets { if s.Name == "" { s.Name = fmt.Sprintf("%s_%s", project.Name, i) project.Secrets[i] = s } } } func relocateExternalName(project *types.Project) error { for i, n := range project.Networks { if n.External.Name != "" { if n.Name != "" { return errors.Wrap(errdefs.ErrInvalid, "can't use both 'networks.external.name' (deprecated) and 'networks.name'") } n.Name = n.External.Name } project.Networks[i] = n } for i, v := range project.Volumes { if v.External.Name != "" { if v.Name != "" { return errors.Wrap(errdefs.ErrInvalid, "can't use both 'volumes.external.name' (deprecated) and 'volumes.name'") } v.Name = v.External.Name } project.Volumes[i] = v } for i, s := range project.Secrets { if s.External.Name != "" { if s.Name != "" { return errors.Wrap(errdefs.ErrInvalid, "can't use both 'secrets.external.name' (deprecated) and 'secrets.name'") } s.Name = s.External.Name } project.Secrets[i] = s } for i, c := range project.Configs { if c.External.Name != "" { if c.Name != "" { return errors.Wrap(errdefs.ErrInvalid, "can't use both 'configs.external.name' (deprecated) and 'configs.name'") } c.Name = c.External.Name } project.Configs[i] = c } return nil } func relocateLogOpt(s *types.ServiceConfig) error { if len(s.LogOpt) != 0 { logrus.Warn("`log_opts` is deprecated. Use the `logging` element") if s.Logging == nil { s.Logging = &types.LoggingConfig{} } for k, v := range s.LogOpt { if _, ok := s.Logging.Options[k]; !ok { s.Logging.Options[k] = v } else { return errors.Wrap(errdefs.ErrInvalid, "can't use both 'log_opt' (deprecated) and 'logging.options'") } } } return nil } func relocateLogDriver(s *types.ServiceConfig) error { if s.LogDriver != "" { logrus.Warn("`log_driver` is deprecated. Use the `logging` element") if s.Logging == nil { s.Logging = &types.LoggingConfig{} } if s.Logging.Driver == "" { s.Logging.Driver = s.LogDriver } else { return errors.Wrap(errdefs.ErrInvalid, "can't use both 'log_driver' (deprecated) and 'logging.driver'") } } return nil } func relocateDockerfile(s *types.ServiceConfig) error { if s.Dockerfile != "" { logrus.Warn("`dockerfile` is deprecated. Use the `build` element") if s.Build == nil { s.Build = &types.BuildConfig{} } if s.Dockerfile == "" { s.Build.Dockerfile = s.Dockerfile } else { return errors.Wrap(errdefs.ErrInvalid, "can't use both 'dockerfile' (deprecated) and 'build.dockerfile'") } } return nil } ================================================ FILE: vendor/github.com/compose-spec/compose-go/loader/validate.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package loader import ( "fmt" "strings" "github.com/compose-spec/compose-go/errdefs" "github.com/compose-spec/compose-go/types" "github.com/pkg/errors" ) // checkConsistency validate a compose model is consistent func checkConsistency(project *types.Project) error { for _, s := range project.Services { if s.Build == nil && s.Image == "" { return errors.Wrapf(errdefs.ErrInvalid, "service %q has neither an image nor a build context specified", s.Name) } for network := range s.Networks { if _, ok := project.Networks[network]; !ok { return errors.Wrap(errdefs.ErrInvalid, fmt.Sprintf("service %q refers to undefined network %s", s.Name, network)) } } if strings.HasPrefix(s.NetworkMode, types.ServicePrefix) { serviceName := s.NetworkMode[len(types.ServicePrefix):] if _, err := project.GetServices(serviceName); err != nil { return fmt.Errorf("service %q not found for network_mode 'service:%s'", serviceName, serviceName) } } for _, volume := range s.Volumes { switch volume.Type { case types.VolumeTypeVolume: if volume.Source != "" { // non anonymous volumes if _, ok := project.Volumes[volume.Source]; !ok { return errors.Wrap(errdefs.ErrInvalid, fmt.Sprintf("service %q refers to undefined volume %s", s.Name, volume.Source)) } } } } for _, secret := range s.Secrets { if _, ok := project.Secrets[secret.Source]; !ok { return errors.Wrap(errdefs.ErrInvalid, fmt.Sprintf("service %q refers to undefined secret %s", s.Name, secret.Source)) } } for _, config := range s.Configs { if _, ok := project.Configs[config.Source]; !ok { return errors.Wrap(errdefs.ErrInvalid, fmt.Sprintf("service %q refers to undefined config %s", s.Name, config.Source)) } } } return nil } ================================================ FILE: vendor/github.com/compose-spec/compose-go/loader/volume.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package loader import ( "strings" "unicode" "unicode/utf8" "github.com/compose-spec/compose-go/types" "github.com/pkg/errors" ) const endOfSpec = rune(0) // ParseVolume parses a volume spec without any knowledge of the target platform func ParseVolume(spec string) (types.ServiceVolumeConfig, error) { volume := types.ServiceVolumeConfig{} switch len(spec) { case 0: return volume, errors.New("invalid empty volume spec") case 1, 2: volume.Target = spec volume.Type = string(types.VolumeTypeVolume) return volume, nil } buffer := []rune{} for _, char := range spec + string(endOfSpec) { switch { case isWindowsDrive(buffer, char): buffer = append(buffer, char) case char == ':' || char == endOfSpec: if err := populateFieldFromBuffer(char, buffer, &volume); err != nil { populateType(&volume) return volume, errors.Wrapf(err, "invalid spec: %s", spec) } buffer = []rune{} default: buffer = append(buffer, char) } } populateType(&volume) return volume, nil } func isWindowsDrive(buffer []rune, char rune) bool { return char == ':' && len(buffer) == 1 && unicode.IsLetter(buffer[0]) } func populateFieldFromBuffer(char rune, buffer []rune, volume *types.ServiceVolumeConfig) error { strBuffer := string(buffer) switch { case len(buffer) == 0: return errors.New("empty section between colons") // Anonymous volume case volume.Source == "" && char == endOfSpec: volume.Target = strBuffer return nil case volume.Source == "": volume.Source = strBuffer return nil case volume.Target == "": volume.Target = strBuffer return nil case char == ':': return errors.New("too many colons") } for _, option := range strings.Split(strBuffer, ",") { switch option { case "ro": volume.ReadOnly = true case "rw": volume.ReadOnly = false case "nocopy": volume.Volume = &types.ServiceVolumeVolume{NoCopy: true} default: if isBindOption(option) { volume.Bind = &types.ServiceVolumeBind{Propagation: option} } // ignore unknown options } } return nil } var Propagations = []string{ types.PropagationRPrivate, types.PropagationPrivate, types.PropagationRShared, types.PropagationShared, types.PropagationRSlave, types.PropagationSlave, } func isBindOption(option string) bool { for _, propagation := range Propagations { if option == propagation { return true } } return false } func populateType(volume *types.ServiceVolumeConfig) { if isFilePath(volume.Source) { volume.Type = types.VolumeTypeBind if volume.Bind == nil { volume.Bind = &types.ServiceVolumeBind{} } // For backward compatibility with docker-compose legacy, using short notation involves // bind will create missing host path volume.Bind.CreateHostPath = true } else { volume.Type = types.VolumeTypeVolume if volume.Volume == nil { volume.Volume = &types.ServiceVolumeVolume{} } } } func isFilePath(source string) bool { if source == "" { return false } switch source[0] { case '.', '/', '~': return true } // windows named pipes if strings.HasPrefix(source, `\\`) { return true } first, nextIndex := utf8.DecodeRuneInString(source) return isWindowsDrive([]rune{first}, rune(source[nextIndex])) } ================================================ FILE: vendor/github.com/compose-spec/compose-go/loader/windows_path.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package loader // Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // https://github.com/golang/go/blob/master/LICENSE // This file contains utilities to check for Windows absolute paths on Linux. // The code in this file was largely copied from the Golang filepath package // https://github.com/golang/go/blob/1d0e94b1e13d5e8a323a63cd1cc1ef95290c9c36/src/path/filepath/path_windows.go#L12-L65 func isSlash(c uint8) bool { return c == '\\' || c == '/' } // isAbs reports whether the path is a Windows absolute path. func isAbs(path string) (b bool) { l := volumeNameLen(path) if l == 0 { return false } path = path[l:] if path == "" { return false } return isSlash(path[0]) } // volumeNameLen returns length of the leading volume name on Windows. // It returns 0 elsewhere. // nolint: gocyclo func volumeNameLen(path string) int { if len(path) < 2 { return 0 } // with drive letter c := path[0] if path[1] == ':' && ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') { return 2 } // is it UNC? https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx if l := len(path); l >= 5 && isSlash(path[0]) && isSlash(path[1]) && !isSlash(path[2]) && path[2] != '.' { // first, leading `\\` and next shouldn't be `\`. its server name. for n := 3; n < l-1; n++ { // second, next '\' shouldn't be repeated. if isSlash(path[n]) { n++ // third, following something characters. its share name. if !isSlash(path[n]) { if path[n] == '.' { break } for ; n < l; n++ { if isSlash(path[n]) { break } } return n } break } } } return 0 } ================================================ FILE: vendor/github.com/compose-spec/compose-go/template/template.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package template import ( "fmt" "regexp" "strings" "github.com/sirupsen/logrus" ) var delimiter = "\\$" var substitutionNamed = "[_a-z][_a-z0-9]*" var substitutionBraced = "[_a-z][_a-z0-9]*(?::?[-?][^}]*)?" var patternString = fmt.Sprintf( "%s(?i:(?P%s)|(?P%s)|{(?P%s)}|(?P))", delimiter, delimiter, substitutionNamed, substitutionBraced, ) var defaultPattern = regexp.MustCompile(patternString) // DefaultSubstituteFuncs contains the default SubstituteFunc used by the docker cli var DefaultSubstituteFuncs = []SubstituteFunc{ softDefault, hardDefault, requiredNonEmpty, required, } // InvalidTemplateError is returned when a variable template is not in a valid // format type InvalidTemplateError struct { Template string } func (e InvalidTemplateError) Error() string { return fmt.Sprintf("Invalid template: %#v", e.Template) } // Mapping is a user-supplied function which maps from variable names to values. // Returns the value as a string and a bool indicating whether // the value is present, to distinguish between an empty string // and the absence of a value. type Mapping func(string) (string, bool) // SubstituteFunc is a user-supplied function that apply substitution. // Returns the value as a string, a bool indicating if the function could apply // the substitution and an error. type SubstituteFunc func(string, Mapping) (string, bool, error) // SubstituteWith substitute variables in the string with their values. // It accepts additional substitute function. func SubstituteWith(template string, mapping Mapping, pattern *regexp.Regexp, subsFuncs ...SubstituteFunc) (string, error) { var err error result := pattern.ReplaceAllStringFunc(template, func(substring string) string { matches := pattern.FindStringSubmatch(substring) groups := matchGroups(matches, pattern) if escaped := groups["escaped"]; escaped != "" { return escaped } braced := false substitution := groups["named"] if substitution == "" { substitution = groups["braced"] braced = true } if substitution == "" { err = &InvalidTemplateError{Template: template} return "" } if braced { for _, f := range subsFuncs { var ( value string applied bool ) value, applied, err = f(substitution, mapping) if err != nil { return "" } if !applied { continue } return value } } value, ok := mapping(substitution) if !ok { logrus.Warnf("The %q variable is not set. Defaulting to a blank string.", substitution) } return value }) return result, err } // Substitute variables in the string with their values func Substitute(template string, mapping Mapping) (string, error) { return SubstituteWith(template, mapping, defaultPattern, DefaultSubstituteFuncs...) } // ExtractVariables returns a map of all the variables defined in the specified // composefile (dict representation) and their default value if any. func ExtractVariables(configDict map[string]interface{}, pattern *regexp.Regexp) map[string]Variable { if pattern == nil { pattern = defaultPattern } return recurseExtract(configDict, pattern) } func recurseExtract(value interface{}, pattern *regexp.Regexp) map[string]Variable { m := map[string]Variable{} switch value := value.(type) { case string: if values, is := extractVariable(value, pattern); is { for _, v := range values { m[v.Name] = v } } case map[string]interface{}: for _, elem := range value { submap := recurseExtract(elem, pattern) for key, value := range submap { m[key] = value } } case []interface{}: for _, elem := range value { if values, is := extractVariable(elem, pattern); is { for _, v := range values { m[v.Name] = v } } } } return m } type Variable struct { Name string DefaultValue string Required bool } func extractVariable(value interface{}, pattern *regexp.Regexp) ([]Variable, bool) { sValue, ok := value.(string) if !ok { return []Variable{}, false } matches := pattern.FindAllStringSubmatch(sValue, -1) if len(matches) == 0 { return []Variable{}, false } values := []Variable{} for _, match := range matches { groups := matchGroups(match, pattern) if escaped := groups["escaped"]; escaped != "" { continue } val := groups["named"] if val == "" { val = groups["braced"] } name := val var defaultValue string var required bool switch { case strings.Contains(val, ":?"): name, _ = partition(val, ":?") required = true case strings.Contains(val, "?"): name, _ = partition(val, "?") required = true case strings.Contains(val, ":-"): name, defaultValue = partition(val, ":-") case strings.Contains(val, "-"): name, defaultValue = partition(val, "-") } values = append(values, Variable{ Name: name, DefaultValue: defaultValue, Required: required, }) } return values, len(values) > 0 } // Soft default (fall back if unset or empty) func softDefault(substitution string, mapping Mapping) (string, bool, error) { sep := ":-" if !strings.Contains(substitution, sep) { return "", false, nil } name, defaultValue := partition(substitution, sep) value, ok := mapping(name) if !ok || value == "" { return defaultValue, true, nil } return value, true, nil } // Hard default (fall back if-and-only-if empty) func hardDefault(substitution string, mapping Mapping) (string, bool, error) { sep := "-" if !strings.Contains(substitution, sep) { return "", false, nil } name, defaultValue := partition(substitution, sep) value, ok := mapping(name) if !ok { return defaultValue, true, nil } return value, true, nil } func requiredNonEmpty(substitution string, mapping Mapping) (string, bool, error) { return withRequired(substitution, mapping, ":?", func(v string) bool { return v != "" }) } func required(substitution string, mapping Mapping) (string, bool, error) { return withRequired(substitution, mapping, "?", func(_ string) bool { return true }) } func withRequired(substitution string, mapping Mapping, sep string, valid func(string) bool) (string, bool, error) { if !strings.Contains(substitution, sep) { return "", false, nil } name, errorMessage := partition(substitution, sep) value, ok := mapping(name) if !ok || !valid(value) { return "", true, &InvalidTemplateError{ Template: fmt.Sprintf("required variable %s is missing a value: %s", name, errorMessage), } } return value, true, nil } func matchGroups(matches []string, pattern *regexp.Regexp) map[string]string { groups := make(map[string]string) for i, name := range pattern.SubexpNames()[1:] { groups[name] = matches[i+1] } return groups } // Split the string at the first occurrence of sep, and return the part before the separator, // and the part after the separator. // // If the separator is not found, return the string itself, followed by an empty string. func partition(s, sep string) (string, string) { if strings.Contains(s, sep) { parts := strings.SplitN(s, sep, 2) return parts[0], parts[1] } return s, "" } ================================================ FILE: vendor/github.com/compose-spec/compose-go/types/config.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package types import ( "encoding/json" "github.com/mitchellh/mapstructure" ) // ConfigDetails are the details about a group of ConfigFiles type ConfigDetails struct { Version string WorkingDir string ConfigFiles []ConfigFile Environment map[string]string } // LookupEnv provides a lookup function for environment variables func (cd ConfigDetails) LookupEnv(key string) (string, bool) { v, ok := cd.Environment[key] return v, ok } // ConfigFile is a filename and the contents of the file as a Dict type ConfigFile struct { // Filename is the name of the yaml configuration file Filename string // Content is the raw yaml content. Will be loaded from Filename if not set Content []byte // Config if the yaml tree for this config file. Will be parsed from Content if not set Config map[string]interface{} } // Config is a full compose file configuration and model type Config struct { Filename string `yaml:"-" json:"-"` Services Services `json:"services"` Networks Networks `yaml:",omitempty" json:"networks,omitempty"` Volumes Volumes `yaml:",omitempty" json:"volumes,omitempty"` Secrets Secrets `yaml:",omitempty" json:"secrets,omitempty"` Configs Configs `yaml:",omitempty" json:"configs,omitempty"` Extensions Extensions `yaml:",inline" json:"-"` } // Volumes is a map of VolumeConfig type Volumes map[string]VolumeConfig // Networks is a map of NetworkConfig type Networks map[string]NetworkConfig // Secrets is a map of SecretConfig type Secrets map[string]SecretConfig // Configs is a map of ConfigObjConfig type Configs map[string]ConfigObjConfig // Extensions is a map of custom extension type Extensions map[string]interface{} // MarshalJSON makes Config implement json.Marshaler func (c Config) MarshalJSON() ([]byte, error) { m := map[string]interface{}{ "services": c.Services, } if len(c.Networks) > 0 { m["networks"] = c.Networks } if len(c.Volumes) > 0 { m["volumes"] = c.Volumes } if len(c.Secrets) > 0 { m["secrets"] = c.Secrets } if len(c.Configs) > 0 { m["configs"] = c.Configs } for k, v := range c.Extensions { m[k] = v } return json.Marshal(m) } func (e Extensions) Get(name string, target interface{}) (bool, error) { if v, ok := e[name]; ok { err := mapstructure.Decode(v, target) return true, err } return false, nil } ================================================ FILE: vendor/github.com/compose-spec/compose-go/types/project.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package types import ( "fmt" "os" "path/filepath" "sort" "github.com/distribution/distribution/v3/reference" "github.com/opencontainers/go-digest" "golang.org/x/sync/errgroup" ) // Project is the result of loading a set of compose files type Project struct { Name string `yaml:"-" json:"-"` WorkingDir string `yaml:"-" json:"-"` Services Services `json:"services"` Networks Networks `yaml:",omitempty" json:"networks,omitempty"` Volumes Volumes `yaml:",omitempty" json:"volumes,omitempty"` Secrets Secrets `yaml:",omitempty" json:"secrets,omitempty"` Configs Configs `yaml:",omitempty" json:"configs,omitempty"` Extensions Extensions `yaml:",inline" json:"-"` // https://github.com/golang/go/issues/6213 ComposeFiles []string `yaml:"-" json:"-"` Environment map[string]string `yaml:"-" json:"-"` // DisabledServices track services which have been disable as profile is not active DisabledServices Services `yaml:"-" json:"-"` } // ServiceNames return names for all services in this Compose config func (p Project) ServiceNames() []string { names := []string{} for _, s := range p.Services { names = append(names, s.Name) } sort.Strings(names) return names } // VolumeNames return names for all volumes in this Compose config func (p Project) VolumeNames() []string { names := []string{} for k := range p.Volumes { names = append(names, k) } sort.Strings(names) return names } // NetworkNames return names for all volumes in this Compose config func (p Project) NetworkNames() []string { names := []string{} for k := range p.Networks { names = append(names, k) } sort.Strings(names) return names } // SecretNames return names for all secrets in this Compose config func (p Project) SecretNames() []string { names := []string{} for k := range p.Secrets { names = append(names, k) } sort.Strings(names) return names } // ConfigNames return names for all configs in this Compose config func (p Project) ConfigNames() []string { names := []string{} for k := range p.Configs { names = append(names, k) } sort.Strings(names) return names } // GetServices retrieve services by names, or return all services if no name specified func (p Project) GetServices(names ...string) (Services, error) { if len(names) == 0 { return p.Services, nil } services := Services{} for _, name := range names { var serviceConfig *ServiceConfig for _, s := range p.Services { if s.Name == name { serviceConfig = &s break } } if serviceConfig == nil { return services, fmt.Errorf("no such service: %s", name) } services = append(services, *serviceConfig) } return services, nil } // GetService retrieve a specific service by name func (p Project) GetService(name string) (ServiceConfig, error) { services, err := p.GetServices(name) if err != nil { return ServiceConfig{}, err } if len(services) == 0 { return ServiceConfig{}, fmt.Errorf("no such service: %s", name) } return services[0], nil } func (p Project) AllServices() Services { var all Services all = append(all, p.Services...) all = append(all, p.DisabledServices...) return all } type ServiceFunc func(service ServiceConfig) error // WithServices run ServiceFunc on each service and dependencies in dependency order func (p Project) WithServices(names []string, fn ServiceFunc) error { return p.withServices(names, fn, map[string]bool{}) } func (p Project) withServices(names []string, fn ServiceFunc, done map[string]bool) error { services, err := p.GetServices(names...) if err != nil { return err } for _, service := range services { if done[service.Name] { continue } //KCQ done[service.Name] = true dependencies := service.GetDependencies() if len(dependencies) > 0 { err := p.withServices(dependencies, fn, done) if err != nil { return err } } if err := fn(service); err != nil { return err } } return nil } // RelativePath resolve a relative path based project's working directory func (p *Project) RelativePath(path string) string { if path[0] == '~' { home, _ := os.UserHomeDir() path = filepath.Join(home, path[1:]) } if filepath.IsAbs(path) { return path } return filepath.Join(p.WorkingDir, path) } // HasProfile return true if service has no profile declared or has at least one profile matching func (service ServiceConfig) HasProfile(profiles []string) bool { if len(service.Profiles) == 0 { return true } for _, p := range profiles { for _, sp := range service.Profiles { if sp == p { return true } } } return false } // GetProfiles retrieve the profiles implicitly enabled by explicitly targeting selected services func (s Services) GetProfiles() []string { set := map[string]struct{}{} for _, service := range s { for _, p := range service.Profiles { set[p] = struct{}{} } } var profiles []string for k := range set { profiles = append(profiles, k) } return profiles } // ApplyProfiles disables service which don't match selected profiles func (p *Project) ApplyProfiles(profiles []string) { for _, p := range profiles { if p == "*" { return } } var enabled, disabled Services for _, service := range p.Services { if service.HasProfile(profiles) { enabled = append(enabled, service) } else { disabled = append(disabled, service) } } p.Services = enabled p.DisabledServices = disabled } // WithoutUnnecessaryResources drops networks/volumes/secrets/configs that are not referenced by active services func (p *Project) WithoutUnnecessaryResources() { requiredNetworks := map[string]struct{}{} requiredVolumes := map[string]struct{}{} requiredSecrets := map[string]struct{}{} requiredConfigs := map[string]struct{}{} for _, s := range p.Services { for k := range s.Networks { requiredNetworks[k] = struct{}{} } for _, v := range s.Volumes { if v.Type != VolumeTypeVolume || v.Source == "" { continue } requiredVolumes[v.Source] = struct{}{} } for _, v := range s.Secrets { requiredSecrets[v.Source] = struct{}{} } for _, v := range s.Configs { requiredConfigs[v.Source] = struct{}{} } } networks := Networks{} for k := range requiredNetworks { networks[k] = p.Networks[k] } p.Networks = networks volumes := Volumes{} for k := range requiredVolumes { volumes[k] = p.Volumes[k] } p.Volumes = volumes secrets := Secrets{} for k := range requiredSecrets { secrets[k] = p.Secrets[k] } p.Secrets = secrets configs := Configs{} for k := range requiredConfigs { configs[k] = p.Configs[k] } p.Configs = configs } // ForServices restrict the project model to a subset of services func (p *Project) ForServices(names []string) error { if len(names) == 0 { // All services return nil } set := map[string]struct{}{} err := p.WithServices(names, func(service ServiceConfig) error { set[service.Name] = struct{}{} return nil }) if err != nil { return err } // Disable all services which are not explicit target or dependencies var enabled Services for _, s := range p.Services { if _, ok := set[s.Name]; ok { enabled = append(enabled, s) } else { p.DisabledServices = append(p.DisabledServices, s) } } p.Services = enabled return nil } // ResolveImages updates services images to include digest computed by a resolver function func (p *Project) ResolveImages(resolver func(named reference.Named) (digest.Digest, error)) error { eg := errgroup.Group{} for i, s := range p.Services { idx := i service := s if service.Image == "" { continue } eg.Go(func() error { named, err := reference.ParseDockerRef(service.Image) if err != nil { return err } if _, ok := named.(reference.Canonical); !ok { // image is named but not digested reference digest, err := resolver(named) if err != nil { return err } named, err = reference.WithDigest(named, digest) if err != nil { return err } } service.Image = named.String() p.Services[idx] = service return nil }) } return eg.Wait() } ================================================ FILE: vendor/github.com/compose-spec/compose-go/types/types.go ================================================ /* Copyright 2020 The Compose Specification Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package types import ( "encoding/json" "fmt" "sort" "strings" "time" "github.com/docker/go-connections/nat" ) // Duration is a thin wrapper around time.Duration with improved JSON marshalling type Duration time.Duration func (d Duration) String() string { return time.Duration(d).String() } // ConvertDurationPtr converts a typedefined Duration pointer to a time.Duration pointer with the same value. func ConvertDurationPtr(d *Duration) *time.Duration { if d == nil { return nil } res := time.Duration(*d) return &res } // MarshalJSON makes Duration implement json.Marshaler func (d Duration) MarshalJSON() ([]byte, error) { return json.Marshal(d.String()) } // MarshalYAML makes Duration implement yaml.Marshaler func (d Duration) MarshalYAML() (interface{}, error) { return d.String(), nil } func (d *Duration) UnmarshalJSON(b []byte) error { s := strings.Trim(string(b), "\"") timeDuration, err := time.ParseDuration(s) if err != nil { return err } *d = Duration(timeDuration) return nil } // Services is a list of ServiceConfig type Services []ServiceConfig // MarshalYAML makes Services implement yaml.Marshaller func (s Services) MarshalYAML() (interface{}, error) { services := map[string]ServiceConfig{} for _, service := range s { services[service.Name] = service } return services, nil } // MarshalJSON makes Services implement json.Marshaler func (s Services) MarshalJSON() ([]byte, error) { data, err := s.MarshalYAML() if err != nil { return nil, err } return json.MarshalIndent(data, "", " ") } // ServiceConfig is the configuration of one service type ServiceConfig struct { Name string `yaml:"-" json:"-"` Profiles []string `mapstructure:"profiles" yaml:"profiles,omitempty" json:"profiles,omitempty"` Build *BuildConfig `yaml:",omitempty" json:"build,omitempty"` BlkioConfig *BlkioConfig `yaml:",omitempty" json:"blkio_config,omitempty"` CapAdd []string `mapstructure:"cap_add" yaml:"cap_add,omitempty" json:"cap_add,omitempty"` CapDrop []string `mapstructure:"cap_drop" yaml:"cap_drop,omitempty" json:"cap_drop,omitempty"` CgroupParent string `mapstructure:"cgroup_parent" yaml:"cgroup_parent,omitempty" json:"cgroup_parent,omitempty"` CPUCount int64 `mapstructure:"cpu_count" yaml:"cpu_count,omitempty" json:"cpu_count,omitempty"` CPUPercent float32 `mapstructure:"cpu_percent" yaml:"cpu_percent,omitempty" json:"cpu_percent,omitempty"` CPUPeriod int64 `mapstructure:"cpu_period" yaml:"cpu_period,omitempty" json:"cpu_period,omitempty"` CPUQuota int64 `mapstructure:"cpu_quota" yaml:"cpu_quota,omitempty" json:"cpu_quota,omitempty"` CPURTPeriod int64 `mapstructure:"cpu_rt_period" yaml:"cpu_rt_period,omitempty" json:"cpu_rt_period,omitempty"` CPURTRuntime int64 `mapstructure:"cpu_rt_runtime" yaml:"cpu_rt_runtime,omitempty" json:"cpu_rt_runtime,omitempty"` CPUS float32 `mapstructure:"cpus" yaml:"cpus,omitempty" json:"cpus,omitempty"` CPUSet string `mapstructure:"cpuset" yaml:"cpuset,omitempty" json:"cpuset,omitempty"` CPUShares int64 `mapstructure:"cpu_shares" yaml:"cpu_shares,omitempty" json:"cpu_shares,omitempty"` Command ShellCommand `yaml:",omitempty" json:"command,omitempty"` Configs []ServiceConfigObjConfig `yaml:",omitempty" json:"configs,omitempty"` ContainerName string `mapstructure:"container_name" yaml:"container_name,omitempty" json:"container_name,omitempty"` CredentialSpec *CredentialSpecConfig `mapstructure:"credential_spec" yaml:"credential_spec,omitempty" json:"credential_spec,omitempty"` DependsOn DependsOnConfig `mapstructure:"depends_on" yaml:"depends_on,omitempty" json:"depends_on,omitempty"` Deploy *DeployConfig `yaml:",omitempty" json:"deploy,omitempty"` Devices []string `yaml:",omitempty" json:"devices,omitempty"` DNS StringList `yaml:",omitempty" json:"dns,omitempty"` DNSOpts []string `mapstructure:"dns_opt" yaml:"dns_opt,omitempty" json:"dns_opt,omitempty"` DNSSearch StringList `mapstructure:"dns_search" yaml:"dns_search,omitempty" json:"dns_search,omitempty"` Dockerfile string `yaml:"dockerfile,omitempty" json:"dockerfile,omitempty"` DomainName string `mapstructure:"domainname" yaml:"domainname,omitempty" json:"domainname,omitempty"` Entrypoint ShellCommand `yaml:",omitempty" json:"entrypoint,omitempty"` Environment MappingWithEquals `yaml:",omitempty" json:"environment,omitempty"` EnvFile StringList `mapstructure:"env_file" yaml:"env_file,omitempty" json:"env_file,omitempty"` Expose StringOrNumberList `yaml:",omitempty" json:"expose,omitempty"` Extends ExtendsConfig `yaml:"extends,omitempty" json:"extends,omitempty"` ExternalLinks []string `mapstructure:"external_links" yaml:"external_links,omitempty" json:"external_links,omitempty"` ExtraHosts HostsList `mapstructure:"extra_hosts" yaml:"extra_hosts,omitempty" json:"extra_hosts,omitempty"` GroupAdd []string `mapstructure:"group_app" yaml:"group_add,omitempty" json:"group_add,omitempty"` Hostname string `yaml:",omitempty" json:"hostname,omitempty"` HealthCheck *HealthCheckConfig `yaml:",omitempty" json:"healthcheck,omitempty"` Image string `yaml:",omitempty" json:"image,omitempty"` Init *bool `yaml:",omitempty" json:"init,omitempty"` Ipc string `yaml:",omitempty" json:"ipc,omitempty"` Isolation string `mapstructure:"isolation" yaml:"isolation,omitempty" json:"isolation,omitempty"` Labels Labels `yaml:",omitempty" json:"labels,omitempty"` Links []string `yaml:",omitempty" json:"links,omitempty"` Logging *LoggingConfig `yaml:",omitempty" json:"logging,omitempty"` LogDriver string `mapstructure:"log_driver" yaml:"log_driver,omitempty" json:"log_driver,omitempty"` LogOpt map[string]string `mapstructure:"log_opt" yaml:"log_opt,omitempty" json:"log_opt,omitempty"` MemLimit UnitBytes `mapstructure:"mem_limit" yaml:"mem_limit,omitempty" json:"mem_limit,omitempty"` MemReservation UnitBytes `mapstructure:"mem_reservation" yaml:"mem_reservation,omitempty" json:"mem_reservation,omitempty"` MemSwapLimit UnitBytes `mapstructure:"memswap_limit" yaml:"memswap_limit,omitempty" json:"memswap_limit,omitempty"` MemSwappiness UnitBytes `mapstructure:"mem_swappiness" yaml:"mem_swappiness,omitempty" json:"mem_swappiness,omitempty"` MacAddress string `mapstructure:"mac_address" yaml:"mac_address,omitempty" json:"mac_address,omitempty"` Net string `yaml:"net,omitempty" json:"net,omitempty"` NetworkMode string `mapstructure:"network_mode" yaml:"network_mode,omitempty" json:"network_mode,omitempty"` Networks map[string]*ServiceNetworkConfig `yaml:",omitempty" json:"networks,omitempty"` OomKillDisable bool `mapstructure:"oom_kill_disable" yaml:"oom_kill_disable,omitempty" json:"oom_kill_disable,omitempty"` OomScoreAdj int64 `mapstructure:"oom_score_adj" yaml:"oom_score_adj,omitempty" json:"oom_score_adj,omitempty"` Pid string `yaml:",omitempty" json:"pid,omitempty"` PidsLimit int64 `mapstructure:"pids_limit" yaml:"pids_limit,omitempty" json:"pids_limit,omitempty"` Platform string `yaml:",omitempty" json:"platform,omitempty"` Ports []ServicePortConfig `yaml:",omitempty" json:"ports,omitempty"` Privileged bool `yaml:",omitempty" json:"privileged,omitempty"` PullPolicy string `mapstructure:"pull_policy" yaml:"pull_policy,omitempty" json:"pull_policy,omitempty"` ReadOnly bool `mapstructure:"read_only" yaml:"read_only,omitempty" json:"read_only,omitempty"` Restart string `yaml:",omitempty" json:"restart,omitempty"` Runtime string `yaml:",omitempty" json:"runtime,omitempty"` Scale int `yaml:"-" json:"-"` Secrets []ServiceSecretConfig `yaml:",omitempty" json:"secrets,omitempty"` SecurityOpt []string `mapstructure:"security_opt" yaml:"security_opt,omitempty" json:"security_opt,omitempty"` ShmSize UnitBytes `mapstructure:"shm_size" yaml:"shm_size,omitempty" json:"shm_size,omitempty"` StdinOpen bool `mapstructure:"stdin_open" yaml:"stdin_open,omitempty" json:"stdin_open,omitempty"` StopGracePeriod *Duration `mapstructure:"stop_grace_period" yaml:"stop_grace_period,omitempty" json:"stop_grace_period,omitempty"` StopSignal string `mapstructure:"stop_signal" yaml:"stop_signal,omitempty" json:"stop_signal,omitempty"` Sysctls Mapping `yaml:",omitempty" json:"sysctls,omitempty"` Tmpfs StringList `yaml:",omitempty" json:"tmpfs,omitempty"` Tty bool `mapstructure:"tty" yaml:"tty,omitempty" json:"tty,omitempty"` Ulimits map[string]*UlimitsConfig `yaml:",omitempty" json:"ulimits,omitempty"` User string `yaml:",omitempty" json:"user,omitempty"` UserNSMode string `mapstructure:"userns_mode" yaml:"userns_mode,omitempty" json:"userns_mode,omitempty"` Uts string `yaml:"uts,omitempty" json:"uts,omitempty"` VolumeDriver string `mapstructure:"volume_driver" yaml:"volume_driver,omitempty" json:"volume_driver,omitempty"` Volumes []ServiceVolumeConfig `yaml:",omitempty" json:"volumes,omitempty"` VolumesFrom []string `mapstructure:"volumes_from" yaml:"volumes_from,omitempty" json:"volumes_from,omitempty"` WorkingDir string `mapstructure:"working_dir" yaml:"working_dir,omitempty" json:"working_dir,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // NetworksByPriority return the service networks IDs sorted according to Priority func (s *ServiceConfig) NetworksByPriority() []string { type key struct { name string priority int } var keys []key for k, v := range s.Networks { priority := 0 if v != nil { priority = v.Priority } keys = append(keys, key{ name: k, priority: priority, }) } sort.Slice(keys, func(i, j int) bool { return keys[i].priority > keys[j].priority }) var sorted []string for _, k := range keys { sorted = append(sorted, k.name) } return sorted } const ( //PullPolicyAlways always pull images PullPolicyAlways = "always" //PullPolicyNever never pull images PullPolicyNever = "never" //PullPolicyIfNotPresent pull missing images PullPolicyIfNotPresent = "if_not_present" //PullPolicyIfNotPresent pull missing images PullPolicyMissing = "missing" //PullPolicyBuild force building images PullPolicyBuild = "build" ) const ( //RestartPolicyAlways always restart the container if it stops RestartPolicyAlways = "always" //RestartPolicyOnFailure restart the container if it exits due to an error RestartPolicyOnFailure = "on-failure" //RestartPolicyNo do not automatically restart the container RestartPolicyNo = "no" //RestartPolicyUnlessStopped always restart the container unless the container is stopped (manually or otherwise) RestartPolicyUnlessStopped = "unless-stopped" ) const ( // ServicePrefix is the prefix for references pointing to a service ServicePrefix = "service:" // ContainerPrefix is the prefix for references pointing to a container ContainerPrefix = "container:" // NetworkModeServicePrefix is the prefix for network_mode pointing to a service // Deprecated prefer ServicePrefix NetworkModeServicePrefix = ServicePrefix // NetworkModeContainerPrefix is the prefix for network_mode pointing to a container // Deprecated prefer ContainerPrefix NetworkModeContainerPrefix = ContainerPrefix ) // GetDependencies retrieve all services this service depends on func (s ServiceConfig) GetDependencies() []string { dependencies := make(set) for dependency := range s.DependsOn { dependencies.append(dependency) } for _, link := range s.Links { parts := strings.Split(link, ":") if len(parts) == 2 { dependencies.append(parts[0]) } else { dependencies.append(link) } } if strings.HasPrefix(s.NetworkMode, ServicePrefix) { dependencies.append(s.NetworkMode[len(ServicePrefix):]) } if strings.HasPrefix(s.Ipc, ServicePrefix) { dependencies.append(s.Ipc[len(ServicePrefix):]) } if strings.HasPrefix(s.Pid, ServicePrefix) { dependencies.append(s.Pid[len(ServicePrefix):]) } for _, vol := range s.VolumesFrom { if !strings.HasPrefix(s.Pid, ContainerPrefix) { dependencies.append(vol) } } return dependencies.toSlice() } type set map[string]struct{} func (s set) append(strings ...string) { for _, str := range strings { s[str] = struct{}{} } } func (s set) toSlice() []string { slice := make([]string, 0, len(s)) for v := range s { slice = append(slice, v) } return slice } // BuildConfig is a type for build type BuildConfig struct { Context string `yaml:",omitempty" json:"context,omitempty"` Dockerfile string `yaml:",omitempty" json:"dockerfile,omitempty"` Args MappingWithEquals `yaml:",omitempty" json:"args,omitempty"` Labels Labels `yaml:",omitempty" json:"labels,omitempty"` CacheFrom StringList `mapstructure:"cache_from" yaml:"cache_from,omitempty" json:"cache_from,omitempty"` ExtraHosts HostsList `mapstructure:"extra_hosts" yaml:"extra_hosts,omitempty" json:"extra_hosts,omitempty"` Isolation string `yaml:",omitempty" json:"isolation,omitempty"` Network string `yaml:",omitempty" json:"network,omitempty"` Target string `yaml:",omitempty" json:"target,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // BlkioConfig define blkio config type BlkioConfig struct { Weight uint16 `yaml:",omitempty" json:"weight,omitempty"` WeightDevice []WeightDevice `yaml:",omitempty" json:"weight_device,omitempty"` DeviceReadBps []ThrottleDevice `yaml:",omitempty" json:"device_read_bps,omitempty"` DeviceReadIOps []ThrottleDevice `yaml:",omitempty" json:"device_read_iops,omitempty"` DeviceWriteBps []ThrottleDevice `yaml:",omitempty" json:"device_write_bps,omitempty"` DeviceWriteIOps []ThrottleDevice `yaml:",omitempty" json:"device_write_iops,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // WeightDevice is a structure that holds device:weight pair type WeightDevice struct { Path string Weight uint16 Extensions map[string]interface{} `yaml:",inline" json:"-"` } // ThrottleDevice is a structure that holds device:rate_per_second pair type ThrottleDevice struct { Path string Rate uint64 Extensions map[string]interface{} `yaml:",inline" json:"-"` } // ShellCommand is a string or list of string args type ShellCommand []string // StringList is a type for fields that can be a string or list of strings type StringList []string // StringOrNumberList is a type for fields that can be a list of strings or // numbers type StringOrNumberList []string // MappingWithEquals is a mapping type that can be converted from a list of // key[=value] strings. // For the key with an empty value (`key=`), the mapped value is set to a pointer to `""`. // For the key without value (`key`), the mapped value is set to nil. type MappingWithEquals map[string]*string // NewMappingWithEquals build a new Mapping from a set of KEY=VALUE strings func NewMappingWithEquals(values []string) MappingWithEquals { mapping := MappingWithEquals{} for _, env := range values { tokens := strings.SplitN(env, "=", 2) if len(tokens) > 1 { mapping[tokens[0]] = &tokens[1] } else { mapping[env] = nil } } return mapping } // OverrideBy update MappingWithEquals with values from another MappingWithEquals func (e MappingWithEquals) OverrideBy(other MappingWithEquals) MappingWithEquals { for k, v := range other { e[k] = v } return e } // Resolve update a MappingWithEquals for keys without value (`key`, but not `key=`) func (e MappingWithEquals) Resolve(lookupFn func(string) (string, bool)) MappingWithEquals { for k, v := range e { if v == nil { if value, ok := lookupFn(k); ok { e[k] = &value } } } return e } // RemoveEmpty excludes keys that are not associated with a value func (e MappingWithEquals) RemoveEmpty() MappingWithEquals { for k, v := range e { if v == nil { delete(e, k) } } return e } // Mapping is a mapping type that can be converted from a list of // key[=value] strings. // For the key with an empty value (`key=`), or key without value (`key`), the // mapped value is set to an empty string `""`. type Mapping map[string]string // NewMapping build a new Mapping from a set of KEY=VALUE strings func NewMapping(values []string) Mapping { mapping := Mapping{} for _, value := range values { parts := strings.SplitN(value, "=", 2) key := parts[0] switch { case len(parts) == 1: mapping[key] = "" default: mapping[key] = parts[1] } } return mapping } // Labels is a mapping type for labels type Labels map[string]string func (l Labels) Add(key, value string) Labels { if l == nil { l = Labels{} } l[key] = value return l } // MappingWithColon is a mapping type that can be converted from a list of // 'key: value' strings type MappingWithColon map[string]string // HostsList is a list of colon-separated host-ip mappings type HostsList []string // LoggingConfig the logging configuration for a service type LoggingConfig struct { Driver string `yaml:",omitempty" json:"driver,omitempty"` Options map[string]string `yaml:",omitempty" json:"options,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // DeployConfig the deployment configuration for a service type DeployConfig struct { Mode string `yaml:",omitempty" json:"mode,omitempty"` Replicas *uint64 `yaml:",omitempty" json:"replicas,omitempty"` Labels Labels `yaml:",omitempty" json:"labels,omitempty"` UpdateConfig *UpdateConfig `mapstructure:"update_config" yaml:"update_config,omitempty" json:"update_config,omitempty"` RollbackConfig *UpdateConfig `mapstructure:"rollback_config" yaml:"rollback_config,omitempty" json:"rollback_config,omitempty"` Resources Resources `yaml:",omitempty" json:"resources,omitempty"` RestartPolicy *RestartPolicy `mapstructure:"restart_policy" yaml:"restart_policy,omitempty" json:"restart_policy,omitempty"` Placement Placement `yaml:",omitempty" json:"placement,omitempty"` EndpointMode string `mapstructure:"endpoint_mode" yaml:"endpoint_mode,omitempty" json:"endpoint_mode,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // HealthCheckConfig the healthcheck configuration for a service type HealthCheckConfig struct { Test HealthCheckTest `yaml:",omitempty" json:"test,omitempty"` Timeout *Duration `yaml:",omitempty" json:"timeout,omitempty"` Interval *Duration `yaml:",omitempty" json:"interval,omitempty"` Retries *uint64 `yaml:",omitempty" json:"retries,omitempty"` StartPeriod *Duration `mapstructure:"start_period" yaml:"start_period,omitempty" json:"start_period,omitempty"` Disable bool `yaml:",omitempty" json:"disable,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // HealthCheckTest is the command run to test the health of a service type HealthCheckTest []string // UpdateConfig the service update configuration type UpdateConfig struct { Parallelism *uint64 `yaml:",omitempty" json:"parallelism,omitempty"` Delay Duration `yaml:",omitempty" json:"delay,omitempty"` FailureAction string `mapstructure:"failure_action" yaml:"failure_action,omitempty" json:"failure_action,omitempty"` Monitor Duration `yaml:",omitempty" json:"monitor,omitempty"` MaxFailureRatio float32 `mapstructure:"max_failure_ratio" yaml:"max_failure_ratio,omitempty" json:"max_failure_ratio,omitempty"` Order string `yaml:",omitempty" json:"order,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // Resources the resource limits and reservations type Resources struct { Limits *Resource `yaml:",omitempty" json:"limits,omitempty"` Reservations *Resource `yaml:",omitempty" json:"reservations,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // Resource is a resource to be limited or reserved type Resource struct { // TODO: types to convert from units and ratios NanoCPUs string `mapstructure:"cpus" yaml:"cpus,omitempty" json:"cpus,omitempty"` MemoryBytes UnitBytes `mapstructure:"memory" yaml:"memory,omitempty" json:"memory,omitempty"` Devices []DeviceRequest `mapstructure:"devices" yaml:"devices,omitempty" json:"devices,omitempty"` GenericResources []GenericResource `mapstructure:"generic_resources" yaml:"generic_resources,omitempty" json:"generic_resources,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } type DeviceRequest struct { Capabilities []string `mapstructure:"capabilities" yaml:"capabilities,omitempty" json:"capabilities,omitempty"` Driver string `mapstructure:"driver" yaml:"driver,omitempty" json:"driver,omitempty"` Count int64 `mapstructure:"count" yaml:"count,omitempty" json:"count,omitempty"` IDs []string `mapstructure:"device_ids" yaml:"device_ids,omitempty" json:"device_ids,omitempty"` } // GenericResource represents a "user defined" resource which can // only be an integer (e.g: SSD=3) for a service type GenericResource struct { DiscreteResourceSpec *DiscreteGenericResource `mapstructure:"discrete_resource_spec" yaml:"discrete_resource_spec,omitempty" json:"discrete_resource_spec,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // DiscreteGenericResource represents a "user defined" resource which is defined // as an integer // "Kind" is used to describe the Kind of a resource (e.g: "GPU", "FPGA", "SSD", ...) // Value is used to count the resource (SSD=5, HDD=3, ...) type DiscreteGenericResource struct { Kind string `json:"kind"` Value int64 `json:"value"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // UnitBytes is the bytes type type UnitBytes int64 // MarshalYAML makes UnitBytes implement yaml.Marshaller func (u UnitBytes) MarshalYAML() (interface{}, error) { return fmt.Sprintf("%d", u), nil } // MarshalJSON makes UnitBytes implement json.Marshaler func (u UnitBytes) MarshalJSON() ([]byte, error) { return []byte(fmt.Sprintf(`"%d"`, u)), nil } // RestartPolicy the service restart policy type RestartPolicy struct { Condition string `yaml:",omitempty" json:"condition,omitempty"` Delay *Duration `yaml:",omitempty" json:"delay,omitempty"` MaxAttempts *uint64 `mapstructure:"max_attempts" yaml:"max_attempts,omitempty" json:"max_attempts,omitempty"` Window *Duration `yaml:",omitempty" json:"window,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // Placement constraints for the service type Placement struct { Constraints []string `yaml:",omitempty" json:"constraints,omitempty"` Preferences []PlacementPreferences `yaml:",omitempty" json:"preferences,omitempty"` MaxReplicas uint64 `mapstructure:"max_replicas_per_node" yaml:"max_replicas_per_node,omitempty" json:"max_replicas_per_node,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // PlacementPreferences is the preferences for a service placement type PlacementPreferences struct { Spread string `yaml:",omitempty" json:"spread,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // ServiceNetworkConfig is the network configuration for a service type ServiceNetworkConfig struct { Priority int `yaml:",omitempty" json:"priotirt,omitempty"` Aliases []string `yaml:",omitempty" json:"aliases,omitempty"` Ipv4Address string `mapstructure:"ipv4_address" yaml:"ipv4_address,omitempty" json:"ipv4_address,omitempty"` Ipv6Address string `mapstructure:"ipv6_address" yaml:"ipv6_address,omitempty" json:"ipv6_address,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // ServicePortConfig is the port configuration for a service type ServicePortConfig struct { Mode string `yaml:",omitempty" json:"mode,omitempty"` HostIP string `mapstructure:"host_ip" yaml:"host_ip,omitempty" json:"host_ip,omitempty"` Target uint32 `yaml:",omitempty" json:"target,omitempty"` Published uint32 `yaml:",omitempty" json:"published,omitempty"` Protocol string `yaml:",omitempty" json:"protocol,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // ParsePortConfig parse short syntax for service port configuration func ParsePortConfig(value string) ([]ServicePortConfig, error) { var portConfigs []ServicePortConfig ports, portBindings, err := nat.ParsePortSpecs([]string{value}) if err != nil { return nil, err } // We need to sort the key of the ports to make sure it is consistent keys := []string{} for port := range ports { keys = append(keys, string(port)) } sort.Strings(keys) for _, key := range keys { port := nat.Port(key) converted, err := convertPortToPortConfig(port, portBindings) if err != nil { return nil, err } portConfigs = append(portConfigs, converted...) } return portConfigs, nil } func convertPortToPortConfig(port nat.Port, portBindings map[nat.Port][]nat.PortBinding) ([]ServicePortConfig, error) { portConfigs := []ServicePortConfig{} for _, binding := range portBindings[port] { startHostPort, endHostPort, err := nat.ParsePortRange(binding.HostPort) if err != nil && binding.HostPort != "" { return nil, fmt.Errorf("invalid hostport binding (%s) for port (%s)", binding.HostPort, port.Port()) } for i := startHostPort; i <= endHostPort; i++ { portConfigs = append(portConfigs, ServicePortConfig{ HostIP: binding.HostIP, Protocol: strings.ToLower(port.Proto()), Target: uint32(port.Int()), Published: uint32(i), Mode: "ingress", }) } } return portConfigs, nil } // ServiceVolumeConfig are references to a volume used by a service type ServiceVolumeConfig struct { Type string `yaml:",omitempty" json:"type,omitempty"` Source string `yaml:",omitempty" json:"source,omitempty"` Target string `yaml:",omitempty" json:"target,omitempty"` ReadOnly bool `mapstructure:"read_only" yaml:"read_only,omitempty" json:"read_only,omitempty"` Consistency string `yaml:",omitempty" json:"consistency,omitempty"` Bind *ServiceVolumeBind `yaml:",omitempty" json:"bind,omitempty"` Volume *ServiceVolumeVolume `yaml:",omitempty" json:"volume,omitempty"` Tmpfs *ServiceVolumeTmpfs `yaml:",omitempty" json:"tmpfs,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } const ( // TypeBind is the type for mounting host dir VolumeTypeBind = "bind" // TypeVolume is the type for remote storage volumes VolumeTypeVolume = "volume" // TypeTmpfs is the type for mounting tmpfs VolumeTypeTmpfs = "tmpfs" // TypeNamedPipe is the type for mounting Windows named pipes VolumeTypeNamedPipe = "npipe" ) // ServiceVolumeBind are options for a service volume of type bind type ServiceVolumeBind struct { Propagation string `yaml:",omitempty" json:"propagation,omitempty"` CreateHostPath bool `mapstructure:"create_host_path" yaml:"create_host_path,omitempty" json:"create_host_path,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // Propagation represents the propagation of a mount. const ( // PropagationRPrivate RPRIVATE PropagationRPrivate string = "rprivate" // PropagationPrivate PRIVATE PropagationPrivate string = "private" // PropagationRShared RSHARED PropagationRShared string = "rshared" // PropagationShared SHARED PropagationShared string = "shared" // PropagationRSlave RSLAVE PropagationRSlave string = "rslave" // PropagationSlave SLAVE PropagationSlave string = "slave" ) // ServiceVolumeVolume are options for a service volume of type volume type ServiceVolumeVolume struct { NoCopy bool `mapstructure:"nocopy" yaml:"nocopy,omitempty" json:"nocopy,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // ServiceVolumeTmpfs are options for a service volume of type tmpfs type ServiceVolumeTmpfs struct { Size int64 `yaml:",omitempty" json:"size,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // FileReferenceConfig for a reference to a swarm file object type FileReferenceConfig struct { Source string `yaml:",omitempty" json:"source,omitempty"` Target string `yaml:",omitempty" json:"target,omitempty"` UID string `yaml:",omitempty" json:"uid,omitempty"` GID string `yaml:",omitempty" json:"gid,omitempty"` Mode *uint32 `yaml:",omitempty" json:"mode,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // ServiceConfigObjConfig is the config obj configuration for a service type ServiceConfigObjConfig FileReferenceConfig // ServiceSecretConfig is the secret configuration for a service type ServiceSecretConfig FileReferenceConfig // UlimitsConfig the ulimit configuration type UlimitsConfig struct { Single int `yaml:",omitempty" json:"single,omitempty"` Soft int `yaml:",omitempty" json:"soft,omitempty"` Hard int `yaml:",omitempty" json:"hard,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // MarshalYAML makes UlimitsConfig implement yaml.Marshaller func (u *UlimitsConfig) MarshalYAML() (interface{}, error) { if u.Single != 0 { return u.Single, nil } return u, nil } // MarshalJSON makes UlimitsConfig implement json.Marshaller func (u *UlimitsConfig) MarshalJSON() ([]byte, error) { if u.Single != 0 { return json.Marshal(u.Single) } // Pass as a value to avoid re-entering this method and use the default implementation return json.Marshal(*u) } // NetworkConfig for a network type NetworkConfig struct { Name string `yaml:",omitempty" json:"name,omitempty"` Driver string `yaml:",omitempty" json:"driver,omitempty"` DriverOpts map[string]string `mapstructure:"driver_opts" yaml:"driver_opts,omitempty" json:"driver_opts,omitempty"` Ipam IPAMConfig `yaml:",omitempty" json:"ipam,omitempty"` External External `yaml:",omitempty" json:"external,omitempty"` Internal bool `yaml:",omitempty" json:"internal,omitempty"` Attachable bool `yaml:",omitempty" json:"attachable,omitempty"` Labels Labels `yaml:",omitempty" json:"labels,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // IPAMConfig for a network type IPAMConfig struct { Driver string `yaml:",omitempty" json:"driver,omitempty"` Config []*IPAMPool `yaml:",omitempty" json:"config,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // IPAMPool for a network type IPAMPool struct { Subnet string `yaml:",omitempty" json:"subnet,omitempty"` Gateway string `yaml:",omitempty" json:"gateway,omitempty"` IPRange string `mapstructure:"ip_range" yaml:"ip_range,omitempty" json:"ip_range,omitempty"` AuxiliaryAddresses map[string]string `mapstructure:"aux_addresses" yaml:"aux_addresses,omitempty" json:"aux_addresses,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // VolumeConfig for a volume type VolumeConfig struct { Name string `yaml:",omitempty" json:"name,omitempty"` Driver string `yaml:",omitempty" json:"driver,omitempty"` DriverOpts map[string]string `mapstructure:"driver_opts" yaml:"driver_opts,omitempty" json:"driver_opts,omitempty"` External External `yaml:",omitempty" json:"external,omitempty"` Labels Labels `yaml:",omitempty" json:"labels,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // External identifies a Volume or Network as a reference to a resource that is // not managed, and should already exist. // External.name is deprecated and replaced by Volume.name type External struct { Name string `yaml:",omitempty" json:"name,omitempty"` External bool `yaml:",omitempty" json:"external,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // MarshalYAML makes External implement yaml.Marshaller func (e External) MarshalYAML() (interface{}, error) { if e.Name == "" { return e.External, nil } return External{Name: e.Name}, nil } // MarshalJSON makes External implement json.Marshaller func (e External) MarshalJSON() ([]byte, error) { if e.Name == "" { return []byte(fmt.Sprintf("%v", e.External)), nil } return []byte(fmt.Sprintf(`{"name": %q}`, e.Name)), nil } // CredentialSpecConfig for credential spec on Windows type CredentialSpecConfig struct { Config string `yaml:",omitempty" json:"config,omitempty"` // Config was added in API v1.40 File string `yaml:",omitempty" json:"file,omitempty"` Registry string `yaml:",omitempty" json:"registry,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } // FileObjectConfig is a config type for a file used by a service type FileObjectConfig struct { Name string `yaml:",omitempty" json:"name,omitempty"` File string `yaml:",omitempty" json:"file,omitempty"` External External `yaml:",omitempty" json:"external,omitempty"` Labels Labels `yaml:",omitempty" json:"labels,omitempty"` Driver string `yaml:",omitempty" json:"driver,omitempty"` DriverOpts map[string]string `mapstructure:"driver_opts" yaml:"driver_opts,omitempty" json:"driver_opts,omitempty"` TemplateDriver string `mapstructure:"template_driver" yaml:"template_driver,omitempty" json:"template_driver,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } const ( // ServiceConditionCompletedSuccessfully is the type for waiting until a service has completed successfully (exit code 0). ServiceConditionCompletedSuccessfully = "service_completed_successfully" // ServiceConditionHealthy is the type for waiting until a service is healthy. ServiceConditionHealthy = "service_healthy" // ServiceConditionStarted is the type for waiting until a service has started (default). ServiceConditionStarted = "service_started" ) type DependsOnConfig map[string]ServiceDependency type ServiceDependency struct { Condition string `yaml:",omitempty" json:"condition,omitempty"` Extensions map[string]interface{} `yaml:",inline" json:"-"` } type ExtendsConfig MappingWithEquals // SecretConfig for a secret type SecretConfig FileObjectConfig // ConfigObjConfig is the config for the swarm "Config" object type ConfigObjConfig FileObjectConfig ================================================ FILE: vendor/github.com/containerd/containerd/LICENSE ================================================ Apache License Version 2.0, January 2004 https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS Copyright The containerd Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/containerd/containerd/NOTICE ================================================ Docker Copyright 2012-2015 Docker, Inc. This product includes software developed at Docker, Inc. (https://www.docker.com). The following is courtesy of our legal counsel: Use and transfer of Docker may be subject to certain restrictions by the United States and other governments. It is your responsibility to ensure that your use and/or transfer does not violate applicable laws. For more information, please see https://www.bis.doc.gov See also https://www.apache.org/dev/crypto.html and/or seek legal counsel. ================================================ FILE: vendor/github.com/containerd/containerd/pkg/userns/userns_linux.go ================================================ /* Copyright The containerd Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package userns import ( "bufio" "fmt" "os" "sync" ) var ( inUserNS bool nsOnce sync.Once ) // RunningInUserNS detects whether we are currently running in a user namespace. // Originally copied from github.com/lxc/lxd/shared/util.go func RunningInUserNS() bool { nsOnce.Do(func() { file, err := os.Open("/proc/self/uid_map") if err != nil { // This kernel-provided file only exists if user namespaces are supported return } defer file.Close() buf := bufio.NewReader(file) l, _, err := buf.ReadLine() if err != nil { return } line := string(l) var a, b, c int64 fmt.Sscanf(line, "%d %d %d", &a, &b, &c) /* * We assume we are in the initial user namespace if we have a full * range - 4294967295 uids starting at uid 0. */ if a == 0 && b == 0 && c == 4294967295 { return } inUserNS = true }) return inUserNS } ================================================ FILE: vendor/github.com/containerd/containerd/pkg/userns/userns_unsupported.go ================================================ //go:build !linux /* Copyright The containerd Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package userns // RunningInUserNS is a stub for non-Linux systems // Always returns false func RunningInUserNS() bool { return false } ================================================ FILE: vendor/github.com/containerd/log/.golangci.yml ================================================ linters: enable: - exportloopref # Checks for pointers to enclosing loop variables - gofmt - goimports - gosec - ineffassign - misspell - nolintlint - revive - staticcheck - tenv # Detects using os.Setenv instead of t.Setenv since Go 1.17 - unconvert - unused - vet - dupword # Checks for duplicate words in the source code disable: - errcheck run: timeout: 5m skip-dirs: - api - cluster - design - docs - docs/man - releases - reports - test # e2e scripts ================================================ FILE: vendor/github.com/containerd/log/LICENSE ================================================ Apache License Version 2.0, January 2004 https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS Copyright The containerd Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/containerd/log/README.md ================================================ # log A Go package providing a common logging interface across containerd repositories and a way for clients to use and configure logging in containerd packages. This package is not intended to be used as a standalone logging package outside of the containerd ecosystem and is intended as an interface wrapper around a logging implementation. In the future this package may be replaced with a common go logging interface. ## Project details **log** is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE). As a containerd sub-project, you will find the: * [Project governance](https://github.com/containerd/project/blob/main/GOVERNANCE.md), * [Maintainers](https://github.com/containerd/project/blob/main/MAINTAINERS), * and [Contributing guidelines](https://github.com/containerd/project/blob/main/CONTRIBUTING.md) information in our [`containerd/project`](https://github.com/containerd/project) repository. ================================================ FILE: vendor/github.com/containerd/log/context.go ================================================ /* Copyright The containerd Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // Package log provides types and functions related to logging, passing // loggers through a context, and attaching context to the logger. // // # Transitional types // // This package contains various types that are aliases for types in [logrus]. // These aliases are intended for transitioning away from hard-coding logrus // as logging implementation. Consumers of this package are encouraged to use // the type-aliases from this package instead of directly using their logrus // equivalent. // // The intent is to replace these aliases with locally defined types and // interfaces once all consumers are no longer directly importing logrus // types. // // IMPORTANT: due to the transitional purpose of this package, it is not // guaranteed for the full logrus API to be provided in the future. As // outlined, these aliases are provided as a step to transition away from // a specific implementation which, as a result, exposes the full logrus API. // While no decisions have been made on the ultimate design and interface // provided by this package, we do not expect carrying "less common" features. package log import ( "context" "fmt" "github.com/sirupsen/logrus" ) // G is a shorthand for [GetLogger]. // // We may want to define this locally to a package to get package tagged log // messages. var G = GetLogger // L is an alias for the standard logger. var L = &Entry{ Logger: logrus.StandardLogger(), // Default is three fields plus a little extra room. Data: make(Fields, 6), } type loggerKey struct{} // Fields type to pass to "WithFields". type Fields = map[string]any // Entry is a logging entry. It contains all the fields passed with // [Entry.WithFields]. It's finally logged when Trace, Debug, Info, Warn, // Error, Fatal or Panic is called on it. These objects can be reused and // passed around as much as you wish to avoid field duplication. // // Entry is a transitional type, and currently an alias for [logrus.Entry]. type Entry = logrus.Entry // RFC3339NanoFixed is [time.RFC3339Nano] with nanoseconds padded using // zeros to ensure the formatted time is always the same number of // characters. const RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00" // Level is a logging level. type Level = logrus.Level // Supported log levels. const ( // TraceLevel level. Designates finer-grained informational events // than [DebugLevel]. TraceLevel Level = logrus.TraceLevel // DebugLevel level. Usually only enabled when debugging. Very verbose // logging. DebugLevel Level = logrus.DebugLevel // InfoLevel level. General operational entries about what's going on // inside the application. InfoLevel Level = logrus.InfoLevel // WarnLevel level. Non-critical entries that deserve eyes. WarnLevel Level = logrus.WarnLevel // ErrorLevel level. Logs errors that should definitely be noted. // Commonly used for hooks to send errors to an error tracking service. ErrorLevel Level = logrus.ErrorLevel // FatalLevel level. Logs and then calls "logger.Exit(1)". It exits // even if the logging level is set to Panic. FatalLevel Level = logrus.FatalLevel // PanicLevel level. This is the highest level of severity. Logs and // then calls panic with the message passed to Debug, Info, ... PanicLevel Level = logrus.PanicLevel ) // SetLevel sets log level globally. It returns an error if the given // level is not supported. // // level can be one of: // // - "trace" ([TraceLevel]) // - "debug" ([DebugLevel]) // - "info" ([InfoLevel]) // - "warn" ([WarnLevel]) // - "error" ([ErrorLevel]) // - "fatal" ([FatalLevel]) // - "panic" ([PanicLevel]) func SetLevel(level string) error { lvl, err := logrus.ParseLevel(level) if err != nil { return err } L.Logger.SetLevel(lvl) return nil } // GetLevel returns the current log level. func GetLevel() Level { return L.Logger.GetLevel() } // OutputFormat specifies a log output format. type OutputFormat string // Supported log output formats. const ( // TextFormat represents the text logging format. TextFormat OutputFormat = "text" // JSONFormat represents the JSON logging format. JSONFormat OutputFormat = "json" ) // SetFormat sets the log output format ([TextFormat] or [JSONFormat]). func SetFormat(format OutputFormat) error { switch format { case TextFormat: L.Logger.SetFormatter(&logrus.TextFormatter{ TimestampFormat: RFC3339NanoFixed, FullTimestamp: true, }) return nil case JSONFormat: L.Logger.SetFormatter(&logrus.JSONFormatter{ TimestampFormat: RFC3339NanoFixed, }) return nil default: return fmt.Errorf("unknown log format: %s", format) } } // WithLogger returns a new context with the provided logger. Use in // combination with logger.WithField(s) for great effect. func WithLogger(ctx context.Context, logger *Entry) context.Context { return context.WithValue(ctx, loggerKey{}, logger.WithContext(ctx)) } // GetLogger retrieves the current logger from the context. If no logger is // available, the default logger is returned. func GetLogger(ctx context.Context) *Entry { if logger := ctx.Value(loggerKey{}); logger != nil { return logger.(*Entry) } return L.WithContext(ctx) } ================================================ FILE: vendor/github.com/containerd/stargz-snapshotter/estargz/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/containerd/stargz-snapshotter/estargz/build.go ================================================ /* Copyright The containerd Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* Copyright 2019 The Go Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. */ package estargz import ( "archive/tar" "bytes" "compress/gzip" "context" "errors" "fmt" "io" "os" "path" "runtime" "strings" "sync" "github.com/containerd/stargz-snapshotter/estargz/errorutil" "github.com/klauspost/compress/zstd" digest "github.com/opencontainers/go-digest" "golang.org/x/sync/errgroup" ) type options struct { chunkSize int compressionLevel int prioritizedFiles []string missedPrioritizedFiles *[]string compression Compression ctx context.Context minChunkSize int } type Option func(o *options) error // WithChunkSize option specifies the chunk size of eStargz blob to build. func WithChunkSize(chunkSize int) Option { return func(o *options) error { o.chunkSize = chunkSize return nil } } // WithCompressionLevel option specifies the gzip compression level. // The default is gzip.BestCompression. // This option will be ignored if WithCompression option is used. // See also: https://godoc.org/compress/gzip#pkg-constants func WithCompressionLevel(level int) Option { return func(o *options) error { o.compressionLevel = level return nil } } // WithPrioritizedFiles option specifies the list of prioritized files. // These files must be complete paths that are absolute or relative to "/" // For example, all of "foo/bar", "/foo/bar", "./foo/bar" and "../foo/bar" // are treated as "/foo/bar". func WithPrioritizedFiles(files []string) Option { return func(o *options) error { o.prioritizedFiles = files return nil } } // WithAllowPrioritizeNotFound makes Build continue the execution even if some // of prioritized files specified by WithPrioritizedFiles option aren't found // in the input tar. Instead, this records all missed file names to the passed // slice. func WithAllowPrioritizeNotFound(missedFiles *[]string) Option { return func(o *options) error { if missedFiles == nil { return fmt.Errorf("WithAllowPrioritizeNotFound: slice must be passed") } o.missedPrioritizedFiles = missedFiles return nil } } // WithCompression specifies compression algorithm to be used. // Default is gzip. func WithCompression(compression Compression) Option { return func(o *options) error { o.compression = compression return nil } } // WithContext specifies a context that can be used for clean canceleration. func WithContext(ctx context.Context) Option { return func(o *options) error { o.ctx = ctx return nil } } // WithMinChunkSize option specifies the minimal number of bytes of data // must be written in one gzip stream. // By increasing this number, one gzip stream can contain multiple files // and it hopefully leads to smaller result blob. // NOTE: This adds a TOC property that old reader doesn't understand. func WithMinChunkSize(minChunkSize int) Option { return func(o *options) error { o.minChunkSize = minChunkSize return nil } } // Blob is an eStargz blob. type Blob struct { io.ReadCloser diffID digest.Digester tocDigest digest.Digest } // DiffID returns the digest of uncompressed blob. // It is only valid to call DiffID after Close. func (b *Blob) DiffID() digest.Digest { return b.diffID.Digest() } // TOCDigest returns the digest of uncompressed TOC JSON. func (b *Blob) TOCDigest() digest.Digest { return b.tocDigest } // Build builds an eStargz blob which is an extended version of stargz, from a blob (gzip, zstd // or plain tar) passed through the argument. If there are some prioritized files are listed in // the option, these files are grouped as "prioritized" and can be used for runtime optimization // (e.g. prefetch). This function builds a blob in parallel, with dividing that blob into several // (at least the number of runtime.GOMAXPROCS(0)) sub-blobs. func Build(tarBlob *io.SectionReader, opt ...Option) (_ *Blob, rErr error) { var opts options opts.compressionLevel = gzip.BestCompression // BestCompression by default for _, o := range opt { if err := o(&opts); err != nil { return nil, err } } if opts.compression == nil { opts.compression = newGzipCompressionWithLevel(opts.compressionLevel) } layerFiles := newTempFiles() ctx := opts.ctx if ctx == nil { ctx = context.Background() } done := make(chan struct{}) defer close(done) go func() { select { case <-done: // nop case <-ctx.Done(): layerFiles.CleanupAll() } }() defer func() { if rErr != nil { if err := layerFiles.CleanupAll(); err != nil { rErr = fmt.Errorf("failed to cleanup tmp files: %v: %w", err, rErr) } } if cErr := ctx.Err(); cErr != nil { rErr = fmt.Errorf("error from context %q: %w", cErr, rErr) } }() tarBlob, err := decompressBlob(tarBlob, layerFiles) if err != nil { return nil, err } entries, err := sortEntries(tarBlob, opts.prioritizedFiles, opts.missedPrioritizedFiles) if err != nil { return nil, err } var tarParts [][]*entry if opts.minChunkSize > 0 { // Each entry needs to know the size of the current gzip stream so they // cannot be processed in parallel. tarParts = [][]*entry{entries} } else { tarParts = divideEntries(entries, runtime.GOMAXPROCS(0)) } writers := make([]*Writer, len(tarParts)) payloads := make([]*os.File, len(tarParts)) var mu sync.Mutex var eg errgroup.Group for i, parts := range tarParts { i, parts := i, parts // builds verifiable stargz sub-blobs eg.Go(func() error { esgzFile, err := layerFiles.TempFile("", "esgzdata") if err != nil { return err } sw := NewWriterWithCompressor(esgzFile, opts.compression) sw.ChunkSize = opts.chunkSize sw.MinChunkSize = opts.minChunkSize if sw.needsOpenGzEntries == nil { sw.needsOpenGzEntries = make(map[string]struct{}) } for _, f := range []string{PrefetchLandmark, NoPrefetchLandmark} { sw.needsOpenGzEntries[f] = struct{}{} } if err := sw.AppendTar(readerFromEntries(parts...)); err != nil { return err } mu.Lock() writers[i] = sw payloads[i] = esgzFile mu.Unlock() return nil }) } if err := eg.Wait(); err != nil { rErr = err return nil, err } tocAndFooter, tocDgst, err := closeWithCombine(writers...) if err != nil { rErr = err return nil, err } var rs []io.Reader for _, p := range payloads { fs, err := fileSectionReader(p) if err != nil { return nil, err } rs = append(rs, fs) } diffID := digest.Canonical.Digester() pr, pw := io.Pipe() go func() { r, err := opts.compression.Reader(io.TeeReader(io.MultiReader(append(rs, tocAndFooter)...), pw)) if err != nil { pw.CloseWithError(err) return } defer r.Close() if _, err := io.Copy(diffID.Hash(), r); err != nil { pw.CloseWithError(err) return } pw.Close() }() return &Blob{ ReadCloser: readCloser{ Reader: pr, closeFunc: layerFiles.CleanupAll, }, tocDigest: tocDgst, diffID: diffID, }, nil } // closeWithCombine takes unclosed Writers and close them. This also returns the // toc that combined all Writers into. // Writers doesn't write TOC and footer to the underlying writers so they can be // combined into a single eStargz and tocAndFooter returned by this function can // be appended at the tail of that combined blob. func closeWithCombine(ws ...*Writer) (tocAndFooterR io.Reader, tocDgst digest.Digest, err error) { if len(ws) == 0 { return nil, "", fmt.Errorf("at least one writer must be passed") } for _, w := range ws { if w.closed { return nil, "", fmt.Errorf("writer must be unclosed") } defer func(w *Writer) { w.closed = true }(w) if err := w.closeGz(); err != nil { return nil, "", err } if err := w.bw.Flush(); err != nil { return nil, "", err } } var ( mtoc = new(JTOC) currentOffset int64 ) mtoc.Version = ws[0].toc.Version for _, w := range ws { for _, e := range w.toc.Entries { // Recalculate Offset of non-empty files/chunks if (e.Type == "reg" && e.Size > 0) || e.Type == "chunk" { e.Offset += currentOffset } mtoc.Entries = append(mtoc.Entries, e) } if w.toc.Version > mtoc.Version { mtoc.Version = w.toc.Version } currentOffset += w.cw.n } return tocAndFooter(ws[0].compressor, mtoc, currentOffset) } func tocAndFooter(compressor Compressor, toc *JTOC, offset int64) (io.Reader, digest.Digest, error) { buf := new(bytes.Buffer) tocDigest, err := compressor.WriteTOCAndFooter(buf, offset, toc, nil) if err != nil { return nil, "", err } return buf, tocDigest, nil } // divideEntries divides passed entries to the parts at least the number specified by the // argument. func divideEntries(entries []*entry, minPartsNum int) (set [][]*entry) { var estimatedSize int64 for _, e := range entries { estimatedSize += e.header.Size } unitSize := estimatedSize / int64(minPartsNum) var ( nextEnd = unitSize offset int64 ) set = append(set, []*entry{}) for _, e := range entries { set[len(set)-1] = append(set[len(set)-1], e) offset += e.header.Size if offset > nextEnd { set = append(set, []*entry{}) nextEnd += unitSize } } return } var errNotFound = errors.New("not found") // sortEntries reads the specified tar blob and returns a list of tar entries. // If some of prioritized files are specified, the list starts from these // files with keeping the order specified by the argument. func sortEntries(in io.ReaderAt, prioritized []string, missedPrioritized *[]string) ([]*entry, error) { // Import tar file. intar, err := importTar(in) if err != nil { return nil, fmt.Errorf("failed to sort: %w", err) } // Sort the tar file respecting to the prioritized files list. sorted := &tarFile{} for _, l := range prioritized { if err := moveRec(l, intar, sorted); err != nil { if errors.Is(err, errNotFound) && missedPrioritized != nil { *missedPrioritized = append(*missedPrioritized, l) continue // allow not found } return nil, fmt.Errorf("failed to sort tar entries: %w", err) } } if len(prioritized) == 0 { sorted.add(&entry{ header: &tar.Header{ Name: NoPrefetchLandmark, Typeflag: tar.TypeReg, Size: int64(len([]byte{landmarkContents})), }, payload: bytes.NewReader([]byte{landmarkContents}), }) } else { sorted.add(&entry{ header: &tar.Header{ Name: PrefetchLandmark, Typeflag: tar.TypeReg, Size: int64(len([]byte{landmarkContents})), }, payload: bytes.NewReader([]byte{landmarkContents}), }) } // Dump all entry and concatinate them. return append(sorted.dump(), intar.dump()...), nil } // readerFromEntries returns a reader of tar archive that contains entries passed // through the arguments. func readerFromEntries(entries ...*entry) io.Reader { pr, pw := io.Pipe() go func() { tw := tar.NewWriter(pw) defer tw.Close() for _, entry := range entries { if err := tw.WriteHeader(entry.header); err != nil { pw.CloseWithError(fmt.Errorf("Failed to write tar header: %v", err)) return } if _, err := io.Copy(tw, entry.payload); err != nil { pw.CloseWithError(fmt.Errorf("Failed to write tar payload: %v", err)) return } } pw.Close() }() return pr } func importTar(in io.ReaderAt) (*tarFile, error) { tf := &tarFile{} pw, err := newCountReadSeeker(in) if err != nil { return nil, fmt.Errorf("failed to make position watcher: %w", err) } tr := tar.NewReader(pw) // Walk through all nodes. for { // Fetch and parse next header. h, err := tr.Next() if err != nil { if err == io.EOF { break } else { return nil, fmt.Errorf("failed to parse tar file, %w", err) } } switch cleanEntryName(h.Name) { case PrefetchLandmark, NoPrefetchLandmark: // Ignore existing landmark continue } // Add entry. If it already exists, replace it. if _, ok := tf.get(h.Name); ok { tf.remove(h.Name) } tf.add(&entry{ header: h, payload: io.NewSectionReader(in, pw.currentPos(), h.Size), }) } return tf, nil } func moveRec(name string, in *tarFile, out *tarFile) error { name = cleanEntryName(name) if name == "" { // root directory. stop recursion. if e, ok := in.get(name); ok { // entry of the root directory exists. we should move it as well. // this case will occur if tar entries are prefixed with "./", "/", etc. out.add(e) in.remove(name) } return nil } _, okIn := in.get(name) _, okOut := out.get(name) if !okIn && !okOut { return fmt.Errorf("file: %q: %w", name, errNotFound) } parent, _ := path.Split(strings.TrimSuffix(name, "/")) if err := moveRec(parent, in, out); err != nil { return err } if e, ok := in.get(name); ok && e.header.Typeflag == tar.TypeLink { if err := moveRec(e.header.Linkname, in, out); err != nil { return err } } if e, ok := in.get(name); ok { out.add(e) in.remove(name) } return nil } type entry struct { header *tar.Header payload io.ReadSeeker } type tarFile struct { index map[string]*entry stream []*entry } func (f *tarFile) add(e *entry) { if f.index == nil { f.index = make(map[string]*entry) } f.index[cleanEntryName(e.header.Name)] = e f.stream = append(f.stream, e) } func (f *tarFile) remove(name string) { name = cleanEntryName(name) if f.index != nil { delete(f.index, name) } var filtered []*entry for _, e := range f.stream { if cleanEntryName(e.header.Name) == name { continue } filtered = append(filtered, e) } f.stream = filtered } func (f *tarFile) get(name string) (e *entry, ok bool) { if f.index == nil { return nil, false } e, ok = f.index[cleanEntryName(name)] return } func (f *tarFile) dump() []*entry { return f.stream } type readCloser struct { io.Reader closeFunc func() error } func (rc readCloser) Close() error { return rc.closeFunc() } func fileSectionReader(file *os.File) (*io.SectionReader, error) { info, err := file.Stat() if err != nil { return nil, err } return io.NewSectionReader(file, 0, info.Size()), nil } func newTempFiles() *tempFiles { return &tempFiles{} } type tempFiles struct { files []*os.File filesMu sync.Mutex cleanupOnce sync.Once } func (tf *tempFiles) TempFile(dir, pattern string) (*os.File, error) { f, err := os.CreateTemp(dir, pattern) if err != nil { return nil, err } tf.filesMu.Lock() tf.files = append(tf.files, f) tf.filesMu.Unlock() return f, nil } func (tf *tempFiles) CleanupAll() (err error) { tf.cleanupOnce.Do(func() { err = tf.cleanupAll() }) return } func (tf *tempFiles) cleanupAll() error { tf.filesMu.Lock() defer tf.filesMu.Unlock() var allErr []error for _, f := range tf.files { if err := f.Close(); err != nil { allErr = append(allErr, err) } if err := os.Remove(f.Name()); err != nil { allErr = append(allErr, err) } } tf.files = nil return errorutil.Aggregate(allErr) } func newCountReadSeeker(r io.ReaderAt) (*countReadSeeker, error) { pos := int64(0) return &countReadSeeker{r: r, cPos: &pos}, nil } type countReadSeeker struct { r io.ReaderAt cPos *int64 mu sync.Mutex } func (cr *countReadSeeker) Read(p []byte) (int, error) { cr.mu.Lock() defer cr.mu.Unlock() n, err := cr.r.ReadAt(p, *cr.cPos) if err == nil { *cr.cPos += int64(n) } return n, err } func (cr *countReadSeeker) Seek(offset int64, whence int) (int64, error) { cr.mu.Lock() defer cr.mu.Unlock() switch whence { default: return 0, fmt.Errorf("Unknown whence: %v", whence) case io.SeekStart: case io.SeekCurrent: offset += *cr.cPos case io.SeekEnd: return 0, fmt.Errorf("Unsupported whence: %v", whence) } if offset < 0 { return 0, fmt.Errorf("invalid offset") } *cr.cPos = offset return offset, nil } func (cr *countReadSeeker) currentPos() int64 { cr.mu.Lock() defer cr.mu.Unlock() return *cr.cPos } func decompressBlob(org *io.SectionReader, tmp *tempFiles) (*io.SectionReader, error) { if org.Size() < 4 { return org, nil } src := make([]byte, 4) if _, err := org.Read(src); err != nil && err != io.EOF { return nil, err } var dR io.Reader if bytes.Equal([]byte{0x1F, 0x8B, 0x08}, src[:3]) { // gzip dgR, err := gzip.NewReader(io.NewSectionReader(org, 0, org.Size())) if err != nil { return nil, err } defer dgR.Close() dR = io.Reader(dgR) } else if bytes.Equal([]byte{0x28, 0xb5, 0x2f, 0xfd}, src[:4]) { // zstd dzR, err := zstd.NewReader(io.NewSectionReader(org, 0, org.Size())) if err != nil { return nil, err } defer dzR.Close() dR = io.Reader(dzR) } else { // uncompressed return io.NewSectionReader(org, 0, org.Size()), nil } b, err := tmp.TempFile("", "uncompresseddata") if err != nil { return nil, err } if _, err := io.Copy(b, dR); err != nil { return nil, err } return fileSectionReader(b) } ================================================ FILE: vendor/github.com/containerd/stargz-snapshotter/estargz/errorutil/errors.go ================================================ /* Copyright The containerd Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package errorutil import ( "errors" "fmt" "strings" ) // Aggregate combines a list of errors into a single new error. func Aggregate(errs []error) error { switch len(errs) { case 0: return nil case 1: return errs[0] default: points := make([]string, len(errs)+1) points[0] = fmt.Sprintf("%d error(s) occurred:", len(errs)) for i, err := range errs { points[i+1] = fmt.Sprintf("* %s", err) } return errors.New(strings.Join(points, "\n\t")) } } ================================================ FILE: vendor/github.com/containerd/stargz-snapshotter/estargz/estargz.go ================================================ /* Copyright The containerd Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* Copyright 2019 The Go Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. */ package estargz import ( "bufio" "bytes" "compress/gzip" "crypto/sha256" "errors" "fmt" "hash" "io" "os" "path" "sort" "strings" "sync" "time" "github.com/containerd/stargz-snapshotter/estargz/errorutil" digest "github.com/opencontainers/go-digest" "github.com/vbatts/tar-split/archive/tar" ) // A Reader permits random access reads from a stargz file. type Reader struct { sr *io.SectionReader toc *JTOC tocDigest digest.Digest // m stores all non-chunk entries, keyed by name. m map[string]*TOCEntry // chunks stores all TOCEntry values for regular files that // are split up. For a file with a single chunk, it's only // stored in m. chunks map[string][]*TOCEntry decompressor Decompressor } type openOpts struct { tocOffset int64 decompressors []Decompressor telemetry *Telemetry } // OpenOption is an option used during opening the layer type OpenOption func(o *openOpts) error // WithTOCOffset option specifies the offset of TOC func WithTOCOffset(tocOffset int64) OpenOption { return func(o *openOpts) error { o.tocOffset = tocOffset return nil } } // WithDecompressors option specifies decompressors to use. // Default is gzip-based decompressor. func WithDecompressors(decompressors ...Decompressor) OpenOption { return func(o *openOpts) error { o.decompressors = decompressors return nil } } // WithTelemetry option specifies the telemetry hooks func WithTelemetry(telemetry *Telemetry) OpenOption { return func(o *openOpts) error { o.telemetry = telemetry return nil } } // MeasureLatencyHook is a func which takes start time and records the diff type MeasureLatencyHook func(time.Time) // Telemetry is a struct which defines telemetry hooks. By implementing these hooks you should be able to record // the latency metrics of the respective steps of estargz open operation. To be used with estargz.OpenWithTelemetry(...) type Telemetry struct { GetFooterLatency MeasureLatencyHook // measure time to get stargz footer (in milliseconds) GetTocLatency MeasureLatencyHook // measure time to GET TOC JSON (in milliseconds) DeserializeTocLatency MeasureLatencyHook // measure time to deserialize TOC JSON (in milliseconds) } // Open opens a stargz file for reading. // The behavior is configurable using options. // // Note that each entry name is normalized as the path that is relative to root. func Open(sr *io.SectionReader, opt ...OpenOption) (*Reader, error) { var opts openOpts for _, o := range opt { if err := o(&opts); err != nil { return nil, err } } gzipCompressors := []Decompressor{new(GzipDecompressor), new(LegacyGzipDecompressor)} decompressors := append(gzipCompressors, opts.decompressors...) // Determine the size to fetch. Try to fetch as many bytes as possible. fetchSize := maxFooterSize(sr.Size(), decompressors...) if maybeTocOffset := opts.tocOffset; maybeTocOffset > fetchSize { if maybeTocOffset > sr.Size() { return nil, fmt.Errorf("blob size %d is smaller than the toc offset", sr.Size()) } fetchSize = sr.Size() - maybeTocOffset } start := time.Now() // before getting layer footer footer := make([]byte, fetchSize) if _, err := sr.ReadAt(footer, sr.Size()-fetchSize); err != nil { return nil, fmt.Errorf("error reading footer: %v", err) } if opts.telemetry != nil && opts.telemetry.GetFooterLatency != nil { opts.telemetry.GetFooterLatency(start) } var allErr []error var found bool var r *Reader for _, d := range decompressors { fSize := d.FooterSize() fOffset := positive(int64(len(footer)) - fSize) maybeTocBytes := footer[:fOffset] _, tocOffset, tocSize, err := d.ParseFooter(footer[fOffset:]) if err != nil { allErr = append(allErr, err) continue } if tocOffset >= 0 && tocSize <= 0 { tocSize = sr.Size() - tocOffset - fSize } if tocOffset >= 0 && tocSize < int64(len(maybeTocBytes)) { maybeTocBytes = maybeTocBytes[:tocSize] } r, err = parseTOC(d, sr, tocOffset, tocSize, maybeTocBytes, opts) if err == nil { found = true break } allErr = append(allErr, err) } if !found { return nil, errorutil.Aggregate(allErr) } if err := r.initFields(); err != nil { return nil, fmt.Errorf("failed to initialize fields of entries: %v", err) } return r, nil } // OpenFooter extracts and parses footer from the given blob. // only supports gzip-based eStargz. func OpenFooter(sr *io.SectionReader) (tocOffset int64, footerSize int64, rErr error) { if sr.Size() < FooterSize && sr.Size() < legacyFooterSize { return 0, 0, fmt.Errorf("blob size %d is smaller than the footer size", sr.Size()) } var footer [FooterSize]byte if _, err := sr.ReadAt(footer[:], sr.Size()-FooterSize); err != nil { return 0, 0, fmt.Errorf("error reading footer: %v", err) } var allErr []error for _, d := range []Decompressor{new(GzipDecompressor), new(LegacyGzipDecompressor)} { fSize := d.FooterSize() fOffset := positive(int64(len(footer)) - fSize) _, tocOffset, _, err := d.ParseFooter(footer[fOffset:]) if err == nil { return tocOffset, fSize, err } allErr = append(allErr, err) } return 0, 0, errorutil.Aggregate(allErr) } // initFields populates the Reader from r.toc after decoding it from // JSON. // // Unexported fields are populated and TOCEntry fields that were // implicit in the JSON are populated. func (r *Reader) initFields() error { r.m = make(map[string]*TOCEntry, len(r.toc.Entries)) r.chunks = make(map[string][]*TOCEntry) var lastPath string uname := map[int]string{} gname := map[int]string{} var lastRegEnt *TOCEntry var chunkTopIndex int for i, ent := range r.toc.Entries { ent.Name = cleanEntryName(ent.Name) switch ent.Type { case "reg", "chunk": if ent.Offset != r.toc.Entries[chunkTopIndex].Offset { chunkTopIndex = i } ent.chunkTopIndex = chunkTopIndex } if ent.Type == "reg" { lastRegEnt = ent } if ent.Type == "chunk" { ent.Name = lastPath r.chunks[ent.Name] = append(r.chunks[ent.Name], ent) if ent.ChunkSize == 0 && lastRegEnt != nil { ent.ChunkSize = lastRegEnt.Size - ent.ChunkOffset } } else { lastPath = ent.Name if ent.Uname != "" { uname[ent.UID] = ent.Uname } else { ent.Uname = uname[ent.UID] } if ent.Gname != "" { gname[ent.GID] = ent.Gname } else { ent.Gname = uname[ent.GID] } ent.modTime, _ = time.Parse(time.RFC3339, ent.ModTime3339) if ent.Type == "dir" { ent.NumLink++ // Parent dir links to this directory } r.m[ent.Name] = ent } if ent.Type == "reg" && ent.ChunkSize > 0 && ent.ChunkSize < ent.Size { r.chunks[ent.Name] = make([]*TOCEntry, 0, ent.Size/ent.ChunkSize+1) r.chunks[ent.Name] = append(r.chunks[ent.Name], ent) } if ent.ChunkSize == 0 && ent.Size != 0 { ent.ChunkSize = ent.Size } } // Populate children, add implicit directories: for _, ent := range r.toc.Entries { if ent.Type == "chunk" { continue } // add "foo/": // add "foo" child to "" (creating "" if necessary) // // add "foo/bar/": // add "bar" child to "foo" (creating "foo" if necessary) // // add "foo/bar.txt": // add "bar.txt" child to "foo" (creating "foo" if necessary) // // add "a/b/c/d/e/f.txt": // create "a/b/c/d/e" node // add "f.txt" child to "e" name := ent.Name pdirName := parentDir(name) if name == pdirName { // This entry and its parent are the same. // Ignore this for avoiding infinite loop of the reference. // The example case where this can occur is when tar contains the root // directory itself (e.g. "./", "/"). continue } pdir := r.getOrCreateDir(pdirName) ent.NumLink++ // at least one name(ent.Name) references this entry. if ent.Type == "hardlink" { org, err := r.getSource(ent) if err != nil { return err } org.NumLink++ // original entry is referenced by this ent.Name. ent = org } pdir.addChild(path.Base(name), ent) } lastOffset := r.sr.Size() for i := len(r.toc.Entries) - 1; i >= 0; i-- { e := r.toc.Entries[i] if e.isDataType() { e.nextOffset = lastOffset } if e.Offset != 0 && e.InnerOffset == 0 { lastOffset = e.Offset } } return nil } func (r *Reader) getSource(ent *TOCEntry) (_ *TOCEntry, err error) { if ent.Type == "hardlink" { org, ok := r.m[cleanEntryName(ent.LinkName)] if !ok { return nil, fmt.Errorf("%q is a hardlink but the linkname %q isn't found", ent.Name, ent.LinkName) } ent, err = r.getSource(org) if err != nil { return nil, err } } return ent, nil } func parentDir(p string) string { dir, _ := path.Split(p) return strings.TrimSuffix(dir, "/") } func (r *Reader) getOrCreateDir(d string) *TOCEntry { e, ok := r.m[d] if !ok { e = &TOCEntry{ Name: d, Type: "dir", Mode: 0755, NumLink: 2, // The directory itself(.) and the parent link to this directory. } r.m[d] = e if d != "" { pdir := r.getOrCreateDir(parentDir(d)) pdir.addChild(path.Base(d), e) } } return e } func (r *Reader) TOCDigest() digest.Digest { return r.tocDigest } // VerifyTOC checks that the TOC JSON in the passed blob matches the // passed digests and that the TOC JSON contains digests for all chunks // contained in the blob. If the verification succceeds, this function // returns TOCEntryVerifier which holds all chunk digests in the stargz blob. func (r *Reader) VerifyTOC(tocDigest digest.Digest) (TOCEntryVerifier, error) { // Verify the digest of TOC JSON if r.tocDigest != tocDigest { return nil, fmt.Errorf("invalid TOC JSON %q; want %q", r.tocDigest, tocDigest) } return r.Verifiers() } // Verifiers returns TOCEntryVerifier of this chunk. Use VerifyTOC instead in most cases // because this doesn't verify TOC. func (r *Reader) Verifiers() (TOCEntryVerifier, error) { chunkDigestMap := make(map[int64]digest.Digest) // map from chunk offset to the chunk digest regDigestMap := make(map[int64]digest.Digest) // map from chunk offset to the reg file digest var chunkDigestMapIncomplete bool var regDigestMapIncomplete bool var containsChunk bool for _, e := range r.toc.Entries { if e.Type != "reg" && e.Type != "chunk" { continue } // offset must be unique in stargz blob _, dOK := chunkDigestMap[e.Offset] _, rOK := regDigestMap[e.Offset] if dOK || rOK { return nil, fmt.Errorf("offset %d found twice", e.Offset) } if e.Type == "reg" { if e.Size == 0 { continue // ignores empty file } // record the digest of regular file payload if e.Digest != "" { d, err := digest.Parse(e.Digest) if err != nil { return nil, fmt.Errorf("failed to parse regular file digest %q: %w", e.Digest, err) } regDigestMap[e.Offset] = d } else { regDigestMapIncomplete = true } } else { containsChunk = true // this layer contains "chunk" entries. } // "reg" also can contain ChunkDigest (e.g. when "reg" is the first entry of // chunked file) if e.ChunkDigest != "" { d, err := digest.Parse(e.ChunkDigest) if err != nil { return nil, fmt.Errorf("failed to parse chunk digest %q: %w", e.ChunkDigest, err) } chunkDigestMap[e.Offset] = d } else { chunkDigestMapIncomplete = true } } if chunkDigestMapIncomplete { // Though some chunk digests are not found, if this layer doesn't contain // "chunk"s and all digest of "reg" files are recorded, we can use them instead. if !containsChunk && !regDigestMapIncomplete { return &verifier{digestMap: regDigestMap}, nil } return nil, fmt.Errorf("some ChunkDigest not found in TOC JSON") } return &verifier{digestMap: chunkDigestMap}, nil } // verifier is an implementation of TOCEntryVerifier which holds verifiers keyed by // offset of the chunk. type verifier struct { digestMap map[int64]digest.Digest digestMapMu sync.Mutex } // Verifier returns a content verifier specified by TOCEntry. func (v *verifier) Verifier(ce *TOCEntry) (digest.Verifier, error) { v.digestMapMu.Lock() defer v.digestMapMu.Unlock() d, ok := v.digestMap[ce.Offset] if !ok { return nil, fmt.Errorf("verifier for offset=%d,size=%d hasn't been registered", ce.Offset, ce.ChunkSize) } return d.Verifier(), nil } // ChunkEntryForOffset returns the TOCEntry containing the byte of the // named file at the given offset within the file. // Name must be absolute path or one that is relative to root. func (r *Reader) ChunkEntryForOffset(name string, offset int64) (e *TOCEntry, ok bool) { name = cleanEntryName(name) e, ok = r.Lookup(name) if !ok || !e.isDataType() { return nil, false } ents := r.chunks[name] if len(ents) < 2 { if offset >= e.ChunkSize { return nil, false } return e, true } i := sort.Search(len(ents), func(i int) bool { e := ents[i] return e.ChunkOffset >= offset || (offset > e.ChunkOffset && offset < e.ChunkOffset+e.ChunkSize) }) if i == len(ents) { return nil, false } return ents[i], true } // Lookup returns the Table of Contents entry for the given path. // // To get the root directory, use the empty string. // Path must be absolute path or one that is relative to root. func (r *Reader) Lookup(path string) (e *TOCEntry, ok bool) { path = cleanEntryName(path) if r == nil { return } e, ok = r.m[path] if ok && e.Type == "hardlink" { var err error e, err = r.getSource(e) if err != nil { return nil, false } } return } // OpenFile returns the reader of the specified file payload. // // Name must be absolute path or one that is relative to root. func (r *Reader) OpenFile(name string) (*io.SectionReader, error) { fr, err := r.newFileReader(name) if err != nil { return nil, err } return io.NewSectionReader(fr, 0, fr.size), nil } func (r *Reader) newFileReader(name string) (*fileReader, error) { name = cleanEntryName(name) ent, ok := r.Lookup(name) if !ok { // TODO: come up with some error plan. This is lazy: return nil, &os.PathError{ Path: name, Op: "OpenFile", Err: os.ErrNotExist, } } if ent.Type != "reg" { return nil, &os.PathError{ Path: name, Op: "OpenFile", Err: errors.New("not a regular file"), } } return &fileReader{ r: r, size: ent.Size, ents: r.getChunks(ent), }, nil } func (r *Reader) OpenFileWithPreReader(name string, preRead func(*TOCEntry, io.Reader) error) (*io.SectionReader, error) { fr, err := r.newFileReader(name) if err != nil { return nil, err } fr.preRead = preRead return io.NewSectionReader(fr, 0, fr.size), nil } func (r *Reader) getChunks(ent *TOCEntry) []*TOCEntry { if ents, ok := r.chunks[ent.Name]; ok { return ents } return []*TOCEntry{ent} } type fileReader struct { r *Reader size int64 ents []*TOCEntry // 1 or more reg/chunk entries preRead func(*TOCEntry, io.Reader) error } func (fr *fileReader) ReadAt(p []byte, off int64) (n int, err error) { if off >= fr.size { return 0, io.EOF } if off < 0 { return 0, errors.New("invalid offset") } var i int if len(fr.ents) > 1 { i = sort.Search(len(fr.ents), func(i int) bool { return fr.ents[i].ChunkOffset >= off }) if i == len(fr.ents) { i = len(fr.ents) - 1 } } ent := fr.ents[i] if ent.ChunkOffset > off { if i == 0 { return 0, errors.New("internal error; first chunk offset is non-zero") } ent = fr.ents[i-1] } // If ent is a chunk of a large file, adjust the ReadAt // offset by the chunk's offset. off -= ent.ChunkOffset finalEnt := fr.ents[len(fr.ents)-1] compressedOff := ent.Offset // compressedBytesRemain is the number of compressed bytes in this // file remaining, over 1+ chunks. compressedBytesRemain := finalEnt.NextOffset() - compressedOff sr := io.NewSectionReader(fr.r.sr, compressedOff, compressedBytesRemain) const maxRead = 2 << 20 var bufSize = maxRead if compressedBytesRemain < maxRead { bufSize = int(compressedBytesRemain) } br := bufio.NewReaderSize(sr, bufSize) if _, err := br.Peek(bufSize); err != nil { return 0, fmt.Errorf("fileReader.ReadAt.peek: %v", err) } dr, err := fr.r.decompressor.Reader(br) if err != nil { return 0, fmt.Errorf("fileReader.ReadAt.decompressor.Reader: %v", err) } defer dr.Close() if fr.preRead == nil { if n, err := io.CopyN(io.Discard, dr, ent.InnerOffset+off); n != ent.InnerOffset+off || err != nil { return 0, fmt.Errorf("discard of %d bytes != %v, %v", ent.InnerOffset+off, n, err) } return io.ReadFull(dr, p) } var retN int var retErr error var found bool var nr int64 for _, e := range fr.r.toc.Entries[ent.chunkTopIndex:] { if !e.isDataType() { continue } if e.Offset != fr.r.toc.Entries[ent.chunkTopIndex].Offset { break } if in, err := io.CopyN(io.Discard, dr, e.InnerOffset-nr); err != nil || in != e.InnerOffset-nr { return 0, fmt.Errorf("discard of remaining %d bytes != %v, %v", e.InnerOffset-nr, in, err) } nr = e.InnerOffset if e == ent { found = true if n, err := io.CopyN(io.Discard, dr, off); n != off || err != nil { return 0, fmt.Errorf("discard of offset %d bytes != %v, %v", off, n, err) } retN, retErr = io.ReadFull(dr, p) nr += off + int64(retN) continue } cr := &countReader{r: io.LimitReader(dr, e.ChunkSize)} if err := fr.preRead(e, cr); err != nil { return 0, fmt.Errorf("failed to pre read: %w", err) } nr += cr.n } if !found { return 0, fmt.Errorf("fileReader.ReadAt: target entry not found") } return retN, retErr } // A Writer writes stargz files. // // Use NewWriter to create a new Writer. type Writer struct { bw *bufio.Writer cw *countWriter toc *JTOC diffHash hash.Hash // SHA-256 of uncompressed tar closed bool gz io.WriteCloser lastUsername map[int]string lastGroupname map[int]string compressor Compressor uncompressedCounter *countWriteFlusher // ChunkSize optionally controls the maximum number of bytes // of data of a regular file that can be written in one gzip // stream before a new gzip stream is started. // Zero means to use a default, currently 4 MiB. ChunkSize int // MinChunkSize optionally controls the minimum number of bytes // of data must be written in one gzip stream before a new gzip // NOTE: This adds a TOC property that stargz snapshotter < v0.13.0 doesn't understand. MinChunkSize int needsOpenGzEntries map[string]struct{} } // currentCompressionWriter writes to the current w.gz field, which can // change throughout writing a tar entry. // // Additionally, it updates w's SHA-256 of the uncompressed bytes // of the tar file. type currentCompressionWriter struct{ w *Writer } func (ccw currentCompressionWriter) Write(p []byte) (int, error) { ccw.w.diffHash.Write(p) if ccw.w.gz == nil { if err := ccw.w.condOpenGz(); err != nil { return 0, err } } return ccw.w.gz.Write(p) } func (w *Writer) chunkSize() int { if w.ChunkSize <= 0 { return 4 << 20 } return w.ChunkSize } // Unpack decompresses the given estargz blob and returns a ReadCloser of the tar blob. // TOC JSON and footer are removed. func Unpack(sr *io.SectionReader, c Decompressor) (io.ReadCloser, error) { footerSize := c.FooterSize() if sr.Size() < footerSize { return nil, fmt.Errorf("blob is too small; %d < %d", sr.Size(), footerSize) } footerOffset := sr.Size() - footerSize footer := make([]byte, footerSize) if _, err := sr.ReadAt(footer, footerOffset); err != nil { return nil, err } blobPayloadSize, _, _, err := c.ParseFooter(footer) if err != nil { return nil, fmt.Errorf("failed to parse footer: %w", err) } if blobPayloadSize < 0 { blobPayloadSize = sr.Size() } return c.Reader(io.LimitReader(sr, blobPayloadSize)) } // NewWriter returns a new stargz writer (gzip-based) writing to w. // // The writer must be closed to write its trailing table of contents. func NewWriter(w io.Writer) *Writer { return NewWriterLevel(w, gzip.BestCompression) } // NewWriterLevel returns a new stargz writer (gzip-based) writing to w. // The compression level is configurable. // // The writer must be closed to write its trailing table of contents. func NewWriterLevel(w io.Writer, compressionLevel int) *Writer { return NewWriterWithCompressor(w, NewGzipCompressorWithLevel(compressionLevel)) } // NewWriterWithCompressor returns a new stargz writer writing to w. // The compression method is configurable. // // The writer must be closed to write its trailing table of contents. func NewWriterWithCompressor(w io.Writer, c Compressor) *Writer { bw := bufio.NewWriter(w) cw := &countWriter{w: bw} return &Writer{ bw: bw, cw: cw, toc: &JTOC{Version: 1}, diffHash: sha256.New(), compressor: c, uncompressedCounter: &countWriteFlusher{}, } } // Close writes the stargz's table of contents and flushes all the // buffers, returning any error. func (w *Writer) Close() (digest.Digest, error) { if w.closed { return "", nil } defer func() { w.closed = true }() if err := w.closeGz(); err != nil { return "", err } // Write the TOC index and footer. tocDigest, err := w.compressor.WriteTOCAndFooter(w.cw, w.cw.n, w.toc, w.diffHash) if err != nil { return "", err } if err := w.bw.Flush(); err != nil { return "", err } return tocDigest, nil } func (w *Writer) closeGz() error { if w.closed { return errors.New("write on closed Writer") } if w.gz != nil { if err := w.gz.Close(); err != nil { return err } w.gz = nil } return nil } func (w *Writer) flushGz() error { if w.closed { return errors.New("flush on closed Writer") } if w.gz != nil { if f, ok := w.gz.(interface { Flush() error }); ok { return f.Flush() } } return nil } // nameIfChanged returns name, unless it was the already the value of (*mp)[id], // in which case it returns the empty string. func (w *Writer) nameIfChanged(mp *map[int]string, id int, name string) string { if name == "" { return "" } if *mp == nil { *mp = make(map[int]string) } if (*mp)[id] == name { return "" } (*mp)[id] = name return name } func (w *Writer) condOpenGz() (err error) { if w.gz == nil { w.gz, err = w.compressor.Writer(w.cw) if w.gz != nil { w.gz = w.uncompressedCounter.register(w.gz) } } return } // AppendTar reads the tar or tar.gz file from r and appends // each of its contents to w. // // The input r can optionally be gzip compressed but the output will // always be compressed by the specified compressor. func (w *Writer) AppendTar(r io.Reader) error { return w.appendTar(r, false) } // AppendTarLossLess reads the tar or tar.gz file from r and appends // each of its contents to w. // // The input r can optionally be gzip compressed but the output will // always be compressed by the specified compressor. // // The difference of this func with AppendTar is that this writes // the input tar stream into w without any modification (e.g. to header bytes). // // Note that if the input tar stream already contains TOC JSON, this returns // error because w cannot overwrite the TOC JSON to the one generated by w without // lossy modification. To avoid this error, if the input stream is known to be stargz/estargz, // you shoud decompress it and remove TOC JSON in advance. func (w *Writer) AppendTarLossLess(r io.Reader) error { return w.appendTar(r, true) } func (w *Writer) appendTar(r io.Reader, lossless bool) error { var src io.Reader br := bufio.NewReader(r) if isGzip(br) { zr, _ := gzip.NewReader(br) src = zr } else { src = io.Reader(br) } dst := currentCompressionWriter{w} var tw *tar.Writer if !lossless { tw = tar.NewWriter(dst) // use tar writer only when this isn't lossless mode. } tr := tar.NewReader(src) if lossless { tr.RawAccounting = true } prevOffset := w.cw.n var prevOffsetUncompressed int64 for { h, err := tr.Next() if err == io.EOF { if lossless { if remain := tr.RawBytes(); len(remain) > 0 { // Collect the remaining null bytes. // https://github.com/vbatts/tar-split/blob/80a436fd6164c557b131f7c59ed69bd81af69761/concept/main.go#L49-L53 if _, err := dst.Write(remain); err != nil { return err } } } break } if err != nil { return fmt.Errorf("error reading from source tar: tar.Reader.Next: %v", err) } if cleanEntryName(h.Name) == TOCTarName { // It is possible for a layer to be "stargzified" twice during the // distribution lifecycle. So we reserve "TOCTarName" here to avoid // duplicated entries in the resulting layer. if lossless { // We cannot handle this in lossless way. return fmt.Errorf("existing TOC JSON is not allowed; decompress layer before append") } continue } xattrs := make(map[string][]byte) const xattrPAXRecordsPrefix = "SCHILY.xattr." if h.PAXRecords != nil { for k, v := range h.PAXRecords { if strings.HasPrefix(k, xattrPAXRecordsPrefix) { xattrs[k[len(xattrPAXRecordsPrefix):]] = []byte(v) } } } ent := &TOCEntry{ Name: h.Name, Mode: h.Mode, UID: h.Uid, GID: h.Gid, Uname: w.nameIfChanged(&w.lastUsername, h.Uid, h.Uname), Gname: w.nameIfChanged(&w.lastGroupname, h.Gid, h.Gname), ModTime3339: formatModtime(h.ModTime), Xattrs: xattrs, } if err := w.condOpenGz(); err != nil { return err } if tw != nil { if err := tw.WriteHeader(h); err != nil { return err } } else { if _, err := dst.Write(tr.RawBytes()); err != nil { return err } } switch h.Typeflag { case tar.TypeLink: ent.Type = "hardlink" ent.LinkName = h.Linkname case tar.TypeSymlink: ent.Type = "symlink" ent.LinkName = h.Linkname case tar.TypeDir: ent.Type = "dir" case tar.TypeReg: ent.Type = "reg" ent.Size = h.Size case tar.TypeChar: ent.Type = "char" ent.DevMajor = int(h.Devmajor) ent.DevMinor = int(h.Devminor) case tar.TypeBlock: ent.Type = "block" ent.DevMajor = int(h.Devmajor) ent.DevMinor = int(h.Devminor) case tar.TypeFifo: ent.Type = "fifo" default: return fmt.Errorf("unsupported input tar entry %q", h.Typeflag) } // We need to keep a reference to the TOC entry for regular files, so that we // can fill the digest later. var regFileEntry *TOCEntry var payloadDigest digest.Digester if h.Typeflag == tar.TypeReg { regFileEntry = ent payloadDigest = digest.Canonical.Digester() } if h.Typeflag == tar.TypeReg && ent.Size > 0 { var written int64 totalSize := ent.Size // save it before we destroy ent tee := io.TeeReader(tr, payloadDigest.Hash()) for written < totalSize { chunkSize := int64(w.chunkSize()) remain := totalSize - written if remain < chunkSize { chunkSize = remain } else { ent.ChunkSize = chunkSize } // We flush the underlying compression writer here to correctly calculate "w.cw.n". if err := w.flushGz(); err != nil { return err } if w.needsOpenGz(ent) || w.cw.n-prevOffset >= int64(w.MinChunkSize) { if err := w.closeGz(); err != nil { return err } ent.Offset = w.cw.n prevOffset = ent.Offset prevOffsetUncompressed = w.uncompressedCounter.n } else { ent.Offset = prevOffset ent.InnerOffset = w.uncompressedCounter.n - prevOffsetUncompressed } ent.ChunkOffset = written chunkDigest := digest.Canonical.Digester() if err := w.condOpenGz(); err != nil { return err } teeChunk := io.TeeReader(tee, chunkDigest.Hash()) var out io.Writer if tw != nil { out = tw } else { out = dst } if _, err := io.CopyN(out, teeChunk, chunkSize); err != nil { return fmt.Errorf("error copying %q: %v", h.Name, err) } ent.ChunkDigest = chunkDigest.Digest().String() w.toc.Entries = append(w.toc.Entries, ent) written += chunkSize ent = &TOCEntry{ Name: h.Name, Type: "chunk", } } } else { w.toc.Entries = append(w.toc.Entries, ent) } if payloadDigest != nil { regFileEntry.Digest = payloadDigest.Digest().String() } if tw != nil { if err := tw.Flush(); err != nil { return err } } } remainDest := io.Discard if lossless { remainDest = dst // Preserve the remaining bytes in lossless mode } _, err := io.Copy(remainDest, src) return err } func (w *Writer) needsOpenGz(ent *TOCEntry) bool { if ent.Type != "reg" { return false } if w.needsOpenGzEntries == nil { return false } _, ok := w.needsOpenGzEntries[ent.Name] return ok } // DiffID returns the SHA-256 of the uncompressed tar bytes. // It is only valid to call DiffID after Close. func (w *Writer) DiffID() string { return fmt.Sprintf("sha256:%x", w.diffHash.Sum(nil)) } func maxFooterSize(blobSize int64, decompressors ...Decompressor) (res int64) { for _, d := range decompressors { if s := d.FooterSize(); res < s && s <= blobSize { res = s } } return } func parseTOC(d Decompressor, sr *io.SectionReader, tocOff, tocSize int64, tocBytes []byte, opts openOpts) (*Reader, error) { if tocOff < 0 { // This means that TOC isn't contained in the blob. // We pass nil reader to ParseTOC and expect that ParseTOC acquire TOC from // the external location. start := time.Now() toc, tocDgst, err := d.ParseTOC(nil) if err != nil { return nil, err } if opts.telemetry != nil && opts.telemetry.GetTocLatency != nil { opts.telemetry.GetTocLatency(start) } if opts.telemetry != nil && opts.telemetry.DeserializeTocLatency != nil { opts.telemetry.DeserializeTocLatency(start) } return &Reader{ sr: sr, toc: toc, tocDigest: tocDgst, decompressor: d, }, nil } if len(tocBytes) > 0 { start := time.Now() toc, tocDgst, err := d.ParseTOC(bytes.NewReader(tocBytes)) if err == nil { if opts.telemetry != nil && opts.telemetry.DeserializeTocLatency != nil { opts.telemetry.DeserializeTocLatency(start) } return &Reader{ sr: sr, toc: toc, tocDigest: tocDgst, decompressor: d, }, nil } } start := time.Now() tocBytes = make([]byte, tocSize) if _, err := sr.ReadAt(tocBytes, tocOff); err != nil { return nil, fmt.Errorf("error reading %d byte TOC targz: %v", len(tocBytes), err) } if opts.telemetry != nil && opts.telemetry.GetTocLatency != nil { opts.telemetry.GetTocLatency(start) } start = time.Now() toc, tocDgst, err := d.ParseTOC(bytes.NewReader(tocBytes)) if err != nil { return nil, err } if opts.telemetry != nil && opts.telemetry.DeserializeTocLatency != nil { opts.telemetry.DeserializeTocLatency(start) } return &Reader{ sr: sr, toc: toc, tocDigest: tocDgst, decompressor: d, }, nil } func formatModtime(t time.Time) string { if t.IsZero() || t.Unix() == 0 { return "" } return t.UTC().Round(time.Second).Format(time.RFC3339) } func cleanEntryName(name string) string { // Use path.Clean to consistently deal with path separators across platforms. return strings.TrimPrefix(path.Clean("/"+name), "/") } // countWriter counts how many bytes have been written to its wrapped // io.Writer. type countWriter struct { w io.Writer n int64 } func (cw *countWriter) Write(p []byte) (n int, err error) { n, err = cw.w.Write(p) cw.n += int64(n) return } type countWriteFlusher struct { io.WriteCloser n int64 } func (wc *countWriteFlusher) register(w io.WriteCloser) io.WriteCloser { wc.WriteCloser = w return wc } func (wc *countWriteFlusher) Write(p []byte) (n int, err error) { n, err = wc.WriteCloser.Write(p) wc.n += int64(n) return } func (wc *countWriteFlusher) Flush() error { if f, ok := wc.WriteCloser.(interface { Flush() error }); ok { return f.Flush() } return nil } func (wc *countWriteFlusher) Close() error { err := wc.WriteCloser.Close() wc.WriteCloser = nil return err } // isGzip reports whether br is positioned right before an upcoming gzip stream. // It does not consume any bytes from br. func isGzip(br *bufio.Reader) bool { const ( gzipID1 = 0x1f gzipID2 = 0x8b gzipDeflate = 8 ) peek, _ := br.Peek(3) return len(peek) >= 3 && peek[0] == gzipID1 && peek[1] == gzipID2 && peek[2] == gzipDeflate } func positive(n int64) int64 { if n < 0 { return 0 } return n } type countReader struct { r io.Reader n int64 } func (cr *countReader) Read(p []byte) (n int, err error) { n, err = cr.r.Read(p) cr.n += int64(n) return } ================================================ FILE: vendor/github.com/containerd/stargz-snapshotter/estargz/gzip.go ================================================ /* Copyright The containerd Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* Copyright 2019 The Go Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. */ package estargz import ( "archive/tar" "bytes" "compress/gzip" "encoding/binary" "encoding/json" "fmt" "hash" "io" "strconv" digest "github.com/opencontainers/go-digest" ) type gzipCompression struct { *GzipCompressor *GzipDecompressor } func newGzipCompressionWithLevel(level int) Compression { return &gzipCompression{ &GzipCompressor{level}, &GzipDecompressor{}, } } func NewGzipCompressor() *GzipCompressor { return &GzipCompressor{gzip.BestCompression} } func NewGzipCompressorWithLevel(level int) *GzipCompressor { return &GzipCompressor{level} } type GzipCompressor struct { compressionLevel int } func (gc *GzipCompressor) Writer(w io.Writer) (WriteFlushCloser, error) { return gzip.NewWriterLevel(w, gc.compressionLevel) } func (gc *GzipCompressor) WriteTOCAndFooter(w io.Writer, off int64, toc *JTOC, diffHash hash.Hash) (digest.Digest, error) { tocJSON, err := json.MarshalIndent(toc, "", "\t") if err != nil { return "", err } gz, _ := gzip.NewWriterLevel(w, gc.compressionLevel) gw := io.Writer(gz) if diffHash != nil { gw = io.MultiWriter(gz, diffHash) } tw := tar.NewWriter(gw) if err := tw.WriteHeader(&tar.Header{ Typeflag: tar.TypeReg, Name: TOCTarName, Size: int64(len(tocJSON)), }); err != nil { return "", err } if _, err := tw.Write(tocJSON); err != nil { return "", err } if err := tw.Close(); err != nil { return "", err } if err := gz.Close(); err != nil { return "", err } if _, err := w.Write(gzipFooterBytes(off)); err != nil { return "", err } return digest.FromBytes(tocJSON), nil } // gzipFooterBytes returns the 51 bytes footer. func gzipFooterBytes(tocOff int64) []byte { buf := bytes.NewBuffer(make([]byte, 0, FooterSize)) gz, _ := gzip.NewWriterLevel(buf, gzip.NoCompression) // MUST be NoCompression to keep 51 bytes // Extra header indicating the offset of TOCJSON // https://tools.ietf.org/html/rfc1952#section-2.3.1.1 header := make([]byte, 4) header[0], header[1] = 'S', 'G' subfield := fmt.Sprintf("%016xSTARGZ", tocOff) binary.LittleEndian.PutUint16(header[2:4], uint16(len(subfield))) // little-endian per RFC1952 gz.Header.Extra = append(header, []byte(subfield)...) gz.Close() if buf.Len() != FooterSize { panic(fmt.Sprintf("footer buffer = %d, not %d", buf.Len(), FooterSize)) } return buf.Bytes() } type GzipDecompressor struct{} func (gz *GzipDecompressor) Reader(r io.Reader) (io.ReadCloser, error) { return gzip.NewReader(r) } func (gz *GzipDecompressor) ParseTOC(r io.Reader) (toc *JTOC, tocDgst digest.Digest, err error) { return parseTOCEStargz(r) } func (gz *GzipDecompressor) ParseFooter(p []byte) (blobPayloadSize, tocOffset, tocSize int64, err error) { if len(p) != FooterSize { return 0, 0, 0, fmt.Errorf("invalid length %d cannot be parsed", len(p)) } zr, err := gzip.NewReader(bytes.NewReader(p)) if err != nil { return 0, 0, 0, err } defer zr.Close() extra := zr.Header.Extra si1, si2, subfieldlen, subfield := extra[0], extra[1], extra[2:4], extra[4:] if si1 != 'S' || si2 != 'G' { return 0, 0, 0, fmt.Errorf("invalid subfield IDs: %q, %q; want E, S", si1, si2) } if slen := binary.LittleEndian.Uint16(subfieldlen); slen != uint16(16+len("STARGZ")) { return 0, 0, 0, fmt.Errorf("invalid length of subfield %d; want %d", slen, 16+len("STARGZ")) } if string(subfield[16:]) != "STARGZ" { return 0, 0, 0, fmt.Errorf("STARGZ magic string must be included in the footer subfield") } tocOffset, err = strconv.ParseInt(string(subfield[:16]), 16, 64) if err != nil { return 0, 0, 0, fmt.Errorf("legacy: failed to parse toc offset: %w", err) } return tocOffset, tocOffset, 0, nil } func (gz *GzipDecompressor) FooterSize() int64 { return FooterSize } func (gz *GzipDecompressor) DecompressTOC(r io.Reader) (tocJSON io.ReadCloser, err error) { return decompressTOCEStargz(r) } type LegacyGzipDecompressor struct{} func (gz *LegacyGzipDecompressor) Reader(r io.Reader) (io.ReadCloser, error) { return gzip.NewReader(r) } func (gz *LegacyGzipDecompressor) ParseTOC(r io.Reader) (toc *JTOC, tocDgst digest.Digest, err error) { return parseTOCEStargz(r) } func (gz *LegacyGzipDecompressor) ParseFooter(p []byte) (blobPayloadSize, tocOffset, tocSize int64, err error) { if len(p) != legacyFooterSize { return 0, 0, 0, fmt.Errorf("legacy: invalid length %d cannot be parsed", len(p)) } zr, err := gzip.NewReader(bytes.NewReader(p)) if err != nil { return 0, 0, 0, fmt.Errorf("legacy: failed to get footer gzip reader: %w", err) } defer zr.Close() extra := zr.Header.Extra if len(extra) != 16+len("STARGZ") { return 0, 0, 0, fmt.Errorf("legacy: invalid stargz's extra field size") } if string(extra[16:]) != "STARGZ" { return 0, 0, 0, fmt.Errorf("legacy: magic string STARGZ not found") } tocOffset, err = strconv.ParseInt(string(extra[:16]), 16, 64) if err != nil { return 0, 0, 0, fmt.Errorf("legacy: failed to parse toc offset: %w", err) } return tocOffset, tocOffset, 0, nil } func (gz *LegacyGzipDecompressor) FooterSize() int64 { return legacyFooterSize } func (gz *LegacyGzipDecompressor) DecompressTOC(r io.Reader) (tocJSON io.ReadCloser, err error) { return decompressTOCEStargz(r) } func parseTOCEStargz(r io.Reader) (toc *JTOC, tocDgst digest.Digest, err error) { tr, err := decompressTOCEStargz(r) if err != nil { return nil, "", err } dgstr := digest.Canonical.Digester() toc = new(JTOC) if err := json.NewDecoder(io.TeeReader(tr, dgstr.Hash())).Decode(&toc); err != nil { return nil, "", fmt.Errorf("error decoding TOC JSON: %v", err) } if err := tr.Close(); err != nil { return nil, "", err } return toc, dgstr.Digest(), nil } func decompressTOCEStargz(r io.Reader) (tocJSON io.ReadCloser, err error) { zr, err := gzip.NewReader(r) if err != nil { return nil, fmt.Errorf("malformed TOC gzip header: %v", err) } zr.Multistream(false) tr := tar.NewReader(zr) h, err := tr.Next() if err != nil { return nil, fmt.Errorf("failed to find tar header in TOC gzip stream: %v", err) } if h.Name != TOCTarName { return nil, fmt.Errorf("TOC tar entry had name %q; expected %q", h.Name, TOCTarName) } return readCloser{tr, zr.Close}, nil } ================================================ FILE: vendor/github.com/containerd/stargz-snapshotter/estargz/testutil.go ================================================ /* Copyright The containerd Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* Copyright 2019 The Go Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. */ package estargz import ( "archive/tar" "bytes" "compress/gzip" "crypto/sha256" "encoding/json" "errors" "fmt" "io" "math/rand" "os" "path/filepath" "reflect" "sort" "strings" "testing" "time" "github.com/containerd/stargz-snapshotter/estargz/errorutil" "github.com/klauspost/compress/zstd" digest "github.com/opencontainers/go-digest" ) func init() { rand.Seed(time.Now().UnixNano()) } // TestingController is Compression with some helper methods necessary for testing. type TestingController interface { Compression TestStreams(t *testing.T, b []byte, streams []int64) DiffIDOf(*testing.T, []byte) string String() string } // CompressionTestSuite tests this pkg with controllers can build valid eStargz blobs and parse them. func CompressionTestSuite(t *testing.T, controllers ...TestingControllerFactory) { t.Run("testBuild", func(t *testing.T) { t.Parallel(); testBuild(t, controllers...) }) t.Run("testDigestAndVerify", func(t *testing.T) { t.Parallel(); testDigestAndVerify(t, controllers...) }) t.Run("testWriteAndOpen", func(t *testing.T) { t.Parallel(); testWriteAndOpen(t, controllers...) }) } type TestingControllerFactory func() TestingController const ( uncompressedType int = iota gzipType zstdType ) var srcCompressions = []int{ uncompressedType, gzipType, zstdType, } var allowedPrefix = [4]string{"", "./", "/", "../"} // testBuild tests the resulting stargz blob built by this pkg has the same // contents as the normal stargz blob. func testBuild(t *testing.T, controllers ...TestingControllerFactory) { tests := []struct { name string chunkSize int minChunkSize []int in []tarEntry }{ { name: "regfiles and directories", chunkSize: 4, in: tarOf( file("foo", "test1"), dir("foo2/"), file("foo2/bar", "test2", xAttr(map[string]string{"test": "sample"})), ), }, { name: "empty files", chunkSize: 4, in: tarOf( file("foo", "tttttt"), file("foo_empty", ""), file("foo2", "tttttt"), file("foo_empty2", ""), file("foo3", "tttttt"), file("foo_empty3", ""), file("foo4", "tttttt"), file("foo_empty4", ""), file("foo5", "tttttt"), file("foo_empty5", ""), file("foo6", "tttttt"), ), }, { name: "various files", chunkSize: 4, minChunkSize: []int{0, 64000}, in: tarOf( file("baz.txt", "bazbazbazbazbazbazbaz"), file("foo1.txt", "a"), file("bar/foo2.txt", "b"), file("foo3.txt", "c"), symlink("barlink", "test/bar.txt"), dir("test/"), dir("dev/"), blockdev("dev/testblock", 3, 4), fifo("dev/testfifo"), chardev("dev/testchar1", 5, 6), file("test/bar.txt", "testbartestbar", xAttr(map[string]string{"test2": "sample2"})), dir("test2/"), link("test2/bazlink", "baz.txt"), chardev("dev/testchar2", 1, 2), ), }, { name: "no contents", chunkSize: 4, in: tarOf( file("baz.txt", ""), symlink("barlink", "test/bar.txt"), dir("test/"), dir("dev/"), blockdev("dev/testblock", 3, 4), fifo("dev/testfifo"), chardev("dev/testchar1", 5, 6), file("test/bar.txt", "", xAttr(map[string]string{"test2": "sample2"})), dir("test2/"), link("test2/bazlink", "baz.txt"), chardev("dev/testchar2", 1, 2), ), }, } for _, tt := range tests { if len(tt.minChunkSize) == 0 { tt.minChunkSize = []int{0} } for _, srcCompression := range srcCompressions { srcCompression := srcCompression for _, newCL := range controllers { newCL := newCL for _, srcTarFormat := range []tar.Format{tar.FormatUSTAR, tar.FormatPAX, tar.FormatGNU} { srcTarFormat := srcTarFormat for _, prefix := range allowedPrefix { prefix := prefix for _, minChunkSize := range tt.minChunkSize { minChunkSize := minChunkSize t.Run(tt.name+"-"+fmt.Sprintf("compression=%v,prefix=%q,src=%d,format=%s,minChunkSize=%d", newCL(), prefix, srcCompression, srcTarFormat, minChunkSize), func(t *testing.T) { tarBlob := buildTar(t, tt.in, prefix, srcTarFormat) // Test divideEntries() entries, err := sortEntries(tarBlob, nil, nil) // identical order if err != nil { t.Fatalf("failed to parse tar: %v", err) } var merged []*entry for _, part := range divideEntries(entries, 4) { merged = append(merged, part...) } if !reflect.DeepEqual(entries, merged) { for _, e := range entries { t.Logf("Original: %v", e.header) } for _, e := range merged { t.Logf("Merged: %v", e.header) } t.Errorf("divided entries couldn't be merged") return } // Prepare sample data cl1 := newCL() wantBuf := new(bytes.Buffer) sw := NewWriterWithCompressor(wantBuf, cl1) sw.MinChunkSize = minChunkSize sw.ChunkSize = tt.chunkSize if err := sw.AppendTar(tarBlob); err != nil { t.Fatalf("failed to append tar to want stargz: %v", err) } if _, err := sw.Close(); err != nil { t.Fatalf("failed to prepare want stargz: %v", err) } wantData := wantBuf.Bytes() want, err := Open(io.NewSectionReader( bytes.NewReader(wantData), 0, int64(len(wantData))), WithDecompressors(cl1), ) if err != nil { t.Fatalf("failed to parse the want stargz: %v", err) } // Prepare testing data var opts []Option if minChunkSize > 0 { opts = append(opts, WithMinChunkSize(minChunkSize)) } cl2 := newCL() rc, err := Build(compressBlob(t, tarBlob, srcCompression), append(opts, WithChunkSize(tt.chunkSize), WithCompression(cl2))...) if err != nil { t.Fatalf("failed to build stargz: %v", err) } defer rc.Close() gotBuf := new(bytes.Buffer) if _, err := io.Copy(gotBuf, rc); err != nil { t.Fatalf("failed to copy built stargz blob: %v", err) } gotData := gotBuf.Bytes() got, err := Open(io.NewSectionReader( bytes.NewReader(gotBuf.Bytes()), 0, int64(len(gotData))), WithDecompressors(cl2), ) if err != nil { t.Fatalf("failed to parse the got stargz: %v", err) } // Check DiffID is properly calculated rc.Close() diffID := rc.DiffID() wantDiffID := cl2.DiffIDOf(t, gotData) if diffID.String() != wantDiffID { t.Errorf("DiffID = %q; want %q", diffID, wantDiffID) } // Compare as stargz if !isSameVersion(t, cl1, wantData, cl2, gotData) { t.Errorf("built stargz hasn't same json") return } if !isSameEntries(t, want, got) { t.Errorf("built stargz isn't same as the original") return } // Compare as tar.gz if !isSameTarGz(t, cl1, wantData, cl2, gotData) { t.Errorf("built stargz isn't same tar.gz") return } }) } } } } } } } func isSameTarGz(t *testing.T, cla TestingController, a []byte, clb TestingController, b []byte) bool { aGz, err := cla.Reader(bytes.NewReader(a)) if err != nil { t.Fatalf("failed to read A") } defer aGz.Close() bGz, err := clb.Reader(bytes.NewReader(b)) if err != nil { t.Fatalf("failed to read B") } defer bGz.Close() // Same as tar's Next() method but ignores landmarks and TOCJSON file next := func(r *tar.Reader) (h *tar.Header, err error) { for { if h, err = r.Next(); err != nil { return } if h.Name != PrefetchLandmark && h.Name != NoPrefetchLandmark && h.Name != TOCTarName { return } } } aTar := tar.NewReader(aGz) bTar := tar.NewReader(bGz) for { // Fetch and parse next header. aH, aErr := next(aTar) bH, bErr := next(bTar) if aErr != nil || bErr != nil { if aErr == io.EOF && bErr == io.EOF { break } t.Fatalf("Failed to parse tar file: A: %v, B: %v", aErr, bErr) } if !reflect.DeepEqual(aH, bH) { t.Logf("different header (A = %v; B = %v)", aH, bH) return false } aFile, err := io.ReadAll(aTar) if err != nil { t.Fatal("failed to read tar payload of A") } bFile, err := io.ReadAll(bTar) if err != nil { t.Fatal("failed to read tar payload of B") } if !bytes.Equal(aFile, bFile) { t.Logf("different tar payload (A = %q; B = %q)", string(a), string(b)) return false } } return true } func isSameVersion(t *testing.T, cla TestingController, a []byte, clb TestingController, b []byte) bool { aJTOC, _, err := parseStargz(io.NewSectionReader(bytes.NewReader(a), 0, int64(len(a))), cla) if err != nil { t.Fatalf("failed to parse A: %v", err) } bJTOC, _, err := parseStargz(io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b))), clb) if err != nil { t.Fatalf("failed to parse B: %v", err) } t.Logf("A: TOCJSON: %v", dumpTOCJSON(t, aJTOC)) t.Logf("B: TOCJSON: %v", dumpTOCJSON(t, bJTOC)) return aJTOC.Version == bJTOC.Version } func isSameEntries(t *testing.T, a, b *Reader) bool { aroot, ok := a.Lookup("") if !ok { t.Fatalf("failed to get root of A") } broot, ok := b.Lookup("") if !ok { t.Fatalf("failed to get root of B") } aEntry := stargzEntry{aroot, a} bEntry := stargzEntry{broot, b} return contains(t, aEntry, bEntry) && contains(t, bEntry, aEntry) } func compressBlob(t *testing.T, src *io.SectionReader, srcCompression int) *io.SectionReader { buf := new(bytes.Buffer) var w io.WriteCloser var err error if srcCompression == gzipType { w = gzip.NewWriter(buf) } else if srcCompression == zstdType { w, err = zstd.NewWriter(buf) if err != nil { t.Fatalf("failed to init zstd writer: %v", err) } } else { return src } src.Seek(0, io.SeekStart) if _, err := io.Copy(w, src); err != nil { t.Fatalf("failed to compress source") } if err := w.Close(); err != nil { t.Fatalf("failed to finalize compress source") } data := buf.Bytes() return io.NewSectionReader(bytes.NewReader(data), 0, int64(len(data))) } type stargzEntry struct { e *TOCEntry r *Reader } // contains checks if all child entries in "b" are also contained in "a". // This function also checks if the files/chunks contain the same contents among "a" and "b". func contains(t *testing.T, a, b stargzEntry) bool { ae, ar := a.e, a.r be, br := b.e, b.r t.Logf("Comparing: %q vs %q", ae.Name, be.Name) if !equalEntry(ae, be) { t.Logf("%q != %q: entry: a: %v, b: %v", ae.Name, be.Name, ae, be) return false } if ae.Type == "dir" { t.Logf("Directory: %q vs %q: %v vs %v", ae.Name, be.Name, allChildrenName(ae), allChildrenName(be)) iscontain := true ae.ForeachChild(func(aBaseName string, aChild *TOCEntry) bool { // Walk through all files on this stargz file. if aChild.Name == PrefetchLandmark || aChild.Name == NoPrefetchLandmark { return true // Ignore landmarks } // Ignore a TOCEntry of "./" (formated as "" by stargz lib) on root directory // because this points to the root directory itself. if aChild.Name == "" && ae.Name == "" { return true } bChild, ok := be.LookupChild(aBaseName) if !ok { t.Logf("%q (base: %q): not found in b: %v", ae.Name, aBaseName, allChildrenName(be)) iscontain = false return false } childcontain := contains(t, stargzEntry{aChild, a.r}, stargzEntry{bChild, b.r}) if !childcontain { t.Logf("%q != %q: non-equal dir", ae.Name, be.Name) iscontain = false return false } return true }) return iscontain } else if ae.Type == "reg" { af, err := ar.OpenFile(ae.Name) if err != nil { t.Fatalf("failed to open file %q on A: %v", ae.Name, err) } bf, err := br.OpenFile(be.Name) if err != nil { t.Fatalf("failed to open file %q on B: %v", be.Name, err) } var nr int64 for nr < ae.Size { abytes, anext, aok := readOffset(t, af, nr, a) bbytes, bnext, bok := readOffset(t, bf, nr, b) if !aok && !bok { break } else if !(aok && bok) || anext != bnext { t.Logf("%q != %q (offset=%d): chunk existence a=%v vs b=%v, anext=%v vs bnext=%v", ae.Name, be.Name, nr, aok, bok, anext, bnext) return false } nr = anext if !bytes.Equal(abytes, bbytes) { t.Logf("%q != %q: different contents %v vs %v", ae.Name, be.Name, string(abytes), string(bbytes)) return false } } return true } return true } func allChildrenName(e *TOCEntry) (children []string) { e.ForeachChild(func(baseName string, _ *TOCEntry) bool { children = append(children, baseName) return true }) return } func equalEntry(a, b *TOCEntry) bool { // Here, we selectively compare fileds that we are interested in. return a.Name == b.Name && a.Type == b.Type && a.Size == b.Size && a.ModTime3339 == b.ModTime3339 && a.Stat().ModTime().Equal(b.Stat().ModTime()) && // modTime time.Time a.LinkName == b.LinkName && a.Mode == b.Mode && a.UID == b.UID && a.GID == b.GID && a.Uname == b.Uname && a.Gname == b.Gname && (a.Offset >= 0) == (b.Offset >= 0) && (a.NextOffset() > 0) == (b.NextOffset() > 0) && a.DevMajor == b.DevMajor && a.DevMinor == b.DevMinor && a.NumLink == b.NumLink && reflect.DeepEqual(a.Xattrs, b.Xattrs) && // chunk-related infomations aren't compared in this function. // ChunkOffset int64 `json:"chunkOffset,omitempty"` // ChunkSize int64 `json:"chunkSize,omitempty"` // children map[string]*TOCEntry a.Digest == b.Digest } func readOffset(t *testing.T, r *io.SectionReader, offset int64, e stargzEntry) ([]byte, int64, bool) { ce, ok := e.r.ChunkEntryForOffset(e.e.Name, offset) if !ok { return nil, 0, false } data := make([]byte, ce.ChunkSize) t.Logf("Offset: %v, NextOffset: %v", ce.Offset, ce.NextOffset()) n, err := r.ReadAt(data, ce.ChunkOffset) if err != nil { t.Fatalf("failed to read file payload of %q (offset:%d,size:%d): %v", e.e.Name, ce.ChunkOffset, ce.ChunkSize, err) } if int64(n) != ce.ChunkSize { t.Fatalf("unexpected copied data size %d; want %d", n, ce.ChunkSize) } return data[:n], offset + ce.ChunkSize, true } func dumpTOCJSON(t *testing.T, tocJSON *JTOC) string { jtocData, err := json.Marshal(*tocJSON) if err != nil { t.Fatalf("failed to marshal TOC JSON: %v", err) } buf := new(bytes.Buffer) if _, err := io.Copy(buf, bytes.NewReader(jtocData)); err != nil { t.Fatalf("failed to read toc json blob: %v", err) } return buf.String() } const chunkSize = 3 // type check func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, compressionLevel int) type check func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) // testDigestAndVerify runs specified checks against sample stargz blobs. func testDigestAndVerify(t *testing.T, controllers ...TestingControllerFactory) { tests := []struct { name string tarInit func(t *testing.T, dgstMap map[string]digest.Digest) (blob []tarEntry) checks []check minChunkSize []int }{ { name: "no-regfile", tarInit: func(t *testing.T, dgstMap map[string]digest.Digest) (blob []tarEntry) { return tarOf( dir("test/"), ) }, checks: []check{ checkStargzTOC, checkVerifyTOC, checkVerifyInvalidStargzFail(buildTar(t, tarOf( dir("test2/"), // modified ), allowedPrefix[0])), }, }, { name: "small-files", tarInit: func(t *testing.T, dgstMap map[string]digest.Digest) (blob []tarEntry) { return tarOf( regDigest(t, "baz.txt", "", dgstMap), regDigest(t, "foo.txt", "a", dgstMap), dir("test/"), regDigest(t, "test/bar.txt", "bbb", dgstMap), ) }, minChunkSize: []int{0, 64000}, checks: []check{ checkStargzTOC, checkVerifyTOC, checkVerifyInvalidStargzFail(buildTar(t, tarOf( file("baz.txt", ""), file("foo.txt", "M"), // modified dir("test/"), file("test/bar.txt", "bbb"), ), allowedPrefix[0])), // checkVerifyInvalidTOCEntryFail("foo.txt"), // TODO checkVerifyBrokenContentFail("foo.txt"), }, }, { name: "big-files", tarInit: func(t *testing.T, dgstMap map[string]digest.Digest) (blob []tarEntry) { return tarOf( regDigest(t, "baz.txt", "bazbazbazbazbazbazbaz", dgstMap), regDigest(t, "foo.txt", "a", dgstMap), dir("test/"), regDigest(t, "test/bar.txt", "testbartestbar", dgstMap), ) }, checks: []check{ checkStargzTOC, checkVerifyTOC, checkVerifyInvalidStargzFail(buildTar(t, tarOf( file("baz.txt", "bazbazbazMMMbazbazbaz"), // modified file("foo.txt", "a"), dir("test/"), file("test/bar.txt", "testbartestbar"), ), allowedPrefix[0])), checkVerifyInvalidTOCEntryFail("test/bar.txt"), checkVerifyBrokenContentFail("test/bar.txt"), }, }, { name: "with-non-regfiles", minChunkSize: []int{0, 64000}, tarInit: func(t *testing.T, dgstMap map[string]digest.Digest) (blob []tarEntry) { return tarOf( regDigest(t, "baz.txt", "bazbazbazbazbazbazbaz", dgstMap), regDigest(t, "foo.txt", "a", dgstMap), regDigest(t, "bar/foo2.txt", "b", dgstMap), regDigest(t, "foo3.txt", "c", dgstMap), symlink("barlink", "test/bar.txt"), dir("test/"), regDigest(t, "test/bar.txt", "testbartestbar", dgstMap), dir("test2/"), link("test2/bazlink", "baz.txt"), ) }, checks: []check{ checkStargzTOC, checkVerifyTOC, checkVerifyInvalidStargzFail(buildTar(t, tarOf( file("baz.txt", "bazbazbazbazbazbazbaz"), file("foo.txt", "a"), file("bar/foo2.txt", "b"), file("foo3.txt", "c"), symlink("barlink", "test/bar.txt"), dir("test/"), file("test/bar.txt", "testbartestbar"), dir("test2/"), link("test2/bazlink", "foo.txt"), // modified ), allowedPrefix[0])), checkVerifyInvalidTOCEntryFail("test/bar.txt"), checkVerifyBrokenContentFail("test/bar.txt"), }, }, } for _, tt := range tests { if len(tt.minChunkSize) == 0 { tt.minChunkSize = []int{0} } for _, srcCompression := range srcCompressions { srcCompression := srcCompression for _, newCL := range controllers { newCL := newCL for _, prefix := range allowedPrefix { prefix := prefix for _, srcTarFormat := range []tar.Format{tar.FormatUSTAR, tar.FormatPAX, tar.FormatGNU} { srcTarFormat := srcTarFormat for _, minChunkSize := range tt.minChunkSize { minChunkSize := minChunkSize t.Run(tt.name+"-"+fmt.Sprintf("compression=%v,prefix=%q,format=%s,minChunkSize=%d", newCL(), prefix, srcTarFormat, minChunkSize), func(t *testing.T) { // Get original tar file and chunk digests dgstMap := make(map[string]digest.Digest) tarBlob := buildTar(t, tt.tarInit(t, dgstMap), prefix, srcTarFormat) cl := newCL() rc, err := Build(compressBlob(t, tarBlob, srcCompression), WithChunkSize(chunkSize), WithCompression(cl)) if err != nil { t.Fatalf("failed to convert stargz: %v", err) } tocDigest := rc.TOCDigest() defer rc.Close() buf := new(bytes.Buffer) if _, err := io.Copy(buf, rc); err != nil { t.Fatalf("failed to copy built stargz blob: %v", err) } newStargz := buf.Bytes() // NoPrefetchLandmark is added during `Bulid`, which is expected behaviour. dgstMap[chunkID(NoPrefetchLandmark, 0, int64(len([]byte{landmarkContents})))] = digest.FromBytes([]byte{landmarkContents}) for _, check := range tt.checks { check(t, newStargz, tocDigest, dgstMap, cl, newCL) } }) } } } } } } } // checkStargzTOC checks the TOC JSON of the passed stargz has the expected // digest and contains valid chunks. It walks all entries in the stargz and // checks all chunk digests stored to the TOC JSON match the actual contents. func checkStargzTOC(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) { sgz, err := Open( io.NewSectionReader(bytes.NewReader(sgzData), 0, int64(len(sgzData))), WithDecompressors(controller), ) if err != nil { t.Errorf("failed to parse converted stargz: %v", err) return } digestMapTOC, err := listDigests(io.NewSectionReader( bytes.NewReader(sgzData), 0, int64(len(sgzData))), controller, ) if err != nil { t.Fatalf("failed to list digest: %v", err) } found := make(map[string]bool) for id := range dgstMap { found[id] = false } zr, err := controller.Reader(bytes.NewReader(sgzData)) if err != nil { t.Fatalf("failed to decompress converted stargz: %v", err) } defer zr.Close() tr := tar.NewReader(zr) for { h, err := tr.Next() if err != nil { if err != io.EOF { t.Errorf("failed to read tar entry: %v", err) return } break } if h.Name == TOCTarName { // Check the digest of TOC JSON based on the actual contents // It's sure that TOC JSON exists in this archive because // Open succeeded. dgstr := digest.Canonical.Digester() if _, err := io.Copy(dgstr.Hash(), tr); err != nil { t.Fatalf("failed to calculate digest of TOC JSON: %v", err) } if dgstr.Digest() != tocDigest { t.Errorf("invalid TOC JSON %q; want %q", tocDigest, dgstr.Digest()) } continue } if _, ok := sgz.Lookup(h.Name); !ok { t.Errorf("lost stargz entry %q in the converted TOC", h.Name) return } var n int64 for n < h.Size { ce, ok := sgz.ChunkEntryForOffset(h.Name, n) if !ok { t.Errorf("lost chunk %q(offset=%d) in the converted TOC", h.Name, n) return } // Get the original digest to make sure the file contents are kept unchanged // from the original tar, during the whole conversion steps. id := chunkID(h.Name, n, ce.ChunkSize) want, ok := dgstMap[id] if !ok { t.Errorf("Unexpected chunk %q(offset=%d,size=%d): %v", h.Name, n, ce.ChunkSize, dgstMap) return } found[id] = true // Check the file contents dgstr := digest.Canonical.Digester() if _, err := io.CopyN(dgstr.Hash(), tr, ce.ChunkSize); err != nil { t.Fatalf("failed to calculate digest of %q (offset=%d,size=%d)", h.Name, n, ce.ChunkSize) } if want != dgstr.Digest() { t.Errorf("Invalid contents in converted stargz %q: %q; want %q", h.Name, dgstr.Digest(), want) return } // Check the digest stored in TOC JSON dgstTOC, ok := digestMapTOC[ce.Offset] if !ok { t.Errorf("digest of %q(offset=%d,size=%d,chunkOffset=%d) isn't registered", h.Name, ce.Offset, ce.ChunkSize, ce.ChunkOffset) } if want != dgstTOC { t.Errorf("Invalid digest in TOCEntry %q: %q; want %q", h.Name, dgstTOC, want) return } n += ce.ChunkSize } } for id, ok := range found { if !ok { t.Errorf("required chunk %q not found in the converted stargz: %v", id, found) } } } // checkVerifyTOC checks the verification works for the TOC JSON of the passed // stargz. It walks all entries in the stargz and checks the verifications for // all chunks work. func checkVerifyTOC(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) { sgz, err := Open( io.NewSectionReader(bytes.NewReader(sgzData), 0, int64(len(sgzData))), WithDecompressors(controller), ) if err != nil { t.Errorf("failed to parse converted stargz: %v", err) return } ev, err := sgz.VerifyTOC(tocDigest) if err != nil { t.Errorf("failed to verify stargz: %v", err) return } found := make(map[string]bool) for id := range dgstMap { found[id] = false } zr, err := controller.Reader(bytes.NewReader(sgzData)) if err != nil { t.Fatalf("failed to decompress converted stargz: %v", err) } defer zr.Close() tr := tar.NewReader(zr) for { h, err := tr.Next() if err != nil { if err != io.EOF { t.Errorf("failed to read tar entry: %v", err) return } break } if h.Name == TOCTarName { continue } if _, ok := sgz.Lookup(h.Name); !ok { t.Errorf("lost stargz entry %q in the converted TOC", h.Name) return } var n int64 for n < h.Size { ce, ok := sgz.ChunkEntryForOffset(h.Name, n) if !ok { t.Errorf("lost chunk %q(offset=%d) in the converted TOC", h.Name, n) return } v, err := ev.Verifier(ce) if err != nil { t.Errorf("failed to get verifier for %q(offset=%d)", h.Name, n) } found[chunkID(h.Name, n, ce.ChunkSize)] = true // Check the file contents if _, err := io.CopyN(v, tr, ce.ChunkSize); err != nil { t.Fatalf("failed to get chunk of %q (offset=%d,size=%d)", h.Name, n, ce.ChunkSize) } if !v.Verified() { t.Errorf("Invalid contents in converted stargz %q (should be succeeded)", h.Name) return } n += ce.ChunkSize } } for id, ok := range found { if !ok { t.Errorf("required chunk %q not found in the converted stargz: %v", id, found) } } } // checkVerifyInvalidTOCEntryFail checks if misconfigured TOC JSON can be // detected during the verification and the verification returns an error. func checkVerifyInvalidTOCEntryFail(filename string) check { return func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) { funcs := map[string]rewriteFunc{ "lost digest in a entry": func(t *testing.T, toc *JTOC, sgz *io.SectionReader) { var found bool for _, e := range toc.Entries { if cleanEntryName(e.Name) == filename { if e.Type != "reg" && e.Type != "chunk" { t.Fatalf("entry %q to break must be regfile or chunk", filename) } if e.ChunkDigest == "" { t.Fatalf("entry %q is already invalid", filename) } e.ChunkDigest = "" found = true } } if !found { t.Fatalf("rewrite target not found") } }, "duplicated entry offset": func(t *testing.T, toc *JTOC, sgz *io.SectionReader) { var ( sampleEntry *TOCEntry targetEntry *TOCEntry ) for _, e := range toc.Entries { if e.Type == "reg" || e.Type == "chunk" { if cleanEntryName(e.Name) == filename { targetEntry = e } else { sampleEntry = e } } } if sampleEntry == nil { t.Fatalf("TOC must contain at least one regfile or chunk entry other than the rewrite target") } if targetEntry == nil { t.Fatalf("rewrite target not found") } targetEntry.Offset = sampleEntry.Offset }, } for name, rFunc := range funcs { t.Run(name, func(t *testing.T) { newSgz, newTocDigest := rewriteTOCJSON(t, io.NewSectionReader(bytes.NewReader(sgzData), 0, int64(len(sgzData))), rFunc, controller) buf := new(bytes.Buffer) if _, err := io.Copy(buf, newSgz); err != nil { t.Fatalf("failed to get converted stargz") } isgz := buf.Bytes() sgz, err := Open( io.NewSectionReader(bytes.NewReader(isgz), 0, int64(len(isgz))), WithDecompressors(controller), ) if err != nil { t.Fatalf("failed to parse converted stargz: %v", err) return } _, err = sgz.VerifyTOC(newTocDigest) if err == nil { t.Errorf("must fail for invalid TOC") return } }) } } } // checkVerifyInvalidStargzFail checks if the verification detects that the // given stargz file doesn't match to the expected digest and returns error. func checkVerifyInvalidStargzFail(invalid *io.SectionReader) check { return func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) { cl := newController() rc, err := Build(invalid, WithChunkSize(chunkSize), WithCompression(cl)) if err != nil { t.Fatalf("failed to convert stargz: %v", err) } defer rc.Close() buf := new(bytes.Buffer) if _, err := io.Copy(buf, rc); err != nil { t.Fatalf("failed to copy built stargz blob: %v", err) } mStargz := buf.Bytes() sgz, err := Open( io.NewSectionReader(bytes.NewReader(mStargz), 0, int64(len(mStargz))), WithDecompressors(cl), ) if err != nil { t.Fatalf("failed to parse converted stargz: %v", err) return } _, err = sgz.VerifyTOC(tocDigest) if err == nil { t.Errorf("must fail for invalid TOC") return } } } // checkVerifyBrokenContentFail checks if the verifier detects broken contents // that doesn't match to the expected digest and returns error. func checkVerifyBrokenContentFail(filename string) check { return func(t *testing.T, sgzData []byte, tocDigest digest.Digest, dgstMap map[string]digest.Digest, controller TestingController, newController TestingControllerFactory) { // Parse stargz file sgz, err := Open( io.NewSectionReader(bytes.NewReader(sgzData), 0, int64(len(sgzData))), WithDecompressors(controller), ) if err != nil { t.Fatalf("failed to parse converted stargz: %v", err) return } ev, err := sgz.VerifyTOC(tocDigest) if err != nil { t.Fatalf("failed to verify stargz: %v", err) return } // Open the target file sr, err := sgz.OpenFile(filename) if err != nil { t.Fatalf("failed to open file %q", filename) } ce, ok := sgz.ChunkEntryForOffset(filename, 0) if !ok { t.Fatalf("lost chunk %q(offset=%d) in the converted TOC", filename, 0) return } if ce.ChunkSize == 0 { t.Fatalf("file mustn't be empty") return } data := make([]byte, ce.ChunkSize) if _, err := sr.ReadAt(data, ce.ChunkOffset); err != nil { t.Errorf("failed to get data of a chunk of %q(offset=%q)", filename, ce.ChunkOffset) } // Check the broken chunk (must fail) v, err := ev.Verifier(ce) if err != nil { t.Fatalf("failed to get verifier for %q", filename) } broken := append([]byte{^data[0]}, data[1:]...) if _, err := io.CopyN(v, bytes.NewReader(broken), ce.ChunkSize); err != nil { t.Fatalf("failed to get chunk of %q (offset=%d,size=%d)", filename, ce.ChunkOffset, ce.ChunkSize) } if v.Verified() { t.Errorf("verification must fail for broken file chunk %q(org:%q,broken:%q)", filename, data, broken) } } } func chunkID(name string, offset, size int64) string { return fmt.Sprintf("%s-%d-%d", cleanEntryName(name), offset, size) } type rewriteFunc func(t *testing.T, toc *JTOC, sgz *io.SectionReader) func rewriteTOCJSON(t *testing.T, sgz *io.SectionReader, rewrite rewriteFunc, controller TestingController) (newSgz io.Reader, tocDigest digest.Digest) { decodedJTOC, jtocOffset, err := parseStargz(sgz, controller) if err != nil { t.Fatalf("failed to extract TOC JSON: %v", err) } rewrite(t, decodedJTOC, sgz) tocFooter, tocDigest, err := tocAndFooter(controller, decodedJTOC, jtocOffset) if err != nil { t.Fatalf("failed to create toc and footer: %v", err) } // Reconstruct stargz file with the modified TOC JSON if _, err := sgz.Seek(0, io.SeekStart); err != nil { t.Fatalf("failed to reset the seek position of stargz: %v", err) } return io.MultiReader( io.LimitReader(sgz, jtocOffset), // Original stargz (before TOC JSON) tocFooter, // Rewritten TOC and footer ), tocDigest } func listDigests(sgz *io.SectionReader, controller TestingController) (map[int64]digest.Digest, error) { decodedJTOC, _, err := parseStargz(sgz, controller) if err != nil { return nil, err } digestMap := make(map[int64]digest.Digest) for _, e := range decodedJTOC.Entries { if e.Type == "reg" || e.Type == "chunk" { if e.Type == "reg" && e.Size == 0 { continue // ignores empty file } if e.ChunkDigest == "" { return nil, fmt.Errorf("ChunkDigest of %q(off=%d) not found in TOC JSON", e.Name, e.Offset) } d, err := digest.Parse(e.ChunkDigest) if err != nil { return nil, err } digestMap[e.Offset] = d } } return digestMap, nil } func parseStargz(sgz *io.SectionReader, controller TestingController) (decodedJTOC *JTOC, jtocOffset int64, err error) { fSize := controller.FooterSize() footer := make([]byte, fSize) if _, err := sgz.ReadAt(footer, sgz.Size()-fSize); err != nil { return nil, 0, fmt.Errorf("error reading footer: %w", err) } _, tocOffset, _, err := controller.ParseFooter(footer[positive(int64(len(footer))-fSize):]) if err != nil { return nil, 0, fmt.Errorf("failed to parse footer: %w", err) } // Decode the TOC JSON var tocReader io.Reader if tocOffset >= 0 { tocReader = io.NewSectionReader(sgz, tocOffset, sgz.Size()-tocOffset-fSize) } decodedJTOC, _, err = controller.ParseTOC(tocReader) if err != nil { return nil, 0, fmt.Errorf("failed to parse TOC: %w", err) } return decodedJTOC, tocOffset, nil } func testWriteAndOpen(t *testing.T, controllers ...TestingControllerFactory) { const content = "Some contents" invalidUtf8 := "\xff\xfe\xfd" xAttrFile := xAttr{"foo": "bar", "invalid-utf8": invalidUtf8} sampleOwner := owner{uid: 50, gid: 100} data64KB := randomContents(64000) tests := []struct { name string chunkSize int minChunkSize int in []tarEntry want []stargzCheck wantNumGz int // expected number of streams wantNumGzLossLess int // expected number of streams (> 0) in lossless mode if it's different from wantNumGz wantFailOnLossLess bool wantTOCVersion int // default = 1 }{ { name: "empty", in: tarOf(), wantNumGz: 2, // (empty tar) + TOC + footer want: checks( numTOCEntries(0), ), }, { name: "1dir_1empty_file", in: tarOf( dir("foo/"), file("foo/bar.txt", ""), ), wantNumGz: 3, // dir, TOC, footer want: checks( numTOCEntries(2), hasDir("foo/"), hasFileLen("foo/bar.txt", 0), entryHasChildren("foo", "bar.txt"), hasFileDigest("foo/bar.txt", digestFor("")), ), }, { name: "1dir_1file", in: tarOf( dir("foo/"), file("foo/bar.txt", content, xAttrFile), ), wantNumGz: 4, // var dir, foo.txt alone, TOC, footer want: checks( numTOCEntries(2), hasDir("foo/"), hasFileLen("foo/bar.txt", len(content)), hasFileDigest("foo/bar.txt", digestFor(content)), hasFileContentsRange("foo/bar.txt", 0, content), hasFileContentsRange("foo/bar.txt", 1, content[1:]), entryHasChildren("", "foo"), entryHasChildren("foo", "bar.txt"), hasFileXattrs("foo/bar.txt", "foo", "bar"), hasFileXattrs("foo/bar.txt", "invalid-utf8", invalidUtf8), ), }, { name: "2meta_2file", in: tarOf( dir("bar/", sampleOwner), dir("foo/", sampleOwner), file("foo/bar.txt", content, sampleOwner), ), wantNumGz: 4, // both dirs, foo.txt alone, TOC, footer want: checks( numTOCEntries(3), hasDir("bar/"), hasDir("foo/"), hasFileLen("foo/bar.txt", len(content)), entryHasChildren("", "bar", "foo"), entryHasChildren("foo", "bar.txt"), hasChunkEntries("foo/bar.txt", 1), hasEntryOwner("bar/", sampleOwner), hasEntryOwner("foo/", sampleOwner), hasEntryOwner("foo/bar.txt", sampleOwner), ), }, { name: "3dir", in: tarOf( dir("bar/"), dir("foo/"), dir("foo/bar/"), ), wantNumGz: 3, // 3 dirs, TOC, footer want: checks( hasDirLinkCount("bar/", 2), hasDirLinkCount("foo/", 3), hasDirLinkCount("foo/bar/", 2), ), }, { name: "symlink", in: tarOf( dir("foo/"), symlink("foo/bar", "../../x"), ), wantNumGz: 3, // metas + TOC + footer want: checks( numTOCEntries(2), hasSymlink("foo/bar", "../../x"), entryHasChildren("", "foo"), entryHasChildren("foo", "bar"), ), }, { name: "chunked_file", chunkSize: 4, in: tarOf( dir("foo/"), file("foo/big.txt", "This "+"is s"+"uch "+"a bi"+"g fi"+"le"), ), wantNumGz: 9, // dir + big.txt(6 chunks) + TOC + footer want: checks( numTOCEntries(7), // 1 for foo dir, 6 for the foo/big.txt file hasDir("foo/"), hasFileLen("foo/big.txt", len("This is such a big file")), hasFileDigest("foo/big.txt", digestFor("This is such a big file")), hasFileContentsRange("foo/big.txt", 0, "This is such a big file"), hasFileContentsRange("foo/big.txt", 1, "his is such a big file"), hasFileContentsRange("foo/big.txt", 2, "is is such a big file"), hasFileContentsRange("foo/big.txt", 3, "s is such a big file"), hasFileContentsRange("foo/big.txt", 4, " is such a big file"), hasFileContentsRange("foo/big.txt", 5, "is such a big file"), hasFileContentsRange("foo/big.txt", 6, "s such a big file"), hasFileContentsRange("foo/big.txt", 7, " such a big file"), hasFileContentsRange("foo/big.txt", 8, "such a big file"), hasFileContentsRange("foo/big.txt", 9, "uch a big file"), hasFileContentsRange("foo/big.txt", 10, "ch a big file"), hasFileContentsRange("foo/big.txt", 11, "h a big file"), hasFileContentsRange("foo/big.txt", 12, " a big file"), hasFileContentsRange("foo/big.txt", len("This is such a big file")-1, ""), hasChunkEntries("foo/big.txt", 6), ), }, { name: "recursive", in: tarOf( dir("/", sampleOwner), dir("bar/", sampleOwner), dir("foo/", sampleOwner), file("foo/bar.txt", content, sampleOwner), ), wantNumGz: 4, // dirs, bar.txt alone, TOC, footer want: checks( maxDepth(2), // 0: root directory, 1: "foo/", 2: "bar.txt" ), }, { name: "block_char_fifo", in: tarOf( tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { return w.WriteHeader(&tar.Header{ Name: prefix + "b", Typeflag: tar.TypeBlock, Devmajor: 123, Devminor: 456, Format: format, }) }), tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { return w.WriteHeader(&tar.Header{ Name: prefix + "c", Typeflag: tar.TypeChar, Devmajor: 111, Devminor: 222, Format: format, }) }), tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { return w.WriteHeader(&tar.Header{ Name: prefix + "f", Typeflag: tar.TypeFifo, Format: format, }) }), ), wantNumGz: 3, want: checks( lookupMatch("b", &TOCEntry{Name: "b", Type: "block", DevMajor: 123, DevMinor: 456, NumLink: 1}), lookupMatch("c", &TOCEntry{Name: "c", Type: "char", DevMajor: 111, DevMinor: 222, NumLink: 1}), lookupMatch("f", &TOCEntry{Name: "f", Type: "fifo", NumLink: 1}), ), }, { name: "modes", in: tarOf( dir("foo1/", 0755|os.ModeDir|os.ModeSetgid), file("foo1/bar1", content, 0700|os.ModeSetuid), file("foo1/bar2", content, 0755|os.ModeSetgid), dir("foo2/", 0755|os.ModeDir|os.ModeSticky), file("foo2/bar3", content, 0755|os.ModeSticky), dir("foo3/", 0755|os.ModeDir), file("foo3/bar4", content, os.FileMode(0700)), file("foo3/bar5", content, os.FileMode(0755)), ), wantNumGz: 8, // dir, bar1 alone, bar2 alone + dir, bar3 alone + dir, bar4 alone, bar5 alone, TOC, footer want: checks( hasMode("foo1/", 0755|os.ModeDir|os.ModeSetgid), hasMode("foo1/bar1", 0700|os.ModeSetuid), hasMode("foo1/bar2", 0755|os.ModeSetgid), hasMode("foo2/", 0755|os.ModeDir|os.ModeSticky), hasMode("foo2/bar3", 0755|os.ModeSticky), hasMode("foo3/", 0755|os.ModeDir), hasMode("foo3/bar4", os.FileMode(0700)), hasMode("foo3/bar5", os.FileMode(0755)), ), }, { name: "lossy", in: tarOf( dir("bar/", sampleOwner), dir("foo/", sampleOwner), file("foo/bar.txt", content, sampleOwner), file(TOCTarName, "dummy"), // ignored by the writer. (lossless write returns error) ), wantNumGz: 4, // both dirs, foo.txt alone, TOC, footer want: checks( numTOCEntries(3), hasDir("bar/"), hasDir("foo/"), hasFileLen("foo/bar.txt", len(content)), entryHasChildren("", "bar", "foo"), entryHasChildren("foo", "bar.txt"), hasChunkEntries("foo/bar.txt", 1), hasEntryOwner("bar/", sampleOwner), hasEntryOwner("foo/", sampleOwner), hasEntryOwner("foo/bar.txt", sampleOwner), ), wantFailOnLossLess: true, }, { name: "hardlink should be replaced to the destination entry", in: tarOf( dir("foo/"), file("foo/foo1", "test"), link("foolink", "foo/foo1"), ), wantNumGz: 4, // dir, foo1 + link, TOC, footer want: checks( mustSameEntry("foo/foo1", "foolink"), ), }, { name: "several_files_in_chunk", minChunkSize: 8000, in: tarOf( dir("foo/"), file("foo/foo1", data64KB), file("foo2", "bb"), file("foo22", "ccc"), dir("bar/"), file("bar/bar.txt", "aaa"), file("foo3", data64KB), ), // NOTE: we assume that the compressed "data64KB" is still larger than 8KB wantNumGz: 4, // dir+foo1, foo2+foo22+dir+bar.txt+foo3, TOC, footer want: checks( numTOCEntries(7), // dir, foo1, foo2, foo22, dir, bar.txt, foo3 hasDir("foo/"), hasDir("bar/"), hasFileLen("foo/foo1", len(data64KB)), hasFileLen("foo2", len("bb")), hasFileLen("foo22", len("ccc")), hasFileLen("bar/bar.txt", len("aaa")), hasFileLen("foo3", len(data64KB)), hasFileDigest("foo/foo1", digestFor(data64KB)), hasFileDigest("foo2", digestFor("bb")), hasFileDigest("foo22", digestFor("ccc")), hasFileDigest("bar/bar.txt", digestFor("aaa")), hasFileDigest("foo3", digestFor(data64KB)), hasFileContentsWithPreRead("foo22", 0, "ccc", chunkInfo{"foo2", "bb"}, chunkInfo{"bar/bar.txt", "aaa"}, chunkInfo{"foo3", data64KB}), hasFileContentsRange("foo/foo1", 0, data64KB), hasFileContentsRange("foo2", 0, "bb"), hasFileContentsRange("foo2", 1, "b"), hasFileContentsRange("foo22", 0, "ccc"), hasFileContentsRange("foo22", 1, "cc"), hasFileContentsRange("foo22", 2, "c"), hasFileContentsRange("bar/bar.txt", 0, "aaa"), hasFileContentsRange("bar/bar.txt", 1, "aa"), hasFileContentsRange("bar/bar.txt", 2, "a"), hasFileContentsRange("foo3", 0, data64KB), hasFileContentsRange("foo3", 1, data64KB[1:]), hasFileContentsRange("foo3", 2, data64KB[2:]), hasFileContentsRange("foo3", len(data64KB)/2, data64KB[len(data64KB)/2:]), hasFileContentsRange("foo3", len(data64KB)-1, data64KB[len(data64KB)-1:]), ), }, { name: "several_files_in_chunk_chunked", minChunkSize: 8000, chunkSize: 32000, in: tarOf( dir("foo/"), file("foo/foo1", data64KB), file("foo2", "bb"), dir("bar/"), file("foo3", data64KB), ), // NOTE: we assume that the compressed chunk of "data64KB" is still larger than 8KB wantNumGz: 6, // dir+foo1(1), foo1(2), foo2+dir+foo3(1), foo3(2), TOC, footer want: checks( numTOCEntries(7), // dir, foo1(2 chunks), foo2, dir, foo3(2 chunks) hasDir("foo/"), hasDir("bar/"), hasFileLen("foo/foo1", len(data64KB)), hasFileLen("foo2", len("bb")), hasFileLen("foo3", len(data64KB)), hasFileDigest("foo/foo1", digestFor(data64KB)), hasFileDigest("foo2", digestFor("bb")), hasFileDigest("foo3", digestFor(data64KB)), hasFileContentsWithPreRead("foo2", 0, "bb", chunkInfo{"foo3", data64KB[:32000]}), hasFileContentsRange("foo/foo1", 0, data64KB), hasFileContentsRange("foo/foo1", 1, data64KB[1:]), hasFileContentsRange("foo/foo1", 2, data64KB[2:]), hasFileContentsRange("foo/foo1", len(data64KB)/2, data64KB[len(data64KB)/2:]), hasFileContentsRange("foo/foo1", len(data64KB)-1, data64KB[len(data64KB)-1:]), hasFileContentsRange("foo2", 0, "bb"), hasFileContentsRange("foo2", 1, "b"), hasFileContentsRange("foo3", 0, data64KB), hasFileContentsRange("foo3", 1, data64KB[1:]), hasFileContentsRange("foo3", 2, data64KB[2:]), hasFileContentsRange("foo3", len(data64KB)/2, data64KB[len(data64KB)/2:]), hasFileContentsRange("foo3", len(data64KB)-1, data64KB[len(data64KB)-1:]), ), }, } for _, tt := range tests { for _, newCL := range controllers { newCL := newCL for _, prefix := range allowedPrefix { prefix := prefix for _, srcTarFormat := range []tar.Format{tar.FormatUSTAR, tar.FormatPAX, tar.FormatGNU} { srcTarFormat := srcTarFormat for _, lossless := range []bool{true, false} { t.Run(tt.name+"-"+fmt.Sprintf("compression=%v,prefix=%q,lossless=%v,format=%s", newCL(), prefix, lossless, srcTarFormat), func(t *testing.T) { var tr io.Reader = buildTar(t, tt.in, prefix, srcTarFormat) origTarDgstr := digest.Canonical.Digester() tr = io.TeeReader(tr, origTarDgstr.Hash()) var stargzBuf bytes.Buffer cl1 := newCL() w := NewWriterWithCompressor(&stargzBuf, cl1) w.ChunkSize = tt.chunkSize w.MinChunkSize = tt.minChunkSize if lossless { err := w.AppendTarLossLess(tr) if tt.wantFailOnLossLess { if err != nil { return // expected to fail } t.Fatalf("Append wanted to fail on lossless") } if err != nil { t.Fatalf("Append(lossless): %v", err) } } else { if err := w.AppendTar(tr); err != nil { t.Fatalf("Append: %v", err) } } if _, err := w.Close(); err != nil { t.Fatalf("Writer.Close: %v", err) } b := stargzBuf.Bytes() if lossless { // Check if the result blob reserves original tar metadata rc, err := Unpack(io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b))), cl1) if err != nil { t.Errorf("failed to decompress blob: %v", err) return } defer rc.Close() resultDgstr := digest.Canonical.Digester() if _, err := io.Copy(resultDgstr.Hash(), rc); err != nil { t.Errorf("failed to read result decompressed blob: %v", err) return } if resultDgstr.Digest() != origTarDgstr.Digest() { t.Errorf("lossy compression occurred: digest=%v; want %v", resultDgstr.Digest(), origTarDgstr.Digest()) return } } diffID := w.DiffID() wantDiffID := cl1.DiffIDOf(t, b) if diffID != wantDiffID { t.Errorf("DiffID = %q; want %q", diffID, wantDiffID) } telemetry, checkCalled := newCalledTelemetry() sr := io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b))) r, err := Open( sr, WithDecompressors(cl1), WithTelemetry(telemetry), ) if err != nil { t.Fatalf("stargz.Open: %v", err) } wantTOCVersion := 1 if tt.wantTOCVersion > 0 { wantTOCVersion = tt.wantTOCVersion } if r.toc.Version != wantTOCVersion { t.Fatalf("invalid TOC Version %d; wanted %d", r.toc.Version, wantTOCVersion) } footerSize := cl1.FooterSize() footerOffset := sr.Size() - footerSize footer := make([]byte, footerSize) if _, err := sr.ReadAt(footer, footerOffset); err != nil { t.Errorf("failed to read footer: %v", err) } _, tocOffset, _, err := cl1.ParseFooter(footer) if err != nil { t.Errorf("failed to parse footer: %v", err) } if err := checkCalled(tocOffset >= 0); err != nil { t.Errorf("telemetry failure: %v", err) } wantNumGz := tt.wantNumGz if lossless && tt.wantNumGzLossLess > 0 { wantNumGz = tt.wantNumGzLossLess } streamOffsets := []int64{0} prevOffset := int64(-1) streams := 0 for _, e := range r.toc.Entries { if e.Offset > prevOffset { streamOffsets = append(streamOffsets, e.Offset) prevOffset = e.Offset streams++ } } streams++ // TOC if tocOffset >= 0 { // toc is in the blob streamOffsets = append(streamOffsets, tocOffset) } streams++ // footer streamOffsets = append(streamOffsets, footerOffset) if streams != wantNumGz { t.Errorf("number of streams in TOC = %d; want %d", streams, wantNumGz) } t.Logf("testing streams: %+v", streamOffsets) cl1.TestStreams(t, b, streamOffsets) for _, want := range tt.want { want.check(t, r) } }) } } } } } } type chunkInfo struct { name string data string } func newCalledTelemetry() (telemetry *Telemetry, check func(needsGetTOC bool) error) { var getFooterLatencyCalled bool var getTocLatencyCalled bool var deserializeTocLatencyCalled bool return &Telemetry{ func(time.Time) { getFooterLatencyCalled = true }, func(time.Time) { getTocLatencyCalled = true }, func(time.Time) { deserializeTocLatencyCalled = true }, }, func(needsGetTOC bool) error { var allErr []error if !getFooterLatencyCalled { allErr = append(allErr, fmt.Errorf("metrics GetFooterLatency isn't called")) } if needsGetTOC { if !getTocLatencyCalled { allErr = append(allErr, fmt.Errorf("metrics GetTocLatency isn't called")) } } if !deserializeTocLatencyCalled { allErr = append(allErr, fmt.Errorf("metrics DeserializeTocLatency isn't called")) } return errorutil.Aggregate(allErr) } } func digestFor(content string) string { sum := sha256.Sum256([]byte(content)) return fmt.Sprintf("sha256:%x", sum) } type numTOCEntries int func (n numTOCEntries) check(t *testing.T, r *Reader) { if r.toc == nil { t.Fatal("nil TOC") } if got, want := len(r.toc.Entries), int(n); got != want { t.Errorf("got %d TOC entries; want %d", got, want) } t.Logf("got TOC entries:") for i, ent := range r.toc.Entries { entj, _ := json.Marshal(ent) t.Logf(" [%d]: %s\n", i, entj) } if t.Failed() { t.FailNow() } } func checks(s ...stargzCheck) []stargzCheck { return s } type stargzCheck interface { check(t *testing.T, r *Reader) } type stargzCheckFn func(*testing.T, *Reader) func (f stargzCheckFn) check(t *testing.T, r *Reader) { f(t, r) } func maxDepth(max int) stargzCheck { return stargzCheckFn(func(t *testing.T, r *Reader) { e, ok := r.Lookup("") if !ok { t.Fatal("root directory not found") } d, err := getMaxDepth(t, e, 0, 10*max) if err != nil { t.Errorf("failed to get max depth (wanted %d): %v", max, err) return } if d != max { t.Errorf("invalid depth %d; want %d", d, max) return } }) } func getMaxDepth(t *testing.T, e *TOCEntry, current, limit int) (max int, rErr error) { if current > limit { return -1, fmt.Errorf("walkMaxDepth: exceeds limit: current:%d > limit:%d", current, limit) } max = current e.ForeachChild(func(baseName string, ent *TOCEntry) bool { t.Logf("%q(basename:%q) is child of %q\n", ent.Name, baseName, e.Name) d, err := getMaxDepth(t, ent, current+1, limit) if err != nil { rErr = err return false } if d > max { max = d } return true }) return } func hasFileLen(file string, wantLen int) stargzCheck { return stargzCheckFn(func(t *testing.T, r *Reader) { for _, ent := range r.toc.Entries { if ent.Name == file { if ent.Type != "reg" { t.Errorf("file type of %q is %q; want \"reg\"", file, ent.Type) } else if ent.Size != int64(wantLen) { t.Errorf("file size of %q = %d; want %d", file, ent.Size, wantLen) } return } } t.Errorf("file %q not found", file) }) } func hasFileXattrs(file, name, value string) stargzCheck { return stargzCheckFn(func(t *testing.T, r *Reader) { for _, ent := range r.toc.Entries { if ent.Name == file { if ent.Type != "reg" { t.Errorf("file type of %q is %q; want \"reg\"", file, ent.Type) } if ent.Xattrs == nil { t.Errorf("file %q has no xattrs", file) return } valueFound, found := ent.Xattrs[name] if !found { t.Errorf("file %q has no xattr %q", file, name) return } if string(valueFound) != value { t.Errorf("file %q has xattr %q with value %q instead of %q", file, name, valueFound, value) } return } } t.Errorf("file %q not found", file) }) } func hasFileDigest(file string, digest string) stargzCheck { return stargzCheckFn(func(t *testing.T, r *Reader) { ent, ok := r.Lookup(file) if !ok { t.Fatalf("didn't find TOCEntry for file %q", file) } if ent.Digest != digest { t.Fatalf("Digest(%q) = %q, want %q", file, ent.Digest, digest) } }) } func hasFileContentsWithPreRead(file string, offset int, want string, extra ...chunkInfo) stargzCheck { return stargzCheckFn(func(t *testing.T, r *Reader) { extraMap := make(map[string]chunkInfo) for _, e := range extra { extraMap[e.name] = e } var extraNames []string for n := range extraMap { extraNames = append(extraNames, n) } f, err := r.OpenFileWithPreReader(file, func(e *TOCEntry, cr io.Reader) error { t.Logf("On %q: got preread of %q", file, e.Name) ex, ok := extraMap[e.Name] if !ok { t.Fatalf("fail on %q: unexpected entry %q: %+v, %+v", file, e.Name, e, extraNames) } got, err := io.ReadAll(cr) if err != nil { t.Fatalf("fail on %q: failed to read %q: %v", file, e.Name, err) } if ex.data != string(got) { t.Fatalf("fail on %q: unexpected contents of %q: len=%d; want=%d", file, e.Name, len(got), len(ex.data)) } delete(extraMap, e.Name) return nil }) if err != nil { t.Fatal(err) } got := make([]byte, len(want)) n, err := f.ReadAt(got, int64(offset)) if err != nil { t.Fatalf("ReadAt(len %d, offset %d, size %d) = %v, %v", len(got), offset, f.Size(), n, err) } if string(got) != want { t.Fatalf("ReadAt(len %d, offset %d) = %q, want %q", len(got), offset, viewContent(got), viewContent([]byte(want))) } if len(extraMap) != 0 { var exNames []string for _, ex := range extraMap { exNames = append(exNames, ex.name) } t.Fatalf("fail on %q: some entries aren't read: %+v", file, exNames) } }) } func hasFileContentsRange(file string, offset int, want string) stargzCheck { return stargzCheckFn(func(t *testing.T, r *Reader) { f, err := r.OpenFile(file) if err != nil { t.Fatal(err) } got := make([]byte, len(want)) n, err := f.ReadAt(got, int64(offset)) if err != nil { t.Fatalf("ReadAt(len %d, offset %d) = %v, %v", len(got), offset, n, err) } if string(got) != want { t.Fatalf("ReadAt(len %d, offset %d) = %q, want %q", len(got), offset, viewContent(got), viewContent([]byte(want))) } }) } func hasChunkEntries(file string, wantChunks int) stargzCheck { return stargzCheckFn(func(t *testing.T, r *Reader) { ent, ok := r.Lookup(file) if !ok { t.Fatalf("no file for %q", file) } if ent.Type != "reg" { t.Fatalf("file %q has unexpected type %q; want reg", file, ent.Type) } chunks := r.getChunks(ent) if len(chunks) != wantChunks { t.Errorf("len(r.getChunks(%q)) = %d; want %d", file, len(chunks), wantChunks) return } f := chunks[0] var gotChunks []*TOCEntry var last *TOCEntry for off := int64(0); off < f.Size; off++ { e, ok := r.ChunkEntryForOffset(file, off) if !ok { t.Errorf("no ChunkEntryForOffset at %d", off) return } if last != e { gotChunks = append(gotChunks, e) last = e } } if !reflect.DeepEqual(chunks, gotChunks) { t.Errorf("gotChunks=%d, want=%d; contents mismatch", len(gotChunks), wantChunks) } // And verify the NextOffset for i := 0; i < len(gotChunks)-1; i++ { ci := gotChunks[i] cnext := gotChunks[i+1] if ci.NextOffset() != cnext.Offset { t.Errorf("chunk %d NextOffset %d != next chunk's Offset of %d", i, ci.NextOffset(), cnext.Offset) } } }) } func entryHasChildren(dir string, want ...string) stargzCheck { return stargzCheckFn(func(t *testing.T, r *Reader) { want := append([]string(nil), want...) var got []string ent, ok := r.Lookup(dir) if !ok { t.Fatalf("didn't find TOCEntry for dir node %q", dir) } for baseName := range ent.children { got = append(got, baseName) } sort.Strings(got) sort.Strings(want) if !reflect.DeepEqual(got, want) { t.Errorf("children of %q = %q; want %q", dir, got, want) } }) } func hasDir(file string) stargzCheck { return stargzCheckFn(func(t *testing.T, r *Reader) { for _, ent := range r.toc.Entries { if ent.Name == cleanEntryName(file) { if ent.Type != "dir" { t.Errorf("file type of %q is %q; want \"dir\"", file, ent.Type) } return } } t.Errorf("directory %q not found", file) }) } func hasDirLinkCount(file string, count int) stargzCheck { return stargzCheckFn(func(t *testing.T, r *Reader) { for _, ent := range r.toc.Entries { if ent.Name == cleanEntryName(file) { if ent.Type != "dir" { t.Errorf("file type of %q is %q; want \"dir\"", file, ent.Type) return } if ent.NumLink != count { t.Errorf("link count of %q = %d; want %d", file, ent.NumLink, count) } return } } t.Errorf("directory %q not found", file) }) } func hasMode(file string, mode os.FileMode) stargzCheck { return stargzCheckFn(func(t *testing.T, r *Reader) { for _, ent := range r.toc.Entries { if ent.Name == cleanEntryName(file) { if ent.Stat().Mode() != mode { t.Errorf("invalid mode: got %v; want %v", ent.Stat().Mode(), mode) return } return } } t.Errorf("file %q not found", file) }) } func hasSymlink(file, target string) stargzCheck { return stargzCheckFn(func(t *testing.T, r *Reader) { for _, ent := range r.toc.Entries { if ent.Name == file { if ent.Type != "symlink" { t.Errorf("file type of %q is %q; want \"symlink\"", file, ent.Type) } else if ent.LinkName != target { t.Errorf("link target of symlink %q is %q; want %q", file, ent.LinkName, target) } return } } t.Errorf("symlink %q not found", file) }) } func lookupMatch(name string, want *TOCEntry) stargzCheck { return stargzCheckFn(func(t *testing.T, r *Reader) { e, ok := r.Lookup(name) if !ok { t.Fatalf("failed to Lookup entry %q", name) } if !reflect.DeepEqual(e, want) { t.Errorf("entry %q mismatch.\n got: %+v\nwant: %+v\n", name, e, want) } }) } func hasEntryOwner(entry string, owner owner) stargzCheck { return stargzCheckFn(func(t *testing.T, r *Reader) { ent, ok := r.Lookup(strings.TrimSuffix(entry, "/")) if !ok { t.Errorf("entry %q not found", entry) return } if ent.UID != owner.uid || ent.GID != owner.gid { t.Errorf("entry %q has invalid owner (uid:%d, gid:%d) instead of (uid:%d, gid:%d)", entry, ent.UID, ent.GID, owner.uid, owner.gid) return } }) } func mustSameEntry(files ...string) stargzCheck { return stargzCheckFn(func(t *testing.T, r *Reader) { var first *TOCEntry for _, f := range files { if first == nil { var ok bool first, ok = r.Lookup(f) if !ok { t.Errorf("unknown first file on Lookup: %q", f) return } } // Test Lookup e, ok := r.Lookup(f) if !ok { t.Errorf("unknown file on Lookup: %q", f) return } if e != first { t.Errorf("Lookup: %+v(%p) != %+v(%p)", e, e, first, first) return } // Test LookupChild pe, ok := r.Lookup(filepath.Dir(filepath.Clean(f))) if !ok { t.Errorf("failed to get parent of %q", f) return } e, ok = pe.LookupChild(filepath.Base(filepath.Clean(f))) if !ok { t.Errorf("failed to get %q as the child of %+v", f, pe) return } if e != first { t.Errorf("LookupChild: %+v(%p) != %+v(%p)", e, e, first, first) return } // Test ForeachChild pe.ForeachChild(func(baseName string, e *TOCEntry) bool { if baseName == filepath.Base(filepath.Clean(f)) { if e != first { t.Errorf("ForeachChild: %+v(%p) != %+v(%p)", e, e, first, first) return false } } return true }) } }) } func viewContent(c []byte) string { if len(c) < 100 { return string(c) } return string(c[:50]) + "...(omit)..." + string(c[50:100]) } func tarOf(s ...tarEntry) []tarEntry { return s } type tarEntry interface { appendTar(tw *tar.Writer, prefix string, format tar.Format) error } type tarEntryFunc func(*tar.Writer, string, tar.Format) error func (f tarEntryFunc) appendTar(tw *tar.Writer, prefix string, format tar.Format) error { return f(tw, prefix, format) } func buildTar(t *testing.T, ents []tarEntry, prefix string, opts ...interface{}) *io.SectionReader { format := tar.FormatUnknown for _, opt := range opts { switch v := opt.(type) { case tar.Format: format = v default: panic(fmt.Errorf("unsupported opt for buildTar: %v", opt)) } } buf := new(bytes.Buffer) tw := tar.NewWriter(buf) for _, ent := range ents { if err := ent.appendTar(tw, prefix, format); err != nil { t.Fatalf("building input tar: %v", err) } } if err := tw.Close(); err != nil { t.Errorf("closing write of input tar: %v", err) } data := append(buf.Bytes(), make([]byte, 100)...) // append empty bytes at the tail to see lossless works return io.NewSectionReader(bytes.NewReader(data), 0, int64(len(data))) } func dir(name string, opts ...interface{}) tarEntry { return tarEntryFunc(func(tw *tar.Writer, prefix string, format tar.Format) error { var o owner mode := os.FileMode(0755) for _, opt := range opts { switch v := opt.(type) { case owner: o = v case os.FileMode: mode = v default: return errors.New("unsupported opt") } } if !strings.HasSuffix(name, "/") { panic(fmt.Sprintf("missing trailing slash in dir %q ", name)) } tm, err := fileModeToTarMode(mode) if err != nil { return err } return tw.WriteHeader(&tar.Header{ Typeflag: tar.TypeDir, Name: prefix + name, Mode: tm, Uid: o.uid, Gid: o.gid, Format: format, }) }) } // xAttr are extended attributes to set on test files created with the file func. type xAttr map[string]string // owner is owner ot set on test files and directories with the file and dir functions. type owner struct { uid int gid int } func file(name, contents string, opts ...interface{}) tarEntry { return tarEntryFunc(func(tw *tar.Writer, prefix string, format tar.Format) error { var xattrs xAttr var o owner mode := os.FileMode(0644) for _, opt := range opts { switch v := opt.(type) { case xAttr: xattrs = v case owner: o = v case os.FileMode: mode = v default: return errors.New("unsupported opt") } } if strings.HasSuffix(name, "/") { return fmt.Errorf("bogus trailing slash in file %q", name) } tm, err := fileModeToTarMode(mode) if err != nil { return err } if len(xattrs) > 0 { format = tar.FormatPAX // only PAX supports xattrs } if err := tw.WriteHeader(&tar.Header{ Typeflag: tar.TypeReg, Name: prefix + name, Mode: tm, Xattrs: xattrs, Size: int64(len(contents)), Uid: o.uid, Gid: o.gid, Format: format, }); err != nil { return err } _, err = io.WriteString(tw, contents) return err }) } func symlink(name, target string) tarEntry { return tarEntryFunc(func(tw *tar.Writer, prefix string, format tar.Format) error { return tw.WriteHeader(&tar.Header{ Typeflag: tar.TypeSymlink, Name: prefix + name, Linkname: target, Mode: 0644, Format: format, }) }) } func link(name string, linkname string) tarEntry { now := time.Now() return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { return w.WriteHeader(&tar.Header{ Typeflag: tar.TypeLink, Name: prefix + name, Linkname: linkname, ModTime: now, Format: format, }) }) } func chardev(name string, major, minor int64) tarEntry { now := time.Now() return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { return w.WriteHeader(&tar.Header{ Typeflag: tar.TypeChar, Name: prefix + name, Devmajor: major, Devminor: minor, ModTime: now, Format: format, }) }) } func blockdev(name string, major, minor int64) tarEntry { now := time.Now() return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { return w.WriteHeader(&tar.Header{ Typeflag: tar.TypeBlock, Name: prefix + name, Devmajor: major, Devminor: minor, ModTime: now, Format: format, }) }) } func fifo(name string) tarEntry { now := time.Now() return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { return w.WriteHeader(&tar.Header{ Typeflag: tar.TypeFifo, Name: prefix + name, ModTime: now, Format: format, }) }) } func prefetchLandmark() tarEntry { return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { if err := w.WriteHeader(&tar.Header{ Name: PrefetchLandmark, Typeflag: tar.TypeReg, Size: int64(len([]byte{landmarkContents})), Format: format, }); err != nil { return err } contents := []byte{landmarkContents} if _, err := io.CopyN(w, bytes.NewReader(contents), int64(len(contents))); err != nil { return err } return nil }) } func noPrefetchLandmark() tarEntry { return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { if err := w.WriteHeader(&tar.Header{ Name: NoPrefetchLandmark, Typeflag: tar.TypeReg, Size: int64(len([]byte{landmarkContents})), Format: format, }); err != nil { return err } contents := []byte{landmarkContents} if _, err := io.CopyN(w, bytes.NewReader(contents), int64(len(contents))); err != nil { return err } return nil }) } func regDigest(t *testing.T, name string, contentStr string, digestMap map[string]digest.Digest) tarEntry { if digestMap == nil { t.Fatalf("digest map mustn't be nil") } content := []byte(contentStr) var n int64 for n < int64(len(content)) { size := int64(chunkSize) remain := int64(len(content)) - n if remain < size { size = remain } dgstr := digest.Canonical.Digester() if _, err := io.CopyN(dgstr.Hash(), bytes.NewReader(content[n:n+size]), size); err != nil { t.Fatalf("failed to calculate digest of %q (name=%q,offset=%d,size=%d)", string(content[n:n+size]), name, n, size) } digestMap[chunkID(name, n, size)] = dgstr.Digest() n += size } return tarEntryFunc(func(w *tar.Writer, prefix string, format tar.Format) error { if err := w.WriteHeader(&tar.Header{ Typeflag: tar.TypeReg, Name: prefix + name, Size: int64(len(content)), Format: format, }); err != nil { return err } if _, err := io.CopyN(w, bytes.NewReader(content), int64(len(content))); err != nil { return err } return nil }) } var runes = []rune("1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") func randomContents(n int) string { b := make([]rune, n) for i := range b { b[i] = runes[rand.Intn(len(runes))] } return string(b) } func fileModeToTarMode(mode os.FileMode) (int64, error) { h, err := tar.FileInfoHeader(fileInfoOnlyMode(mode), "") if err != nil { return 0, err } return h.Mode, nil } // fileInfoOnlyMode is os.FileMode that populates only file mode. type fileInfoOnlyMode os.FileMode func (f fileInfoOnlyMode) Name() string { return "" } func (f fileInfoOnlyMode) Size() int64 { return 0 } func (f fileInfoOnlyMode) Mode() os.FileMode { return os.FileMode(f) } func (f fileInfoOnlyMode) ModTime() time.Time { return time.Now() } func (f fileInfoOnlyMode) IsDir() bool { return os.FileMode(f).IsDir() } func (f fileInfoOnlyMode) Sys() interface{} { return nil } func CheckGzipHasStreams(t *testing.T, b []byte, streams []int64) { if len(streams) == 0 { return // nop } wants := map[int64]struct{}{} for _, s := range streams { wants[s] = struct{}{} } len0 := len(b) br := bytes.NewReader(b) zr := new(gzip.Reader) t.Logf("got gzip streams:") numStreams := 0 for { zoff := len0 - br.Len() if err := zr.Reset(br); err != nil { if err == io.EOF { return } t.Fatalf("countStreams(gzip), Reset: %v", err) } zr.Multistream(false) n, err := io.Copy(io.Discard, zr) if err != nil { t.Fatalf("countStreams(gzip), Copy: %v", err) } var extra string if len(zr.Header.Extra) > 0 { extra = fmt.Sprintf("; extra=%q", zr.Header.Extra) } t.Logf(" [%d] at %d in stargz, uncompressed length %d%s", numStreams, zoff, n, extra) delete(wants, int64(zoff)) numStreams++ } } func GzipDiffIDOf(t *testing.T, b []byte) string { h := sha256.New() zr, err := gzip.NewReader(bytes.NewReader(b)) if err != nil { t.Fatalf("diffIDOf(gzip): %v", err) } defer zr.Close() if _, err := io.Copy(h, zr); err != nil { t.Fatalf("diffIDOf(gzip).Copy: %v", err) } return fmt.Sprintf("sha256:%x", h.Sum(nil)) } ================================================ FILE: vendor/github.com/containerd/stargz-snapshotter/estargz/types.go ================================================ /* Copyright The containerd Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* Copyright 2019 The Go Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. */ package estargz import ( "archive/tar" "hash" "io" "os" "path" "time" digest "github.com/opencontainers/go-digest" ) const ( // TOCTarName is the name of the JSON file in the tar archive in the // table of contents gzip stream. TOCTarName = "stargz.index.json" // FooterSize is the number of bytes in the footer // // The footer is an empty gzip stream with no compression and an Extra // header of the form "%016xSTARGZ", where the 64 bit hex-encoded // number is the offset to the gzip stream of JSON TOC. // // 51 comes from: // // 10 bytes gzip header // 2 bytes XLEN (length of Extra field) = 26 (4 bytes header + 16 hex digits + len("STARGZ")) // 2 bytes Extra: SI1 = 'S', SI2 = 'G' // 2 bytes Extra: LEN = 22 (16 hex digits + len("STARGZ")) // 22 bytes Extra: subfield = fmt.Sprintf("%016xSTARGZ", offsetOfTOC) // 5 bytes flate header // 8 bytes gzip footer // (End of the eStargz blob) // // NOTE: For Extra fields, subfield IDs SI1='S' SI2='G' is used for eStargz. FooterSize = 51 // legacyFooterSize is the number of bytes in the legacy stargz footer. // // 47 comes from: // // 10 byte gzip header + // 2 byte (LE16) length of extra, encoding 22 (16 hex digits + len("STARGZ")) == "\x16\x00" + // 22 bytes of extra (fmt.Sprintf("%016xSTARGZ", tocGzipOffset)) // 5 byte flate header // 8 byte gzip footer (two little endian uint32s: digest, size) legacyFooterSize = 47 // TOCJSONDigestAnnotation is an annotation for an image layer. This stores the // digest of the TOC JSON. // This annotation is valid only when it is specified in `.[]layers.annotations` // of an image manifest. TOCJSONDigestAnnotation = "containerd.io/snapshot/stargz/toc.digest" // StoreUncompressedSizeAnnotation is an additional annotation key for eStargz to enable lazy // pulling on containers/storage. Stargz Store is required to expose the layer's uncompressed size // to the runtime but current OCI image doesn't ship this information by default. So we store this // to the special annotation. StoreUncompressedSizeAnnotation = "io.containers.estargz.uncompressed-size" // PrefetchLandmark is a file entry which indicates the end position of // prefetch in the stargz file. PrefetchLandmark = ".prefetch.landmark" // NoPrefetchLandmark is a file entry which indicates that no prefetch should // occur in the stargz file. NoPrefetchLandmark = ".no.prefetch.landmark" landmarkContents = 0xf ) // JTOC is the JSON-serialized table of contents index of the files in the stargz file. type JTOC struct { Version int `json:"version"` Entries []*TOCEntry `json:"entries"` } // TOCEntry is an entry in the stargz file's TOC (Table of Contents). type TOCEntry struct { // Name is the tar entry's name. It is the complete path // stored in the tar file, not just the base name. Name string `json:"name"` // Type is one of "dir", "reg", "symlink", "hardlink", "char", // "block", "fifo", or "chunk". // The "chunk" type is used for regular file data chunks past the first // TOCEntry; the 2nd chunk and on have only Type ("chunk"), Offset, // ChunkOffset, and ChunkSize populated. Type string `json:"type"` // Size, for regular files, is the logical size of the file. Size int64 `json:"size,omitempty"` // ModTime3339 is the modification time of the tar entry. Empty // means zero or unknown. Otherwise it's in UTC RFC3339 // format. Use the ModTime method to access the time.Time value. ModTime3339 string `json:"modtime,omitempty"` modTime time.Time // LinkName, for symlinks and hardlinks, is the link target. LinkName string `json:"linkName,omitempty"` // Mode is the permission and mode bits. Mode int64 `json:"mode,omitempty"` // UID is the user ID of the owner. UID int `json:"uid,omitempty"` // GID is the group ID of the owner. GID int `json:"gid,omitempty"` // Uname is the username of the owner. // // In the serialized JSON, this field may only be present for // the first entry with the same UID. Uname string `json:"userName,omitempty"` // Gname is the group name of the owner. // // In the serialized JSON, this field may only be present for // the first entry with the same GID. Gname string `json:"groupName,omitempty"` // Offset, for regular files, provides the offset in the // stargz file to the file's data bytes. See ChunkOffset and // ChunkSize. Offset int64 `json:"offset,omitempty"` // InnerOffset is an optional field indicates uncompressed offset // of this "reg" or "chunk" payload in a stream starts from Offset. // This field enables to put multiple "reg" or "chunk" payloads // in one chunk with having the same Offset but different InnerOffset. InnerOffset int64 `json:"innerOffset,omitempty"` nextOffset int64 // the Offset of the next entry with a non-zero Offset // DevMajor is the major device number for "char" and "block" types. DevMajor int `json:"devMajor,omitempty"` // DevMinor is the major device number for "char" and "block" types. DevMinor int `json:"devMinor,omitempty"` // NumLink is the number of entry names pointing to this entry. // Zero means one name references this entry. // This field is calculated during runtime and not recorded in TOC JSON. NumLink int `json:"-"` // Xattrs are the extended attribute for the entry. Xattrs map[string][]byte `json:"xattrs,omitempty"` // Digest stores the OCI checksum for regular files payload. // It has the form "sha256:abcdef01234....". Digest string `json:"digest,omitempty"` // ChunkOffset is non-zero if this is a chunk of a large, // regular file. If so, the Offset is where the gzip header of // ChunkSize bytes at ChunkOffset in Name begin. // // In serialized form, a "chunkSize" JSON field of zero means // that the chunk goes to the end of the file. After reading // from the stargz TOC, though, the ChunkSize is initialized // to a non-zero file for when Type is either "reg" or // "chunk". ChunkOffset int64 `json:"chunkOffset,omitempty"` ChunkSize int64 `json:"chunkSize,omitempty"` // ChunkDigest stores an OCI digest of the chunk. This must be formed // as "sha256:0123abcd...". ChunkDigest string `json:"chunkDigest,omitempty"` children map[string]*TOCEntry // chunkTopIndex is index of the entry where Offset starts in the blob. chunkTopIndex int } // ModTime returns the entry's modification time. func (e *TOCEntry) ModTime() time.Time { return e.modTime } // NextOffset returns the position (relative to the start of the // stargz file) of the next gzip boundary after e.Offset. func (e *TOCEntry) NextOffset() int64 { return e.nextOffset } func (e *TOCEntry) addChild(baseName string, child *TOCEntry) { if e.children == nil { e.children = make(map[string]*TOCEntry) } if child.Type == "dir" { e.NumLink++ // Entry ".." in the subdirectory links to this directory } e.children[baseName] = child } // isDataType reports whether TOCEntry is a regular file or chunk (something that // contains regular file data). func (e *TOCEntry) isDataType() bool { return e.Type == "reg" || e.Type == "chunk" } // Stat returns a FileInfo value representing e. func (e *TOCEntry) Stat() os.FileInfo { return fileInfo{e} } // ForeachChild calls f for each child item. If f returns false, iteration ends. // If e is not a directory, f is not called. func (e *TOCEntry) ForeachChild(f func(baseName string, ent *TOCEntry) bool) { for name, ent := range e.children { if !f(name, ent) { return } } } // LookupChild returns the directory e's child by its base name. func (e *TOCEntry) LookupChild(baseName string) (child *TOCEntry, ok bool) { child, ok = e.children[baseName] return } // fileInfo implements os.FileInfo using the wrapped *TOCEntry. type fileInfo struct{ e *TOCEntry } var _ os.FileInfo = fileInfo{} func (fi fileInfo) Name() string { return path.Base(fi.e.Name) } func (fi fileInfo) IsDir() bool { return fi.e.Type == "dir" } func (fi fileInfo) Size() int64 { return fi.e.Size } func (fi fileInfo) ModTime() time.Time { return fi.e.ModTime() } func (fi fileInfo) Sys() interface{} { return fi.e } func (fi fileInfo) Mode() (m os.FileMode) { // TOCEntry.Mode is tar.Header.Mode so we can understand the these bits using `tar` pkg. m = (&tar.Header{Mode: fi.e.Mode}).FileInfo().Mode() & (os.ModePerm | os.ModeSetuid | os.ModeSetgid | os.ModeSticky) switch fi.e.Type { case "dir": m |= os.ModeDir case "symlink": m |= os.ModeSymlink case "char": m |= os.ModeDevice | os.ModeCharDevice case "block": m |= os.ModeDevice case "fifo": m |= os.ModeNamedPipe } return m } // TOCEntryVerifier holds verifiers that are usable for verifying chunks contained // in a eStargz blob. type TOCEntryVerifier interface { // Verifier provides a content verifier that can be used for verifying the // contents of the specified TOCEntry. Verifier(ce *TOCEntry) (digest.Verifier, error) } // Compression provides the compression helper to be used creating and parsing eStargz. // This package provides gzip-based Compression by default, but any compression // algorithm (e.g. zstd) can be used as long as it implements Compression. type Compression interface { Compressor Decompressor } // Compressor represents the helper mothods to be used for creating eStargz. type Compressor interface { // Writer returns WriteCloser to be used for writing a chunk to eStargz. // Everytime a chunk is written, the WriteCloser is closed and Writer is // called again for writing the next chunk. // // The returned writer should implement "Flush() error" function that flushes // any pending compressed data to the underlying writer. Writer(w io.Writer) (WriteFlushCloser, error) // WriteTOCAndFooter is called to write JTOC to the passed Writer. // diffHash calculates the DiffID (uncompressed sha256 hash) of the blob // WriteTOCAndFooter can optionally write anything that affects DiffID calculation // (e.g. uncompressed TOC JSON). // // This function returns tocDgst that represents the digest of TOC that will be used // to verify this blob when it's parsed. WriteTOCAndFooter(w io.Writer, off int64, toc *JTOC, diffHash hash.Hash) (tocDgst digest.Digest, err error) } // Decompressor represents the helper mothods to be used for parsing eStargz. type Decompressor interface { // Reader returns ReadCloser to be used for decompressing file payload. Reader(r io.Reader) (io.ReadCloser, error) // FooterSize returns the size of the footer of this blob. FooterSize() int64 // ParseFooter parses the footer and returns the offset and (compressed) size of TOC. // payloadBlobSize is the (compressed) size of the blob payload (i.e. the size between // the top until the TOC JSON). // // If tocOffset < 0, we assume that TOC isn't contained in the blob and pass nil reader // to ParseTOC. We expect that ParseTOC acquire TOC from the external location and return it. // // tocSize is optional. If tocSize <= 0, it's by default the size of the range from tocOffset until the beginning of the // footer (blob size - tocOff - FooterSize). // If blobPayloadSize < 0, blobPayloadSize become the blob size. ParseFooter(p []byte) (blobPayloadSize, tocOffset, tocSize int64, err error) // ParseTOC parses TOC from the passed reader. The reader provides the partial contents // of the underlying blob that has the range specified by ParseFooter method. // // This function returns tocDgst that represents the digest of TOC that will be used // to verify this blob. This must match to the value returned from // Compressor.WriteTOCAndFooter that is used when creating this blob. // // If tocOffset returned by ParseFooter is < 0, we assume that TOC isn't contained in the blob. // Pass nil reader to ParseTOC then we expect that ParseTOC acquire TOC from the external location // and return it. ParseTOC(r io.Reader) (toc *JTOC, tocDgst digest.Digest, err error) } type WriteFlushCloser interface { io.WriteCloser Flush() error } ================================================ FILE: vendor/github.com/cpuguy83/go-md2man/v2/LICENSE.md ================================================ The MIT License (MIT) Copyright (c) 2014 Brian Goff Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/cpuguy83/go-md2man/v2/md2man/md2man.go ================================================ package md2man import ( "github.com/russross/blackfriday/v2" ) // Render converts a markdown document into a roff formatted document. func Render(doc []byte) []byte { renderer := NewRoffRenderer() return blackfriday.Run(doc, []blackfriday.Option{ blackfriday.WithRenderer(renderer), blackfriday.WithExtensions(renderer.GetExtensions()), }...) } ================================================ FILE: vendor/github.com/cpuguy83/go-md2man/v2/md2man/roff.go ================================================ package md2man import ( "bytes" "fmt" "io" "os" "strings" "github.com/russross/blackfriday/v2" ) // roffRenderer implements the blackfriday.Renderer interface for creating // roff format (manpages) from markdown text type roffRenderer struct { extensions blackfriday.Extensions listCounters []int firstHeader bool firstDD bool listDepth int } const ( titleHeader = ".TH " topLevelHeader = "\n\n.SH " secondLevelHdr = "\n.SH " otherHeader = "\n.SS " crTag = "\n" emphTag = "\\fI" emphCloseTag = "\\fP" strongTag = "\\fB" strongCloseTag = "\\fP" breakTag = "\n.br\n" paraTag = "\n.PP\n" hruleTag = "\n.ti 0\n\\l'\\n(.lu'\n" linkTag = "\n\\[la]" linkCloseTag = "\\[ra]" codespanTag = "\\fB" codespanCloseTag = "\\fR" codeTag = "\n.EX\n" codeCloseTag = "\n.EE\n" quoteTag = "\n.PP\n.RS\n" quoteCloseTag = "\n.RE\n" listTag = "\n.RS\n" listCloseTag = "\n.RE\n" dtTag = "\n.TP\n" dd2Tag = "\n" tableStart = "\n.TS\nallbox;\n" tableEnd = ".TE\n" tableCellStart = "T{\n" tableCellEnd = "\nT}\n" ) // NewRoffRenderer creates a new blackfriday Renderer for generating roff documents // from markdown func NewRoffRenderer() *roffRenderer { // nolint: golint var extensions blackfriday.Extensions extensions |= blackfriday.NoIntraEmphasis extensions |= blackfriday.Tables extensions |= blackfriday.FencedCode extensions |= blackfriday.SpaceHeadings extensions |= blackfriday.Footnotes extensions |= blackfriday.Titleblock extensions |= blackfriday.DefinitionLists return &roffRenderer{ extensions: extensions, } } // GetExtensions returns the list of extensions used by this renderer implementation func (r *roffRenderer) GetExtensions() blackfriday.Extensions { return r.extensions } // RenderHeader handles outputting the header at document start func (r *roffRenderer) RenderHeader(w io.Writer, ast *blackfriday.Node) { // disable hyphenation out(w, ".nh\n") } // RenderFooter handles outputting the footer at the document end; the roff // renderer has no footer information func (r *roffRenderer) RenderFooter(w io.Writer, ast *blackfriday.Node) { } // RenderNode is called for each node in a markdown document; based on the node // type the equivalent roff output is sent to the writer func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus { walkAction := blackfriday.GoToNext switch node.Type { case blackfriday.Text: escapeSpecialChars(w, node.Literal) case blackfriday.Softbreak: out(w, crTag) case blackfriday.Hardbreak: out(w, breakTag) case blackfriday.Emph: if entering { out(w, emphTag) } else { out(w, emphCloseTag) } case blackfriday.Strong: if entering { out(w, strongTag) } else { out(w, strongCloseTag) } case blackfriday.Link: // Don't render the link text for automatic links, because this // will only duplicate the URL in the roff output. // See https://daringfireball.net/projects/markdown/syntax#autolink if !bytes.Equal(node.LinkData.Destination, node.FirstChild.Literal) { out(w, string(node.FirstChild.Literal)) } // Hyphens in a link must be escaped to avoid word-wrap in the rendered man page. escapedLink := strings.ReplaceAll(string(node.LinkData.Destination), "-", "\\-") out(w, linkTag+escapedLink+linkCloseTag) walkAction = blackfriday.SkipChildren case blackfriday.Image: // ignore images walkAction = blackfriday.SkipChildren case blackfriday.Code: out(w, codespanTag) escapeSpecialChars(w, node.Literal) out(w, codespanCloseTag) case blackfriday.Document: break case blackfriday.Paragraph: // roff .PP markers break lists if r.listDepth > 0 { return blackfriday.GoToNext } if entering { out(w, paraTag) } else { out(w, crTag) } case blackfriday.BlockQuote: if entering { out(w, quoteTag) } else { out(w, quoteCloseTag) } case blackfriday.Heading: r.handleHeading(w, node, entering) case blackfriday.HorizontalRule: out(w, hruleTag) case blackfriday.List: r.handleList(w, node, entering) case blackfriday.Item: r.handleItem(w, node, entering) case blackfriday.CodeBlock: out(w, codeTag) escapeSpecialChars(w, node.Literal) out(w, codeCloseTag) case blackfriday.Table: r.handleTable(w, node, entering) case blackfriday.TableHead: case blackfriday.TableBody: case blackfriday.TableRow: // no action as cell entries do all the nroff formatting return blackfriday.GoToNext case blackfriday.TableCell: r.handleTableCell(w, node, entering) case blackfriday.HTMLSpan: // ignore other HTML tags case blackfriday.HTMLBlock: if bytes.HasPrefix(node.Literal, []byte(" Fail // C:\ --> \ // a --> a // /a --> \a // d:\ --> Fail func CheckSystemDriveAndRemoveDriveLetter(path string) (string, error) { return checkSystemDriveAndRemoveDriveLetter(path) } ================================================ FILE: vendor/github.com/docker/docker/pkg/archive/path_unix.go ================================================ //go:build !windows package archive // checkSystemDriveAndRemoveDriveLetter is the non-Windows implementation // of CheckSystemDriveAndRemoveDriveLetter func checkSystemDriveAndRemoveDriveLetter(path string) (string, error) { return path, nil } ================================================ FILE: vendor/github.com/docker/docker/pkg/archive/path_windows.go ================================================ package archive import ( "fmt" "path/filepath" "strings" ) // checkSystemDriveAndRemoveDriveLetter is the Windows implementation // of CheckSystemDriveAndRemoveDriveLetter func checkSystemDriveAndRemoveDriveLetter(path string) (string, error) { if len(path) == 2 && string(path[1]) == ":" { return "", fmt.Errorf("no relative path specified in %q", path) } if !filepath.IsAbs(path) || len(path) < 2 { return filepath.FromSlash(path), nil } if string(path[1]) == ":" && !strings.EqualFold(string(path[0]), "c") { return "", fmt.Errorf("the specified path is not on the system drive (C:)") } return filepath.FromSlash(path[2:]), nil } ================================================ FILE: vendor/github.com/docker/docker/pkg/archive/time_linux.go ================================================ package archive // import "github.com/docker/docker/pkg/archive" import ( "syscall" "time" ) func timeToTimespec(time time.Time) (ts syscall.Timespec) { if time.IsZero() { // Return UTIME_OMIT special value ts.Sec = 0 ts.Nsec = (1 << 30) - 2 return } return syscall.NsecToTimespec(time.UnixNano()) } ================================================ FILE: vendor/github.com/docker/docker/pkg/archive/time_unsupported.go ================================================ //go:build !linux package archive // import "github.com/docker/docker/pkg/archive" import ( "syscall" "time" ) func timeToTimespec(time time.Time) (ts syscall.Timespec) { nsec := int64(0) if !time.IsZero() { nsec = time.UnixNano() } return syscall.NsecToTimespec(nsec) } ================================================ FILE: vendor/github.com/docker/docker/pkg/archive/whiteouts.go ================================================ package archive // import "github.com/docker/docker/pkg/archive" // Whiteouts are files with a special meaning for the layered filesystem. // Docker uses AUFS whiteout files inside exported archives. In other // filesystems these files are generated/handled on tar creation/extraction. // WhiteoutPrefix prefix means file is a whiteout. If this is followed by a // filename this means that file has been removed from the base layer. const WhiteoutPrefix = ".wh." // WhiteoutMetaPrefix prefix means whiteout has a special meaning and is not // for removing an actual file. Normally these files are excluded from exported // archives. const WhiteoutMetaPrefix = WhiteoutPrefix + WhiteoutPrefix // WhiteoutLinkDir is a directory AUFS uses for storing hardlink links to other // layers. Normally these should not go into exported archives and all changed // hardlinks should be copied to the top layer. const WhiteoutLinkDir = WhiteoutMetaPrefix + "plnk" // WhiteoutOpaqueDir file means directory has been made opaque - meaning // readdir calls to this directory do not follow to lower layers. const WhiteoutOpaqueDir = WhiteoutMetaPrefix + ".opq" ================================================ FILE: vendor/github.com/docker/docker/pkg/archive/wrap.go ================================================ package archive // import "github.com/docker/docker/pkg/archive" import ( "archive/tar" "bytes" "io" ) // Generate generates a new archive from the content provided // as input. // // `files` is a sequence of path/content pairs. A new file is // added to the archive for each pair. // If the last pair is incomplete, the file is created with an // empty content. For example: // // Generate("foo.txt", "hello world", "emptyfile") // // The above call will return an archive with 2 files: // - ./foo.txt with content "hello world" // - ./empty with empty content // // FIXME: stream content instead of buffering // FIXME: specify permissions and other archive metadata func Generate(input ...string) (io.Reader, error) { files := parseStringPairs(input...) buf := new(bytes.Buffer) tw := tar.NewWriter(buf) for _, file := range files { name, content := file[0], file[1] hdr := &tar.Header{ Name: name, Size: int64(len(content)), } if err := tw.WriteHeader(hdr); err != nil { return nil, err } if _, err := tw.Write([]byte(content)); err != nil { return nil, err } } if err := tw.Close(); err != nil { return nil, err } return buf, nil } func parseStringPairs(input ...string) (output [][2]string) { output = make([][2]string, 0, len(input)/2+1) for i := 0; i < len(input); i += 2 { var pair [2]string pair[0] = input[i] if i+1 < len(input) { pair[1] = input[i+1] } output = append(output, pair) } return } ================================================ FILE: vendor/github.com/docker/docker/pkg/homedir/homedir.go ================================================ package homedir import ( "os" "os/user" "runtime" ) // Key returns the env var name for the user's home dir based on // the platform being run on. // // Deprecated: this function is no longer used, and will be removed in the next release. func Key() string { return envKeyName } // Get returns the home directory of the current user with the help of // environment variables depending on the target operating system. // Returned path should be used with "path/filepath" to form new paths. // // On non-Windows platforms, it falls back to nss lookups, if the home // directory cannot be obtained from environment-variables. // // If linking statically with cgo enabled against glibc, ensure the // osusergo build tag is used. // // If needing to do nss lookups, do not disable cgo or set osusergo. func Get() string { home, _ := os.UserHomeDir() if home == "" && runtime.GOOS != "windows" { if u, err := user.Current(); err == nil { return u.HomeDir } } return home } // GetShortcutString returns the string that is shortcut to user's home directory // in the native shell of the platform running on. // // Deprecated: this function is no longer used, and will be removed in the next release. func GetShortcutString() string { return homeShortCut } ================================================ FILE: vendor/github.com/docker/docker/pkg/homedir/homedir_linux.go ================================================ package homedir // import "github.com/docker/docker/pkg/homedir" import ( "errors" "os" "path/filepath" "strings" ) // GetRuntimeDir returns XDG_RUNTIME_DIR. // XDG_RUNTIME_DIR is typically configured via pam_systemd. // GetRuntimeDir returns non-nil error if XDG_RUNTIME_DIR is not set. // // See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html func GetRuntimeDir() (string, error) { if xdgRuntimeDir := os.Getenv("XDG_RUNTIME_DIR"); xdgRuntimeDir != "" { return xdgRuntimeDir, nil } return "", errors.New("could not get XDG_RUNTIME_DIR") } // StickRuntimeDirContents sets the sticky bit on files that are under // XDG_RUNTIME_DIR, so that the files won't be periodically removed by the system. // // StickyRuntimeDir returns slice of sticked files. // StickyRuntimeDir returns nil error if XDG_RUNTIME_DIR is not set. // // See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html func StickRuntimeDirContents(files []string) ([]string, error) { runtimeDir, err := GetRuntimeDir() if err != nil { // ignore error if runtimeDir is empty return nil, nil } runtimeDir, err = filepath.Abs(runtimeDir) if err != nil { return nil, err } var sticked []string for _, f := range files { f, err = filepath.Abs(f) if err != nil { return sticked, err } if strings.HasPrefix(f, runtimeDir+"/") { if err = stick(f); err != nil { return sticked, err } sticked = append(sticked, f) } } return sticked, nil } func stick(f string) error { st, err := os.Stat(f) if err != nil { return err } m := st.Mode() m |= os.ModeSticky return os.Chmod(f, m) } // GetDataHome returns XDG_DATA_HOME. // GetDataHome returns $HOME/.local/share and nil error if XDG_DATA_HOME is not set. // If HOME and XDG_DATA_HOME are not set, getpwent(3) is consulted to determine the users home directory. // // See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html func GetDataHome() (string, error) { if xdgDataHome := os.Getenv("XDG_DATA_HOME"); xdgDataHome != "" { return xdgDataHome, nil } home := Get() if home == "" { return "", errors.New("could not get either XDG_DATA_HOME or HOME") } return filepath.Join(home, ".local", "share"), nil } // GetConfigHome returns XDG_CONFIG_HOME. // GetConfigHome returns $HOME/.config and nil error if XDG_CONFIG_HOME is not set. // If HOME and XDG_CONFIG_HOME are not set, getpwent(3) is consulted to determine the users home directory. // // See also https://standards.freedesktop.org/basedir-spec/latest/ar01s03.html func GetConfigHome() (string, error) { if xdgConfigHome := os.Getenv("XDG_CONFIG_HOME"); xdgConfigHome != "" { return xdgConfigHome, nil } home := Get() if home == "" { return "", errors.New("could not get either XDG_CONFIG_HOME or HOME") } return filepath.Join(home, ".config"), nil } // GetLibHome returns $HOME/.local/lib // If HOME is not set, getpwent(3) is consulted to determine the users home directory. func GetLibHome() (string, error) { home := Get() if home == "" { return "", errors.New("could not get HOME") } return filepath.Join(home, ".local/lib"), nil } ================================================ FILE: vendor/github.com/docker/docker/pkg/homedir/homedir_others.go ================================================ //go:build !linux package homedir // import "github.com/docker/docker/pkg/homedir" import ( "errors" ) // GetRuntimeDir is unsupported on non-linux system. func GetRuntimeDir() (string, error) { return "", errors.New("homedir.GetRuntimeDir() is not supported on this system") } // StickRuntimeDirContents is unsupported on non-linux system. func StickRuntimeDirContents(files []string) ([]string, error) { return nil, errors.New("homedir.StickRuntimeDirContents() is not supported on this system") } // GetDataHome is unsupported on non-linux system. func GetDataHome() (string, error) { return "", errors.New("homedir.GetDataHome() is not supported on this system") } // GetConfigHome is unsupported on non-linux system. func GetConfigHome() (string, error) { return "", errors.New("homedir.GetConfigHome() is not supported on this system") } // GetLibHome is unsupported on non-linux system. func GetLibHome() (string, error) { return "", errors.New("homedir.GetLibHome() is not supported on this system") } ================================================ FILE: vendor/github.com/docker/docker/pkg/homedir/homedir_unix.go ================================================ //go:build !windows package homedir // import "github.com/docker/docker/pkg/homedir" const ( envKeyName = "HOME" homeShortCut = "~" ) ================================================ FILE: vendor/github.com/docker/docker/pkg/homedir/homedir_windows.go ================================================ package homedir // import "github.com/docker/docker/pkg/homedir" const ( envKeyName = "USERPROFILE" homeShortCut = "%USERPROFILE%" // be careful while using in format functions ) ================================================ FILE: vendor/github.com/docker/docker/pkg/idtools/idtools.go ================================================ package idtools // import "github.com/docker/docker/pkg/idtools" import ( "bufio" "fmt" "os" "strconv" "strings" ) // IDMap contains a single entry for user namespace range remapping. An array // of IDMap entries represents the structure that will be provided to the Linux // kernel for creating a user namespace. type IDMap struct { ContainerID int `json:"container_id"` HostID int `json:"host_id"` Size int `json:"size"` } type subIDRange struct { Start int Length int } type ranges []subIDRange func (e ranges) Len() int { return len(e) } func (e ranges) Swap(i, j int) { e[i], e[j] = e[j], e[i] } func (e ranges) Less(i, j int) bool { return e[i].Start < e[j].Start } const ( subuidFileName = "/etc/subuid" subgidFileName = "/etc/subgid" ) // MkdirAllAndChown creates a directory (include any along the path) and then modifies // ownership to the requested uid/gid. If the directory already exists, this // function will still change ownership and permissions. func MkdirAllAndChown(path string, mode os.FileMode, owner Identity) error { return mkdirAs(path, mode, owner, true, true) } // MkdirAndChown creates a directory and then modifies ownership to the requested uid/gid. // If the directory already exists, this function still changes ownership and permissions. // Note that unlike os.Mkdir(), this function does not return IsExist error // in case path already exists. func MkdirAndChown(path string, mode os.FileMode, owner Identity) error { return mkdirAs(path, mode, owner, false, true) } // MkdirAllAndChownNew creates a directory (include any along the path) and then modifies // ownership ONLY of newly created directories to the requested uid/gid. If the // directories along the path exist, no change of ownership or permissions will be performed func MkdirAllAndChownNew(path string, mode os.FileMode, owner Identity) error { return mkdirAs(path, mode, owner, true, false) } // GetRootUIDGID retrieves the remapped root uid/gid pair from the set of maps. // If the maps are empty, then the root uid/gid will default to "real" 0/0 func GetRootUIDGID(uidMap, gidMap []IDMap) (int, int, error) { uid, err := toHost(0, uidMap) if err != nil { return -1, -1, err } gid, err := toHost(0, gidMap) if err != nil { return -1, -1, err } return uid, gid, nil } // toContainer takes an id mapping, and uses it to translate a // host ID to the remapped ID. If no map is provided, then the translation // assumes a 1-to-1 mapping and returns the passed in id func toContainer(hostID int, idMap []IDMap) (int, error) { if idMap == nil { return hostID, nil } for _, m := range idMap { if (hostID >= m.HostID) && (hostID <= (m.HostID + m.Size - 1)) { contID := m.ContainerID + (hostID - m.HostID) return contID, nil } } return -1, fmt.Errorf("Host ID %d cannot be mapped to a container ID", hostID) } // toHost takes an id mapping and a remapped ID, and translates the // ID to the mapped host ID. If no map is provided, then the translation // assumes a 1-to-1 mapping and returns the passed in id # func toHost(contID int, idMap []IDMap) (int, error) { if idMap == nil { return contID, nil } for _, m := range idMap { if (contID >= m.ContainerID) && (contID <= (m.ContainerID + m.Size - 1)) { hostID := m.HostID + (contID - m.ContainerID) return hostID, nil } } return -1, fmt.Errorf("Container ID %d cannot be mapped to a host ID", contID) } // Identity is either a UID and GID pair or a SID (but not both) type Identity struct { UID int GID int SID string } // Chown changes the numeric uid and gid of the named file to id.UID and id.GID. func (id Identity) Chown(name string) error { return os.Chown(name, id.UID, id.GID) } // IdentityMapping contains a mappings of UIDs and GIDs. // The zero value represents an empty mapping. type IdentityMapping struct { UIDMaps []IDMap `json:"UIDMaps"` GIDMaps []IDMap `json:"GIDMaps"` } // RootPair returns a uid and gid pair for the root user. The error is ignored // because a root user always exists, and the defaults are correct when the uid // and gid maps are empty. func (i IdentityMapping) RootPair() Identity { uid, gid, _ := GetRootUIDGID(i.UIDMaps, i.GIDMaps) return Identity{UID: uid, GID: gid} } // ToHost returns the host UID and GID for the container uid, gid. // Remapping is only performed if the ids aren't already the remapped root ids func (i IdentityMapping) ToHost(pair Identity) (Identity, error) { var err error target := i.RootPair() if pair.UID != target.UID { target.UID, err = toHost(pair.UID, i.UIDMaps) if err != nil { return target, err } } if pair.GID != target.GID { target.GID, err = toHost(pair.GID, i.GIDMaps) } return target, err } // ToContainer returns the container UID and GID for the host uid and gid func (i IdentityMapping) ToContainer(pair Identity) (int, int, error) { uid, err := toContainer(pair.UID, i.UIDMaps) if err != nil { return -1, -1, err } gid, err := toContainer(pair.GID, i.GIDMaps) return uid, gid, err } // Empty returns true if there are no id mappings func (i IdentityMapping) Empty() bool { return len(i.UIDMaps) == 0 && len(i.GIDMaps) == 0 } func createIDMap(subidRanges ranges) []IDMap { idMap := []IDMap{} containerID := 0 for _, idrange := range subidRanges { idMap = append(idMap, IDMap{ ContainerID: containerID, HostID: idrange.Start, Size: idrange.Length, }) containerID = containerID + idrange.Length } return idMap } func parseSubuid(username string) (ranges, error) { return parseSubidFile(subuidFileName, username) } func parseSubgid(username string) (ranges, error) { return parseSubidFile(subgidFileName, username) } // parseSubidFile will read the appropriate file (/etc/subuid or /etc/subgid) // and return all found ranges for a specified username. If the special value // "ALL" is supplied for username, then all ranges in the file will be returned func parseSubidFile(path, username string) (ranges, error) { var rangeList ranges subidFile, err := os.Open(path) if err != nil { return rangeList, err } defer subidFile.Close() s := bufio.NewScanner(subidFile) for s.Scan() { text := strings.TrimSpace(s.Text()) if text == "" || strings.HasPrefix(text, "#") { continue } parts := strings.Split(text, ":") if len(parts) != 3 { return rangeList, fmt.Errorf("Cannot parse subuid/gid information: Format not correct for %s file", path) } if parts[0] == username || username == "ALL" { startid, err := strconv.Atoi(parts[1]) if err != nil { return rangeList, fmt.Errorf("String to int conversion failed during subuid/gid parsing of %s: %v", path, err) } length, err := strconv.Atoi(parts[2]) if err != nil { return rangeList, fmt.Errorf("String to int conversion failed during subuid/gid parsing of %s: %v", path, err) } rangeList = append(rangeList, subIDRange{startid, length}) } } return rangeList, s.Err() } // CurrentIdentity returns the identity of the current process func CurrentIdentity() Identity { return Identity{UID: os.Getuid(), GID: os.Getegid()} } ================================================ FILE: vendor/github.com/docker/docker/pkg/idtools/idtools_unix.go ================================================ //go:build !windows package idtools // import "github.com/docker/docker/pkg/idtools" import ( "bytes" "fmt" "io" "os" "os/exec" "path/filepath" "strconv" "syscall" "github.com/moby/sys/user" ) func mkdirAs(path string, mode os.FileMode, owner Identity, mkAll, chownExisting bool) error { path, err := filepath.Abs(path) if err != nil { return err } stat, err := os.Stat(path) if err == nil { if !stat.IsDir() { return &os.PathError{Op: "mkdir", Path: path, Err: syscall.ENOTDIR} } if !chownExisting { return nil } // short-circuit -- we were called with an existing directory and chown was requested return setPermissions(path, mode, owner, stat) } // make an array containing the original path asked for, plus (for mkAll == true) // all path components leading up to the complete path that don't exist before we MkdirAll // so that we can chown all of them properly at the end. If chownExisting is false, we won't // chown the full directory path if it exists var paths []string if os.IsNotExist(err) { paths = []string{path} } if mkAll { // walk back to "/" looking for directories which do not exist // and add them to the paths array for chown after creation dirPath := path for { dirPath = filepath.Dir(dirPath) if dirPath == "/" { break } if _, err = os.Stat(dirPath); err != nil && os.IsNotExist(err) { paths = append(paths, dirPath) } } if err = os.MkdirAll(path, mode); err != nil { return err } } else if err = os.Mkdir(path, mode); err != nil { return err } // even if it existed, we will chown the requested path + any subpaths that // didn't exist when we called MkdirAll for _, pathComponent := range paths { if err = setPermissions(pathComponent, mode, owner, nil); err != nil { return err } } return nil } // LookupUser uses traditional local system files lookup (from libcontainer/user) on a username, // followed by a call to `getent` for supporting host configured non-files passwd and group dbs func LookupUser(name string) (user.User, error) { // first try a local system files lookup using existing capabilities usr, err := user.LookupUser(name) if err == nil { return usr, nil } // local files lookup failed; attempt to call `getent` to query configured passwd dbs usr, err = getentUser(name) if err != nil { return user.User{}, err } return usr, nil } // LookupUID uses traditional local system files lookup (from libcontainer/user) on a uid, // followed by a call to `getent` for supporting host configured non-files passwd and group dbs func LookupUID(uid int) (user.User, error) { // first try a local system files lookup using existing capabilities usr, err := user.LookupUid(uid) if err == nil { return usr, nil } // local files lookup failed; attempt to call `getent` to query configured passwd dbs return getentUser(strconv.Itoa(uid)) } func getentUser(name string) (user.User, error) { reader, err := callGetent("passwd", name) if err != nil { return user.User{}, err } users, err := user.ParsePasswd(reader) if err != nil { return user.User{}, err } if len(users) == 0 { return user.User{}, fmt.Errorf("getent failed to find passwd entry for %q", name) } return users[0], nil } // LookupGroup uses traditional local system files lookup (from libcontainer/user) on a group name, // followed by a call to `getent` for supporting host configured non-files passwd and group dbs func LookupGroup(name string) (user.Group, error) { // first try a local system files lookup using existing capabilities group, err := user.LookupGroup(name) if err == nil { return group, nil } // local files lookup failed; attempt to call `getent` to query configured group dbs return getentGroup(name) } // LookupGID uses traditional local system files lookup (from libcontainer/user) on a group ID, // followed by a call to `getent` for supporting host configured non-files passwd and group dbs func LookupGID(gid int) (user.Group, error) { // first try a local system files lookup using existing capabilities group, err := user.LookupGid(gid) if err == nil { return group, nil } // local files lookup failed; attempt to call `getent` to query configured group dbs return getentGroup(strconv.Itoa(gid)) } func getentGroup(name string) (user.Group, error) { reader, err := callGetent("group", name) if err != nil { return user.Group{}, err } groups, err := user.ParseGroup(reader) if err != nil { return user.Group{}, err } if len(groups) == 0 { return user.Group{}, fmt.Errorf("getent failed to find groups entry for %q", name) } return groups[0], nil } func callGetent(database, key string) (io.Reader, error) { getentCmd, err := resolveBinary("getent") // if no `getent` command within the execution environment, can't do anything else if err != nil { return nil, fmt.Errorf("unable to find getent command: %w", err) } command := exec.Command(getentCmd, database, key) // we run getent within container filesystem, but without /dev so /dev/null is not available for exec to mock stdin command.Stdin = io.NopCloser(bytes.NewReader(nil)) out, err := command.CombinedOutput() if err != nil { exitCode, errC := getExitCode(err) if errC != nil { return nil, err } switch exitCode { case 1: return nil, fmt.Errorf("getent reported invalid parameters/database unknown") case 2: return nil, fmt.Errorf("getent unable to find entry %q in %s database", key, database) case 3: return nil, fmt.Errorf("getent database doesn't support enumeration") default: return nil, err } } return bytes.NewReader(out), nil } // getExitCode returns the ExitStatus of the specified error if its type is // exec.ExitError, returns 0 and an error otherwise. func getExitCode(err error) (int, error) { exitCode := 0 if exiterr, ok := err.(*exec.ExitError); ok { if procExit, ok := exiterr.Sys().(syscall.WaitStatus); ok { return procExit.ExitStatus(), nil } } return exitCode, fmt.Errorf("failed to get exit code") } // setPermissions performs a chown/chmod only if the uid/gid don't match what's requested // Normally a Chown is a no-op if uid/gid match, but in some cases this can still cause an error, e.g. if the // dir is on an NFS share, so don't call chown unless we absolutely must. // Likewise for setting permissions. func setPermissions(p string, mode os.FileMode, owner Identity, stat os.FileInfo) error { if stat == nil { var err error stat, err = os.Stat(p) if err != nil { return err } } if stat.Mode().Perm() != mode.Perm() { if err := os.Chmod(p, mode.Perm()); err != nil { return err } } ssi := stat.Sys().(*syscall.Stat_t) if ssi.Uid == uint32(owner.UID) && ssi.Gid == uint32(owner.GID) { return nil } return os.Chown(p, owner.UID, owner.GID) } // LoadIdentityMapping takes a requested username and // using the data from /etc/sub{uid,gid} ranges, creates the // proper uid and gid remapping ranges for that user/group pair func LoadIdentityMapping(name string) (IdentityMapping, error) { usr, err := LookupUser(name) if err != nil { return IdentityMapping{}, fmt.Errorf("could not get user for username %s: %v", name, err) } subuidRanges, err := lookupSubUIDRanges(usr) if err != nil { return IdentityMapping{}, err } subgidRanges, err := lookupSubGIDRanges(usr) if err != nil { return IdentityMapping{}, err } return IdentityMapping{ UIDMaps: subuidRanges, GIDMaps: subgidRanges, }, nil } func lookupSubUIDRanges(usr user.User) ([]IDMap, error) { rangeList, err := parseSubuid(strconv.Itoa(usr.Uid)) if err != nil { return nil, err } if len(rangeList) == 0 { rangeList, err = parseSubuid(usr.Name) if err != nil { return nil, err } } if len(rangeList) == 0 { return nil, fmt.Errorf("no subuid ranges found for user %q", usr.Name) } return createIDMap(rangeList), nil } func lookupSubGIDRanges(usr user.User) ([]IDMap, error) { rangeList, err := parseSubgid(strconv.Itoa(usr.Uid)) if err != nil { return nil, err } if len(rangeList) == 0 { rangeList, err = parseSubgid(usr.Name) if err != nil { return nil, err } } if len(rangeList) == 0 { return nil, fmt.Errorf("no subgid ranges found for user %q", usr.Name) } return createIDMap(rangeList), nil } ================================================ FILE: vendor/github.com/docker/docker/pkg/idtools/idtools_windows.go ================================================ package idtools // import "github.com/docker/docker/pkg/idtools" import ( "os" "github.com/docker/docker/pkg/system" ) const ( SeTakeOwnershipPrivilege = "SeTakeOwnershipPrivilege" ) const ( ContainerAdministratorSidString = "S-1-5-93-2-1" ContainerUserSidString = "S-1-5-93-2-2" ) // This is currently a wrapper around MkdirAll, however, since currently // permissions aren't set through this path, the identity isn't utilized. // Ownership is handled elsewhere, but in the future could be support here // too. func mkdirAs(path string, _ os.FileMode, _ Identity, _, _ bool) error { return system.MkdirAll(path, 0) } ================================================ FILE: vendor/github.com/docker/docker/pkg/idtools/usergroupadd_linux.go ================================================ package idtools // import "github.com/docker/docker/pkg/idtools" import ( "fmt" "os/exec" "regexp" "sort" "strconv" "strings" "sync" ) // add a user and/or group to Linux /etc/passwd, /etc/group using standard // Linux distribution commands: // adduser --system --shell /bin/false --disabled-login --disabled-password --no-create-home --group // useradd -r -s /bin/false var ( once sync.Once userCommand string idOutRegexp = regexp.MustCompile(`uid=([0-9]+).*gid=([0-9]+)`) ) const ( // default length for a UID/GID subordinate range defaultRangeLen = 65536 defaultRangeStart = 100000 ) // AddNamespaceRangesUser takes a username and uses the standard system // utility to create a system user/group pair used to hold the // /etc/sub{uid,gid} ranges which will be used for user namespace // mapping ranges in containers. func AddNamespaceRangesUser(name string) (int, int, error) { if err := addUser(name); err != nil { return -1, -1, fmt.Errorf("error adding user %q: %v", name, err) } // Query the system for the created uid and gid pair out, err := exec.Command("id", name).CombinedOutput() if err != nil { return -1, -1, fmt.Errorf("error trying to find uid/gid for new user %q: %v", name, err) } matches := idOutRegexp.FindStringSubmatch(strings.TrimSpace(string(out))) if len(matches) != 3 { return -1, -1, fmt.Errorf("can't find uid, gid from `id` output: %q", string(out)) } uid, err := strconv.Atoi(matches[1]) if err != nil { return -1, -1, fmt.Errorf("can't convert found uid (%s) to int: %v", matches[1], err) } gid, err := strconv.Atoi(matches[2]) if err != nil { return -1, -1, fmt.Errorf("Can't convert found gid (%s) to int: %v", matches[2], err) } // Now we need to create the subuid/subgid ranges for our new user/group (system users // do not get auto-created ranges in subuid/subgid) if err := createSubordinateRanges(name); err != nil { return -1, -1, fmt.Errorf("couldn't create subordinate ID ranges: %v", err) } return uid, gid, nil } func addUser(name string) error { once.Do(func() { // set up which commands are used for adding users/groups dependent on distro if _, err := resolveBinary("adduser"); err == nil { userCommand = "adduser" } else if _, err := resolveBinary("useradd"); err == nil { userCommand = "useradd" } }) var args []string switch userCommand { case "adduser": args = []string{"--system", "--shell", "/bin/false", "--no-create-home", "--disabled-login", "--disabled-password", "--group", name} case "useradd": args = []string{"-r", "-s", "/bin/false", name} default: return fmt.Errorf("cannot add user; no useradd/adduser binary found") } if out, err := exec.Command(userCommand, args...).CombinedOutput(); err != nil { return fmt.Errorf("failed to add user with error: %v; output: %q", err, string(out)) } return nil } func createSubordinateRanges(name string) error { // first, we should verify that ranges weren't automatically created // by the distro tooling ranges, err := parseSubuid(name) if err != nil { return fmt.Errorf("error while looking for subuid ranges for user %q: %v", name, err) } if len(ranges) == 0 { // no UID ranges; let's create one startID, err := findNextUIDRange() if err != nil { return fmt.Errorf("can't find available subuid range: %v", err) } idRange := fmt.Sprintf("%d-%d", startID, startID+defaultRangeLen-1) out, err := exec.Command("usermod", "-v", idRange, name).CombinedOutput() if err != nil { return fmt.Errorf("unable to add subuid range to user: %q; output: %s, err: %v", name, out, err) } } ranges, err = parseSubgid(name) if err != nil { return fmt.Errorf("error while looking for subgid ranges for user %q: %v", name, err) } if len(ranges) == 0 { // no GID ranges; let's create one startID, err := findNextGIDRange() if err != nil { return fmt.Errorf("can't find available subgid range: %v", err) } idRange := fmt.Sprintf("%d-%d", startID, startID+defaultRangeLen-1) out, err := exec.Command("usermod", "-w", idRange, name).CombinedOutput() if err != nil { return fmt.Errorf("unable to add subgid range to user: %q; output: %s, err: %v", name, out, err) } } return nil } func findNextUIDRange() (int, error) { ranges, err := parseSubuid("ALL") if err != nil { return -1, fmt.Errorf("couldn't parse all ranges in /etc/subuid file: %v", err) } sort.Sort(ranges) return findNextRangeStart(ranges) } func findNextGIDRange() (int, error) { ranges, err := parseSubgid("ALL") if err != nil { return -1, fmt.Errorf("couldn't parse all ranges in /etc/subgid file: %v", err) } sort.Sort(ranges) return findNextRangeStart(ranges) } func findNextRangeStart(rangeList ranges) (int, error) { startID := defaultRangeStart for _, arange := range rangeList { if wouldOverlap(arange, startID) { startID = arange.Start + arange.Length } } return startID, nil } func wouldOverlap(arange subIDRange, ID int) bool { low := ID high := ID + defaultRangeLen if (low >= arange.Start && low <= arange.Start+arange.Length) || (high <= arange.Start+arange.Length && high >= arange.Start) { return true } return false } ================================================ FILE: vendor/github.com/docker/docker/pkg/idtools/usergroupadd_unsupported.go ================================================ //go:build !linux package idtools // import "github.com/docker/docker/pkg/idtools" import "fmt" // AddNamespaceRangesUser takes a name and finds an unused uid, gid pair // and calls the appropriate helper function to add the group and then // the user to the group in /etc/group and /etc/passwd respectively. func AddNamespaceRangesUser(name string) (int, int, error) { return -1, -1, fmt.Errorf("No support for adding users or groups on this OS") } ================================================ FILE: vendor/github.com/docker/docker/pkg/idtools/utils_unix.go ================================================ //go:build !windows package idtools // import "github.com/docker/docker/pkg/idtools" import ( "fmt" "os/exec" "path/filepath" ) func resolveBinary(binname string) (string, error) { binaryPath, err := exec.LookPath(binname) if err != nil { return "", err } resolvedPath, err := filepath.EvalSymlinks(binaryPath) if err != nil { return "", err } // only return no error if the final resolved binary basename // matches what was searched for if filepath.Base(resolvedPath) == binname { return resolvedPath, nil } return "", fmt.Errorf("Binary %q does not resolve to a binary of that name in $PATH (%q)", binname, resolvedPath) } ================================================ FILE: vendor/github.com/docker/docker/pkg/ioutils/buffer.go ================================================ package ioutils // import "github.com/docker/docker/pkg/ioutils" import ( "errors" "io" ) var errBufferFull = errors.New("buffer is full") type fixedBuffer struct { buf []byte pos int lastRead int } func (b *fixedBuffer) Write(p []byte) (int, error) { n := copy(b.buf[b.pos:cap(b.buf)], p) b.pos += n if n < len(p) { if b.pos == cap(b.buf) { return n, errBufferFull } return n, io.ErrShortWrite } return n, nil } func (b *fixedBuffer) Read(p []byte) (int, error) { n := copy(p, b.buf[b.lastRead:b.pos]) b.lastRead += n return n, nil } func (b *fixedBuffer) Len() int { return b.pos - b.lastRead } func (b *fixedBuffer) Cap() int { return cap(b.buf) } func (b *fixedBuffer) Reset() { b.pos = 0 b.lastRead = 0 b.buf = b.buf[:0] } func (b *fixedBuffer) String() string { return string(b.buf[b.lastRead:b.pos]) } ================================================ FILE: vendor/github.com/docker/docker/pkg/ioutils/bytespipe.go ================================================ package ioutils // import "github.com/docker/docker/pkg/ioutils" import ( "errors" "io" "sync" ) // maxCap is the highest capacity to use in byte slices that buffer data. const maxCap = 1e6 // minCap is the lowest capacity to use in byte slices that buffer data const minCap = 64 // blockThreshold is the minimum number of bytes in the buffer which will cause // a write to BytesPipe to block when allocating a new slice. const blockThreshold = 1e6 var ( // ErrClosed is returned when Write is called on a closed BytesPipe. ErrClosed = errors.New("write to closed BytesPipe") bufPools = make(map[int]*sync.Pool) bufPoolsLock sync.Mutex ) // BytesPipe is io.ReadWriteCloser which works similarly to pipe(queue). // All written data may be read at most once. Also, BytesPipe allocates // and releases new byte slices to adjust to current needs, so the buffer // won't be overgrown after peak loads. type BytesPipe struct { mu sync.Mutex wait *sync.Cond buf []*fixedBuffer bufLen int closeErr error // error to return from next Read. set to nil if not closed. readBlock bool // check read BytesPipe is Wait() or not } // NewBytesPipe creates new BytesPipe, initialized by specified slice. // If buf is nil, then it will be initialized with slice which cap is 64. // buf will be adjusted in a way that len(buf) == 0, cap(buf) == cap(buf). func NewBytesPipe() *BytesPipe { bp := &BytesPipe{} bp.buf = append(bp.buf, getBuffer(minCap)) bp.wait = sync.NewCond(&bp.mu) return bp } // Write writes p to BytesPipe. // It can allocate new []byte slices in a process of writing. func (bp *BytesPipe) Write(p []byte) (int, error) { bp.mu.Lock() defer bp.mu.Unlock() written := 0 loop0: for { if bp.closeErr != nil { return written, ErrClosed } if len(bp.buf) == 0 { bp.buf = append(bp.buf, getBuffer(64)) } // get the last buffer b := bp.buf[len(bp.buf)-1] n, err := b.Write(p) written += n bp.bufLen += n // errBufferFull is an error we expect to get if the buffer is full if err != nil && err != errBufferFull { bp.wait.Broadcast() return written, err } // if there was enough room to write all then break if len(p) == n { break } // more data: write to the next slice p = p[n:] // make sure the buffer doesn't grow too big from this write for bp.bufLen >= blockThreshold { if bp.readBlock { bp.wait.Broadcast() } bp.wait.Wait() if bp.closeErr != nil { continue loop0 } } // add new byte slice to the buffers slice and continue writing nextCap := b.Cap() * 2 if nextCap > maxCap { nextCap = maxCap } bp.buf = append(bp.buf, getBuffer(nextCap)) } bp.wait.Broadcast() return written, nil } // CloseWithError causes further reads from a BytesPipe to return immediately. func (bp *BytesPipe) CloseWithError(err error) error { bp.mu.Lock() if err != nil { bp.closeErr = err } else { bp.closeErr = io.EOF } bp.wait.Broadcast() bp.mu.Unlock() return nil } // Close causes further reads from a BytesPipe to return immediately. func (bp *BytesPipe) Close() error { return bp.CloseWithError(nil) } // Read reads bytes from BytesPipe. // Data could be read only once. func (bp *BytesPipe) Read(p []byte) (n int, err error) { bp.mu.Lock() defer bp.mu.Unlock() if bp.bufLen == 0 { if bp.closeErr != nil { return 0, bp.closeErr } bp.readBlock = true bp.wait.Wait() bp.readBlock = false if bp.bufLen == 0 && bp.closeErr != nil { return 0, bp.closeErr } } for bp.bufLen > 0 { b := bp.buf[0] read, _ := b.Read(p) // ignore error since fixedBuffer doesn't really return an error n += read bp.bufLen -= read if b.Len() == 0 { // it's empty so return it to the pool and move to the next one returnBuffer(b) bp.buf[0] = nil bp.buf = bp.buf[1:] } if len(p) == read { break } p = p[read:] } bp.wait.Broadcast() return } func returnBuffer(b *fixedBuffer) { b.Reset() bufPoolsLock.Lock() pool := bufPools[b.Cap()] bufPoolsLock.Unlock() if pool != nil { pool.Put(b) } } func getBuffer(size int) *fixedBuffer { bufPoolsLock.Lock() pool, ok := bufPools[size] if !ok { pool = &sync.Pool{New: func() interface{} { return &fixedBuffer{buf: make([]byte, 0, size)} }} bufPools[size] = pool } bufPoolsLock.Unlock() return pool.Get().(*fixedBuffer) } ================================================ FILE: vendor/github.com/docker/docker/pkg/ioutils/fswriters.go ================================================ package ioutils // import "github.com/docker/docker/pkg/ioutils" import ( "io" "os" "path/filepath" ) // NewAtomicFileWriter returns WriteCloser so that writing to it writes to a // temporary file and closing it atomically changes the temporary file to // destination path. Writing and closing concurrently is not allowed. func NewAtomicFileWriter(filename string, perm os.FileMode) (io.WriteCloser, error) { f, err := os.CreateTemp(filepath.Dir(filename), ".tmp-"+filepath.Base(filename)) if err != nil { return nil, err } abspath, err := filepath.Abs(filename) if err != nil { return nil, err } return &atomicFileWriter{ f: f, fn: abspath, perm: perm, }, nil } // AtomicWriteFile atomically writes data to a file named by filename. func AtomicWriteFile(filename string, data []byte, perm os.FileMode) error { f, err := NewAtomicFileWriter(filename, perm) if err != nil { return err } n, err := f.Write(data) if err == nil && n < len(data) { err = io.ErrShortWrite f.(*atomicFileWriter).writeErr = err } if err1 := f.Close(); err == nil { err = err1 } return err } type atomicFileWriter struct { f *os.File fn string writeErr error perm os.FileMode } func (w *atomicFileWriter) Write(dt []byte) (int, error) { n, err := w.f.Write(dt) if err != nil { w.writeErr = err } return n, err } func (w *atomicFileWriter) Close() (retErr error) { defer func() { if retErr != nil || w.writeErr != nil { os.Remove(w.f.Name()) } }() if err := w.f.Sync(); err != nil { w.f.Close() return err } if err := w.f.Close(); err != nil { return err } if err := os.Chmod(w.f.Name(), w.perm); err != nil { return err } if w.writeErr == nil { return os.Rename(w.f.Name(), w.fn) } return nil } // AtomicWriteSet is used to atomically write a set // of files and ensure they are visible at the same time. // Must be committed to a new directory. type AtomicWriteSet struct { root string } // NewAtomicWriteSet creates a new atomic write set to // atomically create a set of files. The given directory // is used as the base directory for storing files before // commit. If no temporary directory is given the system // default is used. func NewAtomicWriteSet(tmpDir string) (*AtomicWriteSet, error) { td, err := os.MkdirTemp(tmpDir, "write-set-") if err != nil { return nil, err } return &AtomicWriteSet{ root: td, }, nil } // WriteFile writes a file to the set, guaranteeing the file // has been synced. func (ws *AtomicWriteSet) WriteFile(filename string, data []byte, perm os.FileMode) error { f, err := ws.FileWriter(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) if err != nil { return err } n, err := f.Write(data) if err == nil && n < len(data) { err = io.ErrShortWrite } if err1 := f.Close(); err == nil { err = err1 } return err } type syncFileCloser struct { *os.File } func (w syncFileCloser) Close() error { err := w.File.Sync() if err1 := w.File.Close(); err == nil { err = err1 } return err } // FileWriter opens a file writer inside the set. The file // should be synced and closed before calling commit. func (ws *AtomicWriteSet) FileWriter(name string, flag int, perm os.FileMode) (io.WriteCloser, error) { f, err := os.OpenFile(filepath.Join(ws.root, name), flag, perm) if err != nil { return nil, err } return syncFileCloser{f}, nil } // Cancel cancels the set and removes all temporary data // created in the set. func (ws *AtomicWriteSet) Cancel() error { return os.RemoveAll(ws.root) } // Commit moves all created files to the target directory. The // target directory must not exist and the parent of the target // directory must exist. func (ws *AtomicWriteSet) Commit(target string) error { return os.Rename(ws.root, target) } // String returns the location the set is writing to. func (ws *AtomicWriteSet) String() string { return ws.root } ================================================ FILE: vendor/github.com/docker/docker/pkg/ioutils/readers.go ================================================ package ioutils // import "github.com/docker/docker/pkg/ioutils" import ( "context" "io" "runtime/debug" "sync/atomic" // make sure crypto.SHA256, crypto.sha512 and crypto.SHA384 are registered // TODO remove once https://github.com/opencontainers/go-digest/pull/64 is merged. _ "crypto/sha256" _ "crypto/sha512" "github.com/containerd/log" ) // ReadCloserWrapper wraps an io.Reader, and implements an io.ReadCloser // It calls the given callback function when closed. It should be constructed // with NewReadCloserWrapper type ReadCloserWrapper struct { io.Reader closer func() error closed atomic.Bool } // Close calls back the passed closer function func (r *ReadCloserWrapper) Close() error { if !r.closed.CompareAndSwap(false, true) { subsequentCloseWarn("ReadCloserWrapper") return nil } return r.closer() } // NewReadCloserWrapper returns a new io.ReadCloser. func NewReadCloserWrapper(r io.Reader, closer func() error) io.ReadCloser { return &ReadCloserWrapper{ Reader: r, closer: closer, } } type readerErrWrapper struct { reader io.Reader closer func() } func (r *readerErrWrapper) Read(p []byte) (int, error) { n, err := r.reader.Read(p) if err != nil { r.closer() } return n, err } // NewReaderErrWrapper returns a new io.Reader. func NewReaderErrWrapper(r io.Reader, closer func()) io.Reader { return &readerErrWrapper{ reader: r, closer: closer, } } // OnEOFReader wraps an io.ReadCloser and a function // the function will run at the end of file or close the file. type OnEOFReader struct { Rc io.ReadCloser Fn func() } func (r *OnEOFReader) Read(p []byte) (n int, err error) { n, err = r.Rc.Read(p) if err == io.EOF { r.runFunc() } return } // Close closes the file and run the function. func (r *OnEOFReader) Close() error { err := r.Rc.Close() r.runFunc() return err } func (r *OnEOFReader) runFunc() { if fn := r.Fn; fn != nil { fn() r.Fn = nil } } // cancelReadCloser wraps an io.ReadCloser with a context for cancelling read // operations. type cancelReadCloser struct { cancel func() pR *io.PipeReader // Stream to read from pW *io.PipeWriter closed atomic.Bool } // NewCancelReadCloser creates a wrapper that closes the ReadCloser when the // context is cancelled. The returned io.ReadCloser must be closed when it is // no longer needed. func NewCancelReadCloser(ctx context.Context, in io.ReadCloser) io.ReadCloser { pR, pW := io.Pipe() // Create a context used to signal when the pipe is closed doneCtx, cancel := context.WithCancel(context.Background()) p := &cancelReadCloser{ cancel: cancel, pR: pR, pW: pW, } go func() { _, err := io.Copy(pW, in) select { case <-ctx.Done(): // If the context was closed, p.closeWithError // was already called. Calling it again would // change the error that Read returns. default: p.closeWithError(err) } in.Close() }() go func() { for { select { case <-ctx.Done(): p.closeWithError(ctx.Err()) case <-doneCtx.Done(): return } } }() return p } // Read wraps the Read method of the pipe that provides data from the wrapped // ReadCloser. func (p *cancelReadCloser) Read(buf []byte) (n int, err error) { return p.pR.Read(buf) } // closeWithError closes the wrapper and its underlying reader. It will // cause future calls to Read to return err. func (p *cancelReadCloser) closeWithError(err error) { p.pW.CloseWithError(err) p.cancel() } // Close closes the wrapper its underlying reader. It will cause // future calls to Read to return io.EOF. func (p *cancelReadCloser) Close() error { if !p.closed.CompareAndSwap(false, true) { subsequentCloseWarn("cancelReadCloser") return nil } p.closeWithError(io.EOF) return nil } func subsequentCloseWarn(name string) { log.G(context.TODO()).Error("subsequent attempt to close " + name) if log.GetLevel() >= log.DebugLevel { log.G(context.TODO()).Errorf("stack trace: %s", string(debug.Stack())) } } ================================================ FILE: vendor/github.com/docker/docker/pkg/ioutils/writeflusher.go ================================================ package ioutils // import "github.com/docker/docker/pkg/ioutils" import ( "io" "sync" ) // WriteFlusher wraps the Write and Flush operation ensuring that every write // is a flush. In addition, the Close method can be called to intercept // Read/Write calls if the targets lifecycle has already ended. type WriteFlusher struct { w io.Writer flusher flusher flushed chan struct{} flushedOnce sync.Once closed chan struct{} closeLock sync.Mutex } type flusher interface { Flush() } var errWriteFlusherClosed = io.EOF func (wf *WriteFlusher) Write(b []byte) (n int, err error) { select { case <-wf.closed: return 0, errWriteFlusherClosed default: } n, err = wf.w.Write(b) wf.Flush() // every write is a flush. return n, err } // Flush the stream immediately. func (wf *WriteFlusher) Flush() { select { case <-wf.closed: return default: } wf.flushedOnce.Do(func() { close(wf.flushed) }) wf.flusher.Flush() } // Flushed returns the state of flushed. // If it's flushed, return true, or else it return false. func (wf *WriteFlusher) Flushed() bool { // BUG(stevvooe): Remove this method. Its use is inherently racy. Seems to // be used to detect whether or a response code has been issued or not. // Another hook should be used instead. var flushed bool select { case <-wf.flushed: flushed = true default: } return flushed } // Close closes the write flusher, disallowing any further writes to the // target. After the flusher is closed, all calls to write or flush will // result in an error. func (wf *WriteFlusher) Close() error { wf.closeLock.Lock() defer wf.closeLock.Unlock() select { case <-wf.closed: return errWriteFlusherClosed default: close(wf.closed) } return nil } // NewWriteFlusher returns a new WriteFlusher. func NewWriteFlusher(w io.Writer) *WriteFlusher { var fl flusher if f, ok := w.(flusher); ok { fl = f } else { fl = &NopFlusher{} } return &WriteFlusher{w: w, flusher: fl, closed: make(chan struct{}), flushed: make(chan struct{})} } ================================================ FILE: vendor/github.com/docker/docker/pkg/ioutils/writers.go ================================================ package ioutils // import "github.com/docker/docker/pkg/ioutils" import ( "io" "sync/atomic" ) // NopWriter represents a type which write operation is nop. type NopWriter struct{} func (*NopWriter) Write(buf []byte) (int, error) { return len(buf), nil } type nopWriteCloser struct { io.Writer } func (w *nopWriteCloser) Close() error { return nil } // NopWriteCloser returns a nopWriteCloser. func NopWriteCloser(w io.Writer) io.WriteCloser { return &nopWriteCloser{w} } // NopFlusher represents a type which flush operation is nop. type NopFlusher struct{} // Flush is a nop operation. func (f *NopFlusher) Flush() {} type writeCloserWrapper struct { io.Writer closer func() error closed atomic.Bool } func (r *writeCloserWrapper) Close() error { if !r.closed.CompareAndSwap(false, true) { subsequentCloseWarn("WriteCloserWrapper") return nil } return r.closer() } // NewWriteCloserWrapper returns a new io.WriteCloser. func NewWriteCloserWrapper(r io.Writer, closer func() error) io.WriteCloser { return &writeCloserWrapper{ Writer: r, closer: closer, } } // WriteCounter wraps a concrete io.Writer and hold a count of the number // of bytes written to the writer during a "session". // This can be convenient when write return is masked // (e.g., json.Encoder.Encode()) type WriteCounter struct { Count int64 Writer io.Writer } // NewWriteCounter returns a new WriteCounter. func NewWriteCounter(w io.Writer) *WriteCounter { return &WriteCounter{ Writer: w, } } func (wc *WriteCounter) Write(p []byte) (count int, err error) { count, err = wc.Writer.Write(p) wc.Count += int64(count) return } ================================================ FILE: vendor/github.com/docker/docker/pkg/jsonmessage/jsonmessage.go ================================================ package jsonmessage // import "github.com/docker/docker/pkg/jsonmessage" import ( "encoding/json" "fmt" "io" "strings" "time" units "github.com/docker/go-units" "github.com/moby/term" "github.com/morikuni/aec" ) // RFC3339NanoFixed is time.RFC3339Nano with nanoseconds padded using zeros to // ensure the formatted time isalways the same number of characters. const RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00" // JSONError wraps a concrete Code and Message, Code is // an integer error code, Message is the error message. type JSONError struct { Code int `json:"code,omitempty"` Message string `json:"message,omitempty"` } func (e *JSONError) Error() string { return e.Message } // JSONProgress describes a progress message in a JSON stream. type JSONProgress struct { // Current is the current status and value of the progress made towards Total. Current int64 `json:"current,omitempty"` // Total is the end value describing when we made 100% progress for an operation. Total int64 `json:"total,omitempty"` // Start is the initial value for the operation. Start int64 `json:"start,omitempty"` // HideCounts. if true, hides the progress count indicator (xB/yB). HideCounts bool `json:"hidecounts,omitempty"` // Units is the unit to print for progress. It defaults to "bytes" if empty. Units string `json:"units,omitempty"` // terminalFd is the fd of the current terminal, if any. It is used // to get the terminal width. terminalFd uintptr // nowFunc is used to override the current time in tests. nowFunc func() time.Time // winSize is used to override the terminal width in tests. winSize int } func (p *JSONProgress) String() string { var ( width = p.width() pbBox string numbersBox string timeLeftBox string ) if p.Current <= 0 && p.Total <= 0 { return "" } if p.Total <= 0 { switch p.Units { case "": return fmt.Sprintf("%8v", units.HumanSize(float64(p.Current))) default: return fmt.Sprintf("%d %s", p.Current, p.Units) } } percentage := int(float64(p.Current)/float64(p.Total)*100) / 2 if percentage > 50 { percentage = 50 } if width > 110 { // this number can't be negative gh#7136 numSpaces := 0 if 50-percentage > 0 { numSpaces = 50 - percentage } pbBox = fmt.Sprintf("[%s>%s] ", strings.Repeat("=", percentage), strings.Repeat(" ", numSpaces)) } switch { case p.HideCounts: case p.Units == "": // no units, use bytes current := units.HumanSize(float64(p.Current)) total := units.HumanSize(float64(p.Total)) numbersBox = fmt.Sprintf("%8v/%v", current, total) if p.Current > p.Total { // remove total display if the reported current is wonky. numbersBox = fmt.Sprintf("%8v", current) } default: numbersBox = fmt.Sprintf("%d/%d %s", p.Current, p.Total, p.Units) if p.Current > p.Total { // remove total display if the reported current is wonky. numbersBox = fmt.Sprintf("%d %s", p.Current, p.Units) } } if p.Current > 0 && p.Start > 0 && percentage < 50 { fromStart := p.now().Sub(time.Unix(p.Start, 0)) perEntry := fromStart / time.Duration(p.Current) left := time.Duration(p.Total-p.Current) * perEntry left = (left / time.Second) * time.Second if width > 50 { timeLeftBox = " " + left.String() } } return pbBox + numbersBox + timeLeftBox } // now returns the current time in UTC, but can be overridden in tests // by setting JSONProgress.nowFunc to a custom function. func (p *JSONProgress) now() time.Time { if p.nowFunc != nil { return p.nowFunc() } return time.Now().UTC() } // width returns the current terminal's width, but can be overridden // in tests by setting JSONProgress.winSize to a non-zero value. func (p *JSONProgress) width() int { if p.winSize != 0 { return p.winSize } ws, err := term.GetWinsize(p.terminalFd) if err == nil { return int(ws.Width) } return 200 } // JSONMessage defines a message struct. It describes // the created time, where it from, status, ID of the // message. It's used for docker events. type JSONMessage struct { Stream string `json:"stream,omitempty"` Status string `json:"status,omitempty"` Progress *JSONProgress `json:"progressDetail,omitempty"` ProgressMessage string `json:"progress,omitempty"` // deprecated ID string `json:"id,omitempty"` From string `json:"from,omitempty"` Time int64 `json:"time,omitempty"` TimeNano int64 `json:"timeNano,omitempty"` Error *JSONError `json:"errorDetail,omitempty"` ErrorMessage string `json:"error,omitempty"` // deprecated // Aux contains out-of-band data, such as digests for push signing and image id after building. Aux *json.RawMessage `json:"aux,omitempty"` } func clearLine(out io.Writer) { eraseMode := aec.EraseModes.All cl := aec.EraseLine(eraseMode) fmt.Fprint(out, cl) } func cursorUp(out io.Writer, l uint) { fmt.Fprint(out, aec.Up(l)) } func cursorDown(out io.Writer, l uint) { fmt.Fprint(out, aec.Down(l)) } // Display prints the JSONMessage to out. If isTerminal is true, it erases // the entire current line when displaying the progressbar. It returns an // error if the [JSONMessage.Error] field is non-nil. func (jm *JSONMessage) Display(out io.Writer, isTerminal bool) error { if jm.Error != nil { return jm.Error } var endl string if isTerminal && jm.Stream == "" && jm.Progress != nil { clearLine(out) endl = "\r" fmt.Fprint(out, endl) } else if jm.Progress != nil && jm.Progress.String() != "" { // disable progressbar in non-terminal return nil } if jm.TimeNano != 0 { fmt.Fprintf(out, "%s ", time.Unix(0, jm.TimeNano).Format(RFC3339NanoFixed)) } else if jm.Time != 0 { fmt.Fprintf(out, "%s ", time.Unix(jm.Time, 0).Format(RFC3339NanoFixed)) } if jm.ID != "" { fmt.Fprintf(out, "%s: ", jm.ID) } if jm.From != "" { fmt.Fprintf(out, "(from %s) ", jm.From) } if jm.Progress != nil && isTerminal { fmt.Fprintf(out, "%s %s%s", jm.Status, jm.Progress.String(), endl) } else if jm.ProgressMessage != "" { // deprecated fmt.Fprintf(out, "%s %s%s", jm.Status, jm.ProgressMessage, endl) } else if jm.Stream != "" { fmt.Fprintf(out, "%s%s", jm.Stream, endl) } else { fmt.Fprintf(out, "%s%s\n", jm.Status, endl) } return nil } // DisplayJSONMessagesStream reads a JSON message stream from in, and writes // each [JSONMessage] to out. It returns an error if an invalid JSONMessage // is received, or if a JSONMessage containers a non-zero [JSONMessage.Error]. // // Presentation of the JSONMessage depends on whether a terminal is attached, // and on the terminal width. Progress bars ([JSONProgress]) are suppressed // on narrower terminals (< 110 characters). // // - isTerminal describes if out is a terminal, in which case it prints // a newline ("\n") at the end of each line and moves the cursor while // displaying. // - terminalFd is the fd of the current terminal (if any), and used // to get the terminal width. // - auxCallback allows handling the [JSONMessage.Aux] field. It is // called if a JSONMessage contains an Aux field, in which case // DisplayJSONMessagesStream does not present the JSONMessage. func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, isTerminal bool, auxCallback func(JSONMessage)) error { var ( dec = json.NewDecoder(in) ids = make(map[string]uint) ) for { var diff uint var jm JSONMessage if err := dec.Decode(&jm); err != nil { if err == io.EOF { break } return err } if jm.Aux != nil { if auxCallback != nil { auxCallback(jm) } continue } if jm.Progress != nil { jm.Progress.terminalFd = terminalFd } if jm.ID != "" && (jm.Progress != nil || jm.ProgressMessage != "") { line, ok := ids[jm.ID] if !ok { // NOTE: This approach of using len(id) to // figure out the number of lines of history // only works as long as we clear the history // when we output something that's not // accounted for in the map, such as a line // with no ID. line = uint(len(ids)) ids[jm.ID] = line if isTerminal { fmt.Fprintf(out, "\n") } } diff = uint(len(ids)) - line if isTerminal { cursorUp(out, diff) } } else { // When outputting something that isn't progress // output, clear the history of previous lines. We // don't want progress entries from some previous // operation to be updated (for example, pull -a // with multiple tags). ids = make(map[string]uint) } err := jm.Display(out, isTerminal) if jm.ID != "" && isTerminal { cursorDown(out, diff) } if err != nil { return err } } return nil } // Stream is an io.Writer for output with utilities to get the output's file // descriptor and to detect wether it's a terminal. // // it is subset of the streams.Out type in // https://pkg.go.dev/github.com/docker/cli@v20.10.17+incompatible/cli/streams#Out type Stream interface { io.Writer FD() uintptr IsTerminal() bool } // DisplayJSONMessagesToStream prints json messages to the output Stream. It is // used by the Docker CLI to print JSONMessage streams. func DisplayJSONMessagesToStream(in io.Reader, stream Stream, auxCallback func(JSONMessage)) error { return DisplayJSONMessagesStream(in, stream, stream.FD(), stream.IsTerminal(), auxCallback) } ================================================ FILE: vendor/github.com/docker/docker/pkg/longpath/longpath.go ================================================ // Package longpath introduces some constants and helper functions for handling // long paths in Windows. // // Long paths are expected to be prepended with "\\?\" and followed by either a // drive letter, a UNC server\share, or a volume identifier. package longpath // import "github.com/docker/docker/pkg/longpath" import ( "os" "runtime" "strings" ) // Prefix is the longpath prefix for Windows file paths. const Prefix = `\\?\` // AddPrefix adds the Windows long path prefix to the path provided if // it does not already have it. func AddPrefix(path string) string { if !strings.HasPrefix(path, Prefix) { if strings.HasPrefix(path, `\\`) { // This is a UNC path, so we need to add 'UNC' to the path as well. path = Prefix + `UNC` + path[1:] } else { path = Prefix + path } } return path } // MkdirTemp is the equivalent of [os.MkdirTemp], except that on Windows // the result is in Windows longpath format. On Unix systems it is // equivalent to [os.MkdirTemp]. func MkdirTemp(dir, prefix string) (string, error) { tempDir, err := os.MkdirTemp(dir, prefix) if err != nil { return "", err } if runtime.GOOS != "windows" { return tempDir, nil } return AddPrefix(tempDir), nil } ================================================ FILE: vendor/github.com/docker/docker/pkg/pools/pools.go ================================================ // Package pools provides a collection of pools which provide various // data types with buffers. These can be used to lower the number of // memory allocations and reuse buffers. // // New pools should be added to this package to allow them to be // shared across packages. // // Utility functions which operate on pools should be added to this // package to allow them to be reused. package pools // import "github.com/docker/docker/pkg/pools" import ( "bufio" "io" "sync" "github.com/docker/docker/pkg/ioutils" ) const buffer32K = 32 * 1024 var ( // BufioReader32KPool is a pool which returns bufio.Reader with a 32K buffer. BufioReader32KPool = newBufioReaderPoolWithSize(buffer32K) // BufioWriter32KPool is a pool which returns bufio.Writer with a 32K buffer. BufioWriter32KPool = newBufioWriterPoolWithSize(buffer32K) buffer32KPool = newBufferPoolWithSize(buffer32K) ) // BufioReaderPool is a bufio reader that uses sync.Pool. type BufioReaderPool struct { pool sync.Pool } // newBufioReaderPoolWithSize is unexported because new pools should be // added here to be shared where required. func newBufioReaderPoolWithSize(size int) *BufioReaderPool { return &BufioReaderPool{ pool: sync.Pool{ New: func() interface{} { return bufio.NewReaderSize(nil, size) }, }, } } // Get returns a bufio.Reader which reads from r. The buffer size is that of the pool. func (bufPool *BufioReaderPool) Get(r io.Reader) *bufio.Reader { buf := bufPool.pool.Get().(*bufio.Reader) buf.Reset(r) return buf } // Put puts the bufio.Reader back into the pool. func (bufPool *BufioReaderPool) Put(b *bufio.Reader) { b.Reset(nil) bufPool.pool.Put(b) } type bufferPool struct { pool sync.Pool } func newBufferPoolWithSize(size int) *bufferPool { return &bufferPool{ pool: sync.Pool{ New: func() interface{} { s := make([]byte, size); return &s }, }, } } func (bp *bufferPool) Get() *[]byte { return bp.pool.Get().(*[]byte) } func (bp *bufferPool) Put(b *[]byte) { bp.pool.Put(b) } // Copy is a convenience wrapper which uses a buffer to avoid allocation in io.Copy. func Copy(dst io.Writer, src io.Reader) (written int64, err error) { buf := buffer32KPool.Get() written, err = io.CopyBuffer(dst, src, *buf) buffer32KPool.Put(buf) return } // NewReadCloserWrapper returns a wrapper which puts the bufio.Reader back // into the pool and closes the reader if it's an io.ReadCloser. func (bufPool *BufioReaderPool) NewReadCloserWrapper(buf *bufio.Reader, r io.Reader) io.ReadCloser { return ioutils.NewReadCloserWrapper(r, func() error { if readCloser, ok := r.(io.ReadCloser); ok { readCloser.Close() } bufPool.Put(buf) return nil }) } // BufioWriterPool is a bufio writer that uses sync.Pool. type BufioWriterPool struct { pool sync.Pool } // newBufioWriterPoolWithSize is unexported because new pools should be // added here to be shared where required. func newBufioWriterPoolWithSize(size int) *BufioWriterPool { return &BufioWriterPool{ pool: sync.Pool{ New: func() interface{} { return bufio.NewWriterSize(nil, size) }, }, } } // Get returns a bufio.Writer which writes to w. The buffer size is that of the pool. func (bufPool *BufioWriterPool) Get(w io.Writer) *bufio.Writer { buf := bufPool.pool.Get().(*bufio.Writer) buf.Reset(w) return buf } // Put puts the bufio.Writer back into the pool. func (bufPool *BufioWriterPool) Put(b *bufio.Writer) { b.Reset(nil) bufPool.pool.Put(b) } // NewWriteCloserWrapper returns a wrapper which puts the bufio.Writer back // into the pool and closes the writer if it's an io.Writecloser. func (bufPool *BufioWriterPool) NewWriteCloserWrapper(buf *bufio.Writer, w io.Writer) io.WriteCloser { return ioutils.NewWriteCloserWrapper(w, func() error { buf.Flush() if writeCloser, ok := w.(io.WriteCloser); ok { writeCloser.Close() } bufPool.Put(buf) return nil }) } ================================================ FILE: vendor/github.com/docker/docker/pkg/stdcopy/stdcopy.go ================================================ package stdcopy // import "github.com/docker/docker/pkg/stdcopy" import ( "bytes" "encoding/binary" "errors" "fmt" "io" "sync" ) // StdType is the type of standard stream // a writer can multiplex to. type StdType byte const ( // Stdin represents standard input stream type. Stdin StdType = iota // Stdout represents standard output stream type. Stdout // Stderr represents standard error steam type. Stderr // Systemerr represents errors originating from the system that make it // into the multiplexed stream. Systemerr stdWriterPrefixLen = 8 stdWriterFdIndex = 0 stdWriterSizeIndex = 4 startingBufLen = 32*1024 + stdWriterPrefixLen + 1 ) var bufPool = &sync.Pool{New: func() interface{} { return bytes.NewBuffer(nil) }} // stdWriter is wrapper of io.Writer with extra customized info. type stdWriter struct { io.Writer prefix byte } // Write sends the buffer to the underneath writer. // It inserts the prefix header before the buffer, // so stdcopy.StdCopy knows where to multiplex the output. // It makes stdWriter to implement io.Writer. func (w *stdWriter) Write(p []byte) (n int, err error) { if w == nil || w.Writer == nil { return 0, errors.New("Writer not instantiated") } if p == nil { return 0, nil } header := [stdWriterPrefixLen]byte{stdWriterFdIndex: w.prefix} binary.BigEndian.PutUint32(header[stdWriterSizeIndex:], uint32(len(p))) buf := bufPool.Get().(*bytes.Buffer) buf.Write(header[:]) buf.Write(p) n, err = w.Writer.Write(buf.Bytes()) n -= stdWriterPrefixLen if n < 0 { n = 0 } buf.Reset() bufPool.Put(buf) return } // NewStdWriter instantiates a new Writer. // Everything written to it will be encapsulated using a custom format, // and written to the underlying `w` stream. // This allows multiple write streams (e.g. stdout and stderr) to be muxed into a single connection. // `t` indicates the id of the stream to encapsulate. // It can be stdcopy.Stdin, stdcopy.Stdout, stdcopy.Stderr. func NewStdWriter(w io.Writer, t StdType) io.Writer { return &stdWriter{ Writer: w, prefix: byte(t), } } // StdCopy is a modified version of io.Copy. // // StdCopy will demultiplex `src`, assuming that it contains two streams, // previously multiplexed together using a StdWriter instance. // As it reads from `src`, StdCopy will write to `dstout` and `dsterr`. // // StdCopy will read until it hits EOF on `src`. It will then return a nil error. // In other words: if `err` is non nil, it indicates a real underlying error. // // `written` will hold the total number of bytes written to `dstout` and `dsterr`. func StdCopy(dstout, dsterr io.Writer, src io.Reader) (written int64, err error) { var ( buf = make([]byte, startingBufLen) bufLen = len(buf) nr, nw int er, ew error out io.Writer frameSize int ) for { // Make sure we have at least a full header for nr < stdWriterPrefixLen { var nr2 int nr2, er = src.Read(buf[nr:]) nr += nr2 if er == io.EOF { if nr < stdWriterPrefixLen { return written, nil } break } if er != nil { return 0, er } } stream := StdType(buf[stdWriterFdIndex]) // Check the first byte to know where to write switch stream { case Stdin: fallthrough case Stdout: // Write on stdout out = dstout case Stderr: // Write on stderr out = dsterr case Systemerr: // If we're on Systemerr, we won't write anywhere. // NB: if this code changes later, make sure you don't try to write // to outstream if Systemerr is the stream out = nil default: return 0, fmt.Errorf("Unrecognized input header: %d", buf[stdWriterFdIndex]) } // Retrieve the size of the frame frameSize = int(binary.BigEndian.Uint32(buf[stdWriterSizeIndex : stdWriterSizeIndex+4])) // Check if the buffer is big enough to read the frame. // Extend it if necessary. if frameSize+stdWriterPrefixLen > bufLen { buf = append(buf, make([]byte, frameSize+stdWriterPrefixLen-bufLen+1)...) bufLen = len(buf) } // While the amount of bytes read is less than the size of the frame + header, we keep reading for nr < frameSize+stdWriterPrefixLen { var nr2 int nr2, er = src.Read(buf[nr:]) nr += nr2 if er == io.EOF { if nr < frameSize+stdWriterPrefixLen { return written, nil } break } if er != nil { return 0, er } } // we might have an error from the source mixed up in our multiplexed // stream. if we do, return it. if stream == Systemerr { return written, fmt.Errorf("error from daemon in stream: %s", string(buf[stdWriterPrefixLen:frameSize+stdWriterPrefixLen])) } // Write the retrieved frame (without header) nw, ew = out.Write(buf[stdWriterPrefixLen : frameSize+stdWriterPrefixLen]) if ew != nil { return 0, ew } // If the frame has not been fully written: error if nw != frameSize { return 0, io.ErrShortWrite } written += int64(nw) // Move the rest of the buffer to the beginning copy(buf, buf[frameSize+stdWriterPrefixLen:]) // Move the index nr -= frameSize + stdWriterPrefixLen } } ================================================ FILE: vendor/github.com/docker/docker/pkg/system/args_windows.go ================================================ package system // import "github.com/docker/docker/pkg/system" import ( "strings" "golang.org/x/sys/windows" ) // EscapeArgs makes a Windows-style escaped command line from a set of arguments func EscapeArgs(args []string) string { escapedArgs := make([]string, len(args)) for i, a := range args { escapedArgs[i] = windows.EscapeArg(a) } return strings.Join(escapedArgs, " ") } ================================================ FILE: vendor/github.com/docker/docker/pkg/system/chtimes.go ================================================ package system // import "github.com/docker/docker/pkg/system" import ( "os" "syscall" "time" "unsafe" ) // Used by Chtimes var unixEpochTime, unixMaxTime time.Time func init() { unixEpochTime = time.Unix(0, 0) if unsafe.Sizeof(syscall.Timespec{}.Nsec) == 8 { // This is a 64 bit timespec // os.Chtimes limits time to the following // // Note that this intentionally sets nsec (not sec), which sets both sec // and nsec internally in time.Unix(); // https://github.com/golang/go/blob/go1.19.2/src/time/time.go#L1364-L1380 unixMaxTime = time.Unix(0, 1<<63-1) } else { // This is a 32 bit timespec unixMaxTime = time.Unix(1<<31-1, 0) } } // Chtimes changes the access time and modified time of a file at the given path. // If the modified time is prior to the Unix Epoch (unixMinTime), or after the // end of Unix Time (unixEpochTime), os.Chtimes has undefined behavior. In this // case, Chtimes defaults to Unix Epoch, just in case. func Chtimes(name string, atime time.Time, mtime time.Time) error { if atime.Before(unixEpochTime) || atime.After(unixMaxTime) { atime = unixEpochTime } if mtime.Before(unixEpochTime) || mtime.After(unixMaxTime) { mtime = unixEpochTime } if err := os.Chtimes(name, atime, mtime); err != nil { return err } // Take platform specific action for setting create time. return setCTime(name, mtime) } ================================================ FILE: vendor/github.com/docker/docker/pkg/system/chtimes_nowindows.go ================================================ //go:build !windows package system // import "github.com/docker/docker/pkg/system" import ( "time" ) // setCTime will set the create time on a file. On Unix, the create // time is updated as a side effect of setting the modified time, so // no action is required. func setCTime(path string, ctime time.Time) error { return nil } ================================================ FILE: vendor/github.com/docker/docker/pkg/system/chtimes_windows.go ================================================ package system // import "github.com/docker/docker/pkg/system" import ( "time" "golang.org/x/sys/windows" ) // setCTime will set the create time on a file. On Windows, this requires // calling SetFileTime and explicitly including the create time. func setCTime(path string, ctime time.Time) error { pathp, err := windows.UTF16PtrFromString(path) if err != nil { return err } h, err := windows.CreateFile(pathp, windows.FILE_WRITE_ATTRIBUTES, windows.FILE_SHARE_WRITE, nil, windows.OPEN_EXISTING, windows.FILE_FLAG_BACKUP_SEMANTICS, 0) if err != nil { return err } defer windows.Close(h) c := windows.NsecToFiletime(ctime.UnixNano()) return windows.SetFileTime(h, &c, nil, nil) } ================================================ FILE: vendor/github.com/docker/docker/pkg/system/errors.go ================================================ package system // import "github.com/docker/docker/pkg/system" import "errors" // ErrNotSupportedPlatform means the platform is not supported. var ErrNotSupportedPlatform = errors.New("platform and architecture is not supported") ================================================ FILE: vendor/github.com/docker/docker/pkg/system/filesys.go ================================================ package system import ( "os" "path/filepath" "strings" ) // IsAbs is a platform-agnostic wrapper for filepath.IsAbs. // // On Windows, golang filepath.IsAbs does not consider a path \windows\system32 // as absolute as it doesn't start with a drive-letter/colon combination. However, // in docker we need to verify things such as WORKDIR /windows/system32 in // a Dockerfile (which gets translated to \windows\system32 when being processed // by the daemon). This SHOULD be treated as absolute from a docker processing // perspective. func IsAbs(path string) bool { return filepath.IsAbs(path) || strings.HasPrefix(path, string(os.PathSeparator)) } ================================================ FILE: vendor/github.com/docker/docker/pkg/system/filesys_unix.go ================================================ //go:build !windows package system // import "github.com/docker/docker/pkg/system" import "os" // MkdirAllWithACL is a wrapper for os.MkdirAll on unix systems. func MkdirAllWithACL(path string, perm os.FileMode, sddl string) error { return os.MkdirAll(path, perm) } // MkdirAll creates a directory named path along with any necessary parents, // with permission specified by attribute perm for all dir created. func MkdirAll(path string, perm os.FileMode) error { return os.MkdirAll(path, perm) } ================================================ FILE: vendor/github.com/docker/docker/pkg/system/filesys_windows.go ================================================ package system // import "github.com/docker/docker/pkg/system" import ( "os" "regexp" "syscall" "unsafe" "golang.org/x/sys/windows" ) // SddlAdministratorsLocalSystem is local administrators plus NT AUTHORITY\System. const SddlAdministratorsLocalSystem = "D:P(A;OICI;GA;;;BA)(A;OICI;GA;;;SY)" // volumePath is a regular expression to check if a path is a Windows // volume path (e.g., "\\?\Volume{4c1b02c1-d990-11dc-99ae-806e6f6e6963}" // or "\\?\Volume{4c1b02c1-d990-11dc-99ae-806e6f6e6963}\"). var volumePath = regexp.MustCompile(`^\\\\\?\\Volume{[a-z0-9-]+}\\?$`) // MkdirAllWithACL is a custom version of os.MkdirAll modified for use on Windows // so that it is both volume path aware, and can create a directory with // an appropriate SDDL defined ACL. func MkdirAllWithACL(path string, _ os.FileMode, sddl string) error { sa, err := makeSecurityAttributes(sddl) if err != nil { return &os.PathError{Op: "mkdirall", Path: path, Err: err} } return mkdirall(path, sa) } // MkdirAll is a custom version of os.MkdirAll that is volume path aware for // Windows. It can be used as a drop-in replacement for os.MkdirAll. func MkdirAll(path string, _ os.FileMode) error { return mkdirall(path, nil) } // mkdirall is a custom version of os.MkdirAll modified for use on Windows // so that it is both volume path aware, and can create a directory with // a DACL. func mkdirall(path string, perm *windows.SecurityAttributes) error { if volumePath.MatchString(path) { return nil } // The rest of this method is largely copied from os.MkdirAll and should be kept // as-is to ensure compatibility. // Fast path: if we can tell whether path is a directory or file, stop with success or error. dir, err := os.Stat(path) if err == nil { if dir.IsDir() { return nil } return &os.PathError{Op: "mkdir", Path: path, Err: syscall.ENOTDIR} } // Slow path: make sure parent exists and then call Mkdir for path. i := len(path) for i > 0 && os.IsPathSeparator(path[i-1]) { // Skip trailing path separator. i-- } j := i for j > 0 && !os.IsPathSeparator(path[j-1]) { // Scan backward over element. j-- } if j > 1 { // Create parent. err = mkdirall(fixRootDirectory(path[:j-1]), perm) if err != nil { return err } } // Parent now exists; invoke Mkdir and use its result. err = mkdirWithACL(path, perm) if err != nil { // Handle arguments like "foo/." by // double-checking that directory doesn't exist. dir, err1 := os.Lstat(path) if err1 == nil && dir.IsDir() { return nil } return err } return nil } // mkdirWithACL creates a new directory. If there is an error, it will be of // type *PathError. . // // This is a modified and combined version of os.Mkdir and windows.Mkdir // in golang to cater for creating a directory am ACL permitting full // access, with inheritance, to any subfolder/file for Built-in Administrators // and Local System. func mkdirWithACL(name string, sa *windows.SecurityAttributes) error { if sa == nil { return os.Mkdir(name, 0) } namep, err := windows.UTF16PtrFromString(name) if err != nil { return &os.PathError{Op: "mkdir", Path: name, Err: err} } err = windows.CreateDirectory(namep, sa) if err != nil { return &os.PathError{Op: "mkdir", Path: name, Err: err} } return nil } // fixRootDirectory fixes a reference to a drive's root directory to // have the required trailing slash. func fixRootDirectory(p string) string { if len(p) == len(`\\?\c:`) { if os.IsPathSeparator(p[0]) && os.IsPathSeparator(p[1]) && p[2] == '?' && os.IsPathSeparator(p[3]) && p[5] == ':' { return p + `\` } } return p } func makeSecurityAttributes(sddl string) (*windows.SecurityAttributes, error) { var sa windows.SecurityAttributes sa.Length = uint32(unsafe.Sizeof(sa)) sa.InheritHandle = 1 var err error sa.SecurityDescriptor, err = windows.SecurityDescriptorFromString(sddl) if err != nil { return nil, err } return &sa, nil } ================================================ FILE: vendor/github.com/docker/docker/pkg/system/image_os_deprecated.go ================================================ package system import ( "errors" "runtime" "strings" ) // ErrNotSupportedOperatingSystem means the operating system is not supported. // // Deprecated: use [github.com/docker/docker/image.CheckOS] and check the error returned. var ErrNotSupportedOperatingSystem = errors.New("operating system is not supported") // IsOSSupported determines if an operating system is supported by the host. // // Deprecated: use [github.com/docker/docker/image.CheckOS] and check the error returned. func IsOSSupported(os string) bool { return strings.EqualFold(runtime.GOOS, os) } ================================================ FILE: vendor/github.com/docker/docker/pkg/system/init_windows.go ================================================ package system // import "github.com/docker/docker/pkg/system" // containerdRuntimeSupported determines if containerd should be the runtime. var containerdRuntimeSupported = false // InitContainerdRuntime sets whether to use containerd for runtime on Windows. func InitContainerdRuntime(cdPath string) { if len(cdPath) > 0 { containerdRuntimeSupported = true } } // ContainerdRuntimeSupported returns true if the use of containerd runtime is supported. func ContainerdRuntimeSupported() bool { return containerdRuntimeSupported } ================================================ FILE: vendor/github.com/docker/docker/pkg/system/lstat_unix.go ================================================ //go:build !windows package system // import "github.com/docker/docker/pkg/system" import ( "os" "syscall" ) // Lstat takes a path to a file and returns // a system.StatT type pertaining to that file. // // Throws an error if the file does not exist func Lstat(path string) (*StatT, error) { s := &syscall.Stat_t{} if err := syscall.Lstat(path, s); err != nil { return nil, &os.PathError{Op: "Lstat", Path: path, Err: err} } return fromStatT(s) } ================================================ FILE: vendor/github.com/docker/docker/pkg/system/lstat_windows.go ================================================ package system // import "github.com/docker/docker/pkg/system" import "os" // Lstat calls os.Lstat to get a fileinfo interface back. // This is then copied into our own locally defined structure. func Lstat(path string) (*StatT, error) { fi, err := os.Lstat(path) if err != nil { return nil, err } return fromStatT(&fi) } ================================================ FILE: vendor/github.com/docker/docker/pkg/system/mknod.go ================================================ //go:build !windows package system // import "github.com/docker/docker/pkg/system" import ( "golang.org/x/sys/unix" ) // Mkdev is used to build the value of linux devices (in /dev/) which specifies major // and minor number of the newly created device special file. // Linux device nodes are a bit weird due to backwards compat with 16 bit device nodes. // They are, from low to high: the lower 8 bits of the minor, then 12 bits of the major, // then the top 12 bits of the minor. func Mkdev(major int64, minor int64) uint32 { return uint32(unix.Mkdev(uint32(major), uint32(minor))) } ================================================ FILE: vendor/github.com/docker/docker/pkg/system/mknod_freebsd.go ================================================ //go:build freebsd package system // import "github.com/docker/docker/pkg/system" import ( "golang.org/x/sys/unix" ) // Mknod creates a filesystem node (file, device special file or named pipe) named path // with attributes specified by mode and dev. func Mknod(path string, mode uint32, dev int) error { return unix.Mknod(path, mode, uint64(dev)) } ================================================ FILE: vendor/github.com/docker/docker/pkg/system/mknod_unix.go ================================================ //go:build !freebsd && !windows package system // import "github.com/docker/docker/pkg/system" import ( "golang.org/x/sys/unix" ) // Mknod creates a filesystem node (file, device special file or named pipe) named path // with attributes specified by mode and dev. func Mknod(path string, mode uint32, dev int) error { return unix.Mknod(path, mode, dev) } ================================================ FILE: vendor/github.com/docker/docker/pkg/system/stat_bsd.go ================================================ //go:build freebsd || netbsd package system // import "github.com/docker/docker/pkg/system" import "syscall" // fromStatT converts a syscall.Stat_t type to a system.Stat_t type func fromStatT(s *syscall.Stat_t) (*StatT, error) { return &StatT{ size: s.Size, mode: uint32(s.Mode), uid: s.Uid, gid: s.Gid, rdev: uint64(s.Rdev), mtim: s.Mtimespec, }, nil } ================================================ FILE: vendor/github.com/docker/docker/pkg/system/stat_darwin.go ================================================ package system // import "github.com/docker/docker/pkg/system" import "syscall" // fromStatT converts a syscall.Stat_t type to a system.Stat_t type func fromStatT(s *syscall.Stat_t) (*StatT, error) { return &StatT{ size: s.Size, mode: uint32(s.Mode), uid: s.Uid, gid: s.Gid, rdev: uint64(s.Rdev), mtim: s.Mtimespec, }, nil } ================================================ FILE: vendor/github.com/docker/docker/pkg/system/stat_linux.go ================================================ package system // import "github.com/docker/docker/pkg/system" import "syscall" // fromStatT converts a syscall.Stat_t type to a system.Stat_t type func fromStatT(s *syscall.Stat_t) (*StatT, error) { return &StatT{ size: s.Size, mode: s.Mode, uid: s.Uid, gid: s.Gid, // the type is 32bit on mips rdev: uint64(s.Rdev), //nolint: unconvert mtim: s.Mtim, }, nil } // FromStatT converts a syscall.Stat_t type to a system.Stat_t type // This is exposed on Linux as pkg/archive/changes uses it. func FromStatT(s *syscall.Stat_t) (*StatT, error) { return fromStatT(s) } ================================================ FILE: vendor/github.com/docker/docker/pkg/system/stat_openbsd.go ================================================ package system // import "github.com/docker/docker/pkg/system" import "syscall" // fromStatT converts a syscall.Stat_t type to a system.Stat_t type func fromStatT(s *syscall.Stat_t) (*StatT, error) { return &StatT{ size: s.Size, mode: uint32(s.Mode), uid: s.Uid, gid: s.Gid, rdev: uint64(s.Rdev), mtim: s.Mtim, }, nil } ================================================ FILE: vendor/github.com/docker/docker/pkg/system/stat_unix.go ================================================ //go:build !windows package system // import "github.com/docker/docker/pkg/system" import ( "os" "syscall" ) // StatT type contains status of a file. It contains metadata // like permission, owner, group, size, etc about a file. type StatT struct { mode uint32 uid uint32 gid uint32 rdev uint64 size int64 mtim syscall.Timespec } // Mode returns file's permission mode. func (s StatT) Mode() uint32 { return s.mode } // UID returns file's user id of owner. func (s StatT) UID() uint32 { return s.uid } // GID returns file's group id of owner. func (s StatT) GID() uint32 { return s.gid } // Rdev returns file's device ID (if it's special file). func (s StatT) Rdev() uint64 { return s.rdev } // Size returns file's size. func (s StatT) Size() int64 { return s.size } // Mtim returns file's last modification time. func (s StatT) Mtim() syscall.Timespec { return s.mtim } // IsDir reports whether s describes a directory. func (s StatT) IsDir() bool { return s.mode&syscall.S_IFDIR != 0 } // Stat takes a path to a file and returns // a system.StatT type pertaining to that file. // // Throws an error if the file does not exist func Stat(path string) (*StatT, error) { s := &syscall.Stat_t{} if err := syscall.Stat(path, s); err != nil { return nil, &os.PathError{Op: "Stat", Path: path, Err: err} } return fromStatT(s) } ================================================ FILE: vendor/github.com/docker/docker/pkg/system/stat_windows.go ================================================ package system // import "github.com/docker/docker/pkg/system" import ( "os" "time" ) // StatT type contains status of a file. It contains metadata // like permission, size, etc about a file. type StatT struct { mode os.FileMode size int64 mtim time.Time } // Size returns file's size. func (s StatT) Size() int64 { return s.size } // Mode returns file's permission mode. func (s StatT) Mode() os.FileMode { return s.mode } // Mtim returns file's last modification time. func (s StatT) Mtim() time.Time { return s.mtim } // Stat takes a path to a file and returns // a system.StatT type pertaining to that file. // // Throws an error if the file does not exist func Stat(path string) (*StatT, error) { fi, err := os.Stat(path) if err != nil { return nil, err } return fromStatT(&fi) } // fromStatT converts a os.FileInfo type to a system.StatT type func fromStatT(fi *os.FileInfo) (*StatT, error) { return &StatT{ size: (*fi).Size(), mode: (*fi).Mode(), mtim: (*fi).ModTime(), }, nil } ================================================ FILE: vendor/github.com/docker/docker/pkg/system/utimes_unix.go ================================================ //go:build linux || freebsd package system // import "github.com/docker/docker/pkg/system" import ( "syscall" "golang.org/x/sys/unix" ) // LUtimesNano is used to change access and modification time of the specified path. // It's used for symbol link file because unix.UtimesNano doesn't support a NOFOLLOW flag atm. func LUtimesNano(path string, ts []syscall.Timespec) error { uts := []unix.Timespec{ unix.NsecToTimespec(syscall.TimespecToNsec(ts[0])), unix.NsecToTimespec(syscall.TimespecToNsec(ts[1])), } err := unix.UtimesNanoAt(unix.AT_FDCWD, path, uts, unix.AT_SYMLINK_NOFOLLOW) if err != nil && err != unix.ENOSYS { return err } return nil } ================================================ FILE: vendor/github.com/docker/docker/pkg/system/utimes_unsupported.go ================================================ //go:build !linux && !freebsd package system // import "github.com/docker/docker/pkg/system" import "syscall" // LUtimesNano is only supported on linux and freebsd. func LUtimesNano(path string, ts []syscall.Timespec) error { return ErrNotSupportedPlatform } ================================================ FILE: vendor/github.com/docker/docker/pkg/system/xattrs.go ================================================ package system // import "github.com/docker/docker/pkg/system" type XattrError struct { Op string Attr string Path string Err error } func (e *XattrError) Error() string { return e.Op + " " + e.Attr + " " + e.Path + ": " + e.Err.Error() } func (e *XattrError) Unwrap() error { return e.Err } // Timeout reports whether this error represents a timeout. func (e *XattrError) Timeout() bool { t, ok := e.Err.(interface{ Timeout() bool }) return ok && t.Timeout() } ================================================ FILE: vendor/github.com/docker/docker/pkg/system/xattrs_linux.go ================================================ package system // import "github.com/docker/docker/pkg/system" import ( "golang.org/x/sys/unix" ) // Lgetxattr retrieves the value of the extended attribute identified by attr // and associated with the given path in the file system. // It will returns a nil slice and nil error if the xattr is not set. func Lgetxattr(path string, attr string) ([]byte, error) { sysErr := func(err error) ([]byte, error) { return nil, &XattrError{Op: "lgetxattr", Attr: attr, Path: path, Err: err} } // Start with a 128 length byte array dest := make([]byte, 128) sz, errno := unix.Lgetxattr(path, attr, dest) for errno == unix.ERANGE { // Buffer too small, use zero-sized buffer to get the actual size sz, errno = unix.Lgetxattr(path, attr, []byte{}) if errno != nil { return sysErr(errno) } dest = make([]byte, sz) sz, errno = unix.Lgetxattr(path, attr, dest) } switch { case errno == unix.ENODATA: return nil, nil case errno != nil: return sysErr(errno) } return dest[:sz], nil } // Lsetxattr sets the value of the extended attribute identified by attr // and associated with the given path in the file system. func Lsetxattr(path string, attr string, data []byte, flags int) error { err := unix.Lsetxattr(path, attr, data, flags) if err != nil { return &XattrError{Op: "lsetxattr", Attr: attr, Path: path, Err: err} } return nil } ================================================ FILE: vendor/github.com/docker/docker/pkg/system/xattrs_unsupported.go ================================================ //go:build !linux package system // import "github.com/docker/docker/pkg/system" // Lgetxattr is not supported on platforms other than linux. func Lgetxattr(path string, attr string) ([]byte, error) { return nil, ErrNotSupportedPlatform } // Lsetxattr is not supported on platforms other than linux. func Lsetxattr(path string, attr string, data []byte, flags int) error { return ErrNotSupportedPlatform } ================================================ FILE: vendor/github.com/docker/docker-credential-helpers/LICENSE ================================================ Copyright (c) 2016 David Calavera Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/docker/docker-credential-helpers/client/client.go ================================================ package client import ( "bytes" "encoding/json" "fmt" "strings" "github.com/docker/docker-credential-helpers/credentials" ) // isValidCredsMessage checks if 'msg' contains invalid credentials error message. // It returns whether the logs are free of invalid credentials errors and the error if it isn't. // error values can be errCredentialsMissingServerURL or errCredentialsMissingUsername. func isValidCredsMessage(msg string) error { if credentials.IsCredentialsMissingServerURLMessage(msg) { return credentials.NewErrCredentialsMissingServerURL() } if credentials.IsCredentialsMissingUsernameMessage(msg) { return credentials.NewErrCredentialsMissingUsername() } return nil } // Store uses an external program to save credentials. func Store(program ProgramFunc, creds *credentials.Credentials) error { cmd := program("store") buffer := new(bytes.Buffer) if err := json.NewEncoder(buffer).Encode(creds); err != nil { return err } cmd.Input(buffer) out, err := cmd.Output() if err != nil { t := strings.TrimSpace(string(out)) if isValidErr := isValidCredsMessage(t); isValidErr != nil { err = isValidErr } return fmt.Errorf("error storing credentials - err: %v, out: `%s`", err, t) } return nil } // Get executes an external program to get the credentials from a native store. func Get(program ProgramFunc, serverURL string) (*credentials.Credentials, error) { cmd := program("get") cmd.Input(strings.NewReader(serverURL)) out, err := cmd.Output() if err != nil { t := strings.TrimSpace(string(out)) if credentials.IsErrCredentialsNotFoundMessage(t) { return nil, credentials.NewErrCredentialsNotFound() } if isValidErr := isValidCredsMessage(t); isValidErr != nil { err = isValidErr } return nil, fmt.Errorf("error getting credentials - err: %v, out: `%s`", err, t) } resp := &credentials.Credentials{ ServerURL: serverURL, } if err := json.NewDecoder(bytes.NewReader(out)).Decode(resp); err != nil { return nil, err } return resp, nil } // Erase executes a program to remove the server credentials from the native store. func Erase(program ProgramFunc, serverURL string) error { cmd := program("erase") cmd.Input(strings.NewReader(serverURL)) out, err := cmd.Output() if err != nil { t := strings.TrimSpace(string(out)) if isValidErr := isValidCredsMessage(t); isValidErr != nil { err = isValidErr } return fmt.Errorf("error erasing credentials - err: %v, out: `%s`", err, t) } return nil } // List executes a program to list server credentials in the native store. func List(program ProgramFunc) (map[string]string, error) { cmd := program("list") cmd.Input(strings.NewReader("unused")) out, err := cmd.Output() if err != nil { t := strings.TrimSpace(string(out)) if isValidErr := isValidCredsMessage(t); isValidErr != nil { err = isValidErr } return nil, fmt.Errorf("error listing credentials - err: %v, out: `%s`", err, t) } var resp map[string]string if err = json.NewDecoder(bytes.NewReader(out)).Decode(&resp); err != nil { return nil, err } return resp, nil } ================================================ FILE: vendor/github.com/docker/docker-credential-helpers/client/command.go ================================================ package client import ( "fmt" "io" "os" exec "golang.org/x/sys/execabs" ) // Program is an interface to execute external programs. type Program interface { Output() ([]byte, error) Input(in io.Reader) } // ProgramFunc is a type of function that initializes programs based on arguments. type ProgramFunc func(args ...string) Program // NewShellProgramFunc creates programs that are executed in a Shell. func NewShellProgramFunc(name string) ProgramFunc { return NewShellProgramFuncWithEnv(name, nil) } // NewShellProgramFuncWithEnv creates programs that are executed in a Shell with environment variables func NewShellProgramFuncWithEnv(name string, env *map[string]string) ProgramFunc { return func(args ...string) Program { return &Shell{cmd: createProgramCmdRedirectErr(name, args, env)} } } func createProgramCmdRedirectErr(commandName string, args []string, env *map[string]string) *exec.Cmd { programCmd := exec.Command(commandName, args...) programCmd.Env = os.Environ() if env != nil { for k, v := range *env { programCmd.Env = append(programCmd.Env, fmt.Sprintf("%s=%s", k, v)) } } programCmd.Stderr = os.Stderr return programCmd } // Shell invokes shell commands to talk with a remote credentials helper. type Shell struct { cmd *exec.Cmd } // Output returns responses from the remote credentials helper. func (s *Shell) Output() ([]byte, error) { return s.cmd.Output() } // Input sets the input to send to a remote credentials helper. func (s *Shell) Input(in io.Reader) { s.cmd.Stdin = in } ================================================ FILE: vendor/github.com/docker/docker-credential-helpers/credentials/credentials.go ================================================ package credentials import ( "bufio" "bytes" "encoding/json" "fmt" "io" "os" "strings" ) // Credentials holds the information shared between docker and the credentials store. type Credentials struct { ServerURL string Username string Secret string } // isValid checks the integrity of Credentials object such that no credentials lack // a server URL or a username. // It returns whether the credentials are valid and the error if it isn't. // error values can be errCredentialsMissingServerURL or errCredentialsMissingUsername func (c *Credentials) isValid() (bool, error) { if len(c.ServerURL) == 0 { return false, NewErrCredentialsMissingServerURL() } if len(c.Username) == 0 { return false, NewErrCredentialsMissingUsername() } return true, nil } // CredsLabel holds the way Docker credentials should be labeled as such in credentials stores that allow labelling. // That label allows to filter out non-Docker credentials too at lookup/search in macOS keychain, // Windows credentials manager and Linux libsecret. Default value is "Docker Credentials" var CredsLabel = "Docker Credentials" // SetCredsLabel is a simple setter for CredsLabel func SetCredsLabel(label string) { CredsLabel = label } // Serve initializes the credentials helper and parses the action argument. // This function is designed to be called from a command line interface. // It uses os.Args[1] as the key for the action. // It uses os.Stdin as input and os.Stdout as output. // This function terminates the program with os.Exit(1) if there is an error. func Serve(helper Helper) { var err error if len(os.Args) != 2 { err = fmt.Errorf("Usage: %s ", os.Args[0]) } if err == nil { err = HandleCommand(helper, os.Args[1], os.Stdin, os.Stdout) } if err != nil { fmt.Fprintf(os.Stdout, "%v\n", err) os.Exit(1) } } // HandleCommand uses a helper and a key to run a credential action. func HandleCommand(helper Helper, key string, in io.Reader, out io.Writer) error { switch key { case "store": return Store(helper, in) case "get": return Get(helper, in, out) case "erase": return Erase(helper, in) case "list": return List(helper, out) case "version": return PrintVersion(out) } return fmt.Errorf("Unknown credential action `%s`", key) } // Store uses a helper and an input reader to save credentials. // The reader must contain the JSON serialization of a Credentials struct. func Store(helper Helper, reader io.Reader) error { scanner := bufio.NewScanner(reader) buffer := new(bytes.Buffer) for scanner.Scan() { buffer.Write(scanner.Bytes()) } if err := scanner.Err(); err != nil && err != io.EOF { return err } var creds Credentials if err := json.NewDecoder(buffer).Decode(&creds); err != nil { return err } if ok, err := creds.isValid(); !ok { return err } return helper.Add(&creds) } // Get retrieves the credentials for a given server url. // The reader must contain the server URL to search. // The writer is used to write the JSON serialization of the credentials. func Get(helper Helper, reader io.Reader, writer io.Writer) error { scanner := bufio.NewScanner(reader) buffer := new(bytes.Buffer) for scanner.Scan() { buffer.Write(scanner.Bytes()) } if err := scanner.Err(); err != nil && err != io.EOF { return err } serverURL := strings.TrimSpace(buffer.String()) if len(serverURL) == 0 { return NewErrCredentialsMissingServerURL() } username, secret, err := helper.Get(serverURL) if err != nil { return err } resp := Credentials{ ServerURL: serverURL, Username: username, Secret: secret, } buffer.Reset() if err := json.NewEncoder(buffer).Encode(resp); err != nil { return err } fmt.Fprint(writer, buffer.String()) return nil } // Erase removes credentials from the store. // The reader must contain the server URL to remove. func Erase(helper Helper, reader io.Reader) error { scanner := bufio.NewScanner(reader) buffer := new(bytes.Buffer) for scanner.Scan() { buffer.Write(scanner.Bytes()) } if err := scanner.Err(); err != nil && err != io.EOF { return err } serverURL := strings.TrimSpace(buffer.String()) if len(serverURL) == 0 { return NewErrCredentialsMissingServerURL() } return helper.Delete(serverURL) } // List returns all the serverURLs of keys in // the OS store as a list of strings func List(helper Helper, writer io.Writer) error { accts, err := helper.List() if err != nil { return err } return json.NewEncoder(writer).Encode(accts) } // PrintVersion outputs the current version. func PrintVersion(writer io.Writer) error { fmt.Fprintf(writer, "%s (%s) %s\n", Name, Package, Version) return nil } ================================================ FILE: vendor/github.com/docker/docker-credential-helpers/credentials/error.go ================================================ package credentials const ( // ErrCredentialsNotFound standardizes the not found error, so every helper returns // the same message and docker can handle it properly. errCredentialsNotFoundMessage = "credentials not found in native keychain" // ErrCredentialsMissingServerURL and ErrCredentialsMissingUsername standardize // invalid credentials or credentials management operations errCredentialsMissingServerURLMessage = "no credentials server URL" errCredentialsMissingUsernameMessage = "no credentials username" ) // errCredentialsNotFound represents an error // raised when credentials are not in the store. type errCredentialsNotFound struct{} // Error returns the standard error message // for when the credentials are not in the store. func (errCredentialsNotFound) Error() string { return errCredentialsNotFoundMessage } // NewErrCredentialsNotFound creates a new error // for when the credentials are not in the store. func NewErrCredentialsNotFound() error { return errCredentialsNotFound{} } // IsErrCredentialsNotFound returns true if the error // was caused by not having a set of credentials in a store. func IsErrCredentialsNotFound(err error) bool { _, ok := err.(errCredentialsNotFound) return ok } // IsErrCredentialsNotFoundMessage returns true if the error // was caused by not having a set of credentials in a store. // // This function helps to check messages returned by an // external program via its standard output. func IsErrCredentialsNotFoundMessage(err string) bool { return err == errCredentialsNotFoundMessage } // errCredentialsMissingServerURL represents an error raised // when the credentials object has no server URL or when no // server URL is provided to a credentials operation requiring // one. type errCredentialsMissingServerURL struct{} func (errCredentialsMissingServerURL) Error() string { return errCredentialsMissingServerURLMessage } // errCredentialsMissingUsername represents an error raised // when the credentials object has no username or when no // username is provided to a credentials operation requiring // one. type errCredentialsMissingUsername struct{} func (errCredentialsMissingUsername) Error() string { return errCredentialsMissingUsernameMessage } // NewErrCredentialsMissingServerURL creates a new error for // errCredentialsMissingServerURL. func NewErrCredentialsMissingServerURL() error { return errCredentialsMissingServerURL{} } // NewErrCredentialsMissingUsername creates a new error for // errCredentialsMissingUsername. func NewErrCredentialsMissingUsername() error { return errCredentialsMissingUsername{} } // IsCredentialsMissingServerURL returns true if the error // was an errCredentialsMissingServerURL. func IsCredentialsMissingServerURL(err error) bool { _, ok := err.(errCredentialsMissingServerURL) return ok } // IsCredentialsMissingServerURLMessage checks for an // errCredentialsMissingServerURL in the error message. func IsCredentialsMissingServerURLMessage(err string) bool { return err == errCredentialsMissingServerURLMessage } // IsCredentialsMissingUsername returns true if the error // was an errCredentialsMissingUsername. func IsCredentialsMissingUsername(err error) bool { _, ok := err.(errCredentialsMissingUsername) return ok } // IsCredentialsMissingUsernameMessage checks for an // errCredentialsMissingUsername in the error message. func IsCredentialsMissingUsernameMessage(err string) bool { return err == errCredentialsMissingUsernameMessage } ================================================ FILE: vendor/github.com/docker/docker-credential-helpers/credentials/helper.go ================================================ package credentials // Helper is the interface a credentials store helper must implement. type Helper interface { // Add appends credentials to the store. Add(*Credentials) error // Delete removes credentials from the store. Delete(serverURL string) error // Get retrieves credentials from the store. // It returns username and secret as strings. Get(serverURL string) (string, string, error) // List returns the stored serverURLs and their associated usernames. List() (map[string]string, error) } ================================================ FILE: vendor/github.com/docker/docker-credential-helpers/credentials/version.go ================================================ package credentials var ( // Name is filled at linking time Name = "" // Package is filled at linking time Package = "github.com/docker/docker-credential-helpers" // Version holds the complete version number. Filled in at linking time. Version = "v0.0.0+unknown" // Revision is filled with the VCS (e.g. git) revision being used to build // the program at linking time. Revision = "" ) ================================================ FILE: vendor/github.com/docker/go-connections/LICENSE ================================================ Apache License Version 2.0, January 2004 https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS Copyright 2015 Docker, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/docker/go-connections/nat/nat.go ================================================ // Package nat is a convenience package for manipulation of strings describing network ports. package nat import ( "fmt" "net" "strconv" "strings" ) const ( // portSpecTemplate is the expected format for port specifications portSpecTemplate = "ip:hostPort:containerPort" ) // PortBinding represents a binding between a Host IP address and a Host Port type PortBinding struct { // HostIP is the host IP Address HostIP string `json:"HostIp"` // HostPort is the host port number HostPort string } // PortMap is a collection of PortBinding indexed by Port type PortMap map[Port][]PortBinding // PortSet is a collection of structs indexed by Port type PortSet map[Port]struct{} // Port is a string containing port number and protocol in the format "80/tcp" type Port string // NewPort creates a new instance of a Port given a protocol and port number or port range func NewPort(proto, port string) (Port, error) { // Check for parsing issues on "port" now so we can avoid having // to check it later on. portStartInt, portEndInt, err := ParsePortRangeToInt(port) if err != nil { return "", err } if portStartInt == portEndInt { return Port(fmt.Sprintf("%d/%s", portStartInt, proto)), nil } return Port(fmt.Sprintf("%d-%d/%s", portStartInt, portEndInt, proto)), nil } // ParsePort parses the port number string and returns an int func ParsePort(rawPort string) (int, error) { if len(rawPort) == 0 { return 0, nil } port, err := strconv.ParseUint(rawPort, 10, 16) if err != nil { return 0, err } return int(port), nil } // ParsePortRangeToInt parses the port range string and returns start/end ints func ParsePortRangeToInt(rawPort string) (int, int, error) { if len(rawPort) == 0 { return 0, 0, nil } start, end, err := ParsePortRange(rawPort) if err != nil { return 0, 0, err } return int(start), int(end), nil } // Proto returns the protocol of a Port func (p Port) Proto() string { proto, _ := SplitProtoPort(string(p)) return proto } // Port returns the port number of a Port func (p Port) Port() string { _, port := SplitProtoPort(string(p)) return port } // Int returns the port number of a Port as an int func (p Port) Int() int { portStr := p.Port() // We don't need to check for an error because we're going to // assume that any error would have been found, and reported, in NewPort() port, _ := ParsePort(portStr) return port } // Range returns the start/end port numbers of a Port range as ints func (p Port) Range() (int, int, error) { return ParsePortRangeToInt(p.Port()) } // SplitProtoPort splits a port in the format of proto/port func SplitProtoPort(rawPort string) (string, string) { parts := strings.Split(rawPort, "/") l := len(parts) if len(rawPort) == 0 || l == 0 || len(parts[0]) == 0 { return "", "" } if l == 1 { return "tcp", rawPort } if len(parts[1]) == 0 { return "tcp", parts[0] } return parts[1], parts[0] } func validateProto(proto string) bool { for _, availableProto := range []string{"tcp", "udp", "sctp"} { if availableProto == proto { return true } } return false } // ParsePortSpecs receives port specs in the format of ip:public:private/proto and parses // these in to the internal types func ParsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding, error) { var ( exposedPorts = make(map[Port]struct{}, len(ports)) bindings = make(map[Port][]PortBinding) ) for _, rawPort := range ports { portMappings, err := ParsePortSpec(rawPort) if err != nil { return nil, nil, err } for _, portMapping := range portMappings { port := portMapping.Port if _, exists := exposedPorts[port]; !exists { exposedPorts[port] = struct{}{} } bslice, exists := bindings[port] if !exists { bslice = []PortBinding{} } bindings[port] = append(bslice, portMapping.Binding) } } return exposedPorts, bindings, nil } // PortMapping is a data object mapping a Port to a PortBinding type PortMapping struct { Port Port Binding PortBinding } func splitParts(rawport string) (string, string, string) { parts := strings.Split(rawport, ":") n := len(parts) containerport := parts[n-1] switch n { case 1: return "", "", containerport case 2: return "", parts[0], containerport case 3: return parts[0], parts[1], containerport default: return strings.Join(parts[:n-2], ":"), parts[n-2], containerport } } // ParsePortSpec parses a port specification string into a slice of PortMappings func ParsePortSpec(rawPort string) ([]PortMapping, error) { var proto string rawIP, hostPort, containerPort := splitParts(rawPort) proto, containerPort = SplitProtoPort(containerPort) // Strip [] from IPV6 addresses ip, _, err := net.SplitHostPort(rawIP + ":") if err != nil { return nil, fmt.Errorf("Invalid ip address %v: %s", rawIP, err) } if ip != "" && net.ParseIP(ip) == nil { return nil, fmt.Errorf("Invalid ip address: %s", ip) } if containerPort == "" { return nil, fmt.Errorf("No port specified: %s", rawPort) } startPort, endPort, err := ParsePortRange(containerPort) if err != nil { return nil, fmt.Errorf("Invalid containerPort: %s", containerPort) } var startHostPort, endHostPort uint64 = 0, 0 if len(hostPort) > 0 { startHostPort, endHostPort, err = ParsePortRange(hostPort) if err != nil { return nil, fmt.Errorf("Invalid hostPort: %s", hostPort) } } if hostPort != "" && (endPort-startPort) != (endHostPort-startHostPort) { // Allow host port range iff containerPort is not a range. // In this case, use the host port range as the dynamic // host port range to allocate into. if endPort != startPort { return nil, fmt.Errorf("Invalid ranges specified for container and host Ports: %s and %s", containerPort, hostPort) } } if !validateProto(strings.ToLower(proto)) { return nil, fmt.Errorf("Invalid proto: %s", proto) } ports := []PortMapping{} for i := uint64(0); i <= (endPort - startPort); i++ { containerPort = strconv.FormatUint(startPort+i, 10) if len(hostPort) > 0 { hostPort = strconv.FormatUint(startHostPort+i, 10) } // Set hostPort to a range only if there is a single container port // and a dynamic host port. if startPort == endPort && startHostPort != endHostPort { hostPort = fmt.Sprintf("%s-%s", hostPort, strconv.FormatUint(endHostPort, 10)) } port, err := NewPort(strings.ToLower(proto), containerPort) if err != nil { return nil, err } binding := PortBinding{ HostIP: ip, HostPort: hostPort, } ports = append(ports, PortMapping{Port: port, Binding: binding}) } return ports, nil } ================================================ FILE: vendor/github.com/docker/go-connections/nat/parse.go ================================================ package nat import ( "fmt" "strconv" "strings" ) // PartParser parses and validates the specified string (data) using the specified template // e.g. ip:public:private -> 192.168.0.1:80:8000 // DEPRECATED: do not use, this function may be removed in a future version func PartParser(template, data string) (map[string]string, error) { // ip:public:private var ( templateParts = strings.Split(template, ":") parts = strings.Split(data, ":") out = make(map[string]string, len(templateParts)) ) if len(parts) != len(templateParts) { return nil, fmt.Errorf("Invalid format to parse. %s should match template %s", data, template) } for i, t := range templateParts { value := "" if len(parts) > i { value = parts[i] } out[t] = value } return out, nil } // ParsePortRange parses and validates the specified string as a port-range (8000-9000) func ParsePortRange(ports string) (uint64, uint64, error) { if ports == "" { return 0, 0, fmt.Errorf("Empty string specified for ports.") } if !strings.Contains(ports, "-") { start, err := strconv.ParseUint(ports, 10, 16) end := start return start, end, err } parts := strings.Split(ports, "-") start, err := strconv.ParseUint(parts[0], 10, 16) if err != nil { return 0, 0, err } end, err := strconv.ParseUint(parts[1], 10, 16) if err != nil { return 0, 0, err } if end < start { return 0, 0, fmt.Errorf("Invalid range specified for the Port: %s", ports) } return start, end, nil } ================================================ FILE: vendor/github.com/docker/go-connections/nat/sort.go ================================================ package nat import ( "sort" "strings" ) type portSorter struct { ports []Port by func(i, j Port) bool } func (s *portSorter) Len() int { return len(s.ports) } func (s *portSorter) Swap(i, j int) { s.ports[i], s.ports[j] = s.ports[j], s.ports[i] } func (s *portSorter) Less(i, j int) bool { ip := s.ports[i] jp := s.ports[j] return s.by(ip, jp) } // Sort sorts a list of ports using the provided predicate // This function should compare `i` and `j`, returning true if `i` is // considered to be less than `j` func Sort(ports []Port, predicate func(i, j Port) bool) { s := &portSorter{ports, predicate} sort.Sort(s) } type portMapEntry struct { port Port binding PortBinding } type portMapSorter []portMapEntry func (s portMapSorter) Len() int { return len(s) } func (s portMapSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] } // sort the port so that the order is: // 1. port with larger specified bindings // 2. larger port // 3. port with tcp protocol func (s portMapSorter) Less(i, j int) bool { pi, pj := s[i].port, s[j].port hpi, hpj := toInt(s[i].binding.HostPort), toInt(s[j].binding.HostPort) return hpi > hpj || pi.Int() > pj.Int() || (pi.Int() == pj.Int() && strings.ToLower(pi.Proto()) == "tcp") } // SortPortMap sorts the list of ports and their respected mapping. The ports // will explicit HostPort will be placed first. func SortPortMap(ports []Port, bindings PortMap) { s := portMapSorter{} for _, p := range ports { if binding, ok := bindings[p]; ok { for _, b := range binding { s = append(s, portMapEntry{port: p, binding: b}) } bindings[p] = []PortBinding{} } else { s = append(s, portMapEntry{port: p}) } } sort.Sort(s) var ( i int pm = make(map[Port]struct{}) ) // reorder ports for _, entry := range s { if _, ok := pm[entry.port]; !ok { ports[i] = entry.port pm[entry.port] = struct{}{} i++ } // reorder bindings for this port if _, ok := bindings[entry.port]; ok { bindings[entry.port] = append(bindings[entry.port], entry.binding) } } } func toInt(s string) uint64 { i, _, err := ParsePortRange(s) if err != nil { i = 0 } return i } ================================================ FILE: vendor/github.com/docker/go-connections/sockets/README.md ================================================ ================================================ FILE: vendor/github.com/docker/go-connections/sockets/inmem_socket.go ================================================ package sockets import ( "errors" "net" "sync" ) var errClosed = errors.New("use of closed network connection") // InmemSocket implements net.Listener using in-memory only connections. type InmemSocket struct { chConn chan net.Conn chClose chan struct{} addr string mu sync.Mutex } // dummyAddr is used to satisfy net.Addr for the in-mem socket // it is just stored as a string and returns the string for all calls type dummyAddr string // NewInmemSocket creates an in-memory only net.Listener // The addr argument can be any string, but is used to satisfy the `Addr()` part // of the net.Listener interface func NewInmemSocket(addr string, bufSize int) *InmemSocket { return &InmemSocket{ chConn: make(chan net.Conn, bufSize), chClose: make(chan struct{}), addr: addr, } } // Addr returns the socket's addr string to satisfy net.Listener func (s *InmemSocket) Addr() net.Addr { return dummyAddr(s.addr) } // Accept implements the Accept method in the Listener interface; it waits for the next call and returns a generic Conn. func (s *InmemSocket) Accept() (net.Conn, error) { select { case conn := <-s.chConn: return conn, nil case <-s.chClose: return nil, errClosed } } // Close closes the listener. It will be unavailable for use once closed. func (s *InmemSocket) Close() error { s.mu.Lock() defer s.mu.Unlock() select { case <-s.chClose: default: close(s.chClose) } return nil } // Dial is used to establish a connection with the in-mem server func (s *InmemSocket) Dial(network, addr string) (net.Conn, error) { srvConn, clientConn := net.Pipe() select { case s.chConn <- srvConn: case <-s.chClose: return nil, errClosed } return clientConn, nil } // Network returns the addr string, satisfies net.Addr func (a dummyAddr) Network() string { return string(a) } // String returns the string form func (a dummyAddr) String() string { return string(a) } ================================================ FILE: vendor/github.com/docker/go-connections/sockets/proxy.go ================================================ package sockets import ( "net" "net/url" "os" "strings" "golang.org/x/net/proxy" ) // GetProxyEnv allows access to the uppercase and the lowercase forms of // proxy-related variables. See the Go specification for details on these // variables. https://golang.org/pkg/net/http/ func GetProxyEnv(key string) string { proxyValue := os.Getenv(strings.ToUpper(key)) if proxyValue == "" { return os.Getenv(strings.ToLower(key)) } return proxyValue } // DialerFromEnvironment takes in a "direct" *net.Dialer and returns a // proxy.Dialer which will route the connections through the proxy using the // given dialer. func DialerFromEnvironment(direct *net.Dialer) (proxy.Dialer, error) { allProxy := GetProxyEnv("all_proxy") if len(allProxy) == 0 { return direct, nil } proxyURL, err := url.Parse(allProxy) if err != nil { return direct, err } proxyFromURL, err := proxy.FromURL(proxyURL, direct) if err != nil { return direct, err } noProxy := GetProxyEnv("no_proxy") if len(noProxy) == 0 { return proxyFromURL, nil } perHost := proxy.NewPerHost(proxyFromURL, direct) perHost.AddFromString(noProxy) return perHost, nil } ================================================ FILE: vendor/github.com/docker/go-connections/sockets/sockets.go ================================================ // Package sockets provides helper functions to create and configure Unix or TCP sockets. package sockets import ( "errors" "net" "net/http" "time" ) // Why 32? See https://github.com/docker/docker/pull/8035. const defaultTimeout = 32 * time.Second // ErrProtocolNotAvailable is returned when a given transport protocol is not provided by the operating system. var ErrProtocolNotAvailable = errors.New("protocol not available") // ConfigureTransport configures the specified Transport according to the // specified proto and addr. // If the proto is unix (using a unix socket to communicate) or npipe the // compression is disabled. func ConfigureTransport(tr *http.Transport, proto, addr string) error { switch proto { case "unix": return configureUnixTransport(tr, proto, addr) case "npipe": return configureNpipeTransport(tr, proto, addr) default: tr.Proxy = http.ProxyFromEnvironment dialer, err := DialerFromEnvironment(&net.Dialer{ Timeout: defaultTimeout, }) if err != nil { return err } tr.Dial = dialer.Dial } return nil } ================================================ FILE: vendor/github.com/docker/go-connections/sockets/sockets_unix.go ================================================ // +build !windows package sockets import ( "fmt" "net" "net/http" "syscall" "time" ) const maxUnixSocketPathSize = len(syscall.RawSockaddrUnix{}.Path) func configureUnixTransport(tr *http.Transport, proto, addr string) error { if len(addr) > maxUnixSocketPathSize { return fmt.Errorf("Unix socket path %q is too long", addr) } // No need for compression in local communications. tr.DisableCompression = true tr.Dial = func(_, _ string) (net.Conn, error) { return net.DialTimeout(proto, addr, defaultTimeout) } return nil } func configureNpipeTransport(tr *http.Transport, proto, addr string) error { return ErrProtocolNotAvailable } // DialPipe connects to a Windows named pipe. // This is not supported on other OSes. func DialPipe(_ string, _ time.Duration) (net.Conn, error) { return nil, syscall.EAFNOSUPPORT } ================================================ FILE: vendor/github.com/docker/go-connections/sockets/sockets_windows.go ================================================ package sockets import ( "net" "net/http" "time" "github.com/Microsoft/go-winio" ) func configureUnixTransport(tr *http.Transport, proto, addr string) error { return ErrProtocolNotAvailable } func configureNpipeTransport(tr *http.Transport, proto, addr string) error { // No need for compression in local communications. tr.DisableCompression = true tr.Dial = func(_, _ string) (net.Conn, error) { return DialPipe(addr, defaultTimeout) } return nil } // DialPipe connects to a Windows named pipe. func DialPipe(addr string, timeout time.Duration) (net.Conn, error) { return winio.DialPipe(addr, &timeout) } ================================================ FILE: vendor/github.com/docker/go-connections/sockets/tcp_socket.go ================================================ // Package sockets provides helper functions to create and configure Unix or TCP sockets. package sockets import ( "crypto/tls" "net" ) // NewTCPSocket creates a TCP socket listener with the specified address and // the specified tls configuration. If TLSConfig is set, will encapsulate the // TCP listener inside a TLS one. func NewTCPSocket(addr string, tlsConfig *tls.Config) (net.Listener, error) { l, err := net.Listen("tcp", addr) if err != nil { return nil, err } if tlsConfig != nil { tlsConfig.NextProtos = []string{"http/1.1"} l = tls.NewListener(l, tlsConfig) } return l, nil } ================================================ FILE: vendor/github.com/docker/go-connections/sockets/unix_socket.go ================================================ // +build !windows package sockets import ( "net" "os" "syscall" ) // NewUnixSocket creates a unix socket with the specified path and group. func NewUnixSocket(path string, gid int) (net.Listener, error) { if err := syscall.Unlink(path); err != nil && !os.IsNotExist(err) { return nil, err } mask := syscall.Umask(0777) defer syscall.Umask(mask) l, err := net.Listen("unix", path) if err != nil { return nil, err } if err := os.Chown(path, 0, gid); err != nil { l.Close() return nil, err } if err := os.Chmod(path, 0660); err != nil { l.Close() return nil, err } return l, nil } ================================================ FILE: vendor/github.com/docker/go-connections/tlsconfig/certpool_go17.go ================================================ // +build go1.7 package tlsconfig import ( "crypto/x509" "runtime" ) // SystemCertPool returns a copy of the system cert pool, // returns an error if failed to load or empty pool on windows. func SystemCertPool() (*x509.CertPool, error) { certpool, err := x509.SystemCertPool() if err != nil && runtime.GOOS == "windows" { return x509.NewCertPool(), nil } return certpool, err } ================================================ FILE: vendor/github.com/docker/go-connections/tlsconfig/certpool_other.go ================================================ // +build !go1.7 package tlsconfig import ( "crypto/x509" ) // SystemCertPool returns an new empty cert pool, // accessing system cert pool is supported in go 1.7 func SystemCertPool() (*x509.CertPool, error) { return x509.NewCertPool(), nil } ================================================ FILE: vendor/github.com/docker/go-connections/tlsconfig/config.go ================================================ // Package tlsconfig provides primitives to retrieve secure-enough TLS configurations for both clients and servers. // // As a reminder from https://golang.org/pkg/crypto/tls/#Config: // A Config structure is used to configure a TLS client or server. After one has been passed to a TLS function it must not be modified. // A Config may be reused; the tls package will also not modify it. package tlsconfig import ( "crypto/tls" "crypto/x509" "encoding/pem" "fmt" "io/ioutil" "os" "github.com/pkg/errors" ) // Options represents the information needed to create client and server TLS configurations. type Options struct { CAFile string // If either CertFile or KeyFile is empty, Client() will not load them // preventing the client from authenticating to the server. // However, Server() requires them and will error out if they are empty. CertFile string KeyFile string // client-only option InsecureSkipVerify bool // server-only option ClientAuth tls.ClientAuthType // If ExclusiveRootPools is set, then if a CA file is provided, the root pool used for TLS // creds will include exclusively the roots in that CA file. If no CA file is provided, // the system pool will be used. ExclusiveRootPools bool MinVersion uint16 // If Passphrase is set, it will be used to decrypt a TLS private key // if the key is encrypted Passphrase string } // Extra (server-side) accepted CBC cipher suites - will phase out in the future var acceptedCBCCiphers = []uint16{ tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, } // DefaultServerAcceptedCiphers should be uses by code which already has a crypto/tls // options struct but wants to use a commonly accepted set of TLS cipher suites, with // known weak algorithms removed. var DefaultServerAcceptedCiphers = append(clientCipherSuites, acceptedCBCCiphers...) // allTLSVersions lists all the TLS versions and is used by the code that validates // a uint16 value as a TLS version. var allTLSVersions = map[uint16]struct{}{ tls.VersionSSL30: {}, tls.VersionTLS10: {}, tls.VersionTLS11: {}, tls.VersionTLS12: {}, } // ServerDefault returns a secure-enough TLS configuration for the server TLS configuration. func ServerDefault(ops ...func(*tls.Config)) *tls.Config { tlsconfig := &tls.Config{ // Avoid fallback by default to SSL protocols < TLS1.2 MinVersion: tls.VersionTLS12, PreferServerCipherSuites: true, CipherSuites: DefaultServerAcceptedCiphers, } for _, op := range ops { op(tlsconfig) } return tlsconfig } // ClientDefault returns a secure-enough TLS configuration for the client TLS configuration. func ClientDefault(ops ...func(*tls.Config)) *tls.Config { tlsconfig := &tls.Config{ // Prefer TLS1.2 as the client minimum MinVersion: tls.VersionTLS12, CipherSuites: clientCipherSuites, } for _, op := range ops { op(tlsconfig) } return tlsconfig } // certPool returns an X.509 certificate pool from `caFile`, the certificate file. func certPool(caFile string, exclusivePool bool) (*x509.CertPool, error) { // If we should verify the server, we need to load a trusted ca var ( certPool *x509.CertPool err error ) if exclusivePool { certPool = x509.NewCertPool() } else { certPool, err = SystemCertPool() if err != nil { return nil, fmt.Errorf("failed to read system certificates: %v", err) } } pem, err := ioutil.ReadFile(caFile) if err != nil { return nil, fmt.Errorf("could not read CA certificate %q: %v", caFile, err) } if !certPool.AppendCertsFromPEM(pem) { return nil, fmt.Errorf("failed to append certificates from PEM file: %q", caFile) } return certPool, nil } // isValidMinVersion checks that the input value is a valid tls minimum version func isValidMinVersion(version uint16) bool { _, ok := allTLSVersions[version] return ok } // adjustMinVersion sets the MinVersion on `config`, the input configuration. // It assumes the current MinVersion on the `config` is the lowest allowed. func adjustMinVersion(options Options, config *tls.Config) error { if options.MinVersion > 0 { if !isValidMinVersion(options.MinVersion) { return fmt.Errorf("Invalid minimum TLS version: %x", options.MinVersion) } if options.MinVersion < config.MinVersion { return fmt.Errorf("Requested minimum TLS version is too low. Should be at-least: %x", config.MinVersion) } config.MinVersion = options.MinVersion } return nil } // IsErrEncryptedKey returns true if the 'err' is an error of incorrect // password when tryin to decrypt a TLS private key func IsErrEncryptedKey(err error) bool { return errors.Cause(err) == x509.IncorrectPasswordError } // getPrivateKey returns the private key in 'keyBytes', in PEM-encoded format. // If the private key is encrypted, 'passphrase' is used to decrypted the // private key. func getPrivateKey(keyBytes []byte, passphrase string) ([]byte, error) { // this section makes some small changes to code from notary/tuf/utils/x509.go pemBlock, _ := pem.Decode(keyBytes) if pemBlock == nil { return nil, fmt.Errorf("no valid private key found") } var err error if x509.IsEncryptedPEMBlock(pemBlock) { keyBytes, err = x509.DecryptPEMBlock(pemBlock, []byte(passphrase)) if err != nil { return nil, errors.Wrap(err, "private key is encrypted, but could not decrypt it") } keyBytes = pem.EncodeToMemory(&pem.Block{Type: pemBlock.Type, Bytes: keyBytes}) } return keyBytes, nil } // getCert returns a Certificate from the CertFile and KeyFile in 'options', // if the key is encrypted, the Passphrase in 'options' will be used to // decrypt it. func getCert(options Options) ([]tls.Certificate, error) { if options.CertFile == "" && options.KeyFile == "" { return nil, nil } errMessage := "Could not load X509 key pair" cert, err := ioutil.ReadFile(options.CertFile) if err != nil { return nil, errors.Wrap(err, errMessage) } prKeyBytes, err := ioutil.ReadFile(options.KeyFile) if err != nil { return nil, errors.Wrap(err, errMessage) } prKeyBytes, err = getPrivateKey(prKeyBytes, options.Passphrase) if err != nil { return nil, errors.Wrap(err, errMessage) } tlsCert, err := tls.X509KeyPair(cert, prKeyBytes) if err != nil { return nil, errors.Wrap(err, errMessage) } return []tls.Certificate{tlsCert}, nil } // Client returns a TLS configuration meant to be used by a client. func Client(options Options) (*tls.Config, error) { tlsConfig := ClientDefault() tlsConfig.InsecureSkipVerify = options.InsecureSkipVerify if !options.InsecureSkipVerify && options.CAFile != "" { CAs, err := certPool(options.CAFile, options.ExclusiveRootPools) if err != nil { return nil, err } tlsConfig.RootCAs = CAs } tlsCerts, err := getCert(options) if err != nil { return nil, err } tlsConfig.Certificates = tlsCerts if err := adjustMinVersion(options, tlsConfig); err != nil { return nil, err } return tlsConfig, nil } // Server returns a TLS configuration meant to be used by a server. func Server(options Options) (*tls.Config, error) { tlsConfig := ServerDefault() tlsConfig.ClientAuth = options.ClientAuth tlsCert, err := tls.LoadX509KeyPair(options.CertFile, options.KeyFile) if err != nil { if os.IsNotExist(err) { return nil, fmt.Errorf("Could not load X509 key pair (cert: %q, key: %q): %v", options.CertFile, options.KeyFile, err) } return nil, fmt.Errorf("Error reading X509 key pair (cert: %q, key: %q): %v. Make sure the key is not encrypted.", options.CertFile, options.KeyFile, err) } tlsConfig.Certificates = []tls.Certificate{tlsCert} if options.ClientAuth >= tls.VerifyClientCertIfGiven && options.CAFile != "" { CAs, err := certPool(options.CAFile, options.ExclusiveRootPools) if err != nil { return nil, err } tlsConfig.ClientCAs = CAs } if err := adjustMinVersion(options, tlsConfig); err != nil { return nil, err } return tlsConfig, nil } ================================================ FILE: vendor/github.com/docker/go-connections/tlsconfig/config_client_ciphers.go ================================================ // +build go1.5 // Package tlsconfig provides primitives to retrieve secure-enough TLS configurations for both clients and servers. // package tlsconfig import ( "crypto/tls" ) // Client TLS cipher suites (dropping CBC ciphers for client preferred suite set) var clientCipherSuites = []uint16{ tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, } ================================================ FILE: vendor/github.com/docker/go-connections/tlsconfig/config_legacy_client_ciphers.go ================================================ // +build !go1.5 // Package tlsconfig provides primitives to retrieve secure-enough TLS configurations for both clients and servers. // package tlsconfig import ( "crypto/tls" ) // Client TLS cipher suites (dropping CBC ciphers for client preferred suite set) var clientCipherSuites = []uint16{ tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, } ================================================ FILE: vendor/github.com/docker/go-units/CONTRIBUTING.md ================================================ # Contributing to go-units Want to hack on go-units? Awesome! Here are instructions to get you started. go-units is a part of the [Docker](https://www.docker.com) project, and follows the same rules and principles. If you're already familiar with the way Docker does things, you'll feel right at home. Otherwise, go read Docker's [contributions guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md), [issue triaging](https://github.com/docker/docker/blob/master/project/ISSUE-TRIAGE.md), [review process](https://github.com/docker/docker/blob/master/project/REVIEWING.md) and [branches and tags](https://github.com/docker/docker/blob/master/project/BRANCHES-AND-TAGS.md). ### Sign your work The sign-off is a simple line at the end of the explanation for the patch. Your signature certifies that you wrote the patch or otherwise have the right to pass it on as an open-source patch. The rules are pretty simple: if you can certify the below (from [developercertificate.org](http://developercertificate.org/)): ``` Developer Certificate of Origin Version 1.1 Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 660 York Street, Suite 102, San Francisco, CA 94110 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. ``` Then you just add a line to every git commit message: Signed-off-by: Joe Smith Use your real name (sorry, no pseudonyms or anonymous contributions.) If you set your `user.name` and `user.email` git configs, you can sign your commit automatically with `git commit -s`. ================================================ FILE: vendor/github.com/docker/go-units/LICENSE ================================================ Apache License Version 2.0, January 2004 https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS Copyright 2015 Docker, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/docker/go-units/MAINTAINERS ================================================ # go-units maintainers file # # This file describes who runs the docker/go-units project and how. # This is a living document - if you see something out of date or missing, speak up! # # It is structured to be consumable by both humans and programs. # To extract its contents programmatically, use any TOML-compliant parser. # # This file is compiled into the MAINTAINERS file in docker/opensource. # [Org] [Org."Core maintainers"] people = [ "akihirosuda", "dnephin", "thajeztah", "vdemeester", ] [people] # A reference list of all people associated with the project. # All other sections should refer to people by their canonical key # in the people section. # ADD YOURSELF HERE IN ALPHABETICAL ORDER [people.akihirosuda] Name = "Akihiro Suda" Email = "akihiro.suda.cz@hco.ntt.co.jp" GitHub = "AkihiroSuda" [people.dnephin] Name = "Daniel Nephin" Email = "dnephin@gmail.com" GitHub = "dnephin" [people.thajeztah] Name = "Sebastiaan van Stijn" Email = "github@gone.nl" GitHub = "thaJeztah" [people.vdemeester] Name = "Vincent Demeester" Email = "vincent@sbr.pm" GitHub = "vdemeester" ================================================ FILE: vendor/github.com/docker/go-units/README.md ================================================ [![GoDoc](https://godoc.org/github.com/docker/go-units?status.svg)](https://godoc.org/github.com/docker/go-units) # Introduction go-units is a library to transform human friendly measurements into machine friendly values. ## Usage See the [docs in godoc](https://godoc.org/github.com/docker/go-units) for examples and documentation. ## Copyright and license Copyright © 2015 Docker, Inc. go-units is licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for the full text of the license. ================================================ FILE: vendor/github.com/docker/go-units/circle.yml ================================================ dependencies: post: # install golint - go get golang.org/x/lint/golint test: pre: # run analysis before tests - go vet ./... - test -z "$(golint ./... | tee /dev/stderr)" - test -z "$(gofmt -s -l . | tee /dev/stderr)" ================================================ FILE: vendor/github.com/docker/go-units/duration.go ================================================ // Package units provides helper function to parse and print size and time units // in human-readable format. package units import ( "fmt" "time" ) // HumanDuration returns a human-readable approximation of a duration // (eg. "About a minute", "4 hours ago", etc.). func HumanDuration(d time.Duration) string { if seconds := int(d.Seconds()); seconds < 1 { return "Less than a second" } else if seconds == 1 { return "1 second" } else if seconds < 60 { return fmt.Sprintf("%d seconds", seconds) } else if minutes := int(d.Minutes()); minutes == 1 { return "About a minute" } else if minutes < 60 { return fmt.Sprintf("%d minutes", minutes) } else if hours := int(d.Hours() + 0.5); hours == 1 { return "About an hour" } else if hours < 48 { return fmt.Sprintf("%d hours", hours) } else if hours < 24*7*2 { return fmt.Sprintf("%d days", hours/24) } else if hours < 24*30*2 { return fmt.Sprintf("%d weeks", hours/24/7) } else if hours < 24*365*2 { return fmt.Sprintf("%d months", hours/24/30) } return fmt.Sprintf("%d years", int(d.Hours())/24/365) } ================================================ FILE: vendor/github.com/docker/go-units/size.go ================================================ package units import ( "fmt" "strconv" "strings" ) // See: http://en.wikipedia.org/wiki/Binary_prefix const ( // Decimal KB = 1000 MB = 1000 * KB GB = 1000 * MB TB = 1000 * GB PB = 1000 * TB // Binary KiB = 1024 MiB = 1024 * KiB GiB = 1024 * MiB TiB = 1024 * GiB PiB = 1024 * TiB ) type unitMap map[byte]int64 var ( decimalMap = unitMap{'k': KB, 'm': MB, 'g': GB, 't': TB, 'p': PB} binaryMap = unitMap{'k': KiB, 'm': MiB, 'g': GiB, 't': TiB, 'p': PiB} ) var ( decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} ) func getSizeAndUnit(size float64, base float64, _map []string) (float64, string) { i := 0 unitsLimit := len(_map) - 1 for size >= base && i < unitsLimit { size = size / base i++ } return size, _map[i] } // CustomSize returns a human-readable approximation of a size // using custom format. func CustomSize(format string, size float64, base float64, _map []string) string { size, unit := getSizeAndUnit(size, base, _map) return fmt.Sprintf(format, size, unit) } // HumanSizeWithPrecision allows the size to be in any precision, // instead of 4 digit precision used in units.HumanSize. func HumanSizeWithPrecision(size float64, precision int) string { size, unit := getSizeAndUnit(size, 1000.0, decimapAbbrs) return fmt.Sprintf("%.*g%s", precision, size, unit) } // HumanSize returns a human-readable approximation of a size // capped at 4 valid numbers (eg. "2.746 MB", "796 KB"). func HumanSize(size float64) string { return HumanSizeWithPrecision(size, 4) } // BytesSize returns a human-readable size in bytes, kibibytes, // mebibytes, gibibytes, or tebibytes (eg. "44kiB", "17MiB"). func BytesSize(size float64) string { return CustomSize("%.4g%s", size, 1024.0, binaryAbbrs) } // FromHumanSize returns an integer from a human-readable specification of a // size using SI standard (eg. "44kB", "17MB"). func FromHumanSize(size string) (int64, error) { return parseSize(size, decimalMap) } // RAMInBytes parses a human-readable string representing an amount of RAM // in bytes, kibibytes, mebibytes, gibibytes, or tebibytes and // returns the number of bytes, or -1 if the string is unparseable. // Units are case-insensitive, and the 'b' suffix is optional. func RAMInBytes(size string) (int64, error) { return parseSize(size, binaryMap) } // Parses the human-readable size string into the amount it represents. func parseSize(sizeStr string, uMap unitMap) (int64, error) { // TODO: rewrite to use strings.Cut if there's a space // once Go < 1.18 is deprecated. sep := strings.LastIndexAny(sizeStr, "01234567890. ") if sep == -1 { // There should be at least a digit. return -1, fmt.Errorf("invalid size: '%s'", sizeStr) } var num, sfx string if sizeStr[sep] != ' ' { num = sizeStr[:sep+1] sfx = sizeStr[sep+1:] } else { // Omit the space separator. num = sizeStr[:sep] sfx = sizeStr[sep+1:] } size, err := strconv.ParseFloat(num, 64) if err != nil { return -1, err } // Backward compatibility: reject negative sizes. if size < 0 { return -1, fmt.Errorf("invalid size: '%s'", sizeStr) } if len(sfx) == 0 { return int64(size), nil } // Process the suffix. if len(sfx) > 3 { // Too long. goto badSuffix } sfx = strings.ToLower(sfx) // Trivial case: b suffix. if sfx[0] == 'b' { if len(sfx) > 1 { // no extra characters allowed after b. goto badSuffix } return int64(size), nil } // A suffix from the map. if mul, ok := uMap[sfx[0]]; ok { size *= float64(mul) } else { goto badSuffix } // The suffix may have extra "b" or "ib" (e.g. KiB or MB). switch { case len(sfx) == 2 && sfx[1] != 'b': goto badSuffix case len(sfx) == 3 && sfx[1:] != "ib": goto badSuffix } return int64(size), nil badSuffix: return -1, fmt.Errorf("invalid suffix: '%s'", sfx) } ================================================ FILE: vendor/github.com/docker/go-units/ulimit.go ================================================ package units import ( "fmt" "strconv" "strings" ) // Ulimit is a human friendly version of Rlimit. type Ulimit struct { Name string Hard int64 Soft int64 } // Rlimit specifies the resource limits, such as max open files. type Rlimit struct { Type int `json:"type,omitempty"` Hard uint64 `json:"hard,omitempty"` Soft uint64 `json:"soft,omitempty"` } const ( // magic numbers for making the syscall // some of these are defined in the syscall package, but not all. // Also since Windows client doesn't get access to the syscall package, need to // define these here rlimitAs = 9 rlimitCore = 4 rlimitCPU = 0 rlimitData = 2 rlimitFsize = 1 rlimitLocks = 10 rlimitMemlock = 8 rlimitMsgqueue = 12 rlimitNice = 13 rlimitNofile = 7 rlimitNproc = 6 rlimitRss = 5 rlimitRtprio = 14 rlimitRttime = 15 rlimitSigpending = 11 rlimitStack = 3 ) var ulimitNameMapping = map[string]int{ //"as": rlimitAs, // Disabled since this doesn't seem usable with the way Docker inits a container. "core": rlimitCore, "cpu": rlimitCPU, "data": rlimitData, "fsize": rlimitFsize, "locks": rlimitLocks, "memlock": rlimitMemlock, "msgqueue": rlimitMsgqueue, "nice": rlimitNice, "nofile": rlimitNofile, "nproc": rlimitNproc, "rss": rlimitRss, "rtprio": rlimitRtprio, "rttime": rlimitRttime, "sigpending": rlimitSigpending, "stack": rlimitStack, } // ParseUlimit parses and returns a Ulimit from the specified string. func ParseUlimit(val string) (*Ulimit, error) { parts := strings.SplitN(val, "=", 2) if len(parts) != 2 { return nil, fmt.Errorf("invalid ulimit argument: %s", val) } if _, exists := ulimitNameMapping[parts[0]]; !exists { return nil, fmt.Errorf("invalid ulimit type: %s", parts[0]) } var ( soft int64 hard = &soft // default to soft in case no hard was set temp int64 err error ) switch limitVals := strings.Split(parts[1], ":"); len(limitVals) { case 2: temp, err = strconv.ParseInt(limitVals[1], 10, 64) if err != nil { return nil, err } hard = &temp fallthrough case 1: soft, err = strconv.ParseInt(limitVals[0], 10, 64) if err != nil { return nil, err } default: return nil, fmt.Errorf("too many limit value arguments - %s, can only have up to two, `soft[:hard]`", parts[1]) } if *hard != -1 { if soft == -1 { return nil, fmt.Errorf("ulimit soft limit must be less than or equal to hard limit: soft: -1 (unlimited), hard: %d", *hard) } if soft > *hard { return nil, fmt.Errorf("ulimit soft limit must be less than or equal to hard limit: %d > %d", soft, *hard) } } return &Ulimit{Name: parts[0], Soft: soft, Hard: *hard}, nil } // GetRlimit returns the RLimit corresponding to Ulimit. func (u *Ulimit) GetRlimit() (*Rlimit, error) { t, exists := ulimitNameMapping[u.Name] if !exists { return nil, fmt.Errorf("invalid ulimit name %s", u.Name) } return &Rlimit{Type: t, Soft: uint64(u.Soft), Hard: uint64(u.Hard)}, nil } func (u *Ulimit) String() string { return fmt.Sprintf("%s=%d:%d", u.Name, u.Soft, u.Hard) } ================================================ FILE: vendor/github.com/dsnet/compress/.travis.yml ================================================ sudo: false language: go before_install: - curl -L https://github.com/google/brotli/archive/v1.0.2.tar.gz | tar -zxv - (cd brotli-1.0.2 && mkdir out && cd out && ../configure-cmake && make && sudo make install) - rm -rf brotli-1.0.2 - curl -L https://github.com/facebook/zstd/archive/v1.3.2.tar.gz | tar -zxv - (cd zstd-1.3.2 && sudo make install) - rm -rf zstd-1.3.2 - sudo ldconfig - mkdir /tmp/go1.12 - curl -L -s https://dl.google.com/go/go1.12.linux-amd64.tar.gz | tar -zxf - -C /tmp/go1.12 --strip-components 1 - unset GOROOT - (GO111MODULE=on /tmp/go1.12/bin/go mod vendor) - (cd /tmp && GO111MODULE=on /tmp/go1.12/bin/go get golang.org/x/lint/golint@8f45f776aaf18cebc8d65861cc70c33c60471952) - (cd /tmp && GO111MODULE=on /tmp/go1.12/bin/go get honnef.co/go/tools/cmd/staticcheck@2019.1) matrix: include: - go: 1.9.x script: - go test -v -race ./... - go: 1.10.x script: - go test -v -race ./... - go: 1.11.x script: - go test -v -race ./... - go: 1.12.x script: - ./ztest.sh - go: master script: - go test -v -race ./... allow_failures: - go: master fast_finish: true ================================================ FILE: vendor/github.com/dsnet/compress/LICENSE.md ================================================ Copyright © 2015, Joe Tsai and The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/dsnet/compress/README.md ================================================ # Collection of compression libraries for Go # [![GoDoc](https://godoc.org/github.com/dsnet/compress/cmp?status.svg)](https://godoc.org/github.com/dsnet/compress) [![Build Status](https://travis-ci.org/dsnet/compress.svg?branch=master)](https://travis-ci.org/dsnet/compress) [![Report Card](https://goreportcard.com/badge/github.com/dsnet/compress)](https://goreportcard.com/report/github.com/dsnet/compress) ## Introduction ## **NOTE: This library is in active development. As such, there are no guarantees about the stability of the API. The author reserves the right to arbitrarily break the API for any reason.** This repository hosts a collection of compression related libraries. The goal of this project is to provide pure Go implementations for popular compression algorithms beyond what the Go standard library provides. The goals for these packages are as follows: * Maintainable: That the code remains well documented, well tested, readable, easy to maintain, and easy to verify that it conforms to the specification for the format being implemented. * Performant: To be able to compress and decompress within at least 80% of the rates that the C implementations are able to achieve. * Flexible: That the code provides low-level and fine granularity control over the compression streams similar to what the C APIs would provide. Of these three, the first objective is often at odds with the other two objectives and provides interesting challenges. Higher performance can often be achieved by muddling abstraction layers or using non-intuitive low-level primitives. Also, more features and functionality, while useful in some situations, often complicates the API. Thus, this package will attempt to satisfy all the goals, but will defer to favoring maintainability when the performance or flexibility benefits are not significant enough. ## Library Status ## For the packages available, only some features are currently implemented: | Package | Reader | Writer | | ------- | :----: | :----: | | brotli | :white_check_mark: | | | bzip2 | :white_check_mark: | :white_check_mark: | | flate | :white_check_mark: | | | xflate | :white_check_mark: | :white_check_mark: | This library is in active development. As such, there are no guarantees about the stability of the API. The author reserves the right to arbitrarily break the API for any reason. When the library becomes more mature, it is planned to eventually conform to some strict versioning scheme like [Semantic Versioning](http://semver.org/). However, in the meanwhile, this library does provide some basic API guarantees. For the types defined below, the method signatures are guaranteed to not change. Note that the author still reserves the right to change the fields within each ```Reader``` and ```Writer``` structs. ```go type ReaderConfig struct { ... } type Reader struct { ... } func NewReader(io.Reader, *ReaderConfig) (*Reader, error) { ... } func (*Reader) Read([]byte) (int, error) { ... } func (*Reader) Close() error { ... } type WriterConfig struct { ... } type Writer struct { ... } func NewWriter(io.Writer, *WriterConfig) (*Writer, error) { ... } func (*Writer) Write([]byte) (int, error) { ... } func (*Writer) Close() error { ... } ``` To see what work still remains, see the [Task List](https://github.com/dsnet/compress/wiki/Task-List). ## Performance ## See [Performance Metrics](https://github.com/dsnet/compress/wiki/Performance-Metrics). ## Frequently Asked Questions ## See [Frequently Asked Questions](https://github.com/dsnet/compress/wiki/Frequently-Asked-Questions). ## Installation ## Run the command: ```go get -u github.com/dsnet/compress``` This library requires `Go1.9` or higher in order to build. ## Packages ## | Package | Description | | :------ | :---------- | | [brotli](http://godoc.org/github.com/dsnet/compress/brotli) | Package brotli implements the Brotli format, described in RFC 7932. | | [bzip2](http://godoc.org/github.com/dsnet/compress/bzip2) | Package bzip2 implements the BZip2 compressed data format. | | [flate](http://godoc.org/github.com/dsnet/compress/flate) | Package flate implements the DEFLATE format, described in RFC 1951. | | [xflate](http://godoc.org/github.com/dsnet/compress/xflate) | Package xflate implements the XFLATE format, an random-access extension to DEFLATE. | ================================================ FILE: vendor/github.com/dsnet/compress/api.go ================================================ // Copyright 2015, Joe Tsai. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. // Package compress is a collection of compression libraries. package compress import ( "bufio" "io" "github.com/dsnet/compress/internal/errors" ) // The Error interface identifies all compression related errors. type Error interface { error CompressError() // IsDeprecated reports the use of a deprecated and unsupported feature. IsDeprecated() bool // IsCorrupted reports whether the input stream was corrupted. IsCorrupted() bool } var _ Error = errors.Error{} // ByteReader is an interface accepted by all decompression Readers. // It guarantees that the decompressor never reads more data than is necessary // from the underlying io.Reader. type ByteReader interface { io.Reader io.ByteReader } var _ ByteReader = (*bufio.Reader)(nil) // BufferedReader is an interface accepted by all decompression Readers. // It guarantees that the decompressor never reads more data than is necessary // from the underlying io.Reader. Since BufferedReader allows a decompressor // to peek at bytes further along in the stream without advancing the read // pointer, decompression can experience a significant performance gain when // provided a reader that satisfies this interface. Thus, a decompressor will // prefer this interface over ByteReader for performance reasons. // // The bufio.Reader satisfies this interface. type BufferedReader interface { io.Reader // Buffered returns the number of bytes currently buffered. // // This value becomes invalid following the next Read/Discard operation. Buffered() int // Peek returns the next n bytes without advancing the reader. // // If Peek returns fewer than n bytes, it also returns an error explaining // why the peek is short. Peek must support peeking of at least 8 bytes. // If 0 <= n <= Buffered(), Peek is guaranteed to succeed without reading // from the underlying io.Reader. // // This result becomes invalid following the next Read/Discard operation. Peek(n int) ([]byte, error) // Discard skips the next n bytes, returning the number of bytes discarded. // // If Discard skips fewer than n bytes, it also returns an error. // If 0 <= n <= Buffered(), Discard is guaranteed to succeed without reading // from the underlying io.Reader. Discard(n int) (int, error) } var _ BufferedReader = (*bufio.Reader)(nil) ================================================ FILE: vendor/github.com/dsnet/compress/bzip2/bwt.go ================================================ // Copyright 2015, Joe Tsai. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. package bzip2 import "github.com/dsnet/compress/bzip2/internal/sais" // The Burrows-Wheeler Transform implementation used here is based on the // Suffix Array by Induced Sorting (SA-IS) methodology by Nong, Zhang, and Chan. // This implementation uses the sais algorithm originally written by Yuta Mori. // // The SA-IS algorithm runs in O(n) and outputs a Suffix Array. There is a // mathematical relationship between Suffix Arrays and the Burrows-Wheeler // Transform, such that a SA can be converted to a BWT in O(n) time. // // References: // http://www.hpl.hp.com/techreports/Compaq-DEC/SRC-RR-124.pdf // https://github.com/cscott/compressjs/blob/master/lib/BWT.js // https://www.quora.com/How-can-I-optimize-burrows-wheeler-transform-and-inverse-transform-to-work-in-O-n-time-O-n-space type burrowsWheelerTransform struct { buf []byte sa []int perm []uint32 } func (bwt *burrowsWheelerTransform) Encode(buf []byte) (ptr int) { if len(buf) == 0 { return -1 } // TODO(dsnet): Find a way to avoid the duplicate input string method. // We only need to do this because suffix arrays (by definition) only // operate non-wrapped suffixes of a string. On the other hand, // the BWT specifically used in bzip2 operate on a strings that wrap-around // when being sorted. // Step 1: Concatenate the input string to itself so that we can use the // suffix array algorithm for bzip2's variant of BWT. n := len(buf) bwt.buf = append(append(bwt.buf[:0], buf...), buf...) if cap(bwt.sa) < 2*n { bwt.sa = make([]int, 2*n) } t := bwt.buf[:2*n] sa := bwt.sa[:2*n] // Step 2: Compute the suffix array (SA). The input string, t, will not be // modified, while the results will be written to the output, sa. sais.ComputeSA(t, sa) // Step 3: Convert the SA to a BWT. Since ComputeSA does not mutate the // input, we have two copies of the input; in buf and buf2. Thus, we write // the transformation to buf, while using buf2. var j int buf2 := t[n:] for _, i := range sa { if i < n { if i == 0 { ptr = j i = n } buf[j] = buf2[i-1] j++ } } return ptr } func (bwt *burrowsWheelerTransform) Decode(buf []byte, ptr int) { if len(buf) == 0 { return } // Step 1: Compute cumm, where cumm[ch] reports the total number of // characters that precede the character ch in the alphabet. var cumm [256]int for _, v := range buf { cumm[v]++ } var sum int for i, v := range cumm { cumm[i] = sum sum += v } // Step 2: Compute perm, where perm[ptr] contains a pointer to the next // byte in buf and the next pointer in perm itself. if cap(bwt.perm) < len(buf) { bwt.perm = make([]uint32, len(buf)) } perm := bwt.perm[:len(buf)] for i, b := range buf { perm[cumm[b]] = uint32(i) cumm[b]++ } // Step 3: Follow each pointer in perm to the next byte, starting with the // origin pointer. if cap(bwt.buf) < len(buf) { bwt.buf = make([]byte, len(buf)) } buf2 := bwt.buf[:len(buf)] i := perm[ptr] for j := range buf2 { buf2[j] = buf[i] i = perm[i] } copy(buf, buf2) } ================================================ FILE: vendor/github.com/dsnet/compress/bzip2/common.go ================================================ // Copyright 2015, Joe Tsai. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. // Package bzip2 implements the BZip2 compressed data format. // // Canonical C implementation: // http://bzip.org // // Unofficial format specification: // https://github.com/dsnet/compress/blob/master/doc/bzip2-format.pdf package bzip2 import ( "fmt" "hash/crc32" "github.com/dsnet/compress/internal" "github.com/dsnet/compress/internal/errors" ) // There does not exist a formal specification of the BZip2 format. As such, // much of this work is derived by either reverse engineering the original C // source code or using secondary sources. // // Significant amounts of fuzz testing is done to ensure that outputs from // this package is properly decoded by the C library. Furthermore, we test that // both this package and the C library agree about what inputs are invalid. // // Compression stack: // Run-length encoding 1 (RLE1) // Burrows-Wheeler transform (BWT) // Move-to-front transform (MTF) // Run-length encoding 2 (RLE2) // Prefix encoding (PE) // // References: // http://bzip.org/ // https://en.wikipedia.org/wiki/Bzip2 // https://code.google.com/p/jbzip2/ const ( BestSpeed = 1 BestCompression = 9 DefaultCompression = 6 ) const ( hdrMagic = 0x425a // Hex of "BZ" blkMagic = 0x314159265359 // BCD of PI endMagic = 0x177245385090 // BCD of sqrt(PI) blockSize = 100000 ) func errorf(c int, f string, a ...interface{}) error { return errors.Error{Code: c, Pkg: "bzip2", Msg: fmt.Sprintf(f, a...)} } func panicf(c int, f string, a ...interface{}) { errors.Panic(errorf(c, f, a...)) } // errWrap converts a lower-level errors.Error to be one from this package. // The replaceCode passed in will be used to replace the code for any errors // with the errors.Invalid code. // // For the Reader, set this to errors.Corrupted. // For the Writer, set this to errors.Internal. func errWrap(err error, replaceCode int) error { if cerr, ok := err.(errors.Error); ok { if errors.IsInvalid(cerr) { cerr.Code = replaceCode } err = errorf(cerr.Code, "%s", cerr.Msg) } return err } var errClosed = errorf(errors.Closed, "") // crc computes the CRC-32 used by BZip2. // // The CRC-32 computation in bzip2 treats bytes as having bits in big-endian // order. That is, the MSB is read before the LSB. Thus, we can use the // standard library version of CRC-32 IEEE with some minor adjustments. // // The byte array is used as an intermediate buffer to swap the bits of every // byte of the input. type crc struct { val uint32 buf [256]byte } // update computes the CRC-32 of appending buf to c. func (c *crc) update(buf []byte) { cval := internal.ReverseUint32(c.val) for len(buf) > 0 { n := len(buf) if n > len(c.buf) { n = len(c.buf) } for i, b := range buf[:n] { c.buf[i] = internal.ReverseLUT[b] } cval = crc32.Update(cval, crc32.IEEETable, c.buf[:n]) buf = buf[n:] } c.val = internal.ReverseUint32(cval) } ================================================ FILE: vendor/github.com/dsnet/compress/bzip2/fuzz_off.go ================================================ // Copyright 2016, Joe Tsai. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. // +build !gofuzz // This file exists to suppress fuzzing details from release builds. package bzip2 type fuzzReader struct{} func (*fuzzReader) updateChecksum(int64, uint32) {} ================================================ FILE: vendor/github.com/dsnet/compress/bzip2/fuzz_on.go ================================================ // Copyright 2016, Joe Tsai. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. // +build gofuzz // This file exists to export internal implementation details for fuzz testing. package bzip2 func ForwardBWT(buf []byte) (ptr int) { var bwt burrowsWheelerTransform return bwt.Encode(buf) } func ReverseBWT(buf []byte, ptr int) { var bwt burrowsWheelerTransform bwt.Decode(buf, ptr) } type fuzzReader struct { Checksums Checksums } // updateChecksum updates Checksums. // // If a valid pos is provided, it appends the (pos, val) pair to the slice. // Otherwise, it will update the last record with the new value. func (fr *fuzzReader) updateChecksum(pos int64, val uint32) { if pos >= 0 { fr.Checksums = append(fr.Checksums, Checksum{pos, val}) } else { fr.Checksums[len(fr.Checksums)-1].Value = val } } type Checksum struct { Offset int64 // Bit offset of the checksum Value uint32 // Checksum value } type Checksums []Checksum // Apply overwrites all checksum fields in d with the ones in cs. func (cs Checksums) Apply(d []byte) []byte { d = append([]byte(nil), d...) for _, c := range cs { setU32(d, c.Offset, c.Value) } return d } func setU32(d []byte, pos int64, val uint32) { for i := uint(0); i < 32; i++ { bpos := uint64(pos) + uint64(i) d[bpos/8] &= ^byte(1 << (7 - bpos%8)) d[bpos/8] |= byte(val>>(31-i)) << (7 - bpos%8) } } // Verify checks that all checksum fields in d matches those in cs. func (cs Checksums) Verify(d []byte) bool { for _, c := range cs { if getU32(d, c.Offset) != c.Value { return false } } return true } func getU32(d []byte, pos int64) (val uint32) { for i := uint(0); i < 32; i++ { bpos := uint64(pos) + uint64(i) val |= (uint32(d[bpos/8] >> (7 - bpos%8))) << (31 - i) } return val } ================================================ FILE: vendor/github.com/dsnet/compress/bzip2/internal/sais/common.go ================================================ // Copyright 2015, Joe Tsai. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. // Package sais implements a linear time suffix array algorithm. package sais //go:generate go run sais_gen.go byte sais_byte.go //go:generate go run sais_gen.go int sais_int.go // This package ports the C sais implementation by Yuta Mori. The ports are // located in sais_byte.go and sais_int.go, which are identical to each other // except for the types. Since Go does not support generics, we use generators to // create the two files. // // References: // https://sites.google.com/site/yuta256/sais // https://www.researchgate.net/publication/221313676_Linear_Time_Suffix_Array_Construction_Using_D-Critical_Substrings // https://www.researchgate.net/publication/224176324_Two_Efficient_Algorithms_for_Linear_Time_Suffix_Array_Construction // ComputeSA computes the suffix array of t and places the result in sa. // Both t and sa must be the same length. func ComputeSA(t []byte, sa []int) { if len(sa) != len(t) { panic("mismatching sizes") } computeSA_byte(t, sa, 0, len(t), 256) } ================================================ FILE: vendor/github.com/dsnet/compress/bzip2/internal/sais/sais_byte.go ================================================ // Copyright 2015, Joe Tsai. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. // Code generated by sais_gen.go. DO NOT EDIT. // ==================================================== // Copyright (c) 2008-2010 Yuta Mori All Rights Reserved. // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // ==================================================== package sais func getCounts_byte(T []byte, C []int, n, k int) { var i int for i = 0; i < k; i++ { C[i] = 0 } for i = 0; i < n; i++ { C[T[i]]++ } } func getBuckets_byte(C, B []int, k int, end bool) { var i, sum int if end { for i = 0; i < k; i++ { sum += C[i] B[i] = sum } } else { for i = 0; i < k; i++ { sum += C[i] B[i] = sum - C[i] } } } func sortLMS1_byte(T []byte, SA, C, B []int, n, k int) { var b, i, j int var c0, c1 int // Compute SAl. if &C[0] == &B[0] { getCounts_byte(T, C, n, k) } getBuckets_byte(C, B, k, false) // Find starts of buckets j = n - 1 c1 = int(T[j]) b = B[c1] j-- if int(T[j]) < c1 { SA[b] = ^j } else { SA[b] = j } b++ for i = 0; i < n; i++ { if j = SA[i]; j > 0 { if c0 = int(T[j]); c0 != c1 { B[c1] = b c1 = c0 b = B[c1] } j-- if int(T[j]) < c1 { SA[b] = ^j } else { SA[b] = j } b++ SA[i] = 0 } else if j < 0 { SA[i] = ^j } } // Compute SAs. if &C[0] == &B[0] { getCounts_byte(T, C, n, k) } getBuckets_byte(C, B, k, true) // Find ends of buckets c1 = 0 b = B[c1] for i = n - 1; i >= 0; i-- { if j = SA[i]; j > 0 { if c0 = int(T[j]); c0 != c1 { B[c1] = b c1 = c0 b = B[c1] } j-- b-- if int(T[j]) > c1 { SA[b] = ^(j + 1) } else { SA[b] = j } SA[i] = 0 } } } func postProcLMS1_byte(T []byte, SA []int, n, m int) int { var i, j, p, q, plen, qlen, name int var c0, c1 int var diff bool // Compact all the sorted substrings into the first m items of SA. // 2*m must be not larger than n (provable). for i = 0; SA[i] < 0; i++ { SA[i] = ^SA[i] } if i < m { for j, i = i, i+1; ; i++ { if p = SA[i]; p < 0 { SA[j] = ^p j++ SA[i] = 0 if j == m { break } } } } // Store the length of all substrings. i = n - 1 j = n - 1 c0 = int(T[n-1]) for { c1 = c0 if i--; i < 0 { break } if c0 = int(T[i]); c0 < c1 { break } } for i >= 0 { for { c1 = c0 if i--; i < 0 { break } if c0 = int(T[i]); c0 > c1 { break } } if i >= 0 { SA[m+((i+1)>>1)] = j - i j = i + 1 for { c1 = c0 if i--; i < 0 { break } if c0 = int(T[i]); c0 < c1 { break } } } } // Find the lexicographic names of all substrings. name = 0 qlen = 0 for i, q = 0, n; i < m; i++ { p = SA[i] plen = SA[m+(p>>1)] diff = true if (plen == qlen) && ((q + plen) < n) { for j = 0; (j < plen) && (T[p+j] == T[q+j]); j++ { } if j == plen { diff = false } } if diff { name++ q = p qlen = plen } SA[m+(p>>1)] = name } return name } func sortLMS2_byte(T []byte, SA, C, B, D []int, n, k int) { var b, i, j, t, d int var c0, c1 int // Compute SAl. getBuckets_byte(C, B, k, false) // Find starts of buckets j = n - 1 c1 = int(T[j]) b = B[c1] j-- if int(T[j]) < c1 { t = 1 } else { t = 0 } j += n if t&1 > 0 { SA[b] = ^j } else { SA[b] = j } b++ for i, d = 0, 0; i < n; i++ { if j = SA[i]; j > 0 { if n <= j { d += 1 j -= n } if c0 = int(T[j]); c0 != c1 { B[c1] = b c1 = c0 b = B[c1] } j-- t = int(c0) << 1 if int(T[j]) < c1 { t |= 1 } if D[t] != d { j += n D[t] = d } if t&1 > 0 { SA[b] = ^j } else { SA[b] = j } b++ SA[i] = 0 } else if j < 0 { SA[i] = ^j } } for i = n - 1; 0 <= i; i-- { if SA[i] > 0 { if SA[i] < n { SA[i] += n for j = i - 1; SA[j] < n; j-- { } SA[j] -= n i = j } } } // Compute SAs. getBuckets_byte(C, B, k, true) // Find ends of buckets c1 = 0 b = B[c1] for i, d = n-1, d+1; i >= 0; i-- { if j = SA[i]; j > 0 { if n <= j { d += 1 j -= n } if c0 = int(T[j]); c0 != c1 { B[c1] = b c1 = c0 b = B[c1] } j-- t = int(c0) << 1 if int(T[j]) > c1 { t |= 1 } if D[t] != d { j += n D[t] = d } b-- if t&1 > 0 { SA[b] = ^(j + 1) } else { SA[b] = j } SA[i] = 0 } } } func postProcLMS2_byte(SA []int, n, m int) int { var i, j, d, name int // Compact all the sorted LMS substrings into the first m items of SA. name = 0 for i = 0; SA[i] < 0; i++ { j = ^SA[i] if n <= j { name += 1 } SA[i] = j } if i < m { for d, i = i, i+1; ; i++ { if j = SA[i]; j < 0 { j = ^j if n <= j { name += 1 } SA[d] = j d++ SA[i] = 0 if d == m { break } } } } if name < m { // Store the lexicographic names. for i, d = m-1, name+1; 0 <= i; i-- { if j = SA[i]; n <= j { j -= n d-- } SA[m+(j>>1)] = d } } else { // Unset flags. for i = 0; i < m; i++ { if j = SA[i]; n <= j { j -= n SA[i] = j } } } return name } func induceSA_byte(T []byte, SA, C, B []int, n, k int) { var b, i, j int var c0, c1 int // Compute SAl. if &C[0] == &B[0] { getCounts_byte(T, C, n, k) } getBuckets_byte(C, B, k, false) // Find starts of buckets j = n - 1 c1 = int(T[j]) b = B[c1] if j > 0 && int(T[j-1]) < c1 { SA[b] = ^j } else { SA[b] = j } b++ for i = 0; i < n; i++ { j = SA[i] SA[i] = ^j if j > 0 { j-- if c0 = int(T[j]); c0 != c1 { B[c1] = b c1 = c0 b = B[c1] } if j > 0 && int(T[j-1]) < c1 { SA[b] = ^j } else { SA[b] = j } b++ } } // Compute SAs. if &C[0] == &B[0] { getCounts_byte(T, C, n, k) } getBuckets_byte(C, B, k, true) // Find ends of buckets c1 = 0 b = B[c1] for i = n - 1; i >= 0; i-- { if j = SA[i]; j > 0 { j-- if c0 = int(T[j]); c0 != c1 { B[c1] = b c1 = c0 b = B[c1] } b-- if (j == 0) || (int(T[j-1]) > c1) { SA[b] = ^j } else { SA[b] = j } } else { SA[i] = ^j } } } func computeSA_byte(T []byte, SA []int, fs, n, k int) { const ( minBucketSize = 512 sortLMS2Limit = 0x3fffffff ) var C, B, D, RA []int var bo int // Offset of B relative to SA var b, i, j, m, p, q, name, newfs int var c0, c1 int var flags uint if k <= minBucketSize { C = make([]int, k) if k <= fs { bo = n + fs - k B = SA[bo:] flags = 1 } else { B = make([]int, k) flags = 3 } } else if k <= fs { C = SA[n+fs-k:] if k <= fs-k { bo = n + fs - 2*k B = SA[bo:] flags = 0 } else if k <= 4*minBucketSize { B = make([]int, k) flags = 2 } else { B = C flags = 8 } } else { C = make([]int, k) B = C flags = 4 | 8 } if n <= sortLMS2Limit && 2 <= (n/k) { if flags&1 > 0 { if 2*k <= fs-k { flags |= 32 } else { flags |= 16 } } else if flags == 0 && 2*k <= (fs-2*k) { flags |= 32 } } // Stage 1: Reduce the problem by at least 1/2. // Sort all the LMS-substrings. getCounts_byte(T, C, n, k) getBuckets_byte(C, B, k, true) // Find ends of buckets for i = 0; i < n; i++ { SA[i] = 0 } b = -1 i = n - 1 j = n m = 0 c0 = int(T[n-1]) for { c1 = c0 if i--; i < 0 { break } if c0 = int(T[i]); c0 < c1 { break } } for i >= 0 { for { c1 = c0 if i--; i < 0 { break } if c0 = int(T[i]); c0 > c1 { break } } if i >= 0 { if b >= 0 { SA[b] = j } B[c1]-- b = B[c1] j = i m++ for { c1 = c0 if i--; i < 0 { break } if c0 = int(T[i]); c0 < c1 { break } } } } if m > 1 { if flags&(16|32) > 0 { if flags&16 > 0 { D = make([]int, 2*k) } else { D = SA[bo-2*k:] } B[T[j+1]]++ for i, j = 0, 0; i < k; i++ { j += C[i] if B[i] != j { SA[B[i]] += n } D[i] = 0 D[i+k] = 0 } sortLMS2_byte(T, SA, C, B, D, n, k) name = postProcLMS2_byte(SA, n, m) } else { sortLMS1_byte(T, SA, C, B, n, k) name = postProcLMS1_byte(T, SA, n, m) } } else if m == 1 { SA[b] = j + 1 name = 1 } else { name = 0 } // Stage 2: Solve the reduced problem. // Recurse if names are not yet unique. if name < m { newfs = n + fs - 2*m if flags&(1|4|8) == 0 { if k+name <= newfs { newfs -= k } else { flags |= 8 } } RA = SA[m+newfs:] for i, j = m+(n>>1)-1, m-1; m <= i; i-- { if SA[i] != 0 { RA[j] = SA[i] - 1 j-- } } computeSA_int(RA, SA, newfs, m, name) i = n - 1 j = m - 1 c0 = int(T[n-1]) for { c1 = c0 if i--; i < 0 { break } if c0 = int(T[i]); c0 < c1 { break } } for i >= 0 { for { c1 = c0 if i--; i < 0 { break } if c0 = int(T[i]); c0 > c1 { break } } if i >= 0 { RA[j] = i + 1 j-- for { c1 = c0 if i--; i < 0 { break } if c0 = int(T[i]); c0 < c1 { break } } } } for i = 0; i < m; i++ { SA[i] = RA[SA[i]] } if flags&4 > 0 { B = make([]int, k) C = B } if flags&2 > 0 { B = make([]int, k) } } // Stage 3: Induce the result for the original problem. if flags&8 > 0 { getCounts_byte(T, C, n, k) } // Put all left-most S characters into their buckets. if m > 1 { getBuckets_byte(C, B, k, true) // Find ends of buckets i = m - 1 j = n p = SA[m-1] c1 = int(T[p]) for { c0 = c1 q = B[c0] for q < j { j-- SA[j] = 0 } for { j-- SA[j] = p if i--; i < 0 { break } p = SA[i] if c1 = int(T[p]); c1 != c0 { break } } if i < 0 { break } } for j > 0 { j-- SA[j] = 0 } } induceSA_byte(T, SA, C, B, n, k) } ================================================ FILE: vendor/github.com/dsnet/compress/bzip2/internal/sais/sais_int.go ================================================ // Copyright 2015, Joe Tsai. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. // Code generated by sais_gen.go. DO NOT EDIT. // ==================================================== // Copyright (c) 2008-2010 Yuta Mori All Rights Reserved. // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // ==================================================== package sais func getCounts_int(T []int, C []int, n, k int) { var i int for i = 0; i < k; i++ { C[i] = 0 } for i = 0; i < n; i++ { C[T[i]]++ } } func getBuckets_int(C, B []int, k int, end bool) { var i, sum int if end { for i = 0; i < k; i++ { sum += C[i] B[i] = sum } } else { for i = 0; i < k; i++ { sum += C[i] B[i] = sum - C[i] } } } func sortLMS1_int(T []int, SA, C, B []int, n, k int) { var b, i, j int var c0, c1 int // Compute SAl. if &C[0] == &B[0] { getCounts_int(T, C, n, k) } getBuckets_int(C, B, k, false) // Find starts of buckets j = n - 1 c1 = int(T[j]) b = B[c1] j-- if int(T[j]) < c1 { SA[b] = ^j } else { SA[b] = j } b++ for i = 0; i < n; i++ { if j = SA[i]; j > 0 { if c0 = int(T[j]); c0 != c1 { B[c1] = b c1 = c0 b = B[c1] } j-- if int(T[j]) < c1 { SA[b] = ^j } else { SA[b] = j } b++ SA[i] = 0 } else if j < 0 { SA[i] = ^j } } // Compute SAs. if &C[0] == &B[0] { getCounts_int(T, C, n, k) } getBuckets_int(C, B, k, true) // Find ends of buckets c1 = 0 b = B[c1] for i = n - 1; i >= 0; i-- { if j = SA[i]; j > 0 { if c0 = int(T[j]); c0 != c1 { B[c1] = b c1 = c0 b = B[c1] } j-- b-- if int(T[j]) > c1 { SA[b] = ^(j + 1) } else { SA[b] = j } SA[i] = 0 } } } func postProcLMS1_int(T []int, SA []int, n, m int) int { var i, j, p, q, plen, qlen, name int var c0, c1 int var diff bool // Compact all the sorted substrings into the first m items of SA. // 2*m must be not larger than n (provable). for i = 0; SA[i] < 0; i++ { SA[i] = ^SA[i] } if i < m { for j, i = i, i+1; ; i++ { if p = SA[i]; p < 0 { SA[j] = ^p j++ SA[i] = 0 if j == m { break } } } } // Store the length of all substrings. i = n - 1 j = n - 1 c0 = int(T[n-1]) for { c1 = c0 if i--; i < 0 { break } if c0 = int(T[i]); c0 < c1 { break } } for i >= 0 { for { c1 = c0 if i--; i < 0 { break } if c0 = int(T[i]); c0 > c1 { break } } if i >= 0 { SA[m+((i+1)>>1)] = j - i j = i + 1 for { c1 = c0 if i--; i < 0 { break } if c0 = int(T[i]); c0 < c1 { break } } } } // Find the lexicographic names of all substrings. name = 0 qlen = 0 for i, q = 0, n; i < m; i++ { p = SA[i] plen = SA[m+(p>>1)] diff = true if (plen == qlen) && ((q + plen) < n) { for j = 0; (j < plen) && (T[p+j] == T[q+j]); j++ { } if j == plen { diff = false } } if diff { name++ q = p qlen = plen } SA[m+(p>>1)] = name } return name } func sortLMS2_int(T []int, SA, C, B, D []int, n, k int) { var b, i, j, t, d int var c0, c1 int // Compute SAl. getBuckets_int(C, B, k, false) // Find starts of buckets j = n - 1 c1 = int(T[j]) b = B[c1] j-- if int(T[j]) < c1 { t = 1 } else { t = 0 } j += n if t&1 > 0 { SA[b] = ^j } else { SA[b] = j } b++ for i, d = 0, 0; i < n; i++ { if j = SA[i]; j > 0 { if n <= j { d += 1 j -= n } if c0 = int(T[j]); c0 != c1 { B[c1] = b c1 = c0 b = B[c1] } j-- t = int(c0) << 1 if int(T[j]) < c1 { t |= 1 } if D[t] != d { j += n D[t] = d } if t&1 > 0 { SA[b] = ^j } else { SA[b] = j } b++ SA[i] = 0 } else if j < 0 { SA[i] = ^j } } for i = n - 1; 0 <= i; i-- { if SA[i] > 0 { if SA[i] < n { SA[i] += n for j = i - 1; SA[j] < n; j-- { } SA[j] -= n i = j } } } // Compute SAs. getBuckets_int(C, B, k, true) // Find ends of buckets c1 = 0 b = B[c1] for i, d = n-1, d+1; i >= 0; i-- { if j = SA[i]; j > 0 { if n <= j { d += 1 j -= n } if c0 = int(T[j]); c0 != c1 { B[c1] = b c1 = c0 b = B[c1] } j-- t = int(c0) << 1 if int(T[j]) > c1 { t |= 1 } if D[t] != d { j += n D[t] = d } b-- if t&1 > 0 { SA[b] = ^(j + 1) } else { SA[b] = j } SA[i] = 0 } } } func postProcLMS2_int(SA []int, n, m int) int { var i, j, d, name int // Compact all the sorted LMS substrings into the first m items of SA. name = 0 for i = 0; SA[i] < 0; i++ { j = ^SA[i] if n <= j { name += 1 } SA[i] = j } if i < m { for d, i = i, i+1; ; i++ { if j = SA[i]; j < 0 { j = ^j if n <= j { name += 1 } SA[d] = j d++ SA[i] = 0 if d == m { break } } } } if name < m { // Store the lexicographic names. for i, d = m-1, name+1; 0 <= i; i-- { if j = SA[i]; n <= j { j -= n d-- } SA[m+(j>>1)] = d } } else { // Unset flags. for i = 0; i < m; i++ { if j = SA[i]; n <= j { j -= n SA[i] = j } } } return name } func induceSA_int(T []int, SA, C, B []int, n, k int) { var b, i, j int var c0, c1 int // Compute SAl. if &C[0] == &B[0] { getCounts_int(T, C, n, k) } getBuckets_int(C, B, k, false) // Find starts of buckets j = n - 1 c1 = int(T[j]) b = B[c1] if j > 0 && int(T[j-1]) < c1 { SA[b] = ^j } else { SA[b] = j } b++ for i = 0; i < n; i++ { j = SA[i] SA[i] = ^j if j > 0 { j-- if c0 = int(T[j]); c0 != c1 { B[c1] = b c1 = c0 b = B[c1] } if j > 0 && int(T[j-1]) < c1 { SA[b] = ^j } else { SA[b] = j } b++ } } // Compute SAs. if &C[0] == &B[0] { getCounts_int(T, C, n, k) } getBuckets_int(C, B, k, true) // Find ends of buckets c1 = 0 b = B[c1] for i = n - 1; i >= 0; i-- { if j = SA[i]; j > 0 { j-- if c0 = int(T[j]); c0 != c1 { B[c1] = b c1 = c0 b = B[c1] } b-- if (j == 0) || (int(T[j-1]) > c1) { SA[b] = ^j } else { SA[b] = j } } else { SA[i] = ^j } } } func computeSA_int(T []int, SA []int, fs, n, k int) { const ( minBucketSize = 512 sortLMS2Limit = 0x3fffffff ) var C, B, D, RA []int var bo int // Offset of B relative to SA var b, i, j, m, p, q, name, newfs int var c0, c1 int var flags uint if k <= minBucketSize { C = make([]int, k) if k <= fs { bo = n + fs - k B = SA[bo:] flags = 1 } else { B = make([]int, k) flags = 3 } } else if k <= fs { C = SA[n+fs-k:] if k <= fs-k { bo = n + fs - 2*k B = SA[bo:] flags = 0 } else if k <= 4*minBucketSize { B = make([]int, k) flags = 2 } else { B = C flags = 8 } } else { C = make([]int, k) B = C flags = 4 | 8 } if n <= sortLMS2Limit && 2 <= (n/k) { if flags&1 > 0 { if 2*k <= fs-k { flags |= 32 } else { flags |= 16 } } else if flags == 0 && 2*k <= (fs-2*k) { flags |= 32 } } // Stage 1: Reduce the problem by at least 1/2. // Sort all the LMS-substrings. getCounts_int(T, C, n, k) getBuckets_int(C, B, k, true) // Find ends of buckets for i = 0; i < n; i++ { SA[i] = 0 } b = -1 i = n - 1 j = n m = 0 c0 = int(T[n-1]) for { c1 = c0 if i--; i < 0 { break } if c0 = int(T[i]); c0 < c1 { break } } for i >= 0 { for { c1 = c0 if i--; i < 0 { break } if c0 = int(T[i]); c0 > c1 { break } } if i >= 0 { if b >= 0 { SA[b] = j } B[c1]-- b = B[c1] j = i m++ for { c1 = c0 if i--; i < 0 { break } if c0 = int(T[i]); c0 < c1 { break } } } } if m > 1 { if flags&(16|32) > 0 { if flags&16 > 0 { D = make([]int, 2*k) } else { D = SA[bo-2*k:] } B[T[j+1]]++ for i, j = 0, 0; i < k; i++ { j += C[i] if B[i] != j { SA[B[i]] += n } D[i] = 0 D[i+k] = 0 } sortLMS2_int(T, SA, C, B, D, n, k) name = postProcLMS2_int(SA, n, m) } else { sortLMS1_int(T, SA, C, B, n, k) name = postProcLMS1_int(T, SA, n, m) } } else if m == 1 { SA[b] = j + 1 name = 1 } else { name = 0 } // Stage 2: Solve the reduced problem. // Recurse if names are not yet unique. if name < m { newfs = n + fs - 2*m if flags&(1|4|8) == 0 { if k+name <= newfs { newfs -= k } else { flags |= 8 } } RA = SA[m+newfs:] for i, j = m+(n>>1)-1, m-1; m <= i; i-- { if SA[i] != 0 { RA[j] = SA[i] - 1 j-- } } computeSA_int(RA, SA, newfs, m, name) i = n - 1 j = m - 1 c0 = int(T[n-1]) for { c1 = c0 if i--; i < 0 { break } if c0 = int(T[i]); c0 < c1 { break } } for i >= 0 { for { c1 = c0 if i--; i < 0 { break } if c0 = int(T[i]); c0 > c1 { break } } if i >= 0 { RA[j] = i + 1 j-- for { c1 = c0 if i--; i < 0 { break } if c0 = int(T[i]); c0 < c1 { break } } } } for i = 0; i < m; i++ { SA[i] = RA[SA[i]] } if flags&4 > 0 { B = make([]int, k) C = B } if flags&2 > 0 { B = make([]int, k) } } // Stage 3: Induce the result for the original problem. if flags&8 > 0 { getCounts_int(T, C, n, k) } // Put all left-most S characters into their buckets. if m > 1 { getBuckets_int(C, B, k, true) // Find ends of buckets i = m - 1 j = n p = SA[m-1] c1 = int(T[p]) for { c0 = c1 q = B[c0] for q < j { j-- SA[j] = 0 } for { j-- SA[j] = p if i--; i < 0 { break } p = SA[i] if c1 = int(T[p]); c1 != c0 { break } } if i < 0 { break } } for j > 0 { j-- SA[j] = 0 } } induceSA_int(T, SA, C, B, n, k) } ================================================ FILE: vendor/github.com/dsnet/compress/bzip2/mtf_rle2.go ================================================ // Copyright 2015, Joe Tsai. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. package bzip2 import "github.com/dsnet/compress/internal/errors" // moveToFront implements both the MTF and RLE stages of bzip2 at the same time. // Any runs of zeros in the encoded output will be replaced by a sequence of // RUNA and RUNB symbols are encode the length of the run. // // The RLE encoding used can actually be encoded to and decoded from using // normal two's complement arithmetic. The methodology for doing so is below. // // Assuming the following: // num: The value being encoded by RLE encoding. // run: A sequence of RUNA and RUNB symbols represented as a binary integer, // where RUNA is the 0 bit, RUNB is the 1 bit, and least-significant RUN // symbols are at the least-significant bit positions. // cnt: The number of RUNA and RUNB symbols. // // Then the RLE encoding used by bzip2 has this mathematical property: // num+1 == (1< len(mtf.dictBuf) { panicf(errors.Internal, "alphabet too large") } copy(mtf.dictBuf[:], dict) mtf.dictLen = len(dict) mtf.blkSize = blkSize } func (mtf *moveToFront) Encode(vals []byte) (syms []uint16) { dict := mtf.dictBuf[:mtf.dictLen] syms = mtf.syms[:0] if len(vals) > mtf.blkSize { panicf(errors.Internal, "exceeded block size") } var lastNum uint32 for _, val := range vals { // Normal move-to-front transform. var idx uint8 // Reverse lookup idx in dict for di, dv := range dict { if dv == val { idx = uint8(di) break } } copy(dict[1:], dict[:idx]) dict[0] = val // Run-length encoding augmentation. if idx == 0 { lastNum++ continue } if lastNum > 0 { for rc := lastNum + 1; rc != 1; rc >>= 1 { syms = append(syms, uint16(rc&1)) } lastNum = 0 } syms = append(syms, uint16(idx)+1) } if lastNum > 0 { for rc := lastNum + 1; rc != 1; rc >>= 1 { syms = append(syms, uint16(rc&1)) } } mtf.syms = syms return syms } func (mtf *moveToFront) Decode(syms []uint16) (vals []byte) { dict := mtf.dictBuf[:mtf.dictLen] vals = mtf.vals[:0] var lastCnt uint var lastRun uint32 for _, sym := range syms { // Run-length encoding augmentation. if sym < 2 { lastRun |= uint32(sym) << lastCnt lastCnt++ continue } if lastCnt > 0 { cnt := int((1< mtf.blkSize || lastCnt > 24 { panicf(errors.Corrupted, "run-length decoding exceeded block size") } for i := cnt; i > 0; i-- { vals = append(vals, dict[0]) } lastCnt, lastRun = 0, 0 } // Normal move-to-front transform. val := dict[sym-1] // Forward lookup val in dict copy(dict[1:], dict[:sym-1]) dict[0] = val if len(vals) >= mtf.blkSize { panicf(errors.Corrupted, "run-length decoding exceeded block size") } vals = append(vals, val) } if lastCnt > 0 { cnt := int((1< mtf.blkSize || lastCnt > 24 { panicf(errors.Corrupted, "run-length decoding exceeded block size") } for i := cnt; i > 0; i-- { vals = append(vals, dict[0]) } } mtf.vals = vals return vals } ================================================ FILE: vendor/github.com/dsnet/compress/bzip2/prefix.go ================================================ // Copyright 2015, Joe Tsai. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. package bzip2 import ( "io" "github.com/dsnet/compress/internal" "github.com/dsnet/compress/internal/errors" "github.com/dsnet/compress/internal/prefix" ) const ( minNumTrees = 2 maxNumTrees = 6 maxPrefixBits = 20 // Maximum bit-width of a prefix code maxNumSyms = 256 + 2 // Maximum number of symbols in the alphabet numBlockSyms = 50 // Number of bytes in a block ) // encSel and decSel are used to handle the prefix encoding for tree selectors. // The prefix encoding is as follows: // // Code TreeIdx // 0 <=> 0 // 10 <=> 1 // 110 <=> 2 // 1110 <=> 3 // 11110 <=> 4 // 111110 <=> 5 // 111111 <=> 6 Invalid tree index, so should fail // var encSel, decSel = func() (e prefix.Encoder, d prefix.Decoder) { var selCodes [maxNumTrees + 1]prefix.PrefixCode for i := range selCodes { selCodes[i] = prefix.PrefixCode{Sym: uint32(i), Len: uint32(i + 1)} } selCodes[maxNumTrees] = prefix.PrefixCode{Sym: maxNumTrees, Len: maxNumTrees} prefix.GeneratePrefixes(selCodes[:]) e.Init(selCodes[:]) d.Init(selCodes[:]) return }() type prefixReader struct{ prefix.Reader } func (pr *prefixReader) Init(r io.Reader) { pr.Reader.Init(r, true) } func (pr *prefixReader) ReadBitsBE64(nb uint) uint64 { if nb <= 32 { v := uint32(pr.ReadBits(nb)) return uint64(internal.ReverseUint32N(v, nb)) } v0 := internal.ReverseUint32(uint32(pr.ReadBits(32))) v1 := internal.ReverseUint32(uint32(pr.ReadBits(nb - 32))) v := uint64(v0)<<32 | uint64(v1) return v >> (64 - nb) } func (pr *prefixReader) ReadPrefixCodes(codes []prefix.PrefixCodes, trees []prefix.Decoder) { for i, pc := range codes { clen := int(pr.ReadBitsBE64(5)) sum := 1 << maxPrefixBits for sym := range pc { for { if clen < 1 || clen > maxPrefixBits { panicf(errors.Corrupted, "invalid prefix bit-length: %d", clen) } b, ok := pr.TryReadBits(1) if !ok { b = pr.ReadBits(1) } if b == 0 { break } b, ok = pr.TryReadBits(1) if !ok { b = pr.ReadBits(1) } clen -= int(b*2) - 1 // +1 or -1 } pc[sym] = prefix.PrefixCode{Sym: uint32(sym), Len: uint32(clen)} sum -= (1 << maxPrefixBits) >> uint(clen) } if sum == 0 { // Fast path, but only handles complete trees. if err := prefix.GeneratePrefixes(pc); err != nil { errors.Panic(err) // Using complete trees; should never fail } } else { // Slow path, but handles anything. pc = handleDegenerateCodes(pc) // Never fails, but may fail later codes[i] = pc } trees[i].Init(pc) } } type prefixWriter struct{ prefix.Writer } func (pw *prefixWriter) Init(w io.Writer) { pw.Writer.Init(w, true) } func (pw *prefixWriter) WriteBitsBE64(v uint64, nb uint) { if nb <= 32 { v := internal.ReverseUint32N(uint32(v), nb) pw.WriteBits(uint(v), nb) return } v <<= (64 - nb) v0 := internal.ReverseUint32(uint32(v >> 32)) v1 := internal.ReverseUint32(uint32(v)) pw.WriteBits(uint(v0), 32) pw.WriteBits(uint(v1), nb-32) return } func (pw *prefixWriter) WritePrefixCodes(codes []prefix.PrefixCodes, trees []prefix.Encoder) { for i, pc := range codes { if err := prefix.GeneratePrefixes(pc); err != nil { errors.Panic(err) // Using complete trees; should never fail } trees[i].Init(pc) clen := int(pc[0].Len) pw.WriteBitsBE64(uint64(clen), 5) for _, c := range pc { for int(c.Len) < clen { pw.WriteBits(3, 2) // 11 clen-- } for int(c.Len) > clen { pw.WriteBits(1, 2) // 10 clen++ } pw.WriteBits(0, 1) } } } // handleDegenerateCodes converts a degenerate tree into a canonical tree. // // For example, when the input is an under-subscribed tree: // input: []PrefixCode{ // {Sym: 0, Len: 3}, // {Sym: 1, Len: 4}, // {Sym: 2, Len: 3}, // } // output: []PrefixCode{ // {Sym: 0, Len: 3, Val: 0}, // 000 // {Sym: 1, Len: 4, Val: 2}, // 0010 // {Sym: 2, Len: 3, Val: 4}, // 100 // {Sym: 258, Len: 4, Val: 10}, // 1010 // {Sym: 259, Len: 3, Val: 6}, // 110 // {Sym: 260, Len: 1, Val: 1}, // 1 // } // // For example, when the input is an over-subscribed tree: // input: []PrefixCode{ // {Sym: 0, Len: 1}, // {Sym: 1, Len: 3}, // {Sym: 2, Len: 4}, // {Sym: 3, Len: 3}, // {Sym: 4, Len: 2}, // } // output: []PrefixCode{ // {Sym: 0, Len: 1, Val: 0}, // 0 // {Sym: 1, Len: 3, Val: 3}, // 011 // {Sym: 3, Len: 3, Val: 7}, // 111 // {Sym: 4, Len: 2, Val: 1}, // 01 // } func handleDegenerateCodes(codes prefix.PrefixCodes) prefix.PrefixCodes { // Since there is no formal definition for the BZip2 format, there is no // specification that says that the code lengths must form a complete // prefix tree (IE: it is neither over-subscribed nor under-subscribed). // Thus, the original C implementation becomes the reference for how prefix // decoding is done in these edge cases. Unfortunately, the C version does // not error when an invalid tree is used, but rather allows decoding to // continue and only errors if some bit pattern happens to cause an error. // Thus, it is possible for an invalid tree to end up decoding an input // "properly" so long as invalid bit patterns are not present. In order to // replicate this non-specified behavior, we use a ported version of the // C code to generate the codes as a valid canonical tree by substituting // invalid nodes with invalid symbols. // // ==================================================== // This program, "bzip2", the associated library "libbzip2", and all // documentation, are copyright (C) 1996-2010 Julian R Seward. All // rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // 2. The origin of this software must not be misrepresented; you must // not claim that you wrote the original software. If you use this // software in a product, an acknowledgment in the product // documentation would be appreciated but is not required. // // 3. Altered source versions must be plainly marked as such, and must // not be misrepresented as being the original software. // // 4. The name of the author may not be used to endorse or promote // products derived from this software without specific prior written // permission. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Julian Seward, jseward@bzip.org // bzip2/libbzip2 version 1.0.6 of 6 September 2010 // ==================================================== var ( limits [maxPrefixBits + 2]int32 bases [maxPrefixBits + 2]int32 perms [maxNumSyms]int32 minLen = uint32(maxPrefixBits) maxLen = uint32(0) ) const ( statusOkay = iota statusInvalid statusNeedBits statusMaxBits ) // createTables is the BZ2_hbCreateDecodeTables function from the C code. createTables := func(codes []prefix.PrefixCode) { for _, c := range codes { if c.Len > maxLen { maxLen = c.Len } if c.Len < minLen { minLen = c.Len } } var pp int for i := minLen; i <= maxLen; i++ { for j, c := range codes { if c.Len == i { perms[pp] = int32(j) pp++ } } } var vec int32 for _, c := range codes { bases[c.Len+1]++ } for i := 1; i < len(bases); i++ { bases[i] += bases[i-1] } for i := minLen; i <= maxLen; i++ { vec += bases[i+1] - bases[i] limits[i] = vec - 1 vec <<= 1 } for i := minLen + 1; i <= maxLen; i++ { bases[i] = ((limits[i-1] + 1) << 1) - bases[i] } } // getSymbol is the GET_MTF_VAL macro from the C code. getSymbol := func(c prefix.PrefixCode) (uint32, int) { v := internal.ReverseUint32(c.Val) n := c.Len zn := minLen if zn > n { return 0, statusNeedBits } zvec := int32(v >> (32 - zn)) v <<= zn for { if zn > maxLen { return 0, statusMaxBits } if zvec <= limits[zn] { break } zn++ if zn > n { return 0, statusNeedBits } zvec = (zvec << 1) | int32(v>>31) v <<= 1 } if zvec-bases[zn] < 0 || zvec-bases[zn] >= maxNumSyms { return 0, statusInvalid } return uint32(perms[zvec-bases[zn]]), statusOkay } // Step 1: Create the prefix trees using the C algorithm. createTables(codes) // Step 2: Starting with the shortest bit pattern, explore the whole tree. // If tree is under-subscribed, the worst-case runtime is O(1< 0 { codes = append(codes, c) } } return codes } ================================================ FILE: vendor/github.com/dsnet/compress/bzip2/reader.go ================================================ // Copyright 2015, Joe Tsai. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. package bzip2 import ( "io" "github.com/dsnet/compress/internal" "github.com/dsnet/compress/internal/errors" "github.com/dsnet/compress/internal/prefix" ) type Reader struct { InputOffset int64 // Total number of bytes read from underlying io.Reader OutputOffset int64 // Total number of bytes emitted from Read rd prefixReader err error level int // The current compression level rdHdrFtr int // Number of times we read the stream header and footer blkCRC uint32 // CRC-32 IEEE of each block (as stored) endCRC uint32 // Checksum of all blocks using bzip2's custom method crc crc mtf moveToFront bwt burrowsWheelerTransform rle runLengthEncoding // These fields are allocated with Reader and re-used later. treeSels []uint8 codes2D [maxNumTrees][maxNumSyms]prefix.PrefixCode codes1D [maxNumTrees]prefix.PrefixCodes trees1D [maxNumTrees]prefix.Decoder syms []uint16 fuzzReader // Exported functionality when fuzz testing } type ReaderConfig struct { _ struct{} // Blank field to prevent unkeyed struct literals } func NewReader(r io.Reader, conf *ReaderConfig) (*Reader, error) { zr := new(Reader) zr.Reset(r) return zr, nil } func (zr *Reader) Reset(r io.Reader) error { *zr = Reader{ rd: zr.rd, mtf: zr.mtf, bwt: zr.bwt, rle: zr.rle, treeSels: zr.treeSels, trees1D: zr.trees1D, syms: zr.syms, } zr.rd.Init(r) return nil } func (zr *Reader) Read(buf []byte) (int, error) { for { cnt, err := zr.rle.Read(buf) if err != rleDone && zr.err == nil { zr.err = err } if cnt > 0 { zr.crc.update(buf[:cnt]) zr.OutputOffset += int64(cnt) return cnt, nil } if zr.err != nil || len(buf) == 0 { return 0, zr.err } // Read the next chunk. zr.rd.Offset = zr.InputOffset func() { defer errors.Recover(&zr.err) if zr.rdHdrFtr%2 == 0 { // Check if we are already at EOF. if err := zr.rd.PullBits(1); err != nil { if err == io.ErrUnexpectedEOF && zr.rdHdrFtr > 0 { err = io.EOF // EOF is okay if we read at least one stream } errors.Panic(err) } // Read stream header. if zr.rd.ReadBitsBE64(16) != hdrMagic { panicf(errors.Corrupted, "invalid stream magic") } if ver := zr.rd.ReadBitsBE64(8); ver != 'h' { if ver == '0' { panicf(errors.Deprecated, "bzip1 format is not supported") } panicf(errors.Corrupted, "invalid version: %q", ver) } lvl := int(zr.rd.ReadBitsBE64(8)) - '0' if lvl < BestSpeed || lvl > BestCompression { panicf(errors.Corrupted, "invalid block size: %d", lvl*blockSize) } zr.level = lvl zr.rdHdrFtr++ } else { // Check and update the CRC. if internal.GoFuzz { zr.updateChecksum(-1, zr.crc.val) // Update with value zr.blkCRC = zr.crc.val // Suppress CRC failures } if zr.blkCRC != zr.crc.val { panicf(errors.Corrupted, "mismatching block checksum") } zr.endCRC = (zr.endCRC<<1 | zr.endCRC>>31) ^ zr.blkCRC } buf := zr.decodeBlock() zr.rle.Init(buf) }() if zr.InputOffset, err = zr.rd.Flush(); zr.err == nil { zr.err = err } if zr.err != nil { zr.err = errWrap(zr.err, errors.Corrupted) return 0, zr.err } } } func (zr *Reader) Close() error { if zr.err == io.EOF || zr.err == errClosed { zr.rle.Init(nil) // Make sure future reads fail zr.err = errClosed return nil } return zr.err // Return the persistent error } func (zr *Reader) decodeBlock() []byte { if magic := zr.rd.ReadBitsBE64(48); magic != blkMagic { if magic == endMagic { endCRC := uint32(zr.rd.ReadBitsBE64(32)) if internal.GoFuzz { zr.updateChecksum(zr.rd.BitsRead()-32, zr.endCRC) endCRC = zr.endCRC // Suppress CRC failures } if zr.endCRC != endCRC { panicf(errors.Corrupted, "mismatching stream checksum") } zr.endCRC = 0 zr.rd.ReadPads() zr.rdHdrFtr++ return nil } panicf(errors.Corrupted, "invalid block or footer magic") } zr.crc.val = 0 zr.blkCRC = uint32(zr.rd.ReadBitsBE64(32)) if internal.GoFuzz { zr.updateChecksum(zr.rd.BitsRead()-32, 0) // Record offset only } if zr.rd.ReadBitsBE64(1) != 0 { panicf(errors.Deprecated, "block randomization is not supported") } // Read BWT related fields. ptr := int(zr.rd.ReadBitsBE64(24)) // BWT origin pointer // Read MTF related fields. var dictArr [256]uint8 dict := dictArr[:0] bmapHi := uint16(zr.rd.ReadBits(16)) for i := 0; i < 256; i, bmapHi = i+16, bmapHi>>1 { if bmapHi&1 > 0 { bmapLo := uint16(zr.rd.ReadBits(16)) for j := 0; j < 16; j, bmapLo = j+1, bmapLo>>1 { if bmapLo&1 > 0 { dict = append(dict, uint8(i+j)) } } } } // Step 1: Prefix encoding. syms := zr.decodePrefix(len(dict)) // Step 2: Move-to-front transform and run-length encoding. zr.mtf.Init(dict, zr.level*blockSize) buf := zr.mtf.Decode(syms) // Step 3: Burrows-Wheeler transformation. if ptr >= len(buf) { panicf(errors.Corrupted, "origin pointer (0x%06x) exceeds block size: %d", ptr, len(buf)) } zr.bwt.Decode(buf, ptr) return buf } func (zr *Reader) decodePrefix(numSyms int) (syms []uint16) { numSyms += 2 // Remove 0 symbol, add RUNA, RUNB, and EOF symbols if numSyms < 3 { panicf(errors.Corrupted, "not enough prefix symbols: %d", numSyms) } // Read information about the trees and tree selectors. var mtf internal.MoveToFront numTrees := int(zr.rd.ReadBitsBE64(3)) if numTrees < minNumTrees || numTrees > maxNumTrees { panicf(errors.Corrupted, "invalid number of prefix trees: %d", numTrees) } numSels := int(zr.rd.ReadBitsBE64(15)) if cap(zr.treeSels) < numSels { zr.treeSels = make([]uint8, numSels) } treeSels := zr.treeSels[:numSels] for i := range treeSels { sym, ok := zr.rd.TryReadSymbol(&decSel) if !ok { sym = zr.rd.ReadSymbol(&decSel) } if int(sym) >= numTrees { panicf(errors.Corrupted, "invalid prefix tree selector: %d", sym) } treeSels[i] = uint8(sym) } mtf.Decode(treeSels) zr.treeSels = treeSels // Initialize prefix codes. for i := range zr.codes2D[:numTrees] { zr.codes1D[i] = zr.codes2D[i][:numSyms] } zr.rd.ReadPrefixCodes(zr.codes1D[:numTrees], zr.trees1D[:numTrees]) // Read prefix encoded symbols of compressed data. var tree *prefix.Decoder var blkLen, selIdx int syms = zr.syms[:0] for { if blkLen == 0 { blkLen = numBlockSyms if selIdx >= len(treeSels) { panicf(errors.Corrupted, "not enough prefix tree selectors") } tree = &zr.trees1D[treeSels[selIdx]] selIdx++ } blkLen-- sym, ok := zr.rd.TryReadSymbol(tree) if !ok { sym = zr.rd.ReadSymbol(tree) } if int(sym) == numSyms-1 { break // EOF marker } if int(sym) >= numSyms { panicf(errors.Corrupted, "invalid prefix symbol: %d", sym) } if len(syms) >= zr.level*blockSize { panicf(errors.Corrupted, "number of prefix symbols exceeds block size") } syms = append(syms, uint16(sym)) } zr.syms = syms return syms } ================================================ FILE: vendor/github.com/dsnet/compress/bzip2/rle1.go ================================================ // Copyright 2015, Joe Tsai. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. package bzip2 import "github.com/dsnet/compress/internal/errors" // rleDone is a special "error" to indicate that the RLE stage is done. var rleDone = errorf(errors.Unknown, "RLE1 stage is completed") // runLengthEncoding implements the first RLE stage of bzip2. Every sequence // of 4..255 duplicated bytes is replaced by only the first 4 bytes, and a // single byte representing the repeat length. Similar to the C bzip2 // implementation, the encoder will always terminate repeat sequences with a // count (even if it is the end of the buffer), and it will also never produce // run lengths of 256..259. The decoder can handle the latter case. // // For example, if the input was: // input: "AAAAAAABBBBCCCD" // // Then the output will be: // output: "AAAA\x03BBBB\x00CCCD" type runLengthEncoding struct { buf []byte idx int lastVal byte lastCnt int } func (rle *runLengthEncoding) Init(buf []byte) { *rle = runLengthEncoding{buf: buf} } func (rle *runLengthEncoding) Write(buf []byte) (int, error) { for i, b := range buf { if rle.lastVal != b { rle.lastCnt = 0 } rle.lastCnt++ switch { case rle.lastCnt < 4: if rle.idx >= len(rle.buf) { return i, rleDone } rle.buf[rle.idx] = b rle.idx++ case rle.lastCnt == 4: if rle.idx+1 >= len(rle.buf) { return i, rleDone } rle.buf[rle.idx] = b rle.idx++ rle.buf[rle.idx] = 0 rle.idx++ case rle.lastCnt < 256: rle.buf[rle.idx-1]++ default: if rle.idx >= len(rle.buf) { return i, rleDone } rle.lastCnt = 1 rle.buf[rle.idx] = b rle.idx++ } rle.lastVal = b } return len(buf), nil } func (rle *runLengthEncoding) Read(buf []byte) (int, error) { for i := range buf { switch { case rle.lastCnt == -4: if rle.idx >= len(rle.buf) { return i, errorf(errors.Corrupted, "missing terminating run-length repeater") } rle.lastCnt = int(rle.buf[rle.idx]) rle.idx++ if rle.lastCnt > 0 { break // Break the switch } fallthrough // Count was zero, continue the work case rle.lastCnt <= 0: if rle.idx >= len(rle.buf) { return i, rleDone } b := rle.buf[rle.idx] rle.idx++ if b != rle.lastVal { rle.lastCnt = 0 rle.lastVal = b } } buf[i] = rle.lastVal rle.lastCnt-- } return len(buf), nil } func (rle *runLengthEncoding) Bytes() []byte { return rle.buf[:rle.idx] } ================================================ FILE: vendor/github.com/dsnet/compress/bzip2/writer.go ================================================ // Copyright 2015, Joe Tsai. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. package bzip2 import ( "io" "github.com/dsnet/compress/internal" "github.com/dsnet/compress/internal/errors" "github.com/dsnet/compress/internal/prefix" ) type Writer struct { InputOffset int64 // Total number of bytes issued to Write OutputOffset int64 // Total number of bytes written to underlying io.Writer wr prefixWriter err error level int // The current compression level wrHdr bool // Have we written the stream header? blkCRC uint32 // CRC-32 IEEE of each block endCRC uint32 // Checksum of all blocks using bzip2's custom method crc crc rle runLengthEncoding bwt burrowsWheelerTransform mtf moveToFront // These fields are allocated with Writer and re-used later. buf []byte treeSels []uint8 treeSelsMTF []uint8 codes2D [maxNumTrees][maxNumSyms]prefix.PrefixCode codes1D [maxNumTrees]prefix.PrefixCodes trees1D [maxNumTrees]prefix.Encoder } type WriterConfig struct { Level int _ struct{} // Blank field to prevent unkeyed struct literals } func NewWriter(w io.Writer, conf *WriterConfig) (*Writer, error) { var lvl int if conf != nil { lvl = conf.Level } if lvl == 0 { lvl = DefaultCompression } if lvl < BestSpeed || lvl > BestCompression { return nil, errorf(errors.Invalid, "compression level: %d", lvl) } zw := new(Writer) zw.level = lvl zw.Reset(w) return zw, nil } func (zw *Writer) Reset(w io.Writer) error { *zw = Writer{ wr: zw.wr, level: zw.level, rle: zw.rle, bwt: zw.bwt, mtf: zw.mtf, buf: zw.buf, treeSels: zw.treeSels, treeSelsMTF: zw.treeSelsMTF, trees1D: zw.trees1D, } zw.wr.Init(w) if len(zw.buf) != zw.level*blockSize { zw.buf = make([]byte, zw.level*blockSize) } zw.rle.Init(zw.buf) return nil } func (zw *Writer) Write(buf []byte) (int, error) { if zw.err != nil { return 0, zw.err } cnt := len(buf) for { wrCnt, err := zw.rle.Write(buf) if err != rleDone && zw.err == nil { zw.err = err } zw.crc.update(buf[:wrCnt]) buf = buf[wrCnt:] if len(buf) == 0 { zw.InputOffset += int64(cnt) return cnt, nil } if zw.err = zw.flush(); zw.err != nil { return 0, zw.err } } } func (zw *Writer) flush() error { vals := zw.rle.Bytes() if len(vals) == 0 { return nil } zw.wr.Offset = zw.OutputOffset func() { defer errors.Recover(&zw.err) if !zw.wrHdr { // Write stream header. zw.wr.WriteBitsBE64(hdrMagic, 16) zw.wr.WriteBitsBE64('h', 8) zw.wr.WriteBitsBE64(uint64('0'+zw.level), 8) zw.wrHdr = true } zw.encodeBlock(vals) }() var err error if zw.OutputOffset, err = zw.wr.Flush(); zw.err == nil { zw.err = err } if zw.err != nil { zw.err = errWrap(zw.err, errors.Internal) return zw.err } zw.endCRC = (zw.endCRC<<1 | zw.endCRC>>31) ^ zw.blkCRC zw.blkCRC = 0 zw.rle.Init(zw.buf) return nil } func (zw *Writer) Close() error { if zw.err == errClosed { return nil } // Flush RLE buffer if there is left-over data. if zw.err = zw.flush(); zw.err != nil { return zw.err } // Write stream footer. zw.wr.Offset = zw.OutputOffset func() { defer errors.Recover(&zw.err) if !zw.wrHdr { // Write stream header. zw.wr.WriteBitsBE64(hdrMagic, 16) zw.wr.WriteBitsBE64('h', 8) zw.wr.WriteBitsBE64(uint64('0'+zw.level), 8) zw.wrHdr = true } zw.wr.WriteBitsBE64(endMagic, 48) zw.wr.WriteBitsBE64(uint64(zw.endCRC), 32) zw.wr.WritePads(0) }() var err error if zw.OutputOffset, err = zw.wr.Flush(); zw.err == nil { zw.err = err } if zw.err != nil { zw.err = errWrap(zw.err, errors.Internal) return zw.err } zw.err = errClosed return nil } func (zw *Writer) encodeBlock(buf []byte) { zw.blkCRC = zw.crc.val zw.wr.WriteBitsBE64(blkMagic, 48) zw.wr.WriteBitsBE64(uint64(zw.blkCRC), 32) zw.wr.WriteBitsBE64(0, 1) zw.crc.val = 0 // Step 1: Burrows-Wheeler transformation. ptr := zw.bwt.Encode(buf) zw.wr.WriteBitsBE64(uint64(ptr), 24) // Step 2: Move-to-front transform and run-length encoding. var dictMap [256]bool for _, c := range buf { dictMap[c] = true } var dictArr [256]uint8 var bmapLo [16]uint16 dict := dictArr[:0] bmapHi := uint16(0) for i, b := range dictMap { if b { c := uint8(i) dict = append(dict, c) bmapHi |= 1 << (c >> 4) bmapLo[c>>4] |= 1 << (c & 0xf) } } zw.wr.WriteBits(uint(bmapHi), 16) for _, m := range bmapLo { if m > 0 { zw.wr.WriteBits(uint(m), 16) } } zw.mtf.Init(dict, len(buf)) syms := zw.mtf.Encode(buf) // Step 3: Prefix encoding. zw.encodePrefix(syms, len(dict)) } func (zw *Writer) encodePrefix(syms []uint16, numSyms int) { numSyms += 2 // Remove 0 symbol, add RUNA, RUNB, and EOB symbols if numSyms < 3 { panicf(errors.Internal, "unable to encode EOB marker") } syms = append(syms, uint16(numSyms-1)) // EOB marker // Compute number of prefix trees needed. numTrees := maxNumTrees for i, lim := range []int{200, 600, 1200, 2400} { if len(syms) < lim { numTrees = minNumTrees + i break } } // Compute number of block selectors. numSels := (len(syms) + numBlockSyms - 1) / numBlockSyms if cap(zw.treeSels) < numSels { zw.treeSels = make([]uint8, numSels) } treeSels := zw.treeSels[:numSels] for i := range treeSels { treeSels[i] = uint8(i % numTrees) } // Initialize prefix codes. for i := range zw.codes2D[:numTrees] { pc := zw.codes2D[i][:numSyms] for j := range pc { pc[j] = prefix.PrefixCode{Sym: uint32(j)} } zw.codes1D[i] = pc } // First cut at assigning prefix trees to each group. var codes prefix.PrefixCodes var blkLen, selIdx int for _, sym := range syms { if blkLen == 0 { blkLen = numBlockSyms codes = zw.codes2D[treeSels[selIdx]][:numSyms] selIdx++ } blkLen-- codes[sym].Cnt++ } // TODO(dsnet): Use K-means to cluster groups to each prefix tree. // Generate lengths and prefixes based on symbol frequencies. for i := range zw.trees1D[:numTrees] { pc := prefix.PrefixCodes(zw.codes2D[i][:numSyms]) pc.SortByCount() if err := prefix.GenerateLengths(pc, maxPrefixBits); err != nil { errors.Panic(err) } pc.SortBySymbol() } // Write out information about the trees and tree selectors. var mtf internal.MoveToFront zw.wr.WriteBitsBE64(uint64(numTrees), 3) zw.wr.WriteBitsBE64(uint64(numSels), 15) zw.treeSelsMTF = append(zw.treeSelsMTF[:0], treeSels...) mtf.Encode(zw.treeSelsMTF) for _, sym := range zw.treeSelsMTF { zw.wr.WriteSymbol(uint(sym), &encSel) } zw.wr.WritePrefixCodes(zw.codes1D[:numTrees], zw.trees1D[:numTrees]) // Write out prefix encoded symbols of compressed data. var tree *prefix.Encoder blkLen, selIdx = 0, 0 for _, sym := range syms { if blkLen == 0 { blkLen = numBlockSyms tree = &zw.trees1D[treeSels[selIdx]] selIdx++ } blkLen-- ok := zw.wr.TryWriteSymbol(uint(sym), tree) if !ok { zw.wr.WriteSymbol(uint(sym), tree) } } } ================================================ FILE: vendor/github.com/dsnet/compress/internal/common.go ================================================ // Copyright 2015, Joe Tsai. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. // Package internal is a collection of common compression algorithms. // // For performance reasons, these packages lack strong error checking and // require that the caller to ensure that strict invariants are kept. package internal var ( // IdentityLUT returns the input key itself. IdentityLUT = func() (lut [256]byte) { for i := range lut { lut[i] = uint8(i) } return lut }() // ReverseLUT returns the input key with its bits reversed. ReverseLUT = func() (lut [256]byte) { for i := range lut { b := uint8(i) b = (b&0xaa)>>1 | (b&0x55)<<1 b = (b&0xcc)>>2 | (b&0x33)<<2 b = (b&0xf0)>>4 | (b&0x0f)<<4 lut[i] = b } return lut }() ) // ReverseUint32 reverses all bits of v. func ReverseUint32(v uint32) (x uint32) { x |= uint32(ReverseLUT[byte(v>>0)]) << 24 x |= uint32(ReverseLUT[byte(v>>8)]) << 16 x |= uint32(ReverseLUT[byte(v>>16)]) << 8 x |= uint32(ReverseLUT[byte(v>>24)]) << 0 return x } // ReverseUint32N reverses the lower n bits of v. func ReverseUint32N(v uint32, n uint) (x uint32) { return ReverseUint32(v << (32 - n)) } // ReverseUint64 reverses all bits of v. func ReverseUint64(v uint64) (x uint64) { x |= uint64(ReverseLUT[byte(v>>0)]) << 56 x |= uint64(ReverseLUT[byte(v>>8)]) << 48 x |= uint64(ReverseLUT[byte(v>>16)]) << 40 x |= uint64(ReverseLUT[byte(v>>24)]) << 32 x |= uint64(ReverseLUT[byte(v>>32)]) << 24 x |= uint64(ReverseLUT[byte(v>>40)]) << 16 x |= uint64(ReverseLUT[byte(v>>48)]) << 8 x |= uint64(ReverseLUT[byte(v>>56)]) << 0 return x } // ReverseUint64N reverses the lower n bits of v. func ReverseUint64N(v uint64, n uint) (x uint64) { return ReverseUint64(v << (64 - n)) } // MoveToFront is a data structure that allows for more efficient move-to-front // transformations. This specific implementation assumes that the alphabet is // densely packed within 0..255. type MoveToFront struct { dict [256]uint8 // Mapping from indexes to values tail int // Number of tail bytes that are already ordered } func (m *MoveToFront) Encode(vals []uint8) { copy(m.dict[:], IdentityLUT[:256-m.tail]) // Reset dict to be identity var max int for i, val := range vals { var idx uint8 // Reverse lookup idx in dict for di, dv := range m.dict { if dv == val { idx = uint8(di) break } } vals[i] = idx max |= int(idx) copy(m.dict[1:], m.dict[:idx]) m.dict[0] = val } m.tail = 256 - max - 1 } func (m *MoveToFront) Decode(idxs []uint8) { copy(m.dict[:], IdentityLUT[:256-m.tail]) // Reset dict to be identity var max int for i, idx := range idxs { val := m.dict[idx] // Forward lookup val in dict idxs[i] = val max |= int(idx) copy(m.dict[1:], m.dict[:idx]) m.dict[0] = val } m.tail = 256 - max - 1 } ================================================ FILE: vendor/github.com/dsnet/compress/internal/debug.go ================================================ // Copyright 2015, Joe Tsai. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. // +build debug,!gofuzz package internal const ( Debug = true GoFuzz = false ) ================================================ FILE: vendor/github.com/dsnet/compress/internal/errors/errors.go ================================================ // Copyright 2016, Joe Tsai. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. // Package errors implements functions to manipulate compression errors. // // In idiomatic Go, it is an anti-pattern to use panics as a form of error // reporting in the API. Instead, the expected way to transmit errors is by // returning an error value. Unfortunately, the checking of "err != nil" in // tight loops commonly found in compression causes non-negligible performance // degradation. While this may not be idiomatic, the internal packages of this // repository rely on panics as a normal means to convey errors. In order to // ensure that these panics do not leak across the public API, the public // packages must recover from these panics and present an error value. // // The Panic and Recover functions in this package provide a safe way to // recover from errors only generated from within this repository. // // Example usage: // func Foo() (err error) { // defer errors.Recover(&err) // // if rand.Intn(2) == 0 { // // Unexpected panics will not be caught by Recover. // io.Closer(nil).Close() // } else { // // Errors thrown by Panic will be caught by Recover. // errors.Panic(errors.New("whoopsie")) // } // } // package errors import "strings" const ( // Unknown indicates that there is no classification for this error. Unknown = iota // Internal indicates that this error is due to an internal bug. // Users should file a issue report if this type of error is encountered. Internal // Invalid indicates that this error is due to the user misusing the API // and is indicative of a bug on the user's part. Invalid // Deprecated indicates the use of a deprecated and unsupported feature. Deprecated // Corrupted indicates that the input stream is corrupted. Corrupted // Closed indicates that the handlers are closed. Closed ) var codeMap = map[int]string{ Unknown: "unknown error", Internal: "internal error", Invalid: "invalid argument", Deprecated: "deprecated format", Corrupted: "corrupted input", Closed: "closed handler", } type Error struct { Code int // The error type Pkg string // Name of the package where the error originated Msg string // Descriptive message about the error (optional) } func (e Error) Error() string { var ss []string for _, s := range []string{e.Pkg, codeMap[e.Code], e.Msg} { if s != "" { ss = append(ss, s) } } return strings.Join(ss, ": ") } func (e Error) CompressError() {} func (e Error) IsInternal() bool { return e.Code == Internal } func (e Error) IsInvalid() bool { return e.Code == Invalid } func (e Error) IsDeprecated() bool { return e.Code == Deprecated } func (e Error) IsCorrupted() bool { return e.Code == Corrupted } func (e Error) IsClosed() bool { return e.Code == Closed } func IsInternal(err error) bool { return isCode(err, Internal) } func IsInvalid(err error) bool { return isCode(err, Invalid) } func IsDeprecated(err error) bool { return isCode(err, Deprecated) } func IsCorrupted(err error) bool { return isCode(err, Corrupted) } func IsClosed(err error) bool { return isCode(err, Closed) } func isCode(err error, code int) bool { if cerr, ok := err.(Error); ok && cerr.Code == code { return true } return false } // errWrap is used by Panic and Recover to ensure that only errors raised by // Panic are recovered by Recover. type errWrap struct{ e *error } func Recover(err *error) { switch ex := recover().(type) { case nil: // Do nothing. case errWrap: *err = *ex.e default: panic(ex) } } func Panic(err error) { panic(errWrap{&err}) } ================================================ FILE: vendor/github.com/dsnet/compress/internal/gofuzz.go ================================================ // Copyright 2016, Joe Tsai. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. // +build gofuzz package internal const ( Debug = true GoFuzz = true ) ================================================ FILE: vendor/github.com/dsnet/compress/internal/prefix/debug.go ================================================ // Copyright 2015, Joe Tsai. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. // +build debug package prefix import ( "fmt" "math" "strings" ) func max(a, b int) int { if a > b { return a } return b } func lenBase2(n uint) int { return int(math.Ceil(math.Log2(float64(n + 1)))) } func padBase2(v, n uint, m int) string { s := fmt.Sprintf("%b", 1< 0 { return strings.Repeat(" ", pad) + s } return s } func lenBase10(n int) int { return int(math.Ceil(math.Log10(float64(n + 1)))) } func padBase10(n, m int) string { s := fmt.Sprintf("%d", n) if pad := m - len(s); pad > 0 { return strings.Repeat(" ", pad) + s } return s } func (rc RangeCodes) String() string { var maxLen, maxBase int for _, c := range rc { maxLen = max(maxLen, int(c.Len)) maxBase = max(maxBase, int(c.Base)) } var ss []string ss = append(ss, "{") for i, c := range rc { base := padBase10(int(c.Base), lenBase10(maxBase)) if c.Len > 0 { base += fmt.Sprintf("-%d", c.End()-1) } ss = append(ss, fmt.Sprintf("\t%s: {len: %s, range: %s},", padBase10(int(i), lenBase10(len(rc)-1)), padBase10(int(c.Len), lenBase10(maxLen)), base, )) } ss = append(ss, "}") return strings.Join(ss, "\n") } func (pc PrefixCodes) String() string { var maxSym, maxLen, maxCnt int for _, c := range pc { maxSym = max(maxSym, int(c.Sym)) maxLen = max(maxLen, int(c.Len)) maxCnt = max(maxCnt, int(c.Cnt)) } var ss []string ss = append(ss, "{") for _, c := range pc { var cntStr string if maxCnt > 0 { cnt := int(32*float32(c.Cnt)/float32(maxCnt) + 0.5) cntStr = fmt.Sprintf("%s |%s", padBase10(int(c.Cnt), lenBase10(maxCnt)), strings.Repeat("#", cnt), ) } ss = append(ss, fmt.Sprintf("\t%s: %s, %s", padBase10(int(c.Sym), lenBase10(maxSym)), padBase2(uint(c.Val), uint(c.Len), maxLen), cntStr, )) } ss = append(ss, "}") return strings.Join(ss, "\n") } func (pd Decoder) String() string { var ss []string ss = append(ss, "{") if len(pd.chunks) > 0 { ss = append(ss, "\tchunks: {") for i, c := range pd.chunks { label := "sym" if uint(c&countMask) > uint(pd.chunkBits) { label = "idx" } ss = append(ss, fmt.Sprintf("\t\t%s: {%s: %s, len: %s}", padBase2(uint(i), uint(pd.chunkBits), int(pd.chunkBits)), label, padBase10(int(c>>countBits), 3), padBase10(int(c&countMask), 2), )) } ss = append(ss, "\t},") for j, links := range pd.links { ss = append(ss, fmt.Sprintf("\tlinks[%d]: {", j)) linkBits := lenBase2(uint(pd.linkMask)) for i, c := range links { ss = append(ss, fmt.Sprintf("\t\t%s: {sym: %s, len: %s},", padBase2(uint(i), uint(linkBits), int(linkBits)), padBase10(int(c>>countBits), 3), padBase10(int(c&countMask), 2), )) } ss = append(ss, "\t},") } } ss = append(ss, fmt.Sprintf("\tchunkMask: %b,", pd.chunkMask)) ss = append(ss, fmt.Sprintf("\tlinkMask: %b,", pd.linkMask)) ss = append(ss, fmt.Sprintf("\tchunkBits: %d,", pd.chunkBits)) ss = append(ss, fmt.Sprintf("\tMinBits: %d,", pd.MinBits)) ss = append(ss, fmt.Sprintf("\tNumSyms: %d,", pd.NumSyms)) ss = append(ss, "}") return strings.Join(ss, "\n") } func (pe Encoder) String() string { var maxLen int for _, c := range pe.chunks { maxLen = max(maxLen, int(c&countMask)) } var ss []string ss = append(ss, "{") if len(pe.chunks) > 0 { ss = append(ss, "\tchunks: {") for i, c := range pe.chunks { ss = append(ss, fmt.Sprintf("\t\t%s: %s,", padBase10(i, 3), padBase2(uint(c>>countBits), uint(c&countMask), maxLen), )) } ss = append(ss, "\t},") } ss = append(ss, fmt.Sprintf("\tchunkMask: %b,", pe.chunkMask)) ss = append(ss, fmt.Sprintf("\tNumSyms: %d,", pe.NumSyms)) ss = append(ss, "}") return strings.Join(ss, "\n") } ================================================ FILE: vendor/github.com/dsnet/compress/internal/prefix/decoder.go ================================================ // Copyright 2015, Joe Tsai. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. package prefix import ( "sort" "github.com/dsnet/compress/internal" ) // The algorithm used to decode variable length codes is based on the lookup // method in zlib. If the code is less-than-or-equal to maxChunkBits, // then the symbol can be decoded using a single lookup into the chunks table. // Otherwise, the links table will be used for a second level lookup. // // The chunks slice is keyed by the contents of the bit buffer ANDed with // the chunkMask to avoid a out-of-bounds lookup. The value of chunks is a tuple // that is decoded as follow: // // var length = chunks[bitBuffer&chunkMask] & countMask // var symbol = chunks[bitBuffer&chunkMask] >> countBits // // If the decoded length is larger than chunkBits, then an overflow link table // must be used for further decoding. In this case, the symbol is actually the // index into the links tables. The second-level links table returned is // processed in the same way as the chunks table. // // if length > chunkBits { // var index = symbol // Previous symbol is index into links tables // length = links[index][bitBuffer>>chunkBits & linkMask] & countMask // symbol = links[index][bitBuffer>>chunkBits & linkMask] >> countBits // } // // See the following: // http://www.gzip.org/algorithm.txt type Decoder struct { chunks []uint32 // First-level lookup map links [][]uint32 // Second-level lookup map chunkMask uint32 // Mask the length of the chunks table linkMask uint32 // Mask the length of the link table chunkBits uint32 // Bit-length of the chunks table MinBits uint32 // The minimum number of bits to safely make progress NumSyms uint32 // Number of symbols } // Init initializes Decoder according to the codes provided. func (pd *Decoder) Init(codes PrefixCodes) { // Handle special case trees. if len(codes) <= 1 { switch { case len(codes) == 0: // Empty tree (should error if used later) *pd = Decoder{chunks: pd.chunks[:0], links: pd.links[:0], NumSyms: 0} case len(codes) == 1 && codes[0].Len == 0: // Single code tree (bit-length of zero) pd.chunks = append(pd.chunks[:0], codes[0].Sym< c.Len { minBits = c.Len } if maxBits < c.Len { maxBits = c.Len } } // Allocate chunks table as needed. const maxChunkBits = 9 // This can be tuned for better performance pd.NumSyms = uint32(len(codes)) pd.MinBits = minBits pd.chunkBits = maxBits if pd.chunkBits > maxChunkBits { pd.chunkBits = maxChunkBits } numChunks := 1 << pd.chunkBits pd.chunks = allocUint32s(pd.chunks, numChunks) pd.chunkMask = uint32(numChunks - 1) // Allocate links tables as needed. pd.links = pd.links[:0] pd.linkMask = 0 if pd.chunkBits < maxBits { numLinks := 1 << (maxBits - pd.chunkBits) pd.linkMask = uint32(numLinks - 1) var linkIdx uint32 for i := range pd.chunks { pd.chunks[i] = 0 // Logic below relies on zero value as uninitialized } for _, c := range codes { if c.Len > pd.chunkBits && pd.chunks[c.Val&pd.chunkMask] == 0 { pd.chunks[c.Val&pd.chunkMask] = (linkIdx << countBits) | (pd.chunkBits + 1) linkIdx++ } } pd.links = extendSliceUint32s(pd.links, int(linkIdx)) linksFlat := allocUint32s(pd.links[0], numLinks*int(linkIdx)) for i, j := 0, 0; i < len(pd.links); i, j = i+1, j+numLinks { pd.links[i] = linksFlat[j : j+numLinks] } } // Fill out chunks and links tables with values. for _, c := range codes { chunk := c.Sym<> countBits links := pd.links[linkIdx] skip := 1 << uint(c.Len-pd.chunkBits) for j := int(c.Val >> pd.chunkBits); j < len(links); j += skip { links[j] = chunk } } } } ================================================ FILE: vendor/github.com/dsnet/compress/internal/prefix/encoder.go ================================================ // Copyright 2015, Joe Tsai. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. package prefix import ( "sort" "github.com/dsnet/compress/internal" ) type Encoder struct { chunks []uint32 // First-level lookup map chunkMask uint32 // Mask the length of the chunks table NumSyms uint32 // Number of symbols } // Init initializes Encoder according to the codes provided. func (pe *Encoder) Init(codes PrefixCodes) { // Handle special case trees. if len(codes) <= 1 { switch { case len(codes) == 0: // Empty tree (should error if used later) *pe = Encoder{chunks: pe.chunks[:0], NumSyms: 0} case len(codes) == 1 && codes[0].Len == 0: // Single code tree (bit-length of zero) pe.chunks = append(pe.chunks[:0], codes[0].Val< 0; n >>= 1 { numChunks <<= 1 } pe.NumSyms = uint32(len(codes)) retry: // Allocate and reset chunks. pe.chunks = allocUint32s(pe.chunks, numChunks) pe.chunkMask = uint32(numChunks - 1) for i := range pe.chunks { pe.chunks[i] = 0 // Logic below relies on zero value as uninitialized } // Insert each symbol, checking that there are no conflicts. for _, c := range codes { if pe.chunks[c.Sym&pe.chunkMask] > 0 { // Collision found our "hash" table, so grow and try again. numChunks <<= 1 goto retry } pe.chunks[c.Sym&pe.chunkMask] = c.Val<> uint(c.Len) } return sum == 0 || len(pc) == 0 } // checkPrefixes reports whether all codes have non-overlapping prefixes. func (pc PrefixCodes) checkPrefixes() bool { for i, c1 := range pc { for j, c2 := range pc { mask := uint32(1)< 0 { c.Val = internal.ReverseUint32N(c.Val, uint(c.Len)) if vals[c.Len].Cnt > 0 && vals[c.Len].Val+1 != c.Val { return false } vals[c.Len].Val = c.Val vals[c.Len].Cnt++ } } // Rule 2. var last PrefixCode for _, v := range vals { if v.Cnt > 0 { curVal := v.Val - v.Cnt + 1 if last.Cnt != 0 && last.Val >= curVal { return false } last = v } } return true } // GenerateLengths assigns non-zero bit-lengths to all codes. Codes with high // frequency counts will be assigned shorter codes to reduce bit entropy. // This function is used primarily by compressors. // // The input codes must have the Cnt field populated, be sorted by count. // Even if a code has a count of 0, a non-zero bit-length will be assigned. // // The result will have the Len field populated. The algorithm used guarantees // that Len <= maxBits and that it is a complete prefix tree. The resulting // codes will remain sorted by count. func GenerateLengths(codes PrefixCodes, maxBits uint) error { if len(codes) <= 1 { if len(codes) == 1 { codes[0].Len = 0 } return nil } // Verify that the codes are in ascending order by count. cntLast := codes[0].Cnt for _, c := range codes[1:] { if c.Cnt < cntLast { return errorf(errors.Invalid, "non-monotonically increasing symbol counts") } cntLast = c.Cnt } // Construct a Huffman tree used to generate the bit-lengths. // // The Huffman tree is a binary tree where each symbol lies as a leaf node // on this tree. The length of the prefix code to assign is the depth of // that leaf from the root. The Huffman algorithm, which runs in O(n), // is used to generate the tree. It assumes that codes are sorted in // increasing order of frequency. // // The algorithm is as follows: // 1. Start with two queues, F and Q, where F contains all of the starting // symbols sorted such that symbols with lowest counts come first. // 2. While len(F)+len(Q) > 1: // 2a. Dequeue the node from F or Q that has the lowest weight as N0. // 2b. Dequeue the node from F or Q that has the lowest weight as N1. // 2c. Create a new node N that has N0 and N1 as its children. // 2d. Enqueue N into the back of Q. // 3. The tree's root node is Q[0]. type node struct { cnt uint32 // n0 or c0 represent the left child of this node. // Since Go does not have unions, only one of these will be set. // Similarly, n1 or c1 represent the right child of this node. // // If n0 or n1 is set, then it represents a "pointer" to another // node in the Huffman tree. Since Go's pointer analysis cannot reason // that these node pointers do not escape (golang.org/issue/13493), // we use an index to a node in the nodes slice as a pseudo-pointer. // // If c0 or c1 is set, then it represents a leaf "node" in the // Huffman tree. The leaves are the PrefixCode values themselves. n0, n1 int // Index to child nodes c0, c1 *PrefixCode } var nodeIdx int var nodeArr [1024]node // Large enough to handle most cases on the stack nodes := nodeArr[:] if len(nodes) < len(codes) { nodes = make([]node, len(codes)) // Number of internal nodes < number of leaves } freqs, queue := codes, nodes[:0] for len(freqs)+len(queue) > 1 { // These are the two smallest nodes at the front of freqs and queue. var n node if len(queue) == 0 || (len(freqs) > 0 && freqs[0].Cnt <= queue[0].cnt) { n.c0, freqs = &freqs[0], freqs[1:] n.cnt += n.c0.Cnt } else { n.cnt += queue[0].cnt n.n0 = nodeIdx // nodeIdx is same as &queue[0] - &nodes[0] nodeIdx++ queue = queue[1:] } if len(queue) == 0 || (len(freqs) > 0 && freqs[0].Cnt <= queue[0].cnt) { n.c1, freqs = &freqs[0], freqs[1:] n.cnt += n.c1.Cnt } else { n.cnt += queue[0].cnt n.n1 = nodeIdx // nodeIdx is same as &queue[0] - &nodes[0] nodeIdx++ queue = queue[1:] } queue = append(queue, n) } rootIdx := nodeIdx // Search the whole binary tree, noting when we hit each leaf node. // We do not care about the exact Huffman tree structure, but rather we only // care about depth of each of the leaf nodes. That is, the depth determines // how long each symbol is in bits. // // Since the number of leaves is n, there is at most n internal nodes. // Thus, this algorithm runs in O(n). var fixBits bool var explore func(int, uint) explore = func(rootIdx int, level uint) { root := &nodes[rootIdx] // Explore left branch. if root.c0 == nil { explore(root.n0, level+1) } else { fixBits = fixBits || (level > maxBits) root.c0.Len = uint32(level) } // Explore right branch. if root.c1 == nil { explore(root.n1, level+1) } else { fixBits = fixBits || (level > maxBits) root.c1.Len = uint32(level) } } explore(rootIdx, 1) // Fix the bit-lengths if we violate the maxBits requirement. if fixBits { // Create histogram for number of symbols with each bit-length. var symBitsArr [valueBits + 1]uint32 symBits := symBitsArr[:] // symBits[nb] indicates number of symbols using nb bits for _, c := range codes { for int(c.Len) >= len(symBits) { symBits = append(symBits, 0) } symBits[c.Len]++ } // Fudge the tree such that the largest bit-length is <= maxBits. // This is accomplish by effectively doing a tree rotation. That is, we // increase the bit-length of some higher frequency code, so that the // bit-lengths of lower frequency codes can be decreased. // // Visually, this looks like the following transform: // // Level Before After // __ ___ // / \ / \ // n-1 X / \ /\ /\ // n X /\ X X X X // n+1 X X // var treeRotate func(uint) treeRotate = func(nb uint) { if symBits[nb-1] == 0 { treeRotate(nb - 1) } symBits[nb-1] -= 1 // Push this node to the level below symBits[nb] += 3 // This level gets one node from above, two from below symBits[nb+1] -= 2 // Push two nodes to the level above } for i := uint(len(symBits)) - 1; i > maxBits; i-- { for symBits[i] > 0 { treeRotate(i - 1) } } // Assign bit-lengths to each code. Since codes is sorted in increasing // order of frequency, that means that the most frequently used symbols // should have the shortest bit-lengths. Thus, we copy symbols to codes // from the back of codes first. cs := codes for nb, cnt := range symBits { if cnt > 0 { pos := len(cs) - int(cnt) cs2 := cs[pos:] for i := range cs2 { cs2[i].Len = uint32(nb) } cs = cs[:pos] } } if len(cs) != 0 { panic("not all codes were used up") } } if internal.Debug && !codes.checkLengths() { panic("incomplete prefix tree detected") } return nil } // GeneratePrefixes assigns a prefix value to all codes according to the // bit-lengths. This function is used by both compressors and decompressors. // // The input codes must have the Sym and Len fields populated and be // sorted by symbol. The bit-lengths of each code must be properly allocated, // such that it forms a complete tree. // // The result will have the Val field populated and will produce a canonical // prefix tree. The resulting codes will remain sorted by symbol. func GeneratePrefixes(codes PrefixCodes) error { if len(codes) <= 1 { if len(codes) == 1 { if codes[0].Len != 0 { return errorf(errors.Invalid, "degenerate prefix tree with one node") } codes[0].Val = 0 } return nil } // Compute basic statistics on the symbols. var bitCnts [valueBits + 1]uint c0 := codes[0] bitCnts[c0.Len]++ minBits, maxBits, symLast := c0.Len, c0.Len, c0.Sym for _, c := range codes[1:] { if c.Sym <= symLast { return errorf(errors.Invalid, "non-unique or non-monotonically increasing symbols") } if minBits > c.Len { minBits = c.Len } if maxBits < c.Len { maxBits = c.Len } bitCnts[c.Len]++ // Histogram of bit counts symLast = c.Sym // Keep track of last symbol } if minBits == 0 { return errorf(errors.Invalid, "invalid prefix bit-length") } // Compute the next code for a symbol of a given bit length. var nextCodes [valueBits + 1]uint var code uint for i := minBits; i <= maxBits; i++ { code <<= 1 nextCodes[i] = code code += bitCnts[i] } if code != 1<= n { return s[:n] } return make([]uint32, n, n*3/2) } func extendSliceUint32s(s [][]uint32, n int) [][]uint32 { if cap(s) >= n { return s[:n] } ss := make([][]uint32, n, n*3/2) copy(ss, s[:cap(s)]) return ss } ================================================ FILE: vendor/github.com/dsnet/compress/internal/prefix/range.go ================================================ // Copyright 2015, Joe Tsai. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. package prefix type RangeCode struct { Base uint32 // Starting base offset of the range Len uint32 // Bit-length of a subsequent integer to add to base offset } type RangeCodes []RangeCode type RangeEncoder struct { rcs RangeCodes lut [1024]uint32 minBase uint } // End reports the non-inclusive ending range. func (rc RangeCode) End() uint32 { return rc.Base + (1 << rc.Len) } // MakeRangeCodes creates a RangeCodes, where each region is assumed to be // contiguously stacked, without any gaps, with bit-lengths taken from bits. func MakeRangeCodes(minBase uint, bits []uint) (rc RangeCodes) { for _, nb := range bits { rc = append(rc, RangeCode{Base: uint32(minBase), Len: uint32(nb)}) minBase += 1 << nb } return rc } // Base reports the inclusive starting range for all ranges. func (rcs RangeCodes) Base() uint32 { return rcs[0].Base } // End reports the non-inclusive ending range for all ranges. func (rcs RangeCodes) End() uint32 { return rcs[len(rcs)-1].End() } // checkValid reports whether the RangeCodes is valid. In order to be valid, // the following must hold true: // rcs[i-1].Base <= rcs[i].Base // rcs[i-1].End <= rcs[i].End // rcs[i-1].End >= rcs[i].Base // // Practically speaking, each range must be increasing and must not have any // gaps in between. It is okay for ranges to overlap. func (rcs RangeCodes) checkValid() bool { if len(rcs) == 0 { return false } pre := rcs[0] for _, cur := range rcs[1:] { preBase, preEnd := pre.Base, pre.End() curBase, curEnd := cur.Base, cur.End() if preBase > curBase || preEnd > curEnd || preEnd < curBase { return false } pre = cur } return true } func (re *RangeEncoder) Init(rcs RangeCodes) { if !rcs.checkValid() { panic("invalid range codes") } *re = RangeEncoder{rcs: rcs, minBase: uint(rcs.Base())} for sym, rc := range rcs { base := int(rc.Base) - int(re.minBase) end := int(rc.End()) - int(re.minBase) if base >= len(re.lut) { break } if end > len(re.lut) { end = len(re.lut) } for i := base; i < end; i++ { re.lut[i] = uint32(sym) } } } func (re *RangeEncoder) Encode(offset uint) (sym uint) { if idx := int(offset - re.minBase); idx < len(re.lut) { return uint(re.lut[idx]) } sym = uint(re.lut[len(re.lut)-1]) retry: if int(sym) >= len(re.rcs) || re.rcs[sym].Base > uint32(offset) { return sym - 1 } sym++ goto retry // Avoid for-loop so that this function can be inlined } ================================================ FILE: vendor/github.com/dsnet/compress/internal/prefix/reader.go ================================================ // Copyright 2015, Joe Tsai. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. package prefix import ( "bufio" "bytes" "encoding/binary" "io" "strings" "github.com/dsnet/compress" "github.com/dsnet/compress/internal" "github.com/dsnet/compress/internal/errors" ) // Reader implements a prefix decoder. If the input io.Reader satisfies the // compress.ByteReader or compress.BufferedReader interface, then it also // guarantees that it will never read more bytes than is necessary. // // For high performance, provide an io.Reader that satisfies the // compress.BufferedReader interface. If the input does not satisfy either // compress.ByteReader or compress.BufferedReader, then it will be internally // wrapped with a bufio.Reader. type Reader struct { Offset int64 // Number of bytes read from the underlying io.Reader rd io.Reader byteRd compress.ByteReader // Set if rd is a ByteReader bufRd compress.BufferedReader // Set if rd is a BufferedReader bufBits uint64 // Buffer to hold some bits numBits uint // Number of valid bits in bufBits bigEndian bool // Do we treat input bytes as big endian? // These fields are only used if rd is a compress.BufferedReader. bufPeek []byte // Buffer for the Peek data discardBits int // Number of bits to discard from reader fedBits uint // Number of bits fed in last call to PullBits // These fields are used to reduce allocations. bb *buffer br *bytesReader sr *stringReader bu *bufio.Reader } // Init initializes the bit Reader to read from r. If bigEndian is true, then // bits will be read starting from the most-significant bits of a byte // (as done in bzip2), otherwise it will read starting from the // least-significant bits of a byte (such as for deflate and brotli). func (pr *Reader) Init(r io.Reader, bigEndian bool) { *pr = Reader{ rd: r, bigEndian: bigEndian, bb: pr.bb, br: pr.br, sr: pr.sr, bu: pr.bu, } switch rr := r.(type) { case *bytes.Buffer: if pr.bb == nil { pr.bb = new(buffer) } *pr.bb = buffer{Buffer: rr} pr.bufRd = pr.bb case *bytes.Reader: if pr.br == nil { pr.br = new(bytesReader) } *pr.br = bytesReader{Reader: rr} pr.bufRd = pr.br case *strings.Reader: if pr.sr == nil { pr.sr = new(stringReader) } *pr.sr = stringReader{Reader: rr} pr.bufRd = pr.sr case compress.BufferedReader: pr.bufRd = rr case compress.ByteReader: pr.byteRd = rr default: if pr.bu == nil { pr.bu = bufio.NewReader(nil) } pr.bu.Reset(r) pr.rd, pr.bufRd = pr.bu, pr.bu } } // BitsRead reports the total number of bits emitted from any Read method. func (pr *Reader) BitsRead() int64 { offset := 8*pr.Offset - int64(pr.numBits) if pr.bufRd != nil { discardBits := pr.discardBits + int(pr.fedBits-pr.numBits) offset = 8*pr.Offset + int64(discardBits) } return offset } // IsBufferedReader reports whether the underlying io.Reader is also a // compress.BufferedReader. func (pr *Reader) IsBufferedReader() bool { return pr.bufRd != nil } // ReadPads reads 0-7 bits from the bit buffer to achieve byte-alignment. func (pr *Reader) ReadPads() uint { nb := pr.numBits % 8 val := uint(pr.bufBits & uint64(1<>= nb pr.numBits -= nb return val } // Read reads bytes into buf. // The bit-ordering mode does not affect this method. func (pr *Reader) Read(buf []byte) (cnt int, err error) { if pr.numBits > 0 { if pr.numBits%8 != 0 { return 0, errorf(errors.Invalid, "non-aligned bit buffer") } for cnt = 0; len(buf) > cnt && pr.numBits > 0; cnt++ { if pr.bigEndian { buf[cnt] = internal.ReverseLUT[byte(pr.bufBits)] } else { buf[cnt] = byte(pr.bufBits) } pr.bufBits >>= 8 pr.numBits -= 8 } return cnt, nil } if _, err := pr.Flush(); err != nil { return 0, err } cnt, err = pr.rd.Read(buf) pr.Offset += int64(cnt) return cnt, err } // ReadOffset reads an offset value using the provided RangeCodes indexed by // the symbol read. func (pr *Reader) ReadOffset(pd *Decoder, rcs RangeCodes) uint { rc := rcs[pr.ReadSymbol(pd)] return uint(rc.Base) + pr.ReadBits(uint(rc.Len)) } // TryReadBits attempts to read nb bits using the contents of the bit buffer // alone. It returns the value and whether it succeeded. // // This method is designed to be inlined for performance reasons. func (pr *Reader) TryReadBits(nb uint) (uint, bool) { if pr.numBits < nb { return 0, false } val := uint(pr.bufBits & uint64(1<>= nb pr.numBits -= nb return val, true } // ReadBits reads nb bits in from the underlying reader. func (pr *Reader) ReadBits(nb uint) uint { if err := pr.PullBits(nb); err != nil { errors.Panic(err) } val := uint(pr.bufBits & uint64(1<>= nb pr.numBits -= nb return val } // TryReadSymbol attempts to decode the next symbol using the contents of the // bit buffer alone. It returns the decoded symbol and whether it succeeded. // // This method is designed to be inlined for performance reasons. func (pr *Reader) TryReadSymbol(pd *Decoder) (uint, bool) { if pr.numBits < uint(pd.MinBits) || len(pd.chunks) == 0 { return 0, false } chunk := pd.chunks[uint32(pr.bufBits)&pd.chunkMask] nb := uint(chunk & countMask) if nb > pr.numBits || nb > uint(pd.chunkBits) { return 0, false } pr.bufBits >>= nb pr.numBits -= nb return uint(chunk >> countBits), true } // ReadSymbol reads the next symbol using the provided prefix Decoder. func (pr *Reader) ReadSymbol(pd *Decoder) uint { if len(pd.chunks) == 0 { panicf(errors.Invalid, "decode with empty prefix tree") } nb := uint(pd.MinBits) for { if err := pr.PullBits(nb); err != nil { errors.Panic(err) } chunk := pd.chunks[uint32(pr.bufBits)&pd.chunkMask] nb = uint(chunk & countMask) if nb > uint(pd.chunkBits) { linkIdx := chunk >> countBits chunk = pd.links[linkIdx][uint32(pr.bufBits>>pd.chunkBits)&pd.linkMask] nb = uint(chunk & countMask) } if nb <= pr.numBits { pr.bufBits >>= nb pr.numBits -= nb return uint(chunk >> countBits) } } } // Flush updates the read offset of the underlying ByteReader. // If reader is a compress.BufferedReader, then this calls Discard to update // the read offset. func (pr *Reader) Flush() (int64, error) { if pr.bufRd == nil { return pr.Offset, nil } // Update the number of total bits to discard. pr.discardBits += int(pr.fedBits - pr.numBits) pr.fedBits = pr.numBits // Discard some bytes to update read offset. var err error nd := (pr.discardBits + 7) / 8 // Round up to nearest byte nd, err = pr.bufRd.Discard(nd) pr.discardBits -= nd * 8 // -7..0 pr.Offset += int64(nd) // These are invalid after Discard. pr.bufPeek = nil return pr.Offset, err } // PullBits ensures that at least nb bits exist in the bit buffer. // If the underlying reader is a compress.BufferedReader, then this will fill // the bit buffer with as many bits as possible, relying on Peek and Discard to // properly advance the read offset. Otherwise, it will use ReadByte to fill the // buffer with just the right number of bits. func (pr *Reader) PullBits(nb uint) error { if pr.bufRd != nil { pr.discardBits += int(pr.fedBits - pr.numBits) for { if len(pr.bufPeek) == 0 { pr.fedBits = pr.numBits // Don't discard bits just added if _, err := pr.Flush(); err != nil { return err } // Peek no more bytes than necessary. // The computation for cntPeek computes the minimum number of // bytes to Peek to fill nb bits. var err error cntPeek := int(nb+(-nb&7)) / 8 if cntPeek < pr.bufRd.Buffered() { cntPeek = pr.bufRd.Buffered() } pr.bufPeek, err = pr.bufRd.Peek(cntPeek) pr.bufPeek = pr.bufPeek[int(pr.numBits/8):] // Skip buffered bits if len(pr.bufPeek) == 0 { if pr.numBits >= nb { break } if err == io.EOF { err = io.ErrUnexpectedEOF } return err } } n := int(64-pr.numBits) / 8 // Number of bytes to copy to bit buffer if len(pr.bufPeek) >= 8 { // Starting with Go 1.7, the compiler should use a wide integer // load here if the architecture supports it. u := binary.LittleEndian.Uint64(pr.bufPeek) if pr.bigEndian { // Swap all the bits within each byte. u = (u&0xaaaaaaaaaaaaaaaa)>>1 | (u&0x5555555555555555)<<1 u = (u&0xcccccccccccccccc)>>2 | (u&0x3333333333333333)<<2 u = (u&0xf0f0f0f0f0f0f0f0)>>4 | (u&0x0f0f0f0f0f0f0f0f)<<4 } pr.bufBits |= u << pr.numBits pr.numBits += uint(n * 8) pr.bufPeek = pr.bufPeek[n:] break } else { if n > len(pr.bufPeek) { n = len(pr.bufPeek) } for _, c := range pr.bufPeek[:n] { if pr.bigEndian { c = internal.ReverseLUT[c] } pr.bufBits |= uint64(c) << pr.numBits pr.numBits += 8 } pr.bufPeek = pr.bufPeek[n:] if pr.numBits > 56 { break } } } pr.fedBits = pr.numBits } else { for pr.numBits < nb { c, err := pr.byteRd.ReadByte() if err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } return err } if pr.bigEndian { c = internal.ReverseLUT[c] } pr.bufBits |= uint64(c) << pr.numBits pr.numBits += 8 pr.Offset++ } } return nil } ================================================ FILE: vendor/github.com/dsnet/compress/internal/prefix/wrap.go ================================================ // Copyright 2015, Joe Tsai. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. package prefix import ( "bytes" "io" "strings" ) // For some of the common Readers, we wrap and extend them to satisfy the // compress.BufferedReader interface to improve performance. type buffer struct { *bytes.Buffer } type bytesReader struct { *bytes.Reader pos int64 buf []byte arr [512]byte } type stringReader struct { *strings.Reader pos int64 buf []byte arr [512]byte } func (r *buffer) Buffered() int { return r.Len() } func (r *buffer) Peek(n int) ([]byte, error) { b := r.Bytes() if len(b) < n { return b, io.EOF } return b[:n], nil } func (r *buffer) Discard(n int) (int, error) { b := r.Next(n) if len(b) < n { return len(b), io.EOF } return n, nil } func (r *bytesReader) Buffered() int { r.update() if r.Len() > len(r.buf) { return len(r.buf) } return r.Len() } func (r *bytesReader) Peek(n int) ([]byte, error) { if n > len(r.arr) { return nil, io.ErrShortBuffer } // Return sub-slice of local buffer if possible. r.update() if len(r.buf) >= n { return r.buf[:n], nil } // Fill entire local buffer, and return appropriate sub-slice. cnt, err := r.ReadAt(r.arr[:], r.pos) r.buf = r.arr[:cnt] if cnt < n { return r.arr[:cnt], err } return r.arr[:n], nil } func (r *bytesReader) Discard(n int) (int, error) { var err error if n > r.Len() { n, err = r.Len(), io.EOF } r.Seek(int64(n), io.SeekCurrent) return n, err } // update reslices the internal buffer to be consistent with the read offset. func (r *bytesReader) update() { pos, _ := r.Seek(0, io.SeekCurrent) if off := pos - r.pos; off >= 0 && off < int64(len(r.buf)) { r.buf, r.pos = r.buf[off:], pos } else { r.buf, r.pos = nil, pos } } func (r *stringReader) Buffered() int { r.update() if r.Len() > len(r.buf) { return len(r.buf) } return r.Len() } func (r *stringReader) Peek(n int) ([]byte, error) { if n > len(r.arr) { return nil, io.ErrShortBuffer } // Return sub-slice of local buffer if possible. r.update() if len(r.buf) >= n { return r.buf[:n], nil } // Fill entire local buffer, and return appropriate sub-slice. cnt, err := r.ReadAt(r.arr[:], r.pos) r.buf = r.arr[:cnt] if cnt < n { return r.arr[:cnt], err } return r.arr[:n], nil } func (r *stringReader) Discard(n int) (int, error) { var err error if n > r.Len() { n, err = r.Len(), io.EOF } r.Seek(int64(n), io.SeekCurrent) return n, err } // update reslices the internal buffer to be consistent with the read offset. func (r *stringReader) update() { pos, _ := r.Seek(0, io.SeekCurrent) if off := pos - r.pos; off >= 0 && off < int64(len(r.buf)) { r.buf, r.pos = r.buf[off:], pos } else { r.buf, r.pos = nil, pos } } ================================================ FILE: vendor/github.com/dsnet/compress/internal/prefix/writer.go ================================================ // Copyright 2015, Joe Tsai. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. package prefix import ( "encoding/binary" "io" "github.com/dsnet/compress/internal/errors" ) // Writer implements a prefix encoder. For performance reasons, Writer will not // write bytes immediately to the underlying stream. type Writer struct { Offset int64 // Number of bytes written to the underlying io.Writer wr io.Writer bufBits uint64 // Buffer to hold some bits numBits uint // Number of valid bits in bufBits bigEndian bool // Are bits written in big-endian order? buf [512]byte cntBuf int } // Init initializes the bit Writer to write to w. If bigEndian is true, then // bits will be written starting from the most-significant bits of a byte // (as done in bzip2), otherwise it will write starting from the // least-significant bits of a byte (such as for deflate and brotli). func (pw *Writer) Init(w io.Writer, bigEndian bool) { *pw = Writer{wr: w, bigEndian: bigEndian} return } // BitsWritten reports the total number of bits issued to any Write method. func (pw *Writer) BitsWritten() int64 { return 8*pw.Offset + 8*int64(pw.cntBuf) + int64(pw.numBits) } // WritePads writes 0-7 bits to the bit buffer to achieve byte-alignment. func (pw *Writer) WritePads(v uint) { nb := -pw.numBits & 7 pw.bufBits |= uint64(v) << pw.numBits pw.numBits += nb } // Write writes bytes from buf. // The bit-ordering mode does not affect this method. func (pw *Writer) Write(buf []byte) (cnt int, err error) { if pw.numBits > 0 || pw.cntBuf > 0 { if pw.numBits%8 != 0 { return 0, errorf(errors.Invalid, "non-aligned bit buffer") } if _, err := pw.Flush(); err != nil { return 0, err } } cnt, err = pw.wr.Write(buf) pw.Offset += int64(cnt) return cnt, err } // WriteOffset writes ofs in a (sym, extra) fashion using the provided prefix // Encoder and RangeEncoder. func (pw *Writer) WriteOffset(ofs uint, pe *Encoder, re *RangeEncoder) { sym := re.Encode(ofs) pw.WriteSymbol(sym, pe) rc := re.rcs[sym] pw.WriteBits(ofs-uint(rc.Base), uint(rc.Len)) } // TryWriteBits attempts to write nb bits using the contents of the bit buffer // alone. It reports whether it succeeded. // // This method is designed to be inlined for performance reasons. func (pw *Writer) TryWriteBits(v, nb uint) bool { if 64-pw.numBits < nb { return false } pw.bufBits |= uint64(v) << pw.numBits pw.numBits += nb return true } // WriteBits writes nb bits of v to the underlying writer. func (pw *Writer) WriteBits(v, nb uint) { if _, err := pw.PushBits(); err != nil { errors.Panic(err) } pw.bufBits |= uint64(v) << pw.numBits pw.numBits += nb } // TryWriteSymbol attempts to encode the next symbol using the contents of the // bit buffer alone. It reports whether it succeeded. // // This method is designed to be inlined for performance reasons. func (pw *Writer) TryWriteSymbol(sym uint, pe *Encoder) bool { chunk := pe.chunks[uint32(sym)&pe.chunkMask] nb := uint(chunk & countMask) if 64-pw.numBits < nb { return false } pw.bufBits |= uint64(chunk>>countBits) << pw.numBits pw.numBits += nb return true } // WriteSymbol writes the symbol using the provided prefix Encoder. func (pw *Writer) WriteSymbol(sym uint, pe *Encoder) { if _, err := pw.PushBits(); err != nil { errors.Panic(err) } chunk := pe.chunks[uint32(sym)&pe.chunkMask] nb := uint(chunk & countMask) pw.bufBits |= uint64(chunk>>countBits) << pw.numBits pw.numBits += nb } // Flush flushes all complete bytes from the bit buffer to the byte buffer, and // then flushes all bytes in the byte buffer to the underlying writer. // After this call, the bit Writer is will only withhold 7 bits at most. func (pw *Writer) Flush() (int64, error) { if pw.numBits < 8 && pw.cntBuf == 0 { return pw.Offset, nil } if _, err := pw.PushBits(); err != nil { return pw.Offset, err } cnt, err := pw.wr.Write(pw.buf[:pw.cntBuf]) pw.cntBuf -= cnt pw.Offset += int64(cnt) return pw.Offset, err } // PushBits pushes as many bytes as possible from the bit buffer to the byte // buffer, reporting the number of bits pushed. func (pw *Writer) PushBits() (uint, error) { if pw.cntBuf >= len(pw.buf)-8 { cnt, err := pw.wr.Write(pw.buf[:pw.cntBuf]) pw.cntBuf -= cnt pw.Offset += int64(cnt) if err != nil { return 0, err } } u := pw.bufBits if pw.bigEndian { // Swap all the bits within each byte. u = (u&0xaaaaaaaaaaaaaaaa)>>1 | (u&0x5555555555555555)<<1 u = (u&0xcccccccccccccccc)>>2 | (u&0x3333333333333333)<<2 u = (u&0xf0f0f0f0f0f0f0f0)>>4 | (u&0x0f0f0f0f0f0f0f0f)<<4 } // Starting with Go 1.7, the compiler should use a wide integer // store here if the architecture supports it. binary.LittleEndian.PutUint64(pw.buf[pw.cntBuf:], u) nb := pw.numBits / 8 // Number of bytes to copy from bit buffer pw.cntBuf += int(nb) pw.bufBits >>= 8 * nb pw.numBits -= 8 * nb return 8 * nb, nil } ================================================ FILE: vendor/github.com/dsnet/compress/internal/release.go ================================================ // Copyright 2015, Joe Tsai. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. // +build !debug,!gofuzz package internal // Debug indicates whether the debug build tag was set. // // If set, programs may choose to print with more human-readable // debug information and also perform sanity checks that would otherwise be too // expensive to run in a release build. const Debug = false // GoFuzz indicates whether the gofuzz build tag was set. // // If set, programs may choose to disable certain checks (like checksums) that // would be nearly impossible for gofuzz to properly get right. // If GoFuzz is set, it implies that Debug is set as well. const GoFuzz = false ================================================ FILE: vendor/github.com/dsnet/compress/zbench.sh ================================================ #!/bin/bash # # Copyright 2017, Joe Tsai. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE.md file. # zbench wraps internal/tool/bench and is useful for comparing benchmarks from # the implementations in this repository relative to other implementations. # # See internal/tool/bench/main.go for more details. cd $(dirname "${BASH_SOURCE[0]}")/internal/tool/bench go run $(go list -f '{{ join .GoFiles "\n" }}') "$@" ================================================ FILE: vendor/github.com/dsnet/compress/zfuzz.sh ================================================ #!/bin/bash # # Copyright 2017, Joe Tsai. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE.md file. # zfuzz wraps internal/tool/fuzz and is useful for fuzz testing each of # the implementations in this repository. cd $(dirname "${BASH_SOURCE[0]}")/internal/tool/fuzz ./fuzz.sh "$@" ================================================ FILE: vendor/github.com/dsnet/compress/zprof.sh ================================================ #!/bin/bash # # Copyright 2017, Joe Tsai. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE.md file. if [ $# == 0 ]; then echo "Usage: $0 PKG_PATH TEST_ARGS..." echo "" echo "Runs coverage and performance benchmarks for a given package." echo "The results are stored in the _zprof_ directory." echo "" echo "Example:" echo " $0 flate -test.bench=Decode/Twain/Default" exit 1 fi DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PKG_PATH=$1 PKG_NAME=$(basename $PKG_PATH) shift TMPDIR=$(mktemp -d) trap "rm -rf $TMPDIR $PKG_PATH/$PKG_NAME.test" SIGINT SIGTERM EXIT ( cd $DIR/$PKG_PATH # Print the go version. go version # Perform coverage profiling. go test github.com/dsnet/compress/$PKG_PATH -coverprofile $TMPDIR/cover.profile if [ $? != 0 ]; then exit 1; fi go tool cover -html $TMPDIR/cover.profile -o cover.html # Perform performance profiling. if [ $# != 0 ]; then go test -c github.com/dsnet/compress/$PKG_PATH if [ $? != 0 ]; then exit 1; fi ./$PKG_NAME.test -test.cpuprofile $TMPDIR/cpu.profile -test.memprofile $TMPDIR/mem.profile -test.run - "$@" PPROF="go tool pprof" $PPROF -output=cpu.svg -web $PKG_NAME.test $TMPDIR/cpu.profile 2> /dev/null $PPROF -output=cpu.html -weblist=. $PKG_NAME.test $TMPDIR/cpu.profile 2> /dev/null $PPROF -output=mem_objects.svg -alloc_objects -web $PKG_NAME.test $TMPDIR/mem.profile 2> /dev/null $PPROF -output=mem_objects.html -alloc_objects -weblist=. $PKG_NAME.test $TMPDIR/mem.profile 2> /dev/null $PPROF -output=mem_space.svg -alloc_space -web $PKG_NAME.test $TMPDIR/mem.profile 2> /dev/null $PPROF -output=mem_space.html -alloc_space -weblist=. $PKG_NAME.test $TMPDIR/mem.profile 2> /dev/null fi rm -rf $DIR/_zprof_/$PKG_NAME mkdir -p $DIR/_zprof_/$PKG_NAME mv *.html *.svg $DIR/_zprof_/$PKG_NAME 2> /dev/null ) ================================================ FILE: vendor/github.com/dsnet/compress/ztest.sh ================================================ #!/bin/bash # # Copyright 2017, Joe Tsai. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE.md file. cd $(go list -f '{{ .Dir }}' github.com/dsnet/compress) BOLD="\x1b[1mRunning: " PASS="\x1b[32mPASS" FAIL="\x1b[31mFAIL" RESET="\x1b[0m" echo -e "${BOLD}fmt${RESET}" RET_FMT=$(find . -name "*.go" | egrep -v "/(_.*_|\..*|testdata)/" | xargs gofmt -d) if [[ ! -z "$RET_FMT" ]]; then echo "$RET_FMT"; echo; fi echo -e "${BOLD}test${RESET}" RET_TEST=$(go test -race ./... | egrep -v "^(ok|[?])\s+") if [[ ! -z "$RET_TEST" ]]; then echo "$RET_TEST"; echo; fi echo -e "${BOLD}staticcheck${RESET}" RET_SCHK=$(staticcheck \ -ignore " github.com/dsnet/compress/brotli/*.go:SA4016 github.com/dsnet/compress/brotli/*.go:S1023 github.com/dsnet/compress/brotli/*.go:U1000 github.com/dsnet/compress/bzip2/*.go:S1023 github.com/dsnet/compress/flate/*.go:U1000 github.com/dsnet/compress/internal/cgo/lzma/*.go:SA4000 github.com/dsnet/compress/internal/prefix/*.go:S1004 github.com/dsnet/compress/internal/prefix/*.go:S1023 github.com/dsnet/compress/internal/prefix/*.go:SA4016 github.com/dsnet/compress/internal/tool/bench/*.go:S1007 github.com/dsnet/compress/xflate/internal/meta/*.go:S1023 " ./... 2>&1) if [[ ! -z "$RET_SCHK" ]]; then echo "$RET_SCHK"; echo; fi echo -e "${BOLD}lint${RESET}" RET_LINT=$(golint ./... 2>&1 | egrep -v "^vendor/" | egrep -v "should have comment(.*)or be unexported" | egrep -v "^(.*)type name will be used as(.*)by other packages" | egrep -v "^brotli/transform.go:(.*)replace i [+]= 1 with i[+]{2}" | egrep -v "^internal/prefix/prefix.go:(.*)replace symBits(.*) [-]= 1 with symBits(.*)[-]{2}" | egrep -v "^xflate/common.go:(.*)NoCompression should be of the form" | egrep -v "^exit status") if [[ ! -z "$RET_LINT" ]]; then echo "$RET_LINT"; echo; fi if [[ ! -z "$RET_FMT" ]] || [ ! -z "$RET_TEST" ] || [[ ! -z "$RET_SCHK" ]] || [[ ! -z "$RET_LINT" ]]; then echo -e "${FAIL}${RESET}"; exit 1 else echo -e "${PASS}${RESET}"; exit 0 fi ================================================ FILE: vendor/github.com/dustin/go-humanize/.travis.yml ================================================ sudo: false language: go go: - 1.3.x - 1.5.x - 1.6.x - 1.7.x - 1.8.x - 1.9.x - master matrix: allow_failures: - go: master fast_finish: true install: - # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step). script: - go get -t -v ./... - diff -u <(echo -n) <(gofmt -d -s .) - go tool vet . - go test -v -race ./... ================================================ FILE: vendor/github.com/dustin/go-humanize/LICENSE ================================================ Copyright (c) 2005-2008 Dustin Sallings Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/dustin/go-humanize/README.markdown ================================================ # Humane Units [![Build Status](https://travis-ci.org/dustin/go-humanize.svg?branch=master)](https://travis-ci.org/dustin/go-humanize) [![GoDoc](https://godoc.org/github.com/dustin/go-humanize?status.svg)](https://godoc.org/github.com/dustin/go-humanize) Just a few functions for helping humanize times and sizes. `go get` it as `github.com/dustin/go-humanize`, import it as `"github.com/dustin/go-humanize"`, use it as `humanize`. See [godoc](https://godoc.org/github.com/dustin/go-humanize) for complete documentation. ## Sizes This lets you take numbers like `82854982` and convert them to useful strings like, `83 MB` or `79 MiB` (whichever you prefer). Example: ```go fmt.Printf("That file is %s.", humanize.Bytes(82854982)) // That file is 83 MB. ``` ## Times This lets you take a `time.Time` and spit it out in relative terms. For example, `12 seconds ago` or `3 days from now`. Example: ```go fmt.Printf("This was touched %s.", humanize.Time(someTimeInstance)) // This was touched 7 hours ago. ``` Thanks to Kyle Lemons for the time implementation from an IRC conversation one day. It's pretty neat. ## Ordinals From a [mailing list discussion][odisc] where a user wanted to be able to label ordinals. 0 -> 0th 1 -> 1st 2 -> 2nd 3 -> 3rd 4 -> 4th [...] Example: ```go fmt.Printf("You're my %s best friend.", humanize.Ordinal(193)) // You are my 193rd best friend. ``` ## Commas Want to shove commas into numbers? Be my guest. 0 -> 0 100 -> 100 1000 -> 1,000 1000000000 -> 1,000,000,000 -100000 -> -100,000 Example: ```go fmt.Printf("You owe $%s.\n", humanize.Comma(6582491)) // You owe $6,582,491. ``` ## Ftoa Nicer float64 formatter that removes trailing zeros. ```go fmt.Printf("%f", 2.24) // 2.240000 fmt.Printf("%s", humanize.Ftoa(2.24)) // 2.24 fmt.Printf("%f", 2.0) // 2.000000 fmt.Printf("%s", humanize.Ftoa(2.0)) // 2 ``` ## SI notation Format numbers with [SI notation][sinotation]. Example: ```go humanize.SI(0.00000000223, "M") // 2.23 nM ``` ## English-specific functions The following functions are in the `humanize/english` subpackage. ### Plurals Simple English pluralization ```go english.PluralWord(1, "object", "") // object english.PluralWord(42, "object", "") // objects english.PluralWord(2, "bus", "") // buses english.PluralWord(99, "locus", "loci") // loci english.Plural(1, "object", "") // 1 object english.Plural(42, "object", "") // 42 objects english.Plural(2, "bus", "") // 2 buses english.Plural(99, "locus", "loci") // 99 loci ``` ### Word series Format comma-separated words lists with conjuctions: ```go english.WordSeries([]string{"foo"}, "and") // foo english.WordSeries([]string{"foo", "bar"}, "and") // foo and bar english.WordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar and baz english.OxfordWordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar, and baz ``` [odisc]: https://groups.google.com/d/topic/golang-nuts/l8NhI74jl-4/discussion [sinotation]: http://en.wikipedia.org/wiki/Metric_prefix ================================================ FILE: vendor/github.com/dustin/go-humanize/big.go ================================================ package humanize import ( "math/big" ) // order of magnitude (to a max order) func oomm(n, b *big.Int, maxmag int) (float64, int) { mag := 0 m := &big.Int{} for n.Cmp(b) >= 0 { n.DivMod(n, b, m) mag++ if mag == maxmag && maxmag >= 0 { break } } return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag } // total order of magnitude // (same as above, but with no upper limit) func oom(n, b *big.Int) (float64, int) { mag := 0 m := &big.Int{} for n.Cmp(b) >= 0 { n.DivMod(n, b, m) mag++ } return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag } ================================================ FILE: vendor/github.com/dustin/go-humanize/bigbytes.go ================================================ package humanize import ( "fmt" "math/big" "strings" "unicode" ) var ( bigIECExp = big.NewInt(1024) // BigByte is one byte in bit.Ints BigByte = big.NewInt(1) // BigKiByte is 1,024 bytes in bit.Ints BigKiByte = (&big.Int{}).Mul(BigByte, bigIECExp) // BigMiByte is 1,024 k bytes in bit.Ints BigMiByte = (&big.Int{}).Mul(BigKiByte, bigIECExp) // BigGiByte is 1,024 m bytes in bit.Ints BigGiByte = (&big.Int{}).Mul(BigMiByte, bigIECExp) // BigTiByte is 1,024 g bytes in bit.Ints BigTiByte = (&big.Int{}).Mul(BigGiByte, bigIECExp) // BigPiByte is 1,024 t bytes in bit.Ints BigPiByte = (&big.Int{}).Mul(BigTiByte, bigIECExp) // BigEiByte is 1,024 p bytes in bit.Ints BigEiByte = (&big.Int{}).Mul(BigPiByte, bigIECExp) // BigZiByte is 1,024 e bytes in bit.Ints BigZiByte = (&big.Int{}).Mul(BigEiByte, bigIECExp) // BigYiByte is 1,024 z bytes in bit.Ints BigYiByte = (&big.Int{}).Mul(BigZiByte, bigIECExp) ) var ( bigSIExp = big.NewInt(1000) // BigSIByte is one SI byte in big.Ints BigSIByte = big.NewInt(1) // BigKByte is 1,000 SI bytes in big.Ints BigKByte = (&big.Int{}).Mul(BigSIByte, bigSIExp) // BigMByte is 1,000 SI k bytes in big.Ints BigMByte = (&big.Int{}).Mul(BigKByte, bigSIExp) // BigGByte is 1,000 SI m bytes in big.Ints BigGByte = (&big.Int{}).Mul(BigMByte, bigSIExp) // BigTByte is 1,000 SI g bytes in big.Ints BigTByte = (&big.Int{}).Mul(BigGByte, bigSIExp) // BigPByte is 1,000 SI t bytes in big.Ints BigPByte = (&big.Int{}).Mul(BigTByte, bigSIExp) // BigEByte is 1,000 SI p bytes in big.Ints BigEByte = (&big.Int{}).Mul(BigPByte, bigSIExp) // BigZByte is 1,000 SI e bytes in big.Ints BigZByte = (&big.Int{}).Mul(BigEByte, bigSIExp) // BigYByte is 1,000 SI z bytes in big.Ints BigYByte = (&big.Int{}).Mul(BigZByte, bigSIExp) ) var bigBytesSizeTable = map[string]*big.Int{ "b": BigByte, "kib": BigKiByte, "kb": BigKByte, "mib": BigMiByte, "mb": BigMByte, "gib": BigGiByte, "gb": BigGByte, "tib": BigTiByte, "tb": BigTByte, "pib": BigPiByte, "pb": BigPByte, "eib": BigEiByte, "eb": BigEByte, "zib": BigZiByte, "zb": BigZByte, "yib": BigYiByte, "yb": BigYByte, // Without suffix "": BigByte, "ki": BigKiByte, "k": BigKByte, "mi": BigMiByte, "m": BigMByte, "gi": BigGiByte, "g": BigGByte, "ti": BigTiByte, "t": BigTByte, "pi": BigPiByte, "p": BigPByte, "ei": BigEiByte, "e": BigEByte, "z": BigZByte, "zi": BigZiByte, "y": BigYByte, "yi": BigYiByte, } var ten = big.NewInt(10) func humanateBigBytes(s, base *big.Int, sizes []string) string { if s.Cmp(ten) < 0 { return fmt.Sprintf("%d B", s) } c := (&big.Int{}).Set(s) val, mag := oomm(c, base, len(sizes)-1) suffix := sizes[mag] f := "%.0f %s" if val < 10 { f = "%.1f %s" } return fmt.Sprintf(f, val, suffix) } // BigBytes produces a human readable representation of an SI size. // // See also: ParseBigBytes. // // BigBytes(82854982) -> 83 MB func BigBytes(s *big.Int) string { sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} return humanateBigBytes(s, bigSIExp, sizes) } // BigIBytes produces a human readable representation of an IEC size. // // See also: ParseBigBytes. // // BigIBytes(82854982) -> 79 MiB func BigIBytes(s *big.Int) string { sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} return humanateBigBytes(s, bigIECExp, sizes) } // ParseBigBytes parses a string representation of bytes into the number // of bytes it represents. // // See also: BigBytes, BigIBytes. // // ParseBigBytes("42 MB") -> 42000000, nil // ParseBigBytes("42 mib") -> 44040192, nil func ParseBigBytes(s string) (*big.Int, error) { lastDigit := 0 hasComma := false for _, r := range s { if !(unicode.IsDigit(r) || r == '.' || r == ',') { break } if r == ',' { hasComma = true } lastDigit++ } num := s[:lastDigit] if hasComma { num = strings.Replace(num, ",", "", -1) } val := &big.Rat{} _, err := fmt.Sscanf(num, "%f", val) if err != nil { return nil, err } extra := strings.ToLower(strings.TrimSpace(s[lastDigit:])) if m, ok := bigBytesSizeTable[extra]; ok { mv := (&big.Rat{}).SetInt(m) val.Mul(val, mv) rv := &big.Int{} rv.Div(val.Num(), val.Denom()) return rv, nil } return nil, fmt.Errorf("unhandled size name: %v", extra) } ================================================ FILE: vendor/github.com/dustin/go-humanize/bytes.go ================================================ package humanize import ( "fmt" "math" "strconv" "strings" "unicode" ) // IEC Sizes. // kibis of bits const ( Byte = 1 << (iota * 10) KiByte MiByte GiByte TiByte PiByte EiByte ) // SI Sizes. const ( IByte = 1 KByte = IByte * 1000 MByte = KByte * 1000 GByte = MByte * 1000 TByte = GByte * 1000 PByte = TByte * 1000 EByte = PByte * 1000 ) var bytesSizeTable = map[string]uint64{ "b": Byte, "kib": KiByte, "kb": KByte, "mib": MiByte, "mb": MByte, "gib": GiByte, "gb": GByte, "tib": TiByte, "tb": TByte, "pib": PiByte, "pb": PByte, "eib": EiByte, "eb": EByte, // Without suffix "": Byte, "ki": KiByte, "k": KByte, "mi": MiByte, "m": MByte, "gi": GiByte, "g": GByte, "ti": TiByte, "t": TByte, "pi": PiByte, "p": PByte, "ei": EiByte, "e": EByte, } func logn(n, b float64) float64 { return math.Log(n) / math.Log(b) } func humanateBytes(s uint64, base float64, sizes []string) string { if s < 10 { return fmt.Sprintf("%d B", s) } e := math.Floor(logn(float64(s), base)) suffix := sizes[int(e)] val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10 f := "%.0f %s" if val < 10 { f = "%.1f %s" } return fmt.Sprintf(f, val, suffix) } // Bytes produces a human readable representation of an SI size. // // See also: ParseBytes. // // Bytes(82854982) -> 83 MB func Bytes(s uint64) string { sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"} return humanateBytes(s, 1000, sizes) } // IBytes produces a human readable representation of an IEC size. // // See also: ParseBytes. // // IBytes(82854982) -> 79 MiB func IBytes(s uint64) string { sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"} return humanateBytes(s, 1024, sizes) } // ParseBytes parses a string representation of bytes into the number // of bytes it represents. // // See Also: Bytes, IBytes. // // ParseBytes("42 MB") -> 42000000, nil // ParseBytes("42 mib") -> 44040192, nil func ParseBytes(s string) (uint64, error) { lastDigit := 0 hasComma := false for _, r := range s { if !(unicode.IsDigit(r) || r == '.' || r == ',') { break } if r == ',' { hasComma = true } lastDigit++ } num := s[:lastDigit] if hasComma { num = strings.Replace(num, ",", "", -1) } f, err := strconv.ParseFloat(num, 64) if err != nil { return 0, err } extra := strings.ToLower(strings.TrimSpace(s[lastDigit:])) if m, ok := bytesSizeTable[extra]; ok { f *= float64(m) if f >= math.MaxUint64 { return 0, fmt.Errorf("too large: %v", s) } return uint64(f), nil } return 0, fmt.Errorf("unhandled size name: %v", extra) } ================================================ FILE: vendor/github.com/dustin/go-humanize/comma.go ================================================ package humanize import ( "bytes" "math" "math/big" "strconv" "strings" ) // Comma produces a string form of the given number in base 10 with // commas after every three orders of magnitude. // // e.g. Comma(834142) -> 834,142 func Comma(v int64) string { sign := "" // Min int64 can't be negated to a usable value, so it has to be special cased. if v == math.MinInt64 { return "-9,223,372,036,854,775,808" } if v < 0 { sign = "-" v = 0 - v } parts := []string{"", "", "", "", "", "", ""} j := len(parts) - 1 for v > 999 { parts[j] = strconv.FormatInt(v%1000, 10) switch len(parts[j]) { case 2: parts[j] = "0" + parts[j] case 1: parts[j] = "00" + parts[j] } v = v / 1000 j-- } parts[j] = strconv.Itoa(int(v)) return sign + strings.Join(parts[j:], ",") } // Commaf produces a string form of the given number in base 10 with // commas after every three orders of magnitude. // // e.g. Commaf(834142.32) -> 834,142.32 func Commaf(v float64) string { buf := &bytes.Buffer{} if v < 0 { buf.Write([]byte{'-'}) v = 0 - v } comma := []byte{','} parts := strings.Split(strconv.FormatFloat(v, 'f', -1, 64), ".") pos := 0 if len(parts[0])%3 != 0 { pos += len(parts[0]) % 3 buf.WriteString(parts[0][:pos]) buf.Write(comma) } for ; pos < len(parts[0]); pos += 3 { buf.WriteString(parts[0][pos : pos+3]) buf.Write(comma) } buf.Truncate(buf.Len() - 1) if len(parts) > 1 { buf.Write([]byte{'.'}) buf.WriteString(parts[1]) } return buf.String() } // CommafWithDigits works like the Commaf but limits the resulting // string to the given number of decimal places. // // e.g. CommafWithDigits(834142.32, 1) -> 834,142.3 func CommafWithDigits(f float64, decimals int) string { return stripTrailingDigits(Commaf(f), decimals) } // BigComma produces a string form of the given big.Int in base 10 // with commas after every three orders of magnitude. func BigComma(b *big.Int) string { sign := "" if b.Sign() < 0 { sign = "-" b.Abs(b) } athousand := big.NewInt(1000) c := (&big.Int{}).Set(b) _, m := oom(c, athousand) parts := make([]string, m+1) j := len(parts) - 1 mod := &big.Int{} for b.Cmp(athousand) >= 0 { b.DivMod(b, athousand, mod) parts[j] = strconv.FormatInt(mod.Int64(), 10) switch len(parts[j]) { case 2: parts[j] = "0" + parts[j] case 1: parts[j] = "00" + parts[j] } j-- } parts[j] = strconv.Itoa(int(b.Int64())) return sign + strings.Join(parts[j:], ",") } ================================================ FILE: vendor/github.com/dustin/go-humanize/commaf.go ================================================ // +build go1.6 package humanize import ( "bytes" "math/big" "strings" ) // BigCommaf produces a string form of the given big.Float in base 10 // with commas after every three orders of magnitude. func BigCommaf(v *big.Float) string { buf := &bytes.Buffer{} if v.Sign() < 0 { buf.Write([]byte{'-'}) v.Abs(v) } comma := []byte{','} parts := strings.Split(v.Text('f', -1), ".") pos := 0 if len(parts[0])%3 != 0 { pos += len(parts[0]) % 3 buf.WriteString(parts[0][:pos]) buf.Write(comma) } for ; pos < len(parts[0]); pos += 3 { buf.WriteString(parts[0][pos : pos+3]) buf.Write(comma) } buf.Truncate(buf.Len() - 1) if len(parts) > 1 { buf.Write([]byte{'.'}) buf.WriteString(parts[1]) } return buf.String() } ================================================ FILE: vendor/github.com/dustin/go-humanize/ftoa.go ================================================ package humanize import ( "strconv" "strings" ) func stripTrailingZeros(s string) string { offset := len(s) - 1 for offset > 0 { if s[offset] == '.' { offset-- break } if s[offset] != '0' { break } offset-- } return s[:offset+1] } func stripTrailingDigits(s string, digits int) string { if i := strings.Index(s, "."); i >= 0 { if digits <= 0 { return s[:i] } i++ if i+digits >= len(s) { return s } return s[:i+digits] } return s } // Ftoa converts a float to a string with no trailing zeros. func Ftoa(num float64) string { return stripTrailingZeros(strconv.FormatFloat(num, 'f', 6, 64)) } // FtoaWithDigits converts a float to a string but limits the resulting string // to the given number of decimal places, and no trailing zeros. func FtoaWithDigits(num float64, digits int) string { return stripTrailingZeros(stripTrailingDigits(strconv.FormatFloat(num, 'f', 6, 64), digits)) } ================================================ FILE: vendor/github.com/dustin/go-humanize/humanize.go ================================================ /* Package humanize converts boring ugly numbers to human-friendly strings and back. Durations can be turned into strings such as "3 days ago", numbers representing sizes like 82854982 into useful strings like, "83 MB" or "79 MiB" (whichever you prefer). */ package humanize ================================================ FILE: vendor/github.com/dustin/go-humanize/number.go ================================================ package humanize /* Slightly adapted from the source to fit go-humanize. Author: https://github.com/gorhill Source: https://gist.github.com/gorhill/5285193 */ import ( "math" "strconv" ) var ( renderFloatPrecisionMultipliers = [...]float64{ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, } renderFloatPrecisionRounders = [...]float64{ 0.5, 0.05, 0.005, 0.0005, 0.00005, 0.000005, 0.0000005, 0.00000005, 0.000000005, 0.0000000005, } ) // FormatFloat produces a formatted number as string based on the following user-specified criteria: // * thousands separator // * decimal separator // * decimal precision // // Usage: s := RenderFloat(format, n) // The format parameter tells how to render the number n. // // See examples: http://play.golang.org/p/LXc1Ddm1lJ // // Examples of format strings, given n = 12345.6789: // "#,###.##" => "12,345.67" // "#,###." => "12,345" // "#,###" => "12345,678" // "#\u202F###,##" => "12 345,68" // "#.###,###### => 12.345,678900 // "" (aka default format) => 12,345.67 // // The highest precision allowed is 9 digits after the decimal symbol. // There is also a version for integer number, FormatInteger(), // which is convenient for calls within template. func FormatFloat(format string, n float64) string { // Special cases: // NaN = "NaN" // +Inf = "+Infinity" // -Inf = "-Infinity" if math.IsNaN(n) { return "NaN" } if n > math.MaxFloat64 { return "Infinity" } if n < -math.MaxFloat64 { return "-Infinity" } // default format precision := 2 decimalStr := "." thousandStr := "," positiveStr := "" negativeStr := "-" if len(format) > 0 { format := []rune(format) // If there is an explicit format directive, // then default values are these: precision = 9 thousandStr = "" // collect indices of meaningful formatting directives formatIndx := []int{} for i, char := range format { if char != '#' && char != '0' { formatIndx = append(formatIndx, i) } } if len(formatIndx) > 0 { // Directive at index 0: // Must be a '+' // Raise an error if not the case // index: 0123456789 // +0.000,000 // +000,000.0 // +0000.00 // +0000 if formatIndx[0] == 0 { if format[formatIndx[0]] != '+' { panic("RenderFloat(): invalid positive sign directive") } positiveStr = "+" formatIndx = formatIndx[1:] } // Two directives: // First is thousands separator // Raise an error if not followed by 3-digit // 0123456789 // 0.000,000 // 000,000.00 if len(formatIndx) == 2 { if (formatIndx[1] - formatIndx[0]) != 4 { panic("RenderFloat(): thousands separator directive must be followed by 3 digit-specifiers") } thousandStr = string(format[formatIndx[0]]) formatIndx = formatIndx[1:] } // One directive: // Directive is decimal separator // The number of digit-specifier following the separator indicates wanted precision // 0123456789 // 0.00 // 000,0000 if len(formatIndx) == 1 { decimalStr = string(format[formatIndx[0]]) precision = len(format) - formatIndx[0] - 1 } } } // generate sign part var signStr string if n >= 0.000000001 { signStr = positiveStr } else if n <= -0.000000001 { signStr = negativeStr n = -n } else { signStr = "" n = 0.0 } // split number into integer and fractional parts intf, fracf := math.Modf(n + renderFloatPrecisionRounders[precision]) // generate integer part string intStr := strconv.FormatInt(int64(intf), 10) // add thousand separator if required if len(thousandStr) > 0 { for i := len(intStr); i > 3; { i -= 3 intStr = intStr[:i] + thousandStr + intStr[i:] } } // no fractional part, we can leave now if precision == 0 { return signStr + intStr } // generate fractional part fracStr := strconv.Itoa(int(fracf * renderFloatPrecisionMultipliers[precision])) // may need padding if len(fracStr) < precision { fracStr = "000000000000000"[:precision-len(fracStr)] + fracStr } return signStr + intStr + decimalStr + fracStr } // FormatInteger produces a formatted number as string. // See FormatFloat. func FormatInteger(format string, n int) string { return FormatFloat(format, float64(n)) } ================================================ FILE: vendor/github.com/dustin/go-humanize/ordinals.go ================================================ package humanize import "strconv" // Ordinal gives you the input number in a rank/ordinal format. // // Ordinal(3) -> 3rd func Ordinal(x int) string { suffix := "th" switch x % 10 { case 1: if x%100 != 11 { suffix = "st" } case 2: if x%100 != 12 { suffix = "nd" } case 3: if x%100 != 13 { suffix = "rd" } } return strconv.Itoa(x) + suffix } ================================================ FILE: vendor/github.com/dustin/go-humanize/si.go ================================================ package humanize import ( "errors" "math" "regexp" "strconv" ) var siPrefixTable = map[float64]string{ -24: "y", // yocto -21: "z", // zepto -18: "a", // atto -15: "f", // femto -12: "p", // pico -9: "n", // nano -6: "µ", // micro -3: "m", // milli 0: "", 3: "k", // kilo 6: "M", // mega 9: "G", // giga 12: "T", // tera 15: "P", // peta 18: "E", // exa 21: "Z", // zetta 24: "Y", // yotta } var revSIPrefixTable = revfmap(siPrefixTable) // revfmap reverses the map and precomputes the power multiplier func revfmap(in map[float64]string) map[string]float64 { rv := map[string]float64{} for k, v := range in { rv[v] = math.Pow(10, k) } return rv } var riParseRegex *regexp.Regexp func init() { ri := `^([\-0-9.]+)\s?([` for _, v := range siPrefixTable { ri += v } ri += `]?)(.*)` riParseRegex = regexp.MustCompile(ri) } // ComputeSI finds the most appropriate SI prefix for the given number // and returns the prefix along with the value adjusted to be within // that prefix. // // See also: SI, ParseSI. // // e.g. ComputeSI(2.2345e-12) -> (2.2345, "p") func ComputeSI(input float64) (float64, string) { if input == 0 { return 0, "" } mag := math.Abs(input) exponent := math.Floor(logn(mag, 10)) exponent = math.Floor(exponent/3) * 3 value := mag / math.Pow(10, exponent) // Handle special case where value is exactly 1000.0 // Should return 1 M instead of 1000 k if value == 1000.0 { exponent += 3 value = mag / math.Pow(10, exponent) } value = math.Copysign(value, input) prefix := siPrefixTable[exponent] return value, prefix } // SI returns a string with default formatting. // // SI uses Ftoa to format float value, removing trailing zeros. // // See also: ComputeSI, ParseSI. // // e.g. SI(1000000, "B") -> 1 MB // e.g. SI(2.2345e-12, "F") -> 2.2345 pF func SI(input float64, unit string) string { value, prefix := ComputeSI(input) return Ftoa(value) + " " + prefix + unit } // SIWithDigits works like SI but limits the resulting string to the // given number of decimal places. // // e.g. SIWithDigits(1000000, 0, "B") -> 1 MB // e.g. SIWithDigits(2.2345e-12, 2, "F") -> 2.23 pF func SIWithDigits(input float64, decimals int, unit string) string { value, prefix := ComputeSI(input) return FtoaWithDigits(value, decimals) + " " + prefix + unit } var errInvalid = errors.New("invalid input") // ParseSI parses an SI string back into the number and unit. // // See also: SI, ComputeSI. // // e.g. ParseSI("2.2345 pF") -> (2.2345e-12, "F", nil) func ParseSI(input string) (float64, string, error) { found := riParseRegex.FindStringSubmatch(input) if len(found) != 4 { return 0, "", errInvalid } mag := revSIPrefixTable[found[2]] unit := found[3] base, err := strconv.ParseFloat(found[1], 64) return base * mag, unit, err } ================================================ FILE: vendor/github.com/dustin/go-humanize/times.go ================================================ package humanize import ( "fmt" "math" "sort" "time" ) // Seconds-based time units const ( Day = 24 * time.Hour Week = 7 * Day Month = 30 * Day Year = 12 * Month LongTime = 37 * Year ) // Time formats a time into a relative string. // // Time(someT) -> "3 weeks ago" func Time(then time.Time) string { return RelTime(then, time.Now(), "ago", "from now") } // A RelTimeMagnitude struct contains a relative time point at which // the relative format of time will switch to a new format string. A // slice of these in ascending order by their "D" field is passed to // CustomRelTime to format durations. // // The Format field is a string that may contain a "%s" which will be // replaced with the appropriate signed label (e.g. "ago" or "from // now") and a "%d" that will be replaced by the quantity. // // The DivBy field is the amount of time the time difference must be // divided by in order to display correctly. // // e.g. if D is 2*time.Minute and you want to display "%d minutes %s" // DivBy should be time.Minute so whatever the duration is will be // expressed in minutes. type RelTimeMagnitude struct { D time.Duration Format string DivBy time.Duration } var defaultMagnitudes = []RelTimeMagnitude{ {time.Second, "now", time.Second}, {2 * time.Second, "1 second %s", 1}, {time.Minute, "%d seconds %s", time.Second}, {2 * time.Minute, "1 minute %s", 1}, {time.Hour, "%d minutes %s", time.Minute}, {2 * time.Hour, "1 hour %s", 1}, {Day, "%d hours %s", time.Hour}, {2 * Day, "1 day %s", 1}, {Week, "%d days %s", Day}, {2 * Week, "1 week %s", 1}, {Month, "%d weeks %s", Week}, {2 * Month, "1 month %s", 1}, {Year, "%d months %s", Month}, {18 * Month, "1 year %s", 1}, {2 * Year, "2 years %s", 1}, {LongTime, "%d years %s", Year}, {math.MaxInt64, "a long while %s", 1}, } // RelTime formats a time into a relative string. // // It takes two times and two labels. In addition to the generic time // delta string (e.g. 5 minutes), the labels are used applied so that // the label corresponding to the smaller time is applied. // // RelTime(timeInPast, timeInFuture, "earlier", "later") -> "3 weeks earlier" func RelTime(a, b time.Time, albl, blbl string) string { return CustomRelTime(a, b, albl, blbl, defaultMagnitudes) } // CustomRelTime formats a time into a relative string. // // It takes two times two labels and a table of relative time formats. // In addition to the generic time delta string (e.g. 5 minutes), the // labels are used applied so that the label corresponding to the // smaller time is applied. func CustomRelTime(a, b time.Time, albl, blbl string, magnitudes []RelTimeMagnitude) string { lbl := albl diff := b.Sub(a) if a.After(b) { lbl = blbl diff = a.Sub(b) } n := sort.Search(len(magnitudes), func(i int) bool { return magnitudes[i].D > diff }) if n >= len(magnitudes) { n = len(magnitudes) - 1 } mag := magnitudes[n] args := []interface{}{} escaped := false for _, ch := range mag.Format { if escaped { switch ch { case 's': args = append(args, lbl) case 'd': args = append(args, diff/mag.DivBy) } escaped = false } else { escaped = ch == '%' } } return fmt.Sprintf(mag.Format, args...) } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/.gitignore ================================================ # Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe restful.html *.out tmp.prof go-restful.test examples/restful-basic-authentication examples/restful-encoding-filter examples/restful-filters examples/restful-hello-world examples/restful-resource-functions examples/restful-serve-static examples/restful-user-service *.DS_Store examples/restful-user-resource examples/restful-multi-containers examples/restful-form-handling examples/restful-CORS-filter examples/restful-options-filter examples/restful-curly-router examples/restful-cpuprofiler-service examples/restful-pre-post-filters curly.prof examples/restful-NCSA-logging examples/restful-html-template s.html restful-path-tail .idea ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/.goconvey ================================================ ignore ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/.travis.yml ================================================ language: go go: - 1.x before_install: - go test -v script: - go test -race -coverprofile=coverage.txt -covermode=atomic after_success: - bash <(curl -s https://codecov.io/bash) ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/CHANGES.md ================================================ # Change history of go-restful ## [v3.10.1] - 2022-11-19 - fix broken 3.10.0 by using path package for joining paths ## [v3.10.0] - 2022-10-11 - BROKEN - changed tokenizer to match std route match behavior; do not trimright the path (#511) - Add MIME_ZIP (#512) - Add MIME_ZIP and HEADER_ContentDisposition (#513) - Changed how to get query parameter issue #510 ## [v3.9.0] - 2022-07-21 - add support for http.Handler implementations to work as FilterFunction, issue #504 (thanks to https://github.com/ggicci) ## [v3.8.0] - 2022-06-06 - use exact matching of allowed domain entries, issue #489 (#493) - this changes fixes [security] Authorization Bypass Through User-Controlled Key by changing the behaviour of the AllowedDomains setting in the CORS filter. To support the previous behaviour, the CORS filter type now has a AllowedDomainFunc callback mechanism which is called when a simple domain match fails. - add test and fix for POST without body and Content-type, issue #492 (#496) - [Minor] Bad practice to have a mix of Receiver types. (#491) ## [v3.7.2] - 2021-11-24 - restored FilterChain (#482 by SVilgelm) ## [v3.7.1] - 2021-10-04 - fix problem with contentEncodingEnabled setting (#479) ## [v3.7.0] - 2021-09-24 - feat(parameter): adds additional openapi mappings (#478) ## [v3.6.0] - 2021-09-18 - add support for vendor extensions (#477 thx erraggy) ## [v3.5.2] - 2021-07-14 - fix removing absent route from webservice (#472) ## [v3.5.1] - 2021-04-12 - fix handling no match access selected path - remove obsolete field ## [v3.5.0] - 2021-04-10 - add check for wildcard (#463) in CORS - add access to Route from Request, issue #459 (#462) ## [v3.4.0] - 2020-11-10 - Added OPTIONS to WebService ## [v3.3.2] - 2020-01-23 - Fixed duplicate compression in dispatch. #449 ## [v3.3.1] - 2020-08-31 - Added check on writer to prevent compression of response twice. #447 ## [v3.3.0] - 2020-08-19 - Enable content encoding on Handle and ServeHTTP (#446) - List available representations in 406 body (#437) - Convert to string using rune() (#443) ## [v3.2.0] - 2020-06-21 - 405 Method Not Allowed must have Allow header (#436) (thx Bracken ) - add field allowedMethodsWithoutContentType (#424) ## [v3.1.0] - support describing response headers (#426) - fix openapi examples (#425) v3.0.0 - fix: use request/response resulting from filter chain - add Go module Module consumer should use github.com/emicklei/go-restful/v3 as import path v2.10.0 - support for Custom Verbs (thanks Vinci Xu <277040271@qq.com>) - fixed static example (thanks Arthur ) - simplify code (thanks Christian Muehlhaeuser ) - added JWT HMAC with SHA-512 authentication code example (thanks Amim Knabben ) v2.9.6 - small optimization in filter code v2.11.1 - fix WriteError return value (#415) v2.11.0 - allow prefix and suffix in path variable expression (#414) v2.9.6 - support google custome verb (#413) v2.9.5 - fix panic in Response.WriteError if err == nil v2.9.4 - fix issue #400 , parsing mime type quality - Route Builder added option for contentEncodingEnabled (#398) v2.9.3 - Avoid return of 415 Unsupported Media Type when request body is empty (#396) v2.9.2 - Reduce allocations in per-request methods to improve performance (#395) v2.9.1 - Fix issue with default responses and invalid status code 0. (#393) v2.9.0 - add per Route content encoding setting (overrides container setting) v2.8.0 - add Request.QueryParameters() - add json-iterator (via build tag) - disable vgo module (until log is moved) v2.7.1 - add vgo module v2.6.1 - add JSONNewDecoderFunc to allow custom JSON Decoder usage (go 1.10+) v2.6.0 - Make JSR 311 routing and path param processing consistent - Adding description to RouteBuilder.Reads() - Update example for Swagger12 and OpenAPI 2017-09-13 - added route condition functions using `.If(func)` in route building. 2017-02-16 - solved issue #304, make operation names unique 2017-01-30 [IMPORTANT] For swagger users, change your import statement to: swagger "github.com/emicklei/go-restful-swagger12" - moved swagger 1.2 code to go-restful-swagger12 - created TAG 2.0.0 2017-01-27 - remove defer request body close - expose Dispatch for testing filters and Routefunctions - swagger response model cannot be array - created TAG 1.0.0 2016-12-22 - (API change) Remove code related to caching request content. Removes SetCacheReadEntity(doCache bool) 2016-11-26 - Default change! now use CurlyRouter (was RouterJSR311) - Default change! no more caching of request content - Default change! do not recover from panics 2016-09-22 - fix the DefaultRequestContentType feature 2016-02-14 - take the qualify factor of the Accept header mediatype into account when deciding the contentype of the response - add constructors for custom entity accessors for xml and json 2015-09-27 - rename new WriteStatusAnd... to WriteHeaderAnd... for consistency 2015-09-25 - fixed problem with changing Header after WriteHeader (issue 235) 2015-09-14 - changed behavior of WriteHeader (immediate write) and WriteEntity (no status write) - added support for custom EntityReaderWriters. 2015-08-06 - add support for reading entities from compressed request content - use sync.Pool for compressors of http response and request body - add Description to Parameter for documentation in Swagger UI 2015-03-20 - add configurable logging 2015-03-18 - if not specified, the Operation is derived from the Route function 2015-03-17 - expose Parameter creation functions - make trace logger an interface - fix OPTIONSFilter - customize rendering of ServiceError - JSR311 router now handles wildcards - add Notes to Route 2014-11-27 - (api add) PrettyPrint per response. (as proposed in #167) 2014-11-12 - (api add) ApiVersion(.) for documentation in Swagger UI 2014-11-10 - (api change) struct fields tagged with "description" show up in Swagger UI 2014-10-31 - (api change) ReturnsError -> Returns - (api add) RouteBuilder.Do(aBuilder) for DRY use of RouteBuilder - fix swagger nested structs - sort Swagger response messages by code 2014-10-23 - (api add) ReturnsError allows you to document Http codes in swagger - fixed problem with greedy CurlyRouter - (api add) Access-Control-Max-Age in CORS - add tracing functionality (injectable) for debugging purposes - support JSON parse 64bit int - fix empty parameters for swagger - WebServicesUrl is now optional for swagger - fixed duplicate AccessControlAllowOrigin in CORS - (api change) expose ServeMux in container - (api add) added AllowedDomains in CORS - (api add) ParameterNamed for detailed documentation 2014-04-16 - (api add) expose constructor of Request for testing. 2014-06-27 - (api add) ParameterNamed gives access to a Parameter definition and its data (for further specification). - (api add) SetCacheReadEntity allow scontrol over whether or not the request body is being cached (default true for compatibility reasons). 2014-07-03 - (api add) CORS can be configured with a list of allowed domains 2014-03-12 - (api add) Route path parameters can use wildcard or regular expressions. (requires CurlyRouter) 2014-02-26 - (api add) Request now provides information about the matched Route, see method SelectedRoutePath 2014-02-17 - (api change) renamed parameter constants (go-lint checks) 2014-01-10 - (api add) support for CloseNotify, see http://golang.org/pkg/net/http/#CloseNotifier 2014-01-07 - (api change) Write* methods in Response now return the error or nil. - added example of serving HTML from a Go template. - fixed comparing Allowed headers in CORS (is now case-insensitive) 2013-11-13 - (api add) Response knows how many bytes are written to the response body. 2013-10-29 - (api add) RecoverHandler(handler RecoverHandleFunction) to change how panic recovery is handled. Default behavior is to log and return a stacktrace. This may be a security issue as it exposes sourcecode information. 2013-10-04 - (api add) Response knows what HTTP status has been written - (api add) Request can have attributes (map of string->interface, also called request-scoped variables 2013-09-12 - (api change) Router interface simplified - Implemented CurlyRouter, a Router that does not use|allow regular expressions in paths 2013-08-05 - add OPTIONS support - add CORS support 2013-08-27 - fixed some reported issues (see github) - (api change) deprecated use of WriteError; use WriteErrorString instead 2014-04-15 - (fix) v1.0.1 tag: fix Issue 111: WriteErrorString 2013-08-08 - (api add) Added implementation Container: a WebServices collection with its own http.ServeMux allowing multiple endpoints per program. Existing uses of go-restful will register their services to the DefaultContainer. - (api add) the swagger package has be extended to have a UI per container. - if panic is detected then a small stack trace is printed (thanks to runner-mei) - (api add) WriteErrorString to Response Important API changes: - (api remove) package variable DoNotRecover no longer works ; use restful.DefaultContainer.DoNotRecover(true) instead. - (api remove) package variable EnableContentEncoding no longer works ; use restful.DefaultContainer.EnableContentEncoding(true) instead. 2013-07-06 - (api add) Added support for response encoding (gzip and deflate(zlib)). This feature is disabled on default (for backwards compatibility). Use restful.EnableContentEncoding = true in your initialization to enable this feature. 2013-06-19 - (improve) DoNotRecover option, moved request body closer, improved ReadEntity 2013-06-03 - (api change) removed Dispatcher interface, hide PathExpression - changed receiver names of type functions to be more idiomatic Go 2013-06-02 - (optimize) Cache the RegExp compilation of Paths. 2013-05-22 - (api add) Added support for request/response filter functions 2013-05-18 - (api add) Added feature to change the default Http Request Dispatch function (travis cline) - (api change) Moved Swagger Webservice to swagger package (see example restful-user) [2012-11-14 .. 2013-05-18> - See https://github.com/emicklei/go-restful/commits 2012-11-14 - Initial commit ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/LICENSE ================================================ Copyright (c) 2012,2013 Ernest Micklei MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/Makefile ================================================ all: test test: go vet . go test -cover -v . ex: find ./examples -type f -name "*.go" | xargs -I {} go build -o /tmp/ignore {} ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/README.md ================================================ go-restful ========== package for building REST-style Web Services using Google Go [![Build Status](https://travis-ci.org/emicklei/go-restful.png)](https://travis-ci.org/emicklei/go-restful) [![Go Report Card](https://goreportcard.com/badge/github.com/emicklei/go-restful)](https://goreportcard.com/report/github.com/emicklei/go-restful) [![GoDoc](https://godoc.org/github.com/emicklei/go-restful?status.svg)](https://pkg.go.dev/github.com/emicklei/go-restful) [![codecov](https://codecov.io/gh/emicklei/go-restful/branch/master/graph/badge.svg)](https://codecov.io/gh/emicklei/go-restful) - [Code examples use v3](https://github.com/emicklei/go-restful/tree/v3/examples) REST asks developers to use HTTP methods explicitly and in a way that's consistent with the protocol definition. This basic REST design principle establishes a one-to-one mapping between create, read, update, and delete (CRUD) operations and HTTP methods. According to this mapping: - GET = Retrieve a representation of a resource - POST = Create if you are sending content to the server to create a subordinate of the specified resource collection, using some server-side algorithm. - PUT = Create if you are sending the full content of the specified resource (URI). - PUT = Update if you are updating the full content of the specified resource. - DELETE = Delete if you are requesting the server to delete the resource - PATCH = Update partial content of a resource - OPTIONS = Get information about the communication options for the request URI ### Usage #### Without Go Modules All versions up to `v2.*.*` (on the master) are not supporting Go modules. ``` import ( restful "github.com/emicklei/go-restful" ) ``` #### Using Go Modules As of version `v3.0.0` (on the v3 branch), this package supports Go modules. ``` import ( restful "github.com/emicklei/go-restful/v3" ) ``` ### Example ```Go ws := new(restful.WebService) ws. Path("/users"). Consumes(restful.MIME_XML, restful.MIME_JSON). Produces(restful.MIME_JSON, restful.MIME_XML) ws.Route(ws.GET("/{user-id}").To(u.findUser). Doc("get a user"). Param(ws.PathParameter("user-id", "identifier of the user").DataType("string")). Writes(User{})) ... func (u UserResource) findUser(request *restful.Request, response *restful.Response) { id := request.PathParameter("user-id") ... } ``` [Full API of a UserResource](https://github.com/emicklei/go-restful/blob/v3/examples/user-resource/restful-user-resource.go) ### Features - Routes for request → function mapping with path parameter (e.g. {id} but also prefix_{var} and {var}_suffix) support - Configurable router: - (default) Fast routing algorithm that allows static elements, [google custom method](https://cloud.google.com/apis/design/custom_methods), regular expressions and dynamic parameters in the URL path (e.g. /resource/name:customVerb, /meetings/{id} or /static/{subpath:*}) - Routing algorithm after [JSR311](http://jsr311.java.net/nonav/releases/1.1/spec/spec.html) that is implemented using (but does **not** accept) regular expressions - Request API for reading structs from JSON/XML and accessing parameters (path,query,header) - Response API for writing structs to JSON/XML and setting headers - Customizable encoding using EntityReaderWriter registration - Filters for intercepting the request → response flow on Service or Route level - Request-scoped variables using attributes - Containers for WebServices on different HTTP endpoints - Content encoding (gzip,deflate) of request and response payloads - Automatic responses on OPTIONS (using a filter) - Automatic CORS request handling (using a filter) - API declaration for Swagger UI ([go-restful-openapi](https://github.com/emicklei/go-restful-openapi), see [go-restful-swagger12](https://github.com/emicklei/go-restful-swagger12)) - Panic recovery to produce HTTP 500, customizable using RecoverHandler(...) - Route errors produce HTTP 404/405/406/415 errors, customizable using ServiceErrorHandler(...) - Configurable (trace) logging - Customizable gzip/deflate readers and writers using CompressorProvider registration - Inject your own http.Handler using the `HttpMiddlewareHandlerToFilter` function ## How to customize There are several hooks to customize the behavior of the go-restful package. - Router algorithm - Panic recovery - JSON decoder - Trace logging - Compression - Encoders for other serializers - Use [jsoniter](https://github.com/json-iterator/go) by building this package using a build tag, e.g. `go build -tags=jsoniter .` ## Resources - [Example programs](./examples) - [Example posted on blog](http://ernestmicklei.com/2012/11/go-restful-first-working-example/) - [Design explained on blog](http://ernestmicklei.com/2012/11/go-restful-api-design/) - [sourcegraph](https://sourcegraph.com/github.com/emicklei/go-restful) - [showcase: Zazkia - tcp proxy for testing resiliency](https://github.com/emicklei/zazkia) - [showcase: Mora - MongoDB REST Api server](https://github.com/emicklei/mora) Type ```git shortlog -s``` for a full list of contributors. © 2012 - 2022, http://ernestmicklei.com. MIT License. Contributions are welcome. ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/SECURITY.md ================================================ # Security Policy ## Supported Versions | Version | Supported | | ------- | ------------------ | | v3.7.x | :white_check_mark: | | < v3.0.1 | :x: | ## Reporting a Vulnerability Create an Issue and put the label `[security]` in the title of the issue. Valid reported security issues are expected to be solved within a week. ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/Srcfile ================================================ {"SkipDirs": ["examples"]} ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/bench_test.sh ================================================ #go test -run=none -file bench_test.go -test.bench . -cpuprofile=bench_test.out go test -c ./go-restful.test -test.run=none -test.cpuprofile=tmp.prof -test.bench=BenchmarkMany ./go-restful.test -test.run=none -test.cpuprofile=curly.prof -test.bench=BenchmarkManyCurly #go tool pprof go-restful.test tmp.prof go tool pprof go-restful.test curly.prof ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/compress.go ================================================ package restful // Copyright 2013 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. import ( "bufio" "compress/gzip" "compress/zlib" "errors" "io" "net" "net/http" "strings" ) // OBSOLETE : use restful.DefaultContainer.EnableContentEncoding(true) to change this setting. var EnableContentEncoding = false // CompressingResponseWriter is a http.ResponseWriter that can perform content encoding (gzip and zlib) type CompressingResponseWriter struct { writer http.ResponseWriter compressor io.WriteCloser encoding string } // Header is part of http.ResponseWriter interface func (c *CompressingResponseWriter) Header() http.Header { return c.writer.Header() } // WriteHeader is part of http.ResponseWriter interface func (c *CompressingResponseWriter) WriteHeader(status int) { c.writer.WriteHeader(status) } // Write is part of http.ResponseWriter interface // It is passed through the compressor func (c *CompressingResponseWriter) Write(bytes []byte) (int, error) { if c.isCompressorClosed() { return -1, errors.New("Compressing error: tried to write data using closed compressor") } return c.compressor.Write(bytes) } // CloseNotify is part of http.CloseNotifier interface func (c *CompressingResponseWriter) CloseNotify() <-chan bool { return c.writer.(http.CloseNotifier).CloseNotify() } // Close the underlying compressor func (c *CompressingResponseWriter) Close() error { if c.isCompressorClosed() { return errors.New("Compressing error: tried to close already closed compressor") } c.compressor.Close() if ENCODING_GZIP == c.encoding { currentCompressorProvider.ReleaseGzipWriter(c.compressor.(*gzip.Writer)) } if ENCODING_DEFLATE == c.encoding { currentCompressorProvider.ReleaseZlibWriter(c.compressor.(*zlib.Writer)) } // gc hint needed? c.compressor = nil return nil } func (c *CompressingResponseWriter) isCompressorClosed() bool { return nil == c.compressor } // Hijack implements the Hijacker interface // This is especially useful when combining Container.EnabledContentEncoding // in combination with websockets (for instance gorilla/websocket) func (c *CompressingResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { hijacker, ok := c.writer.(http.Hijacker) if !ok { return nil, nil, errors.New("ResponseWriter doesn't support Hijacker interface") } return hijacker.Hijack() } // WantsCompressedResponse reads the Accept-Encoding header to see if and which encoding is requested. // It also inspects the httpWriter whether its content-encoding is already set (non-empty). func wantsCompressedResponse(httpRequest *http.Request, httpWriter http.ResponseWriter) (bool, string) { if contentEncoding := httpWriter.Header().Get(HEADER_ContentEncoding); contentEncoding != "" { return false, "" } header := httpRequest.Header.Get(HEADER_AcceptEncoding) gi := strings.Index(header, ENCODING_GZIP) zi := strings.Index(header, ENCODING_DEFLATE) // use in order of appearance if gi == -1 { return zi != -1, ENCODING_DEFLATE } else if zi == -1 { return gi != -1, ENCODING_GZIP } else { if gi < zi { return true, ENCODING_GZIP } return true, ENCODING_DEFLATE } } // NewCompressingResponseWriter create a CompressingResponseWriter for a known encoding = {gzip,deflate} func NewCompressingResponseWriter(httpWriter http.ResponseWriter, encoding string) (*CompressingResponseWriter, error) { httpWriter.Header().Set(HEADER_ContentEncoding, encoding) c := new(CompressingResponseWriter) c.writer = httpWriter var err error if ENCODING_GZIP == encoding { w := currentCompressorProvider.AcquireGzipWriter() w.Reset(httpWriter) c.compressor = w c.encoding = ENCODING_GZIP } else if ENCODING_DEFLATE == encoding { w := currentCompressorProvider.AcquireZlibWriter() w.Reset(httpWriter) c.compressor = w c.encoding = ENCODING_DEFLATE } else { return nil, errors.New("Unknown encoding:" + encoding) } return c, err } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/compressor_cache.go ================================================ package restful // Copyright 2015 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. import ( "compress/gzip" "compress/zlib" ) // BoundedCachedCompressors is a CompressorProvider that uses a cache with a fixed amount // of writers and readers (resources). // If a new resource is acquired and all are in use, it will return a new unmanaged resource. type BoundedCachedCompressors struct { gzipWriters chan *gzip.Writer gzipReaders chan *gzip.Reader zlibWriters chan *zlib.Writer writersCapacity int readersCapacity int } // NewBoundedCachedCompressors returns a new, with filled cache, BoundedCachedCompressors. func NewBoundedCachedCompressors(writersCapacity, readersCapacity int) *BoundedCachedCompressors { b := &BoundedCachedCompressors{ gzipWriters: make(chan *gzip.Writer, writersCapacity), gzipReaders: make(chan *gzip.Reader, readersCapacity), zlibWriters: make(chan *zlib.Writer, writersCapacity), writersCapacity: writersCapacity, readersCapacity: readersCapacity, } for ix := 0; ix < writersCapacity; ix++ { b.gzipWriters <- newGzipWriter() b.zlibWriters <- newZlibWriter() } for ix := 0; ix < readersCapacity; ix++ { b.gzipReaders <- newGzipReader() } return b } // AcquireGzipWriter returns an resettable *gzip.Writer. Needs to be released. func (b *BoundedCachedCompressors) AcquireGzipWriter() *gzip.Writer { var writer *gzip.Writer select { case writer, _ = <-b.gzipWriters: default: // return a new unmanaged one writer = newGzipWriter() } return writer } // ReleaseGzipWriter accepts a writer (does not have to be one that was cached) // only when the cache has room for it. It will ignore it otherwise. func (b *BoundedCachedCompressors) ReleaseGzipWriter(w *gzip.Writer) { // forget the unmanaged ones if len(b.gzipWriters) < b.writersCapacity { b.gzipWriters <- w } } // AcquireGzipReader returns a *gzip.Reader. Needs to be released. func (b *BoundedCachedCompressors) AcquireGzipReader() *gzip.Reader { var reader *gzip.Reader select { case reader, _ = <-b.gzipReaders: default: // return a new unmanaged one reader = newGzipReader() } return reader } // ReleaseGzipReader accepts a reader (does not have to be one that was cached) // only when the cache has room for it. It will ignore it otherwise. func (b *BoundedCachedCompressors) ReleaseGzipReader(r *gzip.Reader) { // forget the unmanaged ones if len(b.gzipReaders) < b.readersCapacity { b.gzipReaders <- r } } // AcquireZlibWriter returns an resettable *zlib.Writer. Needs to be released. func (b *BoundedCachedCompressors) AcquireZlibWriter() *zlib.Writer { var writer *zlib.Writer select { case writer, _ = <-b.zlibWriters: default: // return a new unmanaged one writer = newZlibWriter() } return writer } // ReleaseZlibWriter accepts a writer (does not have to be one that was cached) // only when the cache has room for it. It will ignore it otherwise. func (b *BoundedCachedCompressors) ReleaseZlibWriter(w *zlib.Writer) { // forget the unmanaged ones if len(b.zlibWriters) < b.writersCapacity { b.zlibWriters <- w } } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/compressor_pools.go ================================================ package restful // Copyright 2015 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. import ( "bytes" "compress/gzip" "compress/zlib" "sync" ) // SyncPoolCompessors is a CompressorProvider that use the standard sync.Pool. type SyncPoolCompessors struct { GzipWriterPool *sync.Pool GzipReaderPool *sync.Pool ZlibWriterPool *sync.Pool } // NewSyncPoolCompessors returns a new ("empty") SyncPoolCompessors. func NewSyncPoolCompessors() *SyncPoolCompessors { return &SyncPoolCompessors{ GzipWriterPool: &sync.Pool{ New: func() interface{} { return newGzipWriter() }, }, GzipReaderPool: &sync.Pool{ New: func() interface{} { return newGzipReader() }, }, ZlibWriterPool: &sync.Pool{ New: func() interface{} { return newZlibWriter() }, }, } } func (s *SyncPoolCompessors) AcquireGzipWriter() *gzip.Writer { return s.GzipWriterPool.Get().(*gzip.Writer) } func (s *SyncPoolCompessors) ReleaseGzipWriter(w *gzip.Writer) { s.GzipWriterPool.Put(w) } func (s *SyncPoolCompessors) AcquireGzipReader() *gzip.Reader { return s.GzipReaderPool.Get().(*gzip.Reader) } func (s *SyncPoolCompessors) ReleaseGzipReader(r *gzip.Reader) { s.GzipReaderPool.Put(r) } func (s *SyncPoolCompessors) AcquireZlibWriter() *zlib.Writer { return s.ZlibWriterPool.Get().(*zlib.Writer) } func (s *SyncPoolCompessors) ReleaseZlibWriter(w *zlib.Writer) { s.ZlibWriterPool.Put(w) } func newGzipWriter() *gzip.Writer { // create with an empty bytes writer; it will be replaced before using the gzipWriter writer, err := gzip.NewWriterLevel(new(bytes.Buffer), gzip.BestSpeed) if err != nil { panic(err.Error()) } return writer } func newGzipReader() *gzip.Reader { // create with an empty reader (but with GZIP header); it will be replaced before using the gzipReader // we can safely use currentCompressProvider because it is set on package initialization. w := currentCompressorProvider.AcquireGzipWriter() defer currentCompressorProvider.ReleaseGzipWriter(w) b := new(bytes.Buffer) w.Reset(b) w.Flush() w.Close() reader, err := gzip.NewReader(bytes.NewReader(b.Bytes())) if err != nil { panic(err.Error()) } return reader } func newZlibWriter() *zlib.Writer { writer, err := zlib.NewWriterLevel(new(bytes.Buffer), gzip.BestSpeed) if err != nil { panic(err.Error()) } return writer } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/compressors.go ================================================ package restful // Copyright 2015 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. import ( "compress/gzip" "compress/zlib" ) // CompressorProvider describes a component that can provider compressors for the std methods. type CompressorProvider interface { // Returns a *gzip.Writer which needs to be released later. // Before using it, call Reset(). AcquireGzipWriter() *gzip.Writer // Releases an acquired *gzip.Writer. ReleaseGzipWriter(w *gzip.Writer) // Returns a *gzip.Reader which needs to be released later. AcquireGzipReader() *gzip.Reader // Releases an acquired *gzip.Reader. ReleaseGzipReader(w *gzip.Reader) // Returns a *zlib.Writer which needs to be released later. // Before using it, call Reset(). AcquireZlibWriter() *zlib.Writer // Releases an acquired *zlib.Writer. ReleaseZlibWriter(w *zlib.Writer) } // DefaultCompressorProvider is the actual provider of compressors (zlib or gzip). var currentCompressorProvider CompressorProvider func init() { currentCompressorProvider = NewSyncPoolCompessors() } // CurrentCompressorProvider returns the current CompressorProvider. // It is initialized using a SyncPoolCompessors. func CurrentCompressorProvider() CompressorProvider { return currentCompressorProvider } // SetCompressorProvider sets the actual provider of compressors (zlib or gzip). func SetCompressorProvider(p CompressorProvider) { if p == nil { panic("cannot set compressor provider to nil") } currentCompressorProvider = p } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/constants.go ================================================ package restful // Copyright 2013 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. const ( MIME_XML = "application/xml" // Accept or Content-Type used in Consumes() and/or Produces() MIME_JSON = "application/json" // Accept or Content-Type used in Consumes() and/or Produces() MIME_ZIP = "application/zip" // Accept or Content-Type used in Consumes() and/or Produces() MIME_OCTET = "application/octet-stream" // If Content-Type is not present in request, use the default HEADER_Allow = "Allow" HEADER_Accept = "Accept" HEADER_Origin = "Origin" HEADER_ContentType = "Content-Type" HEADER_ContentDisposition = "Content-Disposition" HEADER_LastModified = "Last-Modified" HEADER_AcceptEncoding = "Accept-Encoding" HEADER_ContentEncoding = "Content-Encoding" HEADER_AccessControlExposeHeaders = "Access-Control-Expose-Headers" HEADER_AccessControlRequestMethod = "Access-Control-Request-Method" HEADER_AccessControlRequestHeaders = "Access-Control-Request-Headers" HEADER_AccessControlAllowMethods = "Access-Control-Allow-Methods" HEADER_AccessControlAllowOrigin = "Access-Control-Allow-Origin" HEADER_AccessControlAllowCredentials = "Access-Control-Allow-Credentials" HEADER_AccessControlAllowHeaders = "Access-Control-Allow-Headers" HEADER_AccessControlMaxAge = "Access-Control-Max-Age" ENCODING_GZIP = "gzip" ENCODING_DEFLATE = "deflate" ) ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/container.go ================================================ package restful // Copyright 2013 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. import ( "bytes" "errors" "fmt" "net/http" "os" "runtime" "strings" "sync" "github.com/emicklei/go-restful/v3/log" ) // Container holds a collection of WebServices and a http.ServeMux to dispatch http requests. // The requests are further dispatched to routes of WebServices using a RouteSelector type Container struct { webServicesLock sync.RWMutex webServices []*WebService ServeMux *http.ServeMux isRegisteredOnRoot bool containerFilters []FilterFunction doNotRecover bool // default is true recoverHandleFunc RecoverHandleFunction serviceErrorHandleFunc ServiceErrorHandleFunction router RouteSelector // default is a CurlyRouter (RouterJSR311 is a slower alternative) contentEncodingEnabled bool // default is false } // NewContainer creates a new Container using a new ServeMux and default router (CurlyRouter) func NewContainer() *Container { return &Container{ webServices: []*WebService{}, ServeMux: http.NewServeMux(), isRegisteredOnRoot: false, containerFilters: []FilterFunction{}, doNotRecover: true, recoverHandleFunc: logStackOnRecover, serviceErrorHandleFunc: writeServiceError, router: CurlyRouter{}, contentEncodingEnabled: false} } // RecoverHandleFunction declares functions that can be used to handle a panic situation. // The first argument is what recover() returns. The second must be used to communicate an error response. type RecoverHandleFunction func(interface{}, http.ResponseWriter) // RecoverHandler changes the default function (logStackOnRecover) to be called // when a panic is detected. DoNotRecover must be have its default value (=false). func (c *Container) RecoverHandler(handler RecoverHandleFunction) { c.recoverHandleFunc = handler } // ServiceErrorHandleFunction declares functions that can be used to handle a service error situation. // The first argument is the service error, the second is the request that resulted in the error and // the third must be used to communicate an error response. type ServiceErrorHandleFunction func(ServiceError, *Request, *Response) // ServiceErrorHandler changes the default function (writeServiceError) to be called // when a ServiceError is detected. func (c *Container) ServiceErrorHandler(handler ServiceErrorHandleFunction) { c.serviceErrorHandleFunc = handler } // DoNotRecover controls whether panics will be caught to return HTTP 500. // If set to true, Route functions are responsible for handling any error situation. // Default value is true. func (c *Container) DoNotRecover(doNot bool) { c.doNotRecover = doNot } // Router changes the default Router (currently CurlyRouter) func (c *Container) Router(aRouter RouteSelector) { c.router = aRouter } // EnableContentEncoding (default=false) allows for GZIP or DEFLATE encoding of responses. func (c *Container) EnableContentEncoding(enabled bool) { c.contentEncodingEnabled = enabled } // Add a WebService to the Container. It will detect duplicate root paths and exit in that case. func (c *Container) Add(service *WebService) *Container { c.webServicesLock.Lock() defer c.webServicesLock.Unlock() // if rootPath was not set then lazy initialize it if len(service.rootPath) == 0 { service.Path("/") } // cannot have duplicate root paths for _, each := range c.webServices { if each.RootPath() == service.RootPath() { log.Printf("WebService with duplicate root path detected:['%v']", each) os.Exit(1) } } // If not registered on root then add specific mapping if !c.isRegisteredOnRoot { c.isRegisteredOnRoot = c.addHandler(service, c.ServeMux) } c.webServices = append(c.webServices, service) return c } // addHandler may set a new HandleFunc for the serveMux // this function must run inside the critical region protected by the webServicesLock. // returns true if the function was registered on root ("/") func (c *Container) addHandler(service *WebService, serveMux *http.ServeMux) bool { pattern := fixedPrefixPath(service.RootPath()) // check if root path registration is needed if "/" == pattern || "" == pattern { serveMux.HandleFunc("/", c.dispatch) return true } // detect if registration already exists alreadyMapped := false for _, each := range c.webServices { if each.RootPath() == service.RootPath() { alreadyMapped = true break } } if !alreadyMapped { serveMux.HandleFunc(pattern, c.dispatch) if !strings.HasSuffix(pattern, "/") { serveMux.HandleFunc(pattern+"/", c.dispatch) } } return false } func (c *Container) Remove(ws *WebService) error { if c.ServeMux == http.DefaultServeMux { errMsg := fmt.Sprintf("cannot remove a WebService from a Container using the DefaultServeMux: ['%v']", ws) log.Print(errMsg) return errors.New(errMsg) } c.webServicesLock.Lock() defer c.webServicesLock.Unlock() // build a new ServeMux and re-register all WebServices newServeMux := http.NewServeMux() newServices := []*WebService{} newIsRegisteredOnRoot := false for _, each := range c.webServices { if each.rootPath != ws.rootPath { // If not registered on root then add specific mapping if !newIsRegisteredOnRoot { newIsRegisteredOnRoot = c.addHandler(each, newServeMux) } newServices = append(newServices, each) } } c.webServices, c.ServeMux, c.isRegisteredOnRoot = newServices, newServeMux, newIsRegisteredOnRoot return nil } // logStackOnRecover is the default RecoverHandleFunction and is called // when DoNotRecover is false and the recoverHandleFunc is not set for the container. // Default implementation logs the stacktrace and writes the stacktrace on the response. // This may be a security issue as it exposes sourcecode information. func logStackOnRecover(panicReason interface{}, httpWriter http.ResponseWriter) { var buffer bytes.Buffer buffer.WriteString(fmt.Sprintf("recover from panic situation: - %v\r\n", panicReason)) for i := 2; ; i += 1 { _, file, line, ok := runtime.Caller(i) if !ok { break } buffer.WriteString(fmt.Sprintf(" %s:%d\r\n", file, line)) } log.Print(buffer.String()) httpWriter.WriteHeader(http.StatusInternalServerError) httpWriter.Write(buffer.Bytes()) } // writeServiceError is the default ServiceErrorHandleFunction and is called // when a ServiceError is returned during route selection. Default implementation // calls resp.WriteErrorString(err.Code, err.Message) func writeServiceError(err ServiceError, req *Request, resp *Response) { for header, values := range err.Header { for _, value := range values { resp.Header().Add(header, value) } } resp.WriteErrorString(err.Code, err.Message) } // Dispatch the incoming Http Request to a matching WebService. func (c *Container) Dispatch(httpWriter http.ResponseWriter, httpRequest *http.Request) { if httpWriter == nil { panic("httpWriter cannot be nil") } if httpRequest == nil { panic("httpRequest cannot be nil") } c.dispatch(httpWriter, httpRequest) } // Dispatch the incoming Http Request to a matching WebService. func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.Request) { // so we can assign a compressing one later writer := httpWriter // CompressingResponseWriter should be closed after all operations are done defer func() { if compressWriter, ok := writer.(*CompressingResponseWriter); ok { compressWriter.Close() } }() // Instal panic recovery unless told otherwise if !c.doNotRecover { // catch all for 500 response defer func() { if r := recover(); r != nil { c.recoverHandleFunc(r, writer) return } }() } // Find best match Route ; err is non nil if no match was found var webService *WebService var route *Route var err error func() { c.webServicesLock.RLock() defer c.webServicesLock.RUnlock() webService, route, err = c.router.SelectRoute( c.webServices, httpRequest) }() if err != nil { // a non-200 response (may be compressed) has already been written // run container filters anyway ; they should not touch the response... chain := FilterChain{Filters: c.containerFilters, Target: func(req *Request, resp *Response) { switch err.(type) { case ServiceError: ser := err.(ServiceError) c.serviceErrorHandleFunc(ser, req, resp) } // TODO }} chain.ProcessFilter(NewRequest(httpRequest), NewResponse(writer)) return } // Unless httpWriter is already an CompressingResponseWriter see if we need to install one if _, isCompressing := httpWriter.(*CompressingResponseWriter); !isCompressing { // Detect if compression is needed // assume without compression, test for override contentEncodingEnabled := c.contentEncodingEnabled if route != nil && route.contentEncodingEnabled != nil { contentEncodingEnabled = *route.contentEncodingEnabled } if contentEncodingEnabled { doCompress, encoding := wantsCompressedResponse(httpRequest, httpWriter) if doCompress { var err error writer, err = NewCompressingResponseWriter(httpWriter, encoding) if err != nil { log.Print("unable to install compressor: ", err) httpWriter.WriteHeader(http.StatusInternalServerError) return } } } } pathProcessor, routerProcessesPath := c.router.(PathProcessor) if !routerProcessesPath { pathProcessor = defaultPathProcessor{} } pathParams := pathProcessor.ExtractParameters(route, webService, httpRequest.URL.Path) wrappedRequest, wrappedResponse := route.wrapRequestResponse(writer, httpRequest, pathParams) // pass through filters (if any) if size := len(c.containerFilters) + len(webService.filters) + len(route.Filters); size > 0 { // compose filter chain allFilters := make([]FilterFunction, 0, size) allFilters = append(allFilters, c.containerFilters...) allFilters = append(allFilters, webService.filters...) allFilters = append(allFilters, route.Filters...) chain := FilterChain{ Filters: allFilters, Target: route.Function, ParameterDocs: route.ParameterDocs, Operation: route.Operation, } chain.ProcessFilter(wrappedRequest, wrappedResponse) } else { // no filters, handle request by route route.Function(wrappedRequest, wrappedResponse) } } // fixedPrefixPath returns the fixed part of the partspec ; it may include template vars {} func fixedPrefixPath(pathspec string) string { varBegin := strings.Index(pathspec, "{") if -1 == varBegin { return pathspec } return pathspec[:varBegin] } // ServeHTTP implements net/http.Handler therefore a Container can be a Handler in a http.Server func (c *Container) ServeHTTP(httpWriter http.ResponseWriter, httpRequest *http.Request) { // Skip, if content encoding is disabled if !c.contentEncodingEnabled { c.ServeMux.ServeHTTP(httpWriter, httpRequest) return } // content encoding is enabled // Skip, if httpWriter is already an CompressingResponseWriter if _, ok := httpWriter.(*CompressingResponseWriter); ok { c.ServeMux.ServeHTTP(httpWriter, httpRequest) return } writer := httpWriter // CompressingResponseWriter should be closed after all operations are done defer func() { if compressWriter, ok := writer.(*CompressingResponseWriter); ok { compressWriter.Close() } }() doCompress, encoding := wantsCompressedResponse(httpRequest, httpWriter) if doCompress { var err error writer, err = NewCompressingResponseWriter(httpWriter, encoding) if err != nil { log.Print("unable to install compressor: ", err) httpWriter.WriteHeader(http.StatusInternalServerError) return } } c.ServeMux.ServeHTTP(writer, httpRequest) } // Handle registers the handler for the given pattern. If a handler already exists for pattern, Handle panics. func (c *Container) Handle(pattern string, handler http.Handler) { c.ServeMux.Handle(pattern, http.HandlerFunc(func(httpWriter http.ResponseWriter, httpRequest *http.Request) { // Skip, if httpWriter is already an CompressingResponseWriter if _, ok := httpWriter.(*CompressingResponseWriter); ok { handler.ServeHTTP(httpWriter, httpRequest) return } writer := httpWriter // CompressingResponseWriter should be closed after all operations are done defer func() { if compressWriter, ok := writer.(*CompressingResponseWriter); ok { compressWriter.Close() } }() if c.contentEncodingEnabled { doCompress, encoding := wantsCompressedResponse(httpRequest, httpWriter) if doCompress { var err error writer, err = NewCompressingResponseWriter(httpWriter, encoding) if err != nil { log.Print("unable to install compressor: ", err) httpWriter.WriteHeader(http.StatusInternalServerError) return } } } handler.ServeHTTP(writer, httpRequest) })) } // HandleWithFilter registers the handler for the given pattern. // Container's filter chain is applied for handler. // If a handler already exists for pattern, HandleWithFilter panics. func (c *Container) HandleWithFilter(pattern string, handler http.Handler) { f := func(httpResponse http.ResponseWriter, httpRequest *http.Request) { if len(c.containerFilters) == 0 { handler.ServeHTTP(httpResponse, httpRequest) return } chain := FilterChain{Filters: c.containerFilters, Target: func(req *Request, resp *Response) { handler.ServeHTTP(resp, req.Request) }} chain.ProcessFilter(NewRequest(httpRequest), NewResponse(httpResponse)) } c.Handle(pattern, http.HandlerFunc(f)) } // Filter appends a container FilterFunction. These are called before dispatching // a http.Request to a WebService from the container func (c *Container) Filter(filter FilterFunction) { c.containerFilters = append(c.containerFilters, filter) } // RegisteredWebServices returns the collections of added WebServices func (c *Container) RegisteredWebServices() []*WebService { c.webServicesLock.RLock() defer c.webServicesLock.RUnlock() result := make([]*WebService, len(c.webServices)) for ix := range c.webServices { result[ix] = c.webServices[ix] } return result } // computeAllowedMethods returns a list of HTTP methods that are valid for a Request func (c *Container) computeAllowedMethods(req *Request) []string { // Go through all RegisteredWebServices() and all its Routes to collect the options methods := []string{} requestPath := req.Request.URL.Path for _, ws := range c.RegisteredWebServices() { matches := ws.pathExpr.Matcher.FindStringSubmatch(requestPath) if matches != nil { finalMatch := matches[len(matches)-1] for _, rt := range ws.Routes() { matches := rt.pathExpr.Matcher.FindStringSubmatch(finalMatch) if matches != nil { lastMatch := matches[len(matches)-1] if lastMatch == "" || lastMatch == "/" { // do not include if value is neither empty nor ‘/’. methods = append(methods, rt.Method) } } } } } // methods = append(methods, "OPTIONS") not sure about this return methods } // newBasicRequestResponse creates a pair of Request,Response from its http versions. // It is basic because no parameter or (produces) content-type information is given. func newBasicRequestResponse(httpWriter http.ResponseWriter, httpRequest *http.Request) (*Request, *Response) { resp := NewResponse(httpWriter) resp.requestAccept = httpRequest.Header.Get(HEADER_Accept) return NewRequest(httpRequest), resp } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/cors_filter.go ================================================ package restful // Copyright 2013 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. import ( "regexp" "strconv" "strings" ) // CrossOriginResourceSharing is used to create a Container Filter that implements CORS. // Cross-origin resource sharing (CORS) is a mechanism that allows JavaScript on a web page // to make XMLHttpRequests to another domain, not the domain the JavaScript originated from. // // http://en.wikipedia.org/wiki/Cross-origin_resource_sharing // http://enable-cors.org/server.html // http://www.html5rocks.com/en/tutorials/cors/#toc-handling-a-not-so-simple-request type CrossOriginResourceSharing struct { ExposeHeaders []string // list of Header names // AllowedHeaders is alist of Header names. Checking is case-insensitive. // The list may contain the special wildcard string ".*" ; all is allowed AllowedHeaders []string // AllowedDomains is a list of allowed values for Http Origin. // The list may contain the special wildcard string ".*" ; all is allowed // If empty all are allowed. AllowedDomains []string // AllowedDomainFunc is optional and is a function that will do the check // when the origin is not part of the AllowedDomains and it does not contain the wildcard ".*". AllowedDomainFunc func(origin string) bool // AllowedMethods is either empty or has a list of http methods names. Checking is case-insensitive. AllowedMethods []string MaxAge int // number of seconds before requiring new Options request CookiesAllowed bool Container *Container allowedOriginPatterns []*regexp.Regexp // internal field for origin regexp check. } // Filter is a filter function that implements the CORS flow as documented on http://enable-cors.org/server.html // and http://www.html5rocks.com/static/images/cors_server_flowchart.png func (c CrossOriginResourceSharing) Filter(req *Request, resp *Response, chain *FilterChain) { origin := req.Request.Header.Get(HEADER_Origin) if len(origin) == 0 { if trace { traceLogger.Print("no Http header Origin set") } chain.ProcessFilter(req, resp) return } if !c.isOriginAllowed(origin) { // check whether this origin is allowed if trace { traceLogger.Printf("HTTP Origin:%s is not part of %v, neither matches any part of %v", origin, c.AllowedDomains, c.allowedOriginPatterns) } chain.ProcessFilter(req, resp) return } if req.Request.Method != "OPTIONS" { c.doActualRequest(req, resp) chain.ProcessFilter(req, resp) return } if acrm := req.Request.Header.Get(HEADER_AccessControlRequestMethod); acrm != "" { c.doPreflightRequest(req, resp) } else { c.doActualRequest(req, resp) chain.ProcessFilter(req, resp) return } } func (c CrossOriginResourceSharing) doActualRequest(req *Request, resp *Response) { c.setOptionsHeaders(req, resp) // continue processing the response } func (c *CrossOriginResourceSharing) doPreflightRequest(req *Request, resp *Response) { if len(c.AllowedMethods) == 0 { if c.Container == nil { c.AllowedMethods = DefaultContainer.computeAllowedMethods(req) } else { c.AllowedMethods = c.Container.computeAllowedMethods(req) } } acrm := req.Request.Header.Get(HEADER_AccessControlRequestMethod) if !c.isValidAccessControlRequestMethod(acrm, c.AllowedMethods) { if trace { traceLogger.Printf("Http header %s:%s is not in %v", HEADER_AccessControlRequestMethod, acrm, c.AllowedMethods) } return } acrhs := req.Request.Header.Get(HEADER_AccessControlRequestHeaders) if len(acrhs) > 0 { for _, each := range strings.Split(acrhs, ",") { if !c.isValidAccessControlRequestHeader(strings.Trim(each, " ")) { if trace { traceLogger.Printf("Http header %s:%s is not in %v", HEADER_AccessControlRequestHeaders, acrhs, c.AllowedHeaders) } return } } } resp.AddHeader(HEADER_AccessControlAllowMethods, strings.Join(c.AllowedMethods, ",")) resp.AddHeader(HEADER_AccessControlAllowHeaders, acrhs) c.setOptionsHeaders(req, resp) // return http 200 response, no body } func (c CrossOriginResourceSharing) setOptionsHeaders(req *Request, resp *Response) { c.checkAndSetExposeHeaders(resp) c.setAllowOriginHeader(req, resp) c.checkAndSetAllowCredentials(resp) if c.MaxAge > 0 { resp.AddHeader(HEADER_AccessControlMaxAge, strconv.Itoa(c.MaxAge)) } } func (c CrossOriginResourceSharing) isOriginAllowed(origin string) bool { if len(origin) == 0 { return false } lowerOrigin := strings.ToLower(origin) if len(c.AllowedDomains) == 0 { if c.AllowedDomainFunc != nil { return c.AllowedDomainFunc(lowerOrigin) } return true } // exact match on each allowed domain for _, domain := range c.AllowedDomains { if domain == ".*" || strings.ToLower(domain) == lowerOrigin { return true } } if c.AllowedDomainFunc != nil { return c.AllowedDomainFunc(origin) } return false } func (c CrossOriginResourceSharing) setAllowOriginHeader(req *Request, resp *Response) { origin := req.Request.Header.Get(HEADER_Origin) if c.isOriginAllowed(origin) { resp.AddHeader(HEADER_AccessControlAllowOrigin, origin) } } func (c CrossOriginResourceSharing) checkAndSetExposeHeaders(resp *Response) { if len(c.ExposeHeaders) > 0 { resp.AddHeader(HEADER_AccessControlExposeHeaders, strings.Join(c.ExposeHeaders, ",")) } } func (c CrossOriginResourceSharing) checkAndSetAllowCredentials(resp *Response) { if c.CookiesAllowed { resp.AddHeader(HEADER_AccessControlAllowCredentials, "true") } } func (c CrossOriginResourceSharing) isValidAccessControlRequestMethod(method string, allowedMethods []string) bool { for _, each := range allowedMethods { if each == method { return true } } return false } func (c CrossOriginResourceSharing) isValidAccessControlRequestHeader(header string) bool { for _, each := range c.AllowedHeaders { if strings.ToLower(each) == strings.ToLower(header) { return true } if each == "*" { return true } } return false } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/coverage.sh ================================================ go test -coverprofile=coverage.out go tool cover -html=coverage.out ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/curly.go ================================================ package restful // Copyright 2013 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. import ( "net/http" "regexp" "sort" "strings" ) // CurlyRouter expects Routes with paths that contain zero or more parameters in curly brackets. type CurlyRouter struct{} // SelectRoute is part of the Router interface and returns the best match // for the WebService and its Route for the given Request. func (c CurlyRouter) SelectRoute( webServices []*WebService, httpRequest *http.Request) (selectedService *WebService, selected *Route, err error) { requestTokens := tokenizePath(httpRequest.URL.Path) detectedService := c.detectWebService(requestTokens, webServices) if detectedService == nil { if trace { traceLogger.Printf("no WebService was found to match URL path:%s\n", httpRequest.URL.Path) } return nil, nil, NewError(http.StatusNotFound, "404: Page Not Found") } candidateRoutes := c.selectRoutes(detectedService, requestTokens) if len(candidateRoutes) == 0 { if trace { traceLogger.Printf("no Route in WebService with path %s was found to match URL path:%s\n", detectedService.rootPath, httpRequest.URL.Path) } return detectedService, nil, NewError(http.StatusNotFound, "404: Page Not Found") } selectedRoute, err := c.detectRoute(candidateRoutes, httpRequest) if selectedRoute == nil { return detectedService, nil, err } return detectedService, selectedRoute, nil } // selectRoutes return a collection of Route from a WebService that matches the path tokens from the request. func (c CurlyRouter) selectRoutes(ws *WebService, requestTokens []string) sortableCurlyRoutes { candidates := make(sortableCurlyRoutes, 0, 8) for _, each := range ws.routes { matches, paramCount, staticCount := c.matchesRouteByPathTokens(each.pathParts, requestTokens, each.hasCustomVerb) if matches { candidates.add(curlyRoute{each, paramCount, staticCount}) // TODO make sure Routes() return pointers? } } sort.Sort(candidates) return candidates } // matchesRouteByPathTokens computes whether it matches, howmany parameters do match and what the number of static path elements are. func (c CurlyRouter) matchesRouteByPathTokens(routeTokens, requestTokens []string, routeHasCustomVerb bool) (matches bool, paramCount int, staticCount int) { if len(routeTokens) < len(requestTokens) { // proceed in matching only if last routeToken is wildcard count := len(routeTokens) if count == 0 || !strings.HasSuffix(routeTokens[count-1], "*}") { return false, 0, 0 } // proceed } for i, routeToken := range routeTokens { if i == len(requestTokens) { // reached end of request path return false, 0, 0 } requestToken := requestTokens[i] if routeHasCustomVerb && hasCustomVerb(routeToken){ if !isMatchCustomVerb(routeToken, requestToken) { return false, 0, 0 } staticCount++ requestToken = removeCustomVerb(requestToken) routeToken = removeCustomVerb(routeToken) } if strings.HasPrefix(routeToken, "{") { paramCount++ if colon := strings.Index(routeToken, ":"); colon != -1 { // match by regex matchesToken, matchesRemainder := c.regularMatchesPathToken(routeToken, colon, requestToken) if !matchesToken { return false, 0, 0 } if matchesRemainder { break } } } else { // no { prefix if requestToken != routeToken { return false, 0, 0 } staticCount++ } } return true, paramCount, staticCount } // regularMatchesPathToken tests whether the regular expression part of routeToken matches the requestToken or all remaining tokens // format routeToken is {someVar:someExpression}, e.g. {zipcode:[\d][\d][\d][\d][A-Z][A-Z]} func (c CurlyRouter) regularMatchesPathToken(routeToken string, colon int, requestToken string) (matchesToken bool, matchesRemainder bool) { regPart := routeToken[colon+1 : len(routeToken)-1] if regPart == "*" { if trace { traceLogger.Printf("wildcard parameter detected in route token %s that matches %s\n", routeToken, requestToken) } return true, true } matched, err := regexp.MatchString(regPart, requestToken) return (matched && err == nil), false } var jsr311Router = RouterJSR311{} // detectRoute selectes from a list of Route the first match by inspecting both the Accept and Content-Type // headers of the Request. See also RouterJSR311 in jsr311.go func (c CurlyRouter) detectRoute(candidateRoutes sortableCurlyRoutes, httpRequest *http.Request) (*Route, error) { // tracing is done inside detectRoute return jsr311Router.detectRoute(candidateRoutes.routes(), httpRequest) } // detectWebService returns the best matching webService given the list of path tokens. // see also computeWebserviceScore func (c CurlyRouter) detectWebService(requestTokens []string, webServices []*WebService) *WebService { var best *WebService score := -1 for _, each := range webServices { matches, eachScore := c.computeWebserviceScore(requestTokens, each.pathExpr.tokens) if matches && (eachScore > score) { best = each score = eachScore } } return best } // computeWebserviceScore returns whether tokens match and // the weighted score of the longest matching consecutive tokens from the beginning. func (c CurlyRouter) computeWebserviceScore(requestTokens []string, tokens []string) (bool, int) { if len(tokens) > len(requestTokens) { return false, 0 } score := 0 for i := 0; i < len(tokens); i++ { each := requestTokens[i] other := tokens[i] if len(each) == 0 && len(other) == 0 { score++ continue } if len(other) > 0 && strings.HasPrefix(other, "{") { // no empty match if len(each) == 0 { return false, score } score += 1 } else { // not a parameter if each != other { return false, score } score += (len(tokens) - i) * 10 //fuzzy } } return true, score } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/curly_route.go ================================================ package restful // Copyright 2013 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. // curlyRoute exits for sorting Routes by the CurlyRouter based on number of parameters and number of static path elements. type curlyRoute struct { route Route paramCount int staticCount int } // sortableCurlyRoutes orders by most parameters and path elements first. type sortableCurlyRoutes []curlyRoute func (s *sortableCurlyRoutes) add(route curlyRoute) { *s = append(*s, route) } func (s sortableCurlyRoutes) routes() (routes []Route) { routes = make([]Route, 0, len(s)) for _, each := range s { routes = append(routes, each.route) // TODO change return type } return routes } func (s sortableCurlyRoutes) Len() int { return len(s) } func (s sortableCurlyRoutes) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s sortableCurlyRoutes) Less(i, j int) bool { a := s[j] b := s[i] // primary key if a.staticCount < b.staticCount { return true } if a.staticCount > b.staticCount { return false } // secundary key if a.paramCount < b.paramCount { return true } if a.paramCount > b.paramCount { return false } return a.route.Path < b.route.Path } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/custom_verb.go ================================================ package restful import ( "fmt" "regexp" ) var ( customVerbReg = regexp.MustCompile(":([A-Za-z]+)$") ) func hasCustomVerb(routeToken string) bool { return customVerbReg.MatchString(routeToken) } func isMatchCustomVerb(routeToken string, pathToken string) bool { rs := customVerbReg.FindStringSubmatch(routeToken) if len(rs) < 2 { return false } customVerb := rs[1] specificVerbReg := regexp.MustCompile(fmt.Sprintf(":%s$", customVerb)) return specificVerbReg.MatchString(pathToken) } func removeCustomVerb(str string) string { return customVerbReg.ReplaceAllString(str, "") } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/doc.go ================================================ /* Package restful , a lean package for creating REST-style WebServices without magic. WebServices and Routes A WebService has a collection of Route objects that dispatch incoming Http Requests to a function calls. Typically, a WebService has a root path (e.g. /users) and defines common MIME types for its routes. WebServices must be added to a container (see below) in order to handler Http requests from a server. A Route is defined by a HTTP method, an URL path and (optionally) the MIME types it consumes (Content-Type) and produces (Accept). This package has the logic to find the best matching Route and if found, call its Function. ws := new(restful.WebService) ws. Path("/users"). Consumes(restful.MIME_JSON, restful.MIME_XML). Produces(restful.MIME_JSON, restful.MIME_XML) ws.Route(ws.GET("/{user-id}").To(u.findUser)) // u is a UserResource ... // GET http://localhost:8080/users/1 func (u UserResource) findUser(request *restful.Request, response *restful.Response) { id := request.PathParameter("user-id") ... } The (*Request, *Response) arguments provide functions for reading information from the request and writing information back to the response. See the example https://github.com/emicklei/go-restful/blob/v3/examples/user-resource/restful-user-resource.go with a full implementation. Regular expression matching Routes A Route parameter can be specified using the format "uri/{var[:regexp]}" or the special version "uri/{var:*}" for matching the tail of the path. For example, /persons/{name:[A-Z][A-Z]} can be used to restrict values for the parameter "name" to only contain capital alphabetic characters. Regular expressions must use the standard Go syntax as described in the regexp package. (https://code.google.com/p/re2/wiki/Syntax) This feature requires the use of a CurlyRouter. Containers A Container holds a collection of WebServices, Filters and a http.ServeMux for multiplexing http requests. Using the statements "restful.Add(...) and restful.Filter(...)" will register WebServices and Filters to the Default Container. The Default container of go-restful uses the http.DefaultServeMux. You can create your own Container and create a new http.Server for that particular container. container := restful.NewContainer() server := &http.Server{Addr: ":8081", Handler: container} Filters A filter dynamically intercepts requests and responses to transform or use the information contained in the requests or responses. You can use filters to perform generic logging, measurement, authentication, redirect, set response headers etc. In the restful package there are three hooks into the request,response flow where filters can be added. Each filter must define a FilterFunction: func (req *restful.Request, resp *restful.Response, chain *restful.FilterChain) Use the following statement to pass the request,response pair to the next filter or RouteFunction chain.ProcessFilter(req, resp) Container Filters These are processed before any registered WebService. // install a (global) filter for the default container (processed before any webservice) restful.Filter(globalLogging) WebService Filters These are processed before any Route of a WebService. // install a webservice filter (processed before any route) ws.Filter(webserviceLogging).Filter(measureTime) Route Filters These are processed before calling the function associated with the Route. // install 2 chained route filters (processed before calling findUser) ws.Route(ws.GET("/{user-id}").Filter(routeLogging).Filter(NewCountFilter().routeCounter).To(findUser)) See the example https://github.com/emicklei/go-restful/blob/v3/examples/filters/restful-filters.go with full implementations. Response Encoding Two encodings are supported: gzip and deflate. To enable this for all responses: restful.DefaultContainer.EnableContentEncoding(true) If a Http request includes the Accept-Encoding header then the response content will be compressed using the specified encoding. Alternatively, you can create a Filter that performs the encoding and install it per WebService or Route. See the example https://github.com/emicklei/go-restful/blob/v3/examples/encoding/restful-encoding-filter.go OPTIONS support By installing a pre-defined container filter, your Webservice(s) can respond to the OPTIONS Http request. Filter(OPTIONSFilter()) CORS By installing the filter of a CrossOriginResourceSharing (CORS), your WebService(s) can handle CORS requests. cors := CrossOriginResourceSharing{ExposeHeaders: []string{"X-My-Header"}, CookiesAllowed: false, Container: DefaultContainer} Filter(cors.Filter) Error Handling Unexpected things happen. If a request cannot be processed because of a failure, your service needs to tell via the response what happened and why. For this reason HTTP status codes exist and it is important to use the correct code in every exceptional situation. 400: Bad Request If path or query parameters are not valid (content or type) then use http.StatusBadRequest. 404: Not Found Despite a valid URI, the resource requested may not be available 500: Internal Server Error If the application logic could not process the request (or write the response) then use http.StatusInternalServerError. 405: Method Not Allowed The request has a valid URL but the method (GET,PUT,POST,...) is not allowed. 406: Not Acceptable The request does not have or has an unknown Accept Header set for this operation. 415: Unsupported Media Type The request does not have or has an unknown Content-Type Header set for this operation. ServiceError In addition to setting the correct (error) Http status code, you can choose to write a ServiceError message on the response. Performance options This package has several options that affect the performance of your service. It is important to understand them and how you can change it. restful.DefaultContainer.DoNotRecover(false) DoNotRecover controls whether panics will be caught to return HTTP 500. If set to false, the container will recover from panics. Default value is true restful.SetCompressorProvider(NewBoundedCachedCompressors(20, 20)) If content encoding is enabled then the default strategy for getting new gzip/zlib writers and readers is to use a sync.Pool. Because writers are expensive structures, performance is even more improved when using a preloaded cache. You can also inject your own implementation. Trouble shooting This package has the means to produce detail logging of the complete Http request matching process and filter invocation. Enabling this feature requires you to set an implementation of restful.StdLogger (e.g. log.Logger) instance such as: restful.TraceLogger(log.New(os.Stdout, "[restful] ", log.LstdFlags|log.Lshortfile)) Logging The restful.SetLogger() method allows you to override the logger used by the package. By default restful uses the standard library `log` package and logs to stdout. Different logging packages are supported as long as they conform to `StdLogger` interface defined in the `log` sub-package, writing an adapter for your preferred package is simple. Resources [project]: https://github.com/emicklei/go-restful [examples]: https://github.com/emicklei/go-restful/blob/master/examples [design]: http://ernestmicklei.com/2012/11/11/go-restful-api-design/ [showcases]: https://github.com/emicklei/mora, https://github.com/emicklei/landskape (c) 2012-2015, http://ernestmicklei.com. MIT License */ package restful ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/entity_accessors.go ================================================ package restful // Copyright 2015 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. import ( "encoding/xml" "strings" "sync" ) // EntityReaderWriter can read and write values using an encoding such as JSON,XML. type EntityReaderWriter interface { // Read a serialized version of the value from the request. // The Request may have a decompressing reader. Depends on Content-Encoding. Read(req *Request, v interface{}) error // Write a serialized version of the value on the response. // The Response may have a compressing writer. Depends on Accept-Encoding. // status should be a valid Http Status code Write(resp *Response, status int, v interface{}) error } // entityAccessRegistry is a singleton var entityAccessRegistry = &entityReaderWriters{ protection: new(sync.RWMutex), accessors: map[string]EntityReaderWriter{}, } // entityReaderWriters associates MIME to an EntityReaderWriter type entityReaderWriters struct { protection *sync.RWMutex accessors map[string]EntityReaderWriter } func init() { RegisterEntityAccessor(MIME_JSON, NewEntityAccessorJSON(MIME_JSON)) RegisterEntityAccessor(MIME_XML, NewEntityAccessorXML(MIME_XML)) } // RegisterEntityAccessor add/overrides the ReaderWriter for encoding content with this MIME type. func RegisterEntityAccessor(mime string, erw EntityReaderWriter) { entityAccessRegistry.protection.Lock() defer entityAccessRegistry.protection.Unlock() entityAccessRegistry.accessors[mime] = erw } // NewEntityAccessorJSON returns a new EntityReaderWriter for accessing JSON content. // This package is already initialized with such an accessor using the MIME_JSON contentType. func NewEntityAccessorJSON(contentType string) EntityReaderWriter { return entityJSONAccess{ContentType: contentType} } // NewEntityAccessorXML returns a new EntityReaderWriter for accessing XML content. // This package is already initialized with such an accessor using the MIME_XML contentType. func NewEntityAccessorXML(contentType string) EntityReaderWriter { return entityXMLAccess{ContentType: contentType} } // accessorAt returns the registered ReaderWriter for this MIME type. func (r *entityReaderWriters) accessorAt(mime string) (EntityReaderWriter, bool) { r.protection.RLock() defer r.protection.RUnlock() er, ok := r.accessors[mime] if !ok { // retry with reverse lookup // more expensive but we are in an exceptional situation anyway for k, v := range r.accessors { if strings.Contains(mime, k) { return v, true } } } return er, ok } // entityXMLAccess is a EntityReaderWriter for XML encoding type entityXMLAccess struct { // This is used for setting the Content-Type header when writing ContentType string } // Read unmarshalls the value from XML func (e entityXMLAccess) Read(req *Request, v interface{}) error { return xml.NewDecoder(req.Request.Body).Decode(v) } // Write marshalls the value to JSON and set the Content-Type Header. func (e entityXMLAccess) Write(resp *Response, status int, v interface{}) error { return writeXML(resp, status, e.ContentType, v) } // writeXML marshalls the value to JSON and set the Content-Type Header. func writeXML(resp *Response, status int, contentType string, v interface{}) error { if v == nil { resp.WriteHeader(status) // do not write a nil representation return nil } if resp.prettyPrint { // pretty output must be created and written explicitly output, err := xml.MarshalIndent(v, " ", " ") if err != nil { return err } resp.Header().Set(HEADER_ContentType, contentType) resp.WriteHeader(status) _, err = resp.Write([]byte(xml.Header)) if err != nil { return err } _, err = resp.Write(output) return err } // not-so-pretty resp.Header().Set(HEADER_ContentType, contentType) resp.WriteHeader(status) return xml.NewEncoder(resp).Encode(v) } // entityJSONAccess is a EntityReaderWriter for JSON encoding type entityJSONAccess struct { // This is used for setting the Content-Type header when writing ContentType string } // Read unmarshalls the value from JSON func (e entityJSONAccess) Read(req *Request, v interface{}) error { decoder := NewDecoder(req.Request.Body) decoder.UseNumber() return decoder.Decode(v) } // Write marshalls the value to JSON and set the Content-Type Header. func (e entityJSONAccess) Write(resp *Response, status int, v interface{}) error { return writeJSON(resp, status, e.ContentType, v) } // write marshalls the value to JSON and set the Content-Type Header. func writeJSON(resp *Response, status int, contentType string, v interface{}) error { if v == nil { resp.WriteHeader(status) // do not write a nil representation return nil } if resp.prettyPrint { // pretty output must be created and written explicitly output, err := MarshalIndent(v, "", " ") if err != nil { return err } resp.Header().Set(HEADER_ContentType, contentType) resp.WriteHeader(status) _, err = resp.Write(output) return err } // not-so-pretty resp.Header().Set(HEADER_ContentType, contentType) resp.WriteHeader(status) return NewEncoder(resp).Encode(v) } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/extensions.go ================================================ package restful // Copyright 2021 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. // ExtensionProperties provides storage of vendor extensions for entities type ExtensionProperties struct { // Extensions vendor extensions used to describe extra functionality // (https://swagger.io/docs/specification/2-0/swagger-extensions/) Extensions map[string]interface{} } // AddExtension adds or updates a key=value pair to the extension map. func (ep *ExtensionProperties) AddExtension(key string, value interface{}) { if ep.Extensions == nil { ep.Extensions = map[string]interface{}{key: value} } else { ep.Extensions[key] = value } } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/filter.go ================================================ package restful // Copyright 2013 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. // FilterChain is a request scoped object to process one or more filters before calling the target RouteFunction. type FilterChain struct { Filters []FilterFunction // ordered list of FilterFunction Index int // index into filters that is currently in progress Target RouteFunction // function to call after passing all filters ParameterDocs []*Parameter // the parameter docs for the route Operation string // the name of the operation } // ProcessFilter passes the request,response pair through the next of Filters. // Each filter can decide to proceed to the next Filter or handle the Response itself. func (f *FilterChain) ProcessFilter(request *Request, response *Response) { if f.Index < len(f.Filters) { f.Index++ f.Filters[f.Index-1](request, response, f) } else { f.Target(request, response) } } // FilterFunction definitions must call ProcessFilter on the FilterChain to pass on the control and eventually call the RouteFunction type FilterFunction func(*Request, *Response, *FilterChain) // NoBrowserCacheFilter is a filter function to set HTTP headers that disable browser caching // See examples/restful-no-cache-filter.go for usage func NoBrowserCacheFilter(req *Request, resp *Response, chain *FilterChain) { resp.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") // HTTP 1.1. resp.Header().Set("Pragma", "no-cache") // HTTP 1.0. resp.Header().Set("Expires", "0") // Proxies. chain.ProcessFilter(req, resp) } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/filter_adapter.go ================================================ package restful import ( "net/http" ) // HttpMiddlewareHandler is a function that takes a http.Handler and returns a http.Handler type HttpMiddlewareHandler func(http.Handler) http.Handler // HttpMiddlewareHandlerToFilter converts a HttpMiddlewareHandler to a FilterFunction. func HttpMiddlewareHandlerToFilter(middleware HttpMiddlewareHandler) FilterFunction { return func(req *Request, resp *Response, chain *FilterChain) { next := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { req.Request = r resp.ResponseWriter = rw chain.ProcessFilter(req, resp) }) middleware(next).ServeHTTP(resp.ResponseWriter, req.Request) } } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/json.go ================================================ // +build !jsoniter package restful import "encoding/json" var ( MarshalIndent = json.MarshalIndent NewDecoder = json.NewDecoder NewEncoder = json.NewEncoder ) ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/jsoniter.go ================================================ // +build jsoniter package restful import "github.com/json-iterator/go" var ( json = jsoniter.ConfigCompatibleWithStandardLibrary MarshalIndent = json.MarshalIndent NewDecoder = json.NewDecoder NewEncoder = json.NewEncoder ) ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/jsr311.go ================================================ package restful // Copyright 2013 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. import ( "errors" "fmt" "net/http" "sort" "strings" ) // RouterJSR311 implements the flow for matching Requests to Routes (and consequently Resource Functions) // as specified by the JSR311 http://jsr311.java.net/nonav/releases/1.1/spec/spec.html. // RouterJSR311 implements the Router interface. // Concept of locators is not implemented. type RouterJSR311 struct{} // SelectRoute is part of the Router interface and returns the best match // for the WebService and its Route for the given Request. func (r RouterJSR311) SelectRoute( webServices []*WebService, httpRequest *http.Request) (selectedService *WebService, selectedRoute *Route, err error) { // Identify the root resource class (WebService) dispatcher, finalMatch, err := r.detectDispatcher(httpRequest.URL.Path, webServices) if err != nil { return nil, nil, NewError(http.StatusNotFound, "") } // Obtain the set of candidate methods (Routes) routes := r.selectRoutes(dispatcher, finalMatch) if len(routes) == 0 { return dispatcher, nil, NewError(http.StatusNotFound, "404: Page Not Found") } // Identify the method (Route) that will handle the request route, ok := r.detectRoute(routes, httpRequest) return dispatcher, route, ok } // ExtractParameters is used to obtain the path parameters from the route using the same matching // engine as the JSR 311 router. func (r RouterJSR311) ExtractParameters(route *Route, webService *WebService, urlPath string) map[string]string { webServiceExpr := webService.pathExpr webServiceMatches := webServiceExpr.Matcher.FindStringSubmatch(urlPath) pathParameters := r.extractParams(webServiceExpr, webServiceMatches) routeExpr := route.pathExpr routeMatches := routeExpr.Matcher.FindStringSubmatch(webServiceMatches[len(webServiceMatches)-1]) routeParams := r.extractParams(routeExpr, routeMatches) for key, value := range routeParams { pathParameters[key] = value } return pathParameters } func (RouterJSR311) extractParams(pathExpr *pathExpression, matches []string) map[string]string { params := map[string]string{} for i := 1; i < len(matches); i++ { if len(pathExpr.VarNames) >= i { params[pathExpr.VarNames[i-1]] = matches[i] } } return params } // http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2 func (r RouterJSR311) detectRoute(routes []Route, httpRequest *http.Request) (*Route, error) { candidates := make([]*Route, 0, 8) for i, each := range routes { ok := true for _, fn := range each.If { if !fn(httpRequest) { ok = false break } } if ok { candidates = append(candidates, &routes[i]) } } if len(candidates) == 0 { if trace { traceLogger.Printf("no Route found (from %d) that passes conditional checks", len(routes)) } return nil, NewError(http.StatusNotFound, "404: Not Found") } // http method previous := candidates candidates = candidates[:0] for _, each := range previous { if httpRequest.Method == each.Method { candidates = append(candidates, each) } } if len(candidates) == 0 { if trace { traceLogger.Printf("no Route found (in %d routes) that matches HTTP method %s\n", len(previous), httpRequest.Method) } allowed := []string{} allowedLoop: for _, candidate := range previous { for _, method := range allowed { if method == candidate.Method { continue allowedLoop } } allowed = append(allowed, candidate.Method) } header := http.Header{"Allow": []string{strings.Join(allowed, ", ")}} return nil, NewErrorWithHeader(http.StatusMethodNotAllowed, "405: Method Not Allowed", header) } // content-type contentType := httpRequest.Header.Get(HEADER_ContentType) previous = candidates candidates = candidates[:0] for _, each := range previous { if each.matchesContentType(contentType) { candidates = append(candidates, each) } } if len(candidates) == 0 { if trace { traceLogger.Printf("no Route found (from %d) that matches HTTP Content-Type: %s\n", len(previous), contentType) } if httpRequest.ContentLength > 0 { return nil, NewError(http.StatusUnsupportedMediaType, "415: Unsupported Media Type") } } // accept previous = candidates candidates = candidates[:0] accept := httpRequest.Header.Get(HEADER_Accept) if len(accept) == 0 { accept = "*/*" } for _, each := range previous { if each.matchesAccept(accept) { candidates = append(candidates, each) } } if len(candidates) == 0 { if trace { traceLogger.Printf("no Route found (from %d) that matches HTTP Accept: %s\n", len(previous), accept) } available := []string{} for _, candidate := range previous { available = append(available, candidate.Produces...) } // if POST,PUT,PATCH without body method, length := httpRequest.Method, httpRequest.Header.Get("Content-Length") if (method == http.MethodPost || method == http.MethodPut || method == http.MethodPatch) && length == "" { return nil, NewError( http.StatusUnsupportedMediaType, fmt.Sprintf("415: Unsupported Media Type\n\nAvailable representations: %s", strings.Join(available, ", ")), ) } return nil, NewError( http.StatusNotAcceptable, fmt.Sprintf("406: Not Acceptable\n\nAvailable representations: %s", strings.Join(available, ", ")), ) } // return r.bestMatchByMedia(outputMediaOk, contentType, accept), nil return candidates[0], nil } // http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2 // n/m > n/* > */* func (r RouterJSR311) bestMatchByMedia(routes []Route, contentType string, accept string) *Route { // TODO return &routes[0] } // http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2 (step 2) func (r RouterJSR311) selectRoutes(dispatcher *WebService, pathRemainder string) []Route { filtered := &sortableRouteCandidates{} for _, each := range dispatcher.Routes() { pathExpr := each.pathExpr matches := pathExpr.Matcher.FindStringSubmatch(pathRemainder) if matches != nil { lastMatch := matches[len(matches)-1] if len(lastMatch) == 0 || lastMatch == "/" { // do not include if value is neither empty nor ‘/’. filtered.candidates = append(filtered.candidates, routeCandidate{each, len(matches) - 1, pathExpr.LiteralCount, pathExpr.VarCount}) } } } if len(filtered.candidates) == 0 { if trace { traceLogger.Printf("WebService on path %s has no routes that match URL path remainder:%s\n", dispatcher.rootPath, pathRemainder) } return []Route{} } sort.Sort(sort.Reverse(filtered)) // select other routes from candidates whoes expression matches rmatch matchingRoutes := []Route{filtered.candidates[0].route} for c := 1; c < len(filtered.candidates); c++ { each := filtered.candidates[c] if each.route.pathExpr.Matcher.MatchString(pathRemainder) { matchingRoutes = append(matchingRoutes, each.route) } } return matchingRoutes } // http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-360003.7.2 (step 1) func (r RouterJSR311) detectDispatcher(requestPath string, dispatchers []*WebService) (*WebService, string, error) { filtered := &sortableDispatcherCandidates{} for _, each := range dispatchers { matches := each.pathExpr.Matcher.FindStringSubmatch(requestPath) if matches != nil { filtered.candidates = append(filtered.candidates, dispatcherCandidate{each, matches[len(matches)-1], len(matches), each.pathExpr.LiteralCount, each.pathExpr.VarCount}) } } if len(filtered.candidates) == 0 { if trace { traceLogger.Printf("no WebService was found to match URL path:%s\n", requestPath) } return nil, "", errors.New("not found") } sort.Sort(sort.Reverse(filtered)) return filtered.candidates[0].dispatcher, filtered.candidates[0].finalMatch, nil } // Types and functions to support the sorting of Routes type routeCandidate struct { route Route matchesCount int // the number of capturing groups literalCount int // the number of literal characters (means those not resulting from template variable substitution) nonDefaultCount int // the number of capturing groups with non-default regular expressions (i.e. not ‘([^ /]+?)’) } func (r routeCandidate) expressionToMatch() string { return r.route.pathExpr.Source } func (r routeCandidate) String() string { return fmt.Sprintf("(m=%d,l=%d,n=%d)", r.matchesCount, r.literalCount, r.nonDefaultCount) } type sortableRouteCandidates struct { candidates []routeCandidate } func (rcs *sortableRouteCandidates) Len() int { return len(rcs.candidates) } func (rcs *sortableRouteCandidates) Swap(i, j int) { rcs.candidates[i], rcs.candidates[j] = rcs.candidates[j], rcs.candidates[i] } func (rcs *sortableRouteCandidates) Less(i, j int) bool { ci := rcs.candidates[i] cj := rcs.candidates[j] // primary key if ci.literalCount < cj.literalCount { return true } if ci.literalCount > cj.literalCount { return false } // secundary key if ci.matchesCount < cj.matchesCount { return true } if ci.matchesCount > cj.matchesCount { return false } // tertiary key if ci.nonDefaultCount < cj.nonDefaultCount { return true } if ci.nonDefaultCount > cj.nonDefaultCount { return false } // quaternary key ("source" is interpreted as Path) return ci.route.Path < cj.route.Path } // Types and functions to support the sorting of Dispatchers type dispatcherCandidate struct { dispatcher *WebService finalMatch string matchesCount int // the number of capturing groups literalCount int // the number of literal characters (means those not resulting from template variable substitution) nonDefaultCount int // the number of capturing groups with non-default regular expressions (i.e. not ‘([^ /]+?)’) } type sortableDispatcherCandidates struct { candidates []dispatcherCandidate } func (dc *sortableDispatcherCandidates) Len() int { return len(dc.candidates) } func (dc *sortableDispatcherCandidates) Swap(i, j int) { dc.candidates[i], dc.candidates[j] = dc.candidates[j], dc.candidates[i] } func (dc *sortableDispatcherCandidates) Less(i, j int) bool { ci := dc.candidates[i] cj := dc.candidates[j] // primary key if ci.matchesCount < cj.matchesCount { return true } if ci.matchesCount > cj.matchesCount { return false } // secundary key if ci.literalCount < cj.literalCount { return true } if ci.literalCount > cj.literalCount { return false } // tertiary key return ci.nonDefaultCount < cj.nonDefaultCount } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/log/log.go ================================================ package log import ( stdlog "log" "os" ) // StdLogger corresponds to a minimal subset of the interface satisfied by stdlib log.Logger type StdLogger interface { Print(v ...interface{}) Printf(format string, v ...interface{}) } var Logger StdLogger func init() { // default Logger SetLogger(stdlog.New(os.Stderr, "[restful] ", stdlog.LstdFlags|stdlog.Lshortfile)) } // SetLogger sets the logger for this package func SetLogger(customLogger StdLogger) { Logger = customLogger } // Print delegates to the Logger func Print(v ...interface{}) { Logger.Print(v...) } // Printf delegates to the Logger func Printf(format string, v ...interface{}) { Logger.Printf(format, v...) } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/logger.go ================================================ package restful // Copyright 2014 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. import ( "github.com/emicklei/go-restful/v3/log" ) var trace bool = false var traceLogger log.StdLogger func init() { traceLogger = log.Logger // use the package logger by default } // TraceLogger enables detailed logging of Http request matching and filter invocation. Default no logger is set. // You may call EnableTracing() directly to enable trace logging to the package-wide logger. func TraceLogger(logger log.StdLogger) { traceLogger = logger EnableTracing(logger != nil) } // SetLogger exposes the setter for the global logger on the top-level package func SetLogger(customLogger log.StdLogger) { log.SetLogger(customLogger) } // EnableTracing can be used to Trace logging on and off. func EnableTracing(enabled bool) { trace = enabled } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/mime.go ================================================ package restful import ( "strconv" "strings" ) type mime struct { media string quality float64 } // insertMime adds a mime to a list and keeps it sorted by quality. func insertMime(l []mime, e mime) []mime { for i, each := range l { // if current mime has lower quality then insert before if e.quality > each.quality { left := append([]mime{}, l[0:i]...) return append(append(left, e), l[i:]...) } } return append(l, e) } const qFactorWeightingKey = "q" // sortedMimes returns a list of mime sorted (desc) by its specified quality. // e.g. text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3 func sortedMimes(accept string) (sorted []mime) { for _, each := range strings.Split(accept, ",") { typeAndQuality := strings.Split(strings.Trim(each, " "), ";") if len(typeAndQuality) == 1 { sorted = insertMime(sorted, mime{typeAndQuality[0], 1.0}) } else { // take factor qAndWeight := strings.Split(typeAndQuality[1], "=") if len(qAndWeight) == 2 && strings.Trim(qAndWeight[0], " ") == qFactorWeightingKey { f, err := strconv.ParseFloat(qAndWeight[1], 64) if err != nil { traceLogger.Printf("unable to parse quality in %s, %v", each, err) } else { sorted = insertMime(sorted, mime{typeAndQuality[0], f}) } } else { sorted = insertMime(sorted, mime{typeAndQuality[0], 1.0}) } } } return } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/options_filter.go ================================================ package restful import "strings" // Copyright 2013 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. // OPTIONSFilter is a filter function that inspects the Http Request for the OPTIONS method // and provides the response with a set of allowed methods for the request URL Path. // As for any filter, you can also install it for a particular WebService within a Container. // Note: this filter is not needed when using CrossOriginResourceSharing (for CORS). func (c *Container) OPTIONSFilter(req *Request, resp *Response, chain *FilterChain) { if "OPTIONS" != req.Request.Method { chain.ProcessFilter(req, resp) return } archs := req.Request.Header.Get(HEADER_AccessControlRequestHeaders) methods := strings.Join(c.computeAllowedMethods(req), ",") origin := req.Request.Header.Get(HEADER_Origin) resp.AddHeader(HEADER_Allow, methods) resp.AddHeader(HEADER_AccessControlAllowOrigin, origin) resp.AddHeader(HEADER_AccessControlAllowHeaders, archs) resp.AddHeader(HEADER_AccessControlAllowMethods, methods) } // OPTIONSFilter is a filter function that inspects the Http Request for the OPTIONS method // and provides the response with a set of allowed methods for the request URL Path. // Note: this filter is not needed when using CrossOriginResourceSharing (for CORS). func OPTIONSFilter() FilterFunction { return DefaultContainer.OPTIONSFilter } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/parameter.go ================================================ package restful import "sort" // Copyright 2013 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. const ( // PathParameterKind = indicator of Request parameter type "path" PathParameterKind = iota // QueryParameterKind = indicator of Request parameter type "query" QueryParameterKind // BodyParameterKind = indicator of Request parameter type "body" BodyParameterKind // HeaderParameterKind = indicator of Request parameter type "header" HeaderParameterKind // FormParameterKind = indicator of Request parameter type "form" FormParameterKind // MultiPartFormParameterKind = indicator of Request parameter type "multipart/form-data" MultiPartFormParameterKind // CollectionFormatCSV comma separated values `foo,bar` CollectionFormatCSV = CollectionFormat("csv") // CollectionFormatSSV space separated values `foo bar` CollectionFormatSSV = CollectionFormat("ssv") // CollectionFormatTSV tab separated values `foo\tbar` CollectionFormatTSV = CollectionFormat("tsv") // CollectionFormatPipes pipe separated values `foo|bar` CollectionFormatPipes = CollectionFormat("pipes") // CollectionFormatMulti corresponds to multiple parameter instances instead of multiple values for a single // instance `foo=bar&foo=baz`. This is valid only for QueryParameters and FormParameters CollectionFormatMulti = CollectionFormat("multi") ) type CollectionFormat string func (cf CollectionFormat) String() string { return string(cf) } // Parameter is for documententing the parameter used in a Http Request // ParameterData kinds are Path,Query and Body type Parameter struct { data *ParameterData } // ParameterData represents the state of a Parameter. // It is made public to make it accessible to e.g. the Swagger package. type ParameterData struct { ExtensionProperties Name, Description, DataType, DataFormat string Kind int Required bool // AllowableValues is deprecated. Use PossibleValues instead AllowableValues map[string]string PossibleValues []string AllowMultiple bool AllowEmptyValue bool DefaultValue string CollectionFormat string Pattern string Minimum *float64 Maximum *float64 MinLength *int64 MaxLength *int64 MinItems *int64 MaxItems *int64 UniqueItems bool } // Data returns the state of the Parameter func (p *Parameter) Data() ParameterData { return *p.data } // Kind returns the parameter type indicator (see const for valid values) func (p *Parameter) Kind() int { return p.data.Kind } func (p *Parameter) bePath() *Parameter { p.data.Kind = PathParameterKind return p } func (p *Parameter) beQuery() *Parameter { p.data.Kind = QueryParameterKind return p } func (p *Parameter) beBody() *Parameter { p.data.Kind = BodyParameterKind return p } func (p *Parameter) beHeader() *Parameter { p.data.Kind = HeaderParameterKind return p } func (p *Parameter) beForm() *Parameter { p.data.Kind = FormParameterKind return p } func (p *Parameter) beMultiPartForm() *Parameter { p.data.Kind = MultiPartFormParameterKind return p } // Required sets the required field and returns the receiver func (p *Parameter) Required(required bool) *Parameter { p.data.Required = required return p } // AllowMultiple sets the allowMultiple field and returns the receiver func (p *Parameter) AllowMultiple(multiple bool) *Parameter { p.data.AllowMultiple = multiple return p } // AddExtension adds or updates a key=value pair to the extension map func (p *Parameter) AddExtension(key string, value interface{}) *Parameter { p.data.AddExtension(key, value) return p } // AllowEmptyValue sets the AllowEmptyValue field and returns the receiver func (p *Parameter) AllowEmptyValue(multiple bool) *Parameter { p.data.AllowEmptyValue = multiple return p } // AllowableValues is deprecated. Use PossibleValues instead. Both will be set. func (p *Parameter) AllowableValues(values map[string]string) *Parameter { p.data.AllowableValues = values allowableSortedKeys := make([]string, 0, len(values)) for k := range values { allowableSortedKeys = append(allowableSortedKeys, k) } sort.Strings(allowableSortedKeys) p.data.PossibleValues = make([]string, 0, len(values)) for _, k := range allowableSortedKeys { p.data.PossibleValues = append(p.data.PossibleValues, values[k]) } return p } // PossibleValues sets the possible values field and returns the receiver func (p *Parameter) PossibleValues(values []string) *Parameter { p.data.PossibleValues = values return p } // DataType sets the dataType field and returns the receiver func (p *Parameter) DataType(typeName string) *Parameter { p.data.DataType = typeName return p } // DataFormat sets the dataFormat field for Swagger UI func (p *Parameter) DataFormat(formatName string) *Parameter { p.data.DataFormat = formatName return p } // DefaultValue sets the default value field and returns the receiver func (p *Parameter) DefaultValue(stringRepresentation string) *Parameter { p.data.DefaultValue = stringRepresentation return p } // Description sets the description value field and returns the receiver func (p *Parameter) Description(doc string) *Parameter { p.data.Description = doc return p } // CollectionFormat sets the collection format for an array type func (p *Parameter) CollectionFormat(format CollectionFormat) *Parameter { p.data.CollectionFormat = format.String() return p } // Pattern sets the pattern field and returns the receiver func (p *Parameter) Pattern(pattern string) *Parameter { p.data.Pattern = pattern return p } // Minimum sets the minimum field and returns the receiver func (p *Parameter) Minimum(minimum float64) *Parameter { p.data.Minimum = &minimum return p } // Maximum sets the maximum field and returns the receiver func (p *Parameter) Maximum(maximum float64) *Parameter { p.data.Maximum = &maximum return p } // MinLength sets the minLength field and returns the receiver func (p *Parameter) MinLength(minLength int64) *Parameter { p.data.MinLength = &minLength return p } // MaxLength sets the maxLength field and returns the receiver func (p *Parameter) MaxLength(maxLength int64) *Parameter { p.data.MaxLength = &maxLength return p } // MinItems sets the minItems field and returns the receiver func (p *Parameter) MinItems(minItems int64) *Parameter { p.data.MinItems = &minItems return p } // MaxItems sets the maxItems field and returns the receiver func (p *Parameter) MaxItems(maxItems int64) *Parameter { p.data.MaxItems = &maxItems return p } // UniqueItems sets the uniqueItems field and returns the receiver func (p *Parameter) UniqueItems(uniqueItems bool) *Parameter { p.data.UniqueItems = uniqueItems return p } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/path_expression.go ================================================ package restful // Copyright 2013 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. import ( "bytes" "fmt" "regexp" "strings" ) // PathExpression holds a compiled path expression (RegExp) needed to match against // Http request paths and to extract path parameter values. type pathExpression struct { LiteralCount int // the number of literal characters (means those not resulting from template variable substitution) VarNames []string // the names of parameters (enclosed by {}) in the path VarCount int // the number of named parameters (enclosed by {}) in the path Matcher *regexp.Regexp Source string // Path as defined by the RouteBuilder tokens []string } // NewPathExpression creates a PathExpression from the input URL path. // Returns an error if the path is invalid. func newPathExpression(path string) (*pathExpression, error) { expression, literalCount, varNames, varCount, tokens := templateToRegularExpression(path) compiled, err := regexp.Compile(expression) if err != nil { return nil, err } return &pathExpression{literalCount, varNames, varCount, compiled, expression, tokens}, nil } // http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-370003.7.3 func templateToRegularExpression(template string) (expression string, literalCount int, varNames []string, varCount int, tokens []string) { var buffer bytes.Buffer buffer.WriteString("^") //tokens = strings.Split(template, "/") tokens = tokenizePath(template) for _, each := range tokens { if each == "" { continue } buffer.WriteString("/") if strings.HasPrefix(each, "{") { // check for regular expression in variable colon := strings.Index(each, ":") var varName string if colon != -1 { // extract expression varName = strings.TrimSpace(each[1:colon]) paramExpr := strings.TrimSpace(each[colon+1 : len(each)-1]) if paramExpr == "*" { // special case buffer.WriteString("(.*)") } else { buffer.WriteString(fmt.Sprintf("(%s)", paramExpr)) // between colon and closing moustache } } else { // plain var varName = strings.TrimSpace(each[1 : len(each)-1]) buffer.WriteString("([^/]+?)") } varNames = append(varNames, varName) varCount += 1 } else { literalCount += len(each) encoded := each // TODO URI encode buffer.WriteString(regexp.QuoteMeta(encoded)) } } return strings.TrimRight(buffer.String(), "/") + "(/.*)?$", literalCount, varNames, varCount, tokens } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/path_processor.go ================================================ package restful import ( "bytes" "strings" ) // Copyright 2018 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. // PathProcessor is extra behaviour that a Router can provide to extract path parameters from the path. // If a Router does not implement this interface then the default behaviour will be used. type PathProcessor interface { // ExtractParameters gets the path parameters defined in the route and webService from the urlPath ExtractParameters(route *Route, webService *WebService, urlPath string) map[string]string } type defaultPathProcessor struct{} // Extract the parameters from the request url path func (d defaultPathProcessor) ExtractParameters(r *Route, _ *WebService, urlPath string) map[string]string { urlParts := tokenizePath(urlPath) pathParameters := map[string]string{} for i, key := range r.pathParts { var value string if i >= len(urlParts) { value = "" } else { value = urlParts[i] } if r.hasCustomVerb && hasCustomVerb(key) { key = removeCustomVerb(key) value = removeCustomVerb(value) } if strings.Index(key, "{") > -1 { // path-parameter if colon := strings.Index(key, ":"); colon != -1 { // extract by regex regPart := key[colon+1 : len(key)-1] keyPart := key[1:colon] if regPart == "*" { pathParameters[keyPart] = untokenizePath(i, urlParts) break } else { pathParameters[keyPart] = value } } else { // without enclosing {} startIndex := strings.Index(key, "{") endKeyIndex := strings.Index(key, "}") suffixLength := len(key) - endKeyIndex - 1 endValueIndex := len(value) - suffixLength pathParameters[key[startIndex+1:endKeyIndex]] = value[startIndex:endValueIndex] } } } return pathParameters } // Untokenize back into an URL path using the slash separator func untokenizePath(offset int, parts []string) string { var buffer bytes.Buffer for p := offset; p < len(parts); p++ { buffer.WriteString(parts[p]) // do not end if p < len(parts)-1 { buffer.WriteString("/") } } return buffer.String() } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/request.go ================================================ package restful // Copyright 2013 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. import ( "compress/zlib" "net/http" ) var defaultRequestContentType string // Request is a wrapper for a http Request that provides convenience methods type Request struct { Request *http.Request pathParameters map[string]string attributes map[string]interface{} // for storing request-scoped values selectedRoute *Route // is nil when no route was matched } func NewRequest(httpRequest *http.Request) *Request { return &Request{ Request: httpRequest, pathParameters: map[string]string{}, attributes: map[string]interface{}{}, } // empty parameters, attributes } // If ContentType is missing or */* is given then fall back to this type, otherwise // a "Unable to unmarshal content of type:" response is returned. // Valid values are restful.MIME_JSON and restful.MIME_XML // Example: // // restful.DefaultRequestContentType(restful.MIME_JSON) func DefaultRequestContentType(mime string) { defaultRequestContentType = mime } // PathParameter accesses the Path parameter value by its name func (r *Request) PathParameter(name string) string { return r.pathParameters[name] } // PathParameters accesses the Path parameter values func (r *Request) PathParameters() map[string]string { return r.pathParameters } // QueryParameter returns the (first) Query parameter value by its name func (r *Request) QueryParameter(name string) string { return r.Request.URL.Query().Get(name) } // QueryParameters returns the all the query parameters values by name func (r *Request) QueryParameters(name string) []string { return r.Request.URL.Query()[name] } // BodyParameter parses the body of the request (once for typically a POST or a PUT) and returns the value of the given name or an error. func (r *Request) BodyParameter(name string) (string, error) { err := r.Request.ParseForm() if err != nil { return "", err } return r.Request.PostFormValue(name), nil } // HeaderParameter returns the HTTP Header value of a Header name or empty if missing func (r *Request) HeaderParameter(name string) string { return r.Request.Header.Get(name) } // ReadEntity checks the Accept header and reads the content into the entityPointer. func (r *Request) ReadEntity(entityPointer interface{}) (err error) { contentType := r.Request.Header.Get(HEADER_ContentType) contentEncoding := r.Request.Header.Get(HEADER_ContentEncoding) // check if the request body needs decompression if ENCODING_GZIP == contentEncoding { gzipReader := currentCompressorProvider.AcquireGzipReader() defer currentCompressorProvider.ReleaseGzipReader(gzipReader) gzipReader.Reset(r.Request.Body) r.Request.Body = gzipReader } else if ENCODING_DEFLATE == contentEncoding { zlibReader, err := zlib.NewReader(r.Request.Body) if err != nil { return err } r.Request.Body = zlibReader } // lookup the EntityReader, use defaultRequestContentType if needed and provided entityReader, ok := entityAccessRegistry.accessorAt(contentType) if !ok { if len(defaultRequestContentType) != 0 { entityReader, ok = entityAccessRegistry.accessorAt(defaultRequestContentType) } if !ok { return NewError(http.StatusBadRequest, "Unable to unmarshal content of type:"+contentType) } } return entityReader.Read(r, entityPointer) } // SetAttribute adds or replaces the attribute with the given value. func (r *Request) SetAttribute(name string, value interface{}) { r.attributes[name] = value } // Attribute returns the value associated to the given name. Returns nil if absent. func (r Request) Attribute(name string) interface{} { return r.attributes[name] } // SelectedRoutePath root path + route path that matched the request, e.g. /meetings/{id}/attendees // If no route was matched then return an empty string. func (r Request) SelectedRoutePath() string { if r.selectedRoute == nil { return "" } // skip creating an accessor return r.selectedRoute.Path } // SelectedRoute returns a reader to access the selected Route by the container // Returns nil if no route was matched. func (r Request) SelectedRoute() RouteReader { if r.selectedRoute == nil { return nil } return routeAccessor{route: r.selectedRoute} } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/response.go ================================================ package restful // Copyright 2013 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. import ( "bufio" "errors" "net" "net/http" ) // DefaultResponseMimeType is DEPRECATED, use DefaultResponseContentType(mime) var DefaultResponseMimeType string //PrettyPrintResponses controls the indentation feature of XML and JSON serialization var PrettyPrintResponses = true // Response is a wrapper on the actual http ResponseWriter // It provides several convenience methods to prepare and write response content. type Response struct { http.ResponseWriter requestAccept string // mime-type what the Http Request says it wants to receive routeProduces []string // mime-types what the Route says it can produce statusCode int // HTTP status code that has been written explicitly (if zero then net/http has written 200) contentLength int // number of bytes written for the response body prettyPrint bool // controls the indentation feature of XML and JSON serialization. It is initialized using var PrettyPrintResponses. err error // err property is kept when WriteError is called hijacker http.Hijacker // if underlying ResponseWriter supports it } // NewResponse creates a new response based on a http ResponseWriter. func NewResponse(httpWriter http.ResponseWriter) *Response { hijacker, _ := httpWriter.(http.Hijacker) return &Response{ResponseWriter: httpWriter, routeProduces: []string{}, statusCode: http.StatusOK, prettyPrint: PrettyPrintResponses, hijacker: hijacker} } // DefaultResponseContentType set a default. // If Accept header matching fails, fall back to this type. // Valid values are restful.MIME_JSON and restful.MIME_XML // Example: // restful.DefaultResponseContentType(restful.MIME_JSON) func DefaultResponseContentType(mime string) { DefaultResponseMimeType = mime } // InternalServerError writes the StatusInternalServerError header. // DEPRECATED, use WriteErrorString(http.StatusInternalServerError,reason) func (r Response) InternalServerError() Response { r.WriteHeader(http.StatusInternalServerError) return r } // Hijack implements the http.Hijacker interface. This expands // the Response to fulfill http.Hijacker if the underlying // http.ResponseWriter supports it. func (r *Response) Hijack() (net.Conn, *bufio.ReadWriter, error) { if r.hijacker == nil { return nil, nil, errors.New("http.Hijacker not implemented by underlying http.ResponseWriter") } return r.hijacker.Hijack() } // PrettyPrint changes whether this response must produce pretty (line-by-line, indented) JSON or XML output. func (r *Response) PrettyPrint(bePretty bool) { r.prettyPrint = bePretty } // AddHeader is a shortcut for .Header().Add(header,value) func (r Response) AddHeader(header string, value string) Response { r.Header().Add(header, value) return r } // SetRequestAccepts tells the response what Mime-type(s) the HTTP request said it wants to accept. Exposed for testing. func (r *Response) SetRequestAccepts(mime string) { r.requestAccept = mime } // EntityWriter returns the registered EntityWriter that the entity (requested resource) // can write according to what the request wants (Accept) and what the Route can produce or what the restful defaults say. // If called before WriteEntity and WriteHeader then a false return value can be used to write a 406: Not Acceptable. func (r *Response) EntityWriter() (EntityReaderWriter, bool) { sorted := sortedMimes(r.requestAccept) for _, eachAccept := range sorted { for _, eachProduce := range r.routeProduces { if eachProduce == eachAccept.media { if w, ok := entityAccessRegistry.accessorAt(eachAccept.media); ok { return w, true } } } if eachAccept.media == "*/*" { for _, each := range r.routeProduces { if w, ok := entityAccessRegistry.accessorAt(each); ok { return w, true } } } } // if requestAccept is empty writer, ok := entityAccessRegistry.accessorAt(r.requestAccept) if !ok { // if not registered then fallback to the defaults (if set) if DefaultResponseMimeType == MIME_JSON { return entityAccessRegistry.accessorAt(MIME_JSON) } if DefaultResponseMimeType == MIME_XML { return entityAccessRegistry.accessorAt(MIME_XML) } if DefaultResponseMimeType == MIME_ZIP { return entityAccessRegistry.accessorAt(MIME_ZIP) } // Fallback to whatever the route says it can produce. // https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for _, each := range r.routeProduces { if w, ok := entityAccessRegistry.accessorAt(each); ok { return w, true } } if trace { traceLogger.Printf("no registered EntityReaderWriter found for %s", r.requestAccept) } } return writer, ok } // WriteEntity calls WriteHeaderAndEntity with Http Status OK (200) func (r *Response) WriteEntity(value interface{}) error { return r.WriteHeaderAndEntity(http.StatusOK, value) } // WriteHeaderAndEntity marshals the value using the representation denoted by the Accept Header and the registered EntityWriters. // If no Accept header is specified (or */*) then respond with the Content-Type as specified by the first in the Route.Produces. // If an Accept header is specified then respond with the Content-Type as specified by the first in the Route.Produces that is matched with the Accept header. // If the value is nil then no response is send except for the Http status. You may want to call WriteHeader(http.StatusNotFound) instead. // If there is no writer available that can represent the value in the requested MIME type then Http Status NotAcceptable is written. // Current implementation ignores any q-parameters in the Accept Header. // Returns an error if the value could not be written on the response. func (r *Response) WriteHeaderAndEntity(status int, value interface{}) error { writer, ok := r.EntityWriter() if !ok { r.WriteHeader(http.StatusNotAcceptable) return nil } return writer.Write(r, status, value) } // WriteAsXml is a convenience method for writing a value in xml (requires Xml tags on the value) // It uses the standard encoding/xml package for marshalling the value ; not using a registered EntityReaderWriter. func (r *Response) WriteAsXml(value interface{}) error { return writeXML(r, http.StatusOK, MIME_XML, value) } // WriteHeaderAndXml is a convenience method for writing a status and value in xml (requires Xml tags on the value) // It uses the standard encoding/xml package for marshalling the value ; not using a registered EntityReaderWriter. func (r *Response) WriteHeaderAndXml(status int, value interface{}) error { return writeXML(r, status, MIME_XML, value) } // WriteAsJson is a convenience method for writing a value in json. // It uses the standard encoding/json package for marshalling the value ; not using a registered EntityReaderWriter. func (r *Response) WriteAsJson(value interface{}) error { return writeJSON(r, http.StatusOK, MIME_JSON, value) } // WriteJson is a convenience method for writing a value in Json with a given Content-Type. // It uses the standard encoding/json package for marshalling the value ; not using a registered EntityReaderWriter. func (r *Response) WriteJson(value interface{}, contentType string) error { return writeJSON(r, http.StatusOK, contentType, value) } // WriteHeaderAndJson is a convenience method for writing the status and a value in Json with a given Content-Type. // It uses the standard encoding/json package for marshalling the value ; not using a registered EntityReaderWriter. func (r *Response) WriteHeaderAndJson(status int, value interface{}, contentType string) error { return writeJSON(r, status, contentType, value) } // WriteError writes the http status and the error string on the response. err can be nil. // Return an error if writing was not successful. func (r *Response) WriteError(httpStatus int, err error) (writeErr error) { r.err = err if err == nil { writeErr = r.WriteErrorString(httpStatus, "") } else { writeErr = r.WriteErrorString(httpStatus, err.Error()) } return writeErr } // WriteServiceError is a convenience method for a responding with a status and a ServiceError func (r *Response) WriteServiceError(httpStatus int, err ServiceError) error { r.err = err return r.WriteHeaderAndEntity(httpStatus, err) } // WriteErrorString is a convenience method for an error status with the actual error func (r *Response) WriteErrorString(httpStatus int, errorReason string) error { if r.err == nil { // if not called from WriteError r.err = errors.New(errorReason) } r.WriteHeader(httpStatus) if _, err := r.Write([]byte(errorReason)); err != nil { return err } return nil } // Flush implements http.Flusher interface, which sends any buffered data to the client. func (r *Response) Flush() { if f, ok := r.ResponseWriter.(http.Flusher); ok { f.Flush() } else if trace { traceLogger.Printf("ResponseWriter %v doesn't support Flush", r) } } // WriteHeader is overridden to remember the Status Code that has been written. // Changes to the Header of the response have no effect after this. func (r *Response) WriteHeader(httpStatus int) { r.statusCode = httpStatus r.ResponseWriter.WriteHeader(httpStatus) } // StatusCode returns the code that has been written using WriteHeader. func (r Response) StatusCode() int { if 0 == r.statusCode { // no status code has been written yet; assume OK return http.StatusOK } return r.statusCode } // Write writes the data to the connection as part of an HTTP reply. // Write is part of http.ResponseWriter interface. func (r *Response) Write(bytes []byte) (int, error) { written, err := r.ResponseWriter.Write(bytes) r.contentLength += written return written, err } // ContentLength returns the number of bytes written for the response content. // Note that this value is only correct if all data is written through the Response using its Write* methods. // Data written directly using the underlying http.ResponseWriter is not accounted for. func (r Response) ContentLength() int { return r.contentLength } // CloseNotify is part of http.CloseNotifier interface func (r Response) CloseNotify() <-chan bool { return r.ResponseWriter.(http.CloseNotifier).CloseNotify() } // Error returns the err created by WriteError func (r Response) Error() error { return r.err } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/route.go ================================================ package restful // Copyright 2013 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. import ( "net/http" "strings" ) // RouteFunction declares the signature of a function that can be bound to a Route. type RouteFunction func(*Request, *Response) // RouteSelectionConditionFunction declares the signature of a function that // can be used to add extra conditional logic when selecting whether the route // matches the HTTP request. type RouteSelectionConditionFunction func(httpRequest *http.Request) bool // Route binds a HTTP Method,Path,Consumes combination to a RouteFunction. type Route struct { ExtensionProperties Method string Produces []string Consumes []string Path string // webservice root path + described path Function RouteFunction Filters []FilterFunction If []RouteSelectionConditionFunction // cached values for dispatching relativePath string pathParts []string pathExpr *pathExpression // cached compilation of relativePath as RegExp // documentation Doc string Notes string Operation string ParameterDocs []*Parameter ResponseErrors map[int]ResponseError DefaultResponse *ResponseError ReadSample, WriteSample interface{} // structs that model an example request or response payload // Extra information used to store custom information about the route. Metadata map[string]interface{} // marks a route as deprecated Deprecated bool //Overrides the container.contentEncodingEnabled contentEncodingEnabled *bool // indicate route path has custom verb hasCustomVerb bool // if a request does not include a content-type header then // depending on the method, it may return a 415 Unsupported Media // Must have uppercase HTTP Method names such as GET,HEAD,OPTIONS,... allowedMethodsWithoutContentType []string } // Initialize for Route func (r *Route) postBuild() { r.pathParts = tokenizePath(r.Path) r.hasCustomVerb = hasCustomVerb(r.Path) } // Create Request and Response from their http versions func (r *Route) wrapRequestResponse(httpWriter http.ResponseWriter, httpRequest *http.Request, pathParams map[string]string) (*Request, *Response) { wrappedRequest := NewRequest(httpRequest) wrappedRequest.pathParameters = pathParams wrappedRequest.selectedRoute = r wrappedResponse := NewResponse(httpWriter) wrappedResponse.requestAccept = httpRequest.Header.Get(HEADER_Accept) wrappedResponse.routeProduces = r.Produces return wrappedRequest, wrappedResponse } func stringTrimSpaceCutset(r rune) bool { return r == ' ' } // Return whether the mimeType matches to what this Route can produce. func (r Route) matchesAccept(mimeTypesWithQuality string) bool { remaining := mimeTypesWithQuality for { var mimeType string if end := strings.Index(remaining, ","); end == -1 { mimeType, remaining = remaining, "" } else { mimeType, remaining = remaining[:end], remaining[end+1:] } if quality := strings.Index(mimeType, ";"); quality != -1 { mimeType = mimeType[:quality] } mimeType = strings.TrimFunc(mimeType, stringTrimSpaceCutset) if mimeType == "*/*" { return true } for _, producibleType := range r.Produces { if producibleType == "*/*" || producibleType == mimeType { return true } } if len(remaining) == 0 { return false } } } // Return whether this Route can consume content with a type specified by mimeTypes (can be empty). func (r Route) matchesContentType(mimeTypes string) bool { if len(r.Consumes) == 0 { // did not specify what it can consume ; any media type (“*/*”) is assumed return true } if len(mimeTypes) == 0 { // idempotent methods with (most-likely or guaranteed) empty content match missing Content-Type m := r.Method // if route specifies less or non-idempotent methods then use that if len(r.allowedMethodsWithoutContentType) > 0 { for _, each := range r.allowedMethodsWithoutContentType { if m == each { return true } } } else { if m == "GET" || m == "HEAD" || m == "OPTIONS" || m == "DELETE" || m == "TRACE" { return true } } // proceed with default mimeTypes = MIME_OCTET } remaining := mimeTypes for { var mimeType string if end := strings.Index(remaining, ","); end == -1 { mimeType, remaining = remaining, "" } else { mimeType, remaining = remaining[:end], remaining[end+1:] } if quality := strings.Index(mimeType, ";"); quality != -1 { mimeType = mimeType[:quality] } mimeType = strings.TrimFunc(mimeType, stringTrimSpaceCutset) for _, consumeableType := range r.Consumes { if consumeableType == "*/*" || consumeableType == mimeType { return true } } if len(remaining) == 0 { return false } } } // Tokenize an URL path using the slash separator ; the result does not have empty tokens func tokenizePath(path string) []string { if "/" == path { return nil } return strings.Split(strings.TrimLeft(path, "/"), "/") } // for debugging func (r *Route) String() string { return r.Method + " " + r.Path } // EnableContentEncoding (default=false) allows for GZIP or DEFLATE encoding of responses. Overrides the container.contentEncodingEnabled value. func (r *Route) EnableContentEncoding(enabled bool) { r.contentEncodingEnabled = &enabled } var TrimRightSlashEnabled = false ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/route_builder.go ================================================ package restful // Copyright 2013 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. import ( "fmt" "os" "path" "reflect" "runtime" "strings" "sync/atomic" "github.com/emicklei/go-restful/v3/log" ) // RouteBuilder is a helper to construct Routes. type RouteBuilder struct { rootPath string currentPath string produces []string consumes []string httpMethod string // required function RouteFunction // required filters []FilterFunction conditions []RouteSelectionConditionFunction allowedMethodsWithoutContentType []string // see Route typeNameHandleFunc TypeNameHandleFunction // required // documentation doc string notes string operation string readSample, writeSample interface{} parameters []*Parameter errorMap map[int]ResponseError defaultResponse *ResponseError metadata map[string]interface{} extensions map[string]interface{} deprecated bool contentEncodingEnabled *bool } // Do evaluates each argument with the RouteBuilder itself. // This allows you to follow DRY principles without breaking the fluent programming style. // Example: // // ws.Route(ws.DELETE("/{name}").To(t.deletePerson).Do(Returns200, Returns500)) // // func Returns500(b *RouteBuilder) { // b.Returns(500, "Internal Server Error", restful.ServiceError{}) // } func (b *RouteBuilder) Do(oneArgBlocks ...func(*RouteBuilder)) *RouteBuilder { for _, each := range oneArgBlocks { each(b) } return b } // To bind the route to a function. // If this route is matched with the incoming Http Request then call this function with the *Request,*Response pair. Required. func (b *RouteBuilder) To(function RouteFunction) *RouteBuilder { b.function = function return b } // Method specifies what HTTP method to match. Required. func (b *RouteBuilder) Method(method string) *RouteBuilder { b.httpMethod = method return b } // Produces specifies what MIME types can be produced ; the matched one will appear in the Content-Type Http header. func (b *RouteBuilder) Produces(mimeTypes ...string) *RouteBuilder { b.produces = mimeTypes return b } // Consumes specifies what MIME types can be consumes ; the Accept Http header must matched any of these func (b *RouteBuilder) Consumes(mimeTypes ...string) *RouteBuilder { b.consumes = mimeTypes return b } // Path specifies the relative (w.r.t WebService root path) URL path to match. Default is "/". func (b *RouteBuilder) Path(subPath string) *RouteBuilder { b.currentPath = subPath return b } // Doc tells what this route is all about. Optional. func (b *RouteBuilder) Doc(documentation string) *RouteBuilder { b.doc = documentation return b } // Notes is a verbose explanation of the operation behavior. Optional. func (b *RouteBuilder) Notes(notes string) *RouteBuilder { b.notes = notes return b } // Reads tells what resource type will be read from the request payload. Optional. // A parameter of type "body" is added ,required is set to true and the dataType is set to the qualified name of the sample's type. func (b *RouteBuilder) Reads(sample interface{}, optionalDescription ...string) *RouteBuilder { fn := b.typeNameHandleFunc if fn == nil { fn = reflectTypeName } typeAsName := fn(sample) description := "" if len(optionalDescription) > 0 { description = optionalDescription[0] } b.readSample = sample bodyParameter := &Parameter{&ParameterData{Name: "body", Description: description}} bodyParameter.beBody() bodyParameter.Required(true) bodyParameter.DataType(typeAsName) b.Param(bodyParameter) return b } // ParameterNamed returns a Parameter already known to the RouteBuilder. Returns nil if not. // Use this to modify or extend information for the Parameter (through its Data()). func (b RouteBuilder) ParameterNamed(name string) (p *Parameter) { for _, each := range b.parameters { if each.Data().Name == name { return each } } return p } // Writes tells what resource type will be written as the response payload. Optional. func (b *RouteBuilder) Writes(sample interface{}) *RouteBuilder { b.writeSample = sample return b } // Param allows you to document the parameters of the Route. It adds a new Parameter (does not check for duplicates). func (b *RouteBuilder) Param(parameter *Parameter) *RouteBuilder { if b.parameters == nil { b.parameters = []*Parameter{} } b.parameters = append(b.parameters, parameter) return b } // Operation allows you to document what the actual method/function call is of the Route. // Unless called, the operation name is derived from the RouteFunction set using To(..). func (b *RouteBuilder) Operation(name string) *RouteBuilder { b.operation = name return b } // ReturnsError is deprecated, use Returns instead. func (b *RouteBuilder) ReturnsError(code int, message string, model interface{}) *RouteBuilder { log.Print("ReturnsError is deprecated, use Returns instead.") return b.Returns(code, message, model) } // Returns allows you to document what responses (errors or regular) can be expected. // The model parameter is optional ; either pass a struct instance or use nil if not applicable. func (b *RouteBuilder) Returns(code int, message string, model interface{}) *RouteBuilder { err := ResponseError{ Code: code, Message: message, Model: model, IsDefault: false, // this field is deprecated, use default response instead. } // lazy init because there is no NewRouteBuilder (yet) if b.errorMap == nil { b.errorMap = map[int]ResponseError{} } b.errorMap[code] = err return b } // ReturnsWithHeaders is similar to Returns, but can specify response headers func (b *RouteBuilder) ReturnsWithHeaders(code int, message string, model interface{}, headers map[string]Header) *RouteBuilder { b.Returns(code, message, model) err := b.errorMap[code] err.Headers = headers b.errorMap[code] = err return b } // DefaultReturns is a special Returns call that sets the default of the response. func (b *RouteBuilder) DefaultReturns(message string, model interface{}) *RouteBuilder { b.defaultResponse = &ResponseError{ Message: message, Model: model, } return b } // Metadata adds or updates a key=value pair to the metadata map. func (b *RouteBuilder) Metadata(key string, value interface{}) *RouteBuilder { if b.metadata == nil { b.metadata = map[string]interface{}{} } b.metadata[key] = value return b } // AddExtension adds or updates a key=value pair to the extensions map. func (b *RouteBuilder) AddExtension(key string, value interface{}) *RouteBuilder { if b.extensions == nil { b.extensions = map[string]interface{}{} } b.extensions[key] = value return b } // Deprecate sets the value of deprecated to true. Deprecated routes have a special UI treatment to warn against use func (b *RouteBuilder) Deprecate() *RouteBuilder { b.deprecated = true return b } // AllowedMethodsWithoutContentType overrides the default list GET,HEAD,OPTIONS,DELETE,TRACE // If a request does not include a content-type header then // depending on the method, it may return a 415 Unsupported Media. // Must have uppercase HTTP Method names such as GET,HEAD,OPTIONS,... func (b *RouteBuilder) AllowedMethodsWithoutContentType(methods []string) *RouteBuilder { b.allowedMethodsWithoutContentType = methods return b } // ResponseError represents a response; not necessarily an error. type ResponseError struct { ExtensionProperties Code int Message string Model interface{} Headers map[string]Header IsDefault bool } // Header describes a header for a response of the API // // For more information: http://goo.gl/8us55a#headerObject type Header struct { *Items Description string } // Items describe swagger simple schemas for headers type Items struct { Type string Format string Items *Items CollectionFormat string Default interface{} } func (b *RouteBuilder) servicePath(path string) *RouteBuilder { b.rootPath = path return b } // Filter appends a FilterFunction to the end of filters for this Route to build. func (b *RouteBuilder) Filter(filter FilterFunction) *RouteBuilder { b.filters = append(b.filters, filter) return b } // If sets a condition function that controls matching the Route based on custom logic. // The condition function is provided the HTTP request and should return true if the route // should be considered. // // Efficiency note: the condition function is called before checking the method, produces, and // consumes criteria, so that the correct HTTP status code can be returned. // // Lifecycle note: no filter functions have been called prior to calling the condition function, // so the condition function should not depend on any context that might be set up by container // or route filters. func (b *RouteBuilder) If(condition RouteSelectionConditionFunction) *RouteBuilder { b.conditions = append(b.conditions, condition) return b } // ContentEncodingEnabled allows you to override the Containers value for auto-compressing this route response. func (b *RouteBuilder) ContentEncodingEnabled(enabled bool) *RouteBuilder { b.contentEncodingEnabled = &enabled return b } // If no specific Route path then set to rootPath // If no specific Produces then set to rootProduces // If no specific Consumes then set to rootConsumes func (b *RouteBuilder) copyDefaults(rootProduces, rootConsumes []string) { if len(b.produces) == 0 { b.produces = rootProduces } if len(b.consumes) == 0 { b.consumes = rootConsumes } } // typeNameHandler sets the function that will convert types to strings in the parameter // and model definitions. func (b *RouteBuilder) typeNameHandler(handler TypeNameHandleFunction) *RouteBuilder { b.typeNameHandleFunc = handler return b } // Build creates a new Route using the specification details collected by the RouteBuilder func (b *RouteBuilder) Build() Route { pathExpr, err := newPathExpression(b.currentPath) if err != nil { log.Printf("Invalid path:%s because:%v", b.currentPath, err) os.Exit(1) } if b.function == nil { log.Printf("No function specified for route:" + b.currentPath) os.Exit(1) } operationName := b.operation if len(operationName) == 0 && b.function != nil { // extract from definition operationName = nameOfFunction(b.function) } route := Route{ Method: b.httpMethod, Path: concatPath(b.rootPath, b.currentPath), Produces: b.produces, Consumes: b.consumes, Function: b.function, Filters: b.filters, If: b.conditions, relativePath: b.currentPath, pathExpr: pathExpr, Doc: b.doc, Notes: b.notes, Operation: operationName, ParameterDocs: b.parameters, ResponseErrors: b.errorMap, DefaultResponse: b.defaultResponse, ReadSample: b.readSample, WriteSample: b.writeSample, Metadata: b.metadata, Deprecated: b.deprecated, contentEncodingEnabled: b.contentEncodingEnabled, allowedMethodsWithoutContentType: b.allowedMethodsWithoutContentType, } route.Extensions = b.extensions route.postBuild() return route } func concatPath(path1, path2 string) string { return path.Join(path1, path2) } var anonymousFuncCount int32 // nameOfFunction returns the short name of the function f for documentation. // It uses a runtime feature for debugging ; its value may change for later Go versions. func nameOfFunction(f interface{}) string { fun := runtime.FuncForPC(reflect.ValueOf(f).Pointer()) tokenized := strings.Split(fun.Name(), ".") last := tokenized[len(tokenized)-1] last = strings.TrimSuffix(last, ")·fm") // < Go 1.5 last = strings.TrimSuffix(last, ")-fm") // Go 1.5 last = strings.TrimSuffix(last, "·fm") // < Go 1.5 last = strings.TrimSuffix(last, "-fm") // Go 1.5 if last == "func1" { // this could mean conflicts in API docs val := atomic.AddInt32(&anonymousFuncCount, 1) last = "func" + fmt.Sprintf("%d", val) atomic.StoreInt32(&anonymousFuncCount, val) } return last } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/route_reader.go ================================================ package restful // Copyright 2021 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. type RouteReader interface { Method() string Consumes() []string Path() string Doc() string Notes() string Operation() string ParameterDocs() []*Parameter // Returns a copy Metadata() map[string]interface{} Deprecated() bool } type routeAccessor struct { route *Route } func (r routeAccessor) Method() string { return r.route.Method } func (r routeAccessor) Consumes() []string { return r.route.Consumes[:] } func (r routeAccessor) Path() string { return r.route.Path } func (r routeAccessor) Doc() string { return r.route.Doc } func (r routeAccessor) Notes() string { return r.route.Notes } func (r routeAccessor) Operation() string { return r.route.Operation } func (r routeAccessor) ParameterDocs() []*Parameter { return r.route.ParameterDocs[:] } // Returns a copy func (r routeAccessor) Metadata() map[string]interface{} { return copyMap(r.route.Metadata) } func (r routeAccessor) Deprecated() bool { return r.route.Deprecated } // https://stackoverflow.com/questions/23057785/how-to-copy-a-map func copyMap(m map[string]interface{}) map[string]interface{} { cp := make(map[string]interface{}) for k, v := range m { vm, ok := v.(map[string]interface{}) if ok { cp[k] = copyMap(vm) } else { cp[k] = v } } return cp } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/router.go ================================================ package restful // Copyright 2013 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. import "net/http" // A RouteSelector finds the best matching Route given the input HTTP Request // RouteSelectors can optionally also implement the PathProcessor interface to also calculate the // path parameters after the route has been selected. type RouteSelector interface { // SelectRoute finds a Route given the input HTTP Request and a list of WebServices. // It returns a selected Route and its containing WebService or an error indicating // a problem. SelectRoute( webServices []*WebService, httpRequest *http.Request) (selectedService *WebService, selected *Route, err error) } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/service_error.go ================================================ package restful // Copyright 2013 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. import ( "fmt" "net/http" ) // ServiceError is a transport object to pass information about a non-Http error occurred in a WebService while processing a request. type ServiceError struct { Code int Message string Header http.Header } // NewError returns a ServiceError using the code and reason func NewError(code int, message string) ServiceError { return ServiceError{Code: code, Message: message} } // NewErrorWithHeader returns a ServiceError using the code, reason and header func NewErrorWithHeader(code int, message string, header http.Header) ServiceError { return ServiceError{Code: code, Message: message, Header: header} } // Error returns a text representation of the service error func (s ServiceError) Error() string { return fmt.Sprintf("[ServiceError:%v] %v", s.Code, s.Message) } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/web_service.go ================================================ package restful import ( "errors" "os" "reflect" "sync" "github.com/emicklei/go-restful/v3/log" ) // Copyright 2013 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. // WebService holds a collection of Route values that bind a Http Method + URL Path to a function. type WebService struct { rootPath string pathExpr *pathExpression // cached compilation of rootPath as RegExp routes []Route produces []string consumes []string pathParameters []*Parameter filters []FilterFunction documentation string apiVersion string typeNameHandleFunc TypeNameHandleFunction dynamicRoutes bool // protects 'routes' if dynamic routes are enabled routesLock sync.RWMutex } func (w *WebService) SetDynamicRoutes(enable bool) { w.dynamicRoutes = enable } // TypeNameHandleFunction declares functions that can handle translating the name of a sample object // into the restful documentation for the service. type TypeNameHandleFunction func(sample interface{}) string // TypeNameHandler sets the function that will convert types to strings in the parameter // and model definitions. If not set, the web service will invoke // reflect.TypeOf(object).String(). func (w *WebService) TypeNameHandler(handler TypeNameHandleFunction) *WebService { w.typeNameHandleFunc = handler return w } // reflectTypeName is the default TypeNameHandleFunction and for a given object // returns the name that Go identifies it with (e.g. "string" or "v1.Object") via // the reflection API. func reflectTypeName(sample interface{}) string { return reflect.TypeOf(sample).String() } // compilePathExpression ensures that the path is compiled into a RegEx for those routers that need it. func (w *WebService) compilePathExpression() { compiled, err := newPathExpression(w.rootPath) if err != nil { log.Printf("invalid path:%s because:%v", w.rootPath, err) os.Exit(1) } w.pathExpr = compiled } // ApiVersion sets the API version for documentation purposes. func (w *WebService) ApiVersion(apiVersion string) *WebService { w.apiVersion = apiVersion return w } // Version returns the API version for documentation purposes. func (w *WebService) Version() string { return w.apiVersion } // Path specifies the root URL template path of the WebService. // All Routes will be relative to this path. func (w *WebService) Path(root string) *WebService { w.rootPath = root if len(w.rootPath) == 0 { w.rootPath = "/" } w.compilePathExpression() return w } // Param adds a PathParameter to document parameters used in the root path. func (w *WebService) Param(parameter *Parameter) *WebService { if w.pathParameters == nil { w.pathParameters = []*Parameter{} } w.pathParameters = append(w.pathParameters, parameter) return w } // PathParameter creates a new Parameter of kind Path for documentation purposes. // It is initialized as required with string as its DataType. func (w *WebService) PathParameter(name, description string) *Parameter { return PathParameter(name, description) } // PathParameter creates a new Parameter of kind Path for documentation purposes. // It is initialized as required with string as its DataType. func PathParameter(name, description string) *Parameter { p := &Parameter{&ParameterData{Name: name, Description: description, Required: true, DataType: "string"}} p.bePath() return p } // QueryParameter creates a new Parameter of kind Query for documentation purposes. // It is initialized as not required with string as its DataType. func (w *WebService) QueryParameter(name, description string) *Parameter { return QueryParameter(name, description) } // QueryParameter creates a new Parameter of kind Query for documentation purposes. // It is initialized as not required with string as its DataType. func QueryParameter(name, description string) *Parameter { p := &Parameter{&ParameterData{Name: name, Description: description, Required: false, DataType: "string", CollectionFormat: CollectionFormatCSV.String()}} p.beQuery() return p } // BodyParameter creates a new Parameter of kind Body for documentation purposes. // It is initialized as required without a DataType. func (w *WebService) BodyParameter(name, description string) *Parameter { return BodyParameter(name, description) } // BodyParameter creates a new Parameter of kind Body for documentation purposes. // It is initialized as required without a DataType. func BodyParameter(name, description string) *Parameter { p := &Parameter{&ParameterData{Name: name, Description: description, Required: true}} p.beBody() return p } // HeaderParameter creates a new Parameter of kind (Http) Header for documentation purposes. // It is initialized as not required with string as its DataType. func (w *WebService) HeaderParameter(name, description string) *Parameter { return HeaderParameter(name, description) } // HeaderParameter creates a new Parameter of kind (Http) Header for documentation purposes. // It is initialized as not required with string as its DataType. func HeaderParameter(name, description string) *Parameter { p := &Parameter{&ParameterData{Name: name, Description: description, Required: false, DataType: "string"}} p.beHeader() return p } // FormParameter creates a new Parameter of kind Form (using application/x-www-form-urlencoded) for documentation purposes. // It is initialized as required with string as its DataType. func (w *WebService) FormParameter(name, description string) *Parameter { return FormParameter(name, description) } // FormParameter creates a new Parameter of kind Form (using application/x-www-form-urlencoded) for documentation purposes. // It is initialized as required with string as its DataType. func FormParameter(name, description string) *Parameter { p := &Parameter{&ParameterData{Name: name, Description: description, Required: false, DataType: "string"}} p.beForm() return p } // MultiPartFormParameter creates a new Parameter of kind Form (using multipart/form-data) for documentation purposes. // It is initialized as required with string as its DataType. func (w *WebService) MultiPartFormParameter(name, description string) *Parameter { return MultiPartFormParameter(name, description) } func MultiPartFormParameter(name, description string) *Parameter { p := &Parameter{&ParameterData{Name: name, Description: description, Required: false, DataType: "string"}} p.beMultiPartForm() return p } // Route creates a new Route using the RouteBuilder and add to the ordered list of Routes. func (w *WebService) Route(builder *RouteBuilder) *WebService { w.routesLock.Lock() defer w.routesLock.Unlock() builder.copyDefaults(w.produces, w.consumes) w.routes = append(w.routes, builder.Build()) return w } // RemoveRoute removes the specified route, looks for something that matches 'path' and 'method' func (w *WebService) RemoveRoute(path, method string) error { if !w.dynamicRoutes { return errors.New("dynamic routes are not enabled.") } w.routesLock.Lock() defer w.routesLock.Unlock() newRoutes := []Route{} for _, route := range w.routes { if route.Method == method && route.Path == path { continue } newRoutes = append(newRoutes, route) } w.routes = newRoutes return nil } // Method creates a new RouteBuilder and initialize its http method func (w *WebService) Method(httpMethod string) *RouteBuilder { return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method(httpMethod) } // Produces specifies that this WebService can produce one or more MIME types. // Http requests must have one of these values set for the Accept header. func (w *WebService) Produces(contentTypes ...string) *WebService { w.produces = contentTypes return w } // Consumes specifies that this WebService can consume one or more MIME types. // Http requests must have one of these values set for the Content-Type header. func (w *WebService) Consumes(accepts ...string) *WebService { w.consumes = accepts return w } // Routes returns the Routes associated with this WebService func (w *WebService) Routes() []Route { if !w.dynamicRoutes { return w.routes } // Make a copy of the array to prevent concurrency problems w.routesLock.RLock() defer w.routesLock.RUnlock() result := make([]Route, len(w.routes)) for ix := range w.routes { result[ix] = w.routes[ix] } return result } // RootPath returns the RootPath associated with this WebService. Default "/" func (w *WebService) RootPath() string { return w.rootPath } // PathParameters return the path parameter names for (shared among its Routes) func (w *WebService) PathParameters() []*Parameter { return w.pathParameters } // Filter adds a filter function to the chain of filters applicable to all its Routes func (w *WebService) Filter(filter FilterFunction) *WebService { w.filters = append(w.filters, filter) return w } // Doc is used to set the documentation of this service. func (w *WebService) Doc(plainText string) *WebService { w.documentation = plainText return w } // Documentation returns it. func (w *WebService) Documentation() string { return w.documentation } /* Convenience methods */ // HEAD is a shortcut for .Method("HEAD").Path(subPath) func (w *WebService) HEAD(subPath string) *RouteBuilder { return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method("HEAD").Path(subPath) } // GET is a shortcut for .Method("GET").Path(subPath) func (w *WebService) GET(subPath string) *RouteBuilder { return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method("GET").Path(subPath) } // POST is a shortcut for .Method("POST").Path(subPath) func (w *WebService) POST(subPath string) *RouteBuilder { return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method("POST").Path(subPath) } // PUT is a shortcut for .Method("PUT").Path(subPath) func (w *WebService) PUT(subPath string) *RouteBuilder { return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method("PUT").Path(subPath) } // PATCH is a shortcut for .Method("PATCH").Path(subPath) func (w *WebService) PATCH(subPath string) *RouteBuilder { return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method("PATCH").Path(subPath) } // DELETE is a shortcut for .Method("DELETE").Path(subPath) func (w *WebService) DELETE(subPath string) *RouteBuilder { return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method("DELETE").Path(subPath) } // OPTIONS is a shortcut for .Method("OPTIONS").Path(subPath) func (w *WebService) OPTIONS(subPath string) *RouteBuilder { return new(RouteBuilder).typeNameHandler(w.typeNameHandleFunc).servicePath(w.rootPath).Method("OPTIONS").Path(subPath) } ================================================ FILE: vendor/github.com/emicklei/go-restful/v3/web_service_container.go ================================================ package restful // Copyright 2013 Ernest Micklei. All rights reserved. // Use of this source code is governed by a license // that can be found in the LICENSE file. import ( "net/http" ) // DefaultContainer is a restful.Container that uses http.DefaultServeMux var DefaultContainer *Container func init() { DefaultContainer = NewContainer() DefaultContainer.ServeMux = http.DefaultServeMux } // If set the true then panics will not be caught to return HTTP 500. // In that case, Route functions are responsible for handling any error situation. // Default value is false = recover from panics. This has performance implications. // OBSOLETE ; use restful.DefaultContainer.DoNotRecover(true) var DoNotRecover = false // Add registers a new WebService add it to the DefaultContainer. func Add(service *WebService) { DefaultContainer.Add(service) } // Filter appends a container FilterFunction from the DefaultContainer. // These are called before dispatching a http.Request to a WebService. func Filter(filter FilterFunction) { DefaultContainer.Filter(filter) } // RegisteredWebServices returns the collections of WebServices from the DefaultContainer func RegisteredWebServices() []*WebService { return DefaultContainer.RegisteredWebServices() } ================================================ FILE: vendor/github.com/evanphx/json-patch/.gitignore ================================================ # editor and IDE paraphernalia .idea .vscode # macOS paraphernalia .DS_Store ================================================ FILE: vendor/github.com/evanphx/json-patch/LICENSE ================================================ Copyright (c) 2014, Evan Phoenix All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Evan Phoenix nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/evanphx/json-patch/README.md ================================================ # JSON-Patch `jsonpatch` is a library which provides functionality for both applying [RFC6902 JSON patches](http://tools.ietf.org/html/rfc6902) against documents, as well as for calculating & applying [RFC7396 JSON merge patches](https://tools.ietf.org/html/rfc7396). [![GoDoc](https://godoc.org/github.com/evanphx/json-patch?status.svg)](http://godoc.org/github.com/evanphx/json-patch) [![Build Status](https://travis-ci.org/evanphx/json-patch.svg?branch=master)](https://travis-ci.org/evanphx/json-patch) [![Report Card](https://goreportcard.com/badge/github.com/evanphx/json-patch)](https://goreportcard.com/report/github.com/evanphx/json-patch) # Get It! **Latest and greatest**: ```bash go get -u github.com/evanphx/json-patch/v5 ``` **Stable Versions**: * Version 5: `go get -u gopkg.in/evanphx/json-patch.v5` * Version 4: `go get -u gopkg.in/evanphx/json-patch.v4` (previous versions below `v3` are unavailable) # Use It! * [Create and apply a merge patch](#create-and-apply-a-merge-patch) * [Create and apply a JSON Patch](#create-and-apply-a-json-patch) * [Comparing JSON documents](#comparing-json-documents) * [Combine merge patches](#combine-merge-patches) # Configuration * There is a global configuration variable `jsonpatch.SupportNegativeIndices`. This defaults to `true` and enables the non-standard practice of allowing negative indices to mean indices starting at the end of an array. This functionality can be disabled by setting `jsonpatch.SupportNegativeIndices = false`. * There is a global configuration variable `jsonpatch.AccumulatedCopySizeLimit`, which limits the total size increase in bytes caused by "copy" operations in a patch. It defaults to 0, which means there is no limit. These global variables control the behavior of `jsonpatch.Apply`. An alternative to `jsonpatch.Apply` is `jsonpatch.ApplyWithOptions` whose behavior is controlled by an `options` parameter of type `*jsonpatch.ApplyOptions`. Structure `jsonpatch.ApplyOptions` includes the configuration options above and adds two new options: `AllowMissingPathOnRemove` and `EnsurePathExistsOnAdd`. When `AllowMissingPathOnRemove` is set to `true`, `jsonpatch.ApplyWithOptions` will ignore `remove` operations whose `path` points to a non-existent location in the JSON document. `AllowMissingPathOnRemove` defaults to `false` which will lead to `jsonpatch.ApplyWithOptions` returning an error when hitting a missing `path` on `remove`. When `EnsurePathExistsOnAdd` is set to `true`, `jsonpatch.ApplyWithOptions` will make sure that `add` operations produce all the `path` elements that are missing from the target object. Use `jsonpatch.NewApplyOptions` to create an instance of `jsonpatch.ApplyOptions` whose values are populated from the global configuration variables. ## Create and apply a merge patch Given both an original JSON document and a modified JSON document, you can create a [Merge Patch](https://tools.ietf.org/html/rfc7396) document. It can describe the changes needed to convert from the original to the modified JSON document. Once you have a merge patch, you can apply it to other JSON documents using the `jsonpatch.MergePatch(document, patch)` function. ```go package main import ( "fmt" jsonpatch "github.com/evanphx/json-patch" ) func main() { // Let's create a merge patch from these two documents... original := []byte(`{"name": "John", "age": 24, "height": 3.21}`) target := []byte(`{"name": "Jane", "age": 24}`) patch, err := jsonpatch.CreateMergePatch(original, target) if err != nil { panic(err) } // Now lets apply the patch against a different JSON document... alternative := []byte(`{"name": "Tina", "age": 28, "height": 3.75}`) modifiedAlternative, err := jsonpatch.MergePatch(alternative, patch) fmt.Printf("patch document: %s\n", patch) fmt.Printf("updated alternative doc: %s\n", modifiedAlternative) } ``` When ran, you get the following output: ```bash $ go run main.go patch document: {"height":null,"name":"Jane"} updated alternative doc: {"age":28,"name":"Jane"} ``` ## Create and apply a JSON Patch You can create patch objects using `DecodePatch([]byte)`, which can then be applied against JSON documents. The following is an example of creating a patch from two operations, and applying it against a JSON document. ```go package main import ( "fmt" jsonpatch "github.com/evanphx/json-patch" ) func main() { original := []byte(`{"name": "John", "age": 24, "height": 3.21}`) patchJSON := []byte(`[ {"op": "replace", "path": "/name", "value": "Jane"}, {"op": "remove", "path": "/height"} ]`) patch, err := jsonpatch.DecodePatch(patchJSON) if err != nil { panic(err) } modified, err := patch.Apply(original) if err != nil { panic(err) } fmt.Printf("Original document: %s\n", original) fmt.Printf("Modified document: %s\n", modified) } ``` When ran, you get the following output: ```bash $ go run main.go Original document: {"name": "John", "age": 24, "height": 3.21} Modified document: {"age":24,"name":"Jane"} ``` ## Comparing JSON documents Due to potential whitespace and ordering differences, one cannot simply compare JSON strings or byte-arrays directly. As such, you can instead use `jsonpatch.Equal(document1, document2)` to determine if two JSON documents are _structurally_ equal. This ignores whitespace differences, and key-value ordering. ```go package main import ( "fmt" jsonpatch "github.com/evanphx/json-patch" ) func main() { original := []byte(`{"name": "John", "age": 24, "height": 3.21}`) similar := []byte(` { "age": 24, "height": 3.21, "name": "John" } `) different := []byte(`{"name": "Jane", "age": 20, "height": 3.37}`) if jsonpatch.Equal(original, similar) { fmt.Println(`"original" is structurally equal to "similar"`) } if !jsonpatch.Equal(original, different) { fmt.Println(`"original" is _not_ structurally equal to "different"`) } } ``` When ran, you get the following output: ```bash $ go run main.go "original" is structurally equal to "similar" "original" is _not_ structurally equal to "different" ``` ## Combine merge patches Given two JSON merge patch documents, it is possible to combine them into a single merge patch which can describe both set of changes. The resulting merge patch can be used such that applying it results in a document structurally similar as merging each merge patch to the document in succession. ```go package main import ( "fmt" jsonpatch "github.com/evanphx/json-patch" ) func main() { original := []byte(`{"name": "John", "age": 24, "height": 3.21}`) nameAndHeight := []byte(`{"height":null,"name":"Jane"}`) ageAndEyes := []byte(`{"age":4.23,"eyes":"blue"}`) // Let's combine these merge patch documents... combinedPatch, err := jsonpatch.MergeMergePatches(nameAndHeight, ageAndEyes) if err != nil { panic(err) } // Apply each patch individual against the original document withoutCombinedPatch, err := jsonpatch.MergePatch(original, nameAndHeight) if err != nil { panic(err) } withoutCombinedPatch, err = jsonpatch.MergePatch(withoutCombinedPatch, ageAndEyes) if err != nil { panic(err) } // Apply the combined patch against the original document withCombinedPatch, err := jsonpatch.MergePatch(original, combinedPatch) if err != nil { panic(err) } // Do both result in the same thing? They should! if jsonpatch.Equal(withCombinedPatch, withoutCombinedPatch) { fmt.Println("Both JSON documents are structurally the same!") } fmt.Printf("combined merge patch: %s", combinedPatch) } ``` When ran, you get the following output: ```bash $ go run main.go Both JSON documents are structurally the same! combined merge patch: {"age":4.23,"eyes":"blue","height":null,"name":"Jane"} ``` # CLI for comparing JSON documents You can install the commandline program `json-patch`. This program can take multiple JSON patch documents as arguments, and fed a JSON document from `stdin`. It will apply the patch(es) against the document and output the modified doc. **patch.1.json** ```json [ {"op": "replace", "path": "/name", "value": "Jane"}, {"op": "remove", "path": "/height"} ] ``` **patch.2.json** ```json [ {"op": "add", "path": "/address", "value": "123 Main St"}, {"op": "replace", "path": "/age", "value": "21"} ] ``` **document.json** ```json { "name": "John", "age": 24, "height": 3.21 } ``` You can then run: ```bash $ go install github.com/evanphx/json-patch/cmd/json-patch $ cat document.json | json-patch -p patch.1.json -p patch.2.json {"address":"123 Main St","age":"21","name":"Jane"} ``` # Help It! Contributions are welcomed! Leave [an issue](https://github.com/evanphx/json-patch/issues) or [create a PR](https://github.com/evanphx/json-patch/compare). Before creating a pull request, we'd ask that you make sure tests are passing and that you have added new tests when applicable. Contributors can run tests using: ```bash go test -cover ./... ``` Builds for pull requests are tested automatically using [TravisCI](https://travis-ci.org/evanphx/json-patch). ================================================ FILE: vendor/github.com/evanphx/json-patch/errors.go ================================================ package jsonpatch import "fmt" // AccumulatedCopySizeError is an error type returned when the accumulated size // increase caused by copy operations in a patch operation has exceeded the // limit. type AccumulatedCopySizeError struct { limit int64 accumulated int64 } // NewAccumulatedCopySizeError returns an AccumulatedCopySizeError. func NewAccumulatedCopySizeError(l, a int64) *AccumulatedCopySizeError { return &AccumulatedCopySizeError{limit: l, accumulated: a} } // Error implements the error interface. func (a *AccumulatedCopySizeError) Error() string { return fmt.Sprintf("Unable to complete the copy, the accumulated size increase of copy is %d, exceeding the limit %d", a.accumulated, a.limit) } // ArraySizeError is an error type returned when the array size has exceeded // the limit. type ArraySizeError struct { limit int size int } // NewArraySizeError returns an ArraySizeError. func NewArraySizeError(l, s int) *ArraySizeError { return &ArraySizeError{limit: l, size: s} } // Error implements the error interface. func (a *ArraySizeError) Error() string { return fmt.Sprintf("Unable to create array of size %d, limit is %d", a.size, a.limit) } ================================================ FILE: vendor/github.com/evanphx/json-patch/merge.go ================================================ package jsonpatch import ( "bytes" "encoding/json" "fmt" "reflect" ) func merge(cur, patch *lazyNode, mergeMerge bool) *lazyNode { curDoc, err := cur.intoDoc() if err != nil { pruneNulls(patch) return patch } patchDoc, err := patch.intoDoc() if err != nil { return patch } mergeDocs(curDoc, patchDoc, mergeMerge) return cur } func mergeDocs(doc, patch *partialDoc, mergeMerge bool) { for k, v := range *patch { if v == nil { if mergeMerge { (*doc)[k] = nil } else { delete(*doc, k) } } else { cur, ok := (*doc)[k] if !ok || cur == nil { if !mergeMerge { pruneNulls(v) } (*doc)[k] = v } else { (*doc)[k] = merge(cur, v, mergeMerge) } } } } func pruneNulls(n *lazyNode) { sub, err := n.intoDoc() if err == nil { pruneDocNulls(sub) } else { ary, err := n.intoAry() if err == nil { pruneAryNulls(ary) } } } func pruneDocNulls(doc *partialDoc) *partialDoc { for k, v := range *doc { if v == nil { delete(*doc, k) } else { pruneNulls(v) } } return doc } func pruneAryNulls(ary *partialArray) *partialArray { newAry := []*lazyNode{} for _, v := range *ary { if v != nil { pruneNulls(v) } newAry = append(newAry, v) } *ary = newAry return ary } var ErrBadJSONDoc = fmt.Errorf("Invalid JSON Document") var ErrBadJSONPatch = fmt.Errorf("Invalid JSON Patch") var errBadMergeTypes = fmt.Errorf("Mismatched JSON Documents") // MergeMergePatches merges two merge patches together, such that // applying this resulting merged merge patch to a document yields the same // as merging each merge patch to the document in succession. func MergeMergePatches(patch1Data, patch2Data []byte) ([]byte, error) { return doMergePatch(patch1Data, patch2Data, true) } // MergePatch merges the patchData into the docData. func MergePatch(docData, patchData []byte) ([]byte, error) { return doMergePatch(docData, patchData, false) } func doMergePatch(docData, patchData []byte, mergeMerge bool) ([]byte, error) { doc := &partialDoc{} docErr := json.Unmarshal(docData, doc) patch := &partialDoc{} patchErr := json.Unmarshal(patchData, patch) if _, ok := docErr.(*json.SyntaxError); ok { return nil, ErrBadJSONDoc } if _, ok := patchErr.(*json.SyntaxError); ok { return nil, ErrBadJSONPatch } if docErr == nil && *doc == nil { return nil, ErrBadJSONDoc } if patchErr == nil && *patch == nil { return nil, ErrBadJSONPatch } if docErr != nil || patchErr != nil { // Not an error, just not a doc, so we turn straight into the patch if patchErr == nil { if mergeMerge { doc = patch } else { doc = pruneDocNulls(patch) } } else { patchAry := &partialArray{} patchErr = json.Unmarshal(patchData, patchAry) if patchErr != nil { return nil, ErrBadJSONPatch } pruneAryNulls(patchAry) out, patchErr := json.Marshal(patchAry) if patchErr != nil { return nil, ErrBadJSONPatch } return out, nil } } else { mergeDocs(doc, patch, mergeMerge) } return json.Marshal(doc) } // resemblesJSONArray indicates whether the byte-slice "appears" to be // a JSON array or not. // False-positives are possible, as this function does not check the internal // structure of the array. It only checks that the outer syntax is present and // correct. func resemblesJSONArray(input []byte) bool { input = bytes.TrimSpace(input) hasPrefix := bytes.HasPrefix(input, []byte("[")) hasSuffix := bytes.HasSuffix(input, []byte("]")) return hasPrefix && hasSuffix } // CreateMergePatch will return a merge patch document capable of converting // the original document(s) to the modified document(s). // The parameters can be bytes of either two JSON Documents, or two arrays of // JSON documents. // The merge patch returned follows the specification defined at http://tools.ietf.org/html/draft-ietf-appsawg-json-merge-patch-07 func CreateMergePatch(originalJSON, modifiedJSON []byte) ([]byte, error) { originalResemblesArray := resemblesJSONArray(originalJSON) modifiedResemblesArray := resemblesJSONArray(modifiedJSON) // Do both byte-slices seem like JSON arrays? if originalResemblesArray && modifiedResemblesArray { return createArrayMergePatch(originalJSON, modifiedJSON) } // Are both byte-slices are not arrays? Then they are likely JSON objects... if !originalResemblesArray && !modifiedResemblesArray { return createObjectMergePatch(originalJSON, modifiedJSON) } // None of the above? Then return an error because of mismatched types. return nil, errBadMergeTypes } // createObjectMergePatch will return a merge-patch document capable of // converting the original document to the modified document. func createObjectMergePatch(originalJSON, modifiedJSON []byte) ([]byte, error) { originalDoc := map[string]interface{}{} modifiedDoc := map[string]interface{}{} err := json.Unmarshal(originalJSON, &originalDoc) if err != nil { return nil, ErrBadJSONDoc } err = json.Unmarshal(modifiedJSON, &modifiedDoc) if err != nil { return nil, ErrBadJSONDoc } dest, err := getDiff(originalDoc, modifiedDoc) if err != nil { return nil, err } return json.Marshal(dest) } // createArrayMergePatch will return an array of merge-patch documents capable // of converting the original document to the modified document for each // pair of JSON documents provided in the arrays. // Arrays of mismatched sizes will result in an error. func createArrayMergePatch(originalJSON, modifiedJSON []byte) ([]byte, error) { originalDocs := []json.RawMessage{} modifiedDocs := []json.RawMessage{} err := json.Unmarshal(originalJSON, &originalDocs) if err != nil { return nil, ErrBadJSONDoc } err = json.Unmarshal(modifiedJSON, &modifiedDocs) if err != nil { return nil, ErrBadJSONDoc } total := len(originalDocs) if len(modifiedDocs) != total { return nil, ErrBadJSONDoc } result := []json.RawMessage{} for i := 0; i < len(originalDocs); i++ { original := originalDocs[i] modified := modifiedDocs[i] patch, err := createObjectMergePatch(original, modified) if err != nil { return nil, err } result = append(result, json.RawMessage(patch)) } return json.Marshal(result) } // Returns true if the array matches (must be json types). // As is idiomatic for go, an empty array is not the same as a nil array. func matchesArray(a, b []interface{}) bool { if len(a) != len(b) { return false } if (a == nil && b != nil) || (a != nil && b == nil) { return false } for i := range a { if !matchesValue(a[i], b[i]) { return false } } return true } // Returns true if the values matches (must be json types) // The types of the values must match, otherwise it will always return false // If two map[string]interface{} are given, all elements must match. func matchesValue(av, bv interface{}) bool { if reflect.TypeOf(av) != reflect.TypeOf(bv) { return false } switch at := av.(type) { case string: bt := bv.(string) if bt == at { return true } case float64: bt := bv.(float64) if bt == at { return true } case bool: bt := bv.(bool) if bt == at { return true } case nil: // Both nil, fine. return true case map[string]interface{}: bt := bv.(map[string]interface{}) if len(bt) != len(at) { return false } for key := range bt { av, aOK := at[key] bv, bOK := bt[key] if aOK != bOK { return false } if !matchesValue(av, bv) { return false } } return true case []interface{}: bt := bv.([]interface{}) return matchesArray(at, bt) } return false } // getDiff returns the (recursive) difference between a and b as a map[string]interface{}. func getDiff(a, b map[string]interface{}) (map[string]interface{}, error) { into := map[string]interface{}{} for key, bv := range b { av, ok := a[key] // value was added if !ok { into[key] = bv continue } // If types have changed, replace completely if reflect.TypeOf(av) != reflect.TypeOf(bv) { into[key] = bv continue } // Types are the same, compare values switch at := av.(type) { case map[string]interface{}: bt := bv.(map[string]interface{}) dst := make(map[string]interface{}, len(bt)) dst, err := getDiff(at, bt) if err != nil { return nil, err } if len(dst) > 0 { into[key] = dst } case string, float64, bool: if !matchesValue(av, bv) { into[key] = bv } case []interface{}: bt := bv.([]interface{}) if !matchesArray(at, bt) { into[key] = bv } case nil: switch bv.(type) { case nil: // Both nil, fine. default: into[key] = bv } default: panic(fmt.Sprintf("Unknown type:%T in key %s", av, key)) } } // Now add all deleted values as nil for key := range a { _, found := b[key] if !found { into[key] = nil } } return into, nil } ================================================ FILE: vendor/github.com/evanphx/json-patch/patch.go ================================================ package jsonpatch import ( "bytes" "encoding/json" "fmt" "strconv" "strings" "github.com/pkg/errors" ) const ( eRaw = iota eDoc eAry ) var ( // SupportNegativeIndices decides whether to support non-standard practice of // allowing negative indices to mean indices starting at the end of an array. // Default to true. SupportNegativeIndices bool = true // AccumulatedCopySizeLimit limits the total size increase in bytes caused by // "copy" operations in a patch. AccumulatedCopySizeLimit int64 = 0 ) var ( ErrTestFailed = errors.New("test failed") ErrMissing = errors.New("missing value") ErrUnknownType = errors.New("unknown object type") ErrInvalid = errors.New("invalid state detected") ErrInvalidIndex = errors.New("invalid index referenced") ) type lazyNode struct { raw *json.RawMessage doc partialDoc ary partialArray which int } // Operation is a single JSON-Patch step, such as a single 'add' operation. type Operation map[string]*json.RawMessage // Patch is an ordered collection of Operations. type Patch []Operation type partialDoc map[string]*lazyNode type partialArray []*lazyNode type container interface { get(key string) (*lazyNode, error) set(key string, val *lazyNode) error add(key string, val *lazyNode) error remove(key string) error } func newLazyNode(raw *json.RawMessage) *lazyNode { return &lazyNode{raw: raw, doc: nil, ary: nil, which: eRaw} } func (n *lazyNode) MarshalJSON() ([]byte, error) { switch n.which { case eRaw: return json.Marshal(n.raw) case eDoc: return json.Marshal(n.doc) case eAry: return json.Marshal(n.ary) default: return nil, ErrUnknownType } } func (n *lazyNode) UnmarshalJSON(data []byte) error { dest := make(json.RawMessage, len(data)) copy(dest, data) n.raw = &dest n.which = eRaw return nil } func deepCopy(src *lazyNode) (*lazyNode, int, error) { if src == nil { return nil, 0, nil } a, err := src.MarshalJSON() if err != nil { return nil, 0, err } sz := len(a) ra := make(json.RawMessage, sz) copy(ra, a) return newLazyNode(&ra), sz, nil } func (n *lazyNode) intoDoc() (*partialDoc, error) { if n.which == eDoc { return &n.doc, nil } if n.raw == nil { return nil, ErrInvalid } err := json.Unmarshal(*n.raw, &n.doc) if err != nil { return nil, err } n.which = eDoc return &n.doc, nil } func (n *lazyNode) intoAry() (*partialArray, error) { if n.which == eAry { return &n.ary, nil } if n.raw == nil { return nil, ErrInvalid } err := json.Unmarshal(*n.raw, &n.ary) if err != nil { return nil, err } n.which = eAry return &n.ary, nil } func (n *lazyNode) compact() []byte { buf := &bytes.Buffer{} if n.raw == nil { return nil } err := json.Compact(buf, *n.raw) if err != nil { return *n.raw } return buf.Bytes() } func (n *lazyNode) tryDoc() bool { if n.raw == nil { return false } err := json.Unmarshal(*n.raw, &n.doc) if err != nil { return false } n.which = eDoc return true } func (n *lazyNode) tryAry() bool { if n.raw == nil { return false } err := json.Unmarshal(*n.raw, &n.ary) if err != nil { return false } n.which = eAry return true } func (n *lazyNode) equal(o *lazyNode) bool { if n.which == eRaw { if !n.tryDoc() && !n.tryAry() { if o.which != eRaw { return false } return bytes.Equal(n.compact(), o.compact()) } } if n.which == eDoc { if o.which == eRaw { if !o.tryDoc() { return false } } if o.which != eDoc { return false } if len(n.doc) != len(o.doc) { return false } for k, v := range n.doc { ov, ok := o.doc[k] if !ok { return false } if (v == nil) != (ov == nil) { return false } if v == nil && ov == nil { continue } if !v.equal(ov) { return false } } return true } if o.which != eAry && !o.tryAry() { return false } if len(n.ary) != len(o.ary) { return false } for idx, val := range n.ary { if !val.equal(o.ary[idx]) { return false } } return true } // Kind reads the "op" field of the Operation. func (o Operation) Kind() string { if obj, ok := o["op"]; ok && obj != nil { var op string err := json.Unmarshal(*obj, &op) if err != nil { return "unknown" } return op } return "unknown" } // Path reads the "path" field of the Operation. func (o Operation) Path() (string, error) { if obj, ok := o["path"]; ok && obj != nil { var op string err := json.Unmarshal(*obj, &op) if err != nil { return "unknown", err } return op, nil } return "unknown", errors.Wrapf(ErrMissing, "operation missing path field") } // From reads the "from" field of the Operation. func (o Operation) From() (string, error) { if obj, ok := o["from"]; ok && obj != nil { var op string err := json.Unmarshal(*obj, &op) if err != nil { return "unknown", err } return op, nil } return "unknown", errors.Wrapf(ErrMissing, "operation, missing from field") } func (o Operation) value() *lazyNode { if obj, ok := o["value"]; ok { return newLazyNode(obj) } return nil } // ValueInterface decodes the operation value into an interface. func (o Operation) ValueInterface() (interface{}, error) { if obj, ok := o["value"]; ok && obj != nil { var v interface{} err := json.Unmarshal(*obj, &v) if err != nil { return nil, err } return v, nil } return nil, errors.Wrapf(ErrMissing, "operation, missing value field") } func isArray(buf []byte) bool { Loop: for _, c := range buf { switch c { case ' ': case '\n': case '\t': continue case '[': return true default: break Loop } } return false } func findObject(pd *container, path string) (container, string) { doc := *pd split := strings.Split(path, "/") if len(split) < 2 { return nil, "" } parts := split[1 : len(split)-1] key := split[len(split)-1] var err error for _, part := range parts { next, ok := doc.get(decodePatchKey(part)) if next == nil || ok != nil { return nil, "" } if isArray(*next.raw) { doc, err = next.intoAry() if err != nil { return nil, "" } } else { doc, err = next.intoDoc() if err != nil { return nil, "" } } } return doc, decodePatchKey(key) } func (d *partialDoc) set(key string, val *lazyNode) error { (*d)[key] = val return nil } func (d *partialDoc) add(key string, val *lazyNode) error { (*d)[key] = val return nil } func (d *partialDoc) get(key string) (*lazyNode, error) { return (*d)[key], nil } func (d *partialDoc) remove(key string) error { _, ok := (*d)[key] if !ok { return errors.Wrapf(ErrMissing, "Unable to remove nonexistent key: %s", key) } delete(*d, key) return nil } // set should only be used to implement the "replace" operation, so "key" must // be an already existing index in "d". func (d *partialArray) set(key string, val *lazyNode) error { idx, err := strconv.Atoi(key) if err != nil { return err } if idx < 0 { if !SupportNegativeIndices { return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx) } if idx < -len(*d) { return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx) } idx += len(*d) } (*d)[idx] = val return nil } func (d *partialArray) add(key string, val *lazyNode) error { if key == "-" { *d = append(*d, val) return nil } idx, err := strconv.Atoi(key) if err != nil { return errors.Wrapf(err, "value was not a proper array index: '%s'", key) } sz := len(*d) + 1 ary := make([]*lazyNode, sz) cur := *d if idx >= len(ary) { return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx) } if idx < 0 { if !SupportNegativeIndices { return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx) } if idx < -len(ary) { return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx) } idx += len(ary) } copy(ary[0:idx], cur[0:idx]) ary[idx] = val copy(ary[idx+1:], cur[idx:]) *d = ary return nil } func (d *partialArray) get(key string) (*lazyNode, error) { idx, err := strconv.Atoi(key) if err != nil { return nil, err } if idx < 0 { if !SupportNegativeIndices { return nil, errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx) } if idx < -len(*d) { return nil, errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx) } idx += len(*d) } if idx >= len(*d) { return nil, errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx) } return (*d)[idx], nil } func (d *partialArray) remove(key string) error { idx, err := strconv.Atoi(key) if err != nil { return err } cur := *d if idx >= len(cur) { return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx) } if idx < 0 { if !SupportNegativeIndices { return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx) } if idx < -len(cur) { return errors.Wrapf(ErrInvalidIndex, "Unable to access invalid index: %d", idx) } idx += len(cur) } ary := make([]*lazyNode, len(cur)-1) copy(ary[0:idx], cur[0:idx]) copy(ary[idx:], cur[idx+1:]) *d = ary return nil } func (p Patch) add(doc *container, op Operation) error { path, err := op.Path() if err != nil { return errors.Wrapf(ErrMissing, "add operation failed to decode path") } con, key := findObject(doc, path) if con == nil { return errors.Wrapf(ErrMissing, "add operation does not apply: doc is missing path: \"%s\"", path) } err = con.add(key, op.value()) if err != nil { return errors.Wrapf(err, "error in add for path: '%s'", path) } return nil } func (p Patch) remove(doc *container, op Operation) error { path, err := op.Path() if err != nil { return errors.Wrapf(ErrMissing, "remove operation failed to decode path") } con, key := findObject(doc, path) if con == nil { return errors.Wrapf(ErrMissing, "remove operation does not apply: doc is missing path: \"%s\"", path) } err = con.remove(key) if err != nil { return errors.Wrapf(err, "error in remove for path: '%s'", path) } return nil } func (p Patch) replace(doc *container, op Operation) error { path, err := op.Path() if err != nil { return errors.Wrapf(err, "replace operation failed to decode path") } if path == "" { val := op.value() if val.which == eRaw { if !val.tryDoc() { if !val.tryAry() { return errors.Wrapf(err, "replace operation value must be object or array") } } } switch val.which { case eAry: *doc = &val.ary case eDoc: *doc = &val.doc case eRaw: return errors.Wrapf(err, "replace operation hit impossible case") } return nil } con, key := findObject(doc, path) if con == nil { return errors.Wrapf(ErrMissing, "replace operation does not apply: doc is missing path: %s", path) } _, ok := con.get(key) if ok != nil { return errors.Wrapf(ErrMissing, "replace operation does not apply: doc is missing key: %s", path) } err = con.set(key, op.value()) if err != nil { return errors.Wrapf(err, "error in remove for path: '%s'", path) } return nil } func (p Patch) move(doc *container, op Operation) error { from, err := op.From() if err != nil { return errors.Wrapf(err, "move operation failed to decode from") } con, key := findObject(doc, from) if con == nil { return errors.Wrapf(ErrMissing, "move operation does not apply: doc is missing from path: %s", from) } val, err := con.get(key) if err != nil { return errors.Wrapf(err, "error in move for path: '%s'", key) } err = con.remove(key) if err != nil { return errors.Wrapf(err, "error in move for path: '%s'", key) } path, err := op.Path() if err != nil { return errors.Wrapf(err, "move operation failed to decode path") } con, key = findObject(doc, path) if con == nil { return errors.Wrapf(ErrMissing, "move operation does not apply: doc is missing destination path: %s", path) } err = con.add(key, val) if err != nil { return errors.Wrapf(err, "error in move for path: '%s'", path) } return nil } func (p Patch) test(doc *container, op Operation) error { path, err := op.Path() if err != nil { return errors.Wrapf(err, "test operation failed to decode path") } if path == "" { var self lazyNode switch sv := (*doc).(type) { case *partialDoc: self.doc = *sv self.which = eDoc case *partialArray: self.ary = *sv self.which = eAry } if self.equal(op.value()) { return nil } return errors.Wrapf(ErrTestFailed, "testing value %s failed", path) } con, key := findObject(doc, path) if con == nil { return errors.Wrapf(ErrMissing, "test operation does not apply: is missing path: %s", path) } val, err := con.get(key) if err != nil { return errors.Wrapf(err, "error in test for path: '%s'", path) } if val == nil { if op.value().raw == nil { return nil } return errors.Wrapf(ErrTestFailed, "testing value %s failed", path) } else if op.value() == nil { return errors.Wrapf(ErrTestFailed, "testing value %s failed", path) } if val.equal(op.value()) { return nil } return errors.Wrapf(ErrTestFailed, "testing value %s failed", path) } func (p Patch) copy(doc *container, op Operation, accumulatedCopySize *int64) error { from, err := op.From() if err != nil { return errors.Wrapf(err, "copy operation failed to decode from") } con, key := findObject(doc, from) if con == nil { return errors.Wrapf(ErrMissing, "copy operation does not apply: doc is missing from path: %s", from) } val, err := con.get(key) if err != nil { return errors.Wrapf(err, "error in copy for from: '%s'", from) } path, err := op.Path() if err != nil { return errors.Wrapf(ErrMissing, "copy operation failed to decode path") } con, key = findObject(doc, path) if con == nil { return errors.Wrapf(ErrMissing, "copy operation does not apply: doc is missing destination path: %s", path) } valCopy, sz, err := deepCopy(val) if err != nil { return errors.Wrapf(err, "error while performing deep copy") } (*accumulatedCopySize) += int64(sz) if AccumulatedCopySizeLimit > 0 && *accumulatedCopySize > AccumulatedCopySizeLimit { return NewAccumulatedCopySizeError(AccumulatedCopySizeLimit, *accumulatedCopySize) } err = con.add(key, valCopy) if err != nil { return errors.Wrapf(err, "error while adding value during copy") } return nil } // Equal indicates if 2 JSON documents have the same structural equality. func Equal(a, b []byte) bool { ra := make(json.RawMessage, len(a)) copy(ra, a) la := newLazyNode(&ra) rb := make(json.RawMessage, len(b)) copy(rb, b) lb := newLazyNode(&rb) return la.equal(lb) } // DecodePatch decodes the passed JSON document as an RFC 6902 patch. func DecodePatch(buf []byte) (Patch, error) { var p Patch err := json.Unmarshal(buf, &p) if err != nil { return nil, err } return p, nil } // Apply mutates a JSON document according to the patch, and returns the new // document. func (p Patch) Apply(doc []byte) ([]byte, error) { return p.ApplyIndent(doc, "") } // ApplyIndent mutates a JSON document according to the patch, and returns the new // document indented. func (p Patch) ApplyIndent(doc []byte, indent string) ([]byte, error) { if len(doc) == 0 { return doc, nil } var pd container if doc[0] == '[' { pd = &partialArray{} } else { pd = &partialDoc{} } err := json.Unmarshal(doc, pd) if err != nil { return nil, err } err = nil var accumulatedCopySize int64 for _, op := range p { switch op.Kind() { case "add": err = p.add(&pd, op) case "remove": err = p.remove(&pd, op) case "replace": err = p.replace(&pd, op) case "move": err = p.move(&pd, op) case "test": err = p.test(&pd, op) case "copy": err = p.copy(&pd, op, &accumulatedCopySize) default: err = fmt.Errorf("Unexpected kind: %s", op.Kind()) } if err != nil { return nil, err } } if indent != "" { return json.MarshalIndent(pd, "", indent) } return json.Marshal(pd) } // From http://tools.ietf.org/html/rfc6901#section-4 : // // Evaluation of each reference token begins by decoding any escaped // character sequence. This is performed by first transforming any // occurrence of the sequence '~1' to '/', and then transforming any // occurrence of the sequence '~0' to '~'. var ( rfc6901Decoder = strings.NewReplacer("~1", "/", "~0", "~") ) func decodePatchKey(k string) string { return rfc6901Decoder.Replace(k) } ================================================ FILE: vendor/github.com/fatih/color/LICENSE.md ================================================ The MIT License (MIT) Copyright (c) 2013 Fatih Arslan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/fatih/color/README.md ================================================ # color [![](https://github.com/fatih/color/workflows/build/badge.svg)](https://github.com/fatih/color/actions) [![PkgGoDev](https://pkg.go.dev/badge/github.com/fatih/color)](https://pkg.go.dev/github.com/fatih/color) Color lets you use colorized outputs in terms of [ANSI Escape Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Go (Golang). It has support for Windows too! The API can be used in several ways, pick one that suits you. ![Color](https://user-images.githubusercontent.com/438920/96832689-03b3e000-13f4-11eb-9803-46f4c4de3406.jpg) ## Install ```bash go get github.com/fatih/color ``` ## Examples ### Standard colors ```go // Print with default helper functions color.Cyan("Prints text in cyan.") // A newline will be appended automatically color.Blue("Prints %s in blue.", "text") // These are using the default foreground colors color.Red("We have red") color.Magenta("And many others ..") ``` ### Mix and reuse colors ```go // Create a new color object c := color.New(color.FgCyan).Add(color.Underline) c.Println("Prints cyan text with an underline.") // Or just add them to New() d := color.New(color.FgCyan, color.Bold) d.Printf("This prints bold cyan %s\n", "too!.") // Mix up foreground and background colors, create new mixes! red := color.New(color.FgRed) boldRed := red.Add(color.Bold) boldRed.Println("This will print text in bold red.") whiteBackground := red.Add(color.BgWhite) whiteBackground.Println("Red text with white background.") ``` ### Use your own output (io.Writer) ```go // Use your own io.Writer output color.New(color.FgBlue).Fprintln(myWriter, "blue color!") blue := color.New(color.FgBlue) blue.Fprint(writer, "This will print text in blue.") ``` ### Custom print functions (PrintFunc) ```go // Create a custom print function for convenience red := color.New(color.FgRed).PrintfFunc() red("Warning") red("Error: %s", err) // Mix up multiple attributes notice := color.New(color.Bold, color.FgGreen).PrintlnFunc() notice("Don't forget this...") ``` ### Custom fprint functions (FprintFunc) ```go blue := color.New(color.FgBlue).FprintfFunc() blue(myWriter, "important notice: %s", stars) // Mix up with multiple attributes success := color.New(color.Bold, color.FgGreen).FprintlnFunc() success(myWriter, "Don't forget this...") ``` ### Insert into noncolor strings (SprintFunc) ```go // Create SprintXxx functions to mix strings with other non-colorized strings: yellow := color.New(color.FgYellow).SprintFunc() red := color.New(color.FgRed).SprintFunc() fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error")) info := color.New(color.FgWhite, color.BgGreen).SprintFunc() fmt.Printf("This %s rocks!\n", info("package")) // Use helper functions fmt.Println("This", color.RedString("warning"), "should be not neglected.") fmt.Printf("%v %v\n", color.GreenString("Info:"), "an important message.") // Windows supported too! Just don't forget to change the output to color.Output fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS")) ``` ### Plug into existing code ```go // Use handy standard colors color.Set(color.FgYellow) fmt.Println("Existing text will now be in yellow") fmt.Printf("This one %s\n", "too") color.Unset() // Don't forget to unset // You can mix up parameters color.Set(color.FgMagenta, color.Bold) defer color.Unset() // Use it in your function fmt.Println("All text will now be bold magenta.") ``` ### Disable/Enable color There might be a case where you want to explicitly disable/enable color output. the `go-isatty` package will automatically disable color output for non-tty output streams (for example if the output were piped directly to `less`). The `color` package also disables color output if the [`NO_COLOR`](https://no-color.org) environment variable is set (regardless of its value). `Color` has support to disable/enable colors programatically both globally and for single color definitions. For example suppose you have a CLI app and a `--no-color` bool flag. You can easily disable the color output with: ```go var flagNoColor = flag.Bool("no-color", false, "Disable color output") if *flagNoColor { color.NoColor = true // disables colorized output } ``` It also has support for single color definitions (local). You can disable/enable color output on the fly: ```go c := color.New(color.FgCyan) c.Println("Prints cyan text") c.DisableColor() c.Println("This is printed without any color") c.EnableColor() c.Println("This prints again cyan...") ``` ## GitHub Actions To output color in GitHub Actions (or other CI systems that support ANSI colors), make sure to set `color.NoColor = false` so that it bypasses the check for non-tty output streams. ## Todo * Save/Return previous values * Evaluate fmt.Formatter interface ## Credits * [Fatih Arslan](https://github.com/fatih) * Windows support via @mattn: [colorable](https://github.com/mattn/go-colorable) ## License The MIT License (MIT) - see [`LICENSE.md`](https://github.com/fatih/color/blob/master/LICENSE.md) for more details ================================================ FILE: vendor/github.com/fatih/color/color.go ================================================ package color import ( "fmt" "io" "os" "strconv" "strings" "sync" "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" ) var ( // NoColor defines if the output is colorized or not. It's dynamically set to // false or true based on the stdout's file descriptor referring to a terminal // or not. It's also set to true if the NO_COLOR environment variable is // set (regardless of its value). This is a global option and affects all // colors. For more control over each color block use the methods // DisableColor() individually. NoColor = noColorExists() || os.Getenv("TERM") == "dumb" || (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) // Output defines the standard output of the print functions. By default // os.Stdout is used. Output = colorable.NewColorableStdout() // Error defines a color supporting writer for os.Stderr. Error = colorable.NewColorableStderr() // colorsCache is used to reduce the count of created Color objects and // allows to reuse already created objects with required Attribute. colorsCache = make(map[Attribute]*Color) colorsCacheMu sync.Mutex // protects colorsCache ) // noColorExists returns true if the environment variable NO_COLOR exists. func noColorExists() bool { _, exists := os.LookupEnv("NO_COLOR") return exists } // Color defines a custom color object which is defined by SGR parameters. type Color struct { params []Attribute noColor *bool } // Attribute defines a single SGR Code type Attribute int const escape = "\x1b" // Base attributes const ( Reset Attribute = iota Bold Faint Italic Underline BlinkSlow BlinkRapid ReverseVideo Concealed CrossedOut ) // Foreground text colors const ( FgBlack Attribute = iota + 30 FgRed FgGreen FgYellow FgBlue FgMagenta FgCyan FgWhite ) // Foreground Hi-Intensity text colors const ( FgHiBlack Attribute = iota + 90 FgHiRed FgHiGreen FgHiYellow FgHiBlue FgHiMagenta FgHiCyan FgHiWhite ) // Background text colors const ( BgBlack Attribute = iota + 40 BgRed BgGreen BgYellow BgBlue BgMagenta BgCyan BgWhite ) // Background Hi-Intensity text colors const ( BgHiBlack Attribute = iota + 100 BgHiRed BgHiGreen BgHiYellow BgHiBlue BgHiMagenta BgHiCyan BgHiWhite ) // New returns a newly created color object. func New(value ...Attribute) *Color { c := &Color{ params: make([]Attribute, 0), } if noColorExists() { c.noColor = boolPtr(true) } c.Add(value...) return c } // Set sets the given parameters immediately. It will change the color of // output with the given SGR parameters until color.Unset() is called. func Set(p ...Attribute) *Color { c := New(p...) c.Set() return c } // Unset resets all escape attributes and clears the output. Usually should // be called after Set(). func Unset() { if NoColor { return } fmt.Fprintf(Output, "%s[%dm", escape, Reset) } // Set sets the SGR sequence. func (c *Color) Set() *Color { if c.isNoColorSet() { return c } fmt.Fprintf(Output, c.format()) return c } func (c *Color) unset() { if c.isNoColorSet() { return } Unset() } func (c *Color) setWriter(w io.Writer) *Color { if c.isNoColorSet() { return c } fmt.Fprintf(w, c.format()) return c } func (c *Color) unsetWriter(w io.Writer) { if c.isNoColorSet() { return } if NoColor { return } fmt.Fprintf(w, "%s[%dm", escape, Reset) } // Add is used to chain SGR parameters. Use as many as parameters to combine // and create custom color objects. Example: Add(color.FgRed, color.Underline). func (c *Color) Add(value ...Attribute) *Color { c.params = append(c.params, value...) return c } func (c *Color) prepend(value Attribute) { c.params = append(c.params, 0) copy(c.params[1:], c.params[0:]) c.params[0] = value } // Fprint formats using the default formats for its operands and writes to w. // Spaces are added between operands when neither is a string. // It returns the number of bytes written and any write error encountered. // On Windows, users should wrap w with colorable.NewColorable() if w is of // type *os.File. func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) { c.setWriter(w) defer c.unsetWriter(w) return fmt.Fprint(w, a...) } // Print formats using the default formats for its operands and writes to // standard output. Spaces are added between operands when neither is a // string. It returns the number of bytes written and any write error // encountered. This is the standard fmt.Print() method wrapped with the given // color. func (c *Color) Print(a ...interface{}) (n int, err error) { c.Set() defer c.unset() return fmt.Fprint(Output, a...) } // Fprintf formats according to a format specifier and writes to w. // It returns the number of bytes written and any write error encountered. // On Windows, users should wrap w with colorable.NewColorable() if w is of // type *os.File. func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { c.setWriter(w) defer c.unsetWriter(w) return fmt.Fprintf(w, format, a...) } // Printf formats according to a format specifier and writes to standard output. // It returns the number of bytes written and any write error encountered. // This is the standard fmt.Printf() method wrapped with the given color. func (c *Color) Printf(format string, a ...interface{}) (n int, err error) { c.Set() defer c.unset() return fmt.Fprintf(Output, format, a...) } // Fprintln formats using the default formats for its operands and writes to w. // Spaces are always added between operands and a newline is appended. // On Windows, users should wrap w with colorable.NewColorable() if w is of // type *os.File. func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { c.setWriter(w) defer c.unsetWriter(w) return fmt.Fprintln(w, a...) } // Println formats using the default formats for its operands and writes to // standard output. Spaces are always added between operands and a newline is // appended. It returns the number of bytes written and any write error // encountered. This is the standard fmt.Print() method wrapped with the given // color. func (c *Color) Println(a ...interface{}) (n int, err error) { c.Set() defer c.unset() return fmt.Fprintln(Output, a...) } // Sprint is just like Print, but returns a string instead of printing it. func (c *Color) Sprint(a ...interface{}) string { return c.wrap(fmt.Sprint(a...)) } // Sprintln is just like Println, but returns a string instead of printing it. func (c *Color) Sprintln(a ...interface{}) string { return c.wrap(fmt.Sprintln(a...)) } // Sprintf is just like Printf, but returns a string instead of printing it. func (c *Color) Sprintf(format string, a ...interface{}) string { return c.wrap(fmt.Sprintf(format, a...)) } // FprintFunc returns a new function that prints the passed arguments as // colorized with color.Fprint(). func (c *Color) FprintFunc() func(w io.Writer, a ...interface{}) { return func(w io.Writer, a ...interface{}) { c.Fprint(w, a...) } } // PrintFunc returns a new function that prints the passed arguments as // colorized with color.Print(). func (c *Color) PrintFunc() func(a ...interface{}) { return func(a ...interface{}) { c.Print(a...) } } // FprintfFunc returns a new function that prints the passed arguments as // colorized with color.Fprintf(). func (c *Color) FprintfFunc() func(w io.Writer, format string, a ...interface{}) { return func(w io.Writer, format string, a ...interface{}) { c.Fprintf(w, format, a...) } } // PrintfFunc returns a new function that prints the passed arguments as // colorized with color.Printf(). func (c *Color) PrintfFunc() func(format string, a ...interface{}) { return func(format string, a ...interface{}) { c.Printf(format, a...) } } // FprintlnFunc returns a new function that prints the passed arguments as // colorized with color.Fprintln(). func (c *Color) FprintlnFunc() func(w io.Writer, a ...interface{}) { return func(w io.Writer, a ...interface{}) { c.Fprintln(w, a...) } } // PrintlnFunc returns a new function that prints the passed arguments as // colorized with color.Println(). func (c *Color) PrintlnFunc() func(a ...interface{}) { return func(a ...interface{}) { c.Println(a...) } } // SprintFunc returns a new function that returns colorized strings for the // given arguments with fmt.Sprint(). Useful to put into or mix into other // string. Windows users should use this in conjunction with color.Output, example: // // put := New(FgYellow).SprintFunc() // fmt.Fprintf(color.Output, "This is a %s", put("warning")) func (c *Color) SprintFunc() func(a ...interface{}) string { return func(a ...interface{}) string { return c.wrap(fmt.Sprint(a...)) } } // SprintfFunc returns a new function that returns colorized strings for the // given arguments with fmt.Sprintf(). Useful to put into or mix into other // string. Windows users should use this in conjunction with color.Output. func (c *Color) SprintfFunc() func(format string, a ...interface{}) string { return func(format string, a ...interface{}) string { return c.wrap(fmt.Sprintf(format, a...)) } } // SprintlnFunc returns a new function that returns colorized strings for the // given arguments with fmt.Sprintln(). Useful to put into or mix into other // string. Windows users should use this in conjunction with color.Output. func (c *Color) SprintlnFunc() func(a ...interface{}) string { return func(a ...interface{}) string { return c.wrap(fmt.Sprintln(a...)) } } // sequence returns a formatted SGR sequence to be plugged into a "\x1b[...m" // an example output might be: "1;36" -> bold cyan func (c *Color) sequence() string { format := make([]string, len(c.params)) for i, v := range c.params { format[i] = strconv.Itoa(int(v)) } return strings.Join(format, ";") } // wrap wraps the s string with the colors attributes. The string is ready to // be printed. func (c *Color) wrap(s string) string { if c.isNoColorSet() { return s } return c.format() + s + c.unformat() } func (c *Color) format() string { return fmt.Sprintf("%s[%sm", escape, c.sequence()) } func (c *Color) unformat() string { return fmt.Sprintf("%s[%dm", escape, Reset) } // DisableColor disables the color output. Useful to not change any existing // code and still being able to output. Can be used for flags like // "--no-color". To enable back use EnableColor() method. func (c *Color) DisableColor() { c.noColor = boolPtr(true) } // EnableColor enables the color output. Use it in conjunction with // DisableColor(). Otherwise this method has no side effects. func (c *Color) EnableColor() { c.noColor = boolPtr(false) } func (c *Color) isNoColorSet() bool { // check first if we have user set action if c.noColor != nil { return *c.noColor } // if not return the global option, which is disabled by default return NoColor } // Equals returns a boolean value indicating whether two colors are equal. func (c *Color) Equals(c2 *Color) bool { if len(c.params) != len(c2.params) { return false } for _, attr := range c.params { if !c2.attrExists(attr) { return false } } return true } func (c *Color) attrExists(a Attribute) bool { for _, attr := range c.params { if attr == a { return true } } return false } func boolPtr(v bool) *bool { return &v } func getCachedColor(p Attribute) *Color { colorsCacheMu.Lock() defer colorsCacheMu.Unlock() c, ok := colorsCache[p] if !ok { c = New(p) colorsCache[p] = c } return c } func colorPrint(format string, p Attribute, a ...interface{}) { c := getCachedColor(p) if !strings.HasSuffix(format, "\n") { format += "\n" } if len(a) == 0 { c.Print(format) } else { c.Printf(format, a...) } } func colorString(format string, p Attribute, a ...interface{}) string { c := getCachedColor(p) if len(a) == 0 { return c.SprintFunc()(format) } return c.SprintfFunc()(format, a...) } // Black is a convenient helper function to print with black foreground. A // newline is appended to format by default. func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) } // Red is a convenient helper function to print with red foreground. A // newline is appended to format by default. func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) } // Green is a convenient helper function to print with green foreground. A // newline is appended to format by default. func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) } // Yellow is a convenient helper function to print with yellow foreground. // A newline is appended to format by default. func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) } // Blue is a convenient helper function to print with blue foreground. A // newline is appended to format by default. func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) } // Magenta is a convenient helper function to print with magenta foreground. // A newline is appended to format by default. func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) } // Cyan is a convenient helper function to print with cyan foreground. A // newline is appended to format by default. func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) } // White is a convenient helper function to print with white foreground. A // newline is appended to format by default. func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) } // BlackString is a convenient helper function to return a string with black // foreground. func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) } // RedString is a convenient helper function to return a string with red // foreground. func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) } // GreenString is a convenient helper function to return a string with green // foreground. func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) } // YellowString is a convenient helper function to return a string with yellow // foreground. func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) } // BlueString is a convenient helper function to return a string with blue // foreground. func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) } // MagentaString is a convenient helper function to return a string with magenta // foreground. func MagentaString(format string, a ...interface{}) string { return colorString(format, FgMagenta, a...) } // CyanString is a convenient helper function to return a string with cyan // foreground. func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) } // WhiteString is a convenient helper function to return a string with white // foreground. func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) } // HiBlack is a convenient helper function to print with hi-intensity black foreground. A // newline is appended to format by default. func HiBlack(format string, a ...interface{}) { colorPrint(format, FgHiBlack, a...) } // HiRed is a convenient helper function to print with hi-intensity red foreground. A // newline is appended to format by default. func HiRed(format string, a ...interface{}) { colorPrint(format, FgHiRed, a...) } // HiGreen is a convenient helper function to print with hi-intensity green foreground. A // newline is appended to format by default. func HiGreen(format string, a ...interface{}) { colorPrint(format, FgHiGreen, a...) } // HiYellow is a convenient helper function to print with hi-intensity yellow foreground. // A newline is appended to format by default. func HiYellow(format string, a ...interface{}) { colorPrint(format, FgHiYellow, a...) } // HiBlue is a convenient helper function to print with hi-intensity blue foreground. A // newline is appended to format by default. func HiBlue(format string, a ...interface{}) { colorPrint(format, FgHiBlue, a...) } // HiMagenta is a convenient helper function to print with hi-intensity magenta foreground. // A newline is appended to format by default. func HiMagenta(format string, a ...interface{}) { colorPrint(format, FgHiMagenta, a...) } // HiCyan is a convenient helper function to print with hi-intensity cyan foreground. A // newline is appended to format by default. func HiCyan(format string, a ...interface{}) { colorPrint(format, FgHiCyan, a...) } // HiWhite is a convenient helper function to print with hi-intensity white foreground. A // newline is appended to format by default. func HiWhite(format string, a ...interface{}) { colorPrint(format, FgHiWhite, a...) } // HiBlackString is a convenient helper function to return a string with hi-intensity black // foreground. func HiBlackString(format string, a ...interface{}) string { return colorString(format, FgHiBlack, a...) } // HiRedString is a convenient helper function to return a string with hi-intensity red // foreground. func HiRedString(format string, a ...interface{}) string { return colorString(format, FgHiRed, a...) } // HiGreenString is a convenient helper function to return a string with hi-intensity green // foreground. func HiGreenString(format string, a ...interface{}) string { return colorString(format, FgHiGreen, a...) } // HiYellowString is a convenient helper function to return a string with hi-intensity yellow // foreground. func HiYellowString(format string, a ...interface{}) string { return colorString(format, FgHiYellow, a...) } // HiBlueString is a convenient helper function to return a string with hi-intensity blue // foreground. func HiBlueString(format string, a ...interface{}) string { return colorString(format, FgHiBlue, a...) } // HiMagentaString is a convenient helper function to return a string with hi-intensity magenta // foreground. func HiMagentaString(format string, a ...interface{}) string { return colorString(format, FgHiMagenta, a...) } // HiCyanString is a convenient helper function to return a string with hi-intensity cyan // foreground. func HiCyanString(format string, a ...interface{}) string { return colorString(format, FgHiCyan, a...) } // HiWhiteString is a convenient helper function to return a string with hi-intensity white // foreground. func HiWhiteString(format string, a ...interface{}) string { return colorString(format, FgHiWhite, a...) } ================================================ FILE: vendor/github.com/fatih/color/doc.go ================================================ /* Package color is an ANSI color package to output colorized or SGR defined output to the standard output. The API can be used in several way, pick one that suits you. Use simple and default helper functions with predefined foreground colors: color.Cyan("Prints text in cyan.") // a newline will be appended automatically color.Blue("Prints %s in blue.", "text") // More default foreground colors.. color.Red("We have red") color.Yellow("Yellow color too!") color.Magenta("And many others ..") // Hi-intensity colors color.HiGreen("Bright green color.") color.HiBlack("Bright black means gray..") color.HiWhite("Shiny white color!") However there are times where custom color mixes are required. Below are some examples to create custom color objects and use the print functions of each separate color object. // Create a new color object c := color.New(color.FgCyan).Add(color.Underline) c.Println("Prints cyan text with an underline.") // Or just add them to New() d := color.New(color.FgCyan, color.Bold) d.Printf("This prints bold cyan %s\n", "too!.") // Mix up foreground and background colors, create new mixes! red := color.New(color.FgRed) boldRed := red.Add(color.Bold) boldRed.Println("This will print text in bold red.") whiteBackground := red.Add(color.BgWhite) whiteBackground.Println("Red text with White background.") // Use your own io.Writer output color.New(color.FgBlue).Fprintln(myWriter, "blue color!") blue := color.New(color.FgBlue) blue.Fprint(myWriter, "This will print text in blue.") You can create PrintXxx functions to simplify even more: // Create a custom print function for convenient red := color.New(color.FgRed).PrintfFunc() red("warning") red("error: %s", err) // Mix up multiple attributes notice := color.New(color.Bold, color.FgGreen).PrintlnFunc() notice("don't forget this...") You can also FprintXxx functions to pass your own io.Writer: blue := color.New(FgBlue).FprintfFunc() blue(myWriter, "important notice: %s", stars) // Mix up with multiple attributes success := color.New(color.Bold, color.FgGreen).FprintlnFunc() success(myWriter, don't forget this...") Or create SprintXxx functions to mix strings with other non-colorized strings: yellow := New(FgYellow).SprintFunc() red := New(FgRed).SprintFunc() fmt.Printf("this is a %s and this is %s.\n", yellow("warning"), red("error")) info := New(FgWhite, BgGreen).SprintFunc() fmt.Printf("this %s rocks!\n", info("package")) Windows support is enabled by default. All Print functions work as intended. However only for color.SprintXXX functions, user should use fmt.FprintXXX and set the output to color.Output: fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS")) info := New(FgWhite, BgGreen).SprintFunc() fmt.Fprintf(color.Output, "this %s rocks!\n", info("package")) Using with existing code is possible. Just use the Set() method to set the standard output to the given parameters. That way a rewrite of an existing code is not required. // Use handy standard colors. color.Set(color.FgYellow) fmt.Println("Existing text will be now in Yellow") fmt.Printf("This one %s\n", "too") color.Unset() // don't forget to unset // You can mix up parameters color.Set(color.FgMagenta, color.Bold) defer color.Unset() // use it in your function fmt.Println("All text will be now bold magenta.") There might be a case where you want to disable color output (for example to pipe the standard output of your app to somewhere else). `Color` has support to disable colors both globally and for single color definition. For example suppose you have a CLI app and a `--no-color` bool flag. You can easily disable the color output with: var flagNoColor = flag.Bool("no-color", false, "Disable color output") if *flagNoColor { color.NoColor = true // disables colorized output } You can also disable the color by setting the NO_COLOR environment variable to any value. It also has support for single color definitions (local). You can disable/enable color output on the fly: c := color.New(color.FgCyan) c.Println("Prints cyan text") c.DisableColor() c.Println("This is printed without any color") c.EnableColor() c.Println("This prints again cyan...") */ package color ================================================ FILE: vendor/github.com/felixge/httpsnoop/.gitignore ================================================ ================================================ FILE: vendor/github.com/felixge/httpsnoop/.travis.yml ================================================ language: go go: - 1.6 - 1.7 - 1.8 ================================================ FILE: vendor/github.com/felixge/httpsnoop/LICENSE.txt ================================================ Copyright (c) 2016 Felix Geisendörfer (felix@debuggable.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/felixge/httpsnoop/Makefile ================================================ .PHONY: ci generate clean ci: clean generate go test -v ./... generate: go generate . clean: rm -rf *_generated*.go ================================================ FILE: vendor/github.com/felixge/httpsnoop/README.md ================================================ # httpsnoop Package httpsnoop provides an easy way to capture http related metrics (i.e. response time, bytes written, and http status code) from your application's http.Handlers. Doing this requires non-trivial wrapping of the http.ResponseWriter interface, which is also exposed for users interested in a more low-level API. [![GoDoc](https://godoc.org/github.com/felixge/httpsnoop?status.svg)](https://godoc.org/github.com/felixge/httpsnoop) [![Build Status](https://travis-ci.org/felixge/httpsnoop.svg?branch=master)](https://travis-ci.org/felixge/httpsnoop) ## Usage Example ```go // myH is your app's http handler, perhaps a http.ServeMux or similar. var myH http.Handler // wrappedH wraps myH in order to log every request. wrappedH := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { m := httpsnoop.CaptureMetrics(myH, w, r) log.Printf( "%s %s (code=%d dt=%s written=%d)", r.Method, r.URL, m.Code, m.Duration, m.Written, ) }) http.ListenAndServe(":8080", wrappedH) ``` ## Why this package exists Instrumenting an application's http.Handler is surprisingly difficult. However if you google for e.g. "capture ResponseWriter status code" you'll find lots of advise and code examples that suggest it to be a fairly trivial undertaking. Unfortunately everything I've seen so far has a high chance of breaking your application. The main problem is that a `http.ResponseWriter` often implements additional interfaces such as `http.Flusher`, `http.CloseNotifier`, `http.Hijacker`, `http.Pusher`, and `io.ReaderFrom`. So the naive approach of just wrapping `http.ResponseWriter` in your own struct that also implements the `http.ResponseWriter` interface will hide the additional interfaces mentioned above. This has a high change of introducing subtle bugs into any non-trivial application. Another approach I've seen people take is to return a struct that implements all of the interfaces above. However, that's also problematic, because it's difficult to fake some of these interfaces behaviors when the underlying `http.ResponseWriter` doesn't have an implementation. It's also dangerous, because an application may choose to operate differently, merely because it detects the presence of these additional interfaces. This package solves this problem by checking which additional interfaces a `http.ResponseWriter` implements, returning a wrapped version implementing the exact same set of interfaces. Additionally this package properly handles edge cases such as `WriteHeader` not being called, or called more than once, as well as concurrent calls to `http.ResponseWriter` methods, and even calls happening after the wrapped `ServeHTTP` has already returned. Unfortunately this package is not perfect either. It's possible that it is still missing some interfaces provided by the go core (let me know if you find one), and it won't work for applications adding their own interfaces into the mix. You can however use `httpsnoop.Unwrap(w)` to access the underlying `http.ResponseWriter` and type-assert the result to its other interfaces. However, hopefully the explanation above has sufficiently scared you of rolling your own solution to this problem. httpsnoop may still break your application, but at least it tries to avoid it as much as possible. Anyway, the real problem here is that smuggling additional interfaces inside `http.ResponseWriter` is a problematic design choice, but it probably goes as deep as the Go language specification itself. But that's okay, I still prefer Go over the alternatives ;). ## Performance ``` BenchmarkBaseline-8 20000 94912 ns/op BenchmarkCaptureMetrics-8 20000 95461 ns/op ``` As you can see, using `CaptureMetrics` on a vanilla http.Handler introduces an overhead of ~500 ns per http request on my machine. However, the margin of error appears to be larger than that, therefor it should be reasonable to assume that the overhead introduced by `CaptureMetrics` is absolutely negligible. ## License MIT ================================================ FILE: vendor/github.com/felixge/httpsnoop/capture_metrics.go ================================================ package httpsnoop import ( "io" "net/http" "time" ) // Metrics holds metrics captured from CaptureMetrics. type Metrics struct { // Code is the first http response code passed to the WriteHeader func of // the ResponseWriter. If no such call is made, a default code of 200 is // assumed instead. Code int // Duration is the time it took to execute the handler. Duration time.Duration // Written is the number of bytes successfully written by the Write or // ReadFrom function of the ResponseWriter. ResponseWriters may also write // data to their underlaying connection directly (e.g. headers), but those // are not tracked. Therefor the number of Written bytes will usually match // the size of the response body. Written int64 } // CaptureMetrics wraps the given hnd, executes it with the given w and r, and // returns the metrics it captured from it. func CaptureMetrics(hnd http.Handler, w http.ResponseWriter, r *http.Request) Metrics { return CaptureMetricsFn(w, func(ww http.ResponseWriter) { hnd.ServeHTTP(ww, r) }) } // CaptureMetricsFn wraps w and calls fn with the wrapped w and returns the // resulting metrics. This is very similar to CaptureMetrics (which is just // sugar on top of this func), but is a more usable interface if your // application doesn't use the Go http.Handler interface. func CaptureMetricsFn(w http.ResponseWriter, fn func(http.ResponseWriter)) Metrics { m := Metrics{Code: http.StatusOK} m.CaptureMetrics(w, fn) return m } // CaptureMetrics wraps w and calls fn with the wrapped w and updates // Metrics m with the resulting metrics. This is similar to CaptureMetricsFn, // but allows one to customize starting Metrics object. func (m *Metrics) CaptureMetrics(w http.ResponseWriter, fn func(http.ResponseWriter)) { var ( start = time.Now() headerWritten bool hooks = Hooks{ WriteHeader: func(next WriteHeaderFunc) WriteHeaderFunc { return func(code int) { next(code) if !headerWritten { m.Code = code headerWritten = true } } }, Write: func(next WriteFunc) WriteFunc { return func(p []byte) (int, error) { n, err := next(p) m.Written += int64(n) headerWritten = true return n, err } }, ReadFrom: func(next ReadFromFunc) ReadFromFunc { return func(src io.Reader) (int64, error) { n, err := next(src) headerWritten = true m.Written += n return n, err } }, } ) fn(Wrap(w, hooks)) m.Duration += time.Since(start) } ================================================ FILE: vendor/github.com/felixge/httpsnoop/docs.go ================================================ // Package httpsnoop provides an easy way to capture http related metrics (i.e. // response time, bytes written, and http status code) from your application's // http.Handlers. // // Doing this requires non-trivial wrapping of the http.ResponseWriter // interface, which is also exposed for users interested in a more low-level // API. package httpsnoop //go:generate go run codegen/main.go ================================================ FILE: vendor/github.com/felixge/httpsnoop/wrap_generated_gteq_1.8.go ================================================ // +build go1.8 // Code generated by "httpsnoop/codegen"; DO NOT EDIT package httpsnoop import ( "bufio" "io" "net" "net/http" ) // HeaderFunc is part of the http.ResponseWriter interface. type HeaderFunc func() http.Header // WriteHeaderFunc is part of the http.ResponseWriter interface. type WriteHeaderFunc func(code int) // WriteFunc is part of the http.ResponseWriter interface. type WriteFunc func(b []byte) (int, error) // FlushFunc is part of the http.Flusher interface. type FlushFunc func() // CloseNotifyFunc is part of the http.CloseNotifier interface. type CloseNotifyFunc func() <-chan bool // HijackFunc is part of the http.Hijacker interface. type HijackFunc func() (net.Conn, *bufio.ReadWriter, error) // ReadFromFunc is part of the io.ReaderFrom interface. type ReadFromFunc func(src io.Reader) (int64, error) // PushFunc is part of the http.Pusher interface. type PushFunc func(target string, opts *http.PushOptions) error // Hooks defines a set of method interceptors for methods included in // http.ResponseWriter as well as some others. You can think of them as // middleware for the function calls they target. See Wrap for more details. type Hooks struct { Header func(HeaderFunc) HeaderFunc WriteHeader func(WriteHeaderFunc) WriteHeaderFunc Write func(WriteFunc) WriteFunc Flush func(FlushFunc) FlushFunc CloseNotify func(CloseNotifyFunc) CloseNotifyFunc Hijack func(HijackFunc) HijackFunc ReadFrom func(ReadFromFunc) ReadFromFunc Push func(PushFunc) PushFunc } // Wrap returns a wrapped version of w that provides the exact same interface // as w. Specifically if w implements any combination of: // // - http.Flusher // - http.CloseNotifier // - http.Hijacker // - io.ReaderFrom // - http.Pusher // // The wrapped version will implement the exact same combination. If no hooks // are set, the wrapped version also behaves exactly as w. Hooks targeting // methods not supported by w are ignored. Any other hooks will intercept the // method they target and may modify the call's arguments and/or return values. // The CaptureMetrics implementation serves as a working example for how the // hooks can be used. func Wrap(w http.ResponseWriter, hooks Hooks) http.ResponseWriter { rw := &rw{w: w, h: hooks} _, i0 := w.(http.Flusher) _, i1 := w.(http.CloseNotifier) _, i2 := w.(http.Hijacker) _, i3 := w.(io.ReaderFrom) _, i4 := w.(http.Pusher) switch { // combination 1/32 case !i0 && !i1 && !i2 && !i3 && !i4: return struct { Unwrapper http.ResponseWriter }{rw, rw} // combination 2/32 case !i0 && !i1 && !i2 && !i3 && i4: return struct { Unwrapper http.ResponseWriter http.Pusher }{rw, rw, rw} // combination 3/32 case !i0 && !i1 && !i2 && i3 && !i4: return struct { Unwrapper http.ResponseWriter io.ReaderFrom }{rw, rw, rw} // combination 4/32 case !i0 && !i1 && !i2 && i3 && i4: return struct { Unwrapper http.ResponseWriter io.ReaderFrom http.Pusher }{rw, rw, rw, rw} // combination 5/32 case !i0 && !i1 && i2 && !i3 && !i4: return struct { Unwrapper http.ResponseWriter http.Hijacker }{rw, rw, rw} // combination 6/32 case !i0 && !i1 && i2 && !i3 && i4: return struct { Unwrapper http.ResponseWriter http.Hijacker http.Pusher }{rw, rw, rw, rw} // combination 7/32 case !i0 && !i1 && i2 && i3 && !i4: return struct { Unwrapper http.ResponseWriter http.Hijacker io.ReaderFrom }{rw, rw, rw, rw} // combination 8/32 case !i0 && !i1 && i2 && i3 && i4: return struct { Unwrapper http.ResponseWriter http.Hijacker io.ReaderFrom http.Pusher }{rw, rw, rw, rw, rw} // combination 9/32 case !i0 && i1 && !i2 && !i3 && !i4: return struct { Unwrapper http.ResponseWriter http.CloseNotifier }{rw, rw, rw} // combination 10/32 case !i0 && i1 && !i2 && !i3 && i4: return struct { Unwrapper http.ResponseWriter http.CloseNotifier http.Pusher }{rw, rw, rw, rw} // combination 11/32 case !i0 && i1 && !i2 && i3 && !i4: return struct { Unwrapper http.ResponseWriter http.CloseNotifier io.ReaderFrom }{rw, rw, rw, rw} // combination 12/32 case !i0 && i1 && !i2 && i3 && i4: return struct { Unwrapper http.ResponseWriter http.CloseNotifier io.ReaderFrom http.Pusher }{rw, rw, rw, rw, rw} // combination 13/32 case !i0 && i1 && i2 && !i3 && !i4: return struct { Unwrapper http.ResponseWriter http.CloseNotifier http.Hijacker }{rw, rw, rw, rw} // combination 14/32 case !i0 && i1 && i2 && !i3 && i4: return struct { Unwrapper http.ResponseWriter http.CloseNotifier http.Hijacker http.Pusher }{rw, rw, rw, rw, rw} // combination 15/32 case !i0 && i1 && i2 && i3 && !i4: return struct { Unwrapper http.ResponseWriter http.CloseNotifier http.Hijacker io.ReaderFrom }{rw, rw, rw, rw, rw} // combination 16/32 case !i0 && i1 && i2 && i3 && i4: return struct { Unwrapper http.ResponseWriter http.CloseNotifier http.Hijacker io.ReaderFrom http.Pusher }{rw, rw, rw, rw, rw, rw} // combination 17/32 case i0 && !i1 && !i2 && !i3 && !i4: return struct { Unwrapper http.ResponseWriter http.Flusher }{rw, rw, rw} // combination 18/32 case i0 && !i1 && !i2 && !i3 && i4: return struct { Unwrapper http.ResponseWriter http.Flusher http.Pusher }{rw, rw, rw, rw} // combination 19/32 case i0 && !i1 && !i2 && i3 && !i4: return struct { Unwrapper http.ResponseWriter http.Flusher io.ReaderFrom }{rw, rw, rw, rw} // combination 20/32 case i0 && !i1 && !i2 && i3 && i4: return struct { Unwrapper http.ResponseWriter http.Flusher io.ReaderFrom http.Pusher }{rw, rw, rw, rw, rw} // combination 21/32 case i0 && !i1 && i2 && !i3 && !i4: return struct { Unwrapper http.ResponseWriter http.Flusher http.Hijacker }{rw, rw, rw, rw} // combination 22/32 case i0 && !i1 && i2 && !i3 && i4: return struct { Unwrapper http.ResponseWriter http.Flusher http.Hijacker http.Pusher }{rw, rw, rw, rw, rw} // combination 23/32 case i0 && !i1 && i2 && i3 && !i4: return struct { Unwrapper http.ResponseWriter http.Flusher http.Hijacker io.ReaderFrom }{rw, rw, rw, rw, rw} // combination 24/32 case i0 && !i1 && i2 && i3 && i4: return struct { Unwrapper http.ResponseWriter http.Flusher http.Hijacker io.ReaderFrom http.Pusher }{rw, rw, rw, rw, rw, rw} // combination 25/32 case i0 && i1 && !i2 && !i3 && !i4: return struct { Unwrapper http.ResponseWriter http.Flusher http.CloseNotifier }{rw, rw, rw, rw} // combination 26/32 case i0 && i1 && !i2 && !i3 && i4: return struct { Unwrapper http.ResponseWriter http.Flusher http.CloseNotifier http.Pusher }{rw, rw, rw, rw, rw} // combination 27/32 case i0 && i1 && !i2 && i3 && !i4: return struct { Unwrapper http.ResponseWriter http.Flusher http.CloseNotifier io.ReaderFrom }{rw, rw, rw, rw, rw} // combination 28/32 case i0 && i1 && !i2 && i3 && i4: return struct { Unwrapper http.ResponseWriter http.Flusher http.CloseNotifier io.ReaderFrom http.Pusher }{rw, rw, rw, rw, rw, rw} // combination 29/32 case i0 && i1 && i2 && !i3 && !i4: return struct { Unwrapper http.ResponseWriter http.Flusher http.CloseNotifier http.Hijacker }{rw, rw, rw, rw, rw} // combination 30/32 case i0 && i1 && i2 && !i3 && i4: return struct { Unwrapper http.ResponseWriter http.Flusher http.CloseNotifier http.Hijacker http.Pusher }{rw, rw, rw, rw, rw, rw} // combination 31/32 case i0 && i1 && i2 && i3 && !i4: return struct { Unwrapper http.ResponseWriter http.Flusher http.CloseNotifier http.Hijacker io.ReaderFrom }{rw, rw, rw, rw, rw, rw} // combination 32/32 case i0 && i1 && i2 && i3 && i4: return struct { Unwrapper http.ResponseWriter http.Flusher http.CloseNotifier http.Hijacker io.ReaderFrom http.Pusher }{rw, rw, rw, rw, rw, rw, rw} } panic("unreachable") } type rw struct { w http.ResponseWriter h Hooks } func (w *rw) Unwrap() http.ResponseWriter { return w.w } func (w *rw) Header() http.Header { f := w.w.(http.ResponseWriter).Header if w.h.Header != nil { f = w.h.Header(f) } return f() } func (w *rw) WriteHeader(code int) { f := w.w.(http.ResponseWriter).WriteHeader if w.h.WriteHeader != nil { f = w.h.WriteHeader(f) } f(code) } func (w *rw) Write(b []byte) (int, error) { f := w.w.(http.ResponseWriter).Write if w.h.Write != nil { f = w.h.Write(f) } return f(b) } func (w *rw) Flush() { f := w.w.(http.Flusher).Flush if w.h.Flush != nil { f = w.h.Flush(f) } f() } func (w *rw) CloseNotify() <-chan bool { f := w.w.(http.CloseNotifier).CloseNotify if w.h.CloseNotify != nil { f = w.h.CloseNotify(f) } return f() } func (w *rw) Hijack() (net.Conn, *bufio.ReadWriter, error) { f := w.w.(http.Hijacker).Hijack if w.h.Hijack != nil { f = w.h.Hijack(f) } return f() } func (w *rw) ReadFrom(src io.Reader) (int64, error) { f := w.w.(io.ReaderFrom).ReadFrom if w.h.ReadFrom != nil { f = w.h.ReadFrom(f) } return f(src) } func (w *rw) Push(target string, opts *http.PushOptions) error { f := w.w.(http.Pusher).Push if w.h.Push != nil { f = w.h.Push(f) } return f(target, opts) } type Unwrapper interface { Unwrap() http.ResponseWriter } // Unwrap returns the underlying http.ResponseWriter from within zero or more // layers of httpsnoop wrappers. func Unwrap(w http.ResponseWriter) http.ResponseWriter { if rw, ok := w.(Unwrapper); ok { // recurse until rw.Unwrap() returns a non-Unwrapper return Unwrap(rw.Unwrap()) } else { return w } } ================================================ FILE: vendor/github.com/felixge/httpsnoop/wrap_generated_lt_1.8.go ================================================ // +build !go1.8 // Code generated by "httpsnoop/codegen"; DO NOT EDIT package httpsnoop import ( "bufio" "io" "net" "net/http" ) // HeaderFunc is part of the http.ResponseWriter interface. type HeaderFunc func() http.Header // WriteHeaderFunc is part of the http.ResponseWriter interface. type WriteHeaderFunc func(code int) // WriteFunc is part of the http.ResponseWriter interface. type WriteFunc func(b []byte) (int, error) // FlushFunc is part of the http.Flusher interface. type FlushFunc func() // CloseNotifyFunc is part of the http.CloseNotifier interface. type CloseNotifyFunc func() <-chan bool // HijackFunc is part of the http.Hijacker interface. type HijackFunc func() (net.Conn, *bufio.ReadWriter, error) // ReadFromFunc is part of the io.ReaderFrom interface. type ReadFromFunc func(src io.Reader) (int64, error) // Hooks defines a set of method interceptors for methods included in // http.ResponseWriter as well as some others. You can think of them as // middleware for the function calls they target. See Wrap for more details. type Hooks struct { Header func(HeaderFunc) HeaderFunc WriteHeader func(WriteHeaderFunc) WriteHeaderFunc Write func(WriteFunc) WriteFunc Flush func(FlushFunc) FlushFunc CloseNotify func(CloseNotifyFunc) CloseNotifyFunc Hijack func(HijackFunc) HijackFunc ReadFrom func(ReadFromFunc) ReadFromFunc } // Wrap returns a wrapped version of w that provides the exact same interface // as w. Specifically if w implements any combination of: // // - http.Flusher // - http.CloseNotifier // - http.Hijacker // - io.ReaderFrom // // The wrapped version will implement the exact same combination. If no hooks // are set, the wrapped version also behaves exactly as w. Hooks targeting // methods not supported by w are ignored. Any other hooks will intercept the // method they target and may modify the call's arguments and/or return values. // The CaptureMetrics implementation serves as a working example for how the // hooks can be used. func Wrap(w http.ResponseWriter, hooks Hooks) http.ResponseWriter { rw := &rw{w: w, h: hooks} _, i0 := w.(http.Flusher) _, i1 := w.(http.CloseNotifier) _, i2 := w.(http.Hijacker) _, i3 := w.(io.ReaderFrom) switch { // combination 1/16 case !i0 && !i1 && !i2 && !i3: return struct { Unwrapper http.ResponseWriter }{rw, rw} // combination 2/16 case !i0 && !i1 && !i2 && i3: return struct { Unwrapper http.ResponseWriter io.ReaderFrom }{rw, rw, rw} // combination 3/16 case !i0 && !i1 && i2 && !i3: return struct { Unwrapper http.ResponseWriter http.Hijacker }{rw, rw, rw} // combination 4/16 case !i0 && !i1 && i2 && i3: return struct { Unwrapper http.ResponseWriter http.Hijacker io.ReaderFrom }{rw, rw, rw, rw} // combination 5/16 case !i0 && i1 && !i2 && !i3: return struct { Unwrapper http.ResponseWriter http.CloseNotifier }{rw, rw, rw} // combination 6/16 case !i0 && i1 && !i2 && i3: return struct { Unwrapper http.ResponseWriter http.CloseNotifier io.ReaderFrom }{rw, rw, rw, rw} // combination 7/16 case !i0 && i1 && i2 && !i3: return struct { Unwrapper http.ResponseWriter http.CloseNotifier http.Hijacker }{rw, rw, rw, rw} // combination 8/16 case !i0 && i1 && i2 && i3: return struct { Unwrapper http.ResponseWriter http.CloseNotifier http.Hijacker io.ReaderFrom }{rw, rw, rw, rw, rw} // combination 9/16 case i0 && !i1 && !i2 && !i3: return struct { Unwrapper http.ResponseWriter http.Flusher }{rw, rw, rw} // combination 10/16 case i0 && !i1 && !i2 && i3: return struct { Unwrapper http.ResponseWriter http.Flusher io.ReaderFrom }{rw, rw, rw, rw} // combination 11/16 case i0 && !i1 && i2 && !i3: return struct { Unwrapper http.ResponseWriter http.Flusher http.Hijacker }{rw, rw, rw, rw} // combination 12/16 case i0 && !i1 && i2 && i3: return struct { Unwrapper http.ResponseWriter http.Flusher http.Hijacker io.ReaderFrom }{rw, rw, rw, rw, rw} // combination 13/16 case i0 && i1 && !i2 && !i3: return struct { Unwrapper http.ResponseWriter http.Flusher http.CloseNotifier }{rw, rw, rw, rw} // combination 14/16 case i0 && i1 && !i2 && i3: return struct { Unwrapper http.ResponseWriter http.Flusher http.CloseNotifier io.ReaderFrom }{rw, rw, rw, rw, rw} // combination 15/16 case i0 && i1 && i2 && !i3: return struct { Unwrapper http.ResponseWriter http.Flusher http.CloseNotifier http.Hijacker }{rw, rw, rw, rw, rw} // combination 16/16 case i0 && i1 && i2 && i3: return struct { Unwrapper http.ResponseWriter http.Flusher http.CloseNotifier http.Hijacker io.ReaderFrom }{rw, rw, rw, rw, rw, rw} } panic("unreachable") } type rw struct { w http.ResponseWriter h Hooks } func (w *rw) Unwrap() http.ResponseWriter { return w.w } func (w *rw) Header() http.Header { f := w.w.(http.ResponseWriter).Header if w.h.Header != nil { f = w.h.Header(f) } return f() } func (w *rw) WriteHeader(code int) { f := w.w.(http.ResponseWriter).WriteHeader if w.h.WriteHeader != nil { f = w.h.WriteHeader(f) } f(code) } func (w *rw) Write(b []byte) (int, error) { f := w.w.(http.ResponseWriter).Write if w.h.Write != nil { f = w.h.Write(f) } return f(b) } func (w *rw) Flush() { f := w.w.(http.Flusher).Flush if w.h.Flush != nil { f = w.h.Flush(f) } f() } func (w *rw) CloseNotify() <-chan bool { f := w.w.(http.CloseNotifier).CloseNotify if w.h.CloseNotify != nil { f = w.h.CloseNotify(f) } return f() } func (w *rw) Hijack() (net.Conn, *bufio.ReadWriter, error) { f := w.w.(http.Hijacker).Hijack if w.h.Hijack != nil { f = w.h.Hijack(f) } return f() } func (w *rw) ReadFrom(src io.Reader) (int64, error) { f := w.w.(io.ReaderFrom).ReadFrom if w.h.ReadFrom != nil { f = w.h.ReadFrom(f) } return f(src) } type Unwrapper interface { Unwrap() http.ResponseWriter } // Unwrap returns the underlying http.ResponseWriter from within zero or more // layers of httpsnoop wrappers. func Unwrap(w http.ResponseWriter) http.ResponseWriter { if rw, ok := w.(Unwrapper); ok { // recurse until rw.Unwrap() returns a non-Unwrapper return Unwrap(rw.Unwrap()) } else { return w } } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/.gitattributes ================================================ * text=auto eol=lf ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/.gitignore ================================================ # temporary symlink for testing testing/data/symlink ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/.golangci.yaml ================================================ run: deadline: 5m linters: disable-all: true enable: - gofumpt - goimports ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/DOCKER-LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ You can find the Docker license at the following link: https://raw.githubusercontent.com/docker/docker/HEAD/LICENSE ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/LICENSE ================================================ Copyright (c) go-dockerclient authors All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/Makefile ================================================ ifeq "$(strip $(shell go env GOARCH))" "amd64" RACE_FLAG := -race endif .PHONY: test test: pretest gotest .PHONY: golangci-lint golangci-lint: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest golangci-lint run .PHONY: staticcheck staticcheck: go install honnef.co/go/tools/cmd/staticcheck@master staticcheck ./... .PHONY: lint lint: golangci-lint staticcheck .PHONY: pretest pretest: lint .PHONY: gotest gotest: go test $(RACE_FLAG) -vet all ./... .PHONY: integration integration: go test -tags docker_integration -run TestIntegration -v ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/README.md ================================================ # go-dockerclient [![Build Status](https://github.com/fsouza/go-dockerclient/workflows/Build/badge.svg)](https://github.com/fsouza/go-dockerclient/actions?query=branch:main+workflow:Build) [![GoDoc](https://img.shields.io/badge/api-Godoc-blue.svg?style=flat-square)](https://pkg.go.dev/github.com/fsouza/go-dockerclient) This package presents a client for the Docker remote API. It also provides support for the extensions in the [Swarm API](https://docs.docker.com/swarm/swarm-api/). This package also provides support for docker's network API, which is a simple passthrough to the libnetwork remote API. For more details, check the [remote API documentation](https://docs.docker.com/engine/api/latest/). ## Difference between go-dockerclient and the official SDK Link for the official SDK: https://docs.docker.com/develop/sdk/ go-dockerclient was created before Docker had an official Go SDK and is still maintained and active because it's still used out there. New features in the Docker API do not get automatically implemented here: it's based on demand, if someone wants it, they can file an issue or a PR and the feature may get implemented/merged. For new projects, using the official SDK is probably more appropriate as go-dockerclient lags behind the official SDK. ## Example ```go package main import ( "fmt" docker "github.com/fsouza/go-dockerclient" ) func main() { client, err := docker.NewClientFromEnv() if err != nil { panic(err) } imgs, err := client.ListImages(docker.ListImagesOptions{All: false}) if err != nil { panic(err) } for _, img := range imgs { fmt.Println("ID: ", img.ID) fmt.Println("RepoTags: ", img.RepoTags) fmt.Println("Created: ", img.Created) fmt.Println("Size: ", img.Size) fmt.Println("VirtualSize: ", img.VirtualSize) fmt.Println("ParentId: ", img.ParentID) } } ``` ## Using with TLS In order to instantiate the client for a TLS-enabled daemon, you should use NewTLSClient, passing the endpoint and path for key and certificates as parameters. ```go package main import ( "fmt" docker "github.com/fsouza/go-dockerclient" ) func main() { const endpoint = "tcp://[ip]:[port]" path := os.Getenv("DOCKER_CERT_PATH") ca := fmt.Sprintf("%s/ca.pem", path) cert := fmt.Sprintf("%s/cert.pem", path) key := fmt.Sprintf("%s/key.pem", path) client, _ := docker.NewTLSClient(endpoint, cert, key, ca) // use client } ``` If using [docker-machine](https://docs.docker.com/machine/), or another application that exports environment variables `DOCKER_HOST`, `DOCKER_TLS_VERIFY`, `DOCKER_CERT_PATH`, `DOCKER_API_VERSION`, you can use NewClientFromEnv. ```go package main import ( "fmt" docker "github.com/fsouza/go-dockerclient" ) func main() { client, err := docker.NewClientFromEnv() if err != nil { // handle err } // use client } ``` See the documentation for more details. ## Developing All development commands can be seen in the [Makefile](Makefile). Committed code must pass: * [golangci-lint](https://github.com/golangci/golangci-lint) * [go test](https://golang.org/cmd/go/#hdr-Test_packages) * [staticcheck](https://staticcheck.io/) Running ``make test`` will run all checks, as well as install any required dependencies. ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/auth.go ================================================ // Copyright 2015 go-dockerclient authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package docker import ( "bytes" "context" "encoding/base64" "encoding/json" "errors" "io" "net/http" "os" "os/exec" "path" "strings" ) // ErrCannotParseDockercfg is the error returned by NewAuthConfigurations when the dockercfg cannot be parsed. var ErrCannotParseDockercfg = errors.New("failed to read authentication from dockercfg") // AuthConfiguration represents authentication options to use in the PushImage // method. It represents the authentication in the Docker index server. type AuthConfiguration struct { Username string `json:"username,omitempty"` Password string `json:"password,omitempty"` Email string `json:"email,omitempty"` ServerAddress string `json:"serveraddress,omitempty"` // IdentityToken can be supplied with the identitytoken response of the AuthCheck call // see https://pkg.go.dev/github.com/docker/docker/api/types?tab=doc#AuthConfig // It can be used in place of password not in conjunction with it IdentityToken string `json:"identitytoken,omitempty"` // RegistryToken can be supplied with the registrytoken RegistryToken string `json:"registrytoken,omitempty"` } func (c AuthConfiguration) isEmpty() bool { return c == AuthConfiguration{} } func (c AuthConfiguration) headerKey() string { return "X-Registry-Auth" } // AuthConfigurations represents authentication options to use for the // PushImage method accommodating the new X-Registry-Config header type AuthConfigurations struct { Configs map[string]AuthConfiguration `json:"configs"` } func (c AuthConfigurations) isEmpty() bool { return len(c.Configs) == 0 } func (AuthConfigurations) headerKey() string { return "X-Registry-Config" } // merge updates the configuration. If a key is defined in both maps, the one // in c.Configs takes precedence. func (c *AuthConfigurations) merge(other AuthConfigurations) { for k, v := range other.Configs { if c.Configs == nil { c.Configs = make(map[string]AuthConfiguration) } if _, ok := c.Configs[k]; !ok { c.Configs[k] = v } } } // AuthConfigurations119 is used to serialize a set of AuthConfigurations // for Docker API >= 1.19. type AuthConfigurations119 map[string]AuthConfiguration func (c AuthConfigurations119) isEmpty() bool { return len(c) == 0 } func (c AuthConfigurations119) headerKey() string { return "X-Registry-Config" } // dockerConfig represents a registry authentation configuration from the // .dockercfg file. type dockerConfig struct { Auth string `json:"auth"` Email string `json:"email"` IdentityToken string `json:"identitytoken"` RegistryToken string `json:"registrytoken"` } // NewAuthConfigurationsFromFile returns AuthConfigurations from a path containing JSON // in the same format as the .dockercfg file. func NewAuthConfigurationsFromFile(path string) (*AuthConfigurations, error) { r, err := os.Open(path) if err != nil { return nil, err } return NewAuthConfigurations(r) } func cfgPaths(dockerConfigEnv string, homeEnv string) []string { if dockerConfigEnv != "" { return []string{ path.Join(dockerConfigEnv, "plaintext-passwords.json"), path.Join(dockerConfigEnv, "config.json"), } } if homeEnv != "" { return []string{ path.Join(homeEnv, ".docker", "plaintext-passwords.json"), path.Join(homeEnv, ".docker", "config.json"), path.Join(homeEnv, ".dockercfg"), } } return nil } // NewAuthConfigurationsFromDockerCfg returns AuthConfigurations from system // config files. The following files are checked in the order listed: // // If the environment variable DOCKER_CONFIG is set to a non-empty string: // // - $DOCKER_CONFIG/plaintext-passwords.json // - $DOCKER_CONFIG/config.json // // Otherwise, it looks for files in the $HOME directory and the legacy // location: // // - $HOME/.docker/plaintext-passwords.json // - $HOME/.docker/config.json // - $HOME/.dockercfg func NewAuthConfigurationsFromDockerCfg() (*AuthConfigurations, error) { pathsToTry := cfgPaths(os.Getenv("DOCKER_CONFIG"), os.Getenv("HOME")) if len(pathsToTry) < 1 { return nil, errors.New("no docker configuration found") } return newAuthConfigurationsFromDockerCfg(pathsToTry) } func newAuthConfigurationsFromDockerCfg(pathsToTry []string) (*AuthConfigurations, error) { var result *AuthConfigurations var auths *AuthConfigurations var err error for _, path := range pathsToTry { auths, err = NewAuthConfigurationsFromFile(path) if err != nil { continue } if result == nil { result = auths } else { result.merge(*auths) } } if result != nil { return result, nil } return result, err } // NewAuthConfigurations returns AuthConfigurations from a JSON encoded string in the // same format as the .dockercfg file. func NewAuthConfigurations(r io.Reader) (*AuthConfigurations, error) { var auth *AuthConfigurations confs, err := parseDockerConfig(r) if err != nil { return nil, err } auth, err = authConfigs(confs) if err != nil { return nil, err } return auth, nil } func parseDockerConfig(r io.Reader) (map[string]dockerConfig, error) { buf := new(bytes.Buffer) buf.ReadFrom(r) byteData := buf.Bytes() confsWrapper := struct { Auths map[string]dockerConfig `json:"auths"` }{} if err := json.Unmarshal(byteData, &confsWrapper); err == nil { if len(confsWrapper.Auths) > 0 { return confsWrapper.Auths, nil } } var confs map[string]dockerConfig if err := json.Unmarshal(byteData, &confs); err != nil { return nil, err } return confs, nil } // authConfigs converts a dockerConfigs map to a AuthConfigurations object. func authConfigs(confs map[string]dockerConfig) (*AuthConfigurations, error) { c := &AuthConfigurations{ Configs: make(map[string]AuthConfiguration), } for reg, conf := range confs { if conf.Auth == "" { continue } // support both padded and unpadded encoding data, err := base64.StdEncoding.DecodeString(conf.Auth) if err != nil { data, err = base64.StdEncoding.WithPadding(base64.NoPadding).DecodeString(conf.Auth) } if err != nil { return nil, errors.New("error decoding plaintext credentials") } userpass := strings.SplitN(string(data), ":", 2) if len(userpass) != 2 { return nil, ErrCannotParseDockercfg } authConfig := AuthConfiguration{ Email: conf.Email, Username: userpass[0], Password: userpass[1], ServerAddress: reg, } // if identitytoken provided then zero the password and set it if conf.IdentityToken != "" { authConfig.Password = "" authConfig.IdentityToken = conf.IdentityToken } // if registrytoken provided then zero the password and set it if conf.RegistryToken != "" { authConfig.Password = "" authConfig.RegistryToken = conf.RegistryToken } c.Configs[reg] = authConfig } return c, nil } // AuthStatus returns the authentication status for Docker API versions >= 1.23. type AuthStatus struct { Status string `json:"Status,omitempty" yaml:"Status,omitempty" toml:"Status,omitempty"` IdentityToken string `json:"IdentityToken,omitempty" yaml:"IdentityToken,omitempty" toml:"IdentityToken,omitempty"` } // AuthCheck validates the given credentials. It returns nil if successful. // // For Docker API versions >= 1.23, the AuthStatus struct will be populated, otherwise it will be empty.` // // See https://goo.gl/6nsZkH for more details. func (c *Client) AuthCheck(conf *AuthConfiguration) (AuthStatus, error) { return c.AuthCheckWithContext(conf, context.TODO()) } // AuthCheckWithContext validates the given credentials. It returns nil if successful. The context object // can be used to cancel the request. // // For Docker API versions >= 1.23, the AuthStatus struct will be populated, otherwise it will be empty. // // See https://goo.gl/6nsZkH for more details. func (c *Client) AuthCheckWithContext(conf *AuthConfiguration, ctx context.Context) (AuthStatus, error) { var authStatus AuthStatus if conf == nil { return authStatus, errors.New("conf is nil") } resp, err := c.do(http.MethodPost, "/auth", doOptions{data: conf, context: ctx}) if err != nil { return authStatus, err } defer resp.Body.Close() data, err := io.ReadAll(resp.Body) if err != nil { return authStatus, err } if len(data) == 0 { return authStatus, nil } if err := json.Unmarshal(data, &authStatus); err != nil { return authStatus, err } return authStatus, nil } // helperCredentials represents credentials commit from an helper type helperCredentials struct { Username string `json:"Username,omitempty"` Secret string `json:"Secret,omitempty"` } // NewAuthConfigurationsFromCredsHelpers returns AuthConfigurations from // installed credentials helpers func NewAuthConfigurationsFromCredsHelpers(registry string) (*AuthConfiguration, error) { // Load docker configuration file in order to find a possible helper provider pathsToTry := cfgPaths(os.Getenv("DOCKER_CONFIG"), os.Getenv("HOME")) if len(pathsToTry) < 1 { return nil, errors.New("no docker configuration found") } provider, err := getHelperProviderFromDockerCfg(pathsToTry, registry) if err != nil { return nil, err } c, err := getCredentialsFromHelper(provider, registry) if err != nil { return nil, err } creds := new(AuthConfiguration) creds.Username = c.Username creds.Password = c.Secret return creds, nil } func getHelperProviderFromDockerCfg(pathsToTry []string, registry string) (string, error) { for _, path := range pathsToTry { content, err := os.ReadFile(path) if err != nil { // if we can't read the file keep going continue } provider, err := parseCredsDockerConfig(content, registry) if err != nil { continue } if provider != "" { return provider, nil } } return "", errors.New("no docker credentials provider found") } func parseCredsDockerConfig(config []byte, registry string) (string, error) { creds := struct { CredsStore string `json:"credsStore,omitempty"` CredHelpers map[string]string `json:"credHelpers,omitempty"` }{} err := json.Unmarshal(config, &creds) if err != nil { return "", err } provider, ok := creds.CredHelpers[registry] if ok { return provider, nil } return creds.CredsStore, nil } // Run and parse the found credential helper func getCredentialsFromHelper(provider string, registry string) (*helperCredentials, error) { helpercreds, err := runDockerCredentialsHelper(provider, registry) if err != nil { return nil, err } c := new(helperCredentials) err = json.Unmarshal(helpercreds, c) if err != nil { return nil, err } return c, nil } func runDockerCredentialsHelper(provider string, registry string) ([]byte, error) { cmd := exec.Command("docker-credential-"+provider, "get") var stdout bytes.Buffer cmd.Stdin = bytes.NewBuffer([]byte(registry)) cmd.Stdout = &stdout err := cmd.Run() if err != nil { return nil, err } return stdout.Bytes(), nil } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/change.go ================================================ // Copyright 2014 go-dockerclient authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package docker import "fmt" // ChangeType is a type for constants indicating the type of change // in a container type ChangeType int const ( // ChangeModify is the ChangeType for container modifications ChangeModify ChangeType = iota // ChangeAdd is the ChangeType for additions to a container ChangeAdd // ChangeDelete is the ChangeType for deletions from a container ChangeDelete ) // Change represents a change in a container. // // See https://goo.gl/Wo0JJp for more details. type Change struct { Path string Kind ChangeType } func (change *Change) String() string { var kind string switch change.Kind { case ChangeModify: kind = "C" case ChangeAdd: kind = "A" case ChangeDelete: kind = "D" } return fmt.Sprintf("%s %s", kind, change.Path) } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/client.go ================================================ // Copyright 2013 go-dockerclient authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package docker provides a client for the Docker remote API. // // See https://goo.gl/o2v3rk for more details on the remote API. package docker import ( "bufio" "bytes" "context" "crypto/tls" "crypto/x509" "encoding/json" "errors" "fmt" "io" "net" "net/http" "net/http/httputil" "net/url" "os" "path/filepath" "reflect" "runtime" "strconv" "strings" "sync/atomic" "time" "github.com/docker/docker/pkg/homedir" "github.com/docker/docker/pkg/jsonmessage" "github.com/docker/docker/pkg/stdcopy" ) const ( userAgent = "go-dockerclient" unixProtocol = "unix" namedPipeProtocol = "npipe" ) var ( // ErrInvalidEndpoint is returned when the endpoint is not a valid HTTP URL. ErrInvalidEndpoint = errors.New("invalid endpoint") // ErrConnectionRefused is returned when the client cannot connect to the given endpoint. ErrConnectionRefused = errors.New("cannot connect to Docker endpoint") // ErrInactivityTimeout is returned when a streamable call has been inactive for some time. ErrInactivityTimeout = errors.New("inactivity time exceeded timeout") apiVersion112, _ = NewAPIVersion("1.12") apiVersion118, _ = NewAPIVersion("1.18") apiVersion119, _ = NewAPIVersion("1.19") apiVersion121, _ = NewAPIVersion("1.21") apiVersion124, _ = NewAPIVersion("1.24") apiVersion125, _ = NewAPIVersion("1.25") apiVersion135, _ = NewAPIVersion("1.35") ) // APIVersion is an internal representation of a version of the Remote API. type APIVersion []int // NewAPIVersion returns an instance of APIVersion for the given string. // // The given string must be in the form .., where , // and are integer numbers. func NewAPIVersion(input string) (APIVersion, error) { if !strings.Contains(input, ".") { return nil, fmt.Errorf("unable to parse version %q", input) } raw := strings.Split(input, "-") arr := strings.Split(raw[0], ".") ret := make(APIVersion, len(arr)) var err error for i, val := range arr { ret[i], err = strconv.Atoi(val) if err != nil { return nil, fmt.Errorf("unable to parse version %q: %q is not an integer", input, val) } } return ret, nil } func (version APIVersion) String() string { parts := make([]string, len(version)) for i, val := range version { parts[i] = strconv.Itoa(val) } return strings.Join(parts, ".") } // LessThan is a function for comparing APIVersion structs. func (version APIVersion) LessThan(other APIVersion) bool { return version.compare(other) < 0 } // LessThanOrEqualTo is a function for comparing APIVersion structs. func (version APIVersion) LessThanOrEqualTo(other APIVersion) bool { return version.compare(other) <= 0 } // GreaterThan is a function for comparing APIVersion structs. func (version APIVersion) GreaterThan(other APIVersion) bool { return version.compare(other) > 0 } // GreaterThanOrEqualTo is a function for comparing APIVersion structs. func (version APIVersion) GreaterThanOrEqualTo(other APIVersion) bool { return version.compare(other) >= 0 } func (version APIVersion) compare(other APIVersion) int { for i, v := range version { if i <= len(other)-1 { otherVersion := other[i] if v < otherVersion { return -1 } else if v > otherVersion { return 1 } } } if len(version) > len(other) { return 1 } if len(version) < len(other) { return -1 } return 0 } // Client is the basic type of this package. It provides methods for // interaction with the API. type Client struct { SkipServerVersionCheck bool HTTPClient *http.Client TLSConfig *tls.Config Dialer Dialer endpoint string endpointURL *url.URL eventMonitor *eventMonitoringState requestedAPIVersion APIVersion serverAPIVersion APIVersion expectedAPIVersion APIVersion } // Dialer is an interface that allows network connections to be dialed // (net.Dialer fulfills this interface) and named pipes (a shim using // winio.DialPipe) type Dialer interface { Dial(network, address string) (net.Conn, error) } // NewClient returns a Client instance ready for communication with the given // server endpoint. It will use the latest remote API version available in the // server. func NewClient(endpoint string) (*Client, error) { client, err := NewVersionedClient(endpoint, "") if err != nil { return nil, err } client.SkipServerVersionCheck = true return client, nil } // NewTLSClient returns a Client instance ready for TLS communications with the givens // server endpoint, key and certificates . It will use the latest remote API version // available in the server. func NewTLSClient(endpoint string, cert, key, ca string) (*Client, error) { client, err := NewVersionedTLSClient(endpoint, cert, key, ca, "") if err != nil { return nil, err } client.SkipServerVersionCheck = true return client, nil } // NewTLSClientFromBytes returns a Client instance ready for TLS communications with the givens // server endpoint, key and certificates (passed inline to the function as opposed to being // read from a local file). It will use the latest remote API version available in the server. func NewTLSClientFromBytes(endpoint string, certPEMBlock, keyPEMBlock, caPEMCert []byte) (*Client, error) { client, err := NewVersionedTLSClientFromBytes(endpoint, certPEMBlock, keyPEMBlock, caPEMCert, "") if err != nil { return nil, err } client.SkipServerVersionCheck = true return client, nil } // NewVersionedClient returns a Client instance ready for communication with // the given server endpoint, using a specific remote API version. func NewVersionedClient(endpoint string, apiVersionString string) (*Client, error) { u, err := parseEndpoint(endpoint, false) if err != nil { return nil, err } var requestedAPIVersion APIVersion if strings.Contains(apiVersionString, ".") { requestedAPIVersion, err = NewAPIVersion(apiVersionString) if err != nil { return nil, err } } c := &Client{ HTTPClient: defaultClient(), Dialer: &net.Dialer{}, endpoint: endpoint, endpointURL: u, eventMonitor: new(eventMonitoringState), requestedAPIVersion: requestedAPIVersion, } c.initializeNativeClient(defaultTransport) return c, nil } // WithTransport replaces underlying HTTP client of Docker Client by accepting // a function that returns pointer to a transport object. func (c *Client) WithTransport(trFunc func() *http.Transport) { c.initializeNativeClient(trFunc) } // NewVersionnedTLSClient is like NewVersionedClient, but with ann extra n. // // Deprecated: Use NewVersionedTLSClient instead. func NewVersionnedTLSClient(endpoint string, cert, key, ca, apiVersionString string) (*Client, error) { return NewVersionedTLSClient(endpoint, cert, key, ca, apiVersionString) } // NewVersionedTLSClient returns a Client instance ready for TLS communications with the givens // server endpoint, key and certificates, using a specific remote API version. func NewVersionedTLSClient(endpoint string, cert, key, ca, apiVersionString string) (*Client, error) { var certPEMBlock []byte var keyPEMBlock []byte var caPEMCert []byte if _, err := os.Stat(cert); !os.IsNotExist(err) { certPEMBlock, err = os.ReadFile(cert) if err != nil { return nil, err } } if _, err := os.Stat(key); !os.IsNotExist(err) { keyPEMBlock, err = os.ReadFile(key) if err != nil { return nil, err } } if _, err := os.Stat(ca); !os.IsNotExist(err) { caPEMCert, err = os.ReadFile(ca) if err != nil { return nil, err } } return NewVersionedTLSClientFromBytes(endpoint, certPEMBlock, keyPEMBlock, caPEMCert, apiVersionString) } // NewClientFromEnv returns a Client instance ready for communication created from // Docker's default logic for the environment variables DOCKER_HOST, DOCKER_TLS_VERIFY, DOCKER_CERT_PATH, // and DOCKER_API_VERSION. // // See https://github.com/docker/docker/blob/1f963af697e8df3a78217f6fdbf67b8123a7db94/docker/docker.go#L68. // See https://github.com/docker/compose/blob/81707ef1ad94403789166d2fe042c8a718a4c748/compose/cli/docker_client.py#L7. // See https://github.com/moby/moby/blob/28d7dba41d0c0d9c7f0dafcc79d3c59f2b3f5dc3/client/options.go#L51 func NewClientFromEnv() (*Client, error) { apiVersionString := os.Getenv("DOCKER_API_VERSION") client, err := NewVersionedClientFromEnv(apiVersionString) if err != nil { return nil, err } client.SkipServerVersionCheck = apiVersionString == "" return client, nil } // NewVersionedClientFromEnv returns a Client instance ready for TLS communications created from // Docker's default logic for the environment variables DOCKER_HOST, DOCKER_TLS_VERIFY, and DOCKER_CERT_PATH, // and using a specific remote API version. // // See https://github.com/docker/docker/blob/1f963af697e8df3a78217f6fdbf67b8123a7db94/docker/docker.go#L68. // See https://github.com/docker/compose/blob/81707ef1ad94403789166d2fe042c8a718a4c748/compose/cli/docker_client.py#L7. func NewVersionedClientFromEnv(apiVersionString string) (*Client, error) { dockerEnv, err := getDockerEnv() if err != nil { return nil, err } dockerHost := dockerEnv.dockerHost if dockerEnv.dockerTLSVerify { parts := strings.SplitN(dockerEnv.dockerHost, "://", 2) if len(parts) != 2 { return nil, fmt.Errorf("could not split %s into two parts by ://", dockerHost) } cert := filepath.Join(dockerEnv.dockerCertPath, "cert.pem") key := filepath.Join(dockerEnv.dockerCertPath, "key.pem") ca := filepath.Join(dockerEnv.dockerCertPath, "ca.pem") return NewVersionedTLSClient(dockerEnv.dockerHost, cert, key, ca, apiVersionString) } return NewVersionedClient(dockerEnv.dockerHost, apiVersionString) } // NewVersionedTLSClientFromBytes returns a Client instance ready for TLS communications with the givens // server endpoint, key and certificates (passed inline to the function as opposed to being // read from a local file), using a specific remote API version. func NewVersionedTLSClientFromBytes(endpoint string, certPEMBlock, keyPEMBlock, caPEMCert []byte, apiVersionString string) (*Client, error) { u, err := parseEndpoint(endpoint, true) if err != nil { return nil, err } var requestedAPIVersion APIVersion if strings.Contains(apiVersionString, ".") { requestedAPIVersion, err = NewAPIVersion(apiVersionString) if err != nil { return nil, err } } tlsConfig := &tls.Config{MinVersion: tls.VersionTLS12} if certPEMBlock != nil && keyPEMBlock != nil { tlsCert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock) if err != nil { return nil, err } tlsConfig.Certificates = []tls.Certificate{tlsCert} } if caPEMCert == nil { tlsConfig.InsecureSkipVerify = true } else { caPool := x509.NewCertPool() if !caPool.AppendCertsFromPEM(caPEMCert) { return nil, errors.New("could not add RootCA pem") } tlsConfig.RootCAs = caPool } tr := defaultTransport() tr.TLSClientConfig = tlsConfig if err != nil { return nil, err } c := &Client{ HTTPClient: &http.Client{Transport: tr}, TLSConfig: tlsConfig, Dialer: &net.Dialer{}, endpoint: endpoint, endpointURL: u, eventMonitor: new(eventMonitoringState), requestedAPIVersion: requestedAPIVersion, } c.initializeNativeClient(defaultTransport) return c, nil } // SetTimeout takes a timeout and applies it to the HTTPClient. It should not // be called concurrently with any other Client methods. func (c *Client) SetTimeout(t time.Duration) { if c.HTTPClient != nil { c.HTTPClient.Timeout = t } } func (c *Client) checkAPIVersion() error { serverAPIVersionString, err := c.getServerAPIVersionString() if err != nil { return err } c.serverAPIVersion, err = NewAPIVersion(serverAPIVersionString) if err != nil { return err } if c.requestedAPIVersion == nil { c.expectedAPIVersion = c.serverAPIVersion } else { c.expectedAPIVersion = c.requestedAPIVersion } return nil } // Endpoint returns the current endpoint. It's useful for getting the endpoint // when using functions that get this data from the environment (like // NewClientFromEnv. func (c *Client) Endpoint() string { return c.endpoint } // Ping pings the docker server // // See https://goo.gl/wYfgY1 for more details. func (c *Client) Ping() error { return c.PingWithContext(context.TODO()) } // PingWithContext pings the docker server // The context object can be used to cancel the ping request. // // See https://goo.gl/wYfgY1 for more details. func (c *Client) PingWithContext(ctx context.Context) error { path := "/_ping" resp, err := c.do(http.MethodGet, path, doOptions{context: ctx}) if err != nil { return err } if resp.StatusCode != http.StatusOK { return newError(resp) } resp.Body.Close() return nil } func (c *Client) getServerAPIVersionString() (version string, err error) { resp, err := c.do(http.MethodGet, "/version", doOptions{}) if err != nil { return "", err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return "", fmt.Errorf("received unexpected status %d while trying to retrieve the server version", resp.StatusCode) } var versionResponse map[string]any if err := json.NewDecoder(resp.Body).Decode(&versionResponse); err != nil { return "", err } if version, ok := (versionResponse["ApiVersion"]).(string); ok { return version, nil } return "", nil } type doOptions struct { data any forceJSON bool headers map[string]string context context.Context } func (c *Client) do(method, path string, doOptions doOptions) (*http.Response, error) { var params io.Reader if doOptions.data != nil || doOptions.forceJSON { buf, err := json.Marshal(doOptions.data) if err != nil { return nil, err } params = bytes.NewBuffer(buf) } if path != "/version" && !c.SkipServerVersionCheck && c.expectedAPIVersion == nil { err := c.checkAPIVersion() if err != nil { return nil, err } } protocol := c.endpointURL.Scheme var u string switch protocol { case unixProtocol, namedPipeProtocol: u = c.getFakeNativeURL(path) default: u = c.getURL(path) } req, err := http.NewRequest(method, u, params) if err != nil { return nil, err } req.Header.Set("User-Agent", userAgent) if doOptions.data != nil { req.Header.Set("Content-Type", "application/json") } else if method == http.MethodPost { req.Header.Set("Content-Type", "plain/text") } for k, v := range doOptions.headers { req.Header.Set(k, v) } ctx := doOptions.context if ctx == nil { ctx = context.Background() } resp, err := c.HTTPClient.Do(req.WithContext(ctx)) if err != nil { if strings.Contains(err.Error(), "connection refused") { return nil, ErrConnectionRefused } return nil, chooseError(ctx, err) } if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusBadRequest { return nil, newError(resp) } return resp, nil } type streamOptions struct { setRawTerminal bool rawJSONStream bool useJSONDecoder bool headers map[string]string in io.Reader stdout io.Writer stderr io.Writer reqSent chan struct{} // timeout is the initial connection timeout timeout time.Duration // Timeout with no data is received, it's reset every time new data // arrives inactivityTimeout time.Duration context context.Context } func chooseError(ctx context.Context, err error) error { select { case <-ctx.Done(): return context.Cause(ctx) default: return err } } func (c *Client) stream(method, path string, streamOptions streamOptions) error { if (method == http.MethodPost || method == http.MethodPut) && streamOptions.in == nil { streamOptions.in = bytes.NewReader(nil) } if path != "/version" && !c.SkipServerVersionCheck && c.expectedAPIVersion == nil { err := c.checkAPIVersion() if err != nil { return err } } return c.streamURL(method, c.getURL(path), streamOptions) } func (c *Client) streamURL(method, url string, streamOptions streamOptions) error { if (method == http.MethodPost || method == http.MethodPut) && streamOptions.in == nil { streamOptions.in = bytes.NewReader(nil) } if !c.SkipServerVersionCheck && c.expectedAPIVersion == nil { err := c.checkAPIVersion() if err != nil { return err } } // make a sub-context so that our active cancellation does not affect parent ctx := streamOptions.context if ctx == nil { ctx = context.Background() } subCtx, cancelRequest := context.WithCancel(ctx) defer cancelRequest() req, err := http.NewRequestWithContext(ctx, method, url, streamOptions.in) if err != nil { return err } req.Header.Set("User-Agent", userAgent) if method == http.MethodPost { req.Header.Set("Content-Type", "plain/text") } for key, val := range streamOptions.headers { req.Header.Set(key, val) } var resp *http.Response protocol := c.endpointURL.Scheme address := c.endpointURL.Path if streamOptions.stdout == nil { streamOptions.stdout = io.Discard } if streamOptions.stderr == nil { streamOptions.stderr = io.Discard } if protocol == unixProtocol || protocol == namedPipeProtocol { var dial net.Conn dial, err = c.Dialer.Dial(protocol, address) if err != nil { return err } go func() { <-subCtx.Done() dial.Close() }() breader := bufio.NewReader(dial) err = req.Write(dial) if err != nil { return chooseError(subCtx, err) } // ReadResponse may hang if server does not replay if streamOptions.timeout > 0 { dial.SetDeadline(time.Now().Add(streamOptions.timeout)) } if streamOptions.reqSent != nil { close(streamOptions.reqSent) } if resp, err = http.ReadResponse(breader, req); err != nil { // Cancel timeout for future I/O operations if streamOptions.timeout > 0 { dial.SetDeadline(time.Time{}) } if strings.Contains(err.Error(), "connection refused") { return ErrConnectionRefused } return chooseError(subCtx, err) } defer resp.Body.Close() } else { if resp, err = c.HTTPClient.Do(req.WithContext(subCtx)); err != nil { if strings.Contains(err.Error(), "connection refused") { return ErrConnectionRefused } return chooseError(subCtx, err) } defer resp.Body.Close() if streamOptions.reqSent != nil { close(streamOptions.reqSent) } } if resp.StatusCode < 200 || resp.StatusCode >= 400 { return newError(resp) } var canceled uint32 if streamOptions.inactivityTimeout > 0 { var ch chan<- struct{} resp.Body, ch = handleInactivityTimeout(resp.Body, streamOptions.inactivityTimeout, cancelRequest, &canceled) defer close(ch) } err = handleStreamResponse(resp, &streamOptions) if err != nil { if atomic.LoadUint32(&canceled) != 0 { return ErrInactivityTimeout } return chooseError(subCtx, err) } return nil } func handleStreamResponse(resp *http.Response, streamOptions *streamOptions) error { var err error if !streamOptions.useJSONDecoder && resp.Header.Get("Content-Type") != "application/json" { if streamOptions.setRawTerminal { _, err = io.Copy(streamOptions.stdout, resp.Body) } else { _, err = stdcopy.StdCopy(streamOptions.stdout, streamOptions.stderr, resp.Body) } return err } // if we want to get raw json stream, just copy it back to output // without decoding it if streamOptions.rawJSONStream { _, err = io.Copy(streamOptions.stdout, resp.Body) return err } if st, ok := streamOptions.stdout.(stream); ok { err = jsonmessage.DisplayJSONMessagesToStream(resp.Body, st, nil) } else { err = jsonmessage.DisplayJSONMessagesStream(resp.Body, streamOptions.stdout, 0, false, nil) } return err } type stream interface { io.Writer FD() uintptr IsTerminal() bool } type proxyReader struct { io.ReadCloser calls uint64 } func (p *proxyReader) callCount() uint64 { return atomic.LoadUint64(&p.calls) } func (p *proxyReader) Read(data []byte) (int, error) { atomic.AddUint64(&p.calls, 1) return p.ReadCloser.Read(data) } func handleInactivityTimeout(reader io.ReadCloser, timeout time.Duration, cancelRequest func(), canceled *uint32) (io.ReadCloser, chan<- struct{}) { done := make(chan struct{}) proxyReader := &proxyReader{ReadCloser: reader} go func() { var lastCallCount uint64 for { select { case <-time.After(timeout): case <-done: return } curCallCount := proxyReader.callCount() if curCallCount == lastCallCount { atomic.AddUint32(canceled, 1) cancelRequest() return } lastCallCount = curCallCount } }() return proxyReader, done } type hijackOptions struct { success chan struct{} setRawTerminal bool in io.Reader stdout io.Writer stderr io.Writer data any } // CloseWaiter is an interface with methods for closing the underlying resource // and then waiting for it to finish processing. type CloseWaiter interface { io.Closer Wait() error } type waiterFunc func() error func (w waiterFunc) Wait() error { return w() } type closerFunc func() error func (c closerFunc) Close() error { return c() } func (c *Client) hijack(method, path string, hijackOptions hijackOptions) (CloseWaiter, error) { if path != "/version" && !c.SkipServerVersionCheck && c.expectedAPIVersion == nil { err := c.checkAPIVersion() if err != nil { return nil, err } } var params io.Reader if hijackOptions.data != nil { buf, err := json.Marshal(hijackOptions.data) if err != nil { return nil, err } params = bytes.NewBuffer(buf) } req, err := http.NewRequest(method, c.getURL(path), params) if err != nil { return nil, err } req.Header.Set("Content-Type", "application/json") req.Header.Set("Connection", "Upgrade") req.Header.Set("Upgrade", "tcp") protocol := c.endpointURL.Scheme address := c.endpointURL.Path if protocol != unixProtocol && protocol != namedPipeProtocol { protocol = "tcp" address = c.endpointURL.Host } var dial net.Conn if c.TLSConfig != nil && protocol != unixProtocol && protocol != namedPipeProtocol { netDialer, ok := c.Dialer.(*net.Dialer) if !ok { return nil, ErrTLSNotSupported } dial, err = tlsDialWithDialer(netDialer, protocol, address, c.TLSConfig) if err != nil { return nil, err } } else { dial, err = c.Dialer.Dial(protocol, address) if err != nil { return nil, err } } errs := make(chan error, 1) quit := make(chan struct{}) go func() { //lint:ignore SA1019 the alternative doesn't quite work, so keep using the deprecated thing. clientconn := httputil.NewClientConn(dial, nil) defer clientconn.Close() clientconn.Do(req) if hijackOptions.success != nil { hijackOptions.success <- struct{}{} <-hijackOptions.success } rwc, br := clientconn.Hijack() defer rwc.Close() errChanOut := make(chan error, 1) errChanIn := make(chan error, 2) if hijackOptions.stdout == nil && hijackOptions.stderr == nil { close(errChanOut) } else { // Only copy if hijackOptions.stdout and/or hijackOptions.stderr is actually set. // Otherwise, if the only stream you care about is stdin, your attach session // will "hang" until the container terminates, even though you're not reading // stdout/stderr if hijackOptions.stdout == nil { hijackOptions.stdout = io.Discard } if hijackOptions.stderr == nil { hijackOptions.stderr = io.Discard } go func() { defer func() { if hijackOptions.in != nil { if closer, ok := hijackOptions.in.(io.Closer); ok { closer.Close() } errChanIn <- nil } }() var err error if hijackOptions.setRawTerminal { _, err = io.Copy(hijackOptions.stdout, br) } else { _, err = stdcopy.StdCopy(hijackOptions.stdout, hijackOptions.stderr, br) } errChanOut <- err }() } go func() { var err error if hijackOptions.in != nil { _, err = io.Copy(rwc, hijackOptions.in) } errChanIn <- err rwc.(interface { CloseWrite() error }).CloseWrite() }() var errIn error select { case errIn = <-errChanIn: case <-quit: } var errOut error select { case errOut = <-errChanOut: case <-quit: } if errIn != nil { errs <- errIn } else { errs <- errOut } }() return struct { closerFunc waiterFunc }{ closerFunc(func() error { close(quit); return nil }), waiterFunc(func() error { return <-errs }), }, nil } func (c *Client) getURL(path string) string { urlStr := strings.TrimRight(c.endpointURL.String(), "/") if c.endpointURL.Scheme == unixProtocol || c.endpointURL.Scheme == namedPipeProtocol { urlStr = "" } if c.requestedAPIVersion != nil { return fmt.Sprintf("%s/v%s%s", urlStr, c.requestedAPIVersion, path) } return fmt.Sprintf("%s%s", urlStr, path) } func (c *Client) getPath(basepath string, opts any) (string, error) { queryStr, requiredAPIVersion := queryStringVersion(opts) return c.pathVersionCheck(basepath, queryStr, requiredAPIVersion) } func (c *Client) pathVersionCheck(basepath, queryStr string, requiredAPIVersion APIVersion) (string, error) { urlStr := strings.TrimRight(c.endpointURL.String(), "/") if c.endpointURL.Scheme == unixProtocol || c.endpointURL.Scheme == namedPipeProtocol { urlStr = "" } if c.requestedAPIVersion != nil { if c.requestedAPIVersion.GreaterThanOrEqualTo(requiredAPIVersion) { return fmt.Sprintf("%s/v%s%s?%s", urlStr, c.requestedAPIVersion, basepath, queryStr), nil } return "", fmt.Errorf("API %s requires version %s, requested version %s is insufficient", basepath, requiredAPIVersion, c.requestedAPIVersion) } if requiredAPIVersion != nil { return fmt.Sprintf("%s/v%s%s?%s", urlStr, requiredAPIVersion, basepath, queryStr), nil } return fmt.Sprintf("%s%s?%s", urlStr, basepath, queryStr), nil } // getFakeNativeURL returns the URL needed to make an HTTP request over a UNIX // domain socket to the given path. func (c *Client) getFakeNativeURL(path string) string { u := *c.endpointURL // Copy. // Override URL so that net/http will not complain. u.Scheme = "http" u.Host = "unix.sock" // Doesn't matter what this is - it's not used. u.Path = "" urlStr := strings.TrimRight(u.String(), "/") if c.requestedAPIVersion != nil { return fmt.Sprintf("%s/v%s%s", urlStr, c.requestedAPIVersion, path) } return fmt.Sprintf("%s%s", urlStr, path) } func queryStringVersion(opts any) (string, APIVersion) { if opts == nil { return "", nil } value := reflect.ValueOf(opts) if value.Kind() == reflect.Ptr { value = value.Elem() } if value.Kind() != reflect.Struct { return "", nil } var apiVersion APIVersion items := url.Values(map[string][]string{}) for i := 0; i < value.NumField(); i++ { field := value.Type().Field(i) if field.PkgPath != "" { continue } key := field.Tag.Get("qs") if key == "" { key = strings.ToLower(field.Name) } else if key == "-" { continue } if addQueryStringValue(items, key, value.Field(i)) { verstr := field.Tag.Get("ver") if verstr != "" { ver, _ := NewAPIVersion(verstr) if apiVersion == nil { apiVersion = ver } else if ver.GreaterThan(apiVersion) { apiVersion = ver } } } } return items.Encode(), apiVersion } func queryString(opts any) string { s, _ := queryStringVersion(opts) return s } func addQueryStringValue(items url.Values, key string, v reflect.Value) bool { switch v.Kind() { case reflect.Bool: if v.Bool() { items.Add(key, "1") return true } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if v.Int() > 0 { items.Add(key, strconv.FormatInt(v.Int(), 10)) return true } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: if v.Uint() > 0 { items.Add(key, strconv.FormatUint(v.Uint(), 10)) return true } case reflect.Float32, reflect.Float64: if v.Float() > 0 { items.Add(key, strconv.FormatFloat(v.Float(), 'f', -1, 64)) return true } case reflect.String: if v.String() != "" { items.Add(key, v.String()) return true } case reflect.Ptr: if !v.IsNil() { if b, err := json.Marshal(v.Interface()); err == nil { items.Add(key, string(b)) return true } } case reflect.Map: if len(v.MapKeys()) > 0 { if b, err := json.Marshal(v.Interface()); err == nil { items.Add(key, string(b)) return true } } case reflect.Array, reflect.Slice: vLen := v.Len() var valuesAdded int if vLen > 0 { for i := 0; i < vLen; i++ { if addQueryStringValue(items, key, v.Index(i)) { valuesAdded++ } } } return valuesAdded > 0 } return false } // Error represents failures in the API. It represents a failure from the API. type Error struct { Status int Message string } func newError(resp *http.Response) *Error { type ErrMsg struct { Message string `json:"message"` } defer resp.Body.Close() data, err := io.ReadAll(resp.Body) if err != nil { return &Error{Status: resp.StatusCode, Message: fmt.Sprintf("cannot read body, err: %v", err)} } var emsg ErrMsg err = json.Unmarshal(data, &emsg) if err != nil { return &Error{Status: resp.StatusCode, Message: string(data)} } return &Error{Status: resp.StatusCode, Message: emsg.Message} } func (e *Error) Error() string { return fmt.Sprintf("API error (%d): %s", e.Status, e.Message) } func parseEndpoint(endpoint string, tls bool) (*url.URL, error) { if endpoint != "" && !strings.Contains(endpoint, "://") { endpoint = "tcp://" + endpoint } u, err := url.Parse(endpoint) if err != nil { return nil, ErrInvalidEndpoint } if tls && u.Scheme != "unix" { u.Scheme = "https" } switch u.Scheme { case unixProtocol, namedPipeProtocol: return u, nil case "http", "https", "tcp": _, port, err := net.SplitHostPort(u.Host) if err != nil { var e *net.AddrError if errors.As(err, &e) { if e.Err == "missing port in address" { return u, nil } } return nil, ErrInvalidEndpoint } number, err := strconv.ParseInt(port, 10, 64) if err == nil && number > 0 && number < 65536 { if u.Scheme == "tcp" { if tls { u.Scheme = "https" } else { u.Scheme = "http" } } return u, nil } return nil, ErrInvalidEndpoint default: return nil, ErrInvalidEndpoint } } type dockerEnv struct { dockerHost string dockerTLSVerify bool dockerCertPath string } func getDockerEnv() (*dockerEnv, error) { dockerHost := os.Getenv("DOCKER_HOST") var err error if dockerHost == "" { dockerHost = defaultHost } dockerTLSVerify := os.Getenv("DOCKER_TLS_VERIFY") != "" var dockerCertPath string if dockerTLSVerify { dockerCertPath = os.Getenv("DOCKER_CERT_PATH") if dockerCertPath == "" { home := homedir.Get() if home == "" { return nil, errors.New("environment variable HOME must be set if DOCKER_CERT_PATH is not set") } dockerCertPath = filepath.Join(home, ".docker") dockerCertPath, err = filepath.Abs(dockerCertPath) if err != nil { return nil, err } } } return &dockerEnv{ dockerHost: dockerHost, dockerTLSVerify: dockerTLSVerify, dockerCertPath: dockerCertPath, }, nil } // defaultTransport returns a new http.Transport with similar default values to // http.DefaultTransport, but with idle connections and keepalives disabled. func defaultTransport() *http.Transport { transport := defaultPooledTransport() transport.DisableKeepAlives = true transport.MaxIdleConnsPerHost = -1 return transport } // defaultPooledTransport returns a new http.Transport with similar default // values to http.DefaultTransport. Do not use this for transient transports as // it can leak file descriptors over time. Only use this for transports that // will be re-used for the same host(s). func defaultPooledTransport() *http.Transport { transport := &http.Transport{ Proxy: http.ProxyFromEnvironment, DialContext: (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, }).DialContext, MaxIdleConns: 100, IdleConnTimeout: 90 * time.Second, TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second, MaxIdleConnsPerHost: runtime.GOMAXPROCS(0) + 1, } return transport } // defaultClient returns a new http.Client with similar default values to // http.Client, but with a non-shared Transport, idle connections disabled, and // keepalives disabled. func defaultClient() *http.Client { return &http.Client{ Transport: defaultTransport(), } } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/client_unix.go ================================================ // Copyright 2016 go-dockerclient authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build !windows package docker import ( "context" "net" "net/http" ) const defaultHost = "unix:///var/run/docker.sock" // initializeNativeClient initializes the native Unix domain socket client on // Unix-style operating systems func (c *Client) initializeNativeClient(trFunc func() *http.Transport) { if c.endpointURL.Scheme != unixProtocol { return } sockPath := c.endpointURL.Path tr := trFunc() tr.Proxy = nil tr.DialContext = func(_ context.Context, network, addr string) (net.Conn, error) { return c.Dialer.Dial(unixProtocol, sockPath) } c.HTTPClient.Transport = tr } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/client_windows.go ================================================ // Copyright 2016 go-dockerclient authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package docker import ( "context" "net" "net/http" "time" winio "github.com/Microsoft/go-winio" ) const ( defaultHost = "npipe:////./pipe/docker_engine" namedPipeConnectTimeout = 2 * time.Second ) type pipeDialer struct { dialFunc func(network, addr string) (net.Conn, error) } func (p pipeDialer) Dial(network, address string) (net.Conn, error) { return p.dialFunc(network, address) } // initializeNativeClient initializes the native Named Pipe client for Windows func (c *Client) initializeNativeClient(trFunc func() *http.Transport) { if c.endpointURL.Scheme != namedPipeProtocol { return } namedPipePath := c.endpointURL.Path dialFunc := func(_, addr string) (net.Conn, error) { timeout := namedPipeConnectTimeout return winio.DialPipe(namedPipePath, &timeout) } tr := trFunc() tr.Proxy = nil tr.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { return dialFunc(network, addr) } c.Dialer = &pipeDialer{dialFunc} c.HTTPClient.Transport = tr } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/container.go ================================================ // Copyright 2013 go-dockerclient authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package docker import ( "fmt" "strconv" "strings" "time" units "github.com/docker/go-units" ) // APIPort is a type that represents a port mapping returned by the Docker API type APIPort struct { PrivatePort int64 `json:"PrivatePort,omitempty" yaml:"PrivatePort,omitempty" toml:"PrivatePort,omitempty"` PublicPort int64 `json:"PublicPort,omitempty" yaml:"PublicPort,omitempty" toml:"PublicPort,omitempty"` Type string `json:"Type,omitempty" yaml:"Type,omitempty" toml:"Type,omitempty"` IP string `json:"IP,omitempty" yaml:"IP,omitempty" toml:"IP,omitempty"` } // APIMount represents a mount point for a container. type APIMount struct { Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"` Source string `json:"Source,omitempty" yaml:"Source,omitempty" toml:"Source,omitempty"` Destination string `json:"Destination,omitempty" yaml:"Destination,omitempty" toml:"Destination,omitempty"` Driver string `json:"Driver,omitempty" yaml:"Driver,omitempty" toml:"Driver,omitempty"` Mode string `json:"Mode,omitempty" yaml:"Mode,omitempty" toml:"Mode,omitempty"` RW bool `json:"RW,omitempty" yaml:"RW,omitempty" toml:"RW,omitempty"` Propagation string `json:"Propagation,omitempty" yaml:"Propagation,omitempty" toml:"Propagation,omitempty"` Type string `json:"Type,omitempty" yaml:"Type,omitempty" toml:"Type,omitempty"` } // APIContainers represents each container in the list returned by // ListContainers. type APIContainers struct { ID string `json:"Id" yaml:"Id" toml:"Id"` Image string `json:"Image,omitempty" yaml:"Image,omitempty" toml:"Image,omitempty"` Command string `json:"Command,omitempty" yaml:"Command,omitempty" toml:"Command,omitempty"` Created int64 `json:"Created,omitempty" yaml:"Created,omitempty" toml:"Created,omitempty"` State string `json:"State,omitempty" yaml:"State,omitempty" toml:"State,omitempty"` Status string `json:"Status,omitempty" yaml:"Status,omitempty" toml:"Status,omitempty"` Ports []APIPort `json:"Ports,omitempty" yaml:"Ports,omitempty" toml:"Ports,omitempty"` SizeRw int64 `json:"SizeRw,omitempty" yaml:"SizeRw,omitempty" toml:"SizeRw,omitempty"` SizeRootFs int64 `json:"SizeRootFs,omitempty" yaml:"SizeRootFs,omitempty" toml:"SizeRootFs,omitempty"` Names []string `json:"Names,omitempty" yaml:"Names,omitempty" toml:"Names,omitempty"` Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty" toml:"Labels,omitempty"` Networks NetworkList `json:"NetworkSettings,omitempty" yaml:"NetworkSettings,omitempty" toml:"NetworkSettings,omitempty"` Mounts []APIMount `json:"Mounts,omitempty" yaml:"Mounts,omitempty" toml:"Mounts,omitempty"` } // NetworkList encapsulates a map of networks, as returned by the Docker API in // ListContainers. type NetworkList struct { Networks map[string]ContainerNetwork `json:"Networks" yaml:"Networks,omitempty" toml:"Networks,omitempty"` } // Port represents the port number and the protocol, in the form // /. For example: 80/tcp. type Port string // Port returns the number of the port. func (p Port) Port() string { return strings.Split(string(p), "/")[0] } // Proto returns the name of the protocol. func (p Port) Proto() string { parts := strings.Split(string(p), "/") if len(parts) == 1 { return "tcp" } return parts[1] } // HealthCheck represents one check of health. type HealthCheck struct { Start time.Time `json:"Start,omitempty" yaml:"Start,omitempty" toml:"Start,omitempty"` End time.Time `json:"End,omitempty" yaml:"End,omitempty" toml:"End,omitempty"` ExitCode int `json:"ExitCode,omitempty" yaml:"ExitCode,omitempty" toml:"ExitCode,omitempty"` Output string `json:"Output,omitempty" yaml:"Output,omitempty" toml:"Output,omitempty"` } // Health represents the health of a container. type Health struct { Status string `json:"Status,omitempty" yaml:"Status,omitempty" toml:"Status,omitempty"` FailingStreak int `json:"FailingStreak,omitempty" yaml:"FailingStreak,omitempty" toml:"FailingStreak,omitempty"` Log []HealthCheck `json:"Log,omitempty" yaml:"Log,omitempty" toml:"Log,omitempty"` } // State represents the state of a container. type State struct { Status string `json:"Status,omitempty" yaml:"Status,omitempty" toml:"Status,omitempty"` Running bool `json:"Running,omitempty" yaml:"Running,omitempty" toml:"Running,omitempty"` Paused bool `json:"Paused,omitempty" yaml:"Paused,omitempty" toml:"Paused,omitempty"` Restarting bool `json:"Restarting,omitempty" yaml:"Restarting,omitempty" toml:"Restarting,omitempty"` OOMKilled bool `json:"OOMKilled,omitempty" yaml:"OOMKilled,omitempty" toml:"OOMKilled,omitempty"` RemovalInProgress bool `json:"RemovalInProgress,omitempty" yaml:"RemovalInProgress,omitempty" toml:"RemovalInProgress,omitempty"` Dead bool `json:"Dead,omitempty" yaml:"Dead,omitempty" toml:"Dead,omitempty"` Pid int `json:"Pid,omitempty" yaml:"Pid,omitempty" toml:"Pid,omitempty"` ExitCode int `json:"ExitCode,omitempty" yaml:"ExitCode,omitempty" toml:"ExitCode,omitempty"` Error string `json:"Error,omitempty" yaml:"Error,omitempty" toml:"Error,omitempty"` StartedAt time.Time `json:"StartedAt,omitempty" yaml:"StartedAt,omitempty" toml:"StartedAt,omitempty"` FinishedAt time.Time `json:"FinishedAt,omitempty" yaml:"FinishedAt,omitempty" toml:"FinishedAt,omitempty"` Health Health `json:"Health,omitempty" yaml:"Health,omitempty" toml:"Health,omitempty"` } // String returns a human-readable description of the state func (s *State) String() string { if s.Running { if s.Paused { return fmt.Sprintf("Up %s (Paused)", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt))) } if s.Restarting { return fmt.Sprintf("Restarting (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt))) } return fmt.Sprintf("Up %s", units.HumanDuration(time.Now().UTC().Sub(s.StartedAt))) } if s.RemovalInProgress { return "Removal In Progress" } if s.Dead { return "Dead" } if s.StartedAt.IsZero() { return "Created" } if s.FinishedAt.IsZero() { return "" } return fmt.Sprintf("Exited (%d) %s ago", s.ExitCode, units.HumanDuration(time.Now().UTC().Sub(s.FinishedAt))) } // StateString returns a single string to describe state func (s *State) StateString() string { if s.Running { if s.Paused { return "paused" } if s.Restarting { return "restarting" } return "running" } if s.Dead { return "dead" } if s.StartedAt.IsZero() { return "created" } return "exited" } // PortBinding represents the host/container port mapping as returned in the // `docker inspect` json type PortBinding struct { HostIP string `json:"HostIp,omitempty" yaml:"HostIp,omitempty" toml:"HostIp,omitempty"` HostPort string `json:"HostPort,omitempty" yaml:"HostPort,omitempty" toml:"HostPort,omitempty"` } // PortMapping represents a deprecated field in the `docker inspect` output, // and its value as found in NetworkSettings should always be nil type PortMapping map[string]string // ContainerNetwork represents the networking settings of a container per network. type ContainerNetwork struct { Aliases []string `json:"Aliases,omitempty" yaml:"Aliases,omitempty" toml:"Aliases,omitempty"` MacAddress string `json:"MacAddress,omitempty" yaml:"MacAddress,omitempty" toml:"MacAddress,omitempty"` GlobalIPv6PrefixLen int `json:"GlobalIPv6PrefixLen,omitempty" yaml:"GlobalIPv6PrefixLen,omitempty" toml:"GlobalIPv6PrefixLen,omitempty"` GlobalIPv6Address string `json:"GlobalIPv6Address,omitempty" yaml:"GlobalIPv6Address,omitempty" toml:"GlobalIPv6Address,omitempty"` IPv6Gateway string `json:"IPv6Gateway,omitempty" yaml:"IPv6Gateway,omitempty" toml:"IPv6Gateway,omitempty"` IPPrefixLen int `json:"IPPrefixLen,omitempty" yaml:"IPPrefixLen,omitempty" toml:"IPPrefixLen,omitempty"` IPAddress string `json:"IPAddress,omitempty" yaml:"IPAddress,omitempty" toml:"IPAddress,omitempty"` Gateway string `json:"Gateway,omitempty" yaml:"Gateway,omitempty" toml:"Gateway,omitempty"` EndpointID string `json:"EndpointID,omitempty" yaml:"EndpointID,omitempty" toml:"EndpointID,omitempty"` NetworkID string `json:"NetworkID,omitempty" yaml:"NetworkID,omitempty" toml:"NetworkID,omitempty"` } // NetworkSettings contains network-related information about a container type NetworkSettings struct { Networks map[string]ContainerNetwork `json:"Networks,omitempty" yaml:"Networks,omitempty" toml:"Networks,omitempty"` IPAddress string `json:"IPAddress,omitempty" yaml:"IPAddress,omitempty" toml:"IPAddress,omitempty"` IPPrefixLen int `json:"IPPrefixLen,omitempty" yaml:"IPPrefixLen,omitempty" toml:"IPPrefixLen,omitempty"` MacAddress string `json:"MacAddress,omitempty" yaml:"MacAddress,omitempty" toml:"MacAddress,omitempty"` Gateway string `json:"Gateway,omitempty" yaml:"Gateway,omitempty" toml:"Gateway,omitempty"` Bridge string `json:"Bridge,omitempty" yaml:"Bridge,omitempty" toml:"Bridge,omitempty"` PortMapping map[string]PortMapping `json:"PortMapping,omitempty" yaml:"PortMapping,omitempty" toml:"PortMapping,omitempty"` Ports map[Port][]PortBinding `json:"Ports,omitempty" yaml:"Ports,omitempty" toml:"Ports,omitempty"` NetworkID string `json:"NetworkID,omitempty" yaml:"NetworkID,omitempty" toml:"NetworkID,omitempty"` EndpointID string `json:"EndpointID,omitempty" yaml:"EndpointID,omitempty" toml:"EndpointID,omitempty"` SandboxKey string `json:"SandboxKey,omitempty" yaml:"SandboxKey,omitempty" toml:"SandboxKey,omitempty"` GlobalIPv6Address string `json:"GlobalIPv6Address,omitempty" yaml:"GlobalIPv6Address,omitempty" toml:"GlobalIPv6Address,omitempty"` GlobalIPv6PrefixLen int `json:"GlobalIPv6PrefixLen,omitempty" yaml:"GlobalIPv6PrefixLen,omitempty" toml:"GlobalIPv6PrefixLen,omitempty"` IPv6Gateway string `json:"IPv6Gateway,omitempty" yaml:"IPv6Gateway,omitempty" toml:"IPv6Gateway,omitempty"` LinkLocalIPv6Address string `json:"LinkLocalIPv6Address,omitempty" yaml:"LinkLocalIPv6Address,omitempty" toml:"LinkLocalIPv6Address,omitempty"` LinkLocalIPv6PrefixLen int `json:"LinkLocalIPv6PrefixLen,omitempty" yaml:"LinkLocalIPv6PrefixLen,omitempty" toml:"LinkLocalIPv6PrefixLen,omitempty"` SecondaryIPAddresses []string `json:"SecondaryIPAddresses,omitempty" yaml:"SecondaryIPAddresses,omitempty" toml:"SecondaryIPAddresses,omitempty"` SecondaryIPv6Addresses []string `json:"SecondaryIPv6Addresses,omitempty" yaml:"SecondaryIPv6Addresses,omitempty" toml:"SecondaryIPv6Addresses,omitempty"` } // PortMappingAPI translates the port mappings as contained in NetworkSettings // into the format in which they would appear when returned by the API func (settings *NetworkSettings) PortMappingAPI() []APIPort { var mapping []APIPort for port, bindings := range settings.Ports { p, _ := parsePort(port.Port()) if len(bindings) == 0 { mapping = append(mapping, APIPort{ PrivatePort: int64(p), Type: port.Proto(), }) continue } for _, binding := range bindings { p, _ := parsePort(port.Port()) h, _ := parsePort(binding.HostPort) mapping = append(mapping, APIPort{ PrivatePort: int64(p), PublicPort: int64(h), Type: port.Proto(), IP: binding.HostIP, }) } } return mapping } func parsePort(rawPort string) (int, error) { port, err := strconv.ParseUint(rawPort, 10, 16) if err != nil { return 0, err } return int(port), nil } // Config is the list of configuration options used when creating a container. // Config does not contain the options that are specific to starting a container on a // given host. Those are contained in HostConfig type Config struct { Hostname string `json:"Hostname,omitempty" yaml:"Hostname,omitempty" toml:"Hostname,omitempty"` Domainname string `json:"Domainname,omitempty" yaml:"Domainname,omitempty" toml:"Domainname,omitempty"` User string `json:"User,omitempty" yaml:"User,omitempty" toml:"User,omitempty"` Memory int64 `json:"Memory,omitempty" yaml:"Memory,omitempty" toml:"Memory,omitempty"` MemorySwap int64 `json:"MemorySwap,omitempty" yaml:"MemorySwap,omitempty" toml:"MemorySwap,omitempty"` MemoryReservation int64 `json:"MemoryReservation,omitempty" yaml:"MemoryReservation,omitempty" toml:"MemoryReservation,omitempty"` KernelMemory int64 `json:"KernelMemory,omitempty" yaml:"KernelMemory,omitempty" toml:"KernelMemory,omitempty"` CPUShares int64 `json:"CpuShares,omitempty" yaml:"CpuShares,omitempty" toml:"CpuShares,omitempty"` CPUSet string `json:"Cpuset,omitempty" yaml:"Cpuset,omitempty" toml:"Cpuset,omitempty"` PortSpecs []string `json:"PortSpecs,omitempty" yaml:"PortSpecs,omitempty" toml:"PortSpecs,omitempty"` ExposedPorts map[Port]struct{} `json:"ExposedPorts,omitempty" yaml:"ExposedPorts,omitempty" toml:"ExposedPorts,omitempty"` PublishService string `json:"PublishService,omitempty" yaml:"PublishService,omitempty" toml:"PublishService,omitempty"` StopSignal string `json:"StopSignal,omitempty" yaml:"StopSignal,omitempty" toml:"StopSignal,omitempty"` StopTimeout int `json:"StopTimeout,omitempty" yaml:"StopTimeout,omitempty" toml:"StopTimeout,omitempty"` Env []string `json:"Env,omitempty" yaml:"Env,omitempty" toml:"Env,omitempty"` Cmd []string `json:"Cmd" yaml:"Cmd" toml:"Cmd"` Shell []string `json:"Shell,omitempty" yaml:"Shell,omitempty" toml:"Shell,omitempty"` Healthcheck *HealthConfig `json:"Healthcheck,omitempty" yaml:"Healthcheck,omitempty" toml:"Healthcheck,omitempty"` DNS []string `json:"Dns,omitempty" yaml:"Dns,omitempty" toml:"Dns,omitempty"` // For Docker API v1.9 and below only Image string `json:"Image,omitempty" yaml:"Image,omitempty" toml:"Image,omitempty"` Volumes map[string]struct{} `json:"Volumes,omitempty" yaml:"Volumes,omitempty" toml:"Volumes,omitempty"` VolumeDriver string `json:"VolumeDriver,omitempty" yaml:"VolumeDriver,omitempty" toml:"VolumeDriver,omitempty"` WorkingDir string `json:"WorkingDir,omitempty" yaml:"WorkingDir,omitempty" toml:"WorkingDir,omitempty"` MacAddress string `json:"MacAddress,omitempty" yaml:"MacAddress,omitempty" toml:"MacAddress,omitempty"` Entrypoint []string `json:"Entrypoint" yaml:"Entrypoint" toml:"Entrypoint"` SecurityOpts []string `json:"SecurityOpts,omitempty" yaml:"SecurityOpts,omitempty" toml:"SecurityOpts,omitempty"` OnBuild []string `json:"OnBuild,omitempty" yaml:"OnBuild,omitempty" toml:"OnBuild,omitempty"` Mounts []Mount `json:"Mounts,omitempty" yaml:"Mounts,omitempty" toml:"Mounts,omitempty"` Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty" toml:"Labels,omitempty"` AttachStdin bool `json:"AttachStdin,omitempty" yaml:"AttachStdin,omitempty" toml:"AttachStdin,omitempty"` AttachStdout bool `json:"AttachStdout,omitempty" yaml:"AttachStdout,omitempty" toml:"AttachStdout,omitempty"` AttachStderr bool `json:"AttachStderr,omitempty" yaml:"AttachStderr,omitempty" toml:"AttachStderr,omitempty"` ArgsEscaped bool `json:"ArgsEscaped,omitempty" yaml:"ArgsEscaped,omitempty" toml:"ArgsEscaped,omitempty"` Tty bool `json:"Tty,omitempty" yaml:"Tty,omitempty" toml:"Tty,omitempty"` OpenStdin bool `json:"OpenStdin,omitempty" yaml:"OpenStdin,omitempty" toml:"OpenStdin,omitempty"` StdinOnce bool `json:"StdinOnce,omitempty" yaml:"StdinOnce,omitempty" toml:"StdinOnce,omitempty"` NetworkDisabled bool `json:"NetworkDisabled,omitempty" yaml:"NetworkDisabled,omitempty" toml:"NetworkDisabled,omitempty"` // This is no longer used and has been kept here for backward // compatibility, please use HostConfig.VolumesFrom. VolumesFrom string `json:"VolumesFrom,omitempty" yaml:"VolumesFrom,omitempty" toml:"VolumesFrom,omitempty"` } // HostMount represents a mount point in the container in HostConfig. // // It has been added in the version 1.25 of the Docker API type HostMount struct { Target string `json:"Target,omitempty" yaml:"Target,omitempty" toml:"Target,omitempty"` Source string `json:"Source,omitempty" yaml:"Source,omitempty" toml:"Source,omitempty"` Type string `json:"Type,omitempty" yaml:"Type,omitempty" toml:"Type,omitempty"` ReadOnly bool `json:"ReadOnly,omitempty" yaml:"ReadOnly,omitempty" toml:"ReadOnly,omitempty"` BindOptions *BindOptions `json:"BindOptions,omitempty" yaml:"BindOptions,omitempty" toml:"BindOptions,omitempty"` VolumeOptions *VolumeOptions `json:"VolumeOptions,omitempty" yaml:"VolumeOptions,omitempty" toml:"VolumeOptions,omitempty"` TempfsOptions *TempfsOptions `json:"TmpfsOptions,omitempty" yaml:"TmpfsOptions,omitempty" toml:"TmpfsOptions,omitempty"` } // BindOptions contains optional configuration for the bind type type BindOptions struct { Propagation string `json:"Propagation,omitempty" yaml:"Propagation,omitempty" toml:"Propagation,omitempty"` } // VolumeOptions contains optional configuration for the volume type type VolumeOptions struct { NoCopy bool `json:"NoCopy,omitempty" yaml:"NoCopy,omitempty" toml:"NoCopy,omitempty"` Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty" toml:"Labels,omitempty"` DriverConfig VolumeDriverConfig `json:"DriverConfig,omitempty" yaml:"DriverConfig,omitempty" toml:"DriverConfig,omitempty"` } // TempfsOptions contains optional configuration for the tempfs type type TempfsOptions struct { SizeBytes int64 `json:"SizeBytes,omitempty" yaml:"SizeBytes,omitempty" toml:"SizeBytes,omitempty"` Mode int `json:"Mode,omitempty" yaml:"Mode,omitempty" toml:"Mode,omitempty"` } // VolumeDriverConfig holds a map of volume driver specific options type VolumeDriverConfig struct { Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"` Options map[string]string `json:"Options,omitempty" yaml:"Options,omitempty" toml:"Options,omitempty"` } // Mount represents a mount point in the container. // // It has been added in the version 1.20 of the Docker API, available since // Docker 1.8. type Mount struct { Name string Source string Destination string Driver string Mode string RW bool } // LogConfig defines the log driver type and the configuration for it. type LogConfig struct { Type string `json:"Type,omitempty" yaml:"Type,omitempty" toml:"Type,omitempty"` Config map[string]string `json:"Config,omitempty" yaml:"Config,omitempty" toml:"Config,omitempty"` } // ULimit defines system-wide resource limitations This can help a lot in // system administration, e.g. when a user starts too many processes and // therefore makes the system unresponsive for other users. type ULimit struct { Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"` Soft int64 `json:"Soft,omitempty" yaml:"Soft,omitempty" toml:"Soft,omitempty"` Hard int64 `json:"Hard,omitempty" yaml:"Hard,omitempty" toml:"Hard,omitempty"` } // SwarmNode containers information about which Swarm node the container is on. type SwarmNode struct { ID string `json:"ID,omitempty" yaml:"ID,omitempty" toml:"ID,omitempty"` IP string `json:"IP,omitempty" yaml:"IP,omitempty" toml:"IP,omitempty"` Addr string `json:"Addr,omitempty" yaml:"Addr,omitempty" toml:"Addr,omitempty"` Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"` CPUs int64 `json:"CPUs,omitempty" yaml:"CPUs,omitempty" toml:"CPUs,omitempty"` Memory int64 `json:"Memory,omitempty" yaml:"Memory,omitempty" toml:"Memory,omitempty"` Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty" toml:"Labels,omitempty"` } // GraphDriver contains information about the GraphDriver used by the // container. type GraphDriver struct { Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"` Data map[string]string `json:"Data,omitempty" yaml:"Data,omitempty" toml:"Data,omitempty"` } // HealthConfig holds configuration settings for the HEALTHCHECK feature // // It has been added in the version 1.24 of the Docker API, available since // Docker 1.12. type HealthConfig struct { // Test is the test to perform to check that the container is healthy. // An empty slice means to inherit the default. // The options are: // {} : inherit healthcheck // {"NONE"} : disable healthcheck // {"CMD", args...} : exec arguments directly // {"CMD-SHELL", command} : run command with system's default shell Test []string `json:"Test,omitempty" yaml:"Test,omitempty" toml:"Test,omitempty"` // Zero means to inherit. Durations are expressed as integer nanoseconds. Interval time.Duration `json:"Interval,omitempty" yaml:"Interval,omitempty" toml:"Interval,omitempty"` // Interval is the time to wait between checks. Timeout time.Duration `json:"Timeout,omitempty" yaml:"Timeout,omitempty" toml:"Timeout,omitempty"` // Timeout is the time to wait before considering the check to have hung. StartPeriod time.Duration `json:"StartPeriod,omitempty" yaml:"StartPeriod,omitempty" toml:"StartPeriod,omitempty"` // The start period for the container to initialize before the retries starts to count down. // Retries is the number of consecutive failures needed to consider a container as unhealthy. // Zero means inherit. Retries int `json:"Retries,omitempty" yaml:"Retries,omitempty" toml:"Retries,omitempty"` } // Container is the type encompasing everything about a container - its config, // hostconfig, etc. type Container struct { ID string `json:"Id" yaml:"Id" toml:"Id"` Created time.Time `json:"Created,omitempty" yaml:"Created,omitempty" toml:"Created,omitempty"` Path string `json:"Path,omitempty" yaml:"Path,omitempty" toml:"Path,omitempty"` Args []string `json:"Args,omitempty" yaml:"Args,omitempty" toml:"Args,omitempty"` Config *Config `json:"Config,omitempty" yaml:"Config,omitempty" toml:"Config,omitempty"` State State `json:"State,omitempty" yaml:"State,omitempty" toml:"State,omitempty"` Image string `json:"Image,omitempty" yaml:"Image,omitempty" toml:"Image,omitempty"` Node *SwarmNode `json:"Node,omitempty" yaml:"Node,omitempty" toml:"Node,omitempty"` NetworkSettings *NetworkSettings `json:"NetworkSettings,omitempty" yaml:"NetworkSettings,omitempty" toml:"NetworkSettings,omitempty"` SysInitPath string `json:"SysInitPath,omitempty" yaml:"SysInitPath,omitempty" toml:"SysInitPath,omitempty"` ResolvConfPath string `json:"ResolvConfPath,omitempty" yaml:"ResolvConfPath,omitempty" toml:"ResolvConfPath,omitempty"` HostnamePath string `json:"HostnamePath,omitempty" yaml:"HostnamePath,omitempty" toml:"HostnamePath,omitempty"` HostsPath string `json:"HostsPath,omitempty" yaml:"HostsPath,omitempty" toml:"HostsPath,omitempty"` LogPath string `json:"LogPath,omitempty" yaml:"LogPath,omitempty" toml:"LogPath,omitempty"` Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"` Driver string `json:"Driver,omitempty" yaml:"Driver,omitempty" toml:"Driver,omitempty"` Mounts []Mount `json:"Mounts,omitempty" yaml:"Mounts,omitempty" toml:"Mounts,omitempty"` Volumes map[string]string `json:"Volumes,omitempty" yaml:"Volumes,omitempty" toml:"Volumes,omitempty"` VolumesRW map[string]bool `json:"VolumesRW,omitempty" yaml:"VolumesRW,omitempty" toml:"VolumesRW,omitempty"` HostConfig *HostConfig `json:"HostConfig,omitempty" yaml:"HostConfig,omitempty" toml:"HostConfig,omitempty"` ExecIDs []string `json:"ExecIDs,omitempty" yaml:"ExecIDs,omitempty" toml:"ExecIDs,omitempty"` GraphDriver *GraphDriver `json:"GraphDriver,omitempty" yaml:"GraphDriver,omitempty" toml:"GraphDriver,omitempty"` RestartCount int `json:"RestartCount,omitempty" yaml:"RestartCount,omitempty" toml:"RestartCount,omitempty"` AppArmorProfile string `json:"AppArmorProfile,omitempty" yaml:"AppArmorProfile,omitempty" toml:"AppArmorProfile,omitempty"` MountLabel string `json:"MountLabel,omitempty" yaml:"MountLabel,omitempty" toml:"MountLabel,omitempty"` ProcessLabel string `json:"ProcessLabel,omitempty" yaml:"ProcessLabel,omitempty" toml:"ProcessLabel,omitempty"` Platform string `json:"Platform,omitempty" yaml:"Platform,omitempty" toml:"Platform,omitempty"` SizeRw int64 `json:"SizeRw,omitempty" yaml:"SizeRw,omitempty" toml:"SizeRw,omitempty"` SizeRootFs int64 `json:"SizeRootFs,omitempty" yaml:"SizeRootFs,omitempty" toml:"SizeRootFs,omitempty"` } // KeyValuePair is a type for generic key/value pairs as used in the Lxc // configuration type KeyValuePair struct { Key string `json:"Key,omitempty" yaml:"Key,omitempty" toml:"Key,omitempty"` Value string `json:"Value,omitempty" yaml:"Value,omitempty" toml:"Value,omitempty"` } // Device represents a device mapping between the Docker host and the // container. type Device struct { PathOnHost string `json:"PathOnHost,omitempty" yaml:"PathOnHost,omitempty" toml:"PathOnHost,omitempty"` PathInContainer string `json:"PathInContainer,omitempty" yaml:"PathInContainer,omitempty" toml:"PathInContainer,omitempty"` CgroupPermissions string `json:"CgroupPermissions,omitempty" yaml:"CgroupPermissions,omitempty" toml:"CgroupPermissions,omitempty"` } // DeviceRequest represents a request for device that's sent to device drivers. type DeviceRequest struct { Driver string `json:"Driver,omitempty" yaml:"Driver,omitempty" toml:"Driver,omitempty"` Count int `json:"Count,omitempty" yaml:"Count,omitempty" toml:"Count,omitempty"` DeviceIDs []string `json:"DeviceIDs,omitempty" yaml:"DeviceIDs,omitempty" toml:"DeviceIDs,omitempty"` Capabilities [][]string `json:"Capabilities,omitempty" yaml:"Capabilities,omitempty" toml:"Capabilities,omitempty"` Options map[string]string `json:"Options,omitempty" yaml:"Options,omitempty" toml:"Options,omitempty"` } // BlockWeight represents a relative device weight for an individual device inside // of a container type BlockWeight struct { Path string `json:"Path,omitempty"` Weight string `json:"Weight,omitempty"` } // BlockLimit represents a read/write limit in IOPS or Bandwidth for a device // inside of a container type BlockLimit struct { Path string `json:"Path,omitempty"` Rate int64 `json:"Rate,omitempty"` } // HostConfig contains the container options related to starting a container on // a given host type HostConfig struct { Binds []string `json:"Binds,omitempty" yaml:"Binds,omitempty" toml:"Binds,omitempty"` CapAdd []string `json:"CapAdd,omitempty" yaml:"CapAdd,omitempty" toml:"CapAdd,omitempty"` CapDrop []string `json:"CapDrop,omitempty" yaml:"CapDrop,omitempty" toml:"CapDrop,omitempty"` Capabilities []string `json:"Capabilities,omitempty" yaml:"Capabilities,omitempty" toml:"Capabilities,omitempty"` // Mutually exclusive w.r.t. CapAdd and CapDrop API v1.40 GroupAdd []string `json:"GroupAdd,omitempty" yaml:"GroupAdd,omitempty" toml:"GroupAdd,omitempty"` ContainerIDFile string `json:"ContainerIDFile,omitempty" yaml:"ContainerIDFile,omitempty" toml:"ContainerIDFile,omitempty"` LxcConf []KeyValuePair `json:"LxcConf,omitempty" yaml:"LxcConf,omitempty" toml:"LxcConf,omitempty"` PortBindings map[Port][]PortBinding `json:"PortBindings,omitempty" yaml:"PortBindings,omitempty" toml:"PortBindings,omitempty"` Links []string `json:"Links,omitempty" yaml:"Links,omitempty" toml:"Links,omitempty"` DNS []string `json:"Dns,omitempty" yaml:"Dns,omitempty" toml:"Dns,omitempty"` // For Docker API v1.10 and above only DNSOptions []string `json:"DnsOptions,omitempty" yaml:"DnsOptions,omitempty" toml:"DnsOptions,omitempty"` DNSSearch []string `json:"DnsSearch,omitempty" yaml:"DnsSearch,omitempty" toml:"DnsSearch,omitempty"` ExtraHosts []string `json:"ExtraHosts,omitempty" yaml:"ExtraHosts,omitempty" toml:"ExtraHosts,omitempty"` VolumesFrom []string `json:"VolumesFrom,omitempty" yaml:"VolumesFrom,omitempty" toml:"VolumesFrom,omitempty"` UsernsMode string `json:"UsernsMode,omitempty" yaml:"UsernsMode,omitempty" toml:"UsernsMode,omitempty"` NetworkMode string `json:"NetworkMode,omitempty" yaml:"NetworkMode,omitempty" toml:"NetworkMode,omitempty"` IpcMode string `json:"IpcMode,omitempty" yaml:"IpcMode,omitempty" toml:"IpcMode,omitempty"` Isolation string `json:"Isolation,omitempty" yaml:"Isolation,omitempty" toml:"Isolation,omitempty"` // Windows only ConsoleSize [2]int `json:"ConsoleSize,omitempty" yaml:"ConsoleSize,omitempty" toml:"ConsoleSize,omitempty"` // Windows only height x width PidMode string `json:"PidMode,omitempty" yaml:"PidMode,omitempty" toml:"PidMode,omitempty"` UTSMode string `json:"UTSMode,omitempty" yaml:"UTSMode,omitempty" toml:"UTSMode,omitempty"` RestartPolicy RestartPolicy `json:"RestartPolicy,omitempty" yaml:"RestartPolicy,omitempty" toml:"RestartPolicy,omitempty"` Devices []Device `json:"Devices,omitempty" yaml:"Devices,omitempty" toml:"Devices,omitempty"` DeviceCgroupRules []string `json:"DeviceCgroupRules,omitempty" yaml:"DeviceCgroupRules,omitempty" toml:"DeviceCgroupRules,omitempty"` DeviceRequests []DeviceRequest `json:"DeviceRequests,omitempty" yaml:"DeviceRequests,omitempty" toml:"DeviceRequests,omitempty"` LogConfig LogConfig `json:"LogConfig,omitempty" yaml:"LogConfig,omitempty" toml:"LogConfig,omitempty"` SecurityOpt []string `json:"SecurityOpt,omitempty" yaml:"SecurityOpt,omitempty" toml:"SecurityOpt,omitempty"` CgroupnsMode string `json:"CgroupnsMode,omitempty" yaml:"CgroupnsMode,omitempty" toml:"CgroupnsMode,omitempty"` // v1.40+ Cgroup string `json:"Cgroup,omitempty" yaml:"Cgroup,omitempty" toml:"Cgroup,omitempty"` CgroupParent string `json:"CgroupParent,omitempty" yaml:"CgroupParent,omitempty" toml:"CgroupParent,omitempty"` Memory int64 `json:"Memory,omitempty" yaml:"Memory,omitempty" toml:"Memory,omitempty"` MemoryReservation int64 `json:"MemoryReservation,omitempty" yaml:"MemoryReservation,omitempty" toml:"MemoryReservation,omitempty"` KernelMemory int64 `json:"KernelMemory,omitempty" yaml:"KernelMemory,omitempty" toml:"KernelMemory,omitempty"` MemorySwap int64 `json:"MemorySwap,omitempty" yaml:"MemorySwap,omitempty" toml:"MemorySwap,omitempty"` CPUShares int64 `json:"CpuShares,omitempty" yaml:"CpuShares,omitempty" toml:"CpuShares,omitempty"` CPUSet string `json:"Cpuset,omitempty" yaml:"Cpuset,omitempty" toml:"Cpuset,omitempty"` CPUSetCPUs string `json:"CpusetCpus,omitempty" yaml:"CpusetCpus,omitempty" toml:"CpusetCpus,omitempty"` CPUSetMEMs string `json:"CpusetMems,omitempty" yaml:"CpusetMems,omitempty" toml:"CpusetMems,omitempty"` CPUQuota int64 `json:"CpuQuota,omitempty" yaml:"CpuQuota,omitempty" toml:"CpuQuota,omitempty"` CPUPeriod int64 `json:"CpuPeriod,omitempty" yaml:"CpuPeriod,omitempty" toml:"CpuPeriod,omitempty"` CPURealtimePeriod int64 `json:"CpuRealtimePeriod,omitempty" yaml:"CpuRealtimePeriod,omitempty" toml:"CpuRealtimePeriod,omitempty"` CPURealtimeRuntime int64 `json:"CpuRealtimeRuntime,omitempty" yaml:"CpuRealtimeRuntime,omitempty" toml:"CpuRealtimeRuntime,omitempty"` NanoCPUs int64 `json:"NanoCpus,omitempty" yaml:"NanoCpus,omitempty" toml:"NanoCpus,omitempty"` BlkioWeight int64 `json:"BlkioWeight,omitempty" yaml:"BlkioWeight,omitempty" toml:"BlkioWeight,omitempty"` BlkioWeightDevice []BlockWeight `json:"BlkioWeightDevice,omitempty" yaml:"BlkioWeightDevice,omitempty" toml:"BlkioWeightDevice,omitempty"` BlkioDeviceReadBps []BlockLimit `json:"BlkioDeviceReadBps,omitempty" yaml:"BlkioDeviceReadBps,omitempty" toml:"BlkioDeviceReadBps,omitempty"` BlkioDeviceReadIOps []BlockLimit `json:"BlkioDeviceReadIOps,omitempty" yaml:"BlkioDeviceReadIOps,omitempty" toml:"BlkioDeviceReadIOps,omitempty"` BlkioDeviceWriteBps []BlockLimit `json:"BlkioDeviceWriteBps,omitempty" yaml:"BlkioDeviceWriteBps,omitempty" toml:"BlkioDeviceWriteBps,omitempty"` BlkioDeviceWriteIOps []BlockLimit `json:"BlkioDeviceWriteIOps,omitempty" yaml:"BlkioDeviceWriteIOps,omitempty" toml:"BlkioDeviceWriteIOps,omitempty"` Ulimits []ULimit `json:"Ulimits,omitempty" yaml:"Ulimits,omitempty" toml:"Ulimits,omitempty"` VolumeDriver string `json:"VolumeDriver,omitempty" yaml:"VolumeDriver,omitempty" toml:"VolumeDriver,omitempty"` OomScoreAdj int `json:"OomScoreAdj,omitempty" yaml:"OomScoreAdj,omitempty" toml:"OomScoreAdj,omitempty"` MemorySwappiness *int64 `json:"MemorySwappiness,omitempty" yaml:"MemorySwappiness,omitempty" toml:"MemorySwappiness,omitempty"` PidsLimit *int64 `json:"PidsLimit,omitempty" yaml:"PidsLimit,omitempty" toml:"PidsLimit,omitempty"` OOMKillDisable *bool `json:"OomKillDisable,omitempty" yaml:"OomKillDisable,omitempty" toml:"OomKillDisable,omitempty"` ShmSize int64 `json:"ShmSize,omitempty" yaml:"ShmSize,omitempty" toml:"ShmSize,omitempty"` Tmpfs map[string]string `json:"Tmpfs,omitempty" yaml:"Tmpfs,omitempty" toml:"Tmpfs,omitempty"` StorageOpt map[string]string `json:"StorageOpt,omitempty" yaml:"StorageOpt,omitempty" toml:"StorageOpt,omitempty"` Sysctls map[string]string `json:"Sysctls,omitempty" yaml:"Sysctls,omitempty" toml:"Sysctls,omitempty"` CPUCount int64 `json:"CpuCount,omitempty" yaml:"CpuCount,omitempty"` CPUPercent int64 `json:"CpuPercent,omitempty" yaml:"CpuPercent,omitempty"` IOMaximumBandwidth int64 `json:"IOMaximumBandwidth,omitempty" yaml:"IOMaximumBandwidth,omitempty"` IOMaximumIOps int64 `json:"IOMaximumIOps,omitempty" yaml:"IOMaximumIOps,omitempty"` Mounts []HostMount `json:"Mounts,omitempty" yaml:"Mounts,omitempty" toml:"Mounts,omitempty"` MaskedPaths []string `json:"MaskedPaths,omitempty" yaml:"MaskedPaths,omitempty" toml:"MaskedPaths,omitempty"` ReadonlyPaths []string `json:"ReadonlyPaths,omitempty" yaml:"ReadonlyPaths,omitempty" toml:"ReadonlyPaths,omitempty"` Runtime string `json:"Runtime,omitempty" yaml:"Runtime,omitempty" toml:"Runtime,omitempty"` Init bool `json:",omitempty" yaml:",omitempty"` Privileged bool `json:"Privileged,omitempty" yaml:"Privileged,omitempty" toml:"Privileged,omitempty"` PublishAllPorts bool `json:"PublishAllPorts,omitempty" yaml:"PublishAllPorts,omitempty" toml:"PublishAllPorts,omitempty"` ReadonlyRootfs bool `json:"ReadonlyRootfs,omitempty" yaml:"ReadonlyRootfs,omitempty" toml:"ReadonlyRootfs,omitempty"` AutoRemove bool `json:"AutoRemove,omitempty" yaml:"AutoRemove,omitempty" toml:"AutoRemove,omitempty"` } // NetworkingConfig represents the container's networking configuration for each of its interfaces // Carries the networking configs specified in the `docker run` and `docker network connect` commands type NetworkingConfig struct { EndpointsConfig map[string]*EndpointConfig `json:"EndpointsConfig" yaml:"EndpointsConfig" toml:"EndpointsConfig"` // Endpoint configs for each connecting network } // NoSuchContainer is the error returned when a given container does not exist. type NoSuchContainer struct { ID string Err error } func (err *NoSuchContainer) Error() string { if err.Err != nil { return err.Err.Error() } return "No such container: " + err.ID } // ContainerAlreadyRunning is the error returned when a given container is // already running. type ContainerAlreadyRunning struct { ID string } func (err *ContainerAlreadyRunning) Error() string { return "Container already running: " + err.ID } // ContainerNotRunning is the error returned when a given container is not // running. type ContainerNotRunning struct { ID string } func (err *ContainerNotRunning) Error() string { return "Container not running: " + err.ID } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/container_archive.go ================================================ package docker import ( "context" "fmt" "io" "net/http" "time" ) // UploadToContainerOptions is the set of options that can be used when // uploading an archive into a container. // // See https://goo.gl/g25o7u for more details. type UploadToContainerOptions struct { InputStream io.Reader `json:"-" qs:"-"` Path string `qs:"path"` NoOverwriteDirNonDir bool `qs:"noOverwriteDirNonDir"` Context context.Context } // UploadToContainer uploads a tar archive to be extracted to a path in the // filesystem of the container. // // See https://goo.gl/g25o7u for more details. func (c *Client) UploadToContainer(id string, opts UploadToContainerOptions) error { url := fmt.Sprintf("/containers/%s/archive?", id) + queryString(opts) return c.stream(http.MethodPut, url, streamOptions{ in: opts.InputStream, context: opts.Context, }) } // DownloadFromContainerOptions is the set of options that can be used when // downloading resources from a container. // // See https://goo.gl/W49jxK for more details. type DownloadFromContainerOptions struct { OutputStream io.Writer `json:"-" qs:"-"` Path string `qs:"path"` InactivityTimeout time.Duration `qs:"-"` Context context.Context } // DownloadFromContainer downloads a tar archive of files or folders in a container. // // See https://goo.gl/W49jxK for more details. func (c *Client) DownloadFromContainer(id string, opts DownloadFromContainerOptions) error { url := fmt.Sprintf("/containers/%s/archive?", id) + queryString(opts) return c.stream(http.MethodGet, url, streamOptions{ setRawTerminal: true, stdout: opts.OutputStream, inactivityTimeout: opts.InactivityTimeout, context: opts.Context, }) } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/container_attach.go ================================================ package docker import ( "io" "net/http" ) // AttachToContainerOptions is the set of options that can be used when // attaching to a container. // // See https://goo.gl/JF10Zk for more details. type AttachToContainerOptions struct { Container string `qs:"-"` InputStream io.Reader `qs:"-"` OutputStream io.Writer `qs:"-"` ErrorStream io.Writer `qs:"-"` // If set, after a successful connect, a sentinel will be sent and then the // client will block on receive before continuing. // // It must be an unbuffered channel. Using a buffered channel can lead // to unexpected behavior. Success chan struct{} // Override the key sequence for detaching a container. DetachKeys string // Use raw terminal? Usually true when the container contains a TTY. RawTerminal bool `qs:"-"` // Get container logs, sending it to OutputStream. Logs bool // Stream the response? Stream bool // Attach to stdin, and use InputStream. Stdin bool // Attach to stdout, and use OutputStream. Stdout bool // Attach to stderr, and use ErrorStream. Stderr bool } // AttachToContainer attaches to a container, using the given options. // // See https://goo.gl/JF10Zk for more details. func (c *Client) AttachToContainer(opts AttachToContainerOptions) error { cw, err := c.AttachToContainerNonBlocking(opts) if err != nil { return err } return cw.Wait() } // AttachToContainerNonBlocking attaches to a container, using the given options. // This function does not block. // // See https://goo.gl/NKpkFk for more details. func (c *Client) AttachToContainerNonBlocking(opts AttachToContainerOptions) (CloseWaiter, error) { if opts.Container == "" { return nil, &NoSuchContainer{ID: opts.Container} } path := "/containers/" + opts.Container + "/attach?" + queryString(opts) return c.hijack(http.MethodPost, path, hijackOptions{ success: opts.Success, setRawTerminal: opts.RawTerminal, in: opts.InputStream, stdout: opts.OutputStream, stderr: opts.ErrorStream, }) } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/container_changes.go ================================================ package docker import ( "encoding/json" "errors" "net/http" ) // ContainerChanges returns changes in the filesystem of the given container. // // See https://goo.gl/15KKzh for more details. func (c *Client) ContainerChanges(id string) ([]Change, error) { path := "/containers/" + id + "/changes" resp, err := c.do(http.MethodGet, path, doOptions{}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return nil, &NoSuchContainer{ID: id} } return nil, err } defer resp.Body.Close() var changes []Change if err := json.NewDecoder(resp.Body).Decode(&changes); err != nil { return nil, err } return changes, nil } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/container_commit.go ================================================ package docker import ( "context" "encoding/json" "errors" "net/http" ) // CommitContainerOptions aggregates parameters to the CommitContainer method. // // See https://goo.gl/CzIguf for more details. type CommitContainerOptions struct { Container string Repository string `qs:"repo"` Tag string Message string `qs:"comment"` Author string Changes []string `qs:"changes"` Run *Config `qs:"-"` Context context.Context } // CommitContainer creates a new image from a container's changes. // // See https://goo.gl/CzIguf for more details. func (c *Client) CommitContainer(opts CommitContainerOptions) (*Image, error) { path := "/commit?" + queryString(opts) resp, err := c.do(http.MethodPost, path, doOptions{ data: opts.Run, context: opts.Context, }) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return nil, &NoSuchContainer{ID: opts.Container} } return nil, err } defer resp.Body.Close() var image Image if err := json.NewDecoder(resp.Body).Decode(&image); err != nil { return nil, err } return &image, nil } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/container_copy.go ================================================ package docker import ( "context" "errors" "fmt" "io" "net/http" ) // CopyFromContainerOptions contains the set of options used for copying // files from a container. // // Deprecated: Use DownloadFromContainerOptions and DownloadFromContainer instead. type CopyFromContainerOptions struct { OutputStream io.Writer `json:"-"` Container string `json:"-"` Resource string Context context.Context `json:"-"` } // CopyFromContainer copies files from a container. // // Deprecated: Use DownloadFromContainer and DownloadFromContainer instead. func (c *Client) CopyFromContainer(opts CopyFromContainerOptions) error { if opts.Container == "" { return &NoSuchContainer{ID: opts.Container} } if c.serverAPIVersion == nil { c.checkAPIVersion() } if c.serverAPIVersion != nil && c.serverAPIVersion.GreaterThanOrEqualTo(apiVersion124) { return errors.New("go-dockerclient: CopyFromContainer is no longer available in Docker >= 1.12, use DownloadFromContainer instead") } url := fmt.Sprintf("/containers/%s/copy", opts.Container) resp, err := c.do(http.MethodPost, url, doOptions{ data: opts, context: opts.Context, }) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return &NoSuchContainer{ID: opts.Container} } return err } defer resp.Body.Close() _, err = io.Copy(opts.OutputStream, resp.Body) return err } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/container_create.go ================================================ package docker import ( "context" "encoding/json" "errors" "net/http" "strings" ) // ErrContainerAlreadyExists is the error returned by CreateContainer when the // container already exists. var ErrContainerAlreadyExists = errors.New("container already exists") // CreateContainerOptions specify parameters to the CreateContainer function. // // See https://goo.gl/tyzwVM for more details. type CreateContainerOptions struct { Name string Platform string Config *Config `qs:"-"` HostConfig *HostConfig `qs:"-"` NetworkingConfig *NetworkingConfig `qs:"-"` Context context.Context } // CreateContainer creates a new container, returning the container instance, // or an error in case of failure. // // The returned container instance contains only the container ID. To get more // details about the container after creating it, use InspectContainer. // // See https://goo.gl/tyzwVM for more details. func (c *Client) CreateContainer(opts CreateContainerOptions) (*Container, error) { path := "/containers/create?" + queryString(opts) resp, err := c.do( http.MethodPost, path, doOptions{ data: struct { *Config HostConfig *HostConfig `json:"HostConfig,omitempty" yaml:"HostConfig,omitempty" toml:"HostConfig,omitempty"` NetworkingConfig *NetworkingConfig `json:"NetworkingConfig,omitempty" yaml:"NetworkingConfig,omitempty" toml:"NetworkingConfig,omitempty"` }{ opts.Config, opts.HostConfig, opts.NetworkingConfig, }, context: opts.Context, }, ) var e *Error if errors.As(err, &e) { if e.Status == http.StatusNotFound && strings.Contains(e.Message, "No such image") { return nil, ErrNoSuchImage } if e.Status == http.StatusConflict { return nil, ErrContainerAlreadyExists } // Workaround for 17.09 bug returning 400 instead of 409. // See https://github.com/moby/moby/issues/35021 if e.Status == http.StatusBadRequest && strings.Contains(e.Message, "Conflict.") { return nil, ErrContainerAlreadyExists } } if err != nil { return nil, err } defer resp.Body.Close() var container Container if err := json.NewDecoder(resp.Body).Decode(&container); err != nil { return nil, err } container.Name = opts.Name return &container, nil } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/container_export.go ================================================ package docker import ( "context" "fmt" "io" "net/http" "time" ) // ExportContainerOptions is the set of parameters to the ExportContainer // method. // // See https://goo.gl/yGJCIh for more details. type ExportContainerOptions struct { ID string OutputStream io.Writer InactivityTimeout time.Duration `qs:"-"` Context context.Context } // ExportContainer export the contents of container id as tar archive // and prints the exported contents to stdout. // // See https://goo.gl/yGJCIh for more details. func (c *Client) ExportContainer(opts ExportContainerOptions) error { if opts.ID == "" { return &NoSuchContainer{ID: opts.ID} } url := fmt.Sprintf("/containers/%s/export", opts.ID) return c.stream(http.MethodGet, url, streamOptions{ setRawTerminal: true, stdout: opts.OutputStream, inactivityTimeout: opts.InactivityTimeout, context: opts.Context, }) } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/container_inspect.go ================================================ package docker import ( "context" "encoding/json" "errors" "net/http" ) // InspectContainer returns information about a container by its ID. // // Deprecated: Use InspectContainerWithOptions instead. func (c *Client) InspectContainer(id string) (*Container, error) { return c.InspectContainerWithOptions(InspectContainerOptions{ID: id}) } // InspectContainerWithContext returns information about a container by its ID. // The context object can be used to cancel the inspect request. // // Deprecated: Use InspectContainerWithOptions instead. func (c *Client) InspectContainerWithContext(id string, ctx context.Context) (*Container, error) { return c.InspectContainerWithOptions(InspectContainerOptions{ID: id, Context: ctx}) } // InspectContainerWithOptions returns information about a container by its ID. // // See https://goo.gl/FaI5JT for more details. func (c *Client) InspectContainerWithOptions(opts InspectContainerOptions) (*Container, error) { path := "/containers/" + opts.ID + "/json?" + queryString(opts) resp, err := c.do(http.MethodGet, path, doOptions{ context: opts.Context, }) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return nil, &NoSuchContainer{ID: opts.ID} } return nil, err } defer resp.Body.Close() var container Container if err := json.NewDecoder(resp.Body).Decode(&container); err != nil { return nil, err } return &container, nil } // InspectContainerOptions specifies parameters for InspectContainerWithOptions. // // See https://goo.gl/FaI5JT for more details. type InspectContainerOptions struct { Context context.Context ID string `qs:"-"` Size bool } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/container_kill.go ================================================ package docker import ( "context" "errors" "net/http" ) // KillContainerOptions represents the set of options that can be used in a // call to KillContainer. // // See https://goo.gl/JnTxXZ for more details. type KillContainerOptions struct { // The ID of the container. ID string `qs:"-"` // The signal to send to the container. When omitted, Docker server // will assume SIGKILL. Signal Signal Context context.Context } // KillContainer sends a signal to a container, returning an error in case of // failure. // // See https://goo.gl/JnTxXZ for more details. func (c *Client) KillContainer(opts KillContainerOptions) error { path := "/containers/" + opts.ID + "/kill" + "?" + queryString(opts) resp, err := c.do(http.MethodPost, path, doOptions{context: opts.Context}) if err != nil { var e *Error if !errors.As(err, &e) { return err } switch e.Status { case http.StatusNotFound: return &NoSuchContainer{ID: opts.ID} case http.StatusConflict: return &ContainerNotRunning{ID: opts.ID} default: return err } } resp.Body.Close() return nil } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/container_list.go ================================================ package docker import ( "context" "encoding/json" "net/http" ) // ListContainersOptions specify parameters to the ListContainers function. // // See https://goo.gl/kaOHGw for more details. type ListContainersOptions struct { All bool Size bool Limit int Since string Before string Filters map[string][]string Context context.Context } // ListContainers returns a slice of containers matching the given criteria. // // See https://goo.gl/kaOHGw for more details. func (c *Client) ListContainers(opts ListContainersOptions) ([]APIContainers, error) { path := "/containers/json?" + queryString(opts) resp, err := c.do(http.MethodGet, path, doOptions{context: opts.Context}) if err != nil { return nil, err } defer resp.Body.Close() var containers []APIContainers if err := json.NewDecoder(resp.Body).Decode(&containers); err != nil { return nil, err } return containers, nil } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/container_logs.go ================================================ package docker import ( "context" "io" "net/http" "time" ) // LogsOptions represents the set of options used when getting logs from a // container. // // See https://goo.gl/krK0ZH for more details. type LogsOptions struct { Context context.Context Container string `qs:"-"` OutputStream io.Writer `qs:"-"` ErrorStream io.Writer `qs:"-"` InactivityTimeout time.Duration `qs:"-"` Tail string Since int64 Follow bool Stdout bool Stderr bool Timestamps bool // Use raw terminal? Usually true when the container contains a TTY. RawTerminal bool `qs:"-"` } // Logs gets stdout and stderr logs from the specified container. // // When LogsOptions.RawTerminal is set to false, go-dockerclient will multiplex // the streams and send the containers stdout to LogsOptions.OutputStream, and // stderr to LogsOptions.ErrorStream. // // When LogsOptions.RawTerminal is true, callers will get the raw stream on // LogsOptions.OutputStream. The caller can use libraries such as dlog // (github.com/ahmetalpbalkan/dlog). // // See https://goo.gl/krK0ZH for more details. func (c *Client) Logs(opts LogsOptions) error { if opts.Container == "" { return &NoSuchContainer{ID: opts.Container} } if opts.Tail == "" { opts.Tail = "all" } path := "/containers/" + opts.Container + "/logs?" + queryString(opts) return c.stream(http.MethodGet, path, streamOptions{ setRawTerminal: opts.RawTerminal, stdout: opts.OutputStream, stderr: opts.ErrorStream, inactivityTimeout: opts.InactivityTimeout, context: opts.Context, }) } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/container_pause.go ================================================ package docker import ( "errors" "fmt" "net/http" ) // PauseContainer pauses the given container. // // See https://goo.gl/D1Yaii for more details. func (c *Client) PauseContainer(id string) error { path := fmt.Sprintf("/containers/%s/pause", id) resp, err := c.do(http.MethodPost, path, doOptions{}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return &NoSuchContainer{ID: id} } return err } resp.Body.Close() return nil } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/container_prune.go ================================================ package docker import ( "context" "encoding/json" "net/http" ) // PruneContainersOptions specify parameters to the PruneContainers function. // // See https://goo.gl/wnkgDT for more details. type PruneContainersOptions struct { Filters map[string][]string Context context.Context } // PruneContainersResults specify results from the PruneContainers function. // // See https://goo.gl/wnkgDT for more details. type PruneContainersResults struct { ContainersDeleted []string SpaceReclaimed int64 } // PruneContainers deletes containers which are stopped. // // See https://goo.gl/wnkgDT for more details. func (c *Client) PruneContainers(opts PruneContainersOptions) (*PruneContainersResults, error) { path := "/containers/prune?" + queryString(opts) resp, err := c.do(http.MethodPost, path, doOptions{context: opts.Context}) if err != nil { return nil, err } defer resp.Body.Close() var results PruneContainersResults if err := json.NewDecoder(resp.Body).Decode(&results); err != nil { return nil, err } return &results, nil } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/container_remove.go ================================================ package docker import ( "context" "errors" "net/http" ) // RemoveContainerOptions encapsulates options to remove a container. // // See https://goo.gl/hL5IPC for more details. type RemoveContainerOptions struct { // The ID of the container. ID string `qs:"-"` // A flag that indicates whether Docker should remove the volumes // associated to the container. RemoveVolumes bool `qs:"v"` // A flag that indicates whether Docker should remove the container // even if it is currently running. Force bool Context context.Context } // RemoveContainer removes a container, returning an error in case of failure. // // See https://goo.gl/hL5IPC for more details. func (c *Client) RemoveContainer(opts RemoveContainerOptions) error { path := "/containers/" + opts.ID + "?" + queryString(opts) resp, err := c.do(http.MethodDelete, path, doOptions{context: opts.Context}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return &NoSuchContainer{ID: opts.ID} } return err } resp.Body.Close() return nil } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/container_rename.go ================================================ package docker import ( "context" "fmt" "net/http" ) // RenameContainerOptions specify parameters to the RenameContainer function. // // See https://goo.gl/46inai for more details. type RenameContainerOptions struct { // ID of container to rename ID string `qs:"-"` // New name Name string `json:"name,omitempty" yaml:"name,omitempty"` Context context.Context } // RenameContainer updates and existing containers name // // See https://goo.gl/46inai for more details. func (c *Client) RenameContainer(opts RenameContainerOptions) error { resp, err := c.do(http.MethodPost, fmt.Sprintf("/containers/"+opts.ID+"/rename?%s", queryString(opts)), doOptions{ context: opts.Context, }) if err != nil { return err } resp.Body.Close() return nil } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/container_resize.go ================================================ package docker import ( "net/http" "net/url" "strconv" ) // ResizeContainerTTY resizes the terminal to the given height and width. // // See https://goo.gl/FImjeq for more details. func (c *Client) ResizeContainerTTY(id string, height, width int) error { params := make(url.Values) params.Set("h", strconv.Itoa(height)) params.Set("w", strconv.Itoa(width)) resp, err := c.do(http.MethodPost, "/containers/"+id+"/resize?"+params.Encode(), doOptions{}) if err != nil { return err } resp.Body.Close() return nil } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/container_restart.go ================================================ package docker import ( "errors" "fmt" "net/http" ) // RestartPolicy represents the policy for automatically restarting a container. // // Possible values are: // // - always: the docker daemon will always restart the container // - on-failure: the docker daemon will restart the container on failures, at // most MaximumRetryCount times // - unless-stopped: the docker daemon will always restart the container except // when user has manually stopped the container // - no: the docker daemon will not restart the container automatically type RestartPolicy struct { Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"` MaximumRetryCount int `json:"MaximumRetryCount,omitempty" yaml:"MaximumRetryCount,omitempty" toml:"MaximumRetryCount,omitempty"` } // AlwaysRestart returns a restart policy that tells the Docker daemon to // always restart the container. func AlwaysRestart() RestartPolicy { return RestartPolicy{Name: "always"} } // RestartOnFailure returns a restart policy that tells the Docker daemon to // restart the container on failures, trying at most maxRetry times. func RestartOnFailure(maxRetry int) RestartPolicy { return RestartPolicy{Name: "on-failure", MaximumRetryCount: maxRetry} } // RestartUnlessStopped returns a restart policy that tells the Docker daemon to // always restart the container except when user has manually stopped the container. func RestartUnlessStopped() RestartPolicy { return RestartPolicy{Name: "unless-stopped"} } // NeverRestart returns a restart policy that tells the Docker daemon to never // restart the container on failures. func NeverRestart() RestartPolicy { return RestartPolicy{Name: "no"} } // RestartContainer stops a container, killing it after the given timeout (in // seconds), during the stop process. // // See https://goo.gl/MrAKQ5 for more details. func (c *Client) RestartContainer(id string, timeout uint) error { path := fmt.Sprintf("/containers/%s/restart?t=%d", id, timeout) resp, err := c.do(http.MethodPost, path, doOptions{}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return &NoSuchContainer{ID: id} } return err } resp.Body.Close() return nil } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/container_start.go ================================================ package docker import ( "context" "errors" "net/http" ) // StartContainer starts a container, returning an error in case of failure. // // Passing the HostConfig to this method has been deprecated in Docker API 1.22 // (Docker Engine 1.10.x) and totally removed in Docker API 1.24 (Docker Engine // 1.12.x). The client will ignore the parameter when communicating with Docker // API 1.24 or greater. // // See https://goo.gl/fbOSZy for more details. func (c *Client) StartContainer(id string, hostConfig *HostConfig) error { return c.startContainer(id, hostConfig, doOptions{}) } // StartContainerWithContext starts a container, returning an error in case of // failure. The context can be used to cancel the outstanding start container // request. // // Passing the HostConfig to this method has been deprecated in Docker API 1.22 // (Docker Engine 1.10.x) and totally removed in Docker API 1.24 (Docker Engine // 1.12.x). The client will ignore the parameter when communicating with Docker // API 1.24 or greater. // // See https://goo.gl/fbOSZy for more details. func (c *Client) StartContainerWithContext(id string, hostConfig *HostConfig, ctx context.Context) error { return c.startContainer(id, hostConfig, doOptions{context: ctx}) } func (c *Client) startContainer(id string, hostConfig *HostConfig, opts doOptions) error { path := "/containers/" + id + "/start" if c.serverAPIVersion == nil { c.checkAPIVersion() } if c.serverAPIVersion != nil && c.serverAPIVersion.LessThan(apiVersion124) { opts.data = hostConfig opts.forceJSON = true } resp, err := c.do(http.MethodPost, path, opts) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return &NoSuchContainer{ID: id, Err: err} } return err } defer resp.Body.Close() if resp.StatusCode == http.StatusNotModified { return &ContainerAlreadyRunning{ID: id} } return nil } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/container_stats.go ================================================ package docker import ( "context" "encoding/json" "errors" "fmt" "io" "net/http" "time" ) // Stats represents container statistics, returned by /containers//stats. // // See https://goo.gl/Dk3Xio for more details. type Stats struct { Read time.Time `json:"read,omitempty" yaml:"read,omitempty" toml:"read,omitempty"` PreRead time.Time `json:"preread,omitempty" yaml:"preread,omitempty" toml:"preread,omitempty"` NumProcs uint32 `json:"num_procs" yaml:"num_procs" toml:"num_procs"` PidsStats struct { Current uint64 `json:"current,omitempty" yaml:"current,omitempty"` } `json:"pids_stats,omitempty" yaml:"pids_stats,omitempty" toml:"pids_stats,omitempty"` Network NetworkStats `json:"network,omitempty" yaml:"network,omitempty" toml:"network,omitempty"` Networks map[string]NetworkStats `json:"networks,omitempty" yaml:"networks,omitempty" toml:"networks,omitempty"` MemoryStats struct { Stats struct { TotalPgmafault uint64 `json:"total_pgmafault,omitempty" yaml:"total_pgmafault,omitempty" toml:"total_pgmafault,omitempty"` Cache uint64 `json:"cache,omitempty" yaml:"cache,omitempty" toml:"cache,omitempty"` MappedFile uint64 `json:"mapped_file,omitempty" yaml:"mapped_file,omitempty" toml:"mapped_file,omitempty"` TotalInactiveFile uint64 `json:"total_inactive_file,omitempty" yaml:"total_inactive_file,omitempty" toml:"total_inactive_file,omitempty"` Pgpgout uint64 `json:"pgpgout,omitempty" yaml:"pgpgout,omitempty" toml:"pgpgout,omitempty"` Rss uint64 `json:"rss,omitempty" yaml:"rss,omitempty" toml:"rss,omitempty"` TotalMappedFile uint64 `json:"total_mapped_file,omitempty" yaml:"total_mapped_file,omitempty" toml:"total_mapped_file,omitempty"` Writeback uint64 `json:"writeback,omitempty" yaml:"writeback,omitempty" toml:"writeback,omitempty"` Unevictable uint64 `json:"unevictable,omitempty" yaml:"unevictable,omitempty" toml:"unevictable,omitempty"` Pgpgin uint64 `json:"pgpgin,omitempty" yaml:"pgpgin,omitempty" toml:"pgpgin,omitempty"` TotalUnevictable uint64 `json:"total_unevictable,omitempty" yaml:"total_unevictable,omitempty" toml:"total_unevictable,omitempty"` Pgmajfault uint64 `json:"pgmajfault,omitempty" yaml:"pgmajfault,omitempty" toml:"pgmajfault,omitempty"` TotalRss uint64 `json:"total_rss,omitempty" yaml:"total_rss,omitempty" toml:"total_rss,omitempty"` TotalRssHuge uint64 `json:"total_rss_huge,omitempty" yaml:"total_rss_huge,omitempty" toml:"total_rss_huge,omitempty"` TotalWriteback uint64 `json:"total_writeback,omitempty" yaml:"total_writeback,omitempty" toml:"total_writeback,omitempty"` TotalInactiveAnon uint64 `json:"total_inactive_anon,omitempty" yaml:"total_inactive_anon,omitempty" toml:"total_inactive_anon,omitempty"` RssHuge uint64 `json:"rss_huge,omitempty" yaml:"rss_huge,omitempty" toml:"rss_huge,omitempty"` HierarchicalMemoryLimit uint64 `json:"hierarchical_memory_limit,omitempty" yaml:"hierarchical_memory_limit,omitempty" toml:"hierarchical_memory_limit,omitempty"` TotalPgfault uint64 `json:"total_pgfault,omitempty" yaml:"total_pgfault,omitempty" toml:"total_pgfault,omitempty"` TotalActiveFile uint64 `json:"total_active_file,omitempty" yaml:"total_active_file,omitempty" toml:"total_active_file,omitempty"` ActiveAnon uint64 `json:"active_anon,omitempty" yaml:"active_anon,omitempty" toml:"active_anon,omitempty"` TotalActiveAnon uint64 `json:"total_active_anon,omitempty" yaml:"total_active_anon,omitempty" toml:"total_active_anon,omitempty"` TotalPgpgout uint64 `json:"total_pgpgout,omitempty" yaml:"total_pgpgout,omitempty" toml:"total_pgpgout,omitempty"` TotalCache uint64 `json:"total_cache,omitempty" yaml:"total_cache,omitempty" toml:"total_cache,omitempty"` InactiveAnon uint64 `json:"inactive_anon,omitempty" yaml:"inactive_anon,omitempty" toml:"inactive_anon,omitempty"` ActiveFile uint64 `json:"active_file,omitempty" yaml:"active_file,omitempty" toml:"active_file,omitempty"` Pgfault uint64 `json:"pgfault,omitempty" yaml:"pgfault,omitempty" toml:"pgfault,omitempty"` InactiveFile uint64 `json:"inactive_file,omitempty" yaml:"inactive_file,omitempty" toml:"inactive_file,omitempty"` TotalPgpgin uint64 `json:"total_pgpgin,omitempty" yaml:"total_pgpgin,omitempty" toml:"total_pgpgin,omitempty"` HierarchicalMemswLimit uint64 `json:"hierarchical_memsw_limit,omitempty" yaml:"hierarchical_memsw_limit,omitempty" toml:"hierarchical_memsw_limit,omitempty"` Swap uint64 `json:"swap,omitempty" yaml:"swap,omitempty" toml:"swap,omitempty"` Anon uint64 `json:"anon,omitempty" yaml:"anon,omitempty" toml:"anon,omitempty"` AnonThp uint64 `json:"anon_thp,omitempty" yaml:"anon_thp,omitempty" toml:"anon_thp,omitempty"` File uint64 `json:"file,omitempty" yaml:"file,omitempty" toml:"file,omitempty"` FileDirty uint64 `json:"file_dirty,omitempty" yaml:"file_dirty,omitempty" toml:"file_dirty,omitempty"` FileMapped uint64 `json:"file_mapped,omitempty" yaml:"file_mapped,omitempty" toml:"file_mapped,omitempty"` FileWriteback uint64 `json:"file_writeback,omitempty" yaml:"file_writeback,omitempty" toml:"file_writeback,omitempty"` KernelStack uint64 `json:"kernel_stack,omitempty" yaml:"kernel_stack,omitempty" toml:"kernel_stack,omitempty"` Pgactivate uint64 `json:"pgactivate,omitempty" yaml:"pgactivate,omitempty" toml:"pgactivate,omitempty"` Pgdeactivate uint64 `json:"pgdeactivate,omitempty" yaml:"pgdeactivate,omitempty" toml:"pgdeactivate,omitempty"` Pglazyfree uint64 `json:"pglazyfree,omitempty" yaml:"pglazyfree,omitempty" toml:"pglazyfree,omitempty"` Pglazyfreed uint64 `json:"pglazyfreed,omitempty" yaml:"pglazyfreed,omitempty" toml:"pglazyfreed,omitempty"` Pgrefill uint64 `json:"pgrefill,omitempty" yaml:"pgrefill,omitempty" toml:"pgrefill,omitempty"` Pgscan uint64 `json:"pgscan,omitempty" yaml:"pgscan,omitempty" toml:"pgscan,omitempty"` Pgsteal uint64 `json:"pgsteal,omitempty" yaml:"pgsteal,omitempty" toml:"pgsteal,omitempty"` Shmem uint64 `json:"shmem,omitempty" yaml:"shmem,omitempty" toml:"shmem,omitempty"` Slab uint64 `json:"slab,omitempty" yaml:"slab,omitempty" toml:"slab,omitempty"` SlabReclaimable uint64 `json:"slab_reclaimable,omitempty" yaml:"slab_reclaimable,omitempty" toml:"slab_reclaimable,omitempty"` SlabUnreclaimable uint64 `json:"slab_unreclaimable,omitempty" yaml:"slab_unreclaimable,omitempty" toml:"slab_unreclaimable,omitempty"` Sock uint64 `json:"sock,omitempty" yaml:"sock,omitempty" toml:"sock,omitempty"` ThpCollapseAlloc uint64 `json:"thp_collapse_alloc,omitempty" yaml:"thp_collapse_alloc,omitempty" toml:"thp_collapse_alloc,omitempty"` ThpFaultAlloc uint64 `json:"thp_fault_alloc,omitempty" yaml:"thp_fault_alloc,omitempty" toml:"thp_fault_alloc,omitempty"` WorkingsetActivate uint64 `json:"workingset_activate,omitempty" yaml:"workingset_activate,omitempty" toml:"workingset_activate,omitempty"` WorkingsetNodereclaim uint64 `json:"workingset_nodereclaim,omitempty" yaml:"workingset_nodereclaim,omitempty" toml:"workingset_nodereclaim,omitempty"` WorkingsetRefault uint64 `json:"workingset_refault,omitempty" yaml:"workingset_refault,omitempty" toml:"workingset_refault,omitempty"` } `json:"stats,omitempty" yaml:"stats,omitempty" toml:"stats,omitempty"` MaxUsage uint64 `json:"max_usage,omitempty" yaml:"max_usage,omitempty" toml:"max_usage,omitempty"` Usage uint64 `json:"usage,omitempty" yaml:"usage,omitempty" toml:"usage,omitempty"` Failcnt uint64 `json:"failcnt,omitempty" yaml:"failcnt,omitempty" toml:"failcnt,omitempty"` Limit uint64 `json:"limit,omitempty" yaml:"limit,omitempty" toml:"limit,omitempty"` Commit uint64 `json:"commitbytes,omitempty" yaml:"commitbytes,omitempty" toml:"privateworkingset,omitempty"` CommitPeak uint64 `json:"commitpeakbytes,omitempty" yaml:"commitpeakbytes,omitempty" toml:"commitpeakbytes,omitempty"` PrivateWorkingSet uint64 `json:"privateworkingset,omitempty" yaml:"privateworkingset,omitempty" toml:"privateworkingset,omitempty"` } `json:"memory_stats,omitempty" yaml:"memory_stats,omitempty" toml:"memory_stats,omitempty"` BlkioStats struct { IOServiceBytesRecursive []BlkioStatsEntry `json:"io_service_bytes_recursive,omitempty" yaml:"io_service_bytes_recursive,omitempty" toml:"io_service_bytes_recursive,omitempty"` IOServicedRecursive []BlkioStatsEntry `json:"io_serviced_recursive,omitempty" yaml:"io_serviced_recursive,omitempty" toml:"io_serviced_recursive,omitempty"` IOQueueRecursive []BlkioStatsEntry `json:"io_queue_recursive,omitempty" yaml:"io_queue_recursive,omitempty" toml:"io_queue_recursive,omitempty"` IOServiceTimeRecursive []BlkioStatsEntry `json:"io_service_time_recursive,omitempty" yaml:"io_service_time_recursive,omitempty" toml:"io_service_time_recursive,omitempty"` IOWaitTimeRecursive []BlkioStatsEntry `json:"io_wait_time_recursive,omitempty" yaml:"io_wait_time_recursive,omitempty" toml:"io_wait_time_recursive,omitempty"` IOMergedRecursive []BlkioStatsEntry `json:"io_merged_recursive,omitempty" yaml:"io_merged_recursive,omitempty" toml:"io_merged_recursive,omitempty"` IOTimeRecursive []BlkioStatsEntry `json:"io_time_recursive,omitempty" yaml:"io_time_recursive,omitempty" toml:"io_time_recursive,omitempty"` SectorsRecursive []BlkioStatsEntry `json:"sectors_recursive,omitempty" yaml:"sectors_recursive,omitempty" toml:"sectors_recursive,omitempty"` } `json:"blkio_stats,omitempty" yaml:"blkio_stats,omitempty" toml:"blkio_stats,omitempty"` CPUStats CPUStats `json:"cpu_stats,omitempty" yaml:"cpu_stats,omitempty" toml:"cpu_stats,omitempty"` PreCPUStats CPUStats `json:"precpu_stats,omitempty"` StorageStats struct { ReadCountNormalized uint64 `json:"read_count_normalized,omitempty" yaml:"read_count_normalized,omitempty" toml:"read_count_normalized,omitempty"` ReadSizeBytes uint64 `json:"read_size_bytes,omitempty" yaml:"read_size_bytes,omitempty" toml:"read_size_bytes,omitempty"` WriteCountNormalized uint64 `json:"write_count_normalized,omitempty" yaml:"write_count_normalized,omitempty" toml:"write_count_normalized,omitempty"` WriteSizeBytes uint64 `json:"write_size_bytes,omitempty" yaml:"write_size_bytes,omitempty" toml:"write_size_bytes,omitempty"` } `json:"storage_stats,omitempty" yaml:"storage_stats,omitempty" toml:"storage_stats,omitempty"` } // NetworkStats is a stats entry for network stats type NetworkStats struct { RxDropped uint64 `json:"rx_dropped,omitempty" yaml:"rx_dropped,omitempty" toml:"rx_dropped,omitempty"` RxBytes uint64 `json:"rx_bytes,omitempty" yaml:"rx_bytes,omitempty" toml:"rx_bytes,omitempty"` RxErrors uint64 `json:"rx_errors,omitempty" yaml:"rx_errors,omitempty" toml:"rx_errors,omitempty"` TxPackets uint64 `json:"tx_packets,omitempty" yaml:"tx_packets,omitempty" toml:"tx_packets,omitempty"` TxDropped uint64 `json:"tx_dropped,omitempty" yaml:"tx_dropped,omitempty" toml:"tx_dropped,omitempty"` RxPackets uint64 `json:"rx_packets,omitempty" yaml:"rx_packets,omitempty" toml:"rx_packets,omitempty"` TxErrors uint64 `json:"tx_errors,omitempty" yaml:"tx_errors,omitempty" toml:"tx_errors,omitempty"` TxBytes uint64 `json:"tx_bytes,omitempty" yaml:"tx_bytes,omitempty" toml:"tx_bytes,omitempty"` } // CPUStats is a stats entry for cpu stats type CPUStats struct { CPUUsage struct { PercpuUsage []uint64 `json:"percpu_usage,omitempty" yaml:"percpu_usage,omitempty" toml:"percpu_usage,omitempty"` UsageInUsermode uint64 `json:"usage_in_usermode,omitempty" yaml:"usage_in_usermode,omitempty" toml:"usage_in_usermode,omitempty"` TotalUsage uint64 `json:"total_usage,omitempty" yaml:"total_usage,omitempty" toml:"total_usage,omitempty"` UsageInKernelmode uint64 `json:"usage_in_kernelmode,omitempty" yaml:"usage_in_kernelmode,omitempty" toml:"usage_in_kernelmode,omitempty"` } `json:"cpu_usage,omitempty" yaml:"cpu_usage,omitempty" toml:"cpu_usage,omitempty"` SystemCPUUsage uint64 `json:"system_cpu_usage,omitempty" yaml:"system_cpu_usage,omitempty" toml:"system_cpu_usage,omitempty"` OnlineCPUs uint64 `json:"online_cpus,omitempty" yaml:"online_cpus,omitempty" toml:"online_cpus,omitempty"` ThrottlingData struct { Periods uint64 `json:"periods,omitempty"` ThrottledPeriods uint64 `json:"throttled_periods,omitempty"` ThrottledTime uint64 `json:"throttled_time,omitempty"` } `json:"throttling_data,omitempty" yaml:"throttling_data,omitempty" toml:"throttling_data,omitempty"` } // BlkioStatsEntry is a stats entry for blkio_stats type BlkioStatsEntry struct { Major uint64 `json:"major,omitempty" yaml:"major,omitempty" toml:"major,omitempty"` Minor uint64 `json:"minor,omitempty" yaml:"minor,omitempty" toml:"minor,omitempty"` Op string `json:"op,omitempty" yaml:"op,omitempty" toml:"op,omitempty"` Value uint64 `json:"value,omitempty" yaml:"value,omitempty" toml:"value,omitempty"` } // StatsOptions specify parameters to the Stats function. // // See https://goo.gl/Dk3Xio for more details. type StatsOptions struct { ID string Stats chan<- *Stats Stream bool // A flag that enables stopping the stats operation Done <-chan bool // Initial connection timeout Timeout time.Duration // Timeout with no data is received, it's reset every time new data // arrives InactivityTimeout time.Duration `qs:"-"` Context context.Context } // Stats sends container statistics for the given container to the given channel. // // This function is blocking, similar to a streaming call for logs, and should be run // on a separate goroutine from the caller. Note that this function will block until // the given container is removed, not just exited. When finished, this function // will close the given channel. Alternatively, function can be stopped by // signaling on the Done channel. // // See https://goo.gl/Dk3Xio for more details. func (c *Client) Stats(opts StatsOptions) (retErr error) { errC := make(chan error, 1) readCloser, writeCloser := io.Pipe() defer func() { close(opts.Stats) if err := <-errC; err != nil && retErr == nil { retErr = err } if err := readCloser.Close(); err != nil && retErr == nil { retErr = err } }() reqSent := make(chan struct{}) go func() { defer close(errC) err := c.stream(http.MethodGet, fmt.Sprintf("/containers/%s/stats?stream=%v", opts.ID, opts.Stream), streamOptions{ rawJSONStream: true, useJSONDecoder: true, stdout: writeCloser, timeout: opts.Timeout, inactivityTimeout: opts.InactivityTimeout, context: opts.Context, reqSent: reqSent, }) if err != nil { var dockerError *Error if errors.As(err, &dockerError) { if dockerError.Status == http.StatusNotFound { err = &NoSuchContainer{ID: opts.ID} } } } if closeErr := writeCloser.Close(); closeErr != nil && err == nil { err = closeErr } errC <- err }() quit := make(chan struct{}) defer close(quit) go func() { // block here waiting for the signal to stop function select { case <-opts.Done: readCloser.Close() case <-quit: return } }() decoder := json.NewDecoder(readCloser) stats := new(Stats) <-reqSent for err := decoder.Decode(stats); !errors.Is(err, io.EOF); err = decoder.Decode(stats) { if err != nil { return err } opts.Stats <- stats stats = new(Stats) } return nil } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/container_stop.go ================================================ package docker import ( "context" "errors" "fmt" "net/http" ) // StopContainer stops a container, killing it after the given timeout (in // seconds). // // See https://goo.gl/R9dZcV for more details. func (c *Client) StopContainer(id string, timeout uint) error { return c.stopContainer(id, timeout, doOptions{}) } // StopContainerWithContext stops a container, killing it after the given // timeout (in seconds). The context can be used to cancel the stop // container request. // // See https://goo.gl/R9dZcV for more details. func (c *Client) StopContainerWithContext(id string, timeout uint, ctx context.Context) error { return c.stopContainer(id, timeout, doOptions{context: ctx}) } func (c *Client) stopContainer(id string, timeout uint, opts doOptions) error { path := fmt.Sprintf("/containers/%s/stop?t=%d", id, timeout) resp, err := c.do(http.MethodPost, path, opts) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return &NoSuchContainer{ID: id} } return err } defer resp.Body.Close() if resp.StatusCode == http.StatusNotModified { return &ContainerNotRunning{ID: id} } return nil } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/container_top.go ================================================ package docker import ( "encoding/json" "errors" "fmt" "net/http" ) // TopResult represents the list of processes running in a container, as // returned by /containers//top. // // See https://goo.gl/FLwpPl for more details. type TopResult struct { Titles []string Processes [][]string } // TopContainer returns processes running inside a container // // See https://goo.gl/FLwpPl for more details. func (c *Client) TopContainer(id string, psArgs string) (TopResult, error) { var args string var result TopResult if psArgs != "" { args = fmt.Sprintf("?ps_args=%s", psArgs) } path := fmt.Sprintf("/containers/%s/top%s", id, args) resp, err := c.do(http.MethodGet, path, doOptions{}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return result, &NoSuchContainer{ID: id} } return result, err } defer resp.Body.Close() err = json.NewDecoder(resp.Body).Decode(&result) return result, err } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/container_unpause.go ================================================ package docker import ( "errors" "fmt" "net/http" ) // UnpauseContainer unpauses the given container. // // See https://goo.gl/sZ2faO for more details. func (c *Client) UnpauseContainer(id string) error { path := fmt.Sprintf("/containers/%s/unpause", id) resp, err := c.do(http.MethodPost, path, doOptions{}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return &NoSuchContainer{ID: id} } return err } resp.Body.Close() return nil } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/container_update.go ================================================ package docker import ( "context" "fmt" "net/http" ) // UpdateContainerOptions specify parameters to the UpdateContainer function. // // See https://goo.gl/Y6fXUy for more details. type UpdateContainerOptions struct { BlkioWeight int `json:"BlkioWeight"` CPUShares int `json:"CpuShares"` CPUPeriod int `json:"CpuPeriod"` CPURealtimePeriod int64 `json:"CpuRealtimePeriod"` CPURealtimeRuntime int64 `json:"CpuRealtimeRuntime"` CPUQuota int `json:"CpuQuota"` CpusetCpus string `json:"CpusetCpus"` CpusetMems string `json:"CpusetMems"` Memory int `json:"Memory"` MemorySwap int `json:"MemorySwap"` MemoryReservation int `json:"MemoryReservation"` KernelMemory int `json:"KernelMemory"` RestartPolicy RestartPolicy `json:"RestartPolicy,omitempty"` Context context.Context } // UpdateContainer updates the container at ID with the options // // See https://goo.gl/Y6fXUy for more details. func (c *Client) UpdateContainer(id string, opts UpdateContainerOptions) error { resp, err := c.do(http.MethodPost, fmt.Sprintf("/containers/"+id+"/update"), doOptions{ data: opts, forceJSON: true, context: opts.Context, }) if err != nil { return err } defer resp.Body.Close() return nil } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/container_wait.go ================================================ package docker import ( "context" "encoding/json" "errors" "net/http" ) // WaitContainer blocks until the given container stops, return the exit code // of the container status. // // See https://goo.gl/4AGweZ for more details. func (c *Client) WaitContainer(id string) (int, error) { return c.waitContainer(id, doOptions{}) } // WaitContainerWithContext blocks until the given container stops, return the exit code // of the container status. The context object can be used to cancel the // inspect request. // // See https://goo.gl/4AGweZ for more details. func (c *Client) WaitContainerWithContext(id string, ctx context.Context) (int, error) { return c.waitContainer(id, doOptions{context: ctx}) } func (c *Client) waitContainer(id string, opts doOptions) (int, error) { resp, err := c.do(http.MethodPost, "/containers/"+id+"/wait", opts) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return 0, &NoSuchContainer{ID: id} } return 0, err } defer resp.Body.Close() var r struct{ StatusCode int } if err := json.NewDecoder(resp.Body).Decode(&r); err != nil { return 0, err } return r.StatusCode, nil } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/distribution.go ================================================ // Copyright 2017 go-dockerclient authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package docker import ( "encoding/json" "net/http" "github.com/docker/docker/api/types/registry" ) // InspectDistribution returns image digest and platform information by contacting the registry func (c *Client) InspectDistribution(name string) (*registry.DistributionInspect, error) { path := "/distribution/" + name + "/json" resp, err := c.do(http.MethodGet, path, doOptions{}) if err != nil { return nil, err } defer resp.Body.Close() var distributionInspect registry.DistributionInspect if err := json.NewDecoder(resp.Body).Decode(&distributionInspect); err != nil { return nil, err } return &distributionInspect, nil } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/env.go ================================================ // Copyright 2014 Docker authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the DOCKER-LICENSE file. package docker import ( "encoding/json" "fmt" "io" "strconv" "strings" ) // Env represents a list of key-pair represented in the form KEY=VALUE. type Env []string // Get returns the string value of the given key. func (env *Env) Get(key string) (value string) { return env.Map()[key] } // Exists checks whether the given key is defined in the internal Env // representation. func (env *Env) Exists(key string) bool { _, exists := env.Map()[key] return exists } // GetBool returns a boolean representation of the given key. The key is false // whenever its value if 0, no, false, none or an empty string. Any other value // will be interpreted as true. func (env *Env) GetBool(key string) (value bool) { s := strings.ToLower(strings.Trim(env.Get(key), " \t")) if s == "" || s == "0" || s == "no" || s == "false" || s == "none" { return false } return true } // SetBool defines a boolean value to the given key. func (env *Env) SetBool(key string, value bool) { if value { env.Set(key, "1") } else { env.Set(key, "0") } } // GetInt returns the value of the provided key, converted to int. // // It the value cannot be represented as an integer, it returns -1. func (env *Env) GetInt(key string) int { return int(env.GetInt64(key)) } // SetInt defines an integer value to the given key. func (env *Env) SetInt(key string, value int) { env.Set(key, strconv.Itoa(value)) } // GetInt64 returns the value of the provided key, converted to int64. // // It the value cannot be represented as an integer, it returns -1. func (env *Env) GetInt64(key string) int64 { s := strings.Trim(env.Get(key), " \t") val, err := strconv.ParseInt(s, 10, 64) if err != nil { return -1 } return val } // SetInt64 defines an integer (64-bit wide) value to the given key. func (env *Env) SetInt64(key string, value int64) { env.Set(key, strconv.FormatInt(value, 10)) } // GetJSON unmarshals the value of the provided key in the provided iface. // // iface is a value that can be provided to the json.Unmarshal function. func (env *Env) GetJSON(key string, iface any) error { sval := env.Get(key) if sval == "" { return nil } return json.Unmarshal([]byte(sval), iface) } // SetJSON marshals the given value to JSON format and stores it using the // provided key. func (env *Env) SetJSON(key string, value any) error { sval, err := json.Marshal(value) if err != nil { return err } env.Set(key, string(sval)) return nil } // GetList returns a list of strings matching the provided key. It handles the // list as a JSON representation of a list of strings. // // If the given key matches to a single string, it will return a list // containing only the value that matches the key. func (env *Env) GetList(key string) []string { sval := env.Get(key) if sval == "" { return nil } var l []string if err := json.Unmarshal([]byte(sval), &l); err != nil { l = append(l, sval) } return l } // SetList stores the given list in the provided key, after serializing it to // JSON format. func (env *Env) SetList(key string, value []string) error { return env.SetJSON(key, value) } // Set defines the value of a key to the given string. func (env *Env) Set(key, value string) { *env = append(*env, key+"="+value) } // Decode decodes `src` as a json dictionary, and adds each decoded key-value // pair to the environment. // // If `src` cannot be decoded as a json dictionary, an error is returned. func (env *Env) Decode(src io.Reader) error { m := make(map[string]any) if err := json.NewDecoder(src).Decode(&m); err != nil { return err } for k, v := range m { env.SetAuto(k, v) } return nil } // SetAuto will try to define the Set* method to call based on the given value. func (env *Env) SetAuto(key string, value any) { if fval, ok := value.(float64); ok { env.SetInt64(key, int64(fval)) } else if sval, ok := value.(string); ok { env.Set(key, sval) } else if val, err := json.Marshal(value); err == nil { env.Set(key, string(val)) } else { env.Set(key, fmt.Sprintf("%v", value)) } } // Map returns the map representation of the env. func (env *Env) Map() map[string]string { if env == nil || len(*env) == 0 { return nil } m := make(map[string]string) for _, kv := range *env { parts := strings.SplitN(kv, "=", 2) if len(parts) == 1 { m[parts[0]] = "" } else { m[parts[0]] = parts[1] } } return m } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/event.go ================================================ // Copyright 2014 go-dockerclient authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package docker import ( "encoding/json" "errors" "io" "math" "net" "net/http" "net/http/httputil" "strconv" "sync" "sync/atomic" "time" ) // EventsOptions to filter events // See https://docs.docker.com/engine/api/v1.41/#operation/SystemEvents for more details. type EventsOptions struct { // Show events created since this timestamp then stream new events. Since string // Show events created until this timestamp then stop streaming. Until string // Filter for events. For example: // map[string][]string{"type": {"container"}, "event": {"start", "die"}} // will return events when container was started and stopped or killed // // Available filters: // config= config name or ID // container= container name or ID // daemon= daemon name or ID // event= event type // image= image name or ID // label= image or container label // network= network name or ID // node= node ID // plugin= plugin name or ID // scope= local or swarm // secret= secret name or ID // service= service name or ID // type= container, image, volume, network, daemon, plugin, node, service, secret or config // volume= volume name Filters map[string][]string } // APIEvents represents events coming from the Docker API // The fields in the Docker API changed in API version 1.22, and // events for more than images and containers are now fired off. // To maintain forward and backward compatibility, go-dockerclient // replicates the event in both the new and old format as faithfully as possible. // // For events that only exist in 1.22 in later, `Status` is filled in as // `"Type:Action"` instead of just `Action` to allow for older clients to // differentiate and not break if they rely on the pre-1.22 Status types. // // The transformEvent method can be consulted for more information about how // events are translated from new/old API formats type APIEvents struct { // New API Fields in 1.22 Action string `json:"action,omitempty"` Type string `json:"type,omitempty"` Actor APIActor `json:"actor,omitempty"` // Old API fields for < 1.22 Status string `json:"status,omitempty"` ID string `json:"id,omitempty"` From string `json:"from,omitempty"` // Fields in both Time int64 `json:"time,omitempty"` TimeNano int64 `json:"timeNano,omitempty"` } // APIActor represents an actor that accomplishes something for an event type APIActor struct { ID string `json:"id,omitempty"` Attributes map[string]string `json:"attributes,omitempty"` } type eventMonitoringState struct { // `sync/atomic` expects the first word in an allocated struct to be 64-bit // aligned on both ARM and x86-32. See https://goo.gl/zW7dgq for more details. lastSeen int64 sync.RWMutex sync.WaitGroup enabled bool C chan *APIEvents errC chan error listeners []chan<- *APIEvents closeConn func() } const ( maxMonitorConnRetries = 5 retryInitialWaitTime = 10. ) var ( // ErrNoListeners is the error returned when no listeners are available // to receive an event. ErrNoListeners = errors.New("no listeners present to receive event") // ErrListenerAlreadyExists is the error returned when the listerner already // exists. ErrListenerAlreadyExists = errors.New("listener already exists for docker events") // ErrTLSNotSupported is the error returned when the client does not support // TLS (this applies to the Windows named pipe client). ErrTLSNotSupported = errors.New("tls not supported by this client") // EOFEvent is sent when the event listener receives an EOF error. EOFEvent = &APIEvents{ Type: "EOF", Status: "EOF", } ) // AddEventListener adds a new listener to container events in the Docker API. // // The parameter is a channel through which events will be sent. func (c *Client) AddEventListener(listener chan<- *APIEvents) error { return c.AddEventListenerWithOptions(EventsOptions{}, listener) } // AddEventListener adds a new listener to container events in the Docker API. // See https://docs.docker.com/engine/api/v1.41/#operation/SystemEvents for more details. // // The listener parameter is a channel through which events will be sent. func (c *Client) AddEventListenerWithOptions(options EventsOptions, listener chan<- *APIEvents) error { var err error if !c.eventMonitor.isEnabled() { err = c.eventMonitor.enableEventMonitoring(c, options) if err != nil { return err } } return c.eventMonitor.addListener(listener) } // RemoveEventListener removes a listener from the monitor. func (c *Client) RemoveEventListener(listener chan *APIEvents) error { err := c.eventMonitor.removeListener(listener) if err != nil { return err } if c.eventMonitor.listernersCount() == 0 { c.eventMonitor.disableEventMonitoring() } return nil } func (eventState *eventMonitoringState) addListener(listener chan<- *APIEvents) error { eventState.Lock() defer eventState.Unlock() if listenerExists(listener, &eventState.listeners) { return ErrListenerAlreadyExists } eventState.Add(1) eventState.listeners = append(eventState.listeners, listener) return nil } func (eventState *eventMonitoringState) removeListener(listener chan<- *APIEvents) error { eventState.Lock() defer eventState.Unlock() if listenerExists(listener, &eventState.listeners) { var newListeners []chan<- *APIEvents for _, l := range eventState.listeners { if l != listener { newListeners = append(newListeners, l) } } eventState.listeners = newListeners eventState.Add(-1) } return nil } func (eventState *eventMonitoringState) closeListeners() { for _, l := range eventState.listeners { close(l) eventState.Add(-1) } eventState.listeners = nil } func (eventState *eventMonitoringState) listernersCount() int { eventState.RLock() defer eventState.RUnlock() return len(eventState.listeners) } func listenerExists(a chan<- *APIEvents, list *[]chan<- *APIEvents) bool { for _, b := range *list { if b == a { return true } } return false } func (eventState *eventMonitoringState) enableEventMonitoring(c *Client, opts EventsOptions) error { eventState.Lock() defer eventState.Unlock() if !eventState.enabled { eventState.enabled = true atomic.StoreInt64(&eventState.lastSeen, 0) eventState.C = make(chan *APIEvents, 100) eventState.errC = make(chan error, 1) go eventState.monitorEvents(c, opts) } return nil } func (eventState *eventMonitoringState) disableEventMonitoring() { eventState.Lock() defer eventState.Unlock() eventState.closeListeners() eventState.Wait() if eventState.enabled { eventState.enabled = false close(eventState.C) close(eventState.errC) if eventState.closeConn != nil { eventState.closeConn() eventState.closeConn = nil } } } func (eventState *eventMonitoringState) monitorEvents(c *Client, opts EventsOptions) { const ( noListenersTimeout = 5 * time.Second noListenersInterval = 10 * time.Millisecond noListenersMaxTries = noListenersTimeout / noListenersInterval ) var err error for i := time.Duration(0); i < noListenersMaxTries && eventState.noListeners(); i++ { time.Sleep(10 * time.Millisecond) } if eventState.noListeners() { // terminate if no listener is available after 5 seconds. // Prevents goroutine leak when RemoveEventListener is called // right after AddEventListener. eventState.disableEventMonitoring() return } if err = eventState.connectWithRetry(c, opts); err != nil { // terminate if connect failed eventState.disableEventMonitoring() return } for eventState.isEnabled() { timeout := time.After(100 * time.Millisecond) select { case ev, ok := <-eventState.C: if !ok { return } if ev == EOFEvent { eventState.disableEventMonitoring() return } eventState.updateLastSeen(ev) eventState.sendEvent(ev) case err = <-eventState.errC: if errors.Is(err, ErrNoListeners) { eventState.disableEventMonitoring() return } else if err != nil { defer func() { go eventState.monitorEvents(c, opts) }() return } case <-timeout: continue } } } func (eventState *eventMonitoringState) connectWithRetry(c *Client, opts EventsOptions) error { var retries int eventState.RLock() eventChan := eventState.C errChan := eventState.errC eventState.RUnlock() closeConn, err := c.eventHijack(opts, atomic.LoadInt64(&eventState.lastSeen), eventChan, errChan) for ; err != nil && retries < maxMonitorConnRetries; retries++ { waitTime := int64(retryInitialWaitTime * math.Pow(2, float64(retries))) time.Sleep(time.Duration(waitTime) * time.Millisecond) eventState.RLock() eventChan = eventState.C errChan = eventState.errC eventState.RUnlock() closeConn, err = c.eventHijack(opts, atomic.LoadInt64(&eventState.lastSeen), eventChan, errChan) } eventState.Lock() defer eventState.Unlock() eventState.closeConn = closeConn return err } func (eventState *eventMonitoringState) noListeners() bool { eventState.RLock() defer eventState.RUnlock() return len(eventState.listeners) == 0 } func (eventState *eventMonitoringState) isEnabled() bool { eventState.RLock() defer eventState.RUnlock() return eventState.enabled } func (eventState *eventMonitoringState) sendEvent(event *APIEvents) { eventState.RLock() defer eventState.RUnlock() eventState.Add(1) defer eventState.Done() if eventState.enabled { if len(eventState.listeners) == 0 { eventState.errC <- ErrNoListeners return } for _, listener := range eventState.listeners { select { case listener <- event: default: } } } } func (eventState *eventMonitoringState) updateLastSeen(e *APIEvents) { eventState.Lock() defer eventState.Unlock() if atomic.LoadInt64(&eventState.lastSeen) < e.Time { atomic.StoreInt64(&eventState.lastSeen, e.Time) } } func (c *Client) eventHijack(opts EventsOptions, startTime int64, eventChan chan *APIEvents, errChan chan error) (closeConn func(), err error) { // on reconnect override initial Since with last event seen time if startTime != 0 { opts.Since = strconv.FormatInt(startTime, 10) } uri := "/events?" + queryString(opts) protocol := c.endpointURL.Scheme address := c.endpointURL.Path if protocol != "unix" && protocol != "npipe" { protocol = "tcp" address = c.endpointURL.Host } var dial net.Conn if c.TLSConfig == nil { dial, err = c.Dialer.Dial(protocol, address) } else { netDialer, ok := c.Dialer.(*net.Dialer) if !ok { return nil, ErrTLSNotSupported } dial, err = tlsDialWithDialer(netDialer, protocol, address, c.TLSConfig) } if err != nil { return nil, err } //lint:ignore SA1019 the alternative doesn't quite work, so keep using the deprecated thing. conn := httputil.NewClientConn(dial, nil) req, err := http.NewRequest(http.MethodGet, uri, nil) if err != nil { return nil, err } res, err := conn.Do(req) if err != nil { return nil, err } keepRunning := int32(1) //lint:ignore SA1019 the alternative doesn't quite work, so keep using the deprecated thing. go func(res *http.Response, conn *httputil.ClientConn) { defer conn.Close() defer res.Body.Close() decoder := json.NewDecoder(res.Body) for atomic.LoadInt32(&keepRunning) == 1 { var event APIEvents if err := decoder.Decode(&event); err != nil { if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) { c.eventMonitor.RLock() if c.eventMonitor.enabled && c.eventMonitor.C == eventChan { // Signal that we're exiting. eventChan <- EOFEvent } c.eventMonitor.RUnlock() break } errChan <- err } if event.Time == 0 { continue } transformEvent(&event) c.eventMonitor.RLock() if c.eventMonitor.enabled && c.eventMonitor.C == eventChan { eventChan <- &event } c.eventMonitor.RUnlock() } }(res, conn) return func() { atomic.StoreInt32(&keepRunning, 0) }, nil } // transformEvent takes an event and determines what version it is from // then populates both versions of the event func transformEvent(event *APIEvents) { // if event version is <= 1.21 there will be no Action and no Type if event.Action == "" && event.Type == "" { event.Action = event.Status event.Actor.ID = event.ID event.Actor.Attributes = map[string]string{} switch event.Status { case "delete", "import", "pull", "push", "tag", "untag": event.Type = "image" default: event.Type = "container" if event.From != "" { event.Actor.Attributes["image"] = event.From } } } else { if event.Status == "" { if event.Type == "image" || event.Type == "container" { event.Status = event.Action } else { // Because just the Status has been overloaded with different Types // if an event is not for an image or a container, we prepend the type // to avoid problems for people relying on actions being only for // images and containers event.Status = event.Type + ":" + event.Action } } if event.ID == "" { event.ID = event.Actor.ID } if event.From == "" { event.From = event.Actor.Attributes["image"] } } } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/exec.go ================================================ // Copyright 2014 go-dockerclient authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package docker import ( "context" "encoding/json" "errors" "fmt" "io" "net/http" "net/url" "strconv" ) // Exec is the type representing a `docker exec` instance and containing the // instance ID type Exec struct { ID string `json:"Id,omitempty" yaml:"Id,omitempty"` } // CreateExecOptions specify parameters to the CreateExecContainer function. // // See https://goo.gl/60TeBP for more details type CreateExecOptions struct { Env []string `json:"Env,omitempty" yaml:"Env,omitempty" toml:"Env,omitempty"` Cmd []string `json:"Cmd,omitempty" yaml:"Cmd,omitempty" toml:"Cmd,omitempty"` Container string `json:"Container,omitempty" yaml:"Container,omitempty" toml:"Container,omitempty"` User string `json:"User,omitempty" yaml:"User,omitempty" toml:"User,omitempty"` WorkingDir string `json:"WorkingDir,omitempty" yaml:"WorkingDir,omitempty" toml:"WorkingDir,omitempty"` DetachKeys string `json:"DetachKeys,omitempty" yaml:"DetachKeys,omitempty" toml:"DetachKeys,omitempty"` Context context.Context `json:"-"` AttachStdin bool `json:"AttachStdin,omitempty" yaml:"AttachStdin,omitempty" toml:"AttachStdin,omitempty"` AttachStdout bool `json:"AttachStdout,omitempty" yaml:"AttachStdout,omitempty" toml:"AttachStdout,omitempty"` AttachStderr bool `json:"AttachStderr,omitempty" yaml:"AttachStderr,omitempty" toml:"AttachStderr,omitempty"` Tty bool `json:"Tty,omitempty" yaml:"Tty,omitempty" toml:"Tty,omitempty"` Privileged bool `json:"Privileged,omitempty" yaml:"Privileged,omitempty" toml:"Privileged,omitempty"` } // CreateExec sets up an exec instance in a running container `id`, returning the exec // instance, or an error in case of failure. // // See https://goo.gl/60TeBP for more details func (c *Client) CreateExec(opts CreateExecOptions) (*Exec, error) { if c.serverAPIVersion == nil { c.checkAPIVersion() } if len(opts.Env) > 0 && c.serverAPIVersion.LessThan(apiVersion125) { return nil, errors.New("exec configuration Env is only supported in API#1.25 and above") } if len(opts.WorkingDir) > 0 && c.serverAPIVersion.LessThan(apiVersion135) { return nil, errors.New("exec configuration WorkingDir is only supported in API#1.35 and above") } path := fmt.Sprintf("/containers/%s/exec", opts.Container) resp, err := c.do(http.MethodPost, path, doOptions{data: opts, context: opts.Context}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return nil, &NoSuchContainer{ID: opts.Container} } return nil, err } defer resp.Body.Close() var exec Exec if err := json.NewDecoder(resp.Body).Decode(&exec); err != nil { return nil, err } return &exec, nil } // StartExecOptions specify parameters to the StartExecContainer function. // // See https://goo.gl/1EeDWi for more details type StartExecOptions struct { InputStream io.Reader `qs:"-"` OutputStream io.Writer `qs:"-"` ErrorStream io.Writer `qs:"-"` Detach bool `json:"Detach,omitempty" yaml:"Detach,omitempty" toml:"Detach,omitempty"` Tty bool `json:"Tty,omitempty" yaml:"Tty,omitempty" toml:"Tty,omitempty"` // Use raw terminal? Usually true when the container contains a TTY. RawTerminal bool `qs:"-"` // If set, after a successful connect, a sentinel will be sent and then the // client will block on receive before continuing. // // It must be an unbuffered channel. Using a buffered channel can lead // to unexpected behavior. Success chan struct{} `json:"-"` Context context.Context `json:"-"` } // StartExec starts a previously set up exec instance id. If opts.Detach is // true, it returns after starting the exec command. Otherwise, it sets up an // interactive session with the exec command. // // See https://goo.gl/1EeDWi for more details func (c *Client) StartExec(id string, opts StartExecOptions) error { cw, err := c.StartExecNonBlocking(id, opts) if err != nil { return err } if cw != nil { return cw.Wait() } return nil } // StartExecNonBlocking starts a previously set up exec instance id. If opts.Detach is // true, it returns after starting the exec command. Otherwise, it sets up an // interactive session with the exec command. // // See https://goo.gl/1EeDWi for more details func (c *Client) StartExecNonBlocking(id string, opts StartExecOptions) (CloseWaiter, error) { if id == "" { return nil, &NoSuchExec{ID: id} } path := fmt.Sprintf("/exec/%s/start", id) if opts.Detach { resp, err := c.do(http.MethodPost, path, doOptions{data: opts, context: opts.Context}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return nil, &NoSuchExec{ID: id} } return nil, err } defer resp.Body.Close() return nil, nil } return c.hijack(http.MethodPost, path, hijackOptions{ success: opts.Success, setRawTerminal: opts.RawTerminal, in: opts.InputStream, stdout: opts.OutputStream, stderr: opts.ErrorStream, data: opts, }) } // ResizeExecTTY resizes the tty session used by the exec command id. This API // is valid only if Tty was specified as part of creating and starting the exec // command. // // See https://goo.gl/Mo5bxx for more details func (c *Client) ResizeExecTTY(id string, height, width int) error { params := make(url.Values) params.Set("h", strconv.Itoa(height)) params.Set("w", strconv.Itoa(width)) path := fmt.Sprintf("/exec/%s/resize?%s", id, params.Encode()) resp, err := c.do(http.MethodPost, path, doOptions{}) if err != nil { return err } resp.Body.Close() return nil } // ExecProcessConfig is a type describing the command associated to a Exec // instance. It's used in the ExecInspect type. type ExecProcessConfig struct { User string `json:"user,omitempty" yaml:"user,omitempty" toml:"user,omitempty"` Privileged bool `json:"privileged,omitempty" yaml:"privileged,omitempty" toml:"privileged,omitempty"` Tty bool `json:"tty,omitempty" yaml:"tty,omitempty" toml:"tty,omitempty"` EntryPoint string `json:"entrypoint,omitempty" yaml:"entrypoint,omitempty" toml:"entrypoint,omitempty"` Arguments []string `json:"arguments,omitempty" yaml:"arguments,omitempty" toml:"arguments,omitempty"` } // ExecInspect is a type with details about a exec instance, including the // exit code if the command has finished running. It's returned by a api // call to /exec/(id)/json // // See https://goo.gl/ctMUiW for more details type ExecInspect struct { ID string `json:"ID,omitempty" yaml:"ID,omitempty" toml:"ID,omitempty"` ExitCode int `json:"ExitCode,omitempty" yaml:"ExitCode,omitempty" toml:"ExitCode,omitempty"` ProcessConfig ExecProcessConfig `json:"ProcessConfig,omitempty" yaml:"ProcessConfig,omitempty" toml:"ProcessConfig,omitempty"` ContainerID string `json:"ContainerID,omitempty" yaml:"ContainerID,omitempty" toml:"ContainerID,omitempty"` DetachKeys string `json:"DetachKeys,omitempty" yaml:"DetachKeys,omitempty" toml:"DetachKeys,omitempty"` Running bool `json:"Running,omitempty" yaml:"Running,omitempty" toml:"Running,omitempty"` OpenStdin bool `json:"OpenStdin,omitempty" yaml:"OpenStdin,omitempty" toml:"OpenStdin,omitempty"` OpenStderr bool `json:"OpenStderr,omitempty" yaml:"OpenStderr,omitempty" toml:"OpenStderr,omitempty"` OpenStdout bool `json:"OpenStdout,omitempty" yaml:"OpenStdout,omitempty" toml:"OpenStdout,omitempty"` CanRemove bool `json:"CanRemove,omitempty" yaml:"CanRemove,omitempty" toml:"CanRemove,omitempty"` } // InspectExec returns low-level information about the exec command id. // // See https://goo.gl/ctMUiW for more details func (c *Client) InspectExec(id string) (*ExecInspect, error) { path := fmt.Sprintf("/exec/%s/json", id) resp, err := c.do(http.MethodGet, path, doOptions{}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return nil, &NoSuchExec{ID: id} } return nil, err } defer resp.Body.Close() var exec ExecInspect if err := json.NewDecoder(resp.Body).Decode(&exec); err != nil { return nil, err } return &exec, nil } // NoSuchExec is the error returned when a given exec instance does not exist. type NoSuchExec struct { ID string } func (err *NoSuchExec) Error() string { return "No such exec instance: " + err.ID } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/image.go ================================================ // Copyright 2013 go-dockerclient authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package docker import ( "context" "encoding/base64" "encoding/json" "errors" "fmt" "io" "net/http" "net/url" "os" "strings" "time" ) // APIImages represent an image returned in the ListImages call. type APIImages struct { ID string `json:"Id" yaml:"Id" toml:"Id"` RepoTags []string `json:"RepoTags,omitempty" yaml:"RepoTags,omitempty" toml:"RepoTags,omitempty"` Created int64 `json:"Created,omitempty" yaml:"Created,omitempty" toml:"Created,omitempty"` Size int64 `json:"Size,omitempty" yaml:"Size,omitempty" toml:"Size,omitempty"` VirtualSize int64 `json:"VirtualSize,omitempty" yaml:"VirtualSize,omitempty" toml:"VirtualSize,omitempty"` ParentID string `json:"ParentId,omitempty" yaml:"ParentId,omitempty" toml:"ParentId,omitempty"` RepoDigests []string `json:"RepoDigests,omitempty" yaml:"RepoDigests,omitempty" toml:"RepoDigests,omitempty"` Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty" toml:"Labels,omitempty"` } // RootFS represents the underlying layers used by an image type RootFS struct { Type string `json:"Type,omitempty" yaml:"Type,omitempty" toml:"Type,omitempty"` Layers []string `json:"Layers,omitempty" yaml:"Layers,omitempty" toml:"Layers,omitempty"` } // Image is the type representing a docker image and its various properties type Image struct { ID string `json:"Id" yaml:"Id" toml:"Id"` RepoTags []string `json:"RepoTags,omitempty" yaml:"RepoTags,omitempty" toml:"RepoTags,omitempty"` Parent string `json:"Parent,omitempty" yaml:"Parent,omitempty" toml:"Parent,omitempty"` Comment string `json:"Comment,omitempty" yaml:"Comment,omitempty" toml:"Comment,omitempty"` Created time.Time `json:"Created,omitempty" yaml:"Created,omitempty" toml:"Created,omitempty"` Container string `json:"Container,omitempty" yaml:"Container,omitempty" toml:"Container,omitempty"` ContainerConfig Config `json:"ContainerConfig,omitempty" yaml:"ContainerConfig,omitempty" toml:"ContainerConfig,omitempty"` DockerVersion string `json:"DockerVersion,omitempty" yaml:"DockerVersion,omitempty" toml:"DockerVersion,omitempty"` Author string `json:"Author,omitempty" yaml:"Author,omitempty" toml:"Author,omitempty"` Config *Config `json:"Config,omitempty" yaml:"Config,omitempty" toml:"Config,omitempty"` Architecture string `json:"Architecture,omitempty" yaml:"Architecture,omitempty"` Size int64 `json:"Size,omitempty" yaml:"Size,omitempty" toml:"Size,omitempty"` VirtualSize int64 `json:"VirtualSize,omitempty" yaml:"VirtualSize,omitempty" toml:"VirtualSize,omitempty"` RepoDigests []string `json:"RepoDigests,omitempty" yaml:"RepoDigests,omitempty" toml:"RepoDigests,omitempty"` RootFS *RootFS `json:"RootFS,omitempty" yaml:"RootFS,omitempty" toml:"RootFS,omitempty"` OS string `json:"Os,omitempty" yaml:"Os,omitempty" toml:"Os,omitempty"` } // ImagePre012 serves the same purpose as the Image type except that it is for // earlier versions of the Docker API (pre-012 to be specific) type ImagePre012 struct { ID string `json:"id"` Parent string `json:"parent,omitempty"` Comment string `json:"comment,omitempty"` Created time.Time `json:"created"` Container string `json:"container,omitempty"` ContainerConfig Config `json:"container_config,omitempty"` DockerVersion string `json:"docker_version,omitempty"` Author string `json:"author,omitempty"` Config *Config `json:"config,omitempty"` Architecture string `json:"architecture,omitempty"` Size int64 `json:"size,omitempty"` } var ( // ErrNoSuchImage is the error returned when the image does not exist. ErrNoSuchImage = errors.New("no such image") // ErrMissingRepo is the error returned when the remote repository is // missing. ErrMissingRepo = errors.New("missing remote repository e.g. 'github.com/user/repo'") // ErrMissingOutputStream is the error returned when no output stream // is provided to some calls, like BuildImage. ErrMissingOutputStream = errors.New("missing output stream") // ErrMultipleContexts is the error returned when both a ContextDir and // InputStream are provided in BuildImageOptions ErrMultipleContexts = errors.New("image build may not be provided BOTH context dir and input stream") // ErrMustSpecifyNames is the error returned when the Names field on // ExportImagesOptions is nil or empty ErrMustSpecifyNames = errors.New("must specify at least one name to export") ) // ListImagesOptions specify parameters to the ListImages function. // // See https://goo.gl/BVzauZ for more details. type ListImagesOptions struct { Filters map[string][]string All bool Digests bool Filter string Context context.Context } // ListImages returns the list of available images in the server. // // See https://goo.gl/BVzauZ for more details. func (c *Client) ListImages(opts ListImagesOptions) ([]APIImages, error) { path := "/images/json?" + queryString(opts) resp, err := c.do(http.MethodGet, path, doOptions{context: opts.Context}) if err != nil { return nil, err } defer resp.Body.Close() var images []APIImages if err := json.NewDecoder(resp.Body).Decode(&images); err != nil { return nil, err } return images, nil } // ImageHistory represent a layer in an image's history returned by the // ImageHistory call. type ImageHistory struct { ID string `json:"Id" yaml:"Id" toml:"Id"` Tags []string `json:"Tags,omitempty" yaml:"Tags,omitempty" toml:"Tags,omitempty"` Created int64 `json:"Created,omitempty" yaml:"Created,omitempty" toml:"Tags,omitempty"` CreatedBy string `json:"CreatedBy,omitempty" yaml:"CreatedBy,omitempty" toml:"CreatedBy,omitempty"` Size int64 `json:"Size,omitempty" yaml:"Size,omitempty" toml:"Size,omitempty"` Comment string `json:"Comment,omitempty" yaml:"Comment,omitempty" toml:"Comment,omitempty"` } // ImageHistory returns the history of the image by its name or ID. // // See https://goo.gl/fYtxQa for more details. func (c *Client) ImageHistory(name string) ([]ImageHistory, error) { resp, err := c.do(http.MethodGet, "/images/"+name+"/history", doOptions{}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return nil, ErrNoSuchImage } return nil, err } defer resp.Body.Close() var history []ImageHistory if err := json.NewDecoder(resp.Body).Decode(&history); err != nil { return nil, err } return history, nil } // RemoveImage removes an image by its name or ID. // // See https://goo.gl/Vd2Pck for more details. func (c *Client) RemoveImage(name string) error { resp, err := c.do(http.MethodDelete, "/images/"+name, doOptions{}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return ErrNoSuchImage } return err } resp.Body.Close() return nil } // RemoveImageOptions present the set of options available for removing an image // from a registry. // // See https://goo.gl/Vd2Pck for more details. type RemoveImageOptions struct { Force bool `qs:"force"` NoPrune bool `qs:"noprune"` Context context.Context } // RemoveImageExtended removes an image by its name or ID. // Extra params can be passed, see RemoveImageOptions // // See https://goo.gl/Vd2Pck for more details. func (c *Client) RemoveImageExtended(name string, opts RemoveImageOptions) error { uri := fmt.Sprintf("/images/%s?%s", name, queryString(&opts)) resp, err := c.do(http.MethodDelete, uri, doOptions{context: opts.Context}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return ErrNoSuchImage } return err } resp.Body.Close() return nil } // InspectImage returns an image by its name or ID. // // See https://goo.gl/ncLTG8 for more details. func (c *Client) InspectImage(name string) (*Image, error) { resp, err := c.do(http.MethodGet, "/images/"+name+"/json", doOptions{}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return nil, ErrNoSuchImage } return nil, err } defer resp.Body.Close() var image Image // if the caller elected to skip checking the server's version, assume it's the latest if c.SkipServerVersionCheck || c.expectedAPIVersion.GreaterThanOrEqualTo(apiVersion112) { if err := json.NewDecoder(resp.Body).Decode(&image); err != nil { return nil, err } } else { var imagePre012 ImagePre012 if err := json.NewDecoder(resp.Body).Decode(&imagePre012); err != nil { return nil, err } image.ID = imagePre012.ID image.Parent = imagePre012.Parent image.Comment = imagePre012.Comment image.Created = imagePre012.Created image.Container = imagePre012.Container image.ContainerConfig = imagePre012.ContainerConfig image.DockerVersion = imagePre012.DockerVersion image.Author = imagePre012.Author image.Config = imagePre012.Config image.Architecture = imagePre012.Architecture image.Size = imagePre012.Size } return &image, nil } // PushImageOptions represents options to use in the PushImage method. // // See https://goo.gl/BZemGg for more details. type PushImageOptions struct { // Name of the image Name string // Tag of the image Tag string // Registry server to push the image Registry string OutputStream io.Writer `qs:"-"` RawJSONStream bool `qs:"-"` InactivityTimeout time.Duration `qs:"-"` Context context.Context } // PushImage pushes an image to a remote registry, logging progress to w. // // An empty instance of AuthConfiguration may be used for unauthenticated // pushes. // // See https://goo.gl/BZemGg for more details. func (c *Client) PushImage(opts PushImageOptions, auth AuthConfiguration) error { if opts.Name == "" { return ErrNoSuchImage } headers, err := headersWithAuth(auth) if err != nil { return err } name := opts.Name opts.Name = "" path := "/images/" + name + "/push?" + queryString(&opts) return c.stream(http.MethodPost, path, streamOptions{ setRawTerminal: true, rawJSONStream: opts.RawJSONStream, headers: headers, stdout: opts.OutputStream, inactivityTimeout: opts.InactivityTimeout, context: opts.Context, }) } // PullImageOptions present the set of options available for pulling an image // from a registry. // // See https://goo.gl/qkoSsn for more details. type PullImageOptions struct { All bool Repository string `qs:"fromImage"` Tag string Platform string `ver:"1.32"` // Only required for Docker Engine 1.9 or 1.10 w/ Remote API < 1.21 // and Docker Engine < 1.9 // This parameter was removed in Docker Engine 1.11 Registry string OutputStream io.Writer `qs:"-"` RawJSONStream bool `qs:"-"` InactivityTimeout time.Duration `qs:"-"` Context context.Context } // PullImage pulls an image from a remote registry, logging progress to // opts.OutputStream. // // See https://goo.gl/qkoSsn for more details. func (c *Client) PullImage(opts PullImageOptions, auth AuthConfiguration) error { if opts.Repository == "" { return ErrNoSuchImage } headers, err := headersWithAuth(auth) if err != nil { return err } if opts.Tag == "" && strings.Contains(opts.Repository, "@") { parts := strings.SplitN(opts.Repository, "@", 2) opts.Repository = parts[0] opts.Tag = parts[1] } return c.createImage(&opts, headers, nil, opts.OutputStream, opts.RawJSONStream, opts.InactivityTimeout, opts.Context) } func (c *Client) createImage(opts any, headers map[string]string, in io.Reader, w io.Writer, rawJSONStream bool, timeout time.Duration, context context.Context) error { url, err := c.getPath("/images/create", opts) if err != nil { return err } return c.streamURL(http.MethodPost, url, streamOptions{ setRawTerminal: true, headers: headers, in: in, stdout: w, rawJSONStream: rawJSONStream, inactivityTimeout: timeout, context: context, }) } // LoadImageOptions represents the options for LoadImage Docker API Call // // See https://goo.gl/rEsBV3 for more details. type LoadImageOptions struct { InputStream io.Reader OutputStream io.Writer Context context.Context } // LoadImage imports a tarball docker image // // See https://goo.gl/rEsBV3 for more details. func (c *Client) LoadImage(opts LoadImageOptions) error { return c.stream(http.MethodPost, "/images/load", streamOptions{ setRawTerminal: true, in: opts.InputStream, stdout: opts.OutputStream, context: opts.Context, }) } // ExportImageOptions represent the options for ExportImage Docker API call. // // See https://goo.gl/AuySaA for more details. type ExportImageOptions struct { Name string OutputStream io.Writer InactivityTimeout time.Duration Context context.Context } // ExportImage exports an image (as a tar file) into the stream. // // See https://goo.gl/AuySaA for more details. func (c *Client) ExportImage(opts ExportImageOptions) error { return c.stream(http.MethodGet, fmt.Sprintf("/images/%s/get", opts.Name), streamOptions{ setRawTerminal: true, stdout: opts.OutputStream, inactivityTimeout: opts.InactivityTimeout, context: opts.Context, }) } // ExportImagesOptions represent the options for ExportImages Docker API call // // See https://goo.gl/N9XlDn for more details. type ExportImagesOptions struct { Names []string OutputStream io.Writer `qs:"-"` InactivityTimeout time.Duration `qs:"-"` Context context.Context } // ExportImages exports one or more images (as a tar file) into the stream // // See https://goo.gl/N9XlDn for more details. func (c *Client) ExportImages(opts ExportImagesOptions) error { if opts.Names == nil || len(opts.Names) == 0 { return ErrMustSpecifyNames } // API < 1.25 allows multiple name values // 1.25 says name must be a comma separated list var err error var exporturl string if c.requestedAPIVersion.GreaterThanOrEqualTo(apiVersion125) { str := opts.Names[0] for _, val := range opts.Names[1:] { str += "," + val } exporturl, err = c.getPath("/images/get", ExportImagesOptions{ Names: []string{str}, OutputStream: opts.OutputStream, InactivityTimeout: opts.InactivityTimeout, Context: opts.Context, }) } else { exporturl, err = c.getPath("/images/get", &opts) } if err != nil { return err } return c.streamURL(http.MethodGet, exporturl, streamOptions{ setRawTerminal: true, stdout: opts.OutputStream, inactivityTimeout: opts.InactivityTimeout, }) } // ImportImageOptions present the set of informations available for importing // an image from a source file or the stdin. // // See https://goo.gl/qkoSsn for more details. type ImportImageOptions struct { Repository string `qs:"repo"` Source string `qs:"fromSrc"` Tag string `qs:"tag"` InputStream io.Reader `qs:"-"` OutputStream io.Writer `qs:"-"` RawJSONStream bool `qs:"-"` InactivityTimeout time.Duration `qs:"-"` Context context.Context } // ImportImage imports an image from a url, a file or stdin // // See https://goo.gl/qkoSsn for more details. func (c *Client) ImportImage(opts ImportImageOptions) error { if opts.Repository == "" { return ErrNoSuchImage } if opts.Source != "-" { opts.InputStream = nil } if opts.Source != "-" && !isURL(opts.Source) { f, err := os.Open(opts.Source) if err != nil { return err } opts.InputStream = f opts.Source = "-" } return c.createImage(&opts, nil, opts.InputStream, opts.OutputStream, opts.RawJSONStream, opts.InactivityTimeout, opts.Context) } // BuilderVersion represents either the BuildKit or V1 ("classic") builder. type BuilderVersion string const ( BuilderV1 BuilderVersion = "1" BuilderBuildKit BuilderVersion = "2" ) // BuildImageOptions present the set of informations available for building an // image from a tarfile with a Dockerfile in it. // // For more details about the Docker building process, see // https://goo.gl/4nYHwV. type BuildImageOptions struct { Context context.Context Name string `qs:"t"` Dockerfile string `ver:"1.25"` ExtraHosts string `ver:"1.28"` CacheFrom []string `qs:"-" ver:"1.25"` Memory int64 Memswap int64 ShmSize int64 CPUShares int64 CPUQuota int64 `ver:"1.21"` CPUPeriod int64 `ver:"1.21"` CPUSetCPUs string Labels map[string]string InputStream io.Reader `qs:"-"` OutputStream io.Writer `qs:"-"` Remote string Auth AuthConfiguration `qs:"-"` // for older docker X-Registry-Auth header AuthConfigs AuthConfigurations `qs:"-"` // for newer docker X-Registry-Config header ContextDir string `qs:"-"` Ulimits []ULimit `qs:"-" ver:"1.18"` BuildArgs []BuildArg `qs:"-" ver:"1.21"` NetworkMode string `ver:"1.25"` Platform string `ver:"1.32"` InactivityTimeout time.Duration `qs:"-"` CgroupParent string SecurityOpt []string Target string Outputs string `ver:"1.40"` NoCache bool SuppressOutput bool `qs:"q"` Pull bool `ver:"1.16"` RmTmpContainer bool `qs:"rm"` ForceRmTmpContainer bool `qs:"forcerm" ver:"1.12"` RawJSONStream bool `qs:"-"` Version BuilderVersion `qs:"version" ver:"1.39"` } // BuildArg represents arguments that can be passed to the image when building // it from a Dockerfile. // // For more details about the Docker building process, see // https://goo.gl/4nYHwV. type BuildArg struct { Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"` Value string `json:"Value,omitempty" yaml:"Value,omitempty" toml:"Value,omitempty"` } // BuildImage builds an image from a tarball's url or a Dockerfile in the input // stream. // // See https://goo.gl/4nYHwV for more details. func (c *Client) BuildImage(opts BuildImageOptions) error { if opts.OutputStream == nil { return ErrMissingOutputStream } headers, err := headersWithAuth(opts.Auth, c.versionedAuthConfigs(opts.AuthConfigs)) if err != nil { return err } if opts.Remote != "" && opts.Name == "" { opts.Name = opts.Remote } if opts.InputStream != nil || opts.ContextDir != "" { headers["Content-Type"] = "application/tar" } else if opts.Remote == "" { return ErrMissingRepo } if opts.ContextDir != "" { if opts.InputStream != nil { return ErrMultipleContexts } var err error if opts.InputStream, err = createTarStream(opts.ContextDir, opts.Dockerfile); err != nil { return err } } qs, ver := queryStringVersion(&opts) if len(opts.CacheFrom) > 0 { if b, err := json.Marshal(opts.CacheFrom); err == nil { item := url.Values(map[string][]string{}) item.Add("cachefrom", string(b)) qs = fmt.Sprintf("%s&%s", qs, item.Encode()) if ver == nil || apiVersion125.GreaterThan(ver) { ver = apiVersion125 } } } if len(opts.Ulimits) > 0 { if b, err := json.Marshal(opts.Ulimits); err == nil { item := url.Values(map[string][]string{}) item.Add("ulimits", string(b)) qs = fmt.Sprintf("%s&%s", qs, item.Encode()) if ver == nil || apiVersion118.GreaterThan(ver) { ver = apiVersion118 } } } if len(opts.BuildArgs) > 0 { v := make(map[string]string) for _, arg := range opts.BuildArgs { v[arg.Name] = arg.Value } if b, err := json.Marshal(v); err == nil { item := url.Values(map[string][]string{}) item.Add("buildargs", string(b)) qs = fmt.Sprintf("%s&%s", qs, item.Encode()) if ver == nil || apiVersion121.GreaterThan(ver) { ver = apiVersion121 } } } buildURL, err := c.pathVersionCheck("/build", qs, ver) if err != nil { return err } return c.streamURL(http.MethodPost, buildURL, streamOptions{ setRawTerminal: true, rawJSONStream: opts.RawJSONStream, headers: headers, in: opts.InputStream, stdout: opts.OutputStream, inactivityTimeout: opts.InactivityTimeout, context: opts.Context, }) } func (c *Client) versionedAuthConfigs(authConfigs AuthConfigurations) registryAuth { if c.serverAPIVersion == nil { c.checkAPIVersion() } if c.serverAPIVersion != nil && c.serverAPIVersion.GreaterThanOrEqualTo(apiVersion119) { return AuthConfigurations119(authConfigs.Configs) } return authConfigs } // TagImageOptions present the set of options to tag an image. // // See https://goo.gl/prHrvo for more details. type TagImageOptions struct { Repo string Tag string Force bool Context context.Context } // TagImage adds a tag to the image identified by the given name. // // See https://goo.gl/prHrvo for more details. func (c *Client) TagImage(name string, opts TagImageOptions) error { if name == "" { return ErrNoSuchImage } resp, err := c.do(http.MethodPost, "/images/"+name+"/tag?"+queryString(&opts), doOptions{ context: opts.Context, }) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode == http.StatusNotFound { return ErrNoSuchImage } return err } func isURL(u string) bool { p, err := url.Parse(u) if err != nil { return false } return p.Scheme == "http" || p.Scheme == "https" } func headersWithAuth(auths ...registryAuth) (map[string]string, error) { headers := make(map[string]string) for _, auth := range auths { if auth.isEmpty() { continue } data, err := json.Marshal(auth) if err != nil { return nil, err } headers[auth.headerKey()] = base64.URLEncoding.EncodeToString(data) } return headers, nil } // APIImageSearch reflect the result of a search on the Docker Hub. // // See https://goo.gl/KLO9IZ for more details. type APIImageSearch struct { Description string `json:"description,omitempty" yaml:"description,omitempty" toml:"description,omitempty"` IsOfficial bool `json:"is_official,omitempty" yaml:"is_official,omitempty" toml:"is_official,omitempty"` IsAutomated bool `json:"is_automated,omitempty" yaml:"is_automated,omitempty" toml:"is_automated,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty" toml:"name,omitempty"` StarCount int `json:"star_count,omitempty" yaml:"star_count,omitempty" toml:"star_count,omitempty"` } // SearchImages search the docker hub with a specific given term. // // See https://goo.gl/KLO9IZ for more details. func (c *Client) SearchImages(term string) ([]APIImageSearch, error) { resp, err := c.do(http.MethodGet, "/images/search?term="+term, doOptions{}) if err != nil { return nil, err } defer resp.Body.Close() var searchResult []APIImageSearch if err := json.NewDecoder(resp.Body).Decode(&searchResult); err != nil { return nil, err } return searchResult, nil } // SearchImagesEx search the docker hub with a specific given term and authentication. // // See https://goo.gl/KLO9IZ for more details. func (c *Client) SearchImagesEx(term string, auth AuthConfiguration) ([]APIImageSearch, error) { headers, err := headersWithAuth(auth) if err != nil { return nil, err } resp, err := c.do(http.MethodGet, "/images/search?term="+term, doOptions{ headers: headers, }) if err != nil { return nil, err } defer resp.Body.Close() var searchResult []APIImageSearch if err := json.NewDecoder(resp.Body).Decode(&searchResult); err != nil { return nil, err } return searchResult, nil } // PruneImagesOptions specify parameters to the PruneImages function. // // See https://goo.gl/qfZlbZ for more details. type PruneImagesOptions struct { Filters map[string][]string Context context.Context } // PruneImagesResults specify results from the PruneImages function. // // See https://goo.gl/qfZlbZ for more details. type PruneImagesResults struct { ImagesDeleted []struct{ Untagged, Deleted string } SpaceReclaimed int64 } // PruneImages deletes images which are unused. // // See https://goo.gl/qfZlbZ for more details. func (c *Client) PruneImages(opts PruneImagesOptions) (*PruneImagesResults, error) { path := "/images/prune?" + queryString(opts) resp, err := c.do(http.MethodPost, path, doOptions{context: opts.Context}) if err != nil { return nil, err } defer resp.Body.Close() var results PruneImagesResults if err := json.NewDecoder(resp.Body).Decode(&results); err != nil { return nil, err } return &results, nil } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/misc.go ================================================ // Copyright 2013 go-dockerclient authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package docker import ( "context" "encoding/json" "net" "net/http" "strings" "github.com/docker/docker/api/types/swarm" ) // Version returns version information about the docker server. // // See https://goo.gl/mU7yje for more details. func (c *Client) Version() (*Env, error) { return c.VersionWithContext(context.TODO()) } // VersionWithContext returns version information about the docker server. func (c *Client) VersionWithContext(ctx context.Context) (*Env, error) { resp, err := c.do(http.MethodGet, "/version", doOptions{context: ctx}) if err != nil { return nil, err } defer resp.Body.Close() var env Env if err := env.Decode(resp.Body); err != nil { return nil, err } return &env, nil } // DockerInfo contains information about the Docker server // // See https://goo.gl/bHUoz9 for more details. type DockerInfo struct { ID string Containers int ContainersRunning int ContainersPaused int ContainersStopped int Images int Driver string DriverStatus [][2]string SystemStatus [][2]string Plugins PluginsInfo NFd int NGoroutines int SystemTime string ExecutionDriver string LoggingDriver string CgroupDriver string NEventsListener int KernelVersion string OperatingSystem string OSType string Architecture string IndexServerAddress string RegistryConfig *ServiceConfig SecurityOptions []string NCPU int MemTotal int64 DockerRootDir string HTTPProxy string `json:"HttpProxy"` HTTPSProxy string `json:"HttpsProxy"` NoProxy string Name string Labels []string ServerVersion string ClusterStore string Runtimes map[string]Runtime ClusterAdvertise string Isolation string InitBinary string DefaultRuntime string Swarm swarm.Info LiveRestoreEnabled bool MemoryLimit bool SwapLimit bool KernelMemory bool CPUCfsPeriod bool `json:"CpuCfsPeriod"` CPUCfsQuota bool `json:"CpuCfsQuota"` CPUShares bool CPUSet bool IPv4Forwarding bool BridgeNfIptables bool BridgeNfIP6tables bool `json:"BridgeNfIp6tables"` Debug bool OomKillDisable bool ExperimentalBuild bool } // Runtime describes an OCI runtime // // for more information, see: https://dockr.ly/2NKM8qq type Runtime struct { Path string Args []string `json:"runtimeArgs"` } // PluginsInfo is a struct with the plugins registered with the docker daemon // // for more information, see: https://goo.gl/bHUoz9 type PluginsInfo struct { // List of Volume plugins registered Volume []string // List of Network plugins registered Network []string // List of Authorization plugins registered Authorization []string } // ServiceConfig stores daemon registry services configuration. // // for more information, see: https://goo.gl/7iFFDz type ServiceConfig struct { InsecureRegistryCIDRs []*NetIPNet IndexConfigs map[string]*IndexInfo Mirrors []string } // NetIPNet is the net.IPNet type, which can be marshalled and // unmarshalled to JSON. // // for more information, see: https://goo.gl/7iFFDz type NetIPNet net.IPNet // MarshalJSON returns the JSON representation of the IPNet. func (ipnet *NetIPNet) MarshalJSON() ([]byte, error) { return json.Marshal((*net.IPNet)(ipnet).String()) } // UnmarshalJSON sets the IPNet from a byte array of JSON. func (ipnet *NetIPNet) UnmarshalJSON(b []byte) (err error) { var ipnetStr string if err = json.Unmarshal(b, &ipnetStr); err == nil { var cidr *net.IPNet if _, cidr, err = net.ParseCIDR(ipnetStr); err == nil { *ipnet = NetIPNet(*cidr) } } return } // IndexInfo contains information about a registry. // // for more information, see: https://goo.gl/7iFFDz type IndexInfo struct { Name string Mirrors []string Secure bool Official bool } // Info returns system-wide information about the Docker server. // // See https://goo.gl/ElTHi2 for more details. func (c *Client) Info() (*DockerInfo, error) { resp, err := c.do(http.MethodGet, "/info", doOptions{}) if err != nil { return nil, err } defer resp.Body.Close() var info DockerInfo if err := json.NewDecoder(resp.Body).Decode(&info); err != nil { return nil, err } return &info, nil } // ParseRepositoryTag gets the name of the repository and returns it splitted // in two parts: the repository and the tag. It ignores the digest when it is // present. // // Some examples: // // localhost.localdomain:5000/samalba/hipache:latest -> localhost.localdomain:5000/samalba/hipache, latest // localhost.localdomain:5000/samalba/hipache -> localhost.localdomain:5000/samalba/hipache, "" // busybox:latest@sha256:4a731fb46adc5cefe3ae374a8b6020fc1b6ad667a279647766e9a3cd89f6fa92 -> busybox, latest func ParseRepositoryTag(repoTag string) (repository string, tag string) { parts := strings.SplitN(repoTag, "@", 2) repoTag = parts[0] n := strings.LastIndex(repoTag, ":") if n < 0 { return repoTag, "" } if tag := repoTag[n+1:]; !strings.Contains(tag, "/") { return repoTag[:n], tag } return repoTag, "" } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/network.go ================================================ // Copyright 2015 go-dockerclient authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package docker import ( "context" "encoding/json" "errors" "fmt" "net/http" "net/url" ) // ErrNetworkAlreadyExists is the error returned by CreateNetwork when the // network already exists. var ErrNetworkAlreadyExists = errors.New("network already exists") // Network represents a network. // // See https://goo.gl/6GugX3 for more details. type Network struct { Name string ID string `json:"Id"` Scope string Driver string IPAM IPAMOptions Containers map[string]Endpoint Options map[string]string Internal bool EnableIPv6 bool `json:"EnableIPv6"` Labels map[string]string } // Endpoint contains network resources allocated and used for a container in a network // // See https://goo.gl/6GugX3 for more details. type Endpoint struct { Name string ID string `json:"EndpointID"` MacAddress string IPv4Address string IPv6Address string } // ListNetworks returns all networks. // // See https://goo.gl/6GugX3 for more details. func (c *Client) ListNetworks() ([]Network, error) { resp, err := c.do(http.MethodGet, "/networks", doOptions{}) if err != nil { return nil, err } defer resp.Body.Close() var networks []Network if err := json.NewDecoder(resp.Body).Decode(&networks); err != nil { return nil, err } return networks, nil } // NetworkFilterOpts is an aggregation of key=value that Docker // uses to filter networks type NetworkFilterOpts map[string]map[string]bool // FilteredListNetworks returns all networks with the filters applied // // See goo.gl/zd2mx4 for more details. func (c *Client) FilteredListNetworks(opts NetworkFilterOpts) ([]Network, error) { params, err := json.Marshal(opts) if err != nil { return nil, err } qs := make(url.Values) qs.Add("filters", string(params)) path := "/networks?" + qs.Encode() resp, err := c.do(http.MethodGet, path, doOptions{}) if err != nil { return nil, err } defer resp.Body.Close() var networks []Network if err := json.NewDecoder(resp.Body).Decode(&networks); err != nil { return nil, err } return networks, nil } // NetworkInfo returns information about a network by its ID. // // See https://goo.gl/6GugX3 for more details. func (c *Client) NetworkInfo(id string) (*Network, error) { path := "/networks/" + id resp, err := c.do(http.MethodGet, path, doOptions{}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return nil, &NoSuchNetwork{ID: id} } return nil, err } defer resp.Body.Close() var network Network if err := json.NewDecoder(resp.Body).Decode(&network); err != nil { return nil, err } return &network, nil } // CreateNetworkOptions specify parameters to the CreateNetwork function and // (for now) is the expected body of the "create network" http request message // // See https://goo.gl/6GugX3 for more details. type CreateNetworkOptions struct { Name string `json:"Name" yaml:"Name" toml:"Name"` Driver string `json:"Driver" yaml:"Driver" toml:"Driver"` Scope string `json:"Scope" yaml:"Scope" toml:"Scope"` IPAM *IPAMOptions `json:"IPAM,omitempty" yaml:"IPAM" toml:"IPAM"` ConfigFrom *NetworkConfigFrom `json:"ConfigFrom,omitempty" yaml:"ConfigFrom" toml:"ConfigFrom"` Options map[string]any `json:"Options" yaml:"Options" toml:"Options"` Labels map[string]string `json:"Labels" yaml:"Labels" toml:"Labels"` CheckDuplicate bool `json:"CheckDuplicate" yaml:"CheckDuplicate" toml:"CheckDuplicate"` Internal bool `json:"Internal" yaml:"Internal" toml:"Internal"` EnableIPv6 bool `json:"EnableIPv6" yaml:"EnableIPv6" toml:"EnableIPv6"` Attachable bool `json:"Attachable" yaml:"Attachable" toml:"Attachable"` ConfigOnly bool `json:"ConfigOnly" yaml:"ConfigOnly" toml:"ConfigOnly"` Ingress bool `json:"Ingress" yaml:"Ingress" toml:"Ingress"` Context context.Context `json:"-"` } // NetworkConfigFrom is used in network creation for specifying the source of a // network configuration. type NetworkConfigFrom struct { Network string `json:"Network" yaml:"Network" toml:"Network"` } // IPAMOptions controls IP Address Management when creating a network // // See https://goo.gl/T8kRVH for more details. type IPAMOptions struct { Driver string `json:"Driver" yaml:"Driver" toml:"Driver"` Config []IPAMConfig `json:"Config" yaml:"Config" toml:"Config"` Options map[string]string `json:"Options" yaml:"Options" toml:"Options"` } // IPAMConfig represents IPAM configurations // // See https://goo.gl/T8kRVH for more details. type IPAMConfig struct { Subnet string `json:",omitempty"` IPRange string `json:",omitempty"` Gateway string `json:",omitempty"` AuxAddress map[string]string `json:"AuxiliaryAddresses,omitempty"` } // CreateNetwork creates a new network, returning the network instance, // or an error in case of failure. // // See https://goo.gl/6GugX3 for more details. func (c *Client) CreateNetwork(opts CreateNetworkOptions) (*Network, error) { resp, err := c.do( http.MethodPost, "/networks/create", doOptions{ data: opts, context: opts.Context, }, ) if err != nil { return nil, err } defer resp.Body.Close() type createNetworkResponse struct { ID string } var ( network Network cnr createNetworkResponse ) if err := json.NewDecoder(resp.Body).Decode(&cnr); err != nil { return nil, err } network.Name = opts.Name network.ID = cnr.ID network.Driver = opts.Driver return &network, nil } // RemoveNetwork removes a network or returns an error in case of failure. // // See https://goo.gl/6GugX3 for more details. func (c *Client) RemoveNetwork(id string) error { resp, err := c.do(http.MethodDelete, "/networks/"+id, doOptions{}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return &NoSuchNetwork{ID: id} } return err } resp.Body.Close() return nil } // NetworkConnectionOptions specify parameters to the ConnectNetwork and // DisconnectNetwork function. // // See https://goo.gl/RV7BJU for more details. type NetworkConnectionOptions struct { Container string // EndpointConfig is only applicable to the ConnectNetwork call EndpointConfig *EndpointConfig `json:"EndpointConfig,omitempty"` // Force is only applicable to the DisconnectNetwork call Force bool Context context.Context `json:"-"` } // EndpointConfig stores network endpoint details // // See https://goo.gl/RV7BJU for more details. type EndpointConfig struct { IPAMConfig *EndpointIPAMConfig `json:"IPAMConfig,omitempty" yaml:"IPAMConfig,omitempty" toml:"IPAMConfig,omitempty"` Links []string `json:"Links,omitempty" yaml:"Links,omitempty" toml:"Links,omitempty"` Aliases []string `json:"Aliases,omitempty" yaml:"Aliases,omitempty" toml:"Aliases,omitempty"` NetworkID string `json:"NetworkID,omitempty" yaml:"NetworkID,omitempty" toml:"NetworkID,omitempty"` EndpointID string `json:"EndpointID,omitempty" yaml:"EndpointID,omitempty" toml:"EndpointID,omitempty"` Gateway string `json:"Gateway,omitempty" yaml:"Gateway,omitempty" toml:"Gateway,omitempty"` IPAddress string `json:"IPAddress,omitempty" yaml:"IPAddress,omitempty" toml:"IPAddress,omitempty"` IPPrefixLen int `json:"IPPrefixLen,omitempty" yaml:"IPPrefixLen,omitempty" toml:"IPPrefixLen,omitempty"` IPv6Gateway string `json:"IPv6Gateway,omitempty" yaml:"IPv6Gateway,omitempty" toml:"IPv6Gateway,omitempty"` GlobalIPv6Address string `json:"GlobalIPv6Address,omitempty" yaml:"GlobalIPv6Address,omitempty" toml:"GlobalIPv6Address,omitempty"` GlobalIPv6PrefixLen int `json:"GlobalIPv6PrefixLen,omitempty" yaml:"GlobalIPv6PrefixLen,omitempty" toml:"GlobalIPv6PrefixLen,omitempty"` MacAddress string `json:"MacAddress,omitempty" yaml:"MacAddress,omitempty" toml:"MacAddress,omitempty"` DriverOpts map[string]string `json:"DriverOpts,omitempty" yaml:"DriverOpts,omitempty" toml:"DriverOpts,omitempty"` } // EndpointIPAMConfig represents IPAM configurations for an // endpoint // // See https://goo.gl/RV7BJU for more details. type EndpointIPAMConfig struct { IPv4Address string `json:",omitempty" yaml:"IPv4Address,omitempty"` IPv6Address string `json:",omitempty" yaml:"IPv6Address,omitempty"` } // ConnectNetwork adds a container to a network or returns an error in case of // failure. // // See https://goo.gl/6GugX3 for more details. func (c *Client) ConnectNetwork(id string, opts NetworkConnectionOptions) error { resp, err := c.do(http.MethodPost, "/networks/"+id+"/connect", doOptions{ data: opts, context: opts.Context, }) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return &NoSuchNetworkOrContainer{NetworkID: id, ContainerID: opts.Container} } return err } resp.Body.Close() return nil } // DisconnectNetwork removes a container from a network or returns an error in // case of failure. // // See https://goo.gl/6GugX3 for more details. func (c *Client) DisconnectNetwork(id string, opts NetworkConnectionOptions) error { resp, err := c.do(http.MethodPost, "/networks/"+id+"/disconnect", doOptions{data: opts}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return &NoSuchNetworkOrContainer{NetworkID: id, ContainerID: opts.Container} } return err } resp.Body.Close() return nil } // PruneNetworksOptions specify parameters to the PruneNetworks function. // // See https://goo.gl/kX0S9h for more details. type PruneNetworksOptions struct { Filters map[string][]string Context context.Context } // PruneNetworksResults specify results from the PruneNetworks function. // // See https://goo.gl/kX0S9h for more details. type PruneNetworksResults struct { NetworksDeleted []string } // PruneNetworks deletes networks which are unused. // // See https://goo.gl/kX0S9h for more details. func (c *Client) PruneNetworks(opts PruneNetworksOptions) (*PruneNetworksResults, error) { path := "/networks/prune?" + queryString(opts) resp, err := c.do(http.MethodPost, path, doOptions{context: opts.Context}) if err != nil { return nil, err } defer resp.Body.Close() var results PruneNetworksResults if err := json.NewDecoder(resp.Body).Decode(&results); err != nil { return nil, err } return &results, nil } // NoSuchNetwork is the error returned when a given network does not exist. type NoSuchNetwork struct { ID string } func (err *NoSuchNetwork) Error() string { return fmt.Sprintf("No such network: %s", err.ID) } // NoSuchNetworkOrContainer is the error returned when a given network or // container does not exist. type NoSuchNetworkOrContainer struct { NetworkID string ContainerID string } func (err *NoSuchNetworkOrContainer) Error() string { return fmt.Sprintf("No such network (%s) or container (%s)", err.NetworkID, err.ContainerID) } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/plugin.go ================================================ // Copyright 2018 go-dockerclient authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package docker import ( "context" "encoding/json" "errors" "io" "net/http" ) // PluginPrivilege represents a privilege for a plugin. type PluginPrivilege struct { Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"` Description string `json:"Description,omitempty" yaml:"Description,omitempty" toml:"Description,omitempty"` Value []string `json:"Value,omitempty" yaml:"Value,omitempty" toml:"Value,omitempty"` } // InstallPluginOptions specify parameters to the InstallPlugins function. // // See https://goo.gl/C4t7Tz for more details. type InstallPluginOptions struct { Remote string Name string Plugins []PluginPrivilege `qs:"-"` Auth AuthConfiguration Context context.Context } // InstallPlugins installs a plugin or returns an error in case of failure. // // See https://goo.gl/C4t7Tz for more details. func (c *Client) InstallPlugins(opts InstallPluginOptions) error { headers, err := headersWithAuth(opts.Auth) if err != nil { return err } path := "/plugins/pull?" + queryString(opts) resp, err := c.do(http.MethodPost, path, doOptions{ data: opts.Plugins, context: opts.Context, headers: headers, }) if err != nil { return err } defer resp.Body.Close() // PullPlugin streams back the progress of the pull, we must consume the whole body // otherwise the pull will be canceled on the engine. if _, err := io.ReadAll(resp.Body); err != nil { return err } return nil } // PluginSettings stores plugin settings. // // See https://goo.gl/C4t7Tz for more details. type PluginSettings struct { Env []string `json:"Env,omitempty" yaml:"Env,omitempty" toml:"Env,omitempty"` Args []string `json:"Args,omitempty" yaml:"Args,omitempty" toml:"Args,omitempty"` Devices []string `json:"Devices,omitempty" yaml:"Devices,omitempty" toml:"Devices,omitempty"` } // PluginInterface stores plugin interface. // // See https://goo.gl/C4t7Tz for more details. type PluginInterface struct { Types []string `json:"Types,omitempty" yaml:"Types,omitempty" toml:"Types,omitempty"` Socket string `json:"Socket,omitempty" yaml:"Socket,omitempty" toml:"Socket,omitempty"` } // PluginNetwork stores plugin network type. // // See https://goo.gl/C4t7Tz for more details. type PluginNetwork struct { Type string `json:"Type,omitempty" yaml:"Type,omitempty" toml:"Type,omitempty"` } // PluginLinux stores plugin linux setting. // // See https://goo.gl/C4t7Tz for more details. type PluginLinux struct { Capabilities []string `json:"Capabilities,omitempty" yaml:"Capabilities,omitempty" toml:"Capabilities,omitempty"` AllowAllDevices bool `json:"AllowAllDevices,omitempty" yaml:"AllowAllDevices,omitempty" toml:"AllowAllDevices,omitempty"` Devices []PluginLinuxDevices `json:"Devices,omitempty" yaml:"Devices,omitempty" toml:"Devices,omitempty"` } // PluginLinuxDevices stores plugin linux device setting. // // See https://goo.gl/C4t7Tz for more details. type PluginLinuxDevices struct { Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"` Description string `json:"Documentation,omitempty" yaml:"Documentation,omitempty" toml:"Documentation,omitempty"` Settable []string `json:"Settable,omitempty" yaml:"Settable,omitempty" toml:"Settable,omitempty"` Path string `json:"Path,omitempty" yaml:"Path,omitempty" toml:"Path,omitempty"` } // PluginEnv stores plugin environment. // // See https://goo.gl/C4t7Tz for more details. type PluginEnv struct { Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"` Description string `json:"Description,omitempty" yaml:"Description,omitempty" toml:"Description,omitempty"` Settable []string `json:"Settable,omitempty" yaml:"Settable,omitempty" toml:"Settable,omitempty"` Value string `json:"Value,omitempty" yaml:"Value,omitempty" toml:"Value,omitempty"` } // PluginArgs stores plugin arguments. // // See https://goo.gl/C4t7Tz for more details. type PluginArgs struct { Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"` Description string `json:"Description,omitempty" yaml:"Description,omitempty" toml:"Description,omitempty"` Settable []string `json:"Settable,omitempty" yaml:"Settable,omitempty" toml:"Settable,omitempty"` Value []string `json:"Value,omitempty" yaml:"Value,omitempty" toml:"Value,omitempty"` } // PluginUser stores plugin user. // // See https://goo.gl/C4t7Tz for more details. type PluginUser struct { UID int32 `json:"UID,omitempty" yaml:"UID,omitempty" toml:"UID,omitempty"` GID int32 `json:"GID,omitempty" yaml:"GID,omitempty" toml:"GID,omitempty"` } // PluginConfig stores plugin config. // // See https://goo.gl/C4t7Tz for more details. type PluginConfig struct { Description string `json:"Description,omitempty" yaml:"Description,omitempty" toml:"Description,omitempty"` Documentation string Interface PluginInterface `json:"Interface,omitempty" yaml:"Interface,omitempty" toml:"Interface,omitempty"` Entrypoint []string `json:"Entrypoint,omitempty" yaml:"Entrypoint,omitempty" toml:"Entrypoint,omitempty"` WorkDir string `json:"WorkDir,omitempty" yaml:"WorkDir,omitempty" toml:"WorkDir,omitempty"` User PluginUser `json:"User,omitempty" yaml:"User,omitempty" toml:"User,omitempty"` Network PluginNetwork `json:"Network,omitempty" yaml:"Network,omitempty" toml:"Network,omitempty"` Linux PluginLinux `json:"Linux,omitempty" yaml:"Linux,omitempty" toml:"Linux,omitempty"` PropagatedMount string `json:"PropagatedMount,omitempty" yaml:"PropagatedMount,omitempty" toml:"PropagatedMount,omitempty"` Mounts []Mount `json:"Mounts,omitempty" yaml:"Mounts,omitempty" toml:"Mounts,omitempty"` Env []PluginEnv `json:"Env,omitempty" yaml:"Env,omitempty" toml:"Env,omitempty"` Args PluginArgs `json:"Args,omitempty" yaml:"Args,omitempty" toml:"Args,omitempty"` } // PluginDetail specify results from the ListPlugins function. // // See https://goo.gl/C4t7Tz for more details. type PluginDetail struct { ID string `json:"Id,omitempty" yaml:"Id,omitempty" toml:"Id,omitempty"` Name string `json:"Name,omitempty" yaml:"Name,omitempty" toml:"Name,omitempty"` Tag string `json:"Tag,omitempty" yaml:"Tag,omitempty" toml:"Tag,omitempty"` Active bool `json:"Enabled,omitempty" yaml:"Active,omitempty" toml:"Active,omitempty"` Settings PluginSettings `json:"Settings,omitempty" yaml:"Settings,omitempty" toml:"Settings,omitempty"` Config PluginConfig `json:"Config,omitempty" yaml:"Config,omitempty" toml:"Config,omitempty"` } // ListPlugins returns pluginDetails or an error. // // See https://goo.gl/C4t7Tz for more details. func (c *Client) ListPlugins(ctx context.Context) ([]PluginDetail, error) { resp, err := c.do(http.MethodGet, "/plugins", doOptions{ context: ctx, }) if err != nil { return nil, err } defer resp.Body.Close() pluginDetails := make([]PluginDetail, 0) if err := json.NewDecoder(resp.Body).Decode(&pluginDetails); err != nil { return nil, err } return pluginDetails, nil } // ListFilteredPluginsOptions specify parameters to the ListFilteredPlugins function. // // See https://goo.gl/C4t7Tz for more details. type ListFilteredPluginsOptions struct { Filters map[string][]string Context context.Context } // ListFilteredPlugins returns pluginDetails or an error. // // See https://goo.gl/rmdmWg for more details. func (c *Client) ListFilteredPlugins(opts ListFilteredPluginsOptions) ([]PluginDetail, error) { path := "/plugins/json?" + queryString(opts) resp, err := c.do(http.MethodGet, path, doOptions{ context: opts.Context, }) if err != nil { return nil, err } defer resp.Body.Close() pluginDetails := make([]PluginDetail, 0) if err := json.NewDecoder(resp.Body).Decode(&pluginDetails); err != nil { return nil, err } return pluginDetails, nil } // GetPluginPrivileges returns pluginPrivileges or an error. // // See https://goo.gl/C4t7Tz for more details. func (c *Client) GetPluginPrivileges(remote string, ctx context.Context) ([]PluginPrivilege, error) { return c.GetPluginPrivilegesWithOptions( GetPluginPrivilegesOptions{ Remote: remote, Context: ctx, }) } // GetPluginPrivilegesOptions specify parameters to the GetPluginPrivilegesWithOptions function. // // See https://goo.gl/C4t7Tz for more details. type GetPluginPrivilegesOptions struct { Remote string Auth AuthConfiguration Context context.Context } // GetPluginPrivilegesWithOptions returns pluginPrivileges or an error. // // See https://goo.gl/C4t7Tz for more details. func (c *Client) GetPluginPrivilegesWithOptions(opts GetPluginPrivilegesOptions) ([]PluginPrivilege, error) { headers, err := headersWithAuth(opts.Auth) if err != nil { return nil, err } path := "/plugins/privileges?" + queryString(opts) resp, err := c.do(http.MethodGet, path, doOptions{ context: opts.Context, headers: headers, }) if err != nil { return nil, err } defer resp.Body.Close() var pluginPrivileges []PluginPrivilege if err := json.NewDecoder(resp.Body).Decode(&pluginPrivileges); err != nil { return nil, err } return pluginPrivileges, nil } // InspectPlugins returns a pluginDetail or an error. // // See https://goo.gl/C4t7Tz for more details. func (c *Client) InspectPlugins(name string, ctx context.Context) (*PluginDetail, error) { resp, err := c.do(http.MethodGet, "/plugins/"+name+"/json", doOptions{ context: ctx, }) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return nil, &NoSuchPlugin{ID: name} } return nil, err } defer resp.Body.Close() var pluginDetail PluginDetail if err := json.NewDecoder(resp.Body).Decode(&pluginDetail); err != nil { return nil, err } return &pluginDetail, nil } // RemovePluginOptions specify parameters to the RemovePlugin function. // // See https://goo.gl/C4t7Tz for more details. type RemovePluginOptions struct { // The Name of the plugin. Name string `qs:"-"` Force bool `qs:"force"` Context context.Context } // RemovePlugin returns a PluginDetail or an error. // // See https://goo.gl/C4t7Tz for more details. func (c *Client) RemovePlugin(opts RemovePluginOptions) (*PluginDetail, error) { path := "/plugins/" + opts.Name + "?" + queryString(opts) resp, err := c.do(http.MethodDelete, path, doOptions{context: opts.Context}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return nil, &NoSuchPlugin{ID: opts.Name} } return nil, err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return nil, err } if len(body) == 0 { // Seems like newer docker versions won't return the plugindetail after removal return nil, nil } var pluginDetail PluginDetail if err := json.Unmarshal(body, &pluginDetail); err != nil { return nil, err } return &pluginDetail, nil } // EnablePluginOptions specify parameters to the EnablePlugin function. // // See https://goo.gl/C4t7Tz for more details. type EnablePluginOptions struct { // The Name of the plugin. Name string `qs:"-"` Timeout int64 `qs:"timeout"` Context context.Context } // EnablePlugin enables plugin that opts point or returns an error. // // See https://goo.gl/C4t7Tz for more details. func (c *Client) EnablePlugin(opts EnablePluginOptions) error { path := "/plugins/" + opts.Name + "/enable?" + queryString(opts) resp, err := c.do(http.MethodPost, path, doOptions{context: opts.Context}) if err != nil { return err } resp.Body.Close() return nil } // DisablePluginOptions specify parameters to the DisablePlugin function. // // See https://goo.gl/C4t7Tz for more details. type DisablePluginOptions struct { // The Name of the plugin. Name string `qs:"-"` Context context.Context } // DisablePlugin disables plugin that opts point or returns an error. // // See https://goo.gl/C4t7Tz for more details. func (c *Client) DisablePlugin(opts DisablePluginOptions) error { path := "/plugins/" + opts.Name + "/disable" resp, err := c.do(http.MethodPost, path, doOptions{context: opts.Context}) if err != nil { return err } resp.Body.Close() return nil } // CreatePluginOptions specify parameters to the CreatePlugin function. // // See https://goo.gl/C4t7Tz for more details. type CreatePluginOptions struct { // The Name of the plugin. Name string `qs:"name"` // Path to tar containing plugin Path string `qs:"-"` Context context.Context } // CreatePlugin creates plugin that opts point or returns an error. // // See https://goo.gl/C4t7Tz for more details. func (c *Client) CreatePlugin(opts CreatePluginOptions) (string, error) { path := "/plugins/create?" + queryString(opts) resp, err := c.do(http.MethodPost, path, doOptions{ data: opts.Path, context: opts.Context, }) if err != nil { return "", err } defer resp.Body.Close() containerNameBytes, err := io.ReadAll(resp.Body) if err != nil { return "", err } return string(containerNameBytes), nil } // PushPluginOptions specify parameters to PushPlugin function. // // See https://goo.gl/C4t7Tz for more details. type PushPluginOptions struct { // The Name of the plugin. Name string Context context.Context } // PushPlugin pushes plugin that opts point or returns an error. // // See https://goo.gl/C4t7Tz for more details. func (c *Client) PushPlugin(opts PushPluginOptions) error { path := "/plugins/" + opts.Name + "/push" resp, err := c.do(http.MethodPost, path, doOptions{context: opts.Context}) if err != nil { return err } resp.Body.Close() return nil } // ConfigurePluginOptions specify parameters to the ConfigurePlugin // // See https://goo.gl/C4t7Tz for more details. type ConfigurePluginOptions struct { // The Name of the plugin. Name string `qs:"name"` Envs []string Context context.Context } // ConfigurePlugin configures plugin that opts point or returns an error. // // See https://goo.gl/C4t7Tz for more details. func (c *Client) ConfigurePlugin(opts ConfigurePluginOptions) error { path := "/plugins/" + opts.Name + "/set" resp, err := c.do(http.MethodPost, path, doOptions{ data: opts.Envs, context: opts.Context, }) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return &NoSuchPlugin{ID: opts.Name} } return err } resp.Body.Close() return nil } // NoSuchPlugin is the error returned when a given plugin does not exist. type NoSuchPlugin struct { ID string Err error } func (err *NoSuchPlugin) Error() string { if err.Err != nil { return err.Err.Error() } return "No such plugin: " + err.ID } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/registry_auth.go ================================================ // Copyright 2013 go-dockerclient authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package docker type registryAuth interface { isEmpty() bool headerKey() string } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/signal.go ================================================ // Copyright 2014 go-dockerclient authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package docker // Signal represents a signal that can be send to the container on // KillContainer call. type Signal int // These values represent all signals available on Linux, where containers will // be running. const ( SIGABRT = Signal(0x6) SIGALRM = Signal(0xe) SIGBUS = Signal(0x7) SIGCHLD = Signal(0x11) SIGCLD = Signal(0x11) SIGCONT = Signal(0x12) SIGFPE = Signal(0x8) SIGHUP = Signal(0x1) SIGILL = Signal(0x4) SIGINT = Signal(0x2) SIGIO = Signal(0x1d) SIGIOT = Signal(0x6) SIGKILL = Signal(0x9) SIGPIPE = Signal(0xd) SIGPOLL = Signal(0x1d) SIGPROF = Signal(0x1b) SIGPWR = Signal(0x1e) SIGQUIT = Signal(0x3) SIGSEGV = Signal(0xb) SIGSTKFLT = Signal(0x10) SIGSTOP = Signal(0x13) SIGSYS = Signal(0x1f) SIGTERM = Signal(0xf) SIGTRAP = Signal(0x5) SIGTSTP = Signal(0x14) SIGTTIN = Signal(0x15) SIGTTOU = Signal(0x16) SIGUNUSED = Signal(0x1f) SIGURG = Signal(0x17) SIGUSR1 = Signal(0xa) SIGUSR2 = Signal(0xc) SIGVTALRM = Signal(0x1a) SIGWINCH = Signal(0x1c) SIGXCPU = Signal(0x18) SIGXFSZ = Signal(0x19) ) ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/swarm.go ================================================ // Copyright 2016 go-dockerclient authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package docker import ( "context" "encoding/json" "errors" "net/http" "net/url" "strconv" "github.com/docker/docker/api/types/swarm" ) var ( // ErrNodeAlreadyInSwarm is the error returned by InitSwarm and JoinSwarm // when the node is already part of a Swarm. ErrNodeAlreadyInSwarm = errors.New("node already in a Swarm") // ErrNodeNotInSwarm is the error returned by LeaveSwarm and UpdateSwarm // when the node is not part of a Swarm. ErrNodeNotInSwarm = errors.New("node is not in a Swarm") ) // InitSwarmOptions specify parameters to the InitSwarm function. // See https://goo.gl/hzkgWu for more details. type InitSwarmOptions struct { swarm.InitRequest Context context.Context } // InitSwarm initializes a new Swarm and returns the node ID. // See https://goo.gl/ZWyG1M for more details. func (c *Client) InitSwarm(opts InitSwarmOptions) (string, error) { path := "/swarm/init" resp, err := c.do(http.MethodPost, path, doOptions{ data: opts.InitRequest, forceJSON: true, context: opts.Context, }) if err != nil { var e *Error if errors.As(err, &e) && (e.Status == http.StatusNotAcceptable || e.Status == http.StatusServiceUnavailable) { return "", ErrNodeAlreadyInSwarm } return "", err } defer resp.Body.Close() var response string if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { return "", err } return response, nil } // JoinSwarmOptions specify parameters to the JoinSwarm function. // See https://goo.gl/TdhJWU for more details. type JoinSwarmOptions struct { swarm.JoinRequest Context context.Context } // JoinSwarm joins an existing Swarm. // See https://goo.gl/N59IP1 for more details. func (c *Client) JoinSwarm(opts JoinSwarmOptions) error { path := "/swarm/join" resp, err := c.do(http.MethodPost, path, doOptions{ data: opts.JoinRequest, forceJSON: true, context: opts.Context, }) if err != nil { var e *Error if errors.As(err, &e) && (e.Status == http.StatusNotAcceptable || e.Status == http.StatusServiceUnavailable) { return ErrNodeAlreadyInSwarm } } resp.Body.Close() return err } // LeaveSwarmOptions specify parameters to the LeaveSwarm function. // See https://goo.gl/UWDlLg for more details. type LeaveSwarmOptions struct { Force bool Context context.Context } // LeaveSwarm leaves a Swarm. // See https://goo.gl/FTX1aD for more details. func (c *Client) LeaveSwarm(opts LeaveSwarmOptions) error { params := make(url.Values) params.Set("force", strconv.FormatBool(opts.Force)) path := "/swarm/leave?" + params.Encode() resp, err := c.do(http.MethodPost, path, doOptions{ context: opts.Context, }) if err != nil { var e *Error if errors.As(err, &e) && (e.Status == http.StatusNotAcceptable || e.Status == http.StatusServiceUnavailable) { return ErrNodeNotInSwarm } } resp.Body.Close() return err } // UpdateSwarmOptions specify parameters to the UpdateSwarm function. // See https://goo.gl/vFbq36 for more details. type UpdateSwarmOptions struct { Version int RotateWorkerToken bool RotateManagerToken bool Swarm swarm.Spec Context context.Context } // UpdateSwarm updates a Swarm. // See https://goo.gl/iJFnsw for more details. func (c *Client) UpdateSwarm(opts UpdateSwarmOptions) error { params := make(url.Values) params.Set("version", strconv.Itoa(opts.Version)) params.Set("rotateWorkerToken", strconv.FormatBool(opts.RotateWorkerToken)) params.Set("rotateManagerToken", strconv.FormatBool(opts.RotateManagerToken)) path := "/swarm/update?" + params.Encode() resp, err := c.do(http.MethodPost, path, doOptions{ data: opts.Swarm, forceJSON: true, context: opts.Context, }) if err != nil { var e *Error if errors.As(err, &e) && (e.Status == http.StatusNotAcceptable || e.Status == http.StatusServiceUnavailable) { return ErrNodeNotInSwarm } } resp.Body.Close() return err } // InspectSwarm inspects a Swarm. // See https://goo.gl/MFwgX9 for more details. func (c *Client) InspectSwarm(ctx context.Context) (swarm.Swarm, error) { response := swarm.Swarm{} resp, err := c.do(http.MethodGet, "/swarm", doOptions{ context: ctx, }) if err != nil { var e *Error if errors.As(err, &e) && (e.Status == http.StatusNotAcceptable || e.Status == http.StatusServiceUnavailable) { return response, ErrNodeNotInSwarm } return response, err } defer resp.Body.Close() err = json.NewDecoder(resp.Body).Decode(&response) return response, err } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/swarm_configs.go ================================================ // Copyright 2017 go-dockerclient authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package docker import ( "context" "encoding/json" "errors" "net/http" "net/url" "strconv" "github.com/docker/docker/api/types/swarm" ) // NoSuchConfig is the error returned when a given config does not exist. type NoSuchConfig struct { ID string Err error } func (err *NoSuchConfig) Error() string { if err.Err != nil { return err.Err.Error() } return "No such config: " + err.ID } // CreateConfigOptions specify parameters to the CreateConfig function. // // See https://goo.gl/KrVjHz for more details. type CreateConfigOptions struct { Auth AuthConfiguration `qs:"-"` swarm.ConfigSpec Context context.Context } // CreateConfig creates a new config, returning the config instance // or an error in case of failure. // // See https://goo.gl/KrVjHz for more details. func (c *Client) CreateConfig(opts CreateConfigOptions) (*swarm.Config, error) { headers, err := headersWithAuth(opts.Auth) if err != nil { return nil, err } path := "/configs/create?" + queryString(opts) resp, err := c.do(http.MethodPost, path, doOptions{ headers: headers, data: opts.ConfigSpec, forceJSON: true, context: opts.Context, }) if err != nil { return nil, err } defer resp.Body.Close() var config swarm.Config if err := json.NewDecoder(resp.Body).Decode(&config); err != nil { return nil, err } return &config, nil } // RemoveConfigOptions encapsulates options to remove a config. // // See https://goo.gl/Tqrtya for more details. type RemoveConfigOptions struct { ID string `qs:"-"` Context context.Context } // RemoveConfig removes a config, returning an error in case of failure. // // See https://goo.gl/Tqrtya for more details. func (c *Client) RemoveConfig(opts RemoveConfigOptions) error { path := "/configs/" + opts.ID resp, err := c.do(http.MethodDelete, path, doOptions{context: opts.Context}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return &NoSuchConfig{ID: opts.ID} } return err } resp.Body.Close() return nil } // UpdateConfigOptions specify parameters to the UpdateConfig function. // // See https://goo.gl/wu3MmS for more details. type UpdateConfigOptions struct { Auth AuthConfiguration `qs:"-"` swarm.ConfigSpec Context context.Context Version uint64 } // UpdateConfig updates the config at ID with the options // // Only label can be updated // https://docs.docker.com/engine/api/v1.33/#operation/ConfigUpdate // See https://goo.gl/wu3MmS for more details. func (c *Client) UpdateConfig(id string, opts UpdateConfigOptions) error { headers, err := headersWithAuth(opts.Auth) if err != nil { return err } params := make(url.Values) params.Set("version", strconv.FormatUint(opts.Version, 10)) resp, err := c.do(http.MethodPost, "/configs/"+id+"/update?"+params.Encode(), doOptions{ headers: headers, data: opts.ConfigSpec, forceJSON: true, context: opts.Context, }) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return &NoSuchConfig{ID: id} } return err } defer resp.Body.Close() return nil } // InspectConfig returns information about a config by its ID. // // See https://goo.gl/dHmr75 for more details. func (c *Client) InspectConfig(id string) (*swarm.Config, error) { path := "/configs/" + id resp, err := c.do(http.MethodGet, path, doOptions{}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return nil, &NoSuchConfig{ID: id} } return nil, err } defer resp.Body.Close() var config swarm.Config if err := json.NewDecoder(resp.Body).Decode(&config); err != nil { return nil, err } return &config, nil } // ListConfigsOptions specify parameters to the ListConfigs function. // // See https://goo.gl/DwvNMd for more details. type ListConfigsOptions struct { Filters map[string][]string Context context.Context } // ListConfigs returns a slice of configs matching the given criteria. // // See https://goo.gl/DwvNMd for more details. func (c *Client) ListConfigs(opts ListConfigsOptions) ([]swarm.Config, error) { path := "/configs?" + queryString(opts) resp, err := c.do(http.MethodGet, path, doOptions{context: opts.Context}) if err != nil { return nil, err } defer resp.Body.Close() var configs []swarm.Config if err := json.NewDecoder(resp.Body).Decode(&configs); err != nil { return nil, err } return configs, nil } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/swarm_node.go ================================================ // Copyright 2016 go-dockerclient authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package docker import ( "context" "encoding/json" "errors" "net/http" "net/url" "strconv" "github.com/docker/docker/api/types/swarm" ) // NoSuchNode is the error returned when a given node does not exist. type NoSuchNode struct { ID string Err error } func (err *NoSuchNode) Error() string { if err.Err != nil { return err.Err.Error() } return "No such node: " + err.ID } // ListNodesOptions specify parameters to the ListNodes function. // // See http://goo.gl/3K4GwU for more details. type ListNodesOptions struct { Filters map[string][]string Context context.Context } // ListNodes returns a slice of nodes matching the given criteria. // // See http://goo.gl/3K4GwU for more details. func (c *Client) ListNodes(opts ListNodesOptions) ([]swarm.Node, error) { path := "/nodes?" + queryString(opts) resp, err := c.do(http.MethodGet, path, doOptions{context: opts.Context}) if err != nil { return nil, err } defer resp.Body.Close() var nodes []swarm.Node if err := json.NewDecoder(resp.Body).Decode(&nodes); err != nil { return nil, err } return nodes, nil } // InspectNode returns information about a node by its ID. // // See http://goo.gl/WjkTOk for more details. func (c *Client) InspectNode(id string) (*swarm.Node, error) { resp, err := c.do(http.MethodGet, "/nodes/"+id, doOptions{}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return nil, &NoSuchNode{ID: id} } return nil, err } defer resp.Body.Close() var node swarm.Node if err := json.NewDecoder(resp.Body).Decode(&node); err != nil { return nil, err } return &node, nil } // UpdateNodeOptions specify parameters to the NodeUpdate function. // // See http://goo.gl/VPBFgA for more details. type UpdateNodeOptions struct { swarm.NodeSpec Version uint64 Context context.Context } // UpdateNode updates a node. // // See http://goo.gl/VPBFgA for more details. func (c *Client) UpdateNode(id string, opts UpdateNodeOptions) error { params := make(url.Values) params.Set("version", strconv.FormatUint(opts.Version, 10)) path := "/nodes/" + id + "/update?" + params.Encode() resp, err := c.do(http.MethodPost, path, doOptions{ context: opts.Context, forceJSON: true, data: opts.NodeSpec, }) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return &NoSuchNode{ID: id} } return err } resp.Body.Close() return nil } // RemoveNodeOptions specify parameters to the RemoveNode function. // // See http://goo.gl/0SNvYg for more details. type RemoveNodeOptions struct { ID string Force bool Context context.Context } // RemoveNode removes a node. // // See http://goo.gl/0SNvYg for more details. func (c *Client) RemoveNode(opts RemoveNodeOptions) error { params := make(url.Values) params.Set("force", strconv.FormatBool(opts.Force)) path := "/nodes/" + opts.ID + "?" + params.Encode() resp, err := c.do(http.MethodDelete, path, doOptions{context: opts.Context}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return &NoSuchNode{ID: opts.ID} } return err } resp.Body.Close() return nil } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/swarm_secrets.go ================================================ // Copyright 2016 go-dockerclient authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package docker import ( "context" "encoding/json" "errors" "net/http" "net/url" "strconv" "github.com/docker/docker/api/types/swarm" ) // NoSuchSecret is the error returned when a given secret does not exist. type NoSuchSecret struct { ID string Err error } func (err *NoSuchSecret) Error() string { if err.Err != nil { return err.Err.Error() } return "No such secret: " + err.ID } // CreateSecretOptions specify parameters to the CreateSecret function. // // See https://goo.gl/KrVjHz for more details. type CreateSecretOptions struct { Auth AuthConfiguration `qs:"-"` swarm.SecretSpec Context context.Context } // CreateSecret creates a new secret, returning the secret instance // or an error in case of failure. // // See https://goo.gl/KrVjHz for more details. func (c *Client) CreateSecret(opts CreateSecretOptions) (*swarm.Secret, error) { headers, err := headersWithAuth(opts.Auth) if err != nil { return nil, err } path := "/secrets/create?" + queryString(opts) resp, err := c.do(http.MethodPost, path, doOptions{ headers: headers, data: opts.SecretSpec, forceJSON: true, context: opts.Context, }) if err != nil { return nil, err } defer resp.Body.Close() var secret swarm.Secret if err := json.NewDecoder(resp.Body).Decode(&secret); err != nil { return nil, err } return &secret, nil } // RemoveSecretOptions encapsulates options to remove a secret. // // See https://goo.gl/Tqrtya for more details. type RemoveSecretOptions struct { ID string `qs:"-"` Context context.Context } // RemoveSecret removes a secret, returning an error in case of failure. // // See https://goo.gl/Tqrtya for more details. func (c *Client) RemoveSecret(opts RemoveSecretOptions) error { path := "/secrets/" + opts.ID resp, err := c.do(http.MethodDelete, path, doOptions{context: opts.Context}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return &NoSuchSecret{ID: opts.ID} } return err } resp.Body.Close() return nil } // UpdateSecretOptions specify parameters to the UpdateSecret function. // // Only label can be updated // See https://docs.docker.com/engine/api/v1.33/#operation/SecretUpdate // See https://goo.gl/wu3MmS for more details. type UpdateSecretOptions struct { Auth AuthConfiguration `qs:"-"` swarm.SecretSpec Context context.Context Version uint64 } // UpdateSecret updates the secret at ID with the options // // See https://goo.gl/wu3MmS for more details. func (c *Client) UpdateSecret(id string, opts UpdateSecretOptions) error { headers, err := headersWithAuth(opts.Auth) if err != nil { return err } params := make(url.Values) params.Set("version", strconv.FormatUint(opts.Version, 10)) resp, err := c.do(http.MethodPost, "/secrets/"+id+"/update?"+params.Encode(), doOptions{ headers: headers, data: opts.SecretSpec, forceJSON: true, context: opts.Context, }) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return &NoSuchSecret{ID: id} } return err } defer resp.Body.Close() return nil } // InspectSecret returns information about a secret by its ID. // // See https://goo.gl/dHmr75 for more details. func (c *Client) InspectSecret(id string) (*swarm.Secret, error) { path := "/secrets/" + id resp, err := c.do(http.MethodGet, path, doOptions{}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return nil, &NoSuchSecret{ID: id} } return nil, err } defer resp.Body.Close() var secret swarm.Secret if err := json.NewDecoder(resp.Body).Decode(&secret); err != nil { return nil, err } return &secret, nil } // ListSecretsOptions specify parameters to the ListSecrets function. // // See https://goo.gl/DwvNMd for more details. type ListSecretsOptions struct { Filters map[string][]string Context context.Context } // ListSecrets returns a slice of secrets matching the given criteria. // // See https://goo.gl/DwvNMd for more details. func (c *Client) ListSecrets(opts ListSecretsOptions) ([]swarm.Secret, error) { path := "/secrets?" + queryString(opts) resp, err := c.do(http.MethodGet, path, doOptions{context: opts.Context}) if err != nil { return nil, err } defer resp.Body.Close() var secrets []swarm.Secret if err := json.NewDecoder(resp.Body).Decode(&secrets); err != nil { return nil, err } return secrets, nil } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/swarm_service.go ================================================ // Copyright 2016 go-dockerclient authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package docker import ( "context" "encoding/json" "errors" "io" "net/http" "time" "github.com/docker/docker/api/types/swarm" ) // NoSuchService is the error returned when a given service does not exist. type NoSuchService struct { ID string Err error } func (err *NoSuchService) Error() string { if err.Err != nil { return err.Err.Error() } return "No such service: " + err.ID } // CreateServiceOptions specify parameters to the CreateService function. // // See https://goo.gl/KrVjHz for more details. type CreateServiceOptions struct { Auth AuthConfiguration `qs:"-"` swarm.ServiceSpec Context context.Context } // CreateService creates a new service, returning the service instance // or an error in case of failure. // // See https://goo.gl/KrVjHz for more details. func (c *Client) CreateService(opts CreateServiceOptions) (*swarm.Service, error) { headers, err := headersWithAuth(opts.Auth) if err != nil { return nil, err } path := "/services/create?" + queryString(opts) resp, err := c.do(http.MethodPost, path, doOptions{ headers: headers, data: opts.ServiceSpec, forceJSON: true, context: opts.Context, }) if err != nil { return nil, err } defer resp.Body.Close() var service swarm.Service if err := json.NewDecoder(resp.Body).Decode(&service); err != nil { return nil, err } return &service, nil } // RemoveServiceOptions encapsulates options to remove a service. // // See https://goo.gl/Tqrtya for more details. type RemoveServiceOptions struct { ID string `qs:"-"` Context context.Context } // RemoveService removes a service, returning an error in case of failure. // // See https://goo.gl/Tqrtya for more details. func (c *Client) RemoveService(opts RemoveServiceOptions) error { path := "/services/" + opts.ID resp, err := c.do(http.MethodDelete, path, doOptions{context: opts.Context}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return &NoSuchService{ID: opts.ID} } return err } resp.Body.Close() return nil } // UpdateServiceOptions specify parameters to the UpdateService function. // // See https://goo.gl/wu3MmS for more details. type UpdateServiceOptions struct { Auth AuthConfiguration `qs:"-"` swarm.ServiceSpec `qs:"-"` Context context.Context Version uint64 Rollback string } // UpdateService updates the service at ID with the options // // See https://goo.gl/wu3MmS for more details. func (c *Client) UpdateService(id string, opts UpdateServiceOptions) error { headers, err := headersWithAuth(opts.Auth) if err != nil { return err } resp, err := c.do(http.MethodPost, "/services/"+id+"/update?"+queryString(opts), doOptions{ headers: headers, data: opts.ServiceSpec, forceJSON: true, context: opts.Context, }) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return &NoSuchService{ID: id} } return err } defer resp.Body.Close() return nil } // InspectService returns information about a service by its ID. // // See https://goo.gl/dHmr75 for more details. func (c *Client) InspectService(id string) (*swarm.Service, error) { path := "/services/" + id resp, err := c.do(http.MethodGet, path, doOptions{}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return nil, &NoSuchService{ID: id} } return nil, err } defer resp.Body.Close() var service swarm.Service if err := json.NewDecoder(resp.Body).Decode(&service); err != nil { return nil, err } return &service, nil } // ListServicesOptions specify parameters to the ListServices function. // // See https://goo.gl/DwvNMd for more details. type ListServicesOptions struct { Filters map[string][]string Status bool Context context.Context } // ListServices returns a slice of services matching the given criteria. // // See https://goo.gl/DwvNMd for more details. func (c *Client) ListServices(opts ListServicesOptions) ([]swarm.Service, error) { path := "/services?" + queryString(opts) resp, err := c.do(http.MethodGet, path, doOptions{context: opts.Context}) if err != nil { return nil, err } defer resp.Body.Close() var services []swarm.Service if err := json.NewDecoder(resp.Body).Decode(&services); err != nil { return nil, err } return services, nil } // LogsServiceOptions represents the set of options used when getting logs from a // service. type LogsServiceOptions struct { Context context.Context Service string `qs:"-"` OutputStream io.Writer `qs:"-"` ErrorStream io.Writer `qs:"-"` InactivityTimeout time.Duration `qs:"-"` Tail string Since int64 // Use raw terminal? Usually true when the container contains a TTY. RawTerminal bool `qs:"-"` Follow bool Stdout bool Stderr bool Timestamps bool Details bool } // GetServiceLogs gets stdout and stderr logs from the specified service. // // When LogsServiceOptions.RawTerminal is set to false, go-dockerclient will multiplex // the streams and send the containers stdout to LogsServiceOptions.OutputStream, and // stderr to LogsServiceOptions.ErrorStream. // // When LogsServiceOptions.RawTerminal is true, callers will get the raw stream on // LogsServiceOptions.OutputStream. func (c *Client) GetServiceLogs(opts LogsServiceOptions) error { if opts.Service == "" { return &NoSuchService{ID: opts.Service} } if opts.Tail == "" { opts.Tail = "all" } path := "/services/" + opts.Service + "/logs?" + queryString(opts) return c.stream(http.MethodGet, path, streamOptions{ setRawTerminal: opts.RawTerminal, stdout: opts.OutputStream, stderr: opts.ErrorStream, inactivityTimeout: opts.InactivityTimeout, context: opts.Context, }) } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/swarm_task.go ================================================ // Copyright 2016 go-dockerclient authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package docker import ( "context" "encoding/json" "errors" "net/http" "github.com/docker/docker/api/types/swarm" ) // NoSuchTask is the error returned when a given task does not exist. type NoSuchTask struct { ID string Err error } func (err *NoSuchTask) Error() string { if err.Err != nil { return err.Err.Error() } return "No such task: " + err.ID } // ListTasksOptions specify parameters to the ListTasks function. // // See http://goo.gl/rByLzw for more details. type ListTasksOptions struct { Filters map[string][]string Context context.Context } // ListTasks returns a slice of tasks matching the given criteria. // // See http://goo.gl/rByLzw for more details. func (c *Client) ListTasks(opts ListTasksOptions) ([]swarm.Task, error) { path := "/tasks?" + queryString(opts) resp, err := c.do(http.MethodGet, path, doOptions{context: opts.Context}) if err != nil { return nil, err } defer resp.Body.Close() var tasks []swarm.Task if err := json.NewDecoder(resp.Body).Decode(&tasks); err != nil { return nil, err } return tasks, nil } // InspectTask returns information about a task by its ID. // // See http://goo.gl/kyziuq for more details. func (c *Client) InspectTask(id string) (*swarm.Task, error) { resp, err := c.do(http.MethodGet, "/tasks/"+id, doOptions{}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return nil, &NoSuchTask{ID: id} } return nil, err } defer resp.Body.Close() var task swarm.Task if err := json.NewDecoder(resp.Body).Decode(&task); err != nil { return nil, err } return &task, nil } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/system.go ================================================ package docker import ( "context" "encoding/json" "net/http" ) // VolumeUsageData represents usage data from the docker system api // More Info Here https://dockr.ly/2PNzQyO type VolumeUsageData struct { // The number of containers referencing this volume. This field // is set to `-1` if the reference-count is not available. // // Required: true RefCount int64 `json:"RefCount"` // Amount of disk space used by the volume (in bytes). This information // is only available for volumes created with the `"local"` volume // driver. For volumes created with other volume drivers, this field // is set to `-1` ("not available") // // Required: true Size int64 `json:"Size"` } // ImageSummary represents data about what images are // currently known to docker // More Info Here https://dockr.ly/2PNzQyO type ImageSummary struct { Containers int64 `json:"Containers"` Created int64 `json:"Created"` ID string `json:"Id"` Labels map[string]string `json:"Labels"` ParentID string `json:"ParentId"` RepoDigests []string `json:"RepoDigests"` RepoTags []string `json:"RepoTags"` SharedSize int64 `json:"SharedSize"` Size int64 `json:"Size"` VirtualSize int64 `json:"VirtualSize"` } // DiskUsage holds information about what docker is using disk space on. // More Info Here https://dockr.ly/2PNzQyO type DiskUsage struct { LayersSize int64 Images []*ImageSummary Containers []*APIContainers Volumes []*Volume } // DiskUsageOptions only contains a context for canceling. type DiskUsageOptions struct { Context context.Context } // DiskUsage returns a *DiskUsage describing what docker is using disk on. // // More Info Here https://dockr.ly/2PNzQyO func (c *Client) DiskUsage(opts DiskUsageOptions) (*DiskUsage, error) { path := "/system/df" resp, err := c.do(http.MethodGet, path, doOptions{context: opts.Context}) if err != nil { return nil, err } defer resp.Body.Close() var du *DiskUsage if err := json.NewDecoder(resp.Body).Decode(&du); err != nil { return nil, err } return du, nil } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/tar.go ================================================ // Copyright 2014 go-dockerclient authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package docker import ( "fmt" "io" "os" "path" "path/filepath" "strings" "github.com/docker/docker/pkg/archive" "github.com/moby/patternmatcher" ) func createTarStream(srcPath, dockerfilePath string) (io.ReadCloser, error) { srcPath, err := filepath.Abs(srcPath) if err != nil { return nil, err } excludes, err := parseDockerignore(srcPath) if err != nil { return nil, err } includes := []string{"."} // If .dockerignore mentions .dockerignore or the Dockerfile // then make sure we send both files over to the daemon // because Dockerfile is, obviously, needed no matter what, and // .dockerignore is needed to know if either one needs to be // removed. The deamon will remove them for us, if needed, after it // parses the Dockerfile. // // https://github.com/docker/docker/issues/8330 // forceIncludeFiles := []string{".dockerignore", dockerfilePath} for _, includeFile := range forceIncludeFiles { if includeFile == "" { continue } keepThem, err := patternmatcher.Matches(includeFile, excludes) if err != nil { return nil, fmt.Errorf("cannot match .dockerfileignore: '%s', error: %w", includeFile, err) } if keepThem { includes = append(includes, includeFile) } } if err := validateContextDirectory(srcPath, excludes); err != nil { return nil, err } tarOpts := &archive.TarOptions{ ExcludePatterns: excludes, IncludeFiles: includes, Compression: archive.Uncompressed, NoLchown: true, } return archive.TarWithOptions(srcPath, tarOpts) } // validateContextDirectory checks if all the contents of the directory // can be read and returns an error if some files can't be read. // Symlinks which point to non-existing files don't trigger an error func validateContextDirectory(srcPath string, excludes []string) error { return filepath.Walk(filepath.Join(srcPath, "."), func(filePath string, f os.FileInfo, err error) error { // skip this directory/file if it's not in the path, it won't get added to the context if relFilePath, relErr := filepath.Rel(srcPath, filePath); relErr != nil { return relErr } else if skip, matchErr := patternmatcher.Matches(relFilePath, excludes); matchErr != nil { return matchErr } else if skip { if f.IsDir() { return filepath.SkipDir } return nil } if err != nil { if os.IsPermission(err) { return fmt.Errorf("cannot stat %q: %w", filePath, err) } if os.IsNotExist(err) { return nil } return err } // skip checking if symlinks point to non-existing files, such symlinks can be useful // also skip named pipes, because they hanging on open if f.Mode()&(os.ModeSymlink|os.ModeNamedPipe) != 0 { return nil } if !f.IsDir() { currentFile, err := os.Open(filePath) if err != nil { return fmt.Errorf("cannot open %q for reading: %w", filePath, err) } currentFile.Close() } return nil }) } func parseDockerignore(root string) ([]string, error) { var excludes []string ignore, err := os.ReadFile(path.Join(root, ".dockerignore")) if err != nil && !os.IsNotExist(err) { return excludes, fmt.Errorf("error reading .dockerignore: %w", err) } excludes = strings.Split(string(ignore), "\n") return excludes, nil } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/tls.go ================================================ // Copyright 2014 go-dockerclient authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // // The content is borrowed from Docker's own source code to provide a simple // tls based dialer package docker import ( "crypto/tls" "errors" "net" "strings" "time" ) type tlsClientCon struct { *tls.Conn rawConn net.Conn } func (c *tlsClientCon) CloseWrite() error { // Go standard tls.Conn doesn't provide the CloseWrite() method so we do it // on its underlying connection. if cwc, ok := c.rawConn.(interface { CloseWrite() error }); ok { return cwc.CloseWrite() } return nil } func tlsDialWithDialer(dialer *net.Dialer, network, addr string, config *tls.Config) (net.Conn, error) { // We want the Timeout and Deadline values from dialer to cover the // whole process: TCP connection and TLS handshake. This means that we // also need to start our own timers now. timeout := dialer.Timeout if !dialer.Deadline.IsZero() { deadlineTimeout := time.Until(dialer.Deadline) if timeout == 0 || deadlineTimeout < timeout { timeout = deadlineTimeout } } var errChannel chan error if timeout != 0 { errChannel = make(chan error, 2) time.AfterFunc(timeout, func() { errChannel <- errors.New("") }) } rawConn, err := dialer.Dial(network, addr) if err != nil { return nil, err } colonPos := strings.LastIndex(addr, ":") if colonPos == -1 { colonPos = len(addr) } hostname := addr[:colonPos] // If no ServerName is set, infer the ServerName // from the hostname we're connecting to. if config.ServerName == "" { // Make a copy to avoid polluting argument or default. config = copyTLSConfig(config) config.ServerName = hostname } conn := tls.Client(rawConn, config) if timeout == 0 { err = conn.Handshake() } else { go func() { errChannel <- conn.Handshake() }() err = <-errChannel } if err != nil { rawConn.Close() return nil, err } // This is Docker difference with standard's crypto/tls package: returned a // wrapper which holds both the TLS and raw connections. return &tlsClientCon{conn, rawConn}, nil } // this exists to silent an error message in go vet func copyTLSConfig(cfg *tls.Config) *tls.Config { return &tls.Config{ Certificates: cfg.Certificates, CipherSuites: cfg.CipherSuites, ClientAuth: cfg.ClientAuth, ClientCAs: cfg.ClientCAs, ClientSessionCache: cfg.ClientSessionCache, CurvePreferences: cfg.CurvePreferences, InsecureSkipVerify: cfg.InsecureSkipVerify, MaxVersion: cfg.MaxVersion, MinVersion: cfg.MinVersion, NextProtos: cfg.NextProtos, Rand: cfg.Rand, RootCAs: cfg.RootCAs, ServerName: cfg.ServerName, SessionTicketsDisabled: cfg.SessionTicketsDisabled, } } ================================================ FILE: vendor/github.com/fsouza/go-dockerclient/volume.go ================================================ // Copyright 2015 go-dockerclient authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package docker import ( "context" "encoding/json" "errors" "net/http" "time" ) var ( // ErrNoSuchVolume is the error returned when the volume does not exist. ErrNoSuchVolume = errors.New("no such volume") // ErrVolumeInUse is the error returned when the volume requested to be removed is still in use. ErrVolumeInUse = errors.New("volume in use and cannot be removed") ) // Volume represents a volume. // // See https://goo.gl/3wgTsd for more details. type Volume struct { Name string `json:"Name" yaml:"Name" toml:"Name"` Driver string `json:"Driver,omitempty" yaml:"Driver,omitempty" toml:"Driver,omitempty"` Mountpoint string `json:"Mountpoint,omitempty" yaml:"Mountpoint,omitempty" toml:"Mountpoint,omitempty"` Labels map[string]string `json:"Labels,omitempty" yaml:"Labels,omitempty" toml:"Labels,omitempty"` Options map[string]string `json:"Options,omitempty" yaml:"Options,omitempty" toml:"Options,omitempty"` CreatedAt time.Time `json:"CreatedAt,omitempty" yaml:"CreatedAt,omitempty" toml:"CreatedAt,omitempty"` } // ListVolumesOptions specify parameters to the ListVolumes function. // // See https://goo.gl/3wgTsd for more details. type ListVolumesOptions struct { Filters map[string][]string Context context.Context } // ListVolumes returns a list of available volumes in the server. // // See https://goo.gl/3wgTsd for more details. func (c *Client) ListVolumes(opts ListVolumesOptions) ([]Volume, error) { resp, err := c.do(http.MethodGet, "/volumes?"+queryString(opts), doOptions{ context: opts.Context, }) if err != nil { return nil, err } defer resp.Body.Close() m := make(map[string]any) if err = json.NewDecoder(resp.Body).Decode(&m); err != nil { return nil, err } var volumes []Volume volumesJSON, ok := m["Volumes"] if !ok { return volumes, nil } data, err := json.Marshal(volumesJSON) if err != nil { return nil, err } if err := json.Unmarshal(data, &volumes); err != nil { return nil, err } return volumes, nil } // CreateVolumeOptions specify parameters to the CreateVolume function. // // See https://goo.gl/qEhmEC for more details. type CreateVolumeOptions struct { Name string Driver string DriverOpts map[string]string Context context.Context `json:"-"` Labels map[string]string } // CreateVolume creates a volume on the server. // // See https://goo.gl/qEhmEC for more details. func (c *Client) CreateVolume(opts CreateVolumeOptions) (*Volume, error) { resp, err := c.do(http.MethodPost, "/volumes/create", doOptions{ data: opts, context: opts.Context, }) if err != nil { return nil, err } defer resp.Body.Close() var volume Volume if err := json.NewDecoder(resp.Body).Decode(&volume); err != nil { return nil, err } return &volume, nil } // InspectVolume returns a volume by its name. // // See https://goo.gl/GMjsMc for more details. func (c *Client) InspectVolume(name string) (*Volume, error) { resp, err := c.do(http.MethodGet, "/volumes/"+name, doOptions{}) if err != nil { var e *Error if errors.As(err, &e) && e.Status == http.StatusNotFound { return nil, ErrNoSuchVolume } return nil, err } defer resp.Body.Close() var volume Volume if err := json.NewDecoder(resp.Body).Decode(&volume); err != nil { return nil, err } return &volume, nil } // RemoveVolume removes a volume by its name. // // Deprecated: Use RemoveVolumeWithOptions instead. func (c *Client) RemoveVolume(name string) error { return c.RemoveVolumeWithOptions(RemoveVolumeOptions{Name: name}) } // RemoveVolumeOptions specify parameters to the RemoveVolumeWithOptions // function. // // See https://goo.gl/nvd6qj for more details. type RemoveVolumeOptions struct { Context context.Context Name string `qs:"-"` Force bool } // RemoveVolumeWithOptions removes a volume by its name and takes extra // parameters. // // See https://goo.gl/nvd6qj for more details. func (c *Client) RemoveVolumeWithOptions(opts RemoveVolumeOptions) error { path := "/volumes/" + opts.Name resp, err := c.do(http.MethodDelete, path+"?"+queryString(opts), doOptions{context: opts.Context}) if err != nil { var e *Error if errors.As(err, &e) { if e.Status == http.StatusNotFound { return ErrNoSuchVolume } if e.Status == http.StatusConflict { return ErrVolumeInUse } } return err } defer resp.Body.Close() return nil } // PruneVolumesOptions specify parameters to the PruneVolumes function. // // See https://goo.gl/f9XDem for more details. type PruneVolumesOptions struct { Filters map[string][]string Context context.Context } // PruneVolumesResults specify results from the PruneVolumes function. // // See https://goo.gl/f9XDem for more details. type PruneVolumesResults struct { VolumesDeleted []string SpaceReclaimed int64 } // PruneVolumes deletes volumes which are unused. // // See https://goo.gl/f9XDem for more details. func (c *Client) PruneVolumes(opts PruneVolumesOptions) (*PruneVolumesResults, error) { path := "/volumes/prune?" + queryString(opts) resp, err := c.do(http.MethodPost, path, doOptions{context: opts.Context}) if err != nil { return nil, err } defer resp.Body.Close() var results PruneVolumesResults if err := json.NewDecoder(resp.Body).Decode(&results); err != nil { return nil, err } return &results, nil } ================================================ FILE: vendor/github.com/getkin/kin-openapi/LICENSE ================================================ MIT License Copyright (c) 2017-2018 the project authors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi2/doc.go ================================================ // Package openapi2 parses and writes OpenAPIv2 specification documents. // // Does not cover all elements of OpenAPIv2. // When OpenAPI version 3 is backwards-compatible with version 2, version 3 elements have been used. // // See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md package openapi2 ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi2/header.go ================================================ package openapi2 type Header struct { Parameter } // MarshalJSON returns the JSON encoding of Header. func (header Header) MarshalJSON() ([]byte, error) { return header.Parameter.MarshalJSON() } // UnmarshalJSON sets Header to a copy of data. func (header *Header) UnmarshalJSON(data []byte) error { return header.Parameter.UnmarshalJSON(data) } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi2/helpers.go ================================================ package openapi2 import ( "net/url" ) // copyURI makes a copy of the pointer. func copyURI(u *url.URL) *url.URL { if u == nil { return nil } c := *u // shallow-copy return &c } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi2/marsh.go ================================================ package openapi2 import ( "encoding/json" "fmt" "strings" "github.com/oasdiff/yaml" ) func unmarshalError(jsonUnmarshalErr error) error { if before, after, found := strings.Cut(jsonUnmarshalErr.Error(), "Bis"); found && before != "" && after != "" { before = strings.ReplaceAll(before, " Go struct ", " ") return fmt.Errorf("%s%s", before, strings.ReplaceAll(after, "Bis", "")) } return jsonUnmarshalErr } func unmarshal(data []byte, v any) error { var jsonErr, yamlErr error // See https://github.com/getkin/kin-openapi/issues/680 if jsonErr = json.Unmarshal(data, v); jsonErr == nil { return nil } // UnmarshalStrict(data, v) TODO: investigate how ymlv3 handles duplicate map keys if yamlErr = yaml.Unmarshal(data, v); yamlErr == nil { return nil } // If both unmarshaling attempts fail, return a new error that includes both errors return fmt.Errorf("failed to unmarshal data: json error: %v, yaml error: %v", jsonErr, yamlErr) } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi2/openapi2.go ================================================ package openapi2 import ( "encoding/json" "github.com/getkin/kin-openapi/openapi3" ) // T is the root of an OpenAPI v2 document type T struct { Extensions map[string]any `json:"-" yaml:"-"` Swagger string `json:"swagger" yaml:"swagger"` // required Info openapi3.Info `json:"info" yaml:"info"` // required ExternalDocs *openapi3.ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` Schemes []string `json:"schemes,omitempty" yaml:"schemes,omitempty"` Consumes []string `json:"consumes,omitempty" yaml:"consumes,omitempty"` Produces []string `json:"produces,omitempty" yaml:"produces,omitempty"` Host string `json:"host,omitempty" yaml:"host,omitempty"` BasePath string `json:"basePath,omitempty" yaml:"basePath,omitempty"` Paths map[string]*PathItem `json:"paths,omitempty" yaml:"paths,omitempty"` Definitions map[string]*SchemaRef `json:"definitions,omitempty" yaml:"definitions,omitempty"` Parameters map[string]*Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"` Responses map[string]*Response `json:"responses,omitempty" yaml:"responses,omitempty"` SecurityDefinitions map[string]*SecurityScheme `json:"securityDefinitions,omitempty" yaml:"securityDefinitions,omitempty"` Security SecurityRequirements `json:"security,omitempty" yaml:"security,omitempty"` Tags openapi3.Tags `json:"tags,omitempty" yaml:"tags,omitempty"` } // MarshalJSON returns the JSON encoding of T. func (doc T) MarshalJSON() ([]byte, error) { m := make(map[string]any, 15+len(doc.Extensions)) for k, v := range doc.Extensions { m[k] = v } m["swagger"] = doc.Swagger m["info"] = doc.Info if x := doc.ExternalDocs; x != nil { m["externalDocs"] = x } if x := doc.Schemes; len(x) != 0 { m["schemes"] = x } if x := doc.Consumes; len(x) != 0 { m["consumes"] = x } if x := doc.Produces; len(x) != 0 { m["produces"] = x } if x := doc.Host; x != "" { m["host"] = x } if x := doc.BasePath; x != "" { m["basePath"] = x } if x := doc.Paths; len(x) != 0 { m["paths"] = x } if x := doc.Definitions; len(x) != 0 { m["definitions"] = x } if x := doc.Parameters; len(x) != 0 { m["parameters"] = x } if x := doc.Responses; len(x) != 0 { m["responses"] = x } if x := doc.SecurityDefinitions; len(x) != 0 { m["securityDefinitions"] = x } if x := doc.Security; len(x) != 0 { m["security"] = x } if x := doc.Tags; len(x) != 0 { m["tags"] = x } return json.Marshal(m) } // UnmarshalJSON sets T to a copy of data. func (doc *T) UnmarshalJSON(data []byte) error { type TBis T var x TBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, "swagger") delete(x.Extensions, "info") delete(x.Extensions, "externalDocs") delete(x.Extensions, "schemes") delete(x.Extensions, "consumes") delete(x.Extensions, "produces") delete(x.Extensions, "host") delete(x.Extensions, "basePath") delete(x.Extensions, "paths") delete(x.Extensions, "definitions") delete(x.Extensions, "parameters") delete(x.Extensions, "responses") delete(x.Extensions, "securityDefinitions") delete(x.Extensions, "security") delete(x.Extensions, "tags") if len(x.Extensions) == 0 { x.Extensions = nil } *doc = T(x) return nil } func (doc *T) AddOperation(path string, method string, operation *Operation) { if doc.Paths == nil { doc.Paths = make(map[string]*PathItem) } pathItem := doc.Paths[path] if pathItem == nil { pathItem = &PathItem{} doc.Paths[path] = pathItem } pathItem.SetOperation(method, operation) } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi2/operation.go ================================================ package openapi2 import ( "encoding/json" "github.com/getkin/kin-openapi/openapi3" ) type Operation struct { Extensions map[string]any `json:"-" yaml:"-"` Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` ExternalDocs *openapi3.ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"` OperationID string `json:"operationId,omitempty" yaml:"operationId,omitempty"` Parameters Parameters `json:"parameters,omitempty" yaml:"parameters,omitempty"` Responses map[string]*Response `json:"responses" yaml:"responses"` Consumes []string `json:"consumes,omitempty" yaml:"consumes,omitempty"` Produces []string `json:"produces,omitempty" yaml:"produces,omitempty"` Schemes []string `json:"schemes,omitempty" yaml:"schemes,omitempty"` Security *SecurityRequirements `json:"security,omitempty" yaml:"security,omitempty"` } // MarshalJSON returns the JSON encoding of Operation. func (operation Operation) MarshalJSON() ([]byte, error) { m := make(map[string]any, 12+len(operation.Extensions)) for k, v := range operation.Extensions { m[k] = v } if x := operation.Summary; x != "" { m["summary"] = x } if x := operation.Description; x != "" { m["description"] = x } if x := operation.Deprecated; x { m["deprecated"] = x } if x := operation.ExternalDocs; x != nil { m["externalDocs"] = x } if x := operation.Tags; len(x) != 0 { m["tags"] = x } if x := operation.OperationID; x != "" { m["operationId"] = x } if x := operation.Parameters; len(x) != 0 { m["parameters"] = x } m["responses"] = operation.Responses if x := operation.Consumes; len(x) != 0 { m["consumes"] = x } if x := operation.Produces; len(x) != 0 { m["produces"] = x } if x := operation.Schemes; len(x) != 0 { m["schemes"] = x } if x := operation.Security; x != nil { m["security"] = x } return json.Marshal(m) } // UnmarshalJSON sets Operation to a copy of data. func (operation *Operation) UnmarshalJSON(data []byte) error { type OperationBis Operation var x OperationBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, "summary") delete(x.Extensions, "description") delete(x.Extensions, "deprecated") delete(x.Extensions, "externalDocs") delete(x.Extensions, "tags") delete(x.Extensions, "operationId") delete(x.Extensions, "parameters") delete(x.Extensions, "responses") delete(x.Extensions, "consumes") delete(x.Extensions, "produces") delete(x.Extensions, "schemes") delete(x.Extensions, "security") if len(x.Extensions) == 0 { x.Extensions = nil } *operation = Operation(x) return nil } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi2/parameter.go ================================================ package openapi2 import ( "encoding/json" "sort" "github.com/getkin/kin-openapi/openapi3" ) type Parameters []*Parameter var _ sort.Interface = Parameters{} func (ps Parameters) Len() int { return len(ps) } func (ps Parameters) Swap(i, j int) { ps[i], ps[j] = ps[j], ps[i] } func (ps Parameters) Less(i, j int) bool { if ps[i].Name != ps[j].Name { return ps[i].Name < ps[j].Name } if ps[i].In != ps[j].In { return ps[i].In < ps[j].In } return ps[i].Ref < ps[j].Ref } type Parameter struct { Extensions map[string]any `json:"-" yaml:"-"` Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"` In string `json:"in,omitempty" yaml:"in,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` CollectionFormat string `json:"collectionFormat,omitempty" yaml:"collectionFormat,omitempty"` Type *openapi3.Types `json:"type,omitempty" yaml:"type,omitempty"` Format string `json:"format,omitempty" yaml:"format,omitempty"` Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"` AllowEmptyValue bool `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,omitempty"` Required bool `json:"required,omitempty" yaml:"required,omitempty"` UniqueItems bool `json:"uniqueItems,omitempty" yaml:"uniqueItems,omitempty"` ExclusiveMin bool `json:"exclusiveMinimum,omitempty" yaml:"exclusiveMinimum,omitempty"` ExclusiveMax bool `json:"exclusiveMaximum,omitempty" yaml:"exclusiveMaximum,omitempty"` Schema *SchemaRef `json:"schema,omitempty" yaml:"schema,omitempty"` Items *SchemaRef `json:"items,omitempty" yaml:"items,omitempty"` Enum []any `json:"enum,omitempty" yaml:"enum,omitempty"` MultipleOf *float64 `json:"multipleOf,omitempty" yaml:"multipleOf,omitempty"` Minimum *float64 `json:"minimum,omitempty" yaml:"minimum,omitempty"` Maximum *float64 `json:"maximum,omitempty" yaml:"maximum,omitempty"` MaxLength *uint64 `json:"maxLength,omitempty" yaml:"maxLength,omitempty"` MaxItems *uint64 `json:"maxItems,omitempty" yaml:"maxItems,omitempty"` MinLength uint64 `json:"minLength,omitempty" yaml:"minLength,omitempty"` MinItems uint64 `json:"minItems,omitempty" yaml:"minItems,omitempty"` Default any `json:"default,omitempty" yaml:"default,omitempty"` } // MarshalJSON returns the JSON encoding of Parameter. func (parameter Parameter) MarshalJSON() ([]byte, error) { if ref := parameter.Ref; ref != "" { return json.Marshal(openapi3.Ref{Ref: ref}) } m := make(map[string]any, 24+len(parameter.Extensions)) for k, v := range parameter.Extensions { m[k] = v } if x := parameter.In; x != "" { m["in"] = x } if x := parameter.Name; x != "" { m["name"] = x } if x := parameter.Description; x != "" { m["description"] = x } if x := parameter.CollectionFormat; x != "" { m["collectionFormat"] = x } if x := parameter.Type; x != nil { m["type"] = x } if x := parameter.Format; x != "" { m["format"] = x } if x := parameter.Pattern; x != "" { m["pattern"] = x } if x := parameter.AllowEmptyValue; x { m["allowEmptyValue"] = x } if x := parameter.Required; x { m["required"] = x } if x := parameter.UniqueItems; x { m["uniqueItems"] = x } if x := parameter.ExclusiveMin; x { m["exclusiveMinimum"] = x } if x := parameter.ExclusiveMax; x { m["exclusiveMaximum"] = x } if x := parameter.Schema; x != nil { m["schema"] = x } if x := parameter.Items; x != nil { m["items"] = x } if x := parameter.Enum; x != nil { m["enum"] = x } if x := parameter.MultipleOf; x != nil { m["multipleOf"] = x } if x := parameter.Minimum; x != nil { m["minimum"] = x } if x := parameter.Maximum; x != nil { m["maximum"] = x } if x := parameter.MaxLength; x != nil { m["maxLength"] = x } if x := parameter.MaxItems; x != nil { m["maxItems"] = x } if x := parameter.MinLength; x != 0 { m["minLength"] = x } if x := parameter.MinItems; x != 0 { m["minItems"] = x } if x := parameter.Default; x != nil { m["default"] = x } return json.Marshal(m) } // UnmarshalJSON sets Parameter to a copy of data. func (parameter *Parameter) UnmarshalJSON(data []byte) error { type ParameterBis Parameter var x ParameterBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, "$ref") delete(x.Extensions, "in") delete(x.Extensions, "name") delete(x.Extensions, "description") delete(x.Extensions, "collectionFormat") delete(x.Extensions, "type") delete(x.Extensions, "format") delete(x.Extensions, "pattern") delete(x.Extensions, "allowEmptyValue") delete(x.Extensions, "required") delete(x.Extensions, "uniqueItems") delete(x.Extensions, "exclusiveMinimum") delete(x.Extensions, "exclusiveMaximum") delete(x.Extensions, "schema") delete(x.Extensions, "items") delete(x.Extensions, "enum") delete(x.Extensions, "multipleOf") delete(x.Extensions, "minimum") delete(x.Extensions, "maximum") delete(x.Extensions, "maxLength") delete(x.Extensions, "maxItems") delete(x.Extensions, "minLength") delete(x.Extensions, "minItems") delete(x.Extensions, "default") if len(x.Extensions) == 0 { x.Extensions = nil } *parameter = Parameter(x) return nil } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi2/path_item.go ================================================ package openapi2 import ( "encoding/json" "fmt" "net/http" "github.com/getkin/kin-openapi/openapi3" ) type PathItem struct { Extensions map[string]any `json:"-" yaml:"-"` Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"` Delete *Operation `json:"delete,omitempty" yaml:"delete,omitempty"` Get *Operation `json:"get,omitempty" yaml:"get,omitempty"` Head *Operation `json:"head,omitempty" yaml:"head,omitempty"` Options *Operation `json:"options,omitempty" yaml:"options,omitempty"` Patch *Operation `json:"patch,omitempty" yaml:"patch,omitempty"` Post *Operation `json:"post,omitempty" yaml:"post,omitempty"` Put *Operation `json:"put,omitempty" yaml:"put,omitempty"` Parameters Parameters `json:"parameters,omitempty" yaml:"parameters,omitempty"` } // MarshalJSON returns the JSON encoding of PathItem. func (pathItem PathItem) MarshalJSON() ([]byte, error) { if ref := pathItem.Ref; ref != "" { return json.Marshal(openapi3.Ref{Ref: ref}) } m := make(map[string]any, 8+len(pathItem.Extensions)) for k, v := range pathItem.Extensions { m[k] = v } if x := pathItem.Delete; x != nil { m["delete"] = x } if x := pathItem.Get; x != nil { m["get"] = x } if x := pathItem.Head; x != nil { m["head"] = x } if x := pathItem.Options; x != nil { m["options"] = x } if x := pathItem.Patch; x != nil { m["patch"] = x } if x := pathItem.Post; x != nil { m["post"] = x } if x := pathItem.Put; x != nil { m["put"] = x } if x := pathItem.Parameters; len(x) != 0 { m["parameters"] = x } return json.Marshal(m) } // UnmarshalJSON sets PathItem to a copy of data. func (pathItem *PathItem) UnmarshalJSON(data []byte) error { type PathItemBis PathItem var x PathItemBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, "$ref") delete(x.Extensions, "delete") delete(x.Extensions, "get") delete(x.Extensions, "head") delete(x.Extensions, "options") delete(x.Extensions, "patch") delete(x.Extensions, "post") delete(x.Extensions, "put") delete(x.Extensions, "parameters") if len(x.Extensions) == 0 { x.Extensions = nil } *pathItem = PathItem(x) return nil } func (pathItem *PathItem) Operations() map[string]*Operation { operations := make(map[string]*Operation) if v := pathItem.Delete; v != nil { operations[http.MethodDelete] = v } if v := pathItem.Get; v != nil { operations[http.MethodGet] = v } if v := pathItem.Head; v != nil { operations[http.MethodHead] = v } if v := pathItem.Options; v != nil { operations[http.MethodOptions] = v } if v := pathItem.Patch; v != nil { operations[http.MethodPatch] = v } if v := pathItem.Post; v != nil { operations[http.MethodPost] = v } if v := pathItem.Put; v != nil { operations[http.MethodPut] = v } return operations } func (pathItem *PathItem) GetOperation(method string) *Operation { switch method { case http.MethodDelete: return pathItem.Delete case http.MethodGet: return pathItem.Get case http.MethodHead: return pathItem.Head case http.MethodOptions: return pathItem.Options case http.MethodPatch: return pathItem.Patch case http.MethodPost: return pathItem.Post case http.MethodPut: return pathItem.Put default: panic(fmt.Errorf("unsupported HTTP method %q", method)) } } func (pathItem *PathItem) SetOperation(method string, operation *Operation) { switch method { case http.MethodDelete: pathItem.Delete = operation case http.MethodGet: pathItem.Get = operation case http.MethodHead: pathItem.Head = operation case http.MethodOptions: pathItem.Options = operation case http.MethodPatch: pathItem.Patch = operation case http.MethodPost: pathItem.Post = operation case http.MethodPut: pathItem.Put = operation default: panic(fmt.Errorf("unsupported HTTP method %q", method)) } } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi2/ref.go ================================================ package openapi2 //go:generate go run refsgenerator.go // Ref is specified by OpenAPI/Swagger 2.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md#reference-object type Ref struct { Ref string `json:"$ref" yaml:"$ref"` } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi2/refs.go ================================================ package openapi2 import ( "encoding/json" "net/url" "sort" "strings" "github.com/go-openapi/jsonpointer" "github.com/perimeterx/marshmallow" ) // SchemaRef represents either a Schema or a $ref to a Schema. // When serializing and both fields are set, Ref is preferred over Value. type SchemaRef struct { // Extensions only captures fields starting with 'x-' as no other fields // are allowed by the openapi spec. Extensions map[string]any Ref string Value *Schema extra []string refPath *url.URL } var _ jsonpointer.JSONPointable = (*SchemaRef)(nil) func (x *SchemaRef) isEmpty() bool { return x == nil || x.Ref == "" && x.Value == nil } // RefString returns the $ref value. func (x *SchemaRef) RefString() string { return x.Ref } // CollectionName returns the JSON string used for a collection of these components. func (x *SchemaRef) CollectionName() string { return "schemas" } // RefPath returns the path of the $ref relative to the root document. func (x *SchemaRef) RefPath() *url.URL { return copyURI(x.refPath) } func (x *SchemaRef) setRefPath(u *url.URL) { // Once the refPath is set don't override. References can be loaded // multiple times not all with access to the correct path info. if x.refPath != nil { return } x.refPath = copyURI(u) } // MarshalYAML returns the YAML encoding of SchemaRef. func (x SchemaRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { return &Ref{Ref: ref}, nil } return x.Value.MarshalYAML() } // MarshalJSON returns the JSON encoding of SchemaRef. func (x SchemaRef) MarshalJSON() ([]byte, error) { y, err := x.MarshalYAML() if err != nil { return nil, err } return json.Marshal(y) } // UnmarshalJSON sets SchemaRef to a copy of data. func (x *SchemaRef) UnmarshalJSON(data []byte) error { var refOnly Ref if extra, err := marshmallow.Unmarshal(data, &refOnly, marshmallow.WithExcludeKnownFieldsFromMap(true)); err == nil && refOnly.Ref != "" { x.Ref = refOnly.Ref if len(extra) != 0 { x.extra = make([]string, 0, len(extra)) for key := range extra { x.extra = append(x.extra, key) } sort.Strings(x.extra) for k := range extra { if !strings.HasPrefix(k, "x-") { delete(extra, k) } } if len(extra) != 0 { x.Extensions = extra } } return nil } return json.Unmarshal(data, &x.Value) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (x *SchemaRef) JSONLookup(token string) (any, error) { if token == "$ref" { return x.Ref, nil } if v, ok := x.Extensions[token]; ok { return v, nil } ptr, _, err := jsonpointer.GetForToken(x.Value, token) return ptr, err } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi2/response.go ================================================ package openapi2 import ( "encoding/json" "github.com/getkin/kin-openapi/openapi3" ) type Response struct { Extensions map[string]any `json:"-" yaml:"-"` Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` Schema *SchemaRef `json:"schema,omitempty" yaml:"schema,omitempty"` Headers map[string]*Header `json:"headers,omitempty" yaml:"headers,omitempty"` Examples map[string]any `json:"examples,omitempty" yaml:"examples,omitempty"` } // MarshalJSON returns the JSON encoding of Response. func (response Response) MarshalJSON() ([]byte, error) { if ref := response.Ref; ref != "" { return json.Marshal(openapi3.Ref{Ref: ref}) } m := make(map[string]any, 4+len(response.Extensions)) for k, v := range response.Extensions { m[k] = v } if x := response.Description; x != "" { m["description"] = x } if x := response.Schema; x != nil { m["schema"] = x } if x := response.Headers; len(x) != 0 { m["headers"] = x } if x := response.Examples; len(x) != 0 { m["examples"] = x } return json.Marshal(m) } // UnmarshalJSON sets Response to a copy of data. func (response *Response) UnmarshalJSON(data []byte) error { type ResponseBis Response var x ResponseBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, "$ref") delete(x.Extensions, "description") delete(x.Extensions, "schema") delete(x.Extensions, "headers") delete(x.Extensions, "examples") if len(x.Extensions) == 0 { x.Extensions = nil } *response = Response(x) return nil } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi2/schema.go ================================================ package openapi2 import ( "encoding/json" "strings" "github.com/getkin/kin-openapi/openapi3" ) type ( Schemas map[string]*SchemaRef SchemaRefs []*SchemaRef ) // Schema is specified by OpenAPI/Swagger 2.0 standard. // See https://swagger.io/specification/v2/#schema-object type Schema struct { Extensions map[string]any `json:"-" yaml:"-"` AllOf SchemaRefs `json:"allOf,omitempty" yaml:"allOf,omitempty"` Not *SchemaRef `json:"not,omitempty" yaml:"not,omitempty"` Type *openapi3.Types `json:"type,omitempty" yaml:"type,omitempty"` Title string `json:"title,omitempty" yaml:"title,omitempty"` Format string `json:"format,omitempty" yaml:"format,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` Enum []any `json:"enum,omitempty" yaml:"enum,omitempty"` Default any `json:"default,omitempty" yaml:"default,omitempty"` Example any `json:"example,omitempty" yaml:"example,omitempty"` ExternalDocs *openapi3.ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` // Array-related, here for struct compactness UniqueItems bool `json:"uniqueItems,omitempty" yaml:"uniqueItems,omitempty"` // Number-related, here for struct compactness ExclusiveMin bool `json:"exclusiveMinimum,omitempty" yaml:"exclusiveMinimum,omitempty"` ExclusiveMax bool `json:"exclusiveMaximum,omitempty" yaml:"exclusiveMaximum,omitempty"` // Properties ReadOnly bool `json:"readOnly,omitempty" yaml:"readOnly,omitempty"` WriteOnly bool `json:"writeOnly,omitempty" yaml:"writeOnly,omitempty"` AllowEmptyValue bool `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,omitempty"` Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` XML *openapi3.XML `json:"xml,omitempty" yaml:"xml,omitempty"` // Number Min *float64 `json:"minimum,omitempty" yaml:"minimum,omitempty"` Max *float64 `json:"maximum,omitempty" yaml:"maximum,omitempty"` MultipleOf *float64 `json:"multipleOf,omitempty" yaml:"multipleOf,omitempty"` // String MinLength uint64 `json:"minLength,omitempty" yaml:"minLength,omitempty"` MaxLength *uint64 `json:"maxLength,omitempty" yaml:"maxLength,omitempty"` Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"` // Array MinItems uint64 `json:"minItems,omitempty" yaml:"minItems,omitempty"` MaxItems *uint64 `json:"maxItems,omitempty" yaml:"maxItems,omitempty"` Items *SchemaRef `json:"items,omitempty" yaml:"items,omitempty"` // Object Required []string `json:"required,omitempty" yaml:"required,omitempty"` Properties Schemas `json:"properties,omitempty" yaml:"properties,omitempty"` MinProps uint64 `json:"minProperties,omitempty" yaml:"minProperties,omitempty"` MaxProps *uint64 `json:"maxProperties,omitempty" yaml:"maxProperties,omitempty"` AdditionalProperties openapi3.AdditionalProperties `json:"additionalProperties,omitempty" yaml:"additionalProperties,omitempty"` Discriminator string `json:"discriminator,omitempty" yaml:"discriminator,omitempty"` } // MarshalJSON returns the JSON encoding of Schema. func (schema Schema) MarshalJSON() ([]byte, error) { m, err := schema.MarshalYAML() if err != nil { return nil, err } return json.Marshal(m) } // MarshalYAML returns the YAML encoding of Schema. func (schema Schema) MarshalYAML() (any, error) { m := make(map[string]any, 36+len(schema.Extensions)) for k, v := range schema.Extensions { m[k] = v } if x := schema.AllOf; len(x) != 0 { m["allOf"] = x } if x := schema.Not; x != nil { m["not"] = x } if x := schema.Type; x != nil { m["type"] = x } if x := schema.Title; len(x) != 0 { m["title"] = x } if x := schema.Format; len(x) != 0 { m["format"] = x } if x := schema.Description; len(x) != 0 { m["description"] = x } if x := schema.Enum; len(x) != 0 { m["enum"] = x } if x := schema.Default; x != nil { m["default"] = x } if x := schema.Example; x != nil { m["example"] = x } if x := schema.ExternalDocs; x != nil { m["externalDocs"] = x } // Array-related if x := schema.UniqueItems; x { m["uniqueItems"] = x } // Number-related if x := schema.ExclusiveMin; x { m["exclusiveMinimum"] = x } if x := schema.ExclusiveMax; x { m["exclusiveMaximum"] = x } if x := schema.ReadOnly; x { m["readOnly"] = x } if x := schema.WriteOnly; x { m["writeOnly"] = x } if x := schema.AllowEmptyValue; x { m["allowEmptyValue"] = x } if x := schema.Deprecated; x { m["deprecated"] = x } if x := schema.XML; x != nil { m["xml"] = x } // Number if x := schema.Min; x != nil { m["minimum"] = x } if x := schema.Max; x != nil { m["maximum"] = x } if x := schema.MultipleOf; x != nil { m["multipleOf"] = x } // String if x := schema.MinLength; x != 0 { m["minLength"] = x } if x := schema.MaxLength; x != nil { m["maxLength"] = x } if x := schema.Pattern; x != "" { m["pattern"] = x } // Array if x := schema.MinItems; x != 0 { m["minItems"] = x } if x := schema.MaxItems; x != nil { m["maxItems"] = x } if x := schema.Items; x != nil { m["items"] = x } // Object if x := schema.Required; len(x) != 0 { m["required"] = x } if x := schema.Properties; len(x) != 0 { m["properties"] = x } if x := schema.MinProps; x != 0 { m["minProperties"] = x } if x := schema.MaxProps; x != nil { m["maxProperties"] = x } if x := schema.AdditionalProperties; x.Has != nil || x.Schema != nil { m["additionalProperties"] = &x } if x := schema.Discriminator; x != "" { m["discriminator"] = x } return m, nil } // UnmarshalJSON sets Schema to a copy of data. func (schema *Schema) UnmarshalJSON(data []byte) error { type SchemaBis Schema var x SchemaBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, "oneOf") delete(x.Extensions, "anyOf") delete(x.Extensions, "allOf") delete(x.Extensions, "not") delete(x.Extensions, "type") delete(x.Extensions, "title") delete(x.Extensions, "format") delete(x.Extensions, "description") delete(x.Extensions, "enum") delete(x.Extensions, "default") delete(x.Extensions, "example") delete(x.Extensions, "externalDocs") // Array-related delete(x.Extensions, "uniqueItems") // Number-related delete(x.Extensions, "exclusiveMinimum") delete(x.Extensions, "exclusiveMaximum") // Properties delete(x.Extensions, "nullable") delete(x.Extensions, "readOnly") delete(x.Extensions, "writeOnly") delete(x.Extensions, "allowEmptyValue") delete(x.Extensions, "deprecated") delete(x.Extensions, "xml") // Number delete(x.Extensions, "minimum") delete(x.Extensions, "maximum") delete(x.Extensions, "multipleOf") // String delete(x.Extensions, "minLength") delete(x.Extensions, "maxLength") delete(x.Extensions, "pattern") // Array delete(x.Extensions, "minItems") delete(x.Extensions, "maxItems") delete(x.Extensions, "items") // Object delete(x.Extensions, "required") delete(x.Extensions, "properties") delete(x.Extensions, "minProperties") delete(x.Extensions, "maxProperties") delete(x.Extensions, "additionalProperties") delete(x.Extensions, "discriminator") if len(x.Extensions) == 0 { x.Extensions = nil } *schema = Schema(x) if schema.Format == "date" { // This is a fix for: https://github.com/getkin/kin-openapi/issues/697 if eg, ok := schema.Example.(string); ok { schema.Example = strings.TrimSuffix(eg, "T00:00:00Z") } } return nil } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi2/security_scheme.go ================================================ package openapi2 import ( "encoding/json" "github.com/getkin/kin-openapi/openapi3" ) type SecurityRequirements []map[string][]string type SecurityScheme struct { Extensions map[string]any `json:"-" yaml:"-"` Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` Type string `json:"type,omitempty" yaml:"type,omitempty"` In string `json:"in,omitempty" yaml:"in,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"` Flow string `json:"flow,omitempty" yaml:"flow,omitempty"` AuthorizationURL string `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"` TokenURL string `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"` Scopes map[string]string `json:"scopes,omitempty" yaml:"scopes,omitempty"` Tags openapi3.Tags `json:"tags,omitempty" yaml:"tags,omitempty"` } // MarshalJSON returns the JSON encoding of SecurityScheme. func (securityScheme SecurityScheme) MarshalJSON() ([]byte, error) { if ref := securityScheme.Ref; ref != "" { return json.Marshal(openapi3.Ref{Ref: ref}) } m := make(map[string]any, 10+len(securityScheme.Extensions)) for k, v := range securityScheme.Extensions { m[k] = v } if x := securityScheme.Description; x != "" { m["description"] = x } if x := securityScheme.Type; x != "" { m["type"] = x } if x := securityScheme.In; x != "" { m["in"] = x } if x := securityScheme.Name; x != "" { m["name"] = x } if x := securityScheme.Flow; x != "" { m["flow"] = x } if x := securityScheme.AuthorizationURL; x != "" { m["authorizationUrl"] = x } if x := securityScheme.TokenURL; x != "" { m["tokenUrl"] = x } if x := securityScheme.Scopes; len(x) != 0 { m["scopes"] = x } if x := securityScheme.Tags; len(x) != 0 { m["tags"] = x } return json.Marshal(m) } // UnmarshalJSON sets SecurityScheme to a copy of data. func (securityScheme *SecurityScheme) UnmarshalJSON(data []byte) error { type SecuritySchemeBis SecurityScheme var x SecuritySchemeBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, "$ref") delete(x.Extensions, "description") delete(x.Extensions, "type") delete(x.Extensions, "in") delete(x.Extensions, "name") delete(x.Extensions, "flow") delete(x.Extensions, "authorizationUrl") delete(x.Extensions, "tokenUrl") delete(x.Extensions, "scopes") delete(x.Extensions, "tags") if len(x.Extensions) == 0 { x.Extensions = nil } *securityScheme = SecurityScheme(x) return nil } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi2conv/doc.go ================================================ // Package openapi2conv converts an OpenAPI v2 specification document to v3. package openapi2conv ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi2conv/openapi2_conv.go ================================================ package openapi2conv import ( "errors" "fmt" "net/url" "sort" "strings" "github.com/getkin/kin-openapi/openapi2" "github.com/getkin/kin-openapi/openapi3" ) // ToV3 converts an OpenAPIv2 spec to an OpenAPIv3 spec func ToV3(doc2 *openapi2.T) (*openapi3.T, error) { return ToV3WithLoader(doc2, openapi3.NewLoader(), nil) } func ToV3WithLoader(doc2 *openapi2.T, loader *openapi3.Loader, location *url.URL) (*openapi3.T, error) { doc3 := &openapi3.T{ OpenAPI: "3.0.3", Info: &doc2.Info, Components: &openapi3.Components{}, Tags: doc2.Tags, Extensions: stripNonExtensions(doc2.Extensions), ExternalDocs: doc2.ExternalDocs, } if host := doc2.Host; host != "" { if strings.Contains(host, "/") { err := fmt.Errorf("invalid host %q. This MUST be the host only and does not include the scheme nor sub-paths.", host) return nil, err } schemes := doc2.Schemes if len(schemes) == 0 { schemes = []string{"https"} } basePath := doc2.BasePath if basePath == "" { basePath = "/" } for _, scheme := range schemes { u := url.URL{ Scheme: scheme, Host: host, Path: basePath, } doc3.AddServer(&openapi3.Server{URL: u.String()}) } } doc3.Components.Schemas = make(map[string]*openapi3.SchemaRef) if parameters := doc2.Parameters; len(parameters) != 0 { doc3.Components.Parameters = make(map[string]*openapi3.ParameterRef) doc3.Components.RequestBodies = make(map[string]*openapi3.RequestBodyRef) for k, parameter := range parameters { v3Parameter, v3RequestBody, v3SchemaMap, err := ToV3Parameter(doc3.Components, parameter, doc2.Consumes) switch { case err != nil: return nil, err case v3RequestBody != nil: doc3.Components.RequestBodies[k] = v3RequestBody case v3SchemaMap != nil: for _, v3Schema := range v3SchemaMap { doc3.Components.Schemas[k] = v3Schema } default: doc3.Components.Parameters[k] = v3Parameter } } } if paths := doc2.Paths; len(paths) != 0 { doc3.Paths = openapi3.NewPathsWithCapacity(len(paths)) for path, pathItem := range paths { r, err := ToV3PathItem(doc2, doc3.Components, pathItem, doc2.Consumes) if err != nil { return nil, err } doc3.Paths.Set(path, r) } } if responses := doc2.Responses; len(responses) != 0 { doc3.Components.Responses = make(openapi3.ResponseBodies, len(responses)) for k, response := range responses { r, err := ToV3Response(response, doc2.Produces) if err != nil { return nil, err } doc3.Components.Responses[k] = r } } for key, schema := range ToV3Schemas(doc2.Definitions) { doc3.Components.Schemas[key] = schema } if m := doc2.SecurityDefinitions; len(m) != 0 { doc3SecuritySchemes := make(map[string]*openapi3.SecuritySchemeRef) for k, v := range m { r, err := ToV3SecurityScheme(v) if err != nil { return nil, err } doc3SecuritySchemes[k] = r } doc3.Components.SecuritySchemes = doc3SecuritySchemes } doc3.Security = ToV3SecurityRequirements(doc2.Security) if err := loader.ResolveRefsIn(doc3, location); err != nil { return nil, err } return doc3, nil } func ToV3PathItem(doc2 *openapi2.T, components *openapi3.Components, pathItem *openapi2.PathItem, consumes []string) (*openapi3.PathItem, error) { doc3 := &openapi3.PathItem{ Extensions: stripNonExtensions(pathItem.Extensions), } for method, operation := range pathItem.Operations() { doc3Operation, err := ToV3Operation(doc2, components, pathItem, operation, consumes) if err != nil { return nil, err } doc3.SetOperation(method, doc3Operation) } for _, parameter := range pathItem.Parameters { v3Parameter, v3RequestBody, v3Schema, err := ToV3Parameter(components, parameter, consumes) switch { case err != nil: return nil, err case v3RequestBody != nil: return nil, errors.New("pathItem must not have a body parameter") case v3Schema != nil: return nil, errors.New("pathItem must not have a schema parameter") default: doc3.Parameters = append(doc3.Parameters, v3Parameter) } } return doc3, nil } func ToV3Operation(doc2 *openapi2.T, components *openapi3.Components, pathItem *openapi2.PathItem, operation *openapi2.Operation, consumes []string) (*openapi3.Operation, error) { if operation == nil { return nil, nil } doc3 := &openapi3.Operation{ OperationID: operation.OperationID, Summary: operation.Summary, Description: operation.Description, Deprecated: operation.Deprecated, Tags: operation.Tags, Extensions: stripNonExtensions(operation.Extensions), } if v := operation.Security; v != nil { doc3Security := ToV3SecurityRequirements(*v) doc3.Security = &doc3Security } if len(operation.Consumes) > 0 { consumes = operation.Consumes } var reqBodies []*openapi3.RequestBodyRef formDataSchemas := make(map[string]*openapi3.SchemaRef) for _, parameter := range operation.Parameters { v3Parameter, v3RequestBody, v3SchemaMap, err := ToV3Parameter(components, parameter, consumes) switch { case err != nil: return nil, err case v3RequestBody != nil: reqBodies = append(reqBodies, v3RequestBody) case v3SchemaMap != nil: for key, v3Schema := range v3SchemaMap { formDataSchemas[key] = v3Schema } default: doc3.Parameters = append(doc3.Parameters, v3Parameter) } } var err error if doc3.RequestBody, err = onlyOneReqBodyParam(reqBodies, formDataSchemas, components, consumes); err != nil { return nil, err } if responses := operation.Responses; responses != nil { doc3.Responses = openapi3.NewResponsesWithCapacity(len(responses)) for k, response := range responses { responseRef3, err := ToV3Response(response, operation.Produces) if err != nil { return nil, err } doc3.Responses.Set(k, responseRef3) } } return doc3, nil } func getParameterNameFromOldRef(ref string) string { cleanPath := strings.TrimPrefix(ref, "#/parameters/") pathSections := strings.SplitN(cleanPath, "/", 1) return pathSections[0] } func ToV3Parameter(components *openapi3.Components, parameter *openapi2.Parameter, consumes []string) (*openapi3.ParameterRef, *openapi3.RequestBodyRef, map[string]*openapi3.SchemaRef, error) { if ref := parameter.Ref; ref != "" { if strings.HasPrefix(ref, "#/parameters/") { name := getParameterNameFromOldRef(ref) if _, ok := components.RequestBodies[name]; ok { v3Ref := strings.Replace(ref, "#/parameters/", "#/components/requestBodies/", 1) return nil, &openapi3.RequestBodyRef{Ref: v3Ref}, nil, nil } else if schema, ok := components.Schemas[name]; ok { schemaRefMap := make(map[string]*openapi3.SchemaRef) if val, ok := schema.Value.Extensions["x-formData-name"]; ok { name = val.(string) } v3Ref := strings.Replace(ref, "#/parameters/", "#/components/schemas/", 1) schemaRefMap[name] = &openapi3.SchemaRef{Ref: v3Ref} return nil, nil, schemaRefMap, nil } } return &openapi3.ParameterRef{Ref: ToV3Ref(ref)}, nil, nil, nil } switch parameter.In { case "body": result := &openapi3.RequestBody{ Description: parameter.Description, Required: parameter.Required, Extensions: stripNonExtensions(parameter.Extensions), } if parameter.Name != "" { if result.Extensions == nil { result.Extensions = make(map[string]any, 1) } result.Extensions["x-originalParamName"] = parameter.Name } if schemaRef := parameter.Schema; schemaRef != nil { result.WithSchemaRef(ToV3SchemaRef(schemaRef), consumes) } return nil, &openapi3.RequestBodyRef{Value: result}, nil, nil case "formData": format, typ := parameter.Format, parameter.Type if typ.Is("file") { format, typ = "binary", &openapi3.Types{"string"} } if parameter.Extensions == nil { parameter.Extensions = make(map[string]any, 1) } parameter.Extensions["x-formData-name"] = parameter.Name var required []string if parameter.Required { required = []string{parameter.Name} } schemaRef := &openapi3.SchemaRef{Value: &openapi3.Schema{ Description: parameter.Description, Type: typ, Extensions: stripNonExtensions(parameter.Extensions), Format: format, Enum: parameter.Enum, Min: parameter.Minimum, Max: parameter.Maximum, ExclusiveMin: parameter.ExclusiveMin, ExclusiveMax: parameter.ExclusiveMax, MinLength: parameter.MinLength, MaxLength: parameter.MaxLength, Default: parameter.Default, MinItems: parameter.MinItems, MaxItems: parameter.MaxItems, Pattern: parameter.Pattern, AllowEmptyValue: parameter.AllowEmptyValue, UniqueItems: parameter.UniqueItems, MultipleOf: parameter.MultipleOf, Required: required, }} if parameter.Items != nil { schemaRef.Value.Items = ToV3SchemaRef(parameter.Items) } schemaRefMap := make(map[string]*openapi3.SchemaRef, 1) schemaRefMap[parameter.Name] = schemaRef return nil, nil, schemaRefMap, nil default: required := parameter.Required if parameter.In == openapi3.ParameterInPath { required = true } var schemaRefRef string if schemaRef := parameter.Schema; schemaRef != nil && schemaRef.Ref != "" { schemaRefRef = schemaRef.Ref } result := &openapi3.Parameter{ In: parameter.In, Name: parameter.Name, Description: parameter.Description, Required: required, Extensions: stripNonExtensions(parameter.Extensions), Schema: ToV3SchemaRef(&openapi2.SchemaRef{Value: &openapi2.Schema{ Type: parameter.Type, Format: parameter.Format, Enum: parameter.Enum, Min: parameter.Minimum, Max: parameter.Maximum, ExclusiveMin: parameter.ExclusiveMin, ExclusiveMax: parameter.ExclusiveMax, MinLength: parameter.MinLength, MaxLength: parameter.MaxLength, Default: parameter.Default, Items: parameter.Items, MinItems: parameter.MinItems, MaxItems: parameter.MaxItems, Pattern: parameter.Pattern, AllowEmptyValue: parameter.AllowEmptyValue, UniqueItems: parameter.UniqueItems, MultipleOf: parameter.MultipleOf, }, Ref: schemaRefRef, }), } return &openapi3.ParameterRef{Value: result}, nil, nil, nil } } func formDataBody(bodies map[string]*openapi3.SchemaRef, reqs map[string]bool, consumes []string) *openapi3.RequestBodyRef { if len(bodies) != len(reqs) { panic(`request bodies and them being required must match`) } requireds := make([]string, 0, len(reqs)) for propName, req := range reqs { if _, ok := bodies[propName]; !ok { panic(`request bodies and them being required must match`) } if req { requireds = append(requireds, propName) } } for s, ref := range bodies { if ref.Value != nil && len(ref.Value.Required) > 0 { ref.Value.Required = nil bodies[s] = ref } } sort.Strings(requireds) schema := &openapi3.Schema{ Type: &openapi3.Types{"object"}, Properties: bodies, Required: requireds, } return &openapi3.RequestBodyRef{ Value: openapi3.NewRequestBody().WithSchema(schema, consumes), } } func getParameterNameFromNewRef(ref string) string { cleanPath := strings.TrimPrefix(ref, "#/components/schemas/") pathSections := strings.SplitN(cleanPath, "/", 1) return pathSections[0] } func onlyOneReqBodyParam(bodies []*openapi3.RequestBodyRef, formDataSchemas map[string]*openapi3.SchemaRef, components *openapi3.Components, consumes []string) (*openapi3.RequestBodyRef, error) { if len(bodies) > 1 { return nil, errors.New("multiple body parameters cannot exist for the same operation") } if len(bodies) != 0 && len(formDataSchemas) != 0 { return nil, errors.New("body and form parameters cannot exist together for the same operation") } for _, requestBodyRef := range bodies { return requestBodyRef, nil } if len(formDataSchemas) > 0 { formDataParams := make(map[string]*openapi3.SchemaRef, len(formDataSchemas)) formDataReqs := make(map[string]bool, len(formDataSchemas)) for formDataName, formDataSchema := range formDataSchemas { if formDataSchema.Ref != "" { name := getParameterNameFromNewRef(formDataSchema.Ref) if schema := components.Schemas[name]; schema != nil && schema.Value != nil { if tempName, ok := schema.Value.Extensions["x-formData-name"]; ok { name = tempName.(string) } formDataParams[name] = formDataSchema formDataReqs[name] = false for _, req := range schema.Value.Required { if name == req { formDataReqs[name] = true } } } } else if formDataSchema.Value != nil { formDataParams[formDataName] = formDataSchema formDataReqs[formDataName] = false for _, req := range formDataSchema.Value.Required { if formDataName == req { formDataReqs[formDataName] = true } } } } return formDataBody(formDataParams, formDataReqs, consumes), nil } return nil, nil } func ToV3Response(response *openapi2.Response, produces []string) (*openapi3.ResponseRef, error) { if ref := response.Ref; ref != "" { return &openapi3.ResponseRef{Ref: ToV3Ref(ref)}, nil } result := &openapi3.Response{ Description: &response.Description, Extensions: stripNonExtensions(response.Extensions), } // Default to "application/json" if "produces" is not specified. if len(produces) == 0 { produces = []string{"application/json"} } if schemaRef := response.Schema; schemaRef != nil { schema := ToV3SchemaRef(schemaRef) result.Content = make(openapi3.Content, len(produces)) for _, mime := range produces { result.Content[mime] = openapi3.NewMediaType().WithSchemaRef(schema) } } if headers := response.Headers; len(headers) > 0 { result.Headers = ToV3Headers(headers) } return &openapi3.ResponseRef{Value: result}, nil } func ToV3Headers(defs map[string]*openapi2.Header) openapi3.Headers { headers := make(openapi3.Headers, len(defs)) for name, header := range defs { header.In = "" header.Name = "" if ref := header.Ref; ref != "" { headers[name] = &openapi3.HeaderRef{Ref: ToV3Ref(ref)} } else { parameter, _, _, _ := ToV3Parameter(nil, &header.Parameter, nil) headers[name] = &openapi3.HeaderRef{Value: &openapi3.Header{ Parameter: *parameter.Value, }} } } return headers } func ToV3Schemas(defs map[string]*openapi2.SchemaRef) map[string]*openapi3.SchemaRef { schemas := make(map[string]*openapi3.SchemaRef, len(defs)) for name, schema := range defs { schemas[name] = ToV3SchemaRef(schema) } return schemas } func ToV3SchemaRef(schema *openapi2.SchemaRef) *openapi3.SchemaRef { if schema == nil { return &openapi3.SchemaRef{} } if ref := schema.Ref; ref != "" { return &openapi3.SchemaRef{Ref: ToV3Ref(ref)} } if schema.Value == nil { return &openapi3.SchemaRef{ Extensions: schema.Extensions, } } v3Schema := &openapi3.Schema{ Extensions: schema.Extensions, Type: schema.Value.Type, Title: schema.Value.Title, Format: schema.Value.Format, Description: schema.Value.Description, Enum: schema.Value.Enum, Default: schema.Value.Default, Example: schema.Value.Example, ExternalDocs: schema.Value.ExternalDocs, UniqueItems: schema.Value.UniqueItems, ExclusiveMin: schema.Value.ExclusiveMin, ExclusiveMax: schema.Value.ExclusiveMax, ReadOnly: schema.Value.ReadOnly, WriteOnly: schema.Value.WriteOnly, AllowEmptyValue: schema.Value.AllowEmptyValue, Deprecated: schema.Value.Deprecated, XML: schema.Value.XML, Min: schema.Value.Min, Max: schema.Value.Max, MultipleOf: schema.Value.MultipleOf, MinLength: schema.Value.MinLength, MaxLength: schema.Value.MaxLength, Pattern: schema.Value.Pattern, MinItems: schema.Value.MinItems, MaxItems: schema.Value.MaxItems, Required: schema.Value.Required, MinProps: schema.Value.MinProps, MaxProps: schema.Value.MaxProps, AllOf: make(openapi3.SchemaRefs, len(schema.Value.AllOf)), Properties: make(openapi3.Schemas), AdditionalProperties: toV3AdditionalProperties(schema.Value.AdditionalProperties), } if schema.Value.Discriminator != "" { v3Schema.Discriminator = &openapi3.Discriminator{ PropertyName: schema.Value.Discriminator, } } if schema.Value.Items != nil { v3Schema.Items = ToV3SchemaRef(schema.Value.Items) } if schema.Value.Type.Is("file") { v3Schema.Format, v3Schema.Type = "binary", &openapi3.Types{"string"} } for k, v := range schema.Value.Properties { v3Schema.Properties[k] = ToV3SchemaRef(v) } for i, v := range schema.Value.AllOf { v3Schema.AllOf[i] = ToV3SchemaRef(v) } if val, ok := schema.Value.Extensions["x-nullable"]; ok { if nullable, valid := val.(bool); valid { v3Schema.Nullable = nullable delete(v3Schema.Extensions, "x-nullable") } } return &openapi3.SchemaRef{ Extensions: schema.Extensions, Value: v3Schema, } } func toV3AdditionalProperties(from openapi3.AdditionalProperties) openapi3.AdditionalProperties { return openapi3.AdditionalProperties{ Has: from.Has, Schema: convertRefsInV3SchemaRef(from.Schema), } } func convertRefsInV3SchemaRef(from *openapi3.SchemaRef) *openapi3.SchemaRef { if from == nil { return nil } to := *from to.Ref = ToV3Ref(to.Ref) if to.Value != nil { v := *from.Value to.Value = &v to.Value.AdditionalProperties = toV3AdditionalProperties(to.Value.AdditionalProperties) } return &to } var ref2To3 = map[string]string{ "#/definitions/": "#/components/schemas/", "#/responses/": "#/components/responses/", "#/parameters/": "#/components/parameters/", } func ToV3Ref(ref string) string { for old, new := range ref2To3 { if strings.HasPrefix(ref, old) { ref = strings.Replace(ref, old, new, 1) } } return ref } func FromV3Ref(ref string) string { for new, old := range ref2To3 { if strings.HasPrefix(ref, old) { ref = strings.Replace(ref, old, new, 1) } else if strings.HasPrefix(ref, "#/components/requestBodies/") { ref = strings.Replace(ref, "#/components/requestBodies/", "#/parameters/", 1) } } return ref } func ToV3SecurityRequirements(requirements openapi2.SecurityRequirements) openapi3.SecurityRequirements { if requirements == nil { return nil } result := make(openapi3.SecurityRequirements, len(requirements)) for i, item := range requirements { result[i] = item } return result } func ToV3SecurityScheme(securityScheme *openapi2.SecurityScheme) (*openapi3.SecuritySchemeRef, error) { if securityScheme == nil { return nil, nil } result := &openapi3.SecurityScheme{ Description: securityScheme.Description, Extensions: stripNonExtensions(securityScheme.Extensions), } switch securityScheme.Type { case "basic": result.Type = "http" result.Scheme = "basic" case "apiKey": result.Type = "apiKey" result.In = securityScheme.In result.Name = securityScheme.Name case "oauth2": result.Type = "oauth2" flows := &openapi3.OAuthFlows{} result.Flows = flows scopesMap := make(map[string]string) for scope, desc := range securityScheme.Scopes { scopesMap[scope] = desc } flow := &openapi3.OAuthFlow{ AuthorizationURL: securityScheme.AuthorizationURL, TokenURL: securityScheme.TokenURL, Scopes: scopesMap, } switch securityScheme.Flow { case "implicit": flows.Implicit = flow case "accessCode": flows.AuthorizationCode = flow case "password": flows.Password = flow case "application": flows.ClientCredentials = flow default: return nil, fmt.Errorf("unsupported flow %q", securityScheme.Flow) } } return &openapi3.SecuritySchemeRef{ Ref: ToV3Ref(securityScheme.Ref), Value: result, }, nil } // FromV3 converts an OpenAPIv3 spec to an OpenAPIv2 spec func FromV3(doc3 *openapi3.T) (*openapi2.T, error) { doc2Responses, err := FromV3Responses(doc3.Components.Responses, doc3.Components) if err != nil { return nil, err } schemas, parameters := FromV3Schemas(doc3.Components.Schemas, doc3.Components) doc2 := &openapi2.T{ Swagger: "2.0", Info: *doc3.Info, Definitions: schemas, Parameters: parameters, Responses: doc2Responses, Tags: doc3.Tags, Extensions: stripNonExtensions(doc3.Extensions), ExternalDocs: doc3.ExternalDocs, } isHTTPS := false isHTTP := false servers := doc3.Servers for i, server := range servers { parsedURL, err := url.Parse(server.URL) if err == nil { // See which schemes seem to be supported if parsedURL.Scheme == "https" { isHTTPS = true } else if parsedURL.Scheme == "http" { isHTTP = true } // The first server is assumed to provide the base path if i == 0 { doc2.Host = parsedURL.Host doc2.BasePath = parsedURL.Path } } } if isHTTPS { doc2.Schemes = append(doc2.Schemes, "https") } if isHTTP { doc2.Schemes = append(doc2.Schemes, "http") } for path, pathItem := range doc3.Paths.Map() { if pathItem == nil { continue } doc2.AddOperation(path, "GET", nil) addPathExtensions(doc2, path, stripNonExtensions(pathItem.Extensions)) for method, operation := range pathItem.Operations() { if operation == nil { continue } doc2Operation, err := FromV3Operation(doc3, operation) if err != nil { return nil, err } doc2.AddOperation(path, method, doc2Operation) } params := openapi2.Parameters{} for _, param := range pathItem.Parameters { p, err := FromV3Parameter(param, doc3.Components) if err != nil { return nil, err } params = append(params, p) } sort.Sort(params) doc2.Paths[path].Parameters = params } for name, param := range doc3.Components.Parameters { if doc2.Parameters[name], err = FromV3Parameter(param, doc3.Components); err != nil { return nil, err } } for name, requestBodyRef := range doc3.Components.RequestBodies { bodyOrRefParameters, formDataParameters, consumes, err := fromV3RequestBodies(name, requestBodyRef, doc3.Components) if err != nil { return nil, err } if len(formDataParameters) != 0 { for _, param := range formDataParameters { doc2.Parameters[param.Name] = param } } else if len(bodyOrRefParameters) != 0 { for _, param := range bodyOrRefParameters { doc2.Parameters[name] = param } } if len(consumes) != 0 { doc2.Consumes = consumesToArray(consumes) } } if m := doc3.Components.SecuritySchemes; m != nil { doc2SecuritySchemes := make(map[string]*openapi2.SecurityScheme) for id, securityScheme := range m { v, err := FromV3SecurityScheme(doc3, securityScheme) if err != nil { return nil, err } doc2SecuritySchemes[id] = v } doc2.SecurityDefinitions = doc2SecuritySchemes } doc2.Security = FromV3SecurityRequirements(doc3.Security) return doc2, nil } func consumesToArray(consumes map[string]struct{}) []string { consumesArr := make([]string, 0, len(consumes)) for key := range consumes { consumesArr = append(consumesArr, key) } sort.Strings(consumesArr) return consumesArr } func fromV3RequestBodies(name string, requestBodyRef *openapi3.RequestBodyRef, components *openapi3.Components) ( bodyOrRefParameters openapi2.Parameters, formParameters openapi2.Parameters, consumes map[string]struct{}, err error, ) { if ref := requestBodyRef.Ref; ref != "" { bodyOrRefParameters = append(bodyOrRefParameters, &openapi2.Parameter{Ref: FromV3Ref(ref)}) return } // Only select one formData or request body for an individual requestBody as OpenAPI 2 does not support multiples if requestBodyRef.Value != nil { for contentType, mediaType := range requestBodyRef.Value.Content { if consumes == nil { consumes = make(map[string]struct{}) } consumes[contentType] = struct{}{} if contentType == "application/x-www-form-urlencoded" || contentType == "multipart/form-data" { formParameters = FromV3RequestBodyFormData(mediaType) continue } paramName := name if originalName, ok := requestBodyRef.Value.Extensions["x-originalParamName"]; ok { paramName = originalName.(string) } var r *openapi2.Parameter if r, err = FromV3RequestBody(paramName, requestBodyRef, mediaType, components); err != nil { return } bodyOrRefParameters = append(bodyOrRefParameters, r) } } return } func FromV3Schemas(schemas map[string]*openapi3.SchemaRef, components *openapi3.Components) (map[string]*openapi2.SchemaRef, map[string]*openapi2.Parameter) { v2Defs := make(map[string]*openapi2.SchemaRef) v2Params := make(map[string]*openapi2.Parameter) for name, schema := range schemas { schemaConv, parameterConv := FromV3SchemaRef(schema, components) if schemaConv != nil { v2Defs[name] = schemaConv } else if parameterConv != nil { if parameterConv.Name == "" { parameterConv.Name = name } v2Params[name] = parameterConv } } return v2Defs, v2Params } func FromV3SchemaRef(schema *openapi3.SchemaRef, components *openapi3.Components) (*openapi2.SchemaRef, *openapi2.Parameter) { if ref := schema.Ref; ref != "" { name := getParameterNameFromNewRef(ref) if val, ok := components.Schemas[name]; ok { if val.Value.Format == "binary" { v2Ref := strings.Replace(ref, "#/components/schemas/", "#/parameters/", 1) return nil, &openapi2.Parameter{Ref: v2Ref} } } return &openapi2.SchemaRef{Ref: FromV3Ref(ref)}, nil } if schema.Value == nil { return &openapi2.SchemaRef{ Extensions: schema.Extensions, }, nil } if schema.Value != nil { if schema.Value.Type.Is("string") && schema.Value.Format == "binary" { paramType := &openapi3.Types{"file"} required := false value, _ := schema.Value.Extensions["x-formData-name"] originalName, _ := value.(string) for _, prop := range schema.Value.Required { if originalName == prop { required = true break } } return nil, &openapi2.Parameter{ In: "formData", Name: originalName, Description: schema.Value.Description, Type: paramType, Enum: schema.Value.Enum, Minimum: schema.Value.Min, Maximum: schema.Value.Max, ExclusiveMin: schema.Value.ExclusiveMin, ExclusiveMax: schema.Value.ExclusiveMax, MinLength: schema.Value.MinLength, MaxLength: schema.Value.MaxLength, Default: schema.Value.Default, // Items: schema.Value.Items, MinItems: schema.Value.MinItems, MaxItems: schema.Value.MaxItems, AllowEmptyValue: schema.Value.AllowEmptyValue, UniqueItems: schema.Value.UniqueItems, MultipleOf: schema.Value.MultipleOf, Extensions: stripNonExtensions(schema.Value.Extensions), Required: required, } } } v2Schema := &openapi2.Schema{ Extensions: schema.Value.Extensions, Type: schema.Value.Type, Title: schema.Value.Title, Format: schema.Value.Format, Description: schema.Value.Description, Enum: schema.Value.Enum, Default: schema.Value.Default, Example: schema.Value.Example, ExternalDocs: schema.Value.ExternalDocs, UniqueItems: schema.Value.UniqueItems, ExclusiveMin: schema.Value.ExclusiveMin, ExclusiveMax: schema.Value.ExclusiveMax, ReadOnly: schema.Value.ReadOnly, WriteOnly: schema.Value.WriteOnly, AllowEmptyValue: schema.Value.AllowEmptyValue, Deprecated: schema.Value.Deprecated, XML: schema.Value.XML, Min: schema.Value.Min, Max: schema.Value.Max, MultipleOf: schema.Value.MultipleOf, MinLength: schema.Value.MinLength, MaxLength: schema.Value.MaxLength, Pattern: schema.Value.Pattern, MinItems: schema.Value.MinItems, MaxItems: schema.Value.MaxItems, Required: schema.Value.Required, MinProps: schema.Value.MinProps, MaxProps: schema.Value.MaxProps, Properties: make(openapi2.Schemas), AllOf: make(openapi2.SchemaRefs, len(schema.Value.AllOf)), AdditionalProperties: schema.Value.AdditionalProperties, } if v := schema.Value.Items; v != nil { v2Schema.Items, _ = FromV3SchemaRef(v, components) } keys := make([]string, 0, len(schema.Value.Properties)) for k := range schema.Value.Properties { keys = append(keys, k) } sort.Strings(keys) for _, key := range keys { property, _ := FromV3SchemaRef(schema.Value.Properties[key], components) if property != nil { v2Schema.Properties[key] = property } } for i, v := range schema.Value.AllOf { v2Schema.AllOf[i], _ = FromV3SchemaRef(v, components) } if schema.Value.PermitsNull() { schema.Value.Nullable = false if schema.Value.Extensions == nil { v2Schema.Extensions = make(map[string]any) } v2Schema.Extensions["x-nullable"] = true } return &openapi2.SchemaRef{ Extensions: schema.Extensions, Value: v2Schema, }, nil } func FromV3SecurityRequirements(requirements openapi3.SecurityRequirements) openapi2.SecurityRequirements { if requirements == nil { return nil } result := make([]map[string][]string, 0, len(requirements)) for _, item := range requirements { result = append(result, item) } return result } func FromV3PathItem(doc3 *openapi3.T, pathItem *openapi3.PathItem) (*openapi2.PathItem, error) { result := &openapi2.PathItem{ Extensions: stripNonExtensions(pathItem.Extensions), } for method, operation := range pathItem.Operations() { r, err := FromV3Operation(doc3, operation) if err != nil { return nil, err } result.SetOperation(method, r) } for _, parameter := range pathItem.Parameters { p, err := FromV3Parameter(parameter, doc3.Components) if err != nil { return nil, err } result.Parameters = append(result.Parameters, p) } return result, nil } func findNameForRequestBody(operation *openapi3.Operation) string { nameSearch: for _, name := range attemptedBodyParameterNames { for _, parameterRef := range operation.Parameters { parameter := parameterRef.Value if parameter != nil && parameter.Name == name { continue nameSearch } } return name } return "" } func FromV3RequestBodyFormData(mediaType *openapi3.MediaType) openapi2.Parameters { parameters := openapi2.Parameters{} for propName, schemaRef := range mediaType.Schema.Value.Properties { if ref := schemaRef.Ref; ref != "" { v2Ref := strings.Replace(ref, "#/components/schemas/", "#/parameters/", 1) parameters = append(parameters, &openapi2.Parameter{Ref: v2Ref}) continue } val := schemaRef.Value typ := val.Type if val.Format == "binary" { typ = &openapi3.Types{"file"} } required := false for _, name := range val.Required { if name == propName { required = true break } } var v2Items *openapi2.SchemaRef if val.Items != nil { v2Items, _ = FromV3SchemaRef(val.Items, nil) } parameter := &openapi2.Parameter{ Name: propName, Description: val.Description, Type: typ, In: "formData", Extensions: stripNonExtensions(val.Extensions), Enum: val.Enum, ExclusiveMin: val.ExclusiveMin, ExclusiveMax: val.ExclusiveMax, MinLength: val.MinLength, MaxLength: val.MaxLength, Default: val.Default, Items: v2Items, MinItems: val.MinItems, MaxItems: val.MaxItems, Maximum: val.Max, Minimum: val.Min, Pattern: val.Pattern, // CollectionFormat: val.CollectionFormat, // Format: val.Format, AllowEmptyValue: val.AllowEmptyValue, Required: required, UniqueItems: val.UniqueItems, MultipleOf: val.MultipleOf, } parameters = append(parameters, parameter) } return parameters } func FromV3Operation(doc3 *openapi3.T, operation *openapi3.Operation) (*openapi2.Operation, error) { if operation == nil { return nil, nil } result := &openapi2.Operation{ OperationID: operation.OperationID, Summary: operation.Summary, Description: operation.Description, Deprecated: operation.Deprecated, Tags: operation.Tags, Extensions: stripNonExtensions(operation.Extensions), } if v := operation.Security; v != nil { resultSecurity := FromV3SecurityRequirements(*v) result.Security = &resultSecurity } for _, parameter := range operation.Parameters { r, err := FromV3Parameter(parameter, doc3.Components) if err != nil { return nil, err } result.Parameters = append(result.Parameters, r) } if v := operation.RequestBody; v != nil { // Find parameter name that we can use for the body name := findNameForRequestBody(operation) if name == "" { return nil, errors.New("could not find a name for request body") } bodyOrRefParameters, formDataParameters, consumes, err := fromV3RequestBodies(name, v, doc3.Components) if err != nil { return nil, err } if len(formDataParameters) != 0 { result.Parameters = append(result.Parameters, formDataParameters...) } else if len(bodyOrRefParameters) != 0 { for _, param := range bodyOrRefParameters { result.Parameters = append(result.Parameters, param) break // add a single request body } } if len(consumes) != 0 { result.Consumes = consumesToArray(consumes) } } sort.Sort(result.Parameters) if responses := operation.Responses; responses != nil { resultResponses, err := FromV3Responses(responses.Map(), doc3.Components) if err != nil { return nil, err } result.Responses = resultResponses } return result, nil } func FromV3RequestBody(name string, requestBodyRef *openapi3.RequestBodyRef, mediaType *openapi3.MediaType, components *openapi3.Components) (*openapi2.Parameter, error) { requestBody := requestBodyRef.Value result := &openapi2.Parameter{ In: "body", Name: name, Description: requestBody.Description, Required: requestBody.Required, Extensions: stripNonExtensions(requestBody.Extensions), } if mediaType != nil { result.Schema, _ = FromV3SchemaRef(mediaType.Schema, components) } return result, nil } func FromV3Parameter(ref *openapi3.ParameterRef, components *openapi3.Components) (*openapi2.Parameter, error) { if ref := ref.Ref; ref != "" { return &openapi2.Parameter{Ref: FromV3Ref(ref)}, nil } parameter := ref.Value if parameter == nil { return nil, nil } result := &openapi2.Parameter{ Description: parameter.Description, In: parameter.In, Name: parameter.Name, Required: parameter.Required, Extensions: stripNonExtensions(parameter.Extensions), } if schemaRef := parameter.Schema; schemaRef != nil { schemaRefV2, _ := FromV3SchemaRef(schemaRef, components) if ref := schemaRefV2.Ref; ref != "" { result.Schema = &openapi2.SchemaRef{Ref: FromV3Ref(ref)} return result, nil } schema := schemaRefV2.Value result.Type = schema.Type result.Format = schema.Format result.Enum = schema.Enum result.Minimum = schema.Min result.Maximum = schema.Max result.ExclusiveMin = schema.ExclusiveMin result.ExclusiveMax = schema.ExclusiveMax result.MinLength = schema.MinLength result.MaxLength = schema.MaxLength result.Pattern = schema.Pattern result.Default = schema.Default result.Items = schema.Items result.MinItems = schema.MinItems result.MaxItems = schema.MaxItems result.AllowEmptyValue = schema.AllowEmptyValue // result.CollectionFormat = schema.CollectionFormat result.UniqueItems = schema.UniqueItems result.MultipleOf = schema.MultipleOf } return result, nil } func FromV3Responses(responses map[string]*openapi3.ResponseRef, components *openapi3.Components) (map[string]*openapi2.Response, error) { v2Responses := make(map[string]*openapi2.Response, len(responses)) for k, response := range responses { r, err := FromV3Response(response, components) if err != nil { return nil, err } v2Responses[k] = r } return v2Responses, nil } func FromV3Response(ref *openapi3.ResponseRef, components *openapi3.Components) (*openapi2.Response, error) { if ref := ref.Ref; ref != "" { return &openapi2.Response{Ref: FromV3Ref(ref)}, nil } response := ref.Value if response == nil { return nil, nil } description := "" if desc := response.Description; desc != nil { description = *desc } result := &openapi2.Response{ Description: description, Extensions: stripNonExtensions(response.Extensions), } if content := response.Content; content != nil { if ct := content["application/json"]; ct != nil { result.Schema, _ = FromV3SchemaRef(ct.Schema, components) } } if headers := response.Headers; len(headers) > 0 { var err error if result.Headers, err = FromV3Headers(headers, components); err != nil { return nil, err } } return result, nil } func FromV3Headers(defs openapi3.Headers, components *openapi3.Components) (map[string]*openapi2.Header, error) { headers := make(map[string]*openapi2.Header, len(defs)) for name, header := range defs { ref := openapi3.ParameterRef{Ref: header.Ref, Value: &header.Value.Parameter} parameter, err := FromV3Parameter(&ref, components) if err != nil { return nil, err } parameter.In = "" parameter.Name = "" headers[name] = &openapi2.Header{Parameter: *parameter} } return headers, nil } func FromV3SecurityScheme(doc3 *openapi3.T, ref *openapi3.SecuritySchemeRef) (*openapi2.SecurityScheme, error) { securityScheme := ref.Value if securityScheme == nil { return nil, nil } result := &openapi2.SecurityScheme{ Ref: FromV3Ref(ref.Ref), Description: securityScheme.Description, Extensions: stripNonExtensions(securityScheme.Extensions), } switch securityScheme.Type { case "http": switch securityScheme.Scheme { case "basic": result.Type = "basic" default: result.Type = "apiKey" result.In = "header" result.Name = "Authorization" } case "apiKey": result.Type = "apiKey" result.In = securityScheme.In result.Name = securityScheme.Name case "oauth2": result.Type = "oauth2" flows := securityScheme.Flows if flows != nil { var flow *openapi3.OAuthFlow // TODO: Is this the right priority? What if multiple defined? switch { case flows.Implicit != nil: result.Flow = "implicit" flow = flows.Implicit result.AuthorizationURL = flow.AuthorizationURL case flows.AuthorizationCode != nil: result.Flow = "accessCode" flow = flows.AuthorizationCode result.AuthorizationURL = flow.AuthorizationURL result.TokenURL = flow.TokenURL case flows.Password != nil: result.Flow = "password" flow = flows.Password result.TokenURL = flow.TokenURL case flows.ClientCredentials != nil: result.Flow = "application" flow = flows.ClientCredentials result.TokenURL = flow.TokenURL default: return nil, nil } result.Scopes = make(map[string]string, len(flow.Scopes)) for scope, desc := range flow.Scopes { result.Scopes[scope] = desc } } default: return nil, fmt.Errorf("unsupported security scheme type %q", securityScheme.Type) } return result, nil } var attemptedBodyParameterNames = []string{ "body", "requestBody", } // stripNonExtensions removes invalid extensions: those not prefixed by "x-" and returns them func stripNonExtensions(extensions map[string]any) map[string]any { for extName := range extensions { if !strings.HasPrefix(extName, "x-") { delete(extensions, extName) } } return extensions } func addPathExtensions(doc2 *openapi2.T, path string, extensions map[string]any) { if doc2.Paths == nil { doc2.Paths = make(map[string]*openapi2.PathItem) } pathItem := doc2.Paths[path] if pathItem == nil { pathItem = &openapi2.PathItem{} doc2.Paths[path] = pathItem } pathItem.Extensions = extensions } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/callback.go ================================================ package openapi3 import ( "context" "sort" ) // Callback is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#callback-object type Callback struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` m map[string]*PathItem } // NewCallback builds a Callback object with path items in insertion order. func NewCallback(opts ...NewCallbackOption) *Callback { Callback := NewCallbackWithCapacity(len(opts)) for _, opt := range opts { opt(Callback) } return Callback } // NewCallbackOption describes options to NewCallback func type NewCallbackOption func(*Callback) // WithCallback adds Callback as an option to NewCallback func WithCallback(cb string, pathItem *PathItem) NewCallbackOption { return func(callback *Callback) { if p := pathItem; p != nil && cb != "" { callback.Set(cb, p) } } } // Validate returns an error if Callback does not comply with the OpenAPI spec. func (callback *Callback) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) keys := make([]string, 0, callback.Len()) for key := range callback.Map() { keys = append(keys, key) } sort.Strings(keys) for _, key := range keys { v := callback.Value(key) if err := v.Validate(ctx); err != nil { return err } } return validateExtensions(ctx, callback.Extensions) } // UnmarshalJSON sets Callbacks to a copy of data. func (callbacks *Callbacks) UnmarshalJSON(data []byte) (err error) { *callbacks, _, err = unmarshalStringMapP[CallbackRef](data) return } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/components.go ================================================ package openapi3 import ( "context" "encoding/json" "fmt" "sort" "github.com/go-openapi/jsonpointer" ) type ( Callbacks map[string]*CallbackRef Examples map[string]*ExampleRef Headers map[string]*HeaderRef Links map[string]*LinkRef ParametersMap map[string]*ParameterRef RequestBodies map[string]*RequestBodyRef ResponseBodies map[string]*ResponseRef Schemas map[string]*SchemaRef SecuritySchemes map[string]*SecuritySchemeRef ) // Components is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#components-object type Components struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Schemas Schemas `json:"schemas,omitempty" yaml:"schemas,omitempty"` Parameters ParametersMap `json:"parameters,omitempty" yaml:"parameters,omitempty"` Headers Headers `json:"headers,omitempty" yaml:"headers,omitempty"` RequestBodies RequestBodies `json:"requestBodies,omitempty" yaml:"requestBodies,omitempty"` Responses ResponseBodies `json:"responses,omitempty" yaml:"responses,omitempty"` SecuritySchemes SecuritySchemes `json:"securitySchemes,omitempty" yaml:"securitySchemes,omitempty"` Examples Examples `json:"examples,omitempty" yaml:"examples,omitempty"` Links Links `json:"links,omitempty" yaml:"links,omitempty"` Callbacks Callbacks `json:"callbacks,omitempty" yaml:"callbacks,omitempty"` } func NewComponents() Components { return Components{} } // MarshalJSON returns the JSON encoding of Components. func (components Components) MarshalJSON() ([]byte, error) { x, err := components.MarshalYAML() if err != nil { return nil, err } return json.Marshal(x) } // MarshalYAML returns the YAML encoding of Components. func (components Components) MarshalYAML() (any, error) { m := make(map[string]any, 9+len(components.Extensions)) for k, v := range components.Extensions { m[k] = v } if x := components.Schemas; len(x) != 0 { m["schemas"] = x } if x := components.Parameters; len(x) != 0 { m["parameters"] = x } if x := components.Headers; len(x) != 0 { m["headers"] = x } if x := components.RequestBodies; len(x) != 0 { m["requestBodies"] = x } if x := components.Responses; len(x) != 0 { m["responses"] = x } if x := components.SecuritySchemes; len(x) != 0 { m["securitySchemes"] = x } if x := components.Examples; len(x) != 0 { m["examples"] = x } if x := components.Links; len(x) != 0 { m["links"] = x } if x := components.Callbacks; len(x) != 0 { m["callbacks"] = x } return m, nil } // UnmarshalJSON sets Components to a copy of data. func (components *Components) UnmarshalJSON(data []byte) error { type ComponentsBis Components var x ComponentsBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, originKey) delete(x.Extensions, "schemas") delete(x.Extensions, "parameters") delete(x.Extensions, "headers") delete(x.Extensions, "requestBodies") delete(x.Extensions, "responses") delete(x.Extensions, "securitySchemes") delete(x.Extensions, "examples") delete(x.Extensions, "links") delete(x.Extensions, "callbacks") if len(x.Extensions) == 0 { x.Extensions = nil } *components = Components(x) return nil } // Validate returns an error if Components does not comply with the OpenAPI spec. func (components *Components) Validate(ctx context.Context, opts ...ValidationOption) (err error) { ctx = WithValidationOptions(ctx, opts...) schemas := make([]string, 0, len(components.Schemas)) for name := range components.Schemas { schemas = append(schemas, name) } sort.Strings(schemas) for _, k := range schemas { v := components.Schemas[k] if err = ValidateIdentifier(k); err != nil { return fmt.Errorf("schema %q: %w", k, err) } if err = v.Validate(ctx); err != nil { return fmt.Errorf("schema %q: %w", k, err) } } parameters := make([]string, 0, len(components.Parameters)) for name := range components.Parameters { parameters = append(parameters, name) } sort.Strings(parameters) for _, k := range parameters { v := components.Parameters[k] if err = ValidateIdentifier(k); err != nil { return fmt.Errorf("parameter %q: %w", k, err) } if err = v.Validate(ctx); err != nil { return fmt.Errorf("parameter %q: %w", k, err) } } requestBodies := make([]string, 0, len(components.RequestBodies)) for name := range components.RequestBodies { requestBodies = append(requestBodies, name) } sort.Strings(requestBodies) for _, k := range requestBodies { v := components.RequestBodies[k] if err = ValidateIdentifier(k); err != nil { return fmt.Errorf("request body %q: %w", k, err) } if err = v.Validate(ctx); err != nil { return fmt.Errorf("request body %q: %w", k, err) } } responses := make([]string, 0, len(components.Responses)) for name := range components.Responses { responses = append(responses, name) } sort.Strings(responses) for _, k := range responses { if err = ValidateIdentifier(k); err != nil { return fmt.Errorf("response %q: %w", k, err) } v := components.Responses[k] if err = v.Validate(ctx); err != nil { return fmt.Errorf("response %q: %w", k, err) } } headers := make([]string, 0, len(components.Headers)) for name := range components.Headers { headers = append(headers, name) } sort.Strings(headers) for _, k := range headers { v := components.Headers[k] if err = ValidateIdentifier(k); err != nil { return fmt.Errorf("header %q: %w", k, err) } if err = v.Validate(ctx); err != nil { return fmt.Errorf("header %q: %w", k, err) } } securitySchemes := make([]string, 0, len(components.SecuritySchemes)) for name := range components.SecuritySchemes { securitySchemes = append(securitySchemes, name) } sort.Strings(securitySchemes) for _, k := range securitySchemes { v := components.SecuritySchemes[k] if err = ValidateIdentifier(k); err != nil { return fmt.Errorf("security scheme %q: %w", k, err) } if err = v.Validate(ctx); err != nil { return fmt.Errorf("security scheme %q: %w", k, err) } } examples := make([]string, 0, len(components.Examples)) for name := range components.Examples { examples = append(examples, name) } sort.Strings(examples) for _, k := range examples { v := components.Examples[k] if err = ValidateIdentifier(k); err != nil { return fmt.Errorf("example %q: %w", k, err) } if err = v.Validate(ctx); err != nil { return fmt.Errorf("example %q: %w", k, err) } } links := make([]string, 0, len(components.Links)) for name := range components.Links { links = append(links, name) } sort.Strings(links) for _, k := range links { v := components.Links[k] if err = ValidateIdentifier(k); err != nil { return fmt.Errorf("link %q: %w", k, err) } if err = v.Validate(ctx); err != nil { return fmt.Errorf("link %q: %w", k, err) } } callbacks := make([]string, 0, len(components.Callbacks)) for name := range components.Callbacks { callbacks = append(callbacks, name) } sort.Strings(callbacks) for _, k := range callbacks { v := components.Callbacks[k] if err = ValidateIdentifier(k); err != nil { return fmt.Errorf("callback %q: %w", k, err) } if err = v.Validate(ctx); err != nil { return fmt.Errorf("callback %q: %w", k, err) } } return validateExtensions(ctx, components.Extensions) } var _ jsonpointer.JSONPointable = (*Schemas)(nil) // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (m Schemas) JSONLookup(token string) (any, error) { if v, ok := m[token]; !ok || v == nil { return nil, fmt.Errorf("no schema %q", token) } else if ref := v.Ref; ref != "" { return &Ref{Ref: ref}, nil } else { return v.Value, nil } } var _ jsonpointer.JSONPointable = (*ParametersMap)(nil) // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (m ParametersMap) JSONLookup(token string) (any, error) { if v, ok := m[token]; !ok || v == nil { return nil, fmt.Errorf("no parameter %q", token) } else if ref := v.Ref; ref != "" { return &Ref{Ref: ref}, nil } else { return v.Value, nil } } var _ jsonpointer.JSONPointable = (*Headers)(nil) // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (m Headers) JSONLookup(token string) (any, error) { if v, ok := m[token]; !ok || v == nil { return nil, fmt.Errorf("no header %q", token) } else if ref := v.Ref; ref != "" { return &Ref{Ref: ref}, nil } else { return v.Value, nil } } var _ jsonpointer.JSONPointable = (*RequestBodyRef)(nil) // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (m RequestBodies) JSONLookup(token string) (any, error) { if v, ok := m[token]; !ok || v == nil { return nil, fmt.Errorf("no request body %q", token) } else if ref := v.Ref; ref != "" { return &Ref{Ref: ref}, nil } else { return v.Value, nil } } var _ jsonpointer.JSONPointable = (*ResponseRef)(nil) // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (m ResponseBodies) JSONLookup(token string) (any, error) { if v, ok := m[token]; !ok || v == nil { return nil, fmt.Errorf("no response body %q", token) } else if ref := v.Ref; ref != "" { return &Ref{Ref: ref}, nil } else { return v.Value, nil } } var _ jsonpointer.JSONPointable = (*SecuritySchemes)(nil) // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (m SecuritySchemes) JSONLookup(token string) (any, error) { if v, ok := m[token]; !ok || v == nil { return nil, fmt.Errorf("no security scheme body %q", token) } else if ref := v.Ref; ref != "" { return &Ref{Ref: ref}, nil } else { return v.Value, nil } } var _ jsonpointer.JSONPointable = (*Examples)(nil) // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (m Examples) JSONLookup(token string) (any, error) { if v, ok := m[token]; !ok || v == nil { return nil, fmt.Errorf("no example body %q", token) } else if ref := v.Ref; ref != "" { return &Ref{Ref: ref}, nil } else { return v.Value, nil } } var _ jsonpointer.JSONPointable = (*Links)(nil) // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (m Links) JSONLookup(token string) (any, error) { if v, ok := m[token]; !ok || v == nil { return nil, fmt.Errorf("no link body %q", token) } else if ref := v.Ref; ref != "" { return &Ref{Ref: ref}, nil } else { return v.Value, nil } } var _ jsonpointer.JSONPointable = (*Callbacks)(nil) // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (m Callbacks) JSONLookup(token string) (any, error) { if v, ok := m[token]; !ok || v == nil { return nil, fmt.Errorf("no callback body %q", token) } else if ref := v.Ref; ref != "" { return &Ref{Ref: ref}, nil } else { return v.Value, nil } } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/contact.go ================================================ package openapi3 import ( "context" "encoding/json" ) // Contact is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#contact-object type Contact struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"` URL string `json:"url,omitempty" yaml:"url,omitempty"` Email string `json:"email,omitempty" yaml:"email,omitempty"` } // MarshalJSON returns the JSON encoding of Contact. func (contact Contact) MarshalJSON() ([]byte, error) { x, err := contact.MarshalYAML() if err != nil { return nil, err } return json.Marshal(x) } // MarshalYAML returns the YAML encoding of Contact. func (contact Contact) MarshalYAML() (any, error) { m := make(map[string]any, 3+len(contact.Extensions)) for k, v := range contact.Extensions { m[k] = v } if x := contact.Name; x != "" { m["name"] = x } if x := contact.URL; x != "" { m["url"] = x } if x := contact.Email; x != "" { m["email"] = x } return m, nil } // UnmarshalJSON sets Contact to a copy of data. func (contact *Contact) UnmarshalJSON(data []byte) error { type ContactBis Contact var x ContactBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, originKey) delete(x.Extensions, "name") delete(x.Extensions, "url") delete(x.Extensions, "email") if len(x.Extensions) == 0 { x.Extensions = nil } *contact = Contact(x) return nil } // Validate returns an error if Contact does not comply with the OpenAPI spec. func (contact *Contact) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) return validateExtensions(ctx, contact.Extensions) } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/content.go ================================================ package openapi3 import ( "context" "sort" "strings" ) // Content is specified by OpenAPI/Swagger 3.0 standard. type Content map[string]*MediaType func NewContent() Content { return make(map[string]*MediaType) } func NewContentWithSchema(schema *Schema, consumes []string) Content { if len(consumes) == 0 { return Content{ "*/*": NewMediaType().WithSchema(schema), } } content := make(map[string]*MediaType, len(consumes)) for _, mediaType := range consumes { content[mediaType] = NewMediaType().WithSchema(schema) } return content } func NewContentWithSchemaRef(schema *SchemaRef, consumes []string) Content { if len(consumes) == 0 { return Content{ "*/*": NewMediaType().WithSchemaRef(schema), } } content := make(map[string]*MediaType, len(consumes)) for _, mediaType := range consumes { content[mediaType] = NewMediaType().WithSchemaRef(schema) } return content } func NewContentWithJSONSchema(schema *Schema) Content { return Content{ "application/json": NewMediaType().WithSchema(schema), } } func NewContentWithJSONSchemaRef(schema *SchemaRef) Content { return Content{ "application/json": NewMediaType().WithSchemaRef(schema), } } func NewContentWithFormDataSchema(schema *Schema) Content { return Content{ "multipart/form-data": NewMediaType().WithSchema(schema), } } func NewContentWithFormDataSchemaRef(schema *SchemaRef) Content { return Content{ "multipart/form-data": NewMediaType().WithSchemaRef(schema), } } func (content Content) Get(mime string) *MediaType { // If the mime is empty then short-circuit to the wildcard. // We do this here so that we catch only the specific case of // and empty mime rather than a present, but invalid, mime type. if mime == "" { return content["*/*"] } // Start by making the most specific match possible // by using the mime type in full. if v := content[mime]; v != nil { return v } // If an exact match is not found then we strip all // metadata from the mime type and only use the x/y // portion. i := strings.IndexByte(mime, ';') if i < 0 { // If there is no metadata then preserve the full mime type // string for later wildcard searches. i = len(mime) } mime = mime[:i] if v := content[mime]; v != nil { return v } // If the x/y pattern has no specific match then we // try the x/* pattern. i = strings.IndexByte(mime, '/') if i < 0 { // In the case that the given mime type is not valid because it is // missing the subtype we return nil so that this does not accidentally // resolve with the wildcard. return nil } mime = mime[:i] + "/*" if v := content[mime]; v != nil { return v } // Finally, the most generic match of */* is returned // as a catch-all. return content["*/*"] } // Validate returns an error if Content does not comply with the OpenAPI spec. func (content Content) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) keys := make([]string, 0, len(content)) for key := range content { keys = append(keys, key) } sort.Strings(keys) for _, k := range keys { v := content[k] if err := v.Validate(ctx); err != nil { return err } } return nil } // UnmarshalJSON sets Content to a copy of data. func (content *Content) UnmarshalJSON(data []byte) (err error) { *content, _, err = unmarshalStringMapP[MediaType](data) return } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/discriminator.go ================================================ package openapi3 import ( "context" "encoding/json" ) // Discriminator is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#discriminator-object type Discriminator struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` PropertyName string `json:"propertyName" yaml:"propertyName"` // required Mapping StringMap `json:"mapping,omitempty" yaml:"mapping,omitempty"` } // MarshalJSON returns the JSON encoding of Discriminator. func (discriminator Discriminator) MarshalJSON() ([]byte, error) { x, err := discriminator.MarshalYAML() if err != nil { return nil, err } return json.Marshal(x) } // MarshalYAML returns the YAML encoding of Discriminator. func (discriminator Discriminator) MarshalYAML() (any, error) { m := make(map[string]any, 2+len(discriminator.Extensions)) for k, v := range discriminator.Extensions { m[k] = v } m["propertyName"] = discriminator.PropertyName if x := discriminator.Mapping; len(x) != 0 { m["mapping"] = x } return m, nil } // UnmarshalJSON sets Discriminator to a copy of data. func (discriminator *Discriminator) UnmarshalJSON(data []byte) error { type DiscriminatorBis Discriminator var x DiscriminatorBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, originKey) delete(x.Extensions, "propertyName") delete(x.Extensions, "mapping") if len(x.Extensions) == 0 { x.Extensions = nil } *discriminator = Discriminator(x) return nil } // Validate returns an error if Discriminator does not comply with the OpenAPI spec. func (discriminator *Discriminator) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) return validateExtensions(ctx, discriminator.Extensions) } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/doc.go ================================================ // Package openapi3 parses and writes OpenAPI 3 specification documents. // // See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md package openapi3 ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/encoding.go ================================================ package openapi3 import ( "context" "encoding/json" "fmt" "sort" ) // Encoding is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#encoding-object type Encoding struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` ContentType string `json:"contentType,omitempty" yaml:"contentType,omitempty"` Headers Headers `json:"headers,omitempty" yaml:"headers,omitempty"` Style string `json:"style,omitempty" yaml:"style,omitempty"` Explode *bool `json:"explode,omitempty" yaml:"explode,omitempty"` AllowReserved bool `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"` } func NewEncoding() *Encoding { return &Encoding{} } func (encoding *Encoding) WithHeader(name string, header *Header) *Encoding { return encoding.WithHeaderRef(name, &HeaderRef{ Value: header, }) } func (encoding *Encoding) WithHeaderRef(name string, ref *HeaderRef) *Encoding { headers := encoding.Headers if headers == nil { headers = make(map[string]*HeaderRef) encoding.Headers = headers } headers[name] = ref return encoding } // MarshalJSON returns the JSON encoding of Encoding. func (encoding Encoding) MarshalJSON() ([]byte, error) { x, err := encoding.MarshalYAML() if err != nil { return nil, err } return json.Marshal(x) } // MarshalYAML returns the YAML encoding of Encoding. func (encoding Encoding) MarshalYAML() (any, error) { m := make(map[string]any, 5+len(encoding.Extensions)) for k, v := range encoding.Extensions { m[k] = v } if x := encoding.ContentType; x != "" { m["contentType"] = x } if x := encoding.Headers; len(x) != 0 { m["headers"] = x } if x := encoding.Style; x != "" { m["style"] = x } if x := encoding.Explode; x != nil { m["explode"] = x } if x := encoding.AllowReserved; x { m["allowReserved"] = x } return m, nil } // UnmarshalJSON sets Encoding to a copy of data. func (encoding *Encoding) UnmarshalJSON(data []byte) error { type EncodingBis Encoding var x EncodingBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, originKey) delete(x.Extensions, "contentType") delete(x.Extensions, "headers") delete(x.Extensions, "style") delete(x.Extensions, "explode") delete(x.Extensions, "allowReserved") if len(x.Extensions) == 0 { x.Extensions = nil } *encoding = Encoding(x) return nil } // SerializationMethod returns a serialization method of request body. // When serialization method is not defined the method returns the default serialization method. func (encoding *Encoding) SerializationMethod() *SerializationMethod { sm := &SerializationMethod{Style: SerializationForm, Explode: true} if encoding != nil { if encoding.Style != "" { sm.Style = encoding.Style } if encoding.Explode != nil { sm.Explode = *encoding.Explode } } return sm } // Validate returns an error if Encoding does not comply with the OpenAPI spec. func (encoding *Encoding) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) if encoding == nil { return nil } headers := make([]string, 0, len(encoding.Headers)) for k := range encoding.Headers { headers = append(headers, k) } sort.Strings(headers) for _, k := range headers { v := encoding.Headers[k] if err := ValidateIdentifier(k); err != nil { return nil } if err := v.Validate(ctx); err != nil { return nil } } // Validate a media types's serialization method. sm := encoding.SerializationMethod() switch { case sm.Style == SerializationForm && sm.Explode, sm.Style == SerializationForm && !sm.Explode, sm.Style == SerializationSpaceDelimited && sm.Explode, sm.Style == SerializationSpaceDelimited && !sm.Explode, sm.Style == SerializationPipeDelimited && sm.Explode, sm.Style == SerializationPipeDelimited && !sm.Explode, sm.Style == SerializationDeepObject && sm.Explode: default: return fmt.Errorf("serialization method with style=%q and explode=%v is not supported by media type", sm.Style, sm.Explode) } return validateExtensions(ctx, encoding.Extensions) } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/errors.go ================================================ package openapi3 import ( "bytes" "errors" ) // MultiError is a collection of errors, intended for when // multiple issues need to be reported upstream type MultiError []error func (me MultiError) Error() string { return spliceErr(" | ", me) } func spliceErr(sep string, errs []error) string { buff := &bytes.Buffer{} for i, e := range errs { buff.WriteString(e.Error()) if i != len(errs)-1 { buff.WriteString(sep) } } return buff.String() } // Is allows you to determine if a generic error is in fact a MultiError using `errors.Is()` // It will also return true if any of the contained errors match target func (me MultiError) Is(target error) bool { if _, ok := target.(MultiError); ok { return true } for _, e := range me { if errors.Is(e, target) { return true } } return false } // As allows you to use `errors.As()` to set target to the first error within the multi error that matches the target type func (me MultiError) As(target any) bool { for _, e := range me { if errors.As(e, target) { return true } } return false } type multiErrorForOneOf MultiError func (meo multiErrorForOneOf) Error() string { return spliceErr(" Or ", meo) } func (meo multiErrorForOneOf) Unwrap() error { return MultiError(meo) } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/example.go ================================================ package openapi3 import ( "context" "encoding/json" "errors" ) // Example is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#example-object type Example struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` Value any `json:"value,omitempty" yaml:"value,omitempty"` ExternalValue string `json:"externalValue,omitempty" yaml:"externalValue,omitempty"` } func NewExample(value any) *Example { return &Example{Value: value} } // MarshalJSON returns the JSON encoding of Example. func (example Example) MarshalJSON() ([]byte, error) { x, err := example.MarshalYAML() if err != nil { return nil, err } return json.Marshal(x) } // MarshalYAML returns the YAML encoding of Example. func (example Example) MarshalYAML() (any, error) { m := make(map[string]any, 4+len(example.Extensions)) for k, v := range example.Extensions { m[k] = v } if x := example.Summary; x != "" { m["summary"] = x } if x := example.Description; x != "" { m["description"] = x } if x := example.Value; x != nil { m["value"] = x } if x := example.ExternalValue; x != "" { m["externalValue"] = x } return m, nil } // UnmarshalJSON sets Example to a copy of data. func (example *Example) UnmarshalJSON(data []byte) error { type ExampleBis Example var x ExampleBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, originKey) delete(x.Extensions, "summary") delete(x.Extensions, "description") delete(x.Extensions, "value") delete(x.Extensions, "externalValue") if len(x.Extensions) == 0 { x.Extensions = nil } *example = Example(x) return nil } // Validate returns an error if Example does not comply with the OpenAPI spec. func (example *Example) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) if example.Value != nil && example.ExternalValue != "" { return errors.New("value and externalValue are mutually exclusive") } if example.Value == nil && example.ExternalValue == "" { return errors.New("no value or externalValue field") } return validateExtensions(ctx, example.Extensions) } // UnmarshalJSON sets Examples to a copy of data. func (examples *Examples) UnmarshalJSON(data []byte) (err error) { *examples, _, err = unmarshalStringMapP[ExampleRef](data) return } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/example_validation.go ================================================ package openapi3 import "context" func validateExampleValue(ctx context.Context, input any, schema *Schema) error { opts := make([]SchemaValidationOption, 0, 2) if vo := getValidationOptions(ctx); vo.examplesValidationAsReq { opts = append(opts, VisitAsRequest()) } else if vo.examplesValidationAsRes { opts = append(opts, VisitAsResponse()) } opts = append(opts, MultiErrors()) return schema.VisitJSON(input, opts...) } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/extension.go ================================================ package openapi3 import ( "context" "fmt" "sort" "strings" ) func validateExtensions(ctx context.Context, extensions map[string]any) error { // FIXME: newtype + Validate(...) allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed var unknowns []string for k := range extensions { if strings.HasPrefix(k, "x-") { continue } if allowed != nil { if _, ok := allowed[k]; ok { continue } } unknowns = append(unknowns, k) } if len(unknowns) != 0 { sort.Strings(unknowns) return fmt.Errorf("extra sibling fields: %+v", unknowns) } return nil } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/external_docs.go ================================================ package openapi3 import ( "context" "encoding/json" "errors" "fmt" "net/url" ) // ExternalDocs is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#external-documentation-object type ExternalDocs struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` URL string `json:"url,omitempty" yaml:"url,omitempty"` } // MarshalJSON returns the JSON encoding of ExternalDocs. func (e ExternalDocs) MarshalJSON() ([]byte, error) { x, err := e.MarshalYAML() if err != nil { return nil, err } return json.Marshal(x) } // MarshalYAML returns the YAML encoding of ExternalDocs. func (e ExternalDocs) MarshalYAML() (any, error) { m := make(map[string]any, 2+len(e.Extensions)) for k, v := range e.Extensions { m[k] = v } if x := e.Description; x != "" { m["description"] = x } if x := e.URL; x != "" { m["url"] = x } return m, nil } // UnmarshalJSON sets ExternalDocs to a copy of data. func (e *ExternalDocs) UnmarshalJSON(data []byte) error { type ExternalDocsBis ExternalDocs var x ExternalDocsBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, originKey) delete(x.Extensions, "description") delete(x.Extensions, "url") if len(x.Extensions) == 0 { x.Extensions = nil } *e = ExternalDocs(x) return nil } // Validate returns an error if ExternalDocs does not comply with the OpenAPI spec. func (e *ExternalDocs) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) if e.URL == "" { return errors.New("url is required") } if _, err := url.Parse(e.URL); err != nil { return fmt.Errorf("url is incorrect: %w", err) } return validateExtensions(ctx, e.Extensions) } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/header.go ================================================ package openapi3 import ( "context" "errors" "fmt" "github.com/go-openapi/jsonpointer" ) // Header is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#header-object type Header struct { Parameter } var _ jsonpointer.JSONPointable = (*Header)(nil) // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (header Header) JSONLookup(token string) (any, error) { return header.Parameter.JSONLookup(token) } // MarshalJSON returns the JSON encoding of Header. func (header Header) MarshalJSON() ([]byte, error) { return header.Parameter.MarshalJSON() } // UnmarshalJSON sets Header to a copy of data. func (header *Header) UnmarshalJSON(data []byte) error { return header.Parameter.UnmarshalJSON(data) } // MarshalYAML returns the JSON encoding of Header. func (header Header) MarshalYAML() (any, error) { return header.Parameter, nil } // SerializationMethod returns a header's serialization method. func (header *Header) SerializationMethod() (*SerializationMethod, error) { style := header.Style if style == "" { style = SerializationSimple } explode := false if header.Explode != nil { explode = *header.Explode } return &SerializationMethod{Style: style, Explode: explode}, nil } // Validate returns an error if Header does not comply with the OpenAPI spec. func (header *Header) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) if header.Name != "" { return errors.New("header 'name' MUST NOT be specified, it is given in the corresponding headers map") } if header.In != "" { return errors.New("header 'in' MUST NOT be specified, it is implicitly in header") } // Validate a parameter's serialization method. sm, err := header.SerializationMethod() if err != nil { return err } if smSupported := false || sm.Style == SerializationSimple && !sm.Explode || sm.Style == SerializationSimple && sm.Explode; !smSupported { e := fmt.Errorf("serialization method with style=%q and explode=%v is not supported by a header parameter", sm.Style, sm.Explode) return fmt.Errorf("header schema is invalid: %w", e) } if (header.Schema == nil) == (len(header.Content) == 0) { e := fmt.Errorf("parameter must contain exactly one of content and schema: %v", header) return fmt.Errorf("header schema is invalid: %w", e) } if schema := header.Schema; schema != nil { if err := schema.Validate(ctx); err != nil { return fmt.Errorf("header schema is invalid: %w", err) } } if content := header.Content; content != nil { e := errors.New("parameter content must only contain one entry") if len(content) > 1 { return fmt.Errorf("header content is invalid: %w", e) } if err := content.Validate(ctx); err != nil { return fmt.Errorf("header content is invalid: %w", err) } } return nil } // UnmarshalJSON sets Headers to a copy of data. func (headers *Headers) UnmarshalJSON(data []byte) (err error) { *headers, _, err = unmarshalStringMapP[HeaderRef](data) return } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/helpers.go ================================================ package openapi3 import ( "fmt" "net/url" "path" "reflect" "regexp" "sort" "strings" "github.com/go-openapi/jsonpointer" ) const identifierChars = `a-zA-Z0-9._-` // IdentifierRegExp verifies whether Component object key matches contains just 'identifierChars', according to OpenAPI v3.x. // InvalidIdentifierCharRegExp matches all characters not contained in 'identifierChars'. // However, to be able supporting legacy OpenAPI v2.x, there is a need to customize above pattern in order not to fail // converted v2-v3 validation var ( IdentifierRegExp = regexp.MustCompile(`^[` + identifierChars + `]+$`) InvalidIdentifierCharRegExp = regexp.MustCompile(`[^` + identifierChars + `]`) ) // ValidateIdentifier returns an error if the given component name does not match [IdentifierRegExp]. func ValidateIdentifier(value string) error { if IdentifierRegExp.MatchString(value) { return nil } return fmt.Errorf("identifier %q is not supported by OpenAPIv3 standard (charset: [%q])", value, identifierChars) } // Float64Ptr is a helper for defining OpenAPI schemas. func Float64Ptr(value float64) *float64 { return &value } // BoolPtr is a helper for defining OpenAPI schemas. func BoolPtr(value bool) *bool { return &value } // Int64Ptr is a helper for defining OpenAPI schemas. func Int64Ptr(value int64) *int64 { return &value } // Uint64Ptr is a helper for defining OpenAPI schemas. func Uint64Ptr(value uint64) *uint64 { return &value } // componentNames returns the map keys in a sorted slice. func componentNames[E any](s map[string]E) []string { out := make([]string, 0, len(s)) for i := range s { out = append(out, i) } sort.Strings(out) return out } // copyURI makes a copy of the pointer. func copyURI(u *url.URL) *url.URL { if u == nil { return nil } c := *u // shallow-copy return &c } type ComponentRef interface { RefString() string RefPath() *url.URL CollectionName() string } // refersToSameDocument returns if the $ref refers to the same document. // // Documents in different directories will have distinct $ref values that resolve to // the same document. // For example, consider the 3 files: // // /records.yaml // /root.yaml $ref: records.yaml // /schema/other.yaml $ref: ../records.yaml // // The records.yaml reference in the 2 latter refers to the same document. func refersToSameDocument(o1 ComponentRef, o2 ComponentRef) bool { if o1 == nil || o2 == nil { return false } r1 := o1.RefPath() r2 := o2.RefPath() if r1 == nil || r2 == nil { return false } // refURL is relative to the working directory & base spec file. return referenceURIMatch(r1, r2) } // referencesRootDocument returns if the $ref points to the root document of the OpenAPI spec. // // If the document has no location, perhaps loaded from data in memory, it always returns false. func referencesRootDocument(doc *T, ref ComponentRef) bool { if doc.url == nil || ref == nil || ref.RefPath() == nil { return false } refURL := *ref.RefPath() refURL.Fragment = "" // Check referenced element was in the root document. return referenceURIMatch(doc.url, &refURL) } func referenceURIMatch(u1 *url.URL, u2 *url.URL) bool { s1, s2 := *u1, *u2 if s1.Scheme == "" { s1.Scheme = "file" } if s2.Scheme == "" { s2.Scheme = "file" } return s1.String() == s2.String() } // ReferencesComponentInRootDocument returns if the given component reference references // the same document or element as another component reference in the root document's // '#/components/'. If it does, it returns the name of it in the form // '#/components//NameXXX' // // Of course given a component from the root document will always match itself. // // https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#reference-object // https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#relative-references-in-urls // // Example. Take the spec with directory structure: // // openapi.yaml // schemas/ // ├─ record.yaml // ├─ records.yaml // // In openapi.yaml we have: // // components: // schemas: // Record: // $ref: schemas/record.yaml // // Case 1: records.yml references a component in the root document // // $ref: ../openapi.yaml#/components/schemas/Record // // This would return... // // #/components/schemas/Record // // Case 2: records.yml indirectly refers to the same schema // as a schema the root document's '#/components/schemas'. // // $ref: ./record.yaml // // This would also return... // // #/components/schemas/Record func ReferencesComponentInRootDocument(doc *T, ref ComponentRef) (string, bool) { if ref == nil || ref.RefString() == "" { return "", false } // Case 1: // Something like: ../another-folder/document.json#/myElement if isRemoteReference(ref.RefString()) && isRootComponentReference(ref.RefString(), ref.CollectionName()) { // Determine if it is *this* root doc. if referencesRootDocument(doc, ref) { _, name, _ := strings.Cut(ref.RefString(), path.Join("#/components/", ref.CollectionName())) return path.Join("#/components/", ref.CollectionName(), name), true } } // If there are no schemas defined in the root document return early. if doc.Components == nil { return "", false } collection, _, err := jsonpointer.GetForToken(doc.Components, ref.CollectionName()) if err != nil { panic(err) // unreachable } var components map[string]ComponentRef componentRefType := reflect.TypeOf(new(ComponentRef)).Elem() if t := reflect.TypeOf(collection); t.Kind() == reflect.Map && t.Key().Kind() == reflect.String && t.Elem().AssignableTo(componentRefType) { v := reflect.ValueOf(collection) components = make(map[string]ComponentRef, v.Len()) for _, key := range v.MapKeys() { strct := v.MapIndex(key) // Type assertion safe, already checked via reflection above. components[key.Interface().(string)] = strct.Interface().(ComponentRef) } } else { return "", false } // Case 2: // Something like: ../openapi.yaml#/components/schemas/myElement for name, s := range components { // Must be a reference to a YAML file. if !isWholeDocumentReference(s.RefString()) { continue } // Is the schema a ref to the same resource. if !refersToSameDocument(s, ref) { continue } // Transform the remote ref to the equivalent schema in the root document. return path.Join("#/components/", ref.CollectionName(), name), true } return "", false } // isElementReference takes a $ref value and checks if it references a specific element. func isElementReference(ref string) bool { return ref != "" && !isWholeDocumentReference(ref) } // isSchemaReference takes a $ref value and checks if it references a schema element. func isRootComponentReference(ref string, compType string) bool { return isElementReference(ref) && strings.Contains(ref, path.Join("#/components/", compType)) } // isWholeDocumentReference takes a $ref value and checks if it is whole document reference. func isWholeDocumentReference(ref string) bool { return ref != "" && !strings.ContainsAny(ref, "#") } // isRemoteReference takes a $ref value and checks if it is remote reference. func isRemoteReference(ref string) bool { return ref != "" && !strings.HasPrefix(ref, "#") && !isURLReference(ref) } // isURLReference takes a $ref value and checks if it is URL reference. func isURLReference(ref string) bool { return strings.HasPrefix(ref, "http://") || strings.HasPrefix(ref, "https://") || strings.HasPrefix(ref, "//") } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/info.go ================================================ package openapi3 import ( "context" "encoding/json" "errors" ) // Info is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#info-object type Info struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Title string `json:"title" yaml:"title"` // Required Description string `json:"description,omitempty" yaml:"description,omitempty"` TermsOfService string `json:"termsOfService,omitempty" yaml:"termsOfService,omitempty"` Contact *Contact `json:"contact,omitempty" yaml:"contact,omitempty"` License *License `json:"license,omitempty" yaml:"license,omitempty"` Version string `json:"version" yaml:"version"` // Required } // MarshalJSON returns the JSON encoding of Info. func (info Info) MarshalJSON() ([]byte, error) { x, err := info.MarshalYAML() if err != nil { return nil, err } return json.Marshal(x) } // MarshalYAML returns the YAML encoding of Info. func (info *Info) MarshalYAML() (any, error) { if info == nil { return nil, nil } m := make(map[string]any, 6+len(info.Extensions)) for k, v := range info.Extensions { m[k] = v } m["title"] = info.Title if x := info.Description; x != "" { m["description"] = x } if x := info.TermsOfService; x != "" { m["termsOfService"] = x } if x := info.Contact; x != nil { m["contact"] = x } if x := info.License; x != nil { m["license"] = x } m["version"] = info.Version return m, nil } // UnmarshalJSON sets Info to a copy of data. func (info *Info) UnmarshalJSON(data []byte) error { type InfoBis Info var x InfoBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, originKey) delete(x.Extensions, "title") delete(x.Extensions, "description") delete(x.Extensions, "termsOfService") delete(x.Extensions, "contact") delete(x.Extensions, "license") delete(x.Extensions, "version") if len(x.Extensions) == 0 { x.Extensions = nil } *info = Info(x) return nil } // Validate returns an error if Info does not comply with the OpenAPI spec. func (info *Info) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) if contact := info.Contact; contact != nil { if err := contact.Validate(ctx); err != nil { return err } } if license := info.License; license != nil { if err := license.Validate(ctx); err != nil { return err } } if info.Version == "" { return errors.New("value of version must be a non-empty string") } if info.Title == "" { return errors.New("value of title must be a non-empty string") } return validateExtensions(ctx, info.Extensions) } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/internalize_refs.go ================================================ package openapi3 import ( "context" "path" "strings" ) // RefNameResolver maps a component to an name that is used as it's internalized name. // // The function should avoid name collisions (i.e. be a injective mapping). // It must only contain characters valid for fixed field names: [IdentifierRegExp]. type RefNameResolver func(*T, ComponentRef) string // DefaultRefResolver is a default implementation of refNameResolver for the // InternalizeRefs function. // // The external reference is internalized to (hopefully) a unique name. If // the external reference matches (by path) to another reference in the root // document then the name of that component is used. // // The transformation involves: // - Cutting the "#/components/" part. // - Cutting the file extensions (.yaml/.json) from documents. // - Trimming the common directory with the root spec. // - Replace invalid characters with with underscores. // // This is an injective mapping over a "reasonable" amount of the possible openapi // spec domain space but is not perfect. There might be edge cases. func DefaultRefNameResolver(doc *T, ref ComponentRef) string { if ref.RefString() == "" || ref.RefPath() == nil { panic("unable to resolve reference to name") } name := ref.RefPath() // If refering to a component in the root spec, no need to internalize just use // the existing component. // XXX(percivalalb): since this function call is iterating over components behind the // scenes during an internalization call it actually starts interating over // new & replaced internalized components. This might caused some edge cases, // haven't found one yet but this might need to actually be used on a frozen copy // of doc. if nameInRoot, found := ReferencesComponentInRootDocument(doc, ref); found { nameInRoot = strings.TrimPrefix(nameInRoot, "#") rootCompURI := copyURI(doc.url) rootCompURI.Fragment = nameInRoot name = rootCompURI } filePath, componentPath := name.Path, name.Fragment // Cut out the "#/components/" to make the names shorter. // XXX(percivalalb): This might cause collisions but is worth the brevity. if b, a, ok := strings.Cut(componentPath, path.Join("components", ref.CollectionName(), "")); ok { componentPath = path.Join(b, a) } if filePath != "" { // If the path is the same as the root doc, just remove. if doc.url != nil && filePath == doc.url.Path { filePath = "" } // Remove the path extentions to make this JSON/YAML agnostic. for ext := path.Ext(filePath); len(ext) > 0; ext = path.Ext(filePath) { filePath = strings.TrimSuffix(filePath, ext) } // Trim the common prefix with the root doc path. if doc.url != nil { commonDir := path.Dir(doc.url.Path) for { if commonDir == "." { // no common prefix break } if p, found := cutDirectories(filePath, commonDir); found { filePath = p break } commonDir = path.Dir(commonDir) } } } var internalizedName string // Trim .'s & slashes from start e.g. otherwise ./doc.yaml would end up as __doc if filePath != "" { internalizedName = strings.TrimLeft(filePath, "./") } if componentPath != "" { if internalizedName != "" { internalizedName += "_" } internalizedName += strings.TrimLeft(componentPath, "./") } // Replace invalid characters in component fixed field names. internalizedName = InvalidIdentifierCharRegExp.ReplaceAllString(internalizedName, "_") return internalizedName } // cutDirectories removes the given directories from the start of the path if // the path is a child. func cutDirectories(p, dirs string) (string, bool) { if dirs == "" || p == "" { return p, false } p = strings.TrimRight(p, "/") dirs = strings.TrimRight(dirs, "/") var sb strings.Builder sb.Grow(len(ParameterInHeader)) for _, segments := range strings.Split(p, "/") { sb.WriteString(segments) if sb.String() == p { return strings.TrimPrefix(p, dirs), true } sb.WriteRune('/') } return p, false } func isExternalRef(ref string, parentIsExternal bool) bool { return ref != "" && (!strings.HasPrefix(ref, "#/components/") || parentIsExternal) } func (doc *T) addSchemaToSpec(s *SchemaRef, refNameResolver RefNameResolver, parentIsExternal bool) bool { if s == nil || !isExternalRef(s.Ref, parentIsExternal) { return false } name := refNameResolver(doc, s) if doc.Components != nil { if _, ok := doc.Components.Schemas[name]; ok { s.Ref = "#/components/schemas/" + name return true } } if doc.Components == nil { doc.Components = &Components{} } if doc.Components.Schemas == nil { doc.Components.Schemas = make(Schemas) } doc.Components.Schemas[name] = s.Value.NewRef() s.Ref = "#/components/schemas/" + name return true } func (doc *T) addParameterToSpec(p *ParameterRef, refNameResolver RefNameResolver, parentIsExternal bool) bool { if p == nil || !isExternalRef(p.Ref, parentIsExternal) { return false } name := refNameResolver(doc, p) if doc.Components != nil { if _, ok := doc.Components.Parameters[name]; ok { p.Ref = "#/components/parameters/" + name return true } } if doc.Components == nil { doc.Components = &Components{} } if doc.Components.Parameters == nil { doc.Components.Parameters = make(ParametersMap) } doc.Components.Parameters[name] = &ParameterRef{Value: p.Value} p.Ref = "#/components/parameters/" + name return true } func (doc *T) addHeaderToSpec(h *HeaderRef, refNameResolver RefNameResolver, parentIsExternal bool) bool { if h == nil || !isExternalRef(h.Ref, parentIsExternal) { return false } name := refNameResolver(doc, h) if doc.Components != nil { if _, ok := doc.Components.Headers[name]; ok { h.Ref = "#/components/headers/" + name return true } } if doc.Components == nil { doc.Components = &Components{} } if doc.Components.Headers == nil { doc.Components.Headers = make(Headers) } doc.Components.Headers[name] = &HeaderRef{Value: h.Value} h.Ref = "#/components/headers/" + name return true } func (doc *T) addRequestBodyToSpec(r *RequestBodyRef, refNameResolver RefNameResolver, parentIsExternal bool) bool { if r == nil || !isExternalRef(r.Ref, parentIsExternal) { return false } name := refNameResolver(doc, r) if doc.Components != nil { if _, ok := doc.Components.RequestBodies[name]; ok { r.Ref = "#/components/requestBodies/" + name return true } } if doc.Components == nil { doc.Components = &Components{} } if doc.Components.RequestBodies == nil { doc.Components.RequestBodies = make(RequestBodies) } doc.Components.RequestBodies[name] = &RequestBodyRef{Value: r.Value} r.Ref = "#/components/requestBodies/" + name return true } func (doc *T) addResponseToSpec(r *ResponseRef, refNameResolver RefNameResolver, parentIsExternal bool) bool { if r == nil || !isExternalRef(r.Ref, parentIsExternal) { return false } name := refNameResolver(doc, r) if doc.Components != nil { if _, ok := doc.Components.Responses[name]; ok { r.Ref = "#/components/responses/" + name return true } } if doc.Components == nil { doc.Components = &Components{} } if doc.Components.Responses == nil { doc.Components.Responses = make(ResponseBodies) } doc.Components.Responses[name] = &ResponseRef{Value: r.Value} r.Ref = "#/components/responses/" + name return true } func (doc *T) addSecuritySchemeToSpec(ss *SecuritySchemeRef, refNameResolver RefNameResolver, parentIsExternal bool) { if ss == nil || !isExternalRef(ss.Ref, parentIsExternal) { return } name := refNameResolver(doc, ss) if doc.Components != nil { if _, ok := doc.Components.SecuritySchemes[name]; ok { ss.Ref = "#/components/securitySchemes/" + name return } } if doc.Components == nil { doc.Components = &Components{} } if doc.Components.SecuritySchemes == nil { doc.Components.SecuritySchemes = make(SecuritySchemes) } doc.Components.SecuritySchemes[name] = &SecuritySchemeRef{Value: ss.Value} ss.Ref = "#/components/securitySchemes/" + name } func (doc *T) addExampleToSpec(e *ExampleRef, refNameResolver RefNameResolver, parentIsExternal bool) { if e == nil || !isExternalRef(e.Ref, parentIsExternal) { return } name := refNameResolver(doc, e) if doc.Components != nil { if _, ok := doc.Components.Examples[name]; ok { e.Ref = "#/components/examples/" + name return } } if doc.Components == nil { doc.Components = &Components{} } if doc.Components.Examples == nil { doc.Components.Examples = make(Examples) } doc.Components.Examples[name] = &ExampleRef{Value: e.Value} e.Ref = "#/components/examples/" + name } func (doc *T) addLinkToSpec(l *LinkRef, refNameResolver RefNameResolver, parentIsExternal bool) { if l == nil || !isExternalRef(l.Ref, parentIsExternal) { return } name := refNameResolver(doc, l) if doc.Components != nil { if _, ok := doc.Components.Links[name]; ok { l.Ref = "#/components/links/" + name return } } if doc.Components == nil { doc.Components = &Components{} } if doc.Components.Links == nil { doc.Components.Links = make(Links) } doc.Components.Links[name] = &LinkRef{Value: l.Value} l.Ref = "#/components/links/" + name } func (doc *T) addCallbackToSpec(c *CallbackRef, refNameResolver RefNameResolver, parentIsExternal bool) bool { if c == nil || !isExternalRef(c.Ref, parentIsExternal) { return false } name := refNameResolver(doc, c) if doc.Components == nil { doc.Components = &Components{} } if doc.Components.Callbacks == nil { doc.Components.Callbacks = make(Callbacks) } c.Ref = "#/components/callbacks/" + name doc.Components.Callbacks[name] = &CallbackRef{Value: c.Value} return true } func (doc *T) derefSchema(s *Schema, refNameResolver RefNameResolver, parentIsExternal bool) { if s == nil || doc.isVisitedSchema(s) { return } for _, list := range []SchemaRefs{s.AllOf, s.AnyOf, s.OneOf} { for _, s2 := range list { isExternal := doc.addSchemaToSpec(s2, refNameResolver, parentIsExternal) if s2 != nil { doc.derefSchema(s2.Value, refNameResolver, isExternal || parentIsExternal) } } } for _, name := range componentNames(s.Properties) { s2 := s.Properties[name] isExternal := doc.addSchemaToSpec(s2, refNameResolver, parentIsExternal) if s2 != nil { doc.derefSchema(s2.Value, refNameResolver, isExternal || parentIsExternal) } } for _, ref := range []*SchemaRef{s.Not, s.AdditionalProperties.Schema, s.Items} { isExternal := doc.addSchemaToSpec(ref, refNameResolver, parentIsExternal) if ref != nil { doc.derefSchema(ref.Value, refNameResolver, isExternal || parentIsExternal) } } } func (doc *T) derefHeaders(hs Headers, refNameResolver RefNameResolver, parentIsExternal bool) { for _, name := range componentNames(hs) { h := hs[name] isExternal := doc.addHeaderToSpec(h, refNameResolver, parentIsExternal) if doc.isVisitedHeader(h.Value) { continue } doc.derefParameter(h.Value.Parameter, refNameResolver, parentIsExternal || isExternal) } } func (doc *T) derefExamples(es Examples, refNameResolver RefNameResolver, parentIsExternal bool) { for _, name := range componentNames(es) { e := es[name] doc.addExampleToSpec(e, refNameResolver, parentIsExternal) } } func (doc *T) derefContent(c Content, refNameResolver RefNameResolver, parentIsExternal bool) { for _, name := range componentNames(c) { mediatype := c[name] isExternal := doc.addSchemaToSpec(mediatype.Schema, refNameResolver, parentIsExternal) if mediatype.Schema != nil { doc.derefSchema(mediatype.Schema.Value, refNameResolver, isExternal || parentIsExternal) } doc.derefExamples(mediatype.Examples, refNameResolver, parentIsExternal) for _, name := range componentNames(mediatype.Encoding) { e := mediatype.Encoding[name] doc.derefHeaders(e.Headers, refNameResolver, parentIsExternal) } } } func (doc *T) derefLinks(ls Links, refNameResolver RefNameResolver, parentIsExternal bool) { for _, name := range componentNames(ls) { l := ls[name] doc.addLinkToSpec(l, refNameResolver, parentIsExternal) } } func (doc *T) derefResponse(r *ResponseRef, refNameResolver RefNameResolver, parentIsExternal bool) { isExternal := doc.addResponseToSpec(r, refNameResolver, parentIsExternal) if v := r.Value; v != nil { doc.derefHeaders(v.Headers, refNameResolver, isExternal || parentIsExternal) doc.derefContent(v.Content, refNameResolver, isExternal || parentIsExternal) doc.derefLinks(v.Links, refNameResolver, isExternal || parentIsExternal) } } func (doc *T) derefResponses(rs *Responses, refNameResolver RefNameResolver, parentIsExternal bool) { doc.derefResponseBodies(rs.Map(), refNameResolver, parentIsExternal) } func (doc *T) derefResponseBodies(es ResponseBodies, refNameResolver RefNameResolver, parentIsExternal bool) { for _, name := range componentNames(es) { e := es[name] doc.derefResponse(e, refNameResolver, parentIsExternal) } } func (doc *T) derefParameter(p Parameter, refNameResolver RefNameResolver, parentIsExternal bool) { isExternal := doc.addSchemaToSpec(p.Schema, refNameResolver, parentIsExternal) doc.derefContent(p.Content, refNameResolver, parentIsExternal) if p.Schema != nil { doc.derefSchema(p.Schema.Value, refNameResolver, isExternal || parentIsExternal) } } func (doc *T) derefRequestBody(r RequestBody, refNameResolver RefNameResolver, parentIsExternal bool) { doc.derefContent(r.Content, refNameResolver, parentIsExternal) } func (doc *T) derefPaths(paths map[string]*PathItem, refNameResolver RefNameResolver, parentIsExternal bool) { for _, name := range componentNames(paths) { ops := paths[name] pathIsExternal := isExternalRef(ops.Ref, parentIsExternal) // inline full operations ops.Ref = "" for _, param := range ops.Parameters { isExternal := doc.addParameterToSpec(param, refNameResolver, pathIsExternal) if param.Value != nil { doc.derefParameter(*param.Value, refNameResolver, pathIsExternal || isExternal) } } opsWithMethod := ops.Operations() for _, name := range componentNames(opsWithMethod) { op := opsWithMethod[name] isExternal := doc.addRequestBodyToSpec(op.RequestBody, refNameResolver, pathIsExternal) if op.RequestBody != nil && op.RequestBody.Value != nil { doc.derefRequestBody(*op.RequestBody.Value, refNameResolver, pathIsExternal || isExternal) } for _, name := range componentNames(op.Callbacks) { cb := op.Callbacks[name] isExternal := doc.addCallbackToSpec(cb, refNameResolver, pathIsExternal) if cb.Value != nil { cbValue := (*cb.Value).Map() doc.derefPaths(cbValue, refNameResolver, pathIsExternal || isExternal) } } doc.derefResponses(op.Responses, refNameResolver, pathIsExternal) for _, param := range op.Parameters { isExternal := doc.addParameterToSpec(param, refNameResolver, pathIsExternal) if param.Value != nil { doc.derefParameter(*param.Value, refNameResolver, pathIsExternal || isExternal) } } } } } // InternalizeRefs removes all references to external files from the spec and moves them // to the components section. // // refNameResolver takes in references to returns a name to store the reference under locally. // It MUST return a unique name for each reference type. // A default implementation is provided that will suffice for most use cases. See the function // documentation for more details. // // Example: // // doc.InternalizeRefs(context.Background(), nil) func (doc *T) InternalizeRefs(ctx context.Context, refNameResolver func(*T, ComponentRef) string) { doc.resetVisited() if refNameResolver == nil { refNameResolver = DefaultRefNameResolver } if components := doc.Components; components != nil { for _, name := range componentNames(components.Schemas) { schema := components.Schemas[name] isExternal := doc.addSchemaToSpec(schema, refNameResolver, false) if schema != nil { schema.Ref = "" // always dereference the top level doc.derefSchema(schema.Value, refNameResolver, isExternal) } } for _, name := range componentNames(components.Parameters) { p := components.Parameters[name] isExternal := doc.addParameterToSpec(p, refNameResolver, false) if p != nil && p.Value != nil { p.Ref = "" // always dereference the top level doc.derefParameter(*p.Value, refNameResolver, isExternal) } } doc.derefHeaders(components.Headers, refNameResolver, false) for _, name := range componentNames(components.RequestBodies) { req := components.RequestBodies[name] isExternal := doc.addRequestBodyToSpec(req, refNameResolver, false) if req != nil && req.Value != nil { req.Ref = "" // always dereference the top level doc.derefRequestBody(*req.Value, refNameResolver, isExternal) } } doc.derefResponseBodies(components.Responses, refNameResolver, false) for _, name := range componentNames(components.SecuritySchemes) { ss := components.SecuritySchemes[name] doc.addSecuritySchemeToSpec(ss, refNameResolver, false) } doc.derefExamples(components.Examples, refNameResolver, false) doc.derefLinks(components.Links, refNameResolver, false) for _, name := range componentNames(components.Callbacks) { cb := components.Callbacks[name] isExternal := doc.addCallbackToSpec(cb, refNameResolver, false) if cb != nil && cb.Value != nil { cb.Ref = "" // always dereference the top level cbValue := (*cb.Value).Map() doc.derefPaths(cbValue, refNameResolver, isExternal) } } } doc.derefPaths(doc.Paths.Map(), refNameResolver, false) } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/license.go ================================================ package openapi3 import ( "context" "encoding/json" "errors" ) // License is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#license-object type License struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Name string `json:"name" yaml:"name"` // Required URL string `json:"url,omitempty" yaml:"url,omitempty"` } // MarshalJSON returns the JSON encoding of License. func (license License) MarshalJSON() ([]byte, error) { x, err := license.MarshalYAML() if err != nil { return nil, err } return json.Marshal(x) } // MarshalYAML returns the YAML encoding of License. func (license License) MarshalYAML() (any, error) { m := make(map[string]any, 2+len(license.Extensions)) for k, v := range license.Extensions { m[k] = v } m["name"] = license.Name if x := license.URL; x != "" { m["url"] = x } return m, nil } // UnmarshalJSON sets License to a copy of data. func (license *License) UnmarshalJSON(data []byte) error { type LicenseBis License var x LicenseBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, originKey) delete(x.Extensions, "name") delete(x.Extensions, "url") if len(x.Extensions) == 0 { x.Extensions = nil } *license = License(x) return nil } // Validate returns an error if License does not comply with the OpenAPI spec. func (license *License) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) if license.Name == "" { return errors.New("value of license name must be a non-empty string") } return validateExtensions(ctx, license.Extensions) } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/link.go ================================================ package openapi3 import ( "context" "encoding/json" "errors" "fmt" ) // Link is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#link-object type Link struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` OperationRef string `json:"operationRef,omitempty" yaml:"operationRef,omitempty"` OperationID string `json:"operationId,omitempty" yaml:"operationId,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` Parameters map[string]any `json:"parameters,omitempty" yaml:"parameters,omitempty"` Server *Server `json:"server,omitempty" yaml:"server,omitempty"` RequestBody any `json:"requestBody,omitempty" yaml:"requestBody,omitempty"` } // MarshalJSON returns the JSON encoding of Link. func (link Link) MarshalJSON() ([]byte, error) { x, err := link.MarshalYAML() if err != nil { return nil, err } return json.Marshal(x) } // MarshalYAML returns the YAML encoding of Link. func (link Link) MarshalYAML() (any, error) { m := make(map[string]any, 6+len(link.Extensions)) for k, v := range link.Extensions { m[k] = v } if x := link.OperationRef; x != "" { m["operationRef"] = x } if x := link.OperationID; x != "" { m["operationId"] = x } if x := link.Description; x != "" { m["description"] = x } if x := link.Parameters; len(x) != 0 { m["parameters"] = x } if x := link.Server; x != nil { m["server"] = x } if x := link.RequestBody; x != nil { m["requestBody"] = x } return m, nil } // UnmarshalJSON sets Link to a copy of data. func (link *Link) UnmarshalJSON(data []byte) error { type LinkBis Link var x LinkBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, originKey) delete(x.Extensions, "operationRef") delete(x.Extensions, "operationId") delete(x.Extensions, "description") delete(x.Extensions, "parameters") delete(x.Extensions, "server") delete(x.Extensions, "requestBody") if len(x.Extensions) == 0 { x.Extensions = nil } *link = Link(x) return nil } // Validate returns an error if Link does not comply with the OpenAPI spec. func (link *Link) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) if link.OperationID == "" && link.OperationRef == "" { return errors.New("missing operationId or operationRef on link") } if link.OperationID != "" && link.OperationRef != "" { return fmt.Errorf("operationId %q and operationRef %q are mutually exclusive", link.OperationID, link.OperationRef) } return validateExtensions(ctx, link.Extensions) } // UnmarshalJSON sets Links to a copy of data. func (links *Links) UnmarshalJSON(data []byte) (err error) { *links, _, err = unmarshalStringMapP[LinkRef](data) return } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/loader.go ================================================ package openapi3 import ( "context" "encoding/json" "errors" "fmt" "io" "net/url" "os" "path" "path/filepath" "reflect" "strconv" "strings" ) // IncludeOrigin specifies whether to include the origin of the OpenAPI elements // Set this to true before loading a spec to include the origin of the OpenAPI elements // Note it is global and affects all loaders var IncludeOrigin = false func foundUnresolvedRef(ref string) error { return fmt.Errorf("found unresolved ref: %q", ref) } func failedToResolveRefFragmentPart(value, what string) error { return fmt.Errorf("failed to resolve %q in fragment in URI: %q", what, value) } // Loader helps deserialize an OpenAPIv3 document type Loader struct { // IsExternalRefsAllowed enables visiting other files IsExternalRefsAllowed bool // ReadFromURIFunc allows overriding the any file/URL reading func ReadFromURIFunc ReadFromURIFunc Context context.Context rootDir string rootLocation string visitedPathItemRefs map[string]struct{} visitedDocuments map[string]*T visitedRefs map[string]struct{} visitedPath []string backtrack map[string][]func(value any) } // NewLoader returns an empty Loader func NewLoader() *Loader { return &Loader{ Context: context.Background(), } } func (loader *Loader) resetVisitedPathItemRefs() { loader.visitedPathItemRefs = make(map[string]struct{}) loader.visitedRefs = make(map[string]struct{}) loader.visitedPath = nil loader.backtrack = make(map[string][]func(value any)) } // LoadFromURI loads a spec from a remote URL func (loader *Loader) LoadFromURI(location *url.URL) (*T, error) { loader.resetVisitedPathItemRefs() return loader.loadFromURIInternal(location) } // LoadFromFile loads a spec from a local file path func (loader *Loader) LoadFromFile(location string) (*T, error) { loader.rootDir = path.Dir(location) return loader.LoadFromURI(&url.URL{Path: filepath.ToSlash(location)}) } func (loader *Loader) loadFromURIInternal(location *url.URL) (*T, error) { data, err := loader.readURL(location) if err != nil { return nil, err } return loader.loadFromDataWithPathInternal(data, location) } func (loader *Loader) allowsExternalRefs(ref string) (err error) { if !loader.IsExternalRefsAllowed { err = fmt.Errorf("encountered disallowed external reference: %q", ref) } return } func (loader *Loader) loadSingleElementFromURI(ref string, rootPath *url.URL, element any) (*url.URL, error) { if err := loader.allowsExternalRefs(ref); err != nil { return nil, err } resolvedPath, err := resolvePathWithRef(ref, rootPath) if err != nil { return nil, err } if frag := resolvedPath.Fragment; frag != "" { return nil, fmt.Errorf("unexpected ref fragment %q", frag) } data, err := loader.readURL(resolvedPath) if err != nil { return nil, err } if err := unmarshal(data, element, IncludeOrigin); err != nil { return nil, err } return resolvedPath, nil } func (loader *Loader) readURL(location *url.URL) ([]byte, error) { if f := loader.ReadFromURIFunc; f != nil { return f(loader, location) } return DefaultReadFromURI(loader, location) } // LoadFromStdin loads a spec from stdin func (loader *Loader) LoadFromStdin() (*T, error) { return loader.LoadFromIoReader(os.Stdin) } // LoadFromStdin loads a spec from io.Reader func (loader *Loader) LoadFromIoReader(reader io.Reader) (*T, error) { if reader == nil { return nil, fmt.Errorf("invalid reader: %v", reader) } data, err := io.ReadAll(reader) if err != nil { return nil, err } return loader.LoadFromData(data) } // LoadFromData loads a spec from a byte array func (loader *Loader) LoadFromData(data []byte) (*T, error) { loader.resetVisitedPathItemRefs() doc := &T{} if err := unmarshal(data, doc, IncludeOrigin); err != nil { return nil, err } if err := loader.ResolveRefsIn(doc, nil); err != nil { return nil, err } return doc, nil } // LoadFromDataWithPath takes the OpenAPI document data in bytes and a path where the resolver can find referred // elements and returns a *T with all resolved data or an error if unable to load data or resolve refs. func (loader *Loader) LoadFromDataWithPath(data []byte, location *url.URL) (*T, error) { loader.resetVisitedPathItemRefs() return loader.loadFromDataWithPathInternal(data, location) } func (loader *Loader) loadFromDataWithPathInternal(data []byte, location *url.URL) (*T, error) { if loader.visitedDocuments == nil { loader.visitedDocuments = make(map[string]*T) loader.rootLocation = location.Path } uri := location.String() if doc, ok := loader.visitedDocuments[uri]; ok { return doc, nil } doc := &T{} loader.visitedDocuments[uri] = doc if err := unmarshal(data, doc, IncludeOrigin); err != nil { return nil, err } doc.url = copyURI(location) if err := loader.ResolveRefsIn(doc, location); err != nil { return nil, err } return doc, nil } // ResolveRefsIn expands references if for instance spec was just unmarshaled func (loader *Loader) ResolveRefsIn(doc *T, location *url.URL) (err error) { if loader.Context == nil { loader.Context = context.Background() } if loader.visitedPathItemRefs == nil { loader.resetVisitedPathItemRefs() } if components := doc.Components; components != nil { for _, name := range componentNames(components.Headers) { component := components.Headers[name] if err = loader.resolveHeaderRef(doc, component, location); err != nil { return } } for _, name := range componentNames(components.Parameters) { component := components.Parameters[name] if err = loader.resolveParameterRef(doc, component, location); err != nil { return } } for _, name := range componentNames(components.RequestBodies) { component := components.RequestBodies[name] if err = loader.resolveRequestBodyRef(doc, component, location); err != nil { return } } for _, name := range componentNames(components.Responses) { component := components.Responses[name] if err = loader.resolveResponseRef(doc, component, location); err != nil { return } } for _, name := range componentNames(components.Schemas) { component := components.Schemas[name] if err = loader.resolveSchemaRef(doc, component, location, []string{}); err != nil { return } } for _, name := range componentNames(components.SecuritySchemes) { component := components.SecuritySchemes[name] if err = loader.resolveSecuritySchemeRef(doc, component, location); err != nil { return } } for _, name := range componentNames(components.Examples) { component := components.Examples[name] if err = loader.resolveExampleRef(doc, component, location); err != nil { return } } for _, name := range componentNames(components.Callbacks) { component := components.Callbacks[name] if err = loader.resolveCallbackRef(doc, component, location); err != nil { return } } } // Visit all operations pathItems := doc.Paths.Map() for _, name := range componentNames(pathItems) { pathItem := pathItems[name] if pathItem == nil { continue } if err = loader.resolvePathItemRef(doc, pathItem, location); err != nil { return } } return } func join(basePath *url.URL, relativePath *url.URL) *url.URL { if basePath == nil { return relativePath } newPath := *basePath newPath.Path = path.Join(path.Dir(newPath.Path), relativePath.Path) return &newPath } func resolvePath(basePath *url.URL, componentPath *url.URL) *url.URL { if is_file(componentPath) { // support absolute paths if filepath.IsAbs(componentPath.Path) { return componentPath } return join(basePath, componentPath) } return componentPath } func resolvePathWithRef(ref string, rootPath *url.URL) (*url.URL, error) { parsedURL, err := url.Parse(ref) if err != nil { return nil, fmt.Errorf("cannot parse reference: %q: %w", ref, err) } resolvedPath := resolvePath(rootPath, parsedURL) resolvedPath.Fragment = parsedURL.Fragment return resolvedPath, nil } func (loader *Loader) resolveRefPath(ref string, path *url.URL) (*url.URL, error) { if ref != "" && ref[0] == '#' { path = copyURI(path) // Resolving internal refs of a doc loaded from memory // has no path, so just set the Fragment. if path == nil { path = new(url.URL) } path.Fragment = ref return path, nil } if err := loader.allowsExternalRefs(ref); err != nil { return nil, err } resolvedPath, err := resolvePathWithRef(ref, path) if err != nil { return nil, err } return resolvedPath, nil } func isSingleRefElement(ref string) bool { return !strings.Contains(ref, "#") } func (loader *Loader) visitRef(ref string) { if loader.visitedRefs == nil { loader.visitedRefs = make(map[string]struct{}) loader.backtrack = make(map[string][]func(value any)) } loader.visitedPath = append(loader.visitedPath, ref) loader.visitedRefs[ref] = struct{}{} } func (loader *Loader) unvisitRef(ref string, value any) { if value != nil { for _, fn := range loader.backtrack[ref] { fn(value) } } delete(loader.visitedRefs, ref) delete(loader.backtrack, ref) loader.visitedPath = loader.visitedPath[:len(loader.visitedPath)-1] } func (loader *Loader) shouldVisitRef(ref string, fn func(value any)) bool { if _, ok := loader.visitedRefs[ref]; ok { loader.backtrack[ref] = append(loader.backtrack[ref], fn) return false } return true } func (loader *Loader) resolveComponent(doc *T, ref string, path *url.URL, resolved any) ( componentDoc *T, componentPath *url.URL, err error, ) { if componentDoc, ref, componentPath, err = loader.resolveRefAndDocument(doc, ref, path); err != nil { return nil, nil, err } parsedURL, err := url.Parse(ref) if err != nil { return nil, nil, fmt.Errorf("cannot parse reference: %q: %v", ref, parsedURL) } fragment := parsedURL.Fragment if fragment == "" { fragment = "/" } if fragment[0] != '/' { return nil, nil, fmt.Errorf("expected fragment prefix '#/' in URI %q", ref) } drill := func(cursor any) (any, error) { for _, pathPart := range strings.Split(fragment[1:], "/") { pathPart = unescapeRefString(pathPart) attempted := false switch c := cursor.(type) { // Special case of T // See issue856: a ref to doc => we assume that doc is a T => things live in T.Extensions case *T: if pathPart == "" { cursor = c.Extensions attempted = true } // Special case due to multijson case *SchemaRef: if pathPart == "additionalProperties" { if ap := c.Value.AdditionalProperties.Has; ap != nil { cursor = *ap } else { cursor = c.Value.AdditionalProperties.Schema } attempted = true } case *Responses: cursor = c.m // m map[string]*ResponseRef case *Callback: cursor = c.m // m map[string]*PathItem case *Paths: cursor = c.m // m map[string]*PathItem } if !attempted { if cursor, err = drillIntoField(cursor, pathPart); err != nil { e := failedToResolveRefFragmentPart(ref, pathPart) return nil, fmt.Errorf("%s: %w", e, err) } } if cursor == nil { return nil, failedToResolveRefFragmentPart(ref, pathPart) } } return cursor, nil } var cursor any if cursor, err = drill(componentDoc); err != nil { if path == nil { return nil, nil, err } var err2 error data, err2 := loader.readURL(path) if err2 != nil { return nil, nil, err } if err2 = unmarshal(data, &cursor, IncludeOrigin); err2 != nil { return nil, nil, err } if cursor, err2 = drill(cursor); err2 != nil || cursor == nil { return nil, nil, err } err = nil } setPathRef := func(target any) { if i, ok := target.(interface { setRefPath(*url.URL) }); ok { pathRef := copyURI(componentPath) // Resolving internal refs of a doc loaded from memory // has no path, so just set the Fragment. if pathRef == nil { pathRef = new(url.URL) } pathRef.Fragment = fragment i.setRefPath(pathRef) } } switch { case reflect.TypeOf(cursor) == reflect.TypeOf(resolved): setPathRef(cursor) reflect.ValueOf(resolved).Elem().Set(reflect.ValueOf(cursor).Elem()) return componentDoc, componentPath, nil case reflect.TypeOf(cursor) == reflect.TypeOf(map[string]any{}): codec := func(got, expect any) error { enc, err := json.Marshal(got) if err != nil { return err } if err = json.Unmarshal(enc, expect); err != nil { return err } setPathRef(expect) return nil } if err := codec(cursor, resolved); err != nil { return nil, nil, fmt.Errorf("bad data in %q (expecting %s)", ref, readableType(resolved)) } return componentDoc, componentPath, nil default: return nil, nil, fmt.Errorf("bad data in %q (expecting %s)", ref, readableType(resolved)) } } func readableType(x any) string { switch x.(type) { case *Callback: return "callback object" case *CallbackRef: return "ref to callback object" case *ExampleRef: return "ref to example object" case *HeaderRef: return "ref to header object" case *LinkRef: return "ref to link object" case *ParameterRef: return "ref to parameter object" case *PathItem: return "pathItem object" case *RequestBodyRef: return "ref to requestBody object" case *ResponseRef: return "ref to response object" case *SchemaRef: return "ref to schema object" case *SecuritySchemeRef: return "ref to securityScheme object" default: panic(fmt.Sprintf("unreachable %T", x)) } } func drillIntoField(cursor any, fieldName string) (any, error) { switch val := reflect.Indirect(reflect.ValueOf(cursor)); val.Kind() { case reflect.Map: elementValue := val.MapIndex(reflect.ValueOf(fieldName)) if !elementValue.IsValid() { return nil, fmt.Errorf("map key %q not found", fieldName) } return elementValue.Interface(), nil case reflect.Slice: i, err := strconv.ParseUint(fieldName, 10, 32) if err != nil { return nil, err } index := int(i) if 0 > index || index >= val.Len() { return nil, errors.New("slice index out of bounds") } return val.Index(index).Interface(), nil case reflect.Struct: hasFields := false for i := 0; i < val.NumField(); i++ { hasFields = true if yamlTag := val.Type().Field(i).Tag.Get("yaml"); yamlTag != "-" { if tagName := strings.Split(yamlTag, ",")[0]; tagName != "" { if fieldName == tagName { return val.Field(i).Interface(), nil } } } } // if cursor is a "ref wrapper" struct (e.g. RequestBodyRef), if _, ok := val.Type().FieldByName("Value"); ok { // try digging into its Value field return drillIntoField(val.FieldByName("Value").Interface(), fieldName) } if hasFields { if ff := val.Type().Field(0); ff.PkgPath == "" && ff.Name == "Extensions" { extensions := val.Field(0).Interface().(map[string]any) if enc, ok := extensions[fieldName]; ok { return enc, nil } } } return nil, fmt.Errorf("struct field %q not found", fieldName) default: return nil, errors.New("not a map, slice nor struct") } } func (loader *Loader) resolveRefAndDocument(doc *T, ref string, path *url.URL) (*T, string, *url.URL, error) { if ref != "" && ref[0] == '#' { return doc, ref, path, nil } fragment, resolvedPath, err := loader.resolveRef(ref, path) if err != nil { return nil, "", nil, err } if doc, err = loader.loadFromURIInternal(resolvedPath); err != nil { return nil, "", nil, fmt.Errorf("error resolving reference %q: %w", ref, err) } return doc, fragment, resolvedPath, nil } func (loader *Loader) resolveRef(ref string, path *url.URL) (string, *url.URL, error) { resolvedPathRef, err := loader.resolveRefPath(ref, path) if err != nil { return "", nil, err } fragment := "#" + resolvedPathRef.Fragment resolvedPathRef.Fragment = "" return fragment, resolvedPathRef, nil } var ( errMUSTCallback = errors.New("invalid callback: value MUST be an object") errMUSTExample = errors.New("invalid example: value MUST be an object") errMUSTHeader = errors.New("invalid header: value MUST be an object") errMUSTLink = errors.New("invalid link: value MUST be an object") errMUSTParameter = errors.New("invalid parameter: value MUST be an object") errMUSTPathItem = errors.New("invalid path item: value MUST be an object") errMUSTRequestBody = errors.New("invalid requestBody: value MUST be an object") errMUSTResponse = errors.New("invalid response: value MUST be an object") errMUSTSchema = errors.New("invalid schema: value MUST be an object") errMUSTSecurityScheme = errors.New("invalid securityScheme: value MUST be an object") ) func (loader *Loader) resolveHeaderRef(doc *T, component *HeaderRef, documentPath *url.URL) (err error) { if component.isEmpty() { return errMUSTHeader } if ref := component.Ref; ref != "" { if component.Value != nil { return nil } if !loader.shouldVisitRef(ref, func(value any) { component.Value = value.(*Header) refPath, _ := loader.resolveRefPath(ref, documentPath) component.setRefPath(refPath) }) { return nil } loader.visitRef(ref) if isSingleRefElement(ref) { var header Header if documentPath, err = loader.loadSingleElementFromURI(ref, documentPath, &header); err != nil { return err } component.Value = &header component.setRefPath(documentPath) } else { var resolved HeaderRef doc, componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved) if err != nil { return err } if err := loader.resolveHeaderRef(doc, &resolved, componentPath); err != nil { if err == errMUSTHeader { return nil } return err } component.Value = resolved.Value component.setRefPath(resolved.RefPath()) } defer loader.unvisitRef(ref, component.Value) } value := component.Value if value == nil { return nil } if schema := value.Schema; schema != nil { if err := loader.resolveSchemaRef(doc, schema, documentPath, []string{}); err != nil { return err } } return nil } func (loader *Loader) resolveParameterRef(doc *T, component *ParameterRef, documentPath *url.URL) (err error) { if component.isEmpty() { return errMUSTParameter } if ref := component.Ref; ref != "" { if component.Value != nil { return nil } if !loader.shouldVisitRef(ref, func(value any) { component.Value = value.(*Parameter) refPath, _ := loader.resolveRefPath(ref, documentPath) component.setRefPath(refPath) }) { return nil } loader.visitRef(ref) if isSingleRefElement(ref) { var param Parameter if documentPath, err = loader.loadSingleElementFromURI(ref, documentPath, ¶m); err != nil { return err } component.Value = ¶m component.setRefPath(documentPath) } else { var resolved ParameterRef doc, componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved) if err != nil { return err } if err := loader.resolveParameterRef(doc, &resolved, componentPath); err != nil { if err == errMUSTParameter { return nil } return err } component.Value = resolved.Value component.setRefPath(resolved.RefPath()) } defer loader.unvisitRef(ref, component.Value) } value := component.Value if value == nil { return nil } if value.Content != nil && value.Schema != nil { return errors.New("cannot contain both schema and content in a parameter") } for _, name := range componentNames(value.Content) { contentType := value.Content[name] if schema := contentType.Schema; schema != nil { if err := loader.resolveSchemaRef(doc, schema, documentPath, []string{}); err != nil { return err } } } if schema := value.Schema; schema != nil { if err := loader.resolveSchemaRef(doc, schema, documentPath, []string{}); err != nil { return err } } return nil } func (loader *Loader) resolveRequestBodyRef(doc *T, component *RequestBodyRef, documentPath *url.URL) (err error) { if component.isEmpty() { return errMUSTRequestBody } if ref := component.Ref; ref != "" { if component.Value != nil { return nil } if !loader.shouldVisitRef(ref, func(value any) { component.Value = value.(*RequestBody) refPath, _ := loader.resolveRefPath(ref, documentPath) component.setRefPath(refPath) }) { return nil } loader.visitRef(ref) if isSingleRefElement(ref) { var requestBody RequestBody if documentPath, err = loader.loadSingleElementFromURI(ref, documentPath, &requestBody); err != nil { return err } component.Value = &requestBody component.setRefPath(documentPath) } else { var resolved RequestBodyRef doc, componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved) if err != nil { return err } if err = loader.resolveRequestBodyRef(doc, &resolved, componentPath); err != nil { if err == errMUSTRequestBody { return nil } return err } component.Value = resolved.Value component.setRefPath(resolved.RefPath()) } defer loader.unvisitRef(ref, component.Value) } value := component.Value if value == nil { return nil } for _, name := range componentNames(value.Content) { contentType := value.Content[name] if contentType == nil { continue } for _, name := range componentNames(contentType.Examples) { example := contentType.Examples[name] if err := loader.resolveExampleRef(doc, example, documentPath); err != nil { return err } contentType.Examples[name] = example } if schema := contentType.Schema; schema != nil { if err := loader.resolveSchemaRef(doc, schema, documentPath, []string{}); err != nil { return err } } } return nil } func (loader *Loader) resolveResponseRef(doc *T, component *ResponseRef, documentPath *url.URL) (err error) { if component.isEmpty() { return errMUSTResponse } if ref := component.Ref; ref != "" { if component.Value != nil { return nil } if !loader.shouldVisitRef(ref, func(value any) { component.Value = value.(*Response) refPath, _ := loader.resolveRefPath(ref, documentPath) component.setRefPath(refPath) }) { return nil } loader.visitRef(ref) if isSingleRefElement(ref) { var resp Response if documentPath, err = loader.loadSingleElementFromURI(ref, documentPath, &resp); err != nil { return err } component.Value = &resp component.setRefPath(documentPath) } else { var resolved ResponseRef doc, componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved) if err != nil { return err } if err := loader.resolveResponseRef(doc, &resolved, componentPath); err != nil { if err == errMUSTResponse { return nil } return err } component.Value = resolved.Value component.setRefPath(resolved.RefPath()) } defer loader.unvisitRef(ref, component.Value) } value := component.Value if value == nil { return nil } for _, name := range componentNames(value.Headers) { header := value.Headers[name] if err := loader.resolveHeaderRef(doc, header, documentPath); err != nil { return err } } for _, name := range componentNames(value.Content) { contentType := value.Content[name] if contentType == nil { continue } for _, name := range componentNames(contentType.Examples) { example := contentType.Examples[name] if err := loader.resolveExampleRef(doc, example, documentPath); err != nil { return err } contentType.Examples[name] = example } if schema := contentType.Schema; schema != nil { if err := loader.resolveSchemaRef(doc, schema, documentPath, []string{}); err != nil { return err } contentType.Schema = schema } } for _, name := range componentNames(value.Links) { link := value.Links[name] if err := loader.resolveLinkRef(doc, link, documentPath); err != nil { return err } } return nil } func (loader *Loader) resolveSchemaRef(doc *T, component *SchemaRef, documentPath *url.URL, visited []string) (err error) { if component.isEmpty() { return errMUSTSchema } if ref := component.Ref; ref != "" { if component.Value != nil { return nil } if !loader.shouldVisitRef(ref, func(value any) { component.Value = value.(*Schema) refPath, _ := loader.resolveRefPath(ref, documentPath) component.setRefPath(refPath) }) { return nil } loader.visitRef(ref) if isSingleRefElement(ref) { var schema Schema if documentPath, err = loader.loadSingleElementFromURI(ref, documentPath, &schema); err != nil { return err } component.Value = &schema component.setRefPath(documentPath) } else { var resolved SchemaRef doc, componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved) if err != nil { return err } if err := loader.resolveSchemaRef(doc, &resolved, componentPath, visited); err != nil { if err == errMUSTSchema { return nil } return err } component.Value = resolved.Value component.setRefPath(resolved.RefPath()) } defer loader.unvisitRef(ref, component.Value) } value := component.Value if value == nil { return nil } // ResolveRefs referred schemas if v := value.Items; v != nil { if err := loader.resolveSchemaRef(doc, v, documentPath, visited); err != nil { return err } } for _, name := range componentNames(value.Properties) { v := value.Properties[name] if err := loader.resolveSchemaRef(doc, v, documentPath, visited); err != nil { return err } } if v := value.AdditionalProperties.Schema; v != nil { if err := loader.resolveSchemaRef(doc, v, documentPath, visited); err != nil { return err } } if v := value.Not; v != nil { if err := loader.resolveSchemaRef(doc, v, documentPath, visited); err != nil { return err } } for _, v := range value.AllOf { if err := loader.resolveSchemaRef(doc, v, documentPath, visited); err != nil { return err } } for _, v := range value.AnyOf { if err := loader.resolveSchemaRef(doc, v, documentPath, visited); err != nil { return err } } for _, v := range value.OneOf { if err := loader.resolveSchemaRef(doc, v, documentPath, visited); err != nil { return err } } return nil } func (loader *Loader) resolveSecuritySchemeRef(doc *T, component *SecuritySchemeRef, documentPath *url.URL) (err error) { if component.isEmpty() { return errMUSTSecurityScheme } if ref := component.Ref; ref != "" { if component.Value != nil { return nil } if !loader.shouldVisitRef(ref, func(value any) { component.Value = value.(*SecurityScheme) refPath, _ := loader.resolveRefPath(ref, documentPath) component.setRefPath(refPath) }) { return nil } loader.visitRef(ref) if isSingleRefElement(ref) { var scheme SecurityScheme if _, err = loader.loadSingleElementFromURI(ref, documentPath, &scheme); err != nil { return err } component.Value = &scheme component.setRefPath(documentPath) } else { var resolved SecuritySchemeRef doc, componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved) if err != nil { return err } if err := loader.resolveSecuritySchemeRef(doc, &resolved, componentPath); err != nil { if err == errMUSTSecurityScheme { return nil } return err } component.Value = resolved.Value component.setRefPath(resolved.RefPath()) } defer loader.unvisitRef(ref, component.Value) } return nil } func (loader *Loader) resolveExampleRef(doc *T, component *ExampleRef, documentPath *url.URL) (err error) { if ref := component.Ref; ref != "" { if component.Value != nil { return nil } if !loader.shouldVisitRef(ref, func(value any) { component.Value = value.(*Example) refPath, _ := loader.resolveRefPath(ref, documentPath) component.setRefPath(refPath) }) { return nil } loader.visitRef(ref) if isSingleRefElement(ref) { var example Example if _, err = loader.loadSingleElementFromURI(ref, documentPath, &example); err != nil { return err } component.Value = &example component.setRefPath(documentPath) } else { var resolved ExampleRef doc, componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved) if err != nil { return err } if err := loader.resolveExampleRef(doc, &resolved, componentPath); err != nil { if err == errMUSTExample { return nil } return err } component.Value = resolved.Value component.setRefPath(resolved.RefPath()) } defer loader.unvisitRef(ref, component.Value) } return nil } func (loader *Loader) resolveCallbackRef(doc *T, component *CallbackRef, documentPath *url.URL) (err error) { if component.isEmpty() { return errMUSTCallback } if ref := component.Ref; ref != "" { if component.Value != nil { return nil } if !loader.shouldVisitRef(ref, func(value any) { component.Value = value.(*Callback) refPath, _ := loader.resolveRefPath(ref, documentPath) component.setRefPath(refPath) }) { return nil } loader.visitRef(ref) if isSingleRefElement(ref) { var resolved Callback if documentPath, err = loader.loadSingleElementFromURI(ref, documentPath, &resolved); err != nil { return err } component.Value = &resolved component.setRefPath(documentPath) } else { var resolved CallbackRef doc, componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved) if err != nil { return err } if err = loader.resolveCallbackRef(doc, &resolved, componentPath); err != nil { if err == errMUSTCallback { return nil } return err } component.Value = resolved.Value component.setRefPath(resolved.RefPath()) } defer loader.unvisitRef(ref, component.Value) } value := component.Value if value == nil { return nil } pathItems := value.Map() for _, name := range componentNames(pathItems) { pathItem := pathItems[name] if err = loader.resolvePathItemRef(doc, pathItem, documentPath); err != nil { return err } } return nil } func (loader *Loader) resolveLinkRef(doc *T, component *LinkRef, documentPath *url.URL) (err error) { if component.isEmpty() { return errMUSTLink } if ref := component.Ref; ref != "" { if component.Value != nil { return nil } if !loader.shouldVisitRef(ref, func(value any) { component.Value = value.(*Link) refPath, _ := loader.resolveRefPath(ref, documentPath) component.setRefPath(refPath) }) { return nil } loader.visitRef(ref) if isSingleRefElement(ref) { var link Link if _, err = loader.loadSingleElementFromURI(ref, documentPath, &link); err != nil { return err } component.Value = &link component.setRefPath(documentPath) } else { var resolved LinkRef doc, componentPath, err := loader.resolveComponent(doc, ref, documentPath, &resolved) if err != nil { return err } if err := loader.resolveLinkRef(doc, &resolved, componentPath); err != nil { if err == errMUSTLink { return nil } return err } component.Value = resolved.Value component.setRefPath(resolved.RefPath()) } defer loader.unvisitRef(ref, component.Value) } return nil } func (loader *Loader) resolvePathItemRef(doc *T, pathItem *PathItem, documentPath *url.URL) (err error) { if pathItem == nil { err = errMUSTPathItem return } if ref := pathItem.Ref; ref != "" { if !pathItem.isEmpty() { return } if !loader.shouldVisitRef(ref, func(value any) { *pathItem = *value.(*PathItem) }) { return nil } loader.visitRef(ref) if isSingleRefElement(ref) { var p PathItem if documentPath, err = loader.loadSingleElementFromURI(ref, documentPath, &p); err != nil { return } *pathItem = p } else { var resolved PathItem if doc, documentPath, err = loader.resolveComponent(doc, ref, documentPath, &resolved); err != nil { if err == errMUSTPathItem { return nil } return } *pathItem = resolved } pathItem.Ref = ref defer loader.unvisitRef(ref, pathItem) } for _, parameter := range pathItem.Parameters { if err = loader.resolveParameterRef(doc, parameter, documentPath); err != nil { return } } operations := pathItem.Operations() for _, name := range componentNames(operations) { operation := operations[name] for _, parameter := range operation.Parameters { if err = loader.resolveParameterRef(doc, parameter, documentPath); err != nil { return } } if requestBody := operation.RequestBody; requestBody != nil { if err = loader.resolveRequestBodyRef(doc, requestBody, documentPath); err != nil { return } } responses := operation.Responses.Map() for _, name := range componentNames(responses) { response := responses[name] if err = loader.resolveResponseRef(doc, response, documentPath); err != nil { return } } for _, name := range componentNames(operation.Callbacks) { callback := operation.Callbacks[name] if err = loader.resolveCallbackRef(doc, callback, documentPath); err != nil { return } } } return } func unescapeRefString(ref string) string { return strings.Replace(strings.Replace(ref, "~1", "/", -1), "~0", "~", -1) } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/loader_uri_reader.go ================================================ package openapi3 import ( "errors" "fmt" "io" "net/http" "net/url" "os" "path/filepath" "sync" ) // ReadFromURIFunc defines a function which reads the contents of a resource // located at a URI. type ReadFromURIFunc func(loader *Loader, url *url.URL) ([]byte, error) var uriMu = &sync.RWMutex{} // ErrURINotSupported indicates the ReadFromURIFunc does not know how to handle a // given URI. var ErrURINotSupported = errors.New("unsupported URI") // ReadFromURIs returns a ReadFromURIFunc which tries to read a URI using the // given reader functions, in the same order. If a reader function does not // support the URI and returns ErrURINotSupported, the next function is checked // until a match is found, or the URI is not supported by any. func ReadFromURIs(readers ...ReadFromURIFunc) ReadFromURIFunc { return func(loader *Loader, url *url.URL) ([]byte, error) { for i := range readers { buf, err := readers[i](loader, url) if err == ErrURINotSupported { continue } else if err != nil { return nil, err } return buf, nil } return nil, ErrURINotSupported } } // DefaultReadFromURI returns a caching ReadFromURIFunc which can read remote // HTTP URIs and local file URIs. var DefaultReadFromURI = URIMapCache(ReadFromURIs(ReadFromHTTP(http.DefaultClient), ReadFromFile)) // ReadFromHTTP returns a ReadFromURIFunc which uses the given http.Client to // read the contents from a remote HTTP URI. This client may be customized to // implement timeouts, RFC 7234 caching, etc. func ReadFromHTTP(cl *http.Client) ReadFromURIFunc { return func(loader *Loader, location *url.URL) ([]byte, error) { if location.Scheme == "" || location.Host == "" { return nil, ErrURINotSupported } req, err := http.NewRequest("GET", location.String(), nil) if err != nil { return nil, err } resp, err := cl.Do(req) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode > 399 { return nil, fmt.Errorf("error loading %q: request returned status code %d", location.String(), resp.StatusCode) } return io.ReadAll(resp.Body) } } func is_file(location *url.URL) bool { return location.Path != "" && location.Host == "" && (location.Scheme == "" || location.Scheme == "file") } // ReadFromFile is a ReadFromURIFunc which reads local file URIs. func ReadFromFile(loader *Loader, location *url.URL) ([]byte, error) { if !is_file(location) { return nil, ErrURINotSupported } return os.ReadFile(filepath.FromSlash(location.Path)) } // URIMapCache returns a ReadFromURIFunc that caches the contents read from URI // locations in a simple map. This cache implementation is suitable for // short-lived processes such as command-line tools which process OpenAPI // documents. func URIMapCache(reader ReadFromURIFunc) ReadFromURIFunc { cache := map[string][]byte{} return func(loader *Loader, location *url.URL) (buf []byte, err error) { if location.Scheme == "" || location.Scheme == "file" { if !filepath.IsAbs(location.Path) { // Do not cache relative file paths; this can cause trouble if // the current working directory changes when processing // multiple top-level documents. return reader(loader, location) } } uri := location.String() var ok bool uriMu.RLock() if buf, ok = cache[uri]; ok { uriMu.RUnlock() return } uriMu.RUnlock() if buf, err = reader(loader, location); err != nil { return } uriMu.Lock() defer uriMu.Unlock() cache[uri] = buf return } } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/maplike.go ================================================ package openapi3 import ( "encoding/json" "sort" "strings" "github.com/go-openapi/jsonpointer" ) // NewResponsesWithCapacity builds a responses object of the given capacity. func NewResponsesWithCapacity(cap int) *Responses { if cap == 0 { return &Responses{m: make(map[string]*ResponseRef)} } return &Responses{m: make(map[string]*ResponseRef, cap)} } // Value returns the responses for key or nil func (responses *Responses) Value(key string) *ResponseRef { if responses.Len() == 0 { return nil } return responses.m[key] } // Set adds or replaces key 'key' of 'responses' with 'value'. // Note: 'responses' MUST be non-nil func (responses *Responses) Set(key string, value *ResponseRef) { if responses.m == nil { responses.m = make(map[string]*ResponseRef) } responses.m[key] = value } // Len returns the amount of keys in responses excluding responses.Extensions. func (responses *Responses) Len() int { if responses == nil || responses.m == nil { return 0 } return len(responses.m) } // Delete removes the entry associated with key 'key' from 'responses'. func (responses *Responses) Delete(key string) { if responses != nil && responses.m != nil { delete(responses.m, key) } } // Map returns responses as a 'map'. // Note: iteration on Go maps is not ordered. func (responses *Responses) Map() (m map[string]*ResponseRef) { if responses == nil || len(responses.m) == 0 { return make(map[string]*ResponseRef) } m = make(map[string]*ResponseRef, len(responses.m)) for k, v := range responses.m { m[k] = v } return } var _ jsonpointer.JSONPointable = (*Responses)(nil) // JSONLookup implements https://github.com/go-openapi/jsonpointer#JSONPointable func (responses Responses) JSONLookup(token string) (any, error) { if v := responses.Value(token); v == nil { vv, _, err := jsonpointer.GetForToken(responses.Extensions, token) return vv, err } else if ref := v.Ref; ref != "" { return &Ref{Ref: ref}, nil } else { var vv *Response = v.Value return vv, nil } } // MarshalYAML returns the YAML encoding of Responses. func (responses *Responses) MarshalYAML() (any, error) { if responses == nil { return nil, nil } m := make(map[string]any, responses.Len()+len(responses.Extensions)) for k, v := range responses.Extensions { m[k] = v } for k, v := range responses.Map() { m[k] = v } return m, nil } // MarshalJSON returns the JSON encoding of Responses. func (responses *Responses) MarshalJSON() ([]byte, error) { responsesYaml, err := responses.MarshalYAML() if err != nil { return nil, err } return json.Marshal(responsesYaml) } // UnmarshalJSON sets Responses to a copy of data. func (responses *Responses) UnmarshalJSON(data []byte) (err error) { var m map[string]any if err = json.Unmarshal(data, &m); err != nil { return } ks := make([]string, 0, len(m)) for k := range m { ks = append(ks, k) } sort.Strings(ks) x := Responses{ Extensions: make(map[string]any), m: make(map[string]*ResponseRef, len(m)), } for _, k := range ks { v := m[k] if strings.HasPrefix(k, "x-") { x.Extensions[k] = v continue } if k == originKey { var data []byte if data, err = json.Marshal(v); err != nil { return } if err = json.Unmarshal(data, &x.Origin); err != nil { return } continue } var data []byte if data, err = json.Marshal(v); err != nil { return } var vv ResponseRef if err = vv.UnmarshalJSON(data); err != nil { return } x.m[k] = &vv } *responses = x return } // NewCallbackWithCapacity builds a callback object of the given capacity. func NewCallbackWithCapacity(cap int) *Callback { if cap == 0 { return &Callback{m: make(map[string]*PathItem)} } return &Callback{m: make(map[string]*PathItem, cap)} } // Value returns the callback for key or nil func (callback *Callback) Value(key string) *PathItem { if callback.Len() == 0 { return nil } return callback.m[key] } // Set adds or replaces key 'key' of 'callback' with 'value'. // Note: 'callback' MUST be non-nil func (callback *Callback) Set(key string, value *PathItem) { if callback.m == nil { callback.m = make(map[string]*PathItem) } callback.m[key] = value } // Len returns the amount of keys in callback excluding callback.Extensions. func (callback *Callback) Len() int { if callback == nil || callback.m == nil { return 0 } return len(callback.m) } // Delete removes the entry associated with key 'key' from 'callback'. func (callback *Callback) Delete(key string) { if callback != nil && callback.m != nil { delete(callback.m, key) } } // Map returns callback as a 'map'. // Note: iteration on Go maps is not ordered. func (callback *Callback) Map() (m map[string]*PathItem) { if callback == nil || len(callback.m) == 0 { return make(map[string]*PathItem) } m = make(map[string]*PathItem, len(callback.m)) for k, v := range callback.m { m[k] = v } return } var _ jsonpointer.JSONPointable = (*Callback)(nil) // JSONLookup implements https://github.com/go-openapi/jsonpointer#JSONPointable func (callback Callback) JSONLookup(token string) (any, error) { if v := callback.Value(token); v == nil { vv, _, err := jsonpointer.GetForToken(callback.Extensions, token) return vv, err } else if ref := v.Ref; ref != "" { return &Ref{Ref: ref}, nil } else { var vv *PathItem = v return vv, nil } } // MarshalYAML returns the YAML encoding of Callback. func (callback *Callback) MarshalYAML() (any, error) { if callback == nil { return nil, nil } m := make(map[string]any, callback.Len()+len(callback.Extensions)) for k, v := range callback.Extensions { m[k] = v } for k, v := range callback.Map() { m[k] = v } return m, nil } // MarshalJSON returns the JSON encoding of Callback. func (callback *Callback) MarshalJSON() ([]byte, error) { callbackYaml, err := callback.MarshalYAML() if err != nil { return nil, err } return json.Marshal(callbackYaml) } // UnmarshalJSON sets Callback to a copy of data. func (callback *Callback) UnmarshalJSON(data []byte) (err error) { var m map[string]any if err = json.Unmarshal(data, &m); err != nil { return } ks := make([]string, 0, len(m)) for k := range m { ks = append(ks, k) } sort.Strings(ks) x := Callback{ Extensions: make(map[string]any), m: make(map[string]*PathItem, len(m)), } for _, k := range ks { v := m[k] if strings.HasPrefix(k, "x-") { x.Extensions[k] = v continue } if k == originKey { var data []byte if data, err = json.Marshal(v); err != nil { return } if err = json.Unmarshal(data, &x.Origin); err != nil { return } continue } var data []byte if data, err = json.Marshal(v); err != nil { return } var vv PathItem if err = vv.UnmarshalJSON(data); err != nil { return } x.m[k] = &vv } *callback = x return } // NewPathsWithCapacity builds a paths object of the given capacity. func NewPathsWithCapacity(cap int) *Paths { if cap == 0 { return &Paths{m: make(map[string]*PathItem)} } return &Paths{m: make(map[string]*PathItem, cap)} } // Value returns the paths for key or nil func (paths *Paths) Value(key string) *PathItem { if paths.Len() == 0 { return nil } return paths.m[key] } // Set adds or replaces key 'key' of 'paths' with 'value'. // Note: 'paths' MUST be non-nil func (paths *Paths) Set(key string, value *PathItem) { if paths.m == nil { paths.m = make(map[string]*PathItem) } paths.m[key] = value } // Len returns the amount of keys in paths excluding paths.Extensions. func (paths *Paths) Len() int { if paths == nil || paths.m == nil { return 0 } return len(paths.m) } // Delete removes the entry associated with key 'key' from 'paths'. func (paths *Paths) Delete(key string) { if paths != nil && paths.m != nil { delete(paths.m, key) } } // Map returns paths as a 'map'. // Note: iteration on Go maps is not ordered. func (paths *Paths) Map() (m map[string]*PathItem) { if paths == nil || len(paths.m) == 0 { return make(map[string]*PathItem) } m = make(map[string]*PathItem, len(paths.m)) for k, v := range paths.m { m[k] = v } return } var _ jsonpointer.JSONPointable = (*Paths)(nil) // JSONLookup implements https://github.com/go-openapi/jsonpointer#JSONPointable func (paths Paths) JSONLookup(token string) (any, error) { if v := paths.Value(token); v == nil { vv, _, err := jsonpointer.GetForToken(paths.Extensions, token) return vv, err } else if ref := v.Ref; ref != "" { return &Ref{Ref: ref}, nil } else { var vv *PathItem = v return vv, nil } } // MarshalYAML returns the YAML encoding of Paths. func (paths *Paths) MarshalYAML() (any, error) { if paths == nil { return nil, nil } m := make(map[string]any, paths.Len()+len(paths.Extensions)) for k, v := range paths.Extensions { m[k] = v } for k, v := range paths.Map() { m[k] = v } return m, nil } // MarshalJSON returns the JSON encoding of Paths. func (paths *Paths) MarshalJSON() ([]byte, error) { pathsYaml, err := paths.MarshalYAML() if err != nil { return nil, err } return json.Marshal(pathsYaml) } // UnmarshalJSON sets Paths to a copy of data. func (paths *Paths) UnmarshalJSON(data []byte) (err error) { var m map[string]any if err = json.Unmarshal(data, &m); err != nil { return } ks := make([]string, 0, len(m)) for k := range m { ks = append(ks, k) } sort.Strings(ks) x := Paths{ Extensions: make(map[string]any), m: make(map[string]*PathItem, len(m)), } for _, k := range ks { v := m[k] if strings.HasPrefix(k, "x-") { x.Extensions[k] = v continue } if k == originKey { var data []byte if data, err = json.Marshal(v); err != nil { return } if err = json.Unmarshal(data, &x.Origin); err != nil { return } continue } var data []byte if data, err = json.Marshal(v); err != nil { return } var vv PathItem if err = vv.UnmarshalJSON(data); err != nil { return } x.m[k] = &vv } *paths = x return } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/marsh.go ================================================ package openapi3 import ( "encoding/json" "fmt" "strings" "github.com/oasdiff/yaml" ) func unmarshalError(jsonUnmarshalErr error) error { if before, after, found := strings.Cut(jsonUnmarshalErr.Error(), "Bis"); found && before != "" && after != "" { before = strings.ReplaceAll(before, " Go struct ", " ") return fmt.Errorf("%s%s", before, strings.ReplaceAll(after, "Bis", "")) } return jsonUnmarshalErr } func unmarshal(data []byte, v any, includeOrigin bool) error { var jsonErr, yamlErr error // See https://github.com/getkin/kin-openapi/issues/680 if jsonErr = json.Unmarshal(data, v); jsonErr == nil { return nil } // UnmarshalStrict(data, v) TODO: investigate how ymlv3 handles duplicate map keys if yamlErr = yaml.UnmarshalWithOrigin(data, v, includeOrigin); yamlErr == nil { return nil } // If both unmarshaling attempts fail, return a new error that includes both errors return fmt.Errorf("failed to unmarshal data: json error: %v, yaml error: %v", jsonErr, yamlErr) } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/media_type.go ================================================ package openapi3 import ( "context" "encoding/json" "errors" "fmt" "sort" "github.com/go-openapi/jsonpointer" ) // MediaType is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#media-type-object type MediaType struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Schema *SchemaRef `json:"schema,omitempty" yaml:"schema,omitempty"` Example any `json:"example,omitempty" yaml:"example,omitempty"` Examples Examples `json:"examples,omitempty" yaml:"examples,omitempty"` Encoding map[string]*Encoding `json:"encoding,omitempty" yaml:"encoding,omitempty"` } var _ jsonpointer.JSONPointable = (*MediaType)(nil) func NewMediaType() *MediaType { return &MediaType{} } func (mediaType *MediaType) WithSchema(schema *Schema) *MediaType { if schema == nil { mediaType.Schema = nil } else { mediaType.Schema = &SchemaRef{Value: schema} } return mediaType } func (mediaType *MediaType) WithSchemaRef(schema *SchemaRef) *MediaType { mediaType.Schema = schema return mediaType } func (mediaType *MediaType) WithExample(name string, value any) *MediaType { example := mediaType.Examples if example == nil { example = make(map[string]*ExampleRef) mediaType.Examples = example } example[name] = &ExampleRef{ Value: NewExample(value), } return mediaType } func (mediaType *MediaType) WithEncoding(name string, enc *Encoding) *MediaType { encoding := mediaType.Encoding if encoding == nil { encoding = make(map[string]*Encoding) mediaType.Encoding = encoding } encoding[name] = enc return mediaType } // MarshalJSON returns the JSON encoding of MediaType. func (mediaType MediaType) MarshalJSON() ([]byte, error) { x, err := mediaType.MarshalYAML() if err != nil { return nil, err } return json.Marshal(x) } // MarshalYAML returns the YAML encoding of MediaType. func (mediaType MediaType) MarshalYAML() (any, error) { m := make(map[string]any, 4+len(mediaType.Extensions)) for k, v := range mediaType.Extensions { m[k] = v } if x := mediaType.Schema; x != nil { m["schema"] = x } if x := mediaType.Example; x != nil { m["example"] = x } if x := mediaType.Examples; len(x) != 0 { m["examples"] = x } if x := mediaType.Encoding; len(x) != 0 { m["encoding"] = x } return m, nil } // UnmarshalJSON sets MediaType to a copy of data. func (mediaType *MediaType) UnmarshalJSON(data []byte) error { type MediaTypeBis MediaType var x MediaTypeBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, originKey) delete(x.Extensions, "schema") delete(x.Extensions, "example") delete(x.Extensions, "examples") delete(x.Extensions, "encoding") if len(x.Extensions) == 0 { x.Extensions = nil } *mediaType = MediaType(x) return nil } // Validate returns an error if MediaType does not comply with the OpenAPI spec. func (mediaType *MediaType) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) if mediaType == nil { return nil } if schema := mediaType.Schema; schema != nil { if err := schema.Validate(ctx); err != nil { return err } if mediaType.Example != nil && mediaType.Examples != nil { return errors.New("example and examples are mutually exclusive") } if vo := getValidationOptions(ctx); !vo.examplesValidationDisabled { if example := mediaType.Example; example != nil { if err := validateExampleValue(ctx, example, schema.Value); err != nil { return fmt.Errorf("invalid example: %w", err) } } if examples := mediaType.Examples; examples != nil { names := make([]string, 0, len(examples)) for name := range examples { names = append(names, name) } sort.Strings(names) for _, k := range names { v := examples[k] if err := v.Validate(ctx); err != nil { return fmt.Errorf("example %s: %w", k, err) } if err := validateExampleValue(ctx, v.Value.Value, schema.Value); err != nil { return fmt.Errorf("example %s: %w", k, err) } } } } } return validateExtensions(ctx, mediaType.Extensions) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (mediaType MediaType) JSONLookup(token string) (any, error) { switch token { case "schema": if mediaType.Schema != nil { if mediaType.Schema.Ref != "" { return &Ref{Ref: mediaType.Schema.Ref}, nil } return mediaType.Schema.Value, nil } case "example": return mediaType.Example, nil case "examples": return mediaType.Examples, nil case "encoding": return mediaType.Encoding, nil } v, _, err := jsonpointer.GetForToken(mediaType.Extensions, token) return v, err } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/openapi3.go ================================================ package openapi3 import ( "context" "encoding/json" "errors" "fmt" "net/url" "github.com/go-openapi/jsonpointer" ) // T is the root of an OpenAPI v3 document // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#openapi-object type T struct { Extensions map[string]any `json:"-" yaml:"-"` OpenAPI string `json:"openapi" yaml:"openapi"` // Required Components *Components `json:"components,omitempty" yaml:"components,omitempty"` Info *Info `json:"info" yaml:"info"` // Required Paths *Paths `json:"paths" yaml:"paths"` // Required Security SecurityRequirements `json:"security,omitempty" yaml:"security,omitempty"` Servers Servers `json:"servers,omitempty" yaml:"servers,omitempty"` Tags Tags `json:"tags,omitempty" yaml:"tags,omitempty"` ExternalDocs *ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` visited visitedComponent url *url.URL } var _ jsonpointer.JSONPointable = (*T)(nil) // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (doc *T) JSONLookup(token string) (any, error) { switch token { case "openapi": return doc.OpenAPI, nil case "components": return doc.Components, nil case "info": return doc.Info, nil case "paths": return doc.Paths, nil case "security": return doc.Security, nil case "servers": return doc.Servers, nil case "tags": return doc.Tags, nil case "externalDocs": return doc.ExternalDocs, nil } v, _, err := jsonpointer.GetForToken(doc.Extensions, token) return v, err } // MarshalJSON returns the JSON encoding of T. func (doc *T) MarshalJSON() ([]byte, error) { x, err := doc.MarshalYAML() if err != nil { return nil, err } return json.Marshal(x) } // MarshalYAML returns the YAML encoding of T. func (doc *T) MarshalYAML() (any, error) { if doc == nil { return nil, nil } m := make(map[string]any, 4+len(doc.Extensions)) for k, v := range doc.Extensions { m[k] = v } m["openapi"] = doc.OpenAPI if x := doc.Components; x != nil { m["components"] = x } m["info"] = doc.Info m["paths"] = doc.Paths if x := doc.Security; len(x) != 0 { m["security"] = x } if x := doc.Servers; len(x) != 0 { m["servers"] = x } if x := doc.Tags; len(x) != 0 { m["tags"] = x } if x := doc.ExternalDocs; x != nil { m["externalDocs"] = x } return m, nil } // UnmarshalJSON sets T to a copy of data. func (doc *T) UnmarshalJSON(data []byte) error { type TBis T var x TBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, "openapi") delete(x.Extensions, "components") delete(x.Extensions, "info") delete(x.Extensions, "paths") delete(x.Extensions, "security") delete(x.Extensions, "servers") delete(x.Extensions, "tags") delete(x.Extensions, "externalDocs") if len(x.Extensions) == 0 { x.Extensions = nil } *doc = T(x) return nil } func (doc *T) AddOperation(path string, method string, operation *Operation) { if doc.Paths == nil { doc.Paths = NewPaths() } pathItem := doc.Paths.Value(path) if pathItem == nil { pathItem = &PathItem{} doc.Paths.Set(path, pathItem) } pathItem.SetOperation(method, operation) } func (doc *T) AddServer(server *Server) { doc.Servers = append(doc.Servers, server) } func (doc *T) AddServers(servers ...*Server) { doc.Servers = append(doc.Servers, servers...) } // Validate returns an error if T does not comply with the OpenAPI spec. // Validations Options can be provided to modify the validation behavior. func (doc *T) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) if doc.OpenAPI == "" { return errors.New("value of openapi must be a non-empty string") } var wrap func(error) error wrap = func(e error) error { return fmt.Errorf("invalid components: %w", e) } if v := doc.Components; v != nil { if err := v.Validate(ctx); err != nil { return wrap(err) } } wrap = func(e error) error { return fmt.Errorf("invalid info: %w", e) } if v := doc.Info; v != nil { if err := v.Validate(ctx); err != nil { return wrap(err) } } else { return wrap(errors.New("must be an object")) } wrap = func(e error) error { return fmt.Errorf("invalid paths: %w", e) } if v := doc.Paths; v != nil { if err := v.Validate(ctx); err != nil { return wrap(err) } } else { return wrap(errors.New("must be an object")) } wrap = func(e error) error { return fmt.Errorf("invalid security: %w", e) } if v := doc.Security; v != nil { if err := v.Validate(ctx); err != nil { return wrap(err) } } wrap = func(e error) error { return fmt.Errorf("invalid servers: %w", e) } if v := doc.Servers; v != nil { if err := v.Validate(ctx); err != nil { return wrap(err) } } wrap = func(e error) error { return fmt.Errorf("invalid tags: %w", e) } if v := doc.Tags; v != nil { if err := v.Validate(ctx); err != nil { return wrap(err) } } wrap = func(e error) error { return fmt.Errorf("invalid external docs: %w", e) } if v := doc.ExternalDocs; v != nil { if err := v.Validate(ctx); err != nil { return wrap(err) } } return validateExtensions(ctx, doc.Extensions) } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/operation.go ================================================ package openapi3 import ( "context" "encoding/json" "errors" "fmt" "strconv" "github.com/go-openapi/jsonpointer" ) // Operation represents "operation" specified by" OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object type Operation struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` // Optional tags for documentation. Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"` // Optional short summary. Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` // Optional description. Should use CommonMark syntax. Description string `json:"description,omitempty" yaml:"description,omitempty"` // Optional operation ID. OperationID string `json:"operationId,omitempty" yaml:"operationId,omitempty"` // Optional parameters. Parameters Parameters `json:"parameters,omitempty" yaml:"parameters,omitempty"` // Optional body parameter. RequestBody *RequestBodyRef `json:"requestBody,omitempty" yaml:"requestBody,omitempty"` // Responses. Responses *Responses `json:"responses" yaml:"responses"` // Required // Optional callbacks Callbacks Callbacks `json:"callbacks,omitempty" yaml:"callbacks,omitempty"` Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` // Optional security requirements that overrides top-level security. Security *SecurityRequirements `json:"security,omitempty" yaml:"security,omitempty"` // Optional servers that overrides top-level servers. Servers *Servers `json:"servers,omitempty" yaml:"servers,omitempty"` ExternalDocs *ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` } var _ jsonpointer.JSONPointable = (*Operation)(nil) func NewOperation() *Operation { return &Operation{} } // MarshalJSON returns the JSON encoding of Operation. func (operation Operation) MarshalJSON() ([]byte, error) { x, err := operation.MarshalYAML() if err != nil { return nil, err } return json.Marshal(x) } // MarshalYAML returns the YAML encoding of Operation. func (operation Operation) MarshalYAML() (any, error) { m := make(map[string]any, 12+len(operation.Extensions)) for k, v := range operation.Extensions { m[k] = v } if x := operation.Tags; len(x) != 0 { m["tags"] = x } if x := operation.Summary; x != "" { m["summary"] = x } if x := operation.Description; x != "" { m["description"] = x } if x := operation.OperationID; x != "" { m["operationId"] = x } if x := operation.Parameters; len(x) != 0 { m["parameters"] = x } if x := operation.RequestBody; x != nil { m["requestBody"] = x } m["responses"] = operation.Responses if x := operation.Callbacks; len(x) != 0 { m["callbacks"] = x } if x := operation.Deprecated; x { m["deprecated"] = x } if x := operation.Security; x != nil { m["security"] = x } if x := operation.Servers; x != nil { m["servers"] = x } if x := operation.ExternalDocs; x != nil { m["externalDocs"] = x } return m, nil } // UnmarshalJSON sets Operation to a copy of data. func (operation *Operation) UnmarshalJSON(data []byte) error { type OperationBis Operation var x OperationBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, originKey) delete(x.Extensions, "tags") delete(x.Extensions, "summary") delete(x.Extensions, "description") delete(x.Extensions, "operationId") delete(x.Extensions, "parameters") delete(x.Extensions, "requestBody") delete(x.Extensions, "responses") delete(x.Extensions, "callbacks") delete(x.Extensions, "deprecated") delete(x.Extensions, "security") delete(x.Extensions, "servers") delete(x.Extensions, "externalDocs") if len(x.Extensions) == 0 { x.Extensions = nil } *operation = Operation(x) return nil } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (operation Operation) JSONLookup(token string) (any, error) { switch token { case "requestBody": if operation.RequestBody != nil { if operation.RequestBody.Ref != "" { return &Ref{Ref: operation.RequestBody.Ref}, nil } return operation.RequestBody.Value, nil } case "tags": return operation.Tags, nil case "summary": return operation.Summary, nil case "description": return operation.Description, nil case "operationID": return operation.OperationID, nil case "parameters": return operation.Parameters, nil case "responses": return operation.Responses, nil case "callbacks": return operation.Callbacks, nil case "deprecated": return operation.Deprecated, nil case "security": return operation.Security, nil case "servers": return operation.Servers, nil case "externalDocs": return operation.ExternalDocs, nil } v, _, err := jsonpointer.GetForToken(operation.Extensions, token) return v, err } func (operation *Operation) AddParameter(p *Parameter) { operation.Parameters = append(operation.Parameters, &ParameterRef{Value: p}) } func (operation *Operation) AddResponse(status int, response *Response) { code := "default" if 0 < status && status < 1000 { code = strconv.FormatInt(int64(status), 10) } if operation.Responses == nil { operation.Responses = NewResponses() } operation.Responses.Set(code, &ResponseRef{Value: response}) } // Validate returns an error if Operation does not comply with the OpenAPI spec. func (operation *Operation) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) if v := operation.Parameters; v != nil { if err := v.Validate(ctx); err != nil { return err } } if v := operation.RequestBody; v != nil { if err := v.Validate(ctx); err != nil { return err } } if v := operation.Responses; v != nil { if err := v.Validate(ctx); err != nil { return err } } else { return errors.New("value of responses must be an object") } if v := operation.ExternalDocs; v != nil { if err := v.Validate(ctx); err != nil { return fmt.Errorf("invalid external docs: %w", err) } } return validateExtensions(ctx, operation.Extensions) } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/origin.go ================================================ package openapi3 const originKey = "__origin__" // Origin contains the origin of a collection. // Key is the location of the collection itself. // Fields is a map of the location of each field in the collection. type Origin struct { Key *Location `json:"key,omitempty" yaml:"key,omitempty"` Fields map[string]Location `json:"fields,omitempty" yaml:"fields,omitempty"` } // Location is a struct that contains the location of a field. type Location struct { Line int `json:"line,omitempty" yaml:"line,omitempty"` Column int `json:"column,omitempty" yaml:"column,omitempty"` } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/parameter.go ================================================ package openapi3 import ( "context" "encoding/json" "errors" "fmt" "sort" "strconv" "github.com/go-openapi/jsonpointer" ) // Parameters is specified by OpenAPI/Swagger 3.0 standard. type Parameters []*ParameterRef var _ jsonpointer.JSONPointable = (*Parameters)(nil) // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (p Parameters) JSONLookup(token string) (any, error) { index, err := strconv.Atoi(token) if err != nil { return nil, err } if index < 0 || index >= len(p) { return nil, fmt.Errorf("index %d out of bounds of array of length %d", index, len(p)) } ref := p[index] if ref != nil && ref.Ref != "" { return &Ref{Ref: ref.Ref}, nil } return ref.Value, nil } func NewParameters() Parameters { return make(Parameters, 0, 4) } func (parameters Parameters) GetByInAndName(in string, name string) *Parameter { for _, item := range parameters { if v := item.Value; v != nil { if v.Name == name && v.In == in { return v } } } return nil } // Validate returns an error if Parameters does not comply with the OpenAPI spec. func (parameters Parameters) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) dupes := make(map[string]struct{}) for _, parameterRef := range parameters { if v := parameterRef.Value; v != nil { key := v.In + ":" + v.Name if _, ok := dupes[key]; ok { return fmt.Errorf("more than one %q parameter has name %q", v.In, v.Name) } dupes[key] = struct{}{} } if err := parameterRef.Validate(ctx); err != nil { return err } } return nil } // Parameter is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#parameter-object type Parameter struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"` In string `json:"in,omitempty" yaml:"in,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` Style string `json:"style,omitempty" yaml:"style,omitempty"` Explode *bool `json:"explode,omitempty" yaml:"explode,omitempty"` AllowEmptyValue bool `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,omitempty"` AllowReserved bool `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"` Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` Required bool `json:"required,omitempty" yaml:"required,omitempty"` Schema *SchemaRef `json:"schema,omitempty" yaml:"schema,omitempty"` Example any `json:"example,omitempty" yaml:"example,omitempty"` Examples Examples `json:"examples,omitempty" yaml:"examples,omitempty"` Content Content `json:"content,omitempty" yaml:"content,omitempty"` } var _ jsonpointer.JSONPointable = (*Parameter)(nil) const ( ParameterInPath = "path" ParameterInQuery = "query" ParameterInHeader = "header" ParameterInCookie = "cookie" ) func NewPathParameter(name string) *Parameter { return &Parameter{ Name: name, In: ParameterInPath, Required: true, } } func NewQueryParameter(name string) *Parameter { return &Parameter{ Name: name, In: ParameterInQuery, } } func NewHeaderParameter(name string) *Parameter { return &Parameter{ Name: name, In: ParameterInHeader, } } func NewCookieParameter(name string) *Parameter { return &Parameter{ Name: name, In: ParameterInCookie, } } func (parameter *Parameter) WithDescription(value string) *Parameter { parameter.Description = value return parameter } func (parameter *Parameter) WithRequired(value bool) *Parameter { parameter.Required = value return parameter } func (parameter *Parameter) WithSchema(value *Schema) *Parameter { if value == nil { parameter.Schema = nil } else { parameter.Schema = &SchemaRef{ Value: value, } } return parameter } // MarshalJSON returns the JSON encoding of Parameter. func (parameter Parameter) MarshalJSON() ([]byte, error) { x, err := parameter.MarshalYAML() if err != nil { return nil, err } return json.Marshal(x) } // MarshalYAML returns the YAML encoding of Parameter. func (parameter Parameter) MarshalYAML() (any, error) { m := make(map[string]any, 13+len(parameter.Extensions)) for k, v := range parameter.Extensions { m[k] = v } if x := parameter.Name; x != "" { m["name"] = x } if x := parameter.In; x != "" { m["in"] = x } if x := parameter.Description; x != "" { m["description"] = x } if x := parameter.Style; x != "" { m["style"] = x } if x := parameter.Explode; x != nil { m["explode"] = x } if x := parameter.AllowEmptyValue; x { m["allowEmptyValue"] = x } if x := parameter.AllowReserved; x { m["allowReserved"] = x } if x := parameter.Deprecated; x { m["deprecated"] = x } if x := parameter.Required; x { m["required"] = x } if x := parameter.Schema; x != nil { m["schema"] = x } if x := parameter.Example; x != nil { m["example"] = x } if x := parameter.Examples; len(x) != 0 { m["examples"] = x } if x := parameter.Content; len(x) != 0 { m["content"] = x } return m, nil } // UnmarshalJSON sets Parameter to a copy of data. func (parameter *Parameter) UnmarshalJSON(data []byte) error { type ParameterBis Parameter var x ParameterBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, originKey) delete(x.Extensions, "name") delete(x.Extensions, "in") delete(x.Extensions, "description") delete(x.Extensions, "style") delete(x.Extensions, "explode") delete(x.Extensions, "allowEmptyValue") delete(x.Extensions, "allowReserved") delete(x.Extensions, "deprecated") delete(x.Extensions, "required") delete(x.Extensions, "schema") delete(x.Extensions, "example") delete(x.Extensions, "examples") delete(x.Extensions, "content") if len(x.Extensions) == 0 { x.Extensions = nil } *parameter = Parameter(x) return nil } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (parameter Parameter) JSONLookup(token string) (any, error) { switch token { case "schema": if parameter.Schema != nil { if parameter.Schema.Ref != "" { return &Ref{Ref: parameter.Schema.Ref}, nil } return parameter.Schema.Value, nil } case "name": return parameter.Name, nil case "in": return parameter.In, nil case "description": return parameter.Description, nil case "style": return parameter.Style, nil case "explode": return parameter.Explode, nil case "allowEmptyValue": return parameter.AllowEmptyValue, nil case "allowReserved": return parameter.AllowReserved, nil case "deprecated": return parameter.Deprecated, nil case "required": return parameter.Required, nil case "example": return parameter.Example, nil case "examples": return parameter.Examples, nil case "content": return parameter.Content, nil } v, _, err := jsonpointer.GetForToken(parameter.Extensions, token) return v, err } // SerializationMethod returns a parameter's serialization method. // When a parameter's serialization method is not defined the method returns // the default serialization method corresponding to a parameter's location. func (parameter *Parameter) SerializationMethod() (*SerializationMethod, error) { switch parameter.In { case ParameterInPath, ParameterInHeader: style := parameter.Style if style == "" { style = SerializationSimple } explode := false if parameter.Explode != nil { explode = *parameter.Explode } return &SerializationMethod{Style: style, Explode: explode}, nil case ParameterInQuery, ParameterInCookie: style := parameter.Style if style == "" { style = SerializationForm } explode := true if parameter.Explode != nil { explode = *parameter.Explode } return &SerializationMethod{Style: style, Explode: explode}, nil default: return nil, fmt.Errorf("unexpected parameter's 'in': %q", parameter.In) } } // Validate returns an error if Parameter does not comply with the OpenAPI spec. func (parameter *Parameter) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) if parameter.Name == "" { return errors.New("parameter name can't be blank") } in := parameter.In switch in { case ParameterInPath, ParameterInQuery, ParameterInHeader, ParameterInCookie: default: return fmt.Errorf("parameter can't have 'in' value %q", parameter.In) } if in == ParameterInPath && !parameter.Required { return fmt.Errorf("path parameter %q must be required", parameter.Name) } // Validate a parameter's serialization method. sm, err := parameter.SerializationMethod() if err != nil { return err } var smSupported bool switch { case parameter.In == ParameterInPath && sm.Style == SerializationSimple && !sm.Explode, parameter.In == ParameterInPath && sm.Style == SerializationSimple && sm.Explode, parameter.In == ParameterInPath && sm.Style == SerializationLabel && !sm.Explode, parameter.In == ParameterInPath && sm.Style == SerializationLabel && sm.Explode, parameter.In == ParameterInPath && sm.Style == SerializationMatrix && !sm.Explode, parameter.In == ParameterInPath && sm.Style == SerializationMatrix && sm.Explode, parameter.In == ParameterInQuery && sm.Style == SerializationForm && sm.Explode, parameter.In == ParameterInQuery && sm.Style == SerializationForm && !sm.Explode, parameter.In == ParameterInQuery && sm.Style == SerializationSpaceDelimited && sm.Explode, parameter.In == ParameterInQuery && sm.Style == SerializationSpaceDelimited && !sm.Explode, parameter.In == ParameterInQuery && sm.Style == SerializationPipeDelimited && sm.Explode, parameter.In == ParameterInQuery && sm.Style == SerializationPipeDelimited && !sm.Explode, parameter.In == ParameterInQuery && sm.Style == SerializationDeepObject && sm.Explode, parameter.In == ParameterInHeader && sm.Style == SerializationSimple && !sm.Explode, parameter.In == ParameterInHeader && sm.Style == SerializationSimple && sm.Explode, parameter.In == ParameterInCookie && sm.Style == SerializationForm && !sm.Explode, parameter.In == ParameterInCookie && sm.Style == SerializationForm && sm.Explode: smSupported = true } if !smSupported { e := fmt.Errorf("serialization method with style=%q and explode=%v is not supported by a %s parameter", sm.Style, sm.Explode, in) return fmt.Errorf("parameter %q schema is invalid: %w", parameter.Name, e) } if (parameter.Schema == nil) == (len(parameter.Content) == 0) { e := errors.New("parameter must contain exactly one of content and schema") return fmt.Errorf("parameter %q schema is invalid: %w", parameter.Name, e) } if content := parameter.Content; content != nil { e := errors.New("parameter content must only contain one entry") if len(content) > 1 { return fmt.Errorf("parameter %q content is invalid: %w", parameter.Name, e) } if err := content.Validate(ctx); err != nil { return fmt.Errorf("parameter %q content is invalid: %w", parameter.Name, err) } } if schema := parameter.Schema; schema != nil { if err := schema.Validate(ctx); err != nil { return fmt.Errorf("parameter %q schema is invalid: %w", parameter.Name, err) } if parameter.Example != nil && parameter.Examples != nil { return fmt.Errorf("parameter %q example and examples are mutually exclusive", parameter.Name) } if vo := getValidationOptions(ctx); vo.examplesValidationDisabled { return nil } if example := parameter.Example; example != nil { if err := validateExampleValue(ctx, example, schema.Value); err != nil { return fmt.Errorf("invalid example: %w", err) } } else if examples := parameter.Examples; examples != nil { names := make([]string, 0, len(examples)) for name := range examples { names = append(names, name) } sort.Strings(names) for _, k := range names { v := examples[k] if err := v.Validate(ctx); err != nil { return fmt.Errorf("%s: %w", k, err) } if err := validateExampleValue(ctx, v.Value.Value, schema.Value); err != nil { return fmt.Errorf("%s: %w", k, err) } } } } return validateExtensions(ctx, parameter.Extensions) } // UnmarshalJSON sets ParametersMap to a copy of data. func (parametersMap *ParametersMap) UnmarshalJSON(data []byte) (err error) { *parametersMap, _, err = unmarshalStringMapP[ParameterRef](data) return } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/path_item.go ================================================ package openapi3 import ( "context" "encoding/json" "fmt" "net/http" "sort" ) // PathItem is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#path-item-object type PathItem struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"` Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` Connect *Operation `json:"connect,omitempty" yaml:"connect,omitempty"` Delete *Operation `json:"delete,omitempty" yaml:"delete,omitempty"` Get *Operation `json:"get,omitempty" yaml:"get,omitempty"` Head *Operation `json:"head,omitempty" yaml:"head,omitempty"` Options *Operation `json:"options,omitempty" yaml:"options,omitempty"` Patch *Operation `json:"patch,omitempty" yaml:"patch,omitempty"` Post *Operation `json:"post,omitempty" yaml:"post,omitempty"` Put *Operation `json:"put,omitempty" yaml:"put,omitempty"` Trace *Operation `json:"trace,omitempty" yaml:"trace,omitempty"` Servers Servers `json:"servers,omitempty" yaml:"servers,omitempty"` Parameters Parameters `json:"parameters,omitempty" yaml:"parameters,omitempty"` } // MarshalJSON returns the JSON encoding of PathItem. func (pathItem PathItem) MarshalJSON() ([]byte, error) { x, err := pathItem.MarshalYAML() if err != nil { return nil, err } return json.Marshal(x) } // MarshalYAML returns the YAML encoding of PathItem. func (pathItem PathItem) MarshalYAML() (any, error) { if ref := pathItem.Ref; ref != "" { return Ref{Ref: ref}, nil } m := make(map[string]any, 13+len(pathItem.Extensions)) for k, v := range pathItem.Extensions { m[k] = v } if x := pathItem.Summary; x != "" { m["summary"] = x } if x := pathItem.Description; x != "" { m["description"] = x } if x := pathItem.Connect; x != nil { m["connect"] = x } if x := pathItem.Delete; x != nil { m["delete"] = x } if x := pathItem.Get; x != nil { m["get"] = x } if x := pathItem.Head; x != nil { m["head"] = x } if x := pathItem.Options; x != nil { m["options"] = x } if x := pathItem.Patch; x != nil { m["patch"] = x } if x := pathItem.Post; x != nil { m["post"] = x } if x := pathItem.Put; x != nil { m["put"] = x } if x := pathItem.Trace; x != nil { m["trace"] = x } if x := pathItem.Servers; len(x) != 0 { m["servers"] = x } if x := pathItem.Parameters; len(x) != 0 { m["parameters"] = x } return m, nil } // UnmarshalJSON sets PathItem to a copy of data. func (pathItem *PathItem) UnmarshalJSON(data []byte) error { type PathItemBis PathItem var x PathItemBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, originKey) delete(x.Extensions, "$ref") delete(x.Extensions, "summary") delete(x.Extensions, "description") delete(x.Extensions, "connect") delete(x.Extensions, "delete") delete(x.Extensions, "get") delete(x.Extensions, "head") delete(x.Extensions, "options") delete(x.Extensions, "patch") delete(x.Extensions, "post") delete(x.Extensions, "put") delete(x.Extensions, "trace") delete(x.Extensions, "servers") delete(x.Extensions, "parameters") if len(x.Extensions) == 0 { x.Extensions = nil } *pathItem = PathItem(x) return nil } func (pathItem *PathItem) Operations() map[string]*Operation { operations := make(map[string]*Operation) if v := pathItem.Connect; v != nil { operations[http.MethodConnect] = v } if v := pathItem.Delete; v != nil { operations[http.MethodDelete] = v } if v := pathItem.Get; v != nil { operations[http.MethodGet] = v } if v := pathItem.Head; v != nil { operations[http.MethodHead] = v } if v := pathItem.Options; v != nil { operations[http.MethodOptions] = v } if v := pathItem.Patch; v != nil { operations[http.MethodPatch] = v } if v := pathItem.Post; v != nil { operations[http.MethodPost] = v } if v := pathItem.Put; v != nil { operations[http.MethodPut] = v } if v := pathItem.Trace; v != nil { operations[http.MethodTrace] = v } return operations } func (pathItem *PathItem) GetOperation(method string) *Operation { switch method { case http.MethodConnect: return pathItem.Connect case http.MethodDelete: return pathItem.Delete case http.MethodGet: return pathItem.Get case http.MethodHead: return pathItem.Head case http.MethodOptions: return pathItem.Options case http.MethodPatch: return pathItem.Patch case http.MethodPost: return pathItem.Post case http.MethodPut: return pathItem.Put case http.MethodTrace: return pathItem.Trace default: panic(fmt.Errorf("unsupported HTTP method %q", method)) } } func (pathItem *PathItem) SetOperation(method string, operation *Operation) { switch method { case http.MethodConnect: pathItem.Connect = operation case http.MethodDelete: pathItem.Delete = operation case http.MethodGet: pathItem.Get = operation case http.MethodHead: pathItem.Head = operation case http.MethodOptions: pathItem.Options = operation case http.MethodPatch: pathItem.Patch = operation case http.MethodPost: pathItem.Post = operation case http.MethodPut: pathItem.Put = operation case http.MethodTrace: pathItem.Trace = operation default: panic(fmt.Errorf("unsupported HTTP method %q", method)) } } // Validate returns an error if PathItem does not comply with the OpenAPI spec. func (pathItem *PathItem) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) operations := pathItem.Operations() methods := make([]string, 0, len(operations)) for method := range operations { methods = append(methods, method) } sort.Strings(methods) for _, method := range methods { operation := operations[method] if err := operation.Validate(ctx); err != nil { return fmt.Errorf("invalid operation %s: %v", method, err) } } if v := pathItem.Parameters; v != nil { if err := v.Validate(ctx); err != nil { return err } } return validateExtensions(ctx, pathItem.Extensions) } // isEmpty's introduced in 546590b1 func (pathItem *PathItem) isEmpty() bool { // NOTE: ignores pathItem.Extensions // NOTE: ignores pathItem.Ref return pathItem.Summary == "" && pathItem.Description == "" && pathItem.Connect == nil && pathItem.Delete == nil && pathItem.Get == nil && pathItem.Head == nil && pathItem.Options == nil && pathItem.Patch == nil && pathItem.Post == nil && pathItem.Put == nil && pathItem.Trace == nil && len(pathItem.Servers) == 0 && len(pathItem.Parameters) == 0 } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/paths.go ================================================ package openapi3 import ( "context" "fmt" "sort" "strings" ) // Paths is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#paths-object type Paths struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` m map[string]*PathItem } // NewPaths builds a paths object with path items in insertion order. func NewPaths(opts ...NewPathsOption) *Paths { paths := NewPathsWithCapacity(len(opts)) for _, opt := range opts { opt(paths) } return paths } // NewPathsOption describes options to NewPaths func type NewPathsOption func(*Paths) // WithPath adds a named path item func WithPath(path string, pathItem *PathItem) NewPathsOption { return func(paths *Paths) { if p := pathItem; p != nil && path != "" { paths.Set(path, p) } } } // Validate returns an error if Paths does not comply with the OpenAPI spec. func (paths *Paths) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) normalizedPaths := make(map[string]string, paths.Len()) keys := make([]string, 0, paths.Len()) for key := range paths.Map() { keys = append(keys, key) } sort.Strings(keys) for _, path := range keys { pathItem := paths.Value(path) if path == "" || path[0] != '/' { return fmt.Errorf("path %q does not start with a forward slash (/)", path) } if pathItem == nil { pathItem = &PathItem{} paths.Set(path, pathItem) } normalizedPath, _, varsInPath := normalizeTemplatedPath(path) if oldPath, ok := normalizedPaths[normalizedPath]; ok { return fmt.Errorf("conflicting paths %q and %q", path, oldPath) } normalizedPaths[path] = path var commonParams []string for _, parameterRef := range pathItem.Parameters { if parameterRef != nil { if parameter := parameterRef.Value; parameter != nil && parameter.In == ParameterInPath { commonParams = append(commonParams, parameter.Name) } } } operations := pathItem.Operations() methods := make([]string, 0, len(operations)) for method := range operations { methods = append(methods, method) } sort.Strings(methods) for _, method := range methods { operation := operations[method] var setParams []string for _, parameterRef := range operation.Parameters { if parameterRef != nil { if parameter := parameterRef.Value; parameter != nil && parameter.In == ParameterInPath { setParams = append(setParams, parameter.Name) } } } if expected := len(setParams) + len(commonParams); expected != len(varsInPath) { expected -= len(varsInPath) if expected < 0 { expected *= -1 } missing := make(map[string]struct{}, expected) definedParams := append(setParams, commonParams...) for _, name := range definedParams { if _, ok := varsInPath[name]; !ok { missing[name] = struct{}{} } } for name := range varsInPath { got := false for _, othername := range definedParams { if othername == name { got = true break } } if !got { missing[name] = struct{}{} } } if len(missing) != 0 { missings := make([]string, 0, len(missing)) for name := range missing { missings = append(missings, name) } return fmt.Errorf("operation %s %s must define exactly all path parameters (missing: %v)", method, path, missings) } } } if err := pathItem.Validate(ctx); err != nil { return fmt.Errorf("invalid path %s: %v", path, err) } } if err := paths.validateUniqueOperationIDs(); err != nil { return err } return validateExtensions(ctx, paths.Extensions) } // InMatchingOrder returns paths in the order they are matched against URLs. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#paths-object // When matching URLs, concrete (non-templated) paths would be matched // before their templated counterparts. func (paths *Paths) InMatchingOrder() []string { // NOTE: sorting by number of variables ASC then by descending lexicographical // order seems to be a good heuristic. if paths.Len() == 0 { return nil } vars := make(map[int][]string) max := 0 for path := range paths.Map() { count := strings.Count(path, "}") vars[count] = append(vars[count], path) if count > max { max = count } } ordered := make([]string, 0, paths.Len()) for c := 0; c <= max; c++ { if ps, ok := vars[c]; ok { sort.Sort(sort.Reverse(sort.StringSlice(ps))) ordered = append(ordered, ps...) } } return ordered } // Find returns a path that matches the key. // // The method ignores differences in template variable names (except possible "*" suffix). // // For example: // // paths := openapi3.Paths { // "/person/{personName}": &openapi3.PathItem{}, // } // pathItem := path.Find("/person/{name}") // // would return the correct path item. func (paths *Paths) Find(key string) *PathItem { // Try directly access the map pathItem := paths.Value(key) if pathItem != nil { return pathItem } normalizedPath, expected, _ := normalizeTemplatedPath(key) for path, pathItem := range paths.Map() { pathNormalized, got, _ := normalizeTemplatedPath(path) if got == expected && pathNormalized == normalizedPath { return pathItem } } return nil } func (paths *Paths) validateUniqueOperationIDs() error { operationIDs := make(map[string]string) for urlPath, pathItem := range paths.Map() { if pathItem == nil { continue } for httpMethod, operation := range pathItem.Operations() { if operation == nil || operation.OperationID == "" { continue } endpoint := httpMethod + " " + urlPath if endpointDup, ok := operationIDs[operation.OperationID]; ok { if endpoint > endpointDup { // For make error message a bit more deterministic. May be useful for tests. endpoint, endpointDup = endpointDup, endpoint } return fmt.Errorf("operations %q and %q have the same operation id %q", endpoint, endpointDup, operation.OperationID) } operationIDs[operation.OperationID] = endpoint } } return nil } func normalizeTemplatedPath(path string) (string, uint, map[string]struct{}) { if strings.IndexByte(path, '{') < 0 { return path, 0, nil } var buffTpl strings.Builder buffTpl.Grow(len(path)) var ( cc rune count uint isVariable bool vars = make(map[string]struct{}) buffVar strings.Builder ) for i, c := range path { if isVariable { if c == '}' { // End path variable isVariable = false vars[buffVar.String()] = struct{}{} buffVar = strings.Builder{} // First append possible '*' before this character // The character '}' will be appended if i > 0 && cc == '*' { buffTpl.WriteRune(cc) } } else { buffVar.WriteRune(c) continue } } else if c == '{' { // Begin path variable isVariable = true // The character '{' will be appended count++ } // Append the character buffTpl.WriteRune(c) cc = c } return buffTpl.String(), count, vars } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/ref.go ================================================ package openapi3 //go:generate go run refsgenerator.go // Ref is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#reference-object type Ref struct { Ref string `json:"$ref" yaml:"$ref"` Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/refs.go ================================================ // Code generated by go generate; DO NOT EDIT. package openapi3 import ( "context" "encoding/json" "fmt" "net/url" "sort" "strings" "github.com/go-openapi/jsonpointer" "github.com/perimeterx/marshmallow" ) // CallbackRef represents either a Callback or a $ref to a Callback. // When serializing and both fields are set, Ref is preferred over Value. type CallbackRef struct { // Extensions only captures fields starting with 'x-' as no other fields // are allowed by the openapi spec. Extensions map[string]any Origin *Origin Ref string Value *Callback extra []string refPath *url.URL } var _ jsonpointer.JSONPointable = (*CallbackRef)(nil) func (x *CallbackRef) isEmpty() bool { return x == nil || x.Ref == "" && x.Value == nil } // RefString returns the $ref value. func (x *CallbackRef) RefString() string { return x.Ref } // CollectionName returns the JSON string used for a collection of these components. func (x *CallbackRef) CollectionName() string { return "callbacks" } // RefPath returns the path of the $ref relative to the root document. func (x *CallbackRef) RefPath() *url.URL { return copyURI(x.refPath) } func (x *CallbackRef) setRefPath(u *url.URL) { // Once the refPath is set don't override. References can be loaded // multiple times not all with access to the correct path info. if x.refPath != nil { return } x.refPath = copyURI(u) } // MarshalYAML returns the YAML encoding of CallbackRef. func (x CallbackRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { return &Ref{Ref: ref}, nil } return x.Value.MarshalYAML() } // MarshalJSON returns the JSON encoding of CallbackRef. func (x CallbackRef) MarshalJSON() ([]byte, error) { y, err := x.MarshalYAML() if err != nil { return nil, err } return json.Marshal(y) } // UnmarshalJSON sets CallbackRef to a copy of data. func (x *CallbackRef) UnmarshalJSON(data []byte) error { var refOnly Ref if extra, err := marshmallow.Unmarshal(data, &refOnly, marshmallow.WithExcludeKnownFieldsFromMap(true)); err == nil && refOnly.Ref != "" { x.Ref = refOnly.Ref x.Origin = refOnly.Origin if len(extra) != 0 { x.extra = make([]string, 0, len(extra)) for key := range extra { x.extra = append(x.extra, key) } sort.Strings(x.extra) for k := range extra { if !strings.HasPrefix(k, "x-") { delete(extra, k) } } if len(extra) != 0 { x.Extensions = extra } } return nil } return json.Unmarshal(data, &x.Value) } // Validate returns an error if CallbackRef does not comply with the OpenAPI spec. func (x *CallbackRef) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited var extras []string if extra := x.extra; len(extra) != 0 { allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for _, ex := range extra { if allowed != nil { if _, ok := allowed[ex]; ok { continue } } // extras in the Extensions checked below if _, ok := x.Extensions[ex]; !ok { extras = append(extras, ex) } } } if extra := x.Extensions; exProhibited && len(extra) != 0 { allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for ex := range extra { if allowed != nil { if _, ok := allowed[ex]; ok { continue } } extras = append(extras, ex) } } if len(extras) != 0 { return fmt.Errorf("extra sibling fields: %+v", extras) } if v := x.Value; v != nil { return v.Validate(ctx) } return foundUnresolvedRef(x.Ref) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (x *CallbackRef) JSONLookup(token string) (any, error) { if token == "$ref" { return x.Ref, nil } if v, ok := x.Extensions[token]; ok { return v, nil } ptr, _, err := jsonpointer.GetForToken(x.Value, token) return ptr, err } // ExampleRef represents either a Example or a $ref to a Example. // When serializing and both fields are set, Ref is preferred over Value. type ExampleRef struct { // Extensions only captures fields starting with 'x-' as no other fields // are allowed by the openapi spec. Extensions map[string]any Origin *Origin Ref string Value *Example extra []string refPath *url.URL } var _ jsonpointer.JSONPointable = (*ExampleRef)(nil) func (x *ExampleRef) isEmpty() bool { return x == nil || x.Ref == "" && x.Value == nil } // RefString returns the $ref value. func (x *ExampleRef) RefString() string { return x.Ref } // CollectionName returns the JSON string used for a collection of these components. func (x *ExampleRef) CollectionName() string { return "examples" } // RefPath returns the path of the $ref relative to the root document. func (x *ExampleRef) RefPath() *url.URL { return copyURI(x.refPath) } func (x *ExampleRef) setRefPath(u *url.URL) { // Once the refPath is set don't override. References can be loaded // multiple times not all with access to the correct path info. if x.refPath != nil { return } x.refPath = copyURI(u) } // MarshalYAML returns the YAML encoding of ExampleRef. func (x ExampleRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { return &Ref{Ref: ref}, nil } return x.Value.MarshalYAML() } // MarshalJSON returns the JSON encoding of ExampleRef. func (x ExampleRef) MarshalJSON() ([]byte, error) { y, err := x.MarshalYAML() if err != nil { return nil, err } return json.Marshal(y) } // UnmarshalJSON sets ExampleRef to a copy of data. func (x *ExampleRef) UnmarshalJSON(data []byte) error { var refOnly Ref if extra, err := marshmallow.Unmarshal(data, &refOnly, marshmallow.WithExcludeKnownFieldsFromMap(true)); err == nil && refOnly.Ref != "" { x.Ref = refOnly.Ref x.Origin = refOnly.Origin if len(extra) != 0 { x.extra = make([]string, 0, len(extra)) for key := range extra { x.extra = append(x.extra, key) } sort.Strings(x.extra) for k := range extra { if !strings.HasPrefix(k, "x-") { delete(extra, k) } } if len(extra) != 0 { x.Extensions = extra } } return nil } return json.Unmarshal(data, &x.Value) } // Validate returns an error if ExampleRef does not comply with the OpenAPI spec. func (x *ExampleRef) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited var extras []string if extra := x.extra; len(extra) != 0 { allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for _, ex := range extra { if allowed != nil { if _, ok := allowed[ex]; ok { continue } } // extras in the Extensions checked below if _, ok := x.Extensions[ex]; !ok { extras = append(extras, ex) } } } if extra := x.Extensions; exProhibited && len(extra) != 0 { allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for ex := range extra { if allowed != nil { if _, ok := allowed[ex]; ok { continue } } extras = append(extras, ex) } } if len(extras) != 0 { return fmt.Errorf("extra sibling fields: %+v", extras) } if v := x.Value; v != nil { return v.Validate(ctx) } return foundUnresolvedRef(x.Ref) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (x *ExampleRef) JSONLookup(token string) (any, error) { if token == "$ref" { return x.Ref, nil } if v, ok := x.Extensions[token]; ok { return v, nil } ptr, _, err := jsonpointer.GetForToken(x.Value, token) return ptr, err } // HeaderRef represents either a Header or a $ref to a Header. // When serializing and both fields are set, Ref is preferred over Value. type HeaderRef struct { // Extensions only captures fields starting with 'x-' as no other fields // are allowed by the openapi spec. Extensions map[string]any Origin *Origin Ref string Value *Header extra []string refPath *url.URL } var _ jsonpointer.JSONPointable = (*HeaderRef)(nil) func (x *HeaderRef) isEmpty() bool { return x == nil || x.Ref == "" && x.Value == nil } // RefString returns the $ref value. func (x *HeaderRef) RefString() string { return x.Ref } // CollectionName returns the JSON string used for a collection of these components. func (x *HeaderRef) CollectionName() string { return "headers" } // RefPath returns the path of the $ref relative to the root document. func (x *HeaderRef) RefPath() *url.URL { return copyURI(x.refPath) } func (x *HeaderRef) setRefPath(u *url.URL) { // Once the refPath is set don't override. References can be loaded // multiple times not all with access to the correct path info. if x.refPath != nil { return } x.refPath = copyURI(u) } // MarshalYAML returns the YAML encoding of HeaderRef. func (x HeaderRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { return &Ref{Ref: ref}, nil } return x.Value.MarshalYAML() } // MarshalJSON returns the JSON encoding of HeaderRef. func (x HeaderRef) MarshalJSON() ([]byte, error) { y, err := x.MarshalYAML() if err != nil { return nil, err } return json.Marshal(y) } // UnmarshalJSON sets HeaderRef to a copy of data. func (x *HeaderRef) UnmarshalJSON(data []byte) error { var refOnly Ref if extra, err := marshmallow.Unmarshal(data, &refOnly, marshmallow.WithExcludeKnownFieldsFromMap(true)); err == nil && refOnly.Ref != "" { x.Ref = refOnly.Ref x.Origin = refOnly.Origin if len(extra) != 0 { x.extra = make([]string, 0, len(extra)) for key := range extra { x.extra = append(x.extra, key) } sort.Strings(x.extra) for k := range extra { if !strings.HasPrefix(k, "x-") { delete(extra, k) } } if len(extra) != 0 { x.Extensions = extra } } return nil } return json.Unmarshal(data, &x.Value) } // Validate returns an error if HeaderRef does not comply with the OpenAPI spec. func (x *HeaderRef) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited var extras []string if extra := x.extra; len(extra) != 0 { allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for _, ex := range extra { if allowed != nil { if _, ok := allowed[ex]; ok { continue } } // extras in the Extensions checked below if _, ok := x.Extensions[ex]; !ok { extras = append(extras, ex) } } } if extra := x.Extensions; exProhibited && len(extra) != 0 { allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for ex := range extra { if allowed != nil { if _, ok := allowed[ex]; ok { continue } } extras = append(extras, ex) } } if len(extras) != 0 { return fmt.Errorf("extra sibling fields: %+v", extras) } if v := x.Value; v != nil { return v.Validate(ctx) } return foundUnresolvedRef(x.Ref) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (x *HeaderRef) JSONLookup(token string) (any, error) { if token == "$ref" { return x.Ref, nil } if v, ok := x.Extensions[token]; ok { return v, nil } ptr, _, err := jsonpointer.GetForToken(x.Value, token) return ptr, err } // LinkRef represents either a Link or a $ref to a Link. // When serializing and both fields are set, Ref is preferred over Value. type LinkRef struct { // Extensions only captures fields starting with 'x-' as no other fields // are allowed by the openapi spec. Extensions map[string]any Origin *Origin Ref string Value *Link extra []string refPath *url.URL } var _ jsonpointer.JSONPointable = (*LinkRef)(nil) func (x *LinkRef) isEmpty() bool { return x == nil || x.Ref == "" && x.Value == nil } // RefString returns the $ref value. func (x *LinkRef) RefString() string { return x.Ref } // CollectionName returns the JSON string used for a collection of these components. func (x *LinkRef) CollectionName() string { return "links" } // RefPath returns the path of the $ref relative to the root document. func (x *LinkRef) RefPath() *url.URL { return copyURI(x.refPath) } func (x *LinkRef) setRefPath(u *url.URL) { // Once the refPath is set don't override. References can be loaded // multiple times not all with access to the correct path info. if x.refPath != nil { return } x.refPath = copyURI(u) } // MarshalYAML returns the YAML encoding of LinkRef. func (x LinkRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { return &Ref{Ref: ref}, nil } return x.Value.MarshalYAML() } // MarshalJSON returns the JSON encoding of LinkRef. func (x LinkRef) MarshalJSON() ([]byte, error) { y, err := x.MarshalYAML() if err != nil { return nil, err } return json.Marshal(y) } // UnmarshalJSON sets LinkRef to a copy of data. func (x *LinkRef) UnmarshalJSON(data []byte) error { var refOnly Ref if extra, err := marshmallow.Unmarshal(data, &refOnly, marshmallow.WithExcludeKnownFieldsFromMap(true)); err == nil && refOnly.Ref != "" { x.Ref = refOnly.Ref x.Origin = refOnly.Origin if len(extra) != 0 { x.extra = make([]string, 0, len(extra)) for key := range extra { x.extra = append(x.extra, key) } sort.Strings(x.extra) for k := range extra { if !strings.HasPrefix(k, "x-") { delete(extra, k) } } if len(extra) != 0 { x.Extensions = extra } } return nil } return json.Unmarshal(data, &x.Value) } // Validate returns an error if LinkRef does not comply with the OpenAPI spec. func (x *LinkRef) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited var extras []string if extra := x.extra; len(extra) != 0 { allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for _, ex := range extra { if allowed != nil { if _, ok := allowed[ex]; ok { continue } } // extras in the Extensions checked below if _, ok := x.Extensions[ex]; !ok { extras = append(extras, ex) } } } if extra := x.Extensions; exProhibited && len(extra) != 0 { allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for ex := range extra { if allowed != nil { if _, ok := allowed[ex]; ok { continue } } extras = append(extras, ex) } } if len(extras) != 0 { return fmt.Errorf("extra sibling fields: %+v", extras) } if v := x.Value; v != nil { return v.Validate(ctx) } return foundUnresolvedRef(x.Ref) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (x *LinkRef) JSONLookup(token string) (any, error) { if token == "$ref" { return x.Ref, nil } if v, ok := x.Extensions[token]; ok { return v, nil } ptr, _, err := jsonpointer.GetForToken(x.Value, token) return ptr, err } // ParameterRef represents either a Parameter or a $ref to a Parameter. // When serializing and both fields are set, Ref is preferred over Value. type ParameterRef struct { // Extensions only captures fields starting with 'x-' as no other fields // are allowed by the openapi spec. Extensions map[string]any Origin *Origin Ref string Value *Parameter extra []string refPath *url.URL } var _ jsonpointer.JSONPointable = (*ParameterRef)(nil) func (x *ParameterRef) isEmpty() bool { return x == nil || x.Ref == "" && x.Value == nil } // RefString returns the $ref value. func (x *ParameterRef) RefString() string { return x.Ref } // CollectionName returns the JSON string used for a collection of these components. func (x *ParameterRef) CollectionName() string { return "parameters" } // RefPath returns the path of the $ref relative to the root document. func (x *ParameterRef) RefPath() *url.URL { return copyURI(x.refPath) } func (x *ParameterRef) setRefPath(u *url.URL) { // Once the refPath is set don't override. References can be loaded // multiple times not all with access to the correct path info. if x.refPath != nil { return } x.refPath = copyURI(u) } // MarshalYAML returns the YAML encoding of ParameterRef. func (x ParameterRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { return &Ref{Ref: ref}, nil } return x.Value.MarshalYAML() } // MarshalJSON returns the JSON encoding of ParameterRef. func (x ParameterRef) MarshalJSON() ([]byte, error) { y, err := x.MarshalYAML() if err != nil { return nil, err } return json.Marshal(y) } // UnmarshalJSON sets ParameterRef to a copy of data. func (x *ParameterRef) UnmarshalJSON(data []byte) error { var refOnly Ref if extra, err := marshmallow.Unmarshal(data, &refOnly, marshmallow.WithExcludeKnownFieldsFromMap(true)); err == nil && refOnly.Ref != "" { x.Ref = refOnly.Ref x.Origin = refOnly.Origin if len(extra) != 0 { x.extra = make([]string, 0, len(extra)) for key := range extra { x.extra = append(x.extra, key) } sort.Strings(x.extra) for k := range extra { if !strings.HasPrefix(k, "x-") { delete(extra, k) } } if len(extra) != 0 { x.Extensions = extra } } return nil } return json.Unmarshal(data, &x.Value) } // Validate returns an error if ParameterRef does not comply with the OpenAPI spec. func (x *ParameterRef) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited var extras []string if extra := x.extra; len(extra) != 0 { allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for _, ex := range extra { if allowed != nil { if _, ok := allowed[ex]; ok { continue } } // extras in the Extensions checked below if _, ok := x.Extensions[ex]; !ok { extras = append(extras, ex) } } } if extra := x.Extensions; exProhibited && len(extra) != 0 { allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for ex := range extra { if allowed != nil { if _, ok := allowed[ex]; ok { continue } } extras = append(extras, ex) } } if len(extras) != 0 { return fmt.Errorf("extra sibling fields: %+v", extras) } if v := x.Value; v != nil { return v.Validate(ctx) } return foundUnresolvedRef(x.Ref) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (x *ParameterRef) JSONLookup(token string) (any, error) { if token == "$ref" { return x.Ref, nil } if v, ok := x.Extensions[token]; ok { return v, nil } ptr, _, err := jsonpointer.GetForToken(x.Value, token) return ptr, err } // RequestBodyRef represents either a RequestBody or a $ref to a RequestBody. // When serializing and both fields are set, Ref is preferred over Value. type RequestBodyRef struct { // Extensions only captures fields starting with 'x-' as no other fields // are allowed by the openapi spec. Extensions map[string]any Origin *Origin Ref string Value *RequestBody extra []string refPath *url.URL } var _ jsonpointer.JSONPointable = (*RequestBodyRef)(nil) func (x *RequestBodyRef) isEmpty() bool { return x == nil || x.Ref == "" && x.Value == nil } // RefString returns the $ref value. func (x *RequestBodyRef) RefString() string { return x.Ref } // CollectionName returns the JSON string used for a collection of these components. func (x *RequestBodyRef) CollectionName() string { return "requestBodies" } // RefPath returns the path of the $ref relative to the root document. func (x *RequestBodyRef) RefPath() *url.URL { return copyURI(x.refPath) } func (x *RequestBodyRef) setRefPath(u *url.URL) { // Once the refPath is set don't override. References can be loaded // multiple times not all with access to the correct path info. if x.refPath != nil { return } x.refPath = copyURI(u) } // MarshalYAML returns the YAML encoding of RequestBodyRef. func (x RequestBodyRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { return &Ref{Ref: ref}, nil } return x.Value.MarshalYAML() } // MarshalJSON returns the JSON encoding of RequestBodyRef. func (x RequestBodyRef) MarshalJSON() ([]byte, error) { y, err := x.MarshalYAML() if err != nil { return nil, err } return json.Marshal(y) } // UnmarshalJSON sets RequestBodyRef to a copy of data. func (x *RequestBodyRef) UnmarshalJSON(data []byte) error { var refOnly Ref if extra, err := marshmallow.Unmarshal(data, &refOnly, marshmallow.WithExcludeKnownFieldsFromMap(true)); err == nil && refOnly.Ref != "" { x.Ref = refOnly.Ref x.Origin = refOnly.Origin if len(extra) != 0 { x.extra = make([]string, 0, len(extra)) for key := range extra { x.extra = append(x.extra, key) } sort.Strings(x.extra) for k := range extra { if !strings.HasPrefix(k, "x-") { delete(extra, k) } } if len(extra) != 0 { x.Extensions = extra } } return nil } return json.Unmarshal(data, &x.Value) } // Validate returns an error if RequestBodyRef does not comply with the OpenAPI spec. func (x *RequestBodyRef) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited var extras []string if extra := x.extra; len(extra) != 0 { allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for _, ex := range extra { if allowed != nil { if _, ok := allowed[ex]; ok { continue } } // extras in the Extensions checked below if _, ok := x.Extensions[ex]; !ok { extras = append(extras, ex) } } } if extra := x.Extensions; exProhibited && len(extra) != 0 { allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for ex := range extra { if allowed != nil { if _, ok := allowed[ex]; ok { continue } } extras = append(extras, ex) } } if len(extras) != 0 { return fmt.Errorf("extra sibling fields: %+v", extras) } if v := x.Value; v != nil { return v.Validate(ctx) } return foundUnresolvedRef(x.Ref) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (x *RequestBodyRef) JSONLookup(token string) (any, error) { if token == "$ref" { return x.Ref, nil } if v, ok := x.Extensions[token]; ok { return v, nil } ptr, _, err := jsonpointer.GetForToken(x.Value, token) return ptr, err } // ResponseRef represents either a Response or a $ref to a Response. // When serializing and both fields are set, Ref is preferred over Value. type ResponseRef struct { // Extensions only captures fields starting with 'x-' as no other fields // are allowed by the openapi spec. Extensions map[string]any Origin *Origin Ref string Value *Response extra []string refPath *url.URL } var _ jsonpointer.JSONPointable = (*ResponseRef)(nil) func (x *ResponseRef) isEmpty() bool { return x == nil || x.Ref == "" && x.Value == nil } // RefString returns the $ref value. func (x *ResponseRef) RefString() string { return x.Ref } // CollectionName returns the JSON string used for a collection of these components. func (x *ResponseRef) CollectionName() string { return "responses" } // RefPath returns the path of the $ref relative to the root document. func (x *ResponseRef) RefPath() *url.URL { return copyURI(x.refPath) } func (x *ResponseRef) setRefPath(u *url.URL) { // Once the refPath is set don't override. References can be loaded // multiple times not all with access to the correct path info. if x.refPath != nil { return } x.refPath = copyURI(u) } // MarshalYAML returns the YAML encoding of ResponseRef. func (x ResponseRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { return &Ref{Ref: ref}, nil } return x.Value.MarshalYAML() } // MarshalJSON returns the JSON encoding of ResponseRef. func (x ResponseRef) MarshalJSON() ([]byte, error) { y, err := x.MarshalYAML() if err != nil { return nil, err } return json.Marshal(y) } // UnmarshalJSON sets ResponseRef to a copy of data. func (x *ResponseRef) UnmarshalJSON(data []byte) error { var refOnly Ref if extra, err := marshmallow.Unmarshal(data, &refOnly, marshmallow.WithExcludeKnownFieldsFromMap(true)); err == nil && refOnly.Ref != "" { x.Ref = refOnly.Ref x.Origin = refOnly.Origin if len(extra) != 0 { x.extra = make([]string, 0, len(extra)) for key := range extra { x.extra = append(x.extra, key) } sort.Strings(x.extra) for k := range extra { if !strings.HasPrefix(k, "x-") { delete(extra, k) } } if len(extra) != 0 { x.Extensions = extra } } return nil } return json.Unmarshal(data, &x.Value) } // Validate returns an error if ResponseRef does not comply with the OpenAPI spec. func (x *ResponseRef) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited var extras []string if extra := x.extra; len(extra) != 0 { allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for _, ex := range extra { if allowed != nil { if _, ok := allowed[ex]; ok { continue } } // extras in the Extensions checked below if _, ok := x.Extensions[ex]; !ok { extras = append(extras, ex) } } } if extra := x.Extensions; exProhibited && len(extra) != 0 { allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for ex := range extra { if allowed != nil { if _, ok := allowed[ex]; ok { continue } } extras = append(extras, ex) } } if len(extras) != 0 { return fmt.Errorf("extra sibling fields: %+v", extras) } if v := x.Value; v != nil { return v.Validate(ctx) } return foundUnresolvedRef(x.Ref) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (x *ResponseRef) JSONLookup(token string) (any, error) { if token == "$ref" { return x.Ref, nil } if v, ok := x.Extensions[token]; ok { return v, nil } ptr, _, err := jsonpointer.GetForToken(x.Value, token) return ptr, err } // SchemaRef represents either a Schema or a $ref to a Schema. // When serializing and both fields are set, Ref is preferred over Value. type SchemaRef struct { // Extensions only captures fields starting with 'x-' as no other fields // are allowed by the openapi spec. Extensions map[string]any Origin *Origin Ref string Value *Schema extra []string refPath *url.URL } var _ jsonpointer.JSONPointable = (*SchemaRef)(nil) func (x *SchemaRef) isEmpty() bool { return x == nil || x.Ref == "" && x.Value == nil } // RefString returns the $ref value. func (x *SchemaRef) RefString() string { return x.Ref } // CollectionName returns the JSON string used for a collection of these components. func (x *SchemaRef) CollectionName() string { return "schemas" } // RefPath returns the path of the $ref relative to the root document. func (x *SchemaRef) RefPath() *url.URL { return copyURI(x.refPath) } func (x *SchemaRef) setRefPath(u *url.URL) { // Once the refPath is set don't override. References can be loaded // multiple times not all with access to the correct path info. if x.refPath != nil { return } x.refPath = copyURI(u) } // MarshalYAML returns the YAML encoding of SchemaRef. func (x SchemaRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { return &Ref{Ref: ref}, nil } return x.Value.MarshalYAML() } // MarshalJSON returns the JSON encoding of SchemaRef. func (x SchemaRef) MarshalJSON() ([]byte, error) { y, err := x.MarshalYAML() if err != nil { return nil, err } return json.Marshal(y) } // UnmarshalJSON sets SchemaRef to a copy of data. func (x *SchemaRef) UnmarshalJSON(data []byte) error { var refOnly Ref if extra, err := marshmallow.Unmarshal(data, &refOnly, marshmallow.WithExcludeKnownFieldsFromMap(true)); err == nil && refOnly.Ref != "" { x.Ref = refOnly.Ref x.Origin = refOnly.Origin if len(extra) != 0 { x.extra = make([]string, 0, len(extra)) for key := range extra { x.extra = append(x.extra, key) } sort.Strings(x.extra) for k := range extra { if !strings.HasPrefix(k, "x-") { delete(extra, k) } } if len(extra) != 0 { x.Extensions = extra } } return nil } return json.Unmarshal(data, &x.Value) } // Validate returns an error if SchemaRef does not comply with the OpenAPI spec. func (x *SchemaRef) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited var extras []string if extra := x.extra; len(extra) != 0 { allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for _, ex := range extra { if allowed != nil { if _, ok := allowed[ex]; ok { continue } } // extras in the Extensions checked below if _, ok := x.Extensions[ex]; !ok { extras = append(extras, ex) } } } if extra := x.Extensions; exProhibited && len(extra) != 0 { allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for ex := range extra { if allowed != nil { if _, ok := allowed[ex]; ok { continue } } extras = append(extras, ex) } } if len(extras) != 0 { return fmt.Errorf("extra sibling fields: %+v", extras) } if v := x.Value; v != nil { return v.Validate(ctx) } return foundUnresolvedRef(x.Ref) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (x *SchemaRef) JSONLookup(token string) (any, error) { if token == "$ref" { return x.Ref, nil } if v, ok := x.Extensions[token]; ok { return v, nil } ptr, _, err := jsonpointer.GetForToken(x.Value, token) return ptr, err } // SecuritySchemeRef represents either a SecurityScheme or a $ref to a SecurityScheme. // When serializing and both fields are set, Ref is preferred over Value. type SecuritySchemeRef struct { // Extensions only captures fields starting with 'x-' as no other fields // are allowed by the openapi spec. Extensions map[string]any Origin *Origin Ref string Value *SecurityScheme extra []string refPath *url.URL } var _ jsonpointer.JSONPointable = (*SecuritySchemeRef)(nil) func (x *SecuritySchemeRef) isEmpty() bool { return x == nil || x.Ref == "" && x.Value == nil } // RefString returns the $ref value. func (x *SecuritySchemeRef) RefString() string { return x.Ref } // CollectionName returns the JSON string used for a collection of these components. func (x *SecuritySchemeRef) CollectionName() string { return "securitySchemes" } // RefPath returns the path of the $ref relative to the root document. func (x *SecuritySchemeRef) RefPath() *url.URL { return copyURI(x.refPath) } func (x *SecuritySchemeRef) setRefPath(u *url.URL) { // Once the refPath is set don't override. References can be loaded // multiple times not all with access to the correct path info. if x.refPath != nil { return } x.refPath = copyURI(u) } // MarshalYAML returns the YAML encoding of SecuritySchemeRef. func (x SecuritySchemeRef) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { return &Ref{Ref: ref}, nil } return x.Value.MarshalYAML() } // MarshalJSON returns the JSON encoding of SecuritySchemeRef. func (x SecuritySchemeRef) MarshalJSON() ([]byte, error) { y, err := x.MarshalYAML() if err != nil { return nil, err } return json.Marshal(y) } // UnmarshalJSON sets SecuritySchemeRef to a copy of data. func (x *SecuritySchemeRef) UnmarshalJSON(data []byte) error { var refOnly Ref if extra, err := marshmallow.Unmarshal(data, &refOnly, marshmallow.WithExcludeKnownFieldsFromMap(true)); err == nil && refOnly.Ref != "" { x.Ref = refOnly.Ref x.Origin = refOnly.Origin if len(extra) != 0 { x.extra = make([]string, 0, len(extra)) for key := range extra { x.extra = append(x.extra, key) } sort.Strings(x.extra) for k := range extra { if !strings.HasPrefix(k, "x-") { delete(extra, k) } } if len(extra) != 0 { x.Extensions = extra } } return nil } return json.Unmarshal(data, &x.Value) } // Validate returns an error if SecuritySchemeRef does not comply with the OpenAPI spec. func (x *SecuritySchemeRef) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited var extras []string if extra := x.extra; len(extra) != 0 { allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for _, ex := range extra { if allowed != nil { if _, ok := allowed[ex]; ok { continue } } // extras in the Extensions checked below if _, ok := x.Extensions[ex]; !ok { extras = append(extras, ex) } } } if extra := x.Extensions; exProhibited && len(extra) != 0 { allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for ex := range extra { if allowed != nil { if _, ok := allowed[ex]; ok { continue } } extras = append(extras, ex) } } if len(extras) != 0 { return fmt.Errorf("extra sibling fields: %+v", extras) } if v := x.Value; v != nil { return v.Validate(ctx) } return foundUnresolvedRef(x.Ref) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (x *SecuritySchemeRef) JSONLookup(token string) (any, error) { if token == "$ref" { return x.Ref, nil } if v, ok := x.Extensions[token]; ok { return v, nil } ptr, _, err := jsonpointer.GetForToken(x.Value, token) return ptr, err } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/refs.tmpl ================================================ // Code generated by go generate; DO NOT EDIT. package {{ .Package }} import ( "context" "encoding/json" "fmt" "net/url" "sort" "strings" "github.com/go-openapi/jsonpointer" "github.com/perimeterx/marshmallow" ) {{ range $type := .Types }} // {{ $type.Name }}Ref represents either a {{ $type.Name }} or a $ref to a {{ $type.Name }}. // When serializing and both fields are set, Ref is preferred over Value. type {{ $type.Name }}Ref struct { // Extensions only captures fields starting with 'x-' as no other fields // are allowed by the openapi spec. Extensions map[string]any Origin *Origin Ref string Value *{{ $type.Name }} extra []string refPath *url.URL } var _ jsonpointer.JSONPointable = (*{{ $type.Name }}Ref)(nil) func (x *{{ $type.Name }}Ref) isEmpty() bool { return x == nil || x.Ref == "" && x.Value == nil } // RefString returns the $ref value. func (x *{{ $type.Name }}Ref) RefString() string { return x.Ref } // CollectionName returns the JSON string used for a collection of these components. func (x *{{ $type.Name }}Ref) CollectionName() string { return "{{ $type.CollectionName }}" } // RefPath returns the path of the $ref relative to the root document. func (x *{{ $type.Name }}Ref) RefPath() *url.URL { return copyURI(x.refPath) } func (x *{{ $type.Name }}Ref) setRefPath(u *url.URL) { // Once the refPath is set don't override. References can be loaded // multiple times not all with access to the correct path info. if x.refPath != nil { return } x.refPath = copyURI(u) } // MarshalYAML returns the YAML encoding of {{ $type.Name }}Ref. func (x {{ $type.Name }}Ref) MarshalYAML() (any, error) { if ref := x.Ref; ref != "" { return &Ref{Ref: ref}, nil } return x.Value.MarshalYAML() } // MarshalJSON returns the JSON encoding of {{ $type.Name }}Ref. func (x {{ $type.Name }}Ref) MarshalJSON() ([]byte, error) { y, err := x.MarshalYAML() if err != nil { return nil, err } return json.Marshal(y) } // UnmarshalJSON sets {{ $type.Name }}Ref to a copy of data. func (x *{{ $type.Name }}Ref) UnmarshalJSON(data []byte) error { var refOnly Ref if extra, err := marshmallow.Unmarshal(data, &refOnly, marshmallow.WithExcludeKnownFieldsFromMap(true)); err == nil && refOnly.Ref != "" { x.Ref = refOnly.Ref x.Origin = refOnly.Origin if len(extra) != 0 { x.extra = make([]string, 0, len(extra)) for key := range extra { x.extra = append(x.extra, key) } sort.Strings(x.extra) for k := range extra { if !strings.HasPrefix(k, "x-") { delete(extra, k) } } if len(extra) != 0 { x.Extensions = extra } } return nil } return json.Unmarshal(data, &x.Value) } // Validate returns an error if {{ $type.Name }}Ref does not comply with the OpenAPI spec. func (x *{{ $type.Name }}Ref) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) exProhibited := getValidationOptions(ctx).schemaExtensionsInRefProhibited var extras []string if extra := x.extra; len(extra) != 0 { allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for _, ex := range extra { if allowed != nil { if _, ok := allowed[ex]; ok { continue } } // extras in the Extensions checked below if _, ok := x.Extensions[ex]; !ok { extras = append(extras, ex) } } } if extra := x.Extensions; exProhibited && len(extra) != 0 { allowed := getValidationOptions(ctx).extraSiblingFieldsAllowed for ex := range extra { if allowed != nil { if _, ok := allowed[ex]; ok { continue } } extras = append(extras, ex) } } if len(extras) != 0 { return fmt.Errorf("extra sibling fields: %+v", extras) } if v := x.Value; v != nil { return v.Validate(ctx) } return foundUnresolvedRef(x.Ref) } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (x *{{ $type.Name }}Ref) JSONLookup(token string) (any, error) { if token == "$ref" { return x.Ref, nil } if v, ok := x.Extensions[token]; ok { return v, nil } ptr, _, err := jsonpointer.GetForToken(x.Value, token) return ptr, err } {{ end -}} ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/refs_test.tmpl ================================================ // Code generated by go generate; DO NOT EDIT. package {{ .Package }} import ( "context" "encoding/json" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) {{ range $type := .Types }} func Test{{ $type.Name }}Ref_Extensions(t *testing.T) { data := []byte(`{"$ref":"#/components/schemas/Pet","something":"integer","x-order":1}`) ref := {{ $type.Name }}Ref{} err := json.Unmarshal(data, &ref) assert.NoError(t, err) // captures extension assert.Equal(t, "#/components/schemas/Pet", ref.Ref) assert.Equal(t, float64(1), ref.Extensions["x-order"]) // does not capture non-extensions assert.Nil(t, ref.Extensions["something"]) // validation err = ref.Validate(context.Background()) require.EqualError(t, err, "extra sibling fields: [something]") err = ref.Validate(context.Background(), ProhibitExtensionsWithRef()) require.EqualError(t, err, "extra sibling fields: [something x-order]") err = ref.Validate(context.Background(), AllowExtraSiblingFields("something")) assert.ErrorContains(t, err, "found unresolved ref") // expected since value not defined // non-extension not json lookable _, err = ref.JSONLookup("something") assert.Error(t, err) {{ if ne $type.Name "Header" }} t.Run("extentions in value", func(t *testing.T) { ref.Value = &{{ $type.Name }}{Extensions: map[string]any{}} ref.Value.Extensions["x-order"] = 2.0 // prefers the value next to the \$ref over the one in the \$ref. v, err := ref.JSONLookup("x-order") assert.NoError(t, err) assert.Equal(t, float64(1), v) }) {{ else }} // Header does not have its own extensions. {{ end -}} } {{ end -}} ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/request_body.go ================================================ package openapi3 import ( "context" "encoding/json" "errors" ) // RequestBody is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#request-body-object type RequestBody struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` Required bool `json:"required,omitempty" yaml:"required,omitempty"` Content Content `json:"content" yaml:"content"` } func NewRequestBody() *RequestBody { return &RequestBody{} } func (requestBody *RequestBody) WithDescription(value string) *RequestBody { requestBody.Description = value return requestBody } func (requestBody *RequestBody) WithRequired(value bool) *RequestBody { requestBody.Required = value return requestBody } func (requestBody *RequestBody) WithContent(content Content) *RequestBody { requestBody.Content = content return requestBody } func (requestBody *RequestBody) WithSchemaRef(value *SchemaRef, consumes []string) *RequestBody { requestBody.Content = NewContentWithSchemaRef(value, consumes) return requestBody } func (requestBody *RequestBody) WithSchema(value *Schema, consumes []string) *RequestBody { requestBody.Content = NewContentWithSchema(value, consumes) return requestBody } func (requestBody *RequestBody) WithJSONSchemaRef(value *SchemaRef) *RequestBody { requestBody.Content = NewContentWithJSONSchemaRef(value) return requestBody } func (requestBody *RequestBody) WithJSONSchema(value *Schema) *RequestBody { requestBody.Content = NewContentWithJSONSchema(value) return requestBody } func (requestBody *RequestBody) WithFormDataSchemaRef(value *SchemaRef) *RequestBody { requestBody.Content = NewContentWithFormDataSchemaRef(value) return requestBody } func (requestBody *RequestBody) WithFormDataSchema(value *Schema) *RequestBody { requestBody.Content = NewContentWithFormDataSchema(value) return requestBody } func (requestBody *RequestBody) GetMediaType(mediaType string) *MediaType { m := requestBody.Content if m == nil { return nil } return m[mediaType] } // MarshalJSON returns the JSON encoding of RequestBody. func (requestBody RequestBody) MarshalJSON() ([]byte, error) { x, err := requestBody.MarshalYAML() if err != nil { return nil, err } return json.Marshal(x) } // MarshalYAML returns the YAML encoding of RequestBody. func (requestBody RequestBody) MarshalYAML() (any, error) { m := make(map[string]any, 3+len(requestBody.Extensions)) for k, v := range requestBody.Extensions { m[k] = v } if x := requestBody.Description; x != "" { m["description"] = requestBody.Description } if x := requestBody.Required; x { m["required"] = x } if x := requestBody.Content; true { m["content"] = x } return m, nil } // UnmarshalJSON sets RequestBody to a copy of data. func (requestBody *RequestBody) UnmarshalJSON(data []byte) error { type RequestBodyBis RequestBody var x RequestBodyBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, originKey) delete(x.Extensions, "description") delete(x.Extensions, "required") delete(x.Extensions, "content") if len(x.Extensions) == 0 { x.Extensions = nil } *requestBody = RequestBody(x) return nil } // Validate returns an error if RequestBody does not comply with the OpenAPI spec. func (requestBody *RequestBody) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) if requestBody.Content == nil { return errors.New("content of the request body is required") } if vo := getValidationOptions(ctx); !vo.examplesValidationDisabled { vo.examplesValidationAsReq, vo.examplesValidationAsRes = true, false } if err := requestBody.Content.Validate(ctx); err != nil { return err } return validateExtensions(ctx, requestBody.Extensions) } // UnmarshalJSON sets RequestBodies to a copy of data. func (requestBodies *RequestBodies) UnmarshalJSON(data []byte) (err error) { *requestBodies, _, err = unmarshalStringMapP[RequestBodyRef](data) return } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/response.go ================================================ package openapi3 import ( "context" "encoding/json" "errors" "sort" "strconv" ) // Responses is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#responses-object type Responses struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"-" yaml:"-"` m map[string]*ResponseRef } // NewResponses builds a responses object with response objects in insertion order. // Given no arguments, NewResponses returns a valid responses object containing a default match-all reponse. func NewResponses(opts ...NewResponsesOption) *Responses { if len(opts) == 0 { return NewResponses(WithName("default", NewResponse().WithDescription(""))) } responses := NewResponsesWithCapacity(len(opts)) for _, opt := range opts { opt(responses) } return responses } // NewResponsesOption describes options to NewResponses func type NewResponsesOption func(*Responses) // WithStatus adds a status code keyed ResponseRef func WithStatus(status int, responseRef *ResponseRef) NewResponsesOption { return func(responses *Responses) { if r := responseRef; r != nil { code := strconv.FormatInt(int64(status), 10) responses.Set(code, r) } } } // WithName adds a name-keyed Response func WithName(name string, response *Response) NewResponsesOption { return func(responses *Responses) { if r := response; r != nil && name != "" { responses.Set(name, &ResponseRef{Value: r}) } } } // Default returns the default response func (responses *Responses) Default() *ResponseRef { return responses.Value("default") } // Status returns a ResponseRef for the given status // If an exact match isn't initially found a patterned field is checked using // the first digit to determine the range (eg: 201 to 2XX) // See https://spec.openapis.org/oas/v3.0.3#patterned-fields-0 func (responses *Responses) Status(status int) *ResponseRef { st := strconv.FormatInt(int64(status), 10) if rref := responses.Value(st); rref != nil { return rref } if 99 < status && status < 600 { st = string(st[0]) + "XX" switch st { case "1XX", "2XX", "3XX", "4XX", "5XX": return responses.Value(st) } } return nil } // Validate returns an error if Responses does not comply with the OpenAPI spec. func (responses *Responses) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) if responses.Len() == 0 { return errors.New("the responses object MUST contain at least one response code") } keys := make([]string, 0, responses.Len()) for key := range responses.Map() { keys = append(keys, key) } sort.Strings(keys) for _, key := range keys { v := responses.Value(key) if err := v.Validate(ctx); err != nil { return err } } return validateExtensions(ctx, responses.Extensions) } // Response is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#response-object type Response struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Description *string `json:"description,omitempty" yaml:"description,omitempty"` Headers Headers `json:"headers,omitempty" yaml:"headers,omitempty"` Content Content `json:"content,omitempty" yaml:"content,omitempty"` Links Links `json:"links,omitempty" yaml:"links,omitempty"` } func NewResponse() *Response { return &Response{} } func (response *Response) WithDescription(value string) *Response { response.Description = &value return response } func (response *Response) WithContent(content Content) *Response { response.Content = content return response } func (response *Response) WithJSONSchema(schema *Schema) *Response { response.Content = NewContentWithJSONSchema(schema) return response } func (response *Response) WithJSONSchemaRef(schema *SchemaRef) *Response { response.Content = NewContentWithJSONSchemaRef(schema) return response } // MarshalJSON returns the JSON encoding of Response. func (response Response) MarshalJSON() ([]byte, error) { x, err := response.MarshalYAML() if err != nil { return nil, err } return json.Marshal(x) } // MarshalYAML returns the YAML encoding of Response. func (response Response) MarshalYAML() (any, error) { m := make(map[string]any, 4+len(response.Extensions)) for k, v := range response.Extensions { m[k] = v } if x := response.Description; x != nil { m["description"] = x } if x := response.Headers; len(x) != 0 { m["headers"] = x } if x := response.Content; len(x) != 0 { m["content"] = x } if x := response.Links; len(x) != 0 { m["links"] = x } return m, nil } // UnmarshalJSON sets Response to a copy of data. func (response *Response) UnmarshalJSON(data []byte) error { type ResponseBis Response var x ResponseBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, originKey) delete(x.Extensions, "description") delete(x.Extensions, "headers") delete(x.Extensions, "content") delete(x.Extensions, "links") if len(x.Extensions) == 0 { x.Extensions = nil } *response = Response(x) return nil } // Validate returns an error if Response does not comply with the OpenAPI spec. func (response *Response) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) if response.Description == nil { return errors.New("a short description of the response is required") } if vo := getValidationOptions(ctx); !vo.examplesValidationDisabled { vo.examplesValidationAsReq, vo.examplesValidationAsRes = false, true } if content := response.Content; content != nil { if err := content.Validate(ctx); err != nil { return err } } headers := make([]string, 0, len(response.Headers)) for name := range response.Headers { headers = append(headers, name) } sort.Strings(headers) for _, name := range headers { header := response.Headers[name] if err := header.Validate(ctx); err != nil { return err } } links := make([]string, 0, len(response.Links)) for name := range response.Links { links = append(links, name) } sort.Strings(links) for _, name := range links { link := response.Links[name] if err := link.Validate(ctx); err != nil { return err } } return validateExtensions(ctx, response.Extensions) } // UnmarshalJSON sets ResponseBodies to a copy of data. func (responseBodies *ResponseBodies) UnmarshalJSON(data []byte) (err error) { *responseBodies, _, err = unmarshalStringMapP[ResponseRef](data) return } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/schema.go ================================================ package openapi3 import ( "bytes" "context" "encoding/json" "errors" "fmt" "math" "math/big" "reflect" "sort" "strconv" "strings" "sync" "unicode/utf16" "github.com/go-openapi/jsonpointer" "github.com/mohae/deepcopy" ) const ( TypeArray = "array" TypeBoolean = "boolean" TypeInteger = "integer" TypeNumber = "number" TypeObject = "object" TypeString = "string" TypeNull = "null" ) var ( // SchemaErrorDetailsDisabled disables printing of details about schema errors. SchemaErrorDetailsDisabled = false errSchema = errors.New("input does not match the schema") // ErrOneOfConflict is the SchemaError Origin when data matches more than one oneOf schema ErrOneOfConflict = errors.New("input matches more than one oneOf schemas") // ErrSchemaInputNaN may be returned when validating a number ErrSchemaInputNaN = errors.New("floating point NaN is not allowed") // ErrSchemaInputInf may be returned when validating a number ErrSchemaInputInf = errors.New("floating point Inf is not allowed") compiledPatterns sync.Map ) // NewSchemaRef simply builds a SchemaRef func NewSchemaRef(ref string, value *Schema) *SchemaRef { return &SchemaRef{ Ref: ref, Value: value, } } type SchemaRefs []*SchemaRef var _ jsonpointer.JSONPointable = (*SchemaRefs)(nil) // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (s SchemaRefs) JSONLookup(token string) (any, error) { i, err := strconv.ParseUint(token, 10, 64) if err != nil { return nil, err } if i >= uint64(len(s)) { return nil, fmt.Errorf("index out of range: %d", i) } ref := s[i] if ref == nil || ref.Ref != "" { return &Ref{Ref: ref.Ref}, nil } return ref.Value, nil } // Schema is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#schema-object type Schema struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` OneOf SchemaRefs `json:"oneOf,omitempty" yaml:"oneOf,omitempty"` AnyOf SchemaRefs `json:"anyOf,omitempty" yaml:"anyOf,omitempty"` AllOf SchemaRefs `json:"allOf,omitempty" yaml:"allOf,omitempty"` Not *SchemaRef `json:"not,omitempty" yaml:"not,omitempty"` Type *Types `json:"type,omitempty" yaml:"type,omitempty"` Title string `json:"title,omitempty" yaml:"title,omitempty"` Format string `json:"format,omitempty" yaml:"format,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` Enum []any `json:"enum,omitempty" yaml:"enum,omitempty"` Default any `json:"default,omitempty" yaml:"default,omitempty"` Example any `json:"example,omitempty" yaml:"example,omitempty"` ExternalDocs *ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` // Array-related, here for struct compactness UniqueItems bool `json:"uniqueItems,omitempty" yaml:"uniqueItems,omitempty"` // Number-related, here for struct compactness ExclusiveMin bool `json:"exclusiveMinimum,omitempty" yaml:"exclusiveMinimum,omitempty"` ExclusiveMax bool `json:"exclusiveMaximum,omitempty" yaml:"exclusiveMaximum,omitempty"` // Properties Nullable bool `json:"nullable,omitempty" yaml:"nullable,omitempty"` ReadOnly bool `json:"readOnly,omitempty" yaml:"readOnly,omitempty"` WriteOnly bool `json:"writeOnly,omitempty" yaml:"writeOnly,omitempty"` AllowEmptyValue bool `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,omitempty"` Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` XML *XML `json:"xml,omitempty" yaml:"xml,omitempty"` // Number Min *float64 `json:"minimum,omitempty" yaml:"minimum,omitempty"` Max *float64 `json:"maximum,omitempty" yaml:"maximum,omitempty"` MultipleOf *float64 `json:"multipleOf,omitempty" yaml:"multipleOf,omitempty"` // String MinLength uint64 `json:"minLength,omitempty" yaml:"minLength,omitempty"` MaxLength *uint64 `json:"maxLength,omitempty" yaml:"maxLength,omitempty"` Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"` // Array MinItems uint64 `json:"minItems,omitempty" yaml:"minItems,omitempty"` MaxItems *uint64 `json:"maxItems,omitempty" yaml:"maxItems,omitempty"` Items *SchemaRef `json:"items,omitempty" yaml:"items,omitempty"` // Object Required []string `json:"required,omitempty" yaml:"required,omitempty"` Properties Schemas `json:"properties,omitempty" yaml:"properties,omitempty"` MinProps uint64 `json:"minProperties,omitempty" yaml:"minProperties,omitempty"` MaxProps *uint64 `json:"maxProperties,omitempty" yaml:"maxProperties,omitempty"` AdditionalProperties AdditionalProperties `json:"additionalProperties,omitempty" yaml:"additionalProperties,omitempty"` Discriminator *Discriminator `json:"discriminator,omitempty" yaml:"discriminator,omitempty"` } type Types []string func (types *Types) Is(typ string) bool { return types != nil && len(*types) == 1 && (*types)[0] == typ } func (types *Types) Slice() []string { if types == nil { return nil } return *types } func (pTypes *Types) Includes(typ string) bool { if pTypes == nil { return false } types := *pTypes for _, candidate := range types { if candidate == typ { return true } } return false } func (types *Types) Permits(typ string) bool { if types == nil { return true } return types.Includes(typ) } func (pTypes *Types) MarshalJSON() ([]byte, error) { x, err := pTypes.MarshalYAML() if err != nil { return nil, err } return json.Marshal(x) } func (pTypes *Types) MarshalYAML() (any, error) { if pTypes == nil { return nil, nil } types := *pTypes switch len(types) { case 0: return nil, nil case 1: return types[0], nil default: return []string(types), nil } } func (types *Types) UnmarshalJSON(data []byte) error { var strings []string if err := json.Unmarshal(data, &strings); err != nil { var s string if err := json.Unmarshal(data, &s); err != nil { return unmarshalError(err) } strings = []string{s} } *types = strings return nil } type AdditionalProperties struct { Has *bool Schema *SchemaRef } // MarshalYAML returns the YAML encoding of AdditionalProperties. func (addProps AdditionalProperties) MarshalYAML() (any, error) { if x := addProps.Has; x != nil { if *x { return true, nil } return false, nil } if x := addProps.Schema; x != nil { return x.MarshalYAML() } return nil, nil } // MarshalJSON returns the JSON encoding of AdditionalProperties. func (addProps AdditionalProperties) MarshalJSON() ([]byte, error) { x, err := addProps.MarshalYAML() if err != nil { return nil, err } return json.Marshal(x) } // UnmarshalJSON sets AdditionalProperties to a copy of data. func (addProps *AdditionalProperties) UnmarshalJSON(data []byte) error { var x any if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } switch y := x.(type) { case nil: case bool: addProps.Has = &y case map[string]any: if len(y) == 0 { addProps.Schema = &SchemaRef{Value: &Schema{}} } else { buf := new(bytes.Buffer) json.NewEncoder(buf).Encode(y) if err := json.NewDecoder(buf).Decode(&addProps.Schema); err != nil { return err } } default: return errors.New("cannot unmarshal additionalProperties: value must be either a schema object or a boolean") } return nil } var _ jsonpointer.JSONPointable = (*Schema)(nil) func NewSchema() *Schema { return &Schema{} } // MarshalJSON returns the JSON encoding of Schema. func (schema Schema) MarshalJSON() ([]byte, error) { m, err := schema.MarshalYAML() if err != nil { return nil, err } return json.Marshal(m) } // MarshalYAML returns the YAML encoding of Schema. func (schema Schema) MarshalYAML() (any, error) { m := make(map[string]any, 36+len(schema.Extensions)) for k, v := range schema.Extensions { m[k] = v } if x := schema.OneOf; len(x) != 0 { m["oneOf"] = x } if x := schema.AnyOf; len(x) != 0 { m["anyOf"] = x } if x := schema.AllOf; len(x) != 0 { m["allOf"] = x } if x := schema.Not; x != nil { m["not"] = x } if x := schema.Type; x != nil { m["type"] = x } if x := schema.Title; len(x) != 0 { m["title"] = x } if x := schema.Format; len(x) != 0 { m["format"] = x } if x := schema.Description; len(x) != 0 { m["description"] = x } if x := schema.Enum; len(x) != 0 { m["enum"] = x } if x := schema.Default; x != nil { m["default"] = x } if x := schema.Example; x != nil { m["example"] = x } if x := schema.ExternalDocs; x != nil { m["externalDocs"] = x } // Array-related if x := schema.UniqueItems; x { m["uniqueItems"] = x } // Number-related if x := schema.ExclusiveMin; x { m["exclusiveMinimum"] = x } if x := schema.ExclusiveMax; x { m["exclusiveMaximum"] = x } // Properties if x := schema.Nullable; x { m["nullable"] = x } if x := schema.ReadOnly; x { m["readOnly"] = x } if x := schema.WriteOnly; x { m["writeOnly"] = x } if x := schema.AllowEmptyValue; x { m["allowEmptyValue"] = x } if x := schema.Deprecated; x { m["deprecated"] = x } if x := schema.XML; x != nil { m["xml"] = x } // Number if x := schema.Min; x != nil { m["minimum"] = x } if x := schema.Max; x != nil { m["maximum"] = x } if x := schema.MultipleOf; x != nil { m["multipleOf"] = x } // String if x := schema.MinLength; x != 0 { m["minLength"] = x } if x := schema.MaxLength; x != nil { m["maxLength"] = x } if x := schema.Pattern; x != "" { m["pattern"] = x } // Array if x := schema.MinItems; x != 0 { m["minItems"] = x } if x := schema.MaxItems; x != nil { m["maxItems"] = x } if x := schema.Items; x != nil { m["items"] = x } // Object if x := schema.Required; len(x) != 0 { m["required"] = x } if x := schema.Properties; len(x) != 0 { m["properties"] = x } if x := schema.MinProps; x != 0 { m["minProperties"] = x } if x := schema.MaxProps; x != nil { m["maxProperties"] = x } if x := schema.AdditionalProperties; x.Has != nil || x.Schema != nil { m["additionalProperties"] = &x } if x := schema.Discriminator; x != nil { m["discriminator"] = x } return m, nil } // UnmarshalJSON sets Schema to a copy of data. func (schema *Schema) UnmarshalJSON(data []byte) error { type SchemaBis Schema var x SchemaBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, originKey) delete(x.Extensions, "oneOf") delete(x.Extensions, "anyOf") delete(x.Extensions, "allOf") delete(x.Extensions, "not") delete(x.Extensions, "type") delete(x.Extensions, "title") delete(x.Extensions, "format") delete(x.Extensions, "description") delete(x.Extensions, "enum") delete(x.Extensions, "default") delete(x.Extensions, "example") delete(x.Extensions, "externalDocs") // Array-related delete(x.Extensions, "uniqueItems") // Number-related delete(x.Extensions, "exclusiveMinimum") delete(x.Extensions, "exclusiveMaximum") // Properties delete(x.Extensions, "nullable") delete(x.Extensions, "readOnly") delete(x.Extensions, "writeOnly") delete(x.Extensions, "allowEmptyValue") delete(x.Extensions, "deprecated") delete(x.Extensions, "xml") // Number delete(x.Extensions, "minimum") delete(x.Extensions, "maximum") delete(x.Extensions, "multipleOf") // String delete(x.Extensions, "minLength") delete(x.Extensions, "maxLength") delete(x.Extensions, "pattern") // Array delete(x.Extensions, "minItems") delete(x.Extensions, "maxItems") delete(x.Extensions, "items") // Object delete(x.Extensions, "required") delete(x.Extensions, "properties") delete(x.Extensions, "minProperties") delete(x.Extensions, "maxProperties") delete(x.Extensions, "additionalProperties") delete(x.Extensions, "discriminator") if len(x.Extensions) == 0 { x.Extensions = nil } *schema = Schema(x) if schema.Format == "date" { // This is a fix for: https://github.com/getkin/kin-openapi/issues/697 if eg, ok := schema.Example.(string); ok { schema.Example = strings.TrimSuffix(eg, "T00:00:00Z") } } return nil } // JSONLookup implements https://pkg.go.dev/github.com/go-openapi/jsonpointer#JSONPointable func (schema Schema) JSONLookup(token string) (any, error) { switch token { case "additionalProperties": if addProps := schema.AdditionalProperties.Has; addProps != nil { return *addProps, nil } if addProps := schema.AdditionalProperties.Schema; addProps != nil { if addProps.Ref != "" { return &Ref{Ref: addProps.Ref}, nil } return addProps.Value, nil } case "not": if schema.Not != nil { if schema.Not.Ref != "" { return &Ref{Ref: schema.Not.Ref}, nil } return schema.Not.Value, nil } case "items": if schema.Items != nil { if schema.Items.Ref != "" { return &Ref{Ref: schema.Items.Ref}, nil } return schema.Items.Value, nil } case "oneOf": return schema.OneOf, nil case "anyOf": return schema.AnyOf, nil case "allOf": return schema.AllOf, nil case "type": return schema.Type, nil case "title": return schema.Title, nil case "format": return schema.Format, nil case "description": return schema.Description, nil case "enum": return schema.Enum, nil case "default": return schema.Default, nil case "example": return schema.Example, nil case "externalDocs": return schema.ExternalDocs, nil case "uniqueItems": return schema.UniqueItems, nil case "exclusiveMin": return schema.ExclusiveMin, nil case "exclusiveMax": return schema.ExclusiveMax, nil case "nullable": return schema.Nullable, nil case "readOnly": return schema.ReadOnly, nil case "writeOnly": return schema.WriteOnly, nil case "allowEmptyValue": return schema.AllowEmptyValue, nil case "xml": return schema.XML, nil case "deprecated": return schema.Deprecated, nil case "min": return schema.Min, nil case "max": return schema.Max, nil case "multipleOf": return schema.MultipleOf, nil case "minLength": return schema.MinLength, nil case "maxLength": return schema.MaxLength, nil case "pattern": return schema.Pattern, nil case "minItems": return schema.MinItems, nil case "maxItems": return schema.MaxItems, nil case "required": return schema.Required, nil case "properties": return schema.Properties, nil case "minProps": return schema.MinProps, nil case "maxProps": return schema.MaxProps, nil case "discriminator": return schema.Discriminator, nil } v, _, err := jsonpointer.GetForToken(schema.Extensions, token) return v, err } func (schema *Schema) NewRef() *SchemaRef { return &SchemaRef{ Value: schema, } } func NewOneOfSchema(schemas ...*Schema) *Schema { refs := make([]*SchemaRef, 0, len(schemas)) for _, schema := range schemas { refs = append(refs, &SchemaRef{Value: schema}) } return &Schema{ OneOf: refs, } } func NewAnyOfSchema(schemas ...*Schema) *Schema { refs := make([]*SchemaRef, 0, len(schemas)) for _, schema := range schemas { refs = append(refs, &SchemaRef{Value: schema}) } return &Schema{ AnyOf: refs, } } func NewAllOfSchema(schemas ...*Schema) *Schema { refs := make([]*SchemaRef, 0, len(schemas)) for _, schema := range schemas { refs = append(refs, &SchemaRef{Value: schema}) } return &Schema{ AllOf: refs, } } func NewBoolSchema() *Schema { return &Schema{ Type: &Types{TypeBoolean}, } } func NewFloat64Schema() *Schema { return &Schema{ Type: &Types{TypeNumber}, } } func NewIntegerSchema() *Schema { return &Schema{ Type: &Types{TypeInteger}, } } func NewInt32Schema() *Schema { return &Schema{ Type: &Types{TypeInteger}, Format: "int32", } } func NewInt64Schema() *Schema { return &Schema{ Type: &Types{TypeInteger}, Format: "int64", } } func NewStringSchema() *Schema { return &Schema{ Type: &Types{TypeString}, } } func NewDateTimeSchema() *Schema { return &Schema{ Type: &Types{TypeString}, Format: "date-time", } } func NewUUIDSchema() *Schema { return &Schema{ Type: &Types{TypeString}, Format: "uuid", } } func NewBytesSchema() *Schema { return &Schema{ Type: &Types{TypeString}, Format: "byte", } } func NewArraySchema() *Schema { return &Schema{ Type: &Types{TypeArray}, } } func NewObjectSchema() *Schema { return &Schema{ Type: &Types{TypeObject}, Properties: make(Schemas), } } func (schema *Schema) WithNullable() *Schema { schema.Nullable = true return schema } func (schema *Schema) WithMin(value float64) *Schema { schema.Min = &value return schema } func (schema *Schema) WithMax(value float64) *Schema { schema.Max = &value return schema } func (schema *Schema) WithExclusiveMin(value bool) *Schema { schema.ExclusiveMin = value return schema } func (schema *Schema) WithExclusiveMax(value bool) *Schema { schema.ExclusiveMax = value return schema } func (schema *Schema) WithEnum(values ...any) *Schema { schema.Enum = values return schema } func (schema *Schema) WithDefault(defaultValue any) *Schema { schema.Default = defaultValue return schema } func (schema *Schema) WithFormat(value string) *Schema { schema.Format = value return schema } func (schema *Schema) WithLength(i int64) *Schema { n := uint64(i) schema.MinLength = n schema.MaxLength = &n return schema } func (schema *Schema) WithMinLength(i int64) *Schema { n := uint64(i) schema.MinLength = n return schema } func (schema *Schema) WithMaxLength(i int64) *Schema { n := uint64(i) schema.MaxLength = &n return schema } func (schema *Schema) WithLengthDecodedBase64(i int64) *Schema { n := uint64(i) v := (n*8 + 5) / 6 schema.MinLength = v schema.MaxLength = &v return schema } func (schema *Schema) WithMinLengthDecodedBase64(i int64) *Schema { n := uint64(i) schema.MinLength = (n*8 + 5) / 6 return schema } func (schema *Schema) WithMaxLengthDecodedBase64(i int64) *Schema { n := uint64(i) schema.MinLength = (n*8 + 5) / 6 return schema } func (schema *Schema) WithPattern(pattern string) *Schema { schema.Pattern = pattern return schema } func (schema *Schema) WithItems(value *Schema) *Schema { schema.Items = &SchemaRef{ Value: value, } return schema } func (schema *Schema) WithMinItems(i int64) *Schema { n := uint64(i) schema.MinItems = n return schema } func (schema *Schema) WithMaxItems(i int64) *Schema { n := uint64(i) schema.MaxItems = &n return schema } func (schema *Schema) WithUniqueItems(unique bool) *Schema { schema.UniqueItems = unique return schema } func (schema *Schema) WithProperty(name string, propertySchema *Schema) *Schema { return schema.WithPropertyRef(name, &SchemaRef{ Value: propertySchema, }) } func (schema *Schema) WithPropertyRef(name string, ref *SchemaRef) *Schema { properties := schema.Properties if properties == nil { properties = make(Schemas) schema.Properties = properties } properties[name] = ref return schema } func (schema *Schema) WithProperties(properties map[string]*Schema) *Schema { result := make(Schemas, len(properties)) for k, v := range properties { result[k] = &SchemaRef{ Value: v, } } schema.Properties = result return schema } func (schema *Schema) WithRequired(required []string) *Schema { schema.Required = required return schema } func (schema *Schema) WithMinProperties(i int64) *Schema { n := uint64(i) schema.MinProps = n return schema } func (schema *Schema) WithMaxProperties(i int64) *Schema { n := uint64(i) schema.MaxProps = &n return schema } func (schema *Schema) WithAnyAdditionalProperties() *Schema { schema.AdditionalProperties = AdditionalProperties{Has: BoolPtr(true)} return schema } func (schema *Schema) WithoutAdditionalProperties() *Schema { schema.AdditionalProperties = AdditionalProperties{Has: BoolPtr(false)} return schema } func (schema *Schema) WithAdditionalProperties(v *Schema) *Schema { schema.AdditionalProperties = AdditionalProperties{} if v != nil { schema.AdditionalProperties.Schema = &SchemaRef{Value: v} } return schema } func (schema *Schema) PermitsNull() bool { return schema.Nullable || schema.Type.Includes("null") } // IsEmpty tells whether schema is equivalent to the empty schema `{}`. func (schema *Schema) IsEmpty() bool { if schema.Type != nil || schema.Format != "" || len(schema.Enum) != 0 || schema.UniqueItems || schema.ExclusiveMin || schema.ExclusiveMax || schema.Nullable || schema.ReadOnly || schema.WriteOnly || schema.AllowEmptyValue || schema.Min != nil || schema.Max != nil || schema.MultipleOf != nil || schema.MinLength != 0 || schema.MaxLength != nil || schema.Pattern != "" || schema.MinItems != 0 || schema.MaxItems != nil || len(schema.Required) != 0 || schema.MinProps != 0 || schema.MaxProps != nil { return false } if n := schema.Not; n != nil && n.Value != nil && !n.Value.IsEmpty() { return false } if ap := schema.AdditionalProperties.Schema; ap != nil && ap.Value != nil && !ap.Value.IsEmpty() { return false } if apa := schema.AdditionalProperties.Has; apa != nil && !*apa { return false } if items := schema.Items; items != nil && items.Value != nil && !items.Value.IsEmpty() { return false } for _, s := range schema.Properties { if ss := s.Value; ss != nil && !ss.IsEmpty() { return false } } for _, s := range schema.OneOf { if ss := s.Value; ss != nil && !ss.IsEmpty() { return false } } for _, s := range schema.AnyOf { if ss := s.Value; ss != nil && !ss.IsEmpty() { return false } } for _, s := range schema.AllOf { if ss := s.Value; ss != nil && !ss.IsEmpty() { return false } } return true } // Validate returns an error if Schema does not comply with the OpenAPI spec. func (schema *Schema) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) _, err := schema.validate(ctx, []*Schema{}) return err } // returns the updated stack and an error if Schema does not comply with the OpenAPI spec. func (schema *Schema) validate(ctx context.Context, stack []*Schema) ([]*Schema, error) { validationOpts := getValidationOptions(ctx) for _, existing := range stack { if existing == schema { return stack, nil } } stack = append(stack, schema) if schema.ReadOnly && schema.WriteOnly { return stack, errors.New("a property MUST NOT be marked as both readOnly and writeOnly being true") } for _, item := range schema.OneOf { v := item.Value if v == nil { return stack, foundUnresolvedRef(item.Ref) } var err error if stack, err = v.validate(ctx, stack); err != nil { return stack, err } } for _, item := range schema.AnyOf { v := item.Value if v == nil { return stack, foundUnresolvedRef(item.Ref) } var err error if stack, err = v.validate(ctx, stack); err != nil { return stack, err } } for _, item := range schema.AllOf { v := item.Value if v == nil { return stack, foundUnresolvedRef(item.Ref) } var err error if stack, err = v.validate(ctx, stack); err != nil { return stack, err } } if ref := schema.Not; ref != nil { v := ref.Value if v == nil { return stack, foundUnresolvedRef(ref.Ref) } var err error if stack, err = v.validate(ctx, stack); err != nil { return stack, err } } for _, schemaType := range schema.Type.Slice() { switch schemaType { case TypeBoolean: case TypeNumber: if format := schema.Format; len(format) > 0 { switch format { case "float", "double": default: if _, ok := SchemaNumberFormats[format]; !ok && validationOpts.schemaFormatValidationEnabled { return stack, unsupportedFormat(format) } } } case TypeInteger: if format := schema.Format; len(format) > 0 { switch format { case "int32", "int64": default: if _, ok := SchemaIntegerFormats[format]; !ok && validationOpts.schemaFormatValidationEnabled { return stack, unsupportedFormat(format) } } } case TypeString: if format := schema.Format; len(format) > 0 { switch format { // Supported by OpenAPIv3.0.3: // https://spec.openapis.org/oas/v3.0.3 case "byte", "binary", "date", "date-time", "password": // In JSON Draft-07 (not validated yet though): // https://json-schema.org/draft-07/json-schema-release-notes.html#formats case "iri", "iri-reference", "uri-template", "idn-email", "idn-hostname": case "json-pointer", "relative-json-pointer", "regex", "time": // In JSON Draft 2019-09 (not validated yet though): // https://json-schema.org/draft/2019-09/release-notes.html#format-vocabulary case "duration", "uuid": // Defined in some other specification case "email", "hostname", "ipv4", "ipv6", "uri", "uri-reference": default: if _, ok := SchemaStringFormats[format]; !ok && validationOpts.schemaFormatValidationEnabled { return stack, unsupportedFormat(format) } } } if !validationOpts.schemaPatternValidationDisabled && schema.Pattern != "" { if _, err := schema.compilePattern(validationOpts.regexCompilerFunc); err != nil { return stack, err } } case TypeArray: if schema.Items == nil { return stack, errors.New("when schema type is 'array', schema 'items' must be non-null") } case TypeObject: default: return stack, fmt.Errorf("unsupported 'type' value %q", schemaType) } } if ref := schema.Items; ref != nil { v := ref.Value if v == nil { return stack, foundUnresolvedRef(ref.Ref) } var err error if stack, err = v.validate(ctx, stack); err != nil { return stack, err } } properties := make([]string, 0, len(schema.Properties)) for name := range schema.Properties { properties = append(properties, name) } sort.Strings(properties) for _, name := range properties { ref := schema.Properties[name] v := ref.Value if v == nil { return stack, foundUnresolvedRef(ref.Ref) } var err error if stack, err = v.validate(ctx, stack); err != nil { return stack, err } } if schema.AdditionalProperties.Has != nil && schema.AdditionalProperties.Schema != nil { return stack, errors.New("additionalProperties are set to both boolean and schema") } if ref := schema.AdditionalProperties.Schema; ref != nil { v := ref.Value if v == nil { return stack, foundUnresolvedRef(ref.Ref) } var err error if stack, err = v.validate(ctx, stack); err != nil { return stack, err } } if v := schema.ExternalDocs; v != nil { if err := v.Validate(ctx); err != nil { return stack, fmt.Errorf("invalid external docs: %w", err) } } if v := schema.Default; v != nil && !validationOpts.schemaDefaultsValidationDisabled { if err := schema.VisitJSON(v); err != nil { return stack, fmt.Errorf("invalid default: %w", err) } } if x := schema.Example; x != nil && !validationOpts.examplesValidationDisabled { if err := validateExampleValue(ctx, x, schema); err != nil { return stack, fmt.Errorf("invalid example: %w", err) } } return stack, validateExtensions(ctx, schema.Extensions) } func (schema *Schema) IsMatching(value any) bool { settings := newSchemaValidationSettings(FailFast()) return schema.visitJSON(settings, value) == nil } func (schema *Schema) IsMatchingJSONBoolean(value bool) bool { settings := newSchemaValidationSettings(FailFast()) return schema.visitJSON(settings, value) == nil } func (schema *Schema) IsMatchingJSONNumber(value float64) bool { settings := newSchemaValidationSettings(FailFast()) return schema.visitJSON(settings, value) == nil } func (schema *Schema) IsMatchingJSONString(value string) bool { settings := newSchemaValidationSettings(FailFast()) return schema.visitJSON(settings, value) == nil } func (schema *Schema) IsMatchingJSONArray(value []any) bool { settings := newSchemaValidationSettings(FailFast()) return schema.visitJSON(settings, value) == nil } func (schema *Schema) IsMatchingJSONObject(value map[string]any) bool { settings := newSchemaValidationSettings(FailFast()) return schema.visitJSON(settings, value) == nil } func (schema *Schema) VisitJSON(value any, opts ...SchemaValidationOption) error { settings := newSchemaValidationSettings(opts...) return schema.visitJSON(settings, value) } func (schema *Schema) visitJSON(settings *schemaValidationSettings, value any) (err error) { switch value := value.(type) { case nil: // Don't use VisitJSONNull, as we still want to reach 'visitXOFOperations', since // those could allow for a nullable value even though this one doesn't if schema.PermitsNull() { return } case float64: if math.IsNaN(value) { return ErrSchemaInputNaN } if math.IsInf(value, 0) { return ErrSchemaInputInf } } if schema.IsEmpty() { switch value.(type) { case nil: return schema.visitJSONNull(settings) default: return } } if err = schema.visitNotOperation(settings, value); err != nil { return } var run bool if err, run = schema.visitXOFOperations(settings, value); err != nil || !run { return } if err = schema.visitEnumOperation(settings, value); err != nil { return } switch value := value.(type) { case nil: return schema.visitJSONNull(settings) case bool: return schema.visitJSONBoolean(settings, value) case json.Number: valueFloat64, err := value.Float64() if err != nil { return &SchemaError{ Value: value, Schema: schema, SchemaField: "type", Reason: "cannot convert json.Number to float64", customizeMessageError: settings.customizeMessageError, Origin: err, } } return schema.visitJSONNumber(settings, valueFloat64) case int: return schema.visitJSONNumber(settings, float64(value)) case int32: return schema.visitJSONNumber(settings, float64(value)) case int64: return schema.visitJSONNumber(settings, float64(value)) case float64: return schema.visitJSONNumber(settings, value) case string: return schema.visitJSONString(settings, value) case []any: return schema.visitJSONArray(settings, value) case map[string]any: return schema.visitJSONObject(settings, value) case map[any]any: // for YAML cf. issue https://github.com/getkin/kin-openapi/issues/444 values := make(map[string]any, len(value)) for key, v := range value { if k, ok := key.(string); ok { values[k] = v } } if len(value) == len(values) { return schema.visitJSONObject(settings, values) } } // Catch slice of non-empty interface type if reflect.TypeOf(value).Kind() == reflect.Slice { valueR := reflect.ValueOf(value) newValue := make([]any, 0, valueR.Len()) for i := 0; i < valueR.Len(); i++ { newValue = append(newValue, valueR.Index(i).Interface()) } return schema.visitJSONArray(settings, newValue) } return &SchemaError{ Value: value, Schema: schema, SchemaField: "type", Reason: fmt.Sprintf("unhandled value of type %T", value), customizeMessageError: settings.customizeMessageError, } } func (schema *Schema) visitEnumOperation(settings *schemaValidationSettings, value any) (err error) { if enum := schema.Enum; len(enum) != 0 { for _, v := range enum { switch c := value.(type) { case json.Number: var f float64 if f, err = strconv.ParseFloat(c.String(), 64); err != nil { return err } if v == f { return } case int64: if v == float64(c) { return } default: if reflect.DeepEqual(v, value) { return } } } if settings.failfast { return errSchema } allowedValues, _ := json.Marshal(enum) return &SchemaError{ Value: value, Schema: schema, SchemaField: "enum", Reason: fmt.Sprintf("value is not one of the allowed values %s", string(allowedValues)), customizeMessageError: settings.customizeMessageError, } } return } func (schema *Schema) visitNotOperation(settings *schemaValidationSettings, value any) (err error) { if ref := schema.Not; ref != nil { v := ref.Value if v == nil { return foundUnresolvedRef(ref.Ref) } if err := v.visitJSON(settings, value); err == nil { if settings.failfast { return errSchema } return &SchemaError{ Value: value, Schema: schema, SchemaField: "not", customizeMessageError: settings.customizeMessageError, } } } return } // If the XOF operations pass successfully, abort further run of validation, as they will already be satisfied (unless the schema // itself is badly specified func (schema *Schema) visitXOFOperations(settings *schemaValidationSettings, value any) (err error, run bool) { var visitedOneOf, visitedAnyOf, visitedAllOf bool if v := schema.OneOf; len(v) > 0 { var discriminatorRef string if schema.Discriminator != nil { pn := schema.Discriminator.PropertyName if valuemap, okcheck := value.(map[string]any); okcheck { discriminatorVal, okcheck := valuemap[pn] if !okcheck { return &SchemaError{ Schema: schema, SchemaField: "discriminator", Reason: fmt.Sprintf("input does not contain the discriminator property %q", pn), }, false } discriminatorValString, okcheck := discriminatorVal.(string) if !okcheck { return &SchemaError{ Value: discriminatorVal, Schema: schema, SchemaField: "discriminator", Reason: fmt.Sprintf("value of discriminator property %q is not a string", pn), }, false } if discriminatorRef, okcheck = schema.Discriminator.Mapping[discriminatorValString]; len(schema.Discriminator.Mapping) > 0 && !okcheck { return &SchemaError{ Value: discriminatorVal, Schema: schema, SchemaField: "discriminator", Reason: fmt.Sprintf("discriminator property %q has invalid value", pn), }, false } } } var ( ok = 0 validationErrors = multiErrorForOneOf{} matchedOneOfIndices = make([]int, 0) tempValue = value ) for idx, item := range v { v := item.Value if v == nil { return foundUnresolvedRef(item.Ref), false } if discriminatorRef != "" && discriminatorRef != item.Ref { continue } // make a deep copy to protect origin value from being injected default value that defined in mismatched oneOf schema if settings.asreq || settings.asrep { tempValue = deepcopy.Copy(value) } if err := v.visitJSON(settings, tempValue); err != nil { validationErrors = append(validationErrors, err) continue } matchedOneOfIndices = append(matchedOneOfIndices, idx) ok++ } if ok != 1 { if settings.failfast { return errSchema, false } e := &SchemaError{ Value: value, Schema: schema, SchemaField: "oneOf", customizeMessageError: settings.customizeMessageError, } if ok > 1 { e.Origin = ErrOneOfConflict e.Reason = fmt.Sprintf(`value matches more than one schema from "oneOf" (matches schemas at indices %v)`, matchedOneOfIndices) } else { e.Origin = fmt.Errorf("doesn't match schema due to: %w", validationErrors) e.Reason = `value doesn't match any schema from "oneOf"` } return e, false } // run again to inject default value that defined in matched oneOf schema if settings.asreq || settings.asrep { _ = v[matchedOneOfIndices[0]].Value.visitJSON(settings, value) } visitedOneOf = true } if v := schema.AnyOf; len(v) > 0 { var ( ok = false matchedAnyOfIdx = 0 tempValue = value ) for idx, item := range v { v := item.Value if v == nil { return foundUnresolvedRef(item.Ref), false } // make a deep copy to protect origin value from being injected default value that defined in mismatched anyOf schema if settings.asreq || settings.asrep { tempValue = deepcopy.Copy(value) } if err := v.visitJSON(settings, tempValue); err == nil { ok = true matchedAnyOfIdx = idx break } } if !ok { if settings.failfast { return errSchema, false } return &SchemaError{ Value: value, Schema: schema, SchemaField: "anyOf", Reason: `doesn't match any schema from "anyOf"`, customizeMessageError: settings.customizeMessageError, }, false } _ = v[matchedAnyOfIdx].Value.visitJSON(settings, value) visitedAnyOf = true } for _, item := range schema.AllOf { v := item.Value if v == nil { return foundUnresolvedRef(item.Ref), false } if err := v.visitJSON(settings, value); err != nil { if settings.failfast { return errSchema, false } return &SchemaError{ Value: value, Schema: schema, SchemaField: "allOf", Reason: `doesn't match all schemas from "allOf"`, Origin: err, customizeMessageError: settings.customizeMessageError, }, false } visitedAllOf = true } run = !((visitedOneOf || visitedAnyOf || visitedAllOf) && value == nil) return } // The value is not considered in visitJSONNull because according to the spec // "null is not supported as a type" unless `nullable` is also set to true // https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#data-types // https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#schema-object func (schema *Schema) visitJSONNull(settings *schemaValidationSettings) (err error) { if schema.PermitsNull() { return } if settings.failfast { return errSchema } return &SchemaError{ Value: nil, Schema: schema, SchemaField: "nullable", Reason: "Value is not nullable", customizeMessageError: settings.customizeMessageError, } } func (schema *Schema) VisitJSONBoolean(value bool) error { settings := newSchemaValidationSettings() return schema.visitJSONBoolean(settings, value) } func (schema *Schema) visitJSONBoolean(settings *schemaValidationSettings, value bool) (err error) { if !schema.Type.Permits(TypeBoolean) { return schema.expectedType(settings, value) } return } func (schema *Schema) VisitJSONNumber(value float64) error { settings := newSchemaValidationSettings() return schema.visitJSONNumber(settings, value) } func (schema *Schema) visitJSONNumber(settings *schemaValidationSettings, value float64) error { var me MultiError schemaType := schema.Type requireInteger := false if schemaType.Permits(TypeInteger) && !schemaType.Permits(TypeNumber) { requireInteger = true if bigFloat := big.NewFloat(value); !bigFloat.IsInt() { if settings.failfast { return errSchema } err := &SchemaError{ Value: value, Schema: schema, SchemaField: "type", Reason: "value must be an integer", customizeMessageError: settings.customizeMessageError, } if !settings.multiError { return err } me = append(me, err) } } else if !(schemaType.Permits(TypeInteger) || schemaType.Permits(TypeNumber)) { return schema.expectedType(settings, value) } // formats var formatStrErr string var formatErr error format := schema.Format if format != "" { if requireInteger { if f, ok := SchemaIntegerFormats[format]; ok { if err := f.Validate(int64(value)); err != nil { var reason string schemaErr := &SchemaError{} if errors.As(err, &schemaErr) { reason = schemaErr.Reason } else { reason = err.Error() } formatStrErr = fmt.Sprintf(`integer doesn't match the format %q (%v)`, format, reason) formatErr = fmt.Errorf("integer doesn't match the format %q: %w", format, err) } } } else { if f, ok := SchemaNumberFormats[format]; ok { if err := f.Validate(value); err != nil { var reason string schemaErr := &SchemaError{} if errors.As(err, &schemaErr) { reason = schemaErr.Reason } else { reason = err.Error() } formatStrErr = fmt.Sprintf(`number doesn't match the format %q (%v)`, format, reason) formatErr = fmt.Errorf("number doesn't match the format %q: %w", format, err) } } } } if formatStrErr != "" || formatErr != nil { err := &SchemaError{ Value: value, Schema: schema, SchemaField: "format", Reason: formatStrErr, Origin: formatErr, customizeMessageError: settings.customizeMessageError, } if !settings.multiError { return err } me = append(me, err) } // "exclusiveMinimum" if v := schema.ExclusiveMin; v && !(*schema.Min < value) { if settings.failfast { return errSchema } err := &SchemaError{ Value: value, Schema: schema, SchemaField: "exclusiveMinimum", Reason: fmt.Sprintf("number must be more than %g", *schema.Min), customizeMessageError: settings.customizeMessageError, } if !settings.multiError { return err } me = append(me, err) } // "exclusiveMaximum" if v := schema.ExclusiveMax; v && !(*schema.Max > value) { if settings.failfast { return errSchema } err := &SchemaError{ Value: value, Schema: schema, SchemaField: "exclusiveMaximum", Reason: fmt.Sprintf("number must be less than %g", *schema.Max), customizeMessageError: settings.customizeMessageError, } if !settings.multiError { return err } me = append(me, err) } // "minimum" if v := schema.Min; v != nil && !(*v <= value) { if settings.failfast { return errSchema } err := &SchemaError{ Value: value, Schema: schema, SchemaField: "minimum", Reason: fmt.Sprintf("number must be at least %g", *v), customizeMessageError: settings.customizeMessageError, } if !settings.multiError { return err } me = append(me, err) } // "maximum" if v := schema.Max; v != nil && !(*v >= value) { if settings.failfast { return errSchema } err := &SchemaError{ Value: value, Schema: schema, SchemaField: "maximum", Reason: fmt.Sprintf("number must be at most %g", *v), customizeMessageError: settings.customizeMessageError, } if !settings.multiError { return err } me = append(me, err) } // "multipleOf" if v := schema.MultipleOf; v != nil { // "A numeric instance is valid only if division by this keyword's // value results in an integer." if bigFloat := big.NewFloat(value / *v); !bigFloat.IsInt() { if settings.failfast { return errSchema } err := &SchemaError{ Value: value, Schema: schema, SchemaField: "multipleOf", Reason: fmt.Sprintf("number must be a multiple of %g", *v), customizeMessageError: settings.customizeMessageError, } if !settings.multiError { return err } me = append(me, err) } } if len(me) > 0 { return me } return nil } func (schema *Schema) VisitJSONString(value string) error { settings := newSchemaValidationSettings() return schema.visitJSONString(settings, value) } func (schema *Schema) visitJSONString(settings *schemaValidationSettings, value string) error { if !schema.Type.Permits(TypeString) { return schema.expectedType(settings, value) } var me MultiError // "minLength" and "maxLength" minLength := schema.MinLength maxLength := schema.MaxLength if minLength != 0 || maxLength != nil { // JSON schema string lengths are UTF-16, not UTF-8! length := int64(0) for _, r := range value { if utf16.IsSurrogate(r) { length += 2 } else { length++ } } if minLength != 0 && length < int64(minLength) { if settings.failfast { return errSchema } err := &SchemaError{ Value: value, Schema: schema, SchemaField: "minLength", Reason: fmt.Sprintf("minimum string length is %d", minLength), customizeMessageError: settings.customizeMessageError, } if !settings.multiError { return err } me = append(me, err) } if maxLength != nil && length > int64(*maxLength) { if settings.failfast { return errSchema } err := &SchemaError{ Value: value, Schema: schema, SchemaField: "maxLength", Reason: fmt.Sprintf("maximum string length is %d", *maxLength), customizeMessageError: settings.customizeMessageError, } if !settings.multiError { return err } me = append(me, err) } } // "pattern" if !settings.patternValidationDisabled && schema.Pattern != "" { cpiface, _ := compiledPatterns.Load(schema.Pattern) cp, _ := cpiface.(RegexMatcher) if cp == nil { var err error if cp, err = schema.compilePattern(settings.regexCompiler); err != nil { if !settings.multiError { return err } me = append(me, err) } } if !cp.MatchString(value) { err := &SchemaError{ Value: value, Schema: schema, SchemaField: "pattern", Reason: fmt.Sprintf(`string doesn't match the regular expression "%s"`, schema.Pattern), customizeMessageError: settings.customizeMessageError, } if !settings.multiError { return err } me = append(me, err) } } // "format" var formatStrErr string var formatErr error if format := schema.Format; format != "" { if f, ok := SchemaStringFormats[format]; ok { if err := f.Validate(value); err != nil { var reason string schemaErr := &SchemaError{} if errors.As(err, &schemaErr) { reason = schemaErr.Reason } else { reason = err.Error() } formatStrErr = fmt.Sprintf(`string doesn't match the format %q (%v)`, format, reason) formatErr = fmt.Errorf("string doesn't match the format %q: %w", format, err) } } } if formatStrErr != "" || formatErr != nil { err := &SchemaError{ Value: value, Schema: schema, SchemaField: "format", Reason: formatStrErr, Origin: formatErr, customizeMessageError: settings.customizeMessageError, } if !settings.multiError { return err } me = append(me, err) } if len(me) > 0 { return me } return nil } func (schema *Schema) VisitJSONArray(value []any) error { settings := newSchemaValidationSettings() return schema.visitJSONArray(settings, value) } func (schema *Schema) visitJSONArray(settings *schemaValidationSettings, value []any) error { if !schema.Type.Permits(TypeArray) { return schema.expectedType(settings, value) } var me MultiError lenValue := int64(len(value)) // "minItems" if v := schema.MinItems; v != 0 && lenValue < int64(v) { if settings.failfast { return errSchema } err := &SchemaError{ Value: value, Schema: schema, SchemaField: "minItems", Reason: fmt.Sprintf("minimum number of items is %d", v), customizeMessageError: settings.customizeMessageError, } if !settings.multiError { return err } me = append(me, err) } // "maxItems" if v := schema.MaxItems; v != nil && lenValue > int64(*v) { if settings.failfast { return errSchema } err := &SchemaError{ Value: value, Schema: schema, SchemaField: "maxItems", Reason: fmt.Sprintf("maximum number of items is %d", *v), customizeMessageError: settings.customizeMessageError, } if !settings.multiError { return err } me = append(me, err) } // "uniqueItems" if sliceUniqueItemsChecker == nil { sliceUniqueItemsChecker = isSliceOfUniqueItems } if v := schema.UniqueItems; v && !sliceUniqueItemsChecker(value) { if settings.failfast { return errSchema } err := &SchemaError{ Value: value, Schema: schema, SchemaField: "uniqueItems", Reason: "duplicate items found", customizeMessageError: settings.customizeMessageError, } if !settings.multiError { return err } me = append(me, err) } // "items" if itemSchemaRef := schema.Items; itemSchemaRef != nil { itemSchema := itemSchemaRef.Value if itemSchema == nil { return foundUnresolvedRef(itemSchemaRef.Ref) } for i, item := range value { if err := itemSchema.visitJSON(settings, item); err != nil { err = markSchemaErrorIndex(err, i) if !settings.multiError { return err } if itemMe, ok := err.(MultiError); ok { me = append(me, itemMe...) } else { me = append(me, err) } } } } if len(me) > 0 { return me } return nil } func (schema *Schema) VisitJSONObject(value map[string]any) error { settings := newSchemaValidationSettings() return schema.visitJSONObject(settings, value) } func (schema *Schema) visitJSONObject(settings *schemaValidationSettings, value map[string]any) error { if !schema.Type.Permits(TypeObject) { return schema.expectedType(settings, value) } var me MultiError if settings.asreq || settings.asrep { properties := make([]string, 0, len(schema.Properties)) for propName := range schema.Properties { properties = append(properties, propName) } sort.Strings(properties) for _, propName := range properties { propSchema := schema.Properties[propName] reqRO := settings.asreq && propSchema.Value.ReadOnly && !settings.readOnlyValidationDisabled repWO := settings.asrep && propSchema.Value.WriteOnly && !settings.writeOnlyValidationDisabled if f := settings.defaultsSet; f != nil && value[propName] == nil { if dflt := propSchema.Value.Default; dflt != nil && !reqRO && !repWO { value[propName] = dflt settings.onceSettingDefaults.Do(f) } } if value[propName] != nil { if reqRO { me = append(me, fmt.Errorf("readOnly property %q in request", propName)) } else if repWO { me = append(me, fmt.Errorf("writeOnly property %q in response", propName)) } } } } // "properties" properties := schema.Properties lenValue := int64(len(value)) // "minProperties" if v := schema.MinProps; v != 0 && lenValue < int64(v) { if settings.failfast { return errSchema } err := &SchemaError{ Value: value, Schema: schema, SchemaField: "minProperties", Reason: fmt.Sprintf("there must be at least %d properties", v), customizeMessageError: settings.customizeMessageError, } if !settings.multiError { return err } me = append(me, err) } // "maxProperties" if v := schema.MaxProps; v != nil && lenValue > int64(*v) { if settings.failfast { return errSchema } err := &SchemaError{ Value: value, Schema: schema, SchemaField: "maxProperties", Reason: fmt.Sprintf("there must be at most %d properties", *v), customizeMessageError: settings.customizeMessageError, } if !settings.multiError { return err } me = append(me, err) } // "additionalProperties" var additionalProperties *Schema if ref := schema.AdditionalProperties.Schema; ref != nil { additionalProperties = ref.Value } keys := make([]string, 0, len(value)) for k := range value { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { v := value[k] if properties != nil { propertyRef := properties[k] if propertyRef != nil { p := propertyRef.Value if p == nil { return foundUnresolvedRef(propertyRef.Ref) } if err := p.visitJSON(settings, v); err != nil { if settings.failfast { return errSchema } err = markSchemaErrorKey(err, k) if !settings.multiError { return err } if v, ok := err.(MultiError); ok { me = append(me, v...) continue } me = append(me, err) } continue } } if allowed := schema.AdditionalProperties.Has; allowed == nil || *allowed { if additionalProperties != nil { if err := additionalProperties.visitJSON(settings, v); err != nil { if settings.failfast { return errSchema } err = markSchemaErrorKey(err, k) if !settings.multiError { return err } if v, ok := err.(MultiError); ok { me = append(me, v...) continue } me = append(me, err) } } continue } if settings.failfast { return errSchema } err := &SchemaError{ Value: value, Schema: schema, SchemaField: "properties", Reason: fmt.Sprintf("property %q is unsupported", k), customizeMessageError: settings.customizeMessageError, } if !settings.multiError { return err } me = append(me, err) } // "required" for _, k := range schema.Required { if _, ok := value[k]; !ok { if s := schema.Properties[k]; s != nil && s.Value.ReadOnly && settings.asreq { continue } if s := schema.Properties[k]; s != nil && s.Value.WriteOnly && settings.asrep { continue } if settings.failfast { return errSchema } err := markSchemaErrorKey(&SchemaError{ Value: value, Schema: schema, SchemaField: "required", Reason: fmt.Sprintf("property %q is missing", k), customizeMessageError: settings.customizeMessageError, }, k) if !settings.multiError { return err } me = append(me, err) } } if len(me) > 0 { return me } return nil } func (schema *Schema) expectedType(settings *schemaValidationSettings, value any) error { if settings.failfast { return errSchema } a := "a" var x string schemaTypes := (*schema.Type) if len(schemaTypes) == 1 { x = schemaTypes[0] switch x { case TypeArray, TypeObject, TypeInteger: a = "an" } } else { a = "one of" x = strings.Join(schemaTypes, ", ") } return &SchemaError{ Value: value, Schema: schema, SchemaField: "type", Reason: fmt.Sprintf("value must be %s %s", a, x), customizeMessageError: settings.customizeMessageError, } } // SchemaError is an error that occurs during schema validation. type SchemaError struct { // Value is the value that failed validation. Value any // reversePath is the path to the value that failed validation. reversePath []string // Schema is the schema that failed validation. Schema *Schema // SchemaField is the field of the schema that failed validation. SchemaField string // Reason is a human-readable message describing the error. // The message should never include the original value to prevent leakage of potentially sensitive inputs in error messages. Reason string // Origin is the original error that caused this error. Origin error // customizeMessageError is a function that can be used to customize the error message. customizeMessageError func(err *SchemaError) string } var _ interface{ Unwrap() error } = SchemaError{} func markSchemaErrorKey(err error, key string) error { if v, ok := err.(*SchemaError); ok { v.reversePath = append(v.reversePath, key) if v.Origin != nil { if unwrapped := errors.Unwrap(v.Origin); unwrapped != nil { if me, ok := unwrapped.(multiErrorForOneOf); ok { _ = markSchemaErrorKey(MultiError(me), key) } } } return v } if v, ok := err.(MultiError); ok { for _, e := range v { _ = markSchemaErrorKey(e, key) } return v } return err } func markSchemaErrorIndex(err error, index int) error { return markSchemaErrorKey(err, strconv.FormatInt(int64(index), 10)) } func (err *SchemaError) JSONPointer() []string { reversePath := err.reversePath path := append([]string(nil), reversePath...) for left, right := 0, len(path)-1; left < right; left, right = left+1, right-1 { path[left], path[right] = path[right], path[left] } return path } func (err *SchemaError) Error() string { if err.customizeMessageError != nil { if msg := err.customizeMessageError(err); msg != "" { return msg } } buf := bytes.NewBuffer(make([]byte, 0, 256)) if len(err.reversePath) > 0 { buf.WriteString(`Error at "`) reversePath := err.reversePath for i := len(reversePath) - 1; i >= 0; i-- { buf.WriteByte('/') buf.WriteString(reversePath[i]) } buf.WriteString(`": `) } if err.Origin != nil { buf.WriteString(err.Origin.Error()) return buf.String() } reason := err.Reason if reason == "" { buf.WriteString(`Doesn't match schema "`) buf.WriteString(err.SchemaField) buf.WriteString(`"`) } else { buf.WriteString(reason) } if !SchemaErrorDetailsDisabled { buf.WriteString("\nSchema:\n ") encoder := json.NewEncoder(buf) encoder.SetIndent(" ", " ") if err := encoder.Encode(err.Schema); err != nil { panic(err) } buf.WriteString("\nValue:\n ") if err := encoder.Encode(err.Value); err != nil { panic(err) } } return buf.String() } func (err SchemaError) Unwrap() error { return err.Origin } func isSliceOfUniqueItems(xs []any) bool { s := len(xs) m := make(map[string]struct{}, s) for _, x := range xs { // The input slice is converted from a JSON string, there shall // have no error when convert it back. key, _ := json.Marshal(&x) m[string(key)] = struct{}{} } return s == len(m) } // SliceUniqueItemsChecker is an function used to check if an given slice // have unique items. type SliceUniqueItemsChecker func(items []any) bool // By default using predefined func isSliceOfUniqueItems which make use of // json.Marshal to generate a key for map used to check if a given slice // have unique items. var sliceUniqueItemsChecker SliceUniqueItemsChecker = isSliceOfUniqueItems // RegisterArrayUniqueItemsChecker is used to register a customized function // used to check if JSON array have unique items. func RegisterArrayUniqueItemsChecker(fn SliceUniqueItemsChecker) { sliceUniqueItemsChecker = fn } func unsupportedFormat(format string) error { return fmt.Errorf("unsupported 'format' value %q", format) } // UnmarshalJSON sets Schemas to a copy of data. func (schemas *Schemas) UnmarshalJSON(data []byte) (err error) { *schemas, _, err = unmarshalStringMapP[SchemaRef](data) return } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/schema_formats.go ================================================ package openapi3 import ( "fmt" "math" "net/netip" "regexp" ) type ( // FormatValidator is an interface for custom format validators. FormatValidator[T any] interface { Validate(value T) error } // StringFormatValidator is a type alias for FormatValidator[string] StringFormatValidator = FormatValidator[string] // NumberFormatValidator is a type alias for FormatValidator[float64] NumberFormatValidator = FormatValidator[float64] // IntegerFormatValidator is a type alias for FormatValidator[int64] IntegerFormatValidator = FormatValidator[int64] ) var ( // SchemaStringFormats is a map of custom string format validators. SchemaStringFormats = make(map[string]StringFormatValidator) // SchemaNumberFormats is a map of custom number format validators. SchemaNumberFormats = make(map[string]NumberFormatValidator) // SchemaIntegerFormats is a map of custom integer format validators. SchemaIntegerFormats = make(map[string]IntegerFormatValidator) ) const ( // FormatOfStringForUUIDOfRFC4122 is an optional predefined format for UUID v1-v5 as specified by RFC4122 FormatOfStringForUUIDOfRFC4122 = `^(?:[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000)$` // FormatOfStringForEmail pattern catches only some suspiciously wrong-looking email addresses. // Use DefineStringFormat(...) if you need something stricter. FormatOfStringForEmail = `^[^@]+@[^@<>",\s]+$` // FormatOfStringByte is a regexp for base64-encoded characters, for example, "U3dhZ2dlciByb2Nrcw==" FormatOfStringByte = `(^$|^[a-zA-Z0-9+/\-_]*=*$)` // FormatOfStringDate is a RFC3339 date format regexp, for example "2017-07-21". FormatOfStringDate = `^[0-9]{4}-(0[1-9]|10|11|12)-(0[1-9]|[12][0-9]|3[01])$` // FormatOfStringDateTime is a RFC3339 date-time format regexp, for example "2017-07-21T17:32:28Z". FormatOfStringDateTime = `^[0-9]{4}-(0[1-9]|10|11|12)-(0[1-9]|[12][0-9]|3[01])T([0-1][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\.[0-9]+)?(Z|(\+|-)[0-9]{2}:[0-9]{2})?$` ) func init() { DefineStringFormatValidator("byte", NewRegexpFormatValidator(FormatOfStringByte)) DefineStringFormatValidator("date", NewRegexpFormatValidator(FormatOfStringDate)) DefineStringFormatValidator("date-time", NewRegexpFormatValidator(FormatOfStringDateTime)) DefineIntegerFormatValidator("int32", NewRangeFormatValidator(int64(math.MinInt32), int64(math.MaxInt32))) DefineIntegerFormatValidator("int64", NewRangeFormatValidator(int64(math.MinInt64), int64(math.MaxInt64))) } // DefineIPv4Format opts in ipv4 format validation on top of OAS 3 spec func DefineIPv4Format() { DefineStringFormatValidator("ipv4", NewIPValidator(true)) } // DefineIPv6Format opts in ipv6 format validation on top of OAS 3 spec func DefineIPv6Format() { DefineStringFormatValidator("ipv6", NewIPValidator(false)) } type stringRegexpFormatValidator struct { re *regexp.Regexp } func (s stringRegexpFormatValidator) Validate(value string) error { if !s.re.MatchString(value) { return fmt.Errorf(`string doesn't match pattern "%s"`, s.re.String()) } return nil } type callbackValidator[T any] struct { fn func(T) error } func (c callbackValidator[T]) Validate(value T) error { return c.fn(value) } type rangeFormat[T int64 | float64] struct { min, max T } func (r rangeFormat[T]) Validate(value T) error { if value < r.min || value > r.max { return fmt.Errorf("value should be between %v and %v", r.min, r.max) } return nil } // NewRangeFormatValidator creates a new FormatValidator that validates the value is within a given range. func NewRangeFormatValidator[T int64 | float64](min, max T) FormatValidator[T] { return rangeFormat[T]{min: min, max: max} } // NewRegexpFormatValidator creates a new FormatValidator that uses a regular expression to validate the value. func NewRegexpFormatValidator(pattern string) StringFormatValidator { re, err := regexp.Compile(pattern) if err != nil { err := fmt.Errorf("string regexp format has invalid pattern %q: %w", pattern, err) panic(err) } return stringRegexpFormatValidator{re: re} } // NewCallbackValidator creates a new FormatValidator that uses a callback function to validate the value. func NewCallbackValidator[T any](fn func(T) error) FormatValidator[T] { return callbackValidator[T]{fn: fn} } // DefineStringFormatValidator defines a custom format validator for a given string format. func DefineStringFormatValidator(name string, validator StringFormatValidator) { SchemaStringFormats[name] = validator } // DefineNumberFormatValidator defines a custom format validator for a given number format. func DefineNumberFormatValidator(name string, validator NumberFormatValidator) { SchemaNumberFormats[name] = validator } // DefineIntegerFormatValidator defines a custom format validator for a given integer format. func DefineIntegerFormatValidator(name string, validator IntegerFormatValidator) { SchemaIntegerFormats[name] = validator } // DefineStringFormat defines a regexp pattern for a given string format // // Deprecated: Use openapi3.DefineStringFormatValidator(name, NewRegexpFormatValidator(pattern)) instead. func DefineStringFormat(name string, pattern string) { DefineStringFormatValidator(name, NewRegexpFormatValidator(pattern)) } // DefineStringFormatCallback defines a callback function for a given string format // // Deprecated: Use openapi3.DefineStringFormatValidator(name, NewCallbackValidator(fn)) instead. func DefineStringFormatCallback(name string, callback func(string) error) { DefineStringFormatValidator(name, NewCallbackValidator(callback)) } // NewIPValidator creates a new FormatValidator that validates the value is an IP address. func NewIPValidator(isIPv4 bool) FormatValidator[string] { return callbackValidator[string]{fn: func(ip string) error { addr, err := netip.ParseAddr(ip) if err != nil { return &SchemaError{ Value: ip, Reason: "Not an IP address", } } if isIPv4 && !addr.Is4() { return &SchemaError{ Value: ip, Reason: "Not an IPv4 address (it's IPv6)", } } if !isIPv4 && !addr.Is6() { return &SchemaError{ Value: ip, Reason: "Not an IPv6 address (it's IPv4)", } } return nil }} } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/schema_pattern.go ================================================ package openapi3 import ( "fmt" "regexp" ) var patRewriteCodepoints = regexp.MustCompile(`(?P\\u)(?P[0-9A-F]{4})`) // See https://pkg.go.dev/regexp/syntax func intoGoRegexp(re string) string { return patRewriteCodepoints.ReplaceAllString(re, `\x{${code}}`) } // NOTE: racey WRT [writes to schema.Pattern] vs [reads schema.Pattern then writes to compiledPatterns] func (schema *Schema) compilePattern(c RegexCompilerFunc) (cp RegexMatcher, err error) { pattern := schema.Pattern if c != nil { cp, err = c(pattern) } else { cp, err = regexp.Compile(intoGoRegexp(pattern)) } if err != nil { err = &SchemaError{ Schema: schema, SchemaField: "pattern", Origin: err, Reason: fmt.Sprintf("cannot compile pattern %q: %v", pattern, err), } return } var _ bool = compiledPatterns.CompareAndSwap(pattern, nil, cp) return } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/schema_validation_settings.go ================================================ package openapi3 import ( "sync" ) // SchemaValidationOption describes options a user has when validating request / response bodies. type SchemaValidationOption func(*schemaValidationSettings) type RegexCompilerFunc func(expr string) (RegexMatcher, error) type RegexMatcher interface { MatchString(s string) bool } type schemaValidationSettings struct { failfast bool multiError bool asreq, asrep bool // exclusive (XOR) fields formatValidationEnabled bool patternValidationDisabled bool readOnlyValidationDisabled bool writeOnlyValidationDisabled bool regexCompiler RegexCompilerFunc onceSettingDefaults sync.Once defaultsSet func() customizeMessageError func(err *SchemaError) string } // FailFast returns schema validation errors quicker. func FailFast() SchemaValidationOption { return func(s *schemaValidationSettings) { s.failfast = true } } func MultiErrors() SchemaValidationOption { return func(s *schemaValidationSettings) { s.multiError = true } } func VisitAsRequest() SchemaValidationOption { return func(s *schemaValidationSettings) { s.asreq, s.asrep = true, false } } func VisitAsResponse() SchemaValidationOption { return func(s *schemaValidationSettings) { s.asreq, s.asrep = false, true } } // EnableFormatValidation setting makes Validate not return an error when validating documents that mention schema formats that are not defined by the OpenAPIv3 specification. func EnableFormatValidation() SchemaValidationOption { return func(s *schemaValidationSettings) { s.formatValidationEnabled = true } } // DisablePatternValidation setting makes Validate not return an error when validating patterns that are not supported by the Go regexp engine. func DisablePatternValidation() SchemaValidationOption { return func(s *schemaValidationSettings) { s.patternValidationDisabled = true } } // DisableReadOnlyValidation setting makes Validate not return an error when validating properties marked as read-only func DisableReadOnlyValidation() SchemaValidationOption { return func(s *schemaValidationSettings) { s.readOnlyValidationDisabled = true } } // DisableWriteOnlyValidation setting makes Validate not return an error when validating properties marked as write-only func DisableWriteOnlyValidation() SchemaValidationOption { return func(s *schemaValidationSettings) { s.writeOnlyValidationDisabled = true } } // DefaultsSet executes the given callback (once) IFF schema validation set default values. func DefaultsSet(f func()) SchemaValidationOption { return func(s *schemaValidationSettings) { s.defaultsSet = f } } // SetSchemaErrorMessageCustomizer allows to override the schema error message. // If the passed function returns an empty string, it returns to the previous Error() implementation. func SetSchemaErrorMessageCustomizer(f func(err *SchemaError) string) SchemaValidationOption { return func(s *schemaValidationSettings) { s.customizeMessageError = f } } // SetSchemaRegexCompiler allows to override the regex implementation used to validate field "pattern". func SetSchemaRegexCompiler(c RegexCompilerFunc) SchemaValidationOption { return func(s *schemaValidationSettings) { s.regexCompiler = c } } func newSchemaValidationSettings(opts ...SchemaValidationOption) *schemaValidationSettings { settings := &schemaValidationSettings{} for _, opt := range opts { opt(settings) } return settings } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/security_requirements.go ================================================ package openapi3 import ( "context" ) type SecurityRequirements []SecurityRequirement func NewSecurityRequirements() *SecurityRequirements { return &SecurityRequirements{} } func (srs *SecurityRequirements) With(securityRequirement SecurityRequirement) *SecurityRequirements { *srs = append(*srs, securityRequirement) return srs } // Validate returns an error if SecurityRequirements does not comply with the OpenAPI spec. func (srs SecurityRequirements) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) for _, security := range srs { if err := security.Validate(ctx); err != nil { return err } } return nil } // SecurityRequirement is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#security-requirement-object type SecurityRequirement map[string][]string func NewSecurityRequirement() SecurityRequirement { return make(SecurityRequirement) } func (security SecurityRequirement) Authenticate(provider string, scopes ...string) SecurityRequirement { if len(scopes) == 0 { scopes = []string{} // Forces the variable to be encoded as an array instead of null } security[provider] = scopes return security } // Validate returns an error if SecurityRequirement does not comply with the OpenAPI spec. func (security *SecurityRequirement) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) return nil } // UnmarshalJSON sets SecurityRequirement to a copy of data. func (security *SecurityRequirement) UnmarshalJSON(data []byte) (err error) { *security, _, err = unmarshalStringMap[[]string](data) return } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/security_scheme.go ================================================ package openapi3 import ( "context" "encoding/json" "errors" "fmt" "net/url" ) // SecurityScheme is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#security-scheme-object type SecurityScheme struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Type string `json:"type,omitempty" yaml:"type,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"` In string `json:"in,omitempty" yaml:"in,omitempty"` Scheme string `json:"scheme,omitempty" yaml:"scheme,omitempty"` BearerFormat string `json:"bearerFormat,omitempty" yaml:"bearerFormat,omitempty"` Flows *OAuthFlows `json:"flows,omitempty" yaml:"flows,omitempty"` OpenIdConnectUrl string `json:"openIdConnectUrl,omitempty" yaml:"openIdConnectUrl,omitempty"` } func NewSecurityScheme() *SecurityScheme { return &SecurityScheme{} } func NewCSRFSecurityScheme() *SecurityScheme { return &SecurityScheme{ Type: "apiKey", In: "header", Name: "X-XSRF-TOKEN", } } func NewOIDCSecurityScheme(oidcUrl string) *SecurityScheme { return &SecurityScheme{ Type: "openIdConnect", OpenIdConnectUrl: oidcUrl, } } func NewJWTSecurityScheme() *SecurityScheme { return &SecurityScheme{ Type: "http", Scheme: "bearer", BearerFormat: "JWT", } } // MarshalJSON returns the JSON encoding of SecurityScheme. func (ss SecurityScheme) MarshalJSON() ([]byte, error) { x, err := ss.MarshalYAML() if err != nil { return nil, err } return json.Marshal(x) } // MarshalYAML returns the YAML encoding of SecurityScheme. func (ss SecurityScheme) MarshalYAML() (any, error) { m := make(map[string]any, 8+len(ss.Extensions)) for k, v := range ss.Extensions { m[k] = v } if x := ss.Type; x != "" { m["type"] = x } if x := ss.Description; x != "" { m["description"] = x } if x := ss.Name; x != "" { m["name"] = x } if x := ss.In; x != "" { m["in"] = x } if x := ss.Scheme; x != "" { m["scheme"] = x } if x := ss.BearerFormat; x != "" { m["bearerFormat"] = x } if x := ss.Flows; x != nil { m["flows"] = x } if x := ss.OpenIdConnectUrl; x != "" { m["openIdConnectUrl"] = x } return m, nil } // UnmarshalJSON sets SecurityScheme to a copy of data. func (ss *SecurityScheme) UnmarshalJSON(data []byte) error { type SecuritySchemeBis SecurityScheme var x SecuritySchemeBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, originKey) delete(x.Extensions, "type") delete(x.Extensions, "description") delete(x.Extensions, "name") delete(x.Extensions, "in") delete(x.Extensions, "scheme") delete(x.Extensions, "bearerFormat") delete(x.Extensions, "flows") delete(x.Extensions, "openIdConnectUrl") if len(x.Extensions) == 0 { x.Extensions = nil } *ss = SecurityScheme(x) return nil } func (ss *SecurityScheme) WithType(value string) *SecurityScheme { ss.Type = value return ss } func (ss *SecurityScheme) WithDescription(value string) *SecurityScheme { ss.Description = value return ss } func (ss *SecurityScheme) WithName(value string) *SecurityScheme { ss.Name = value return ss } func (ss *SecurityScheme) WithIn(value string) *SecurityScheme { ss.In = value return ss } func (ss *SecurityScheme) WithScheme(value string) *SecurityScheme { ss.Scheme = value return ss } func (ss *SecurityScheme) WithBearerFormat(value string) *SecurityScheme { ss.BearerFormat = value return ss } // Validate returns an error if SecurityScheme does not comply with the OpenAPI spec. func (ss *SecurityScheme) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) hasIn := false hasBearerFormat := false hasFlow := false switch ss.Type { case "apiKey": hasIn = true case "http": scheme := ss.Scheme switch scheme { case "bearer": hasBearerFormat = true case "basic", "negotiate", "digest": default: return fmt.Errorf("security scheme of type 'http' has invalid 'scheme' value %q", scheme) } case "oauth2": hasFlow = true case "openIdConnect": if ss.OpenIdConnectUrl == "" { return fmt.Errorf("no OIDC URL found for openIdConnect security scheme %q", ss.Name) } default: return fmt.Errorf("security scheme 'type' can't be %q", ss.Type) } // Validate "in" and "name" if hasIn { switch ss.In { case "query", "header", "cookie": default: return fmt.Errorf("security scheme of type 'apiKey' should have 'in'. It can be 'query', 'header' or 'cookie', not %q", ss.In) } if ss.Name == "" { return errors.New("security scheme of type 'apiKey' should have 'name'") } } else if len(ss.In) > 0 { return fmt.Errorf("security scheme of type %q can't have 'in'", ss.Type) } else if len(ss.Name) > 0 { return fmt.Errorf("security scheme of type %q can't have 'name'", ss.Type) } // Validate "format" // "bearerFormat" is an arbitrary string so we only check if the scheme supports it if !hasBearerFormat && len(ss.BearerFormat) > 0 { return fmt.Errorf("security scheme of type %q can't have 'bearerFormat'", ss.Type) } // Validate "flow" if hasFlow { flow := ss.Flows if flow == nil { return fmt.Errorf("security scheme of type %q should have 'flows'", ss.Type) } if err := flow.Validate(ctx); err != nil { return fmt.Errorf("security scheme 'flow' is invalid: %w", err) } } else if ss.Flows != nil { return fmt.Errorf("security scheme of type %q can't have 'flows'", ss.Type) } return validateExtensions(ctx, ss.Extensions) } // OAuthFlows is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#oauth-flows-object type OAuthFlows struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Implicit *OAuthFlow `json:"implicit,omitempty" yaml:"implicit,omitempty"` Password *OAuthFlow `json:"password,omitempty" yaml:"password,omitempty"` ClientCredentials *OAuthFlow `json:"clientCredentials,omitempty" yaml:"clientCredentials,omitempty"` AuthorizationCode *OAuthFlow `json:"authorizationCode,omitempty" yaml:"authorizationCode,omitempty"` } type oAuthFlowType int const ( oAuthFlowTypeImplicit oAuthFlowType = iota oAuthFlowTypePassword oAuthFlowTypeClientCredentials oAuthFlowAuthorizationCode ) // MarshalJSON returns the JSON encoding of OAuthFlows. func (flows OAuthFlows) MarshalJSON() ([]byte, error) { x, err := flows.MarshalYAML() if err != nil { return nil, err } return json.Marshal(x) } // MarshalYAML returns the YAML encoding of OAuthFlows. func (flows OAuthFlows) MarshalYAML() (any, error) { m := make(map[string]any, 4+len(flows.Extensions)) for k, v := range flows.Extensions { m[k] = v } if x := flows.Implicit; x != nil { m["implicit"] = x } if x := flows.Password; x != nil { m["password"] = x } if x := flows.ClientCredentials; x != nil { m["clientCredentials"] = x } if x := flows.AuthorizationCode; x != nil { m["authorizationCode"] = x } return m, nil } // UnmarshalJSON sets OAuthFlows to a copy of data. func (flows *OAuthFlows) UnmarshalJSON(data []byte) error { type OAuthFlowsBis OAuthFlows var x OAuthFlowsBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, originKey) delete(x.Extensions, "implicit") delete(x.Extensions, "password") delete(x.Extensions, "clientCredentials") delete(x.Extensions, "authorizationCode") if len(x.Extensions) == 0 { x.Extensions = nil } *flows = OAuthFlows(x) return nil } // Validate returns an error if OAuthFlows does not comply with the OpenAPI spec. func (flows *OAuthFlows) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) if v := flows.Implicit; v != nil { if err := v.validate(ctx, oAuthFlowTypeImplicit, opts...); err != nil { return fmt.Errorf("the OAuth flow 'implicit' is invalid: %w", err) } } if v := flows.Password; v != nil { if err := v.validate(ctx, oAuthFlowTypePassword, opts...); err != nil { return fmt.Errorf("the OAuth flow 'password' is invalid: %w", err) } } if v := flows.ClientCredentials; v != nil { if err := v.validate(ctx, oAuthFlowTypeClientCredentials, opts...); err != nil { return fmt.Errorf("the OAuth flow 'clientCredentials' is invalid: %w", err) } } if v := flows.AuthorizationCode; v != nil { if err := v.validate(ctx, oAuthFlowAuthorizationCode, opts...); err != nil { return fmt.Errorf("the OAuth flow 'authorizationCode' is invalid: %w", err) } } return validateExtensions(ctx, flows.Extensions) } // OAuthFlow is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#oauth-flow-object type OAuthFlow struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` AuthorizationURL string `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"` TokenURL string `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"` RefreshURL string `json:"refreshUrl,omitempty" yaml:"refreshUrl,omitempty"` Scopes StringMap `json:"scopes" yaml:"scopes"` // required } // MarshalJSON returns the JSON encoding of OAuthFlow. func (flow OAuthFlow) MarshalJSON() ([]byte, error) { x, err := flow.MarshalYAML() if err != nil { return nil, err } return json.Marshal(x) } // MarshalYAML returns the YAML encoding of OAuthFlow. func (flow OAuthFlow) MarshalYAML() (any, error) { m := make(map[string]any, 4+len(flow.Extensions)) for k, v := range flow.Extensions { m[k] = v } if x := flow.AuthorizationURL; x != "" { m["authorizationUrl"] = x } if x := flow.TokenURL; x != "" { m["tokenUrl"] = x } if x := flow.RefreshURL; x != "" { m["refreshUrl"] = x } m["scopes"] = flow.Scopes return m, nil } // UnmarshalJSON sets OAuthFlow to a copy of data. func (flow *OAuthFlow) UnmarshalJSON(data []byte) error { type OAuthFlowBis OAuthFlow var x OAuthFlowBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, originKey) delete(x.Extensions, "authorizationUrl") delete(x.Extensions, "tokenUrl") delete(x.Extensions, "refreshUrl") delete(x.Extensions, "scopes") if len(x.Extensions) == 0 { x.Extensions = nil } *flow = OAuthFlow(x) return nil } // Validate returns an error if OAuthFlows does not comply with the OpenAPI spec. func (flow *OAuthFlow) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) if v := flow.RefreshURL; v != "" { if _, err := url.Parse(v); err != nil { return fmt.Errorf("field 'refreshUrl' is invalid: %w", err) } } if flow.Scopes == nil { return errors.New("field 'scopes' is missing") } return validateExtensions(ctx, flow.Extensions) } func (flow *OAuthFlow) validate(ctx context.Context, typ oAuthFlowType, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) typeIn := func(types ...oAuthFlowType) bool { for _, ty := range types { if ty == typ { return true } } return false } if in := typeIn(oAuthFlowTypeImplicit, oAuthFlowAuthorizationCode); true { switch { case flow.AuthorizationURL == "" && in: return errors.New("field 'authorizationUrl' is empty or missing") case flow.AuthorizationURL != "" && !in: return errors.New("field 'authorizationUrl' should not be set") case flow.AuthorizationURL != "": if _, err := url.Parse(flow.AuthorizationURL); err != nil { return fmt.Errorf("field 'authorizationUrl' is invalid: %w", err) } } } if in := typeIn(oAuthFlowTypePassword, oAuthFlowTypeClientCredentials, oAuthFlowAuthorizationCode); true { switch { case flow.TokenURL == "" && in: return errors.New("field 'tokenUrl' is empty or missing") case flow.TokenURL != "" && !in: return errors.New("field 'tokenUrl' should not be set") case flow.TokenURL != "": if _, err := url.Parse(flow.TokenURL); err != nil { return fmt.Errorf("field 'tokenUrl' is invalid: %w", err) } } } return flow.Validate(ctx, opts...) } // UnmarshalJSON sets SecuritySchemes to a copy of data. func (securitySchemes *SecuritySchemes) UnmarshalJSON(data []byte) (err error) { *securitySchemes, _, err = unmarshalStringMapP[SecuritySchemeRef](data) return } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/serialization_method.go ================================================ package openapi3 const ( SerializationSimple = "simple" SerializationLabel = "label" SerializationMatrix = "matrix" SerializationForm = "form" SerializationSpaceDelimited = "spaceDelimited" SerializationPipeDelimited = "pipeDelimited" SerializationDeepObject = "deepObject" ) // SerializationMethod describes a serialization method of HTTP request's parameters and body. type SerializationMethod struct { Style string Explode bool } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/server.go ================================================ package openapi3 import ( "context" "encoding/json" "errors" "fmt" "net/url" "sort" "strings" ) // Servers is specified by OpenAPI/Swagger standard version 3. type Servers []*Server // Validate returns an error if Servers does not comply with the OpenAPI spec. func (servers Servers) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) for _, v := range servers { if err := v.Validate(ctx); err != nil { return err } } return nil } // BasePath returns the base path of the first server in the list, or /. func (servers Servers) BasePath() (string, error) { for _, server := range servers { return server.BasePath() } return "/", nil } func (servers Servers) MatchURL(parsedURL *url.URL) (*Server, []string, string) { rawURL := parsedURL.String() if i := strings.IndexByte(rawURL, '?'); i >= 0 { rawURL = rawURL[:i] } for _, server := range servers { pathParams, remaining, ok := server.MatchRawURL(rawURL) if ok { return server, pathParams, remaining } } return nil, nil, "" } // Server is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#server-object type Server struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` URL string `json:"url" yaml:"url"` // Required Description string `json:"description,omitempty" yaml:"description,omitempty"` Variables map[string]*ServerVariable `json:"variables,omitempty" yaml:"variables,omitempty"` } // BasePath returns the base path extracted from the default values of variables, if any. // Assumes a valid struct (per Validate()). func (server *Server) BasePath() (string, error) { if server == nil { return "/", nil } uri := server.URL for name, svar := range server.Variables { uri = strings.ReplaceAll(uri, "{"+name+"}", svar.Default) } u, err := url.ParseRequestURI(uri) if err != nil { return "", err } if bp := u.Path; bp != "" { return bp, nil } return "/", nil } // MarshalJSON returns the JSON encoding of Server. func (server Server) MarshalJSON() ([]byte, error) { x, err := server.MarshalYAML() if err != nil { return nil, err } return json.Marshal(x) } // MarshalYAML returns the YAML encoding of Server. func (server Server) MarshalYAML() (any, error) { m := make(map[string]any, 3+len(server.Extensions)) for k, v := range server.Extensions { m[k] = v } m["url"] = server.URL if x := server.Description; x != "" { m["description"] = x } if x := server.Variables; len(x) != 0 { m["variables"] = x } return m, nil } // UnmarshalJSON sets Server to a copy of data. func (server *Server) UnmarshalJSON(data []byte) error { type ServerBis Server var x ServerBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, originKey) delete(x.Extensions, "url") delete(x.Extensions, "description") delete(x.Extensions, "variables") if len(x.Extensions) == 0 { x.Extensions = nil } *server = Server(x) return nil } func (server Server) ParameterNames() ([]string, error) { pattern := server.URL var params []string for len(pattern) > 0 { i := strings.IndexByte(pattern, '{') if i < 0 { break } pattern = pattern[i+1:] i = strings.IndexByte(pattern, '}') if i < 0 { return nil, errors.New("missing '}'") } params = append(params, strings.TrimSpace(pattern[:i])) pattern = pattern[i+1:] } return params, nil } func (server Server) MatchRawURL(input string) ([]string, string, bool) { pattern := server.URL var params []string for len(pattern) > 0 { c := pattern[0] if len(pattern) == 1 && c == '/' { break } if c == '{' { // Find end of pattern i := strings.IndexByte(pattern, '}') if i < 0 { return nil, "", false } pattern = pattern[i+1:] // Find next matching pattern character or next '/' whichever comes first np := -1 if len(pattern) > 0 { np = strings.IndexByte(input, pattern[0]) } ns := strings.IndexByte(input, '/') if np < 0 { i = ns } else if ns < 0 { i = np } else { i = min(np, ns) } if i < 0 { i = len(input) } params = append(params, input[:i]) input = input[i:] continue } if len(input) == 0 || input[0] != c { return nil, "", false } pattern = pattern[1:] input = input[1:] } if input == "" { input = "/" } if input[0] != '/' { return nil, "", false } return params, input, true } // Validate returns an error if Server does not comply with the OpenAPI spec. func (server *Server) Validate(ctx context.Context, opts ...ValidationOption) (err error) { ctx = WithValidationOptions(ctx, opts...) if server.URL == "" { return errors.New("value of url must be a non-empty string") } opening, closing := strings.Count(server.URL, "{"), strings.Count(server.URL, "}") if opening != closing { return errors.New("server URL has mismatched { and }") } if opening != len(server.Variables) { return errors.New("server has undeclared variables") } variables := make([]string, 0, len(server.Variables)) for name := range server.Variables { variables = append(variables, name) } sort.Strings(variables) for _, name := range variables { v := server.Variables[name] if !strings.Contains(server.URL, "{"+name+"}") { return errors.New("server has undeclared variables") } if err = v.Validate(ctx); err != nil { return } } return validateExtensions(ctx, server.Extensions) } // ServerVariable is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#server-variable-object type ServerVariable struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Enum []string `json:"enum,omitempty" yaml:"enum,omitempty"` Default string `json:"default,omitempty" yaml:"default,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` } // MarshalJSON returns the JSON encoding of ServerVariable. func (serverVariable ServerVariable) MarshalJSON() ([]byte, error) { x, err := serverVariable.MarshalYAML() if err != nil { return nil, err } return json.Marshal(x) } // MarshalYAML returns the YAML encoding of ServerVariable. func (serverVariable ServerVariable) MarshalYAML() (any, error) { m := make(map[string]any, 4+len(serverVariable.Extensions)) for k, v := range serverVariable.Extensions { m[k] = v } if x := serverVariable.Enum; len(x) != 0 { m["enum"] = x } if x := serverVariable.Default; x != "" { m["default"] = x } if x := serverVariable.Description; x != "" { m["description"] = x } return m, nil } // UnmarshalJSON sets ServerVariable to a copy of data. func (serverVariable *ServerVariable) UnmarshalJSON(data []byte) error { type ServerVariableBis ServerVariable var x ServerVariableBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, originKey) delete(x.Extensions, "enum") delete(x.Extensions, "default") delete(x.Extensions, "description") if len(x.Extensions) == 0 { x.Extensions = nil } *serverVariable = ServerVariable(x) return nil } // Validate returns an error if ServerVariable does not comply with the OpenAPI spec. func (serverVariable *ServerVariable) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) if serverVariable.Default == "" { data, err := serverVariable.MarshalJSON() if err != nil { return err } return fmt.Errorf("field default is required in %s", data) } return validateExtensions(ctx, serverVariable.Extensions) } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/stringmap.go ================================================ package openapi3 import "encoding/json" // StringMap is a map[string]string that ignores the origin in the underlying json representation. type StringMap map[string]string // UnmarshalJSON sets StringMap to a copy of data. func (stringMap *StringMap) UnmarshalJSON(data []byte) (err error) { *stringMap, _, err = unmarshalStringMap[string](data) return } // unmarshalStringMapP unmarshals given json into a map[string]*V func unmarshalStringMapP[V any](data []byte) (map[string]*V, *Origin, error) { var m map[string]any if err := json.Unmarshal(data, &m); err != nil { return nil, nil, err } origin, err := popOrigin(m, originKey) if err != nil { return nil, nil, err } result := make(map[string]*V, len(m)) for k, v := range m { value, err := deepCast[V](v) if err != nil { return nil, nil, err } result[k] = value } return result, origin, nil } // unmarshalStringMap unmarshals given json into a map[string]V func unmarshalStringMap[V any](data []byte) (map[string]V, *Origin, error) { var m map[string]any if err := json.Unmarshal(data, &m); err != nil { return nil, nil, err } origin, err := popOrigin(m, originKey) if err != nil { return nil, nil, err } result := make(map[string]V, len(m)) for k, v := range m { value, err := deepCast[V](v) if err != nil { return nil, nil, err } result[k] = *value } return result, origin, nil } // deepCast casts any value to a value of type V. func deepCast[V any](value any) (*V, error) { data, err := json.Marshal(value) if err != nil { return nil, err } var result V if err = json.Unmarshal(data, &result); err != nil { return nil, err } return &result, nil } // popOrigin removes the origin from the map and returns it. func popOrigin(m map[string]any, key string) (*Origin, error) { if !IncludeOrigin { return nil, nil } origin, err := deepCast[Origin](m[key]) if err != nil { return nil, err } delete(m, key) return origin, nil } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/tag.go ================================================ package openapi3 import ( "context" "encoding/json" "fmt" ) // Tags is specified by OpenAPI/Swagger 3.0 standard. type Tags []*Tag func (tags Tags) Get(name string) *Tag { for _, tag := range tags { if tag.Name == name { return tag } } return nil } // Validate returns an error if Tags does not comply with the OpenAPI spec. func (tags Tags) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) for _, v := range tags { if err := v.Validate(ctx); err != nil { return err } } return nil } // Tag is specified by OpenAPI/Swagger 3.0 standard. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#tag-object type Tag struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"` ExternalDocs *ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` } // MarshalJSON returns the JSON encoding of Tag. func (t Tag) MarshalJSON() ([]byte, error) { x, err := t.MarshalYAML() if err != nil { return nil, err } return json.Marshal(x) } // MarshalYAML returns the YAML encoding of Tag. func (t Tag) MarshalYAML() (any, error) { m := make(map[string]any, 3+len(t.Extensions)) for k, v := range t.Extensions { m[k] = v } if x := t.Name; x != "" { m["name"] = x } if x := t.Description; x != "" { m["description"] = x } if x := t.ExternalDocs; x != nil { m["externalDocs"] = x } return m, nil } // UnmarshalJSON sets Tag to a copy of data. func (t *Tag) UnmarshalJSON(data []byte) error { type TagBis Tag var x TagBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, originKey) delete(x.Extensions, "name") delete(x.Extensions, "description") delete(x.Extensions, "externalDocs") if len(x.Extensions) == 0 { x.Extensions = nil } *t = Tag(x) return nil } // Validate returns an error if Tag does not comply with the OpenAPI spec. func (t *Tag) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) if v := t.ExternalDocs; v != nil { if err := v.Validate(ctx); err != nil { return fmt.Errorf("invalid external docs: %w", err) } } return validateExtensions(ctx, t.Extensions) } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/validation_options.go ================================================ package openapi3 import "context" // ValidationOption allows the modification of how the OpenAPI document is validated. type ValidationOption func(options *ValidationOptions) // ValidationOptions provides configuration for validating OpenAPI documents. type ValidationOptions struct { examplesValidationAsReq, examplesValidationAsRes bool examplesValidationDisabled bool schemaDefaultsValidationDisabled bool schemaFormatValidationEnabled bool schemaPatternValidationDisabled bool schemaExtensionsInRefProhibited bool regexCompilerFunc RegexCompilerFunc extraSiblingFieldsAllowed map[string]struct{} } type validationOptionsKey struct{} // AllowExtraSiblingFields called as AllowExtraSiblingFields("description") makes Validate not return an error when said field appears next to a $ref. func AllowExtraSiblingFields(fields ...string) ValidationOption { return func(options *ValidationOptions) { if options.extraSiblingFieldsAllowed == nil && len(fields) != 0 { options.extraSiblingFieldsAllowed = make(map[string]struct{}, len(fields)) } for _, field := range fields { options.extraSiblingFieldsAllowed[field] = struct{}{} } } } // EnableSchemaFormatValidation makes Validate not return an error when validating documents that mention schema formats that are not defined by the OpenAPIv3 specification. // By default, schema format validation is disabled. func EnableSchemaFormatValidation() ValidationOption { return func(options *ValidationOptions) { options.schemaFormatValidationEnabled = true } } // DisableSchemaFormatValidation does the opposite of EnableSchemaFormatValidation. // By default, schema format validation is disabled. func DisableSchemaFormatValidation() ValidationOption { return func(options *ValidationOptions) { options.schemaFormatValidationEnabled = false } } // EnableSchemaPatternValidation does the opposite of DisableSchemaPatternValidation. // By default, schema pattern validation is enabled. func EnableSchemaPatternValidation() ValidationOption { return func(options *ValidationOptions) { options.schemaPatternValidationDisabled = false } } // DisableSchemaPatternValidation makes Validate not return an error when validating patterns that are not supported by the Go regexp engine. func DisableSchemaPatternValidation() ValidationOption { return func(options *ValidationOptions) { options.schemaPatternValidationDisabled = true } } // EnableSchemaDefaultsValidation does the opposite of DisableSchemaDefaultsValidation. // By default, schema default values are validated against their schema. func EnableSchemaDefaultsValidation() ValidationOption { return func(options *ValidationOptions) { options.schemaDefaultsValidationDisabled = false } } // DisableSchemaDefaultsValidation disables schemas' default field validation. // By default, schema default values are validated against their schema. func DisableSchemaDefaultsValidation() ValidationOption { return func(options *ValidationOptions) { options.schemaDefaultsValidationDisabled = true } } // EnableExamplesValidation does the opposite of DisableExamplesValidation. // By default, all schema examples are validated. func EnableExamplesValidation() ValidationOption { return func(options *ValidationOptions) { options.examplesValidationDisabled = false } } // DisableExamplesValidation disables all example schema validation. // By default, all schema examples are validated. func DisableExamplesValidation() ValidationOption { return func(options *ValidationOptions) { options.examplesValidationDisabled = true } } // AllowExtensionsWithRef allows extensions (fields starting with 'x-') // as siblings for $ref fields. This is the default. // Non-extension fields are prohibited unless allowed explicitly with the // AllowExtraSiblingFields option. func AllowExtensionsWithRef() ValidationOption { return func(options *ValidationOptions) { options.schemaExtensionsInRefProhibited = false } } // ProhibitExtensionsWithRef causes the validation to return an // error if extensions (fields starting with 'x-') are found as // siblings for $ref fields. Non-extension fields are prohibited // unless allowed explicitly with the AllowExtraSiblingFields option. func ProhibitExtensionsWithRef() ValidationOption { return func(options *ValidationOptions) { options.schemaExtensionsInRefProhibited = true } } // SetRegexCompiler allows to override the regex implementation used to validate // field "pattern". func SetRegexCompiler(c RegexCompilerFunc) ValidationOption { return func(options *ValidationOptions) { options.regexCompilerFunc = c } } // WithValidationOptions allows adding validation options to a context object that can be used when validating any OpenAPI type. func WithValidationOptions(ctx context.Context, opts ...ValidationOption) context.Context { if len(opts) == 0 { return ctx } options := &ValidationOptions{} for _, opt := range opts { opt(options) } return context.WithValue(ctx, validationOptionsKey{}, options) } func getValidationOptions(ctx context.Context) *ValidationOptions { if options, ok := ctx.Value(validationOptionsKey{}).(*ValidationOptions); ok { return options } return &ValidationOptions{} } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/visited.go ================================================ package openapi3 func newVisited() visitedComponent { return visitedComponent{ header: make(map[*Header]struct{}), schema: make(map[*Schema]struct{}), } } type visitedComponent struct { header map[*Header]struct{} schema map[*Schema]struct{} } // resetVisited clears visitedComponent map // should be called before recursion over doc *T func (doc *T) resetVisited() { doc.visited = newVisited() } // isVisitedHeader returns `true` if the *Header pointer was already visited // otherwise it returns `false` func (doc *T) isVisitedHeader(h *Header) bool { if _, ok := doc.visited.header[h]; ok { return true } doc.visited.header[h] = struct{}{} return false } // isVisitedHeader returns `true` if the *Schema pointer was already visited // otherwise it returns `false` func (doc *T) isVisitedSchema(s *Schema) bool { if _, ok := doc.visited.schema[s]; ok { return true } doc.visited.schema[s] = struct{}{} return false } ================================================ FILE: vendor/github.com/getkin/kin-openapi/openapi3/xml.go ================================================ package openapi3 import ( "context" "encoding/json" ) // XML is specified by OpenAPI/Swagger standard version 3. // See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#xml-object type XML struct { Extensions map[string]any `json:"-" yaml:"-"` Origin *Origin `json:"__origin__,omitempty" yaml:"__origin__,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"` Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"` Attribute bool `json:"attribute,omitempty" yaml:"attribute,omitempty"` Wrapped bool `json:"wrapped,omitempty" yaml:"wrapped,omitempty"` } // MarshalJSON returns the JSON encoding of XML. func (xml XML) MarshalJSON() ([]byte, error) { x, err := xml.MarshalYAML() if err != nil { return nil, err } return json.Marshal(x) } // MarshalYAML returns the YAML encoding of XML. func (xml XML) MarshalYAML() (any, error) { m := make(map[string]any, 5+len(xml.Extensions)) for k, v := range xml.Extensions { m[k] = v } if x := xml.Name; x != "" { m["name"] = x } if x := xml.Namespace; x != "" { m["namespace"] = x } if x := xml.Prefix; x != "" { m["prefix"] = x } if x := xml.Attribute; x { m["attribute"] = x } if x := xml.Wrapped; x { m["wrapped"] = x } return m, nil } // UnmarshalJSON sets XML to a copy of data. func (xml *XML) UnmarshalJSON(data []byte) error { type XMLBis XML var x XMLBis if err := json.Unmarshal(data, &x); err != nil { return unmarshalError(err) } _ = json.Unmarshal(data, &x.Extensions) delete(x.Extensions, originKey) delete(x.Extensions, "name") delete(x.Extensions, "namespace") delete(x.Extensions, "prefix") delete(x.Extensions, "attribute") delete(x.Extensions, "wrapped") if len(x.Extensions) == 0 { x.Extensions = nil } *xml = XML(x) return nil } // Validate returns an error if XML does not comply with the OpenAPI spec. func (xml *XML) Validate(ctx context.Context, opts ...ValidationOption) error { ctx = WithValidationOptions(ctx, opts...) return validateExtensions(ctx, xml.Extensions) } ================================================ FILE: vendor/github.com/ghodss/yaml/.gitignore ================================================ # OSX leaves these everywhere on SMB shares ._* # Eclipse files .classpath .project .settings/** # Emacs save files *~ # Vim-related files [._]*.s[a-w][a-z] [._]s[a-w][a-z] *.un~ Session.vim .netrwhist # Go test binaries *.test ================================================ FILE: vendor/github.com/ghodss/yaml/.travis.yml ================================================ language: go go: - 1.3 - 1.4 script: - go test - go build ================================================ FILE: vendor/github.com/ghodss/yaml/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2014 Sam Ghods Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Copyright (c) 2012 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/ghodss/yaml/README.md ================================================ # YAML marshaling and unmarshaling support for Go [![Build Status](https://travis-ci.org/ghodss/yaml.svg)](https://travis-ci.org/ghodss/yaml) ## Introduction A wrapper around [go-yaml](https://github.com/go-yaml/yaml) designed to enable a better way of handling YAML when marshaling to and from structs. In short, this library first converts YAML to JSON using go-yaml and then uses `json.Marshal` and `json.Unmarshal` to convert to or from the struct. This means that it effectively reuses the JSON struct tags as well as the custom JSON methods `MarshalJSON` and `UnmarshalJSON` unlike go-yaml. For a detailed overview of the rationale behind this method, [see this blog post](http://ghodss.com/2014/the-right-way-to-handle-yaml-in-golang/). ## Compatibility This package uses [go-yaml](https://github.com/go-yaml/yaml) and therefore supports [everything go-yaml supports](https://github.com/go-yaml/yaml#compatibility). ## Caveats **Caveat #1:** When using `yaml.Marshal` and `yaml.Unmarshal`, binary data should NOT be preceded with the `!!binary` YAML tag. If you do, go-yaml will convert the binary data from base64 to native binary data, which is not compatible with JSON. You can still use binary in your YAML files though - just store them without the `!!binary` tag and decode the base64 in your code (e.g. in the custom JSON methods `MarshalJSON` and `UnmarshalJSON`). This also has the benefit that your YAML and your JSON binary data will be decoded exactly the same way. As an example: ``` BAD: exampleKey: !!binary gIGC GOOD: exampleKey: gIGC ... and decode the base64 data in your code. ``` **Caveat #2:** When using `YAMLToJSON` directly, maps with keys that are maps will result in an error since this is not supported by JSON. This error will occur in `Unmarshal` as well since you can't unmarshal map keys anyways since struct fields can't be keys. ## Installation and usage To install, run: ``` $ go get github.com/ghodss/yaml ``` And import using: ``` import "github.com/ghodss/yaml" ``` Usage is very similar to the JSON library: ```go package main import ( "fmt" "github.com/ghodss/yaml" ) type Person struct { Name string `json:"name"` // Affects YAML field names too. Age int `json:"age"` } func main() { // Marshal a Person struct to YAML. p := Person{"John", 30} y, err := yaml.Marshal(p) if err != nil { fmt.Printf("err: %v\n", err) return } fmt.Println(string(y)) /* Output: age: 30 name: John */ // Unmarshal the YAML back into a Person struct. var p2 Person err = yaml.Unmarshal(y, &p2) if err != nil { fmt.Printf("err: %v\n", err) return } fmt.Println(p2) /* Output: {John 30} */ } ``` `yaml.YAMLToJSON` and `yaml.JSONToYAML` methods are also available: ```go package main import ( "fmt" "github.com/ghodss/yaml" ) func main() { j := []byte(`{"name": "John", "age": 30}`) y, err := yaml.JSONToYAML(j) if err != nil { fmt.Printf("err: %v\n", err) return } fmt.Println(string(y)) /* Output: name: John age: 30 */ j2, err := yaml.YAMLToJSON(y) if err != nil { fmt.Printf("err: %v\n", err) return } fmt.Println(string(j2)) /* Output: {"age":30,"name":"John"} */ } ``` ================================================ FILE: vendor/github.com/ghodss/yaml/fields.go ================================================ // Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package yaml import ( "bytes" "encoding" "encoding/json" "reflect" "sort" "strings" "sync" "unicode" "unicode/utf8" ) // indirect walks down v allocating pointers as needed, // until it gets to a non-pointer. // if it encounters an Unmarshaler, indirect stops and returns that. // if decodingNull is true, indirect stops at the last pointer so it can be set to nil. func indirect(v reflect.Value, decodingNull bool) (json.Unmarshaler, encoding.TextUnmarshaler, reflect.Value) { // If v is a named type and is addressable, // start with its address, so that if the type has pointer methods, // we find them. if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() { v = v.Addr() } for { // Load value from interface, but only if the result will be // usefully addressable. if v.Kind() == reflect.Interface && !v.IsNil() { e := v.Elem() if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) { v = e continue } } if v.Kind() != reflect.Ptr { break } if v.Elem().Kind() != reflect.Ptr && decodingNull && v.CanSet() { break } if v.IsNil() { if v.CanSet() { v.Set(reflect.New(v.Type().Elem())) } else { v = reflect.New(v.Type().Elem()) } } if v.Type().NumMethod() > 0 { if u, ok := v.Interface().(json.Unmarshaler); ok { return u, nil, reflect.Value{} } if u, ok := v.Interface().(encoding.TextUnmarshaler); ok { return nil, u, reflect.Value{} } } v = v.Elem() } return nil, nil, v } // A field represents a single field found in a struct. type field struct { name string nameBytes []byte // []byte(name) equalFold func(s, t []byte) bool // bytes.EqualFold or equivalent tag bool index []int typ reflect.Type omitEmpty bool quoted bool } func fillField(f field) field { f.nameBytes = []byte(f.name) f.equalFold = foldFunc(f.nameBytes) return f } // byName sorts field by name, breaking ties with depth, // then breaking ties with "name came from json tag", then // breaking ties with index sequence. type byName []field func (x byName) Len() int { return len(x) } func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x byName) Less(i, j int) bool { if x[i].name != x[j].name { return x[i].name < x[j].name } if len(x[i].index) != len(x[j].index) { return len(x[i].index) < len(x[j].index) } if x[i].tag != x[j].tag { return x[i].tag } return byIndex(x).Less(i, j) } // byIndex sorts field by index sequence. type byIndex []field func (x byIndex) Len() int { return len(x) } func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x byIndex) Less(i, j int) bool { for k, xik := range x[i].index { if k >= len(x[j].index) { return false } if xik != x[j].index[k] { return xik < x[j].index[k] } } return len(x[i].index) < len(x[j].index) } // typeFields returns a list of fields that JSON should recognize for the given type. // The algorithm is breadth-first search over the set of structs to include - the top struct // and then any reachable anonymous structs. func typeFields(t reflect.Type) []field { // Anonymous fields to explore at the current level and the next. current := []field{} next := []field{{typ: t}} // Count of queued names for current level and the next. count := map[reflect.Type]int{} nextCount := map[reflect.Type]int{} // Types already visited at an earlier level. visited := map[reflect.Type]bool{} // Fields found. var fields []field for len(next) > 0 { current, next = next, current[:0] count, nextCount = nextCount, map[reflect.Type]int{} for _, f := range current { if visited[f.typ] { continue } visited[f.typ] = true // Scan f.typ for fields to include. for i := 0; i < f.typ.NumField(); i++ { sf := f.typ.Field(i) if sf.PkgPath != "" { // unexported continue } tag := sf.Tag.Get("json") if tag == "-" { continue } name, opts := parseTag(tag) if !isValidTag(name) { name = "" } index := make([]int, len(f.index)+1) copy(index, f.index) index[len(f.index)] = i ft := sf.Type if ft.Name() == "" && ft.Kind() == reflect.Ptr { // Follow pointer. ft = ft.Elem() } // Record found field and index sequence. if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct { tagged := name != "" if name == "" { name = sf.Name } fields = append(fields, fillField(field{ name: name, tag: tagged, index: index, typ: ft, omitEmpty: opts.Contains("omitempty"), quoted: opts.Contains("string"), })) if count[f.typ] > 1 { // If there were multiple instances, add a second, // so that the annihilation code will see a duplicate. // It only cares about the distinction between 1 or 2, // so don't bother generating any more copies. fields = append(fields, fields[len(fields)-1]) } continue } // Record new anonymous struct to explore in next round. nextCount[ft]++ if nextCount[ft] == 1 { next = append(next, fillField(field{name: ft.Name(), index: index, typ: ft})) } } } } sort.Sort(byName(fields)) // Delete all fields that are hidden by the Go rules for embedded fields, // except that fields with JSON tags are promoted. // The fields are sorted in primary order of name, secondary order // of field index length. Loop over names; for each name, delete // hidden fields by choosing the one dominant field that survives. out := fields[:0] for advance, i := 0, 0; i < len(fields); i += advance { // One iteration per name. // Find the sequence of fields with the name of this first field. fi := fields[i] name := fi.name for advance = 1; i+advance < len(fields); advance++ { fj := fields[i+advance] if fj.name != name { break } } if advance == 1 { // Only one field with this name out = append(out, fi) continue } dominant, ok := dominantField(fields[i : i+advance]) if ok { out = append(out, dominant) } } fields = out sort.Sort(byIndex(fields)) return fields } // dominantField looks through the fields, all of which are known to // have the same name, to find the single field that dominates the // others using Go's embedding rules, modified by the presence of // JSON tags. If there are multiple top-level fields, the boolean // will be false: This condition is an error in Go and we skip all // the fields. func dominantField(fields []field) (field, bool) { // The fields are sorted in increasing index-length order. The winner // must therefore be one with the shortest index length. Drop all // longer entries, which is easy: just truncate the slice. length := len(fields[0].index) tagged := -1 // Index of first tagged field. for i, f := range fields { if len(f.index) > length { fields = fields[:i] break } if f.tag { if tagged >= 0 { // Multiple tagged fields at the same level: conflict. // Return no field. return field{}, false } tagged = i } } if tagged >= 0 { return fields[tagged], true } // All remaining fields have the same length. If there's more than one, // we have a conflict (two fields named "X" at the same level) and we // return no field. if len(fields) > 1 { return field{}, false } return fields[0], true } var fieldCache struct { sync.RWMutex m map[reflect.Type][]field } // cachedTypeFields is like typeFields but uses a cache to avoid repeated work. func cachedTypeFields(t reflect.Type) []field { fieldCache.RLock() f := fieldCache.m[t] fieldCache.RUnlock() if f != nil { return f } // Compute fields without lock. // Might duplicate effort but won't hold other computations back. f = typeFields(t) if f == nil { f = []field{} } fieldCache.Lock() if fieldCache.m == nil { fieldCache.m = map[reflect.Type][]field{} } fieldCache.m[t] = f fieldCache.Unlock() return f } func isValidTag(s string) bool { if s == "" { return false } for _, c := range s { switch { case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c): // Backslash and quote chars are reserved, but // otherwise any punctuation chars are allowed // in a tag name. default: if !unicode.IsLetter(c) && !unicode.IsDigit(c) { return false } } } return true } const ( caseMask = ^byte(0x20) // Mask to ignore case in ASCII. kelvin = '\u212a' smallLongEss = '\u017f' ) // foldFunc returns one of four different case folding equivalence // functions, from most general (and slow) to fastest: // // 1) bytes.EqualFold, if the key s contains any non-ASCII UTF-8 // 2) equalFoldRight, if s contains special folding ASCII ('k', 'K', 's', 'S') // 3) asciiEqualFold, no special, but includes non-letters (including _) // 4) simpleLetterEqualFold, no specials, no non-letters. // // The letters S and K are special because they map to 3 runes, not just 2: // * S maps to s and to U+017F 'ſ' Latin small letter long s // * k maps to K and to U+212A 'K' Kelvin sign // See http://play.golang.org/p/tTxjOc0OGo // // The returned function is specialized for matching against s and // should only be given s. It's not curried for performance reasons. func foldFunc(s []byte) func(s, t []byte) bool { nonLetter := false special := false // special letter for _, b := range s { if b >= utf8.RuneSelf { return bytes.EqualFold } upper := b & caseMask if upper < 'A' || upper > 'Z' { nonLetter = true } else if upper == 'K' || upper == 'S' { // See above for why these letters are special. special = true } } if special { return equalFoldRight } if nonLetter { return asciiEqualFold } return simpleLetterEqualFold } // equalFoldRight is a specialization of bytes.EqualFold when s is // known to be all ASCII (including punctuation), but contains an 's', // 'S', 'k', or 'K', requiring a Unicode fold on the bytes in t. // See comments on foldFunc. func equalFoldRight(s, t []byte) bool { for _, sb := range s { if len(t) == 0 { return false } tb := t[0] if tb < utf8.RuneSelf { if sb != tb { sbUpper := sb & caseMask if 'A' <= sbUpper && sbUpper <= 'Z' { if sbUpper != tb&caseMask { return false } } else { return false } } t = t[1:] continue } // sb is ASCII and t is not. t must be either kelvin // sign or long s; sb must be s, S, k, or K. tr, size := utf8.DecodeRune(t) switch sb { case 's', 'S': if tr != smallLongEss { return false } case 'k', 'K': if tr != kelvin { return false } default: return false } t = t[size:] } if len(t) > 0 { return false } return true } // asciiEqualFold is a specialization of bytes.EqualFold for use when // s is all ASCII (but may contain non-letters) and contains no // special-folding letters. // See comments on foldFunc. func asciiEqualFold(s, t []byte) bool { if len(s) != len(t) { return false } for i, sb := range s { tb := t[i] if sb == tb { continue } if ('a' <= sb && sb <= 'z') || ('A' <= sb && sb <= 'Z') { if sb&caseMask != tb&caseMask { return false } } else { return false } } return true } // simpleLetterEqualFold is a specialization of bytes.EqualFold for // use when s is all ASCII letters (no underscores, etc) and also // doesn't contain 'k', 'K', 's', or 'S'. // See comments on foldFunc. func simpleLetterEqualFold(s, t []byte) bool { if len(s) != len(t) { return false } for i, b := range s { if b&caseMask != t[i]&caseMask { return false } } return true } // tagOptions is the string following a comma in a struct field's "json" // tag, or the empty string. It does not include the leading comma. type tagOptions string // parseTag splits a struct field's json tag into its name and // comma-separated options. func parseTag(tag string) (string, tagOptions) { if idx := strings.Index(tag, ","); idx != -1 { return tag[:idx], tagOptions(tag[idx+1:]) } return tag, tagOptions("") } // Contains reports whether a comma-separated list of options // contains a particular substr flag. substr must be surrounded by a // string boundary or commas. func (o tagOptions) Contains(optionName string) bool { if len(o) == 0 { return false } s := string(o) for s != "" { var next string i := strings.Index(s, ",") if i >= 0 { s, next = s[:i], s[i+1:] } if s == optionName { return true } s = next } return false } ================================================ FILE: vendor/github.com/ghodss/yaml/yaml.go ================================================ package yaml import ( "bytes" "encoding/json" "fmt" "reflect" "strconv" "gopkg.in/yaml.v2" ) // Marshals the object into JSON then converts JSON to YAML and returns the // YAML. func Marshal(o interface{}) ([]byte, error) { j, err := json.Marshal(o) if err != nil { return nil, fmt.Errorf("error marshaling into JSON: %v", err) } y, err := JSONToYAML(j) if err != nil { return nil, fmt.Errorf("error converting JSON to YAML: %v", err) } return y, nil } // Converts YAML to JSON then uses JSON to unmarshal into an object. func Unmarshal(y []byte, o interface{}) error { vo := reflect.ValueOf(o) j, err := yamlToJSON(y, &vo) if err != nil { return fmt.Errorf("error converting YAML to JSON: %v", err) } err = json.Unmarshal(j, o) if err != nil { return fmt.Errorf("error unmarshaling JSON: %v", err) } return nil } // Convert JSON to YAML. func JSONToYAML(j []byte) ([]byte, error) { // Convert the JSON to an object. var jsonObj interface{} // We are using yaml.Unmarshal here (instead of json.Unmarshal) because the // Go JSON library doesn't try to pick the right number type (int, float, // etc.) when unmarshalling to interface{}, it just picks float64 // universally. go-yaml does go through the effort of picking the right // number type, so we can preserve number type throughout this process. err := yaml.Unmarshal(j, &jsonObj) if err != nil { return nil, err } // Marshal this object into YAML. return yaml.Marshal(jsonObj) } // Convert YAML to JSON. Since JSON is a subset of YAML, passing JSON through // this method should be a no-op. // // Things YAML can do that are not supported by JSON: // * In YAML you can have binary and null keys in your maps. These are invalid // in JSON. (int and float keys are converted to strings.) // * Binary data in YAML with the !!binary tag is not supported. If you want to // use binary data with this library, encode the data as base64 as usual but do // not use the !!binary tag in your YAML. This will ensure the original base64 // encoded data makes it all the way through to the JSON. func YAMLToJSON(y []byte) ([]byte, error) { return yamlToJSON(y, nil) } func yamlToJSON(y []byte, jsonTarget *reflect.Value) ([]byte, error) { // Convert the YAML to an object. var yamlObj interface{} err := yaml.Unmarshal(y, &yamlObj) if err != nil { return nil, err } // YAML objects are not completely compatible with JSON objects (e.g. you // can have non-string keys in YAML). So, convert the YAML-compatible object // to a JSON-compatible object, failing with an error if irrecoverable // incompatibilties happen along the way. jsonObj, err := convertToJSONableObject(yamlObj, jsonTarget) if err != nil { return nil, err } // Convert this object to JSON and return the data. return json.Marshal(jsonObj) } func convertToJSONableObject(yamlObj interface{}, jsonTarget *reflect.Value) (interface{}, error) { var err error // Resolve jsonTarget to a concrete value (i.e. not a pointer or an // interface). We pass decodingNull as false because we're not actually // decoding into the value, we're just checking if the ultimate target is a // string. if jsonTarget != nil { ju, tu, pv := indirect(*jsonTarget, false) // We have a JSON or Text Umarshaler at this level, so we can't be trying // to decode into a string. if ju != nil || tu != nil { jsonTarget = nil } else { jsonTarget = &pv } } // If yamlObj is a number or a boolean, check if jsonTarget is a string - // if so, coerce. Else return normal. // If yamlObj is a map or array, find the field that each key is // unmarshaling to, and when you recurse pass the reflect.Value for that // field back into this function. switch typedYAMLObj := yamlObj.(type) { case map[interface{}]interface{}: // JSON does not support arbitrary keys in a map, so we must convert // these keys to strings. // // From my reading of go-yaml v2 (specifically the resolve function), // keys can only have the types string, int, int64, float64, binary // (unsupported), or null (unsupported). strMap := make(map[string]interface{}) for k, v := range typedYAMLObj { // Resolve the key to a string first. var keyString string switch typedKey := k.(type) { case string: keyString = typedKey case int: keyString = strconv.Itoa(typedKey) case int64: // go-yaml will only return an int64 as a key if the system // architecture is 32-bit and the key's value is between 32-bit // and 64-bit. Otherwise the key type will simply be int. keyString = strconv.FormatInt(typedKey, 10) case float64: // Stolen from go-yaml to use the same conversion to string as // the go-yaml library uses to convert float to string when // Marshaling. s := strconv.FormatFloat(typedKey, 'g', -1, 32) switch s { case "+Inf": s = ".inf" case "-Inf": s = "-.inf" case "NaN": s = ".nan" } keyString = s case bool: if typedKey { keyString = "true" } else { keyString = "false" } default: return nil, fmt.Errorf("Unsupported map key of type: %s, key: %+#v, value: %+#v", reflect.TypeOf(k), k, v) } // jsonTarget should be a struct or a map. If it's a struct, find // the field it's going to map to and pass its reflect.Value. If // it's a map, find the element type of the map and pass the // reflect.Value created from that type. If it's neither, just pass // nil - JSON conversion will error for us if it's a real issue. if jsonTarget != nil { t := *jsonTarget if t.Kind() == reflect.Struct { keyBytes := []byte(keyString) // Find the field that the JSON library would use. var f *field fields := cachedTypeFields(t.Type()) for i := range fields { ff := &fields[i] if bytes.Equal(ff.nameBytes, keyBytes) { f = ff break } // Do case-insensitive comparison. if f == nil && ff.equalFold(ff.nameBytes, keyBytes) { f = ff } } if f != nil { // Find the reflect.Value of the most preferential // struct field. jtf := t.Field(f.index[0]) strMap[keyString], err = convertToJSONableObject(v, &jtf) if err != nil { return nil, err } continue } } else if t.Kind() == reflect.Map { // Create a zero value of the map's element type to use as // the JSON target. jtv := reflect.Zero(t.Type().Elem()) strMap[keyString], err = convertToJSONableObject(v, &jtv) if err != nil { return nil, err } continue } } strMap[keyString], err = convertToJSONableObject(v, nil) if err != nil { return nil, err } } return strMap, nil case []interface{}: // We need to recurse into arrays in case there are any // map[interface{}]interface{}'s inside and to convert any // numbers to strings. // If jsonTarget is a slice (which it really should be), find the // thing it's going to map to. If it's not a slice, just pass nil // - JSON conversion will error for us if it's a real issue. var jsonSliceElemValue *reflect.Value if jsonTarget != nil { t := *jsonTarget if t.Kind() == reflect.Slice { // By default slices point to nil, but we need a reflect.Value // pointing to a value of the slice type, so we create one here. ev := reflect.Indirect(reflect.New(t.Type().Elem())) jsonSliceElemValue = &ev } } // Make and use a new array. arr := make([]interface{}, len(typedYAMLObj)) for i, v := range typedYAMLObj { arr[i], err = convertToJSONableObject(v, jsonSliceElemValue) if err != nil { return nil, err } } return arr, nil default: // If the target type is a string and the YAML type is a number, // convert the YAML type to a string. if jsonTarget != nil && (*jsonTarget).Kind() == reflect.String { // Based on my reading of go-yaml, it may return int, int64, // float64, or uint64. var s string switch typedVal := typedYAMLObj.(type) { case int: s = strconv.FormatInt(int64(typedVal), 10) case int64: s = strconv.FormatInt(typedVal, 10) case float64: s = strconv.FormatFloat(typedVal, 'g', -1, 32) case uint64: s = strconv.FormatUint(typedVal, 10) case bool: if typedVal { s = "true" } else { s = "false" } } if len(s) > 0 { yamlObj = interface{}(s) } } return yamlObj, nil } return nil, nil } ================================================ FILE: vendor/github.com/go-errors/errors/.travis.yml ================================================ language: go go: - "1.8.x" - "1.10.x" - "1.13.x" - "1.14.x" - "1.16.x" ================================================ FILE: vendor/github.com/go-errors/errors/LICENSE.MIT ================================================ Copyright (c) 2015 Conrad Irwin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/go-errors/errors/README.md ================================================ go-errors/errors ================ [![Build Status](https://travis-ci.org/go-errors/errors.svg?branch=master)](https://travis-ci.org/go-errors/errors) Package errors adds stacktrace support to errors in go. This is particularly useful when you want to understand the state of execution when an error was returned unexpectedly. It provides the type \*Error which implements the standard golang error interface, so you can use this library interchangably with code that is expecting a normal error return. Usage ----- Full documentation is available on [godoc](https://godoc.org/github.com/go-errors/errors), but here's a simple example: ```go package crashy import "github.com/go-errors/errors" var Crashed = errors.Errorf("oh dear") func Crash() error { return errors.New(Crashed) } ``` This can be called as follows: ```go package main import ( "crashy" "fmt" "github.com/go-errors/errors" ) func main() { err := crashy.Crash() if err != nil { if errors.Is(err, crashy.Crashed) { fmt.Println(err.(*errors.Error).ErrorStack()) } else { panic(err) } } } ``` Meta-fu ------- This package was original written to allow reporting to [Bugsnag](https://bugsnag.com/) from [bugsnag-go](https://github.com/bugsnag/bugsnag-go), but after I found similar packages by Facebook and Dropbox, it was moved to one canonical location so everyone can benefit. This package is licensed under the MIT license, see LICENSE.MIT for details. ## Changelog * v1.1.0 updated to use go1.13's standard-library errors.Is method instead of == in errors.Is * v1.2.0 added `errors.As` from the standard library. * v1.3.0 *BREAKING* updated error methods to return `error` instead of `*Error`. > Code that needs access to the underlying `*Error` can use the new errors.AsError(e) > ``` > // before > errors.New(err).ErrorStack() > // after >. errors.AsError(errors.Wrap(err)).ErrorStack() > ``` * v1.4.0 *BREAKING* v1.4.0 reverted all changes from v1.3.0 and is identical to v1.2.0 * v1.4.1 no code change, but now without an unnecessary cover.out file. * v1.4.2 performance improvement to ErrorStack() to avoid unnecessary work https://github.com/go-errors/errors/pull/40 ================================================ FILE: vendor/github.com/go-errors/errors/error.go ================================================ // Package errors provides errors that have stack-traces. // // This is particularly useful when you want to understand the // state of execution when an error was returned unexpectedly. // // It provides the type *Error which implements the standard // golang error interface, so you can use this library interchangably // with code that is expecting a normal error return. // // For example: // // package crashy // // import "github.com/go-errors/errors" // // var Crashed = errors.Errorf("oh dear") // // func Crash() error { // return errors.New(Crashed) // } // // This can be called as follows: // // package main // // import ( // "crashy" // "fmt" // "github.com/go-errors/errors" // ) // // func main() { // err := crashy.Crash() // if err != nil { // if errors.Is(err, crashy.Crashed) { // fmt.Println(err.(*errors.Error).ErrorStack()) // } else { // panic(err) // } // } // } // // This package was original written to allow reporting to Bugsnag, // but after I found similar packages by Facebook and Dropbox, it // was moved to one canonical location so everyone can benefit. package errors import ( "bytes" "fmt" "reflect" "runtime" ) // The maximum number of stackframes on any error. var MaxStackDepth = 50 // Error is an error with an attached stacktrace. It can be used // wherever the builtin error interface is expected. type Error struct { Err error stack []uintptr frames []StackFrame prefix string } // New makes an Error from the given value. If that value is already an // error then it will be used directly, if not, it will be passed to // fmt.Errorf("%v"). The stacktrace will point to the line of code that // called New. func New(e interface{}) *Error { var err error switch e := e.(type) { case error: err = e default: err = fmt.Errorf("%v", e) } stack := make([]uintptr, MaxStackDepth) length := runtime.Callers(2, stack[:]) return &Error{ Err: err, stack: stack[:length], } } // Wrap makes an Error from the given value. If that value is already an // error then it will be used directly, if not, it will be passed to // fmt.Errorf("%v"). The skip parameter indicates how far up the stack // to start the stacktrace. 0 is from the current call, 1 from its caller, etc. func Wrap(e interface{}, skip int) *Error { if e == nil { return nil } var err error switch e := e.(type) { case *Error: return e case error: err = e default: err = fmt.Errorf("%v", e) } stack := make([]uintptr, MaxStackDepth) length := runtime.Callers(2+skip, stack[:]) return &Error{ Err: err, stack: stack[:length], } } // WrapPrefix makes an Error from the given value. If that value is already an // error then it will be used directly, if not, it will be passed to // fmt.Errorf("%v"). The prefix parameter is used to add a prefix to the // error message when calling Error(). The skip parameter indicates how far // up the stack to start the stacktrace. 0 is from the current call, // 1 from its caller, etc. func WrapPrefix(e interface{}, prefix string, skip int) *Error { if e == nil { return nil } err := Wrap(e, 1+skip) if err.prefix != "" { prefix = fmt.Sprintf("%s: %s", prefix, err.prefix) } return &Error{ Err: err.Err, stack: err.stack, prefix: prefix, } } // Errorf creates a new error with the given message. You can use it // as a drop-in replacement for fmt.Errorf() to provide descriptive // errors in return values. func Errorf(format string, a ...interface{}) *Error { return Wrap(fmt.Errorf(format, a...), 1) } // Error returns the underlying error's message. func (err *Error) Error() string { msg := err.Err.Error() if err.prefix != "" { msg = fmt.Sprintf("%s: %s", err.prefix, msg) } return msg } // Stack returns the callstack formatted the same way that go does // in runtime/debug.Stack() func (err *Error) Stack() []byte { buf := bytes.Buffer{} for _, frame := range err.StackFrames() { buf.WriteString(frame.String()) } return buf.Bytes() } // Callers satisfies the bugsnag ErrorWithCallerS() interface // so that the stack can be read out. func (err *Error) Callers() []uintptr { return err.stack } // ErrorStack returns a string that contains both the // error message and the callstack. func (err *Error) ErrorStack() string { return err.TypeName() + " " + err.Error() + "\n" + string(err.Stack()) } // StackFrames returns an array of frames containing information about the // stack. func (err *Error) StackFrames() []StackFrame { if err.frames == nil { err.frames = make([]StackFrame, len(err.stack)) for i, pc := range err.stack { err.frames[i] = NewStackFrame(pc) } } return err.frames } // TypeName returns the type this error. e.g. *errors.stringError. func (err *Error) TypeName() string { if _, ok := err.Err.(uncaughtPanic); ok { return "panic" } return reflect.TypeOf(err.Err).String() } // Return the wrapped error (implements api for As function). func (err *Error) Unwrap() error { return err.Err } ================================================ FILE: vendor/github.com/go-errors/errors/error_1_13.go ================================================ // +build go1.13 package errors import ( baseErrors "errors" ) // find error in any wrapped error func As(err error, target interface{}) bool { return baseErrors.As(err, target) } // Is detects whether the error is equal to a given error. Errors // are considered equal by this function if they are matched by errors.Is // or if their contained errors are matched through errors.Is func Is(e error, original error) bool { if baseErrors.Is(e, original) { return true } if e, ok := e.(*Error); ok { return Is(e.Err, original) } if original, ok := original.(*Error); ok { return Is(e, original.Err) } return false } ================================================ FILE: vendor/github.com/go-errors/errors/error_backward.go ================================================ // +build !go1.13 package errors import ( "reflect" ) type unwrapper interface { Unwrap() error } // As assigns error or any wrapped error to the value target points // to. If there is no value of the target type of target As returns // false. func As(err error, target interface{}) bool { targetType := reflect.TypeOf(target) for { errType := reflect.TypeOf(err) if errType == nil { return false } if reflect.PtrTo(errType) == targetType { reflect.ValueOf(target).Elem().Set(reflect.ValueOf(err)) return true } wrapped, ok := err.(unwrapper) if ok { err = wrapped.Unwrap() } else { return false } } } // Is detects whether the error is equal to a given error. Errors // are considered equal by this function if they are the same object, // or if they both contain the same error inside an errors.Error. func Is(e error, original error) bool { if e == original { return true } if e, ok := e.(*Error); ok { return Is(e.Err, original) } if original, ok := original.(*Error); ok { return Is(e, original.Err) } return false } ================================================ FILE: vendor/github.com/go-errors/errors/parse_panic.go ================================================ package errors import ( "strconv" "strings" ) type uncaughtPanic struct{ message string } func (p uncaughtPanic) Error() string { return p.message } // ParsePanic allows you to get an error object from the output of a go program // that panicked. This is particularly useful with https://github.com/mitchellh/panicwrap. func ParsePanic(text string) (*Error, error) { lines := strings.Split(text, "\n") state := "start" var message string var stack []StackFrame for i := 0; i < len(lines); i++ { line := lines[i] if state == "start" { if strings.HasPrefix(line, "panic: ") { message = strings.TrimPrefix(line, "panic: ") state = "seek" } else { return nil, Errorf("bugsnag.panicParser: Invalid line (no prefix): %s", line) } } else if state == "seek" { if strings.HasPrefix(line, "goroutine ") && strings.HasSuffix(line, "[running]:") { state = "parsing" } } else if state == "parsing" { if line == "" { state = "done" break } createdBy := false if strings.HasPrefix(line, "created by ") { line = strings.TrimPrefix(line, "created by ") createdBy = true } i++ if i >= len(lines) { return nil, Errorf("bugsnag.panicParser: Invalid line (unpaired): %s", line) } frame, err := parsePanicFrame(line, lines[i], createdBy) if err != nil { return nil, err } stack = append(stack, *frame) if createdBy { state = "done" break } } } if state == "done" || state == "parsing" { return &Error{Err: uncaughtPanic{message}, frames: stack}, nil } return nil, Errorf("could not parse panic: %v", text) } // The lines we're passing look like this: // // main.(*foo).destruct(0xc208067e98) // /0/go/src/github.com/bugsnag/bugsnag-go/pan/main.go:22 +0x151 func parsePanicFrame(name string, line string, createdBy bool) (*StackFrame, error) { idx := strings.LastIndex(name, "(") if idx == -1 && !createdBy { return nil, Errorf("bugsnag.panicParser: Invalid line (no call): %s", name) } if idx != -1 { name = name[:idx] } pkg := "" if lastslash := strings.LastIndex(name, "/"); lastslash >= 0 { pkg += name[:lastslash] + "/" name = name[lastslash+1:] } if period := strings.Index(name, "."); period >= 0 { pkg += name[:period] name = name[period+1:] } name = strings.Replace(name, "·", ".", -1) if !strings.HasPrefix(line, "\t") { return nil, Errorf("bugsnag.panicParser: Invalid line (no tab): %s", line) } idx = strings.LastIndex(line, ":") if idx == -1 { return nil, Errorf("bugsnag.panicParser: Invalid line (no line number): %s", line) } file := line[1:idx] number := line[idx+1:] if idx = strings.Index(number, " +"); idx > -1 { number = number[:idx] } lno, err := strconv.ParseInt(number, 10, 32) if err != nil { return nil, Errorf("bugsnag.panicParser: Invalid line (bad line number): %s", line) } return &StackFrame{ File: file, LineNumber: int(lno), Package: pkg, Name: name, }, nil } ================================================ FILE: vendor/github.com/go-errors/errors/stackframe.go ================================================ package errors import ( "bufio" "bytes" "fmt" "os" "runtime" "strings" ) // A StackFrame contains all necessary information about to generate a line // in a callstack. type StackFrame struct { // The path to the file containing this ProgramCounter File string // The LineNumber in that file LineNumber int // The Name of the function that contains this ProgramCounter Name string // The Package that contains this function Package string // The underlying ProgramCounter ProgramCounter uintptr } // NewStackFrame popoulates a stack frame object from the program counter. func NewStackFrame(pc uintptr) (frame StackFrame) { frame = StackFrame{ProgramCounter: pc} if frame.Func() == nil { return } frame.Package, frame.Name = packageAndName(frame.Func()) // pc -1 because the program counters we use are usually return addresses, // and we want to show the line that corresponds to the function call frame.File, frame.LineNumber = frame.Func().FileLine(pc - 1) return } // Func returns the function that contained this frame. func (frame *StackFrame) Func() *runtime.Func { if frame.ProgramCounter == 0 { return nil } return runtime.FuncForPC(frame.ProgramCounter) } // String returns the stackframe formatted in the same way as go does // in runtime/debug.Stack() func (frame *StackFrame) String() string { str := fmt.Sprintf("%s:%d (0x%x)\n", frame.File, frame.LineNumber, frame.ProgramCounter) source, err := frame.sourceLine() if err != nil { return str } return str + fmt.Sprintf("\t%s: %s\n", frame.Name, source) } // SourceLine gets the line of code (from File and Line) of the original source if possible. func (frame *StackFrame) SourceLine() (string, error) { source, err := frame.sourceLine() if err != nil { return source, New(err) } return source, err } func (frame *StackFrame) sourceLine() (string, error) { if frame.LineNumber <= 0 { return "???", nil } file, err := os.Open(frame.File) if err != nil { return "", err } defer file.Close() scanner := bufio.NewScanner(file) currentLine := 1 for scanner.Scan() { if currentLine == frame.LineNumber { return string(bytes.Trim(scanner.Bytes(), " \t")), nil } currentLine++ } if err := scanner.Err(); err != nil { return "", err } return "???", nil } func packageAndName(fn *runtime.Func) (string, string) { name := fn.Name() pkg := "" // The name includes the path name to the package, which is unnecessary // since the file name is already included. Plus, it has center dots. // That is, we see // runtime/debug.*T·ptrmethod // and want // *T.ptrmethod // Since the package path might contains dots (e.g. code.google.com/...), // we first remove the path prefix if there is one. if lastslash := strings.LastIndex(name, "/"); lastslash >= 0 { pkg += name[:lastslash] + "/" name = name[lastslash+1:] } if period := strings.Index(name, "."); period >= 0 { pkg += name[:period] name = name[period+1:] } name = strings.Replace(name, "·", ".", -1) return pkg, name } ================================================ FILE: vendor/github.com/go-logr/logr/.golangci.yaml ================================================ run: timeout: 1m tests: true linters: disable-all: true enable: - asciicheck - errcheck - forcetypeassert - gocritic - gofmt - goimports - gosimple - govet - ineffassign - misspell - revive - staticcheck - typecheck - unused issues: exclude-use-default: false max-issues-per-linter: 0 max-same-issues: 10 ================================================ FILE: vendor/github.com/go-logr/logr/CHANGELOG.md ================================================ # CHANGELOG ## v1.0.0-rc1 This is the first logged release. Major changes (including breaking changes) have occurred since earlier tags. ================================================ FILE: vendor/github.com/go-logr/logr/CONTRIBUTING.md ================================================ # Contributing Logr is open to pull-requests, provided they fit within the intended scope of the project. Specifically, this library aims to be VERY small and minimalist, with no external dependencies. ## Compatibility This project intends to follow [semantic versioning](http://semver.org) and is very strict about compatibility. Any proposed changes MUST follow those rules. ## Performance As a logging library, logr must be as light-weight as possible. Any proposed code change must include results of running the [benchmark](./benchmark) before and after the change. ================================================ FILE: vendor/github.com/go-logr/logr/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/go-logr/logr/README.md ================================================ # A minimal logging API for Go [![Go Reference](https://pkg.go.dev/badge/github.com/go-logr/logr.svg)](https://pkg.go.dev/github.com/go-logr/logr) logr offers an(other) opinion on how Go programs and libraries can do logging without becoming coupled to a particular logging implementation. This is not an implementation of logging - it is an API. In fact it is two APIs with two different sets of users. The `Logger` type is intended for application and library authors. It provides a relatively small API which can be used everywhere you want to emit logs. It defers the actual act of writing logs (to files, to stdout, or whatever) to the `LogSink` interface. The `LogSink` interface is intended for logging library implementers. It is a pure interface which can be implemented by logging frameworks to provide the actual logging functionality. This decoupling allows application and library developers to write code in terms of `logr.Logger` (which has very low dependency fan-out) while the implementation of logging is managed "up stack" (e.g. in or near `main()`.) Application developers can then switch out implementations as necessary. Many people assert that libraries should not be logging, and as such efforts like this are pointless. Those people are welcome to convince the authors of the tens-of-thousands of libraries that *DO* write logs that they are all wrong. In the meantime, logr takes a more practical approach. ## Typical usage Somewhere, early in an application's life, it will make a decision about which logging library (implementation) it actually wants to use. Something like: ``` func main() { // ... other setup code ... // Create the "root" logger. We have chosen the "logimpl" implementation, // which takes some initial parameters and returns a logr.Logger. logger := logimpl.New(param1, param2) // ... other setup code ... ``` Most apps will call into other libraries, create structures to govern the flow, etc. The `logr.Logger` object can be passed to these other libraries, stored in structs, or even used as a package-global variable, if needed. For example: ``` app := createTheAppObject(logger) app.Run() ``` Outside of this early setup, no other packages need to know about the choice of implementation. They write logs in terms of the `logr.Logger` that they received: ``` type appObject struct { // ... other fields ... logger logr.Logger // ... other fields ... } func (app *appObject) Run() { app.logger.Info("starting up", "timestamp", time.Now()) // ... app code ... ``` ## Background If the Go standard library had defined an interface for logging, this project probably would not be needed. Alas, here we are. ### Inspiration Before you consider this package, please read [this blog post by the inimitable Dave Cheney][warning-makes-no-sense]. We really appreciate what he has to say, and it largely aligns with our own experiences. ### Differences from Dave's ideas The main differences are: 1. Dave basically proposes doing away with the notion of a logging API in favor of `fmt.Printf()`. We disagree, especially when you consider things like output locations, timestamps, file and line decorations, and structured logging. This package restricts the logging API to just 2 types of logs: info and error. Info logs are things you want to tell the user which are not errors. Error logs are, well, errors. If your code receives an `error` from a subordinate function call and is logging that `error` *and not returning it*, use error logs. 2. Verbosity-levels on info logs. This gives developers a chance to indicate arbitrary grades of importance for info logs, without assigning names with semantic meaning such as "warning", "trace", and "debug." Superficially this may feel very similar, but the primary difference is the lack of semantics. Because verbosity is a numerical value, it's safe to assume that an app running with higher verbosity means more (and less important) logs will be generated. ## Implementations (non-exhaustive) There are implementations for the following logging libraries: - **a function** (can bridge to non-structured libraries): [funcr](https://github.com/go-logr/logr/tree/master/funcr) - **a testing.T** (for use in Go tests, with JSON-like output): [testr](https://github.com/go-logr/logr/tree/master/testr) - **github.com/google/glog**: [glogr](https://github.com/go-logr/glogr) - **k8s.io/klog** (for Kubernetes): [klogr](https://git.k8s.io/klog/klogr) - **a testing.T** (with klog-like text output): [ktesting](https://git.k8s.io/klog/ktesting) - **go.uber.org/zap**: [zapr](https://github.com/go-logr/zapr) - **log** (the Go standard library logger): [stdr](https://github.com/go-logr/stdr) - **github.com/sirupsen/logrus**: [logrusr](https://github.com/bombsimon/logrusr) - **github.com/wojas/genericr**: [genericr](https://github.com/wojas/genericr) (makes it easy to implement your own backend) - **logfmt** (Heroku style [logging](https://www.brandur.org/logfmt)): [logfmtr](https://github.com/iand/logfmtr) - **github.com/rs/zerolog**: [zerologr](https://github.com/go-logr/zerologr) - **github.com/go-kit/log**: [gokitlogr](https://github.com/tonglil/gokitlogr) (also compatible with github.com/go-kit/kit/log since v0.12.0) - **bytes.Buffer** (writing to a buffer): [bufrlogr](https://github.com/tonglil/buflogr) (useful for ensuring values were logged, like during testing) ## FAQ ### Conceptual #### Why structured logging? - **Structured logs are more easily queryable**: Since you've got key-value pairs, it's much easier to query your structured logs for particular values by filtering on the contents of a particular key -- think searching request logs for error codes, Kubernetes reconcilers for the name and namespace of the reconciled object, etc. - **Structured logging makes it easier to have cross-referenceable logs**: Similarly to searchability, if you maintain conventions around your keys, it becomes easy to gather all log lines related to a particular concept. - **Structured logs allow better dimensions of filtering**: if you have structure to your logs, you've got more precise control over how much information is logged -- you might choose in a particular configuration to log certain keys but not others, only log lines where a certain key matches a certain value, etc., instead of just having v-levels and names to key off of. - **Structured logs better represent structured data**: sometimes, the data that you want to log is inherently structured (think tuple-link objects.) Structured logs allow you to preserve that structure when outputting. #### Why V-levels? **V-levels give operators an easy way to control the chattiness of log operations**. V-levels provide a way for a given package to distinguish the relative importance or verbosity of a given log message. Then, if a particular logger or package is logging too many messages, the user of the package can simply change the v-levels for that library. #### Why not named levels, like Info/Warning/Error? Read [Dave Cheney's post][warning-makes-no-sense]. Then read [Differences from Dave's ideas](#differences-from-daves-ideas). #### Why not allow format strings, too? **Format strings negate many of the benefits of structured logs**: - They're not easily searchable without resorting to fuzzy searching, regular expressions, etc. - They don't store structured data well, since contents are flattened into a string. - They're not cross-referenceable. - They don't compress easily, since the message is not constant. (Unless you turn positional parameters into key-value pairs with numerical keys, at which point you've gotten key-value logging with meaningless keys.) ### Practical #### Why key-value pairs, and not a map? Key-value pairs are *much* easier to optimize, especially around allocations. Zap (a structured logger that inspired logr's interface) has [performance measurements](https://github.com/uber-go/zap#performance) that show this quite nicely. While the interface ends up being a little less obvious, you get potentially better performance, plus avoid making users type `map[string]string{}` every time they want to log. #### What if my V-levels differ between libraries? That's fine. Control your V-levels on a per-logger basis, and use the `WithName` method to pass different loggers to different libraries. Generally, you should take care to ensure that you have relatively consistent V-levels within a given logger, however, as this makes deciding on what verbosity of logs to request easier. #### But I really want to use a format string! That's not actually a question. Assuming your question is "how do I convert my mental model of logging with format strings to logging with constant messages": 1. Figure out what the error actually is, as you'd write in a TL;DR style, and use that as a message. 2. For every place you'd write a format specifier, look to the word before it, and add that as a key value pair. For instance, consider the following examples (all taken from spots in the Kubernetes codebase): - `klog.V(4).Infof("Client is returning errors: code %v, error %v", responseCode, err)` becomes `logger.Error(err, "client returned an error", "code", responseCode)` - `klog.V(4).Infof("Got a Retry-After %ds response for attempt %d to %v", seconds, retries, url)` becomes `logger.V(4).Info("got a retry-after response when requesting url", "attempt", retries, "after seconds", seconds, "url", url)` If you *really* must use a format string, use it in a key's value, and call `fmt.Sprintf` yourself. For instance: `log.Printf("unable to reflect over type %T")` becomes `logger.Info("unable to reflect over type", "type", fmt.Sprintf("%T"))`. In general though, the cases where this is necessary should be few and far between. #### How do I choose my V-levels? This is basically the only hard constraint: increase V-levels to denote more verbose or more debug-y logs. Otherwise, you can start out with `0` as "you always want to see this", `1` as "common logging that you might *possibly* want to turn off", and `10` as "I would like to performance-test your log collection stack." Then gradually choose levels in between as you need them, working your way down from 10 (for debug and trace style logs) and up from 1 (for chattier info-type logs.) #### How do I choose my keys? Keys are fairly flexible, and can hold more or less any string value. For best compatibility with implementations and consistency with existing code in other projects, there are a few conventions you should consider. - Make your keys human-readable. - Constant keys are generally a good idea. - Be consistent across your codebase. - Keys should naturally match parts of the message string. - Use lower case for simple keys and [lowerCamelCase](https://en.wiktionary.org/wiki/lowerCamelCase) for more complex ones. Kubernetes is one example of a project that has [adopted that convention](https://github.com/kubernetes/community/blob/HEAD/contributors/devel/sig-instrumentation/migration-to-structured-logging.md#name-arguments). While key names are mostly unrestricted (and spaces are acceptable), it's generally a good idea to stick to printable ascii characters, or at least match the general character set of your log lines. #### Why should keys be constant values? The point of structured logging is to make later log processing easier. Your keys are, effectively, the schema of each log message. If you use different keys across instances of the same log line, you will make your structured logs much harder to use. `Sprintf()` is for values, not for keys! #### Why is this not a pure interface? The Logger type is implemented as a struct in order to allow the Go compiler to optimize things like high-V `Info` logs that are not triggered. Not all of these implementations are implemented yet, but this structure was suggested as a way to ensure they *can* be implemented. All of the real work is behind the `LogSink` interface. [warning-makes-no-sense]: http://dave.cheney.net/2015/11/05/lets-talk-about-logging ================================================ FILE: vendor/github.com/go-logr/logr/discard.go ================================================ /* Copyright 2020 The logr Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package logr // Discard returns a Logger that discards all messages logged to it. It can be // used whenever the caller is not interested in the logs. Logger instances // produced by this function always compare as equal. func Discard() Logger { return New(nil) } ================================================ FILE: vendor/github.com/go-logr/logr/funcr/funcr.go ================================================ /* Copyright 2021 The logr Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // Package funcr implements formatting of structured log messages and // optionally captures the call site and timestamp. // // The simplest way to use it is via its implementation of a // github.com/go-logr/logr.LogSink with output through an arbitrary // "write" function. See New and NewJSON for details. // // # Custom LogSinks // // For users who need more control, a funcr.Formatter can be embedded inside // your own custom LogSink implementation. This is useful when the LogSink // needs to implement additional methods, for example. // // # Formatting // // This will respect logr.Marshaler, fmt.Stringer, and error interfaces for // values which are being logged. When rendering a struct, funcr will use Go's // standard JSON tags (all except "string"). package funcr import ( "bytes" "encoding" "encoding/json" "fmt" "path/filepath" "reflect" "runtime" "strconv" "strings" "time" "github.com/go-logr/logr" ) // New returns a logr.Logger which is implemented by an arbitrary function. func New(fn func(prefix, args string), opts Options) logr.Logger { return logr.New(newSink(fn, NewFormatter(opts))) } // NewJSON returns a logr.Logger which is implemented by an arbitrary function // and produces JSON output. func NewJSON(fn func(obj string), opts Options) logr.Logger { fnWrapper := func(_, obj string) { fn(obj) } return logr.New(newSink(fnWrapper, NewFormatterJSON(opts))) } // Underlier exposes access to the underlying logging function. Since // callers only have a logr.Logger, they have to know which // implementation is in use, so this interface is less of an // abstraction and more of a way to test type conversion. type Underlier interface { GetUnderlying() func(prefix, args string) } func newSink(fn func(prefix, args string), formatter Formatter) logr.LogSink { l := &fnlogger{ Formatter: formatter, write: fn, } // For skipping fnlogger.Info and fnlogger.Error. l.Formatter.AddCallDepth(1) return l } // Options carries parameters which influence the way logs are generated. type Options struct { // LogCaller tells funcr to add a "caller" key to some or all log lines. // This has some overhead, so some users might not want it. LogCaller MessageClass // LogCallerFunc tells funcr to also log the calling function name. This // has no effect if caller logging is not enabled (see Options.LogCaller). LogCallerFunc bool // LogTimestamp tells funcr to add a "ts" key to log lines. This has some // overhead, so some users might not want it. LogTimestamp bool // TimestampFormat tells funcr how to render timestamps when LogTimestamp // is enabled. If not specified, a default format will be used. For more // details, see docs for Go's time.Layout. TimestampFormat string // Verbosity tells funcr which V logs to produce. Higher values enable // more logs. Info logs at or below this level will be written, while logs // above this level will be discarded. Verbosity int // RenderBuiltinsHook allows users to mutate the list of key-value pairs // while a log line is being rendered. The kvList argument follows logr // conventions - each pair of slice elements is comprised of a string key // and an arbitrary value (verified and sanitized before calling this // hook). The value returned must follow the same conventions. This hook // can be used to audit or modify logged data. For example, you might want // to prefix all of funcr's built-in keys with some string. This hook is // only called for built-in (provided by funcr itself) key-value pairs. // Equivalent hooks are offered for key-value pairs saved via // logr.Logger.WithValues or Formatter.AddValues (see RenderValuesHook) and // for user-provided pairs (see RenderArgsHook). RenderBuiltinsHook func(kvList []interface{}) []interface{} // RenderValuesHook is the same as RenderBuiltinsHook, except that it is // only called for key-value pairs saved via logr.Logger.WithValues. See // RenderBuiltinsHook for more details. RenderValuesHook func(kvList []interface{}) []interface{} // RenderArgsHook is the same as RenderBuiltinsHook, except that it is only // called for key-value pairs passed directly to Info and Error. See // RenderBuiltinsHook for more details. RenderArgsHook func(kvList []interface{}) []interface{} // MaxLogDepth tells funcr how many levels of nested fields (e.g. a struct // that contains a struct, etc.) it may log. Every time it finds a struct, // slice, array, or map the depth is increased by one. When the maximum is // reached, the value will be converted to a string indicating that the max // depth has been exceeded. If this field is not specified, a default // value will be used. MaxLogDepth int } // MessageClass indicates which category or categories of messages to consider. type MessageClass int const ( // None ignores all message classes. None MessageClass = iota // All considers all message classes. All // Info only considers info messages. Info // Error only considers error messages. Error ) // fnlogger inherits some of its LogSink implementation from Formatter // and just needs to add some glue code. type fnlogger struct { Formatter write func(prefix, args string) } func (l fnlogger) WithName(name string) logr.LogSink { l.Formatter.AddName(name) return &l } func (l fnlogger) WithValues(kvList ...interface{}) logr.LogSink { l.Formatter.AddValues(kvList) return &l } func (l fnlogger) WithCallDepth(depth int) logr.LogSink { l.Formatter.AddCallDepth(depth) return &l } func (l fnlogger) Info(level int, msg string, kvList ...interface{}) { prefix, args := l.FormatInfo(level, msg, kvList) l.write(prefix, args) } func (l fnlogger) Error(err error, msg string, kvList ...interface{}) { prefix, args := l.FormatError(err, msg, kvList) l.write(prefix, args) } func (l fnlogger) GetUnderlying() func(prefix, args string) { return l.write } // Assert conformance to the interfaces. var _ logr.LogSink = &fnlogger{} var _ logr.CallDepthLogSink = &fnlogger{} var _ Underlier = &fnlogger{} // NewFormatter constructs a Formatter which emits a JSON-like key=value format. func NewFormatter(opts Options) Formatter { return newFormatter(opts, outputKeyValue) } // NewFormatterJSON constructs a Formatter which emits strict JSON. func NewFormatterJSON(opts Options) Formatter { return newFormatter(opts, outputJSON) } // Defaults for Options. const defaultTimestampFormat = "2006-01-02 15:04:05.000000" const defaultMaxLogDepth = 16 func newFormatter(opts Options, outfmt outputFormat) Formatter { if opts.TimestampFormat == "" { opts.TimestampFormat = defaultTimestampFormat } if opts.MaxLogDepth == 0 { opts.MaxLogDepth = defaultMaxLogDepth } f := Formatter{ outputFormat: outfmt, prefix: "", values: nil, depth: 0, opts: &opts, } return f } // Formatter is an opaque struct which can be embedded in a LogSink // implementation. It should be constructed with NewFormatter. Some of // its methods directly implement logr.LogSink. type Formatter struct { outputFormat outputFormat prefix string values []interface{} valuesStr string depth int opts *Options } // outputFormat indicates which outputFormat to use. type outputFormat int const ( // outputKeyValue emits a JSON-like key=value format, but not strict JSON. outputKeyValue outputFormat = iota // outputJSON emits strict JSON. outputJSON ) // PseudoStruct is a list of key-value pairs that gets logged as a struct. type PseudoStruct []interface{} // render produces a log line, ready to use. func (f Formatter) render(builtins, args []interface{}) string { // Empirically bytes.Buffer is faster than strings.Builder for this. buf := bytes.NewBuffer(make([]byte, 0, 1024)) if f.outputFormat == outputJSON { buf.WriteByte('{') } vals := builtins if hook := f.opts.RenderBuiltinsHook; hook != nil { vals = hook(f.sanitize(vals)) } f.flatten(buf, vals, false, false) // keys are ours, no need to escape continuing := len(builtins) > 0 if len(f.valuesStr) > 0 { if continuing { if f.outputFormat == outputJSON { buf.WriteByte(',') } else { buf.WriteByte(' ') } } continuing = true buf.WriteString(f.valuesStr) } vals = args if hook := f.opts.RenderArgsHook; hook != nil { vals = hook(f.sanitize(vals)) } f.flatten(buf, vals, continuing, true) // escape user-provided keys if f.outputFormat == outputJSON { buf.WriteByte('}') } return buf.String() } // flatten renders a list of key-value pairs into a buffer. If continuing is // true, it assumes that the buffer has previous values and will emit a // separator (which depends on the output format) before the first pair it // writes. If escapeKeys is true, the keys are assumed to have // non-JSON-compatible characters in them and must be evaluated for escapes. // // This function returns a potentially modified version of kvList, which // ensures that there is a value for every key (adding a value if needed) and // that each key is a string (substituting a key if needed). func (f Formatter) flatten(buf *bytes.Buffer, kvList []interface{}, continuing bool, escapeKeys bool) []interface{} { // This logic overlaps with sanitize() but saves one type-cast per key, // which can be measurable. if len(kvList)%2 != 0 { kvList = append(kvList, noValue) } for i := 0; i < len(kvList); i += 2 { k, ok := kvList[i].(string) if !ok { k = f.nonStringKey(kvList[i]) kvList[i] = k } v := kvList[i+1] if i > 0 || continuing { if f.outputFormat == outputJSON { buf.WriteByte(',') } else { // In theory the format could be something we don't understand. In // practice, we control it, so it won't be. buf.WriteByte(' ') } } if escapeKeys { buf.WriteString(prettyString(k)) } else { // this is faster buf.WriteByte('"') buf.WriteString(k) buf.WriteByte('"') } if f.outputFormat == outputJSON { buf.WriteByte(':') } else { buf.WriteByte('=') } buf.WriteString(f.pretty(v)) } return kvList } func (f Formatter) pretty(value interface{}) string { return f.prettyWithFlags(value, 0, 0) } const ( flagRawStruct = 0x1 // do not print braces on structs ) // TODO: This is not fast. Most of the overhead goes here. func (f Formatter) prettyWithFlags(value interface{}, flags uint32, depth int) string { if depth > f.opts.MaxLogDepth { return `""` } // Handle types that take full control of logging. if v, ok := value.(logr.Marshaler); ok { // Replace the value with what the type wants to get logged. // That then gets handled below via reflection. value = invokeMarshaler(v) } // Handle types that want to format themselves. switch v := value.(type) { case fmt.Stringer: value = invokeStringer(v) case error: value = invokeError(v) } // Handling the most common types without reflect is a small perf win. switch v := value.(type) { case bool: return strconv.FormatBool(v) case string: return prettyString(v) case int: return strconv.FormatInt(int64(v), 10) case int8: return strconv.FormatInt(int64(v), 10) case int16: return strconv.FormatInt(int64(v), 10) case int32: return strconv.FormatInt(int64(v), 10) case int64: return strconv.FormatInt(int64(v), 10) case uint: return strconv.FormatUint(uint64(v), 10) case uint8: return strconv.FormatUint(uint64(v), 10) case uint16: return strconv.FormatUint(uint64(v), 10) case uint32: return strconv.FormatUint(uint64(v), 10) case uint64: return strconv.FormatUint(v, 10) case uintptr: return strconv.FormatUint(uint64(v), 10) case float32: return strconv.FormatFloat(float64(v), 'f', -1, 32) case float64: return strconv.FormatFloat(v, 'f', -1, 64) case complex64: return `"` + strconv.FormatComplex(complex128(v), 'f', -1, 64) + `"` case complex128: return `"` + strconv.FormatComplex(v, 'f', -1, 128) + `"` case PseudoStruct: buf := bytes.NewBuffer(make([]byte, 0, 1024)) v = f.sanitize(v) if flags&flagRawStruct == 0 { buf.WriteByte('{') } for i := 0; i < len(v); i += 2 { if i > 0 { buf.WriteByte(',') } k, _ := v[i].(string) // sanitize() above means no need to check success // arbitrary keys might need escaping buf.WriteString(prettyString(k)) buf.WriteByte(':') buf.WriteString(f.prettyWithFlags(v[i+1], 0, depth+1)) } if flags&flagRawStruct == 0 { buf.WriteByte('}') } return buf.String() } buf := bytes.NewBuffer(make([]byte, 0, 256)) t := reflect.TypeOf(value) if t == nil { return "null" } v := reflect.ValueOf(value) switch t.Kind() { case reflect.Bool: return strconv.FormatBool(v.Bool()) case reflect.String: return prettyString(v.String()) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return strconv.FormatInt(int64(v.Int()), 10) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return strconv.FormatUint(uint64(v.Uint()), 10) case reflect.Float32: return strconv.FormatFloat(float64(v.Float()), 'f', -1, 32) case reflect.Float64: return strconv.FormatFloat(v.Float(), 'f', -1, 64) case reflect.Complex64: return `"` + strconv.FormatComplex(complex128(v.Complex()), 'f', -1, 64) + `"` case reflect.Complex128: return `"` + strconv.FormatComplex(v.Complex(), 'f', -1, 128) + `"` case reflect.Struct: if flags&flagRawStruct == 0 { buf.WriteByte('{') } printComma := false // testing i>0 is not enough because of JSON omitted fields for i := 0; i < t.NumField(); i++ { fld := t.Field(i) if fld.PkgPath != "" { // reflect says this field is only defined for non-exported fields. continue } if !v.Field(i).CanInterface() { // reflect isn't clear exactly what this means, but we can't use it. continue } name := "" omitempty := false if tag, found := fld.Tag.Lookup("json"); found { if tag == "-" { continue } if comma := strings.Index(tag, ","); comma != -1 { if n := tag[:comma]; n != "" { name = n } rest := tag[comma:] if strings.Contains(rest, ",omitempty,") || strings.HasSuffix(rest, ",omitempty") { omitempty = true } } else { name = tag } } if omitempty && isEmpty(v.Field(i)) { continue } if printComma { buf.WriteByte(',') } printComma = true // if we got here, we are rendering a field if fld.Anonymous && fld.Type.Kind() == reflect.Struct && name == "" { buf.WriteString(f.prettyWithFlags(v.Field(i).Interface(), flags|flagRawStruct, depth+1)) continue } if name == "" { name = fld.Name } // field names can't contain characters which need escaping buf.WriteByte('"') buf.WriteString(name) buf.WriteByte('"') buf.WriteByte(':') buf.WriteString(f.prettyWithFlags(v.Field(i).Interface(), 0, depth+1)) } if flags&flagRawStruct == 0 { buf.WriteByte('}') } return buf.String() case reflect.Slice, reflect.Array: // If this is outputing as JSON make sure this isn't really a json.RawMessage. // If so just emit "as-is" and don't pretty it as that will just print // it as [X,Y,Z,...] which isn't terribly useful vs the string form you really want. if f.outputFormat == outputJSON { if rm, ok := value.(json.RawMessage); ok { // If it's empty make sure we emit an empty value as the array style would below. if len(rm) > 0 { buf.Write(rm) } else { buf.WriteString("null") } return buf.String() } } buf.WriteByte('[') for i := 0; i < v.Len(); i++ { if i > 0 { buf.WriteByte(',') } e := v.Index(i) buf.WriteString(f.prettyWithFlags(e.Interface(), 0, depth+1)) } buf.WriteByte(']') return buf.String() case reflect.Map: buf.WriteByte('{') // This does not sort the map keys, for best perf. it := v.MapRange() i := 0 for it.Next() { if i > 0 { buf.WriteByte(',') } // If a map key supports TextMarshaler, use it. keystr := "" if m, ok := it.Key().Interface().(encoding.TextMarshaler); ok { txt, err := m.MarshalText() if err != nil { keystr = fmt.Sprintf("", err.Error()) } else { keystr = string(txt) } keystr = prettyString(keystr) } else { // prettyWithFlags will produce already-escaped values keystr = f.prettyWithFlags(it.Key().Interface(), 0, depth+1) if t.Key().Kind() != reflect.String { // JSON only does string keys. Unlike Go's standard JSON, we'll // convert just about anything to a string. keystr = prettyString(keystr) } } buf.WriteString(keystr) buf.WriteByte(':') buf.WriteString(f.prettyWithFlags(it.Value().Interface(), 0, depth+1)) i++ } buf.WriteByte('}') return buf.String() case reflect.Ptr, reflect.Interface: if v.IsNil() { return "null" } return f.prettyWithFlags(v.Elem().Interface(), 0, depth) } return fmt.Sprintf(`""`, t.Kind().String()) } func prettyString(s string) string { // Avoid escaping (which does allocations) if we can. if needsEscape(s) { return strconv.Quote(s) } b := bytes.NewBuffer(make([]byte, 0, 1024)) b.WriteByte('"') b.WriteString(s) b.WriteByte('"') return b.String() } // needsEscape determines whether the input string needs to be escaped or not, // without doing any allocations. func needsEscape(s string) bool { for _, r := range s { if !strconv.IsPrint(r) || r == '\\' || r == '"' { return true } } return false } func isEmpty(v reflect.Value) bool { switch v.Kind() { case reflect.Array, reflect.Map, reflect.Slice, reflect.String: return v.Len() == 0 case reflect.Bool: return !v.Bool() case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return v.Int() == 0 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return v.Uint() == 0 case reflect.Float32, reflect.Float64: return v.Float() == 0 case reflect.Complex64, reflect.Complex128: return v.Complex() == 0 case reflect.Interface, reflect.Ptr: return v.IsNil() } return false } func invokeMarshaler(m logr.Marshaler) (ret interface{}) { defer func() { if r := recover(); r != nil { ret = fmt.Sprintf("", r) } }() return m.MarshalLog() } func invokeStringer(s fmt.Stringer) (ret string) { defer func() { if r := recover(); r != nil { ret = fmt.Sprintf("", r) } }() return s.String() } func invokeError(e error) (ret string) { defer func() { if r := recover(); r != nil { ret = fmt.Sprintf("", r) } }() return e.Error() } // Caller represents the original call site for a log line, after considering // logr.Logger.WithCallDepth and logr.Logger.WithCallStackHelper. The File and // Line fields will always be provided, while the Func field is optional. // Users can set the render hook fields in Options to examine logged key-value // pairs, one of which will be {"caller", Caller} if the Options.LogCaller // field is enabled for the given MessageClass. type Caller struct { // File is the basename of the file for this call site. File string `json:"file"` // Line is the line number in the file for this call site. Line int `json:"line"` // Func is the function name for this call site, or empty if // Options.LogCallerFunc is not enabled. Func string `json:"function,omitempty"` } func (f Formatter) caller() Caller { // +1 for this frame, +1 for Info/Error. pc, file, line, ok := runtime.Caller(f.depth + 2) if !ok { return Caller{"", 0, ""} } fn := "" if f.opts.LogCallerFunc { if fp := runtime.FuncForPC(pc); fp != nil { fn = fp.Name() } } return Caller{filepath.Base(file), line, fn} } const noValue = "" func (f Formatter) nonStringKey(v interface{}) string { return fmt.Sprintf("", f.snippet(v)) } // snippet produces a short snippet string of an arbitrary value. func (f Formatter) snippet(v interface{}) string { const snipLen = 16 snip := f.pretty(v) if len(snip) > snipLen { snip = snip[:snipLen] } return snip } // sanitize ensures that a list of key-value pairs has a value for every key // (adding a value if needed) and that each key is a string (substituting a key // if needed). func (f Formatter) sanitize(kvList []interface{}) []interface{} { if len(kvList)%2 != 0 { kvList = append(kvList, noValue) } for i := 0; i < len(kvList); i += 2 { _, ok := kvList[i].(string) if !ok { kvList[i] = f.nonStringKey(kvList[i]) } } return kvList } // Init configures this Formatter from runtime info, such as the call depth // imposed by logr itself. // Note that this receiver is a pointer, so depth can be saved. func (f *Formatter) Init(info logr.RuntimeInfo) { f.depth += info.CallDepth } // Enabled checks whether an info message at the given level should be logged. func (f Formatter) Enabled(level int) bool { return level <= f.opts.Verbosity } // GetDepth returns the current depth of this Formatter. This is useful for // implementations which do their own caller attribution. func (f Formatter) GetDepth() int { return f.depth } // FormatInfo renders an Info log message into strings. The prefix will be // empty when no names were set (via AddNames), or when the output is // configured for JSON. func (f Formatter) FormatInfo(level int, msg string, kvList []interface{}) (prefix, argsStr string) { args := make([]interface{}, 0, 64) // using a constant here impacts perf prefix = f.prefix if f.outputFormat == outputJSON { args = append(args, "logger", prefix) prefix = "" } if f.opts.LogTimestamp { args = append(args, "ts", time.Now().Format(f.opts.TimestampFormat)) } if policy := f.opts.LogCaller; policy == All || policy == Info { args = append(args, "caller", f.caller()) } args = append(args, "level", level, "msg", msg) return prefix, f.render(args, kvList) } // FormatError renders an Error log message into strings. The prefix will be // empty when no names were set (via AddNames), or when the output is // configured for JSON. func (f Formatter) FormatError(err error, msg string, kvList []interface{}) (prefix, argsStr string) { args := make([]interface{}, 0, 64) // using a constant here impacts perf prefix = f.prefix if f.outputFormat == outputJSON { args = append(args, "logger", prefix) prefix = "" } if f.opts.LogTimestamp { args = append(args, "ts", time.Now().Format(f.opts.TimestampFormat)) } if policy := f.opts.LogCaller; policy == All || policy == Error { args = append(args, "caller", f.caller()) } args = append(args, "msg", msg) var loggableErr interface{} if err != nil { loggableErr = err.Error() } args = append(args, "error", loggableErr) return f.prefix, f.render(args, kvList) } // AddName appends the specified name. funcr uses '/' characters to separate // name elements. Callers should not pass '/' in the provided name string, but // this library does not actually enforce that. func (f *Formatter) AddName(name string) { if len(f.prefix) > 0 { f.prefix += "/" } f.prefix += name } // AddValues adds key-value pairs to the set of saved values to be logged with // each log line. func (f *Formatter) AddValues(kvList []interface{}) { // Three slice args forces a copy. n := len(f.values) f.values = append(f.values[:n:n], kvList...) vals := f.values if hook := f.opts.RenderValuesHook; hook != nil { vals = hook(f.sanitize(vals)) } // Pre-render values, so we don't have to do it on each Info/Error call. buf := bytes.NewBuffer(make([]byte, 0, 1024)) f.flatten(buf, vals, false, true) // escape user-provided keys f.valuesStr = buf.String() } // AddCallDepth increases the number of stack-frames to skip when attributing // the log line to a file and line. func (f *Formatter) AddCallDepth(depth int) { f.depth += depth } ================================================ FILE: vendor/github.com/go-logr/logr/logr.go ================================================ /* Copyright 2019 The logr Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // This design derives from Dave Cheney's blog: // http://dave.cheney.net/2015/11/05/lets-talk-about-logging // Package logr defines a general-purpose logging API and abstract interfaces // to back that API. Packages in the Go ecosystem can depend on this package, // while callers can implement logging with whatever backend is appropriate. // // # Usage // // Logging is done using a Logger instance. Logger is a concrete type with // methods, which defers the actual logging to a LogSink interface. The main // methods of Logger are Info() and Error(). Arguments to Info() and Error() // are key/value pairs rather than printf-style formatted strings, emphasizing // "structured logging". // // With Go's standard log package, we might write: // // log.Printf("setting target value %s", targetValue) // // With logr's structured logging, we'd write: // // logger.Info("setting target", "value", targetValue) // // Errors are much the same. Instead of: // // log.Printf("failed to open the pod bay door for user %s: %v", user, err) // // We'd write: // // logger.Error(err, "failed to open the pod bay door", "user", user) // // Info() and Error() are very similar, but they are separate methods so that // LogSink implementations can choose to do things like attach additional // information (such as stack traces) on calls to Error(). Error() messages are // always logged, regardless of the current verbosity. If there is no error // instance available, passing nil is valid. // // # Verbosity // // Often we want to log information only when the application in "verbose // mode". To write log lines that are more verbose, Logger has a V() method. // The higher the V-level of a log line, the less critical it is considered. // Log-lines with V-levels that are not enabled (as per the LogSink) will not // be written. Level V(0) is the default, and logger.V(0).Info() has the same // meaning as logger.Info(). Negative V-levels have the same meaning as V(0). // Error messages do not have a verbosity level and are always logged. // // Where we might have written: // // if flVerbose >= 2 { // log.Printf("an unusual thing happened") // } // // We can write: // // logger.V(2).Info("an unusual thing happened") // // # Logger Names // // Logger instances can have name strings so that all messages logged through // that instance have additional context. For example, you might want to add // a subsystem name: // // logger.WithName("compactor").Info("started", "time", time.Now()) // // The WithName() method returns a new Logger, which can be passed to // constructors or other functions for further use. Repeated use of WithName() // will accumulate name "segments". These name segments will be joined in some // way by the LogSink implementation. It is strongly recommended that name // segments contain simple identifiers (letters, digits, and hyphen), and do // not contain characters that could muddle the log output or confuse the // joining operation (e.g. whitespace, commas, periods, slashes, brackets, // quotes, etc). // // # Saved Values // // Logger instances can store any number of key/value pairs, which will be // logged alongside all messages logged through that instance. For example, // you might want to create a Logger instance per managed object: // // With the standard log package, we might write: // // log.Printf("decided to set field foo to value %q for object %s/%s", // targetValue, object.Namespace, object.Name) // // With logr we'd write: // // // Elsewhere: set up the logger to log the object name. // obj.logger = mainLogger.WithValues( // "name", obj.name, "namespace", obj.namespace) // // // later on... // obj.logger.Info("setting foo", "value", targetValue) // // # Best Practices // // Logger has very few hard rules, with the goal that LogSink implementations // might have a lot of freedom to differentiate. There are, however, some // things to consider. // // The log message consists of a constant message attached to the log line. // This should generally be a simple description of what's occurring, and should // never be a format string. Variable information can then be attached using // named values. // // Keys are arbitrary strings, but should generally be constant values. Values // may be any Go value, but how the value is formatted is determined by the // LogSink implementation. // // Logger instances are meant to be passed around by value. Code that receives // such a value can call its methods without having to check whether the // instance is ready for use. // // Calling methods with the null logger (Logger{}) as instance will crash // because it has no LogSink. Therefore this null logger should never be passed // around. For cases where passing a logger is optional, a pointer to Logger // should be used. // // # Key Naming Conventions // // Keys are not strictly required to conform to any specification or regex, but // it is recommended that they: // - be human-readable and meaningful (not auto-generated or simple ordinals) // - be constant (not dependent on input data) // - contain only printable characters // - not contain whitespace or punctuation // - use lower case for simple keys and lowerCamelCase for more complex ones // // These guidelines help ensure that log data is processed properly regardless // of the log implementation. For example, log implementations will try to // output JSON data or will store data for later database (e.g. SQL) queries. // // While users are generally free to use key names of their choice, it's // generally best to avoid using the following keys, as they're frequently used // by implementations: // - "caller": the calling information (file/line) of a particular log line // - "error": the underlying error value in the `Error` method // - "level": the log level // - "logger": the name of the associated logger // - "msg": the log message // - "stacktrace": the stack trace associated with a particular log line or // error (often from the `Error` message) // - "ts": the timestamp for a log line // // Implementations are encouraged to make use of these keys to represent the // above concepts, when necessary (for example, in a pure-JSON output form, it // would be necessary to represent at least message and timestamp as ordinary // named values). // // # Break Glass // // Implementations may choose to give callers access to the underlying // logging implementation. The recommended pattern for this is: // // // Underlier exposes access to the underlying logging implementation. // // Since callers only have a logr.Logger, they have to know which // // implementation is in use, so this interface is less of an abstraction // // and more of way to test type conversion. // type Underlier interface { // GetUnderlying() // } // // Logger grants access to the sink to enable type assertions like this: // // func DoSomethingWithImpl(log logr.Logger) { // if underlier, ok := log.GetSink().(impl.Underlier); ok { // implLogger := underlier.GetUnderlying() // ... // } // } // // Custom `With*` functions can be implemented by copying the complete // Logger struct and replacing the sink in the copy: // // // WithFooBar changes the foobar parameter in the log sink and returns a // // new logger with that modified sink. It does nothing for loggers where // // the sink doesn't support that parameter. // func WithFoobar(log logr.Logger, foobar int) logr.Logger { // if foobarLogSink, ok := log.GetSink().(FoobarSink); ok { // log = log.WithSink(foobarLogSink.WithFooBar(foobar)) // } // return log // } // // Don't use New to construct a new Logger with a LogSink retrieved from an // existing Logger. Source code attribution might not work correctly and // unexported fields in Logger get lost. // // Beware that the same LogSink instance may be shared by different logger // instances. Calling functions that modify the LogSink will affect all of // those. package logr import ( "context" ) // New returns a new Logger instance. This is primarily used by libraries // implementing LogSink, rather than end users. Passing a nil sink will create // a Logger which discards all log lines. func New(sink LogSink) Logger { logger := Logger{} logger.setSink(sink) if sink != nil { sink.Init(runtimeInfo) } return logger } // setSink stores the sink and updates any related fields. It mutates the // logger and thus is only safe to use for loggers that are not currently being // used concurrently. func (l *Logger) setSink(sink LogSink) { l.sink = sink } // GetSink returns the stored sink. func (l Logger) GetSink() LogSink { return l.sink } // WithSink returns a copy of the logger with the new sink. func (l Logger) WithSink(sink LogSink) Logger { l.setSink(sink) return l } // Logger is an interface to an abstract logging implementation. This is a // concrete type for performance reasons, but all the real work is passed on to // a LogSink. Implementations of LogSink should provide their own constructors // that return Logger, not LogSink. // // The underlying sink can be accessed through GetSink and be modified through // WithSink. This enables the implementation of custom extensions (see "Break // Glass" in the package documentation). Normally the sink should be used only // indirectly. type Logger struct { sink LogSink level int } // Enabled tests whether this Logger is enabled. For example, commandline // flags might be used to set the logging verbosity and disable some info logs. func (l Logger) Enabled() bool { return l.sink != nil && l.sink.Enabled(l.level) } // Info logs a non-error message with the given key/value pairs as context. // // The msg argument should be used to add some constant description to the log // line. The key/value pairs can then be used to add additional variable // information. The key/value pairs must alternate string keys and arbitrary // values. func (l Logger) Info(msg string, keysAndValues ...interface{}) { if l.sink == nil { return } if l.Enabled() { if withHelper, ok := l.sink.(CallStackHelperLogSink); ok { withHelper.GetCallStackHelper()() } l.sink.Info(l.level, msg, keysAndValues...) } } // Error logs an error, with the given message and key/value pairs as context. // It functions similarly to Info, but may have unique behavior, and should be // preferred for logging errors (see the package documentations for more // information). The log message will always be emitted, regardless of // verbosity level. // // The msg argument should be used to add context to any underlying error, // while the err argument should be used to attach the actual error that // triggered this log line, if present. The err parameter is optional // and nil may be passed instead of an error instance. func (l Logger) Error(err error, msg string, keysAndValues ...interface{}) { if l.sink == nil { return } if withHelper, ok := l.sink.(CallStackHelperLogSink); ok { withHelper.GetCallStackHelper()() } l.sink.Error(err, msg, keysAndValues...) } // V returns a new Logger instance for a specific verbosity level, relative to // this Logger. In other words, V-levels are additive. A higher verbosity // level means a log message is less important. Negative V-levels are treated // as 0. func (l Logger) V(level int) Logger { if l.sink == nil { return l } if level < 0 { level = 0 } l.level += level return l } // WithValues returns a new Logger instance with additional key/value pairs. // See Info for documentation on how key/value pairs work. func (l Logger) WithValues(keysAndValues ...interface{}) Logger { if l.sink == nil { return l } l.setSink(l.sink.WithValues(keysAndValues...)) return l } // WithName returns a new Logger instance with the specified name element added // to the Logger's name. Successive calls with WithName append additional // suffixes to the Logger's name. It's strongly recommended that name segments // contain only letters, digits, and hyphens (see the package documentation for // more information). func (l Logger) WithName(name string) Logger { if l.sink == nil { return l } l.setSink(l.sink.WithName(name)) return l } // WithCallDepth returns a Logger instance that offsets the call stack by the // specified number of frames when logging call site information, if possible. // This is useful for users who have helper functions between the "real" call // site and the actual calls to Logger methods. If depth is 0 the attribution // should be to the direct caller of this function. If depth is 1 the // attribution should skip 1 call frame, and so on. Successive calls to this // are additive. // // If the underlying log implementation supports a WithCallDepth(int) method, // it will be called and the result returned. If the implementation does not // support CallDepthLogSink, the original Logger will be returned. // // To skip one level, WithCallStackHelper() should be used instead of // WithCallDepth(1) because it works with implementions that support the // CallDepthLogSink and/or CallStackHelperLogSink interfaces. func (l Logger) WithCallDepth(depth int) Logger { if l.sink == nil { return l } if withCallDepth, ok := l.sink.(CallDepthLogSink); ok { l.setSink(withCallDepth.WithCallDepth(depth)) } return l } // WithCallStackHelper returns a new Logger instance that skips the direct // caller when logging call site information, if possible. This is useful for // users who have helper functions between the "real" call site and the actual // calls to Logger methods and want to support loggers which depend on marking // each individual helper function, like loggers based on testing.T. // // In addition to using that new logger instance, callers also must call the // returned function. // // If the underlying log implementation supports a WithCallDepth(int) method, // WithCallDepth(1) will be called to produce a new logger. If it supports a // WithCallStackHelper() method, that will be also called. If the // implementation does not support either of these, the original Logger will be // returned. func (l Logger) WithCallStackHelper() (func(), Logger) { if l.sink == nil { return func() {}, l } var helper func() if withCallDepth, ok := l.sink.(CallDepthLogSink); ok { l.setSink(withCallDepth.WithCallDepth(1)) } if withHelper, ok := l.sink.(CallStackHelperLogSink); ok { helper = withHelper.GetCallStackHelper() } else { helper = func() {} } return helper, l } // IsZero returns true if this logger is an uninitialized zero value func (l Logger) IsZero() bool { return l.sink == nil } // contextKey is how we find Loggers in a context.Context. type contextKey struct{} // FromContext returns a Logger from ctx or an error if no Logger is found. func FromContext(ctx context.Context) (Logger, error) { if v, ok := ctx.Value(contextKey{}).(Logger); ok { return v, nil } return Logger{}, notFoundError{} } // notFoundError exists to carry an IsNotFound method. type notFoundError struct{} func (notFoundError) Error() string { return "no logr.Logger was present" } func (notFoundError) IsNotFound() bool { return true } // FromContextOrDiscard returns a Logger from ctx. If no Logger is found, this // returns a Logger that discards all log messages. func FromContextOrDiscard(ctx context.Context) Logger { if v, ok := ctx.Value(contextKey{}).(Logger); ok { return v } return Discard() } // NewContext returns a new Context, derived from ctx, which carries the // provided Logger. func NewContext(ctx context.Context, logger Logger) context.Context { return context.WithValue(ctx, contextKey{}, logger) } // RuntimeInfo holds information that the logr "core" library knows which // LogSinks might want to know. type RuntimeInfo struct { // CallDepth is the number of call frames the logr library adds between the // end-user and the LogSink. LogSink implementations which choose to print // the original logging site (e.g. file & line) should climb this many // additional frames to find it. CallDepth int } // runtimeInfo is a static global. It must not be changed at run time. var runtimeInfo = RuntimeInfo{ CallDepth: 1, } // LogSink represents a logging implementation. End-users will generally not // interact with this type. type LogSink interface { // Init receives optional information about the logr library for LogSink // implementations that need it. Init(info RuntimeInfo) // Enabled tests whether this LogSink is enabled at the specified V-level. // For example, commandline flags might be used to set the logging // verbosity and disable some info logs. Enabled(level int) bool // Info logs a non-error message with the given key/value pairs as context. // The level argument is provided for optional logging. This method will // only be called when Enabled(level) is true. See Logger.Info for more // details. Info(level int, msg string, keysAndValues ...interface{}) // Error logs an error, with the given message and key/value pairs as // context. See Logger.Error for more details. Error(err error, msg string, keysAndValues ...interface{}) // WithValues returns a new LogSink with additional key/value pairs. See // Logger.WithValues for more details. WithValues(keysAndValues ...interface{}) LogSink // WithName returns a new LogSink with the specified name appended. See // Logger.WithName for more details. WithName(name string) LogSink } // CallDepthLogSink represents a LogSink that knows how to climb the call stack // to identify the original call site and can offset the depth by a specified // number of frames. This is useful for users who have helper functions // between the "real" call site and the actual calls to Logger methods. // Implementations that log information about the call site (such as file, // function, or line) would otherwise log information about the intermediate // helper functions. // // This is an optional interface and implementations are not required to // support it. type CallDepthLogSink interface { // WithCallDepth returns a LogSink that will offset the call // stack by the specified number of frames when logging call // site information. // // If depth is 0, the LogSink should skip exactly the number // of call frames defined in RuntimeInfo.CallDepth when Info // or Error are called, i.e. the attribution should be to the // direct caller of Logger.Info or Logger.Error. // // If depth is 1 the attribution should skip 1 call frame, and so on. // Successive calls to this are additive. WithCallDepth(depth int) LogSink } // CallStackHelperLogSink represents a LogSink that knows how to climb // the call stack to identify the original call site and can skip // intermediate helper functions if they mark themselves as // helper. Go's testing package uses that approach. // // This is useful for users who have helper functions between the // "real" call site and the actual calls to Logger methods. // Implementations that log information about the call site (such as // file, function, or line) would otherwise log information about the // intermediate helper functions. // // This is an optional interface and implementations are not required // to support it. Implementations that choose to support this must not // simply implement it as WithCallDepth(1), because // Logger.WithCallStackHelper will call both methods if they are // present. This should only be implemented for LogSinks that actually // need it, as with testing.T. type CallStackHelperLogSink interface { // GetCallStackHelper returns a function that must be called // to mark the direct caller as helper function when logging // call site information. GetCallStackHelper() func() } // Marshaler is an optional interface that logged values may choose to // implement. Loggers with structured output, such as JSON, should // log the object return by the MarshalLog method instead of the // original value. type Marshaler interface { // MarshalLog can be used to: // - ensure that structs are not logged as strings when the original // value has a String method: return a different type without a // String method // - select which fields of a complex type should get logged: // return a simpler struct with fewer fields // - log unexported fields: return a different struct // with exported fields // // It may return any value of any type. MarshalLog() interface{} } ================================================ FILE: vendor/github.com/go-logr/stdr/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/go-logr/stdr/README.md ================================================ # Minimal Go logging using logr and Go's standard library [![Go Reference](https://pkg.go.dev/badge/github.com/go-logr/stdr.svg)](https://pkg.go.dev/github.com/go-logr/stdr) This package implements the [logr interface](https://github.com/go-logr/logr) in terms of Go's standard log package(https://pkg.go.dev/log). ================================================ FILE: vendor/github.com/go-logr/stdr/stdr.go ================================================ /* Copyright 2019 The logr Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // Package stdr implements github.com/go-logr/logr.Logger in terms of // Go's standard log package. package stdr import ( "log" "os" "github.com/go-logr/logr" "github.com/go-logr/logr/funcr" ) // The global verbosity level. See SetVerbosity(). var globalVerbosity int // SetVerbosity sets the global level against which all info logs will be // compared. If this is greater than or equal to the "V" of the logger, the // message will be logged. A higher value here means more logs will be written. // The previous verbosity value is returned. This is not concurrent-safe - // callers must be sure to call it from only one goroutine. func SetVerbosity(v int) int { old := globalVerbosity globalVerbosity = v return old } // New returns a logr.Logger which is implemented by Go's standard log package, // or something like it. If std is nil, this will use a default logger // instead. // // Example: stdr.New(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile))) func New(std StdLogger) logr.Logger { return NewWithOptions(std, Options{}) } // NewWithOptions returns a logr.Logger which is implemented by Go's standard // log package, or something like it. See New for details. func NewWithOptions(std StdLogger, opts Options) logr.Logger { if std == nil { // Go's log.Default() is only available in 1.16 and higher. std = log.New(os.Stderr, "", log.LstdFlags) } if opts.Depth < 0 { opts.Depth = 0 } fopts := funcr.Options{ LogCaller: funcr.MessageClass(opts.LogCaller), } sl := &logger{ Formatter: funcr.NewFormatter(fopts), std: std, } // For skipping our own logger.Info/Error. sl.Formatter.AddCallDepth(1 + opts.Depth) return logr.New(sl) } // Options carries parameters which influence the way logs are generated. type Options struct { // Depth biases the assumed number of call frames to the "true" caller. // This is useful when the calling code calls a function which then calls // stdr (e.g. a logging shim to another API). Values less than zero will // be treated as zero. Depth int // LogCaller tells stdr to add a "caller" key to some or all log lines. // Go's log package has options to log this natively, too. LogCaller MessageClass // TODO: add an option to log the date/time } // MessageClass indicates which category or categories of messages to consider. type MessageClass int const ( // None ignores all message classes. None MessageClass = iota // All considers all message classes. All // Info only considers info messages. Info // Error only considers error messages. Error ) // StdLogger is the subset of the Go stdlib log.Logger API that is needed for // this adapter. type StdLogger interface { // Output is the same as log.Output and log.Logger.Output. Output(calldepth int, logline string) error } type logger struct { funcr.Formatter std StdLogger } var _ logr.LogSink = &logger{} var _ logr.CallDepthLogSink = &logger{} func (l logger) Enabled(level int) bool { return globalVerbosity >= level } func (l logger) Info(level int, msg string, kvList ...interface{}) { prefix, args := l.FormatInfo(level, msg, kvList) if prefix != "" { args = prefix + ": " + args } _ = l.std.Output(l.Formatter.GetDepth()+1, args) } func (l logger) Error(err error, msg string, kvList ...interface{}) { prefix, args := l.FormatError(err, msg, kvList) if prefix != "" { args = prefix + ": " + args } _ = l.std.Output(l.Formatter.GetDepth()+1, args) } func (l logger) WithName(name string) logr.LogSink { l.Formatter.AddName(name) return &l } func (l logger) WithValues(kvList ...interface{}) logr.LogSink { l.Formatter.AddValues(kvList) return &l } func (l logger) WithCallDepth(depth int) logr.LogSink { l.Formatter.AddCallDepth(depth) return &l } // Underlier exposes access to the underlying logging implementation. Since // callers only have a logr.Logger, they have to know which implementation is // in use, so this interface is less of an abstraction and more of way to test // type conversion. type Underlier interface { GetUnderlying() StdLogger } // GetUnderlying returns the StdLogger underneath this logger. Since StdLogger // is itself an interface, the result may or may not be a Go log.Logger. func (l logger) GetUnderlying() StdLogger { return l.std } ================================================ FILE: vendor/github.com/go-openapi/jsonpointer/.editorconfig ================================================ # top-most EditorConfig file root = true # Unix-style newlines with a newline ending every file [*] end_of_line = lf insert_final_newline = true indent_style = space indent_size = 2 trim_trailing_whitespace = true # Set default charset [*.{js,py,go,scala,rb,java,html,css,less,sass,md}] charset = utf-8 # Tab indentation (no size specified) [*.go] indent_style = tab [*.md] trim_trailing_whitespace = false # Matches the exact files either package.json or .travis.yml [{package.json,.travis.yml}] indent_style = space indent_size = 2 ================================================ FILE: vendor/github.com/go-openapi/jsonpointer/.gitignore ================================================ secrets.yml ================================================ FILE: vendor/github.com/go-openapi/jsonpointer/.golangci.yml ================================================ linters-settings: govet: check-shadowing: true golint: min-confidence: 0 gocyclo: min-complexity: 45 maligned: suggest-new: true dupl: threshold: 200 goconst: min-len: 2 min-occurrences: 3 linters: enable-all: true disable: - maligned - unparam - lll - gochecknoinits - gochecknoglobals - funlen - godox - gocognit - whitespace - wsl - wrapcheck - testpackage - nlreturn - gomnd - exhaustivestruct - goerr113 - errorlint - nestif - godot - gofumpt - paralleltest - tparallel - thelper - ifshort - exhaustruct - varnamelen - gci - depguard - errchkjson - inamedparam - nonamedreturns - musttag - ireturn - forcetypeassert - cyclop # deprecated linters - deadcode - interfacer - scopelint - varcheck - structcheck - golint - nosnakecase ================================================ FILE: vendor/github.com/go-openapi/jsonpointer/CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at ivan+abuse@flanders.co.nz. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ ================================================ FILE: vendor/github.com/go-openapi/jsonpointer/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/go-openapi/jsonpointer/README.md ================================================ # gojsonpointer [![Build Status](https://github.com/go-openapi/jsonpointer/actions/workflows/go-test.yml/badge.svg)](https://github.com/go-openapi/jsonpointer/actions?query=workflow%3A"go+test") [![codecov](https://codecov.io/gh/go-openapi/jsonpointer/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/jsonpointer) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) [![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/jsonpointer/master/LICENSE) [![Go Reference](https://pkg.go.dev/badge/github.com/go-openapi/jsonpointer.svg)](https://pkg.go.dev/github.com/go-openapi/jsonpointer) [![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/jsonpointer)](https://goreportcard.com/report/github.com/go-openapi/jsonpointer) An implementation of JSON Pointer - Go language ## Status Completed YES Tested YES ## References http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07 ### Note The 4.Evaluation part of the previous reference, starting with 'If the currently referenced value is a JSON array, the reference token MUST contain either...' is not implemented. ================================================ FILE: vendor/github.com/go-openapi/jsonpointer/pointer.go ================================================ // Copyright 2013 sigu-399 ( https://github.com/sigu-399 ) // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // author sigu-399 // author-github https://github.com/sigu-399 // author-mail sigu.399@gmail.com // // repository-name jsonpointer // repository-desc An implementation of JSON Pointer - Go language // // description Main and unique file. // // created 25-02-2013 package jsonpointer import ( "encoding/json" "errors" "fmt" "reflect" "strconv" "strings" "github.com/go-openapi/swag" ) const ( emptyPointer = `` pointerSeparator = `/` invalidStart = `JSON pointer must be empty or start with a "` + pointerSeparator notFound = `Can't find the pointer in the document` ) var jsonPointableType = reflect.TypeOf(new(JSONPointable)).Elem() var jsonSetableType = reflect.TypeOf(new(JSONSetable)).Elem() // JSONPointable is an interface for structs to implement when they need to customize the // json pointer process type JSONPointable interface { JSONLookup(string) (any, error) } // JSONSetable is an interface for structs to implement when they need to customize the // json pointer process type JSONSetable interface { JSONSet(string, any) error } // New creates a new json pointer for the given string func New(jsonPointerString string) (Pointer, error) { var p Pointer err := p.parse(jsonPointerString) return p, err } // Pointer the json pointer reprsentation type Pointer struct { referenceTokens []string } // "Constructor", parses the given string JSON pointer func (p *Pointer) parse(jsonPointerString string) error { var err error if jsonPointerString != emptyPointer { if !strings.HasPrefix(jsonPointerString, pointerSeparator) { err = errors.New(invalidStart) } else { referenceTokens := strings.Split(jsonPointerString, pointerSeparator) p.referenceTokens = append(p.referenceTokens, referenceTokens[1:]...) } } return err } // Get uses the pointer to retrieve a value from a JSON document func (p *Pointer) Get(document any) (any, reflect.Kind, error) { return p.get(document, swag.DefaultJSONNameProvider) } // Set uses the pointer to set a value from a JSON document func (p *Pointer) Set(document any, value any) (any, error) { return document, p.set(document, value, swag.DefaultJSONNameProvider) } // GetForToken gets a value for a json pointer token 1 level deep func GetForToken(document any, decodedToken string) (any, reflect.Kind, error) { return getSingleImpl(document, decodedToken, swag.DefaultJSONNameProvider) } // SetForToken gets a value for a json pointer token 1 level deep func SetForToken(document any, decodedToken string, value any) (any, error) { return document, setSingleImpl(document, value, decodedToken, swag.DefaultJSONNameProvider) } func isNil(input any) bool { if input == nil { return true } kind := reflect.TypeOf(input).Kind() switch kind { //nolint:exhaustive case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Chan: return reflect.ValueOf(input).IsNil() default: return false } } func getSingleImpl(node any, decodedToken string, nameProvider *swag.NameProvider) (any, reflect.Kind, error) { rValue := reflect.Indirect(reflect.ValueOf(node)) kind := rValue.Kind() if isNil(node) { return nil, kind, fmt.Errorf("nil value has not field %q", decodedToken) } switch typed := node.(type) { case JSONPointable: r, err := typed.JSONLookup(decodedToken) if err != nil { return nil, kind, err } return r, kind, nil case *any: // case of a pointer to interface, that is not resolved by reflect.Indirect return getSingleImpl(*typed, decodedToken, nameProvider) } switch kind { //nolint:exhaustive case reflect.Struct: nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) if !ok { return nil, kind, fmt.Errorf("object has no field %q", decodedToken) } fld := rValue.FieldByName(nm) return fld.Interface(), kind, nil case reflect.Map: kv := reflect.ValueOf(decodedToken) mv := rValue.MapIndex(kv) if mv.IsValid() { return mv.Interface(), kind, nil } return nil, kind, fmt.Errorf("object has no key %q", decodedToken) case reflect.Slice: tokenIndex, err := strconv.Atoi(decodedToken) if err != nil { return nil, kind, err } sLength := rValue.Len() if tokenIndex < 0 || tokenIndex >= sLength { return nil, kind, fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength-1, tokenIndex) } elem := rValue.Index(tokenIndex) return elem.Interface(), kind, nil default: return nil, kind, fmt.Errorf("invalid token reference %q", decodedToken) } } func setSingleImpl(node, data any, decodedToken string, nameProvider *swag.NameProvider) error { rValue := reflect.Indirect(reflect.ValueOf(node)) if ns, ok := node.(JSONSetable); ok { // pointer impl return ns.JSONSet(decodedToken, data) } if rValue.Type().Implements(jsonSetableType) { return node.(JSONSetable).JSONSet(decodedToken, data) } switch rValue.Kind() { //nolint:exhaustive case reflect.Struct: nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) if !ok { return fmt.Errorf("object has no field %q", decodedToken) } fld := rValue.FieldByName(nm) if fld.IsValid() { fld.Set(reflect.ValueOf(data)) } return nil case reflect.Map: kv := reflect.ValueOf(decodedToken) rValue.SetMapIndex(kv, reflect.ValueOf(data)) return nil case reflect.Slice: tokenIndex, err := strconv.Atoi(decodedToken) if err != nil { return err } sLength := rValue.Len() if tokenIndex < 0 || tokenIndex >= sLength { return fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex) } elem := rValue.Index(tokenIndex) if !elem.CanSet() { return fmt.Errorf("can't set slice index %s to %v", decodedToken, data) } elem.Set(reflect.ValueOf(data)) return nil default: return fmt.Errorf("invalid token reference %q", decodedToken) } } func (p *Pointer) get(node any, nameProvider *swag.NameProvider) (any, reflect.Kind, error) { if nameProvider == nil { nameProvider = swag.DefaultJSONNameProvider } kind := reflect.Invalid // Full document when empty if len(p.referenceTokens) == 0 { return node, kind, nil } for _, token := range p.referenceTokens { decodedToken := Unescape(token) r, knd, err := getSingleImpl(node, decodedToken, nameProvider) if err != nil { return nil, knd, err } node = r } rValue := reflect.ValueOf(node) kind = rValue.Kind() return node, kind, nil } func (p *Pointer) set(node, data any, nameProvider *swag.NameProvider) error { knd := reflect.ValueOf(node).Kind() if knd != reflect.Ptr && knd != reflect.Struct && knd != reflect.Map && knd != reflect.Slice && knd != reflect.Array { return errors.New("only structs, pointers, maps and slices are supported for setting values") } if nameProvider == nil { nameProvider = swag.DefaultJSONNameProvider } // Full document when empty if len(p.referenceTokens) == 0 { return nil } lastI := len(p.referenceTokens) - 1 for i, token := range p.referenceTokens { isLastToken := i == lastI decodedToken := Unescape(token) if isLastToken { return setSingleImpl(node, data, decodedToken, nameProvider) } rValue := reflect.Indirect(reflect.ValueOf(node)) kind := rValue.Kind() if rValue.Type().Implements(jsonPointableType) { r, err := node.(JSONPointable).JSONLookup(decodedToken) if err != nil { return err } fld := reflect.ValueOf(r) if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr { node = fld.Addr().Interface() continue } node = r continue } switch kind { //nolint:exhaustive case reflect.Struct: nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) if !ok { return fmt.Errorf("object has no field %q", decodedToken) } fld := rValue.FieldByName(nm) if fld.CanAddr() && fld.Kind() != reflect.Interface && fld.Kind() != reflect.Map && fld.Kind() != reflect.Slice && fld.Kind() != reflect.Ptr { node = fld.Addr().Interface() continue } node = fld.Interface() case reflect.Map: kv := reflect.ValueOf(decodedToken) mv := rValue.MapIndex(kv) if !mv.IsValid() { return fmt.Errorf("object has no key %q", decodedToken) } if mv.CanAddr() && mv.Kind() != reflect.Interface && mv.Kind() != reflect.Map && mv.Kind() != reflect.Slice && mv.Kind() != reflect.Ptr { node = mv.Addr().Interface() continue } node = mv.Interface() case reflect.Slice: tokenIndex, err := strconv.Atoi(decodedToken) if err != nil { return err } sLength := rValue.Len() if tokenIndex < 0 || tokenIndex >= sLength { return fmt.Errorf("index out of bounds array[0,%d] index '%d'", sLength, tokenIndex) } elem := rValue.Index(tokenIndex) if elem.CanAddr() && elem.Kind() != reflect.Interface && elem.Kind() != reflect.Map && elem.Kind() != reflect.Slice && elem.Kind() != reflect.Ptr { node = elem.Addr().Interface() continue } node = elem.Interface() default: return fmt.Errorf("invalid token reference %q", decodedToken) } } return nil } // DecodedTokens returns the decoded tokens func (p *Pointer) DecodedTokens() []string { result := make([]string, 0, len(p.referenceTokens)) for _, t := range p.referenceTokens { result = append(result, Unescape(t)) } return result } // IsEmpty returns true if this is an empty json pointer // this indicates that it points to the root document func (p *Pointer) IsEmpty() bool { return len(p.referenceTokens) == 0 } // Pointer to string representation function func (p *Pointer) String() string { if len(p.referenceTokens) == 0 { return emptyPointer } pointerString := pointerSeparator + strings.Join(p.referenceTokens, pointerSeparator) return pointerString } func (p *Pointer) Offset(document string) (int64, error) { dec := json.NewDecoder(strings.NewReader(document)) var offset int64 for _, ttk := range p.DecodedTokens() { tk, err := dec.Token() if err != nil { return 0, err } switch tk := tk.(type) { case json.Delim: switch tk { case '{': offset, err = offsetSingleObject(dec, ttk) if err != nil { return 0, err } case '[': offset, err = offsetSingleArray(dec, ttk) if err != nil { return 0, err } default: return 0, fmt.Errorf("invalid token %#v", tk) } default: return 0, fmt.Errorf("invalid token %#v", tk) } } return offset, nil } func offsetSingleObject(dec *json.Decoder, decodedToken string) (int64, error) { for dec.More() { offset := dec.InputOffset() tk, err := dec.Token() if err != nil { return 0, err } switch tk := tk.(type) { case json.Delim: switch tk { case '{': if err = drainSingle(dec); err != nil { return 0, err } case '[': if err = drainSingle(dec); err != nil { return 0, err } } case string: if tk == decodedToken { return offset, nil } default: return 0, fmt.Errorf("invalid token %#v", tk) } } return 0, fmt.Errorf("token reference %q not found", decodedToken) } func offsetSingleArray(dec *json.Decoder, decodedToken string) (int64, error) { idx, err := strconv.Atoi(decodedToken) if err != nil { return 0, fmt.Errorf("token reference %q is not a number: %v", decodedToken, err) } var i int for i = 0; i < idx && dec.More(); i++ { tk, err := dec.Token() if err != nil { return 0, err } if delim, isDelim := tk.(json.Delim); isDelim { switch delim { case '{': if err = drainSingle(dec); err != nil { return 0, err } case '[': if err = drainSingle(dec); err != nil { return 0, err } } } } if !dec.More() { return 0, fmt.Errorf("token reference %q not found", decodedToken) } return dec.InputOffset(), nil } // drainSingle drains a single level of object or array. // The decoder has to guarantee the beginning delim (i.e. '{' or '[') has been consumed. func drainSingle(dec *json.Decoder) error { for dec.More() { tk, err := dec.Token() if err != nil { return err } if delim, isDelim := tk.(json.Delim); isDelim { switch delim { case '{': if err = drainSingle(dec); err != nil { return err } case '[': if err = drainSingle(dec); err != nil { return err } } } } // Consumes the ending delim if _, err := dec.Token(); err != nil { return err } return nil } // Specific JSON pointer encoding here // ~0 => ~ // ~1 => / // ... and vice versa const ( encRefTok0 = `~0` encRefTok1 = `~1` decRefTok0 = `~` decRefTok1 = `/` ) // Unescape unescapes a json pointer reference token string to the original representation func Unescape(token string) string { step1 := strings.ReplaceAll(token, encRefTok1, decRefTok1) step2 := strings.ReplaceAll(step1, encRefTok0, decRefTok0) return step2 } // Escape escapes a pointer reference token string func Escape(token string) string { step1 := strings.ReplaceAll(token, decRefTok0, encRefTok0) step2 := strings.ReplaceAll(step1, decRefTok1, encRefTok1) return step2 } ================================================ FILE: vendor/github.com/go-openapi/jsonreference/.gitignore ================================================ secrets.yml ================================================ FILE: vendor/github.com/go-openapi/jsonreference/.golangci.yml ================================================ linters-settings: govet: check-shadowing: true gocyclo: min-complexity: 30 maligned: suggest-new: true dupl: threshold: 100 goconst: min-len: 2 min-occurrences: 4 paralleltest: ignore-missing: true linters: enable-all: true disable: - maligned - lll - gochecknoglobals - godox - gocognit - whitespace - wsl - funlen - gochecknoglobals - gochecknoinits - scopelint - wrapcheck - exhaustivestruct - exhaustive - nlreturn - testpackage - gci - gofumpt - goerr113 - gomnd - tparallel - nestif - godot - errorlint - varcheck - interfacer - deadcode - golint - ifshort - structcheck - nosnakecase - varnamelen - exhaustruct ================================================ FILE: vendor/github.com/go-openapi/jsonreference/CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at ivan+abuse@flanders.co.nz. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ ================================================ FILE: vendor/github.com/go-openapi/jsonreference/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/go-openapi/jsonreference/README.md ================================================ # gojsonreference [![Build Status](https://travis-ci.org/go-openapi/jsonreference.svg?branch=master)](https://travis-ci.org/go-openapi/jsonreference) [![codecov](https://codecov.io/gh/go-openapi/jsonreference/branch/master/graph/badge.svg)](https://codecov.io/gh/go-openapi/jsonreference) [![Slack Status](https://slackin.goswagger.io/badge.svg)](https://slackin.goswagger.io) [![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/jsonreference/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/jsonreference?status.svg)](http://godoc.org/github.com/go-openapi/jsonreference) An implementation of JSON Reference - Go language ## Status Feature complete. Stable API ## Dependencies https://github.com/go-openapi/jsonpointer ## References http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07 http://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03 ================================================ FILE: vendor/github.com/go-openapi/jsonreference/internal/normalize_url.go ================================================ package internal import ( "net/url" "regexp" "strings" ) const ( defaultHTTPPort = ":80" defaultHTTPSPort = ":443" ) // Regular expressions used by the normalizations var rxPort = regexp.MustCompile(`(:\d+)/?$`) var rxDupSlashes = regexp.MustCompile(`/{2,}`) // NormalizeURL will normalize the specified URL // This was added to replace a previous call to the no longer maintained purell library: // The call that was used looked like the following: // // url.Parse(purell.NormalizeURL(parsed, purell.FlagsSafe|purell.FlagRemoveDuplicateSlashes)) // // To explain all that was included in the call above, purell.FlagsSafe was really just the following: // - FlagLowercaseScheme // - FlagLowercaseHost // - FlagRemoveDefaultPort // - FlagRemoveDuplicateSlashes (and this was mixed in with the |) func NormalizeURL(u *url.URL) { lowercaseScheme(u) lowercaseHost(u) removeDefaultPort(u) removeDuplicateSlashes(u) } func lowercaseScheme(u *url.URL) { if len(u.Scheme) > 0 { u.Scheme = strings.ToLower(u.Scheme) } } func lowercaseHost(u *url.URL) { if len(u.Host) > 0 { u.Host = strings.ToLower(u.Host) } } func removeDefaultPort(u *url.URL) { if len(u.Host) > 0 { scheme := strings.ToLower(u.Scheme) u.Host = rxPort.ReplaceAllStringFunc(u.Host, func(val string) string { if (scheme == "http" && val == defaultHTTPPort) || (scheme == "https" && val == defaultHTTPSPort) { return "" } return val }) } } func removeDuplicateSlashes(u *url.URL) { if len(u.Path) > 0 { u.Path = rxDupSlashes.ReplaceAllString(u.Path, "/") } } ================================================ FILE: vendor/github.com/go-openapi/jsonreference/reference.go ================================================ // Copyright 2013 sigu-399 ( https://github.com/sigu-399 ) // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // author sigu-399 // author-github https://github.com/sigu-399 // author-mail sigu.399@gmail.com // // repository-name jsonreference // repository-desc An implementation of JSON Reference - Go language // // description Main and unique file. // // created 26-02-2013 package jsonreference import ( "errors" "net/url" "strings" "github.com/go-openapi/jsonpointer" "github.com/go-openapi/jsonreference/internal" ) const ( fragmentRune = `#` ) // New creates a new reference for the given string func New(jsonReferenceString string) (Ref, error) { var r Ref err := r.parse(jsonReferenceString) return r, err } // MustCreateRef parses the ref string and panics when it's invalid. // Use the New method for a version that returns an error func MustCreateRef(ref string) Ref { r, err := New(ref) if err != nil { panic(err) } return r } // Ref represents a json reference object type Ref struct { referenceURL *url.URL referencePointer jsonpointer.Pointer HasFullURL bool HasURLPathOnly bool HasFragmentOnly bool HasFileScheme bool HasFullFilePath bool } // GetURL gets the URL for this reference func (r *Ref) GetURL() *url.URL { return r.referenceURL } // GetPointer gets the json pointer for this reference func (r *Ref) GetPointer() *jsonpointer.Pointer { return &r.referencePointer } // String returns the best version of the url for this reference func (r *Ref) String() string { if r.referenceURL != nil { return r.referenceURL.String() } if r.HasFragmentOnly { return fragmentRune + r.referencePointer.String() } return r.referencePointer.String() } // IsRoot returns true if this reference is a root document func (r *Ref) IsRoot() bool { return r.referenceURL != nil && !r.IsCanonical() && !r.HasURLPathOnly && r.referenceURL.Fragment == "" } // IsCanonical returns true when this pointer starts with http(s):// or file:// func (r *Ref) IsCanonical() bool { return (r.HasFileScheme && r.HasFullFilePath) || (!r.HasFileScheme && r.HasFullURL) } // "Constructor", parses the given string JSON reference func (r *Ref) parse(jsonReferenceString string) error { parsed, err := url.Parse(jsonReferenceString) if err != nil { return err } internal.NormalizeURL(parsed) r.referenceURL = parsed refURL := r.referenceURL if refURL.Scheme != "" && refURL.Host != "" { r.HasFullURL = true } else { if refURL.Path != "" { r.HasURLPathOnly = true } else if refURL.RawQuery == "" && refURL.Fragment != "" { r.HasFragmentOnly = true } } r.HasFileScheme = refURL.Scheme == "file" r.HasFullFilePath = strings.HasPrefix(refURL.Path, "/") // invalid json-pointer error means url has no json-pointer fragment. simply ignore error r.referencePointer, _ = jsonpointer.New(refURL.Fragment) return nil } // Inherits creates a new reference from a parent and a child // If the child cannot inherit from the parent, an error is returned func (r *Ref) Inherits(child Ref) (*Ref, error) { childURL := child.GetURL() parentURL := r.GetURL() if childURL == nil { return nil, errors.New("child url is nil") } if parentURL == nil { return &child, nil } ref, err := New(parentURL.ResolveReference(childURL).String()) if err != nil { return nil, err } return &ref, nil } ================================================ FILE: vendor/github.com/gobwas/glob/.gitignore ================================================ glob.iml .idea *.cpu *.mem *.test *.dot *.png *.svg ================================================ FILE: vendor/github.com/gobwas/glob/.travis.yml ================================================ sudo: false language: go go: - 1.5.3 script: - go test -v ./... ================================================ FILE: vendor/github.com/gobwas/glob/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2016 Sergey Kamardin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/gobwas/glob/bench.sh ================================================ #! /bin/bash bench() { filename="/tmp/$1-$2.bench" if test -e "${filename}"; then echo "Already exists ${filename}" else backup=`git rev-parse --abbrev-ref HEAD` git checkout $1 echo -n "Creating ${filename}... " go test ./... -run=NONE -bench=$2 > "${filename}" -benchmem echo "OK" git checkout ${backup} sleep 5 fi } to=$1 current=`git rev-parse --abbrev-ref HEAD` bench ${to} $2 bench ${current} $2 benchcmp $3 "/tmp/${to}-$2.bench" "/tmp/${current}-$2.bench" ================================================ FILE: vendor/github.com/gobwas/glob/compiler/compiler.go ================================================ package compiler // TODO use constructor with all matchers, and to their structs private // TODO glue multiple Text nodes (like after QuoteMeta) import ( "fmt" "reflect" "github.com/gobwas/glob/match" "github.com/gobwas/glob/syntax/ast" "github.com/gobwas/glob/util/runes" ) func optimizeMatcher(matcher match.Matcher) match.Matcher { switch m := matcher.(type) { case match.Any: if len(m.Separators) == 0 { return match.NewSuper() } case match.AnyOf: if len(m.Matchers) == 1 { return m.Matchers[0] } return m case match.List: if m.Not == false && len(m.List) == 1 { return match.NewText(string(m.List)) } return m case match.BTree: m.Left = optimizeMatcher(m.Left) m.Right = optimizeMatcher(m.Right) r, ok := m.Value.(match.Text) if !ok { return m } var ( leftNil = m.Left == nil rightNil = m.Right == nil ) if leftNil && rightNil { return match.NewText(r.Str) } _, leftSuper := m.Left.(match.Super) lp, leftPrefix := m.Left.(match.Prefix) la, leftAny := m.Left.(match.Any) _, rightSuper := m.Right.(match.Super) rs, rightSuffix := m.Right.(match.Suffix) ra, rightAny := m.Right.(match.Any) switch { case leftSuper && rightSuper: return match.NewContains(r.Str, false) case leftSuper && rightNil: return match.NewSuffix(r.Str) case rightSuper && leftNil: return match.NewPrefix(r.Str) case leftNil && rightSuffix: return match.NewPrefixSuffix(r.Str, rs.Suffix) case rightNil && leftPrefix: return match.NewPrefixSuffix(lp.Prefix, r.Str) case rightNil && leftAny: return match.NewSuffixAny(r.Str, la.Separators) case leftNil && rightAny: return match.NewPrefixAny(r.Str, ra.Separators) } return m } return matcher } func compileMatchers(matchers []match.Matcher) (match.Matcher, error) { if len(matchers) == 0 { return nil, fmt.Errorf("compile error: need at least one matcher") } if len(matchers) == 1 { return matchers[0], nil } if m := glueMatchers(matchers); m != nil { return m, nil } idx := -1 maxLen := -1 var val match.Matcher for i, matcher := range matchers { if l := matcher.Len(); l != -1 && l >= maxLen { maxLen = l idx = i val = matcher } } if val == nil { // not found matcher with static length r, err := compileMatchers(matchers[1:]) if err != nil { return nil, err } return match.NewBTree(matchers[0], nil, r), nil } left := matchers[:idx] var right []match.Matcher if len(matchers) > idx+1 { right = matchers[idx+1:] } var l, r match.Matcher var err error if len(left) > 0 { l, err = compileMatchers(left) if err != nil { return nil, err } } if len(right) > 0 { r, err = compileMatchers(right) if err != nil { return nil, err } } return match.NewBTree(val, l, r), nil } func glueMatchers(matchers []match.Matcher) match.Matcher { if m := glueMatchersAsEvery(matchers); m != nil { return m } if m := glueMatchersAsRow(matchers); m != nil { return m } return nil } func glueMatchersAsRow(matchers []match.Matcher) match.Matcher { if len(matchers) <= 1 { return nil } var ( c []match.Matcher l int ) for _, matcher := range matchers { if ml := matcher.Len(); ml == -1 { return nil } else { c = append(c, matcher) l += ml } } return match.NewRow(l, c...) } func glueMatchersAsEvery(matchers []match.Matcher) match.Matcher { if len(matchers) <= 1 { return nil } var ( hasAny bool hasSuper bool hasSingle bool min int separator []rune ) for i, matcher := range matchers { var sep []rune switch m := matcher.(type) { case match.Super: sep = []rune{} hasSuper = true case match.Any: sep = m.Separators hasAny = true case match.Single: sep = m.Separators hasSingle = true min++ case match.List: if !m.Not { return nil } sep = m.List hasSingle = true min++ default: return nil } // initialize if i == 0 { separator = sep } if runes.Equal(sep, separator) { continue } return nil } if hasSuper && !hasAny && !hasSingle { return match.NewSuper() } if hasAny && !hasSuper && !hasSingle { return match.NewAny(separator) } if (hasAny || hasSuper) && min > 0 && len(separator) == 0 { return match.NewMin(min) } every := match.NewEveryOf() if min > 0 { every.Add(match.NewMin(min)) if !hasAny && !hasSuper { every.Add(match.NewMax(min)) } } if len(separator) > 0 { every.Add(match.NewContains(string(separator), true)) } return every } func minimizeMatchers(matchers []match.Matcher) []match.Matcher { var done match.Matcher var left, right, count int for l := 0; l < len(matchers); l++ { for r := len(matchers); r > l; r-- { if glued := glueMatchers(matchers[l:r]); glued != nil { var swap bool if done == nil { swap = true } else { cl, gl := done.Len(), glued.Len() swap = cl > -1 && gl > -1 && gl > cl swap = swap || count < r-l } if swap { done = glued left = l right = r count = r - l } } } } if done == nil { return matchers } next := append(append([]match.Matcher{}, matchers[:left]...), done) if right < len(matchers) { next = append(next, matchers[right:]...) } if len(next) == len(matchers) { return next } return minimizeMatchers(next) } // minimizeAnyOf tries to apply some heuristics to minimize number of nodes in given tree func minimizeTree(tree *ast.Node) *ast.Node { switch tree.Kind { case ast.KindAnyOf: return minimizeTreeAnyOf(tree) default: return nil } } // minimizeAnyOf tries to find common children of given node of AnyOf pattern // it searches for common children from left and from right // if any common children are found – then it returns new optimized ast tree // else it returns nil func minimizeTreeAnyOf(tree *ast.Node) *ast.Node { if !areOfSameKind(tree.Children, ast.KindPattern) { return nil } commonLeft, commonRight := commonChildren(tree.Children) commonLeftCount, commonRightCount := len(commonLeft), len(commonRight) if commonLeftCount == 0 && commonRightCount == 0 { // there are no common parts return nil } var result []*ast.Node if commonLeftCount > 0 { result = append(result, ast.NewNode(ast.KindPattern, nil, commonLeft...)) } var anyOf []*ast.Node for _, child := range tree.Children { reuse := child.Children[commonLeftCount : len(child.Children)-commonRightCount] var node *ast.Node if len(reuse) == 0 { // this pattern is completely reduced by commonLeft and commonRight patterns // so it become nothing node = ast.NewNode(ast.KindNothing, nil) } else { node = ast.NewNode(ast.KindPattern, nil, reuse...) } anyOf = appendIfUnique(anyOf, node) } switch { case len(anyOf) == 1 && anyOf[0].Kind != ast.KindNothing: result = append(result, anyOf[0]) case len(anyOf) > 1: result = append(result, ast.NewNode(ast.KindAnyOf, nil, anyOf...)) } if commonRightCount > 0 { result = append(result, ast.NewNode(ast.KindPattern, nil, commonRight...)) } return ast.NewNode(ast.KindPattern, nil, result...) } func commonChildren(nodes []*ast.Node) (commonLeft, commonRight []*ast.Node) { if len(nodes) <= 1 { return } // find node that has least number of children idx := leastChildren(nodes) if idx == -1 { return } tree := nodes[idx] treeLength := len(tree.Children) // allocate max able size for rightCommon slice // to get ability insert elements in reverse order (from end to start) // without sorting commonRight = make([]*ast.Node, treeLength) lastRight := treeLength // will use this to get results as commonRight[lastRight:] var ( breakLeft bool breakRight bool commonTotal int ) for i, j := 0, treeLength-1; commonTotal < treeLength && j >= 0 && !(breakLeft && breakRight); i, j = i+1, j-1 { treeLeft := tree.Children[i] treeRight := tree.Children[j] for k := 0; k < len(nodes) && !(breakLeft && breakRight); k++ { // skip least children node if k == idx { continue } restLeft := nodes[k].Children[i] restRight := nodes[k].Children[j+len(nodes[k].Children)-treeLength] breakLeft = breakLeft || !treeLeft.Equal(restLeft) // disable searching for right common parts, if left part is already overlapping breakRight = breakRight || (!breakLeft && j <= i) breakRight = breakRight || !treeRight.Equal(restRight) } if !breakLeft { commonTotal++ commonLeft = append(commonLeft, treeLeft) } if !breakRight { commonTotal++ lastRight = j commonRight[j] = treeRight } } commonRight = commonRight[lastRight:] return } func appendIfUnique(target []*ast.Node, val *ast.Node) []*ast.Node { for _, n := range target { if reflect.DeepEqual(n, val) { return target } } return append(target, val) } func areOfSameKind(nodes []*ast.Node, kind ast.Kind) bool { for _, n := range nodes { if n.Kind != kind { return false } } return true } func leastChildren(nodes []*ast.Node) int { min := -1 idx := -1 for i, n := range nodes { if idx == -1 || (len(n.Children) < min) { min = len(n.Children) idx = i } } return idx } func compileTreeChildren(tree *ast.Node, sep []rune) ([]match.Matcher, error) { var matchers []match.Matcher for _, desc := range tree.Children { m, err := compile(desc, sep) if err != nil { return nil, err } matchers = append(matchers, optimizeMatcher(m)) } return matchers, nil } func compile(tree *ast.Node, sep []rune) (m match.Matcher, err error) { switch tree.Kind { case ast.KindAnyOf: // todo this could be faster on pattern_alternatives_combine_lite (see glob_test.go) if n := minimizeTree(tree); n != nil { return compile(n, sep) } matchers, err := compileTreeChildren(tree, sep) if err != nil { return nil, err } return match.NewAnyOf(matchers...), nil case ast.KindPattern: if len(tree.Children) == 0 { return match.NewNothing(), nil } matchers, err := compileTreeChildren(tree, sep) if err != nil { return nil, err } m, err = compileMatchers(minimizeMatchers(matchers)) if err != nil { return nil, err } case ast.KindAny: m = match.NewAny(sep) case ast.KindSuper: m = match.NewSuper() case ast.KindSingle: m = match.NewSingle(sep) case ast.KindNothing: m = match.NewNothing() case ast.KindList: l := tree.Value.(ast.List) m = match.NewList([]rune(l.Chars), l.Not) case ast.KindRange: r := tree.Value.(ast.Range) m = match.NewRange(r.Lo, r.Hi, r.Not) case ast.KindText: t := tree.Value.(ast.Text) m = match.NewText(t.Text) default: return nil, fmt.Errorf("could not compile tree: unknown node type") } return optimizeMatcher(m), nil } func Compile(tree *ast.Node, sep []rune) (match.Matcher, error) { m, err := compile(tree, sep) if err != nil { return nil, err } return m, nil } ================================================ FILE: vendor/github.com/gobwas/glob/glob.go ================================================ package glob import ( "github.com/gobwas/glob/compiler" "github.com/gobwas/glob/syntax" ) // Glob represents compiled glob pattern. type Glob interface { Match(string) bool } // Compile creates Glob for given pattern and strings (if any present after pattern) as separators. // The pattern syntax is: // // pattern: // { term } // // term: // `*` matches any sequence of non-separator characters // `**` matches any sequence of characters // `?` matches any single non-separator character // `[` [ `!` ] { character-range } `]` // character class (must be non-empty) // `{` pattern-list `}` // pattern alternatives // c matches character c (c != `*`, `**`, `?`, `\`, `[`, `{`, `}`) // `\` c matches character c // // character-range: // c matches character c (c != `\\`, `-`, `]`) // `\` c matches character c // lo `-` hi matches character c for lo <= c <= hi // // pattern-list: // pattern { `,` pattern } // comma-separated (without spaces) patterns // func Compile(pattern string, separators ...rune) (Glob, error) { ast, err := syntax.Parse(pattern) if err != nil { return nil, err } matcher, err := compiler.Compile(ast, separators) if err != nil { return nil, err } return matcher, nil } // MustCompile is the same as Compile, except that if Compile returns error, this will panic func MustCompile(pattern string, separators ...rune) Glob { g, err := Compile(pattern, separators...) if err != nil { panic(err) } return g } // QuoteMeta returns a string that quotes all glob pattern meta characters // inside the argument text; For example, QuoteMeta(`{foo*}`) returns `\[foo\*\]`. func QuoteMeta(s string) string { b := make([]byte, 2*len(s)) // a byte loop is correct because all meta characters are ASCII j := 0 for i := 0; i < len(s); i++ { if syntax.Special(s[i]) { b[j] = '\\' j++ } b[j] = s[i] j++ } return string(b[0:j]) } ================================================ FILE: vendor/github.com/gobwas/glob/match/any.go ================================================ package match import ( "fmt" "github.com/gobwas/glob/util/strings" ) type Any struct { Separators []rune } func NewAny(s []rune) Any { return Any{s} } func (self Any) Match(s string) bool { return strings.IndexAnyRunes(s, self.Separators) == -1 } func (self Any) Index(s string) (int, []int) { found := strings.IndexAnyRunes(s, self.Separators) switch found { case -1: case 0: return 0, segments0 default: s = s[:found] } segments := acquireSegments(len(s)) for i := range s { segments = append(segments, i) } segments = append(segments, len(s)) return 0, segments } func (self Any) Len() int { return lenNo } func (self Any) String() string { return fmt.Sprintf("", string(self.Separators)) } ================================================ FILE: vendor/github.com/gobwas/glob/match/any_of.go ================================================ package match import "fmt" type AnyOf struct { Matchers Matchers } func NewAnyOf(m ...Matcher) AnyOf { return AnyOf{Matchers(m)} } func (self *AnyOf) Add(m Matcher) error { self.Matchers = append(self.Matchers, m) return nil } func (self AnyOf) Match(s string) bool { for _, m := range self.Matchers { if m.Match(s) { return true } } return false } func (self AnyOf) Index(s string) (int, []int) { index := -1 segments := acquireSegments(len(s)) for _, m := range self.Matchers { idx, seg := m.Index(s) if idx == -1 { continue } if index == -1 || idx < index { index = idx segments = append(segments[:0], seg...) continue } if idx > index { continue } // here idx == index segments = appendMerge(segments, seg) } if index == -1 { releaseSegments(segments) return -1, nil } return index, segments } func (self AnyOf) Len() (l int) { l = -1 for _, m := range self.Matchers { ml := m.Len() switch { case l == -1: l = ml continue case ml == -1: return -1 case l != ml: return -1 } } return } func (self AnyOf) String() string { return fmt.Sprintf("", self.Matchers) } ================================================ FILE: vendor/github.com/gobwas/glob/match/btree.go ================================================ package match import ( "fmt" "unicode/utf8" ) type BTree struct { Value Matcher Left Matcher Right Matcher ValueLengthRunes int LeftLengthRunes int RightLengthRunes int LengthRunes int } func NewBTree(Value, Left, Right Matcher) (tree BTree) { tree.Value = Value tree.Left = Left tree.Right = Right lenOk := true if tree.ValueLengthRunes = Value.Len(); tree.ValueLengthRunes == -1 { lenOk = false } if Left != nil { if tree.LeftLengthRunes = Left.Len(); tree.LeftLengthRunes == -1 { lenOk = false } } if Right != nil { if tree.RightLengthRunes = Right.Len(); tree.RightLengthRunes == -1 { lenOk = false } } if lenOk { tree.LengthRunes = tree.LeftLengthRunes + tree.ValueLengthRunes + tree.RightLengthRunes } else { tree.LengthRunes = -1 } return tree } func (self BTree) Len() int { return self.LengthRunes } // todo? func (self BTree) Index(s string) (int, []int) { return -1, nil } func (self BTree) Match(s string) bool { inputLen := len(s) // self.Length, self.RLen and self.LLen are values meaning the length of runes for each part // here we manipulating byte length for better optimizations // but these checks still works, cause minLen of 1-rune string is 1 byte. if self.LengthRunes != -1 && self.LengthRunes > inputLen { return false } // try to cut unnecessary parts // by knowledge of length of right and left part var offset, limit int if self.LeftLengthRunes >= 0 { offset = self.LeftLengthRunes } if self.RightLengthRunes >= 0 { limit = inputLen - self.RightLengthRunes } else { limit = inputLen } for offset < limit { // search for matching part in substring index, segments := self.Value.Index(s[offset:limit]) if index == -1 { releaseSegments(segments) return false } l := s[:offset+index] var left bool if self.Left != nil { left = self.Left.Match(l) } else { left = l == "" } if left { for i := len(segments) - 1; i >= 0; i-- { length := segments[i] var right bool var r string // if there is no string for the right branch if inputLen <= offset+index+length { r = "" } else { r = s[offset+index+length:] } if self.Right != nil { right = self.Right.Match(r) } else { right = r == "" } if right { releaseSegments(segments) return true } } } _, step := utf8.DecodeRuneInString(s[offset+index:]) offset += index + step releaseSegments(segments) } return false } func (self BTree) String() string { const n string = "" var l, r string if self.Left == nil { l = n } else { l = self.Left.String() } if self.Right == nil { r = n } else { r = self.Right.String() } return fmt.Sprintf("%s]>", l, self.Value, r) } ================================================ FILE: vendor/github.com/gobwas/glob/match/contains.go ================================================ package match import ( "fmt" "strings" ) type Contains struct { Needle string Not bool } func NewContains(needle string, not bool) Contains { return Contains{needle, not} } func (self Contains) Match(s string) bool { return strings.Contains(s, self.Needle) != self.Not } func (self Contains) Index(s string) (int, []int) { var offset int idx := strings.Index(s, self.Needle) if !self.Not { if idx == -1 { return -1, nil } offset = idx + len(self.Needle) if len(s) <= offset { return 0, []int{offset} } s = s[offset:] } else if idx != -1 { s = s[:idx] } segments := acquireSegments(len(s) + 1) for i := range s { segments = append(segments, offset+i) } return 0, append(segments, offset+len(s)) } func (self Contains) Len() int { return lenNo } func (self Contains) String() string { var not string if self.Not { not = "!" } return fmt.Sprintf("", not, self.Needle) } ================================================ FILE: vendor/github.com/gobwas/glob/match/every_of.go ================================================ package match import ( "fmt" ) type EveryOf struct { Matchers Matchers } func NewEveryOf(m ...Matcher) EveryOf { return EveryOf{Matchers(m)} } func (self *EveryOf) Add(m Matcher) error { self.Matchers = append(self.Matchers, m) return nil } func (self EveryOf) Len() (l int) { for _, m := range self.Matchers { if ml := m.Len(); l > 0 { l += ml } else { return -1 } } return } func (self EveryOf) Index(s string) (int, []int) { var index int var offset int // make `in` with cap as len(s), // cause it is the maximum size of output segments values next := acquireSegments(len(s)) current := acquireSegments(len(s)) sub := s for i, m := range self.Matchers { idx, seg := m.Index(sub) if idx == -1 { releaseSegments(next) releaseSegments(current) return -1, nil } if i == 0 { // we use copy here instead of `current = seg` // cause seg is a slice from reusable buffer `in` // and it could be overwritten in next iteration current = append(current, seg...) } else { // clear the next next = next[:0] delta := index - (idx + offset) for _, ex := range current { for _, n := range seg { if ex+delta == n { next = append(next, n) } } } if len(next) == 0 { releaseSegments(next) releaseSegments(current) return -1, nil } current = append(current[:0], next...) } index = idx + offset sub = s[index:] offset += idx } releaseSegments(next) return index, current } func (self EveryOf) Match(s string) bool { for _, m := range self.Matchers { if !m.Match(s) { return false } } return true } func (self EveryOf) String() string { return fmt.Sprintf("", self.Matchers) } ================================================ FILE: vendor/github.com/gobwas/glob/match/list.go ================================================ package match import ( "fmt" "github.com/gobwas/glob/util/runes" "unicode/utf8" ) type List struct { List []rune Not bool } func NewList(list []rune, not bool) List { return List{list, not} } func (self List) Match(s string) bool { r, w := utf8.DecodeRuneInString(s) if len(s) > w { return false } inList := runes.IndexRune(self.List, r) != -1 return inList == !self.Not } func (self List) Len() int { return lenOne } func (self List) Index(s string) (int, []int) { for i, r := range s { if self.Not == (runes.IndexRune(self.List, r) == -1) { return i, segmentsByRuneLength[utf8.RuneLen(r)] } } return -1, nil } func (self List) String() string { var not string if self.Not { not = "!" } return fmt.Sprintf("", not, string(self.List)) } ================================================ FILE: vendor/github.com/gobwas/glob/match/match.go ================================================ package match // todo common table of rune's length import ( "fmt" "strings" ) const lenOne = 1 const lenZero = 0 const lenNo = -1 type Matcher interface { Match(string) bool Index(string) (int, []int) Len() int String() string } type Matchers []Matcher func (m Matchers) String() string { var s []string for _, matcher := range m { s = append(s, fmt.Sprint(matcher)) } return fmt.Sprintf("%s", strings.Join(s, ",")) } // appendMerge merges and sorts given already SORTED and UNIQUE segments. func appendMerge(target, sub []int) []int { lt, ls := len(target), len(sub) out := make([]int, 0, lt+ls) for x, y := 0, 0; x < lt || y < ls; { if x >= lt { out = append(out, sub[y:]...) break } if y >= ls { out = append(out, target[x:]...) break } xValue := target[x] yValue := sub[y] switch { case xValue == yValue: out = append(out, xValue) x++ y++ case xValue < yValue: out = append(out, xValue) x++ case yValue < xValue: out = append(out, yValue) y++ } } target = append(target[:0], out...) return target } func reverseSegments(input []int) { l := len(input) m := l / 2 for i := 0; i < m; i++ { input[i], input[l-i-1] = input[l-i-1], input[i] } } ================================================ FILE: vendor/github.com/gobwas/glob/match/max.go ================================================ package match import ( "fmt" "unicode/utf8" ) type Max struct { Limit int } func NewMax(l int) Max { return Max{l} } func (self Max) Match(s string) bool { var l int for range s { l += 1 if l > self.Limit { return false } } return true } func (self Max) Index(s string) (int, []int) { segments := acquireSegments(self.Limit + 1) segments = append(segments, 0) var count int for i, r := range s { count++ if count > self.Limit { break } segments = append(segments, i+utf8.RuneLen(r)) } return 0, segments } func (self Max) Len() int { return lenNo } func (self Max) String() string { return fmt.Sprintf("", self.Limit) } ================================================ FILE: vendor/github.com/gobwas/glob/match/min.go ================================================ package match import ( "fmt" "unicode/utf8" ) type Min struct { Limit int } func NewMin(l int) Min { return Min{l} } func (self Min) Match(s string) bool { var l int for range s { l += 1 if l >= self.Limit { return true } } return false } func (self Min) Index(s string) (int, []int) { var count int c := len(s) - self.Limit + 1 if c <= 0 { return -1, nil } segments := acquireSegments(c) for i, r := range s { count++ if count >= self.Limit { segments = append(segments, i+utf8.RuneLen(r)) } } if len(segments) == 0 { return -1, nil } return 0, segments } func (self Min) Len() int { return lenNo } func (self Min) String() string { return fmt.Sprintf("", self.Limit) } ================================================ FILE: vendor/github.com/gobwas/glob/match/nothing.go ================================================ package match import ( "fmt" ) type Nothing struct{} func NewNothing() Nothing { return Nothing{} } func (self Nothing) Match(s string) bool { return len(s) == 0 } func (self Nothing) Index(s string) (int, []int) { return 0, segments0 } func (self Nothing) Len() int { return lenZero } func (self Nothing) String() string { return fmt.Sprintf("") } ================================================ FILE: vendor/github.com/gobwas/glob/match/prefix.go ================================================ package match import ( "fmt" "strings" "unicode/utf8" ) type Prefix struct { Prefix string } func NewPrefix(p string) Prefix { return Prefix{p} } func (self Prefix) Index(s string) (int, []int) { idx := strings.Index(s, self.Prefix) if idx == -1 { return -1, nil } length := len(self.Prefix) var sub string if len(s) > idx+length { sub = s[idx+length:] } else { sub = "" } segments := acquireSegments(len(sub) + 1) segments = append(segments, length) for i, r := range sub { segments = append(segments, length+i+utf8.RuneLen(r)) } return idx, segments } func (self Prefix) Len() int { return lenNo } func (self Prefix) Match(s string) bool { return strings.HasPrefix(s, self.Prefix) } func (self Prefix) String() string { return fmt.Sprintf("", self.Prefix) } ================================================ FILE: vendor/github.com/gobwas/glob/match/prefix_any.go ================================================ package match import ( "fmt" "strings" "unicode/utf8" sutil "github.com/gobwas/glob/util/strings" ) type PrefixAny struct { Prefix string Separators []rune } func NewPrefixAny(s string, sep []rune) PrefixAny { return PrefixAny{s, sep} } func (self PrefixAny) Index(s string) (int, []int) { idx := strings.Index(s, self.Prefix) if idx == -1 { return -1, nil } n := len(self.Prefix) sub := s[idx+n:] i := sutil.IndexAnyRunes(sub, self.Separators) if i > -1 { sub = sub[:i] } seg := acquireSegments(len(sub) + 1) seg = append(seg, n) for i, r := range sub { seg = append(seg, n+i+utf8.RuneLen(r)) } return idx, seg } func (self PrefixAny) Len() int { return lenNo } func (self PrefixAny) Match(s string) bool { if !strings.HasPrefix(s, self.Prefix) { return false } return sutil.IndexAnyRunes(s[len(self.Prefix):], self.Separators) == -1 } func (self PrefixAny) String() string { return fmt.Sprintf("", self.Prefix, string(self.Separators)) } ================================================ FILE: vendor/github.com/gobwas/glob/match/prefix_suffix.go ================================================ package match import ( "fmt" "strings" ) type PrefixSuffix struct { Prefix, Suffix string } func NewPrefixSuffix(p, s string) PrefixSuffix { return PrefixSuffix{p, s} } func (self PrefixSuffix) Index(s string) (int, []int) { prefixIdx := strings.Index(s, self.Prefix) if prefixIdx == -1 { return -1, nil } suffixLen := len(self.Suffix) if suffixLen <= 0 { return prefixIdx, []int{len(s) - prefixIdx} } if (len(s) - prefixIdx) <= 0 { return -1, nil } segments := acquireSegments(len(s) - prefixIdx) for sub := s[prefixIdx:]; ; { suffixIdx := strings.LastIndex(sub, self.Suffix) if suffixIdx == -1 { break } segments = append(segments, suffixIdx+suffixLen) sub = sub[:suffixIdx] } if len(segments) == 0 { releaseSegments(segments) return -1, nil } reverseSegments(segments) return prefixIdx, segments } func (self PrefixSuffix) Len() int { return lenNo } func (self PrefixSuffix) Match(s string) bool { return strings.HasPrefix(s, self.Prefix) && strings.HasSuffix(s, self.Suffix) } func (self PrefixSuffix) String() string { return fmt.Sprintf("", self.Prefix, self.Suffix) } ================================================ FILE: vendor/github.com/gobwas/glob/match/range.go ================================================ package match import ( "fmt" "unicode/utf8" ) type Range struct { Lo, Hi rune Not bool } func NewRange(lo, hi rune, not bool) Range { return Range{lo, hi, not} } func (self Range) Len() int { return lenOne } func (self Range) Match(s string) bool { r, w := utf8.DecodeRuneInString(s) if len(s) > w { return false } inRange := r >= self.Lo && r <= self.Hi return inRange == !self.Not } func (self Range) Index(s string) (int, []int) { for i, r := range s { if self.Not != (r >= self.Lo && r <= self.Hi) { return i, segmentsByRuneLength[utf8.RuneLen(r)] } } return -1, nil } func (self Range) String() string { var not string if self.Not { not = "!" } return fmt.Sprintf("", not, string(self.Lo), string(self.Hi)) } ================================================ FILE: vendor/github.com/gobwas/glob/match/row.go ================================================ package match import ( "fmt" ) type Row struct { Matchers Matchers RunesLength int Segments []int } func NewRow(len int, m ...Matcher) Row { return Row{ Matchers: Matchers(m), RunesLength: len, Segments: []int{len}, } } func (self Row) matchAll(s string) bool { var idx int for _, m := range self.Matchers { length := m.Len() var next, i int for next = range s[idx:] { i++ if i == length { break } } if i < length || !m.Match(s[idx:idx+next+1]) { return false } idx += next + 1 } return true } func (self Row) lenOk(s string) bool { var i int for range s { i++ if i > self.RunesLength { return false } } return self.RunesLength == i } func (self Row) Match(s string) bool { return self.lenOk(s) && self.matchAll(s) } func (self Row) Len() (l int) { return self.RunesLength } func (self Row) Index(s string) (int, []int) { for i := range s { if len(s[i:]) < self.RunesLength { break } if self.matchAll(s[i:]) { return i, self.Segments } } return -1, nil } func (self Row) String() string { return fmt.Sprintf("", self.RunesLength, self.Matchers) } ================================================ FILE: vendor/github.com/gobwas/glob/match/segments.go ================================================ package match import ( "sync" ) type SomePool interface { Get() []int Put([]int) } var segmentsPools [1024]sync.Pool func toPowerOfTwo(v int) int { v-- v |= v >> 1 v |= v >> 2 v |= v >> 4 v |= v >> 8 v |= v >> 16 v++ return v } const ( cacheFrom = 16 cacheToAndHigher = 1024 cacheFromIndex = 15 cacheToAndHigherIndex = 1023 ) var ( segments0 = []int{0} segments1 = []int{1} segments2 = []int{2} segments3 = []int{3} segments4 = []int{4} ) var segmentsByRuneLength [5][]int = [5][]int{ 0: segments0, 1: segments1, 2: segments2, 3: segments3, 4: segments4, } func init() { for i := cacheToAndHigher; i >= cacheFrom; i >>= 1 { func(i int) { segmentsPools[i-1] = sync.Pool{New: func() interface{} { return make([]int, 0, i) }} }(i) } } func getTableIndex(c int) int { p := toPowerOfTwo(c) switch { case p >= cacheToAndHigher: return cacheToAndHigherIndex case p <= cacheFrom: return cacheFromIndex default: return p - 1 } } func acquireSegments(c int) []int { // make []int with less capacity than cacheFrom // is faster than acquiring it from pool if c < cacheFrom { return make([]int, 0, c) } return segmentsPools[getTableIndex(c)].Get().([]int)[:0] } func releaseSegments(s []int) { c := cap(s) // make []int with less capacity than cacheFrom // is faster than acquiring it from pool if c < cacheFrom { return } segmentsPools[getTableIndex(c)].Put(s) } ================================================ FILE: vendor/github.com/gobwas/glob/match/single.go ================================================ package match import ( "fmt" "github.com/gobwas/glob/util/runes" "unicode/utf8" ) // single represents ? type Single struct { Separators []rune } func NewSingle(s []rune) Single { return Single{s} } func (self Single) Match(s string) bool { r, w := utf8.DecodeRuneInString(s) if len(s) > w { return false } return runes.IndexRune(self.Separators, r) == -1 } func (self Single) Len() int { return lenOne } func (self Single) Index(s string) (int, []int) { for i, r := range s { if runes.IndexRune(self.Separators, r) == -1 { return i, segmentsByRuneLength[utf8.RuneLen(r)] } } return -1, nil } func (self Single) String() string { return fmt.Sprintf("", string(self.Separators)) } ================================================ FILE: vendor/github.com/gobwas/glob/match/suffix.go ================================================ package match import ( "fmt" "strings" ) type Suffix struct { Suffix string } func NewSuffix(s string) Suffix { return Suffix{s} } func (self Suffix) Len() int { return lenNo } func (self Suffix) Match(s string) bool { return strings.HasSuffix(s, self.Suffix) } func (self Suffix) Index(s string) (int, []int) { idx := strings.Index(s, self.Suffix) if idx == -1 { return -1, nil } return 0, []int{idx + len(self.Suffix)} } func (self Suffix) String() string { return fmt.Sprintf("", self.Suffix) } ================================================ FILE: vendor/github.com/gobwas/glob/match/suffix_any.go ================================================ package match import ( "fmt" "strings" sutil "github.com/gobwas/glob/util/strings" ) type SuffixAny struct { Suffix string Separators []rune } func NewSuffixAny(s string, sep []rune) SuffixAny { return SuffixAny{s, sep} } func (self SuffixAny) Index(s string) (int, []int) { idx := strings.Index(s, self.Suffix) if idx == -1 { return -1, nil } i := sutil.LastIndexAnyRunes(s[:idx], self.Separators) + 1 return i, []int{idx + len(self.Suffix) - i} } func (self SuffixAny) Len() int { return lenNo } func (self SuffixAny) Match(s string) bool { if !strings.HasSuffix(s, self.Suffix) { return false } return sutil.IndexAnyRunes(s[:len(s)-len(self.Suffix)], self.Separators) == -1 } func (self SuffixAny) String() string { return fmt.Sprintf("", string(self.Separators), self.Suffix) } ================================================ FILE: vendor/github.com/gobwas/glob/match/super.go ================================================ package match import ( "fmt" ) type Super struct{} func NewSuper() Super { return Super{} } func (self Super) Match(s string) bool { return true } func (self Super) Len() int { return lenNo } func (self Super) Index(s string) (int, []int) { segments := acquireSegments(len(s) + 1) for i := range s { segments = append(segments, i) } segments = append(segments, len(s)) return 0, segments } func (self Super) String() string { return fmt.Sprintf("") } ================================================ FILE: vendor/github.com/gobwas/glob/match/text.go ================================================ package match import ( "fmt" "strings" "unicode/utf8" ) // raw represents raw string to match type Text struct { Str string RunesLength int BytesLength int Segments []int } func NewText(s string) Text { return Text{ Str: s, RunesLength: utf8.RuneCountInString(s), BytesLength: len(s), Segments: []int{len(s)}, } } func (self Text) Match(s string) bool { return self.Str == s } func (self Text) Len() int { return self.RunesLength } func (self Text) Index(s string) (int, []int) { index := strings.Index(s, self.Str) if index == -1 { return -1, nil } return index, self.Segments } func (self Text) String() string { return fmt.Sprintf("", self.Str) } ================================================ FILE: vendor/github.com/gobwas/glob/readme.md ================================================ # glob.[go](https://golang.org) [![GoDoc][godoc-image]][godoc-url] [![Build Status][travis-image]][travis-url] > Go Globbing Library. ## Install ```shell go get github.com/gobwas/glob ``` ## Example ```go package main import "github.com/gobwas/glob" func main() { var g glob.Glob // create simple glob g = glob.MustCompile("*.github.com") g.Match("api.github.com") // true // quote meta characters and then create simple glob g = glob.MustCompile(glob.QuoteMeta("*.github.com")) g.Match("*.github.com") // true // create new glob with set of delimiters as ["."] g = glob.MustCompile("api.*.com", '.') g.Match("api.github.com") // true g.Match("api.gi.hub.com") // false // create new glob with set of delimiters as ["."] // but now with super wildcard g = glob.MustCompile("api.**.com", '.') g.Match("api.github.com") // true g.Match("api.gi.hub.com") // true // create glob with single symbol wildcard g = glob.MustCompile("?at") g.Match("cat") // true g.Match("fat") // true g.Match("at") // false // create glob with single symbol wildcard and delimiters ['f'] g = glob.MustCompile("?at", 'f') g.Match("cat") // true g.Match("fat") // false g.Match("at") // false // create glob with character-list matchers g = glob.MustCompile("[abc]at") g.Match("cat") // true g.Match("bat") // true g.Match("fat") // false g.Match("at") // false // create glob with character-list matchers g = glob.MustCompile("[!abc]at") g.Match("cat") // false g.Match("bat") // false g.Match("fat") // true g.Match("at") // false // create glob with character-range matchers g = glob.MustCompile("[a-c]at") g.Match("cat") // true g.Match("bat") // true g.Match("fat") // false g.Match("at") // false // create glob with character-range matchers g = glob.MustCompile("[!a-c]at") g.Match("cat") // false g.Match("bat") // false g.Match("fat") // true g.Match("at") // false // create glob with pattern-alternatives list g = glob.MustCompile("{cat,bat,[fr]at}") g.Match("cat") // true g.Match("bat") // true g.Match("fat") // true g.Match("rat") // true g.Match("at") // false g.Match("zat") // false } ``` ## Performance This library is created for compile-once patterns. This means, that compilation could take time, but strings matching is done faster, than in case when always parsing template. If you will not use compiled `glob.Glob` object, and do `g := glob.MustCompile(pattern); g.Match(...)` every time, then your code will be much more slower. Run `go test -bench=.` from source root to see the benchmarks: Pattern | Fixture | Match | Speed (ns/op) --------|---------|-------|-------------- `[a-z][!a-x]*cat*[h][!b]*eyes*` | `my cat has very bright eyes` | `true` | 432 `[a-z][!a-x]*cat*[h][!b]*eyes*` | `my dog has very bright eyes` | `false` | 199 `https://*.google.*` | `https://account.google.com` | `true` | 96 `https://*.google.*` | `https://google.com` | `false` | 66 `{https://*.google.*,*yandex.*,*yahoo.*,*mail.ru}` | `http://yahoo.com` | `true` | 163 `{https://*.google.*,*yandex.*,*yahoo.*,*mail.ru}` | `http://google.com` | `false` | 197 `{https://*gobwas.com,http://exclude.gobwas.com}` | `https://safe.gobwas.com` | `true` | 22 `{https://*gobwas.com,http://exclude.gobwas.com}` | `http://safe.gobwas.com` | `false` | 24 `abc*` | `abcdef` | `true` | 8.15 `abc*` | `af` | `false` | 5.68 `*def` | `abcdef` | `true` | 8.84 `*def` | `af` | `false` | 5.74 `ab*ef` | `abcdef` | `true` | 15.2 `ab*ef` | `af` | `false` | 10.4 The same things with `regexp` package: Pattern | Fixture | Match | Speed (ns/op) --------|---------|-------|-------------- `^[a-z][^a-x].*cat.*[h][^b].*eyes.*$` | `my cat has very bright eyes` | `true` | 2553 `^[a-z][^a-x].*cat.*[h][^b].*eyes.*$` | `my dog has very bright eyes` | `false` | 1383 `^https:\/\/.*\.google\..*$` | `https://account.google.com` | `true` | 1205 `^https:\/\/.*\.google\..*$` | `https://google.com` | `false` | 767 `^(https:\/\/.*\.google\..*|.*yandex\..*|.*yahoo\..*|.*mail\.ru)$` | `http://yahoo.com` | `true` | 1435 `^(https:\/\/.*\.google\..*|.*yandex\..*|.*yahoo\..*|.*mail\.ru)$` | `http://google.com` | `false` | 1674 `^(https:\/\/.*gobwas\.com|http://exclude.gobwas.com)$` | `https://safe.gobwas.com` | `true` | 1039 `^(https:\/\/.*gobwas\.com|http://exclude.gobwas.com)$` | `http://safe.gobwas.com` | `false` | 272 `^abc.*$` | `abcdef` | `true` | 237 `^abc.*$` | `af` | `false` | 100 `^.*def$` | `abcdef` | `true` | 464 `^.*def$` | `af` | `false` | 265 `^ab.*ef$` | `abcdef` | `true` | 375 `^ab.*ef$` | `af` | `false` | 145 [godoc-image]: https://godoc.org/github.com/gobwas/glob?status.svg [godoc-url]: https://godoc.org/github.com/gobwas/glob [travis-image]: https://travis-ci.org/gobwas/glob.svg?branch=master [travis-url]: https://travis-ci.org/gobwas/glob ## Syntax Syntax is inspired by [standard wildcards](http://tldp.org/LDP/GNU-Linux-Tools-Summary/html/x11655.htm), except that `**` is aka super-asterisk, that do not sensitive for separators. ================================================ FILE: vendor/github.com/gobwas/glob/syntax/ast/ast.go ================================================ package ast import ( "bytes" "fmt" ) type Node struct { Parent *Node Children []*Node Value interface{} Kind Kind } func NewNode(k Kind, v interface{}, ch ...*Node) *Node { n := &Node{ Kind: k, Value: v, } for _, c := range ch { Insert(n, c) } return n } func (a *Node) Equal(b *Node) bool { if a.Kind != b.Kind { return false } if a.Value != b.Value { return false } if len(a.Children) != len(b.Children) { return false } for i, c := range a.Children { if !c.Equal(b.Children[i]) { return false } } return true } func (a *Node) String() string { var buf bytes.Buffer buf.WriteString(a.Kind.String()) if a.Value != nil { buf.WriteString(" =") buf.WriteString(fmt.Sprintf("%v", a.Value)) } if len(a.Children) > 0 { buf.WriteString(" [") for i, c := range a.Children { if i > 0 { buf.WriteString(", ") } buf.WriteString(c.String()) } buf.WriteString("]") } return buf.String() } func Insert(parent *Node, children ...*Node) { parent.Children = append(parent.Children, children...) for _, ch := range children { ch.Parent = parent } } type List struct { Not bool Chars string } type Range struct { Not bool Lo, Hi rune } type Text struct { Text string } type Kind int const ( KindNothing Kind = iota KindPattern KindList KindRange KindText KindAny KindSuper KindSingle KindAnyOf ) func (k Kind) String() string { switch k { case KindNothing: return "Nothing" case KindPattern: return "Pattern" case KindList: return "List" case KindRange: return "Range" case KindText: return "Text" case KindAny: return "Any" case KindSuper: return "Super" case KindSingle: return "Single" case KindAnyOf: return "AnyOf" default: return "" } } ================================================ FILE: vendor/github.com/gobwas/glob/syntax/ast/parser.go ================================================ package ast import ( "errors" "fmt" "github.com/gobwas/glob/syntax/lexer" "unicode/utf8" ) type Lexer interface { Next() lexer.Token } type parseFn func(*Node, Lexer) (parseFn, *Node, error) func Parse(lexer Lexer) (*Node, error) { var parser parseFn root := NewNode(KindPattern, nil) var ( tree *Node err error ) for parser, tree = parserMain, root; parser != nil; { parser, tree, err = parser(tree, lexer) if err != nil { return nil, err } } return root, nil } func parserMain(tree *Node, lex Lexer) (parseFn, *Node, error) { for { token := lex.Next() switch token.Type { case lexer.EOF: return nil, tree, nil case lexer.Error: return nil, tree, errors.New(token.Raw) case lexer.Text: Insert(tree, NewNode(KindText, Text{token.Raw})) return parserMain, tree, nil case lexer.Any: Insert(tree, NewNode(KindAny, nil)) return parserMain, tree, nil case lexer.Super: Insert(tree, NewNode(KindSuper, nil)) return parserMain, tree, nil case lexer.Single: Insert(tree, NewNode(KindSingle, nil)) return parserMain, tree, nil case lexer.RangeOpen: return parserRange, tree, nil case lexer.TermsOpen: a := NewNode(KindAnyOf, nil) Insert(tree, a) p := NewNode(KindPattern, nil) Insert(a, p) return parserMain, p, nil case lexer.Separator: p := NewNode(KindPattern, nil) Insert(tree.Parent, p) return parserMain, p, nil case lexer.TermsClose: return parserMain, tree.Parent.Parent, nil default: return nil, tree, fmt.Errorf("unexpected token: %s", token) } } return nil, tree, fmt.Errorf("unknown error") } func parserRange(tree *Node, lex Lexer) (parseFn, *Node, error) { var ( not bool lo rune hi rune chars string ) for { token := lex.Next() switch token.Type { case lexer.EOF: return nil, tree, errors.New("unexpected end") case lexer.Error: return nil, tree, errors.New(token.Raw) case lexer.Not: not = true case lexer.RangeLo: r, w := utf8.DecodeRuneInString(token.Raw) if len(token.Raw) > w { return nil, tree, fmt.Errorf("unexpected length of lo character") } lo = r case lexer.RangeBetween: // case lexer.RangeHi: r, w := utf8.DecodeRuneInString(token.Raw) if len(token.Raw) > w { return nil, tree, fmt.Errorf("unexpected length of lo character") } hi = r if hi < lo { return nil, tree, fmt.Errorf("hi character '%s' should be greater than lo '%s'", string(hi), string(lo)) } case lexer.Text: chars = token.Raw case lexer.RangeClose: isRange := lo != 0 && hi != 0 isChars := chars != "" if isChars == isRange { return nil, tree, fmt.Errorf("could not parse range") } if isRange { Insert(tree, NewNode(KindRange, Range{ Lo: lo, Hi: hi, Not: not, })) } else { Insert(tree, NewNode(KindList, List{ Chars: chars, Not: not, })) } return parserMain, tree, nil } } } ================================================ FILE: vendor/github.com/gobwas/glob/syntax/lexer/lexer.go ================================================ package lexer import ( "bytes" "fmt" "github.com/gobwas/glob/util/runes" "unicode/utf8" ) const ( char_any = '*' char_comma = ',' char_single = '?' char_escape = '\\' char_range_open = '[' char_range_close = ']' char_terms_open = '{' char_terms_close = '}' char_range_not = '!' char_range_between = '-' ) var specials = []byte{ char_any, char_single, char_escape, char_range_open, char_range_close, char_terms_open, char_terms_close, } func Special(c byte) bool { return bytes.IndexByte(specials, c) != -1 } type tokens []Token func (i *tokens) shift() (ret Token) { ret = (*i)[0] copy(*i, (*i)[1:]) *i = (*i)[:len(*i)-1] return } func (i *tokens) push(v Token) { *i = append(*i, v) } func (i *tokens) empty() bool { return len(*i) == 0 } var eof rune = 0 type lexer struct { data string pos int err error tokens tokens termsLevel int lastRune rune lastRuneSize int hasRune bool } func NewLexer(source string) *lexer { l := &lexer{ data: source, tokens: tokens(make([]Token, 0, 4)), } return l } func (l *lexer) Next() Token { if l.err != nil { return Token{Error, l.err.Error()} } if !l.tokens.empty() { return l.tokens.shift() } l.fetchItem() return l.Next() } func (l *lexer) peek() (r rune, w int) { if l.pos == len(l.data) { return eof, 0 } r, w = utf8.DecodeRuneInString(l.data[l.pos:]) if r == utf8.RuneError { l.errorf("could not read rune") r = eof w = 0 } return } func (l *lexer) read() rune { if l.hasRune { l.hasRune = false l.seek(l.lastRuneSize) return l.lastRune } r, s := l.peek() l.seek(s) l.lastRune = r l.lastRuneSize = s return r } func (l *lexer) seek(w int) { l.pos += w } func (l *lexer) unread() { if l.hasRune { l.errorf("could not unread rune") return } l.seek(-l.lastRuneSize) l.hasRune = true } func (l *lexer) errorf(f string, v ...interface{}) { l.err = fmt.Errorf(f, v...) } func (l *lexer) inTerms() bool { return l.termsLevel > 0 } func (l *lexer) termsEnter() { l.termsLevel++ } func (l *lexer) termsLeave() { l.termsLevel-- } var inTextBreakers = []rune{char_single, char_any, char_range_open, char_terms_open} var inTermsBreakers = append(inTextBreakers, char_terms_close, char_comma) func (l *lexer) fetchItem() { r := l.read() switch { case r == eof: l.tokens.push(Token{EOF, ""}) case r == char_terms_open: l.termsEnter() l.tokens.push(Token{TermsOpen, string(r)}) case r == char_comma && l.inTerms(): l.tokens.push(Token{Separator, string(r)}) case r == char_terms_close && l.inTerms(): l.tokens.push(Token{TermsClose, string(r)}) l.termsLeave() case r == char_range_open: l.tokens.push(Token{RangeOpen, string(r)}) l.fetchRange() case r == char_single: l.tokens.push(Token{Single, string(r)}) case r == char_any: if l.read() == char_any { l.tokens.push(Token{Super, string(r) + string(r)}) } else { l.unread() l.tokens.push(Token{Any, string(r)}) } default: l.unread() var breakers []rune if l.inTerms() { breakers = inTermsBreakers } else { breakers = inTextBreakers } l.fetchText(breakers) } } func (l *lexer) fetchRange() { var wantHi bool var wantClose bool var seenNot bool for { r := l.read() if r == eof { l.errorf("unexpected end of input") return } if wantClose { if r != char_range_close { l.errorf("expected close range character") } else { l.tokens.push(Token{RangeClose, string(r)}) } return } if wantHi { l.tokens.push(Token{RangeHi, string(r)}) wantClose = true continue } if !seenNot && r == char_range_not { l.tokens.push(Token{Not, string(r)}) seenNot = true continue } if n, w := l.peek(); n == char_range_between { l.seek(w) l.tokens.push(Token{RangeLo, string(r)}) l.tokens.push(Token{RangeBetween, string(n)}) wantHi = true continue } l.unread() // unread first peek and fetch as text l.fetchText([]rune{char_range_close}) wantClose = true } } func (l *lexer) fetchText(breakers []rune) { var data []rune var escaped bool reading: for { r := l.read() if r == eof { break } if !escaped { if r == char_escape { escaped = true continue } if runes.IndexRune(breakers, r) != -1 { l.unread() break reading } } escaped = false data = append(data, r) } if len(data) > 0 { l.tokens.push(Token{Text, string(data)}) } } ================================================ FILE: vendor/github.com/gobwas/glob/syntax/lexer/token.go ================================================ package lexer import "fmt" type TokenType int const ( EOF TokenType = iota Error Text Char Any Super Single Not Separator RangeOpen RangeClose RangeLo RangeHi RangeBetween TermsOpen TermsClose ) func (tt TokenType) String() string { switch tt { case EOF: return "eof" case Error: return "error" case Text: return "text" case Char: return "char" case Any: return "any" case Super: return "super" case Single: return "single" case Not: return "not" case Separator: return "separator" case RangeOpen: return "range_open" case RangeClose: return "range_close" case RangeLo: return "range_lo" case RangeHi: return "range_hi" case RangeBetween: return "range_between" case TermsOpen: return "terms_open" case TermsClose: return "terms_close" default: return "undef" } } type Token struct { Type TokenType Raw string } func (t Token) String() string { return fmt.Sprintf("%v<%q>", t.Type, t.Raw) } ================================================ FILE: vendor/github.com/gobwas/glob/syntax/syntax.go ================================================ package syntax import ( "github.com/gobwas/glob/syntax/ast" "github.com/gobwas/glob/syntax/lexer" ) func Parse(s string) (*ast.Node, error) { return ast.Parse(lexer.NewLexer(s)) } func Special(b byte) bool { return lexer.Special(b) } ================================================ FILE: vendor/github.com/gobwas/glob/util/runes/runes.go ================================================ package runes func Index(s, needle []rune) int { ls, ln := len(s), len(needle) switch { case ln == 0: return 0 case ln == 1: return IndexRune(s, needle[0]) case ln == ls: if Equal(s, needle) { return 0 } return -1 case ln > ls: return -1 } head: for i := 0; i < ls && ls-i >= ln; i++ { for y := 0; y < ln; y++ { if s[i+y] != needle[y] { continue head } } return i } return -1 } func LastIndex(s, needle []rune) int { ls, ln := len(s), len(needle) switch { case ln == 0: if ls == 0 { return 0 } return ls case ln == 1: return IndexLastRune(s, needle[0]) case ln == ls: if Equal(s, needle) { return 0 } return -1 case ln > ls: return -1 } head: for i := ls - 1; i >= 0 && i >= ln; i-- { for y := ln - 1; y >= 0; y-- { if s[i-(ln-y-1)] != needle[y] { continue head } } return i - ln + 1 } return -1 } // IndexAny returns the index of the first instance of any Unicode code point // from chars in s, or -1 if no Unicode code point from chars is present in s. func IndexAny(s, chars []rune) int { if len(chars) > 0 { for i, c := range s { for _, m := range chars { if c == m { return i } } } } return -1 } func Contains(s, needle []rune) bool { return Index(s, needle) >= 0 } func Max(s []rune) (max rune) { for _, r := range s { if r > max { max = r } } return } func Min(s []rune) rune { min := rune(-1) for _, r := range s { if min == -1 { min = r continue } if r < min { min = r } } return min } func IndexRune(s []rune, r rune) int { for i, c := range s { if c == r { return i } } return -1 } func IndexLastRune(s []rune, r rune) int { for i := len(s) - 1; i >= 0; i-- { if s[i] == r { return i } } return -1 } func Equal(a, b []rune) bool { if len(a) == len(b) { for i := 0; i < len(a); i++ { if a[i] != b[i] { return false } } return true } return false } // HasPrefix tests whether the string s begins with prefix. func HasPrefix(s, prefix []rune) bool { return len(s) >= len(prefix) && Equal(s[0:len(prefix)], prefix) } // HasSuffix tests whether the string s ends with suffix. func HasSuffix(s, suffix []rune) bool { return len(s) >= len(suffix) && Equal(s[len(s)-len(suffix):], suffix) } ================================================ FILE: vendor/github.com/gobwas/glob/util/strings/strings.go ================================================ package strings import ( "strings" "unicode/utf8" ) func IndexAnyRunes(s string, rs []rune) int { for _, r := range rs { if i := strings.IndexRune(s, r); i != -1 { return i } } return -1 } func LastIndexAnyRunes(s string, rs []rune) int { for _, r := range rs { i := -1 if 0 <= r && r < utf8.RuneSelf { i = strings.LastIndexByte(s, byte(r)) } else { sub := s for len(sub) > 0 { j := strings.IndexRune(s, r) if j == -1 { break } i = j sub = sub[i+1:] } } if i != -1 { return i } } return -1 } ================================================ FILE: vendor/github.com/gocolly/colly/v2/.codecov.yml ================================================ comment: false ================================================ FILE: vendor/github.com/gocolly/colly/v2/.travis.yml ================================================ language: go sudo: false go: - 1.11.x - 1.12.x - 1.13.x - tip script: - go get -u golang.org/x/lint/golint - OUT="$(go get -a)"; test -z "$OUT" || (echo "$OUT" && return 1) - OUT="$(gofmt -l -d ./)"; test -z "$OUT" || (echo "$OUT" && return 1) - OUT="$(golint ./...)"; test -z "$OUT" || (echo "$OUT" && return 1) - go vet -v ./... - go test -race -v -coverprofile=coverage.txt -covermode=atomic ./ - go build after_success: - bash <(curl -s https://codecov.io/bash) ================================================ FILE: vendor/github.com/gocolly/colly/v2/CHANGELOG.md ================================================ # 2.0.0 - 2019.11.28 - Breaking change: Change Collector.RedirectHandler member to Collector.SetRedirectHandler function - Go module support - Collector.HasVisited method added to be able to check if an url has been visited - Collector.SetClient method introduced - HTMLElement.ChildTexts method added - New user agents - Multiple bugfixes # 1.2.0 - 2019.02.13 - Compatibility with the latest htmlquery package - New request shortcut for HEAD requests - Check URL availibility before visiting - Fix proxy URL value - Request counter fix - Minor fixes in examples # 1.1.0 - 2018.08.13 - Appengine integration takes context.Context instead of http.Request (API change) - Added "Accept" http header by default to every request - Support slices of pointers in unmarshal - Fixed a race condition in queues - ForEachWithBreak method added to HTMLElement - Added a local file example - Support gzip decompression of response bodies - Don't share waitgroup when cloning a collector - Fixed instagram example # 1.0.0 - 2018.05.13 ================================================ FILE: vendor/github.com/gocolly/colly/v2/CONTRIBUTING.md ================================================ # Contribute ## Introduction First, thank you for considering contributing to colly! It's people like you that make the open source community such a great community! 😊 We welcome any type of contribution, not only code. You can help with - **QA**: file bug reports, the more details you can give the better (e.g. screenshots with the console open) - **Marketing**: writing blog posts, howto's, printing stickers, ... - **Community**: presenting the project at meetups, organizing a dedicated meetup for the local community, ... - **Code**: take a look at the [open issues](https://github.com/gocolly/colly/issues). Even if you can't write code, commenting on them, showing that you care about a given issue matters. It helps us triage them. - **Money**: we welcome financial contributions in full transparency on our [open collective](https://opencollective.com/colly). ## Your First Contribution Working on your first Pull Request? You can learn how from this *free* series, [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github). ## Submitting code Any code change should be submitted as a pull request. The description should explain what the code does and give steps to execute it. The pull request should also contain tests. ## Code review process The bigger the pull request, the longer it will take to review and merge. Try to break down large pull requests in smaller chunks that are easier to review and merge. It is also always helpful to have some context for your pull request. What was the purpose? Why does it matter to you? ## Financial contributions We also welcome financial contributions in full transparency on our [open collective](https://opencollective.com/colly). Anyone can file an expense. If the expense makes sense for the development of the community, it will be "merged" in the ledger of our open collective by the core contributors and the person who filed the expense will be reimbursed. ## Questions If you have any questions, create an [issue](https://github.com/gocolly/colly/issues/new) (protip: do a quick search first to see if someone else didn't ask the same question before!). You can also reach us at hello@colly.opencollective.com. ## Credits ### Contributors Thank you to all the people who have already contributed to colly! ### Backers Thank you to all our backers! [[Become a backer](https://opencollective.com/colly#backer)] ### Sponsors Thank you to all our sponsors! (please ask your company to also support this open source project by [becoming a sponsor](https://opencollective.com/colly#sponsor)) ================================================ FILE: vendor/github.com/gocolly/colly/v2/LICENSE.txt ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/gocolly/colly/v2/README.md ================================================ # Colly Lightning Fast and Elegant Scraping Framework for Gophers Colly provides a clean interface to write any kind of crawler/scraper/spider. With Colly you can easily extract structured data from websites, which can be used for a wide range of applications, like data mining, data processing or archiving. [![GoDoc](https://godoc.org/github.com/gocolly/colly?status.svg)](https://pkg.go.dev/github.com/gocolly/colly/v2) [![Backers on Open Collective](https://opencollective.com/colly/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/colly/sponsors/badge.svg)](#sponsors) [![build status](https://img.shields.io/travis/gocolly/colly/master.svg?style=flat-square)](https://travis-ci.org/gocolly/colly) [![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/gocolly/colly) [![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/gocolly/colly/tree/master/_examples) [![Code Coverage](https://img.shields.io/codecov/c/github/gocolly/colly/master.svg)](https://codecov.io/github/gocolly/colly?branch=master) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fgocolly%2Fcolly.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fgocolly%2Fcolly?ref=badge_shield) [![Twitter URL](https://img.shields.io/badge/twitter-follow-green.svg)](https://twitter.com/gocolly) ## Features - Clean API - Fast (>1k request/sec on a single core) - Manages request delays and maximum concurrency per domain - Automatic cookie and session handling - Sync/async/parallel scraping - Caching - Automatic encoding of non-unicode responses - Robots.txt support - Distributed scraping - Configuration via environment variables - Extensions ## Example ```go func main() { c := colly.NewCollector() // Find and visit all links c.OnHTML("a[href]", func(e *colly.HTMLElement) { e.Request.Visit(e.Attr("href")) }) c.OnRequest(func(r *colly.Request) { fmt.Println("Visiting", r.URL) }) c.Visit("http://go-colly.org/") } ``` See [examples folder](https://github.com/gocolly/colly/tree/master/_examples) for more detailed examples. ## Installation Add colly to your `go.mod` file: ``` module github.com/x/y go 1.14 require ( github.com/gocolly/colly/v2 latest ) ``` ## Bugs Bugs or suggestions? Visit the [issue tracker](https://github.com/gocolly/colly/issues) or join `#colly` on freenode ## Other Projects Using Colly Below is a list of public, open source projects that use Colly: - [greenpeace/check-my-pages](https://github.com/greenpeace/check-my-pages) Scraping script to test the Spanish Greenpeace web archive. - [altsab/gowap](https://github.com/altsab/gowap) Wappalyzer implementation in Go. - [jesuiscamille/goquotes](https://github.com/jesuiscamille/goquotes) A quotes scrapper, making your day a little better! - [jivesearch/jivesearch](https://github.com/jivesearch/jivesearch) A search engine that doesn't track you. - [Leagify/colly-draft-prospects](https://github.com/Leagify/colly-draft-prospects) A scraper for future NFL Draft prospects. - [lucasepe/go-ps4](https://github.com/lucasepe/go-ps4) Search playstation store for your favorite PS4 games using the command line. - [yringler/inside-chassidus-scraper](https://github.com/yringler/inside-chassidus-scraper) Scrapes Rabbi Paltiel's web site for lesson metadata. - [gamedb/gamedb](https://github.com/gamedb/gamedb) A database of Steam games. - [lawzava/scrape](https://github.com/lawzava/scrape) CLI for email scraping from any website. - [eureka101v/WeiboSpiderGo](https://github.com/eureka101v/WeiboSpiderGo) A sina weibo(chinese twitter) scrapper - [Go-phie/gophie](https://github.com/Go-phie/gophie) Search, Download and Stream movies from your terminal - [imthaghost/goclone](https://github.com/imthaghost/goclone) Clone websites to your computer within seconds. If you are using Colly in a project please send a pull request to add it to the list. ## Contributors This project exists thanks to all the people who contribute. [[Contribute]](CONTRIBUTING.md). ## Backers Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/colly#backer)] ## Sponsors Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/colly#sponsor)] ## License [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fgocolly%2Fcolly.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fgocolly%2Fcolly?ref=badge_large) ================================================ FILE: vendor/github.com/gocolly/colly/v2/VERSION ================================================ 2.1.0 ================================================ FILE: vendor/github.com/gocolly/colly/v2/colly.go ================================================ // Copyright 2018 Adam Tauber // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package colly implements a HTTP scraping framework package colly import ( "bytes" "context" "crypto/rand" "encoding/json" "errors" "fmt" "hash/fnv" "io" "io/ioutil" "log" "net/http" "net/http/cookiejar" "net/url" "os" "path/filepath" "regexp" "strconv" "strings" "sync" "sync/atomic" "time" "github.com/PuerkitoBio/goquery" "github.com/antchfx/htmlquery" "github.com/antchfx/xmlquery" "github.com/gocolly/colly/v2/debug" "github.com/gocolly/colly/v2/storage" "github.com/kennygrant/sanitize" "github.com/temoto/robotstxt" "google.golang.org/appengine/urlfetch" ) // A CollectorOption sets an option on a Collector. type CollectorOption func(*Collector) // Collector provides the scraper instance for a scraping job type Collector struct { // UserAgent is the User-Agent string used by HTTP requests UserAgent string // MaxDepth limits the recursion depth of visited URLs. // Set it to 0 for infinite recursion (default). MaxDepth int // AllowedDomains is a domain whitelist. // Leave it blank to allow any domains to be visited AllowedDomains []string // DisallowedDomains is a domain blacklist. DisallowedDomains []string // DisallowedURLFilters is a list of regular expressions which restricts // visiting URLs. If any of the rules matches to a URL the // request will be stopped. DisallowedURLFilters will // be evaluated before URLFilters // Leave it blank to allow any URLs to be visited DisallowedURLFilters []*regexp.Regexp // URLFilters is a list of regular expressions which restricts // visiting URLs. If any of the rules matches to a URL the // request won't be stopped. DisallowedURLFilters will // be evaluated before URLFilters // Leave it blank to allow any URLs to be visited URLFilters []*regexp.Regexp // AllowURLRevisit allows multiple downloads of the same URL AllowURLRevisit bool // MaxBodySize is the limit of the retrieved response body in bytes. // 0 means unlimited. // The default value for MaxBodySize is 10MB (10 * 1024 * 1024 bytes). MaxBodySize int // CacheDir specifies a location where GET requests are cached as files. // When it's not defined, caching is disabled. CacheDir string // IgnoreRobotsTxt allows the Collector to ignore any restrictions set by // the target host's robots.txt file. See http://www.robotstxt.org/ for more // information. IgnoreRobotsTxt bool // Async turns on asynchronous network communication. Use Collector.Wait() to // be sure all requests have been finished. Async bool // ParseHTTPErrorResponse allows parsing HTTP responses with non 2xx status codes. // By default, Colly parses only successful HTTP responses. Set ParseHTTPErrorResponse // to true to enable it. ParseHTTPErrorResponse bool // ID is the unique identifier of a collector ID uint32 // DetectCharset can enable character encoding detection for non-utf8 response bodies // without explicit charset declaration. This feature uses https://github.com/saintfish/chardet DetectCharset bool // RedirectHandler allows control on how a redirect will be managed // use c.SetRedirectHandler to set this value redirectHandler func(req *http.Request, via []*http.Request) error // CheckHead performs a HEAD request before every GET to pre-validate the response CheckHead bool // TraceHTTP enables capturing and reporting request performance for crawler tuning. // When set to true, the Response.Trace will be filled in with an HTTPTrace object. TraceHTTP bool store storage.Storage debugger debug.Debugger robotsMap map[string]*robotstxt.RobotsData htmlCallbacks []*htmlCallbackContainer xmlCallbacks []*xmlCallbackContainer requestCallbacks []RequestCallback responseCallbacks []ResponseCallback responseHeadersCallbacks []ResponseHeadersCallback errorCallbacks []ErrorCallback scrapedCallbacks []ScrapedCallback requestCount uint32 responseCount uint32 backend *httpBackend wg *sync.WaitGroup lock *sync.RWMutex } // RequestCallback is a type alias for OnRequest callback functions type RequestCallback func(*Request) // ResponseHeadersCallback is a type alias for OnResponseHeaders callback functions type ResponseHeadersCallback func(*Response) // ResponseCallback is a type alias for OnResponse callback functions type ResponseCallback func(*Response) // HTMLCallback is a type alias for OnHTML callback functions type HTMLCallback func(*HTMLElement) // XMLCallback is a type alias for OnXML callback functions type XMLCallback func(*XMLElement) // ErrorCallback is a type alias for OnError callback functions type ErrorCallback func(*Response, error) // ScrapedCallback is a type alias for OnScraped callback functions type ScrapedCallback func(*Response) // ProxyFunc is a type alias for proxy setter functions. type ProxyFunc func(*http.Request) (*url.URL, error) type htmlCallbackContainer struct { Selector string Function HTMLCallback } type xmlCallbackContainer struct { Query string Function XMLCallback } type cookieJarSerializer struct { store storage.Storage lock *sync.RWMutex } var collectorCounter uint32 // The key type is unexported to prevent collisions with context keys defined in // other packages. type key int // ProxyURLKey is the context key for the request proxy address. const ProxyURLKey key = iota var ( // ErrForbiddenDomain is the error thrown if visiting // a domain which is not allowed in AllowedDomains ErrForbiddenDomain = errors.New("Forbidden domain") // ErrMissingURL is the error type for missing URL errors ErrMissingURL = errors.New("Missing URL") // ErrMaxDepth is the error type for exceeding max depth ErrMaxDepth = errors.New("Max depth limit reached") // ErrForbiddenURL is the error thrown if visiting // a URL which is not allowed by URLFilters ErrForbiddenURL = errors.New("ForbiddenURL") // ErrNoURLFiltersMatch is the error thrown if visiting // a URL which is not allowed by URLFilters ErrNoURLFiltersMatch = errors.New("No URLFilters match") // ErrAlreadyVisited is the error type for already visited URLs ErrAlreadyVisited = errors.New("URL already visited") // ErrRobotsTxtBlocked is the error type for robots.txt errors ErrRobotsTxtBlocked = errors.New("URL blocked by robots.txt") // ErrNoCookieJar is the error type for missing cookie jar ErrNoCookieJar = errors.New("Cookie jar is not available") // ErrNoPattern is the error type for LimitRules without patterns ErrNoPattern = errors.New("No pattern defined in LimitRule") // ErrEmptyProxyURL is the error type for empty Proxy URL list ErrEmptyProxyURL = errors.New("Proxy URL list is empty") // ErrAbortedAfterHeaders is the error returned when OnResponseHeaders aborts the transfer. ErrAbortedAfterHeaders = errors.New("Aborted after receiving response headers") // ErrQueueFull is the error returned when the queue is full ErrQueueFull = errors.New("Queue MaxSize reached") ) var envMap = map[string]func(*Collector, string){ "ALLOWED_DOMAINS": func(c *Collector, val string) { c.AllowedDomains = strings.Split(val, ",") }, "CACHE_DIR": func(c *Collector, val string) { c.CacheDir = val }, "DETECT_CHARSET": func(c *Collector, val string) { c.DetectCharset = isYesString(val) }, "DISABLE_COOKIES": func(c *Collector, _ string) { c.backend.Client.Jar = nil }, "DISALLOWED_DOMAINS": func(c *Collector, val string) { c.DisallowedDomains = strings.Split(val, ",") }, "IGNORE_ROBOTSTXT": func(c *Collector, val string) { c.IgnoreRobotsTxt = isYesString(val) }, "FOLLOW_REDIRECTS": func(c *Collector, val string) { if !isYesString(val) { c.redirectHandler = func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse } } }, "MAX_BODY_SIZE": func(c *Collector, val string) { size, err := strconv.Atoi(val) if err == nil { c.MaxBodySize = size } }, "MAX_DEPTH": func(c *Collector, val string) { maxDepth, err := strconv.Atoi(val) if err == nil { c.MaxDepth = maxDepth } }, "PARSE_HTTP_ERROR_RESPONSE": func(c *Collector, val string) { c.ParseHTTPErrorResponse = isYesString(val) }, "TRACE_HTTP": func(c *Collector, val string) { c.TraceHTTP = isYesString(val) }, "USER_AGENT": func(c *Collector, val string) { c.UserAgent = val }, } // NewCollector creates a new Collector instance with default configuration func NewCollector(options ...CollectorOption) *Collector { c := &Collector{} c.Init() for _, f := range options { f(c) } c.parseSettingsFromEnv() return c } // UserAgent sets the user agent used by the Collector. func UserAgent(ua string) CollectorOption { return func(c *Collector) { c.UserAgent = ua } } // MaxDepth limits the recursion depth of visited URLs. func MaxDepth(depth int) CollectorOption { return func(c *Collector) { c.MaxDepth = depth } } // AllowedDomains sets the domain whitelist used by the Collector. func AllowedDomains(domains ...string) CollectorOption { return func(c *Collector) { c.AllowedDomains = domains } } // ParseHTTPErrorResponse allows parsing responses with HTTP errors func ParseHTTPErrorResponse() CollectorOption { return func(c *Collector) { c.ParseHTTPErrorResponse = true } } // DisallowedDomains sets the domain blacklist used by the Collector. func DisallowedDomains(domains ...string) CollectorOption { return func(c *Collector) { c.DisallowedDomains = domains } } // DisallowedURLFilters sets the list of regular expressions which restricts // visiting URLs. If any of the rules matches to a URL the request will be stopped. func DisallowedURLFilters(filters ...*regexp.Regexp) CollectorOption { return func(c *Collector) { c.DisallowedURLFilters = filters } } // URLFilters sets the list of regular expressions which restricts // visiting URLs. If any of the rules matches to a URL the request won't be stopped. func URLFilters(filters ...*regexp.Regexp) CollectorOption { return func(c *Collector) { c.URLFilters = filters } } // AllowURLRevisit instructs the Collector to allow multiple downloads of the same URL func AllowURLRevisit() CollectorOption { return func(c *Collector) { c.AllowURLRevisit = true } } // MaxBodySize sets the limit of the retrieved response body in bytes. func MaxBodySize(sizeInBytes int) CollectorOption { return func(c *Collector) { c.MaxBodySize = sizeInBytes } } // CacheDir specifies the location where GET requests are cached as files. func CacheDir(path string) CollectorOption { return func(c *Collector) { c.CacheDir = path } } // IgnoreRobotsTxt instructs the Collector to ignore any restrictions // set by the target host's robots.txt file. func IgnoreRobotsTxt() CollectorOption { return func(c *Collector) { c.IgnoreRobotsTxt = true } } // TraceHTTP instructs the Collector to collect and report request trace data // on the Response.Trace. func TraceHTTP() CollectorOption { return func(c *Collector) { c.TraceHTTP = true } } // ID sets the unique identifier of the Collector. func ID(id uint32) CollectorOption { return func(c *Collector) { c.ID = id } } // Async turns on asynchronous network requests. func Async(a ...bool) CollectorOption { return func(c *Collector) { c.Async = true } } // DetectCharset enables character encoding detection for non-utf8 response bodies // without explicit charset declaration. This feature uses https://github.com/saintfish/chardet func DetectCharset() CollectorOption { return func(c *Collector) { c.DetectCharset = true } } // Debugger sets the debugger used by the Collector. func Debugger(d debug.Debugger) CollectorOption { return func(c *Collector) { d.Init() c.debugger = d } } // CheckHead performs a HEAD request before every GET to pre-validate the response func CheckHead() CollectorOption { return func(c *Collector) { c.CheckHead = true } } // Init initializes the Collector's private variables and sets default // configuration for the Collector func (c *Collector) Init() { c.UserAgent = "colly - https://github.com/gocolly/colly/v2" c.MaxDepth = 0 c.store = &storage.InMemoryStorage{} c.store.Init() c.MaxBodySize = 10 * 1024 * 1024 c.backend = &httpBackend{} jar, _ := cookiejar.New(nil) c.backend.Init(jar) c.backend.Client.CheckRedirect = c.checkRedirectFunc() c.wg = &sync.WaitGroup{} c.lock = &sync.RWMutex{} c.robotsMap = make(map[string]*robotstxt.RobotsData) c.IgnoreRobotsTxt = true c.ID = atomic.AddUint32(&collectorCounter, 1) c.TraceHTTP = false } // Appengine will replace the Collector's backend http.Client // With an Http.Client that is provided by appengine/urlfetch // This function should be used when the scraper is run on // Google App Engine. Example: // func startScraper(w http.ResponseWriter, r *http.Request) { // ctx := appengine.NewContext(r) // c := colly.NewCollector() // c.Appengine(ctx) // ... // c.Visit("https://google.ca") // } func (c *Collector) Appengine(ctx context.Context) { client := urlfetch.Client(ctx) client.Jar = c.backend.Client.Jar client.CheckRedirect = c.backend.Client.CheckRedirect client.Timeout = c.backend.Client.Timeout c.backend.Client = client } // Visit starts Collector's collecting job by creating a // request to the URL specified in parameter. // Visit also calls the previously provided callbacks func (c *Collector) Visit(URL string) error { if c.CheckHead { if check := c.scrape(URL, "HEAD", 1, nil, nil, nil, true); check != nil { return check } } return c.scrape(URL, "GET", 1, nil, nil, nil, true) } // HasVisited checks if the provided URL has been visited func (c *Collector) HasVisited(URL string) (bool, error) { return c.checkHasVisited(URL, nil) } // HasPosted checks if the provided URL and requestData has been visited // This method is useful more likely to prevent re-visit same URL and POST body func (c *Collector) HasPosted(URL string, requestData map[string]string) (bool, error) { return c.checkHasVisited(URL, requestData) } // Head starts a collector job by creating a HEAD request. func (c *Collector) Head(URL string) error { return c.scrape(URL, "HEAD", 1, nil, nil, nil, false) } // Post starts a collector job by creating a POST request. // Post also calls the previously provided callbacks func (c *Collector) Post(URL string, requestData map[string]string) error { return c.scrape(URL, "POST", 1, createFormReader(requestData), nil, nil, true) } // PostRaw starts a collector job by creating a POST request with raw binary data. // Post also calls the previously provided callbacks func (c *Collector) PostRaw(URL string, requestData []byte) error { return c.scrape(URL, "POST", 1, bytes.NewReader(requestData), nil, nil, true) } // PostMultipart starts a collector job by creating a Multipart POST request // with raw binary data. PostMultipart also calls the previously provided callbacks func (c *Collector) PostMultipart(URL string, requestData map[string][]byte) error { boundary := randomBoundary() hdr := http.Header{} hdr.Set("Content-Type", "multipart/form-data; boundary="+boundary) hdr.Set("User-Agent", c.UserAgent) return c.scrape(URL, "POST", 1, createMultipartReader(boundary, requestData), nil, hdr, true) } // Request starts a collector job by creating a custom HTTP request // where method, context, headers and request data can be specified. // Set requestData, ctx, hdr parameters to nil if you don't want to use them. // Valid methods: // - "GET" // - "HEAD" // - "POST" // - "PUT" // - "DELETE" // - "PATCH" // - "OPTIONS" func (c *Collector) Request(method, URL string, requestData io.Reader, ctx *Context, hdr http.Header) error { return c.scrape(URL, method, 1, requestData, ctx, hdr, true) } // SetDebugger attaches a debugger to the collector func (c *Collector) SetDebugger(d debug.Debugger) { d.Init() c.debugger = d } // UnmarshalRequest creates a Request from serialized data func (c *Collector) UnmarshalRequest(r []byte) (*Request, error) { req := &serializableRequest{} err := json.Unmarshal(r, req) if err != nil { return nil, err } u, err := url.Parse(req.URL) if err != nil { return nil, err } ctx := NewContext() for k, v := range req.Ctx { ctx.Put(k, v) } return &Request{ Method: req.Method, URL: u, Depth: req.Depth, Body: bytes.NewReader(req.Body), Ctx: ctx, ID: atomic.AddUint32(&c.requestCount, 1), Headers: &req.Headers, collector: c, }, nil } func (c *Collector) scrape(u, method string, depth int, requestData io.Reader, ctx *Context, hdr http.Header, checkRevisit bool) error { parsedURL, err := url.Parse(u) if err != nil { return err } if err := c.requestCheck(u, parsedURL, method, requestData, depth, checkRevisit); err != nil { return err } if hdr == nil { hdr = http.Header{"User-Agent": []string{c.UserAgent}} } rc, ok := requestData.(io.ReadCloser) if !ok && requestData != nil { rc = ioutil.NopCloser(requestData) } // The Go HTTP API ignores "Host" in the headers, preferring the client // to use the Host field on Request. host := parsedURL.Host if hostHeader := hdr.Get("Host"); hostHeader != "" { host = hostHeader } req := &http.Request{ Method: method, URL: parsedURL, Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1, Header: hdr, Body: rc, Host: host, } setRequestBody(req, requestData) u = parsedURL.String() c.wg.Add(1) if c.Async { go c.fetch(u, method, depth, requestData, ctx, hdr, req) return nil } return c.fetch(u, method, depth, requestData, ctx, hdr, req) } func setRequestBody(req *http.Request, body io.Reader) { if body != nil { switch v := body.(type) { case *bytes.Buffer: req.ContentLength = int64(v.Len()) buf := v.Bytes() req.GetBody = func() (io.ReadCloser, error) { r := bytes.NewReader(buf) return ioutil.NopCloser(r), nil } case *bytes.Reader: req.ContentLength = int64(v.Len()) snapshot := *v req.GetBody = func() (io.ReadCloser, error) { r := snapshot return ioutil.NopCloser(&r), nil } case *strings.Reader: req.ContentLength = int64(v.Len()) snapshot := *v req.GetBody = func() (io.ReadCloser, error) { r := snapshot return ioutil.NopCloser(&r), nil } } if req.GetBody != nil && req.ContentLength == 0 { req.Body = http.NoBody req.GetBody = func() (io.ReadCloser, error) { return http.NoBody, nil } } } } func (c *Collector) fetch(u, method string, depth int, requestData io.Reader, ctx *Context, hdr http.Header, req *http.Request) error { defer c.wg.Done() if ctx == nil { ctx = NewContext() } request := &Request{ URL: req.URL, Headers: &req.Header, Ctx: ctx, Depth: depth, Method: method, Body: requestData, collector: c, ID: atomic.AddUint32(&c.requestCount, 1), } c.handleOnRequest(request) if request.abort { return nil } if method == "POST" && req.Header.Get("Content-Type") == "" { req.Header.Add("Content-Type", "application/x-www-form-urlencoded") } if req.Header.Get("Accept") == "" { req.Header.Set("Accept", "*/*") } var hTrace *HTTPTrace if c.TraceHTTP { hTrace = &HTTPTrace{} req = hTrace.WithTrace(req) } checkHeadersFunc := func(statusCode int, headers http.Header) bool { c.handleOnResponseHeaders(&Response{Ctx: ctx, Request: request, StatusCode: statusCode, Headers: &headers}) return !request.abort } origURL := req.URL response, err := c.backend.Cache(req, c.MaxBodySize, checkHeadersFunc, c.CacheDir) if proxyURL, ok := req.Context().Value(ProxyURLKey).(string); ok { request.ProxyURL = proxyURL } if err := c.handleOnError(response, err, request, ctx); err != nil { return err } if req.URL != origURL { request.URL = req.URL request.Headers = &req.Header } atomic.AddUint32(&c.responseCount, 1) response.Ctx = ctx response.Request = request response.Trace = hTrace err = response.fixCharset(c.DetectCharset, request.ResponseCharacterEncoding) if err != nil { return err } c.handleOnResponse(response) err = c.handleOnHTML(response) if err != nil { c.handleOnError(response, err, request, ctx) } err = c.handleOnXML(response) if err != nil { c.handleOnError(response, err, request, ctx) } c.handleOnScraped(response) return err } func (c *Collector) requestCheck(u string, parsedURL *url.URL, method string, requestData io.Reader, depth int, checkRevisit bool) error { if u == "" { return ErrMissingURL } if c.MaxDepth > 0 && c.MaxDepth < depth { return ErrMaxDepth } if len(c.DisallowedURLFilters) > 0 { if isMatchingFilter(c.DisallowedURLFilters, []byte(u)) { return ErrForbiddenURL } } if len(c.URLFilters) > 0 { if !isMatchingFilter(c.URLFilters, []byte(u)) { return ErrNoURLFiltersMatch } } if !c.isDomainAllowed(parsedURL.Hostname()) { return ErrForbiddenDomain } if method != "HEAD" && !c.IgnoreRobotsTxt { if err := c.checkRobots(parsedURL); err != nil { return err } } if checkRevisit && !c.AllowURLRevisit { h := fnv.New64a() h.Write([]byte(u)) var uHash uint64 if method == "GET" { uHash = h.Sum64() } else if requestData != nil { h.Write(streamToByte(requestData)) uHash = h.Sum64() } else { return nil } visited, err := c.store.IsVisited(uHash) if err != nil { return err } if visited { return ErrAlreadyVisited } return c.store.Visited(uHash) } return nil } func (c *Collector) isDomainAllowed(domain string) bool { for _, d2 := range c.DisallowedDomains { if d2 == domain { return false } } if c.AllowedDomains == nil || len(c.AllowedDomains) == 0 { return true } for _, d2 := range c.AllowedDomains { if d2 == domain { return true } } return false } func (c *Collector) checkRobots(u *url.URL) error { c.lock.RLock() robot, ok := c.robotsMap[u.Host] c.lock.RUnlock() if !ok { // no robots file cached resp, err := c.backend.Client.Get(u.Scheme + "://" + u.Host + "/robots.txt") if err != nil { return err } defer resp.Body.Close() robot, err = robotstxt.FromResponse(resp) if err != nil { return err } c.lock.Lock() c.robotsMap[u.Host] = robot c.lock.Unlock() } uaGroup := robot.FindGroup(c.UserAgent) if uaGroup == nil { return nil } eu := u.EscapedPath() if u.RawQuery != "" { eu += "?" + u.Query().Encode() } if !uaGroup.Test(eu) { return ErrRobotsTxtBlocked } return nil } // String is the text representation of the collector. // It contains useful debug information about the collector's internals func (c *Collector) String() string { return fmt.Sprintf( "Requests made: %d (%d responses) | Callbacks: OnRequest: %d, OnHTML: %d, OnResponse: %d, OnError: %d", c.requestCount, c.responseCount, len(c.requestCallbacks), len(c.htmlCallbacks), len(c.responseCallbacks), len(c.errorCallbacks), ) } // Wait returns when the collector jobs are finished func (c *Collector) Wait() { c.wg.Wait() } // OnRequest registers a function. Function will be executed on every // request made by the Collector func (c *Collector) OnRequest(f RequestCallback) { c.lock.Lock() if c.requestCallbacks == nil { c.requestCallbacks = make([]RequestCallback, 0, 4) } c.requestCallbacks = append(c.requestCallbacks, f) c.lock.Unlock() } // OnResponseHeaders registers a function. Function will be executed on every response // when headers and status are already received, but body is not yet read. // // Like in OnRequest, you can call Request.Abort to abort the transfer. This might be // useful if, for example, you're following all hyperlinks, but want to avoid // downloading files. // // Be aware that using this will prevent HTTP/1.1 connection reuse, as // the only way to abort a download is to immediately close the connection. // HTTP/2 doesn't suffer from this problem, as it's possible to close // specific stream inside the connection. func (c *Collector) OnResponseHeaders(f ResponseHeadersCallback) { c.lock.Lock() c.responseHeadersCallbacks = append(c.responseHeadersCallbacks, f) c.lock.Unlock() } // OnResponse registers a function. Function will be executed on every response func (c *Collector) OnResponse(f ResponseCallback) { c.lock.Lock() if c.responseCallbacks == nil { c.responseCallbacks = make([]ResponseCallback, 0, 4) } c.responseCallbacks = append(c.responseCallbacks, f) c.lock.Unlock() } // OnHTML registers a function. Function will be executed on every HTML // element matched by the GoQuery Selector parameter. // GoQuery Selector is a selector used by https://github.com/PuerkitoBio/goquery func (c *Collector) OnHTML(goquerySelector string, f HTMLCallback) { c.lock.Lock() if c.htmlCallbacks == nil { c.htmlCallbacks = make([]*htmlCallbackContainer, 0, 4) } c.htmlCallbacks = append(c.htmlCallbacks, &htmlCallbackContainer{ Selector: goquerySelector, Function: f, }) c.lock.Unlock() } // OnXML registers a function. Function will be executed on every XML // element matched by the xpath Query parameter. // xpath Query is used by https://github.com/antchfx/xmlquery func (c *Collector) OnXML(xpathQuery string, f XMLCallback) { c.lock.Lock() if c.xmlCallbacks == nil { c.xmlCallbacks = make([]*xmlCallbackContainer, 0, 4) } c.xmlCallbacks = append(c.xmlCallbacks, &xmlCallbackContainer{ Query: xpathQuery, Function: f, }) c.lock.Unlock() } // OnHTMLDetach deregister a function. Function will not be execute after detached func (c *Collector) OnHTMLDetach(goquerySelector string) { c.lock.Lock() deleteIdx := -1 for i, cc := range c.htmlCallbacks { if cc.Selector == goquerySelector { deleteIdx = i break } } if deleteIdx != -1 { c.htmlCallbacks = append(c.htmlCallbacks[:deleteIdx], c.htmlCallbacks[deleteIdx+1:]...) } c.lock.Unlock() } // OnXMLDetach deregister a function. Function will not be execute after detached func (c *Collector) OnXMLDetach(xpathQuery string) { c.lock.Lock() deleteIdx := -1 for i, cc := range c.xmlCallbacks { if cc.Query == xpathQuery { deleteIdx = i break } } if deleteIdx != -1 { c.xmlCallbacks = append(c.xmlCallbacks[:deleteIdx], c.xmlCallbacks[deleteIdx+1:]...) } c.lock.Unlock() } // OnError registers a function. Function will be executed if an error // occurs during the HTTP request. func (c *Collector) OnError(f ErrorCallback) { c.lock.Lock() if c.errorCallbacks == nil { c.errorCallbacks = make([]ErrorCallback, 0, 4) } c.errorCallbacks = append(c.errorCallbacks, f) c.lock.Unlock() } // OnScraped registers a function. Function will be executed after // OnHTML, as a final part of the scraping. func (c *Collector) OnScraped(f ScrapedCallback) { c.lock.Lock() if c.scrapedCallbacks == nil { c.scrapedCallbacks = make([]ScrapedCallback, 0, 4) } c.scrapedCallbacks = append(c.scrapedCallbacks, f) c.lock.Unlock() } // SetClient will override the previously set http.Client func (c *Collector) SetClient(client *http.Client) { c.backend.Client = client } // WithTransport allows you to set a custom http.RoundTripper (transport) func (c *Collector) WithTransport(transport http.RoundTripper) { c.backend.Client.Transport = transport } // DisableCookies turns off cookie handling func (c *Collector) DisableCookies() { c.backend.Client.Jar = nil } // SetCookieJar overrides the previously set cookie jar func (c *Collector) SetCookieJar(j http.CookieJar) { c.backend.Client.Jar = j } // SetRequestTimeout overrides the default timeout (10 seconds) for this collector func (c *Collector) SetRequestTimeout(timeout time.Duration) { c.backend.Client.Timeout = timeout } // SetStorage overrides the default in-memory storage. // Storage stores scraping related data like cookies and visited urls func (c *Collector) SetStorage(s storage.Storage) error { if err := s.Init(); err != nil { return err } c.store = s c.backend.Client.Jar = createJar(s) return nil } // SetProxy sets a proxy for the collector. This method overrides the previously // used http.Transport if the type of the transport is not http.RoundTripper. // The proxy type is determined by the URL scheme. "http" // and "socks5" are supported. If the scheme is empty, // "http" is assumed. func (c *Collector) SetProxy(proxyURL string) error { proxyParsed, err := url.Parse(proxyURL) if err != nil { return err } c.SetProxyFunc(http.ProxyURL(proxyParsed)) return nil } // SetProxyFunc sets a custom proxy setter/switcher function. // See built-in ProxyFuncs for more details. // This method overrides the previously used http.Transport // if the type of the transport is not http.RoundTripper. // The proxy type is determined by the URL scheme. "http" // and "socks5" are supported. If the scheme is empty, // "http" is assumed. func (c *Collector) SetProxyFunc(p ProxyFunc) { t, ok := c.backend.Client.Transport.(*http.Transport) if c.backend.Client.Transport != nil && ok { t.Proxy = p } else { c.backend.Client.Transport = &http.Transport{ Proxy: p, } } } func createEvent(eventType string, requestID, collectorID uint32, kvargs map[string]string) *debug.Event { return &debug.Event{ CollectorID: collectorID, RequestID: requestID, Type: eventType, Values: kvargs, } } func (c *Collector) handleOnRequest(r *Request) { if c.debugger != nil { c.debugger.Event(createEvent("request", r.ID, c.ID, map[string]string{ "url": r.URL.String(), })) } for _, f := range c.requestCallbacks { f(r) } } func (c *Collector) handleOnResponse(r *Response) { if c.debugger != nil { c.debugger.Event(createEvent("response", r.Request.ID, c.ID, map[string]string{ "url": r.Request.URL.String(), "status": http.StatusText(r.StatusCode), })) } for _, f := range c.responseCallbacks { f(r) } } func (c *Collector) handleOnResponseHeaders(r *Response) { if c.debugger != nil { c.debugger.Event(createEvent("responseHeaders", r.Request.ID, c.ID, map[string]string{ "url": r.Request.URL.String(), "status": http.StatusText(r.StatusCode), })) } for _, f := range c.responseHeadersCallbacks { f(r) } } func (c *Collector) handleOnHTML(resp *Response) error { if len(c.htmlCallbacks) == 0 || !strings.Contains(strings.ToLower(resp.Headers.Get("Content-Type")), "html") { return nil } doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(resp.Body)) if err != nil { return err } if href, found := doc.Find("base[href]").Attr("href"); found { resp.Request.baseURL, _ = resp.Request.URL.Parse(href) } for _, cc := range c.htmlCallbacks { i := 0 doc.Find(cc.Selector).Each(func(_ int, s *goquery.Selection) { for _, n := range s.Nodes { e := NewHTMLElementFromSelectionNode(resp, s, n, i) i++ if c.debugger != nil { c.debugger.Event(createEvent("html", resp.Request.ID, c.ID, map[string]string{ "selector": cc.Selector, "url": resp.Request.URL.String(), })) } cc.Function(e) } }) } return nil } func (c *Collector) handleOnXML(resp *Response) error { if len(c.xmlCallbacks) == 0 { return nil } contentType := strings.ToLower(resp.Headers.Get("Content-Type")) isXMLFile := strings.HasSuffix(strings.ToLower(resp.Request.URL.Path), ".xml") || strings.HasSuffix(strings.ToLower(resp.Request.URL.Path), ".xml.gz") if !strings.Contains(contentType, "html") && (!strings.Contains(contentType, "xml") && !isXMLFile) { return nil } if strings.Contains(contentType, "html") { doc, err := htmlquery.Parse(bytes.NewBuffer(resp.Body)) if err != nil { return err } if e := htmlquery.FindOne(doc, "//base"); e != nil { for _, a := range e.Attr { if a.Key == "href" { resp.Request.baseURL, _ = resp.Request.URL.Parse(a.Val) break } } } for _, cc := range c.xmlCallbacks { for _, n := range htmlquery.Find(doc, cc.Query) { e := NewXMLElementFromHTMLNode(resp, n) if c.debugger != nil { c.debugger.Event(createEvent("xml", resp.Request.ID, c.ID, map[string]string{ "selector": cc.Query, "url": resp.Request.URL.String(), })) } cc.Function(e) } } } else if strings.Contains(contentType, "xml") || isXMLFile { doc, err := xmlquery.Parse(bytes.NewBuffer(resp.Body)) if err != nil { return err } for _, cc := range c.xmlCallbacks { xmlquery.FindEach(doc, cc.Query, func(i int, n *xmlquery.Node) { e := NewXMLElementFromXMLNode(resp, n) if c.debugger != nil { c.debugger.Event(createEvent("xml", resp.Request.ID, c.ID, map[string]string{ "selector": cc.Query, "url": resp.Request.URL.String(), })) } cc.Function(e) }) } } return nil } func (c *Collector) handleOnError(response *Response, err error, request *Request, ctx *Context) error { if err == nil && (c.ParseHTTPErrorResponse || response.StatusCode < 203) { return nil } if err == nil && response.StatusCode >= 203 { err = errors.New(http.StatusText(response.StatusCode)) } if response == nil { response = &Response{ Request: request, Ctx: ctx, } } if c.debugger != nil { c.debugger.Event(createEvent("error", request.ID, c.ID, map[string]string{ "url": request.URL.String(), "status": http.StatusText(response.StatusCode), })) } if response.Request == nil { response.Request = request } if response.Ctx == nil { response.Ctx = request.Ctx } for _, f := range c.errorCallbacks { f(response, err) } return err } func (c *Collector) handleOnScraped(r *Response) { if c.debugger != nil { c.debugger.Event(createEvent("scraped", r.Request.ID, c.ID, map[string]string{ "url": r.Request.URL.String(), })) } for _, f := range c.scrapedCallbacks { f(r) } } // Limit adds a new LimitRule to the collector func (c *Collector) Limit(rule *LimitRule) error { return c.backend.Limit(rule) } // Limits adds new LimitRules to the collector func (c *Collector) Limits(rules []*LimitRule) error { return c.backend.Limits(rules) } // SetRedirectHandler instructs the Collector to allow multiple downloads of the same URL func (c *Collector) SetRedirectHandler(f func(req *http.Request, via []*http.Request) error) { c.redirectHandler = f c.backend.Client.CheckRedirect = c.checkRedirectFunc() } // SetCookies handles the receipt of the cookies in a reply for the given URL func (c *Collector) SetCookies(URL string, cookies []*http.Cookie) error { if c.backend.Client.Jar == nil { return ErrNoCookieJar } u, err := url.Parse(URL) if err != nil { return err } c.backend.Client.Jar.SetCookies(u, cookies) return nil } // Cookies returns the cookies to send in a request for the given URL. func (c *Collector) Cookies(URL string) []*http.Cookie { if c.backend.Client.Jar == nil { return nil } u, err := url.Parse(URL) if err != nil { return nil } return c.backend.Client.Jar.Cookies(u) } // Clone creates an exact copy of a Collector without callbacks. // HTTP backend, robots.txt cache and cookie jar are shared // between collectors. func (c *Collector) Clone() *Collector { return &Collector{ AllowedDomains: c.AllowedDomains, AllowURLRevisit: c.AllowURLRevisit, CacheDir: c.CacheDir, DetectCharset: c.DetectCharset, DisallowedDomains: c.DisallowedDomains, ID: atomic.AddUint32(&collectorCounter, 1), IgnoreRobotsTxt: c.IgnoreRobotsTxt, MaxBodySize: c.MaxBodySize, MaxDepth: c.MaxDepth, DisallowedURLFilters: c.DisallowedURLFilters, URLFilters: c.URLFilters, CheckHead: c.CheckHead, ParseHTTPErrorResponse: c.ParseHTTPErrorResponse, UserAgent: c.UserAgent, TraceHTTP: c.TraceHTTP, store: c.store, backend: c.backend, debugger: c.debugger, Async: c.Async, redirectHandler: c.redirectHandler, errorCallbacks: make([]ErrorCallback, 0, 8), htmlCallbacks: make([]*htmlCallbackContainer, 0, 8), xmlCallbacks: make([]*xmlCallbackContainer, 0, 8), scrapedCallbacks: make([]ScrapedCallback, 0, 8), lock: c.lock, requestCallbacks: make([]RequestCallback, 0, 8), responseCallbacks: make([]ResponseCallback, 0, 8), robotsMap: c.robotsMap, wg: &sync.WaitGroup{}, } } func (c *Collector) checkRedirectFunc() func(req *http.Request, via []*http.Request) error { return func(req *http.Request, via []*http.Request) error { if !c.isDomainAllowed(req.URL.Hostname()) { return fmt.Errorf("Not following redirect to %s because its not in AllowedDomains", req.URL.Host) } if c.redirectHandler != nil { return c.redirectHandler(req, via) } // Honor golangs default of maximum of 10 redirects if len(via) >= 10 { return http.ErrUseLastResponse } lastRequest := via[len(via)-1] // If domain has changed, remove the Authorization-header if it exists if req.URL.Host != lastRequest.URL.Host { req.Header.Del("Authorization") } return nil } } func (c *Collector) parseSettingsFromEnv() { for _, e := range os.Environ() { if !strings.HasPrefix(e, "COLLY_") { continue } pair := strings.SplitN(e[6:], "=", 2) if f, ok := envMap[pair[0]]; ok { f(c, pair[1]) } else { log.Println("Unknown environment variable:", pair[0]) } } } func (c *Collector) checkHasVisited(URL string, requestData map[string]string) (bool, error) { h := fnv.New64a() h.Write([]byte(URL)) if requestData != nil { h.Write(streamToByte(createFormReader(requestData))) } return c.store.IsVisited(h.Sum64()) } // SanitizeFileName replaces dangerous characters in a string // so the return value can be used as a safe file name. func SanitizeFileName(fileName string) string { ext := filepath.Ext(fileName) cleanExt := sanitize.BaseName(ext) if cleanExt == "" { cleanExt = ".unknown" } return strings.Replace(fmt.Sprintf( "%s.%s", sanitize.BaseName(fileName[:len(fileName)-len(ext)]), cleanExt[1:], ), "-", "_", -1) } func createFormReader(data map[string]string) io.Reader { form := url.Values{} for k, v := range data { form.Add(k, v) } return strings.NewReader(form.Encode()) } func createMultipartReader(boundary string, data map[string][]byte) io.Reader { dashBoundary := "--" + boundary body := []byte{} buffer := bytes.NewBuffer(body) buffer.WriteString("Content-type: multipart/form-data; boundary=" + boundary + "\n\n") for contentType, content := range data { buffer.WriteString(dashBoundary + "\n") buffer.WriteString("Content-Disposition: form-data; name=" + contentType + "\n") buffer.WriteString(fmt.Sprintf("Content-Length: %d \n\n", len(content))) buffer.Write(content) buffer.WriteString("\n") } buffer.WriteString(dashBoundary + "--\n\n") return buffer } // randomBoundary was borrowed from // github.com/golang/go/mime/multipart/writer.go#randomBoundary func randomBoundary() string { var buf [30]byte _, err := io.ReadFull(rand.Reader, buf[:]) if err != nil { panic(err) } return fmt.Sprintf("%x", buf[:]) } func isYesString(s string) bool { switch strings.ToLower(s) { case "1", "yes", "true", "y": return true } return false } func createJar(s storage.Storage) http.CookieJar { return &cookieJarSerializer{store: s, lock: &sync.RWMutex{}} } func (j *cookieJarSerializer) SetCookies(u *url.URL, cookies []*http.Cookie) { j.lock.Lock() defer j.lock.Unlock() cookieStr := j.store.Cookies(u) // Merge existing cookies, new cookies have precedence. cnew := make([]*http.Cookie, len(cookies)) copy(cnew, cookies) existing := storage.UnstringifyCookies(cookieStr) for _, c := range existing { if !storage.ContainsCookie(cnew, c.Name) { cnew = append(cnew, c) } } j.store.SetCookies(u, storage.StringifyCookies(cnew)) } func (j *cookieJarSerializer) Cookies(u *url.URL) []*http.Cookie { cookies := storage.UnstringifyCookies(j.store.Cookies(u)) // Filter. now := time.Now() cnew := make([]*http.Cookie, 0, len(cookies)) for _, c := range cookies { // Drop expired cookies. if c.RawExpires != "" && c.Expires.Before(now) { continue } // Drop secure cookies if not over https. if c.Secure && u.Scheme != "https" { continue } cnew = append(cnew, c) } return cnew } func isMatchingFilter(fs []*regexp.Regexp, d []byte) bool { for _, r := range fs { if r.Match(d) { return true } } return false } func streamToByte(r io.Reader) []byte { buf := new(bytes.Buffer) buf.ReadFrom(r) if strReader, k := r.(*strings.Reader); k { strReader.Seek(0, 0) } else if bReader, kb := r.(*bytes.Reader); kb { bReader.Seek(0, 0) } return buf.Bytes() } ================================================ FILE: vendor/github.com/gocolly/colly/v2/context.go ================================================ // Copyright 2018 Adam Tauber // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package colly import ( "sync" ) // Context provides a tiny layer for passing data between callbacks type Context struct { contextMap map[string]interface{} lock *sync.RWMutex } // NewContext initializes a new Context instance func NewContext() *Context { return &Context{ contextMap: make(map[string]interface{}), lock: &sync.RWMutex{}, } } // UnmarshalBinary decodes Context value to nil // This function is used by request caching func (c *Context) UnmarshalBinary(_ []byte) error { return nil } // MarshalBinary encodes Context value // This function is used by request caching func (c *Context) MarshalBinary() (_ []byte, _ error) { return nil, nil } // Put stores a value of any type in Context func (c *Context) Put(key string, value interface{}) { c.lock.Lock() c.contextMap[key] = value c.lock.Unlock() } // Get retrieves a string value from Context. // Get returns an empty string if key not found func (c *Context) Get(key string) string { c.lock.RLock() defer c.lock.RUnlock() if v, ok := c.contextMap[key]; ok { return v.(string) } return "" } // GetAny retrieves a value from Context. // GetAny returns nil if key not found func (c *Context) GetAny(key string) interface{} { c.lock.RLock() defer c.lock.RUnlock() if v, ok := c.contextMap[key]; ok { return v } return nil } // ForEach iterate context func (c *Context) ForEach(fn func(k string, v interface{}) interface{}) []interface{} { c.lock.RLock() defer c.lock.RUnlock() ret := make([]interface{}, 0, len(c.contextMap)) for k, v := range c.contextMap { ret = append(ret, fn(k, v)) } return ret } ================================================ FILE: vendor/github.com/gocolly/colly/v2/debug/debug.go ================================================ // Copyright 2018 Adam Tauber // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package debug // Event represents an action inside a collector type Event struct { // Type is the type of the event Type string // RequestID identifies the HTTP request of the Event RequestID uint32 // CollectorID identifies the collector of the Event CollectorID uint32 // Values contains the event's key-value pairs. Different type of events // can return different key-value pairs Values map[string]string } // Debugger is an interface for different type of debugging backends type Debugger interface { // Init initializes the backend Init() error // Event receives a new collector event. Event(e *Event) } ================================================ FILE: vendor/github.com/gocolly/colly/v2/debug/logdebugger.go ================================================ // Copyright 2018 Adam Tauber // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package debug import ( "io" "log" "os" "sync/atomic" "time" ) // LogDebugger is the simplest debugger which prints log messages to the STDERR type LogDebugger struct { // Output is the log destination, anything can be used which implements them // io.Writer interface. Leave it blank to use STDERR Output io.Writer // Prefix appears at the beginning of each generated log line Prefix string // Flag defines the logging properties. Flag int logger *log.Logger counter int32 start time.Time } // Init initializes the LogDebugger func (l *LogDebugger) Init() error { l.counter = 0 l.start = time.Now() if l.Output == nil { l.Output = os.Stderr } l.logger = log.New(l.Output, l.Prefix, l.Flag) return nil } // Event receives Collector events and prints them to STDERR func (l *LogDebugger) Event(e *Event) { i := atomic.AddInt32(&l.counter, 1) l.logger.Printf("[%06d] %d [%6d - %s] %q (%s)\n", i, e.CollectorID, e.RequestID, e.Type, e.Values, time.Since(l.start)) } ================================================ FILE: vendor/github.com/gocolly/colly/v2/debug/webdebugger.go ================================================ // Copyright 2018 Adam Tauber // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package debug import ( "encoding/json" "log" "net/http" "sync" "time" ) // WebDebugger is a web based debuging frontend for colly type WebDebugger struct { // Address is the address of the web server. It is 127.0.0.1:7676 by default. Address string initialized bool CurrentRequests map[uint32]requestInfo RequestLog []requestInfo sync.Mutex } type requestInfo struct { URL string Started time.Time Duration time.Duration ResponseStatus string ID uint32 CollectorID uint32 } // Init initializes the WebDebugger func (w *WebDebugger) Init() error { if w.initialized { return nil } defer func() { w.initialized = true }() if w.Address == "" { w.Address = "127.0.0.1:7676" } w.RequestLog = make([]requestInfo, 0) w.CurrentRequests = make(map[uint32]requestInfo) http.HandleFunc("/", w.indexHandler) http.HandleFunc("/status", w.statusHandler) log.Println("Starting debug webserver on", w.Address) go http.ListenAndServe(w.Address, nil) return nil } // Event updates the debugger's status func (w *WebDebugger) Event(e *Event) { w.Lock() defer w.Unlock() switch e.Type { case "request": w.CurrentRequests[e.RequestID] = requestInfo{ URL: e.Values["url"], Started: time.Now(), ID: e.RequestID, CollectorID: e.CollectorID, } case "response", "error": r := w.CurrentRequests[e.RequestID] r.Duration = time.Since(r.Started) r.ResponseStatus = e.Values["status"] w.RequestLog = append(w.RequestLog, r) delete(w.CurrentRequests, e.RequestID) } } func (w *WebDebugger) indexHandler(wr http.ResponseWriter, r *http.Request) { wr.Write([]byte(` Colly Debugger WebUI

Current Requests

Finished Requests

`)) } func (w *WebDebugger) statusHandler(wr http.ResponseWriter, r *http.Request) { w.Lock() jsonData, err := json.MarshalIndent(w, "", " ") w.Unlock() if err != nil { panic(err) } wr.Write(jsonData) } ================================================ FILE: vendor/github.com/gocolly/colly/v2/htmlelement.go ================================================ // Copyright 2018 Adam Tauber // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package colly import ( "strings" "github.com/PuerkitoBio/goquery" "golang.org/x/net/html" ) // HTMLElement is the representation of a HTML tag. type HTMLElement struct { // Name is the name of the tag Name string Text string attributes []html.Attribute // Request is the request object of the element's HTML document Request *Request // Response is the Response object of the element's HTML document Response *Response // DOM is the goquery parsed DOM object of the page. DOM is relative // to the current HTMLElement DOM *goquery.Selection // Index stores the position of the current element within all the elements matched by an OnHTML callback Index int } // NewHTMLElementFromSelectionNode creates a HTMLElement from a goquery.Selection Node. func NewHTMLElementFromSelectionNode(resp *Response, s *goquery.Selection, n *html.Node, idx int) *HTMLElement { return &HTMLElement{ Name: n.Data, Request: resp.Request, Response: resp, Text: goquery.NewDocumentFromNode(n).Text(), DOM: s, Index: idx, attributes: n.Attr, } } // Attr returns the selected attribute of a HTMLElement or empty string // if no attribute found func (h *HTMLElement) Attr(k string) string { for _, a := range h.attributes { if a.Key == k { return a.Val } } return "" } // ChildText returns the concatenated and stripped text content of the matching // elements. func (h *HTMLElement) ChildText(goquerySelector string) string { return strings.TrimSpace(h.DOM.Find(goquerySelector).Text()) } // ChildTexts returns the stripped text content of all the matching // elements. func (h *HTMLElement) ChildTexts(goquerySelector string) []string { var res []string h.DOM.Find(goquerySelector).Each(func(_ int, s *goquery.Selection) { res = append(res, strings.TrimSpace(s.Text())) }) return res } // ChildAttr returns the stripped text content of the first matching // element's attribute. func (h *HTMLElement) ChildAttr(goquerySelector, attrName string) string { if attr, ok := h.DOM.Find(goquerySelector).Attr(attrName); ok { return strings.TrimSpace(attr) } return "" } // ChildAttrs returns the stripped text content of all the matching // element's attributes. func (h *HTMLElement) ChildAttrs(goquerySelector, attrName string) []string { var res []string h.DOM.Find(goquerySelector).Each(func(_ int, s *goquery.Selection) { if attr, ok := s.Attr(attrName); ok { res = append(res, strings.TrimSpace(attr)) } }) return res } // ForEach iterates over the elements matched by the first argument // and calls the callback function on every HTMLElement match. func (h *HTMLElement) ForEach(goquerySelector string, callback func(int, *HTMLElement)) { i := 0 h.DOM.Find(goquerySelector).Each(func(_ int, s *goquery.Selection) { for _, n := range s.Nodes { callback(i, NewHTMLElementFromSelectionNode(h.Response, s, n, i)) i++ } }) } // ForEachWithBreak iterates over the elements matched by the first argument // and calls the callback function on every HTMLElement match. // It is identical to ForEach except that it is possible to break // out of the loop by returning false in the callback function. It returns the // current Selection object. func (h *HTMLElement) ForEachWithBreak(goquerySelector string, callback func(int, *HTMLElement) bool) { i := 0 h.DOM.Find(goquerySelector).EachWithBreak(func(_ int, s *goquery.Selection) bool { for _, n := range s.Nodes { if callback(i, NewHTMLElementFromSelectionNode(h.Response, s, n, i)) { i++ return true } } return false }) } ================================================ FILE: vendor/github.com/gocolly/colly/v2/http_backend.go ================================================ // Copyright 2018 Adam Tauber // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package colly import ( "crypto/sha1" "encoding/gob" "encoding/hex" "io" "io/ioutil" "math/rand" "net/http" "os" "path" "regexp" "strings" "sync" "time" "compress/gzip" "github.com/gobwas/glob" ) type httpBackend struct { LimitRules []*LimitRule Client *http.Client lock *sync.RWMutex } type checkHeadersFunc func(statusCode int, header http.Header) bool // LimitRule provides connection restrictions for domains. // Both DomainRegexp and DomainGlob can be used to specify // the included domains patterns, but at least one is required. // There can be two kind of limitations: // - Parallelism: Set limit for the number of concurrent requests to matching domains // - Delay: Wait specified amount of time between requests (parallelism is 1 in this case) type LimitRule struct { // DomainRegexp is a regular expression to match against domains DomainRegexp string // DomainGlob is a glob pattern to match against domains DomainGlob string // Delay is the duration to wait before creating a new request to the matching domains Delay time.Duration // RandomDelay is the extra randomized duration to wait added to Delay before creating a new request RandomDelay time.Duration // Parallelism is the number of the maximum allowed concurrent requests of the matching domains Parallelism int waitChan chan bool compiledRegexp *regexp.Regexp compiledGlob glob.Glob } // Init initializes the private members of LimitRule func (r *LimitRule) Init() error { waitChanSize := 1 if r.Parallelism > 1 { waitChanSize = r.Parallelism } r.waitChan = make(chan bool, waitChanSize) hasPattern := false if r.DomainRegexp != "" { c, err := regexp.Compile(r.DomainRegexp) if err != nil { return err } r.compiledRegexp = c hasPattern = true } if r.DomainGlob != "" { c, err := glob.Compile(r.DomainGlob) if err != nil { return err } r.compiledGlob = c hasPattern = true } if !hasPattern { return ErrNoPattern } return nil } func (h *httpBackend) Init(jar http.CookieJar) { rand.Seed(time.Now().UnixNano()) h.Client = &http.Client{ Jar: jar, Timeout: 10 * time.Second, } h.lock = &sync.RWMutex{} } // Match checks that the domain parameter triggers the rule func (r *LimitRule) Match(domain string) bool { match := false if r.compiledRegexp != nil && r.compiledRegexp.MatchString(domain) { match = true } if r.compiledGlob != nil && r.compiledGlob.Match(domain) { match = true } return match } func (h *httpBackend) GetMatchingRule(domain string) *LimitRule { if h.LimitRules == nil { return nil } h.lock.RLock() defer h.lock.RUnlock() for _, r := range h.LimitRules { if r.Match(domain) { return r } } return nil } func (h *httpBackend) Cache(request *http.Request, bodySize int, checkHeadersFunc checkHeadersFunc, cacheDir string) (*Response, error) { if cacheDir == "" || request.Method != "GET" { return h.Do(request, bodySize, checkHeadersFunc) } sum := sha1.Sum([]byte(request.URL.String())) hash := hex.EncodeToString(sum[:]) dir := path.Join(cacheDir, hash[:2]) filename := path.Join(dir, hash) if file, err := os.Open(filename); err == nil { resp := new(Response) err := gob.NewDecoder(file).Decode(resp) file.Close() if resp.StatusCode < 500 { return resp, err } } resp, err := h.Do(request, bodySize, checkHeadersFunc) if err != nil || resp.StatusCode >= 500 { return resp, err } if _, err := os.Stat(dir); err != nil { if err := os.MkdirAll(dir, 0750); err != nil { return resp, err } } file, err := os.Create(filename + "~") if err != nil { return resp, err } if err := gob.NewEncoder(file).Encode(resp); err != nil { file.Close() return resp, err } file.Close() return resp, os.Rename(filename+"~", filename) } func (h *httpBackend) Do(request *http.Request, bodySize int, checkHeadersFunc checkHeadersFunc) (*Response, error) { r := h.GetMatchingRule(request.URL.Host) if r != nil { r.waitChan <- true defer func(r *LimitRule) { randomDelay := time.Duration(0) if r.RandomDelay != 0 { randomDelay = time.Duration(rand.Int63n(int64(r.RandomDelay))) } time.Sleep(r.Delay + randomDelay) <-r.waitChan }(r) } res, err := h.Client.Do(request) if err != nil { return nil, err } defer res.Body.Close() if res.Request != nil { *request = *res.Request } if !checkHeadersFunc(res.StatusCode, res.Header) { // closing res.Body (see defer above) without reading it aborts // the download return nil, ErrAbortedAfterHeaders } var bodyReader io.Reader = res.Body if bodySize > 0 { bodyReader = io.LimitReader(bodyReader, int64(bodySize)) } contentEncoding := strings.ToLower(res.Header.Get("Content-Encoding")) if !res.Uncompressed && (strings.Contains(contentEncoding, "gzip") || (contentEncoding == "" && strings.Contains(strings.ToLower(res.Header.Get("Content-Type")), "gzip")) || strings.HasSuffix(strings.ToLower(request.URL.Path), ".xml.gz")) { bodyReader, err = gzip.NewReader(bodyReader) if err != nil { return nil, err } defer bodyReader.(*gzip.Reader).Close() } body, err := ioutil.ReadAll(bodyReader) if err != nil { return nil, err } return &Response{ StatusCode: res.StatusCode, Body: body, Headers: &res.Header, }, nil } func (h *httpBackend) Limit(rule *LimitRule) error { h.lock.Lock() if h.LimitRules == nil { h.LimitRules = make([]*LimitRule, 0, 8) } h.LimitRules = append(h.LimitRules, rule) h.lock.Unlock() return rule.Init() } func (h *httpBackend) Limits(rules []*LimitRule) error { for _, r := range rules { if err := h.Limit(r); err != nil { return err } } return nil } ================================================ FILE: vendor/github.com/gocolly/colly/v2/http_trace.go ================================================ package colly import ( "net/http" "net/http/httptrace" "time" ) // HTTPTrace provides a datastructure for storing an http trace. type HTTPTrace struct { start, connect time.Time ConnectDuration time.Duration FirstByteDuration time.Duration } // trace returns a httptrace.ClientTrace object to be used with an http // request via httptrace.WithClientTrace() that fills in the HttpTrace. func (ht *HTTPTrace) trace() *httptrace.ClientTrace { trace := &httptrace.ClientTrace{ ConnectStart: func(network, addr string) { ht.connect = time.Now() }, ConnectDone: func(network, addr string, err error) { ht.ConnectDuration = time.Since(ht.connect) }, GetConn: func(hostPort string) { ht.start = time.Now() }, GotFirstResponseByte: func() { ht.FirstByteDuration = time.Since(ht.start) }, } return trace } // WithTrace returns the given HTTP Request with this HTTPTrace added to its // context. func (ht *HTTPTrace) WithTrace(req *http.Request) *http.Request { return req.WithContext(httptrace.WithClientTrace(req.Context(), ht.trace())) } ================================================ FILE: vendor/github.com/gocolly/colly/v2/request.go ================================================ // Copyright 2018 Adam Tauber // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package colly import ( "bytes" "encoding/json" "io" "io/ioutil" "net/http" "net/url" "strings" "sync/atomic" ) // Request is the representation of a HTTP request made by a Collector type Request struct { // URL is the parsed URL of the HTTP request URL *url.URL // Headers contains the Request's HTTP headers Headers *http.Header // Ctx is a context between a Request and a Response Ctx *Context // Depth is the number of the parents of the request Depth int // Method is the HTTP method of the request Method string // Body is the request body which is used on POST/PUT requests Body io.Reader // ResponseCharacterencoding is the character encoding of the response body. // Leave it blank to allow automatic character encoding of the response body. // It is empty by default and it can be set in OnRequest callback. ResponseCharacterEncoding string // ID is the Unique identifier of the request ID uint32 collector *Collector abort bool baseURL *url.URL // ProxyURL is the proxy address that handles the request ProxyURL string } type serializableRequest struct { URL string Method string Depth int Body []byte ID uint32 Ctx map[string]interface{} Headers http.Header } // New creates a new request with the context of the original request func (r *Request) New(method, URL string, body io.Reader) (*Request, error) { u, err := url.Parse(URL) if err != nil { return nil, err } return &Request{ Method: method, URL: u, Body: body, Ctx: r.Ctx, Headers: &http.Header{}, ID: atomic.AddUint32(&r.collector.requestCount, 1), collector: r.collector, }, nil } // Abort cancels the HTTP request when called in an OnRequest callback func (r *Request) Abort() { r.abort = true } // AbsoluteURL returns with the resolved absolute URL of an URL chunk. // AbsoluteURL returns empty string if the URL chunk is a fragment or // could not be parsed func (r *Request) AbsoluteURL(u string) string { if strings.HasPrefix(u, "#") { return "" } var base *url.URL if r.baseURL != nil { base = r.baseURL } else { base = r.URL } absURL, err := base.Parse(u) if err != nil { return "" } absURL.Fragment = "" if absURL.Scheme == "//" { absURL.Scheme = r.URL.Scheme } return absURL.String() } // Visit continues Collector's collecting job by creating a // request and preserves the Context of the previous request. // Visit also calls the previously provided callbacks func (r *Request) Visit(URL string) error { return r.collector.scrape(r.AbsoluteURL(URL), "GET", r.Depth+1, nil, r.Ctx, nil, true) } // HasVisited checks if the provided URL has been visited func (r *Request) HasVisited(URL string) (bool, error) { return r.collector.HasVisited(URL) } // Post continues a collector job by creating a POST request and preserves the Context // of the previous request. // Post also calls the previously provided callbacks func (r *Request) Post(URL string, requestData map[string]string) error { return r.collector.scrape(r.AbsoluteURL(URL), "POST", r.Depth+1, createFormReader(requestData), r.Ctx, nil, true) } // PostRaw starts a collector job by creating a POST request with raw binary data. // PostRaw preserves the Context of the previous request // and calls the previously provided callbacks func (r *Request) PostRaw(URL string, requestData []byte) error { return r.collector.scrape(r.AbsoluteURL(URL), "POST", r.Depth+1, bytes.NewReader(requestData), r.Ctx, nil, true) } // PostMultipart starts a collector job by creating a Multipart POST request // with raw binary data. PostMultipart also calls the previously provided. // callbacks func (r *Request) PostMultipart(URL string, requestData map[string][]byte) error { boundary := randomBoundary() hdr := http.Header{} hdr.Set("Content-Type", "multipart/form-data; boundary="+boundary) hdr.Set("User-Agent", r.collector.UserAgent) return r.collector.scrape(r.AbsoluteURL(URL), "POST", r.Depth+1, createMultipartReader(boundary, requestData), r.Ctx, hdr, true) } // Retry submits HTTP request again with the same parameters func (r *Request) Retry() error { r.Headers.Del("Cookie") return r.collector.scrape(r.URL.String(), r.Method, r.Depth, r.Body, r.Ctx, *r.Headers, false) } // Do submits the request func (r *Request) Do() error { return r.collector.scrape(r.URL.String(), r.Method, r.Depth, r.Body, r.Ctx, *r.Headers, !r.collector.AllowURLRevisit) } // Marshal serializes the Request func (r *Request) Marshal() ([]byte, error) { ctx := make(map[string]interface{}) if r.Ctx != nil { r.Ctx.ForEach(func(k string, v interface{}) interface{} { ctx[k] = v return nil }) } var err error var body []byte if r.Body != nil { body, err = ioutil.ReadAll(r.Body) if err != nil { return nil, err } } sr := &serializableRequest{ URL: r.URL.String(), Method: r.Method, Depth: r.Depth, Body: body, ID: r.ID, Ctx: ctx, } if r.Headers != nil { sr.Headers = *r.Headers } return json.Marshal(sr) } ================================================ FILE: vendor/github.com/gocolly/colly/v2/response.go ================================================ // Copyright 2018 Adam Tauber // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package colly import ( "bytes" "fmt" "io/ioutil" "mime" "net/http" "strings" "github.com/saintfish/chardet" "golang.org/x/net/html/charset" ) // Response is the representation of a HTTP response made by a Collector type Response struct { // StatusCode is the status code of the Response StatusCode int // Body is the content of the Response Body []byte // Ctx is a context between a Request and a Response Ctx *Context // Request is the Request object of the response Request *Request // Headers contains the Response's HTTP headers Headers *http.Header // Trace contains the HTTPTrace for the request. Will only be set by the // collector if Collector.TraceHTTP is set to true. Trace *HTTPTrace } // Save writes response body to disk func (r *Response) Save(fileName string) error { return ioutil.WriteFile(fileName, r.Body, 0644) } // FileName returns the sanitized file name parsed from "Content-Disposition" // header or from URL func (r *Response) FileName() string { _, params, err := mime.ParseMediaType(r.Headers.Get("Content-Disposition")) if fName, ok := params["filename"]; ok && err == nil { return SanitizeFileName(fName) } if r.Request.URL.RawQuery != "" { return SanitizeFileName(fmt.Sprintf("%s_%s", r.Request.URL.Path, r.Request.URL.RawQuery)) } return SanitizeFileName(strings.TrimPrefix(r.Request.URL.Path, "/")) } func (r *Response) fixCharset(detectCharset bool, defaultEncoding string) error { if len(r.Body) == 0 { return nil } if defaultEncoding != "" { tmpBody, err := encodeBytes(r.Body, "text/plain; charset="+defaultEncoding) if err != nil { return err } r.Body = tmpBody return nil } contentType := strings.ToLower(r.Headers.Get("Content-Type")) if strings.Contains(contentType, "image/") || strings.Contains(contentType, "video/") || strings.Contains(contentType, "audio/") || strings.Contains(contentType, "font/") { // These MIME types should not have textual data. return nil } if !strings.Contains(contentType, "charset") { if !detectCharset { return nil } d := chardet.NewTextDetector() r, err := d.DetectBest(r.Body) if err != nil { return err } contentType = "text/plain; charset=" + r.Charset } if strings.Contains(contentType, "utf-8") || strings.Contains(contentType, "utf8") { return nil } tmpBody, err := encodeBytes(r.Body, contentType) if err != nil { return err } r.Body = tmpBody return nil } func encodeBytes(b []byte, contentType string) ([]byte, error) { r, err := charset.NewReader(bytes.NewReader(b), contentType) if err != nil { return nil, err } return ioutil.ReadAll(r) } ================================================ FILE: vendor/github.com/gocolly/colly/v2/storage/storage.go ================================================ // Copyright 2018 Adam Tauber // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package storage import ( "net/http" "net/http/cookiejar" "net/url" "strings" "sync" ) // Storage is an interface which handles Collector's internal data, // like visited urls and cookies. // The default Storage of the Collector is the InMemoryStorage. // Collector's storage can be changed by calling Collector.SetStorage() // function. type Storage interface { // Init initializes the storage Init() error // Visited receives and stores a request ID that is visited by the Collector Visited(requestID uint64) error // IsVisited returns true if the request was visited before IsVisited // is called IsVisited(requestID uint64) (bool, error) // Cookies retrieves stored cookies for a given host Cookies(u *url.URL) string // SetCookies stores cookies for a given host SetCookies(u *url.URL, cookies string) } // InMemoryStorage is the default storage backend of colly. // InMemoryStorage keeps cookies and visited urls in memory // without persisting data on the disk. type InMemoryStorage struct { visitedURLs map[uint64]bool lock *sync.RWMutex jar *cookiejar.Jar } // Init initializes InMemoryStorage func (s *InMemoryStorage) Init() error { if s.visitedURLs == nil { s.visitedURLs = make(map[uint64]bool) } if s.lock == nil { s.lock = &sync.RWMutex{} } if s.jar == nil { var err error s.jar, err = cookiejar.New(nil) return err } return nil } // Visited implements Storage.Visited() func (s *InMemoryStorage) Visited(requestID uint64) error { s.lock.Lock() s.visitedURLs[requestID] = true s.lock.Unlock() return nil } // IsVisited implements Storage.IsVisited() func (s *InMemoryStorage) IsVisited(requestID uint64) (bool, error) { s.lock.RLock() visited := s.visitedURLs[requestID] s.lock.RUnlock() return visited, nil } // Cookies implements Storage.Cookies() func (s *InMemoryStorage) Cookies(u *url.URL) string { return StringifyCookies(s.jar.Cookies(u)) } // SetCookies implements Storage.SetCookies() func (s *InMemoryStorage) SetCookies(u *url.URL, cookies string) { s.jar.SetCookies(u, UnstringifyCookies(cookies)) } // Close implements Storage.Close() func (s *InMemoryStorage) Close() error { return nil } // StringifyCookies serializes list of http.Cookies to string func StringifyCookies(cookies []*http.Cookie) string { // Stringify cookies. cs := make([]string, len(cookies)) for i, c := range cookies { cs[i] = c.String() } return strings.Join(cs, "\n") } // UnstringifyCookies deserializes a cookie string to http.Cookies func UnstringifyCookies(s string) []*http.Cookie { h := http.Header{} for _, c := range strings.Split(s, "\n") { h.Add("Set-Cookie", c) } r := http.Response{Header: h} return r.Cookies() } // ContainsCookie checks if a cookie name is represented in cookies func ContainsCookie(cookies []*http.Cookie, name string) bool { for _, c := range cookies { if c.Name == name { return true } } return false } ================================================ FILE: vendor/github.com/gocolly/colly/v2/unmarshal.go ================================================ // Copyright 2018 Adam Tauber // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package colly import ( "errors" "reflect" "strings" "github.com/PuerkitoBio/goquery" ) // Unmarshal is a shorthand for colly.UnmarshalHTML func (h *HTMLElement) Unmarshal(v interface{}) error { return UnmarshalHTML(v, h.DOM, nil) } // UnmarshalWithMap is a shorthand for colly.UnmarshalHTML, extended to allow maps to be passed in. func (h *HTMLElement) UnmarshalWithMap(v interface{}, structMap map[string]string) error { return UnmarshalHTML(v, h.DOM, structMap) } // UnmarshalHTML declaratively extracts text or attributes to a struct from // HTML response using struct tags composed of css selectors. // Allowed struct tags: // - "selector" (required): CSS (goquery) selector of the desired data // - "attr" (optional): Selects the matching element's attribute's value. // Leave it blank or omit to get the text of the element. // // Example struct declaration: // // type Nested struct { // String string `selector:"div > p"` // Classes []string `selector:"li" attr:"class"` // Struct *Nested `selector:"div > div"` // } // // Supported types: struct, *struct, string, []string func UnmarshalHTML(v interface{}, s *goquery.Selection, structMap map[string]string) error { rv := reflect.ValueOf(v) if rv.Kind() != reflect.Ptr || rv.IsNil() { return errors.New("Invalid type or nil-pointer") } sv := rv.Elem() st := reflect.TypeOf(v).Elem() if structMap != nil { for k, v := range structMap { attrV := sv.FieldByName(k) if !attrV.CanAddr() || !attrV.CanSet() { continue } if err := unmarshalSelector(s, attrV, v); err != nil { return err } } } else { for i := 0; i < sv.NumField(); i++ { attrV := sv.Field(i) if !attrV.CanAddr() || !attrV.CanSet() { continue } if err := unmarshalAttr(s, attrV, st.Field(i)); err != nil { return err } } } return nil } func unmarshalSelector(s *goquery.Selection, attrV reflect.Value, selector string) error { //selector is "-" specify that field should ignore. if selector == "-" { return nil } htmlAttr := "" // TODO support more types switch attrV.Kind() { case reflect.Slice: if err := unmarshalSlice(s, selector, htmlAttr, attrV); err != nil { return err } case reflect.String: val := getDOMValue(s.Find(selector), htmlAttr) attrV.Set(reflect.Indirect(reflect.ValueOf(val))) case reflect.Struct: if err := unmarshalStruct(s, selector, attrV); err != nil { return err } case reflect.Ptr: if err := unmarshalPtr(s, selector, attrV); err != nil { return err } default: return errors.New("Invalid type: " + attrV.String()) } return nil } func unmarshalAttr(s *goquery.Selection, attrV reflect.Value, attrT reflect.StructField) error { selector := attrT.Tag.Get("selector") //selector is "-" specify that field should ignore. if selector == "-" { return nil } htmlAttr := attrT.Tag.Get("attr") // TODO support more types switch attrV.Kind() { case reflect.Slice: if err := unmarshalSlice(s, selector, htmlAttr, attrV); err != nil { return err } case reflect.String: val := getDOMValue(s.Find(selector), htmlAttr) attrV.Set(reflect.Indirect(reflect.ValueOf(val))) case reflect.Struct: if err := unmarshalStruct(s, selector, attrV); err != nil { return err } case reflect.Ptr: if err := unmarshalPtr(s, selector, attrV); err != nil { return err } default: return errors.New("Invalid type: " + attrV.String()) } return nil } func unmarshalStruct(s *goquery.Selection, selector string, attrV reflect.Value) error { newS := s if selector != "" { newS = newS.Find(selector) } if newS.Nodes == nil { return nil } v := reflect.New(attrV.Type()) err := UnmarshalHTML(v.Interface(), newS, nil) if err != nil { return err } attrV.Set(reflect.Indirect(v)) return nil } func unmarshalPtr(s *goquery.Selection, selector string, attrV reflect.Value) error { newS := s if selector != "" { newS = newS.Find(selector) } if newS.Nodes == nil { return nil } e := attrV.Type().Elem() if e.Kind() != reflect.Struct { return errors.New("Invalid slice type") } v := reflect.New(e) err := UnmarshalHTML(v.Interface(), newS, nil) if err != nil { return err } attrV.Set(v) return nil } func unmarshalSlice(s *goquery.Selection, selector, htmlAttr string, attrV reflect.Value) error { if attrV.Pointer() == 0 { v := reflect.MakeSlice(attrV.Type(), 0, 0) attrV.Set(v) } switch attrV.Type().Elem().Kind() { case reflect.String: s.Find(selector).Each(func(_ int, s *goquery.Selection) { val := getDOMValue(s, htmlAttr) attrV.Set(reflect.Append(attrV, reflect.Indirect(reflect.ValueOf(val)))) }) case reflect.Ptr: s.Find(selector).Each(func(_ int, innerSel *goquery.Selection) { someVal := reflect.New(attrV.Type().Elem().Elem()) UnmarshalHTML(someVal.Interface(), innerSel, nil) attrV.Set(reflect.Append(attrV, someVal)) }) case reflect.Struct: s.Find(selector).Each(func(_ int, innerSel *goquery.Selection) { someVal := reflect.New(attrV.Type().Elem()) UnmarshalHTML(someVal.Interface(), innerSel, nil) attrV.Set(reflect.Append(attrV, reflect.Indirect(someVal))) }) default: return errors.New("Invalid slice type") } return nil } func getDOMValue(s *goquery.Selection, attr string) string { if attr == "" { return strings.TrimSpace(s.First().Text()) } attrV, _ := s.Attr(attr) return attrV } ================================================ FILE: vendor/github.com/gocolly/colly/v2/xmlelement.go ================================================ // Copyright 2018 Adam Tauber // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package colly import ( "encoding/xml" "strings" "github.com/antchfx/htmlquery" "github.com/antchfx/xmlquery" "golang.org/x/net/html" ) // XMLElement is the representation of a XML tag. type XMLElement struct { // Name is the name of the tag Name string Text string attributes interface{} // Request is the request object of the element's HTML document Request *Request // Response is the Response object of the element's HTML document Response *Response // DOM is the DOM object of the page. DOM is relative // to the current XMLElement and is either a html.Node or xmlquery.Node // based on how the XMLElement was created. DOM interface{} isHTML bool } // NewXMLElementFromHTMLNode creates a XMLElement from a html.Node. func NewXMLElementFromHTMLNode(resp *Response, s *html.Node) *XMLElement { return &XMLElement{ Name: s.Data, Request: resp.Request, Response: resp, Text: htmlquery.InnerText(s), DOM: s, attributes: s.Attr, isHTML: true, } } // NewXMLElementFromXMLNode creates a XMLElement from a xmlquery.Node. func NewXMLElementFromXMLNode(resp *Response, s *xmlquery.Node) *XMLElement { return &XMLElement{ Name: s.Data, Request: resp.Request, Response: resp, Text: s.InnerText(), DOM: s, attributes: s.Attr, isHTML: false, } } // Attr returns the selected attribute of a HTMLElement or empty string // if no attribute found func (h *XMLElement) Attr(k string) string { if h.isHTML { for _, a := range h.attributes.([]html.Attribute) { if a.Key == k { return a.Val } } } else { for _, a := range h.attributes.([]xml.Attr) { if a.Name.Local == k { return a.Value } } } return "" } // ChildText returns the concatenated and stripped text content of the matching // elements. func (h *XMLElement) ChildText(xpathQuery string) string { if h.isHTML { child := htmlquery.FindOne(h.DOM.(*html.Node), xpathQuery) if child == nil { return "" } return strings.TrimSpace(htmlquery.InnerText(child)) } child := xmlquery.FindOne(h.DOM.(*xmlquery.Node), xpathQuery) if child == nil { return "" } return strings.TrimSpace(child.InnerText()) } // ChildAttr returns the stripped text content of the first matching // element's attribute. func (h *XMLElement) ChildAttr(xpathQuery, attrName string) string { if h.isHTML { child := htmlquery.FindOne(h.DOM.(*html.Node), xpathQuery) if child != nil { for _, attr := range child.Attr { if attr.Key == attrName { return strings.TrimSpace(attr.Val) } } } } else { child := xmlquery.FindOne(h.DOM.(*xmlquery.Node), xpathQuery) if child != nil { for _, attr := range child.Attr { if attr.Name.Local == attrName { return strings.TrimSpace(attr.Value) } } } } return "" } // ChildAttrs returns the stripped text content of all the matching // element's attributes. func (h *XMLElement) ChildAttrs(xpathQuery, attrName string) []string { var res []string if h.isHTML { for _, child := range htmlquery.Find(h.DOM.(*html.Node), xpathQuery) { for _, attr := range child.Attr { if attr.Key == attrName { res = append(res, strings.TrimSpace(attr.Val)) } } } } else { xmlquery.FindEach(h.DOM.(*xmlquery.Node), xpathQuery, func(i int, child *xmlquery.Node) { for _, attr := range child.Attr { if attr.Name.Local == attrName { res = append(res, strings.TrimSpace(attr.Value)) } } }) } return res } // ChildTexts returns an array of strings corresponding to child elements that match the xpath query. // Each item in the array is the stripped text content of the corresponding matching child element. func (h *XMLElement) ChildTexts(xpathQuery string) []string { texts := make([]string, 0) if h.isHTML { for _, child := range htmlquery.Find(h.DOM.(*html.Node), xpathQuery) { texts = append(texts, strings.TrimSpace(htmlquery.InnerText(child))) } } else { xmlquery.FindEach(h.DOM.(*xmlquery.Node), xpathQuery, func(i int, child *xmlquery.Node) { texts = append(texts, strings.TrimSpace(child.InnerText())) }) } return texts } ================================================ FILE: vendor/github.com/gogo/protobuf/AUTHORS ================================================ # This is the official list of GoGo authors for copyright purposes. # This file is distinct from the CONTRIBUTORS file, which # lists people. For example, employees are listed in CONTRIBUTORS, # but not in AUTHORS, because the employer holds the copyright. # Names should be added to this file as one of # Organization's name # Individual's name # Individual's name # Please keep the list sorted. Sendgrid, Inc Vastech SA (PTY) LTD Walter Schulze ================================================ FILE: vendor/github.com/gogo/protobuf/CONTRIBUTORS ================================================ Anton Povarov Brian Goff Clayton Coleman Denis Smirnov DongYun Kang Dwayne Schultz Georg Apitz Gustav Paul Johan Brandhorst John Shahid John Tuley Laurent Patrick Lee Peter Edge Roger Johansson Sam Nguyen Sergio Arbeo Stephen J Day Tamir Duberstein Todd Eisenberger Tormod Erevik Lea Vyacheslav Kim Walter Schulze ================================================ FILE: vendor/github.com/gogo/protobuf/LICENSE ================================================ Copyright (c) 2013, The GoGo Authors. All rights reserved. Protocol Buffers for Go with Gadgets Go support for Protocol Buffers - Google's data interchange format Copyright 2010 The Go Authors. All rights reserved. https://github.com/golang/protobuf Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/gogo/protobuf/proto/Makefile ================================================ # Go support for Protocol Buffers - Google's data interchange format # # Copyright 2010 The Go Authors. All rights reserved. # https://github.com/golang/protobuf # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following disclaimer # in the documentation and/or other materials provided with the # distribution. # * Neither the name of Google Inc. nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. install: go install test: install generate-test-pbs go test generate-test-pbs: make install make -C test_proto make -C proto3_proto make ================================================ FILE: vendor/github.com/gogo/protobuf/proto/clone.go ================================================ // Go support for Protocol Buffers - Google's data interchange format // // Copyright 2011 The Go Authors. All rights reserved. // https://github.com/golang/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Protocol buffer deep copy and merge. // TODO: RawMessage. package proto import ( "fmt" "log" "reflect" "strings" ) // Clone returns a deep copy of a protocol buffer. func Clone(src Message) Message { in := reflect.ValueOf(src) if in.IsNil() { return src } out := reflect.New(in.Type().Elem()) dst := out.Interface().(Message) Merge(dst, src) return dst } // Merger is the interface representing objects that can merge messages of the same type. type Merger interface { // Merge merges src into this message. // Required and optional fields that are set in src will be set to that value in dst. // Elements of repeated fields will be appended. // // Merge may panic if called with a different argument type than the receiver. Merge(src Message) } // generatedMerger is the custom merge method that generated protos will have. // We must add this method since a generate Merge method will conflict with // many existing protos that have a Merge data field already defined. type generatedMerger interface { XXX_Merge(src Message) } // Merge merges src into dst. // Required and optional fields that are set in src will be set to that value in dst. // Elements of repeated fields will be appended. // Merge panics if src and dst are not the same type, or if dst is nil. func Merge(dst, src Message) { if m, ok := dst.(Merger); ok { m.Merge(src) return } in := reflect.ValueOf(src) out := reflect.ValueOf(dst) if out.IsNil() { panic("proto: nil destination") } if in.Type() != out.Type() { panic(fmt.Sprintf("proto.Merge(%T, %T) type mismatch", dst, src)) } if in.IsNil() { return // Merge from nil src is a noop } if m, ok := dst.(generatedMerger); ok { m.XXX_Merge(src) return } mergeStruct(out.Elem(), in.Elem()) } func mergeStruct(out, in reflect.Value) { sprop := GetProperties(in.Type()) for i := 0; i < in.NumField(); i++ { f := in.Type().Field(i) if strings.HasPrefix(f.Name, "XXX_") { continue } mergeAny(out.Field(i), in.Field(i), false, sprop.Prop[i]) } if emIn, ok := in.Addr().Interface().(extensionsBytes); ok { emOut := out.Addr().Interface().(extensionsBytes) bIn := emIn.GetExtensions() bOut := emOut.GetExtensions() *bOut = append(*bOut, *bIn...) } else if emIn, err := extendable(in.Addr().Interface()); err == nil { emOut, _ := extendable(out.Addr().Interface()) mIn, muIn := emIn.extensionsRead() if mIn != nil { mOut := emOut.extensionsWrite() muIn.Lock() mergeExtension(mOut, mIn) muIn.Unlock() } } uf := in.FieldByName("XXX_unrecognized") if !uf.IsValid() { return } uin := uf.Bytes() if len(uin) > 0 { out.FieldByName("XXX_unrecognized").SetBytes(append([]byte(nil), uin...)) } } // mergeAny performs a merge between two values of the same type. // viaPtr indicates whether the values were indirected through a pointer (implying proto2). // prop is set if this is a struct field (it may be nil). func mergeAny(out, in reflect.Value, viaPtr bool, prop *Properties) { if in.Type() == protoMessageType { if !in.IsNil() { if out.IsNil() { out.Set(reflect.ValueOf(Clone(in.Interface().(Message)))) } else { Merge(out.Interface().(Message), in.Interface().(Message)) } } return } switch in.Kind() { case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64, reflect.String, reflect.Uint32, reflect.Uint64: if !viaPtr && isProto3Zero(in) { return } out.Set(in) case reflect.Interface: // Probably a oneof field; copy non-nil values. if in.IsNil() { return } // Allocate destination if it is not set, or set to a different type. // Otherwise we will merge as normal. if out.IsNil() || out.Elem().Type() != in.Elem().Type() { out.Set(reflect.New(in.Elem().Elem().Type())) // interface -> *T -> T -> new(T) } mergeAny(out.Elem(), in.Elem(), false, nil) case reflect.Map: if in.Len() == 0 { return } if out.IsNil() { out.Set(reflect.MakeMap(in.Type())) } // For maps with value types of *T or []byte we need to deep copy each value. elemKind := in.Type().Elem().Kind() for _, key := range in.MapKeys() { var val reflect.Value switch elemKind { case reflect.Ptr: val = reflect.New(in.Type().Elem().Elem()) mergeAny(val, in.MapIndex(key), false, nil) case reflect.Slice: val = in.MapIndex(key) val = reflect.ValueOf(append([]byte{}, val.Bytes()...)) default: val = in.MapIndex(key) } out.SetMapIndex(key, val) } case reflect.Ptr: if in.IsNil() { return } if out.IsNil() { out.Set(reflect.New(in.Elem().Type())) } mergeAny(out.Elem(), in.Elem(), true, nil) case reflect.Slice: if in.IsNil() { return } if in.Type().Elem().Kind() == reflect.Uint8 { // []byte is a scalar bytes field, not a repeated field. // Edge case: if this is in a proto3 message, a zero length // bytes field is considered the zero value, and should not // be merged. if prop != nil && prop.proto3 && in.Len() == 0 { return } // Make a deep copy. // Append to []byte{} instead of []byte(nil) so that we never end up // with a nil result. out.SetBytes(append([]byte{}, in.Bytes()...)) return } n := in.Len() if out.IsNil() { out.Set(reflect.MakeSlice(in.Type(), 0, n)) } switch in.Type().Elem().Kind() { case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64, reflect.String, reflect.Uint32, reflect.Uint64: out.Set(reflect.AppendSlice(out, in)) default: for i := 0; i < n; i++ { x := reflect.Indirect(reflect.New(in.Type().Elem())) mergeAny(x, in.Index(i), false, nil) out.Set(reflect.Append(out, x)) } } case reflect.Struct: mergeStruct(out, in) default: // unknown type, so not a protocol buffer log.Printf("proto: don't know how to copy %v", in) } } func mergeExtension(out, in map[int32]Extension) { for extNum, eIn := range in { eOut := Extension{desc: eIn.desc} if eIn.value != nil { v := reflect.New(reflect.TypeOf(eIn.value)).Elem() mergeAny(v, reflect.ValueOf(eIn.value), false, nil) eOut.value = v.Interface() } if eIn.enc != nil { eOut.enc = make([]byte, len(eIn.enc)) copy(eOut.enc, eIn.enc) } out[extNum] = eOut } } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/custom_gogo.go ================================================ // Protocol Buffers for Go with Gadgets // // Copyright (c) 2018, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto import "reflect" type custom interface { Marshal() ([]byte, error) Unmarshal(data []byte) error Size() int } var customType = reflect.TypeOf((*custom)(nil)).Elem() ================================================ FILE: vendor/github.com/gogo/protobuf/proto/decode.go ================================================ // Go support for Protocol Buffers - Google's data interchange format // // Copyright 2010 The Go Authors. All rights reserved. // https://github.com/golang/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto /* * Routines for decoding protocol buffer data to construct in-memory representations. */ import ( "errors" "fmt" "io" ) // errOverflow is returned when an integer is too large to be represented. var errOverflow = errors.New("proto: integer overflow") // ErrInternalBadWireType is returned by generated code when an incorrect // wire type is encountered. It does not get returned to user code. var ErrInternalBadWireType = errors.New("proto: internal error: bad wiretype for oneof") // DecodeVarint reads a varint-encoded integer from the slice. // It returns the integer and the number of bytes consumed, or // zero if there is not enough. // This is the format for the // int32, int64, uint32, uint64, bool, and enum // protocol buffer types. func DecodeVarint(buf []byte) (x uint64, n int) { for shift := uint(0); shift < 64; shift += 7 { if n >= len(buf) { return 0, 0 } b := uint64(buf[n]) n++ x |= (b & 0x7F) << shift if (b & 0x80) == 0 { return x, n } } // The number is too large to represent in a 64-bit value. return 0, 0 } func (p *Buffer) decodeVarintSlow() (x uint64, err error) { i := p.index l := len(p.buf) for shift := uint(0); shift < 64; shift += 7 { if i >= l { err = io.ErrUnexpectedEOF return } b := p.buf[i] i++ x |= (uint64(b) & 0x7F) << shift if b < 0x80 { p.index = i return } } // The number is too large to represent in a 64-bit value. err = errOverflow return } // DecodeVarint reads a varint-encoded integer from the Buffer. // This is the format for the // int32, int64, uint32, uint64, bool, and enum // protocol buffer types. func (p *Buffer) DecodeVarint() (x uint64, err error) { i := p.index buf := p.buf if i >= len(buf) { return 0, io.ErrUnexpectedEOF } else if buf[i] < 0x80 { p.index++ return uint64(buf[i]), nil } else if len(buf)-i < 10 { return p.decodeVarintSlow() } var b uint64 // we already checked the first byte x = uint64(buf[i]) - 0x80 i++ b = uint64(buf[i]) i++ x += b << 7 if b&0x80 == 0 { goto done } x -= 0x80 << 7 b = uint64(buf[i]) i++ x += b << 14 if b&0x80 == 0 { goto done } x -= 0x80 << 14 b = uint64(buf[i]) i++ x += b << 21 if b&0x80 == 0 { goto done } x -= 0x80 << 21 b = uint64(buf[i]) i++ x += b << 28 if b&0x80 == 0 { goto done } x -= 0x80 << 28 b = uint64(buf[i]) i++ x += b << 35 if b&0x80 == 0 { goto done } x -= 0x80 << 35 b = uint64(buf[i]) i++ x += b << 42 if b&0x80 == 0 { goto done } x -= 0x80 << 42 b = uint64(buf[i]) i++ x += b << 49 if b&0x80 == 0 { goto done } x -= 0x80 << 49 b = uint64(buf[i]) i++ x += b << 56 if b&0x80 == 0 { goto done } x -= 0x80 << 56 b = uint64(buf[i]) i++ x += b << 63 if b&0x80 == 0 { goto done } return 0, errOverflow done: p.index = i return x, nil } // DecodeFixed64 reads a 64-bit integer from the Buffer. // This is the format for the // fixed64, sfixed64, and double protocol buffer types. func (p *Buffer) DecodeFixed64() (x uint64, err error) { // x, err already 0 i := p.index + 8 if i < 0 || i > len(p.buf) { err = io.ErrUnexpectedEOF return } p.index = i x = uint64(p.buf[i-8]) x |= uint64(p.buf[i-7]) << 8 x |= uint64(p.buf[i-6]) << 16 x |= uint64(p.buf[i-5]) << 24 x |= uint64(p.buf[i-4]) << 32 x |= uint64(p.buf[i-3]) << 40 x |= uint64(p.buf[i-2]) << 48 x |= uint64(p.buf[i-1]) << 56 return } // DecodeFixed32 reads a 32-bit integer from the Buffer. // This is the format for the // fixed32, sfixed32, and float protocol buffer types. func (p *Buffer) DecodeFixed32() (x uint64, err error) { // x, err already 0 i := p.index + 4 if i < 0 || i > len(p.buf) { err = io.ErrUnexpectedEOF return } p.index = i x = uint64(p.buf[i-4]) x |= uint64(p.buf[i-3]) << 8 x |= uint64(p.buf[i-2]) << 16 x |= uint64(p.buf[i-1]) << 24 return } // DecodeZigzag64 reads a zigzag-encoded 64-bit integer // from the Buffer. // This is the format used for the sint64 protocol buffer type. func (p *Buffer) DecodeZigzag64() (x uint64, err error) { x, err = p.DecodeVarint() if err != nil { return } x = (x >> 1) ^ uint64((int64(x&1)<<63)>>63) return } // DecodeZigzag32 reads a zigzag-encoded 32-bit integer // from the Buffer. // This is the format used for the sint32 protocol buffer type. func (p *Buffer) DecodeZigzag32() (x uint64, err error) { x, err = p.DecodeVarint() if err != nil { return } x = uint64((uint32(x) >> 1) ^ uint32((int32(x&1)<<31)>>31)) return } // DecodeRawBytes reads a count-delimited byte buffer from the Buffer. // This is the format used for the bytes protocol buffer // type and for embedded messages. func (p *Buffer) DecodeRawBytes(alloc bool) (buf []byte, err error) { n, err := p.DecodeVarint() if err != nil { return nil, err } nb := int(n) if nb < 0 { return nil, fmt.Errorf("proto: bad byte length %d", nb) } end := p.index + nb if end < p.index || end > len(p.buf) { return nil, io.ErrUnexpectedEOF } if !alloc { // todo: check if can get more uses of alloc=false buf = p.buf[p.index:end] p.index += nb return } buf = make([]byte, nb) copy(buf, p.buf[p.index:]) p.index += nb return } // DecodeStringBytes reads an encoded string from the Buffer. // This is the format used for the proto2 string type. func (p *Buffer) DecodeStringBytes() (s string, err error) { buf, err := p.DecodeRawBytes(false) if err != nil { return } return string(buf), nil } // Unmarshaler is the interface representing objects that can // unmarshal themselves. The argument points to data that may be // overwritten, so implementations should not keep references to the // buffer. // Unmarshal implementations should not clear the receiver. // Any unmarshaled data should be merged into the receiver. // Callers of Unmarshal that do not want to retain existing data // should Reset the receiver before calling Unmarshal. type Unmarshaler interface { Unmarshal([]byte) error } // newUnmarshaler is the interface representing objects that can // unmarshal themselves. The semantics are identical to Unmarshaler. // // This exists to support protoc-gen-go generated messages. // The proto package will stop type-asserting to this interface in the future. // // DO NOT DEPEND ON THIS. type newUnmarshaler interface { XXX_Unmarshal([]byte) error } // Unmarshal parses the protocol buffer representation in buf and places the // decoded result in pb. If the struct underlying pb does not match // the data in buf, the results can be unpredictable. // // Unmarshal resets pb before starting to unmarshal, so any // existing data in pb is always removed. Use UnmarshalMerge // to preserve and append to existing data. func Unmarshal(buf []byte, pb Message) error { pb.Reset() if u, ok := pb.(newUnmarshaler); ok { return u.XXX_Unmarshal(buf) } if u, ok := pb.(Unmarshaler); ok { return u.Unmarshal(buf) } return NewBuffer(buf).Unmarshal(pb) } // UnmarshalMerge parses the protocol buffer representation in buf and // writes the decoded result to pb. If the struct underlying pb does not match // the data in buf, the results can be unpredictable. // // UnmarshalMerge merges into existing data in pb. // Most code should use Unmarshal instead. func UnmarshalMerge(buf []byte, pb Message) error { if u, ok := pb.(newUnmarshaler); ok { return u.XXX_Unmarshal(buf) } if u, ok := pb.(Unmarshaler); ok { // NOTE: The history of proto have unfortunately been inconsistent // whether Unmarshaler should or should not implicitly clear itself. // Some implementations do, most do not. // Thus, calling this here may or may not do what people want. // // See https://github.com/golang/protobuf/issues/424 return u.Unmarshal(buf) } return NewBuffer(buf).Unmarshal(pb) } // DecodeMessage reads a count-delimited message from the Buffer. func (p *Buffer) DecodeMessage(pb Message) error { enc, err := p.DecodeRawBytes(false) if err != nil { return err } return NewBuffer(enc).Unmarshal(pb) } // DecodeGroup reads a tag-delimited group from the Buffer. // StartGroup tag is already consumed. This function consumes // EndGroup tag. func (p *Buffer) DecodeGroup(pb Message) error { b := p.buf[p.index:] x, y := findEndGroup(b) if x < 0 { return io.ErrUnexpectedEOF } err := Unmarshal(b[:x], pb) p.index += y return err } // Unmarshal parses the protocol buffer representation in the // Buffer and places the decoded result in pb. If the struct // underlying pb does not match the data in the buffer, the results can be // unpredictable. // // Unlike proto.Unmarshal, this does not reset pb before starting to unmarshal. func (p *Buffer) Unmarshal(pb Message) error { // If the object can unmarshal itself, let it. if u, ok := pb.(newUnmarshaler); ok { err := u.XXX_Unmarshal(p.buf[p.index:]) p.index = len(p.buf) return err } if u, ok := pb.(Unmarshaler); ok { // NOTE: The history of proto have unfortunately been inconsistent // whether Unmarshaler should or should not implicitly clear itself. // Some implementations do, most do not. // Thus, calling this here may or may not do what people want. // // See https://github.com/golang/protobuf/issues/424 err := u.Unmarshal(p.buf[p.index:]) p.index = len(p.buf) return err } // Slow workaround for messages that aren't Unmarshalers. // This includes some hand-coded .pb.go files and // bootstrap protos. // TODO: fix all of those and then add Unmarshal to // the Message interface. Then: // The cast above and code below can be deleted. // The old unmarshaler can be deleted. // Clients can call Unmarshal directly (can already do that, actually). var info InternalMessageInfo err := info.Unmarshal(pb, p.buf[p.index:]) p.index = len(p.buf) return err } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/deprecated.go ================================================ // Go support for Protocol Buffers - Google's data interchange format // // Copyright 2018 The Go Authors. All rights reserved. // https://github.com/golang/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto import "errors" // Deprecated: do not use. type Stats struct{ Emalloc, Dmalloc, Encode, Decode, Chit, Cmiss, Size uint64 } // Deprecated: do not use. func GetStats() Stats { return Stats{} } // Deprecated: do not use. func MarshalMessageSet(interface{}) ([]byte, error) { return nil, errors.New("proto: not implemented") } // Deprecated: do not use. func UnmarshalMessageSet([]byte, interface{}) error { return errors.New("proto: not implemented") } // Deprecated: do not use. func MarshalMessageSetJSON(interface{}) ([]byte, error) { return nil, errors.New("proto: not implemented") } // Deprecated: do not use. func UnmarshalMessageSetJSON([]byte, interface{}) error { return errors.New("proto: not implemented") } // Deprecated: do not use. func RegisterMessageSetType(Message, int32, string) {} ================================================ FILE: vendor/github.com/gogo/protobuf/proto/discard.go ================================================ // Go support for Protocol Buffers - Google's data interchange format // // Copyright 2017 The Go Authors. All rights reserved. // https://github.com/golang/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto import ( "fmt" "reflect" "strings" "sync" "sync/atomic" ) type generatedDiscarder interface { XXX_DiscardUnknown() } // DiscardUnknown recursively discards all unknown fields from this message // and all embedded messages. // // When unmarshaling a message with unrecognized fields, the tags and values // of such fields are preserved in the Message. This allows a later call to // marshal to be able to produce a message that continues to have those // unrecognized fields. To avoid this, DiscardUnknown is used to // explicitly clear the unknown fields after unmarshaling. // // For proto2 messages, the unknown fields of message extensions are only // discarded from messages that have been accessed via GetExtension. func DiscardUnknown(m Message) { if m, ok := m.(generatedDiscarder); ok { m.XXX_DiscardUnknown() return } // TODO: Dynamically populate a InternalMessageInfo for legacy messages, // but the master branch has no implementation for InternalMessageInfo, // so it would be more work to replicate that approach. discardLegacy(m) } // DiscardUnknown recursively discards all unknown fields. func (a *InternalMessageInfo) DiscardUnknown(m Message) { di := atomicLoadDiscardInfo(&a.discard) if di == nil { di = getDiscardInfo(reflect.TypeOf(m).Elem()) atomicStoreDiscardInfo(&a.discard, di) } di.discard(toPointer(&m)) } type discardInfo struct { typ reflect.Type initialized int32 // 0: only typ is valid, 1: everything is valid lock sync.Mutex fields []discardFieldInfo unrecognized field } type discardFieldInfo struct { field field // Offset of field, guaranteed to be valid discard func(src pointer) } var ( discardInfoMap = map[reflect.Type]*discardInfo{} discardInfoLock sync.Mutex ) func getDiscardInfo(t reflect.Type) *discardInfo { discardInfoLock.Lock() defer discardInfoLock.Unlock() di := discardInfoMap[t] if di == nil { di = &discardInfo{typ: t} discardInfoMap[t] = di } return di } func (di *discardInfo) discard(src pointer) { if src.isNil() { return // Nothing to do. } if atomic.LoadInt32(&di.initialized) == 0 { di.computeDiscardInfo() } for _, fi := range di.fields { sfp := src.offset(fi.field) fi.discard(sfp) } // For proto2 messages, only discard unknown fields in message extensions // that have been accessed via GetExtension. if em, err := extendable(src.asPointerTo(di.typ).Interface()); err == nil { // Ignore lock since DiscardUnknown is not concurrency safe. emm, _ := em.extensionsRead() for _, mx := range emm { if m, ok := mx.value.(Message); ok { DiscardUnknown(m) } } } if di.unrecognized.IsValid() { *src.offset(di.unrecognized).toBytes() = nil } } func (di *discardInfo) computeDiscardInfo() { di.lock.Lock() defer di.lock.Unlock() if di.initialized != 0 { return } t := di.typ n := t.NumField() for i := 0; i < n; i++ { f := t.Field(i) if strings.HasPrefix(f.Name, "XXX_") { continue } dfi := discardFieldInfo{field: toField(&f)} tf := f.Type // Unwrap tf to get its most basic type. var isPointer, isSlice bool if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 { isSlice = true tf = tf.Elem() } if tf.Kind() == reflect.Ptr { isPointer = true tf = tf.Elem() } if isPointer && isSlice && tf.Kind() != reflect.Struct { panic(fmt.Sprintf("%v.%s cannot be a slice of pointers to primitive types", t, f.Name)) } switch tf.Kind() { case reflect.Struct: switch { case !isPointer: panic(fmt.Sprintf("%v.%s cannot be a direct struct value", t, f.Name)) case isSlice: // E.g., []*pb.T discardInfo := getDiscardInfo(tf) dfi.discard = func(src pointer) { sps := src.getPointerSlice() for _, sp := range sps { if !sp.isNil() { discardInfo.discard(sp) } } } default: // E.g., *pb.T discardInfo := getDiscardInfo(tf) dfi.discard = func(src pointer) { sp := src.getPointer() if !sp.isNil() { discardInfo.discard(sp) } } } case reflect.Map: switch { case isPointer || isSlice: panic(fmt.Sprintf("%v.%s cannot be a pointer to a map or a slice of map values", t, f.Name)) default: // E.g., map[K]V if tf.Elem().Kind() == reflect.Ptr { // Proto struct (e.g., *T) dfi.discard = func(src pointer) { sm := src.asPointerTo(tf).Elem() if sm.Len() == 0 { return } for _, key := range sm.MapKeys() { val := sm.MapIndex(key) DiscardUnknown(val.Interface().(Message)) } } } else { dfi.discard = func(pointer) {} // Noop } } case reflect.Interface: // Must be oneof field. switch { case isPointer || isSlice: panic(fmt.Sprintf("%v.%s cannot be a pointer to a interface or a slice of interface values", t, f.Name)) default: // E.g., interface{} // TODO: Make this faster? dfi.discard = func(src pointer) { su := src.asPointerTo(tf).Elem() if !su.IsNil() { sv := su.Elem().Elem().Field(0) if sv.Kind() == reflect.Ptr && sv.IsNil() { return } switch sv.Type().Kind() { case reflect.Ptr: // Proto struct (e.g., *T) DiscardUnknown(sv.Interface().(Message)) } } } } default: continue } di.fields = append(di.fields, dfi) } di.unrecognized = invalidField if f, ok := t.FieldByName("XXX_unrecognized"); ok { if f.Type != reflect.TypeOf([]byte{}) { panic("expected XXX_unrecognized to be of type []byte") } di.unrecognized = toField(&f) } atomic.StoreInt32(&di.initialized, 1) } func discardLegacy(m Message) { v := reflect.ValueOf(m) if v.Kind() != reflect.Ptr || v.IsNil() { return } v = v.Elem() if v.Kind() != reflect.Struct { return } t := v.Type() for i := 0; i < v.NumField(); i++ { f := t.Field(i) if strings.HasPrefix(f.Name, "XXX_") { continue } vf := v.Field(i) tf := f.Type // Unwrap tf to get its most basic type. var isPointer, isSlice bool if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 { isSlice = true tf = tf.Elem() } if tf.Kind() == reflect.Ptr { isPointer = true tf = tf.Elem() } if isPointer && isSlice && tf.Kind() != reflect.Struct { panic(fmt.Sprintf("%T.%s cannot be a slice of pointers to primitive types", m, f.Name)) } switch tf.Kind() { case reflect.Struct: switch { case !isPointer: panic(fmt.Sprintf("%T.%s cannot be a direct struct value", m, f.Name)) case isSlice: // E.g., []*pb.T for j := 0; j < vf.Len(); j++ { discardLegacy(vf.Index(j).Interface().(Message)) } default: // E.g., *pb.T discardLegacy(vf.Interface().(Message)) } case reflect.Map: switch { case isPointer || isSlice: panic(fmt.Sprintf("%T.%s cannot be a pointer to a map or a slice of map values", m, f.Name)) default: // E.g., map[K]V tv := vf.Type().Elem() if tv.Kind() == reflect.Ptr && tv.Implements(protoMessageType) { // Proto struct (e.g., *T) for _, key := range vf.MapKeys() { val := vf.MapIndex(key) discardLegacy(val.Interface().(Message)) } } } case reflect.Interface: // Must be oneof field. switch { case isPointer || isSlice: panic(fmt.Sprintf("%T.%s cannot be a pointer to a interface or a slice of interface values", m, f.Name)) default: // E.g., test_proto.isCommunique_Union interface if !vf.IsNil() && f.Tag.Get("protobuf_oneof") != "" { vf = vf.Elem() // E.g., *test_proto.Communique_Msg if !vf.IsNil() { vf = vf.Elem() // E.g., test_proto.Communique_Msg vf = vf.Field(0) // E.g., Proto struct (e.g., *T) or primitive value if vf.Kind() == reflect.Ptr { discardLegacy(vf.Interface().(Message)) } } } } } } if vf := v.FieldByName("XXX_unrecognized"); vf.IsValid() { if vf.Type() != reflect.TypeOf([]byte{}) { panic("expected XXX_unrecognized to be of type []byte") } vf.Set(reflect.ValueOf([]byte(nil))) } // For proto2 messages, only discard unknown fields in message extensions // that have been accessed via GetExtension. if em, err := extendable(m); err == nil { // Ignore lock since discardLegacy is not concurrency safe. emm, _ := em.extensionsRead() for _, mx := range emm { if m, ok := mx.value.(Message); ok { discardLegacy(m) } } } } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/duration.go ================================================ // Go support for Protocol Buffers - Google's data interchange format // // Copyright 2016 The Go Authors. All rights reserved. // https://github.com/golang/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto // This file implements conversions between google.protobuf.Duration // and time.Duration. import ( "errors" "fmt" "time" ) const ( // Range of a Duration in seconds, as specified in // google/protobuf/duration.proto. This is about 10,000 years in seconds. maxSeconds = int64(10000 * 365.25 * 24 * 60 * 60) minSeconds = -maxSeconds ) // validateDuration determines whether the Duration is valid according to the // definition in google/protobuf/duration.proto. A valid Duration // may still be too large to fit into a time.Duration (the range of Duration // is about 10,000 years, and the range of time.Duration is about 290). func validateDuration(d *duration) error { if d == nil { return errors.New("duration: nil Duration") } if d.Seconds < minSeconds || d.Seconds > maxSeconds { return fmt.Errorf("duration: %#v: seconds out of range", d) } if d.Nanos <= -1e9 || d.Nanos >= 1e9 { return fmt.Errorf("duration: %#v: nanos out of range", d) } // Seconds and Nanos must have the same sign, unless d.Nanos is zero. if (d.Seconds < 0 && d.Nanos > 0) || (d.Seconds > 0 && d.Nanos < 0) { return fmt.Errorf("duration: %#v: seconds and nanos have different signs", d) } return nil } // DurationFromProto converts a Duration to a time.Duration. DurationFromProto // returns an error if the Duration is invalid or is too large to be // represented in a time.Duration. func durationFromProto(p *duration) (time.Duration, error) { if err := validateDuration(p); err != nil { return 0, err } d := time.Duration(p.Seconds) * time.Second if int64(d/time.Second) != p.Seconds { return 0, fmt.Errorf("duration: %#v is out of range for time.Duration", p) } if p.Nanos != 0 { d += time.Duration(p.Nanos) if (d < 0) != (p.Nanos < 0) { return 0, fmt.Errorf("duration: %#v is out of range for time.Duration", p) } } return d, nil } // DurationProto converts a time.Duration to a Duration. func durationProto(d time.Duration) *duration { nanos := d.Nanoseconds() secs := nanos / 1e9 nanos -= secs * 1e9 return &duration{ Seconds: secs, Nanos: int32(nanos), } } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/duration_gogo.go ================================================ // Protocol Buffers for Go with Gadgets // // Copyright (c) 2016, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto import ( "reflect" "time" ) var durationType = reflect.TypeOf((*time.Duration)(nil)).Elem() type duration struct { Seconds int64 `protobuf:"varint,1,opt,name=seconds,proto3" json:"seconds,omitempty"` Nanos int32 `protobuf:"varint,2,opt,name=nanos,proto3" json:"nanos,omitempty"` } func (m *duration) Reset() { *m = duration{} } func (*duration) ProtoMessage() {} func (*duration) String() string { return "duration" } func init() { RegisterType((*duration)(nil), "gogo.protobuf.proto.duration") } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/encode.go ================================================ // Go support for Protocol Buffers - Google's data interchange format // // Copyright 2010 The Go Authors. All rights reserved. // https://github.com/golang/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto /* * Routines for encoding data into the wire format for protocol buffers. */ import ( "errors" "reflect" ) var ( // errRepeatedHasNil is the error returned if Marshal is called with // a struct with a repeated field containing a nil element. errRepeatedHasNil = errors.New("proto: repeated field has nil element") // errOneofHasNil is the error returned if Marshal is called with // a struct with a oneof field containing a nil element. errOneofHasNil = errors.New("proto: oneof field has nil value") // ErrNil is the error returned if Marshal is called with nil. ErrNil = errors.New("proto: Marshal called with nil") // ErrTooLarge is the error returned if Marshal is called with a // message that encodes to >2GB. ErrTooLarge = errors.New("proto: message encodes to over 2 GB") ) // The fundamental encoders that put bytes on the wire. // Those that take integer types all accept uint64 and are // therefore of type valueEncoder. const maxVarintBytes = 10 // maximum length of a varint // EncodeVarint returns the varint encoding of x. // This is the format for the // int32, int64, uint32, uint64, bool, and enum // protocol buffer types. // Not used by the package itself, but helpful to clients // wishing to use the same encoding. func EncodeVarint(x uint64) []byte { var buf [maxVarintBytes]byte var n int for n = 0; x > 127; n++ { buf[n] = 0x80 | uint8(x&0x7F) x >>= 7 } buf[n] = uint8(x) n++ return buf[0:n] } // EncodeVarint writes a varint-encoded integer to the Buffer. // This is the format for the // int32, int64, uint32, uint64, bool, and enum // protocol buffer types. func (p *Buffer) EncodeVarint(x uint64) error { for x >= 1<<7 { p.buf = append(p.buf, uint8(x&0x7f|0x80)) x >>= 7 } p.buf = append(p.buf, uint8(x)) return nil } // SizeVarint returns the varint encoding size of an integer. func SizeVarint(x uint64) int { switch { case x < 1<<7: return 1 case x < 1<<14: return 2 case x < 1<<21: return 3 case x < 1<<28: return 4 case x < 1<<35: return 5 case x < 1<<42: return 6 case x < 1<<49: return 7 case x < 1<<56: return 8 case x < 1<<63: return 9 } return 10 } // EncodeFixed64 writes a 64-bit integer to the Buffer. // This is the format for the // fixed64, sfixed64, and double protocol buffer types. func (p *Buffer) EncodeFixed64(x uint64) error { p.buf = append(p.buf, uint8(x), uint8(x>>8), uint8(x>>16), uint8(x>>24), uint8(x>>32), uint8(x>>40), uint8(x>>48), uint8(x>>56)) return nil } // EncodeFixed32 writes a 32-bit integer to the Buffer. // This is the format for the // fixed32, sfixed32, and float protocol buffer types. func (p *Buffer) EncodeFixed32(x uint64) error { p.buf = append(p.buf, uint8(x), uint8(x>>8), uint8(x>>16), uint8(x>>24)) return nil } // EncodeZigzag64 writes a zigzag-encoded 64-bit integer // to the Buffer. // This is the format used for the sint64 protocol buffer type. func (p *Buffer) EncodeZigzag64(x uint64) error { // use signed number to get arithmetic right shift. return p.EncodeVarint(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } // EncodeZigzag32 writes a zigzag-encoded 32-bit integer // to the Buffer. // This is the format used for the sint32 protocol buffer type. func (p *Buffer) EncodeZigzag32(x uint64) error { // use signed number to get arithmetic right shift. return p.EncodeVarint(uint64((uint32(x) << 1) ^ uint32((int32(x) >> 31)))) } // EncodeRawBytes writes a count-delimited byte buffer to the Buffer. // This is the format used for the bytes protocol buffer // type and for embedded messages. func (p *Buffer) EncodeRawBytes(b []byte) error { p.EncodeVarint(uint64(len(b))) p.buf = append(p.buf, b...) return nil } // EncodeStringBytes writes an encoded string to the Buffer. // This is the format used for the proto2 string type. func (p *Buffer) EncodeStringBytes(s string) error { p.EncodeVarint(uint64(len(s))) p.buf = append(p.buf, s...) return nil } // Marshaler is the interface representing objects that can marshal themselves. type Marshaler interface { Marshal() ([]byte, error) } // EncodeMessage writes the protocol buffer to the Buffer, // prefixed by a varint-encoded length. func (p *Buffer) EncodeMessage(pb Message) error { siz := Size(pb) sizVar := SizeVarint(uint64(siz)) p.grow(siz + sizVar) p.EncodeVarint(uint64(siz)) return p.Marshal(pb) } // All protocol buffer fields are nillable, but be careful. func isNil(v reflect.Value) bool { switch v.Kind() { case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: return v.IsNil() } return false } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/encode_gogo.go ================================================ // Protocol Buffers for Go with Gadgets // // Copyright (c) 2013, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto func NewRequiredNotSetError(field string) *RequiredNotSetError { return &RequiredNotSetError{field} } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/equal.go ================================================ // Go support for Protocol Buffers - Google's data interchange format // // Copyright 2011 The Go Authors. All rights reserved. // https://github.com/golang/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Protocol buffer comparison. package proto import ( "bytes" "log" "reflect" "strings" ) /* Equal returns true iff protocol buffers a and b are equal. The arguments must both be pointers to protocol buffer structs. Equality is defined in this way: - Two messages are equal iff they are the same type, corresponding fields are equal, unknown field sets are equal, and extensions sets are equal. - Two set scalar fields are equal iff their values are equal. If the fields are of a floating-point type, remember that NaN != x for all x, including NaN. If the message is defined in a proto3 .proto file, fields are not "set"; specifically, zero length proto3 "bytes" fields are equal (nil == {}). - Two repeated fields are equal iff their lengths are the same, and their corresponding elements are equal. Note a "bytes" field, although represented by []byte, is not a repeated field and the rule for the scalar fields described above applies. - Two unset fields are equal. - Two unknown field sets are equal if their current encoded state is equal. - Two extension sets are equal iff they have corresponding elements that are pairwise equal. - Two map fields are equal iff their lengths are the same, and they contain the same set of elements. Zero-length map fields are equal. - Every other combination of things are not equal. The return value is undefined if a and b are not protocol buffers. */ func Equal(a, b Message) bool { if a == nil || b == nil { return a == b } v1, v2 := reflect.ValueOf(a), reflect.ValueOf(b) if v1.Type() != v2.Type() { return false } if v1.Kind() == reflect.Ptr { if v1.IsNil() { return v2.IsNil() } if v2.IsNil() { return false } v1, v2 = v1.Elem(), v2.Elem() } if v1.Kind() != reflect.Struct { return false } return equalStruct(v1, v2) } // v1 and v2 are known to have the same type. func equalStruct(v1, v2 reflect.Value) bool { sprop := GetProperties(v1.Type()) for i := 0; i < v1.NumField(); i++ { f := v1.Type().Field(i) if strings.HasPrefix(f.Name, "XXX_") { continue } f1, f2 := v1.Field(i), v2.Field(i) if f.Type.Kind() == reflect.Ptr { if n1, n2 := f1.IsNil(), f2.IsNil(); n1 && n2 { // both unset continue } else if n1 != n2 { // set/unset mismatch return false } f1, f2 = f1.Elem(), f2.Elem() } if !equalAny(f1, f2, sprop.Prop[i]) { return false } } if em1 := v1.FieldByName("XXX_InternalExtensions"); em1.IsValid() { em2 := v2.FieldByName("XXX_InternalExtensions") if !equalExtensions(v1.Type(), em1.Interface().(XXX_InternalExtensions), em2.Interface().(XXX_InternalExtensions)) { return false } } if em1 := v1.FieldByName("XXX_extensions"); em1.IsValid() { em2 := v2.FieldByName("XXX_extensions") if !equalExtMap(v1.Type(), em1.Interface().(map[int32]Extension), em2.Interface().(map[int32]Extension)) { return false } } uf := v1.FieldByName("XXX_unrecognized") if !uf.IsValid() { return true } u1 := uf.Bytes() u2 := v2.FieldByName("XXX_unrecognized").Bytes() return bytes.Equal(u1, u2) } // v1 and v2 are known to have the same type. // prop may be nil. func equalAny(v1, v2 reflect.Value, prop *Properties) bool { if v1.Type() == protoMessageType { m1, _ := v1.Interface().(Message) m2, _ := v2.Interface().(Message) return Equal(m1, m2) } switch v1.Kind() { case reflect.Bool: return v1.Bool() == v2.Bool() case reflect.Float32, reflect.Float64: return v1.Float() == v2.Float() case reflect.Int32, reflect.Int64: return v1.Int() == v2.Int() case reflect.Interface: // Probably a oneof field; compare the inner values. n1, n2 := v1.IsNil(), v2.IsNil() if n1 || n2 { return n1 == n2 } e1, e2 := v1.Elem(), v2.Elem() if e1.Type() != e2.Type() { return false } return equalAny(e1, e2, nil) case reflect.Map: if v1.Len() != v2.Len() { return false } for _, key := range v1.MapKeys() { val2 := v2.MapIndex(key) if !val2.IsValid() { // This key was not found in the second map. return false } if !equalAny(v1.MapIndex(key), val2, nil) { return false } } return true case reflect.Ptr: // Maps may have nil values in them, so check for nil. if v1.IsNil() && v2.IsNil() { return true } if v1.IsNil() != v2.IsNil() { return false } return equalAny(v1.Elem(), v2.Elem(), prop) case reflect.Slice: if v1.Type().Elem().Kind() == reflect.Uint8 { // short circuit: []byte // Edge case: if this is in a proto3 message, a zero length // bytes field is considered the zero value. if prop != nil && prop.proto3 && v1.Len() == 0 && v2.Len() == 0 { return true } if v1.IsNil() != v2.IsNil() { return false } return bytes.Equal(v1.Interface().([]byte), v2.Interface().([]byte)) } if v1.Len() != v2.Len() { return false } for i := 0; i < v1.Len(); i++ { if !equalAny(v1.Index(i), v2.Index(i), prop) { return false } } return true case reflect.String: return v1.Interface().(string) == v2.Interface().(string) case reflect.Struct: return equalStruct(v1, v2) case reflect.Uint32, reflect.Uint64: return v1.Uint() == v2.Uint() } // unknown type, so not a protocol buffer log.Printf("proto: don't know how to compare %v", v1) return false } // base is the struct type that the extensions are based on. // x1 and x2 are InternalExtensions. func equalExtensions(base reflect.Type, x1, x2 XXX_InternalExtensions) bool { em1, _ := x1.extensionsRead() em2, _ := x2.extensionsRead() return equalExtMap(base, em1, em2) } func equalExtMap(base reflect.Type, em1, em2 map[int32]Extension) bool { if len(em1) != len(em2) { return false } for extNum, e1 := range em1 { e2, ok := em2[extNum] if !ok { return false } m1, m2 := e1.value, e2.value if m1 == nil && m2 == nil { // Both have only encoded form. if bytes.Equal(e1.enc, e2.enc) { continue } // The bytes are different, but the extensions might still be // equal. We need to decode them to compare. } if m1 != nil && m2 != nil { // Both are unencoded. if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) { return false } continue } // At least one is encoded. To do a semantically correct comparison // we need to unmarshal them first. var desc *ExtensionDesc if m := extensionMaps[base]; m != nil { desc = m[extNum] } if desc == nil { // If both have only encoded form and the bytes are the same, // it is handled above. We get here when the bytes are different. // We don't know how to decode it, so just compare them as byte // slices. log.Printf("proto: don't know how to compare extension %d of %v", extNum, base) return false } var err error if m1 == nil { m1, err = decodeExtension(e1.enc, desc) } if m2 == nil && err == nil { m2, err = decodeExtension(e2.enc, desc) } if err != nil { // The encoded form is invalid. log.Printf("proto: badly encoded extension %d of %v: %v", extNum, base, err) return false } if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) { return false } } return true } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/extensions.go ================================================ // Go support for Protocol Buffers - Google's data interchange format // // Copyright 2010 The Go Authors. All rights reserved. // https://github.com/golang/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto /* * Types and routines for supporting protocol buffer extensions. */ import ( "errors" "fmt" "io" "reflect" "strconv" "sync" ) // ErrMissingExtension is the error returned by GetExtension if the named extension is not in the message. var ErrMissingExtension = errors.New("proto: missing extension") // ExtensionRange represents a range of message extensions for a protocol buffer. // Used in code generated by the protocol compiler. type ExtensionRange struct { Start, End int32 // both inclusive } // extendableProto is an interface implemented by any protocol buffer generated by the current // proto compiler that may be extended. type extendableProto interface { Message ExtensionRangeArray() []ExtensionRange extensionsWrite() map[int32]Extension extensionsRead() (map[int32]Extension, sync.Locker) } // extendableProtoV1 is an interface implemented by a protocol buffer generated by the previous // version of the proto compiler that may be extended. type extendableProtoV1 interface { Message ExtensionRangeArray() []ExtensionRange ExtensionMap() map[int32]Extension } // extensionAdapter is a wrapper around extendableProtoV1 that implements extendableProto. type extensionAdapter struct { extendableProtoV1 } func (e extensionAdapter) extensionsWrite() map[int32]Extension { return e.ExtensionMap() } func (e extensionAdapter) extensionsRead() (map[int32]Extension, sync.Locker) { return e.ExtensionMap(), notLocker{} } // notLocker is a sync.Locker whose Lock and Unlock methods are nops. type notLocker struct{} func (n notLocker) Lock() {} func (n notLocker) Unlock() {} // extendable returns the extendableProto interface for the given generated proto message. // If the proto message has the old extension format, it returns a wrapper that implements // the extendableProto interface. func extendable(p interface{}) (extendableProto, error) { switch p := p.(type) { case extendableProto: if isNilPtr(p) { return nil, fmt.Errorf("proto: nil %T is not extendable", p) } return p, nil case extendableProtoV1: if isNilPtr(p) { return nil, fmt.Errorf("proto: nil %T is not extendable", p) } return extensionAdapter{p}, nil case extensionsBytes: return slowExtensionAdapter{p}, nil } // Don't allocate a specific error containing %T: // this is the hot path for Clone and MarshalText. return nil, errNotExtendable } var errNotExtendable = errors.New("proto: not an extendable proto.Message") func isNilPtr(x interface{}) bool { v := reflect.ValueOf(x) return v.Kind() == reflect.Ptr && v.IsNil() } // XXX_InternalExtensions is an internal representation of proto extensions. // // Each generated message struct type embeds an anonymous XXX_InternalExtensions field, // thus gaining the unexported 'extensions' method, which can be called only from the proto package. // // The methods of XXX_InternalExtensions are not concurrency safe in general, // but calls to logically read-only methods such as has and get may be executed concurrently. type XXX_InternalExtensions struct { // The struct must be indirect so that if a user inadvertently copies a // generated message and its embedded XXX_InternalExtensions, they // avoid the mayhem of a copied mutex. // // The mutex serializes all logically read-only operations to p.extensionMap. // It is up to the client to ensure that write operations to p.extensionMap are // mutually exclusive with other accesses. p *struct { mu sync.Mutex extensionMap map[int32]Extension } } // extensionsWrite returns the extension map, creating it on first use. func (e *XXX_InternalExtensions) extensionsWrite() map[int32]Extension { if e.p == nil { e.p = new(struct { mu sync.Mutex extensionMap map[int32]Extension }) e.p.extensionMap = make(map[int32]Extension) } return e.p.extensionMap } // extensionsRead returns the extensions map for read-only use. It may be nil. // The caller must hold the returned mutex's lock when accessing Elements within the map. func (e *XXX_InternalExtensions) extensionsRead() (map[int32]Extension, sync.Locker) { if e.p == nil { return nil, nil } return e.p.extensionMap, &e.p.mu } // ExtensionDesc represents an extension specification. // Used in generated code from the protocol compiler. type ExtensionDesc struct { ExtendedType Message // nil pointer to the type that is being extended ExtensionType interface{} // nil pointer to the extension type Field int32 // field number Name string // fully-qualified name of extension, for text formatting Tag string // protobuf tag style Filename string // name of the file in which the extension is defined } func (ed *ExtensionDesc) repeated() bool { t := reflect.TypeOf(ed.ExtensionType) return t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8 } // Extension represents an extension in a message. type Extension struct { // When an extension is stored in a message using SetExtension // only desc and value are set. When the message is marshaled // enc will be set to the encoded form of the message. // // When a message is unmarshaled and contains extensions, each // extension will have only enc set. When such an extension is // accessed using GetExtension (or GetExtensions) desc and value // will be set. desc *ExtensionDesc value interface{} enc []byte } // SetRawExtension is for testing only. func SetRawExtension(base Message, id int32, b []byte) { if ebase, ok := base.(extensionsBytes); ok { clearExtension(base, id) ext := ebase.GetExtensions() *ext = append(*ext, b...) return } epb, err := extendable(base) if err != nil { return } extmap := epb.extensionsWrite() extmap[id] = Extension{enc: b} } // isExtensionField returns true iff the given field number is in an extension range. func isExtensionField(pb extendableProto, field int32) bool { for _, er := range pb.ExtensionRangeArray() { if er.Start <= field && field <= er.End { return true } } return false } // checkExtensionTypes checks that the given extension is valid for pb. func checkExtensionTypes(pb extendableProto, extension *ExtensionDesc) error { var pbi interface{} = pb // Check the extended type. if ea, ok := pbi.(extensionAdapter); ok { pbi = ea.extendableProtoV1 } if ea, ok := pbi.(slowExtensionAdapter); ok { pbi = ea.extensionsBytes } if a, b := reflect.TypeOf(pbi), reflect.TypeOf(extension.ExtendedType); a != b { return fmt.Errorf("proto: bad extended type; %v does not extend %v", b, a) } // Check the range. if !isExtensionField(pb, extension.Field) { return errors.New("proto: bad extension number; not in declared ranges") } return nil } // extPropKey is sufficient to uniquely identify an extension. type extPropKey struct { base reflect.Type field int32 } var extProp = struct { sync.RWMutex m map[extPropKey]*Properties }{ m: make(map[extPropKey]*Properties), } func extensionProperties(ed *ExtensionDesc) *Properties { key := extPropKey{base: reflect.TypeOf(ed.ExtendedType), field: ed.Field} extProp.RLock() if prop, ok := extProp.m[key]; ok { extProp.RUnlock() return prop } extProp.RUnlock() extProp.Lock() defer extProp.Unlock() // Check again. if prop, ok := extProp.m[key]; ok { return prop } prop := new(Properties) prop.Init(reflect.TypeOf(ed.ExtensionType), "unknown_name", ed.Tag, nil) extProp.m[key] = prop return prop } // HasExtension returns whether the given extension is present in pb. func HasExtension(pb Message, extension *ExtensionDesc) bool { if epb, doki := pb.(extensionsBytes); doki { ext := epb.GetExtensions() buf := *ext o := 0 for o < len(buf) { tag, n := DecodeVarint(buf[o:]) fieldNum := int32(tag >> 3) if int32(fieldNum) == extension.Field { return true } wireType := int(tag & 0x7) o += n l, err := size(buf[o:], wireType) if err != nil { return false } o += l } return false } // TODO: Check types, field numbers, etc.? epb, err := extendable(pb) if err != nil { return false } extmap, mu := epb.extensionsRead() if extmap == nil { return false } mu.Lock() _, ok := extmap[extension.Field] mu.Unlock() return ok } // ClearExtension removes the given extension from pb. func ClearExtension(pb Message, extension *ExtensionDesc) { clearExtension(pb, extension.Field) } func clearExtension(pb Message, fieldNum int32) { if epb, ok := pb.(extensionsBytes); ok { offset := 0 for offset != -1 { offset = deleteExtension(epb, fieldNum, offset) } return } epb, err := extendable(pb) if err != nil { return } // TODO: Check types, field numbers, etc.? extmap := epb.extensionsWrite() delete(extmap, fieldNum) } // GetExtension retrieves a proto2 extended field from pb. // // If the descriptor is type complete (i.e., ExtensionDesc.ExtensionType is non-nil), // then GetExtension parses the encoded field and returns a Go value of the specified type. // If the field is not present, then the default value is returned (if one is specified), // otherwise ErrMissingExtension is reported. // // If the descriptor is not type complete (i.e., ExtensionDesc.ExtensionType is nil), // then GetExtension returns the raw encoded bytes of the field extension. func GetExtension(pb Message, extension *ExtensionDesc) (interface{}, error) { if epb, doki := pb.(extensionsBytes); doki { ext := epb.GetExtensions() return decodeExtensionFromBytes(extension, *ext) } epb, err := extendable(pb) if err != nil { return nil, err } if extension.ExtendedType != nil { // can only check type if this is a complete descriptor if cerr := checkExtensionTypes(epb, extension); cerr != nil { return nil, cerr } } emap, mu := epb.extensionsRead() if emap == nil { return defaultExtensionValue(extension) } mu.Lock() defer mu.Unlock() e, ok := emap[extension.Field] if !ok { // defaultExtensionValue returns the default value or // ErrMissingExtension if there is no default. return defaultExtensionValue(extension) } if e.value != nil { // Already decoded. Check the descriptor, though. if e.desc != extension { // This shouldn't happen. If it does, it means that // GetExtension was called twice with two different // descriptors with the same field number. return nil, errors.New("proto: descriptor conflict") } return e.value, nil } if extension.ExtensionType == nil { // incomplete descriptor return e.enc, nil } v, err := decodeExtension(e.enc, extension) if err != nil { return nil, err } // Remember the decoded version and drop the encoded version. // That way it is safe to mutate what we return. e.value = v e.desc = extension e.enc = nil emap[extension.Field] = e return e.value, nil } // defaultExtensionValue returns the default value for extension. // If no default for an extension is defined ErrMissingExtension is returned. func defaultExtensionValue(extension *ExtensionDesc) (interface{}, error) { if extension.ExtensionType == nil { // incomplete descriptor, so no default return nil, ErrMissingExtension } t := reflect.TypeOf(extension.ExtensionType) props := extensionProperties(extension) sf, _, err := fieldDefault(t, props) if err != nil { return nil, err } if sf == nil || sf.value == nil { // There is no default value. return nil, ErrMissingExtension } if t.Kind() != reflect.Ptr { // We do not need to return a Ptr, we can directly return sf.value. return sf.value, nil } // We need to return an interface{} that is a pointer to sf.value. value := reflect.New(t).Elem() value.Set(reflect.New(value.Type().Elem())) if sf.kind == reflect.Int32 { // We may have an int32 or an enum, but the underlying data is int32. // Since we can't set an int32 into a non int32 reflect.value directly // set it as a int32. value.Elem().SetInt(int64(sf.value.(int32))) } else { value.Elem().Set(reflect.ValueOf(sf.value)) } return value.Interface(), nil } // decodeExtension decodes an extension encoded in b. func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) { t := reflect.TypeOf(extension.ExtensionType) unmarshal := typeUnmarshaler(t, extension.Tag) // t is a pointer to a struct, pointer to basic type or a slice. // Allocate space to store the pointer/slice. value := reflect.New(t).Elem() var err error for { x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] wire := int(x) & 7 b, err = unmarshal(b, valToPointer(value.Addr()), wire) if err != nil { return nil, err } if len(b) == 0 { break } } return value.Interface(), nil } // GetExtensions returns a slice of the extensions present in pb that are also listed in es. // The returned slice has the same length as es; missing extensions will appear as nil elements. func GetExtensions(pb Message, es []*ExtensionDesc) (extensions []interface{}, err error) { epb, err := extendable(pb) if err != nil { return nil, err } extensions = make([]interface{}, len(es)) for i, e := range es { extensions[i], err = GetExtension(epb, e) if err == ErrMissingExtension { err = nil } if err != nil { return } } return } // ExtensionDescs returns a new slice containing pb's extension descriptors, in undefined order. // For non-registered extensions, ExtensionDescs returns an incomplete descriptor containing // just the Field field, which defines the extension's field number. func ExtensionDescs(pb Message) ([]*ExtensionDesc, error) { epb, err := extendable(pb) if err != nil { return nil, err } registeredExtensions := RegisteredExtensions(pb) emap, mu := epb.extensionsRead() if emap == nil { return nil, nil } mu.Lock() defer mu.Unlock() extensions := make([]*ExtensionDesc, 0, len(emap)) for extid, e := range emap { desc := e.desc if desc == nil { desc = registeredExtensions[extid] if desc == nil { desc = &ExtensionDesc{Field: extid} } } extensions = append(extensions, desc) } return extensions, nil } // SetExtension sets the specified extension of pb to the specified value. func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error { if epb, ok := pb.(extensionsBytes); ok { ClearExtension(pb, extension) newb, err := encodeExtension(extension, value) if err != nil { return err } bb := epb.GetExtensions() *bb = append(*bb, newb...) return nil } epb, err := extendable(pb) if err != nil { return err } if err := checkExtensionTypes(epb, extension); err != nil { return err } typ := reflect.TypeOf(extension.ExtensionType) if typ != reflect.TypeOf(value) { return fmt.Errorf("proto: bad extension value type. got: %T, want: %T", value, extension.ExtensionType) } // nil extension values need to be caught early, because the // encoder can't distinguish an ErrNil due to a nil extension // from an ErrNil due to a missing field. Extensions are // always optional, so the encoder would just swallow the error // and drop all the extensions from the encoded message. if reflect.ValueOf(value).IsNil() { return fmt.Errorf("proto: SetExtension called with nil value of type %T", value) } extmap := epb.extensionsWrite() extmap[extension.Field] = Extension{desc: extension, value: value} return nil } // ClearAllExtensions clears all extensions from pb. func ClearAllExtensions(pb Message) { if epb, doki := pb.(extensionsBytes); doki { ext := epb.GetExtensions() *ext = []byte{} return } epb, err := extendable(pb) if err != nil { return } m := epb.extensionsWrite() for k := range m { delete(m, k) } } // A global registry of extensions. // The generated code will register the generated descriptors by calling RegisterExtension. var extensionMaps = make(map[reflect.Type]map[int32]*ExtensionDesc) // RegisterExtension is called from the generated code. func RegisterExtension(desc *ExtensionDesc) { st := reflect.TypeOf(desc.ExtendedType).Elem() m := extensionMaps[st] if m == nil { m = make(map[int32]*ExtensionDesc) extensionMaps[st] = m } if _, ok := m[desc.Field]; ok { panic("proto: duplicate extension registered: " + st.String() + " " + strconv.Itoa(int(desc.Field))) } m[desc.Field] = desc } // RegisteredExtensions returns a map of the registered extensions of a // protocol buffer struct, indexed by the extension number. // The argument pb should be a nil pointer to the struct type. func RegisteredExtensions(pb Message) map[int32]*ExtensionDesc { return extensionMaps[reflect.TypeOf(pb).Elem()] } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/extensions_gogo.go ================================================ // Protocol Buffers for Go with Gadgets // // Copyright (c) 2013, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto import ( "bytes" "errors" "fmt" "io" "reflect" "sort" "strings" "sync" ) type extensionsBytes interface { Message ExtensionRangeArray() []ExtensionRange GetExtensions() *[]byte } type slowExtensionAdapter struct { extensionsBytes } func (s slowExtensionAdapter) extensionsWrite() map[int32]Extension { panic("Please report a bug to github.com/gogo/protobuf if you see this message: Writing extensions is not supported for extensions stored in a byte slice field.") } func (s slowExtensionAdapter) extensionsRead() (map[int32]Extension, sync.Locker) { b := s.GetExtensions() m, err := BytesToExtensionsMap(*b) if err != nil { panic(err) } return m, notLocker{} } func GetBoolExtension(pb Message, extension *ExtensionDesc, ifnotset bool) bool { if reflect.ValueOf(pb).IsNil() { return ifnotset } value, err := GetExtension(pb, extension) if err != nil { return ifnotset } if value == nil { return ifnotset } if value.(*bool) == nil { return ifnotset } return *(value.(*bool)) } func (this *Extension) Equal(that *Extension) bool { if err := this.Encode(); err != nil { return false } if err := that.Encode(); err != nil { return false } return bytes.Equal(this.enc, that.enc) } func (this *Extension) Compare(that *Extension) int { if err := this.Encode(); err != nil { return 1 } if err := that.Encode(); err != nil { return -1 } return bytes.Compare(this.enc, that.enc) } func SizeOfInternalExtension(m extendableProto) (n int) { info := getMarshalInfo(reflect.TypeOf(m)) return info.sizeV1Extensions(m.extensionsWrite()) } type sortableMapElem struct { field int32 ext Extension } func newSortableExtensionsFromMap(m map[int32]Extension) sortableExtensions { s := make(sortableExtensions, 0, len(m)) for k, v := range m { s = append(s, &sortableMapElem{field: k, ext: v}) } return s } type sortableExtensions []*sortableMapElem func (this sortableExtensions) Len() int { return len(this) } func (this sortableExtensions) Swap(i, j int) { this[i], this[j] = this[j], this[i] } func (this sortableExtensions) Less(i, j int) bool { return this[i].field < this[j].field } func (this sortableExtensions) String() string { sort.Sort(this) ss := make([]string, len(this)) for i := range this { ss[i] = fmt.Sprintf("%d: %v", this[i].field, this[i].ext) } return "map[" + strings.Join(ss, ",") + "]" } func StringFromInternalExtension(m extendableProto) string { return StringFromExtensionsMap(m.extensionsWrite()) } func StringFromExtensionsMap(m map[int32]Extension) string { return newSortableExtensionsFromMap(m).String() } func StringFromExtensionsBytes(ext []byte) string { m, err := BytesToExtensionsMap(ext) if err != nil { panic(err) } return StringFromExtensionsMap(m) } func EncodeInternalExtension(m extendableProto, data []byte) (n int, err error) { return EncodeExtensionMap(m.extensionsWrite(), data) } func EncodeInternalExtensionBackwards(m extendableProto, data []byte) (n int, err error) { return EncodeExtensionMapBackwards(m.extensionsWrite(), data) } func EncodeExtensionMap(m map[int32]Extension, data []byte) (n int, err error) { o := 0 for _, e := range m { if err := e.Encode(); err != nil { return 0, err } n := copy(data[o:], e.enc) if n != len(e.enc) { return 0, io.ErrShortBuffer } o += n } return o, nil } func EncodeExtensionMapBackwards(m map[int32]Extension, data []byte) (n int, err error) { o := 0 end := len(data) for _, e := range m { if err := e.Encode(); err != nil { return 0, err } n := copy(data[end-len(e.enc):], e.enc) if n != len(e.enc) { return 0, io.ErrShortBuffer } end -= n o += n } return o, nil } func GetRawExtension(m map[int32]Extension, id int32) ([]byte, error) { e := m[id] if err := e.Encode(); err != nil { return nil, err } return e.enc, nil } func size(buf []byte, wire int) (int, error) { switch wire { case WireVarint: _, n := DecodeVarint(buf) return n, nil case WireFixed64: return 8, nil case WireBytes: v, n := DecodeVarint(buf) return int(v) + n, nil case WireFixed32: return 4, nil case WireStartGroup: offset := 0 for { u, n := DecodeVarint(buf[offset:]) fwire := int(u & 0x7) offset += n if fwire == WireEndGroup { return offset, nil } s, err := size(buf[offset:], wire) if err != nil { return 0, err } offset += s } } return 0, fmt.Errorf("proto: can't get size for unknown wire type %d", wire) } func BytesToExtensionsMap(buf []byte) (map[int32]Extension, error) { m := make(map[int32]Extension) i := 0 for i < len(buf) { tag, n := DecodeVarint(buf[i:]) if n <= 0 { return nil, fmt.Errorf("unable to decode varint") } fieldNum := int32(tag >> 3) wireType := int(tag & 0x7) l, err := size(buf[i+n:], wireType) if err != nil { return nil, err } end := i + int(l) + n m[int32(fieldNum)] = Extension{enc: buf[i:end]} i = end } return m, nil } func NewExtension(e []byte) Extension { ee := Extension{enc: make([]byte, len(e))} copy(ee.enc, e) return ee } func AppendExtension(e Message, tag int32, buf []byte) { if ee, eok := e.(extensionsBytes); eok { ext := ee.GetExtensions() *ext = append(*ext, buf...) return } if ee, eok := e.(extendableProto); eok { m := ee.extensionsWrite() ext := m[int32(tag)] // may be missing ext.enc = append(ext.enc, buf...) m[int32(tag)] = ext } } func encodeExtension(extension *ExtensionDesc, value interface{}) ([]byte, error) { u := getMarshalInfo(reflect.TypeOf(extension.ExtendedType)) ei := u.getExtElemInfo(extension) v := value p := toAddrPointer(&v, ei.isptr) siz := ei.sizer(p, SizeVarint(ei.wiretag)) buf := make([]byte, 0, siz) return ei.marshaler(buf, p, ei.wiretag, false) } func decodeExtensionFromBytes(extension *ExtensionDesc, buf []byte) (interface{}, error) { o := 0 for o < len(buf) { tag, n := DecodeVarint((buf)[o:]) fieldNum := int32(tag >> 3) wireType := int(tag & 0x7) if o+n > len(buf) { return nil, fmt.Errorf("unable to decode extension") } l, err := size((buf)[o+n:], wireType) if err != nil { return nil, err } if int32(fieldNum) == extension.Field { if o+n+l > len(buf) { return nil, fmt.Errorf("unable to decode extension") } v, err := decodeExtension((buf)[o:o+n+l], extension) if err != nil { return nil, err } return v, nil } o += n + l } return defaultExtensionValue(extension) } func (this *Extension) Encode() error { if this.enc == nil { var err error this.enc, err = encodeExtension(this.desc, this.value) if err != nil { return err } } return nil } func (this Extension) GoString() string { if err := this.Encode(); err != nil { return fmt.Sprintf("error encoding extension: %v", err) } return fmt.Sprintf("proto.NewExtension(%#v)", this.enc) } func SetUnsafeExtension(pb Message, fieldNum int32, value interface{}) error { typ := reflect.TypeOf(pb).Elem() ext, ok := extensionMaps[typ] if !ok { return fmt.Errorf("proto: bad extended type; %s is not extendable", typ.String()) } desc, ok := ext[fieldNum] if !ok { return errors.New("proto: bad extension number; not in declared ranges") } return SetExtension(pb, desc, value) } func GetUnsafeExtension(pb Message, fieldNum int32) (interface{}, error) { typ := reflect.TypeOf(pb).Elem() ext, ok := extensionMaps[typ] if !ok { return nil, fmt.Errorf("proto: bad extended type; %s is not extendable", typ.String()) } desc, ok := ext[fieldNum] if !ok { return nil, fmt.Errorf("unregistered field number %d", fieldNum) } return GetExtension(pb, desc) } func NewUnsafeXXX_InternalExtensions(m map[int32]Extension) XXX_InternalExtensions { x := &XXX_InternalExtensions{ p: new(struct { mu sync.Mutex extensionMap map[int32]Extension }), } x.p.extensionMap = m return *x } func GetUnsafeExtensionsMap(extendable Message) map[int32]Extension { pb := extendable.(extendableProto) return pb.extensionsWrite() } func deleteExtension(pb extensionsBytes, theFieldNum int32, offset int) int { ext := pb.GetExtensions() for offset < len(*ext) { tag, n1 := DecodeVarint((*ext)[offset:]) fieldNum := int32(tag >> 3) wireType := int(tag & 0x7) n2, err := size((*ext)[offset+n1:], wireType) if err != nil { panic(err) } newOffset := offset + n1 + n2 if fieldNum == theFieldNum { *ext = append((*ext)[:offset], (*ext)[newOffset:]...) return offset } offset = newOffset } return -1 } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/lib.go ================================================ // Go support for Protocol Buffers - Google's data interchange format // // Copyright 2010 The Go Authors. All rights reserved. // https://github.com/golang/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /* Package proto converts data structures to and from the wire format of protocol buffers. It works in concert with the Go source code generated for .proto files by the protocol compiler. A summary of the properties of the protocol buffer interface for a protocol buffer variable v: - Names are turned from camel_case to CamelCase for export. - There are no methods on v to set fields; just treat them as structure fields. - There are getters that return a field's value if set, and return the field's default value if unset. The getters work even if the receiver is a nil message. - The zero value for a struct is its correct initialization state. All desired fields must be set before marshaling. - A Reset() method will restore a protobuf struct to its zero state. - Non-repeated fields are pointers to the values; nil means unset. That is, optional or required field int32 f becomes F *int32. - Repeated fields are slices. - Helper functions are available to aid the setting of fields. msg.Foo = proto.String("hello") // set field - Constants are defined to hold the default values of all fields that have them. They have the form Default_StructName_FieldName. Because the getter methods handle defaulted values, direct use of these constants should be rare. - Enums are given type names and maps from names to values. Enum values are prefixed by the enclosing message's name, or by the enum's type name if it is a top-level enum. Enum types have a String method, and a Enum method to assist in message construction. - Nested messages, groups and enums have type names prefixed with the name of the surrounding message type. - Extensions are given descriptor names that start with E_, followed by an underscore-delimited list of the nested messages that contain it (if any) followed by the CamelCased name of the extension field itself. HasExtension, ClearExtension, GetExtension and SetExtension are functions for manipulating extensions. - Oneof field sets are given a single field in their message, with distinguished wrapper types for each possible field value. - Marshal and Unmarshal are functions to encode and decode the wire format. When the .proto file specifies `syntax="proto3"`, there are some differences: - Non-repeated fields of non-message type are values instead of pointers. - Enum types do not get an Enum method. The simplest way to describe this is to see an example. Given file test.proto, containing package example; enum FOO { X = 17; } message Test { required string label = 1; optional int32 type = 2 [default=77]; repeated int64 reps = 3; optional group OptionalGroup = 4 { required string RequiredField = 5; } oneof union { int32 number = 6; string name = 7; } } The resulting file, test.pb.go, is: package example import proto "github.com/gogo/protobuf/proto" import math "math" type FOO int32 const ( FOO_X FOO = 17 ) var FOO_name = map[int32]string{ 17: "X", } var FOO_value = map[string]int32{ "X": 17, } func (x FOO) Enum() *FOO { p := new(FOO) *p = x return p } func (x FOO) String() string { return proto.EnumName(FOO_name, int32(x)) } func (x *FOO) UnmarshalJSON(data []byte) error { value, err := proto.UnmarshalJSONEnum(FOO_value, data) if err != nil { return err } *x = FOO(value) return nil } type Test struct { Label *string `protobuf:"bytes,1,req,name=label" json:"label,omitempty"` Type *int32 `protobuf:"varint,2,opt,name=type,def=77" json:"type,omitempty"` Reps []int64 `protobuf:"varint,3,rep,name=reps" json:"reps,omitempty"` Optionalgroup *Test_OptionalGroup `protobuf:"group,4,opt,name=OptionalGroup" json:"optionalgroup,omitempty"` // Types that are valid to be assigned to Union: // *Test_Number // *Test_Name Union isTest_Union `protobuf_oneof:"union"` XXX_unrecognized []byte `json:"-"` } func (m *Test) Reset() { *m = Test{} } func (m *Test) String() string { return proto.CompactTextString(m) } func (*Test) ProtoMessage() {} type isTest_Union interface { isTest_Union() } type Test_Number struct { Number int32 `protobuf:"varint,6,opt,name=number"` } type Test_Name struct { Name string `protobuf:"bytes,7,opt,name=name"` } func (*Test_Number) isTest_Union() {} func (*Test_Name) isTest_Union() {} func (m *Test) GetUnion() isTest_Union { if m != nil { return m.Union } return nil } const Default_Test_Type int32 = 77 func (m *Test) GetLabel() string { if m != nil && m.Label != nil { return *m.Label } return "" } func (m *Test) GetType() int32 { if m != nil && m.Type != nil { return *m.Type } return Default_Test_Type } func (m *Test) GetOptionalgroup() *Test_OptionalGroup { if m != nil { return m.Optionalgroup } return nil } type Test_OptionalGroup struct { RequiredField *string `protobuf:"bytes,5,req" json:"RequiredField,omitempty"` } func (m *Test_OptionalGroup) Reset() { *m = Test_OptionalGroup{} } func (m *Test_OptionalGroup) String() string { return proto.CompactTextString(m) } func (m *Test_OptionalGroup) GetRequiredField() string { if m != nil && m.RequiredField != nil { return *m.RequiredField } return "" } func (m *Test) GetNumber() int32 { if x, ok := m.GetUnion().(*Test_Number); ok { return x.Number } return 0 } func (m *Test) GetName() string { if x, ok := m.GetUnion().(*Test_Name); ok { return x.Name } return "" } func init() { proto.RegisterEnum("example.FOO", FOO_name, FOO_value) } To create and play with a Test object: package main import ( "log" "github.com/gogo/protobuf/proto" pb "./example.pb" ) func main() { test := &pb.Test{ Label: proto.String("hello"), Type: proto.Int32(17), Reps: []int64{1, 2, 3}, Optionalgroup: &pb.Test_OptionalGroup{ RequiredField: proto.String("good bye"), }, Union: &pb.Test_Name{"fred"}, } data, err := proto.Marshal(test) if err != nil { log.Fatal("marshaling error: ", err) } newTest := &pb.Test{} err = proto.Unmarshal(data, newTest) if err != nil { log.Fatal("unmarshaling error: ", err) } // Now test and newTest contain the same data. if test.GetLabel() != newTest.GetLabel() { log.Fatalf("data mismatch %q != %q", test.GetLabel(), newTest.GetLabel()) } // Use a type switch to determine which oneof was set. switch u := test.Union.(type) { case *pb.Test_Number: // u.Number contains the number. case *pb.Test_Name: // u.Name contains the string. } // etc. } */ package proto import ( "encoding/json" "fmt" "log" "reflect" "sort" "strconv" "sync" ) // RequiredNotSetError is an error type returned by either Marshal or Unmarshal. // Marshal reports this when a required field is not initialized. // Unmarshal reports this when a required field is missing from the wire data. type RequiredNotSetError struct{ field string } func (e *RequiredNotSetError) Error() string { if e.field == "" { return fmt.Sprintf("proto: required field not set") } return fmt.Sprintf("proto: required field %q not set", e.field) } func (e *RequiredNotSetError) RequiredNotSet() bool { return true } type invalidUTF8Error struct{ field string } func (e *invalidUTF8Error) Error() string { if e.field == "" { return "proto: invalid UTF-8 detected" } return fmt.Sprintf("proto: field %q contains invalid UTF-8", e.field) } func (e *invalidUTF8Error) InvalidUTF8() bool { return true } // errInvalidUTF8 is a sentinel error to identify fields with invalid UTF-8. // This error should not be exposed to the external API as such errors should // be recreated with the field information. var errInvalidUTF8 = &invalidUTF8Error{} // isNonFatal reports whether the error is either a RequiredNotSet error // or a InvalidUTF8 error. func isNonFatal(err error) bool { if re, ok := err.(interface{ RequiredNotSet() bool }); ok && re.RequiredNotSet() { return true } if re, ok := err.(interface{ InvalidUTF8() bool }); ok && re.InvalidUTF8() { return true } return false } type nonFatal struct{ E error } // Merge merges err into nf and reports whether it was successful. // Otherwise it returns false for any fatal non-nil errors. func (nf *nonFatal) Merge(err error) (ok bool) { if err == nil { return true // not an error } if !isNonFatal(err) { return false // fatal error } if nf.E == nil { nf.E = err // store first instance of non-fatal error } return true } // Message is implemented by generated protocol buffer messages. type Message interface { Reset() String() string ProtoMessage() } // A Buffer is a buffer manager for marshaling and unmarshaling // protocol buffers. It may be reused between invocations to // reduce memory usage. It is not necessary to use a Buffer; // the global functions Marshal and Unmarshal create a // temporary Buffer and are fine for most applications. type Buffer struct { buf []byte // encode/decode byte stream index int // read point deterministic bool } // NewBuffer allocates a new Buffer and initializes its internal data to // the contents of the argument slice. func NewBuffer(e []byte) *Buffer { return &Buffer{buf: e} } // Reset resets the Buffer, ready for marshaling a new protocol buffer. func (p *Buffer) Reset() { p.buf = p.buf[0:0] // for reading/writing p.index = 0 // for reading } // SetBuf replaces the internal buffer with the slice, // ready for unmarshaling the contents of the slice. func (p *Buffer) SetBuf(s []byte) { p.buf = s p.index = 0 } // Bytes returns the contents of the Buffer. func (p *Buffer) Bytes() []byte { return p.buf } // SetDeterministic sets whether to use deterministic serialization. // // Deterministic serialization guarantees that for a given binary, equal // messages will always be serialized to the same bytes. This implies: // // - Repeated serialization of a message will return the same bytes. // - Different processes of the same binary (which may be executing on // different machines) will serialize equal messages to the same bytes. // // Note that the deterministic serialization is NOT canonical across // languages. It is not guaranteed to remain stable over time. It is unstable // across different builds with schema changes due to unknown fields. // Users who need canonical serialization (e.g., persistent storage in a // canonical form, fingerprinting, etc.) should define their own // canonicalization specification and implement their own serializer rather // than relying on this API. // // If deterministic serialization is requested, map entries will be sorted // by keys in lexographical order. This is an implementation detail and // subject to change. func (p *Buffer) SetDeterministic(deterministic bool) { p.deterministic = deterministic } /* * Helper routines for simplifying the creation of optional fields of basic type. */ // Bool is a helper routine that allocates a new bool value // to store v and returns a pointer to it. func Bool(v bool) *bool { return &v } // Int32 is a helper routine that allocates a new int32 value // to store v and returns a pointer to it. func Int32(v int32) *int32 { return &v } // Int is a helper routine that allocates a new int32 value // to store v and returns a pointer to it, but unlike Int32 // its argument value is an int. func Int(v int) *int32 { p := new(int32) *p = int32(v) return p } // Int64 is a helper routine that allocates a new int64 value // to store v and returns a pointer to it. func Int64(v int64) *int64 { return &v } // Float32 is a helper routine that allocates a new float32 value // to store v and returns a pointer to it. func Float32(v float32) *float32 { return &v } // Float64 is a helper routine that allocates a new float64 value // to store v and returns a pointer to it. func Float64(v float64) *float64 { return &v } // Uint32 is a helper routine that allocates a new uint32 value // to store v and returns a pointer to it. func Uint32(v uint32) *uint32 { return &v } // Uint64 is a helper routine that allocates a new uint64 value // to store v and returns a pointer to it. func Uint64(v uint64) *uint64 { return &v } // String is a helper routine that allocates a new string value // to store v and returns a pointer to it. func String(v string) *string { return &v } // EnumName is a helper function to simplify printing protocol buffer enums // by name. Given an enum map and a value, it returns a useful string. func EnumName(m map[int32]string, v int32) string { s, ok := m[v] if ok { return s } return strconv.Itoa(int(v)) } // UnmarshalJSONEnum is a helper function to simplify recovering enum int values // from their JSON-encoded representation. Given a map from the enum's symbolic // names to its int values, and a byte buffer containing the JSON-encoded // value, it returns an int32 that can be cast to the enum type by the caller. // // The function can deal with both JSON representations, numeric and symbolic. func UnmarshalJSONEnum(m map[string]int32, data []byte, enumName string) (int32, error) { if data[0] == '"' { // New style: enums are strings. var repr string if err := json.Unmarshal(data, &repr); err != nil { return -1, err } val, ok := m[repr] if !ok { return 0, fmt.Errorf("unrecognized enum %s value %q", enumName, repr) } return val, nil } // Old style: enums are ints. var val int32 if err := json.Unmarshal(data, &val); err != nil { return 0, fmt.Errorf("cannot unmarshal %#q into enum %s", data, enumName) } return val, nil } // DebugPrint dumps the encoded data in b in a debugging format with a header // including the string s. Used in testing but made available for general debugging. func (p *Buffer) DebugPrint(s string, b []byte) { var u uint64 obuf := p.buf sindex := p.index p.buf = b p.index = 0 depth := 0 fmt.Printf("\n--- %s ---\n", s) out: for { for i := 0; i < depth; i++ { fmt.Print(" ") } index := p.index if index == len(p.buf) { break } op, err := p.DecodeVarint() if err != nil { fmt.Printf("%3d: fetching op err %v\n", index, err) break out } tag := op >> 3 wire := op & 7 switch wire { default: fmt.Printf("%3d: t=%3d unknown wire=%d\n", index, tag, wire) break out case WireBytes: var r []byte r, err = p.DecodeRawBytes(false) if err != nil { break out } fmt.Printf("%3d: t=%3d bytes [%d]", index, tag, len(r)) if len(r) <= 6 { for i := 0; i < len(r); i++ { fmt.Printf(" %.2x", r[i]) } } else { for i := 0; i < 3; i++ { fmt.Printf(" %.2x", r[i]) } fmt.Printf(" ..") for i := len(r) - 3; i < len(r); i++ { fmt.Printf(" %.2x", r[i]) } } fmt.Printf("\n") case WireFixed32: u, err = p.DecodeFixed32() if err != nil { fmt.Printf("%3d: t=%3d fix32 err %v\n", index, tag, err) break out } fmt.Printf("%3d: t=%3d fix32 %d\n", index, tag, u) case WireFixed64: u, err = p.DecodeFixed64() if err != nil { fmt.Printf("%3d: t=%3d fix64 err %v\n", index, tag, err) break out } fmt.Printf("%3d: t=%3d fix64 %d\n", index, tag, u) case WireVarint: u, err = p.DecodeVarint() if err != nil { fmt.Printf("%3d: t=%3d varint err %v\n", index, tag, err) break out } fmt.Printf("%3d: t=%3d varint %d\n", index, tag, u) case WireStartGroup: fmt.Printf("%3d: t=%3d start\n", index, tag) depth++ case WireEndGroup: depth-- fmt.Printf("%3d: t=%3d end\n", index, tag) } } if depth != 0 { fmt.Printf("%3d: start-end not balanced %d\n", p.index, depth) } fmt.Printf("\n") p.buf = obuf p.index = sindex } // SetDefaults sets unset protocol buffer fields to their default values. // It only modifies fields that are both unset and have defined defaults. // It recursively sets default values in any non-nil sub-messages. func SetDefaults(pb Message) { setDefaults(reflect.ValueOf(pb), true, false) } // v is a struct. func setDefaults(v reflect.Value, recur, zeros bool) { if v.Kind() == reflect.Ptr { v = v.Elem() } defaultMu.RLock() dm, ok := defaults[v.Type()] defaultMu.RUnlock() if !ok { dm = buildDefaultMessage(v.Type()) defaultMu.Lock() defaults[v.Type()] = dm defaultMu.Unlock() } for _, sf := range dm.scalars { f := v.Field(sf.index) if !f.IsNil() { // field already set continue } dv := sf.value if dv == nil && !zeros { // no explicit default, and don't want to set zeros continue } fptr := f.Addr().Interface() // **T // TODO: Consider batching the allocations we do here. switch sf.kind { case reflect.Bool: b := new(bool) if dv != nil { *b = dv.(bool) } *(fptr.(**bool)) = b case reflect.Float32: f := new(float32) if dv != nil { *f = dv.(float32) } *(fptr.(**float32)) = f case reflect.Float64: f := new(float64) if dv != nil { *f = dv.(float64) } *(fptr.(**float64)) = f case reflect.Int32: // might be an enum if ft := f.Type(); ft != int32PtrType { // enum f.Set(reflect.New(ft.Elem())) if dv != nil { f.Elem().SetInt(int64(dv.(int32))) } } else { // int32 field i := new(int32) if dv != nil { *i = dv.(int32) } *(fptr.(**int32)) = i } case reflect.Int64: i := new(int64) if dv != nil { *i = dv.(int64) } *(fptr.(**int64)) = i case reflect.String: s := new(string) if dv != nil { *s = dv.(string) } *(fptr.(**string)) = s case reflect.Uint8: // exceptional case: []byte var b []byte if dv != nil { db := dv.([]byte) b = make([]byte, len(db)) copy(b, db) } else { b = []byte{} } *(fptr.(*[]byte)) = b case reflect.Uint32: u := new(uint32) if dv != nil { *u = dv.(uint32) } *(fptr.(**uint32)) = u case reflect.Uint64: u := new(uint64) if dv != nil { *u = dv.(uint64) } *(fptr.(**uint64)) = u default: log.Printf("proto: can't set default for field %v (sf.kind=%v)", f, sf.kind) } } for _, ni := range dm.nested { f := v.Field(ni) // f is *T or T or []*T or []T switch f.Kind() { case reflect.Struct: setDefaults(f, recur, zeros) case reflect.Ptr: if f.IsNil() { continue } setDefaults(f, recur, zeros) case reflect.Slice: for i := 0; i < f.Len(); i++ { e := f.Index(i) if e.Kind() == reflect.Ptr && e.IsNil() { continue } setDefaults(e, recur, zeros) } case reflect.Map: for _, k := range f.MapKeys() { e := f.MapIndex(k) if e.IsNil() { continue } setDefaults(e, recur, zeros) } } } } var ( // defaults maps a protocol buffer struct type to a slice of the fields, // with its scalar fields set to their proto-declared non-zero default values. defaultMu sync.RWMutex defaults = make(map[reflect.Type]defaultMessage) int32PtrType = reflect.TypeOf((*int32)(nil)) ) // defaultMessage represents information about the default values of a message. type defaultMessage struct { scalars []scalarField nested []int // struct field index of nested messages } type scalarField struct { index int // struct field index kind reflect.Kind // element type (the T in *T or []T) value interface{} // the proto-declared default value, or nil } // t is a struct type. func buildDefaultMessage(t reflect.Type) (dm defaultMessage) { sprop := GetProperties(t) for _, prop := range sprop.Prop { fi, ok := sprop.decoderTags.get(prop.Tag) if !ok { // XXX_unrecognized continue } ft := t.Field(fi).Type sf, nested, err := fieldDefault(ft, prop) switch { case err != nil: log.Print(err) case nested: dm.nested = append(dm.nested, fi) case sf != nil: sf.index = fi dm.scalars = append(dm.scalars, *sf) } } return dm } // fieldDefault returns the scalarField for field type ft. // sf will be nil if the field can not have a default. // nestedMessage will be true if this is a nested message. // Note that sf.index is not set on return. func fieldDefault(ft reflect.Type, prop *Properties) (sf *scalarField, nestedMessage bool, err error) { var canHaveDefault bool switch ft.Kind() { case reflect.Struct: nestedMessage = true // non-nullable case reflect.Ptr: if ft.Elem().Kind() == reflect.Struct { nestedMessage = true } else { canHaveDefault = true // proto2 scalar field } case reflect.Slice: switch ft.Elem().Kind() { case reflect.Ptr, reflect.Struct: nestedMessage = true // repeated message case reflect.Uint8: canHaveDefault = true // bytes field } case reflect.Map: if ft.Elem().Kind() == reflect.Ptr { nestedMessage = true // map with message values } } if !canHaveDefault { if nestedMessage { return nil, true, nil } return nil, false, nil } // We now know that ft is a pointer or slice. sf = &scalarField{kind: ft.Elem().Kind()} // scalar fields without defaults if !prop.HasDefault { return sf, false, nil } // a scalar field: either *T or []byte switch ft.Elem().Kind() { case reflect.Bool: x, err := strconv.ParseBool(prop.Default) if err != nil { return nil, false, fmt.Errorf("proto: bad default bool %q: %v", prop.Default, err) } sf.value = x case reflect.Float32: x, err := strconv.ParseFloat(prop.Default, 32) if err != nil { return nil, false, fmt.Errorf("proto: bad default float32 %q: %v", prop.Default, err) } sf.value = float32(x) case reflect.Float64: x, err := strconv.ParseFloat(prop.Default, 64) if err != nil { return nil, false, fmt.Errorf("proto: bad default float64 %q: %v", prop.Default, err) } sf.value = x case reflect.Int32: x, err := strconv.ParseInt(prop.Default, 10, 32) if err != nil { return nil, false, fmt.Errorf("proto: bad default int32 %q: %v", prop.Default, err) } sf.value = int32(x) case reflect.Int64: x, err := strconv.ParseInt(prop.Default, 10, 64) if err != nil { return nil, false, fmt.Errorf("proto: bad default int64 %q: %v", prop.Default, err) } sf.value = x case reflect.String: sf.value = prop.Default case reflect.Uint8: // []byte (not *uint8) sf.value = []byte(prop.Default) case reflect.Uint32: x, err := strconv.ParseUint(prop.Default, 10, 32) if err != nil { return nil, false, fmt.Errorf("proto: bad default uint32 %q: %v", prop.Default, err) } sf.value = uint32(x) case reflect.Uint64: x, err := strconv.ParseUint(prop.Default, 10, 64) if err != nil { return nil, false, fmt.Errorf("proto: bad default uint64 %q: %v", prop.Default, err) } sf.value = x default: return nil, false, fmt.Errorf("proto: unhandled def kind %v", ft.Elem().Kind()) } return sf, false, nil } // mapKeys returns a sort.Interface to be used for sorting the map keys. // Map fields may have key types of non-float scalars, strings and enums. func mapKeys(vs []reflect.Value) sort.Interface { s := mapKeySorter{vs: vs} // Type specialization per https://developers.google.com/protocol-buffers/docs/proto#maps. if len(vs) == 0 { return s } switch vs[0].Kind() { case reflect.Int32, reflect.Int64: s.less = func(a, b reflect.Value) bool { return a.Int() < b.Int() } case reflect.Uint32, reflect.Uint64: s.less = func(a, b reflect.Value) bool { return a.Uint() < b.Uint() } case reflect.Bool: s.less = func(a, b reflect.Value) bool { return !a.Bool() && b.Bool() } // false < true case reflect.String: s.less = func(a, b reflect.Value) bool { return a.String() < b.String() } default: panic(fmt.Sprintf("unsupported map key type: %v", vs[0].Kind())) } return s } type mapKeySorter struct { vs []reflect.Value less func(a, b reflect.Value) bool } func (s mapKeySorter) Len() int { return len(s.vs) } func (s mapKeySorter) Swap(i, j int) { s.vs[i], s.vs[j] = s.vs[j], s.vs[i] } func (s mapKeySorter) Less(i, j int) bool { return s.less(s.vs[i], s.vs[j]) } // isProto3Zero reports whether v is a zero proto3 value. func isProto3Zero(v reflect.Value) bool { switch v.Kind() { case reflect.Bool: return !v.Bool() case reflect.Int32, reflect.Int64: return v.Int() == 0 case reflect.Uint32, reflect.Uint64: return v.Uint() == 0 case reflect.Float32, reflect.Float64: return v.Float() == 0 case reflect.String: return v.String() == "" } return false } const ( // ProtoPackageIsVersion3 is referenced from generated protocol buffer files // to assert that that code is compatible with this version of the proto package. GoGoProtoPackageIsVersion3 = true // ProtoPackageIsVersion2 is referenced from generated protocol buffer files // to assert that that code is compatible with this version of the proto package. GoGoProtoPackageIsVersion2 = true // ProtoPackageIsVersion1 is referenced from generated protocol buffer files // to assert that that code is compatible with this version of the proto package. GoGoProtoPackageIsVersion1 = true ) // InternalMessageInfo is a type used internally by generated .pb.go files. // This type is not intended to be used by non-generated code. // This type is not subject to any compatibility guarantee. type InternalMessageInfo struct { marshal *marshalInfo unmarshal *unmarshalInfo merge *mergeInfo discard *discardInfo } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/lib_gogo.go ================================================ // Protocol Buffers for Go with Gadgets // // Copyright (c) 2013, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto import ( "encoding/json" "strconv" ) type Sizer interface { Size() int } type ProtoSizer interface { ProtoSize() int } func MarshalJSONEnum(m map[int32]string, value int32) ([]byte, error) { s, ok := m[value] if !ok { s = strconv.Itoa(int(value)) } return json.Marshal(s) } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/message_set.go ================================================ // Go support for Protocol Buffers - Google's data interchange format // // Copyright 2010 The Go Authors. All rights reserved. // https://github.com/golang/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto /* * Support for message sets. */ import ( "errors" ) // errNoMessageTypeID occurs when a protocol buffer does not have a message type ID. // A message type ID is required for storing a protocol buffer in a message set. var errNoMessageTypeID = errors.New("proto does not have a message type ID") // The first two types (_MessageSet_Item and messageSet) // model what the protocol compiler produces for the following protocol message: // message MessageSet { // repeated group Item = 1 { // required int32 type_id = 2; // required string message = 3; // }; // } // That is the MessageSet wire format. We can't use a proto to generate these // because that would introduce a circular dependency between it and this package. type _MessageSet_Item struct { TypeId *int32 `protobuf:"varint,2,req,name=type_id"` Message []byte `protobuf:"bytes,3,req,name=message"` } type messageSet struct { Item []*_MessageSet_Item `protobuf:"group,1,rep"` XXX_unrecognized []byte // TODO: caching? } // Make sure messageSet is a Message. var _ Message = (*messageSet)(nil) // messageTypeIder is an interface satisfied by a protocol buffer type // that may be stored in a MessageSet. type messageTypeIder interface { MessageTypeId() int32 } func (ms *messageSet) find(pb Message) *_MessageSet_Item { mti, ok := pb.(messageTypeIder) if !ok { return nil } id := mti.MessageTypeId() for _, item := range ms.Item { if *item.TypeId == id { return item } } return nil } func (ms *messageSet) Has(pb Message) bool { return ms.find(pb) != nil } func (ms *messageSet) Unmarshal(pb Message) error { if item := ms.find(pb); item != nil { return Unmarshal(item.Message, pb) } if _, ok := pb.(messageTypeIder); !ok { return errNoMessageTypeID } return nil // TODO: return error instead? } func (ms *messageSet) Marshal(pb Message) error { msg, err := Marshal(pb) if err != nil { return err } if item := ms.find(pb); item != nil { // reuse existing item item.Message = msg return nil } mti, ok := pb.(messageTypeIder) if !ok { return errNoMessageTypeID } mtid := mti.MessageTypeId() ms.Item = append(ms.Item, &_MessageSet_Item{ TypeId: &mtid, Message: msg, }) return nil } func (ms *messageSet) Reset() { *ms = messageSet{} } func (ms *messageSet) String() string { return CompactTextString(ms) } func (*messageSet) ProtoMessage() {} // Support for the message_set_wire_format message option. func skipVarint(buf []byte) []byte { i := 0 for ; buf[i]&0x80 != 0; i++ { } return buf[i+1:] } // unmarshalMessageSet decodes the extension map encoded in buf in the message set wire format. // It is called by Unmarshal methods on protocol buffer messages with the message_set_wire_format option. func unmarshalMessageSet(buf []byte, exts interface{}) error { var m map[int32]Extension switch exts := exts.(type) { case *XXX_InternalExtensions: m = exts.extensionsWrite() case map[int32]Extension: m = exts default: return errors.New("proto: not an extension map") } ms := new(messageSet) if err := Unmarshal(buf, ms); err != nil { return err } for _, item := range ms.Item { id := *item.TypeId msg := item.Message // Restore wire type and field number varint, plus length varint. // Be careful to preserve duplicate items. b := EncodeVarint(uint64(id)<<3 | WireBytes) if ext, ok := m[id]; ok { // Existing data; rip off the tag and length varint // so we join the new data correctly. // We can assume that ext.enc is set because we are unmarshaling. o := ext.enc[len(b):] // skip wire type and field number _, n := DecodeVarint(o) // calculate length of length varint o = o[n:] // skip length varint msg = append(o, msg...) // join old data and new data } b = append(b, EncodeVarint(uint64(len(msg)))...) b = append(b, msg...) m[id] = Extension{enc: b} } return nil } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/pointer_reflect.go ================================================ // Go support for Protocol Buffers - Google's data interchange format // // Copyright 2012 The Go Authors. All rights reserved. // https://github.com/golang/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // +build purego appengine js // This file contains an implementation of proto field accesses using package reflect. // It is slower than the code in pointer_unsafe.go but it avoids package unsafe and can // be used on App Engine. package proto import ( "reflect" "sync" ) const unsafeAllowed = false // A field identifies a field in a struct, accessible from a pointer. // In this implementation, a field is identified by the sequence of field indices // passed to reflect's FieldByIndex. type field []int // toField returns a field equivalent to the given reflect field. func toField(f *reflect.StructField) field { return f.Index } // invalidField is an invalid field identifier. var invalidField = field(nil) // zeroField is a noop when calling pointer.offset. var zeroField = field([]int{}) // IsValid reports whether the field identifier is valid. func (f field) IsValid() bool { return f != nil } // The pointer type is for the table-driven decoder. // The implementation here uses a reflect.Value of pointer type to // create a generic pointer. In pointer_unsafe.go we use unsafe // instead of reflect to implement the same (but faster) interface. type pointer struct { v reflect.Value } // toPointer converts an interface of pointer type to a pointer // that points to the same target. func toPointer(i *Message) pointer { return pointer{v: reflect.ValueOf(*i)} } // toAddrPointer converts an interface to a pointer that points to // the interface data. func toAddrPointer(i *interface{}, isptr bool) pointer { v := reflect.ValueOf(*i) u := reflect.New(v.Type()) u.Elem().Set(v) return pointer{v: u} } // valToPointer converts v to a pointer. v must be of pointer type. func valToPointer(v reflect.Value) pointer { return pointer{v: v} } // offset converts from a pointer to a structure to a pointer to // one of its fields. func (p pointer) offset(f field) pointer { return pointer{v: p.v.Elem().FieldByIndex(f).Addr()} } func (p pointer) isNil() bool { return p.v.IsNil() } // grow updates the slice s in place to make it one element longer. // s must be addressable. // Returns the (addressable) new element. func grow(s reflect.Value) reflect.Value { n, m := s.Len(), s.Cap() if n < m { s.SetLen(n + 1) } else { s.Set(reflect.Append(s, reflect.Zero(s.Type().Elem()))) } return s.Index(n) } func (p pointer) toInt64() *int64 { return p.v.Interface().(*int64) } func (p pointer) toInt64Ptr() **int64 { return p.v.Interface().(**int64) } func (p pointer) toInt64Slice() *[]int64 { return p.v.Interface().(*[]int64) } var int32ptr = reflect.TypeOf((*int32)(nil)) func (p pointer) toInt32() *int32 { return p.v.Convert(int32ptr).Interface().(*int32) } // The toInt32Ptr/Slice methods don't work because of enums. // Instead, we must use set/get methods for the int32ptr/slice case. /* func (p pointer) toInt32Ptr() **int32 { return p.v.Interface().(**int32) } func (p pointer) toInt32Slice() *[]int32 { return p.v.Interface().(*[]int32) } */ func (p pointer) getInt32Ptr() *int32 { if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) { // raw int32 type return p.v.Elem().Interface().(*int32) } // an enum return p.v.Elem().Convert(int32PtrType).Interface().(*int32) } func (p pointer) setInt32Ptr(v int32) { // Allocate value in a *int32. Possibly convert that to a *enum. // Then assign it to a **int32 or **enum. // Note: we can convert *int32 to *enum, but we can't convert // **int32 to **enum! p.v.Elem().Set(reflect.ValueOf(&v).Convert(p.v.Type().Elem())) } // getInt32Slice copies []int32 from p as a new slice. // This behavior differs from the implementation in pointer_unsafe.go. func (p pointer) getInt32Slice() []int32 { if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) { // raw int32 type return p.v.Elem().Interface().([]int32) } // an enum // Allocate a []int32, then assign []enum's values into it. // Note: we can't convert []enum to []int32. slice := p.v.Elem() s := make([]int32, slice.Len()) for i := 0; i < slice.Len(); i++ { s[i] = int32(slice.Index(i).Int()) } return s } // setInt32Slice copies []int32 into p as a new slice. // This behavior differs from the implementation in pointer_unsafe.go. func (p pointer) setInt32Slice(v []int32) { if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) { // raw int32 type p.v.Elem().Set(reflect.ValueOf(v)) return } // an enum // Allocate a []enum, then assign []int32's values into it. // Note: we can't convert []enum to []int32. slice := reflect.MakeSlice(p.v.Type().Elem(), len(v), cap(v)) for i, x := range v { slice.Index(i).SetInt(int64(x)) } p.v.Elem().Set(slice) } func (p pointer) appendInt32Slice(v int32) { grow(p.v.Elem()).SetInt(int64(v)) } func (p pointer) toUint64() *uint64 { return p.v.Interface().(*uint64) } func (p pointer) toUint64Ptr() **uint64 { return p.v.Interface().(**uint64) } func (p pointer) toUint64Slice() *[]uint64 { return p.v.Interface().(*[]uint64) } func (p pointer) toUint32() *uint32 { return p.v.Interface().(*uint32) } func (p pointer) toUint32Ptr() **uint32 { return p.v.Interface().(**uint32) } func (p pointer) toUint32Slice() *[]uint32 { return p.v.Interface().(*[]uint32) } func (p pointer) toBool() *bool { return p.v.Interface().(*bool) } func (p pointer) toBoolPtr() **bool { return p.v.Interface().(**bool) } func (p pointer) toBoolSlice() *[]bool { return p.v.Interface().(*[]bool) } func (p pointer) toFloat64() *float64 { return p.v.Interface().(*float64) } func (p pointer) toFloat64Ptr() **float64 { return p.v.Interface().(**float64) } func (p pointer) toFloat64Slice() *[]float64 { return p.v.Interface().(*[]float64) } func (p pointer) toFloat32() *float32 { return p.v.Interface().(*float32) } func (p pointer) toFloat32Ptr() **float32 { return p.v.Interface().(**float32) } func (p pointer) toFloat32Slice() *[]float32 { return p.v.Interface().(*[]float32) } func (p pointer) toString() *string { return p.v.Interface().(*string) } func (p pointer) toStringPtr() **string { return p.v.Interface().(**string) } func (p pointer) toStringSlice() *[]string { return p.v.Interface().(*[]string) } func (p pointer) toBytes() *[]byte { return p.v.Interface().(*[]byte) } func (p pointer) toBytesSlice() *[][]byte { return p.v.Interface().(*[][]byte) } func (p pointer) toExtensions() *XXX_InternalExtensions { return p.v.Interface().(*XXX_InternalExtensions) } func (p pointer) toOldExtensions() *map[int32]Extension { return p.v.Interface().(*map[int32]Extension) } func (p pointer) getPointer() pointer { return pointer{v: p.v.Elem()} } func (p pointer) setPointer(q pointer) { p.v.Elem().Set(q.v) } func (p pointer) appendPointer(q pointer) { grow(p.v.Elem()).Set(q.v) } // getPointerSlice copies []*T from p as a new []pointer. // This behavior differs from the implementation in pointer_unsafe.go. func (p pointer) getPointerSlice() []pointer { if p.v.IsNil() { return nil } n := p.v.Elem().Len() s := make([]pointer, n) for i := 0; i < n; i++ { s[i] = pointer{v: p.v.Elem().Index(i)} } return s } // setPointerSlice copies []pointer into p as a new []*T. // This behavior differs from the implementation in pointer_unsafe.go. func (p pointer) setPointerSlice(v []pointer) { if v == nil { p.v.Elem().Set(reflect.New(p.v.Elem().Type()).Elem()) return } s := reflect.MakeSlice(p.v.Elem().Type(), 0, len(v)) for _, p := range v { s = reflect.Append(s, p.v) } p.v.Elem().Set(s) } // getInterfacePointer returns a pointer that points to the // interface data of the interface pointed by p. func (p pointer) getInterfacePointer() pointer { if p.v.Elem().IsNil() { return pointer{v: p.v.Elem()} } return pointer{v: p.v.Elem().Elem().Elem().Field(0).Addr()} // *interface -> interface -> *struct -> struct } func (p pointer) asPointerTo(t reflect.Type) reflect.Value { // TODO: check that p.v.Type().Elem() == t? return p.v } func atomicLoadUnmarshalInfo(p **unmarshalInfo) *unmarshalInfo { atomicLock.Lock() defer atomicLock.Unlock() return *p } func atomicStoreUnmarshalInfo(p **unmarshalInfo, v *unmarshalInfo) { atomicLock.Lock() defer atomicLock.Unlock() *p = v } func atomicLoadMarshalInfo(p **marshalInfo) *marshalInfo { atomicLock.Lock() defer atomicLock.Unlock() return *p } func atomicStoreMarshalInfo(p **marshalInfo, v *marshalInfo) { atomicLock.Lock() defer atomicLock.Unlock() *p = v } func atomicLoadMergeInfo(p **mergeInfo) *mergeInfo { atomicLock.Lock() defer atomicLock.Unlock() return *p } func atomicStoreMergeInfo(p **mergeInfo, v *mergeInfo) { atomicLock.Lock() defer atomicLock.Unlock() *p = v } func atomicLoadDiscardInfo(p **discardInfo) *discardInfo { atomicLock.Lock() defer atomicLock.Unlock() return *p } func atomicStoreDiscardInfo(p **discardInfo, v *discardInfo) { atomicLock.Lock() defer atomicLock.Unlock() *p = v } var atomicLock sync.Mutex ================================================ FILE: vendor/github.com/gogo/protobuf/proto/pointer_reflect_gogo.go ================================================ // Protocol Buffers for Go with Gadgets // // Copyright (c) 2018, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // +build purego appengine js // This file contains an implementation of proto field accesses using package reflect. // It is slower than the code in pointer_unsafe.go but it avoids package unsafe and can // be used on App Engine. package proto import ( "reflect" ) // TODO: untested, so probably incorrect. func (p pointer) getRef() pointer { return pointer{v: p.v.Addr()} } func (p pointer) appendRef(v pointer, typ reflect.Type) { slice := p.getSlice(typ) elem := v.asPointerTo(typ).Elem() newSlice := reflect.Append(slice, elem) slice.Set(newSlice) } func (p pointer) getSlice(typ reflect.Type) reflect.Value { sliceTyp := reflect.SliceOf(typ) slice := p.asPointerTo(sliceTyp) slice = slice.Elem() return slice } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/pointer_unsafe.go ================================================ // Go support for Protocol Buffers - Google's data interchange format // // Copyright 2012 The Go Authors. All rights reserved. // https://github.com/golang/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // +build !purego,!appengine,!js // This file contains the implementation of the proto field accesses using package unsafe. package proto import ( "reflect" "sync/atomic" "unsafe" ) const unsafeAllowed = true // A field identifies a field in a struct, accessible from a pointer. // In this implementation, a field is identified by its byte offset from the start of the struct. type field uintptr // toField returns a field equivalent to the given reflect field. func toField(f *reflect.StructField) field { return field(f.Offset) } // invalidField is an invalid field identifier. const invalidField = ^field(0) // zeroField is a noop when calling pointer.offset. const zeroField = field(0) // IsValid reports whether the field identifier is valid. func (f field) IsValid() bool { return f != invalidField } // The pointer type below is for the new table-driven encoder/decoder. // The implementation here uses unsafe.Pointer to create a generic pointer. // In pointer_reflect.go we use reflect instead of unsafe to implement // the same (but slower) interface. type pointer struct { p unsafe.Pointer } // size of pointer var ptrSize = unsafe.Sizeof(uintptr(0)) // toPointer converts an interface of pointer type to a pointer // that points to the same target. func toPointer(i *Message) pointer { // Super-tricky - read pointer out of data word of interface value. // Saves ~25ns over the equivalent: // return valToPointer(reflect.ValueOf(*i)) return pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]} } // toAddrPointer converts an interface to a pointer that points to // the interface data. func toAddrPointer(i *interface{}, isptr bool) pointer { // Super-tricky - read or get the address of data word of interface value. if isptr { // The interface is of pointer type, thus it is a direct interface. // The data word is the pointer data itself. We take its address. return pointer{p: unsafe.Pointer(uintptr(unsafe.Pointer(i)) + ptrSize)} } // The interface is not of pointer type. The data word is the pointer // to the data. return pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]} } // valToPointer converts v to a pointer. v must be of pointer type. func valToPointer(v reflect.Value) pointer { return pointer{p: unsafe.Pointer(v.Pointer())} } // offset converts from a pointer to a structure to a pointer to // one of its fields. func (p pointer) offset(f field) pointer { // For safety, we should panic if !f.IsValid, however calling panic causes // this to no longer be inlineable, which is a serious performance cost. /* if !f.IsValid() { panic("invalid field") } */ return pointer{p: unsafe.Pointer(uintptr(p.p) + uintptr(f))} } func (p pointer) isNil() bool { return p.p == nil } func (p pointer) toInt64() *int64 { return (*int64)(p.p) } func (p pointer) toInt64Ptr() **int64 { return (**int64)(p.p) } func (p pointer) toInt64Slice() *[]int64 { return (*[]int64)(p.p) } func (p pointer) toInt32() *int32 { return (*int32)(p.p) } // See pointer_reflect.go for why toInt32Ptr/Slice doesn't exist. /* func (p pointer) toInt32Ptr() **int32 { return (**int32)(p.p) } func (p pointer) toInt32Slice() *[]int32 { return (*[]int32)(p.p) } */ func (p pointer) getInt32Ptr() *int32 { return *(**int32)(p.p) } func (p pointer) setInt32Ptr(v int32) { *(**int32)(p.p) = &v } // getInt32Slice loads a []int32 from p. // The value returned is aliased with the original slice. // This behavior differs from the implementation in pointer_reflect.go. func (p pointer) getInt32Slice() []int32 { return *(*[]int32)(p.p) } // setInt32Slice stores a []int32 to p. // The value set is aliased with the input slice. // This behavior differs from the implementation in pointer_reflect.go. func (p pointer) setInt32Slice(v []int32) { *(*[]int32)(p.p) = v } // TODO: Can we get rid of appendInt32Slice and use setInt32Slice instead? func (p pointer) appendInt32Slice(v int32) { s := (*[]int32)(p.p) *s = append(*s, v) } func (p pointer) toUint64() *uint64 { return (*uint64)(p.p) } func (p pointer) toUint64Ptr() **uint64 { return (**uint64)(p.p) } func (p pointer) toUint64Slice() *[]uint64 { return (*[]uint64)(p.p) } func (p pointer) toUint32() *uint32 { return (*uint32)(p.p) } func (p pointer) toUint32Ptr() **uint32 { return (**uint32)(p.p) } func (p pointer) toUint32Slice() *[]uint32 { return (*[]uint32)(p.p) } func (p pointer) toBool() *bool { return (*bool)(p.p) } func (p pointer) toBoolPtr() **bool { return (**bool)(p.p) } func (p pointer) toBoolSlice() *[]bool { return (*[]bool)(p.p) } func (p pointer) toFloat64() *float64 { return (*float64)(p.p) } func (p pointer) toFloat64Ptr() **float64 { return (**float64)(p.p) } func (p pointer) toFloat64Slice() *[]float64 { return (*[]float64)(p.p) } func (p pointer) toFloat32() *float32 { return (*float32)(p.p) } func (p pointer) toFloat32Ptr() **float32 { return (**float32)(p.p) } func (p pointer) toFloat32Slice() *[]float32 { return (*[]float32)(p.p) } func (p pointer) toString() *string { return (*string)(p.p) } func (p pointer) toStringPtr() **string { return (**string)(p.p) } func (p pointer) toStringSlice() *[]string { return (*[]string)(p.p) } func (p pointer) toBytes() *[]byte { return (*[]byte)(p.p) } func (p pointer) toBytesSlice() *[][]byte { return (*[][]byte)(p.p) } func (p pointer) toExtensions() *XXX_InternalExtensions { return (*XXX_InternalExtensions)(p.p) } func (p pointer) toOldExtensions() *map[int32]Extension { return (*map[int32]Extension)(p.p) } // getPointerSlice loads []*T from p as a []pointer. // The value returned is aliased with the original slice. // This behavior differs from the implementation in pointer_reflect.go. func (p pointer) getPointerSlice() []pointer { // Super-tricky - p should point to a []*T where T is a // message type. We load it as []pointer. return *(*[]pointer)(p.p) } // setPointerSlice stores []pointer into p as a []*T. // The value set is aliased with the input slice. // This behavior differs from the implementation in pointer_reflect.go. func (p pointer) setPointerSlice(v []pointer) { // Super-tricky - p should point to a []*T where T is a // message type. We store it as []pointer. *(*[]pointer)(p.p) = v } // getPointer loads the pointer at p and returns it. func (p pointer) getPointer() pointer { return pointer{p: *(*unsafe.Pointer)(p.p)} } // setPointer stores the pointer q at p. func (p pointer) setPointer(q pointer) { *(*unsafe.Pointer)(p.p) = q.p } // append q to the slice pointed to by p. func (p pointer) appendPointer(q pointer) { s := (*[]unsafe.Pointer)(p.p) *s = append(*s, q.p) } // getInterfacePointer returns a pointer that points to the // interface data of the interface pointed by p. func (p pointer) getInterfacePointer() pointer { // Super-tricky - read pointer out of data word of interface value. return pointer{p: (*(*[2]unsafe.Pointer)(p.p))[1]} } // asPointerTo returns a reflect.Value that is a pointer to an // object of type t stored at p. func (p pointer) asPointerTo(t reflect.Type) reflect.Value { return reflect.NewAt(t, p.p) } func atomicLoadUnmarshalInfo(p **unmarshalInfo) *unmarshalInfo { return (*unmarshalInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p)))) } func atomicStoreUnmarshalInfo(p **unmarshalInfo, v *unmarshalInfo) { atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v)) } func atomicLoadMarshalInfo(p **marshalInfo) *marshalInfo { return (*marshalInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p)))) } func atomicStoreMarshalInfo(p **marshalInfo, v *marshalInfo) { atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v)) } func atomicLoadMergeInfo(p **mergeInfo) *mergeInfo { return (*mergeInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p)))) } func atomicStoreMergeInfo(p **mergeInfo, v *mergeInfo) { atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v)) } func atomicLoadDiscardInfo(p **discardInfo) *discardInfo { return (*discardInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p)))) } func atomicStoreDiscardInfo(p **discardInfo, v *discardInfo) { atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v)) } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/pointer_unsafe_gogo.go ================================================ // Protocol Buffers for Go with Gadgets // // Copyright (c) 2018, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // +build !purego,!appengine,!js // This file contains the implementation of the proto field accesses using package unsafe. package proto import ( "reflect" "unsafe" ) func (p pointer) getRef() pointer { return pointer{p: (unsafe.Pointer)(&p.p)} } func (p pointer) appendRef(v pointer, typ reflect.Type) { slice := p.getSlice(typ) elem := v.asPointerTo(typ).Elem() newSlice := reflect.Append(slice, elem) slice.Set(newSlice) } func (p pointer) getSlice(typ reflect.Type) reflect.Value { sliceTyp := reflect.SliceOf(typ) slice := p.asPointerTo(sliceTyp) slice = slice.Elem() return slice } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/properties.go ================================================ // Protocol Buffers for Go with Gadgets // // Copyright (c) 2013, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // // Go support for Protocol Buffers - Google's data interchange format // // Copyright 2010 The Go Authors. All rights reserved. // https://github.com/golang/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto /* * Routines for encoding data into the wire format for protocol buffers. */ import ( "fmt" "log" "reflect" "sort" "strconv" "strings" "sync" ) const debug bool = false // Constants that identify the encoding of a value on the wire. const ( WireVarint = 0 WireFixed64 = 1 WireBytes = 2 WireStartGroup = 3 WireEndGroup = 4 WireFixed32 = 5 ) // tagMap is an optimization over map[int]int for typical protocol buffer // use-cases. Encoded protocol buffers are often in tag order with small tag // numbers. type tagMap struct { fastTags []int slowTags map[int]int } // tagMapFastLimit is the upper bound on the tag number that will be stored in // the tagMap slice rather than its map. const tagMapFastLimit = 1024 func (p *tagMap) get(t int) (int, bool) { if t > 0 && t < tagMapFastLimit { if t >= len(p.fastTags) { return 0, false } fi := p.fastTags[t] return fi, fi >= 0 } fi, ok := p.slowTags[t] return fi, ok } func (p *tagMap) put(t int, fi int) { if t > 0 && t < tagMapFastLimit { for len(p.fastTags) < t+1 { p.fastTags = append(p.fastTags, -1) } p.fastTags[t] = fi return } if p.slowTags == nil { p.slowTags = make(map[int]int) } p.slowTags[t] = fi } // StructProperties represents properties for all the fields of a struct. // decoderTags and decoderOrigNames should only be used by the decoder. type StructProperties struct { Prop []*Properties // properties for each field reqCount int // required count decoderTags tagMap // map from proto tag to struct field number decoderOrigNames map[string]int // map from original name to struct field number order []int // list of struct field numbers in tag order // OneofTypes contains information about the oneof fields in this message. // It is keyed by the original name of a field. OneofTypes map[string]*OneofProperties } // OneofProperties represents information about a specific field in a oneof. type OneofProperties struct { Type reflect.Type // pointer to generated struct type for this oneof field Field int // struct field number of the containing oneof in the message Prop *Properties } // Implement the sorting interface so we can sort the fields in tag order, as recommended by the spec. // See encode.go, (*Buffer).enc_struct. func (sp *StructProperties) Len() int { return len(sp.order) } func (sp *StructProperties) Less(i, j int) bool { return sp.Prop[sp.order[i]].Tag < sp.Prop[sp.order[j]].Tag } func (sp *StructProperties) Swap(i, j int) { sp.order[i], sp.order[j] = sp.order[j], sp.order[i] } // Properties represents the protocol-specific behavior of a single struct field. type Properties struct { Name string // name of the field, for error messages OrigName string // original name before protocol compiler (always set) JSONName string // name to use for JSON; determined by protoc Wire string WireType int Tag int Required bool Optional bool Repeated bool Packed bool // relevant for repeated primitives only Enum string // set for enum types only proto3 bool // whether this is known to be a proto3 field oneof bool // whether this is a oneof field Default string // default value HasDefault bool // whether an explicit default was provided CustomType string CastType string StdTime bool StdDuration bool WktPointer bool stype reflect.Type // set for struct types only ctype reflect.Type // set for custom types only sprop *StructProperties // set for struct types only mtype reflect.Type // set for map types only MapKeyProp *Properties // set for map types only MapValProp *Properties // set for map types only } // String formats the properties in the protobuf struct field tag style. func (p *Properties) String() string { s := p.Wire s += "," s += strconv.Itoa(p.Tag) if p.Required { s += ",req" } if p.Optional { s += ",opt" } if p.Repeated { s += ",rep" } if p.Packed { s += ",packed" } s += ",name=" + p.OrigName if p.JSONName != p.OrigName { s += ",json=" + p.JSONName } if p.proto3 { s += ",proto3" } if p.oneof { s += ",oneof" } if len(p.Enum) > 0 { s += ",enum=" + p.Enum } if p.HasDefault { s += ",def=" + p.Default } return s } // Parse populates p by parsing a string in the protobuf struct field tag style. func (p *Properties) Parse(s string) { // "bytes,49,opt,name=foo,def=hello!" fields := strings.Split(s, ",") // breaks def=, but handled below. if len(fields) < 2 { log.Printf("proto: tag has too few fields: %q", s) return } p.Wire = fields[0] switch p.Wire { case "varint": p.WireType = WireVarint case "fixed32": p.WireType = WireFixed32 case "fixed64": p.WireType = WireFixed64 case "zigzag32": p.WireType = WireVarint case "zigzag64": p.WireType = WireVarint case "bytes", "group": p.WireType = WireBytes // no numeric converter for non-numeric types default: log.Printf("proto: tag has unknown wire type: %q", s) return } var err error p.Tag, err = strconv.Atoi(fields[1]) if err != nil { return } outer: for i := 2; i < len(fields); i++ { f := fields[i] switch { case f == "req": p.Required = true case f == "opt": p.Optional = true case f == "rep": p.Repeated = true case f == "packed": p.Packed = true case strings.HasPrefix(f, "name="): p.OrigName = f[5:] case strings.HasPrefix(f, "json="): p.JSONName = f[5:] case strings.HasPrefix(f, "enum="): p.Enum = f[5:] case f == "proto3": p.proto3 = true case f == "oneof": p.oneof = true case strings.HasPrefix(f, "def="): p.HasDefault = true p.Default = f[4:] // rest of string if i+1 < len(fields) { // Commas aren't escaped, and def is always last. p.Default += "," + strings.Join(fields[i+1:], ",") break outer } case strings.HasPrefix(f, "embedded="): p.OrigName = strings.Split(f, "=")[1] case strings.HasPrefix(f, "customtype="): p.CustomType = strings.Split(f, "=")[1] case strings.HasPrefix(f, "casttype="): p.CastType = strings.Split(f, "=")[1] case f == "stdtime": p.StdTime = true case f == "stdduration": p.StdDuration = true case f == "wktptr": p.WktPointer = true } } } var protoMessageType = reflect.TypeOf((*Message)(nil)).Elem() // setFieldProps initializes the field properties for submessages and maps. func (p *Properties) setFieldProps(typ reflect.Type, f *reflect.StructField, lockGetProp bool) { isMap := typ.Kind() == reflect.Map if len(p.CustomType) > 0 && !isMap { p.ctype = typ p.setTag(lockGetProp) return } if p.StdTime && !isMap { p.setTag(lockGetProp) return } if p.StdDuration && !isMap { p.setTag(lockGetProp) return } if p.WktPointer && !isMap { p.setTag(lockGetProp) return } switch t1 := typ; t1.Kind() { case reflect.Struct: p.stype = typ case reflect.Ptr: if t1.Elem().Kind() == reflect.Struct { p.stype = t1.Elem() } case reflect.Slice: switch t2 := t1.Elem(); t2.Kind() { case reflect.Ptr: switch t3 := t2.Elem(); t3.Kind() { case reflect.Struct: p.stype = t3 } case reflect.Struct: p.stype = t2 } case reflect.Map: p.mtype = t1 p.MapKeyProp = &Properties{} p.MapKeyProp.init(reflect.PtrTo(p.mtype.Key()), "Key", f.Tag.Get("protobuf_key"), nil, lockGetProp) p.MapValProp = &Properties{} vtype := p.mtype.Elem() if vtype.Kind() != reflect.Ptr && vtype.Kind() != reflect.Slice { // The value type is not a message (*T) or bytes ([]byte), // so we need encoders for the pointer to this type. vtype = reflect.PtrTo(vtype) } p.MapValProp.CustomType = p.CustomType p.MapValProp.StdDuration = p.StdDuration p.MapValProp.StdTime = p.StdTime p.MapValProp.WktPointer = p.WktPointer p.MapValProp.init(vtype, "Value", f.Tag.Get("protobuf_val"), nil, lockGetProp) } p.setTag(lockGetProp) } func (p *Properties) setTag(lockGetProp bool) { if p.stype != nil { if lockGetProp { p.sprop = GetProperties(p.stype) } else { p.sprop = getPropertiesLocked(p.stype) } } } var ( marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem() ) // Init populates the properties from a protocol buffer struct tag. func (p *Properties) Init(typ reflect.Type, name, tag string, f *reflect.StructField) { p.init(typ, name, tag, f, true) } func (p *Properties) init(typ reflect.Type, name, tag string, f *reflect.StructField, lockGetProp bool) { // "bytes,49,opt,def=hello!" p.Name = name p.OrigName = name if tag == "" { return } p.Parse(tag) p.setFieldProps(typ, f, lockGetProp) } var ( propertiesMu sync.RWMutex propertiesMap = make(map[reflect.Type]*StructProperties) ) // GetProperties returns the list of properties for the type represented by t. // t must represent a generated struct type of a protocol message. func GetProperties(t reflect.Type) *StructProperties { if t.Kind() != reflect.Struct { panic("proto: type must have kind struct") } // Most calls to GetProperties in a long-running program will be // retrieving details for types we have seen before. propertiesMu.RLock() sprop, ok := propertiesMap[t] propertiesMu.RUnlock() if ok { return sprop } propertiesMu.Lock() sprop = getPropertiesLocked(t) propertiesMu.Unlock() return sprop } type ( oneofFuncsIface interface { XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{}) } oneofWrappersIface interface { XXX_OneofWrappers() []interface{} } ) // getPropertiesLocked requires that propertiesMu is held. func getPropertiesLocked(t reflect.Type) *StructProperties { if prop, ok := propertiesMap[t]; ok { return prop } prop := new(StructProperties) // in case of recursive protos, fill this in now. propertiesMap[t] = prop // build properties prop.Prop = make([]*Properties, t.NumField()) prop.order = make([]int, t.NumField()) isOneofMessage := false for i := 0; i < t.NumField(); i++ { f := t.Field(i) p := new(Properties) name := f.Name p.init(f.Type, name, f.Tag.Get("protobuf"), &f, false) oneof := f.Tag.Get("protobuf_oneof") // special case if oneof != "" { isOneofMessage = true // Oneof fields don't use the traditional protobuf tag. p.OrigName = oneof } prop.Prop[i] = p prop.order[i] = i if debug { print(i, " ", f.Name, " ", t.String(), " ") if p.Tag > 0 { print(p.String()) } print("\n") } } // Re-order prop.order. sort.Sort(prop) if isOneofMessage { var oots []interface{} switch m := reflect.Zero(reflect.PtrTo(t)).Interface().(type) { case oneofFuncsIface: _, _, _, oots = m.XXX_OneofFuncs() case oneofWrappersIface: oots = m.XXX_OneofWrappers() } if len(oots) > 0 { // Interpret oneof metadata. prop.OneofTypes = make(map[string]*OneofProperties) for _, oot := range oots { oop := &OneofProperties{ Type: reflect.ValueOf(oot).Type(), // *T Prop: new(Properties), } sft := oop.Type.Elem().Field(0) oop.Prop.Name = sft.Name oop.Prop.Parse(sft.Tag.Get("protobuf")) // There will be exactly one interface field that // this new value is assignable to. for i := 0; i < t.NumField(); i++ { f := t.Field(i) if f.Type.Kind() != reflect.Interface { continue } if !oop.Type.AssignableTo(f.Type) { continue } oop.Field = i break } prop.OneofTypes[oop.Prop.OrigName] = oop } } } // build required counts // build tags reqCount := 0 prop.decoderOrigNames = make(map[string]int) for i, p := range prop.Prop { if strings.HasPrefix(p.Name, "XXX_") { // Internal fields should not appear in tags/origNames maps. // They are handled specially when encoding and decoding. continue } if p.Required { reqCount++ } prop.decoderTags.put(p.Tag, i) prop.decoderOrigNames[p.OrigName] = i } prop.reqCount = reqCount return prop } // A global registry of enum types. // The generated code will register the generated maps by calling RegisterEnum. var enumValueMaps = make(map[string]map[string]int32) var enumStringMaps = make(map[string]map[int32]string) // RegisterEnum is called from the generated code to install the enum descriptor // maps into the global table to aid parsing text format protocol buffers. func RegisterEnum(typeName string, unusedNameMap map[int32]string, valueMap map[string]int32) { if _, ok := enumValueMaps[typeName]; ok { panic("proto: duplicate enum registered: " + typeName) } enumValueMaps[typeName] = valueMap if _, ok := enumStringMaps[typeName]; ok { panic("proto: duplicate enum registered: " + typeName) } enumStringMaps[typeName] = unusedNameMap } // EnumValueMap returns the mapping from names to integers of the // enum type enumType, or a nil if not found. func EnumValueMap(enumType string) map[string]int32 { return enumValueMaps[enumType] } // A registry of all linked message types. // The string is a fully-qualified proto name ("pkg.Message"). var ( protoTypedNils = make(map[string]Message) // a map from proto names to typed nil pointers protoMapTypes = make(map[string]reflect.Type) // a map from proto names to map types revProtoTypes = make(map[reflect.Type]string) ) // RegisterType is called from generated code and maps from the fully qualified // proto name to the type (pointer to struct) of the protocol buffer. func RegisterType(x Message, name string) { if _, ok := protoTypedNils[name]; ok { // TODO: Some day, make this a panic. log.Printf("proto: duplicate proto type registered: %s", name) return } t := reflect.TypeOf(x) if v := reflect.ValueOf(x); v.Kind() == reflect.Ptr && v.Pointer() == 0 { // Generated code always calls RegisterType with nil x. // This check is just for extra safety. protoTypedNils[name] = x } else { protoTypedNils[name] = reflect.Zero(t).Interface().(Message) } revProtoTypes[t] = name } // RegisterMapType is called from generated code and maps from the fully qualified // proto name to the native map type of the proto map definition. func RegisterMapType(x interface{}, name string) { if reflect.TypeOf(x).Kind() != reflect.Map { panic(fmt.Sprintf("RegisterMapType(%T, %q); want map", x, name)) } if _, ok := protoMapTypes[name]; ok { log.Printf("proto: duplicate proto type registered: %s", name) return } t := reflect.TypeOf(x) protoMapTypes[name] = t revProtoTypes[t] = name } // MessageName returns the fully-qualified proto name for the given message type. func MessageName(x Message) string { type xname interface { XXX_MessageName() string } if m, ok := x.(xname); ok { return m.XXX_MessageName() } return revProtoTypes[reflect.TypeOf(x)] } // MessageType returns the message type (pointer to struct) for a named message. // The type is not guaranteed to implement proto.Message if the name refers to a // map entry. func MessageType(name string) reflect.Type { if t, ok := protoTypedNils[name]; ok { return reflect.TypeOf(t) } return protoMapTypes[name] } // A registry of all linked proto files. var ( protoFiles = make(map[string][]byte) // file name => fileDescriptor ) // RegisterFile is called from generated code and maps from the // full file name of a .proto file to its compressed FileDescriptorProto. func RegisterFile(filename string, fileDescriptor []byte) { protoFiles[filename] = fileDescriptor } // FileDescriptor returns the compressed FileDescriptorProto for a .proto file. func FileDescriptor(filename string) []byte { return protoFiles[filename] } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/properties_gogo.go ================================================ // Protocol Buffers for Go with Gadgets // // Copyright (c) 2018, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto import ( "reflect" ) var sizerType = reflect.TypeOf((*Sizer)(nil)).Elem() var protosizerType = reflect.TypeOf((*ProtoSizer)(nil)).Elem() ================================================ FILE: vendor/github.com/gogo/protobuf/proto/skip_gogo.go ================================================ // Protocol Buffers for Go with Gadgets // // Copyright (c) 2013, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto import ( "fmt" "io" ) func Skip(data []byte) (n int, err error) { l := len(data) index := 0 for index < l { var wire uint64 for shift := uint(0); ; shift += 7 { if index >= l { return 0, io.ErrUnexpectedEOF } b := data[index] index++ wire |= (uint64(b) & 0x7F) << shift if b < 0x80 { break } } wireType := int(wire & 0x7) switch wireType { case 0: for { if index >= l { return 0, io.ErrUnexpectedEOF } index++ if data[index-1] < 0x80 { break } } return index, nil case 1: index += 8 return index, nil case 2: var length int for shift := uint(0); ; shift += 7 { if index >= l { return 0, io.ErrUnexpectedEOF } b := data[index] index++ length |= (int(b) & 0x7F) << shift if b < 0x80 { break } } index += length return index, nil case 3: for { var innerWire uint64 var start int = index for shift := uint(0); ; shift += 7 { if index >= l { return 0, io.ErrUnexpectedEOF } b := data[index] index++ innerWire |= (uint64(b) & 0x7F) << shift if b < 0x80 { break } } innerWireType := int(innerWire & 0x7) if innerWireType == 4 { break } next, err := Skip(data[start:]) if err != nil { return 0, err } index = start + next } return index, nil case 4: return index, nil case 5: index += 4 return index, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } } panic("unreachable") } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/table_marshal.go ================================================ // Go support for Protocol Buffers - Google's data interchange format // // Copyright 2016 The Go Authors. All rights reserved. // https://github.com/golang/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto import ( "errors" "fmt" "math" "reflect" "sort" "strconv" "strings" "sync" "sync/atomic" "unicode/utf8" ) // a sizer takes a pointer to a field and the size of its tag, computes the size of // the encoded data. type sizer func(pointer, int) int // a marshaler takes a byte slice, a pointer to a field, and its tag (in wire format), // marshals the field to the end of the slice, returns the slice and error (if any). type marshaler func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) // marshalInfo is the information used for marshaling a message. type marshalInfo struct { typ reflect.Type fields []*marshalFieldInfo unrecognized field // offset of XXX_unrecognized extensions field // offset of XXX_InternalExtensions v1extensions field // offset of XXX_extensions sizecache field // offset of XXX_sizecache initialized int32 // 0 -- only typ is set, 1 -- fully initialized messageset bool // uses message set wire format hasmarshaler bool // has custom marshaler sync.RWMutex // protect extElems map, also for initialization extElems map[int32]*marshalElemInfo // info of extension elements hassizer bool // has custom sizer hasprotosizer bool // has custom protosizer bytesExtensions field // offset of XXX_extensions where the field type is []byte } // marshalFieldInfo is the information used for marshaling a field of a message. type marshalFieldInfo struct { field field wiretag uint64 // tag in wire format tagsize int // size of tag in wire format sizer sizer marshaler marshaler isPointer bool required bool // field is required name string // name of the field, for error reporting oneofElems map[reflect.Type]*marshalElemInfo // info of oneof elements } // marshalElemInfo is the information used for marshaling an extension or oneof element. type marshalElemInfo struct { wiretag uint64 // tag in wire format tagsize int // size of tag in wire format sizer sizer marshaler marshaler isptr bool // elem is pointer typed, thus interface of this type is a direct interface (extension only) } var ( marshalInfoMap = map[reflect.Type]*marshalInfo{} marshalInfoLock sync.Mutex uint8SliceType = reflect.TypeOf(([]uint8)(nil)).Kind() ) // getMarshalInfo returns the information to marshal a given type of message. // The info it returns may not necessarily initialized. // t is the type of the message (NOT the pointer to it). func getMarshalInfo(t reflect.Type) *marshalInfo { marshalInfoLock.Lock() u, ok := marshalInfoMap[t] if !ok { u = &marshalInfo{typ: t} marshalInfoMap[t] = u } marshalInfoLock.Unlock() return u } // Size is the entry point from generated code, // and should be ONLY called by generated code. // It computes the size of encoded data of msg. // a is a pointer to a place to store cached marshal info. func (a *InternalMessageInfo) Size(msg Message) int { u := getMessageMarshalInfo(msg, a) ptr := toPointer(&msg) if ptr.isNil() { // We get here if msg is a typed nil ((*SomeMessage)(nil)), // so it satisfies the interface, and msg == nil wouldn't // catch it. We don't want crash in this case. return 0 } return u.size(ptr) } // Marshal is the entry point from generated code, // and should be ONLY called by generated code. // It marshals msg to the end of b. // a is a pointer to a place to store cached marshal info. func (a *InternalMessageInfo) Marshal(b []byte, msg Message, deterministic bool) ([]byte, error) { u := getMessageMarshalInfo(msg, a) ptr := toPointer(&msg) if ptr.isNil() { // We get here if msg is a typed nil ((*SomeMessage)(nil)), // so it satisfies the interface, and msg == nil wouldn't // catch it. We don't want crash in this case. return b, ErrNil } return u.marshal(b, ptr, deterministic) } func getMessageMarshalInfo(msg interface{}, a *InternalMessageInfo) *marshalInfo { // u := a.marshal, but atomically. // We use an atomic here to ensure memory consistency. u := atomicLoadMarshalInfo(&a.marshal) if u == nil { // Get marshal information from type of message. t := reflect.ValueOf(msg).Type() if t.Kind() != reflect.Ptr { panic(fmt.Sprintf("cannot handle non-pointer message type %v", t)) } u = getMarshalInfo(t.Elem()) // Store it in the cache for later users. // a.marshal = u, but atomically. atomicStoreMarshalInfo(&a.marshal, u) } return u } // size is the main function to compute the size of the encoded data of a message. // ptr is the pointer to the message. func (u *marshalInfo) size(ptr pointer) int { if atomic.LoadInt32(&u.initialized) == 0 { u.computeMarshalInfo() } // If the message can marshal itself, let it do it, for compatibility. // NOTE: This is not efficient. if u.hasmarshaler { // Uses the message's Size method if available if u.hassizer { s := ptr.asPointerTo(u.typ).Interface().(Sizer) return s.Size() } // Uses the message's ProtoSize method if available if u.hasprotosizer { s := ptr.asPointerTo(u.typ).Interface().(ProtoSizer) return s.ProtoSize() } m := ptr.asPointerTo(u.typ).Interface().(Marshaler) b, _ := m.Marshal() return len(b) } n := 0 for _, f := range u.fields { if f.isPointer && ptr.offset(f.field).getPointer().isNil() { // nil pointer always marshals to nothing continue } n += f.sizer(ptr.offset(f.field), f.tagsize) } if u.extensions.IsValid() { e := ptr.offset(u.extensions).toExtensions() if u.messageset { n += u.sizeMessageSet(e) } else { n += u.sizeExtensions(e) } } if u.v1extensions.IsValid() { m := *ptr.offset(u.v1extensions).toOldExtensions() n += u.sizeV1Extensions(m) } if u.bytesExtensions.IsValid() { s := *ptr.offset(u.bytesExtensions).toBytes() n += len(s) } if u.unrecognized.IsValid() { s := *ptr.offset(u.unrecognized).toBytes() n += len(s) } // cache the result for use in marshal if u.sizecache.IsValid() { atomic.StoreInt32(ptr.offset(u.sizecache).toInt32(), int32(n)) } return n } // cachedsize gets the size from cache. If there is no cache (i.e. message is not generated), // fall back to compute the size. func (u *marshalInfo) cachedsize(ptr pointer) int { if u.sizecache.IsValid() { return int(atomic.LoadInt32(ptr.offset(u.sizecache).toInt32())) } return u.size(ptr) } // marshal is the main function to marshal a message. It takes a byte slice and appends // the encoded data to the end of the slice, returns the slice and error (if any). // ptr is the pointer to the message. // If deterministic is true, map is marshaled in deterministic order. func (u *marshalInfo) marshal(b []byte, ptr pointer, deterministic bool) ([]byte, error) { if atomic.LoadInt32(&u.initialized) == 0 { u.computeMarshalInfo() } // If the message can marshal itself, let it do it, for compatibility. // NOTE: This is not efficient. if u.hasmarshaler { m := ptr.asPointerTo(u.typ).Interface().(Marshaler) b1, err := m.Marshal() b = append(b, b1...) return b, err } var err, errLater error // The old marshaler encodes extensions at beginning. if u.extensions.IsValid() { e := ptr.offset(u.extensions).toExtensions() if u.messageset { b, err = u.appendMessageSet(b, e, deterministic) } else { b, err = u.appendExtensions(b, e, deterministic) } if err != nil { return b, err } } if u.v1extensions.IsValid() { m := *ptr.offset(u.v1extensions).toOldExtensions() b, err = u.appendV1Extensions(b, m, deterministic) if err != nil { return b, err } } if u.bytesExtensions.IsValid() { s := *ptr.offset(u.bytesExtensions).toBytes() b = append(b, s...) } for _, f := range u.fields { if f.required { if f.isPointer && ptr.offset(f.field).getPointer().isNil() { // Required field is not set. // We record the error but keep going, to give a complete marshaling. if errLater == nil { errLater = &RequiredNotSetError{f.name} } continue } } if f.isPointer && ptr.offset(f.field).getPointer().isNil() { // nil pointer always marshals to nothing continue } b, err = f.marshaler(b, ptr.offset(f.field), f.wiretag, deterministic) if err != nil { if err1, ok := err.(*RequiredNotSetError); ok { // Required field in submessage is not set. // We record the error but keep going, to give a complete marshaling. if errLater == nil { errLater = &RequiredNotSetError{f.name + "." + err1.field} } continue } if err == errRepeatedHasNil { err = errors.New("proto: repeated field " + f.name + " has nil element") } if err == errInvalidUTF8 { if errLater == nil { fullName := revProtoTypes[reflect.PtrTo(u.typ)] + "." + f.name errLater = &invalidUTF8Error{fullName} } continue } return b, err } } if u.unrecognized.IsValid() { s := *ptr.offset(u.unrecognized).toBytes() b = append(b, s...) } return b, errLater } // computeMarshalInfo initializes the marshal info. func (u *marshalInfo) computeMarshalInfo() { u.Lock() defer u.Unlock() if u.initialized != 0 { // non-atomic read is ok as it is protected by the lock return } t := u.typ u.unrecognized = invalidField u.extensions = invalidField u.v1extensions = invalidField u.bytesExtensions = invalidField u.sizecache = invalidField isOneofMessage := false if reflect.PtrTo(t).Implements(sizerType) { u.hassizer = true } if reflect.PtrTo(t).Implements(protosizerType) { u.hasprotosizer = true } // If the message can marshal itself, let it do it, for compatibility. // NOTE: This is not efficient. if reflect.PtrTo(t).Implements(marshalerType) { u.hasmarshaler = true atomic.StoreInt32(&u.initialized, 1) return } n := t.NumField() // deal with XXX fields first for i := 0; i < t.NumField(); i++ { f := t.Field(i) if f.Tag.Get("protobuf_oneof") != "" { isOneofMessage = true } if !strings.HasPrefix(f.Name, "XXX_") { continue } switch f.Name { case "XXX_sizecache": u.sizecache = toField(&f) case "XXX_unrecognized": u.unrecognized = toField(&f) case "XXX_InternalExtensions": u.extensions = toField(&f) u.messageset = f.Tag.Get("protobuf_messageset") == "1" case "XXX_extensions": if f.Type.Kind() == reflect.Map { u.v1extensions = toField(&f) } else { u.bytesExtensions = toField(&f) } case "XXX_NoUnkeyedLiteral": // nothing to do default: panic("unknown XXX field: " + f.Name) } n-- } // get oneof implementers var oneofImplementers []interface{} // gogo: isOneofMessage is needed for embedded oneof messages, without a marshaler and unmarshaler if isOneofMessage { switch m := reflect.Zero(reflect.PtrTo(t)).Interface().(type) { case oneofFuncsIface: _, _, _, oneofImplementers = m.XXX_OneofFuncs() case oneofWrappersIface: oneofImplementers = m.XXX_OneofWrappers() } } // normal fields fields := make([]marshalFieldInfo, n) // batch allocation u.fields = make([]*marshalFieldInfo, 0, n) for i, j := 0, 0; i < t.NumField(); i++ { f := t.Field(i) if strings.HasPrefix(f.Name, "XXX_") { continue } field := &fields[j] j++ field.name = f.Name u.fields = append(u.fields, field) if f.Tag.Get("protobuf_oneof") != "" { field.computeOneofFieldInfo(&f, oneofImplementers) continue } if f.Tag.Get("protobuf") == "" { // field has no tag (not in generated message), ignore it u.fields = u.fields[:len(u.fields)-1] j-- continue } field.computeMarshalFieldInfo(&f) } // fields are marshaled in tag order on the wire. sort.Sort(byTag(u.fields)) atomic.StoreInt32(&u.initialized, 1) } // helper for sorting fields by tag type byTag []*marshalFieldInfo func (a byTag) Len() int { return len(a) } func (a byTag) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a byTag) Less(i, j int) bool { return a[i].wiretag < a[j].wiretag } // getExtElemInfo returns the information to marshal an extension element. // The info it returns is initialized. func (u *marshalInfo) getExtElemInfo(desc *ExtensionDesc) *marshalElemInfo { // get from cache first u.RLock() e, ok := u.extElems[desc.Field] u.RUnlock() if ok { return e } t := reflect.TypeOf(desc.ExtensionType) // pointer or slice to basic type or struct tags := strings.Split(desc.Tag, ",") tag, err := strconv.Atoi(tags[1]) if err != nil { panic("tag is not an integer") } wt := wiretype(tags[0]) sizr, marshalr := typeMarshaler(t, tags, false, false) e = &marshalElemInfo{ wiretag: uint64(tag)<<3 | wt, tagsize: SizeVarint(uint64(tag) << 3), sizer: sizr, marshaler: marshalr, isptr: t.Kind() == reflect.Ptr, } // update cache u.Lock() if u.extElems == nil { u.extElems = make(map[int32]*marshalElemInfo) } u.extElems[desc.Field] = e u.Unlock() return e } // computeMarshalFieldInfo fills up the information to marshal a field. func (fi *marshalFieldInfo) computeMarshalFieldInfo(f *reflect.StructField) { // parse protobuf tag of the field. // tag has format of "bytes,49,opt,name=foo,def=hello!" tags := strings.Split(f.Tag.Get("protobuf"), ",") if tags[0] == "" { return } tag, err := strconv.Atoi(tags[1]) if err != nil { panic("tag is not an integer") } wt := wiretype(tags[0]) if tags[2] == "req" { fi.required = true } fi.setTag(f, tag, wt) fi.setMarshaler(f, tags) } func (fi *marshalFieldInfo) computeOneofFieldInfo(f *reflect.StructField, oneofImplementers []interface{}) { fi.field = toField(f) fi.wiretag = math.MaxInt32 // Use a large tag number, make oneofs sorted at the end. This tag will not appear on the wire. fi.isPointer = true fi.sizer, fi.marshaler = makeOneOfMarshaler(fi, f) fi.oneofElems = make(map[reflect.Type]*marshalElemInfo) ityp := f.Type // interface type for _, o := range oneofImplementers { t := reflect.TypeOf(o) if !t.Implements(ityp) { continue } sf := t.Elem().Field(0) // oneof implementer is a struct with a single field tags := strings.Split(sf.Tag.Get("protobuf"), ",") tag, err := strconv.Atoi(tags[1]) if err != nil { panic("tag is not an integer") } wt := wiretype(tags[0]) sizr, marshalr := typeMarshaler(sf.Type, tags, false, true) // oneof should not omit any zero value fi.oneofElems[t.Elem()] = &marshalElemInfo{ wiretag: uint64(tag)<<3 | wt, tagsize: SizeVarint(uint64(tag) << 3), sizer: sizr, marshaler: marshalr, } } } // wiretype returns the wire encoding of the type. func wiretype(encoding string) uint64 { switch encoding { case "fixed32": return WireFixed32 case "fixed64": return WireFixed64 case "varint", "zigzag32", "zigzag64": return WireVarint case "bytes": return WireBytes case "group": return WireStartGroup } panic("unknown wire type " + encoding) } // setTag fills up the tag (in wire format) and its size in the info of a field. func (fi *marshalFieldInfo) setTag(f *reflect.StructField, tag int, wt uint64) { fi.field = toField(f) fi.wiretag = uint64(tag)<<3 | wt fi.tagsize = SizeVarint(uint64(tag) << 3) } // setMarshaler fills up the sizer and marshaler in the info of a field. func (fi *marshalFieldInfo) setMarshaler(f *reflect.StructField, tags []string) { switch f.Type.Kind() { case reflect.Map: // map field fi.isPointer = true fi.sizer, fi.marshaler = makeMapMarshaler(f) return case reflect.Ptr, reflect.Slice: fi.isPointer = true } fi.sizer, fi.marshaler = typeMarshaler(f.Type, tags, true, false) } // typeMarshaler returns the sizer and marshaler of a given field. // t is the type of the field. // tags is the generated "protobuf" tag of the field. // If nozero is true, zero value is not marshaled to the wire. // If oneof is true, it is a oneof field. func typeMarshaler(t reflect.Type, tags []string, nozero, oneof bool) (sizer, marshaler) { encoding := tags[0] pointer := false slice := false if t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8 { slice = true t = t.Elem() } if t.Kind() == reflect.Ptr { pointer = true t = t.Elem() } packed := false proto3 := false ctype := false isTime := false isDuration := false isWktPointer := false validateUTF8 := true for i := 2; i < len(tags); i++ { if tags[i] == "packed" { packed = true } if tags[i] == "proto3" { proto3 = true } if strings.HasPrefix(tags[i], "customtype=") { ctype = true } if tags[i] == "stdtime" { isTime = true } if tags[i] == "stdduration" { isDuration = true } if tags[i] == "wktptr" { isWktPointer = true } } validateUTF8 = validateUTF8 && proto3 if !proto3 && !pointer && !slice { nozero = false } if ctype { if reflect.PtrTo(t).Implements(customType) { if slice { return makeMessageRefSliceMarshaler(getMarshalInfo(t)) } if pointer { return makeCustomPtrMarshaler(getMarshalInfo(t)) } return makeCustomMarshaler(getMarshalInfo(t)) } else { panic(fmt.Sprintf("custom type: type: %v, does not implement the proto.custom interface", t)) } } if isTime { if pointer { if slice { return makeTimePtrSliceMarshaler(getMarshalInfo(t)) } return makeTimePtrMarshaler(getMarshalInfo(t)) } if slice { return makeTimeSliceMarshaler(getMarshalInfo(t)) } return makeTimeMarshaler(getMarshalInfo(t)) } if isDuration { if pointer { if slice { return makeDurationPtrSliceMarshaler(getMarshalInfo(t)) } return makeDurationPtrMarshaler(getMarshalInfo(t)) } if slice { return makeDurationSliceMarshaler(getMarshalInfo(t)) } return makeDurationMarshaler(getMarshalInfo(t)) } if isWktPointer { switch t.Kind() { case reflect.Float64: if pointer { if slice { return makeStdDoubleValuePtrSliceMarshaler(getMarshalInfo(t)) } return makeStdDoubleValuePtrMarshaler(getMarshalInfo(t)) } if slice { return makeStdDoubleValueSliceMarshaler(getMarshalInfo(t)) } return makeStdDoubleValueMarshaler(getMarshalInfo(t)) case reflect.Float32: if pointer { if slice { return makeStdFloatValuePtrSliceMarshaler(getMarshalInfo(t)) } return makeStdFloatValuePtrMarshaler(getMarshalInfo(t)) } if slice { return makeStdFloatValueSliceMarshaler(getMarshalInfo(t)) } return makeStdFloatValueMarshaler(getMarshalInfo(t)) case reflect.Int64: if pointer { if slice { return makeStdInt64ValuePtrSliceMarshaler(getMarshalInfo(t)) } return makeStdInt64ValuePtrMarshaler(getMarshalInfo(t)) } if slice { return makeStdInt64ValueSliceMarshaler(getMarshalInfo(t)) } return makeStdInt64ValueMarshaler(getMarshalInfo(t)) case reflect.Uint64: if pointer { if slice { return makeStdUInt64ValuePtrSliceMarshaler(getMarshalInfo(t)) } return makeStdUInt64ValuePtrMarshaler(getMarshalInfo(t)) } if slice { return makeStdUInt64ValueSliceMarshaler(getMarshalInfo(t)) } return makeStdUInt64ValueMarshaler(getMarshalInfo(t)) case reflect.Int32: if pointer { if slice { return makeStdInt32ValuePtrSliceMarshaler(getMarshalInfo(t)) } return makeStdInt32ValuePtrMarshaler(getMarshalInfo(t)) } if slice { return makeStdInt32ValueSliceMarshaler(getMarshalInfo(t)) } return makeStdInt32ValueMarshaler(getMarshalInfo(t)) case reflect.Uint32: if pointer { if slice { return makeStdUInt32ValuePtrSliceMarshaler(getMarshalInfo(t)) } return makeStdUInt32ValuePtrMarshaler(getMarshalInfo(t)) } if slice { return makeStdUInt32ValueSliceMarshaler(getMarshalInfo(t)) } return makeStdUInt32ValueMarshaler(getMarshalInfo(t)) case reflect.Bool: if pointer { if slice { return makeStdBoolValuePtrSliceMarshaler(getMarshalInfo(t)) } return makeStdBoolValuePtrMarshaler(getMarshalInfo(t)) } if slice { return makeStdBoolValueSliceMarshaler(getMarshalInfo(t)) } return makeStdBoolValueMarshaler(getMarshalInfo(t)) case reflect.String: if pointer { if slice { return makeStdStringValuePtrSliceMarshaler(getMarshalInfo(t)) } return makeStdStringValuePtrMarshaler(getMarshalInfo(t)) } if slice { return makeStdStringValueSliceMarshaler(getMarshalInfo(t)) } return makeStdStringValueMarshaler(getMarshalInfo(t)) case uint8SliceType: if pointer { if slice { return makeStdBytesValuePtrSliceMarshaler(getMarshalInfo(t)) } return makeStdBytesValuePtrMarshaler(getMarshalInfo(t)) } if slice { return makeStdBytesValueSliceMarshaler(getMarshalInfo(t)) } return makeStdBytesValueMarshaler(getMarshalInfo(t)) default: panic(fmt.Sprintf("unknown wktpointer type %#v", t)) } } switch t.Kind() { case reflect.Bool: if pointer { return sizeBoolPtr, appendBoolPtr } if slice { if packed { return sizeBoolPackedSlice, appendBoolPackedSlice } return sizeBoolSlice, appendBoolSlice } if nozero { return sizeBoolValueNoZero, appendBoolValueNoZero } return sizeBoolValue, appendBoolValue case reflect.Uint32: switch encoding { case "fixed32": if pointer { return sizeFixed32Ptr, appendFixed32Ptr } if slice { if packed { return sizeFixed32PackedSlice, appendFixed32PackedSlice } return sizeFixed32Slice, appendFixed32Slice } if nozero { return sizeFixed32ValueNoZero, appendFixed32ValueNoZero } return sizeFixed32Value, appendFixed32Value case "varint": if pointer { return sizeVarint32Ptr, appendVarint32Ptr } if slice { if packed { return sizeVarint32PackedSlice, appendVarint32PackedSlice } return sizeVarint32Slice, appendVarint32Slice } if nozero { return sizeVarint32ValueNoZero, appendVarint32ValueNoZero } return sizeVarint32Value, appendVarint32Value } case reflect.Int32: switch encoding { case "fixed32": if pointer { return sizeFixedS32Ptr, appendFixedS32Ptr } if slice { if packed { return sizeFixedS32PackedSlice, appendFixedS32PackedSlice } return sizeFixedS32Slice, appendFixedS32Slice } if nozero { return sizeFixedS32ValueNoZero, appendFixedS32ValueNoZero } return sizeFixedS32Value, appendFixedS32Value case "varint": if pointer { return sizeVarintS32Ptr, appendVarintS32Ptr } if slice { if packed { return sizeVarintS32PackedSlice, appendVarintS32PackedSlice } return sizeVarintS32Slice, appendVarintS32Slice } if nozero { return sizeVarintS32ValueNoZero, appendVarintS32ValueNoZero } return sizeVarintS32Value, appendVarintS32Value case "zigzag32": if pointer { return sizeZigzag32Ptr, appendZigzag32Ptr } if slice { if packed { return sizeZigzag32PackedSlice, appendZigzag32PackedSlice } return sizeZigzag32Slice, appendZigzag32Slice } if nozero { return sizeZigzag32ValueNoZero, appendZigzag32ValueNoZero } return sizeZigzag32Value, appendZigzag32Value } case reflect.Uint64: switch encoding { case "fixed64": if pointer { return sizeFixed64Ptr, appendFixed64Ptr } if slice { if packed { return sizeFixed64PackedSlice, appendFixed64PackedSlice } return sizeFixed64Slice, appendFixed64Slice } if nozero { return sizeFixed64ValueNoZero, appendFixed64ValueNoZero } return sizeFixed64Value, appendFixed64Value case "varint": if pointer { return sizeVarint64Ptr, appendVarint64Ptr } if slice { if packed { return sizeVarint64PackedSlice, appendVarint64PackedSlice } return sizeVarint64Slice, appendVarint64Slice } if nozero { return sizeVarint64ValueNoZero, appendVarint64ValueNoZero } return sizeVarint64Value, appendVarint64Value } case reflect.Int64: switch encoding { case "fixed64": if pointer { return sizeFixedS64Ptr, appendFixedS64Ptr } if slice { if packed { return sizeFixedS64PackedSlice, appendFixedS64PackedSlice } return sizeFixedS64Slice, appendFixedS64Slice } if nozero { return sizeFixedS64ValueNoZero, appendFixedS64ValueNoZero } return sizeFixedS64Value, appendFixedS64Value case "varint": if pointer { return sizeVarintS64Ptr, appendVarintS64Ptr } if slice { if packed { return sizeVarintS64PackedSlice, appendVarintS64PackedSlice } return sizeVarintS64Slice, appendVarintS64Slice } if nozero { return sizeVarintS64ValueNoZero, appendVarintS64ValueNoZero } return sizeVarintS64Value, appendVarintS64Value case "zigzag64": if pointer { return sizeZigzag64Ptr, appendZigzag64Ptr } if slice { if packed { return sizeZigzag64PackedSlice, appendZigzag64PackedSlice } return sizeZigzag64Slice, appendZigzag64Slice } if nozero { return sizeZigzag64ValueNoZero, appendZigzag64ValueNoZero } return sizeZigzag64Value, appendZigzag64Value } case reflect.Float32: if pointer { return sizeFloat32Ptr, appendFloat32Ptr } if slice { if packed { return sizeFloat32PackedSlice, appendFloat32PackedSlice } return sizeFloat32Slice, appendFloat32Slice } if nozero { return sizeFloat32ValueNoZero, appendFloat32ValueNoZero } return sizeFloat32Value, appendFloat32Value case reflect.Float64: if pointer { return sizeFloat64Ptr, appendFloat64Ptr } if slice { if packed { return sizeFloat64PackedSlice, appendFloat64PackedSlice } return sizeFloat64Slice, appendFloat64Slice } if nozero { return sizeFloat64ValueNoZero, appendFloat64ValueNoZero } return sizeFloat64Value, appendFloat64Value case reflect.String: if validateUTF8 { if pointer { return sizeStringPtr, appendUTF8StringPtr } if slice { return sizeStringSlice, appendUTF8StringSlice } if nozero { return sizeStringValueNoZero, appendUTF8StringValueNoZero } return sizeStringValue, appendUTF8StringValue } if pointer { return sizeStringPtr, appendStringPtr } if slice { return sizeStringSlice, appendStringSlice } if nozero { return sizeStringValueNoZero, appendStringValueNoZero } return sizeStringValue, appendStringValue case reflect.Slice: if slice { return sizeBytesSlice, appendBytesSlice } if oneof { // Oneof bytes field may also have "proto3" tag. // We want to marshal it as a oneof field. Do this // check before the proto3 check. return sizeBytesOneof, appendBytesOneof } if proto3 { return sizeBytes3, appendBytes3 } return sizeBytes, appendBytes case reflect.Struct: switch encoding { case "group": if slice { return makeGroupSliceMarshaler(getMarshalInfo(t)) } return makeGroupMarshaler(getMarshalInfo(t)) case "bytes": if pointer { if slice { return makeMessageSliceMarshaler(getMarshalInfo(t)) } return makeMessageMarshaler(getMarshalInfo(t)) } else { if slice { return makeMessageRefSliceMarshaler(getMarshalInfo(t)) } return makeMessageRefMarshaler(getMarshalInfo(t)) } } } panic(fmt.Sprintf("unknown or mismatched type: type: %v, wire type: %v", t, encoding)) } // Below are functions to size/marshal a specific type of a field. // They are stored in the field's info, and called by function pointers. // They have type sizer or marshaler. func sizeFixed32Value(_ pointer, tagsize int) int { return 4 + tagsize } func sizeFixed32ValueNoZero(ptr pointer, tagsize int) int { v := *ptr.toUint32() if v == 0 { return 0 } return 4 + tagsize } func sizeFixed32Ptr(ptr pointer, tagsize int) int { p := *ptr.toUint32Ptr() if p == nil { return 0 } return 4 + tagsize } func sizeFixed32Slice(ptr pointer, tagsize int) int { s := *ptr.toUint32Slice() return (4 + tagsize) * len(s) } func sizeFixed32PackedSlice(ptr pointer, tagsize int) int { s := *ptr.toUint32Slice() if len(s) == 0 { return 0 } return 4*len(s) + SizeVarint(uint64(4*len(s))) + tagsize } func sizeFixedS32Value(_ pointer, tagsize int) int { return 4 + tagsize } func sizeFixedS32ValueNoZero(ptr pointer, tagsize int) int { v := *ptr.toInt32() if v == 0 { return 0 } return 4 + tagsize } func sizeFixedS32Ptr(ptr pointer, tagsize int) int { p := ptr.getInt32Ptr() if p == nil { return 0 } return 4 + tagsize } func sizeFixedS32Slice(ptr pointer, tagsize int) int { s := ptr.getInt32Slice() return (4 + tagsize) * len(s) } func sizeFixedS32PackedSlice(ptr pointer, tagsize int) int { s := ptr.getInt32Slice() if len(s) == 0 { return 0 } return 4*len(s) + SizeVarint(uint64(4*len(s))) + tagsize } func sizeFloat32Value(_ pointer, tagsize int) int { return 4 + tagsize } func sizeFloat32ValueNoZero(ptr pointer, tagsize int) int { v := math.Float32bits(*ptr.toFloat32()) if v == 0 { return 0 } return 4 + tagsize } func sizeFloat32Ptr(ptr pointer, tagsize int) int { p := *ptr.toFloat32Ptr() if p == nil { return 0 } return 4 + tagsize } func sizeFloat32Slice(ptr pointer, tagsize int) int { s := *ptr.toFloat32Slice() return (4 + tagsize) * len(s) } func sizeFloat32PackedSlice(ptr pointer, tagsize int) int { s := *ptr.toFloat32Slice() if len(s) == 0 { return 0 } return 4*len(s) + SizeVarint(uint64(4*len(s))) + tagsize } func sizeFixed64Value(_ pointer, tagsize int) int { return 8 + tagsize } func sizeFixed64ValueNoZero(ptr pointer, tagsize int) int { v := *ptr.toUint64() if v == 0 { return 0 } return 8 + tagsize } func sizeFixed64Ptr(ptr pointer, tagsize int) int { p := *ptr.toUint64Ptr() if p == nil { return 0 } return 8 + tagsize } func sizeFixed64Slice(ptr pointer, tagsize int) int { s := *ptr.toUint64Slice() return (8 + tagsize) * len(s) } func sizeFixed64PackedSlice(ptr pointer, tagsize int) int { s := *ptr.toUint64Slice() if len(s) == 0 { return 0 } return 8*len(s) + SizeVarint(uint64(8*len(s))) + tagsize } func sizeFixedS64Value(_ pointer, tagsize int) int { return 8 + tagsize } func sizeFixedS64ValueNoZero(ptr pointer, tagsize int) int { v := *ptr.toInt64() if v == 0 { return 0 } return 8 + tagsize } func sizeFixedS64Ptr(ptr pointer, tagsize int) int { p := *ptr.toInt64Ptr() if p == nil { return 0 } return 8 + tagsize } func sizeFixedS64Slice(ptr pointer, tagsize int) int { s := *ptr.toInt64Slice() return (8 + tagsize) * len(s) } func sizeFixedS64PackedSlice(ptr pointer, tagsize int) int { s := *ptr.toInt64Slice() if len(s) == 0 { return 0 } return 8*len(s) + SizeVarint(uint64(8*len(s))) + tagsize } func sizeFloat64Value(_ pointer, tagsize int) int { return 8 + tagsize } func sizeFloat64ValueNoZero(ptr pointer, tagsize int) int { v := math.Float64bits(*ptr.toFloat64()) if v == 0 { return 0 } return 8 + tagsize } func sizeFloat64Ptr(ptr pointer, tagsize int) int { p := *ptr.toFloat64Ptr() if p == nil { return 0 } return 8 + tagsize } func sizeFloat64Slice(ptr pointer, tagsize int) int { s := *ptr.toFloat64Slice() return (8 + tagsize) * len(s) } func sizeFloat64PackedSlice(ptr pointer, tagsize int) int { s := *ptr.toFloat64Slice() if len(s) == 0 { return 0 } return 8*len(s) + SizeVarint(uint64(8*len(s))) + tagsize } func sizeVarint32Value(ptr pointer, tagsize int) int { v := *ptr.toUint32() return SizeVarint(uint64(v)) + tagsize } func sizeVarint32ValueNoZero(ptr pointer, tagsize int) int { v := *ptr.toUint32() if v == 0 { return 0 } return SizeVarint(uint64(v)) + tagsize } func sizeVarint32Ptr(ptr pointer, tagsize int) int { p := *ptr.toUint32Ptr() if p == nil { return 0 } return SizeVarint(uint64(*p)) + tagsize } func sizeVarint32Slice(ptr pointer, tagsize int) int { s := *ptr.toUint32Slice() n := 0 for _, v := range s { n += SizeVarint(uint64(v)) + tagsize } return n } func sizeVarint32PackedSlice(ptr pointer, tagsize int) int { s := *ptr.toUint32Slice() if len(s) == 0 { return 0 } n := 0 for _, v := range s { n += SizeVarint(uint64(v)) } return n + SizeVarint(uint64(n)) + tagsize } func sizeVarintS32Value(ptr pointer, tagsize int) int { v := *ptr.toInt32() return SizeVarint(uint64(v)) + tagsize } func sizeVarintS32ValueNoZero(ptr pointer, tagsize int) int { v := *ptr.toInt32() if v == 0 { return 0 } return SizeVarint(uint64(v)) + tagsize } func sizeVarintS32Ptr(ptr pointer, tagsize int) int { p := ptr.getInt32Ptr() if p == nil { return 0 } return SizeVarint(uint64(*p)) + tagsize } func sizeVarintS32Slice(ptr pointer, tagsize int) int { s := ptr.getInt32Slice() n := 0 for _, v := range s { n += SizeVarint(uint64(v)) + tagsize } return n } func sizeVarintS32PackedSlice(ptr pointer, tagsize int) int { s := ptr.getInt32Slice() if len(s) == 0 { return 0 } n := 0 for _, v := range s { n += SizeVarint(uint64(v)) } return n + SizeVarint(uint64(n)) + tagsize } func sizeVarint64Value(ptr pointer, tagsize int) int { v := *ptr.toUint64() return SizeVarint(v) + tagsize } func sizeVarint64ValueNoZero(ptr pointer, tagsize int) int { v := *ptr.toUint64() if v == 0 { return 0 } return SizeVarint(v) + tagsize } func sizeVarint64Ptr(ptr pointer, tagsize int) int { p := *ptr.toUint64Ptr() if p == nil { return 0 } return SizeVarint(*p) + tagsize } func sizeVarint64Slice(ptr pointer, tagsize int) int { s := *ptr.toUint64Slice() n := 0 for _, v := range s { n += SizeVarint(v) + tagsize } return n } func sizeVarint64PackedSlice(ptr pointer, tagsize int) int { s := *ptr.toUint64Slice() if len(s) == 0 { return 0 } n := 0 for _, v := range s { n += SizeVarint(v) } return n + SizeVarint(uint64(n)) + tagsize } func sizeVarintS64Value(ptr pointer, tagsize int) int { v := *ptr.toInt64() return SizeVarint(uint64(v)) + tagsize } func sizeVarintS64ValueNoZero(ptr pointer, tagsize int) int { v := *ptr.toInt64() if v == 0 { return 0 } return SizeVarint(uint64(v)) + tagsize } func sizeVarintS64Ptr(ptr pointer, tagsize int) int { p := *ptr.toInt64Ptr() if p == nil { return 0 } return SizeVarint(uint64(*p)) + tagsize } func sizeVarintS64Slice(ptr pointer, tagsize int) int { s := *ptr.toInt64Slice() n := 0 for _, v := range s { n += SizeVarint(uint64(v)) + tagsize } return n } func sizeVarintS64PackedSlice(ptr pointer, tagsize int) int { s := *ptr.toInt64Slice() if len(s) == 0 { return 0 } n := 0 for _, v := range s { n += SizeVarint(uint64(v)) } return n + SizeVarint(uint64(n)) + tagsize } func sizeZigzag32Value(ptr pointer, tagsize int) int { v := *ptr.toInt32() return SizeVarint(uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) + tagsize } func sizeZigzag32ValueNoZero(ptr pointer, tagsize int) int { v := *ptr.toInt32() if v == 0 { return 0 } return SizeVarint(uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) + tagsize } func sizeZigzag32Ptr(ptr pointer, tagsize int) int { p := ptr.getInt32Ptr() if p == nil { return 0 } v := *p return SizeVarint(uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) + tagsize } func sizeZigzag32Slice(ptr pointer, tagsize int) int { s := ptr.getInt32Slice() n := 0 for _, v := range s { n += SizeVarint(uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) + tagsize } return n } func sizeZigzag32PackedSlice(ptr pointer, tagsize int) int { s := ptr.getInt32Slice() if len(s) == 0 { return 0 } n := 0 for _, v := range s { n += SizeVarint(uint64((uint32(v) << 1) ^ uint32((int32(v) >> 31)))) } return n + SizeVarint(uint64(n)) + tagsize } func sizeZigzag64Value(ptr pointer, tagsize int) int { v := *ptr.toInt64() return SizeVarint(uint64(v<<1)^uint64((int64(v)>>63))) + tagsize } func sizeZigzag64ValueNoZero(ptr pointer, tagsize int) int { v := *ptr.toInt64() if v == 0 { return 0 } return SizeVarint(uint64(v<<1)^uint64((int64(v)>>63))) + tagsize } func sizeZigzag64Ptr(ptr pointer, tagsize int) int { p := *ptr.toInt64Ptr() if p == nil { return 0 } v := *p return SizeVarint(uint64(v<<1)^uint64((int64(v)>>63))) + tagsize } func sizeZigzag64Slice(ptr pointer, tagsize int) int { s := *ptr.toInt64Slice() n := 0 for _, v := range s { n += SizeVarint(uint64(v<<1)^uint64((int64(v)>>63))) + tagsize } return n } func sizeZigzag64PackedSlice(ptr pointer, tagsize int) int { s := *ptr.toInt64Slice() if len(s) == 0 { return 0 } n := 0 for _, v := range s { n += SizeVarint(uint64(v<<1) ^ uint64((int64(v) >> 63))) } return n + SizeVarint(uint64(n)) + tagsize } func sizeBoolValue(_ pointer, tagsize int) int { return 1 + tagsize } func sizeBoolValueNoZero(ptr pointer, tagsize int) int { v := *ptr.toBool() if !v { return 0 } return 1 + tagsize } func sizeBoolPtr(ptr pointer, tagsize int) int { p := *ptr.toBoolPtr() if p == nil { return 0 } return 1 + tagsize } func sizeBoolSlice(ptr pointer, tagsize int) int { s := *ptr.toBoolSlice() return (1 + tagsize) * len(s) } func sizeBoolPackedSlice(ptr pointer, tagsize int) int { s := *ptr.toBoolSlice() if len(s) == 0 { return 0 } return len(s) + SizeVarint(uint64(len(s))) + tagsize } func sizeStringValue(ptr pointer, tagsize int) int { v := *ptr.toString() return len(v) + SizeVarint(uint64(len(v))) + tagsize } func sizeStringValueNoZero(ptr pointer, tagsize int) int { v := *ptr.toString() if v == "" { return 0 } return len(v) + SizeVarint(uint64(len(v))) + tagsize } func sizeStringPtr(ptr pointer, tagsize int) int { p := *ptr.toStringPtr() if p == nil { return 0 } v := *p return len(v) + SizeVarint(uint64(len(v))) + tagsize } func sizeStringSlice(ptr pointer, tagsize int) int { s := *ptr.toStringSlice() n := 0 for _, v := range s { n += len(v) + SizeVarint(uint64(len(v))) + tagsize } return n } func sizeBytes(ptr pointer, tagsize int) int { v := *ptr.toBytes() if v == nil { return 0 } return len(v) + SizeVarint(uint64(len(v))) + tagsize } func sizeBytes3(ptr pointer, tagsize int) int { v := *ptr.toBytes() if len(v) == 0 { return 0 } return len(v) + SizeVarint(uint64(len(v))) + tagsize } func sizeBytesOneof(ptr pointer, tagsize int) int { v := *ptr.toBytes() return len(v) + SizeVarint(uint64(len(v))) + tagsize } func sizeBytesSlice(ptr pointer, tagsize int) int { s := *ptr.toBytesSlice() n := 0 for _, v := range s { n += len(v) + SizeVarint(uint64(len(v))) + tagsize } return n } // appendFixed32 appends an encoded fixed32 to b. func appendFixed32(b []byte, v uint32) []byte { b = append(b, byte(v), byte(v>>8), byte(v>>16), byte(v>>24)) return b } // appendFixed64 appends an encoded fixed64 to b. func appendFixed64(b []byte, v uint64) []byte { b = append(b, byte(v), byte(v>>8), byte(v>>16), byte(v>>24), byte(v>>32), byte(v>>40), byte(v>>48), byte(v>>56)) return b } // appendVarint appends an encoded varint to b. func appendVarint(b []byte, v uint64) []byte { // TODO: make 1-byte (maybe 2-byte) case inline-able, once we // have non-leaf inliner. switch { case v < 1<<7: b = append(b, byte(v)) case v < 1<<14: b = append(b, byte(v&0x7f|0x80), byte(v>>7)) case v < 1<<21: b = append(b, byte(v&0x7f|0x80), byte((v>>7)&0x7f|0x80), byte(v>>14)) case v < 1<<28: b = append(b, byte(v&0x7f|0x80), byte((v>>7)&0x7f|0x80), byte((v>>14)&0x7f|0x80), byte(v>>21)) case v < 1<<35: b = append(b, byte(v&0x7f|0x80), byte((v>>7)&0x7f|0x80), byte((v>>14)&0x7f|0x80), byte((v>>21)&0x7f|0x80), byte(v>>28)) case v < 1<<42: b = append(b, byte(v&0x7f|0x80), byte((v>>7)&0x7f|0x80), byte((v>>14)&0x7f|0x80), byte((v>>21)&0x7f|0x80), byte((v>>28)&0x7f|0x80), byte(v>>35)) case v < 1<<49: b = append(b, byte(v&0x7f|0x80), byte((v>>7)&0x7f|0x80), byte((v>>14)&0x7f|0x80), byte((v>>21)&0x7f|0x80), byte((v>>28)&0x7f|0x80), byte((v>>35)&0x7f|0x80), byte(v>>42)) case v < 1<<56: b = append(b, byte(v&0x7f|0x80), byte((v>>7)&0x7f|0x80), byte((v>>14)&0x7f|0x80), byte((v>>21)&0x7f|0x80), byte((v>>28)&0x7f|0x80), byte((v>>35)&0x7f|0x80), byte((v>>42)&0x7f|0x80), byte(v>>49)) case v < 1<<63: b = append(b, byte(v&0x7f|0x80), byte((v>>7)&0x7f|0x80), byte((v>>14)&0x7f|0x80), byte((v>>21)&0x7f|0x80), byte((v>>28)&0x7f|0x80), byte((v>>35)&0x7f|0x80), byte((v>>42)&0x7f|0x80), byte((v>>49)&0x7f|0x80), byte(v>>56)) default: b = append(b, byte(v&0x7f|0x80), byte((v>>7)&0x7f|0x80), byte((v>>14)&0x7f|0x80), byte((v>>21)&0x7f|0x80), byte((v>>28)&0x7f|0x80), byte((v>>35)&0x7f|0x80), byte((v>>42)&0x7f|0x80), byte((v>>49)&0x7f|0x80), byte((v>>56)&0x7f|0x80), 1) } return b } func appendFixed32Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toUint32() b = appendVarint(b, wiretag) b = appendFixed32(b, v) return b, nil } func appendFixed32ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toUint32() if v == 0 { return b, nil } b = appendVarint(b, wiretag) b = appendFixed32(b, v) return b, nil } func appendFixed32Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { p := *ptr.toUint32Ptr() if p == nil { return b, nil } b = appendVarint(b, wiretag) b = appendFixed32(b, *p) return b, nil } func appendFixed32Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := *ptr.toUint32Slice() for _, v := range s { b = appendVarint(b, wiretag) b = appendFixed32(b, v) } return b, nil } func appendFixed32PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := *ptr.toUint32Slice() if len(s) == 0 { return b, nil } b = appendVarint(b, wiretag&^7|WireBytes) b = appendVarint(b, uint64(4*len(s))) for _, v := range s { b = appendFixed32(b, v) } return b, nil } func appendFixedS32Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toInt32() b = appendVarint(b, wiretag) b = appendFixed32(b, uint32(v)) return b, nil } func appendFixedS32ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toInt32() if v == 0 { return b, nil } b = appendVarint(b, wiretag) b = appendFixed32(b, uint32(v)) return b, nil } func appendFixedS32Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { p := ptr.getInt32Ptr() if p == nil { return b, nil } b = appendVarint(b, wiretag) b = appendFixed32(b, uint32(*p)) return b, nil } func appendFixedS32Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := ptr.getInt32Slice() for _, v := range s { b = appendVarint(b, wiretag) b = appendFixed32(b, uint32(v)) } return b, nil } func appendFixedS32PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := ptr.getInt32Slice() if len(s) == 0 { return b, nil } b = appendVarint(b, wiretag&^7|WireBytes) b = appendVarint(b, uint64(4*len(s))) for _, v := range s { b = appendFixed32(b, uint32(v)) } return b, nil } func appendFloat32Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := math.Float32bits(*ptr.toFloat32()) b = appendVarint(b, wiretag) b = appendFixed32(b, v) return b, nil } func appendFloat32ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := math.Float32bits(*ptr.toFloat32()) if v == 0 { return b, nil } b = appendVarint(b, wiretag) b = appendFixed32(b, v) return b, nil } func appendFloat32Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { p := *ptr.toFloat32Ptr() if p == nil { return b, nil } b = appendVarint(b, wiretag) b = appendFixed32(b, math.Float32bits(*p)) return b, nil } func appendFloat32Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := *ptr.toFloat32Slice() for _, v := range s { b = appendVarint(b, wiretag) b = appendFixed32(b, math.Float32bits(v)) } return b, nil } func appendFloat32PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := *ptr.toFloat32Slice() if len(s) == 0 { return b, nil } b = appendVarint(b, wiretag&^7|WireBytes) b = appendVarint(b, uint64(4*len(s))) for _, v := range s { b = appendFixed32(b, math.Float32bits(v)) } return b, nil } func appendFixed64Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toUint64() b = appendVarint(b, wiretag) b = appendFixed64(b, v) return b, nil } func appendFixed64ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toUint64() if v == 0 { return b, nil } b = appendVarint(b, wiretag) b = appendFixed64(b, v) return b, nil } func appendFixed64Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { p := *ptr.toUint64Ptr() if p == nil { return b, nil } b = appendVarint(b, wiretag) b = appendFixed64(b, *p) return b, nil } func appendFixed64Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := *ptr.toUint64Slice() for _, v := range s { b = appendVarint(b, wiretag) b = appendFixed64(b, v) } return b, nil } func appendFixed64PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := *ptr.toUint64Slice() if len(s) == 0 { return b, nil } b = appendVarint(b, wiretag&^7|WireBytes) b = appendVarint(b, uint64(8*len(s))) for _, v := range s { b = appendFixed64(b, v) } return b, nil } func appendFixedS64Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toInt64() b = appendVarint(b, wiretag) b = appendFixed64(b, uint64(v)) return b, nil } func appendFixedS64ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toInt64() if v == 0 { return b, nil } b = appendVarint(b, wiretag) b = appendFixed64(b, uint64(v)) return b, nil } func appendFixedS64Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { p := *ptr.toInt64Ptr() if p == nil { return b, nil } b = appendVarint(b, wiretag) b = appendFixed64(b, uint64(*p)) return b, nil } func appendFixedS64Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := *ptr.toInt64Slice() for _, v := range s { b = appendVarint(b, wiretag) b = appendFixed64(b, uint64(v)) } return b, nil } func appendFixedS64PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := *ptr.toInt64Slice() if len(s) == 0 { return b, nil } b = appendVarint(b, wiretag&^7|WireBytes) b = appendVarint(b, uint64(8*len(s))) for _, v := range s { b = appendFixed64(b, uint64(v)) } return b, nil } func appendFloat64Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := math.Float64bits(*ptr.toFloat64()) b = appendVarint(b, wiretag) b = appendFixed64(b, v) return b, nil } func appendFloat64ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := math.Float64bits(*ptr.toFloat64()) if v == 0 { return b, nil } b = appendVarint(b, wiretag) b = appendFixed64(b, v) return b, nil } func appendFloat64Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { p := *ptr.toFloat64Ptr() if p == nil { return b, nil } b = appendVarint(b, wiretag) b = appendFixed64(b, math.Float64bits(*p)) return b, nil } func appendFloat64Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := *ptr.toFloat64Slice() for _, v := range s { b = appendVarint(b, wiretag) b = appendFixed64(b, math.Float64bits(v)) } return b, nil } func appendFloat64PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := *ptr.toFloat64Slice() if len(s) == 0 { return b, nil } b = appendVarint(b, wiretag&^7|WireBytes) b = appendVarint(b, uint64(8*len(s))) for _, v := range s { b = appendFixed64(b, math.Float64bits(v)) } return b, nil } func appendVarint32Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toUint32() b = appendVarint(b, wiretag) b = appendVarint(b, uint64(v)) return b, nil } func appendVarint32ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toUint32() if v == 0 { return b, nil } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(v)) return b, nil } func appendVarint32Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { p := *ptr.toUint32Ptr() if p == nil { return b, nil } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(*p)) return b, nil } func appendVarint32Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := *ptr.toUint32Slice() for _, v := range s { b = appendVarint(b, wiretag) b = appendVarint(b, uint64(v)) } return b, nil } func appendVarint32PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := *ptr.toUint32Slice() if len(s) == 0 { return b, nil } b = appendVarint(b, wiretag&^7|WireBytes) // compute size n := 0 for _, v := range s { n += SizeVarint(uint64(v)) } b = appendVarint(b, uint64(n)) for _, v := range s { b = appendVarint(b, uint64(v)) } return b, nil } func appendVarintS32Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toInt32() b = appendVarint(b, wiretag) b = appendVarint(b, uint64(v)) return b, nil } func appendVarintS32ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toInt32() if v == 0 { return b, nil } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(v)) return b, nil } func appendVarintS32Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { p := ptr.getInt32Ptr() if p == nil { return b, nil } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(*p)) return b, nil } func appendVarintS32Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := ptr.getInt32Slice() for _, v := range s { b = appendVarint(b, wiretag) b = appendVarint(b, uint64(v)) } return b, nil } func appendVarintS32PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := ptr.getInt32Slice() if len(s) == 0 { return b, nil } b = appendVarint(b, wiretag&^7|WireBytes) // compute size n := 0 for _, v := range s { n += SizeVarint(uint64(v)) } b = appendVarint(b, uint64(n)) for _, v := range s { b = appendVarint(b, uint64(v)) } return b, nil } func appendVarint64Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toUint64() b = appendVarint(b, wiretag) b = appendVarint(b, v) return b, nil } func appendVarint64ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toUint64() if v == 0 { return b, nil } b = appendVarint(b, wiretag) b = appendVarint(b, v) return b, nil } func appendVarint64Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { p := *ptr.toUint64Ptr() if p == nil { return b, nil } b = appendVarint(b, wiretag) b = appendVarint(b, *p) return b, nil } func appendVarint64Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := *ptr.toUint64Slice() for _, v := range s { b = appendVarint(b, wiretag) b = appendVarint(b, v) } return b, nil } func appendVarint64PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := *ptr.toUint64Slice() if len(s) == 0 { return b, nil } b = appendVarint(b, wiretag&^7|WireBytes) // compute size n := 0 for _, v := range s { n += SizeVarint(v) } b = appendVarint(b, uint64(n)) for _, v := range s { b = appendVarint(b, v) } return b, nil } func appendVarintS64Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toInt64() b = appendVarint(b, wiretag) b = appendVarint(b, uint64(v)) return b, nil } func appendVarintS64ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toInt64() if v == 0 { return b, nil } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(v)) return b, nil } func appendVarintS64Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { p := *ptr.toInt64Ptr() if p == nil { return b, nil } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(*p)) return b, nil } func appendVarintS64Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := *ptr.toInt64Slice() for _, v := range s { b = appendVarint(b, wiretag) b = appendVarint(b, uint64(v)) } return b, nil } func appendVarintS64PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := *ptr.toInt64Slice() if len(s) == 0 { return b, nil } b = appendVarint(b, wiretag&^7|WireBytes) // compute size n := 0 for _, v := range s { n += SizeVarint(uint64(v)) } b = appendVarint(b, uint64(n)) for _, v := range s { b = appendVarint(b, uint64(v)) } return b, nil } func appendZigzag32Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toInt32() b = appendVarint(b, wiretag) b = appendVarint(b, uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) return b, nil } func appendZigzag32ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toInt32() if v == 0 { return b, nil } b = appendVarint(b, wiretag) b = appendVarint(b, uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) return b, nil } func appendZigzag32Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { p := ptr.getInt32Ptr() if p == nil { return b, nil } b = appendVarint(b, wiretag) v := *p b = appendVarint(b, uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) return b, nil } func appendZigzag32Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := ptr.getInt32Slice() for _, v := range s { b = appendVarint(b, wiretag) b = appendVarint(b, uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) } return b, nil } func appendZigzag32PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := ptr.getInt32Slice() if len(s) == 0 { return b, nil } b = appendVarint(b, wiretag&^7|WireBytes) // compute size n := 0 for _, v := range s { n += SizeVarint(uint64((uint32(v) << 1) ^ uint32((int32(v) >> 31)))) } b = appendVarint(b, uint64(n)) for _, v := range s { b = appendVarint(b, uint64((uint32(v)<<1)^uint32((int32(v)>>31)))) } return b, nil } func appendZigzag64Value(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toInt64() b = appendVarint(b, wiretag) b = appendVarint(b, uint64(v<<1)^uint64((int64(v)>>63))) return b, nil } func appendZigzag64ValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toInt64() if v == 0 { return b, nil } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(v<<1)^uint64((int64(v)>>63))) return b, nil } func appendZigzag64Ptr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { p := *ptr.toInt64Ptr() if p == nil { return b, nil } b = appendVarint(b, wiretag) v := *p b = appendVarint(b, uint64(v<<1)^uint64((int64(v)>>63))) return b, nil } func appendZigzag64Slice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := *ptr.toInt64Slice() for _, v := range s { b = appendVarint(b, wiretag) b = appendVarint(b, uint64(v<<1)^uint64((int64(v)>>63))) } return b, nil } func appendZigzag64PackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := *ptr.toInt64Slice() if len(s) == 0 { return b, nil } b = appendVarint(b, wiretag&^7|WireBytes) // compute size n := 0 for _, v := range s { n += SizeVarint(uint64(v<<1) ^ uint64((int64(v) >> 63))) } b = appendVarint(b, uint64(n)) for _, v := range s { b = appendVarint(b, uint64(v<<1)^uint64((int64(v)>>63))) } return b, nil } func appendBoolValue(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toBool() b = appendVarint(b, wiretag) if v { b = append(b, 1) } else { b = append(b, 0) } return b, nil } func appendBoolValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toBool() if !v { return b, nil } b = appendVarint(b, wiretag) b = append(b, 1) return b, nil } func appendBoolPtr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { p := *ptr.toBoolPtr() if p == nil { return b, nil } b = appendVarint(b, wiretag) if *p { b = append(b, 1) } else { b = append(b, 0) } return b, nil } func appendBoolSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := *ptr.toBoolSlice() for _, v := range s { b = appendVarint(b, wiretag) if v { b = append(b, 1) } else { b = append(b, 0) } } return b, nil } func appendBoolPackedSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := *ptr.toBoolSlice() if len(s) == 0 { return b, nil } b = appendVarint(b, wiretag&^7|WireBytes) b = appendVarint(b, uint64(len(s))) for _, v := range s { if v { b = append(b, 1) } else { b = append(b, 0) } } return b, nil } func appendStringValue(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toString() b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(v))) b = append(b, v...) return b, nil } func appendStringValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toString() if v == "" { return b, nil } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(v))) b = append(b, v...) return b, nil } func appendStringPtr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { p := *ptr.toStringPtr() if p == nil { return b, nil } v := *p b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(v))) b = append(b, v...) return b, nil } func appendStringSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := *ptr.toStringSlice() for _, v := range s { b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(v))) b = append(b, v...) } return b, nil } func appendUTF8StringValue(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { var invalidUTF8 bool v := *ptr.toString() if !utf8.ValidString(v) { invalidUTF8 = true } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(v))) b = append(b, v...) if invalidUTF8 { return b, errInvalidUTF8 } return b, nil } func appendUTF8StringValueNoZero(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { var invalidUTF8 bool v := *ptr.toString() if v == "" { return b, nil } if !utf8.ValidString(v) { invalidUTF8 = true } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(v))) b = append(b, v...) if invalidUTF8 { return b, errInvalidUTF8 } return b, nil } func appendUTF8StringPtr(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { var invalidUTF8 bool p := *ptr.toStringPtr() if p == nil { return b, nil } v := *p if !utf8.ValidString(v) { invalidUTF8 = true } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(v))) b = append(b, v...) if invalidUTF8 { return b, errInvalidUTF8 } return b, nil } func appendUTF8StringSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { var invalidUTF8 bool s := *ptr.toStringSlice() for _, v := range s { if !utf8.ValidString(v) { invalidUTF8 = true } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(v))) b = append(b, v...) } if invalidUTF8 { return b, errInvalidUTF8 } return b, nil } func appendBytes(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toBytes() if v == nil { return b, nil } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(v))) b = append(b, v...) return b, nil } func appendBytes3(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toBytes() if len(v) == 0 { return b, nil } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(v))) b = append(b, v...) return b, nil } func appendBytesOneof(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { v := *ptr.toBytes() b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(v))) b = append(b, v...) return b, nil } func appendBytesSlice(b []byte, ptr pointer, wiretag uint64, _ bool) ([]byte, error) { s := *ptr.toBytesSlice() for _, v := range s { b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(v))) b = append(b, v...) } return b, nil } // makeGroupMarshaler returns the sizer and marshaler for a group. // u is the marshal info of the underlying message. func makeGroupMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { p := ptr.getPointer() if p.isNil() { return 0 } return u.size(p) + 2*tagsize }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { p := ptr.getPointer() if p.isNil() { return b, nil } var err error b = appendVarint(b, wiretag) // start group b, err = u.marshal(b, p, deterministic) b = appendVarint(b, wiretag+(WireEndGroup-WireStartGroup)) // end group return b, err } } // makeGroupSliceMarshaler returns the sizer and marshaler for a group slice. // u is the marshal info of the underlying message. func makeGroupSliceMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { s := ptr.getPointerSlice() n := 0 for _, v := range s { if v.isNil() { continue } n += u.size(v) + 2*tagsize } return n }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getPointerSlice() var err error var nerr nonFatal for _, v := range s { if v.isNil() { return b, errRepeatedHasNil } b = appendVarint(b, wiretag) // start group b, err = u.marshal(b, v, deterministic) b = appendVarint(b, wiretag+(WireEndGroup-WireStartGroup)) // end group if !nerr.Merge(err) { if err == ErrNil { err = errRepeatedHasNil } return b, err } } return b, nerr.E } } // makeMessageMarshaler returns the sizer and marshaler for a message field. // u is the marshal info of the message. func makeMessageMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { p := ptr.getPointer() if p.isNil() { return 0 } siz := u.size(p) return siz + SizeVarint(uint64(siz)) + tagsize }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { p := ptr.getPointer() if p.isNil() { return b, nil } b = appendVarint(b, wiretag) siz := u.cachedsize(p) b = appendVarint(b, uint64(siz)) return u.marshal(b, p, deterministic) } } // makeMessageSliceMarshaler returns the sizer and marshaler for a message slice. // u is the marshal info of the message. func makeMessageSliceMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { s := ptr.getPointerSlice() n := 0 for _, v := range s { if v.isNil() { continue } siz := u.size(v) n += siz + SizeVarint(uint64(siz)) + tagsize } return n }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getPointerSlice() var err error var nerr nonFatal for _, v := range s { if v.isNil() { return b, errRepeatedHasNil } b = appendVarint(b, wiretag) siz := u.cachedsize(v) b = appendVarint(b, uint64(siz)) b, err = u.marshal(b, v, deterministic) if !nerr.Merge(err) { if err == ErrNil { err = errRepeatedHasNil } return b, err } } return b, nerr.E } } // makeMapMarshaler returns the sizer and marshaler for a map field. // f is the pointer to the reflect data structure of the field. func makeMapMarshaler(f *reflect.StructField) (sizer, marshaler) { // figure out key and value type t := f.Type keyType := t.Key() valType := t.Elem() tags := strings.Split(f.Tag.Get("protobuf"), ",") keyTags := strings.Split(f.Tag.Get("protobuf_key"), ",") valTags := strings.Split(f.Tag.Get("protobuf_val"), ",") stdOptions := false for _, t := range tags { if strings.HasPrefix(t, "customtype=") { valTags = append(valTags, t) } if t == "stdtime" { valTags = append(valTags, t) stdOptions = true } if t == "stdduration" { valTags = append(valTags, t) stdOptions = true } if t == "wktptr" { valTags = append(valTags, t) } } keySizer, keyMarshaler := typeMarshaler(keyType, keyTags, false, false) // don't omit zero value in map valSizer, valMarshaler := typeMarshaler(valType, valTags, false, false) // don't omit zero value in map keyWireTag := 1<<3 | wiretype(keyTags[0]) valWireTag := 2<<3 | wiretype(valTags[0]) // We create an interface to get the addresses of the map key and value. // If value is pointer-typed, the interface is a direct interface, the // idata itself is the value. Otherwise, the idata is the pointer to the // value. // Key cannot be pointer-typed. valIsPtr := valType.Kind() == reflect.Ptr // If value is a message with nested maps, calling // valSizer in marshal may be quadratic. We should use // cached version in marshal (but not in size). // If value is not message type, we don't have size cache, // but it cannot be nested either. Just use valSizer. valCachedSizer := valSizer if valIsPtr && !stdOptions && valType.Elem().Kind() == reflect.Struct { u := getMarshalInfo(valType.Elem()) valCachedSizer = func(ptr pointer, tagsize int) int { // Same as message sizer, but use cache. p := ptr.getPointer() if p.isNil() { return 0 } siz := u.cachedsize(p) return siz + SizeVarint(uint64(siz)) + tagsize } } return func(ptr pointer, tagsize int) int { m := ptr.asPointerTo(t).Elem() // the map n := 0 for _, k := range m.MapKeys() { ki := k.Interface() vi := m.MapIndex(k).Interface() kaddr := toAddrPointer(&ki, false) // pointer to key vaddr := toAddrPointer(&vi, valIsPtr) // pointer to value siz := keySizer(kaddr, 1) + valSizer(vaddr, 1) // tag of key = 1 (size=1), tag of val = 2 (size=1) n += siz + SizeVarint(uint64(siz)) + tagsize } return n }, func(b []byte, ptr pointer, tag uint64, deterministic bool) ([]byte, error) { m := ptr.asPointerTo(t).Elem() // the map var err error keys := m.MapKeys() if len(keys) > 1 && deterministic { sort.Sort(mapKeys(keys)) } var nerr nonFatal for _, k := range keys { ki := k.Interface() vi := m.MapIndex(k).Interface() kaddr := toAddrPointer(&ki, false) // pointer to key vaddr := toAddrPointer(&vi, valIsPtr) // pointer to value b = appendVarint(b, tag) siz := keySizer(kaddr, 1) + valCachedSizer(vaddr, 1) // tag of key = 1 (size=1), tag of val = 2 (size=1) b = appendVarint(b, uint64(siz)) b, err = keyMarshaler(b, kaddr, keyWireTag, deterministic) if !nerr.Merge(err) { return b, err } b, err = valMarshaler(b, vaddr, valWireTag, deterministic) if err != ErrNil && !nerr.Merge(err) { // allow nil value in map return b, err } } return b, nerr.E } } // makeOneOfMarshaler returns the sizer and marshaler for a oneof field. // fi is the marshal info of the field. // f is the pointer to the reflect data structure of the field. func makeOneOfMarshaler(fi *marshalFieldInfo, f *reflect.StructField) (sizer, marshaler) { // Oneof field is an interface. We need to get the actual data type on the fly. t := f.Type return func(ptr pointer, _ int) int { p := ptr.getInterfacePointer() if p.isNil() { return 0 } v := ptr.asPointerTo(t).Elem().Elem().Elem() // *interface -> interface -> *struct -> struct telem := v.Type() e := fi.oneofElems[telem] return e.sizer(p, e.tagsize) }, func(b []byte, ptr pointer, _ uint64, deterministic bool) ([]byte, error) { p := ptr.getInterfacePointer() if p.isNil() { return b, nil } v := ptr.asPointerTo(t).Elem().Elem().Elem() // *interface -> interface -> *struct -> struct telem := v.Type() if telem.Field(0).Type.Kind() == reflect.Ptr && p.getPointer().isNil() { return b, errOneofHasNil } e := fi.oneofElems[telem] return e.marshaler(b, p, e.wiretag, deterministic) } } // sizeExtensions computes the size of encoded data for a XXX_InternalExtensions field. func (u *marshalInfo) sizeExtensions(ext *XXX_InternalExtensions) int { m, mu := ext.extensionsRead() if m == nil { return 0 } mu.Lock() n := 0 for _, e := range m { if e.value == nil || e.desc == nil { // Extension is only in its encoded form. n += len(e.enc) continue } // We don't skip extensions that have an encoded form set, // because the extension value may have been mutated after // the last time this function was called. ei := u.getExtElemInfo(e.desc) v := e.value p := toAddrPointer(&v, ei.isptr) n += ei.sizer(p, ei.tagsize) } mu.Unlock() return n } // appendExtensions marshals a XXX_InternalExtensions field to the end of byte slice b. func (u *marshalInfo) appendExtensions(b []byte, ext *XXX_InternalExtensions, deterministic bool) ([]byte, error) { m, mu := ext.extensionsRead() if m == nil { return b, nil } mu.Lock() defer mu.Unlock() var err error var nerr nonFatal // Fast-path for common cases: zero or one extensions. // Don't bother sorting the keys. if len(m) <= 1 { for _, e := range m { if e.value == nil || e.desc == nil { // Extension is only in its encoded form. b = append(b, e.enc...) continue } // We don't skip extensions that have an encoded form set, // because the extension value may have been mutated after // the last time this function was called. ei := u.getExtElemInfo(e.desc) v := e.value p := toAddrPointer(&v, ei.isptr) b, err = ei.marshaler(b, p, ei.wiretag, deterministic) if !nerr.Merge(err) { return b, err } } return b, nerr.E } // Sort the keys to provide a deterministic encoding. // Not sure this is required, but the old code does it. keys := make([]int, 0, len(m)) for k := range m { keys = append(keys, int(k)) } sort.Ints(keys) for _, k := range keys { e := m[int32(k)] if e.value == nil || e.desc == nil { // Extension is only in its encoded form. b = append(b, e.enc...) continue } // We don't skip extensions that have an encoded form set, // because the extension value may have been mutated after // the last time this function was called. ei := u.getExtElemInfo(e.desc) v := e.value p := toAddrPointer(&v, ei.isptr) b, err = ei.marshaler(b, p, ei.wiretag, deterministic) if !nerr.Merge(err) { return b, err } } return b, nerr.E } // message set format is: // message MessageSet { // repeated group Item = 1 { // required int32 type_id = 2; // required string message = 3; // }; // } // sizeMessageSet computes the size of encoded data for a XXX_InternalExtensions field // in message set format (above). func (u *marshalInfo) sizeMessageSet(ext *XXX_InternalExtensions) int { m, mu := ext.extensionsRead() if m == nil { return 0 } mu.Lock() n := 0 for id, e := range m { n += 2 // start group, end group. tag = 1 (size=1) n += SizeVarint(uint64(id)) + 1 // type_id, tag = 2 (size=1) if e.value == nil || e.desc == nil { // Extension is only in its encoded form. msgWithLen := skipVarint(e.enc) // skip old tag, but leave the length varint siz := len(msgWithLen) n += siz + 1 // message, tag = 3 (size=1) continue } // We don't skip extensions that have an encoded form set, // because the extension value may have been mutated after // the last time this function was called. ei := u.getExtElemInfo(e.desc) v := e.value p := toAddrPointer(&v, ei.isptr) n += ei.sizer(p, 1) // message, tag = 3 (size=1) } mu.Unlock() return n } // appendMessageSet marshals a XXX_InternalExtensions field in message set format (above) // to the end of byte slice b. func (u *marshalInfo) appendMessageSet(b []byte, ext *XXX_InternalExtensions, deterministic bool) ([]byte, error) { m, mu := ext.extensionsRead() if m == nil { return b, nil } mu.Lock() defer mu.Unlock() var err error var nerr nonFatal // Fast-path for common cases: zero or one extensions. // Don't bother sorting the keys. if len(m) <= 1 { for id, e := range m { b = append(b, 1<<3|WireStartGroup) b = append(b, 2<<3|WireVarint) b = appendVarint(b, uint64(id)) if e.value == nil || e.desc == nil { // Extension is only in its encoded form. msgWithLen := skipVarint(e.enc) // skip old tag, but leave the length varint b = append(b, 3<<3|WireBytes) b = append(b, msgWithLen...) b = append(b, 1<<3|WireEndGroup) continue } // We don't skip extensions that have an encoded form set, // because the extension value may have been mutated after // the last time this function was called. ei := u.getExtElemInfo(e.desc) v := e.value p := toAddrPointer(&v, ei.isptr) b, err = ei.marshaler(b, p, 3<<3|WireBytes, deterministic) if !nerr.Merge(err) { return b, err } b = append(b, 1<<3|WireEndGroup) } return b, nerr.E } // Sort the keys to provide a deterministic encoding. keys := make([]int, 0, len(m)) for k := range m { keys = append(keys, int(k)) } sort.Ints(keys) for _, id := range keys { e := m[int32(id)] b = append(b, 1<<3|WireStartGroup) b = append(b, 2<<3|WireVarint) b = appendVarint(b, uint64(id)) if e.value == nil || e.desc == nil { // Extension is only in its encoded form. msgWithLen := skipVarint(e.enc) // skip old tag, but leave the length varint b = append(b, 3<<3|WireBytes) b = append(b, msgWithLen...) b = append(b, 1<<3|WireEndGroup) continue } // We don't skip extensions that have an encoded form set, // because the extension value may have been mutated after // the last time this function was called. ei := u.getExtElemInfo(e.desc) v := e.value p := toAddrPointer(&v, ei.isptr) b, err = ei.marshaler(b, p, 3<<3|WireBytes, deterministic) b = append(b, 1<<3|WireEndGroup) if !nerr.Merge(err) { return b, err } } return b, nerr.E } // sizeV1Extensions computes the size of encoded data for a V1-API extension field. func (u *marshalInfo) sizeV1Extensions(m map[int32]Extension) int { if m == nil { return 0 } n := 0 for _, e := range m { if e.value == nil || e.desc == nil { // Extension is only in its encoded form. n += len(e.enc) continue } // We don't skip extensions that have an encoded form set, // because the extension value may have been mutated after // the last time this function was called. ei := u.getExtElemInfo(e.desc) v := e.value p := toAddrPointer(&v, ei.isptr) n += ei.sizer(p, ei.tagsize) } return n } // appendV1Extensions marshals a V1-API extension field to the end of byte slice b. func (u *marshalInfo) appendV1Extensions(b []byte, m map[int32]Extension, deterministic bool) ([]byte, error) { if m == nil { return b, nil } // Sort the keys to provide a deterministic encoding. keys := make([]int, 0, len(m)) for k := range m { keys = append(keys, int(k)) } sort.Ints(keys) var err error var nerr nonFatal for _, k := range keys { e := m[int32(k)] if e.value == nil || e.desc == nil { // Extension is only in its encoded form. b = append(b, e.enc...) continue } // We don't skip extensions that have an encoded form set, // because the extension value may have been mutated after // the last time this function was called. ei := u.getExtElemInfo(e.desc) v := e.value p := toAddrPointer(&v, ei.isptr) b, err = ei.marshaler(b, p, ei.wiretag, deterministic) if !nerr.Merge(err) { return b, err } } return b, nerr.E } // newMarshaler is the interface representing objects that can marshal themselves. // // This exists to support protoc-gen-go generated messages. // The proto package will stop type-asserting to this interface in the future. // // DO NOT DEPEND ON THIS. type newMarshaler interface { XXX_Size() int XXX_Marshal(b []byte, deterministic bool) ([]byte, error) } // Size returns the encoded size of a protocol buffer message. // This is the main entry point. func Size(pb Message) int { if m, ok := pb.(newMarshaler); ok { return m.XXX_Size() } if m, ok := pb.(Marshaler); ok { // If the message can marshal itself, let it do it, for compatibility. // NOTE: This is not efficient. b, _ := m.Marshal() return len(b) } // in case somehow we didn't generate the wrapper if pb == nil { return 0 } var info InternalMessageInfo return info.Size(pb) } // Marshal takes a protocol buffer message // and encodes it into the wire format, returning the data. // This is the main entry point. func Marshal(pb Message) ([]byte, error) { if m, ok := pb.(newMarshaler); ok { siz := m.XXX_Size() b := make([]byte, 0, siz) return m.XXX_Marshal(b, false) } if m, ok := pb.(Marshaler); ok { // If the message can marshal itself, let it do it, for compatibility. // NOTE: This is not efficient. return m.Marshal() } // in case somehow we didn't generate the wrapper if pb == nil { return nil, ErrNil } var info InternalMessageInfo siz := info.Size(pb) b := make([]byte, 0, siz) return info.Marshal(b, pb, false) } // Marshal takes a protocol buffer message // and encodes it into the wire format, writing the result to the // Buffer. // This is an alternative entry point. It is not necessary to use // a Buffer for most applications. func (p *Buffer) Marshal(pb Message) error { var err error if p.deterministic { if _, ok := pb.(Marshaler); ok { return fmt.Errorf("proto: deterministic not supported by the Marshal method of %T", pb) } } if m, ok := pb.(newMarshaler); ok { siz := m.XXX_Size() p.grow(siz) // make sure buf has enough capacity pp := p.buf[len(p.buf) : len(p.buf) : len(p.buf)+siz] pp, err = m.XXX_Marshal(pp, p.deterministic) p.buf = append(p.buf, pp...) return err } if m, ok := pb.(Marshaler); ok { // If the message can marshal itself, let it do it, for compatibility. // NOTE: This is not efficient. var b []byte b, err = m.Marshal() p.buf = append(p.buf, b...) return err } // in case somehow we didn't generate the wrapper if pb == nil { return ErrNil } var info InternalMessageInfo siz := info.Size(pb) p.grow(siz) // make sure buf has enough capacity p.buf, err = info.Marshal(p.buf, pb, p.deterministic) return err } // grow grows the buffer's capacity, if necessary, to guarantee space for // another n bytes. After grow(n), at least n bytes can be written to the // buffer without another allocation. func (p *Buffer) grow(n int) { need := len(p.buf) + n if need <= cap(p.buf) { return } newCap := len(p.buf) * 2 if newCap < need { newCap = need } p.buf = append(make([]byte, 0, newCap), p.buf...) } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/table_marshal_gogo.go ================================================ // Protocol Buffers for Go with Gadgets // // Copyright (c) 2018, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto import ( "reflect" "time" ) // makeMessageRefMarshaler differs a bit from makeMessageMarshaler // It marshal a message T instead of a *T func makeMessageRefMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { siz := u.size(ptr) return siz + SizeVarint(uint64(siz)) + tagsize }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { b = appendVarint(b, wiretag) siz := u.cachedsize(ptr) b = appendVarint(b, uint64(siz)) return u.marshal(b, ptr, deterministic) } } // makeMessageRefSliceMarshaler differs quite a lot from makeMessageSliceMarshaler // It marshals a slice of messages []T instead of []*T func makeMessageRefSliceMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { s := ptr.getSlice(u.typ) n := 0 for i := 0; i < s.Len(); i++ { elem := s.Index(i) e := elem.Interface() v := toAddrPointer(&e, false) siz := u.size(v) n += siz + SizeVarint(uint64(siz)) + tagsize } return n }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getSlice(u.typ) var err, errreq error for i := 0; i < s.Len(); i++ { elem := s.Index(i) e := elem.Interface() v := toAddrPointer(&e, false) b = appendVarint(b, wiretag) siz := u.size(v) b = appendVarint(b, uint64(siz)) b, err = u.marshal(b, v, deterministic) if err != nil { if _, ok := err.(*RequiredNotSetError); ok { // Required field in submessage is not set. // We record the error but keep going, to give a complete marshaling. if errreq == nil { errreq = err } continue } if err == ErrNil { err = errRepeatedHasNil } return b, err } } return b, errreq } } func makeCustomPtrMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { if ptr.isNil() { return 0 } m := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(custom) siz := m.Size() return tagsize + SizeVarint(uint64(siz)) + siz }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { if ptr.isNil() { return b, nil } m := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(custom) siz := m.Size() buf, err := m.Marshal() if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(siz)) b = append(b, buf...) return b, nil } } func makeCustomMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { m := ptr.asPointerTo(u.typ).Interface().(custom) siz := m.Size() return tagsize + SizeVarint(uint64(siz)) + siz }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { m := ptr.asPointerTo(u.typ).Interface().(custom) siz := m.Size() buf, err := m.Marshal() if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(siz)) b = append(b, buf...) return b, nil } } func makeTimeMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { t := ptr.asPointerTo(u.typ).Interface().(*time.Time) ts, err := timestampProto(*t) if err != nil { return 0 } siz := Size(ts) return tagsize + SizeVarint(uint64(siz)) + siz }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { t := ptr.asPointerTo(u.typ).Interface().(*time.Time) ts, err := timestampProto(*t) if err != nil { return nil, err } buf, err := Marshal(ts) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(buf))) b = append(b, buf...) return b, nil } } func makeTimePtrMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { if ptr.isNil() { return 0 } t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*time.Time) ts, err := timestampProto(*t) if err != nil { return 0 } siz := Size(ts) return tagsize + SizeVarint(uint64(siz)) + siz }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { if ptr.isNil() { return b, nil } t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*time.Time) ts, err := timestampProto(*t) if err != nil { return nil, err } buf, err := Marshal(ts) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(buf))) b = append(b, buf...) return b, nil } } func makeTimeSliceMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { s := ptr.getSlice(u.typ) n := 0 for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(time.Time) ts, err := timestampProto(t) if err != nil { return 0 } siz := Size(ts) n += siz + SizeVarint(uint64(siz)) + tagsize } return n }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getSlice(u.typ) for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(time.Time) ts, err := timestampProto(t) if err != nil { return nil, err } siz := Size(ts) buf, err := Marshal(ts) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(siz)) b = append(b, buf...) } return b, nil } } func makeTimePtrSliceMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { s := ptr.getSlice(reflect.PtrTo(u.typ)) n := 0 for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(*time.Time) ts, err := timestampProto(*t) if err != nil { return 0 } siz := Size(ts) n += siz + SizeVarint(uint64(siz)) + tagsize } return n }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getSlice(reflect.PtrTo(u.typ)) for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(*time.Time) ts, err := timestampProto(*t) if err != nil { return nil, err } siz := Size(ts) buf, err := Marshal(ts) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(siz)) b = append(b, buf...) } return b, nil } } func makeDurationMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { d := ptr.asPointerTo(u.typ).Interface().(*time.Duration) dur := durationProto(*d) siz := Size(dur) return tagsize + SizeVarint(uint64(siz)) + siz }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { d := ptr.asPointerTo(u.typ).Interface().(*time.Duration) dur := durationProto(*d) buf, err := Marshal(dur) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(buf))) b = append(b, buf...) return b, nil } } func makeDurationPtrMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { if ptr.isNil() { return 0 } d := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*time.Duration) dur := durationProto(*d) siz := Size(dur) return tagsize + SizeVarint(uint64(siz)) + siz }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { if ptr.isNil() { return b, nil } d := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*time.Duration) dur := durationProto(*d) buf, err := Marshal(dur) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(buf))) b = append(b, buf...) return b, nil } } func makeDurationSliceMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { s := ptr.getSlice(u.typ) n := 0 for i := 0; i < s.Len(); i++ { elem := s.Index(i) d := elem.Interface().(time.Duration) dur := durationProto(d) siz := Size(dur) n += siz + SizeVarint(uint64(siz)) + tagsize } return n }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getSlice(u.typ) for i := 0; i < s.Len(); i++ { elem := s.Index(i) d := elem.Interface().(time.Duration) dur := durationProto(d) siz := Size(dur) buf, err := Marshal(dur) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(siz)) b = append(b, buf...) } return b, nil } } func makeDurationPtrSliceMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { s := ptr.getSlice(reflect.PtrTo(u.typ)) n := 0 for i := 0; i < s.Len(); i++ { elem := s.Index(i) d := elem.Interface().(*time.Duration) dur := durationProto(*d) siz := Size(dur) n += siz + SizeVarint(uint64(siz)) + tagsize } return n }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getSlice(reflect.PtrTo(u.typ)) for i := 0; i < s.Len(); i++ { elem := s.Index(i) d := elem.Interface().(*time.Duration) dur := durationProto(*d) siz := Size(dur) buf, err := Marshal(dur) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(siz)) b = append(b, buf...) } return b, nil } } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/table_merge.go ================================================ // Go support for Protocol Buffers - Google's data interchange format // // Copyright 2016 The Go Authors. All rights reserved. // https://github.com/golang/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto import ( "fmt" "reflect" "strings" "sync" "sync/atomic" ) // Merge merges the src message into dst. // This assumes that dst and src of the same type and are non-nil. func (a *InternalMessageInfo) Merge(dst, src Message) { mi := atomicLoadMergeInfo(&a.merge) if mi == nil { mi = getMergeInfo(reflect.TypeOf(dst).Elem()) atomicStoreMergeInfo(&a.merge, mi) } mi.merge(toPointer(&dst), toPointer(&src)) } type mergeInfo struct { typ reflect.Type initialized int32 // 0: only typ is valid, 1: everything is valid lock sync.Mutex fields []mergeFieldInfo unrecognized field // Offset of XXX_unrecognized } type mergeFieldInfo struct { field field // Offset of field, guaranteed to be valid // isPointer reports whether the value in the field is a pointer. // This is true for the following situations: // * Pointer to struct // * Pointer to basic type (proto2 only) // * Slice (first value in slice header is a pointer) // * String (first value in string header is a pointer) isPointer bool // basicWidth reports the width of the field assuming that it is directly // embedded in the struct (as is the case for basic types in proto3). // The possible values are: // 0: invalid // 1: bool // 4: int32, uint32, float32 // 8: int64, uint64, float64 basicWidth int // Where dst and src are pointers to the types being merged. merge func(dst, src pointer) } var ( mergeInfoMap = map[reflect.Type]*mergeInfo{} mergeInfoLock sync.Mutex ) func getMergeInfo(t reflect.Type) *mergeInfo { mergeInfoLock.Lock() defer mergeInfoLock.Unlock() mi := mergeInfoMap[t] if mi == nil { mi = &mergeInfo{typ: t} mergeInfoMap[t] = mi } return mi } // merge merges src into dst assuming they are both of type *mi.typ. func (mi *mergeInfo) merge(dst, src pointer) { if dst.isNil() { panic("proto: nil destination") } if src.isNil() { return // Nothing to do. } if atomic.LoadInt32(&mi.initialized) == 0 { mi.computeMergeInfo() } for _, fi := range mi.fields { sfp := src.offset(fi.field) // As an optimization, we can avoid the merge function call cost // if we know for sure that the source will have no effect // by checking if it is the zero value. if unsafeAllowed { if fi.isPointer && sfp.getPointer().isNil() { // Could be slice or string continue } if fi.basicWidth > 0 { switch { case fi.basicWidth == 1 && !*sfp.toBool(): continue case fi.basicWidth == 4 && *sfp.toUint32() == 0: continue case fi.basicWidth == 8 && *sfp.toUint64() == 0: continue } } } dfp := dst.offset(fi.field) fi.merge(dfp, sfp) } // TODO: Make this faster? out := dst.asPointerTo(mi.typ).Elem() in := src.asPointerTo(mi.typ).Elem() if emIn, err := extendable(in.Addr().Interface()); err == nil { emOut, _ := extendable(out.Addr().Interface()) mIn, muIn := emIn.extensionsRead() if mIn != nil { mOut := emOut.extensionsWrite() muIn.Lock() mergeExtension(mOut, mIn) muIn.Unlock() } } if mi.unrecognized.IsValid() { if b := *src.offset(mi.unrecognized).toBytes(); len(b) > 0 { *dst.offset(mi.unrecognized).toBytes() = append([]byte(nil), b...) } } } func (mi *mergeInfo) computeMergeInfo() { mi.lock.Lock() defer mi.lock.Unlock() if mi.initialized != 0 { return } t := mi.typ n := t.NumField() props := GetProperties(t) for i := 0; i < n; i++ { f := t.Field(i) if strings.HasPrefix(f.Name, "XXX_") { continue } mfi := mergeFieldInfo{field: toField(&f)} tf := f.Type // As an optimization, we can avoid the merge function call cost // if we know for sure that the source will have no effect // by checking if it is the zero value. if unsafeAllowed { switch tf.Kind() { case reflect.Ptr, reflect.Slice, reflect.String: // As a special case, we assume slices and strings are pointers // since we know that the first field in the SliceSlice or // StringHeader is a data pointer. mfi.isPointer = true case reflect.Bool: mfi.basicWidth = 1 case reflect.Int32, reflect.Uint32, reflect.Float32: mfi.basicWidth = 4 case reflect.Int64, reflect.Uint64, reflect.Float64: mfi.basicWidth = 8 } } // Unwrap tf to get at its most basic type. var isPointer, isSlice bool if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 { isSlice = true tf = tf.Elem() } if tf.Kind() == reflect.Ptr { isPointer = true tf = tf.Elem() } if isPointer && isSlice && tf.Kind() != reflect.Struct { panic("both pointer and slice for basic type in " + tf.Name()) } switch tf.Kind() { case reflect.Int32: switch { case isSlice: // E.g., []int32 mfi.merge = func(dst, src pointer) { // NOTE: toInt32Slice is not defined (see pointer_reflect.go). /* sfsp := src.toInt32Slice() if *sfsp != nil { dfsp := dst.toInt32Slice() *dfsp = append(*dfsp, *sfsp...) if *dfsp == nil { *dfsp = []int64{} } } */ sfs := src.getInt32Slice() if sfs != nil { dfs := dst.getInt32Slice() dfs = append(dfs, sfs...) if dfs == nil { dfs = []int32{} } dst.setInt32Slice(dfs) } } case isPointer: // E.g., *int32 mfi.merge = func(dst, src pointer) { // NOTE: toInt32Ptr is not defined (see pointer_reflect.go). /* sfpp := src.toInt32Ptr() if *sfpp != nil { dfpp := dst.toInt32Ptr() if *dfpp == nil { *dfpp = Int32(**sfpp) } else { **dfpp = **sfpp } } */ sfp := src.getInt32Ptr() if sfp != nil { dfp := dst.getInt32Ptr() if dfp == nil { dst.setInt32Ptr(*sfp) } else { *dfp = *sfp } } } default: // E.g., int32 mfi.merge = func(dst, src pointer) { if v := *src.toInt32(); v != 0 { *dst.toInt32() = v } } } case reflect.Int64: switch { case isSlice: // E.g., []int64 mfi.merge = func(dst, src pointer) { sfsp := src.toInt64Slice() if *sfsp != nil { dfsp := dst.toInt64Slice() *dfsp = append(*dfsp, *sfsp...) if *dfsp == nil { *dfsp = []int64{} } } } case isPointer: // E.g., *int64 mfi.merge = func(dst, src pointer) { sfpp := src.toInt64Ptr() if *sfpp != nil { dfpp := dst.toInt64Ptr() if *dfpp == nil { *dfpp = Int64(**sfpp) } else { **dfpp = **sfpp } } } default: // E.g., int64 mfi.merge = func(dst, src pointer) { if v := *src.toInt64(); v != 0 { *dst.toInt64() = v } } } case reflect.Uint32: switch { case isSlice: // E.g., []uint32 mfi.merge = func(dst, src pointer) { sfsp := src.toUint32Slice() if *sfsp != nil { dfsp := dst.toUint32Slice() *dfsp = append(*dfsp, *sfsp...) if *dfsp == nil { *dfsp = []uint32{} } } } case isPointer: // E.g., *uint32 mfi.merge = func(dst, src pointer) { sfpp := src.toUint32Ptr() if *sfpp != nil { dfpp := dst.toUint32Ptr() if *dfpp == nil { *dfpp = Uint32(**sfpp) } else { **dfpp = **sfpp } } } default: // E.g., uint32 mfi.merge = func(dst, src pointer) { if v := *src.toUint32(); v != 0 { *dst.toUint32() = v } } } case reflect.Uint64: switch { case isSlice: // E.g., []uint64 mfi.merge = func(dst, src pointer) { sfsp := src.toUint64Slice() if *sfsp != nil { dfsp := dst.toUint64Slice() *dfsp = append(*dfsp, *sfsp...) if *dfsp == nil { *dfsp = []uint64{} } } } case isPointer: // E.g., *uint64 mfi.merge = func(dst, src pointer) { sfpp := src.toUint64Ptr() if *sfpp != nil { dfpp := dst.toUint64Ptr() if *dfpp == nil { *dfpp = Uint64(**sfpp) } else { **dfpp = **sfpp } } } default: // E.g., uint64 mfi.merge = func(dst, src pointer) { if v := *src.toUint64(); v != 0 { *dst.toUint64() = v } } } case reflect.Float32: switch { case isSlice: // E.g., []float32 mfi.merge = func(dst, src pointer) { sfsp := src.toFloat32Slice() if *sfsp != nil { dfsp := dst.toFloat32Slice() *dfsp = append(*dfsp, *sfsp...) if *dfsp == nil { *dfsp = []float32{} } } } case isPointer: // E.g., *float32 mfi.merge = func(dst, src pointer) { sfpp := src.toFloat32Ptr() if *sfpp != nil { dfpp := dst.toFloat32Ptr() if *dfpp == nil { *dfpp = Float32(**sfpp) } else { **dfpp = **sfpp } } } default: // E.g., float32 mfi.merge = func(dst, src pointer) { if v := *src.toFloat32(); v != 0 { *dst.toFloat32() = v } } } case reflect.Float64: switch { case isSlice: // E.g., []float64 mfi.merge = func(dst, src pointer) { sfsp := src.toFloat64Slice() if *sfsp != nil { dfsp := dst.toFloat64Slice() *dfsp = append(*dfsp, *sfsp...) if *dfsp == nil { *dfsp = []float64{} } } } case isPointer: // E.g., *float64 mfi.merge = func(dst, src pointer) { sfpp := src.toFloat64Ptr() if *sfpp != nil { dfpp := dst.toFloat64Ptr() if *dfpp == nil { *dfpp = Float64(**sfpp) } else { **dfpp = **sfpp } } } default: // E.g., float64 mfi.merge = func(dst, src pointer) { if v := *src.toFloat64(); v != 0 { *dst.toFloat64() = v } } } case reflect.Bool: switch { case isSlice: // E.g., []bool mfi.merge = func(dst, src pointer) { sfsp := src.toBoolSlice() if *sfsp != nil { dfsp := dst.toBoolSlice() *dfsp = append(*dfsp, *sfsp...) if *dfsp == nil { *dfsp = []bool{} } } } case isPointer: // E.g., *bool mfi.merge = func(dst, src pointer) { sfpp := src.toBoolPtr() if *sfpp != nil { dfpp := dst.toBoolPtr() if *dfpp == nil { *dfpp = Bool(**sfpp) } else { **dfpp = **sfpp } } } default: // E.g., bool mfi.merge = func(dst, src pointer) { if v := *src.toBool(); v { *dst.toBool() = v } } } case reflect.String: switch { case isSlice: // E.g., []string mfi.merge = func(dst, src pointer) { sfsp := src.toStringSlice() if *sfsp != nil { dfsp := dst.toStringSlice() *dfsp = append(*dfsp, *sfsp...) if *dfsp == nil { *dfsp = []string{} } } } case isPointer: // E.g., *string mfi.merge = func(dst, src pointer) { sfpp := src.toStringPtr() if *sfpp != nil { dfpp := dst.toStringPtr() if *dfpp == nil { *dfpp = String(**sfpp) } else { **dfpp = **sfpp } } } default: // E.g., string mfi.merge = func(dst, src pointer) { if v := *src.toString(); v != "" { *dst.toString() = v } } } case reflect.Slice: isProto3 := props.Prop[i].proto3 switch { case isPointer: panic("bad pointer in byte slice case in " + tf.Name()) case tf.Elem().Kind() != reflect.Uint8: panic("bad element kind in byte slice case in " + tf.Name()) case isSlice: // E.g., [][]byte mfi.merge = func(dst, src pointer) { sbsp := src.toBytesSlice() if *sbsp != nil { dbsp := dst.toBytesSlice() for _, sb := range *sbsp { if sb == nil { *dbsp = append(*dbsp, nil) } else { *dbsp = append(*dbsp, append([]byte{}, sb...)) } } if *dbsp == nil { *dbsp = [][]byte{} } } } default: // E.g., []byte mfi.merge = func(dst, src pointer) { sbp := src.toBytes() if *sbp != nil { dbp := dst.toBytes() if !isProto3 || len(*sbp) > 0 { *dbp = append([]byte{}, *sbp...) } } } } case reflect.Struct: switch { case isSlice && !isPointer: // E.g. []pb.T mergeInfo := getMergeInfo(tf) zero := reflect.Zero(tf) mfi.merge = func(dst, src pointer) { // TODO: Make this faster? dstsp := dst.asPointerTo(f.Type) dsts := dstsp.Elem() srcs := src.asPointerTo(f.Type).Elem() for i := 0; i < srcs.Len(); i++ { dsts = reflect.Append(dsts, zero) srcElement := srcs.Index(i).Addr() dstElement := dsts.Index(dsts.Len() - 1).Addr() mergeInfo.merge(valToPointer(dstElement), valToPointer(srcElement)) } if dsts.IsNil() { dsts = reflect.MakeSlice(f.Type, 0, 0) } dstsp.Elem().Set(dsts) } case !isPointer: mergeInfo := getMergeInfo(tf) mfi.merge = func(dst, src pointer) { mergeInfo.merge(dst, src) } case isSlice: // E.g., []*pb.T mergeInfo := getMergeInfo(tf) mfi.merge = func(dst, src pointer) { sps := src.getPointerSlice() if sps != nil { dps := dst.getPointerSlice() for _, sp := range sps { var dp pointer if !sp.isNil() { dp = valToPointer(reflect.New(tf)) mergeInfo.merge(dp, sp) } dps = append(dps, dp) } if dps == nil { dps = []pointer{} } dst.setPointerSlice(dps) } } default: // E.g., *pb.T mergeInfo := getMergeInfo(tf) mfi.merge = func(dst, src pointer) { sp := src.getPointer() if !sp.isNil() { dp := dst.getPointer() if dp.isNil() { dp = valToPointer(reflect.New(tf)) dst.setPointer(dp) } mergeInfo.merge(dp, sp) } } } case reflect.Map: switch { case isPointer || isSlice: panic("bad pointer or slice in map case in " + tf.Name()) default: // E.g., map[K]V mfi.merge = func(dst, src pointer) { sm := src.asPointerTo(tf).Elem() if sm.Len() == 0 { return } dm := dst.asPointerTo(tf).Elem() if dm.IsNil() { dm.Set(reflect.MakeMap(tf)) } switch tf.Elem().Kind() { case reflect.Ptr: // Proto struct (e.g., *T) for _, key := range sm.MapKeys() { val := sm.MapIndex(key) val = reflect.ValueOf(Clone(val.Interface().(Message))) dm.SetMapIndex(key, val) } case reflect.Slice: // E.g. Bytes type (e.g., []byte) for _, key := range sm.MapKeys() { val := sm.MapIndex(key) val = reflect.ValueOf(append([]byte{}, val.Bytes()...)) dm.SetMapIndex(key, val) } default: // Basic type (e.g., string) for _, key := range sm.MapKeys() { val := sm.MapIndex(key) dm.SetMapIndex(key, val) } } } } case reflect.Interface: // Must be oneof field. switch { case isPointer || isSlice: panic("bad pointer or slice in interface case in " + tf.Name()) default: // E.g., interface{} // TODO: Make this faster? mfi.merge = func(dst, src pointer) { su := src.asPointerTo(tf).Elem() if !su.IsNil() { du := dst.asPointerTo(tf).Elem() typ := su.Elem().Type() if du.IsNil() || du.Elem().Type() != typ { du.Set(reflect.New(typ.Elem())) // Initialize interface if empty } sv := su.Elem().Elem().Field(0) if sv.Kind() == reflect.Ptr && sv.IsNil() { return } dv := du.Elem().Elem().Field(0) if dv.Kind() == reflect.Ptr && dv.IsNil() { dv.Set(reflect.New(sv.Type().Elem())) // Initialize proto message if empty } switch sv.Type().Kind() { case reflect.Ptr: // Proto struct (e.g., *T) Merge(dv.Interface().(Message), sv.Interface().(Message)) case reflect.Slice: // E.g. Bytes type (e.g., []byte) dv.Set(reflect.ValueOf(append([]byte{}, sv.Bytes()...))) default: // Basic type (e.g., string) dv.Set(sv) } } } } default: panic(fmt.Sprintf("merger not found for type:%s", tf)) } mi.fields = append(mi.fields, mfi) } mi.unrecognized = invalidField if f, ok := t.FieldByName("XXX_unrecognized"); ok { if f.Type != reflect.TypeOf([]byte{}) { panic("expected XXX_unrecognized to be of type []byte") } mi.unrecognized = toField(&f) } atomic.StoreInt32(&mi.initialized, 1) } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/table_unmarshal.go ================================================ // Go support for Protocol Buffers - Google's data interchange format // // Copyright 2016 The Go Authors. All rights reserved. // https://github.com/golang/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto import ( "errors" "fmt" "io" "math" "reflect" "strconv" "strings" "sync" "sync/atomic" "unicode/utf8" ) // Unmarshal is the entry point from the generated .pb.go files. // This function is not intended to be used by non-generated code. // This function is not subject to any compatibility guarantee. // msg contains a pointer to a protocol buffer struct. // b is the data to be unmarshaled into the protocol buffer. // a is a pointer to a place to store cached unmarshal information. func (a *InternalMessageInfo) Unmarshal(msg Message, b []byte) error { // Load the unmarshal information for this message type. // The atomic load ensures memory consistency. u := atomicLoadUnmarshalInfo(&a.unmarshal) if u == nil { // Slow path: find unmarshal info for msg, update a with it. u = getUnmarshalInfo(reflect.TypeOf(msg).Elem()) atomicStoreUnmarshalInfo(&a.unmarshal, u) } // Then do the unmarshaling. err := u.unmarshal(toPointer(&msg), b) return err } type unmarshalInfo struct { typ reflect.Type // type of the protobuf struct // 0 = only typ field is initialized // 1 = completely initialized initialized int32 lock sync.Mutex // prevents double initialization dense []unmarshalFieldInfo // fields indexed by tag # sparse map[uint64]unmarshalFieldInfo // fields indexed by tag # reqFields []string // names of required fields reqMask uint64 // 1< 0 { // Read tag and wire type. // Special case 1 and 2 byte varints. var x uint64 if b[0] < 128 { x = uint64(b[0]) b = b[1:] } else if len(b) >= 2 && b[1] < 128 { x = uint64(b[0]&0x7f) + uint64(b[1])<<7 b = b[2:] } else { var n int x, n = decodeVarint(b) if n == 0 { return io.ErrUnexpectedEOF } b = b[n:] } tag := x >> 3 wire := int(x) & 7 // Dispatch on the tag to one of the unmarshal* functions below. var f unmarshalFieldInfo if tag < uint64(len(u.dense)) { f = u.dense[tag] } else { f = u.sparse[tag] } if fn := f.unmarshal; fn != nil { var err error b, err = fn(b, m.offset(f.field), wire) if err == nil { reqMask |= f.reqMask continue } if r, ok := err.(*RequiredNotSetError); ok { // Remember this error, but keep parsing. We need to produce // a full parse even if a required field is missing. if errLater == nil { errLater = r } reqMask |= f.reqMask continue } if err != errInternalBadWireType { if err == errInvalidUTF8 { if errLater == nil { fullName := revProtoTypes[reflect.PtrTo(u.typ)] + "." + f.name errLater = &invalidUTF8Error{fullName} } continue } return err } // Fragments with bad wire type are treated as unknown fields. } // Unknown tag. if !u.unrecognized.IsValid() { // Don't keep unrecognized data; just skip it. var err error b, err = skipField(b, wire) if err != nil { return err } continue } // Keep unrecognized data around. // maybe in extensions, maybe in the unrecognized field. z := m.offset(u.unrecognized).toBytes() var emap map[int32]Extension var e Extension for _, r := range u.extensionRanges { if uint64(r.Start) <= tag && tag <= uint64(r.End) { if u.extensions.IsValid() { mp := m.offset(u.extensions).toExtensions() emap = mp.extensionsWrite() e = emap[int32(tag)] z = &e.enc break } if u.oldExtensions.IsValid() { p := m.offset(u.oldExtensions).toOldExtensions() emap = *p if emap == nil { emap = map[int32]Extension{} *p = emap } e = emap[int32(tag)] z = &e.enc break } if u.bytesExtensions.IsValid() { z = m.offset(u.bytesExtensions).toBytes() break } panic("no extensions field available") } } // Use wire type to skip data. var err error b0 := b b, err = skipField(b, wire) if err != nil { return err } *z = encodeVarint(*z, tag<<3|uint64(wire)) *z = append(*z, b0[:len(b0)-len(b)]...) if emap != nil { emap[int32(tag)] = e } } if reqMask != u.reqMask && errLater == nil { // A required field of this message is missing. for _, n := range u.reqFields { if reqMask&1 == 0 { errLater = &RequiredNotSetError{n} } reqMask >>= 1 } } return errLater } // computeUnmarshalInfo fills in u with information for use // in unmarshaling protocol buffers of type u.typ. func (u *unmarshalInfo) computeUnmarshalInfo() { u.lock.Lock() defer u.lock.Unlock() if u.initialized != 0 { return } t := u.typ n := t.NumField() // Set up the "not found" value for the unrecognized byte buffer. // This is the default for proto3. u.unrecognized = invalidField u.extensions = invalidField u.oldExtensions = invalidField u.bytesExtensions = invalidField // List of the generated type and offset for each oneof field. type oneofField struct { ityp reflect.Type // interface type of oneof field field field // offset in containing message } var oneofFields []oneofField for i := 0; i < n; i++ { f := t.Field(i) if f.Name == "XXX_unrecognized" { // The byte slice used to hold unrecognized input is special. if f.Type != reflect.TypeOf(([]byte)(nil)) { panic("bad type for XXX_unrecognized field: " + f.Type.Name()) } u.unrecognized = toField(&f) continue } if f.Name == "XXX_InternalExtensions" { // Ditto here. if f.Type != reflect.TypeOf(XXX_InternalExtensions{}) { panic("bad type for XXX_InternalExtensions field: " + f.Type.Name()) } u.extensions = toField(&f) if f.Tag.Get("protobuf_messageset") == "1" { u.isMessageSet = true } continue } if f.Name == "XXX_extensions" { // An older form of the extensions field. if f.Type == reflect.TypeOf((map[int32]Extension)(nil)) { u.oldExtensions = toField(&f) continue } else if f.Type == reflect.TypeOf(([]byte)(nil)) { u.bytesExtensions = toField(&f) continue } panic("bad type for XXX_extensions field: " + f.Type.Name()) } if f.Name == "XXX_NoUnkeyedLiteral" || f.Name == "XXX_sizecache" { continue } oneof := f.Tag.Get("protobuf_oneof") if oneof != "" { oneofFields = append(oneofFields, oneofField{f.Type, toField(&f)}) // The rest of oneof processing happens below. continue } tags := f.Tag.Get("protobuf") tagArray := strings.Split(tags, ",") if len(tagArray) < 2 { panic("protobuf tag not enough fields in " + t.Name() + "." + f.Name + ": " + tags) } tag, err := strconv.Atoi(tagArray[1]) if err != nil { panic("protobuf tag field not an integer: " + tagArray[1]) } name := "" for _, tag := range tagArray[3:] { if strings.HasPrefix(tag, "name=") { name = tag[5:] } } // Extract unmarshaling function from the field (its type and tags). unmarshal := fieldUnmarshaler(&f) // Required field? var reqMask uint64 if tagArray[2] == "req" { bit := len(u.reqFields) u.reqFields = append(u.reqFields, name) reqMask = uint64(1) << uint(bit) // TODO: if we have more than 64 required fields, we end up // not verifying that all required fields are present. // Fix this, perhaps using a count of required fields? } // Store the info in the correct slot in the message. u.setTag(tag, toField(&f), unmarshal, reqMask, name) } // Find any types associated with oneof fields. // gogo: len(oneofFields) > 0 is needed for embedded oneof messages, without a marshaler and unmarshaler if len(oneofFields) > 0 { var oneofImplementers []interface{} switch m := reflect.Zero(reflect.PtrTo(t)).Interface().(type) { case oneofFuncsIface: _, _, _, oneofImplementers = m.XXX_OneofFuncs() case oneofWrappersIface: oneofImplementers = m.XXX_OneofWrappers() } for _, v := range oneofImplementers { tptr := reflect.TypeOf(v) // *Msg_X typ := tptr.Elem() // Msg_X f := typ.Field(0) // oneof implementers have one field baseUnmarshal := fieldUnmarshaler(&f) tags := strings.Split(f.Tag.Get("protobuf"), ",") fieldNum, err := strconv.Atoi(tags[1]) if err != nil { panic("protobuf tag field not an integer: " + tags[1]) } var name string for _, tag := range tags { if strings.HasPrefix(tag, "name=") { name = strings.TrimPrefix(tag, "name=") break } } // Find the oneof field that this struct implements. // Might take O(n^2) to process all of the oneofs, but who cares. for _, of := range oneofFields { if tptr.Implements(of.ityp) { // We have found the corresponding interface for this struct. // That lets us know where this struct should be stored // when we encounter it during unmarshaling. unmarshal := makeUnmarshalOneof(typ, of.ityp, baseUnmarshal) u.setTag(fieldNum, of.field, unmarshal, 0, name) } } } } // Get extension ranges, if any. fn := reflect.Zero(reflect.PtrTo(t)).MethodByName("ExtensionRangeArray") if fn.IsValid() { if !u.extensions.IsValid() && !u.oldExtensions.IsValid() && !u.bytesExtensions.IsValid() { panic("a message with extensions, but no extensions field in " + t.Name()) } u.extensionRanges = fn.Call(nil)[0].Interface().([]ExtensionRange) } // Explicitly disallow tag 0. This will ensure we flag an error // when decoding a buffer of all zeros. Without this code, we // would decode and skip an all-zero buffer of even length. // [0 0] is [tag=0/wiretype=varint varint-encoded-0]. u.setTag(0, zeroField, func(b []byte, f pointer, w int) ([]byte, error) { return nil, fmt.Errorf("proto: %s: illegal tag 0 (wire type %d)", t, w) }, 0, "") // Set mask for required field check. u.reqMask = uint64(1)<= 0 && (tag < 16 || tag < 2*n) { // TODO: what are the right numbers here? for len(u.dense) <= tag { u.dense = append(u.dense, unmarshalFieldInfo{}) } u.dense[tag] = i return } if u.sparse == nil { u.sparse = map[uint64]unmarshalFieldInfo{} } u.sparse[uint64(tag)] = i } // fieldUnmarshaler returns an unmarshaler for the given field. func fieldUnmarshaler(f *reflect.StructField) unmarshaler { if f.Type.Kind() == reflect.Map { return makeUnmarshalMap(f) } return typeUnmarshaler(f.Type, f.Tag.Get("protobuf")) } // typeUnmarshaler returns an unmarshaler for the given field type / field tag pair. func typeUnmarshaler(t reflect.Type, tags string) unmarshaler { tagArray := strings.Split(tags, ",") encoding := tagArray[0] name := "unknown" ctype := false isTime := false isDuration := false isWktPointer := false proto3 := false validateUTF8 := true for _, tag := range tagArray[3:] { if strings.HasPrefix(tag, "name=") { name = tag[5:] } if tag == "proto3" { proto3 = true } if strings.HasPrefix(tag, "customtype=") { ctype = true } if tag == "stdtime" { isTime = true } if tag == "stdduration" { isDuration = true } if tag == "wktptr" { isWktPointer = true } } validateUTF8 = validateUTF8 && proto3 // Figure out packaging (pointer, slice, or both) slice := false pointer := false if t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8 { slice = true t = t.Elem() } if t.Kind() == reflect.Ptr { pointer = true t = t.Elem() } if ctype { if reflect.PtrTo(t).Implements(customType) { if slice { return makeUnmarshalCustomSlice(getUnmarshalInfo(t), name) } if pointer { return makeUnmarshalCustomPtr(getUnmarshalInfo(t), name) } return makeUnmarshalCustom(getUnmarshalInfo(t), name) } else { panic(fmt.Sprintf("custom type: type: %v, does not implement the proto.custom interface", t)) } } if isTime { if pointer { if slice { return makeUnmarshalTimePtrSlice(getUnmarshalInfo(t), name) } return makeUnmarshalTimePtr(getUnmarshalInfo(t), name) } if slice { return makeUnmarshalTimeSlice(getUnmarshalInfo(t), name) } return makeUnmarshalTime(getUnmarshalInfo(t), name) } if isDuration { if pointer { if slice { return makeUnmarshalDurationPtrSlice(getUnmarshalInfo(t), name) } return makeUnmarshalDurationPtr(getUnmarshalInfo(t), name) } if slice { return makeUnmarshalDurationSlice(getUnmarshalInfo(t), name) } return makeUnmarshalDuration(getUnmarshalInfo(t), name) } if isWktPointer { switch t.Kind() { case reflect.Float64: if pointer { if slice { return makeStdDoubleValuePtrSliceUnmarshaler(getUnmarshalInfo(t), name) } return makeStdDoubleValuePtrUnmarshaler(getUnmarshalInfo(t), name) } if slice { return makeStdDoubleValueSliceUnmarshaler(getUnmarshalInfo(t), name) } return makeStdDoubleValueUnmarshaler(getUnmarshalInfo(t), name) case reflect.Float32: if pointer { if slice { return makeStdFloatValuePtrSliceUnmarshaler(getUnmarshalInfo(t), name) } return makeStdFloatValuePtrUnmarshaler(getUnmarshalInfo(t), name) } if slice { return makeStdFloatValueSliceUnmarshaler(getUnmarshalInfo(t), name) } return makeStdFloatValueUnmarshaler(getUnmarshalInfo(t), name) case reflect.Int64: if pointer { if slice { return makeStdInt64ValuePtrSliceUnmarshaler(getUnmarshalInfo(t), name) } return makeStdInt64ValuePtrUnmarshaler(getUnmarshalInfo(t), name) } if slice { return makeStdInt64ValueSliceUnmarshaler(getUnmarshalInfo(t), name) } return makeStdInt64ValueUnmarshaler(getUnmarshalInfo(t), name) case reflect.Uint64: if pointer { if slice { return makeStdUInt64ValuePtrSliceUnmarshaler(getUnmarshalInfo(t), name) } return makeStdUInt64ValuePtrUnmarshaler(getUnmarshalInfo(t), name) } if slice { return makeStdUInt64ValueSliceUnmarshaler(getUnmarshalInfo(t), name) } return makeStdUInt64ValueUnmarshaler(getUnmarshalInfo(t), name) case reflect.Int32: if pointer { if slice { return makeStdInt32ValuePtrSliceUnmarshaler(getUnmarshalInfo(t), name) } return makeStdInt32ValuePtrUnmarshaler(getUnmarshalInfo(t), name) } if slice { return makeStdInt32ValueSliceUnmarshaler(getUnmarshalInfo(t), name) } return makeStdInt32ValueUnmarshaler(getUnmarshalInfo(t), name) case reflect.Uint32: if pointer { if slice { return makeStdUInt32ValuePtrSliceUnmarshaler(getUnmarshalInfo(t), name) } return makeStdUInt32ValuePtrUnmarshaler(getUnmarshalInfo(t), name) } if slice { return makeStdUInt32ValueSliceUnmarshaler(getUnmarshalInfo(t), name) } return makeStdUInt32ValueUnmarshaler(getUnmarshalInfo(t), name) case reflect.Bool: if pointer { if slice { return makeStdBoolValuePtrSliceUnmarshaler(getUnmarshalInfo(t), name) } return makeStdBoolValuePtrUnmarshaler(getUnmarshalInfo(t), name) } if slice { return makeStdBoolValueSliceUnmarshaler(getUnmarshalInfo(t), name) } return makeStdBoolValueUnmarshaler(getUnmarshalInfo(t), name) case reflect.String: if pointer { if slice { return makeStdStringValuePtrSliceUnmarshaler(getUnmarshalInfo(t), name) } return makeStdStringValuePtrUnmarshaler(getUnmarshalInfo(t), name) } if slice { return makeStdStringValueSliceUnmarshaler(getUnmarshalInfo(t), name) } return makeStdStringValueUnmarshaler(getUnmarshalInfo(t), name) case uint8SliceType: if pointer { if slice { return makeStdBytesValuePtrSliceUnmarshaler(getUnmarshalInfo(t), name) } return makeStdBytesValuePtrUnmarshaler(getUnmarshalInfo(t), name) } if slice { return makeStdBytesValueSliceUnmarshaler(getUnmarshalInfo(t), name) } return makeStdBytesValueUnmarshaler(getUnmarshalInfo(t), name) default: panic(fmt.Sprintf("unknown wktpointer type %#v", t)) } } // We'll never have both pointer and slice for basic types. if pointer && slice && t.Kind() != reflect.Struct { panic("both pointer and slice for basic type in " + t.Name()) } switch t.Kind() { case reflect.Bool: if pointer { return unmarshalBoolPtr } if slice { return unmarshalBoolSlice } return unmarshalBoolValue case reflect.Int32: switch encoding { case "fixed32": if pointer { return unmarshalFixedS32Ptr } if slice { return unmarshalFixedS32Slice } return unmarshalFixedS32Value case "varint": // this could be int32 or enum if pointer { return unmarshalInt32Ptr } if slice { return unmarshalInt32Slice } return unmarshalInt32Value case "zigzag32": if pointer { return unmarshalSint32Ptr } if slice { return unmarshalSint32Slice } return unmarshalSint32Value } case reflect.Int64: switch encoding { case "fixed64": if pointer { return unmarshalFixedS64Ptr } if slice { return unmarshalFixedS64Slice } return unmarshalFixedS64Value case "varint": if pointer { return unmarshalInt64Ptr } if slice { return unmarshalInt64Slice } return unmarshalInt64Value case "zigzag64": if pointer { return unmarshalSint64Ptr } if slice { return unmarshalSint64Slice } return unmarshalSint64Value } case reflect.Uint32: switch encoding { case "fixed32": if pointer { return unmarshalFixed32Ptr } if slice { return unmarshalFixed32Slice } return unmarshalFixed32Value case "varint": if pointer { return unmarshalUint32Ptr } if slice { return unmarshalUint32Slice } return unmarshalUint32Value } case reflect.Uint64: switch encoding { case "fixed64": if pointer { return unmarshalFixed64Ptr } if slice { return unmarshalFixed64Slice } return unmarshalFixed64Value case "varint": if pointer { return unmarshalUint64Ptr } if slice { return unmarshalUint64Slice } return unmarshalUint64Value } case reflect.Float32: if pointer { return unmarshalFloat32Ptr } if slice { return unmarshalFloat32Slice } return unmarshalFloat32Value case reflect.Float64: if pointer { return unmarshalFloat64Ptr } if slice { return unmarshalFloat64Slice } return unmarshalFloat64Value case reflect.Map: panic("map type in typeUnmarshaler in " + t.Name()) case reflect.Slice: if pointer { panic("bad pointer in slice case in " + t.Name()) } if slice { return unmarshalBytesSlice } return unmarshalBytesValue case reflect.String: if validateUTF8 { if pointer { return unmarshalUTF8StringPtr } if slice { return unmarshalUTF8StringSlice } return unmarshalUTF8StringValue } if pointer { return unmarshalStringPtr } if slice { return unmarshalStringSlice } return unmarshalStringValue case reflect.Struct: // message or group field if !pointer { switch encoding { case "bytes": if slice { return makeUnmarshalMessageSlice(getUnmarshalInfo(t), name) } return makeUnmarshalMessage(getUnmarshalInfo(t), name) } } switch encoding { case "bytes": if slice { return makeUnmarshalMessageSlicePtr(getUnmarshalInfo(t), name) } return makeUnmarshalMessagePtr(getUnmarshalInfo(t), name) case "group": if slice { return makeUnmarshalGroupSlicePtr(getUnmarshalInfo(t), name) } return makeUnmarshalGroupPtr(getUnmarshalInfo(t), name) } } panic(fmt.Sprintf("unmarshaler not found type:%s encoding:%s", t, encoding)) } // Below are all the unmarshalers for individual fields of various types. func unmarshalInt64Value(b []byte, f pointer, w int) ([]byte, error) { if w != WireVarint { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] v := int64(x) *f.toInt64() = v return b, nil } func unmarshalInt64Ptr(b []byte, f pointer, w int) ([]byte, error) { if w != WireVarint { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] v := int64(x) *f.toInt64Ptr() = &v return b, nil } func unmarshalInt64Slice(b []byte, f pointer, w int) ([]byte, error) { if w == WireBytes { // packed x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } res := b[x:] b = b[:x] for len(b) > 0 { x, n = decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] v := int64(x) s := f.toInt64Slice() *s = append(*s, v) } return res, nil } if w != WireVarint { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] v := int64(x) s := f.toInt64Slice() *s = append(*s, v) return b, nil } func unmarshalSint64Value(b []byte, f pointer, w int) ([]byte, error) { if w != WireVarint { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] v := int64(x>>1) ^ int64(x)<<63>>63 *f.toInt64() = v return b, nil } func unmarshalSint64Ptr(b []byte, f pointer, w int) ([]byte, error) { if w != WireVarint { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] v := int64(x>>1) ^ int64(x)<<63>>63 *f.toInt64Ptr() = &v return b, nil } func unmarshalSint64Slice(b []byte, f pointer, w int) ([]byte, error) { if w == WireBytes { // packed x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } res := b[x:] b = b[:x] for len(b) > 0 { x, n = decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] v := int64(x>>1) ^ int64(x)<<63>>63 s := f.toInt64Slice() *s = append(*s, v) } return res, nil } if w != WireVarint { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] v := int64(x>>1) ^ int64(x)<<63>>63 s := f.toInt64Slice() *s = append(*s, v) return b, nil } func unmarshalUint64Value(b []byte, f pointer, w int) ([]byte, error) { if w != WireVarint { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] v := uint64(x) *f.toUint64() = v return b, nil } func unmarshalUint64Ptr(b []byte, f pointer, w int) ([]byte, error) { if w != WireVarint { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] v := uint64(x) *f.toUint64Ptr() = &v return b, nil } func unmarshalUint64Slice(b []byte, f pointer, w int) ([]byte, error) { if w == WireBytes { // packed x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } res := b[x:] b = b[:x] for len(b) > 0 { x, n = decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] v := uint64(x) s := f.toUint64Slice() *s = append(*s, v) } return res, nil } if w != WireVarint { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] v := uint64(x) s := f.toUint64Slice() *s = append(*s, v) return b, nil } func unmarshalInt32Value(b []byte, f pointer, w int) ([]byte, error) { if w != WireVarint { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] v := int32(x) *f.toInt32() = v return b, nil } func unmarshalInt32Ptr(b []byte, f pointer, w int) ([]byte, error) { if w != WireVarint { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] v := int32(x) f.setInt32Ptr(v) return b, nil } func unmarshalInt32Slice(b []byte, f pointer, w int) ([]byte, error) { if w == WireBytes { // packed x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } res := b[x:] b = b[:x] for len(b) > 0 { x, n = decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] v := int32(x) f.appendInt32Slice(v) } return res, nil } if w != WireVarint { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] v := int32(x) f.appendInt32Slice(v) return b, nil } func unmarshalSint32Value(b []byte, f pointer, w int) ([]byte, error) { if w != WireVarint { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] v := int32(x>>1) ^ int32(x)<<31>>31 *f.toInt32() = v return b, nil } func unmarshalSint32Ptr(b []byte, f pointer, w int) ([]byte, error) { if w != WireVarint { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] v := int32(x>>1) ^ int32(x)<<31>>31 f.setInt32Ptr(v) return b, nil } func unmarshalSint32Slice(b []byte, f pointer, w int) ([]byte, error) { if w == WireBytes { // packed x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } res := b[x:] b = b[:x] for len(b) > 0 { x, n = decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] v := int32(x>>1) ^ int32(x)<<31>>31 f.appendInt32Slice(v) } return res, nil } if w != WireVarint { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] v := int32(x>>1) ^ int32(x)<<31>>31 f.appendInt32Slice(v) return b, nil } func unmarshalUint32Value(b []byte, f pointer, w int) ([]byte, error) { if w != WireVarint { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] v := uint32(x) *f.toUint32() = v return b, nil } func unmarshalUint32Ptr(b []byte, f pointer, w int) ([]byte, error) { if w != WireVarint { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] v := uint32(x) *f.toUint32Ptr() = &v return b, nil } func unmarshalUint32Slice(b []byte, f pointer, w int) ([]byte, error) { if w == WireBytes { // packed x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } res := b[x:] b = b[:x] for len(b) > 0 { x, n = decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] v := uint32(x) s := f.toUint32Slice() *s = append(*s, v) } return res, nil } if w != WireVarint { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] v := uint32(x) s := f.toUint32Slice() *s = append(*s, v) return b, nil } func unmarshalFixed64Value(b []byte, f pointer, w int) ([]byte, error) { if w != WireFixed64 { return b, errInternalBadWireType } if len(b) < 8 { return nil, io.ErrUnexpectedEOF } v := uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 *f.toUint64() = v return b[8:], nil } func unmarshalFixed64Ptr(b []byte, f pointer, w int) ([]byte, error) { if w != WireFixed64 { return b, errInternalBadWireType } if len(b) < 8 { return nil, io.ErrUnexpectedEOF } v := uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 *f.toUint64Ptr() = &v return b[8:], nil } func unmarshalFixed64Slice(b []byte, f pointer, w int) ([]byte, error) { if w == WireBytes { // packed x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } res := b[x:] b = b[:x] for len(b) > 0 { if len(b) < 8 { return nil, io.ErrUnexpectedEOF } v := uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 s := f.toUint64Slice() *s = append(*s, v) b = b[8:] } return res, nil } if w != WireFixed64 { return b, errInternalBadWireType } if len(b) < 8 { return nil, io.ErrUnexpectedEOF } v := uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 s := f.toUint64Slice() *s = append(*s, v) return b[8:], nil } func unmarshalFixedS64Value(b []byte, f pointer, w int) ([]byte, error) { if w != WireFixed64 { return b, errInternalBadWireType } if len(b) < 8 { return nil, io.ErrUnexpectedEOF } v := int64(b[0]) | int64(b[1])<<8 | int64(b[2])<<16 | int64(b[3])<<24 | int64(b[4])<<32 | int64(b[5])<<40 | int64(b[6])<<48 | int64(b[7])<<56 *f.toInt64() = v return b[8:], nil } func unmarshalFixedS64Ptr(b []byte, f pointer, w int) ([]byte, error) { if w != WireFixed64 { return b, errInternalBadWireType } if len(b) < 8 { return nil, io.ErrUnexpectedEOF } v := int64(b[0]) | int64(b[1])<<8 | int64(b[2])<<16 | int64(b[3])<<24 | int64(b[4])<<32 | int64(b[5])<<40 | int64(b[6])<<48 | int64(b[7])<<56 *f.toInt64Ptr() = &v return b[8:], nil } func unmarshalFixedS64Slice(b []byte, f pointer, w int) ([]byte, error) { if w == WireBytes { // packed x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } res := b[x:] b = b[:x] for len(b) > 0 { if len(b) < 8 { return nil, io.ErrUnexpectedEOF } v := int64(b[0]) | int64(b[1])<<8 | int64(b[2])<<16 | int64(b[3])<<24 | int64(b[4])<<32 | int64(b[5])<<40 | int64(b[6])<<48 | int64(b[7])<<56 s := f.toInt64Slice() *s = append(*s, v) b = b[8:] } return res, nil } if w != WireFixed64 { return b, errInternalBadWireType } if len(b) < 8 { return nil, io.ErrUnexpectedEOF } v := int64(b[0]) | int64(b[1])<<8 | int64(b[2])<<16 | int64(b[3])<<24 | int64(b[4])<<32 | int64(b[5])<<40 | int64(b[6])<<48 | int64(b[7])<<56 s := f.toInt64Slice() *s = append(*s, v) return b[8:], nil } func unmarshalFixed32Value(b []byte, f pointer, w int) ([]byte, error) { if w != WireFixed32 { return b, errInternalBadWireType } if len(b) < 4 { return nil, io.ErrUnexpectedEOF } v := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 *f.toUint32() = v return b[4:], nil } func unmarshalFixed32Ptr(b []byte, f pointer, w int) ([]byte, error) { if w != WireFixed32 { return b, errInternalBadWireType } if len(b) < 4 { return nil, io.ErrUnexpectedEOF } v := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 *f.toUint32Ptr() = &v return b[4:], nil } func unmarshalFixed32Slice(b []byte, f pointer, w int) ([]byte, error) { if w == WireBytes { // packed x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } res := b[x:] b = b[:x] for len(b) > 0 { if len(b) < 4 { return nil, io.ErrUnexpectedEOF } v := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 s := f.toUint32Slice() *s = append(*s, v) b = b[4:] } return res, nil } if w != WireFixed32 { return b, errInternalBadWireType } if len(b) < 4 { return nil, io.ErrUnexpectedEOF } v := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 s := f.toUint32Slice() *s = append(*s, v) return b[4:], nil } func unmarshalFixedS32Value(b []byte, f pointer, w int) ([]byte, error) { if w != WireFixed32 { return b, errInternalBadWireType } if len(b) < 4 { return nil, io.ErrUnexpectedEOF } v := int32(b[0]) | int32(b[1])<<8 | int32(b[2])<<16 | int32(b[3])<<24 *f.toInt32() = v return b[4:], nil } func unmarshalFixedS32Ptr(b []byte, f pointer, w int) ([]byte, error) { if w != WireFixed32 { return b, errInternalBadWireType } if len(b) < 4 { return nil, io.ErrUnexpectedEOF } v := int32(b[0]) | int32(b[1])<<8 | int32(b[2])<<16 | int32(b[3])<<24 f.setInt32Ptr(v) return b[4:], nil } func unmarshalFixedS32Slice(b []byte, f pointer, w int) ([]byte, error) { if w == WireBytes { // packed x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } res := b[x:] b = b[:x] for len(b) > 0 { if len(b) < 4 { return nil, io.ErrUnexpectedEOF } v := int32(b[0]) | int32(b[1])<<8 | int32(b[2])<<16 | int32(b[3])<<24 f.appendInt32Slice(v) b = b[4:] } return res, nil } if w != WireFixed32 { return b, errInternalBadWireType } if len(b) < 4 { return nil, io.ErrUnexpectedEOF } v := int32(b[0]) | int32(b[1])<<8 | int32(b[2])<<16 | int32(b[3])<<24 f.appendInt32Slice(v) return b[4:], nil } func unmarshalBoolValue(b []byte, f pointer, w int) ([]byte, error) { if w != WireVarint { return b, errInternalBadWireType } // Note: any length varint is allowed, even though any sane // encoder will use one byte. // See https://github.com/golang/protobuf/issues/76 x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } // TODO: check if x>1? Tests seem to indicate no. v := x != 0 *f.toBool() = v return b[n:], nil } func unmarshalBoolPtr(b []byte, f pointer, w int) ([]byte, error) { if w != WireVarint { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } v := x != 0 *f.toBoolPtr() = &v return b[n:], nil } func unmarshalBoolSlice(b []byte, f pointer, w int) ([]byte, error) { if w == WireBytes { // packed x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } res := b[x:] b = b[:x] for len(b) > 0 { x, n = decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } v := x != 0 s := f.toBoolSlice() *s = append(*s, v) b = b[n:] } return res, nil } if w != WireVarint { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } v := x != 0 s := f.toBoolSlice() *s = append(*s, v) return b[n:], nil } func unmarshalFloat64Value(b []byte, f pointer, w int) ([]byte, error) { if w != WireFixed64 { return b, errInternalBadWireType } if len(b) < 8 { return nil, io.ErrUnexpectedEOF } v := math.Float64frombits(uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56) *f.toFloat64() = v return b[8:], nil } func unmarshalFloat64Ptr(b []byte, f pointer, w int) ([]byte, error) { if w != WireFixed64 { return b, errInternalBadWireType } if len(b) < 8 { return nil, io.ErrUnexpectedEOF } v := math.Float64frombits(uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56) *f.toFloat64Ptr() = &v return b[8:], nil } func unmarshalFloat64Slice(b []byte, f pointer, w int) ([]byte, error) { if w == WireBytes { // packed x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } res := b[x:] b = b[:x] for len(b) > 0 { if len(b) < 8 { return nil, io.ErrUnexpectedEOF } v := math.Float64frombits(uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56) s := f.toFloat64Slice() *s = append(*s, v) b = b[8:] } return res, nil } if w != WireFixed64 { return b, errInternalBadWireType } if len(b) < 8 { return nil, io.ErrUnexpectedEOF } v := math.Float64frombits(uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56) s := f.toFloat64Slice() *s = append(*s, v) return b[8:], nil } func unmarshalFloat32Value(b []byte, f pointer, w int) ([]byte, error) { if w != WireFixed32 { return b, errInternalBadWireType } if len(b) < 4 { return nil, io.ErrUnexpectedEOF } v := math.Float32frombits(uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24) *f.toFloat32() = v return b[4:], nil } func unmarshalFloat32Ptr(b []byte, f pointer, w int) ([]byte, error) { if w != WireFixed32 { return b, errInternalBadWireType } if len(b) < 4 { return nil, io.ErrUnexpectedEOF } v := math.Float32frombits(uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24) *f.toFloat32Ptr() = &v return b[4:], nil } func unmarshalFloat32Slice(b []byte, f pointer, w int) ([]byte, error) { if w == WireBytes { // packed x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } res := b[x:] b = b[:x] for len(b) > 0 { if len(b) < 4 { return nil, io.ErrUnexpectedEOF } v := math.Float32frombits(uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24) s := f.toFloat32Slice() *s = append(*s, v) b = b[4:] } return res, nil } if w != WireFixed32 { return b, errInternalBadWireType } if len(b) < 4 { return nil, io.ErrUnexpectedEOF } v := math.Float32frombits(uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24) s := f.toFloat32Slice() *s = append(*s, v) return b[4:], nil } func unmarshalStringValue(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } v := string(b[:x]) *f.toString() = v return b[x:], nil } func unmarshalStringPtr(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } v := string(b[:x]) *f.toStringPtr() = &v return b[x:], nil } func unmarshalStringSlice(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } v := string(b[:x]) s := f.toStringSlice() *s = append(*s, v) return b[x:], nil } func unmarshalUTF8StringValue(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } v := string(b[:x]) *f.toString() = v if !utf8.ValidString(v) { return b[x:], errInvalidUTF8 } return b[x:], nil } func unmarshalUTF8StringPtr(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } v := string(b[:x]) *f.toStringPtr() = &v if !utf8.ValidString(v) { return b[x:], errInvalidUTF8 } return b[x:], nil } func unmarshalUTF8StringSlice(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } v := string(b[:x]) s := f.toStringSlice() *s = append(*s, v) if !utf8.ValidString(v) { return b[x:], errInvalidUTF8 } return b[x:], nil } var emptyBuf [0]byte func unmarshalBytesValue(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } // The use of append here is a trick which avoids the zeroing // that would be required if we used a make/copy pair. // We append to emptyBuf instead of nil because we want // a non-nil result even when the length is 0. v := append(emptyBuf[:], b[:x]...) *f.toBytes() = v return b[x:], nil } func unmarshalBytesSlice(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } v := append(emptyBuf[:], b[:x]...) s := f.toBytesSlice() *s = append(*s, v) return b[x:], nil } func makeUnmarshalMessagePtr(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } // First read the message field to see if something is there. // The semantics of multiple submessages are weird. Instead of // the last one winning (as it is for all other fields), multiple // submessages are merged. v := f.getPointer() if v.isNil() { v = valToPointer(reflect.New(sub.typ)) f.setPointer(v) } err := sub.unmarshal(v, b[:x]) if err != nil { if r, ok := err.(*RequiredNotSetError); ok { r.field = name + "." + r.field } else { return nil, err } } return b[x:], err } } func makeUnmarshalMessageSlicePtr(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return b, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } v := valToPointer(reflect.New(sub.typ)) err := sub.unmarshal(v, b[:x]) if err != nil { if r, ok := err.(*RequiredNotSetError); ok { r.field = name + "." + r.field } else { return nil, err } } f.appendPointer(v) return b[x:], err } } func makeUnmarshalGroupPtr(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireStartGroup { return b, errInternalBadWireType } x, y := findEndGroup(b) if x < 0 { return nil, io.ErrUnexpectedEOF } v := f.getPointer() if v.isNil() { v = valToPointer(reflect.New(sub.typ)) f.setPointer(v) } err := sub.unmarshal(v, b[:x]) if err != nil { if r, ok := err.(*RequiredNotSetError); ok { r.field = name + "." + r.field } else { return nil, err } } return b[y:], err } } func makeUnmarshalGroupSlicePtr(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireStartGroup { return b, errInternalBadWireType } x, y := findEndGroup(b) if x < 0 { return nil, io.ErrUnexpectedEOF } v := valToPointer(reflect.New(sub.typ)) err := sub.unmarshal(v, b[:x]) if err != nil { if r, ok := err.(*RequiredNotSetError); ok { r.field = name + "." + r.field } else { return nil, err } } f.appendPointer(v) return b[y:], err } } func makeUnmarshalMap(f *reflect.StructField) unmarshaler { t := f.Type kt := t.Key() vt := t.Elem() tagArray := strings.Split(f.Tag.Get("protobuf"), ",") valTags := strings.Split(f.Tag.Get("protobuf_val"), ",") for _, t := range tagArray { if strings.HasPrefix(t, "customtype=") { valTags = append(valTags, t) } if t == "stdtime" { valTags = append(valTags, t) } if t == "stdduration" { valTags = append(valTags, t) } if t == "wktptr" { valTags = append(valTags, t) } } unmarshalKey := typeUnmarshaler(kt, f.Tag.Get("protobuf_key")) unmarshalVal := typeUnmarshaler(vt, strings.Join(valTags, ",")) return func(b []byte, f pointer, w int) ([]byte, error) { // The map entry is a submessage. Figure out how big it is. if w != WireBytes { return nil, fmt.Errorf("proto: bad wiretype for map field: got %d want %d", w, WireBytes) } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } r := b[x:] // unused data to return b = b[:x] // data for map entry // Note: we could use #keys * #values ~= 200 functions // to do map decoding without reflection. Probably not worth it. // Maps will be somewhat slow. Oh well. // Read key and value from data. var nerr nonFatal k := reflect.New(kt) v := reflect.New(vt) for len(b) > 0 { x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } wire := int(x) & 7 b = b[n:] var err error switch x >> 3 { case 1: b, err = unmarshalKey(b, valToPointer(k), wire) case 2: b, err = unmarshalVal(b, valToPointer(v), wire) default: err = errInternalBadWireType // skip unknown tag } if nerr.Merge(err) { continue } if err != errInternalBadWireType { return nil, err } // Skip past unknown fields. b, err = skipField(b, wire) if err != nil { return nil, err } } // Get map, allocate if needed. m := f.asPointerTo(t).Elem() // an addressable map[K]T if m.IsNil() { m.Set(reflect.MakeMap(t)) } // Insert into map. m.SetMapIndex(k.Elem(), v.Elem()) return r, nerr.E } } // makeUnmarshalOneof makes an unmarshaler for oneof fields. // for: // message Msg { // oneof F { // int64 X = 1; // float64 Y = 2; // } // } // typ is the type of the concrete entry for a oneof case (e.g. Msg_X). // ityp is the interface type of the oneof field (e.g. isMsg_F). // unmarshal is the unmarshaler for the base type of the oneof case (e.g. int64). // Note that this function will be called once for each case in the oneof. func makeUnmarshalOneof(typ, ityp reflect.Type, unmarshal unmarshaler) unmarshaler { sf := typ.Field(0) field0 := toField(&sf) return func(b []byte, f pointer, w int) ([]byte, error) { // Allocate holder for value. v := reflect.New(typ) // Unmarshal data into holder. // We unmarshal into the first field of the holder object. var err error var nerr nonFatal b, err = unmarshal(b, valToPointer(v).offset(field0), w) if !nerr.Merge(err) { return nil, err } // Write pointer to holder into target field. f.asPointerTo(ityp).Elem().Set(v) return b, nerr.E } } // Error used by decode internally. var errInternalBadWireType = errors.New("proto: internal error: bad wiretype") // skipField skips past a field of type wire and returns the remaining bytes. func skipField(b []byte, wire int) ([]byte, error) { switch wire { case WireVarint: _, k := decodeVarint(b) if k == 0 { return b, io.ErrUnexpectedEOF } b = b[k:] case WireFixed32: if len(b) < 4 { return b, io.ErrUnexpectedEOF } b = b[4:] case WireFixed64: if len(b) < 8 { return b, io.ErrUnexpectedEOF } b = b[8:] case WireBytes: m, k := decodeVarint(b) if k == 0 || uint64(len(b)-k) < m { return b, io.ErrUnexpectedEOF } b = b[uint64(k)+m:] case WireStartGroup: _, i := findEndGroup(b) if i == -1 { return b, io.ErrUnexpectedEOF } b = b[i:] default: return b, fmt.Errorf("proto: can't skip unknown wire type %d", wire) } return b, nil } // findEndGroup finds the index of the next EndGroup tag. // Groups may be nested, so the "next" EndGroup tag is the first // unpaired EndGroup. // findEndGroup returns the indexes of the start and end of the EndGroup tag. // Returns (-1,-1) if it can't find one. func findEndGroup(b []byte) (int, int) { depth := 1 i := 0 for { x, n := decodeVarint(b[i:]) if n == 0 { return -1, -1 } j := i i += n switch x & 7 { case WireVarint: _, k := decodeVarint(b[i:]) if k == 0 { return -1, -1 } i += k case WireFixed32: if len(b)-4 < i { return -1, -1 } i += 4 case WireFixed64: if len(b)-8 < i { return -1, -1 } i += 8 case WireBytes: m, k := decodeVarint(b[i:]) if k == 0 { return -1, -1 } i += k if uint64(len(b)-i) < m { return -1, -1 } i += int(m) case WireStartGroup: depth++ case WireEndGroup: depth-- if depth == 0 { return j, i } default: return -1, -1 } } } // encodeVarint appends a varint-encoded integer to b and returns the result. func encodeVarint(b []byte, x uint64) []byte { for x >= 1<<7 { b = append(b, byte(x&0x7f|0x80)) x >>= 7 } return append(b, byte(x)) } // decodeVarint reads a varint-encoded integer from b. // Returns the decoded integer and the number of bytes read. // If there is an error, it returns 0,0. func decodeVarint(b []byte) (uint64, int) { var x, y uint64 if len(b) == 0 { goto bad } x = uint64(b[0]) if x < 0x80 { return x, 1 } x -= 0x80 if len(b) <= 1 { goto bad } y = uint64(b[1]) x += y << 7 if y < 0x80 { return x, 2 } x -= 0x80 << 7 if len(b) <= 2 { goto bad } y = uint64(b[2]) x += y << 14 if y < 0x80 { return x, 3 } x -= 0x80 << 14 if len(b) <= 3 { goto bad } y = uint64(b[3]) x += y << 21 if y < 0x80 { return x, 4 } x -= 0x80 << 21 if len(b) <= 4 { goto bad } y = uint64(b[4]) x += y << 28 if y < 0x80 { return x, 5 } x -= 0x80 << 28 if len(b) <= 5 { goto bad } y = uint64(b[5]) x += y << 35 if y < 0x80 { return x, 6 } x -= 0x80 << 35 if len(b) <= 6 { goto bad } y = uint64(b[6]) x += y << 42 if y < 0x80 { return x, 7 } x -= 0x80 << 42 if len(b) <= 7 { goto bad } y = uint64(b[7]) x += y << 49 if y < 0x80 { return x, 8 } x -= 0x80 << 49 if len(b) <= 8 { goto bad } y = uint64(b[8]) x += y << 56 if y < 0x80 { return x, 9 } x -= 0x80 << 56 if len(b) <= 9 { goto bad } y = uint64(b[9]) x += y << 63 if y < 2 { return x, 10 } bad: return 0, 0 } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/table_unmarshal_gogo.go ================================================ // Protocol Buffers for Go with Gadgets // // Copyright (c) 2018, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto import ( "io" "reflect" ) func makeUnmarshalMessage(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } // First read the message field to see if something is there. // The semantics of multiple submessages are weird. Instead of // the last one winning (as it is for all other fields), multiple // submessages are merged. v := f // gogo: changed from v := f.getPointer() if v.isNil() { v = valToPointer(reflect.New(sub.typ)) f.setPointer(v) } err := sub.unmarshal(v, b[:x]) if err != nil { if r, ok := err.(*RequiredNotSetError); ok { r.field = name + "." + r.field } else { return nil, err } } return b[x:], err } } func makeUnmarshalMessageSlice(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } v := valToPointer(reflect.New(sub.typ)) err := sub.unmarshal(v, b[:x]) if err != nil { if r, ok := err.(*RequiredNotSetError); ok { r.field = name + "." + r.field } else { return nil, err } } f.appendRef(v, sub.typ) // gogo: changed from f.appendPointer(v) return b[x:], err } } func makeUnmarshalCustomPtr(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } s := f.asPointerTo(reflect.PtrTo(sub.typ)).Elem() s.Set(reflect.New(sub.typ)) m := s.Interface().(custom) if err := m.Unmarshal(b[:x]); err != nil { return nil, err } return b[x:], nil } } func makeUnmarshalCustomSlice(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := reflect.New(sub.typ) c := m.Interface().(custom) if err := c.Unmarshal(b[:x]); err != nil { return nil, err } v := valToPointer(m) f.appendRef(v, sub.typ) return b[x:], nil } } func makeUnmarshalCustom(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := f.asPointerTo(sub.typ).Interface().(custom) if err := m.Unmarshal(b[:x]); err != nil { return nil, err } return b[x:], nil } } func makeUnmarshalTime(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := ×tamp{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } t, err := timestampFromProto(m) if err != nil { return nil, err } s := f.asPointerTo(sub.typ).Elem() s.Set(reflect.ValueOf(t)) return b[x:], nil } } func makeUnmarshalTimePtr(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := ×tamp{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } t, err := timestampFromProto(m) if err != nil { return nil, err } s := f.asPointerTo(reflect.PtrTo(sub.typ)).Elem() s.Set(reflect.ValueOf(&t)) return b[x:], nil } } func makeUnmarshalTimePtrSlice(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := ×tamp{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } t, err := timestampFromProto(m) if err != nil { return nil, err } slice := f.getSlice(reflect.PtrTo(sub.typ)) newSlice := reflect.Append(slice, reflect.ValueOf(&t)) slice.Set(newSlice) return b[x:], nil } } func makeUnmarshalTimeSlice(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := ×tamp{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } t, err := timestampFromProto(m) if err != nil { return nil, err } slice := f.getSlice(sub.typ) newSlice := reflect.Append(slice, reflect.ValueOf(t)) slice.Set(newSlice) return b[x:], nil } } func makeUnmarshalDurationPtr(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &duration{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } d, err := durationFromProto(m) if err != nil { return nil, err } s := f.asPointerTo(reflect.PtrTo(sub.typ)).Elem() s.Set(reflect.ValueOf(&d)) return b[x:], nil } } func makeUnmarshalDuration(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &duration{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } d, err := durationFromProto(m) if err != nil { return nil, err } s := f.asPointerTo(sub.typ).Elem() s.Set(reflect.ValueOf(d)) return b[x:], nil } } func makeUnmarshalDurationPtrSlice(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &duration{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } d, err := durationFromProto(m) if err != nil { return nil, err } slice := f.getSlice(reflect.PtrTo(sub.typ)) newSlice := reflect.Append(slice, reflect.ValueOf(&d)) slice.Set(newSlice) return b[x:], nil } } func makeUnmarshalDurationSlice(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &duration{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } d, err := durationFromProto(m) if err != nil { return nil, err } slice := f.getSlice(sub.typ) newSlice := reflect.Append(slice, reflect.ValueOf(d)) slice.Set(newSlice) return b[x:], nil } } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/text.go ================================================ // Protocol Buffers for Go with Gadgets // // Copyright (c) 2013, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // // Go support for Protocol Buffers - Google's data interchange format // // Copyright 2010 The Go Authors. All rights reserved. // https://github.com/golang/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto // Functions for writing the text protocol buffer format. import ( "bufio" "bytes" "encoding" "errors" "fmt" "io" "log" "math" "reflect" "sort" "strings" "sync" "time" ) var ( newline = []byte("\n") spaces = []byte(" ") endBraceNewline = []byte("}\n") backslashN = []byte{'\\', 'n'} backslashR = []byte{'\\', 'r'} backslashT = []byte{'\\', 't'} backslashDQ = []byte{'\\', '"'} backslashBS = []byte{'\\', '\\'} posInf = []byte("inf") negInf = []byte("-inf") nan = []byte("nan") ) type writer interface { io.Writer WriteByte(byte) error } // textWriter is an io.Writer that tracks its indentation level. type textWriter struct { ind int complete bool // if the current position is a complete line compact bool // whether to write out as a one-liner w writer } func (w *textWriter) WriteString(s string) (n int, err error) { if !strings.Contains(s, "\n") { if !w.compact && w.complete { w.writeIndent() } w.complete = false return io.WriteString(w.w, s) } // WriteString is typically called without newlines, so this // codepath and its copy are rare. We copy to avoid // duplicating all of Write's logic here. return w.Write([]byte(s)) } func (w *textWriter) Write(p []byte) (n int, err error) { newlines := bytes.Count(p, newline) if newlines == 0 { if !w.compact && w.complete { w.writeIndent() } n, err = w.w.Write(p) w.complete = false return n, err } frags := bytes.SplitN(p, newline, newlines+1) if w.compact { for i, frag := range frags { if i > 0 { if err := w.w.WriteByte(' '); err != nil { return n, err } n++ } nn, err := w.w.Write(frag) n += nn if err != nil { return n, err } } return n, nil } for i, frag := range frags { if w.complete { w.writeIndent() } nn, err := w.w.Write(frag) n += nn if err != nil { return n, err } if i+1 < len(frags) { if err := w.w.WriteByte('\n'); err != nil { return n, err } n++ } } w.complete = len(frags[len(frags)-1]) == 0 return n, nil } func (w *textWriter) WriteByte(c byte) error { if w.compact && c == '\n' { c = ' ' } if !w.compact && w.complete { w.writeIndent() } err := w.w.WriteByte(c) w.complete = c == '\n' return err } func (w *textWriter) indent() { w.ind++ } func (w *textWriter) unindent() { if w.ind == 0 { log.Print("proto: textWriter unindented too far") return } w.ind-- } func writeName(w *textWriter, props *Properties) error { if _, err := w.WriteString(props.OrigName); err != nil { return err } if props.Wire != "group" { return w.WriteByte(':') } return nil } func requiresQuotes(u string) bool { // When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted. for _, ch := range u { switch { case ch == '.' || ch == '/' || ch == '_': continue case '0' <= ch && ch <= '9': continue case 'A' <= ch && ch <= 'Z': continue case 'a' <= ch && ch <= 'z': continue default: return true } } return false } // isAny reports whether sv is a google.protobuf.Any message func isAny(sv reflect.Value) bool { type wkt interface { XXX_WellKnownType() string } t, ok := sv.Addr().Interface().(wkt) return ok && t.XXX_WellKnownType() == "Any" } // writeProto3Any writes an expanded google.protobuf.Any message. // // It returns (false, nil) if sv value can't be unmarshaled (e.g. because // required messages are not linked in). // // It returns (true, error) when sv was written in expanded format or an error // was encountered. func (tm *TextMarshaler) writeProto3Any(w *textWriter, sv reflect.Value) (bool, error) { turl := sv.FieldByName("TypeUrl") val := sv.FieldByName("Value") if !turl.IsValid() || !val.IsValid() { return true, errors.New("proto: invalid google.protobuf.Any message") } b, ok := val.Interface().([]byte) if !ok { return true, errors.New("proto: invalid google.protobuf.Any message") } parts := strings.Split(turl.String(), "/") mt := MessageType(parts[len(parts)-1]) if mt == nil { return false, nil } m := reflect.New(mt.Elem()) if err := Unmarshal(b, m.Interface().(Message)); err != nil { return false, nil } w.Write([]byte("[")) u := turl.String() if requiresQuotes(u) { writeString(w, u) } else { w.Write([]byte(u)) } if w.compact { w.Write([]byte("]:<")) } else { w.Write([]byte("]: <\n")) w.ind++ } if err := tm.writeStruct(w, m.Elem()); err != nil { return true, err } if w.compact { w.Write([]byte("> ")) } else { w.ind-- w.Write([]byte(">\n")) } return true, nil } func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error { if tm.ExpandAny && isAny(sv) { if canExpand, err := tm.writeProto3Any(w, sv); canExpand { return err } } st := sv.Type() sprops := GetProperties(st) for i := 0; i < sv.NumField(); i++ { fv := sv.Field(i) props := sprops.Prop[i] name := st.Field(i).Name if name == "XXX_NoUnkeyedLiteral" { continue } if strings.HasPrefix(name, "XXX_") { // There are two XXX_ fields: // XXX_unrecognized []byte // XXX_extensions map[int32]proto.Extension // The first is handled here; // the second is handled at the bottom of this function. if name == "XXX_unrecognized" && !fv.IsNil() { if err := writeUnknownStruct(w, fv.Interface().([]byte)); err != nil { return err } } continue } if fv.Kind() == reflect.Ptr && fv.IsNil() { // Field not filled in. This could be an optional field or // a required field that wasn't filled in. Either way, there // isn't anything we can show for it. continue } if fv.Kind() == reflect.Slice && fv.IsNil() { // Repeated field that is empty, or a bytes field that is unused. continue } if props.Repeated && fv.Kind() == reflect.Slice { // Repeated field. for j := 0; j < fv.Len(); j++ { if err := writeName(w, props); err != nil { return err } if !w.compact { if err := w.WriteByte(' '); err != nil { return err } } v := fv.Index(j) if v.Kind() == reflect.Ptr && v.IsNil() { // A nil message in a repeated field is not valid, // but we can handle that more gracefully than panicking. if _, err := w.Write([]byte("\n")); err != nil { return err } continue } if len(props.Enum) > 0 { if err := tm.writeEnum(w, v, props); err != nil { return err } } else if err := tm.writeAny(w, v, props); err != nil { return err } if err := w.WriteByte('\n'); err != nil { return err } } continue } if fv.Kind() == reflect.Map { // Map fields are rendered as a repeated struct with key/value fields. keys := fv.MapKeys() sort.Sort(mapKeys(keys)) for _, key := range keys { val := fv.MapIndex(key) if err := writeName(w, props); err != nil { return err } if !w.compact { if err := w.WriteByte(' '); err != nil { return err } } // open struct if err := w.WriteByte('<'); err != nil { return err } if !w.compact { if err := w.WriteByte('\n'); err != nil { return err } } w.indent() // key if _, err := w.WriteString("key:"); err != nil { return err } if !w.compact { if err := w.WriteByte(' '); err != nil { return err } } if err := tm.writeAny(w, key, props.MapKeyProp); err != nil { return err } if err := w.WriteByte('\n'); err != nil { return err } // nil values aren't legal, but we can avoid panicking because of them. if val.Kind() != reflect.Ptr || !val.IsNil() { // value if _, err := w.WriteString("value:"); err != nil { return err } if !w.compact { if err := w.WriteByte(' '); err != nil { return err } } if err := tm.writeAny(w, val, props.MapValProp); err != nil { return err } if err := w.WriteByte('\n'); err != nil { return err } } // close struct w.unindent() if err := w.WriteByte('>'); err != nil { return err } if err := w.WriteByte('\n'); err != nil { return err } } continue } if props.proto3 && fv.Kind() == reflect.Slice && fv.Len() == 0 { // empty bytes field continue } if props.proto3 && fv.Kind() != reflect.Ptr && fv.Kind() != reflect.Slice { // proto3 non-repeated scalar field; skip if zero value if isProto3Zero(fv) { continue } } if fv.Kind() == reflect.Interface { // Check if it is a oneof. if st.Field(i).Tag.Get("protobuf_oneof") != "" { // fv is nil, or holds a pointer to generated struct. // That generated struct has exactly one field, // which has a protobuf struct tag. if fv.IsNil() { continue } inner := fv.Elem().Elem() // interface -> *T -> T tag := inner.Type().Field(0).Tag.Get("protobuf") props = new(Properties) // Overwrite the outer props var, but not its pointee. props.Parse(tag) // Write the value in the oneof, not the oneof itself. fv = inner.Field(0) // Special case to cope with malformed messages gracefully: // If the value in the oneof is a nil pointer, don't panic // in writeAny. if fv.Kind() == reflect.Ptr && fv.IsNil() { // Use errors.New so writeAny won't render quotes. msg := errors.New("/* nil */") fv = reflect.ValueOf(&msg).Elem() } } } if err := writeName(w, props); err != nil { return err } if !w.compact { if err := w.WriteByte(' '); err != nil { return err } } if len(props.Enum) > 0 { if err := tm.writeEnum(w, fv, props); err != nil { return err } } else if err := tm.writeAny(w, fv, props); err != nil { return err } if err := w.WriteByte('\n'); err != nil { return err } } // Extensions (the XXX_extensions field). pv := sv if pv.CanAddr() { pv = sv.Addr() } else { pv = reflect.New(sv.Type()) pv.Elem().Set(sv) } if _, err := extendable(pv.Interface()); err == nil { if err := tm.writeExtensions(w, pv); err != nil { return err } } return nil } var textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() // writeAny writes an arbitrary field. func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error { v = reflect.Indirect(v) if props != nil { if len(props.CustomType) > 0 { custom, ok := v.Interface().(Marshaler) if ok { data, err := custom.Marshal() if err != nil { return err } if err := writeString(w, string(data)); err != nil { return err } return nil } } else if len(props.CastType) > 0 { if _, ok := v.Interface().(interface { String() string }); ok { switch v.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: _, err := fmt.Fprintf(w, "%d", v.Interface()) return err } } } else if props.StdTime { t, ok := v.Interface().(time.Time) if !ok { return fmt.Errorf("stdtime is not time.Time, but %T", v.Interface()) } tproto, err := timestampProto(t) if err != nil { return err } propsCopy := *props // Make a copy so that this is goroutine-safe propsCopy.StdTime = false err = tm.writeAny(w, reflect.ValueOf(tproto), &propsCopy) return err } else if props.StdDuration { d, ok := v.Interface().(time.Duration) if !ok { return fmt.Errorf("stdtime is not time.Duration, but %T", v.Interface()) } dproto := durationProto(d) propsCopy := *props // Make a copy so that this is goroutine-safe propsCopy.StdDuration = false err := tm.writeAny(w, reflect.ValueOf(dproto), &propsCopy) return err } } // Floats have special cases. if v.Kind() == reflect.Float32 || v.Kind() == reflect.Float64 { x := v.Float() var b []byte switch { case math.IsInf(x, 1): b = posInf case math.IsInf(x, -1): b = negInf case math.IsNaN(x): b = nan } if b != nil { _, err := w.Write(b) return err } // Other values are handled below. } // We don't attempt to serialise every possible value type; only those // that can occur in protocol buffers. switch v.Kind() { case reflect.Slice: // Should only be a []byte; repeated fields are handled in writeStruct. if err := writeString(w, string(v.Bytes())); err != nil { return err } case reflect.String: if err := writeString(w, v.String()); err != nil { return err } case reflect.Struct: // Required/optional group/message. var bra, ket byte = '<', '>' if props != nil && props.Wire == "group" { bra, ket = '{', '}' } if err := w.WriteByte(bra); err != nil { return err } if !w.compact { if err := w.WriteByte('\n'); err != nil { return err } } w.indent() if v.CanAddr() { // Calling v.Interface on a struct causes the reflect package to // copy the entire struct. This is racy with the new Marshaler // since we atomically update the XXX_sizecache. // // Thus, we retrieve a pointer to the struct if possible to avoid // a race since v.Interface on the pointer doesn't copy the struct. // // If v is not addressable, then we are not worried about a race // since it implies that the binary Marshaler cannot possibly be // mutating this value. v = v.Addr() } if v.Type().Implements(textMarshalerType) { text, err := v.Interface().(encoding.TextMarshaler).MarshalText() if err != nil { return err } if _, err = w.Write(text); err != nil { return err } } else { if v.Kind() == reflect.Ptr { v = v.Elem() } if err := tm.writeStruct(w, v); err != nil { return err } } w.unindent() if err := w.WriteByte(ket); err != nil { return err } default: _, err := fmt.Fprint(w, v.Interface()) return err } return nil } // equivalent to C's isprint. func isprint(c byte) bool { return c >= 0x20 && c < 0x7f } // writeString writes a string in the protocol buffer text format. // It is similar to strconv.Quote except we don't use Go escape sequences, // we treat the string as a byte sequence, and we use octal escapes. // These differences are to maintain interoperability with the other // languages' implementations of the text format. func writeString(w *textWriter, s string) error { // use WriteByte here to get any needed indent if err := w.WriteByte('"'); err != nil { return err } // Loop over the bytes, not the runes. for i := 0; i < len(s); i++ { var err error // Divergence from C++: we don't escape apostrophes. // There's no need to escape them, and the C++ parser // copes with a naked apostrophe. switch c := s[i]; c { case '\n': _, err = w.w.Write(backslashN) case '\r': _, err = w.w.Write(backslashR) case '\t': _, err = w.w.Write(backslashT) case '"': _, err = w.w.Write(backslashDQ) case '\\': _, err = w.w.Write(backslashBS) default: if isprint(c) { err = w.w.WriteByte(c) } else { _, err = fmt.Fprintf(w.w, "\\%03o", c) } } if err != nil { return err } } return w.WriteByte('"') } func writeUnknownStruct(w *textWriter, data []byte) (err error) { if !w.compact { if _, err := fmt.Fprintf(w, "/* %d unknown bytes */\n", len(data)); err != nil { return err } } b := NewBuffer(data) for b.index < len(b.buf) { x, err := b.DecodeVarint() if err != nil { _, ferr := fmt.Fprintf(w, "/* %v */\n", err) return ferr } wire, tag := x&7, x>>3 if wire == WireEndGroup { w.unindent() if _, werr := w.Write(endBraceNewline); werr != nil { return werr } continue } if _, ferr := fmt.Fprint(w, tag); ferr != nil { return ferr } if wire != WireStartGroup { if err = w.WriteByte(':'); err != nil { return err } } if !w.compact || wire == WireStartGroup { if err = w.WriteByte(' '); err != nil { return err } } switch wire { case WireBytes: buf, e := b.DecodeRawBytes(false) if e == nil { _, err = fmt.Fprintf(w, "%q", buf) } else { _, err = fmt.Fprintf(w, "/* %v */", e) } case WireFixed32: x, err = b.DecodeFixed32() err = writeUnknownInt(w, x, err) case WireFixed64: x, err = b.DecodeFixed64() err = writeUnknownInt(w, x, err) case WireStartGroup: err = w.WriteByte('{') w.indent() case WireVarint: x, err = b.DecodeVarint() err = writeUnknownInt(w, x, err) default: _, err = fmt.Fprintf(w, "/* unknown wire type %d */", wire) } if err != nil { return err } if err := w.WriteByte('\n'); err != nil { return err } } return nil } func writeUnknownInt(w *textWriter, x uint64, err error) error { if err == nil { _, err = fmt.Fprint(w, x) } else { _, err = fmt.Fprintf(w, "/* %v */", err) } return err } type int32Slice []int32 func (s int32Slice) Len() int { return len(s) } func (s int32Slice) Less(i, j int) bool { return s[i] < s[j] } func (s int32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } // writeExtensions writes all the extensions in pv. // pv is assumed to be a pointer to a protocol message struct that is extendable. func (tm *TextMarshaler) writeExtensions(w *textWriter, pv reflect.Value) error { emap := extensionMaps[pv.Type().Elem()] e := pv.Interface().(Message) var m map[int32]Extension var mu sync.Locker if em, ok := e.(extensionsBytes); ok { eb := em.GetExtensions() var err error m, err = BytesToExtensionsMap(*eb) if err != nil { return err } mu = notLocker{} } else if _, ok := e.(extendableProto); ok { ep, _ := extendable(e) m, mu = ep.extensionsRead() if m == nil { return nil } } // Order the extensions by ID. // This isn't strictly necessary, but it will give us // canonical output, which will also make testing easier. mu.Lock() ids := make([]int32, 0, len(m)) for id := range m { ids = append(ids, id) } sort.Sort(int32Slice(ids)) mu.Unlock() for _, extNum := range ids { ext := m[extNum] var desc *ExtensionDesc if emap != nil { desc = emap[extNum] } if desc == nil { // Unknown extension. if err := writeUnknownStruct(w, ext.enc); err != nil { return err } continue } pb, err := GetExtension(e, desc) if err != nil { return fmt.Errorf("failed getting extension: %v", err) } // Repeated extensions will appear as a slice. if !desc.repeated() { if err := tm.writeExtension(w, desc.Name, pb); err != nil { return err } } else { v := reflect.ValueOf(pb) for i := 0; i < v.Len(); i++ { if err := tm.writeExtension(w, desc.Name, v.Index(i).Interface()); err != nil { return err } } } } return nil } func (tm *TextMarshaler) writeExtension(w *textWriter, name string, pb interface{}) error { if _, err := fmt.Fprintf(w, "[%s]:", name); err != nil { return err } if !w.compact { if err := w.WriteByte(' '); err != nil { return err } } if err := tm.writeAny(w, reflect.ValueOf(pb), nil); err != nil { return err } if err := w.WriteByte('\n'); err != nil { return err } return nil } func (w *textWriter) writeIndent() { if !w.complete { return } remain := w.ind * 2 for remain > 0 { n := remain if n > len(spaces) { n = len(spaces) } w.w.Write(spaces[:n]) remain -= n } w.complete = false } // TextMarshaler is a configurable text format marshaler. type TextMarshaler struct { Compact bool // use compact text format (one line). ExpandAny bool // expand google.protobuf.Any messages of known types } // Marshal writes a given protocol buffer in text format. // The only errors returned are from w. func (tm *TextMarshaler) Marshal(w io.Writer, pb Message) error { val := reflect.ValueOf(pb) if pb == nil || val.IsNil() { w.Write([]byte("")) return nil } var bw *bufio.Writer ww, ok := w.(writer) if !ok { bw = bufio.NewWriter(w) ww = bw } aw := &textWriter{ w: ww, complete: true, compact: tm.Compact, } if etm, ok := pb.(encoding.TextMarshaler); ok { text, err := etm.MarshalText() if err != nil { return err } if _, err = aw.Write(text); err != nil { return err } if bw != nil { return bw.Flush() } return nil } // Dereference the received pointer so we don't have outer < and >. v := reflect.Indirect(val) if err := tm.writeStruct(aw, v); err != nil { return err } if bw != nil { return bw.Flush() } return nil } // Text is the same as Marshal, but returns the string directly. func (tm *TextMarshaler) Text(pb Message) string { var buf bytes.Buffer tm.Marshal(&buf, pb) return buf.String() } var ( defaultTextMarshaler = TextMarshaler{} compactTextMarshaler = TextMarshaler{Compact: true} ) // TODO: consider removing some of the Marshal functions below. // MarshalText writes a given protocol buffer in text format. // The only errors returned are from w. func MarshalText(w io.Writer, pb Message) error { return defaultTextMarshaler.Marshal(w, pb) } // MarshalTextString is the same as MarshalText, but returns the string directly. func MarshalTextString(pb Message) string { return defaultTextMarshaler.Text(pb) } // CompactText writes a given protocol buffer in compact text format (one line). func CompactText(w io.Writer, pb Message) error { return compactTextMarshaler.Marshal(w, pb) } // CompactTextString is the same as CompactText, but returns the string directly. func CompactTextString(pb Message) string { return compactTextMarshaler.Text(pb) } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/text_gogo.go ================================================ // Protocol Buffers for Go with Gadgets // // Copyright (c) 2013, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto import ( "fmt" "reflect" ) func (tm *TextMarshaler) writeEnum(w *textWriter, v reflect.Value, props *Properties) error { m, ok := enumStringMaps[props.Enum] if !ok { if err := tm.writeAny(w, v, props); err != nil { return err } } key := int32(0) if v.Kind() == reflect.Ptr { key = int32(v.Elem().Int()) } else { key = int32(v.Int()) } s, ok := m[key] if !ok { if err := tm.writeAny(w, v, props); err != nil { return err } } _, err := fmt.Fprint(w, s) return err } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/text_parser.go ================================================ // Protocol Buffers for Go with Gadgets // // Copyright (c) 2013, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // // Go support for Protocol Buffers - Google's data interchange format // // Copyright 2010 The Go Authors. All rights reserved. // https://github.com/golang/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto // Functions for parsing the Text protocol buffer format. // TODO: message sets. import ( "encoding" "errors" "fmt" "reflect" "strconv" "strings" "time" "unicode/utf8" ) // Error string emitted when deserializing Any and fields are already set const anyRepeatedlyUnpacked = "Any message unpacked multiple times, or %q already set" type ParseError struct { Message string Line int // 1-based line number Offset int // 0-based byte offset from start of input } func (p *ParseError) Error() string { if p.Line == 1 { // show offset only for first line return fmt.Sprintf("line 1.%d: %v", p.Offset, p.Message) } return fmt.Sprintf("line %d: %v", p.Line, p.Message) } type token struct { value string err *ParseError line int // line number offset int // byte number from start of input, not start of line unquoted string // the unquoted version of value, if it was a quoted string } func (t *token) String() string { if t.err == nil { return fmt.Sprintf("%q (line=%d, offset=%d)", t.value, t.line, t.offset) } return fmt.Sprintf("parse error: %v", t.err) } type textParser struct { s string // remaining input done bool // whether the parsing is finished (success or error) backed bool // whether back() was called offset, line int cur token } func newTextParser(s string) *textParser { p := new(textParser) p.s = s p.line = 1 p.cur.line = 1 return p } func (p *textParser) errorf(format string, a ...interface{}) *ParseError { pe := &ParseError{fmt.Sprintf(format, a...), p.cur.line, p.cur.offset} p.cur.err = pe p.done = true return pe } // Numbers and identifiers are matched by [-+._A-Za-z0-9] func isIdentOrNumberChar(c byte) bool { switch { case 'A' <= c && c <= 'Z', 'a' <= c && c <= 'z': return true case '0' <= c && c <= '9': return true } switch c { case '-', '+', '.', '_': return true } return false } func isWhitespace(c byte) bool { switch c { case ' ', '\t', '\n', '\r': return true } return false } func isQuote(c byte) bool { switch c { case '"', '\'': return true } return false } func (p *textParser) skipWhitespace() { i := 0 for i < len(p.s) && (isWhitespace(p.s[i]) || p.s[i] == '#') { if p.s[i] == '#' { // comment; skip to end of line or input for i < len(p.s) && p.s[i] != '\n' { i++ } if i == len(p.s) { break } } if p.s[i] == '\n' { p.line++ } i++ } p.offset += i p.s = p.s[i:len(p.s)] if len(p.s) == 0 { p.done = true } } func (p *textParser) advance() { // Skip whitespace p.skipWhitespace() if p.done { return } // Start of non-whitespace p.cur.err = nil p.cur.offset, p.cur.line = p.offset, p.line p.cur.unquoted = "" switch p.s[0] { case '<', '>', '{', '}', ':', '[', ']', ';', ',', '/': // Single symbol p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)] case '"', '\'': // Quoted string i := 1 for i < len(p.s) && p.s[i] != p.s[0] && p.s[i] != '\n' { if p.s[i] == '\\' && i+1 < len(p.s) { // skip escaped char i++ } i++ } if i >= len(p.s) || p.s[i] != p.s[0] { p.errorf("unmatched quote") return } unq, err := unquoteC(p.s[1:i], rune(p.s[0])) if err != nil { p.errorf("invalid quoted string %s: %v", p.s[0:i+1], err) return } p.cur.value, p.s = p.s[0:i+1], p.s[i+1:len(p.s)] p.cur.unquoted = unq default: i := 0 for i < len(p.s) && isIdentOrNumberChar(p.s[i]) { i++ } if i == 0 { p.errorf("unexpected byte %#x", p.s[0]) return } p.cur.value, p.s = p.s[0:i], p.s[i:len(p.s)] } p.offset += len(p.cur.value) } var ( errBadUTF8 = errors.New("proto: bad UTF-8") ) func unquoteC(s string, quote rune) (string, error) { // This is based on C++'s tokenizer.cc. // Despite its name, this is *not* parsing C syntax. // For instance, "\0" is an invalid quoted string. // Avoid allocation in trivial cases. simple := true for _, r := range s { if r == '\\' || r == quote { simple = false break } } if simple { return s, nil } buf := make([]byte, 0, 3*len(s)/2) for len(s) > 0 { r, n := utf8.DecodeRuneInString(s) if r == utf8.RuneError && n == 1 { return "", errBadUTF8 } s = s[n:] if r != '\\' { if r < utf8.RuneSelf { buf = append(buf, byte(r)) } else { buf = append(buf, string(r)...) } continue } ch, tail, err := unescape(s) if err != nil { return "", err } buf = append(buf, ch...) s = tail } return string(buf), nil } func unescape(s string) (ch string, tail string, err error) { r, n := utf8.DecodeRuneInString(s) if r == utf8.RuneError && n == 1 { return "", "", errBadUTF8 } s = s[n:] switch r { case 'a': return "\a", s, nil case 'b': return "\b", s, nil case 'f': return "\f", s, nil case 'n': return "\n", s, nil case 'r': return "\r", s, nil case 't': return "\t", s, nil case 'v': return "\v", s, nil case '?': return "?", s, nil // trigraph workaround case '\'', '"', '\\': return string(r), s, nil case '0', '1', '2', '3', '4', '5', '6', '7': if len(s) < 2 { return "", "", fmt.Errorf(`\%c requires 2 following digits`, r) } ss := string(r) + s[:2] s = s[2:] i, err := strconv.ParseUint(ss, 8, 8) if err != nil { return "", "", fmt.Errorf(`\%s contains non-octal digits`, ss) } return string([]byte{byte(i)}), s, nil case 'x', 'X', 'u', 'U': var n int switch r { case 'x', 'X': n = 2 case 'u': n = 4 case 'U': n = 8 } if len(s) < n { return "", "", fmt.Errorf(`\%c requires %d following digits`, r, n) } ss := s[:n] s = s[n:] i, err := strconv.ParseUint(ss, 16, 64) if err != nil { return "", "", fmt.Errorf(`\%c%s contains non-hexadecimal digits`, r, ss) } if r == 'x' || r == 'X' { return string([]byte{byte(i)}), s, nil } if i > utf8.MaxRune { return "", "", fmt.Errorf(`\%c%s is not a valid Unicode code point`, r, ss) } return string(rune(i)), s, nil } return "", "", fmt.Errorf(`unknown escape \%c`, r) } // Back off the parser by one token. Can only be done between calls to next(). // It makes the next advance() a no-op. func (p *textParser) back() { p.backed = true } // Advances the parser and returns the new current token. func (p *textParser) next() *token { if p.backed || p.done { p.backed = false return &p.cur } p.advance() if p.done { p.cur.value = "" } else if len(p.cur.value) > 0 && isQuote(p.cur.value[0]) { // Look for multiple quoted strings separated by whitespace, // and concatenate them. cat := p.cur for { p.skipWhitespace() if p.done || !isQuote(p.s[0]) { break } p.advance() if p.cur.err != nil { return &p.cur } cat.value += " " + p.cur.value cat.unquoted += p.cur.unquoted } p.done = false // parser may have seen EOF, but we want to return cat p.cur = cat } return &p.cur } func (p *textParser) consumeToken(s string) error { tok := p.next() if tok.err != nil { return tok.err } if tok.value != s { p.back() return p.errorf("expected %q, found %q", s, tok.value) } return nil } // Return a RequiredNotSetError indicating which required field was not set. func (p *textParser) missingRequiredFieldError(sv reflect.Value) *RequiredNotSetError { st := sv.Type() sprops := GetProperties(st) for i := 0; i < st.NumField(); i++ { if !isNil(sv.Field(i)) { continue } props := sprops.Prop[i] if props.Required { return &RequiredNotSetError{fmt.Sprintf("%v.%v", st, props.OrigName)} } } return &RequiredNotSetError{fmt.Sprintf("%v.", st)} // should not happen } // Returns the index in the struct for the named field, as well as the parsed tag properties. func structFieldByName(sprops *StructProperties, name string) (int, *Properties, bool) { i, ok := sprops.decoderOrigNames[name] if ok { return i, sprops.Prop[i], true } return -1, nil, false } // Consume a ':' from the input stream (if the next token is a colon), // returning an error if a colon is needed but not present. func (p *textParser) checkForColon(props *Properties, typ reflect.Type) *ParseError { tok := p.next() if tok.err != nil { return tok.err } if tok.value != ":" { // Colon is optional when the field is a group or message. needColon := true switch props.Wire { case "group": needColon = false case "bytes": // A "bytes" field is either a message, a string, or a repeated field; // those three become *T, *string and []T respectively, so we can check for // this field being a pointer to a non-string. if typ.Kind() == reflect.Ptr { // *T or *string if typ.Elem().Kind() == reflect.String { break } } else if typ.Kind() == reflect.Slice { // []T or []*T if typ.Elem().Kind() != reflect.Ptr { break } } else if typ.Kind() == reflect.String { // The proto3 exception is for a string field, // which requires a colon. break } needColon = false } if needColon { return p.errorf("expected ':', found %q", tok.value) } p.back() } return nil } func (p *textParser) readStruct(sv reflect.Value, terminator string) error { st := sv.Type() sprops := GetProperties(st) reqCount := sprops.reqCount var reqFieldErr error fieldSet := make(map[string]bool) // A struct is a sequence of "name: value", terminated by one of // '>' or '}', or the end of the input. A name may also be // "[extension]" or "[type/url]". // // The whole struct can also be an expanded Any message, like: // [type/url] < ... struct contents ... > for { tok := p.next() if tok.err != nil { return tok.err } if tok.value == terminator { break } if tok.value == "[" { // Looks like an extension or an Any. // // TODO: Check whether we need to handle // namespace rooted names (e.g. ".something.Foo"). extName, err := p.consumeExtName() if err != nil { return err } if s := strings.LastIndex(extName, "/"); s >= 0 { // If it contains a slash, it's an Any type URL. messageName := extName[s+1:] mt := MessageType(messageName) if mt == nil { return p.errorf("unrecognized message %q in google.protobuf.Any", messageName) } tok = p.next() if tok.err != nil { return tok.err } // consume an optional colon if tok.value == ":" { tok = p.next() if tok.err != nil { return tok.err } } var terminator string switch tok.value { case "<": terminator = ">" case "{": terminator = "}" default: return p.errorf("expected '{' or '<', found %q", tok.value) } v := reflect.New(mt.Elem()) if pe := p.readStruct(v.Elem(), terminator); pe != nil { return pe } b, err := Marshal(v.Interface().(Message)) if err != nil { return p.errorf("failed to marshal message of type %q: %v", messageName, err) } if fieldSet["type_url"] { return p.errorf(anyRepeatedlyUnpacked, "type_url") } if fieldSet["value"] { return p.errorf(anyRepeatedlyUnpacked, "value") } sv.FieldByName("TypeUrl").SetString(extName) sv.FieldByName("Value").SetBytes(b) fieldSet["type_url"] = true fieldSet["value"] = true continue } var desc *ExtensionDesc // This could be faster, but it's functional. // TODO: Do something smarter than a linear scan. for _, d := range RegisteredExtensions(reflect.New(st).Interface().(Message)) { if d.Name == extName { desc = d break } } if desc == nil { return p.errorf("unrecognized extension %q", extName) } props := &Properties{} props.Parse(desc.Tag) typ := reflect.TypeOf(desc.ExtensionType) if err := p.checkForColon(props, typ); err != nil { return err } rep := desc.repeated() // Read the extension structure, and set it in // the value we're constructing. var ext reflect.Value if !rep { ext = reflect.New(typ).Elem() } else { ext = reflect.New(typ.Elem()).Elem() } if err := p.readAny(ext, props); err != nil { if _, ok := err.(*RequiredNotSetError); !ok { return err } reqFieldErr = err } ep := sv.Addr().Interface().(Message) if !rep { SetExtension(ep, desc, ext.Interface()) } else { old, err := GetExtension(ep, desc) var sl reflect.Value if err == nil { sl = reflect.ValueOf(old) // existing slice } else { sl = reflect.MakeSlice(typ, 0, 1) } sl = reflect.Append(sl, ext) SetExtension(ep, desc, sl.Interface()) } if err := p.consumeOptionalSeparator(); err != nil { return err } continue } // This is a normal, non-extension field. name := tok.value var dst reflect.Value fi, props, ok := structFieldByName(sprops, name) if ok { dst = sv.Field(fi) } else if oop, ok := sprops.OneofTypes[name]; ok { // It is a oneof. props = oop.Prop nv := reflect.New(oop.Type.Elem()) dst = nv.Elem().Field(0) field := sv.Field(oop.Field) if !field.IsNil() { return p.errorf("field '%s' would overwrite already parsed oneof '%s'", name, sv.Type().Field(oop.Field).Name) } field.Set(nv) } if !dst.IsValid() { return p.errorf("unknown field name %q in %v", name, st) } if dst.Kind() == reflect.Map { // Consume any colon. if err := p.checkForColon(props, dst.Type()); err != nil { return err } // Construct the map if it doesn't already exist. if dst.IsNil() { dst.Set(reflect.MakeMap(dst.Type())) } key := reflect.New(dst.Type().Key()).Elem() val := reflect.New(dst.Type().Elem()).Elem() // The map entry should be this sequence of tokens: // < key : KEY value : VALUE > // However, implementations may omit key or value, and technically // we should support them in any order. See b/28924776 for a time // this went wrong. tok := p.next() var terminator string switch tok.value { case "<": terminator = ">" case "{": terminator = "}" default: return p.errorf("expected '{' or '<', found %q", tok.value) } for { tok := p.next() if tok.err != nil { return tok.err } if tok.value == terminator { break } switch tok.value { case "key": if err := p.consumeToken(":"); err != nil { return err } if err := p.readAny(key, props.MapKeyProp); err != nil { return err } if err := p.consumeOptionalSeparator(); err != nil { return err } case "value": if err := p.checkForColon(props.MapValProp, dst.Type().Elem()); err != nil { return err } if err := p.readAny(val, props.MapValProp); err != nil { return err } if err := p.consumeOptionalSeparator(); err != nil { return err } default: p.back() return p.errorf(`expected "key", "value", or %q, found %q`, terminator, tok.value) } } dst.SetMapIndex(key, val) continue } // Check that it's not already set if it's not a repeated field. if !props.Repeated && fieldSet[name] { return p.errorf("non-repeated field %q was repeated", name) } if err := p.checkForColon(props, dst.Type()); err != nil { return err } // Parse into the field. fieldSet[name] = true if err := p.readAny(dst, props); err != nil { if _, ok := err.(*RequiredNotSetError); !ok { return err } reqFieldErr = err } if props.Required { reqCount-- } if err := p.consumeOptionalSeparator(); err != nil { return err } } if reqCount > 0 { return p.missingRequiredFieldError(sv) } return reqFieldErr } // consumeExtName consumes extension name or expanded Any type URL and the // following ']'. It returns the name or URL consumed. func (p *textParser) consumeExtName() (string, error) { tok := p.next() if tok.err != nil { return "", tok.err } // If extension name or type url is quoted, it's a single token. if len(tok.value) > 2 && isQuote(tok.value[0]) && tok.value[len(tok.value)-1] == tok.value[0] { name, err := unquoteC(tok.value[1:len(tok.value)-1], rune(tok.value[0])) if err != nil { return "", err } return name, p.consumeToken("]") } // Consume everything up to "]" var parts []string for tok.value != "]" { parts = append(parts, tok.value) tok = p.next() if tok.err != nil { return "", p.errorf("unrecognized type_url or extension name: %s", tok.err) } if p.done && tok.value != "]" { return "", p.errorf("unclosed type_url or extension name") } } return strings.Join(parts, ""), nil } // consumeOptionalSeparator consumes an optional semicolon or comma. // It is used in readStruct to provide backward compatibility. func (p *textParser) consumeOptionalSeparator() error { tok := p.next() if tok.err != nil { return tok.err } if tok.value != ";" && tok.value != "," { p.back() } return nil } func (p *textParser) readAny(v reflect.Value, props *Properties) error { tok := p.next() if tok.err != nil { return tok.err } if tok.value == "" { return p.errorf("unexpected EOF") } if len(props.CustomType) > 0 { if props.Repeated { t := reflect.TypeOf(v.Interface()) if t.Kind() == reflect.Slice { tc := reflect.TypeOf(new(Marshaler)) ok := t.Elem().Implements(tc.Elem()) if ok { fv := v flen := fv.Len() if flen == fv.Cap() { nav := reflect.MakeSlice(v.Type(), flen, 2*flen+1) reflect.Copy(nav, fv) fv.Set(nav) } fv.SetLen(flen + 1) // Read one. p.back() return p.readAny(fv.Index(flen), props) } } } if reflect.TypeOf(v.Interface()).Kind() == reflect.Ptr { custom := reflect.New(props.ctype.Elem()).Interface().(Unmarshaler) err := custom.Unmarshal([]byte(tok.unquoted)) if err != nil { return p.errorf("%v %v: %v", err, v.Type(), tok.value) } v.Set(reflect.ValueOf(custom)) } else { custom := reflect.New(reflect.TypeOf(v.Interface())).Interface().(Unmarshaler) err := custom.Unmarshal([]byte(tok.unquoted)) if err != nil { return p.errorf("%v %v: %v", err, v.Type(), tok.value) } v.Set(reflect.Indirect(reflect.ValueOf(custom))) } return nil } if props.StdTime { fv := v p.back() props.StdTime = false tproto := ×tamp{} err := p.readAny(reflect.ValueOf(tproto).Elem(), props) props.StdTime = true if err != nil { return err } tim, err := timestampFromProto(tproto) if err != nil { return err } if props.Repeated { t := reflect.TypeOf(v.Interface()) if t.Kind() == reflect.Slice { if t.Elem().Kind() == reflect.Ptr { ts := fv.Interface().([]*time.Time) ts = append(ts, &tim) fv.Set(reflect.ValueOf(ts)) return nil } else { ts := fv.Interface().([]time.Time) ts = append(ts, tim) fv.Set(reflect.ValueOf(ts)) return nil } } } if reflect.TypeOf(v.Interface()).Kind() == reflect.Ptr { v.Set(reflect.ValueOf(&tim)) } else { v.Set(reflect.Indirect(reflect.ValueOf(&tim))) } return nil } if props.StdDuration { fv := v p.back() props.StdDuration = false dproto := &duration{} err := p.readAny(reflect.ValueOf(dproto).Elem(), props) props.StdDuration = true if err != nil { return err } dur, err := durationFromProto(dproto) if err != nil { return err } if props.Repeated { t := reflect.TypeOf(v.Interface()) if t.Kind() == reflect.Slice { if t.Elem().Kind() == reflect.Ptr { ds := fv.Interface().([]*time.Duration) ds = append(ds, &dur) fv.Set(reflect.ValueOf(ds)) return nil } else { ds := fv.Interface().([]time.Duration) ds = append(ds, dur) fv.Set(reflect.ValueOf(ds)) return nil } } } if reflect.TypeOf(v.Interface()).Kind() == reflect.Ptr { v.Set(reflect.ValueOf(&dur)) } else { v.Set(reflect.Indirect(reflect.ValueOf(&dur))) } return nil } switch fv := v; fv.Kind() { case reflect.Slice: at := v.Type() if at.Elem().Kind() == reflect.Uint8 { // Special case for []byte if tok.value[0] != '"' && tok.value[0] != '\'' { // Deliberately written out here, as the error after // this switch statement would write "invalid []byte: ...", // which is not as user-friendly. return p.errorf("invalid string: %v", tok.value) } bytes := []byte(tok.unquoted) fv.Set(reflect.ValueOf(bytes)) return nil } // Repeated field. if tok.value == "[" { // Repeated field with list notation, like [1,2,3]. for { fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem())) err := p.readAny(fv.Index(fv.Len()-1), props) if err != nil { return err } ntok := p.next() if ntok.err != nil { return ntok.err } if ntok.value == "]" { break } if ntok.value != "," { return p.errorf("Expected ']' or ',' found %q", ntok.value) } } return nil } // One value of the repeated field. p.back() fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem())) return p.readAny(fv.Index(fv.Len()-1), props) case reflect.Bool: // true/1/t/True or false/f/0/False. switch tok.value { case "true", "1", "t", "True": fv.SetBool(true) return nil case "false", "0", "f", "False": fv.SetBool(false) return nil } case reflect.Float32, reflect.Float64: v := tok.value // Ignore 'f' for compatibility with output generated by C++, but don't // remove 'f' when the value is "-inf" or "inf". if strings.HasSuffix(v, "f") && tok.value != "-inf" && tok.value != "inf" { v = v[:len(v)-1] } if f, err := strconv.ParseFloat(v, fv.Type().Bits()); err == nil { fv.SetFloat(f) return nil } case reflect.Int8: if x, err := strconv.ParseInt(tok.value, 0, 8); err == nil { fv.SetInt(x) return nil } case reflect.Int16: if x, err := strconv.ParseInt(tok.value, 0, 16); err == nil { fv.SetInt(x) return nil } case reflect.Int32: if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil { fv.SetInt(x) return nil } if len(props.Enum) == 0 { break } m, ok := enumValueMaps[props.Enum] if !ok { break } x, ok := m[tok.value] if !ok { break } fv.SetInt(int64(x)) return nil case reflect.Int64: if x, err := strconv.ParseInt(tok.value, 0, 64); err == nil { fv.SetInt(x) return nil } case reflect.Ptr: // A basic field (indirected through pointer), or a repeated message/group p.back() fv.Set(reflect.New(fv.Type().Elem())) return p.readAny(fv.Elem(), props) case reflect.String: if tok.value[0] == '"' || tok.value[0] == '\'' { fv.SetString(tok.unquoted) return nil } case reflect.Struct: var terminator string switch tok.value { case "{": terminator = "}" case "<": terminator = ">" default: return p.errorf("expected '{' or '<', found %q", tok.value) } // TODO: Handle nested messages which implement encoding.TextUnmarshaler. return p.readStruct(fv, terminator) case reflect.Uint8: if x, err := strconv.ParseUint(tok.value, 0, 8); err == nil { fv.SetUint(x) return nil } case reflect.Uint16: if x, err := strconv.ParseUint(tok.value, 0, 16); err == nil { fv.SetUint(x) return nil } case reflect.Uint32: if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil { fv.SetUint(uint64(x)) return nil } case reflect.Uint64: if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil { fv.SetUint(x) return nil } } return p.errorf("invalid %v: %v", v.Type(), tok.value) } // UnmarshalText reads a protocol buffer in Text format. UnmarshalText resets pb // before starting to unmarshal, so any existing data in pb is always removed. // If a required field is not set and no other error occurs, // UnmarshalText returns *RequiredNotSetError. func UnmarshalText(s string, pb Message) error { if um, ok := pb.(encoding.TextUnmarshaler); ok { return um.UnmarshalText([]byte(s)) } pb.Reset() v := reflect.ValueOf(pb) return newTextParser(s).readStruct(v.Elem(), "") } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/timestamp.go ================================================ // Go support for Protocol Buffers - Google's data interchange format // // Copyright 2016 The Go Authors. All rights reserved. // https://github.com/golang/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto // This file implements operations on google.protobuf.Timestamp. import ( "errors" "fmt" "time" ) const ( // Seconds field of the earliest valid Timestamp. // This is time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC).Unix(). minValidSeconds = -62135596800 // Seconds field just after the latest valid Timestamp. // This is time.Date(10000, 1, 1, 0, 0, 0, 0, time.UTC).Unix(). maxValidSeconds = 253402300800 ) // validateTimestamp determines whether a Timestamp is valid. // A valid timestamp represents a time in the range // [0001-01-01, 10000-01-01) and has a Nanos field // in the range [0, 1e9). // // If the Timestamp is valid, validateTimestamp returns nil. // Otherwise, it returns an error that describes // the problem. // // Every valid Timestamp can be represented by a time.Time, but the converse is not true. func validateTimestamp(ts *timestamp) error { if ts == nil { return errors.New("timestamp: nil Timestamp") } if ts.Seconds < minValidSeconds { return fmt.Errorf("timestamp: %#v before 0001-01-01", ts) } if ts.Seconds >= maxValidSeconds { return fmt.Errorf("timestamp: %#v after 10000-01-01", ts) } if ts.Nanos < 0 || ts.Nanos >= 1e9 { return fmt.Errorf("timestamp: %#v: nanos not in range [0, 1e9)", ts) } return nil } // TimestampFromProto converts a google.protobuf.Timestamp proto to a time.Time. // It returns an error if the argument is invalid. // // Unlike most Go functions, if Timestamp returns an error, the first return value // is not the zero time.Time. Instead, it is the value obtained from the // time.Unix function when passed the contents of the Timestamp, in the UTC // locale. This may or may not be a meaningful time; many invalid Timestamps // do map to valid time.Times. // // A nil Timestamp returns an error. The first return value in that case is // undefined. func timestampFromProto(ts *timestamp) (time.Time, error) { // Don't return the zero value on error, because corresponds to a valid // timestamp. Instead return whatever time.Unix gives us. var t time.Time if ts == nil { t = time.Unix(0, 0).UTC() // treat nil like the empty Timestamp } else { t = time.Unix(ts.Seconds, int64(ts.Nanos)).UTC() } return t, validateTimestamp(ts) } // TimestampProto converts the time.Time to a google.protobuf.Timestamp proto. // It returns an error if the resulting Timestamp is invalid. func timestampProto(t time.Time) (*timestamp, error) { seconds := t.Unix() nanos := int32(t.Sub(time.Unix(seconds, 0))) ts := ×tamp{ Seconds: seconds, Nanos: nanos, } if err := validateTimestamp(ts); err != nil { return nil, err } return ts, nil } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/timestamp_gogo.go ================================================ // Protocol Buffers for Go with Gadgets // // Copyright (c) 2016, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto import ( "reflect" "time" ) var timeType = reflect.TypeOf((*time.Time)(nil)).Elem() type timestamp struct { Seconds int64 `protobuf:"varint,1,opt,name=seconds,proto3" json:"seconds,omitempty"` Nanos int32 `protobuf:"varint,2,opt,name=nanos,proto3" json:"nanos,omitempty"` } func (m *timestamp) Reset() { *m = timestamp{} } func (*timestamp) ProtoMessage() {} func (*timestamp) String() string { return "timestamp" } func init() { RegisterType((*timestamp)(nil), "gogo.protobuf.proto.timestamp") } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/wrappers.go ================================================ // Protocol Buffers for Go with Gadgets // // Copyright (c) 2018, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto import ( "io" "reflect" ) func makeStdDoubleValueMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { t := ptr.asPointerTo(u.typ).Interface().(*float64) v := &float64Value{*t} siz := Size(v) return tagsize + SizeVarint(uint64(siz)) + siz }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { t := ptr.asPointerTo(u.typ).Interface().(*float64) v := &float64Value{*t} buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(buf))) b = append(b, buf...) return b, nil } } func makeStdDoubleValuePtrMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { if ptr.isNil() { return 0 } t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*float64) v := &float64Value{*t} siz := Size(v) return tagsize + SizeVarint(uint64(siz)) + siz }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { if ptr.isNil() { return b, nil } t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*float64) v := &float64Value{*t} buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(buf))) b = append(b, buf...) return b, nil } } func makeStdDoubleValueSliceMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { s := ptr.getSlice(u.typ) n := 0 for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(float64) v := &float64Value{t} siz := Size(v) n += siz + SizeVarint(uint64(siz)) + tagsize } return n }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getSlice(u.typ) for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(float64) v := &float64Value{t} siz := Size(v) buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(siz)) b = append(b, buf...) } return b, nil } } func makeStdDoubleValuePtrSliceMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { s := ptr.getSlice(reflect.PtrTo(u.typ)) n := 0 for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(*float64) v := &float64Value{*t} siz := Size(v) n += siz + SizeVarint(uint64(siz)) + tagsize } return n }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getSlice(reflect.PtrTo(u.typ)) for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(*float64) v := &float64Value{*t} siz := Size(v) buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(siz)) b = append(b, buf...) } return b, nil } } func makeStdDoubleValueUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &float64Value{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } s := f.asPointerTo(sub.typ).Elem() s.Set(reflect.ValueOf(m.Value)) return b[x:], nil } } func makeStdDoubleValuePtrUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &float64Value{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } s := f.asPointerTo(reflect.PtrTo(sub.typ)).Elem() s.Set(reflect.ValueOf(&m.Value)) return b[x:], nil } } func makeStdDoubleValuePtrSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &float64Value{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } slice := f.getSlice(reflect.PtrTo(sub.typ)) newSlice := reflect.Append(slice, reflect.ValueOf(&m.Value)) slice.Set(newSlice) return b[x:], nil } } func makeStdDoubleValueSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &float64Value{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } slice := f.getSlice(sub.typ) newSlice := reflect.Append(slice, reflect.ValueOf(m.Value)) slice.Set(newSlice) return b[x:], nil } } func makeStdFloatValueMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { t := ptr.asPointerTo(u.typ).Interface().(*float32) v := &float32Value{*t} siz := Size(v) return tagsize + SizeVarint(uint64(siz)) + siz }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { t := ptr.asPointerTo(u.typ).Interface().(*float32) v := &float32Value{*t} buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(buf))) b = append(b, buf...) return b, nil } } func makeStdFloatValuePtrMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { if ptr.isNil() { return 0 } t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*float32) v := &float32Value{*t} siz := Size(v) return tagsize + SizeVarint(uint64(siz)) + siz }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { if ptr.isNil() { return b, nil } t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*float32) v := &float32Value{*t} buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(buf))) b = append(b, buf...) return b, nil } } func makeStdFloatValueSliceMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { s := ptr.getSlice(u.typ) n := 0 for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(float32) v := &float32Value{t} siz := Size(v) n += siz + SizeVarint(uint64(siz)) + tagsize } return n }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getSlice(u.typ) for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(float32) v := &float32Value{t} siz := Size(v) buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(siz)) b = append(b, buf...) } return b, nil } } func makeStdFloatValuePtrSliceMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { s := ptr.getSlice(reflect.PtrTo(u.typ)) n := 0 for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(*float32) v := &float32Value{*t} siz := Size(v) n += siz + SizeVarint(uint64(siz)) + tagsize } return n }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getSlice(reflect.PtrTo(u.typ)) for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(*float32) v := &float32Value{*t} siz := Size(v) buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(siz)) b = append(b, buf...) } return b, nil } } func makeStdFloatValueUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &float32Value{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } s := f.asPointerTo(sub.typ).Elem() s.Set(reflect.ValueOf(m.Value)) return b[x:], nil } } func makeStdFloatValuePtrUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &float32Value{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } s := f.asPointerTo(reflect.PtrTo(sub.typ)).Elem() s.Set(reflect.ValueOf(&m.Value)) return b[x:], nil } } func makeStdFloatValuePtrSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &float32Value{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } slice := f.getSlice(reflect.PtrTo(sub.typ)) newSlice := reflect.Append(slice, reflect.ValueOf(&m.Value)) slice.Set(newSlice) return b[x:], nil } } func makeStdFloatValueSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &float32Value{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } slice := f.getSlice(sub.typ) newSlice := reflect.Append(slice, reflect.ValueOf(m.Value)) slice.Set(newSlice) return b[x:], nil } } func makeStdInt64ValueMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { t := ptr.asPointerTo(u.typ).Interface().(*int64) v := &int64Value{*t} siz := Size(v) return tagsize + SizeVarint(uint64(siz)) + siz }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { t := ptr.asPointerTo(u.typ).Interface().(*int64) v := &int64Value{*t} buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(buf))) b = append(b, buf...) return b, nil } } func makeStdInt64ValuePtrMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { if ptr.isNil() { return 0 } t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*int64) v := &int64Value{*t} siz := Size(v) return tagsize + SizeVarint(uint64(siz)) + siz }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { if ptr.isNil() { return b, nil } t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*int64) v := &int64Value{*t} buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(buf))) b = append(b, buf...) return b, nil } } func makeStdInt64ValueSliceMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { s := ptr.getSlice(u.typ) n := 0 for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(int64) v := &int64Value{t} siz := Size(v) n += siz + SizeVarint(uint64(siz)) + tagsize } return n }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getSlice(u.typ) for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(int64) v := &int64Value{t} siz := Size(v) buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(siz)) b = append(b, buf...) } return b, nil } } func makeStdInt64ValuePtrSliceMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { s := ptr.getSlice(reflect.PtrTo(u.typ)) n := 0 for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(*int64) v := &int64Value{*t} siz := Size(v) n += siz + SizeVarint(uint64(siz)) + tagsize } return n }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getSlice(reflect.PtrTo(u.typ)) for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(*int64) v := &int64Value{*t} siz := Size(v) buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(siz)) b = append(b, buf...) } return b, nil } } func makeStdInt64ValueUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &int64Value{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } s := f.asPointerTo(sub.typ).Elem() s.Set(reflect.ValueOf(m.Value)) return b[x:], nil } } func makeStdInt64ValuePtrUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &int64Value{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } s := f.asPointerTo(reflect.PtrTo(sub.typ)).Elem() s.Set(reflect.ValueOf(&m.Value)) return b[x:], nil } } func makeStdInt64ValuePtrSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &int64Value{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } slice := f.getSlice(reflect.PtrTo(sub.typ)) newSlice := reflect.Append(slice, reflect.ValueOf(&m.Value)) slice.Set(newSlice) return b[x:], nil } } func makeStdInt64ValueSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &int64Value{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } slice := f.getSlice(sub.typ) newSlice := reflect.Append(slice, reflect.ValueOf(m.Value)) slice.Set(newSlice) return b[x:], nil } } func makeStdUInt64ValueMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { t := ptr.asPointerTo(u.typ).Interface().(*uint64) v := &uint64Value{*t} siz := Size(v) return tagsize + SizeVarint(uint64(siz)) + siz }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { t := ptr.asPointerTo(u.typ).Interface().(*uint64) v := &uint64Value{*t} buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(buf))) b = append(b, buf...) return b, nil } } func makeStdUInt64ValuePtrMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { if ptr.isNil() { return 0 } t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*uint64) v := &uint64Value{*t} siz := Size(v) return tagsize + SizeVarint(uint64(siz)) + siz }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { if ptr.isNil() { return b, nil } t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*uint64) v := &uint64Value{*t} buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(buf))) b = append(b, buf...) return b, nil } } func makeStdUInt64ValueSliceMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { s := ptr.getSlice(u.typ) n := 0 for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(uint64) v := &uint64Value{t} siz := Size(v) n += siz + SizeVarint(uint64(siz)) + tagsize } return n }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getSlice(u.typ) for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(uint64) v := &uint64Value{t} siz := Size(v) buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(siz)) b = append(b, buf...) } return b, nil } } func makeStdUInt64ValuePtrSliceMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { s := ptr.getSlice(reflect.PtrTo(u.typ)) n := 0 for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(*uint64) v := &uint64Value{*t} siz := Size(v) n += siz + SizeVarint(uint64(siz)) + tagsize } return n }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getSlice(reflect.PtrTo(u.typ)) for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(*uint64) v := &uint64Value{*t} siz := Size(v) buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(siz)) b = append(b, buf...) } return b, nil } } func makeStdUInt64ValueUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &uint64Value{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } s := f.asPointerTo(sub.typ).Elem() s.Set(reflect.ValueOf(m.Value)) return b[x:], nil } } func makeStdUInt64ValuePtrUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &uint64Value{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } s := f.asPointerTo(reflect.PtrTo(sub.typ)).Elem() s.Set(reflect.ValueOf(&m.Value)) return b[x:], nil } } func makeStdUInt64ValuePtrSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &uint64Value{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } slice := f.getSlice(reflect.PtrTo(sub.typ)) newSlice := reflect.Append(slice, reflect.ValueOf(&m.Value)) slice.Set(newSlice) return b[x:], nil } } func makeStdUInt64ValueSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &uint64Value{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } slice := f.getSlice(sub.typ) newSlice := reflect.Append(slice, reflect.ValueOf(m.Value)) slice.Set(newSlice) return b[x:], nil } } func makeStdInt32ValueMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { t := ptr.asPointerTo(u.typ).Interface().(*int32) v := &int32Value{*t} siz := Size(v) return tagsize + SizeVarint(uint64(siz)) + siz }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { t := ptr.asPointerTo(u.typ).Interface().(*int32) v := &int32Value{*t} buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(buf))) b = append(b, buf...) return b, nil } } func makeStdInt32ValuePtrMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { if ptr.isNil() { return 0 } t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*int32) v := &int32Value{*t} siz := Size(v) return tagsize + SizeVarint(uint64(siz)) + siz }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { if ptr.isNil() { return b, nil } t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*int32) v := &int32Value{*t} buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(buf))) b = append(b, buf...) return b, nil } } func makeStdInt32ValueSliceMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { s := ptr.getSlice(u.typ) n := 0 for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(int32) v := &int32Value{t} siz := Size(v) n += siz + SizeVarint(uint64(siz)) + tagsize } return n }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getSlice(u.typ) for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(int32) v := &int32Value{t} siz := Size(v) buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(siz)) b = append(b, buf...) } return b, nil } } func makeStdInt32ValuePtrSliceMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { s := ptr.getSlice(reflect.PtrTo(u.typ)) n := 0 for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(*int32) v := &int32Value{*t} siz := Size(v) n += siz + SizeVarint(uint64(siz)) + tagsize } return n }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getSlice(reflect.PtrTo(u.typ)) for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(*int32) v := &int32Value{*t} siz := Size(v) buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(siz)) b = append(b, buf...) } return b, nil } } func makeStdInt32ValueUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &int32Value{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } s := f.asPointerTo(sub.typ).Elem() s.Set(reflect.ValueOf(m.Value)) return b[x:], nil } } func makeStdInt32ValuePtrUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &int32Value{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } s := f.asPointerTo(reflect.PtrTo(sub.typ)).Elem() s.Set(reflect.ValueOf(&m.Value)) return b[x:], nil } } func makeStdInt32ValuePtrSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &int32Value{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } slice := f.getSlice(reflect.PtrTo(sub.typ)) newSlice := reflect.Append(slice, reflect.ValueOf(&m.Value)) slice.Set(newSlice) return b[x:], nil } } func makeStdInt32ValueSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &int32Value{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } slice := f.getSlice(sub.typ) newSlice := reflect.Append(slice, reflect.ValueOf(m.Value)) slice.Set(newSlice) return b[x:], nil } } func makeStdUInt32ValueMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { t := ptr.asPointerTo(u.typ).Interface().(*uint32) v := &uint32Value{*t} siz := Size(v) return tagsize + SizeVarint(uint64(siz)) + siz }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { t := ptr.asPointerTo(u.typ).Interface().(*uint32) v := &uint32Value{*t} buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(buf))) b = append(b, buf...) return b, nil } } func makeStdUInt32ValuePtrMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { if ptr.isNil() { return 0 } t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*uint32) v := &uint32Value{*t} siz := Size(v) return tagsize + SizeVarint(uint64(siz)) + siz }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { if ptr.isNil() { return b, nil } t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*uint32) v := &uint32Value{*t} buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(buf))) b = append(b, buf...) return b, nil } } func makeStdUInt32ValueSliceMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { s := ptr.getSlice(u.typ) n := 0 for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(uint32) v := &uint32Value{t} siz := Size(v) n += siz + SizeVarint(uint64(siz)) + tagsize } return n }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getSlice(u.typ) for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(uint32) v := &uint32Value{t} siz := Size(v) buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(siz)) b = append(b, buf...) } return b, nil } } func makeStdUInt32ValuePtrSliceMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { s := ptr.getSlice(reflect.PtrTo(u.typ)) n := 0 for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(*uint32) v := &uint32Value{*t} siz := Size(v) n += siz + SizeVarint(uint64(siz)) + tagsize } return n }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getSlice(reflect.PtrTo(u.typ)) for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(*uint32) v := &uint32Value{*t} siz := Size(v) buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(siz)) b = append(b, buf...) } return b, nil } } func makeStdUInt32ValueUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &uint32Value{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } s := f.asPointerTo(sub.typ).Elem() s.Set(reflect.ValueOf(m.Value)) return b[x:], nil } } func makeStdUInt32ValuePtrUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &uint32Value{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } s := f.asPointerTo(reflect.PtrTo(sub.typ)).Elem() s.Set(reflect.ValueOf(&m.Value)) return b[x:], nil } } func makeStdUInt32ValuePtrSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &uint32Value{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } slice := f.getSlice(reflect.PtrTo(sub.typ)) newSlice := reflect.Append(slice, reflect.ValueOf(&m.Value)) slice.Set(newSlice) return b[x:], nil } } func makeStdUInt32ValueSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &uint32Value{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } slice := f.getSlice(sub.typ) newSlice := reflect.Append(slice, reflect.ValueOf(m.Value)) slice.Set(newSlice) return b[x:], nil } } func makeStdBoolValueMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { t := ptr.asPointerTo(u.typ).Interface().(*bool) v := &boolValue{*t} siz := Size(v) return tagsize + SizeVarint(uint64(siz)) + siz }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { t := ptr.asPointerTo(u.typ).Interface().(*bool) v := &boolValue{*t} buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(buf))) b = append(b, buf...) return b, nil } } func makeStdBoolValuePtrMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { if ptr.isNil() { return 0 } t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*bool) v := &boolValue{*t} siz := Size(v) return tagsize + SizeVarint(uint64(siz)) + siz }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { if ptr.isNil() { return b, nil } t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*bool) v := &boolValue{*t} buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(buf))) b = append(b, buf...) return b, nil } } func makeStdBoolValueSliceMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { s := ptr.getSlice(u.typ) n := 0 for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(bool) v := &boolValue{t} siz := Size(v) n += siz + SizeVarint(uint64(siz)) + tagsize } return n }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getSlice(u.typ) for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(bool) v := &boolValue{t} siz := Size(v) buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(siz)) b = append(b, buf...) } return b, nil } } func makeStdBoolValuePtrSliceMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { s := ptr.getSlice(reflect.PtrTo(u.typ)) n := 0 for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(*bool) v := &boolValue{*t} siz := Size(v) n += siz + SizeVarint(uint64(siz)) + tagsize } return n }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getSlice(reflect.PtrTo(u.typ)) for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(*bool) v := &boolValue{*t} siz := Size(v) buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(siz)) b = append(b, buf...) } return b, nil } } func makeStdBoolValueUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &boolValue{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } s := f.asPointerTo(sub.typ).Elem() s.Set(reflect.ValueOf(m.Value)) return b[x:], nil } } func makeStdBoolValuePtrUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &boolValue{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } s := f.asPointerTo(reflect.PtrTo(sub.typ)).Elem() s.Set(reflect.ValueOf(&m.Value)) return b[x:], nil } } func makeStdBoolValuePtrSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &boolValue{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } slice := f.getSlice(reflect.PtrTo(sub.typ)) newSlice := reflect.Append(slice, reflect.ValueOf(&m.Value)) slice.Set(newSlice) return b[x:], nil } } func makeStdBoolValueSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &boolValue{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } slice := f.getSlice(sub.typ) newSlice := reflect.Append(slice, reflect.ValueOf(m.Value)) slice.Set(newSlice) return b[x:], nil } } func makeStdStringValueMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { t := ptr.asPointerTo(u.typ).Interface().(*string) v := &stringValue{*t} siz := Size(v) return tagsize + SizeVarint(uint64(siz)) + siz }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { t := ptr.asPointerTo(u.typ).Interface().(*string) v := &stringValue{*t} buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(buf))) b = append(b, buf...) return b, nil } } func makeStdStringValuePtrMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { if ptr.isNil() { return 0 } t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*string) v := &stringValue{*t} siz := Size(v) return tagsize + SizeVarint(uint64(siz)) + siz }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { if ptr.isNil() { return b, nil } t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*string) v := &stringValue{*t} buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(buf))) b = append(b, buf...) return b, nil } } func makeStdStringValueSliceMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { s := ptr.getSlice(u.typ) n := 0 for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(string) v := &stringValue{t} siz := Size(v) n += siz + SizeVarint(uint64(siz)) + tagsize } return n }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getSlice(u.typ) for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(string) v := &stringValue{t} siz := Size(v) buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(siz)) b = append(b, buf...) } return b, nil } } func makeStdStringValuePtrSliceMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { s := ptr.getSlice(reflect.PtrTo(u.typ)) n := 0 for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(*string) v := &stringValue{*t} siz := Size(v) n += siz + SizeVarint(uint64(siz)) + tagsize } return n }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getSlice(reflect.PtrTo(u.typ)) for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(*string) v := &stringValue{*t} siz := Size(v) buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(siz)) b = append(b, buf...) } return b, nil } } func makeStdStringValueUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &stringValue{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } s := f.asPointerTo(sub.typ).Elem() s.Set(reflect.ValueOf(m.Value)) return b[x:], nil } } func makeStdStringValuePtrUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &stringValue{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } s := f.asPointerTo(reflect.PtrTo(sub.typ)).Elem() s.Set(reflect.ValueOf(&m.Value)) return b[x:], nil } } func makeStdStringValuePtrSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &stringValue{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } slice := f.getSlice(reflect.PtrTo(sub.typ)) newSlice := reflect.Append(slice, reflect.ValueOf(&m.Value)) slice.Set(newSlice) return b[x:], nil } } func makeStdStringValueSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &stringValue{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } slice := f.getSlice(sub.typ) newSlice := reflect.Append(slice, reflect.ValueOf(m.Value)) slice.Set(newSlice) return b[x:], nil } } func makeStdBytesValueMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { t := ptr.asPointerTo(u.typ).Interface().(*[]byte) v := &bytesValue{*t} siz := Size(v) return tagsize + SizeVarint(uint64(siz)) + siz }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { t := ptr.asPointerTo(u.typ).Interface().(*[]byte) v := &bytesValue{*t} buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(buf))) b = append(b, buf...) return b, nil } } func makeStdBytesValuePtrMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { if ptr.isNil() { return 0 } t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*[]byte) v := &bytesValue{*t} siz := Size(v) return tagsize + SizeVarint(uint64(siz)) + siz }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { if ptr.isNil() { return b, nil } t := ptr.asPointerTo(reflect.PtrTo(u.typ)).Elem().Interface().(*[]byte) v := &bytesValue{*t} buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(len(buf))) b = append(b, buf...) return b, nil } } func makeStdBytesValueSliceMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { s := ptr.getSlice(u.typ) n := 0 for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().([]byte) v := &bytesValue{t} siz := Size(v) n += siz + SizeVarint(uint64(siz)) + tagsize } return n }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getSlice(u.typ) for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().([]byte) v := &bytesValue{t} siz := Size(v) buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(siz)) b = append(b, buf...) } return b, nil } } func makeStdBytesValuePtrSliceMarshaler(u *marshalInfo) (sizer, marshaler) { return func(ptr pointer, tagsize int) int { s := ptr.getSlice(reflect.PtrTo(u.typ)) n := 0 for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(*[]byte) v := &bytesValue{*t} siz := Size(v) n += siz + SizeVarint(uint64(siz)) + tagsize } return n }, func(b []byte, ptr pointer, wiretag uint64, deterministic bool) ([]byte, error) { s := ptr.getSlice(reflect.PtrTo(u.typ)) for i := 0; i < s.Len(); i++ { elem := s.Index(i) t := elem.Interface().(*[]byte) v := &bytesValue{*t} siz := Size(v) buf, err := Marshal(v) if err != nil { return nil, err } b = appendVarint(b, wiretag) b = appendVarint(b, uint64(siz)) b = append(b, buf...) } return b, nil } } func makeStdBytesValueUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &bytesValue{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } s := f.asPointerTo(sub.typ).Elem() s.Set(reflect.ValueOf(m.Value)) return b[x:], nil } } func makeStdBytesValuePtrUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &bytesValue{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } s := f.asPointerTo(reflect.PtrTo(sub.typ)).Elem() s.Set(reflect.ValueOf(&m.Value)) return b[x:], nil } } func makeStdBytesValuePtrSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &bytesValue{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } slice := f.getSlice(reflect.PtrTo(sub.typ)) newSlice := reflect.Append(slice, reflect.ValueOf(&m.Value)) slice.Set(newSlice) return b[x:], nil } } func makeStdBytesValueSliceUnmarshaler(sub *unmarshalInfo, name string) unmarshaler { return func(b []byte, f pointer, w int) ([]byte, error) { if w != WireBytes { return nil, errInternalBadWireType } x, n := decodeVarint(b) if n == 0 { return nil, io.ErrUnexpectedEOF } b = b[n:] if x > uint64(len(b)) { return nil, io.ErrUnexpectedEOF } m := &bytesValue{} if err := Unmarshal(b[:x], m); err != nil { return nil, err } slice := f.getSlice(sub.typ) newSlice := reflect.Append(slice, reflect.ValueOf(m.Value)) slice.Set(newSlice) return b[x:], nil } } ================================================ FILE: vendor/github.com/gogo/protobuf/proto/wrappers_gogo.go ================================================ // Protocol Buffers for Go with Gadgets // // Copyright (c) 2018, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package proto type float64Value struct { Value float64 `protobuf:"fixed64,1,opt,name=value,proto3" json:"value,omitempty"` } func (m *float64Value) Reset() { *m = float64Value{} } func (*float64Value) ProtoMessage() {} func (*float64Value) String() string { return "float64" } type float32Value struct { Value float32 `protobuf:"fixed32,1,opt,name=value,proto3" json:"value,omitempty"` } func (m *float32Value) Reset() { *m = float32Value{} } func (*float32Value) ProtoMessage() {} func (*float32Value) String() string { return "float32" } type int64Value struct { Value int64 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` } func (m *int64Value) Reset() { *m = int64Value{} } func (*int64Value) ProtoMessage() {} func (*int64Value) String() string { return "int64" } type uint64Value struct { Value uint64 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` } func (m *uint64Value) Reset() { *m = uint64Value{} } func (*uint64Value) ProtoMessage() {} func (*uint64Value) String() string { return "uint64" } type int32Value struct { Value int32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` } func (m *int32Value) Reset() { *m = int32Value{} } func (*int32Value) ProtoMessage() {} func (*int32Value) String() string { return "int32" } type uint32Value struct { Value uint32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` } func (m *uint32Value) Reset() { *m = uint32Value{} } func (*uint32Value) ProtoMessage() {} func (*uint32Value) String() string { return "uint32" } type boolValue struct { Value bool `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` } func (m *boolValue) Reset() { *m = boolValue{} } func (*boolValue) ProtoMessage() {} func (*boolValue) String() string { return "bool" } type stringValue struct { Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` } func (m *stringValue) Reset() { *m = stringValue{} } func (*stringValue) ProtoMessage() {} func (*stringValue) String() string { return "string" } type bytesValue struct { Value []byte `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` } func (m *bytesValue) Reset() { *m = bytesValue{} } func (*bytesValue) ProtoMessage() {} func (*bytesValue) String() string { return "[]byte" } func init() { RegisterType((*float64Value)(nil), "gogo.protobuf.proto.DoubleValue") RegisterType((*float32Value)(nil), "gogo.protobuf.proto.FloatValue") RegisterType((*int64Value)(nil), "gogo.protobuf.proto.Int64Value") RegisterType((*uint64Value)(nil), "gogo.protobuf.proto.UInt64Value") RegisterType((*int32Value)(nil), "gogo.protobuf.proto.Int32Value") RegisterType((*uint32Value)(nil), "gogo.protobuf.proto.UInt32Value") RegisterType((*boolValue)(nil), "gogo.protobuf.proto.BoolValue") RegisterType((*stringValue)(nil), "gogo.protobuf.proto.StringValue") RegisterType((*bytesValue)(nil), "gogo.protobuf.proto.BytesValue") } ================================================ FILE: vendor/github.com/gogo/protobuf/sortkeys/sortkeys.go ================================================ // Protocol Buffers for Go with Gadgets // // Copyright (c) 2013, The GoGo Authors. All rights reserved. // http://github.com/gogo/protobuf // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package sortkeys import ( "sort" ) func Strings(l []string) { sort.Strings(l) } func Float64s(l []float64) { sort.Float64s(l) } func Float32s(l []float32) { sort.Sort(Float32Slice(l)) } func Int64s(l []int64) { sort.Sort(Int64Slice(l)) } func Int32s(l []int32) { sort.Sort(Int32Slice(l)) } func Uint64s(l []uint64) { sort.Sort(Uint64Slice(l)) } func Uint32s(l []uint32) { sort.Sort(Uint32Slice(l)) } func Bools(l []bool) { sort.Sort(BoolSlice(l)) } type BoolSlice []bool func (p BoolSlice) Len() int { return len(p) } func (p BoolSlice) Less(i, j int) bool { return p[j] } func (p BoolSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } type Int64Slice []int64 func (p Int64Slice) Len() int { return len(p) } func (p Int64Slice) Less(i, j int) bool { return p[i] < p[j] } func (p Int64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } type Int32Slice []int32 func (p Int32Slice) Len() int { return len(p) } func (p Int32Slice) Less(i, j int) bool { return p[i] < p[j] } func (p Int32Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } type Uint64Slice []uint64 func (p Uint64Slice) Len() int { return len(p) } func (p Uint64Slice) Less(i, j int) bool { return p[i] < p[j] } func (p Uint64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } type Uint32Slice []uint32 func (p Uint32Slice) Len() int { return len(p) } func (p Uint32Slice) Less(i, j int) bool { return p[i] < p[j] } func (p Uint32Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } type Float32Slice []float32 func (p Float32Slice) Len() int { return len(p) } func (p Float32Slice) Less(i, j int) bool { return p[i] < p[j] } func (p Float32Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } ================================================ FILE: vendor/github.com/golang/groupcache/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: You must give any other recipients of the Work or Derivative Works a copy of this License; and You must cause any modified files to carry prominent notices stating that You changed the files; and You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/golang/groupcache/lru/lru.go ================================================ /* Copyright 2013 Google Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // Package lru implements an LRU cache. package lru import "container/list" // Cache is an LRU cache. It is not safe for concurrent access. type Cache struct { // MaxEntries is the maximum number of cache entries before // an item is evicted. Zero means no limit. MaxEntries int // OnEvicted optionally specifies a callback function to be // executed when an entry is purged from the cache. OnEvicted func(key Key, value interface{}) ll *list.List cache map[interface{}]*list.Element } // A Key may be any value that is comparable. See http://golang.org/ref/spec#Comparison_operators type Key interface{} type entry struct { key Key value interface{} } // New creates a new Cache. // If maxEntries is zero, the cache has no limit and it's assumed // that eviction is done by the caller. func New(maxEntries int) *Cache { return &Cache{ MaxEntries: maxEntries, ll: list.New(), cache: make(map[interface{}]*list.Element), } } // Add adds a value to the cache. func (c *Cache) Add(key Key, value interface{}) { if c.cache == nil { c.cache = make(map[interface{}]*list.Element) c.ll = list.New() } if ee, ok := c.cache[key]; ok { c.ll.MoveToFront(ee) ee.Value.(*entry).value = value return } ele := c.ll.PushFront(&entry{key, value}) c.cache[key] = ele if c.MaxEntries != 0 && c.ll.Len() > c.MaxEntries { c.RemoveOldest() } } // Get looks up a key's value from the cache. func (c *Cache) Get(key Key) (value interface{}, ok bool) { if c.cache == nil { return } if ele, hit := c.cache[key]; hit { c.ll.MoveToFront(ele) return ele.Value.(*entry).value, true } return } // Remove removes the provided key from the cache. func (c *Cache) Remove(key Key) { if c.cache == nil { return } if ele, hit := c.cache[key]; hit { c.removeElement(ele) } } // RemoveOldest removes the oldest item from the cache. func (c *Cache) RemoveOldest() { if c.cache == nil { return } ele := c.ll.Back() if ele != nil { c.removeElement(ele) } } func (c *Cache) removeElement(e *list.Element) { c.ll.Remove(e) kv := e.Value.(*entry) delete(c.cache, kv.key) if c.OnEvicted != nil { c.OnEvicted(kv.key, kv.value) } } // Len returns the number of items in the cache. func (c *Cache) Len() int { if c.cache == nil { return 0 } return c.ll.Len() } // Clear purges all stored items from the cache. func (c *Cache) Clear() { if c.OnEvicted != nil { for _, e := range c.cache { kv := e.Value.(*entry) c.OnEvicted(kv.key, kv.value) } } c.ll = nil c.cache = nil } ================================================ FILE: vendor/github.com/golang/protobuf/AUTHORS ================================================ # This source code refers to The Go Authors for copyright purposes. # The master list of authors is in the main Go distribution, # visible at http://tip.golang.org/AUTHORS. ================================================ FILE: vendor/github.com/golang/protobuf/CONTRIBUTORS ================================================ # This source code was written by the Go contributors. # The master list of contributors is in the main Go distribution, # visible at http://tip.golang.org/CONTRIBUTORS. ================================================ FILE: vendor/github.com/golang/protobuf/LICENSE ================================================ Copyright 2010 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/golang/protobuf/proto/buffer.go ================================================ // Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package proto import ( "errors" "fmt" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/encoding/protowire" "google.golang.org/protobuf/runtime/protoimpl" ) const ( WireVarint = 0 WireFixed32 = 5 WireFixed64 = 1 WireBytes = 2 WireStartGroup = 3 WireEndGroup = 4 ) // EncodeVarint returns the varint encoded bytes of v. func EncodeVarint(v uint64) []byte { return protowire.AppendVarint(nil, v) } // SizeVarint returns the length of the varint encoded bytes of v. // This is equal to len(EncodeVarint(v)). func SizeVarint(v uint64) int { return protowire.SizeVarint(v) } // DecodeVarint parses a varint encoded integer from b, // returning the integer value and the length of the varint. // It returns (0, 0) if there is a parse error. func DecodeVarint(b []byte) (uint64, int) { v, n := protowire.ConsumeVarint(b) if n < 0 { return 0, 0 } return v, n } // Buffer is a buffer for encoding and decoding the protobuf wire format. // It may be reused between invocations to reduce memory usage. type Buffer struct { buf []byte idx int deterministic bool } // NewBuffer allocates a new Buffer initialized with buf, // where the contents of buf are considered the unread portion of the buffer. func NewBuffer(buf []byte) *Buffer { return &Buffer{buf: buf} } // SetDeterministic specifies whether to use deterministic serialization. // // Deterministic serialization guarantees that for a given binary, equal // messages will always be serialized to the same bytes. This implies: // // - Repeated serialization of a message will return the same bytes. // - Different processes of the same binary (which may be executing on // different machines) will serialize equal messages to the same bytes. // // Note that the deterministic serialization is NOT canonical across // languages. It is not guaranteed to remain stable over time. It is unstable // across different builds with schema changes due to unknown fields. // Users who need canonical serialization (e.g., persistent storage in a // canonical form, fingerprinting, etc.) should define their own // canonicalization specification and implement their own serializer rather // than relying on this API. // // If deterministic serialization is requested, map entries will be sorted // by keys in lexographical order. This is an implementation detail and // subject to change. func (b *Buffer) SetDeterministic(deterministic bool) { b.deterministic = deterministic } // SetBuf sets buf as the internal buffer, // where the contents of buf are considered the unread portion of the buffer. func (b *Buffer) SetBuf(buf []byte) { b.buf = buf b.idx = 0 } // Reset clears the internal buffer of all written and unread data. func (b *Buffer) Reset() { b.buf = b.buf[:0] b.idx = 0 } // Bytes returns the internal buffer. func (b *Buffer) Bytes() []byte { return b.buf } // Unread returns the unread portion of the buffer. func (b *Buffer) Unread() []byte { return b.buf[b.idx:] } // Marshal appends the wire-format encoding of m to the buffer. func (b *Buffer) Marshal(m Message) error { var err error b.buf, err = marshalAppend(b.buf, m, b.deterministic) return err } // Unmarshal parses the wire-format message in the buffer and // places the decoded results in m. // It does not reset m before unmarshaling. func (b *Buffer) Unmarshal(m Message) error { err := UnmarshalMerge(b.Unread(), m) b.idx = len(b.buf) return err } type unknownFields struct{ XXX_unrecognized protoimpl.UnknownFields } func (m *unknownFields) String() string { panic("not implemented") } func (m *unknownFields) Reset() { panic("not implemented") } func (m *unknownFields) ProtoMessage() { panic("not implemented") } // DebugPrint dumps the encoded bytes of b with a header and footer including s // to stdout. This is only intended for debugging. func (*Buffer) DebugPrint(s string, b []byte) { m := MessageReflect(new(unknownFields)) m.SetUnknown(b) b, _ = prototext.MarshalOptions{AllowPartial: true, Indent: "\t"}.Marshal(m.Interface()) fmt.Printf("==== %s ====\n%s==== %s ====\n", s, b, s) } // EncodeVarint appends an unsigned varint encoding to the buffer. func (b *Buffer) EncodeVarint(v uint64) error { b.buf = protowire.AppendVarint(b.buf, v) return nil } // EncodeZigzag32 appends a 32-bit zig-zag varint encoding to the buffer. func (b *Buffer) EncodeZigzag32(v uint64) error { return b.EncodeVarint(uint64((uint32(v) << 1) ^ uint32((int32(v) >> 31)))) } // EncodeZigzag64 appends a 64-bit zig-zag varint encoding to the buffer. func (b *Buffer) EncodeZigzag64(v uint64) error { return b.EncodeVarint(uint64((uint64(v) << 1) ^ uint64((int64(v) >> 63)))) } // EncodeFixed32 appends a 32-bit little-endian integer to the buffer. func (b *Buffer) EncodeFixed32(v uint64) error { b.buf = protowire.AppendFixed32(b.buf, uint32(v)) return nil } // EncodeFixed64 appends a 64-bit little-endian integer to the buffer. func (b *Buffer) EncodeFixed64(v uint64) error { b.buf = protowire.AppendFixed64(b.buf, uint64(v)) return nil } // EncodeRawBytes appends a length-prefixed raw bytes to the buffer. func (b *Buffer) EncodeRawBytes(v []byte) error { b.buf = protowire.AppendBytes(b.buf, v) return nil } // EncodeStringBytes appends a length-prefixed raw bytes to the buffer. // It does not validate whether v contains valid UTF-8. func (b *Buffer) EncodeStringBytes(v string) error { b.buf = protowire.AppendString(b.buf, v) return nil } // EncodeMessage appends a length-prefixed encoded message to the buffer. func (b *Buffer) EncodeMessage(m Message) error { var err error b.buf = protowire.AppendVarint(b.buf, uint64(Size(m))) b.buf, err = marshalAppend(b.buf, m, b.deterministic) return err } // DecodeVarint consumes an encoded unsigned varint from the buffer. func (b *Buffer) DecodeVarint() (uint64, error) { v, n := protowire.ConsumeVarint(b.buf[b.idx:]) if n < 0 { return 0, protowire.ParseError(n) } b.idx += n return uint64(v), nil } // DecodeZigzag32 consumes an encoded 32-bit zig-zag varint from the buffer. func (b *Buffer) DecodeZigzag32() (uint64, error) { v, err := b.DecodeVarint() if err != nil { return 0, err } return uint64((uint32(v) >> 1) ^ uint32((int32(v&1)<<31)>>31)), nil } // DecodeZigzag64 consumes an encoded 64-bit zig-zag varint from the buffer. func (b *Buffer) DecodeZigzag64() (uint64, error) { v, err := b.DecodeVarint() if err != nil { return 0, err } return uint64((uint64(v) >> 1) ^ uint64((int64(v&1)<<63)>>63)), nil } // DecodeFixed32 consumes a 32-bit little-endian integer from the buffer. func (b *Buffer) DecodeFixed32() (uint64, error) { v, n := protowire.ConsumeFixed32(b.buf[b.idx:]) if n < 0 { return 0, protowire.ParseError(n) } b.idx += n return uint64(v), nil } // DecodeFixed64 consumes a 64-bit little-endian integer from the buffer. func (b *Buffer) DecodeFixed64() (uint64, error) { v, n := protowire.ConsumeFixed64(b.buf[b.idx:]) if n < 0 { return 0, protowire.ParseError(n) } b.idx += n return uint64(v), nil } // DecodeRawBytes consumes a length-prefixed raw bytes from the buffer. // If alloc is specified, it returns a copy the raw bytes // rather than a sub-slice of the buffer. func (b *Buffer) DecodeRawBytes(alloc bool) ([]byte, error) { v, n := protowire.ConsumeBytes(b.buf[b.idx:]) if n < 0 { return nil, protowire.ParseError(n) } b.idx += n if alloc { v = append([]byte(nil), v...) } return v, nil } // DecodeStringBytes consumes a length-prefixed raw bytes from the buffer. // It does not validate whether the raw bytes contain valid UTF-8. func (b *Buffer) DecodeStringBytes() (string, error) { v, n := protowire.ConsumeString(b.buf[b.idx:]) if n < 0 { return "", protowire.ParseError(n) } b.idx += n return v, nil } // DecodeMessage consumes a length-prefixed message from the buffer. // It does not reset m before unmarshaling. func (b *Buffer) DecodeMessage(m Message) error { v, err := b.DecodeRawBytes(false) if err != nil { return err } return UnmarshalMerge(v, m) } // DecodeGroup consumes a message group from the buffer. // It assumes that the start group marker has already been consumed and // consumes all bytes until (and including the end group marker). // It does not reset m before unmarshaling. func (b *Buffer) DecodeGroup(m Message) error { v, n, err := consumeGroup(b.buf[b.idx:]) if err != nil { return err } b.idx += n return UnmarshalMerge(v, m) } // consumeGroup parses b until it finds an end group marker, returning // the raw bytes of the message (excluding the end group marker) and the // the total length of the message (including the end group marker). func consumeGroup(b []byte) ([]byte, int, error) { b0 := b depth := 1 // assume this follows a start group marker for { _, wtyp, tagLen := protowire.ConsumeTag(b) if tagLen < 0 { return nil, 0, protowire.ParseError(tagLen) } b = b[tagLen:] var valLen int switch wtyp { case protowire.VarintType: _, valLen = protowire.ConsumeVarint(b) case protowire.Fixed32Type: _, valLen = protowire.ConsumeFixed32(b) case protowire.Fixed64Type: _, valLen = protowire.ConsumeFixed64(b) case protowire.BytesType: _, valLen = protowire.ConsumeBytes(b) case protowire.StartGroupType: depth++ case protowire.EndGroupType: depth-- default: return nil, 0, errors.New("proto: cannot parse reserved wire type") } if valLen < 0 { return nil, 0, protowire.ParseError(valLen) } b = b[valLen:] if depth == 0 { return b0[:len(b0)-len(b)-tagLen], len(b0) - len(b), nil } } } ================================================ FILE: vendor/github.com/golang/protobuf/proto/defaults.go ================================================ // Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package proto import ( "google.golang.org/protobuf/reflect/protoreflect" ) // SetDefaults sets unpopulated scalar fields to their default values. // Fields within a oneof are not set even if they have a default value. // SetDefaults is recursively called upon any populated message fields. func SetDefaults(m Message) { if m != nil { setDefaults(MessageReflect(m)) } } func setDefaults(m protoreflect.Message) { fds := m.Descriptor().Fields() for i := 0; i < fds.Len(); i++ { fd := fds.Get(i) if !m.Has(fd) { if fd.HasDefault() && fd.ContainingOneof() == nil { v := fd.Default() if fd.Kind() == protoreflect.BytesKind { v = protoreflect.ValueOf(append([]byte(nil), v.Bytes()...)) // copy the default bytes } m.Set(fd, v) } continue } } m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { switch { // Handle singular message. case fd.Cardinality() != protoreflect.Repeated: if fd.Message() != nil { setDefaults(m.Get(fd).Message()) } // Handle list of messages. case fd.IsList(): if fd.Message() != nil { ls := m.Get(fd).List() for i := 0; i < ls.Len(); i++ { setDefaults(ls.Get(i).Message()) } } // Handle map of messages. case fd.IsMap(): if fd.MapValue().Message() != nil { ms := m.Get(fd).Map() ms.Range(func(_ protoreflect.MapKey, v protoreflect.Value) bool { setDefaults(v.Message()) return true }) } } return true }) } ================================================ FILE: vendor/github.com/golang/protobuf/proto/deprecated.go ================================================ // Copyright 2018 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package proto import ( "encoding/json" "errors" "fmt" "strconv" protoV2 "google.golang.org/protobuf/proto" ) var ( // Deprecated: No longer returned. ErrNil = errors.New("proto: Marshal called with nil") // Deprecated: No longer returned. ErrTooLarge = errors.New("proto: message encodes to over 2 GB") // Deprecated: No longer returned. ErrInternalBadWireType = errors.New("proto: internal error: bad wiretype for oneof") ) // Deprecated: Do not use. type Stats struct{ Emalloc, Dmalloc, Encode, Decode, Chit, Cmiss, Size uint64 } // Deprecated: Do not use. func GetStats() Stats { return Stats{} } // Deprecated: Do not use. func MarshalMessageSet(interface{}) ([]byte, error) { return nil, errors.New("proto: not implemented") } // Deprecated: Do not use. func UnmarshalMessageSet([]byte, interface{}) error { return errors.New("proto: not implemented") } // Deprecated: Do not use. func MarshalMessageSetJSON(interface{}) ([]byte, error) { return nil, errors.New("proto: not implemented") } // Deprecated: Do not use. func UnmarshalMessageSetJSON([]byte, interface{}) error { return errors.New("proto: not implemented") } // Deprecated: Do not use. func RegisterMessageSetType(Message, int32, string) {} // Deprecated: Do not use. func EnumName(m map[int32]string, v int32) string { s, ok := m[v] if ok { return s } return strconv.Itoa(int(v)) } // Deprecated: Do not use. func UnmarshalJSONEnum(m map[string]int32, data []byte, enumName string) (int32, error) { if data[0] == '"' { // New style: enums are strings. var repr string if err := json.Unmarshal(data, &repr); err != nil { return -1, err } val, ok := m[repr] if !ok { return 0, fmt.Errorf("unrecognized enum %s value %q", enumName, repr) } return val, nil } // Old style: enums are ints. var val int32 if err := json.Unmarshal(data, &val); err != nil { return 0, fmt.Errorf("cannot unmarshal %#q into enum %s", data, enumName) } return val, nil } // Deprecated: Do not use; this type existed for intenal-use only. type InternalMessageInfo struct{} // Deprecated: Do not use; this method existed for intenal-use only. func (*InternalMessageInfo) DiscardUnknown(m Message) { DiscardUnknown(m) } // Deprecated: Do not use; this method existed for intenal-use only. func (*InternalMessageInfo) Marshal(b []byte, m Message, deterministic bool) ([]byte, error) { return protoV2.MarshalOptions{Deterministic: deterministic}.MarshalAppend(b, MessageV2(m)) } // Deprecated: Do not use; this method existed for intenal-use only. func (*InternalMessageInfo) Merge(dst, src Message) { protoV2.Merge(MessageV2(dst), MessageV2(src)) } // Deprecated: Do not use; this method existed for intenal-use only. func (*InternalMessageInfo) Size(m Message) int { return protoV2.Size(MessageV2(m)) } // Deprecated: Do not use; this method existed for intenal-use only. func (*InternalMessageInfo) Unmarshal(m Message, b []byte) error { return protoV2.UnmarshalOptions{Merge: true}.Unmarshal(b, MessageV2(m)) } ================================================ FILE: vendor/github.com/golang/protobuf/proto/discard.go ================================================ // Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package proto import ( "google.golang.org/protobuf/reflect/protoreflect" ) // DiscardUnknown recursively discards all unknown fields from this message // and all embedded messages. // // When unmarshaling a message with unrecognized fields, the tags and values // of such fields are preserved in the Message. This allows a later call to // marshal to be able to produce a message that continues to have those // unrecognized fields. To avoid this, DiscardUnknown is used to // explicitly clear the unknown fields after unmarshaling. func DiscardUnknown(m Message) { if m != nil { discardUnknown(MessageReflect(m)) } } func discardUnknown(m protoreflect.Message) { m.Range(func(fd protoreflect.FieldDescriptor, val protoreflect.Value) bool { switch { // Handle singular message. case fd.Cardinality() != protoreflect.Repeated: if fd.Message() != nil { discardUnknown(m.Get(fd).Message()) } // Handle list of messages. case fd.IsList(): if fd.Message() != nil { ls := m.Get(fd).List() for i := 0; i < ls.Len(); i++ { discardUnknown(ls.Get(i).Message()) } } // Handle map of messages. case fd.IsMap(): if fd.MapValue().Message() != nil { ms := m.Get(fd).Map() ms.Range(func(_ protoreflect.MapKey, v protoreflect.Value) bool { discardUnknown(v.Message()) return true }) } } return true }) // Discard unknown fields. if len(m.GetUnknown()) > 0 { m.SetUnknown(nil) } } ================================================ FILE: vendor/github.com/golang/protobuf/proto/extensions.go ================================================ // Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package proto import ( "errors" "fmt" "reflect" "google.golang.org/protobuf/encoding/protowire" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/reflect/protoregistry" "google.golang.org/protobuf/runtime/protoiface" "google.golang.org/protobuf/runtime/protoimpl" ) type ( // ExtensionDesc represents an extension descriptor and // is used to interact with an extension field in a message. // // Variables of this type are generated in code by protoc-gen-go. ExtensionDesc = protoimpl.ExtensionInfo // ExtensionRange represents a range of message extensions. // Used in code generated by protoc-gen-go. ExtensionRange = protoiface.ExtensionRangeV1 // Deprecated: Do not use; this is an internal type. Extension = protoimpl.ExtensionFieldV1 // Deprecated: Do not use; this is an internal type. XXX_InternalExtensions = protoimpl.ExtensionFields ) // ErrMissingExtension reports whether the extension was not present. var ErrMissingExtension = errors.New("proto: missing extension") var errNotExtendable = errors.New("proto: not an extendable proto.Message") // HasExtension reports whether the extension field is present in m // either as an explicitly populated field or as an unknown field. func HasExtension(m Message, xt *ExtensionDesc) (has bool) { mr := MessageReflect(m) if mr == nil || !mr.IsValid() { return false } // Check whether any populated known field matches the field number. xtd := xt.TypeDescriptor() if isValidExtension(mr.Descriptor(), xtd) { has = mr.Has(xtd) } else { mr.Range(func(fd protoreflect.FieldDescriptor, _ protoreflect.Value) bool { has = int32(fd.Number()) == xt.Field return !has }) } // Check whether any unknown field matches the field number. for b := mr.GetUnknown(); !has && len(b) > 0; { num, _, n := protowire.ConsumeField(b) has = int32(num) == xt.Field b = b[n:] } return has } // ClearExtension removes the extension field from m // either as an explicitly populated field or as an unknown field. func ClearExtension(m Message, xt *ExtensionDesc) { mr := MessageReflect(m) if mr == nil || !mr.IsValid() { return } xtd := xt.TypeDescriptor() if isValidExtension(mr.Descriptor(), xtd) { mr.Clear(xtd) } else { mr.Range(func(fd protoreflect.FieldDescriptor, _ protoreflect.Value) bool { if int32(fd.Number()) == xt.Field { mr.Clear(fd) return false } return true }) } clearUnknown(mr, fieldNum(xt.Field)) } // ClearAllExtensions clears all extensions from m. // This includes populated fields and unknown fields in the extension range. func ClearAllExtensions(m Message) { mr := MessageReflect(m) if mr == nil || !mr.IsValid() { return } mr.Range(func(fd protoreflect.FieldDescriptor, _ protoreflect.Value) bool { if fd.IsExtension() { mr.Clear(fd) } return true }) clearUnknown(mr, mr.Descriptor().ExtensionRanges()) } // GetExtension retrieves a proto2 extended field from m. // // If the descriptor is type complete (i.e., ExtensionDesc.ExtensionType is non-nil), // then GetExtension parses the encoded field and returns a Go value of the specified type. // If the field is not present, then the default value is returned (if one is specified), // otherwise ErrMissingExtension is reported. // // If the descriptor is type incomplete (i.e., ExtensionDesc.ExtensionType is nil), // then GetExtension returns the raw encoded bytes for the extension field. func GetExtension(m Message, xt *ExtensionDesc) (interface{}, error) { mr := MessageReflect(m) if mr == nil || !mr.IsValid() || mr.Descriptor().ExtensionRanges().Len() == 0 { return nil, errNotExtendable } // Retrieve the unknown fields for this extension field. var bo protoreflect.RawFields for bi := mr.GetUnknown(); len(bi) > 0; { num, _, n := protowire.ConsumeField(bi) if int32(num) == xt.Field { bo = append(bo, bi[:n]...) } bi = bi[n:] } // For type incomplete descriptors, only retrieve the unknown fields. if xt.ExtensionType == nil { return []byte(bo), nil } // If the extension field only exists as unknown fields, unmarshal it. // This is rarely done since proto.Unmarshal eagerly unmarshals extensions. xtd := xt.TypeDescriptor() if !isValidExtension(mr.Descriptor(), xtd) { return nil, fmt.Errorf("proto: bad extended type; %T does not extend %T", xt.ExtendedType, m) } if !mr.Has(xtd) && len(bo) > 0 { m2 := mr.New() if err := (proto.UnmarshalOptions{ Resolver: extensionResolver{xt}, }.Unmarshal(bo, m2.Interface())); err != nil { return nil, err } if m2.Has(xtd) { mr.Set(xtd, m2.Get(xtd)) clearUnknown(mr, fieldNum(xt.Field)) } } // Check whether the message has the extension field set or a default. var pv protoreflect.Value switch { case mr.Has(xtd): pv = mr.Get(xtd) case xtd.HasDefault(): pv = xtd.Default() default: return nil, ErrMissingExtension } v := xt.InterfaceOf(pv) rv := reflect.ValueOf(v) if isScalarKind(rv.Kind()) { rv2 := reflect.New(rv.Type()) rv2.Elem().Set(rv) v = rv2.Interface() } return v, nil } // extensionResolver is a custom extension resolver that stores a single // extension type that takes precedence over the global registry. type extensionResolver struct{ xt protoreflect.ExtensionType } func (r extensionResolver) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) { if xtd := r.xt.TypeDescriptor(); xtd.FullName() == field { return r.xt, nil } return protoregistry.GlobalTypes.FindExtensionByName(field) } func (r extensionResolver) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) { if xtd := r.xt.TypeDescriptor(); xtd.ContainingMessage().FullName() == message && xtd.Number() == field { return r.xt, nil } return protoregistry.GlobalTypes.FindExtensionByNumber(message, field) } // GetExtensions returns a list of the extensions values present in m, // corresponding with the provided list of extension descriptors, xts. // If an extension is missing in m, the corresponding value is nil. func GetExtensions(m Message, xts []*ExtensionDesc) ([]interface{}, error) { mr := MessageReflect(m) if mr == nil || !mr.IsValid() { return nil, errNotExtendable } vs := make([]interface{}, len(xts)) for i, xt := range xts { v, err := GetExtension(m, xt) if err != nil { if err == ErrMissingExtension { continue } return vs, err } vs[i] = v } return vs, nil } // SetExtension sets an extension field in m to the provided value. func SetExtension(m Message, xt *ExtensionDesc, v interface{}) error { mr := MessageReflect(m) if mr == nil || !mr.IsValid() || mr.Descriptor().ExtensionRanges().Len() == 0 { return errNotExtendable } rv := reflect.ValueOf(v) if reflect.TypeOf(v) != reflect.TypeOf(xt.ExtensionType) { return fmt.Errorf("proto: bad extension value type. got: %T, want: %T", v, xt.ExtensionType) } if rv.Kind() == reflect.Ptr { if rv.IsNil() { return fmt.Errorf("proto: SetExtension called with nil value of type %T", v) } if isScalarKind(rv.Elem().Kind()) { v = rv.Elem().Interface() } } xtd := xt.TypeDescriptor() if !isValidExtension(mr.Descriptor(), xtd) { return fmt.Errorf("proto: bad extended type; %T does not extend %T", xt.ExtendedType, m) } mr.Set(xtd, xt.ValueOf(v)) clearUnknown(mr, fieldNum(xt.Field)) return nil } // SetRawExtension inserts b into the unknown fields of m. // // Deprecated: Use Message.ProtoReflect.SetUnknown instead. func SetRawExtension(m Message, fnum int32, b []byte) { mr := MessageReflect(m) if mr == nil || !mr.IsValid() { return } // Verify that the raw field is valid. for b0 := b; len(b0) > 0; { num, _, n := protowire.ConsumeField(b0) if int32(num) != fnum { panic(fmt.Sprintf("mismatching field number: got %d, want %d", num, fnum)) } b0 = b0[n:] } ClearExtension(m, &ExtensionDesc{Field: fnum}) mr.SetUnknown(append(mr.GetUnknown(), b...)) } // ExtensionDescs returns a list of extension descriptors found in m, // containing descriptors for both populated extension fields in m and // also unknown fields of m that are in the extension range. // For the later case, an type incomplete descriptor is provided where only // the ExtensionDesc.Field field is populated. // The order of the extension descriptors is undefined. func ExtensionDescs(m Message) ([]*ExtensionDesc, error) { mr := MessageReflect(m) if mr == nil || !mr.IsValid() || mr.Descriptor().ExtensionRanges().Len() == 0 { return nil, errNotExtendable } // Collect a set of known extension descriptors. extDescs := make(map[protoreflect.FieldNumber]*ExtensionDesc) mr.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { if fd.IsExtension() { xt := fd.(protoreflect.ExtensionTypeDescriptor) if xd, ok := xt.Type().(*ExtensionDesc); ok { extDescs[fd.Number()] = xd } } return true }) // Collect a set of unknown extension descriptors. extRanges := mr.Descriptor().ExtensionRanges() for b := mr.GetUnknown(); len(b) > 0; { num, _, n := protowire.ConsumeField(b) if extRanges.Has(num) && extDescs[num] == nil { extDescs[num] = nil } b = b[n:] } // Transpose the set of descriptors into a list. var xts []*ExtensionDesc for num, xt := range extDescs { if xt == nil { xt = &ExtensionDesc{Field: int32(num)} } xts = append(xts, xt) } return xts, nil } // isValidExtension reports whether xtd is a valid extension descriptor for md. func isValidExtension(md protoreflect.MessageDescriptor, xtd protoreflect.ExtensionTypeDescriptor) bool { return xtd.ContainingMessage() == md && md.ExtensionRanges().Has(xtd.Number()) } // isScalarKind reports whether k is a protobuf scalar kind (except bytes). // This function exists for historical reasons since the representation of // scalars differs between v1 and v2, where v1 uses *T and v2 uses T. func isScalarKind(k reflect.Kind) bool { switch k { case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String: return true default: return false } } // clearUnknown removes unknown fields from m where remover.Has reports true. func clearUnknown(m protoreflect.Message, remover interface { Has(protoreflect.FieldNumber) bool }) { var bo protoreflect.RawFields for bi := m.GetUnknown(); len(bi) > 0; { num, _, n := protowire.ConsumeField(bi) if !remover.Has(num) { bo = append(bo, bi[:n]...) } bi = bi[n:] } if bi := m.GetUnknown(); len(bi) != len(bo) { m.SetUnknown(bo) } } type fieldNum protoreflect.FieldNumber func (n1 fieldNum) Has(n2 protoreflect.FieldNumber) bool { return protoreflect.FieldNumber(n1) == n2 } ================================================ FILE: vendor/github.com/golang/protobuf/proto/properties.go ================================================ // Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package proto import ( "fmt" "reflect" "strconv" "strings" "sync" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/runtime/protoimpl" ) // StructProperties represents protocol buffer type information for a // generated protobuf message in the open-struct API. // // Deprecated: Do not use. type StructProperties struct { // Prop are the properties for each field. // // Fields belonging to a oneof are stored in OneofTypes instead, with a // single Properties representing the parent oneof held here. // // The order of Prop matches the order of fields in the Go struct. // Struct fields that are not related to protobufs have a "XXX_" prefix // in the Properties.Name and must be ignored by the user. Prop []*Properties // OneofTypes contains information about the oneof fields in this message. // It is keyed by the protobuf field name. OneofTypes map[string]*OneofProperties } // Properties represents the type information for a protobuf message field. // // Deprecated: Do not use. type Properties struct { // Name is a placeholder name with little meaningful semantic value. // If the name has an "XXX_" prefix, the entire Properties must be ignored. Name string // OrigName is the protobuf field name or oneof name. OrigName string // JSONName is the JSON name for the protobuf field. JSONName string // Enum is a placeholder name for enums. // For historical reasons, this is neither the Go name for the enum, // nor the protobuf name for the enum. Enum string // Deprecated: Do not use. // Weak contains the full name of the weakly referenced message. Weak string // Wire is a string representation of the wire type. Wire string // WireType is the protobuf wire type for the field. WireType int // Tag is the protobuf field number. Tag int // Required reports whether this is a required field. Required bool // Optional reports whether this is a optional field. Optional bool // Repeated reports whether this is a repeated field. Repeated bool // Packed reports whether this is a packed repeated field of scalars. Packed bool // Proto3 reports whether this field operates under the proto3 syntax. Proto3 bool // Oneof reports whether this field belongs within a oneof. Oneof bool // Default is the default value in string form. Default string // HasDefault reports whether the field has a default value. HasDefault bool // MapKeyProp is the properties for the key field for a map field. MapKeyProp *Properties // MapValProp is the properties for the value field for a map field. MapValProp *Properties } // OneofProperties represents the type information for a protobuf oneof. // // Deprecated: Do not use. type OneofProperties struct { // Type is a pointer to the generated wrapper type for the field value. // This is nil for messages that are not in the open-struct API. Type reflect.Type // Field is the index into StructProperties.Prop for the containing oneof. Field int // Prop is the properties for the field. Prop *Properties } // String formats the properties in the protobuf struct field tag style. func (p *Properties) String() string { s := p.Wire s += "," + strconv.Itoa(p.Tag) if p.Required { s += ",req" } if p.Optional { s += ",opt" } if p.Repeated { s += ",rep" } if p.Packed { s += ",packed" } s += ",name=" + p.OrigName if p.JSONName != "" { s += ",json=" + p.JSONName } if len(p.Enum) > 0 { s += ",enum=" + p.Enum } if len(p.Weak) > 0 { s += ",weak=" + p.Weak } if p.Proto3 { s += ",proto3" } if p.Oneof { s += ",oneof" } if p.HasDefault { s += ",def=" + p.Default } return s } // Parse populates p by parsing a string in the protobuf struct field tag style. func (p *Properties) Parse(tag string) { // For example: "bytes,49,opt,name=foo,def=hello!" for len(tag) > 0 { i := strings.IndexByte(tag, ',') if i < 0 { i = len(tag) } switch s := tag[:i]; { case strings.HasPrefix(s, "name="): p.OrigName = s[len("name="):] case strings.HasPrefix(s, "json="): p.JSONName = s[len("json="):] case strings.HasPrefix(s, "enum="): p.Enum = s[len("enum="):] case strings.HasPrefix(s, "weak="): p.Weak = s[len("weak="):] case strings.Trim(s, "0123456789") == "": n, _ := strconv.ParseUint(s, 10, 32) p.Tag = int(n) case s == "opt": p.Optional = true case s == "req": p.Required = true case s == "rep": p.Repeated = true case s == "varint" || s == "zigzag32" || s == "zigzag64": p.Wire = s p.WireType = WireVarint case s == "fixed32": p.Wire = s p.WireType = WireFixed32 case s == "fixed64": p.Wire = s p.WireType = WireFixed64 case s == "bytes": p.Wire = s p.WireType = WireBytes case s == "group": p.Wire = s p.WireType = WireStartGroup case s == "packed": p.Packed = true case s == "proto3": p.Proto3 = true case s == "oneof": p.Oneof = true case strings.HasPrefix(s, "def="): // The default tag is special in that everything afterwards is the // default regardless of the presence of commas. p.HasDefault = true p.Default, i = tag[len("def="):], len(tag) } tag = strings.TrimPrefix(tag[i:], ",") } } // Init populates the properties from a protocol buffer struct tag. // // Deprecated: Do not use. func (p *Properties) Init(typ reflect.Type, name, tag string, f *reflect.StructField) { p.Name = name p.OrigName = name if tag == "" { return } p.Parse(tag) if typ != nil && typ.Kind() == reflect.Map { p.MapKeyProp = new(Properties) p.MapKeyProp.Init(nil, "Key", f.Tag.Get("protobuf_key"), nil) p.MapValProp = new(Properties) p.MapValProp.Init(nil, "Value", f.Tag.Get("protobuf_val"), nil) } } var propertiesCache sync.Map // map[reflect.Type]*StructProperties // GetProperties returns the list of properties for the type represented by t, // which must be a generated protocol buffer message in the open-struct API, // where protobuf message fields are represented by exported Go struct fields. // // Deprecated: Use protobuf reflection instead. func GetProperties(t reflect.Type) *StructProperties { if p, ok := propertiesCache.Load(t); ok { return p.(*StructProperties) } p, _ := propertiesCache.LoadOrStore(t, newProperties(t)) return p.(*StructProperties) } func newProperties(t reflect.Type) *StructProperties { if t.Kind() != reflect.Struct { panic(fmt.Sprintf("%v is not a generated message in the open-struct API", t)) } var hasOneof bool prop := new(StructProperties) // Construct a list of properties for each field in the struct. for i := 0; i < t.NumField(); i++ { p := new(Properties) f := t.Field(i) tagField := f.Tag.Get("protobuf") p.Init(f.Type, f.Name, tagField, &f) tagOneof := f.Tag.Get("protobuf_oneof") if tagOneof != "" { hasOneof = true p.OrigName = tagOneof } // Rename unrelated struct fields with the "XXX_" prefix since so much // user code simply checks for this to exclude special fields. if tagField == "" && tagOneof == "" && !strings.HasPrefix(p.Name, "XXX_") { p.Name = "XXX_" + p.Name p.OrigName = "XXX_" + p.OrigName } else if p.Weak != "" { p.Name = p.OrigName // avoid possible "XXX_" prefix on weak field } prop.Prop = append(prop.Prop, p) } // Construct a mapping of oneof field names to properties. if hasOneof { var oneofWrappers []interface{} if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofFuncs"); ok { oneofWrappers = fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[3].Interface().([]interface{}) } if fn, ok := reflect.PtrTo(t).MethodByName("XXX_OneofWrappers"); ok { oneofWrappers = fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))})[0].Interface().([]interface{}) } if m, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(protoreflect.ProtoMessage); ok { if m, ok := m.ProtoReflect().(interface{ ProtoMessageInfo() *protoimpl.MessageInfo }); ok { oneofWrappers = m.ProtoMessageInfo().OneofWrappers } } prop.OneofTypes = make(map[string]*OneofProperties) for _, wrapper := range oneofWrappers { p := &OneofProperties{ Type: reflect.ValueOf(wrapper).Type(), // *T Prop: new(Properties), } f := p.Type.Elem().Field(0) p.Prop.Name = f.Name p.Prop.Parse(f.Tag.Get("protobuf")) // Determine the struct field that contains this oneof. // Each wrapper is assignable to exactly one parent field. var foundOneof bool for i := 0; i < t.NumField() && !foundOneof; i++ { if p.Type.AssignableTo(t.Field(i).Type) { p.Field = i foundOneof = true } } if !foundOneof { panic(fmt.Sprintf("%v is not a generated message in the open-struct API", t)) } prop.OneofTypes[p.Prop.OrigName] = p } } return prop } func (sp *StructProperties) Len() int { return len(sp.Prop) } func (sp *StructProperties) Less(i, j int) bool { return false } func (sp *StructProperties) Swap(i, j int) { return } ================================================ FILE: vendor/github.com/golang/protobuf/proto/proto.go ================================================ // Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package proto provides functionality for handling protocol buffer messages. // In particular, it provides marshaling and unmarshaling between a protobuf // message and the binary wire format. // // See https://developers.google.com/protocol-buffers/docs/gotutorial for // more information. // // Deprecated: Use the "google.golang.org/protobuf/proto" package instead. package proto import ( protoV2 "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/runtime/protoiface" "google.golang.org/protobuf/runtime/protoimpl" ) const ( ProtoPackageIsVersion1 = true ProtoPackageIsVersion2 = true ProtoPackageIsVersion3 = true ProtoPackageIsVersion4 = true ) // GeneratedEnum is any enum type generated by protoc-gen-go // which is a named int32 kind. // This type exists for documentation purposes. type GeneratedEnum interface{} // GeneratedMessage is any message type generated by protoc-gen-go // which is a pointer to a named struct kind. // This type exists for documentation purposes. type GeneratedMessage interface{} // Message is a protocol buffer message. // // This is the v1 version of the message interface and is marginally better // than an empty interface as it lacks any method to programatically interact // with the contents of the message. // // A v2 message is declared in "google.golang.org/protobuf/proto".Message and // exposes protobuf reflection as a first-class feature of the interface. // // To convert a v1 message to a v2 message, use the MessageV2 function. // To convert a v2 message to a v1 message, use the MessageV1 function. type Message = protoiface.MessageV1 // MessageV1 converts either a v1 or v2 message to a v1 message. // It returns nil if m is nil. func MessageV1(m GeneratedMessage) protoiface.MessageV1 { return protoimpl.X.ProtoMessageV1Of(m) } // MessageV2 converts either a v1 or v2 message to a v2 message. // It returns nil if m is nil. func MessageV2(m GeneratedMessage) protoV2.Message { return protoimpl.X.ProtoMessageV2Of(m) } // MessageReflect returns a reflective view for a message. // It returns nil if m is nil. func MessageReflect(m Message) protoreflect.Message { return protoimpl.X.MessageOf(m) } // Marshaler is implemented by messages that can marshal themselves. // This interface is used by the following functions: Size, Marshal, // Buffer.Marshal, and Buffer.EncodeMessage. // // Deprecated: Do not implement. type Marshaler interface { // Marshal formats the encoded bytes of the message. // It should be deterministic and emit valid protobuf wire data. // The caller takes ownership of the returned buffer. Marshal() ([]byte, error) } // Unmarshaler is implemented by messages that can unmarshal themselves. // This interface is used by the following functions: Unmarshal, UnmarshalMerge, // Buffer.Unmarshal, Buffer.DecodeMessage, and Buffer.DecodeGroup. // // Deprecated: Do not implement. type Unmarshaler interface { // Unmarshal parses the encoded bytes of the protobuf wire input. // The provided buffer is only valid for during method call. // It should not reset the receiver message. Unmarshal([]byte) error } // Merger is implemented by messages that can merge themselves. // This interface is used by the following functions: Clone and Merge. // // Deprecated: Do not implement. type Merger interface { // Merge merges the contents of src into the receiver message. // It clones all data structures in src such that it aliases no mutable // memory referenced by src. Merge(src Message) } // RequiredNotSetError is an error type returned when // marshaling or unmarshaling a message with missing required fields. type RequiredNotSetError struct { err error } func (e *RequiredNotSetError) Error() string { if e.err != nil { return e.err.Error() } return "proto: required field not set" } func (e *RequiredNotSetError) RequiredNotSet() bool { return true } func checkRequiredNotSet(m protoV2.Message) error { if err := protoV2.CheckInitialized(m); err != nil { return &RequiredNotSetError{err: err} } return nil } // Clone returns a deep copy of src. func Clone(src Message) Message { return MessageV1(protoV2.Clone(MessageV2(src))) } // Merge merges src into dst, which must be messages of the same type. // // Populated scalar fields in src are copied to dst, while populated // singular messages in src are merged into dst by recursively calling Merge. // The elements of every list field in src is appended to the corresponded // list fields in dst. The entries of every map field in src is copied into // the corresponding map field in dst, possibly replacing existing entries. // The unknown fields of src are appended to the unknown fields of dst. func Merge(dst, src Message) { protoV2.Merge(MessageV2(dst), MessageV2(src)) } // Equal reports whether two messages are equal. // If two messages marshal to the same bytes under deterministic serialization, // then Equal is guaranteed to report true. // // Two messages are equal if they are the same protobuf message type, // have the same set of populated known and extension field values, // and the same set of unknown fields values. // // Scalar values are compared with the equivalent of the == operator in Go, // except bytes values which are compared using bytes.Equal and // floating point values which specially treat NaNs as equal. // Message values are compared by recursively calling Equal. // Lists are equal if each element value is also equal. // Maps are equal if they have the same set of keys, where the pair of values // for each key is also equal. func Equal(x, y Message) bool { return protoV2.Equal(MessageV2(x), MessageV2(y)) } func isMessageSet(md protoreflect.MessageDescriptor) bool { ms, ok := md.(interface{ IsMessageSet() bool }) return ok && ms.IsMessageSet() } ================================================ FILE: vendor/github.com/golang/protobuf/proto/registry.go ================================================ // Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package proto import ( "bytes" "compress/gzip" "fmt" "io/ioutil" "reflect" "strings" "sync" "google.golang.org/protobuf/reflect/protodesc" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/reflect/protoregistry" "google.golang.org/protobuf/runtime/protoimpl" ) // filePath is the path to the proto source file. type filePath = string // e.g., "google/protobuf/descriptor.proto" // fileDescGZIP is the compressed contents of the encoded FileDescriptorProto. type fileDescGZIP = []byte var fileCache sync.Map // map[filePath]fileDescGZIP // RegisterFile is called from generated code to register the compressed // FileDescriptorProto with the file path for a proto source file. // // Deprecated: Use protoregistry.GlobalFiles.RegisterFile instead. func RegisterFile(s filePath, d fileDescGZIP) { // Decompress the descriptor. zr, err := gzip.NewReader(bytes.NewReader(d)) if err != nil { panic(fmt.Sprintf("proto: invalid compressed file descriptor: %v", err)) } b, err := ioutil.ReadAll(zr) if err != nil { panic(fmt.Sprintf("proto: invalid compressed file descriptor: %v", err)) } // Construct a protoreflect.FileDescriptor from the raw descriptor. // Note that DescBuilder.Build automatically registers the constructed // file descriptor with the v2 registry. protoimpl.DescBuilder{RawDescriptor: b}.Build() // Locally cache the raw descriptor form for the file. fileCache.Store(s, d) } // FileDescriptor returns the compressed FileDescriptorProto given the file path // for a proto source file. It returns nil if not found. // // Deprecated: Use protoregistry.GlobalFiles.FindFileByPath instead. func FileDescriptor(s filePath) fileDescGZIP { if v, ok := fileCache.Load(s); ok { return v.(fileDescGZIP) } // Find the descriptor in the v2 registry. var b []byte if fd, _ := protoregistry.GlobalFiles.FindFileByPath(s); fd != nil { b, _ = Marshal(protodesc.ToFileDescriptorProto(fd)) } // Locally cache the raw descriptor form for the file. if len(b) > 0 { v, _ := fileCache.LoadOrStore(s, protoimpl.X.CompressGZIP(b)) return v.(fileDescGZIP) } return nil } // enumName is the name of an enum. For historical reasons, the enum name is // neither the full Go name nor the full protobuf name of the enum. // The name is the dot-separated combination of just the proto package that the // enum is declared within followed by the Go type name of the generated enum. type enumName = string // e.g., "my.proto.package.GoMessage_GoEnum" // enumsByName maps enum values by name to their numeric counterpart. type enumsByName = map[string]int32 // enumsByNumber maps enum values by number to their name counterpart. type enumsByNumber = map[int32]string var enumCache sync.Map // map[enumName]enumsByName var numFilesCache sync.Map // map[protoreflect.FullName]int // RegisterEnum is called from the generated code to register the mapping of // enum value names to enum numbers for the enum identified by s. // // Deprecated: Use protoregistry.GlobalTypes.RegisterEnum instead. func RegisterEnum(s enumName, _ enumsByNumber, m enumsByName) { if _, ok := enumCache.Load(s); ok { panic("proto: duplicate enum registered: " + s) } enumCache.Store(s, m) // This does not forward registration to the v2 registry since this API // lacks sufficient information to construct a complete v2 enum descriptor. } // EnumValueMap returns the mapping from enum value names to enum numbers for // the enum of the given name. It returns nil if not found. // // Deprecated: Use protoregistry.GlobalTypes.FindEnumByName instead. func EnumValueMap(s enumName) enumsByName { if v, ok := enumCache.Load(s); ok { return v.(enumsByName) } // Check whether the cache is stale. If the number of files in the current // package differs, then it means that some enums may have been recently // registered upstream that we do not know about. var protoPkg protoreflect.FullName if i := strings.LastIndexByte(s, '.'); i >= 0 { protoPkg = protoreflect.FullName(s[:i]) } v, _ := numFilesCache.Load(protoPkg) numFiles, _ := v.(int) if protoregistry.GlobalFiles.NumFilesByPackage(protoPkg) == numFiles { return nil // cache is up-to-date; was not found earlier } // Update the enum cache for all enums declared in the given proto package. numFiles = 0 protoregistry.GlobalFiles.RangeFilesByPackage(protoPkg, func(fd protoreflect.FileDescriptor) bool { walkEnums(fd, func(ed protoreflect.EnumDescriptor) { name := protoimpl.X.LegacyEnumName(ed) if _, ok := enumCache.Load(name); !ok { m := make(enumsByName) evs := ed.Values() for i := evs.Len() - 1; i >= 0; i-- { ev := evs.Get(i) m[string(ev.Name())] = int32(ev.Number()) } enumCache.LoadOrStore(name, m) } }) numFiles++ return true }) numFilesCache.Store(protoPkg, numFiles) // Check cache again for enum map. if v, ok := enumCache.Load(s); ok { return v.(enumsByName) } return nil } // walkEnums recursively walks all enums declared in d. func walkEnums(d interface { Enums() protoreflect.EnumDescriptors Messages() protoreflect.MessageDescriptors }, f func(protoreflect.EnumDescriptor)) { eds := d.Enums() for i := eds.Len() - 1; i >= 0; i-- { f(eds.Get(i)) } mds := d.Messages() for i := mds.Len() - 1; i >= 0; i-- { walkEnums(mds.Get(i), f) } } // messageName is the full name of protobuf message. type messageName = string var messageTypeCache sync.Map // map[messageName]reflect.Type // RegisterType is called from generated code to register the message Go type // for a message of the given name. // // Deprecated: Use protoregistry.GlobalTypes.RegisterMessage instead. func RegisterType(m Message, s messageName) { mt := protoimpl.X.LegacyMessageTypeOf(m, protoreflect.FullName(s)) if err := protoregistry.GlobalTypes.RegisterMessage(mt); err != nil { panic(err) } messageTypeCache.Store(s, reflect.TypeOf(m)) } // RegisterMapType is called from generated code to register the Go map type // for a protobuf message representing a map entry. // // Deprecated: Do not use. func RegisterMapType(m interface{}, s messageName) { t := reflect.TypeOf(m) if t.Kind() != reflect.Map { panic(fmt.Sprintf("invalid map kind: %v", t)) } if _, ok := messageTypeCache.Load(s); ok { panic(fmt.Errorf("proto: duplicate proto message registered: %s", s)) } messageTypeCache.Store(s, t) } // MessageType returns the message type for a named message. // It returns nil if not found. // // Deprecated: Use protoregistry.GlobalTypes.FindMessageByName instead. func MessageType(s messageName) reflect.Type { if v, ok := messageTypeCache.Load(s); ok { return v.(reflect.Type) } // Derive the message type from the v2 registry. var t reflect.Type if mt, _ := protoregistry.GlobalTypes.FindMessageByName(protoreflect.FullName(s)); mt != nil { t = messageGoType(mt) } // If we could not get a concrete type, it is possible that it is a // pseudo-message for a map entry. if t == nil { d, _ := protoregistry.GlobalFiles.FindDescriptorByName(protoreflect.FullName(s)) if md, _ := d.(protoreflect.MessageDescriptor); md != nil && md.IsMapEntry() { kt := goTypeForField(md.Fields().ByNumber(1)) vt := goTypeForField(md.Fields().ByNumber(2)) t = reflect.MapOf(kt, vt) } } // Locally cache the message type for the given name. if t != nil { v, _ := messageTypeCache.LoadOrStore(s, t) return v.(reflect.Type) } return nil } func goTypeForField(fd protoreflect.FieldDescriptor) reflect.Type { switch k := fd.Kind(); k { case protoreflect.EnumKind: if et, _ := protoregistry.GlobalTypes.FindEnumByName(fd.Enum().FullName()); et != nil { return enumGoType(et) } return reflect.TypeOf(protoreflect.EnumNumber(0)) case protoreflect.MessageKind, protoreflect.GroupKind: if mt, _ := protoregistry.GlobalTypes.FindMessageByName(fd.Message().FullName()); mt != nil { return messageGoType(mt) } return reflect.TypeOf((*protoreflect.Message)(nil)).Elem() default: return reflect.TypeOf(fd.Default().Interface()) } } func enumGoType(et protoreflect.EnumType) reflect.Type { return reflect.TypeOf(et.New(0)) } func messageGoType(mt protoreflect.MessageType) reflect.Type { return reflect.TypeOf(MessageV1(mt.Zero().Interface())) } // MessageName returns the full protobuf name for the given message type. // // Deprecated: Use protoreflect.MessageDescriptor.FullName instead. func MessageName(m Message) messageName { if m == nil { return "" } if m, ok := m.(interface{ XXX_MessageName() messageName }); ok { return m.XXX_MessageName() } return messageName(protoimpl.X.MessageDescriptorOf(m).FullName()) } // RegisterExtension is called from the generated code to register // the extension descriptor. // // Deprecated: Use protoregistry.GlobalTypes.RegisterExtension instead. func RegisterExtension(d *ExtensionDesc) { if err := protoregistry.GlobalTypes.RegisterExtension(d); err != nil { panic(err) } } type extensionsByNumber = map[int32]*ExtensionDesc var extensionCache sync.Map // map[messageName]extensionsByNumber // RegisteredExtensions returns a map of the registered extensions for the // provided protobuf message, indexed by the extension field number. // // Deprecated: Use protoregistry.GlobalTypes.RangeExtensionsByMessage instead. func RegisteredExtensions(m Message) extensionsByNumber { // Check whether the cache is stale. If the number of extensions for // the given message differs, then it means that some extensions were // recently registered upstream that we do not know about. s := MessageName(m) v, _ := extensionCache.Load(s) xs, _ := v.(extensionsByNumber) if protoregistry.GlobalTypes.NumExtensionsByMessage(protoreflect.FullName(s)) == len(xs) { return xs // cache is up-to-date } // Cache is stale, re-compute the extensions map. xs = make(extensionsByNumber) protoregistry.GlobalTypes.RangeExtensionsByMessage(protoreflect.FullName(s), func(xt protoreflect.ExtensionType) bool { if xd, ok := xt.(*ExtensionDesc); ok { xs[int32(xt.TypeDescriptor().Number())] = xd } else { // TODO: This implies that the protoreflect.ExtensionType is a // custom type not generated by protoc-gen-go. We could try and // convert the type to an ExtensionDesc. } return true }) extensionCache.Store(s, xs) return xs } ================================================ FILE: vendor/github.com/golang/protobuf/proto/text_decode.go ================================================ // Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package proto import ( "encoding" "errors" "fmt" "reflect" "strconv" "strings" "unicode/utf8" "google.golang.org/protobuf/encoding/prototext" protoV2 "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/reflect/protoregistry" ) const wrapTextUnmarshalV2 = false // ParseError is returned by UnmarshalText. type ParseError struct { Message string // Deprecated: Do not use. Line, Offset int } func (e *ParseError) Error() string { if wrapTextUnmarshalV2 { return e.Message } if e.Line == 1 { return fmt.Sprintf("line 1.%d: %v", e.Offset, e.Message) } return fmt.Sprintf("line %d: %v", e.Line, e.Message) } // UnmarshalText parses a proto text formatted string into m. func UnmarshalText(s string, m Message) error { if u, ok := m.(encoding.TextUnmarshaler); ok { return u.UnmarshalText([]byte(s)) } m.Reset() mi := MessageV2(m) if wrapTextUnmarshalV2 { err := prototext.UnmarshalOptions{ AllowPartial: true, }.Unmarshal([]byte(s), mi) if err != nil { return &ParseError{Message: err.Error()} } return checkRequiredNotSet(mi) } else { if err := newTextParser(s).unmarshalMessage(mi.ProtoReflect(), ""); err != nil { return err } return checkRequiredNotSet(mi) } } type textParser struct { s string // remaining input done bool // whether the parsing is finished (success or error) backed bool // whether back() was called offset, line int cur token } type token struct { value string err *ParseError line int // line number offset int // byte number from start of input, not start of line unquoted string // the unquoted version of value, if it was a quoted string } func newTextParser(s string) *textParser { p := new(textParser) p.s = s p.line = 1 p.cur.line = 1 return p } func (p *textParser) unmarshalMessage(m protoreflect.Message, terminator string) (err error) { md := m.Descriptor() fds := md.Fields() // A struct is a sequence of "name: value", terminated by one of // '>' or '}', or the end of the input. A name may also be // "[extension]" or "[type/url]". // // The whole struct can also be an expanded Any message, like: // [type/url] < ... struct contents ... > seen := make(map[protoreflect.FieldNumber]bool) for { tok := p.next() if tok.err != nil { return tok.err } if tok.value == terminator { break } if tok.value == "[" { if err := p.unmarshalExtensionOrAny(m, seen); err != nil { return err } continue } // This is a normal, non-extension field. name := protoreflect.Name(tok.value) fd := fds.ByName(name) switch { case fd == nil: gd := fds.ByName(protoreflect.Name(strings.ToLower(string(name)))) if gd != nil && gd.Kind() == protoreflect.GroupKind && gd.Message().Name() == name { fd = gd } case fd.Kind() == protoreflect.GroupKind && fd.Message().Name() != name: fd = nil case fd.IsWeak() && fd.Message().IsPlaceholder(): fd = nil } if fd == nil { typeName := string(md.FullName()) if m, ok := m.Interface().(Message); ok { t := reflect.TypeOf(m) if t.Kind() == reflect.Ptr { typeName = t.Elem().String() } } return p.errorf("unknown field name %q in %v", name, typeName) } if od := fd.ContainingOneof(); od != nil && m.WhichOneof(od) != nil { return p.errorf("field '%s' would overwrite already parsed oneof '%s'", name, od.Name()) } if fd.Cardinality() != protoreflect.Repeated && seen[fd.Number()] { return p.errorf("non-repeated field %q was repeated", fd.Name()) } seen[fd.Number()] = true // Consume any colon. if err := p.checkForColon(fd); err != nil { return err } // Parse into the field. v := m.Get(fd) if !m.Has(fd) && (fd.IsList() || fd.IsMap() || fd.Message() != nil) { v = m.Mutable(fd) } if v, err = p.unmarshalValue(v, fd); err != nil { return err } m.Set(fd, v) if err := p.consumeOptionalSeparator(); err != nil { return err } } return nil } func (p *textParser) unmarshalExtensionOrAny(m protoreflect.Message, seen map[protoreflect.FieldNumber]bool) error { name, err := p.consumeExtensionOrAnyName() if err != nil { return err } // If it contains a slash, it's an Any type URL. if slashIdx := strings.LastIndex(name, "/"); slashIdx >= 0 { tok := p.next() if tok.err != nil { return tok.err } // consume an optional colon if tok.value == ":" { tok = p.next() if tok.err != nil { return tok.err } } var terminator string switch tok.value { case "<": terminator = ">" case "{": terminator = "}" default: return p.errorf("expected '{' or '<', found %q", tok.value) } mt, err := protoregistry.GlobalTypes.FindMessageByURL(name) if err != nil { return p.errorf("unrecognized message %q in google.protobuf.Any", name[slashIdx+len("/"):]) } m2 := mt.New() if err := p.unmarshalMessage(m2, terminator); err != nil { return err } b, err := protoV2.Marshal(m2.Interface()) if err != nil { return p.errorf("failed to marshal message of type %q: %v", name[slashIdx+len("/"):], err) } urlFD := m.Descriptor().Fields().ByName("type_url") valFD := m.Descriptor().Fields().ByName("value") if seen[urlFD.Number()] { return p.errorf("Any message unpacked multiple times, or %q already set", urlFD.Name()) } if seen[valFD.Number()] { return p.errorf("Any message unpacked multiple times, or %q already set", valFD.Name()) } m.Set(urlFD, protoreflect.ValueOfString(name)) m.Set(valFD, protoreflect.ValueOfBytes(b)) seen[urlFD.Number()] = true seen[valFD.Number()] = true return nil } xname := protoreflect.FullName(name) xt, _ := protoregistry.GlobalTypes.FindExtensionByName(xname) if xt == nil && isMessageSet(m.Descriptor()) { xt, _ = protoregistry.GlobalTypes.FindExtensionByName(xname.Append("message_set_extension")) } if xt == nil { return p.errorf("unrecognized extension %q", name) } fd := xt.TypeDescriptor() if fd.ContainingMessage().FullName() != m.Descriptor().FullName() { return p.errorf("extension field %q does not extend message %q", name, m.Descriptor().FullName()) } if err := p.checkForColon(fd); err != nil { return err } v := m.Get(fd) if !m.Has(fd) && (fd.IsList() || fd.IsMap() || fd.Message() != nil) { v = m.Mutable(fd) } v, err = p.unmarshalValue(v, fd) if err != nil { return err } m.Set(fd, v) return p.consumeOptionalSeparator() } func (p *textParser) unmarshalValue(v protoreflect.Value, fd protoreflect.FieldDescriptor) (protoreflect.Value, error) { tok := p.next() if tok.err != nil { return v, tok.err } if tok.value == "" { return v, p.errorf("unexpected EOF") } switch { case fd.IsList(): lv := v.List() var err error if tok.value == "[" { // Repeated field with list notation, like [1,2,3]. for { vv := lv.NewElement() vv, err = p.unmarshalSingularValue(vv, fd) if err != nil { return v, err } lv.Append(vv) tok := p.next() if tok.err != nil { return v, tok.err } if tok.value == "]" { break } if tok.value != "," { return v, p.errorf("Expected ']' or ',' found %q", tok.value) } } return v, nil } // One value of the repeated field. p.back() vv := lv.NewElement() vv, err = p.unmarshalSingularValue(vv, fd) if err != nil { return v, err } lv.Append(vv) return v, nil case fd.IsMap(): // The map entry should be this sequence of tokens: // < key : KEY value : VALUE > // However, implementations may omit key or value, and technically // we should support them in any order. var terminator string switch tok.value { case "<": terminator = ">" case "{": terminator = "}" default: return v, p.errorf("expected '{' or '<', found %q", tok.value) } keyFD := fd.MapKey() valFD := fd.MapValue() mv := v.Map() kv := keyFD.Default() vv := mv.NewValue() for { tok := p.next() if tok.err != nil { return v, tok.err } if tok.value == terminator { break } var err error switch tok.value { case "key": if err := p.consumeToken(":"); err != nil { return v, err } if kv, err = p.unmarshalSingularValue(kv, keyFD); err != nil { return v, err } if err := p.consumeOptionalSeparator(); err != nil { return v, err } case "value": if err := p.checkForColon(valFD); err != nil { return v, err } if vv, err = p.unmarshalSingularValue(vv, valFD); err != nil { return v, err } if err := p.consumeOptionalSeparator(); err != nil { return v, err } default: p.back() return v, p.errorf(`expected "key", "value", or %q, found %q`, terminator, tok.value) } } mv.Set(kv.MapKey(), vv) return v, nil default: p.back() return p.unmarshalSingularValue(v, fd) } } func (p *textParser) unmarshalSingularValue(v protoreflect.Value, fd protoreflect.FieldDescriptor) (protoreflect.Value, error) { tok := p.next() if tok.err != nil { return v, tok.err } if tok.value == "" { return v, p.errorf("unexpected EOF") } switch fd.Kind() { case protoreflect.BoolKind: switch tok.value { case "true", "1", "t", "True": return protoreflect.ValueOfBool(true), nil case "false", "0", "f", "False": return protoreflect.ValueOfBool(false), nil } case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil { return protoreflect.ValueOfInt32(int32(x)), nil } // The C++ parser accepts large positive hex numbers that uses // two's complement arithmetic to represent negative numbers. // This feature is here for backwards compatibility with C++. if strings.HasPrefix(tok.value, "0x") { if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil { return protoreflect.ValueOfInt32(int32(-(int64(^x) + 1))), nil } } case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: if x, err := strconv.ParseInt(tok.value, 0, 64); err == nil { return protoreflect.ValueOfInt64(int64(x)), nil } // The C++ parser accepts large positive hex numbers that uses // two's complement arithmetic to represent negative numbers. // This feature is here for backwards compatibility with C++. if strings.HasPrefix(tok.value, "0x") { if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil { return protoreflect.ValueOfInt64(int64(-(int64(^x) + 1))), nil } } case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil { return protoreflect.ValueOfUint32(uint32(x)), nil } case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil { return protoreflect.ValueOfUint64(uint64(x)), nil } case protoreflect.FloatKind: // Ignore 'f' for compatibility with output generated by C++, // but don't remove 'f' when the value is "-inf" or "inf". v := tok.value if strings.HasSuffix(v, "f") && v != "-inf" && v != "inf" { v = v[:len(v)-len("f")] } if x, err := strconv.ParseFloat(v, 32); err == nil { return protoreflect.ValueOfFloat32(float32(x)), nil } case protoreflect.DoubleKind: // Ignore 'f' for compatibility with output generated by C++, // but don't remove 'f' when the value is "-inf" or "inf". v := tok.value if strings.HasSuffix(v, "f") && v != "-inf" && v != "inf" { v = v[:len(v)-len("f")] } if x, err := strconv.ParseFloat(v, 64); err == nil { return protoreflect.ValueOfFloat64(float64(x)), nil } case protoreflect.StringKind: if isQuote(tok.value[0]) { return protoreflect.ValueOfString(tok.unquoted), nil } case protoreflect.BytesKind: if isQuote(tok.value[0]) { return protoreflect.ValueOfBytes([]byte(tok.unquoted)), nil } case protoreflect.EnumKind: if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil { return protoreflect.ValueOfEnum(protoreflect.EnumNumber(x)), nil } vd := fd.Enum().Values().ByName(protoreflect.Name(tok.value)) if vd != nil { return protoreflect.ValueOfEnum(vd.Number()), nil } case protoreflect.MessageKind, protoreflect.GroupKind: var terminator string switch tok.value { case "{": terminator = "}" case "<": terminator = ">" default: return v, p.errorf("expected '{' or '<', found %q", tok.value) } err := p.unmarshalMessage(v.Message(), terminator) return v, err default: panic(fmt.Sprintf("invalid kind %v", fd.Kind())) } return v, p.errorf("invalid %v: %v", fd.Kind(), tok.value) } // Consume a ':' from the input stream (if the next token is a colon), // returning an error if a colon is needed but not present. func (p *textParser) checkForColon(fd protoreflect.FieldDescriptor) *ParseError { tok := p.next() if tok.err != nil { return tok.err } if tok.value != ":" { if fd.Message() == nil { return p.errorf("expected ':', found %q", tok.value) } p.back() } return nil } // consumeExtensionOrAnyName consumes an extension name or an Any type URL and // the following ']'. It returns the name or URL consumed. func (p *textParser) consumeExtensionOrAnyName() (string, error) { tok := p.next() if tok.err != nil { return "", tok.err } // If extension name or type url is quoted, it's a single token. if len(tok.value) > 2 && isQuote(tok.value[0]) && tok.value[len(tok.value)-1] == tok.value[0] { name, err := unquoteC(tok.value[1:len(tok.value)-1], rune(tok.value[0])) if err != nil { return "", err } return name, p.consumeToken("]") } // Consume everything up to "]" var parts []string for tok.value != "]" { parts = append(parts, tok.value) tok = p.next() if tok.err != nil { return "", p.errorf("unrecognized type_url or extension name: %s", tok.err) } if p.done && tok.value != "]" { return "", p.errorf("unclosed type_url or extension name") } } return strings.Join(parts, ""), nil } // consumeOptionalSeparator consumes an optional semicolon or comma. // It is used in unmarshalMessage to provide backward compatibility. func (p *textParser) consumeOptionalSeparator() error { tok := p.next() if tok.err != nil { return tok.err } if tok.value != ";" && tok.value != "," { p.back() } return nil } func (p *textParser) errorf(format string, a ...interface{}) *ParseError { pe := &ParseError{fmt.Sprintf(format, a...), p.cur.line, p.cur.offset} p.cur.err = pe p.done = true return pe } func (p *textParser) skipWhitespace() { i := 0 for i < len(p.s) && (isWhitespace(p.s[i]) || p.s[i] == '#') { if p.s[i] == '#' { // comment; skip to end of line or input for i < len(p.s) && p.s[i] != '\n' { i++ } if i == len(p.s) { break } } if p.s[i] == '\n' { p.line++ } i++ } p.offset += i p.s = p.s[i:len(p.s)] if len(p.s) == 0 { p.done = true } } func (p *textParser) advance() { // Skip whitespace p.skipWhitespace() if p.done { return } // Start of non-whitespace p.cur.err = nil p.cur.offset, p.cur.line = p.offset, p.line p.cur.unquoted = "" switch p.s[0] { case '<', '>', '{', '}', ':', '[', ']', ';', ',', '/': // Single symbol p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)] case '"', '\'': // Quoted string i := 1 for i < len(p.s) && p.s[i] != p.s[0] && p.s[i] != '\n' { if p.s[i] == '\\' && i+1 < len(p.s) { // skip escaped char i++ } i++ } if i >= len(p.s) || p.s[i] != p.s[0] { p.errorf("unmatched quote") return } unq, err := unquoteC(p.s[1:i], rune(p.s[0])) if err != nil { p.errorf("invalid quoted string %s: %v", p.s[0:i+1], err) return } p.cur.value, p.s = p.s[0:i+1], p.s[i+1:len(p.s)] p.cur.unquoted = unq default: i := 0 for i < len(p.s) && isIdentOrNumberChar(p.s[i]) { i++ } if i == 0 { p.errorf("unexpected byte %#x", p.s[0]) return } p.cur.value, p.s = p.s[0:i], p.s[i:len(p.s)] } p.offset += len(p.cur.value) } // Back off the parser by one token. Can only be done between calls to next(). // It makes the next advance() a no-op. func (p *textParser) back() { p.backed = true } // Advances the parser and returns the new current token. func (p *textParser) next() *token { if p.backed || p.done { p.backed = false return &p.cur } p.advance() if p.done { p.cur.value = "" } else if len(p.cur.value) > 0 && isQuote(p.cur.value[0]) { // Look for multiple quoted strings separated by whitespace, // and concatenate them. cat := p.cur for { p.skipWhitespace() if p.done || !isQuote(p.s[0]) { break } p.advance() if p.cur.err != nil { return &p.cur } cat.value += " " + p.cur.value cat.unquoted += p.cur.unquoted } p.done = false // parser may have seen EOF, but we want to return cat p.cur = cat } return &p.cur } func (p *textParser) consumeToken(s string) error { tok := p.next() if tok.err != nil { return tok.err } if tok.value != s { p.back() return p.errorf("expected %q, found %q", s, tok.value) } return nil } var errBadUTF8 = errors.New("proto: bad UTF-8") func unquoteC(s string, quote rune) (string, error) { // This is based on C++'s tokenizer.cc. // Despite its name, this is *not* parsing C syntax. // For instance, "\0" is an invalid quoted string. // Avoid allocation in trivial cases. simple := true for _, r := range s { if r == '\\' || r == quote { simple = false break } } if simple { return s, nil } buf := make([]byte, 0, 3*len(s)/2) for len(s) > 0 { r, n := utf8.DecodeRuneInString(s) if r == utf8.RuneError && n == 1 { return "", errBadUTF8 } s = s[n:] if r != '\\' { if r < utf8.RuneSelf { buf = append(buf, byte(r)) } else { buf = append(buf, string(r)...) } continue } ch, tail, err := unescape(s) if err != nil { return "", err } buf = append(buf, ch...) s = tail } return string(buf), nil } func unescape(s string) (ch string, tail string, err error) { r, n := utf8.DecodeRuneInString(s) if r == utf8.RuneError && n == 1 { return "", "", errBadUTF8 } s = s[n:] switch r { case 'a': return "\a", s, nil case 'b': return "\b", s, nil case 'f': return "\f", s, nil case 'n': return "\n", s, nil case 'r': return "\r", s, nil case 't': return "\t", s, nil case 'v': return "\v", s, nil case '?': return "?", s, nil // trigraph workaround case '\'', '"', '\\': return string(r), s, nil case '0', '1', '2', '3', '4', '5', '6', '7': if len(s) < 2 { return "", "", fmt.Errorf(`\%c requires 2 following digits`, r) } ss := string(r) + s[:2] s = s[2:] i, err := strconv.ParseUint(ss, 8, 8) if err != nil { return "", "", fmt.Errorf(`\%s contains non-octal digits`, ss) } return string([]byte{byte(i)}), s, nil case 'x', 'X', 'u', 'U': var n int switch r { case 'x', 'X': n = 2 case 'u': n = 4 case 'U': n = 8 } if len(s) < n { return "", "", fmt.Errorf(`\%c requires %d following digits`, r, n) } ss := s[:n] s = s[n:] i, err := strconv.ParseUint(ss, 16, 64) if err != nil { return "", "", fmt.Errorf(`\%c%s contains non-hexadecimal digits`, r, ss) } if r == 'x' || r == 'X' { return string([]byte{byte(i)}), s, nil } if i > utf8.MaxRune { return "", "", fmt.Errorf(`\%c%s is not a valid Unicode code point`, r, ss) } return string(rune(i)), s, nil } return "", "", fmt.Errorf(`unknown escape \%c`, r) } func isIdentOrNumberChar(c byte) bool { switch { case 'A' <= c && c <= 'Z', 'a' <= c && c <= 'z': return true case '0' <= c && c <= '9': return true } switch c { case '-', '+', '.', '_': return true } return false } func isWhitespace(c byte) bool { switch c { case ' ', '\t', '\n', '\r': return true } return false } func isQuote(c byte) bool { switch c { case '"', '\'': return true } return false } ================================================ FILE: vendor/github.com/golang/protobuf/proto/text_encode.go ================================================ // Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package proto import ( "bytes" "encoding" "fmt" "io" "math" "sort" "strings" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/encoding/protowire" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/reflect/protoregistry" ) const wrapTextMarshalV2 = false // TextMarshaler is a configurable text format marshaler. type TextMarshaler struct { Compact bool // use compact text format (one line) ExpandAny bool // expand google.protobuf.Any messages of known types } // Marshal writes the proto text format of m to w. func (tm *TextMarshaler) Marshal(w io.Writer, m Message) error { b, err := tm.marshal(m) if len(b) > 0 { if _, err := w.Write(b); err != nil { return err } } return err } // Text returns a proto text formatted string of m. func (tm *TextMarshaler) Text(m Message) string { b, _ := tm.marshal(m) return string(b) } func (tm *TextMarshaler) marshal(m Message) ([]byte, error) { mr := MessageReflect(m) if mr == nil || !mr.IsValid() { return []byte(""), nil } if wrapTextMarshalV2 { if m, ok := m.(encoding.TextMarshaler); ok { return m.MarshalText() } opts := prototext.MarshalOptions{ AllowPartial: true, EmitUnknown: true, } if !tm.Compact { opts.Indent = " " } if !tm.ExpandAny { opts.Resolver = (*protoregistry.Types)(nil) } return opts.Marshal(mr.Interface()) } else { w := &textWriter{ compact: tm.Compact, expandAny: tm.ExpandAny, complete: true, } if m, ok := m.(encoding.TextMarshaler); ok { b, err := m.MarshalText() if err != nil { return nil, err } w.Write(b) return w.buf, nil } err := w.writeMessage(mr) return w.buf, err } } var ( defaultTextMarshaler = TextMarshaler{} compactTextMarshaler = TextMarshaler{Compact: true} ) // MarshalText writes the proto text format of m to w. func MarshalText(w io.Writer, m Message) error { return defaultTextMarshaler.Marshal(w, m) } // MarshalTextString returns a proto text formatted string of m. func MarshalTextString(m Message) string { return defaultTextMarshaler.Text(m) } // CompactText writes the compact proto text format of m to w. func CompactText(w io.Writer, m Message) error { return compactTextMarshaler.Marshal(w, m) } // CompactTextString returns a compact proto text formatted string of m. func CompactTextString(m Message) string { return compactTextMarshaler.Text(m) } var ( newline = []byte("\n") endBraceNewline = []byte("}\n") posInf = []byte("inf") negInf = []byte("-inf") nan = []byte("nan") ) // textWriter is an io.Writer that tracks its indentation level. type textWriter struct { compact bool // same as TextMarshaler.Compact expandAny bool // same as TextMarshaler.ExpandAny complete bool // whether the current position is a complete line indent int // indentation level; never negative buf []byte } func (w *textWriter) Write(p []byte) (n int, _ error) { newlines := bytes.Count(p, newline) if newlines == 0 { if !w.compact && w.complete { w.writeIndent() } w.buf = append(w.buf, p...) w.complete = false return len(p), nil } frags := bytes.SplitN(p, newline, newlines+1) if w.compact { for i, frag := range frags { if i > 0 { w.buf = append(w.buf, ' ') n++ } w.buf = append(w.buf, frag...) n += len(frag) } return n, nil } for i, frag := range frags { if w.complete { w.writeIndent() } w.buf = append(w.buf, frag...) n += len(frag) if i+1 < len(frags) { w.buf = append(w.buf, '\n') n++ } } w.complete = len(frags[len(frags)-1]) == 0 return n, nil } func (w *textWriter) WriteByte(c byte) error { if w.compact && c == '\n' { c = ' ' } if !w.compact && w.complete { w.writeIndent() } w.buf = append(w.buf, c) w.complete = c == '\n' return nil } func (w *textWriter) writeName(fd protoreflect.FieldDescriptor) { if !w.compact && w.complete { w.writeIndent() } w.complete = false if fd.Kind() != protoreflect.GroupKind { w.buf = append(w.buf, fd.Name()...) w.WriteByte(':') } else { // Use message type name for group field name. w.buf = append(w.buf, fd.Message().Name()...) } if !w.compact { w.WriteByte(' ') } } func requiresQuotes(u string) bool { // When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted. for _, ch := range u { switch { case ch == '.' || ch == '/' || ch == '_': continue case '0' <= ch && ch <= '9': continue case 'A' <= ch && ch <= 'Z': continue case 'a' <= ch && ch <= 'z': continue default: return true } } return false } // writeProto3Any writes an expanded google.protobuf.Any message. // // It returns (false, nil) if sv value can't be unmarshaled (e.g. because // required messages are not linked in). // // It returns (true, error) when sv was written in expanded format or an error // was encountered. func (w *textWriter) writeProto3Any(m protoreflect.Message) (bool, error) { md := m.Descriptor() fdURL := md.Fields().ByName("type_url") fdVal := md.Fields().ByName("value") url := m.Get(fdURL).String() mt, err := protoregistry.GlobalTypes.FindMessageByURL(url) if err != nil { return false, nil } b := m.Get(fdVal).Bytes() m2 := mt.New() if err := proto.Unmarshal(b, m2.Interface()); err != nil { return false, nil } w.Write([]byte("[")) if requiresQuotes(url) { w.writeQuotedString(url) } else { w.Write([]byte(url)) } if w.compact { w.Write([]byte("]:<")) } else { w.Write([]byte("]: <\n")) w.indent++ } if err := w.writeMessage(m2); err != nil { return true, err } if w.compact { w.Write([]byte("> ")) } else { w.indent-- w.Write([]byte(">\n")) } return true, nil } func (w *textWriter) writeMessage(m protoreflect.Message) error { md := m.Descriptor() if w.expandAny && md.FullName() == "google.protobuf.Any" { if canExpand, err := w.writeProto3Any(m); canExpand { return err } } fds := md.Fields() for i := 0; i < fds.Len(); { fd := fds.Get(i) if od := fd.ContainingOneof(); od != nil { fd = m.WhichOneof(od) i += od.Fields().Len() } else { i++ } if fd == nil || !m.Has(fd) { continue } switch { case fd.IsList(): lv := m.Get(fd).List() for j := 0; j < lv.Len(); j++ { w.writeName(fd) v := lv.Get(j) if err := w.writeSingularValue(v, fd); err != nil { return err } w.WriteByte('\n') } case fd.IsMap(): kfd := fd.MapKey() vfd := fd.MapValue() mv := m.Get(fd).Map() type entry struct{ key, val protoreflect.Value } var entries []entry mv.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool { entries = append(entries, entry{k.Value(), v}) return true }) sort.Slice(entries, func(i, j int) bool { switch kfd.Kind() { case protoreflect.BoolKind: return !entries[i].key.Bool() && entries[j].key.Bool() case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: return entries[i].key.Int() < entries[j].key.Int() case protoreflect.Uint32Kind, protoreflect.Fixed32Kind, protoreflect.Uint64Kind, protoreflect.Fixed64Kind: return entries[i].key.Uint() < entries[j].key.Uint() case protoreflect.StringKind: return entries[i].key.String() < entries[j].key.String() default: panic("invalid kind") } }) for _, entry := range entries { w.writeName(fd) w.WriteByte('<') if !w.compact { w.WriteByte('\n') } w.indent++ w.writeName(kfd) if err := w.writeSingularValue(entry.key, kfd); err != nil { return err } w.WriteByte('\n') w.writeName(vfd) if err := w.writeSingularValue(entry.val, vfd); err != nil { return err } w.WriteByte('\n') w.indent-- w.WriteByte('>') w.WriteByte('\n') } default: w.writeName(fd) if err := w.writeSingularValue(m.Get(fd), fd); err != nil { return err } w.WriteByte('\n') } } if b := m.GetUnknown(); len(b) > 0 { w.writeUnknownFields(b) } return w.writeExtensions(m) } func (w *textWriter) writeSingularValue(v protoreflect.Value, fd protoreflect.FieldDescriptor) error { switch fd.Kind() { case protoreflect.FloatKind, protoreflect.DoubleKind: switch vf := v.Float(); { case math.IsInf(vf, +1): w.Write(posInf) case math.IsInf(vf, -1): w.Write(negInf) case math.IsNaN(vf): w.Write(nan) default: fmt.Fprint(w, v.Interface()) } case protoreflect.StringKind: // NOTE: This does not validate UTF-8 for historical reasons. w.writeQuotedString(string(v.String())) case protoreflect.BytesKind: w.writeQuotedString(string(v.Bytes())) case protoreflect.MessageKind, protoreflect.GroupKind: var bra, ket byte = '<', '>' if fd.Kind() == protoreflect.GroupKind { bra, ket = '{', '}' } w.WriteByte(bra) if !w.compact { w.WriteByte('\n') } w.indent++ m := v.Message() if m2, ok := m.Interface().(encoding.TextMarshaler); ok { b, err := m2.MarshalText() if err != nil { return err } w.Write(b) } else { w.writeMessage(m) } w.indent-- w.WriteByte(ket) case protoreflect.EnumKind: if ev := fd.Enum().Values().ByNumber(v.Enum()); ev != nil { fmt.Fprint(w, ev.Name()) } else { fmt.Fprint(w, v.Enum()) } default: fmt.Fprint(w, v.Interface()) } return nil } // writeQuotedString writes a quoted string in the protocol buffer text format. func (w *textWriter) writeQuotedString(s string) { w.WriteByte('"') for i := 0; i < len(s); i++ { switch c := s[i]; c { case '\n': w.buf = append(w.buf, `\n`...) case '\r': w.buf = append(w.buf, `\r`...) case '\t': w.buf = append(w.buf, `\t`...) case '"': w.buf = append(w.buf, `\"`...) case '\\': w.buf = append(w.buf, `\\`...) default: if isPrint := c >= 0x20 && c < 0x7f; isPrint { w.buf = append(w.buf, c) } else { w.buf = append(w.buf, fmt.Sprintf(`\%03o`, c)...) } } } w.WriteByte('"') } func (w *textWriter) writeUnknownFields(b []byte) { if !w.compact { fmt.Fprintf(w, "/* %d unknown bytes */\n", len(b)) } for len(b) > 0 { num, wtyp, n := protowire.ConsumeTag(b) if n < 0 { return } b = b[n:] if wtyp == protowire.EndGroupType { w.indent-- w.Write(endBraceNewline) continue } fmt.Fprint(w, num) if wtyp != protowire.StartGroupType { w.WriteByte(':') } if !w.compact || wtyp == protowire.StartGroupType { w.WriteByte(' ') } switch wtyp { case protowire.VarintType: v, n := protowire.ConsumeVarint(b) if n < 0 { return } b = b[n:] fmt.Fprint(w, v) case protowire.Fixed32Type: v, n := protowire.ConsumeFixed32(b) if n < 0 { return } b = b[n:] fmt.Fprint(w, v) case protowire.Fixed64Type: v, n := protowire.ConsumeFixed64(b) if n < 0 { return } b = b[n:] fmt.Fprint(w, v) case protowire.BytesType: v, n := protowire.ConsumeBytes(b) if n < 0 { return } b = b[n:] fmt.Fprintf(w, "%q", v) case protowire.StartGroupType: w.WriteByte('{') w.indent++ default: fmt.Fprintf(w, "/* unknown wire type %d */", wtyp) } w.WriteByte('\n') } } // writeExtensions writes all the extensions in m. func (w *textWriter) writeExtensions(m protoreflect.Message) error { md := m.Descriptor() if md.ExtensionRanges().Len() == 0 { return nil } type ext struct { desc protoreflect.FieldDescriptor val protoreflect.Value } var exts []ext m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { if fd.IsExtension() { exts = append(exts, ext{fd, v}) } return true }) sort.Slice(exts, func(i, j int) bool { return exts[i].desc.Number() < exts[j].desc.Number() }) for _, ext := range exts { // For message set, use the name of the message as the extension name. name := string(ext.desc.FullName()) if isMessageSet(ext.desc.ContainingMessage()) { name = strings.TrimSuffix(name, ".message_set_extension") } if !ext.desc.IsList() { if err := w.writeSingularExtension(name, ext.val, ext.desc); err != nil { return err } } else { lv := ext.val.List() for i := 0; i < lv.Len(); i++ { if err := w.writeSingularExtension(name, lv.Get(i), ext.desc); err != nil { return err } } } } return nil } func (w *textWriter) writeSingularExtension(name string, v protoreflect.Value, fd protoreflect.FieldDescriptor) error { fmt.Fprintf(w, "[%s]:", name) if !w.compact { w.WriteByte(' ') } if err := w.writeSingularValue(v, fd); err != nil { return err } w.WriteByte('\n') return nil } func (w *textWriter) writeIndent() { if !w.complete { return } for i := 0; i < w.indent*2; i++ { w.buf = append(w.buf, ' ') } w.complete = false } ================================================ FILE: vendor/github.com/golang/protobuf/proto/wire.go ================================================ // Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package proto import ( protoV2 "google.golang.org/protobuf/proto" "google.golang.org/protobuf/runtime/protoiface" ) // Size returns the size in bytes of the wire-format encoding of m. func Size(m Message) int { if m == nil { return 0 } mi := MessageV2(m) return protoV2.Size(mi) } // Marshal returns the wire-format encoding of m. func Marshal(m Message) ([]byte, error) { b, err := marshalAppend(nil, m, false) if b == nil { b = zeroBytes } return b, err } var zeroBytes = make([]byte, 0, 0) func marshalAppend(buf []byte, m Message, deterministic bool) ([]byte, error) { if m == nil { return nil, ErrNil } mi := MessageV2(m) nbuf, err := protoV2.MarshalOptions{ Deterministic: deterministic, AllowPartial: true, }.MarshalAppend(buf, mi) if err != nil { return buf, err } if len(buf) == len(nbuf) { if !mi.ProtoReflect().IsValid() { return buf, ErrNil } } return nbuf, checkRequiredNotSet(mi) } // Unmarshal parses a wire-format message in b and places the decoded results in m. // // Unmarshal resets m before starting to unmarshal, so any existing data in m is always // removed. Use UnmarshalMerge to preserve and append to existing data. func Unmarshal(b []byte, m Message) error { m.Reset() return UnmarshalMerge(b, m) } // UnmarshalMerge parses a wire-format message in b and places the decoded results in m. func UnmarshalMerge(b []byte, m Message) error { mi := MessageV2(m) out, err := protoV2.UnmarshalOptions{ AllowPartial: true, Merge: true, }.UnmarshalState(protoiface.UnmarshalInput{ Buf: b, Message: mi.ProtoReflect(), }) if err != nil { return err } if out.Flags&protoiface.UnmarshalInitialized > 0 { return nil } return checkRequiredNotSet(mi) } ================================================ FILE: vendor/github.com/golang/protobuf/proto/wrappers.go ================================================ // Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package proto // Bool stores v in a new bool value and returns a pointer to it. func Bool(v bool) *bool { return &v } // Int stores v in a new int32 value and returns a pointer to it. // // Deprecated: Use Int32 instead. func Int(v int) *int32 { return Int32(int32(v)) } // Int32 stores v in a new int32 value and returns a pointer to it. func Int32(v int32) *int32 { return &v } // Int64 stores v in a new int64 value and returns a pointer to it. func Int64(v int64) *int64 { return &v } // Uint32 stores v in a new uint32 value and returns a pointer to it. func Uint32(v uint32) *uint32 { return &v } // Uint64 stores v in a new uint64 value and returns a pointer to it. func Uint64(v uint64) *uint64 { return &v } // Float32 stores v in a new float32 value and returns a pointer to it. func Float32(v float32) *float32 { return &v } // Float64 stores v in a new float64 value and returns a pointer to it. func Float64(v float64) *float64 { return &v } // String stores v in a new string value and returns a pointer to it. func String(v string) *string { return &v } ================================================ FILE: vendor/github.com/golang/protobuf/ptypes/any/any.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // source: github.com/golang/protobuf/ptypes/any/any.proto package any import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" ) // Symbols defined in public import of google/protobuf/any.proto. type Any = anypb.Any var File_github_com_golang_protobuf_ptypes_any_any_proto protoreflect.FileDescriptor var file_github_com_golang_protobuf_ptypes_any_any_proto_rawDesc = []byte{ 0x0a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x61, 0x6e, 0x79, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x2b, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x61, 0x6e, 0x79, 0x3b, 0x61, 0x6e, 0x79, 0x50, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var file_github_com_golang_protobuf_ptypes_any_any_proto_goTypes = []interface{}{} var file_github_com_golang_protobuf_ptypes_any_any_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_github_com_golang_protobuf_ptypes_any_any_proto_init() } func file_github_com_golang_protobuf_ptypes_any_any_proto_init() { if File_github_com_golang_protobuf_ptypes_any_any_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_github_com_golang_protobuf_ptypes_any_any_proto_rawDesc, NumEnums: 0, NumMessages: 0, NumExtensions: 0, NumServices: 0, }, GoTypes: file_github_com_golang_protobuf_ptypes_any_any_proto_goTypes, DependencyIndexes: file_github_com_golang_protobuf_ptypes_any_any_proto_depIdxs, }.Build() File_github_com_golang_protobuf_ptypes_any_any_proto = out.File file_github_com_golang_protobuf_ptypes_any_any_proto_rawDesc = nil file_github_com_golang_protobuf_ptypes_any_any_proto_goTypes = nil file_github_com_golang_protobuf_ptypes_any_any_proto_depIdxs = nil } ================================================ FILE: vendor/github.com/golang/protobuf/ptypes/any.go ================================================ // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package ptypes import ( "fmt" "strings" "github.com/golang/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/reflect/protoregistry" anypb "github.com/golang/protobuf/ptypes/any" ) const urlPrefix = "type.googleapis.com/" // AnyMessageName returns the message name contained in an anypb.Any message. // Most type assertions should use the Is function instead. // // Deprecated: Call the any.MessageName method instead. func AnyMessageName(any *anypb.Any) (string, error) { name, err := anyMessageName(any) return string(name), err } func anyMessageName(any *anypb.Any) (protoreflect.FullName, error) { if any == nil { return "", fmt.Errorf("message is nil") } name := protoreflect.FullName(any.TypeUrl) if i := strings.LastIndex(any.TypeUrl, "/"); i >= 0 { name = name[i+len("/"):] } if !name.IsValid() { return "", fmt.Errorf("message type url %q is invalid", any.TypeUrl) } return name, nil } // MarshalAny marshals the given message m into an anypb.Any message. // // Deprecated: Call the anypb.New function instead. func MarshalAny(m proto.Message) (*anypb.Any, error) { switch dm := m.(type) { case DynamicAny: m = dm.Message case *DynamicAny: if dm == nil { return nil, proto.ErrNil } m = dm.Message } b, err := proto.Marshal(m) if err != nil { return nil, err } return &anypb.Any{TypeUrl: urlPrefix + proto.MessageName(m), Value: b}, nil } // Empty returns a new message of the type specified in an anypb.Any message. // It returns protoregistry.NotFound if the corresponding message type could not // be resolved in the global registry. // // Deprecated: Use protoregistry.GlobalTypes.FindMessageByName instead // to resolve the message name and create a new instance of it. func Empty(any *anypb.Any) (proto.Message, error) { name, err := anyMessageName(any) if err != nil { return nil, err } mt, err := protoregistry.GlobalTypes.FindMessageByName(name) if err != nil { return nil, err } return proto.MessageV1(mt.New().Interface()), nil } // UnmarshalAny unmarshals the encoded value contained in the anypb.Any message // into the provided message m. It returns an error if the target message // does not match the type in the Any message or if an unmarshal error occurs. // // The target message m may be a *DynamicAny message. If the underlying message // type could not be resolved, then this returns protoregistry.NotFound. // // Deprecated: Call the any.UnmarshalTo method instead. func UnmarshalAny(any *anypb.Any, m proto.Message) error { if dm, ok := m.(*DynamicAny); ok { if dm.Message == nil { var err error dm.Message, err = Empty(any) if err != nil { return err } } m = dm.Message } anyName, err := AnyMessageName(any) if err != nil { return err } msgName := proto.MessageName(m) if anyName != msgName { return fmt.Errorf("mismatched message type: got %q want %q", anyName, msgName) } return proto.Unmarshal(any.Value, m) } // Is reports whether the Any message contains a message of the specified type. // // Deprecated: Call the any.MessageIs method instead. func Is(any *anypb.Any, m proto.Message) bool { if any == nil || m == nil { return false } name := proto.MessageName(m) if !strings.HasSuffix(any.TypeUrl, name) { return false } return len(any.TypeUrl) == len(name) || any.TypeUrl[len(any.TypeUrl)-len(name)-1] == '/' } // DynamicAny is a value that can be passed to UnmarshalAny to automatically // allocate a proto.Message for the type specified in an anypb.Any message. // The allocated message is stored in the embedded proto.Message. // // Example: // var x ptypes.DynamicAny // if err := ptypes.UnmarshalAny(a, &x); err != nil { ... } // fmt.Printf("unmarshaled message: %v", x.Message) // // Deprecated: Use the any.UnmarshalNew method instead to unmarshal // the any message contents into a new instance of the underlying message. type DynamicAny struct{ proto.Message } func (m DynamicAny) String() string { if m.Message == nil { return "" } return m.Message.String() } func (m DynamicAny) Reset() { if m.Message == nil { return } m.Message.Reset() } func (m DynamicAny) ProtoMessage() { return } func (m DynamicAny) ProtoReflect() protoreflect.Message { if m.Message == nil { return nil } return dynamicAny{proto.MessageReflect(m.Message)} } type dynamicAny struct{ protoreflect.Message } func (m dynamicAny) Type() protoreflect.MessageType { return dynamicAnyType{m.Message.Type()} } func (m dynamicAny) New() protoreflect.Message { return dynamicAnyType{m.Message.Type()}.New() } func (m dynamicAny) Interface() protoreflect.ProtoMessage { return DynamicAny{proto.MessageV1(m.Message.Interface())} } type dynamicAnyType struct{ protoreflect.MessageType } func (t dynamicAnyType) New() protoreflect.Message { return dynamicAny{t.MessageType.New()} } func (t dynamicAnyType) Zero() protoreflect.Message { return dynamicAny{t.MessageType.Zero()} } ================================================ FILE: vendor/github.com/golang/protobuf/ptypes/doc.go ================================================ // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package ptypes provides functionality for interacting with well-known types. // // Deprecated: Well-known types have specialized functionality directly // injected into the generated packages for each message type. // See the deprecation notice for each function for the suggested alternative. package ptypes ================================================ FILE: vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // source: github.com/golang/protobuf/ptypes/duration/duration.proto package duration import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" durationpb "google.golang.org/protobuf/types/known/durationpb" reflect "reflect" ) // Symbols defined in public import of google/protobuf/duration.proto. type Duration = durationpb.Duration var File_github_com_golang_protobuf_ptypes_duration_duration_proto protoreflect.FileDescriptor var file_github_com_golang_protobuf_ptypes_duration_duration_proto_rawDesc = []byte{ 0x0a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var file_github_com_golang_protobuf_ptypes_duration_duration_proto_goTypes = []interface{}{} var file_github_com_golang_protobuf_ptypes_duration_duration_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_github_com_golang_protobuf_ptypes_duration_duration_proto_init() } func file_github_com_golang_protobuf_ptypes_duration_duration_proto_init() { if File_github_com_golang_protobuf_ptypes_duration_duration_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_github_com_golang_protobuf_ptypes_duration_duration_proto_rawDesc, NumEnums: 0, NumMessages: 0, NumExtensions: 0, NumServices: 0, }, GoTypes: file_github_com_golang_protobuf_ptypes_duration_duration_proto_goTypes, DependencyIndexes: file_github_com_golang_protobuf_ptypes_duration_duration_proto_depIdxs, }.Build() File_github_com_golang_protobuf_ptypes_duration_duration_proto = out.File file_github_com_golang_protobuf_ptypes_duration_duration_proto_rawDesc = nil file_github_com_golang_protobuf_ptypes_duration_duration_proto_goTypes = nil file_github_com_golang_protobuf_ptypes_duration_duration_proto_depIdxs = nil } ================================================ FILE: vendor/github.com/golang/protobuf/ptypes/duration.go ================================================ // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package ptypes import ( "errors" "fmt" "time" durationpb "github.com/golang/protobuf/ptypes/duration" ) // Range of google.protobuf.Duration as specified in duration.proto. // This is about 10,000 years in seconds. const ( maxSeconds = int64(10000 * 365.25 * 24 * 60 * 60) minSeconds = -maxSeconds ) // Duration converts a durationpb.Duration to a time.Duration. // Duration returns an error if dur is invalid or overflows a time.Duration. // // Deprecated: Call the dur.AsDuration and dur.CheckValid methods instead. func Duration(dur *durationpb.Duration) (time.Duration, error) { if err := validateDuration(dur); err != nil { return 0, err } d := time.Duration(dur.Seconds) * time.Second if int64(d/time.Second) != dur.Seconds { return 0, fmt.Errorf("duration: %v is out of range for time.Duration", dur) } if dur.Nanos != 0 { d += time.Duration(dur.Nanos) * time.Nanosecond if (d < 0) != (dur.Nanos < 0) { return 0, fmt.Errorf("duration: %v is out of range for time.Duration", dur) } } return d, nil } // DurationProto converts a time.Duration to a durationpb.Duration. // // Deprecated: Call the durationpb.New function instead. func DurationProto(d time.Duration) *durationpb.Duration { nanos := d.Nanoseconds() secs := nanos / 1e9 nanos -= secs * 1e9 return &durationpb.Duration{ Seconds: int64(secs), Nanos: int32(nanos), } } // validateDuration determines whether the durationpb.Duration is valid // according to the definition in google/protobuf/duration.proto. // A valid durpb.Duration may still be too large to fit into a time.Duration // Note that the range of durationpb.Duration is about 10,000 years, // while the range of time.Duration is about 290 years. func validateDuration(dur *durationpb.Duration) error { if dur == nil { return errors.New("duration: nil Duration") } if dur.Seconds < minSeconds || dur.Seconds > maxSeconds { return fmt.Errorf("duration: %v: seconds out of range", dur) } if dur.Nanos <= -1e9 || dur.Nanos >= 1e9 { return fmt.Errorf("duration: %v: nanos out of range", dur) } // Seconds and Nanos must have the same sign, unless d.Nanos is zero. if (dur.Seconds < 0 && dur.Nanos > 0) || (dur.Seconds > 0 && dur.Nanos < 0) { return fmt.Errorf("duration: %v: seconds and nanos have different signs", dur) } return nil } ================================================ FILE: vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // source: github.com/golang/protobuf/ptypes/timestamp/timestamp.proto package timestamp import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" ) // Symbols defined in public import of google/protobuf/timestamp.proto. type Timestamp = timestamppb.Timestamp var File_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto protoreflect.FileDescriptor var file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_rawDesc = []byte{ 0x0a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x37, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x3b, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x50, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_goTypes = []interface{}{} var file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_init() } func file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_init() { if File_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_rawDesc, NumEnums: 0, NumMessages: 0, NumExtensions: 0, NumServices: 0, }, GoTypes: file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_goTypes, DependencyIndexes: file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_depIdxs, }.Build() File_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto = out.File file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_rawDesc = nil file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_goTypes = nil file_github_com_golang_protobuf_ptypes_timestamp_timestamp_proto_depIdxs = nil } ================================================ FILE: vendor/github.com/golang/protobuf/ptypes/timestamp.go ================================================ // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package ptypes import ( "errors" "fmt" "time" timestamppb "github.com/golang/protobuf/ptypes/timestamp" ) // Range of google.protobuf.Duration as specified in timestamp.proto. const ( // Seconds field of the earliest valid Timestamp. // This is time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC).Unix(). minValidSeconds = -62135596800 // Seconds field just after the latest valid Timestamp. // This is time.Date(10000, 1, 1, 0, 0, 0, 0, time.UTC).Unix(). maxValidSeconds = 253402300800 ) // Timestamp converts a timestamppb.Timestamp to a time.Time. // It returns an error if the argument is invalid. // // Unlike most Go functions, if Timestamp returns an error, the first return // value is not the zero time.Time. Instead, it is the value obtained from the // time.Unix function when passed the contents of the Timestamp, in the UTC // locale. This may or may not be a meaningful time; many invalid Timestamps // do map to valid time.Times. // // A nil Timestamp returns an error. The first return value in that case is // undefined. // // Deprecated: Call the ts.AsTime and ts.CheckValid methods instead. func Timestamp(ts *timestamppb.Timestamp) (time.Time, error) { // Don't return the zero value on error, because corresponds to a valid // timestamp. Instead return whatever time.Unix gives us. var t time.Time if ts == nil { t = time.Unix(0, 0).UTC() // treat nil like the empty Timestamp } else { t = time.Unix(ts.Seconds, int64(ts.Nanos)).UTC() } return t, validateTimestamp(ts) } // TimestampNow returns a google.protobuf.Timestamp for the current time. // // Deprecated: Call the timestamppb.Now function instead. func TimestampNow() *timestamppb.Timestamp { ts, err := TimestampProto(time.Now()) if err != nil { panic("ptypes: time.Now() out of Timestamp range") } return ts } // TimestampProto converts the time.Time to a google.protobuf.Timestamp proto. // It returns an error if the resulting Timestamp is invalid. // // Deprecated: Call the timestamppb.New function instead. func TimestampProto(t time.Time) (*timestamppb.Timestamp, error) { ts := ×tamppb.Timestamp{ Seconds: t.Unix(), Nanos: int32(t.Nanosecond()), } if err := validateTimestamp(ts); err != nil { return nil, err } return ts, nil } // TimestampString returns the RFC 3339 string for valid Timestamps. // For invalid Timestamps, it returns an error message in parentheses. // // Deprecated: Call the ts.AsTime method instead, // followed by a call to the Format method on the time.Time value. func TimestampString(ts *timestamppb.Timestamp) string { t, err := Timestamp(ts) if err != nil { return fmt.Sprintf("(%v)", err) } return t.Format(time.RFC3339Nano) } // validateTimestamp determines whether a Timestamp is valid. // A valid timestamp represents a time in the range [0001-01-01, 10000-01-01) // and has a Nanos field in the range [0, 1e9). // // If the Timestamp is valid, validateTimestamp returns nil. // Otherwise, it returns an error that describes the problem. // // Every valid Timestamp can be represented by a time.Time, // but the converse is not true. func validateTimestamp(ts *timestamppb.Timestamp) error { if ts == nil { return errors.New("timestamp: nil Timestamp") } if ts.Seconds < minValidSeconds { return fmt.Errorf("timestamp: %v before 0001-01-01", ts) } if ts.Seconds >= maxValidSeconds { return fmt.Errorf("timestamp: %v after 10000-01-01", ts) } if ts.Nanos < 0 || ts.Nanos >= 1e9 { return fmt.Errorf("timestamp: %v: nanos not in range [0, 1e9)", ts) } return nil } ================================================ FILE: vendor/github.com/google/btree/.travis.yml ================================================ language: go ================================================ FILE: vendor/github.com/google/btree/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/google/btree/README.md ================================================ # BTree implementation for Go ![Travis CI Build Status](https://api.travis-ci.org/google/btree.svg?branch=master) This package provides an in-memory B-Tree implementation for Go, useful as an ordered, mutable data structure. The API is based off of the wonderful http://godoc.org/github.com/petar/GoLLRB/llrb, and is meant to allow btree to act as a drop-in replacement for gollrb trees. See http://godoc.org/github.com/google/btree for documentation. ================================================ FILE: vendor/github.com/google/btree/btree.go ================================================ // Copyright 2014 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package btree implements in-memory B-Trees of arbitrary degree. // // btree implements an in-memory B-Tree for use as an ordered data structure. // It is not meant for persistent storage solutions. // // It has a flatter structure than an equivalent red-black or other binary tree, // which in some cases yields better memory usage and/or performance. // See some discussion on the matter here: // http://google-opensource.blogspot.com/2013/01/c-containers-that-save-memory-and-time.html // Note, though, that this project is in no way related to the C++ B-Tree // implementation written about there. // // Within this tree, each node contains a slice of items and a (possibly nil) // slice of children. For basic numeric values or raw structs, this can cause // efficiency differences when compared to equivalent C++ template code that // stores values in arrays within the node: // * Due to the overhead of storing values as interfaces (each // value needs to be stored as the value itself, then 2 words for the // interface pointing to that value and its type), resulting in higher // memory use. // * Since interfaces can point to values anywhere in memory, values are // most likely not stored in contiguous blocks, resulting in a higher // number of cache misses. // These issues don't tend to matter, though, when working with strings or other // heap-allocated structures, since C++-equivalent structures also must store // pointers and also distribute their values across the heap. // // This implementation is designed to be a drop-in replacement to gollrb.LLRB // trees, (http://github.com/petar/gollrb), an excellent and probably the most // widely used ordered tree implementation in the Go ecosystem currently. // Its functions, therefore, exactly mirror those of // llrb.LLRB where possible. Unlike gollrb, though, we currently don't // support storing multiple equivalent values. package btree import ( "fmt" "io" "sort" "strings" "sync" ) // Item represents a single object in the tree. type Item interface { // Less tests whether the current item is less than the given argument. // // This must provide a strict weak ordering. // If !a.Less(b) && !b.Less(a), we treat this to mean a == b (i.e. we can only // hold one of either a or b in the tree). Less(than Item) bool } const ( DefaultFreeListSize = 32 ) var ( nilItems = make(items, 16) nilChildren = make(children, 16) ) // FreeList represents a free list of btree nodes. By default each // BTree has its own FreeList, but multiple BTrees can share the same // FreeList. // Two Btrees using the same freelist are safe for concurrent write access. type FreeList struct { mu sync.Mutex freelist []*node } // NewFreeList creates a new free list. // size is the maximum size of the returned free list. func NewFreeList(size int) *FreeList { return &FreeList{freelist: make([]*node, 0, size)} } func (f *FreeList) newNode() (n *node) { f.mu.Lock() index := len(f.freelist) - 1 if index < 0 { f.mu.Unlock() return new(node) } n = f.freelist[index] f.freelist[index] = nil f.freelist = f.freelist[:index] f.mu.Unlock() return } // freeNode adds the given node to the list, returning true if it was added // and false if it was discarded. func (f *FreeList) freeNode(n *node) (out bool) { f.mu.Lock() if len(f.freelist) < cap(f.freelist) { f.freelist = append(f.freelist, n) out = true } f.mu.Unlock() return } // ItemIterator allows callers of Ascend* to iterate in-order over portions of // the tree. When this function returns false, iteration will stop and the // associated Ascend* function will immediately return. type ItemIterator func(i Item) bool // New creates a new B-Tree with the given degree. // // New(2), for example, will create a 2-3-4 tree (each node contains 1-3 items // and 2-4 children). func New(degree int) *BTree { return NewWithFreeList(degree, NewFreeList(DefaultFreeListSize)) } // NewWithFreeList creates a new B-Tree that uses the given node free list. func NewWithFreeList(degree int, f *FreeList) *BTree { if degree <= 1 { panic("bad degree") } return &BTree{ degree: degree, cow: ©OnWriteContext{freelist: f}, } } // items stores items in a node. type items []Item // insertAt inserts a value into the given index, pushing all subsequent values // forward. func (s *items) insertAt(index int, item Item) { *s = append(*s, nil) if index < len(*s) { copy((*s)[index+1:], (*s)[index:]) } (*s)[index] = item } // removeAt removes a value at a given index, pulling all subsequent values // back. func (s *items) removeAt(index int) Item { item := (*s)[index] copy((*s)[index:], (*s)[index+1:]) (*s)[len(*s)-1] = nil *s = (*s)[:len(*s)-1] return item } // pop removes and returns the last element in the list. func (s *items) pop() (out Item) { index := len(*s) - 1 out = (*s)[index] (*s)[index] = nil *s = (*s)[:index] return } // truncate truncates this instance at index so that it contains only the // first index items. index must be less than or equal to length. func (s *items) truncate(index int) { var toClear items *s, toClear = (*s)[:index], (*s)[index:] for len(toClear) > 0 { toClear = toClear[copy(toClear, nilItems):] } } // find returns the index where the given item should be inserted into this // list. 'found' is true if the item already exists in the list at the given // index. func (s items) find(item Item) (index int, found bool) { i := sort.Search(len(s), func(i int) bool { return item.Less(s[i]) }) if i > 0 && !s[i-1].Less(item) { return i - 1, true } return i, false } // children stores child nodes in a node. type children []*node // insertAt inserts a value into the given index, pushing all subsequent values // forward. func (s *children) insertAt(index int, n *node) { *s = append(*s, nil) if index < len(*s) { copy((*s)[index+1:], (*s)[index:]) } (*s)[index] = n } // removeAt removes a value at a given index, pulling all subsequent values // back. func (s *children) removeAt(index int) *node { n := (*s)[index] copy((*s)[index:], (*s)[index+1:]) (*s)[len(*s)-1] = nil *s = (*s)[:len(*s)-1] return n } // pop removes and returns the last element in the list. func (s *children) pop() (out *node) { index := len(*s) - 1 out = (*s)[index] (*s)[index] = nil *s = (*s)[:index] return } // truncate truncates this instance at index so that it contains only the // first index children. index must be less than or equal to length. func (s *children) truncate(index int) { var toClear children *s, toClear = (*s)[:index], (*s)[index:] for len(toClear) > 0 { toClear = toClear[copy(toClear, nilChildren):] } } // node is an internal node in a tree. // // It must at all times maintain the invariant that either // * len(children) == 0, len(items) unconstrained // * len(children) == len(items) + 1 type node struct { items items children children cow *copyOnWriteContext } func (n *node) mutableFor(cow *copyOnWriteContext) *node { if n.cow == cow { return n } out := cow.newNode() if cap(out.items) >= len(n.items) { out.items = out.items[:len(n.items)] } else { out.items = make(items, len(n.items), cap(n.items)) } copy(out.items, n.items) // Copy children if cap(out.children) >= len(n.children) { out.children = out.children[:len(n.children)] } else { out.children = make(children, len(n.children), cap(n.children)) } copy(out.children, n.children) return out } func (n *node) mutableChild(i int) *node { c := n.children[i].mutableFor(n.cow) n.children[i] = c return c } // split splits the given node at the given index. The current node shrinks, // and this function returns the item that existed at that index and a new node // containing all items/children after it. func (n *node) split(i int) (Item, *node) { item := n.items[i] next := n.cow.newNode() next.items = append(next.items, n.items[i+1:]...) n.items.truncate(i) if len(n.children) > 0 { next.children = append(next.children, n.children[i+1:]...) n.children.truncate(i + 1) } return item, next } // maybeSplitChild checks if a child should be split, and if so splits it. // Returns whether or not a split occurred. func (n *node) maybeSplitChild(i, maxItems int) bool { if len(n.children[i].items) < maxItems { return false } first := n.mutableChild(i) item, second := first.split(maxItems / 2) n.items.insertAt(i, item) n.children.insertAt(i+1, second) return true } // insert inserts an item into the subtree rooted at this node, making sure // no nodes in the subtree exceed maxItems items. Should an equivalent item be // be found/replaced by insert, it will be returned. func (n *node) insert(item Item, maxItems int) Item { i, found := n.items.find(item) if found { out := n.items[i] n.items[i] = item return out } if len(n.children) == 0 { n.items.insertAt(i, item) return nil } if n.maybeSplitChild(i, maxItems) { inTree := n.items[i] switch { case item.Less(inTree): // no change, we want first split node case inTree.Less(item): i++ // we want second split node default: out := n.items[i] n.items[i] = item return out } } return n.mutableChild(i).insert(item, maxItems) } // get finds the given key in the subtree and returns it. func (n *node) get(key Item) Item { i, found := n.items.find(key) if found { return n.items[i] } else if len(n.children) > 0 { return n.children[i].get(key) } return nil } // min returns the first item in the subtree. func min(n *node) Item { if n == nil { return nil } for len(n.children) > 0 { n = n.children[0] } if len(n.items) == 0 { return nil } return n.items[0] } // max returns the last item in the subtree. func max(n *node) Item { if n == nil { return nil } for len(n.children) > 0 { n = n.children[len(n.children)-1] } if len(n.items) == 0 { return nil } return n.items[len(n.items)-1] } // toRemove details what item to remove in a node.remove call. type toRemove int const ( removeItem toRemove = iota // removes the given item removeMin // removes smallest item in the subtree removeMax // removes largest item in the subtree ) // remove removes an item from the subtree rooted at this node. func (n *node) remove(item Item, minItems int, typ toRemove) Item { var i int var found bool switch typ { case removeMax: if len(n.children) == 0 { return n.items.pop() } i = len(n.items) case removeMin: if len(n.children) == 0 { return n.items.removeAt(0) } i = 0 case removeItem: i, found = n.items.find(item) if len(n.children) == 0 { if found { return n.items.removeAt(i) } return nil } default: panic("invalid type") } // If we get to here, we have children. if len(n.children[i].items) <= minItems { return n.growChildAndRemove(i, item, minItems, typ) } child := n.mutableChild(i) // Either we had enough items to begin with, or we've done some // merging/stealing, because we've got enough now and we're ready to return // stuff. if found { // The item exists at index 'i', and the child we've selected can give us a // predecessor, since if we've gotten here it's got > minItems items in it. out := n.items[i] // We use our special-case 'remove' call with typ=maxItem to pull the // predecessor of item i (the rightmost leaf of our immediate left child) // and set it into where we pulled the item from. n.items[i] = child.remove(nil, minItems, removeMax) return out } // Final recursive call. Once we're here, we know that the item isn't in this // node and that the child is big enough to remove from. return child.remove(item, minItems, typ) } // growChildAndRemove grows child 'i' to make sure it's possible to remove an // item from it while keeping it at minItems, then calls remove to actually // remove it. // // Most documentation says we have to do two sets of special casing: // 1) item is in this node // 2) item is in child // In both cases, we need to handle the two subcases: // A) node has enough values that it can spare one // B) node doesn't have enough values // For the latter, we have to check: // a) left sibling has node to spare // b) right sibling has node to spare // c) we must merge // To simplify our code here, we handle cases #1 and #2 the same: // If a node doesn't have enough items, we make sure it does (using a,b,c). // We then simply redo our remove call, and the second time (regardless of // whether we're in case 1 or 2), we'll have enough items and can guarantee // that we hit case A. func (n *node) growChildAndRemove(i int, item Item, minItems int, typ toRemove) Item { if i > 0 && len(n.children[i-1].items) > minItems { // Steal from left child child := n.mutableChild(i) stealFrom := n.mutableChild(i - 1) stolenItem := stealFrom.items.pop() child.items.insertAt(0, n.items[i-1]) n.items[i-1] = stolenItem if len(stealFrom.children) > 0 { child.children.insertAt(0, stealFrom.children.pop()) } } else if i < len(n.items) && len(n.children[i+1].items) > minItems { // steal from right child child := n.mutableChild(i) stealFrom := n.mutableChild(i + 1) stolenItem := stealFrom.items.removeAt(0) child.items = append(child.items, n.items[i]) n.items[i] = stolenItem if len(stealFrom.children) > 0 { child.children = append(child.children, stealFrom.children.removeAt(0)) } } else { if i >= len(n.items) { i-- } child := n.mutableChild(i) // merge with right child mergeItem := n.items.removeAt(i) mergeChild := n.children.removeAt(i + 1) child.items = append(child.items, mergeItem) child.items = append(child.items, mergeChild.items...) child.children = append(child.children, mergeChild.children...) n.cow.freeNode(mergeChild) } return n.remove(item, minItems, typ) } type direction int const ( descend = direction(-1) ascend = direction(+1) ) // iterate provides a simple method for iterating over elements in the tree. // // When ascending, the 'start' should be less than 'stop' and when descending, // the 'start' should be greater than 'stop'. Setting 'includeStart' to true // will force the iterator to include the first item when it equals 'start', // thus creating a "greaterOrEqual" or "lessThanEqual" rather than just a // "greaterThan" or "lessThan" queries. func (n *node) iterate(dir direction, start, stop Item, includeStart bool, hit bool, iter ItemIterator) (bool, bool) { var ok, found bool var index int switch dir { case ascend: if start != nil { index, _ = n.items.find(start) } for i := index; i < len(n.items); i++ { if len(n.children) > 0 { if hit, ok = n.children[i].iterate(dir, start, stop, includeStart, hit, iter); !ok { return hit, false } } if !includeStart && !hit && start != nil && !start.Less(n.items[i]) { hit = true continue } hit = true if stop != nil && !n.items[i].Less(stop) { return hit, false } if !iter(n.items[i]) { return hit, false } } if len(n.children) > 0 { if hit, ok = n.children[len(n.children)-1].iterate(dir, start, stop, includeStart, hit, iter); !ok { return hit, false } } case descend: if start != nil { index, found = n.items.find(start) if !found { index = index - 1 } } else { index = len(n.items) - 1 } for i := index; i >= 0; i-- { if start != nil && !n.items[i].Less(start) { if !includeStart || hit || start.Less(n.items[i]) { continue } } if len(n.children) > 0 { if hit, ok = n.children[i+1].iterate(dir, start, stop, includeStart, hit, iter); !ok { return hit, false } } if stop != nil && !stop.Less(n.items[i]) { return hit, false // continue } hit = true if !iter(n.items[i]) { return hit, false } } if len(n.children) > 0 { if hit, ok = n.children[0].iterate(dir, start, stop, includeStart, hit, iter); !ok { return hit, false } } } return hit, true } // Used for testing/debugging purposes. func (n *node) print(w io.Writer, level int) { fmt.Fprintf(w, "%sNODE:%v\n", strings.Repeat(" ", level), n.items) for _, c := range n.children { c.print(w, level+1) } } // BTree is an implementation of a B-Tree. // // BTree stores Item instances in an ordered structure, allowing easy insertion, // removal, and iteration. // // Write operations are not safe for concurrent mutation by multiple // goroutines, but Read operations are. type BTree struct { degree int length int root *node cow *copyOnWriteContext } // copyOnWriteContext pointers determine node ownership... a tree with a write // context equivalent to a node's write context is allowed to modify that node. // A tree whose write context does not match a node's is not allowed to modify // it, and must create a new, writable copy (IE: it's a Clone). // // When doing any write operation, we maintain the invariant that the current // node's context is equal to the context of the tree that requested the write. // We do this by, before we descend into any node, creating a copy with the // correct context if the contexts don't match. // // Since the node we're currently visiting on any write has the requesting // tree's context, that node is modifiable in place. Children of that node may // not share context, but before we descend into them, we'll make a mutable // copy. type copyOnWriteContext struct { freelist *FreeList } // Clone clones the btree, lazily. Clone should not be called concurrently, // but the original tree (t) and the new tree (t2) can be used concurrently // once the Clone call completes. // // The internal tree structure of b is marked read-only and shared between t and // t2. Writes to both t and t2 use copy-on-write logic, creating new nodes // whenever one of b's original nodes would have been modified. Read operations // should have no performance degredation. Write operations for both t and t2 // will initially experience minor slow-downs caused by additional allocs and // copies due to the aforementioned copy-on-write logic, but should converge to // the original performance characteristics of the original tree. func (t *BTree) Clone() (t2 *BTree) { // Create two entirely new copy-on-write contexts. // This operation effectively creates three trees: // the original, shared nodes (old b.cow) // the new b.cow nodes // the new out.cow nodes cow1, cow2 := *t.cow, *t.cow out := *t t.cow = &cow1 out.cow = &cow2 return &out } // maxItems returns the max number of items to allow per node. func (t *BTree) maxItems() int { return t.degree*2 - 1 } // minItems returns the min number of items to allow per node (ignored for the // root node). func (t *BTree) minItems() int { return t.degree - 1 } func (c *copyOnWriteContext) newNode() (n *node) { n = c.freelist.newNode() n.cow = c return } type freeType int const ( ftFreelistFull freeType = iota // node was freed (available for GC, not stored in freelist) ftStored // node was stored in the freelist for later use ftNotOwned // node was ignored by COW, since it's owned by another one ) // freeNode frees a node within a given COW context, if it's owned by that // context. It returns what happened to the node (see freeType const // documentation). func (c *copyOnWriteContext) freeNode(n *node) freeType { if n.cow == c { // clear to allow GC n.items.truncate(0) n.children.truncate(0) n.cow = nil if c.freelist.freeNode(n) { return ftStored } else { return ftFreelistFull } } else { return ftNotOwned } } // ReplaceOrInsert adds the given item to the tree. If an item in the tree // already equals the given one, it is removed from the tree and returned. // Otherwise, nil is returned. // // nil cannot be added to the tree (will panic). func (t *BTree) ReplaceOrInsert(item Item) Item { if item == nil { panic("nil item being added to BTree") } if t.root == nil { t.root = t.cow.newNode() t.root.items = append(t.root.items, item) t.length++ return nil } else { t.root = t.root.mutableFor(t.cow) if len(t.root.items) >= t.maxItems() { item2, second := t.root.split(t.maxItems() / 2) oldroot := t.root t.root = t.cow.newNode() t.root.items = append(t.root.items, item2) t.root.children = append(t.root.children, oldroot, second) } } out := t.root.insert(item, t.maxItems()) if out == nil { t.length++ } return out } // Delete removes an item equal to the passed in item from the tree, returning // it. If no such item exists, returns nil. func (t *BTree) Delete(item Item) Item { return t.deleteItem(item, removeItem) } // DeleteMin removes the smallest item in the tree and returns it. // If no such item exists, returns nil. func (t *BTree) DeleteMin() Item { return t.deleteItem(nil, removeMin) } // DeleteMax removes the largest item in the tree and returns it. // If no such item exists, returns nil. func (t *BTree) DeleteMax() Item { return t.deleteItem(nil, removeMax) } func (t *BTree) deleteItem(item Item, typ toRemove) Item { if t.root == nil || len(t.root.items) == 0 { return nil } t.root = t.root.mutableFor(t.cow) out := t.root.remove(item, t.minItems(), typ) if len(t.root.items) == 0 && len(t.root.children) > 0 { oldroot := t.root t.root = t.root.children[0] t.cow.freeNode(oldroot) } if out != nil { t.length-- } return out } // AscendRange calls the iterator for every value in the tree within the range // [greaterOrEqual, lessThan), until iterator returns false. func (t *BTree) AscendRange(greaterOrEqual, lessThan Item, iterator ItemIterator) { if t.root == nil { return } t.root.iterate(ascend, greaterOrEqual, lessThan, true, false, iterator) } // AscendLessThan calls the iterator for every value in the tree within the range // [first, pivot), until iterator returns false. func (t *BTree) AscendLessThan(pivot Item, iterator ItemIterator) { if t.root == nil { return } t.root.iterate(ascend, nil, pivot, false, false, iterator) } // AscendGreaterOrEqual calls the iterator for every value in the tree within // the range [pivot, last], until iterator returns false. func (t *BTree) AscendGreaterOrEqual(pivot Item, iterator ItemIterator) { if t.root == nil { return } t.root.iterate(ascend, pivot, nil, true, false, iterator) } // Ascend calls the iterator for every value in the tree within the range // [first, last], until iterator returns false. func (t *BTree) Ascend(iterator ItemIterator) { if t.root == nil { return } t.root.iterate(ascend, nil, nil, false, false, iterator) } // DescendRange calls the iterator for every value in the tree within the range // [lessOrEqual, greaterThan), until iterator returns false. func (t *BTree) DescendRange(lessOrEqual, greaterThan Item, iterator ItemIterator) { if t.root == nil { return } t.root.iterate(descend, lessOrEqual, greaterThan, true, false, iterator) } // DescendLessOrEqual calls the iterator for every value in the tree within the range // [pivot, first], until iterator returns false. func (t *BTree) DescendLessOrEqual(pivot Item, iterator ItemIterator) { if t.root == nil { return } t.root.iterate(descend, pivot, nil, true, false, iterator) } // DescendGreaterThan calls the iterator for every value in the tree within // the range [last, pivot), until iterator returns false. func (t *BTree) DescendGreaterThan(pivot Item, iterator ItemIterator) { if t.root == nil { return } t.root.iterate(descend, nil, pivot, false, false, iterator) } // Descend calls the iterator for every value in the tree within the range // [last, first], until iterator returns false. func (t *BTree) Descend(iterator ItemIterator) { if t.root == nil { return } t.root.iterate(descend, nil, nil, false, false, iterator) } // Get looks for the key item in the tree, returning it. It returns nil if // unable to find that item. func (t *BTree) Get(key Item) Item { if t.root == nil { return nil } return t.root.get(key) } // Min returns the smallest item in the tree, or nil if the tree is empty. func (t *BTree) Min() Item { return min(t.root) } // Max returns the largest item in the tree, or nil if the tree is empty. func (t *BTree) Max() Item { return max(t.root) } // Has returns true if the given key is in the tree. func (t *BTree) Has(key Item) bool { return t.Get(key) != nil } // Len returns the number of items currently in the tree. func (t *BTree) Len() int { return t.length } // Clear removes all items from the btree. If addNodesToFreelist is true, // t's nodes are added to its freelist as part of this call, until the freelist // is full. Otherwise, the root node is simply dereferenced and the subtree // left to Go's normal GC processes. // // This can be much faster // than calling Delete on all elements, because that requires finding/removing // each element in the tree and updating the tree accordingly. It also is // somewhat faster than creating a new tree to replace the old one, because // nodes from the old tree are reclaimed into the freelist for use by the new // one, instead of being lost to the garbage collector. // // This call takes: // O(1): when addNodesToFreelist is false, this is a single operation. // O(1): when the freelist is already full, it breaks out immediately // O(freelist size): when the freelist is empty and the nodes are all owned // by this tree, nodes are added to the freelist until full. // O(tree size): when all nodes are owned by another tree, all nodes are // iterated over looking for nodes to add to the freelist, and due to // ownership, none are. func (t *BTree) Clear(addNodesToFreelist bool) { if t.root != nil && addNodesToFreelist { t.root.reset(t.cow) } t.root, t.length = nil, 0 } // reset returns a subtree to the freelist. It breaks out immediately if the // freelist is full, since the only benefit of iterating is to fill that // freelist up. Returns true if parent reset call should continue. func (n *node) reset(c *copyOnWriteContext) bool { for _, child := range n.children { if !child.reset(c) { return false } } return c.freeNode(n) != ftFreelistFull } // Int implements the Item interface for integers. type Int int // Less returns true if int(a) < int(b). func (a Int) Less(b Item) bool { return a < b.(Int) } ================================================ FILE: vendor/github.com/google/gnostic/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/google/gnostic/compiler/README.md ================================================ # Compiler support code This directory contains compiler support code used by Gnostic and Gnostic extensions. ================================================ FILE: vendor/github.com/google/gnostic/compiler/context.go ================================================ // Copyright 2017 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package compiler import ( yaml "gopkg.in/yaml.v3" ) // Context contains state of the compiler as it traverses a document. type Context struct { Parent *Context Name string Node *yaml.Node ExtensionHandlers *[]ExtensionHandler } // NewContextWithExtensions returns a new object representing the compiler state func NewContextWithExtensions(name string, node *yaml.Node, parent *Context, extensionHandlers *[]ExtensionHandler) *Context { return &Context{Name: name, Node: node, Parent: parent, ExtensionHandlers: extensionHandlers} } // NewContext returns a new object representing the compiler state func NewContext(name string, node *yaml.Node, parent *Context) *Context { if parent != nil { return &Context{Name: name, Node: node, Parent: parent, ExtensionHandlers: parent.ExtensionHandlers} } return &Context{Name: name, Parent: parent, ExtensionHandlers: nil} } // Description returns a text description of the compiler state func (context *Context) Description() string { name := context.Name if context.Parent != nil { name = context.Parent.Description() + "." + name } return name } ================================================ FILE: vendor/github.com/google/gnostic/compiler/error.go ================================================ // Copyright 2017 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package compiler import "fmt" // Error represents compiler errors and their location in the document. type Error struct { Context *Context Message string } // NewError creates an Error. func NewError(context *Context, message string) *Error { return &Error{Context: context, Message: message} } func (err *Error) locationDescription() string { if err.Context.Node != nil { return fmt.Sprintf("[%d,%d] %s", err.Context.Node.Line, err.Context.Node.Column, err.Context.Description()) } return err.Context.Description() } // Error returns the string value of an Error. func (err *Error) Error() string { if err.Context == nil { return err.Message } return err.locationDescription() + " " + err.Message } // ErrorGroup is a container for groups of Error values. type ErrorGroup struct { Errors []error } // NewErrorGroupOrNil returns a new ErrorGroup for a slice of errors or nil if the slice is empty. func NewErrorGroupOrNil(errors []error) error { if len(errors) == 0 { return nil } else if len(errors) == 1 { return errors[0] } else { return &ErrorGroup{Errors: errors} } } func (group *ErrorGroup) Error() string { result := "" for i, err := range group.Errors { if i > 0 { result += "\n" } result += err.Error() } return result } ================================================ FILE: vendor/github.com/google/gnostic/compiler/extensions.go ================================================ // Copyright 2017 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package compiler import ( "bytes" "fmt" "os/exec" "strings" "github.com/golang/protobuf/proto" "github.com/golang/protobuf/ptypes/any" yaml "gopkg.in/yaml.v3" extensions "github.com/google/gnostic/extensions" ) // ExtensionHandler describes a binary that is called by the compiler to handle specification extensions. type ExtensionHandler struct { Name string } // CallExtension calls a binary extension handler. func CallExtension(context *Context, in *yaml.Node, extensionName string) (handled bool, response *any.Any, err error) { if context == nil || context.ExtensionHandlers == nil { return false, nil, nil } handled = false for _, handler := range *(context.ExtensionHandlers) { response, err = handler.handle(in, extensionName) if response == nil { continue } else { handled = true break } } return handled, response, err } func (extensionHandlers *ExtensionHandler) handle(in *yaml.Node, extensionName string) (*any.Any, error) { if extensionHandlers.Name != "" { yamlData, _ := yaml.Marshal(in) request := &extensions.ExtensionHandlerRequest{ CompilerVersion: &extensions.Version{ Major: 0, Minor: 1, Patch: 0, }, Wrapper: &extensions.Wrapper{ Version: "unknown", // TODO: set this to the type/version of spec being parsed. Yaml: string(yamlData), ExtensionName: extensionName, }, } requestBytes, _ := proto.Marshal(request) cmd := exec.Command(extensionHandlers.Name) cmd.Stdin = bytes.NewReader(requestBytes) output, err := cmd.Output() if err != nil { return nil, err } response := &extensions.ExtensionHandlerResponse{} err = proto.Unmarshal(output, response) if err != nil || !response.Handled { return nil, err } if len(response.Errors) != 0 { return nil, fmt.Errorf("Errors when parsing: %+v for field %s by vendor extension handler %s. Details %+v", in, extensionName, extensionHandlers.Name, strings.Join(response.Errors, ",")) } return response.Value, nil } return nil, nil } ================================================ FILE: vendor/github.com/google/gnostic/compiler/helpers.go ================================================ // Copyright 2017 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package compiler import ( "fmt" "regexp" "sort" "strconv" "gopkg.in/yaml.v3" "github.com/google/gnostic/jsonschema" ) // compiler helper functions, usually called from generated code // UnpackMap gets a *yaml.Node if possible. func UnpackMap(in *yaml.Node) (*yaml.Node, bool) { if in == nil { return nil, false } return in, true } // SortedKeysForMap returns the sorted keys of a yamlv2.MapSlice. func SortedKeysForMap(m *yaml.Node) []string { keys := make([]string, 0) if m.Kind == yaml.MappingNode { for i := 0; i < len(m.Content); i += 2 { keys = append(keys, m.Content[i].Value) } } sort.Strings(keys) return keys } // MapHasKey returns true if a yamlv2.MapSlice contains a specified key. func MapHasKey(m *yaml.Node, key string) bool { if m == nil { return false } if m.Kind == yaml.MappingNode { for i := 0; i < len(m.Content); i += 2 { itemKey := m.Content[i].Value if key == itemKey { return true } } } return false } // MapValueForKey gets the value of a map value for a specified key. func MapValueForKey(m *yaml.Node, key string) *yaml.Node { if m == nil { return nil } if m.Kind == yaml.MappingNode { for i := 0; i < len(m.Content); i += 2 { itemKey := m.Content[i].Value if key == itemKey { return m.Content[i+1] } } } return nil } // ConvertInterfaceArrayToStringArray converts an array of interfaces to an array of strings, if possible. func ConvertInterfaceArrayToStringArray(interfaceArray []interface{}) []string { stringArray := make([]string, 0) for _, item := range interfaceArray { v, ok := item.(string) if ok { stringArray = append(stringArray, v) } } return stringArray } // SequenceNodeForNode returns a node if it is a SequenceNode. func SequenceNodeForNode(node *yaml.Node) (*yaml.Node, bool) { if node.Kind != yaml.SequenceNode { return nil, false } return node, true } // BoolForScalarNode returns the bool value of a node. func BoolForScalarNode(node *yaml.Node) (bool, bool) { if node == nil { return false, false } if node.Kind == yaml.DocumentNode { return BoolForScalarNode(node.Content[0]) } if node.Kind != yaml.ScalarNode { return false, false } if node.Tag != "!!bool" { return false, false } v, err := strconv.ParseBool(node.Value) if err != nil { return false, false } return v, true } // IntForScalarNode returns the integer value of a node. func IntForScalarNode(node *yaml.Node) (int64, bool) { if node == nil { return 0, false } if node.Kind == yaml.DocumentNode { return IntForScalarNode(node.Content[0]) } if node.Kind != yaml.ScalarNode { return 0, false } if node.Tag != "!!int" { return 0, false } v, err := strconv.ParseInt(node.Value, 10, 64) if err != nil { return 0, false } return v, true } // FloatForScalarNode returns the float value of a node. func FloatForScalarNode(node *yaml.Node) (float64, bool) { if node == nil { return 0.0, false } if node.Kind == yaml.DocumentNode { return FloatForScalarNode(node.Content[0]) } if node.Kind != yaml.ScalarNode { return 0.0, false } if (node.Tag != "!!int") && (node.Tag != "!!float") { return 0.0, false } v, err := strconv.ParseFloat(node.Value, 64) if err != nil { return 0.0, false } return v, true } // StringForScalarNode returns the string value of a node. func StringForScalarNode(node *yaml.Node) (string, bool) { if node == nil { return "", false } if node.Kind == yaml.DocumentNode { return StringForScalarNode(node.Content[0]) } switch node.Kind { case yaml.ScalarNode: switch node.Tag { case "!!int": return node.Value, true case "!!str": return node.Value, true case "!!timestamp": return node.Value, true case "!!null": return "", true default: return "", false } default: return "", false } } // StringArrayForSequenceNode converts a sequence node to an array of strings, if possible. func StringArrayForSequenceNode(node *yaml.Node) []string { stringArray := make([]string, 0) for _, item := range node.Content { v, ok := StringForScalarNode(item) if ok { stringArray = append(stringArray, v) } } return stringArray } // MissingKeysInMap identifies which keys from a list of required keys are not in a map. func MissingKeysInMap(m *yaml.Node, requiredKeys []string) []string { missingKeys := make([]string, 0) for _, k := range requiredKeys { if !MapHasKey(m, k) { missingKeys = append(missingKeys, k) } } return missingKeys } // InvalidKeysInMap returns keys in a map that don't match a list of allowed keys and patterns. func InvalidKeysInMap(m *yaml.Node, allowedKeys []string, allowedPatterns []*regexp.Regexp) []string { invalidKeys := make([]string, 0) if m == nil || m.Kind != yaml.MappingNode { return invalidKeys } for i := 0; i < len(m.Content); i += 2 { key := m.Content[i].Value found := false // does the key match an allowed key? for _, allowedKey := range allowedKeys { if key == allowedKey { found = true break } } if !found { // does the key match an allowed pattern? for _, allowedPattern := range allowedPatterns { if allowedPattern.MatchString(key) { found = true break } } if !found { invalidKeys = append(invalidKeys, key) } } } return invalidKeys } // NewNullNode creates a new Null node. func NewNullNode() *yaml.Node { node := &yaml.Node{ Kind: yaml.ScalarNode, Tag: "!!null", } return node } // NewMappingNode creates a new Mapping node. func NewMappingNode() *yaml.Node { return &yaml.Node{ Kind: yaml.MappingNode, Content: make([]*yaml.Node, 0), } } // NewSequenceNode creates a new Sequence node. func NewSequenceNode() *yaml.Node { node := &yaml.Node{ Kind: yaml.SequenceNode, Content: make([]*yaml.Node, 0), } return node } // NewScalarNodeForString creates a new node to hold a string. func NewScalarNodeForString(s string) *yaml.Node { return &yaml.Node{ Kind: yaml.ScalarNode, Tag: "!!str", Value: s, } } // NewSequenceNodeForStringArray creates a new node to hold an array of strings. func NewSequenceNodeForStringArray(strings []string) *yaml.Node { node := &yaml.Node{ Kind: yaml.SequenceNode, Content: make([]*yaml.Node, 0), } for _, s := range strings { node.Content = append(node.Content, NewScalarNodeForString(s)) } return node } // NewScalarNodeForBool creates a new node to hold a bool. func NewScalarNodeForBool(b bool) *yaml.Node { return &yaml.Node{ Kind: yaml.ScalarNode, Tag: "!!bool", Value: fmt.Sprintf("%t", b), } } // NewScalarNodeForFloat creates a new node to hold a float. func NewScalarNodeForFloat(f float64) *yaml.Node { return &yaml.Node{ Kind: yaml.ScalarNode, Tag: "!!float", Value: fmt.Sprintf("%g", f), } } // NewScalarNodeForInt creates a new node to hold an integer. func NewScalarNodeForInt(i int64) *yaml.Node { return &yaml.Node{ Kind: yaml.ScalarNode, Tag: "!!int", Value: fmt.Sprintf("%d", i), } } // PluralProperties returns the string "properties" pluralized. func PluralProperties(count int) string { if count == 1 { return "property" } return "properties" } // StringArrayContainsValue returns true if a string array contains a specified value. func StringArrayContainsValue(array []string, value string) bool { for _, item := range array { if item == value { return true } } return false } // StringArrayContainsValues returns true if a string array contains all of a list of specified values. func StringArrayContainsValues(array []string, values []string) bool { for _, value := range values { if !StringArrayContainsValue(array, value) { return false } } return true } // StringValue returns the string value of an item. func StringValue(item interface{}) (value string, ok bool) { value, ok = item.(string) if ok { return value, ok } intValue, ok := item.(int) if ok { return strconv.Itoa(intValue), true } return "", false } // Description returns a human-readable represention of an item. func Description(item interface{}) string { value, ok := item.(*yaml.Node) if ok { return jsonschema.Render(value) } return fmt.Sprintf("%+v", item) } // Display returns a description of a node for use in error messages. func Display(node *yaml.Node) string { switch node.Kind { case yaml.ScalarNode: switch node.Tag { case "!!str": return fmt.Sprintf("%s (string)", node.Value) } } return fmt.Sprintf("%+v (%T)", node, node) } // Marshal creates a yaml version of a structure in our preferred style func Marshal(in *yaml.Node) []byte { clearStyle(in) //bytes, _ := yaml.Marshal(&yaml.Node{Kind: yaml.DocumentNode, Content: []*yaml.Node{in}}) bytes, _ := yaml.Marshal(in) return bytes } func clearStyle(node *yaml.Node) { node.Style = 0 for _, c := range node.Content { clearStyle(c) } } ================================================ FILE: vendor/github.com/google/gnostic/compiler/main.go ================================================ // Copyright 2017 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package compiler provides support functions to generated compiler code. package compiler ================================================ FILE: vendor/github.com/google/gnostic/compiler/reader.go ================================================ // Copyright 2017 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package compiler import ( "fmt" "io/ioutil" "log" "net/http" "net/url" "path/filepath" "strings" "sync" yaml "gopkg.in/yaml.v3" ) var verboseReader = false var fileCache map[string][]byte var infoCache map[string]*yaml.Node var fileCacheEnable = true var infoCacheEnable = true // These locks are used to synchronize accesses to the fileCache and infoCache // maps (above). They are global state and can throw thread-related errors // when modified from separate goroutines. The general strategy is to protect // all public functions in this file with mutex Lock() calls. As a result, to // avoid deadlock, these public functions should not call other public // functions, so some public functions have private equivalents. // In the future, we might consider replacing the maps with sync.Map and // eliminating these mutexes. var fileCacheMutex sync.Mutex var infoCacheMutex sync.Mutex func initializeFileCache() { if fileCache == nil { fileCache = make(map[string][]byte, 0) } } func initializeInfoCache() { if infoCache == nil { infoCache = make(map[string]*yaml.Node, 0) } } // EnableFileCache turns on file caching. func EnableFileCache() { fileCacheMutex.Lock() defer fileCacheMutex.Unlock() fileCacheEnable = true } // EnableInfoCache turns on parsed info caching. func EnableInfoCache() { infoCacheMutex.Lock() defer infoCacheMutex.Unlock() infoCacheEnable = true } // DisableFileCache turns off file caching. func DisableFileCache() { fileCacheMutex.Lock() defer fileCacheMutex.Unlock() fileCacheEnable = false } // DisableInfoCache turns off parsed info caching. func DisableInfoCache() { infoCacheMutex.Lock() defer infoCacheMutex.Unlock() infoCacheEnable = false } // RemoveFromFileCache removes an entry from the file cache. func RemoveFromFileCache(fileurl string) { fileCacheMutex.Lock() defer fileCacheMutex.Unlock() if !fileCacheEnable { return } initializeFileCache() delete(fileCache, fileurl) } // RemoveFromInfoCache removes an entry from the info cache. func RemoveFromInfoCache(filename string) { infoCacheMutex.Lock() defer infoCacheMutex.Unlock() if !infoCacheEnable { return } initializeInfoCache() delete(infoCache, filename) } // GetInfoCache returns the info cache map. func GetInfoCache() map[string]*yaml.Node { infoCacheMutex.Lock() defer infoCacheMutex.Unlock() if infoCache == nil { initializeInfoCache() } return infoCache } // ClearFileCache clears the file cache. func ClearFileCache() { fileCacheMutex.Lock() defer fileCacheMutex.Unlock() fileCache = make(map[string][]byte, 0) } // ClearInfoCache clears the info cache. func ClearInfoCache() { infoCacheMutex.Lock() defer infoCacheMutex.Unlock() infoCache = make(map[string]*yaml.Node) } // ClearCaches clears all caches. func ClearCaches() { ClearFileCache() ClearInfoCache() } // FetchFile gets a specified file from the local filesystem or a remote location. func FetchFile(fileurl string) ([]byte, error) { fileCacheMutex.Lock() defer fileCacheMutex.Unlock() return fetchFile(fileurl) } func fetchFile(fileurl string) ([]byte, error) { var bytes []byte initializeFileCache() if fileCacheEnable { bytes, ok := fileCache[fileurl] if ok { if verboseReader { log.Printf("Cache hit %s", fileurl) } return bytes, nil } if verboseReader { log.Printf("Fetching %s", fileurl) } } response, err := http.Get(fileurl) if err != nil { return nil, err } defer response.Body.Close() if response.StatusCode != 200 { return nil, fmt.Errorf("Error downloading %s: %s", fileurl, response.Status) } bytes, err = ioutil.ReadAll(response.Body) if fileCacheEnable && err == nil { fileCache[fileurl] = bytes } return bytes, err } // ReadBytesForFile reads the bytes of a file. func ReadBytesForFile(filename string) ([]byte, error) { fileCacheMutex.Lock() defer fileCacheMutex.Unlock() return readBytesForFile(filename) } func readBytesForFile(filename string) ([]byte, error) { // is the filename a url? fileurl, _ := url.Parse(filename) if fileurl.Scheme != "" { // yes, fetch it bytes, err := fetchFile(filename) if err != nil { return nil, err } return bytes, nil } // no, it's a local filename bytes, err := ioutil.ReadFile(filename) if err != nil { return nil, err } return bytes, nil } // ReadInfoFromBytes unmarshals a file as a *yaml.Node. func ReadInfoFromBytes(filename string, bytes []byte) (*yaml.Node, error) { infoCacheMutex.Lock() defer infoCacheMutex.Unlock() return readInfoFromBytes(filename, bytes) } func readInfoFromBytes(filename string, bytes []byte) (*yaml.Node, error) { initializeInfoCache() if infoCacheEnable { cachedInfo, ok := infoCache[filename] if ok { if verboseReader { log.Printf("Cache hit info for file %s", filename) } return cachedInfo, nil } if verboseReader { log.Printf("Reading info for file %s", filename) } } var info yaml.Node err := yaml.Unmarshal(bytes, &info) if err != nil { return nil, err } if infoCacheEnable && len(filename) > 0 { infoCache[filename] = &info } return &info, nil } // ReadInfoForRef reads a file and return the fragment needed to resolve a $ref. func ReadInfoForRef(basefile string, ref string) (*yaml.Node, error) { fileCacheMutex.Lock() defer fileCacheMutex.Unlock() infoCacheMutex.Lock() defer infoCacheMutex.Unlock() initializeInfoCache() if infoCacheEnable { info, ok := infoCache[ref] if ok { if verboseReader { log.Printf("Cache hit for ref %s#%s", basefile, ref) } return info, nil } if verboseReader { log.Printf("Reading info for ref %s#%s", basefile, ref) } } basedir, _ := filepath.Split(basefile) parts := strings.Split(ref, "#") var filename string if parts[0] != "" { filename = parts[0] if _, err := url.ParseRequestURI(parts[0]); err != nil { // It is not an URL, so the file is local filename = basedir + parts[0] } } else { filename = basefile } bytes, err := readBytesForFile(filename) if err != nil { return nil, err } info, err := readInfoFromBytes(filename, bytes) if info != nil && info.Kind == yaml.DocumentNode { info = info.Content[0] } if err != nil { log.Printf("File error: %v\n", err) } else { if info == nil { return nil, NewError(nil, fmt.Sprintf("could not resolve %s", ref)) } if len(parts) > 1 { path := strings.Split(parts[1], "/") for i, key := range path { if i > 0 { m := info if true { found := false for i := 0; i < len(m.Content); i += 2 { if m.Content[i].Value == key { info = m.Content[i+1] found = true } } if !found { infoCache[ref] = nil return nil, NewError(nil, fmt.Sprintf("could not resolve %s", ref)) } } } } } } if infoCacheEnable { infoCache[ref] = info } return info, nil } ================================================ FILE: vendor/github.com/google/gnostic/extensions/README.md ================================================ # Extensions **Extension Support is experimental.** This directory contains support code for building Gnostic extensio handlers and associated examples. Extension handlers can be used to compile vendor or specification extensions into protocol buffer structures. Like plugins, extension handlers are built as separate executables. Extension bodies are written to extension handlers as serialized ExtensionHandlerRequests. ================================================ FILE: vendor/github.com/google/gnostic/extensions/extension.pb.go ================================================ // Copyright 2017 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 // protoc v3.18.1 // source: extensions/extension.proto package gnostic_extension_v1 import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // The version number of Gnostic. type Version struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Major int32 `protobuf:"varint,1,opt,name=major,proto3" json:"major,omitempty"` Minor int32 `protobuf:"varint,2,opt,name=minor,proto3" json:"minor,omitempty"` Patch int32 `protobuf:"varint,3,opt,name=patch,proto3" json:"patch,omitempty"` // A suffix for alpha, beta or rc release, e.g., "alpha-1", "rc2". It should // be empty for mainline stable releases. Suffix string `protobuf:"bytes,4,opt,name=suffix,proto3" json:"suffix,omitempty"` } func (x *Version) Reset() { *x = Version{} if protoimpl.UnsafeEnabled { mi := &file_extensions_extension_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Version) String() string { return protoimpl.X.MessageStringOf(x) } func (*Version) ProtoMessage() {} func (x *Version) ProtoReflect() protoreflect.Message { mi := &file_extensions_extension_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Version.ProtoReflect.Descriptor instead. func (*Version) Descriptor() ([]byte, []int) { return file_extensions_extension_proto_rawDescGZIP(), []int{0} } func (x *Version) GetMajor() int32 { if x != nil { return x.Major } return 0 } func (x *Version) GetMinor() int32 { if x != nil { return x.Minor } return 0 } func (x *Version) GetPatch() int32 { if x != nil { return x.Patch } return 0 } func (x *Version) GetSuffix() string { if x != nil { return x.Suffix } return "" } // An encoded Request is written to the ExtensionHandler's stdin. type ExtensionHandlerRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // The extension to process. Wrapper *Wrapper `protobuf:"bytes,1,opt,name=wrapper,proto3" json:"wrapper,omitempty"` // The version number of Gnostic. CompilerVersion *Version `protobuf:"bytes,2,opt,name=compiler_version,json=compilerVersion,proto3" json:"compiler_version,omitempty"` } func (x *ExtensionHandlerRequest) Reset() { *x = ExtensionHandlerRequest{} if protoimpl.UnsafeEnabled { mi := &file_extensions_extension_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ExtensionHandlerRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*ExtensionHandlerRequest) ProtoMessage() {} func (x *ExtensionHandlerRequest) ProtoReflect() protoreflect.Message { mi := &file_extensions_extension_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ExtensionHandlerRequest.ProtoReflect.Descriptor instead. func (*ExtensionHandlerRequest) Descriptor() ([]byte, []int) { return file_extensions_extension_proto_rawDescGZIP(), []int{1} } func (x *ExtensionHandlerRequest) GetWrapper() *Wrapper { if x != nil { return x.Wrapper } return nil } func (x *ExtensionHandlerRequest) GetCompilerVersion() *Version { if x != nil { return x.CompilerVersion } return nil } // The extensions writes an encoded ExtensionHandlerResponse to stdout. type ExtensionHandlerResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // true if the extension is handled by the extension handler; false otherwise Handled bool `protobuf:"varint,1,opt,name=handled,proto3" json:"handled,omitempty"` // Error message(s). If non-empty, the extension handling failed. // The extension handler process should exit with status code zero // even if it reports an error in this way. // // This should be used to indicate errors which prevent the extension from // operating as intended. Errors which indicate a problem in gnostic // itself -- such as the input Document being unparseable -- should be // reported by writing a message to stderr and exiting with a non-zero // status code. Errors []string `protobuf:"bytes,2,rep,name=errors,proto3" json:"errors,omitempty"` // text output Value *anypb.Any `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` } func (x *ExtensionHandlerResponse) Reset() { *x = ExtensionHandlerResponse{} if protoimpl.UnsafeEnabled { mi := &file_extensions_extension_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ExtensionHandlerResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*ExtensionHandlerResponse) ProtoMessage() {} func (x *ExtensionHandlerResponse) ProtoReflect() protoreflect.Message { mi := &file_extensions_extension_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ExtensionHandlerResponse.ProtoReflect.Descriptor instead. func (*ExtensionHandlerResponse) Descriptor() ([]byte, []int) { return file_extensions_extension_proto_rawDescGZIP(), []int{2} } func (x *ExtensionHandlerResponse) GetHandled() bool { if x != nil { return x.Handled } return false } func (x *ExtensionHandlerResponse) GetErrors() []string { if x != nil { return x.Errors } return nil } func (x *ExtensionHandlerResponse) GetValue() *anypb.Any { if x != nil { return x.Value } return nil } type Wrapper struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // version of the OpenAPI specification in which this extension was written. Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` // Name of the extension. ExtensionName string `protobuf:"bytes,2,opt,name=extension_name,json=extensionName,proto3" json:"extension_name,omitempty"` // YAML-formatted extension value. Yaml string `protobuf:"bytes,3,opt,name=yaml,proto3" json:"yaml,omitempty"` } func (x *Wrapper) Reset() { *x = Wrapper{} if protoimpl.UnsafeEnabled { mi := &file_extensions_extension_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Wrapper) String() string { return protoimpl.X.MessageStringOf(x) } func (*Wrapper) ProtoMessage() {} func (x *Wrapper) ProtoReflect() protoreflect.Message { mi := &file_extensions_extension_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Wrapper.ProtoReflect.Descriptor instead. func (*Wrapper) Descriptor() ([]byte, []int) { return file_extensions_extension_proto_rawDescGZIP(), []int{3} } func (x *Wrapper) GetVersion() string { if x != nil { return x.Version } return "" } func (x *Wrapper) GetExtensionName() string { if x != nil { return x.ExtensionName } return "" } func (x *Wrapper) GetYaml() string { if x != nil { return x.Yaml } return "" } var File_extensions_extension_proto protoreflect.FileDescriptor var file_extensions_extension_proto_rawDesc = []byte{ 0x0a, 0x1a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x14, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x63, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x70, 0x61, 0x74, 0x63, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x22, 0x9c, 0x01, 0x0a, 0x17, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x37, 0x0a, 0x07, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x52, 0x07, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x48, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x78, 0x0a, 0x18, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x5e, 0x0a, 0x07, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x79, 0x61, 0x6d, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x79, 0x61, 0x6d, 0x6c, 0x42, 0x4d, 0x0a, 0x0e, 0x6f, 0x72, 0x67, 0x2e, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x2e, 0x76, 0x31, 0x42, 0x10, 0x47, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x01, 0x5a, 0x21, 0x2e, 0x2f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x3b, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x47, 0x4e, 0x58, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_extensions_extension_proto_rawDescOnce sync.Once file_extensions_extension_proto_rawDescData = file_extensions_extension_proto_rawDesc ) func file_extensions_extension_proto_rawDescGZIP() []byte { file_extensions_extension_proto_rawDescOnce.Do(func() { file_extensions_extension_proto_rawDescData = protoimpl.X.CompressGZIP(file_extensions_extension_proto_rawDescData) }) return file_extensions_extension_proto_rawDescData } var file_extensions_extension_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_extensions_extension_proto_goTypes = []interface{}{ (*Version)(nil), // 0: gnostic.extension.v1.Version (*ExtensionHandlerRequest)(nil), // 1: gnostic.extension.v1.ExtensionHandlerRequest (*ExtensionHandlerResponse)(nil), // 2: gnostic.extension.v1.ExtensionHandlerResponse (*Wrapper)(nil), // 3: gnostic.extension.v1.Wrapper (*anypb.Any)(nil), // 4: google.protobuf.Any } var file_extensions_extension_proto_depIdxs = []int32{ 3, // 0: gnostic.extension.v1.ExtensionHandlerRequest.wrapper:type_name -> gnostic.extension.v1.Wrapper 0, // 1: gnostic.extension.v1.ExtensionHandlerRequest.compiler_version:type_name -> gnostic.extension.v1.Version 4, // 2: gnostic.extension.v1.ExtensionHandlerResponse.value:type_name -> google.protobuf.Any 3, // [3:3] is the sub-list for method output_type 3, // [3:3] is the sub-list for method input_type 3, // [3:3] is the sub-list for extension type_name 3, // [3:3] is the sub-list for extension extendee 0, // [0:3] is the sub-list for field type_name } func init() { file_extensions_extension_proto_init() } func file_extensions_extension_proto_init() { if File_extensions_extension_proto != nil { return } if !protoimpl.UnsafeEnabled { file_extensions_extension_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Version); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_extensions_extension_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ExtensionHandlerRequest); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_extensions_extension_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ExtensionHandlerResponse); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_extensions_extension_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Wrapper); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_extensions_extension_proto_rawDesc, NumEnums: 0, NumMessages: 4, NumExtensions: 0, NumServices: 0, }, GoTypes: file_extensions_extension_proto_goTypes, DependencyIndexes: file_extensions_extension_proto_depIdxs, MessageInfos: file_extensions_extension_proto_msgTypes, }.Build() File_extensions_extension_proto = out.File file_extensions_extension_proto_rawDesc = nil file_extensions_extension_proto_goTypes = nil file_extensions_extension_proto_depIdxs = nil } ================================================ FILE: vendor/github.com/google/gnostic/extensions/extension.proto ================================================ // Copyright 2017 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. syntax = "proto3"; package gnostic.extension.v1; import "google/protobuf/any.proto"; // This option lets the proto compiler generate Java code inside the package // name (see below) instead of inside an outer class. It creates a simpler // developer experience by reducing one-level of name nesting and be // consistent with most programming languages that don't support outer classes. option java_multiple_files = true; // The Java outer classname should be the filename in UpperCamelCase. This // class is only used to hold proto descriptor, so developers don't need to // work with it directly. option java_outer_classname = "GnosticExtension"; // The Java package name must be proto package name with proper prefix. option java_package = "org.gnostic.v1"; // A reasonable prefix for the Objective-C symbols generated from the package. // It should at a minimum be 3 characters long, all uppercase, and convention // is to use an abbreviation of the package name. Something short, but // hopefully unique enough to not conflict with things that may come along in // the future. 'GPB' is reserved for the protocol buffer implementation itself. // // "Gnostic Extension" option objc_class_prefix = "GNX"; // The Go package name. option go_package = "./extensions;gnostic_extension_v1"; // The version number of Gnostic. message Version { int32 major = 1; int32 minor = 2; int32 patch = 3; // A suffix for alpha, beta or rc release, e.g., "alpha-1", "rc2". It should // be empty for mainline stable releases. string suffix = 4; } // An encoded Request is written to the ExtensionHandler's stdin. message ExtensionHandlerRequest { // The extension to process. Wrapper wrapper = 1; // The version number of Gnostic. Version compiler_version = 2; } // The extensions writes an encoded ExtensionHandlerResponse to stdout. message ExtensionHandlerResponse { // true if the extension is handled by the extension handler; false otherwise bool handled = 1; // Error message(s). If non-empty, the extension handling failed. // The extension handler process should exit with status code zero // even if it reports an error in this way. // // This should be used to indicate errors which prevent the extension from // operating as intended. Errors which indicate a problem in gnostic // itself -- such as the input Document being unparseable -- should be // reported by writing a message to stderr and exiting with a non-zero // status code. repeated string errors = 2; // text output google.protobuf.Any value = 3; } message Wrapper { // version of the OpenAPI specification in which this extension was written. string version = 1; // Name of the extension. string extension_name = 2; // YAML-formatted extension value. string yaml = 3; } ================================================ FILE: vendor/github.com/google/gnostic/extensions/extensions.go ================================================ // Copyright 2017 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package gnostic_extension_v1 import ( "io/ioutil" "log" "os" "github.com/golang/protobuf/proto" "github.com/golang/protobuf/ptypes" ) type extensionHandler func(name string, yamlInput string) (bool, proto.Message, error) // Main implements the main program of an extension handler. func Main(handler extensionHandler) { // unpack the request data, err := ioutil.ReadAll(os.Stdin) if err != nil { log.Println("File error:", err.Error()) os.Exit(1) } if len(data) == 0 { log.Println("No input data.") os.Exit(1) } request := &ExtensionHandlerRequest{} err = proto.Unmarshal(data, request) if err != nil { log.Println("Input error:", err.Error()) os.Exit(1) } // call the handler handled, output, err := handler(request.Wrapper.ExtensionName, request.Wrapper.Yaml) // respond with the output of the handler response := &ExtensionHandlerResponse{ Handled: false, // default assumption Errors: make([]string, 0), } if err != nil { response.Errors = append(response.Errors, err.Error()) } else if handled { response.Handled = true response.Value, err = ptypes.MarshalAny(output) if err != nil { response.Errors = append(response.Errors, err.Error()) } } responseBytes, _ := proto.Marshal(response) os.Stdout.Write(responseBytes) } ================================================ FILE: vendor/github.com/google/gnostic/jsonschema/README.md ================================================ # jsonschema This directory contains code for reading, writing, and manipulating JSON schemas. ================================================ FILE: vendor/github.com/google/gnostic/jsonschema/base.go ================================================ // THIS FILE IS AUTOMATICALLY GENERATED. package jsonschema import ( "encoding/base64" ) func baseSchemaBytes() ([]byte, error){ return base64.StdEncoding.DecodeString( `ewogICAgImlkIjogImh0dHA6Ly9qc29uLXNjaGVtYS5vcmcvZHJhZnQtMDQvc2NoZW1hIyIsCiAgICAi JHNjaGVtYSI6ICJodHRwOi8vanNvbi1zY2hlbWEub3JnL2RyYWZ0LTA0L3NjaGVtYSMiLAogICAgImRl c2NyaXB0aW9uIjogIkNvcmUgc2NoZW1hIG1ldGEtc2NoZW1hIiwKICAgICJkZWZpbml0aW9ucyI6IHsK ICAgICAgICAic2NoZW1hQXJyYXkiOiB7CiAgICAgICAgICAgICJ0eXBlIjogImFycmF5IiwKICAgICAg ICAgICAgIm1pbkl0ZW1zIjogMSwKICAgICAgICAgICAgIml0ZW1zIjogeyAiJHJlZiI6ICIjIiB9CiAg ICAgICAgfSwKICAgICAgICAicG9zaXRpdmVJbnRlZ2VyIjogewogICAgICAgICAgICAidHlwZSI6ICJp bnRlZ2VyIiwKICAgICAgICAgICAgIm1pbmltdW0iOiAwCiAgICAgICAgfSwKICAgICAgICAicG9zaXRp dmVJbnRlZ2VyRGVmYXVsdDAiOiB7CiAgICAgICAgICAgICJhbGxPZiI6IFsgeyAiJHJlZiI6ICIjL2Rl ZmluaXRpb25zL3Bvc2l0aXZlSW50ZWdlciIgfSwgeyAiZGVmYXVsdCI6IDAgfSBdCiAgICAgICAgfSwK ICAgICAgICAic2ltcGxlVHlwZXMiOiB7CiAgICAgICAgICAgICJlbnVtIjogWyAiYXJyYXkiLCAiYm9v bGVhbiIsICJpbnRlZ2VyIiwgIm51bGwiLCAibnVtYmVyIiwgIm9iamVjdCIsICJzdHJpbmciIF0KICAg ICAgICB9LAogICAgICAgICJzdHJpbmdBcnJheSI6IHsKICAgICAgICAgICAgInR5cGUiOiAiYXJyYXki LAogICAgICAgICAgICAiaXRlbXMiOiB7ICJ0eXBlIjogInN0cmluZyIgfSwKICAgICAgICAgICAgIm1p bkl0ZW1zIjogMSwKICAgICAgICAgICAgInVuaXF1ZUl0ZW1zIjogdHJ1ZQogICAgICAgIH0KICAgIH0s CiAgICAidHlwZSI6ICJvYmplY3QiLAogICAgInByb3BlcnRpZXMiOiB7CiAgICAgICAgImlkIjogewog ICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciLAogICAgICAgICAgICAiZm9ybWF0IjogInVyaSIKICAg ICAgICB9LAogICAgICAgICIkc2NoZW1hIjogewogICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciLAog ICAgICAgICAgICAiZm9ybWF0IjogInVyaSIKICAgICAgICB9LAogICAgICAgICJ0aXRsZSI6IHsKICAg ICAgICAgICAgInR5cGUiOiAic3RyaW5nIgogICAgICAgIH0sCiAgICAgICAgImRlc2NyaXB0aW9uIjog ewogICAgICAgICAgICAidHlwZSI6ICJzdHJpbmciCiAgICAgICAgfSwKICAgICAgICAiZGVmYXVsdCI6 IHt9LAogICAgICAgICJtdWx0aXBsZU9mIjogewogICAgICAgICAgICAidHlwZSI6ICJudW1iZXIiLAog ICAgICAgICAgICAibWluaW11bSI6IDAsCiAgICAgICAgICAgICJleGNsdXNpdmVNaW5pbXVtIjogdHJ1 ZQogICAgICAgIH0sCiAgICAgICAgIm1heGltdW0iOiB7CiAgICAgICAgICAgICJ0eXBlIjogIm51bWJl ciIKICAgICAgICB9LAogICAgICAgICJleGNsdXNpdmVNYXhpbXVtIjogewogICAgICAgICAgICAidHlw ZSI6ICJib29sZWFuIiwKICAgICAgICAgICAgImRlZmF1bHQiOiBmYWxzZQogICAgICAgIH0sCiAgICAg ICAgIm1pbmltdW0iOiB7CiAgICAgICAgICAgICJ0eXBlIjogIm51bWJlciIKICAgICAgICB9LAogICAg ICAgICJleGNsdXNpdmVNaW5pbXVtIjogewogICAgICAgICAgICAidHlwZSI6ICJib29sZWFuIiwKICAg ICAgICAgICAgImRlZmF1bHQiOiBmYWxzZQogICAgICAgIH0sCiAgICAgICAgIm1heExlbmd0aCI6IHsg IiRyZWYiOiAiIy9kZWZpbml0aW9ucy9wb3NpdGl2ZUludGVnZXIiIH0sCiAgICAgICAgIm1pbkxlbmd0 aCI6IHsgIiRyZWYiOiAiIy9kZWZpbml0aW9ucy9wb3NpdGl2ZUludGVnZXJEZWZhdWx0MCIgfSwKICAg ICAgICAicGF0dGVybiI6IHsKICAgICAgICAgICAgInR5cGUiOiAic3RyaW5nIiwKICAgICAgICAgICAg ImZvcm1hdCI6ICJyZWdleCIKICAgICAgICB9LAogICAgICAgICJhZGRpdGlvbmFsSXRlbXMiOiB7CiAg ICAgICAgICAgICJhbnlPZiI6IFsKICAgICAgICAgICAgICAgIHsgInR5cGUiOiAiYm9vbGVhbiIgfSwK ICAgICAgICAgICAgICAgIHsgIiRyZWYiOiAiIyIgfQogICAgICAgICAgICBdLAogICAgICAgICAgICAi ZGVmYXVsdCI6IHt9CiAgICAgICAgfSwKICAgICAgICAiaXRlbXMiOiB7CiAgICAgICAgICAgICJhbnlP ZiI6IFsKICAgICAgICAgICAgICAgIHsgIiRyZWYiOiAiIyIgfSwKICAgICAgICAgICAgICAgIHsgIiRy ZWYiOiAiIy9kZWZpbml0aW9ucy9zY2hlbWFBcnJheSIgfQogICAgICAgICAgICBdLAogICAgICAgICAg ICAiZGVmYXVsdCI6IHt9CiAgICAgICAgfSwKICAgICAgICAibWF4SXRlbXMiOiB7ICIkcmVmIjogIiMv ZGVmaW5pdGlvbnMvcG9zaXRpdmVJbnRlZ2VyIiB9LAogICAgICAgICJtaW5JdGVtcyI6IHsgIiRyZWYi OiAiIy9kZWZpbml0aW9ucy9wb3NpdGl2ZUludGVnZXJEZWZhdWx0MCIgfSwKICAgICAgICAidW5pcXVl SXRlbXMiOiB7CiAgICAgICAgICAgICJ0eXBlIjogImJvb2xlYW4iLAogICAgICAgICAgICAiZGVmYXVs dCI6IGZhbHNlCiAgICAgICAgfSwKICAgICAgICAibWF4UHJvcGVydGllcyI6IHsgIiRyZWYiOiAiIy9k ZWZpbml0aW9ucy9wb3NpdGl2ZUludGVnZXIiIH0sCiAgICAgICAgIm1pblByb3BlcnRpZXMiOiB7ICIk cmVmIjogIiMvZGVmaW5pdGlvbnMvcG9zaXRpdmVJbnRlZ2VyRGVmYXVsdDAiIH0sCiAgICAgICAgInJl cXVpcmVkIjogeyAiJHJlZiI6ICIjL2RlZmluaXRpb25zL3N0cmluZ0FycmF5IiB9LAogICAgICAgICJh ZGRpdGlvbmFsUHJvcGVydGllcyI6IHsKICAgICAgICAgICAgImFueU9mIjogWwogICAgICAgICAgICAg ICAgeyAidHlwZSI6ICJib29sZWFuIiB9LAogICAgICAgICAgICAgICAgeyAiJHJlZiI6ICIjIiB9CiAg ICAgICAgICAgIF0sCiAgICAgICAgICAgICJkZWZhdWx0Ijoge30KICAgICAgICB9LAogICAgICAgICJk ZWZpbml0aW9ucyI6IHsKICAgICAgICAgICAgInR5cGUiOiAib2JqZWN0IiwKICAgICAgICAgICAgImFk ZGl0aW9uYWxQcm9wZXJ0aWVzIjogeyAiJHJlZiI6ICIjIiB9LAogICAgICAgICAgICAiZGVmYXVsdCI6 IHt9CiAgICAgICAgfSwKICAgICAgICAicHJvcGVydGllcyI6IHsKICAgICAgICAgICAgInR5cGUiOiAi b2JqZWN0IiwKICAgICAgICAgICAgImFkZGl0aW9uYWxQcm9wZXJ0aWVzIjogeyAiJHJlZiI6ICIjIiB9 LAogICAgICAgICAgICAiZGVmYXVsdCI6IHt9CiAgICAgICAgfSwKICAgICAgICAicGF0dGVyblByb3Bl cnRpZXMiOiB7CiAgICAgICAgICAgICJ0eXBlIjogIm9iamVjdCIsCiAgICAgICAgICAgICJhZGRpdGlv bmFsUHJvcGVydGllcyI6IHsgIiRyZWYiOiAiIyIgfSwKICAgICAgICAgICAgImRlZmF1bHQiOiB7fQog ICAgICAgIH0sCiAgICAgICAgImRlcGVuZGVuY2llcyI6IHsKICAgICAgICAgICAgInR5cGUiOiAib2Jq ZWN0IiwKICAgICAgICAgICAgImFkZGl0aW9uYWxQcm9wZXJ0aWVzIjogewogICAgICAgICAgICAgICAg ImFueU9mIjogWwogICAgICAgICAgICAgICAgICAgIHsgIiRyZWYiOiAiIyIgfSwKICAgICAgICAgICAg ICAgICAgICB7ICIkcmVmIjogIiMvZGVmaW5pdGlvbnMvc3RyaW5nQXJyYXkiIH0KICAgICAgICAgICAg ICAgIF0KICAgICAgICAgICAgfQogICAgICAgIH0sCiAgICAgICAgImVudW0iOiB7CiAgICAgICAgICAg ICJ0eXBlIjogImFycmF5IiwKICAgICAgICAgICAgIm1pbkl0ZW1zIjogMSwKICAgICAgICAgICAgInVu aXF1ZUl0ZW1zIjogdHJ1ZQogICAgICAgIH0sCiAgICAgICAgInR5cGUiOiB7CiAgICAgICAgICAgICJh bnlPZiI6IFsKICAgICAgICAgICAgICAgIHsgIiRyZWYiOiAiIy9kZWZpbml0aW9ucy9zaW1wbGVUeXBl cyIgfSwKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICAidHlwZSI6ICJhcnJheSIs CiAgICAgICAgICAgICAgICAgICAgIml0ZW1zIjogeyAiJHJlZiI6ICIjL2RlZmluaXRpb25zL3NpbXBs ZVR5cGVzIiB9LAogICAgICAgICAgICAgICAgICAgICJtaW5JdGVtcyI6IDEsCiAgICAgICAgICAgICAg ICAgICAgInVuaXF1ZUl0ZW1zIjogdHJ1ZQogICAgICAgICAgICAgICAgfQogICAgICAgICAgICBdCiAg ICAgICAgfSwKICAgICAgICAiYWxsT2YiOiB7ICIkcmVmIjogIiMvZGVmaW5pdGlvbnMvc2NoZW1hQXJy YXkiIH0sCiAgICAgICAgImFueU9mIjogeyAiJHJlZiI6ICIjL2RlZmluaXRpb25zL3NjaGVtYUFycmF5 IiB9LAogICAgICAgICJvbmVPZiI6IHsgIiRyZWYiOiAiIy9kZWZpbml0aW9ucy9zY2hlbWFBcnJheSIg fSwKICAgICAgICAibm90IjogeyAiJHJlZiI6ICIjIiB9CiAgICB9LAogICAgImRlcGVuZGVuY2llcyI6 IHsKICAgICAgICAiZXhjbHVzaXZlTWF4aW11bSI6IFsgIm1heGltdW0iIF0sCiAgICAgICAgImV4Y2x1 c2l2ZU1pbmltdW0iOiBbICJtaW5pbXVtIiBdCiAgICB9LAogICAgImRlZmF1bHQiOiB7fQp9Cg==`)} ================================================ FILE: vendor/github.com/google/gnostic/jsonschema/display.go ================================================ // Copyright 2017 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package jsonschema import ( "fmt" "strings" ) // // DISPLAY // The following methods display Schemas. // // Description returns a string representation of a string or string array. func (s *StringOrStringArray) Description() string { if s.String != nil { return *s.String } if s.StringArray != nil { return strings.Join(*s.StringArray, ", ") } return "" } // Returns a string representation of a Schema. func (schema *Schema) String() string { return schema.describeSchema("") } // Helper: Returns a string representation of a Schema indented by a specified string. func (schema *Schema) describeSchema(indent string) string { result := "" if schema.Schema != nil { result += indent + "$schema: " + *(schema.Schema) + "\n" } if schema.ID != nil { result += indent + "id: " + *(schema.ID) + "\n" } if schema.MultipleOf != nil { result += indent + fmt.Sprintf("multipleOf: %+v\n", *(schema.MultipleOf)) } if schema.Maximum != nil { result += indent + fmt.Sprintf("maximum: %+v\n", *(schema.Maximum)) } if schema.ExclusiveMaximum != nil { result += indent + fmt.Sprintf("exclusiveMaximum: %+v\n", *(schema.ExclusiveMaximum)) } if schema.Minimum != nil { result += indent + fmt.Sprintf("minimum: %+v\n", *(schema.Minimum)) } if schema.ExclusiveMinimum != nil { result += indent + fmt.Sprintf("exclusiveMinimum: %+v\n", *(schema.ExclusiveMinimum)) } if schema.MaxLength != nil { result += indent + fmt.Sprintf("maxLength: %+v\n", *(schema.MaxLength)) } if schema.MinLength != nil { result += indent + fmt.Sprintf("minLength: %+v\n", *(schema.MinLength)) } if schema.Pattern != nil { result += indent + fmt.Sprintf("pattern: %+v\n", *(schema.Pattern)) } if schema.AdditionalItems != nil { s := schema.AdditionalItems.Schema if s != nil { result += indent + "additionalItems:\n" result += s.describeSchema(indent + " ") } else { b := *(schema.AdditionalItems.Boolean) result += indent + fmt.Sprintf("additionalItems: %+v\n", b) } } if schema.Items != nil { result += indent + "items:\n" items := schema.Items if items.SchemaArray != nil { for i, s := range *(items.SchemaArray) { result += indent + " " + fmt.Sprintf("%d", i) + ":\n" result += s.describeSchema(indent + " " + " ") } } else if items.Schema != nil { result += items.Schema.describeSchema(indent + " " + " ") } } if schema.MaxItems != nil { result += indent + fmt.Sprintf("maxItems: %+v\n", *(schema.MaxItems)) } if schema.MinItems != nil { result += indent + fmt.Sprintf("minItems: %+v\n", *(schema.MinItems)) } if schema.UniqueItems != nil { result += indent + fmt.Sprintf("uniqueItems: %+v\n", *(schema.UniqueItems)) } if schema.MaxProperties != nil { result += indent + fmt.Sprintf("maxProperties: %+v\n", *(schema.MaxProperties)) } if schema.MinProperties != nil { result += indent + fmt.Sprintf("minProperties: %+v\n", *(schema.MinProperties)) } if schema.Required != nil { result += indent + fmt.Sprintf("required: %+v\n", *(schema.Required)) } if schema.AdditionalProperties != nil { s := schema.AdditionalProperties.Schema if s != nil { result += indent + "additionalProperties:\n" result += s.describeSchema(indent + " ") } else { b := *(schema.AdditionalProperties.Boolean) result += indent + fmt.Sprintf("additionalProperties: %+v\n", b) } } if schema.Properties != nil { result += indent + "properties:\n" for _, pair := range *(schema.Properties) { name := pair.Name s := pair.Value result += indent + " " + name + ":\n" result += s.describeSchema(indent + " " + " ") } } if schema.PatternProperties != nil { result += indent + "patternProperties:\n" for _, pair := range *(schema.PatternProperties) { name := pair.Name s := pair.Value result += indent + " " + name + ":\n" result += s.describeSchema(indent + " " + " ") } } if schema.Dependencies != nil { result += indent + "dependencies:\n" for _, pair := range *(schema.Dependencies) { name := pair.Name schemaOrStringArray := pair.Value s := schemaOrStringArray.Schema if s != nil { result += indent + " " + name + ":\n" result += s.describeSchema(indent + " " + " ") } else { a := schemaOrStringArray.StringArray if a != nil { result += indent + " " + name + ":\n" for _, s2 := range *a { result += indent + " " + " " + s2 + "\n" } } } } } if schema.Enumeration != nil { result += indent + "enumeration:\n" for _, value := range *(schema.Enumeration) { if value.String != nil { result += indent + " " + fmt.Sprintf("%+v\n", *value.String) } else { result += indent + " " + fmt.Sprintf("%+v\n", *value.Bool) } } } if schema.Type != nil { result += indent + fmt.Sprintf("type: %+v\n", schema.Type.Description()) } if schema.AllOf != nil { result += indent + "allOf:\n" for _, s := range *(schema.AllOf) { result += s.describeSchema(indent + " ") result += indent + "-\n" } } if schema.AnyOf != nil { result += indent + "anyOf:\n" for _, s := range *(schema.AnyOf) { result += s.describeSchema(indent + " ") result += indent + "-\n" } } if schema.OneOf != nil { result += indent + "oneOf:\n" for _, s := range *(schema.OneOf) { result += s.describeSchema(indent + " ") result += indent + "-\n" } } if schema.Not != nil { result += indent + "not:\n" result += schema.Not.describeSchema(indent + " ") } if schema.Definitions != nil { result += indent + "definitions:\n" for _, pair := range *(schema.Definitions) { name := pair.Name s := pair.Value result += indent + " " + name + ":\n" result += s.describeSchema(indent + " " + " ") } } if schema.Title != nil { result += indent + "title: " + *(schema.Title) + "\n" } if schema.Description != nil { result += indent + "description: " + *(schema.Description) + "\n" } if schema.Default != nil { result += indent + "default:\n" result += indent + fmt.Sprintf(" %+v\n", *(schema.Default)) } if schema.Format != nil { result += indent + "format: " + *(schema.Format) + "\n" } if schema.Ref != nil { result += indent + "$ref: " + *(schema.Ref) + "\n" } return result } ================================================ FILE: vendor/github.com/google/gnostic/jsonschema/models.go ================================================ // Copyright 2017 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package jsonschema supports the reading, writing, and manipulation // of JSON Schemas. package jsonschema import "gopkg.in/yaml.v3" // The Schema struct models a JSON Schema and, because schemas are // defined hierarchically, contains many references to itself. // All fields are pointers and are nil if the associated values // are not specified. type Schema struct { Schema *string // $schema ID *string // id keyword used for $ref resolution scope Ref *string // $ref, i.e. JSON Pointers // http://json-schema.org/latest/json-schema-validation.html // 5.1. Validation keywords for numeric instances (number and integer) MultipleOf *SchemaNumber Maximum *SchemaNumber ExclusiveMaximum *bool Minimum *SchemaNumber ExclusiveMinimum *bool // 5.2. Validation keywords for strings MaxLength *int64 MinLength *int64 Pattern *string // 5.3. Validation keywords for arrays AdditionalItems *SchemaOrBoolean Items *SchemaOrSchemaArray MaxItems *int64 MinItems *int64 UniqueItems *bool // 5.4. Validation keywords for objects MaxProperties *int64 MinProperties *int64 Required *[]string AdditionalProperties *SchemaOrBoolean Properties *[]*NamedSchema PatternProperties *[]*NamedSchema Dependencies *[]*NamedSchemaOrStringArray // 5.5. Validation keywords for any instance type Enumeration *[]SchemaEnumValue Type *StringOrStringArray AllOf *[]*Schema AnyOf *[]*Schema OneOf *[]*Schema Not *Schema Definitions *[]*NamedSchema // 6. Metadata keywords Title *string Description *string Default *yaml.Node // 7. Semantic validation with "format" Format *string } // These helper structs represent "combination" types that generally can // have values of one type or another. All are used to represent parts // of Schemas. // SchemaNumber represents a value that can be either an Integer or a Float. type SchemaNumber struct { Integer *int64 Float *float64 } // NewSchemaNumberWithInteger creates and returns a new object func NewSchemaNumberWithInteger(i int64) *SchemaNumber { result := &SchemaNumber{} result.Integer = &i return result } // NewSchemaNumberWithFloat creates and returns a new object func NewSchemaNumberWithFloat(f float64) *SchemaNumber { result := &SchemaNumber{} result.Float = &f return result } // SchemaOrBoolean represents a value that can be either a Schema or a Boolean. type SchemaOrBoolean struct { Schema *Schema Boolean *bool } // NewSchemaOrBooleanWithSchema creates and returns a new object func NewSchemaOrBooleanWithSchema(s *Schema) *SchemaOrBoolean { result := &SchemaOrBoolean{} result.Schema = s return result } // NewSchemaOrBooleanWithBoolean creates and returns a new object func NewSchemaOrBooleanWithBoolean(b bool) *SchemaOrBoolean { result := &SchemaOrBoolean{} result.Boolean = &b return result } // StringOrStringArray represents a value that can be either // a String or an Array of Strings. type StringOrStringArray struct { String *string StringArray *[]string } // NewStringOrStringArrayWithString creates and returns a new object func NewStringOrStringArrayWithString(s string) *StringOrStringArray { result := &StringOrStringArray{} result.String = &s return result } // NewStringOrStringArrayWithStringArray creates and returns a new object func NewStringOrStringArrayWithStringArray(a []string) *StringOrStringArray { result := &StringOrStringArray{} result.StringArray = &a return result } // SchemaOrStringArray represents a value that can be either // a Schema or an Array of Strings. type SchemaOrStringArray struct { Schema *Schema StringArray *[]string } // SchemaOrSchemaArray represents a value that can be either // a Schema or an Array of Schemas. type SchemaOrSchemaArray struct { Schema *Schema SchemaArray *[]*Schema } // NewSchemaOrSchemaArrayWithSchema creates and returns a new object func NewSchemaOrSchemaArrayWithSchema(s *Schema) *SchemaOrSchemaArray { result := &SchemaOrSchemaArray{} result.Schema = s return result } // NewSchemaOrSchemaArrayWithSchemaArray creates and returns a new object func NewSchemaOrSchemaArrayWithSchemaArray(a []*Schema) *SchemaOrSchemaArray { result := &SchemaOrSchemaArray{} result.SchemaArray = &a return result } // SchemaEnumValue represents a value that can be part of an // enumeration in a Schema. type SchemaEnumValue struct { String *string Bool *bool } // NamedSchema is a name-value pair that is used to emulate maps // with ordered keys. type NamedSchema struct { Name string Value *Schema } // NewNamedSchema creates and returns a new object func NewNamedSchema(name string, value *Schema) *NamedSchema { return &NamedSchema{Name: name, Value: value} } // NamedSchemaOrStringArray is a name-value pair that is used // to emulate maps with ordered keys. type NamedSchemaOrStringArray struct { Name string Value *SchemaOrStringArray } // Access named subschemas by name func namedSchemaArrayElementWithName(array *[]*NamedSchema, name string) *Schema { if array == nil { return nil } for _, pair := range *array { if pair.Name == name { return pair.Value } } return nil } // PropertyWithName returns the selected element. func (s *Schema) PropertyWithName(name string) *Schema { return namedSchemaArrayElementWithName(s.Properties, name) } // PatternPropertyWithName returns the selected element. func (s *Schema) PatternPropertyWithName(name string) *Schema { return namedSchemaArrayElementWithName(s.PatternProperties, name) } // DefinitionWithName returns the selected element. func (s *Schema) DefinitionWithName(name string) *Schema { return namedSchemaArrayElementWithName(s.Definitions, name) } // AddProperty adds a named property. func (s *Schema) AddProperty(name string, property *Schema) { *s.Properties = append(*s.Properties, NewNamedSchema(name, property)) } ================================================ FILE: vendor/github.com/google/gnostic/jsonschema/operations.go ================================================ // Copyright 2017 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package jsonschema import ( "fmt" "log" "strings" ) // // OPERATIONS // The following methods perform operations on Schemas. // // IsEmpty returns true if no members of the Schema are specified. func (schema *Schema) IsEmpty() bool { return (schema.Schema == nil) && (schema.ID == nil) && (schema.MultipleOf == nil) && (schema.Maximum == nil) && (schema.ExclusiveMaximum == nil) && (schema.Minimum == nil) && (schema.ExclusiveMinimum == nil) && (schema.MaxLength == nil) && (schema.MinLength == nil) && (schema.Pattern == nil) && (schema.AdditionalItems == nil) && (schema.Items == nil) && (schema.MaxItems == nil) && (schema.MinItems == nil) && (schema.UniqueItems == nil) && (schema.MaxProperties == nil) && (schema.MinProperties == nil) && (schema.Required == nil) && (schema.AdditionalProperties == nil) && (schema.Properties == nil) && (schema.PatternProperties == nil) && (schema.Dependencies == nil) && (schema.Enumeration == nil) && (schema.Type == nil) && (schema.AllOf == nil) && (schema.AnyOf == nil) && (schema.OneOf == nil) && (schema.Not == nil) && (schema.Definitions == nil) && (schema.Title == nil) && (schema.Description == nil) && (schema.Default == nil) && (schema.Format == nil) && (schema.Ref == nil) } // IsEqual returns true if two schemas are equal. func (schema *Schema) IsEqual(schema2 *Schema) bool { return schema.String() == schema2.String() } // SchemaOperation represents a function that can be applied to a Schema. type SchemaOperation func(schema *Schema, context string) // Applies a specified function to a Schema and all of the Schemas that it contains. func (schema *Schema) applyToSchemas(operation SchemaOperation, context string) { if schema.AdditionalItems != nil { s := schema.AdditionalItems.Schema if s != nil { s.applyToSchemas(operation, "AdditionalItems") } } if schema.Items != nil { if schema.Items.SchemaArray != nil { for _, s := range *(schema.Items.SchemaArray) { s.applyToSchemas(operation, "Items.SchemaArray") } } else if schema.Items.Schema != nil { schema.Items.Schema.applyToSchemas(operation, "Items.Schema") } } if schema.AdditionalProperties != nil { s := schema.AdditionalProperties.Schema if s != nil { s.applyToSchemas(operation, "AdditionalProperties") } } if schema.Properties != nil { for _, pair := range *(schema.Properties) { s := pair.Value s.applyToSchemas(operation, "Properties") } } if schema.PatternProperties != nil { for _, pair := range *(schema.PatternProperties) { s := pair.Value s.applyToSchemas(operation, "PatternProperties") } } if schema.Dependencies != nil { for _, pair := range *(schema.Dependencies) { schemaOrStringArray := pair.Value s := schemaOrStringArray.Schema if s != nil { s.applyToSchemas(operation, "Dependencies") } } } if schema.AllOf != nil { for _, s := range *(schema.AllOf) { s.applyToSchemas(operation, "AllOf") } } if schema.AnyOf != nil { for _, s := range *(schema.AnyOf) { s.applyToSchemas(operation, "AnyOf") } } if schema.OneOf != nil { for _, s := range *(schema.OneOf) { s.applyToSchemas(operation, "OneOf") } } if schema.Not != nil { schema.Not.applyToSchemas(operation, "Not") } if schema.Definitions != nil { for _, pair := range *(schema.Definitions) { s := pair.Value s.applyToSchemas(operation, "Definitions") } } operation(schema, context) } // CopyProperties copies all non-nil properties from the source Schema to the schema Schema. func (schema *Schema) CopyProperties(source *Schema) { if source.Schema != nil { schema.Schema = source.Schema } if source.ID != nil { schema.ID = source.ID } if source.MultipleOf != nil { schema.MultipleOf = source.MultipleOf } if source.Maximum != nil { schema.Maximum = source.Maximum } if source.ExclusiveMaximum != nil { schema.ExclusiveMaximum = source.ExclusiveMaximum } if source.Minimum != nil { schema.Minimum = source.Minimum } if source.ExclusiveMinimum != nil { schema.ExclusiveMinimum = source.ExclusiveMinimum } if source.MaxLength != nil { schema.MaxLength = source.MaxLength } if source.MinLength != nil { schema.MinLength = source.MinLength } if source.Pattern != nil { schema.Pattern = source.Pattern } if source.AdditionalItems != nil { schema.AdditionalItems = source.AdditionalItems } if source.Items != nil { schema.Items = source.Items } if source.MaxItems != nil { schema.MaxItems = source.MaxItems } if source.MinItems != nil { schema.MinItems = source.MinItems } if source.UniqueItems != nil { schema.UniqueItems = source.UniqueItems } if source.MaxProperties != nil { schema.MaxProperties = source.MaxProperties } if source.MinProperties != nil { schema.MinProperties = source.MinProperties } if source.Required != nil { schema.Required = source.Required } if source.AdditionalProperties != nil { schema.AdditionalProperties = source.AdditionalProperties } if source.Properties != nil { schema.Properties = source.Properties } if source.PatternProperties != nil { schema.PatternProperties = source.PatternProperties } if source.Dependencies != nil { schema.Dependencies = source.Dependencies } if source.Enumeration != nil { schema.Enumeration = source.Enumeration } if source.Type != nil { schema.Type = source.Type } if source.AllOf != nil { schema.AllOf = source.AllOf } if source.AnyOf != nil { schema.AnyOf = source.AnyOf } if source.OneOf != nil { schema.OneOf = source.OneOf } if source.Not != nil { schema.Not = source.Not } if source.Definitions != nil { schema.Definitions = source.Definitions } if source.Title != nil { schema.Title = source.Title } if source.Description != nil { schema.Description = source.Description } if source.Default != nil { schema.Default = source.Default } if source.Format != nil { schema.Format = source.Format } if source.Ref != nil { schema.Ref = source.Ref } } // TypeIs returns true if the Type of a Schema includes the specified type func (schema *Schema) TypeIs(typeName string) bool { if schema.Type != nil { // the schema Type is either a string or an array of strings if schema.Type.String != nil { return (*(schema.Type.String) == typeName) } else if schema.Type.StringArray != nil { for _, n := range *(schema.Type.StringArray) { if n == typeName { return true } } } } return false } // ResolveRefs resolves "$ref" elements in a Schema and its children. // But if a reference refers to an object type, is inside a oneOf, or contains a oneOf, // the reference is kept and we expect downstream tools to separately model these // referenced schemas. func (schema *Schema) ResolveRefs() { rootSchema := schema count := 1 for count > 0 { count = 0 schema.applyToSchemas( func(schema *Schema, context string) { if schema.Ref != nil { resolvedRef, err := rootSchema.resolveJSONPointer(*(schema.Ref)) if err != nil { log.Printf("%+v", err) } else if resolvedRef.TypeIs("object") { // don't substitute for objects, we'll model the referenced schema with a class } else if context == "OneOf" { // don't substitute for references inside oneOf declarations } else if resolvedRef.OneOf != nil { // don't substitute for references that contain oneOf declarations } else if resolvedRef.AdditionalProperties != nil { // don't substitute for references that look like objects } else { schema.Ref = nil schema.CopyProperties(resolvedRef) count++ } } }, "") } } // resolveJSONPointer resolves JSON pointers. // This current implementation is very crude and custom for OpenAPI 2.0 schemas. // It panics for any pointer that it is unable to resolve. func (schema *Schema) resolveJSONPointer(ref string) (result *Schema, err error) { parts := strings.Split(ref, "#") if len(parts) == 2 { documentName := parts[0] + "#" if documentName == "#" && schema.ID != nil { documentName = *(schema.ID) } path := parts[1] document := schemas[documentName] pathParts := strings.Split(path, "/") // we currently do a very limited (hard-coded) resolution of certain paths and log errors for missed cases if len(pathParts) == 1 { return document, nil } else if len(pathParts) == 3 { switch pathParts[1] { case "definitions": dictionary := document.Definitions for _, pair := range *dictionary { if pair.Name == pathParts[2] { result = pair.Value } } case "properties": dictionary := document.Properties for _, pair := range *dictionary { if pair.Name == pathParts[2] { result = pair.Value } } default: break } } } if result == nil { return nil, fmt.Errorf("unresolved pointer: %+v", ref) } return result, nil } // ResolveAllOfs replaces "allOf" elements by merging their properties into the parent Schema. func (schema *Schema) ResolveAllOfs() { schema.applyToSchemas( func(schema *Schema, context string) { if schema.AllOf != nil { for _, allOf := range *(schema.AllOf) { schema.CopyProperties(allOf) } schema.AllOf = nil } }, "resolveAllOfs") } // ResolveAnyOfs replaces all "anyOf" elements with "oneOf". func (schema *Schema) ResolveAnyOfs() { schema.applyToSchemas( func(schema *Schema, context string) { if schema.AnyOf != nil { schema.OneOf = schema.AnyOf schema.AnyOf = nil } }, "resolveAnyOfs") } // return a pointer to a copy of a passed-in string func stringptr(input string) (output *string) { return &input } // CopyOfficialSchemaProperty copies a named property from the official JSON Schema definition func (schema *Schema) CopyOfficialSchemaProperty(name string) { *schema.Properties = append(*schema.Properties, NewNamedSchema(name, &Schema{Ref: stringptr("http://json-schema.org/draft-04/schema#/properties/" + name)})) } // CopyOfficialSchemaProperties copies named properties from the official JSON Schema definition func (schema *Schema) CopyOfficialSchemaProperties(names []string) { for _, name := range names { schema.CopyOfficialSchemaProperty(name) } } ================================================ FILE: vendor/github.com/google/gnostic/jsonschema/reader.go ================================================ // Copyright 2017 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:generate go run generate-base.go package jsonschema import ( "fmt" "io/ioutil" "strconv" "gopkg.in/yaml.v3" ) // This is a global map of all known Schemas. // It is initialized when the first Schema is created and inserted. var schemas map[string]*Schema // NewBaseSchema builds a schema object from an embedded json representation. func NewBaseSchema() (schema *Schema, err error) { b, err := baseSchemaBytes() if err != nil { return nil, err } var node yaml.Node err = yaml.Unmarshal(b, &node) if err != nil { return nil, err } return NewSchemaFromObject(&node), nil } // NewSchemaFromFile reads a schema from a file. // Currently this assumes that schemas are stored in the source distribution of this project. func NewSchemaFromFile(filename string) (schema *Schema, err error) { file, err := ioutil.ReadFile(filename) if err != nil { return nil, err } var node yaml.Node err = yaml.Unmarshal(file, &node) if err != nil { return nil, err } return NewSchemaFromObject(&node), nil } // NewSchemaFromObject constructs a schema from a parsed JSON object. // Due to the complexity of the schema representation, this is a // custom reader and not the standard Go JSON reader (encoding/json). func NewSchemaFromObject(jsonData *yaml.Node) *Schema { switch jsonData.Kind { case yaml.DocumentNode: return NewSchemaFromObject(jsonData.Content[0]) case yaml.MappingNode: schema := &Schema{} for i := 0; i < len(jsonData.Content); i += 2 { k := jsonData.Content[i].Value v := jsonData.Content[i+1] switch k { case "$schema": schema.Schema = schema.stringValue(v) case "id": schema.ID = schema.stringValue(v) case "multipleOf": schema.MultipleOf = schema.numberValue(v) case "maximum": schema.Maximum = schema.numberValue(v) case "exclusiveMaximum": schema.ExclusiveMaximum = schema.boolValue(v) case "minimum": schema.Minimum = schema.numberValue(v) case "exclusiveMinimum": schema.ExclusiveMinimum = schema.boolValue(v) case "maxLength": schema.MaxLength = schema.intValue(v) case "minLength": schema.MinLength = schema.intValue(v) case "pattern": schema.Pattern = schema.stringValue(v) case "additionalItems": schema.AdditionalItems = schema.schemaOrBooleanValue(v) case "items": schema.Items = schema.schemaOrSchemaArrayValue(v) case "maxItems": schema.MaxItems = schema.intValue(v) case "minItems": schema.MinItems = schema.intValue(v) case "uniqueItems": schema.UniqueItems = schema.boolValue(v) case "maxProperties": schema.MaxProperties = schema.intValue(v) case "minProperties": schema.MinProperties = schema.intValue(v) case "required": schema.Required = schema.arrayOfStringsValue(v) case "additionalProperties": schema.AdditionalProperties = schema.schemaOrBooleanValue(v) case "properties": schema.Properties = schema.mapOfSchemasValue(v) case "patternProperties": schema.PatternProperties = schema.mapOfSchemasValue(v) case "dependencies": schema.Dependencies = schema.mapOfSchemasOrStringArraysValue(v) case "enum": schema.Enumeration = schema.arrayOfEnumValuesValue(v) case "type": schema.Type = schema.stringOrStringArrayValue(v) case "allOf": schema.AllOf = schema.arrayOfSchemasValue(v) case "anyOf": schema.AnyOf = schema.arrayOfSchemasValue(v) case "oneOf": schema.OneOf = schema.arrayOfSchemasValue(v) case "not": schema.Not = NewSchemaFromObject(v) case "definitions": schema.Definitions = schema.mapOfSchemasValue(v) case "title": schema.Title = schema.stringValue(v) case "description": schema.Description = schema.stringValue(v) case "default": schema.Default = v case "format": schema.Format = schema.stringValue(v) case "$ref": schema.Ref = schema.stringValue(v) default: fmt.Printf("UNSUPPORTED (%s)\n", k) } } // insert schema in global map if schema.ID != nil { if schemas == nil { schemas = make(map[string]*Schema, 0) } schemas[*(schema.ID)] = schema } return schema default: fmt.Printf("schemaValue: unexpected node %+v\n", jsonData) return nil } return nil } // // BUILDERS // The following methods build elements of Schemas from interface{} values. // Each returns nil if it is unable to build the desired element. // // Gets the string value of an interface{} value if possible. func (schema *Schema) stringValue(v *yaml.Node) *string { switch v.Kind { case yaml.ScalarNode: return &v.Value default: fmt.Printf("stringValue: unexpected node %+v\n", v) } return nil } // Gets the numeric value of an interface{} value if possible. func (schema *Schema) numberValue(v *yaml.Node) *SchemaNumber { number := &SchemaNumber{} switch v.Kind { case yaml.ScalarNode: switch v.Tag { case "!!float": v2, _ := strconv.ParseFloat(v.Value, 64) number.Float = &v2 return number case "!!int": v2, _ := strconv.ParseInt(v.Value, 10, 64) number.Integer = &v2 return number default: fmt.Printf("stringValue: unexpected node %+v\n", v) } default: fmt.Printf("stringValue: unexpected node %+v\n", v) } return nil } // Gets the integer value of an interface{} value if possible. func (schema *Schema) intValue(v *yaml.Node) *int64 { switch v.Kind { case yaml.ScalarNode: switch v.Tag { case "!!float": v2, _ := strconv.ParseFloat(v.Value, 64) v3 := int64(v2) return &v3 case "!!int": v2, _ := strconv.ParseInt(v.Value, 10, 64) return &v2 default: fmt.Printf("intValue: unexpected node %+v\n", v) } default: fmt.Printf("intValue: unexpected node %+v\n", v) } return nil } // Gets the bool value of an interface{} value if possible. func (schema *Schema) boolValue(v *yaml.Node) *bool { switch v.Kind { case yaml.ScalarNode: switch v.Tag { case "!!bool": v2, _ := strconv.ParseBool(v.Value) return &v2 default: fmt.Printf("boolValue: unexpected node %+v\n", v) } default: fmt.Printf("boolValue: unexpected node %+v\n", v) } return nil } // Gets a map of Schemas from an interface{} value if possible. func (schema *Schema) mapOfSchemasValue(v *yaml.Node) *[]*NamedSchema { switch v.Kind { case yaml.MappingNode: m := make([]*NamedSchema, 0) for i := 0; i < len(v.Content); i += 2 { k2 := v.Content[i].Value v2 := v.Content[i+1] pair := &NamedSchema{Name: k2, Value: NewSchemaFromObject(v2)} m = append(m, pair) } return &m default: fmt.Printf("mapOfSchemasValue: unexpected node %+v\n", v) } return nil } // Gets an array of Schemas from an interface{} value if possible. func (schema *Schema) arrayOfSchemasValue(v *yaml.Node) *[]*Schema { switch v.Kind { case yaml.SequenceNode: m := make([]*Schema, 0) for _, v2 := range v.Content { switch v2.Kind { case yaml.MappingNode: s := NewSchemaFromObject(v2) m = append(m, s) default: fmt.Printf("arrayOfSchemasValue: unexpected node %+v\n", v2) } } return &m case yaml.MappingNode: m := make([]*Schema, 0) s := NewSchemaFromObject(v) m = append(m, s) return &m default: fmt.Printf("arrayOfSchemasValue: unexpected node %+v\n", v) } return nil } // Gets a Schema or an array of Schemas from an interface{} value if possible. func (schema *Schema) schemaOrSchemaArrayValue(v *yaml.Node) *SchemaOrSchemaArray { switch v.Kind { case yaml.SequenceNode: m := make([]*Schema, 0) for _, v2 := range v.Content { switch v2.Kind { case yaml.MappingNode: s := NewSchemaFromObject(v2) m = append(m, s) default: fmt.Printf("schemaOrSchemaArrayValue: unexpected node %+v\n", v2) } } return &SchemaOrSchemaArray{SchemaArray: &m} case yaml.MappingNode: s := NewSchemaFromObject(v) return &SchemaOrSchemaArray{Schema: s} default: fmt.Printf("schemaOrSchemaArrayValue: unexpected node %+v\n", v) } return nil } // Gets an array of strings from an interface{} value if possible. func (schema *Schema) arrayOfStringsValue(v *yaml.Node) *[]string { switch v.Kind { case yaml.ScalarNode: a := []string{v.Value} return &a case yaml.SequenceNode: a := make([]string, 0) for _, v2 := range v.Content { switch v2.Kind { case yaml.ScalarNode: a = append(a, v2.Value) default: fmt.Printf("arrayOfStringsValue: unexpected node %+v\n", v2) } } return &a default: fmt.Printf("arrayOfStringsValue: unexpected node %+v\n", v) } return nil } // Gets a string or an array of strings from an interface{} value if possible. func (schema *Schema) stringOrStringArrayValue(v *yaml.Node) *StringOrStringArray { switch v.Kind { case yaml.ScalarNode: s := &StringOrStringArray{} s.String = &v.Value return s case yaml.SequenceNode: a := make([]string, 0) for _, v2 := range v.Content { switch v2.Kind { case yaml.ScalarNode: a = append(a, v2.Value) default: fmt.Printf("arrayOfStringsValue: unexpected node %+v\n", v2) } } s := &StringOrStringArray{} s.StringArray = &a return s default: fmt.Printf("arrayOfStringsValue: unexpected node %+v\n", v) } return nil } // Gets an array of enum values from an interface{} value if possible. func (schema *Schema) arrayOfEnumValuesValue(v *yaml.Node) *[]SchemaEnumValue { a := make([]SchemaEnumValue, 0) switch v.Kind { case yaml.SequenceNode: for _, v2 := range v.Content { switch v2.Kind { case yaml.ScalarNode: switch v2.Tag { case "!!str": a = append(a, SchemaEnumValue{String: &v2.Value}) case "!!bool": v3, _ := strconv.ParseBool(v2.Value) a = append(a, SchemaEnumValue{Bool: &v3}) default: fmt.Printf("arrayOfEnumValuesValue: unexpected type %s\n", v2.Tag) } default: fmt.Printf("arrayOfEnumValuesValue: unexpected node %+v\n", v2) } } default: fmt.Printf("arrayOfEnumValuesValue: unexpected node %+v\n", v) } return &a } // Gets a map of schemas or string arrays from an interface{} value if possible. func (schema *Schema) mapOfSchemasOrStringArraysValue(v *yaml.Node) *[]*NamedSchemaOrStringArray { m := make([]*NamedSchemaOrStringArray, 0) switch v.Kind { case yaml.MappingNode: for i := 0; i < len(v.Content); i += 2 { k2 := v.Content[i].Value v2 := v.Content[i+1] switch v2.Kind { case yaml.SequenceNode: a := make([]string, 0) for _, v3 := range v2.Content { switch v3.Kind { case yaml.ScalarNode: a = append(a, v3.Value) default: fmt.Printf("mapOfSchemasOrStringArraysValue: unexpected node %+v\n", v3) } } s := &SchemaOrStringArray{} s.StringArray = &a pair := &NamedSchemaOrStringArray{Name: k2, Value: s} m = append(m, pair) default: fmt.Printf("mapOfSchemasOrStringArraysValue: unexpected node %+v\n", v2) } } default: fmt.Printf("mapOfSchemasOrStringArraysValue: unexpected node %+v\n", v) } return &m } // Gets a schema or a boolean value from an interface{} value if possible. func (schema *Schema) schemaOrBooleanValue(v *yaml.Node) *SchemaOrBoolean { schemaOrBoolean := &SchemaOrBoolean{} switch v.Kind { case yaml.ScalarNode: v2, _ := strconv.ParseBool(v.Value) schemaOrBoolean.Boolean = &v2 case yaml.MappingNode: schemaOrBoolean.Schema = NewSchemaFromObject(v) default: fmt.Printf("schemaOrBooleanValue: unexpected node %+v\n", v) } return schemaOrBoolean } ================================================ FILE: vendor/github.com/google/gnostic/jsonschema/schema.json ================================================ { "id": "http://json-schema.org/draft-04/schema#", "$schema": "http://json-schema.org/draft-04/schema#", "description": "Core schema meta-schema", "definitions": { "schemaArray": { "type": "array", "minItems": 1, "items": { "$ref": "#" } }, "positiveInteger": { "type": "integer", "minimum": 0 }, "positiveIntegerDefault0": { "allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ] }, "simpleTypes": { "enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ] }, "stringArray": { "type": "array", "items": { "type": "string" }, "minItems": 1, "uniqueItems": true } }, "type": "object", "properties": { "id": { "type": "string", "format": "uri" }, "$schema": { "type": "string", "format": "uri" }, "title": { "type": "string" }, "description": { "type": "string" }, "default": {}, "multipleOf": { "type": "number", "minimum": 0, "exclusiveMinimum": true }, "maximum": { "type": "number" }, "exclusiveMaximum": { "type": "boolean", "default": false }, "minimum": { "type": "number" }, "exclusiveMinimum": { "type": "boolean", "default": false }, "maxLength": { "$ref": "#/definitions/positiveInteger" }, "minLength": { "$ref": "#/definitions/positiveIntegerDefault0" }, "pattern": { "type": "string", "format": "regex" }, "additionalItems": { "anyOf": [ { "type": "boolean" }, { "$ref": "#" } ], "default": {} }, "items": { "anyOf": [ { "$ref": "#" }, { "$ref": "#/definitions/schemaArray" } ], "default": {} }, "maxItems": { "$ref": "#/definitions/positiveInteger" }, "minItems": { "$ref": "#/definitions/positiveIntegerDefault0" }, "uniqueItems": { "type": "boolean", "default": false }, "maxProperties": { "$ref": "#/definitions/positiveInteger" }, "minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" }, "required": { "$ref": "#/definitions/stringArray" }, "additionalProperties": { "anyOf": [ { "type": "boolean" }, { "$ref": "#" } ], "default": {} }, "definitions": { "type": "object", "additionalProperties": { "$ref": "#" }, "default": {} }, "properties": { "type": "object", "additionalProperties": { "$ref": "#" }, "default": {} }, "patternProperties": { "type": "object", "additionalProperties": { "$ref": "#" }, "default": {} }, "dependencies": { "type": "object", "additionalProperties": { "anyOf": [ { "$ref": "#" }, { "$ref": "#/definitions/stringArray" } ] } }, "enum": { "type": "array", "minItems": 1, "uniqueItems": true }, "type": { "anyOf": [ { "$ref": "#/definitions/simpleTypes" }, { "type": "array", "items": { "$ref": "#/definitions/simpleTypes" }, "minItems": 1, "uniqueItems": true } ] }, "allOf": { "$ref": "#/definitions/schemaArray" }, "anyOf": { "$ref": "#/definitions/schemaArray" }, "oneOf": { "$ref": "#/definitions/schemaArray" }, "not": { "$ref": "#" } }, "dependencies": { "exclusiveMaximum": [ "maximum" ], "exclusiveMinimum": [ "minimum" ] }, "default": {} } ================================================ FILE: vendor/github.com/google/gnostic/jsonschema/writer.go ================================================ // Copyright 2017 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package jsonschema import ( "fmt" "gopkg.in/yaml.v3" ) const indentation = " " func renderMappingNode(node *yaml.Node, indent string) (result string) { result = "{\n" innerIndent := indent + indentation for i := 0; i < len(node.Content); i += 2 { // first print the key key := node.Content[i].Value result += fmt.Sprintf("%s\"%+v\": ", innerIndent, key) // then the value value := node.Content[i+1] switch value.Kind { case yaml.ScalarNode: result += "\"" + value.Value + "\"" case yaml.MappingNode: result += renderMappingNode(value, innerIndent) case yaml.SequenceNode: result += renderSequenceNode(value, innerIndent) default: result += fmt.Sprintf("???MapItem(Key:%+v, Value:%T)", value, value) } if i < len(node.Content)-2 { result += "," } result += "\n" } result += indent + "}" return result } func renderSequenceNode(node *yaml.Node, indent string) (result string) { result = "[\n" innerIndent := indent + indentation for i := 0; i < len(node.Content); i++ { item := node.Content[i] switch item.Kind { case yaml.ScalarNode: result += innerIndent + "\"" + item.Value + "\"" case yaml.MappingNode: result += innerIndent + renderMappingNode(item, innerIndent) + "" default: result += innerIndent + fmt.Sprintf("???ArrayItem(%+v)", item) } if i < len(node.Content)-1 { result += "," } result += "\n" } result += indent + "]" return result } func renderStringArray(array []string, indent string) (result string) { result = "[\n" innerIndent := indent + indentation for i, item := range array { result += innerIndent + "\"" + item + "\"" if i < len(array)-1 { result += "," } result += "\n" } result += indent + "]" return result } // Render renders a yaml.Node as JSON func Render(node *yaml.Node) string { if node.Kind == yaml.DocumentNode { if len(node.Content) == 1 { return Render(node.Content[0]) } } else if node.Kind == yaml.MappingNode { return renderMappingNode(node, "") + "\n" } else if node.Kind == yaml.SequenceNode { return renderSequenceNode(node, "") + "\n" } return "" } func (object *SchemaNumber) nodeValue() *yaml.Node { if object.Integer != nil { return nodeForInt64(*object.Integer) } else if object.Float != nil { return nodeForFloat64(*object.Float) } else { return nil } } func (object *SchemaOrBoolean) nodeValue() *yaml.Node { if object.Schema != nil { return object.Schema.nodeValue() } else if object.Boolean != nil { return nodeForBoolean(*object.Boolean) } else { return nil } } func nodeForStringArray(array []string) *yaml.Node { content := make([]*yaml.Node, 0) for _, item := range array { content = append(content, nodeForString(item)) } return nodeForSequence(content) } func nodeForSchemaArray(array []*Schema) *yaml.Node { content := make([]*yaml.Node, 0) for _, item := range array { content = append(content, item.nodeValue()) } return nodeForSequence(content) } func (object *StringOrStringArray) nodeValue() *yaml.Node { if object.String != nil { return nodeForString(*object.String) } else if object.StringArray != nil { return nodeForStringArray(*(object.StringArray)) } else { return nil } } func (object *SchemaOrStringArray) nodeValue() *yaml.Node { if object.Schema != nil { return object.Schema.nodeValue() } else if object.StringArray != nil { return nodeForStringArray(*(object.StringArray)) } else { return nil } } func (object *SchemaOrSchemaArray) nodeValue() *yaml.Node { if object.Schema != nil { return object.Schema.nodeValue() } else if object.SchemaArray != nil { return nodeForSchemaArray(*(object.SchemaArray)) } else { return nil } } func (object *SchemaEnumValue) nodeValue() *yaml.Node { if object.String != nil { return nodeForString(*object.String) } else if object.Bool != nil { return nodeForBoolean(*object.Bool) } else { return nil } } func nodeForNamedSchemaArray(array *[]*NamedSchema) *yaml.Node { content := make([]*yaml.Node, 0) for _, pair := range *(array) { content = appendPair(content, pair.Name, pair.Value.nodeValue()) } return nodeForMapping(content) } func nodeForNamedSchemaOrStringArray(array *[]*NamedSchemaOrStringArray) *yaml.Node { content := make([]*yaml.Node, 0) for _, pair := range *(array) { content = appendPair(content, pair.Name, pair.Value.nodeValue()) } return nodeForMapping(content) } func nodeForSchemaEnumArray(array *[]SchemaEnumValue) *yaml.Node { content := make([]*yaml.Node, 0) for _, item := range *array { content = append(content, item.nodeValue()) } return nodeForSequence(content) } func nodeForMapping(content []*yaml.Node) *yaml.Node { return &yaml.Node{ Kind: yaml.MappingNode, Content: content, } } func nodeForSequence(content []*yaml.Node) *yaml.Node { return &yaml.Node{ Kind: yaml.SequenceNode, Content: content, } } func nodeForString(value string) *yaml.Node { return &yaml.Node{ Kind: yaml.ScalarNode, Tag: "!!str", Value: value, } } func nodeForBoolean(value bool) *yaml.Node { return &yaml.Node{ Kind: yaml.ScalarNode, Tag: "!!bool", Value: fmt.Sprintf("%t", value), } } func nodeForInt64(value int64) *yaml.Node { return &yaml.Node{ Kind: yaml.ScalarNode, Tag: "!!int", Value: fmt.Sprintf("%d", value), } } func nodeForFloat64(value float64) *yaml.Node { return &yaml.Node{ Kind: yaml.ScalarNode, Tag: "!!float", Value: fmt.Sprintf("%f", value), } } func appendPair(nodes []*yaml.Node, name string, value *yaml.Node) []*yaml.Node { nodes = append(nodes, nodeForString(name)) nodes = append(nodes, value) return nodes } func (schema *Schema) nodeValue() *yaml.Node { n := &yaml.Node{Kind: yaml.MappingNode} content := make([]*yaml.Node, 0) if schema.Title != nil { content = appendPair(content, "title", nodeForString(*schema.Title)) } if schema.ID != nil { content = appendPair(content, "id", nodeForString(*schema.ID)) } if schema.Schema != nil { content = appendPair(content, "$schema", nodeForString(*schema.Schema)) } if schema.Type != nil { content = appendPair(content, "type", schema.Type.nodeValue()) } if schema.Items != nil { content = appendPair(content, "items", schema.Items.nodeValue()) } if schema.Description != nil { content = appendPair(content, "description", nodeForString(*schema.Description)) } if schema.Required != nil { content = appendPair(content, "required", nodeForStringArray(*schema.Required)) } if schema.AdditionalProperties != nil { content = appendPair(content, "additionalProperties", schema.AdditionalProperties.nodeValue()) } if schema.PatternProperties != nil { content = appendPair(content, "patternProperties", nodeForNamedSchemaArray(schema.PatternProperties)) } if schema.Properties != nil { content = appendPair(content, "properties", nodeForNamedSchemaArray(schema.Properties)) } if schema.Dependencies != nil { content = appendPair(content, "dependencies", nodeForNamedSchemaOrStringArray(schema.Dependencies)) } if schema.Ref != nil { content = appendPair(content, "$ref", nodeForString(*schema.Ref)) } if schema.MultipleOf != nil { content = appendPair(content, "multipleOf", schema.MultipleOf.nodeValue()) } if schema.Maximum != nil { content = appendPair(content, "maximum", schema.Maximum.nodeValue()) } if schema.ExclusiveMaximum != nil { content = appendPair(content, "exclusiveMaximum", nodeForBoolean(*schema.ExclusiveMaximum)) } if schema.Minimum != nil { content = appendPair(content, "minimum", schema.Minimum.nodeValue()) } if schema.ExclusiveMinimum != nil { content = appendPair(content, "exclusiveMinimum", nodeForBoolean(*schema.ExclusiveMinimum)) } if schema.MaxLength != nil { content = appendPair(content, "maxLength", nodeForInt64(*schema.MaxLength)) } if schema.MinLength != nil { content = appendPair(content, "minLength", nodeForInt64(*schema.MinLength)) } if schema.Pattern != nil { content = appendPair(content, "pattern", nodeForString(*schema.Pattern)) } if schema.AdditionalItems != nil { content = appendPair(content, "additionalItems", schema.AdditionalItems.nodeValue()) } if schema.MaxItems != nil { content = appendPair(content, "maxItems", nodeForInt64(*schema.MaxItems)) } if schema.MinItems != nil { content = appendPair(content, "minItems", nodeForInt64(*schema.MinItems)) } if schema.UniqueItems != nil { content = appendPair(content, "uniqueItems", nodeForBoolean(*schema.UniqueItems)) } if schema.MaxProperties != nil { content = appendPair(content, "maxProperties", nodeForInt64(*schema.MaxProperties)) } if schema.MinProperties != nil { content = appendPair(content, "minProperties", nodeForInt64(*schema.MinProperties)) } if schema.Enumeration != nil { content = appendPair(content, "enum", nodeForSchemaEnumArray(schema.Enumeration)) } if schema.AllOf != nil { content = appendPair(content, "allOf", nodeForSchemaArray(*schema.AllOf)) } if schema.AnyOf != nil { content = appendPair(content, "anyOf", nodeForSchemaArray(*schema.AnyOf)) } if schema.OneOf != nil { content = appendPair(content, "oneOf", nodeForSchemaArray(*schema.OneOf)) } if schema.Not != nil { content = appendPair(content, "not", schema.Not.nodeValue()) } if schema.Definitions != nil { content = appendPair(content, "definitions", nodeForNamedSchemaArray(schema.Definitions)) } if schema.Default != nil { // m = append(m, yaml.MapItem{Key: "default", Value: *schema.Default}) } if schema.Format != nil { content = appendPair(content, "format", nodeForString(*schema.Format)) } n.Content = content return n } // JSONString returns a json representation of a schema. func (schema *Schema) JSONString() string { node := schema.nodeValue() return Render(node) } ================================================ FILE: vendor/github.com/google/gnostic/openapiv2/OpenAPIv2.go ================================================ // Copyright 2020 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // THIS FILE IS AUTOMATICALLY GENERATED. package openapi_v2 import ( "fmt" "regexp" "strings" "gopkg.in/yaml.v3" "github.com/google/gnostic/compiler" ) // Version returns the package name (and OpenAPI version). func Version() string { return "openapi_v2" } // NewAdditionalPropertiesItem creates an object of type AdditionalPropertiesItem if possible, returning an error if not. func NewAdditionalPropertiesItem(in *yaml.Node, context *compiler.Context) (*AdditionalPropertiesItem, error) { errors := make([]error, 0) x := &AdditionalPropertiesItem{} matched := false // Schema schema = 1; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewSchema(m, compiler.NewContext("schema", m, context)) if matchingError == nil { x.Oneof = &AdditionalPropertiesItem_Schema{Schema: t} matched = true } else { errors = append(errors, matchingError) } } } // bool boolean = 2; boolValue, ok := compiler.BoolForScalarNode(in) if ok { x.Oneof = &AdditionalPropertiesItem_Boolean{Boolean: boolValue} matched = true } if matched { // since the oneof matched one of its possibilities, discard any matching errors errors = make([]error, 0) } else { message := fmt.Sprintf("contains an invalid AdditionalPropertiesItem") err := compiler.NewError(context, message) errors = []error{err} } return x, compiler.NewErrorGroupOrNil(errors) } // NewAny creates an object of type Any if possible, returning an error if not. func NewAny(in *yaml.Node, context *compiler.Context) (*Any, error) { errors := make([]error, 0) x := &Any{} bytes := compiler.Marshal(in) x.Yaml = string(bytes) return x, compiler.NewErrorGroupOrNil(errors) } // NewApiKeySecurity creates an object of type ApiKeySecurity if possible, returning an error if not. func NewApiKeySecurity(in *yaml.Node, context *compiler.Context) (*ApiKeySecurity, error) { errors := make([]error, 0) x := &ApiKeySecurity{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"in", "name", "type"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"description", "in", "name", "type"} allowedPatterns := []*regexp.Regexp{pattern0} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string type = 1; v1 := compiler.MapValueForKey(m, "type") if v1 != nil { x.Type, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [apiKey] if ok && !compiler.StringArrayContainsValue([]string{"apiKey"}, x.Type) { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string name = 2; v2 := compiler.MapValueForKey(m, "name") if v2 != nil { x.Name, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // string in = 3; v3 := compiler.MapValueForKey(m, "in") if v3 != nil { x.In, ok = compiler.StringForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for in: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [header query] if ok && !compiler.StringArrayContainsValue([]string{"header", "query"}, x.In) { message := fmt.Sprintf("has unexpected value for in: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 4; v4 := compiler.MapValueForKey(m, "description") if v4 != nil { x.Description, ok = compiler.StringForScalarNode(v4) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v4)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny vendor_extension = 5; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewBasicAuthenticationSecurity creates an object of type BasicAuthenticationSecurity if possible, returning an error if not. func NewBasicAuthenticationSecurity(in *yaml.Node, context *compiler.Context) (*BasicAuthenticationSecurity, error) { errors := make([]error, 0) x := &BasicAuthenticationSecurity{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"type"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"description", "type"} allowedPatterns := []*regexp.Regexp{pattern0} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string type = 1; v1 := compiler.MapValueForKey(m, "type") if v1 != nil { x.Type, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [basic] if ok && !compiler.StringArrayContainsValue([]string{"basic"}, x.Type) { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 2; v2 := compiler.MapValueForKey(m, "description") if v2 != nil { x.Description, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny vendor_extension = 3; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewBodyParameter creates an object of type BodyParameter if possible, returning an error if not. func NewBodyParameter(in *yaml.Node, context *compiler.Context) (*BodyParameter, error) { errors := make([]error, 0) x := &BodyParameter{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"in", "name", "schema"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"description", "in", "name", "required", "schema"} allowedPatterns := []*regexp.Regexp{pattern0} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string description = 1; v1 := compiler.MapValueForKey(m, "description") if v1 != nil { x.Description, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string name = 2; v2 := compiler.MapValueForKey(m, "name") if v2 != nil { x.Name, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // string in = 3; v3 := compiler.MapValueForKey(m, "in") if v3 != nil { x.In, ok = compiler.StringForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for in: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [body] if ok && !compiler.StringArrayContainsValue([]string{"body"}, x.In) { message := fmt.Sprintf("has unexpected value for in: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } // bool required = 4; v4 := compiler.MapValueForKey(m, "required") if v4 != nil { x.Required, ok = compiler.BoolForScalarNode(v4) if !ok { message := fmt.Sprintf("has unexpected value for required: %s", compiler.Display(v4)) errors = append(errors, compiler.NewError(context, message)) } } // Schema schema = 5; v5 := compiler.MapValueForKey(m, "schema") if v5 != nil { var err error x.Schema, err = NewSchema(v5, compiler.NewContext("schema", v5, context)) if err != nil { errors = append(errors, err) } } // repeated NamedAny vendor_extension = 6; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewContact creates an object of type Contact if possible, returning an error if not. func NewContact(in *yaml.Node, context *compiler.Context) (*Contact, error) { errors := make([]error, 0) x := &Contact{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"email", "name", "url"} allowedPatterns := []*regexp.Regexp{pattern0} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string url = 2; v2 := compiler.MapValueForKey(m, "url") if v2 != nil { x.Url, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for url: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // string email = 3; v3 := compiler.MapValueForKey(m, "email") if v3 != nil { x.Email, ok = compiler.StringForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for email: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny vendor_extension = 4; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewDefault creates an object of type Default if possible, returning an error if not. func NewDefault(in *yaml.Node, context *compiler.Context) (*Default, error) { errors := make([]error, 0) x := &Default{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedAny additional_properties = 1; // MAP: Any x.AdditionalProperties = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewDefinitions creates an object of type Definitions if possible, returning an error if not. func NewDefinitions(in *yaml.Node, context *compiler.Context) (*Definitions, error) { errors := make([]error, 0) x := &Definitions{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedSchema additional_properties = 1; // MAP: Schema x.AdditionalProperties = make([]*NamedSchema, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedSchema{} pair.Name = k var err error pair.Value, err = NewSchema(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewDocument creates an object of type Document if possible, returning an error if not. func NewDocument(in *yaml.Node, context *compiler.Context) (*Document, error) { errors := make([]error, 0) x := &Document{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"info", "paths", "swagger"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"basePath", "consumes", "definitions", "externalDocs", "host", "info", "parameters", "paths", "produces", "responses", "schemes", "security", "securityDefinitions", "swagger", "tags"} allowedPatterns := []*regexp.Regexp{pattern0} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string swagger = 1; v1 := compiler.MapValueForKey(m, "swagger") if v1 != nil { x.Swagger, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for swagger: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [2.0] if ok && !compiler.StringArrayContainsValue([]string{"2.0"}, x.Swagger) { message := fmt.Sprintf("has unexpected value for swagger: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // Info info = 2; v2 := compiler.MapValueForKey(m, "info") if v2 != nil { var err error x.Info, err = NewInfo(v2, compiler.NewContext("info", v2, context)) if err != nil { errors = append(errors, err) } } // string host = 3; v3 := compiler.MapValueForKey(m, "host") if v3 != nil { x.Host, ok = compiler.StringForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for host: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } // string base_path = 4; v4 := compiler.MapValueForKey(m, "basePath") if v4 != nil { x.BasePath, ok = compiler.StringForScalarNode(v4) if !ok { message := fmt.Sprintf("has unexpected value for basePath: %s", compiler.Display(v4)) errors = append(errors, compiler.NewError(context, message)) } } // repeated string schemes = 5; v5 := compiler.MapValueForKey(m, "schemes") if v5 != nil { v, ok := compiler.SequenceNodeForNode(v5) if ok { x.Schemes = compiler.StringArrayForSequenceNode(v) } else { message := fmt.Sprintf("has unexpected value for schemes: %s", compiler.Display(v5)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [http https ws wss] if ok && !compiler.StringArrayContainsValues([]string{"http", "https", "ws", "wss"}, x.Schemes) { message := fmt.Sprintf("has unexpected value for schemes: %s", compiler.Display(v5)) errors = append(errors, compiler.NewError(context, message)) } } // repeated string consumes = 6; v6 := compiler.MapValueForKey(m, "consumes") if v6 != nil { v, ok := compiler.SequenceNodeForNode(v6) if ok { x.Consumes = compiler.StringArrayForSequenceNode(v) } else { message := fmt.Sprintf("has unexpected value for consumes: %s", compiler.Display(v6)) errors = append(errors, compiler.NewError(context, message)) } } // repeated string produces = 7; v7 := compiler.MapValueForKey(m, "produces") if v7 != nil { v, ok := compiler.SequenceNodeForNode(v7) if ok { x.Produces = compiler.StringArrayForSequenceNode(v) } else { message := fmt.Sprintf("has unexpected value for produces: %s", compiler.Display(v7)) errors = append(errors, compiler.NewError(context, message)) } } // Paths paths = 8; v8 := compiler.MapValueForKey(m, "paths") if v8 != nil { var err error x.Paths, err = NewPaths(v8, compiler.NewContext("paths", v8, context)) if err != nil { errors = append(errors, err) } } // Definitions definitions = 9; v9 := compiler.MapValueForKey(m, "definitions") if v9 != nil { var err error x.Definitions, err = NewDefinitions(v9, compiler.NewContext("definitions", v9, context)) if err != nil { errors = append(errors, err) } } // ParameterDefinitions parameters = 10; v10 := compiler.MapValueForKey(m, "parameters") if v10 != nil { var err error x.Parameters, err = NewParameterDefinitions(v10, compiler.NewContext("parameters", v10, context)) if err != nil { errors = append(errors, err) } } // ResponseDefinitions responses = 11; v11 := compiler.MapValueForKey(m, "responses") if v11 != nil { var err error x.Responses, err = NewResponseDefinitions(v11, compiler.NewContext("responses", v11, context)) if err != nil { errors = append(errors, err) } } // repeated SecurityRequirement security = 12; v12 := compiler.MapValueForKey(m, "security") if v12 != nil { // repeated SecurityRequirement x.Security = make([]*SecurityRequirement, 0) a, ok := compiler.SequenceNodeForNode(v12) if ok { for _, item := range a.Content { y, err := NewSecurityRequirement(item, compiler.NewContext("security", item, context)) if err != nil { errors = append(errors, err) } x.Security = append(x.Security, y) } } } // SecurityDefinitions security_definitions = 13; v13 := compiler.MapValueForKey(m, "securityDefinitions") if v13 != nil { var err error x.SecurityDefinitions, err = NewSecurityDefinitions(v13, compiler.NewContext("securityDefinitions", v13, context)) if err != nil { errors = append(errors, err) } } // repeated Tag tags = 14; v14 := compiler.MapValueForKey(m, "tags") if v14 != nil { // repeated Tag x.Tags = make([]*Tag, 0) a, ok := compiler.SequenceNodeForNode(v14) if ok { for _, item := range a.Content { y, err := NewTag(item, compiler.NewContext("tags", item, context)) if err != nil { errors = append(errors, err) } x.Tags = append(x.Tags, y) } } } // ExternalDocs external_docs = 15; v15 := compiler.MapValueForKey(m, "externalDocs") if v15 != nil { var err error x.ExternalDocs, err = NewExternalDocs(v15, compiler.NewContext("externalDocs", v15, context)) if err != nil { errors = append(errors, err) } } // repeated NamedAny vendor_extension = 16; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewExamples creates an object of type Examples if possible, returning an error if not. func NewExamples(in *yaml.Node, context *compiler.Context) (*Examples, error) { errors := make([]error, 0) x := &Examples{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedAny additional_properties = 1; // MAP: Any x.AdditionalProperties = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewExternalDocs creates an object of type ExternalDocs if possible, returning an error if not. func NewExternalDocs(in *yaml.Node, context *compiler.Context) (*ExternalDocs, error) { errors := make([]error, 0) x := &ExternalDocs{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"url"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"description", "url"} allowedPatterns := []*regexp.Regexp{pattern0} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string description = 1; v1 := compiler.MapValueForKey(m, "description") if v1 != nil { x.Description, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string url = 2; v2 := compiler.MapValueForKey(m, "url") if v2 != nil { x.Url, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for url: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny vendor_extension = 3; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewFileSchema creates an object of type FileSchema if possible, returning an error if not. func NewFileSchema(in *yaml.Node, context *compiler.Context) (*FileSchema, error) { errors := make([]error, 0) x := &FileSchema{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"type"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"default", "description", "example", "externalDocs", "format", "readOnly", "required", "title", "type"} allowedPatterns := []*regexp.Regexp{pattern0} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string format = 1; v1 := compiler.MapValueForKey(m, "format") if v1 != nil { x.Format, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for format: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string title = 2; v2 := compiler.MapValueForKey(m, "title") if v2 != nil { x.Title, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for title: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 3; v3 := compiler.MapValueForKey(m, "description") if v3 != nil { x.Description, ok = compiler.StringForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } // Any default = 4; v4 := compiler.MapValueForKey(m, "default") if v4 != nil { var err error x.Default, err = NewAny(v4, compiler.NewContext("default", v4, context)) if err != nil { errors = append(errors, err) } } // repeated string required = 5; v5 := compiler.MapValueForKey(m, "required") if v5 != nil { v, ok := compiler.SequenceNodeForNode(v5) if ok { x.Required = compiler.StringArrayForSequenceNode(v) } else { message := fmt.Sprintf("has unexpected value for required: %s", compiler.Display(v5)) errors = append(errors, compiler.NewError(context, message)) } } // string type = 6; v6 := compiler.MapValueForKey(m, "type") if v6 != nil { x.Type, ok = compiler.StringForScalarNode(v6) if !ok { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v6)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [file] if ok && !compiler.StringArrayContainsValue([]string{"file"}, x.Type) { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v6)) errors = append(errors, compiler.NewError(context, message)) } } // bool read_only = 7; v7 := compiler.MapValueForKey(m, "readOnly") if v7 != nil { x.ReadOnly, ok = compiler.BoolForScalarNode(v7) if !ok { message := fmt.Sprintf("has unexpected value for readOnly: %s", compiler.Display(v7)) errors = append(errors, compiler.NewError(context, message)) } } // ExternalDocs external_docs = 8; v8 := compiler.MapValueForKey(m, "externalDocs") if v8 != nil { var err error x.ExternalDocs, err = NewExternalDocs(v8, compiler.NewContext("externalDocs", v8, context)) if err != nil { errors = append(errors, err) } } // Any example = 9; v9 := compiler.MapValueForKey(m, "example") if v9 != nil { var err error x.Example, err = NewAny(v9, compiler.NewContext("example", v9, context)) if err != nil { errors = append(errors, err) } } // repeated NamedAny vendor_extension = 10; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewFormDataParameterSubSchema creates an object of type FormDataParameterSubSchema if possible, returning an error if not. func NewFormDataParameterSubSchema(in *yaml.Node, context *compiler.Context) (*FormDataParameterSubSchema, error) { errors := make([]error, 0) x := &FormDataParameterSubSchema{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"allowEmptyValue", "collectionFormat", "default", "description", "enum", "exclusiveMaximum", "exclusiveMinimum", "format", "in", "items", "maxItems", "maxLength", "maximum", "minItems", "minLength", "minimum", "multipleOf", "name", "pattern", "required", "type", "uniqueItems"} allowedPatterns := []*regexp.Regexp{pattern0} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // bool required = 1; v1 := compiler.MapValueForKey(m, "required") if v1 != nil { x.Required, ok = compiler.BoolForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for required: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string in = 2; v2 := compiler.MapValueForKey(m, "in") if v2 != nil { x.In, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for in: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [formData] if ok && !compiler.StringArrayContainsValue([]string{"formData"}, x.In) { message := fmt.Sprintf("has unexpected value for in: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 3; v3 := compiler.MapValueForKey(m, "description") if v3 != nil { x.Description, ok = compiler.StringForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } // string name = 4; v4 := compiler.MapValueForKey(m, "name") if v4 != nil { x.Name, ok = compiler.StringForScalarNode(v4) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v4)) errors = append(errors, compiler.NewError(context, message)) } } // bool allow_empty_value = 5; v5 := compiler.MapValueForKey(m, "allowEmptyValue") if v5 != nil { x.AllowEmptyValue, ok = compiler.BoolForScalarNode(v5) if !ok { message := fmt.Sprintf("has unexpected value for allowEmptyValue: %s", compiler.Display(v5)) errors = append(errors, compiler.NewError(context, message)) } } // string type = 6; v6 := compiler.MapValueForKey(m, "type") if v6 != nil { x.Type, ok = compiler.StringForScalarNode(v6) if !ok { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v6)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [string number boolean integer array file] if ok && !compiler.StringArrayContainsValue([]string{"string", "number", "boolean", "integer", "array", "file"}, x.Type) { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v6)) errors = append(errors, compiler.NewError(context, message)) } } // string format = 7; v7 := compiler.MapValueForKey(m, "format") if v7 != nil { x.Format, ok = compiler.StringForScalarNode(v7) if !ok { message := fmt.Sprintf("has unexpected value for format: %s", compiler.Display(v7)) errors = append(errors, compiler.NewError(context, message)) } } // PrimitivesItems items = 8; v8 := compiler.MapValueForKey(m, "items") if v8 != nil { var err error x.Items, err = NewPrimitivesItems(v8, compiler.NewContext("items", v8, context)) if err != nil { errors = append(errors, err) } } // string collection_format = 9; v9 := compiler.MapValueForKey(m, "collectionFormat") if v9 != nil { x.CollectionFormat, ok = compiler.StringForScalarNode(v9) if !ok { message := fmt.Sprintf("has unexpected value for collectionFormat: %s", compiler.Display(v9)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [csv ssv tsv pipes multi] if ok && !compiler.StringArrayContainsValue([]string{"csv", "ssv", "tsv", "pipes", "multi"}, x.CollectionFormat) { message := fmt.Sprintf("has unexpected value for collectionFormat: %s", compiler.Display(v9)) errors = append(errors, compiler.NewError(context, message)) } } // Any default = 10; v10 := compiler.MapValueForKey(m, "default") if v10 != nil { var err error x.Default, err = NewAny(v10, compiler.NewContext("default", v10, context)) if err != nil { errors = append(errors, err) } } // float maximum = 11; v11 := compiler.MapValueForKey(m, "maximum") if v11 != nil { v, ok := compiler.FloatForScalarNode(v11) if ok { x.Maximum = v } else { message := fmt.Sprintf("has unexpected value for maximum: %s", compiler.Display(v11)) errors = append(errors, compiler.NewError(context, message)) } } // bool exclusive_maximum = 12; v12 := compiler.MapValueForKey(m, "exclusiveMaximum") if v12 != nil { x.ExclusiveMaximum, ok = compiler.BoolForScalarNode(v12) if !ok { message := fmt.Sprintf("has unexpected value for exclusiveMaximum: %s", compiler.Display(v12)) errors = append(errors, compiler.NewError(context, message)) } } // float minimum = 13; v13 := compiler.MapValueForKey(m, "minimum") if v13 != nil { v, ok := compiler.FloatForScalarNode(v13) if ok { x.Minimum = v } else { message := fmt.Sprintf("has unexpected value for minimum: %s", compiler.Display(v13)) errors = append(errors, compiler.NewError(context, message)) } } // bool exclusive_minimum = 14; v14 := compiler.MapValueForKey(m, "exclusiveMinimum") if v14 != nil { x.ExclusiveMinimum, ok = compiler.BoolForScalarNode(v14) if !ok { message := fmt.Sprintf("has unexpected value for exclusiveMinimum: %s", compiler.Display(v14)) errors = append(errors, compiler.NewError(context, message)) } } // int64 max_length = 15; v15 := compiler.MapValueForKey(m, "maxLength") if v15 != nil { t, ok := compiler.IntForScalarNode(v15) if ok { x.MaxLength = int64(t) } else { message := fmt.Sprintf("has unexpected value for maxLength: %s", compiler.Display(v15)) errors = append(errors, compiler.NewError(context, message)) } } // int64 min_length = 16; v16 := compiler.MapValueForKey(m, "minLength") if v16 != nil { t, ok := compiler.IntForScalarNode(v16) if ok { x.MinLength = int64(t) } else { message := fmt.Sprintf("has unexpected value for minLength: %s", compiler.Display(v16)) errors = append(errors, compiler.NewError(context, message)) } } // string pattern = 17; v17 := compiler.MapValueForKey(m, "pattern") if v17 != nil { x.Pattern, ok = compiler.StringForScalarNode(v17) if !ok { message := fmt.Sprintf("has unexpected value for pattern: %s", compiler.Display(v17)) errors = append(errors, compiler.NewError(context, message)) } } // int64 max_items = 18; v18 := compiler.MapValueForKey(m, "maxItems") if v18 != nil { t, ok := compiler.IntForScalarNode(v18) if ok { x.MaxItems = int64(t) } else { message := fmt.Sprintf("has unexpected value for maxItems: %s", compiler.Display(v18)) errors = append(errors, compiler.NewError(context, message)) } } // int64 min_items = 19; v19 := compiler.MapValueForKey(m, "minItems") if v19 != nil { t, ok := compiler.IntForScalarNode(v19) if ok { x.MinItems = int64(t) } else { message := fmt.Sprintf("has unexpected value for minItems: %s", compiler.Display(v19)) errors = append(errors, compiler.NewError(context, message)) } } // bool unique_items = 20; v20 := compiler.MapValueForKey(m, "uniqueItems") if v20 != nil { x.UniqueItems, ok = compiler.BoolForScalarNode(v20) if !ok { message := fmt.Sprintf("has unexpected value for uniqueItems: %s", compiler.Display(v20)) errors = append(errors, compiler.NewError(context, message)) } } // repeated Any enum = 21; v21 := compiler.MapValueForKey(m, "enum") if v21 != nil { // repeated Any x.Enum = make([]*Any, 0) a, ok := compiler.SequenceNodeForNode(v21) if ok { for _, item := range a.Content { y, err := NewAny(item, compiler.NewContext("enum", item, context)) if err != nil { errors = append(errors, err) } x.Enum = append(x.Enum, y) } } } // float multiple_of = 22; v22 := compiler.MapValueForKey(m, "multipleOf") if v22 != nil { v, ok := compiler.FloatForScalarNode(v22) if ok { x.MultipleOf = v } else { message := fmt.Sprintf("has unexpected value for multipleOf: %s", compiler.Display(v22)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny vendor_extension = 23; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewHeader creates an object of type Header if possible, returning an error if not. func NewHeader(in *yaml.Node, context *compiler.Context) (*Header, error) { errors := make([]error, 0) x := &Header{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"type"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"collectionFormat", "default", "description", "enum", "exclusiveMaximum", "exclusiveMinimum", "format", "items", "maxItems", "maxLength", "maximum", "minItems", "minLength", "minimum", "multipleOf", "pattern", "type", "uniqueItems"} allowedPatterns := []*regexp.Regexp{pattern0} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string type = 1; v1 := compiler.MapValueForKey(m, "type") if v1 != nil { x.Type, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [string number integer boolean array] if ok && !compiler.StringArrayContainsValue([]string{"string", "number", "integer", "boolean", "array"}, x.Type) { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string format = 2; v2 := compiler.MapValueForKey(m, "format") if v2 != nil { x.Format, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for format: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // PrimitivesItems items = 3; v3 := compiler.MapValueForKey(m, "items") if v3 != nil { var err error x.Items, err = NewPrimitivesItems(v3, compiler.NewContext("items", v3, context)) if err != nil { errors = append(errors, err) } } // string collection_format = 4; v4 := compiler.MapValueForKey(m, "collectionFormat") if v4 != nil { x.CollectionFormat, ok = compiler.StringForScalarNode(v4) if !ok { message := fmt.Sprintf("has unexpected value for collectionFormat: %s", compiler.Display(v4)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [csv ssv tsv pipes] if ok && !compiler.StringArrayContainsValue([]string{"csv", "ssv", "tsv", "pipes"}, x.CollectionFormat) { message := fmt.Sprintf("has unexpected value for collectionFormat: %s", compiler.Display(v4)) errors = append(errors, compiler.NewError(context, message)) } } // Any default = 5; v5 := compiler.MapValueForKey(m, "default") if v5 != nil { var err error x.Default, err = NewAny(v5, compiler.NewContext("default", v5, context)) if err != nil { errors = append(errors, err) } } // float maximum = 6; v6 := compiler.MapValueForKey(m, "maximum") if v6 != nil { v, ok := compiler.FloatForScalarNode(v6) if ok { x.Maximum = v } else { message := fmt.Sprintf("has unexpected value for maximum: %s", compiler.Display(v6)) errors = append(errors, compiler.NewError(context, message)) } } // bool exclusive_maximum = 7; v7 := compiler.MapValueForKey(m, "exclusiveMaximum") if v7 != nil { x.ExclusiveMaximum, ok = compiler.BoolForScalarNode(v7) if !ok { message := fmt.Sprintf("has unexpected value for exclusiveMaximum: %s", compiler.Display(v7)) errors = append(errors, compiler.NewError(context, message)) } } // float minimum = 8; v8 := compiler.MapValueForKey(m, "minimum") if v8 != nil { v, ok := compiler.FloatForScalarNode(v8) if ok { x.Minimum = v } else { message := fmt.Sprintf("has unexpected value for minimum: %s", compiler.Display(v8)) errors = append(errors, compiler.NewError(context, message)) } } // bool exclusive_minimum = 9; v9 := compiler.MapValueForKey(m, "exclusiveMinimum") if v9 != nil { x.ExclusiveMinimum, ok = compiler.BoolForScalarNode(v9) if !ok { message := fmt.Sprintf("has unexpected value for exclusiveMinimum: %s", compiler.Display(v9)) errors = append(errors, compiler.NewError(context, message)) } } // int64 max_length = 10; v10 := compiler.MapValueForKey(m, "maxLength") if v10 != nil { t, ok := compiler.IntForScalarNode(v10) if ok { x.MaxLength = int64(t) } else { message := fmt.Sprintf("has unexpected value for maxLength: %s", compiler.Display(v10)) errors = append(errors, compiler.NewError(context, message)) } } // int64 min_length = 11; v11 := compiler.MapValueForKey(m, "minLength") if v11 != nil { t, ok := compiler.IntForScalarNode(v11) if ok { x.MinLength = int64(t) } else { message := fmt.Sprintf("has unexpected value for minLength: %s", compiler.Display(v11)) errors = append(errors, compiler.NewError(context, message)) } } // string pattern = 12; v12 := compiler.MapValueForKey(m, "pattern") if v12 != nil { x.Pattern, ok = compiler.StringForScalarNode(v12) if !ok { message := fmt.Sprintf("has unexpected value for pattern: %s", compiler.Display(v12)) errors = append(errors, compiler.NewError(context, message)) } } // int64 max_items = 13; v13 := compiler.MapValueForKey(m, "maxItems") if v13 != nil { t, ok := compiler.IntForScalarNode(v13) if ok { x.MaxItems = int64(t) } else { message := fmt.Sprintf("has unexpected value for maxItems: %s", compiler.Display(v13)) errors = append(errors, compiler.NewError(context, message)) } } // int64 min_items = 14; v14 := compiler.MapValueForKey(m, "minItems") if v14 != nil { t, ok := compiler.IntForScalarNode(v14) if ok { x.MinItems = int64(t) } else { message := fmt.Sprintf("has unexpected value for minItems: %s", compiler.Display(v14)) errors = append(errors, compiler.NewError(context, message)) } } // bool unique_items = 15; v15 := compiler.MapValueForKey(m, "uniqueItems") if v15 != nil { x.UniqueItems, ok = compiler.BoolForScalarNode(v15) if !ok { message := fmt.Sprintf("has unexpected value for uniqueItems: %s", compiler.Display(v15)) errors = append(errors, compiler.NewError(context, message)) } } // repeated Any enum = 16; v16 := compiler.MapValueForKey(m, "enum") if v16 != nil { // repeated Any x.Enum = make([]*Any, 0) a, ok := compiler.SequenceNodeForNode(v16) if ok { for _, item := range a.Content { y, err := NewAny(item, compiler.NewContext("enum", item, context)) if err != nil { errors = append(errors, err) } x.Enum = append(x.Enum, y) } } } // float multiple_of = 17; v17 := compiler.MapValueForKey(m, "multipleOf") if v17 != nil { v, ok := compiler.FloatForScalarNode(v17) if ok { x.MultipleOf = v } else { message := fmt.Sprintf("has unexpected value for multipleOf: %s", compiler.Display(v17)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 18; v18 := compiler.MapValueForKey(m, "description") if v18 != nil { x.Description, ok = compiler.StringForScalarNode(v18) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v18)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny vendor_extension = 19; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewHeaderParameterSubSchema creates an object of type HeaderParameterSubSchema if possible, returning an error if not. func NewHeaderParameterSubSchema(in *yaml.Node, context *compiler.Context) (*HeaderParameterSubSchema, error) { errors := make([]error, 0) x := &HeaderParameterSubSchema{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"collectionFormat", "default", "description", "enum", "exclusiveMaximum", "exclusiveMinimum", "format", "in", "items", "maxItems", "maxLength", "maximum", "minItems", "minLength", "minimum", "multipleOf", "name", "pattern", "required", "type", "uniqueItems"} allowedPatterns := []*regexp.Regexp{pattern0} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // bool required = 1; v1 := compiler.MapValueForKey(m, "required") if v1 != nil { x.Required, ok = compiler.BoolForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for required: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string in = 2; v2 := compiler.MapValueForKey(m, "in") if v2 != nil { x.In, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for in: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [header] if ok && !compiler.StringArrayContainsValue([]string{"header"}, x.In) { message := fmt.Sprintf("has unexpected value for in: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 3; v3 := compiler.MapValueForKey(m, "description") if v3 != nil { x.Description, ok = compiler.StringForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } // string name = 4; v4 := compiler.MapValueForKey(m, "name") if v4 != nil { x.Name, ok = compiler.StringForScalarNode(v4) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v4)) errors = append(errors, compiler.NewError(context, message)) } } // string type = 5; v5 := compiler.MapValueForKey(m, "type") if v5 != nil { x.Type, ok = compiler.StringForScalarNode(v5) if !ok { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v5)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [string number boolean integer array] if ok && !compiler.StringArrayContainsValue([]string{"string", "number", "boolean", "integer", "array"}, x.Type) { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v5)) errors = append(errors, compiler.NewError(context, message)) } } // string format = 6; v6 := compiler.MapValueForKey(m, "format") if v6 != nil { x.Format, ok = compiler.StringForScalarNode(v6) if !ok { message := fmt.Sprintf("has unexpected value for format: %s", compiler.Display(v6)) errors = append(errors, compiler.NewError(context, message)) } } // PrimitivesItems items = 7; v7 := compiler.MapValueForKey(m, "items") if v7 != nil { var err error x.Items, err = NewPrimitivesItems(v7, compiler.NewContext("items", v7, context)) if err != nil { errors = append(errors, err) } } // string collection_format = 8; v8 := compiler.MapValueForKey(m, "collectionFormat") if v8 != nil { x.CollectionFormat, ok = compiler.StringForScalarNode(v8) if !ok { message := fmt.Sprintf("has unexpected value for collectionFormat: %s", compiler.Display(v8)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [csv ssv tsv pipes] if ok && !compiler.StringArrayContainsValue([]string{"csv", "ssv", "tsv", "pipes"}, x.CollectionFormat) { message := fmt.Sprintf("has unexpected value for collectionFormat: %s", compiler.Display(v8)) errors = append(errors, compiler.NewError(context, message)) } } // Any default = 9; v9 := compiler.MapValueForKey(m, "default") if v9 != nil { var err error x.Default, err = NewAny(v9, compiler.NewContext("default", v9, context)) if err != nil { errors = append(errors, err) } } // float maximum = 10; v10 := compiler.MapValueForKey(m, "maximum") if v10 != nil { v, ok := compiler.FloatForScalarNode(v10) if ok { x.Maximum = v } else { message := fmt.Sprintf("has unexpected value for maximum: %s", compiler.Display(v10)) errors = append(errors, compiler.NewError(context, message)) } } // bool exclusive_maximum = 11; v11 := compiler.MapValueForKey(m, "exclusiveMaximum") if v11 != nil { x.ExclusiveMaximum, ok = compiler.BoolForScalarNode(v11) if !ok { message := fmt.Sprintf("has unexpected value for exclusiveMaximum: %s", compiler.Display(v11)) errors = append(errors, compiler.NewError(context, message)) } } // float minimum = 12; v12 := compiler.MapValueForKey(m, "minimum") if v12 != nil { v, ok := compiler.FloatForScalarNode(v12) if ok { x.Minimum = v } else { message := fmt.Sprintf("has unexpected value for minimum: %s", compiler.Display(v12)) errors = append(errors, compiler.NewError(context, message)) } } // bool exclusive_minimum = 13; v13 := compiler.MapValueForKey(m, "exclusiveMinimum") if v13 != nil { x.ExclusiveMinimum, ok = compiler.BoolForScalarNode(v13) if !ok { message := fmt.Sprintf("has unexpected value for exclusiveMinimum: %s", compiler.Display(v13)) errors = append(errors, compiler.NewError(context, message)) } } // int64 max_length = 14; v14 := compiler.MapValueForKey(m, "maxLength") if v14 != nil { t, ok := compiler.IntForScalarNode(v14) if ok { x.MaxLength = int64(t) } else { message := fmt.Sprintf("has unexpected value for maxLength: %s", compiler.Display(v14)) errors = append(errors, compiler.NewError(context, message)) } } // int64 min_length = 15; v15 := compiler.MapValueForKey(m, "minLength") if v15 != nil { t, ok := compiler.IntForScalarNode(v15) if ok { x.MinLength = int64(t) } else { message := fmt.Sprintf("has unexpected value for minLength: %s", compiler.Display(v15)) errors = append(errors, compiler.NewError(context, message)) } } // string pattern = 16; v16 := compiler.MapValueForKey(m, "pattern") if v16 != nil { x.Pattern, ok = compiler.StringForScalarNode(v16) if !ok { message := fmt.Sprintf("has unexpected value for pattern: %s", compiler.Display(v16)) errors = append(errors, compiler.NewError(context, message)) } } // int64 max_items = 17; v17 := compiler.MapValueForKey(m, "maxItems") if v17 != nil { t, ok := compiler.IntForScalarNode(v17) if ok { x.MaxItems = int64(t) } else { message := fmt.Sprintf("has unexpected value for maxItems: %s", compiler.Display(v17)) errors = append(errors, compiler.NewError(context, message)) } } // int64 min_items = 18; v18 := compiler.MapValueForKey(m, "minItems") if v18 != nil { t, ok := compiler.IntForScalarNode(v18) if ok { x.MinItems = int64(t) } else { message := fmt.Sprintf("has unexpected value for minItems: %s", compiler.Display(v18)) errors = append(errors, compiler.NewError(context, message)) } } // bool unique_items = 19; v19 := compiler.MapValueForKey(m, "uniqueItems") if v19 != nil { x.UniqueItems, ok = compiler.BoolForScalarNode(v19) if !ok { message := fmt.Sprintf("has unexpected value for uniqueItems: %s", compiler.Display(v19)) errors = append(errors, compiler.NewError(context, message)) } } // repeated Any enum = 20; v20 := compiler.MapValueForKey(m, "enum") if v20 != nil { // repeated Any x.Enum = make([]*Any, 0) a, ok := compiler.SequenceNodeForNode(v20) if ok { for _, item := range a.Content { y, err := NewAny(item, compiler.NewContext("enum", item, context)) if err != nil { errors = append(errors, err) } x.Enum = append(x.Enum, y) } } } // float multiple_of = 21; v21 := compiler.MapValueForKey(m, "multipleOf") if v21 != nil { v, ok := compiler.FloatForScalarNode(v21) if ok { x.MultipleOf = v } else { message := fmt.Sprintf("has unexpected value for multipleOf: %s", compiler.Display(v21)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny vendor_extension = 22; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewHeaders creates an object of type Headers if possible, returning an error if not. func NewHeaders(in *yaml.Node, context *compiler.Context) (*Headers, error) { errors := make([]error, 0) x := &Headers{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedHeader additional_properties = 1; // MAP: Header x.AdditionalProperties = make([]*NamedHeader, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedHeader{} pair.Name = k var err error pair.Value, err = NewHeader(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewInfo creates an object of type Info if possible, returning an error if not. func NewInfo(in *yaml.Node, context *compiler.Context) (*Info, error) { errors := make([]error, 0) x := &Info{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"title", "version"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"contact", "description", "license", "termsOfService", "title", "version"} allowedPatterns := []*regexp.Regexp{pattern0} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string title = 1; v1 := compiler.MapValueForKey(m, "title") if v1 != nil { x.Title, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for title: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string version = 2; v2 := compiler.MapValueForKey(m, "version") if v2 != nil { x.Version, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for version: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 3; v3 := compiler.MapValueForKey(m, "description") if v3 != nil { x.Description, ok = compiler.StringForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } // string terms_of_service = 4; v4 := compiler.MapValueForKey(m, "termsOfService") if v4 != nil { x.TermsOfService, ok = compiler.StringForScalarNode(v4) if !ok { message := fmt.Sprintf("has unexpected value for termsOfService: %s", compiler.Display(v4)) errors = append(errors, compiler.NewError(context, message)) } } // Contact contact = 5; v5 := compiler.MapValueForKey(m, "contact") if v5 != nil { var err error x.Contact, err = NewContact(v5, compiler.NewContext("contact", v5, context)) if err != nil { errors = append(errors, err) } } // License license = 6; v6 := compiler.MapValueForKey(m, "license") if v6 != nil { var err error x.License, err = NewLicense(v6, compiler.NewContext("license", v6, context)) if err != nil { errors = append(errors, err) } } // repeated NamedAny vendor_extension = 7; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewItemsItem creates an object of type ItemsItem if possible, returning an error if not. func NewItemsItem(in *yaml.Node, context *compiler.Context) (*ItemsItem, error) { errors := make([]error, 0) x := &ItemsItem{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value for item array: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { x.Schema = make([]*Schema, 0) y, err := NewSchema(m, compiler.NewContext("", m, context)) if err != nil { return nil, err } x.Schema = append(x.Schema, y) } return x, compiler.NewErrorGroupOrNil(errors) } // NewJsonReference creates an object of type JsonReference if possible, returning an error if not. func NewJsonReference(in *yaml.Node, context *compiler.Context) (*JsonReference, error) { errors := make([]error, 0) x := &JsonReference{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"$ref"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string _ref = 1; v1 := compiler.MapValueForKey(m, "$ref") if v1 != nil { x.XRef, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for $ref: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 2; v2 := compiler.MapValueForKey(m, "description") if v2 != nil { x.Description, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewLicense creates an object of type License if possible, returning an error if not. func NewLicense(in *yaml.Node, context *compiler.Context) (*License, error) { errors := make([]error, 0) x := &License{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"name"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"name", "url"} allowedPatterns := []*regexp.Regexp{pattern0} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string url = 2; v2 := compiler.MapValueForKey(m, "url") if v2 != nil { x.Url, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for url: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny vendor_extension = 3; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNamedAny creates an object of type NamedAny if possible, returning an error if not. func NewNamedAny(in *yaml.Node, context *compiler.Context) (*NamedAny, error) { errors := make([]error, 0) x := &NamedAny{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"name", "value"} var allowedPatterns []*regexp.Regexp invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // Any value = 2; v2 := compiler.MapValueForKey(m, "value") if v2 != nil { var err error x.Value, err = NewAny(v2, compiler.NewContext("value", v2, context)) if err != nil { errors = append(errors, err) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNamedHeader creates an object of type NamedHeader if possible, returning an error if not. func NewNamedHeader(in *yaml.Node, context *compiler.Context) (*NamedHeader, error) { errors := make([]error, 0) x := &NamedHeader{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"name", "value"} var allowedPatterns []*regexp.Regexp invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // Header value = 2; v2 := compiler.MapValueForKey(m, "value") if v2 != nil { var err error x.Value, err = NewHeader(v2, compiler.NewContext("value", v2, context)) if err != nil { errors = append(errors, err) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNamedParameter creates an object of type NamedParameter if possible, returning an error if not. func NewNamedParameter(in *yaml.Node, context *compiler.Context) (*NamedParameter, error) { errors := make([]error, 0) x := &NamedParameter{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"name", "value"} var allowedPatterns []*regexp.Regexp invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // Parameter value = 2; v2 := compiler.MapValueForKey(m, "value") if v2 != nil { var err error x.Value, err = NewParameter(v2, compiler.NewContext("value", v2, context)) if err != nil { errors = append(errors, err) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNamedPathItem creates an object of type NamedPathItem if possible, returning an error if not. func NewNamedPathItem(in *yaml.Node, context *compiler.Context) (*NamedPathItem, error) { errors := make([]error, 0) x := &NamedPathItem{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"name", "value"} var allowedPatterns []*regexp.Regexp invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // PathItem value = 2; v2 := compiler.MapValueForKey(m, "value") if v2 != nil { var err error x.Value, err = NewPathItem(v2, compiler.NewContext("value", v2, context)) if err != nil { errors = append(errors, err) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNamedResponse creates an object of type NamedResponse if possible, returning an error if not. func NewNamedResponse(in *yaml.Node, context *compiler.Context) (*NamedResponse, error) { errors := make([]error, 0) x := &NamedResponse{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"name", "value"} var allowedPatterns []*regexp.Regexp invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // Response value = 2; v2 := compiler.MapValueForKey(m, "value") if v2 != nil { var err error x.Value, err = NewResponse(v2, compiler.NewContext("value", v2, context)) if err != nil { errors = append(errors, err) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNamedResponseValue creates an object of type NamedResponseValue if possible, returning an error if not. func NewNamedResponseValue(in *yaml.Node, context *compiler.Context) (*NamedResponseValue, error) { errors := make([]error, 0) x := &NamedResponseValue{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"name", "value"} var allowedPatterns []*regexp.Regexp invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // ResponseValue value = 2; v2 := compiler.MapValueForKey(m, "value") if v2 != nil { var err error x.Value, err = NewResponseValue(v2, compiler.NewContext("value", v2, context)) if err != nil { errors = append(errors, err) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNamedSchema creates an object of type NamedSchema if possible, returning an error if not. func NewNamedSchema(in *yaml.Node, context *compiler.Context) (*NamedSchema, error) { errors := make([]error, 0) x := &NamedSchema{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"name", "value"} var allowedPatterns []*regexp.Regexp invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // Schema value = 2; v2 := compiler.MapValueForKey(m, "value") if v2 != nil { var err error x.Value, err = NewSchema(v2, compiler.NewContext("value", v2, context)) if err != nil { errors = append(errors, err) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNamedSecurityDefinitionsItem creates an object of type NamedSecurityDefinitionsItem if possible, returning an error if not. func NewNamedSecurityDefinitionsItem(in *yaml.Node, context *compiler.Context) (*NamedSecurityDefinitionsItem, error) { errors := make([]error, 0) x := &NamedSecurityDefinitionsItem{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"name", "value"} var allowedPatterns []*regexp.Regexp invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // SecurityDefinitionsItem value = 2; v2 := compiler.MapValueForKey(m, "value") if v2 != nil { var err error x.Value, err = NewSecurityDefinitionsItem(v2, compiler.NewContext("value", v2, context)) if err != nil { errors = append(errors, err) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNamedString creates an object of type NamedString if possible, returning an error if not. func NewNamedString(in *yaml.Node, context *compiler.Context) (*NamedString, error) { errors := make([]error, 0) x := &NamedString{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"name", "value"} var allowedPatterns []*regexp.Regexp invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string value = 2; v2 := compiler.MapValueForKey(m, "value") if v2 != nil { x.Value, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for value: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNamedStringArray creates an object of type NamedStringArray if possible, returning an error if not. func NewNamedStringArray(in *yaml.Node, context *compiler.Context) (*NamedStringArray, error) { errors := make([]error, 0) x := &NamedStringArray{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"name", "value"} var allowedPatterns []*regexp.Regexp invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // StringArray value = 2; v2 := compiler.MapValueForKey(m, "value") if v2 != nil { var err error x.Value, err = NewStringArray(v2, compiler.NewContext("value", v2, context)) if err != nil { errors = append(errors, err) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNonBodyParameter creates an object of type NonBodyParameter if possible, returning an error if not. func NewNonBodyParameter(in *yaml.Node, context *compiler.Context) (*NonBodyParameter, error) { errors := make([]error, 0) x := &NonBodyParameter{} matched := false m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"in", "name", "type"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // HeaderParameterSubSchema header_parameter_sub_schema = 1; { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewHeaderParameterSubSchema(m, compiler.NewContext("headerParameterSubSchema", m, context)) if matchingError == nil { x.Oneof = &NonBodyParameter_HeaderParameterSubSchema{HeaderParameterSubSchema: t} matched = true } else { errors = append(errors, matchingError) } } // FormDataParameterSubSchema form_data_parameter_sub_schema = 2; { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewFormDataParameterSubSchema(m, compiler.NewContext("formDataParameterSubSchema", m, context)) if matchingError == nil { x.Oneof = &NonBodyParameter_FormDataParameterSubSchema{FormDataParameterSubSchema: t} matched = true } else { errors = append(errors, matchingError) } } // QueryParameterSubSchema query_parameter_sub_schema = 3; { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewQueryParameterSubSchema(m, compiler.NewContext("queryParameterSubSchema", m, context)) if matchingError == nil { x.Oneof = &NonBodyParameter_QueryParameterSubSchema{QueryParameterSubSchema: t} matched = true } else { errors = append(errors, matchingError) } } // PathParameterSubSchema path_parameter_sub_schema = 4; { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewPathParameterSubSchema(m, compiler.NewContext("pathParameterSubSchema", m, context)) if matchingError == nil { x.Oneof = &NonBodyParameter_PathParameterSubSchema{PathParameterSubSchema: t} matched = true } else { errors = append(errors, matchingError) } } } if matched { // since the oneof matched one of its possibilities, discard any matching errors errors = make([]error, 0) } else { message := fmt.Sprintf("contains an invalid NonBodyParameter") err := compiler.NewError(context, message) errors = []error{err} } return x, compiler.NewErrorGroupOrNil(errors) } // NewOauth2AccessCodeSecurity creates an object of type Oauth2AccessCodeSecurity if possible, returning an error if not. func NewOauth2AccessCodeSecurity(in *yaml.Node, context *compiler.Context) (*Oauth2AccessCodeSecurity, error) { errors := make([]error, 0) x := &Oauth2AccessCodeSecurity{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"authorizationUrl", "flow", "tokenUrl", "type"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"authorizationUrl", "description", "flow", "scopes", "tokenUrl", "type"} allowedPatterns := []*regexp.Regexp{pattern0} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string type = 1; v1 := compiler.MapValueForKey(m, "type") if v1 != nil { x.Type, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [oauth2] if ok && !compiler.StringArrayContainsValue([]string{"oauth2"}, x.Type) { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string flow = 2; v2 := compiler.MapValueForKey(m, "flow") if v2 != nil { x.Flow, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for flow: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [accessCode] if ok && !compiler.StringArrayContainsValue([]string{"accessCode"}, x.Flow) { message := fmt.Sprintf("has unexpected value for flow: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // Oauth2Scopes scopes = 3; v3 := compiler.MapValueForKey(m, "scopes") if v3 != nil { var err error x.Scopes, err = NewOauth2Scopes(v3, compiler.NewContext("scopes", v3, context)) if err != nil { errors = append(errors, err) } } // string authorization_url = 4; v4 := compiler.MapValueForKey(m, "authorizationUrl") if v4 != nil { x.AuthorizationUrl, ok = compiler.StringForScalarNode(v4) if !ok { message := fmt.Sprintf("has unexpected value for authorizationUrl: %s", compiler.Display(v4)) errors = append(errors, compiler.NewError(context, message)) } } // string token_url = 5; v5 := compiler.MapValueForKey(m, "tokenUrl") if v5 != nil { x.TokenUrl, ok = compiler.StringForScalarNode(v5) if !ok { message := fmt.Sprintf("has unexpected value for tokenUrl: %s", compiler.Display(v5)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 6; v6 := compiler.MapValueForKey(m, "description") if v6 != nil { x.Description, ok = compiler.StringForScalarNode(v6) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v6)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny vendor_extension = 7; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewOauth2ApplicationSecurity creates an object of type Oauth2ApplicationSecurity if possible, returning an error if not. func NewOauth2ApplicationSecurity(in *yaml.Node, context *compiler.Context) (*Oauth2ApplicationSecurity, error) { errors := make([]error, 0) x := &Oauth2ApplicationSecurity{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"flow", "tokenUrl", "type"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"description", "flow", "scopes", "tokenUrl", "type"} allowedPatterns := []*regexp.Regexp{pattern0} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string type = 1; v1 := compiler.MapValueForKey(m, "type") if v1 != nil { x.Type, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [oauth2] if ok && !compiler.StringArrayContainsValue([]string{"oauth2"}, x.Type) { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string flow = 2; v2 := compiler.MapValueForKey(m, "flow") if v2 != nil { x.Flow, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for flow: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [application] if ok && !compiler.StringArrayContainsValue([]string{"application"}, x.Flow) { message := fmt.Sprintf("has unexpected value for flow: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // Oauth2Scopes scopes = 3; v3 := compiler.MapValueForKey(m, "scopes") if v3 != nil { var err error x.Scopes, err = NewOauth2Scopes(v3, compiler.NewContext("scopes", v3, context)) if err != nil { errors = append(errors, err) } } // string token_url = 4; v4 := compiler.MapValueForKey(m, "tokenUrl") if v4 != nil { x.TokenUrl, ok = compiler.StringForScalarNode(v4) if !ok { message := fmt.Sprintf("has unexpected value for tokenUrl: %s", compiler.Display(v4)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 5; v5 := compiler.MapValueForKey(m, "description") if v5 != nil { x.Description, ok = compiler.StringForScalarNode(v5) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v5)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny vendor_extension = 6; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewOauth2ImplicitSecurity creates an object of type Oauth2ImplicitSecurity if possible, returning an error if not. func NewOauth2ImplicitSecurity(in *yaml.Node, context *compiler.Context) (*Oauth2ImplicitSecurity, error) { errors := make([]error, 0) x := &Oauth2ImplicitSecurity{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"authorizationUrl", "flow", "type"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"authorizationUrl", "description", "flow", "scopes", "type"} allowedPatterns := []*regexp.Regexp{pattern0} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string type = 1; v1 := compiler.MapValueForKey(m, "type") if v1 != nil { x.Type, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [oauth2] if ok && !compiler.StringArrayContainsValue([]string{"oauth2"}, x.Type) { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string flow = 2; v2 := compiler.MapValueForKey(m, "flow") if v2 != nil { x.Flow, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for flow: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [implicit] if ok && !compiler.StringArrayContainsValue([]string{"implicit"}, x.Flow) { message := fmt.Sprintf("has unexpected value for flow: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // Oauth2Scopes scopes = 3; v3 := compiler.MapValueForKey(m, "scopes") if v3 != nil { var err error x.Scopes, err = NewOauth2Scopes(v3, compiler.NewContext("scopes", v3, context)) if err != nil { errors = append(errors, err) } } // string authorization_url = 4; v4 := compiler.MapValueForKey(m, "authorizationUrl") if v4 != nil { x.AuthorizationUrl, ok = compiler.StringForScalarNode(v4) if !ok { message := fmt.Sprintf("has unexpected value for authorizationUrl: %s", compiler.Display(v4)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 5; v5 := compiler.MapValueForKey(m, "description") if v5 != nil { x.Description, ok = compiler.StringForScalarNode(v5) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v5)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny vendor_extension = 6; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewOauth2PasswordSecurity creates an object of type Oauth2PasswordSecurity if possible, returning an error if not. func NewOauth2PasswordSecurity(in *yaml.Node, context *compiler.Context) (*Oauth2PasswordSecurity, error) { errors := make([]error, 0) x := &Oauth2PasswordSecurity{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"flow", "tokenUrl", "type"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"description", "flow", "scopes", "tokenUrl", "type"} allowedPatterns := []*regexp.Regexp{pattern0} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string type = 1; v1 := compiler.MapValueForKey(m, "type") if v1 != nil { x.Type, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [oauth2] if ok && !compiler.StringArrayContainsValue([]string{"oauth2"}, x.Type) { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string flow = 2; v2 := compiler.MapValueForKey(m, "flow") if v2 != nil { x.Flow, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for flow: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [password] if ok && !compiler.StringArrayContainsValue([]string{"password"}, x.Flow) { message := fmt.Sprintf("has unexpected value for flow: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // Oauth2Scopes scopes = 3; v3 := compiler.MapValueForKey(m, "scopes") if v3 != nil { var err error x.Scopes, err = NewOauth2Scopes(v3, compiler.NewContext("scopes", v3, context)) if err != nil { errors = append(errors, err) } } // string token_url = 4; v4 := compiler.MapValueForKey(m, "tokenUrl") if v4 != nil { x.TokenUrl, ok = compiler.StringForScalarNode(v4) if !ok { message := fmt.Sprintf("has unexpected value for tokenUrl: %s", compiler.Display(v4)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 5; v5 := compiler.MapValueForKey(m, "description") if v5 != nil { x.Description, ok = compiler.StringForScalarNode(v5) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v5)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny vendor_extension = 6; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewOauth2Scopes creates an object of type Oauth2Scopes if possible, returning an error if not. func NewOauth2Scopes(in *yaml.Node, context *compiler.Context) (*Oauth2Scopes, error) { errors := make([]error, 0) x := &Oauth2Scopes{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedString additional_properties = 1; // MAP: string x.AdditionalProperties = make([]*NamedString, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedString{} pair.Name = k pair.Value, _ = compiler.StringForScalarNode(v) x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewOperation creates an object of type Operation if possible, returning an error if not. func NewOperation(in *yaml.Node, context *compiler.Context) (*Operation, error) { errors := make([]error, 0) x := &Operation{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"responses"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"consumes", "deprecated", "description", "externalDocs", "operationId", "parameters", "produces", "responses", "schemes", "security", "summary", "tags"} allowedPatterns := []*regexp.Regexp{pattern0} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // repeated string tags = 1; v1 := compiler.MapValueForKey(m, "tags") if v1 != nil { v, ok := compiler.SequenceNodeForNode(v1) if ok { x.Tags = compiler.StringArrayForSequenceNode(v) } else { message := fmt.Sprintf("has unexpected value for tags: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string summary = 2; v2 := compiler.MapValueForKey(m, "summary") if v2 != nil { x.Summary, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for summary: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 3; v3 := compiler.MapValueForKey(m, "description") if v3 != nil { x.Description, ok = compiler.StringForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } // ExternalDocs external_docs = 4; v4 := compiler.MapValueForKey(m, "externalDocs") if v4 != nil { var err error x.ExternalDocs, err = NewExternalDocs(v4, compiler.NewContext("externalDocs", v4, context)) if err != nil { errors = append(errors, err) } } // string operation_id = 5; v5 := compiler.MapValueForKey(m, "operationId") if v5 != nil { x.OperationId, ok = compiler.StringForScalarNode(v5) if !ok { message := fmt.Sprintf("has unexpected value for operationId: %s", compiler.Display(v5)) errors = append(errors, compiler.NewError(context, message)) } } // repeated string produces = 6; v6 := compiler.MapValueForKey(m, "produces") if v6 != nil { v, ok := compiler.SequenceNodeForNode(v6) if ok { x.Produces = compiler.StringArrayForSequenceNode(v) } else { message := fmt.Sprintf("has unexpected value for produces: %s", compiler.Display(v6)) errors = append(errors, compiler.NewError(context, message)) } } // repeated string consumes = 7; v7 := compiler.MapValueForKey(m, "consumes") if v7 != nil { v, ok := compiler.SequenceNodeForNode(v7) if ok { x.Consumes = compiler.StringArrayForSequenceNode(v) } else { message := fmt.Sprintf("has unexpected value for consumes: %s", compiler.Display(v7)) errors = append(errors, compiler.NewError(context, message)) } } // repeated ParametersItem parameters = 8; v8 := compiler.MapValueForKey(m, "parameters") if v8 != nil { // repeated ParametersItem x.Parameters = make([]*ParametersItem, 0) a, ok := compiler.SequenceNodeForNode(v8) if ok { for _, item := range a.Content { y, err := NewParametersItem(item, compiler.NewContext("parameters", item, context)) if err != nil { errors = append(errors, err) } x.Parameters = append(x.Parameters, y) } } } // Responses responses = 9; v9 := compiler.MapValueForKey(m, "responses") if v9 != nil { var err error x.Responses, err = NewResponses(v9, compiler.NewContext("responses", v9, context)) if err != nil { errors = append(errors, err) } } // repeated string schemes = 10; v10 := compiler.MapValueForKey(m, "schemes") if v10 != nil { v, ok := compiler.SequenceNodeForNode(v10) if ok { x.Schemes = compiler.StringArrayForSequenceNode(v) } else { message := fmt.Sprintf("has unexpected value for schemes: %s", compiler.Display(v10)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [http https ws wss] if ok && !compiler.StringArrayContainsValues([]string{"http", "https", "ws", "wss"}, x.Schemes) { message := fmt.Sprintf("has unexpected value for schemes: %s", compiler.Display(v10)) errors = append(errors, compiler.NewError(context, message)) } } // bool deprecated = 11; v11 := compiler.MapValueForKey(m, "deprecated") if v11 != nil { x.Deprecated, ok = compiler.BoolForScalarNode(v11) if !ok { message := fmt.Sprintf("has unexpected value for deprecated: %s", compiler.Display(v11)) errors = append(errors, compiler.NewError(context, message)) } } // repeated SecurityRequirement security = 12; v12 := compiler.MapValueForKey(m, "security") if v12 != nil { // repeated SecurityRequirement x.Security = make([]*SecurityRequirement, 0) a, ok := compiler.SequenceNodeForNode(v12) if ok { for _, item := range a.Content { y, err := NewSecurityRequirement(item, compiler.NewContext("security", item, context)) if err != nil { errors = append(errors, err) } x.Security = append(x.Security, y) } } } // repeated NamedAny vendor_extension = 13; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewParameter creates an object of type Parameter if possible, returning an error if not. func NewParameter(in *yaml.Node, context *compiler.Context) (*Parameter, error) { errors := make([]error, 0) x := &Parameter{} matched := false // BodyParameter body_parameter = 1; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewBodyParameter(m, compiler.NewContext("bodyParameter", m, context)) if matchingError == nil { x.Oneof = &Parameter_BodyParameter{BodyParameter: t} matched = true } else { errors = append(errors, matchingError) } } } // NonBodyParameter non_body_parameter = 2; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewNonBodyParameter(m, compiler.NewContext("nonBodyParameter", m, context)) if matchingError == nil { x.Oneof = &Parameter_NonBodyParameter{NonBodyParameter: t} matched = true } else { errors = append(errors, matchingError) } } } if matched { // since the oneof matched one of its possibilities, discard any matching errors errors = make([]error, 0) } else { message := fmt.Sprintf("contains an invalid Parameter") err := compiler.NewError(context, message) errors = []error{err} } return x, compiler.NewErrorGroupOrNil(errors) } // NewParameterDefinitions creates an object of type ParameterDefinitions if possible, returning an error if not. func NewParameterDefinitions(in *yaml.Node, context *compiler.Context) (*ParameterDefinitions, error) { errors := make([]error, 0) x := &ParameterDefinitions{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedParameter additional_properties = 1; // MAP: Parameter x.AdditionalProperties = make([]*NamedParameter, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedParameter{} pair.Name = k var err error pair.Value, err = NewParameter(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewParametersItem creates an object of type ParametersItem if possible, returning an error if not. func NewParametersItem(in *yaml.Node, context *compiler.Context) (*ParametersItem, error) { errors := make([]error, 0) x := &ParametersItem{} matched := false // Parameter parameter = 1; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewParameter(m, compiler.NewContext("parameter", m, context)) if matchingError == nil { x.Oneof = &ParametersItem_Parameter{Parameter: t} matched = true } else { errors = append(errors, matchingError) } } } // JsonReference json_reference = 2; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewJsonReference(m, compiler.NewContext("jsonReference", m, context)) if matchingError == nil { x.Oneof = &ParametersItem_JsonReference{JsonReference: t} matched = true } else { errors = append(errors, matchingError) } } } if matched { // since the oneof matched one of its possibilities, discard any matching errors errors = make([]error, 0) } else { message := fmt.Sprintf("contains an invalid ParametersItem") err := compiler.NewError(context, message) errors = []error{err} } return x, compiler.NewErrorGroupOrNil(errors) } // NewPathItem creates an object of type PathItem if possible, returning an error if not. func NewPathItem(in *yaml.Node, context *compiler.Context) (*PathItem, error) { errors := make([]error, 0) x := &PathItem{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"$ref", "delete", "get", "head", "options", "parameters", "patch", "post", "put"} allowedPatterns := []*regexp.Regexp{pattern0} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string _ref = 1; v1 := compiler.MapValueForKey(m, "$ref") if v1 != nil { x.XRef, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for $ref: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // Operation get = 2; v2 := compiler.MapValueForKey(m, "get") if v2 != nil { var err error x.Get, err = NewOperation(v2, compiler.NewContext("get", v2, context)) if err != nil { errors = append(errors, err) } } // Operation put = 3; v3 := compiler.MapValueForKey(m, "put") if v3 != nil { var err error x.Put, err = NewOperation(v3, compiler.NewContext("put", v3, context)) if err != nil { errors = append(errors, err) } } // Operation post = 4; v4 := compiler.MapValueForKey(m, "post") if v4 != nil { var err error x.Post, err = NewOperation(v4, compiler.NewContext("post", v4, context)) if err != nil { errors = append(errors, err) } } // Operation delete = 5; v5 := compiler.MapValueForKey(m, "delete") if v5 != nil { var err error x.Delete, err = NewOperation(v5, compiler.NewContext("delete", v5, context)) if err != nil { errors = append(errors, err) } } // Operation options = 6; v6 := compiler.MapValueForKey(m, "options") if v6 != nil { var err error x.Options, err = NewOperation(v6, compiler.NewContext("options", v6, context)) if err != nil { errors = append(errors, err) } } // Operation head = 7; v7 := compiler.MapValueForKey(m, "head") if v7 != nil { var err error x.Head, err = NewOperation(v7, compiler.NewContext("head", v7, context)) if err != nil { errors = append(errors, err) } } // Operation patch = 8; v8 := compiler.MapValueForKey(m, "patch") if v8 != nil { var err error x.Patch, err = NewOperation(v8, compiler.NewContext("patch", v8, context)) if err != nil { errors = append(errors, err) } } // repeated ParametersItem parameters = 9; v9 := compiler.MapValueForKey(m, "parameters") if v9 != nil { // repeated ParametersItem x.Parameters = make([]*ParametersItem, 0) a, ok := compiler.SequenceNodeForNode(v9) if ok { for _, item := range a.Content { y, err := NewParametersItem(item, compiler.NewContext("parameters", item, context)) if err != nil { errors = append(errors, err) } x.Parameters = append(x.Parameters, y) } } } // repeated NamedAny vendor_extension = 10; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewPathParameterSubSchema creates an object of type PathParameterSubSchema if possible, returning an error if not. func NewPathParameterSubSchema(in *yaml.Node, context *compiler.Context) (*PathParameterSubSchema, error) { errors := make([]error, 0) x := &PathParameterSubSchema{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"required"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"collectionFormat", "default", "description", "enum", "exclusiveMaximum", "exclusiveMinimum", "format", "in", "items", "maxItems", "maxLength", "maximum", "minItems", "minLength", "minimum", "multipleOf", "name", "pattern", "required", "type", "uniqueItems"} allowedPatterns := []*regexp.Regexp{pattern0} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // bool required = 1; v1 := compiler.MapValueForKey(m, "required") if v1 != nil { x.Required, ok = compiler.BoolForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for required: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string in = 2; v2 := compiler.MapValueForKey(m, "in") if v2 != nil { x.In, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for in: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [path] if ok && !compiler.StringArrayContainsValue([]string{"path"}, x.In) { message := fmt.Sprintf("has unexpected value for in: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 3; v3 := compiler.MapValueForKey(m, "description") if v3 != nil { x.Description, ok = compiler.StringForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } // string name = 4; v4 := compiler.MapValueForKey(m, "name") if v4 != nil { x.Name, ok = compiler.StringForScalarNode(v4) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v4)) errors = append(errors, compiler.NewError(context, message)) } } // string type = 5; v5 := compiler.MapValueForKey(m, "type") if v5 != nil { x.Type, ok = compiler.StringForScalarNode(v5) if !ok { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v5)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [string number boolean integer array] if ok && !compiler.StringArrayContainsValue([]string{"string", "number", "boolean", "integer", "array"}, x.Type) { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v5)) errors = append(errors, compiler.NewError(context, message)) } } // string format = 6; v6 := compiler.MapValueForKey(m, "format") if v6 != nil { x.Format, ok = compiler.StringForScalarNode(v6) if !ok { message := fmt.Sprintf("has unexpected value for format: %s", compiler.Display(v6)) errors = append(errors, compiler.NewError(context, message)) } } // PrimitivesItems items = 7; v7 := compiler.MapValueForKey(m, "items") if v7 != nil { var err error x.Items, err = NewPrimitivesItems(v7, compiler.NewContext("items", v7, context)) if err != nil { errors = append(errors, err) } } // string collection_format = 8; v8 := compiler.MapValueForKey(m, "collectionFormat") if v8 != nil { x.CollectionFormat, ok = compiler.StringForScalarNode(v8) if !ok { message := fmt.Sprintf("has unexpected value for collectionFormat: %s", compiler.Display(v8)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [csv ssv tsv pipes] if ok && !compiler.StringArrayContainsValue([]string{"csv", "ssv", "tsv", "pipes"}, x.CollectionFormat) { message := fmt.Sprintf("has unexpected value for collectionFormat: %s", compiler.Display(v8)) errors = append(errors, compiler.NewError(context, message)) } } // Any default = 9; v9 := compiler.MapValueForKey(m, "default") if v9 != nil { var err error x.Default, err = NewAny(v9, compiler.NewContext("default", v9, context)) if err != nil { errors = append(errors, err) } } // float maximum = 10; v10 := compiler.MapValueForKey(m, "maximum") if v10 != nil { v, ok := compiler.FloatForScalarNode(v10) if ok { x.Maximum = v } else { message := fmt.Sprintf("has unexpected value for maximum: %s", compiler.Display(v10)) errors = append(errors, compiler.NewError(context, message)) } } // bool exclusive_maximum = 11; v11 := compiler.MapValueForKey(m, "exclusiveMaximum") if v11 != nil { x.ExclusiveMaximum, ok = compiler.BoolForScalarNode(v11) if !ok { message := fmt.Sprintf("has unexpected value for exclusiveMaximum: %s", compiler.Display(v11)) errors = append(errors, compiler.NewError(context, message)) } } // float minimum = 12; v12 := compiler.MapValueForKey(m, "minimum") if v12 != nil { v, ok := compiler.FloatForScalarNode(v12) if ok { x.Minimum = v } else { message := fmt.Sprintf("has unexpected value for minimum: %s", compiler.Display(v12)) errors = append(errors, compiler.NewError(context, message)) } } // bool exclusive_minimum = 13; v13 := compiler.MapValueForKey(m, "exclusiveMinimum") if v13 != nil { x.ExclusiveMinimum, ok = compiler.BoolForScalarNode(v13) if !ok { message := fmt.Sprintf("has unexpected value for exclusiveMinimum: %s", compiler.Display(v13)) errors = append(errors, compiler.NewError(context, message)) } } // int64 max_length = 14; v14 := compiler.MapValueForKey(m, "maxLength") if v14 != nil { t, ok := compiler.IntForScalarNode(v14) if ok { x.MaxLength = int64(t) } else { message := fmt.Sprintf("has unexpected value for maxLength: %s", compiler.Display(v14)) errors = append(errors, compiler.NewError(context, message)) } } // int64 min_length = 15; v15 := compiler.MapValueForKey(m, "minLength") if v15 != nil { t, ok := compiler.IntForScalarNode(v15) if ok { x.MinLength = int64(t) } else { message := fmt.Sprintf("has unexpected value for minLength: %s", compiler.Display(v15)) errors = append(errors, compiler.NewError(context, message)) } } // string pattern = 16; v16 := compiler.MapValueForKey(m, "pattern") if v16 != nil { x.Pattern, ok = compiler.StringForScalarNode(v16) if !ok { message := fmt.Sprintf("has unexpected value for pattern: %s", compiler.Display(v16)) errors = append(errors, compiler.NewError(context, message)) } } // int64 max_items = 17; v17 := compiler.MapValueForKey(m, "maxItems") if v17 != nil { t, ok := compiler.IntForScalarNode(v17) if ok { x.MaxItems = int64(t) } else { message := fmt.Sprintf("has unexpected value for maxItems: %s", compiler.Display(v17)) errors = append(errors, compiler.NewError(context, message)) } } // int64 min_items = 18; v18 := compiler.MapValueForKey(m, "minItems") if v18 != nil { t, ok := compiler.IntForScalarNode(v18) if ok { x.MinItems = int64(t) } else { message := fmt.Sprintf("has unexpected value for minItems: %s", compiler.Display(v18)) errors = append(errors, compiler.NewError(context, message)) } } // bool unique_items = 19; v19 := compiler.MapValueForKey(m, "uniqueItems") if v19 != nil { x.UniqueItems, ok = compiler.BoolForScalarNode(v19) if !ok { message := fmt.Sprintf("has unexpected value for uniqueItems: %s", compiler.Display(v19)) errors = append(errors, compiler.NewError(context, message)) } } // repeated Any enum = 20; v20 := compiler.MapValueForKey(m, "enum") if v20 != nil { // repeated Any x.Enum = make([]*Any, 0) a, ok := compiler.SequenceNodeForNode(v20) if ok { for _, item := range a.Content { y, err := NewAny(item, compiler.NewContext("enum", item, context)) if err != nil { errors = append(errors, err) } x.Enum = append(x.Enum, y) } } } // float multiple_of = 21; v21 := compiler.MapValueForKey(m, "multipleOf") if v21 != nil { v, ok := compiler.FloatForScalarNode(v21) if ok { x.MultipleOf = v } else { message := fmt.Sprintf("has unexpected value for multipleOf: %s", compiler.Display(v21)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny vendor_extension = 22; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewPaths creates an object of type Paths if possible, returning an error if not. func NewPaths(in *yaml.Node, context *compiler.Context) (*Paths, error) { errors := make([]error, 0) x := &Paths{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{} allowedPatterns := []*regexp.Regexp{pattern0, pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // repeated NamedAny vendor_extension = 1; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } // repeated NamedPathItem path = 2; // MAP: PathItem ^/ x.Path = make([]*NamedPathItem, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "/") { pair := &NamedPathItem{} pair.Name = k var err error pair.Value, err = NewPathItem(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } x.Path = append(x.Path, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewPrimitivesItems creates an object of type PrimitivesItems if possible, returning an error if not. func NewPrimitivesItems(in *yaml.Node, context *compiler.Context) (*PrimitivesItems, error) { errors := make([]error, 0) x := &PrimitivesItems{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"collectionFormat", "default", "enum", "exclusiveMaximum", "exclusiveMinimum", "format", "items", "maxItems", "maxLength", "maximum", "minItems", "minLength", "minimum", "multipleOf", "pattern", "type", "uniqueItems"} allowedPatterns := []*regexp.Regexp{pattern0} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string type = 1; v1 := compiler.MapValueForKey(m, "type") if v1 != nil { x.Type, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [string number integer boolean array] if ok && !compiler.StringArrayContainsValue([]string{"string", "number", "integer", "boolean", "array"}, x.Type) { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string format = 2; v2 := compiler.MapValueForKey(m, "format") if v2 != nil { x.Format, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for format: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // PrimitivesItems items = 3; v3 := compiler.MapValueForKey(m, "items") if v3 != nil { var err error x.Items, err = NewPrimitivesItems(v3, compiler.NewContext("items", v3, context)) if err != nil { errors = append(errors, err) } } // string collection_format = 4; v4 := compiler.MapValueForKey(m, "collectionFormat") if v4 != nil { x.CollectionFormat, ok = compiler.StringForScalarNode(v4) if !ok { message := fmt.Sprintf("has unexpected value for collectionFormat: %s", compiler.Display(v4)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [csv ssv tsv pipes] if ok && !compiler.StringArrayContainsValue([]string{"csv", "ssv", "tsv", "pipes"}, x.CollectionFormat) { message := fmt.Sprintf("has unexpected value for collectionFormat: %s", compiler.Display(v4)) errors = append(errors, compiler.NewError(context, message)) } } // Any default = 5; v5 := compiler.MapValueForKey(m, "default") if v5 != nil { var err error x.Default, err = NewAny(v5, compiler.NewContext("default", v5, context)) if err != nil { errors = append(errors, err) } } // float maximum = 6; v6 := compiler.MapValueForKey(m, "maximum") if v6 != nil { v, ok := compiler.FloatForScalarNode(v6) if ok { x.Maximum = v } else { message := fmt.Sprintf("has unexpected value for maximum: %s", compiler.Display(v6)) errors = append(errors, compiler.NewError(context, message)) } } // bool exclusive_maximum = 7; v7 := compiler.MapValueForKey(m, "exclusiveMaximum") if v7 != nil { x.ExclusiveMaximum, ok = compiler.BoolForScalarNode(v7) if !ok { message := fmt.Sprintf("has unexpected value for exclusiveMaximum: %s", compiler.Display(v7)) errors = append(errors, compiler.NewError(context, message)) } } // float minimum = 8; v8 := compiler.MapValueForKey(m, "minimum") if v8 != nil { v, ok := compiler.FloatForScalarNode(v8) if ok { x.Minimum = v } else { message := fmt.Sprintf("has unexpected value for minimum: %s", compiler.Display(v8)) errors = append(errors, compiler.NewError(context, message)) } } // bool exclusive_minimum = 9; v9 := compiler.MapValueForKey(m, "exclusiveMinimum") if v9 != nil { x.ExclusiveMinimum, ok = compiler.BoolForScalarNode(v9) if !ok { message := fmt.Sprintf("has unexpected value for exclusiveMinimum: %s", compiler.Display(v9)) errors = append(errors, compiler.NewError(context, message)) } } // int64 max_length = 10; v10 := compiler.MapValueForKey(m, "maxLength") if v10 != nil { t, ok := compiler.IntForScalarNode(v10) if ok { x.MaxLength = int64(t) } else { message := fmt.Sprintf("has unexpected value for maxLength: %s", compiler.Display(v10)) errors = append(errors, compiler.NewError(context, message)) } } // int64 min_length = 11; v11 := compiler.MapValueForKey(m, "minLength") if v11 != nil { t, ok := compiler.IntForScalarNode(v11) if ok { x.MinLength = int64(t) } else { message := fmt.Sprintf("has unexpected value for minLength: %s", compiler.Display(v11)) errors = append(errors, compiler.NewError(context, message)) } } // string pattern = 12; v12 := compiler.MapValueForKey(m, "pattern") if v12 != nil { x.Pattern, ok = compiler.StringForScalarNode(v12) if !ok { message := fmt.Sprintf("has unexpected value for pattern: %s", compiler.Display(v12)) errors = append(errors, compiler.NewError(context, message)) } } // int64 max_items = 13; v13 := compiler.MapValueForKey(m, "maxItems") if v13 != nil { t, ok := compiler.IntForScalarNode(v13) if ok { x.MaxItems = int64(t) } else { message := fmt.Sprintf("has unexpected value for maxItems: %s", compiler.Display(v13)) errors = append(errors, compiler.NewError(context, message)) } } // int64 min_items = 14; v14 := compiler.MapValueForKey(m, "minItems") if v14 != nil { t, ok := compiler.IntForScalarNode(v14) if ok { x.MinItems = int64(t) } else { message := fmt.Sprintf("has unexpected value for minItems: %s", compiler.Display(v14)) errors = append(errors, compiler.NewError(context, message)) } } // bool unique_items = 15; v15 := compiler.MapValueForKey(m, "uniqueItems") if v15 != nil { x.UniqueItems, ok = compiler.BoolForScalarNode(v15) if !ok { message := fmt.Sprintf("has unexpected value for uniqueItems: %s", compiler.Display(v15)) errors = append(errors, compiler.NewError(context, message)) } } // repeated Any enum = 16; v16 := compiler.MapValueForKey(m, "enum") if v16 != nil { // repeated Any x.Enum = make([]*Any, 0) a, ok := compiler.SequenceNodeForNode(v16) if ok { for _, item := range a.Content { y, err := NewAny(item, compiler.NewContext("enum", item, context)) if err != nil { errors = append(errors, err) } x.Enum = append(x.Enum, y) } } } // float multiple_of = 17; v17 := compiler.MapValueForKey(m, "multipleOf") if v17 != nil { v, ok := compiler.FloatForScalarNode(v17) if ok { x.MultipleOf = v } else { message := fmt.Sprintf("has unexpected value for multipleOf: %s", compiler.Display(v17)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny vendor_extension = 18; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewProperties creates an object of type Properties if possible, returning an error if not. func NewProperties(in *yaml.Node, context *compiler.Context) (*Properties, error) { errors := make([]error, 0) x := &Properties{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedSchema additional_properties = 1; // MAP: Schema x.AdditionalProperties = make([]*NamedSchema, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedSchema{} pair.Name = k var err error pair.Value, err = NewSchema(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewQueryParameterSubSchema creates an object of type QueryParameterSubSchema if possible, returning an error if not. func NewQueryParameterSubSchema(in *yaml.Node, context *compiler.Context) (*QueryParameterSubSchema, error) { errors := make([]error, 0) x := &QueryParameterSubSchema{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"allowEmptyValue", "collectionFormat", "default", "description", "enum", "exclusiveMaximum", "exclusiveMinimum", "format", "in", "items", "maxItems", "maxLength", "maximum", "minItems", "minLength", "minimum", "multipleOf", "name", "pattern", "required", "type", "uniqueItems"} allowedPatterns := []*regexp.Regexp{pattern0} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // bool required = 1; v1 := compiler.MapValueForKey(m, "required") if v1 != nil { x.Required, ok = compiler.BoolForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for required: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string in = 2; v2 := compiler.MapValueForKey(m, "in") if v2 != nil { x.In, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for in: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [query] if ok && !compiler.StringArrayContainsValue([]string{"query"}, x.In) { message := fmt.Sprintf("has unexpected value for in: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 3; v3 := compiler.MapValueForKey(m, "description") if v3 != nil { x.Description, ok = compiler.StringForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } // string name = 4; v4 := compiler.MapValueForKey(m, "name") if v4 != nil { x.Name, ok = compiler.StringForScalarNode(v4) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v4)) errors = append(errors, compiler.NewError(context, message)) } } // bool allow_empty_value = 5; v5 := compiler.MapValueForKey(m, "allowEmptyValue") if v5 != nil { x.AllowEmptyValue, ok = compiler.BoolForScalarNode(v5) if !ok { message := fmt.Sprintf("has unexpected value for allowEmptyValue: %s", compiler.Display(v5)) errors = append(errors, compiler.NewError(context, message)) } } // string type = 6; v6 := compiler.MapValueForKey(m, "type") if v6 != nil { x.Type, ok = compiler.StringForScalarNode(v6) if !ok { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v6)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [string number boolean integer array] if ok && !compiler.StringArrayContainsValue([]string{"string", "number", "boolean", "integer", "array"}, x.Type) { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v6)) errors = append(errors, compiler.NewError(context, message)) } } // string format = 7; v7 := compiler.MapValueForKey(m, "format") if v7 != nil { x.Format, ok = compiler.StringForScalarNode(v7) if !ok { message := fmt.Sprintf("has unexpected value for format: %s", compiler.Display(v7)) errors = append(errors, compiler.NewError(context, message)) } } // PrimitivesItems items = 8; v8 := compiler.MapValueForKey(m, "items") if v8 != nil { var err error x.Items, err = NewPrimitivesItems(v8, compiler.NewContext("items", v8, context)) if err != nil { errors = append(errors, err) } } // string collection_format = 9; v9 := compiler.MapValueForKey(m, "collectionFormat") if v9 != nil { x.CollectionFormat, ok = compiler.StringForScalarNode(v9) if !ok { message := fmt.Sprintf("has unexpected value for collectionFormat: %s", compiler.Display(v9)) errors = append(errors, compiler.NewError(context, message)) } // check for valid enum values // [csv ssv tsv pipes multi] if ok && !compiler.StringArrayContainsValue([]string{"csv", "ssv", "tsv", "pipes", "multi"}, x.CollectionFormat) { message := fmt.Sprintf("has unexpected value for collectionFormat: %s", compiler.Display(v9)) errors = append(errors, compiler.NewError(context, message)) } } // Any default = 10; v10 := compiler.MapValueForKey(m, "default") if v10 != nil { var err error x.Default, err = NewAny(v10, compiler.NewContext("default", v10, context)) if err != nil { errors = append(errors, err) } } // float maximum = 11; v11 := compiler.MapValueForKey(m, "maximum") if v11 != nil { v, ok := compiler.FloatForScalarNode(v11) if ok { x.Maximum = v } else { message := fmt.Sprintf("has unexpected value for maximum: %s", compiler.Display(v11)) errors = append(errors, compiler.NewError(context, message)) } } // bool exclusive_maximum = 12; v12 := compiler.MapValueForKey(m, "exclusiveMaximum") if v12 != nil { x.ExclusiveMaximum, ok = compiler.BoolForScalarNode(v12) if !ok { message := fmt.Sprintf("has unexpected value for exclusiveMaximum: %s", compiler.Display(v12)) errors = append(errors, compiler.NewError(context, message)) } } // float minimum = 13; v13 := compiler.MapValueForKey(m, "minimum") if v13 != nil { v, ok := compiler.FloatForScalarNode(v13) if ok { x.Minimum = v } else { message := fmt.Sprintf("has unexpected value for minimum: %s", compiler.Display(v13)) errors = append(errors, compiler.NewError(context, message)) } } // bool exclusive_minimum = 14; v14 := compiler.MapValueForKey(m, "exclusiveMinimum") if v14 != nil { x.ExclusiveMinimum, ok = compiler.BoolForScalarNode(v14) if !ok { message := fmt.Sprintf("has unexpected value for exclusiveMinimum: %s", compiler.Display(v14)) errors = append(errors, compiler.NewError(context, message)) } } // int64 max_length = 15; v15 := compiler.MapValueForKey(m, "maxLength") if v15 != nil { t, ok := compiler.IntForScalarNode(v15) if ok { x.MaxLength = int64(t) } else { message := fmt.Sprintf("has unexpected value for maxLength: %s", compiler.Display(v15)) errors = append(errors, compiler.NewError(context, message)) } } // int64 min_length = 16; v16 := compiler.MapValueForKey(m, "minLength") if v16 != nil { t, ok := compiler.IntForScalarNode(v16) if ok { x.MinLength = int64(t) } else { message := fmt.Sprintf("has unexpected value for minLength: %s", compiler.Display(v16)) errors = append(errors, compiler.NewError(context, message)) } } // string pattern = 17; v17 := compiler.MapValueForKey(m, "pattern") if v17 != nil { x.Pattern, ok = compiler.StringForScalarNode(v17) if !ok { message := fmt.Sprintf("has unexpected value for pattern: %s", compiler.Display(v17)) errors = append(errors, compiler.NewError(context, message)) } } // int64 max_items = 18; v18 := compiler.MapValueForKey(m, "maxItems") if v18 != nil { t, ok := compiler.IntForScalarNode(v18) if ok { x.MaxItems = int64(t) } else { message := fmt.Sprintf("has unexpected value for maxItems: %s", compiler.Display(v18)) errors = append(errors, compiler.NewError(context, message)) } } // int64 min_items = 19; v19 := compiler.MapValueForKey(m, "minItems") if v19 != nil { t, ok := compiler.IntForScalarNode(v19) if ok { x.MinItems = int64(t) } else { message := fmt.Sprintf("has unexpected value for minItems: %s", compiler.Display(v19)) errors = append(errors, compiler.NewError(context, message)) } } // bool unique_items = 20; v20 := compiler.MapValueForKey(m, "uniqueItems") if v20 != nil { x.UniqueItems, ok = compiler.BoolForScalarNode(v20) if !ok { message := fmt.Sprintf("has unexpected value for uniqueItems: %s", compiler.Display(v20)) errors = append(errors, compiler.NewError(context, message)) } } // repeated Any enum = 21; v21 := compiler.MapValueForKey(m, "enum") if v21 != nil { // repeated Any x.Enum = make([]*Any, 0) a, ok := compiler.SequenceNodeForNode(v21) if ok { for _, item := range a.Content { y, err := NewAny(item, compiler.NewContext("enum", item, context)) if err != nil { errors = append(errors, err) } x.Enum = append(x.Enum, y) } } } // float multiple_of = 22; v22 := compiler.MapValueForKey(m, "multipleOf") if v22 != nil { v, ok := compiler.FloatForScalarNode(v22) if ok { x.MultipleOf = v } else { message := fmt.Sprintf("has unexpected value for multipleOf: %s", compiler.Display(v22)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny vendor_extension = 23; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewResponse creates an object of type Response if possible, returning an error if not. func NewResponse(in *yaml.Node, context *compiler.Context) (*Response, error) { errors := make([]error, 0) x := &Response{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"description"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"description", "examples", "headers", "schema"} allowedPatterns := []*regexp.Regexp{pattern0} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string description = 1; v1 := compiler.MapValueForKey(m, "description") if v1 != nil { x.Description, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // SchemaItem schema = 2; v2 := compiler.MapValueForKey(m, "schema") if v2 != nil { var err error x.Schema, err = NewSchemaItem(v2, compiler.NewContext("schema", v2, context)) if err != nil { errors = append(errors, err) } } // Headers headers = 3; v3 := compiler.MapValueForKey(m, "headers") if v3 != nil { var err error x.Headers, err = NewHeaders(v3, compiler.NewContext("headers", v3, context)) if err != nil { errors = append(errors, err) } } // Examples examples = 4; v4 := compiler.MapValueForKey(m, "examples") if v4 != nil { var err error x.Examples, err = NewExamples(v4, compiler.NewContext("examples", v4, context)) if err != nil { errors = append(errors, err) } } // repeated NamedAny vendor_extension = 5; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewResponseDefinitions creates an object of type ResponseDefinitions if possible, returning an error if not. func NewResponseDefinitions(in *yaml.Node, context *compiler.Context) (*ResponseDefinitions, error) { errors := make([]error, 0) x := &ResponseDefinitions{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedResponse additional_properties = 1; // MAP: Response x.AdditionalProperties = make([]*NamedResponse, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedResponse{} pair.Name = k var err error pair.Value, err = NewResponse(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewResponseValue creates an object of type ResponseValue if possible, returning an error if not. func NewResponseValue(in *yaml.Node, context *compiler.Context) (*ResponseValue, error) { errors := make([]error, 0) x := &ResponseValue{} matched := false // Response response = 1; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewResponse(m, compiler.NewContext("response", m, context)) if matchingError == nil { x.Oneof = &ResponseValue_Response{Response: t} matched = true } else { errors = append(errors, matchingError) } } } // JsonReference json_reference = 2; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewJsonReference(m, compiler.NewContext("jsonReference", m, context)) if matchingError == nil { x.Oneof = &ResponseValue_JsonReference{JsonReference: t} matched = true } else { errors = append(errors, matchingError) } } } if matched { // since the oneof matched one of its possibilities, discard any matching errors errors = make([]error, 0) } else { message := fmt.Sprintf("contains an invalid ResponseValue") err := compiler.NewError(context, message) errors = []error{err} } return x, compiler.NewErrorGroupOrNil(errors) } // NewResponses creates an object of type Responses if possible, returning an error if not. func NewResponses(in *yaml.Node, context *compiler.Context) (*Responses, error) { errors := make([]error, 0) x := &Responses{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{} allowedPatterns := []*regexp.Regexp{pattern2, pattern0} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // repeated NamedResponseValue response_code = 1; // MAP: ResponseValue ^([0-9]{3})$|^(default)$ x.ResponseCode = make([]*NamedResponseValue, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if pattern2.MatchString(k) { pair := &NamedResponseValue{} pair.Name = k var err error pair.Value, err = NewResponseValue(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } x.ResponseCode = append(x.ResponseCode, pair) } } } // repeated NamedAny vendor_extension = 2; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewSchema creates an object of type Schema if possible, returning an error if not. func NewSchema(in *yaml.Node, context *compiler.Context) (*Schema, error) { errors := make([]error, 0) x := &Schema{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"$ref", "additionalProperties", "allOf", "default", "description", "discriminator", "enum", "example", "exclusiveMaximum", "exclusiveMinimum", "externalDocs", "format", "items", "maxItems", "maxLength", "maxProperties", "maximum", "minItems", "minLength", "minProperties", "minimum", "multipleOf", "pattern", "properties", "readOnly", "required", "title", "type", "uniqueItems", "xml"} allowedPatterns := []*regexp.Regexp{pattern0} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string _ref = 1; v1 := compiler.MapValueForKey(m, "$ref") if v1 != nil { x.XRef, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for $ref: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string format = 2; v2 := compiler.MapValueForKey(m, "format") if v2 != nil { x.Format, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for format: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // string title = 3; v3 := compiler.MapValueForKey(m, "title") if v3 != nil { x.Title, ok = compiler.StringForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for title: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 4; v4 := compiler.MapValueForKey(m, "description") if v4 != nil { x.Description, ok = compiler.StringForScalarNode(v4) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v4)) errors = append(errors, compiler.NewError(context, message)) } } // Any default = 5; v5 := compiler.MapValueForKey(m, "default") if v5 != nil { var err error x.Default, err = NewAny(v5, compiler.NewContext("default", v5, context)) if err != nil { errors = append(errors, err) } } // float multiple_of = 6; v6 := compiler.MapValueForKey(m, "multipleOf") if v6 != nil { v, ok := compiler.FloatForScalarNode(v6) if ok { x.MultipleOf = v } else { message := fmt.Sprintf("has unexpected value for multipleOf: %s", compiler.Display(v6)) errors = append(errors, compiler.NewError(context, message)) } } // float maximum = 7; v7 := compiler.MapValueForKey(m, "maximum") if v7 != nil { v, ok := compiler.FloatForScalarNode(v7) if ok { x.Maximum = v } else { message := fmt.Sprintf("has unexpected value for maximum: %s", compiler.Display(v7)) errors = append(errors, compiler.NewError(context, message)) } } // bool exclusive_maximum = 8; v8 := compiler.MapValueForKey(m, "exclusiveMaximum") if v8 != nil { x.ExclusiveMaximum, ok = compiler.BoolForScalarNode(v8) if !ok { message := fmt.Sprintf("has unexpected value for exclusiveMaximum: %s", compiler.Display(v8)) errors = append(errors, compiler.NewError(context, message)) } } // float minimum = 9; v9 := compiler.MapValueForKey(m, "minimum") if v9 != nil { v, ok := compiler.FloatForScalarNode(v9) if ok { x.Minimum = v } else { message := fmt.Sprintf("has unexpected value for minimum: %s", compiler.Display(v9)) errors = append(errors, compiler.NewError(context, message)) } } // bool exclusive_minimum = 10; v10 := compiler.MapValueForKey(m, "exclusiveMinimum") if v10 != nil { x.ExclusiveMinimum, ok = compiler.BoolForScalarNode(v10) if !ok { message := fmt.Sprintf("has unexpected value for exclusiveMinimum: %s", compiler.Display(v10)) errors = append(errors, compiler.NewError(context, message)) } } // int64 max_length = 11; v11 := compiler.MapValueForKey(m, "maxLength") if v11 != nil { t, ok := compiler.IntForScalarNode(v11) if ok { x.MaxLength = int64(t) } else { message := fmt.Sprintf("has unexpected value for maxLength: %s", compiler.Display(v11)) errors = append(errors, compiler.NewError(context, message)) } } // int64 min_length = 12; v12 := compiler.MapValueForKey(m, "minLength") if v12 != nil { t, ok := compiler.IntForScalarNode(v12) if ok { x.MinLength = int64(t) } else { message := fmt.Sprintf("has unexpected value for minLength: %s", compiler.Display(v12)) errors = append(errors, compiler.NewError(context, message)) } } // string pattern = 13; v13 := compiler.MapValueForKey(m, "pattern") if v13 != nil { x.Pattern, ok = compiler.StringForScalarNode(v13) if !ok { message := fmt.Sprintf("has unexpected value for pattern: %s", compiler.Display(v13)) errors = append(errors, compiler.NewError(context, message)) } } // int64 max_items = 14; v14 := compiler.MapValueForKey(m, "maxItems") if v14 != nil { t, ok := compiler.IntForScalarNode(v14) if ok { x.MaxItems = int64(t) } else { message := fmt.Sprintf("has unexpected value for maxItems: %s", compiler.Display(v14)) errors = append(errors, compiler.NewError(context, message)) } } // int64 min_items = 15; v15 := compiler.MapValueForKey(m, "minItems") if v15 != nil { t, ok := compiler.IntForScalarNode(v15) if ok { x.MinItems = int64(t) } else { message := fmt.Sprintf("has unexpected value for minItems: %s", compiler.Display(v15)) errors = append(errors, compiler.NewError(context, message)) } } // bool unique_items = 16; v16 := compiler.MapValueForKey(m, "uniqueItems") if v16 != nil { x.UniqueItems, ok = compiler.BoolForScalarNode(v16) if !ok { message := fmt.Sprintf("has unexpected value for uniqueItems: %s", compiler.Display(v16)) errors = append(errors, compiler.NewError(context, message)) } } // int64 max_properties = 17; v17 := compiler.MapValueForKey(m, "maxProperties") if v17 != nil { t, ok := compiler.IntForScalarNode(v17) if ok { x.MaxProperties = int64(t) } else { message := fmt.Sprintf("has unexpected value for maxProperties: %s", compiler.Display(v17)) errors = append(errors, compiler.NewError(context, message)) } } // int64 min_properties = 18; v18 := compiler.MapValueForKey(m, "minProperties") if v18 != nil { t, ok := compiler.IntForScalarNode(v18) if ok { x.MinProperties = int64(t) } else { message := fmt.Sprintf("has unexpected value for minProperties: %s", compiler.Display(v18)) errors = append(errors, compiler.NewError(context, message)) } } // repeated string required = 19; v19 := compiler.MapValueForKey(m, "required") if v19 != nil { v, ok := compiler.SequenceNodeForNode(v19) if ok { x.Required = compiler.StringArrayForSequenceNode(v) } else { message := fmt.Sprintf("has unexpected value for required: %s", compiler.Display(v19)) errors = append(errors, compiler.NewError(context, message)) } } // repeated Any enum = 20; v20 := compiler.MapValueForKey(m, "enum") if v20 != nil { // repeated Any x.Enum = make([]*Any, 0) a, ok := compiler.SequenceNodeForNode(v20) if ok { for _, item := range a.Content { y, err := NewAny(item, compiler.NewContext("enum", item, context)) if err != nil { errors = append(errors, err) } x.Enum = append(x.Enum, y) } } } // AdditionalPropertiesItem additional_properties = 21; v21 := compiler.MapValueForKey(m, "additionalProperties") if v21 != nil { var err error x.AdditionalProperties, err = NewAdditionalPropertiesItem(v21, compiler.NewContext("additionalProperties", v21, context)) if err != nil { errors = append(errors, err) } } // TypeItem type = 22; v22 := compiler.MapValueForKey(m, "type") if v22 != nil { var err error x.Type, err = NewTypeItem(v22, compiler.NewContext("type", v22, context)) if err != nil { errors = append(errors, err) } } // ItemsItem items = 23; v23 := compiler.MapValueForKey(m, "items") if v23 != nil { var err error x.Items, err = NewItemsItem(v23, compiler.NewContext("items", v23, context)) if err != nil { errors = append(errors, err) } } // repeated Schema all_of = 24; v24 := compiler.MapValueForKey(m, "allOf") if v24 != nil { // repeated Schema x.AllOf = make([]*Schema, 0) a, ok := compiler.SequenceNodeForNode(v24) if ok { for _, item := range a.Content { y, err := NewSchema(item, compiler.NewContext("allOf", item, context)) if err != nil { errors = append(errors, err) } x.AllOf = append(x.AllOf, y) } } } // Properties properties = 25; v25 := compiler.MapValueForKey(m, "properties") if v25 != nil { var err error x.Properties, err = NewProperties(v25, compiler.NewContext("properties", v25, context)) if err != nil { errors = append(errors, err) } } // string discriminator = 26; v26 := compiler.MapValueForKey(m, "discriminator") if v26 != nil { x.Discriminator, ok = compiler.StringForScalarNode(v26) if !ok { message := fmt.Sprintf("has unexpected value for discriminator: %s", compiler.Display(v26)) errors = append(errors, compiler.NewError(context, message)) } } // bool read_only = 27; v27 := compiler.MapValueForKey(m, "readOnly") if v27 != nil { x.ReadOnly, ok = compiler.BoolForScalarNode(v27) if !ok { message := fmt.Sprintf("has unexpected value for readOnly: %s", compiler.Display(v27)) errors = append(errors, compiler.NewError(context, message)) } } // Xml xml = 28; v28 := compiler.MapValueForKey(m, "xml") if v28 != nil { var err error x.Xml, err = NewXml(v28, compiler.NewContext("xml", v28, context)) if err != nil { errors = append(errors, err) } } // ExternalDocs external_docs = 29; v29 := compiler.MapValueForKey(m, "externalDocs") if v29 != nil { var err error x.ExternalDocs, err = NewExternalDocs(v29, compiler.NewContext("externalDocs", v29, context)) if err != nil { errors = append(errors, err) } } // Any example = 30; v30 := compiler.MapValueForKey(m, "example") if v30 != nil { var err error x.Example, err = NewAny(v30, compiler.NewContext("example", v30, context)) if err != nil { errors = append(errors, err) } } // repeated NamedAny vendor_extension = 31; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewSchemaItem creates an object of type SchemaItem if possible, returning an error if not. func NewSchemaItem(in *yaml.Node, context *compiler.Context) (*SchemaItem, error) { errors := make([]error, 0) x := &SchemaItem{} matched := false // Schema schema = 1; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewSchema(m, compiler.NewContext("schema", m, context)) if matchingError == nil { x.Oneof = &SchemaItem_Schema{Schema: t} matched = true } else { errors = append(errors, matchingError) } } } // FileSchema file_schema = 2; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewFileSchema(m, compiler.NewContext("fileSchema", m, context)) if matchingError == nil { x.Oneof = &SchemaItem_FileSchema{FileSchema: t} matched = true } else { errors = append(errors, matchingError) } } } if matched { // since the oneof matched one of its possibilities, discard any matching errors errors = make([]error, 0) } else { message := fmt.Sprintf("contains an invalid SchemaItem") err := compiler.NewError(context, message) errors = []error{err} } return x, compiler.NewErrorGroupOrNil(errors) } // NewSecurityDefinitions creates an object of type SecurityDefinitions if possible, returning an error if not. func NewSecurityDefinitions(in *yaml.Node, context *compiler.Context) (*SecurityDefinitions, error) { errors := make([]error, 0) x := &SecurityDefinitions{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedSecurityDefinitionsItem additional_properties = 1; // MAP: SecurityDefinitionsItem x.AdditionalProperties = make([]*NamedSecurityDefinitionsItem, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedSecurityDefinitionsItem{} pair.Name = k var err error pair.Value, err = NewSecurityDefinitionsItem(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewSecurityDefinitionsItem creates an object of type SecurityDefinitionsItem if possible, returning an error if not. func NewSecurityDefinitionsItem(in *yaml.Node, context *compiler.Context) (*SecurityDefinitionsItem, error) { errors := make([]error, 0) x := &SecurityDefinitionsItem{} matched := false // BasicAuthenticationSecurity basic_authentication_security = 1; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewBasicAuthenticationSecurity(m, compiler.NewContext("basicAuthenticationSecurity", m, context)) if matchingError == nil { x.Oneof = &SecurityDefinitionsItem_BasicAuthenticationSecurity{BasicAuthenticationSecurity: t} matched = true } else { errors = append(errors, matchingError) } } } // ApiKeySecurity api_key_security = 2; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewApiKeySecurity(m, compiler.NewContext("apiKeySecurity", m, context)) if matchingError == nil { x.Oneof = &SecurityDefinitionsItem_ApiKeySecurity{ApiKeySecurity: t} matched = true } else { errors = append(errors, matchingError) } } } // Oauth2ImplicitSecurity oauth2_implicit_security = 3; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewOauth2ImplicitSecurity(m, compiler.NewContext("oauth2ImplicitSecurity", m, context)) if matchingError == nil { x.Oneof = &SecurityDefinitionsItem_Oauth2ImplicitSecurity{Oauth2ImplicitSecurity: t} matched = true } else { errors = append(errors, matchingError) } } } // Oauth2PasswordSecurity oauth2_password_security = 4; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewOauth2PasswordSecurity(m, compiler.NewContext("oauth2PasswordSecurity", m, context)) if matchingError == nil { x.Oneof = &SecurityDefinitionsItem_Oauth2PasswordSecurity{Oauth2PasswordSecurity: t} matched = true } else { errors = append(errors, matchingError) } } } // Oauth2ApplicationSecurity oauth2_application_security = 5; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewOauth2ApplicationSecurity(m, compiler.NewContext("oauth2ApplicationSecurity", m, context)) if matchingError == nil { x.Oneof = &SecurityDefinitionsItem_Oauth2ApplicationSecurity{Oauth2ApplicationSecurity: t} matched = true } else { errors = append(errors, matchingError) } } } // Oauth2AccessCodeSecurity oauth2_access_code_security = 6; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewOauth2AccessCodeSecurity(m, compiler.NewContext("oauth2AccessCodeSecurity", m, context)) if matchingError == nil { x.Oneof = &SecurityDefinitionsItem_Oauth2AccessCodeSecurity{Oauth2AccessCodeSecurity: t} matched = true } else { errors = append(errors, matchingError) } } } if matched { // since the oneof matched one of its possibilities, discard any matching errors errors = make([]error, 0) } else { message := fmt.Sprintf("contains an invalid SecurityDefinitionsItem") err := compiler.NewError(context, message) errors = []error{err} } return x, compiler.NewErrorGroupOrNil(errors) } // NewSecurityRequirement creates an object of type SecurityRequirement if possible, returning an error if not. func NewSecurityRequirement(in *yaml.Node, context *compiler.Context) (*SecurityRequirement, error) { errors := make([]error, 0) x := &SecurityRequirement{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedStringArray additional_properties = 1; // MAP: StringArray x.AdditionalProperties = make([]*NamedStringArray, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedStringArray{} pair.Name = k var err error pair.Value, err = NewStringArray(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewStringArray creates an object of type StringArray if possible, returning an error if not. func NewStringArray(in *yaml.Node, context *compiler.Context) (*StringArray, error) { errors := make([]error, 0) x := &StringArray{} x.Value = make([]string, 0) for _, node := range in.Content { s, _ := compiler.StringForScalarNode(node) x.Value = append(x.Value, s) } return x, compiler.NewErrorGroupOrNil(errors) } // NewTag creates an object of type Tag if possible, returning an error if not. func NewTag(in *yaml.Node, context *compiler.Context) (*Tag, error) { errors := make([]error, 0) x := &Tag{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"name"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"description", "externalDocs", "name"} allowedPatterns := []*regexp.Regexp{pattern0} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 2; v2 := compiler.MapValueForKey(m, "description") if v2 != nil { x.Description, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // ExternalDocs external_docs = 3; v3 := compiler.MapValueForKey(m, "externalDocs") if v3 != nil { var err error x.ExternalDocs, err = NewExternalDocs(v3, compiler.NewContext("externalDocs", v3, context)) if err != nil { errors = append(errors, err) } } // repeated NamedAny vendor_extension = 4; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewTypeItem creates an object of type TypeItem if possible, returning an error if not. func NewTypeItem(in *yaml.Node, context *compiler.Context) (*TypeItem, error) { errors := make([]error, 0) x := &TypeItem{} v1 := in switch v1.Kind { case yaml.ScalarNode: x.Value = make([]string, 0) x.Value = append(x.Value, v1.Value) case yaml.SequenceNode: x.Value = make([]string, 0) for _, v := range v1.Content { value := v.Value ok := v.Kind == yaml.ScalarNode if ok { x.Value = append(x.Value, value) } else { message := fmt.Sprintf("has unexpected value for string array element: %+v (%T)", value, value) errors = append(errors, compiler.NewError(context, message)) } } default: message := fmt.Sprintf("has unexpected value for string array: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } return x, compiler.NewErrorGroupOrNil(errors) } // NewVendorExtension creates an object of type VendorExtension if possible, returning an error if not. func NewVendorExtension(in *yaml.Node, context *compiler.Context) (*VendorExtension, error) { errors := make([]error, 0) x := &VendorExtension{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedAny additional_properties = 1; // MAP: Any x.AdditionalProperties = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewXml creates an object of type Xml if possible, returning an error if not. func NewXml(in *yaml.Node, context *compiler.Context) (*Xml, error) { errors := make([]error, 0) x := &Xml{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"attribute", "name", "namespace", "prefix", "wrapped"} allowedPatterns := []*regexp.Regexp{pattern0} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string namespace = 2; v2 := compiler.MapValueForKey(m, "namespace") if v2 != nil { x.Namespace, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for namespace: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // string prefix = 3; v3 := compiler.MapValueForKey(m, "prefix") if v3 != nil { x.Prefix, ok = compiler.StringForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for prefix: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } // bool attribute = 4; v4 := compiler.MapValueForKey(m, "attribute") if v4 != nil { x.Attribute, ok = compiler.BoolForScalarNode(v4) if !ok { message := fmt.Sprintf("has unexpected value for attribute: %s", compiler.Display(v4)) errors = append(errors, compiler.NewError(context, message)) } } // bool wrapped = 5; v5 := compiler.MapValueForKey(m, "wrapped") if v5 != nil { x.Wrapped, ok = compiler.BoolForScalarNode(v5) if !ok { message := fmt.Sprintf("has unexpected value for wrapped: %s", compiler.Display(v5)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny vendor_extension = 6; // MAP: Any ^x- x.VendorExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.VendorExtension = append(x.VendorExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside AdditionalPropertiesItem objects. func (m *AdditionalPropertiesItem) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) { p, ok := m.Oneof.(*AdditionalPropertiesItem_Schema) if ok { _, err := p.Schema.ResolveReferences(root) if err != nil { return nil, err } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Any objects. func (m *Any) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside ApiKeySecurity objects. func (m *ApiKeySecurity) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside BasicAuthenticationSecurity objects. func (m *BasicAuthenticationSecurity) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside BodyParameter objects. func (m *BodyParameter) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Schema != nil { _, err := m.Schema.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Contact objects. func (m *Contact) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Default objects. func (m *Default) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Definitions objects. func (m *Definitions) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Document objects. func (m *Document) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Info != nil { _, err := m.Info.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Paths != nil { _, err := m.Paths.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Definitions != nil { _, err := m.Definitions.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Parameters != nil { _, err := m.Parameters.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Responses != nil { _, err := m.Responses.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.Security { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } if m.SecurityDefinitions != nil { _, err := m.SecurityDefinitions.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.Tags { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } if m.ExternalDocs != nil { _, err := m.ExternalDocs.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Examples objects. func (m *Examples) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside ExternalDocs objects. func (m *ExternalDocs) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside FileSchema objects. func (m *FileSchema) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Default != nil { _, err := m.Default.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.ExternalDocs != nil { _, err := m.ExternalDocs.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Example != nil { _, err := m.Example.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside FormDataParameterSubSchema objects. func (m *FormDataParameterSubSchema) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Items != nil { _, err := m.Items.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Default != nil { _, err := m.Default.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.Enum { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Header objects. func (m *Header) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Items != nil { _, err := m.Items.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Default != nil { _, err := m.Default.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.Enum { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside HeaderParameterSubSchema objects. func (m *HeaderParameterSubSchema) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Items != nil { _, err := m.Items.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Default != nil { _, err := m.Default.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.Enum { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Headers objects. func (m *Headers) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Info objects. func (m *Info) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Contact != nil { _, err := m.Contact.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.License != nil { _, err := m.License.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside ItemsItem objects. func (m *ItemsItem) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.Schema { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside JsonReference objects. func (m *JsonReference) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.XRef != "" { info, err := compiler.ReadInfoForRef(root, m.XRef) if err != nil { return nil, err } if info != nil { replacement, err := NewJsonReference(info, nil) if err == nil { *m = *replacement return m.ResolveReferences(root) } } return info, nil } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside License objects. func (m *License) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NamedAny objects. func (m *NamedAny) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Value != nil { _, err := m.Value.ResolveReferences(root) if err != nil { errors = append(errors, err) } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NamedHeader objects. func (m *NamedHeader) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Value != nil { _, err := m.Value.ResolveReferences(root) if err != nil { errors = append(errors, err) } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NamedParameter objects. func (m *NamedParameter) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Value != nil { _, err := m.Value.ResolveReferences(root) if err != nil { errors = append(errors, err) } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NamedPathItem objects. func (m *NamedPathItem) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Value != nil { _, err := m.Value.ResolveReferences(root) if err != nil { errors = append(errors, err) } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NamedResponse objects. func (m *NamedResponse) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Value != nil { _, err := m.Value.ResolveReferences(root) if err != nil { errors = append(errors, err) } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NamedResponseValue objects. func (m *NamedResponseValue) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Value != nil { _, err := m.Value.ResolveReferences(root) if err != nil { errors = append(errors, err) } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NamedSchema objects. func (m *NamedSchema) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Value != nil { _, err := m.Value.ResolveReferences(root) if err != nil { errors = append(errors, err) } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NamedSecurityDefinitionsItem objects. func (m *NamedSecurityDefinitionsItem) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Value != nil { _, err := m.Value.ResolveReferences(root) if err != nil { errors = append(errors, err) } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NamedString objects. func (m *NamedString) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NamedStringArray objects. func (m *NamedStringArray) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Value != nil { _, err := m.Value.ResolveReferences(root) if err != nil { errors = append(errors, err) } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NonBodyParameter objects. func (m *NonBodyParameter) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) { p, ok := m.Oneof.(*NonBodyParameter_HeaderParameterSubSchema) if ok { _, err := p.HeaderParameterSubSchema.ResolveReferences(root) if err != nil { return nil, err } } } { p, ok := m.Oneof.(*NonBodyParameter_FormDataParameterSubSchema) if ok { _, err := p.FormDataParameterSubSchema.ResolveReferences(root) if err != nil { return nil, err } } } { p, ok := m.Oneof.(*NonBodyParameter_QueryParameterSubSchema) if ok { _, err := p.QueryParameterSubSchema.ResolveReferences(root) if err != nil { return nil, err } } } { p, ok := m.Oneof.(*NonBodyParameter_PathParameterSubSchema) if ok { _, err := p.PathParameterSubSchema.ResolveReferences(root) if err != nil { return nil, err } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Oauth2AccessCodeSecurity objects. func (m *Oauth2AccessCodeSecurity) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Scopes != nil { _, err := m.Scopes.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Oauth2ApplicationSecurity objects. func (m *Oauth2ApplicationSecurity) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Scopes != nil { _, err := m.Scopes.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Oauth2ImplicitSecurity objects. func (m *Oauth2ImplicitSecurity) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Scopes != nil { _, err := m.Scopes.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Oauth2PasswordSecurity objects. func (m *Oauth2PasswordSecurity) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Scopes != nil { _, err := m.Scopes.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Oauth2Scopes objects. func (m *Oauth2Scopes) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Operation objects. func (m *Operation) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.ExternalDocs != nil { _, err := m.ExternalDocs.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.Parameters { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } if m.Responses != nil { _, err := m.Responses.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.Security { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Parameter objects. func (m *Parameter) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) { p, ok := m.Oneof.(*Parameter_BodyParameter) if ok { _, err := p.BodyParameter.ResolveReferences(root) if err != nil { return nil, err } } } { p, ok := m.Oneof.(*Parameter_NonBodyParameter) if ok { _, err := p.NonBodyParameter.ResolveReferences(root) if err != nil { return nil, err } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside ParameterDefinitions objects. func (m *ParameterDefinitions) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside ParametersItem objects. func (m *ParametersItem) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) { p, ok := m.Oneof.(*ParametersItem_Parameter) if ok { _, err := p.Parameter.ResolveReferences(root) if err != nil { return nil, err } } } { p, ok := m.Oneof.(*ParametersItem_JsonReference) if ok { info, err := p.JsonReference.ResolveReferences(root) if err != nil { return nil, err } else if info != nil { n, err := NewParametersItem(info, nil) if err != nil { return nil, err } else if n != nil { *m = *n return nil, nil } } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside PathItem objects. func (m *PathItem) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.XRef != "" { info, err := compiler.ReadInfoForRef(root, m.XRef) if err != nil { return nil, err } if info != nil { replacement, err := NewPathItem(info, nil) if err == nil { *m = *replacement return m.ResolveReferences(root) } } return info, nil } if m.Get != nil { _, err := m.Get.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Put != nil { _, err := m.Put.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Post != nil { _, err := m.Post.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Delete != nil { _, err := m.Delete.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Options != nil { _, err := m.Options.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Head != nil { _, err := m.Head.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Patch != nil { _, err := m.Patch.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.Parameters { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside PathParameterSubSchema objects. func (m *PathParameterSubSchema) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Items != nil { _, err := m.Items.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Default != nil { _, err := m.Default.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.Enum { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Paths objects. func (m *Paths) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } for _, item := range m.Path { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside PrimitivesItems objects. func (m *PrimitivesItems) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Items != nil { _, err := m.Items.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Default != nil { _, err := m.Default.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.Enum { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Properties objects. func (m *Properties) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside QueryParameterSubSchema objects. func (m *QueryParameterSubSchema) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Items != nil { _, err := m.Items.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Default != nil { _, err := m.Default.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.Enum { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Response objects. func (m *Response) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Schema != nil { _, err := m.Schema.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Headers != nil { _, err := m.Headers.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Examples != nil { _, err := m.Examples.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside ResponseDefinitions objects. func (m *ResponseDefinitions) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside ResponseValue objects. func (m *ResponseValue) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) { p, ok := m.Oneof.(*ResponseValue_Response) if ok { _, err := p.Response.ResolveReferences(root) if err != nil { return nil, err } } } { p, ok := m.Oneof.(*ResponseValue_JsonReference) if ok { info, err := p.JsonReference.ResolveReferences(root) if err != nil { return nil, err } else if info != nil { n, err := NewResponseValue(info, nil) if err != nil { return nil, err } else if n != nil { *m = *n return nil, nil } } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Responses objects. func (m *Responses) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.ResponseCode { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Schema objects. func (m *Schema) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.XRef != "" { info, err := compiler.ReadInfoForRef(root, m.XRef) if err != nil { return nil, err } if info != nil { replacement, err := NewSchema(info, nil) if err == nil { *m = *replacement return m.ResolveReferences(root) } } return info, nil } if m.Default != nil { _, err := m.Default.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.Enum { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } if m.AdditionalProperties != nil { _, err := m.AdditionalProperties.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Type != nil { _, err := m.Type.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Items != nil { _, err := m.Items.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.AllOf { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } if m.Properties != nil { _, err := m.Properties.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Xml != nil { _, err := m.Xml.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.ExternalDocs != nil { _, err := m.ExternalDocs.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Example != nil { _, err := m.Example.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside SchemaItem objects. func (m *SchemaItem) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) { p, ok := m.Oneof.(*SchemaItem_Schema) if ok { _, err := p.Schema.ResolveReferences(root) if err != nil { return nil, err } } } { p, ok := m.Oneof.(*SchemaItem_FileSchema) if ok { _, err := p.FileSchema.ResolveReferences(root) if err != nil { return nil, err } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside SecurityDefinitions objects. func (m *SecurityDefinitions) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside SecurityDefinitionsItem objects. func (m *SecurityDefinitionsItem) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) { p, ok := m.Oneof.(*SecurityDefinitionsItem_BasicAuthenticationSecurity) if ok { _, err := p.BasicAuthenticationSecurity.ResolveReferences(root) if err != nil { return nil, err } } } { p, ok := m.Oneof.(*SecurityDefinitionsItem_ApiKeySecurity) if ok { _, err := p.ApiKeySecurity.ResolveReferences(root) if err != nil { return nil, err } } } { p, ok := m.Oneof.(*SecurityDefinitionsItem_Oauth2ImplicitSecurity) if ok { _, err := p.Oauth2ImplicitSecurity.ResolveReferences(root) if err != nil { return nil, err } } } { p, ok := m.Oneof.(*SecurityDefinitionsItem_Oauth2PasswordSecurity) if ok { _, err := p.Oauth2PasswordSecurity.ResolveReferences(root) if err != nil { return nil, err } } } { p, ok := m.Oneof.(*SecurityDefinitionsItem_Oauth2ApplicationSecurity) if ok { _, err := p.Oauth2ApplicationSecurity.ResolveReferences(root) if err != nil { return nil, err } } } { p, ok := m.Oneof.(*SecurityDefinitionsItem_Oauth2AccessCodeSecurity) if ok { _, err := p.Oauth2AccessCodeSecurity.ResolveReferences(root) if err != nil { return nil, err } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside SecurityRequirement objects. func (m *SecurityRequirement) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside StringArray objects. func (m *StringArray) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Tag objects. func (m *Tag) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.ExternalDocs != nil { _, err := m.ExternalDocs.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside TypeItem objects. func (m *TypeItem) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside VendorExtension objects. func (m *VendorExtension) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Xml objects. func (m *Xml) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.VendorExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ToRawInfo returns a description of AdditionalPropertiesItem suitable for JSON or YAML export. func (m *AdditionalPropertiesItem) ToRawInfo() *yaml.Node { // ONE OF WRAPPER // AdditionalPropertiesItem // {Name:schema Type:Schema StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v0 := m.GetSchema() if v0 != nil { return v0.ToRawInfo() } // {Name:boolean Type:bool StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} if v1, ok := m.GetOneof().(*AdditionalPropertiesItem_Boolean); ok { return compiler.NewScalarNodeForBool(v1.Boolean) } return compiler.NewNullNode() } // ToRawInfo returns a description of Any suitable for JSON or YAML export. func (m *Any) ToRawInfo() *yaml.Node { var err error var node yaml.Node err = yaml.Unmarshal([]byte(m.Yaml), &node) if err == nil { if node.Kind == yaml.DocumentNode { return node.Content[0] } return &node } return compiler.NewNullNode() } // ToRawInfo returns a description of ApiKeySecurity suitable for JSON or YAML export. func (m *ApiKeySecurity) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("type")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Type)) // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("in")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.In)) if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of BasicAuthenticationSecurity suitable for JSON or YAML export. func (m *BasicAuthenticationSecurity) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("type")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Type)) if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of BodyParameter suitable for JSON or YAML export. func (m *BodyParameter) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("in")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.In)) if m.Required != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("required")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Required)) } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("schema")) info.Content = append(info.Content, m.Schema.ToRawInfo()) if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Contact suitable for JSON or YAML export. func (m *Contact) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } if m.Url != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("url")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Url)) } if m.Email != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("email")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Email)) } if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Default suitable for JSON or YAML export. func (m *Default) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AdditionalProperties != nil { for _, item := range m.AdditionalProperties { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Definitions suitable for JSON or YAML export. func (m *Definitions) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AdditionalProperties != nil { for _, item := range m.AdditionalProperties { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Document suitable for JSON or YAML export. func (m *Document) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("swagger")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Swagger)) // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("info")) info.Content = append(info.Content, m.Info.ToRawInfo()) if m.Host != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("host")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Host)) } if m.BasePath != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("basePath")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.BasePath)) } if len(m.Schemes) != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("schemes")) info.Content = append(info.Content, compiler.NewSequenceNodeForStringArray(m.Schemes)) } if len(m.Consumes) != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("consumes")) info.Content = append(info.Content, compiler.NewSequenceNodeForStringArray(m.Consumes)) } if len(m.Produces) != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("produces")) info.Content = append(info.Content, compiler.NewSequenceNodeForStringArray(m.Produces)) } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("paths")) info.Content = append(info.Content, m.Paths.ToRawInfo()) if m.Definitions != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("definitions")) info.Content = append(info.Content, m.Definitions.ToRawInfo()) } if m.Parameters != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("parameters")) info.Content = append(info.Content, m.Parameters.ToRawInfo()) } if m.Responses != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("responses")) info.Content = append(info.Content, m.Responses.ToRawInfo()) } if len(m.Security) != 0 { items := compiler.NewSequenceNode() for _, item := range m.Security { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("security")) info.Content = append(info.Content, items) } if m.SecurityDefinitions != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("securityDefinitions")) info.Content = append(info.Content, m.SecurityDefinitions.ToRawInfo()) } if len(m.Tags) != 0 { items := compiler.NewSequenceNode() for _, item := range m.Tags { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("tags")) info.Content = append(info.Content, items) } if m.ExternalDocs != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("externalDocs")) info.Content = append(info.Content, m.ExternalDocs.ToRawInfo()) } if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Examples suitable for JSON or YAML export. func (m *Examples) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AdditionalProperties != nil { for _, item := range m.AdditionalProperties { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of ExternalDocs suitable for JSON or YAML export. func (m *ExternalDocs) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("url")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Url)) if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of FileSchema suitable for JSON or YAML export. func (m *FileSchema) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Format != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("format")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Format)) } if m.Title != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("title")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Title)) } if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.Default != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("default")) info.Content = append(info.Content, m.Default.ToRawInfo()) } if len(m.Required) != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("required")) info.Content = append(info.Content, compiler.NewSequenceNodeForStringArray(m.Required)) } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("type")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Type)) if m.ReadOnly != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("readOnly")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ReadOnly)) } if m.ExternalDocs != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("externalDocs")) info.Content = append(info.Content, m.ExternalDocs.ToRawInfo()) } if m.Example != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("example")) info.Content = append(info.Content, m.Example.ToRawInfo()) } if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of FormDataParameterSubSchema suitable for JSON or YAML export. func (m *FormDataParameterSubSchema) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Required != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("required")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Required)) } if m.In != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("in")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.In)) } if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } if m.AllowEmptyValue != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("allowEmptyValue")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.AllowEmptyValue)) } if m.Type != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("type")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Type)) } if m.Format != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("format")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Format)) } if m.Items != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("items")) info.Content = append(info.Content, m.Items.ToRawInfo()) } if m.CollectionFormat != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("collectionFormat")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.CollectionFormat)) } if m.Default != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("default")) info.Content = append(info.Content, m.Default.ToRawInfo()) } if m.Maximum != 0.0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("maximum")) info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Maximum)) } if m.ExclusiveMaximum != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMaximum")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMaximum)) } if m.Minimum != 0.0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("minimum")) info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Minimum)) } if m.ExclusiveMinimum != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMinimum")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMinimum)) } if m.MaxLength != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("maxLength")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MaxLength)) } if m.MinLength != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("minLength")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MinLength)) } if m.Pattern != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("pattern")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Pattern)) } if m.MaxItems != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("maxItems")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MaxItems)) } if m.MinItems != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("minItems")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MinItems)) } if m.UniqueItems != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("uniqueItems")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.UniqueItems)) } if len(m.Enum) != 0 { items := compiler.NewSequenceNode() for _, item := range m.Enum { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("enum")) info.Content = append(info.Content, items) } if m.MultipleOf != 0.0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("multipleOf")) info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.MultipleOf)) } if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Header suitable for JSON or YAML export. func (m *Header) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("type")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Type)) if m.Format != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("format")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Format)) } if m.Items != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("items")) info.Content = append(info.Content, m.Items.ToRawInfo()) } if m.CollectionFormat != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("collectionFormat")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.CollectionFormat)) } if m.Default != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("default")) info.Content = append(info.Content, m.Default.ToRawInfo()) } if m.Maximum != 0.0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("maximum")) info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Maximum)) } if m.ExclusiveMaximum != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMaximum")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMaximum)) } if m.Minimum != 0.0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("minimum")) info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Minimum)) } if m.ExclusiveMinimum != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMinimum")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMinimum)) } if m.MaxLength != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("maxLength")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MaxLength)) } if m.MinLength != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("minLength")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MinLength)) } if m.Pattern != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("pattern")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Pattern)) } if m.MaxItems != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("maxItems")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MaxItems)) } if m.MinItems != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("minItems")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MinItems)) } if m.UniqueItems != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("uniqueItems")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.UniqueItems)) } if len(m.Enum) != 0 { items := compiler.NewSequenceNode() for _, item := range m.Enum { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("enum")) info.Content = append(info.Content, items) } if m.MultipleOf != 0.0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("multipleOf")) info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.MultipleOf)) } if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of HeaderParameterSubSchema suitable for JSON or YAML export. func (m *HeaderParameterSubSchema) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Required != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("required")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Required)) } if m.In != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("in")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.In)) } if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } if m.Type != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("type")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Type)) } if m.Format != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("format")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Format)) } if m.Items != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("items")) info.Content = append(info.Content, m.Items.ToRawInfo()) } if m.CollectionFormat != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("collectionFormat")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.CollectionFormat)) } if m.Default != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("default")) info.Content = append(info.Content, m.Default.ToRawInfo()) } if m.Maximum != 0.0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("maximum")) info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Maximum)) } if m.ExclusiveMaximum != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMaximum")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMaximum)) } if m.Minimum != 0.0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("minimum")) info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Minimum)) } if m.ExclusiveMinimum != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMinimum")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMinimum)) } if m.MaxLength != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("maxLength")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MaxLength)) } if m.MinLength != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("minLength")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MinLength)) } if m.Pattern != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("pattern")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Pattern)) } if m.MaxItems != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("maxItems")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MaxItems)) } if m.MinItems != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("minItems")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MinItems)) } if m.UniqueItems != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("uniqueItems")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.UniqueItems)) } if len(m.Enum) != 0 { items := compiler.NewSequenceNode() for _, item := range m.Enum { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("enum")) info.Content = append(info.Content, items) } if m.MultipleOf != 0.0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("multipleOf")) info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.MultipleOf)) } if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Headers suitable for JSON or YAML export. func (m *Headers) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AdditionalProperties != nil { for _, item := range m.AdditionalProperties { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Info suitable for JSON or YAML export. func (m *Info) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("title")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Title)) // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("version")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Version)) if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.TermsOfService != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("termsOfService")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.TermsOfService)) } if m.Contact != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("contact")) info.Content = append(info.Content, m.Contact.ToRawInfo()) } if m.License != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("license")) info.Content = append(info.Content, m.License.ToRawInfo()) } if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of ItemsItem suitable for JSON or YAML export. func (m *ItemsItem) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if len(m.Schema) != 0 { items := compiler.NewSequenceNode() for _, item := range m.Schema { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("schema")) info.Content = append(info.Content, items) } return info } // ToRawInfo returns a description of JsonReference suitable for JSON or YAML export. func (m *JsonReference) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("$ref")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.XRef)) if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } return info } // ToRawInfo returns a description of License suitable for JSON or YAML export. func (m *License) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) if m.Url != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("url")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Url)) } if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of NamedAny suitable for JSON or YAML export. func (m *NamedAny) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } if m.Value != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("value")) info.Content = append(info.Content, m.Value.ToRawInfo()) } return info } // ToRawInfo returns a description of NamedHeader suitable for JSON or YAML export. func (m *NamedHeader) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } // &{Name:value Type:Header StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:Mapped value} return info } // ToRawInfo returns a description of NamedParameter suitable for JSON or YAML export. func (m *NamedParameter) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } // &{Name:value Type:Parameter StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:Mapped value} return info } // ToRawInfo returns a description of NamedPathItem suitable for JSON or YAML export. func (m *NamedPathItem) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } // &{Name:value Type:PathItem StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:Mapped value} return info } // ToRawInfo returns a description of NamedResponse suitable for JSON or YAML export. func (m *NamedResponse) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } // &{Name:value Type:Response StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:Mapped value} return info } // ToRawInfo returns a description of NamedResponseValue suitable for JSON or YAML export. func (m *NamedResponseValue) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } // &{Name:value Type:ResponseValue StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:Mapped value} return info } // ToRawInfo returns a description of NamedSchema suitable for JSON or YAML export. func (m *NamedSchema) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } // &{Name:value Type:Schema StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:Mapped value} return info } // ToRawInfo returns a description of NamedSecurityDefinitionsItem suitable for JSON or YAML export. func (m *NamedSecurityDefinitionsItem) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } // &{Name:value Type:SecurityDefinitionsItem StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:Mapped value} return info } // ToRawInfo returns a description of NamedString suitable for JSON or YAML export. func (m *NamedString) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } if m.Value != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("value")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Value)) } return info } // ToRawInfo returns a description of NamedStringArray suitable for JSON or YAML export. func (m *NamedStringArray) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } // &{Name:value Type:StringArray StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:Mapped value} return info } // ToRawInfo returns a description of NonBodyParameter suitable for JSON or YAML export. func (m *NonBodyParameter) ToRawInfo() *yaml.Node { // ONE OF WRAPPER // NonBodyParameter // {Name:headerParameterSubSchema Type:HeaderParameterSubSchema StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v0 := m.GetHeaderParameterSubSchema() if v0 != nil { return v0.ToRawInfo() } // {Name:formDataParameterSubSchema Type:FormDataParameterSubSchema StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v1 := m.GetFormDataParameterSubSchema() if v1 != nil { return v1.ToRawInfo() } // {Name:queryParameterSubSchema Type:QueryParameterSubSchema StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v2 := m.GetQueryParameterSubSchema() if v2 != nil { return v2.ToRawInfo() } // {Name:pathParameterSubSchema Type:PathParameterSubSchema StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v3 := m.GetPathParameterSubSchema() if v3 != nil { return v3.ToRawInfo() } return compiler.NewNullNode() } // ToRawInfo returns a description of Oauth2AccessCodeSecurity suitable for JSON or YAML export. func (m *Oauth2AccessCodeSecurity) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("type")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Type)) // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("flow")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Flow)) if m.Scopes != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("scopes")) info.Content = append(info.Content, m.Scopes.ToRawInfo()) } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("authorizationUrl")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.AuthorizationUrl)) // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("tokenUrl")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.TokenUrl)) if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Oauth2ApplicationSecurity suitable for JSON or YAML export. func (m *Oauth2ApplicationSecurity) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("type")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Type)) // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("flow")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Flow)) if m.Scopes != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("scopes")) info.Content = append(info.Content, m.Scopes.ToRawInfo()) } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("tokenUrl")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.TokenUrl)) if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Oauth2ImplicitSecurity suitable for JSON or YAML export. func (m *Oauth2ImplicitSecurity) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("type")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Type)) // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("flow")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Flow)) if m.Scopes != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("scopes")) info.Content = append(info.Content, m.Scopes.ToRawInfo()) } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("authorizationUrl")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.AuthorizationUrl)) if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Oauth2PasswordSecurity suitable for JSON or YAML export. func (m *Oauth2PasswordSecurity) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("type")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Type)) // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("flow")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Flow)) if m.Scopes != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("scopes")) info.Content = append(info.Content, m.Scopes.ToRawInfo()) } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("tokenUrl")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.TokenUrl)) if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Oauth2Scopes suitable for JSON or YAML export. func (m *Oauth2Scopes) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } // &{Name:additionalProperties Type:NamedString StringEnumValues:[] MapType:string Repeated:true Pattern: Implicit:true Description:} return info } // ToRawInfo returns a description of Operation suitable for JSON or YAML export. func (m *Operation) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if len(m.Tags) != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("tags")) info.Content = append(info.Content, compiler.NewSequenceNodeForStringArray(m.Tags)) } if m.Summary != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("summary")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Summary)) } if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.ExternalDocs != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("externalDocs")) info.Content = append(info.Content, m.ExternalDocs.ToRawInfo()) } if m.OperationId != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("operationId")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.OperationId)) } if len(m.Produces) != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("produces")) info.Content = append(info.Content, compiler.NewSequenceNodeForStringArray(m.Produces)) } if len(m.Consumes) != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("consumes")) info.Content = append(info.Content, compiler.NewSequenceNodeForStringArray(m.Consumes)) } if len(m.Parameters) != 0 { items := compiler.NewSequenceNode() for _, item := range m.Parameters { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("parameters")) info.Content = append(info.Content, items) } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("responses")) info.Content = append(info.Content, m.Responses.ToRawInfo()) if len(m.Schemes) != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("schemes")) info.Content = append(info.Content, compiler.NewSequenceNodeForStringArray(m.Schemes)) } if m.Deprecated != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("deprecated")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Deprecated)) } if len(m.Security) != 0 { items := compiler.NewSequenceNode() for _, item := range m.Security { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("security")) info.Content = append(info.Content, items) } if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Parameter suitable for JSON or YAML export. func (m *Parameter) ToRawInfo() *yaml.Node { // ONE OF WRAPPER // Parameter // {Name:bodyParameter Type:BodyParameter StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v0 := m.GetBodyParameter() if v0 != nil { return v0.ToRawInfo() } // {Name:nonBodyParameter Type:NonBodyParameter StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v1 := m.GetNonBodyParameter() if v1 != nil { return v1.ToRawInfo() } return compiler.NewNullNode() } // ToRawInfo returns a description of ParameterDefinitions suitable for JSON or YAML export. func (m *ParameterDefinitions) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AdditionalProperties != nil { for _, item := range m.AdditionalProperties { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of ParametersItem suitable for JSON or YAML export. func (m *ParametersItem) ToRawInfo() *yaml.Node { // ONE OF WRAPPER // ParametersItem // {Name:parameter Type:Parameter StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v0 := m.GetParameter() if v0 != nil { return v0.ToRawInfo() } // {Name:jsonReference Type:JsonReference StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v1 := m.GetJsonReference() if v1 != nil { return v1.ToRawInfo() } return compiler.NewNullNode() } // ToRawInfo returns a description of PathItem suitable for JSON or YAML export. func (m *PathItem) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.XRef != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("$ref")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.XRef)) } if m.Get != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("get")) info.Content = append(info.Content, m.Get.ToRawInfo()) } if m.Put != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("put")) info.Content = append(info.Content, m.Put.ToRawInfo()) } if m.Post != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("post")) info.Content = append(info.Content, m.Post.ToRawInfo()) } if m.Delete != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("delete")) info.Content = append(info.Content, m.Delete.ToRawInfo()) } if m.Options != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("options")) info.Content = append(info.Content, m.Options.ToRawInfo()) } if m.Head != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("head")) info.Content = append(info.Content, m.Head.ToRawInfo()) } if m.Patch != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("patch")) info.Content = append(info.Content, m.Patch.ToRawInfo()) } if len(m.Parameters) != 0 { items := compiler.NewSequenceNode() for _, item := range m.Parameters { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("parameters")) info.Content = append(info.Content, items) } if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of PathParameterSubSchema suitable for JSON or YAML export. func (m *PathParameterSubSchema) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("required")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Required)) if m.In != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("in")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.In)) } if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } if m.Type != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("type")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Type)) } if m.Format != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("format")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Format)) } if m.Items != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("items")) info.Content = append(info.Content, m.Items.ToRawInfo()) } if m.CollectionFormat != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("collectionFormat")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.CollectionFormat)) } if m.Default != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("default")) info.Content = append(info.Content, m.Default.ToRawInfo()) } if m.Maximum != 0.0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("maximum")) info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Maximum)) } if m.ExclusiveMaximum != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMaximum")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMaximum)) } if m.Minimum != 0.0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("minimum")) info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Minimum)) } if m.ExclusiveMinimum != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMinimum")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMinimum)) } if m.MaxLength != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("maxLength")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MaxLength)) } if m.MinLength != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("minLength")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MinLength)) } if m.Pattern != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("pattern")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Pattern)) } if m.MaxItems != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("maxItems")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MaxItems)) } if m.MinItems != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("minItems")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MinItems)) } if m.UniqueItems != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("uniqueItems")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.UniqueItems)) } if len(m.Enum) != 0 { items := compiler.NewSequenceNode() for _, item := range m.Enum { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("enum")) info.Content = append(info.Content, items) } if m.MultipleOf != 0.0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("multipleOf")) info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.MultipleOf)) } if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Paths suitable for JSON or YAML export. func (m *Paths) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } if m.Path != nil { for _, item := range m.Path { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of PrimitivesItems suitable for JSON or YAML export. func (m *PrimitivesItems) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Type != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("type")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Type)) } if m.Format != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("format")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Format)) } if m.Items != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("items")) info.Content = append(info.Content, m.Items.ToRawInfo()) } if m.CollectionFormat != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("collectionFormat")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.CollectionFormat)) } if m.Default != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("default")) info.Content = append(info.Content, m.Default.ToRawInfo()) } if m.Maximum != 0.0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("maximum")) info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Maximum)) } if m.ExclusiveMaximum != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMaximum")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMaximum)) } if m.Minimum != 0.0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("minimum")) info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Minimum)) } if m.ExclusiveMinimum != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMinimum")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMinimum)) } if m.MaxLength != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("maxLength")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MaxLength)) } if m.MinLength != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("minLength")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MinLength)) } if m.Pattern != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("pattern")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Pattern)) } if m.MaxItems != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("maxItems")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MaxItems)) } if m.MinItems != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("minItems")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MinItems)) } if m.UniqueItems != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("uniqueItems")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.UniqueItems)) } if len(m.Enum) != 0 { items := compiler.NewSequenceNode() for _, item := range m.Enum { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("enum")) info.Content = append(info.Content, items) } if m.MultipleOf != 0.0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("multipleOf")) info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.MultipleOf)) } if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Properties suitable for JSON or YAML export. func (m *Properties) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AdditionalProperties != nil { for _, item := range m.AdditionalProperties { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of QueryParameterSubSchema suitable for JSON or YAML export. func (m *QueryParameterSubSchema) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Required != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("required")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Required)) } if m.In != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("in")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.In)) } if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } if m.AllowEmptyValue != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("allowEmptyValue")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.AllowEmptyValue)) } if m.Type != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("type")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Type)) } if m.Format != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("format")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Format)) } if m.Items != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("items")) info.Content = append(info.Content, m.Items.ToRawInfo()) } if m.CollectionFormat != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("collectionFormat")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.CollectionFormat)) } if m.Default != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("default")) info.Content = append(info.Content, m.Default.ToRawInfo()) } if m.Maximum != 0.0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("maximum")) info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Maximum)) } if m.ExclusiveMaximum != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMaximum")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMaximum)) } if m.Minimum != 0.0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("minimum")) info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Minimum)) } if m.ExclusiveMinimum != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMinimum")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMinimum)) } if m.MaxLength != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("maxLength")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MaxLength)) } if m.MinLength != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("minLength")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MinLength)) } if m.Pattern != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("pattern")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Pattern)) } if m.MaxItems != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("maxItems")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MaxItems)) } if m.MinItems != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("minItems")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MinItems)) } if m.UniqueItems != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("uniqueItems")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.UniqueItems)) } if len(m.Enum) != 0 { items := compiler.NewSequenceNode() for _, item := range m.Enum { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("enum")) info.Content = append(info.Content, items) } if m.MultipleOf != 0.0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("multipleOf")) info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.MultipleOf)) } if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Response suitable for JSON or YAML export. func (m *Response) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) if m.Schema != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("schema")) info.Content = append(info.Content, m.Schema.ToRawInfo()) } if m.Headers != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("headers")) info.Content = append(info.Content, m.Headers.ToRawInfo()) } if m.Examples != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("examples")) info.Content = append(info.Content, m.Examples.ToRawInfo()) } if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of ResponseDefinitions suitable for JSON or YAML export. func (m *ResponseDefinitions) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AdditionalProperties != nil { for _, item := range m.AdditionalProperties { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of ResponseValue suitable for JSON or YAML export. func (m *ResponseValue) ToRawInfo() *yaml.Node { // ONE OF WRAPPER // ResponseValue // {Name:response Type:Response StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v0 := m.GetResponse() if v0 != nil { return v0.ToRawInfo() } // {Name:jsonReference Type:JsonReference StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v1 := m.GetJsonReference() if v1 != nil { return v1.ToRawInfo() } return compiler.NewNullNode() } // ToRawInfo returns a description of Responses suitable for JSON or YAML export. func (m *Responses) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.ResponseCode != nil { for _, item := range m.ResponseCode { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Schema suitable for JSON or YAML export. func (m *Schema) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.XRef != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("$ref")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.XRef)) } if m.Format != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("format")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Format)) } if m.Title != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("title")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Title)) } if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.Default != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("default")) info.Content = append(info.Content, m.Default.ToRawInfo()) } if m.MultipleOf != 0.0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("multipleOf")) info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.MultipleOf)) } if m.Maximum != 0.0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("maximum")) info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Maximum)) } if m.ExclusiveMaximum != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMaximum")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMaximum)) } if m.Minimum != 0.0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("minimum")) info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Minimum)) } if m.ExclusiveMinimum != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMinimum")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMinimum)) } if m.MaxLength != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("maxLength")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MaxLength)) } if m.MinLength != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("minLength")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MinLength)) } if m.Pattern != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("pattern")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Pattern)) } if m.MaxItems != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("maxItems")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MaxItems)) } if m.MinItems != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("minItems")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MinItems)) } if m.UniqueItems != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("uniqueItems")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.UniqueItems)) } if m.MaxProperties != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("maxProperties")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MaxProperties)) } if m.MinProperties != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("minProperties")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MinProperties)) } if len(m.Required) != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("required")) info.Content = append(info.Content, compiler.NewSequenceNodeForStringArray(m.Required)) } if len(m.Enum) != 0 { items := compiler.NewSequenceNode() for _, item := range m.Enum { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("enum")) info.Content = append(info.Content, items) } if m.AdditionalProperties != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("additionalProperties")) info.Content = append(info.Content, m.AdditionalProperties.ToRawInfo()) } if m.Type != nil { if len(m.Type.Value) == 1 { info.Content = append(info.Content, compiler.NewScalarNodeForString("type")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Type.Value[0])) } else { info.Content = append(info.Content, compiler.NewScalarNodeForString("type")) info.Content = append(info.Content, compiler.NewSequenceNodeForStringArray(m.Type.Value)) } } if m.Items != nil { items := compiler.NewSequenceNode() for _, item := range m.Items.Schema { items.Content = append(items.Content, item.ToRawInfo()) } if len(items.Content) == 1 { items = items.Content[0] } info.Content = append(info.Content, compiler.NewScalarNodeForString("items")) info.Content = append(info.Content, items) } if len(m.AllOf) != 0 { items := compiler.NewSequenceNode() for _, item := range m.AllOf { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("allOf")) info.Content = append(info.Content, items) } if m.Properties != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("properties")) info.Content = append(info.Content, m.Properties.ToRawInfo()) } if m.Discriminator != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("discriminator")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Discriminator)) } if m.ReadOnly != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("readOnly")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ReadOnly)) } if m.Xml != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("xml")) info.Content = append(info.Content, m.Xml.ToRawInfo()) } if m.ExternalDocs != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("externalDocs")) info.Content = append(info.Content, m.ExternalDocs.ToRawInfo()) } if m.Example != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("example")) info.Content = append(info.Content, m.Example.ToRawInfo()) } if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of SchemaItem suitable for JSON or YAML export. func (m *SchemaItem) ToRawInfo() *yaml.Node { // ONE OF WRAPPER // SchemaItem // {Name:schema Type:Schema StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v0 := m.GetSchema() if v0 != nil { return v0.ToRawInfo() } // {Name:fileSchema Type:FileSchema StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v1 := m.GetFileSchema() if v1 != nil { return v1.ToRawInfo() } return compiler.NewNullNode() } // ToRawInfo returns a description of SecurityDefinitions suitable for JSON or YAML export. func (m *SecurityDefinitions) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AdditionalProperties != nil { for _, item := range m.AdditionalProperties { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of SecurityDefinitionsItem suitable for JSON or YAML export. func (m *SecurityDefinitionsItem) ToRawInfo() *yaml.Node { // ONE OF WRAPPER // SecurityDefinitionsItem // {Name:basicAuthenticationSecurity Type:BasicAuthenticationSecurity StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v0 := m.GetBasicAuthenticationSecurity() if v0 != nil { return v0.ToRawInfo() } // {Name:apiKeySecurity Type:ApiKeySecurity StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v1 := m.GetApiKeySecurity() if v1 != nil { return v1.ToRawInfo() } // {Name:oauth2ImplicitSecurity Type:Oauth2ImplicitSecurity StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v2 := m.GetOauth2ImplicitSecurity() if v2 != nil { return v2.ToRawInfo() } // {Name:oauth2PasswordSecurity Type:Oauth2PasswordSecurity StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v3 := m.GetOauth2PasswordSecurity() if v3 != nil { return v3.ToRawInfo() } // {Name:oauth2ApplicationSecurity Type:Oauth2ApplicationSecurity StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v4 := m.GetOauth2ApplicationSecurity() if v4 != nil { return v4.ToRawInfo() } // {Name:oauth2AccessCodeSecurity Type:Oauth2AccessCodeSecurity StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v5 := m.GetOauth2AccessCodeSecurity() if v5 != nil { return v5.ToRawInfo() } return compiler.NewNullNode() } // ToRawInfo returns a description of SecurityRequirement suitable for JSON or YAML export. func (m *SecurityRequirement) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AdditionalProperties != nil { for _, item := range m.AdditionalProperties { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of StringArray suitable for JSON or YAML export. func (m *StringArray) ToRawInfo() *yaml.Node { return compiler.NewSequenceNodeForStringArray(m.Value) } // ToRawInfo returns a description of Tag suitable for JSON or YAML export. func (m *Tag) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.ExternalDocs != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("externalDocs")) info.Content = append(info.Content, m.ExternalDocs.ToRawInfo()) } if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of TypeItem suitable for JSON or YAML export. func (m *TypeItem) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if len(m.Value) != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("value")) info.Content = append(info.Content, compiler.NewSequenceNodeForStringArray(m.Value)) } return info } // ToRawInfo returns a description of VendorExtension suitable for JSON or YAML export. func (m *VendorExtension) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AdditionalProperties != nil { for _, item := range m.AdditionalProperties { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Xml suitable for JSON or YAML export. func (m *Xml) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } if m.Namespace != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("namespace")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Namespace)) } if m.Prefix != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("prefix")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Prefix)) } if m.Attribute != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("attribute")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Attribute)) } if m.Wrapped != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("wrapped")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Wrapped)) } if m.VendorExtension != nil { for _, item := range m.VendorExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } var ( pattern0 = regexp.MustCompile("^x-") pattern1 = regexp.MustCompile("^/") pattern2 = regexp.MustCompile("^([0-9]{3})$|^(default)$") ) ================================================ FILE: vendor/github.com/google/gnostic/openapiv2/OpenAPIv2.pb.go ================================================ // Copyright 2020 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // THIS FILE IS AUTOMATICALLY GENERATED. // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 // protoc v3.18.1 // source: openapiv2/OpenAPIv2.proto package openapi_v2 import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type AdditionalPropertiesItem struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Types that are assignable to Oneof: // *AdditionalPropertiesItem_Schema // *AdditionalPropertiesItem_Boolean Oneof isAdditionalPropertiesItem_Oneof `protobuf_oneof:"oneof"` } func (x *AdditionalPropertiesItem) Reset() { *x = AdditionalPropertiesItem{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *AdditionalPropertiesItem) String() string { return protoimpl.X.MessageStringOf(x) } func (*AdditionalPropertiesItem) ProtoMessage() {} func (x *AdditionalPropertiesItem) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AdditionalPropertiesItem.ProtoReflect.Descriptor instead. func (*AdditionalPropertiesItem) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{0} } func (m *AdditionalPropertiesItem) GetOneof() isAdditionalPropertiesItem_Oneof { if m != nil { return m.Oneof } return nil } func (x *AdditionalPropertiesItem) GetSchema() *Schema { if x, ok := x.GetOneof().(*AdditionalPropertiesItem_Schema); ok { return x.Schema } return nil } func (x *AdditionalPropertiesItem) GetBoolean() bool { if x, ok := x.GetOneof().(*AdditionalPropertiesItem_Boolean); ok { return x.Boolean } return false } type isAdditionalPropertiesItem_Oneof interface { isAdditionalPropertiesItem_Oneof() } type AdditionalPropertiesItem_Schema struct { Schema *Schema `protobuf:"bytes,1,opt,name=schema,proto3,oneof"` } type AdditionalPropertiesItem_Boolean struct { Boolean bool `protobuf:"varint,2,opt,name=boolean,proto3,oneof"` } func (*AdditionalPropertiesItem_Schema) isAdditionalPropertiesItem_Oneof() {} func (*AdditionalPropertiesItem_Boolean) isAdditionalPropertiesItem_Oneof() {} type Any struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Value *anypb.Any `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` Yaml string `protobuf:"bytes,2,opt,name=yaml,proto3" json:"yaml,omitempty"` } func (x *Any) Reset() { *x = Any{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Any) String() string { return protoimpl.X.MessageStringOf(x) } func (*Any) ProtoMessage() {} func (x *Any) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Any.ProtoReflect.Descriptor instead. func (*Any) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{1} } func (x *Any) GetValue() *anypb.Any { if x != nil { return x.Value } return nil } func (x *Any) GetYaml() string { if x != nil { return x.Yaml } return "" } type ApiKeySecurity struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` In string `protobuf:"bytes,3,opt,name=in,proto3" json:"in,omitempty"` Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` VendorExtension []*NamedAny `protobuf:"bytes,5,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` } func (x *ApiKeySecurity) Reset() { *x = ApiKeySecurity{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ApiKeySecurity) String() string { return protoimpl.X.MessageStringOf(x) } func (*ApiKeySecurity) ProtoMessage() {} func (x *ApiKeySecurity) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ApiKeySecurity.ProtoReflect.Descriptor instead. func (*ApiKeySecurity) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{2} } func (x *ApiKeySecurity) GetType() string { if x != nil { return x.Type } return "" } func (x *ApiKeySecurity) GetName() string { if x != nil { return x.Name } return "" } func (x *ApiKeySecurity) GetIn() string { if x != nil { return x.In } return "" } func (x *ApiKeySecurity) GetDescription() string { if x != nil { return x.Description } return "" } func (x *ApiKeySecurity) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } type BasicAuthenticationSecurity struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` VendorExtension []*NamedAny `protobuf:"bytes,3,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` } func (x *BasicAuthenticationSecurity) Reset() { *x = BasicAuthenticationSecurity{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *BasicAuthenticationSecurity) String() string { return protoimpl.X.MessageStringOf(x) } func (*BasicAuthenticationSecurity) ProtoMessage() {} func (x *BasicAuthenticationSecurity) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use BasicAuthenticationSecurity.ProtoReflect.Descriptor instead. func (*BasicAuthenticationSecurity) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{3} } func (x *BasicAuthenticationSecurity) GetType() string { if x != nil { return x.Type } return "" } func (x *BasicAuthenticationSecurity) GetDescription() string { if x != nil { return x.Description } return "" } func (x *BasicAuthenticationSecurity) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } type BodyParameter struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed. Description string `protobuf:"bytes,1,opt,name=description,proto3" json:"description,omitempty"` // The name of the parameter. Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // Determines the location of the parameter. In string `protobuf:"bytes,3,opt,name=in,proto3" json:"in,omitempty"` // Determines whether or not this parameter is required or optional. Required bool `protobuf:"varint,4,opt,name=required,proto3" json:"required,omitempty"` Schema *Schema `protobuf:"bytes,5,opt,name=schema,proto3" json:"schema,omitempty"` VendorExtension []*NamedAny `protobuf:"bytes,6,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` } func (x *BodyParameter) Reset() { *x = BodyParameter{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *BodyParameter) String() string { return protoimpl.X.MessageStringOf(x) } func (*BodyParameter) ProtoMessage() {} func (x *BodyParameter) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use BodyParameter.ProtoReflect.Descriptor instead. func (*BodyParameter) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{4} } func (x *BodyParameter) GetDescription() string { if x != nil { return x.Description } return "" } func (x *BodyParameter) GetName() string { if x != nil { return x.Name } return "" } func (x *BodyParameter) GetIn() string { if x != nil { return x.In } return "" } func (x *BodyParameter) GetRequired() bool { if x != nil { return x.Required } return false } func (x *BodyParameter) GetSchema() *Schema { if x != nil { return x.Schema } return nil } func (x *BodyParameter) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } // Contact information for the owners of the API. type Contact struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // The identifying name of the contact person/organization. Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // The URL pointing to the contact information. Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` // The email address of the contact person/organization. Email string `protobuf:"bytes,3,opt,name=email,proto3" json:"email,omitempty"` VendorExtension []*NamedAny `protobuf:"bytes,4,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` } func (x *Contact) Reset() { *x = Contact{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Contact) String() string { return protoimpl.X.MessageStringOf(x) } func (*Contact) ProtoMessage() {} func (x *Contact) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Contact.ProtoReflect.Descriptor instead. func (*Contact) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{5} } func (x *Contact) GetName() string { if x != nil { return x.Name } return "" } func (x *Contact) GetUrl() string { if x != nil { return x.Url } return "" } func (x *Contact) GetEmail() string { if x != nil { return x.Email } return "" } func (x *Contact) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } type Default struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedAny `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *Default) Reset() { *x = Default{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Default) String() string { return protoimpl.X.MessageStringOf(x) } func (*Default) ProtoMessage() {} func (x *Default) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Default.ProtoReflect.Descriptor instead. func (*Default) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{6} } func (x *Default) GetAdditionalProperties() []*NamedAny { if x != nil { return x.AdditionalProperties } return nil } // One or more JSON objects describing the schemas being consumed and produced by the API. type Definitions struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedSchema `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *Definitions) Reset() { *x = Definitions{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Definitions) String() string { return protoimpl.X.MessageStringOf(x) } func (*Definitions) ProtoMessage() {} func (x *Definitions) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Definitions.ProtoReflect.Descriptor instead. func (*Definitions) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{7} } func (x *Definitions) GetAdditionalProperties() []*NamedSchema { if x != nil { return x.AdditionalProperties } return nil } type Document struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // The Swagger version of this document. Swagger string `protobuf:"bytes,1,opt,name=swagger,proto3" json:"swagger,omitempty"` Info *Info `protobuf:"bytes,2,opt,name=info,proto3" json:"info,omitempty"` // The host (name or ip) of the API. Example: 'swagger.io' Host string `protobuf:"bytes,3,opt,name=host,proto3" json:"host,omitempty"` // The base path to the API. Example: '/api'. BasePath string `protobuf:"bytes,4,opt,name=base_path,json=basePath,proto3" json:"base_path,omitempty"` // The transfer protocol of the API. Schemes []string `protobuf:"bytes,5,rep,name=schemes,proto3" json:"schemes,omitempty"` // A list of MIME types accepted by the API. Consumes []string `protobuf:"bytes,6,rep,name=consumes,proto3" json:"consumes,omitempty"` // A list of MIME types the API can produce. Produces []string `protobuf:"bytes,7,rep,name=produces,proto3" json:"produces,omitempty"` Paths *Paths `protobuf:"bytes,8,opt,name=paths,proto3" json:"paths,omitempty"` Definitions *Definitions `protobuf:"bytes,9,opt,name=definitions,proto3" json:"definitions,omitempty"` Parameters *ParameterDefinitions `protobuf:"bytes,10,opt,name=parameters,proto3" json:"parameters,omitempty"` Responses *ResponseDefinitions `protobuf:"bytes,11,opt,name=responses,proto3" json:"responses,omitempty"` Security []*SecurityRequirement `protobuf:"bytes,12,rep,name=security,proto3" json:"security,omitempty"` SecurityDefinitions *SecurityDefinitions `protobuf:"bytes,13,opt,name=security_definitions,json=securityDefinitions,proto3" json:"security_definitions,omitempty"` Tags []*Tag `protobuf:"bytes,14,rep,name=tags,proto3" json:"tags,omitempty"` ExternalDocs *ExternalDocs `protobuf:"bytes,15,opt,name=external_docs,json=externalDocs,proto3" json:"external_docs,omitempty"` VendorExtension []*NamedAny `protobuf:"bytes,16,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` } func (x *Document) Reset() { *x = Document{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Document) String() string { return protoimpl.X.MessageStringOf(x) } func (*Document) ProtoMessage() {} func (x *Document) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Document.ProtoReflect.Descriptor instead. func (*Document) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{8} } func (x *Document) GetSwagger() string { if x != nil { return x.Swagger } return "" } func (x *Document) GetInfo() *Info { if x != nil { return x.Info } return nil } func (x *Document) GetHost() string { if x != nil { return x.Host } return "" } func (x *Document) GetBasePath() string { if x != nil { return x.BasePath } return "" } func (x *Document) GetSchemes() []string { if x != nil { return x.Schemes } return nil } func (x *Document) GetConsumes() []string { if x != nil { return x.Consumes } return nil } func (x *Document) GetProduces() []string { if x != nil { return x.Produces } return nil } func (x *Document) GetPaths() *Paths { if x != nil { return x.Paths } return nil } func (x *Document) GetDefinitions() *Definitions { if x != nil { return x.Definitions } return nil } func (x *Document) GetParameters() *ParameterDefinitions { if x != nil { return x.Parameters } return nil } func (x *Document) GetResponses() *ResponseDefinitions { if x != nil { return x.Responses } return nil } func (x *Document) GetSecurity() []*SecurityRequirement { if x != nil { return x.Security } return nil } func (x *Document) GetSecurityDefinitions() *SecurityDefinitions { if x != nil { return x.SecurityDefinitions } return nil } func (x *Document) GetTags() []*Tag { if x != nil { return x.Tags } return nil } func (x *Document) GetExternalDocs() *ExternalDocs { if x != nil { return x.ExternalDocs } return nil } func (x *Document) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } type Examples struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedAny `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *Examples) Reset() { *x = Examples{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Examples) String() string { return protoimpl.X.MessageStringOf(x) } func (*Examples) ProtoMessage() {} func (x *Examples) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Examples.ProtoReflect.Descriptor instead. func (*Examples) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{9} } func (x *Examples) GetAdditionalProperties() []*NamedAny { if x != nil { return x.AdditionalProperties } return nil } // information about external documentation type ExternalDocs struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Description string `protobuf:"bytes,1,opt,name=description,proto3" json:"description,omitempty"` Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` VendorExtension []*NamedAny `protobuf:"bytes,3,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` } func (x *ExternalDocs) Reset() { *x = ExternalDocs{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ExternalDocs) String() string { return protoimpl.X.MessageStringOf(x) } func (*ExternalDocs) ProtoMessage() {} func (x *ExternalDocs) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ExternalDocs.ProtoReflect.Descriptor instead. func (*ExternalDocs) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{10} } func (x *ExternalDocs) GetDescription() string { if x != nil { return x.Description } return "" } func (x *ExternalDocs) GetUrl() string { if x != nil { return x.Url } return "" } func (x *ExternalDocs) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } // A deterministic version of a JSON Schema object. type FileSchema struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Format string `protobuf:"bytes,1,opt,name=format,proto3" json:"format,omitempty"` Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"` Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` Default *Any `protobuf:"bytes,4,opt,name=default,proto3" json:"default,omitempty"` Required []string `protobuf:"bytes,5,rep,name=required,proto3" json:"required,omitempty"` Type string `protobuf:"bytes,6,opt,name=type,proto3" json:"type,omitempty"` ReadOnly bool `protobuf:"varint,7,opt,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"` ExternalDocs *ExternalDocs `protobuf:"bytes,8,opt,name=external_docs,json=externalDocs,proto3" json:"external_docs,omitempty"` Example *Any `protobuf:"bytes,9,opt,name=example,proto3" json:"example,omitempty"` VendorExtension []*NamedAny `protobuf:"bytes,10,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` } func (x *FileSchema) Reset() { *x = FileSchema{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *FileSchema) String() string { return protoimpl.X.MessageStringOf(x) } func (*FileSchema) ProtoMessage() {} func (x *FileSchema) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use FileSchema.ProtoReflect.Descriptor instead. func (*FileSchema) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{11} } func (x *FileSchema) GetFormat() string { if x != nil { return x.Format } return "" } func (x *FileSchema) GetTitle() string { if x != nil { return x.Title } return "" } func (x *FileSchema) GetDescription() string { if x != nil { return x.Description } return "" } func (x *FileSchema) GetDefault() *Any { if x != nil { return x.Default } return nil } func (x *FileSchema) GetRequired() []string { if x != nil { return x.Required } return nil } func (x *FileSchema) GetType() string { if x != nil { return x.Type } return "" } func (x *FileSchema) GetReadOnly() bool { if x != nil { return x.ReadOnly } return false } func (x *FileSchema) GetExternalDocs() *ExternalDocs { if x != nil { return x.ExternalDocs } return nil } func (x *FileSchema) GetExample() *Any { if x != nil { return x.Example } return nil } func (x *FileSchema) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } type FormDataParameterSubSchema struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Determines whether or not this parameter is required or optional. Required bool `protobuf:"varint,1,opt,name=required,proto3" json:"required,omitempty"` // Determines the location of the parameter. In string `protobuf:"bytes,2,opt,name=in,proto3" json:"in,omitempty"` // A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed. Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` // The name of the parameter. Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` // allows sending a parameter by name only or with an empty value. AllowEmptyValue bool `protobuf:"varint,5,opt,name=allow_empty_value,json=allowEmptyValue,proto3" json:"allow_empty_value,omitempty"` Type string `protobuf:"bytes,6,opt,name=type,proto3" json:"type,omitempty"` Format string `protobuf:"bytes,7,opt,name=format,proto3" json:"format,omitempty"` Items *PrimitivesItems `protobuf:"bytes,8,opt,name=items,proto3" json:"items,omitempty"` CollectionFormat string `protobuf:"bytes,9,opt,name=collection_format,json=collectionFormat,proto3" json:"collection_format,omitempty"` Default *Any `protobuf:"bytes,10,opt,name=default,proto3" json:"default,omitempty"` Maximum float64 `protobuf:"fixed64,11,opt,name=maximum,proto3" json:"maximum,omitempty"` ExclusiveMaximum bool `protobuf:"varint,12,opt,name=exclusive_maximum,json=exclusiveMaximum,proto3" json:"exclusive_maximum,omitempty"` Minimum float64 `protobuf:"fixed64,13,opt,name=minimum,proto3" json:"minimum,omitempty"` ExclusiveMinimum bool `protobuf:"varint,14,opt,name=exclusive_minimum,json=exclusiveMinimum,proto3" json:"exclusive_minimum,omitempty"` MaxLength int64 `protobuf:"varint,15,opt,name=max_length,json=maxLength,proto3" json:"max_length,omitempty"` MinLength int64 `protobuf:"varint,16,opt,name=min_length,json=minLength,proto3" json:"min_length,omitempty"` Pattern string `protobuf:"bytes,17,opt,name=pattern,proto3" json:"pattern,omitempty"` MaxItems int64 `protobuf:"varint,18,opt,name=max_items,json=maxItems,proto3" json:"max_items,omitempty"` MinItems int64 `protobuf:"varint,19,opt,name=min_items,json=minItems,proto3" json:"min_items,omitempty"` UniqueItems bool `protobuf:"varint,20,opt,name=unique_items,json=uniqueItems,proto3" json:"unique_items,omitempty"` Enum []*Any `protobuf:"bytes,21,rep,name=enum,proto3" json:"enum,omitempty"` MultipleOf float64 `protobuf:"fixed64,22,opt,name=multiple_of,json=multipleOf,proto3" json:"multiple_of,omitempty"` VendorExtension []*NamedAny `protobuf:"bytes,23,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` } func (x *FormDataParameterSubSchema) Reset() { *x = FormDataParameterSubSchema{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *FormDataParameterSubSchema) String() string { return protoimpl.X.MessageStringOf(x) } func (*FormDataParameterSubSchema) ProtoMessage() {} func (x *FormDataParameterSubSchema) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use FormDataParameterSubSchema.ProtoReflect.Descriptor instead. func (*FormDataParameterSubSchema) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{12} } func (x *FormDataParameterSubSchema) GetRequired() bool { if x != nil { return x.Required } return false } func (x *FormDataParameterSubSchema) GetIn() string { if x != nil { return x.In } return "" } func (x *FormDataParameterSubSchema) GetDescription() string { if x != nil { return x.Description } return "" } func (x *FormDataParameterSubSchema) GetName() string { if x != nil { return x.Name } return "" } func (x *FormDataParameterSubSchema) GetAllowEmptyValue() bool { if x != nil { return x.AllowEmptyValue } return false } func (x *FormDataParameterSubSchema) GetType() string { if x != nil { return x.Type } return "" } func (x *FormDataParameterSubSchema) GetFormat() string { if x != nil { return x.Format } return "" } func (x *FormDataParameterSubSchema) GetItems() *PrimitivesItems { if x != nil { return x.Items } return nil } func (x *FormDataParameterSubSchema) GetCollectionFormat() string { if x != nil { return x.CollectionFormat } return "" } func (x *FormDataParameterSubSchema) GetDefault() *Any { if x != nil { return x.Default } return nil } func (x *FormDataParameterSubSchema) GetMaximum() float64 { if x != nil { return x.Maximum } return 0 } func (x *FormDataParameterSubSchema) GetExclusiveMaximum() bool { if x != nil { return x.ExclusiveMaximum } return false } func (x *FormDataParameterSubSchema) GetMinimum() float64 { if x != nil { return x.Minimum } return 0 } func (x *FormDataParameterSubSchema) GetExclusiveMinimum() bool { if x != nil { return x.ExclusiveMinimum } return false } func (x *FormDataParameterSubSchema) GetMaxLength() int64 { if x != nil { return x.MaxLength } return 0 } func (x *FormDataParameterSubSchema) GetMinLength() int64 { if x != nil { return x.MinLength } return 0 } func (x *FormDataParameterSubSchema) GetPattern() string { if x != nil { return x.Pattern } return "" } func (x *FormDataParameterSubSchema) GetMaxItems() int64 { if x != nil { return x.MaxItems } return 0 } func (x *FormDataParameterSubSchema) GetMinItems() int64 { if x != nil { return x.MinItems } return 0 } func (x *FormDataParameterSubSchema) GetUniqueItems() bool { if x != nil { return x.UniqueItems } return false } func (x *FormDataParameterSubSchema) GetEnum() []*Any { if x != nil { return x.Enum } return nil } func (x *FormDataParameterSubSchema) GetMultipleOf() float64 { if x != nil { return x.MultipleOf } return 0 } func (x *FormDataParameterSubSchema) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } type Header struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` Format string `protobuf:"bytes,2,opt,name=format,proto3" json:"format,omitempty"` Items *PrimitivesItems `protobuf:"bytes,3,opt,name=items,proto3" json:"items,omitempty"` CollectionFormat string `protobuf:"bytes,4,opt,name=collection_format,json=collectionFormat,proto3" json:"collection_format,omitempty"` Default *Any `protobuf:"bytes,5,opt,name=default,proto3" json:"default,omitempty"` Maximum float64 `protobuf:"fixed64,6,opt,name=maximum,proto3" json:"maximum,omitempty"` ExclusiveMaximum bool `protobuf:"varint,7,opt,name=exclusive_maximum,json=exclusiveMaximum,proto3" json:"exclusive_maximum,omitempty"` Minimum float64 `protobuf:"fixed64,8,opt,name=minimum,proto3" json:"minimum,omitempty"` ExclusiveMinimum bool `protobuf:"varint,9,opt,name=exclusive_minimum,json=exclusiveMinimum,proto3" json:"exclusive_minimum,omitempty"` MaxLength int64 `protobuf:"varint,10,opt,name=max_length,json=maxLength,proto3" json:"max_length,omitempty"` MinLength int64 `protobuf:"varint,11,opt,name=min_length,json=minLength,proto3" json:"min_length,omitempty"` Pattern string `protobuf:"bytes,12,opt,name=pattern,proto3" json:"pattern,omitempty"` MaxItems int64 `protobuf:"varint,13,opt,name=max_items,json=maxItems,proto3" json:"max_items,omitempty"` MinItems int64 `protobuf:"varint,14,opt,name=min_items,json=minItems,proto3" json:"min_items,omitempty"` UniqueItems bool `protobuf:"varint,15,opt,name=unique_items,json=uniqueItems,proto3" json:"unique_items,omitempty"` Enum []*Any `protobuf:"bytes,16,rep,name=enum,proto3" json:"enum,omitempty"` MultipleOf float64 `protobuf:"fixed64,17,opt,name=multiple_of,json=multipleOf,proto3" json:"multiple_of,omitempty"` Description string `protobuf:"bytes,18,opt,name=description,proto3" json:"description,omitempty"` VendorExtension []*NamedAny `protobuf:"bytes,19,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` } func (x *Header) Reset() { *x = Header{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Header) String() string { return protoimpl.X.MessageStringOf(x) } func (*Header) ProtoMessage() {} func (x *Header) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Header.ProtoReflect.Descriptor instead. func (*Header) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{13} } func (x *Header) GetType() string { if x != nil { return x.Type } return "" } func (x *Header) GetFormat() string { if x != nil { return x.Format } return "" } func (x *Header) GetItems() *PrimitivesItems { if x != nil { return x.Items } return nil } func (x *Header) GetCollectionFormat() string { if x != nil { return x.CollectionFormat } return "" } func (x *Header) GetDefault() *Any { if x != nil { return x.Default } return nil } func (x *Header) GetMaximum() float64 { if x != nil { return x.Maximum } return 0 } func (x *Header) GetExclusiveMaximum() bool { if x != nil { return x.ExclusiveMaximum } return false } func (x *Header) GetMinimum() float64 { if x != nil { return x.Minimum } return 0 } func (x *Header) GetExclusiveMinimum() bool { if x != nil { return x.ExclusiveMinimum } return false } func (x *Header) GetMaxLength() int64 { if x != nil { return x.MaxLength } return 0 } func (x *Header) GetMinLength() int64 { if x != nil { return x.MinLength } return 0 } func (x *Header) GetPattern() string { if x != nil { return x.Pattern } return "" } func (x *Header) GetMaxItems() int64 { if x != nil { return x.MaxItems } return 0 } func (x *Header) GetMinItems() int64 { if x != nil { return x.MinItems } return 0 } func (x *Header) GetUniqueItems() bool { if x != nil { return x.UniqueItems } return false } func (x *Header) GetEnum() []*Any { if x != nil { return x.Enum } return nil } func (x *Header) GetMultipleOf() float64 { if x != nil { return x.MultipleOf } return 0 } func (x *Header) GetDescription() string { if x != nil { return x.Description } return "" } func (x *Header) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } type HeaderParameterSubSchema struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Determines whether or not this parameter is required or optional. Required bool `protobuf:"varint,1,opt,name=required,proto3" json:"required,omitempty"` // Determines the location of the parameter. In string `protobuf:"bytes,2,opt,name=in,proto3" json:"in,omitempty"` // A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed. Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` // The name of the parameter. Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` Type string `protobuf:"bytes,5,opt,name=type,proto3" json:"type,omitempty"` Format string `protobuf:"bytes,6,opt,name=format,proto3" json:"format,omitempty"` Items *PrimitivesItems `protobuf:"bytes,7,opt,name=items,proto3" json:"items,omitempty"` CollectionFormat string `protobuf:"bytes,8,opt,name=collection_format,json=collectionFormat,proto3" json:"collection_format,omitempty"` Default *Any `protobuf:"bytes,9,opt,name=default,proto3" json:"default,omitempty"` Maximum float64 `protobuf:"fixed64,10,opt,name=maximum,proto3" json:"maximum,omitempty"` ExclusiveMaximum bool `protobuf:"varint,11,opt,name=exclusive_maximum,json=exclusiveMaximum,proto3" json:"exclusive_maximum,omitempty"` Minimum float64 `protobuf:"fixed64,12,opt,name=minimum,proto3" json:"minimum,omitempty"` ExclusiveMinimum bool `protobuf:"varint,13,opt,name=exclusive_minimum,json=exclusiveMinimum,proto3" json:"exclusive_minimum,omitempty"` MaxLength int64 `protobuf:"varint,14,opt,name=max_length,json=maxLength,proto3" json:"max_length,omitempty"` MinLength int64 `protobuf:"varint,15,opt,name=min_length,json=minLength,proto3" json:"min_length,omitempty"` Pattern string `protobuf:"bytes,16,opt,name=pattern,proto3" json:"pattern,omitempty"` MaxItems int64 `protobuf:"varint,17,opt,name=max_items,json=maxItems,proto3" json:"max_items,omitempty"` MinItems int64 `protobuf:"varint,18,opt,name=min_items,json=minItems,proto3" json:"min_items,omitempty"` UniqueItems bool `protobuf:"varint,19,opt,name=unique_items,json=uniqueItems,proto3" json:"unique_items,omitempty"` Enum []*Any `protobuf:"bytes,20,rep,name=enum,proto3" json:"enum,omitempty"` MultipleOf float64 `protobuf:"fixed64,21,opt,name=multiple_of,json=multipleOf,proto3" json:"multiple_of,omitempty"` VendorExtension []*NamedAny `protobuf:"bytes,22,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` } func (x *HeaderParameterSubSchema) Reset() { *x = HeaderParameterSubSchema{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *HeaderParameterSubSchema) String() string { return protoimpl.X.MessageStringOf(x) } func (*HeaderParameterSubSchema) ProtoMessage() {} func (x *HeaderParameterSubSchema) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HeaderParameterSubSchema.ProtoReflect.Descriptor instead. func (*HeaderParameterSubSchema) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{14} } func (x *HeaderParameterSubSchema) GetRequired() bool { if x != nil { return x.Required } return false } func (x *HeaderParameterSubSchema) GetIn() string { if x != nil { return x.In } return "" } func (x *HeaderParameterSubSchema) GetDescription() string { if x != nil { return x.Description } return "" } func (x *HeaderParameterSubSchema) GetName() string { if x != nil { return x.Name } return "" } func (x *HeaderParameterSubSchema) GetType() string { if x != nil { return x.Type } return "" } func (x *HeaderParameterSubSchema) GetFormat() string { if x != nil { return x.Format } return "" } func (x *HeaderParameterSubSchema) GetItems() *PrimitivesItems { if x != nil { return x.Items } return nil } func (x *HeaderParameterSubSchema) GetCollectionFormat() string { if x != nil { return x.CollectionFormat } return "" } func (x *HeaderParameterSubSchema) GetDefault() *Any { if x != nil { return x.Default } return nil } func (x *HeaderParameterSubSchema) GetMaximum() float64 { if x != nil { return x.Maximum } return 0 } func (x *HeaderParameterSubSchema) GetExclusiveMaximum() bool { if x != nil { return x.ExclusiveMaximum } return false } func (x *HeaderParameterSubSchema) GetMinimum() float64 { if x != nil { return x.Minimum } return 0 } func (x *HeaderParameterSubSchema) GetExclusiveMinimum() bool { if x != nil { return x.ExclusiveMinimum } return false } func (x *HeaderParameterSubSchema) GetMaxLength() int64 { if x != nil { return x.MaxLength } return 0 } func (x *HeaderParameterSubSchema) GetMinLength() int64 { if x != nil { return x.MinLength } return 0 } func (x *HeaderParameterSubSchema) GetPattern() string { if x != nil { return x.Pattern } return "" } func (x *HeaderParameterSubSchema) GetMaxItems() int64 { if x != nil { return x.MaxItems } return 0 } func (x *HeaderParameterSubSchema) GetMinItems() int64 { if x != nil { return x.MinItems } return 0 } func (x *HeaderParameterSubSchema) GetUniqueItems() bool { if x != nil { return x.UniqueItems } return false } func (x *HeaderParameterSubSchema) GetEnum() []*Any { if x != nil { return x.Enum } return nil } func (x *HeaderParameterSubSchema) GetMultipleOf() float64 { if x != nil { return x.MultipleOf } return 0 } func (x *HeaderParameterSubSchema) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } type Headers struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedHeader `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *Headers) Reset() { *x = Headers{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Headers) String() string { return protoimpl.X.MessageStringOf(x) } func (*Headers) ProtoMessage() {} func (x *Headers) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Headers.ProtoReflect.Descriptor instead. func (*Headers) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{15} } func (x *Headers) GetAdditionalProperties() []*NamedHeader { if x != nil { return x.AdditionalProperties } return nil } // General information about the API. type Info struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // A unique and precise title of the API. Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` // A semantic version number of the API. Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` // A longer description of the API. Should be different from the title. GitHub Flavored Markdown is allowed. Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` // The terms of service for the API. TermsOfService string `protobuf:"bytes,4,opt,name=terms_of_service,json=termsOfService,proto3" json:"terms_of_service,omitempty"` Contact *Contact `protobuf:"bytes,5,opt,name=contact,proto3" json:"contact,omitempty"` License *License `protobuf:"bytes,6,opt,name=license,proto3" json:"license,omitempty"` VendorExtension []*NamedAny `protobuf:"bytes,7,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` } func (x *Info) Reset() { *x = Info{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Info) String() string { return protoimpl.X.MessageStringOf(x) } func (*Info) ProtoMessage() {} func (x *Info) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Info.ProtoReflect.Descriptor instead. func (*Info) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{16} } func (x *Info) GetTitle() string { if x != nil { return x.Title } return "" } func (x *Info) GetVersion() string { if x != nil { return x.Version } return "" } func (x *Info) GetDescription() string { if x != nil { return x.Description } return "" } func (x *Info) GetTermsOfService() string { if x != nil { return x.TermsOfService } return "" } func (x *Info) GetContact() *Contact { if x != nil { return x.Contact } return nil } func (x *Info) GetLicense() *License { if x != nil { return x.License } return nil } func (x *Info) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } type ItemsItem struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Schema []*Schema `protobuf:"bytes,1,rep,name=schema,proto3" json:"schema,omitempty"` } func (x *ItemsItem) Reset() { *x = ItemsItem{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ItemsItem) String() string { return protoimpl.X.MessageStringOf(x) } func (*ItemsItem) ProtoMessage() {} func (x *ItemsItem) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ItemsItem.ProtoReflect.Descriptor instead. func (*ItemsItem) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{17} } func (x *ItemsItem) GetSchema() []*Schema { if x != nil { return x.Schema } return nil } type JsonReference struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields XRef string `protobuf:"bytes,1,opt,name=_ref,json=Ref,proto3" json:"_ref,omitempty"` Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` } func (x *JsonReference) Reset() { *x = JsonReference{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *JsonReference) String() string { return protoimpl.X.MessageStringOf(x) } func (*JsonReference) ProtoMessage() {} func (x *JsonReference) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use JsonReference.ProtoReflect.Descriptor instead. func (*JsonReference) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{18} } func (x *JsonReference) GetXRef() string { if x != nil { return x.XRef } return "" } func (x *JsonReference) GetDescription() string { if x != nil { return x.Description } return "" } type License struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // The name of the license type. It's encouraged to use an OSI compatible license. Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // The URL pointing to the license. Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` VendorExtension []*NamedAny `protobuf:"bytes,3,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` } func (x *License) Reset() { *x = License{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *License) String() string { return protoimpl.X.MessageStringOf(x) } func (*License) ProtoMessage() {} func (x *License) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use License.ProtoReflect.Descriptor instead. func (*License) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{19} } func (x *License) GetName() string { if x != nil { return x.Name } return "" } func (x *License) GetUrl() string { if x != nil { return x.Url } return "" } func (x *License) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } // Automatically-generated message used to represent maps of Any as ordered (name,value) pairs. type NamedAny struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Map key Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Mapped value Value *Any `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *NamedAny) Reset() { *x = NamedAny{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NamedAny) String() string { return protoimpl.X.MessageStringOf(x) } func (*NamedAny) ProtoMessage() {} func (x *NamedAny) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NamedAny.ProtoReflect.Descriptor instead. func (*NamedAny) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{20} } func (x *NamedAny) GetName() string { if x != nil { return x.Name } return "" } func (x *NamedAny) GetValue() *Any { if x != nil { return x.Value } return nil } // Automatically-generated message used to represent maps of Header as ordered (name,value) pairs. type NamedHeader struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Map key Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Mapped value Value *Header `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *NamedHeader) Reset() { *x = NamedHeader{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NamedHeader) String() string { return protoimpl.X.MessageStringOf(x) } func (*NamedHeader) ProtoMessage() {} func (x *NamedHeader) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NamedHeader.ProtoReflect.Descriptor instead. func (*NamedHeader) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{21} } func (x *NamedHeader) GetName() string { if x != nil { return x.Name } return "" } func (x *NamedHeader) GetValue() *Header { if x != nil { return x.Value } return nil } // Automatically-generated message used to represent maps of Parameter as ordered (name,value) pairs. type NamedParameter struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Map key Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Mapped value Value *Parameter `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *NamedParameter) Reset() { *x = NamedParameter{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NamedParameter) String() string { return protoimpl.X.MessageStringOf(x) } func (*NamedParameter) ProtoMessage() {} func (x *NamedParameter) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NamedParameter.ProtoReflect.Descriptor instead. func (*NamedParameter) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{22} } func (x *NamedParameter) GetName() string { if x != nil { return x.Name } return "" } func (x *NamedParameter) GetValue() *Parameter { if x != nil { return x.Value } return nil } // Automatically-generated message used to represent maps of PathItem as ordered (name,value) pairs. type NamedPathItem struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Map key Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Mapped value Value *PathItem `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *NamedPathItem) Reset() { *x = NamedPathItem{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NamedPathItem) String() string { return protoimpl.X.MessageStringOf(x) } func (*NamedPathItem) ProtoMessage() {} func (x *NamedPathItem) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NamedPathItem.ProtoReflect.Descriptor instead. func (*NamedPathItem) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{23} } func (x *NamedPathItem) GetName() string { if x != nil { return x.Name } return "" } func (x *NamedPathItem) GetValue() *PathItem { if x != nil { return x.Value } return nil } // Automatically-generated message used to represent maps of Response as ordered (name,value) pairs. type NamedResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Map key Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Mapped value Value *Response `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *NamedResponse) Reset() { *x = NamedResponse{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NamedResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*NamedResponse) ProtoMessage() {} func (x *NamedResponse) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NamedResponse.ProtoReflect.Descriptor instead. func (*NamedResponse) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{24} } func (x *NamedResponse) GetName() string { if x != nil { return x.Name } return "" } func (x *NamedResponse) GetValue() *Response { if x != nil { return x.Value } return nil } // Automatically-generated message used to represent maps of ResponseValue as ordered (name,value) pairs. type NamedResponseValue struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Map key Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Mapped value Value *ResponseValue `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *NamedResponseValue) Reset() { *x = NamedResponseValue{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NamedResponseValue) String() string { return protoimpl.X.MessageStringOf(x) } func (*NamedResponseValue) ProtoMessage() {} func (x *NamedResponseValue) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NamedResponseValue.ProtoReflect.Descriptor instead. func (*NamedResponseValue) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{25} } func (x *NamedResponseValue) GetName() string { if x != nil { return x.Name } return "" } func (x *NamedResponseValue) GetValue() *ResponseValue { if x != nil { return x.Value } return nil } // Automatically-generated message used to represent maps of Schema as ordered (name,value) pairs. type NamedSchema struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Map key Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Mapped value Value *Schema `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *NamedSchema) Reset() { *x = NamedSchema{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NamedSchema) String() string { return protoimpl.X.MessageStringOf(x) } func (*NamedSchema) ProtoMessage() {} func (x *NamedSchema) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NamedSchema.ProtoReflect.Descriptor instead. func (*NamedSchema) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{26} } func (x *NamedSchema) GetName() string { if x != nil { return x.Name } return "" } func (x *NamedSchema) GetValue() *Schema { if x != nil { return x.Value } return nil } // Automatically-generated message used to represent maps of SecurityDefinitionsItem as ordered (name,value) pairs. type NamedSecurityDefinitionsItem struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Map key Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Mapped value Value *SecurityDefinitionsItem `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *NamedSecurityDefinitionsItem) Reset() { *x = NamedSecurityDefinitionsItem{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NamedSecurityDefinitionsItem) String() string { return protoimpl.X.MessageStringOf(x) } func (*NamedSecurityDefinitionsItem) ProtoMessage() {} func (x *NamedSecurityDefinitionsItem) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NamedSecurityDefinitionsItem.ProtoReflect.Descriptor instead. func (*NamedSecurityDefinitionsItem) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{27} } func (x *NamedSecurityDefinitionsItem) GetName() string { if x != nil { return x.Name } return "" } func (x *NamedSecurityDefinitionsItem) GetValue() *SecurityDefinitionsItem { if x != nil { return x.Value } return nil } // Automatically-generated message used to represent maps of string as ordered (name,value) pairs. type NamedString struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Map key Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Mapped value Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *NamedString) Reset() { *x = NamedString{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NamedString) String() string { return protoimpl.X.MessageStringOf(x) } func (*NamedString) ProtoMessage() {} func (x *NamedString) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NamedString.ProtoReflect.Descriptor instead. func (*NamedString) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{28} } func (x *NamedString) GetName() string { if x != nil { return x.Name } return "" } func (x *NamedString) GetValue() string { if x != nil { return x.Value } return "" } // Automatically-generated message used to represent maps of StringArray as ordered (name,value) pairs. type NamedStringArray struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Map key Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Mapped value Value *StringArray `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *NamedStringArray) Reset() { *x = NamedStringArray{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NamedStringArray) String() string { return protoimpl.X.MessageStringOf(x) } func (*NamedStringArray) ProtoMessage() {} func (x *NamedStringArray) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NamedStringArray.ProtoReflect.Descriptor instead. func (*NamedStringArray) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{29} } func (x *NamedStringArray) GetName() string { if x != nil { return x.Name } return "" } func (x *NamedStringArray) GetValue() *StringArray { if x != nil { return x.Value } return nil } type NonBodyParameter struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Types that are assignable to Oneof: // *NonBodyParameter_HeaderParameterSubSchema // *NonBodyParameter_FormDataParameterSubSchema // *NonBodyParameter_QueryParameterSubSchema // *NonBodyParameter_PathParameterSubSchema Oneof isNonBodyParameter_Oneof `protobuf_oneof:"oneof"` } func (x *NonBodyParameter) Reset() { *x = NonBodyParameter{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NonBodyParameter) String() string { return protoimpl.X.MessageStringOf(x) } func (*NonBodyParameter) ProtoMessage() {} func (x *NonBodyParameter) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NonBodyParameter.ProtoReflect.Descriptor instead. func (*NonBodyParameter) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{30} } func (m *NonBodyParameter) GetOneof() isNonBodyParameter_Oneof { if m != nil { return m.Oneof } return nil } func (x *NonBodyParameter) GetHeaderParameterSubSchema() *HeaderParameterSubSchema { if x, ok := x.GetOneof().(*NonBodyParameter_HeaderParameterSubSchema); ok { return x.HeaderParameterSubSchema } return nil } func (x *NonBodyParameter) GetFormDataParameterSubSchema() *FormDataParameterSubSchema { if x, ok := x.GetOneof().(*NonBodyParameter_FormDataParameterSubSchema); ok { return x.FormDataParameterSubSchema } return nil } func (x *NonBodyParameter) GetQueryParameterSubSchema() *QueryParameterSubSchema { if x, ok := x.GetOneof().(*NonBodyParameter_QueryParameterSubSchema); ok { return x.QueryParameterSubSchema } return nil } func (x *NonBodyParameter) GetPathParameterSubSchema() *PathParameterSubSchema { if x, ok := x.GetOneof().(*NonBodyParameter_PathParameterSubSchema); ok { return x.PathParameterSubSchema } return nil } type isNonBodyParameter_Oneof interface { isNonBodyParameter_Oneof() } type NonBodyParameter_HeaderParameterSubSchema struct { HeaderParameterSubSchema *HeaderParameterSubSchema `protobuf:"bytes,1,opt,name=header_parameter_sub_schema,json=headerParameterSubSchema,proto3,oneof"` } type NonBodyParameter_FormDataParameterSubSchema struct { FormDataParameterSubSchema *FormDataParameterSubSchema `protobuf:"bytes,2,opt,name=form_data_parameter_sub_schema,json=formDataParameterSubSchema,proto3,oneof"` } type NonBodyParameter_QueryParameterSubSchema struct { QueryParameterSubSchema *QueryParameterSubSchema `protobuf:"bytes,3,opt,name=query_parameter_sub_schema,json=queryParameterSubSchema,proto3,oneof"` } type NonBodyParameter_PathParameterSubSchema struct { PathParameterSubSchema *PathParameterSubSchema `protobuf:"bytes,4,opt,name=path_parameter_sub_schema,json=pathParameterSubSchema,proto3,oneof"` } func (*NonBodyParameter_HeaderParameterSubSchema) isNonBodyParameter_Oneof() {} func (*NonBodyParameter_FormDataParameterSubSchema) isNonBodyParameter_Oneof() {} func (*NonBodyParameter_QueryParameterSubSchema) isNonBodyParameter_Oneof() {} func (*NonBodyParameter_PathParameterSubSchema) isNonBodyParameter_Oneof() {} type Oauth2AccessCodeSecurity struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` Flow string `protobuf:"bytes,2,opt,name=flow,proto3" json:"flow,omitempty"` Scopes *Oauth2Scopes `protobuf:"bytes,3,opt,name=scopes,proto3" json:"scopes,omitempty"` AuthorizationUrl string `protobuf:"bytes,4,opt,name=authorization_url,json=authorizationUrl,proto3" json:"authorization_url,omitempty"` TokenUrl string `protobuf:"bytes,5,opt,name=token_url,json=tokenUrl,proto3" json:"token_url,omitempty"` Description string `protobuf:"bytes,6,opt,name=description,proto3" json:"description,omitempty"` VendorExtension []*NamedAny `protobuf:"bytes,7,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` } func (x *Oauth2AccessCodeSecurity) Reset() { *x = Oauth2AccessCodeSecurity{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Oauth2AccessCodeSecurity) String() string { return protoimpl.X.MessageStringOf(x) } func (*Oauth2AccessCodeSecurity) ProtoMessage() {} func (x *Oauth2AccessCodeSecurity) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Oauth2AccessCodeSecurity.ProtoReflect.Descriptor instead. func (*Oauth2AccessCodeSecurity) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{31} } func (x *Oauth2AccessCodeSecurity) GetType() string { if x != nil { return x.Type } return "" } func (x *Oauth2AccessCodeSecurity) GetFlow() string { if x != nil { return x.Flow } return "" } func (x *Oauth2AccessCodeSecurity) GetScopes() *Oauth2Scopes { if x != nil { return x.Scopes } return nil } func (x *Oauth2AccessCodeSecurity) GetAuthorizationUrl() string { if x != nil { return x.AuthorizationUrl } return "" } func (x *Oauth2AccessCodeSecurity) GetTokenUrl() string { if x != nil { return x.TokenUrl } return "" } func (x *Oauth2AccessCodeSecurity) GetDescription() string { if x != nil { return x.Description } return "" } func (x *Oauth2AccessCodeSecurity) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } type Oauth2ApplicationSecurity struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` Flow string `protobuf:"bytes,2,opt,name=flow,proto3" json:"flow,omitempty"` Scopes *Oauth2Scopes `protobuf:"bytes,3,opt,name=scopes,proto3" json:"scopes,omitempty"` TokenUrl string `protobuf:"bytes,4,opt,name=token_url,json=tokenUrl,proto3" json:"token_url,omitempty"` Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty"` VendorExtension []*NamedAny `protobuf:"bytes,6,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` } func (x *Oauth2ApplicationSecurity) Reset() { *x = Oauth2ApplicationSecurity{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Oauth2ApplicationSecurity) String() string { return protoimpl.X.MessageStringOf(x) } func (*Oauth2ApplicationSecurity) ProtoMessage() {} func (x *Oauth2ApplicationSecurity) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Oauth2ApplicationSecurity.ProtoReflect.Descriptor instead. func (*Oauth2ApplicationSecurity) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{32} } func (x *Oauth2ApplicationSecurity) GetType() string { if x != nil { return x.Type } return "" } func (x *Oauth2ApplicationSecurity) GetFlow() string { if x != nil { return x.Flow } return "" } func (x *Oauth2ApplicationSecurity) GetScopes() *Oauth2Scopes { if x != nil { return x.Scopes } return nil } func (x *Oauth2ApplicationSecurity) GetTokenUrl() string { if x != nil { return x.TokenUrl } return "" } func (x *Oauth2ApplicationSecurity) GetDescription() string { if x != nil { return x.Description } return "" } func (x *Oauth2ApplicationSecurity) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } type Oauth2ImplicitSecurity struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` Flow string `protobuf:"bytes,2,opt,name=flow,proto3" json:"flow,omitempty"` Scopes *Oauth2Scopes `protobuf:"bytes,3,opt,name=scopes,proto3" json:"scopes,omitempty"` AuthorizationUrl string `protobuf:"bytes,4,opt,name=authorization_url,json=authorizationUrl,proto3" json:"authorization_url,omitempty"` Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty"` VendorExtension []*NamedAny `protobuf:"bytes,6,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` } func (x *Oauth2ImplicitSecurity) Reset() { *x = Oauth2ImplicitSecurity{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Oauth2ImplicitSecurity) String() string { return protoimpl.X.MessageStringOf(x) } func (*Oauth2ImplicitSecurity) ProtoMessage() {} func (x *Oauth2ImplicitSecurity) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Oauth2ImplicitSecurity.ProtoReflect.Descriptor instead. func (*Oauth2ImplicitSecurity) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{33} } func (x *Oauth2ImplicitSecurity) GetType() string { if x != nil { return x.Type } return "" } func (x *Oauth2ImplicitSecurity) GetFlow() string { if x != nil { return x.Flow } return "" } func (x *Oauth2ImplicitSecurity) GetScopes() *Oauth2Scopes { if x != nil { return x.Scopes } return nil } func (x *Oauth2ImplicitSecurity) GetAuthorizationUrl() string { if x != nil { return x.AuthorizationUrl } return "" } func (x *Oauth2ImplicitSecurity) GetDescription() string { if x != nil { return x.Description } return "" } func (x *Oauth2ImplicitSecurity) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } type Oauth2PasswordSecurity struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` Flow string `protobuf:"bytes,2,opt,name=flow,proto3" json:"flow,omitempty"` Scopes *Oauth2Scopes `protobuf:"bytes,3,opt,name=scopes,proto3" json:"scopes,omitempty"` TokenUrl string `protobuf:"bytes,4,opt,name=token_url,json=tokenUrl,proto3" json:"token_url,omitempty"` Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty"` VendorExtension []*NamedAny `protobuf:"bytes,6,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` } func (x *Oauth2PasswordSecurity) Reset() { *x = Oauth2PasswordSecurity{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Oauth2PasswordSecurity) String() string { return protoimpl.X.MessageStringOf(x) } func (*Oauth2PasswordSecurity) ProtoMessage() {} func (x *Oauth2PasswordSecurity) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Oauth2PasswordSecurity.ProtoReflect.Descriptor instead. func (*Oauth2PasswordSecurity) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{34} } func (x *Oauth2PasswordSecurity) GetType() string { if x != nil { return x.Type } return "" } func (x *Oauth2PasswordSecurity) GetFlow() string { if x != nil { return x.Flow } return "" } func (x *Oauth2PasswordSecurity) GetScopes() *Oauth2Scopes { if x != nil { return x.Scopes } return nil } func (x *Oauth2PasswordSecurity) GetTokenUrl() string { if x != nil { return x.TokenUrl } return "" } func (x *Oauth2PasswordSecurity) GetDescription() string { if x != nil { return x.Description } return "" } func (x *Oauth2PasswordSecurity) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } type Oauth2Scopes struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedString `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *Oauth2Scopes) Reset() { *x = Oauth2Scopes{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Oauth2Scopes) String() string { return protoimpl.X.MessageStringOf(x) } func (*Oauth2Scopes) ProtoMessage() {} func (x *Oauth2Scopes) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Oauth2Scopes.ProtoReflect.Descriptor instead. func (*Oauth2Scopes) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{35} } func (x *Oauth2Scopes) GetAdditionalProperties() []*NamedString { if x != nil { return x.AdditionalProperties } return nil } type Operation struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Tags []string `protobuf:"bytes,1,rep,name=tags,proto3" json:"tags,omitempty"` // A brief summary of the operation. Summary string `protobuf:"bytes,2,opt,name=summary,proto3" json:"summary,omitempty"` // A longer description of the operation, GitHub Flavored Markdown is allowed. Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` ExternalDocs *ExternalDocs `protobuf:"bytes,4,opt,name=external_docs,json=externalDocs,proto3" json:"external_docs,omitempty"` // A unique identifier of the operation. OperationId string `protobuf:"bytes,5,opt,name=operation_id,json=operationId,proto3" json:"operation_id,omitempty"` // A list of MIME types the API can produce. Produces []string `protobuf:"bytes,6,rep,name=produces,proto3" json:"produces,omitempty"` // A list of MIME types the API can consume. Consumes []string `protobuf:"bytes,7,rep,name=consumes,proto3" json:"consumes,omitempty"` // The parameters needed to send a valid API call. Parameters []*ParametersItem `protobuf:"bytes,8,rep,name=parameters,proto3" json:"parameters,omitempty"` Responses *Responses `protobuf:"bytes,9,opt,name=responses,proto3" json:"responses,omitempty"` // The transfer protocol of the API. Schemes []string `protobuf:"bytes,10,rep,name=schemes,proto3" json:"schemes,omitempty"` Deprecated bool `protobuf:"varint,11,opt,name=deprecated,proto3" json:"deprecated,omitempty"` Security []*SecurityRequirement `protobuf:"bytes,12,rep,name=security,proto3" json:"security,omitempty"` VendorExtension []*NamedAny `protobuf:"bytes,13,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` } func (x *Operation) Reset() { *x = Operation{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Operation) String() string { return protoimpl.X.MessageStringOf(x) } func (*Operation) ProtoMessage() {} func (x *Operation) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Operation.ProtoReflect.Descriptor instead. func (*Operation) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{36} } func (x *Operation) GetTags() []string { if x != nil { return x.Tags } return nil } func (x *Operation) GetSummary() string { if x != nil { return x.Summary } return "" } func (x *Operation) GetDescription() string { if x != nil { return x.Description } return "" } func (x *Operation) GetExternalDocs() *ExternalDocs { if x != nil { return x.ExternalDocs } return nil } func (x *Operation) GetOperationId() string { if x != nil { return x.OperationId } return "" } func (x *Operation) GetProduces() []string { if x != nil { return x.Produces } return nil } func (x *Operation) GetConsumes() []string { if x != nil { return x.Consumes } return nil } func (x *Operation) GetParameters() []*ParametersItem { if x != nil { return x.Parameters } return nil } func (x *Operation) GetResponses() *Responses { if x != nil { return x.Responses } return nil } func (x *Operation) GetSchemes() []string { if x != nil { return x.Schemes } return nil } func (x *Operation) GetDeprecated() bool { if x != nil { return x.Deprecated } return false } func (x *Operation) GetSecurity() []*SecurityRequirement { if x != nil { return x.Security } return nil } func (x *Operation) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } type Parameter struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Types that are assignable to Oneof: // *Parameter_BodyParameter // *Parameter_NonBodyParameter Oneof isParameter_Oneof `protobuf_oneof:"oneof"` } func (x *Parameter) Reset() { *x = Parameter{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Parameter) String() string { return protoimpl.X.MessageStringOf(x) } func (*Parameter) ProtoMessage() {} func (x *Parameter) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Parameter.ProtoReflect.Descriptor instead. func (*Parameter) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{37} } func (m *Parameter) GetOneof() isParameter_Oneof { if m != nil { return m.Oneof } return nil } func (x *Parameter) GetBodyParameter() *BodyParameter { if x, ok := x.GetOneof().(*Parameter_BodyParameter); ok { return x.BodyParameter } return nil } func (x *Parameter) GetNonBodyParameter() *NonBodyParameter { if x, ok := x.GetOneof().(*Parameter_NonBodyParameter); ok { return x.NonBodyParameter } return nil } type isParameter_Oneof interface { isParameter_Oneof() } type Parameter_BodyParameter struct { BodyParameter *BodyParameter `protobuf:"bytes,1,opt,name=body_parameter,json=bodyParameter,proto3,oneof"` } type Parameter_NonBodyParameter struct { NonBodyParameter *NonBodyParameter `protobuf:"bytes,2,opt,name=non_body_parameter,json=nonBodyParameter,proto3,oneof"` } func (*Parameter_BodyParameter) isParameter_Oneof() {} func (*Parameter_NonBodyParameter) isParameter_Oneof() {} // One or more JSON representations for parameters type ParameterDefinitions struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedParameter `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *ParameterDefinitions) Reset() { *x = ParameterDefinitions{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ParameterDefinitions) String() string { return protoimpl.X.MessageStringOf(x) } func (*ParameterDefinitions) ProtoMessage() {} func (x *ParameterDefinitions) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ParameterDefinitions.ProtoReflect.Descriptor instead. func (*ParameterDefinitions) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{38} } func (x *ParameterDefinitions) GetAdditionalProperties() []*NamedParameter { if x != nil { return x.AdditionalProperties } return nil } type ParametersItem struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Types that are assignable to Oneof: // *ParametersItem_Parameter // *ParametersItem_JsonReference Oneof isParametersItem_Oneof `protobuf_oneof:"oneof"` } func (x *ParametersItem) Reset() { *x = ParametersItem{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ParametersItem) String() string { return protoimpl.X.MessageStringOf(x) } func (*ParametersItem) ProtoMessage() {} func (x *ParametersItem) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ParametersItem.ProtoReflect.Descriptor instead. func (*ParametersItem) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{39} } func (m *ParametersItem) GetOneof() isParametersItem_Oneof { if m != nil { return m.Oneof } return nil } func (x *ParametersItem) GetParameter() *Parameter { if x, ok := x.GetOneof().(*ParametersItem_Parameter); ok { return x.Parameter } return nil } func (x *ParametersItem) GetJsonReference() *JsonReference { if x, ok := x.GetOneof().(*ParametersItem_JsonReference); ok { return x.JsonReference } return nil } type isParametersItem_Oneof interface { isParametersItem_Oneof() } type ParametersItem_Parameter struct { Parameter *Parameter `protobuf:"bytes,1,opt,name=parameter,proto3,oneof"` } type ParametersItem_JsonReference struct { JsonReference *JsonReference `protobuf:"bytes,2,opt,name=json_reference,json=jsonReference,proto3,oneof"` } func (*ParametersItem_Parameter) isParametersItem_Oneof() {} func (*ParametersItem_JsonReference) isParametersItem_Oneof() {} type PathItem struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields XRef string `protobuf:"bytes,1,opt,name=_ref,json=Ref,proto3" json:"_ref,omitempty"` Get *Operation `protobuf:"bytes,2,opt,name=get,proto3" json:"get,omitempty"` Put *Operation `protobuf:"bytes,3,opt,name=put,proto3" json:"put,omitempty"` Post *Operation `protobuf:"bytes,4,opt,name=post,proto3" json:"post,omitempty"` Delete *Operation `protobuf:"bytes,5,opt,name=delete,proto3" json:"delete,omitempty"` Options *Operation `protobuf:"bytes,6,opt,name=options,proto3" json:"options,omitempty"` Head *Operation `protobuf:"bytes,7,opt,name=head,proto3" json:"head,omitempty"` Patch *Operation `protobuf:"bytes,8,opt,name=patch,proto3" json:"patch,omitempty"` // The parameters needed to send a valid API call. Parameters []*ParametersItem `protobuf:"bytes,9,rep,name=parameters,proto3" json:"parameters,omitempty"` VendorExtension []*NamedAny `protobuf:"bytes,10,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` } func (x *PathItem) Reset() { *x = PathItem{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *PathItem) String() string { return protoimpl.X.MessageStringOf(x) } func (*PathItem) ProtoMessage() {} func (x *PathItem) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PathItem.ProtoReflect.Descriptor instead. func (*PathItem) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{40} } func (x *PathItem) GetXRef() string { if x != nil { return x.XRef } return "" } func (x *PathItem) GetGet() *Operation { if x != nil { return x.Get } return nil } func (x *PathItem) GetPut() *Operation { if x != nil { return x.Put } return nil } func (x *PathItem) GetPost() *Operation { if x != nil { return x.Post } return nil } func (x *PathItem) GetDelete() *Operation { if x != nil { return x.Delete } return nil } func (x *PathItem) GetOptions() *Operation { if x != nil { return x.Options } return nil } func (x *PathItem) GetHead() *Operation { if x != nil { return x.Head } return nil } func (x *PathItem) GetPatch() *Operation { if x != nil { return x.Patch } return nil } func (x *PathItem) GetParameters() []*ParametersItem { if x != nil { return x.Parameters } return nil } func (x *PathItem) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } type PathParameterSubSchema struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Determines whether or not this parameter is required or optional. Required bool `protobuf:"varint,1,opt,name=required,proto3" json:"required,omitempty"` // Determines the location of the parameter. In string `protobuf:"bytes,2,opt,name=in,proto3" json:"in,omitempty"` // A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed. Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` // The name of the parameter. Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` Type string `protobuf:"bytes,5,opt,name=type,proto3" json:"type,omitempty"` Format string `protobuf:"bytes,6,opt,name=format,proto3" json:"format,omitempty"` Items *PrimitivesItems `protobuf:"bytes,7,opt,name=items,proto3" json:"items,omitempty"` CollectionFormat string `protobuf:"bytes,8,opt,name=collection_format,json=collectionFormat,proto3" json:"collection_format,omitempty"` Default *Any `protobuf:"bytes,9,opt,name=default,proto3" json:"default,omitempty"` Maximum float64 `protobuf:"fixed64,10,opt,name=maximum,proto3" json:"maximum,omitempty"` ExclusiveMaximum bool `protobuf:"varint,11,opt,name=exclusive_maximum,json=exclusiveMaximum,proto3" json:"exclusive_maximum,omitempty"` Minimum float64 `protobuf:"fixed64,12,opt,name=minimum,proto3" json:"minimum,omitempty"` ExclusiveMinimum bool `protobuf:"varint,13,opt,name=exclusive_minimum,json=exclusiveMinimum,proto3" json:"exclusive_minimum,omitempty"` MaxLength int64 `protobuf:"varint,14,opt,name=max_length,json=maxLength,proto3" json:"max_length,omitempty"` MinLength int64 `protobuf:"varint,15,opt,name=min_length,json=minLength,proto3" json:"min_length,omitempty"` Pattern string `protobuf:"bytes,16,opt,name=pattern,proto3" json:"pattern,omitempty"` MaxItems int64 `protobuf:"varint,17,opt,name=max_items,json=maxItems,proto3" json:"max_items,omitempty"` MinItems int64 `protobuf:"varint,18,opt,name=min_items,json=minItems,proto3" json:"min_items,omitempty"` UniqueItems bool `protobuf:"varint,19,opt,name=unique_items,json=uniqueItems,proto3" json:"unique_items,omitempty"` Enum []*Any `protobuf:"bytes,20,rep,name=enum,proto3" json:"enum,omitempty"` MultipleOf float64 `protobuf:"fixed64,21,opt,name=multiple_of,json=multipleOf,proto3" json:"multiple_of,omitempty"` VendorExtension []*NamedAny `protobuf:"bytes,22,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` } func (x *PathParameterSubSchema) Reset() { *x = PathParameterSubSchema{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *PathParameterSubSchema) String() string { return protoimpl.X.MessageStringOf(x) } func (*PathParameterSubSchema) ProtoMessage() {} func (x *PathParameterSubSchema) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[41] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PathParameterSubSchema.ProtoReflect.Descriptor instead. func (*PathParameterSubSchema) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{41} } func (x *PathParameterSubSchema) GetRequired() bool { if x != nil { return x.Required } return false } func (x *PathParameterSubSchema) GetIn() string { if x != nil { return x.In } return "" } func (x *PathParameterSubSchema) GetDescription() string { if x != nil { return x.Description } return "" } func (x *PathParameterSubSchema) GetName() string { if x != nil { return x.Name } return "" } func (x *PathParameterSubSchema) GetType() string { if x != nil { return x.Type } return "" } func (x *PathParameterSubSchema) GetFormat() string { if x != nil { return x.Format } return "" } func (x *PathParameterSubSchema) GetItems() *PrimitivesItems { if x != nil { return x.Items } return nil } func (x *PathParameterSubSchema) GetCollectionFormat() string { if x != nil { return x.CollectionFormat } return "" } func (x *PathParameterSubSchema) GetDefault() *Any { if x != nil { return x.Default } return nil } func (x *PathParameterSubSchema) GetMaximum() float64 { if x != nil { return x.Maximum } return 0 } func (x *PathParameterSubSchema) GetExclusiveMaximum() bool { if x != nil { return x.ExclusiveMaximum } return false } func (x *PathParameterSubSchema) GetMinimum() float64 { if x != nil { return x.Minimum } return 0 } func (x *PathParameterSubSchema) GetExclusiveMinimum() bool { if x != nil { return x.ExclusiveMinimum } return false } func (x *PathParameterSubSchema) GetMaxLength() int64 { if x != nil { return x.MaxLength } return 0 } func (x *PathParameterSubSchema) GetMinLength() int64 { if x != nil { return x.MinLength } return 0 } func (x *PathParameterSubSchema) GetPattern() string { if x != nil { return x.Pattern } return "" } func (x *PathParameterSubSchema) GetMaxItems() int64 { if x != nil { return x.MaxItems } return 0 } func (x *PathParameterSubSchema) GetMinItems() int64 { if x != nil { return x.MinItems } return 0 } func (x *PathParameterSubSchema) GetUniqueItems() bool { if x != nil { return x.UniqueItems } return false } func (x *PathParameterSubSchema) GetEnum() []*Any { if x != nil { return x.Enum } return nil } func (x *PathParameterSubSchema) GetMultipleOf() float64 { if x != nil { return x.MultipleOf } return 0 } func (x *PathParameterSubSchema) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } // Relative paths to the individual endpoints. They must be relative to the 'basePath'. type Paths struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields VendorExtension []*NamedAny `protobuf:"bytes,1,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` Path []*NamedPathItem `protobuf:"bytes,2,rep,name=path,proto3" json:"path,omitempty"` } func (x *Paths) Reset() { *x = Paths{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Paths) String() string { return protoimpl.X.MessageStringOf(x) } func (*Paths) ProtoMessage() {} func (x *Paths) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[42] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Paths.ProtoReflect.Descriptor instead. func (*Paths) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{42} } func (x *Paths) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } func (x *Paths) GetPath() []*NamedPathItem { if x != nil { return x.Path } return nil } type PrimitivesItems struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` Format string `protobuf:"bytes,2,opt,name=format,proto3" json:"format,omitempty"` Items *PrimitivesItems `protobuf:"bytes,3,opt,name=items,proto3" json:"items,omitempty"` CollectionFormat string `protobuf:"bytes,4,opt,name=collection_format,json=collectionFormat,proto3" json:"collection_format,omitempty"` Default *Any `protobuf:"bytes,5,opt,name=default,proto3" json:"default,omitempty"` Maximum float64 `protobuf:"fixed64,6,opt,name=maximum,proto3" json:"maximum,omitempty"` ExclusiveMaximum bool `protobuf:"varint,7,opt,name=exclusive_maximum,json=exclusiveMaximum,proto3" json:"exclusive_maximum,omitempty"` Minimum float64 `protobuf:"fixed64,8,opt,name=minimum,proto3" json:"minimum,omitempty"` ExclusiveMinimum bool `protobuf:"varint,9,opt,name=exclusive_minimum,json=exclusiveMinimum,proto3" json:"exclusive_minimum,omitempty"` MaxLength int64 `protobuf:"varint,10,opt,name=max_length,json=maxLength,proto3" json:"max_length,omitempty"` MinLength int64 `protobuf:"varint,11,opt,name=min_length,json=minLength,proto3" json:"min_length,omitempty"` Pattern string `protobuf:"bytes,12,opt,name=pattern,proto3" json:"pattern,omitempty"` MaxItems int64 `protobuf:"varint,13,opt,name=max_items,json=maxItems,proto3" json:"max_items,omitempty"` MinItems int64 `protobuf:"varint,14,opt,name=min_items,json=minItems,proto3" json:"min_items,omitempty"` UniqueItems bool `protobuf:"varint,15,opt,name=unique_items,json=uniqueItems,proto3" json:"unique_items,omitempty"` Enum []*Any `protobuf:"bytes,16,rep,name=enum,proto3" json:"enum,omitempty"` MultipleOf float64 `protobuf:"fixed64,17,opt,name=multiple_of,json=multipleOf,proto3" json:"multiple_of,omitempty"` VendorExtension []*NamedAny `protobuf:"bytes,18,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` } func (x *PrimitivesItems) Reset() { *x = PrimitivesItems{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *PrimitivesItems) String() string { return protoimpl.X.MessageStringOf(x) } func (*PrimitivesItems) ProtoMessage() {} func (x *PrimitivesItems) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PrimitivesItems.ProtoReflect.Descriptor instead. func (*PrimitivesItems) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{43} } func (x *PrimitivesItems) GetType() string { if x != nil { return x.Type } return "" } func (x *PrimitivesItems) GetFormat() string { if x != nil { return x.Format } return "" } func (x *PrimitivesItems) GetItems() *PrimitivesItems { if x != nil { return x.Items } return nil } func (x *PrimitivesItems) GetCollectionFormat() string { if x != nil { return x.CollectionFormat } return "" } func (x *PrimitivesItems) GetDefault() *Any { if x != nil { return x.Default } return nil } func (x *PrimitivesItems) GetMaximum() float64 { if x != nil { return x.Maximum } return 0 } func (x *PrimitivesItems) GetExclusiveMaximum() bool { if x != nil { return x.ExclusiveMaximum } return false } func (x *PrimitivesItems) GetMinimum() float64 { if x != nil { return x.Minimum } return 0 } func (x *PrimitivesItems) GetExclusiveMinimum() bool { if x != nil { return x.ExclusiveMinimum } return false } func (x *PrimitivesItems) GetMaxLength() int64 { if x != nil { return x.MaxLength } return 0 } func (x *PrimitivesItems) GetMinLength() int64 { if x != nil { return x.MinLength } return 0 } func (x *PrimitivesItems) GetPattern() string { if x != nil { return x.Pattern } return "" } func (x *PrimitivesItems) GetMaxItems() int64 { if x != nil { return x.MaxItems } return 0 } func (x *PrimitivesItems) GetMinItems() int64 { if x != nil { return x.MinItems } return 0 } func (x *PrimitivesItems) GetUniqueItems() bool { if x != nil { return x.UniqueItems } return false } func (x *PrimitivesItems) GetEnum() []*Any { if x != nil { return x.Enum } return nil } func (x *PrimitivesItems) GetMultipleOf() float64 { if x != nil { return x.MultipleOf } return 0 } func (x *PrimitivesItems) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } type Properties struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedSchema `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *Properties) Reset() { *x = Properties{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Properties) String() string { return protoimpl.X.MessageStringOf(x) } func (*Properties) ProtoMessage() {} func (x *Properties) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Properties.ProtoReflect.Descriptor instead. func (*Properties) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{44} } func (x *Properties) GetAdditionalProperties() []*NamedSchema { if x != nil { return x.AdditionalProperties } return nil } type QueryParameterSubSchema struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Determines whether or not this parameter is required or optional. Required bool `protobuf:"varint,1,opt,name=required,proto3" json:"required,omitempty"` // Determines the location of the parameter. In string `protobuf:"bytes,2,opt,name=in,proto3" json:"in,omitempty"` // A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed. Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` // The name of the parameter. Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` // allows sending a parameter by name only or with an empty value. AllowEmptyValue bool `protobuf:"varint,5,opt,name=allow_empty_value,json=allowEmptyValue,proto3" json:"allow_empty_value,omitempty"` Type string `protobuf:"bytes,6,opt,name=type,proto3" json:"type,omitempty"` Format string `protobuf:"bytes,7,opt,name=format,proto3" json:"format,omitempty"` Items *PrimitivesItems `protobuf:"bytes,8,opt,name=items,proto3" json:"items,omitempty"` CollectionFormat string `protobuf:"bytes,9,opt,name=collection_format,json=collectionFormat,proto3" json:"collection_format,omitempty"` Default *Any `protobuf:"bytes,10,opt,name=default,proto3" json:"default,omitempty"` Maximum float64 `protobuf:"fixed64,11,opt,name=maximum,proto3" json:"maximum,omitempty"` ExclusiveMaximum bool `protobuf:"varint,12,opt,name=exclusive_maximum,json=exclusiveMaximum,proto3" json:"exclusive_maximum,omitempty"` Minimum float64 `protobuf:"fixed64,13,opt,name=minimum,proto3" json:"minimum,omitempty"` ExclusiveMinimum bool `protobuf:"varint,14,opt,name=exclusive_minimum,json=exclusiveMinimum,proto3" json:"exclusive_minimum,omitempty"` MaxLength int64 `protobuf:"varint,15,opt,name=max_length,json=maxLength,proto3" json:"max_length,omitempty"` MinLength int64 `protobuf:"varint,16,opt,name=min_length,json=minLength,proto3" json:"min_length,omitempty"` Pattern string `protobuf:"bytes,17,opt,name=pattern,proto3" json:"pattern,omitempty"` MaxItems int64 `protobuf:"varint,18,opt,name=max_items,json=maxItems,proto3" json:"max_items,omitempty"` MinItems int64 `protobuf:"varint,19,opt,name=min_items,json=minItems,proto3" json:"min_items,omitempty"` UniqueItems bool `protobuf:"varint,20,opt,name=unique_items,json=uniqueItems,proto3" json:"unique_items,omitempty"` Enum []*Any `protobuf:"bytes,21,rep,name=enum,proto3" json:"enum,omitempty"` MultipleOf float64 `protobuf:"fixed64,22,opt,name=multiple_of,json=multipleOf,proto3" json:"multiple_of,omitempty"` VendorExtension []*NamedAny `protobuf:"bytes,23,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` } func (x *QueryParameterSubSchema) Reset() { *x = QueryParameterSubSchema{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *QueryParameterSubSchema) String() string { return protoimpl.X.MessageStringOf(x) } func (*QueryParameterSubSchema) ProtoMessage() {} func (x *QueryParameterSubSchema) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[45] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use QueryParameterSubSchema.ProtoReflect.Descriptor instead. func (*QueryParameterSubSchema) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{45} } func (x *QueryParameterSubSchema) GetRequired() bool { if x != nil { return x.Required } return false } func (x *QueryParameterSubSchema) GetIn() string { if x != nil { return x.In } return "" } func (x *QueryParameterSubSchema) GetDescription() string { if x != nil { return x.Description } return "" } func (x *QueryParameterSubSchema) GetName() string { if x != nil { return x.Name } return "" } func (x *QueryParameterSubSchema) GetAllowEmptyValue() bool { if x != nil { return x.AllowEmptyValue } return false } func (x *QueryParameterSubSchema) GetType() string { if x != nil { return x.Type } return "" } func (x *QueryParameterSubSchema) GetFormat() string { if x != nil { return x.Format } return "" } func (x *QueryParameterSubSchema) GetItems() *PrimitivesItems { if x != nil { return x.Items } return nil } func (x *QueryParameterSubSchema) GetCollectionFormat() string { if x != nil { return x.CollectionFormat } return "" } func (x *QueryParameterSubSchema) GetDefault() *Any { if x != nil { return x.Default } return nil } func (x *QueryParameterSubSchema) GetMaximum() float64 { if x != nil { return x.Maximum } return 0 } func (x *QueryParameterSubSchema) GetExclusiveMaximum() bool { if x != nil { return x.ExclusiveMaximum } return false } func (x *QueryParameterSubSchema) GetMinimum() float64 { if x != nil { return x.Minimum } return 0 } func (x *QueryParameterSubSchema) GetExclusiveMinimum() bool { if x != nil { return x.ExclusiveMinimum } return false } func (x *QueryParameterSubSchema) GetMaxLength() int64 { if x != nil { return x.MaxLength } return 0 } func (x *QueryParameterSubSchema) GetMinLength() int64 { if x != nil { return x.MinLength } return 0 } func (x *QueryParameterSubSchema) GetPattern() string { if x != nil { return x.Pattern } return "" } func (x *QueryParameterSubSchema) GetMaxItems() int64 { if x != nil { return x.MaxItems } return 0 } func (x *QueryParameterSubSchema) GetMinItems() int64 { if x != nil { return x.MinItems } return 0 } func (x *QueryParameterSubSchema) GetUniqueItems() bool { if x != nil { return x.UniqueItems } return false } func (x *QueryParameterSubSchema) GetEnum() []*Any { if x != nil { return x.Enum } return nil } func (x *QueryParameterSubSchema) GetMultipleOf() float64 { if x != nil { return x.MultipleOf } return 0 } func (x *QueryParameterSubSchema) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } type Response struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Description string `protobuf:"bytes,1,opt,name=description,proto3" json:"description,omitempty"` Schema *SchemaItem `protobuf:"bytes,2,opt,name=schema,proto3" json:"schema,omitempty"` Headers *Headers `protobuf:"bytes,3,opt,name=headers,proto3" json:"headers,omitempty"` Examples *Examples `protobuf:"bytes,4,opt,name=examples,proto3" json:"examples,omitempty"` VendorExtension []*NamedAny `protobuf:"bytes,5,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` } func (x *Response) Reset() { *x = Response{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Response) String() string { return protoimpl.X.MessageStringOf(x) } func (*Response) ProtoMessage() {} func (x *Response) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[46] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Response.ProtoReflect.Descriptor instead. func (*Response) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{46} } func (x *Response) GetDescription() string { if x != nil { return x.Description } return "" } func (x *Response) GetSchema() *SchemaItem { if x != nil { return x.Schema } return nil } func (x *Response) GetHeaders() *Headers { if x != nil { return x.Headers } return nil } func (x *Response) GetExamples() *Examples { if x != nil { return x.Examples } return nil } func (x *Response) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } // One or more JSON representations for responses type ResponseDefinitions struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedResponse `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *ResponseDefinitions) Reset() { *x = ResponseDefinitions{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ResponseDefinitions) String() string { return protoimpl.X.MessageStringOf(x) } func (*ResponseDefinitions) ProtoMessage() {} func (x *ResponseDefinitions) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[47] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ResponseDefinitions.ProtoReflect.Descriptor instead. func (*ResponseDefinitions) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{47} } func (x *ResponseDefinitions) GetAdditionalProperties() []*NamedResponse { if x != nil { return x.AdditionalProperties } return nil } type ResponseValue struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Types that are assignable to Oneof: // *ResponseValue_Response // *ResponseValue_JsonReference Oneof isResponseValue_Oneof `protobuf_oneof:"oneof"` } func (x *ResponseValue) Reset() { *x = ResponseValue{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ResponseValue) String() string { return protoimpl.X.MessageStringOf(x) } func (*ResponseValue) ProtoMessage() {} func (x *ResponseValue) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[48] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ResponseValue.ProtoReflect.Descriptor instead. func (*ResponseValue) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{48} } func (m *ResponseValue) GetOneof() isResponseValue_Oneof { if m != nil { return m.Oneof } return nil } func (x *ResponseValue) GetResponse() *Response { if x, ok := x.GetOneof().(*ResponseValue_Response); ok { return x.Response } return nil } func (x *ResponseValue) GetJsonReference() *JsonReference { if x, ok := x.GetOneof().(*ResponseValue_JsonReference); ok { return x.JsonReference } return nil } type isResponseValue_Oneof interface { isResponseValue_Oneof() } type ResponseValue_Response struct { Response *Response `protobuf:"bytes,1,opt,name=response,proto3,oneof"` } type ResponseValue_JsonReference struct { JsonReference *JsonReference `protobuf:"bytes,2,opt,name=json_reference,json=jsonReference,proto3,oneof"` } func (*ResponseValue_Response) isResponseValue_Oneof() {} func (*ResponseValue_JsonReference) isResponseValue_Oneof() {} // Response objects names can either be any valid HTTP status code or 'default'. type Responses struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields ResponseCode []*NamedResponseValue `protobuf:"bytes,1,rep,name=response_code,json=responseCode,proto3" json:"response_code,omitempty"` VendorExtension []*NamedAny `protobuf:"bytes,2,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` } func (x *Responses) Reset() { *x = Responses{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Responses) String() string { return protoimpl.X.MessageStringOf(x) } func (*Responses) ProtoMessage() {} func (x *Responses) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[49] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Responses.ProtoReflect.Descriptor instead. func (*Responses) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{49} } func (x *Responses) GetResponseCode() []*NamedResponseValue { if x != nil { return x.ResponseCode } return nil } func (x *Responses) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } // A deterministic version of a JSON Schema object. type Schema struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields XRef string `protobuf:"bytes,1,opt,name=_ref,json=Ref,proto3" json:"_ref,omitempty"` Format string `protobuf:"bytes,2,opt,name=format,proto3" json:"format,omitempty"` Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"` Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` Default *Any `protobuf:"bytes,5,opt,name=default,proto3" json:"default,omitempty"` MultipleOf float64 `protobuf:"fixed64,6,opt,name=multiple_of,json=multipleOf,proto3" json:"multiple_of,omitempty"` Maximum float64 `protobuf:"fixed64,7,opt,name=maximum,proto3" json:"maximum,omitempty"` ExclusiveMaximum bool `protobuf:"varint,8,opt,name=exclusive_maximum,json=exclusiveMaximum,proto3" json:"exclusive_maximum,omitempty"` Minimum float64 `protobuf:"fixed64,9,opt,name=minimum,proto3" json:"minimum,omitempty"` ExclusiveMinimum bool `protobuf:"varint,10,opt,name=exclusive_minimum,json=exclusiveMinimum,proto3" json:"exclusive_minimum,omitempty"` MaxLength int64 `protobuf:"varint,11,opt,name=max_length,json=maxLength,proto3" json:"max_length,omitempty"` MinLength int64 `protobuf:"varint,12,opt,name=min_length,json=minLength,proto3" json:"min_length,omitempty"` Pattern string `protobuf:"bytes,13,opt,name=pattern,proto3" json:"pattern,omitempty"` MaxItems int64 `protobuf:"varint,14,opt,name=max_items,json=maxItems,proto3" json:"max_items,omitempty"` MinItems int64 `protobuf:"varint,15,opt,name=min_items,json=minItems,proto3" json:"min_items,omitempty"` UniqueItems bool `protobuf:"varint,16,opt,name=unique_items,json=uniqueItems,proto3" json:"unique_items,omitempty"` MaxProperties int64 `protobuf:"varint,17,opt,name=max_properties,json=maxProperties,proto3" json:"max_properties,omitempty"` MinProperties int64 `protobuf:"varint,18,opt,name=min_properties,json=minProperties,proto3" json:"min_properties,omitempty"` Required []string `protobuf:"bytes,19,rep,name=required,proto3" json:"required,omitempty"` Enum []*Any `protobuf:"bytes,20,rep,name=enum,proto3" json:"enum,omitempty"` AdditionalProperties *AdditionalPropertiesItem `protobuf:"bytes,21,opt,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` Type *TypeItem `protobuf:"bytes,22,opt,name=type,proto3" json:"type,omitempty"` Items *ItemsItem `protobuf:"bytes,23,opt,name=items,proto3" json:"items,omitempty"` AllOf []*Schema `protobuf:"bytes,24,rep,name=all_of,json=allOf,proto3" json:"all_of,omitempty"` Properties *Properties `protobuf:"bytes,25,opt,name=properties,proto3" json:"properties,omitempty"` Discriminator string `protobuf:"bytes,26,opt,name=discriminator,proto3" json:"discriminator,omitempty"` ReadOnly bool `protobuf:"varint,27,opt,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"` Xml *Xml `protobuf:"bytes,28,opt,name=xml,proto3" json:"xml,omitempty"` ExternalDocs *ExternalDocs `protobuf:"bytes,29,opt,name=external_docs,json=externalDocs,proto3" json:"external_docs,omitempty"` Example *Any `protobuf:"bytes,30,opt,name=example,proto3" json:"example,omitempty"` VendorExtension []*NamedAny `protobuf:"bytes,31,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` } func (x *Schema) Reset() { *x = Schema{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Schema) String() string { return protoimpl.X.MessageStringOf(x) } func (*Schema) ProtoMessage() {} func (x *Schema) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[50] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Schema.ProtoReflect.Descriptor instead. func (*Schema) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{50} } func (x *Schema) GetXRef() string { if x != nil { return x.XRef } return "" } func (x *Schema) GetFormat() string { if x != nil { return x.Format } return "" } func (x *Schema) GetTitle() string { if x != nil { return x.Title } return "" } func (x *Schema) GetDescription() string { if x != nil { return x.Description } return "" } func (x *Schema) GetDefault() *Any { if x != nil { return x.Default } return nil } func (x *Schema) GetMultipleOf() float64 { if x != nil { return x.MultipleOf } return 0 } func (x *Schema) GetMaximum() float64 { if x != nil { return x.Maximum } return 0 } func (x *Schema) GetExclusiveMaximum() bool { if x != nil { return x.ExclusiveMaximum } return false } func (x *Schema) GetMinimum() float64 { if x != nil { return x.Minimum } return 0 } func (x *Schema) GetExclusiveMinimum() bool { if x != nil { return x.ExclusiveMinimum } return false } func (x *Schema) GetMaxLength() int64 { if x != nil { return x.MaxLength } return 0 } func (x *Schema) GetMinLength() int64 { if x != nil { return x.MinLength } return 0 } func (x *Schema) GetPattern() string { if x != nil { return x.Pattern } return "" } func (x *Schema) GetMaxItems() int64 { if x != nil { return x.MaxItems } return 0 } func (x *Schema) GetMinItems() int64 { if x != nil { return x.MinItems } return 0 } func (x *Schema) GetUniqueItems() bool { if x != nil { return x.UniqueItems } return false } func (x *Schema) GetMaxProperties() int64 { if x != nil { return x.MaxProperties } return 0 } func (x *Schema) GetMinProperties() int64 { if x != nil { return x.MinProperties } return 0 } func (x *Schema) GetRequired() []string { if x != nil { return x.Required } return nil } func (x *Schema) GetEnum() []*Any { if x != nil { return x.Enum } return nil } func (x *Schema) GetAdditionalProperties() *AdditionalPropertiesItem { if x != nil { return x.AdditionalProperties } return nil } func (x *Schema) GetType() *TypeItem { if x != nil { return x.Type } return nil } func (x *Schema) GetItems() *ItemsItem { if x != nil { return x.Items } return nil } func (x *Schema) GetAllOf() []*Schema { if x != nil { return x.AllOf } return nil } func (x *Schema) GetProperties() *Properties { if x != nil { return x.Properties } return nil } func (x *Schema) GetDiscriminator() string { if x != nil { return x.Discriminator } return "" } func (x *Schema) GetReadOnly() bool { if x != nil { return x.ReadOnly } return false } func (x *Schema) GetXml() *Xml { if x != nil { return x.Xml } return nil } func (x *Schema) GetExternalDocs() *ExternalDocs { if x != nil { return x.ExternalDocs } return nil } func (x *Schema) GetExample() *Any { if x != nil { return x.Example } return nil } func (x *Schema) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } type SchemaItem struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Types that are assignable to Oneof: // *SchemaItem_Schema // *SchemaItem_FileSchema Oneof isSchemaItem_Oneof `protobuf_oneof:"oneof"` } func (x *SchemaItem) Reset() { *x = SchemaItem{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *SchemaItem) String() string { return protoimpl.X.MessageStringOf(x) } func (*SchemaItem) ProtoMessage() {} func (x *SchemaItem) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[51] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SchemaItem.ProtoReflect.Descriptor instead. func (*SchemaItem) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{51} } func (m *SchemaItem) GetOneof() isSchemaItem_Oneof { if m != nil { return m.Oneof } return nil } func (x *SchemaItem) GetSchema() *Schema { if x, ok := x.GetOneof().(*SchemaItem_Schema); ok { return x.Schema } return nil } func (x *SchemaItem) GetFileSchema() *FileSchema { if x, ok := x.GetOneof().(*SchemaItem_FileSchema); ok { return x.FileSchema } return nil } type isSchemaItem_Oneof interface { isSchemaItem_Oneof() } type SchemaItem_Schema struct { Schema *Schema `protobuf:"bytes,1,opt,name=schema,proto3,oneof"` } type SchemaItem_FileSchema struct { FileSchema *FileSchema `protobuf:"bytes,2,opt,name=file_schema,json=fileSchema,proto3,oneof"` } func (*SchemaItem_Schema) isSchemaItem_Oneof() {} func (*SchemaItem_FileSchema) isSchemaItem_Oneof() {} type SecurityDefinitions struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedSecurityDefinitionsItem `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *SecurityDefinitions) Reset() { *x = SecurityDefinitions{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *SecurityDefinitions) String() string { return protoimpl.X.MessageStringOf(x) } func (*SecurityDefinitions) ProtoMessage() {} func (x *SecurityDefinitions) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[52] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SecurityDefinitions.ProtoReflect.Descriptor instead. func (*SecurityDefinitions) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{52} } func (x *SecurityDefinitions) GetAdditionalProperties() []*NamedSecurityDefinitionsItem { if x != nil { return x.AdditionalProperties } return nil } type SecurityDefinitionsItem struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Types that are assignable to Oneof: // *SecurityDefinitionsItem_BasicAuthenticationSecurity // *SecurityDefinitionsItem_ApiKeySecurity // *SecurityDefinitionsItem_Oauth2ImplicitSecurity // *SecurityDefinitionsItem_Oauth2PasswordSecurity // *SecurityDefinitionsItem_Oauth2ApplicationSecurity // *SecurityDefinitionsItem_Oauth2AccessCodeSecurity Oneof isSecurityDefinitionsItem_Oneof `protobuf_oneof:"oneof"` } func (x *SecurityDefinitionsItem) Reset() { *x = SecurityDefinitionsItem{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *SecurityDefinitionsItem) String() string { return protoimpl.X.MessageStringOf(x) } func (*SecurityDefinitionsItem) ProtoMessage() {} func (x *SecurityDefinitionsItem) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[53] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SecurityDefinitionsItem.ProtoReflect.Descriptor instead. func (*SecurityDefinitionsItem) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{53} } func (m *SecurityDefinitionsItem) GetOneof() isSecurityDefinitionsItem_Oneof { if m != nil { return m.Oneof } return nil } func (x *SecurityDefinitionsItem) GetBasicAuthenticationSecurity() *BasicAuthenticationSecurity { if x, ok := x.GetOneof().(*SecurityDefinitionsItem_BasicAuthenticationSecurity); ok { return x.BasicAuthenticationSecurity } return nil } func (x *SecurityDefinitionsItem) GetApiKeySecurity() *ApiKeySecurity { if x, ok := x.GetOneof().(*SecurityDefinitionsItem_ApiKeySecurity); ok { return x.ApiKeySecurity } return nil } func (x *SecurityDefinitionsItem) GetOauth2ImplicitSecurity() *Oauth2ImplicitSecurity { if x, ok := x.GetOneof().(*SecurityDefinitionsItem_Oauth2ImplicitSecurity); ok { return x.Oauth2ImplicitSecurity } return nil } func (x *SecurityDefinitionsItem) GetOauth2PasswordSecurity() *Oauth2PasswordSecurity { if x, ok := x.GetOneof().(*SecurityDefinitionsItem_Oauth2PasswordSecurity); ok { return x.Oauth2PasswordSecurity } return nil } func (x *SecurityDefinitionsItem) GetOauth2ApplicationSecurity() *Oauth2ApplicationSecurity { if x, ok := x.GetOneof().(*SecurityDefinitionsItem_Oauth2ApplicationSecurity); ok { return x.Oauth2ApplicationSecurity } return nil } func (x *SecurityDefinitionsItem) GetOauth2AccessCodeSecurity() *Oauth2AccessCodeSecurity { if x, ok := x.GetOneof().(*SecurityDefinitionsItem_Oauth2AccessCodeSecurity); ok { return x.Oauth2AccessCodeSecurity } return nil } type isSecurityDefinitionsItem_Oneof interface { isSecurityDefinitionsItem_Oneof() } type SecurityDefinitionsItem_BasicAuthenticationSecurity struct { BasicAuthenticationSecurity *BasicAuthenticationSecurity `protobuf:"bytes,1,opt,name=basic_authentication_security,json=basicAuthenticationSecurity,proto3,oneof"` } type SecurityDefinitionsItem_ApiKeySecurity struct { ApiKeySecurity *ApiKeySecurity `protobuf:"bytes,2,opt,name=api_key_security,json=apiKeySecurity,proto3,oneof"` } type SecurityDefinitionsItem_Oauth2ImplicitSecurity struct { Oauth2ImplicitSecurity *Oauth2ImplicitSecurity `protobuf:"bytes,3,opt,name=oauth2_implicit_security,json=oauth2ImplicitSecurity,proto3,oneof"` } type SecurityDefinitionsItem_Oauth2PasswordSecurity struct { Oauth2PasswordSecurity *Oauth2PasswordSecurity `protobuf:"bytes,4,opt,name=oauth2_password_security,json=oauth2PasswordSecurity,proto3,oneof"` } type SecurityDefinitionsItem_Oauth2ApplicationSecurity struct { Oauth2ApplicationSecurity *Oauth2ApplicationSecurity `protobuf:"bytes,5,opt,name=oauth2_application_security,json=oauth2ApplicationSecurity,proto3,oneof"` } type SecurityDefinitionsItem_Oauth2AccessCodeSecurity struct { Oauth2AccessCodeSecurity *Oauth2AccessCodeSecurity `protobuf:"bytes,6,opt,name=oauth2_access_code_security,json=oauth2AccessCodeSecurity,proto3,oneof"` } func (*SecurityDefinitionsItem_BasicAuthenticationSecurity) isSecurityDefinitionsItem_Oneof() {} func (*SecurityDefinitionsItem_ApiKeySecurity) isSecurityDefinitionsItem_Oneof() {} func (*SecurityDefinitionsItem_Oauth2ImplicitSecurity) isSecurityDefinitionsItem_Oneof() {} func (*SecurityDefinitionsItem_Oauth2PasswordSecurity) isSecurityDefinitionsItem_Oneof() {} func (*SecurityDefinitionsItem_Oauth2ApplicationSecurity) isSecurityDefinitionsItem_Oneof() {} func (*SecurityDefinitionsItem_Oauth2AccessCodeSecurity) isSecurityDefinitionsItem_Oneof() {} type SecurityRequirement struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedStringArray `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *SecurityRequirement) Reset() { *x = SecurityRequirement{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *SecurityRequirement) String() string { return protoimpl.X.MessageStringOf(x) } func (*SecurityRequirement) ProtoMessage() {} func (x *SecurityRequirement) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[54] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SecurityRequirement.ProtoReflect.Descriptor instead. func (*SecurityRequirement) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{54} } func (x *SecurityRequirement) GetAdditionalProperties() []*NamedStringArray { if x != nil { return x.AdditionalProperties } return nil } type StringArray struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Value []string `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"` } func (x *StringArray) Reset() { *x = StringArray{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *StringArray) String() string { return protoimpl.X.MessageStringOf(x) } func (*StringArray) ProtoMessage() {} func (x *StringArray) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[55] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StringArray.ProtoReflect.Descriptor instead. func (*StringArray) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{55} } func (x *StringArray) GetValue() []string { if x != nil { return x.Value } return nil } type Tag struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` ExternalDocs *ExternalDocs `protobuf:"bytes,3,opt,name=external_docs,json=externalDocs,proto3" json:"external_docs,omitempty"` VendorExtension []*NamedAny `protobuf:"bytes,4,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` } func (x *Tag) Reset() { *x = Tag{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Tag) String() string { return protoimpl.X.MessageStringOf(x) } func (*Tag) ProtoMessage() {} func (x *Tag) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[56] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Tag.ProtoReflect.Descriptor instead. func (*Tag) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{56} } func (x *Tag) GetName() string { if x != nil { return x.Name } return "" } func (x *Tag) GetDescription() string { if x != nil { return x.Description } return "" } func (x *Tag) GetExternalDocs() *ExternalDocs { if x != nil { return x.ExternalDocs } return nil } func (x *Tag) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } type TypeItem struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Value []string `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"` } func (x *TypeItem) Reset() { *x = TypeItem{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *TypeItem) String() string { return protoimpl.X.MessageStringOf(x) } func (*TypeItem) ProtoMessage() {} func (x *TypeItem) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[57] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use TypeItem.ProtoReflect.Descriptor instead. func (*TypeItem) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{57} } func (x *TypeItem) GetValue() []string { if x != nil { return x.Value } return nil } // Any property starting with x- is valid. type VendorExtension struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedAny `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *VendorExtension) Reset() { *x = VendorExtension{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *VendorExtension) String() string { return protoimpl.X.MessageStringOf(x) } func (*VendorExtension) ProtoMessage() {} func (x *VendorExtension) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[58] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use VendorExtension.ProtoReflect.Descriptor instead. func (*VendorExtension) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{58} } func (x *VendorExtension) GetAdditionalProperties() []*NamedAny { if x != nil { return x.AdditionalProperties } return nil } type Xml struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Namespace string `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"` Prefix string `protobuf:"bytes,3,opt,name=prefix,proto3" json:"prefix,omitempty"` Attribute bool `protobuf:"varint,4,opt,name=attribute,proto3" json:"attribute,omitempty"` Wrapped bool `protobuf:"varint,5,opt,name=wrapped,proto3" json:"wrapped,omitempty"` VendorExtension []*NamedAny `protobuf:"bytes,6,rep,name=vendor_extension,json=vendorExtension,proto3" json:"vendor_extension,omitempty"` } func (x *Xml) Reset() { *x = Xml{} if protoimpl.UnsafeEnabled { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Xml) String() string { return protoimpl.X.MessageStringOf(x) } func (*Xml) ProtoMessage() {} func (x *Xml) ProtoReflect() protoreflect.Message { mi := &file_openapiv2_OpenAPIv2_proto_msgTypes[59] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Xml.ProtoReflect.Descriptor instead. func (*Xml) Descriptor() ([]byte, []int) { return file_openapiv2_OpenAPIv2_proto_rawDescGZIP(), []int{59} } func (x *Xml) GetName() string { if x != nil { return x.Name } return "" } func (x *Xml) GetNamespace() string { if x != nil { return x.Namespace } return "" } func (x *Xml) GetPrefix() string { if x != nil { return x.Prefix } return "" } func (x *Xml) GetAttribute() bool { if x != nil { return x.Attribute } return false } func (x *Xml) GetWrapped() bool { if x != nil { return x.Wrapped } return false } func (x *Xml) GetVendorExtension() []*NamedAny { if x != nil { return x.VendorExtension } return nil } var File_openapiv2_OpenAPIv2_proto protoreflect.FileDescriptor var file_openapiv2_OpenAPIv2_proto_rawDesc = []byte{ 0x0a, 0x19, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x4f, 0x70, 0x65, 0x6e, 0x41, 0x50, 0x49, 0x76, 0x32, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x6d, 0x0a, 0x18, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x2c, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x48, 0x00, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x1a, 0x0a, 0x07, 0x62, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x07, 0x62, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x42, 0x07, 0x0a, 0x05, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x22, 0x45, 0x0a, 0x03, 0x41, 0x6e, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x79, 0x61, 0x6d, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x79, 0x61, 0x6d, 0x6c, 0x22, 0xab, 0x01, 0x0a, 0x0e, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x94, 0x01, 0x0a, 0x1b, 0x42, 0x61, 0x73, 0x69, 0x63, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xde, 0x01, 0x0a, 0x0d, 0x42, 0x6f, 0x64, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x86, 0x01, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x54, 0x0a, 0x07, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x49, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0x5b, 0x0a, 0x0b, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x4c, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0xe8, 0x05, 0x0a, 0x08, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, 0x61, 0x73, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x73, 0x12, 0x27, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x73, 0x52, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x12, 0x39, 0x0a, 0x0b, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0b, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x40, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x3d, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x08, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x52, 0x0a, 0x14, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x13, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x23, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x61, 0x67, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x3d, 0x0a, 0x0d, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x6f, 0x63, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x73, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x73, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x55, 0x0a, 0x08, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x12, 0x49, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0x83, 0x01, 0x0a, 0x0c, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xff, 0x02, 0x0a, 0x0a, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x3d, 0x0a, 0x0d, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x6f, 0x63, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x73, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x73, 0x12, 0x29, 0x0a, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xab, 0x06, 0x0a, 0x1a, 0x46, 0x6f, 0x72, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x75, 0x62, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x31, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x29, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x2b, 0x0a, 0x11, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x2b, 0x0a, 0x11, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6d, 0x61, 0x78, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x69, 0x6e, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x10, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6d, 0x69, 0x6e, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6e, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6d, 0x69, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x23, 0x0a, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x5f, 0x6f, 0x66, 0x18, 0x16, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x4f, 0x66, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x17, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xab, 0x05, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x31, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x29, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x2b, 0x0a, 0x11, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x2b, 0x0a, 0x11, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6d, 0x61, 0x78, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x69, 0x6e, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6d, 0x69, 0x6e, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6e, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6d, 0x69, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x23, 0x0a, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x5f, 0x6f, 0x66, 0x18, 0x11, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x4f, 0x66, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x13, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xfd, 0x05, 0x0a, 0x18, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x75, 0x62, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x31, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x29, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x2b, 0x0a, 0x11, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x2b, 0x0a, 0x11, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6d, 0x61, 0x78, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x69, 0x6e, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6d, 0x69, 0x6e, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6e, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6d, 0x69, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x23, 0x0a, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x5f, 0x6f, 0x66, 0x18, 0x15, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x4f, 0x66, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x16, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x57, 0x0a, 0x07, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x4c, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0xa1, 0x02, 0x0a, 0x04, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x4f, 0x66, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2d, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x12, 0x2d, 0x0a, 0x07, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x52, 0x07, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x37, 0x0a, 0x09, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x22, 0x44, 0x0a, 0x0d, 0x4a, 0x73, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x11, 0x0a, 0x04, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x52, 0x65, 0x66, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x70, 0x0a, 0x07, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x45, 0x0a, 0x08, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x4b, 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x51, 0x0a, 0x0e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x4f, 0x0a, 0x0d, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x4f, 0x0a, 0x0d, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x59, 0x0a, 0x12, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x4b, 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x6d, 0x0a, 0x1c, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x39, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x37, 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x55, 0x0a, 0x10, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x41, 0x72, 0x72, 0x61, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x41, 0x72, 0x72, 0x61, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xb5, 0x03, 0x0a, 0x10, 0x4e, 0x6f, 0x6e, 0x42, 0x6f, 0x64, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x65, 0x0a, 0x1b, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x62, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x75, 0x62, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x48, 0x00, 0x52, 0x18, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x75, 0x62, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x6c, 0x0a, 0x1e, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x62, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x46, 0x6f, 0x72, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x75, 0x62, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x48, 0x00, 0x52, 0x1a, 0x66, 0x6f, 0x72, 0x6d, 0x44, 0x61, 0x74, 0x61, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x75, 0x62, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x62, 0x0a, 0x1a, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x62, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x75, 0x62, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x48, 0x00, 0x52, 0x17, 0x71, 0x75, 0x65, 0x72, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x75, 0x62, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x5f, 0x0a, 0x19, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x62, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x75, 0x62, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x48, 0x00, 0x52, 0x16, 0x70, 0x61, 0x74, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x75, 0x62, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x42, 0x07, 0x0a, 0x05, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x22, 0xa1, 0x02, 0x0a, 0x18, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x30, 0x0a, 0x06, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x52, 0x06, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x55, 0x72, 0x6c, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xf5, 0x01, 0x0a, 0x19, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x30, 0x0a, 0x06, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x52, 0x06, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x55, 0x72, 0x6c, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x82, 0x02, 0x0a, 0x16, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x49, 0x6d, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x30, 0x0a, 0x06, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x52, 0x06, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x72, 0x6c, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xf2, 0x01, 0x0a, 0x16, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x30, 0x0a, 0x06, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x52, 0x06, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x55, 0x72, 0x6c, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x5c, 0x0a, 0x0c, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x12, 0x4c, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0x9e, 0x04, 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, 0x0d, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x6f, 0x63, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x73, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x52, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x3b, 0x0a, 0x08, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xa6, 0x01, 0x0a, 0x09, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x42, 0x0a, 0x0e, 0x62, 0x6f, 0x64, 0x79, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x6f, 0x64, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0d, 0x62, 0x6f, 0x64, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x4c, 0x0a, 0x12, 0x6e, 0x6f, 0x6e, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x6f, 0x6e, 0x42, 0x6f, 0x64, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x48, 0x00, 0x52, 0x10, 0x6e, 0x6f, 0x6e, 0x42, 0x6f, 0x64, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x42, 0x07, 0x0a, 0x05, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x22, 0x67, 0x0a, 0x14, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x4f, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0x94, 0x01, 0x0a, 0x0e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x35, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x48, 0x00, 0x52, 0x09, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x42, 0x0a, 0x0e, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4a, 0x73, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x6a, 0x73, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x22, 0xcf, 0x03, 0x0a, 0x08, 0x50, 0x61, 0x74, 0x68, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x11, 0x0a, 0x04, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x52, 0x65, 0x66, 0x12, 0x27, 0x0a, 0x03, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x67, 0x65, 0x74, 0x12, 0x27, 0x0a, 0x03, 0x70, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x70, 0x75, 0x74, 0x12, 0x29, 0x0a, 0x04, 0x70, 0x6f, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x70, 0x6f, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x29, 0x0a, 0x04, 0x68, 0x65, 0x61, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x68, 0x65, 0x61, 0x64, 0x12, 0x2b, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x63, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x70, 0x61, 0x74, 0x63, 0x68, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xfb, 0x05, 0x0a, 0x16, 0x50, 0x61, 0x74, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x75, 0x62, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x31, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x29, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x2b, 0x0a, 0x11, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x2b, 0x0a, 0x11, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6d, 0x61, 0x78, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x69, 0x6e, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6d, 0x69, 0x6e, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6e, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6d, 0x69, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x23, 0x0a, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x5f, 0x6f, 0x66, 0x18, 0x15, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x4f, 0x66, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x16, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x77, 0x0a, 0x05, 0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x92, 0x05, 0x0a, 0x0f, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x31, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x29, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x2b, 0x0a, 0x11, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x2b, 0x0a, 0x11, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6d, 0x61, 0x78, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x69, 0x6e, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6d, 0x69, 0x6e, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6e, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6d, 0x69, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x23, 0x0a, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x5f, 0x6f, 0x66, 0x18, 0x11, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x4f, 0x66, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x12, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x5a, 0x0a, 0x0a, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x4c, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0xa8, 0x06, 0x0a, 0x17, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x53, 0x75, 0x62, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x31, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x29, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x2b, 0x0a, 0x11, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x2b, 0x0a, 0x11, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6d, 0x61, 0x78, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x69, 0x6e, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x10, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6d, 0x69, 0x6e, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6e, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6d, 0x69, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x23, 0x0a, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x5f, 0x6f, 0x66, 0x18, 0x16, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x4f, 0x66, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x17, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xfe, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x2d, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x30, 0x0a, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x52, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x65, 0x0a, 0x13, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x4e, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0x90, 0x01, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x32, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x0e, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4a, 0x73, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x6a, 0x73, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x22, 0x91, 0x01, 0x0a, 0x09, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x0d, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xaf, 0x09, 0x0a, 0x06, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x11, 0x0a, 0x04, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x52, 0x65, 0x66, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x5f, 0x6f, 0x66, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x4f, 0x66, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x07, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x2b, 0x0a, 0x11, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x09, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x2b, 0x0a, 0x11, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6d, 0x61, 0x78, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x69, 0x6e, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6d, 0x69, 0x6e, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6e, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6d, 0x69, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6d, 0x69, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6d, 0x69, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x13, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x23, 0x0a, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x12, 0x59, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x29, 0x0a, 0x06, 0x61, 0x6c, 0x6c, 0x5f, 0x6f, 0x66, 0x18, 0x18, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x05, 0x61, 0x6c, 0x6c, 0x4f, 0x66, 0x12, 0x36, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x64, 0x69, 0x73, 0x63, 0x72, 0x69, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x69, 0x73, 0x63, 0x72, 0x69, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x21, 0x0a, 0x03, 0x78, 0x6d, 0x6c, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x58, 0x6d, 0x6c, 0x52, 0x03, 0x78, 0x6d, 0x6c, 0x12, 0x3d, 0x0a, 0x0d, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x6f, 0x63, 0x73, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x73, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x73, 0x12, 0x29, 0x0a, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x1f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x7e, 0x0a, 0x0a, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x2c, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x48, 0x00, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x39, 0x0a, 0x0b, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x48, 0x00, 0x52, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x42, 0x07, 0x0a, 0x05, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x22, 0x74, 0x0a, 0x13, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x5d, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0xe9, 0x04, 0x0a, 0x17, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x6d, 0x0a, 0x1d, 0x62, 0x61, 0x73, 0x69, 0x63, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x61, 0x73, 0x69, 0x63, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x48, 0x00, 0x52, 0x1b, 0x62, 0x61, 0x73, 0x69, 0x63, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x46, 0x0a, 0x10, 0x61, 0x70, 0x69, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x48, 0x00, 0x52, 0x0e, 0x61, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x5e, 0x0a, 0x18, 0x6f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x5f, 0x69, 0x6d, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x49, 0x6d, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x48, 0x00, 0x52, 0x16, 0x6f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x49, 0x6d, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x5e, 0x0a, 0x18, 0x6f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x48, 0x00, 0x52, 0x16, 0x6f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x67, 0x0a, 0x1b, 0x6f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x48, 0x00, 0x52, 0x19, 0x6f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x65, 0x0a, 0x1b, 0x6f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x48, 0x00, 0x52, 0x18, 0x6f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x42, 0x07, 0x0a, 0x05, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x22, 0x68, 0x0a, 0x13, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x51, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x41, 0x72, 0x72, 0x61, 0x79, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0x23, 0x0a, 0x0b, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x41, 0x72, 0x72, 0x61, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xbb, 0x01, 0x0a, 0x03, 0x54, 0x61, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, 0x0d, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x6f, 0x63, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x73, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x73, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x20, 0x0a, 0x08, 0x54, 0x79, 0x70, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x5c, 0x0a, 0x0f, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x49, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0xc8, 0x01, 0x0a, 0x03, 0x58, 0x6d, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x12, 0x3f, 0x0a, 0x10, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x3e, 0x0a, 0x0e, 0x6f, 0x72, 0x67, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0x42, 0x0c, 0x4f, 0x70, 0x65, 0x6e, 0x41, 0x50, 0x49, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x16, 0x2e, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x3b, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x32, 0xa2, 0x02, 0x03, 0x4f, 0x41, 0x53, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_openapiv2_OpenAPIv2_proto_rawDescOnce sync.Once file_openapiv2_OpenAPIv2_proto_rawDescData = file_openapiv2_OpenAPIv2_proto_rawDesc ) func file_openapiv2_OpenAPIv2_proto_rawDescGZIP() []byte { file_openapiv2_OpenAPIv2_proto_rawDescOnce.Do(func() { file_openapiv2_OpenAPIv2_proto_rawDescData = protoimpl.X.CompressGZIP(file_openapiv2_OpenAPIv2_proto_rawDescData) }) return file_openapiv2_OpenAPIv2_proto_rawDescData } var file_openapiv2_OpenAPIv2_proto_msgTypes = make([]protoimpl.MessageInfo, 60) var file_openapiv2_OpenAPIv2_proto_goTypes = []interface{}{ (*AdditionalPropertiesItem)(nil), // 0: openapi.v2.AdditionalPropertiesItem (*Any)(nil), // 1: openapi.v2.Any (*ApiKeySecurity)(nil), // 2: openapi.v2.ApiKeySecurity (*BasicAuthenticationSecurity)(nil), // 3: openapi.v2.BasicAuthenticationSecurity (*BodyParameter)(nil), // 4: openapi.v2.BodyParameter (*Contact)(nil), // 5: openapi.v2.Contact (*Default)(nil), // 6: openapi.v2.Default (*Definitions)(nil), // 7: openapi.v2.Definitions (*Document)(nil), // 8: openapi.v2.Document (*Examples)(nil), // 9: openapi.v2.Examples (*ExternalDocs)(nil), // 10: openapi.v2.ExternalDocs (*FileSchema)(nil), // 11: openapi.v2.FileSchema (*FormDataParameterSubSchema)(nil), // 12: openapi.v2.FormDataParameterSubSchema (*Header)(nil), // 13: openapi.v2.Header (*HeaderParameterSubSchema)(nil), // 14: openapi.v2.HeaderParameterSubSchema (*Headers)(nil), // 15: openapi.v2.Headers (*Info)(nil), // 16: openapi.v2.Info (*ItemsItem)(nil), // 17: openapi.v2.ItemsItem (*JsonReference)(nil), // 18: openapi.v2.JsonReference (*License)(nil), // 19: openapi.v2.License (*NamedAny)(nil), // 20: openapi.v2.NamedAny (*NamedHeader)(nil), // 21: openapi.v2.NamedHeader (*NamedParameter)(nil), // 22: openapi.v2.NamedParameter (*NamedPathItem)(nil), // 23: openapi.v2.NamedPathItem (*NamedResponse)(nil), // 24: openapi.v2.NamedResponse (*NamedResponseValue)(nil), // 25: openapi.v2.NamedResponseValue (*NamedSchema)(nil), // 26: openapi.v2.NamedSchema (*NamedSecurityDefinitionsItem)(nil), // 27: openapi.v2.NamedSecurityDefinitionsItem (*NamedString)(nil), // 28: openapi.v2.NamedString (*NamedStringArray)(nil), // 29: openapi.v2.NamedStringArray (*NonBodyParameter)(nil), // 30: openapi.v2.NonBodyParameter (*Oauth2AccessCodeSecurity)(nil), // 31: openapi.v2.Oauth2AccessCodeSecurity (*Oauth2ApplicationSecurity)(nil), // 32: openapi.v2.Oauth2ApplicationSecurity (*Oauth2ImplicitSecurity)(nil), // 33: openapi.v2.Oauth2ImplicitSecurity (*Oauth2PasswordSecurity)(nil), // 34: openapi.v2.Oauth2PasswordSecurity (*Oauth2Scopes)(nil), // 35: openapi.v2.Oauth2Scopes (*Operation)(nil), // 36: openapi.v2.Operation (*Parameter)(nil), // 37: openapi.v2.Parameter (*ParameterDefinitions)(nil), // 38: openapi.v2.ParameterDefinitions (*ParametersItem)(nil), // 39: openapi.v2.ParametersItem (*PathItem)(nil), // 40: openapi.v2.PathItem (*PathParameterSubSchema)(nil), // 41: openapi.v2.PathParameterSubSchema (*Paths)(nil), // 42: openapi.v2.Paths (*PrimitivesItems)(nil), // 43: openapi.v2.PrimitivesItems (*Properties)(nil), // 44: openapi.v2.Properties (*QueryParameterSubSchema)(nil), // 45: openapi.v2.QueryParameterSubSchema (*Response)(nil), // 46: openapi.v2.Response (*ResponseDefinitions)(nil), // 47: openapi.v2.ResponseDefinitions (*ResponseValue)(nil), // 48: openapi.v2.ResponseValue (*Responses)(nil), // 49: openapi.v2.Responses (*Schema)(nil), // 50: openapi.v2.Schema (*SchemaItem)(nil), // 51: openapi.v2.SchemaItem (*SecurityDefinitions)(nil), // 52: openapi.v2.SecurityDefinitions (*SecurityDefinitionsItem)(nil), // 53: openapi.v2.SecurityDefinitionsItem (*SecurityRequirement)(nil), // 54: openapi.v2.SecurityRequirement (*StringArray)(nil), // 55: openapi.v2.StringArray (*Tag)(nil), // 56: openapi.v2.Tag (*TypeItem)(nil), // 57: openapi.v2.TypeItem (*VendorExtension)(nil), // 58: openapi.v2.VendorExtension (*Xml)(nil), // 59: openapi.v2.Xml (*anypb.Any)(nil), // 60: google.protobuf.Any } var file_openapiv2_OpenAPIv2_proto_depIdxs = []int32{ 50, // 0: openapi.v2.AdditionalPropertiesItem.schema:type_name -> openapi.v2.Schema 60, // 1: openapi.v2.Any.value:type_name -> google.protobuf.Any 20, // 2: openapi.v2.ApiKeySecurity.vendor_extension:type_name -> openapi.v2.NamedAny 20, // 3: openapi.v2.BasicAuthenticationSecurity.vendor_extension:type_name -> openapi.v2.NamedAny 50, // 4: openapi.v2.BodyParameter.schema:type_name -> openapi.v2.Schema 20, // 5: openapi.v2.BodyParameter.vendor_extension:type_name -> openapi.v2.NamedAny 20, // 6: openapi.v2.Contact.vendor_extension:type_name -> openapi.v2.NamedAny 20, // 7: openapi.v2.Default.additional_properties:type_name -> openapi.v2.NamedAny 26, // 8: openapi.v2.Definitions.additional_properties:type_name -> openapi.v2.NamedSchema 16, // 9: openapi.v2.Document.info:type_name -> openapi.v2.Info 42, // 10: openapi.v2.Document.paths:type_name -> openapi.v2.Paths 7, // 11: openapi.v2.Document.definitions:type_name -> openapi.v2.Definitions 38, // 12: openapi.v2.Document.parameters:type_name -> openapi.v2.ParameterDefinitions 47, // 13: openapi.v2.Document.responses:type_name -> openapi.v2.ResponseDefinitions 54, // 14: openapi.v2.Document.security:type_name -> openapi.v2.SecurityRequirement 52, // 15: openapi.v2.Document.security_definitions:type_name -> openapi.v2.SecurityDefinitions 56, // 16: openapi.v2.Document.tags:type_name -> openapi.v2.Tag 10, // 17: openapi.v2.Document.external_docs:type_name -> openapi.v2.ExternalDocs 20, // 18: openapi.v2.Document.vendor_extension:type_name -> openapi.v2.NamedAny 20, // 19: openapi.v2.Examples.additional_properties:type_name -> openapi.v2.NamedAny 20, // 20: openapi.v2.ExternalDocs.vendor_extension:type_name -> openapi.v2.NamedAny 1, // 21: openapi.v2.FileSchema.default:type_name -> openapi.v2.Any 10, // 22: openapi.v2.FileSchema.external_docs:type_name -> openapi.v2.ExternalDocs 1, // 23: openapi.v2.FileSchema.example:type_name -> openapi.v2.Any 20, // 24: openapi.v2.FileSchema.vendor_extension:type_name -> openapi.v2.NamedAny 43, // 25: openapi.v2.FormDataParameterSubSchema.items:type_name -> openapi.v2.PrimitivesItems 1, // 26: openapi.v2.FormDataParameterSubSchema.default:type_name -> openapi.v2.Any 1, // 27: openapi.v2.FormDataParameterSubSchema.enum:type_name -> openapi.v2.Any 20, // 28: openapi.v2.FormDataParameterSubSchema.vendor_extension:type_name -> openapi.v2.NamedAny 43, // 29: openapi.v2.Header.items:type_name -> openapi.v2.PrimitivesItems 1, // 30: openapi.v2.Header.default:type_name -> openapi.v2.Any 1, // 31: openapi.v2.Header.enum:type_name -> openapi.v2.Any 20, // 32: openapi.v2.Header.vendor_extension:type_name -> openapi.v2.NamedAny 43, // 33: openapi.v2.HeaderParameterSubSchema.items:type_name -> openapi.v2.PrimitivesItems 1, // 34: openapi.v2.HeaderParameterSubSchema.default:type_name -> openapi.v2.Any 1, // 35: openapi.v2.HeaderParameterSubSchema.enum:type_name -> openapi.v2.Any 20, // 36: openapi.v2.HeaderParameterSubSchema.vendor_extension:type_name -> openapi.v2.NamedAny 21, // 37: openapi.v2.Headers.additional_properties:type_name -> openapi.v2.NamedHeader 5, // 38: openapi.v2.Info.contact:type_name -> openapi.v2.Contact 19, // 39: openapi.v2.Info.license:type_name -> openapi.v2.License 20, // 40: openapi.v2.Info.vendor_extension:type_name -> openapi.v2.NamedAny 50, // 41: openapi.v2.ItemsItem.schema:type_name -> openapi.v2.Schema 20, // 42: openapi.v2.License.vendor_extension:type_name -> openapi.v2.NamedAny 1, // 43: openapi.v2.NamedAny.value:type_name -> openapi.v2.Any 13, // 44: openapi.v2.NamedHeader.value:type_name -> openapi.v2.Header 37, // 45: openapi.v2.NamedParameter.value:type_name -> openapi.v2.Parameter 40, // 46: openapi.v2.NamedPathItem.value:type_name -> openapi.v2.PathItem 46, // 47: openapi.v2.NamedResponse.value:type_name -> openapi.v2.Response 48, // 48: openapi.v2.NamedResponseValue.value:type_name -> openapi.v2.ResponseValue 50, // 49: openapi.v2.NamedSchema.value:type_name -> openapi.v2.Schema 53, // 50: openapi.v2.NamedSecurityDefinitionsItem.value:type_name -> openapi.v2.SecurityDefinitionsItem 55, // 51: openapi.v2.NamedStringArray.value:type_name -> openapi.v2.StringArray 14, // 52: openapi.v2.NonBodyParameter.header_parameter_sub_schema:type_name -> openapi.v2.HeaderParameterSubSchema 12, // 53: openapi.v2.NonBodyParameter.form_data_parameter_sub_schema:type_name -> openapi.v2.FormDataParameterSubSchema 45, // 54: openapi.v2.NonBodyParameter.query_parameter_sub_schema:type_name -> openapi.v2.QueryParameterSubSchema 41, // 55: openapi.v2.NonBodyParameter.path_parameter_sub_schema:type_name -> openapi.v2.PathParameterSubSchema 35, // 56: openapi.v2.Oauth2AccessCodeSecurity.scopes:type_name -> openapi.v2.Oauth2Scopes 20, // 57: openapi.v2.Oauth2AccessCodeSecurity.vendor_extension:type_name -> openapi.v2.NamedAny 35, // 58: openapi.v2.Oauth2ApplicationSecurity.scopes:type_name -> openapi.v2.Oauth2Scopes 20, // 59: openapi.v2.Oauth2ApplicationSecurity.vendor_extension:type_name -> openapi.v2.NamedAny 35, // 60: openapi.v2.Oauth2ImplicitSecurity.scopes:type_name -> openapi.v2.Oauth2Scopes 20, // 61: openapi.v2.Oauth2ImplicitSecurity.vendor_extension:type_name -> openapi.v2.NamedAny 35, // 62: openapi.v2.Oauth2PasswordSecurity.scopes:type_name -> openapi.v2.Oauth2Scopes 20, // 63: openapi.v2.Oauth2PasswordSecurity.vendor_extension:type_name -> openapi.v2.NamedAny 28, // 64: openapi.v2.Oauth2Scopes.additional_properties:type_name -> openapi.v2.NamedString 10, // 65: openapi.v2.Operation.external_docs:type_name -> openapi.v2.ExternalDocs 39, // 66: openapi.v2.Operation.parameters:type_name -> openapi.v2.ParametersItem 49, // 67: openapi.v2.Operation.responses:type_name -> openapi.v2.Responses 54, // 68: openapi.v2.Operation.security:type_name -> openapi.v2.SecurityRequirement 20, // 69: openapi.v2.Operation.vendor_extension:type_name -> openapi.v2.NamedAny 4, // 70: openapi.v2.Parameter.body_parameter:type_name -> openapi.v2.BodyParameter 30, // 71: openapi.v2.Parameter.non_body_parameter:type_name -> openapi.v2.NonBodyParameter 22, // 72: openapi.v2.ParameterDefinitions.additional_properties:type_name -> openapi.v2.NamedParameter 37, // 73: openapi.v2.ParametersItem.parameter:type_name -> openapi.v2.Parameter 18, // 74: openapi.v2.ParametersItem.json_reference:type_name -> openapi.v2.JsonReference 36, // 75: openapi.v2.PathItem.get:type_name -> openapi.v2.Operation 36, // 76: openapi.v2.PathItem.put:type_name -> openapi.v2.Operation 36, // 77: openapi.v2.PathItem.post:type_name -> openapi.v2.Operation 36, // 78: openapi.v2.PathItem.delete:type_name -> openapi.v2.Operation 36, // 79: openapi.v2.PathItem.options:type_name -> openapi.v2.Operation 36, // 80: openapi.v2.PathItem.head:type_name -> openapi.v2.Operation 36, // 81: openapi.v2.PathItem.patch:type_name -> openapi.v2.Operation 39, // 82: openapi.v2.PathItem.parameters:type_name -> openapi.v2.ParametersItem 20, // 83: openapi.v2.PathItem.vendor_extension:type_name -> openapi.v2.NamedAny 43, // 84: openapi.v2.PathParameterSubSchema.items:type_name -> openapi.v2.PrimitivesItems 1, // 85: openapi.v2.PathParameterSubSchema.default:type_name -> openapi.v2.Any 1, // 86: openapi.v2.PathParameterSubSchema.enum:type_name -> openapi.v2.Any 20, // 87: openapi.v2.PathParameterSubSchema.vendor_extension:type_name -> openapi.v2.NamedAny 20, // 88: openapi.v2.Paths.vendor_extension:type_name -> openapi.v2.NamedAny 23, // 89: openapi.v2.Paths.path:type_name -> openapi.v2.NamedPathItem 43, // 90: openapi.v2.PrimitivesItems.items:type_name -> openapi.v2.PrimitivesItems 1, // 91: openapi.v2.PrimitivesItems.default:type_name -> openapi.v2.Any 1, // 92: openapi.v2.PrimitivesItems.enum:type_name -> openapi.v2.Any 20, // 93: openapi.v2.PrimitivesItems.vendor_extension:type_name -> openapi.v2.NamedAny 26, // 94: openapi.v2.Properties.additional_properties:type_name -> openapi.v2.NamedSchema 43, // 95: openapi.v2.QueryParameterSubSchema.items:type_name -> openapi.v2.PrimitivesItems 1, // 96: openapi.v2.QueryParameterSubSchema.default:type_name -> openapi.v2.Any 1, // 97: openapi.v2.QueryParameterSubSchema.enum:type_name -> openapi.v2.Any 20, // 98: openapi.v2.QueryParameterSubSchema.vendor_extension:type_name -> openapi.v2.NamedAny 51, // 99: openapi.v2.Response.schema:type_name -> openapi.v2.SchemaItem 15, // 100: openapi.v2.Response.headers:type_name -> openapi.v2.Headers 9, // 101: openapi.v2.Response.examples:type_name -> openapi.v2.Examples 20, // 102: openapi.v2.Response.vendor_extension:type_name -> openapi.v2.NamedAny 24, // 103: openapi.v2.ResponseDefinitions.additional_properties:type_name -> openapi.v2.NamedResponse 46, // 104: openapi.v2.ResponseValue.response:type_name -> openapi.v2.Response 18, // 105: openapi.v2.ResponseValue.json_reference:type_name -> openapi.v2.JsonReference 25, // 106: openapi.v2.Responses.response_code:type_name -> openapi.v2.NamedResponseValue 20, // 107: openapi.v2.Responses.vendor_extension:type_name -> openapi.v2.NamedAny 1, // 108: openapi.v2.Schema.default:type_name -> openapi.v2.Any 1, // 109: openapi.v2.Schema.enum:type_name -> openapi.v2.Any 0, // 110: openapi.v2.Schema.additional_properties:type_name -> openapi.v2.AdditionalPropertiesItem 57, // 111: openapi.v2.Schema.type:type_name -> openapi.v2.TypeItem 17, // 112: openapi.v2.Schema.items:type_name -> openapi.v2.ItemsItem 50, // 113: openapi.v2.Schema.all_of:type_name -> openapi.v2.Schema 44, // 114: openapi.v2.Schema.properties:type_name -> openapi.v2.Properties 59, // 115: openapi.v2.Schema.xml:type_name -> openapi.v2.Xml 10, // 116: openapi.v2.Schema.external_docs:type_name -> openapi.v2.ExternalDocs 1, // 117: openapi.v2.Schema.example:type_name -> openapi.v2.Any 20, // 118: openapi.v2.Schema.vendor_extension:type_name -> openapi.v2.NamedAny 50, // 119: openapi.v2.SchemaItem.schema:type_name -> openapi.v2.Schema 11, // 120: openapi.v2.SchemaItem.file_schema:type_name -> openapi.v2.FileSchema 27, // 121: openapi.v2.SecurityDefinitions.additional_properties:type_name -> openapi.v2.NamedSecurityDefinitionsItem 3, // 122: openapi.v2.SecurityDefinitionsItem.basic_authentication_security:type_name -> openapi.v2.BasicAuthenticationSecurity 2, // 123: openapi.v2.SecurityDefinitionsItem.api_key_security:type_name -> openapi.v2.ApiKeySecurity 33, // 124: openapi.v2.SecurityDefinitionsItem.oauth2_implicit_security:type_name -> openapi.v2.Oauth2ImplicitSecurity 34, // 125: openapi.v2.SecurityDefinitionsItem.oauth2_password_security:type_name -> openapi.v2.Oauth2PasswordSecurity 32, // 126: openapi.v2.SecurityDefinitionsItem.oauth2_application_security:type_name -> openapi.v2.Oauth2ApplicationSecurity 31, // 127: openapi.v2.SecurityDefinitionsItem.oauth2_access_code_security:type_name -> openapi.v2.Oauth2AccessCodeSecurity 29, // 128: openapi.v2.SecurityRequirement.additional_properties:type_name -> openapi.v2.NamedStringArray 10, // 129: openapi.v2.Tag.external_docs:type_name -> openapi.v2.ExternalDocs 20, // 130: openapi.v2.Tag.vendor_extension:type_name -> openapi.v2.NamedAny 20, // 131: openapi.v2.VendorExtension.additional_properties:type_name -> openapi.v2.NamedAny 20, // 132: openapi.v2.Xml.vendor_extension:type_name -> openapi.v2.NamedAny 133, // [133:133] is the sub-list for method output_type 133, // [133:133] is the sub-list for method input_type 133, // [133:133] is the sub-list for extension type_name 133, // [133:133] is the sub-list for extension extendee 0, // [0:133] is the sub-list for field type_name } func init() { file_openapiv2_OpenAPIv2_proto_init() } func file_openapiv2_OpenAPIv2_proto_init() { if File_openapiv2_OpenAPIv2_proto != nil { return } if !protoimpl.UnsafeEnabled { file_openapiv2_OpenAPIv2_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AdditionalPropertiesItem); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Any); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ApiKeySecurity); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*BasicAuthenticationSecurity); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*BodyParameter); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Contact); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Default); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Definitions); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Document); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Examples); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ExternalDocs); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*FileSchema); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*FormDataParameterSubSchema); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Header); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HeaderParameterSubSchema); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Headers); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Info); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ItemsItem); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*JsonReference); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*License); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedAny); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedHeader); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedParameter); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedPathItem); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedResponse); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedResponseValue); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedSchema); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedSecurityDefinitionsItem); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedString); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedStringArray); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NonBodyParameter); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Oauth2AccessCodeSecurity); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Oauth2ApplicationSecurity); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Oauth2ImplicitSecurity); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Oauth2PasswordSecurity); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Oauth2Scopes); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Operation); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Parameter); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ParameterDefinitions); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ParametersItem); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PathItem); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PathParameterSubSchema); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Paths); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PrimitivesItems); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Properties); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*QueryParameterSubSchema); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Response); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ResponseDefinitions); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ResponseValue); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Responses); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Schema); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SchemaItem); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SecurityDefinitions); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SecurityDefinitionsItem); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SecurityRequirement); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*StringArray); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Tag); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TypeItem); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*VendorExtension); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv2_OpenAPIv2_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Xml); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } file_openapiv2_OpenAPIv2_proto_msgTypes[0].OneofWrappers = []interface{}{ (*AdditionalPropertiesItem_Schema)(nil), (*AdditionalPropertiesItem_Boolean)(nil), } file_openapiv2_OpenAPIv2_proto_msgTypes[30].OneofWrappers = []interface{}{ (*NonBodyParameter_HeaderParameterSubSchema)(nil), (*NonBodyParameter_FormDataParameterSubSchema)(nil), (*NonBodyParameter_QueryParameterSubSchema)(nil), (*NonBodyParameter_PathParameterSubSchema)(nil), } file_openapiv2_OpenAPIv2_proto_msgTypes[37].OneofWrappers = []interface{}{ (*Parameter_BodyParameter)(nil), (*Parameter_NonBodyParameter)(nil), } file_openapiv2_OpenAPIv2_proto_msgTypes[39].OneofWrappers = []interface{}{ (*ParametersItem_Parameter)(nil), (*ParametersItem_JsonReference)(nil), } file_openapiv2_OpenAPIv2_proto_msgTypes[48].OneofWrappers = []interface{}{ (*ResponseValue_Response)(nil), (*ResponseValue_JsonReference)(nil), } file_openapiv2_OpenAPIv2_proto_msgTypes[51].OneofWrappers = []interface{}{ (*SchemaItem_Schema)(nil), (*SchemaItem_FileSchema)(nil), } file_openapiv2_OpenAPIv2_proto_msgTypes[53].OneofWrappers = []interface{}{ (*SecurityDefinitionsItem_BasicAuthenticationSecurity)(nil), (*SecurityDefinitionsItem_ApiKeySecurity)(nil), (*SecurityDefinitionsItem_Oauth2ImplicitSecurity)(nil), (*SecurityDefinitionsItem_Oauth2PasswordSecurity)(nil), (*SecurityDefinitionsItem_Oauth2ApplicationSecurity)(nil), (*SecurityDefinitionsItem_Oauth2AccessCodeSecurity)(nil), } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_openapiv2_OpenAPIv2_proto_rawDesc, NumEnums: 0, NumMessages: 60, NumExtensions: 0, NumServices: 0, }, GoTypes: file_openapiv2_OpenAPIv2_proto_goTypes, DependencyIndexes: file_openapiv2_OpenAPIv2_proto_depIdxs, MessageInfos: file_openapiv2_OpenAPIv2_proto_msgTypes, }.Build() File_openapiv2_OpenAPIv2_proto = out.File file_openapiv2_OpenAPIv2_proto_rawDesc = nil file_openapiv2_OpenAPIv2_proto_goTypes = nil file_openapiv2_OpenAPIv2_proto_depIdxs = nil } ================================================ FILE: vendor/github.com/google/gnostic/openapiv2/OpenAPIv2.proto ================================================ // Copyright 2020 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // THIS FILE IS AUTOMATICALLY GENERATED. syntax = "proto3"; package openapi.v2; import "google/protobuf/any.proto"; // This option lets the proto compiler generate Java code inside the package // name (see below) instead of inside an outer class. It creates a simpler // developer experience by reducing one-level of name nesting and be // consistent with most programming languages that don't support outer classes. option java_multiple_files = true; // The Java outer classname should be the filename in UpperCamelCase. This // class is only used to hold proto descriptor, so developers don't need to // work with it directly. option java_outer_classname = "OpenAPIProto"; // The Java package name must be proto package name with proper prefix. option java_package = "org.openapi_v2"; // A reasonable prefix for the Objective-C symbols generated from the package. // It should at a minimum be 3 characters long, all uppercase, and convention // is to use an abbreviation of the package name. Something short, but // hopefully unique enough to not conflict with things that may come along in // the future. 'GPB' is reserved for the protocol buffer implementation itself. option objc_class_prefix = "OAS"; // The Go package name. option go_package = "./openapiv2;openapi_v2"; message AdditionalPropertiesItem { oneof oneof { Schema schema = 1; bool boolean = 2; } } message Any { google.protobuf.Any value = 1; string yaml = 2; } message ApiKeySecurity { string type = 1; string name = 2; string in = 3; string description = 4; repeated NamedAny vendor_extension = 5; } message BasicAuthenticationSecurity { string type = 1; string description = 2; repeated NamedAny vendor_extension = 3; } message BodyParameter { // A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed. string description = 1; // The name of the parameter. string name = 2; // Determines the location of the parameter. string in = 3; // Determines whether or not this parameter is required or optional. bool required = 4; Schema schema = 5; repeated NamedAny vendor_extension = 6; } // Contact information for the owners of the API. message Contact { // The identifying name of the contact person/organization. string name = 1; // The URL pointing to the contact information. string url = 2; // The email address of the contact person/organization. string email = 3; repeated NamedAny vendor_extension = 4; } message Default { repeated NamedAny additional_properties = 1; } // One or more JSON objects describing the schemas being consumed and produced by the API. message Definitions { repeated NamedSchema additional_properties = 1; } message Document { // The Swagger version of this document. string swagger = 1; Info info = 2; // The host (name or ip) of the API. Example: 'swagger.io' string host = 3; // The base path to the API. Example: '/api'. string base_path = 4; // The transfer protocol of the API. repeated string schemes = 5; // A list of MIME types accepted by the API. repeated string consumes = 6; // A list of MIME types the API can produce. repeated string produces = 7; Paths paths = 8; Definitions definitions = 9; ParameterDefinitions parameters = 10; ResponseDefinitions responses = 11; repeated SecurityRequirement security = 12; SecurityDefinitions security_definitions = 13; repeated Tag tags = 14; ExternalDocs external_docs = 15; repeated NamedAny vendor_extension = 16; } message Examples { repeated NamedAny additional_properties = 1; } // information about external documentation message ExternalDocs { string description = 1; string url = 2; repeated NamedAny vendor_extension = 3; } // A deterministic version of a JSON Schema object. message FileSchema { string format = 1; string title = 2; string description = 3; Any default = 4; repeated string required = 5; string type = 6; bool read_only = 7; ExternalDocs external_docs = 8; Any example = 9; repeated NamedAny vendor_extension = 10; } message FormDataParameterSubSchema { // Determines whether or not this parameter is required or optional. bool required = 1; // Determines the location of the parameter. string in = 2; // A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed. string description = 3; // The name of the parameter. string name = 4; // allows sending a parameter by name only or with an empty value. bool allow_empty_value = 5; string type = 6; string format = 7; PrimitivesItems items = 8; string collection_format = 9; Any default = 10; double maximum = 11; bool exclusive_maximum = 12; double minimum = 13; bool exclusive_minimum = 14; int64 max_length = 15; int64 min_length = 16; string pattern = 17; int64 max_items = 18; int64 min_items = 19; bool unique_items = 20; repeated Any enum = 21; double multiple_of = 22; repeated NamedAny vendor_extension = 23; } message Header { string type = 1; string format = 2; PrimitivesItems items = 3; string collection_format = 4; Any default = 5; double maximum = 6; bool exclusive_maximum = 7; double minimum = 8; bool exclusive_minimum = 9; int64 max_length = 10; int64 min_length = 11; string pattern = 12; int64 max_items = 13; int64 min_items = 14; bool unique_items = 15; repeated Any enum = 16; double multiple_of = 17; string description = 18; repeated NamedAny vendor_extension = 19; } message HeaderParameterSubSchema { // Determines whether or not this parameter is required or optional. bool required = 1; // Determines the location of the parameter. string in = 2; // A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed. string description = 3; // The name of the parameter. string name = 4; string type = 5; string format = 6; PrimitivesItems items = 7; string collection_format = 8; Any default = 9; double maximum = 10; bool exclusive_maximum = 11; double minimum = 12; bool exclusive_minimum = 13; int64 max_length = 14; int64 min_length = 15; string pattern = 16; int64 max_items = 17; int64 min_items = 18; bool unique_items = 19; repeated Any enum = 20; double multiple_of = 21; repeated NamedAny vendor_extension = 22; } message Headers { repeated NamedHeader additional_properties = 1; } // General information about the API. message Info { // A unique and precise title of the API. string title = 1; // A semantic version number of the API. string version = 2; // A longer description of the API. Should be different from the title. GitHub Flavored Markdown is allowed. string description = 3; // The terms of service for the API. string terms_of_service = 4; Contact contact = 5; License license = 6; repeated NamedAny vendor_extension = 7; } message ItemsItem { repeated Schema schema = 1; } message JsonReference { string _ref = 1; string description = 2; } message License { // The name of the license type. It's encouraged to use an OSI compatible license. string name = 1; // The URL pointing to the license. string url = 2; repeated NamedAny vendor_extension = 3; } // Automatically-generated message used to represent maps of Any as ordered (name,value) pairs. message NamedAny { // Map key string name = 1; // Mapped value Any value = 2; } // Automatically-generated message used to represent maps of Header as ordered (name,value) pairs. message NamedHeader { // Map key string name = 1; // Mapped value Header value = 2; } // Automatically-generated message used to represent maps of Parameter as ordered (name,value) pairs. message NamedParameter { // Map key string name = 1; // Mapped value Parameter value = 2; } // Automatically-generated message used to represent maps of PathItem as ordered (name,value) pairs. message NamedPathItem { // Map key string name = 1; // Mapped value PathItem value = 2; } // Automatically-generated message used to represent maps of Response as ordered (name,value) pairs. message NamedResponse { // Map key string name = 1; // Mapped value Response value = 2; } // Automatically-generated message used to represent maps of ResponseValue as ordered (name,value) pairs. message NamedResponseValue { // Map key string name = 1; // Mapped value ResponseValue value = 2; } // Automatically-generated message used to represent maps of Schema as ordered (name,value) pairs. message NamedSchema { // Map key string name = 1; // Mapped value Schema value = 2; } // Automatically-generated message used to represent maps of SecurityDefinitionsItem as ordered (name,value) pairs. message NamedSecurityDefinitionsItem { // Map key string name = 1; // Mapped value SecurityDefinitionsItem value = 2; } // Automatically-generated message used to represent maps of string as ordered (name,value) pairs. message NamedString { // Map key string name = 1; // Mapped value string value = 2; } // Automatically-generated message used to represent maps of StringArray as ordered (name,value) pairs. message NamedStringArray { // Map key string name = 1; // Mapped value StringArray value = 2; } message NonBodyParameter { oneof oneof { HeaderParameterSubSchema header_parameter_sub_schema = 1; FormDataParameterSubSchema form_data_parameter_sub_schema = 2; QueryParameterSubSchema query_parameter_sub_schema = 3; PathParameterSubSchema path_parameter_sub_schema = 4; } } message Oauth2AccessCodeSecurity { string type = 1; string flow = 2; Oauth2Scopes scopes = 3; string authorization_url = 4; string token_url = 5; string description = 6; repeated NamedAny vendor_extension = 7; } message Oauth2ApplicationSecurity { string type = 1; string flow = 2; Oauth2Scopes scopes = 3; string token_url = 4; string description = 5; repeated NamedAny vendor_extension = 6; } message Oauth2ImplicitSecurity { string type = 1; string flow = 2; Oauth2Scopes scopes = 3; string authorization_url = 4; string description = 5; repeated NamedAny vendor_extension = 6; } message Oauth2PasswordSecurity { string type = 1; string flow = 2; Oauth2Scopes scopes = 3; string token_url = 4; string description = 5; repeated NamedAny vendor_extension = 6; } message Oauth2Scopes { repeated NamedString additional_properties = 1; } message Operation { repeated string tags = 1; // A brief summary of the operation. string summary = 2; // A longer description of the operation, GitHub Flavored Markdown is allowed. string description = 3; ExternalDocs external_docs = 4; // A unique identifier of the operation. string operation_id = 5; // A list of MIME types the API can produce. repeated string produces = 6; // A list of MIME types the API can consume. repeated string consumes = 7; // The parameters needed to send a valid API call. repeated ParametersItem parameters = 8; Responses responses = 9; // The transfer protocol of the API. repeated string schemes = 10; bool deprecated = 11; repeated SecurityRequirement security = 12; repeated NamedAny vendor_extension = 13; } message Parameter { oneof oneof { BodyParameter body_parameter = 1; NonBodyParameter non_body_parameter = 2; } } // One or more JSON representations for parameters message ParameterDefinitions { repeated NamedParameter additional_properties = 1; } message ParametersItem { oneof oneof { Parameter parameter = 1; JsonReference json_reference = 2; } } message PathItem { string _ref = 1; Operation get = 2; Operation put = 3; Operation post = 4; Operation delete = 5; Operation options = 6; Operation head = 7; Operation patch = 8; // The parameters needed to send a valid API call. repeated ParametersItem parameters = 9; repeated NamedAny vendor_extension = 10; } message PathParameterSubSchema { // Determines whether or not this parameter is required or optional. bool required = 1; // Determines the location of the parameter. string in = 2; // A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed. string description = 3; // The name of the parameter. string name = 4; string type = 5; string format = 6; PrimitivesItems items = 7; string collection_format = 8; Any default = 9; double maximum = 10; bool exclusive_maximum = 11; double minimum = 12; bool exclusive_minimum = 13; int64 max_length = 14; int64 min_length = 15; string pattern = 16; int64 max_items = 17; int64 min_items = 18; bool unique_items = 19; repeated Any enum = 20; double multiple_of = 21; repeated NamedAny vendor_extension = 22; } // Relative paths to the individual endpoints. They must be relative to the 'basePath'. message Paths { repeated NamedAny vendor_extension = 1; repeated NamedPathItem path = 2; } message PrimitivesItems { string type = 1; string format = 2; PrimitivesItems items = 3; string collection_format = 4; Any default = 5; double maximum = 6; bool exclusive_maximum = 7; double minimum = 8; bool exclusive_minimum = 9; int64 max_length = 10; int64 min_length = 11; string pattern = 12; int64 max_items = 13; int64 min_items = 14; bool unique_items = 15; repeated Any enum = 16; double multiple_of = 17; repeated NamedAny vendor_extension = 18; } message Properties { repeated NamedSchema additional_properties = 1; } message QueryParameterSubSchema { // Determines whether or not this parameter is required or optional. bool required = 1; // Determines the location of the parameter. string in = 2; // A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed. string description = 3; // The name of the parameter. string name = 4; // allows sending a parameter by name only or with an empty value. bool allow_empty_value = 5; string type = 6; string format = 7; PrimitivesItems items = 8; string collection_format = 9; Any default = 10; double maximum = 11; bool exclusive_maximum = 12; double minimum = 13; bool exclusive_minimum = 14; int64 max_length = 15; int64 min_length = 16; string pattern = 17; int64 max_items = 18; int64 min_items = 19; bool unique_items = 20; repeated Any enum = 21; double multiple_of = 22; repeated NamedAny vendor_extension = 23; } message Response { string description = 1; SchemaItem schema = 2; Headers headers = 3; Examples examples = 4; repeated NamedAny vendor_extension = 5; } // One or more JSON representations for responses message ResponseDefinitions { repeated NamedResponse additional_properties = 1; } message ResponseValue { oneof oneof { Response response = 1; JsonReference json_reference = 2; } } // Response objects names can either be any valid HTTP status code or 'default'. message Responses { repeated NamedResponseValue response_code = 1; repeated NamedAny vendor_extension = 2; } // A deterministic version of a JSON Schema object. message Schema { string _ref = 1; string format = 2; string title = 3; string description = 4; Any default = 5; double multiple_of = 6; double maximum = 7; bool exclusive_maximum = 8; double minimum = 9; bool exclusive_minimum = 10; int64 max_length = 11; int64 min_length = 12; string pattern = 13; int64 max_items = 14; int64 min_items = 15; bool unique_items = 16; int64 max_properties = 17; int64 min_properties = 18; repeated string required = 19; repeated Any enum = 20; AdditionalPropertiesItem additional_properties = 21; TypeItem type = 22; ItemsItem items = 23; repeated Schema all_of = 24; Properties properties = 25; string discriminator = 26; bool read_only = 27; Xml xml = 28; ExternalDocs external_docs = 29; Any example = 30; repeated NamedAny vendor_extension = 31; } message SchemaItem { oneof oneof { Schema schema = 1; FileSchema file_schema = 2; } } message SecurityDefinitions { repeated NamedSecurityDefinitionsItem additional_properties = 1; } message SecurityDefinitionsItem { oneof oneof { BasicAuthenticationSecurity basic_authentication_security = 1; ApiKeySecurity api_key_security = 2; Oauth2ImplicitSecurity oauth2_implicit_security = 3; Oauth2PasswordSecurity oauth2_password_security = 4; Oauth2ApplicationSecurity oauth2_application_security = 5; Oauth2AccessCodeSecurity oauth2_access_code_security = 6; } } message SecurityRequirement { repeated NamedStringArray additional_properties = 1; } message StringArray { repeated string value = 1; } message Tag { string name = 1; string description = 2; ExternalDocs external_docs = 3; repeated NamedAny vendor_extension = 4; } message TypeItem { repeated string value = 1; } // Any property starting with x- is valid. message VendorExtension { repeated NamedAny additional_properties = 1; } message Xml { string name = 1; string namespace = 2; string prefix = 3; bool attribute = 4; bool wrapped = 5; repeated NamedAny vendor_extension = 6; } ================================================ FILE: vendor/github.com/google/gnostic/openapiv2/README.md ================================================ # OpenAPI v2 Protocol Buffer Models This directory contains a Protocol Buffer-language model and related code for supporting OpenAPI v2. Gnostic applications and plugins can use OpenAPIv2.proto to generate Protocol Buffer support code for their preferred languages. OpenAPIv2.go is used by Gnostic to read JSON and YAML OpenAPI descriptions into the Protocol Buffer-based datastructures generated from OpenAPIv2.proto. OpenAPIv2.proto and OpenAPIv2.go are generated by the Gnostic compiler generator, and OpenAPIv2.pb.go is generated by protoc, the Protocol Buffer compiler, and protoc-gen-go, the Protocol Buffer Go code generation plugin. ================================================ FILE: vendor/github.com/google/gnostic/openapiv2/document.go ================================================ // Copyright 2020 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package openapi_v2 import ( "gopkg.in/yaml.v3" "github.com/google/gnostic/compiler" ) // ParseDocument reads an OpenAPI v2 description from a YAML/JSON representation. func ParseDocument(b []byte) (*Document, error) { info, err := compiler.ReadInfoFromBytes("", b) if err != nil { return nil, err } root := info.Content[0] return NewDocument(root, compiler.NewContextWithExtensions("$root", root, nil, nil)) } // YAMLValue produces a serialized YAML representation of the document. func (d *Document) YAMLValue(comment string) ([]byte, error) { rawInfo := d.ToRawInfo() rawInfo = &yaml.Node{ Kind: yaml.DocumentNode, Content: []*yaml.Node{rawInfo}, HeadComment: comment, } return yaml.Marshal(rawInfo) } ================================================ FILE: vendor/github.com/google/gnostic/openapiv2/openapi-2.0.json ================================================ { "title": "A JSON Schema for Swagger 2.0 API.", "id": "http://swagger.io/v2/schema.json#", "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "required": [ "swagger", "info", "paths" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "swagger": { "type": "string", "enum": [ "2.0" ], "description": "The Swagger version of this document." }, "info": { "$ref": "#/definitions/info" }, "host": { "type": "string", "pattern": "^[^{}/ :\\\\]+(?::\\d+)?$", "description": "The host (name or ip) of the API. Example: 'swagger.io'" }, "basePath": { "type": "string", "pattern": "^/", "description": "The base path to the API. Example: '/api'." }, "schemes": { "$ref": "#/definitions/schemesList" }, "consumes": { "description": "A list of MIME types accepted by the API.", "allOf": [ { "$ref": "#/definitions/mediaTypeList" } ] }, "produces": { "description": "A list of MIME types the API can produce.", "allOf": [ { "$ref": "#/definitions/mediaTypeList" } ] }, "paths": { "$ref": "#/definitions/paths" }, "definitions": { "$ref": "#/definitions/definitions" }, "parameters": { "$ref": "#/definitions/parameterDefinitions" }, "responses": { "$ref": "#/definitions/responseDefinitions" }, "security": { "$ref": "#/definitions/security" }, "securityDefinitions": { "$ref": "#/definitions/securityDefinitions" }, "tags": { "type": "array", "items": { "$ref": "#/definitions/tag" }, "uniqueItems": true }, "externalDocs": { "$ref": "#/definitions/externalDocs" } }, "definitions": { "info": { "type": "object", "description": "General information about the API.", "required": [ "version", "title" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "title": { "type": "string", "description": "A unique and precise title of the API." }, "version": { "type": "string", "description": "A semantic version number of the API." }, "description": { "type": "string", "description": "A longer description of the API. Should be different from the title. GitHub Flavored Markdown is allowed." }, "termsOfService": { "type": "string", "description": "The terms of service for the API." }, "contact": { "$ref": "#/definitions/contact" }, "license": { "$ref": "#/definitions/license" } } }, "contact": { "type": "object", "description": "Contact information for the owners of the API.", "additionalProperties": false, "properties": { "name": { "type": "string", "description": "The identifying name of the contact person/organization." }, "url": { "type": "string", "description": "The URL pointing to the contact information.", "format": "uri" }, "email": { "type": "string", "description": "The email address of the contact person/organization.", "format": "email" } }, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } } }, "license": { "type": "object", "required": [ "name" ], "additionalProperties": false, "properties": { "name": { "type": "string", "description": "The name of the license type. It's encouraged to use an OSI compatible license." }, "url": { "type": "string", "description": "The URL pointing to the license.", "format": "uri" } }, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } } }, "paths": { "type": "object", "description": "Relative paths to the individual endpoints. They must be relative to the 'basePath'.", "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" }, "^/": { "$ref": "#/definitions/pathItem" } }, "additionalProperties": false }, "definitions": { "type": "object", "additionalProperties": { "$ref": "#/definitions/schema" }, "description": "One or more JSON objects describing the schemas being consumed and produced by the API." }, "parameterDefinitions": { "type": "object", "additionalProperties": { "$ref": "#/definitions/parameter" }, "description": "One or more JSON representations for parameters" }, "responseDefinitions": { "type": "object", "additionalProperties": { "$ref": "#/definitions/response" }, "description": "One or more JSON representations for responses" }, "externalDocs": { "type": "object", "additionalProperties": false, "description": "information about external documentation", "required": [ "url" ], "properties": { "description": { "type": "string" }, "url": { "type": "string", "format": "uri" } }, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } } }, "examples": { "type": "object", "additionalProperties": true }, "mimeType": { "type": "string", "description": "The MIME type of the HTTP message." }, "operation": { "type": "object", "required": [ "responses" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "tags": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }, "summary": { "type": "string", "description": "A brief summary of the operation." }, "description": { "type": "string", "description": "A longer description of the operation, GitHub Flavored Markdown is allowed." }, "externalDocs": { "$ref": "#/definitions/externalDocs" }, "operationId": { "type": "string", "description": "A unique identifier of the operation." }, "produces": { "description": "A list of MIME types the API can produce.", "allOf": [ { "$ref": "#/definitions/mediaTypeList" } ] }, "consumes": { "description": "A list of MIME types the API can consume.", "allOf": [ { "$ref": "#/definitions/mediaTypeList" } ] }, "parameters": { "$ref": "#/definitions/parametersList" }, "responses": { "$ref": "#/definitions/responses" }, "schemes": { "$ref": "#/definitions/schemesList" }, "deprecated": { "type": "boolean", "default": false }, "security": { "$ref": "#/definitions/security" } } }, "pathItem": { "type": "object", "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "$ref": { "type": "string" }, "get": { "$ref": "#/definitions/operation" }, "put": { "$ref": "#/definitions/operation" }, "post": { "$ref": "#/definitions/operation" }, "delete": { "$ref": "#/definitions/operation" }, "options": { "$ref": "#/definitions/operation" }, "head": { "$ref": "#/definitions/operation" }, "patch": { "$ref": "#/definitions/operation" }, "parameters": { "$ref": "#/definitions/parametersList" } } }, "responses": { "type": "object", "description": "Response objects names can either be any valid HTTP status code or 'default'.", "minProperties": 1, "additionalProperties": false, "patternProperties": { "^([0-9]{3})$|^(default)$": { "$ref": "#/definitions/responseValue" }, "^x-": { "$ref": "#/definitions/vendorExtension" } }, "not": { "type": "object", "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } } } }, "responseValue": { "oneOf": [ { "$ref": "#/definitions/response" }, { "$ref": "#/definitions/jsonReference" } ] }, "response": { "type": "object", "required": [ "description" ], "properties": { "description": { "type": "string" }, "schema": { "oneOf": [ { "$ref": "#/definitions/schema" }, { "$ref": "#/definitions/fileSchema" } ] }, "headers": { "$ref": "#/definitions/headers" }, "examples": { "$ref": "#/definitions/examples" } }, "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } } }, "headers": { "type": "object", "additionalProperties": { "$ref": "#/definitions/header" } }, "header": { "type": "object", "additionalProperties": false, "required": [ "type" ], "properties": { "type": { "type": "string", "enum": [ "string", "number", "integer", "boolean", "array" ] }, "format": { "type": "string" }, "items": { "$ref": "#/definitions/primitivesItems" }, "collectionFormat": { "$ref": "#/definitions/collectionFormat" }, "default": { "$ref": "#/definitions/default" }, "maximum": { "$ref": "#/definitions/maximum" }, "exclusiveMaximum": { "$ref": "#/definitions/exclusiveMaximum" }, "minimum": { "$ref": "#/definitions/minimum" }, "exclusiveMinimum": { "$ref": "#/definitions/exclusiveMinimum" }, "maxLength": { "$ref": "#/definitions/maxLength" }, "minLength": { "$ref": "#/definitions/minLength" }, "pattern": { "$ref": "#/definitions/pattern" }, "maxItems": { "$ref": "#/definitions/maxItems" }, "minItems": { "$ref": "#/definitions/minItems" }, "uniqueItems": { "$ref": "#/definitions/uniqueItems" }, "enum": { "$ref": "#/definitions/enum" }, "multipleOf": { "$ref": "#/definitions/multipleOf" }, "description": { "type": "string" } }, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } } }, "vendorExtension": { "description": "Any property starting with x- is valid.", "additionalProperties": true, "additionalItems": true }, "bodyParameter": { "type": "object", "required": [ "name", "in", "schema" ], "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "description": { "type": "string", "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed." }, "name": { "type": "string", "description": "The name of the parameter." }, "in": { "type": "string", "description": "Determines the location of the parameter.", "enum": [ "body" ] }, "required": { "type": "boolean", "description": "Determines whether or not this parameter is required or optional.", "default": false }, "schema": { "$ref": "#/definitions/schema" } }, "additionalProperties": false }, "headerParameterSubSchema": { "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "required": { "type": "boolean", "description": "Determines whether or not this parameter is required or optional.", "default": false }, "in": { "type": "string", "description": "Determines the location of the parameter.", "enum": [ "header" ] }, "description": { "type": "string", "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed." }, "name": { "type": "string", "description": "The name of the parameter." }, "type": { "type": "string", "enum": [ "string", "number", "boolean", "integer", "array" ] }, "format": { "type": "string" }, "items": { "$ref": "#/definitions/primitivesItems" }, "collectionFormat": { "$ref": "#/definitions/collectionFormat" }, "default": { "$ref": "#/definitions/default" }, "maximum": { "$ref": "#/definitions/maximum" }, "exclusiveMaximum": { "$ref": "#/definitions/exclusiveMaximum" }, "minimum": { "$ref": "#/definitions/minimum" }, "exclusiveMinimum": { "$ref": "#/definitions/exclusiveMinimum" }, "maxLength": { "$ref": "#/definitions/maxLength" }, "minLength": { "$ref": "#/definitions/minLength" }, "pattern": { "$ref": "#/definitions/pattern" }, "maxItems": { "$ref": "#/definitions/maxItems" }, "minItems": { "$ref": "#/definitions/minItems" }, "uniqueItems": { "$ref": "#/definitions/uniqueItems" }, "enum": { "$ref": "#/definitions/enum" }, "multipleOf": { "$ref": "#/definitions/multipleOf" } } }, "queryParameterSubSchema": { "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "required": { "type": "boolean", "description": "Determines whether or not this parameter is required or optional.", "default": false }, "in": { "type": "string", "description": "Determines the location of the parameter.", "enum": [ "query" ] }, "description": { "type": "string", "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed." }, "name": { "type": "string", "description": "The name of the parameter." }, "allowEmptyValue": { "type": "boolean", "default": false, "description": "allows sending a parameter by name only or with an empty value." }, "type": { "type": "string", "enum": [ "string", "number", "boolean", "integer", "array" ] }, "format": { "type": "string" }, "items": { "$ref": "#/definitions/primitivesItems" }, "collectionFormat": { "$ref": "#/definitions/collectionFormatWithMulti" }, "default": { "$ref": "#/definitions/default" }, "maximum": { "$ref": "#/definitions/maximum" }, "exclusiveMaximum": { "$ref": "#/definitions/exclusiveMaximum" }, "minimum": { "$ref": "#/definitions/minimum" }, "exclusiveMinimum": { "$ref": "#/definitions/exclusiveMinimum" }, "maxLength": { "$ref": "#/definitions/maxLength" }, "minLength": { "$ref": "#/definitions/minLength" }, "pattern": { "$ref": "#/definitions/pattern" }, "maxItems": { "$ref": "#/definitions/maxItems" }, "minItems": { "$ref": "#/definitions/minItems" }, "uniqueItems": { "$ref": "#/definitions/uniqueItems" }, "enum": { "$ref": "#/definitions/enum" }, "multipleOf": { "$ref": "#/definitions/multipleOf" } } }, "formDataParameterSubSchema": { "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "required": { "type": "boolean", "description": "Determines whether or not this parameter is required or optional.", "default": false }, "in": { "type": "string", "description": "Determines the location of the parameter.", "enum": [ "formData" ] }, "description": { "type": "string", "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed." }, "name": { "type": "string", "description": "The name of the parameter." }, "allowEmptyValue": { "type": "boolean", "default": false, "description": "allows sending a parameter by name only or with an empty value." }, "type": { "type": "string", "enum": [ "string", "number", "boolean", "integer", "array", "file" ] }, "format": { "type": "string" }, "items": { "$ref": "#/definitions/primitivesItems" }, "collectionFormat": { "$ref": "#/definitions/collectionFormatWithMulti" }, "default": { "$ref": "#/definitions/default" }, "maximum": { "$ref": "#/definitions/maximum" }, "exclusiveMaximum": { "$ref": "#/definitions/exclusiveMaximum" }, "minimum": { "$ref": "#/definitions/minimum" }, "exclusiveMinimum": { "$ref": "#/definitions/exclusiveMinimum" }, "maxLength": { "$ref": "#/definitions/maxLength" }, "minLength": { "$ref": "#/definitions/minLength" }, "pattern": { "$ref": "#/definitions/pattern" }, "maxItems": { "$ref": "#/definitions/maxItems" }, "minItems": { "$ref": "#/definitions/minItems" }, "uniqueItems": { "$ref": "#/definitions/uniqueItems" }, "enum": { "$ref": "#/definitions/enum" }, "multipleOf": { "$ref": "#/definitions/multipleOf" } } }, "pathParameterSubSchema": { "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "required": [ "required" ], "properties": { "required": { "type": "boolean", "enum": [ true ], "description": "Determines whether or not this parameter is required or optional." }, "in": { "type": "string", "description": "Determines the location of the parameter.", "enum": [ "path" ] }, "description": { "type": "string", "description": "A brief description of the parameter. This could contain examples of use. GitHub Flavored Markdown is allowed." }, "name": { "type": "string", "description": "The name of the parameter." }, "type": { "type": "string", "enum": [ "string", "number", "boolean", "integer", "array" ] }, "format": { "type": "string" }, "items": { "$ref": "#/definitions/primitivesItems" }, "collectionFormat": { "$ref": "#/definitions/collectionFormat" }, "default": { "$ref": "#/definitions/default" }, "maximum": { "$ref": "#/definitions/maximum" }, "exclusiveMaximum": { "$ref": "#/definitions/exclusiveMaximum" }, "minimum": { "$ref": "#/definitions/minimum" }, "exclusiveMinimum": { "$ref": "#/definitions/exclusiveMinimum" }, "maxLength": { "$ref": "#/definitions/maxLength" }, "minLength": { "$ref": "#/definitions/minLength" }, "pattern": { "$ref": "#/definitions/pattern" }, "maxItems": { "$ref": "#/definitions/maxItems" }, "minItems": { "$ref": "#/definitions/minItems" }, "uniqueItems": { "$ref": "#/definitions/uniqueItems" }, "enum": { "$ref": "#/definitions/enum" }, "multipleOf": { "$ref": "#/definitions/multipleOf" } } }, "nonBodyParameter": { "type": "object", "required": [ "name", "in", "type" ], "oneOf": [ { "$ref": "#/definitions/headerParameterSubSchema" }, { "$ref": "#/definitions/formDataParameterSubSchema" }, { "$ref": "#/definitions/queryParameterSubSchema" }, { "$ref": "#/definitions/pathParameterSubSchema" } ] }, "parameter": { "oneOf": [ { "$ref": "#/definitions/bodyParameter" }, { "$ref": "#/definitions/nonBodyParameter" } ] }, "schema": { "type": "object", "description": "A deterministic version of a JSON Schema object.", "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "properties": { "$ref": { "type": "string" }, "format": { "type": "string" }, "title": { "$ref": "http://json-schema.org/draft-04/schema#/properties/title" }, "description": { "$ref": "http://json-schema.org/draft-04/schema#/properties/description" }, "default": { "$ref": "http://json-schema.org/draft-04/schema#/properties/default" }, "multipleOf": { "$ref": "http://json-schema.org/draft-04/schema#/properties/multipleOf" }, "maximum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/maximum" }, "exclusiveMaximum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMaximum" }, "minimum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/minimum" }, "exclusiveMinimum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMinimum" }, "maxLength": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" }, "minLength": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" }, "pattern": { "$ref": "http://json-schema.org/draft-04/schema#/properties/pattern" }, "maxItems": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" }, "minItems": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" }, "uniqueItems": { "$ref": "http://json-schema.org/draft-04/schema#/properties/uniqueItems" }, "maxProperties": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" }, "minProperties": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" }, "required": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/stringArray" }, "enum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/enum" }, "additionalProperties": { "oneOf": [ { "$ref": "#/definitions/schema" }, { "type": "boolean" } ], "default": {} }, "type": { "$ref": "http://json-schema.org/draft-04/schema#/properties/type" }, "items": { "anyOf": [ { "$ref": "#/definitions/schema" }, { "type": "array", "minItems": 1, "items": { "$ref": "#/definitions/schema" } } ], "default": {} }, "allOf": { "type": "array", "minItems": 1, "items": { "$ref": "#/definitions/schema" } }, "properties": { "type": "object", "additionalProperties": { "$ref": "#/definitions/schema" }, "default": {} }, "discriminator": { "type": "string" }, "readOnly": { "type": "boolean", "default": false }, "xml": { "$ref": "#/definitions/xml" }, "externalDocs": { "$ref": "#/definitions/externalDocs" }, "example": {} }, "additionalProperties": false }, "fileSchema": { "type": "object", "description": "A deterministic version of a JSON Schema object.", "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } }, "required": [ "type" ], "properties": { "format": { "type": "string" }, "title": { "$ref": "http://json-schema.org/draft-04/schema#/properties/title" }, "description": { "$ref": "http://json-schema.org/draft-04/schema#/properties/description" }, "default": { "$ref": "http://json-schema.org/draft-04/schema#/properties/default" }, "required": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/stringArray" }, "type": { "type": "string", "enum": [ "file" ] }, "readOnly": { "type": "boolean", "default": false }, "externalDocs": { "$ref": "#/definitions/externalDocs" }, "example": {} }, "additionalProperties": false }, "primitivesItems": { "type": "object", "additionalProperties": false, "properties": { "type": { "type": "string", "enum": [ "string", "number", "integer", "boolean", "array" ] }, "format": { "type": "string" }, "items": { "$ref": "#/definitions/primitivesItems" }, "collectionFormat": { "$ref": "#/definitions/collectionFormat" }, "default": { "$ref": "#/definitions/default" }, "maximum": { "$ref": "#/definitions/maximum" }, "exclusiveMaximum": { "$ref": "#/definitions/exclusiveMaximum" }, "minimum": { "$ref": "#/definitions/minimum" }, "exclusiveMinimum": { "$ref": "#/definitions/exclusiveMinimum" }, "maxLength": { "$ref": "#/definitions/maxLength" }, "minLength": { "$ref": "#/definitions/minLength" }, "pattern": { "$ref": "#/definitions/pattern" }, "maxItems": { "$ref": "#/definitions/maxItems" }, "minItems": { "$ref": "#/definitions/minItems" }, "uniqueItems": { "$ref": "#/definitions/uniqueItems" }, "enum": { "$ref": "#/definitions/enum" }, "multipleOf": { "$ref": "#/definitions/multipleOf" } }, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } } }, "security": { "type": "array", "items": { "$ref": "#/definitions/securityRequirement" }, "uniqueItems": true }, "securityRequirement": { "type": "object", "additionalProperties": { "type": "array", "items": { "type": "string" }, "uniqueItems": true } }, "xml": { "type": "object", "additionalProperties": false, "properties": { "name": { "type": "string" }, "namespace": { "type": "string" }, "prefix": { "type": "string" }, "attribute": { "type": "boolean", "default": false }, "wrapped": { "type": "boolean", "default": false } }, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } } }, "tag": { "type": "object", "additionalProperties": false, "required": [ "name" ], "properties": { "name": { "type": "string" }, "description": { "type": "string" }, "externalDocs": { "$ref": "#/definitions/externalDocs" } }, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } } }, "securityDefinitions": { "type": "object", "additionalProperties": { "oneOf": [ { "$ref": "#/definitions/basicAuthenticationSecurity" }, { "$ref": "#/definitions/apiKeySecurity" }, { "$ref": "#/definitions/oauth2ImplicitSecurity" }, { "$ref": "#/definitions/oauth2PasswordSecurity" }, { "$ref": "#/definitions/oauth2ApplicationSecurity" }, { "$ref": "#/definitions/oauth2AccessCodeSecurity" } ] } }, "basicAuthenticationSecurity": { "type": "object", "additionalProperties": false, "required": [ "type" ], "properties": { "type": { "type": "string", "enum": [ "basic" ] }, "description": { "type": "string" } }, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } } }, "apiKeySecurity": { "type": "object", "additionalProperties": false, "required": [ "type", "name", "in" ], "properties": { "type": { "type": "string", "enum": [ "apiKey" ] }, "name": { "type": "string" }, "in": { "type": "string", "enum": [ "header", "query" ] }, "description": { "type": "string" } }, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } } }, "oauth2ImplicitSecurity": { "type": "object", "additionalProperties": false, "required": [ "type", "flow", "authorizationUrl" ], "properties": { "type": { "type": "string", "enum": [ "oauth2" ] }, "flow": { "type": "string", "enum": [ "implicit" ] }, "scopes": { "$ref": "#/definitions/oauth2Scopes" }, "authorizationUrl": { "type": "string", "format": "uri" }, "description": { "type": "string" } }, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } } }, "oauth2PasswordSecurity": { "type": "object", "additionalProperties": false, "required": [ "type", "flow", "tokenUrl" ], "properties": { "type": { "type": "string", "enum": [ "oauth2" ] }, "flow": { "type": "string", "enum": [ "password" ] }, "scopes": { "$ref": "#/definitions/oauth2Scopes" }, "tokenUrl": { "type": "string", "format": "uri" }, "description": { "type": "string" } }, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } } }, "oauth2ApplicationSecurity": { "type": "object", "additionalProperties": false, "required": [ "type", "flow", "tokenUrl" ], "properties": { "type": { "type": "string", "enum": [ "oauth2" ] }, "flow": { "type": "string", "enum": [ "application" ] }, "scopes": { "$ref": "#/definitions/oauth2Scopes" }, "tokenUrl": { "type": "string", "format": "uri" }, "description": { "type": "string" } }, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } } }, "oauth2AccessCodeSecurity": { "type": "object", "additionalProperties": false, "required": [ "type", "flow", "authorizationUrl", "tokenUrl" ], "properties": { "type": { "type": "string", "enum": [ "oauth2" ] }, "flow": { "type": "string", "enum": [ "accessCode" ] }, "scopes": { "$ref": "#/definitions/oauth2Scopes" }, "authorizationUrl": { "type": "string", "format": "uri" }, "tokenUrl": { "type": "string", "format": "uri" }, "description": { "type": "string" } }, "patternProperties": { "^x-": { "$ref": "#/definitions/vendorExtension" } } }, "oauth2Scopes": { "type": "object", "additionalProperties": { "type": "string" } }, "mediaTypeList": { "type": "array", "items": { "$ref": "#/definitions/mimeType" }, "uniqueItems": true }, "parametersList": { "type": "array", "description": "The parameters needed to send a valid API call.", "additionalItems": false, "items": { "oneOf": [ { "$ref": "#/definitions/parameter" }, { "$ref": "#/definitions/jsonReference" } ] }, "uniqueItems": true }, "schemesList": { "type": "array", "description": "The transfer protocol of the API.", "items": { "type": "string", "enum": [ "http", "https", "ws", "wss" ] }, "uniqueItems": true }, "collectionFormat": { "type": "string", "enum": [ "csv", "ssv", "tsv", "pipes" ], "default": "csv" }, "collectionFormatWithMulti": { "type": "string", "enum": [ "csv", "ssv", "tsv", "pipes", "multi" ], "default": "csv" }, "title": { "$ref": "http://json-schema.org/draft-04/schema#/properties/title" }, "description": { "$ref": "http://json-schema.org/draft-04/schema#/properties/description" }, "default": { "$ref": "http://json-schema.org/draft-04/schema#/properties/default" }, "multipleOf": { "$ref": "http://json-schema.org/draft-04/schema#/properties/multipleOf" }, "maximum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/maximum" }, "exclusiveMaximum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMaximum" }, "minimum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/minimum" }, "exclusiveMinimum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMinimum" }, "maxLength": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" }, "minLength": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" }, "pattern": { "$ref": "http://json-schema.org/draft-04/schema#/properties/pattern" }, "maxItems": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" }, "minItems": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" }, "uniqueItems": { "$ref": "http://json-schema.org/draft-04/schema#/properties/uniqueItems" }, "enum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/enum" }, "jsonReference": { "type": "object", "required": [ "$ref" ], "additionalProperties": false, "properties": { "$ref": { "type": "string" }, "description": { "type": "string" } } } } } ================================================ FILE: vendor/github.com/google/gnostic/openapiv3/OpenAPIv3.go ================================================ // Copyright 2020 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // THIS FILE IS AUTOMATICALLY GENERATED. package openapi_v3 import ( "fmt" "regexp" "strings" "gopkg.in/yaml.v3" "github.com/google/gnostic/compiler" ) // Version returns the package name (and OpenAPI version). func Version() string { return "openapi_v3" } // NewAdditionalPropertiesItem creates an object of type AdditionalPropertiesItem if possible, returning an error if not. func NewAdditionalPropertiesItem(in *yaml.Node, context *compiler.Context) (*AdditionalPropertiesItem, error) { errors := make([]error, 0) x := &AdditionalPropertiesItem{} matched := false // SchemaOrReference schema_or_reference = 1; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewSchemaOrReference(m, compiler.NewContext("schemaOrReference", m, context)) if matchingError == nil { x.Oneof = &AdditionalPropertiesItem_SchemaOrReference{SchemaOrReference: t} matched = true } else { errors = append(errors, matchingError) } } } // bool boolean = 2; boolValue, ok := compiler.BoolForScalarNode(in) if ok { x.Oneof = &AdditionalPropertiesItem_Boolean{Boolean: boolValue} matched = true } if matched { // since the oneof matched one of its possibilities, discard any matching errors errors = make([]error, 0) } else { message := fmt.Sprintf("contains an invalid AdditionalPropertiesItem") err := compiler.NewError(context, message) errors = []error{err} } return x, compiler.NewErrorGroupOrNil(errors) } // NewAny creates an object of type Any if possible, returning an error if not. func NewAny(in *yaml.Node, context *compiler.Context) (*Any, error) { errors := make([]error, 0) x := &Any{} bytes := compiler.Marshal(in) x.Yaml = string(bytes) return x, compiler.NewErrorGroupOrNil(errors) } // NewAnyOrExpression creates an object of type AnyOrExpression if possible, returning an error if not. func NewAnyOrExpression(in *yaml.Node, context *compiler.Context) (*AnyOrExpression, error) { errors := make([]error, 0) x := &AnyOrExpression{} matched := false // Any any = 1; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewAny(m, compiler.NewContext("any", m, context)) if matchingError == nil { x.Oneof = &AnyOrExpression_Any{Any: t} matched = true } else { errors = append(errors, matchingError) } } } // Expression expression = 2; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewExpression(m, compiler.NewContext("expression", m, context)) if matchingError == nil { x.Oneof = &AnyOrExpression_Expression{Expression: t} matched = true } else { errors = append(errors, matchingError) } } } if matched { // since the oneof matched one of its possibilities, discard any matching errors errors = make([]error, 0) } else { message := fmt.Sprintf("contains an invalid AnyOrExpression") err := compiler.NewError(context, message) errors = []error{err} } return x, compiler.NewErrorGroupOrNil(errors) } // NewCallback creates an object of type Callback if possible, returning an error if not. func NewCallback(in *yaml.Node, context *compiler.Context) (*Callback, error) { errors := make([]error, 0) x := &Callback{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{} allowedPatterns := []*regexp.Regexp{pattern0, pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // repeated NamedPathItem path = 1; // MAP: PathItem ^ x.Path = make([]*NamedPathItem, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if true { pair := &NamedPathItem{} pair.Name = k var err error pair.Value, err = NewPathItem(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } x.Path = append(x.Path, pair) } } } // repeated NamedAny specification_extension = 2; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewCallbackOrReference creates an object of type CallbackOrReference if possible, returning an error if not. func NewCallbackOrReference(in *yaml.Node, context *compiler.Context) (*CallbackOrReference, error) { errors := make([]error, 0) x := &CallbackOrReference{} matched := false // Callback callback = 1; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewCallback(m, compiler.NewContext("callback", m, context)) if matchingError == nil { x.Oneof = &CallbackOrReference_Callback{Callback: t} matched = true } else { errors = append(errors, matchingError) } } } // Reference reference = 2; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewReference(m, compiler.NewContext("reference", m, context)) if matchingError == nil { x.Oneof = &CallbackOrReference_Reference{Reference: t} matched = true } else { errors = append(errors, matchingError) } } } if matched { // since the oneof matched one of its possibilities, discard any matching errors errors = make([]error, 0) } else { message := fmt.Sprintf("contains an invalid CallbackOrReference") err := compiler.NewError(context, message) errors = []error{err} } return x, compiler.NewErrorGroupOrNil(errors) } // NewCallbacksOrReferences creates an object of type CallbacksOrReferences if possible, returning an error if not. func NewCallbacksOrReferences(in *yaml.Node, context *compiler.Context) (*CallbacksOrReferences, error) { errors := make([]error, 0) x := &CallbacksOrReferences{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedCallbackOrReference additional_properties = 1; // MAP: CallbackOrReference x.AdditionalProperties = make([]*NamedCallbackOrReference, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedCallbackOrReference{} pair.Name = k var err error pair.Value, err = NewCallbackOrReference(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewComponents creates an object of type Components if possible, returning an error if not. func NewComponents(in *yaml.Node, context *compiler.Context) (*Components, error) { errors := make([]error, 0) x := &Components{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"callbacks", "examples", "headers", "links", "parameters", "requestBodies", "responses", "schemas", "securitySchemes"} allowedPatterns := []*regexp.Regexp{pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // SchemasOrReferences schemas = 1; v1 := compiler.MapValueForKey(m, "schemas") if v1 != nil { var err error x.Schemas, err = NewSchemasOrReferences(v1, compiler.NewContext("schemas", v1, context)) if err != nil { errors = append(errors, err) } } // ResponsesOrReferences responses = 2; v2 := compiler.MapValueForKey(m, "responses") if v2 != nil { var err error x.Responses, err = NewResponsesOrReferences(v2, compiler.NewContext("responses", v2, context)) if err != nil { errors = append(errors, err) } } // ParametersOrReferences parameters = 3; v3 := compiler.MapValueForKey(m, "parameters") if v3 != nil { var err error x.Parameters, err = NewParametersOrReferences(v3, compiler.NewContext("parameters", v3, context)) if err != nil { errors = append(errors, err) } } // ExamplesOrReferences examples = 4; v4 := compiler.MapValueForKey(m, "examples") if v4 != nil { var err error x.Examples, err = NewExamplesOrReferences(v4, compiler.NewContext("examples", v4, context)) if err != nil { errors = append(errors, err) } } // RequestBodiesOrReferences request_bodies = 5; v5 := compiler.MapValueForKey(m, "requestBodies") if v5 != nil { var err error x.RequestBodies, err = NewRequestBodiesOrReferences(v5, compiler.NewContext("requestBodies", v5, context)) if err != nil { errors = append(errors, err) } } // HeadersOrReferences headers = 6; v6 := compiler.MapValueForKey(m, "headers") if v6 != nil { var err error x.Headers, err = NewHeadersOrReferences(v6, compiler.NewContext("headers", v6, context)) if err != nil { errors = append(errors, err) } } // SecuritySchemesOrReferences security_schemes = 7; v7 := compiler.MapValueForKey(m, "securitySchemes") if v7 != nil { var err error x.SecuritySchemes, err = NewSecuritySchemesOrReferences(v7, compiler.NewContext("securitySchemes", v7, context)) if err != nil { errors = append(errors, err) } } // LinksOrReferences links = 8; v8 := compiler.MapValueForKey(m, "links") if v8 != nil { var err error x.Links, err = NewLinksOrReferences(v8, compiler.NewContext("links", v8, context)) if err != nil { errors = append(errors, err) } } // CallbacksOrReferences callbacks = 9; v9 := compiler.MapValueForKey(m, "callbacks") if v9 != nil { var err error x.Callbacks, err = NewCallbacksOrReferences(v9, compiler.NewContext("callbacks", v9, context)) if err != nil { errors = append(errors, err) } } // repeated NamedAny specification_extension = 10; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewContact creates an object of type Contact if possible, returning an error if not. func NewContact(in *yaml.Node, context *compiler.Context) (*Contact, error) { errors := make([]error, 0) x := &Contact{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"email", "name", "url"} allowedPatterns := []*regexp.Regexp{pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string url = 2; v2 := compiler.MapValueForKey(m, "url") if v2 != nil { x.Url, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for url: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // string email = 3; v3 := compiler.MapValueForKey(m, "email") if v3 != nil { x.Email, ok = compiler.StringForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for email: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny specification_extension = 4; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewDefaultType creates an object of type DefaultType if possible, returning an error if not. func NewDefaultType(in *yaml.Node, context *compiler.Context) (*DefaultType, error) { errors := make([]error, 0) x := &DefaultType{} matched := false switch in.Tag { case "!!bool": var v bool v, matched = compiler.BoolForScalarNode(in) x.Oneof = &DefaultType_Boolean{Boolean: v} case "!!str": var v string v, matched = compiler.StringForScalarNode(in) x.Oneof = &DefaultType_String_{String_: v} case "!!float": var v float64 v, matched = compiler.FloatForScalarNode(in) x.Oneof = &DefaultType_Number{Number: v} case "!!int": var v int64 v, matched = compiler.IntForScalarNode(in) x.Oneof = &DefaultType_Number{Number: float64(v)} } if matched { // since the oneof matched one of its possibilities, discard any matching errors errors = make([]error, 0) } return x, compiler.NewErrorGroupOrNil(errors) } // NewDiscriminator creates an object of type Discriminator if possible, returning an error if not. func NewDiscriminator(in *yaml.Node, context *compiler.Context) (*Discriminator, error) { errors := make([]error, 0) x := &Discriminator{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"propertyName"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"mapping", "propertyName"} allowedPatterns := []*regexp.Regexp{pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string property_name = 1; v1 := compiler.MapValueForKey(m, "propertyName") if v1 != nil { x.PropertyName, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for propertyName: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // Strings mapping = 2; v2 := compiler.MapValueForKey(m, "mapping") if v2 != nil { var err error x.Mapping, err = NewStrings(v2, compiler.NewContext("mapping", v2, context)) if err != nil { errors = append(errors, err) } } // repeated NamedAny specification_extension = 3; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewDocument creates an object of type Document if possible, returning an error if not. func NewDocument(in *yaml.Node, context *compiler.Context) (*Document, error) { errors := make([]error, 0) x := &Document{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"info", "openapi", "paths"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"components", "externalDocs", "info", "openapi", "paths", "security", "servers", "tags"} allowedPatterns := []*regexp.Regexp{pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string openapi = 1; v1 := compiler.MapValueForKey(m, "openapi") if v1 != nil { x.Openapi, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for openapi: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // Info info = 2; v2 := compiler.MapValueForKey(m, "info") if v2 != nil { var err error x.Info, err = NewInfo(v2, compiler.NewContext("info", v2, context)) if err != nil { errors = append(errors, err) } } // repeated Server servers = 3; v3 := compiler.MapValueForKey(m, "servers") if v3 != nil { // repeated Server x.Servers = make([]*Server, 0) a, ok := compiler.SequenceNodeForNode(v3) if ok { for _, item := range a.Content { y, err := NewServer(item, compiler.NewContext("servers", item, context)) if err != nil { errors = append(errors, err) } x.Servers = append(x.Servers, y) } } } // Paths paths = 4; v4 := compiler.MapValueForKey(m, "paths") if v4 != nil { var err error x.Paths, err = NewPaths(v4, compiler.NewContext("paths", v4, context)) if err != nil { errors = append(errors, err) } } // Components components = 5; v5 := compiler.MapValueForKey(m, "components") if v5 != nil { var err error x.Components, err = NewComponents(v5, compiler.NewContext("components", v5, context)) if err != nil { errors = append(errors, err) } } // repeated SecurityRequirement security = 6; v6 := compiler.MapValueForKey(m, "security") if v6 != nil { // repeated SecurityRequirement x.Security = make([]*SecurityRequirement, 0) a, ok := compiler.SequenceNodeForNode(v6) if ok { for _, item := range a.Content { y, err := NewSecurityRequirement(item, compiler.NewContext("security", item, context)) if err != nil { errors = append(errors, err) } x.Security = append(x.Security, y) } } } // repeated Tag tags = 7; v7 := compiler.MapValueForKey(m, "tags") if v7 != nil { // repeated Tag x.Tags = make([]*Tag, 0) a, ok := compiler.SequenceNodeForNode(v7) if ok { for _, item := range a.Content { y, err := NewTag(item, compiler.NewContext("tags", item, context)) if err != nil { errors = append(errors, err) } x.Tags = append(x.Tags, y) } } } // ExternalDocs external_docs = 8; v8 := compiler.MapValueForKey(m, "externalDocs") if v8 != nil { var err error x.ExternalDocs, err = NewExternalDocs(v8, compiler.NewContext("externalDocs", v8, context)) if err != nil { errors = append(errors, err) } } // repeated NamedAny specification_extension = 9; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewEncoding creates an object of type Encoding if possible, returning an error if not. func NewEncoding(in *yaml.Node, context *compiler.Context) (*Encoding, error) { errors := make([]error, 0) x := &Encoding{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"allowReserved", "contentType", "explode", "headers", "style"} allowedPatterns := []*regexp.Regexp{pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string content_type = 1; v1 := compiler.MapValueForKey(m, "contentType") if v1 != nil { x.ContentType, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for contentType: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // HeadersOrReferences headers = 2; v2 := compiler.MapValueForKey(m, "headers") if v2 != nil { var err error x.Headers, err = NewHeadersOrReferences(v2, compiler.NewContext("headers", v2, context)) if err != nil { errors = append(errors, err) } } // string style = 3; v3 := compiler.MapValueForKey(m, "style") if v3 != nil { x.Style, ok = compiler.StringForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for style: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } // bool explode = 4; v4 := compiler.MapValueForKey(m, "explode") if v4 != nil { x.Explode, ok = compiler.BoolForScalarNode(v4) if !ok { message := fmt.Sprintf("has unexpected value for explode: %s", compiler.Display(v4)) errors = append(errors, compiler.NewError(context, message)) } } // bool allow_reserved = 5; v5 := compiler.MapValueForKey(m, "allowReserved") if v5 != nil { x.AllowReserved, ok = compiler.BoolForScalarNode(v5) if !ok { message := fmt.Sprintf("has unexpected value for allowReserved: %s", compiler.Display(v5)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny specification_extension = 6; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewEncodings creates an object of type Encodings if possible, returning an error if not. func NewEncodings(in *yaml.Node, context *compiler.Context) (*Encodings, error) { errors := make([]error, 0) x := &Encodings{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedEncoding additional_properties = 1; // MAP: Encoding x.AdditionalProperties = make([]*NamedEncoding, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedEncoding{} pair.Name = k var err error pair.Value, err = NewEncoding(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewExample creates an object of type Example if possible, returning an error if not. func NewExample(in *yaml.Node, context *compiler.Context) (*Example, error) { errors := make([]error, 0) x := &Example{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"description", "externalValue", "summary", "value"} allowedPatterns := []*regexp.Regexp{pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string summary = 1; v1 := compiler.MapValueForKey(m, "summary") if v1 != nil { x.Summary, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for summary: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 2; v2 := compiler.MapValueForKey(m, "description") if v2 != nil { x.Description, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // Any value = 3; v3 := compiler.MapValueForKey(m, "value") if v3 != nil { var err error x.Value, err = NewAny(v3, compiler.NewContext("value", v3, context)) if err != nil { errors = append(errors, err) } } // string external_value = 4; v4 := compiler.MapValueForKey(m, "externalValue") if v4 != nil { x.ExternalValue, ok = compiler.StringForScalarNode(v4) if !ok { message := fmt.Sprintf("has unexpected value for externalValue: %s", compiler.Display(v4)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny specification_extension = 5; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewExampleOrReference creates an object of type ExampleOrReference if possible, returning an error if not. func NewExampleOrReference(in *yaml.Node, context *compiler.Context) (*ExampleOrReference, error) { errors := make([]error, 0) x := &ExampleOrReference{} matched := false // Example example = 1; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewExample(m, compiler.NewContext("example", m, context)) if matchingError == nil { x.Oneof = &ExampleOrReference_Example{Example: t} matched = true } else { errors = append(errors, matchingError) } } } // Reference reference = 2; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewReference(m, compiler.NewContext("reference", m, context)) if matchingError == nil { x.Oneof = &ExampleOrReference_Reference{Reference: t} matched = true } else { errors = append(errors, matchingError) } } } if matched { // since the oneof matched one of its possibilities, discard any matching errors errors = make([]error, 0) } else { message := fmt.Sprintf("contains an invalid ExampleOrReference") err := compiler.NewError(context, message) errors = []error{err} } return x, compiler.NewErrorGroupOrNil(errors) } // NewExamplesOrReferences creates an object of type ExamplesOrReferences if possible, returning an error if not. func NewExamplesOrReferences(in *yaml.Node, context *compiler.Context) (*ExamplesOrReferences, error) { errors := make([]error, 0) x := &ExamplesOrReferences{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedExampleOrReference additional_properties = 1; // MAP: ExampleOrReference x.AdditionalProperties = make([]*NamedExampleOrReference, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedExampleOrReference{} pair.Name = k var err error pair.Value, err = NewExampleOrReference(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewExpression creates an object of type Expression if possible, returning an error if not. func NewExpression(in *yaml.Node, context *compiler.Context) (*Expression, error) { errors := make([]error, 0) x := &Expression{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedAny additional_properties = 1; // MAP: Any x.AdditionalProperties = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewExternalDocs creates an object of type ExternalDocs if possible, returning an error if not. func NewExternalDocs(in *yaml.Node, context *compiler.Context) (*ExternalDocs, error) { errors := make([]error, 0) x := &ExternalDocs{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"url"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"description", "url"} allowedPatterns := []*regexp.Regexp{pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string description = 1; v1 := compiler.MapValueForKey(m, "description") if v1 != nil { x.Description, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string url = 2; v2 := compiler.MapValueForKey(m, "url") if v2 != nil { x.Url, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for url: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny specification_extension = 3; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewHeader creates an object of type Header if possible, returning an error if not. func NewHeader(in *yaml.Node, context *compiler.Context) (*Header, error) { errors := make([]error, 0) x := &Header{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"allowEmptyValue", "allowReserved", "content", "deprecated", "description", "example", "examples", "explode", "required", "schema", "style"} allowedPatterns := []*regexp.Regexp{pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string description = 1; v1 := compiler.MapValueForKey(m, "description") if v1 != nil { x.Description, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // bool required = 2; v2 := compiler.MapValueForKey(m, "required") if v2 != nil { x.Required, ok = compiler.BoolForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for required: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // bool deprecated = 3; v3 := compiler.MapValueForKey(m, "deprecated") if v3 != nil { x.Deprecated, ok = compiler.BoolForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for deprecated: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } // bool allow_empty_value = 4; v4 := compiler.MapValueForKey(m, "allowEmptyValue") if v4 != nil { x.AllowEmptyValue, ok = compiler.BoolForScalarNode(v4) if !ok { message := fmt.Sprintf("has unexpected value for allowEmptyValue: %s", compiler.Display(v4)) errors = append(errors, compiler.NewError(context, message)) } } // string style = 5; v5 := compiler.MapValueForKey(m, "style") if v5 != nil { x.Style, ok = compiler.StringForScalarNode(v5) if !ok { message := fmt.Sprintf("has unexpected value for style: %s", compiler.Display(v5)) errors = append(errors, compiler.NewError(context, message)) } } // bool explode = 6; v6 := compiler.MapValueForKey(m, "explode") if v6 != nil { x.Explode, ok = compiler.BoolForScalarNode(v6) if !ok { message := fmt.Sprintf("has unexpected value for explode: %s", compiler.Display(v6)) errors = append(errors, compiler.NewError(context, message)) } } // bool allow_reserved = 7; v7 := compiler.MapValueForKey(m, "allowReserved") if v7 != nil { x.AllowReserved, ok = compiler.BoolForScalarNode(v7) if !ok { message := fmt.Sprintf("has unexpected value for allowReserved: %s", compiler.Display(v7)) errors = append(errors, compiler.NewError(context, message)) } } // SchemaOrReference schema = 8; v8 := compiler.MapValueForKey(m, "schema") if v8 != nil { var err error x.Schema, err = NewSchemaOrReference(v8, compiler.NewContext("schema", v8, context)) if err != nil { errors = append(errors, err) } } // Any example = 9; v9 := compiler.MapValueForKey(m, "example") if v9 != nil { var err error x.Example, err = NewAny(v9, compiler.NewContext("example", v9, context)) if err != nil { errors = append(errors, err) } } // ExamplesOrReferences examples = 10; v10 := compiler.MapValueForKey(m, "examples") if v10 != nil { var err error x.Examples, err = NewExamplesOrReferences(v10, compiler.NewContext("examples", v10, context)) if err != nil { errors = append(errors, err) } } // MediaTypes content = 11; v11 := compiler.MapValueForKey(m, "content") if v11 != nil { var err error x.Content, err = NewMediaTypes(v11, compiler.NewContext("content", v11, context)) if err != nil { errors = append(errors, err) } } // repeated NamedAny specification_extension = 12; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewHeaderOrReference creates an object of type HeaderOrReference if possible, returning an error if not. func NewHeaderOrReference(in *yaml.Node, context *compiler.Context) (*HeaderOrReference, error) { errors := make([]error, 0) x := &HeaderOrReference{} matched := false // Header header = 1; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewHeader(m, compiler.NewContext("header", m, context)) if matchingError == nil { x.Oneof = &HeaderOrReference_Header{Header: t} matched = true } else { errors = append(errors, matchingError) } } } // Reference reference = 2; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewReference(m, compiler.NewContext("reference", m, context)) if matchingError == nil { x.Oneof = &HeaderOrReference_Reference{Reference: t} matched = true } else { errors = append(errors, matchingError) } } } if matched { // since the oneof matched one of its possibilities, discard any matching errors errors = make([]error, 0) } else { message := fmt.Sprintf("contains an invalid HeaderOrReference") err := compiler.NewError(context, message) errors = []error{err} } return x, compiler.NewErrorGroupOrNil(errors) } // NewHeadersOrReferences creates an object of type HeadersOrReferences if possible, returning an error if not. func NewHeadersOrReferences(in *yaml.Node, context *compiler.Context) (*HeadersOrReferences, error) { errors := make([]error, 0) x := &HeadersOrReferences{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedHeaderOrReference additional_properties = 1; // MAP: HeaderOrReference x.AdditionalProperties = make([]*NamedHeaderOrReference, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedHeaderOrReference{} pair.Name = k var err error pair.Value, err = NewHeaderOrReference(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewInfo creates an object of type Info if possible, returning an error if not. func NewInfo(in *yaml.Node, context *compiler.Context) (*Info, error) { errors := make([]error, 0) x := &Info{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"title", "version"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"contact", "description", "license", "summary", "termsOfService", "title", "version"} allowedPatterns := []*regexp.Regexp{pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string title = 1; v1 := compiler.MapValueForKey(m, "title") if v1 != nil { x.Title, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for title: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 2; v2 := compiler.MapValueForKey(m, "description") if v2 != nil { x.Description, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // string terms_of_service = 3; v3 := compiler.MapValueForKey(m, "termsOfService") if v3 != nil { x.TermsOfService, ok = compiler.StringForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for termsOfService: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } // Contact contact = 4; v4 := compiler.MapValueForKey(m, "contact") if v4 != nil { var err error x.Contact, err = NewContact(v4, compiler.NewContext("contact", v4, context)) if err != nil { errors = append(errors, err) } } // License license = 5; v5 := compiler.MapValueForKey(m, "license") if v5 != nil { var err error x.License, err = NewLicense(v5, compiler.NewContext("license", v5, context)) if err != nil { errors = append(errors, err) } } // string version = 6; v6 := compiler.MapValueForKey(m, "version") if v6 != nil { x.Version, ok = compiler.StringForScalarNode(v6) if !ok { message := fmt.Sprintf("has unexpected value for version: %s", compiler.Display(v6)) errors = append(errors, compiler.NewError(context, message)) } } // string summary = 7; v7 := compiler.MapValueForKey(m, "summary") if v7 != nil { x.Summary, ok = compiler.StringForScalarNode(v7) if !ok { message := fmt.Sprintf("has unexpected value for summary: %s", compiler.Display(v7)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny specification_extension = 8; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewItemsItem creates an object of type ItemsItem if possible, returning an error if not. func NewItemsItem(in *yaml.Node, context *compiler.Context) (*ItemsItem, error) { errors := make([]error, 0) x := &ItemsItem{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value for item array: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { x.SchemaOrReference = make([]*SchemaOrReference, 0) y, err := NewSchemaOrReference(m, compiler.NewContext("", m, context)) if err != nil { return nil, err } x.SchemaOrReference = append(x.SchemaOrReference, y) } return x, compiler.NewErrorGroupOrNil(errors) } // NewLicense creates an object of type License if possible, returning an error if not. func NewLicense(in *yaml.Node, context *compiler.Context) (*License, error) { errors := make([]error, 0) x := &License{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"name"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"name", "url"} allowedPatterns := []*regexp.Regexp{pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string url = 2; v2 := compiler.MapValueForKey(m, "url") if v2 != nil { x.Url, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for url: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny specification_extension = 3; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewLink creates an object of type Link if possible, returning an error if not. func NewLink(in *yaml.Node, context *compiler.Context) (*Link, error) { errors := make([]error, 0) x := &Link{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"description", "operationId", "operationRef", "parameters", "requestBody", "server"} allowedPatterns := []*regexp.Regexp{pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string operation_ref = 1; v1 := compiler.MapValueForKey(m, "operationRef") if v1 != nil { x.OperationRef, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for operationRef: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string operation_id = 2; v2 := compiler.MapValueForKey(m, "operationId") if v2 != nil { x.OperationId, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for operationId: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // AnyOrExpression parameters = 3; v3 := compiler.MapValueForKey(m, "parameters") if v3 != nil { var err error x.Parameters, err = NewAnyOrExpression(v3, compiler.NewContext("parameters", v3, context)) if err != nil { errors = append(errors, err) } } // AnyOrExpression request_body = 4; v4 := compiler.MapValueForKey(m, "requestBody") if v4 != nil { var err error x.RequestBody, err = NewAnyOrExpression(v4, compiler.NewContext("requestBody", v4, context)) if err != nil { errors = append(errors, err) } } // string description = 5; v5 := compiler.MapValueForKey(m, "description") if v5 != nil { x.Description, ok = compiler.StringForScalarNode(v5) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v5)) errors = append(errors, compiler.NewError(context, message)) } } // Server server = 6; v6 := compiler.MapValueForKey(m, "server") if v6 != nil { var err error x.Server, err = NewServer(v6, compiler.NewContext("server", v6, context)) if err != nil { errors = append(errors, err) } } // repeated NamedAny specification_extension = 7; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewLinkOrReference creates an object of type LinkOrReference if possible, returning an error if not. func NewLinkOrReference(in *yaml.Node, context *compiler.Context) (*LinkOrReference, error) { errors := make([]error, 0) x := &LinkOrReference{} matched := false // Link link = 1; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewLink(m, compiler.NewContext("link", m, context)) if matchingError == nil { x.Oneof = &LinkOrReference_Link{Link: t} matched = true } else { errors = append(errors, matchingError) } } } // Reference reference = 2; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewReference(m, compiler.NewContext("reference", m, context)) if matchingError == nil { x.Oneof = &LinkOrReference_Reference{Reference: t} matched = true } else { errors = append(errors, matchingError) } } } if matched { // since the oneof matched one of its possibilities, discard any matching errors errors = make([]error, 0) } else { message := fmt.Sprintf("contains an invalid LinkOrReference") err := compiler.NewError(context, message) errors = []error{err} } return x, compiler.NewErrorGroupOrNil(errors) } // NewLinksOrReferences creates an object of type LinksOrReferences if possible, returning an error if not. func NewLinksOrReferences(in *yaml.Node, context *compiler.Context) (*LinksOrReferences, error) { errors := make([]error, 0) x := &LinksOrReferences{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedLinkOrReference additional_properties = 1; // MAP: LinkOrReference x.AdditionalProperties = make([]*NamedLinkOrReference, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedLinkOrReference{} pair.Name = k var err error pair.Value, err = NewLinkOrReference(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewMediaType creates an object of type MediaType if possible, returning an error if not. func NewMediaType(in *yaml.Node, context *compiler.Context) (*MediaType, error) { errors := make([]error, 0) x := &MediaType{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"encoding", "example", "examples", "schema"} allowedPatterns := []*regexp.Regexp{pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // SchemaOrReference schema = 1; v1 := compiler.MapValueForKey(m, "schema") if v1 != nil { var err error x.Schema, err = NewSchemaOrReference(v1, compiler.NewContext("schema", v1, context)) if err != nil { errors = append(errors, err) } } // Any example = 2; v2 := compiler.MapValueForKey(m, "example") if v2 != nil { var err error x.Example, err = NewAny(v2, compiler.NewContext("example", v2, context)) if err != nil { errors = append(errors, err) } } // ExamplesOrReferences examples = 3; v3 := compiler.MapValueForKey(m, "examples") if v3 != nil { var err error x.Examples, err = NewExamplesOrReferences(v3, compiler.NewContext("examples", v3, context)) if err != nil { errors = append(errors, err) } } // Encodings encoding = 4; v4 := compiler.MapValueForKey(m, "encoding") if v4 != nil { var err error x.Encoding, err = NewEncodings(v4, compiler.NewContext("encoding", v4, context)) if err != nil { errors = append(errors, err) } } // repeated NamedAny specification_extension = 5; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewMediaTypes creates an object of type MediaTypes if possible, returning an error if not. func NewMediaTypes(in *yaml.Node, context *compiler.Context) (*MediaTypes, error) { errors := make([]error, 0) x := &MediaTypes{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedMediaType additional_properties = 1; // MAP: MediaType x.AdditionalProperties = make([]*NamedMediaType, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedMediaType{} pair.Name = k var err error pair.Value, err = NewMediaType(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNamedAny creates an object of type NamedAny if possible, returning an error if not. func NewNamedAny(in *yaml.Node, context *compiler.Context) (*NamedAny, error) { errors := make([]error, 0) x := &NamedAny{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"name", "value"} var allowedPatterns []*regexp.Regexp invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // Any value = 2; v2 := compiler.MapValueForKey(m, "value") if v2 != nil { var err error x.Value, err = NewAny(v2, compiler.NewContext("value", v2, context)) if err != nil { errors = append(errors, err) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNamedCallbackOrReference creates an object of type NamedCallbackOrReference if possible, returning an error if not. func NewNamedCallbackOrReference(in *yaml.Node, context *compiler.Context) (*NamedCallbackOrReference, error) { errors := make([]error, 0) x := &NamedCallbackOrReference{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"name", "value"} var allowedPatterns []*regexp.Regexp invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // CallbackOrReference value = 2; v2 := compiler.MapValueForKey(m, "value") if v2 != nil { var err error x.Value, err = NewCallbackOrReference(v2, compiler.NewContext("value", v2, context)) if err != nil { errors = append(errors, err) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNamedEncoding creates an object of type NamedEncoding if possible, returning an error if not. func NewNamedEncoding(in *yaml.Node, context *compiler.Context) (*NamedEncoding, error) { errors := make([]error, 0) x := &NamedEncoding{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"name", "value"} var allowedPatterns []*regexp.Regexp invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // Encoding value = 2; v2 := compiler.MapValueForKey(m, "value") if v2 != nil { var err error x.Value, err = NewEncoding(v2, compiler.NewContext("value", v2, context)) if err != nil { errors = append(errors, err) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNamedExampleOrReference creates an object of type NamedExampleOrReference if possible, returning an error if not. func NewNamedExampleOrReference(in *yaml.Node, context *compiler.Context) (*NamedExampleOrReference, error) { errors := make([]error, 0) x := &NamedExampleOrReference{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"name", "value"} var allowedPatterns []*regexp.Regexp invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // ExampleOrReference value = 2; v2 := compiler.MapValueForKey(m, "value") if v2 != nil { var err error x.Value, err = NewExampleOrReference(v2, compiler.NewContext("value", v2, context)) if err != nil { errors = append(errors, err) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNamedHeaderOrReference creates an object of type NamedHeaderOrReference if possible, returning an error if not. func NewNamedHeaderOrReference(in *yaml.Node, context *compiler.Context) (*NamedHeaderOrReference, error) { errors := make([]error, 0) x := &NamedHeaderOrReference{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"name", "value"} var allowedPatterns []*regexp.Regexp invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // HeaderOrReference value = 2; v2 := compiler.MapValueForKey(m, "value") if v2 != nil { var err error x.Value, err = NewHeaderOrReference(v2, compiler.NewContext("value", v2, context)) if err != nil { errors = append(errors, err) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNamedLinkOrReference creates an object of type NamedLinkOrReference if possible, returning an error if not. func NewNamedLinkOrReference(in *yaml.Node, context *compiler.Context) (*NamedLinkOrReference, error) { errors := make([]error, 0) x := &NamedLinkOrReference{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"name", "value"} var allowedPatterns []*regexp.Regexp invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // LinkOrReference value = 2; v2 := compiler.MapValueForKey(m, "value") if v2 != nil { var err error x.Value, err = NewLinkOrReference(v2, compiler.NewContext("value", v2, context)) if err != nil { errors = append(errors, err) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNamedMediaType creates an object of type NamedMediaType if possible, returning an error if not. func NewNamedMediaType(in *yaml.Node, context *compiler.Context) (*NamedMediaType, error) { errors := make([]error, 0) x := &NamedMediaType{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"name", "value"} var allowedPatterns []*regexp.Regexp invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // MediaType value = 2; v2 := compiler.MapValueForKey(m, "value") if v2 != nil { var err error x.Value, err = NewMediaType(v2, compiler.NewContext("value", v2, context)) if err != nil { errors = append(errors, err) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNamedParameterOrReference creates an object of type NamedParameterOrReference if possible, returning an error if not. func NewNamedParameterOrReference(in *yaml.Node, context *compiler.Context) (*NamedParameterOrReference, error) { errors := make([]error, 0) x := &NamedParameterOrReference{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"name", "value"} var allowedPatterns []*regexp.Regexp invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // ParameterOrReference value = 2; v2 := compiler.MapValueForKey(m, "value") if v2 != nil { var err error x.Value, err = NewParameterOrReference(v2, compiler.NewContext("value", v2, context)) if err != nil { errors = append(errors, err) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNamedPathItem creates an object of type NamedPathItem if possible, returning an error if not. func NewNamedPathItem(in *yaml.Node, context *compiler.Context) (*NamedPathItem, error) { errors := make([]error, 0) x := &NamedPathItem{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"name", "value"} var allowedPatterns []*regexp.Regexp invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // PathItem value = 2; v2 := compiler.MapValueForKey(m, "value") if v2 != nil { var err error x.Value, err = NewPathItem(v2, compiler.NewContext("value", v2, context)) if err != nil { errors = append(errors, err) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNamedRequestBodyOrReference creates an object of type NamedRequestBodyOrReference if possible, returning an error if not. func NewNamedRequestBodyOrReference(in *yaml.Node, context *compiler.Context) (*NamedRequestBodyOrReference, error) { errors := make([]error, 0) x := &NamedRequestBodyOrReference{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"name", "value"} var allowedPatterns []*regexp.Regexp invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // RequestBodyOrReference value = 2; v2 := compiler.MapValueForKey(m, "value") if v2 != nil { var err error x.Value, err = NewRequestBodyOrReference(v2, compiler.NewContext("value", v2, context)) if err != nil { errors = append(errors, err) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNamedResponseOrReference creates an object of type NamedResponseOrReference if possible, returning an error if not. func NewNamedResponseOrReference(in *yaml.Node, context *compiler.Context) (*NamedResponseOrReference, error) { errors := make([]error, 0) x := &NamedResponseOrReference{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"name", "value"} var allowedPatterns []*regexp.Regexp invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // ResponseOrReference value = 2; v2 := compiler.MapValueForKey(m, "value") if v2 != nil { var err error x.Value, err = NewResponseOrReference(v2, compiler.NewContext("value", v2, context)) if err != nil { errors = append(errors, err) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNamedSchemaOrReference creates an object of type NamedSchemaOrReference if possible, returning an error if not. func NewNamedSchemaOrReference(in *yaml.Node, context *compiler.Context) (*NamedSchemaOrReference, error) { errors := make([]error, 0) x := &NamedSchemaOrReference{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"name", "value"} var allowedPatterns []*regexp.Regexp invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // SchemaOrReference value = 2; v2 := compiler.MapValueForKey(m, "value") if v2 != nil { var err error x.Value, err = NewSchemaOrReference(v2, compiler.NewContext("value", v2, context)) if err != nil { errors = append(errors, err) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNamedSecuritySchemeOrReference creates an object of type NamedSecuritySchemeOrReference if possible, returning an error if not. func NewNamedSecuritySchemeOrReference(in *yaml.Node, context *compiler.Context) (*NamedSecuritySchemeOrReference, error) { errors := make([]error, 0) x := &NamedSecuritySchemeOrReference{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"name", "value"} var allowedPatterns []*regexp.Regexp invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // SecuritySchemeOrReference value = 2; v2 := compiler.MapValueForKey(m, "value") if v2 != nil { var err error x.Value, err = NewSecuritySchemeOrReference(v2, compiler.NewContext("value", v2, context)) if err != nil { errors = append(errors, err) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNamedServerVariable creates an object of type NamedServerVariable if possible, returning an error if not. func NewNamedServerVariable(in *yaml.Node, context *compiler.Context) (*NamedServerVariable, error) { errors := make([]error, 0) x := &NamedServerVariable{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"name", "value"} var allowedPatterns []*regexp.Regexp invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // ServerVariable value = 2; v2 := compiler.MapValueForKey(m, "value") if v2 != nil { var err error x.Value, err = NewServerVariable(v2, compiler.NewContext("value", v2, context)) if err != nil { errors = append(errors, err) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNamedString creates an object of type NamedString if possible, returning an error if not. func NewNamedString(in *yaml.Node, context *compiler.Context) (*NamedString, error) { errors := make([]error, 0) x := &NamedString{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"name", "value"} var allowedPatterns []*regexp.Regexp invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string value = 2; v2 := compiler.MapValueForKey(m, "value") if v2 != nil { x.Value, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for value: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewNamedStringArray creates an object of type NamedStringArray if possible, returning an error if not. func NewNamedStringArray(in *yaml.Node, context *compiler.Context) (*NamedStringArray, error) { errors := make([]error, 0) x := &NamedStringArray{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"name", "value"} var allowedPatterns []*regexp.Regexp invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // StringArray value = 2; v2 := compiler.MapValueForKey(m, "value") if v2 != nil { var err error x.Value, err = NewStringArray(v2, compiler.NewContext("value", v2, context)) if err != nil { errors = append(errors, err) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewOauthFlow creates an object of type OauthFlow if possible, returning an error if not. func NewOauthFlow(in *yaml.Node, context *compiler.Context) (*OauthFlow, error) { errors := make([]error, 0) x := &OauthFlow{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"authorizationUrl", "refreshUrl", "scopes", "tokenUrl"} allowedPatterns := []*regexp.Regexp{pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string authorization_url = 1; v1 := compiler.MapValueForKey(m, "authorizationUrl") if v1 != nil { x.AuthorizationUrl, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for authorizationUrl: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string token_url = 2; v2 := compiler.MapValueForKey(m, "tokenUrl") if v2 != nil { x.TokenUrl, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for tokenUrl: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // string refresh_url = 3; v3 := compiler.MapValueForKey(m, "refreshUrl") if v3 != nil { x.RefreshUrl, ok = compiler.StringForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for refreshUrl: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } // Strings scopes = 4; v4 := compiler.MapValueForKey(m, "scopes") if v4 != nil { var err error x.Scopes, err = NewStrings(v4, compiler.NewContext("scopes", v4, context)) if err != nil { errors = append(errors, err) } } // repeated NamedAny specification_extension = 5; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewOauthFlows creates an object of type OauthFlows if possible, returning an error if not. func NewOauthFlows(in *yaml.Node, context *compiler.Context) (*OauthFlows, error) { errors := make([]error, 0) x := &OauthFlows{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"authorizationCode", "clientCredentials", "implicit", "password"} allowedPatterns := []*regexp.Regexp{pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // OauthFlow implicit = 1; v1 := compiler.MapValueForKey(m, "implicit") if v1 != nil { var err error x.Implicit, err = NewOauthFlow(v1, compiler.NewContext("implicit", v1, context)) if err != nil { errors = append(errors, err) } } // OauthFlow password = 2; v2 := compiler.MapValueForKey(m, "password") if v2 != nil { var err error x.Password, err = NewOauthFlow(v2, compiler.NewContext("password", v2, context)) if err != nil { errors = append(errors, err) } } // OauthFlow client_credentials = 3; v3 := compiler.MapValueForKey(m, "clientCredentials") if v3 != nil { var err error x.ClientCredentials, err = NewOauthFlow(v3, compiler.NewContext("clientCredentials", v3, context)) if err != nil { errors = append(errors, err) } } // OauthFlow authorization_code = 4; v4 := compiler.MapValueForKey(m, "authorizationCode") if v4 != nil { var err error x.AuthorizationCode, err = NewOauthFlow(v4, compiler.NewContext("authorizationCode", v4, context)) if err != nil { errors = append(errors, err) } } // repeated NamedAny specification_extension = 5; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewObject creates an object of type Object if possible, returning an error if not. func NewObject(in *yaml.Node, context *compiler.Context) (*Object, error) { errors := make([]error, 0) x := &Object{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedAny additional_properties = 1; // MAP: Any x.AdditionalProperties = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewOperation creates an object of type Operation if possible, returning an error if not. func NewOperation(in *yaml.Node, context *compiler.Context) (*Operation, error) { errors := make([]error, 0) x := &Operation{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"responses"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"callbacks", "deprecated", "description", "externalDocs", "operationId", "parameters", "requestBody", "responses", "security", "servers", "summary", "tags"} allowedPatterns := []*regexp.Regexp{pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // repeated string tags = 1; v1 := compiler.MapValueForKey(m, "tags") if v1 != nil { v, ok := compiler.SequenceNodeForNode(v1) if ok { x.Tags = compiler.StringArrayForSequenceNode(v) } else { message := fmt.Sprintf("has unexpected value for tags: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string summary = 2; v2 := compiler.MapValueForKey(m, "summary") if v2 != nil { x.Summary, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for summary: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 3; v3 := compiler.MapValueForKey(m, "description") if v3 != nil { x.Description, ok = compiler.StringForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } // ExternalDocs external_docs = 4; v4 := compiler.MapValueForKey(m, "externalDocs") if v4 != nil { var err error x.ExternalDocs, err = NewExternalDocs(v4, compiler.NewContext("externalDocs", v4, context)) if err != nil { errors = append(errors, err) } } // string operation_id = 5; v5 := compiler.MapValueForKey(m, "operationId") if v5 != nil { x.OperationId, ok = compiler.StringForScalarNode(v5) if !ok { message := fmt.Sprintf("has unexpected value for operationId: %s", compiler.Display(v5)) errors = append(errors, compiler.NewError(context, message)) } } // repeated ParameterOrReference parameters = 6; v6 := compiler.MapValueForKey(m, "parameters") if v6 != nil { // repeated ParameterOrReference x.Parameters = make([]*ParameterOrReference, 0) a, ok := compiler.SequenceNodeForNode(v6) if ok { for _, item := range a.Content { y, err := NewParameterOrReference(item, compiler.NewContext("parameters", item, context)) if err != nil { errors = append(errors, err) } x.Parameters = append(x.Parameters, y) } } } // RequestBodyOrReference request_body = 7; v7 := compiler.MapValueForKey(m, "requestBody") if v7 != nil { var err error x.RequestBody, err = NewRequestBodyOrReference(v7, compiler.NewContext("requestBody", v7, context)) if err != nil { errors = append(errors, err) } } // Responses responses = 8; v8 := compiler.MapValueForKey(m, "responses") if v8 != nil { var err error x.Responses, err = NewResponses(v8, compiler.NewContext("responses", v8, context)) if err != nil { errors = append(errors, err) } } // CallbacksOrReferences callbacks = 9; v9 := compiler.MapValueForKey(m, "callbacks") if v9 != nil { var err error x.Callbacks, err = NewCallbacksOrReferences(v9, compiler.NewContext("callbacks", v9, context)) if err != nil { errors = append(errors, err) } } // bool deprecated = 10; v10 := compiler.MapValueForKey(m, "deprecated") if v10 != nil { x.Deprecated, ok = compiler.BoolForScalarNode(v10) if !ok { message := fmt.Sprintf("has unexpected value for deprecated: %s", compiler.Display(v10)) errors = append(errors, compiler.NewError(context, message)) } } // repeated SecurityRequirement security = 11; v11 := compiler.MapValueForKey(m, "security") if v11 != nil { // repeated SecurityRequirement x.Security = make([]*SecurityRequirement, 0) a, ok := compiler.SequenceNodeForNode(v11) if ok { for _, item := range a.Content { y, err := NewSecurityRequirement(item, compiler.NewContext("security", item, context)) if err != nil { errors = append(errors, err) } x.Security = append(x.Security, y) } } } // repeated Server servers = 12; v12 := compiler.MapValueForKey(m, "servers") if v12 != nil { // repeated Server x.Servers = make([]*Server, 0) a, ok := compiler.SequenceNodeForNode(v12) if ok { for _, item := range a.Content { y, err := NewServer(item, compiler.NewContext("servers", item, context)) if err != nil { errors = append(errors, err) } x.Servers = append(x.Servers, y) } } } // repeated NamedAny specification_extension = 13; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewParameter creates an object of type Parameter if possible, returning an error if not. func NewParameter(in *yaml.Node, context *compiler.Context) (*Parameter, error) { errors := make([]error, 0) x := &Parameter{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"in", "name"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"allowEmptyValue", "allowReserved", "content", "deprecated", "description", "example", "examples", "explode", "in", "name", "required", "schema", "style"} allowedPatterns := []*regexp.Regexp{pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string in = 2; v2 := compiler.MapValueForKey(m, "in") if v2 != nil { x.In, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for in: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 3; v3 := compiler.MapValueForKey(m, "description") if v3 != nil { x.Description, ok = compiler.StringForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } // bool required = 4; v4 := compiler.MapValueForKey(m, "required") if v4 != nil { x.Required, ok = compiler.BoolForScalarNode(v4) if !ok { message := fmt.Sprintf("has unexpected value for required: %s", compiler.Display(v4)) errors = append(errors, compiler.NewError(context, message)) } } // bool deprecated = 5; v5 := compiler.MapValueForKey(m, "deprecated") if v5 != nil { x.Deprecated, ok = compiler.BoolForScalarNode(v5) if !ok { message := fmt.Sprintf("has unexpected value for deprecated: %s", compiler.Display(v5)) errors = append(errors, compiler.NewError(context, message)) } } // bool allow_empty_value = 6; v6 := compiler.MapValueForKey(m, "allowEmptyValue") if v6 != nil { x.AllowEmptyValue, ok = compiler.BoolForScalarNode(v6) if !ok { message := fmt.Sprintf("has unexpected value for allowEmptyValue: %s", compiler.Display(v6)) errors = append(errors, compiler.NewError(context, message)) } } // string style = 7; v7 := compiler.MapValueForKey(m, "style") if v7 != nil { x.Style, ok = compiler.StringForScalarNode(v7) if !ok { message := fmt.Sprintf("has unexpected value for style: %s", compiler.Display(v7)) errors = append(errors, compiler.NewError(context, message)) } } // bool explode = 8; v8 := compiler.MapValueForKey(m, "explode") if v8 != nil { x.Explode, ok = compiler.BoolForScalarNode(v8) if !ok { message := fmt.Sprintf("has unexpected value for explode: %s", compiler.Display(v8)) errors = append(errors, compiler.NewError(context, message)) } } // bool allow_reserved = 9; v9 := compiler.MapValueForKey(m, "allowReserved") if v9 != nil { x.AllowReserved, ok = compiler.BoolForScalarNode(v9) if !ok { message := fmt.Sprintf("has unexpected value for allowReserved: %s", compiler.Display(v9)) errors = append(errors, compiler.NewError(context, message)) } } // SchemaOrReference schema = 10; v10 := compiler.MapValueForKey(m, "schema") if v10 != nil { var err error x.Schema, err = NewSchemaOrReference(v10, compiler.NewContext("schema", v10, context)) if err != nil { errors = append(errors, err) } } // Any example = 11; v11 := compiler.MapValueForKey(m, "example") if v11 != nil { var err error x.Example, err = NewAny(v11, compiler.NewContext("example", v11, context)) if err != nil { errors = append(errors, err) } } // ExamplesOrReferences examples = 12; v12 := compiler.MapValueForKey(m, "examples") if v12 != nil { var err error x.Examples, err = NewExamplesOrReferences(v12, compiler.NewContext("examples", v12, context)) if err != nil { errors = append(errors, err) } } // MediaTypes content = 13; v13 := compiler.MapValueForKey(m, "content") if v13 != nil { var err error x.Content, err = NewMediaTypes(v13, compiler.NewContext("content", v13, context)) if err != nil { errors = append(errors, err) } } // repeated NamedAny specification_extension = 14; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewParameterOrReference creates an object of type ParameterOrReference if possible, returning an error if not. func NewParameterOrReference(in *yaml.Node, context *compiler.Context) (*ParameterOrReference, error) { errors := make([]error, 0) x := &ParameterOrReference{} matched := false // Parameter parameter = 1; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewParameter(m, compiler.NewContext("parameter", m, context)) if matchingError == nil { x.Oneof = &ParameterOrReference_Parameter{Parameter: t} matched = true } else { errors = append(errors, matchingError) } } } // Reference reference = 2; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewReference(m, compiler.NewContext("reference", m, context)) if matchingError == nil { x.Oneof = &ParameterOrReference_Reference{Reference: t} matched = true } else { errors = append(errors, matchingError) } } } if matched { // since the oneof matched one of its possibilities, discard any matching errors errors = make([]error, 0) } else { message := fmt.Sprintf("contains an invalid ParameterOrReference") err := compiler.NewError(context, message) errors = []error{err} } return x, compiler.NewErrorGroupOrNil(errors) } // NewParametersOrReferences creates an object of type ParametersOrReferences if possible, returning an error if not. func NewParametersOrReferences(in *yaml.Node, context *compiler.Context) (*ParametersOrReferences, error) { errors := make([]error, 0) x := &ParametersOrReferences{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedParameterOrReference additional_properties = 1; // MAP: ParameterOrReference x.AdditionalProperties = make([]*NamedParameterOrReference, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedParameterOrReference{} pair.Name = k var err error pair.Value, err = NewParameterOrReference(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewPathItem creates an object of type PathItem if possible, returning an error if not. func NewPathItem(in *yaml.Node, context *compiler.Context) (*PathItem, error) { errors := make([]error, 0) x := &PathItem{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"$ref", "delete", "description", "get", "head", "options", "parameters", "patch", "post", "put", "servers", "summary", "trace"} allowedPatterns := []*regexp.Regexp{pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string _ref = 1; v1 := compiler.MapValueForKey(m, "$ref") if v1 != nil { x.XRef, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for $ref: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string summary = 2; v2 := compiler.MapValueForKey(m, "summary") if v2 != nil { x.Summary, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for summary: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 3; v3 := compiler.MapValueForKey(m, "description") if v3 != nil { x.Description, ok = compiler.StringForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } // Operation get = 4; v4 := compiler.MapValueForKey(m, "get") if v4 != nil { var err error x.Get, err = NewOperation(v4, compiler.NewContext("get", v4, context)) if err != nil { errors = append(errors, err) } } // Operation put = 5; v5 := compiler.MapValueForKey(m, "put") if v5 != nil { var err error x.Put, err = NewOperation(v5, compiler.NewContext("put", v5, context)) if err != nil { errors = append(errors, err) } } // Operation post = 6; v6 := compiler.MapValueForKey(m, "post") if v6 != nil { var err error x.Post, err = NewOperation(v6, compiler.NewContext("post", v6, context)) if err != nil { errors = append(errors, err) } } // Operation delete = 7; v7 := compiler.MapValueForKey(m, "delete") if v7 != nil { var err error x.Delete, err = NewOperation(v7, compiler.NewContext("delete", v7, context)) if err != nil { errors = append(errors, err) } } // Operation options = 8; v8 := compiler.MapValueForKey(m, "options") if v8 != nil { var err error x.Options, err = NewOperation(v8, compiler.NewContext("options", v8, context)) if err != nil { errors = append(errors, err) } } // Operation head = 9; v9 := compiler.MapValueForKey(m, "head") if v9 != nil { var err error x.Head, err = NewOperation(v9, compiler.NewContext("head", v9, context)) if err != nil { errors = append(errors, err) } } // Operation patch = 10; v10 := compiler.MapValueForKey(m, "patch") if v10 != nil { var err error x.Patch, err = NewOperation(v10, compiler.NewContext("patch", v10, context)) if err != nil { errors = append(errors, err) } } // Operation trace = 11; v11 := compiler.MapValueForKey(m, "trace") if v11 != nil { var err error x.Trace, err = NewOperation(v11, compiler.NewContext("trace", v11, context)) if err != nil { errors = append(errors, err) } } // repeated Server servers = 12; v12 := compiler.MapValueForKey(m, "servers") if v12 != nil { // repeated Server x.Servers = make([]*Server, 0) a, ok := compiler.SequenceNodeForNode(v12) if ok { for _, item := range a.Content { y, err := NewServer(item, compiler.NewContext("servers", item, context)) if err != nil { errors = append(errors, err) } x.Servers = append(x.Servers, y) } } } // repeated ParameterOrReference parameters = 13; v13 := compiler.MapValueForKey(m, "parameters") if v13 != nil { // repeated ParameterOrReference x.Parameters = make([]*ParameterOrReference, 0) a, ok := compiler.SequenceNodeForNode(v13) if ok { for _, item := range a.Content { y, err := NewParameterOrReference(item, compiler.NewContext("parameters", item, context)) if err != nil { errors = append(errors, err) } x.Parameters = append(x.Parameters, y) } } } // repeated NamedAny specification_extension = 14; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewPaths creates an object of type Paths if possible, returning an error if not. func NewPaths(in *yaml.Node, context *compiler.Context) (*Paths, error) { errors := make([]error, 0) x := &Paths{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{} allowedPatterns := []*regexp.Regexp{pattern2, pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // repeated NamedPathItem path = 1; // MAP: PathItem ^/ x.Path = make([]*NamedPathItem, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "/") { pair := &NamedPathItem{} pair.Name = k var err error pair.Value, err = NewPathItem(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } x.Path = append(x.Path, pair) } } } // repeated NamedAny specification_extension = 2; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewProperties creates an object of type Properties if possible, returning an error if not. func NewProperties(in *yaml.Node, context *compiler.Context) (*Properties, error) { errors := make([]error, 0) x := &Properties{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedSchemaOrReference additional_properties = 1; // MAP: SchemaOrReference x.AdditionalProperties = make([]*NamedSchemaOrReference, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedSchemaOrReference{} pair.Name = k var err error pair.Value, err = NewSchemaOrReference(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewReference creates an object of type Reference if possible, returning an error if not. func NewReference(in *yaml.Node, context *compiler.Context) (*Reference, error) { errors := make([]error, 0) x := &Reference{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"$ref"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string _ref = 1; v1 := compiler.MapValueForKey(m, "$ref") if v1 != nil { x.XRef, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for $ref: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string summary = 2; v2 := compiler.MapValueForKey(m, "summary") if v2 != nil { x.Summary, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for summary: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 3; v3 := compiler.MapValueForKey(m, "description") if v3 != nil { x.Description, ok = compiler.StringForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewRequestBodiesOrReferences creates an object of type RequestBodiesOrReferences if possible, returning an error if not. func NewRequestBodiesOrReferences(in *yaml.Node, context *compiler.Context) (*RequestBodiesOrReferences, error) { errors := make([]error, 0) x := &RequestBodiesOrReferences{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedRequestBodyOrReference additional_properties = 1; // MAP: RequestBodyOrReference x.AdditionalProperties = make([]*NamedRequestBodyOrReference, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedRequestBodyOrReference{} pair.Name = k var err error pair.Value, err = NewRequestBodyOrReference(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewRequestBody creates an object of type RequestBody if possible, returning an error if not. func NewRequestBody(in *yaml.Node, context *compiler.Context) (*RequestBody, error) { errors := make([]error, 0) x := &RequestBody{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"content"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"content", "description", "required"} allowedPatterns := []*regexp.Regexp{pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string description = 1; v1 := compiler.MapValueForKey(m, "description") if v1 != nil { x.Description, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // MediaTypes content = 2; v2 := compiler.MapValueForKey(m, "content") if v2 != nil { var err error x.Content, err = NewMediaTypes(v2, compiler.NewContext("content", v2, context)) if err != nil { errors = append(errors, err) } } // bool required = 3; v3 := compiler.MapValueForKey(m, "required") if v3 != nil { x.Required, ok = compiler.BoolForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for required: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny specification_extension = 4; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewRequestBodyOrReference creates an object of type RequestBodyOrReference if possible, returning an error if not. func NewRequestBodyOrReference(in *yaml.Node, context *compiler.Context) (*RequestBodyOrReference, error) { errors := make([]error, 0) x := &RequestBodyOrReference{} matched := false // RequestBody request_body = 1; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewRequestBody(m, compiler.NewContext("requestBody", m, context)) if matchingError == nil { x.Oneof = &RequestBodyOrReference_RequestBody{RequestBody: t} matched = true } else { errors = append(errors, matchingError) } } } // Reference reference = 2; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewReference(m, compiler.NewContext("reference", m, context)) if matchingError == nil { x.Oneof = &RequestBodyOrReference_Reference{Reference: t} matched = true } else { errors = append(errors, matchingError) } } } if matched { // since the oneof matched one of its possibilities, discard any matching errors errors = make([]error, 0) } else { message := fmt.Sprintf("contains an invalid RequestBodyOrReference") err := compiler.NewError(context, message) errors = []error{err} } return x, compiler.NewErrorGroupOrNil(errors) } // NewResponse creates an object of type Response if possible, returning an error if not. func NewResponse(in *yaml.Node, context *compiler.Context) (*Response, error) { errors := make([]error, 0) x := &Response{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"description"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"content", "description", "headers", "links"} allowedPatterns := []*regexp.Regexp{pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string description = 1; v1 := compiler.MapValueForKey(m, "description") if v1 != nil { x.Description, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // HeadersOrReferences headers = 2; v2 := compiler.MapValueForKey(m, "headers") if v2 != nil { var err error x.Headers, err = NewHeadersOrReferences(v2, compiler.NewContext("headers", v2, context)) if err != nil { errors = append(errors, err) } } // MediaTypes content = 3; v3 := compiler.MapValueForKey(m, "content") if v3 != nil { var err error x.Content, err = NewMediaTypes(v3, compiler.NewContext("content", v3, context)) if err != nil { errors = append(errors, err) } } // LinksOrReferences links = 4; v4 := compiler.MapValueForKey(m, "links") if v4 != nil { var err error x.Links, err = NewLinksOrReferences(v4, compiler.NewContext("links", v4, context)) if err != nil { errors = append(errors, err) } } // repeated NamedAny specification_extension = 5; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewResponseOrReference creates an object of type ResponseOrReference if possible, returning an error if not. func NewResponseOrReference(in *yaml.Node, context *compiler.Context) (*ResponseOrReference, error) { errors := make([]error, 0) x := &ResponseOrReference{} matched := false // Response response = 1; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewResponse(m, compiler.NewContext("response", m, context)) if matchingError == nil { x.Oneof = &ResponseOrReference_Response{Response: t} matched = true } else { errors = append(errors, matchingError) } } } // Reference reference = 2; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewReference(m, compiler.NewContext("reference", m, context)) if matchingError == nil { x.Oneof = &ResponseOrReference_Reference{Reference: t} matched = true } else { errors = append(errors, matchingError) } } } if matched { // since the oneof matched one of its possibilities, discard any matching errors errors = make([]error, 0) } else { message := fmt.Sprintf("contains an invalid ResponseOrReference") err := compiler.NewError(context, message) errors = []error{err} } return x, compiler.NewErrorGroupOrNil(errors) } // NewResponses creates an object of type Responses if possible, returning an error if not. func NewResponses(in *yaml.Node, context *compiler.Context) (*Responses, error) { errors := make([]error, 0) x := &Responses{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"default"} allowedPatterns := []*regexp.Regexp{pattern3, pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // ResponseOrReference default = 1; v1 := compiler.MapValueForKey(m, "default") if v1 != nil { var err error x.Default, err = NewResponseOrReference(v1, compiler.NewContext("default", v1, context)) if err != nil { errors = append(errors, err) } } // repeated NamedResponseOrReference response_or_reference = 2; // MAP: ResponseOrReference ^([0-9X]{3})$ x.ResponseOrReference = make([]*NamedResponseOrReference, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if pattern3.MatchString(k) { pair := &NamedResponseOrReference{} pair.Name = k var err error pair.Value, err = NewResponseOrReference(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } x.ResponseOrReference = append(x.ResponseOrReference, pair) } } } // repeated NamedAny specification_extension = 3; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewResponsesOrReferences creates an object of type ResponsesOrReferences if possible, returning an error if not. func NewResponsesOrReferences(in *yaml.Node, context *compiler.Context) (*ResponsesOrReferences, error) { errors := make([]error, 0) x := &ResponsesOrReferences{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedResponseOrReference additional_properties = 1; // MAP: ResponseOrReference x.AdditionalProperties = make([]*NamedResponseOrReference, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedResponseOrReference{} pair.Name = k var err error pair.Value, err = NewResponseOrReference(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewSchema creates an object of type Schema if possible, returning an error if not. func NewSchema(in *yaml.Node, context *compiler.Context) (*Schema, error) { errors := make([]error, 0) x := &Schema{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"additionalProperties", "allOf", "anyOf", "default", "deprecated", "description", "discriminator", "enum", "example", "exclusiveMaximum", "exclusiveMinimum", "externalDocs", "format", "items", "maxItems", "maxLength", "maxProperties", "maximum", "minItems", "minLength", "minProperties", "minimum", "multipleOf", "not", "nullable", "oneOf", "pattern", "properties", "readOnly", "required", "title", "type", "uniqueItems", "writeOnly", "xml"} allowedPatterns := []*regexp.Regexp{pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // bool nullable = 1; v1 := compiler.MapValueForKey(m, "nullable") if v1 != nil { x.Nullable, ok = compiler.BoolForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for nullable: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // Discriminator discriminator = 2; v2 := compiler.MapValueForKey(m, "discriminator") if v2 != nil { var err error x.Discriminator, err = NewDiscriminator(v2, compiler.NewContext("discriminator", v2, context)) if err != nil { errors = append(errors, err) } } // bool read_only = 3; v3 := compiler.MapValueForKey(m, "readOnly") if v3 != nil { x.ReadOnly, ok = compiler.BoolForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for readOnly: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } // bool write_only = 4; v4 := compiler.MapValueForKey(m, "writeOnly") if v4 != nil { x.WriteOnly, ok = compiler.BoolForScalarNode(v4) if !ok { message := fmt.Sprintf("has unexpected value for writeOnly: %s", compiler.Display(v4)) errors = append(errors, compiler.NewError(context, message)) } } // Xml xml = 5; v5 := compiler.MapValueForKey(m, "xml") if v5 != nil { var err error x.Xml, err = NewXml(v5, compiler.NewContext("xml", v5, context)) if err != nil { errors = append(errors, err) } } // ExternalDocs external_docs = 6; v6 := compiler.MapValueForKey(m, "externalDocs") if v6 != nil { var err error x.ExternalDocs, err = NewExternalDocs(v6, compiler.NewContext("externalDocs", v6, context)) if err != nil { errors = append(errors, err) } } // Any example = 7; v7 := compiler.MapValueForKey(m, "example") if v7 != nil { var err error x.Example, err = NewAny(v7, compiler.NewContext("example", v7, context)) if err != nil { errors = append(errors, err) } } // bool deprecated = 8; v8 := compiler.MapValueForKey(m, "deprecated") if v8 != nil { x.Deprecated, ok = compiler.BoolForScalarNode(v8) if !ok { message := fmt.Sprintf("has unexpected value for deprecated: %s", compiler.Display(v8)) errors = append(errors, compiler.NewError(context, message)) } } // string title = 9; v9 := compiler.MapValueForKey(m, "title") if v9 != nil { x.Title, ok = compiler.StringForScalarNode(v9) if !ok { message := fmt.Sprintf("has unexpected value for title: %s", compiler.Display(v9)) errors = append(errors, compiler.NewError(context, message)) } } // float multiple_of = 10; v10 := compiler.MapValueForKey(m, "multipleOf") if v10 != nil { v, ok := compiler.FloatForScalarNode(v10) if ok { x.MultipleOf = v } else { message := fmt.Sprintf("has unexpected value for multipleOf: %s", compiler.Display(v10)) errors = append(errors, compiler.NewError(context, message)) } } // float maximum = 11; v11 := compiler.MapValueForKey(m, "maximum") if v11 != nil { v, ok := compiler.FloatForScalarNode(v11) if ok { x.Maximum = v } else { message := fmt.Sprintf("has unexpected value for maximum: %s", compiler.Display(v11)) errors = append(errors, compiler.NewError(context, message)) } } // bool exclusive_maximum = 12; v12 := compiler.MapValueForKey(m, "exclusiveMaximum") if v12 != nil { x.ExclusiveMaximum, ok = compiler.BoolForScalarNode(v12) if !ok { message := fmt.Sprintf("has unexpected value for exclusiveMaximum: %s", compiler.Display(v12)) errors = append(errors, compiler.NewError(context, message)) } } // float minimum = 13; v13 := compiler.MapValueForKey(m, "minimum") if v13 != nil { v, ok := compiler.FloatForScalarNode(v13) if ok { x.Minimum = v } else { message := fmt.Sprintf("has unexpected value for minimum: %s", compiler.Display(v13)) errors = append(errors, compiler.NewError(context, message)) } } // bool exclusive_minimum = 14; v14 := compiler.MapValueForKey(m, "exclusiveMinimum") if v14 != nil { x.ExclusiveMinimum, ok = compiler.BoolForScalarNode(v14) if !ok { message := fmt.Sprintf("has unexpected value for exclusiveMinimum: %s", compiler.Display(v14)) errors = append(errors, compiler.NewError(context, message)) } } // int64 max_length = 15; v15 := compiler.MapValueForKey(m, "maxLength") if v15 != nil { t, ok := compiler.IntForScalarNode(v15) if ok { x.MaxLength = int64(t) } else { message := fmt.Sprintf("has unexpected value for maxLength: %s", compiler.Display(v15)) errors = append(errors, compiler.NewError(context, message)) } } // int64 min_length = 16; v16 := compiler.MapValueForKey(m, "minLength") if v16 != nil { t, ok := compiler.IntForScalarNode(v16) if ok { x.MinLength = int64(t) } else { message := fmt.Sprintf("has unexpected value for minLength: %s", compiler.Display(v16)) errors = append(errors, compiler.NewError(context, message)) } } // string pattern = 17; v17 := compiler.MapValueForKey(m, "pattern") if v17 != nil { x.Pattern, ok = compiler.StringForScalarNode(v17) if !ok { message := fmt.Sprintf("has unexpected value for pattern: %s", compiler.Display(v17)) errors = append(errors, compiler.NewError(context, message)) } } // int64 max_items = 18; v18 := compiler.MapValueForKey(m, "maxItems") if v18 != nil { t, ok := compiler.IntForScalarNode(v18) if ok { x.MaxItems = int64(t) } else { message := fmt.Sprintf("has unexpected value for maxItems: %s", compiler.Display(v18)) errors = append(errors, compiler.NewError(context, message)) } } // int64 min_items = 19; v19 := compiler.MapValueForKey(m, "minItems") if v19 != nil { t, ok := compiler.IntForScalarNode(v19) if ok { x.MinItems = int64(t) } else { message := fmt.Sprintf("has unexpected value for minItems: %s", compiler.Display(v19)) errors = append(errors, compiler.NewError(context, message)) } } // bool unique_items = 20; v20 := compiler.MapValueForKey(m, "uniqueItems") if v20 != nil { x.UniqueItems, ok = compiler.BoolForScalarNode(v20) if !ok { message := fmt.Sprintf("has unexpected value for uniqueItems: %s", compiler.Display(v20)) errors = append(errors, compiler.NewError(context, message)) } } // int64 max_properties = 21; v21 := compiler.MapValueForKey(m, "maxProperties") if v21 != nil { t, ok := compiler.IntForScalarNode(v21) if ok { x.MaxProperties = int64(t) } else { message := fmt.Sprintf("has unexpected value for maxProperties: %s", compiler.Display(v21)) errors = append(errors, compiler.NewError(context, message)) } } // int64 min_properties = 22; v22 := compiler.MapValueForKey(m, "minProperties") if v22 != nil { t, ok := compiler.IntForScalarNode(v22) if ok { x.MinProperties = int64(t) } else { message := fmt.Sprintf("has unexpected value for minProperties: %s", compiler.Display(v22)) errors = append(errors, compiler.NewError(context, message)) } } // repeated string required = 23; v23 := compiler.MapValueForKey(m, "required") if v23 != nil { v, ok := compiler.SequenceNodeForNode(v23) if ok { x.Required = compiler.StringArrayForSequenceNode(v) } else { message := fmt.Sprintf("has unexpected value for required: %s", compiler.Display(v23)) errors = append(errors, compiler.NewError(context, message)) } } // repeated Any enum = 24; v24 := compiler.MapValueForKey(m, "enum") if v24 != nil { // repeated Any x.Enum = make([]*Any, 0) a, ok := compiler.SequenceNodeForNode(v24) if ok { for _, item := range a.Content { y, err := NewAny(item, compiler.NewContext("enum", item, context)) if err != nil { errors = append(errors, err) } x.Enum = append(x.Enum, y) } } } // string type = 25; v25 := compiler.MapValueForKey(m, "type") if v25 != nil { x.Type, ok = compiler.StringForScalarNode(v25) if !ok { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v25)) errors = append(errors, compiler.NewError(context, message)) } } // repeated SchemaOrReference all_of = 26; v26 := compiler.MapValueForKey(m, "allOf") if v26 != nil { // repeated SchemaOrReference x.AllOf = make([]*SchemaOrReference, 0) a, ok := compiler.SequenceNodeForNode(v26) if ok { for _, item := range a.Content { y, err := NewSchemaOrReference(item, compiler.NewContext("allOf", item, context)) if err != nil { errors = append(errors, err) } x.AllOf = append(x.AllOf, y) } } } // repeated SchemaOrReference one_of = 27; v27 := compiler.MapValueForKey(m, "oneOf") if v27 != nil { // repeated SchemaOrReference x.OneOf = make([]*SchemaOrReference, 0) a, ok := compiler.SequenceNodeForNode(v27) if ok { for _, item := range a.Content { y, err := NewSchemaOrReference(item, compiler.NewContext("oneOf", item, context)) if err != nil { errors = append(errors, err) } x.OneOf = append(x.OneOf, y) } } } // repeated SchemaOrReference any_of = 28; v28 := compiler.MapValueForKey(m, "anyOf") if v28 != nil { // repeated SchemaOrReference x.AnyOf = make([]*SchemaOrReference, 0) a, ok := compiler.SequenceNodeForNode(v28) if ok { for _, item := range a.Content { y, err := NewSchemaOrReference(item, compiler.NewContext("anyOf", item, context)) if err != nil { errors = append(errors, err) } x.AnyOf = append(x.AnyOf, y) } } } // Schema not = 29; v29 := compiler.MapValueForKey(m, "not") if v29 != nil { var err error x.Not, err = NewSchema(v29, compiler.NewContext("not", v29, context)) if err != nil { errors = append(errors, err) } } // ItemsItem items = 30; v30 := compiler.MapValueForKey(m, "items") if v30 != nil { var err error x.Items, err = NewItemsItem(v30, compiler.NewContext("items", v30, context)) if err != nil { errors = append(errors, err) } } // Properties properties = 31; v31 := compiler.MapValueForKey(m, "properties") if v31 != nil { var err error x.Properties, err = NewProperties(v31, compiler.NewContext("properties", v31, context)) if err != nil { errors = append(errors, err) } } // AdditionalPropertiesItem additional_properties = 32; v32 := compiler.MapValueForKey(m, "additionalProperties") if v32 != nil { var err error x.AdditionalProperties, err = NewAdditionalPropertiesItem(v32, compiler.NewContext("additionalProperties", v32, context)) if err != nil { errors = append(errors, err) } } // DefaultType default = 33; v33 := compiler.MapValueForKey(m, "default") if v33 != nil { var err error x.Default, err = NewDefaultType(v33, compiler.NewContext("default", v33, context)) if err != nil { errors = append(errors, err) } } // string description = 34; v34 := compiler.MapValueForKey(m, "description") if v34 != nil { x.Description, ok = compiler.StringForScalarNode(v34) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v34)) errors = append(errors, compiler.NewError(context, message)) } } // string format = 35; v35 := compiler.MapValueForKey(m, "format") if v35 != nil { x.Format, ok = compiler.StringForScalarNode(v35) if !ok { message := fmt.Sprintf("has unexpected value for format: %s", compiler.Display(v35)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny specification_extension = 36; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewSchemaOrReference creates an object of type SchemaOrReference if possible, returning an error if not. func NewSchemaOrReference(in *yaml.Node, context *compiler.Context) (*SchemaOrReference, error) { errors := make([]error, 0) x := &SchemaOrReference{} matched := false // Schema schema = 1; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewSchema(m, compiler.NewContext("schema", m, context)) if matchingError == nil { x.Oneof = &SchemaOrReference_Schema{Schema: t} matched = true } else { errors = append(errors, matchingError) } } } // Reference reference = 2; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewReference(m, compiler.NewContext("reference", m, context)) if matchingError == nil { x.Oneof = &SchemaOrReference_Reference{Reference: t} matched = true } else { errors = append(errors, matchingError) } } } if matched { // since the oneof matched one of its possibilities, discard any matching errors errors = make([]error, 0) } else { message := fmt.Sprintf("contains an invalid SchemaOrReference") err := compiler.NewError(context, message) errors = []error{err} } return x, compiler.NewErrorGroupOrNil(errors) } // NewSchemasOrReferences creates an object of type SchemasOrReferences if possible, returning an error if not. func NewSchemasOrReferences(in *yaml.Node, context *compiler.Context) (*SchemasOrReferences, error) { errors := make([]error, 0) x := &SchemasOrReferences{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedSchemaOrReference additional_properties = 1; // MAP: SchemaOrReference x.AdditionalProperties = make([]*NamedSchemaOrReference, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedSchemaOrReference{} pair.Name = k var err error pair.Value, err = NewSchemaOrReference(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewSecurityRequirement creates an object of type SecurityRequirement if possible, returning an error if not. func NewSecurityRequirement(in *yaml.Node, context *compiler.Context) (*SecurityRequirement, error) { errors := make([]error, 0) x := &SecurityRequirement{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedStringArray additional_properties = 1; // MAP: StringArray x.AdditionalProperties = make([]*NamedStringArray, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedStringArray{} pair.Name = k var err error pair.Value, err = NewStringArray(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewSecurityScheme creates an object of type SecurityScheme if possible, returning an error if not. func NewSecurityScheme(in *yaml.Node, context *compiler.Context) (*SecurityScheme, error) { errors := make([]error, 0) x := &SecurityScheme{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"type"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"bearerFormat", "description", "flows", "in", "name", "openIdConnectUrl", "scheme", "type"} allowedPatterns := []*regexp.Regexp{pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string type = 1; v1 := compiler.MapValueForKey(m, "type") if v1 != nil { x.Type, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for type: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 2; v2 := compiler.MapValueForKey(m, "description") if v2 != nil { x.Description, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // string name = 3; v3 := compiler.MapValueForKey(m, "name") if v3 != nil { x.Name, ok = compiler.StringForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } // string in = 4; v4 := compiler.MapValueForKey(m, "in") if v4 != nil { x.In, ok = compiler.StringForScalarNode(v4) if !ok { message := fmt.Sprintf("has unexpected value for in: %s", compiler.Display(v4)) errors = append(errors, compiler.NewError(context, message)) } } // string scheme = 5; v5 := compiler.MapValueForKey(m, "scheme") if v5 != nil { x.Scheme, ok = compiler.StringForScalarNode(v5) if !ok { message := fmt.Sprintf("has unexpected value for scheme: %s", compiler.Display(v5)) errors = append(errors, compiler.NewError(context, message)) } } // string bearer_format = 6; v6 := compiler.MapValueForKey(m, "bearerFormat") if v6 != nil { x.BearerFormat, ok = compiler.StringForScalarNode(v6) if !ok { message := fmt.Sprintf("has unexpected value for bearerFormat: %s", compiler.Display(v6)) errors = append(errors, compiler.NewError(context, message)) } } // OauthFlows flows = 7; v7 := compiler.MapValueForKey(m, "flows") if v7 != nil { var err error x.Flows, err = NewOauthFlows(v7, compiler.NewContext("flows", v7, context)) if err != nil { errors = append(errors, err) } } // string open_id_connect_url = 8; v8 := compiler.MapValueForKey(m, "openIdConnectUrl") if v8 != nil { x.OpenIdConnectUrl, ok = compiler.StringForScalarNode(v8) if !ok { message := fmt.Sprintf("has unexpected value for openIdConnectUrl: %s", compiler.Display(v8)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny specification_extension = 9; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewSecuritySchemeOrReference creates an object of type SecuritySchemeOrReference if possible, returning an error if not. func NewSecuritySchemeOrReference(in *yaml.Node, context *compiler.Context) (*SecuritySchemeOrReference, error) { errors := make([]error, 0) x := &SecuritySchemeOrReference{} matched := false // SecurityScheme security_scheme = 1; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewSecurityScheme(m, compiler.NewContext("securityScheme", m, context)) if matchingError == nil { x.Oneof = &SecuritySchemeOrReference_SecurityScheme{SecurityScheme: t} matched = true } else { errors = append(errors, matchingError) } } } // Reference reference = 2; { m, ok := compiler.UnpackMap(in) if ok { // errors might be ok here, they mean we just don't have the right subtype t, matchingError := NewReference(m, compiler.NewContext("reference", m, context)) if matchingError == nil { x.Oneof = &SecuritySchemeOrReference_Reference{Reference: t} matched = true } else { errors = append(errors, matchingError) } } } if matched { // since the oneof matched one of its possibilities, discard any matching errors errors = make([]error, 0) } else { message := fmt.Sprintf("contains an invalid SecuritySchemeOrReference") err := compiler.NewError(context, message) errors = []error{err} } return x, compiler.NewErrorGroupOrNil(errors) } // NewSecuritySchemesOrReferences creates an object of type SecuritySchemesOrReferences if possible, returning an error if not. func NewSecuritySchemesOrReferences(in *yaml.Node, context *compiler.Context) (*SecuritySchemesOrReferences, error) { errors := make([]error, 0) x := &SecuritySchemesOrReferences{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedSecuritySchemeOrReference additional_properties = 1; // MAP: SecuritySchemeOrReference x.AdditionalProperties = make([]*NamedSecuritySchemeOrReference, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedSecuritySchemeOrReference{} pair.Name = k var err error pair.Value, err = NewSecuritySchemeOrReference(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewServer creates an object of type Server if possible, returning an error if not. func NewServer(in *yaml.Node, context *compiler.Context) (*Server, error) { errors := make([]error, 0) x := &Server{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"url"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"description", "url", "variables"} allowedPatterns := []*regexp.Regexp{pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string url = 1; v1 := compiler.MapValueForKey(m, "url") if v1 != nil { x.Url, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for url: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 2; v2 := compiler.MapValueForKey(m, "description") if v2 != nil { x.Description, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // ServerVariables variables = 3; v3 := compiler.MapValueForKey(m, "variables") if v3 != nil { var err error x.Variables, err = NewServerVariables(v3, compiler.NewContext("variables", v3, context)) if err != nil { errors = append(errors, err) } } // repeated NamedAny specification_extension = 4; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewServerVariable creates an object of type ServerVariable if possible, returning an error if not. func NewServerVariable(in *yaml.Node, context *compiler.Context) (*ServerVariable, error) { errors := make([]error, 0) x := &ServerVariable{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"default"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"default", "description", "enum"} allowedPatterns := []*regexp.Regexp{pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // repeated string enum = 1; v1 := compiler.MapValueForKey(m, "enum") if v1 != nil { v, ok := compiler.SequenceNodeForNode(v1) if ok { x.Enum = compiler.StringArrayForSequenceNode(v) } else { message := fmt.Sprintf("has unexpected value for enum: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string default = 2; v2 := compiler.MapValueForKey(m, "default") if v2 != nil { x.Default, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for default: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 3; v3 := compiler.MapValueForKey(m, "description") if v3 != nil { x.Description, ok = compiler.StringForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny specification_extension = 4; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewServerVariables creates an object of type ServerVariables if possible, returning an error if not. func NewServerVariables(in *yaml.Node, context *compiler.Context) (*ServerVariables, error) { errors := make([]error, 0) x := &ServerVariables{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedServerVariable additional_properties = 1; // MAP: ServerVariable x.AdditionalProperties = make([]*NamedServerVariable, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedServerVariable{} pair.Name = k var err error pair.Value, err = NewServerVariable(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewSpecificationExtension creates an object of type SpecificationExtension if possible, returning an error if not. func NewSpecificationExtension(in *yaml.Node, context *compiler.Context) (*SpecificationExtension, error) { errors := make([]error, 0) x := &SpecificationExtension{} matched := false switch in.Tag { case "!!bool": var v bool v, matched = compiler.BoolForScalarNode(in) x.Oneof = &SpecificationExtension_Boolean{Boolean: v} case "!!str": var v string v, matched = compiler.StringForScalarNode(in) x.Oneof = &SpecificationExtension_String_{String_: v} case "!!float": var v float64 v, matched = compiler.FloatForScalarNode(in) x.Oneof = &SpecificationExtension_Number{Number: v} case "!!int": var v int64 v, matched = compiler.IntForScalarNode(in) x.Oneof = &SpecificationExtension_Number{Number: float64(v)} } if matched { // since the oneof matched one of its possibilities, discard any matching errors errors = make([]error, 0) } return x, compiler.NewErrorGroupOrNil(errors) } // NewStringArray creates an object of type StringArray if possible, returning an error if not. func NewStringArray(in *yaml.Node, context *compiler.Context) (*StringArray, error) { errors := make([]error, 0) x := &StringArray{} x.Value = make([]string, 0) for _, node := range in.Content { s, _ := compiler.StringForScalarNode(node) x.Value = append(x.Value, s) } return x, compiler.NewErrorGroupOrNil(errors) } // NewStrings creates an object of type Strings if possible, returning an error if not. func NewStrings(in *yaml.Node, context *compiler.Context) (*Strings, error) { errors := make([]error, 0) x := &Strings{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { // repeated NamedString additional_properties = 1; // MAP: string x.AdditionalProperties = make([]*NamedString, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] pair := &NamedString{} pair.Name = k pair.Value, _ = compiler.StringForScalarNode(v) x.AdditionalProperties = append(x.AdditionalProperties, pair) } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewTag creates an object of type Tag if possible, returning an error if not. func NewTag(in *yaml.Node, context *compiler.Context) (*Tag, error) { errors := make([]error, 0) x := &Tag{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { requiredKeys := []string{"name"} missingKeys := compiler.MissingKeysInMap(m, requiredKeys) if len(missingKeys) > 0 { message := fmt.Sprintf("is missing required %s: %+v", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } allowedKeys := []string{"description", "externalDocs", "name"} allowedPatterns := []*regexp.Regexp{pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string description = 2; v2 := compiler.MapValueForKey(m, "description") if v2 != nil { x.Description, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for description: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // ExternalDocs external_docs = 3; v3 := compiler.MapValueForKey(m, "externalDocs") if v3 != nil { var err error x.ExternalDocs, err = NewExternalDocs(v3, compiler.NewContext("externalDocs", v3, context)) if err != nil { errors = append(errors, err) } } // repeated NamedAny specification_extension = 4; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // NewXml creates an object of type Xml if possible, returning an error if not. func NewXml(in *yaml.Node, context *compiler.Context) (*Xml, error) { errors := make([]error, 0) x := &Xml{} m, ok := compiler.UnpackMap(in) if !ok { message := fmt.Sprintf("has unexpected value: %+v (%T)", in, in) errors = append(errors, compiler.NewError(context, message)) } else { allowedKeys := []string{"attribute", "name", "namespace", "prefix", "wrapped"} allowedPatterns := []*regexp.Regexp{pattern1} invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns) if len(invalidKeys) > 0 { message := fmt.Sprintf("has invalid %s: %+v", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, ", ")) errors = append(errors, compiler.NewError(context, message)) } // string name = 1; v1 := compiler.MapValueForKey(m, "name") if v1 != nil { x.Name, ok = compiler.StringForScalarNode(v1) if !ok { message := fmt.Sprintf("has unexpected value for name: %s", compiler.Display(v1)) errors = append(errors, compiler.NewError(context, message)) } } // string namespace = 2; v2 := compiler.MapValueForKey(m, "namespace") if v2 != nil { x.Namespace, ok = compiler.StringForScalarNode(v2) if !ok { message := fmt.Sprintf("has unexpected value for namespace: %s", compiler.Display(v2)) errors = append(errors, compiler.NewError(context, message)) } } // string prefix = 3; v3 := compiler.MapValueForKey(m, "prefix") if v3 != nil { x.Prefix, ok = compiler.StringForScalarNode(v3) if !ok { message := fmt.Sprintf("has unexpected value for prefix: %s", compiler.Display(v3)) errors = append(errors, compiler.NewError(context, message)) } } // bool attribute = 4; v4 := compiler.MapValueForKey(m, "attribute") if v4 != nil { x.Attribute, ok = compiler.BoolForScalarNode(v4) if !ok { message := fmt.Sprintf("has unexpected value for attribute: %s", compiler.Display(v4)) errors = append(errors, compiler.NewError(context, message)) } } // bool wrapped = 5; v5 := compiler.MapValueForKey(m, "wrapped") if v5 != nil { x.Wrapped, ok = compiler.BoolForScalarNode(v5) if !ok { message := fmt.Sprintf("has unexpected value for wrapped: %s", compiler.Display(v5)) errors = append(errors, compiler.NewError(context, message)) } } // repeated NamedAny specification_extension = 6; // MAP: Any ^x- x.SpecificationExtension = make([]*NamedAny, 0) for i := 0; i < len(m.Content); i += 2 { k, ok := compiler.StringForScalarNode(m.Content[i]) if ok { v := m.Content[i+1] if strings.HasPrefix(k, "x-") { pair := &NamedAny{} pair.Name = k result := &Any{} handled, resultFromExt, err := compiler.CallExtension(context, v, k) if handled { if err != nil { errors = append(errors, err) } else { bytes := compiler.Marshal(v) result.Yaml = string(bytes) result.Value = resultFromExt pair.Value = result } } else { pair.Value, err = NewAny(v, compiler.NewContext(k, v, context)) if err != nil { errors = append(errors, err) } } x.SpecificationExtension = append(x.SpecificationExtension, pair) } } } } return x, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside AdditionalPropertiesItem objects. func (m *AdditionalPropertiesItem) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) { p, ok := m.Oneof.(*AdditionalPropertiesItem_SchemaOrReference) if ok { _, err := p.SchemaOrReference.ResolveReferences(root) if err != nil { return nil, err } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Any objects. func (m *Any) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside AnyOrExpression objects. func (m *AnyOrExpression) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) { p, ok := m.Oneof.(*AnyOrExpression_Any) if ok { _, err := p.Any.ResolveReferences(root) if err != nil { return nil, err } } } { p, ok := m.Oneof.(*AnyOrExpression_Expression) if ok { _, err := p.Expression.ResolveReferences(root) if err != nil { return nil, err } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Callback objects. func (m *Callback) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.Path { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside CallbackOrReference objects. func (m *CallbackOrReference) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) { p, ok := m.Oneof.(*CallbackOrReference_Callback) if ok { _, err := p.Callback.ResolveReferences(root) if err != nil { return nil, err } } } { p, ok := m.Oneof.(*CallbackOrReference_Reference) if ok { _, err := p.Reference.ResolveReferences(root) if err != nil { return nil, err } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside CallbacksOrReferences objects. func (m *CallbacksOrReferences) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Components objects. func (m *Components) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Schemas != nil { _, err := m.Schemas.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Responses != nil { _, err := m.Responses.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Parameters != nil { _, err := m.Parameters.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Examples != nil { _, err := m.Examples.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.RequestBodies != nil { _, err := m.RequestBodies.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Headers != nil { _, err := m.Headers.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.SecuritySchemes != nil { _, err := m.SecuritySchemes.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Links != nil { _, err := m.Links.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Callbacks != nil { _, err := m.Callbacks.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Contact objects. func (m *Contact) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside DefaultType objects. func (m *DefaultType) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Discriminator objects. func (m *Discriminator) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Mapping != nil { _, err := m.Mapping.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Document objects. func (m *Document) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Info != nil { _, err := m.Info.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.Servers { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } if m.Paths != nil { _, err := m.Paths.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Components != nil { _, err := m.Components.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.Security { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } for _, item := range m.Tags { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } if m.ExternalDocs != nil { _, err := m.ExternalDocs.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Encoding objects. func (m *Encoding) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Headers != nil { _, err := m.Headers.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Encodings objects. func (m *Encodings) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Example objects. func (m *Example) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Value != nil { _, err := m.Value.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside ExampleOrReference objects. func (m *ExampleOrReference) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) { p, ok := m.Oneof.(*ExampleOrReference_Example) if ok { _, err := p.Example.ResolveReferences(root) if err != nil { return nil, err } } } { p, ok := m.Oneof.(*ExampleOrReference_Reference) if ok { _, err := p.Reference.ResolveReferences(root) if err != nil { return nil, err } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside ExamplesOrReferences objects. func (m *ExamplesOrReferences) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Expression objects. func (m *Expression) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside ExternalDocs objects. func (m *ExternalDocs) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Header objects. func (m *Header) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Schema != nil { _, err := m.Schema.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Example != nil { _, err := m.Example.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Examples != nil { _, err := m.Examples.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Content != nil { _, err := m.Content.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside HeaderOrReference objects. func (m *HeaderOrReference) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) { p, ok := m.Oneof.(*HeaderOrReference_Header) if ok { _, err := p.Header.ResolveReferences(root) if err != nil { return nil, err } } } { p, ok := m.Oneof.(*HeaderOrReference_Reference) if ok { _, err := p.Reference.ResolveReferences(root) if err != nil { return nil, err } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside HeadersOrReferences objects. func (m *HeadersOrReferences) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Info objects. func (m *Info) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Contact != nil { _, err := m.Contact.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.License != nil { _, err := m.License.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside ItemsItem objects. func (m *ItemsItem) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.SchemaOrReference { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside License objects. func (m *License) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Link objects. func (m *Link) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Parameters != nil { _, err := m.Parameters.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.RequestBody != nil { _, err := m.RequestBody.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Server != nil { _, err := m.Server.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside LinkOrReference objects. func (m *LinkOrReference) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) { p, ok := m.Oneof.(*LinkOrReference_Link) if ok { _, err := p.Link.ResolveReferences(root) if err != nil { return nil, err } } } { p, ok := m.Oneof.(*LinkOrReference_Reference) if ok { _, err := p.Reference.ResolveReferences(root) if err != nil { return nil, err } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside LinksOrReferences objects. func (m *LinksOrReferences) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside MediaType objects. func (m *MediaType) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Schema != nil { _, err := m.Schema.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Example != nil { _, err := m.Example.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Examples != nil { _, err := m.Examples.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Encoding != nil { _, err := m.Encoding.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside MediaTypes objects. func (m *MediaTypes) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NamedAny objects. func (m *NamedAny) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Value != nil { _, err := m.Value.ResolveReferences(root) if err != nil { errors = append(errors, err) } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NamedCallbackOrReference objects. func (m *NamedCallbackOrReference) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Value != nil { _, err := m.Value.ResolveReferences(root) if err != nil { errors = append(errors, err) } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NamedEncoding objects. func (m *NamedEncoding) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Value != nil { _, err := m.Value.ResolveReferences(root) if err != nil { errors = append(errors, err) } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NamedExampleOrReference objects. func (m *NamedExampleOrReference) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Value != nil { _, err := m.Value.ResolveReferences(root) if err != nil { errors = append(errors, err) } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NamedHeaderOrReference objects. func (m *NamedHeaderOrReference) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Value != nil { _, err := m.Value.ResolveReferences(root) if err != nil { errors = append(errors, err) } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NamedLinkOrReference objects. func (m *NamedLinkOrReference) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Value != nil { _, err := m.Value.ResolveReferences(root) if err != nil { errors = append(errors, err) } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NamedMediaType objects. func (m *NamedMediaType) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Value != nil { _, err := m.Value.ResolveReferences(root) if err != nil { errors = append(errors, err) } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NamedParameterOrReference objects. func (m *NamedParameterOrReference) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Value != nil { _, err := m.Value.ResolveReferences(root) if err != nil { errors = append(errors, err) } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NamedPathItem objects. func (m *NamedPathItem) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Value != nil { _, err := m.Value.ResolveReferences(root) if err != nil { errors = append(errors, err) } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NamedRequestBodyOrReference objects. func (m *NamedRequestBodyOrReference) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Value != nil { _, err := m.Value.ResolveReferences(root) if err != nil { errors = append(errors, err) } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NamedResponseOrReference objects. func (m *NamedResponseOrReference) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Value != nil { _, err := m.Value.ResolveReferences(root) if err != nil { errors = append(errors, err) } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NamedSchemaOrReference objects. func (m *NamedSchemaOrReference) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Value != nil { _, err := m.Value.ResolveReferences(root) if err != nil { errors = append(errors, err) } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NamedSecuritySchemeOrReference objects. func (m *NamedSecuritySchemeOrReference) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Value != nil { _, err := m.Value.ResolveReferences(root) if err != nil { errors = append(errors, err) } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NamedServerVariable objects. func (m *NamedServerVariable) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Value != nil { _, err := m.Value.ResolveReferences(root) if err != nil { errors = append(errors, err) } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NamedString objects. func (m *NamedString) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside NamedStringArray objects. func (m *NamedStringArray) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Value != nil { _, err := m.Value.ResolveReferences(root) if err != nil { errors = append(errors, err) } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside OauthFlow objects. func (m *OauthFlow) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Scopes != nil { _, err := m.Scopes.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside OauthFlows objects. func (m *OauthFlows) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Implicit != nil { _, err := m.Implicit.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Password != nil { _, err := m.Password.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.ClientCredentials != nil { _, err := m.ClientCredentials.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.AuthorizationCode != nil { _, err := m.AuthorizationCode.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Object objects. func (m *Object) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Operation objects. func (m *Operation) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.ExternalDocs != nil { _, err := m.ExternalDocs.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.Parameters { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } if m.RequestBody != nil { _, err := m.RequestBody.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Responses != nil { _, err := m.Responses.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Callbacks != nil { _, err := m.Callbacks.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.Security { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } for _, item := range m.Servers { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Parameter objects. func (m *Parameter) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Schema != nil { _, err := m.Schema.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Example != nil { _, err := m.Example.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Examples != nil { _, err := m.Examples.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Content != nil { _, err := m.Content.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside ParameterOrReference objects. func (m *ParameterOrReference) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) { p, ok := m.Oneof.(*ParameterOrReference_Parameter) if ok { _, err := p.Parameter.ResolveReferences(root) if err != nil { return nil, err } } } { p, ok := m.Oneof.(*ParameterOrReference_Reference) if ok { _, err := p.Reference.ResolveReferences(root) if err != nil { return nil, err } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside ParametersOrReferences objects. func (m *ParametersOrReferences) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside PathItem objects. func (m *PathItem) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.XRef != "" { info, err := compiler.ReadInfoForRef(root, m.XRef) if err != nil { return nil, err } if info != nil { replacement, err := NewPathItem(info, nil) if err == nil { *m = *replacement return m.ResolveReferences(root) } } return info, nil } if m.Get != nil { _, err := m.Get.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Put != nil { _, err := m.Put.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Post != nil { _, err := m.Post.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Delete != nil { _, err := m.Delete.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Options != nil { _, err := m.Options.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Head != nil { _, err := m.Head.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Patch != nil { _, err := m.Patch.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Trace != nil { _, err := m.Trace.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.Servers { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } for _, item := range m.Parameters { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Paths objects. func (m *Paths) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.Path { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Properties objects. func (m *Properties) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Reference objects. func (m *Reference) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.XRef != "" { info, err := compiler.ReadInfoForRef(root, m.XRef) if err != nil { return nil, err } if info != nil { replacement, err := NewReference(info, nil) if err == nil { *m = *replacement return m.ResolveReferences(root) } } return info, nil } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside RequestBodiesOrReferences objects. func (m *RequestBodiesOrReferences) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside RequestBody objects. func (m *RequestBody) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Content != nil { _, err := m.Content.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside RequestBodyOrReference objects. func (m *RequestBodyOrReference) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) { p, ok := m.Oneof.(*RequestBodyOrReference_RequestBody) if ok { _, err := p.RequestBody.ResolveReferences(root) if err != nil { return nil, err } } } { p, ok := m.Oneof.(*RequestBodyOrReference_Reference) if ok { _, err := p.Reference.ResolveReferences(root) if err != nil { return nil, err } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Response objects. func (m *Response) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Headers != nil { _, err := m.Headers.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Content != nil { _, err := m.Content.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Links != nil { _, err := m.Links.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside ResponseOrReference objects. func (m *ResponseOrReference) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) { p, ok := m.Oneof.(*ResponseOrReference_Response) if ok { _, err := p.Response.ResolveReferences(root) if err != nil { return nil, err } } } { p, ok := m.Oneof.(*ResponseOrReference_Reference) if ok { _, err := p.Reference.ResolveReferences(root) if err != nil { return nil, err } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Responses objects. func (m *Responses) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Default != nil { _, err := m.Default.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.ResponseOrReference { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside ResponsesOrReferences objects. func (m *ResponsesOrReferences) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Schema objects. func (m *Schema) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Discriminator != nil { _, err := m.Discriminator.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Xml != nil { _, err := m.Xml.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.ExternalDocs != nil { _, err := m.ExternalDocs.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Example != nil { _, err := m.Example.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.Enum { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } for _, item := range m.AllOf { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } for _, item := range m.OneOf { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } for _, item := range m.AnyOf { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } if m.Not != nil { _, err := m.Not.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Items != nil { _, err := m.Items.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Properties != nil { _, err := m.Properties.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.AdditionalProperties != nil { _, err := m.AdditionalProperties.ResolveReferences(root) if err != nil { errors = append(errors, err) } } if m.Default != nil { _, err := m.Default.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside SchemaOrReference objects. func (m *SchemaOrReference) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) { p, ok := m.Oneof.(*SchemaOrReference_Schema) if ok { _, err := p.Schema.ResolveReferences(root) if err != nil { return nil, err } } } { p, ok := m.Oneof.(*SchemaOrReference_Reference) if ok { _, err := p.Reference.ResolveReferences(root) if err != nil { return nil, err } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside SchemasOrReferences objects. func (m *SchemasOrReferences) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside SecurityRequirement objects. func (m *SecurityRequirement) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside SecurityScheme objects. func (m *SecurityScheme) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Flows != nil { _, err := m.Flows.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside SecuritySchemeOrReference objects. func (m *SecuritySchemeOrReference) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) { p, ok := m.Oneof.(*SecuritySchemeOrReference_SecurityScheme) if ok { _, err := p.SecurityScheme.ResolveReferences(root) if err != nil { return nil, err } } } { p, ok := m.Oneof.(*SecuritySchemeOrReference_Reference) if ok { _, err := p.Reference.ResolveReferences(root) if err != nil { return nil, err } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside SecuritySchemesOrReferences objects. func (m *SecuritySchemesOrReferences) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Server objects. func (m *Server) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.Variables != nil { _, err := m.Variables.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside ServerVariable objects. func (m *ServerVariable) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside ServerVariables objects. func (m *ServerVariables) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside SpecificationExtension objects. func (m *SpecificationExtension) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside StringArray objects. func (m *StringArray) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Strings objects. func (m *Strings) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.AdditionalProperties { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Tag objects. func (m *Tag) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) if m.ExternalDocs != nil { _, err := m.ExternalDocs.ResolveReferences(root) if err != nil { errors = append(errors, err) } } for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ResolveReferences resolves references found inside Xml objects. func (m *Xml) ResolveReferences(root string) (*yaml.Node, error) { errors := make([]error, 0) for _, item := range m.SpecificationExtension { if item != nil { _, err := item.ResolveReferences(root) if err != nil { errors = append(errors, err) } } } return nil, compiler.NewErrorGroupOrNil(errors) } // ToRawInfo returns a description of AdditionalPropertiesItem suitable for JSON or YAML export. func (m *AdditionalPropertiesItem) ToRawInfo() *yaml.Node { // ONE OF WRAPPER // AdditionalPropertiesItem // {Name:schemaOrReference Type:SchemaOrReference StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v0 := m.GetSchemaOrReference() if v0 != nil { return v0.ToRawInfo() } // {Name:boolean Type:bool StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} if v1, ok := m.GetOneof().(*AdditionalPropertiesItem_Boolean); ok { return compiler.NewScalarNodeForBool(v1.Boolean) } return compiler.NewNullNode() } // ToRawInfo returns a description of Any suitable for JSON or YAML export. func (m *Any) ToRawInfo() *yaml.Node { var err error var node yaml.Node err = yaml.Unmarshal([]byte(m.Yaml), &node) if err == nil { if node.Kind == yaml.DocumentNode { return node.Content[0] } return &node } return compiler.NewNullNode() } // ToRawInfo returns a description of AnyOrExpression suitable for JSON or YAML export. func (m *AnyOrExpression) ToRawInfo() *yaml.Node { // ONE OF WRAPPER // AnyOrExpression // {Name:any Type:Any StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v0 := m.GetAny() if v0 != nil { return v0.ToRawInfo() } // {Name:expression Type:Expression StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v1 := m.GetExpression() if v1 != nil { return v1.ToRawInfo() } return compiler.NewNullNode() } // ToRawInfo returns a description of Callback suitable for JSON or YAML export. func (m *Callback) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Path != nil { for _, item := range m.Path { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of CallbackOrReference suitable for JSON or YAML export. func (m *CallbackOrReference) ToRawInfo() *yaml.Node { // ONE OF WRAPPER // CallbackOrReference // {Name:callback Type:Callback StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v0 := m.GetCallback() if v0 != nil { return v0.ToRawInfo() } // {Name:reference Type:Reference StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v1 := m.GetReference() if v1 != nil { return v1.ToRawInfo() } return compiler.NewNullNode() } // ToRawInfo returns a description of CallbacksOrReferences suitable for JSON or YAML export. func (m *CallbacksOrReferences) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AdditionalProperties != nil { for _, item := range m.AdditionalProperties { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Components suitable for JSON or YAML export. func (m *Components) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Schemas != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("schemas")) info.Content = append(info.Content, m.Schemas.ToRawInfo()) } if m.Responses != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("responses")) info.Content = append(info.Content, m.Responses.ToRawInfo()) } if m.Parameters != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("parameters")) info.Content = append(info.Content, m.Parameters.ToRawInfo()) } if m.Examples != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("examples")) info.Content = append(info.Content, m.Examples.ToRawInfo()) } if m.RequestBodies != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("requestBodies")) info.Content = append(info.Content, m.RequestBodies.ToRawInfo()) } if m.Headers != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("headers")) info.Content = append(info.Content, m.Headers.ToRawInfo()) } if m.SecuritySchemes != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("securitySchemes")) info.Content = append(info.Content, m.SecuritySchemes.ToRawInfo()) } if m.Links != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("links")) info.Content = append(info.Content, m.Links.ToRawInfo()) } if m.Callbacks != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("callbacks")) info.Content = append(info.Content, m.Callbacks.ToRawInfo()) } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Contact suitable for JSON or YAML export. func (m *Contact) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } if m.Url != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("url")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Url)) } if m.Email != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("email")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Email)) } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of DefaultType suitable for JSON or YAML export. func (m *DefaultType) ToRawInfo() *yaml.Node { // ONE OF WRAPPER // DefaultType // {Name:number Type:float StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} if v0, ok := m.GetOneof().(*DefaultType_Number); ok { return compiler.NewScalarNodeForFloat(v0.Number) } // {Name:boolean Type:bool StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} if v1, ok := m.GetOneof().(*DefaultType_Boolean); ok { return compiler.NewScalarNodeForBool(v1.Boolean) } // {Name:string Type:string StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} if v2, ok := m.GetOneof().(*DefaultType_String_); ok { return compiler.NewScalarNodeForString(v2.String_) } return compiler.NewNullNode() } // ToRawInfo returns a description of Discriminator suitable for JSON or YAML export. func (m *Discriminator) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("propertyName")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.PropertyName)) if m.Mapping != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("mapping")) info.Content = append(info.Content, m.Mapping.ToRawInfo()) } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Document suitable for JSON or YAML export. func (m *Document) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("openapi")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Openapi)) // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("info")) info.Content = append(info.Content, m.Info.ToRawInfo()) if len(m.Servers) != 0 { items := compiler.NewSequenceNode() for _, item := range m.Servers { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("servers")) info.Content = append(info.Content, items) } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("paths")) info.Content = append(info.Content, m.Paths.ToRawInfo()) if m.Components != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("components")) info.Content = append(info.Content, m.Components.ToRawInfo()) } if len(m.Security) != 0 { items := compiler.NewSequenceNode() for _, item := range m.Security { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("security")) info.Content = append(info.Content, items) } if len(m.Tags) != 0 { items := compiler.NewSequenceNode() for _, item := range m.Tags { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("tags")) info.Content = append(info.Content, items) } if m.ExternalDocs != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("externalDocs")) info.Content = append(info.Content, m.ExternalDocs.ToRawInfo()) } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Encoding suitable for JSON or YAML export. func (m *Encoding) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.ContentType != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("contentType")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.ContentType)) } if m.Headers != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("headers")) info.Content = append(info.Content, m.Headers.ToRawInfo()) } if m.Style != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("style")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Style)) } if m.Explode != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("explode")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Explode)) } if m.AllowReserved != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("allowReserved")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.AllowReserved)) } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Encodings suitable for JSON or YAML export. func (m *Encodings) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AdditionalProperties != nil { for _, item := range m.AdditionalProperties { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Example suitable for JSON or YAML export. func (m *Example) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Summary != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("summary")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Summary)) } if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.Value != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("value")) info.Content = append(info.Content, m.Value.ToRawInfo()) } if m.ExternalValue != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("externalValue")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.ExternalValue)) } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of ExampleOrReference suitable for JSON or YAML export. func (m *ExampleOrReference) ToRawInfo() *yaml.Node { // ONE OF WRAPPER // ExampleOrReference // {Name:example Type:Example StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v0 := m.GetExample() if v0 != nil { return v0.ToRawInfo() } // {Name:reference Type:Reference StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v1 := m.GetReference() if v1 != nil { return v1.ToRawInfo() } return compiler.NewNullNode() } // ToRawInfo returns a description of ExamplesOrReferences suitable for JSON or YAML export. func (m *ExamplesOrReferences) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AdditionalProperties != nil { for _, item := range m.AdditionalProperties { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Expression suitable for JSON or YAML export. func (m *Expression) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AdditionalProperties != nil { for _, item := range m.AdditionalProperties { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of ExternalDocs suitable for JSON or YAML export. func (m *ExternalDocs) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("url")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Url)) if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Header suitable for JSON or YAML export. func (m *Header) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.Required != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("required")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Required)) } if m.Deprecated != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("deprecated")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Deprecated)) } if m.AllowEmptyValue != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("allowEmptyValue")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.AllowEmptyValue)) } if m.Style != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("style")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Style)) } if m.Explode != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("explode")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Explode)) } if m.AllowReserved != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("allowReserved")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.AllowReserved)) } if m.Schema != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("schema")) info.Content = append(info.Content, m.Schema.ToRawInfo()) } if m.Example != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("example")) info.Content = append(info.Content, m.Example.ToRawInfo()) } if m.Examples != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("examples")) info.Content = append(info.Content, m.Examples.ToRawInfo()) } if m.Content != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("content")) info.Content = append(info.Content, m.Content.ToRawInfo()) } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of HeaderOrReference suitable for JSON or YAML export. func (m *HeaderOrReference) ToRawInfo() *yaml.Node { // ONE OF WRAPPER // HeaderOrReference // {Name:header Type:Header StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v0 := m.GetHeader() if v0 != nil { return v0.ToRawInfo() } // {Name:reference Type:Reference StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v1 := m.GetReference() if v1 != nil { return v1.ToRawInfo() } return compiler.NewNullNode() } // ToRawInfo returns a description of HeadersOrReferences suitable for JSON or YAML export. func (m *HeadersOrReferences) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AdditionalProperties != nil { for _, item := range m.AdditionalProperties { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Info suitable for JSON or YAML export. func (m *Info) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("title")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Title)) if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.TermsOfService != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("termsOfService")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.TermsOfService)) } if m.Contact != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("contact")) info.Content = append(info.Content, m.Contact.ToRawInfo()) } if m.License != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("license")) info.Content = append(info.Content, m.License.ToRawInfo()) } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("version")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Version)) if m.Summary != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("summary")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Summary)) } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of ItemsItem suitable for JSON or YAML export. func (m *ItemsItem) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if len(m.SchemaOrReference) != 0 { items := compiler.NewSequenceNode() for _, item := range m.SchemaOrReference { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("schemaOrReference")) info.Content = append(info.Content, items) } return info } // ToRawInfo returns a description of License suitable for JSON or YAML export. func (m *License) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) if m.Url != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("url")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Url)) } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Link suitable for JSON or YAML export. func (m *Link) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.OperationRef != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("operationRef")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.OperationRef)) } if m.OperationId != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("operationId")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.OperationId)) } if m.Parameters != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("parameters")) info.Content = append(info.Content, m.Parameters.ToRawInfo()) } if m.RequestBody != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("requestBody")) info.Content = append(info.Content, m.RequestBody.ToRawInfo()) } if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.Server != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("server")) info.Content = append(info.Content, m.Server.ToRawInfo()) } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of LinkOrReference suitable for JSON or YAML export. func (m *LinkOrReference) ToRawInfo() *yaml.Node { // ONE OF WRAPPER // LinkOrReference // {Name:link Type:Link StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v0 := m.GetLink() if v0 != nil { return v0.ToRawInfo() } // {Name:reference Type:Reference StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v1 := m.GetReference() if v1 != nil { return v1.ToRawInfo() } return compiler.NewNullNode() } // ToRawInfo returns a description of LinksOrReferences suitable for JSON or YAML export. func (m *LinksOrReferences) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AdditionalProperties != nil { for _, item := range m.AdditionalProperties { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of MediaType suitable for JSON or YAML export. func (m *MediaType) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Schema != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("schema")) info.Content = append(info.Content, m.Schema.ToRawInfo()) } if m.Example != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("example")) info.Content = append(info.Content, m.Example.ToRawInfo()) } if m.Examples != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("examples")) info.Content = append(info.Content, m.Examples.ToRawInfo()) } if m.Encoding != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("encoding")) info.Content = append(info.Content, m.Encoding.ToRawInfo()) } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of MediaTypes suitable for JSON or YAML export. func (m *MediaTypes) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AdditionalProperties != nil { for _, item := range m.AdditionalProperties { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of NamedAny suitable for JSON or YAML export. func (m *NamedAny) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } if m.Value != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("value")) info.Content = append(info.Content, m.Value.ToRawInfo()) } return info } // ToRawInfo returns a description of NamedCallbackOrReference suitable for JSON or YAML export. func (m *NamedCallbackOrReference) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } // &{Name:value Type:CallbackOrReference StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:Mapped value} return info } // ToRawInfo returns a description of NamedEncoding suitable for JSON or YAML export. func (m *NamedEncoding) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } // &{Name:value Type:Encoding StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:Mapped value} return info } // ToRawInfo returns a description of NamedExampleOrReference suitable for JSON or YAML export. func (m *NamedExampleOrReference) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } // &{Name:value Type:ExampleOrReference StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:Mapped value} return info } // ToRawInfo returns a description of NamedHeaderOrReference suitable for JSON or YAML export. func (m *NamedHeaderOrReference) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } // &{Name:value Type:HeaderOrReference StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:Mapped value} return info } // ToRawInfo returns a description of NamedLinkOrReference suitable for JSON or YAML export. func (m *NamedLinkOrReference) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } // &{Name:value Type:LinkOrReference StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:Mapped value} return info } // ToRawInfo returns a description of NamedMediaType suitable for JSON or YAML export. func (m *NamedMediaType) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } // &{Name:value Type:MediaType StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:Mapped value} return info } // ToRawInfo returns a description of NamedParameterOrReference suitable for JSON or YAML export. func (m *NamedParameterOrReference) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } // &{Name:value Type:ParameterOrReference StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:Mapped value} return info } // ToRawInfo returns a description of NamedPathItem suitable for JSON or YAML export. func (m *NamedPathItem) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } // &{Name:value Type:PathItem StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:Mapped value} return info } // ToRawInfo returns a description of NamedRequestBodyOrReference suitable for JSON or YAML export. func (m *NamedRequestBodyOrReference) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } // &{Name:value Type:RequestBodyOrReference StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:Mapped value} return info } // ToRawInfo returns a description of NamedResponseOrReference suitable for JSON or YAML export. func (m *NamedResponseOrReference) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } // &{Name:value Type:ResponseOrReference StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:Mapped value} return info } // ToRawInfo returns a description of NamedSchemaOrReference suitable for JSON or YAML export. func (m *NamedSchemaOrReference) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } // &{Name:value Type:SchemaOrReference StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:Mapped value} return info } // ToRawInfo returns a description of NamedSecuritySchemeOrReference suitable for JSON or YAML export. func (m *NamedSecuritySchemeOrReference) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } // &{Name:value Type:SecuritySchemeOrReference StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:Mapped value} return info } // ToRawInfo returns a description of NamedServerVariable suitable for JSON or YAML export. func (m *NamedServerVariable) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } // &{Name:value Type:ServerVariable StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:Mapped value} return info } // ToRawInfo returns a description of NamedString suitable for JSON or YAML export. func (m *NamedString) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } if m.Value != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("value")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Value)) } return info } // ToRawInfo returns a description of NamedStringArray suitable for JSON or YAML export. func (m *NamedStringArray) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } // &{Name:value Type:StringArray StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:Mapped value} return info } // ToRawInfo returns a description of OauthFlow suitable for JSON or YAML export. func (m *OauthFlow) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AuthorizationUrl != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("authorizationUrl")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.AuthorizationUrl)) } if m.TokenUrl != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("tokenUrl")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.TokenUrl)) } if m.RefreshUrl != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("refreshUrl")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.RefreshUrl)) } if m.Scopes != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("scopes")) info.Content = append(info.Content, m.Scopes.ToRawInfo()) } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of OauthFlows suitable for JSON or YAML export. func (m *OauthFlows) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Implicit != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("implicit")) info.Content = append(info.Content, m.Implicit.ToRawInfo()) } if m.Password != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("password")) info.Content = append(info.Content, m.Password.ToRawInfo()) } if m.ClientCredentials != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("clientCredentials")) info.Content = append(info.Content, m.ClientCredentials.ToRawInfo()) } if m.AuthorizationCode != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("authorizationCode")) info.Content = append(info.Content, m.AuthorizationCode.ToRawInfo()) } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Object suitable for JSON or YAML export. func (m *Object) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AdditionalProperties != nil { for _, item := range m.AdditionalProperties { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Operation suitable for JSON or YAML export. func (m *Operation) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if len(m.Tags) != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("tags")) info.Content = append(info.Content, compiler.NewSequenceNodeForStringArray(m.Tags)) } if m.Summary != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("summary")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Summary)) } if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.ExternalDocs != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("externalDocs")) info.Content = append(info.Content, m.ExternalDocs.ToRawInfo()) } if m.OperationId != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("operationId")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.OperationId)) } if len(m.Parameters) != 0 { items := compiler.NewSequenceNode() for _, item := range m.Parameters { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("parameters")) info.Content = append(info.Content, items) } if m.RequestBody != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("requestBody")) info.Content = append(info.Content, m.RequestBody.ToRawInfo()) } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("responses")) info.Content = append(info.Content, m.Responses.ToRawInfo()) if m.Callbacks != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("callbacks")) info.Content = append(info.Content, m.Callbacks.ToRawInfo()) } if m.Deprecated != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("deprecated")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Deprecated)) } if len(m.Security) != 0 { items := compiler.NewSequenceNode() for _, item := range m.Security { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("security")) info.Content = append(info.Content, items) } if len(m.Servers) != 0 { items := compiler.NewSequenceNode() for _, item := range m.Servers { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("servers")) info.Content = append(info.Content, items) } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Parameter suitable for JSON or YAML export. func (m *Parameter) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("in")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.In)) if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.Required != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("required")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Required)) } if m.Deprecated != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("deprecated")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Deprecated)) } if m.AllowEmptyValue != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("allowEmptyValue")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.AllowEmptyValue)) } if m.Style != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("style")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Style)) } if m.Explode != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("explode")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Explode)) } if m.AllowReserved != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("allowReserved")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.AllowReserved)) } if m.Schema != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("schema")) info.Content = append(info.Content, m.Schema.ToRawInfo()) } if m.Example != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("example")) info.Content = append(info.Content, m.Example.ToRawInfo()) } if m.Examples != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("examples")) info.Content = append(info.Content, m.Examples.ToRawInfo()) } if m.Content != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("content")) info.Content = append(info.Content, m.Content.ToRawInfo()) } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of ParameterOrReference suitable for JSON or YAML export. func (m *ParameterOrReference) ToRawInfo() *yaml.Node { // ONE OF WRAPPER // ParameterOrReference // {Name:parameter Type:Parameter StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v0 := m.GetParameter() if v0 != nil { return v0.ToRawInfo() } // {Name:reference Type:Reference StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v1 := m.GetReference() if v1 != nil { return v1.ToRawInfo() } return compiler.NewNullNode() } // ToRawInfo returns a description of ParametersOrReferences suitable for JSON or YAML export. func (m *ParametersOrReferences) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AdditionalProperties != nil { for _, item := range m.AdditionalProperties { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of PathItem suitable for JSON or YAML export. func (m *PathItem) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.XRef != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("$ref")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.XRef)) } if m.Summary != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("summary")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Summary)) } if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.Get != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("get")) info.Content = append(info.Content, m.Get.ToRawInfo()) } if m.Put != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("put")) info.Content = append(info.Content, m.Put.ToRawInfo()) } if m.Post != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("post")) info.Content = append(info.Content, m.Post.ToRawInfo()) } if m.Delete != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("delete")) info.Content = append(info.Content, m.Delete.ToRawInfo()) } if m.Options != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("options")) info.Content = append(info.Content, m.Options.ToRawInfo()) } if m.Head != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("head")) info.Content = append(info.Content, m.Head.ToRawInfo()) } if m.Patch != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("patch")) info.Content = append(info.Content, m.Patch.ToRawInfo()) } if m.Trace != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("trace")) info.Content = append(info.Content, m.Trace.ToRawInfo()) } if len(m.Servers) != 0 { items := compiler.NewSequenceNode() for _, item := range m.Servers { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("servers")) info.Content = append(info.Content, items) } if len(m.Parameters) != 0 { items := compiler.NewSequenceNode() for _, item := range m.Parameters { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("parameters")) info.Content = append(info.Content, items) } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Paths suitable for JSON or YAML export. func (m *Paths) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Path != nil { for _, item := range m.Path { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Properties suitable for JSON or YAML export. func (m *Properties) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AdditionalProperties != nil { for _, item := range m.AdditionalProperties { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Reference suitable for JSON or YAML export. func (m *Reference) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("$ref")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.XRef)) if m.Summary != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("summary")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Summary)) } if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } return info } // ToRawInfo returns a description of RequestBodiesOrReferences suitable for JSON or YAML export. func (m *RequestBodiesOrReferences) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AdditionalProperties != nil { for _, item := range m.AdditionalProperties { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of RequestBody suitable for JSON or YAML export. func (m *RequestBody) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("content")) info.Content = append(info.Content, m.Content.ToRawInfo()) if m.Required != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("required")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Required)) } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of RequestBodyOrReference suitable for JSON or YAML export. func (m *RequestBodyOrReference) ToRawInfo() *yaml.Node { // ONE OF WRAPPER // RequestBodyOrReference // {Name:requestBody Type:RequestBody StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v0 := m.GetRequestBody() if v0 != nil { return v0.ToRawInfo() } // {Name:reference Type:Reference StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v1 := m.GetReference() if v1 != nil { return v1.ToRawInfo() } return compiler.NewNullNode() } // ToRawInfo returns a description of Response suitable for JSON or YAML export. func (m *Response) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) if m.Headers != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("headers")) info.Content = append(info.Content, m.Headers.ToRawInfo()) } if m.Content != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("content")) info.Content = append(info.Content, m.Content.ToRawInfo()) } if m.Links != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("links")) info.Content = append(info.Content, m.Links.ToRawInfo()) } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of ResponseOrReference suitable for JSON or YAML export. func (m *ResponseOrReference) ToRawInfo() *yaml.Node { // ONE OF WRAPPER // ResponseOrReference // {Name:response Type:Response StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v0 := m.GetResponse() if v0 != nil { return v0.ToRawInfo() } // {Name:reference Type:Reference StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v1 := m.GetReference() if v1 != nil { return v1.ToRawInfo() } return compiler.NewNullNode() } // ToRawInfo returns a description of Responses suitable for JSON or YAML export. func (m *Responses) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Default != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("default")) info.Content = append(info.Content, m.Default.ToRawInfo()) } if m.ResponseOrReference != nil { for _, item := range m.ResponseOrReference { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of ResponsesOrReferences suitable for JSON or YAML export. func (m *ResponsesOrReferences) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AdditionalProperties != nil { for _, item := range m.AdditionalProperties { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Schema suitable for JSON or YAML export. func (m *Schema) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Nullable != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("nullable")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Nullable)) } if m.Discriminator != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("discriminator")) info.Content = append(info.Content, m.Discriminator.ToRawInfo()) } if m.ReadOnly != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("readOnly")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ReadOnly)) } if m.WriteOnly != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("writeOnly")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.WriteOnly)) } if m.Xml != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("xml")) info.Content = append(info.Content, m.Xml.ToRawInfo()) } if m.ExternalDocs != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("externalDocs")) info.Content = append(info.Content, m.ExternalDocs.ToRawInfo()) } if m.Example != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("example")) info.Content = append(info.Content, m.Example.ToRawInfo()) } if m.Deprecated != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("deprecated")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Deprecated)) } if m.Title != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("title")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Title)) } if m.MultipleOf != 0.0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("multipleOf")) info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.MultipleOf)) } if m.Maximum != 0.0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("maximum")) info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Maximum)) } if m.ExclusiveMaximum != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMaximum")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMaximum)) } if m.Minimum != 0.0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("minimum")) info.Content = append(info.Content, compiler.NewScalarNodeForFloat(m.Minimum)) } if m.ExclusiveMinimum != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("exclusiveMinimum")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.ExclusiveMinimum)) } if m.MaxLength != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("maxLength")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MaxLength)) } if m.MinLength != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("minLength")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MinLength)) } if m.Pattern != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("pattern")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Pattern)) } if m.MaxItems != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("maxItems")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MaxItems)) } if m.MinItems != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("minItems")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MinItems)) } if m.UniqueItems != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("uniqueItems")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.UniqueItems)) } if m.MaxProperties != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("maxProperties")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MaxProperties)) } if m.MinProperties != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("minProperties")) info.Content = append(info.Content, compiler.NewScalarNodeForInt(m.MinProperties)) } if len(m.Required) != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("required")) info.Content = append(info.Content, compiler.NewSequenceNodeForStringArray(m.Required)) } if len(m.Enum) != 0 { items := compiler.NewSequenceNode() for _, item := range m.Enum { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("enum")) info.Content = append(info.Content, items) } if m.Type != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("type")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Type)) } if len(m.AllOf) != 0 { items := compiler.NewSequenceNode() for _, item := range m.AllOf { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("allOf")) info.Content = append(info.Content, items) } if len(m.OneOf) != 0 { items := compiler.NewSequenceNode() for _, item := range m.OneOf { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("oneOf")) info.Content = append(info.Content, items) } if len(m.AnyOf) != 0 { items := compiler.NewSequenceNode() for _, item := range m.AnyOf { items.Content = append(items.Content, item.ToRawInfo()) } info.Content = append(info.Content, compiler.NewScalarNodeForString("anyOf")) info.Content = append(info.Content, items) } if m.Not != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("not")) info.Content = append(info.Content, m.Not.ToRawInfo()) } if m.Items != nil { items := compiler.NewSequenceNode() for _, item := range m.Items.SchemaOrReference { items.Content = append(items.Content, item.ToRawInfo()) } if len(items.Content) == 1 { items = items.Content[0] } info.Content = append(info.Content, compiler.NewScalarNodeForString("items")) info.Content = append(info.Content, items) } if m.Properties != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("properties")) info.Content = append(info.Content, m.Properties.ToRawInfo()) } if m.AdditionalProperties != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("additionalProperties")) info.Content = append(info.Content, m.AdditionalProperties.ToRawInfo()) } if m.Default != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("default")) info.Content = append(info.Content, m.Default.ToRawInfo()) } if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.Format != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("format")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Format)) } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of SchemaOrReference suitable for JSON or YAML export. func (m *SchemaOrReference) ToRawInfo() *yaml.Node { // ONE OF WRAPPER // SchemaOrReference // {Name:schema Type:Schema StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v0 := m.GetSchema() if v0 != nil { return v0.ToRawInfo() } // {Name:reference Type:Reference StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v1 := m.GetReference() if v1 != nil { return v1.ToRawInfo() } return compiler.NewNullNode() } // ToRawInfo returns a description of SchemasOrReferences suitable for JSON or YAML export. func (m *SchemasOrReferences) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AdditionalProperties != nil { for _, item := range m.AdditionalProperties { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of SecurityRequirement suitable for JSON or YAML export. func (m *SecurityRequirement) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AdditionalProperties != nil { for _, item := range m.AdditionalProperties { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of SecurityScheme suitable for JSON or YAML export. func (m *SecurityScheme) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("type")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Type)) if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } if m.In != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("in")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.In)) } if m.Scheme != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("scheme")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Scheme)) } if m.BearerFormat != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("bearerFormat")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.BearerFormat)) } if m.Flows != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("flows")) info.Content = append(info.Content, m.Flows.ToRawInfo()) } if m.OpenIdConnectUrl != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("openIdConnectUrl")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.OpenIdConnectUrl)) } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of SecuritySchemeOrReference suitable for JSON or YAML export. func (m *SecuritySchemeOrReference) ToRawInfo() *yaml.Node { // ONE OF WRAPPER // SecuritySchemeOrReference // {Name:securityScheme Type:SecurityScheme StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v0 := m.GetSecurityScheme() if v0 != nil { return v0.ToRawInfo() } // {Name:reference Type:Reference StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} v1 := m.GetReference() if v1 != nil { return v1.ToRawInfo() } return compiler.NewNullNode() } // ToRawInfo returns a description of SecuritySchemesOrReferences suitable for JSON or YAML export. func (m *SecuritySchemesOrReferences) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AdditionalProperties != nil { for _, item := range m.AdditionalProperties { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Server suitable for JSON or YAML export. func (m *Server) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("url")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Url)) if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.Variables != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("variables")) info.Content = append(info.Content, m.Variables.ToRawInfo()) } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of ServerVariable suitable for JSON or YAML export. func (m *ServerVariable) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if len(m.Enum) != 0 { info.Content = append(info.Content, compiler.NewScalarNodeForString("enum")) info.Content = append(info.Content, compiler.NewSequenceNodeForStringArray(m.Enum)) } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("default")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Default)) if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of ServerVariables suitable for JSON or YAML export. func (m *ServerVariables) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.AdditionalProperties != nil { for _, item := range m.AdditionalProperties { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of SpecificationExtension suitable for JSON or YAML export. func (m *SpecificationExtension) ToRawInfo() *yaml.Node { // ONE OF WRAPPER // SpecificationExtension // {Name:number Type:float StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} if v0, ok := m.GetOneof().(*SpecificationExtension_Number); ok { return compiler.NewScalarNodeForFloat(v0.Number) } // {Name:boolean Type:bool StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} if v1, ok := m.GetOneof().(*SpecificationExtension_Boolean); ok { return compiler.NewScalarNodeForBool(v1.Boolean) } // {Name:string Type:string StringEnumValues:[] MapType: Repeated:false Pattern: Implicit:false Description:} if v2, ok := m.GetOneof().(*SpecificationExtension_String_); ok { return compiler.NewScalarNodeForString(v2.String_) } return compiler.NewNullNode() } // ToRawInfo returns a description of StringArray suitable for JSON or YAML export. func (m *StringArray) ToRawInfo() *yaml.Node { return compiler.NewSequenceNodeForStringArray(m.Value) } // ToRawInfo returns a description of Strings suitable for JSON or YAML export. func (m *Strings) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } // &{Name:additionalProperties Type:NamedString StringEnumValues:[] MapType:string Repeated:true Pattern: Implicit:true Description:} return info } // ToRawInfo returns a description of Tag suitable for JSON or YAML export. func (m *Tag) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } // always include this required field. info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) if m.Description != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("description")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Description)) } if m.ExternalDocs != nil { info.Content = append(info.Content, compiler.NewScalarNodeForString("externalDocs")) info.Content = append(info.Content, m.ExternalDocs.ToRawInfo()) } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } // ToRawInfo returns a description of Xml suitable for JSON or YAML export. func (m *Xml) ToRawInfo() *yaml.Node { info := compiler.NewMappingNode() if m == nil { return info } if m.Name != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("name")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Name)) } if m.Namespace != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("namespace")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Namespace)) } if m.Prefix != "" { info.Content = append(info.Content, compiler.NewScalarNodeForString("prefix")) info.Content = append(info.Content, compiler.NewScalarNodeForString(m.Prefix)) } if m.Attribute != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("attribute")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Attribute)) } if m.Wrapped != false { info.Content = append(info.Content, compiler.NewScalarNodeForString("wrapped")) info.Content = append(info.Content, compiler.NewScalarNodeForBool(m.Wrapped)) } if m.SpecificationExtension != nil { for _, item := range m.SpecificationExtension { info.Content = append(info.Content, compiler.NewScalarNodeForString(item.Name)) info.Content = append(info.Content, item.Value.ToRawInfo()) } } return info } var ( pattern0 = regexp.MustCompile("^") pattern1 = regexp.MustCompile("^x-") pattern2 = regexp.MustCompile("^/") pattern3 = regexp.MustCompile("^([0-9X]{3})$") ) ================================================ FILE: vendor/github.com/google/gnostic/openapiv3/OpenAPIv3.pb.go ================================================ // Copyright 2020 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // THIS FILE IS AUTOMATICALLY GENERATED. // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 // protoc v3.18.1 // source: openapiv3/OpenAPIv3.proto package openapi_v3 import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" reflect "reflect" sync "sync" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type AdditionalPropertiesItem struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Types that are assignable to Oneof: // *AdditionalPropertiesItem_SchemaOrReference // *AdditionalPropertiesItem_Boolean Oneof isAdditionalPropertiesItem_Oneof `protobuf_oneof:"oneof"` } func (x *AdditionalPropertiesItem) Reset() { *x = AdditionalPropertiesItem{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *AdditionalPropertiesItem) String() string { return protoimpl.X.MessageStringOf(x) } func (*AdditionalPropertiesItem) ProtoMessage() {} func (x *AdditionalPropertiesItem) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AdditionalPropertiesItem.ProtoReflect.Descriptor instead. func (*AdditionalPropertiesItem) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{0} } func (m *AdditionalPropertiesItem) GetOneof() isAdditionalPropertiesItem_Oneof { if m != nil { return m.Oneof } return nil } func (x *AdditionalPropertiesItem) GetSchemaOrReference() *SchemaOrReference { if x, ok := x.GetOneof().(*AdditionalPropertiesItem_SchemaOrReference); ok { return x.SchemaOrReference } return nil } func (x *AdditionalPropertiesItem) GetBoolean() bool { if x, ok := x.GetOneof().(*AdditionalPropertiesItem_Boolean); ok { return x.Boolean } return false } type isAdditionalPropertiesItem_Oneof interface { isAdditionalPropertiesItem_Oneof() } type AdditionalPropertiesItem_SchemaOrReference struct { SchemaOrReference *SchemaOrReference `protobuf:"bytes,1,opt,name=schema_or_reference,json=schemaOrReference,proto3,oneof"` } type AdditionalPropertiesItem_Boolean struct { Boolean bool `protobuf:"varint,2,opt,name=boolean,proto3,oneof"` } func (*AdditionalPropertiesItem_SchemaOrReference) isAdditionalPropertiesItem_Oneof() {} func (*AdditionalPropertiesItem_Boolean) isAdditionalPropertiesItem_Oneof() {} type Any struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Value *anypb.Any `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` Yaml string `protobuf:"bytes,2,opt,name=yaml,proto3" json:"yaml,omitempty"` } func (x *Any) Reset() { *x = Any{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Any) String() string { return protoimpl.X.MessageStringOf(x) } func (*Any) ProtoMessage() {} func (x *Any) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Any.ProtoReflect.Descriptor instead. func (*Any) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{1} } func (x *Any) GetValue() *anypb.Any { if x != nil { return x.Value } return nil } func (x *Any) GetYaml() string { if x != nil { return x.Yaml } return "" } type AnyOrExpression struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Types that are assignable to Oneof: // *AnyOrExpression_Any // *AnyOrExpression_Expression Oneof isAnyOrExpression_Oneof `protobuf_oneof:"oneof"` } func (x *AnyOrExpression) Reset() { *x = AnyOrExpression{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *AnyOrExpression) String() string { return protoimpl.X.MessageStringOf(x) } func (*AnyOrExpression) ProtoMessage() {} func (x *AnyOrExpression) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AnyOrExpression.ProtoReflect.Descriptor instead. func (*AnyOrExpression) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{2} } func (m *AnyOrExpression) GetOneof() isAnyOrExpression_Oneof { if m != nil { return m.Oneof } return nil } func (x *AnyOrExpression) GetAny() *Any { if x, ok := x.GetOneof().(*AnyOrExpression_Any); ok { return x.Any } return nil } func (x *AnyOrExpression) GetExpression() *Expression { if x, ok := x.GetOneof().(*AnyOrExpression_Expression); ok { return x.Expression } return nil } type isAnyOrExpression_Oneof interface { isAnyOrExpression_Oneof() } type AnyOrExpression_Any struct { Any *Any `protobuf:"bytes,1,opt,name=any,proto3,oneof"` } type AnyOrExpression_Expression struct { Expression *Expression `protobuf:"bytes,2,opt,name=expression,proto3,oneof"` } func (*AnyOrExpression_Any) isAnyOrExpression_Oneof() {} func (*AnyOrExpression_Expression) isAnyOrExpression_Oneof() {} // A map of possible out-of band callbacks related to the parent operation. Each value in the map is a Path Item Object that describes a set of requests that may be initiated by the API provider and the expected responses. The key value used to identify the callback object is an expression, evaluated at runtime, that identifies a URL to use for the callback operation. type Callback struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Path []*NamedPathItem `protobuf:"bytes,1,rep,name=path,proto3" json:"path,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,2,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *Callback) Reset() { *x = Callback{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Callback) String() string { return protoimpl.X.MessageStringOf(x) } func (*Callback) ProtoMessage() {} func (x *Callback) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Callback.ProtoReflect.Descriptor instead. func (*Callback) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{3} } func (x *Callback) GetPath() []*NamedPathItem { if x != nil { return x.Path } return nil } func (x *Callback) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } type CallbackOrReference struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Types that are assignable to Oneof: // *CallbackOrReference_Callback // *CallbackOrReference_Reference Oneof isCallbackOrReference_Oneof `protobuf_oneof:"oneof"` } func (x *CallbackOrReference) Reset() { *x = CallbackOrReference{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *CallbackOrReference) String() string { return protoimpl.X.MessageStringOf(x) } func (*CallbackOrReference) ProtoMessage() {} func (x *CallbackOrReference) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CallbackOrReference.ProtoReflect.Descriptor instead. func (*CallbackOrReference) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{4} } func (m *CallbackOrReference) GetOneof() isCallbackOrReference_Oneof { if m != nil { return m.Oneof } return nil } func (x *CallbackOrReference) GetCallback() *Callback { if x, ok := x.GetOneof().(*CallbackOrReference_Callback); ok { return x.Callback } return nil } func (x *CallbackOrReference) GetReference() *Reference { if x, ok := x.GetOneof().(*CallbackOrReference_Reference); ok { return x.Reference } return nil } type isCallbackOrReference_Oneof interface { isCallbackOrReference_Oneof() } type CallbackOrReference_Callback struct { Callback *Callback `protobuf:"bytes,1,opt,name=callback,proto3,oneof"` } type CallbackOrReference_Reference struct { Reference *Reference `protobuf:"bytes,2,opt,name=reference,proto3,oneof"` } func (*CallbackOrReference_Callback) isCallbackOrReference_Oneof() {} func (*CallbackOrReference_Reference) isCallbackOrReference_Oneof() {} type CallbacksOrReferences struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedCallbackOrReference `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *CallbacksOrReferences) Reset() { *x = CallbacksOrReferences{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *CallbacksOrReferences) String() string { return protoimpl.X.MessageStringOf(x) } func (*CallbacksOrReferences) ProtoMessage() {} func (x *CallbacksOrReferences) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CallbacksOrReferences.ProtoReflect.Descriptor instead. func (*CallbacksOrReferences) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{5} } func (x *CallbacksOrReferences) GetAdditionalProperties() []*NamedCallbackOrReference { if x != nil { return x.AdditionalProperties } return nil } // Holds a set of reusable objects for different aspects of the OAS. All objects defined within the components object will have no effect on the API unless they are explicitly referenced from properties outside the components object. type Components struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Schemas *SchemasOrReferences `protobuf:"bytes,1,opt,name=schemas,proto3" json:"schemas,omitempty"` Responses *ResponsesOrReferences `protobuf:"bytes,2,opt,name=responses,proto3" json:"responses,omitempty"` Parameters *ParametersOrReferences `protobuf:"bytes,3,opt,name=parameters,proto3" json:"parameters,omitempty"` Examples *ExamplesOrReferences `protobuf:"bytes,4,opt,name=examples,proto3" json:"examples,omitempty"` RequestBodies *RequestBodiesOrReferences `protobuf:"bytes,5,opt,name=request_bodies,json=requestBodies,proto3" json:"request_bodies,omitempty"` Headers *HeadersOrReferences `protobuf:"bytes,6,opt,name=headers,proto3" json:"headers,omitempty"` SecuritySchemes *SecuritySchemesOrReferences `protobuf:"bytes,7,opt,name=security_schemes,json=securitySchemes,proto3" json:"security_schemes,omitempty"` Links *LinksOrReferences `protobuf:"bytes,8,opt,name=links,proto3" json:"links,omitempty"` Callbacks *CallbacksOrReferences `protobuf:"bytes,9,opt,name=callbacks,proto3" json:"callbacks,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,10,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *Components) Reset() { *x = Components{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Components) String() string { return protoimpl.X.MessageStringOf(x) } func (*Components) ProtoMessage() {} func (x *Components) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Components.ProtoReflect.Descriptor instead. func (*Components) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{6} } func (x *Components) GetSchemas() *SchemasOrReferences { if x != nil { return x.Schemas } return nil } func (x *Components) GetResponses() *ResponsesOrReferences { if x != nil { return x.Responses } return nil } func (x *Components) GetParameters() *ParametersOrReferences { if x != nil { return x.Parameters } return nil } func (x *Components) GetExamples() *ExamplesOrReferences { if x != nil { return x.Examples } return nil } func (x *Components) GetRequestBodies() *RequestBodiesOrReferences { if x != nil { return x.RequestBodies } return nil } func (x *Components) GetHeaders() *HeadersOrReferences { if x != nil { return x.Headers } return nil } func (x *Components) GetSecuritySchemes() *SecuritySchemesOrReferences { if x != nil { return x.SecuritySchemes } return nil } func (x *Components) GetLinks() *LinksOrReferences { if x != nil { return x.Links } return nil } func (x *Components) GetCallbacks() *CallbacksOrReferences { if x != nil { return x.Callbacks } return nil } func (x *Components) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } // Contact information for the exposed API. type Contact struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` Email string `protobuf:"bytes,3,opt,name=email,proto3" json:"email,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,4,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *Contact) Reset() { *x = Contact{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Contact) String() string { return protoimpl.X.MessageStringOf(x) } func (*Contact) ProtoMessage() {} func (x *Contact) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Contact.ProtoReflect.Descriptor instead. func (*Contact) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{7} } func (x *Contact) GetName() string { if x != nil { return x.Name } return "" } func (x *Contact) GetUrl() string { if x != nil { return x.Url } return "" } func (x *Contact) GetEmail() string { if x != nil { return x.Email } return "" } func (x *Contact) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } type DefaultType struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Types that are assignable to Oneof: // *DefaultType_Number // *DefaultType_Boolean // *DefaultType_String_ Oneof isDefaultType_Oneof `protobuf_oneof:"oneof"` } func (x *DefaultType) Reset() { *x = DefaultType{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *DefaultType) String() string { return protoimpl.X.MessageStringOf(x) } func (*DefaultType) ProtoMessage() {} func (x *DefaultType) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DefaultType.ProtoReflect.Descriptor instead. func (*DefaultType) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{8} } func (m *DefaultType) GetOneof() isDefaultType_Oneof { if m != nil { return m.Oneof } return nil } func (x *DefaultType) GetNumber() float64 { if x, ok := x.GetOneof().(*DefaultType_Number); ok { return x.Number } return 0 } func (x *DefaultType) GetBoolean() bool { if x, ok := x.GetOneof().(*DefaultType_Boolean); ok { return x.Boolean } return false } func (x *DefaultType) GetString_() string { if x, ok := x.GetOneof().(*DefaultType_String_); ok { return x.String_ } return "" } type isDefaultType_Oneof interface { isDefaultType_Oneof() } type DefaultType_Number struct { Number float64 `protobuf:"fixed64,1,opt,name=number,proto3,oneof"` } type DefaultType_Boolean struct { Boolean bool `protobuf:"varint,2,opt,name=boolean,proto3,oneof"` } type DefaultType_String_ struct { String_ string `protobuf:"bytes,3,opt,name=string,proto3,oneof"` } func (*DefaultType_Number) isDefaultType_Oneof() {} func (*DefaultType_Boolean) isDefaultType_Oneof() {} func (*DefaultType_String_) isDefaultType_Oneof() {} // When request bodies or response payloads may be one of a number of different schemas, a `discriminator` object can be used to aid in serialization, deserialization, and validation. The discriminator is a specific object in a schema which is used to inform the consumer of the specification of an alternative schema based on the value associated with it. When using the discriminator, _inline_ schemas will not be considered. type Discriminator struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields PropertyName string `protobuf:"bytes,1,opt,name=property_name,json=propertyName,proto3" json:"property_name,omitempty"` Mapping *Strings `protobuf:"bytes,2,opt,name=mapping,proto3" json:"mapping,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,3,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *Discriminator) Reset() { *x = Discriminator{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Discriminator) String() string { return protoimpl.X.MessageStringOf(x) } func (*Discriminator) ProtoMessage() {} func (x *Discriminator) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Discriminator.ProtoReflect.Descriptor instead. func (*Discriminator) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{9} } func (x *Discriminator) GetPropertyName() string { if x != nil { return x.PropertyName } return "" } func (x *Discriminator) GetMapping() *Strings { if x != nil { return x.Mapping } return nil } func (x *Discriminator) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } type Document struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Openapi string `protobuf:"bytes,1,opt,name=openapi,proto3" json:"openapi,omitempty"` Info *Info `protobuf:"bytes,2,opt,name=info,proto3" json:"info,omitempty"` Servers []*Server `protobuf:"bytes,3,rep,name=servers,proto3" json:"servers,omitempty"` Paths *Paths `protobuf:"bytes,4,opt,name=paths,proto3" json:"paths,omitempty"` Components *Components `protobuf:"bytes,5,opt,name=components,proto3" json:"components,omitempty"` Security []*SecurityRequirement `protobuf:"bytes,6,rep,name=security,proto3" json:"security,omitempty"` Tags []*Tag `protobuf:"bytes,7,rep,name=tags,proto3" json:"tags,omitempty"` ExternalDocs *ExternalDocs `protobuf:"bytes,8,opt,name=external_docs,json=externalDocs,proto3" json:"external_docs,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,9,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *Document) Reset() { *x = Document{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Document) String() string { return protoimpl.X.MessageStringOf(x) } func (*Document) ProtoMessage() {} func (x *Document) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Document.ProtoReflect.Descriptor instead. func (*Document) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{10} } func (x *Document) GetOpenapi() string { if x != nil { return x.Openapi } return "" } func (x *Document) GetInfo() *Info { if x != nil { return x.Info } return nil } func (x *Document) GetServers() []*Server { if x != nil { return x.Servers } return nil } func (x *Document) GetPaths() *Paths { if x != nil { return x.Paths } return nil } func (x *Document) GetComponents() *Components { if x != nil { return x.Components } return nil } func (x *Document) GetSecurity() []*SecurityRequirement { if x != nil { return x.Security } return nil } func (x *Document) GetTags() []*Tag { if x != nil { return x.Tags } return nil } func (x *Document) GetExternalDocs() *ExternalDocs { if x != nil { return x.ExternalDocs } return nil } func (x *Document) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } // A single encoding definition applied to a single schema property. type Encoding struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields ContentType string `protobuf:"bytes,1,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"` Headers *HeadersOrReferences `protobuf:"bytes,2,opt,name=headers,proto3" json:"headers,omitempty"` Style string `protobuf:"bytes,3,opt,name=style,proto3" json:"style,omitempty"` Explode bool `protobuf:"varint,4,opt,name=explode,proto3" json:"explode,omitempty"` AllowReserved bool `protobuf:"varint,5,opt,name=allow_reserved,json=allowReserved,proto3" json:"allow_reserved,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,6,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *Encoding) Reset() { *x = Encoding{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Encoding) String() string { return protoimpl.X.MessageStringOf(x) } func (*Encoding) ProtoMessage() {} func (x *Encoding) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Encoding.ProtoReflect.Descriptor instead. func (*Encoding) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{11} } func (x *Encoding) GetContentType() string { if x != nil { return x.ContentType } return "" } func (x *Encoding) GetHeaders() *HeadersOrReferences { if x != nil { return x.Headers } return nil } func (x *Encoding) GetStyle() string { if x != nil { return x.Style } return "" } func (x *Encoding) GetExplode() bool { if x != nil { return x.Explode } return false } func (x *Encoding) GetAllowReserved() bool { if x != nil { return x.AllowReserved } return false } func (x *Encoding) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } type Encodings struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedEncoding `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *Encodings) Reset() { *x = Encodings{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Encodings) String() string { return protoimpl.X.MessageStringOf(x) } func (*Encodings) ProtoMessage() {} func (x *Encodings) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Encodings.ProtoReflect.Descriptor instead. func (*Encodings) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{12} } func (x *Encodings) GetAdditionalProperties() []*NamedEncoding { if x != nil { return x.AdditionalProperties } return nil } type Example struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Summary string `protobuf:"bytes,1,opt,name=summary,proto3" json:"summary,omitempty"` Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` Value *Any `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` ExternalValue string `protobuf:"bytes,4,opt,name=external_value,json=externalValue,proto3" json:"external_value,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,5,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *Example) Reset() { *x = Example{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Example) String() string { return protoimpl.X.MessageStringOf(x) } func (*Example) ProtoMessage() {} func (x *Example) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Example.ProtoReflect.Descriptor instead. func (*Example) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{13} } func (x *Example) GetSummary() string { if x != nil { return x.Summary } return "" } func (x *Example) GetDescription() string { if x != nil { return x.Description } return "" } func (x *Example) GetValue() *Any { if x != nil { return x.Value } return nil } func (x *Example) GetExternalValue() string { if x != nil { return x.ExternalValue } return "" } func (x *Example) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } type ExampleOrReference struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Types that are assignable to Oneof: // *ExampleOrReference_Example // *ExampleOrReference_Reference Oneof isExampleOrReference_Oneof `protobuf_oneof:"oneof"` } func (x *ExampleOrReference) Reset() { *x = ExampleOrReference{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ExampleOrReference) String() string { return protoimpl.X.MessageStringOf(x) } func (*ExampleOrReference) ProtoMessage() {} func (x *ExampleOrReference) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ExampleOrReference.ProtoReflect.Descriptor instead. func (*ExampleOrReference) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{14} } func (m *ExampleOrReference) GetOneof() isExampleOrReference_Oneof { if m != nil { return m.Oneof } return nil } func (x *ExampleOrReference) GetExample() *Example { if x, ok := x.GetOneof().(*ExampleOrReference_Example); ok { return x.Example } return nil } func (x *ExampleOrReference) GetReference() *Reference { if x, ok := x.GetOneof().(*ExampleOrReference_Reference); ok { return x.Reference } return nil } type isExampleOrReference_Oneof interface { isExampleOrReference_Oneof() } type ExampleOrReference_Example struct { Example *Example `protobuf:"bytes,1,opt,name=example,proto3,oneof"` } type ExampleOrReference_Reference struct { Reference *Reference `protobuf:"bytes,2,opt,name=reference,proto3,oneof"` } func (*ExampleOrReference_Example) isExampleOrReference_Oneof() {} func (*ExampleOrReference_Reference) isExampleOrReference_Oneof() {} type ExamplesOrReferences struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedExampleOrReference `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *ExamplesOrReferences) Reset() { *x = ExamplesOrReferences{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ExamplesOrReferences) String() string { return protoimpl.X.MessageStringOf(x) } func (*ExamplesOrReferences) ProtoMessage() {} func (x *ExamplesOrReferences) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ExamplesOrReferences.ProtoReflect.Descriptor instead. func (*ExamplesOrReferences) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{15} } func (x *ExamplesOrReferences) GetAdditionalProperties() []*NamedExampleOrReference { if x != nil { return x.AdditionalProperties } return nil } type Expression struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedAny `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *Expression) Reset() { *x = Expression{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Expression) String() string { return protoimpl.X.MessageStringOf(x) } func (*Expression) ProtoMessage() {} func (x *Expression) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Expression.ProtoReflect.Descriptor instead. func (*Expression) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{16} } func (x *Expression) GetAdditionalProperties() []*NamedAny { if x != nil { return x.AdditionalProperties } return nil } // Allows referencing an external resource for extended documentation. type ExternalDocs struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Description string `protobuf:"bytes,1,opt,name=description,proto3" json:"description,omitempty"` Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,3,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *ExternalDocs) Reset() { *x = ExternalDocs{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ExternalDocs) String() string { return protoimpl.X.MessageStringOf(x) } func (*ExternalDocs) ProtoMessage() {} func (x *ExternalDocs) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ExternalDocs.ProtoReflect.Descriptor instead. func (*ExternalDocs) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{17} } func (x *ExternalDocs) GetDescription() string { if x != nil { return x.Description } return "" } func (x *ExternalDocs) GetUrl() string { if x != nil { return x.Url } return "" } func (x *ExternalDocs) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } // The Header Object follows the structure of the Parameter Object with the following changes: 1. `name` MUST NOT be specified, it is given in the corresponding `headers` map. 1. `in` MUST NOT be specified, it is implicitly in `header`. 1. All traits that are affected by the location MUST be applicable to a location of `header` (for example, `style`). type Header struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Description string `protobuf:"bytes,1,opt,name=description,proto3" json:"description,omitempty"` Required bool `protobuf:"varint,2,opt,name=required,proto3" json:"required,omitempty"` Deprecated bool `protobuf:"varint,3,opt,name=deprecated,proto3" json:"deprecated,omitempty"` AllowEmptyValue bool `protobuf:"varint,4,opt,name=allow_empty_value,json=allowEmptyValue,proto3" json:"allow_empty_value,omitempty"` Style string `protobuf:"bytes,5,opt,name=style,proto3" json:"style,omitempty"` Explode bool `protobuf:"varint,6,opt,name=explode,proto3" json:"explode,omitempty"` AllowReserved bool `protobuf:"varint,7,opt,name=allow_reserved,json=allowReserved,proto3" json:"allow_reserved,omitempty"` Schema *SchemaOrReference `protobuf:"bytes,8,opt,name=schema,proto3" json:"schema,omitempty"` Example *Any `protobuf:"bytes,9,opt,name=example,proto3" json:"example,omitempty"` Examples *ExamplesOrReferences `protobuf:"bytes,10,opt,name=examples,proto3" json:"examples,omitempty"` Content *MediaTypes `protobuf:"bytes,11,opt,name=content,proto3" json:"content,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,12,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *Header) Reset() { *x = Header{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Header) String() string { return protoimpl.X.MessageStringOf(x) } func (*Header) ProtoMessage() {} func (x *Header) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Header.ProtoReflect.Descriptor instead. func (*Header) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{18} } func (x *Header) GetDescription() string { if x != nil { return x.Description } return "" } func (x *Header) GetRequired() bool { if x != nil { return x.Required } return false } func (x *Header) GetDeprecated() bool { if x != nil { return x.Deprecated } return false } func (x *Header) GetAllowEmptyValue() bool { if x != nil { return x.AllowEmptyValue } return false } func (x *Header) GetStyle() string { if x != nil { return x.Style } return "" } func (x *Header) GetExplode() bool { if x != nil { return x.Explode } return false } func (x *Header) GetAllowReserved() bool { if x != nil { return x.AllowReserved } return false } func (x *Header) GetSchema() *SchemaOrReference { if x != nil { return x.Schema } return nil } func (x *Header) GetExample() *Any { if x != nil { return x.Example } return nil } func (x *Header) GetExamples() *ExamplesOrReferences { if x != nil { return x.Examples } return nil } func (x *Header) GetContent() *MediaTypes { if x != nil { return x.Content } return nil } func (x *Header) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } type HeaderOrReference struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Types that are assignable to Oneof: // *HeaderOrReference_Header // *HeaderOrReference_Reference Oneof isHeaderOrReference_Oneof `protobuf_oneof:"oneof"` } func (x *HeaderOrReference) Reset() { *x = HeaderOrReference{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *HeaderOrReference) String() string { return protoimpl.X.MessageStringOf(x) } func (*HeaderOrReference) ProtoMessage() {} func (x *HeaderOrReference) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HeaderOrReference.ProtoReflect.Descriptor instead. func (*HeaderOrReference) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{19} } func (m *HeaderOrReference) GetOneof() isHeaderOrReference_Oneof { if m != nil { return m.Oneof } return nil } func (x *HeaderOrReference) GetHeader() *Header { if x, ok := x.GetOneof().(*HeaderOrReference_Header); ok { return x.Header } return nil } func (x *HeaderOrReference) GetReference() *Reference { if x, ok := x.GetOneof().(*HeaderOrReference_Reference); ok { return x.Reference } return nil } type isHeaderOrReference_Oneof interface { isHeaderOrReference_Oneof() } type HeaderOrReference_Header struct { Header *Header `protobuf:"bytes,1,opt,name=header,proto3,oneof"` } type HeaderOrReference_Reference struct { Reference *Reference `protobuf:"bytes,2,opt,name=reference,proto3,oneof"` } func (*HeaderOrReference_Header) isHeaderOrReference_Oneof() {} func (*HeaderOrReference_Reference) isHeaderOrReference_Oneof() {} type HeadersOrReferences struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedHeaderOrReference `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *HeadersOrReferences) Reset() { *x = HeadersOrReferences{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *HeadersOrReferences) String() string { return protoimpl.X.MessageStringOf(x) } func (*HeadersOrReferences) ProtoMessage() {} func (x *HeadersOrReferences) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HeadersOrReferences.ProtoReflect.Descriptor instead. func (*HeadersOrReferences) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{20} } func (x *HeadersOrReferences) GetAdditionalProperties() []*NamedHeaderOrReference { if x != nil { return x.AdditionalProperties } return nil } // The object provides metadata about the API. The metadata MAY be used by the clients if needed, and MAY be presented in editing or documentation generation tools for convenience. type Info struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"` Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` TermsOfService string `protobuf:"bytes,3,opt,name=terms_of_service,json=termsOfService,proto3" json:"terms_of_service,omitempty"` Contact *Contact `protobuf:"bytes,4,opt,name=contact,proto3" json:"contact,omitempty"` License *License `protobuf:"bytes,5,opt,name=license,proto3" json:"license,omitempty"` Version string `protobuf:"bytes,6,opt,name=version,proto3" json:"version,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,7,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` Summary string `protobuf:"bytes,8,opt,name=summary,proto3" json:"summary,omitempty"` } func (x *Info) Reset() { *x = Info{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Info) String() string { return protoimpl.X.MessageStringOf(x) } func (*Info) ProtoMessage() {} func (x *Info) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Info.ProtoReflect.Descriptor instead. func (*Info) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{21} } func (x *Info) GetTitle() string { if x != nil { return x.Title } return "" } func (x *Info) GetDescription() string { if x != nil { return x.Description } return "" } func (x *Info) GetTermsOfService() string { if x != nil { return x.TermsOfService } return "" } func (x *Info) GetContact() *Contact { if x != nil { return x.Contact } return nil } func (x *Info) GetLicense() *License { if x != nil { return x.License } return nil } func (x *Info) GetVersion() string { if x != nil { return x.Version } return "" } func (x *Info) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } func (x *Info) GetSummary() string { if x != nil { return x.Summary } return "" } type ItemsItem struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields SchemaOrReference []*SchemaOrReference `protobuf:"bytes,1,rep,name=schema_or_reference,json=schemaOrReference,proto3" json:"schema_or_reference,omitempty"` } func (x *ItemsItem) Reset() { *x = ItemsItem{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ItemsItem) String() string { return protoimpl.X.MessageStringOf(x) } func (*ItemsItem) ProtoMessage() {} func (x *ItemsItem) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ItemsItem.ProtoReflect.Descriptor instead. func (*ItemsItem) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{22} } func (x *ItemsItem) GetSchemaOrReference() []*SchemaOrReference { if x != nil { return x.SchemaOrReference } return nil } // License information for the exposed API. type License struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,3,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *License) Reset() { *x = License{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *License) String() string { return protoimpl.X.MessageStringOf(x) } func (*License) ProtoMessage() {} func (x *License) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use License.ProtoReflect.Descriptor instead. func (*License) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{23} } func (x *License) GetName() string { if x != nil { return x.Name } return "" } func (x *License) GetUrl() string { if x != nil { return x.Url } return "" } func (x *License) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } // The `Link object` represents a possible design-time link for a response. The presence of a link does not guarantee the caller's ability to successfully invoke it, rather it provides a known relationship and traversal mechanism between responses and other operations. Unlike _dynamic_ links (i.e. links provided **in** the response payload), the OAS linking mechanism does not require link information in the runtime response. For computing links, and providing instructions to execute them, a runtime expression is used for accessing values in an operation and using them as parameters while invoking the linked operation. type Link struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields OperationRef string `protobuf:"bytes,1,opt,name=operation_ref,json=operationRef,proto3" json:"operation_ref,omitempty"` OperationId string `protobuf:"bytes,2,opt,name=operation_id,json=operationId,proto3" json:"operation_id,omitempty"` Parameters *AnyOrExpression `protobuf:"bytes,3,opt,name=parameters,proto3" json:"parameters,omitempty"` RequestBody *AnyOrExpression `protobuf:"bytes,4,opt,name=request_body,json=requestBody,proto3" json:"request_body,omitempty"` Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty"` Server *Server `protobuf:"bytes,6,opt,name=server,proto3" json:"server,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,7,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *Link) Reset() { *x = Link{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Link) String() string { return protoimpl.X.MessageStringOf(x) } func (*Link) ProtoMessage() {} func (x *Link) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Link.ProtoReflect.Descriptor instead. func (*Link) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{24} } func (x *Link) GetOperationRef() string { if x != nil { return x.OperationRef } return "" } func (x *Link) GetOperationId() string { if x != nil { return x.OperationId } return "" } func (x *Link) GetParameters() *AnyOrExpression { if x != nil { return x.Parameters } return nil } func (x *Link) GetRequestBody() *AnyOrExpression { if x != nil { return x.RequestBody } return nil } func (x *Link) GetDescription() string { if x != nil { return x.Description } return "" } func (x *Link) GetServer() *Server { if x != nil { return x.Server } return nil } func (x *Link) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } type LinkOrReference struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Types that are assignable to Oneof: // *LinkOrReference_Link // *LinkOrReference_Reference Oneof isLinkOrReference_Oneof `protobuf_oneof:"oneof"` } func (x *LinkOrReference) Reset() { *x = LinkOrReference{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *LinkOrReference) String() string { return protoimpl.X.MessageStringOf(x) } func (*LinkOrReference) ProtoMessage() {} func (x *LinkOrReference) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LinkOrReference.ProtoReflect.Descriptor instead. func (*LinkOrReference) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{25} } func (m *LinkOrReference) GetOneof() isLinkOrReference_Oneof { if m != nil { return m.Oneof } return nil } func (x *LinkOrReference) GetLink() *Link { if x, ok := x.GetOneof().(*LinkOrReference_Link); ok { return x.Link } return nil } func (x *LinkOrReference) GetReference() *Reference { if x, ok := x.GetOneof().(*LinkOrReference_Reference); ok { return x.Reference } return nil } type isLinkOrReference_Oneof interface { isLinkOrReference_Oneof() } type LinkOrReference_Link struct { Link *Link `protobuf:"bytes,1,opt,name=link,proto3,oneof"` } type LinkOrReference_Reference struct { Reference *Reference `protobuf:"bytes,2,opt,name=reference,proto3,oneof"` } func (*LinkOrReference_Link) isLinkOrReference_Oneof() {} func (*LinkOrReference_Reference) isLinkOrReference_Oneof() {} type LinksOrReferences struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedLinkOrReference `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *LinksOrReferences) Reset() { *x = LinksOrReferences{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *LinksOrReferences) String() string { return protoimpl.X.MessageStringOf(x) } func (*LinksOrReferences) ProtoMessage() {} func (x *LinksOrReferences) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LinksOrReferences.ProtoReflect.Descriptor instead. func (*LinksOrReferences) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{26} } func (x *LinksOrReferences) GetAdditionalProperties() []*NamedLinkOrReference { if x != nil { return x.AdditionalProperties } return nil } // Each Media Type Object provides schema and examples for the media type identified by its key. type MediaType struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Schema *SchemaOrReference `protobuf:"bytes,1,opt,name=schema,proto3" json:"schema,omitempty"` Example *Any `protobuf:"bytes,2,opt,name=example,proto3" json:"example,omitempty"` Examples *ExamplesOrReferences `protobuf:"bytes,3,opt,name=examples,proto3" json:"examples,omitempty"` Encoding *Encodings `protobuf:"bytes,4,opt,name=encoding,proto3" json:"encoding,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,5,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *MediaType) Reset() { *x = MediaType{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *MediaType) String() string { return protoimpl.X.MessageStringOf(x) } func (*MediaType) ProtoMessage() {} func (x *MediaType) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MediaType.ProtoReflect.Descriptor instead. func (*MediaType) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{27} } func (x *MediaType) GetSchema() *SchemaOrReference { if x != nil { return x.Schema } return nil } func (x *MediaType) GetExample() *Any { if x != nil { return x.Example } return nil } func (x *MediaType) GetExamples() *ExamplesOrReferences { if x != nil { return x.Examples } return nil } func (x *MediaType) GetEncoding() *Encodings { if x != nil { return x.Encoding } return nil } func (x *MediaType) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } type MediaTypes struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedMediaType `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *MediaTypes) Reset() { *x = MediaTypes{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *MediaTypes) String() string { return protoimpl.X.MessageStringOf(x) } func (*MediaTypes) ProtoMessage() {} func (x *MediaTypes) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MediaTypes.ProtoReflect.Descriptor instead. func (*MediaTypes) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{28} } func (x *MediaTypes) GetAdditionalProperties() []*NamedMediaType { if x != nil { return x.AdditionalProperties } return nil } // Automatically-generated message used to represent maps of Any as ordered (name,value) pairs. type NamedAny struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Map key Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Mapped value Value *Any `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *NamedAny) Reset() { *x = NamedAny{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NamedAny) String() string { return protoimpl.X.MessageStringOf(x) } func (*NamedAny) ProtoMessage() {} func (x *NamedAny) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NamedAny.ProtoReflect.Descriptor instead. func (*NamedAny) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{29} } func (x *NamedAny) GetName() string { if x != nil { return x.Name } return "" } func (x *NamedAny) GetValue() *Any { if x != nil { return x.Value } return nil } // Automatically-generated message used to represent maps of CallbackOrReference as ordered (name,value) pairs. type NamedCallbackOrReference struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Map key Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Mapped value Value *CallbackOrReference `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *NamedCallbackOrReference) Reset() { *x = NamedCallbackOrReference{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NamedCallbackOrReference) String() string { return protoimpl.X.MessageStringOf(x) } func (*NamedCallbackOrReference) ProtoMessage() {} func (x *NamedCallbackOrReference) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NamedCallbackOrReference.ProtoReflect.Descriptor instead. func (*NamedCallbackOrReference) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{30} } func (x *NamedCallbackOrReference) GetName() string { if x != nil { return x.Name } return "" } func (x *NamedCallbackOrReference) GetValue() *CallbackOrReference { if x != nil { return x.Value } return nil } // Automatically-generated message used to represent maps of Encoding as ordered (name,value) pairs. type NamedEncoding struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Map key Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Mapped value Value *Encoding `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *NamedEncoding) Reset() { *x = NamedEncoding{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NamedEncoding) String() string { return protoimpl.X.MessageStringOf(x) } func (*NamedEncoding) ProtoMessage() {} func (x *NamedEncoding) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NamedEncoding.ProtoReflect.Descriptor instead. func (*NamedEncoding) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{31} } func (x *NamedEncoding) GetName() string { if x != nil { return x.Name } return "" } func (x *NamedEncoding) GetValue() *Encoding { if x != nil { return x.Value } return nil } // Automatically-generated message used to represent maps of ExampleOrReference as ordered (name,value) pairs. type NamedExampleOrReference struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Map key Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Mapped value Value *ExampleOrReference `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *NamedExampleOrReference) Reset() { *x = NamedExampleOrReference{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NamedExampleOrReference) String() string { return protoimpl.X.MessageStringOf(x) } func (*NamedExampleOrReference) ProtoMessage() {} func (x *NamedExampleOrReference) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NamedExampleOrReference.ProtoReflect.Descriptor instead. func (*NamedExampleOrReference) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{32} } func (x *NamedExampleOrReference) GetName() string { if x != nil { return x.Name } return "" } func (x *NamedExampleOrReference) GetValue() *ExampleOrReference { if x != nil { return x.Value } return nil } // Automatically-generated message used to represent maps of HeaderOrReference as ordered (name,value) pairs. type NamedHeaderOrReference struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Map key Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Mapped value Value *HeaderOrReference `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *NamedHeaderOrReference) Reset() { *x = NamedHeaderOrReference{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NamedHeaderOrReference) String() string { return protoimpl.X.MessageStringOf(x) } func (*NamedHeaderOrReference) ProtoMessage() {} func (x *NamedHeaderOrReference) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NamedHeaderOrReference.ProtoReflect.Descriptor instead. func (*NamedHeaderOrReference) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{33} } func (x *NamedHeaderOrReference) GetName() string { if x != nil { return x.Name } return "" } func (x *NamedHeaderOrReference) GetValue() *HeaderOrReference { if x != nil { return x.Value } return nil } // Automatically-generated message used to represent maps of LinkOrReference as ordered (name,value) pairs. type NamedLinkOrReference struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Map key Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Mapped value Value *LinkOrReference `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *NamedLinkOrReference) Reset() { *x = NamedLinkOrReference{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NamedLinkOrReference) String() string { return protoimpl.X.MessageStringOf(x) } func (*NamedLinkOrReference) ProtoMessage() {} func (x *NamedLinkOrReference) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NamedLinkOrReference.ProtoReflect.Descriptor instead. func (*NamedLinkOrReference) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{34} } func (x *NamedLinkOrReference) GetName() string { if x != nil { return x.Name } return "" } func (x *NamedLinkOrReference) GetValue() *LinkOrReference { if x != nil { return x.Value } return nil } // Automatically-generated message used to represent maps of MediaType as ordered (name,value) pairs. type NamedMediaType struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Map key Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Mapped value Value *MediaType `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *NamedMediaType) Reset() { *x = NamedMediaType{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NamedMediaType) String() string { return protoimpl.X.MessageStringOf(x) } func (*NamedMediaType) ProtoMessage() {} func (x *NamedMediaType) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NamedMediaType.ProtoReflect.Descriptor instead. func (*NamedMediaType) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{35} } func (x *NamedMediaType) GetName() string { if x != nil { return x.Name } return "" } func (x *NamedMediaType) GetValue() *MediaType { if x != nil { return x.Value } return nil } // Automatically-generated message used to represent maps of ParameterOrReference as ordered (name,value) pairs. type NamedParameterOrReference struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Map key Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Mapped value Value *ParameterOrReference `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *NamedParameterOrReference) Reset() { *x = NamedParameterOrReference{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NamedParameterOrReference) String() string { return protoimpl.X.MessageStringOf(x) } func (*NamedParameterOrReference) ProtoMessage() {} func (x *NamedParameterOrReference) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NamedParameterOrReference.ProtoReflect.Descriptor instead. func (*NamedParameterOrReference) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{36} } func (x *NamedParameterOrReference) GetName() string { if x != nil { return x.Name } return "" } func (x *NamedParameterOrReference) GetValue() *ParameterOrReference { if x != nil { return x.Value } return nil } // Automatically-generated message used to represent maps of PathItem as ordered (name,value) pairs. type NamedPathItem struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Map key Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Mapped value Value *PathItem `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *NamedPathItem) Reset() { *x = NamedPathItem{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NamedPathItem) String() string { return protoimpl.X.MessageStringOf(x) } func (*NamedPathItem) ProtoMessage() {} func (x *NamedPathItem) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NamedPathItem.ProtoReflect.Descriptor instead. func (*NamedPathItem) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{37} } func (x *NamedPathItem) GetName() string { if x != nil { return x.Name } return "" } func (x *NamedPathItem) GetValue() *PathItem { if x != nil { return x.Value } return nil } // Automatically-generated message used to represent maps of RequestBodyOrReference as ordered (name,value) pairs. type NamedRequestBodyOrReference struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Map key Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Mapped value Value *RequestBodyOrReference `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *NamedRequestBodyOrReference) Reset() { *x = NamedRequestBodyOrReference{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NamedRequestBodyOrReference) String() string { return protoimpl.X.MessageStringOf(x) } func (*NamedRequestBodyOrReference) ProtoMessage() {} func (x *NamedRequestBodyOrReference) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NamedRequestBodyOrReference.ProtoReflect.Descriptor instead. func (*NamedRequestBodyOrReference) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{38} } func (x *NamedRequestBodyOrReference) GetName() string { if x != nil { return x.Name } return "" } func (x *NamedRequestBodyOrReference) GetValue() *RequestBodyOrReference { if x != nil { return x.Value } return nil } // Automatically-generated message used to represent maps of ResponseOrReference as ordered (name,value) pairs. type NamedResponseOrReference struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Map key Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Mapped value Value *ResponseOrReference `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *NamedResponseOrReference) Reset() { *x = NamedResponseOrReference{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NamedResponseOrReference) String() string { return protoimpl.X.MessageStringOf(x) } func (*NamedResponseOrReference) ProtoMessage() {} func (x *NamedResponseOrReference) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NamedResponseOrReference.ProtoReflect.Descriptor instead. func (*NamedResponseOrReference) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{39} } func (x *NamedResponseOrReference) GetName() string { if x != nil { return x.Name } return "" } func (x *NamedResponseOrReference) GetValue() *ResponseOrReference { if x != nil { return x.Value } return nil } // Automatically-generated message used to represent maps of SchemaOrReference as ordered (name,value) pairs. type NamedSchemaOrReference struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Map key Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Mapped value Value *SchemaOrReference `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *NamedSchemaOrReference) Reset() { *x = NamedSchemaOrReference{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NamedSchemaOrReference) String() string { return protoimpl.X.MessageStringOf(x) } func (*NamedSchemaOrReference) ProtoMessage() {} func (x *NamedSchemaOrReference) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NamedSchemaOrReference.ProtoReflect.Descriptor instead. func (*NamedSchemaOrReference) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{40} } func (x *NamedSchemaOrReference) GetName() string { if x != nil { return x.Name } return "" } func (x *NamedSchemaOrReference) GetValue() *SchemaOrReference { if x != nil { return x.Value } return nil } // Automatically-generated message used to represent maps of SecuritySchemeOrReference as ordered (name,value) pairs. type NamedSecuritySchemeOrReference struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Map key Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Mapped value Value *SecuritySchemeOrReference `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *NamedSecuritySchemeOrReference) Reset() { *x = NamedSecuritySchemeOrReference{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NamedSecuritySchemeOrReference) String() string { return protoimpl.X.MessageStringOf(x) } func (*NamedSecuritySchemeOrReference) ProtoMessage() {} func (x *NamedSecuritySchemeOrReference) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[41] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NamedSecuritySchemeOrReference.ProtoReflect.Descriptor instead. func (*NamedSecuritySchemeOrReference) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{41} } func (x *NamedSecuritySchemeOrReference) GetName() string { if x != nil { return x.Name } return "" } func (x *NamedSecuritySchemeOrReference) GetValue() *SecuritySchemeOrReference { if x != nil { return x.Value } return nil } // Automatically-generated message used to represent maps of ServerVariable as ordered (name,value) pairs. type NamedServerVariable struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Map key Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Mapped value Value *ServerVariable `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *NamedServerVariable) Reset() { *x = NamedServerVariable{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NamedServerVariable) String() string { return protoimpl.X.MessageStringOf(x) } func (*NamedServerVariable) ProtoMessage() {} func (x *NamedServerVariable) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[42] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NamedServerVariable.ProtoReflect.Descriptor instead. func (*NamedServerVariable) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{42} } func (x *NamedServerVariable) GetName() string { if x != nil { return x.Name } return "" } func (x *NamedServerVariable) GetValue() *ServerVariable { if x != nil { return x.Value } return nil } // Automatically-generated message used to represent maps of string as ordered (name,value) pairs. type NamedString struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Map key Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Mapped value Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *NamedString) Reset() { *x = NamedString{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NamedString) String() string { return protoimpl.X.MessageStringOf(x) } func (*NamedString) ProtoMessage() {} func (x *NamedString) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NamedString.ProtoReflect.Descriptor instead. func (*NamedString) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{43} } func (x *NamedString) GetName() string { if x != nil { return x.Name } return "" } func (x *NamedString) GetValue() string { if x != nil { return x.Value } return "" } // Automatically-generated message used to represent maps of StringArray as ordered (name,value) pairs. type NamedStringArray struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Map key Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Mapped value Value *StringArray `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` } func (x *NamedStringArray) Reset() { *x = NamedStringArray{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *NamedStringArray) String() string { return protoimpl.X.MessageStringOf(x) } func (*NamedStringArray) ProtoMessage() {} func (x *NamedStringArray) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NamedStringArray.ProtoReflect.Descriptor instead. func (*NamedStringArray) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{44} } func (x *NamedStringArray) GetName() string { if x != nil { return x.Name } return "" } func (x *NamedStringArray) GetValue() *StringArray { if x != nil { return x.Value } return nil } // Configuration details for a supported OAuth Flow type OauthFlow struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AuthorizationUrl string `protobuf:"bytes,1,opt,name=authorization_url,json=authorizationUrl,proto3" json:"authorization_url,omitempty"` TokenUrl string `protobuf:"bytes,2,opt,name=token_url,json=tokenUrl,proto3" json:"token_url,omitempty"` RefreshUrl string `protobuf:"bytes,3,opt,name=refresh_url,json=refreshUrl,proto3" json:"refresh_url,omitempty"` Scopes *Strings `protobuf:"bytes,4,opt,name=scopes,proto3" json:"scopes,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,5,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *OauthFlow) Reset() { *x = OauthFlow{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *OauthFlow) String() string { return protoimpl.X.MessageStringOf(x) } func (*OauthFlow) ProtoMessage() {} func (x *OauthFlow) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[45] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use OauthFlow.ProtoReflect.Descriptor instead. func (*OauthFlow) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{45} } func (x *OauthFlow) GetAuthorizationUrl() string { if x != nil { return x.AuthorizationUrl } return "" } func (x *OauthFlow) GetTokenUrl() string { if x != nil { return x.TokenUrl } return "" } func (x *OauthFlow) GetRefreshUrl() string { if x != nil { return x.RefreshUrl } return "" } func (x *OauthFlow) GetScopes() *Strings { if x != nil { return x.Scopes } return nil } func (x *OauthFlow) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } // Allows configuration of the supported OAuth Flows. type OauthFlows struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Implicit *OauthFlow `protobuf:"bytes,1,opt,name=implicit,proto3" json:"implicit,omitempty"` Password *OauthFlow `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` ClientCredentials *OauthFlow `protobuf:"bytes,3,opt,name=client_credentials,json=clientCredentials,proto3" json:"client_credentials,omitempty"` AuthorizationCode *OauthFlow `protobuf:"bytes,4,opt,name=authorization_code,json=authorizationCode,proto3" json:"authorization_code,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,5,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *OauthFlows) Reset() { *x = OauthFlows{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *OauthFlows) String() string { return protoimpl.X.MessageStringOf(x) } func (*OauthFlows) ProtoMessage() {} func (x *OauthFlows) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[46] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use OauthFlows.ProtoReflect.Descriptor instead. func (*OauthFlows) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{46} } func (x *OauthFlows) GetImplicit() *OauthFlow { if x != nil { return x.Implicit } return nil } func (x *OauthFlows) GetPassword() *OauthFlow { if x != nil { return x.Password } return nil } func (x *OauthFlows) GetClientCredentials() *OauthFlow { if x != nil { return x.ClientCredentials } return nil } func (x *OauthFlows) GetAuthorizationCode() *OauthFlow { if x != nil { return x.AuthorizationCode } return nil } func (x *OauthFlows) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } type Object struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedAny `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *Object) Reset() { *x = Object{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Object) String() string { return protoimpl.X.MessageStringOf(x) } func (*Object) ProtoMessage() {} func (x *Object) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[47] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Object.ProtoReflect.Descriptor instead. func (*Object) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{47} } func (x *Object) GetAdditionalProperties() []*NamedAny { if x != nil { return x.AdditionalProperties } return nil } // Describes a single API operation on a path. type Operation struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Tags []string `protobuf:"bytes,1,rep,name=tags,proto3" json:"tags,omitempty"` Summary string `protobuf:"bytes,2,opt,name=summary,proto3" json:"summary,omitempty"` Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` ExternalDocs *ExternalDocs `protobuf:"bytes,4,opt,name=external_docs,json=externalDocs,proto3" json:"external_docs,omitempty"` OperationId string `protobuf:"bytes,5,opt,name=operation_id,json=operationId,proto3" json:"operation_id,omitempty"` Parameters []*ParameterOrReference `protobuf:"bytes,6,rep,name=parameters,proto3" json:"parameters,omitempty"` RequestBody *RequestBodyOrReference `protobuf:"bytes,7,opt,name=request_body,json=requestBody,proto3" json:"request_body,omitempty"` Responses *Responses `protobuf:"bytes,8,opt,name=responses,proto3" json:"responses,omitempty"` Callbacks *CallbacksOrReferences `protobuf:"bytes,9,opt,name=callbacks,proto3" json:"callbacks,omitempty"` Deprecated bool `protobuf:"varint,10,opt,name=deprecated,proto3" json:"deprecated,omitempty"` Security []*SecurityRequirement `protobuf:"bytes,11,rep,name=security,proto3" json:"security,omitempty"` Servers []*Server `protobuf:"bytes,12,rep,name=servers,proto3" json:"servers,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,13,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *Operation) Reset() { *x = Operation{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Operation) String() string { return protoimpl.X.MessageStringOf(x) } func (*Operation) ProtoMessage() {} func (x *Operation) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[48] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Operation.ProtoReflect.Descriptor instead. func (*Operation) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{48} } func (x *Operation) GetTags() []string { if x != nil { return x.Tags } return nil } func (x *Operation) GetSummary() string { if x != nil { return x.Summary } return "" } func (x *Operation) GetDescription() string { if x != nil { return x.Description } return "" } func (x *Operation) GetExternalDocs() *ExternalDocs { if x != nil { return x.ExternalDocs } return nil } func (x *Operation) GetOperationId() string { if x != nil { return x.OperationId } return "" } func (x *Operation) GetParameters() []*ParameterOrReference { if x != nil { return x.Parameters } return nil } func (x *Operation) GetRequestBody() *RequestBodyOrReference { if x != nil { return x.RequestBody } return nil } func (x *Operation) GetResponses() *Responses { if x != nil { return x.Responses } return nil } func (x *Operation) GetCallbacks() *CallbacksOrReferences { if x != nil { return x.Callbacks } return nil } func (x *Operation) GetDeprecated() bool { if x != nil { return x.Deprecated } return false } func (x *Operation) GetSecurity() []*SecurityRequirement { if x != nil { return x.Security } return nil } func (x *Operation) GetServers() []*Server { if x != nil { return x.Servers } return nil } func (x *Operation) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } // Describes a single operation parameter. A unique parameter is defined by a combination of a name and location. type Parameter struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` In string `protobuf:"bytes,2,opt,name=in,proto3" json:"in,omitempty"` Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` Required bool `protobuf:"varint,4,opt,name=required,proto3" json:"required,omitempty"` Deprecated bool `protobuf:"varint,5,opt,name=deprecated,proto3" json:"deprecated,omitempty"` AllowEmptyValue bool `protobuf:"varint,6,opt,name=allow_empty_value,json=allowEmptyValue,proto3" json:"allow_empty_value,omitempty"` Style string `protobuf:"bytes,7,opt,name=style,proto3" json:"style,omitempty"` Explode bool `protobuf:"varint,8,opt,name=explode,proto3" json:"explode,omitempty"` AllowReserved bool `protobuf:"varint,9,opt,name=allow_reserved,json=allowReserved,proto3" json:"allow_reserved,omitempty"` Schema *SchemaOrReference `protobuf:"bytes,10,opt,name=schema,proto3" json:"schema,omitempty"` Example *Any `protobuf:"bytes,11,opt,name=example,proto3" json:"example,omitempty"` Examples *ExamplesOrReferences `protobuf:"bytes,12,opt,name=examples,proto3" json:"examples,omitempty"` Content *MediaTypes `protobuf:"bytes,13,opt,name=content,proto3" json:"content,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,14,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *Parameter) Reset() { *x = Parameter{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Parameter) String() string { return protoimpl.X.MessageStringOf(x) } func (*Parameter) ProtoMessage() {} func (x *Parameter) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[49] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Parameter.ProtoReflect.Descriptor instead. func (*Parameter) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{49} } func (x *Parameter) GetName() string { if x != nil { return x.Name } return "" } func (x *Parameter) GetIn() string { if x != nil { return x.In } return "" } func (x *Parameter) GetDescription() string { if x != nil { return x.Description } return "" } func (x *Parameter) GetRequired() bool { if x != nil { return x.Required } return false } func (x *Parameter) GetDeprecated() bool { if x != nil { return x.Deprecated } return false } func (x *Parameter) GetAllowEmptyValue() bool { if x != nil { return x.AllowEmptyValue } return false } func (x *Parameter) GetStyle() string { if x != nil { return x.Style } return "" } func (x *Parameter) GetExplode() bool { if x != nil { return x.Explode } return false } func (x *Parameter) GetAllowReserved() bool { if x != nil { return x.AllowReserved } return false } func (x *Parameter) GetSchema() *SchemaOrReference { if x != nil { return x.Schema } return nil } func (x *Parameter) GetExample() *Any { if x != nil { return x.Example } return nil } func (x *Parameter) GetExamples() *ExamplesOrReferences { if x != nil { return x.Examples } return nil } func (x *Parameter) GetContent() *MediaTypes { if x != nil { return x.Content } return nil } func (x *Parameter) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } type ParameterOrReference struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Types that are assignable to Oneof: // *ParameterOrReference_Parameter // *ParameterOrReference_Reference Oneof isParameterOrReference_Oneof `protobuf_oneof:"oneof"` } func (x *ParameterOrReference) Reset() { *x = ParameterOrReference{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ParameterOrReference) String() string { return protoimpl.X.MessageStringOf(x) } func (*ParameterOrReference) ProtoMessage() {} func (x *ParameterOrReference) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[50] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ParameterOrReference.ProtoReflect.Descriptor instead. func (*ParameterOrReference) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{50} } func (m *ParameterOrReference) GetOneof() isParameterOrReference_Oneof { if m != nil { return m.Oneof } return nil } func (x *ParameterOrReference) GetParameter() *Parameter { if x, ok := x.GetOneof().(*ParameterOrReference_Parameter); ok { return x.Parameter } return nil } func (x *ParameterOrReference) GetReference() *Reference { if x, ok := x.GetOneof().(*ParameterOrReference_Reference); ok { return x.Reference } return nil } type isParameterOrReference_Oneof interface { isParameterOrReference_Oneof() } type ParameterOrReference_Parameter struct { Parameter *Parameter `protobuf:"bytes,1,opt,name=parameter,proto3,oneof"` } type ParameterOrReference_Reference struct { Reference *Reference `protobuf:"bytes,2,opt,name=reference,proto3,oneof"` } func (*ParameterOrReference_Parameter) isParameterOrReference_Oneof() {} func (*ParameterOrReference_Reference) isParameterOrReference_Oneof() {} type ParametersOrReferences struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedParameterOrReference `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *ParametersOrReferences) Reset() { *x = ParametersOrReferences{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ParametersOrReferences) String() string { return protoimpl.X.MessageStringOf(x) } func (*ParametersOrReferences) ProtoMessage() {} func (x *ParametersOrReferences) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[51] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ParametersOrReferences.ProtoReflect.Descriptor instead. func (*ParametersOrReferences) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{51} } func (x *ParametersOrReferences) GetAdditionalProperties() []*NamedParameterOrReference { if x != nil { return x.AdditionalProperties } return nil } // Describes the operations available on a single path. A Path Item MAY be empty, due to ACL constraints. The path itself is still exposed to the documentation viewer but they will not know which operations and parameters are available. type PathItem struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields XRef string `protobuf:"bytes,1,opt,name=_ref,json=Ref,proto3" json:"_ref,omitempty"` Summary string `protobuf:"bytes,2,opt,name=summary,proto3" json:"summary,omitempty"` Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` Get *Operation `protobuf:"bytes,4,opt,name=get,proto3" json:"get,omitempty"` Put *Operation `protobuf:"bytes,5,opt,name=put,proto3" json:"put,omitempty"` Post *Operation `protobuf:"bytes,6,opt,name=post,proto3" json:"post,omitempty"` Delete *Operation `protobuf:"bytes,7,opt,name=delete,proto3" json:"delete,omitempty"` Options *Operation `protobuf:"bytes,8,opt,name=options,proto3" json:"options,omitempty"` Head *Operation `protobuf:"bytes,9,opt,name=head,proto3" json:"head,omitempty"` Patch *Operation `protobuf:"bytes,10,opt,name=patch,proto3" json:"patch,omitempty"` Trace *Operation `protobuf:"bytes,11,opt,name=trace,proto3" json:"trace,omitempty"` Servers []*Server `protobuf:"bytes,12,rep,name=servers,proto3" json:"servers,omitempty"` Parameters []*ParameterOrReference `protobuf:"bytes,13,rep,name=parameters,proto3" json:"parameters,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,14,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *PathItem) Reset() { *x = PathItem{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *PathItem) String() string { return protoimpl.X.MessageStringOf(x) } func (*PathItem) ProtoMessage() {} func (x *PathItem) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[52] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PathItem.ProtoReflect.Descriptor instead. func (*PathItem) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{52} } func (x *PathItem) GetXRef() string { if x != nil { return x.XRef } return "" } func (x *PathItem) GetSummary() string { if x != nil { return x.Summary } return "" } func (x *PathItem) GetDescription() string { if x != nil { return x.Description } return "" } func (x *PathItem) GetGet() *Operation { if x != nil { return x.Get } return nil } func (x *PathItem) GetPut() *Operation { if x != nil { return x.Put } return nil } func (x *PathItem) GetPost() *Operation { if x != nil { return x.Post } return nil } func (x *PathItem) GetDelete() *Operation { if x != nil { return x.Delete } return nil } func (x *PathItem) GetOptions() *Operation { if x != nil { return x.Options } return nil } func (x *PathItem) GetHead() *Operation { if x != nil { return x.Head } return nil } func (x *PathItem) GetPatch() *Operation { if x != nil { return x.Patch } return nil } func (x *PathItem) GetTrace() *Operation { if x != nil { return x.Trace } return nil } func (x *PathItem) GetServers() []*Server { if x != nil { return x.Servers } return nil } func (x *PathItem) GetParameters() []*ParameterOrReference { if x != nil { return x.Parameters } return nil } func (x *PathItem) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } // Holds the relative paths to the individual endpoints and their operations. The path is appended to the URL from the `Server Object` in order to construct the full URL. The Paths MAY be empty, due to ACL constraints. type Paths struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Path []*NamedPathItem `protobuf:"bytes,1,rep,name=path,proto3" json:"path,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,2,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *Paths) Reset() { *x = Paths{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Paths) String() string { return protoimpl.X.MessageStringOf(x) } func (*Paths) ProtoMessage() {} func (x *Paths) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[53] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Paths.ProtoReflect.Descriptor instead. func (*Paths) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{53} } func (x *Paths) GetPath() []*NamedPathItem { if x != nil { return x.Path } return nil } func (x *Paths) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } type Properties struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedSchemaOrReference `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *Properties) Reset() { *x = Properties{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Properties) String() string { return protoimpl.X.MessageStringOf(x) } func (*Properties) ProtoMessage() {} func (x *Properties) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[54] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Properties.ProtoReflect.Descriptor instead. func (*Properties) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{54} } func (x *Properties) GetAdditionalProperties() []*NamedSchemaOrReference { if x != nil { return x.AdditionalProperties } return nil } // A simple object to allow referencing other components in the specification, internally and externally. The Reference Object is defined by JSON Reference and follows the same structure, behavior and rules. For this specification, reference resolution is accomplished as defined by the JSON Reference specification and not by the JSON Schema specification. type Reference struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields XRef string `protobuf:"bytes,1,opt,name=_ref,json=Ref,proto3" json:"_ref,omitempty"` Summary string `protobuf:"bytes,2,opt,name=summary,proto3" json:"summary,omitempty"` Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` } func (x *Reference) Reset() { *x = Reference{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Reference) String() string { return protoimpl.X.MessageStringOf(x) } func (*Reference) ProtoMessage() {} func (x *Reference) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[55] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Reference.ProtoReflect.Descriptor instead. func (*Reference) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{55} } func (x *Reference) GetXRef() string { if x != nil { return x.XRef } return "" } func (x *Reference) GetSummary() string { if x != nil { return x.Summary } return "" } func (x *Reference) GetDescription() string { if x != nil { return x.Description } return "" } type RequestBodiesOrReferences struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedRequestBodyOrReference `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *RequestBodiesOrReferences) Reset() { *x = RequestBodiesOrReferences{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *RequestBodiesOrReferences) String() string { return protoimpl.X.MessageStringOf(x) } func (*RequestBodiesOrReferences) ProtoMessage() {} func (x *RequestBodiesOrReferences) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[56] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RequestBodiesOrReferences.ProtoReflect.Descriptor instead. func (*RequestBodiesOrReferences) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{56} } func (x *RequestBodiesOrReferences) GetAdditionalProperties() []*NamedRequestBodyOrReference { if x != nil { return x.AdditionalProperties } return nil } // Describes a single request body. type RequestBody struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Description string `protobuf:"bytes,1,opt,name=description,proto3" json:"description,omitempty"` Content *MediaTypes `protobuf:"bytes,2,opt,name=content,proto3" json:"content,omitempty"` Required bool `protobuf:"varint,3,opt,name=required,proto3" json:"required,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,4,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *RequestBody) Reset() { *x = RequestBody{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *RequestBody) String() string { return protoimpl.X.MessageStringOf(x) } func (*RequestBody) ProtoMessage() {} func (x *RequestBody) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[57] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RequestBody.ProtoReflect.Descriptor instead. func (*RequestBody) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{57} } func (x *RequestBody) GetDescription() string { if x != nil { return x.Description } return "" } func (x *RequestBody) GetContent() *MediaTypes { if x != nil { return x.Content } return nil } func (x *RequestBody) GetRequired() bool { if x != nil { return x.Required } return false } func (x *RequestBody) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } type RequestBodyOrReference struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Types that are assignable to Oneof: // *RequestBodyOrReference_RequestBody // *RequestBodyOrReference_Reference Oneof isRequestBodyOrReference_Oneof `protobuf_oneof:"oneof"` } func (x *RequestBodyOrReference) Reset() { *x = RequestBodyOrReference{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *RequestBodyOrReference) String() string { return protoimpl.X.MessageStringOf(x) } func (*RequestBodyOrReference) ProtoMessage() {} func (x *RequestBodyOrReference) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[58] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RequestBodyOrReference.ProtoReflect.Descriptor instead. func (*RequestBodyOrReference) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{58} } func (m *RequestBodyOrReference) GetOneof() isRequestBodyOrReference_Oneof { if m != nil { return m.Oneof } return nil } func (x *RequestBodyOrReference) GetRequestBody() *RequestBody { if x, ok := x.GetOneof().(*RequestBodyOrReference_RequestBody); ok { return x.RequestBody } return nil } func (x *RequestBodyOrReference) GetReference() *Reference { if x, ok := x.GetOneof().(*RequestBodyOrReference_Reference); ok { return x.Reference } return nil } type isRequestBodyOrReference_Oneof interface { isRequestBodyOrReference_Oneof() } type RequestBodyOrReference_RequestBody struct { RequestBody *RequestBody `protobuf:"bytes,1,opt,name=request_body,json=requestBody,proto3,oneof"` } type RequestBodyOrReference_Reference struct { Reference *Reference `protobuf:"bytes,2,opt,name=reference,proto3,oneof"` } func (*RequestBodyOrReference_RequestBody) isRequestBodyOrReference_Oneof() {} func (*RequestBodyOrReference_Reference) isRequestBodyOrReference_Oneof() {} // Describes a single response from an API Operation, including design-time, static `links` to operations based on the response. type Response struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Description string `protobuf:"bytes,1,opt,name=description,proto3" json:"description,omitempty"` Headers *HeadersOrReferences `protobuf:"bytes,2,opt,name=headers,proto3" json:"headers,omitempty"` Content *MediaTypes `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"` Links *LinksOrReferences `protobuf:"bytes,4,opt,name=links,proto3" json:"links,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,5,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *Response) Reset() { *x = Response{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Response) String() string { return protoimpl.X.MessageStringOf(x) } func (*Response) ProtoMessage() {} func (x *Response) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[59] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Response.ProtoReflect.Descriptor instead. func (*Response) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{59} } func (x *Response) GetDescription() string { if x != nil { return x.Description } return "" } func (x *Response) GetHeaders() *HeadersOrReferences { if x != nil { return x.Headers } return nil } func (x *Response) GetContent() *MediaTypes { if x != nil { return x.Content } return nil } func (x *Response) GetLinks() *LinksOrReferences { if x != nil { return x.Links } return nil } func (x *Response) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } type ResponseOrReference struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Types that are assignable to Oneof: // *ResponseOrReference_Response // *ResponseOrReference_Reference Oneof isResponseOrReference_Oneof `protobuf_oneof:"oneof"` } func (x *ResponseOrReference) Reset() { *x = ResponseOrReference{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ResponseOrReference) String() string { return protoimpl.X.MessageStringOf(x) } func (*ResponseOrReference) ProtoMessage() {} func (x *ResponseOrReference) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[60] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ResponseOrReference.ProtoReflect.Descriptor instead. func (*ResponseOrReference) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{60} } func (m *ResponseOrReference) GetOneof() isResponseOrReference_Oneof { if m != nil { return m.Oneof } return nil } func (x *ResponseOrReference) GetResponse() *Response { if x, ok := x.GetOneof().(*ResponseOrReference_Response); ok { return x.Response } return nil } func (x *ResponseOrReference) GetReference() *Reference { if x, ok := x.GetOneof().(*ResponseOrReference_Reference); ok { return x.Reference } return nil } type isResponseOrReference_Oneof interface { isResponseOrReference_Oneof() } type ResponseOrReference_Response struct { Response *Response `protobuf:"bytes,1,opt,name=response,proto3,oneof"` } type ResponseOrReference_Reference struct { Reference *Reference `protobuf:"bytes,2,opt,name=reference,proto3,oneof"` } func (*ResponseOrReference_Response) isResponseOrReference_Oneof() {} func (*ResponseOrReference_Reference) isResponseOrReference_Oneof() {} // A container for the expected responses of an operation. The container maps a HTTP response code to the expected response. The documentation is not necessarily expected to cover all possible HTTP response codes because they may not be known in advance. However, documentation is expected to cover a successful operation response and any known errors. The `default` MAY be used as a default response object for all HTTP codes that are not covered individually by the specification. The `Responses Object` MUST contain at least one response code, and it SHOULD be the response for a successful operation call. type Responses struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Default *ResponseOrReference `protobuf:"bytes,1,opt,name=default,proto3" json:"default,omitempty"` ResponseOrReference []*NamedResponseOrReference `protobuf:"bytes,2,rep,name=response_or_reference,json=responseOrReference,proto3" json:"response_or_reference,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,3,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *Responses) Reset() { *x = Responses{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Responses) String() string { return protoimpl.X.MessageStringOf(x) } func (*Responses) ProtoMessage() {} func (x *Responses) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[61] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Responses.ProtoReflect.Descriptor instead. func (*Responses) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{61} } func (x *Responses) GetDefault() *ResponseOrReference { if x != nil { return x.Default } return nil } func (x *Responses) GetResponseOrReference() []*NamedResponseOrReference { if x != nil { return x.ResponseOrReference } return nil } func (x *Responses) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } type ResponsesOrReferences struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedResponseOrReference `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *ResponsesOrReferences) Reset() { *x = ResponsesOrReferences{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ResponsesOrReferences) String() string { return protoimpl.X.MessageStringOf(x) } func (*ResponsesOrReferences) ProtoMessage() {} func (x *ResponsesOrReferences) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[62] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ResponsesOrReferences.ProtoReflect.Descriptor instead. func (*ResponsesOrReferences) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{62} } func (x *ResponsesOrReferences) GetAdditionalProperties() []*NamedResponseOrReference { if x != nil { return x.AdditionalProperties } return nil } // The Schema Object allows the definition of input and output data types. These types can be objects, but also primitives and arrays. This object is an extended subset of the JSON Schema Specification Wright Draft 00. For more information about the properties, see JSON Schema Core and JSON Schema Validation. Unless stated otherwise, the property definitions follow the JSON Schema. type Schema struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Nullable bool `protobuf:"varint,1,opt,name=nullable,proto3" json:"nullable,omitempty"` Discriminator *Discriminator `protobuf:"bytes,2,opt,name=discriminator,proto3" json:"discriminator,omitempty"` ReadOnly bool `protobuf:"varint,3,opt,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"` WriteOnly bool `protobuf:"varint,4,opt,name=write_only,json=writeOnly,proto3" json:"write_only,omitempty"` Xml *Xml `protobuf:"bytes,5,opt,name=xml,proto3" json:"xml,omitempty"` ExternalDocs *ExternalDocs `protobuf:"bytes,6,opt,name=external_docs,json=externalDocs,proto3" json:"external_docs,omitempty"` Example *Any `protobuf:"bytes,7,opt,name=example,proto3" json:"example,omitempty"` Deprecated bool `protobuf:"varint,8,opt,name=deprecated,proto3" json:"deprecated,omitempty"` Title string `protobuf:"bytes,9,opt,name=title,proto3" json:"title,omitempty"` MultipleOf float64 `protobuf:"fixed64,10,opt,name=multiple_of,json=multipleOf,proto3" json:"multiple_of,omitempty"` Maximum float64 `protobuf:"fixed64,11,opt,name=maximum,proto3" json:"maximum,omitempty"` ExclusiveMaximum bool `protobuf:"varint,12,opt,name=exclusive_maximum,json=exclusiveMaximum,proto3" json:"exclusive_maximum,omitempty"` Minimum float64 `protobuf:"fixed64,13,opt,name=minimum,proto3" json:"minimum,omitempty"` ExclusiveMinimum bool `protobuf:"varint,14,opt,name=exclusive_minimum,json=exclusiveMinimum,proto3" json:"exclusive_minimum,omitempty"` MaxLength int64 `protobuf:"varint,15,opt,name=max_length,json=maxLength,proto3" json:"max_length,omitempty"` MinLength int64 `protobuf:"varint,16,opt,name=min_length,json=minLength,proto3" json:"min_length,omitempty"` Pattern string `protobuf:"bytes,17,opt,name=pattern,proto3" json:"pattern,omitempty"` MaxItems int64 `protobuf:"varint,18,opt,name=max_items,json=maxItems,proto3" json:"max_items,omitempty"` MinItems int64 `protobuf:"varint,19,opt,name=min_items,json=minItems,proto3" json:"min_items,omitempty"` UniqueItems bool `protobuf:"varint,20,opt,name=unique_items,json=uniqueItems,proto3" json:"unique_items,omitempty"` MaxProperties int64 `protobuf:"varint,21,opt,name=max_properties,json=maxProperties,proto3" json:"max_properties,omitempty"` MinProperties int64 `protobuf:"varint,22,opt,name=min_properties,json=minProperties,proto3" json:"min_properties,omitempty"` Required []string `protobuf:"bytes,23,rep,name=required,proto3" json:"required,omitempty"` Enum []*Any `protobuf:"bytes,24,rep,name=enum,proto3" json:"enum,omitempty"` Type string `protobuf:"bytes,25,opt,name=type,proto3" json:"type,omitempty"` AllOf []*SchemaOrReference `protobuf:"bytes,26,rep,name=all_of,json=allOf,proto3" json:"all_of,omitempty"` OneOf []*SchemaOrReference `protobuf:"bytes,27,rep,name=one_of,json=oneOf,proto3" json:"one_of,omitempty"` AnyOf []*SchemaOrReference `protobuf:"bytes,28,rep,name=any_of,json=anyOf,proto3" json:"any_of,omitempty"` Not *Schema `protobuf:"bytes,29,opt,name=not,proto3" json:"not,omitempty"` Items *ItemsItem `protobuf:"bytes,30,opt,name=items,proto3" json:"items,omitempty"` Properties *Properties `protobuf:"bytes,31,opt,name=properties,proto3" json:"properties,omitempty"` AdditionalProperties *AdditionalPropertiesItem `protobuf:"bytes,32,opt,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` Default *DefaultType `protobuf:"bytes,33,opt,name=default,proto3" json:"default,omitempty"` Description string `protobuf:"bytes,34,opt,name=description,proto3" json:"description,omitempty"` Format string `protobuf:"bytes,35,opt,name=format,proto3" json:"format,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,36,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *Schema) Reset() { *x = Schema{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Schema) String() string { return protoimpl.X.MessageStringOf(x) } func (*Schema) ProtoMessage() {} func (x *Schema) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[63] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Schema.ProtoReflect.Descriptor instead. func (*Schema) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{63} } func (x *Schema) GetNullable() bool { if x != nil { return x.Nullable } return false } func (x *Schema) GetDiscriminator() *Discriminator { if x != nil { return x.Discriminator } return nil } func (x *Schema) GetReadOnly() bool { if x != nil { return x.ReadOnly } return false } func (x *Schema) GetWriteOnly() bool { if x != nil { return x.WriteOnly } return false } func (x *Schema) GetXml() *Xml { if x != nil { return x.Xml } return nil } func (x *Schema) GetExternalDocs() *ExternalDocs { if x != nil { return x.ExternalDocs } return nil } func (x *Schema) GetExample() *Any { if x != nil { return x.Example } return nil } func (x *Schema) GetDeprecated() bool { if x != nil { return x.Deprecated } return false } func (x *Schema) GetTitle() string { if x != nil { return x.Title } return "" } func (x *Schema) GetMultipleOf() float64 { if x != nil { return x.MultipleOf } return 0 } func (x *Schema) GetMaximum() float64 { if x != nil { return x.Maximum } return 0 } func (x *Schema) GetExclusiveMaximum() bool { if x != nil { return x.ExclusiveMaximum } return false } func (x *Schema) GetMinimum() float64 { if x != nil { return x.Minimum } return 0 } func (x *Schema) GetExclusiveMinimum() bool { if x != nil { return x.ExclusiveMinimum } return false } func (x *Schema) GetMaxLength() int64 { if x != nil { return x.MaxLength } return 0 } func (x *Schema) GetMinLength() int64 { if x != nil { return x.MinLength } return 0 } func (x *Schema) GetPattern() string { if x != nil { return x.Pattern } return "" } func (x *Schema) GetMaxItems() int64 { if x != nil { return x.MaxItems } return 0 } func (x *Schema) GetMinItems() int64 { if x != nil { return x.MinItems } return 0 } func (x *Schema) GetUniqueItems() bool { if x != nil { return x.UniqueItems } return false } func (x *Schema) GetMaxProperties() int64 { if x != nil { return x.MaxProperties } return 0 } func (x *Schema) GetMinProperties() int64 { if x != nil { return x.MinProperties } return 0 } func (x *Schema) GetRequired() []string { if x != nil { return x.Required } return nil } func (x *Schema) GetEnum() []*Any { if x != nil { return x.Enum } return nil } func (x *Schema) GetType() string { if x != nil { return x.Type } return "" } func (x *Schema) GetAllOf() []*SchemaOrReference { if x != nil { return x.AllOf } return nil } func (x *Schema) GetOneOf() []*SchemaOrReference { if x != nil { return x.OneOf } return nil } func (x *Schema) GetAnyOf() []*SchemaOrReference { if x != nil { return x.AnyOf } return nil } func (x *Schema) GetNot() *Schema { if x != nil { return x.Not } return nil } func (x *Schema) GetItems() *ItemsItem { if x != nil { return x.Items } return nil } func (x *Schema) GetProperties() *Properties { if x != nil { return x.Properties } return nil } func (x *Schema) GetAdditionalProperties() *AdditionalPropertiesItem { if x != nil { return x.AdditionalProperties } return nil } func (x *Schema) GetDefault() *DefaultType { if x != nil { return x.Default } return nil } func (x *Schema) GetDescription() string { if x != nil { return x.Description } return "" } func (x *Schema) GetFormat() string { if x != nil { return x.Format } return "" } func (x *Schema) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } type SchemaOrReference struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Types that are assignable to Oneof: // *SchemaOrReference_Schema // *SchemaOrReference_Reference Oneof isSchemaOrReference_Oneof `protobuf_oneof:"oneof"` } func (x *SchemaOrReference) Reset() { *x = SchemaOrReference{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *SchemaOrReference) String() string { return protoimpl.X.MessageStringOf(x) } func (*SchemaOrReference) ProtoMessage() {} func (x *SchemaOrReference) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[64] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SchemaOrReference.ProtoReflect.Descriptor instead. func (*SchemaOrReference) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{64} } func (m *SchemaOrReference) GetOneof() isSchemaOrReference_Oneof { if m != nil { return m.Oneof } return nil } func (x *SchemaOrReference) GetSchema() *Schema { if x, ok := x.GetOneof().(*SchemaOrReference_Schema); ok { return x.Schema } return nil } func (x *SchemaOrReference) GetReference() *Reference { if x, ok := x.GetOneof().(*SchemaOrReference_Reference); ok { return x.Reference } return nil } type isSchemaOrReference_Oneof interface { isSchemaOrReference_Oneof() } type SchemaOrReference_Schema struct { Schema *Schema `protobuf:"bytes,1,opt,name=schema,proto3,oneof"` } type SchemaOrReference_Reference struct { Reference *Reference `protobuf:"bytes,2,opt,name=reference,proto3,oneof"` } func (*SchemaOrReference_Schema) isSchemaOrReference_Oneof() {} func (*SchemaOrReference_Reference) isSchemaOrReference_Oneof() {} type SchemasOrReferences struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedSchemaOrReference `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *SchemasOrReferences) Reset() { *x = SchemasOrReferences{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *SchemasOrReferences) String() string { return protoimpl.X.MessageStringOf(x) } func (*SchemasOrReferences) ProtoMessage() {} func (x *SchemasOrReferences) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[65] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SchemasOrReferences.ProtoReflect.Descriptor instead. func (*SchemasOrReferences) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{65} } func (x *SchemasOrReferences) GetAdditionalProperties() []*NamedSchemaOrReference { if x != nil { return x.AdditionalProperties } return nil } // Lists the required security schemes to execute this operation. The name used for each property MUST correspond to a security scheme declared in the Security Schemes under the Components Object. Security Requirement Objects that contain multiple schemes require that all schemes MUST be satisfied for a request to be authorized. This enables support for scenarios where multiple query parameters or HTTP headers are required to convey security information. When a list of Security Requirement Objects is defined on the OpenAPI Object or Operation Object, only one of the Security Requirement Objects in the list needs to be satisfied to authorize the request. type SecurityRequirement struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedStringArray `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *SecurityRequirement) Reset() { *x = SecurityRequirement{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *SecurityRequirement) String() string { return protoimpl.X.MessageStringOf(x) } func (*SecurityRequirement) ProtoMessage() {} func (x *SecurityRequirement) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[66] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SecurityRequirement.ProtoReflect.Descriptor instead. func (*SecurityRequirement) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{66} } func (x *SecurityRequirement) GetAdditionalProperties() []*NamedStringArray { if x != nil { return x.AdditionalProperties } return nil } // Defines a security scheme that can be used by the operations. Supported schemes are HTTP authentication, an API key (either as a header, a cookie parameter or as a query parameter), mutual TLS (use of a client certificate), OAuth2's common flows (implicit, password, application and access code) as defined in RFC6749, and OpenID Connect. Please note that currently (2019) the implicit flow is about to be deprecated OAuth 2.0 Security Best Current Practice. Recommended for most use case is Authorization Code Grant flow with PKCE. type SecurityScheme struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` In string `protobuf:"bytes,4,opt,name=in,proto3" json:"in,omitempty"` Scheme string `protobuf:"bytes,5,opt,name=scheme,proto3" json:"scheme,omitempty"` BearerFormat string `protobuf:"bytes,6,opt,name=bearer_format,json=bearerFormat,proto3" json:"bearer_format,omitempty"` Flows *OauthFlows `protobuf:"bytes,7,opt,name=flows,proto3" json:"flows,omitempty"` OpenIdConnectUrl string `protobuf:"bytes,8,opt,name=open_id_connect_url,json=openIdConnectUrl,proto3" json:"open_id_connect_url,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,9,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *SecurityScheme) Reset() { *x = SecurityScheme{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *SecurityScheme) String() string { return protoimpl.X.MessageStringOf(x) } func (*SecurityScheme) ProtoMessage() {} func (x *SecurityScheme) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[67] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SecurityScheme.ProtoReflect.Descriptor instead. func (*SecurityScheme) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{67} } func (x *SecurityScheme) GetType() string { if x != nil { return x.Type } return "" } func (x *SecurityScheme) GetDescription() string { if x != nil { return x.Description } return "" } func (x *SecurityScheme) GetName() string { if x != nil { return x.Name } return "" } func (x *SecurityScheme) GetIn() string { if x != nil { return x.In } return "" } func (x *SecurityScheme) GetScheme() string { if x != nil { return x.Scheme } return "" } func (x *SecurityScheme) GetBearerFormat() string { if x != nil { return x.BearerFormat } return "" } func (x *SecurityScheme) GetFlows() *OauthFlows { if x != nil { return x.Flows } return nil } func (x *SecurityScheme) GetOpenIdConnectUrl() string { if x != nil { return x.OpenIdConnectUrl } return "" } func (x *SecurityScheme) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } type SecuritySchemeOrReference struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Types that are assignable to Oneof: // *SecuritySchemeOrReference_SecurityScheme // *SecuritySchemeOrReference_Reference Oneof isSecuritySchemeOrReference_Oneof `protobuf_oneof:"oneof"` } func (x *SecuritySchemeOrReference) Reset() { *x = SecuritySchemeOrReference{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *SecuritySchemeOrReference) String() string { return protoimpl.X.MessageStringOf(x) } func (*SecuritySchemeOrReference) ProtoMessage() {} func (x *SecuritySchemeOrReference) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[68] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SecuritySchemeOrReference.ProtoReflect.Descriptor instead. func (*SecuritySchemeOrReference) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{68} } func (m *SecuritySchemeOrReference) GetOneof() isSecuritySchemeOrReference_Oneof { if m != nil { return m.Oneof } return nil } func (x *SecuritySchemeOrReference) GetSecurityScheme() *SecurityScheme { if x, ok := x.GetOneof().(*SecuritySchemeOrReference_SecurityScheme); ok { return x.SecurityScheme } return nil } func (x *SecuritySchemeOrReference) GetReference() *Reference { if x, ok := x.GetOneof().(*SecuritySchemeOrReference_Reference); ok { return x.Reference } return nil } type isSecuritySchemeOrReference_Oneof interface { isSecuritySchemeOrReference_Oneof() } type SecuritySchemeOrReference_SecurityScheme struct { SecurityScheme *SecurityScheme `protobuf:"bytes,1,opt,name=security_scheme,json=securityScheme,proto3,oneof"` } type SecuritySchemeOrReference_Reference struct { Reference *Reference `protobuf:"bytes,2,opt,name=reference,proto3,oneof"` } func (*SecuritySchemeOrReference_SecurityScheme) isSecuritySchemeOrReference_Oneof() {} func (*SecuritySchemeOrReference_Reference) isSecuritySchemeOrReference_Oneof() {} type SecuritySchemesOrReferences struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedSecuritySchemeOrReference `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *SecuritySchemesOrReferences) Reset() { *x = SecuritySchemesOrReferences{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *SecuritySchemesOrReferences) String() string { return protoimpl.X.MessageStringOf(x) } func (*SecuritySchemesOrReferences) ProtoMessage() {} func (x *SecuritySchemesOrReferences) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[69] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SecuritySchemesOrReferences.ProtoReflect.Descriptor instead. func (*SecuritySchemesOrReferences) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{69} } func (x *SecuritySchemesOrReferences) GetAdditionalProperties() []*NamedSecuritySchemeOrReference { if x != nil { return x.AdditionalProperties } return nil } // An object representing a Server. type Server struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` Variables *ServerVariables `protobuf:"bytes,3,opt,name=variables,proto3" json:"variables,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,4,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *Server) Reset() { *x = Server{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Server) String() string { return protoimpl.X.MessageStringOf(x) } func (*Server) ProtoMessage() {} func (x *Server) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[70] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Server.ProtoReflect.Descriptor instead. func (*Server) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{70} } func (x *Server) GetUrl() string { if x != nil { return x.Url } return "" } func (x *Server) GetDescription() string { if x != nil { return x.Description } return "" } func (x *Server) GetVariables() *ServerVariables { if x != nil { return x.Variables } return nil } func (x *Server) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } // An object representing a Server Variable for server URL template substitution. type ServerVariable struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Enum []string `protobuf:"bytes,1,rep,name=enum,proto3" json:"enum,omitempty"` Default string `protobuf:"bytes,2,opt,name=default,proto3" json:"default,omitempty"` Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,4,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *ServerVariable) Reset() { *x = ServerVariable{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ServerVariable) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServerVariable) ProtoMessage() {} func (x *ServerVariable) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[71] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServerVariable.ProtoReflect.Descriptor instead. func (*ServerVariable) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{71} } func (x *ServerVariable) GetEnum() []string { if x != nil { return x.Enum } return nil } func (x *ServerVariable) GetDefault() string { if x != nil { return x.Default } return "" } func (x *ServerVariable) GetDescription() string { if x != nil { return x.Description } return "" } func (x *ServerVariable) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } type ServerVariables struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedServerVariable `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *ServerVariables) Reset() { *x = ServerVariables{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *ServerVariables) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServerVariables) ProtoMessage() {} func (x *ServerVariables) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[72] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServerVariables.ProtoReflect.Descriptor instead. func (*ServerVariables) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{72} } func (x *ServerVariables) GetAdditionalProperties() []*NamedServerVariable { if x != nil { return x.AdditionalProperties } return nil } // Any property starting with x- is valid. type SpecificationExtension struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields // Types that are assignable to Oneof: // *SpecificationExtension_Number // *SpecificationExtension_Boolean // *SpecificationExtension_String_ Oneof isSpecificationExtension_Oneof `protobuf_oneof:"oneof"` } func (x *SpecificationExtension) Reset() { *x = SpecificationExtension{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *SpecificationExtension) String() string { return protoimpl.X.MessageStringOf(x) } func (*SpecificationExtension) ProtoMessage() {} func (x *SpecificationExtension) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[73] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SpecificationExtension.ProtoReflect.Descriptor instead. func (*SpecificationExtension) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{73} } func (m *SpecificationExtension) GetOneof() isSpecificationExtension_Oneof { if m != nil { return m.Oneof } return nil } func (x *SpecificationExtension) GetNumber() float64 { if x, ok := x.GetOneof().(*SpecificationExtension_Number); ok { return x.Number } return 0 } func (x *SpecificationExtension) GetBoolean() bool { if x, ok := x.GetOneof().(*SpecificationExtension_Boolean); ok { return x.Boolean } return false } func (x *SpecificationExtension) GetString_() string { if x, ok := x.GetOneof().(*SpecificationExtension_String_); ok { return x.String_ } return "" } type isSpecificationExtension_Oneof interface { isSpecificationExtension_Oneof() } type SpecificationExtension_Number struct { Number float64 `protobuf:"fixed64,1,opt,name=number,proto3,oneof"` } type SpecificationExtension_Boolean struct { Boolean bool `protobuf:"varint,2,opt,name=boolean,proto3,oneof"` } type SpecificationExtension_String_ struct { String_ string `protobuf:"bytes,3,opt,name=string,proto3,oneof"` } func (*SpecificationExtension_Number) isSpecificationExtension_Oneof() {} func (*SpecificationExtension_Boolean) isSpecificationExtension_Oneof() {} func (*SpecificationExtension_String_) isSpecificationExtension_Oneof() {} type StringArray struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Value []string `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"` } func (x *StringArray) Reset() { *x = StringArray{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *StringArray) String() string { return protoimpl.X.MessageStringOf(x) } func (*StringArray) ProtoMessage() {} func (x *StringArray) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[74] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StringArray.ProtoReflect.Descriptor instead. func (*StringArray) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{74} } func (x *StringArray) GetValue() []string { if x != nil { return x.Value } return nil } type Strings struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields AdditionalProperties []*NamedString `protobuf:"bytes,1,rep,name=additional_properties,json=additionalProperties,proto3" json:"additional_properties,omitempty"` } func (x *Strings) Reset() { *x = Strings{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[75] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Strings) String() string { return protoimpl.X.MessageStringOf(x) } func (*Strings) ProtoMessage() {} func (x *Strings) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[75] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Strings.ProtoReflect.Descriptor instead. func (*Strings) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{75} } func (x *Strings) GetAdditionalProperties() []*NamedString { if x != nil { return x.AdditionalProperties } return nil } // Adds metadata to a single tag that is used by the Operation Object. It is not mandatory to have a Tag Object per tag defined in the Operation Object instances. type Tag struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` ExternalDocs *ExternalDocs `protobuf:"bytes,3,opt,name=external_docs,json=externalDocs,proto3" json:"external_docs,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,4,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *Tag) Reset() { *x = Tag{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[76] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Tag) String() string { return protoimpl.X.MessageStringOf(x) } func (*Tag) ProtoMessage() {} func (x *Tag) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[76] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Tag.ProtoReflect.Descriptor instead. func (*Tag) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{76} } func (x *Tag) GetName() string { if x != nil { return x.Name } return "" } func (x *Tag) GetDescription() string { if x != nil { return x.Description } return "" } func (x *Tag) GetExternalDocs() *ExternalDocs { if x != nil { return x.ExternalDocs } return nil } func (x *Tag) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } // A metadata object that allows for more fine-tuned XML model definitions. When using arrays, XML element names are *not* inferred (for singular/plural forms) and the `name` property SHOULD be used to add that information. See examples for expected behavior. type Xml struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Namespace string `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"` Prefix string `protobuf:"bytes,3,opt,name=prefix,proto3" json:"prefix,omitempty"` Attribute bool `protobuf:"varint,4,opt,name=attribute,proto3" json:"attribute,omitempty"` Wrapped bool `protobuf:"varint,5,opt,name=wrapped,proto3" json:"wrapped,omitempty"` SpecificationExtension []*NamedAny `protobuf:"bytes,6,rep,name=specification_extension,json=specificationExtension,proto3" json:"specification_extension,omitempty"` } func (x *Xml) Reset() { *x = Xml{} if protoimpl.UnsafeEnabled { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[77] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } func (x *Xml) String() string { return protoimpl.X.MessageStringOf(x) } func (*Xml) ProtoMessage() {} func (x *Xml) ProtoReflect() protoreflect.Message { mi := &file_openapiv3_OpenAPIv3_proto_msgTypes[77] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Xml.ProtoReflect.Descriptor instead. func (*Xml) Descriptor() ([]byte, []int) { return file_openapiv3_OpenAPIv3_proto_rawDescGZIP(), []int{77} } func (x *Xml) GetName() string { if x != nil { return x.Name } return "" } func (x *Xml) GetNamespace() string { if x != nil { return x.Namespace } return "" } func (x *Xml) GetPrefix() string { if x != nil { return x.Prefix } return "" } func (x *Xml) GetAttribute() bool { if x != nil { return x.Attribute } return false } func (x *Xml) GetWrapped() bool { if x != nil { return x.Wrapped } return false } func (x *Xml) GetSpecificationExtension() []*NamedAny { if x != nil { return x.SpecificationExtension } return nil } var File_openapiv3_OpenAPIv3_proto protoreflect.FileDescriptor var file_openapiv3_OpenAPIv3_proto_rawDesc = []byte{ 0x0a, 0x19, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2f, 0x4f, 0x70, 0x65, 0x6e, 0x41, 0x50, 0x49, 0x76, 0x33, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x90, 0x01, 0x0a, 0x18, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x4f, 0x0a, 0x13, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x6f, 0x72, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x48, 0x00, 0x52, 0x11, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x07, 0x62, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x07, 0x62, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x42, 0x07, 0x0a, 0x05, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x22, 0x45, 0x0a, 0x03, 0x41, 0x6e, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x79, 0x61, 0x6d, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x79, 0x61, 0x6d, 0x6c, 0x22, 0x79, 0x0a, 0x0f, 0x41, 0x6e, 0x79, 0x4f, 0x72, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x03, 0x61, 0x6e, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x6e, 0x79, 0x48, 0x00, 0x52, 0x03, 0x61, 0x6e, 0x79, 0x12, 0x38, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x07, 0x0a, 0x05, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x22, 0x88, 0x01, 0x0a, 0x08, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x2d, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x89, 0x01, 0x0a, 0x13, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x32, 0x0a, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x08, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x35, 0x0a, 0x09, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x48, 0x00, 0x52, 0x09, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x22, 0x72, 0x0a, 0x15, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x59, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0xac, 0x05, 0x0a, 0x0a, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x39, 0x0a, 0x07, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x07, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x12, 0x3f, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x3c, 0x0a, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x12, 0x4c, 0x0a, 0x0e, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x62, 0x6f, 0x64, 0x69, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, 0x69, 0x65, 0x73, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x0d, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, 0x69, 0x65, 0x73, 0x12, 0x39, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x52, 0x0a, 0x10, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x73, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x0f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4c, 0x69, 0x6e, 0x6b, 0x73, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x12, 0x3f, 0x0a, 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x94, 0x01, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x66, 0x0a, 0x0b, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x07, 0x62, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x07, 0x62, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x12, 0x18, 0x0a, 0x06, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x42, 0x07, 0x0a, 0x05, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x22, 0xb2, 0x01, 0x0a, 0x0d, 0x44, 0x69, 0x73, 0x63, 0x72, 0x69, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2d, 0x0a, 0x07, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x07, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xc9, 0x03, 0x0a, 0x08, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x12, 0x24, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x2c, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x27, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x73, 0x52, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x12, 0x36, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x3b, 0x0a, 0x08, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x23, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x54, 0x61, 0x67, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x3d, 0x0a, 0x0d, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x6f, 0x63, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x73, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x73, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x8e, 0x02, 0x0a, 0x08, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x39, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x64, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x5b, 0x0a, 0x09, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x4e, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0xe2, 0x01, 0x0a, 0x07, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x85, 0x01, 0x0a, 0x12, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x48, 0x00, 0x52, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x35, 0x0a, 0x09, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x48, 0x00, 0x52, 0x09, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x22, 0x70, 0x0a, 0x14, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x58, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0x57, 0x0a, 0x0a, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x49, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0x91, 0x01, 0x0a, 0x0c, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x8a, 0x04, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x64, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x12, 0x35, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x29, 0x0a, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x3c, 0x0a, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x12, 0x30, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x54, 0x79, 0x70, 0x65, 0x73, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x81, 0x01, 0x0a, 0x11, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x35, 0x0a, 0x09, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x48, 0x00, 0x52, 0x09, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x22, 0x6e, 0x0a, 0x13, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x57, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0xc9, 0x02, 0x0a, 0x04, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x4f, 0x66, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2d, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x12, 0x2d, 0x0a, 0x07, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x52, 0x07, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x5a, 0x0a, 0x09, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x4d, 0x0a, 0x13, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x6f, 0x72, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x11, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x22, 0x7e, 0x0a, 0x07, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xe8, 0x02, 0x0a, 0x04, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x23, 0x0a, 0x0d, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x3b, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x6e, 0x79, 0x4f, 0x72, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x3e, 0x0a, 0x0c, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x6e, 0x79, 0x4f, 0x72, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x79, 0x0a, 0x0f, 0x4c, 0x69, 0x6e, 0x6b, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4c, 0x69, 0x6e, 0x6b, 0x48, 0x00, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x35, 0x0a, 0x09, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x48, 0x00, 0x52, 0x09, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x22, 0x6a, 0x0a, 0x11, 0x4c, 0x69, 0x6e, 0x6b, 0x73, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x55, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x4c, 0x69, 0x6e, 0x6b, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0xad, 0x02, 0x0a, 0x09, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x54, 0x79, 0x70, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x29, 0x0a, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x3c, 0x0a, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x12, 0x31, 0x0a, 0x08, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x08, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x5d, 0x0a, 0x0a, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x4f, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x54, 0x79, 0x70, 0x65, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0x45, 0x0a, 0x08, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x65, 0x0a, 0x18, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x4f, 0x0a, 0x0d, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x63, 0x0a, 0x17, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x34, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x61, 0x0a, 0x16, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x33, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x5d, 0x0a, 0x14, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x4c, 0x69, 0x6e, 0x6b, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4c, 0x69, 0x6e, 0x6b, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x51, 0x0a, 0x0e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x54, 0x79, 0x70, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x67, 0x0a, 0x19, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x36, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x4f, 0x0a, 0x0d, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x6b, 0x0a, 0x1b, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, 0x79, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, 0x79, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x65, 0x0a, 0x18, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x61, 0x0a, 0x16, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x33, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x71, 0x0a, 0x1e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x5b, 0x0a, 0x13, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x37, 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x55, 0x0a, 0x10, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x41, 0x72, 0x72, 0x61, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x41, 0x72, 0x72, 0x61, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xf2, 0x01, 0x0a, 0x09, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x55, 0x72, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x55, 0x72, 0x6c, 0x12, 0x2b, 0x0a, 0x06, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x06, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xcd, 0x02, 0x0a, 0x0a, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x46, 0x6c, 0x6f, 0x77, 0x73, 0x12, 0x31, 0x0a, 0x08, 0x69, 0x6d, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x08, 0x69, 0x6d, 0x70, 0x6c, 0x69, 0x63, 0x69, 0x74, 0x12, 0x31, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x44, 0x0a, 0x12, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x11, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x44, 0x0a, 0x12, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x11, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x53, 0x0a, 0x06, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x49, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0x96, 0x05, 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, 0x0d, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x6f, 0x63, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x73, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x40, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x45, 0x0a, 0x0c, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, 0x79, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x33, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x52, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x12, 0x3f, 0x0a, 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x09, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x3b, 0x0a, 0x08, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x2c, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xb1, 0x04, 0x0a, 0x09, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x64, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x64, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x12, 0x35, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x29, 0x0a, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x3c, 0x0a, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x12, 0x30, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x54, 0x79, 0x70, 0x65, 0x73, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x8d, 0x01, 0x0a, 0x14, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x35, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x48, 0x00, 0x52, 0x09, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x35, 0x0a, 0x09, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x48, 0x00, 0x52, 0x09, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x22, 0x74, 0x0a, 0x16, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x5a, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0xfa, 0x04, 0x0a, 0x08, 0x50, 0x61, 0x74, 0x68, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x11, 0x0a, 0x04, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x52, 0x65, 0x66, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x27, 0x0a, 0x03, 0x67, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x67, 0x65, 0x74, 0x12, 0x27, 0x0a, 0x03, 0x70, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x70, 0x75, 0x74, 0x12, 0x29, 0x0a, 0x04, 0x70, 0x6f, 0x73, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x70, 0x6f, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x29, 0x0a, 0x04, 0x68, 0x65, 0x61, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x68, 0x65, 0x61, 0x64, 0x12, 0x2b, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x63, 0x68, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x70, 0x61, 0x74, 0x63, 0x68, 0x12, 0x2b, 0x0a, 0x05, 0x74, 0x72, 0x61, 0x63, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x74, 0x72, 0x61, 0x63, 0x65, 0x12, 0x2c, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x40, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x85, 0x01, 0x0a, 0x05, 0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x2d, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x50, 0x61, 0x74, 0x68, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x65, 0x0a, 0x0a, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x57, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0x5a, 0x0a, 0x09, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x11, 0x0a, 0x04, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x52, 0x65, 0x66, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x79, 0x0a, 0x19, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, 0x69, 0x65, 0x73, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x5c, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, 0x79, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0xcc, 0x01, 0x0a, 0x0b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x54, 0x79, 0x70, 0x65, 0x73, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x96, 0x01, 0x0a, 0x16, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, 0x79, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x3c, 0x0a, 0x0c, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, 0x79, 0x48, 0x00, 0x52, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x35, 0x0a, 0x09, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x48, 0x00, 0x52, 0x09, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x22, 0x9d, 0x02, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x30, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x54, 0x79, 0x70, 0x65, 0x73, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x33, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4c, 0x69, 0x6e, 0x6b, 0x73, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x89, 0x01, 0x0a, 0x13, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x32, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x09, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x48, 0x00, 0x52, 0x09, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x22, 0xef, 0x01, 0x0a, 0x09, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x12, 0x39, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x58, 0x0a, 0x15, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x6f, 0x72, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x13, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x72, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x59, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0xaf, 0x0b, 0x0a, 0x06, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x75, 0x6c, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6e, 0x75, 0x6c, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x3f, 0x0a, 0x0d, 0x64, 0x69, 0x73, 0x63, 0x72, 0x69, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x72, 0x69, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x0d, 0x64, 0x69, 0x73, 0x63, 0x72, 0x69, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x77, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x21, 0x0a, 0x03, 0x78, 0x6d, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x58, 0x6d, 0x6c, 0x52, 0x03, 0x78, 0x6d, 0x6c, 0x12, 0x3d, 0x0a, 0x0d, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x6f, 0x63, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x73, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x73, 0x12, 0x29, 0x0a, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x5f, 0x6f, 0x66, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x4f, 0x66, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x2b, 0x0a, 0x11, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x2b, 0x0a, 0x11, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6d, 0x61, 0x78, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x69, 0x6e, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x10, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6d, 0x69, 0x6e, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6e, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6d, 0x69, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6d, 0x61, 0x78, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x15, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6d, 0x69, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x16, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6d, 0x69, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x17, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x23, 0x0a, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x18, 0x18, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x19, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x34, 0x0a, 0x06, 0x61, 0x6c, 0x6c, 0x5f, 0x6f, 0x66, 0x18, 0x1a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x05, 0x61, 0x6c, 0x6c, 0x4f, 0x66, 0x12, 0x34, 0x0a, 0x06, 0x6f, 0x6e, 0x65, 0x5f, 0x6f, 0x66, 0x18, 0x1b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x05, 0x6f, 0x6e, 0x65, 0x4f, 0x66, 0x12, 0x34, 0x0a, 0x06, 0x61, 0x6e, 0x79, 0x5f, 0x6f, 0x66, 0x18, 0x1c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x05, 0x61, 0x6e, 0x79, 0x4f, 0x66, 0x12, 0x24, 0x0a, 0x03, 0x6e, 0x6f, 0x74, 0x18, 0x1d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x03, 0x6e, 0x6f, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x36, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x59, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x20, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x41, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x31, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x21, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x22, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x23, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x24, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x81, 0x01, 0x0a, 0x11, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x48, 0x00, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x35, 0x0a, 0x09, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x48, 0x00, 0x52, 0x09, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x22, 0x6e, 0x0a, 0x13, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x73, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x57, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0x68, 0x0a, 0x13, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x51, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x41, 0x72, 0x72, 0x61, 0x79, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0xd3, 0x02, 0x0a, 0x0e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x62, 0x65, 0x61, 0x72, 0x65, 0x72, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x62, 0x65, 0x61, 0x72, 0x65, 0x72, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x2c, 0x0a, 0x05, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x46, 0x6c, 0x6f, 0x77, 0x73, 0x52, 0x05, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x12, 0x2d, 0x0a, 0x13, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x69, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6f, 0x70, 0x65, 0x6e, 0x49, 0x64, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x55, 0x72, 0x6c, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xa2, 0x01, 0x0a, 0x19, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x0f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x48, 0x00, 0x52, 0x0e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x09, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x48, 0x00, 0x52, 0x09, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x22, 0x7e, 0x0a, 0x1b, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x73, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x5f, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x4f, 0x72, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0xc6, 0x01, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x52, 0x09, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xaf, 0x01, 0x0a, 0x0e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x67, 0x0a, 0x0f, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x54, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0x71, 0x0a, 0x16, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x07, 0x62, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x07, 0x62, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x12, 0x18, 0x0a, 0x06, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x42, 0x07, 0x0a, 0x05, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x22, 0x23, 0x0a, 0x0b, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x41, 0x72, 0x72, 0x61, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x57, 0x0a, 0x07, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x4c, 0x0a, 0x15, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x14, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0xc9, 0x01, 0x0a, 0x03, 0x54, 0x61, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, 0x0d, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x6f, 0x63, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x73, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x73, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xd6, 0x01, 0x0a, 0x03, 0x58, 0x6d, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x12, 0x4d, 0x0a, 0x17, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x41, 0x6e, 0x79, 0x52, 0x16, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x3e, 0x0a, 0x0e, 0x6f, 0x72, 0x67, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x33, 0x42, 0x0c, 0x4f, 0x70, 0x65, 0x6e, 0x41, 0x50, 0x49, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x16, 0x2e, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x3b, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x33, 0xa2, 0x02, 0x03, 0x4f, 0x41, 0x53, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( file_openapiv3_OpenAPIv3_proto_rawDescOnce sync.Once file_openapiv3_OpenAPIv3_proto_rawDescData = file_openapiv3_OpenAPIv3_proto_rawDesc ) func file_openapiv3_OpenAPIv3_proto_rawDescGZIP() []byte { file_openapiv3_OpenAPIv3_proto_rawDescOnce.Do(func() { file_openapiv3_OpenAPIv3_proto_rawDescData = protoimpl.X.CompressGZIP(file_openapiv3_OpenAPIv3_proto_rawDescData) }) return file_openapiv3_OpenAPIv3_proto_rawDescData } var file_openapiv3_OpenAPIv3_proto_msgTypes = make([]protoimpl.MessageInfo, 78) var file_openapiv3_OpenAPIv3_proto_goTypes = []interface{}{ (*AdditionalPropertiesItem)(nil), // 0: openapi.v3.AdditionalPropertiesItem (*Any)(nil), // 1: openapi.v3.Any (*AnyOrExpression)(nil), // 2: openapi.v3.AnyOrExpression (*Callback)(nil), // 3: openapi.v3.Callback (*CallbackOrReference)(nil), // 4: openapi.v3.CallbackOrReference (*CallbacksOrReferences)(nil), // 5: openapi.v3.CallbacksOrReferences (*Components)(nil), // 6: openapi.v3.Components (*Contact)(nil), // 7: openapi.v3.Contact (*DefaultType)(nil), // 8: openapi.v3.DefaultType (*Discriminator)(nil), // 9: openapi.v3.Discriminator (*Document)(nil), // 10: openapi.v3.Document (*Encoding)(nil), // 11: openapi.v3.Encoding (*Encodings)(nil), // 12: openapi.v3.Encodings (*Example)(nil), // 13: openapi.v3.Example (*ExampleOrReference)(nil), // 14: openapi.v3.ExampleOrReference (*ExamplesOrReferences)(nil), // 15: openapi.v3.ExamplesOrReferences (*Expression)(nil), // 16: openapi.v3.Expression (*ExternalDocs)(nil), // 17: openapi.v3.ExternalDocs (*Header)(nil), // 18: openapi.v3.Header (*HeaderOrReference)(nil), // 19: openapi.v3.HeaderOrReference (*HeadersOrReferences)(nil), // 20: openapi.v3.HeadersOrReferences (*Info)(nil), // 21: openapi.v3.Info (*ItemsItem)(nil), // 22: openapi.v3.ItemsItem (*License)(nil), // 23: openapi.v3.License (*Link)(nil), // 24: openapi.v3.Link (*LinkOrReference)(nil), // 25: openapi.v3.LinkOrReference (*LinksOrReferences)(nil), // 26: openapi.v3.LinksOrReferences (*MediaType)(nil), // 27: openapi.v3.MediaType (*MediaTypes)(nil), // 28: openapi.v3.MediaTypes (*NamedAny)(nil), // 29: openapi.v3.NamedAny (*NamedCallbackOrReference)(nil), // 30: openapi.v3.NamedCallbackOrReference (*NamedEncoding)(nil), // 31: openapi.v3.NamedEncoding (*NamedExampleOrReference)(nil), // 32: openapi.v3.NamedExampleOrReference (*NamedHeaderOrReference)(nil), // 33: openapi.v3.NamedHeaderOrReference (*NamedLinkOrReference)(nil), // 34: openapi.v3.NamedLinkOrReference (*NamedMediaType)(nil), // 35: openapi.v3.NamedMediaType (*NamedParameterOrReference)(nil), // 36: openapi.v3.NamedParameterOrReference (*NamedPathItem)(nil), // 37: openapi.v3.NamedPathItem (*NamedRequestBodyOrReference)(nil), // 38: openapi.v3.NamedRequestBodyOrReference (*NamedResponseOrReference)(nil), // 39: openapi.v3.NamedResponseOrReference (*NamedSchemaOrReference)(nil), // 40: openapi.v3.NamedSchemaOrReference (*NamedSecuritySchemeOrReference)(nil), // 41: openapi.v3.NamedSecuritySchemeOrReference (*NamedServerVariable)(nil), // 42: openapi.v3.NamedServerVariable (*NamedString)(nil), // 43: openapi.v3.NamedString (*NamedStringArray)(nil), // 44: openapi.v3.NamedStringArray (*OauthFlow)(nil), // 45: openapi.v3.OauthFlow (*OauthFlows)(nil), // 46: openapi.v3.OauthFlows (*Object)(nil), // 47: openapi.v3.Object (*Operation)(nil), // 48: openapi.v3.Operation (*Parameter)(nil), // 49: openapi.v3.Parameter (*ParameterOrReference)(nil), // 50: openapi.v3.ParameterOrReference (*ParametersOrReferences)(nil), // 51: openapi.v3.ParametersOrReferences (*PathItem)(nil), // 52: openapi.v3.PathItem (*Paths)(nil), // 53: openapi.v3.Paths (*Properties)(nil), // 54: openapi.v3.Properties (*Reference)(nil), // 55: openapi.v3.Reference (*RequestBodiesOrReferences)(nil), // 56: openapi.v3.RequestBodiesOrReferences (*RequestBody)(nil), // 57: openapi.v3.RequestBody (*RequestBodyOrReference)(nil), // 58: openapi.v3.RequestBodyOrReference (*Response)(nil), // 59: openapi.v3.Response (*ResponseOrReference)(nil), // 60: openapi.v3.ResponseOrReference (*Responses)(nil), // 61: openapi.v3.Responses (*ResponsesOrReferences)(nil), // 62: openapi.v3.ResponsesOrReferences (*Schema)(nil), // 63: openapi.v3.Schema (*SchemaOrReference)(nil), // 64: openapi.v3.SchemaOrReference (*SchemasOrReferences)(nil), // 65: openapi.v3.SchemasOrReferences (*SecurityRequirement)(nil), // 66: openapi.v3.SecurityRequirement (*SecurityScheme)(nil), // 67: openapi.v3.SecurityScheme (*SecuritySchemeOrReference)(nil), // 68: openapi.v3.SecuritySchemeOrReference (*SecuritySchemesOrReferences)(nil), // 69: openapi.v3.SecuritySchemesOrReferences (*Server)(nil), // 70: openapi.v3.Server (*ServerVariable)(nil), // 71: openapi.v3.ServerVariable (*ServerVariables)(nil), // 72: openapi.v3.ServerVariables (*SpecificationExtension)(nil), // 73: openapi.v3.SpecificationExtension (*StringArray)(nil), // 74: openapi.v3.StringArray (*Strings)(nil), // 75: openapi.v3.Strings (*Tag)(nil), // 76: openapi.v3.Tag (*Xml)(nil), // 77: openapi.v3.Xml (*anypb.Any)(nil), // 78: google.protobuf.Any } var file_openapiv3_OpenAPIv3_proto_depIdxs = []int32{ 64, // 0: openapi.v3.AdditionalPropertiesItem.schema_or_reference:type_name -> openapi.v3.SchemaOrReference 78, // 1: openapi.v3.Any.value:type_name -> google.protobuf.Any 1, // 2: openapi.v3.AnyOrExpression.any:type_name -> openapi.v3.Any 16, // 3: openapi.v3.AnyOrExpression.expression:type_name -> openapi.v3.Expression 37, // 4: openapi.v3.Callback.path:type_name -> openapi.v3.NamedPathItem 29, // 5: openapi.v3.Callback.specification_extension:type_name -> openapi.v3.NamedAny 3, // 6: openapi.v3.CallbackOrReference.callback:type_name -> openapi.v3.Callback 55, // 7: openapi.v3.CallbackOrReference.reference:type_name -> openapi.v3.Reference 30, // 8: openapi.v3.CallbacksOrReferences.additional_properties:type_name -> openapi.v3.NamedCallbackOrReference 65, // 9: openapi.v3.Components.schemas:type_name -> openapi.v3.SchemasOrReferences 62, // 10: openapi.v3.Components.responses:type_name -> openapi.v3.ResponsesOrReferences 51, // 11: openapi.v3.Components.parameters:type_name -> openapi.v3.ParametersOrReferences 15, // 12: openapi.v3.Components.examples:type_name -> openapi.v3.ExamplesOrReferences 56, // 13: openapi.v3.Components.request_bodies:type_name -> openapi.v3.RequestBodiesOrReferences 20, // 14: openapi.v3.Components.headers:type_name -> openapi.v3.HeadersOrReferences 69, // 15: openapi.v3.Components.security_schemes:type_name -> openapi.v3.SecuritySchemesOrReferences 26, // 16: openapi.v3.Components.links:type_name -> openapi.v3.LinksOrReferences 5, // 17: openapi.v3.Components.callbacks:type_name -> openapi.v3.CallbacksOrReferences 29, // 18: openapi.v3.Components.specification_extension:type_name -> openapi.v3.NamedAny 29, // 19: openapi.v3.Contact.specification_extension:type_name -> openapi.v3.NamedAny 75, // 20: openapi.v3.Discriminator.mapping:type_name -> openapi.v3.Strings 29, // 21: openapi.v3.Discriminator.specification_extension:type_name -> openapi.v3.NamedAny 21, // 22: openapi.v3.Document.info:type_name -> openapi.v3.Info 70, // 23: openapi.v3.Document.servers:type_name -> openapi.v3.Server 53, // 24: openapi.v3.Document.paths:type_name -> openapi.v3.Paths 6, // 25: openapi.v3.Document.components:type_name -> openapi.v3.Components 66, // 26: openapi.v3.Document.security:type_name -> openapi.v3.SecurityRequirement 76, // 27: openapi.v3.Document.tags:type_name -> openapi.v3.Tag 17, // 28: openapi.v3.Document.external_docs:type_name -> openapi.v3.ExternalDocs 29, // 29: openapi.v3.Document.specification_extension:type_name -> openapi.v3.NamedAny 20, // 30: openapi.v3.Encoding.headers:type_name -> openapi.v3.HeadersOrReferences 29, // 31: openapi.v3.Encoding.specification_extension:type_name -> openapi.v3.NamedAny 31, // 32: openapi.v3.Encodings.additional_properties:type_name -> openapi.v3.NamedEncoding 1, // 33: openapi.v3.Example.value:type_name -> openapi.v3.Any 29, // 34: openapi.v3.Example.specification_extension:type_name -> openapi.v3.NamedAny 13, // 35: openapi.v3.ExampleOrReference.example:type_name -> openapi.v3.Example 55, // 36: openapi.v3.ExampleOrReference.reference:type_name -> openapi.v3.Reference 32, // 37: openapi.v3.ExamplesOrReferences.additional_properties:type_name -> openapi.v3.NamedExampleOrReference 29, // 38: openapi.v3.Expression.additional_properties:type_name -> openapi.v3.NamedAny 29, // 39: openapi.v3.ExternalDocs.specification_extension:type_name -> openapi.v3.NamedAny 64, // 40: openapi.v3.Header.schema:type_name -> openapi.v3.SchemaOrReference 1, // 41: openapi.v3.Header.example:type_name -> openapi.v3.Any 15, // 42: openapi.v3.Header.examples:type_name -> openapi.v3.ExamplesOrReferences 28, // 43: openapi.v3.Header.content:type_name -> openapi.v3.MediaTypes 29, // 44: openapi.v3.Header.specification_extension:type_name -> openapi.v3.NamedAny 18, // 45: openapi.v3.HeaderOrReference.header:type_name -> openapi.v3.Header 55, // 46: openapi.v3.HeaderOrReference.reference:type_name -> openapi.v3.Reference 33, // 47: openapi.v3.HeadersOrReferences.additional_properties:type_name -> openapi.v3.NamedHeaderOrReference 7, // 48: openapi.v3.Info.contact:type_name -> openapi.v3.Contact 23, // 49: openapi.v3.Info.license:type_name -> openapi.v3.License 29, // 50: openapi.v3.Info.specification_extension:type_name -> openapi.v3.NamedAny 64, // 51: openapi.v3.ItemsItem.schema_or_reference:type_name -> openapi.v3.SchemaOrReference 29, // 52: openapi.v3.License.specification_extension:type_name -> openapi.v3.NamedAny 2, // 53: openapi.v3.Link.parameters:type_name -> openapi.v3.AnyOrExpression 2, // 54: openapi.v3.Link.request_body:type_name -> openapi.v3.AnyOrExpression 70, // 55: openapi.v3.Link.server:type_name -> openapi.v3.Server 29, // 56: openapi.v3.Link.specification_extension:type_name -> openapi.v3.NamedAny 24, // 57: openapi.v3.LinkOrReference.link:type_name -> openapi.v3.Link 55, // 58: openapi.v3.LinkOrReference.reference:type_name -> openapi.v3.Reference 34, // 59: openapi.v3.LinksOrReferences.additional_properties:type_name -> openapi.v3.NamedLinkOrReference 64, // 60: openapi.v3.MediaType.schema:type_name -> openapi.v3.SchemaOrReference 1, // 61: openapi.v3.MediaType.example:type_name -> openapi.v3.Any 15, // 62: openapi.v3.MediaType.examples:type_name -> openapi.v3.ExamplesOrReferences 12, // 63: openapi.v3.MediaType.encoding:type_name -> openapi.v3.Encodings 29, // 64: openapi.v3.MediaType.specification_extension:type_name -> openapi.v3.NamedAny 35, // 65: openapi.v3.MediaTypes.additional_properties:type_name -> openapi.v3.NamedMediaType 1, // 66: openapi.v3.NamedAny.value:type_name -> openapi.v3.Any 4, // 67: openapi.v3.NamedCallbackOrReference.value:type_name -> openapi.v3.CallbackOrReference 11, // 68: openapi.v3.NamedEncoding.value:type_name -> openapi.v3.Encoding 14, // 69: openapi.v3.NamedExampleOrReference.value:type_name -> openapi.v3.ExampleOrReference 19, // 70: openapi.v3.NamedHeaderOrReference.value:type_name -> openapi.v3.HeaderOrReference 25, // 71: openapi.v3.NamedLinkOrReference.value:type_name -> openapi.v3.LinkOrReference 27, // 72: openapi.v3.NamedMediaType.value:type_name -> openapi.v3.MediaType 50, // 73: openapi.v3.NamedParameterOrReference.value:type_name -> openapi.v3.ParameterOrReference 52, // 74: openapi.v3.NamedPathItem.value:type_name -> openapi.v3.PathItem 58, // 75: openapi.v3.NamedRequestBodyOrReference.value:type_name -> openapi.v3.RequestBodyOrReference 60, // 76: openapi.v3.NamedResponseOrReference.value:type_name -> openapi.v3.ResponseOrReference 64, // 77: openapi.v3.NamedSchemaOrReference.value:type_name -> openapi.v3.SchemaOrReference 68, // 78: openapi.v3.NamedSecuritySchemeOrReference.value:type_name -> openapi.v3.SecuritySchemeOrReference 71, // 79: openapi.v3.NamedServerVariable.value:type_name -> openapi.v3.ServerVariable 74, // 80: openapi.v3.NamedStringArray.value:type_name -> openapi.v3.StringArray 75, // 81: openapi.v3.OauthFlow.scopes:type_name -> openapi.v3.Strings 29, // 82: openapi.v3.OauthFlow.specification_extension:type_name -> openapi.v3.NamedAny 45, // 83: openapi.v3.OauthFlows.implicit:type_name -> openapi.v3.OauthFlow 45, // 84: openapi.v3.OauthFlows.password:type_name -> openapi.v3.OauthFlow 45, // 85: openapi.v3.OauthFlows.client_credentials:type_name -> openapi.v3.OauthFlow 45, // 86: openapi.v3.OauthFlows.authorization_code:type_name -> openapi.v3.OauthFlow 29, // 87: openapi.v3.OauthFlows.specification_extension:type_name -> openapi.v3.NamedAny 29, // 88: openapi.v3.Object.additional_properties:type_name -> openapi.v3.NamedAny 17, // 89: openapi.v3.Operation.external_docs:type_name -> openapi.v3.ExternalDocs 50, // 90: openapi.v3.Operation.parameters:type_name -> openapi.v3.ParameterOrReference 58, // 91: openapi.v3.Operation.request_body:type_name -> openapi.v3.RequestBodyOrReference 61, // 92: openapi.v3.Operation.responses:type_name -> openapi.v3.Responses 5, // 93: openapi.v3.Operation.callbacks:type_name -> openapi.v3.CallbacksOrReferences 66, // 94: openapi.v3.Operation.security:type_name -> openapi.v3.SecurityRequirement 70, // 95: openapi.v3.Operation.servers:type_name -> openapi.v3.Server 29, // 96: openapi.v3.Operation.specification_extension:type_name -> openapi.v3.NamedAny 64, // 97: openapi.v3.Parameter.schema:type_name -> openapi.v3.SchemaOrReference 1, // 98: openapi.v3.Parameter.example:type_name -> openapi.v3.Any 15, // 99: openapi.v3.Parameter.examples:type_name -> openapi.v3.ExamplesOrReferences 28, // 100: openapi.v3.Parameter.content:type_name -> openapi.v3.MediaTypes 29, // 101: openapi.v3.Parameter.specification_extension:type_name -> openapi.v3.NamedAny 49, // 102: openapi.v3.ParameterOrReference.parameter:type_name -> openapi.v3.Parameter 55, // 103: openapi.v3.ParameterOrReference.reference:type_name -> openapi.v3.Reference 36, // 104: openapi.v3.ParametersOrReferences.additional_properties:type_name -> openapi.v3.NamedParameterOrReference 48, // 105: openapi.v3.PathItem.get:type_name -> openapi.v3.Operation 48, // 106: openapi.v3.PathItem.put:type_name -> openapi.v3.Operation 48, // 107: openapi.v3.PathItem.post:type_name -> openapi.v3.Operation 48, // 108: openapi.v3.PathItem.delete:type_name -> openapi.v3.Operation 48, // 109: openapi.v3.PathItem.options:type_name -> openapi.v3.Operation 48, // 110: openapi.v3.PathItem.head:type_name -> openapi.v3.Operation 48, // 111: openapi.v3.PathItem.patch:type_name -> openapi.v3.Operation 48, // 112: openapi.v3.PathItem.trace:type_name -> openapi.v3.Operation 70, // 113: openapi.v3.PathItem.servers:type_name -> openapi.v3.Server 50, // 114: openapi.v3.PathItem.parameters:type_name -> openapi.v3.ParameterOrReference 29, // 115: openapi.v3.PathItem.specification_extension:type_name -> openapi.v3.NamedAny 37, // 116: openapi.v3.Paths.path:type_name -> openapi.v3.NamedPathItem 29, // 117: openapi.v3.Paths.specification_extension:type_name -> openapi.v3.NamedAny 40, // 118: openapi.v3.Properties.additional_properties:type_name -> openapi.v3.NamedSchemaOrReference 38, // 119: openapi.v3.RequestBodiesOrReferences.additional_properties:type_name -> openapi.v3.NamedRequestBodyOrReference 28, // 120: openapi.v3.RequestBody.content:type_name -> openapi.v3.MediaTypes 29, // 121: openapi.v3.RequestBody.specification_extension:type_name -> openapi.v3.NamedAny 57, // 122: openapi.v3.RequestBodyOrReference.request_body:type_name -> openapi.v3.RequestBody 55, // 123: openapi.v3.RequestBodyOrReference.reference:type_name -> openapi.v3.Reference 20, // 124: openapi.v3.Response.headers:type_name -> openapi.v3.HeadersOrReferences 28, // 125: openapi.v3.Response.content:type_name -> openapi.v3.MediaTypes 26, // 126: openapi.v3.Response.links:type_name -> openapi.v3.LinksOrReferences 29, // 127: openapi.v3.Response.specification_extension:type_name -> openapi.v3.NamedAny 59, // 128: openapi.v3.ResponseOrReference.response:type_name -> openapi.v3.Response 55, // 129: openapi.v3.ResponseOrReference.reference:type_name -> openapi.v3.Reference 60, // 130: openapi.v3.Responses.default:type_name -> openapi.v3.ResponseOrReference 39, // 131: openapi.v3.Responses.response_or_reference:type_name -> openapi.v3.NamedResponseOrReference 29, // 132: openapi.v3.Responses.specification_extension:type_name -> openapi.v3.NamedAny 39, // 133: openapi.v3.ResponsesOrReferences.additional_properties:type_name -> openapi.v3.NamedResponseOrReference 9, // 134: openapi.v3.Schema.discriminator:type_name -> openapi.v3.Discriminator 77, // 135: openapi.v3.Schema.xml:type_name -> openapi.v3.Xml 17, // 136: openapi.v3.Schema.external_docs:type_name -> openapi.v3.ExternalDocs 1, // 137: openapi.v3.Schema.example:type_name -> openapi.v3.Any 1, // 138: openapi.v3.Schema.enum:type_name -> openapi.v3.Any 64, // 139: openapi.v3.Schema.all_of:type_name -> openapi.v3.SchemaOrReference 64, // 140: openapi.v3.Schema.one_of:type_name -> openapi.v3.SchemaOrReference 64, // 141: openapi.v3.Schema.any_of:type_name -> openapi.v3.SchemaOrReference 63, // 142: openapi.v3.Schema.not:type_name -> openapi.v3.Schema 22, // 143: openapi.v3.Schema.items:type_name -> openapi.v3.ItemsItem 54, // 144: openapi.v3.Schema.properties:type_name -> openapi.v3.Properties 0, // 145: openapi.v3.Schema.additional_properties:type_name -> openapi.v3.AdditionalPropertiesItem 8, // 146: openapi.v3.Schema.default:type_name -> openapi.v3.DefaultType 29, // 147: openapi.v3.Schema.specification_extension:type_name -> openapi.v3.NamedAny 63, // 148: openapi.v3.SchemaOrReference.schema:type_name -> openapi.v3.Schema 55, // 149: openapi.v3.SchemaOrReference.reference:type_name -> openapi.v3.Reference 40, // 150: openapi.v3.SchemasOrReferences.additional_properties:type_name -> openapi.v3.NamedSchemaOrReference 44, // 151: openapi.v3.SecurityRequirement.additional_properties:type_name -> openapi.v3.NamedStringArray 46, // 152: openapi.v3.SecurityScheme.flows:type_name -> openapi.v3.OauthFlows 29, // 153: openapi.v3.SecurityScheme.specification_extension:type_name -> openapi.v3.NamedAny 67, // 154: openapi.v3.SecuritySchemeOrReference.security_scheme:type_name -> openapi.v3.SecurityScheme 55, // 155: openapi.v3.SecuritySchemeOrReference.reference:type_name -> openapi.v3.Reference 41, // 156: openapi.v3.SecuritySchemesOrReferences.additional_properties:type_name -> openapi.v3.NamedSecuritySchemeOrReference 72, // 157: openapi.v3.Server.variables:type_name -> openapi.v3.ServerVariables 29, // 158: openapi.v3.Server.specification_extension:type_name -> openapi.v3.NamedAny 29, // 159: openapi.v3.ServerVariable.specification_extension:type_name -> openapi.v3.NamedAny 42, // 160: openapi.v3.ServerVariables.additional_properties:type_name -> openapi.v3.NamedServerVariable 43, // 161: openapi.v3.Strings.additional_properties:type_name -> openapi.v3.NamedString 17, // 162: openapi.v3.Tag.external_docs:type_name -> openapi.v3.ExternalDocs 29, // 163: openapi.v3.Tag.specification_extension:type_name -> openapi.v3.NamedAny 29, // 164: openapi.v3.Xml.specification_extension:type_name -> openapi.v3.NamedAny 165, // [165:165] is the sub-list for method output_type 165, // [165:165] is the sub-list for method input_type 165, // [165:165] is the sub-list for extension type_name 165, // [165:165] is the sub-list for extension extendee 0, // [0:165] is the sub-list for field type_name } func init() { file_openapiv3_OpenAPIv3_proto_init() } func file_openapiv3_OpenAPIv3_proto_init() { if File_openapiv3_OpenAPIv3_proto != nil { return } if !protoimpl.UnsafeEnabled { file_openapiv3_OpenAPIv3_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AdditionalPropertiesItem); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Any); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AnyOrExpression); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Callback); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CallbackOrReference); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CallbacksOrReferences); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Components); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Contact); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DefaultType); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Discriminator); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Document); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Encoding); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Encodings); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Example); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ExampleOrReference); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ExamplesOrReferences); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Expression); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ExternalDocs); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Header); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HeaderOrReference); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HeadersOrReferences); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Info); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ItemsItem); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*License); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Link); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*LinkOrReference); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*LinksOrReferences); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MediaType); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MediaTypes); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedAny); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedCallbackOrReference); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedEncoding); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedExampleOrReference); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedHeaderOrReference); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedLinkOrReference); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedMediaType); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedParameterOrReference); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedPathItem); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedRequestBodyOrReference); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedResponseOrReference); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedSchemaOrReference); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedSecuritySchemeOrReference); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedServerVariable); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedString); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedStringArray); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*OauthFlow); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*OauthFlows); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Object); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Operation); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Parameter); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ParameterOrReference); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ParametersOrReferences); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PathItem); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Paths); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Properties); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Reference); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RequestBodiesOrReferences); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RequestBody); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RequestBodyOrReference); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Response); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ResponseOrReference); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Responses); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ResponsesOrReferences); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Schema); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SchemaOrReference); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SchemasOrReferences); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SecurityRequirement); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SecurityScheme); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[68].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SecuritySchemeOrReference); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[69].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SecuritySchemesOrReferences); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[70].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Server); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[71].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ServerVariable); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[72].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ServerVariables); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[73].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SpecificationExtension); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[74].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*StringArray); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[75].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Strings); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[76].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Tag); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } file_openapiv3_OpenAPIv3_proto_msgTypes[77].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Xml); i { case 0: return &v.state case 1: return &v.sizeCache case 2: return &v.unknownFields default: return nil } } } file_openapiv3_OpenAPIv3_proto_msgTypes[0].OneofWrappers = []interface{}{ (*AdditionalPropertiesItem_SchemaOrReference)(nil), (*AdditionalPropertiesItem_Boolean)(nil), } file_openapiv3_OpenAPIv3_proto_msgTypes[2].OneofWrappers = []interface{}{ (*AnyOrExpression_Any)(nil), (*AnyOrExpression_Expression)(nil), } file_openapiv3_OpenAPIv3_proto_msgTypes[4].OneofWrappers = []interface{}{ (*CallbackOrReference_Callback)(nil), (*CallbackOrReference_Reference)(nil), } file_openapiv3_OpenAPIv3_proto_msgTypes[8].OneofWrappers = []interface{}{ (*DefaultType_Number)(nil), (*DefaultType_Boolean)(nil), (*DefaultType_String_)(nil), } file_openapiv3_OpenAPIv3_proto_msgTypes[14].OneofWrappers = []interface{}{ (*ExampleOrReference_Example)(nil), (*ExampleOrReference_Reference)(nil), } file_openapiv3_OpenAPIv3_proto_msgTypes[19].OneofWrappers = []interface{}{ (*HeaderOrReference_Header)(nil), (*HeaderOrReference_Reference)(nil), } file_openapiv3_OpenAPIv3_proto_msgTypes[25].OneofWrappers = []interface{}{ (*LinkOrReference_Link)(nil), (*LinkOrReference_Reference)(nil), } file_openapiv3_OpenAPIv3_proto_msgTypes[50].OneofWrappers = []interface{}{ (*ParameterOrReference_Parameter)(nil), (*ParameterOrReference_Reference)(nil), } file_openapiv3_OpenAPIv3_proto_msgTypes[58].OneofWrappers = []interface{}{ (*RequestBodyOrReference_RequestBody)(nil), (*RequestBodyOrReference_Reference)(nil), } file_openapiv3_OpenAPIv3_proto_msgTypes[60].OneofWrappers = []interface{}{ (*ResponseOrReference_Response)(nil), (*ResponseOrReference_Reference)(nil), } file_openapiv3_OpenAPIv3_proto_msgTypes[64].OneofWrappers = []interface{}{ (*SchemaOrReference_Schema)(nil), (*SchemaOrReference_Reference)(nil), } file_openapiv3_OpenAPIv3_proto_msgTypes[68].OneofWrappers = []interface{}{ (*SecuritySchemeOrReference_SecurityScheme)(nil), (*SecuritySchemeOrReference_Reference)(nil), } file_openapiv3_OpenAPIv3_proto_msgTypes[73].OneofWrappers = []interface{}{ (*SpecificationExtension_Number)(nil), (*SpecificationExtension_Boolean)(nil), (*SpecificationExtension_String_)(nil), } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_openapiv3_OpenAPIv3_proto_rawDesc, NumEnums: 0, NumMessages: 78, NumExtensions: 0, NumServices: 0, }, GoTypes: file_openapiv3_OpenAPIv3_proto_goTypes, DependencyIndexes: file_openapiv3_OpenAPIv3_proto_depIdxs, MessageInfos: file_openapiv3_OpenAPIv3_proto_msgTypes, }.Build() File_openapiv3_OpenAPIv3_proto = out.File file_openapiv3_OpenAPIv3_proto_rawDesc = nil file_openapiv3_OpenAPIv3_proto_goTypes = nil file_openapiv3_OpenAPIv3_proto_depIdxs = nil } ================================================ FILE: vendor/github.com/google/gnostic/openapiv3/OpenAPIv3.proto ================================================ // Copyright 2020 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // THIS FILE IS AUTOMATICALLY GENERATED. syntax = "proto3"; package openapi.v3; import "google/protobuf/any.proto"; // This option lets the proto compiler generate Java code inside the package // name (see below) instead of inside an outer class. It creates a simpler // developer experience by reducing one-level of name nesting and be // consistent with most programming languages that don't support outer classes. option java_multiple_files = true; // The Java outer classname should be the filename in UpperCamelCase. This // class is only used to hold proto descriptor, so developers don't need to // work with it directly. option java_outer_classname = "OpenAPIProto"; // The Java package name must be proto package name with proper prefix. option java_package = "org.openapi_v3"; // A reasonable prefix for the Objective-C symbols generated from the package. // It should at a minimum be 3 characters long, all uppercase, and convention // is to use an abbreviation of the package name. Something short, but // hopefully unique enough to not conflict with things that may come along in // the future. 'GPB' is reserved for the protocol buffer implementation itself. option objc_class_prefix = "OAS"; // The Go package name. option go_package = "./openapiv3;openapi_v3"; message AdditionalPropertiesItem { oneof oneof { SchemaOrReference schema_or_reference = 1; bool boolean = 2; } } message Any { google.protobuf.Any value = 1; string yaml = 2; } message AnyOrExpression { oneof oneof { Any any = 1; Expression expression = 2; } } // A map of possible out-of band callbacks related to the parent operation. Each value in the map is a Path Item Object that describes a set of requests that may be initiated by the API provider and the expected responses. The key value used to identify the callback object is an expression, evaluated at runtime, that identifies a URL to use for the callback operation. message Callback { repeated NamedPathItem path = 1; repeated NamedAny specification_extension = 2; } message CallbackOrReference { oneof oneof { Callback callback = 1; Reference reference = 2; } } message CallbacksOrReferences { repeated NamedCallbackOrReference additional_properties = 1; } // Holds a set of reusable objects for different aspects of the OAS. All objects defined within the components object will have no effect on the API unless they are explicitly referenced from properties outside the components object. message Components { SchemasOrReferences schemas = 1; ResponsesOrReferences responses = 2; ParametersOrReferences parameters = 3; ExamplesOrReferences examples = 4; RequestBodiesOrReferences request_bodies = 5; HeadersOrReferences headers = 6; SecuritySchemesOrReferences security_schemes = 7; LinksOrReferences links = 8; CallbacksOrReferences callbacks = 9; repeated NamedAny specification_extension = 10; } // Contact information for the exposed API. message Contact { string name = 1; string url = 2; string email = 3; repeated NamedAny specification_extension = 4; } message DefaultType { oneof oneof { double number = 1; bool boolean = 2; string string = 3; } } // When request bodies or response payloads may be one of a number of different schemas, a `discriminator` object can be used to aid in serialization, deserialization, and validation. The discriminator is a specific object in a schema which is used to inform the consumer of the specification of an alternative schema based on the value associated with it. When using the discriminator, _inline_ schemas will not be considered. message Discriminator { string property_name = 1; Strings mapping = 2; repeated NamedAny specification_extension = 3; } message Document { string openapi = 1; Info info = 2; repeated Server servers = 3; Paths paths = 4; Components components = 5; repeated SecurityRequirement security = 6; repeated Tag tags = 7; ExternalDocs external_docs = 8; repeated NamedAny specification_extension = 9; } // A single encoding definition applied to a single schema property. message Encoding { string content_type = 1; HeadersOrReferences headers = 2; string style = 3; bool explode = 4; bool allow_reserved = 5; repeated NamedAny specification_extension = 6; } message Encodings { repeated NamedEncoding additional_properties = 1; } message Example { string summary = 1; string description = 2; Any value = 3; string external_value = 4; repeated NamedAny specification_extension = 5; } message ExampleOrReference { oneof oneof { Example example = 1; Reference reference = 2; } } message ExamplesOrReferences { repeated NamedExampleOrReference additional_properties = 1; } message Expression { repeated NamedAny additional_properties = 1; } // Allows referencing an external resource for extended documentation. message ExternalDocs { string description = 1; string url = 2; repeated NamedAny specification_extension = 3; } // The Header Object follows the structure of the Parameter Object with the following changes: 1. `name` MUST NOT be specified, it is given in the corresponding `headers` map. 1. `in` MUST NOT be specified, it is implicitly in `header`. 1. All traits that are affected by the location MUST be applicable to a location of `header` (for example, `style`). message Header { string description = 1; bool required = 2; bool deprecated = 3; bool allow_empty_value = 4; string style = 5; bool explode = 6; bool allow_reserved = 7; SchemaOrReference schema = 8; Any example = 9; ExamplesOrReferences examples = 10; MediaTypes content = 11; repeated NamedAny specification_extension = 12; } message HeaderOrReference { oneof oneof { Header header = 1; Reference reference = 2; } } message HeadersOrReferences { repeated NamedHeaderOrReference additional_properties = 1; } // The object provides metadata about the API. The metadata MAY be used by the clients if needed, and MAY be presented in editing or documentation generation tools for convenience. message Info { string title = 1; string description = 2; string terms_of_service = 3; Contact contact = 4; License license = 5; string version = 6; repeated NamedAny specification_extension = 7; string summary = 8; } message ItemsItem { repeated SchemaOrReference schema_or_reference = 1; } // License information for the exposed API. message License { string name = 1; string url = 2; repeated NamedAny specification_extension = 3; } // The `Link object` represents a possible design-time link for a response. The presence of a link does not guarantee the caller's ability to successfully invoke it, rather it provides a known relationship and traversal mechanism between responses and other operations. Unlike _dynamic_ links (i.e. links provided **in** the response payload), the OAS linking mechanism does not require link information in the runtime response. For computing links, and providing instructions to execute them, a runtime expression is used for accessing values in an operation and using them as parameters while invoking the linked operation. message Link { string operation_ref = 1; string operation_id = 2; AnyOrExpression parameters = 3; AnyOrExpression request_body = 4; string description = 5; Server server = 6; repeated NamedAny specification_extension = 7; } message LinkOrReference { oneof oneof { Link link = 1; Reference reference = 2; } } message LinksOrReferences { repeated NamedLinkOrReference additional_properties = 1; } // Each Media Type Object provides schema and examples for the media type identified by its key. message MediaType { SchemaOrReference schema = 1; Any example = 2; ExamplesOrReferences examples = 3; Encodings encoding = 4; repeated NamedAny specification_extension = 5; } message MediaTypes { repeated NamedMediaType additional_properties = 1; } // Automatically-generated message used to represent maps of Any as ordered (name,value) pairs. message NamedAny { // Map key string name = 1; // Mapped value Any value = 2; } // Automatically-generated message used to represent maps of CallbackOrReference as ordered (name,value) pairs. message NamedCallbackOrReference { // Map key string name = 1; // Mapped value CallbackOrReference value = 2; } // Automatically-generated message used to represent maps of Encoding as ordered (name,value) pairs. message NamedEncoding { // Map key string name = 1; // Mapped value Encoding value = 2; } // Automatically-generated message used to represent maps of ExampleOrReference as ordered (name,value) pairs. message NamedExampleOrReference { // Map key string name = 1; // Mapped value ExampleOrReference value = 2; } // Automatically-generated message used to represent maps of HeaderOrReference as ordered (name,value) pairs. message NamedHeaderOrReference { // Map key string name = 1; // Mapped value HeaderOrReference value = 2; } // Automatically-generated message used to represent maps of LinkOrReference as ordered (name,value) pairs. message NamedLinkOrReference { // Map key string name = 1; // Mapped value LinkOrReference value = 2; } // Automatically-generated message used to represent maps of MediaType as ordered (name,value) pairs. message NamedMediaType { // Map key string name = 1; // Mapped value MediaType value = 2; } // Automatically-generated message used to represent maps of ParameterOrReference as ordered (name,value) pairs. message NamedParameterOrReference { // Map key string name = 1; // Mapped value ParameterOrReference value = 2; } // Automatically-generated message used to represent maps of PathItem as ordered (name,value) pairs. message NamedPathItem { // Map key string name = 1; // Mapped value PathItem value = 2; } // Automatically-generated message used to represent maps of RequestBodyOrReference as ordered (name,value) pairs. message NamedRequestBodyOrReference { // Map key string name = 1; // Mapped value RequestBodyOrReference value = 2; } // Automatically-generated message used to represent maps of ResponseOrReference as ordered (name,value) pairs. message NamedResponseOrReference { // Map key string name = 1; // Mapped value ResponseOrReference value = 2; } // Automatically-generated message used to represent maps of SchemaOrReference as ordered (name,value) pairs. message NamedSchemaOrReference { // Map key string name = 1; // Mapped value SchemaOrReference value = 2; } // Automatically-generated message used to represent maps of SecuritySchemeOrReference as ordered (name,value) pairs. message NamedSecuritySchemeOrReference { // Map key string name = 1; // Mapped value SecuritySchemeOrReference value = 2; } // Automatically-generated message used to represent maps of ServerVariable as ordered (name,value) pairs. message NamedServerVariable { // Map key string name = 1; // Mapped value ServerVariable value = 2; } // Automatically-generated message used to represent maps of string as ordered (name,value) pairs. message NamedString { // Map key string name = 1; // Mapped value string value = 2; } // Automatically-generated message used to represent maps of StringArray as ordered (name,value) pairs. message NamedStringArray { // Map key string name = 1; // Mapped value StringArray value = 2; } // Configuration details for a supported OAuth Flow message OauthFlow { string authorization_url = 1; string token_url = 2; string refresh_url = 3; Strings scopes = 4; repeated NamedAny specification_extension = 5; } // Allows configuration of the supported OAuth Flows. message OauthFlows { OauthFlow implicit = 1; OauthFlow password = 2; OauthFlow client_credentials = 3; OauthFlow authorization_code = 4; repeated NamedAny specification_extension = 5; } message Object { repeated NamedAny additional_properties = 1; } // Describes a single API operation on a path. message Operation { repeated string tags = 1; string summary = 2; string description = 3; ExternalDocs external_docs = 4; string operation_id = 5; repeated ParameterOrReference parameters = 6; RequestBodyOrReference request_body = 7; Responses responses = 8; CallbacksOrReferences callbacks = 9; bool deprecated = 10; repeated SecurityRequirement security = 11; repeated Server servers = 12; repeated NamedAny specification_extension = 13; } // Describes a single operation parameter. A unique parameter is defined by a combination of a name and location. message Parameter { string name = 1; string in = 2; string description = 3; bool required = 4; bool deprecated = 5; bool allow_empty_value = 6; string style = 7; bool explode = 8; bool allow_reserved = 9; SchemaOrReference schema = 10; Any example = 11; ExamplesOrReferences examples = 12; MediaTypes content = 13; repeated NamedAny specification_extension = 14; } message ParameterOrReference { oneof oneof { Parameter parameter = 1; Reference reference = 2; } } message ParametersOrReferences { repeated NamedParameterOrReference additional_properties = 1; } // Describes the operations available on a single path. A Path Item MAY be empty, due to ACL constraints. The path itself is still exposed to the documentation viewer but they will not know which operations and parameters are available. message PathItem { string _ref = 1; string summary = 2; string description = 3; Operation get = 4; Operation put = 5; Operation post = 6; Operation delete = 7; Operation options = 8; Operation head = 9; Operation patch = 10; Operation trace = 11; repeated Server servers = 12; repeated ParameterOrReference parameters = 13; repeated NamedAny specification_extension = 14; } // Holds the relative paths to the individual endpoints and their operations. The path is appended to the URL from the `Server Object` in order to construct the full URL. The Paths MAY be empty, due to ACL constraints. message Paths { repeated NamedPathItem path = 1; repeated NamedAny specification_extension = 2; } message Properties { repeated NamedSchemaOrReference additional_properties = 1; } // A simple object to allow referencing other components in the specification, internally and externally. The Reference Object is defined by JSON Reference and follows the same structure, behavior and rules. For this specification, reference resolution is accomplished as defined by the JSON Reference specification and not by the JSON Schema specification. message Reference { string _ref = 1; string summary = 2; string description = 3; } message RequestBodiesOrReferences { repeated NamedRequestBodyOrReference additional_properties = 1; } // Describes a single request body. message RequestBody { string description = 1; MediaTypes content = 2; bool required = 3; repeated NamedAny specification_extension = 4; } message RequestBodyOrReference { oneof oneof { RequestBody request_body = 1; Reference reference = 2; } } // Describes a single response from an API Operation, including design-time, static `links` to operations based on the response. message Response { string description = 1; HeadersOrReferences headers = 2; MediaTypes content = 3; LinksOrReferences links = 4; repeated NamedAny specification_extension = 5; } message ResponseOrReference { oneof oneof { Response response = 1; Reference reference = 2; } } // A container for the expected responses of an operation. The container maps a HTTP response code to the expected response. The documentation is not necessarily expected to cover all possible HTTP response codes because they may not be known in advance. However, documentation is expected to cover a successful operation response and any known errors. The `default` MAY be used as a default response object for all HTTP codes that are not covered individually by the specification. The `Responses Object` MUST contain at least one response code, and it SHOULD be the response for a successful operation call. message Responses { ResponseOrReference default = 1; repeated NamedResponseOrReference response_or_reference = 2; repeated NamedAny specification_extension = 3; } message ResponsesOrReferences { repeated NamedResponseOrReference additional_properties = 1; } // The Schema Object allows the definition of input and output data types. These types can be objects, but also primitives and arrays. This object is an extended subset of the JSON Schema Specification Wright Draft 00. For more information about the properties, see JSON Schema Core and JSON Schema Validation. Unless stated otherwise, the property definitions follow the JSON Schema. message Schema { bool nullable = 1; Discriminator discriminator = 2; bool read_only = 3; bool write_only = 4; Xml xml = 5; ExternalDocs external_docs = 6; Any example = 7; bool deprecated = 8; string title = 9; double multiple_of = 10; double maximum = 11; bool exclusive_maximum = 12; double minimum = 13; bool exclusive_minimum = 14; int64 max_length = 15; int64 min_length = 16; string pattern = 17; int64 max_items = 18; int64 min_items = 19; bool unique_items = 20; int64 max_properties = 21; int64 min_properties = 22; repeated string required = 23; repeated Any enum = 24; string type = 25; repeated SchemaOrReference all_of = 26; repeated SchemaOrReference one_of = 27; repeated SchemaOrReference any_of = 28; Schema not = 29; ItemsItem items = 30; Properties properties = 31; AdditionalPropertiesItem additional_properties = 32; DefaultType default = 33; string description = 34; string format = 35; repeated NamedAny specification_extension = 36; } message SchemaOrReference { oneof oneof { Schema schema = 1; Reference reference = 2; } } message SchemasOrReferences { repeated NamedSchemaOrReference additional_properties = 1; } // Lists the required security schemes to execute this operation. The name used for each property MUST correspond to a security scheme declared in the Security Schemes under the Components Object. Security Requirement Objects that contain multiple schemes require that all schemes MUST be satisfied for a request to be authorized. This enables support for scenarios where multiple query parameters or HTTP headers are required to convey security information. When a list of Security Requirement Objects is defined on the OpenAPI Object or Operation Object, only one of the Security Requirement Objects in the list needs to be satisfied to authorize the request. message SecurityRequirement { repeated NamedStringArray additional_properties = 1; } // Defines a security scheme that can be used by the operations. Supported schemes are HTTP authentication, an API key (either as a header, a cookie parameter or as a query parameter), mutual TLS (use of a client certificate), OAuth2's common flows (implicit, password, application and access code) as defined in RFC6749, and OpenID Connect. Please note that currently (2019) the implicit flow is about to be deprecated OAuth 2.0 Security Best Current Practice. Recommended for most use case is Authorization Code Grant flow with PKCE. message SecurityScheme { string type = 1; string description = 2; string name = 3; string in = 4; string scheme = 5; string bearer_format = 6; OauthFlows flows = 7; string open_id_connect_url = 8; repeated NamedAny specification_extension = 9; } message SecuritySchemeOrReference { oneof oneof { SecurityScheme security_scheme = 1; Reference reference = 2; } } message SecuritySchemesOrReferences { repeated NamedSecuritySchemeOrReference additional_properties = 1; } // An object representing a Server. message Server { string url = 1; string description = 2; ServerVariables variables = 3; repeated NamedAny specification_extension = 4; } // An object representing a Server Variable for server URL template substitution. message ServerVariable { repeated string enum = 1; string default = 2; string description = 3; repeated NamedAny specification_extension = 4; } message ServerVariables { repeated NamedServerVariable additional_properties = 1; } // Any property starting with x- is valid. message SpecificationExtension { oneof oneof { double number = 1; bool boolean = 2; string string = 3; } } message StringArray { repeated string value = 1; } message Strings { repeated NamedString additional_properties = 1; } // Adds metadata to a single tag that is used by the Operation Object. It is not mandatory to have a Tag Object per tag defined in the Operation Object instances. message Tag { string name = 1; string description = 2; ExternalDocs external_docs = 3; repeated NamedAny specification_extension = 4; } // A metadata object that allows for more fine-tuned XML model definitions. When using arrays, XML element names are *not* inferred (for singular/plural forms) and the `name` property SHOULD be used to add that information. See examples for expected behavior. message Xml { string name = 1; string namespace = 2; string prefix = 3; bool attribute = 4; bool wrapped = 5; repeated NamedAny specification_extension = 6; } ================================================ FILE: vendor/github.com/google/gnostic/openapiv3/README.md ================================================ # OpenAPI v3 Protocol Buffer Models This directory contains a Protocol Buffer-language model and related code for supporting OpenAPI v3. Gnostic applications and plugins can use OpenAPIv3.proto to generate Protocol Buffer support code for their preferred languages. OpenAPIv3.go is used by Gnostic to read JSON and YAML OpenAPI descriptions into the Protocol Buffer-based datastructures generated from OpenAPIv3.proto. OpenAPIv3.proto and OpenAPIv3.go are generated by the Gnostic compiler generator, and OpenAPIv3.pb.go is generated by protoc, the Protocol Buffer compiler, and protoc-gen-go, the Protocol Buffer Go code generation plugin. openapi-3.1.json is a JSON schema for OpenAPI 3.1 that is automatically generated from the OpenAPI 3.1 specification. It is not an official JSON Schema for OpenAPI. The schema-generator directory contains support code which generates openapi-3.1.json from the OpenAPI 3.1 specification document (Markdown). ================================================ FILE: vendor/github.com/google/gnostic/openapiv3/document.go ================================================ // Copyright 2020 Google LLC. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package openapi_v3 import ( "gopkg.in/yaml.v3" "github.com/google/gnostic/compiler" ) // ParseDocument reads an OpenAPI v3 description from a YAML/JSON representation. func ParseDocument(b []byte) (*Document, error) { info, err := compiler.ReadInfoFromBytes("", b) if err != nil { return nil, err } root := info.Content[0] return NewDocument(root, compiler.NewContextWithExtensions("$root", root, nil, nil)) } // YAMLValue produces a serialized YAML representation of the document. func (d *Document) YAMLValue(comment string) ([]byte, error) { rawInfo := d.ToRawInfo() rawInfo = &yaml.Node{ Kind: yaml.DocumentNode, Content: []*yaml.Node{rawInfo}, HeadComment: comment, } return yaml.Marshal(rawInfo) } ================================================ FILE: vendor/github.com/google/gnostic/openapiv3/openapi-3.0.json ================================================ { "title": "A JSON Schema for OpenAPI 3.0.", "id": "http://openapis.org/v3/schema.json#", "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "description": "This is the root document object of the OpenAPI document.", "required": [ "openapi", "info", "paths" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "openapi": { "type": "string" }, "info": { "$ref": "#/definitions/info" }, "servers": { "type": "array", "items": { "$ref": "#/definitions/server" }, "uniqueItems": true }, "paths": { "$ref": "#/definitions/paths" }, "components": { "$ref": "#/definitions/components" }, "security": { "type": "array", "items": { "$ref": "#/definitions/securityRequirement" }, "uniqueItems": true }, "tags": { "type": "array", "items": { "$ref": "#/definitions/tag" }, "uniqueItems": true }, "externalDocs": { "$ref": "#/definitions/externalDocs" } }, "definitions": { "info": { "type": "object", "description": "The object provides metadata about the API. The metadata MAY be used by the clients if needed, and MAY be presented in editing or documentation generation tools for convenience.", "required": [ "title", "version" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "title": { "type": "string" }, "description": { "type": "string" }, "termsOfService": { "type": "string" }, "contact": { "$ref": "#/definitions/contact" }, "license": { "$ref": "#/definitions/license" }, "version": { "type": "string" } } }, "contact": { "type": "object", "description": "Contact information for the exposed API.", "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "name": { "type": "string" }, "url": { "type": "string", "format": "uri" }, "email": { "type": "string", "format": "email" } } }, "license": { "type": "object", "description": "License information for the exposed API.", "required": [ "name" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "name": { "type": "string" }, "url": { "type": "string" } } }, "server": { "type": "object", "description": "An object representing a Server.", "required": [ "url" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "url": { "type": "string" }, "description": { "type": "string" }, "variables": { "$ref": "#/definitions/serverVariables" } } }, "serverVariable": { "type": "object", "description": "An object representing a Server Variable for server URL template substitution.", "required": [ "default" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "enum": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }, "default": { "type": "string" }, "description": { "type": "string" } } }, "components": { "type": "object", "description": "Holds a set of reusable objects for different aspects of the OAS. All objects defined within the components object will have no effect on the API unless they are explicitly referenced from properties outside the components object.", "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "schemas": { "$ref": "#/definitions/schemasOrReferences" }, "responses": { "$ref": "#/definitions/responsesOrReferences" }, "parameters": { "$ref": "#/definitions/parametersOrReferences" }, "examples": { "$ref": "#/definitions/examplesOrReferences" }, "requestBodies": { "$ref": "#/definitions/requestBodiesOrReferences" }, "headers": { "$ref": "#/definitions/headersOrReferences" }, "securitySchemes": { "$ref": "#/definitions/securitySchemesOrReferences" }, "links": { "$ref": "#/definitions/linksOrReferences" }, "callbacks": { "$ref": "#/definitions/callbacksOrReferences" } } }, "paths": { "type": "object", "description": "Holds the relative paths to the individual endpoints and their operations. The path is appended to the URL from the `Server Object` in order to construct the full URL. The Paths MAY be empty, due to ACL constraints.", "additionalProperties": false, "patternProperties": { "^/": { "$ref": "#/definitions/pathItem" }, "^x-": { "$ref": "#/definitions/specificationExtension" } } }, "pathItem": { "type": "object", "description": "Describes the operations available on a single path. A Path Item MAY be empty, due to ACL constraints. The path itself is still exposed to the documentation viewer but they will not know which operations and parameters are available.", "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "$ref": { "type": "string" }, "summary": { "type": "string" }, "description": { "type": "string" }, "get": { "$ref": "#/definitions/operation" }, "put": { "$ref": "#/definitions/operation" }, "post": { "$ref": "#/definitions/operation" }, "delete": { "$ref": "#/definitions/operation" }, "options": { "$ref": "#/definitions/operation" }, "head": { "$ref": "#/definitions/operation" }, "patch": { "$ref": "#/definitions/operation" }, "trace": { "$ref": "#/definitions/operation" }, "servers": { "type": "array", "items": { "$ref": "#/definitions/server" }, "uniqueItems": true }, "parameters": { "type": "array", "items": { "$ref": "#/definitions/parameterOrReference" }, "uniqueItems": true } } }, "operation": { "type": "object", "description": "Describes a single API operation on a path.", "required": [ "responses" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "tags": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }, "summary": { "type": "string" }, "description": { "type": "string" }, "externalDocs": { "$ref": "#/definitions/externalDocs" }, "operationId": { "type": "string" }, "parameters": { "type": "array", "items": { "$ref": "#/definitions/parameterOrReference" }, "uniqueItems": true }, "requestBody": { "$ref": "#/definitions/requestBodyOrReference" }, "responses": { "$ref": "#/definitions/responses" }, "callbacks": { "$ref": "#/definitions/callbacksOrReferences" }, "deprecated": { "type": "boolean" }, "security": { "type": "array", "items": { "$ref": "#/definitions/securityRequirement" }, "uniqueItems": true }, "servers": { "type": "array", "items": { "$ref": "#/definitions/server" }, "uniqueItems": true } } }, "externalDocs": { "type": "object", "description": "Allows referencing an external resource for extended documentation.", "required": [ "url" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "description": { "type": "string" }, "url": { "type": "string" } } }, "parameter": { "type": "object", "description": "Describes a single operation parameter. A unique parameter is defined by a combination of a name and location.", "required": [ "name", "in" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "name": { "type": "string" }, "in": { "type": "string" }, "description": { "type": "string" }, "required": { "type": "boolean" }, "deprecated": { "type": "boolean" }, "allowEmptyValue": { "type": "boolean" }, "style": { "type": "string" }, "explode": { "type": "boolean" }, "allowReserved": { "type": "boolean" }, "schema": { "$ref": "#/definitions/schemaOrReference" }, "example": { "$ref": "#/definitions/any" }, "examples": { "$ref": "#/definitions/examplesOrReferences" }, "content": { "$ref": "#/definitions/mediaTypes" } } }, "requestBody": { "type": "object", "description": "Describes a single request body.", "required": [ "content" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "description": { "type": "string" }, "content": { "$ref": "#/definitions/mediaTypes" }, "required": { "type": "boolean" } } }, "mediaType": { "type": "object", "description": "Each Media Type Object provides schema and examples for the media type identified by its key.", "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "schema": { "$ref": "#/definitions/schemaOrReference" }, "example": { "$ref": "#/definitions/any" }, "examples": { "$ref": "#/definitions/examplesOrReferences" }, "encoding": { "$ref": "#/definitions/encodings" } } }, "encoding": { "type": "object", "description": "A single encoding definition applied to a single schema property.", "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "contentType": { "type": "string" }, "headers": { "$ref": "#/definitions/headersOrReferences" }, "style": { "type": "string" }, "explode": { "type": "boolean" }, "allowReserved": { "type": "boolean" } } }, "responses": { "type": "object", "description": "A container for the expected responses of an operation. The container maps a HTTP response code to the expected response. The documentation is not necessarily expected to cover all possible HTTP response codes because they may not be known in advance. However, documentation is expected to cover a successful operation response and any known errors. The `default` MAY be used as a default response object for all HTTP codes that are not covered individually by the specification. The `Responses Object` MUST contain at least one response code, and it SHOULD be the response for a successful operation call.", "additionalProperties": false, "patternProperties": { "^([0-9X]{3})$": { "$ref": "#/definitions/responseOrReference" }, "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "default": { "$ref": "#/definitions/responseOrReference" } } }, "response": { "type": "object", "description": "Describes a single response from an API Operation, including design-time, static `links` to operations based on the response.", "required": [ "description" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "description": { "type": "string" }, "headers": { "$ref": "#/definitions/headersOrReferences" }, "content": { "$ref": "#/definitions/mediaTypes" }, "links": { "$ref": "#/definitions/linksOrReferences" } } }, "callback": { "type": "object", "description": "A map of possible out-of band callbacks related to the parent operation. Each value in the map is a Path Item Object that describes a set of requests that may be initiated by the API provider and the expected responses. The key value used to identify the callback object is an expression, evaluated at runtime, that identifies a URL to use for the callback operation.", "additionalProperties": false, "patternProperties": { "^": { "$ref": "#/definitions/pathItem" }, "^x-": { "$ref": "#/definitions/specificationExtension" } } }, "example": { "type": "object", "description": "", "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "summary": { "type": "string" }, "description": { "type": "string" }, "value": { "$ref": "#/definitions/any" }, "externalValue": { "type": "string" } } }, "link": { "type": "object", "description": "The `Link object` represents a possible design-time link for a response. The presence of a link does not guarantee the caller's ability to successfully invoke it, rather it provides a known relationship and traversal mechanism between responses and other operations. Unlike _dynamic_ links (i.e. links provided **in** the response payload), the OAS linking mechanism does not require link information in the runtime response. For computing links, and providing instructions to execute them, a runtime expression is used for accessing values in an operation and using them as parameters while invoking the linked operation.", "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "operationRef": { "type": "string" }, "operationId": { "type": "string" }, "parameters": { "$ref": "#/definitions/anysOrExpressions" }, "requestBody": { "$ref": "#/definitions/anyOrExpression" }, "description": { "type": "string" }, "server": { "$ref": "#/definitions/server" } } }, "header": { "type": "object", "description": "The Header Object follows the structure of the Parameter Object with the following changes: 1. `name` MUST NOT be specified, it is given in the corresponding `headers` map. 1. `in` MUST NOT be specified, it is implicitly in `header`. 1. All traits that are affected by the location MUST be applicable to a location of `header` (for example, `style`).", "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "description": { "type": "string" }, "required": { "type": "boolean" }, "deprecated": { "type": "boolean" }, "allowEmptyValue": { "type": "boolean" }, "style": { "type": "string" }, "explode": { "type": "boolean" }, "allowReserved": { "type": "boolean" }, "schema": { "$ref": "#/definitions/schemaOrReference" }, "example": { "$ref": "#/definitions/any" }, "examples": { "$ref": "#/definitions/examplesOrReferences" }, "content": { "$ref": "#/definitions/mediaTypes" } } }, "tag": { "type": "object", "description": "Adds metadata to a single tag that is used by the Operation Object. It is not mandatory to have a Tag Object per tag defined in the Operation Object instances.", "required": [ "name" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "name": { "type": "string" }, "description": { "type": "string" }, "externalDocs": { "$ref": "#/definitions/externalDocs" } } }, "reference": { "type": "object", "description": "A simple object to allow referencing other components in the specification, internally and externally. The Reference Object is defined by JSON Reference and follows the same structure, behavior and rules. For this specification, reference resolution is accomplished as defined by the JSON Reference specification and not by the JSON Schema specification.", "required": [ "$ref" ], "additionalProperties": false, "properties": { "$ref": { "type": "string" }, "summary": { "type": "string" }, "description": { "type": "string" } } }, "schema": { "type": "object", "description": "The Schema Object allows the definition of input and output data types. These types can be objects, but also primitives and arrays. This object is an extended subset of the JSON Schema Specification Wright Draft 00. For more information about the properties, see JSON Schema Core and JSON Schema Validation. Unless stated otherwise, the property definitions follow the JSON Schema.", "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "nullable": { "type": "boolean" }, "discriminator": { "$ref": "#/definitions/discriminator" }, "readOnly": { "type": "boolean" }, "writeOnly": { "type": "boolean" }, "xml": { "$ref": "#/definitions/xml" }, "externalDocs": { "$ref": "#/definitions/externalDocs" }, "example": { "$ref": "#/definitions/any" }, "deprecated": { "type": "boolean" }, "title": { "$ref": "http://json-schema.org/draft-04/schema#/properties/title" }, "multipleOf": { "$ref": "http://json-schema.org/draft-04/schema#/properties/multipleOf" }, "maximum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/maximum" }, "exclusiveMaximum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMaximum" }, "minimum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/minimum" }, "exclusiveMinimum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMinimum" }, "maxLength": { "$ref": "http://json-schema.org/draft-04/schema#/properties/maxLength" }, "minLength": { "$ref": "http://json-schema.org/draft-04/schema#/properties/minLength" }, "pattern": { "$ref": "http://json-schema.org/draft-04/schema#/properties/pattern" }, "maxItems": { "$ref": "http://json-schema.org/draft-04/schema#/properties/maxItems" }, "minItems": { "$ref": "http://json-schema.org/draft-04/schema#/properties/minItems" }, "uniqueItems": { "$ref": "http://json-schema.org/draft-04/schema#/properties/uniqueItems" }, "maxProperties": { "$ref": "http://json-schema.org/draft-04/schema#/properties/maxProperties" }, "minProperties": { "$ref": "http://json-schema.org/draft-04/schema#/properties/minProperties" }, "required": { "$ref": "http://json-schema.org/draft-04/schema#/properties/required" }, "enum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/enum" }, "type": { "type": "string" }, "allOf": { "type": "array", "items": { "$ref": "#/definitions/schemaOrReference" }, "minItems": 1 }, "oneOf": { "type": "array", "items": { "$ref": "#/definitions/schemaOrReference" }, "minItems": 1 }, "anyOf": { "type": "array", "items": { "$ref": "#/definitions/schemaOrReference" }, "minItems": 1 }, "not": { "$ref": "#/definitions/schema" }, "items": { "anyOf": [ { "$ref": "#/definitions/schemaOrReference" }, { "type": "array", "items": { "$ref": "#/definitions/schemaOrReference" }, "minItems": 1 } ] }, "properties": { "type": "object", "additionalProperties": { "$ref": "#/definitions/schemaOrReference" } }, "additionalProperties": { "oneOf": [ { "$ref": "#/definitions/schemaOrReference" }, { "type": "boolean" } ] }, "default": { "$ref": "#/definitions/defaultType" }, "description": { "type": "string" }, "format": { "type": "string" } } }, "discriminator": { "type": "object", "description": "When request bodies or response payloads may be one of a number of different schemas, a `discriminator` object can be used to aid in serialization, deserialization, and validation. The discriminator is a specific object in a schema which is used to inform the consumer of the specification of an alternative schema based on the value associated with it. When using the discriminator, _inline_ schemas will not be considered.", "required": [ "propertyName" ], "additionalProperties": false, "properties": { "propertyName": { "type": "string" }, "mapping": { "$ref": "#/definitions/strings" } } }, "xml": { "type": "object", "description": "A metadata object that allows for more fine-tuned XML model definitions. When using arrays, XML element names are *not* inferred (for singular/plural forms) and the `name` property SHOULD be used to add that information. See examples for expected behavior.", "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "name": { "type": "string" }, "namespace": { "type": "string" }, "prefix": { "type": "string" }, "attribute": { "type": "boolean" }, "wrapped": { "type": "boolean" } } }, "securityScheme": { "type": "object", "description": "Defines a security scheme that can be used by the operations. Supported schemes are HTTP authentication, an API key (either as a header or as a query parameter), OAuth2's common flows (implicit, password, application and access code) as defined in RFC6749, and OpenID Connect Discovery.", "required": [ "type" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "type": { "type": "string" }, "description": { "type": "string" }, "name": { "type": "string" }, "in": { "type": "string" }, "scheme": { "type": "string" }, "bearerFormat": { "type": "string" }, "flows": { "$ref": "#/definitions/oauthFlows" }, "openIdConnectUrl": { "type": "string" } } }, "oauthFlows": { "type": "object", "description": "Allows configuration of the supported OAuth Flows.", "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "implicit": { "$ref": "#/definitions/oauthFlow" }, "password": { "$ref": "#/definitions/oauthFlow" }, "clientCredentials": { "$ref": "#/definitions/oauthFlow" }, "authorizationCode": { "$ref": "#/definitions/oauthFlow" } } }, "oauthFlow": { "type": "object", "description": "Configuration details for a supported OAuth Flow", "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "authorizationUrl": { "type": "string" }, "tokenUrl": { "type": "string" }, "refreshUrl": { "type": "string" }, "scopes": { "$ref": "#/definitions/strings" } } }, "securityRequirement": { "type": "object", "description": "Lists the required security schemes to execute this operation. The name used for each property MUST correspond to a security scheme declared in the Security Schemes under the Components Object. Security Requirement Objects that contain multiple schemes require that all schemes MUST be satisfied for a request to be authorized. This enables support for scenarios where multiple query parameters or HTTP headers are required to convey security information. When a list of Security Requirement Objects is defined on the Open API object or Operation Object, only one of Security Requirement Objects in the list needs to be satisfied to authorize the request.", "additionalProperties": false, "patternProperties": { "^[a-zA-Z0-9\\.\\-_]+$": { "type": "array", "items": { "type": "string" }, "uniqueItems": true } } }, "anyOrExpression": { "oneOf": [ { "$ref": "#/definitions/any" }, { "$ref": "#/definitions/expression" } ] }, "callbackOrReference": { "oneOf": [ { "$ref": "#/definitions/callback" }, { "$ref": "#/definitions/reference" } ] }, "exampleOrReference": { "oneOf": [ { "$ref": "#/definitions/example" }, { "$ref": "#/definitions/reference" } ] }, "headerOrReference": { "oneOf": [ { "$ref": "#/definitions/header" }, { "$ref": "#/definitions/reference" } ] }, "linkOrReference": { "oneOf": [ { "$ref": "#/definitions/link" }, { "$ref": "#/definitions/reference" } ] }, "parameterOrReference": { "oneOf": [ { "$ref": "#/definitions/parameter" }, { "$ref": "#/definitions/reference" } ] }, "requestBodyOrReference": { "oneOf": [ { "$ref": "#/definitions/requestBody" }, { "$ref": "#/definitions/reference" } ] }, "responseOrReference": { "oneOf": [ { "$ref": "#/definitions/response" }, { "$ref": "#/definitions/reference" } ] }, "schemaOrReference": { "oneOf": [ { "$ref": "#/definitions/schema" }, { "$ref": "#/definitions/reference" } ] }, "securitySchemeOrReference": { "oneOf": [ { "$ref": "#/definitions/securityScheme" }, { "$ref": "#/definitions/reference" } ] }, "anysOrExpressions": { "type": "object", "additionalProperties": { "$ref": "#/definitions/anyOrExpression" } }, "callbacksOrReferences": { "type": "object", "additionalProperties": { "$ref": "#/definitions/callbackOrReference" } }, "encodings": { "type": "object", "additionalProperties": { "$ref": "#/definitions/encoding" } }, "examplesOrReferences": { "type": "object", "additionalProperties": { "$ref": "#/definitions/exampleOrReference" } }, "headersOrReferences": { "type": "object", "additionalProperties": { "$ref": "#/definitions/headerOrReference" } }, "linksOrReferences": { "type": "object", "additionalProperties": { "$ref": "#/definitions/linkOrReference" } }, "mediaTypes": { "type": "object", "additionalProperties": { "$ref": "#/definitions/mediaType" } }, "parametersOrReferences": { "type": "object", "additionalProperties": { "$ref": "#/definitions/parameterOrReference" } }, "requestBodiesOrReferences": { "type": "object", "additionalProperties": { "$ref": "#/definitions/requestBodyOrReference" } }, "responsesOrReferences": { "type": "object", "additionalProperties": { "$ref": "#/definitions/responseOrReference" } }, "schemasOrReferences": { "type": "object", "additionalProperties": { "$ref": "#/definitions/schemaOrReference" } }, "securitySchemesOrReferences": { "type": "object", "additionalProperties": { "$ref": "#/definitions/securitySchemeOrReference" } }, "serverVariables": { "type": "object", "additionalProperties": { "$ref": "#/definitions/serverVariable" } }, "strings": { "type": "object", "additionalProperties": { "type": "string" } }, "object": { "type": "object", "additionalProperties": true }, "any": { "additionalProperties": true }, "expression": { "type": "object", "additionalProperties": true }, "specificationExtension": { "description": "Any property starting with x- is valid.", "oneOf": [ { "type": "null" }, { "type": "number" }, { "type": "boolean" }, { "type": "string" }, { "type": "object" }, { "type": "array" } ] }, "defaultType": { "oneOf": [ { "type": "null" }, { "type": "array" }, { "type": "object" }, { "type": "number" }, { "type": "boolean" }, { "type": "string" } ] } } } ================================================ FILE: vendor/github.com/google/gnostic/openapiv3/openapi-3.1.json ================================================ { "title": "A JSON Schema for OpenAPI 3.0.", "id": "http://openapis.org/v3/schema.json#", "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "description": "This is the root document object of the OpenAPI document.", "required": [ "openapi", "info", "paths" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "openapi": { "type": "string" }, "info": { "$ref": "#/definitions/info" }, "servers": { "type": "array", "items": { "$ref": "#/definitions/server" }, "uniqueItems": true }, "paths": { "$ref": "#/definitions/paths" }, "components": { "$ref": "#/definitions/components" }, "security": { "type": "array", "items": { "$ref": "#/definitions/securityRequirement" }, "uniqueItems": true }, "tags": { "type": "array", "items": { "$ref": "#/definitions/tag" }, "uniqueItems": true }, "externalDocs": { "$ref": "#/definitions/externalDocs" } }, "definitions": { "info": { "type": "object", "description": "The object provides metadata about the API. The metadata MAY be used by the clients if needed, and MAY be presented in editing or documentation generation tools for convenience.", "required": [ "title", "version" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "title": { "type": "string" }, "description": { "type": "string" }, "termsOfService": { "type": "string" }, "contact": { "$ref": "#/definitions/contact" }, "license": { "$ref": "#/definitions/license" }, "version": { "type": "string" }, "summary": { "type": "string" } } }, "contact": { "type": "object", "description": "Contact information for the exposed API.", "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "name": { "type": "string" }, "url": { "type": "string", "format": "uri" }, "email": { "type": "string", "format": "email" } } }, "license": { "type": "object", "description": "License information for the exposed API.", "required": [ "name" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "name": { "type": "string" }, "url": { "type": "string" } } }, "server": { "type": "object", "description": "An object representing a Server.", "required": [ "url" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "url": { "type": "string" }, "description": { "type": "string" }, "variables": { "$ref": "#/definitions/serverVariables" } } }, "serverVariable": { "type": "object", "description": "An object representing a Server Variable for server URL template substitution.", "required": [ "default" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "enum": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }, "default": { "type": "string" }, "description": { "type": "string" } } }, "components": { "type": "object", "description": "Holds a set of reusable objects for different aspects of the OAS. All objects defined within the components object will have no effect on the API unless they are explicitly referenced from properties outside the components object.", "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "schemas": { "$ref": "#/definitions/schemasOrReferences" }, "responses": { "$ref": "#/definitions/responsesOrReferences" }, "parameters": { "$ref": "#/definitions/parametersOrReferences" }, "examples": { "$ref": "#/definitions/examplesOrReferences" }, "requestBodies": { "$ref": "#/definitions/requestBodiesOrReferences" }, "headers": { "$ref": "#/definitions/headersOrReferences" }, "securitySchemes": { "$ref": "#/definitions/securitySchemesOrReferences" }, "links": { "$ref": "#/definitions/linksOrReferences" }, "callbacks": { "$ref": "#/definitions/callbacksOrReferences" } } }, "paths": { "type": "object", "description": "Holds the relative paths to the individual endpoints and their operations. The path is appended to the URL from the `Server Object` in order to construct the full URL. The Paths MAY be empty, due to ACL constraints.", "additionalProperties": false, "patternProperties": { "^/": { "$ref": "#/definitions/pathItem" }, "^x-": { "$ref": "#/definitions/specificationExtension" } } }, "pathItem": { "type": "object", "description": "Describes the operations available on a single path. A Path Item MAY be empty, due to ACL constraints. The path itself is still exposed to the documentation viewer but they will not know which operations and parameters are available.", "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "$ref": { "type": "string" }, "summary": { "type": "string" }, "description": { "type": "string" }, "get": { "$ref": "#/definitions/operation" }, "put": { "$ref": "#/definitions/operation" }, "post": { "$ref": "#/definitions/operation" }, "delete": { "$ref": "#/definitions/operation" }, "options": { "$ref": "#/definitions/operation" }, "head": { "$ref": "#/definitions/operation" }, "patch": { "$ref": "#/definitions/operation" }, "trace": { "$ref": "#/definitions/operation" }, "servers": { "type": "array", "items": { "$ref": "#/definitions/server" }, "uniqueItems": true }, "parameters": { "type": "array", "items": { "$ref": "#/definitions/parameterOrReference" }, "uniqueItems": true } } }, "operation": { "type": "object", "description": "Describes a single API operation on a path.", "required": [ "responses" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "tags": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }, "summary": { "type": "string" }, "description": { "type": "string" }, "externalDocs": { "$ref": "#/definitions/externalDocs" }, "operationId": { "type": "string" }, "parameters": { "type": "array", "items": { "$ref": "#/definitions/parameterOrReference" }, "uniqueItems": true }, "requestBody": { "$ref": "#/definitions/requestBodyOrReference" }, "responses": { "$ref": "#/definitions/responses" }, "callbacks": { "$ref": "#/definitions/callbacksOrReferences" }, "deprecated": { "type": "boolean" }, "security": { "type": "array", "items": { "$ref": "#/definitions/securityRequirement" }, "uniqueItems": true }, "servers": { "type": "array", "items": { "$ref": "#/definitions/server" }, "uniqueItems": true } } }, "externalDocs": { "type": "object", "description": "Allows referencing an external resource for extended documentation.", "required": [ "url" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "description": { "type": "string" }, "url": { "type": "string" } } }, "parameter": { "type": "object", "description": "Describes a single operation parameter. A unique parameter is defined by a combination of a name and location.", "required": [ "name", "in" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "name": { "type": "string" }, "in": { "type": "string" }, "description": { "type": "string" }, "required": { "type": "boolean" }, "deprecated": { "type": "boolean" }, "allowEmptyValue": { "type": "boolean" }, "style": { "type": "string" }, "explode": { "type": "boolean" }, "allowReserved": { "type": "boolean" }, "schema": { "$ref": "#/definitions/schemaOrReference" }, "example": { "$ref": "#/definitions/any" }, "examples": { "$ref": "#/definitions/examplesOrReferences" }, "content": { "$ref": "#/definitions/mediaTypes" } } }, "requestBody": { "type": "object", "description": "Describes a single request body.", "required": [ "content" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "description": { "type": "string" }, "content": { "$ref": "#/definitions/mediaTypes" }, "required": { "type": "boolean" } } }, "mediaType": { "type": "object", "description": "Each Media Type Object provides schema and examples for the media type identified by its key.", "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "schema": { "$ref": "#/definitions/schemaOrReference" }, "example": { "$ref": "#/definitions/any" }, "examples": { "$ref": "#/definitions/examplesOrReferences" }, "encoding": { "$ref": "#/definitions/encodings" } } }, "encoding": { "type": "object", "description": "A single encoding definition applied to a single schema property.", "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "contentType": { "type": "string" }, "headers": { "$ref": "#/definitions/headersOrReferences" }, "style": { "type": "string" }, "explode": { "type": "boolean" }, "allowReserved": { "type": "boolean" } } }, "responses": { "type": "object", "description": "A container for the expected responses of an operation. The container maps a HTTP response code to the expected response. The documentation is not necessarily expected to cover all possible HTTP response codes because they may not be known in advance. However, documentation is expected to cover a successful operation response and any known errors. The `default` MAY be used as a default response object for all HTTP codes that are not covered individually by the specification. The `Responses Object` MUST contain at least one response code, and it SHOULD be the response for a successful operation call.", "additionalProperties": false, "patternProperties": { "^([0-9X]{3})$": { "$ref": "#/definitions/responseOrReference" }, "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "default": { "$ref": "#/definitions/responseOrReference" } } }, "response": { "type": "object", "description": "Describes a single response from an API Operation, including design-time, static `links` to operations based on the response.", "required": [ "description" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "description": { "type": "string" }, "headers": { "$ref": "#/definitions/headersOrReferences" }, "content": { "$ref": "#/definitions/mediaTypes" }, "links": { "$ref": "#/definitions/linksOrReferences" } } }, "callback": { "type": "object", "description": "A map of possible out-of band callbacks related to the parent operation. Each value in the map is a Path Item Object that describes a set of requests that may be initiated by the API provider and the expected responses. The key value used to identify the callback object is an expression, evaluated at runtime, that identifies a URL to use for the callback operation.", "additionalProperties": false, "patternProperties": { "^": { "$ref": "#/definitions/pathItem" }, "^x-": { "$ref": "#/definitions/specificationExtension" } } }, "example": { "type": "object", "description": "", "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "summary": { "type": "string" }, "description": { "type": "string" }, "value": { "$ref": "#/definitions/any" }, "externalValue": { "type": "string" } } }, "link": { "type": "object", "description": "The `Link object` represents a possible design-time link for a response. The presence of a link does not guarantee the caller's ability to successfully invoke it, rather it provides a known relationship and traversal mechanism between responses and other operations. Unlike _dynamic_ links (i.e. links provided **in** the response payload), the OAS linking mechanism does not require link information in the runtime response. For computing links, and providing instructions to execute them, a runtime expression is used for accessing values in an operation and using them as parameters while invoking the linked operation.", "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "operationRef": { "type": "string" }, "operationId": { "type": "string" }, "parameters": { "$ref": "#/definitions/anyOrExpression" }, "requestBody": { "$ref": "#/definitions/anyOrExpression" }, "description": { "type": "string" }, "server": { "$ref": "#/definitions/server" } } }, "header": { "type": "object", "description": "The Header Object follows the structure of the Parameter Object with the following changes: 1. `name` MUST NOT be specified, it is given in the corresponding `headers` map. 1. `in` MUST NOT be specified, it is implicitly in `header`. 1. All traits that are affected by the location MUST be applicable to a location of `header` (for example, `style`).", "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "description": { "type": "string" }, "required": { "type": "boolean" }, "deprecated": { "type": "boolean" }, "allowEmptyValue": { "type": "boolean" }, "style": { "type": "string" }, "explode": { "type": "boolean" }, "allowReserved": { "type": "boolean" }, "schema": { "$ref": "#/definitions/schemaOrReference" }, "example": { "$ref": "#/definitions/any" }, "examples": { "$ref": "#/definitions/examplesOrReferences" }, "content": { "$ref": "#/definitions/mediaTypes" } } }, "tag": { "type": "object", "description": "Adds metadata to a single tag that is used by the Operation Object. It is not mandatory to have a Tag Object per tag defined in the Operation Object instances.", "required": [ "name" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "name": { "type": "string" }, "description": { "type": "string" }, "externalDocs": { "$ref": "#/definitions/externalDocs" } } }, "reference": { "type": "object", "description": "A simple object to allow referencing other components in the specification, internally and externally. The Reference Object is defined by JSON Reference and follows the same structure, behavior and rules. For this specification, reference resolution is accomplished as defined by the JSON Reference specification and not by the JSON Schema specification.", "required": [ "$ref" ], "additionalProperties": false, "properties": { "$ref": { "type": "string" }, "summary": { "type": "string" }, "description": { "type": "string" } } }, "schema": { "type": "object", "description": "The Schema Object allows the definition of input and output data types. These types can be objects, but also primitives and arrays. This object is an extended subset of the JSON Schema Specification Wright Draft 00. For more information about the properties, see JSON Schema Core and JSON Schema Validation. Unless stated otherwise, the property definitions follow the JSON Schema.", "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "nullable": { "type": "boolean" }, "discriminator": { "$ref": "#/definitions/discriminator" }, "readOnly": { "type": "boolean" }, "writeOnly": { "type": "boolean" }, "xml": { "$ref": "#/definitions/xml" }, "externalDocs": { "$ref": "#/definitions/externalDocs" }, "example": { "$ref": "#/definitions/any" }, "deprecated": { "type": "boolean" }, "title": { "$ref": "http://json-schema.org/draft-04/schema#/properties/title" }, "multipleOf": { "$ref": "http://json-schema.org/draft-04/schema#/properties/multipleOf" }, "maximum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/maximum" }, "exclusiveMaximum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMaximum" }, "minimum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/minimum" }, "exclusiveMinimum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMinimum" }, "maxLength": { "$ref": "http://json-schema.org/draft-04/schema#/properties/maxLength" }, "minLength": { "$ref": "http://json-schema.org/draft-04/schema#/properties/minLength" }, "pattern": { "$ref": "http://json-schema.org/draft-04/schema#/properties/pattern" }, "maxItems": { "$ref": "http://json-schema.org/draft-04/schema#/properties/maxItems" }, "minItems": { "$ref": "http://json-schema.org/draft-04/schema#/properties/minItems" }, "uniqueItems": { "$ref": "http://json-schema.org/draft-04/schema#/properties/uniqueItems" }, "maxProperties": { "$ref": "http://json-schema.org/draft-04/schema#/properties/maxProperties" }, "minProperties": { "$ref": "http://json-schema.org/draft-04/schema#/properties/minProperties" }, "required": { "$ref": "http://json-schema.org/draft-04/schema#/properties/required" }, "enum": { "$ref": "http://json-schema.org/draft-04/schema#/properties/enum" }, "type": { "type": "string" }, "allOf": { "type": "array", "items": { "$ref": "#/definitions/schemaOrReference" }, "minItems": 1 }, "oneOf": { "type": "array", "items": { "$ref": "#/definitions/schemaOrReference" }, "minItems": 1 }, "anyOf": { "type": "array", "items": { "$ref": "#/definitions/schemaOrReference" }, "minItems": 1 }, "not": { "$ref": "#/definitions/schema" }, "items": { "anyOf": [ { "$ref": "#/definitions/schemaOrReference" }, { "type": "array", "items": { "$ref": "#/definitions/schemaOrReference" }, "minItems": 1 } ] }, "properties": { "type": "object", "additionalProperties": { "$ref": "#/definitions/schemaOrReference" } }, "additionalProperties": { "oneOf": [ { "$ref": "#/definitions/schemaOrReference" }, { "type": "boolean" } ] }, "default": { "$ref": "#/definitions/defaultType" }, "description": { "type": "string" }, "format": { "type": "string" } } }, "discriminator": { "type": "object", "description": "When request bodies or response payloads may be one of a number of different schemas, a `discriminator` object can be used to aid in serialization, deserialization, and validation. The discriminator is a specific object in a schema which is used to inform the consumer of the specification of an alternative schema based on the value associated with it. When using the discriminator, _inline_ schemas will not be considered.", "required": [ "propertyName" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "propertyName": { "type": "string" }, "mapping": { "$ref": "#/definitions/strings" } } }, "xml": { "type": "object", "description": "A metadata object that allows for more fine-tuned XML model definitions. When using arrays, XML element names are *not* inferred (for singular/plural forms) and the `name` property SHOULD be used to add that information. See examples for expected behavior.", "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "name": { "type": "string" }, "namespace": { "type": "string" }, "prefix": { "type": "string" }, "attribute": { "type": "boolean" }, "wrapped": { "type": "boolean" } } }, "securityScheme": { "type": "object", "description": "Defines a security scheme that can be used by the operations. Supported schemes are HTTP authentication, an API key (either as a header, a cookie parameter or as a query parameter), mutual TLS (use of a client certificate), OAuth2's common flows (implicit, password, application and access code) as defined in RFC6749, and OpenID Connect. Please note that currently (2019) the implicit flow is about to be deprecated OAuth 2.0 Security Best Current Practice. Recommended for most use case is Authorization Code Grant flow with PKCE.", "required": [ "type" ], "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "type": { "type": "string" }, "description": { "type": "string" }, "name": { "type": "string" }, "in": { "type": "string" }, "scheme": { "type": "string" }, "bearerFormat": { "type": "string" }, "flows": { "$ref": "#/definitions/oauthFlows" }, "openIdConnectUrl": { "type": "string" } } }, "oauthFlows": { "type": "object", "description": "Allows configuration of the supported OAuth Flows.", "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "implicit": { "$ref": "#/definitions/oauthFlow" }, "password": { "$ref": "#/definitions/oauthFlow" }, "clientCredentials": { "$ref": "#/definitions/oauthFlow" }, "authorizationCode": { "$ref": "#/definitions/oauthFlow" } } }, "oauthFlow": { "type": "object", "description": "Configuration details for a supported OAuth Flow", "additionalProperties": false, "patternProperties": { "^x-": { "$ref": "#/definitions/specificationExtension" } }, "properties": { "authorizationUrl": { "type": "string" }, "tokenUrl": { "type": "string" }, "refreshUrl": { "type": "string" }, "scopes": { "$ref": "#/definitions/strings" } } }, "securityRequirement": { "type": "object", "description": "Lists the required security schemes to execute this operation. The name used for each property MUST correspond to a security scheme declared in the Security Schemes under the Components Object. Security Requirement Objects that contain multiple schemes require that all schemes MUST be satisfied for a request to be authorized. This enables support for scenarios where multiple query parameters or HTTP headers are required to convey security information. When a list of Security Requirement Objects is defined on the OpenAPI Object or Operation Object, only one of the Security Requirement Objects in the list needs to be satisfied to authorize the request.", "additionalProperties": { "type": "array", "items": { "type": "string" }, "uniqueItems": true } }, "anyOrExpression": { "oneOf": [ { "$ref": "#/definitions/any" }, { "$ref": "#/definitions/expression" } ] }, "callbackOrReference": { "oneOf": [ { "$ref": "#/definitions/callback" }, { "$ref": "#/definitions/reference" } ] }, "exampleOrReference": { "oneOf": [ { "$ref": "#/definitions/example" }, { "$ref": "#/definitions/reference" } ] }, "headerOrReference": { "oneOf": [ { "$ref": "#/definitions/header" }, { "$ref": "#/definitions/reference" } ] }, "linkOrReference": { "oneOf": [ { "$ref": "#/definitions/link" }, { "$ref": "#/definitions/reference" } ] }, "parameterOrReference": { "oneOf": [ { "$ref": "#/definitions/parameter" }, { "$ref": "#/definitions/reference" } ] }, "requestBodyOrReference": { "oneOf": [ { "$ref": "#/definitions/requestBody" }, { "$ref": "#/definitions/reference" } ] }, "responseOrReference": { "oneOf": [ { "$ref": "#/definitions/response" }, { "$ref": "#/definitions/reference" } ] }, "schemaOrReference": { "oneOf": [ { "$ref": "#/definitions/schema" }, { "$ref": "#/definitions/reference" } ] }, "securitySchemeOrReference": { "oneOf": [ { "$ref": "#/definitions/securityScheme" }, { "$ref": "#/definitions/reference" } ] }, "callbacksOrReferences": { "type": "object", "additionalProperties": { "$ref": "#/definitions/callbackOrReference" } }, "encodings": { "type": "object", "additionalProperties": { "$ref": "#/definitions/encoding" } }, "examplesOrReferences": { "type": "object", "additionalProperties": { "$ref": "#/definitions/exampleOrReference" } }, "headersOrReferences": { "type": "object", "additionalProperties": { "$ref": "#/definitions/headerOrReference" } }, "linksOrReferences": { "type": "object", "additionalProperties": { "$ref": "#/definitions/linkOrReference" } }, "mediaTypes": { "type": "object", "additionalProperties": { "$ref": "#/definitions/mediaType" } }, "parametersOrReferences": { "type": "object", "additionalProperties": { "$ref": "#/definitions/parameterOrReference" } }, "requestBodiesOrReferences": { "type": "object", "additionalProperties": { "$ref": "#/definitions/requestBodyOrReference" } }, "responsesOrReferences": { "type": "object", "additionalProperties": { "$ref": "#/definitions/responseOrReference" } }, "schemasOrReferences": { "type": "object", "additionalProperties": { "$ref": "#/definitions/schemaOrReference" } }, "securitySchemesOrReferences": { "type": "object", "additionalProperties": { "$ref": "#/definitions/securitySchemeOrReference" } }, "serverVariables": { "type": "object", "additionalProperties": { "$ref": "#/definitions/serverVariable" } }, "strings": { "type": "object", "additionalProperties": { "type": "string" } }, "object": { "type": "object", "additionalProperties": true }, "any": { "additionalProperties": true }, "expression": { "type": "object", "additionalProperties": true }, "specificationExtension": { "description": "Any property starting with x- is valid.", "oneOf": [ { "type": "null" }, { "type": "number" }, { "type": "boolean" }, { "type": "string" }, { "type": "object" }, { "type": "array" } ] }, "defaultType": { "oneOf": [ { "type": "null" }, { "type": "array" }, { "type": "object" }, { "type": "number" }, { "type": "boolean" }, { "type": "string" } ] } } } ================================================ FILE: vendor/github.com/google/go-cmp/LICENSE ================================================ Copyright (c) 2017 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/google/go-cmp/cmp/compare.go ================================================ // Copyright 2017, The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package cmp determines equality of values. // // This package is intended to be a more powerful and safer alternative to // [reflect.DeepEqual] for comparing whether two values are semantically equal. // It is intended to only be used in tests, as performance is not a goal and // it may panic if it cannot compare the values. Its propensity towards // panicking means that its unsuitable for production environments where a // spurious panic may be fatal. // // The primary features of cmp are: // // - When the default behavior of equality does not suit the test's needs, // custom equality functions can override the equality operation. // For example, an equality function may report floats as equal so long as // they are within some tolerance of each other. // // - Types with an Equal method (e.g., [time.Time.Equal]) may use that method // to determine equality. This allows package authors to determine // the equality operation for the types that they define. // // - If no custom equality functions are used and no Equal method is defined, // equality is determined by recursively comparing the primitive kinds on // both values, much like [reflect.DeepEqual]. Unlike [reflect.DeepEqual], // unexported fields are not compared by default; they result in panics // unless suppressed by using an [Ignore] option // (see [github.com/google/go-cmp/cmp/cmpopts.IgnoreUnexported]) // or explicitly compared using the [Exporter] option. package cmp import ( "fmt" "reflect" "strings" "github.com/google/go-cmp/cmp/internal/diff" "github.com/google/go-cmp/cmp/internal/function" "github.com/google/go-cmp/cmp/internal/value" ) // TODO(≥go1.18): Use any instead of interface{}. // Equal reports whether x and y are equal by recursively applying the // following rules in the given order to x and y and all of their sub-values: // // - Let S be the set of all [Ignore], [Transformer], and [Comparer] options that // remain after applying all path filters, value filters, and type filters. // If at least one [Ignore] exists in S, then the comparison is ignored. // If the number of [Transformer] and [Comparer] options in S is non-zero, // then Equal panics because it is ambiguous which option to use. // If S contains a single [Transformer], then use that to transform // the current values and recursively call Equal on the output values. // If S contains a single [Comparer], then use that to compare the current values. // Otherwise, evaluation proceeds to the next rule. // // - If the values have an Equal method of the form "(T) Equal(T) bool" or // "(T) Equal(I) bool" where T is assignable to I, then use the result of // x.Equal(y) even if x or y is nil. Otherwise, no such method exists and // evaluation proceeds to the next rule. // // - Lastly, try to compare x and y based on their basic kinds. // Simple kinds like booleans, integers, floats, complex numbers, strings, // and channels are compared using the equivalent of the == operator in Go. // Functions are only equal if they are both nil, otherwise they are unequal. // // Structs are equal if recursively calling Equal on all fields report equal. // If a struct contains unexported fields, Equal panics unless an [Ignore] option // (e.g., [github.com/google/go-cmp/cmp/cmpopts.IgnoreUnexported]) ignores that field // or the [Exporter] option explicitly permits comparing the unexported field. // // Slices are equal if they are both nil or both non-nil, where recursively // calling Equal on all non-ignored slice or array elements report equal. // Empty non-nil slices and nil slices are not equal; to equate empty slices, // consider using [github.com/google/go-cmp/cmp/cmpopts.EquateEmpty]. // // Maps are equal if they are both nil or both non-nil, where recursively // calling Equal on all non-ignored map entries report equal. // Map keys are equal according to the == operator. // To use custom comparisons for map keys, consider using // [github.com/google/go-cmp/cmp/cmpopts.SortMaps]. // Empty non-nil maps and nil maps are not equal; to equate empty maps, // consider using [github.com/google/go-cmp/cmp/cmpopts.EquateEmpty]. // // Pointers and interfaces are equal if they are both nil or both non-nil, // where they have the same underlying concrete type and recursively // calling Equal on the underlying values reports equal. // // Before recursing into a pointer, slice element, or map, the current path // is checked to detect whether the address has already been visited. // If there is a cycle, then the pointed at values are considered equal // only if both addresses were previously visited in the same path step. func Equal(x, y interface{}, opts ...Option) bool { s := newState(opts) s.compareAny(rootStep(x, y)) return s.result.Equal() } // Diff returns a human-readable report of the differences between two values: // y - x. It returns an empty string if and only if Equal returns true for the // same input values and options. // // The output is displayed as a literal in pseudo-Go syntax. // At the start of each line, a "-" prefix indicates an element removed from x, // a "+" prefix to indicates an element added from y, and the lack of a prefix // indicates an element common to both x and y. If possible, the output // uses fmt.Stringer.String or error.Error methods to produce more humanly // readable outputs. In such cases, the string is prefixed with either an // 's' or 'e' character, respectively, to indicate that the method was called. // // Do not depend on this output being stable. If you need the ability to // programmatically interpret the difference, consider using a custom Reporter. func Diff(x, y interface{}, opts ...Option) string { s := newState(opts) // Optimization: If there are no other reporters, we can optimize for the // common case where the result is equal (and thus no reported difference). // This avoids the expensive construction of a difference tree. if len(s.reporters) == 0 { s.compareAny(rootStep(x, y)) if s.result.Equal() { return "" } s.result = diff.Result{} // Reset results } r := new(defaultReporter) s.reporters = append(s.reporters, reporter{r}) s.compareAny(rootStep(x, y)) d := r.String() if (d == "") != s.result.Equal() { panic("inconsistent difference and equality results") } return d } // rootStep constructs the first path step. If x and y have differing types, // then they are stored within an empty interface type. func rootStep(x, y interface{}) PathStep { vx := reflect.ValueOf(x) vy := reflect.ValueOf(y) // If the inputs are different types, auto-wrap them in an empty interface // so that they have the same parent type. var t reflect.Type if !vx.IsValid() || !vy.IsValid() || vx.Type() != vy.Type() { t = anyType if vx.IsValid() { vvx := reflect.New(t).Elem() vvx.Set(vx) vx = vvx } if vy.IsValid() { vvy := reflect.New(t).Elem() vvy.Set(vy) vy = vvy } } else { t = vx.Type() } return &pathStep{t, vx, vy} } type state struct { // These fields represent the "comparison state". // Calling statelessCompare must not result in observable changes to these. result diff.Result // The current result of comparison curPath Path // The current path in the value tree curPtrs pointerPath // The current set of visited pointers reporters []reporter // Optional reporters // recChecker checks for infinite cycles applying the same set of // transformers upon the output of itself. recChecker recChecker // dynChecker triggers pseudo-random checks for option correctness. // It is safe for statelessCompare to mutate this value. dynChecker dynChecker // These fields, once set by processOption, will not change. exporters []exporter // List of exporters for structs with unexported fields opts Options // List of all fundamental and filter options } func newState(opts []Option) *state { // Always ensure a validator option exists to validate the inputs. s := &state{opts: Options{validator{}}} s.curPtrs.Init() s.processOption(Options(opts)) return s } func (s *state) processOption(opt Option) { switch opt := opt.(type) { case nil: case Options: for _, o := range opt { s.processOption(o) } case coreOption: type filtered interface { isFiltered() bool } if fopt, ok := opt.(filtered); ok && !fopt.isFiltered() { panic(fmt.Sprintf("cannot use an unfiltered option: %v", opt)) } s.opts = append(s.opts, opt) case exporter: s.exporters = append(s.exporters, opt) case reporter: s.reporters = append(s.reporters, opt) default: panic(fmt.Sprintf("unknown option %T", opt)) } } // statelessCompare compares two values and returns the result. // This function is stateless in that it does not alter the current result, // or output to any registered reporters. func (s *state) statelessCompare(step PathStep) diff.Result { // We do not save and restore curPath and curPtrs because all of the // compareX methods should properly push and pop from them. // It is an implementation bug if the contents of the paths differ from // when calling this function to when returning from it. oldResult, oldReporters := s.result, s.reporters s.result = diff.Result{} // Reset result s.reporters = nil // Remove reporters to avoid spurious printouts s.compareAny(step) res := s.result s.result, s.reporters = oldResult, oldReporters return res } func (s *state) compareAny(step PathStep) { // Update the path stack. s.curPath.push(step) defer s.curPath.pop() for _, r := range s.reporters { r.PushStep(step) defer r.PopStep() } s.recChecker.Check(s.curPath) // Cycle-detection for slice elements (see NOTE in compareSlice). t := step.Type() vx, vy := step.Values() if si, ok := step.(SliceIndex); ok && si.isSlice && vx.IsValid() && vy.IsValid() { px, py := vx.Addr(), vy.Addr() if eq, visited := s.curPtrs.Push(px, py); visited { s.report(eq, reportByCycle) return } defer s.curPtrs.Pop(px, py) } // Rule 1: Check whether an option applies on this node in the value tree. if s.tryOptions(t, vx, vy) { return } // Rule 2: Check whether the type has a valid Equal method. if s.tryMethod(t, vx, vy) { return } // Rule 3: Compare based on the underlying kind. switch t.Kind() { case reflect.Bool: s.report(vx.Bool() == vy.Bool(), 0) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: s.report(vx.Int() == vy.Int(), 0) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: s.report(vx.Uint() == vy.Uint(), 0) case reflect.Float32, reflect.Float64: s.report(vx.Float() == vy.Float(), 0) case reflect.Complex64, reflect.Complex128: s.report(vx.Complex() == vy.Complex(), 0) case reflect.String: s.report(vx.String() == vy.String(), 0) case reflect.Chan, reflect.UnsafePointer: s.report(vx.Pointer() == vy.Pointer(), 0) case reflect.Func: s.report(vx.IsNil() && vy.IsNil(), 0) case reflect.Struct: s.compareStruct(t, vx, vy) case reflect.Slice, reflect.Array: s.compareSlice(t, vx, vy) case reflect.Map: s.compareMap(t, vx, vy) case reflect.Ptr: s.comparePtr(t, vx, vy) case reflect.Interface: s.compareInterface(t, vx, vy) default: panic(fmt.Sprintf("%v kind not handled", t.Kind())) } } func (s *state) tryOptions(t reflect.Type, vx, vy reflect.Value) bool { // Evaluate all filters and apply the remaining options. if opt := s.opts.filter(s, t, vx, vy); opt != nil { opt.apply(s, vx, vy) return true } return false } func (s *state) tryMethod(t reflect.Type, vx, vy reflect.Value) bool { // Check if this type even has an Equal method. m, ok := t.MethodByName("Equal") if !ok || !function.IsType(m.Type, function.EqualAssignable) { return false } eq := s.callTTBFunc(m.Func, vx, vy) s.report(eq, reportByMethod) return true } func (s *state) callTRFunc(f, v reflect.Value, step Transform) reflect.Value { if !s.dynChecker.Next() { return f.Call([]reflect.Value{v})[0] } // Run the function twice and ensure that we get the same results back. // We run in goroutines so that the race detector (if enabled) can detect // unsafe mutations to the input. c := make(chan reflect.Value) go detectRaces(c, f, v) got := <-c want := f.Call([]reflect.Value{v})[0] if step.vx, step.vy = got, want; !s.statelessCompare(step).Equal() { // To avoid false-positives with non-reflexive equality operations, // we sanity check whether a value is equal to itself. if step.vx, step.vy = want, want; !s.statelessCompare(step).Equal() { return want } panic(fmt.Sprintf("non-deterministic function detected: %s", function.NameOf(f))) } return want } func (s *state) callTTBFunc(f, x, y reflect.Value) bool { if !s.dynChecker.Next() { return f.Call([]reflect.Value{x, y})[0].Bool() } // Swapping the input arguments is sufficient to check that // f is symmetric and deterministic. // We run in goroutines so that the race detector (if enabled) can detect // unsafe mutations to the input. c := make(chan reflect.Value) go detectRaces(c, f, y, x) got := <-c want := f.Call([]reflect.Value{x, y})[0].Bool() if !got.IsValid() || got.Bool() != want { panic(fmt.Sprintf("non-deterministic or non-symmetric function detected: %s", function.NameOf(f))) } return want } func detectRaces(c chan<- reflect.Value, f reflect.Value, vs ...reflect.Value) { var ret reflect.Value defer func() { recover() // Ignore panics, let the other call to f panic instead c <- ret }() ret = f.Call(vs)[0] } func (s *state) compareStruct(t reflect.Type, vx, vy reflect.Value) { var addr bool var vax, vay reflect.Value // Addressable versions of vx and vy var mayForce, mayForceInit bool step := StructField{&structField{}} for i := 0; i < t.NumField(); i++ { step.typ = t.Field(i).Type step.vx = vx.Field(i) step.vy = vy.Field(i) step.name = t.Field(i).Name step.idx = i step.unexported = !isExported(step.name) if step.unexported { if step.name == "_" { continue } // Defer checking of unexported fields until later to give an // Ignore a chance to ignore the field. if !vax.IsValid() || !vay.IsValid() { // For retrieveUnexportedField to work, the parent struct must // be addressable. Create a new copy of the values if // necessary to make them addressable. addr = vx.CanAddr() || vy.CanAddr() vax = makeAddressable(vx) vay = makeAddressable(vy) } if !mayForceInit { for _, xf := range s.exporters { mayForce = mayForce || xf(t) } mayForceInit = true } step.mayForce = mayForce step.paddr = addr step.pvx = vax step.pvy = vay step.field = t.Field(i) } s.compareAny(step) } } func (s *state) compareSlice(t reflect.Type, vx, vy reflect.Value) { isSlice := t.Kind() == reflect.Slice if isSlice && (vx.IsNil() || vy.IsNil()) { s.report(vx.IsNil() && vy.IsNil(), 0) return } // NOTE: It is incorrect to call curPtrs.Push on the slice header pointer // since slices represents a list of pointers, rather than a single pointer. // The pointer checking logic must be handled on a per-element basis // in compareAny. // // A slice header (see reflect.SliceHeader) in Go is a tuple of a starting // pointer P, a length N, and a capacity C. Supposing each slice element has // a memory size of M, then the slice is equivalent to the list of pointers: // [P+i*M for i in range(N)] // // For example, v[:0] and v[:1] are slices with the same starting pointer, // but they are clearly different values. Using the slice pointer alone // violates the assumption that equal pointers implies equal values. step := SliceIndex{&sliceIndex{pathStep: pathStep{typ: t.Elem()}, isSlice: isSlice}} withIndexes := func(ix, iy int) SliceIndex { if ix >= 0 { step.vx, step.xkey = vx.Index(ix), ix } else { step.vx, step.xkey = reflect.Value{}, -1 } if iy >= 0 { step.vy, step.ykey = vy.Index(iy), iy } else { step.vy, step.ykey = reflect.Value{}, -1 } return step } // Ignore options are able to ignore missing elements in a slice. // However, detecting these reliably requires an optimal differencing // algorithm, for which diff.Difference is not. // // Instead, we first iterate through both slices to detect which elements // would be ignored if standing alone. The index of non-discarded elements // are stored in a separate slice, which diffing is then performed on. var indexesX, indexesY []int var ignoredX, ignoredY []bool for ix := 0; ix < vx.Len(); ix++ { ignored := s.statelessCompare(withIndexes(ix, -1)).NumDiff == 0 if !ignored { indexesX = append(indexesX, ix) } ignoredX = append(ignoredX, ignored) } for iy := 0; iy < vy.Len(); iy++ { ignored := s.statelessCompare(withIndexes(-1, iy)).NumDiff == 0 if !ignored { indexesY = append(indexesY, iy) } ignoredY = append(ignoredY, ignored) } // Compute an edit-script for slices vx and vy (excluding ignored elements). edits := diff.Difference(len(indexesX), len(indexesY), func(ix, iy int) diff.Result { return s.statelessCompare(withIndexes(indexesX[ix], indexesY[iy])) }) // Replay the ignore-scripts and the edit-script. var ix, iy int for ix < vx.Len() || iy < vy.Len() { var e diff.EditType switch { case ix < len(ignoredX) && ignoredX[ix]: e = diff.UniqueX case iy < len(ignoredY) && ignoredY[iy]: e = diff.UniqueY default: e, edits = edits[0], edits[1:] } switch e { case diff.UniqueX: s.compareAny(withIndexes(ix, -1)) ix++ case diff.UniqueY: s.compareAny(withIndexes(-1, iy)) iy++ default: s.compareAny(withIndexes(ix, iy)) ix++ iy++ } } } func (s *state) compareMap(t reflect.Type, vx, vy reflect.Value) { if vx.IsNil() || vy.IsNil() { s.report(vx.IsNil() && vy.IsNil(), 0) return } // Cycle-detection for maps. if eq, visited := s.curPtrs.Push(vx, vy); visited { s.report(eq, reportByCycle) return } defer s.curPtrs.Pop(vx, vy) // We combine and sort the two map keys so that we can perform the // comparisons in a deterministic order. step := MapIndex{&mapIndex{pathStep: pathStep{typ: t.Elem()}}} for _, k := range value.SortKeys(append(vx.MapKeys(), vy.MapKeys()...)) { step.vx = vx.MapIndex(k) step.vy = vy.MapIndex(k) step.key = k if !step.vx.IsValid() && !step.vy.IsValid() { // It is possible for both vx and vy to be invalid if the // key contained a NaN value in it. // // Even with the ability to retrieve NaN keys in Go 1.12, // there still isn't a sensible way to compare the values since // a NaN key may map to multiple unordered values. // The most reasonable way to compare NaNs would be to compare the // set of values. However, this is impossible to do efficiently // since set equality is provably an O(n^2) operation given only // an Equal function. If we had a Less function or Hash function, // this could be done in O(n*log(n)) or O(n), respectively. // // Rather than adding complex logic to deal with NaNs, make it // the user's responsibility to compare such obscure maps. const help = "consider providing a Comparer to compare the map" panic(fmt.Sprintf("%#v has map key with NaNs\n%s", s.curPath, help)) } s.compareAny(step) } } func (s *state) comparePtr(t reflect.Type, vx, vy reflect.Value) { if vx.IsNil() || vy.IsNil() { s.report(vx.IsNil() && vy.IsNil(), 0) return } // Cycle-detection for pointers. if eq, visited := s.curPtrs.Push(vx, vy); visited { s.report(eq, reportByCycle) return } defer s.curPtrs.Pop(vx, vy) vx, vy = vx.Elem(), vy.Elem() s.compareAny(Indirect{&indirect{pathStep{t.Elem(), vx, vy}}}) } func (s *state) compareInterface(t reflect.Type, vx, vy reflect.Value) { if vx.IsNil() || vy.IsNil() { s.report(vx.IsNil() && vy.IsNil(), 0) return } vx, vy = vx.Elem(), vy.Elem() if vx.Type() != vy.Type() { s.report(false, 0) return } s.compareAny(TypeAssertion{&typeAssertion{pathStep{vx.Type(), vx, vy}}}) } func (s *state) report(eq bool, rf resultFlags) { if rf&reportByIgnore == 0 { if eq { s.result.NumSame++ rf |= reportEqual } else { s.result.NumDiff++ rf |= reportUnequal } } for _, r := range s.reporters { r.Report(Result{flags: rf}) } } // recChecker tracks the state needed to periodically perform checks that // user provided transformers are not stuck in an infinitely recursive cycle. type recChecker struct{ next int } // Check scans the Path for any recursive transformers and panics when any // recursive transformers are detected. Note that the presence of a // recursive Transformer does not necessarily imply an infinite cycle. // As such, this check only activates after some minimal number of path steps. func (rc *recChecker) Check(p Path) { const minLen = 1 << 16 if rc.next == 0 { rc.next = minLen } if len(p) < rc.next { return } rc.next <<= 1 // Check whether the same transformer has appeared at least twice. var ss []string m := map[Option]int{} for _, ps := range p { if t, ok := ps.(Transform); ok { t := t.Option() if m[t] == 1 { // Transformer was used exactly once before tf := t.(*transformer).fnc.Type() ss = append(ss, fmt.Sprintf("%v: %v => %v", t, tf.In(0), tf.Out(0))) } m[t]++ } } if len(ss) > 0 { const warning = "recursive set of Transformers detected" const help = "consider using cmpopts.AcyclicTransformer" set := strings.Join(ss, "\n\t") panic(fmt.Sprintf("%s:\n\t%s\n%s", warning, set, help)) } } // dynChecker tracks the state needed to periodically perform checks that // user provided functions are symmetric and deterministic. // The zero value is safe for immediate use. type dynChecker struct{ curr, next int } // Next increments the state and reports whether a check should be performed. // // Checks occur every Nth function call, where N is a triangular number: // // 0 1 3 6 10 15 21 28 36 45 55 66 78 91 105 120 136 153 171 190 ... // // See https://en.wikipedia.org/wiki/Triangular_number // // This sequence ensures that the cost of checks drops significantly as // the number of functions calls grows larger. func (dc *dynChecker) Next() bool { ok := dc.curr == dc.next if ok { dc.curr = 0 dc.next++ } dc.curr++ return ok } // makeAddressable returns a value that is always addressable. // It returns the input verbatim if it is already addressable, // otherwise it creates a new value and returns an addressable copy. func makeAddressable(v reflect.Value) reflect.Value { if v.CanAddr() { return v } vc := reflect.New(v.Type()).Elem() vc.Set(v) return vc } ================================================ FILE: vendor/github.com/google/go-cmp/cmp/export.go ================================================ // Copyright 2017, The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package cmp import ( "reflect" "unsafe" ) // retrieveUnexportedField uses unsafe to forcibly retrieve any field from // a struct such that the value has read-write permissions. // // The parent struct, v, must be addressable, while f must be a StructField // describing the field to retrieve. If addr is false, // then the returned value will be shallowed copied to be non-addressable. func retrieveUnexportedField(v reflect.Value, f reflect.StructField, addr bool) reflect.Value { ve := reflect.NewAt(f.Type, unsafe.Pointer(uintptr(unsafe.Pointer(v.UnsafeAddr()))+f.Offset)).Elem() if !addr { // A field is addressable if and only if the struct is addressable. // If the original parent value was not addressable, shallow copy the // value to make it non-addressable to avoid leaking an implementation // detail of how forcibly exporting a field works. if ve.Kind() == reflect.Interface && ve.IsNil() { return reflect.Zero(f.Type) } return reflect.ValueOf(ve.Interface()).Convert(f.Type) } return ve } ================================================ FILE: vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go ================================================ // Copyright 2017, The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build !cmp_debug // +build !cmp_debug package diff var debug debugger type debugger struct{} func (debugger) Begin(_, _ int, f EqualFunc, _, _ *EditScript) EqualFunc { return f } func (debugger) Update() {} func (debugger) Finish() {} ================================================ FILE: vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go ================================================ // Copyright 2017, The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build cmp_debug // +build cmp_debug package diff import ( "fmt" "strings" "sync" "time" ) // The algorithm can be seen running in real-time by enabling debugging: // go test -tags=cmp_debug -v // // Example output: // === RUN TestDifference/#34 // ┌───────────────────────────────┐ // │ \ · · · · · · · · · · · · · · │ // │ · # · · · · · · · · · · · · · │ // │ · \ · · · · · · · · · · · · · │ // │ · · \ · · · · · · · · · · · · │ // │ · · · X # · · · · · · · · · · │ // │ · · · # \ · · · · · · · · · · │ // │ · · · · · # # · · · · · · · · │ // │ · · · · · # \ · · · · · · · · │ // │ · · · · · · · \ · · · · · · · │ // │ · · · · · · · · \ · · · · · · │ // │ · · · · · · · · · \ · · · · · │ // │ · · · · · · · · · · \ · · # · │ // │ · · · · · · · · · · · \ # # · │ // │ · · · · · · · · · · · # # # · │ // │ · · · · · · · · · · # # # # · │ // │ · · · · · · · · · # # # # # · │ // │ · · · · · · · · · · · · · · \ │ // └───────────────────────────────┘ // [.Y..M.XY......YXYXY.|] // // The grid represents the edit-graph where the horizontal axis represents // list X and the vertical axis represents list Y. The start of the two lists // is the top-left, while the ends are the bottom-right. The '·' represents // an unexplored node in the graph. The '\' indicates that the two symbols // from list X and Y are equal. The 'X' indicates that two symbols are similar // (but not exactly equal) to each other. The '#' indicates that the two symbols // are different (and not similar). The algorithm traverses this graph trying to // make the paths starting in the top-left and the bottom-right connect. // // The series of '.', 'X', 'Y', and 'M' characters at the bottom represents // the currently established path from the forward and reverse searches, // separated by a '|' character. const ( updateDelay = 100 * time.Millisecond finishDelay = 500 * time.Millisecond ansiTerminal = true // ANSI escape codes used to move terminal cursor ) var debug debugger type debugger struct { sync.Mutex p1, p2 EditScript fwdPath, revPath *EditScript grid []byte lines int } func (dbg *debugger) Begin(nx, ny int, f EqualFunc, p1, p2 *EditScript) EqualFunc { dbg.Lock() dbg.fwdPath, dbg.revPath = p1, p2 top := "┌─" + strings.Repeat("──", nx) + "┐\n" row := "│ " + strings.Repeat("· ", nx) + "│\n" btm := "└─" + strings.Repeat("──", nx) + "┘\n" dbg.grid = []byte(top + strings.Repeat(row, ny) + btm) dbg.lines = strings.Count(dbg.String(), "\n") fmt.Print(dbg) // Wrap the EqualFunc so that we can intercept each result. return func(ix, iy int) (r Result) { cell := dbg.grid[len(top)+iy*len(row):][len("│ ")+len("· ")*ix:][:len("·")] for i := range cell { cell[i] = 0 // Zero out the multiple bytes of UTF-8 middle-dot } switch r = f(ix, iy); { case r.Equal(): cell[0] = '\\' case r.Similar(): cell[0] = 'X' default: cell[0] = '#' } return } } func (dbg *debugger) Update() { dbg.print(updateDelay) } func (dbg *debugger) Finish() { dbg.print(finishDelay) dbg.Unlock() } func (dbg *debugger) String() string { dbg.p1, dbg.p2 = *dbg.fwdPath, dbg.p2[:0] for i := len(*dbg.revPath) - 1; i >= 0; i-- { dbg.p2 = append(dbg.p2, (*dbg.revPath)[i]) } return fmt.Sprintf("%s[%v|%v]\n\n", dbg.grid, dbg.p1, dbg.p2) } func (dbg *debugger) print(d time.Duration) { if ansiTerminal { fmt.Printf("\x1b[%dA", dbg.lines) // Reset terminal cursor } fmt.Print(dbg) time.Sleep(d) } ================================================ FILE: vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go ================================================ // Copyright 2017, The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package diff implements an algorithm for producing edit-scripts. // The edit-script is a sequence of operations needed to transform one list // of symbols into another (or vice-versa). The edits allowed are insertions, // deletions, and modifications. The summation of all edits is called the // Levenshtein distance as this problem is well-known in computer science. // // This package prioritizes performance over accuracy. That is, the run time // is more important than obtaining a minimal Levenshtein distance. package diff import ( "math/rand" "time" "github.com/google/go-cmp/cmp/internal/flags" ) // EditType represents a single operation within an edit-script. type EditType uint8 const ( // Identity indicates that a symbol pair is identical in both list X and Y. Identity EditType = iota // UniqueX indicates that a symbol only exists in X and not Y. UniqueX // UniqueY indicates that a symbol only exists in Y and not X. UniqueY // Modified indicates that a symbol pair is a modification of each other. Modified ) // EditScript represents the series of differences between two lists. type EditScript []EditType // String returns a human-readable string representing the edit-script where // Identity, UniqueX, UniqueY, and Modified are represented by the // '.', 'X', 'Y', and 'M' characters, respectively. func (es EditScript) String() string { b := make([]byte, len(es)) for i, e := range es { switch e { case Identity: b[i] = '.' case UniqueX: b[i] = 'X' case UniqueY: b[i] = 'Y' case Modified: b[i] = 'M' default: panic("invalid edit-type") } } return string(b) } // stats returns a histogram of the number of each type of edit operation. func (es EditScript) stats() (s struct{ NI, NX, NY, NM int }) { for _, e := range es { switch e { case Identity: s.NI++ case UniqueX: s.NX++ case UniqueY: s.NY++ case Modified: s.NM++ default: panic("invalid edit-type") } } return } // Dist is the Levenshtein distance and is guaranteed to be 0 if and only if // lists X and Y are equal. func (es EditScript) Dist() int { return len(es) - es.stats().NI } // LenX is the length of the X list. func (es EditScript) LenX() int { return len(es) - es.stats().NY } // LenY is the length of the Y list. func (es EditScript) LenY() int { return len(es) - es.stats().NX } // EqualFunc reports whether the symbols at indexes ix and iy are equal. // When called by Difference, the index is guaranteed to be within nx and ny. type EqualFunc func(ix int, iy int) Result // Result is the result of comparison. // NumSame is the number of sub-elements that are equal. // NumDiff is the number of sub-elements that are not equal. type Result struct{ NumSame, NumDiff int } // BoolResult returns a Result that is either Equal or not Equal. func BoolResult(b bool) Result { if b { return Result{NumSame: 1} // Equal, Similar } else { return Result{NumDiff: 2} // Not Equal, not Similar } } // Equal indicates whether the symbols are equal. Two symbols are equal // if and only if NumDiff == 0. If Equal, then they are also Similar. func (r Result) Equal() bool { return r.NumDiff == 0 } // Similar indicates whether two symbols are similar and may be represented // by using the Modified type. As a special case, we consider binary comparisons // (i.e., those that return Result{1, 0} or Result{0, 1}) to be similar. // // The exact ratio of NumSame to NumDiff to determine similarity may change. func (r Result) Similar() bool { // Use NumSame+1 to offset NumSame so that binary comparisons are similar. return r.NumSame+1 >= r.NumDiff } var randBool = rand.New(rand.NewSource(time.Now().Unix())).Intn(2) == 0 // Difference reports whether two lists of lengths nx and ny are equal // given the definition of equality provided as f. // // This function returns an edit-script, which is a sequence of operations // needed to convert one list into the other. The following invariants for // the edit-script are maintained: // - eq == (es.Dist()==0) // - nx == es.LenX() // - ny == es.LenY() // // This algorithm is not guaranteed to be an optimal solution (i.e., one that // produces an edit-script with a minimal Levenshtein distance). This algorithm // favors performance over optimality. The exact output is not guaranteed to // be stable and may change over time. func Difference(nx, ny int, f EqualFunc) (es EditScript) { // This algorithm is based on traversing what is known as an "edit-graph". // See Figure 1 from "An O(ND) Difference Algorithm and Its Variations" // by Eugene W. Myers. Since D can be as large as N itself, this is // effectively O(N^2). Unlike the algorithm from that paper, we are not // interested in the optimal path, but at least some "decent" path. // // For example, let X and Y be lists of symbols: // X = [A B C A B B A] // Y = [C B A B A C] // // The edit-graph can be drawn as the following: // A B C A B B A // ┌─────────────┐ // C │_|_|\|_|_|_|_│ 0 // B │_|\|_|_|\|\|_│ 1 // A │\|_|_|\|_|_|\│ 2 // B │_|\|_|_|\|\|_│ 3 // A │\|_|_|\|_|_|\│ 4 // C │ | |\| | | | │ 5 // └─────────────┘ 6 // 0 1 2 3 4 5 6 7 // // List X is written along the horizontal axis, while list Y is written // along the vertical axis. At any point on this grid, if the symbol in // list X matches the corresponding symbol in list Y, then a '\' is drawn. // The goal of any minimal edit-script algorithm is to find a path from the // top-left corner to the bottom-right corner, while traveling through the // fewest horizontal or vertical edges. // A horizontal edge is equivalent to inserting a symbol from list X. // A vertical edge is equivalent to inserting a symbol from list Y. // A diagonal edge is equivalent to a matching symbol between both X and Y. // Invariants: // - 0 ≤ fwdPath.X ≤ (fwdFrontier.X, revFrontier.X) ≤ revPath.X ≤ nx // - 0 ≤ fwdPath.Y ≤ (fwdFrontier.Y, revFrontier.Y) ≤ revPath.Y ≤ ny // // In general: // - fwdFrontier.X < revFrontier.X // - fwdFrontier.Y < revFrontier.Y // // Unless, it is time for the algorithm to terminate. fwdPath := path{+1, point{0, 0}, make(EditScript, 0, (nx+ny)/2)} revPath := path{-1, point{nx, ny}, make(EditScript, 0)} fwdFrontier := fwdPath.point // Forward search frontier revFrontier := revPath.point // Reverse search frontier // Search budget bounds the cost of searching for better paths. // The longest sequence of non-matching symbols that can be tolerated is // approximately the square-root of the search budget. searchBudget := 4 * (nx + ny) // O(n) // Running the tests with the "cmp_debug" build tag prints a visualization // of the algorithm running in real-time. This is educational for // understanding how the algorithm works. See debug_enable.go. f = debug.Begin(nx, ny, f, &fwdPath.es, &revPath.es) // The algorithm below is a greedy, meet-in-the-middle algorithm for // computing sub-optimal edit-scripts between two lists. // // The algorithm is approximately as follows: // - Searching for differences switches back-and-forth between // a search that starts at the beginning (the top-left corner), and // a search that starts at the end (the bottom-right corner). // The goal of the search is connect with the search // from the opposite corner. // - As we search, we build a path in a greedy manner, // where the first match seen is added to the path (this is sub-optimal, // but provides a decent result in practice). When matches are found, // we try the next pair of symbols in the lists and follow all matches // as far as possible. // - When searching for matches, we search along a diagonal going through // through the "frontier" point. If no matches are found, // we advance the frontier towards the opposite corner. // - This algorithm terminates when either the X coordinates or the // Y coordinates of the forward and reverse frontier points ever intersect. // This algorithm is correct even if searching only in the forward direction // or in the reverse direction. We do both because it is commonly observed // that two lists commonly differ because elements were added to the front // or end of the other list. // // Non-deterministically start with either the forward or reverse direction // to introduce some deliberate instability so that we have the flexibility // to change this algorithm in the future. if flags.Deterministic || randBool { goto forwardSearch } else { goto reverseSearch } forwardSearch: { // Forward search from the beginning. if fwdFrontier.X >= revFrontier.X || fwdFrontier.Y >= revFrontier.Y || searchBudget == 0 { goto finishSearch } for stop1, stop2, i := false, false, 0; !(stop1 && stop2) && searchBudget > 0; i++ { // Search in a diagonal pattern for a match. z := zigzag(i) p := point{fwdFrontier.X + z, fwdFrontier.Y - z} switch { case p.X >= revPath.X || p.Y < fwdPath.Y: stop1 = true // Hit top-right corner case p.Y >= revPath.Y || p.X < fwdPath.X: stop2 = true // Hit bottom-left corner case f(p.X, p.Y).Equal(): // Match found, so connect the path to this point. fwdPath.connect(p, f) fwdPath.append(Identity) // Follow sequence of matches as far as possible. for fwdPath.X < revPath.X && fwdPath.Y < revPath.Y { if !f(fwdPath.X, fwdPath.Y).Equal() { break } fwdPath.append(Identity) } fwdFrontier = fwdPath.point stop1, stop2 = true, true default: searchBudget-- // Match not found } debug.Update() } // Advance the frontier towards reverse point. if revPath.X-fwdFrontier.X >= revPath.Y-fwdFrontier.Y { fwdFrontier.X++ } else { fwdFrontier.Y++ } goto reverseSearch } reverseSearch: { // Reverse search from the end. if fwdFrontier.X >= revFrontier.X || fwdFrontier.Y >= revFrontier.Y || searchBudget == 0 { goto finishSearch } for stop1, stop2, i := false, false, 0; !(stop1 && stop2) && searchBudget > 0; i++ { // Search in a diagonal pattern for a match. z := zigzag(i) p := point{revFrontier.X - z, revFrontier.Y + z} switch { case fwdPath.X >= p.X || revPath.Y < p.Y: stop1 = true // Hit bottom-left corner case fwdPath.Y >= p.Y || revPath.X < p.X: stop2 = true // Hit top-right corner case f(p.X-1, p.Y-1).Equal(): // Match found, so connect the path to this point. revPath.connect(p, f) revPath.append(Identity) // Follow sequence of matches as far as possible. for fwdPath.X < revPath.X && fwdPath.Y < revPath.Y { if !f(revPath.X-1, revPath.Y-1).Equal() { break } revPath.append(Identity) } revFrontier = revPath.point stop1, stop2 = true, true default: searchBudget-- // Match not found } debug.Update() } // Advance the frontier towards forward point. if revFrontier.X-fwdPath.X >= revFrontier.Y-fwdPath.Y { revFrontier.X-- } else { revFrontier.Y-- } goto forwardSearch } finishSearch: // Join the forward and reverse paths and then append the reverse path. fwdPath.connect(revPath.point, f) for i := len(revPath.es) - 1; i >= 0; i-- { t := revPath.es[i] revPath.es = revPath.es[:i] fwdPath.append(t) } debug.Finish() return fwdPath.es } type path struct { dir int // +1 if forward, -1 if reverse point // Leading point of the EditScript path es EditScript } // connect appends any necessary Identity, Modified, UniqueX, or UniqueY types // to the edit-script to connect p.point to dst. func (p *path) connect(dst point, f EqualFunc) { if p.dir > 0 { // Connect in forward direction. for dst.X > p.X && dst.Y > p.Y { switch r := f(p.X, p.Y); { case r.Equal(): p.append(Identity) case r.Similar(): p.append(Modified) case dst.X-p.X >= dst.Y-p.Y: p.append(UniqueX) default: p.append(UniqueY) } } for dst.X > p.X { p.append(UniqueX) } for dst.Y > p.Y { p.append(UniqueY) } } else { // Connect in reverse direction. for p.X > dst.X && p.Y > dst.Y { switch r := f(p.X-1, p.Y-1); { case r.Equal(): p.append(Identity) case r.Similar(): p.append(Modified) case p.Y-dst.Y >= p.X-dst.X: p.append(UniqueY) default: p.append(UniqueX) } } for p.X > dst.X { p.append(UniqueX) } for p.Y > dst.Y { p.append(UniqueY) } } } func (p *path) append(t EditType) { p.es = append(p.es, t) switch t { case Identity, Modified: p.add(p.dir, p.dir) case UniqueX: p.add(p.dir, 0) case UniqueY: p.add(0, p.dir) } debug.Update() } type point struct{ X, Y int } func (p *point) add(dx, dy int) { p.X += dx; p.Y += dy } // zigzag maps a consecutive sequence of integers to a zig-zag sequence. // // [0 1 2 3 4 5 ...] => [0 -1 +1 -2 +2 ...] func zigzag(x int) int { if x&1 != 0 { x = ^x } return x >> 1 } ================================================ FILE: vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go ================================================ // Copyright 2019, The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package flags // Deterministic controls whether the output of Diff should be deterministic. // This is only used for testing. var Deterministic bool ================================================ FILE: vendor/github.com/google/go-cmp/cmp/internal/function/func.go ================================================ // Copyright 2017, The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package function provides functionality for identifying function types. package function import ( "reflect" "regexp" "runtime" "strings" ) type funcType int const ( _ funcType = iota tbFunc // func(T) bool ttbFunc // func(T, T) bool trbFunc // func(T, R) bool tibFunc // func(T, I) bool trFunc // func(T) R Equal = ttbFunc // func(T, T) bool EqualAssignable = tibFunc // func(T, I) bool; encapsulates func(T, T) bool Transformer = trFunc // func(T) R ValueFilter = ttbFunc // func(T, T) bool Less = ttbFunc // func(T, T) bool ValuePredicate = tbFunc // func(T) bool KeyValuePredicate = trbFunc // func(T, R) bool ) var boolType = reflect.TypeOf(true) // IsType reports whether the reflect.Type is of the specified function type. func IsType(t reflect.Type, ft funcType) bool { if t == nil || t.Kind() != reflect.Func || t.IsVariadic() { return false } ni, no := t.NumIn(), t.NumOut() switch ft { case tbFunc: // func(T) bool if ni == 1 && no == 1 && t.Out(0) == boolType { return true } case ttbFunc: // func(T, T) bool if ni == 2 && no == 1 && t.In(0) == t.In(1) && t.Out(0) == boolType { return true } case trbFunc: // func(T, R) bool if ni == 2 && no == 1 && t.Out(0) == boolType { return true } case tibFunc: // func(T, I) bool if ni == 2 && no == 1 && t.In(0).AssignableTo(t.In(1)) && t.Out(0) == boolType { return true } case trFunc: // func(T) R if ni == 1 && no == 1 { return true } } return false } var lastIdentRx = regexp.MustCompile(`[_\p{L}][_\p{L}\p{N}]*$`) // NameOf returns the name of the function value. func NameOf(v reflect.Value) string { fnc := runtime.FuncForPC(v.Pointer()) if fnc == nil { return "" } fullName := fnc.Name() // e.g., "long/path/name/mypkg.(*MyType).(long/path/name/mypkg.myMethod)-fm" // Method closures have a "-fm" suffix. fullName = strings.TrimSuffix(fullName, "-fm") var name string for len(fullName) > 0 { inParen := strings.HasSuffix(fullName, ")") fullName = strings.TrimSuffix(fullName, ")") s := lastIdentRx.FindString(fullName) if s == "" { break } name = s + "." + name fullName = strings.TrimSuffix(fullName, s) if i := strings.LastIndexByte(fullName, '('); inParen && i >= 0 { fullName = fullName[:i] } fullName = strings.TrimSuffix(fullName, ".") } return strings.TrimSuffix(name, ".") } ================================================ FILE: vendor/github.com/google/go-cmp/cmp/internal/value/name.go ================================================ // Copyright 2020, The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package value import ( "reflect" "strconv" ) var anyType = reflect.TypeOf((*interface{})(nil)).Elem() // TypeString is nearly identical to reflect.Type.String, // but has an additional option to specify that full type names be used. func TypeString(t reflect.Type, qualified bool) string { return string(appendTypeName(nil, t, qualified, false)) } func appendTypeName(b []byte, t reflect.Type, qualified, elideFunc bool) []byte { // BUG: Go reflection provides no way to disambiguate two named types // of the same name and within the same package, // but declared within the namespace of different functions. // Use the "any" alias instead of "interface{}" for better readability. if t == anyType { return append(b, "any"...) } // Named type. if t.Name() != "" { if qualified && t.PkgPath() != "" { b = append(b, '"') b = append(b, t.PkgPath()...) b = append(b, '"') b = append(b, '.') b = append(b, t.Name()...) } else { b = append(b, t.String()...) } return b } // Unnamed type. switch k := t.Kind(); k { case reflect.Bool, reflect.String, reflect.UnsafePointer, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: b = append(b, k.String()...) case reflect.Chan: if t.ChanDir() == reflect.RecvDir { b = append(b, "<-"...) } b = append(b, "chan"...) if t.ChanDir() == reflect.SendDir { b = append(b, "<-"...) } b = append(b, ' ') b = appendTypeName(b, t.Elem(), qualified, false) case reflect.Func: if !elideFunc { b = append(b, "func"...) } b = append(b, '(') for i := 0; i < t.NumIn(); i++ { if i > 0 { b = append(b, ", "...) } if i == t.NumIn()-1 && t.IsVariadic() { b = append(b, "..."...) b = appendTypeName(b, t.In(i).Elem(), qualified, false) } else { b = appendTypeName(b, t.In(i), qualified, false) } } b = append(b, ')') switch t.NumOut() { case 0: // Do nothing case 1: b = append(b, ' ') b = appendTypeName(b, t.Out(0), qualified, false) default: b = append(b, " ("...) for i := 0; i < t.NumOut(); i++ { if i > 0 { b = append(b, ", "...) } b = appendTypeName(b, t.Out(i), qualified, false) } b = append(b, ')') } case reflect.Struct: b = append(b, "struct{ "...) for i := 0; i < t.NumField(); i++ { if i > 0 { b = append(b, "; "...) } sf := t.Field(i) if !sf.Anonymous { if qualified && sf.PkgPath != "" { b = append(b, '"') b = append(b, sf.PkgPath...) b = append(b, '"') b = append(b, '.') } b = append(b, sf.Name...) b = append(b, ' ') } b = appendTypeName(b, sf.Type, qualified, false) if sf.Tag != "" { b = append(b, ' ') b = strconv.AppendQuote(b, string(sf.Tag)) } } if b[len(b)-1] == ' ' { b = b[:len(b)-1] } else { b = append(b, ' ') } b = append(b, '}') case reflect.Slice, reflect.Array: b = append(b, '[') if k == reflect.Array { b = strconv.AppendUint(b, uint64(t.Len()), 10) } b = append(b, ']') b = appendTypeName(b, t.Elem(), qualified, false) case reflect.Map: b = append(b, "map["...) b = appendTypeName(b, t.Key(), qualified, false) b = append(b, ']') b = appendTypeName(b, t.Elem(), qualified, false) case reflect.Ptr: b = append(b, '*') b = appendTypeName(b, t.Elem(), qualified, false) case reflect.Interface: b = append(b, "interface{ "...) for i := 0; i < t.NumMethod(); i++ { if i > 0 { b = append(b, "; "...) } m := t.Method(i) if qualified && m.PkgPath != "" { b = append(b, '"') b = append(b, m.PkgPath...) b = append(b, '"') b = append(b, '.') } b = append(b, m.Name...) b = appendTypeName(b, m.Type, qualified, true) } if b[len(b)-1] == ' ' { b = b[:len(b)-1] } else { b = append(b, ' ') } b = append(b, '}') default: panic("invalid kind: " + k.String()) } return b } ================================================ FILE: vendor/github.com/google/go-cmp/cmp/internal/value/pointer.go ================================================ // Copyright 2018, The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package value import ( "reflect" "unsafe" ) // Pointer is an opaque typed pointer and is guaranteed to be comparable. type Pointer struct { p unsafe.Pointer t reflect.Type } // PointerOf returns a Pointer from v, which must be a // reflect.Ptr, reflect.Slice, or reflect.Map. func PointerOf(v reflect.Value) Pointer { // The proper representation of a pointer is unsafe.Pointer, // which is necessary if the GC ever uses a moving collector. return Pointer{unsafe.Pointer(v.Pointer()), v.Type()} } // IsNil reports whether the pointer is nil. func (p Pointer) IsNil() bool { return p.p == nil } // Uintptr returns the pointer as a uintptr. func (p Pointer) Uintptr() uintptr { return uintptr(p.p) } ================================================ FILE: vendor/github.com/google/go-cmp/cmp/internal/value/sort.go ================================================ // Copyright 2017, The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package value import ( "fmt" "math" "reflect" "sort" ) // SortKeys sorts a list of map keys, deduplicating keys if necessary. // The type of each value must be comparable. func SortKeys(vs []reflect.Value) []reflect.Value { if len(vs) == 0 { return vs } // Sort the map keys. sort.SliceStable(vs, func(i, j int) bool { return isLess(vs[i], vs[j]) }) // Deduplicate keys (fails for NaNs). vs2 := vs[:1] for _, v := range vs[1:] { if isLess(vs2[len(vs2)-1], v) { vs2 = append(vs2, v) } } return vs2 } // isLess is a generic function for sorting arbitrary map keys. // The inputs must be of the same type and must be comparable. func isLess(x, y reflect.Value) bool { switch x.Type().Kind() { case reflect.Bool: return !x.Bool() && y.Bool() case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return x.Int() < y.Int() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return x.Uint() < y.Uint() case reflect.Float32, reflect.Float64: // NOTE: This does not sort -0 as less than +0 // since Go maps treat -0 and +0 as equal keys. fx, fy := x.Float(), y.Float() return fx < fy || math.IsNaN(fx) && !math.IsNaN(fy) case reflect.Complex64, reflect.Complex128: cx, cy := x.Complex(), y.Complex() rx, ix, ry, iy := real(cx), imag(cx), real(cy), imag(cy) if rx == ry || (math.IsNaN(rx) && math.IsNaN(ry)) { return ix < iy || math.IsNaN(ix) && !math.IsNaN(iy) } return rx < ry || math.IsNaN(rx) && !math.IsNaN(ry) case reflect.Ptr, reflect.UnsafePointer, reflect.Chan: return x.Pointer() < y.Pointer() case reflect.String: return x.String() < y.String() case reflect.Array: for i := 0; i < x.Len(); i++ { if isLess(x.Index(i), y.Index(i)) { return true } if isLess(y.Index(i), x.Index(i)) { return false } } return false case reflect.Struct: for i := 0; i < x.NumField(); i++ { if isLess(x.Field(i), y.Field(i)) { return true } if isLess(y.Field(i), x.Field(i)) { return false } } return false case reflect.Interface: vx, vy := x.Elem(), y.Elem() if !vx.IsValid() || !vy.IsValid() { return !vx.IsValid() && vy.IsValid() } tx, ty := vx.Type(), vy.Type() if tx == ty { return isLess(x.Elem(), y.Elem()) } if tx.Kind() != ty.Kind() { return vx.Kind() < vy.Kind() } if tx.String() != ty.String() { return tx.String() < ty.String() } if tx.PkgPath() != ty.PkgPath() { return tx.PkgPath() < ty.PkgPath() } // This can happen in rare situations, so we fallback to just comparing // the unique pointer for a reflect.Type. This guarantees deterministic // ordering within a program, but it is obviously not stable. return reflect.ValueOf(vx.Type()).Pointer() < reflect.ValueOf(vy.Type()).Pointer() default: // Must be Func, Map, or Slice; which are not comparable. panic(fmt.Sprintf("%T is not comparable", x.Type())) } } ================================================ FILE: vendor/github.com/google/go-cmp/cmp/options.go ================================================ // Copyright 2017, The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package cmp import ( "fmt" "reflect" "regexp" "strings" "github.com/google/go-cmp/cmp/internal/function" ) // Option configures for specific behavior of [Equal] and [Diff]. In particular, // the fundamental Option functions ([Ignore], [Transformer], and [Comparer]), // configure how equality is determined. // // The fundamental options may be composed with filters ([FilterPath] and // [FilterValues]) to control the scope over which they are applied. // // The [github.com/google/go-cmp/cmp/cmpopts] package provides helper functions // for creating options that may be used with [Equal] and [Diff]. type Option interface { // filter applies all filters and returns the option that remains. // Each option may only read s.curPath and call s.callTTBFunc. // // An Options is returned only if multiple comparers or transformers // can apply simultaneously and will only contain values of those types // or sub-Options containing values of those types. filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption } // applicableOption represents the following types: // // Fundamental: ignore | validator | *comparer | *transformer // Grouping: Options type applicableOption interface { Option // apply executes the option, which may mutate s or panic. apply(s *state, vx, vy reflect.Value) } // coreOption represents the following types: // // Fundamental: ignore | validator | *comparer | *transformer // Filters: *pathFilter | *valuesFilter type coreOption interface { Option isCore() } type core struct{} func (core) isCore() {} // Options is a list of [Option] values that also satisfies the [Option] interface. // Helper comparison packages may return an Options value when packing multiple // [Option] values into a single [Option]. When this package processes an Options, // it will be implicitly expanded into a flat list. // // Applying a filter on an Options is equivalent to applying that same filter // on all individual options held within. type Options []Option func (opts Options) filter(s *state, t reflect.Type, vx, vy reflect.Value) (out applicableOption) { for _, opt := range opts { switch opt := opt.filter(s, t, vx, vy); opt.(type) { case ignore: return ignore{} // Only ignore can short-circuit evaluation case validator: out = validator{} // Takes precedence over comparer or transformer case *comparer, *transformer, Options: switch out.(type) { case nil: out = opt case validator: // Keep validator case *comparer, *transformer, Options: out = Options{out, opt} // Conflicting comparers or transformers } } } return out } func (opts Options) apply(s *state, _, _ reflect.Value) { const warning = "ambiguous set of applicable options" const help = "consider using filters to ensure at most one Comparer or Transformer may apply" var ss []string for _, opt := range flattenOptions(nil, opts) { ss = append(ss, fmt.Sprint(opt)) } set := strings.Join(ss, "\n\t") panic(fmt.Sprintf("%s at %#v:\n\t%s\n%s", warning, s.curPath, set, help)) } func (opts Options) String() string { var ss []string for _, opt := range opts { ss = append(ss, fmt.Sprint(opt)) } return fmt.Sprintf("Options{%s}", strings.Join(ss, ", ")) } // FilterPath returns a new [Option] where opt is only evaluated if filter f // returns true for the current [Path] in the value tree. // // This filter is called even if a slice element or map entry is missing and // provides an opportunity to ignore such cases. The filter function must be // symmetric such that the filter result is identical regardless of whether the // missing value is from x or y. // // The option passed in may be an [Ignore], [Transformer], [Comparer], [Options], or // a previously filtered [Option]. func FilterPath(f func(Path) bool, opt Option) Option { if f == nil { panic("invalid path filter function") } if opt := normalizeOption(opt); opt != nil { return &pathFilter{fnc: f, opt: opt} } return nil } type pathFilter struct { core fnc func(Path) bool opt Option } func (f pathFilter) filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption { if f.fnc(s.curPath) { return f.opt.filter(s, t, vx, vy) } return nil } func (f pathFilter) String() string { return fmt.Sprintf("FilterPath(%s, %v)", function.NameOf(reflect.ValueOf(f.fnc)), f.opt) } // FilterValues returns a new [Option] where opt is only evaluated if filter f, // which is a function of the form "func(T, T) bool", returns true for the // current pair of values being compared. If either value is invalid or // the type of the values is not assignable to T, then this filter implicitly // returns false. // // The filter function must be // symmetric (i.e., agnostic to the order of the inputs) and // deterministic (i.e., produces the same result when given the same inputs). // If T is an interface, it is possible that f is called with two values with // different concrete types that both implement T. // // The option passed in may be an [Ignore], [Transformer], [Comparer], [Options], or // a previously filtered [Option]. func FilterValues(f interface{}, opt Option) Option { v := reflect.ValueOf(f) if !function.IsType(v.Type(), function.ValueFilter) || v.IsNil() { panic(fmt.Sprintf("invalid values filter function: %T", f)) } if opt := normalizeOption(opt); opt != nil { vf := &valuesFilter{fnc: v, opt: opt} if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 { vf.typ = ti } return vf } return nil } type valuesFilter struct { core typ reflect.Type // T fnc reflect.Value // func(T, T) bool opt Option } func (f valuesFilter) filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption { if !vx.IsValid() || !vx.CanInterface() || !vy.IsValid() || !vy.CanInterface() { return nil } if (f.typ == nil || t.AssignableTo(f.typ)) && s.callTTBFunc(f.fnc, vx, vy) { return f.opt.filter(s, t, vx, vy) } return nil } func (f valuesFilter) String() string { return fmt.Sprintf("FilterValues(%s, %v)", function.NameOf(f.fnc), f.opt) } // Ignore is an [Option] that causes all comparisons to be ignored. // This value is intended to be combined with [FilterPath] or [FilterValues]. // It is an error to pass an unfiltered Ignore option to [Equal]. func Ignore() Option { return ignore{} } type ignore struct{ core } func (ignore) isFiltered() bool { return false } func (ignore) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption { return ignore{} } func (ignore) apply(s *state, _, _ reflect.Value) { s.report(true, reportByIgnore) } func (ignore) String() string { return "Ignore()" } // validator is a sentinel Option type to indicate that some options could not // be evaluated due to unexported fields, missing slice elements, or // missing map entries. Both values are validator only for unexported fields. type validator struct{ core } func (validator) filter(_ *state, _ reflect.Type, vx, vy reflect.Value) applicableOption { if !vx.IsValid() || !vy.IsValid() { return validator{} } if !vx.CanInterface() || !vy.CanInterface() { return validator{} } return nil } func (validator) apply(s *state, vx, vy reflect.Value) { // Implies missing slice element or map entry. if !vx.IsValid() || !vy.IsValid() { s.report(vx.IsValid() == vy.IsValid(), 0) return } // Unable to Interface implies unexported field without visibility access. if !vx.CanInterface() || !vy.CanInterface() { help := "consider using a custom Comparer; if you control the implementation of type, you can also consider using an Exporter, AllowUnexported, or cmpopts.IgnoreUnexported" var name string if t := s.curPath.Index(-2).Type(); t.Name() != "" { // Named type with unexported fields. name = fmt.Sprintf("%q.%v", t.PkgPath(), t.Name()) // e.g., "path/to/package".MyType if _, ok := reflect.New(t).Interface().(error); ok { help = "consider using cmpopts.EquateErrors to compare error values" } else if t.Comparable() { help = "consider using cmpopts.EquateComparable to compare comparable Go types" } } else { // Unnamed type with unexported fields. Derive PkgPath from field. var pkgPath string for i := 0; i < t.NumField() && pkgPath == ""; i++ { pkgPath = t.Field(i).PkgPath } name = fmt.Sprintf("%q.(%v)", pkgPath, t.String()) // e.g., "path/to/package".(struct { a int }) } panic(fmt.Sprintf("cannot handle unexported field at %#v:\n\t%v\n%s", s.curPath, name, help)) } panic("not reachable") } // identRx represents a valid identifier according to the Go specification. const identRx = `[_\p{L}][_\p{L}\p{N}]*` var identsRx = regexp.MustCompile(`^` + identRx + `(\.` + identRx + `)*$`) // Transformer returns an [Option] that applies a transformation function that // converts values of a certain type into that of another. // // The transformer f must be a function "func(T) R" that converts values of // type T to those of type R and is implicitly filtered to input values // assignable to T. The transformer must not mutate T in any way. // // To help prevent some cases of infinite recursive cycles applying the // same transform to the output of itself (e.g., in the case where the // input and output types are the same), an implicit filter is added such that // a transformer is applicable only if that exact transformer is not already // in the tail of the [Path] since the last non-[Transform] step. // For situations where the implicit filter is still insufficient, // consider using [github.com/google/go-cmp/cmp/cmpopts.AcyclicTransformer], // which adds a filter to prevent the transformer from // being recursively applied upon itself. // // The name is a user provided label that is used as the [Transform.Name] in the // transformation [PathStep] (and eventually shown in the [Diff] output). // The name must be a valid identifier or qualified identifier in Go syntax. // If empty, an arbitrary name is used. func Transformer(name string, f interface{}) Option { v := reflect.ValueOf(f) if !function.IsType(v.Type(), function.Transformer) || v.IsNil() { panic(fmt.Sprintf("invalid transformer function: %T", f)) } if name == "" { name = function.NameOf(v) if !identsRx.MatchString(name) { name = "λ" // Lambda-symbol as placeholder name } } else if !identsRx.MatchString(name) { panic(fmt.Sprintf("invalid name: %q", name)) } tr := &transformer{name: name, fnc: reflect.ValueOf(f)} if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 { tr.typ = ti } return tr } type transformer struct { core name string typ reflect.Type // T fnc reflect.Value // func(T) R } func (tr *transformer) isFiltered() bool { return tr.typ != nil } func (tr *transformer) filter(s *state, t reflect.Type, _, _ reflect.Value) applicableOption { for i := len(s.curPath) - 1; i >= 0; i-- { if t, ok := s.curPath[i].(Transform); !ok { break // Hit most recent non-Transform step } else if tr == t.trans { return nil // Cannot directly use same Transform } } if tr.typ == nil || t.AssignableTo(tr.typ) { return tr } return nil } func (tr *transformer) apply(s *state, vx, vy reflect.Value) { step := Transform{&transform{pathStep{typ: tr.fnc.Type().Out(0)}, tr}} vvx := s.callTRFunc(tr.fnc, vx, step) vvy := s.callTRFunc(tr.fnc, vy, step) step.vx, step.vy = vvx, vvy s.compareAny(step) } func (tr transformer) String() string { return fmt.Sprintf("Transformer(%s, %s)", tr.name, function.NameOf(tr.fnc)) } // Comparer returns an [Option] that determines whether two values are equal // to each other. // // The comparer f must be a function "func(T, T) bool" and is implicitly // filtered to input values assignable to T. If T is an interface, it is // possible that f is called with two values of different concrete types that // both implement T. // // The equality function must be: // - Symmetric: equal(x, y) == equal(y, x) // - Deterministic: equal(x, y) == equal(x, y) // - Pure: equal(x, y) does not modify x or y func Comparer(f interface{}) Option { v := reflect.ValueOf(f) if !function.IsType(v.Type(), function.Equal) || v.IsNil() { panic(fmt.Sprintf("invalid comparer function: %T", f)) } cm := &comparer{fnc: v} if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 { cm.typ = ti } return cm } type comparer struct { core typ reflect.Type // T fnc reflect.Value // func(T, T) bool } func (cm *comparer) isFiltered() bool { return cm.typ != nil } func (cm *comparer) filter(_ *state, t reflect.Type, _, _ reflect.Value) applicableOption { if cm.typ == nil || t.AssignableTo(cm.typ) { return cm } return nil } func (cm *comparer) apply(s *state, vx, vy reflect.Value) { eq := s.callTTBFunc(cm.fnc, vx, vy) s.report(eq, reportByFunc) } func (cm comparer) String() string { return fmt.Sprintf("Comparer(%s)", function.NameOf(cm.fnc)) } // Exporter returns an [Option] that specifies whether [Equal] is allowed to // introspect into the unexported fields of certain struct types. // // Users of this option must understand that comparing on unexported fields // from external packages is not safe since changes in the internal // implementation of some external package may cause the result of [Equal] // to unexpectedly change. However, it may be valid to use this option on types // defined in an internal package where the semantic meaning of an unexported // field is in the control of the user. // // In many cases, a custom [Comparer] should be used instead that defines // equality as a function of the public API of a type rather than the underlying // unexported implementation. // // For example, the [reflect.Type] documentation defines equality to be determined // by the == operator on the interface (essentially performing a shallow pointer // comparison) and most attempts to compare *[regexp.Regexp] types are interested // in only checking that the regular expression strings are equal. // Both of these are accomplished using [Comparer] options: // // Comparer(func(x, y reflect.Type) bool { return x == y }) // Comparer(func(x, y *regexp.Regexp) bool { return x.String() == y.String() }) // // In other cases, the [github.com/google/go-cmp/cmp/cmpopts.IgnoreUnexported] // option can be used to ignore all unexported fields on specified struct types. func Exporter(f func(reflect.Type) bool) Option { return exporter(f) } type exporter func(reflect.Type) bool func (exporter) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption { panic("not implemented") } // AllowUnexported returns an [Option] that allows [Equal] to forcibly introspect // unexported fields of the specified struct types. // // See [Exporter] for the proper use of this option. func AllowUnexported(types ...interface{}) Option { m := make(map[reflect.Type]bool) for _, typ := range types { t := reflect.TypeOf(typ) if t.Kind() != reflect.Struct { panic(fmt.Sprintf("invalid struct type: %T", typ)) } m[t] = true } return exporter(func(t reflect.Type) bool { return m[t] }) } // Result represents the comparison result for a single node and // is provided by cmp when calling Report (see [Reporter]). type Result struct { _ [0]func() // Make Result incomparable flags resultFlags } // Equal reports whether the node was determined to be equal or not. // As a special case, ignored nodes are considered equal. func (r Result) Equal() bool { return r.flags&(reportEqual|reportByIgnore) != 0 } // ByIgnore reports whether the node is equal because it was ignored. // This never reports true if [Result.Equal] reports false. func (r Result) ByIgnore() bool { return r.flags&reportByIgnore != 0 } // ByMethod reports whether the Equal method determined equality. func (r Result) ByMethod() bool { return r.flags&reportByMethod != 0 } // ByFunc reports whether a [Comparer] function determined equality. func (r Result) ByFunc() bool { return r.flags&reportByFunc != 0 } // ByCycle reports whether a reference cycle was detected. func (r Result) ByCycle() bool { return r.flags&reportByCycle != 0 } type resultFlags uint const ( _ resultFlags = (1 << iota) / 2 reportEqual reportUnequal reportByIgnore reportByMethod reportByFunc reportByCycle ) // Reporter is an [Option] that can be passed to [Equal]. When [Equal] traverses // the value trees, it calls PushStep as it descends into each node in the // tree and PopStep as it ascend out of the node. The leaves of the tree are // either compared (determined to be equal or not equal) or ignored and reported // as such by calling the Report method. func Reporter(r interface { // PushStep is called when a tree-traversal operation is performed. // The PathStep itself is only valid until the step is popped. // The PathStep.Values are valid for the duration of the entire traversal // and must not be mutated. // // Equal always calls PushStep at the start to provide an operation-less // PathStep used to report the root values. // // Within a slice, the exact set of inserted, removed, or modified elements // is unspecified and may change in future implementations. // The entries of a map are iterated through in an unspecified order. PushStep(PathStep) // Report is called exactly once on leaf nodes to report whether the // comparison identified the node as equal, unequal, or ignored. // A leaf node is one that is immediately preceded by and followed by // a pair of PushStep and PopStep calls. Report(Result) // PopStep ascends back up the value tree. // There is always a matching pop call for every push call. PopStep() }) Option { return reporter{r} } type reporter struct{ reporterIface } type reporterIface interface { PushStep(PathStep) Report(Result) PopStep() } func (reporter) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption { panic("not implemented") } // normalizeOption normalizes the input options such that all Options groups // are flattened and groups with a single element are reduced to that element. // Only coreOptions and Options containing coreOptions are allowed. func normalizeOption(src Option) Option { switch opts := flattenOptions(nil, Options{src}); len(opts) { case 0: return nil case 1: return opts[0] default: return opts } } // flattenOptions copies all options in src to dst as a flat list. // Only coreOptions and Options containing coreOptions are allowed. func flattenOptions(dst, src Options) Options { for _, opt := range src { switch opt := opt.(type) { case nil: continue case Options: dst = flattenOptions(dst, opt) case coreOption: dst = append(dst, opt) default: panic(fmt.Sprintf("invalid option type: %T", opt)) } } return dst } ================================================ FILE: vendor/github.com/google/go-cmp/cmp/path.go ================================================ // Copyright 2017, The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package cmp import ( "fmt" "reflect" "strings" "unicode" "unicode/utf8" "github.com/google/go-cmp/cmp/internal/value" ) // Path is a list of [PathStep] describing the sequence of operations to get // from some root type to the current position in the value tree. // The first Path element is always an operation-less [PathStep] that exists // simply to identify the initial type. // // When traversing structs with embedded structs, the embedded struct will // always be accessed as a field before traversing the fields of the // embedded struct themselves. That is, an exported field from the // embedded struct will never be accessed directly from the parent struct. type Path []PathStep // PathStep is a union-type for specific operations to traverse // a value's tree structure. Users of this package never need to implement // these types as values of this type will be returned by this package. // // Implementations of this interface: // - [StructField] // - [SliceIndex] // - [MapIndex] // - [Indirect] // - [TypeAssertion] // - [Transform] type PathStep interface { String() string // Type is the resulting type after performing the path step. Type() reflect.Type // Values is the resulting values after performing the path step. // The type of each valid value is guaranteed to be identical to Type. // // In some cases, one or both may be invalid or have restrictions: // - For StructField, both are not interface-able if the current field // is unexported and the struct type is not explicitly permitted by // an Exporter to traverse unexported fields. // - For SliceIndex, one may be invalid if an element is missing from // either the x or y slice. // - For MapIndex, one may be invalid if an entry is missing from // either the x or y map. // // The provided values must not be mutated. Values() (vx, vy reflect.Value) } var ( _ PathStep = StructField{} _ PathStep = SliceIndex{} _ PathStep = MapIndex{} _ PathStep = Indirect{} _ PathStep = TypeAssertion{} _ PathStep = Transform{} ) func (pa *Path) push(s PathStep) { *pa = append(*pa, s) } func (pa *Path) pop() { *pa = (*pa)[:len(*pa)-1] } // Last returns the last [PathStep] in the Path. // If the path is empty, this returns a non-nil [PathStep] // that reports a nil [PathStep.Type]. func (pa Path) Last() PathStep { return pa.Index(-1) } // Index returns the ith step in the Path and supports negative indexing. // A negative index starts counting from the tail of the Path such that -1 // refers to the last step, -2 refers to the second-to-last step, and so on. // If index is invalid, this returns a non-nil [PathStep] // that reports a nil [PathStep.Type]. func (pa Path) Index(i int) PathStep { if i < 0 { i = len(pa) + i } if i < 0 || i >= len(pa) { return pathStep{} } return pa[i] } // String returns the simplified path to a node. // The simplified path only contains struct field accesses. // // For example: // // MyMap.MySlices.MyField func (pa Path) String() string { var ss []string for _, s := range pa { if _, ok := s.(StructField); ok { ss = append(ss, s.String()) } } return strings.TrimPrefix(strings.Join(ss, ""), ".") } // GoString returns the path to a specific node using Go syntax. // // For example: // // (*root.MyMap["key"].(*mypkg.MyStruct).MySlices)[2][3].MyField func (pa Path) GoString() string { var ssPre, ssPost []string var numIndirect int for i, s := range pa { var nextStep PathStep if i+1 < len(pa) { nextStep = pa[i+1] } switch s := s.(type) { case Indirect: numIndirect++ pPre, pPost := "(", ")" switch nextStep.(type) { case Indirect: continue // Next step is indirection, so let them batch up case StructField: numIndirect-- // Automatic indirection on struct fields case nil: pPre, pPost = "", "" // Last step; no need for parenthesis } if numIndirect > 0 { ssPre = append(ssPre, pPre+strings.Repeat("*", numIndirect)) ssPost = append(ssPost, pPost) } numIndirect = 0 continue case Transform: ssPre = append(ssPre, s.trans.name+"(") ssPost = append(ssPost, ")") continue } ssPost = append(ssPost, s.String()) } for i, j := 0, len(ssPre)-1; i < j; i, j = i+1, j-1 { ssPre[i], ssPre[j] = ssPre[j], ssPre[i] } return strings.Join(ssPre, "") + strings.Join(ssPost, "") } type pathStep struct { typ reflect.Type vx, vy reflect.Value } func (ps pathStep) Type() reflect.Type { return ps.typ } func (ps pathStep) Values() (vx, vy reflect.Value) { return ps.vx, ps.vy } func (ps pathStep) String() string { if ps.typ == nil { return "" } s := value.TypeString(ps.typ, false) if s == "" || strings.ContainsAny(s, "{}\n") { return "root" // Type too simple or complex to print } return fmt.Sprintf("{%s}", s) } // StructField is a [PathStep] that represents a struct field access // on a field called [StructField.Name]. type StructField struct{ *structField } type structField struct { pathStep name string idx int // These fields are used for forcibly accessing an unexported field. // pvx, pvy, and field are only valid if unexported is true. unexported bool mayForce bool // Forcibly allow visibility paddr bool // Was parent addressable? pvx, pvy reflect.Value // Parent values (always addressable) field reflect.StructField // Field information } func (sf StructField) Type() reflect.Type { return sf.typ } func (sf StructField) Values() (vx, vy reflect.Value) { if !sf.unexported { return sf.vx, sf.vy // CanInterface reports true } // Forcibly obtain read-write access to an unexported struct field. if sf.mayForce { vx = retrieveUnexportedField(sf.pvx, sf.field, sf.paddr) vy = retrieveUnexportedField(sf.pvy, sf.field, sf.paddr) return vx, vy // CanInterface reports true } return sf.vx, sf.vy // CanInterface reports false } func (sf StructField) String() string { return fmt.Sprintf(".%s", sf.name) } // Name is the field name. func (sf StructField) Name() string { return sf.name } // Index is the index of the field in the parent struct type. // See [reflect.Type.Field]. func (sf StructField) Index() int { return sf.idx } // SliceIndex is a [PathStep] that represents an index operation on // a slice or array at some index [SliceIndex.Key]. type SliceIndex struct{ *sliceIndex } type sliceIndex struct { pathStep xkey, ykey int isSlice bool // False for reflect.Array } func (si SliceIndex) Type() reflect.Type { return si.typ } func (si SliceIndex) Values() (vx, vy reflect.Value) { return si.vx, si.vy } func (si SliceIndex) String() string { switch { case si.xkey == si.ykey: return fmt.Sprintf("[%d]", si.xkey) case si.ykey == -1: // [5->?] means "I don't know where X[5] went" return fmt.Sprintf("[%d->?]", si.xkey) case si.xkey == -1: // [?->3] means "I don't know where Y[3] came from" return fmt.Sprintf("[?->%d]", si.ykey) default: // [5->3] means "X[5] moved to Y[3]" return fmt.Sprintf("[%d->%d]", si.xkey, si.ykey) } } // Key is the index key; it may return -1 if in a split state func (si SliceIndex) Key() int { if si.xkey != si.ykey { return -1 } return si.xkey } // SplitKeys are the indexes for indexing into slices in the // x and y values, respectively. These indexes may differ due to the // insertion or removal of an element in one of the slices, causing // all of the indexes to be shifted. If an index is -1, then that // indicates that the element does not exist in the associated slice. // // [SliceIndex.Key] is guaranteed to return -1 if and only if the indexes // returned by SplitKeys are not the same. SplitKeys will never return -1 for // both indexes. func (si SliceIndex) SplitKeys() (ix, iy int) { return si.xkey, si.ykey } // MapIndex is a [PathStep] that represents an index operation on a map at some index Key. type MapIndex struct{ *mapIndex } type mapIndex struct { pathStep key reflect.Value } func (mi MapIndex) Type() reflect.Type { return mi.typ } func (mi MapIndex) Values() (vx, vy reflect.Value) { return mi.vx, mi.vy } func (mi MapIndex) String() string { return fmt.Sprintf("[%#v]", mi.key) } // Key is the value of the map key. func (mi MapIndex) Key() reflect.Value { return mi.key } // Indirect is a [PathStep] that represents pointer indirection on the parent type. type Indirect struct{ *indirect } type indirect struct { pathStep } func (in Indirect) Type() reflect.Type { return in.typ } func (in Indirect) Values() (vx, vy reflect.Value) { return in.vx, in.vy } func (in Indirect) String() string { return "*" } // TypeAssertion is a [PathStep] that represents a type assertion on an interface. type TypeAssertion struct{ *typeAssertion } type typeAssertion struct { pathStep } func (ta TypeAssertion) Type() reflect.Type { return ta.typ } func (ta TypeAssertion) Values() (vx, vy reflect.Value) { return ta.vx, ta.vy } func (ta TypeAssertion) String() string { return fmt.Sprintf(".(%v)", value.TypeString(ta.typ, false)) } // Transform is a [PathStep] that represents a transformation // from the parent type to the current type. type Transform struct{ *transform } type transform struct { pathStep trans *transformer } func (tf Transform) Type() reflect.Type { return tf.typ } func (tf Transform) Values() (vx, vy reflect.Value) { return tf.vx, tf.vy } func (tf Transform) String() string { return fmt.Sprintf("%s()", tf.trans.name) } // Name is the name of the [Transformer]. func (tf Transform) Name() string { return tf.trans.name } // Func is the function pointer to the transformer function. func (tf Transform) Func() reflect.Value { return tf.trans.fnc } // Option returns the originally constructed [Transformer] option. // The == operator can be used to detect the exact option used. func (tf Transform) Option() Option { return tf.trans } // pointerPath represents a dual-stack of pointers encountered when // recursively traversing the x and y values. This data structure supports // detection of cycles and determining whether the cycles are equal. // In Go, cycles can occur via pointers, slices, and maps. // // The pointerPath uses a map to represent a stack; where descension into a // pointer pushes the address onto the stack, and ascension from a pointer // pops the address from the stack. Thus, when traversing into a pointer from // reflect.Ptr, reflect.Slice element, or reflect.Map, we can detect cycles // by checking whether the pointer has already been visited. The cycle detection // uses a separate stack for the x and y values. // // If a cycle is detected we need to determine whether the two pointers // should be considered equal. The definition of equality chosen by Equal // requires two graphs to have the same structure. To determine this, both the // x and y values must have a cycle where the previous pointers were also // encountered together as a pair. // // Semantically, this is equivalent to augmenting Indirect, SliceIndex, and // MapIndex with pointer information for the x and y values. // Suppose px and py are two pointers to compare, we then search the // Path for whether px was ever encountered in the Path history of x, and // similarly so with py. If either side has a cycle, the comparison is only // equal if both px and py have a cycle resulting from the same PathStep. // // Using a map as a stack is more performant as we can perform cycle detection // in O(1) instead of O(N) where N is len(Path). type pointerPath struct { // mx is keyed by x pointers, where the value is the associated y pointer. mx map[value.Pointer]value.Pointer // my is keyed by y pointers, where the value is the associated x pointer. my map[value.Pointer]value.Pointer } func (p *pointerPath) Init() { p.mx = make(map[value.Pointer]value.Pointer) p.my = make(map[value.Pointer]value.Pointer) } // Push indicates intent to descend into pointers vx and vy where // visited reports whether either has been seen before. If visited before, // equal reports whether both pointers were encountered together. // Pop must be called if and only if the pointers were never visited. // // The pointers vx and vy must be a reflect.Ptr, reflect.Slice, or reflect.Map // and be non-nil. func (p pointerPath) Push(vx, vy reflect.Value) (equal, visited bool) { px := value.PointerOf(vx) py := value.PointerOf(vy) _, ok1 := p.mx[px] _, ok2 := p.my[py] if ok1 || ok2 { equal = p.mx[px] == py && p.my[py] == px // Pointers paired together return equal, true } p.mx[px] = py p.my[py] = px return false, false } // Pop ascends from pointers vx and vy. func (p pointerPath) Pop(vx, vy reflect.Value) { delete(p.mx, value.PointerOf(vx)) delete(p.my, value.PointerOf(vy)) } // isExported reports whether the identifier is exported. func isExported(id string) bool { r, _ := utf8.DecodeRuneInString(id) return unicode.IsUpper(r) } ================================================ FILE: vendor/github.com/google/go-cmp/cmp/report.go ================================================ // Copyright 2017, The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package cmp // defaultReporter implements the reporter interface. // // As Equal serially calls the PushStep, Report, and PopStep methods, the // defaultReporter constructs a tree-based representation of the compared value // and the result of each comparison (see valueNode). // // When the String method is called, the FormatDiff method transforms the // valueNode tree into a textNode tree, which is a tree-based representation // of the textual output (see textNode). // // Lastly, the textNode.String method produces the final report as a string. type defaultReporter struct { root *valueNode curr *valueNode } func (r *defaultReporter) PushStep(ps PathStep) { r.curr = r.curr.PushStep(ps) if r.root == nil { r.root = r.curr } } func (r *defaultReporter) Report(rs Result) { r.curr.Report(rs) } func (r *defaultReporter) PopStep() { r.curr = r.curr.PopStep() } // String provides a full report of the differences detected as a structured // literal in pseudo-Go syntax. String may only be called after the entire tree // has been traversed. func (r *defaultReporter) String() string { assert(r.root != nil && r.curr == nil) if r.root.NumDiff == 0 { return "" } ptrs := new(pointerReferences) text := formatOptions{}.FormatDiff(r.root, ptrs) resolveReferences(text) return text.String() } func assert(ok bool) { if !ok { panic("assertion failure") } } ================================================ FILE: vendor/github.com/google/go-cmp/cmp/report_compare.go ================================================ // Copyright 2019, The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package cmp import ( "fmt" "reflect" ) // numContextRecords is the number of surrounding equal records to print. const numContextRecords = 2 type diffMode byte const ( diffUnknown diffMode = 0 diffIdentical diffMode = ' ' diffRemoved diffMode = '-' diffInserted diffMode = '+' ) type typeMode int const ( // emitType always prints the type. emitType typeMode = iota // elideType never prints the type. elideType // autoType prints the type only for composite kinds // (i.e., structs, slices, arrays, and maps). autoType ) type formatOptions struct { // DiffMode controls the output mode of FormatDiff. // // If diffUnknown, then produce a diff of the x and y values. // If diffIdentical, then emit values as if they were equal. // If diffRemoved, then only emit x values (ignoring y values). // If diffInserted, then only emit y values (ignoring x values). DiffMode diffMode // TypeMode controls whether to print the type for the current node. // // As a general rule of thumb, we always print the type of the next node // after an interface, and always elide the type of the next node after // a slice or map node. TypeMode typeMode // formatValueOptions are options specific to printing reflect.Values. formatValueOptions } func (opts formatOptions) WithDiffMode(d diffMode) formatOptions { opts.DiffMode = d return opts } func (opts formatOptions) WithTypeMode(t typeMode) formatOptions { opts.TypeMode = t return opts } func (opts formatOptions) WithVerbosity(level int) formatOptions { opts.VerbosityLevel = level opts.LimitVerbosity = true return opts } func (opts formatOptions) verbosity() uint { switch { case opts.VerbosityLevel < 0: return 0 case opts.VerbosityLevel > 16: return 16 // some reasonable maximum to avoid shift overflow default: return uint(opts.VerbosityLevel) } } const maxVerbosityPreset = 6 // verbosityPreset modifies the verbosity settings given an index // between 0 and maxVerbosityPreset, inclusive. func verbosityPreset(opts formatOptions, i int) formatOptions { opts.VerbosityLevel = int(opts.verbosity()) + 2*i if i > 0 { opts.AvoidStringer = true } if i >= maxVerbosityPreset { opts.PrintAddresses = true opts.QualifiedNames = true } return opts } // FormatDiff converts a valueNode tree into a textNode tree, where the later // is a textual representation of the differences detected in the former. func (opts formatOptions) FormatDiff(v *valueNode, ptrs *pointerReferences) (out textNode) { if opts.DiffMode == diffIdentical { opts = opts.WithVerbosity(1) } else if opts.verbosity() < 3 { opts = opts.WithVerbosity(3) } // Check whether we have specialized formatting for this node. // This is not necessary, but helpful for producing more readable outputs. if opts.CanFormatDiffSlice(v) { return opts.FormatDiffSlice(v) } var parentKind reflect.Kind if v.parent != nil && v.parent.TransformerName == "" { parentKind = v.parent.Type.Kind() } // For leaf nodes, format the value based on the reflect.Values alone. // As a special case, treat equal []byte as a leaf nodes. isBytes := v.Type.Kind() == reflect.Slice && v.Type.Elem() == byteType isEqualBytes := isBytes && v.NumDiff+v.NumIgnored+v.NumTransformed == 0 if v.MaxDepth == 0 || isEqualBytes { switch opts.DiffMode { case diffUnknown, diffIdentical: // Format Equal. if v.NumDiff == 0 { outx := opts.FormatValue(v.ValueX, parentKind, ptrs) outy := opts.FormatValue(v.ValueY, parentKind, ptrs) if v.NumIgnored > 0 && v.NumSame == 0 { return textEllipsis } else if outx.Len() < outy.Len() { return outx } else { return outy } } // Format unequal. assert(opts.DiffMode == diffUnknown) var list textList outx := opts.WithTypeMode(elideType).FormatValue(v.ValueX, parentKind, ptrs) outy := opts.WithTypeMode(elideType).FormatValue(v.ValueY, parentKind, ptrs) for i := 0; i <= maxVerbosityPreset && outx != nil && outy != nil && outx.Equal(outy); i++ { opts2 := verbosityPreset(opts, i).WithTypeMode(elideType) outx = opts2.FormatValue(v.ValueX, parentKind, ptrs) outy = opts2.FormatValue(v.ValueY, parentKind, ptrs) } if outx != nil { list = append(list, textRecord{Diff: '-', Value: outx}) } if outy != nil { list = append(list, textRecord{Diff: '+', Value: outy}) } return opts.WithTypeMode(emitType).FormatType(v.Type, list) case diffRemoved: return opts.FormatValue(v.ValueX, parentKind, ptrs) case diffInserted: return opts.FormatValue(v.ValueY, parentKind, ptrs) default: panic("invalid diff mode") } } // Register slice element to support cycle detection. if parentKind == reflect.Slice { ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, true) defer ptrs.Pop() defer func() { out = wrapTrunkReferences(ptrRefs, out) }() } // Descend into the child value node. if v.TransformerName != "" { out := opts.WithTypeMode(emitType).FormatDiff(v.Value, ptrs) out = &textWrap{Prefix: "Inverse(" + v.TransformerName + ", ", Value: out, Suffix: ")"} return opts.FormatType(v.Type, out) } else { switch k := v.Type.Kind(); k { case reflect.Struct, reflect.Array, reflect.Slice: out = opts.formatDiffList(v.Records, k, ptrs) out = opts.FormatType(v.Type, out) case reflect.Map: // Register map to support cycle detection. ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, false) defer ptrs.Pop() out = opts.formatDiffList(v.Records, k, ptrs) out = wrapTrunkReferences(ptrRefs, out) out = opts.FormatType(v.Type, out) case reflect.Ptr: // Register pointer to support cycle detection. ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, false) defer ptrs.Pop() out = opts.FormatDiff(v.Value, ptrs) out = wrapTrunkReferences(ptrRefs, out) out = &textWrap{Prefix: "&", Value: out} case reflect.Interface: out = opts.WithTypeMode(emitType).FormatDiff(v.Value, ptrs) default: panic(fmt.Sprintf("%v cannot have children", k)) } return out } } func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind, ptrs *pointerReferences) textNode { // Derive record name based on the data structure kind. var name string var formatKey func(reflect.Value) string switch k { case reflect.Struct: name = "field" opts = opts.WithTypeMode(autoType) formatKey = func(v reflect.Value) string { return v.String() } case reflect.Slice, reflect.Array: name = "element" opts = opts.WithTypeMode(elideType) formatKey = func(reflect.Value) string { return "" } case reflect.Map: name = "entry" opts = opts.WithTypeMode(elideType) formatKey = func(v reflect.Value) string { return formatMapKey(v, false, ptrs) } } maxLen := -1 if opts.LimitVerbosity { if opts.DiffMode == diffIdentical { maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc... } else { maxLen = (1 << opts.verbosity()) << 1 // 2, 4, 8, 16, 32, 64, etc... } opts.VerbosityLevel-- } // Handle unification. switch opts.DiffMode { case diffIdentical, diffRemoved, diffInserted: var list textList var deferredEllipsis bool // Add final "..." to indicate records were dropped for _, r := range recs { if len(list) == maxLen { deferredEllipsis = true break } // Elide struct fields that are zero value. if k == reflect.Struct { var isZero bool switch opts.DiffMode { case diffIdentical: isZero = r.Value.ValueX.IsZero() || r.Value.ValueY.IsZero() case diffRemoved: isZero = r.Value.ValueX.IsZero() case diffInserted: isZero = r.Value.ValueY.IsZero() } if isZero { continue } } // Elide ignored nodes. if r.Value.NumIgnored > 0 && r.Value.NumSame+r.Value.NumDiff == 0 { deferredEllipsis = !(k == reflect.Slice || k == reflect.Array) if !deferredEllipsis { list.AppendEllipsis(diffStats{}) } continue } if out := opts.FormatDiff(r.Value, ptrs); out != nil { list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) } } if deferredEllipsis { list.AppendEllipsis(diffStats{}) } return &textWrap{Prefix: "{", Value: list, Suffix: "}"} case diffUnknown: default: panic("invalid diff mode") } // Handle differencing. var numDiffs int var list textList var keys []reflect.Value // invariant: len(list) == len(keys) groups := coalesceAdjacentRecords(name, recs) maxGroup := diffStats{Name: name} for i, ds := range groups { if maxLen >= 0 && numDiffs >= maxLen { maxGroup = maxGroup.Append(ds) continue } // Handle equal records. if ds.NumDiff() == 0 { // Compute the number of leading and trailing records to print. var numLo, numHi int numEqual := ds.NumIgnored + ds.NumIdentical for numLo < numContextRecords && numLo+numHi < numEqual && i != 0 { if r := recs[numLo].Value; r.NumIgnored > 0 && r.NumSame+r.NumDiff == 0 { break } numLo++ } for numHi < numContextRecords && numLo+numHi < numEqual && i != len(groups)-1 { if r := recs[numEqual-numHi-1].Value; r.NumIgnored > 0 && r.NumSame+r.NumDiff == 0 { break } numHi++ } if numEqual-(numLo+numHi) == 1 && ds.NumIgnored == 0 { numHi++ // Avoid pointless coalescing of a single equal record } // Format the equal values. for _, r := range recs[:numLo] { out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value, ptrs) list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) keys = append(keys, r.Key) } if numEqual > numLo+numHi { ds.NumIdentical -= numLo + numHi list.AppendEllipsis(ds) for len(keys) < len(list) { keys = append(keys, reflect.Value{}) } } for _, r := range recs[numEqual-numHi : numEqual] { out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value, ptrs) list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) keys = append(keys, r.Key) } recs = recs[numEqual:] continue } // Handle unequal records. for _, r := range recs[:ds.NumDiff()] { switch { case opts.CanFormatDiffSlice(r.Value): out := opts.FormatDiffSlice(r.Value) list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) keys = append(keys, r.Key) case r.Value.NumChildren == r.Value.MaxDepth: outx := opts.WithDiffMode(diffRemoved).FormatDiff(r.Value, ptrs) outy := opts.WithDiffMode(diffInserted).FormatDiff(r.Value, ptrs) for i := 0; i <= maxVerbosityPreset && outx != nil && outy != nil && outx.Equal(outy); i++ { opts2 := verbosityPreset(opts, i) outx = opts2.WithDiffMode(diffRemoved).FormatDiff(r.Value, ptrs) outy = opts2.WithDiffMode(diffInserted).FormatDiff(r.Value, ptrs) } if outx != nil { list = append(list, textRecord{Diff: diffRemoved, Key: formatKey(r.Key), Value: outx}) keys = append(keys, r.Key) } if outy != nil { list = append(list, textRecord{Diff: diffInserted, Key: formatKey(r.Key), Value: outy}) keys = append(keys, r.Key) } default: out := opts.FormatDiff(r.Value, ptrs) list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) keys = append(keys, r.Key) } } recs = recs[ds.NumDiff():] numDiffs += ds.NumDiff() } if maxGroup.IsZero() { assert(len(recs) == 0) } else { list.AppendEllipsis(maxGroup) for len(keys) < len(list) { keys = append(keys, reflect.Value{}) } } assert(len(list) == len(keys)) // For maps, the default formatting logic uses fmt.Stringer which may // produce ambiguous output. Avoid calling String to disambiguate. if k == reflect.Map { var ambiguous bool seenKeys := map[string]reflect.Value{} for i, currKey := range keys { if currKey.IsValid() { strKey := list[i].Key prevKey, seen := seenKeys[strKey] if seen && prevKey.CanInterface() && currKey.CanInterface() { ambiguous = prevKey.Interface() != currKey.Interface() if ambiguous { break } } seenKeys[strKey] = currKey } } if ambiguous { for i, k := range keys { if k.IsValid() { list[i].Key = formatMapKey(k, true, ptrs) } } } } return &textWrap{Prefix: "{", Value: list, Suffix: "}"} } // coalesceAdjacentRecords coalesces the list of records into groups of // adjacent equal, or unequal counts. func coalesceAdjacentRecords(name string, recs []reportRecord) (groups []diffStats) { var prevCase int // Arbitrary index into which case last occurred lastStats := func(i int) *diffStats { if prevCase != i { groups = append(groups, diffStats{Name: name}) prevCase = i } return &groups[len(groups)-1] } for _, r := range recs { switch rv := r.Value; { case rv.NumIgnored > 0 && rv.NumSame+rv.NumDiff == 0: lastStats(1).NumIgnored++ case rv.NumDiff == 0: lastStats(1).NumIdentical++ case rv.NumDiff > 0 && !rv.ValueY.IsValid(): lastStats(2).NumRemoved++ case rv.NumDiff > 0 && !rv.ValueX.IsValid(): lastStats(2).NumInserted++ default: lastStats(2).NumModified++ } } return groups } ================================================ FILE: vendor/github.com/google/go-cmp/cmp/report_references.go ================================================ // Copyright 2020, The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package cmp import ( "fmt" "reflect" "strings" "github.com/google/go-cmp/cmp/internal/flags" "github.com/google/go-cmp/cmp/internal/value" ) const ( pointerDelimPrefix = "⟪" pointerDelimSuffix = "⟫" ) // formatPointer prints the address of the pointer. func formatPointer(p value.Pointer, withDelims bool) string { v := p.Uintptr() if flags.Deterministic { v = 0xdeadf00f // Only used for stable testing purposes } if withDelims { return pointerDelimPrefix + formatHex(uint64(v)) + pointerDelimSuffix } return formatHex(uint64(v)) } // pointerReferences is a stack of pointers visited so far. type pointerReferences [][2]value.Pointer func (ps *pointerReferences) PushPair(vx, vy reflect.Value, d diffMode, deref bool) (pp [2]value.Pointer) { if deref && vx.IsValid() { vx = vx.Addr() } if deref && vy.IsValid() { vy = vy.Addr() } switch d { case diffUnknown, diffIdentical: pp = [2]value.Pointer{value.PointerOf(vx), value.PointerOf(vy)} case diffRemoved: pp = [2]value.Pointer{value.PointerOf(vx), value.Pointer{}} case diffInserted: pp = [2]value.Pointer{value.Pointer{}, value.PointerOf(vy)} } *ps = append(*ps, pp) return pp } func (ps *pointerReferences) Push(v reflect.Value) (p value.Pointer, seen bool) { p = value.PointerOf(v) for _, pp := range *ps { if p == pp[0] || p == pp[1] { return p, true } } *ps = append(*ps, [2]value.Pointer{p, p}) return p, false } func (ps *pointerReferences) Pop() { *ps = (*ps)[:len(*ps)-1] } // trunkReferences is metadata for a textNode indicating that the sub-tree // represents the value for either pointer in a pair of references. type trunkReferences struct{ pp [2]value.Pointer } // trunkReference is metadata for a textNode indicating that the sub-tree // represents the value for the given pointer reference. type trunkReference struct{ p value.Pointer } // leafReference is metadata for a textNode indicating that the value is // truncated as it refers to another part of the tree (i.e., a trunk). type leafReference struct{ p value.Pointer } func wrapTrunkReferences(pp [2]value.Pointer, s textNode) textNode { switch { case pp[0].IsNil(): return &textWrap{Value: s, Metadata: trunkReference{pp[1]}} case pp[1].IsNil(): return &textWrap{Value: s, Metadata: trunkReference{pp[0]}} case pp[0] == pp[1]: return &textWrap{Value: s, Metadata: trunkReference{pp[0]}} default: return &textWrap{Value: s, Metadata: trunkReferences{pp}} } } func wrapTrunkReference(p value.Pointer, printAddress bool, s textNode) textNode { var prefix string if printAddress { prefix = formatPointer(p, true) } return &textWrap{Prefix: prefix, Value: s, Metadata: trunkReference{p}} } func makeLeafReference(p value.Pointer, printAddress bool) textNode { out := &textWrap{Prefix: "(", Value: textEllipsis, Suffix: ")"} var prefix string if printAddress { prefix = formatPointer(p, true) } return &textWrap{Prefix: prefix, Value: out, Metadata: leafReference{p}} } // resolveReferences walks the textNode tree searching for any leaf reference // metadata and resolves each against the corresponding trunk references. // Since pointer addresses in memory are not particularly readable to the user, // it replaces each pointer value with an arbitrary and unique reference ID. func resolveReferences(s textNode) { var walkNodes func(textNode, func(textNode)) walkNodes = func(s textNode, f func(textNode)) { f(s) switch s := s.(type) { case *textWrap: walkNodes(s.Value, f) case textList: for _, r := range s { walkNodes(r.Value, f) } } } // Collect all trunks and leaves with reference metadata. var trunks, leaves []*textWrap walkNodes(s, func(s textNode) { if s, ok := s.(*textWrap); ok { switch s.Metadata.(type) { case leafReference: leaves = append(leaves, s) case trunkReference, trunkReferences: trunks = append(trunks, s) } } }) // No leaf references to resolve. if len(leaves) == 0 { return } // Collect the set of all leaf references to resolve. leafPtrs := make(map[value.Pointer]bool) for _, leaf := range leaves { leafPtrs[leaf.Metadata.(leafReference).p] = true } // Collect the set of trunk pointers that are always paired together. // This allows us to assign a single ID to both pointers for brevity. // If a pointer in a pair ever occurs by itself or as a different pair, // then the pair is broken. pairedTrunkPtrs := make(map[value.Pointer]value.Pointer) unpair := func(p value.Pointer) { if !pairedTrunkPtrs[p].IsNil() { pairedTrunkPtrs[pairedTrunkPtrs[p]] = value.Pointer{} // invalidate other half } pairedTrunkPtrs[p] = value.Pointer{} // invalidate this half } for _, trunk := range trunks { switch p := trunk.Metadata.(type) { case trunkReference: unpair(p.p) // standalone pointer cannot be part of a pair case trunkReferences: p0, ok0 := pairedTrunkPtrs[p.pp[0]] p1, ok1 := pairedTrunkPtrs[p.pp[1]] switch { case !ok0 && !ok1: // Register the newly seen pair. pairedTrunkPtrs[p.pp[0]] = p.pp[1] pairedTrunkPtrs[p.pp[1]] = p.pp[0] case ok0 && ok1 && p0 == p.pp[1] && p1 == p.pp[0]: // Exact pair already seen; do nothing. default: // Pair conflicts with some other pair; break all pairs. unpair(p.pp[0]) unpair(p.pp[1]) } } } // Correlate each pointer referenced by leaves to a unique identifier, // and print the IDs for each trunk that matches those pointers. var nextID uint ptrIDs := make(map[value.Pointer]uint) newID := func() uint { id := nextID nextID++ return id } for _, trunk := range trunks { switch p := trunk.Metadata.(type) { case trunkReference: if print := leafPtrs[p.p]; print { id, ok := ptrIDs[p.p] if !ok { id = newID() ptrIDs[p.p] = id } trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id)) } case trunkReferences: print0 := leafPtrs[p.pp[0]] print1 := leafPtrs[p.pp[1]] if print0 || print1 { id0, ok0 := ptrIDs[p.pp[0]] id1, ok1 := ptrIDs[p.pp[1]] isPair := pairedTrunkPtrs[p.pp[0]] == p.pp[1] && pairedTrunkPtrs[p.pp[1]] == p.pp[0] if isPair { var id uint assert(ok0 == ok1) // must be seen together or not at all if ok0 { assert(id0 == id1) // must have the same ID id = id0 } else { id = newID() ptrIDs[p.pp[0]] = id ptrIDs[p.pp[1]] = id } trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id)) } else { if print0 && !ok0 { id0 = newID() ptrIDs[p.pp[0]] = id0 } if print1 && !ok1 { id1 = newID() ptrIDs[p.pp[1]] = id1 } switch { case print0 && print1: trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id0)+","+formatReference(id1)) case print0: trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id0)) case print1: trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id1)) } } } } } // Update all leaf references with the unique identifier. for _, leaf := range leaves { if id, ok := ptrIDs[leaf.Metadata.(leafReference).p]; ok { leaf.Prefix = updateReferencePrefix(leaf.Prefix, formatReference(id)) } } } func formatReference(id uint) string { return fmt.Sprintf("ref#%d", id) } func updateReferencePrefix(prefix, ref string) string { if prefix == "" { return pointerDelimPrefix + ref + pointerDelimSuffix } suffix := strings.TrimPrefix(prefix, pointerDelimPrefix) return pointerDelimPrefix + ref + ": " + suffix } ================================================ FILE: vendor/github.com/google/go-cmp/cmp/report_reflect.go ================================================ // Copyright 2019, The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package cmp import ( "bytes" "fmt" "reflect" "strconv" "strings" "unicode" "unicode/utf8" "github.com/google/go-cmp/cmp/internal/value" ) var ( anyType = reflect.TypeOf((*interface{})(nil)).Elem() stringType = reflect.TypeOf((*string)(nil)).Elem() bytesType = reflect.TypeOf((*[]byte)(nil)).Elem() byteType = reflect.TypeOf((*byte)(nil)).Elem() ) type formatValueOptions struct { // AvoidStringer controls whether to avoid calling custom stringer // methods like error.Error or fmt.Stringer.String. AvoidStringer bool // PrintAddresses controls whether to print the address of all pointers, // slice elements, and maps. PrintAddresses bool // QualifiedNames controls whether FormatType uses the fully qualified name // (including the full package path as opposed to just the package name). QualifiedNames bool // VerbosityLevel controls the amount of output to produce. // A higher value produces more output. A value of zero or lower produces // no output (represented using an ellipsis). // If LimitVerbosity is false, then the level is treated as infinite. VerbosityLevel int // LimitVerbosity specifies that formatting should respect VerbosityLevel. LimitVerbosity bool } // FormatType prints the type as if it were wrapping s. // This may return s as-is depending on the current type and TypeMode mode. func (opts formatOptions) FormatType(t reflect.Type, s textNode) textNode { // Check whether to emit the type or not. switch opts.TypeMode { case autoType: switch t.Kind() { case reflect.Struct, reflect.Slice, reflect.Array, reflect.Map: if s.Equal(textNil) { return s } default: return s } if opts.DiffMode == diffIdentical { return s // elide type for identical nodes } case elideType: return s } // Determine the type label, applying special handling for unnamed types. typeName := value.TypeString(t, opts.QualifiedNames) if t.Name() == "" { // According to Go grammar, certain type literals contain symbols that // do not strongly bind to the next lexicographical token (e.g., *T). switch t.Kind() { case reflect.Chan, reflect.Func, reflect.Ptr: typeName = "(" + typeName + ")" } } return &textWrap{Prefix: typeName, Value: wrapParens(s)} } // wrapParens wraps s with a set of parenthesis, but avoids it if the // wrapped node itself is already surrounded by a pair of parenthesis or braces. // It handles unwrapping one level of pointer-reference nodes. func wrapParens(s textNode) textNode { var refNode *textWrap if s2, ok := s.(*textWrap); ok { // Unwrap a single pointer reference node. switch s2.Metadata.(type) { case leafReference, trunkReference, trunkReferences: refNode = s2 if s3, ok := refNode.Value.(*textWrap); ok { s2 = s3 } } // Already has delimiters that make parenthesis unnecessary. hasParens := strings.HasPrefix(s2.Prefix, "(") && strings.HasSuffix(s2.Suffix, ")") hasBraces := strings.HasPrefix(s2.Prefix, "{") && strings.HasSuffix(s2.Suffix, "}") if hasParens || hasBraces { return s } } if refNode != nil { refNode.Value = &textWrap{Prefix: "(", Value: refNode.Value, Suffix: ")"} return s } return &textWrap{Prefix: "(", Value: s, Suffix: ")"} } // FormatValue prints the reflect.Value, taking extra care to avoid descending // into pointers already in ptrs. As pointers are visited, ptrs is also updated. func (opts formatOptions) FormatValue(v reflect.Value, parentKind reflect.Kind, ptrs *pointerReferences) (out textNode) { if !v.IsValid() { return nil } t := v.Type() // Check slice element for cycles. if parentKind == reflect.Slice { ptrRef, visited := ptrs.Push(v.Addr()) if visited { return makeLeafReference(ptrRef, false) } defer ptrs.Pop() defer func() { out = wrapTrunkReference(ptrRef, false, out) }() } // Check whether there is an Error or String method to call. if !opts.AvoidStringer && v.CanInterface() { // Avoid calling Error or String methods on nil receivers since many // implementations crash when doing so. if (t.Kind() != reflect.Ptr && t.Kind() != reflect.Interface) || !v.IsNil() { var prefix, strVal string func() { // Swallow and ignore any panics from String or Error. defer func() { recover() }() switch v := v.Interface().(type) { case error: strVal = v.Error() prefix = "e" case fmt.Stringer: strVal = v.String() prefix = "s" } }() if prefix != "" { return opts.formatString(prefix, strVal) } } } // Check whether to explicitly wrap the result with the type. var skipType bool defer func() { if !skipType { out = opts.FormatType(t, out) } }() switch t.Kind() { case reflect.Bool: return textLine(fmt.Sprint(v.Bool())) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return textLine(fmt.Sprint(v.Int())) case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64: return textLine(fmt.Sprint(v.Uint())) case reflect.Uint8: if parentKind == reflect.Slice || parentKind == reflect.Array { return textLine(formatHex(v.Uint())) } return textLine(fmt.Sprint(v.Uint())) case reflect.Uintptr: return textLine(formatHex(v.Uint())) case reflect.Float32, reflect.Float64: return textLine(fmt.Sprint(v.Float())) case reflect.Complex64, reflect.Complex128: return textLine(fmt.Sprint(v.Complex())) case reflect.String: return opts.formatString("", v.String()) case reflect.UnsafePointer, reflect.Chan, reflect.Func: return textLine(formatPointer(value.PointerOf(v), true)) case reflect.Struct: var list textList v := makeAddressable(v) // needed for retrieveUnexportedField maxLen := v.NumField() if opts.LimitVerbosity { maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc... opts.VerbosityLevel-- } for i := 0; i < v.NumField(); i++ { vv := v.Field(i) if vv.IsZero() { continue // Elide fields with zero values } if len(list) == maxLen { list.AppendEllipsis(diffStats{}) break } sf := t.Field(i) if !isExported(sf.Name) { vv = retrieveUnexportedField(v, sf, true) } s := opts.WithTypeMode(autoType).FormatValue(vv, t.Kind(), ptrs) list = append(list, textRecord{Key: sf.Name, Value: s}) } return &textWrap{Prefix: "{", Value: list, Suffix: "}"} case reflect.Slice: if v.IsNil() { return textNil } // Check whether this is a []byte of text data. if t.Elem() == byteType { b := v.Bytes() isPrintSpace := func(r rune) bool { return unicode.IsPrint(r) || unicode.IsSpace(r) } if len(b) > 0 && utf8.Valid(b) && len(bytes.TrimFunc(b, isPrintSpace)) == 0 { out = opts.formatString("", string(b)) skipType = true return opts.FormatType(t, out) } } fallthrough case reflect.Array: maxLen := v.Len() if opts.LimitVerbosity { maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc... opts.VerbosityLevel-- } var list textList for i := 0; i < v.Len(); i++ { if len(list) == maxLen { list.AppendEllipsis(diffStats{}) break } s := opts.WithTypeMode(elideType).FormatValue(v.Index(i), t.Kind(), ptrs) list = append(list, textRecord{Value: s}) } out = &textWrap{Prefix: "{", Value: list, Suffix: "}"} if t.Kind() == reflect.Slice && opts.PrintAddresses { header := fmt.Sprintf("ptr:%v, len:%d, cap:%d", formatPointer(value.PointerOf(v), false), v.Len(), v.Cap()) out = &textWrap{Prefix: pointerDelimPrefix + header + pointerDelimSuffix, Value: out} } return out case reflect.Map: if v.IsNil() { return textNil } // Check pointer for cycles. ptrRef, visited := ptrs.Push(v) if visited { return makeLeafReference(ptrRef, opts.PrintAddresses) } defer ptrs.Pop() maxLen := v.Len() if opts.LimitVerbosity { maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc... opts.VerbosityLevel-- } var list textList for _, k := range value.SortKeys(v.MapKeys()) { if len(list) == maxLen { list.AppendEllipsis(diffStats{}) break } sk := formatMapKey(k, false, ptrs) sv := opts.WithTypeMode(elideType).FormatValue(v.MapIndex(k), t.Kind(), ptrs) list = append(list, textRecord{Key: sk, Value: sv}) } out = &textWrap{Prefix: "{", Value: list, Suffix: "}"} out = wrapTrunkReference(ptrRef, opts.PrintAddresses, out) return out case reflect.Ptr: if v.IsNil() { return textNil } // Check pointer for cycles. ptrRef, visited := ptrs.Push(v) if visited { out = makeLeafReference(ptrRef, opts.PrintAddresses) return &textWrap{Prefix: "&", Value: out} } defer ptrs.Pop() // Skip the name only if this is an unnamed pointer type. // Otherwise taking the address of a value does not reproduce // the named pointer type. if v.Type().Name() == "" { skipType = true // Let the underlying value print the type instead } out = opts.FormatValue(v.Elem(), t.Kind(), ptrs) out = wrapTrunkReference(ptrRef, opts.PrintAddresses, out) out = &textWrap{Prefix: "&", Value: out} return out case reflect.Interface: if v.IsNil() { return textNil } // Interfaces accept different concrete types, // so configure the underlying value to explicitly print the type. return opts.WithTypeMode(emitType).FormatValue(v.Elem(), t.Kind(), ptrs) default: panic(fmt.Sprintf("%v kind not handled", v.Kind())) } } func (opts formatOptions) formatString(prefix, s string) textNode { maxLen := len(s) maxLines := strings.Count(s, "\n") + 1 if opts.LimitVerbosity { maxLen = (1 << opts.verbosity()) << 5 // 32, 64, 128, 256, etc... maxLines = (1 << opts.verbosity()) << 2 // 4, 8, 16, 32, 64, etc... } // For multiline strings, use the triple-quote syntax, // but only use it when printing removed or inserted nodes since // we only want the extra verbosity for those cases. lines := strings.Split(strings.TrimSuffix(s, "\n"), "\n") isTripleQuoted := len(lines) >= 4 && (opts.DiffMode == '-' || opts.DiffMode == '+') for i := 0; i < len(lines) && isTripleQuoted; i++ { lines[i] = strings.TrimPrefix(strings.TrimSuffix(lines[i], "\r"), "\r") // trim leading/trailing carriage returns for legacy Windows endline support isPrintable := func(r rune) bool { return unicode.IsPrint(r) || r == '\t' // specially treat tab as printable } line := lines[i] isTripleQuoted = !strings.HasPrefix(strings.TrimPrefix(line, prefix), `"""`) && !strings.HasPrefix(line, "...") && strings.TrimFunc(line, isPrintable) == "" && len(line) <= maxLen } if isTripleQuoted { var list textList list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(prefix + `"""`), ElideComma: true}) for i, line := range lines { if numElided := len(lines) - i; i == maxLines-1 && numElided > 1 { comment := commentString(fmt.Sprintf("%d elided lines", numElided)) list = append(list, textRecord{Diff: opts.DiffMode, Value: textEllipsis, ElideComma: true, Comment: comment}) break } list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(line), ElideComma: true}) } list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(prefix + `"""`), ElideComma: true}) return &textWrap{Prefix: "(", Value: list, Suffix: ")"} } // Format the string as a single-line quoted string. if len(s) > maxLen+len(textEllipsis) { return textLine(prefix + formatString(s[:maxLen]) + string(textEllipsis)) } return textLine(prefix + formatString(s)) } // formatMapKey formats v as if it were a map key. // The result is guaranteed to be a single line. func formatMapKey(v reflect.Value, disambiguate bool, ptrs *pointerReferences) string { var opts formatOptions opts.DiffMode = diffIdentical opts.TypeMode = elideType opts.PrintAddresses = disambiguate opts.AvoidStringer = disambiguate opts.QualifiedNames = disambiguate opts.VerbosityLevel = maxVerbosityPreset opts.LimitVerbosity = true s := opts.FormatValue(v, reflect.Map, ptrs).String() return strings.TrimSpace(s) } // formatString prints s as a double-quoted or backtick-quoted string. func formatString(s string) string { // Use quoted string if it the same length as a raw string literal. // Otherwise, attempt to use the raw string form. qs := strconv.Quote(s) if len(qs) == 1+len(s)+1 { return qs } // Disallow newlines to ensure output is a single line. // Only allow printable runes for readability purposes. rawInvalid := func(r rune) bool { return r == '`' || r == '\n' || !(unicode.IsPrint(r) || r == '\t') } if utf8.ValidString(s) && strings.IndexFunc(s, rawInvalid) < 0 { return "`" + s + "`" } return qs } // formatHex prints u as a hexadecimal integer in Go notation. func formatHex(u uint64) string { var f string switch { case u <= 0xff: f = "0x%02x" case u <= 0xffff: f = "0x%04x" case u <= 0xffffff: f = "0x%06x" case u <= 0xffffffff: f = "0x%08x" case u <= 0xffffffffff: f = "0x%010x" case u <= 0xffffffffffff: f = "0x%012x" case u <= 0xffffffffffffff: f = "0x%014x" case u <= 0xffffffffffffffff: f = "0x%016x" } return fmt.Sprintf(f, u) } ================================================ FILE: vendor/github.com/google/go-cmp/cmp/report_slices.go ================================================ // Copyright 2019, The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package cmp import ( "bytes" "fmt" "math" "reflect" "strconv" "strings" "unicode" "unicode/utf8" "github.com/google/go-cmp/cmp/internal/diff" ) // CanFormatDiffSlice reports whether we support custom formatting for nodes // that are slices of primitive kinds or strings. func (opts formatOptions) CanFormatDiffSlice(v *valueNode) bool { switch { case opts.DiffMode != diffUnknown: return false // Must be formatting in diff mode case v.NumDiff == 0: return false // No differences detected case !v.ValueX.IsValid() || !v.ValueY.IsValid(): return false // Both values must be valid case v.NumIgnored > 0: return false // Some ignore option was used case v.NumTransformed > 0: return false // Some transform option was used case v.NumCompared > 1: return false // More than one comparison was used case v.NumCompared == 1 && v.Type.Name() != "": // The need for cmp to check applicability of options on every element // in a slice is a significant performance detriment for large []byte. // The workaround is to specify Comparer(bytes.Equal), // which enables cmp to compare []byte more efficiently. // If they differ, we still want to provide batched diffing. // The logic disallows named types since they tend to have their own // String method, with nicer formatting than what this provides. return false } // Check whether this is an interface with the same concrete types. t := v.Type vx, vy := v.ValueX, v.ValueY if t.Kind() == reflect.Interface && !vx.IsNil() && !vy.IsNil() && vx.Elem().Type() == vy.Elem().Type() { vx, vy = vx.Elem(), vy.Elem() t = vx.Type() } // Check whether we provide specialized diffing for this type. switch t.Kind() { case reflect.String: case reflect.Array, reflect.Slice: // Only slices of primitive types have specialized handling. switch t.Elem().Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: default: return false } // Both slice values have to be non-empty. if t.Kind() == reflect.Slice && (vx.Len() == 0 || vy.Len() == 0) { return false } // If a sufficient number of elements already differ, // use specialized formatting even if length requirement is not met. if v.NumDiff > v.NumSame { return true } default: return false } // Use specialized string diffing for longer slices or strings. const minLength = 32 return vx.Len() >= minLength && vy.Len() >= minLength } // FormatDiffSlice prints a diff for the slices (or strings) represented by v. // This provides custom-tailored logic to make printing of differences in // textual strings and slices of primitive kinds more readable. func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode { assert(opts.DiffMode == diffUnknown) t, vx, vy := v.Type, v.ValueX, v.ValueY if t.Kind() == reflect.Interface { vx, vy = vx.Elem(), vy.Elem() t = vx.Type() opts = opts.WithTypeMode(emitType) } // Auto-detect the type of the data. var sx, sy string var ssx, ssy []string var isString, isMostlyText, isPureLinedText, isBinary bool switch { case t.Kind() == reflect.String: sx, sy = vx.String(), vy.String() isString = true case t.Kind() == reflect.Slice && t.Elem() == byteType: sx, sy = string(vx.Bytes()), string(vy.Bytes()) isString = true case t.Kind() == reflect.Array: // Arrays need to be addressable for slice operations to work. vx2, vy2 := reflect.New(t).Elem(), reflect.New(t).Elem() vx2.Set(vx) vy2.Set(vy) vx, vy = vx2, vy2 } if isString { var numTotalRunes, numValidRunes, numLines, lastLineIdx, maxLineLen int for i, r := range sx + sy { numTotalRunes++ if (unicode.IsPrint(r) || unicode.IsSpace(r)) && r != utf8.RuneError { numValidRunes++ } if r == '\n' { if maxLineLen < i-lastLineIdx { maxLineLen = i - lastLineIdx } lastLineIdx = i + 1 numLines++ } } isPureText := numValidRunes == numTotalRunes isMostlyText = float64(numValidRunes) > math.Floor(0.90*float64(numTotalRunes)) isPureLinedText = isPureText && numLines >= 4 && maxLineLen <= 1024 isBinary = !isMostlyText // Avoid diffing by lines if it produces a significantly more complex // edit script than diffing by bytes. if isPureLinedText { ssx = strings.Split(sx, "\n") ssy = strings.Split(sy, "\n") esLines := diff.Difference(len(ssx), len(ssy), func(ix, iy int) diff.Result { return diff.BoolResult(ssx[ix] == ssy[iy]) }) esBytes := diff.Difference(len(sx), len(sy), func(ix, iy int) diff.Result { return diff.BoolResult(sx[ix] == sy[iy]) }) efficiencyLines := float64(esLines.Dist()) / float64(len(esLines)) efficiencyBytes := float64(esBytes.Dist()) / float64(len(esBytes)) quotedLength := len(strconv.Quote(sx + sy)) unquotedLength := len(sx) + len(sy) escapeExpansionRatio := float64(quotedLength) / float64(unquotedLength) isPureLinedText = efficiencyLines < 4*efficiencyBytes || escapeExpansionRatio > 1.1 } } // Format the string into printable records. var list textList var delim string switch { // If the text appears to be multi-lined text, // then perform differencing across individual lines. case isPureLinedText: list = opts.formatDiffSlice( reflect.ValueOf(ssx), reflect.ValueOf(ssy), 1, "line", func(v reflect.Value, d diffMode) textRecord { s := formatString(v.Index(0).String()) return textRecord{Diff: d, Value: textLine(s)} }, ) delim = "\n" // If possible, use a custom triple-quote (""") syntax for printing // differences in a string literal. This format is more readable, // but has edge-cases where differences are visually indistinguishable. // This format is avoided under the following conditions: // - A line starts with `"""` // - A line starts with "..." // - A line contains non-printable characters // - Adjacent different lines differ only by whitespace // // For example: // // """ // ... // 3 identical lines // foo // bar // - baz // + BAZ // """ isTripleQuoted := true prevRemoveLines := map[string]bool{} prevInsertLines := map[string]bool{} var list2 textList list2 = append(list2, textRecord{Value: textLine(`"""`), ElideComma: true}) for _, r := range list { if !r.Value.Equal(textEllipsis) { line, _ := strconv.Unquote(string(r.Value.(textLine))) line = strings.TrimPrefix(strings.TrimSuffix(line, "\r"), "\r") // trim leading/trailing carriage returns for legacy Windows endline support normLine := strings.Map(func(r rune) rune { if unicode.IsSpace(r) { return -1 // drop whitespace to avoid visually indistinguishable output } return r }, line) isPrintable := func(r rune) bool { return unicode.IsPrint(r) || r == '\t' // specially treat tab as printable } isTripleQuoted = !strings.HasPrefix(line, `"""`) && !strings.HasPrefix(line, "...") && strings.TrimFunc(line, isPrintable) == "" switch r.Diff { case diffRemoved: isTripleQuoted = isTripleQuoted && !prevInsertLines[normLine] prevRemoveLines[normLine] = true case diffInserted: isTripleQuoted = isTripleQuoted && !prevRemoveLines[normLine] prevInsertLines[normLine] = true } if !isTripleQuoted { break } r.Value = textLine(line) r.ElideComma = true } if !(r.Diff == diffRemoved || r.Diff == diffInserted) { // start a new non-adjacent difference group prevRemoveLines = map[string]bool{} prevInsertLines = map[string]bool{} } list2 = append(list2, r) } if r := list2[len(list2)-1]; r.Diff == diffIdentical && len(r.Value.(textLine)) == 0 { list2 = list2[:len(list2)-1] // elide single empty line at the end } list2 = append(list2, textRecord{Value: textLine(`"""`), ElideComma: true}) if isTripleQuoted { var out textNode = &textWrap{Prefix: "(", Value: list2, Suffix: ")"} switch t.Kind() { case reflect.String: if t != stringType { out = opts.FormatType(t, out) } case reflect.Slice: // Always emit type for slices since the triple-quote syntax // looks like a string (not a slice). opts = opts.WithTypeMode(emitType) out = opts.FormatType(t, out) } return out } // If the text appears to be single-lined text, // then perform differencing in approximately fixed-sized chunks. // The output is printed as quoted strings. case isMostlyText: list = opts.formatDiffSlice( reflect.ValueOf(sx), reflect.ValueOf(sy), 64, "byte", func(v reflect.Value, d diffMode) textRecord { s := formatString(v.String()) return textRecord{Diff: d, Value: textLine(s)} }, ) // If the text appears to be binary data, // then perform differencing in approximately fixed-sized chunks. // The output is inspired by hexdump. case isBinary: list = opts.formatDiffSlice( reflect.ValueOf(sx), reflect.ValueOf(sy), 16, "byte", func(v reflect.Value, d diffMode) textRecord { var ss []string for i := 0; i < v.Len(); i++ { ss = append(ss, formatHex(v.Index(i).Uint())) } s := strings.Join(ss, ", ") comment := commentString(fmt.Sprintf("%c|%v|", d, formatASCII(v.String()))) return textRecord{Diff: d, Value: textLine(s), Comment: comment} }, ) // For all other slices of primitive types, // then perform differencing in approximately fixed-sized chunks. // The size of each chunk depends on the width of the element kind. default: var chunkSize int if t.Elem().Kind() == reflect.Bool { chunkSize = 16 } else { switch t.Elem().Bits() { case 8: chunkSize = 16 case 16: chunkSize = 12 case 32: chunkSize = 8 default: chunkSize = 8 } } list = opts.formatDiffSlice( vx, vy, chunkSize, t.Elem().Kind().String(), func(v reflect.Value, d diffMode) textRecord { var ss []string for i := 0; i < v.Len(); i++ { switch t.Elem().Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: ss = append(ss, fmt.Sprint(v.Index(i).Int())) case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64: ss = append(ss, fmt.Sprint(v.Index(i).Uint())) case reflect.Uint8, reflect.Uintptr: ss = append(ss, formatHex(v.Index(i).Uint())) case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: ss = append(ss, fmt.Sprint(v.Index(i).Interface())) } } s := strings.Join(ss, ", ") return textRecord{Diff: d, Value: textLine(s)} }, ) } // Wrap the output with appropriate type information. var out textNode = &textWrap{Prefix: "{", Value: list, Suffix: "}"} if !isMostlyText { // The "{...}" byte-sequence literal is not valid Go syntax for strings. // Emit the type for extra clarity (e.g. "string{...}"). if t.Kind() == reflect.String { opts = opts.WithTypeMode(emitType) } return opts.FormatType(t, out) } switch t.Kind() { case reflect.String: out = &textWrap{Prefix: "strings.Join(", Value: out, Suffix: fmt.Sprintf(", %q)", delim)} if t != stringType { out = opts.FormatType(t, out) } case reflect.Slice: out = &textWrap{Prefix: "bytes.Join(", Value: out, Suffix: fmt.Sprintf(", %q)", delim)} if t != bytesType { out = opts.FormatType(t, out) } } return out } // formatASCII formats s as an ASCII string. // This is useful for printing binary strings in a semi-legible way. func formatASCII(s string) string { b := bytes.Repeat([]byte{'.'}, len(s)) for i := 0; i < len(s); i++ { if ' ' <= s[i] && s[i] <= '~' { b[i] = s[i] } } return string(b) } func (opts formatOptions) formatDiffSlice( vx, vy reflect.Value, chunkSize int, name string, makeRec func(reflect.Value, diffMode) textRecord, ) (list textList) { eq := func(ix, iy int) bool { return vx.Index(ix).Interface() == vy.Index(iy).Interface() } es := diff.Difference(vx.Len(), vy.Len(), func(ix, iy int) diff.Result { return diff.BoolResult(eq(ix, iy)) }) appendChunks := func(v reflect.Value, d diffMode) int { n0 := v.Len() for v.Len() > 0 { n := chunkSize if n > v.Len() { n = v.Len() } list = append(list, makeRec(v.Slice(0, n), d)) v = v.Slice(n, v.Len()) } return n0 - v.Len() } var numDiffs int maxLen := -1 if opts.LimitVerbosity { maxLen = (1 << opts.verbosity()) << 2 // 4, 8, 16, 32, 64, etc... opts.VerbosityLevel-- } groups := coalesceAdjacentEdits(name, es) groups = coalesceInterveningIdentical(groups, chunkSize/4) groups = cleanupSurroundingIdentical(groups, eq) maxGroup := diffStats{Name: name} for i, ds := range groups { if maxLen >= 0 && numDiffs >= maxLen { maxGroup = maxGroup.Append(ds) continue } // Print equal. if ds.NumDiff() == 0 { // Compute the number of leading and trailing equal bytes to print. var numLo, numHi int numEqual := ds.NumIgnored + ds.NumIdentical for numLo < chunkSize*numContextRecords && numLo+numHi < numEqual && i != 0 { numLo++ } for numHi < chunkSize*numContextRecords && numLo+numHi < numEqual && i != len(groups)-1 { numHi++ } if numEqual-(numLo+numHi) <= chunkSize && ds.NumIgnored == 0 { numHi = numEqual - numLo // Avoid pointless coalescing of single equal row } // Print the equal bytes. appendChunks(vx.Slice(0, numLo), diffIdentical) if numEqual > numLo+numHi { ds.NumIdentical -= numLo + numHi list.AppendEllipsis(ds) } appendChunks(vx.Slice(numEqual-numHi, numEqual), diffIdentical) vx = vx.Slice(numEqual, vx.Len()) vy = vy.Slice(numEqual, vy.Len()) continue } // Print unequal. len0 := len(list) nx := appendChunks(vx.Slice(0, ds.NumIdentical+ds.NumRemoved+ds.NumModified), diffRemoved) vx = vx.Slice(nx, vx.Len()) ny := appendChunks(vy.Slice(0, ds.NumIdentical+ds.NumInserted+ds.NumModified), diffInserted) vy = vy.Slice(ny, vy.Len()) numDiffs += len(list) - len0 } if maxGroup.IsZero() { assert(vx.Len() == 0 && vy.Len() == 0) } else { list.AppendEllipsis(maxGroup) } return list } // coalesceAdjacentEdits coalesces the list of edits into groups of adjacent // equal or unequal counts. // // Example: // // Input: "..XXY...Y" // Output: [ // {NumIdentical: 2}, // {NumRemoved: 2, NumInserted 1}, // {NumIdentical: 3}, // {NumInserted: 1}, // ] func coalesceAdjacentEdits(name string, es diff.EditScript) (groups []diffStats) { var prevMode byte lastStats := func(mode byte) *diffStats { if prevMode != mode { groups = append(groups, diffStats{Name: name}) prevMode = mode } return &groups[len(groups)-1] } for _, e := range es { switch e { case diff.Identity: lastStats('=').NumIdentical++ case diff.UniqueX: lastStats('!').NumRemoved++ case diff.UniqueY: lastStats('!').NumInserted++ case diff.Modified: lastStats('!').NumModified++ } } return groups } // coalesceInterveningIdentical coalesces sufficiently short (<= windowSize) // equal groups into adjacent unequal groups that currently result in a // dual inserted/removed printout. This acts as a high-pass filter to smooth // out high-frequency changes within the windowSize. // // Example: // // WindowSize: 16, // Input: [ // {NumIdentical: 61}, // group 0 // {NumRemoved: 3, NumInserted: 1}, // group 1 // {NumIdentical: 6}, // ├── coalesce // {NumInserted: 2}, // ├── coalesce // {NumIdentical: 1}, // ├── coalesce // {NumRemoved: 9}, // └── coalesce // {NumIdentical: 64}, // group 2 // {NumRemoved: 3, NumInserted: 1}, // group 3 // {NumIdentical: 6}, // ├── coalesce // {NumInserted: 2}, // ├── coalesce // {NumIdentical: 1}, // ├── coalesce // {NumRemoved: 7}, // ├── coalesce // {NumIdentical: 1}, // ├── coalesce // {NumRemoved: 2}, // └── coalesce // {NumIdentical: 63}, // group 4 // ] // Output: [ // {NumIdentical: 61}, // {NumIdentical: 7, NumRemoved: 12, NumInserted: 3}, // {NumIdentical: 64}, // {NumIdentical: 8, NumRemoved: 12, NumInserted: 3}, // {NumIdentical: 63}, // ] func coalesceInterveningIdentical(groups []diffStats, windowSize int) []diffStats { groups, groupsOrig := groups[:0], groups for i, ds := range groupsOrig { if len(groups) >= 2 && ds.NumDiff() > 0 { prev := &groups[len(groups)-2] // Unequal group curr := &groups[len(groups)-1] // Equal group next := &groupsOrig[i] // Unequal group hadX, hadY := prev.NumRemoved > 0, prev.NumInserted > 0 hasX, hasY := next.NumRemoved > 0, next.NumInserted > 0 if ((hadX || hasX) && (hadY || hasY)) && curr.NumIdentical <= windowSize { *prev = prev.Append(*curr).Append(*next) groups = groups[:len(groups)-1] // Truncate off equal group continue } } groups = append(groups, ds) } return groups } // cleanupSurroundingIdentical scans through all unequal groups, and // moves any leading sequence of equal elements to the preceding equal group and // moves and trailing sequence of equal elements to the succeeding equal group. // // This is necessary since coalesceInterveningIdentical may coalesce edit groups // together such that leading/trailing spans of equal elements becomes possible. // Note that this can occur even with an optimal diffing algorithm. // // Example: // // Input: [ // {NumIdentical: 61}, // {NumIdentical: 1 , NumRemoved: 11, NumInserted: 2}, // assume 3 leading identical elements // {NumIdentical: 67}, // {NumIdentical: 7, NumRemoved: 12, NumInserted: 3}, // assume 10 trailing identical elements // {NumIdentical: 54}, // ] // Output: [ // {NumIdentical: 64}, // incremented by 3 // {NumRemoved: 9}, // {NumIdentical: 67}, // {NumRemoved: 9}, // {NumIdentical: 64}, // incremented by 10 // ] func cleanupSurroundingIdentical(groups []diffStats, eq func(i, j int) bool) []diffStats { var ix, iy int // indexes into sequence x and y for i, ds := range groups { // Handle equal group. if ds.NumDiff() == 0 { ix += ds.NumIdentical iy += ds.NumIdentical continue } // Handle unequal group. nx := ds.NumIdentical + ds.NumRemoved + ds.NumModified ny := ds.NumIdentical + ds.NumInserted + ds.NumModified var numLeadingIdentical, numTrailingIdentical int for j := 0; j < nx && j < ny && eq(ix+j, iy+j); j++ { numLeadingIdentical++ } for j := 0; j < nx && j < ny && eq(ix+nx-1-j, iy+ny-1-j); j++ { numTrailingIdentical++ } if numIdentical := numLeadingIdentical + numTrailingIdentical; numIdentical > 0 { if numLeadingIdentical > 0 { // Remove leading identical span from this group and // insert it into the preceding group. if i-1 >= 0 { groups[i-1].NumIdentical += numLeadingIdentical } else { // No preceding group exists, so prepend a new group, // but do so after we finish iterating over all groups. defer func() { groups = append([]diffStats{{Name: groups[0].Name, NumIdentical: numLeadingIdentical}}, groups...) }() } // Increment indexes since the preceding group would have handled this. ix += numLeadingIdentical iy += numLeadingIdentical } if numTrailingIdentical > 0 { // Remove trailing identical span from this group and // insert it into the succeeding group. if i+1 < len(groups) { groups[i+1].NumIdentical += numTrailingIdentical } else { // No succeeding group exists, so append a new group, // but do so after we finish iterating over all groups. defer func() { groups = append(groups, diffStats{Name: groups[len(groups)-1].Name, NumIdentical: numTrailingIdentical}) }() } // Do not increment indexes since the succeeding group will handle this. } // Update this group since some identical elements were removed. nx -= numIdentical ny -= numIdentical groups[i] = diffStats{Name: ds.Name, NumRemoved: nx, NumInserted: ny} } ix += nx iy += ny } return groups } ================================================ FILE: vendor/github.com/google/go-cmp/cmp/report_text.go ================================================ // Copyright 2019, The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package cmp import ( "bytes" "fmt" "math/rand" "strings" "time" "unicode/utf8" "github.com/google/go-cmp/cmp/internal/flags" ) var randBool = rand.New(rand.NewSource(time.Now().Unix())).Intn(2) == 0 const maxColumnLength = 80 type indentMode int func (n indentMode) appendIndent(b []byte, d diffMode) []byte { // The output of Diff is documented as being unstable to provide future // flexibility in changing the output for more humanly readable reports. // This logic intentionally introduces instability to the exact output // so that users can detect accidental reliance on stability early on, // rather than much later when an actual change to the format occurs. if flags.Deterministic || randBool { // Use regular spaces (U+0020). switch d { case diffUnknown, diffIdentical: b = append(b, " "...) case diffRemoved: b = append(b, "- "...) case diffInserted: b = append(b, "+ "...) } } else { // Use non-breaking spaces (U+00a0). switch d { case diffUnknown, diffIdentical: b = append(b, "  "...) case diffRemoved: b = append(b, "- "...) case diffInserted: b = append(b, "+ "...) } } return repeatCount(n).appendChar(b, '\t') } type repeatCount int func (n repeatCount) appendChar(b []byte, c byte) []byte { for ; n > 0; n-- { b = append(b, c) } return b } // textNode is a simplified tree-based representation of structured text. // Possible node types are textWrap, textList, or textLine. type textNode interface { // Len reports the length in bytes of a single-line version of the tree. // Nested textRecord.Diff and textRecord.Comment fields are ignored. Len() int // Equal reports whether the two trees are structurally identical. // Nested textRecord.Diff and textRecord.Comment fields are compared. Equal(textNode) bool // String returns the string representation of the text tree. // It is not guaranteed that len(x.String()) == x.Len(), // nor that x.String() == y.String() implies that x.Equal(y). String() string // formatCompactTo formats the contents of the tree as a single-line string // to the provided buffer. Any nested textRecord.Diff and textRecord.Comment // fields are ignored. // // However, not all nodes in the tree should be collapsed as a single-line. // If a node can be collapsed as a single-line, it is replaced by a textLine // node. Since the top-level node cannot replace itself, this also returns // the current node itself. // // This does not mutate the receiver. formatCompactTo([]byte, diffMode) ([]byte, textNode) // formatExpandedTo formats the contents of the tree as a multi-line string // to the provided buffer. In order for column alignment to operate well, // formatCompactTo must be called before calling formatExpandedTo. formatExpandedTo([]byte, diffMode, indentMode) []byte } // textWrap is a wrapper that concatenates a prefix and/or a suffix // to the underlying node. type textWrap struct { Prefix string // e.g., "bytes.Buffer{" Value textNode // textWrap | textList | textLine Suffix string // e.g., "}" Metadata interface{} // arbitrary metadata; has no effect on formatting } func (s *textWrap) Len() int { return len(s.Prefix) + s.Value.Len() + len(s.Suffix) } func (s1 *textWrap) Equal(s2 textNode) bool { if s2, ok := s2.(*textWrap); ok { return s1.Prefix == s2.Prefix && s1.Value.Equal(s2.Value) && s1.Suffix == s2.Suffix } return false } func (s *textWrap) String() string { var d diffMode var n indentMode _, s2 := s.formatCompactTo(nil, d) b := n.appendIndent(nil, d) // Leading indent b = s2.formatExpandedTo(b, d, n) // Main body b = append(b, '\n') // Trailing newline return string(b) } func (s *textWrap) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) { n0 := len(b) // Original buffer length b = append(b, s.Prefix...) b, s.Value = s.Value.formatCompactTo(b, d) b = append(b, s.Suffix...) if _, ok := s.Value.(textLine); ok { return b, textLine(b[n0:]) } return b, s } func (s *textWrap) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte { b = append(b, s.Prefix...) b = s.Value.formatExpandedTo(b, d, n) b = append(b, s.Suffix...) return b } // textList is a comma-separated list of textWrap or textLine nodes. // The list may be formatted as multi-lines or single-line at the discretion // of the textList.formatCompactTo method. type textList []textRecord type textRecord struct { Diff diffMode // e.g., 0 or '-' or '+' Key string // e.g., "MyField" Value textNode // textWrap | textLine ElideComma bool // avoid trailing comma Comment fmt.Stringer // e.g., "6 identical fields" } // AppendEllipsis appends a new ellipsis node to the list if none already // exists at the end. If cs is non-zero it coalesces the statistics with the // previous diffStats. func (s *textList) AppendEllipsis(ds diffStats) { hasStats := !ds.IsZero() if len(*s) == 0 || !(*s)[len(*s)-1].Value.Equal(textEllipsis) { if hasStats { *s = append(*s, textRecord{Value: textEllipsis, ElideComma: true, Comment: ds}) } else { *s = append(*s, textRecord{Value: textEllipsis, ElideComma: true}) } return } if hasStats { (*s)[len(*s)-1].Comment = (*s)[len(*s)-1].Comment.(diffStats).Append(ds) } } func (s textList) Len() (n int) { for i, r := range s { n += len(r.Key) if r.Key != "" { n += len(": ") } n += r.Value.Len() if i < len(s)-1 { n += len(", ") } } return n } func (s1 textList) Equal(s2 textNode) bool { if s2, ok := s2.(textList); ok { if len(s1) != len(s2) { return false } for i := range s1 { r1, r2 := s1[i], s2[i] if !(r1.Diff == r2.Diff && r1.Key == r2.Key && r1.Value.Equal(r2.Value) && r1.Comment == r2.Comment) { return false } } return true } return false } func (s textList) String() string { return (&textWrap{Prefix: "{", Value: s, Suffix: "}"}).String() } func (s textList) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) { s = append(textList(nil), s...) // Avoid mutating original // Determine whether we can collapse this list as a single line. n0 := len(b) // Original buffer length var multiLine bool for i, r := range s { if r.Diff == diffInserted || r.Diff == diffRemoved { multiLine = true } b = append(b, r.Key...) if r.Key != "" { b = append(b, ": "...) } b, s[i].Value = r.Value.formatCompactTo(b, d|r.Diff) if _, ok := s[i].Value.(textLine); !ok { multiLine = true } if r.Comment != nil { multiLine = true } if i < len(s)-1 { b = append(b, ", "...) } } // Force multi-lined output when printing a removed/inserted node that // is sufficiently long. if (d == diffInserted || d == diffRemoved) && len(b[n0:]) > maxColumnLength { multiLine = true } if !multiLine { return b, textLine(b[n0:]) } return b, s } func (s textList) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte { alignKeyLens := s.alignLens( func(r textRecord) bool { _, isLine := r.Value.(textLine) return r.Key == "" || !isLine }, func(r textRecord) int { return utf8.RuneCountInString(r.Key) }, ) alignValueLens := s.alignLens( func(r textRecord) bool { _, isLine := r.Value.(textLine) return !isLine || r.Value.Equal(textEllipsis) || r.Comment == nil }, func(r textRecord) int { return utf8.RuneCount(r.Value.(textLine)) }, ) // Format lists of simple lists in a batched form. // If the list is sequence of only textLine values, // then batch multiple values on a single line. var isSimple bool for _, r := range s { _, isLine := r.Value.(textLine) isSimple = r.Diff == 0 && r.Key == "" && isLine && r.Comment == nil if !isSimple { break } } if isSimple { n++ var batch []byte emitBatch := func() { if len(batch) > 0 { b = n.appendIndent(append(b, '\n'), d) b = append(b, bytes.TrimRight(batch, " ")...) batch = batch[:0] } } for _, r := range s { line := r.Value.(textLine) if len(batch)+len(line)+len(", ") > maxColumnLength { emitBatch() } batch = append(batch, line...) batch = append(batch, ", "...) } emitBatch() n-- return n.appendIndent(append(b, '\n'), d) } // Format the list as a multi-lined output. n++ for i, r := range s { b = n.appendIndent(append(b, '\n'), d|r.Diff) if r.Key != "" { b = append(b, r.Key+": "...) } b = alignKeyLens[i].appendChar(b, ' ') b = r.Value.formatExpandedTo(b, d|r.Diff, n) if !r.ElideComma { b = append(b, ',') } b = alignValueLens[i].appendChar(b, ' ') if r.Comment != nil { b = append(b, " // "+r.Comment.String()...) } } n-- return n.appendIndent(append(b, '\n'), d) } func (s textList) alignLens( skipFunc func(textRecord) bool, lenFunc func(textRecord) int, ) []repeatCount { var startIdx, endIdx, maxLen int lens := make([]repeatCount, len(s)) for i, r := range s { if skipFunc(r) { for j := startIdx; j < endIdx && j < len(s); j++ { lens[j] = repeatCount(maxLen - lenFunc(s[j])) } startIdx, endIdx, maxLen = i+1, i+1, 0 } else { if maxLen < lenFunc(r) { maxLen = lenFunc(r) } endIdx = i + 1 } } for j := startIdx; j < endIdx && j < len(s); j++ { lens[j] = repeatCount(maxLen - lenFunc(s[j])) } return lens } // textLine is a single-line segment of text and is always a leaf node // in the textNode tree. type textLine []byte var ( textNil = textLine("nil") textEllipsis = textLine("...") ) func (s textLine) Len() int { return len(s) } func (s1 textLine) Equal(s2 textNode) bool { if s2, ok := s2.(textLine); ok { return bytes.Equal([]byte(s1), []byte(s2)) } return false } func (s textLine) String() string { return string(s) } func (s textLine) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) { return append(b, s...), s } func (s textLine) formatExpandedTo(b []byte, _ diffMode, _ indentMode) []byte { return append(b, s...) } type diffStats struct { Name string NumIgnored int NumIdentical int NumRemoved int NumInserted int NumModified int } func (s diffStats) IsZero() bool { s.Name = "" return s == diffStats{} } func (s diffStats) NumDiff() int { return s.NumRemoved + s.NumInserted + s.NumModified } func (s diffStats) Append(ds diffStats) diffStats { assert(s.Name == ds.Name) s.NumIgnored += ds.NumIgnored s.NumIdentical += ds.NumIdentical s.NumRemoved += ds.NumRemoved s.NumInserted += ds.NumInserted s.NumModified += ds.NumModified return s } // String prints a humanly-readable summary of coalesced records. // // Example: // // diffStats{Name: "Field", NumIgnored: 5}.String() => "5 ignored fields" func (s diffStats) String() string { var ss []string var sum int labels := [...]string{"ignored", "identical", "removed", "inserted", "modified"} counts := [...]int{s.NumIgnored, s.NumIdentical, s.NumRemoved, s.NumInserted, s.NumModified} for i, n := range counts { if n > 0 { ss = append(ss, fmt.Sprintf("%d %v", n, labels[i])) } sum += n } // Pluralize the name (adjusting for some obscure English grammar rules). name := s.Name if sum > 1 { name += "s" if strings.HasSuffix(name, "ys") { name = name[:len(name)-2] + "ies" // e.g., "entrys" => "entries" } } // Format the list according to English grammar (with Oxford comma). switch n := len(ss); n { case 0: return "" case 1, 2: return strings.Join(ss, " and ") + " " + name default: return strings.Join(ss[:n-1], ", ") + ", and " + ss[n-1] + " " + name } } type commentString string func (s commentString) String() string { return string(s) } ================================================ FILE: vendor/github.com/google/go-cmp/cmp/report_value.go ================================================ // Copyright 2019, The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package cmp import "reflect" // valueNode represents a single node within a report, which is a // structured representation of the value tree, containing information // regarding which nodes are equal or not. type valueNode struct { parent *valueNode Type reflect.Type ValueX reflect.Value ValueY reflect.Value // NumSame is the number of leaf nodes that are equal. // All descendants are equal only if NumDiff is 0. NumSame int // NumDiff is the number of leaf nodes that are not equal. NumDiff int // NumIgnored is the number of leaf nodes that are ignored. NumIgnored int // NumCompared is the number of leaf nodes that were compared // using an Equal method or Comparer function. NumCompared int // NumTransformed is the number of non-leaf nodes that were transformed. NumTransformed int // NumChildren is the number of transitive descendants of this node. // This counts from zero; thus, leaf nodes have no descendants. NumChildren int // MaxDepth is the maximum depth of the tree. This counts from zero; // thus, leaf nodes have a depth of zero. MaxDepth int // Records is a list of struct fields, slice elements, or map entries. Records []reportRecord // If populated, implies Value is not populated // Value is the result of a transformation, pointer indirect, of // type assertion. Value *valueNode // If populated, implies Records is not populated // TransformerName is the name of the transformer. TransformerName string // If non-empty, implies Value is populated } type reportRecord struct { Key reflect.Value // Invalid for slice element Value *valueNode } func (parent *valueNode) PushStep(ps PathStep) (child *valueNode) { vx, vy := ps.Values() child = &valueNode{parent: parent, Type: ps.Type(), ValueX: vx, ValueY: vy} switch s := ps.(type) { case StructField: assert(parent.Value == nil) parent.Records = append(parent.Records, reportRecord{Key: reflect.ValueOf(s.Name()), Value: child}) case SliceIndex: assert(parent.Value == nil) parent.Records = append(parent.Records, reportRecord{Value: child}) case MapIndex: assert(parent.Value == nil) parent.Records = append(parent.Records, reportRecord{Key: s.Key(), Value: child}) case Indirect: assert(parent.Value == nil && parent.Records == nil) parent.Value = child case TypeAssertion: assert(parent.Value == nil && parent.Records == nil) parent.Value = child case Transform: assert(parent.Value == nil && parent.Records == nil) parent.Value = child parent.TransformerName = s.Name() parent.NumTransformed++ default: assert(parent == nil) // Must be the root step } return child } func (r *valueNode) Report(rs Result) { assert(r.MaxDepth == 0) // May only be called on leaf nodes if rs.ByIgnore() { r.NumIgnored++ } else { if rs.Equal() { r.NumSame++ } else { r.NumDiff++ } } assert(r.NumSame+r.NumDiff+r.NumIgnored == 1) if rs.ByMethod() { r.NumCompared++ } if rs.ByFunc() { r.NumCompared++ } assert(r.NumCompared <= 1) } func (child *valueNode) PopStep() (parent *valueNode) { if child.parent == nil { return nil } parent = child.parent parent.NumSame += child.NumSame parent.NumDiff += child.NumDiff parent.NumIgnored += child.NumIgnored parent.NumCompared += child.NumCompared parent.NumTransformed += child.NumTransformed parent.NumChildren += child.NumChildren + 1 if parent.MaxDepth < child.MaxDepth+1 { parent.MaxDepth = child.MaxDepth + 1 } return parent } ================================================ FILE: vendor/github.com/google/go-containerregistry/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/google/go-containerregistry/internal/and/and_closer.go ================================================ // Copyright 2020 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package and provides helpers for adding Close to io.{Reader|Writer}. package and import ( "io" ) // ReadCloser implements io.ReadCloser by reading from a particular io.Reader // and then calling the provided "Close()" method. type ReadCloser struct { io.Reader CloseFunc func() error } var _ io.ReadCloser = (*ReadCloser)(nil) // Close implements io.ReadCloser func (rac *ReadCloser) Close() error { return rac.CloseFunc() } // WriteCloser implements io.WriteCloser by reading from a particular io.Writer // and then calling the provided "Close()" method. type WriteCloser struct { io.Writer CloseFunc func() error } var _ io.WriteCloser = (*WriteCloser)(nil) // Close implements io.WriteCloser func (wac *WriteCloser) Close() error { return wac.CloseFunc() } ================================================ FILE: vendor/github.com/google/go-containerregistry/internal/compression/compression.go ================================================ // Copyright 2022 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package compression abstracts over gzip and zstd. package compression import ( "bufio" "bytes" "io" "github.com/google/go-containerregistry/internal/gzip" "github.com/google/go-containerregistry/internal/zstd" "github.com/google/go-containerregistry/pkg/compression" ) // Opener represents e.g. opening a file. type Opener = func() (io.ReadCloser, error) // GetCompression detects whether an Opener is compressed and which algorithm is used. func GetCompression(opener Opener) (compression.Compression, error) { rc, err := opener() if err != nil { return compression.None, err } defer rc.Close() cp, _, err := PeekCompression(rc) if err != nil { return compression.None, err } return cp, nil } // PeekCompression detects whether the input stream is compressed and which algorithm is used. // // If r implements Peek, we will use that directly, otherwise a small number // of bytes are buffered to Peek at the gzip/zstd header, and the returned // PeekReader can be used as a replacement for the consumed input io.Reader. func PeekCompression(r io.Reader) (compression.Compression, PeekReader, error) { pr := intoPeekReader(r) if isGZip, _, err := checkHeader(pr, gzip.MagicHeader); err != nil { return compression.None, pr, err } else if isGZip { return compression.GZip, pr, nil } if isZStd, _, err := checkHeader(pr, zstd.MagicHeader); err != nil { return compression.None, pr, err } else if isZStd { return compression.ZStd, pr, nil } return compression.None, pr, nil } // PeekReader is an io.Reader that also implements Peek a la bufio.Reader. type PeekReader interface { io.Reader Peek(n int) ([]byte, error) } // IntoPeekReader creates a PeekReader from an io.Reader. // If the reader already has a Peek method, it will just return the passed reader. func intoPeekReader(r io.Reader) PeekReader { if p, ok := r.(PeekReader); ok { return p } return bufio.NewReader(r) } // CheckHeader checks whether the first bytes from a PeekReader match an expected header func checkHeader(pr PeekReader, expectedHeader []byte) (bool, PeekReader, error) { header, err := pr.Peek(len(expectedHeader)) if err != nil { // https://github.com/google/go-containerregistry/issues/367 if err == io.EOF { return false, pr, nil } return false, pr, err } return bytes.Equal(header, expectedHeader), pr, nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/internal/estargz/estargz.go ================================================ // Copyright 2020 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package estargz adapts the containerd estargz package to our abstractions. package estargz import ( "bytes" "io" "github.com/containerd/stargz-snapshotter/estargz" v1 "github.com/google/go-containerregistry/pkg/v1" ) // Assert that what we're returning is an io.ReadCloser var _ io.ReadCloser = (*estargz.Blob)(nil) // ReadCloser reads uncompressed tarball input from the io.ReadCloser and // returns: // - An io.ReadCloser from which compressed data may be read, and // - A v1.Hash with the hash of the estargz table of contents, or // - An error if the estargz processing encountered a problem. // // Refer to estargz for the options: // https://pkg.go.dev/github.com/containerd/stargz-snapshotter/estargz@v0.4.1#Option func ReadCloser(r io.ReadCloser, opts ...estargz.Option) (*estargz.Blob, v1.Hash, error) { defer r.Close() // TODO(#876): Avoid buffering into memory. bs, err := io.ReadAll(r) if err != nil { return nil, v1.Hash{}, err } br := bytes.NewReader(bs) rc, err := estargz.Build(io.NewSectionReader(br, 0, int64(len(bs))), opts...) if err != nil { return nil, v1.Hash{}, err } h, err := v1.NewHash(rc.TOCDigest().String()) return rc, h, err } ================================================ FILE: vendor/github.com/google/go-containerregistry/internal/gzip/zip.go ================================================ // Copyright 2020 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package gzip provides helper functions for interacting with gzipped streams. package gzip import ( "bufio" "bytes" "compress/gzip" "io" "github.com/google/go-containerregistry/internal/and" ) // MagicHeader is the start of gzip files. var MagicHeader = []byte{'\x1f', '\x8b'} // ReadCloser reads uncompressed input data from the io.ReadCloser and // returns an io.ReadCloser from which compressed data may be read. // This uses gzip.BestSpeed for the compression level. func ReadCloser(r io.ReadCloser) io.ReadCloser { return ReadCloserLevel(r, gzip.BestSpeed) } // ReadCloserLevel reads uncompressed input data from the io.ReadCloser and // returns an io.ReadCloser from which compressed data may be read. // Refer to compress/gzip for the level: // https://golang.org/pkg/compress/gzip/#pkg-constants func ReadCloserLevel(r io.ReadCloser, level int) io.ReadCloser { pr, pw := io.Pipe() // For highly compressible layers, gzip.Writer will output a very small // number of bytes per Write(). This is normally fine, but when pushing // to a registry, we want to ensure that we're taking full advantage of // the available bandwidth instead of sending tons of tiny writes over // the wire. // 64K ought to be small enough for anybody. bw := bufio.NewWriterSize(pw, 2<<16) // Returns err so we can pw.CloseWithError(err) go func() error { // TODO(go1.14): Just defer {pw,gw,r}.Close like you'd expect. // Context: https://golang.org/issue/24283 gw, err := gzip.NewWriterLevel(bw, level) if err != nil { return pw.CloseWithError(err) } if _, err := io.Copy(gw, r); err != nil { defer r.Close() defer gw.Close() return pw.CloseWithError(err) } // Close gzip writer to Flush it and write gzip trailers. if err := gw.Close(); err != nil { return pw.CloseWithError(err) } // Flush bufio writer to ensure we write out everything. if err := bw.Flush(); err != nil { return pw.CloseWithError(err) } // We don't really care if these fail. defer pw.Close() defer r.Close() return nil }() return pr } // UnzipReadCloser reads compressed input data from the io.ReadCloser and // returns an io.ReadCloser from which uncompressed data may be read. func UnzipReadCloser(r io.ReadCloser) (io.ReadCloser, error) { gr, err := gzip.NewReader(r) if err != nil { return nil, err } return &and.ReadCloser{ Reader: gr, CloseFunc: func() error { // If the unzip fails, then this seems to return the same // error as the read. We don't want this to interfere with // us closing the main ReadCloser, since this could leave // an open file descriptor (fails on Windows). gr.Close() return r.Close() }, }, nil } // Is detects whether the input stream is compressed. func Is(r io.Reader) (bool, error) { magicHeader := make([]byte, 2) n, err := r.Read(magicHeader) if n == 0 && err == io.EOF { return false, nil } if err != nil { return false, err } return bytes.Equal(magicHeader, MagicHeader), nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/internal/httptest/httptest.go ================================================ // Copyright 2020 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package httptest provides a method for testing a TLS server a la net/http/httptest. package httptest import ( "bytes" "context" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/tls" "crypto/x509" "encoding/pem" "math/big" "net" "net/http" "net/http/httptest" "time" ) // NewTLSServer returns an httptest server, with an http client that has been configured to // send all requests to the returned server. The TLS certs are generated for the given domain. // If you need a transport, Client().Transport is correctly configured. func NewTLSServer(domain string, handler http.Handler) (*httptest.Server, error) { s := httptest.NewUnstartedServer(handler) template := x509.Certificate{ SerialNumber: big.NewInt(1), NotBefore: time.Now().Add(-1 * time.Hour), NotAfter: time.Now().Add(time.Hour), IPAddresses: []net.IP{ net.IPv4(127, 0, 0, 1), net.IPv6loopback, }, DNSNames: []string{domain}, KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, BasicConstraintsValid: true, IsCA: true, } priv, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) if err != nil { return nil, err } b, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) if err != nil { return nil, err } pc := &bytes.Buffer{} if err := pem.Encode(pc, &pem.Block{Type: "CERTIFICATE", Bytes: b}); err != nil { return nil, err } ek, err := x509.MarshalECPrivateKey(priv) if err != nil { return nil, err } pk := &bytes.Buffer{} if err := pem.Encode(pk, &pem.Block{Type: "EC PRIVATE KEY", Bytes: ek}); err != nil { return nil, err } c, err := tls.X509KeyPair(pc.Bytes(), pk.Bytes()) if err != nil { return nil, err } s.TLS = &tls.Config{ Certificates: []tls.Certificate{c}, } s.StartTLS() certpool := x509.NewCertPool() certpool.AddCert(s.Certificate()) t := &http.Transport{ TLSClientConfig: &tls.Config{ RootCAs: certpool, }, DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { return net.Dial(s.Listener.Addr().Network(), s.Listener.Addr().String()) }, } s.Client().Transport = t return s, nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/internal/redact/redact.go ================================================ // Copyright 2020 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package redact contains a simple context signal for redacting requests. package redact import ( "context" "errors" "net/url" ) type contextKey string var redactKey = contextKey("redact") // NewContext creates a new ctx with the reason for redaction. func NewContext(ctx context.Context, reason string) context.Context { return context.WithValue(ctx, redactKey, reason) } // FromContext returns the redaction reason, if any. func FromContext(ctx context.Context) (bool, string) { reason, ok := ctx.Value(redactKey).(string) return ok, reason } // Error redacts potentially sensitive query parameter values in the URL from the error's message. // // If the error is a *url.Error, this returns a *url.Error with the URL redacted. // Any other error type, or nil, is returned unchanged. func Error(err error) error { // If the error is a url.Error, we can redact the URL. // Otherwise (including if err is nil), we can't redact. var uerr *url.Error if ok := errors.As(err, &uerr); !ok { return err } u, perr := url.Parse(uerr.URL) if perr != nil { return err // If the URL can't be parsed, just return the original error. } uerr.URL = URL(u).String() // Update the URL to the redacted URL. return uerr } // The set of query string keys that we expect to send as part of the registry // protocol. Anything else is potentially dangerous to leak, as it's probably // from a redirect. These redirects often included tokens or signed URLs. var paramAllowlist = map[string]struct{}{ // Token exchange "scope": {}, "service": {}, // Cross-repo mounting "mount": {}, "from": {}, // Layer PUT "digest": {}, // Listing tags and catalog "n": {}, "last": {}, } // URL redacts potentially sensitive query parameter values from the URL's query string. func URL(u *url.URL) *url.URL { qs := u.Query() for k, v := range qs { for i := range v { if _, ok := paramAllowlist[k]; !ok { // key is not in the Allowlist v[i] = "REDACTED" } } } r := *u r.RawQuery = qs.Encode() return &r } ================================================ FILE: vendor/github.com/google/go-containerregistry/internal/retry/retry.go ================================================ // Copyright 2019 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package retry provides methods for retrying operations. It is a thin wrapper // around k8s.io/apimachinery/pkg/util/wait to make certain operations easier. package retry import ( "context" "errors" "fmt" "github.com/google/go-containerregistry/internal/retry/wait" ) // Backoff is an alias of our own wait.Backoff to avoid name conflicts with // the kubernetes wait package. Typing retry.Backoff is aesier than fixing // the wrong import every time you use wait.Backoff. type Backoff = wait.Backoff // This is implemented by several errors in the net package as well as our // transport.Error. type temporary interface { Temporary() bool } // IsTemporary returns true if err implements Temporary() and it returns true. func IsTemporary(err error) bool { if errors.Is(err, context.DeadlineExceeded) { return false } if te, ok := err.(temporary); ok && te.Temporary() { return true } return false } // IsNotNil returns true if err is not nil. func IsNotNil(err error) bool { return err != nil } // Predicate determines whether an error should be retried. type Predicate func(error) (retry bool) // Retry retries a given function, f, until a predicate is satisfied, using // exponential backoff. If the predicate is never satisfied, it will return the // last error returned by f. func Retry(f func() error, p Predicate, backoff wait.Backoff) (err error) { if f == nil { return fmt.Errorf("nil f passed to retry") } if p == nil { return fmt.Errorf("nil p passed to retry") } condition := func() (bool, error) { err = f() if p(err) { return false, nil } return true, err } wait.ExponentialBackoff(backoff, condition) return } type contextKey string var key = contextKey("never") // Never returns a context that signals something should not be retried. // This is a hack and can be used to communicate across package boundaries // to avoid retry amplification. func Never(ctx context.Context) context.Context { return context.WithValue(ctx, key, true) } // Ever returns true if the context was wrapped by Never. func Ever(ctx context.Context) bool { return ctx.Value(key) == nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/internal/retry/wait/kubernetes_apimachinery_wait.go ================================================ /* Copyright 2014 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // Package wait is a subset of k8s.io/apimachinery to avoid conflicts // in dependencies (specifically, logging). package wait import ( "errors" "math/rand" "time" ) // Jitter returns a time.Duration between duration and duration + maxFactor * // duration. // // This allows clients to avoid converging on periodic behavior. If maxFactor // is 0.0, a suggested default value will be chosen. func Jitter(duration time.Duration, maxFactor float64) time.Duration { if maxFactor <= 0.0 { maxFactor = 1.0 } wait := duration + time.Duration(rand.Float64()*maxFactor*float64(duration)) return wait } // ErrWaitTimeout is returned when the condition exited without success. var ErrWaitTimeout = errors.New("timed out waiting for the condition") // ConditionFunc returns true if the condition is satisfied, or an error // if the loop should be aborted. type ConditionFunc func() (done bool, err error) // Backoff holds parameters applied to a Backoff function. type Backoff struct { // The initial duration. Duration time.Duration // Duration is multiplied by factor each iteration, if factor is not zero // and the limits imposed by Steps and Cap have not been reached. // Should not be negative. // The jitter does not contribute to the updates to the duration parameter. Factor float64 // The sleep at each iteration is the duration plus an additional // amount chosen uniformly at random from the interval between // zero and `jitter*duration`. Jitter float64 // The remaining number of iterations in which the duration // parameter may change (but progress can be stopped earlier by // hitting the cap). If not positive, the duration is not // changed. Used for exponential backoff in combination with // Factor and Cap. Steps int // A limit on revised values of the duration parameter. If a // multiplication by the factor parameter would make the duration // exceed the cap then the duration is set to the cap and the // steps parameter is set to zero. Cap time.Duration } // Step (1) returns an amount of time to sleep determined by the // original Duration and Jitter and (2) mutates the provided Backoff // to update its Steps and Duration. func (b *Backoff) Step() time.Duration { if b.Steps < 1 { if b.Jitter > 0 { return Jitter(b.Duration, b.Jitter) } return b.Duration } b.Steps-- duration := b.Duration // calculate the next step if b.Factor != 0 { b.Duration = time.Duration(float64(b.Duration) * b.Factor) if b.Cap > 0 && b.Duration > b.Cap { b.Duration = b.Cap b.Steps = 0 } } if b.Jitter > 0 { duration = Jitter(duration, b.Jitter) } return duration } // ExponentialBackoff repeats a condition check with exponential backoff. // // It repeatedly checks the condition and then sleeps, using `backoff.Step()` // to determine the length of the sleep and adjust Duration and Steps. // Stops and returns as soon as: // 1. the condition check returns true or an error, // 2. `backoff.Steps` checks of the condition have been done, or // 3. a sleep truncated by the cap on duration has been completed. // In case (1) the returned error is what the condition function returned. // In all other cases, ErrWaitTimeout is returned. func ExponentialBackoff(backoff Backoff, condition ConditionFunc) error { for backoff.Steps > 0 { if ok, err := condition(); err != nil || ok { return err } if backoff.Steps == 1 { break } time.Sleep(backoff.Step()) } return ErrWaitTimeout } ================================================ FILE: vendor/github.com/google/go-containerregistry/internal/verify/verify.go ================================================ // Copyright 2020 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package verify provides a ReadCloser that verifies content matches the // expected hash values. package verify import ( "bytes" "encoding/hex" "errors" "fmt" "hash" "io" "github.com/google/go-containerregistry/internal/and" v1 "github.com/google/go-containerregistry/pkg/v1" ) // SizeUnknown is a sentinel value to indicate that the expected size is not known. const SizeUnknown = -1 type verifyReader struct { inner io.Reader hasher hash.Hash expected v1.Hash gotSize, wantSize int64 } // Error provides information about the failed hash verification. type Error struct { got string want v1.Hash gotSize int64 } func (v Error) Error() string { return fmt.Sprintf("error verifying %s checksum after reading %d bytes; got %q, want %q", v.want.Algorithm, v.gotSize, v.got, v.want) } // Read implements io.Reader func (vc *verifyReader) Read(b []byte) (int, error) { n, err := vc.inner.Read(b) vc.gotSize += int64(n) if err == io.EOF { if vc.wantSize != SizeUnknown && vc.gotSize != vc.wantSize { return n, fmt.Errorf("error verifying size; got %d, want %d", vc.gotSize, vc.wantSize) } got := hex.EncodeToString(vc.hasher.Sum(nil)) if want := vc.expected.Hex; got != want { return n, Error{ got: vc.expected.Algorithm + ":" + got, want: vc.expected, gotSize: vc.gotSize, } } } return n, err } // ReadCloser wraps the given io.ReadCloser to verify that its contents match // the provided v1.Hash before io.EOF is returned. // // The reader will only be read up to size bytes, to prevent resource // exhaustion. If EOF is returned before size bytes are read, an error is // returned. // // A size of SizeUnknown (-1) indicates disables size verification when the size // is unknown ahead of time. func ReadCloser(r io.ReadCloser, size int64, h v1.Hash) (io.ReadCloser, error) { w, err := v1.Hasher(h.Algorithm) if err != nil { return nil, err } r2 := io.TeeReader(r, w) // pass all writes to the hasher. if size != SizeUnknown { r2 = io.LimitReader(r2, size) // if we know the size, limit to that size. } return &and.ReadCloser{ Reader: &verifyReader{ inner: r2, hasher: w, expected: h, wantSize: size, }, CloseFunc: r.Close, }, nil } // Descriptor verifies that the embedded Data field matches the Size and Digest // fields of the given v1.Descriptor, returning an error if the Data field is // missing or if it contains incorrect data. func Descriptor(d v1.Descriptor) error { if d.Data == nil { return errors.New("error verifying descriptor; Data == nil") } h, sz, err := v1.SHA256(bytes.NewReader(d.Data)) if err != nil { return err } if h != d.Digest { return fmt.Errorf("error verifying Digest; got %q, want %q", h, d.Digest) } if sz != d.Size { return fmt.Errorf("error verifying Size; got %d, want %d", sz, d.Size) } return nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/internal/windows/windows.go ================================================ // Copyright 2021 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package windows import ( "archive/tar" "bytes" "errors" "fmt" "io" "path" "strings" "github.com/google/go-containerregistry/internal/gzip" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/tarball" ) // userOwnerAndGroupSID is a magic value needed to make the binary executable // in a Windows container. // // owner: BUILTIN/Users group: BUILTIN/Users ($sddlValue="O:BUG:BU") const userOwnerAndGroupSID = "AQAAgBQAAAAkAAAAAAAAAAAAAAABAgAAAAAABSAAAAAhAgAAAQIAAAAAAAUgAAAAIQIAAA==" // Windows returns a Layer that is converted to be pullable on Windows. func Windows(layer v1.Layer) (v1.Layer, error) { // TODO: do this lazily. layerReader, err := layer.Uncompressed() if err != nil { return nil, fmt.Errorf("getting layer: %w", err) } defer layerReader.Close() tarReader := tar.NewReader(layerReader) w := new(bytes.Buffer) tarWriter := tar.NewWriter(w) defer tarWriter.Close() for _, dir := range []string{"Files", "Hives"} { if err := tarWriter.WriteHeader(&tar.Header{ Name: dir, Typeflag: tar.TypeDir, // Use a fixed Mode, so that this isn't sensitive to the directory and umask // under which it was created. Additionally, windows can only set 0222, // 0444, or 0666, none of which are executable. Mode: 0555, Format: tar.FormatPAX, }); err != nil { return nil, fmt.Errorf("writing %s directory: %w", dir, err) } } for { header, err := tarReader.Next() if errors.Is(err, io.EOF) { break } if err != nil { return nil, fmt.Errorf("reading layer: %w", err) } if strings.HasPrefix(header.Name, "Files/") { return nil, fmt.Errorf("file path %q already suitable for Windows", header.Name) } header.Name = path.Join("Files", header.Name) header.Format = tar.FormatPAX // TODO: this seems to make the file executable on Windows; // only do this if the file should be executable. if header.PAXRecords == nil { header.PAXRecords = map[string]string{} } header.PAXRecords["MSWINDOWS.rawsd"] = userOwnerAndGroupSID if err := tarWriter.WriteHeader(header); err != nil { return nil, fmt.Errorf("writing tar header: %w", err) } if header.Typeflag == tar.TypeReg { if _, err = io.Copy(tarWriter, tarReader); err != nil { return nil, fmt.Errorf("writing layer file: %w", err) } } } if err := tarWriter.Close(); err != nil { return nil, err } b := w.Bytes() // gzip the contents, then create the layer opener := func() (io.ReadCloser, error) { return gzip.ReadCloser(io.NopCloser(bytes.NewReader(b))), nil } layer, err = tarball.LayerFromOpener(opener) if err != nil { return nil, fmt.Errorf("creating layer: %w", err) } return layer, nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/internal/zstd/zstd.go ================================================ // Copyright 2022 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package zstd provides helper functions for interacting with zstd streams. package zstd import ( "bufio" "bytes" "io" "github.com/google/go-containerregistry/internal/and" "github.com/klauspost/compress/zstd" ) // MagicHeader is the start of zstd files. var MagicHeader = []byte{'\x28', '\xb5', '\x2f', '\xfd'} // ReadCloser reads uncompressed input data from the io.ReadCloser and // returns an io.ReadCloser from which compressed data may be read. // This uses zstd level 1 for the compression. func ReadCloser(r io.ReadCloser) io.ReadCloser { return ReadCloserLevel(r, 1) } // ReadCloserLevel reads uncompressed input data from the io.ReadCloser and // returns an io.ReadCloser from which compressed data may be read. func ReadCloserLevel(r io.ReadCloser, level int) io.ReadCloser { pr, pw := io.Pipe() // For highly compressible layers, zstd.Writer will output a very small // number of bytes per Write(). This is normally fine, but when pushing // to a registry, we want to ensure that we're taking full advantage of // the available bandwidth instead of sending tons of tiny writes over // the wire. // 64K ought to be small enough for anybody. bw := bufio.NewWriterSize(pw, 2<<16) // Returns err so we can pw.CloseWithError(err) go func() error { // TODO(go1.14): Just defer {pw,zw,r}.Close like you'd expect. // Context: https://golang.org/issue/24283 zw, err := zstd.NewWriter(bw, zstd.WithEncoderLevel(zstd.EncoderLevelFromZstd(level))) if err != nil { return pw.CloseWithError(err) } if _, err := io.Copy(zw, r); err != nil { defer r.Close() defer zw.Close() return pw.CloseWithError(err) } // Close zstd writer to Flush it and write zstd trailers. if err := zw.Close(); err != nil { return pw.CloseWithError(err) } // Flush bufio writer to ensure we write out everything. if err := bw.Flush(); err != nil { return pw.CloseWithError(err) } // We don't really care if these fail. defer pw.Close() defer r.Close() return nil }() return pr } // UnzipReadCloser reads compressed input data from the io.ReadCloser and // returns an io.ReadCloser from which uncompressed data may be read. func UnzipReadCloser(r io.ReadCloser) (io.ReadCloser, error) { gr, err := zstd.NewReader(r) if err != nil { return nil, err } return &and.ReadCloser{ Reader: gr, CloseFunc: func() error { // If the unzip fails, then this seems to return the same // error as the read. We don't want this to interfere with // us closing the main ReadCloser, since this could leave // an open file descriptor (fails on Windows). gr.Close() return r.Close() }, }, nil } // Is detects whether the input stream is compressed. func Is(r io.Reader) (bool, error) { magicHeader := make([]byte, 4) n, err := r.Read(magicHeader) if n == 0 && err == io.EOF { return false, nil } if err != nil { return false, err } return bytes.Equal(magicHeader, MagicHeader), nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/authn/README.md ================================================ # `authn` [![GoDoc](https://godoc.org/github.com/google/go-containerregistry/pkg/authn?status.svg)](https://godoc.org/github.com/google/go-containerregistry/pkg/authn) This README outlines how we acquire and use credentials when interacting with a registry. As much as possible, we attempt to emulate `docker`'s authentication behavior and configuration so that this library "just works" if you've already configured credentials that work with `docker`; however, when things don't work, a basic understanding of what's going on can help with debugging. The official documentation for how authentication with `docker` works is (reasonably) scattered across several different sites and GitHub repositories, so we've tried to summarize the relevant bits here. ## tl;dr for consumers of this package By default, [`pkg/v1/remote`](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/remote) uses [`Anonymous`](https://godoc.org/github.com/google/go-containerregistry/pkg/authn#Anonymous) credentials (i.e. _none_), which for most registries will only allow read access to public images. To use the credentials found in your Docker config file, you can use the [`DefaultKeychain`](https://godoc.org/github.com/google/go-containerregistry/pkg/authn#DefaultKeychain), e.g.: ```go package main import ( "fmt" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote" ) func main() { ref, err := name.ParseReference("registry.example.com/private/repo") if err != nil { panic(err) } // Fetch the manifest using default credentials. img, err := remote.Get(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain)) if err != nil { panic(err) } // Prints the digest of registry.example.com/private/repo fmt.Println(img.Digest) } ``` The `DefaultKeychain` will use credentials as described in your Docker config file -- usually `~/.docker/config.json`, or `%USERPROFILE%\.docker\config.json` on Windows -- or the location described by the `DOCKER_CONFIG` environment variable, if set. If those are not found, `DefaultKeychain` will look for credentials configured using [Podman's expectation](https://docs.podman.io/en/latest/markdown/podman-login.1.html) that these are found in `${XDG_RUNTIME_DIR}/containers/auth.json`. [See below](#docker-config-auth) for more information about what is configured in this file. ## Emulating Cloud Provider Credential Helpers [`pkg/v1/google.Keychain`](https://pkg.go.dev/github.com/google/go-containerregistry/pkg/v1/google#Keychain) provides a `Keychain` implementation that emulates [`docker-credential-gcr`](https://github.com/GoogleCloudPlatform/docker-credential-gcr) to find credentials in the environment. See [`google.NewEnvAuthenticator`](https://pkg.go.dev/github.com/google/go-containerregistry/pkg/v1/google#NewEnvAuthenticator) and [`google.NewGcloudAuthenticator`](https://pkg.go.dev/github.com/google/go-containerregistry/pkg/v1/google#NewGcloudAuthenticator) for more information. To emulate other credential helpers without requiring them to be available as executables, [`NewKeychainFromHelper`](https://pkg.go.dev/github.com/google/go-containerregistry/pkg/authn#NewKeychainFromHelper) provides an adapter that takes a Go implementation satisfying a subset of the [`credentials.Helper`](https://pkg.go.dev/github.com/docker/docker-credential-helpers/credentials#Helper) interface, and makes it available as a `Keychain`. This means that you can emulate, for example, [Amazon ECR's `docker-credential-ecr-login` credential helper](https://github.com/awslabs/amazon-ecr-credential-helper) using the same implementation: ```go import ( ecr "github.com/awslabs/amazon-ecr-credential-helper/ecr-login" "github.com/awslabs/amazon-ecr-credential-helper/ecr-login/api" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/v1/remote" ) func main() { // ... ecrHelper := ecr.ECRHelper{ClientFactory: api.DefaultClientFactory{}} img, err := remote.Get(ref, remote.WithAuthFromKeychain(authn.NewKeychainFromHelper(ecrHelper))) if err != nil { panic(err) } // ... } ``` Likewise, you can emulate [Azure's ACR `docker-credential-acr-env` credential helper](https://github.com/chrismellard/docker-credential-acr-env): ```go import ( "github.com/chrismellard/docker-credential-acr-env/pkg/credhelper" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/v1/remote" ) func main() { // ... acrHelper := credhelper.NewACRCredentialsHelper() img, err := remote.Get(ref, remote.WithAuthFromKeychain(authn.NewKeychainFromHelper(acrHelper))) if err != nil { panic(err) } // ... } ``` ## Using Multiple `Keychain`s [`NewMultiKeychain`](https://pkg.go.dev/github.com/google/go-containerregistry/pkg/authn#NewMultiKeychain) allows you to specify multiple `Keychain` implementations, which will be checked in order when credentials are needed. For example: ```go kc := authn.NewMultiKeychain( authn.DefaultKeychain, google.Keychain, authn.NewKeychainFromHelper(ecr.ECRHelper{ClientFactory: api.DefaultClientFactory{}}), authn.NewKeychainFromHelper(acr.ACRCredHelper{}), ) ``` This multi-keychain will: - first check for credentials found in the Docker config file, as describe above, then - check for GCP credentials available in the environment, as described above, then - check for ECR credentials by emulating the ECR credential helper, then - check for ACR credentials by emulating the ACR credential helper. If any keychain implementation is able to provide credentials for the request, they will be used, and further keychain implementations will not be consulted. If no implementations are able to provide credentials, `Anonymous` credentials will be used. ## Docker Config Auth What follows attempts to gather useful information about Docker's config.json and make it available in one place. If you have questions, please [file an issue](https://github.com/google/go-containerregistry/issues/new). ### Plaintext The config file is where your credentials are stored when you invoke `docker login`, e.g. the contents may look something like this: ```json { "auths": { "registry.example.com": { "auth": "QXp1cmVEaWFtb25kOmh1bnRlcjI=" } } } ``` The `auths` map has an entry per registry, and the `auth` field contains your username and password encoded as [HTTP 'Basic' Auth](https://tools.ietf.org/html/rfc7617). **NOTE**: This means that your credentials are stored _in plaintext_: ```bash $ echo "QXp1cmVEaWFtb25kOmh1bnRlcjI=" | base64 -d AzureDiamond:hunter2 ``` For what it's worth, this config file is equivalent to: ```json { "auths": { "registry.example.com": { "username": "AzureDiamond", "password": "hunter2" } } } ``` ... which is useful to know if e.g. your CI system provides you a registry username and password via environment variables and you want to populate this file manually without invoking `docker login`. ### Helpers If you log in like this, `docker` will warn you that you should use a [credential helper](https://docs.docker.com/engine/reference/commandline/login/#credentials-store), and you should! To configure a global credential helper: ```json { "credsStore": "osxkeychain" } ``` To configure a per-registry credential helper: ```json { "credHelpers": { "gcr.io": "gcr" } } ``` We use [`github.com/docker/cli/cli/config.Load`](https://godoc.org/github.com/docker/cli/cli/config#Load) to parse the config file and invoke any necessary credential helpers. This handles the logic of taking a [`ConfigFile`](https://github.com/docker/cli/blob/ba63a92655c0bea4857b8d6cc4991498858b3c60/cli/config/configfile/file.go#L25-L54) + registry domain and producing an [`AuthConfig`](https://github.com/docker/cli/blob/ba63a92655c0bea4857b8d6cc4991498858b3c60/cli/config/types/authconfig.go#L3-L22), which determines how we authenticate to the registry. ## Credential Helpers The [credential helper protocol](https://github.com/docker/docker-credential-helpers) allows you to configure a binary that supplies credentials for the registry, rather than hard-coding them in the config file. The protocol has several verbs, but the one we most care about is `get`. For example, using the following config file: ```json { "credHelpers": { "gcr.io": "gcr", "eu.gcr.io": "gcr" } } ``` To acquire credentials for `gcr.io`, we look in the `credHelpers` map to find the credential helper for `gcr.io` is `gcr`. By appending that value to `docker-credential-`, we can get the name of the binary we need to use. For this example, that's `docker-credential-gcr`, which must be on our `$PATH`. We'll then invoke that binary to get credentials: ```bash $ echo "gcr.io" | docker-credential-gcr get {"Username":"_token","Secret":""} ``` You can configure the same credential helper for multiple registries, which is why we need to pass the domain in via STDIN, e.g. if we were trying to access `eu.gcr.io`, we'd do this instead: ```bash $ echo "eu.gcr.io" | docker-credential-gcr get {"Username":"_token","Secret":""} ``` ### Debugging credential helpers If a credential helper is configured but doesn't seem to be working, it can be challenging to debug. Implementing a fake credential helper lets you poke around to make it easier to see where the failure is happening. This "implements" a credential helper with hard-coded values: ``` #!/usr/bin/env bash echo '{"Username":"","Secret":"hunter2"}' ``` This implements a credential helper that prints the output of `docker-credential-gcr` to both stderr and whatever called it, which allows you to snoop on another credential helper: ``` #!/usr/bin/env bash docker-credential-gcr $@ | tee >(cat 1>&2) ``` Put those files somewhere on your path, naming them e.g. `docker-credential-hardcoded` and `docker-credential-tee`, then modify the config file to use them: ```json { "credHelpers": { "gcr.io": "tee", "eu.gcr.io": "hardcoded" } } ``` The `docker-credential-tee` trick works with both `crane` and `docker`: ```bash $ crane manifest gcr.io/google-containers/pause > /dev/null {"ServerURL":"","Username":"_dcgcr_1_5_0_token","Secret":""} $ docker pull gcr.io/google-containers/pause Using default tag: latest {"ServerURL":"","Username":"_dcgcr_1_5_0_token","Secret":""} latest: Pulling from google-containers/pause a3ed95caeb02: Pull complete 4964c72cd024: Pull complete Digest: sha256:a78c2d6208eff9b672de43f880093100050983047b7b0afe0217d3656e1b0d5f Status: Downloaded newer image for gcr.io/google-containers/pause:latest gcr.io/google-containers/pause:latest ``` ## The Registry There are two methods for authenticating against a registry: [token](https://docs.docker.com/registry/spec/auth/token/) and [oauth2](https://docs.docker.com/registry/spec/auth/oauth/). Both methods are used to acquire an opaque `Bearer` token (or [RegistryToken](https://github.com/docker/cli/blob/ba63a92655c0bea4857b8d6cc4991498858b3c60/cli/config/types/authconfig.go#L21)) to use in the `Authorization` header. The registry will return a `401 Unauthorized` during the [version check](https://github.com/opencontainers/distribution-spec/blob/2c3975d1f03b67c9a0203199038adea0413f0573/spec.md#api-version-check) (or during normal operations) with [Www-Authenticate](https://tools.ietf.org/html/rfc7235#section-4.1) challenge indicating how to proceed. ### Token If we get back an `AuthConfig` containing a [`Username/Password`](https://github.com/docker/cli/blob/ba63a92655c0bea4857b8d6cc4991498858b3c60/cli/config/types/authconfig.go#L5-L6) or [`Auth`](https://github.com/docker/cli/blob/ba63a92655c0bea4857b8d6cc4991498858b3c60/cli/config/types/authconfig.go#L7), we'll use the token method for authentication: ![basic](../../images/credhelper-basic.svg) ### OAuth 2 If we get back an `AuthConfig` containing an [`IdentityToken`](https://github.com/docker/cli/blob/ba63a92655c0bea4857b8d6cc4991498858b3c60/cli/config/types/authconfig.go#L18) we'll use the oauth2 method for authentication: ![oauth](../../images/credhelper-oauth.svg) This happens when a credential helper returns a response with the [`Username`](https://github.com/docker/docker-credential-helpers/blob/f78081d1f7fef6ad74ad6b79368de6348386e591/credentials/credentials.go#L16) set to `` (no, that's not a placeholder, the literal string `""`). It is unclear why: [moby/moby#36926](https://github.com/moby/moby/issues/36926). We only support the oauth2 `grant_type` for `refresh_token` ([#629](https://github.com/google/go-containerregistry/issues/629)), since it's impossible to determine from the registry response whether we should use oauth, and the token method for authentication is widely implemented by registries. ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/authn/anon.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package authn // anonymous implements Authenticator for anonymous authentication. type anonymous struct{} // Authorization implements Authenticator. func (a *anonymous) Authorization() (*AuthConfig, error) { return &AuthConfig{}, nil } // Anonymous is a singleton Authenticator for providing anonymous auth. var Anonymous Authenticator = &anonymous{} ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/authn/auth.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package authn // auth is an Authenticator that simply returns the wrapped AuthConfig. type auth struct { config AuthConfig } // FromConfig returns an Authenticator that just returns the given AuthConfig. func FromConfig(cfg AuthConfig) Authenticator { return &auth{cfg} } // Authorization implements Authenticator. func (a *auth) Authorization() (*AuthConfig, error) { return &a.config, nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/authn/authn.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package authn import ( "encoding/base64" "encoding/json" "fmt" "strings" ) // Authenticator is used to authenticate Docker transports. type Authenticator interface { // Authorization returns the value to use in an http transport's Authorization header. Authorization() (*AuthConfig, error) } // AuthConfig contains authorization information for connecting to a Registry // Inlined what we use from github.com/docker/cli/cli/config/types type AuthConfig struct { Username string `json:"username,omitempty"` Password string `json:"password,omitempty"` Auth string `json:"auth,omitempty"` // IdentityToken is used to authenticate the user and get // an access token for the registry. IdentityToken string `json:"identitytoken,omitempty"` // RegistryToken is a bearer token to be sent to a registry RegistryToken string `json:"registrytoken,omitempty"` } // This is effectively a copy of the type AuthConfig. This simplifies // JSON unmarshalling since AuthConfig methods are not inherited type authConfig AuthConfig // UnmarshalJSON implements json.Unmarshaler func (a *AuthConfig) UnmarshalJSON(data []byte) error { var shadow authConfig err := json.Unmarshal(data, &shadow) if err != nil { return err } *a = (AuthConfig)(shadow) if len(shadow.Auth) != 0 { var derr error a.Username, a.Password, derr = decodeDockerConfigFieldAuth(shadow.Auth) if derr != nil { err = fmt.Errorf("unable to decode auth field: %w", derr) } } else if len(a.Username) != 0 && len(a.Password) != 0 { a.Auth = encodeDockerConfigFieldAuth(shadow.Username, shadow.Password) } return err } // MarshalJSON implements json.Marshaler func (a AuthConfig) MarshalJSON() ([]byte, error) { shadow := (authConfig)(a) shadow.Auth = encodeDockerConfigFieldAuth(shadow.Username, shadow.Password) return json.Marshal(shadow) } // decodeDockerConfigFieldAuth deserializes the "auth" field from dockercfg into a // username and a password. The format of the auth field is base64(:). // // From https://github.com/kubernetes/kubernetes/blob/75e49ec824b183288e1dbaccfd7dbe77d89db381/pkg/credentialprovider/config.go // Copyright 2014 The Kubernetes Authors. // SPDX-License-Identifier: Apache-2.0 func decodeDockerConfigFieldAuth(field string) (username, password string, err error) { var decoded []byte // StdEncoding can only decode padded string // RawStdEncoding can only decode unpadded string if strings.HasSuffix(strings.TrimSpace(field), "=") { // decode padded data decoded, err = base64.StdEncoding.DecodeString(field) } else { // decode unpadded data decoded, err = base64.RawStdEncoding.DecodeString(field) } if err != nil { return } parts := strings.SplitN(string(decoded), ":", 2) if len(parts) != 2 { err = fmt.Errorf("must be formatted as base64(username:password)") return } username = parts[0] password = parts[1] return } func encodeDockerConfigFieldAuth(username, password string) string { return base64.StdEncoding.EncodeToString([]byte(username + ":" + password)) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/authn/basic.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package authn // Basic implements Authenticator for basic authentication. type Basic struct { Username string Password string } // Authorization implements Authenticator. func (b *Basic) Authorization() (*AuthConfig, error) { return &AuthConfig{ Username: b.Username, Password: b.Password, }, nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/authn/bearer.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package authn // Bearer implements Authenticator for bearer authentication. type Bearer struct { Token string `json:"token"` } // Authorization implements Authenticator. func (b *Bearer) Authorization() (*AuthConfig, error) { return &AuthConfig{ RegistryToken: b.Token, }, nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/authn/doc.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package authn defines different methods of authentication for // talking to a container registry. package authn ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/authn/keychain.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package authn import ( "os" "path/filepath" "sync" "time" "github.com/docker/cli/cli/config" "github.com/docker/cli/cli/config/configfile" "github.com/docker/cli/cli/config/types" "github.com/google/go-containerregistry/pkg/name" "github.com/mitchellh/go-homedir" ) // Resource represents a registry or repository that can be authenticated against. type Resource interface { // String returns the full string representation of the target, e.g. // gcr.io/my-project or just gcr.io. String() string // RegistryStr returns just the registry portion of the target, e.g. for // gcr.io/my-project, this should just return gcr.io. This is needed to // pull out an appropriate hostname. RegistryStr() string } // Keychain is an interface for resolving an image reference to a credential. type Keychain interface { // Resolve looks up the most appropriate credential for the specified target. Resolve(Resource) (Authenticator, error) } // defaultKeychain implements Keychain with the semantics of the standard Docker // credential keychain. type defaultKeychain struct { mu sync.Mutex } var ( // DefaultKeychain implements Keychain by interpreting the docker config file. DefaultKeychain = &defaultKeychain{} ) const ( // DefaultAuthKey is the key used for dockerhub in config files, which // is hardcoded for historical reasons. DefaultAuthKey = "https://" + name.DefaultRegistry + "/v1/" ) // Resolve implements Keychain. func (dk *defaultKeychain) Resolve(target Resource) (Authenticator, error) { dk.mu.Lock() defer dk.mu.Unlock() // Podman users may have their container registry auth configured in a // different location, that Docker packages aren't aware of. // If the Docker config file isn't found, we'll fallback to look where // Podman configures it, and parse that as a Docker auth config instead. // First, check $HOME/.docker/config.json foundDockerConfig := false home, err := homedir.Dir() if err == nil { foundDockerConfig = fileExists(filepath.Join(home, ".docker/config.json")) } // If $HOME/.docker/config.json isn't found, check $DOCKER_CONFIG (if set) if !foundDockerConfig && os.Getenv("DOCKER_CONFIG") != "" { foundDockerConfig = fileExists(filepath.Join(os.Getenv("DOCKER_CONFIG"), "config.json")) } // If either of those locations are found, load it using Docker's // config.Load, which may fail if the config can't be parsed. // // If neither was found, look for Podman's auth at // $XDG_RUNTIME_DIR/containers/auth.json and attempt to load it as a // Docker config. // // If neither are found, fallback to Anonymous. var cf *configfile.ConfigFile if foundDockerConfig { cf, err = config.Load(os.Getenv("DOCKER_CONFIG")) if err != nil { return nil, err } } else { f, err := os.Open(filepath.Join(os.Getenv("XDG_RUNTIME_DIR"), "containers/auth.json")) if err != nil { return Anonymous, nil } defer f.Close() cf, err = config.LoadFromReader(f) if err != nil { return nil, err } } // See: // https://github.com/google/ko/issues/90 // https://github.com/moby/moby/blob/fc01c2b481097a6057bec3cd1ab2d7b4488c50c4/registry/config.go#L397-L404 var cfg, empty types.AuthConfig for _, key := range []string{ target.String(), target.RegistryStr(), } { if key == name.DefaultRegistry { key = DefaultAuthKey } cfg, err = cf.GetAuthConfig(key) if err != nil { return nil, err } // cf.GetAuthConfig automatically sets the ServerAddress attribute. Since // we don't make use of it, clear the value for a proper "is-empty" test. // See: https://github.com/google/go-containerregistry/issues/1510 cfg.ServerAddress = "" if cfg != empty { break } } if cfg == empty { return Anonymous, nil } return FromConfig(AuthConfig{ Username: cfg.Username, Password: cfg.Password, Auth: cfg.Auth, IdentityToken: cfg.IdentityToken, RegistryToken: cfg.RegistryToken, }), nil } // fileExists returns true if the given path exists and is not a directory. func fileExists(path string) bool { fi, err := os.Stat(path) return err == nil && !fi.IsDir() } // Helper is a subset of the Docker credential helper credentials.Helper // interface used by NewKeychainFromHelper. // // See: // https://pkg.go.dev/github.com/docker/docker-credential-helpers/credentials#Helper type Helper interface { Get(serverURL string) (string, string, error) } // NewKeychainFromHelper returns a Keychain based on a Docker credential helper // implementation that can Get username and password credentials for a given // server URL. func NewKeychainFromHelper(h Helper) Keychain { return wrapper{h} } type wrapper struct{ h Helper } func (w wrapper) Resolve(r Resource) (Authenticator, error) { u, p, err := w.h.Get(r.RegistryStr()) if err != nil { return Anonymous, nil } // If the secret being stored is an identity token, the Username should be set to // ref: https://docs.docker.com/engine/reference/commandline/login/#credential-helper-protocol if u == "" { return FromConfig(AuthConfig{Username: u, IdentityToken: p}), nil } return FromConfig(AuthConfig{Username: u, Password: p}), nil } func RefreshingKeychain(inner Keychain, duration time.Duration) Keychain { return &refreshingKeychain{ keychain: inner, duration: duration, } } type refreshingKeychain struct { keychain Keychain duration time.Duration clock func() time.Time } func (r *refreshingKeychain) Resolve(target Resource) (Authenticator, error) { last := time.Now() auth, err := r.keychain.Resolve(target) if err != nil || auth == Anonymous { return auth, err } return &refreshing{ target: target, keychain: r.keychain, last: last, cached: auth, duration: r.duration, clock: r.clock, }, nil } type refreshing struct { sync.Mutex target Resource keychain Keychain duration time.Duration last time.Time cached Authenticator // for testing clock func() time.Time } func (r *refreshing) Authorization() (*AuthConfig, error) { r.Lock() defer r.Unlock() if r.cached == nil || r.expired() { r.last = r.now() auth, err := r.keychain.Resolve(r.target) if err != nil { return nil, err } r.cached = auth } return r.cached.Authorization() } func (r *refreshing) now() time.Time { if r.clock == nil { return time.Now() } return r.clock() } func (r *refreshing) expired() bool { return r.now().Sub(r.last) > r.duration } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/authn/multikeychain.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package authn type multiKeychain struct { keychains []Keychain } // Assert that our multi-keychain implements Keychain. var _ (Keychain) = (*multiKeychain)(nil) // NewMultiKeychain composes a list of keychains into one new keychain. func NewMultiKeychain(kcs ...Keychain) Keychain { return &multiKeychain{keychains: kcs} } // Resolve implements Keychain. func (mk *multiKeychain) Resolve(target Resource) (Authenticator, error) { for _, kc := range mk.keychains { auth, err := kc.Resolve(target) if err != nil { return nil, err } if auth != Anonymous { return auth, nil } } return Anonymous, nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/compression/compression.go ================================================ // Copyright 2022 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package compression abstracts over gzip and zstd. package compression // Compression is an enumeration of the supported compression algorithms type Compression string // The collection of known MediaType values. const ( None Compression = "none" GZip Compression = "gzip" ZStd Compression = "zstd" ) ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/crane/append.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package crane import ( "fmt" "os" "github.com/google/go-containerregistry/internal/windows" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/mutate" "github.com/google/go-containerregistry/pkg/v1/stream" "github.com/google/go-containerregistry/pkg/v1/tarball" "github.com/google/go-containerregistry/pkg/v1/types" ) func isWindows(img v1.Image) (bool, error) { cfg, err := img.ConfigFile() if err != nil { return false, err } return cfg != nil && cfg.OS == "windows", nil } // Append reads a layer from path and appends it the the v1.Image base. // // If the base image is a Windows base image (i.e., its config.OS is // "windows"), the contents of the tarballs will be modified to be suitable for // a Windows container image.`, func Append(base v1.Image, paths ...string) (v1.Image, error) { if base == nil { return nil, fmt.Errorf("invalid argument: base") } win, err := isWindows(base) if err != nil { return nil, fmt.Errorf("getting base image: %w", err) } baseMediaType, err := base.MediaType() if err != nil { return nil, fmt.Errorf("getting base image media type: %w", err) } layerType := types.DockerLayer if baseMediaType == types.OCIManifestSchema1 { layerType = types.OCILayer } layers := make([]v1.Layer, 0, len(paths)) for _, path := range paths { layer, err := getLayer(path, layerType) if err != nil { return nil, fmt.Errorf("reading layer %q: %w", path, err) } if win { layer, err = windows.Windows(layer) if err != nil { return nil, fmt.Errorf("converting %q for Windows: %w", path, err) } } layers = append(layers, layer) } return mutate.AppendLayers(base, layers...) } func getLayer(path string, layerType types.MediaType) (v1.Layer, error) { f, err := streamFile(path) if err != nil { return nil, err } if f != nil { return stream.NewLayer(f, stream.WithMediaType(layerType)), nil } return tarball.LayerFromFile(path, tarball.WithMediaType(layerType)) } // If we're dealing with a named pipe, trying to open it multiple times will // fail, so we need to do a streaming upload. // // returns nil, nil for non-streaming files func streamFile(path string) (*os.File, error) { if path == "-" { return os.Stdin, nil } fi, err := os.Stat(path) if err != nil { return nil, err } if !fi.Mode().IsRegular() { return os.Open(path) } return nil, nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/crane/catalog.go ================================================ // Copyright 2019 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package crane import ( "context" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote" ) // Catalog returns the repositories in a registry's catalog. func Catalog(src string, opt ...Option) (res []string, err error) { o := makeOptions(opt...) reg, err := name.NewRegistry(src, o.Name...) if err != nil { return nil, err } // This context gets overridden by remote.WithContext, which is set by // crane.WithContext. return remote.Catalog(context.Background(), reg, o.Remote...) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/crane/config.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package crane // Config returns the config file for the remote image ref. func Config(ref string, opt ...Option) ([]byte, error) { i, _, err := getImage(ref, opt...) if err != nil { return nil, err } return i.RawConfigFile() } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/crane/copy.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package crane import ( "errors" "fmt" "net/http" "github.com/google/go-containerregistry/pkg/logs" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/google/go-containerregistry/pkg/v1/remote/transport" "golang.org/x/sync/errgroup" ) // Copy copies a remote image or index from src to dst. func Copy(src, dst string, opt ...Option) error { o := makeOptions(opt...) srcRef, err := name.ParseReference(src, o.Name...) if err != nil { return fmt.Errorf("parsing reference %q: %w", src, err) } dstRef, err := name.ParseReference(dst, o.Name...) if err != nil { return fmt.Errorf("parsing reference for %q: %w", dst, err) } puller, err := remote.NewPuller(o.Remote...) if err != nil { return err } if tag, ok := dstRef.(name.Tag); ok { if o.noclobber { logs.Progress.Printf("Checking existing tag %v", tag) head, err := puller.Head(o.ctx, tag) var terr *transport.Error if errors.As(err, &terr) { if terr.StatusCode != http.StatusNotFound && terr.StatusCode != http.StatusForbidden { return err } } else if err != nil { return err } if head != nil { return fmt.Errorf("refusing to clobber existing tag %s@%s", tag, head.Digest) } } } pusher, err := remote.NewPusher(o.Remote...) if err != nil { return err } logs.Progress.Printf("Copying from %v to %v", srcRef, dstRef) desc, err := puller.Get(o.ctx, srcRef) if err != nil { return fmt.Errorf("fetching %q: %w", src, err) } if o.Platform == nil { return pusher.Push(o.ctx, dstRef, desc) } // If platform is explicitly set, don't copy the whole index, just the appropriate image. img, err := desc.Image() if err != nil { return err } return pusher.Push(o.ctx, dstRef, img) } // CopyRepository copies every tag from src to dst. func CopyRepository(src, dst string, opt ...Option) error { o := makeOptions(opt...) srcRepo, err := name.NewRepository(src, o.Name...) if err != nil { return err } dstRepo, err := name.NewRepository(dst, o.Name...) if err != nil { return fmt.Errorf("parsing reference for %q: %w", dst, err) } puller, err := remote.NewPuller(o.Remote...) if err != nil { return err } ignoredTags := map[string]struct{}{} if o.noclobber { // TODO: It would be good to propagate noclobber down into remote so we can use Etags. have, err := puller.List(o.ctx, dstRepo) if err != nil { var terr *transport.Error if errors.As(err, &terr) { // Some registries create repository on first push, so listing tags will fail. // If we see 404 or 403, assume we failed because the repository hasn't been created yet. if !(terr.StatusCode == http.StatusNotFound || terr.StatusCode == http.StatusForbidden) { return err } } else { return err } } for _, tag := range have { ignoredTags[tag] = struct{}{} } } pusher, err := remote.NewPusher(o.Remote...) if err != nil { return err } lister, err := puller.Lister(o.ctx, srcRepo) if err != nil { return err } g, ctx := errgroup.WithContext(o.ctx) g.SetLimit(o.jobs) for lister.HasNext() { tags, err := lister.Next(ctx) if err != nil { return err } for _, tag := range tags.Tags { tag := tag if o.noclobber { if _, ok := ignoredTags[tag]; ok { logs.Progress.Printf("Skipping %s due to no-clobber", tag) continue } } g.Go(func() error { srcTag, err := name.ParseReference(src+":"+tag, o.Name...) if err != nil { return fmt.Errorf("failed to parse tag: %w", err) } dstTag, err := name.ParseReference(dst+":"+tag, o.Name...) if err != nil { return fmt.Errorf("failed to parse tag: %w", err) } logs.Progress.Printf("Fetching %s", srcTag) desc, err := puller.Get(ctx, srcTag) if err != nil { return err } logs.Progress.Printf("Pushing %s", dstTag) return pusher.Push(ctx, dstTag, desc) }) } } return g.Wait() } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/crane/delete.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package crane import ( "fmt" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote" ) // Delete deletes the remote reference at src. func Delete(src string, opt ...Option) error { o := makeOptions(opt...) ref, err := name.ParseReference(src, o.Name...) if err != nil { return fmt.Errorf("parsing reference %q: %w", src, err) } return remote.Delete(ref, o.Remote...) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/crane/digest.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package crane import "github.com/google/go-containerregistry/pkg/logs" // Digest returns the sha256 hash of the remote image at ref. func Digest(ref string, opt ...Option) (string, error) { o := makeOptions(opt...) if o.Platform != nil { desc, err := getManifest(ref, opt...) if err != nil { return "", err } if !desc.MediaType.IsIndex() { return desc.Digest.String(), nil } // TODO: does not work for indexes which contain schema v1 manifests img, err := desc.Image() if err != nil { return "", err } digest, err := img.Digest() if err != nil { return "", err } return digest.String(), nil } desc, err := Head(ref, opt...) if err != nil { logs.Warn.Printf("HEAD request failed, falling back on GET: %v", err) rdesc, err := getManifest(ref, opt...) if err != nil { return "", err } return rdesc.Digest.String(), nil } return desc.Digest.String(), nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/crane/doc.go ================================================ // Copyright 2019 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package crane holds libraries used to implement the crane CLI. package crane ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/crane/export.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package crane import ( "io" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/mutate" ) // Export writes the filesystem contents (as a tarball) of img to w. // If img has a single layer, just write the (uncompressed) contents to w so // that this "just works" for images that just wrap a single blob. func Export(img v1.Image, w io.Writer) error { layers, err := img.Layers() if err != nil { return err } if len(layers) == 1 { // If it's a single layer... l := layers[0] mt, err := l.MediaType() if err != nil { return err } if !mt.IsLayer() { // ...and isn't an OCI mediaType, we don't have to flatten it. // This lets export work for single layer, non-tarball images. rc, err := l.Uncompressed() if err != nil { return err } _, err = io.Copy(w, rc) return err } } fs := mutate.Extract(img) _, err = io.Copy(w, fs) return err } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/crane/filemap.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package crane import ( "archive/tar" "bytes" "io" "sort" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/empty" "github.com/google/go-containerregistry/pkg/v1/mutate" "github.com/google/go-containerregistry/pkg/v1/tarball" ) // Layer creates a layer from a single file map. These layers are reproducible and consistent. // A filemap is a path -> file content map representing a file system. func Layer(filemap map[string][]byte) (v1.Layer, error) { b := &bytes.Buffer{} w := tar.NewWriter(b) fn := []string{} for f := range filemap { fn = append(fn, f) } sort.Strings(fn) for _, f := range fn { c := filemap[f] if err := w.WriteHeader(&tar.Header{ Name: f, Size: int64(len(c)), }); err != nil { return nil, err } if _, err := w.Write(c); err != nil { return nil, err } } if err := w.Close(); err != nil { return nil, err } // Return a new copy of the buffer each time it's opened. return tarball.LayerFromOpener(func() (io.ReadCloser, error) { return io.NopCloser(bytes.NewBuffer(b.Bytes())), nil }) } // Image creates a image with the given filemaps as its contents. These images are reproducible and consistent. // A filemap is a path -> file content map representing a file system. func Image(filemap map[string][]byte) (v1.Image, error) { y, err := Layer(filemap) if err != nil { return nil, err } return mutate.AppendLayers(empty.Image, y) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/crane/get.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package crane import ( "fmt" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/remote" ) func getImage(r string, opt ...Option) (v1.Image, name.Reference, error) { o := makeOptions(opt...) ref, err := name.ParseReference(r, o.Name...) if err != nil { return nil, nil, fmt.Errorf("parsing reference %q: %w", r, err) } img, err := remote.Image(ref, o.Remote...) if err != nil { return nil, nil, fmt.Errorf("reading image %q: %w", ref, err) } return img, ref, nil } func getManifest(r string, opt ...Option) (*remote.Descriptor, error) { o := makeOptions(opt...) ref, err := name.ParseReference(r, o.Name...) if err != nil { return nil, fmt.Errorf("parsing reference %q: %w", r, err) } return remote.Get(ref, o.Remote...) } // Get calls remote.Get and returns an uninterpreted response. func Get(r string, opt ...Option) (*remote.Descriptor, error) { return getManifest(r, opt...) } // Head performs a HEAD request for a manifest and returns a content descriptor // based on the registry's response. func Head(r string, opt ...Option) (*v1.Descriptor, error) { o := makeOptions(opt...) ref, err := name.ParseReference(r, o.Name...) if err != nil { return nil, err } return remote.Head(ref, o.Remote...) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/crane/list.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package crane import ( "fmt" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote" ) // ListTags returns the tags in repository src. func ListTags(src string, opt ...Option) ([]string, error) { o := makeOptions(opt...) repo, err := name.NewRepository(src, o.Name...) if err != nil { return nil, fmt.Errorf("parsing repo %q: %w", src, err) } return remote.List(repo, o.Remote...) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/crane/manifest.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package crane // Manifest returns the manifest for the remote image or index ref. func Manifest(ref string, opt ...Option) ([]byte, error) { desc, err := getManifest(ref, opt...) if err != nil { return nil, err } o := makeOptions(opt...) if o.Platform != nil { img, err := desc.Image() if err != nil { return nil, err } return img.RawManifest() } return desc.Manifest, nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/crane/options.go ================================================ // Copyright 2019 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package crane import ( "context" "crypto/tls" "net/http" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/remote" ) // Options hold the options that crane uses when calling other packages. type Options struct { Name []name.Option Remote []remote.Option Platform *v1.Platform Keychain authn.Keychain Transport http.RoundTripper auth authn.Authenticator insecure bool jobs int noclobber bool ctx context.Context } // GetOptions exposes the underlying []remote.Option, []name.Option, and // platform, based on the passed Option. Generally, you shouldn't need to use // this unless you've painted yourself into a dependency corner as we have // with the crane and gcrane cli packages. func GetOptions(opts ...Option) Options { return makeOptions(opts...) } func makeOptions(opts ...Option) Options { opt := Options{ Remote: []remote.Option{ remote.WithAuthFromKeychain(authn.DefaultKeychain), }, Keychain: authn.DefaultKeychain, jobs: 4, ctx: context.Background(), } for _, o := range opts { o(&opt) } // Allow for untrusted certificates if the user // passed Insecure but no custom transport. if opt.insecure && opt.Transport == nil { transport := remote.DefaultTransport.(*http.Transport).Clone() transport.TLSClientConfig = &tls.Config{ InsecureSkipVerify: true, //nolint: gosec } WithTransport(transport)(&opt) } else if opt.Transport == nil { opt.Transport = remote.DefaultTransport } return opt } // Option is a functional option for crane. type Option func(*Options) // WithTransport is a functional option for overriding the default transport // for remote operations. Setting a transport will override the Insecure option's // configuration allowing for image registries to use untrusted certificates. func WithTransport(t http.RoundTripper) Option { return func(o *Options) { o.Remote = append(o.Remote, remote.WithTransport(t)) o.Transport = t } } // Insecure is an Option that allows image references to be fetched without TLS. // This will also allow for untrusted (e.g. self-signed) certificates in cases where // the default transport is used (i.e. when WithTransport is not used). func Insecure(o *Options) { o.Name = append(o.Name, name.Insecure) o.insecure = true } // WithPlatform is an Option to specify the platform. func WithPlatform(platform *v1.Platform) Option { return func(o *Options) { if platform != nil { o.Remote = append(o.Remote, remote.WithPlatform(*platform)) } o.Platform = platform } } // WithAuthFromKeychain is a functional option for overriding the default // authenticator for remote operations, using an authn.Keychain to find // credentials. // // By default, crane will use authn.DefaultKeychain. func WithAuthFromKeychain(keys authn.Keychain) Option { return func(o *Options) { // Replace the default keychain at position 0. o.Remote[0] = remote.WithAuthFromKeychain(keys) o.Keychain = keys } } // WithAuth is a functional option for overriding the default authenticator // for remote operations. // // By default, crane will use authn.DefaultKeychain. func WithAuth(auth authn.Authenticator) Option { return func(o *Options) { // Replace the default keychain at position 0. o.Remote[0] = remote.WithAuth(auth) o.auth = auth } } // WithUserAgent adds the given string to the User-Agent header for any HTTP // requests. func WithUserAgent(ua string) Option { return func(o *Options) { o.Remote = append(o.Remote, remote.WithUserAgent(ua)) } } // WithNondistributable is an option that allows pushing non-distributable // layers. func WithNondistributable() Option { return func(o *Options) { o.Remote = append(o.Remote, remote.WithNondistributable) } } // WithContext is a functional option for setting the context. func WithContext(ctx context.Context) Option { return func(o *Options) { o.ctx = ctx o.Remote = append(o.Remote, remote.WithContext(ctx)) } } // WithJobs sets the number of concurrent jobs to run. // // The default number of jobs is GOMAXPROCS. func WithJobs(jobs int) Option { return func(o *Options) { if jobs > 0 { o.jobs = jobs } o.Remote = append(o.Remote, remote.WithJobs(o.jobs)) } } // WithNoClobber modifies behavior to avoid overwriting existing tags, if possible. func WithNoClobber(noclobber bool) Option { return func(o *Options) { o.noclobber = noclobber } } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/crane/pull.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package crane import ( "fmt" "os" legacy "github.com/google/go-containerregistry/pkg/legacy/tarball" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/empty" "github.com/google/go-containerregistry/pkg/v1/layout" "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/google/go-containerregistry/pkg/v1/tarball" ) // Tag applied to images that were pulled by digest. This denotes that the // image was (probably) never tagged with this, but lets us avoid applying the // ":latest" tag which might be misleading. const iWasADigestTag = "i-was-a-digest" // Pull returns a v1.Image of the remote image src. func Pull(src string, opt ...Option) (v1.Image, error) { o := makeOptions(opt...) ref, err := name.ParseReference(src, o.Name...) if err != nil { return nil, fmt.Errorf("parsing reference %q: %w", src, err) } return remote.Image(ref, o.Remote...) } // Save writes the v1.Image img as a tarball at path with tag src. func Save(img v1.Image, src, path string) error { imgMap := map[string]v1.Image{src: img} return MultiSave(imgMap, path) } // MultiSave writes collection of v1.Image img with tag as a tarball. func MultiSave(imgMap map[string]v1.Image, path string, opt ...Option) error { o := makeOptions(opt...) tagToImage := map[name.Tag]v1.Image{} for src, img := range imgMap { ref, err := name.ParseReference(src, o.Name...) if err != nil { return fmt.Errorf("parsing ref %q: %w", src, err) } // WriteToFile wants a tag to write to the tarball, but we might have // been given a digest. // If the original ref was a tag, use that. Otherwise, if it was a // digest, tag the image with :i-was-a-digest instead. tag, ok := ref.(name.Tag) if !ok { d, ok := ref.(name.Digest) if !ok { return fmt.Errorf("ref wasn't a tag or digest") } tag = d.Repository.Tag(iWasADigestTag) } tagToImage[tag] = img } // no progress channel (for now) return tarball.MultiWriteToFile(path, tagToImage) } // PullLayer returns the given layer from a registry. func PullLayer(ref string, opt ...Option) (v1.Layer, error) { o := makeOptions(opt...) digest, err := name.NewDigest(ref, o.Name...) if err != nil { return nil, err } return remote.Layer(digest, o.Remote...) } // SaveLegacy writes the v1.Image img as a legacy tarball at path with tag src. func SaveLegacy(img v1.Image, src, path string) error { imgMap := map[string]v1.Image{src: img} return MultiSave(imgMap, path) } // MultiSaveLegacy writes collection of v1.Image img with tag as a legacy tarball. func MultiSaveLegacy(imgMap map[string]v1.Image, path string) error { refToImage := map[name.Reference]v1.Image{} for src, img := range imgMap { ref, err := name.ParseReference(src) if err != nil { return fmt.Errorf("parsing ref %q: %w", src, err) } refToImage[ref] = img } w, err := os.Create(path) if err != nil { return err } defer w.Close() return legacy.MultiWrite(refToImage, w) } // SaveOCI writes the v1.Image img as an OCI Image Layout at path. If a layout // already exists at that path, it will add the image to the index. func SaveOCI(img v1.Image, path string) error { imgMap := map[string]v1.Image{"": img} return MultiSaveOCI(imgMap, path) } // MultiSaveOCI writes collection of v1.Image img as an OCI Image Layout at path. If a layout // already exists at that path, it will add the image to the index. func MultiSaveOCI(imgMap map[string]v1.Image, path string) error { p, err := layout.FromPath(path) if err != nil { p, err = layout.Write(path, empty.Index) if err != nil { return err } } for _, img := range imgMap { if err = p.AppendImage(img); err != nil { return err } } return nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/crane/push.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package crane import ( "fmt" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/google/go-containerregistry/pkg/v1/tarball" ) // Load reads the tarball at path as a v1.Image. func Load(path string, opt ...Option) (v1.Image, error) { return LoadTag(path, "", opt...) } // LoadTag reads a tag from the tarball at path as a v1.Image. // If tag is "", will attempt to read the tarball as a single image. func LoadTag(path, tag string, opt ...Option) (v1.Image, error) { if tag == "" { return tarball.ImageFromPath(path, nil) } o := makeOptions(opt...) t, err := name.NewTag(tag, o.Name...) if err != nil { return nil, fmt.Errorf("parsing tag %q: %w", tag, err) } return tarball.ImageFromPath(path, &t) } // Push pushes the v1.Image img to a registry as dst. func Push(img v1.Image, dst string, opt ...Option) error { o := makeOptions(opt...) tag, err := name.ParseReference(dst, o.Name...) if err != nil { return fmt.Errorf("parsing reference %q: %w", dst, err) } return remote.Write(tag, img, o.Remote...) } // Upload pushes the v1.Layer to a given repo. func Upload(layer v1.Layer, repo string, opt ...Option) error { o := makeOptions(opt...) ref, err := name.NewRepository(repo, o.Name...) if err != nil { return fmt.Errorf("parsing repo %q: %w", repo, err) } return remote.WriteLayer(ref, layer, o.Remote...) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/crane/tag.go ================================================ // Copyright 2019 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package crane import ( "fmt" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote" ) // Tag adds tag to the remote img. func Tag(img, tag string, opt ...Option) error { o := makeOptions(opt...) ref, err := name.ParseReference(img, o.Name...) if err != nil { return fmt.Errorf("parsing reference %q: %w", img, err) } desc, err := remote.Get(ref, o.Remote...) if err != nil { return fmt.Errorf("fetching %q: %w", img, err) } dst := ref.Context().Tag(tag) return remote.Tag(dst, desc, o.Remote...) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/legacy/config.go ================================================ // Copyright 2019 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package legacy import ( v1 "github.com/google/go-containerregistry/pkg/v1" ) // LayerConfigFile is the configuration file that holds the metadata describing // a v1 layer. See: // https://github.com/moby/moby/blob/master/image/spec/v1.md type LayerConfigFile struct { v1.ConfigFile ContainerConfig v1.Config `json:"container_config,omitempty"` ID string `json:"id,omitempty"` Parent string `json:"parent,omitempty"` Throwaway bool `json:"throwaway,omitempty"` Comment string `json:"comment,omitempty"` } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/legacy/doc.go ================================================ // Copyright 2019 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package legacy provides functionality to work with docker images in the v1 // format. // See: https://github.com/moby/moby/blob/master/image/spec/v1.md package legacy ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/legacy/tarball/README.md ================================================ # `legacy/tarball` [![GoDoc](https://godoc.org/github.com/google/go-containerregistry/pkg/legacy/tarball?status.svg)](https://godoc.org/github.com/google/go-containerregistry/pkg/legacy/tarball) This package implements support for writing legacy tarballs, as described [here](https://github.com/moby/moby/blob/749d90e10f989802638ae542daf54257f3bf71f2/image/spec/v1.2.md#combined-image-json--filesystem-changeset-format). ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/legacy/tarball/doc.go ================================================ // Copyright 2019 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package tarball provides facilities for writing v1 docker images // (https://github.com/moby/moby/blob/master/image/spec/v1.md) from/to a tarball // on-disk. package tarball ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/legacy/tarball/write.go ================================================ // Copyright 2019 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package tarball import ( "archive/tar" "bytes" "encoding/json" "fmt" "io" "sort" "strings" "github.com/google/go-containerregistry/pkg/legacy" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/partial" "github.com/google/go-containerregistry/pkg/v1/tarball" ) // repositoriesTarDescriptor represents the repositories file inside a `docker save` tarball. type repositoriesTarDescriptor map[string]map[string]string // v1Layer represents a layer with metadata needed by the v1 image spec https://github.com/moby/moby/blob/master/image/spec/v1.md. type v1Layer struct { // config is the layer metadata. config *legacy.LayerConfigFile // layer is the v1.Layer object this v1Layer represents. layer v1.Layer } // json returns the raw bytes of the json metadata of the given v1Layer. func (l *v1Layer) json() ([]byte, error) { return json.Marshal(l.config) } // version returns the raw bytes of the "VERSION" file of the given v1Layer. func (l *v1Layer) version() []byte { return []byte("1.0") } // v1LayerID computes the v1 image format layer id for the given v1.Layer with the given v1 parent ID and raw image config. func v1LayerID(layer v1.Layer, parentID string, rawConfig []byte) (string, error) { d, err := layer.Digest() if err != nil { return "", fmt.Errorf("unable to get layer digest to generate v1 layer ID: %w", err) } s := fmt.Sprintf("%s %s", d.Hex, parentID) if len(rawConfig) != 0 { s = fmt.Sprintf("%s %s", s, string(rawConfig)) } h, _, _ := v1.SHA256(strings.NewReader(s)) return h.Hex, nil } // newTopV1Layer creates a new v1Layer for a layer other than the top layer in a v1 image tarball. func newV1Layer(layer v1.Layer, parent *v1Layer, history v1.History) (*v1Layer, error) { parentID := "" if parent != nil { parentID = parent.config.ID } id, err := v1LayerID(layer, parentID, nil) if err != nil { return nil, fmt.Errorf("unable to generate v1 layer ID: %w", err) } result := &v1Layer{ layer: layer, config: &legacy.LayerConfigFile{ ConfigFile: v1.ConfigFile{ Created: history.Created, Author: history.Author, }, ContainerConfig: v1.Config{ Cmd: []string{history.CreatedBy}, }, ID: id, Parent: parentID, Throwaway: history.EmptyLayer, Comment: history.Comment, }, } return result, nil } // newTopV1Layer creates a new v1Layer for the top layer in a v1 image tarball. func newTopV1Layer(layer v1.Layer, parent *v1Layer, history v1.History, imgConfig *v1.ConfigFile, rawConfig []byte) (*v1Layer, error) { result, err := newV1Layer(layer, parent, history) if err != nil { return nil, err } id, err := v1LayerID(layer, result.config.Parent, rawConfig) if err != nil { return nil, fmt.Errorf("unable to generate v1 layer ID for top layer: %w", err) } result.config.ID = id result.config.Architecture = imgConfig.Architecture result.config.Container = imgConfig.Container result.config.DockerVersion = imgConfig.DockerVersion result.config.OS = imgConfig.OS result.config.Config = imgConfig.Config result.config.Created = imgConfig.Created return result, nil } // splitTag splits the given tagged image name /: // into / and . func splitTag(name string) (string, string) { // Split on ":" parts := strings.Split(name, ":") // Verify that we aren't confusing a tag for a hostname w/ port for the purposes of weak validation. if len(parts) > 1 && !strings.Contains(parts[len(parts)-1], "/") { base := strings.Join(parts[:len(parts)-1], ":") tag := parts[len(parts)-1] return base, tag } return name, "" } // addTags adds the given image tags to the given "repositories" file descriptor in a v1 image tarball. func addTags(repos repositoriesTarDescriptor, tags []string, topLayerID string) { for _, t := range tags { base, tag := splitTag(t) tagToID, ok := repos[base] if !ok { tagToID = make(map[string]string) repos[base] = tagToID } tagToID[tag] = topLayerID } } // updateLayerSources updates the given layer digest to descriptor map with the descriptor of the given layer in the given image if it's an undistributable layer. func updateLayerSources(layerSources map[v1.Hash]v1.Descriptor, layer v1.Layer, img v1.Image) error { d, err := layer.Digest() if err != nil { return err } // Add to LayerSources if it's a foreign layer. desc, err := partial.BlobDescriptor(img, d) if err != nil { return err } if !desc.MediaType.IsDistributable() { diffid, err := partial.BlobToDiffID(img, d) if err != nil { return err } layerSources[diffid] = *desc } return nil } // Write is a wrapper to write a single image in V1 format and tag to a tarball. func Write(ref name.Reference, img v1.Image, w io.Writer) error { return MultiWrite(map[name.Reference]v1.Image{ref: img}, w) } // filterEmpty filters out the history corresponding to empty layers from the // given history. func filterEmpty(h []v1.History) []v1.History { result := []v1.History{} for _, i := range h { if i.EmptyLayer { continue } result = append(result, i) } return result } // MultiWrite writes the contents of each image to the provided reader, in the V1 image tarball format. // The contents are written in the following format: // One manifest.json file at the top level containing information about several images. // One repositories file mapping from the image / to to the id of the top most layer. // For every layer, a directory named with the layer ID is created with the following contents: // // layer.tar - The uncompressed layer tarball. // .json- Layer metadata json. // VERSION- Schema version string. Always set to "1.0". // // One file for the config blob, named after its SHA. func MultiWrite(refToImage map[name.Reference]v1.Image, w io.Writer) error { tf := tar.NewWriter(w) defer tf.Close() sortedImages, imageToTags := dedupRefToImage(refToImage) var m tarball.Manifest repos := make(repositoriesTarDescriptor) seenLayerIDs := make(map[string]struct{}) for _, img := range sortedImages { tags := imageToTags[img] // Write the config. cfgName, err := img.ConfigName() if err != nil { return err } cfgFileName := fmt.Sprintf("%s.json", cfgName.Hex) cfgBlob, err := img.RawConfigFile() if err != nil { return err } if err := writeTarEntry(tf, cfgFileName, bytes.NewReader(cfgBlob), int64(len(cfgBlob))); err != nil { return err } cfg, err := img.ConfigFile() if err != nil { return err } // Store foreign layer info. layerSources := make(map[v1.Hash]v1.Descriptor) // Write the layers. layers, err := img.Layers() if err != nil { return err } history := filterEmpty(cfg.History) // Create a blank config history if the config didn't have a history. if len(history) == 0 && len(layers) != 0 { history = make([]v1.History, len(layers)) } else if len(layers) != len(history) { return fmt.Errorf("image config had layer history which did not match the number of layers, got len(history)=%d, len(layers)=%d, want len(history)=len(layers)", len(history), len(layers)) } layerFiles := make([]string, len(layers)) var prev *v1Layer for i, l := range layers { if err := updateLayerSources(layerSources, l, img); err != nil { return fmt.Errorf("unable to update image metadata to include undistributable layer source information: %w", err) } var cur *v1Layer if i < (len(layers) - 1) { cur, err = newV1Layer(l, prev, history[i]) } else { cur, err = newTopV1Layer(l, prev, history[i], cfg, cfgBlob) } if err != nil { return err } layerFiles[i] = fmt.Sprintf("%s/layer.tar", cur.config.ID) if _, ok := seenLayerIDs[cur.config.ID]; ok { prev = cur continue } seenLayerIDs[cur.config.ID] = struct{}{} // If the v1.Layer implements UncompressedSize efficiently, use that // for the tar header. Otherwise, this iterates over Uncompressed(). // NOTE: If using a streaming layer, this may consume the layer. size, err := partial.UncompressedSize(l) if err != nil { return err } u, err := l.Uncompressed() if err != nil { return err } defer u.Close() if err := writeTarEntry(tf, layerFiles[i], u, size); err != nil { return err } j, err := cur.json() if err != nil { return err } if err := writeTarEntry(tf, fmt.Sprintf("%s/json", cur.config.ID), bytes.NewReader(j), int64(len(j))); err != nil { return err } v := cur.version() if err := writeTarEntry(tf, fmt.Sprintf("%s/VERSION", cur.config.ID), bytes.NewReader(v), int64(len(v))); err != nil { return err } prev = cur } // Generate the tar descriptor and write it. m = append(m, tarball.Descriptor{ Config: cfgFileName, RepoTags: tags, Layers: layerFiles, LayerSources: layerSources, }) // prev should be the top layer here. Use it to add the image tags // to the tarball repositories file. addTags(repos, tags, prev.config.ID) } mBytes, err := json.Marshal(m) if err != nil { return err } if err := writeTarEntry(tf, "manifest.json", bytes.NewReader(mBytes), int64(len(mBytes))); err != nil { return err } reposBytes, err := json.Marshal(&repos) if err != nil { return err } return writeTarEntry(tf, "repositories", bytes.NewReader(reposBytes), int64(len(reposBytes))) } func dedupRefToImage(refToImage map[name.Reference]v1.Image) ([]v1.Image, map[v1.Image][]string) { imageToTags := make(map[v1.Image][]string) for ref, img := range refToImage { if tag, ok := ref.(name.Tag); ok { if tags, ok := imageToTags[img]; ok && tags != nil { imageToTags[img] = append(tags, tag.String()) } else { imageToTags[img] = []string{tag.String()} } } else { if _, ok := imageToTags[img]; !ok { imageToTags[img] = nil } } } // Force specific order on tags imgs := []v1.Image{} for img, tags := range imageToTags { sort.Strings(tags) imgs = append(imgs, img) } sort.Slice(imgs, func(i, j int) bool { cfI, err := imgs[i].ConfigName() if err != nil { return false } cfJ, err := imgs[j].ConfigName() if err != nil { return false } return cfI.Hex < cfJ.Hex }) return imgs, imageToTags } // Writes a file to the provided writer with a corresponding tar header func writeTarEntry(tf *tar.Writer, path string, r io.Reader, size int64) error { hdr := &tar.Header{ Mode: 0644, Typeflag: tar.TypeReg, Size: size, Name: path, } if err := tf.WriteHeader(hdr); err != nil { return err } _, err := io.Copy(tf, r) return err } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/logs/logs.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package logs exposes the loggers used by this library. package logs import ( "io" "log" ) var ( // Warn is used to log non-fatal errors. Warn = log.New(io.Discard, "", log.LstdFlags) // Progress is used to log notable, successful events. Progress = log.New(io.Discard, "", log.LstdFlags) // Debug is used to log information that is useful for debugging. Debug = log.New(io.Discard, "", log.LstdFlags) ) // Enabled checks to see if the logger's writer is set to something other // than io.Discard. This allows callers to avoid expensive operations // that will end up in /dev/null anyway. func Enabled(l *log.Logger) bool { return l.Writer() != io.Discard } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/name/README.md ================================================ # `name` [![GoDoc](https://godoc.org/github.com/google/go-containerregistry/pkg/name?status.svg)](https://godoc.org/github.com/google/go-containerregistry/pkg/name) ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/name/check.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package name import ( "strings" "unicode/utf8" ) // stripRunesFn returns a function which returns -1 (i.e. a value which // signals deletion in strings.Map) for runes in 'runes', and the rune otherwise. func stripRunesFn(runes string) func(rune) rune { return func(r rune) rune { if strings.ContainsRune(runes, r) { return -1 } return r } } // checkElement checks a given named element matches character and length restrictions. // Returns true if the given element adheres to the given restrictions, false otherwise. func checkElement(name, element, allowedRunes string, minRunes, maxRunes int) error { numRunes := utf8.RuneCountInString(element) if (numRunes < minRunes) || (maxRunes < numRunes) { return newErrBadName("%s must be between %d and %d characters in length: %s", name, minRunes, maxRunes, element) } else if len(strings.Map(stripRunesFn(allowedRunes), element)) != 0 { return newErrBadName("%s can only contain the characters `%s`: %s", name, allowedRunes, element) } return nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/name/digest.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package name import ( // nolint: depguard _ "crypto/sha256" // Recommended by go-digest. "strings" "github.com/opencontainers/go-digest" ) const digestDelim = "@" // Digest stores a digest name in a structured form. type Digest struct { Repository digest string original string } // Ensure Digest implements Reference var _ Reference = (*Digest)(nil) // Context implements Reference. func (d Digest) Context() Repository { return d.Repository } // Identifier implements Reference. func (d Digest) Identifier() string { return d.DigestStr() } // DigestStr returns the digest component of the Digest. func (d Digest) DigestStr() string { return d.digest } // Name returns the name from which the Digest was derived. func (d Digest) Name() string { return d.Repository.Name() + digestDelim + d.DigestStr() } // String returns the original input string. func (d Digest) String() string { return d.original } // NewDigest returns a new Digest representing the given name. func NewDigest(name string, opts ...Option) (Digest, error) { // Split on "@" parts := strings.Split(name, digestDelim) if len(parts) != 2 { return Digest{}, newErrBadName("a digest must contain exactly one '@' separator (e.g. registry/repository@digest) saw: %s", name) } base := parts[0] dig := parts[1] prefix := digest.Canonical.String() + ":" if !strings.HasPrefix(dig, prefix) { return Digest{}, newErrBadName("unsupported digest algorithm: %s", dig) } hex := strings.TrimPrefix(dig, prefix) if err := digest.Canonical.Validate(hex); err != nil { return Digest{}, err } tag, err := NewTag(base, opts...) if err == nil { base = tag.Repository.Name() } repo, err := NewRepository(base, opts...) if err != nil { return Digest{}, err } return Digest{ Repository: repo, digest: dig, original: name, }, nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/name/doc.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package name defines structured types for representing image references. // // What's in a name? For image references, not nearly enough! // // Image references look a lot like URLs, but they differ in that they don't // contain the scheme (http or https), they can end with a :tag or a @digest // (the latter being validated), and they perform defaulting for missing // components. // // Since image references don't contain the scheme, we do our best to infer // if we use http or https from the given hostname. We allow http fallback for // any host that looks like localhost (localhost, 127.0.0.1, ::1), ends in // ".local", or is in the "private" address space per RFC 1918. For everything // else, we assume https only. To override this heuristic, use the Insecure // option. // // Image references with a digest signal to us that we should verify the content // of the image matches the digest. E.g. when pulling a Digest reference, we'll // calculate the sha256 of the manifest returned by the registry and error out // if it doesn't match what we asked for. // // For defaulting, we interpret "ubuntu" as // "index.docker.io/library/ubuntu:latest" because we add the missing repo // "library", the missing registry "index.docker.io", and the missing tag // "latest". To disable this defaulting, use the StrictValidation option. This // is useful e.g. to only allow image references that explicitly set a tag or // digest, so that you don't accidentally pull "latest". package name ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/name/errors.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package name import ( "errors" "fmt" ) // ErrBadName is an error for when a bad docker name is supplied. type ErrBadName struct { info string } func (e *ErrBadName) Error() string { return e.info } // Is reports whether target is an error of type ErrBadName func (e *ErrBadName) Is(target error) bool { var berr *ErrBadName return errors.As(target, &berr) } // newErrBadName returns a ErrBadName which returns the given formatted string from Error(). func newErrBadName(fmtStr string, args ...any) *ErrBadName { return &ErrBadName{fmt.Sprintf(fmtStr, args...)} } // IsErrBadName returns true if the given error is an ErrBadName. // // Deprecated: Use errors.Is. func IsErrBadName(err error) bool { var berr *ErrBadName return errors.As(err, &berr) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/name/options.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package name const ( // DefaultRegistry is the registry name that will be used if no registry // provided and the default is not overridden. DefaultRegistry = "index.docker.io" defaultRegistryAlias = "docker.io" // DefaultTag is the tag name that will be used if no tag provided and the // default is not overridden. DefaultTag = "latest" ) type options struct { strict bool // weak by default insecure bool // secure by default defaultRegistry string defaultTag string } func makeOptions(opts ...Option) options { opt := options{ defaultRegistry: DefaultRegistry, defaultTag: DefaultTag, } for _, o := range opts { o(&opt) } return opt } // Option is a functional option for name parsing. type Option func(*options) // StrictValidation is an Option that requires image references to be fully // specified; i.e. no defaulting for registry (dockerhub), repo (library), // or tag (latest). func StrictValidation(opts *options) { opts.strict = true } // WeakValidation is an Option that sets defaults when parsing names, see // StrictValidation. func WeakValidation(opts *options) { opts.strict = false } // Insecure is an Option that allows image references to be fetched without TLS. func Insecure(opts *options) { opts.insecure = true } // OptionFn is a function that returns an option. type OptionFn func() Option // WithDefaultRegistry sets the default registry that will be used if one is not // provided. func WithDefaultRegistry(r string) Option { return func(opts *options) { opts.defaultRegistry = r } } // WithDefaultTag sets the default tag that will be used if one is not provided. func WithDefaultTag(t string) Option { return func(opts *options) { opts.defaultTag = t } } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/name/ref.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package name import ( "fmt" ) // Reference defines the interface that consumers use when they can // take either a tag or a digest. type Reference interface { fmt.Stringer // Context accesses the Repository context of the reference. Context() Repository // Identifier accesses the type-specific portion of the reference. Identifier() string // Name is the fully-qualified reference name. Name() string // Scope is the scope needed to access this reference. Scope(string) string } // ParseReference parses the string as a reference, either by tag or digest. func ParseReference(s string, opts ...Option) (Reference, error) { if t, err := NewTag(s, opts...); err == nil { return t, nil } if d, err := NewDigest(s, opts...); err == nil { return d, nil } return nil, newErrBadName("could not parse reference: " + s) } type stringConst string // MustParseReference behaves like ParseReference, but panics instead of // returning an error. It's intended for use in tests, or when a value is // expected to be valid at code authoring time. // // To discourage its use in scenarios where the value is not known at code // authoring time, it must be passed a string constant: // // const str = "valid/string" // MustParseReference(str) // MustParseReference("another/valid/string") // MustParseReference(str + "/and/more") // // These will not compile: // // var str = "valid/string" // MustParseReference(str) // MustParseReference(strings.Join([]string{"valid", "string"}, "/")) func MustParseReference(s stringConst, opts ...Option) Reference { ref, err := ParseReference(string(s), opts...) if err != nil { panic(err) } return ref } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/name/registry.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package name import ( "net" "net/url" "path" "regexp" "strings" ) // Detect more complex forms of local references. var reLocal = regexp.MustCompile(`.*\.local(?:host)?(?::\d{1,5})?$`) // Detect the loopback IP (127.0.0.1) var reLoopback = regexp.MustCompile(regexp.QuoteMeta("127.0.0.1")) // Detect the loopback IPV6 (::1) var reipv6Loopback = regexp.MustCompile(regexp.QuoteMeta("::1")) // Registry stores a docker registry name in a structured form. type Registry struct { insecure bool registry string } // RegistryStr returns the registry component of the Registry. func (r Registry) RegistryStr() string { return r.registry } // Name returns the name from which the Registry was derived. func (r Registry) Name() string { return r.RegistryStr() } func (r Registry) String() string { return r.Name() } // Repo returns a Repository in the Registry with the given name. func (r Registry) Repo(repo ...string) Repository { return Repository{Registry: r, repository: path.Join(repo...)} } // Scope returns the scope required to access the registry. func (r Registry) Scope(string) string { // The only resource under 'registry' is 'catalog'. http://goo.gl/N9cN9Z return "registry:catalog:*" } func (r Registry) isRFC1918() bool { ipStr := strings.Split(r.Name(), ":")[0] ip := net.ParseIP(ipStr) if ip == nil { return false } for _, cidr := range []string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"} { _, block, _ := net.ParseCIDR(cidr) if block.Contains(ip) { return true } } return false } // Scheme returns https scheme for all the endpoints except localhost or when explicitly defined. func (r Registry) Scheme() string { if r.insecure { return "http" } if r.isRFC1918() { return "http" } if strings.HasPrefix(r.Name(), "localhost:") { return "http" } if reLocal.MatchString(r.Name()) { return "http" } if reLoopback.MatchString(r.Name()) { return "http" } if reipv6Loopback.MatchString(r.Name()) { return "http" } return "https" } func checkRegistry(name string) error { // Per RFC 3986, registries (authorities) are required to be prefixed with "//" // url.Host == hostname[:port] == authority if url, err := url.Parse("//" + name); err != nil || url.Host != name { return newErrBadName("registries must be valid RFC 3986 URI authorities: %s", name) } return nil } // NewRegistry returns a Registry based on the given name. // Strict validation requires explicit, valid RFC 3986 URI authorities to be given. func NewRegistry(name string, opts ...Option) (Registry, error) { opt := makeOptions(opts...) if opt.strict && len(name) == 0 { return Registry{}, newErrBadName("strict validation requires the registry to be explicitly defined") } if err := checkRegistry(name); err != nil { return Registry{}, err } if name == "" { name = opt.defaultRegistry } // Rewrite "docker.io" to "index.docker.io". // See: https://github.com/google/go-containerregistry/issues/68 if name == defaultRegistryAlias { name = DefaultRegistry } return Registry{registry: name, insecure: opt.insecure}, nil } // NewInsecureRegistry returns an Insecure Registry based on the given name. // // Deprecated: Use the Insecure Option with NewRegistry instead. func NewInsecureRegistry(name string, opts ...Option) (Registry, error) { opts = append(opts, Insecure) return NewRegistry(name, opts...) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/name/repository.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package name import ( "fmt" "strings" ) const ( defaultNamespace = "library" repositoryChars = "abcdefghijklmnopqrstuvwxyz0123456789_-./" regRepoDelimiter = "/" ) // Repository stores a docker repository name in a structured form. type Repository struct { Registry repository string } // See https://docs.docker.com/docker-hub/official_repos func hasImplicitNamespace(repo string, reg Registry) bool { return !strings.ContainsRune(repo, '/') && reg.RegistryStr() == DefaultRegistry } // RepositoryStr returns the repository component of the Repository. func (r Repository) RepositoryStr() string { if hasImplicitNamespace(r.repository, r.Registry) { return fmt.Sprintf("%s/%s", defaultNamespace, r.repository) } return r.repository } // Name returns the name from which the Repository was derived. func (r Repository) Name() string { regName := r.Registry.Name() if regName != "" { return regName + regRepoDelimiter + r.RepositoryStr() } // TODO: As far as I can tell, this is unreachable. return r.RepositoryStr() } func (r Repository) String() string { return r.Name() } // Scope returns the scope required to perform the given action on the registry. // TODO(jonjohnsonjr): consider moving scopes to a separate package. func (r Repository) Scope(action string) string { return fmt.Sprintf("repository:%s:%s", r.RepositoryStr(), action) } func checkRepository(repository string) error { return checkElement("repository", repository, repositoryChars, 2, 255) } // NewRepository returns a new Repository representing the given name, according to the given strictness. func NewRepository(name string, opts ...Option) (Repository, error) { opt := makeOptions(opts...) if len(name) == 0 { return Repository{}, newErrBadName("a repository name must be specified") } var registry string repo := name parts := strings.SplitN(name, regRepoDelimiter, 2) if len(parts) == 2 && (strings.ContainsRune(parts[0], '.') || strings.ContainsRune(parts[0], ':')) { // The first part of the repository is treated as the registry domain // iff it contains a '.' or ':' character, otherwise it is all repository // and the domain defaults to Docker Hub. registry = parts[0] repo = parts[1] } if err := checkRepository(repo); err != nil { return Repository{}, err } reg, err := NewRegistry(registry, opts...) if err != nil { return Repository{}, err } if hasImplicitNamespace(repo, reg) && opt.strict { return Repository{}, newErrBadName("strict validation requires the full repository path (missing 'library')") } return Repository{reg, repo}, nil } // Tag returns a Tag in this Repository. func (r Repository) Tag(identifier string) Tag { t := Tag{ tag: identifier, Repository: r, } t.original = t.Name() return t } // Digest returns a Digest in this Repository. func (r Repository) Digest(identifier string) Digest { d := Digest{ digest: identifier, Repository: r, } d.original = d.Name() return d } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/name/tag.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package name import ( "strings" ) const ( // TODO(dekkagaijin): use the docker/distribution regexes for validation. tagChars = "abcdefghijklmnopqrstuvwxyz0123456789_-.ABCDEFGHIJKLMNOPQRSTUVWXYZ" tagDelim = ":" ) // Tag stores a docker tag name in a structured form. type Tag struct { Repository tag string original string } // Ensure Tag implements Reference var _ Reference = (*Tag)(nil) // Context implements Reference. func (t Tag) Context() Repository { return t.Repository } // Identifier implements Reference. func (t Tag) Identifier() string { return t.TagStr() } // TagStr returns the tag component of the Tag. func (t Tag) TagStr() string { return t.tag } // Name returns the name from which the Tag was derived. func (t Tag) Name() string { return t.Repository.Name() + tagDelim + t.TagStr() } // String returns the original input string. func (t Tag) String() string { return t.original } // Scope returns the scope required to perform the given action on the tag. func (t Tag) Scope(action string) string { return t.Repository.Scope(action) } func checkTag(name string) error { return checkElement("tag", name, tagChars, 1, 128) } // NewTag returns a new Tag representing the given name, according to the given strictness. func NewTag(name string, opts ...Option) (Tag, error) { opt := makeOptions(opts...) base := name tag := "" // Split on ":" parts := strings.Split(name, tagDelim) // Verify that we aren't confusing a tag for a hostname w/ port for the purposes of weak validation. if len(parts) > 1 && !strings.Contains(parts[len(parts)-1], regRepoDelimiter) { base = strings.Join(parts[:len(parts)-1], tagDelim) tag = parts[len(parts)-1] } // We don't require a tag, but if we get one check it's valid, // even when not being strict. // If we are being strict, we want to validate the tag regardless in case // it's empty. if tag != "" || opt.strict { if err := checkTag(tag); err != nil { return Tag{}, err } } if tag == "" { tag = opt.defaultTag } repo, err := NewRepository(base, opts...) if err != nil { return Tag{}, err } return Tag{ Repository: repo, tag: tag, original: name, }, nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/registry/README.md ================================================ # `pkg/registry` This package implements a Docker v2 registry and the OCI distribution specification. It is designed to be used anywhere a low dependency container registry is needed, with an initial focus on tests. Its goal is to be standards compliant and its strictness will increase over time. This is currently a low flightmiles system. It's likely quite safe to use in tests; If you're using it in production, please let us know how and send us PRs for integration tests. Before sending a PR, understand that the expectation of this package is that it remain free of extraneous dependencies. This means that we expect `pkg/registry` to only have dependencies on Go's standard library, and other packages in `go-containerregistry`. You may be asked to change your code to reduce dependencies, and your PR might be rejected if this is deemed impossible. ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/registry/blobs.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package registry import ( "bytes" "context" "errors" "fmt" "io" "log" "math/rand" "net/http" "path" "strings" "sync" "github.com/google/go-containerregistry/internal/verify" v1 "github.com/google/go-containerregistry/pkg/v1" ) // Returns whether this url should be handled by the blob handler // This is complicated because blob is indicated by the trailing path, not the leading path. // https://github.com/opencontainers/distribution-spec/blob/master/spec.md#pulling-a-layer // https://github.com/opencontainers/distribution-spec/blob/master/spec.md#pushing-a-layer func isBlob(req *http.Request) bool { elem := strings.Split(req.URL.Path, "/") elem = elem[1:] if elem[len(elem)-1] == "" { elem = elem[:len(elem)-1] } if len(elem) < 3 { return false } return elem[len(elem)-2] == "blobs" || (elem[len(elem)-3] == "blobs" && elem[len(elem)-2] == "uploads") } // BlobHandler represents a minimal blob storage backend, capable of serving // blob contents. type BlobHandler interface { // Get gets the blob contents, or errNotFound if the blob wasn't found. Get(ctx context.Context, repo string, h v1.Hash) (io.ReadCloser, error) } // BlobStatHandler is an extension interface representing a blob storage // backend that can serve metadata about blobs. type BlobStatHandler interface { // Stat returns the size of the blob, or errNotFound if the blob wasn't // found, or redirectError if the blob can be found elsewhere. Stat(ctx context.Context, repo string, h v1.Hash) (int64, error) } // BlobPutHandler is an extension interface representing a blob storage backend // that can write blob contents. type BlobPutHandler interface { // Put puts the blob contents. // // The contents will be verified against the expected size and digest // as the contents are read, and an error will be returned if these // don't match. Implementations should return that error, or a wrapper // around that error, to return the correct error when these don't match. Put(ctx context.Context, repo string, h v1.Hash, rc io.ReadCloser) error } // BlobDeleteHandler is an extension interface representing a blob storage // backend that can delete blob contents. type BlobDeleteHandler interface { // Delete the blob contents. Delete(ctx context.Context, repo string, h v1.Hash) error } // redirectError represents a signal that the blob handler doesn't have the blob // contents, but that those contents are at another location which registry // clients should redirect to. type redirectError struct { // Location is the location to find the contents. Location string // Code is the HTTP redirect status code to return to clients. Code int } func (e redirectError) Error() string { return fmt.Sprintf("redirecting (%d): %s", e.Code, e.Location) } // errNotFound represents an error locating the blob. var errNotFound = errors.New("not found") type memHandler struct { m map[string][]byte lock sync.Mutex } func NewInMemoryBlobHandler() BlobHandler { return &memHandler{m: map[string][]byte{}} } func (m *memHandler) Stat(_ context.Context, _ string, h v1.Hash) (int64, error) { m.lock.Lock() defer m.lock.Unlock() b, found := m.m[h.String()] if !found { return 0, errNotFound } return int64(len(b)), nil } func (m *memHandler) Get(_ context.Context, _ string, h v1.Hash) (io.ReadCloser, error) { m.lock.Lock() defer m.lock.Unlock() b, found := m.m[h.String()] if !found { return nil, errNotFound } return io.NopCloser(bytes.NewReader(b)), nil } func (m *memHandler) Put(_ context.Context, _ string, h v1.Hash, rc io.ReadCloser) error { m.lock.Lock() defer m.lock.Unlock() defer rc.Close() all, err := io.ReadAll(rc) if err != nil { return err } m.m[h.String()] = all return nil } func (m *memHandler) Delete(_ context.Context, _ string, h v1.Hash) error { m.lock.Lock() defer m.lock.Unlock() if _, found := m.m[h.String()]; !found { return errNotFound } delete(m.m, h.String()) return nil } // blobs type blobs struct { blobHandler BlobHandler // Each upload gets a unique id that writes occur to until finalized. uploads map[string][]byte lock sync.Mutex log *log.Logger } func (b *blobs) handle(resp http.ResponseWriter, req *http.Request) *regError { elem := strings.Split(req.URL.Path, "/") elem = elem[1:] if elem[len(elem)-1] == "" { elem = elem[:len(elem)-1] } // Must have a path of form /v2/{name}/blobs/{upload,sha256:} if len(elem) < 4 { return ®Error{ Status: http.StatusBadRequest, Code: "NAME_INVALID", Message: "blobs must be attached to a repo", } } target := elem[len(elem)-1] service := elem[len(elem)-2] digest := req.URL.Query().Get("digest") contentRange := req.Header.Get("Content-Range") repo := req.URL.Host + path.Join(elem[1:len(elem)-2]...) switch req.Method { case http.MethodHead: h, err := v1.NewHash(target) if err != nil { return ®Error{ Status: http.StatusBadRequest, Code: "NAME_INVALID", Message: "invalid digest", } } var size int64 if bsh, ok := b.blobHandler.(BlobStatHandler); ok { size, err = bsh.Stat(req.Context(), repo, h) if errors.Is(err, errNotFound) { return regErrBlobUnknown } else if err != nil { var rerr redirectError if errors.As(err, &rerr) { http.Redirect(resp, req, rerr.Location, rerr.Code) return nil } return regErrInternal(err) } } else { rc, err := b.blobHandler.Get(req.Context(), repo, h) if errors.Is(err, errNotFound) { return regErrBlobUnknown } else if err != nil { var rerr redirectError if errors.As(err, &rerr) { http.Redirect(resp, req, rerr.Location, rerr.Code) return nil } return regErrInternal(err) } defer rc.Close() size, err = io.Copy(io.Discard, rc) if err != nil { return regErrInternal(err) } } resp.Header().Set("Content-Length", fmt.Sprint(size)) resp.Header().Set("Docker-Content-Digest", h.String()) resp.WriteHeader(http.StatusOK) return nil case http.MethodGet: h, err := v1.NewHash(target) if err != nil { return ®Error{ Status: http.StatusBadRequest, Code: "NAME_INVALID", Message: "invalid digest", } } var size int64 var r io.Reader if bsh, ok := b.blobHandler.(BlobStatHandler); ok { size, err = bsh.Stat(req.Context(), repo, h) if errors.Is(err, errNotFound) { return regErrBlobUnknown } else if err != nil { var rerr redirectError if errors.As(err, &rerr) { http.Redirect(resp, req, rerr.Location, rerr.Code) return nil } return regErrInternal(err) } rc, err := b.blobHandler.Get(req.Context(), repo, h) if errors.Is(err, errNotFound) { return regErrBlobUnknown } else if err != nil { var rerr redirectError if errors.As(err, &rerr) { http.Redirect(resp, req, rerr.Location, rerr.Code) return nil } return regErrInternal(err) } defer rc.Close() r = rc } else { tmp, err := b.blobHandler.Get(req.Context(), repo, h) if errors.Is(err, errNotFound) { return regErrBlobUnknown } else if err != nil { var rerr redirectError if errors.As(err, &rerr) { http.Redirect(resp, req, rerr.Location, rerr.Code) return nil } return regErrInternal(err) } defer tmp.Close() var buf bytes.Buffer io.Copy(&buf, tmp) size = int64(buf.Len()) r = &buf } resp.Header().Set("Content-Length", fmt.Sprint(size)) resp.Header().Set("Docker-Content-Digest", h.String()) resp.WriteHeader(http.StatusOK) io.Copy(resp, r) return nil case http.MethodPost: bph, ok := b.blobHandler.(BlobPutHandler) if !ok { return regErrUnsupported } // It is weird that this is "target" instead of "service", but // that's how the index math works out above. if target != "uploads" { return ®Error{ Status: http.StatusBadRequest, Code: "METHOD_UNKNOWN", Message: fmt.Sprintf("POST to /blobs must be followed by /uploads, got %s", target), } } if digest != "" { h, err := v1.NewHash(digest) if err != nil { return regErrDigestInvalid } vrc, err := verify.ReadCloser(req.Body, req.ContentLength, h) if err != nil { return regErrInternal(err) } defer vrc.Close() if err = bph.Put(req.Context(), repo, h, vrc); err != nil { if errors.As(err, &verify.Error{}) { log.Printf("Digest mismatch: %v", err) return regErrDigestMismatch } return regErrInternal(err) } resp.Header().Set("Docker-Content-Digest", h.String()) resp.WriteHeader(http.StatusCreated) return nil } id := fmt.Sprint(rand.Int63()) resp.Header().Set("Location", "/"+path.Join("v2", path.Join(elem[1:len(elem)-2]...), "blobs/uploads", id)) resp.Header().Set("Range", "0-0") resp.WriteHeader(http.StatusAccepted) return nil case http.MethodPatch: if service != "uploads" { return ®Error{ Status: http.StatusBadRequest, Code: "METHOD_UNKNOWN", Message: fmt.Sprintf("PATCH to /blobs must be followed by /uploads, got %s", service), } } if contentRange != "" { start, end := 0, 0 if _, err := fmt.Sscanf(contentRange, "%d-%d", &start, &end); err != nil { return ®Error{ Status: http.StatusRequestedRangeNotSatisfiable, Code: "BLOB_UPLOAD_UNKNOWN", Message: "We don't understand your Content-Range", } } b.lock.Lock() defer b.lock.Unlock() if start != len(b.uploads[target]) { return ®Error{ Status: http.StatusRequestedRangeNotSatisfiable, Code: "BLOB_UPLOAD_UNKNOWN", Message: "Your content range doesn't match what we have", } } l := bytes.NewBuffer(b.uploads[target]) io.Copy(l, req.Body) b.uploads[target] = l.Bytes() resp.Header().Set("Location", "/"+path.Join("v2", path.Join(elem[1:len(elem)-3]...), "blobs/uploads", target)) resp.Header().Set("Range", fmt.Sprintf("0-%d", len(l.Bytes())-1)) resp.WriteHeader(http.StatusNoContent) return nil } b.lock.Lock() defer b.lock.Unlock() if _, ok := b.uploads[target]; ok { return ®Error{ Status: http.StatusBadRequest, Code: "BLOB_UPLOAD_INVALID", Message: "Stream uploads after first write are not allowed", } } l := &bytes.Buffer{} io.Copy(l, req.Body) b.uploads[target] = l.Bytes() resp.Header().Set("Location", "/"+path.Join("v2", path.Join(elem[1:len(elem)-3]...), "blobs/uploads", target)) resp.Header().Set("Range", fmt.Sprintf("0-%d", len(l.Bytes())-1)) resp.WriteHeader(http.StatusNoContent) return nil case http.MethodPut: bph, ok := b.blobHandler.(BlobPutHandler) if !ok { return regErrUnsupported } if service != "uploads" { return ®Error{ Status: http.StatusBadRequest, Code: "METHOD_UNKNOWN", Message: fmt.Sprintf("PUT to /blobs must be followed by /uploads, got %s", service), } } if digest == "" { return ®Error{ Status: http.StatusBadRequest, Code: "DIGEST_INVALID", Message: "digest not specified", } } b.lock.Lock() defer b.lock.Unlock() h, err := v1.NewHash(digest) if err != nil { return ®Error{ Status: http.StatusBadRequest, Code: "NAME_INVALID", Message: "invalid digest", } } defer req.Body.Close() in := io.NopCloser(io.MultiReader(bytes.NewBuffer(b.uploads[target]), req.Body)) size := int64(verify.SizeUnknown) if req.ContentLength > 0 { size = int64(len(b.uploads[target])) + req.ContentLength } vrc, err := verify.ReadCloser(in, size, h) if err != nil { return regErrInternal(err) } defer vrc.Close() if err := bph.Put(req.Context(), repo, h, vrc); err != nil { if errors.As(err, &verify.Error{}) { log.Printf("Digest mismatch: %v", err) return regErrDigestMismatch } return regErrInternal(err) } delete(b.uploads, target) resp.Header().Set("Docker-Content-Digest", h.String()) resp.WriteHeader(http.StatusCreated) return nil case http.MethodDelete: bdh, ok := b.blobHandler.(BlobDeleteHandler) if !ok { return regErrUnsupported } h, err := v1.NewHash(target) if err != nil { return ®Error{ Status: http.StatusBadRequest, Code: "NAME_INVALID", Message: "invalid digest", } } if err := bdh.Delete(req.Context(), repo, h); err != nil { return regErrInternal(err) } resp.WriteHeader(http.StatusAccepted) return nil default: return ®Error{ Status: http.StatusBadRequest, Code: "METHOD_UNKNOWN", Message: "We don't understand your method + url", } } } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/registry/blobs_disk.go ================================================ // Copyright 2023 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package registry import ( "context" "errors" "io" "os" "path/filepath" v1 "github.com/google/go-containerregistry/pkg/v1" ) type diskHandler struct { dir string } func NewDiskBlobHandler(dir string) BlobHandler { return &diskHandler{dir: dir} } func (m *diskHandler) blobHashPath(h v1.Hash) string { return filepath.Join(m.dir, h.Algorithm, h.Hex) } func (m *diskHandler) Stat(_ context.Context, _ string, h v1.Hash) (int64, error) { fi, err := os.Stat(m.blobHashPath(h)) if errors.Is(err, os.ErrNotExist) { return 0, errNotFound } else if err != nil { return 0, err } return fi.Size(), nil } func (m *diskHandler) Get(_ context.Context, _ string, h v1.Hash) (io.ReadCloser, error) { return os.Open(m.blobHashPath(h)) } func (m *diskHandler) Put(_ context.Context, _ string, h v1.Hash, rc io.ReadCloser) error { // Put the temp file in the same directory to avoid cross-device problems // during the os.Rename. The filenames cannot conflict. f, err := os.CreateTemp(m.dir, "upload-*") if err != nil { return err } if err := func() error { defer f.Close() _, err := io.Copy(f, rc) return err }(); err != nil { return err } if err := os.MkdirAll(filepath.Join(m.dir, h.Algorithm), os.ModePerm); err != nil { return err } return os.Rename(f.Name(), m.blobHashPath(h)) } func (m *diskHandler) Delete(_ context.Context, _ string, h v1.Hash) error { return os.Remove(m.blobHashPath(h)) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/registry/error.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package registry import ( "encoding/json" "net/http" ) type regError struct { Status int Code string Message string } func (r *regError) Write(resp http.ResponseWriter) error { resp.WriteHeader(r.Status) type err struct { Code string `json:"code"` Message string `json:"message"` } type wrap struct { Errors []err `json:"errors"` } return json.NewEncoder(resp).Encode(wrap{ Errors: []err{ { Code: r.Code, Message: r.Message, }, }, }) } // regErrInternal returns an internal server error. func regErrInternal(err error) *regError { return ®Error{ Status: http.StatusInternalServerError, Code: "INTERNAL_SERVER_ERROR", Message: err.Error(), } } var regErrBlobUnknown = ®Error{ Status: http.StatusNotFound, Code: "BLOB_UNKNOWN", Message: "Unknown blob", } var regErrUnsupported = ®Error{ Status: http.StatusMethodNotAllowed, Code: "UNSUPPORTED", Message: "Unsupported operation", } var regErrDigestMismatch = ®Error{ Status: http.StatusBadRequest, Code: "DIGEST_INVALID", Message: "digest does not match contents", } var regErrDigestInvalid = ®Error{ Status: http.StatusBadRequest, Code: "NAME_INVALID", Message: "invalid digest", } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/registry/manifest.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package registry import ( "bytes" "encoding/json" "fmt" "io" "log" "net/http" "sort" "strconv" "strings" "sync" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/types" ) type catalog struct { Repos []string `json:"repositories"` } type listTags struct { Name string `json:"name"` Tags []string `json:"tags"` } type manifest struct { contentType string blob []byte } type manifests struct { // maps repo -> manifest tag/digest -> manifest manifests map[string]map[string]manifest lock sync.RWMutex log *log.Logger } func isManifest(req *http.Request) bool { elems := strings.Split(req.URL.Path, "/") elems = elems[1:] if len(elems) < 4 { return false } return elems[len(elems)-2] == "manifests" } func isTags(req *http.Request) bool { elems := strings.Split(req.URL.Path, "/") elems = elems[1:] if len(elems) < 4 { return false } return elems[len(elems)-2] == "tags" } func isCatalog(req *http.Request) bool { elems := strings.Split(req.URL.Path, "/") elems = elems[1:] if len(elems) < 2 { return false } return elems[len(elems)-1] == "_catalog" } // Returns whether this url should be handled by the referrers handler func isReferrers(req *http.Request) bool { elems := strings.Split(req.URL.Path, "/") elems = elems[1:] if len(elems) < 4 { return false } return elems[len(elems)-2] == "referrers" } // https://github.com/opencontainers/distribution-spec/blob/master/spec.md#pulling-an-image-manifest // https://github.com/opencontainers/distribution-spec/blob/master/spec.md#pushing-an-image func (m *manifests) handle(resp http.ResponseWriter, req *http.Request) *regError { elem := strings.Split(req.URL.Path, "/") elem = elem[1:] target := elem[len(elem)-1] repo := strings.Join(elem[1:len(elem)-2], "/") switch req.Method { case http.MethodGet: m.lock.RLock() defer m.lock.RUnlock() c, ok := m.manifests[repo] if !ok { return ®Error{ Status: http.StatusNotFound, Code: "NAME_UNKNOWN", Message: "Unknown name", } } m, ok := c[target] if !ok { return ®Error{ Status: http.StatusNotFound, Code: "MANIFEST_UNKNOWN", Message: "Unknown manifest", } } h, _, _ := v1.SHA256(bytes.NewReader(m.blob)) resp.Header().Set("Docker-Content-Digest", h.String()) resp.Header().Set("Content-Type", m.contentType) resp.Header().Set("Content-Length", fmt.Sprint(len(m.blob))) resp.WriteHeader(http.StatusOK) io.Copy(resp, bytes.NewReader(m.blob)) return nil case http.MethodHead: m.lock.RLock() defer m.lock.RUnlock() if _, ok := m.manifests[repo]; !ok { return ®Error{ Status: http.StatusNotFound, Code: "NAME_UNKNOWN", Message: "Unknown name", } } m, ok := m.manifests[repo][target] if !ok { return ®Error{ Status: http.StatusNotFound, Code: "MANIFEST_UNKNOWN", Message: "Unknown manifest", } } h, _, _ := v1.SHA256(bytes.NewReader(m.blob)) resp.Header().Set("Docker-Content-Digest", h.String()) resp.Header().Set("Content-Type", m.contentType) resp.Header().Set("Content-Length", fmt.Sprint(len(m.blob))) resp.WriteHeader(http.StatusOK) return nil case http.MethodPut: b := &bytes.Buffer{} io.Copy(b, req.Body) h, _, _ := v1.SHA256(bytes.NewReader(b.Bytes())) digest := h.String() mf := manifest{ blob: b.Bytes(), contentType: req.Header.Get("Content-Type"), } // If the manifest is a manifest list, check that the manifest // list's constituent manifests are already uploaded. // This isn't strictly required by the registry API, but some // registries require this. if types.MediaType(mf.contentType).IsIndex() { if err := func() *regError { m.lock.RLock() defer m.lock.RUnlock() im, err := v1.ParseIndexManifest(b) if err != nil { return ®Error{ Status: http.StatusBadRequest, Code: "MANIFEST_INVALID", Message: err.Error(), } } for _, desc := range im.Manifests { if !desc.MediaType.IsDistributable() { continue } if desc.MediaType.IsIndex() || desc.MediaType.IsImage() { if _, found := m.manifests[repo][desc.Digest.String()]; !found { return ®Error{ Status: http.StatusNotFound, Code: "MANIFEST_UNKNOWN", Message: fmt.Sprintf("Sub-manifest %q not found", desc.Digest), } } } else { // TODO: Probably want to do an existence check for blobs. m.log.Printf("TODO: Check blobs for %q", desc.Digest) } } return nil }(); err != nil { return err } } m.lock.Lock() defer m.lock.Unlock() if _, ok := m.manifests[repo]; !ok { m.manifests[repo] = make(map[string]manifest, 2) } // Allow future references by target (tag) and immutable digest. // See https://docs.docker.com/engine/reference/commandline/pull/#pull-an-image-by-digest-immutable-identifier. m.manifests[repo][digest] = mf m.manifests[repo][target] = mf resp.Header().Set("Docker-Content-Digest", digest) resp.WriteHeader(http.StatusCreated) return nil case http.MethodDelete: m.lock.Lock() defer m.lock.Unlock() if _, ok := m.manifests[repo]; !ok { return ®Error{ Status: http.StatusNotFound, Code: "NAME_UNKNOWN", Message: "Unknown name", } } _, ok := m.manifests[repo][target] if !ok { return ®Error{ Status: http.StatusNotFound, Code: "MANIFEST_UNKNOWN", Message: "Unknown manifest", } } delete(m.manifests[repo], target) resp.WriteHeader(http.StatusAccepted) return nil default: return ®Error{ Status: http.StatusBadRequest, Code: "METHOD_UNKNOWN", Message: "We don't understand your method + url", } } } func (m *manifests) handleTags(resp http.ResponseWriter, req *http.Request) *regError { elem := strings.Split(req.URL.Path, "/") elem = elem[1:] repo := strings.Join(elem[1:len(elem)-2], "/") if req.Method == "GET" { m.lock.RLock() defer m.lock.RUnlock() c, ok := m.manifests[repo] if !ok { return ®Error{ Status: http.StatusNotFound, Code: "NAME_UNKNOWN", Message: "Unknown name", } } var tags []string for tag := range c { if !strings.Contains(tag, "sha256:") { tags = append(tags, tag) } } sort.Strings(tags) // https://github.com/opencontainers/distribution-spec/blob/b505e9cc53ec499edbd9c1be32298388921bb705/detail.md#tags-paginated // Offset using last query parameter. if last := req.URL.Query().Get("last"); last != "" { for i, t := range tags { if t > last { tags = tags[i:] break } } } // Limit using n query parameter. if ns := req.URL.Query().Get("n"); ns != "" { if n, err := strconv.Atoi(ns); err != nil { return ®Error{ Status: http.StatusBadRequest, Code: "BAD_REQUEST", Message: fmt.Sprintf("parsing n: %v", err), } } else if n < len(tags) { tags = tags[:n] } } tagsToList := listTags{ Name: repo, Tags: tags, } msg, _ := json.Marshal(tagsToList) resp.Header().Set("Content-Length", fmt.Sprint(len(msg))) resp.WriteHeader(http.StatusOK) io.Copy(resp, bytes.NewReader([]byte(msg))) return nil } return ®Error{ Status: http.StatusBadRequest, Code: "METHOD_UNKNOWN", Message: "We don't understand your method + url", } } func (m *manifests) handleCatalog(resp http.ResponseWriter, req *http.Request) *regError { query := req.URL.Query() nStr := query.Get("n") n := 10000 if nStr != "" { n, _ = strconv.Atoi(nStr) } if req.Method == "GET" { m.lock.RLock() defer m.lock.RUnlock() var repos []string countRepos := 0 // TODO: implement pagination for key := range m.manifests { if countRepos >= n { break } countRepos++ repos = append(repos, key) } repositoriesToList := catalog{ Repos: repos, } msg, _ := json.Marshal(repositoriesToList) resp.Header().Set("Content-Length", fmt.Sprint(len(msg))) resp.WriteHeader(http.StatusOK) io.Copy(resp, bytes.NewReader([]byte(msg))) return nil } return ®Error{ Status: http.StatusBadRequest, Code: "METHOD_UNKNOWN", Message: "We don't understand your method + url", } } // TODO: implement handling of artifactType querystring func (m *manifests) handleReferrers(resp http.ResponseWriter, req *http.Request) *regError { // Ensure this is a GET request if req.Method != "GET" { return ®Error{ Status: http.StatusBadRequest, Code: "METHOD_UNKNOWN", Message: "We don't understand your method + url", } } elem := strings.Split(req.URL.Path, "/") elem = elem[1:] target := elem[len(elem)-1] repo := strings.Join(elem[1:len(elem)-2], "/") // Validate that incoming target is a valid digest if _, err := v1.NewHash(target); err != nil { return ®Error{ Status: http.StatusBadRequest, Code: "UNSUPPORTED", Message: "Target must be a valid digest", } } m.lock.RLock() defer m.lock.RUnlock() digestToManifestMap, repoExists := m.manifests[repo] if !repoExists { return ®Error{ Status: http.StatusNotFound, Code: "NAME_UNKNOWN", Message: "Unknown name", } } im := v1.IndexManifest{ SchemaVersion: 2, MediaType: types.OCIImageIndex, Manifests: []v1.Descriptor{}, } for digest, manifest := range digestToManifestMap { h, err := v1.NewHash(digest) if err != nil { continue } var refPointer struct { Subject *v1.Descriptor `json:"subject"` } json.Unmarshal(manifest.blob, &refPointer) if refPointer.Subject == nil { continue } referenceDigest := refPointer.Subject.Digest if referenceDigest.String() != target { continue } // At this point, we know the current digest references the target var imageAsArtifact struct { Config struct { MediaType string `json:"mediaType"` } `json:"config"` } json.Unmarshal(manifest.blob, &imageAsArtifact) im.Manifests = append(im.Manifests, v1.Descriptor{ MediaType: types.MediaType(manifest.contentType), Size: int64(len(manifest.blob)), Digest: h, ArtifactType: imageAsArtifact.Config.MediaType, }) } msg, _ := json.Marshal(&im) resp.Header().Set("Content-Length", fmt.Sprint(len(msg))) resp.Header().Set("Content-Type", string(types.OCIImageIndex)) resp.WriteHeader(http.StatusOK) io.Copy(resp, bytes.NewReader([]byte(msg))) return nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/registry/registry.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package registry implements a docker V2 registry and the OCI distribution specification. // // It is designed to be used anywhere a low dependency container registry is needed, with an // initial focus on tests. // // Its goal is to be standards compliant and its strictness will increase over time. // // This is currently a low flightmiles system. It's likely quite safe to use in tests; If you're using it // in production, please let us know how and send us CL's for integration tests. package registry import ( "fmt" "log" "math/rand" "net/http" "os" ) type registry struct { log *log.Logger blobs blobs manifests manifests referrersEnabled bool warnings map[float64]string } // https://docs.docker.com/registry/spec/api/#api-version-check // https://github.com/opencontainers/distribution-spec/blob/master/spec.md#api-version-check func (r *registry) v2(resp http.ResponseWriter, req *http.Request) *regError { if r.warnings != nil { rnd := rand.Float64() for prob, msg := range r.warnings { if prob > rnd { resp.Header().Add("Warning", fmt.Sprintf(`299 - "%s"`, msg)) } } } if isBlob(req) { return r.blobs.handle(resp, req) } if isManifest(req) { return r.manifests.handle(resp, req) } if isTags(req) { return r.manifests.handleTags(resp, req) } if isCatalog(req) { return r.manifests.handleCatalog(resp, req) } if r.referrersEnabled && isReferrers(req) { return r.manifests.handleReferrers(resp, req) } resp.Header().Set("Docker-Distribution-API-Version", "registry/2.0") if req.URL.Path != "/v2/" && req.URL.Path != "/v2" { return ®Error{ Status: http.StatusNotFound, Code: "METHOD_UNKNOWN", Message: "We don't understand your method + url", } } resp.WriteHeader(200) return nil } func (r *registry) root(resp http.ResponseWriter, req *http.Request) { if rerr := r.v2(resp, req); rerr != nil { r.log.Printf("%s %s %d %s %s", req.Method, req.URL, rerr.Status, rerr.Code, rerr.Message) rerr.Write(resp) return } r.log.Printf("%s %s", req.Method, req.URL) } // New returns a handler which implements the docker registry protocol. // It should be registered at the site root. func New(opts ...Option) http.Handler { r := ®istry{ log: log.New(os.Stderr, "", log.LstdFlags), blobs: blobs{ blobHandler: &memHandler{m: map[string][]byte{}}, uploads: map[string][]byte{}, log: log.New(os.Stderr, "", log.LstdFlags), }, manifests: manifests{ manifests: map[string]map[string]manifest{}, log: log.New(os.Stderr, "", log.LstdFlags), }, } for _, o := range opts { o(r) } return http.HandlerFunc(r.root) } // Option describes the available options // for creating the registry. type Option func(r *registry) // Logger overrides the logger used to record requests to the registry. func Logger(l *log.Logger) Option { return func(r *registry) { r.log = l r.manifests.log = l r.blobs.log = l } } // WithReferrersSupport enables the referrers API endpoint (OCI 1.1+) func WithReferrersSupport(enabled bool) Option { return func(r *registry) { r.referrersEnabled = enabled } } func WithWarning(prob float64, msg string) Option { return func(r *registry) { if r.warnings == nil { r.warnings = map[float64]string{} } r.warnings[prob] = msg } } func WithBlobHandler(h BlobHandler) Option { return func(r *registry) { r.blobs.blobHandler = h } } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/registry/tls.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package registry import ( "net/http/httptest" ggcrtest "github.com/google/go-containerregistry/internal/httptest" ) // TLS returns an httptest server, with an http client that has been configured to // send all requests to the returned server. The TLS certs are generated for the given domain // which should correspond to the domain the image is stored in. // If you need a transport, Client().Transport is correctly configured. func TLS(domain string) (*httptest.Server, error) { return ggcrtest.NewTLSServer(domain, New()) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/config.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package v1 import ( "encoding/json" "io" "time" ) // ConfigFile is the configuration file that holds the metadata describing // how to launch a container. See: // https://github.com/opencontainers/image-spec/blob/master/config.md // // docker_version and os.version are not part of the spec but included // for backwards compatibility. type ConfigFile struct { Architecture string `json:"architecture"` Author string `json:"author,omitempty"` Container string `json:"container,omitempty"` Created Time `json:"created,omitempty"` DockerVersion string `json:"docker_version,omitempty"` History []History `json:"history,omitempty"` OS string `json:"os"` RootFS RootFS `json:"rootfs"` Config Config `json:"config"` OSVersion string `json:"os.version,omitempty"` Variant string `json:"variant,omitempty"` OSFeatures []string `json:"os.features,omitempty"` } // Platform attempts to generates a Platform from the ConfigFile fields. func (cf *ConfigFile) Platform() *Platform { if cf.OS == "" && cf.Architecture == "" && cf.OSVersion == "" && cf.Variant == "" && len(cf.OSFeatures) == 0 { return nil } return &Platform{ OS: cf.OS, Architecture: cf.Architecture, OSVersion: cf.OSVersion, Variant: cf.Variant, OSFeatures: cf.OSFeatures, } } // History is one entry of a list recording how this container image was built. type History struct { Author string `json:"author,omitempty"` Created Time `json:"created,omitempty"` CreatedBy string `json:"created_by,omitempty"` Comment string `json:"comment,omitempty"` EmptyLayer bool `json:"empty_layer,omitempty"` } // Time is a wrapper around time.Time to help with deep copying type Time struct { time.Time } // DeepCopyInto creates a deep-copy of the Time value. The underlying time.Time // type is effectively immutable in the time API, so it is safe to // copy-by-assign, despite the presence of (unexported) Pointer fields. func (t *Time) DeepCopyInto(out *Time) { *out = *t } // RootFS holds the ordered list of file system deltas that comprise the // container image's root filesystem. type RootFS struct { Type string `json:"type"` DiffIDs []Hash `json:"diff_ids"` } // HealthConfig holds configuration settings for the HEALTHCHECK feature. type HealthConfig struct { // Test is the test to perform to check that the container is healthy. // An empty slice means to inherit the default. // The options are: // {} : inherit healthcheck // {"NONE"} : disable healthcheck // {"CMD", args...} : exec arguments directly // {"CMD-SHELL", command} : run command with system's default shell Test []string `json:",omitempty"` // Zero means to inherit. Durations are expressed as integer nanoseconds. Interval time.Duration `json:",omitempty"` // Interval is the time to wait between checks. Timeout time.Duration `json:",omitempty"` // Timeout is the time to wait before considering the check to have hung. StartPeriod time.Duration `json:",omitempty"` // The start period for the container to initialize before the retries starts to count down. // Retries is the number of consecutive failures needed to consider a container as unhealthy. // Zero means inherit. Retries int `json:",omitempty"` } // Config is a submessage of the config file described as: // // The execution parameters which SHOULD be used as a base when running // a container using the image. // // The names of the fields in this message are chosen to reflect the JSON // payload of the Config as defined here: // https://git.io/vrAET // and // https://github.com/opencontainers/image-spec/blob/master/config.md type Config struct { AttachStderr bool `json:"AttachStderr,omitempty"` AttachStdin bool `json:"AttachStdin,omitempty"` AttachStdout bool `json:"AttachStdout,omitempty"` Cmd []string `json:"Cmd,omitempty"` Healthcheck *HealthConfig `json:"Healthcheck,omitempty"` Domainname string `json:"Domainname,omitempty"` Entrypoint []string `json:"Entrypoint,omitempty"` Env []string `json:"Env,omitempty"` Hostname string `json:"Hostname,omitempty"` Image string `json:"Image,omitempty"` Labels map[string]string `json:"Labels,omitempty"` OnBuild []string `json:"OnBuild,omitempty"` OpenStdin bool `json:"OpenStdin,omitempty"` StdinOnce bool `json:"StdinOnce,omitempty"` Tty bool `json:"Tty,omitempty"` User string `json:"User,omitempty"` Volumes map[string]struct{} `json:"Volumes,omitempty"` WorkingDir string `json:"WorkingDir,omitempty"` ExposedPorts map[string]struct{} `json:"ExposedPorts,omitempty"` ArgsEscaped bool `json:"ArgsEscaped,omitempty"` NetworkDisabled bool `json:"NetworkDisabled,omitempty"` MacAddress string `json:"MacAddress,omitempty"` StopSignal string `json:"StopSignal,omitempty"` Shell []string `json:"Shell,omitempty"` } // ParseConfigFile parses the io.Reader's contents into a ConfigFile. func ParseConfigFile(r io.Reader) (*ConfigFile, error) { cf := ConfigFile{} if err := json.NewDecoder(r).Decode(&cf); err != nil { return nil, err } return &cf, nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/daemon/README.md ================================================ # `daemon` [![GoDoc](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/daemon?status.svg)](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/daemon) The `daemon` package enables reading/writing images from/to the docker daemon. It is not fully fleshed out, but is useful for interoperability, see various issues: * https://github.com/google/go-containerregistry/issues/205 * https://github.com/google/go-containerregistry/issues/552 * https://github.com/google/go-containerregistry/issues/627 ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/daemon/doc.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package daemon provides facilities for reading/writing v1.Image from/to // a running daemon. package daemon ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/daemon/image.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package daemon import ( "bytes" "context" "io" "sync" "time" api "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/tarball" "github.com/google/go-containerregistry/pkg/v1/types" ) type image struct { ref name.Reference opener *imageOpener tarballImage v1.Image computed bool id *v1.Hash configFile *v1.ConfigFile once sync.Once err error } type imageOpener struct { ref name.Reference ctx context.Context buffered bool client Client once sync.Once bytes []byte err error } func (i *imageOpener) saveImage() (io.ReadCloser, error) { return i.client.ImageSave(i.ctx, []string{i.ref.Name()}) } func (i *imageOpener) bufferedOpener() (io.ReadCloser, error) { // Store the tarball in memory and return a new reader into the bytes each time we need to access something. i.once.Do(func() { i.bytes, i.err = func() ([]byte, error) { rc, err := i.saveImage() if err != nil { return nil, err } defer rc.Close() return io.ReadAll(rc) }() }) // Wrap the bytes in a ReadCloser so it looks like an opened file. return io.NopCloser(bytes.NewReader(i.bytes)), i.err } func (i *imageOpener) opener() tarball.Opener { if i.buffered { return i.bufferedOpener } // To avoid storing the tarball in memory, do a save every time we need to access something. return i.saveImage } // Image provides access to an image reference from the Docker daemon, // applying functional options to the underlying imageOpener before // resolving the reference into a v1.Image. func Image(ref name.Reference, options ...Option) (v1.Image, error) { o, err := makeOptions(options...) if err != nil { return nil, err } i := &imageOpener{ ref: ref, buffered: o.buffered, client: o.client, ctx: o.ctx, } img := &image{ ref: ref, opener: i, } // Eagerly fetch Image ID to ensure it actually exists. // https://github.com/google/go-containerregistry/issues/1186 id, err := img.ConfigName() if err != nil { return nil, err } img.id = &id return img, nil } func (i *image) initialize() error { // Don't re-initialize tarball if already initialized. if i.tarballImage == nil { i.once.Do(func() { i.tarballImage, i.err = tarball.Image(i.opener.opener(), nil) }) } return i.err } func (i *image) compute() error { // Don't re-compute if already computed. if i.computed { return nil } inspect, _, err := i.opener.client.ImageInspectWithRaw(i.opener.ctx, i.ref.String()) if err != nil { return err } configFile, err := i.computeConfigFile(inspect) if err != nil { return err } i.configFile = configFile i.computed = true return nil } func (i *image) Layers() ([]v1.Layer, error) { if err := i.initialize(); err != nil { return nil, err } return i.tarballImage.Layers() } func (i *image) MediaType() (types.MediaType, error) { if err := i.initialize(); err != nil { return "", err } return i.tarballImage.MediaType() } func (i *image) Size() (int64, error) { if err := i.initialize(); err != nil { return 0, err } return i.tarballImage.Size() } func (i *image) ConfigName() (v1.Hash, error) { if i.id != nil { return *i.id, nil } res, _, err := i.opener.client.ImageInspectWithRaw(i.opener.ctx, i.ref.String()) if err != nil { return v1.Hash{}, err } return v1.NewHash(res.ID) } func (i *image) ConfigFile() (*v1.ConfigFile, error) { if err := i.compute(); err != nil { return nil, err } return i.configFile.DeepCopy(), nil } func (i *image) RawConfigFile() ([]byte, error) { if err := i.initialize(); err != nil { return nil, err } // RawConfigFile cannot be generated from "docker inspect" because Docker Engine API returns serialized data, // and formatting information of the raw config such as indent and prefix will be lost. return i.tarballImage.RawConfigFile() } func (i *image) Digest() (v1.Hash, error) { if err := i.initialize(); err != nil { return v1.Hash{}, err } return i.tarballImage.Digest() } func (i *image) Manifest() (*v1.Manifest, error) { if err := i.initialize(); err != nil { return nil, err } return i.tarballImage.Manifest() } func (i *image) RawManifest() ([]byte, error) { if err := i.initialize(); err != nil { return nil, err } return i.tarballImage.RawManifest() } func (i *image) LayerByDigest(h v1.Hash) (v1.Layer, error) { if err := i.initialize(); err != nil { return nil, err } return i.tarballImage.LayerByDigest(h) } func (i *image) LayerByDiffID(h v1.Hash) (v1.Layer, error) { if err := i.initialize(); err != nil { return nil, err } return i.tarballImage.LayerByDiffID(h) } func (i *image) configHistory(author string) ([]v1.History, error) { historyItems, err := i.opener.client.ImageHistory(i.opener.ctx, i.ref.String()) if err != nil { return nil, err } history := make([]v1.History, len(historyItems)) for j, h := range historyItems { history[j] = v1.History{ Author: author, Created: v1.Time{ Time: time.Unix(h.Created, 0).UTC(), }, CreatedBy: h.CreatedBy, Comment: h.Comment, EmptyLayer: h.Size == 0, } } return history, nil } func (i *image) diffIDs(rootFS api.RootFS) ([]v1.Hash, error) { diffIDs := make([]v1.Hash, len(rootFS.Layers)) for j, l := range rootFS.Layers { h, err := v1.NewHash(l) if err != nil { return nil, err } diffIDs[j] = h } return diffIDs, nil } func (i *image) computeConfigFile(inspect api.ImageInspect) (*v1.ConfigFile, error) { diffIDs, err := i.diffIDs(inspect.RootFS) if err != nil { return nil, err } history, err := i.configHistory(inspect.Author) if err != nil { return nil, err } created, err := time.Parse(time.RFC3339Nano, inspect.Created) if err != nil { return nil, err } return &v1.ConfigFile{ Architecture: inspect.Architecture, Author: inspect.Author, Container: inspect.Container, Created: v1.Time{Time: created}, DockerVersion: inspect.DockerVersion, History: history, OS: inspect.Os, RootFS: v1.RootFS{ Type: inspect.RootFS.Type, DiffIDs: diffIDs, }, Config: i.computeImageConfig(inspect.Config), OSVersion: inspect.OsVersion, }, nil } func (i *image) computeImageConfig(config *container.Config) v1.Config { if config == nil { return v1.Config{} } c := v1.Config{ AttachStderr: config.AttachStderr, AttachStdin: config.AttachStdin, AttachStdout: config.AttachStdout, Cmd: config.Cmd, Domainname: config.Domainname, Entrypoint: config.Entrypoint, Env: config.Env, Hostname: config.Hostname, Image: config.Image, Labels: config.Labels, OnBuild: config.OnBuild, OpenStdin: config.OpenStdin, StdinOnce: config.StdinOnce, Tty: config.Tty, User: config.User, Volumes: config.Volumes, WorkingDir: config.WorkingDir, ArgsEscaped: config.ArgsEscaped, NetworkDisabled: config.NetworkDisabled, MacAddress: config.MacAddress, StopSignal: config.StopSignal, Shell: config.Shell, } if config.Healthcheck != nil { c.Healthcheck = &v1.HealthConfig{ Test: config.Healthcheck.Test, Interval: config.Healthcheck.Interval, Timeout: config.Healthcheck.Timeout, StartPeriod: config.Healthcheck.StartPeriod, Retries: config.Healthcheck.Retries, } } if len(config.ExposedPorts) > 0 { c.ExposedPorts = map[string]struct{}{} for port := range c.ExposedPorts { c.ExposedPorts[port] = struct{}{} } } return c } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/daemon/options.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package daemon import ( "context" "io" "github.com/docker/docker/api/types" api "github.com/docker/docker/api/types/image" "github.com/docker/docker/client" ) // ImageOption is an alias for Option. // Deprecated: Use Option instead. type ImageOption Option // Option is a functional option for daemon operations. type Option func(*options) type options struct { ctx context.Context client Client buffered bool } var defaultClient = func() (Client, error) { return client.NewClientWithOpts(client.FromEnv) } func makeOptions(opts ...Option) (*options, error) { o := &options{ buffered: true, ctx: context.Background(), } for _, opt := range opts { opt(o) } if o.client == nil { client, err := defaultClient() if err != nil { return nil, err } o.client = client } o.client.NegotiateAPIVersion(o.ctx) return o, nil } // WithBufferedOpener buffers the image. func WithBufferedOpener() Option { return func(o *options) { o.buffered = true } } // WithUnbufferedOpener streams the image to avoid buffering. func WithUnbufferedOpener() Option { return func(o *options) { o.buffered = false } } // WithClient is a functional option to allow injecting a docker client. // // By default, github.com/docker/docker/client.FromEnv is used. func WithClient(client Client) Option { return func(o *options) { o.client = client } } // WithContext is a functional option to pass through a context.Context. // // By default, context.Background() is used. func WithContext(ctx context.Context) Option { return func(o *options) { o.ctx = ctx } } // Client represents the subset of a docker client that the daemon // package uses. type Client interface { NegotiateAPIVersion(ctx context.Context) ImageSave(context.Context, []string) (io.ReadCloser, error) ImageLoad(context.Context, io.Reader, bool) (types.ImageLoadResponse, error) ImageTag(context.Context, string, string) error ImageInspectWithRaw(context.Context, string) (types.ImageInspect, []byte, error) ImageHistory(context.Context, string) ([]api.HistoryResponseItem, error) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/daemon/write.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package daemon import ( "fmt" "io" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/tarball" ) // Tag adds a tag to an already existent image. func Tag(src, dest name.Tag, options ...Option) error { o, err := makeOptions(options...) if err != nil { return err } return o.client.ImageTag(o.ctx, src.String(), dest.String()) } // Write saves the image into the daemon as the given tag. func Write(tag name.Tag, img v1.Image, options ...Option) (string, error) { o, err := makeOptions(options...) if err != nil { return "", err } // If we already have this image by this image ID, we can skip loading it. id, err := img.ConfigName() if err != nil { return "", fmt.Errorf("computing image ID: %w", err) } if resp, _, err := o.client.ImageInspectWithRaw(o.ctx, id.String()); err == nil { want := tag.String() // If we already have this tag, we can skip tagging it. for _, have := range resp.RepoTags { if have == want { return "", nil } } return "", o.client.ImageTag(o.ctx, id.String(), want) } pr, pw := io.Pipe() go func() { pw.CloseWithError(tarball.Write(tag, img, pw)) }() // write the image in docker save format first, then load it resp, err := o.client.ImageLoad(o.ctx, pr, false) if err != nil { return "", fmt.Errorf("error loading image: %w", err) } defer resp.Body.Close() b, err := io.ReadAll(resp.Body) response := string(b) if err != nil { return response, fmt.Errorf("error reading load response body: %w", err) } return response, nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/doc.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // +k8s:deepcopy-gen=package // Package v1 defines structured types for OCI v1 images package v1 ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/empty/README.md ================================================ # `empty` [![GoDoc](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/empty?status.svg)](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/empty) The empty packages provides an empty base for constructing a `v1.Image` or `v1.ImageIndex`. This is especially useful when paired with the [`mutate`](/pkg/v1/mutate) package, see [`mutate.Append`](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/mutate#Append) and [`mutate.AppendManifests`](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/mutate#AppendManifests). ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/empty/doc.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package empty provides an implementation of v1.Image equivalent to "FROM scratch". package empty ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/empty/image.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package empty import ( "fmt" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/partial" "github.com/google/go-containerregistry/pkg/v1/types" ) // Image is a singleton empty image, think: FROM scratch. var Image, _ = partial.UncompressedToImage(emptyImage{}) type emptyImage struct{} // MediaType implements partial.UncompressedImageCore. func (i emptyImage) MediaType() (types.MediaType, error) { return types.DockerManifestSchema2, nil } // RawConfigFile implements partial.UncompressedImageCore. func (i emptyImage) RawConfigFile() ([]byte, error) { return partial.RawConfigFile(i) } // ConfigFile implements v1.Image. func (i emptyImage) ConfigFile() (*v1.ConfigFile, error) { return &v1.ConfigFile{ RootFS: v1.RootFS{ // Some clients check this. Type: "layers", }, }, nil } func (i emptyImage) LayerByDiffID(h v1.Hash) (partial.UncompressedLayer, error) { return nil, fmt.Errorf("LayerByDiffID(%s): empty image", h) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/empty/index.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package empty import ( "encoding/json" "errors" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/partial" "github.com/google/go-containerregistry/pkg/v1/types" ) // Index is a singleton empty index, think: FROM scratch. var Index = emptyIndex{} type emptyIndex struct{} func (i emptyIndex) MediaType() (types.MediaType, error) { return types.OCIImageIndex, nil } func (i emptyIndex) Digest() (v1.Hash, error) { return partial.Digest(i) } func (i emptyIndex) Size() (int64, error) { return partial.Size(i) } func (i emptyIndex) IndexManifest() (*v1.IndexManifest, error) { return base(), nil } func (i emptyIndex) RawManifest() ([]byte, error) { return json.Marshal(base()) } func (i emptyIndex) Image(v1.Hash) (v1.Image, error) { return nil, errors.New("empty index") } func (i emptyIndex) ImageIndex(v1.Hash) (v1.ImageIndex, error) { return nil, errors.New("empty index") } func base() *v1.IndexManifest { return &v1.IndexManifest{ SchemaVersion: 2, MediaType: types.OCIImageIndex, Manifests: []v1.Descriptor{}, } } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/hash.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package v1 import ( "crypto" "encoding/hex" "encoding/json" "fmt" "hash" "io" "strconv" "strings" ) // Hash is an unqualified digest of some content, e.g. sha256:deadbeef type Hash struct { // Algorithm holds the algorithm used to compute the hash. Algorithm string // Hex holds the hex portion of the content hash. Hex string } // String reverses NewHash returning the string-form of the hash. func (h Hash) String() string { return fmt.Sprintf("%s:%s", h.Algorithm, h.Hex) } // NewHash validates the input string is a hash and returns a strongly type Hash object. func NewHash(s string) (Hash, error) { h := Hash{} if err := h.parse(s); err != nil { return Hash{}, err } return h, nil } // MarshalJSON implements json.Marshaler func (h Hash) MarshalJSON() ([]byte, error) { return json.Marshal(h.String()) } // UnmarshalJSON implements json.Unmarshaler func (h *Hash) UnmarshalJSON(data []byte) error { s, err := strconv.Unquote(string(data)) if err != nil { return err } return h.parse(s) } // MarshalText implements encoding.TextMarshaler. This is required to use // v1.Hash as a key in a map when marshalling JSON. func (h Hash) MarshalText() (text []byte, err error) { return []byte(h.String()), nil } // UnmarshalText implements encoding.TextUnmarshaler. This is required to use // v1.Hash as a key in a map when unmarshalling JSON. func (h *Hash) UnmarshalText(text []byte) error { return h.parse(string(text)) } // Hasher returns a hash.Hash for the named algorithm (e.g. "sha256") func Hasher(name string) (hash.Hash, error) { switch name { case "sha256": return crypto.SHA256.New(), nil default: return nil, fmt.Errorf("unsupported hash: %q", name) } } func (h *Hash) parse(unquoted string) error { parts := strings.Split(unquoted, ":") if len(parts) != 2 { return fmt.Errorf("cannot parse hash: %q", unquoted) } rest := strings.TrimLeft(parts[1], "0123456789abcdef") if len(rest) != 0 { return fmt.Errorf("found non-hex character in hash: %c", rest[0]) } hasher, err := Hasher(parts[0]) if err != nil { return err } // Compare the hex to the expected size (2 hex characters per byte) if len(parts[1]) != hasher.Size()*2 { return fmt.Errorf("wrong number of hex digits for %s: %s", parts[0], parts[1]) } h.Algorithm = parts[0] h.Hex = parts[1] return nil } // SHA256 computes the Hash of the provided io.Reader's content. func SHA256(r io.Reader) (Hash, int64, error) { hasher := crypto.SHA256.New() n, err := io.Copy(hasher, r) if err != nil { return Hash{}, 0, err } return Hash{ Algorithm: "sha256", Hex: hex.EncodeToString(hasher.Sum(make([]byte, 0, hasher.Size()))), }, n, nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/image.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package v1 import ( "github.com/google/go-containerregistry/pkg/v1/types" ) // Image defines the interface for interacting with an OCI v1 image. type Image interface { // Layers returns the ordered collection of filesystem layers that comprise this image. // The order of the list is oldest/base layer first, and most-recent/top layer last. Layers() ([]Layer, error) // MediaType of this image's manifest. MediaType() (types.MediaType, error) // Size returns the size of the manifest. Size() (int64, error) // ConfigName returns the hash of the image's config file, also known as // the Image ID. ConfigName() (Hash, error) // ConfigFile returns this image's config file. ConfigFile() (*ConfigFile, error) // RawConfigFile returns the serialized bytes of ConfigFile(). RawConfigFile() ([]byte, error) // Digest returns the sha256 of this image's manifest. Digest() (Hash, error) // Manifest returns this image's Manifest object. Manifest() (*Manifest, error) // RawManifest returns the serialized bytes of Manifest() RawManifest() ([]byte, error) // LayerByDigest returns a Layer for interacting with a particular layer of // the image, looking it up by "digest" (the compressed hash). LayerByDigest(Hash) (Layer, error) // LayerByDiffID is an analog to LayerByDigest, looking up by "diff id" // (the uncompressed hash). LayerByDiffID(Hash) (Layer, error) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/index.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package v1 import ( "github.com/google/go-containerregistry/pkg/v1/types" ) // ImageIndex defines the interface for interacting with an OCI image index. type ImageIndex interface { // MediaType of this image's manifest. MediaType() (types.MediaType, error) // Digest returns the sha256 of this index's manifest. Digest() (Hash, error) // Size returns the size of the manifest. Size() (int64, error) // IndexManifest returns this image index's manifest object. IndexManifest() (*IndexManifest, error) // RawManifest returns the serialized bytes of IndexManifest(). RawManifest() ([]byte, error) // Image returns a v1.Image that this ImageIndex references. Image(Hash) (Image, error) // ImageIndex returns a v1.ImageIndex that this ImageIndex references. ImageIndex(Hash) (ImageIndex, error) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/layer.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package v1 import ( "io" "github.com/google/go-containerregistry/pkg/v1/types" ) // Layer is an interface for accessing the properties of a particular layer of a v1.Image type Layer interface { // Digest returns the Hash of the compressed layer. Digest() (Hash, error) // DiffID returns the Hash of the uncompressed layer. DiffID() (Hash, error) // Compressed returns an io.ReadCloser for the compressed layer contents. Compressed() (io.ReadCloser, error) // Uncompressed returns an io.ReadCloser for the uncompressed layer contents. Uncompressed() (io.ReadCloser, error) // Size returns the compressed size of the Layer. Size() (int64, error) // MediaType returns the media type of the Layer. MediaType() (types.MediaType, error) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/layout/README.md ================================================ # `layout` [![GoDoc](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/layout?status.svg)](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/layout) The `layout` package implements support for interacting with an [OCI Image Layout](https://github.com/opencontainers/image-spec/blob/master/image-layout.md). ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/layout/blob.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package layout import ( "io" "os" v1 "github.com/google/go-containerregistry/pkg/v1" ) // Blob returns a blob with the given hash from the Path. func (l Path) Blob(h v1.Hash) (io.ReadCloser, error) { return os.Open(l.blobPath(h)) } // Bytes is a convenience function to return a blob from the Path as // a byte slice. func (l Path) Bytes(h v1.Hash) ([]byte, error) { return os.ReadFile(l.blobPath(h)) } func (l Path) blobPath(h v1.Hash) string { return l.path("blobs", h.Algorithm, h.Hex) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/layout/doc.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package layout provides facilities for reading/writing artifacts from/to // an OCI image layout on disk, see: // // https://github.com/opencontainers/image-spec/blob/master/image-layout.md package layout ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/layout/gc.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // This is an EXPERIMENTAL package, and may change in arbitrary ways without notice. package layout import ( "fmt" "io/fs" "path/filepath" "strings" v1 "github.com/google/go-containerregistry/pkg/v1" ) // GarbageCollect removes unreferenced blobs from the oci-layout // // This is an experimental api, and not subject to any stability guarantees // We may abandon it at any time, without prior notice. // Deprecated: Use it at your own risk! func (l Path) GarbageCollect() ([]v1.Hash, error) { idx, err := l.ImageIndex() if err != nil { return nil, err } blobsToKeep := map[string]bool{} if err := l.garbageCollectImageIndex(idx, blobsToKeep); err != nil { return nil, err } blobsDir := l.path("blobs") removedBlobs := []v1.Hash{} err = filepath.WalkDir(blobsDir, func(path string, d fs.DirEntry, err error) error { if err != nil { return err } if d.IsDir() { return nil } rel, err := filepath.Rel(blobsDir, path) if err != nil { return err } hashString := strings.Replace(rel, "/", ":", 1) if present := blobsToKeep[hashString]; !present { h, err := v1.NewHash(hashString) if err != nil { return err } removedBlobs = append(removedBlobs, h) } return nil }) if err != nil { return nil, err } return removedBlobs, nil } func (l Path) garbageCollectImageIndex(index v1.ImageIndex, blobsToKeep map[string]bool) error { idxm, err := index.IndexManifest() if err != nil { return err } h, err := index.Digest() if err != nil { return err } blobsToKeep[h.String()] = true for _, descriptor := range idxm.Manifests { if descriptor.MediaType.IsImage() { img, err := index.Image(descriptor.Digest) if err != nil { return err } if err := l.garbageCollectImage(img, blobsToKeep); err != nil { return err } } else if descriptor.MediaType.IsIndex() { idx, err := index.ImageIndex(descriptor.Digest) if err != nil { return err } if err := l.garbageCollectImageIndex(idx, blobsToKeep); err != nil { return err } } else { return fmt.Errorf("gc: unknown media type: %s", descriptor.MediaType) } } return nil } func (l Path) garbageCollectImage(image v1.Image, blobsToKeep map[string]bool) error { h, err := image.Digest() if err != nil { return err } blobsToKeep[h.String()] = true h, err = image.ConfigName() if err != nil { return err } blobsToKeep[h.String()] = true ls, err := image.Layers() if err != nil { return err } for _, l := range ls { h, err := l.Digest() if err != nil { return err } blobsToKeep[h.String()] = true } return nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/layout/image.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package layout import ( "fmt" "io" "os" "sync" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/partial" "github.com/google/go-containerregistry/pkg/v1/types" ) type layoutImage struct { path Path desc v1.Descriptor manifestLock sync.Mutex // Protects rawManifest rawManifest []byte } var _ partial.CompressedImageCore = (*layoutImage)(nil) // Image reads a v1.Image with digest h from the Path. func (l Path) Image(h v1.Hash) (v1.Image, error) { ii, err := l.ImageIndex() if err != nil { return nil, err } return ii.Image(h) } func (li *layoutImage) MediaType() (types.MediaType, error) { return li.desc.MediaType, nil } // Implements WithManifest for partial.Blobset. func (li *layoutImage) Manifest() (*v1.Manifest, error) { return partial.Manifest(li) } func (li *layoutImage) RawManifest() ([]byte, error) { li.manifestLock.Lock() defer li.manifestLock.Unlock() if li.rawManifest != nil { return li.rawManifest, nil } b, err := li.path.Bytes(li.desc.Digest) if err != nil { return nil, err } li.rawManifest = b return li.rawManifest, nil } func (li *layoutImage) RawConfigFile() ([]byte, error) { manifest, err := li.Manifest() if err != nil { return nil, err } return li.path.Bytes(manifest.Config.Digest) } func (li *layoutImage) LayerByDigest(h v1.Hash) (partial.CompressedLayer, error) { manifest, err := li.Manifest() if err != nil { return nil, err } if h == manifest.Config.Digest { return &compressedBlob{ path: li.path, desc: manifest.Config, }, nil } for _, desc := range manifest.Layers { if h == desc.Digest { return &compressedBlob{ path: li.path, desc: desc, }, nil } } return nil, fmt.Errorf("could not find layer in image: %s", h) } type compressedBlob struct { path Path desc v1.Descriptor } func (b *compressedBlob) Digest() (v1.Hash, error) { return b.desc.Digest, nil } func (b *compressedBlob) Compressed() (io.ReadCloser, error) { return b.path.Blob(b.desc.Digest) } func (b *compressedBlob) Size() (int64, error) { return b.desc.Size, nil } func (b *compressedBlob) MediaType() (types.MediaType, error) { return b.desc.MediaType, nil } // Descriptor implements partial.withDescriptor. func (b *compressedBlob) Descriptor() (*v1.Descriptor, error) { return &b.desc, nil } // See partial.Exists. func (b *compressedBlob) Exists() (bool, error) { _, err := os.Stat(b.path.blobPath(b.desc.Digest)) if os.IsNotExist(err) { return false, nil } return err == nil, err } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/layout/index.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package layout import ( "encoding/json" "errors" "fmt" "io" "os" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/partial" "github.com/google/go-containerregistry/pkg/v1/types" ) var _ v1.ImageIndex = (*layoutIndex)(nil) type layoutIndex struct { mediaType types.MediaType path Path rawIndex []byte } // ImageIndexFromPath is a convenience function which constructs a Path and returns its v1.ImageIndex. func ImageIndexFromPath(path string) (v1.ImageIndex, error) { lp, err := FromPath(path) if err != nil { return nil, err } return lp.ImageIndex() } // ImageIndex returns a v1.ImageIndex for the Path. func (l Path) ImageIndex() (v1.ImageIndex, error) { rawIndex, err := os.ReadFile(l.path("index.json")) if err != nil { return nil, err } idx := &layoutIndex{ mediaType: types.OCIImageIndex, path: l, rawIndex: rawIndex, } return idx, nil } func (i *layoutIndex) MediaType() (types.MediaType, error) { return i.mediaType, nil } func (i *layoutIndex) Digest() (v1.Hash, error) { return partial.Digest(i) } func (i *layoutIndex) Size() (int64, error) { return partial.Size(i) } func (i *layoutIndex) IndexManifest() (*v1.IndexManifest, error) { var index v1.IndexManifest err := json.Unmarshal(i.rawIndex, &index) return &index, err } func (i *layoutIndex) RawManifest() ([]byte, error) { return i.rawIndex, nil } func (i *layoutIndex) Image(h v1.Hash) (v1.Image, error) { // Look up the digest in our manifest first to return a better error. desc, err := i.findDescriptor(h) if err != nil { return nil, err } if !isExpectedMediaType(desc.MediaType, types.OCIManifestSchema1, types.DockerManifestSchema2) { return nil, fmt.Errorf("unexpected media type for %v: %s", h, desc.MediaType) } img := &layoutImage{ path: i.path, desc: *desc, } return partial.CompressedToImage(img) } func (i *layoutIndex) ImageIndex(h v1.Hash) (v1.ImageIndex, error) { // Look up the digest in our manifest first to return a better error. desc, err := i.findDescriptor(h) if err != nil { return nil, err } if !isExpectedMediaType(desc.MediaType, types.OCIImageIndex, types.DockerManifestList) { return nil, fmt.Errorf("unexpected media type for %v: %s", h, desc.MediaType) } rawIndex, err := i.path.Bytes(h) if err != nil { return nil, err } return &layoutIndex{ mediaType: desc.MediaType, path: i.path, rawIndex: rawIndex, }, nil } func (i *layoutIndex) Blob(h v1.Hash) (io.ReadCloser, error) { return i.path.Blob(h) } func (i *layoutIndex) findDescriptor(h v1.Hash) (*v1.Descriptor, error) { im, err := i.IndexManifest() if err != nil { return nil, err } if h == (v1.Hash{}) { if len(im.Manifests) != 1 { return nil, errors.New("oci layout must contain only a single image to be used with layout.Image") } return &(im.Manifests)[0], nil } for _, desc := range im.Manifests { if desc.Digest == h { return &desc, nil } } return nil, fmt.Errorf("could not find descriptor in index: %s", h) } // TODO: Pull this out into methods on types.MediaType? e.g. instead, have: // * mt.IsIndex() // * mt.IsImage() func isExpectedMediaType(mt types.MediaType, expected ...types.MediaType) bool { for _, allowed := range expected { if mt == allowed { return true } } return false } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/layout/layoutpath.go ================================================ // Copyright 2019 The original author or authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package layout import "path/filepath" // Path represents an OCI image layout rooted in a file system path type Path string func (l Path) path(elem ...string) string { complete := []string{string(l)} return filepath.Join(append(complete, elem...)...) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/layout/options.go ================================================ // Copyright 2019 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package layout import v1 "github.com/google/go-containerregistry/pkg/v1" // Option is a functional option for Layout. type Option func(*options) type options struct { descOpts []descriptorOption } func makeOptions(opts ...Option) *options { o := &options{ descOpts: []descriptorOption{}, } for _, apply := range opts { apply(o) } return o } type descriptorOption func(*v1.Descriptor) // WithAnnotations adds annotations to the artifact descriptor. func WithAnnotations(annotations map[string]string) Option { return func(o *options) { o.descOpts = append(o.descOpts, func(desc *v1.Descriptor) { if desc.Annotations == nil { desc.Annotations = make(map[string]string) } for k, v := range annotations { desc.Annotations[k] = v } }) } } // WithURLs adds urls to the artifact descriptor. func WithURLs(urls []string) Option { return func(o *options) { o.descOpts = append(o.descOpts, func(desc *v1.Descriptor) { if desc.URLs == nil { desc.URLs = []string{} } desc.URLs = append(desc.URLs, urls...) }) } } // WithPlatform sets the platform of the artifact descriptor. func WithPlatform(platform v1.Platform) Option { return func(o *options) { o.descOpts = append(o.descOpts, func(desc *v1.Descriptor) { desc.Platform = &platform }) } } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/layout/read.go ================================================ // Copyright 2019 The original author or authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package layout import ( "os" "path/filepath" ) // FromPath reads an OCI image layout at path and constructs a layout.Path. func FromPath(path string) (Path, error) { // TODO: check oci-layout exists _, err := os.Stat(filepath.Join(path, "index.json")) if err != nil { return "", err } return Path(path), nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/layout/write.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package layout import ( "bytes" "encoding/json" "errors" "fmt" "io" "os" "path/filepath" "github.com/google/go-containerregistry/pkg/logs" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/match" "github.com/google/go-containerregistry/pkg/v1/mutate" "github.com/google/go-containerregistry/pkg/v1/partial" "github.com/google/go-containerregistry/pkg/v1/stream" "github.com/google/go-containerregistry/pkg/v1/types" "golang.org/x/sync/errgroup" ) var layoutFile = `{ "imageLayoutVersion": "1.0.0" }` // AppendImage writes a v1.Image to the Path and updates // the index.json to reference it. func (l Path) AppendImage(img v1.Image, options ...Option) error { if err := l.WriteImage(img); err != nil { return err } desc, err := partial.Descriptor(img) if err != nil { return err } o := makeOptions(options...) for _, opt := range o.descOpts { opt(desc) } return l.AppendDescriptor(*desc) } // AppendIndex writes a v1.ImageIndex to the Path and updates // the index.json to reference it. func (l Path) AppendIndex(ii v1.ImageIndex, options ...Option) error { if err := l.WriteIndex(ii); err != nil { return err } desc, err := partial.Descriptor(ii) if err != nil { return err } o := makeOptions(options...) for _, opt := range o.descOpts { opt(desc) } return l.AppendDescriptor(*desc) } // AppendDescriptor adds a descriptor to the index.json of the Path. func (l Path) AppendDescriptor(desc v1.Descriptor) error { ii, err := l.ImageIndex() if err != nil { return err } index, err := ii.IndexManifest() if err != nil { return err } index.Manifests = append(index.Manifests, desc) rawIndex, err := json.MarshalIndent(index, "", " ") if err != nil { return err } return l.WriteFile("index.json", rawIndex, os.ModePerm) } // ReplaceImage writes a v1.Image to the Path and updates // the index.json to reference it, replacing any existing one that matches matcher, if found. func (l Path) ReplaceImage(img v1.Image, matcher match.Matcher, options ...Option) error { if err := l.WriteImage(img); err != nil { return err } return l.replaceDescriptor(img, matcher, options...) } // ReplaceIndex writes a v1.ImageIndex to the Path and updates // the index.json to reference it, replacing any existing one that matches matcher, if found. func (l Path) ReplaceIndex(ii v1.ImageIndex, matcher match.Matcher, options ...Option) error { if err := l.WriteIndex(ii); err != nil { return err } return l.replaceDescriptor(ii, matcher, options...) } // replaceDescriptor adds a descriptor to the index.json of the Path, replacing // any one matching matcher, if found. func (l Path) replaceDescriptor(append mutate.Appendable, matcher match.Matcher, options ...Option) error { ii, err := l.ImageIndex() if err != nil { return err } desc, err := partial.Descriptor(append) if err != nil { return err } o := makeOptions(options...) for _, opt := range o.descOpts { opt(desc) } add := mutate.IndexAddendum{ Add: append, Descriptor: *desc, } ii = mutate.AppendManifests(mutate.RemoveManifests(ii, matcher), add) index, err := ii.IndexManifest() if err != nil { return err } rawIndex, err := json.MarshalIndent(index, "", " ") if err != nil { return err } return l.WriteFile("index.json", rawIndex, os.ModePerm) } // RemoveDescriptors removes any descriptors that match the match.Matcher from the index.json of the Path. func (l Path) RemoveDescriptors(matcher match.Matcher) error { ii, err := l.ImageIndex() if err != nil { return err } ii = mutate.RemoveManifests(ii, matcher) index, err := ii.IndexManifest() if err != nil { return err } rawIndex, err := json.MarshalIndent(index, "", " ") if err != nil { return err } return l.WriteFile("index.json", rawIndex, os.ModePerm) } // WriteFile write a file with arbitrary data at an arbitrary location in a v1 // layout. Used mostly internally to write files like "oci-layout" and // "index.json", also can be used to write other arbitrary files. Do *not* use // this to write blobs. Use only WriteBlob() for that. func (l Path) WriteFile(name string, data []byte, perm os.FileMode) error { if err := os.MkdirAll(l.path(), os.ModePerm); err != nil && !os.IsExist(err) { return err } return os.WriteFile(l.path(name), data, perm) } // WriteBlob copies a file to the blobs/ directory in the Path from the given ReadCloser at // blobs/{hash.Algorithm}/{hash.Hex}. func (l Path) WriteBlob(hash v1.Hash, r io.ReadCloser) error { return l.writeBlob(hash, -1, r, nil) } func (l Path) writeBlob(hash v1.Hash, size int64, rc io.ReadCloser, renamer func() (v1.Hash, error)) error { defer rc.Close() if hash.Hex == "" && renamer == nil { panic("writeBlob called an invalid hash and no renamer") } dir := l.path("blobs", hash.Algorithm) if err := os.MkdirAll(dir, os.ModePerm); err != nil && !os.IsExist(err) { return err } // Check if blob already exists and is the correct size file := filepath.Join(dir, hash.Hex) if s, err := os.Stat(file); err == nil && !s.IsDir() && (s.Size() == size || size == -1) { return nil } // If a renamer func was provided write to a temporary file open := func() (*os.File, error) { return os.Create(file) } if renamer != nil { open = func() (*os.File, error) { return os.CreateTemp(dir, hash.Hex) } } w, err := open() if err != nil { return err } if renamer != nil { // Delete temp file if an error is encountered before renaming defer func() { if err := os.Remove(w.Name()); err != nil && !errors.Is(err, os.ErrNotExist) { logs.Warn.Printf("error removing temporary file after encountering an error while writing blob: %v", err) } }() } defer w.Close() // Write to file and exit if not renaming if n, err := io.Copy(w, rc); err != nil || renamer == nil { return err } else if size != -1 && n != size { return fmt.Errorf("expected blob size %d, but only wrote %d", size, n) } // Always close reader before renaming, since Close computes the digest in // the case of streaming layers. If Close is not called explicitly, it will // occur in a goroutine that is not guaranteed to succeed before renamer is // called. When renamer is the layer's Digest method, it can return // ErrNotComputed. if err := rc.Close(); err != nil { return err } // Always close file before renaming if err := w.Close(); err != nil { return err } // Rename file based on the final hash finalHash, err := renamer() if err != nil { return fmt.Errorf("error getting final digest of layer: %w", err) } renamePath := l.path("blobs", finalHash.Algorithm, finalHash.Hex) return os.Rename(w.Name(), renamePath) } // writeLayer writes the compressed layer to a blob. Unlike WriteBlob it will // write to a temporary file (suffixed with .tmp) within the layout until the // compressed reader is fully consumed and written to disk. Also unlike // WriteBlob, it will not skip writing and exit without error when a blob file // exists, but does not have the correct size. (The blob hash is not // considered, because it may be expensive to compute.) func (l Path) writeLayer(layer v1.Layer) error { d, err := layer.Digest() if errors.Is(err, stream.ErrNotComputed) { // Allow digest errors, since streams may not have calculated the hash // yet. Instead, use an empty value, which will be transformed into a // random file name with `os.CreateTemp` and the final digest will be // calculated after writing to a temp file and before renaming to the // final path. d = v1.Hash{Algorithm: "sha256", Hex: ""} } else if err != nil { return err } s, err := layer.Size() if errors.Is(err, stream.ErrNotComputed) { // Allow size errors, since streams may not have calculated the size // yet. Instead, use zero as a sentinel value meaning that no size // comparison can be done and any sized blob file should be considered // valid and not overwritten. // // TODO: Provide an option to always overwrite blobs. s = -1 } else if err != nil { return err } r, err := layer.Compressed() if err != nil { return err } if err := l.writeBlob(d, s, r, layer.Digest); err != nil { return fmt.Errorf("error writing layer: %w", err) } return nil } // RemoveBlob removes a file from the blobs directory in the Path // at blobs/{hash.Algorithm}/{hash.Hex} // It does *not* remove any reference to it from other manifests or indexes, or // from the root index.json. func (l Path) RemoveBlob(hash v1.Hash) error { dir := l.path("blobs", hash.Algorithm) err := os.Remove(filepath.Join(dir, hash.Hex)) if err != nil && !os.IsNotExist(err) { return err } return nil } // WriteImage writes an image, including its manifest, config and all of its // layers, to the blobs directory. If any blob already exists, as determined by // the hash filename, does not write it. // This function does *not* update the `index.json` file. If you want to write the // image and also update the `index.json`, call AppendImage(), which wraps this // and also updates the `index.json`. func (l Path) WriteImage(img v1.Image) error { layers, err := img.Layers() if err != nil { return err } // Write the layers concurrently. var g errgroup.Group for _, layer := range layers { layer := layer g.Go(func() error { return l.writeLayer(layer) }) } if err := g.Wait(); err != nil { return err } // Write the config. cfgName, err := img.ConfigName() if err != nil { return err } cfgBlob, err := img.RawConfigFile() if err != nil { return err } if err := l.WriteBlob(cfgName, io.NopCloser(bytes.NewReader(cfgBlob))); err != nil { return err } // Write the img manifest. d, err := img.Digest() if err != nil { return err } manifest, err := img.RawManifest() if err != nil { return err } return l.WriteBlob(d, io.NopCloser(bytes.NewReader(manifest))) } type withLayer interface { Layer(v1.Hash) (v1.Layer, error) } type withBlob interface { Blob(v1.Hash) (io.ReadCloser, error) } func (l Path) writeIndexToFile(indexFile string, ii v1.ImageIndex) error { index, err := ii.IndexManifest() if err != nil { return err } // Walk the descriptors and write any v1.Image or v1.ImageIndex that we find. // If we come across something we don't expect, just write it as a blob. for _, desc := range index.Manifests { switch desc.MediaType { case types.OCIImageIndex, types.DockerManifestList: ii, err := ii.ImageIndex(desc.Digest) if err != nil { return err } if err := l.WriteIndex(ii); err != nil { return err } case types.OCIManifestSchema1, types.DockerManifestSchema2: img, err := ii.Image(desc.Digest) if err != nil { return err } if err := l.WriteImage(img); err != nil { return err } default: // TODO: The layout could reference arbitrary things, which we should // probably just pass through. var blob io.ReadCloser // Workaround for #819. if wl, ok := ii.(withLayer); ok { layer, lerr := wl.Layer(desc.Digest) if lerr != nil { return lerr } blob, err = layer.Compressed() } else if wb, ok := ii.(withBlob); ok { blob, err = wb.Blob(desc.Digest) } if err != nil { return err } if err := l.WriteBlob(desc.Digest, blob); err != nil { return err } } } rawIndex, err := ii.RawManifest() if err != nil { return err } return l.WriteFile(indexFile, rawIndex, os.ModePerm) } // WriteIndex writes an index to the blobs directory. Walks down the children, // including its children manifests and/or indexes, and down the tree until all of // config and all layers, have been written. If any blob already exists, as determined by // the hash filename, does not write it. // This function does *not* update the `index.json` file. If you want to write the // index and also update the `index.json`, call AppendIndex(), which wraps this // and also updates the `index.json`. func (l Path) WriteIndex(ii v1.ImageIndex) error { // Always just write oci-layout file, since it's small. if err := l.WriteFile("oci-layout", []byte(layoutFile), os.ModePerm); err != nil { return err } h, err := ii.Digest() if err != nil { return err } indexFile := filepath.Join("blobs", h.Algorithm, h.Hex) return l.writeIndexToFile(indexFile, ii) } // Write constructs a Path at path from an ImageIndex. // // The contents are written in the following format: // At the top level, there is: // // One oci-layout file containing the version of this image-layout. // One index.json file listing descriptors for the contained images. // // Under blobs/, there is, for each image: // // One file for each layer, named after the layer's SHA. // One file for each config blob, named after its SHA. // One file for each manifest blob, named after its SHA. func Write(path string, ii v1.ImageIndex) (Path, error) { lp := Path(path) // Always just write oci-layout file, since it's small. if err := lp.WriteFile("oci-layout", []byte(layoutFile), os.ModePerm); err != nil { return "", err } // TODO create blobs/ in case there is a blobs file which would prevent the directory from being created return lp, lp.writeIndexToFile("index.json", ii) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/manifest.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package v1 import ( "encoding/json" "io" "github.com/google/go-containerregistry/pkg/v1/types" ) // Manifest represents the OCI image manifest in a structured way. type Manifest struct { SchemaVersion int64 `json:"schemaVersion"` MediaType types.MediaType `json:"mediaType,omitempty"` Config Descriptor `json:"config"` Layers []Descriptor `json:"layers"` Annotations map[string]string `json:"annotations,omitempty"` Subject *Descriptor `json:"subject,omitempty"` } // IndexManifest represents an OCI image index in a structured way. type IndexManifest struct { SchemaVersion int64 `json:"schemaVersion"` MediaType types.MediaType `json:"mediaType,omitempty"` Manifests []Descriptor `json:"manifests"` Annotations map[string]string `json:"annotations,omitempty"` Subject *Descriptor `json:"subject,omitempty"` } // Descriptor holds a reference from the manifest to one of its constituent elements. type Descriptor struct { MediaType types.MediaType `json:"mediaType"` Size int64 `json:"size"` Digest Hash `json:"digest"` Data []byte `json:"data,omitempty"` URLs []string `json:"urls,omitempty"` Annotations map[string]string `json:"annotations,omitempty"` Platform *Platform `json:"platform,omitempty"` ArtifactType string `json:"artifactType,omitempty"` } // ParseManifest parses the io.Reader's contents into a Manifest. func ParseManifest(r io.Reader) (*Manifest, error) { m := Manifest{} if err := json.NewDecoder(r).Decode(&m); err != nil { return nil, err } return &m, nil } // ParseIndexManifest parses the io.Reader's contents into an IndexManifest. func ParseIndexManifest(r io.Reader) (*IndexManifest, error) { im := IndexManifest{} if err := json.NewDecoder(r).Decode(&im); err != nil { return nil, err } return &im, nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/match/match.go ================================================ // Copyright 2020 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package match provides functionality for conveniently matching a v1.Descriptor. package match import ( v1 "github.com/google/go-containerregistry/pkg/v1" imagespec "github.com/opencontainers/image-spec/specs-go/v1" ) // Matcher function that is given a v1.Descriptor, and returns whether or // not it matches a given rule. Can match on anything it wants in the Descriptor. type Matcher func(desc v1.Descriptor) bool // Name returns a match.Matcher that matches based on the value of the // // "org.opencontainers.image.ref.name" annotation: // // github.com/opencontainers/image-spec/blob/v1.0.1/annotations.md#pre-defined-annotation-keys func Name(name string) Matcher { return Annotation(imagespec.AnnotationRefName, name) } // Annotation returns a match.Matcher that matches based on the provided annotation. func Annotation(key, value string) Matcher { return func(desc v1.Descriptor) bool { if desc.Annotations == nil { return false } if aValue, ok := desc.Annotations[key]; ok && aValue == value { return true } return false } } // Platforms returns a match.Matcher that matches on any one of the provided platforms. // Ignores any descriptors that do not have a platform. func Platforms(platforms ...v1.Platform) Matcher { return func(desc v1.Descriptor) bool { if desc.Platform == nil { return false } for _, platform := range platforms { if desc.Platform.Equals(platform) { return true } } return false } } // MediaTypes returns a match.Matcher that matches at least one of the provided media types. func MediaTypes(mediaTypes ...string) Matcher { mts := map[string]bool{} for _, media := range mediaTypes { mts[media] = true } return func(desc v1.Descriptor) bool { if desc.MediaType == "" { return false } if _, ok := mts[string(desc.MediaType)]; ok { return true } return false } } // Digests returns a match.Matcher that matches at least one of the provided Digests func Digests(digests ...v1.Hash) Matcher { digs := map[v1.Hash]bool{} for _, digest := range digests { digs[digest] = true } return func(desc v1.Descriptor) bool { _, ok := digs[desc.Digest] return ok } } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/mutate/README.md ================================================ # `mutate` [![GoDoc](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/mutate?status.svg)](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/mutate) The `v1.Image`, `v1.ImageIndex`, and `v1.Layer` interfaces provide only accessor methods, so they are essentially immutable. If you want to change something about them, you need to produce a new instance of that interface. A common use case for this library is to read an image from somewhere (a source), change something about it, and write the image somewhere else (a sink). Graphically, this looks something like:

## Mutations This is obviously not a comprehensive set of useful transformations (PRs welcome!), but a rough summary of what the `mutate` package currently does: ### `Config` and `ConfigFile` These allow you to change the [image configuration](https://github.com/opencontainers/image-spec/blob/master/config.md#properties), e.g. to change the entrypoint, environment, author, etc. ### `Time`, `Canonical`, and `CreatedAt` These are useful in the context of [reproducible builds](https://reproducible-builds.org/), where you may want to strip timestamps and other non-reproducible information. ### `Append`, `AppendLayers`, and `AppendManifests` These functions allow the extension of a `v1.Image` or `v1.ImageIndex` with new layers or manifests. For constructing an image `FROM scratch`, see the [`empty`](/pkg/v1/empty) package. ### `MediaType` and `IndexMediaType` Sometimes, it is necessary to change the media type of an image or index, e.g. to appease a registry with strict validation of images (_looking at you, GCR_). ### `Rebase` Rebase has [its own README](/cmd/crane/rebase.md). This is the underlying implementation of [`crane rebase`](https://github.com/google/go-containerregistry/blob/main/cmd/crane/doc/crane_rebase.md). ### `Extract` Extract will flatten an image filesystem into a single tar stream, respecting whiteout files. This is the underlying implementation of [`crane export`](https://github.com/google/go-containerregistry/blob/main/cmd/crane/doc/crane_export.md). ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/mutate/doc.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package mutate provides facilities for mutating v1.Images of any kind. package mutate ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/mutate/image.go ================================================ // Copyright 2019 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package mutate import ( "bytes" "encoding/json" "errors" "sync" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/partial" "github.com/google/go-containerregistry/pkg/v1/stream" "github.com/google/go-containerregistry/pkg/v1/types" ) type image struct { base v1.Image adds []Addendum computed bool configFile *v1.ConfigFile manifest *v1.Manifest annotations map[string]string mediaType *types.MediaType configMediaType *types.MediaType diffIDMap map[v1.Hash]v1.Layer digestMap map[v1.Hash]v1.Layer subject *v1.Descriptor sync.Mutex } var _ v1.Image = (*image)(nil) func (i *image) MediaType() (types.MediaType, error) { if i.mediaType != nil { return *i.mediaType, nil } return i.base.MediaType() } func (i *image) compute() error { i.Lock() defer i.Unlock() // Don't re-compute if already computed. if i.computed { return nil } var configFile *v1.ConfigFile if i.configFile != nil { configFile = i.configFile } else { cf, err := i.base.ConfigFile() if err != nil { return err } configFile = cf.DeepCopy() } diffIDs := configFile.RootFS.DiffIDs history := configFile.History diffIDMap := make(map[v1.Hash]v1.Layer) digestMap := make(map[v1.Hash]v1.Layer) for _, add := range i.adds { history = append(history, add.History) if add.Layer != nil { diffID, err := add.Layer.DiffID() if err != nil { return err } diffIDs = append(diffIDs, diffID) diffIDMap[diffID] = add.Layer } } m, err := i.base.Manifest() if err != nil { return err } manifest := m.DeepCopy() manifestLayers := manifest.Layers for _, add := range i.adds { if add.Layer == nil { // Empty layers include only history in manifest. continue } desc, err := partial.Descriptor(add.Layer) if err != nil { return err } // Fields in the addendum override the original descriptor. if len(add.Annotations) != 0 { desc.Annotations = add.Annotations } if len(add.URLs) != 0 { desc.URLs = add.URLs } if add.MediaType != "" { desc.MediaType = add.MediaType } manifestLayers = append(manifestLayers, *desc) digestMap[desc.Digest] = add.Layer } configFile.RootFS.DiffIDs = diffIDs configFile.History = history manifest.Layers = manifestLayers rcfg, err := json.Marshal(configFile) if err != nil { return err } d, sz, err := v1.SHA256(bytes.NewBuffer(rcfg)) if err != nil { return err } manifest.Config.Digest = d manifest.Config.Size = sz // If Data was set in the base image, we need to update it in the mutated image. if m.Config.Data != nil { manifest.Config.Data = rcfg } // If the user wants to mutate the media type of the config if i.configMediaType != nil { manifest.Config.MediaType = *i.configMediaType } if i.mediaType != nil { manifest.MediaType = *i.mediaType } if i.annotations != nil { if manifest.Annotations == nil { manifest.Annotations = map[string]string{} } for k, v := range i.annotations { manifest.Annotations[k] = v } } manifest.Subject = i.subject i.configFile = configFile i.manifest = manifest i.diffIDMap = diffIDMap i.digestMap = digestMap i.computed = true return nil } // Layers returns the ordered collection of filesystem layers that comprise this image. // The order of the list is oldest/base layer first, and most-recent/top layer last. func (i *image) Layers() ([]v1.Layer, error) { if err := i.compute(); errors.Is(err, stream.ErrNotComputed) { // Image contains a streamable layer which has not yet been // consumed. Just return the layers we have in case the caller // is going to consume the layers. layers, err := i.base.Layers() if err != nil { return nil, err } for _, add := range i.adds { layers = append(layers, add.Layer) } return layers, nil } else if err != nil { return nil, err } diffIDs, err := partial.DiffIDs(i) if err != nil { return nil, err } ls := make([]v1.Layer, 0, len(diffIDs)) for _, h := range diffIDs { l, err := i.LayerByDiffID(h) if err != nil { return nil, err } ls = append(ls, l) } return ls, nil } // ConfigName returns the hash of the image's config file. func (i *image) ConfigName() (v1.Hash, error) { if err := i.compute(); err != nil { return v1.Hash{}, err } return partial.ConfigName(i) } // ConfigFile returns this image's config file. func (i *image) ConfigFile() (*v1.ConfigFile, error) { if err := i.compute(); err != nil { return nil, err } return i.configFile.DeepCopy(), nil } // RawConfigFile returns the serialized bytes of ConfigFile() func (i *image) RawConfigFile() ([]byte, error) { if err := i.compute(); err != nil { return nil, err } return json.Marshal(i.configFile) } // Digest returns the sha256 of this image's manifest. func (i *image) Digest() (v1.Hash, error) { if err := i.compute(); err != nil { return v1.Hash{}, err } return partial.Digest(i) } // Size implements v1.Image. func (i *image) Size() (int64, error) { if err := i.compute(); err != nil { return -1, err } return partial.Size(i) } // Manifest returns this image's Manifest object. func (i *image) Manifest() (*v1.Manifest, error) { if err := i.compute(); err != nil { return nil, err } return i.manifest.DeepCopy(), nil } // RawManifest returns the serialized bytes of Manifest() func (i *image) RawManifest() ([]byte, error) { if err := i.compute(); err != nil { return nil, err } return json.Marshal(i.manifest) } // LayerByDigest returns a Layer for interacting with a particular layer of // the image, looking it up by "digest" (the compressed hash). func (i *image) LayerByDigest(h v1.Hash) (v1.Layer, error) { if cn, err := i.ConfigName(); err != nil { return nil, err } else if h == cn { return partial.ConfigLayer(i) } if layer, ok := i.digestMap[h]; ok { return layer, nil } return i.base.LayerByDigest(h) } // LayerByDiffID is an analog to LayerByDigest, looking up by "diff id" // (the uncompressed hash). func (i *image) LayerByDiffID(h v1.Hash) (v1.Layer, error) { if layer, ok := i.diffIDMap[h]; ok { return layer, nil } return i.base.LayerByDiffID(h) } func validate(adds []Addendum) error { for _, add := range adds { if add.Layer == nil && !add.History.EmptyLayer { return errors.New("unable to add a nil layer to the image") } } return nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/mutate/index.go ================================================ // Copyright 2019 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package mutate import ( "encoding/json" "errors" "fmt" "sync" "github.com/google/go-containerregistry/pkg/logs" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/match" "github.com/google/go-containerregistry/pkg/v1/partial" "github.com/google/go-containerregistry/pkg/v1/stream" "github.com/google/go-containerregistry/pkg/v1/types" ) func computeDescriptor(ia IndexAddendum) (*v1.Descriptor, error) { desc, err := partial.Descriptor(ia.Add) if err != nil { return nil, err } // The IndexAddendum allows overriding Descriptor values. if ia.Descriptor.Size != 0 { desc.Size = ia.Descriptor.Size } if string(ia.Descriptor.MediaType) != "" { desc.MediaType = ia.Descriptor.MediaType } if ia.Descriptor.Digest != (v1.Hash{}) { desc.Digest = ia.Descriptor.Digest } if ia.Descriptor.Platform != nil { desc.Platform = ia.Descriptor.Platform } if len(ia.Descriptor.URLs) != 0 { desc.URLs = ia.Descriptor.URLs } if len(ia.Descriptor.Annotations) != 0 { desc.Annotations = ia.Descriptor.Annotations } if ia.Descriptor.Data != nil { desc.Data = ia.Descriptor.Data } return desc, nil } type index struct { base v1.ImageIndex adds []IndexAddendum // remove is removed before adds remove match.Matcher computed bool manifest *v1.IndexManifest annotations map[string]string mediaType *types.MediaType imageMap map[v1.Hash]v1.Image indexMap map[v1.Hash]v1.ImageIndex layerMap map[v1.Hash]v1.Layer subject *v1.Descriptor sync.Mutex } var _ v1.ImageIndex = (*index)(nil) func (i *index) MediaType() (types.MediaType, error) { if i.mediaType != nil { return *i.mediaType, nil } return i.base.MediaType() } func (i *index) Size() (int64, error) { return partial.Size(i) } func (i *index) compute() error { i.Lock() defer i.Unlock() // Don't re-compute if already computed. if i.computed { return nil } i.imageMap = make(map[v1.Hash]v1.Image) i.indexMap = make(map[v1.Hash]v1.ImageIndex) i.layerMap = make(map[v1.Hash]v1.Layer) m, err := i.base.IndexManifest() if err != nil { return err } manifest := m.DeepCopy() manifests := manifest.Manifests if i.remove != nil { var cleanedManifests []v1.Descriptor for _, m := range manifests { if !i.remove(m) { cleanedManifests = append(cleanedManifests, m) } } manifests = cleanedManifests } for _, add := range i.adds { desc, err := computeDescriptor(add) if err != nil { return err } manifests = append(manifests, *desc) if idx, ok := add.Add.(v1.ImageIndex); ok { i.indexMap[desc.Digest] = idx } else if img, ok := add.Add.(v1.Image); ok { i.imageMap[desc.Digest] = img } else if l, ok := add.Add.(v1.Layer); ok { i.layerMap[desc.Digest] = l } else { logs.Warn.Printf("Unexpected index addendum: %T", add.Add) } } manifest.Manifests = manifests if i.mediaType != nil { manifest.MediaType = *i.mediaType } if i.annotations != nil { if manifest.Annotations == nil { manifest.Annotations = map[string]string{} } for k, v := range i.annotations { manifest.Annotations[k] = v } } manifest.Subject = i.subject i.manifest = manifest i.computed = true return nil } func (i *index) Image(h v1.Hash) (v1.Image, error) { if img, ok := i.imageMap[h]; ok { return img, nil } return i.base.Image(h) } func (i *index) ImageIndex(h v1.Hash) (v1.ImageIndex, error) { if idx, ok := i.indexMap[h]; ok { return idx, nil } return i.base.ImageIndex(h) } type withLayer interface { Layer(v1.Hash) (v1.Layer, error) } // Workaround for #819. func (i *index) Layer(h v1.Hash) (v1.Layer, error) { if layer, ok := i.layerMap[h]; ok { return layer, nil } if wl, ok := i.base.(withLayer); ok { return wl.Layer(h) } return nil, fmt.Errorf("layer not found: %s", h) } // Digest returns the sha256 of this image's manifest. func (i *index) Digest() (v1.Hash, error) { if err := i.compute(); err != nil { return v1.Hash{}, err } return partial.Digest(i) } // Manifest returns this image's Manifest object. func (i *index) IndexManifest() (*v1.IndexManifest, error) { if err := i.compute(); err != nil { return nil, err } return i.manifest.DeepCopy(), nil } // RawManifest returns the serialized bytes of Manifest() func (i *index) RawManifest() ([]byte, error) { if err := i.compute(); err != nil { return nil, err } return json.Marshal(i.manifest) } func (i *index) Manifests() ([]partial.Describable, error) { if err := i.compute(); errors.Is(err, stream.ErrNotComputed) { // Index contains a streamable layer which has not yet been // consumed. Just return the manifests we have in case the caller // is going to consume the streamable layers. manifests, err := partial.Manifests(i.base) if err != nil { return nil, err } for _, add := range i.adds { manifests = append(manifests, add.Add) } return manifests, nil } else if err != nil { return nil, err } return partial.ComputeManifests(i) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/mutate/mutate.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package mutate import ( "archive/tar" "bytes" "encoding/json" "errors" "fmt" "io" "path/filepath" "strings" "time" "github.com/google/go-containerregistry/internal/gzip" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/empty" "github.com/google/go-containerregistry/pkg/v1/match" "github.com/google/go-containerregistry/pkg/v1/partial" "github.com/google/go-containerregistry/pkg/v1/tarball" "github.com/google/go-containerregistry/pkg/v1/types" ) const whiteoutPrefix = ".wh." // Addendum contains layers and history to be appended // to a base image type Addendum struct { Layer v1.Layer History v1.History URLs []string Annotations map[string]string MediaType types.MediaType } // AppendLayers applies layers to a base image. func AppendLayers(base v1.Image, layers ...v1.Layer) (v1.Image, error) { additions := make([]Addendum, 0, len(layers)) for _, layer := range layers { additions = append(additions, Addendum{Layer: layer}) } return Append(base, additions...) } // Append will apply the list of addendums to the base image func Append(base v1.Image, adds ...Addendum) (v1.Image, error) { if len(adds) == 0 { return base, nil } if err := validate(adds); err != nil { return nil, err } return &image{ base: base, adds: adds, }, nil } // Appendable is an interface that represents something that can be appended // to an ImageIndex. We need to be able to construct a v1.Descriptor in order // to append something, and this is the minimum required information for that. type Appendable interface { MediaType() (types.MediaType, error) Digest() (v1.Hash, error) Size() (int64, error) } // IndexAddendum represents an appendable thing and all the properties that // we may want to override in the resulting v1.Descriptor. type IndexAddendum struct { Add Appendable v1.Descriptor } // AppendManifests appends a manifest to the ImageIndex. func AppendManifests(base v1.ImageIndex, adds ...IndexAddendum) v1.ImageIndex { return &index{ base: base, adds: adds, } } // RemoveManifests removes any descriptors that match the match.Matcher. func RemoveManifests(base v1.ImageIndex, matcher match.Matcher) v1.ImageIndex { return &index{ base: base, remove: matcher, } } // Config mutates the provided v1.Image to have the provided v1.Config func Config(base v1.Image, cfg v1.Config) (v1.Image, error) { cf, err := base.ConfigFile() if err != nil { return nil, err } cf.Config = cfg return ConfigFile(base, cf) } // Subject mutates the subject on an image or index manifest. // // The input is expected to be a v1.Image or v1.ImageIndex, and // returns the same type. You can type-assert the result like so: // // img := Subject(empty.Image, subj).(v1.Image) // // Or for an index: // // idx := Subject(empty.Index, subj).(v1.ImageIndex) // // If the input is not an Image or ImageIndex, the result will // attempt to lazily annotate the raw manifest. func Subject(f partial.WithRawManifest, subject v1.Descriptor) partial.WithRawManifest { if img, ok := f.(v1.Image); ok { return &image{ base: img, subject: &subject, } } if idx, ok := f.(v1.ImageIndex); ok { return &index{ base: idx, subject: &subject, } } return arbitraryRawManifest{a: f, subject: &subject} } // Annotations mutates the annotations on an annotatable image or index manifest. // // The annotatable input is expected to be a v1.Image or v1.ImageIndex, and // returns the same type. You can type-assert the result like so: // // img := Annotations(empty.Image, map[string]string{ // "foo": "bar", // }).(v1.Image) // // Or for an index: // // idx := Annotations(empty.Index, map[string]string{ // "foo": "bar", // }).(v1.ImageIndex) // // If the input Annotatable is not an Image or ImageIndex, the result will // attempt to lazily annotate the raw manifest. func Annotations(f partial.WithRawManifest, anns map[string]string) partial.WithRawManifest { if img, ok := f.(v1.Image); ok { return &image{ base: img, annotations: anns, } } if idx, ok := f.(v1.ImageIndex); ok { return &index{ base: idx, annotations: anns, } } return arbitraryRawManifest{a: f, anns: anns} } type arbitraryRawManifest struct { a partial.WithRawManifest anns map[string]string subject *v1.Descriptor } func (a arbitraryRawManifest) RawManifest() ([]byte, error) { b, err := a.a.RawManifest() if err != nil { return nil, err } var m map[string]any if err := json.Unmarshal(b, &m); err != nil { return nil, err } if ann, ok := m["annotations"]; ok { if annm, ok := ann.(map[string]string); ok { for k, v := range a.anns { annm[k] = v } } else { return nil, fmt.Errorf(".annotations is not a map: %T", ann) } } else { m["annotations"] = a.anns } if a.subject != nil { m["subject"] = a.subject } return json.Marshal(m) } // ConfigFile mutates the provided v1.Image to have the provided v1.ConfigFile func ConfigFile(base v1.Image, cfg *v1.ConfigFile) (v1.Image, error) { m, err := base.Manifest() if err != nil { return nil, err } image := &image{ base: base, manifest: m.DeepCopy(), configFile: cfg, } return image, nil } // CreatedAt mutates the provided v1.Image to have the provided v1.Time func CreatedAt(base v1.Image, created v1.Time) (v1.Image, error) { cf, err := base.ConfigFile() if err != nil { return nil, err } cfg := cf.DeepCopy() cfg.Created = created return ConfigFile(base, cfg) } // Extract takes an image and returns an io.ReadCloser containing the image's // flattened filesystem. // // Callers can read the filesystem contents by passing the reader to // tar.NewReader, or io.Copy it directly to some output. // // If a caller doesn't read the full contents, they should Close it to free up // resources used during extraction. func Extract(img v1.Image) io.ReadCloser { pr, pw := io.Pipe() go func() { // Close the writer with any errors encountered during // extraction. These errors will be returned by the reader end // on subsequent reads. If err == nil, the reader will return // EOF. pw.CloseWithError(extract(img, pw)) }() return pr } // Adapted from https://github.com/google/containerregistry/blob/da03b395ccdc4e149e34fbb540483efce962dc64/client/v2_2/docker_image_.py#L816 func extract(img v1.Image, w io.Writer) error { tarWriter := tar.NewWriter(w) defer tarWriter.Close() fileMap := map[string]bool{} layers, err := img.Layers() if err != nil { return fmt.Errorf("retrieving image layers: %w", err) } // we iterate through the layers in reverse order because it makes handling // whiteout layers more efficient, since we can just keep track of the removed // files as we see .wh. layers and ignore those in previous layers. for i := len(layers) - 1; i >= 0; i-- { layer := layers[i] layerReader, err := layer.Uncompressed() if err != nil { return fmt.Errorf("reading layer contents: %w", err) } defer layerReader.Close() tarReader := tar.NewReader(layerReader) for { header, err := tarReader.Next() if errors.Is(err, io.EOF) { break } if err != nil { return fmt.Errorf("reading tar: %w", err) } // Some tools prepend everything with "./", so if we don't Clean the // name, we may have duplicate entries, which angers tar-split. header.Name = filepath.Clean(header.Name) // force PAX format to remove Name/Linkname length limit of 100 characters // required by USTAR and to not depend on internal tar package guess which // prefers USTAR over PAX header.Format = tar.FormatPAX basename := filepath.Base(header.Name) dirname := filepath.Dir(header.Name) tombstone := strings.HasPrefix(basename, whiteoutPrefix) if tombstone { basename = basename[len(whiteoutPrefix):] } // check if we have seen value before // if we're checking a directory, don't filepath.Join names var name string if header.Typeflag == tar.TypeDir { name = header.Name } else { name = filepath.Join(dirname, basename) } if _, ok := fileMap[name]; ok { continue } // check for a whited out parent directory if inWhiteoutDir(fileMap, name) { continue } // mark file as handled. non-directory implicitly tombstones // any entries with a matching (or child) name fileMap[name] = tombstone || !(header.Typeflag == tar.TypeDir) if !tombstone { if err := tarWriter.WriteHeader(header); err != nil { return err } if header.Size > 0 { if _, err := io.CopyN(tarWriter, tarReader, header.Size); err != nil { return err } } } } } return nil } func inWhiteoutDir(fileMap map[string]bool, file string) bool { for { if file == "" { break } dirname := filepath.Dir(file) if file == dirname { break } if val, ok := fileMap[dirname]; ok && val { return true } file = dirname } return false } func max(a, b int) int { if a > b { return a } return b } // Time sets all timestamps in an image to the given timestamp. func Time(img v1.Image, t time.Time) (v1.Image, error) { newImage := empty.Image layers, err := img.Layers() if err != nil { return nil, fmt.Errorf("getting image layers: %w", err) } ocf, err := img.ConfigFile() if err != nil { return nil, fmt.Errorf("getting original config file: %w", err) } addendums := make([]Addendum, max(len(ocf.History), len(layers))) var historyIdx, addendumIdx int for layerIdx := 0; layerIdx < len(layers); addendumIdx, layerIdx = addendumIdx+1, layerIdx+1 { newLayer, err := layerTime(layers[layerIdx], t) if err != nil { return nil, fmt.Errorf("setting layer times: %w", err) } // try to search for the history entry that corresponds to this layer for ; historyIdx < len(ocf.History); historyIdx++ { addendums[addendumIdx].History = ocf.History[historyIdx] // if it's an EmptyLayer, do not set the Layer and have the Addendum with just the History // and move on to the next History entry if ocf.History[historyIdx].EmptyLayer { addendumIdx++ continue } // otherwise, we can exit from the cycle historyIdx++ break } if addendumIdx < len(addendums) { addendums[addendumIdx].Layer = newLayer } } // add all leftover History entries for ; historyIdx < len(ocf.History); historyIdx, addendumIdx = historyIdx+1, addendumIdx+1 { addendums[addendumIdx].History = ocf.History[historyIdx] } newImage, err = Append(newImage, addendums...) if err != nil { return nil, fmt.Errorf("appending layers: %w", err) } cf, err := newImage.ConfigFile() if err != nil { return nil, fmt.Errorf("setting config file: %w", err) } cfg := cf.DeepCopy() // Copy basic config over cfg.Architecture = ocf.Architecture cfg.OS = ocf.OS cfg.OSVersion = ocf.OSVersion cfg.Config = ocf.Config // Strip away timestamps from the config file cfg.Created = v1.Time{Time: t} for i, h := range cfg.History { h.Created = v1.Time{Time: t} h.CreatedBy = ocf.History[i].CreatedBy h.Comment = ocf.History[i].Comment h.EmptyLayer = ocf.History[i].EmptyLayer // Explicitly ignore Author field; which hinders reproducibility h.Author = "" cfg.History[i] = h } return ConfigFile(newImage, cfg) } func layerTime(layer v1.Layer, t time.Time) (v1.Layer, error) { layerReader, err := layer.Uncompressed() if err != nil { return nil, fmt.Errorf("getting layer: %w", err) } defer layerReader.Close() w := new(bytes.Buffer) tarWriter := tar.NewWriter(w) defer tarWriter.Close() tarReader := tar.NewReader(layerReader) for { header, err := tarReader.Next() if errors.Is(err, io.EOF) { break } if err != nil { return nil, fmt.Errorf("reading layer: %w", err) } header.ModTime = t //PAX and GNU Format support additional timestamps in the header if header.Format == tar.FormatPAX || header.Format == tar.FormatGNU { header.AccessTime = t header.ChangeTime = t } if err := tarWriter.WriteHeader(header); err != nil { return nil, fmt.Errorf("writing tar header: %w", err) } if header.Typeflag == tar.TypeReg { // TODO(#1168): This should be lazy, and not buffer the entire layer contents. if _, err = io.CopyN(tarWriter, tarReader, header.Size); err != nil { return nil, fmt.Errorf("writing layer file: %w", err) } } } if err := tarWriter.Close(); err != nil { return nil, err } b := w.Bytes() // gzip the contents, then create the layer opener := func() (io.ReadCloser, error) { return gzip.ReadCloser(io.NopCloser(bytes.NewReader(b))), nil } layer, err = tarball.LayerFromOpener(opener) if err != nil { return nil, fmt.Errorf("creating layer: %w", err) } return layer, nil } // Canonical is a helper function to combine Time and configFile // to remove any randomness during a docker build. func Canonical(img v1.Image) (v1.Image, error) { // Set all timestamps to 0 created := time.Time{} img, err := Time(img, created) if err != nil { return nil, err } cf, err := img.ConfigFile() if err != nil { return nil, err } // Get rid of host-dependent random config cfg := cf.DeepCopy() cfg.Container = "" cfg.Config.Hostname = "" cfg.DockerVersion = "" return ConfigFile(img, cfg) } // MediaType modifies the MediaType() of the given image. func MediaType(img v1.Image, mt types.MediaType) v1.Image { return &image{ base: img, mediaType: &mt, } } // ConfigMediaType modifies the MediaType() of the given image's Config. // // If !mt.IsConfig(), this will be the image's artifactType in any indexes it's a part of. func ConfigMediaType(img v1.Image, mt types.MediaType) v1.Image { return &image{ base: img, configMediaType: &mt, } } // IndexMediaType modifies the MediaType() of the given index. func IndexMediaType(idx v1.ImageIndex, mt types.MediaType) v1.ImageIndex { return &index{ base: idx, mediaType: &mt, } } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/mutate/rebase.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package mutate import ( "fmt" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/empty" ) // Rebase returns a new v1.Image where the oldBase in orig is replaced by newBase. func Rebase(orig, oldBase, newBase v1.Image) (v1.Image, error) { // Verify that oldBase's layers are present in orig, otherwise orig is // not based on oldBase at all. origLayers, err := orig.Layers() if err != nil { return nil, fmt.Errorf("failed to get layers for original: %w", err) } oldBaseLayers, err := oldBase.Layers() if err != nil { return nil, err } if len(oldBaseLayers) > len(origLayers) { return nil, fmt.Errorf("image %q is not based on %q (too few layers)", orig, oldBase) } for i, l := range oldBaseLayers { oldLayerDigest, err := l.Digest() if err != nil { return nil, fmt.Errorf("failed to get digest of layer %d of %q: %w", i, oldBase, err) } origLayerDigest, err := origLayers[i].Digest() if err != nil { return nil, fmt.Errorf("failed to get digest of layer %d of %q: %w", i, orig, err) } if oldLayerDigest != origLayerDigest { return nil, fmt.Errorf("image %q is not based on %q (layer %d mismatch)", orig, oldBase, i) } } oldConfig, err := oldBase.ConfigFile() if err != nil { return nil, fmt.Errorf("failed to get config for old base: %w", err) } origConfig, err := orig.ConfigFile() if err != nil { return nil, fmt.Errorf("failed to get config for original: %w", err) } newConfig, err := newBase.ConfigFile() if err != nil { return nil, fmt.Errorf("could not get config for new base: %w", err) } // Stitch together an image that contains: // - original image's config // - new base image's os/arch properties // - new base image's layers + top of original image's layers // - new base image's history + top of original image's history rebasedImage, err := Config(empty.Image, *origConfig.Config.DeepCopy()) if err != nil { return nil, fmt.Errorf("failed to create empty image with original config: %w", err) } // Add new config properties from existing images. rebasedConfig, err := rebasedImage.ConfigFile() if err != nil { return nil, fmt.Errorf("could not get config for rebased image: %w", err) } // OS/Arch properties from new base rebasedConfig.Architecture = newConfig.Architecture rebasedConfig.OS = newConfig.OS rebasedConfig.OSVersion = newConfig.OSVersion // Apply config properties to rebased. rebasedImage, err = ConfigFile(rebasedImage, rebasedConfig) if err != nil { return nil, fmt.Errorf("failed to replace config for rebased image: %w", err) } // Get new base layers and config for history. newBaseLayers, err := newBase.Layers() if err != nil { return nil, fmt.Errorf("could not get new base layers for new base: %w", err) } // Add new base layers. rebasedImage, err = Append(rebasedImage, createAddendums(0, 0, newConfig.History, newBaseLayers)...) if err != nil { return nil, fmt.Errorf("failed to append new base image: %w", err) } // Add original layers above the old base. rebasedImage, err = Append(rebasedImage, createAddendums(len(oldConfig.History), len(oldBaseLayers)+1, origConfig.History, origLayers)...) if err != nil { return nil, fmt.Errorf("failed to append original image: %w", err) } return rebasedImage, nil } // createAddendums makes a list of addendums from a history and layers starting from a specific history and layer // indexes. func createAddendums(startHistory, startLayer int, history []v1.History, layers []v1.Layer) []Addendum { var adds []Addendum // History should be a superset of layers; empty layers (e.g. ENV statements) only exist in history. // They cannot be iterated identically but must be walked independently, only advancing the iterator for layers // when a history entry for a non-empty layer is seen. layerIndex := 0 for historyIndex := range history { var layer v1.Layer emptyLayer := history[historyIndex].EmptyLayer if !emptyLayer { layer = layers[layerIndex] layerIndex++ } if historyIndex >= startHistory || layerIndex >= startLayer { adds = append(adds, Addendum{ Layer: layer, History: history[historyIndex], }) } } // In the event history was malformed or non-existent, append the remaining layers. for i := layerIndex; i < len(layers); i++ { if i >= startLayer { adds = append(adds, Addendum{Layer: layers[layerIndex]}) } } return adds } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/partial/README.md ================================================ # `partial` [![GoDoc](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/partial?status.svg)](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/partial) ## Partial Implementations There are roughly two kinds of image representations: compressed and uncompressed. The implementations for these kinds of images are almost identical, with the only major difference being how blobs (config and layers) are fetched. This common code lives in this package, where you provide a _partial_ implementation of a compressed or uncompressed image, and you get back a full `v1.Image` implementation. ### Examples In a registry, blobs are compressed, so it's easiest to implement a `v1.Image` in terms of compressed layers. `remote.remoteImage` does this by implementing `CompressedImageCore`: ```go type CompressedImageCore interface { RawConfigFile() ([]byte, error) MediaType() (types.MediaType, error) RawManifest() ([]byte, error) LayerByDigest(v1.Hash) (CompressedLayer, error) } ``` In a tarball, blobs are (often) uncompressed, so it's easiest to implement a `v1.Image` in terms of uncompressed layers. `tarball.uncompressedImage` does this by implementing `UncompressedImageCore`: ```go type UncompressedImageCore interface { RawConfigFile() ([]byte, error) MediaType() (types.MediaType, error) LayerByDiffID(v1.Hash) (UncompressedLayer, error) } ``` ## Optional Methods Where possible, we access some information via optional methods as an optimization. ### [`partial.Descriptor`](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/partial#Descriptor) There are some properties of a [`Descriptor`](https://github.com/opencontainers/image-spec/blob/master/descriptor.md#properties) that aren't derivable from just image data: * `MediaType` * `Platform` * `URLs` * `Annotations` For example, in a `tarball.Image`, there is a `LayerSources` field that contains an entire layer descriptor with `URLs` information for foreign layers. This information can be passed through to callers by implementing this optional `Descriptor` method. See [`#654`](https://github.com/google/go-containerregistry/pull/654). ### [`partial.UncompressedSize`](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/partial#UncompressedSize) Usually, you don't need to know the uncompressed size of a layer, since that information isn't stored in a config file (just he sha256 is needed); however, there are cases where it is very helpful to know the layer size, e.g. when writing the uncompressed layer into a tarball. See [`#655`](https://github.com/google/go-containerregistry/pull/655). ### [`partial.Exists`](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/partial#Exists) We generally don't care about the existence of something as granular as a layer, and would rather ensure all the invariants of an image are upheld via the `validate` package. However, there are situations where we want to do a quick smoke test to ensure that the underlying storage engine hasn't been corrupted by something e.g. deleting files or blobs. Thus, we've exposed an optional `Exists` method that does an existence check without actually reading any bytes. The `remote` package implements this via `HEAD` requests. The `layout` package implements this via `os.Stat`. See [`#838`](https://github.com/google/go-containerregistry/pull/838). ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/partial/compressed.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package partial import ( "io" "github.com/google/go-containerregistry/internal/and" "github.com/google/go-containerregistry/internal/compression" "github.com/google/go-containerregistry/internal/gzip" "github.com/google/go-containerregistry/internal/zstd" comp "github.com/google/go-containerregistry/pkg/compression" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/types" ) // CompressedLayer represents the bare minimum interface a natively // compressed layer must implement for us to produce a v1.Layer type CompressedLayer interface { // Digest returns the Hash of the compressed layer. Digest() (v1.Hash, error) // Compressed returns an io.ReadCloser for the compressed layer contents. Compressed() (io.ReadCloser, error) // Size returns the compressed size of the Layer. Size() (int64, error) // Returns the mediaType for the compressed Layer MediaType() (types.MediaType, error) } // compressedLayerExtender implements v1.Image using the compressed base properties. type compressedLayerExtender struct { CompressedLayer } // Uncompressed implements v1.Layer func (cle *compressedLayerExtender) Uncompressed() (io.ReadCloser, error) { rc, err := cle.Compressed() if err != nil { return nil, err } // Often, the "compressed" bytes are not actually-compressed. // Peek at the first two bytes to determine whether it's correct to // wrap this with gzip.UnzipReadCloser or zstd.UnzipReadCloser. cp, pr, err := compression.PeekCompression(rc) if err != nil { return nil, err } prc := &and.ReadCloser{ Reader: pr, CloseFunc: rc.Close, } switch cp { case comp.GZip: return gzip.UnzipReadCloser(prc) case comp.ZStd: return zstd.UnzipReadCloser(prc) default: return prc, nil } } // DiffID implements v1.Layer func (cle *compressedLayerExtender) DiffID() (v1.Hash, error) { // If our nested CompressedLayer implements DiffID, // then delegate to it instead. if wdi, ok := cle.CompressedLayer.(WithDiffID); ok { return wdi.DiffID() } r, err := cle.Uncompressed() if err != nil { return v1.Hash{}, err } defer r.Close() h, _, err := v1.SHA256(r) return h, err } // CompressedToLayer fills in the missing methods from a CompressedLayer so that it implements v1.Layer func CompressedToLayer(ul CompressedLayer) (v1.Layer, error) { return &compressedLayerExtender{ul}, nil } // CompressedImageCore represents the base minimum interface a natively // compressed image must implement for us to produce a v1.Image. type CompressedImageCore interface { ImageCore // RawManifest returns the serialized bytes of the manifest. RawManifest() ([]byte, error) // LayerByDigest is a variation on the v1.Image method, which returns // a CompressedLayer instead. LayerByDigest(v1.Hash) (CompressedLayer, error) } // compressedImageExtender implements v1.Image by extending CompressedImageCore with the // appropriate methods computed from the minimal core. type compressedImageExtender struct { CompressedImageCore } // Assert that our extender type completes the v1.Image interface var _ v1.Image = (*compressedImageExtender)(nil) // Digest implements v1.Image func (i *compressedImageExtender) Digest() (v1.Hash, error) { return Digest(i) } // ConfigName implements v1.Image func (i *compressedImageExtender) ConfigName() (v1.Hash, error) { return ConfigName(i) } // Layers implements v1.Image func (i *compressedImageExtender) Layers() ([]v1.Layer, error) { hs, err := FSLayers(i) if err != nil { return nil, err } ls := make([]v1.Layer, 0, len(hs)) for _, h := range hs { l, err := i.LayerByDigest(h) if err != nil { return nil, err } ls = append(ls, l) } return ls, nil } // LayerByDigest implements v1.Image func (i *compressedImageExtender) LayerByDigest(h v1.Hash) (v1.Layer, error) { cl, err := i.CompressedImageCore.LayerByDigest(h) if err != nil { return nil, err } return CompressedToLayer(cl) } // LayerByDiffID implements v1.Image func (i *compressedImageExtender) LayerByDiffID(h v1.Hash) (v1.Layer, error) { h, err := DiffIDToBlob(i, h) if err != nil { return nil, err } return i.LayerByDigest(h) } // ConfigFile implements v1.Image func (i *compressedImageExtender) ConfigFile() (*v1.ConfigFile, error) { return ConfigFile(i) } // Manifest implements v1.Image func (i *compressedImageExtender) Manifest() (*v1.Manifest, error) { return Manifest(i) } // Size implements v1.Image func (i *compressedImageExtender) Size() (int64, error) { return Size(i) } // CompressedToImage fills in the missing methods from a CompressedImageCore so that it implements v1.Image func CompressedToImage(cic CompressedImageCore) (v1.Image, error) { return &compressedImageExtender{ CompressedImageCore: cic, }, nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/partial/doc.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package partial defines methods for building up a v1.Image from // minimal subsets that are sufficient for defining a v1.Image. package partial ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/partial/image.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package partial import ( "github.com/google/go-containerregistry/pkg/v1/types" ) // ImageCore is the core set of properties without which we cannot build a v1.Image type ImageCore interface { // RawConfigFile returns the serialized bytes of this image's config file. RawConfigFile() ([]byte, error) // MediaType of this image's manifest. MediaType() (types.MediaType, error) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/partial/index.go ================================================ // Copyright 2020 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package partial import ( "fmt" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/match" "github.com/google/go-containerregistry/pkg/v1/types" ) // FindManifests given a v1.ImageIndex, find the manifests that fit the matcher. func FindManifests(index v1.ImageIndex, matcher match.Matcher) ([]v1.Descriptor, error) { // get the actual manifest list indexManifest, err := index.IndexManifest() if err != nil { return nil, fmt.Errorf("unable to get raw index: %w", err) } manifests := []v1.Descriptor{} // try to get the root of our image for _, manifest := range indexManifest.Manifests { if matcher(manifest) { manifests = append(manifests, manifest) } } return manifests, nil } // FindImages given a v1.ImageIndex, find the images that fit the matcher. If a Descriptor // matches the provider Matcher, but the referenced item is not an Image, ignores it. // Only returns those that match the Matcher and are images. func FindImages(index v1.ImageIndex, matcher match.Matcher) ([]v1.Image, error) { matches := []v1.Image{} manifests, err := FindManifests(index, matcher) if err != nil { return nil, err } for _, desc := range manifests { // if it is not an image, ignore it if !desc.MediaType.IsImage() { continue } img, err := index.Image(desc.Digest) if err != nil { return nil, err } matches = append(matches, img) } return matches, nil } // FindIndexes given a v1.ImageIndex, find the indexes that fit the matcher. If a Descriptor // matches the provider Matcher, but the referenced item is not an Index, ignores it. // Only returns those that match the Matcher and are indexes. func FindIndexes(index v1.ImageIndex, matcher match.Matcher) ([]v1.ImageIndex, error) { matches := []v1.ImageIndex{} manifests, err := FindManifests(index, matcher) if err != nil { return nil, err } for _, desc := range manifests { if !desc.MediaType.IsIndex() { continue } // if it is not an index, ignore it idx, err := index.ImageIndex(desc.Digest) if err != nil { return nil, err } matches = append(matches, idx) } return matches, nil } type withManifests interface { Manifests() ([]Describable, error) } type withLayer interface { Layer(v1.Hash) (v1.Layer, error) } type describable struct { desc v1.Descriptor } func (d describable) Digest() (v1.Hash, error) { return d.desc.Digest, nil } func (d describable) Size() (int64, error) { return d.desc.Size, nil } func (d describable) MediaType() (types.MediaType, error) { return d.desc.MediaType, nil } func (d describable) Descriptor() (*v1.Descriptor, error) { return &d.desc, nil } // Manifests is analogous to v1.Image.Layers in that it allows values in the // returned list to be lazily evaluated, which enables an index to contain // an image that contains a streaming layer. // // This should have been part of the v1.ImageIndex interface, but wasn't. // It is instead usable through this extension interface. func Manifests(idx v1.ImageIndex) ([]Describable, error) { if wm, ok := idx.(withManifests); ok { return wm.Manifests() } return ComputeManifests(idx) } // ComputeManifests provides a fallback implementation for Manifests. func ComputeManifests(idx v1.ImageIndex) ([]Describable, error) { m, err := idx.IndexManifest() if err != nil { return nil, err } manifests := []Describable{} for _, desc := range m.Manifests { switch { case desc.MediaType.IsImage(): img, err := idx.Image(desc.Digest) if err != nil { return nil, err } manifests = append(manifests, img) case desc.MediaType.IsIndex(): idx, err := idx.ImageIndex(desc.Digest) if err != nil { return nil, err } manifests = append(manifests, idx) default: if wl, ok := idx.(withLayer); ok { layer, err := wl.Layer(desc.Digest) if err != nil { return nil, err } manifests = append(manifests, layer) } else { manifests = append(manifests, describable{desc}) } } } return manifests, nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/partial/uncompressed.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package partial import ( "bytes" "io" "sync" "github.com/google/go-containerregistry/internal/gzip" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/types" ) // UncompressedLayer represents the bare minimum interface a natively // uncompressed layer must implement for us to produce a v1.Layer type UncompressedLayer interface { // DiffID returns the Hash of the uncompressed layer. DiffID() (v1.Hash, error) // Uncompressed returns an io.ReadCloser for the uncompressed layer contents. Uncompressed() (io.ReadCloser, error) // Returns the mediaType for the compressed Layer MediaType() (types.MediaType, error) } // uncompressedLayerExtender implements v1.Image using the uncompressed base properties. type uncompressedLayerExtender struct { UncompressedLayer // Memoize size/hash so that the methods aren't twice as // expensive as doing this manually. hash v1.Hash size int64 hashSizeError error once sync.Once } // Compressed implements v1.Layer func (ule *uncompressedLayerExtender) Compressed() (io.ReadCloser, error) { u, err := ule.Uncompressed() if err != nil { return nil, err } return gzip.ReadCloser(u), nil } // Digest implements v1.Layer func (ule *uncompressedLayerExtender) Digest() (v1.Hash, error) { ule.calcSizeHash() return ule.hash, ule.hashSizeError } // Size implements v1.Layer func (ule *uncompressedLayerExtender) Size() (int64, error) { ule.calcSizeHash() return ule.size, ule.hashSizeError } func (ule *uncompressedLayerExtender) calcSizeHash() { ule.once.Do(func() { var r io.ReadCloser r, ule.hashSizeError = ule.Compressed() if ule.hashSizeError != nil { return } defer r.Close() ule.hash, ule.size, ule.hashSizeError = v1.SHA256(r) }) } // UncompressedToLayer fills in the missing methods from an UncompressedLayer so that it implements v1.Layer func UncompressedToLayer(ul UncompressedLayer) (v1.Layer, error) { return &uncompressedLayerExtender{UncompressedLayer: ul}, nil } // UncompressedImageCore represents the bare minimum interface a natively // uncompressed image must implement for us to produce a v1.Image type UncompressedImageCore interface { ImageCore // LayerByDiffID is a variation on the v1.Image method, which returns // an UncompressedLayer instead. LayerByDiffID(v1.Hash) (UncompressedLayer, error) } // UncompressedToImage fills in the missing methods from an UncompressedImageCore so that it implements v1.Image. func UncompressedToImage(uic UncompressedImageCore) (v1.Image, error) { return &uncompressedImageExtender{ UncompressedImageCore: uic, }, nil } // uncompressedImageExtender implements v1.Image by extending UncompressedImageCore with the // appropriate methods computed from the minimal core. type uncompressedImageExtender struct { UncompressedImageCore lock sync.Mutex manifest *v1.Manifest } // Assert that our extender type completes the v1.Image interface var _ v1.Image = (*uncompressedImageExtender)(nil) // Digest implements v1.Image func (i *uncompressedImageExtender) Digest() (v1.Hash, error) { return Digest(i) } // Manifest implements v1.Image func (i *uncompressedImageExtender) Manifest() (*v1.Manifest, error) { i.lock.Lock() defer i.lock.Unlock() if i.manifest != nil { return i.manifest, nil } b, err := i.RawConfigFile() if err != nil { return nil, err } cfgHash, cfgSize, err := v1.SHA256(bytes.NewReader(b)) if err != nil { return nil, err } m := &v1.Manifest{ SchemaVersion: 2, MediaType: types.DockerManifestSchema2, Config: v1.Descriptor{ MediaType: types.DockerConfigJSON, Size: cfgSize, Digest: cfgHash, }, } ls, err := i.Layers() if err != nil { return nil, err } m.Layers = make([]v1.Descriptor, len(ls)) for i, l := range ls { desc, err := Descriptor(l) if err != nil { return nil, err } m.Layers[i] = *desc } i.manifest = m return i.manifest, nil } // RawManifest implements v1.Image func (i *uncompressedImageExtender) RawManifest() ([]byte, error) { return RawManifest(i) } // Size implements v1.Image func (i *uncompressedImageExtender) Size() (int64, error) { return Size(i) } // ConfigName implements v1.Image func (i *uncompressedImageExtender) ConfigName() (v1.Hash, error) { return ConfigName(i) } // ConfigFile implements v1.Image func (i *uncompressedImageExtender) ConfigFile() (*v1.ConfigFile, error) { return ConfigFile(i) } // Layers implements v1.Image func (i *uncompressedImageExtender) Layers() ([]v1.Layer, error) { diffIDs, err := DiffIDs(i) if err != nil { return nil, err } ls := make([]v1.Layer, 0, len(diffIDs)) for _, h := range diffIDs { l, err := i.LayerByDiffID(h) if err != nil { return nil, err } ls = append(ls, l) } return ls, nil } // LayerByDiffID implements v1.Image func (i *uncompressedImageExtender) LayerByDiffID(diffID v1.Hash) (v1.Layer, error) { ul, err := i.UncompressedImageCore.LayerByDiffID(diffID) if err != nil { return nil, err } return UncompressedToLayer(ul) } // LayerByDigest implements v1.Image func (i *uncompressedImageExtender) LayerByDigest(h v1.Hash) (v1.Layer, error) { diffID, err := BlobToDiffID(i, h) if err != nil { return nil, err } return i.LayerByDiffID(diffID) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/partial/with.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package partial import ( "bytes" "encoding/json" "fmt" "io" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/types" ) // WithRawConfigFile defines the subset of v1.Image used by these helper methods type WithRawConfigFile interface { // RawConfigFile returns the serialized bytes of this image's config file. RawConfigFile() ([]byte, error) } // ConfigFile is a helper for implementing v1.Image func ConfigFile(i WithRawConfigFile) (*v1.ConfigFile, error) { b, err := i.RawConfigFile() if err != nil { return nil, err } return v1.ParseConfigFile(bytes.NewReader(b)) } // ConfigName is a helper for implementing v1.Image func ConfigName(i WithRawConfigFile) (v1.Hash, error) { b, err := i.RawConfigFile() if err != nil { return v1.Hash{}, err } h, _, err := v1.SHA256(bytes.NewReader(b)) return h, err } type configLayer struct { hash v1.Hash content []byte } // Digest implements v1.Layer func (cl *configLayer) Digest() (v1.Hash, error) { return cl.hash, nil } // DiffID implements v1.Layer func (cl *configLayer) DiffID() (v1.Hash, error) { return cl.hash, nil } // Uncompressed implements v1.Layer func (cl *configLayer) Uncompressed() (io.ReadCloser, error) { return io.NopCloser(bytes.NewBuffer(cl.content)), nil } // Compressed implements v1.Layer func (cl *configLayer) Compressed() (io.ReadCloser, error) { return io.NopCloser(bytes.NewBuffer(cl.content)), nil } // Size implements v1.Layer func (cl *configLayer) Size() (int64, error) { return int64(len(cl.content)), nil } func (cl *configLayer) MediaType() (types.MediaType, error) { // Defaulting this to OCIConfigJSON as it should remain // backwards compatible with DockerConfigJSON return types.OCIConfigJSON, nil } var _ v1.Layer = (*configLayer)(nil) // withConfigLayer allows partial image implementations to provide a layer // for their config file. type withConfigLayer interface { ConfigLayer() (v1.Layer, error) } // ConfigLayer implements v1.Layer from the raw config bytes. // This is so that clients (e.g. remote) can access the config as a blob. // // Images that want to return a specific layer implementation can implement // withConfigLayer. func ConfigLayer(i WithRawConfigFile) (v1.Layer, error) { if wcl, ok := unwrap(i).(withConfigLayer); ok { return wcl.ConfigLayer() } h, err := ConfigName(i) if err != nil { return nil, err } rcfg, err := i.RawConfigFile() if err != nil { return nil, err } return &configLayer{ hash: h, content: rcfg, }, nil } // WithConfigFile defines the subset of v1.Image used by these helper methods type WithConfigFile interface { // ConfigFile returns this image's config file. ConfigFile() (*v1.ConfigFile, error) } // DiffIDs is a helper for implementing v1.Image func DiffIDs(i WithConfigFile) ([]v1.Hash, error) { cfg, err := i.ConfigFile() if err != nil { return nil, err } return cfg.RootFS.DiffIDs, nil } // RawConfigFile is a helper for implementing v1.Image func RawConfigFile(i WithConfigFile) ([]byte, error) { cfg, err := i.ConfigFile() if err != nil { return nil, err } return json.Marshal(cfg) } // WithRawManifest defines the subset of v1.Image used by these helper methods type WithRawManifest interface { // RawManifest returns the serialized bytes of this image's config file. RawManifest() ([]byte, error) } // Digest is a helper for implementing v1.Image func Digest(i WithRawManifest) (v1.Hash, error) { mb, err := i.RawManifest() if err != nil { return v1.Hash{}, err } digest, _, err := v1.SHA256(bytes.NewReader(mb)) return digest, err } // Manifest is a helper for implementing v1.Image func Manifest(i WithRawManifest) (*v1.Manifest, error) { b, err := i.RawManifest() if err != nil { return nil, err } return v1.ParseManifest(bytes.NewReader(b)) } // WithManifest defines the subset of v1.Image used by these helper methods type WithManifest interface { // Manifest returns this image's Manifest object. Manifest() (*v1.Manifest, error) } // RawManifest is a helper for implementing v1.Image func RawManifest(i WithManifest) ([]byte, error) { m, err := i.Manifest() if err != nil { return nil, err } return json.Marshal(m) } // Size is a helper for implementing v1.Image func Size(i WithRawManifest) (int64, error) { b, err := i.RawManifest() if err != nil { return -1, err } return int64(len(b)), nil } // FSLayers is a helper for implementing v1.Image func FSLayers(i WithManifest) ([]v1.Hash, error) { m, err := i.Manifest() if err != nil { return nil, err } fsl := make([]v1.Hash, len(m.Layers)) for i, l := range m.Layers { fsl[i] = l.Digest } return fsl, nil } // BlobSize is a helper for implementing v1.Image func BlobSize(i WithManifest, h v1.Hash) (int64, error) { d, err := BlobDescriptor(i, h) if err != nil { return -1, err } return d.Size, nil } // BlobDescriptor is a helper for implementing v1.Image func BlobDescriptor(i WithManifest, h v1.Hash) (*v1.Descriptor, error) { m, err := i.Manifest() if err != nil { return nil, err } if m.Config.Digest == h { return &m.Config, nil } for _, l := range m.Layers { if l.Digest == h { return &l, nil } } return nil, fmt.Errorf("blob %v not found", h) } // WithManifestAndConfigFile defines the subset of v1.Image used by these helper methods type WithManifestAndConfigFile interface { WithConfigFile // Manifest returns this image's Manifest object. Manifest() (*v1.Manifest, error) } // BlobToDiffID is a helper for mapping between compressed // and uncompressed blob hashes. func BlobToDiffID(i WithManifestAndConfigFile, h v1.Hash) (v1.Hash, error) { blobs, err := FSLayers(i) if err != nil { return v1.Hash{}, err } diffIDs, err := DiffIDs(i) if err != nil { return v1.Hash{}, err } if len(blobs) != len(diffIDs) { return v1.Hash{}, fmt.Errorf("mismatched fs layers (%d) and diff ids (%d)", len(blobs), len(diffIDs)) } for i, blob := range blobs { if blob == h { return diffIDs[i], nil } } return v1.Hash{}, fmt.Errorf("unknown blob %v", h) } // DiffIDToBlob is a helper for mapping between uncompressed // and compressed blob hashes. func DiffIDToBlob(wm WithManifestAndConfigFile, h v1.Hash) (v1.Hash, error) { blobs, err := FSLayers(wm) if err != nil { return v1.Hash{}, err } diffIDs, err := DiffIDs(wm) if err != nil { return v1.Hash{}, err } if len(blobs) != len(diffIDs) { return v1.Hash{}, fmt.Errorf("mismatched fs layers (%d) and diff ids (%d)", len(blobs), len(diffIDs)) } for i, diffID := range diffIDs { if diffID == h { return blobs[i], nil } } return v1.Hash{}, fmt.Errorf("unknown diffID %v", h) } // WithDiffID defines the subset of v1.Layer for exposing the DiffID method. type WithDiffID interface { DiffID() (v1.Hash, error) } // withDescriptor allows partial layer implementations to provide a layer // descriptor to the partial image manifest builder. This allows partial // uncompressed layers to provide foreign layer metadata like URLs to the // uncompressed image manifest. type withDescriptor interface { Descriptor() (*v1.Descriptor, error) } // Describable represents something for which we can produce a v1.Descriptor. type Describable interface { Digest() (v1.Hash, error) MediaType() (types.MediaType, error) Size() (int64, error) } // Descriptor returns a v1.Descriptor given a Describable. It also encodes // some logic for unwrapping things that have been wrapped by // CompressedToLayer, UncompressedToLayer, CompressedToImage, or // UncompressedToImage. func Descriptor(d Describable) (*v1.Descriptor, error) { // If Describable implements Descriptor itself, return that. if wd, ok := unwrap(d).(withDescriptor); ok { return wd.Descriptor() } // If all else fails, compute the descriptor from the individual methods. var ( desc v1.Descriptor err error ) if desc.Size, err = d.Size(); err != nil { return nil, err } if desc.Digest, err = d.Digest(); err != nil { return nil, err } if desc.MediaType, err = d.MediaType(); err != nil { return nil, err } if wat, ok := d.(withArtifactType); ok { if desc.ArtifactType, err = wat.ArtifactType(); err != nil { return nil, err } } else { if wrm, ok := d.(WithRawManifest); ok && desc.MediaType.IsImage() { mf, _ := Manifest(wrm) // Failing to parse as a manifest should just be ignored. // The manifest might not be valid, and that's okay. if mf != nil && !mf.Config.MediaType.IsConfig() { desc.ArtifactType = string(mf.Config.MediaType) } } } return &desc, nil } type withArtifactType interface { ArtifactType() (string, error) } type withUncompressedSize interface { UncompressedSize() (int64, error) } // UncompressedSize returns the size of the Uncompressed layer. If the // underlying implementation doesn't implement UncompressedSize directly, // this will compute the uncompressedSize by reading everything returned // by Compressed(). This is potentially expensive and may consume the contents // for streaming layers. func UncompressedSize(l v1.Layer) (int64, error) { // If the layer implements UncompressedSize itself, return that. if wus, ok := unwrap(l).(withUncompressedSize); ok { return wus.UncompressedSize() } // The layer doesn't implement UncompressedSize, we need to compute it. rc, err := l.Uncompressed() if err != nil { return -1, err } defer rc.Close() return io.Copy(io.Discard, rc) } type withExists interface { Exists() (bool, error) } // Exists checks to see if a layer exists. This is a hack to work around the // mistakes of the partial package. Don't use this. func Exists(l v1.Layer) (bool, error) { // If the layer implements Exists itself, return that. if we, ok := unwrap(l).(withExists); ok { return we.Exists() } // The layer doesn't implement Exists, so we hope that calling Compressed() // is enough to trigger an error if the layer does not exist. rc, err := l.Compressed() if err != nil { return false, err } defer rc.Close() // We may want to try actually reading a single byte, but if we need to do // that, we should just fix this hack. return true, nil } // Recursively unwrap our wrappers so that we can check for the original implementation. // We might want to expose this? func unwrap(i any) any { if ule, ok := i.(*uncompressedLayerExtender); ok { return unwrap(ule.UncompressedLayer) } if cle, ok := i.(*compressedLayerExtender); ok { return unwrap(cle.CompressedLayer) } if uie, ok := i.(*uncompressedImageExtender); ok { return unwrap(uie.UncompressedImageCore) } if cie, ok := i.(*compressedImageExtender); ok { return unwrap(cie.CompressedImageCore) } return i } // ArtifactType returns the artifact type for the given manifest. // // If the manifest reports its own artifact type, that's returned, otherwise // the manifest is parsed and, if successful, its config.mediaType is returned. func ArtifactType(w WithManifest) (string, error) { if wat, ok := w.(withArtifactType); ok { return wat.ArtifactType() } mf, _ := w.Manifest() // Failing to parse as a manifest should just be ignored. // The manifest might not be valid, and that's okay. if mf != nil && !mf.Config.MediaType.IsConfig() { return string(mf.Config.MediaType), nil } return "", nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/platform.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package v1 import ( "fmt" "sort" "strings" ) // Platform represents the target os/arch for an image. type Platform struct { Architecture string `json:"architecture"` OS string `json:"os"` OSVersion string `json:"os.version,omitempty"` OSFeatures []string `json:"os.features,omitempty"` Variant string `json:"variant,omitempty"` Features []string `json:"features,omitempty"` } func (p Platform) String() string { if p.OS == "" { return "" } var b strings.Builder b.WriteString(p.OS) if p.Architecture != "" { b.WriteString("/") b.WriteString(p.Architecture) } if p.Variant != "" { b.WriteString("/") b.WriteString(p.Variant) } if p.OSVersion != "" { b.WriteString(":") b.WriteString(p.OSVersion) } return b.String() } // ParsePlatform parses a string representing a Platform, if possible. func ParsePlatform(s string) (*Platform, error) { var p Platform parts := strings.Split(strings.TrimSpace(s), ":") if len(parts) == 2 { p.OSVersion = parts[1] } parts = strings.Split(parts[0], "/") if len(parts) > 0 { p.OS = parts[0] } if len(parts) > 1 { p.Architecture = parts[1] } if len(parts) > 2 { p.Variant = parts[2] } if len(parts) > 3 { return nil, fmt.Errorf("too many slashes in platform spec: %s", s) } return &p, nil } // Equals returns true if the given platform is semantically equivalent to this one. // The order of Features and OSFeatures is not important. func (p Platform) Equals(o Platform) bool { return p.OS == o.OS && p.Architecture == o.Architecture && p.Variant == o.Variant && p.OSVersion == o.OSVersion && stringSliceEqualIgnoreOrder(p.OSFeatures, o.OSFeatures) && stringSliceEqualIgnoreOrder(p.Features, o.Features) } // Satisfies returns true if this Platform "satisfies" the given spec Platform. // // Note that this is different from Equals and that Satisfies is not reflexive. // // The given spec represents "requirements" such that any missing values in the // spec are not compared. // // For OSFeatures and Features, Satisfies will return true if this Platform's // fields contain a superset of the values in the spec's fields (order ignored). func (p Platform) Satisfies(spec Platform) bool { return satisfies(spec.OS, p.OS) && satisfies(spec.Architecture, p.Architecture) && satisfies(spec.Variant, p.Variant) && satisfies(spec.OSVersion, p.OSVersion) && satisfiesList(spec.OSFeatures, p.OSFeatures) && satisfiesList(spec.Features, p.Features) } func satisfies(want, have string) bool { return want == "" || want == have } func satisfiesList(want, have []string) bool { if len(want) == 0 { return true } set := map[string]struct{}{} for _, h := range have { set[h] = struct{}{} } for _, w := range want { if _, ok := set[w]; !ok { return false } } return true } // stringSliceEqual compares 2 string slices and returns if their contents are identical. func stringSliceEqual(a, b []string) bool { if len(a) != len(b) { return false } for i, elm := range a { if elm != b[i] { return false } } return true } // stringSliceEqualIgnoreOrder compares 2 string slices and returns if their contents are identical, ignoring order func stringSliceEqualIgnoreOrder(a, b []string) bool { if a != nil && b != nil { sort.Strings(a) sort.Strings(b) } return stringSliceEqual(a, b) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/progress.go ================================================ // Copyright 2020 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package v1 // Update representation of an update of transfer progress. Some functions // in this module can take a channel to which updates will be sent while a // transfer is in progress. // +k8s:deepcopy-gen=false type Update struct { Total int64 Complete int64 Error error } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/README.md ================================================ # `remote` [![GoDoc](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/remote?status.svg)](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/remote) The `remote` package implements a client for accessing a registry, per the [OCI distribution spec](https://github.com/opencontainers/distribution-spec/blob/master/spec.md). It leans heavily on the lower level [`transport`](/pkg/v1/remote/transport) package, which handles the authentication handshake and structured errors. ## Usage ```go package main import ( "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote" ) func main() { ref, err := name.ParseReference("gcr.io/google-containers/pause") if err != nil { panic(err) } img, err := remote.Image(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain)) if err != nil { panic(err) } // do stuff with img } ``` ## Structure

## Background There are a lot of confusingly similar terms that come up when talking about images in registries. ### Anatomy of an image In general... * A tag refers to an image manifest. * An image manifest references a config file and an orderered list of _compressed_ layers by sha256 digest. * A config file references an ordered list of _uncompressed_ layers by sha256 digest and contains runtime configuration. * The sha256 digest of the config file is the [image id](https://github.com/opencontainers/image-spec/blob/master/config.md#imageid) for the image. For example, an image with two layers would look something like this: ![image anatomy](/images/image-anatomy.dot.svg) ### Anatomy of an index In the normal case, an [index](https://github.com/opencontainers/image-spec/blob/master/image-index.md) is used to represent a multi-platform image. This was the original use case for a [manifest list](https://docs.docker.com/registry/spec/manifest-v2-2/#manifest-list). ![image index anatomy](/images/index-anatomy.dot.svg) It is possible for an index to reference another index, per the OCI [image-spec](https://github.com/opencontainers/image-spec/blob/master/media-types.md#compatibility-matrix). In theory, both an image and image index can reference arbitrary things via [descriptors](https://github.com/opencontainers/image-spec/blob/master/descriptor.md), e.g. see the [image layout example](https://github.com/opencontainers/image-spec/blob/master/image-layout.md#index-example), which references an application/xml file from an image index. That could look something like this: ![strange image index anatomy](/images/index-anatomy-strange.dot.svg) Using a recursive index like this might not be possible with all registries, but this flexibility allows for some interesting applications, e.g. the [OCI Artifacts](https://github.com/opencontainers/artifacts) effort. ### Anatomy of an image upload The structure of an image requires a delicate ordering when uploading an image to a registry. Below is a (slightly simplified) figure that describes how an image is prepared for upload to a registry and how the data flows between various artifacts: ![upload](/images/upload.dot.svg) Note that: * A config file references the uncompressed layer contents by sha256. * A manifest references the compressed layer contents by sha256 and the size of the layer. * A manifest references the config file contents by sha256 and the size of the file. It follows that during an upload, we need to upload layers before the config file, and we need to upload the config file before the manifest. Sometimes, we know all of this information ahead of time, (e.g. when copying from remote.Image), so the ordering is less important. In other cases, e.g. when using a [`stream.Layer`](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/stream#Layer), we can't compute anything until we have already uploaded the layer, so we need to be careful about ordering. ## Caveats ### schema 1 This package does not support schema 1 images, see [`#377`](https://github.com/google/go-containerregistry/issues/377), however, it's possible to do _something_ useful with them via [`remote.Get`](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/remote#Get), which doesn't try to interpret what is returned by the registry. [`crane.Copy`](https://godoc.org/github.com/google/go-containerregistry/pkg/crane#Copy) takes advantage of this to implement support for copying schema 1 images, see [here](https://github.com/google/go-containerregistry/blob/main/pkg/internal/legacy/copy.go). ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/catalog.go ================================================ // Copyright 2019 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package remote import ( "context" "encoding/json" "fmt" "net/http" "net/url" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote/transport" ) type Catalogs struct { Repos []string `json:"repositories"` Next string `json:"next,omitempty"` } // CatalogPage calls /_catalog, returning the list of repositories on the registry. func CatalogPage(target name.Registry, last string, n int, options ...Option) ([]string, error) { o, err := makeOptions(options...) if err != nil { return nil, err } f, err := newPuller(o).fetcher(o.context, target) if err != nil { return nil, err } uri := url.URL{ Scheme: target.Scheme(), Host: target.RegistryStr(), Path: "/v2/_catalog", RawQuery: fmt.Sprintf("last=%s&n=%d", url.QueryEscape(last), n), } req, err := http.NewRequest(http.MethodGet, uri.String(), nil) if err != nil { return nil, err } resp, err := f.client.Do(req.WithContext(o.context)) if err != nil { return nil, err } defer resp.Body.Close() if err := transport.CheckError(resp, http.StatusOK); err != nil { return nil, err } var parsed Catalogs if err := json.NewDecoder(resp.Body).Decode(&parsed); err != nil { return nil, err } return parsed.Repos, nil } // Catalog calls /_catalog, returning the list of repositories on the registry. func Catalog(ctx context.Context, target name.Registry, options ...Option) ([]string, error) { o, err := makeOptions(options...) if err != nil { return nil, err } // WithContext overrides the ctx passed directly. if o.context != context.Background() { ctx = o.context } return newPuller(o).catalog(ctx, target, o.pageSize) } func (f *fetcher) catalogPage(ctx context.Context, reg name.Registry, next string, pageSize int) (*Catalogs, error) { if next == "" { uri := &url.URL{ Scheme: reg.Scheme(), Host: reg.RegistryStr(), Path: "/v2/_catalog", } if pageSize > 0 { uri.RawQuery = fmt.Sprintf("n=%d", pageSize) } next = uri.String() } req, err := http.NewRequestWithContext(ctx, "GET", next, nil) if err != nil { return nil, err } resp, err := f.client.Do(req) if err != nil { return nil, err } if err := transport.CheckError(resp, http.StatusOK); err != nil { return nil, err } parsed := Catalogs{} if err := json.NewDecoder(resp.Body).Decode(&parsed); err != nil { return nil, err } if err := resp.Body.Close(); err != nil { return nil, err } uri, err := getNextPageURL(resp) if err != nil { return nil, err } if uri != nil { parsed.Next = uri.String() } return &parsed, nil } type Catalogger struct { f *fetcher reg name.Registry pageSize int page *Catalogs err error needMore bool } func (l *Catalogger) Next(ctx context.Context) (*Catalogs, error) { if l.needMore { l.page, l.err = l.f.catalogPage(ctx, l.reg, l.page.Next, l.pageSize) } else { l.needMore = true } return l.page, l.err } func (l *Catalogger) HasNext() bool { return l.page != nil && (!l.needMore || l.page.Next != "") } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/check.go ================================================ // Copyright 2019 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package remote import ( "context" "fmt" "net/http" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote/transport" ) // CheckPushPermission returns an error if the given keychain cannot authorize // a push operation to the given ref. // // This can be useful to check whether the caller has permission to push an // image before doing work to construct the image. // // TODO(#412): Remove the need for this method. func CheckPushPermission(ref name.Reference, kc authn.Keychain, t http.RoundTripper) error { auth, err := kc.Resolve(ref.Context().Registry) if err != nil { return fmt.Errorf("resolving authorization for %v failed: %w", ref.Context().Registry, err) } scopes := []string{ref.Scope(transport.PushScope)} tr, err := transport.NewWithContext(context.TODO(), ref.Context().Registry, auth, t, scopes) if err != nil { return fmt.Errorf("creating push check transport for %v failed: %w", ref.Context().Registry, err) } // TODO(jasonhall): Against GCR, just doing the token handshake is // enough, but this doesn't extend to Dockerhub // (https://github.com/docker/hub-feedback/issues/1771), so we actually // need to initiate an upload to tell whether the credentials can // authorize a push. Figure out how to return early here when we can, // to avoid a roundtrip for spec-compliant registries. w := writer{ repo: ref.Context(), client: &http.Client{Transport: tr}, } loc, _, err := w.initiateUpload(context.Background(), "", "", "") if loc != "" { // Since we're only initiating the upload to check whether we // can, we should attempt to cancel it, in case initiating // reserves some resources on the server. We shouldn't wait for // cancelling to complete, and we don't care if it fails. go w.cancelUpload(loc) } return err } func (w *writer) cancelUpload(loc string) { req, err := http.NewRequest(http.MethodDelete, loc, nil) if err != nil { return } _, _ = w.client.Do(req) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/delete.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package remote import ( "github.com/google/go-containerregistry/pkg/name" ) // Delete removes the specified image reference from the remote registry. func Delete(ref name.Reference, options ...Option) error { o, err := makeOptions(options...) if err != nil { return err } return newPusher(o).Delete(o.context, ref) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/descriptor.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package remote import ( "context" "errors" "fmt" "github.com/google/go-containerregistry/pkg/logs" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/partial" "github.com/google/go-containerregistry/pkg/v1/types" ) var allManifestMediaTypes = append(append([]types.MediaType{ types.DockerManifestSchema1, types.DockerManifestSchema1Signed, }, acceptableImageMediaTypes...), acceptableIndexMediaTypes...) // ErrSchema1 indicates that we received a schema1 manifest from the registry. // This library doesn't have plans to support this legacy image format: // https://github.com/google/go-containerregistry/issues/377 var ErrSchema1 = errors.New("see https://github.com/google/go-containerregistry/issues/377") // newErrSchema1 returns an ErrSchema1 with the unexpected MediaType. func newErrSchema1(schema types.MediaType) error { return fmt.Errorf("unsupported MediaType: %q, %w", schema, ErrSchema1) } // Descriptor provides access to metadata about remote artifact and accessors // for efficiently converting it into a v1.Image or v1.ImageIndex. type Descriptor struct { fetcher fetcher v1.Descriptor ref name.Reference Manifest []byte ctx context.Context // So we can share this implementation with Image. platform v1.Platform } func (d *Descriptor) toDesc() v1.Descriptor { return d.Descriptor } // RawManifest exists to satisfy the Taggable interface. func (d *Descriptor) RawManifest() ([]byte, error) { return d.Manifest, nil } // Get returns a remote.Descriptor for the given reference. The response from // the registry is left un-interpreted, for the most part. This is useful for // querying what kind of artifact a reference represents. // // See Head if you don't need the response body. func Get(ref name.Reference, options ...Option) (*Descriptor, error) { return get(ref, allManifestMediaTypes, options...) } // Head returns a v1.Descriptor for the given reference by issuing a HEAD // request. // // Note that the server response will not have a body, so any errors encountered // should be retried with Get to get more details. func Head(ref name.Reference, options ...Option) (*v1.Descriptor, error) { o, err := makeOptions(options...) if err != nil { return nil, err } return newPuller(o).Head(o.context, ref) } // Handle options and fetch the manifest with the acceptable MediaTypes in the // Accept header. func get(ref name.Reference, acceptable []types.MediaType, options ...Option) (*Descriptor, error) { o, err := makeOptions(options...) if err != nil { return nil, err } return newPuller(o).get(o.context, ref, acceptable, o.platform) } // Image converts the Descriptor into a v1.Image. // // If the fetched artifact is already an image, it will just return it. // // If the fetched artifact is an index, it will attempt to resolve the index to // a child image with the appropriate platform. // // See WithPlatform to set the desired platform. func (d *Descriptor) Image() (v1.Image, error) { switch d.MediaType { case types.DockerManifestSchema1, types.DockerManifestSchema1Signed: // We don't care to support schema 1 images: // https://github.com/google/go-containerregistry/issues/377 return nil, newErrSchema1(d.MediaType) case types.OCIImageIndex, types.DockerManifestList: // We want an image but the registry has an index, resolve it to an image. return d.remoteIndex().imageByPlatform(d.platform) case types.OCIManifestSchema1, types.DockerManifestSchema2: // These are expected. Enumerated here to allow a default case. default: // We could just return an error here, but some registries (e.g. static // registries) don't set the Content-Type headers correctly, so instead... logs.Warn.Printf("Unexpected media type for Image(): %s", d.MediaType) } // Wrap the v1.Layers returned by this v1.Image in a hint for downstream // remote.Write calls to facilitate cross-repo "mounting". imgCore, err := partial.CompressedToImage(d.remoteImage()) if err != nil { return nil, err } return &mountableImage{ Image: imgCore, Reference: d.ref, }, nil } // Schema1 converts the Descriptor into a v1.Image for v2 schema 1 media types. // // The v1.Image returned by this method does not implement the entire interface because it would be inefficient. // This exists mostly to make it easier to copy schema 1 images around or look at their filesystems. // This is separate from Image() to avoid a backward incompatible change for callers expecting ErrSchema1. func (d *Descriptor) Schema1() (v1.Image, error) { i := &schema1{ ref: d.ref, fetcher: d.fetcher, ctx: d.ctx, manifest: d.Manifest, mediaType: d.MediaType, descriptor: &d.Descriptor, } return &mountableImage{ Image: i, Reference: d.ref, }, nil } // ImageIndex converts the Descriptor into a v1.ImageIndex. func (d *Descriptor) ImageIndex() (v1.ImageIndex, error) { switch d.MediaType { case types.DockerManifestSchema1, types.DockerManifestSchema1Signed: // We don't care to support schema 1 images: // https://github.com/google/go-containerregistry/issues/377 return nil, newErrSchema1(d.MediaType) case types.OCIManifestSchema1, types.DockerManifestSchema2: // We want an index but the registry has an image, nothing we can do. return nil, fmt.Errorf("unexpected media type for ImageIndex(): %s; call Image() instead", d.MediaType) case types.OCIImageIndex, types.DockerManifestList: // These are expected. default: // We could just return an error here, but some registries (e.g. static // registries) don't set the Content-Type headers correctly, so instead... logs.Warn.Printf("Unexpected media type for ImageIndex(): %s", d.MediaType) } return d.remoteIndex(), nil } func (d *Descriptor) remoteImage() *remoteImage { return &remoteImage{ ref: d.ref, ctx: d.ctx, fetcher: d.fetcher, manifest: d.Manifest, mediaType: d.MediaType, descriptor: &d.Descriptor, } } func (d *Descriptor) remoteIndex() *remoteIndex { return &remoteIndex{ ref: d.ref, ctx: d.ctx, fetcher: d.fetcher, manifest: d.Manifest, mediaType: d.MediaType, descriptor: &d.Descriptor, } } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/doc.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package remote provides facilities for reading/writing v1.Images from/to // a remote image registry. package remote ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/fetcher.go ================================================ // Copyright 2023 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package remote import ( "bytes" "context" "fmt" "io" "net/http" "net/url" "strings" "github.com/google/go-containerregistry/internal/redact" "github.com/google/go-containerregistry/internal/verify" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/remote/transport" "github.com/google/go-containerregistry/pkg/v1/types" ) const ( kib = 1024 mib = 1024 * kib manifestLimit = 100 * mib ) // fetcher implements methods for reading from a registry. type fetcher struct { target resource client *http.Client } func makeFetcher(ctx context.Context, target resource, o *options) (*fetcher, error) { auth := o.auth if o.keychain != nil { kauth, err := o.keychain.Resolve(target) if err != nil { return nil, err } auth = kauth } reg, ok := target.(name.Registry) if !ok { repo, ok := target.(name.Repository) if !ok { return nil, fmt.Errorf("unexpected resource: %T", target) } reg = repo.Registry } tr, err := transport.NewWithContext(ctx, reg, auth, o.transport, []string{target.Scope(transport.PullScope)}) if err != nil { return nil, err } return &fetcher{ target: target, client: &http.Client{Transport: tr}, }, nil } func (f *fetcher) Do(req *http.Request) (*http.Response, error) { return f.client.Do(req) } type resource interface { Scheme() string RegistryStr() string Scope(string) string authn.Resource } // url returns a url.Url for the specified path in the context of this remote image reference. func (f *fetcher) url(resource, identifier string) url.URL { u := url.URL{ Scheme: f.target.Scheme(), Host: f.target.RegistryStr(), // Default path if this is not a repository. Path: "/v2/_catalog", } if repo, ok := f.target.(name.Repository); ok { u.Path = fmt.Sprintf("/v2/%s/%s/%s", repo.RepositoryStr(), resource, identifier) } return u } func (f *fetcher) get(ctx context.Context, ref name.Reference, acceptable []types.MediaType, platform v1.Platform) (*Descriptor, error) { b, desc, err := f.fetchManifest(ctx, ref, acceptable) if err != nil { return nil, err } return &Descriptor{ ref: ref, ctx: ctx, fetcher: *f, Manifest: b, Descriptor: *desc, platform: platform, }, nil } func (f *fetcher) fetchManifest(ctx context.Context, ref name.Reference, acceptable []types.MediaType) ([]byte, *v1.Descriptor, error) { u := f.url("manifests", ref.Identifier()) req, err := http.NewRequest(http.MethodGet, u.String(), nil) if err != nil { return nil, nil, err } accept := []string{} for _, mt := range acceptable { accept = append(accept, string(mt)) } req.Header.Set("Accept", strings.Join(accept, ",")) resp, err := f.client.Do(req.WithContext(ctx)) if err != nil { return nil, nil, err } defer resp.Body.Close() if err := transport.CheckError(resp, http.StatusOK); err != nil { return nil, nil, err } manifest, err := io.ReadAll(io.LimitReader(resp.Body, manifestLimit)) if err != nil { return nil, nil, err } digest, size, err := v1.SHA256(bytes.NewReader(manifest)) if err != nil { return nil, nil, err } mediaType := types.MediaType(resp.Header.Get("Content-Type")) contentDigest, err := v1.NewHash(resp.Header.Get("Docker-Content-Digest")) if err == nil && mediaType == types.DockerManifestSchema1Signed { // If we can parse the digest from the header, and it's a signed schema 1 // manifest, let's use that for the digest to appease older registries. digest = contentDigest } // Validate the digest matches what we asked for, if pulling by digest. if dgst, ok := ref.(name.Digest); ok { if digest.String() != dgst.DigestStr() { return nil, nil, fmt.Errorf("manifest digest: %q does not match requested digest: %q for %q", digest, dgst.DigestStr(), ref) } } var artifactType string mf, _ := v1.ParseManifest(bytes.NewReader(manifest)) // Failing to parse as a manifest should just be ignored. // The manifest might not be valid, and that's okay. if mf != nil && !mf.Config.MediaType.IsConfig() { artifactType = string(mf.Config.MediaType) } // Do nothing for tags; I give up. // // We'd like to validate that the "Docker-Content-Digest" header matches what is returned by the registry, // but so many registries implement this incorrectly that it's not worth checking. // // For reference: // https://github.com/GoogleContainerTools/kaniko/issues/298 // Return all this info since we have to calculate it anyway. desc := v1.Descriptor{ Digest: digest, Size: size, MediaType: mediaType, ArtifactType: artifactType, } return manifest, &desc, nil } func (f *fetcher) headManifest(ctx context.Context, ref name.Reference, acceptable []types.MediaType) (*v1.Descriptor, error) { u := f.url("manifests", ref.Identifier()) req, err := http.NewRequest(http.MethodHead, u.String(), nil) if err != nil { return nil, err } accept := []string{} for _, mt := range acceptable { accept = append(accept, string(mt)) } req.Header.Set("Accept", strings.Join(accept, ",")) resp, err := f.client.Do(req.WithContext(ctx)) if err != nil { return nil, err } defer resp.Body.Close() if err := transport.CheckError(resp, http.StatusOK); err != nil { return nil, err } mth := resp.Header.Get("Content-Type") if mth == "" { return nil, fmt.Errorf("HEAD %s: response did not include Content-Type header", u.String()) } mediaType := types.MediaType(mth) size := resp.ContentLength if size == -1 { return nil, fmt.Errorf("GET %s: response did not include Content-Length header", u.String()) } dh := resp.Header.Get("Docker-Content-Digest") if dh == "" { return nil, fmt.Errorf("HEAD %s: response did not include Docker-Content-Digest header", u.String()) } digest, err := v1.NewHash(dh) if err != nil { return nil, err } // Validate the digest matches what we asked for, if pulling by digest. if dgst, ok := ref.(name.Digest); ok { if digest.String() != dgst.DigestStr() { return nil, fmt.Errorf("manifest digest: %q does not match requested digest: %q for %q", digest, dgst.DigestStr(), ref) } } // Return all this info since we have to calculate it anyway. return &v1.Descriptor{ Digest: digest, Size: size, MediaType: mediaType, }, nil } func (f *fetcher) fetchBlob(ctx context.Context, size int64, h v1.Hash) (io.ReadCloser, error) { u := f.url("blobs", h.String()) req, err := http.NewRequest(http.MethodGet, u.String(), nil) if err != nil { return nil, err } resp, err := f.client.Do(req.WithContext(ctx)) if err != nil { return nil, redact.Error(err) } if err := transport.CheckError(resp, http.StatusOK); err != nil { resp.Body.Close() return nil, err } // Do whatever we can. // If we have an expected size and Content-Length doesn't match, return an error. // If we don't have an expected size and we do have a Content-Length, use Content-Length. if hsize := resp.ContentLength; hsize != -1 { if size == verify.SizeUnknown { size = hsize } else if hsize != size { return nil, fmt.Errorf("GET %s: Content-Length header %d does not match expected size %d", u.String(), hsize, size) } } return verify.ReadCloser(resp.Body, size, h) } func (f *fetcher) headBlob(ctx context.Context, h v1.Hash) (*http.Response, error) { u := f.url("blobs", h.String()) req, err := http.NewRequest(http.MethodHead, u.String(), nil) if err != nil { return nil, err } resp, err := f.client.Do(req.WithContext(ctx)) if err != nil { return nil, redact.Error(err) } if err := transport.CheckError(resp, http.StatusOK); err != nil { resp.Body.Close() return nil, err } return resp, nil } func (f *fetcher) blobExists(ctx context.Context, h v1.Hash) (bool, error) { u := f.url("blobs", h.String()) req, err := http.NewRequest(http.MethodHead, u.String(), nil) if err != nil { return false, err } resp, err := f.client.Do(req.WithContext(ctx)) if err != nil { return false, redact.Error(err) } defer resp.Body.Close() if err := transport.CheckError(resp, http.StatusOK, http.StatusNotFound); err != nil { return false, err } return resp.StatusCode == http.StatusOK, nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/image.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package remote import ( "bytes" "context" "io" "net/http" "net/url" "sync" "github.com/google/go-containerregistry/internal/redact" "github.com/google/go-containerregistry/internal/verify" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/partial" "github.com/google/go-containerregistry/pkg/v1/remote/transport" "github.com/google/go-containerregistry/pkg/v1/types" ) var acceptableImageMediaTypes = []types.MediaType{ types.DockerManifestSchema2, types.OCIManifestSchema1, } // remoteImage accesses an image from a remote registry type remoteImage struct { fetcher fetcher ref name.Reference ctx context.Context manifestLock sync.Mutex // Protects manifest manifest []byte configLock sync.Mutex // Protects config config []byte mediaType types.MediaType descriptor *v1.Descriptor } func (r *remoteImage) ArtifactType() (string, error) { // kind of a hack, but RawManifest does appropriate locking/memoization // and makes sure r.descriptor is populated. if _, err := r.RawManifest(); err != nil { return "", err } return r.descriptor.ArtifactType, nil } var _ partial.CompressedImageCore = (*remoteImage)(nil) // Image provides access to a remote image reference. func Image(ref name.Reference, options ...Option) (v1.Image, error) { desc, err := Get(ref, options...) if err != nil { return nil, err } return desc.Image() } func (r *remoteImage) MediaType() (types.MediaType, error) { if string(r.mediaType) != "" { return r.mediaType, nil } return types.DockerManifestSchema2, nil } func (r *remoteImage) RawManifest() ([]byte, error) { r.manifestLock.Lock() defer r.manifestLock.Unlock() if r.manifest != nil { return r.manifest, nil } // NOTE(jonjohnsonjr): We should never get here because the public entrypoints // do type-checking via remote.Descriptor. I've left this here for tests that // directly instantiate a remoteImage. manifest, desc, err := r.fetcher.fetchManifest(r.ctx, r.ref, acceptableImageMediaTypes) if err != nil { return nil, err } if r.descriptor == nil { r.descriptor = desc } r.mediaType = desc.MediaType r.manifest = manifest return r.manifest, nil } func (r *remoteImage) RawConfigFile() ([]byte, error) { r.configLock.Lock() defer r.configLock.Unlock() if r.config != nil { return r.config, nil } m, err := partial.Manifest(r) if err != nil { return nil, err } if m.Config.Data != nil { if err := verify.Descriptor(m.Config); err != nil { return nil, err } r.config = m.Config.Data return r.config, nil } body, err := r.fetcher.fetchBlob(r.ctx, m.Config.Size, m.Config.Digest) if err != nil { return nil, err } defer body.Close() r.config, err = io.ReadAll(body) if err != nil { return nil, err } return r.config, nil } // Descriptor retains the original descriptor from an index manifest. // See partial.Descriptor. func (r *remoteImage) Descriptor() (*v1.Descriptor, error) { // kind of a hack, but RawManifest does appropriate locking/memoization // and makes sure r.descriptor is populated. _, err := r.RawManifest() return r.descriptor, err } func (r *remoteImage) ConfigLayer() (v1.Layer, error) { if _, err := r.RawManifest(); err != nil { return nil, err } m, err := partial.Manifest(r) if err != nil { return nil, err } return partial.CompressedToLayer(&remoteImageLayer{ ri: r, ctx: r.ctx, digest: m.Config.Digest, }) } // remoteImageLayer implements partial.CompressedLayer type remoteImageLayer struct { ri *remoteImage ctx context.Context digest v1.Hash } // Digest implements partial.CompressedLayer func (rl *remoteImageLayer) Digest() (v1.Hash, error) { return rl.digest, nil } // Compressed implements partial.CompressedLayer func (rl *remoteImageLayer) Compressed() (io.ReadCloser, error) { urls := []url.URL{rl.ri.fetcher.url("blobs", rl.digest.String())} // Add alternative layer sources from URLs (usually none). d, err := partial.BlobDescriptor(rl, rl.digest) if err != nil { return nil, err } if d.Data != nil { return verify.ReadCloser(io.NopCloser(bytes.NewReader(d.Data)), d.Size, d.Digest) } // We don't want to log binary layers -- this can break terminals. ctx := redact.NewContext(rl.ctx, "omitting binary blobs from logs") for _, s := range d.URLs { u, err := url.Parse(s) if err != nil { return nil, err } urls = append(urls, *u) } // The lastErr for most pulls will be the same (the first error), but for // foreign layers we'll want to surface the last one, since we try to pull // from the registry first, which would often fail. // TODO: Maybe we don't want to try pulling from the registry first? var lastErr error for _, u := range urls { req, err := http.NewRequest(http.MethodGet, u.String(), nil) if err != nil { return nil, err } resp, err := rl.ri.fetcher.Do(req.WithContext(ctx)) if err != nil { lastErr = err continue } if err := transport.CheckError(resp, http.StatusOK); err != nil { resp.Body.Close() lastErr = err continue } return verify.ReadCloser(resp.Body, d.Size, rl.digest) } return nil, lastErr } // Manifest implements partial.WithManifest so that we can use partial.BlobSize below. func (rl *remoteImageLayer) Manifest() (*v1.Manifest, error) { return partial.Manifest(rl.ri) } // MediaType implements v1.Layer func (rl *remoteImageLayer) MediaType() (types.MediaType, error) { bd, err := partial.BlobDescriptor(rl, rl.digest) if err != nil { return "", err } return bd.MediaType, nil } // Size implements partial.CompressedLayer func (rl *remoteImageLayer) Size() (int64, error) { // Look up the size of this digest in the manifest to avoid a request. return partial.BlobSize(rl, rl.digest) } // ConfigFile implements partial.WithManifestAndConfigFile so that we can use partial.BlobToDiffID below. func (rl *remoteImageLayer) ConfigFile() (*v1.ConfigFile, error) { return partial.ConfigFile(rl.ri) } // DiffID implements partial.WithDiffID so that we don't recompute a DiffID that we already have // available in our ConfigFile. func (rl *remoteImageLayer) DiffID() (v1.Hash, error) { return partial.BlobToDiffID(rl, rl.digest) } // Descriptor retains the original descriptor from an image manifest. // See partial.Descriptor. func (rl *remoteImageLayer) Descriptor() (*v1.Descriptor, error) { return partial.BlobDescriptor(rl, rl.digest) } // See partial.Exists. func (rl *remoteImageLayer) Exists() (bool, error) { return rl.ri.fetcher.blobExists(rl.ri.ctx, rl.digest) } // LayerByDigest implements partial.CompressedLayer func (r *remoteImage) LayerByDigest(h v1.Hash) (partial.CompressedLayer, error) { return &remoteImageLayer{ ri: r, ctx: r.ctx, digest: h, }, nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/index.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package remote import ( "bytes" "context" "fmt" "sync" "github.com/google/go-containerregistry/internal/verify" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/partial" "github.com/google/go-containerregistry/pkg/v1/types" ) var acceptableIndexMediaTypes = []types.MediaType{ types.DockerManifestList, types.OCIImageIndex, } // remoteIndex accesses an index from a remote registry type remoteIndex struct { fetcher fetcher ref name.Reference ctx context.Context manifestLock sync.Mutex // Protects manifest manifest []byte mediaType types.MediaType descriptor *v1.Descriptor } // Index provides access to a remote index reference. func Index(ref name.Reference, options ...Option) (v1.ImageIndex, error) { desc, err := get(ref, acceptableIndexMediaTypes, options...) if err != nil { return nil, err } return desc.ImageIndex() } func (r *remoteIndex) MediaType() (types.MediaType, error) { if string(r.mediaType) != "" { return r.mediaType, nil } return types.DockerManifestList, nil } func (r *remoteIndex) Digest() (v1.Hash, error) { return partial.Digest(r) } func (r *remoteIndex) Size() (int64, error) { return partial.Size(r) } func (r *remoteIndex) RawManifest() ([]byte, error) { r.manifestLock.Lock() defer r.manifestLock.Unlock() if r.manifest != nil { return r.manifest, nil } // NOTE(jonjohnsonjr): We should never get here because the public entrypoints // do type-checking via remote.Descriptor. I've left this here for tests that // directly instantiate a remoteIndex. manifest, desc, err := r.fetcher.fetchManifest(r.ctx, r.ref, acceptableIndexMediaTypes) if err != nil { return nil, err } if r.descriptor == nil { r.descriptor = desc } r.mediaType = desc.MediaType r.manifest = manifest return r.manifest, nil } func (r *remoteIndex) IndexManifest() (*v1.IndexManifest, error) { b, err := r.RawManifest() if err != nil { return nil, err } return v1.ParseIndexManifest(bytes.NewReader(b)) } func (r *remoteIndex) Image(h v1.Hash) (v1.Image, error) { desc, err := r.childByHash(h) if err != nil { return nil, err } // Descriptor.Image will handle coercing nested indexes into an Image. return desc.Image() } // Descriptor retains the original descriptor from an index manifest. // See partial.Descriptor. func (r *remoteIndex) Descriptor() (*v1.Descriptor, error) { // kind of a hack, but RawManifest does appropriate locking/memoization // and makes sure r.descriptor is populated. _, err := r.RawManifest() return r.descriptor, err } func (r *remoteIndex) ImageIndex(h v1.Hash) (v1.ImageIndex, error) { desc, err := r.childByHash(h) if err != nil { return nil, err } return desc.ImageIndex() } // Workaround for #819. func (r *remoteIndex) Layer(h v1.Hash) (v1.Layer, error) { index, err := r.IndexManifest() if err != nil { return nil, err } for _, childDesc := range index.Manifests { if h == childDesc.Digest { l, err := partial.CompressedToLayer(&remoteLayer{ fetcher: r.fetcher, ctx: r.ctx, digest: h, }) if err != nil { return nil, err } return &MountableLayer{ Layer: l, Reference: r.ref.Context().Digest(h.String()), }, nil } } return nil, fmt.Errorf("layer not found: %s", h) } func (r *remoteIndex) imageByPlatform(platform v1.Platform) (v1.Image, error) { desc, err := r.childByPlatform(platform) if err != nil { return nil, err } // Descriptor.Image will handle coercing nested indexes into an Image. return desc.Image() } // This naively matches the first manifest with matching platform attributes. // // We should probably use this instead: // // github.com/containerd/containerd/platforms // // But first we'd need to migrate to: // // github.com/opencontainers/image-spec/specs-go/v1 func (r *remoteIndex) childByPlatform(platform v1.Platform) (*Descriptor, error) { index, err := r.IndexManifest() if err != nil { return nil, err } for _, childDesc := range index.Manifests { // If platform is missing from child descriptor, assume it's amd64/linux. p := defaultPlatform if childDesc.Platform != nil { p = *childDesc.Platform } if matchesPlatform(p, platform) { return r.childDescriptor(childDesc, platform) } } return nil, fmt.Errorf("no child with platform %+v in index %s", platform, r.ref) } func (r *remoteIndex) childByHash(h v1.Hash) (*Descriptor, error) { index, err := r.IndexManifest() if err != nil { return nil, err } for _, childDesc := range index.Manifests { if h == childDesc.Digest { return r.childDescriptor(childDesc, defaultPlatform) } } return nil, fmt.Errorf("no child with digest %s in index %s", h, r.ref) } // Convert one of this index's child's v1.Descriptor into a remote.Descriptor, with the given platform option. func (r *remoteIndex) childDescriptor(child v1.Descriptor, platform v1.Platform) (*Descriptor, error) { ref := r.ref.Context().Digest(child.Digest.String()) var ( manifest []byte err error ) if child.Data != nil { if err := verify.Descriptor(child); err != nil { return nil, err } manifest = child.Data } else { manifest, _, err = r.fetcher.fetchManifest(r.ctx, ref, []types.MediaType{child.MediaType}) if err != nil { return nil, err } } if child.MediaType.IsImage() { mf, _ := v1.ParseManifest(bytes.NewReader(manifest)) // Failing to parse as a manifest should just be ignored. // The manifest might not be valid, and that's okay. if mf != nil && !mf.Config.MediaType.IsConfig() { child.ArtifactType = string(mf.Config.MediaType) } } return &Descriptor{ ref: ref, ctx: r.ctx, fetcher: r.fetcher, Manifest: manifest, Descriptor: child, platform: platform, }, nil } // matchesPlatform checks if the given platform matches the required platforms. // The given platform matches the required platform if // - architecture and OS are identical. // - OS version and variant are identical if provided. // - features and OS features of the required platform are subsets of those of the given platform. func matchesPlatform(given, required v1.Platform) bool { // Required fields that must be identical. if given.Architecture != required.Architecture || given.OS != required.OS { return false } // Optional fields that may be empty, but must be identical if provided. if required.OSVersion != "" && given.OSVersion != required.OSVersion { return false } if required.Variant != "" && given.Variant != required.Variant { return false } // Verify required platform's features are a subset of given platform's features. if !isSubset(given.OSFeatures, required.OSFeatures) { return false } if !isSubset(given.Features, required.Features) { return false } return true } // isSubset checks if the required array of strings is a subset of the given lst. func isSubset(lst, required []string) bool { set := make(map[string]bool) for _, value := range lst { set[value] = true } for _, value := range required { if _, ok := set[value]; !ok { return false } } return true } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/layer.go ================================================ // Copyright 2019 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package remote import ( "context" "io" "github.com/google/go-containerregistry/internal/redact" "github.com/google/go-containerregistry/internal/verify" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/types" ) // remoteImagelayer implements partial.CompressedLayer type remoteLayer struct { ctx context.Context fetcher fetcher digest v1.Hash } // Compressed implements partial.CompressedLayer func (rl *remoteLayer) Compressed() (io.ReadCloser, error) { // We don't want to log binary layers -- this can break terminals. ctx := redact.NewContext(rl.ctx, "omitting binary blobs from logs") return rl.fetcher.fetchBlob(ctx, verify.SizeUnknown, rl.digest) } // Compressed implements partial.CompressedLayer func (rl *remoteLayer) Size() (int64, error) { resp, err := rl.fetcher.headBlob(rl.ctx, rl.digest) if err != nil { return -1, err } defer resp.Body.Close() return resp.ContentLength, nil } // Digest implements partial.CompressedLayer func (rl *remoteLayer) Digest() (v1.Hash, error) { return rl.digest, nil } // MediaType implements v1.Layer func (rl *remoteLayer) MediaType() (types.MediaType, error) { return types.DockerLayer, nil } // See partial.Exists. func (rl *remoteLayer) Exists() (bool, error) { return rl.fetcher.blobExists(rl.ctx, rl.digest) } // Layer reads the given blob reference from a registry as a Layer. A blob // reference here is just a punned name.Digest where the digest portion is the // digest of the blob to be read and the repository portion is the repo where // that blob lives. func Layer(ref name.Digest, options ...Option) (v1.Layer, error) { o, err := makeOptions(options...) if err != nil { return nil, err } return newPuller(o).Layer(o.context, ref) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/list.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package remote import ( "context" "encoding/json" "fmt" "net/http" "net/url" "strings" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote/transport" ) // ListWithContext calls List with the given context. // // Deprecated: Use List and WithContext. This will be removed in a future release. func ListWithContext(ctx context.Context, repo name.Repository, options ...Option) ([]string, error) { return List(repo, append(options, WithContext(ctx))...) } // List calls /tags/list for the given repository, returning the list of tags // in the "tags" property. func List(repo name.Repository, options ...Option) ([]string, error) { o, err := makeOptions(options...) if err != nil { return nil, err } return newPuller(o).List(o.context, repo) } type Tags struct { Name string `json:"name"` Tags []string `json:"tags"` Next string `json:"next,omitempty"` } func (f *fetcher) listPage(ctx context.Context, repo name.Repository, next string, pageSize int) (*Tags, error) { if next == "" { uri := &url.URL{ Scheme: repo.Scheme(), Host: repo.RegistryStr(), Path: fmt.Sprintf("/v2/%s/tags/list", repo.RepositoryStr()), } if pageSize > 0 { uri.RawQuery = fmt.Sprintf("n=%d", pageSize) } next = uri.String() } req, err := http.NewRequestWithContext(ctx, "GET", next, nil) if err != nil { return nil, err } resp, err := f.client.Do(req) if err != nil { return nil, err } if err := transport.CheckError(resp, http.StatusOK); err != nil { return nil, err } parsed := Tags{} if err := json.NewDecoder(resp.Body).Decode(&parsed); err != nil { return nil, err } if err := resp.Body.Close(); err != nil { return nil, err } uri, err := getNextPageURL(resp) if err != nil { return nil, err } if uri != nil { parsed.Next = uri.String() } return &parsed, nil } // getNextPageURL checks if there is a Link header in a http.Response which // contains a link to the next page. If yes it returns the url.URL of the next // page otherwise it returns nil. func getNextPageURL(resp *http.Response) (*url.URL, error) { link := resp.Header.Get("Link") if link == "" { return nil, nil } if link[0] != '<' { return nil, fmt.Errorf("failed to parse link header: missing '<' in: %s", link) } end := strings.Index(link, ">") if end == -1 { return nil, fmt.Errorf("failed to parse link header: missing '>' in: %s", link) } link = link[1:end] linkURL, err := url.Parse(link) if err != nil { return nil, err } if resp.Request == nil || resp.Request.URL == nil { return nil, nil } linkURL = resp.Request.URL.ResolveReference(linkURL) return linkURL, nil } type Lister struct { f *fetcher repo name.Repository pageSize int page *Tags err error needMore bool } func (l *Lister) Next(ctx context.Context) (*Tags, error) { if l.needMore { l.page, l.err = l.f.listPage(ctx, l.repo, l.page.Next, l.pageSize) } else { l.needMore = true } return l.page, l.err } func (l *Lister) HasNext() bool { return l.page != nil && (!l.needMore || l.page.Next != "") } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/mount.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package remote import ( "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/partial" ) // MountableLayer wraps a v1.Layer in a shim that enables the layer to be // "mounted" when published to another registry. type MountableLayer struct { v1.Layer Reference name.Reference } // Descriptor retains the original descriptor from an image manifest. // See partial.Descriptor. func (ml *MountableLayer) Descriptor() (*v1.Descriptor, error) { return partial.Descriptor(ml.Layer) } // Exists is a hack. See partial.Exists. func (ml *MountableLayer) Exists() (bool, error) { return partial.Exists(ml.Layer) } // mountableImage wraps the v1.Layer references returned by the embedded v1.Image // in MountableLayer's so that remote.Write might attempt to mount them from their // source repository. type mountableImage struct { v1.Image Reference name.Reference } // Layers implements v1.Image func (mi *mountableImage) Layers() ([]v1.Layer, error) { ls, err := mi.Image.Layers() if err != nil { return nil, err } mls := make([]v1.Layer, 0, len(ls)) for _, l := range ls { mls = append(mls, &MountableLayer{ Layer: l, Reference: mi.Reference, }) } return mls, nil } // LayerByDigest implements v1.Image func (mi *mountableImage) LayerByDigest(d v1.Hash) (v1.Layer, error) { l, err := mi.Image.LayerByDigest(d) if err != nil { return nil, err } return &MountableLayer{ Layer: l, Reference: mi.Reference, }, nil } // LayerByDiffID implements v1.Image func (mi *mountableImage) LayerByDiffID(d v1.Hash) (v1.Layer, error) { l, err := mi.Image.LayerByDiffID(d) if err != nil { return nil, err } return &MountableLayer{ Layer: l, Reference: mi.Reference, }, nil } // Descriptor retains the original descriptor from an index manifest. // See partial.Descriptor. func (mi *mountableImage) Descriptor() (*v1.Descriptor, error) { return partial.Descriptor(mi.Image) } // ConfigLayer retains the original reference so that it can be mounted. // See partial.ConfigLayer. func (mi *mountableImage) ConfigLayer() (v1.Layer, error) { l, err := partial.ConfigLayer(mi.Image) if err != nil { return nil, err } return &MountableLayer{ Layer: l, Reference: mi.Reference, }, nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/multi_write.go ================================================ // Copyright 2020 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package remote import ( "github.com/google/go-containerregistry/pkg/name" "golang.org/x/sync/errgroup" ) // MultiWrite writes the given Images or ImageIndexes to the given refs, as // efficiently as possible, by deduping shared layer blobs while uploading them // in parallel. func MultiWrite(todo map[name.Reference]Taggable, options ...Option) (rerr error) { o, err := makeOptions(options...) if err != nil { return err } if o.progress != nil { defer func() { o.progress.Close(rerr) }() } p := newPusher(o) g, ctx := errgroup.WithContext(o.context) g.SetLimit(o.jobs) for ref, t := range todo { ref, t := ref, t g.Go(func() error { return p.Push(ctx, ref, t) }) } return g.Wait() } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/options.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package remote import ( "context" "errors" "io" "net" "net/http" "syscall" "time" "github.com/google/go-containerregistry/internal/retry" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/logs" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/remote/transport" ) // Option is a functional option for remote operations. type Option func(*options) error type options struct { auth authn.Authenticator keychain authn.Keychain transport http.RoundTripper context context.Context jobs int userAgent string allowNondistributableArtifacts bool progress *progress retryBackoff Backoff retryPredicate retry.Predicate retryStatusCodes []int // Only these options can overwrite Reuse()d options. platform v1.Platform pageSize int filter map[string]string // Set by Reuse, we currently store one or the other. puller *Puller pusher *Pusher } var defaultPlatform = v1.Platform{ Architecture: "amd64", OS: "linux", } // Backoff is an alias of retry.Backoff to expose this configuration option to consumers of this lib type Backoff = retry.Backoff var defaultRetryPredicate retry.Predicate = func(err error) bool { // Various failure modes here, as we're often reading from and writing to // the network. if retry.IsTemporary(err) || errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, io.EOF) || errors.Is(err, syscall.EPIPE) || errors.Is(err, syscall.ECONNRESET) || errors.Is(err, net.ErrClosed) { logs.Warn.Printf("retrying %v", err) return true } return false } // Try this three times, waiting 1s after first failure, 3s after second. var defaultRetryBackoff = Backoff{ Duration: 1.0 * time.Second, Factor: 3.0, Jitter: 0.1, Steps: 3, } // Useful for tests var fastBackoff = Backoff{ Duration: 1.0 * time.Millisecond, Factor: 3.0, Jitter: 0.1, Steps: 3, } var defaultRetryStatusCodes = []int{ http.StatusRequestTimeout, http.StatusInternalServerError, http.StatusBadGateway, http.StatusServiceUnavailable, http.StatusGatewayTimeout, 499, // nginx-specific, client closed request 522, // Cloudflare-specific, connection timeout } const ( defaultJobs = 4 // ECR returns an error if n > 1000: // https://github.com/google/go-containerregistry/issues/1091 defaultPageSize = 1000 ) // DefaultTransport is based on http.DefaultTransport with modifications // documented inline below. var DefaultTransport http.RoundTripper = &http.Transport{ Proxy: http.ProxyFromEnvironment, DialContext: (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, }).DialContext, ForceAttemptHTTP2: true, MaxIdleConns: 100, IdleConnTimeout: 90 * time.Second, TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second, // We usually are dealing with 2 hosts (at most), split MaxIdleConns between them. MaxIdleConnsPerHost: 50, } func makeOptions(opts ...Option) (*options, error) { o := &options{ transport: DefaultTransport, platform: defaultPlatform, context: context.Background(), jobs: defaultJobs, pageSize: defaultPageSize, retryPredicate: defaultRetryPredicate, retryBackoff: defaultRetryBackoff, retryStatusCodes: defaultRetryStatusCodes, } for _, option := range opts { if err := option(o); err != nil { return nil, err } } switch { case o.auth != nil && o.keychain != nil: // It is a better experience to explicitly tell a caller their auth is misconfigured // than potentially fail silently when the correct auth is overridden by option misuse. return nil, errors.New("provide an option for either authn.Authenticator or authn.Keychain, not both") case o.auth == nil: o.auth = authn.Anonymous } // transport.Wrapper is a signal that consumers are opt-ing into providing their own transport without any additional wrapping. // This is to allow consumers full control over the transports logic, such as providing retry logic. if _, ok := o.transport.(*transport.Wrapper); !ok { // Wrap the transport in something that logs requests and responses. // It's expensive to generate the dumps, so skip it if we're writing // to nothing. if logs.Enabled(logs.Debug) { o.transport = transport.NewLogger(o.transport) } // Wrap the transport in something that can retry network flakes. o.transport = transport.NewRetry(o.transport, transport.WithRetryPredicate(defaultRetryPredicate), transport.WithRetryStatusCodes(o.retryStatusCodes...)) // Wrap this last to prevent transport.New from double-wrapping. if o.userAgent != "" { o.transport = transport.NewUserAgent(o.transport, o.userAgent) } } return o, nil } // WithTransport is a functional option for overriding the default transport // for remote operations. // If transport.Wrapper is provided, this signals that the consumer does *not* want any further wrapping to occur. // i.e. logging, retry and useragent // // The default transport is DefaultTransport. func WithTransport(t http.RoundTripper) Option { return func(o *options) error { o.transport = t return nil } } // WithAuth is a functional option for overriding the default authenticator // for remote operations. // It is an error to use both WithAuth and WithAuthFromKeychain in the same Option set. // // The default authenticator is authn.Anonymous. func WithAuth(auth authn.Authenticator) Option { return func(o *options) error { o.auth = auth return nil } } // WithAuthFromKeychain is a functional option for overriding the default // authenticator for remote operations, using an authn.Keychain to find // credentials. // It is an error to use both WithAuth and WithAuthFromKeychain in the same Option set. // // The default authenticator is authn.Anonymous. func WithAuthFromKeychain(keys authn.Keychain) Option { return func(o *options) error { o.keychain = keys return nil } } // WithPlatform is a functional option for overriding the default platform // that Image and Descriptor.Image use for resolving an index to an image. // // The default platform is amd64/linux. func WithPlatform(p v1.Platform) Option { return func(o *options) error { o.platform = p return nil } } // WithContext is a functional option for setting the context in http requests // performed by a given function. Note that this context is used for _all_ // http requests, not just the initial volley. E.g., for remote.Image, the // context will be set on http requests generated by subsequent calls to // RawConfigFile() and even methods on layers returned by Layers(). // // The default context is context.Background(). func WithContext(ctx context.Context) Option { return func(o *options) error { o.context = ctx return nil } } // WithJobs is a functional option for setting the parallelism of remote // operations performed by a given function. Note that not all remote // operations support parallelism. // // The default value is 4. func WithJobs(jobs int) Option { return func(o *options) error { if jobs <= 0 { return errors.New("jobs must be greater than zero") } o.jobs = jobs return nil } } // WithUserAgent adds the given string to the User-Agent header for any HTTP // requests. This header will also include "go-containerregistry/${version}". // // If you want to completely overwrite the User-Agent header, use WithTransport. func WithUserAgent(ua string) Option { return func(o *options) error { o.userAgent = ua return nil } } // WithNondistributable includes non-distributable (foreign) layers // when writing images, see: // https://github.com/opencontainers/image-spec/blob/master/layer.md#non-distributable-layers // // The default behaviour is to skip these layers func WithNondistributable(o *options) error { o.allowNondistributableArtifacts = true return nil } // WithProgress takes a channel that will receive progress updates as bytes are written. // // Sending updates to an unbuffered channel will block writes, so callers // should provide a buffered channel to avoid potential deadlocks. func WithProgress(updates chan<- v1.Update) Option { return func(o *options) error { o.progress = &progress{updates: updates} o.progress.lastUpdate = &v1.Update{} return nil } } // WithPageSize sets the given size as the value of parameter 'n' in the request. // // To omit the `n` parameter entirely, use WithPageSize(0). // The default value is 1000. func WithPageSize(size int) Option { return func(o *options) error { o.pageSize = size return nil } } // WithRetryBackoff sets the httpBackoff for retry HTTP operations. func WithRetryBackoff(backoff Backoff) Option { return func(o *options) error { o.retryBackoff = backoff return nil } } // WithRetryPredicate sets the predicate for retry HTTP operations. func WithRetryPredicate(predicate retry.Predicate) Option { return func(o *options) error { o.retryPredicate = predicate return nil } } // WithRetryStatusCodes sets which http response codes will be retried. func WithRetryStatusCodes(codes ...int) Option { return func(o *options) error { o.retryStatusCodes = codes return nil } } // WithFilter sets the filter querystring for HTTP operations. func WithFilter(key string, value string) Option { return func(o *options) error { if o.filter == nil { o.filter = map[string]string{} } o.filter[key] = value return nil } } // Reuse takes a Puller or Pusher and reuses it for remote interactions // rather than starting from a clean slate. For example, it will reuse token exchanges // when possible and avoid sending redundant HEAD requests. // // Reuse will take precedence over other options passed to most remote functions because // most options deal with setting up auth and transports, which Reuse intetionally skips. func Reuse[I *Puller | *Pusher](i I) Option { return func(o *options) error { if puller, ok := any(i).(*Puller); ok { o.puller = puller } else if pusher, ok := any(i).(*Pusher); ok { o.pusher = pusher } return nil } } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/progress.go ================================================ // Copyright 2022 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package remote import ( "io" "sync" "sync/atomic" v1 "github.com/google/go-containerregistry/pkg/v1" ) type progress struct { sync.Mutex updates chan<- v1.Update lastUpdate *v1.Update } func (p *progress) total(delta int64) { p.Lock() defer p.Unlock() atomic.AddInt64(&p.lastUpdate.Total, delta) } func (p *progress) complete(delta int64) { p.Lock() defer p.Unlock() p.updates <- v1.Update{ Total: p.lastUpdate.Total, Complete: atomic.AddInt64(&p.lastUpdate.Complete, delta), } } func (p *progress) err(err error) error { if err != nil && p.updates != nil { p.updates <- v1.Update{Error: err} } return err } func (p *progress) Close(err error) { _ = p.err(err) close(p.updates) } type progressReader struct { rc io.ReadCloser count *int64 // number of bytes this reader has read, to support resetting on retry. progress *progress } func (r *progressReader) Read(b []byte) (int, error) { n, err := r.rc.Read(b) if err != nil { return n, err } atomic.AddInt64(r.count, int64(n)) // TODO: warn/debug log if sending takes too long, or if sending is blocked while context is canceled. r.progress.complete(int64(n)) return n, nil } func (r *progressReader) Close() error { return r.rc.Close() } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/puller.go ================================================ // Copyright 2023 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package remote import ( "context" "sync" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/partial" "github.com/google/go-containerregistry/pkg/v1/types" ) type Puller struct { o *options // map[resource]*reader readers sync.Map } func NewPuller(options ...Option) (*Puller, error) { o, err := makeOptions(options...) if err != nil { return nil, err } return newPuller(o), nil } func newPuller(o *options) *Puller { if o.puller != nil { return o.puller } return &Puller{ o: o, } } type reader struct { // in target resource o *options // f() once sync.Once // out f *fetcher err error } // this will run once per reader instance func (r *reader) init(ctx context.Context) error { r.once.Do(func() { r.f, r.err = makeFetcher(ctx, r.target, r.o) }) return r.err } func (p *Puller) fetcher(ctx context.Context, target resource) (*fetcher, error) { v, _ := p.readers.LoadOrStore(target, &reader{ target: target, o: p.o, }) rr := v.(*reader) return rr.f, rr.init(ctx) } // Head is like remote.Head, but avoids re-authenticating when possible. func (p *Puller) Head(ctx context.Context, ref name.Reference) (*v1.Descriptor, error) { f, err := p.fetcher(ctx, ref.Context()) if err != nil { return nil, err } return f.headManifest(ctx, ref, allManifestMediaTypes) } // Get is like remote.Get, but avoids re-authenticating when possible. func (p *Puller) Get(ctx context.Context, ref name.Reference) (*Descriptor, error) { return p.get(ctx, ref, allManifestMediaTypes, p.o.platform) } func (p *Puller) get(ctx context.Context, ref name.Reference, acceptable []types.MediaType, platform v1.Platform) (*Descriptor, error) { f, err := p.fetcher(ctx, ref.Context()) if err != nil { return nil, err } return f.get(ctx, ref, acceptable, platform) } // Layer is like remote.Layer, but avoids re-authenticating when possible. func (p *Puller) Layer(ctx context.Context, ref name.Digest) (v1.Layer, error) { f, err := p.fetcher(ctx, ref.Context()) if err != nil { return nil, err } h, err := v1.NewHash(ref.Identifier()) if err != nil { return nil, err } l, err := partial.CompressedToLayer(&remoteLayer{ fetcher: *f, ctx: ctx, digest: h, }) if err != nil { return nil, err } return &MountableLayer{ Layer: l, Reference: ref, }, nil } // List lists tags in a repo and handles pagination, returning the full list of tags. func (p *Puller) List(ctx context.Context, repo name.Repository) ([]string, error) { lister, err := p.Lister(ctx, repo) if err != nil { return nil, err } tagList := []string{} for lister.HasNext() { tags, err := lister.Next(ctx) if err != nil { return nil, err } tagList = append(tagList, tags.Tags...) } return tagList, nil } // Lister lists tags in a repo and returns a Lister for paginating through the results. func (p *Puller) Lister(ctx context.Context, repo name.Repository) (*Lister, error) { return p.lister(ctx, repo, p.o.pageSize) } func (p *Puller) lister(ctx context.Context, repo name.Repository, pageSize int) (*Lister, error) { f, err := p.fetcher(ctx, repo) if err != nil { return nil, err } page, err := f.listPage(ctx, repo, "", pageSize) if err != nil { return nil, err } return &Lister{ f: f, repo: repo, pageSize: pageSize, page: page, err: err, }, nil } // Catalog lists repos in a registry and handles pagination, returning the full list of repos. func (p *Puller) Catalog(ctx context.Context, reg name.Registry) ([]string, error) { return p.catalog(ctx, reg, p.o.pageSize) } func (p *Puller) catalog(ctx context.Context, reg name.Registry, pageSize int) ([]string, error) { catalogger, err := p.catalogger(ctx, reg, pageSize) if err != nil { return nil, err } repoList := []string{} for catalogger.HasNext() { repos, err := catalogger.Next(ctx) if err != nil { return nil, err } repoList = append(repoList, repos.Repos...) } return repoList, nil } // Catalogger lists repos in a registry and returns a Catalogger for paginating through the results. func (p *Puller) Catalogger(ctx context.Context, reg name.Registry) (*Catalogger, error) { return p.catalogger(ctx, reg, p.o.pageSize) } func (p *Puller) catalogger(ctx context.Context, reg name.Registry, pageSize int) (*Catalogger, error) { f, err := p.fetcher(ctx, reg) if err != nil { return nil, err } page, err := f.catalogPage(ctx, reg, "", pageSize) if err != nil { return nil, err } return &Catalogger{ f: f, reg: reg, pageSize: pageSize, page: page, err: err, }, nil } func (p *Puller) referrers(ctx context.Context, d name.Digest, filter map[string]string) (v1.ImageIndex, error) { f, err := p.fetcher(ctx, d.Context()) if err != nil { return nil, err } return f.fetchReferrers(ctx, filter, d) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/pusher.go ================================================ // Copyright 2023 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package remote import ( "bytes" "context" "errors" "fmt" "net/http" "net/url" "sync" "github.com/google/go-containerregistry/pkg/logs" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/partial" "github.com/google/go-containerregistry/pkg/v1/remote/transport" "github.com/google/go-containerregistry/pkg/v1/stream" "github.com/google/go-containerregistry/pkg/v1/types" "golang.org/x/sync/errgroup" ) type manifest interface { Taggable partial.Describable } // key is either v1.Hash or v1.Layer (for stream.Layer) type workers struct { // map[v1.Hash|v1.Layer]*sync.Once onces sync.Map // map[v1.Hash|v1.Layer]error errors sync.Map } func nop() error { return nil } func (w *workers) err(digest v1.Hash) error { v, ok := w.errors.Load(digest) if !ok || v == nil { return nil } return v.(error) } func (w *workers) Do(digest v1.Hash, f func() error) error { // We don't care if it was loaded or not because the sync.Once will do it for us. once, _ := w.onces.LoadOrStore(digest, &sync.Once{}) once.(*sync.Once).Do(func() { w.errors.Store(digest, f()) }) err := w.err(digest) if err != nil { // Allow this to be retried by another caller. w.onces.Delete(digest) } return err } func (w *workers) Stream(layer v1.Layer, f func() error) error { // We don't care if it was loaded or not because the sync.Once will do it for us. once, _ := w.onces.LoadOrStore(layer, &sync.Once{}) once.(*sync.Once).Do(func() { w.errors.Store(layer, f()) }) v, ok := w.errors.Load(layer) if !ok || v == nil { return nil } return v.(error) } type Pusher struct { o *options // map[name.Repository]*repoWriter writers sync.Map } func NewPusher(options ...Option) (*Pusher, error) { o, err := makeOptions(options...) if err != nil { return nil, err } return newPusher(o), nil } func newPusher(o *options) *Pusher { if o.pusher != nil { return o.pusher } return &Pusher{ o: o, } } func (p *Pusher) writer(ctx context.Context, repo name.Repository, o *options) (*repoWriter, error) { v, _ := p.writers.LoadOrStore(repo, &repoWriter{ repo: repo, o: o, }) rw := v.(*repoWriter) return rw, rw.init(ctx) } func (p *Pusher) Push(ctx context.Context, ref name.Reference, t Taggable) error { w, err := p.writer(ctx, ref.Context(), p.o) if err != nil { return err } return w.writeManifest(ctx, ref, t) } func (p *Pusher) Upload(ctx context.Context, repo name.Repository, l v1.Layer) error { w, err := p.writer(ctx, repo, p.o) if err != nil { return err } return w.writeLayer(ctx, l) } func (p *Pusher) Delete(ctx context.Context, ref name.Reference) error { w, err := p.writer(ctx, ref.Context(), p.o) if err != nil { return err } u := url.URL{ Scheme: ref.Context().Registry.Scheme(), Host: ref.Context().RegistryStr(), Path: fmt.Sprintf("/v2/%s/manifests/%s", ref.Context().RepositoryStr(), ref.Identifier()), } req, err := http.NewRequest(http.MethodDelete, u.String(), nil) if err != nil { return err } resp, err := w.w.client.Do(req.WithContext(ctx)) if err != nil { return err } defer resp.Body.Close() return transport.CheckError(resp, http.StatusOK, http.StatusAccepted) // TODO(jason): If the manifest had a `subject`, and if the registry // doesn't support Referrers, update the index pointed to by the // subject's fallback tag to remove the descriptor for this manifest. } type repoWriter struct { repo name.Repository o *options once sync.Once w *writer err error work *workers } // this will run once per repoWriter instance func (rw *repoWriter) init(ctx context.Context) error { rw.once.Do(func() { rw.work = &workers{} rw.w, rw.err = makeWriter(ctx, rw.repo, nil, rw.o) }) return rw.err } func (rw *repoWriter) writeDeps(ctx context.Context, m manifest) error { if img, ok := m.(v1.Image); ok { return rw.writeLayers(ctx, img) } if idx, ok := m.(v1.ImageIndex); ok { return rw.writeChildren(ctx, idx) } // This has no deps, not an error (e.g. something you want to just PUT). return nil } type describable struct { desc v1.Descriptor } func (d describable) Digest() (v1.Hash, error) { return d.desc.Digest, nil } func (d describable) Size() (int64, error) { return d.desc.Size, nil } func (d describable) MediaType() (types.MediaType, error) { return d.desc.MediaType, nil } type tagManifest struct { Taggable partial.Describable } func taggableToManifest(t Taggable) (manifest, error) { if m, ok := t.(manifest); ok { return m, nil } if d, ok := t.(*Descriptor); ok { if d.MediaType.IsIndex() { return d.ImageIndex() } if d.MediaType.IsImage() { return d.Image() } if d.MediaType.IsSchema1() { return d.Schema1() } return tagManifest{t, describable{d.toDesc()}}, nil } desc := v1.Descriptor{ // A reasonable default if Taggable doesn't implement MediaType. MediaType: types.DockerManifestSchema2, } b, err := t.RawManifest() if err != nil { return nil, err } if wmt, ok := t.(withMediaType); ok { desc.MediaType, err = wmt.MediaType() if err != nil { return nil, err } } desc.Digest, desc.Size, err = v1.SHA256(bytes.NewReader(b)) if err != nil { return nil, err } return tagManifest{t, describable{desc}}, nil } func (rw *repoWriter) writeManifest(ctx context.Context, ref name.Reference, t Taggable) error { m, err := taggableToManifest(t) if err != nil { return err } needDeps := true digest, err := m.Digest() if errors.Is(err, stream.ErrNotComputed) { if err := rw.writeDeps(ctx, m); err != nil { return err } needDeps = false digest, err = m.Digest() if err != nil { return err } } else if err != nil { return err } // This may be a lazy child where we have no ref until digest is computed. if ref == nil { ref = rw.repo.Digest(digest.String()) } // For tags, we want to do this check outside of our Work.Do closure because // we don't want to dedupe based on the manifest digest. _, byTag := ref.(name.Tag) if byTag { if exists, err := rw.manifestExists(ctx, ref, t); err != nil { return err } else if exists { return nil } } // The following work.Do will get deduped by digest, so it won't happen unless // this tag happens to be the first commitManifest to run for that digest. needPut := byTag if err := rw.work.Do(digest, func() error { if !byTag { if exists, err := rw.manifestExists(ctx, ref, t); err != nil { return err } else if exists { return nil } } if needDeps { if err := rw.writeDeps(ctx, m); err != nil { return err } } needPut = false return rw.commitManifest(ctx, ref, m) }); err != nil { return err } if !needPut { return nil } // Only runs for tags that got deduped by digest. return rw.commitManifest(ctx, ref, m) } func (rw *repoWriter) writeChildren(ctx context.Context, idx v1.ImageIndex) error { children, err := partial.Manifests(idx) if err != nil { return err } g, ctx := errgroup.WithContext(ctx) g.SetLimit(rw.o.jobs) for _, child := range children { child := child if err := rw.writeChild(ctx, child, g); err != nil { return err } } return g.Wait() } func (rw *repoWriter) writeChild(ctx context.Context, child partial.Describable, g *errgroup.Group) error { switch child := child.(type) { case v1.ImageIndex: // For recursive index, we want to do a depth-first launching of goroutines // to avoid deadlocking. // // Note that this is rare, so the impact of this should be really small. return rw.writeManifest(ctx, nil, child) case v1.Image: g.Go(func() error { return rw.writeManifest(ctx, nil, child) }) case v1.Layer: g.Go(func() error { return rw.writeLayer(ctx, child) }) default: // This can't happen. return fmt.Errorf("encountered unknown child: %T", child) } return nil } // TODO: Consider caching some representation of the tags/digests in the destination // repository as a hint to avoid this optimistic check in cases where we will most // likely have to do a PUT anyway, e.g. if we are overwriting a tag we just wrote. func (rw *repoWriter) manifestExists(ctx context.Context, ref name.Reference, t Taggable) (bool, error) { f := &fetcher{ target: ref.Context(), client: rw.w.client, } m, err := taggableToManifest(t) if err != nil { return false, err } digest, err := m.Digest() if err != nil { // Possibly due to streaming layers. return false, nil } got, err := f.headManifest(ctx, ref, allManifestMediaTypes) if err != nil { var terr *transport.Error if errors.As(err, &terr) { if terr.StatusCode == http.StatusNotFound { return false, nil } // We treat a 403 here as non-fatal because this existence check is an optimization and // some registries will return a 403 instead of a 404 in certain situations. // E.g. https://jfrog.atlassian.net/browse/RTFACT-13797 if terr.StatusCode == http.StatusForbidden { logs.Debug.Printf("manifestExists unexpected 403: %v", err) return false, nil } } return false, err } if digest != got.Digest { // Mark that we saw this digest in the registry so we don't have to check it again. rw.work.Do(got.Digest, nop) return false, nil } if tag, ok := ref.(name.Tag); ok { logs.Progress.Printf("existing manifest: %s@%s", tag.Identifier(), got.Digest) } else { logs.Progress.Print("existing manifest: ", got.Digest) } return true, nil } func (rw *repoWriter) commitManifest(ctx context.Context, ref name.Reference, m manifest) error { if rw.o.progress != nil { size, err := m.Size() if err != nil { return err } rw.o.progress.total(size) } return rw.w.commitManifest(ctx, m, ref) } func (rw *repoWriter) writeLayers(pctx context.Context, img v1.Image) error { ls, err := img.Layers() if err != nil { return err } g, ctx := errgroup.WithContext(pctx) g.SetLimit(rw.o.jobs) for _, l := range ls { l := l g.Go(func() error { return rw.writeLayer(ctx, l) }) } mt, err := img.MediaType() if err != nil { return err } if mt.IsSchema1() { return g.Wait() } cl, err := partial.ConfigLayer(img) if errors.Is(err, stream.ErrNotComputed) { if err := g.Wait(); err != nil { return err } cl, err := partial.ConfigLayer(img) if err != nil { return err } return rw.writeLayer(pctx, cl) } else if err != nil { return err } g.Go(func() error { return rw.writeLayer(ctx, cl) }) return g.Wait() } func (rw *repoWriter) writeLayer(ctx context.Context, l v1.Layer) error { // Skip any non-distributable things. mt, err := l.MediaType() if err != nil { return err } if !mt.IsDistributable() && !rw.o.allowNondistributableArtifacts { return nil } digest, err := l.Digest() if err != nil { if errors.Is(err, stream.ErrNotComputed) { return rw.lazyWriteLayer(ctx, l) } return err } return rw.work.Do(digest, func() error { if rw.o.progress != nil { size, err := l.Size() if err != nil { return err } rw.o.progress.total(size) } return rw.w.uploadOne(ctx, l) }) } func (rw *repoWriter) lazyWriteLayer(ctx context.Context, l v1.Layer) error { return rw.work.Stream(l, func() error { if err := rw.w.uploadOne(ctx, l); err != nil { return err } // Mark this upload completed. digest, err := l.Digest() if err != nil { return err } rw.work.Do(digest, nop) if rw.o.progress != nil { size, err := l.Size() if err != nil { return err } rw.o.progress.total(size) } return nil }) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/referrers.go ================================================ // Copyright 2023 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package remote import ( "bytes" "context" "errors" "io" "net/http" "strings" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/empty" "github.com/google/go-containerregistry/pkg/v1/mutate" "github.com/google/go-containerregistry/pkg/v1/remote/transport" "github.com/google/go-containerregistry/pkg/v1/types" ) // Referrers returns a list of descriptors that refer to the given manifest digest. // // The subject manifest doesn't have to exist in the registry for there to be descriptors that refer to it. func Referrers(d name.Digest, options ...Option) (v1.ImageIndex, error) { o, err := makeOptions(options...) if err != nil { return nil, err } return newPuller(o).referrers(o.context, d, o.filter) } // https://github.com/opencontainers/distribution-spec/blob/main/spec.md#referrers-tag-schema func fallbackTag(d name.Digest) name.Tag { return d.Context().Tag(strings.Replace(d.DigestStr(), ":", "-", 1)) } func (f *fetcher) fetchReferrers(ctx context.Context, filter map[string]string, d name.Digest) (v1.ImageIndex, error) { // Check the Referrers API endpoint first. u := f.url("referrers", d.DigestStr()) req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) if err != nil { return nil, err } req.Header.Set("Accept", string(types.OCIImageIndex)) resp, err := f.client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() if err := transport.CheckError(resp, http.StatusOK, http.StatusNotFound, http.StatusBadRequest); err != nil { return nil, err } var b []byte if resp.StatusCode == http.StatusOK { b, err = io.ReadAll(resp.Body) if err != nil { return nil, err } } else { // The registry doesn't support the Referrers API endpoint, so we'll use the fallback tag scheme. b, _, err = f.fetchManifest(ctx, fallbackTag(d), []types.MediaType{types.OCIImageIndex}) var terr *transport.Error if errors.As(err, &terr) && terr.StatusCode == http.StatusNotFound { // Not found just means there are no attachments yet. Start with an empty manifest. return empty.Index, nil } else if err != nil { return nil, err } } h, sz, err := v1.SHA256(bytes.NewReader(b)) if err != nil { return nil, err } idx := &remoteIndex{ fetcher: *f, ctx: ctx, manifest: b, mediaType: types.OCIImageIndex, descriptor: &v1.Descriptor{ Digest: h, MediaType: types.OCIImageIndex, Size: sz, }, } return filterReferrersResponse(filter, idx), nil } // If filter applied, filter out by artifactType. // See https://github.com/opencontainers/distribution-spec/blob/main/spec.md#listing-referrers func filterReferrersResponse(filter map[string]string, in v1.ImageIndex) v1.ImageIndex { if filter == nil { return in } v, ok := filter["artifactType"] if !ok { return in } return mutate.RemoveManifests(in, func(desc v1.Descriptor) bool { return desc.ArtifactType != v }) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/schema1.go ================================================ // Copyright 2023 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package remote import ( "bytes" "context" "encoding/json" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/partial" "github.com/google/go-containerregistry/pkg/v1/types" ) type schema1 struct { ref name.Reference ctx context.Context fetcher fetcher manifest []byte mediaType types.MediaType descriptor *v1.Descriptor } func (s *schema1) Layers() ([]v1.Layer, error) { m := schema1Manifest{} if err := json.NewDecoder(bytes.NewReader(s.manifest)).Decode(&m); err != nil { return nil, err } layers := []v1.Layer{} for i := len(m.FSLayers) - 1; i >= 0; i-- { fsl := m.FSLayers[i] h, err := v1.NewHash(fsl.BlobSum) if err != nil { return nil, err } l, err := s.LayerByDigest(h) if err != nil { return nil, err } layers = append(layers, l) } return layers, nil } func (s *schema1) MediaType() (types.MediaType, error) { return s.mediaType, nil } func (s *schema1) Size() (int64, error) { return s.descriptor.Size, nil } func (s *schema1) ConfigName() (v1.Hash, error) { return partial.ConfigName(s) } func (s *schema1) ConfigFile() (*v1.ConfigFile, error) { return nil, newErrSchema1(s.mediaType) } func (s *schema1) RawConfigFile() ([]byte, error) { return []byte("{}"), nil } func (s *schema1) Digest() (v1.Hash, error) { return s.descriptor.Digest, nil } func (s *schema1) Manifest() (*v1.Manifest, error) { return nil, newErrSchema1(s.mediaType) } func (s *schema1) RawManifest() ([]byte, error) { return s.manifest, nil } func (s *schema1) LayerByDigest(h v1.Hash) (v1.Layer, error) { l, err := partial.CompressedToLayer(&remoteLayer{ fetcher: s.fetcher, ctx: s.ctx, digest: h, }) if err != nil { return nil, err } return &MountableLayer{ Layer: l, Reference: s.ref.Context().Digest(h.String()), }, nil } func (s *schema1) LayerByDiffID(v1.Hash) (v1.Layer, error) { return nil, newErrSchema1(s.mediaType) } type fslayer struct { BlobSum string `json:"blobSum"` } type schema1Manifest struct { FSLayers []fslayer `json:"fsLayers"` } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/README.md ================================================ # `transport` [![GoDoc](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/transport?status.svg)](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/transport) The [distribution protocol](https://github.com/opencontainers/distribution-spec) is fairly simple, but correctly [implementing authentication](../../../authn/README.md) is **hard**. This package [implements](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/remote/transport#New) an [`http.RoundTripper`](https://godoc.org/net/http#RoundTripper) that transparently performs: * [Token Authentication](https://docs.docker.com/registry/spec/auth/token/) and * [OAuth2 Authentication](https://docs.docker.com/registry/spec/auth/oauth/) for registry clients. ## Raison d'être > Why not just use the [`docker/distribution`](https://godoc.org/github.com/docker/distribution/registry/client/auth) client? Great question! Mostly, because I don't want to depend on [`prometheus/client_golang`](https://github.com/prometheus/client_golang). As a performance optimization, that client uses [a cache](https://github.com/docker/distribution/blob/a8371794149d1d95f1e846744b05c87f2f825e5a/registry/client/repository.go#L173) to keep track of a mapping between blob digests and their [descriptors](https://github.com/docker/distribution/blob/a8371794149d1d95f1e846744b05c87f2f825e5a/blobs.go#L57-L86). Unfortunately, the cache [uses prometheus](https://github.com/docker/distribution/blob/a8371794149d1d95f1e846744b05c87f2f825e5a/registry/storage/cache/cachedblobdescriptorstore.go#L44) to track hits and misses, so if you want to use that client you have to pull in all of prometheus, which is pretty large. ![docker/distribution](../../../../images/docker.dot.svg) > Why does it matter if you depend on prometheus? Who cares? It's generally polite to your downstream to reduce the number of dependencies your package requires: * Downloading your package is faster, which helps our Australian friends and people on airplanes. * There is less code to compile, which speeds up builds and saves the planet from global warming. * You reduce the likelihood of inflicting dependency hell upon your consumers. * [Tim Hockin](https://twitter.com/thockin/status/958606077456654336) prefers it based on his experience working on Kubernetes, and he's a pretty smart guy. > Okay, what about [`containerd/containerd`](https://godoc.org/github.com/containerd/containerd/remotes/docker)? Similar reasons! That ends up pulling in grpc, protobuf, and logrus. ![containerd/containerd](../../../../images/containerd.dot.svg) > Well... what about [`containers/image`](https://godoc.org/github.com/containers/image/docker)? That just uses the the `docker/distribution` client... and more! ![containers/image](../../../../images/containers.dot.svg) > Wow, what about this package? Of course, this package isn't perfect either. `transport` depends on `authn`, which in turn depends on docker's config file parsing and handling package, which you don't strictly need but almost certainly want if you're going to be interacting with a registry. ![google/go-containerregistry](../../../../images/ggcr.dot.svg) *These graphs were generated by [`kisielk/godepgraph`](https://github.com/kisielk/godepgraph).* ## Usage This is heavily used by the [`remote`](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/remote) package, which implements higher level image-centric functionality, but this package is useful if you want to interact directly with the registry to do something that `remote` doesn't support, e.g. [to handle with schema 1 images](https://github.com/google/go-containerregistry/pull/509). This package also includes some [error handling](https://github.com/opencontainers/distribution-spec/blob/60be706c34ee7805bdd1d3d11affec53b0dfb8fb/spec.md#errors) facilities in the form of [`CheckError`](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/remote/transport#CheckError), which will parse the response body into a structured error for unexpected http status codes. Here's a "simple" program that writes the result of [listing tags](https://github.com/opencontainers/distribution-spec/blob/60be706c34ee7805bdd1d3d11affec53b0dfb8fb/spec.md#tags) for [`gcr.io/google-containers/pause`](https://gcr.io/google-containers/pause) to stdout. ```go package main import ( "io" "net/http" "os" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote/transport" ) func main() { repo, err := name.NewRepository("gcr.io/google-containers/pause") if err != nil { panic(err) } // Fetch credentials based on your docker config file, which is $HOME/.docker/config.json or $DOCKER_CONFIG. auth, err := authn.DefaultKeychain.Resolve(repo.Registry) if err != nil { panic(err) } // Construct an http.Client that is authorized to pull from gcr.io/google-containers/pause. scopes := []string{repo.Scope(transport.PullScope)} t, err := transport.New(repo.Registry, auth, http.DefaultTransport, scopes) if err != nil { panic(err) } client := &http.Client{Transport: t} // Make the actual request. resp, err := client.Get("https://gcr.io/v2/google-containers/pause/tags/list") if err != nil { panic(err) } // Assert that we get a 200, otherwise attempt to parse body as a structured error. if err := transport.CheckError(resp, http.StatusOK); err != nil { panic(err) } // Write the response to stdout. if _, err := io.Copy(os.Stdout, resp.Body); err != nil { panic(err) } } ``` ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/basic.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package transport import ( "encoding/base64" "fmt" "net/http" "github.com/google/go-containerregistry/pkg/authn" ) type basicTransport struct { inner http.RoundTripper auth authn.Authenticator target string } var _ http.RoundTripper = (*basicTransport)(nil) // RoundTrip implements http.RoundTripper func (bt *basicTransport) RoundTrip(in *http.Request) (*http.Response, error) { if bt.auth != authn.Anonymous { auth, err := bt.auth.Authorization() if err != nil { return nil, err } // http.Client handles redirects at a layer above the http.RoundTripper // abstraction, so to avoid forwarding Authorization headers to places // we are redirected, only set it when the authorization header matches // the host with which we are interacting. // In case of redirect http.Client can use an empty Host, check URL too. if in.Host == bt.target || in.URL.Host == bt.target { if bearer := auth.RegistryToken; bearer != "" { hdr := fmt.Sprintf("Bearer %s", bearer) in.Header.Set("Authorization", hdr) } else if user, pass := auth.Username, auth.Password; user != "" && pass != "" { delimited := fmt.Sprintf("%s:%s", user, pass) encoded := base64.StdEncoding.EncodeToString([]byte(delimited)) hdr := fmt.Sprintf("Basic %s", encoded) in.Header.Set("Authorization", hdr) } else if token := auth.Auth; token != "" { hdr := fmt.Sprintf("Basic %s", token) in.Header.Set("Authorization", hdr) } } } return bt.inner.RoundTrip(in) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/bearer.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package transport import ( "context" "encoding/json" "errors" "fmt" "io" "net" "net/http" "net/url" "strings" authchallenge "github.com/docker/distribution/registry/client/auth/challenge" "github.com/google/go-containerregistry/internal/redact" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/logs" "github.com/google/go-containerregistry/pkg/name" ) type Token struct { Token string `json:"token"` AccessToken string `json:"access_token,omitempty"` RefreshToken string `json:"refresh_token"` ExpiresIn int `json:"expires_in"` } // Exchange requests a registry Token with the given scopes. func Exchange(ctx context.Context, reg name.Registry, auth authn.Authenticator, t http.RoundTripper, scopes []string, pr *Challenge) (*Token, error) { if strings.ToLower(pr.Scheme) != "bearer" { // TODO: Pretend token for basic? return nil, fmt.Errorf("challenge scheme %q is not bearer", pr.Scheme) } bt, err := fromChallenge(reg, auth, t, pr, scopes...) if err != nil { return nil, err } authcfg, err := auth.Authorization() if err != nil { return nil, err } tok, err := bt.Refresh(ctx, authcfg) if err != nil { return nil, err } return tok, nil } // FromToken returns a transport given a Challenge + Token. func FromToken(reg name.Registry, auth authn.Authenticator, t http.RoundTripper, pr *Challenge, tok *Token) (http.RoundTripper, error) { if strings.ToLower(pr.Scheme) != "bearer" { return &Wrapper{&basicTransport{inner: t, auth: auth, target: reg.RegistryStr()}}, nil } bt, err := fromChallenge(reg, auth, t, pr) if err != nil { return nil, err } if tok.Token != "" { bt.bearer.RegistryToken = tok.Token } return &Wrapper{bt}, nil } func fromChallenge(reg name.Registry, auth authn.Authenticator, t http.RoundTripper, pr *Challenge, scopes ...string) (*bearerTransport, error) { // We require the realm, which tells us where to send our Basic auth to turn it into Bearer auth. realm, ok := pr.Parameters["realm"] if !ok { return nil, fmt.Errorf("malformed www-authenticate, missing realm: %v", pr.Parameters) } service := pr.Parameters["service"] scheme := "https" if pr.Insecure { scheme = "http" } return &bearerTransport{ inner: t, basic: auth, realm: realm, registry: reg, service: service, scopes: scopes, scheme: scheme, }, nil } type bearerTransport struct { // Wrapped by bearerTransport. inner http.RoundTripper // Basic credentials that we exchange for bearer tokens. basic authn.Authenticator // Holds the bearer response from the token service. bearer authn.AuthConfig // Registry to which we send bearer tokens. registry name.Registry // See https://tools.ietf.org/html/rfc6750#section-3 realm string // See https://docs.docker.com/registry/spec/auth/token/ service string scopes []string // Scheme we should use, determined by ping response. scheme string } var _ http.RoundTripper = (*bearerTransport)(nil) var portMap = map[string]string{ "http": "80", "https": "443", } func stringSet(ss []string) map[string]struct{} { set := make(map[string]struct{}) for _, s := range ss { set[s] = struct{}{} } return set } // RoundTrip implements http.RoundTripper func (bt *bearerTransport) RoundTrip(in *http.Request) (*http.Response, error) { sendRequest := func() (*http.Response, error) { // http.Client handles redirects at a layer above the http.RoundTripper // abstraction, so to avoid forwarding Authorization headers to places // we are redirected, only set it when the authorization header matches // the registry with which we are interacting. // In case of redirect http.Client can use an empty Host, check URL too. if matchesHost(bt.registry.RegistryStr(), in, bt.scheme) { hdr := fmt.Sprintf("Bearer %s", bt.bearer.RegistryToken) in.Header.Set("Authorization", hdr) } return bt.inner.RoundTrip(in) } res, err := sendRequest() if err != nil { return nil, err } // If we hit a WWW-Authenticate challenge, it might be due to expired tokens or insufficient scope. if challenges := authchallenge.ResponseChallenges(res); len(challenges) != 0 { // close out old response, since we will not return it. res.Body.Close() newScopes := []string{} for _, wac := range challenges { // TODO(jonjohnsonjr): Should we also update "realm" or "service"? if want, ok := wac.Parameters["scope"]; ok { // Add any scopes that we don't already request. got := stringSet(bt.scopes) if _, ok := got[want]; !ok { newScopes = append(newScopes, want) } } } // Some registries seem to only look at the first scope parameter during a token exchange. // If a request fails because it's missing a scope, we should put those at the beginning, // otherwise the registry might just ignore it :/ newScopes = append(newScopes, bt.scopes...) bt.scopes = newScopes // TODO(jonjohnsonjr): Teach transport.Error about "error" and "error_description" from challenge. // Retry the request to attempt to get a valid token. if err = bt.refresh(in.Context()); err != nil { return nil, err } return sendRequest() } return res, err } // It's unclear which authentication flow to use based purely on the protocol, // so we rely on heuristics and fallbacks to support as many registries as possible. // The basic token exchange is attempted first, falling back to the oauth flow. // If the IdentityToken is set, this indicates that we should start with the oauth flow. func (bt *bearerTransport) refresh(ctx context.Context) error { auth, err := bt.basic.Authorization() if err != nil { return err } if auth.RegistryToken != "" { bt.bearer.RegistryToken = auth.RegistryToken return nil } response, err := bt.Refresh(ctx, auth) if err != nil { return err } // Some registries set access_token instead of token. See #54. if response.AccessToken != "" { response.Token = response.AccessToken } // Find a token to turn into a Bearer authenticator if response.Token != "" { bt.bearer.RegistryToken = response.Token } // If we obtained a refresh token from the oauth flow, use that for refresh() now. if response.RefreshToken != "" { bt.basic = authn.FromConfig(authn.AuthConfig{ IdentityToken: response.RefreshToken, }) } return nil } func (bt *bearerTransport) Refresh(ctx context.Context, auth *authn.AuthConfig) (*Token, error) { var ( content []byte err error ) if auth.IdentityToken != "" { // If the secret being stored is an identity token, // the Username should be set to , which indicates // we are using an oauth flow. content, err = bt.refreshOauth(ctx) var terr *Error if errors.As(err, &terr) && terr.StatusCode == http.StatusNotFound { // Note: Not all token servers implement oauth2. // If the request to the endpoint returns 404 using the HTTP POST method, // refer to Token Documentation for using the HTTP GET method supported by all token servers. content, err = bt.refreshBasic(ctx) } } else { content, err = bt.refreshBasic(ctx) } if err != nil { return nil, err } var response Token if err := json.Unmarshal(content, &response); err != nil { return nil, err } if response.Token == "" && response.AccessToken == "" { return &response, fmt.Errorf("no token in bearer response:\n%s", content) } return &response, nil } func matchesHost(host string, in *http.Request, scheme string) bool { canonicalHeaderHost := canonicalAddress(in.Host, scheme) canonicalURLHost := canonicalAddress(in.URL.Host, scheme) canonicalRegistryHost := canonicalAddress(host, scheme) return canonicalHeaderHost == canonicalRegistryHost || canonicalURLHost == canonicalRegistryHost } func canonicalAddress(host, scheme string) (address string) { // The host may be any one of: // - hostname // - hostname:port // - ipv4 // - ipv4:port // - ipv6 // - [ipv6]:port // As net.SplitHostPort returns an error if the host does not contain a port, we should only attempt // to call it when we know that the address contains a port if strings.Count(host, ":") == 1 || (strings.Count(host, ":") >= 2 && strings.Contains(host, "]:")) { hostname, port, err := net.SplitHostPort(host) if err != nil { return host } if port == "" { port = portMap[scheme] } return net.JoinHostPort(hostname, port) } return net.JoinHostPort(host, portMap[scheme]) } // https://docs.docker.com/registry/spec/auth/oauth/ func (bt *bearerTransport) refreshOauth(ctx context.Context) ([]byte, error) { auth, err := bt.basic.Authorization() if err != nil { return nil, err } u, err := url.Parse(bt.realm) if err != nil { return nil, err } v := url.Values{} v.Set("scope", strings.Join(bt.scopes, " ")) if bt.service != "" { v.Set("service", bt.service) } v.Set("client_id", defaultUserAgent) if auth.IdentityToken != "" { v.Set("grant_type", "refresh_token") v.Set("refresh_token", auth.IdentityToken) } else if auth.Username != "" && auth.Password != "" { // TODO(#629): This is unreachable. v.Set("grant_type", "password") v.Set("username", auth.Username) v.Set("password", auth.Password) v.Set("access_type", "offline") } client := http.Client{Transport: bt.inner} req, err := http.NewRequest(http.MethodPost, u.String(), strings.NewReader(v.Encode())) if err != nil { return nil, err } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") // We don't want to log credentials. ctx = redact.NewContext(ctx, "oauth token response contains credentials") resp, err := client.Do(req.WithContext(ctx)) if err != nil { return nil, err } defer resp.Body.Close() if err := CheckError(resp, http.StatusOK); err != nil { if bt.basic == authn.Anonymous { logs.Warn.Printf("No matching credentials were found for %q", bt.registry) } return nil, err } return io.ReadAll(resp.Body) } // https://docs.docker.com/registry/spec/auth/token/ func (bt *bearerTransport) refreshBasic(ctx context.Context) ([]byte, error) { u, err := url.Parse(bt.realm) if err != nil { return nil, err } b := &basicTransport{ inner: bt.inner, auth: bt.basic, target: u.Host, } client := http.Client{Transport: b} v := u.Query() v["scope"] = bt.scopes v.Set("service", bt.service) u.RawQuery = v.Encode() req, err := http.NewRequest(http.MethodGet, u.String(), nil) if err != nil { return nil, err } // We don't want to log credentials. ctx = redact.NewContext(ctx, "basic token response contains credentials") resp, err := client.Do(req.WithContext(ctx)) if err != nil { return nil, err } defer resp.Body.Close() if err := CheckError(resp, http.StatusOK); err != nil { if bt.basic == authn.Anonymous { logs.Warn.Printf("No matching credentials were found for %q", bt.registry) } return nil, err } return io.ReadAll(resp.Body) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/doc.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package transport provides facilities for setting up an authenticated // http.RoundTripper given an Authenticator and base RoundTripper. See // transport.New for more information. package transport ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/error.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package transport import ( "encoding/json" "fmt" "io" "net/http" "strings" "github.com/google/go-containerregistry/internal/redact" ) // Error implements error to support the following error specification: // https://github.com/docker/distribution/blob/master/docs/spec/api.md#errors type Error struct { Errors []Diagnostic `json:"errors,omitempty"` // The http status code returned. StatusCode int // The request that failed. Request *http.Request // The raw body if we couldn't understand it. rawBody string // Bit of a hack to make it easier to force a retry. temporary bool } // Check that Error implements error var _ error = (*Error)(nil) // Error implements error func (e *Error) Error() string { prefix := "" if e.Request != nil { prefix = fmt.Sprintf("%s %s: ", e.Request.Method, redact.URL(e.Request.URL)) } return prefix + e.responseErr() } func (e *Error) responseErr() string { switch len(e.Errors) { case 0: if len(e.rawBody) == 0 { if e.Request != nil && e.Request.Method == http.MethodHead { return fmt.Sprintf("unexpected status code %d %s (HEAD responses have no body, use GET for details)", e.StatusCode, http.StatusText(e.StatusCode)) } return fmt.Sprintf("unexpected status code %d %s", e.StatusCode, http.StatusText(e.StatusCode)) } return fmt.Sprintf("unexpected status code %d %s: %s", e.StatusCode, http.StatusText(e.StatusCode), e.rawBody) case 1: return e.Errors[0].String() default: var errors []string for _, d := range e.Errors { errors = append(errors, d.String()) } return fmt.Sprintf("multiple errors returned: %s", strings.Join(errors, "; ")) } } // Temporary returns whether the request that preceded the error is temporary. func (e *Error) Temporary() bool { if e.temporary { return true } if len(e.Errors) == 0 { _, ok := temporaryStatusCodes[e.StatusCode] return ok } for _, d := range e.Errors { if _, ok := temporaryErrorCodes[d.Code]; !ok { return false } } return true } // Diagnostic represents a single error returned by a Docker registry interaction. type Diagnostic struct { Code ErrorCode `json:"code"` Message string `json:"message,omitempty"` Detail any `json:"detail,omitempty"` } // String stringifies the Diagnostic in the form: $Code: $Message[; $Detail] func (d Diagnostic) String() string { msg := fmt.Sprintf("%s: %s", d.Code, d.Message) if d.Detail != nil { msg = fmt.Sprintf("%s; %v", msg, d.Detail) } return msg } // ErrorCode is an enumeration of supported error codes. type ErrorCode string // The set of error conditions a registry may return: // https://github.com/docker/distribution/blob/master/docs/spec/api.md#errors-2 const ( BlobUnknownErrorCode ErrorCode = "BLOB_UNKNOWN" BlobUploadInvalidErrorCode ErrorCode = "BLOB_UPLOAD_INVALID" BlobUploadUnknownErrorCode ErrorCode = "BLOB_UPLOAD_UNKNOWN" DigestInvalidErrorCode ErrorCode = "DIGEST_INVALID" ManifestBlobUnknownErrorCode ErrorCode = "MANIFEST_BLOB_UNKNOWN" ManifestInvalidErrorCode ErrorCode = "MANIFEST_INVALID" ManifestUnknownErrorCode ErrorCode = "MANIFEST_UNKNOWN" ManifestUnverifiedErrorCode ErrorCode = "MANIFEST_UNVERIFIED" NameInvalidErrorCode ErrorCode = "NAME_INVALID" NameUnknownErrorCode ErrorCode = "NAME_UNKNOWN" SizeInvalidErrorCode ErrorCode = "SIZE_INVALID" TagInvalidErrorCode ErrorCode = "TAG_INVALID" UnauthorizedErrorCode ErrorCode = "UNAUTHORIZED" DeniedErrorCode ErrorCode = "DENIED" UnsupportedErrorCode ErrorCode = "UNSUPPORTED" TooManyRequestsErrorCode ErrorCode = "TOOMANYREQUESTS" UnknownErrorCode ErrorCode = "UNKNOWN" // This isn't defined by either docker or OCI spec, but is defined by docker/distribution: // https://github.com/distribution/distribution/blob/6a977a5a754baa213041443f841705888107362a/registry/api/errcode/register.go#L60 UnavailableErrorCode ErrorCode = "UNAVAILABLE" ) // TODO: Include other error types. var temporaryErrorCodes = map[ErrorCode]struct{}{ BlobUploadInvalidErrorCode: {}, TooManyRequestsErrorCode: {}, UnknownErrorCode: {}, UnavailableErrorCode: {}, } var temporaryStatusCodes = map[int]struct{}{ http.StatusRequestTimeout: {}, http.StatusInternalServerError: {}, http.StatusBadGateway: {}, http.StatusServiceUnavailable: {}, http.StatusGatewayTimeout: {}, } // CheckError returns a structured error if the response status is not in codes. func CheckError(resp *http.Response, codes ...int) error { for _, code := range codes { if resp.StatusCode == code { // This is one of the supported status codes. return nil } } b, err := io.ReadAll(resp.Body) if err != nil { return err } return makeError(resp, b) } func makeError(resp *http.Response, body []byte) *Error { // https://github.com/docker/distribution/blob/master/docs/spec/api.md#errors structuredError := &Error{} // This can fail if e.g. the response body is not valid JSON. That's fine, // we'll construct an appropriate error string from the body and status code. _ = json.Unmarshal(body, structuredError) structuredError.rawBody = string(body) structuredError.StatusCode = resp.StatusCode structuredError.Request = resp.Request return structuredError } func retryError(resp *http.Response) error { b, err := io.ReadAll(resp.Body) if err != nil { return err } rerr := makeError(resp, b) rerr.temporary = true return rerr } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/logger.go ================================================ // Copyright 2020 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package transport import ( "fmt" "net/http" "net/http/httputil" "time" "github.com/google/go-containerregistry/internal/redact" "github.com/google/go-containerregistry/pkg/logs" ) type logTransport struct { inner http.RoundTripper } // NewLogger returns a transport that logs requests and responses to // github.com/google/go-containerregistry/pkg/logs.Debug. func NewLogger(inner http.RoundTripper) http.RoundTripper { return &logTransport{inner} } func (t *logTransport) RoundTrip(in *http.Request) (out *http.Response, err error) { // Inspired by: github.com/motemen/go-loghttp // We redact token responses and binary blobs in response/request. omitBody, reason := redact.FromContext(in.Context()) if omitBody { logs.Debug.Printf("--> %s %s [body redacted: %s]", in.Method, in.URL, reason) } else { logs.Debug.Printf("--> %s %s", in.Method, in.URL) } // Save these headers so we can redact Authorization. savedHeaders := in.Header.Clone() if in.Header != nil && in.Header.Get("authorization") != "" { in.Header.Set("authorization", "") } b, err := httputil.DumpRequestOut(in, !omitBody) if err == nil { logs.Debug.Println(string(b)) } else { logs.Debug.Printf("Failed to dump request %s %s: %v", in.Method, in.URL, err) } // Restore the non-redacted headers. in.Header = savedHeaders start := time.Now() out, err = t.inner.RoundTrip(in) duration := time.Since(start) if err != nil { logs.Debug.Printf("<-- %v %s %s (%s)", err, in.Method, in.URL, duration) } if out != nil { msg := fmt.Sprintf("<-- %d", out.StatusCode) if out.Request != nil { msg = fmt.Sprintf("%s %s", msg, out.Request.URL) } msg = fmt.Sprintf("%s (%s)", msg, duration) if omitBody { msg = fmt.Sprintf("%s [body redacted: %s]", msg, reason) } logs.Debug.Print(msg) b, err := httputil.DumpResponse(out, !omitBody) if err == nil { logs.Debug.Println(string(b)) } else { logs.Debug.Printf("Failed to dump response %s %s: %v", in.Method, in.URL, err) } } return } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/ping.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package transport import ( "context" "errors" "fmt" "io" "net/http" "strings" "time" authchallenge "github.com/docker/distribution/registry/client/auth/challenge" "github.com/google/go-containerregistry/pkg/logs" "github.com/google/go-containerregistry/pkg/name" ) // 300ms is the default fallback period for go's DNS dialer but we could make this configurable. var fallbackDelay = 300 * time.Millisecond type Challenge struct { Scheme string // Following the challenge there are often key/value pairs // e.g. Bearer service="gcr.io",realm="https://auth.gcr.io/v36/tokenz" Parameters map[string]string // Whether we had to use http to complete the Ping. Insecure bool } // Ping does a GET /v2/ against the registry and returns the response. func Ping(ctx context.Context, reg name.Registry, t http.RoundTripper) (*Challenge, error) { // This first attempts to use "https" for every request, falling back to http // if the registry matches our localhost heuristic or if it is intentionally // set to insecure via name.NewInsecureRegistry. schemes := []string{"https"} if reg.Scheme() == "http" { schemes = append(schemes, "http") } if len(schemes) == 1 { return pingSingle(ctx, reg, t, schemes[0]) } return pingParallel(ctx, reg, t, schemes) } func pingSingle(ctx context.Context, reg name.Registry, t http.RoundTripper, scheme string) (*Challenge, error) { client := http.Client{Transport: t} url := fmt.Sprintf("%s://%s/v2/", scheme, reg.RegistryStr()) req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { return nil, err } resp, err := client.Do(req.WithContext(ctx)) if err != nil { return nil, err } defer func() { // By draining the body, make sure to reuse the connection made by // the ping for the following access to the registry io.Copy(io.Discard, resp.Body) resp.Body.Close() }() insecure := scheme == "http" switch resp.StatusCode { case http.StatusOK: // If we get a 200, then no authentication is needed. return &Challenge{ Insecure: insecure, }, nil case http.StatusUnauthorized: if challenges := authchallenge.ResponseChallenges(resp); len(challenges) != 0 { // If we hit more than one, let's try to find one that we know how to handle. wac := pickFromMultipleChallenges(challenges) return &Challenge{ Scheme: wac.Scheme, Parameters: wac.Parameters, Insecure: insecure, }, nil } // Otherwise, just return the challenge without parameters. return &Challenge{ Scheme: resp.Header.Get("WWW-Authenticate"), Insecure: insecure, }, nil default: return nil, CheckError(resp, http.StatusOK, http.StatusUnauthorized) } } // Based on the golang happy eyeballs dialParallel impl in net/dial.go. func pingParallel(ctx context.Context, reg name.Registry, t http.RoundTripper, schemes []string) (*Challenge, error) { returned := make(chan struct{}) defer close(returned) type pingResult struct { *Challenge error primary bool done bool } results := make(chan pingResult) startRacer := func(ctx context.Context, scheme string) { pr, err := pingSingle(ctx, reg, t, scheme) select { case results <- pingResult{Challenge: pr, error: err, primary: scheme == "https", done: true}: case <-returned: if pr != nil { logs.Debug.Printf("%s lost race", scheme) } } } var primary, fallback pingResult primaryCtx, primaryCancel := context.WithCancel(ctx) defer primaryCancel() go startRacer(primaryCtx, schemes[0]) fallbackTimer := time.NewTimer(fallbackDelay) defer fallbackTimer.Stop() for { select { case <-fallbackTimer.C: fallbackCtx, fallbackCancel := context.WithCancel(ctx) defer fallbackCancel() go startRacer(fallbackCtx, schemes[1]) case res := <-results: if res.error == nil { return res.Challenge, nil } if res.primary { primary = res } else { fallback = res } if primary.done && fallback.done { return nil, multierrs{primary.error, fallback.error} } if res.primary && fallbackTimer.Stop() { // Primary failed and we haven't started the fallback, // reset time to start fallback immediately. fallbackTimer.Reset(0) } } } } func pickFromMultipleChallenges(challenges []authchallenge.Challenge) authchallenge.Challenge { // It might happen there are multiple www-authenticate headers, e.g. `Negotiate` and `Basic`. // Picking simply the first one could result eventually in `unrecognized challenge` error, // that's why we're looping through the challenges in search for one that can be handled. allowedSchemes := []string{"basic", "bearer"} for _, wac := range challenges { currentScheme := strings.ToLower(wac.Scheme) for _, allowed := range allowedSchemes { if allowed == currentScheme { return wac } } } return challenges[0] } type multierrs []error func (m multierrs) Error() string { var b strings.Builder hasWritten := false for _, err := range m { if hasWritten { b.WriteString("; ") } hasWritten = true b.WriteString(err.Error()) } return b.String() } func (m multierrs) As(target any) bool { for _, err := range m { if errors.As(err, target) { return true } } return false } func (m multierrs) Is(target error) bool { for _, err := range m { if errors.Is(err, target) { return true } } return false } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/retry.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package transport import ( "net/http" "time" "github.com/google/go-containerregistry/internal/retry" ) // Sleep for 0.1 then 0.3 seconds. This should cover networking blips. var defaultBackoff = retry.Backoff{ Duration: 100 * time.Millisecond, Factor: 3.0, Jitter: 0.1, Steps: 3, } var _ http.RoundTripper = (*retryTransport)(nil) // retryTransport wraps a RoundTripper and retries temporary network errors. type retryTransport struct { inner http.RoundTripper backoff retry.Backoff predicate retry.Predicate codes []int } // Option is a functional option for retryTransport. type Option func(*options) type options struct { backoff retry.Backoff predicate retry.Predicate codes []int } // Backoff is an alias of retry.Backoff to expose this configuration option to consumers of this lib type Backoff = retry.Backoff // WithRetryBackoff sets the backoff for retry operations. func WithRetryBackoff(backoff Backoff) Option { return func(o *options) { o.backoff = backoff } } // WithRetryPredicate sets the predicate for retry operations. func WithRetryPredicate(predicate func(error) bool) Option { return func(o *options) { o.predicate = predicate } } // WithRetryStatusCodes sets which http response codes will be retried. func WithRetryStatusCodes(codes ...int) Option { return func(o *options) { o.codes = codes } } // NewRetry returns a transport that retries errors. func NewRetry(inner http.RoundTripper, opts ...Option) http.RoundTripper { o := &options{ backoff: defaultBackoff, predicate: retry.IsTemporary, } for _, opt := range opts { opt(o) } return &retryTransport{ inner: inner, backoff: o.backoff, predicate: o.predicate, codes: o.codes, } } func (t *retryTransport) RoundTrip(in *http.Request) (out *http.Response, err error) { roundtrip := func() error { out, err = t.inner.RoundTrip(in) if !retry.Ever(in.Context()) { return nil } if out != nil { for _, code := range t.codes { if out.StatusCode == code { return retryError(out) } } } return err } retry.Retry(roundtrip, t.predicate, t.backoff) return } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/schemer.go ================================================ // Copyright 2019 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package transport import ( "net/http" "github.com/google/go-containerregistry/pkg/name" ) type schemeTransport struct { // Scheme we should use, determined by ping response. scheme string // Registry we're talking to. registry name.Registry // Wrapped by schemeTransport. inner http.RoundTripper } // RoundTrip implements http.RoundTripper func (st *schemeTransport) RoundTrip(in *http.Request) (*http.Response, error) { // When we ping() the registry, we determine whether to use http or https // based on which scheme was successful. That is only valid for the // registry server and not e.g. a separate token server or blob storage, // so we should only override the scheme if the host is the registry. if matchesHost(st.registry.String(), in, st.scheme) { in.URL.Scheme = st.scheme } return st.inner.RoundTrip(in) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/scope.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package transport // Scopes suitable to qualify each Repository const ( PullScope string = "pull" PushScope string = "push,pull" // For now DELETE is PUSH, which is the read/write ACL. DeleteScope string = PushScope CatalogScope string = "catalog" ) ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/transport.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package transport import ( "context" "net/http" "strings" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/name" ) // New returns a new RoundTripper based on the provided RoundTripper that has been // setup to authenticate with the remote registry "reg", in the capacity // laid out by the specified scopes. // // Deprecated: Use NewWithContext. func New(reg name.Registry, auth authn.Authenticator, t http.RoundTripper, scopes []string) (http.RoundTripper, error) { return NewWithContext(context.Background(), reg, auth, t, scopes) } // NewWithContext returns a new RoundTripper based on the provided RoundTripper that has been // set up to authenticate with the remote registry "reg", in the capacity // laid out by the specified scopes. // In case the RoundTripper is already of the type Wrapper it assumes // authentication was already done prior to this call, so it just returns // the provided RoundTripper without further action func NewWithContext(ctx context.Context, reg name.Registry, auth authn.Authenticator, t http.RoundTripper, scopes []string) (http.RoundTripper, error) { // When the transport provided is of the type Wrapper this function assumes that the caller already // executed the necessary login and check. switch t.(type) { case *Wrapper: return t, nil } // The handshake: // 1. Use "t" to ping() the registry for the authentication challenge. // // 2a. If we get back a 200, then simply use "t". // // 2b. If we get back a 401 with a Basic challenge, then use a transport // that just attachs auth each roundtrip. // // 2c. If we get back a 401 with a Bearer challenge, then use a transport // that attaches a bearer token to each request, and refreshes is on 401s. // Perform an initial refresh to seed the bearer token. // First we ping the registry to determine the parameters of the authentication handshake // (if one is even necessary). pr, err := Ping(ctx, reg, t) if err != nil { return nil, err } // Wrap t with a useragent transport unless we already have one. if _, ok := t.(*userAgentTransport); !ok { t = NewUserAgent(t, "") } scheme := "https" if pr.Insecure { scheme = "http" } // Wrap t in a transport that selects the appropriate scheme based on the ping response. t = &schemeTransport{ scheme: scheme, registry: reg, inner: t, } if strings.ToLower(pr.Scheme) != "bearer" { return &Wrapper{&basicTransport{inner: t, auth: auth, target: reg.RegistryStr()}}, nil } bt, err := fromChallenge(reg, auth, t, pr) if err != nil { return nil, err } bt.scopes = scopes if err := bt.refresh(ctx); err != nil { return nil, err } return &Wrapper{bt}, nil } // Wrapper results in *not* wrapping supplied transport with additional logic such as retries, useragent and debug logging // Consumers are opt-ing into providing their own transport without any additional wrapping. type Wrapper struct { inner http.RoundTripper } // RoundTrip delegates to the inner RoundTripper func (w *Wrapper) RoundTrip(in *http.Request) (*http.Response, error) { return w.inner.RoundTrip(in) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/transport/useragent.go ================================================ // Copyright 2019 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package transport import ( "fmt" "net/http" "runtime/debug" ) var ( // Version can be set via: // -ldflags="-X 'github.com/google/go-containerregistry/pkg/v1/remote/transport.Version=$TAG'" Version string ggcrVersion = defaultUserAgent ) const ( defaultUserAgent = "go-containerregistry" moduleName = "github.com/google/go-containerregistry" ) type userAgentTransport struct { inner http.RoundTripper ua string } func init() { if v := version(); v != "" { ggcrVersion = fmt.Sprintf("%s/%s", defaultUserAgent, v) } } func version() string { if Version != "" { // Version was set via ldflags, just return it. return Version } info, ok := debug.ReadBuildInfo() if !ok { return "" } // Happens for crane and gcrane. if info.Main.Path == moduleName { return info.Main.Version } // Anything else. for _, dep := range info.Deps { if dep.Path == moduleName { return dep.Version } } return "" } // NewUserAgent returns an http.Roundtripper that sets the user agent to // The provided string plus additional go-containerregistry information, // e.g. if provided "crane/v0.1.4" and this modules was built at v0.1.4: // // User-Agent: crane/v0.1.4 go-containerregistry/v0.1.4 func NewUserAgent(inner http.RoundTripper, ua string) http.RoundTripper { if ua == "" { ua = ggcrVersion } else { ua = fmt.Sprintf("%s %s", ua, ggcrVersion) } return &userAgentTransport{ inner: inner, ua: ua, } } // RoundTrip implements http.RoundTripper func (ut *userAgentTransport) RoundTrip(in *http.Request) (*http.Response, error) { in.Header.Set("User-Agent", ut.ua) return ut.inner.RoundTrip(in) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/remote/write.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package remote import ( "bytes" "context" "encoding/json" "errors" "fmt" "io" "net/http" "net/url" "sort" "strings" "sync" "github.com/google/go-containerregistry/internal/redact" "github.com/google/go-containerregistry/internal/retry" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/logs" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/remote/transport" "github.com/google/go-containerregistry/pkg/v1/stream" "github.com/google/go-containerregistry/pkg/v1/types" ) // Taggable is an interface that enables a manifest PUT (e.g. for tagging). type Taggable interface { RawManifest() ([]byte, error) } // Write pushes the provided img to the specified image reference. func Write(ref name.Reference, img v1.Image, options ...Option) (rerr error) { o, err := makeOptions(options...) if err != nil { return err } if o.progress != nil { defer func() { o.progress.Close(rerr) }() } return newPusher(o).Push(o.context, ref, img) } // writer writes the elements of an image to a remote image reference. type writer struct { repo name.Repository auth authn.Authenticator transport http.RoundTripper client *http.Client progress *progress backoff Backoff predicate retry.Predicate scopeLock sync.Mutex // Keep track of scopes that we have already requested. scopeSet map[string]struct{} scopes []string } func makeWriter(ctx context.Context, repo name.Repository, ls []v1.Layer, o *options) (*writer, error) { auth := o.auth if o.keychain != nil { kauth, err := o.keychain.Resolve(repo) if err != nil { return nil, err } auth = kauth } scopes := scopesForUploadingImage(repo, ls) tr, err := transport.NewWithContext(ctx, repo.Registry, auth, o.transport, scopes) if err != nil { return nil, err } scopeSet := map[string]struct{}{} for _, scope := range scopes { scopeSet[scope] = struct{}{} } return &writer{ repo: repo, client: &http.Client{Transport: tr}, auth: auth, transport: o.transport, progress: o.progress, backoff: o.retryBackoff, predicate: o.retryPredicate, scopes: scopes, scopeSet: scopeSet, }, nil } // url returns a url.Url for the specified path in the context of this remote image reference. func (w *writer) url(path string) url.URL { return url.URL{ Scheme: w.repo.Registry.Scheme(), Host: w.repo.RegistryStr(), Path: path, } } func (w *writer) maybeUpdateScopes(ctx context.Context, ml *MountableLayer) error { if ml.Reference.Context().String() == w.repo.String() { return nil } if ml.Reference.Context().Registry.String() != w.repo.Registry.String() { return nil } scope := ml.Reference.Scope(transport.PullScope) w.scopeLock.Lock() defer w.scopeLock.Unlock() if _, ok := w.scopeSet[scope]; !ok { w.scopeSet[scope] = struct{}{} w.scopes = append(w.scopes, scope) logs.Debug.Printf("Refreshing token to add scope %q", scope) wt, err := transport.NewWithContext(ctx, w.repo.Registry, w.auth, w.transport, w.scopes) if err != nil { return err } w.client = &http.Client{Transport: wt} } return nil } // nextLocation extracts the fully-qualified URL to which we should send the next request in an upload sequence. func (w *writer) nextLocation(resp *http.Response) (string, error) { loc := resp.Header.Get("Location") if len(loc) == 0 { return "", errors.New("missing Location header") } u, err := url.Parse(loc) if err != nil { return "", err } // If the location header returned is just a url path, then fully qualify it. // We cannot simply call w.url, since there might be an embedded query string. return resp.Request.URL.ResolveReference(u).String(), nil } // checkExistingBlob checks if a blob exists already in the repository by making a // HEAD request to the blob store API. GCR performs an existence check on the // initiation if "mount" is specified, even if no "from" sources are specified. // However, this is not broadly applicable to all registries, e.g. ECR. func (w *writer) checkExistingBlob(ctx context.Context, h v1.Hash) (bool, error) { u := w.url(fmt.Sprintf("/v2/%s/blobs/%s", w.repo.RepositoryStr(), h.String())) req, err := http.NewRequest(http.MethodHead, u.String(), nil) if err != nil { return false, err } resp, err := w.client.Do(req.WithContext(ctx)) if err != nil { return false, err } defer resp.Body.Close() if err := transport.CheckError(resp, http.StatusOK, http.StatusNotFound); err != nil { return false, err } return resp.StatusCode == http.StatusOK, nil } // initiateUpload initiates the blob upload, which starts with a POST that can // optionally include the hash of the layer and a list of repositories from // which that layer might be read. On failure, an error is returned. // On success, the layer was either mounted (nothing more to do) or a blob // upload was initiated and the body of that blob should be sent to the returned // location. func (w *writer) initiateUpload(ctx context.Context, from, mount, origin string) (location string, mounted bool, err error) { u := w.url(fmt.Sprintf("/v2/%s/blobs/uploads/", w.repo.RepositoryStr())) uv := url.Values{} if mount != "" && from != "" { // Quay will fail if we specify a "mount" without a "from". uv.Set("mount", mount) uv.Set("from", from) if origin != "" { uv.Set("origin", origin) } } u.RawQuery = uv.Encode() // Make the request to initiate the blob upload. req, err := http.NewRequest(http.MethodPost, u.String(), nil) if err != nil { return "", false, err } req.Header.Set("Content-Type", "application/json") resp, err := w.client.Do(req.WithContext(ctx)) if err != nil { if from != "" { // https://github.com/google/go-containerregistry/issues/1679 logs.Warn.Printf("retrying without mount: %v", err) return w.initiateUpload(ctx, "", "", "") } return "", false, err } defer resp.Body.Close() if err := transport.CheckError(resp, http.StatusCreated, http.StatusAccepted); err != nil { if from != "" { // https://github.com/google/go-containerregistry/issues/1404 logs.Warn.Printf("retrying without mount: %v", err) return w.initiateUpload(ctx, "", "", "") } return "", false, err } // Check the response code to determine the result. switch resp.StatusCode { case http.StatusCreated: // We're done, we were able to fast-path. return "", true, nil case http.StatusAccepted: // Proceed to PATCH, upload has begun. loc, err := w.nextLocation(resp) return loc, false, err default: panic("Unreachable: initiateUpload") } } // streamBlob streams the contents of the blob to the specified location. // On failure, this will return an error. On success, this will return the location // header indicating how to commit the streamed blob. func (w *writer) streamBlob(ctx context.Context, layer v1.Layer, streamLocation string) (commitLocation string, rerr error) { reset := func() {} defer func() { if rerr != nil { reset() } }() blob, err := layer.Compressed() if err != nil { return "", err } getBody := layer.Compressed if w.progress != nil { var count int64 blob = &progressReader{rc: blob, progress: w.progress, count: &count} getBody = func() (io.ReadCloser, error) { blob, err := layer.Compressed() if err != nil { return nil, err } return &progressReader{rc: blob, progress: w.progress, count: &count}, nil } reset = func() { w.progress.complete(-count) } } req, err := http.NewRequest(http.MethodPatch, streamLocation, blob) if err != nil { return "", err } if _, ok := layer.(*stream.Layer); !ok { // We can't retry streaming layers. req.GetBody = getBody // If we know the size, set it. if size, err := layer.Size(); err == nil { req.ContentLength = size } } req.Header.Set("Content-Type", "application/octet-stream") resp, err := w.client.Do(req.WithContext(ctx)) if err != nil { return "", err } defer resp.Body.Close() if err := transport.CheckError(resp, http.StatusNoContent, http.StatusAccepted, http.StatusCreated); err != nil { return "", err } // The blob has been uploaded, return the location header indicating // how to commit this layer. return w.nextLocation(resp) } // commitBlob commits this blob by sending a PUT to the location returned from // streaming the blob. func (w *writer) commitBlob(ctx context.Context, location, digest string) error { u, err := url.Parse(location) if err != nil { return err } v := u.Query() v.Set("digest", digest) u.RawQuery = v.Encode() req, err := http.NewRequest(http.MethodPut, u.String(), nil) if err != nil { return err } req.Header.Set("Content-Type", "application/octet-stream") resp, err := w.client.Do(req.WithContext(ctx)) if err != nil { return err } defer resp.Body.Close() return transport.CheckError(resp, http.StatusCreated) } // incrProgress increments and sends a progress update, if WithProgress is used. func (w *writer) incrProgress(written int64) { if w.progress == nil { return } w.progress.complete(written) } // uploadOne performs a complete upload of a single layer. func (w *writer) uploadOne(ctx context.Context, l v1.Layer) error { tryUpload := func() error { ctx := retry.Never(ctx) var from, mount, origin string if h, err := l.Digest(); err == nil { // If we know the digest, this isn't a streaming layer. Do an existence // check so we can skip uploading the layer if possible. existing, err := w.checkExistingBlob(ctx, h) if err != nil { return err } if existing { size, err := l.Size() if err != nil { return err } w.incrProgress(size) logs.Progress.Printf("existing blob: %v", h) return nil } mount = h.String() } if ml, ok := l.(*MountableLayer); ok { if err := w.maybeUpdateScopes(ctx, ml); err != nil { return err } from = ml.Reference.Context().RepositoryStr() origin = ml.Reference.Context().RegistryStr() // This keeps breaking with DockerHub. // https://github.com/google/go-containerregistry/issues/1741 if w.repo.RegistryStr() == name.DefaultRegistry && origin != w.repo.RegistryStr() { from = "" origin = "" } } location, mounted, err := w.initiateUpload(ctx, from, mount, origin) if err != nil { return err } else if mounted { size, err := l.Size() if err != nil { return err } w.incrProgress(size) h, err := l.Digest() if err != nil { return err } logs.Progress.Printf("mounted blob: %s", h.String()) return nil } // Only log layers with +json or +yaml. We can let through other stuff if it becomes popular. // TODO(opencontainers/image-spec#791): Would be great to have an actual parser. mt, err := l.MediaType() if err != nil { return err } smt := string(mt) if !(strings.HasSuffix(smt, "+json") || strings.HasSuffix(smt, "+yaml")) { ctx = redact.NewContext(ctx, "omitting binary blobs from logs") } location, err = w.streamBlob(ctx, l, location) if err != nil { return err } h, err := l.Digest() if err != nil { return err } digest := h.String() if err := w.commitBlob(ctx, location, digest); err != nil { return err } logs.Progress.Printf("pushed blob: %s", digest) return nil } return retry.Retry(tryUpload, w.predicate, w.backoff) } type withMediaType interface { MediaType() (types.MediaType, error) } // This is really silly, but go interfaces don't let me satisfy remote.Taggable // with remote.Descriptor because of name collisions between method names and // struct fields. // // Use reflection to either pull the v1.Descriptor out of remote.Descriptor or // create a descriptor based on the RawManifest and (optionally) MediaType. func unpackTaggable(t Taggable) ([]byte, *v1.Descriptor, error) { if d, ok := t.(*Descriptor); ok { return d.Manifest, &d.Descriptor, nil } b, err := t.RawManifest() if err != nil { return nil, nil, err } // A reasonable default if Taggable doesn't implement MediaType. mt := types.DockerManifestSchema2 if wmt, ok := t.(withMediaType); ok { m, err := wmt.MediaType() if err != nil { return nil, nil, err } mt = m } h, sz, err := v1.SHA256(bytes.NewReader(b)) if err != nil { return nil, nil, err } return b, &v1.Descriptor{ MediaType: mt, Size: sz, Digest: h, }, nil } // commitSubjectReferrers is responsible for updating the fallback tag manifest to track descriptors referring to a subject for registries that don't yet support the Referrers API. // TODO: use conditional requests to avoid race conditions func (w *writer) commitSubjectReferrers(ctx context.Context, sub name.Digest, add v1.Descriptor) error { // Check if the registry supports Referrers API. // TODO: This should be done once per registry, not once per subject. u := w.url(fmt.Sprintf("/v2/%s/referrers/%s", w.repo.RepositoryStr(), sub.DigestStr())) req, err := http.NewRequest(http.MethodGet, u.String(), nil) if err != nil { return err } req.Header.Set("Accept", string(types.OCIImageIndex)) resp, err := w.client.Do(req.WithContext(ctx)) if err != nil { return err } defer resp.Body.Close() if err := transport.CheckError(resp, http.StatusOK, http.StatusNotFound, http.StatusBadRequest); err != nil { return err } if resp.StatusCode == http.StatusOK { // The registry supports Referrers API. The registry is responsible for updating the referrers list. return nil } // The registry doesn't support Referrers API, we need to update the manifest tagged with the fallback tag. // Make the request to GET the current manifest. t := fallbackTag(sub) u = w.url(fmt.Sprintf("/v2/%s/manifests/%s", w.repo.RepositoryStr(), t.Identifier())) req, err = http.NewRequest(http.MethodGet, u.String(), nil) if err != nil { return err } req.Header.Set("Accept", string(types.OCIImageIndex)) resp, err = w.client.Do(req.WithContext(ctx)) if err != nil { return err } defer resp.Body.Close() var im v1.IndexManifest if err := transport.CheckError(resp, http.StatusOK, http.StatusNotFound); err != nil { return err } else if resp.StatusCode == http.StatusNotFound { // Not found just means there are no attachments. Start with an empty index. im = v1.IndexManifest{ SchemaVersion: 2, MediaType: types.OCIImageIndex, Manifests: []v1.Descriptor{add}, } } else { if err := json.NewDecoder(resp.Body).Decode(&im); err != nil { return err } if im.SchemaVersion != 2 { return fmt.Errorf("fallback tag manifest is not a schema version 2: %d", im.SchemaVersion) } if im.MediaType != types.OCIImageIndex { return fmt.Errorf("fallback tag manifest is not an OCI image index: %s", im.MediaType) } for _, desc := range im.Manifests { if desc.Digest == add.Digest { // The digest is already attached, nothing to do. logs.Progress.Printf("fallback tag %s already had referrer", t.Identifier()) return nil } } // Append the new descriptor to the index. im.Manifests = append(im.Manifests, add) } // Sort the manifests for reproducibility. sort.Slice(im.Manifests, func(i, j int) bool { return im.Manifests[i].Digest.String() < im.Manifests[j].Digest.String() }) logs.Progress.Printf("updating fallback tag %s with new referrer", t.Identifier()) return w.commitManifest(ctx, fallbackTaggable{im}, t) } type fallbackTaggable struct { im v1.IndexManifest } func (f fallbackTaggable) RawManifest() ([]byte, error) { return json.Marshal(f.im) } func (f fallbackTaggable) MediaType() (types.MediaType, error) { return types.OCIImageIndex, nil } // commitManifest does a PUT of the image's manifest. func (w *writer) commitManifest(ctx context.Context, t Taggable, ref name.Reference) error { // If the manifest refers to a subject, we need to check whether we need to update the fallback tag manifest. raw, err := t.RawManifest() if err != nil { return err } var mf struct { MediaType types.MediaType `json:"mediaType"` Subject *v1.Descriptor `json:"subject,omitempty"` Config struct { MediaType types.MediaType `json:"mediaType"` } `json:"config"` } if err := json.Unmarshal(raw, &mf); err != nil { return err } tryUpload := func() error { ctx := retry.Never(ctx) raw, desc, err := unpackTaggable(t) if err != nil { return err } u := w.url(fmt.Sprintf("/v2/%s/manifests/%s", w.repo.RepositoryStr(), ref.Identifier())) // Make the request to PUT the serialized manifest req, err := http.NewRequest(http.MethodPut, u.String(), bytes.NewBuffer(raw)) if err != nil { return err } req.Header.Set("Content-Type", string(desc.MediaType)) resp, err := w.client.Do(req.WithContext(ctx)) if err != nil { return err } defer resp.Body.Close() if err := transport.CheckError(resp, http.StatusOK, http.StatusCreated, http.StatusAccepted); err != nil { return err } // If the manifest referred to a subject, we may need to update the fallback tag manifest. // TODO: If this fails, we'll retry the whole upload. We should retry just this part. if mf.Subject != nil { h, size, err := v1.SHA256(bytes.NewReader(raw)) if err != nil { return err } desc := v1.Descriptor{ ArtifactType: string(mf.Config.MediaType), MediaType: mf.MediaType, Digest: h, Size: size, } if err := w.commitSubjectReferrers(ctx, ref.Context().Digest(mf.Subject.Digest.String()), desc); err != nil { return err } } // The image was successfully pushed! logs.Progress.Printf("%v: digest: %v size: %d", ref, desc.Digest, desc.Size) w.incrProgress(int64(len(raw))) return nil } return retry.Retry(tryUpload, w.predicate, w.backoff) } func scopesForUploadingImage(repo name.Repository, layers []v1.Layer) []string { // use a map as set to remove duplicates scope strings scopeSet := map[string]struct{}{} for _, l := range layers { if ml, ok := l.(*MountableLayer); ok { // we will add push scope for ref.Context() after the loop. // for now we ask pull scope for references of the same registry if ml.Reference.Context().String() != repo.String() && ml.Reference.Context().Registry.String() == repo.Registry.String() { scopeSet[ml.Reference.Scope(transport.PullScope)] = struct{}{} } } } scopes := make([]string, 0) // Push scope should be the first element because a few registries just look at the first scope to determine access. scopes = append(scopes, repo.Scope(transport.PushScope)) for scope := range scopeSet { scopes = append(scopes, scope) } return scopes } // WriteIndex pushes the provided ImageIndex to the specified image reference. // WriteIndex will attempt to push all of the referenced manifests before // attempting to push the ImageIndex, to retain referential integrity. func WriteIndex(ref name.Reference, ii v1.ImageIndex, options ...Option) (rerr error) { o, err := makeOptions(options...) if err != nil { return err } if o.progress != nil { defer func() { o.progress.Close(rerr) }() } return newPusher(o).Push(o.context, ref, ii) } // WriteLayer uploads the provided Layer to the specified repo. func WriteLayer(repo name.Repository, layer v1.Layer, options ...Option) (rerr error) { o, err := makeOptions(options...) if err != nil { return err } if o.progress != nil { defer func() { o.progress.Close(rerr) }() } return newPusher(o).Upload(o.context, repo, layer) } // Tag adds a tag to the given Taggable via PUT /v2/.../manifests/ // // Notable implementations of Taggable are v1.Image, v1.ImageIndex, and // remote.Descriptor. // // If t implements MediaType, we will use that for the Content-Type, otherwise // we will default to types.DockerManifestSchema2. // // Tag does not attempt to write anything other than the manifest, so callers // should ensure that all blobs or manifests that are referenced by t exist // in the target registry. func Tag(tag name.Tag, t Taggable, options ...Option) error { return Put(tag, t, options...) } // Put adds a manifest from the given Taggable via PUT /v1/.../manifest/ // // Notable implementations of Taggable are v1.Image, v1.ImageIndex, and // remote.Descriptor. // // If t implements MediaType, we will use that for the Content-Type, otherwise // we will default to types.DockerManifestSchema2. // // Put does not attempt to write anything other than the manifest, so callers // should ensure that all blobs or manifests that are referenced by t exist // in the target registry. func Put(ref name.Reference, t Taggable, options ...Option) error { o, err := makeOptions(options...) if err != nil { return err } return newPusher(o).Push(o.context, ref, t) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/stream/README.md ================================================ # `stream` [![GoDoc](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/stream?status.svg)](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/stream) The `stream` package contains an implementation of [`v1.Layer`](https://godoc.org/github.com/google/go-containerregistry/pkg/v1#Layer) that supports _streaming_ access, i.e. the layer contents are read once and not buffered. ## Usage ```go package main import ( "os" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/google/go-containerregistry/pkg/v1/stream" ) // upload the contents of stdin as a layer to a local registry func main() { repo, err := name.NewRepository("localhost:5000/stream") if err != nil { panic(err) } layer := stream.NewLayer(os.Stdin) if err := remote.WriteLayer(repo, layer); err != nil { panic(err) } } ``` ## Structure This implements the layer portion of an [image upload](/pkg/v1/remote#anatomy-of-an-image-upload). We launch a goroutine that is responsible for hashing the uncompressed contents to compute the `DiffID`, gzipping them to produce the `Compressed` contents, and hashing/counting the bytes to produce the `Digest`/`Size`. This goroutine writes to an `io.PipeWriter`, which blocks until `Compressed` reads the gzipped contents from the corresponding `io.PipeReader`.

## Caveats This assumes that you have an uncompressed layer (i.e. a tarball) and would like to compress it. Calling `Uncompressed` is always an error. Likewise, other methods are invalid until the contents of `Compressed` have been completely consumed and `Close`d. Using a `stream.Layer` will likely not work without careful consideration. For example, in the `mutate` package, we defer computing the manifest and config file until they are actually called. This allows you to `mutate.Append` a streaming layer to an image without accidentally consuming it. Similarly, in `remote.Write`, if calling `Digest` on a layer fails, we attempt to upload the layer anyway, understanding that we may be dealing with a `stream.Layer` whose contents need to be uploaded before we can upload the config file. Given the [structure](#structure) of how this is implemented, forgetting to `Close` a `stream.Layer` will leak a goroutine. ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/stream/layer.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package stream implements a single-pass streaming v1.Layer. package stream import ( "bufio" "compress/gzip" "crypto" "encoding/hex" "errors" "hash" "io" "os" "sync" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/types" ) var ( // ErrNotComputed is returned when the requested value is not yet // computed because the stream has not been consumed yet. ErrNotComputed = errors.New("value not computed until stream is consumed") // ErrConsumed is returned by Compressed when the underlying stream has // already been consumed and closed. ErrConsumed = errors.New("stream was already consumed") ) // Layer is a streaming implementation of v1.Layer. type Layer struct { blob io.ReadCloser consumed bool compression int mu sync.Mutex digest, diffID *v1.Hash size int64 mediaType types.MediaType } var _ v1.Layer = (*Layer)(nil) // LayerOption applies options to layer type LayerOption func(*Layer) // WithCompressionLevel sets the gzip compression. See `gzip.NewWriterLevel` for possible values. func WithCompressionLevel(level int) LayerOption { return func(l *Layer) { l.compression = level } } // WithMediaType is a functional option for overriding the layer's media type. func WithMediaType(mt types.MediaType) LayerOption { return func(l *Layer) { l.mediaType = mt } } // NewLayer creates a Layer from an io.ReadCloser. func NewLayer(rc io.ReadCloser, opts ...LayerOption) *Layer { layer := &Layer{ blob: rc, compression: gzip.BestSpeed, // We use DockerLayer for now as uncompressed layers // are unimplemented mediaType: types.DockerLayer, } for _, opt := range opts { opt(layer) } return layer } // Digest implements v1.Layer. func (l *Layer) Digest() (v1.Hash, error) { l.mu.Lock() defer l.mu.Unlock() if l.digest == nil { return v1.Hash{}, ErrNotComputed } return *l.digest, nil } // DiffID implements v1.Layer. func (l *Layer) DiffID() (v1.Hash, error) { l.mu.Lock() defer l.mu.Unlock() if l.diffID == nil { return v1.Hash{}, ErrNotComputed } return *l.diffID, nil } // Size implements v1.Layer. func (l *Layer) Size() (int64, error) { l.mu.Lock() defer l.mu.Unlock() if l.size == 0 { return 0, ErrNotComputed } return l.size, nil } // MediaType implements v1.Layer func (l *Layer) MediaType() (types.MediaType, error) { return l.mediaType, nil } // Uncompressed implements v1.Layer. func (l *Layer) Uncompressed() (io.ReadCloser, error) { return nil, errors.New("NYI: stream.Layer.Uncompressed is not implemented") } // Compressed implements v1.Layer. func (l *Layer) Compressed() (io.ReadCloser, error) { l.mu.Lock() defer l.mu.Unlock() if l.consumed { return nil, ErrConsumed } return newCompressedReader(l) } // finalize sets the layer to consumed and computes all hash and size values. func (l *Layer) finalize(uncompressed, compressed hash.Hash, size int64) error { l.mu.Lock() defer l.mu.Unlock() diffID, err := v1.NewHash("sha256:" + hex.EncodeToString(uncompressed.Sum(nil))) if err != nil { return err } l.diffID = &diffID digest, err := v1.NewHash("sha256:" + hex.EncodeToString(compressed.Sum(nil))) if err != nil { return err } l.digest = &digest l.size = size l.consumed = true return nil } type compressedReader struct { pr io.Reader closer func() error } func newCompressedReader(l *Layer) (*compressedReader, error) { // Collect digests of compressed and uncompressed stream and size of // compressed stream. h := crypto.SHA256.New() zh := crypto.SHA256.New() count := &countWriter{} // gzip.Writer writes to the output stream via pipe, a hasher to // capture compressed digest, and a countWriter to capture compressed // size. pr, pw := io.Pipe() // Write compressed bytes to be read by the pipe.Reader, hashed by zh, and counted by count. mw := io.MultiWriter(pw, zh, count) // Buffer the output of the gzip writer so we don't have to wait on pr to keep writing. // 64K ought to be small enough for anybody. bw := bufio.NewWriterSize(mw, 2<<16) zw, err := gzip.NewWriterLevel(bw, l.compression) if err != nil { return nil, err } doneDigesting := make(chan struct{}) cr := &compressedReader{ pr: pr, closer: func() error { // Immediately close pw without error. There are three ways to get // here. // // 1. There was a copy error due from the underlying reader, in which // case the error will not be overwritten. // 2. Copying from the underlying reader completed successfully. // 3. Close has been called before the underlying reader has been // fully consumed. In this case pw must be closed in order to // keep the flush of bw from blocking indefinitely. // // NOTE: pw.Close never returns an error. The signature is only to // implement io.Closer. _ = pw.Close() // Close the inner ReadCloser. // // NOTE: net/http will call close on success, so if we've already // closed the inner rc, it's not an error. if err := l.blob.Close(); err != nil && !errors.Is(err, os.ErrClosed) { return err } // Finalize layer with its digest and size values. <-doneDigesting return l.finalize(h, zh, count.n) }, } go func() { // Copy blob into the gzip writer, which also hashes and counts the // size of the compressed output, and hasher of the raw contents. _, copyErr := io.Copy(io.MultiWriter(h, zw), l.blob) // Close the gzip writer once copying is done. If this is done in the // Close method of compressedReader instead, then it can cause a panic // when the compressedReader is closed before the blob is fully // consumed and io.Copy in this goroutine is still blocking. closeErr := zw.Close() // Check errors from writing and closing streams. if copyErr != nil { close(doneDigesting) pw.CloseWithError(copyErr) return } if closeErr != nil { close(doneDigesting) pw.CloseWithError(closeErr) return } // Flush the buffer once all writes are complete to the gzip writer. if err := bw.Flush(); err != nil { close(doneDigesting) pw.CloseWithError(err) return } // Notify closer that digests are done being written. close(doneDigesting) // Close the compressed reader to calculate digest/diffID/size. This // will cause pr to return EOF which will cause readers of the // Compressed stream to finish reading. pw.CloseWithError(cr.Close()) }() return cr, nil } func (cr *compressedReader) Read(b []byte) (int, error) { return cr.pr.Read(b) } func (cr *compressedReader) Close() error { return cr.closer() } // countWriter counts bytes written to it. type countWriter struct{ n int64 } func (c *countWriter) Write(p []byte) (int, error) { c.n += int64(len(p)) return len(p), nil } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/tarball/README.md ================================================ # `tarball` [![GoDoc](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/tarball?status.svg)](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/tarball) This package produces tarballs that can consumed via `docker load`. Note that this is a _different_ format from the [`legacy`](/pkg/legacy/tarball) tarballs that are produced by `docker save`, but this package is still able to read the legacy tarballs produced by `docker save`. ## Usage ```go package main import ( "os" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/tarball" ) func main() { // Read a tarball from os.Args[1] that contains ubuntu. tag, err := name.NewTag("ubuntu") if err != nil { panic(err) } img, err := tarball.ImageFromPath(os.Args[1], &tag) if err != nil { panic(err) } // Write that tarball to os.Args[2] with a different tag. newTag, err := name.NewTag("ubuntu:newest") if err != nil { panic(err) } f, err := os.Create(os.Args[2]) if err != nil { panic(err) } defer f.Close() if err := tarball.Write(newTag, img, f); err != nil { panic(err) } } ``` ## Structure

Let's look at what happens when we write out a tarball: ### `ubuntu:latest` ``` $ crane pull ubuntu ubuntu.tar && mkdir ubuntu && tar xf ubuntu.tar -C ubuntu && rm ubuntu.tar $ tree ubuntu/ ubuntu/ ├── 423ae2b273f4c17ceee9e8482fa8d071d90c7d052ae208e1fe4963fceb3d6954.tar.gz ├── b6b53be908de2c0c78070fff0a9f04835211b3156c4e73785747af365e71a0d7.tar.gz ├── de83a2304fa1f7c4a13708a0d15b9704f5945c2be5cbb2b3ed9b2ccb718d0b3d.tar.gz ├── f9a83bce3af0648efaa60b9bb28225b09136d2d35d0bed25ac764297076dec1b.tar.gz ├── manifest.json └── sha256:72300a873c2ca11c70d0c8642177ce76ff69ae04d61a5813ef58d40ff66e3e7c 0 directories, 6 files ``` There are a couple interesting files here. `manifest.json` is the entrypoint: a list of [`tarball.Descriptor`s](https://godoc.org/github.com/google/go-containerregistry/pkg/v1/tarball#Descriptor) that describe the images contained in this tarball. For each image, this has the `RepoTags` (how it was pulled), a `Config` file that points to the image's config file, a list of `Layers`, and (optionally) `LayerSources`. ``` $ jq < ubuntu/manifest.json [ { "Config": "sha256:72300a873c2ca11c70d0c8642177ce76ff69ae04d61a5813ef58d40ff66e3e7c", "RepoTags": [ "ubuntu" ], "Layers": [ "423ae2b273f4c17ceee9e8482fa8d071d90c7d052ae208e1fe4963fceb3d6954.tar.gz", "de83a2304fa1f7c4a13708a0d15b9704f5945c2be5cbb2b3ed9b2ccb718d0b3d.tar.gz", "f9a83bce3af0648efaa60b9bb28225b09136d2d35d0bed25ac764297076dec1b.tar.gz", "b6b53be908de2c0c78070fff0a9f04835211b3156c4e73785747af365e71a0d7.tar.gz" ] } ] ``` The config file and layers are exactly what you would expect, and match the registry representations of the same artifacts. You'll notice that the `manifest.json` contains similar information as the registry manifest, but isn't quite the same: ``` $ crane manifest ubuntu@sha256:0925d086715714114c1988f7c947db94064fd385e171a63c07730f1fa014e6f9 { "schemaVersion": 2, "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "config": { "mediaType": "application/vnd.docker.container.image.v1+json", "size": 3408, "digest": "sha256:72300a873c2ca11c70d0c8642177ce76ff69ae04d61a5813ef58d40ff66e3e7c" }, "layers": [ { "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", "size": 26692096, "digest": "sha256:423ae2b273f4c17ceee9e8482fa8d071d90c7d052ae208e1fe4963fceb3d6954" }, { "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", "size": 35365, "digest": "sha256:de83a2304fa1f7c4a13708a0d15b9704f5945c2be5cbb2b3ed9b2ccb718d0b3d" }, { "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", "size": 852, "digest": "sha256:f9a83bce3af0648efaa60b9bb28225b09136d2d35d0bed25ac764297076dec1b" }, { "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", "size": 163, "digest": "sha256:b6b53be908de2c0c78070fff0a9f04835211b3156c4e73785747af365e71a0d7" } ] } ``` This makes it difficult to maintain image digests when roundtripping images through the tarball format, so it's not a great format if you care about provenance. The ubuntu example didn't have any `LayerSources` -- let's look at another image that does. ### `hello-world:nanoserver` ``` $ crane pull hello-world:nanoserver@sha256:63c287625c2b0b72900e562de73c0e381472a83b1b39217aef3856cd398eca0b nanoserver.tar $ mkdir nanoserver && tar xf nanoserver.tar -C nanoserver && rm nanoserver.tar $ tree nanoserver/ nanoserver/ ├── 10d1439be4eb8819987ec2e9c140d44d74d6b42a823d57fe1953bd99948e1bc0.tar.gz ├── a35da61c356213336e646756218539950461ff2bf096badf307a23add6e70053.tar.gz ├── be21f08f670160cbae227e3053205b91d6bfa3de750b90c7e00bd2c511ccb63a.tar.gz ├── manifest.json └── sha256:bc5d255ea81f83c8c38a982a6d29a6f2198427d258aea5f166e49856896b2da6 0 directories, 5 files $ jq < nanoserver/manifest.json [ { "Config": "sha256:bc5d255ea81f83c8c38a982a6d29a6f2198427d258aea5f166e49856896b2da6", "RepoTags": [ "index.docker.io/library/hello-world:i-was-a-digest" ], "Layers": [ "a35da61c356213336e646756218539950461ff2bf096badf307a23add6e70053.tar.gz", "be21f08f670160cbae227e3053205b91d6bfa3de750b90c7e00bd2c511ccb63a.tar.gz", "10d1439be4eb8819987ec2e9c140d44d74d6b42a823d57fe1953bd99948e1bc0.tar.gz" ], "LayerSources": { "sha256:26fd2d9d4c64a4f965bbc77939a454a31b607470f430b5d69fc21ded301fa55e": { "mediaType": "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip", "size": 101145811, "digest": "sha256:a35da61c356213336e646756218539950461ff2bf096badf307a23add6e70053", "urls": [ "https://mcr.microsoft.com/v2/windows/nanoserver/blobs/sha256:a35da61c356213336e646756218539950461ff2bf096badf307a23add6e70053" ] } } } ] ``` A couple things to note about this `manifest.json` versus the other: * The `RepoTags` field is a bit weird here. `hello-world` is a multi-platform image, so We had to pull this image by digest, since we're (I'm) on amd64/linux and wanted to grab a windows image. Since the tarball format expects a tag under `RepoTags`, and we didn't pull by tag, we replace the digest with a sentinel `i-was-a-digest` "tag" to appease docker. * The `LayerSources` has enough information to reconstruct the foreign layers pointer when pushing/pulling from the registry. For legal reasons, microsoft doesn't want anyone but them to serve windows base images, so the mediaType here indicates a "foreign" or "non-distributable" layer with an URL for where you can download it from microsoft (see the [OCI image-spec](https://github.com/opencontainers/image-spec/blob/master/layer.md#non-distributable-layers)). We can look at what's in the registry to explain both of these things: ``` $ crane manifest hello-world:nanoserver | jq . { "manifests": [ { "digest": "sha256:63c287625c2b0b72900e562de73c0e381472a83b1b39217aef3856cd398eca0b", "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "platform": { "architecture": "amd64", "os": "windows", "os.version": "10.0.17763.1040" }, "size": 1124 } ], "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json", "schemaVersion": 2 } # Note the media type and "urls" field. $ crane manifest hello-world:nanoserver@sha256:63c287625c2b0b72900e562de73c0e381472a83b1b39217aef3856cd398eca0b | jq . { "schemaVersion": 2, "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "config": { "mediaType": "application/vnd.docker.container.image.v1+json", "size": 1721, "digest": "sha256:bc5d255ea81f83c8c38a982a6d29a6f2198427d258aea5f166e49856896b2da6" }, "layers": [ { "mediaType": "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip", "size": 101145811, "digest": "sha256:a35da61c356213336e646756218539950461ff2bf096badf307a23add6e70053", "urls": [ "https://mcr.microsoft.com/v2/windows/nanoserver/blobs/sha256:a35da61c356213336e646756218539950461ff2bf096badf307a23add6e70053" ] }, { "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", "size": 1669, "digest": "sha256:be21f08f670160cbae227e3053205b91d6bfa3de750b90c7e00bd2c511ccb63a" }, { "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", "size": 949, "digest": "sha256:10d1439be4eb8819987ec2e9c140d44d74d6b42a823d57fe1953bd99948e1bc0" } ] } ``` The `LayerSources` map is keyed by the diffid. Note that `sha256:26fd2d9d4c64a4f965bbc77939a454a31b607470f430b5d69fc21ded301fa55e` matches the first layer in the config file: ``` $ jq '.[0].LayerSources' < nanoserver/manifest.json { "sha256:26fd2d9d4c64a4f965bbc77939a454a31b607470f430b5d69fc21ded301fa55e": { "mediaType": "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip", "size": 101145811, "digest": "sha256:a35da61c356213336e646756218539950461ff2bf096badf307a23add6e70053", "urls": [ "https://mcr.microsoft.com/v2/windows/nanoserver/blobs/sha256:a35da61c356213336e646756218539950461ff2bf096badf307a23add6e70053" ] } } $ jq < nanoserver/sha256\:bc5d255ea81f83c8c38a982a6d29a6f2198427d258aea5f166e49856896b2da6 | jq .rootfs { "type": "layers", "diff_ids": [ "sha256:26fd2d9d4c64a4f965bbc77939a454a31b607470f430b5d69fc21ded301fa55e", "sha256:601cf7d78c62e4b4d32a7bbf96a17606a9cea5bd9d22ffa6f34aa431d056b0e8", "sha256:a1e1a3bf6529adcce4d91dce2cad86c2604a66b507ccbc4d2239f3da0ec5aab9" ] } ``` ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/tarball/doc.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package tarball provides facilities for reading/writing v1.Images from/to // a tarball on-disk. package tarball ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/tarball/image.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package tarball import ( "archive/tar" "bytes" "encoding/json" "errors" "fmt" "io" "os" "path" "path/filepath" "sync" comp "github.com/google/go-containerregistry/internal/compression" "github.com/google/go-containerregistry/pkg/compression" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/partial" "github.com/google/go-containerregistry/pkg/v1/types" ) type image struct { opener Opener manifest *Manifest config []byte imgDescriptor *Descriptor tag *name.Tag } type uncompressedImage struct { *image } type compressedImage struct { *image manifestLock sync.Mutex // Protects manifest manifest *v1.Manifest } var _ partial.UncompressedImageCore = (*uncompressedImage)(nil) var _ partial.CompressedImageCore = (*compressedImage)(nil) // Opener is a thunk for opening a tar file. type Opener func() (io.ReadCloser, error) func pathOpener(path string) Opener { return func() (io.ReadCloser, error) { return os.Open(path) } } // ImageFromPath returns a v1.Image from a tarball located on path. func ImageFromPath(path string, tag *name.Tag) (v1.Image, error) { return Image(pathOpener(path), tag) } // LoadManifest load manifest func LoadManifest(opener Opener) (Manifest, error) { m, err := extractFileFromTar(opener, "manifest.json") if err != nil { return nil, err } defer m.Close() var manifest Manifest if err := json.NewDecoder(m).Decode(&manifest); err != nil { return nil, err } return manifest, nil } // Image exposes an image from the tarball at the provided path. func Image(opener Opener, tag *name.Tag) (v1.Image, error) { img := &image{ opener: opener, tag: tag, } if err := img.loadTarDescriptorAndConfig(); err != nil { return nil, err } // Peek at the first layer and see if it's compressed. if len(img.imgDescriptor.Layers) > 0 { compressed, err := img.areLayersCompressed() if err != nil { return nil, err } if compressed { c := compressedImage{ image: img, } return partial.CompressedToImage(&c) } } uc := uncompressedImage{ image: img, } return partial.UncompressedToImage(&uc) } func (i *image) MediaType() (types.MediaType, error) { return types.DockerManifestSchema2, nil } // Descriptor stores the manifest data for a single image inside a `docker save` tarball. type Descriptor struct { Config string RepoTags []string Layers []string // Tracks foreign layer info. Key is DiffID. LayerSources map[v1.Hash]v1.Descriptor `json:",omitempty"` } // Manifest represents the manifests of all images as the `manifest.json` file in a `docker save` tarball. type Manifest []Descriptor func (m Manifest) findDescriptor(tag *name.Tag) (*Descriptor, error) { if tag == nil { if len(m) != 1 { return nil, errors.New("tarball must contain only a single image to be used with tarball.Image") } return &(m)[0], nil } for _, img := range m { for _, tagStr := range img.RepoTags { repoTag, err := name.NewTag(tagStr) if err != nil { return nil, err } // Compare the resolved names, since there are several ways to specify the same tag. if repoTag.Name() == tag.Name() { return &img, nil } } } return nil, fmt.Errorf("tag %s not found in tarball", tag) } func (i *image) areLayersCompressed() (bool, error) { if len(i.imgDescriptor.Layers) == 0 { return false, errors.New("0 layers found in image") } layer := i.imgDescriptor.Layers[0] blob, err := extractFileFromTar(i.opener, layer) if err != nil { return false, err } defer blob.Close() cp, _, err := comp.PeekCompression(blob) if err != nil { return false, err } return cp != compression.None, nil } func (i *image) loadTarDescriptorAndConfig() error { m, err := extractFileFromTar(i.opener, "manifest.json") if err != nil { return err } defer m.Close() if err := json.NewDecoder(m).Decode(&i.manifest); err != nil { return err } if i.manifest == nil { return errors.New("no valid manifest.json in tarball") } i.imgDescriptor, err = i.manifest.findDescriptor(i.tag) if err != nil { return err } cfg, err := extractFileFromTar(i.opener, i.imgDescriptor.Config) if err != nil { return err } defer cfg.Close() i.config, err = io.ReadAll(cfg) if err != nil { return err } return nil } func (i *image) RawConfigFile() ([]byte, error) { return i.config, nil } // tarFile represents a single file inside a tar. Closing it closes the tar itself. type tarFile struct { io.Reader io.Closer } func extractFileFromTar(opener Opener, filePath string) (io.ReadCloser, error) { f, err := opener() if err != nil { return nil, err } needClose := true defer func() { if needClose { f.Close() } }() tf := tar.NewReader(f) for { hdr, err := tf.Next() if errors.Is(err, io.EOF) { break } if err != nil { return nil, err } if hdr.Name == filePath { if hdr.Typeflag == tar.TypeSymlink || hdr.Typeflag == tar.TypeLink { currentDir := filepath.Dir(filePath) return extractFileFromTar(opener, path.Join(currentDir, path.Clean(hdr.Linkname))) } needClose = false return tarFile{ Reader: tf, Closer: f, }, nil } } return nil, fmt.Errorf("file %s not found in tar", filePath) } // uncompressedLayerFromTarball implements partial.UncompressedLayer type uncompressedLayerFromTarball struct { diffID v1.Hash mediaType types.MediaType opener Opener filePath string } // foreignUncompressedLayer implements partial.UncompressedLayer but returns // a custom descriptor. This allows the foreign layer URLs to be included in // the generated image manifest for uncompressed layers. type foreignUncompressedLayer struct { uncompressedLayerFromTarball desc v1.Descriptor } func (fl *foreignUncompressedLayer) Descriptor() (*v1.Descriptor, error) { return &fl.desc, nil } // DiffID implements partial.UncompressedLayer func (ulft *uncompressedLayerFromTarball) DiffID() (v1.Hash, error) { return ulft.diffID, nil } // Uncompressed implements partial.UncompressedLayer func (ulft *uncompressedLayerFromTarball) Uncompressed() (io.ReadCloser, error) { return extractFileFromTar(ulft.opener, ulft.filePath) } func (ulft *uncompressedLayerFromTarball) MediaType() (types.MediaType, error) { return ulft.mediaType, nil } func (i *uncompressedImage) LayerByDiffID(h v1.Hash) (partial.UncompressedLayer, error) { cfg, err := partial.ConfigFile(i) if err != nil { return nil, err } for idx, diffID := range cfg.RootFS.DiffIDs { if diffID == h { // Technically the media type should be 'application/tar' but given that our // v1.Layer doesn't force consumers to care about whether the layer is compressed // we should be fine returning the DockerLayer media type mt := types.DockerLayer bd, ok := i.imgDescriptor.LayerSources[h] if ok { // This is janky, but we don't want to implement Descriptor for // uncompressed layers because it breaks a bunch of assumptions in partial. // See https://github.com/google/go-containerregistry/issues/1870 docker25workaround := bd.MediaType == types.DockerUncompressedLayer || bd.MediaType == types.OCIUncompressedLayer if !docker25workaround { // Overwrite the mediaType for foreign layers. return &foreignUncompressedLayer{ uncompressedLayerFromTarball: uncompressedLayerFromTarball{ diffID: diffID, mediaType: bd.MediaType, opener: i.opener, filePath: i.imgDescriptor.Layers[idx], }, desc: bd, }, nil } // Intentional fall through. } return &uncompressedLayerFromTarball{ diffID: diffID, mediaType: mt, opener: i.opener, filePath: i.imgDescriptor.Layers[idx], }, nil } } return nil, fmt.Errorf("diff id %q not found", h) } func (c *compressedImage) Manifest() (*v1.Manifest, error) { c.manifestLock.Lock() defer c.manifestLock.Unlock() if c.manifest != nil { return c.manifest, nil } b, err := c.RawConfigFile() if err != nil { return nil, err } cfgHash, cfgSize, err := v1.SHA256(bytes.NewReader(b)) if err != nil { return nil, err } c.manifest = &v1.Manifest{ SchemaVersion: 2, MediaType: types.DockerManifestSchema2, Config: v1.Descriptor{ MediaType: types.DockerConfigJSON, Size: cfgSize, Digest: cfgHash, }, } for i, p := range c.imgDescriptor.Layers { cfg, err := partial.ConfigFile(c) if err != nil { return nil, err } diffid := cfg.RootFS.DiffIDs[i] if d, ok := c.imgDescriptor.LayerSources[diffid]; ok { // If it's a foreign layer, just append the descriptor so we can avoid // reading the entire file. c.manifest.Layers = append(c.manifest.Layers, d) } else { l, err := extractFileFromTar(c.opener, p) if err != nil { return nil, err } defer l.Close() sha, size, err := v1.SHA256(l) if err != nil { return nil, err } c.manifest.Layers = append(c.manifest.Layers, v1.Descriptor{ MediaType: types.DockerLayer, Size: size, Digest: sha, }) } } return c.manifest, nil } func (c *compressedImage) RawManifest() ([]byte, error) { return partial.RawManifest(c) } // compressedLayerFromTarball implements partial.CompressedLayer type compressedLayerFromTarball struct { desc v1.Descriptor opener Opener filePath string } // Digest implements partial.CompressedLayer func (clft *compressedLayerFromTarball) Digest() (v1.Hash, error) { return clft.desc.Digest, nil } // Compressed implements partial.CompressedLayer func (clft *compressedLayerFromTarball) Compressed() (io.ReadCloser, error) { return extractFileFromTar(clft.opener, clft.filePath) } // MediaType implements partial.CompressedLayer func (clft *compressedLayerFromTarball) MediaType() (types.MediaType, error) { return clft.desc.MediaType, nil } // Size implements partial.CompressedLayer func (clft *compressedLayerFromTarball) Size() (int64, error) { return clft.desc.Size, nil } func (c *compressedImage) LayerByDigest(h v1.Hash) (partial.CompressedLayer, error) { m, err := c.Manifest() if err != nil { return nil, err } for i, l := range m.Layers { if l.Digest == h { fp := c.imgDescriptor.Layers[i] return &compressedLayerFromTarball{ desc: l, opener: c.opener, filePath: fp, }, nil } } return nil, fmt.Errorf("blob %v not found", h) } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/tarball/layer.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package tarball import ( "bytes" "compress/gzip" "fmt" "io" "os" "sync" "github.com/containerd/stargz-snapshotter/estargz" "github.com/google/go-containerregistry/internal/and" comp "github.com/google/go-containerregistry/internal/compression" gestargz "github.com/google/go-containerregistry/internal/estargz" ggzip "github.com/google/go-containerregistry/internal/gzip" "github.com/google/go-containerregistry/internal/zstd" "github.com/google/go-containerregistry/pkg/compression" "github.com/google/go-containerregistry/pkg/logs" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/types" ) type layer struct { digest v1.Hash diffID v1.Hash size int64 compressedopener Opener uncompressedopener Opener compression compression.Compression compressionLevel int annotations map[string]string estgzopts []estargz.Option mediaType types.MediaType } // Descriptor implements partial.withDescriptor. func (l *layer) Descriptor() (*v1.Descriptor, error) { digest, err := l.Digest() if err != nil { return nil, err } return &v1.Descriptor{ Size: l.size, Digest: digest, Annotations: l.annotations, MediaType: l.mediaType, }, nil } // Digest implements v1.Layer func (l *layer) Digest() (v1.Hash, error) { return l.digest, nil } // DiffID implements v1.Layer func (l *layer) DiffID() (v1.Hash, error) { return l.diffID, nil } // Compressed implements v1.Layer func (l *layer) Compressed() (io.ReadCloser, error) { return l.compressedopener() } // Uncompressed implements v1.Layer func (l *layer) Uncompressed() (io.ReadCloser, error) { return l.uncompressedopener() } // Size implements v1.Layer func (l *layer) Size() (int64, error) { return l.size, nil } // MediaType implements v1.Layer func (l *layer) MediaType() (types.MediaType, error) { return l.mediaType, nil } // LayerOption applies options to layer type LayerOption func(*layer) // WithCompression is a functional option for overriding the default // compression algorithm used for compressing uncompressed tarballs. // Please note that WithCompression(compression.ZStd) should be used // in conjunction with WithMediaType(types.OCILayerZStd) func WithCompression(comp compression.Compression) LayerOption { return func(l *layer) { switch comp { case compression.ZStd: l.compression = compression.ZStd case compression.GZip: l.compression = compression.GZip case compression.None: logs.Warn.Printf("Compression type 'none' is not supported for tarball layers; using gzip compression.") l.compression = compression.GZip default: logs.Warn.Printf("Unexpected compression type for WithCompression(): %s; using gzip compression instead.", comp) l.compression = compression.GZip } } } // WithCompressionLevel is a functional option for overriding the default // compression level used for compressing uncompressed tarballs. func WithCompressionLevel(level int) LayerOption { return func(l *layer) { l.compressionLevel = level } } // WithMediaType is a functional option for overriding the layer's media type. func WithMediaType(mt types.MediaType) LayerOption { return func(l *layer) { l.mediaType = mt } } // WithCompressedCaching is a functional option that overrides the // logic for accessing the compressed bytes to memoize the result // and avoid expensive repeated gzips. func WithCompressedCaching(l *layer) { var once sync.Once var err error buf := bytes.NewBuffer(nil) og := l.compressedopener l.compressedopener = func() (io.ReadCloser, error) { once.Do(func() { var rc io.ReadCloser rc, err = og() if err == nil { defer rc.Close() _, err = io.Copy(buf, rc) } }) if err != nil { return nil, err } return io.NopCloser(bytes.NewBuffer(buf.Bytes())), nil } } // WithEstargzOptions is a functional option that allow the caller to pass // through estargz.Options to the underlying compression layer. This is // only meaningful when estargz is enabled. // // Deprecated: WithEstargz is deprecated, and will be removed in a future release. func WithEstargzOptions(opts ...estargz.Option) LayerOption { return func(l *layer) { l.estgzopts = opts } } // WithEstargz is a functional option that explicitly enables estargz support. // // Deprecated: WithEstargz is deprecated, and will be removed in a future release. func WithEstargz(l *layer) { oguncompressed := l.uncompressedopener estargz := func() (io.ReadCloser, error) { crc, err := oguncompressed() if err != nil { return nil, err } eopts := append(l.estgzopts, estargz.WithCompressionLevel(l.compressionLevel)) rc, h, err := gestargz.ReadCloser(crc, eopts...) if err != nil { return nil, err } l.annotations[estargz.TOCJSONDigestAnnotation] = h.String() return &and.ReadCloser{ Reader: rc, CloseFunc: func() error { err := rc.Close() if err != nil { return err } // As an optimization, leverage the DiffID exposed by the estargz ReadCloser l.diffID, err = v1.NewHash(rc.DiffID().String()) return err }, }, nil } uncompressed := func() (io.ReadCloser, error) { urc, err := estargz() if err != nil { return nil, err } return ggzip.UnzipReadCloser(urc) } l.compressedopener = estargz l.uncompressedopener = uncompressed } // LayerFromFile returns a v1.Layer given a tarball func LayerFromFile(path string, opts ...LayerOption) (v1.Layer, error) { opener := func() (io.ReadCloser, error) { return os.Open(path) } return LayerFromOpener(opener, opts...) } // LayerFromOpener returns a v1.Layer given an Opener function. // The Opener may return either an uncompressed tarball (common), // or a compressed tarball (uncommon). // // When using this in conjunction with something like remote.Write // the uncompressed path may end up gzipping things multiple times: // 1. Compute the layer SHA256 // 2. Upload the compressed layer. // // Since gzip can be expensive, we support an option to memoize the // compression that can be passed here: tarball.WithCompressedCaching func LayerFromOpener(opener Opener, opts ...LayerOption) (v1.Layer, error) { comp, err := comp.GetCompression(opener) if err != nil { return nil, err } layer := &layer{ compression: compression.GZip, compressionLevel: gzip.BestSpeed, annotations: make(map[string]string, 1), mediaType: types.DockerLayer, } if estgz := os.Getenv("GGCR_EXPERIMENT_ESTARGZ"); estgz == "1" { logs.Warn.Println("GGCR_EXPERIMENT_ESTARGZ is deprecated, and will be removed in a future release.") opts = append([]LayerOption{WithEstargz}, opts...) } switch comp { case compression.GZip: layer.compressedopener = opener layer.uncompressedopener = func() (io.ReadCloser, error) { urc, err := opener() if err != nil { return nil, err } return ggzip.UnzipReadCloser(urc) } case compression.ZStd: layer.compressedopener = opener layer.uncompressedopener = func() (io.ReadCloser, error) { urc, err := opener() if err != nil { return nil, err } return zstd.UnzipReadCloser(urc) } default: layer.uncompressedopener = opener layer.compressedopener = func() (io.ReadCloser, error) { crc, err := opener() if err != nil { return nil, err } if layer.compression == compression.ZStd { return zstd.ReadCloserLevel(crc, layer.compressionLevel), nil } return ggzip.ReadCloserLevel(crc, layer.compressionLevel), nil } } for _, opt := range opts { opt(layer) } // Warn if media type does not match compression var mediaTypeMismatch = false switch layer.compression { case compression.GZip: mediaTypeMismatch = layer.mediaType != types.OCILayer && layer.mediaType != types.OCIRestrictedLayer && layer.mediaType != types.DockerLayer case compression.ZStd: mediaTypeMismatch = layer.mediaType != types.OCILayerZStd } if mediaTypeMismatch { logs.Warn.Printf("Unexpected mediaType (%s) for selected compression in %s in LayerFromOpener().", layer.mediaType, layer.compression) } if layer.digest, layer.size, err = computeDigest(layer.compressedopener); err != nil { return nil, err } empty := v1.Hash{} if layer.diffID == empty { if layer.diffID, err = computeDiffID(layer.uncompressedopener); err != nil { return nil, err } } return layer, nil } // LayerFromReader returns a v1.Layer given a io.Reader. // // The reader's contents are read and buffered to a temp file in the process. // // Deprecated: Use LayerFromOpener or stream.NewLayer instead, if possible. func LayerFromReader(reader io.Reader, opts ...LayerOption) (v1.Layer, error) { tmp, err := os.CreateTemp("", "") if err != nil { return nil, fmt.Errorf("creating temp file to buffer reader: %w", err) } if _, err := io.Copy(tmp, reader); err != nil { return nil, fmt.Errorf("writing temp file to buffer reader: %w", err) } return LayerFromFile(tmp.Name(), opts...) } func computeDigest(opener Opener) (v1.Hash, int64, error) { rc, err := opener() if err != nil { return v1.Hash{}, 0, err } defer rc.Close() return v1.SHA256(rc) } func computeDiffID(opener Opener) (v1.Hash, error) { rc, err := opener() if err != nil { return v1.Hash{}, err } defer rc.Close() digest, _, err := v1.SHA256(rc) return digest, err } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/tarball/write.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package tarball import ( "archive/tar" "bytes" "encoding/json" "errors" "fmt" "io" "os" "sort" "strings" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/partial" ) // WriteToFile writes in the compressed format to a tarball, on disk. // This is just syntactic sugar wrapping tarball.Write with a new file. func WriteToFile(p string, ref name.Reference, img v1.Image, opts ...WriteOption) error { w, err := os.Create(p) if err != nil { return err } defer w.Close() return Write(ref, img, w, opts...) } // MultiWriteToFile writes in the compressed format to a tarball, on disk. // This is just syntactic sugar wrapping tarball.MultiWrite with a new file. func MultiWriteToFile(p string, tagToImage map[name.Tag]v1.Image, opts ...WriteOption) error { refToImage := make(map[name.Reference]v1.Image, len(tagToImage)) for i, d := range tagToImage { refToImage[i] = d } return MultiRefWriteToFile(p, refToImage, opts...) } // MultiRefWriteToFile writes in the compressed format to a tarball, on disk. // This is just syntactic sugar wrapping tarball.MultiRefWrite with a new file. func MultiRefWriteToFile(p string, refToImage map[name.Reference]v1.Image, opts ...WriteOption) error { w, err := os.Create(p) if err != nil { return err } defer w.Close() return MultiRefWrite(refToImage, w, opts...) } // Write is a wrapper to write a single image and tag to a tarball. func Write(ref name.Reference, img v1.Image, w io.Writer, opts ...WriteOption) error { return MultiRefWrite(map[name.Reference]v1.Image{ref: img}, w, opts...) } // MultiWrite writes the contents of each image to the provided writer, in the compressed format. // The contents are written in the following format: // One manifest.json file at the top level containing information about several images. // One file for each layer, named after the layer's SHA. // One file for the config blob, named after its SHA. func MultiWrite(tagToImage map[name.Tag]v1.Image, w io.Writer, opts ...WriteOption) error { refToImage := make(map[name.Reference]v1.Image, len(tagToImage)) for i, d := range tagToImage { refToImage[i] = d } return MultiRefWrite(refToImage, w, opts...) } // MultiRefWrite writes the contents of each image to the provided writer, in the compressed format. // The contents are written in the following format: // One manifest.json file at the top level containing information about several images. // One file for each layer, named after the layer's SHA. // One file for the config blob, named after its SHA. func MultiRefWrite(refToImage map[name.Reference]v1.Image, w io.Writer, opts ...WriteOption) error { // process options o := &writeOptions{ updates: nil, } for _, option := range opts { if err := option(o); err != nil { return err } } imageToTags := dedupRefToImage(refToImage) size, mBytes, err := getSizeAndManifest(imageToTags) if err != nil { return sendUpdateReturn(o, err) } return writeImagesToTar(imageToTags, mBytes, size, w, o) } // sendUpdateReturn return the passed in error message, also sending on update channel, if it exists func sendUpdateReturn(o *writeOptions, err error) error { if o != nil && o.updates != nil { o.updates <- v1.Update{ Error: err, } } return err } // sendProgressWriterReturn return the passed in error message, also sending on update channel, if it exists, along with downloaded information func sendProgressWriterReturn(pw *progressWriter, err error) error { if pw != nil { return pw.Error(err) } return err } // writeImagesToTar writes the images to the tarball func writeImagesToTar(imageToTags map[v1.Image][]string, m []byte, size int64, w io.Writer, o *writeOptions) (err error) { if w == nil { return sendUpdateReturn(o, errors.New("must pass valid writer")) } tw := w var pw *progressWriter // we only calculate the sizes and use a progressWriter if we were provided // an option with a progress channel if o != nil && o.updates != nil { pw = &progressWriter{ w: w, updates: o.updates, size: size, } tw = pw } tf := tar.NewWriter(tw) defer tf.Close() seenLayerDigests := make(map[string]struct{}) for img := range imageToTags { // Write the config. cfgName, err := img.ConfigName() if err != nil { return sendProgressWriterReturn(pw, err) } cfgBlob, err := img.RawConfigFile() if err != nil { return sendProgressWriterReturn(pw, err) } if err := writeTarEntry(tf, cfgName.String(), bytes.NewReader(cfgBlob), int64(len(cfgBlob))); err != nil { return sendProgressWriterReturn(pw, err) } // Write the layers. layers, err := img.Layers() if err != nil { return sendProgressWriterReturn(pw, err) } layerFiles := make([]string, len(layers)) for i, l := range layers { d, err := l.Digest() if err != nil { return sendProgressWriterReturn(pw, err) } // Munge the file name to appease ancient technology. // // tar assumes anything with a colon is a remote tape drive: // https://www.gnu.org/software/tar/manual/html_section/tar_45.html // Drop the algorithm prefix, e.g. "sha256:" hex := d.Hex // gunzip expects certain file extensions: // https://www.gnu.org/software/gzip/manual/html_node/Overview.html layerFiles[i] = fmt.Sprintf("%s.tar.gz", hex) if _, ok := seenLayerDigests[hex]; ok { continue } seenLayerDigests[hex] = struct{}{} r, err := l.Compressed() if err != nil { return sendProgressWriterReturn(pw, err) } blobSize, err := l.Size() if err != nil { return sendProgressWriterReturn(pw, err) } if err := writeTarEntry(tf, layerFiles[i], r, blobSize); err != nil { return sendProgressWriterReturn(pw, err) } } } if err := writeTarEntry(tf, "manifest.json", bytes.NewReader(m), int64(len(m))); err != nil { return sendProgressWriterReturn(pw, err) } // be sure to close the tar writer so everything is flushed out before we send our EOF if err := tf.Close(); err != nil { return sendProgressWriterReturn(pw, err) } // send an EOF to indicate finished on the channel, but nil as our return error _ = sendProgressWriterReturn(pw, io.EOF) return nil } // calculateManifest calculates the manifest and optionally the size of the tar file func calculateManifest(imageToTags map[v1.Image][]string) (m Manifest, err error) { if len(imageToTags) == 0 { return nil, errors.New("set of images is empty") } for img, tags := range imageToTags { cfgName, err := img.ConfigName() if err != nil { return nil, err } // Store foreign layer info. layerSources := make(map[v1.Hash]v1.Descriptor) // Write the layers. layers, err := img.Layers() if err != nil { return nil, err } layerFiles := make([]string, len(layers)) for i, l := range layers { d, err := l.Digest() if err != nil { return nil, err } // Munge the file name to appease ancient technology. // // tar assumes anything with a colon is a remote tape drive: // https://www.gnu.org/software/tar/manual/html_section/tar_45.html // Drop the algorithm prefix, e.g. "sha256:" hex := d.Hex // gunzip expects certain file extensions: // https://www.gnu.org/software/gzip/manual/html_node/Overview.html layerFiles[i] = fmt.Sprintf("%s.tar.gz", hex) // Add to LayerSources if it's a foreign layer. desc, err := partial.BlobDescriptor(img, d) if err != nil { return nil, err } if !desc.MediaType.IsDistributable() { diffid, err := partial.BlobToDiffID(img, d) if err != nil { return nil, err } layerSources[diffid] = *desc } } // Generate the tar descriptor and write it. m = append(m, Descriptor{ Config: cfgName.String(), RepoTags: tags, Layers: layerFiles, LayerSources: layerSources, }) } // sort by name of the repotags so it is consistent. Alternatively, we could sort by hash of the // descriptor, but that would make it hard for humans to process sort.Slice(m, func(i, j int) bool { return strings.Join(m[i].RepoTags, ",") < strings.Join(m[j].RepoTags, ",") }) return m, nil } // CalculateSize calculates the expected complete size of the output tar file func CalculateSize(refToImage map[name.Reference]v1.Image) (size int64, err error) { imageToTags := dedupRefToImage(refToImage) size, _, err = getSizeAndManifest(imageToTags) return size, err } func getSizeAndManifest(imageToTags map[v1.Image][]string) (int64, []byte, error) { m, err := calculateManifest(imageToTags) if err != nil { return 0, nil, fmt.Errorf("unable to calculate manifest: %w", err) } mBytes, err := json.Marshal(m) if err != nil { return 0, nil, fmt.Errorf("could not marshall manifest to bytes: %w", err) } size, err := calculateTarballSize(imageToTags, mBytes) if err != nil { return 0, nil, fmt.Errorf("error calculating tarball size: %w", err) } return size, mBytes, nil } // calculateTarballSize calculates the size of the tar file func calculateTarballSize(imageToTags map[v1.Image][]string, mBytes []byte) (size int64, err error) { seenLayerDigests := make(map[string]struct{}) for img, name := range imageToTags { manifest, err := img.Manifest() if err != nil { return size, fmt.Errorf("unable to get manifest for img %s: %w", name, err) } size += calculateSingleFileInTarSize(manifest.Config.Size) for _, l := range manifest.Layers { hex := l.Digest.Hex if _, ok := seenLayerDigests[hex]; ok { continue } seenLayerDigests[hex] = struct{}{} size += calculateSingleFileInTarSize(l.Size) } } // add the manifest size += calculateSingleFileInTarSize(int64(len(mBytes))) // add the two padding blocks that indicate end of a tar file size += 1024 return size, nil } func dedupRefToImage(refToImage map[name.Reference]v1.Image) map[v1.Image][]string { imageToTags := make(map[v1.Image][]string) for ref, img := range refToImage { if tag, ok := ref.(name.Tag); ok { if tags, ok := imageToTags[img]; !ok || tags == nil { imageToTags[img] = []string{} } // Docker cannot load tarballs without an explicit tag: // https://github.com/google/go-containerregistry/issues/890 // // We can't use the fully qualified tag.Name() because of rules_docker: // https://github.com/google/go-containerregistry/issues/527 // // If the tag is "latest", but tag.String() doesn't end in ":latest", // just append it. Kind of gross, but should work for now. ts := tag.String() if tag.Identifier() == name.DefaultTag && !strings.HasSuffix(ts, ":"+name.DefaultTag) { ts = fmt.Sprintf("%s:%s", ts, name.DefaultTag) } imageToTags[img] = append(imageToTags[img], ts) } else if _, ok := imageToTags[img]; !ok { imageToTags[img] = nil } } return imageToTags } // writeTarEntry writes a file to the provided writer with a corresponding tar header func writeTarEntry(tf *tar.Writer, path string, r io.Reader, size int64) error { hdr := &tar.Header{ Mode: 0644, Typeflag: tar.TypeReg, Size: size, Name: path, } if err := tf.WriteHeader(hdr); err != nil { return err } _, err := io.Copy(tf, r) return err } // ComputeManifest get the manifest.json that will be written to the tarball // for multiple references func ComputeManifest(refToImage map[name.Reference]v1.Image) (Manifest, error) { imageToTags := dedupRefToImage(refToImage) return calculateManifest(imageToTags) } // WriteOption a function option to pass to Write() type WriteOption func(*writeOptions) error type writeOptions struct { updates chan<- v1.Update } // WithProgress create a WriteOption for passing to Write() that enables // a channel to receive updates as they are downloaded and written to disk. func WithProgress(updates chan<- v1.Update) WriteOption { return func(o *writeOptions) error { o.updates = updates return nil } } // progressWriter is a writer which will send the download progress type progressWriter struct { w io.Writer updates chan<- v1.Update size, complete int64 } func (pw *progressWriter) Write(p []byte) (int, error) { n, err := pw.w.Write(p) if err != nil { return n, err } pw.complete += int64(n) pw.updates <- v1.Update{ Total: pw.size, Complete: pw.complete, } return n, err } func (pw *progressWriter) Error(err error) error { pw.updates <- v1.Update{ Total: pw.size, Complete: pw.complete, Error: err, } return err } func (pw *progressWriter) Close() error { pw.updates <- v1.Update{ Total: pw.size, Complete: pw.complete, Error: io.EOF, } return io.EOF } // calculateSingleFileInTarSize calculate the size a file will take up in a tar archive, // given the input data. Provided by rounding up to nearest whole block (512) // and adding header 512 func calculateSingleFileInTarSize(in int64) (out int64) { // doing this manually, because math.Round() works with float64 out += in if remainder := out % 512; remainder != 0 { out += (512 - remainder) } out += 512 return out } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/types/types.go ================================================ // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package types holds common OCI media types. package types // MediaType is an enumeration of the supported mime types that an element of an image might have. type MediaType string // The collection of known MediaType values. const ( OCIContentDescriptor MediaType = "application/vnd.oci.descriptor.v1+json" OCIImageIndex MediaType = "application/vnd.oci.image.index.v1+json" OCIManifestSchema1 MediaType = "application/vnd.oci.image.manifest.v1+json" OCIConfigJSON MediaType = "application/vnd.oci.image.config.v1+json" OCILayer MediaType = "application/vnd.oci.image.layer.v1.tar+gzip" OCILayerZStd MediaType = "application/vnd.oci.image.layer.v1.tar+zstd" OCIRestrictedLayer MediaType = "application/vnd.oci.image.layer.nondistributable.v1.tar+gzip" OCIUncompressedLayer MediaType = "application/vnd.oci.image.layer.v1.tar" OCIUncompressedRestrictedLayer MediaType = "application/vnd.oci.image.layer.nondistributable.v1.tar" DockerManifestSchema1 MediaType = "application/vnd.docker.distribution.manifest.v1+json" DockerManifestSchema1Signed MediaType = "application/vnd.docker.distribution.manifest.v1+prettyjws" DockerManifestSchema2 MediaType = "application/vnd.docker.distribution.manifest.v2+json" DockerManifestList MediaType = "application/vnd.docker.distribution.manifest.list.v2+json" DockerLayer MediaType = "application/vnd.docker.image.rootfs.diff.tar.gzip" DockerConfigJSON MediaType = "application/vnd.docker.container.image.v1+json" DockerPluginConfig MediaType = "application/vnd.docker.plugin.v1+json" DockerForeignLayer MediaType = "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip" DockerUncompressedLayer MediaType = "application/vnd.docker.image.rootfs.diff.tar" OCIVendorPrefix = "vnd.oci" DockerVendorPrefix = "vnd.docker" ) // IsDistributable returns true if a layer is distributable, see: // https://github.com/opencontainers/image-spec/blob/master/layer.md#non-distributable-layers func (m MediaType) IsDistributable() bool { switch m { case DockerForeignLayer, OCIRestrictedLayer, OCIUncompressedRestrictedLayer: return false } return true } // IsImage returns true if the mediaType represents an image manifest, as opposed to something else, like an index. func (m MediaType) IsImage() bool { switch m { case OCIManifestSchema1, DockerManifestSchema2: return true } return false } // IsIndex returns true if the mediaType represents an index, as opposed to something else, like an image. func (m MediaType) IsIndex() bool { switch m { case OCIImageIndex, DockerManifestList: return true } return false } // IsConfig returns true if the mediaType represents a config, as opposed to something else, like an image. func (m MediaType) IsConfig() bool { switch m { case OCIConfigJSON, DockerConfigJSON: return true } return false } func (m MediaType) IsSchema1() bool { switch m { case DockerManifestSchema1, DockerManifestSchema1Signed: return true } return false } func (m MediaType) IsLayer() bool { switch m { case DockerLayer, DockerUncompressedLayer, OCILayer, OCILayerZStd, OCIUncompressedLayer, DockerForeignLayer, OCIRestrictedLayer, OCIUncompressedRestrictedLayer: return true } return false } ================================================ FILE: vendor/github.com/google/go-containerregistry/pkg/v1/zz_deepcopy_generated.go ================================================ //go:build !ignore_autogenerated // +build !ignore_autogenerated // Copyright 2018 Google LLC All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Code generated by deepcopy-gen. DO NOT EDIT. package v1 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Config) DeepCopyInto(out *Config) { *out = *in if in.Cmd != nil { in, out := &in.Cmd, &out.Cmd *out = make([]string, len(*in)) copy(*out, *in) } if in.Healthcheck != nil { in, out := &in.Healthcheck, &out.Healthcheck *out = new(HealthConfig) (*in).DeepCopyInto(*out) } if in.Entrypoint != nil { in, out := &in.Entrypoint, &out.Entrypoint *out = make([]string, len(*in)) copy(*out, *in) } if in.Env != nil { in, out := &in.Env, &out.Env *out = make([]string, len(*in)) copy(*out, *in) } if in.Labels != nil { in, out := &in.Labels, &out.Labels *out = make(map[string]string, len(*in)) for key, val := range *in { (*out)[key] = val } } if in.OnBuild != nil { in, out := &in.OnBuild, &out.OnBuild *out = make([]string, len(*in)) copy(*out, *in) } if in.Volumes != nil { in, out := &in.Volumes, &out.Volumes *out = make(map[string]struct{}, len(*in)) for key, val := range *in { (*out)[key] = val } } if in.ExposedPorts != nil { in, out := &in.ExposedPorts, &out.ExposedPorts *out = make(map[string]struct{}, len(*in)) for key, val := range *in { (*out)[key] = val } } if in.Shell != nil { in, out := &in.Shell, &out.Shell *out = make([]string, len(*in)) copy(*out, *in) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. func (in *Config) DeepCopy() *Config { if in == nil { return nil } out := new(Config) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ConfigFile) DeepCopyInto(out *ConfigFile) { *out = *in in.Created.DeepCopyInto(&out.Created) if in.History != nil { in, out := &in.History, &out.History *out = make([]History, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } in.RootFS.DeepCopyInto(&out.RootFS) in.Config.DeepCopyInto(&out.Config) if in.OSFeatures != nil { in, out := &in.OSFeatures, &out.OSFeatures *out = make([]string, len(*in)) copy(*out, *in) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigFile. func (in *ConfigFile) DeepCopy() *ConfigFile { if in == nil { return nil } out := new(ConfigFile) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Descriptor) DeepCopyInto(out *Descriptor) { *out = *in out.Digest = in.Digest if in.Data != nil { in, out := &in.Data, &out.Data *out = make([]byte, len(*in)) copy(*out, *in) } if in.URLs != nil { in, out := &in.URLs, &out.URLs *out = make([]string, len(*in)) copy(*out, *in) } if in.Annotations != nil { in, out := &in.Annotations, &out.Annotations *out = make(map[string]string, len(*in)) for key, val := range *in { (*out)[key] = val } } if in.Platform != nil { in, out := &in.Platform, &out.Platform *out = new(Platform) (*in).DeepCopyInto(*out) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Descriptor. func (in *Descriptor) DeepCopy() *Descriptor { if in == nil { return nil } out := new(Descriptor) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Hash) DeepCopyInto(out *Hash) { *out = *in return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Hash. func (in *Hash) DeepCopy() *Hash { if in == nil { return nil } out := new(Hash) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HealthConfig) DeepCopyInto(out *HealthConfig) { *out = *in if in.Test != nil { in, out := &in.Test, &out.Test *out = make([]string, len(*in)) copy(*out, *in) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HealthConfig. func (in *HealthConfig) DeepCopy() *HealthConfig { if in == nil { return nil } out := new(HealthConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *History) DeepCopyInto(out *History) { *out = *in in.Created.DeepCopyInto(&out.Created) return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new History. func (in *History) DeepCopy() *History { if in == nil { return nil } out := new(History) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *IndexManifest) DeepCopyInto(out *IndexManifest) { *out = *in if in.Manifests != nil { in, out := &in.Manifests, &out.Manifests *out = make([]Descriptor, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } if in.Annotations != nil { in, out := &in.Annotations, &out.Annotations *out = make(map[string]string, len(*in)) for key, val := range *in { (*out)[key] = val } } if in.Subject != nil { in, out := &in.Subject, &out.Subject *out = new(Descriptor) (*in).DeepCopyInto(*out) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IndexManifest. func (in *IndexManifest) DeepCopy() *IndexManifest { if in == nil { return nil } out := new(IndexManifest) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Manifest) DeepCopyInto(out *Manifest) { *out = *in in.Config.DeepCopyInto(&out.Config) if in.Layers != nil { in, out := &in.Layers, &out.Layers *out = make([]Descriptor, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } if in.Annotations != nil { in, out := &in.Annotations, &out.Annotations *out = make(map[string]string, len(*in)) for key, val := range *in { (*out)[key] = val } } if in.Subject != nil { in, out := &in.Subject, &out.Subject *out = new(Descriptor) (*in).DeepCopyInto(*out) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Manifest. func (in *Manifest) DeepCopy() *Manifest { if in == nil { return nil } out := new(Manifest) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Platform) DeepCopyInto(out *Platform) { *out = *in if in.OSFeatures != nil { in, out := &in.OSFeatures, &out.OSFeatures *out = make([]string, len(*in)) copy(*out, *in) } if in.Features != nil { in, out := &in.Features, &out.Features *out = make([]string, len(*in)) copy(*out, *in) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Platform. func (in *Platform) DeepCopy() *Platform { if in == nil { return nil } out := new(Platform) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RootFS) DeepCopyInto(out *RootFS) { *out = *in if in.DiffIDs != nil { in, out := &in.DiffIDs, &out.DiffIDs *out = make([]Hash, len(*in)) copy(*out, *in) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RootFS. func (in *RootFS) DeepCopy() *RootFS { if in == nil { return nil } out := new(RootFS) in.DeepCopyInto(out) return out } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Time. func (in *Time) DeepCopy() *Time { if in == nil { return nil } out := new(Time) in.DeepCopyInto(out) return out } ================================================ FILE: vendor/github.com/google/gofuzz/.travis.yml ================================================ language: go go: - 1.11.x - 1.12.x - 1.13.x - master script: - go test -cover ================================================ FILE: vendor/github.com/google/gofuzz/CONTRIBUTING.md ================================================ # How to contribute # We'd love to accept your patches and contributions to this project. There are just a few small guidelines you need to follow. ## Contributor License Agreement ## Contributions to any Google project must be accompanied by a Contributor License Agreement. This is not a copyright **assignment**, it simply gives Google permission to use and redistribute your contributions as part of the project. * If you are an individual writing original source code and you're sure you own the intellectual property, then you'll need to sign an [individual CLA][]. * If you work for a company that wants to allow you to contribute your work, then you'll need to sign a [corporate CLA][]. You generally only need to submit a CLA once, so if you've already submitted one (even if it was for a different project), you probably don't need to do it again. [individual CLA]: https://developers.google.com/open-source/cla/individual [corporate CLA]: https://developers.google.com/open-source/cla/corporate ## Submitting a patch ## 1. It's generally best to start by opening a new issue describing the bug or feature you're intending to fix. Even if you think it's relatively minor, it's helpful to know what people are working on. Mention in the initial issue that you are planning to work on that bug or feature so that it can be assigned to you. 1. Follow the normal process of [forking][] the project, and setup a new branch to work in. It's important that each group of changes be done in separate branches in order to ensure that a pull request only includes the commits related to that bug or feature. 1. Go makes it very simple to ensure properly formatted code, so always run `go fmt` on your code before committing it. You should also run [golint][] over your code. As noted in the [golint readme][], it's not strictly necessary that your code be completely "lint-free", but this will help you find common style issues. 1. Any significant changes should almost always be accompanied by tests. The project already has good test coverage, so look at some of the existing tests if you're unsure how to go about it. [gocov][] and [gocov-html][] are invaluable tools for seeing which parts of your code aren't being exercised by your tests. 1. Do your best to have [well-formed commit messages][] for each change. This provides consistency throughout the project, and ensures that commit messages are able to be formatted properly by various git tools. 1. Finally, push the commits to your fork and submit a [pull request][]. [forking]: https://help.github.com/articles/fork-a-repo [golint]: https://github.com/golang/lint [golint readme]: https://github.com/golang/lint/blob/master/README [gocov]: https://github.com/axw/gocov [gocov-html]: https://github.com/matm/gocov-html [well-formed commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html [squash]: http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits [pull request]: https://help.github.com/articles/creating-a-pull-request ================================================ FILE: vendor/github.com/google/gofuzz/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/google/gofuzz/README.md ================================================ gofuzz ====== gofuzz is a library for populating go objects with random values. [![GoDoc](https://godoc.org/github.com/google/gofuzz?status.svg)](https://godoc.org/github.com/google/gofuzz) [![Travis](https://travis-ci.org/google/gofuzz.svg?branch=master)](https://travis-ci.org/google/gofuzz) This is useful for testing: * Do your project's objects really serialize/unserialize correctly in all cases? * Is there an incorrectly formatted object that will cause your project to panic? Import with ```import "github.com/google/gofuzz"``` You can use it on single variables: ```go f := fuzz.New() var myInt int f.Fuzz(&myInt) // myInt gets a random value. ``` You can use it on maps: ```go f := fuzz.New().NilChance(0).NumElements(1, 1) var myMap map[ComplexKeyType]string f.Fuzz(&myMap) // myMap will have exactly one element. ``` Customize the chance of getting a nil pointer: ```go f := fuzz.New().NilChance(.5) var fancyStruct struct { A, B, C, D *string } f.Fuzz(&fancyStruct) // About half the pointers should be set. ``` You can even customize the randomization completely if needed: ```go type MyEnum string const ( A MyEnum = "A" B MyEnum = "B" ) type MyInfo struct { Type MyEnum AInfo *string BInfo *string } f := fuzz.New().NilChance(0).Funcs( func(e *MyInfo, c fuzz.Continue) { switch c.Intn(2) { case 0: e.Type = A c.Fuzz(&e.AInfo) case 1: e.Type = B c.Fuzz(&e.BInfo) } }, ) var myObject MyInfo f.Fuzz(&myObject) // Type will correspond to whether A or B info is set. ``` See more examples in ```example_test.go```. You can use this library for easier [go-fuzz](https://github.com/dvyukov/go-fuzz)ing. go-fuzz provides the user a byte-slice, which should be converted to different inputs for the tested function. This library can help convert the byte slice. Consider for example a fuzz test for a the function `mypackage.MyFunc` that takes an int arguments: ```go // +build gofuzz package mypackage import fuzz "github.com/google/gofuzz" func Fuzz(data []byte) int { var i int fuzz.NewFromGoFuzz(data).Fuzz(&i) MyFunc(i) return 0 } ``` Happy testing! ================================================ FILE: vendor/github.com/google/gofuzz/bytesource/bytesource.go ================================================ /* Copyright 2014 Google Inc. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // Package bytesource provides a rand.Source64 that is determined by a slice of bytes. package bytesource import ( "bytes" "encoding/binary" "io" "math/rand" ) // ByteSource implements rand.Source64 determined by a slice of bytes. The random numbers are // generated from each 8 bytes in the slice, until the last bytes are consumed, from which a // fallback pseudo random source is created in case more random numbers are required. // It also exposes a `bytes.Reader` API, which lets callers consume the bytes directly. type ByteSource struct { *bytes.Reader fallback rand.Source } // New returns a new ByteSource from a given slice of bytes. func New(input []byte) *ByteSource { s := &ByteSource{ Reader: bytes.NewReader(input), fallback: rand.NewSource(0), } if len(input) > 0 { s.fallback = rand.NewSource(int64(s.consumeUint64())) } return s } func (s *ByteSource) Uint64() uint64 { // Return from input if it was not exhausted. if s.Len() > 0 { return s.consumeUint64() } // Input was exhausted, return random number from fallback (in this case fallback should not be // nil). Try first having a Uint64 output (Should work in current rand implementation), // otherwise return a conversion of Int63. if s64, ok := s.fallback.(rand.Source64); ok { return s64.Uint64() } return uint64(s.fallback.Int63()) } func (s *ByteSource) Int63() int64 { return int64(s.Uint64() >> 1) } func (s *ByteSource) Seed(seed int64) { s.fallback = rand.NewSource(seed) s.Reader = bytes.NewReader(nil) } // consumeUint64 reads 8 bytes from the input and convert them to a uint64. It assumes that the the // bytes reader is not empty. func (s *ByteSource) consumeUint64() uint64 { var bytes [8]byte _, err := s.Read(bytes[:]) if err != nil && err != io.EOF { panic("failed reading source") // Should not happen. } return binary.BigEndian.Uint64(bytes[:]) } ================================================ FILE: vendor/github.com/google/gofuzz/doc.go ================================================ /* Copyright 2014 Google Inc. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // Package fuzz is a library for populating go objects with random values. package fuzz ================================================ FILE: vendor/github.com/google/gofuzz/fuzz.go ================================================ /* Copyright 2014 Google Inc. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package fuzz import ( "fmt" "math/rand" "reflect" "regexp" "time" "github.com/google/gofuzz/bytesource" "strings" ) // fuzzFuncMap is a map from a type to a fuzzFunc that handles that type. type fuzzFuncMap map[reflect.Type]reflect.Value // Fuzzer knows how to fill any object with random fields. type Fuzzer struct { fuzzFuncs fuzzFuncMap defaultFuzzFuncs fuzzFuncMap r *rand.Rand nilChance float64 minElements int maxElements int maxDepth int skipFieldPatterns []*regexp.Regexp } // New returns a new Fuzzer. Customize your Fuzzer further by calling Funcs, // RandSource, NilChance, or NumElements in any order. func New() *Fuzzer { return NewWithSeed(time.Now().UnixNano()) } func NewWithSeed(seed int64) *Fuzzer { f := &Fuzzer{ defaultFuzzFuncs: fuzzFuncMap{ reflect.TypeOf(&time.Time{}): reflect.ValueOf(fuzzTime), }, fuzzFuncs: fuzzFuncMap{}, r: rand.New(rand.NewSource(seed)), nilChance: .2, minElements: 1, maxElements: 10, maxDepth: 100, } return f } // NewFromGoFuzz is a helper function that enables using gofuzz (this // project) with go-fuzz (https://github.com/dvyukov/go-fuzz) for continuous // fuzzing. Essentially, it enables translating the fuzzing bytes from // go-fuzz to any Go object using this library. // // This implementation promises a constant translation from a given slice of // bytes to the fuzzed objects. This promise will remain over future // versions of Go and of this library. // // Note: the returned Fuzzer should not be shared between multiple goroutines, // as its deterministic output will no longer be available. // // Example: use go-fuzz to test the function `MyFunc(int)` in the package // `mypackage`. Add the file: "mypacakge_fuzz.go" with the content: // // // +build gofuzz // package mypacakge // import fuzz "github.com/google/gofuzz" // func Fuzz(data []byte) int { // var i int // fuzz.NewFromGoFuzz(data).Fuzz(&i) // MyFunc(i) // return 0 // } func NewFromGoFuzz(data []byte) *Fuzzer { return New().RandSource(bytesource.New(data)) } // Funcs adds each entry in fuzzFuncs as a custom fuzzing function. // // Each entry in fuzzFuncs must be a function taking two parameters. // The first parameter must be a pointer or map. It is the variable that // function will fill with random data. The second parameter must be a // fuzz.Continue, which will provide a source of randomness and a way // to automatically continue fuzzing smaller pieces of the first parameter. // // These functions are called sensibly, e.g., if you wanted custom string // fuzzing, the function `func(s *string, c fuzz.Continue)` would get // called and passed the address of strings. Maps and pointers will always // be made/new'd for you, ignoring the NilChange option. For slices, it // doesn't make much sense to pre-create them--Fuzzer doesn't know how // long you want your slice--so take a pointer to a slice, and make it // yourself. (If you don't want your map/pointer type pre-made, take a // pointer to it, and make it yourself.) See the examples for a range of // custom functions. func (f *Fuzzer) Funcs(fuzzFuncs ...interface{}) *Fuzzer { for i := range fuzzFuncs { v := reflect.ValueOf(fuzzFuncs[i]) if v.Kind() != reflect.Func { panic("Need only funcs!") } t := v.Type() if t.NumIn() != 2 || t.NumOut() != 0 { panic("Need 2 in and 0 out params!") } argT := t.In(0) switch argT.Kind() { case reflect.Ptr, reflect.Map: default: panic("fuzzFunc must take pointer or map type") } if t.In(1) != reflect.TypeOf(Continue{}) { panic("fuzzFunc's second parameter must be type fuzz.Continue") } f.fuzzFuncs[argT] = v } return f } // RandSource causes f to get values from the given source of randomness. // Use if you want deterministic fuzzing. func (f *Fuzzer) RandSource(s rand.Source) *Fuzzer { f.r = rand.New(s) return f } // NilChance sets the probability of creating a nil pointer, map, or slice to // 'p'. 'p' should be between 0 (no nils) and 1 (all nils), inclusive. func (f *Fuzzer) NilChance(p float64) *Fuzzer { if p < 0 || p > 1 { panic("p should be between 0 and 1, inclusive.") } f.nilChance = p return f } // NumElements sets the minimum and maximum number of elements that will be // added to a non-nil map or slice. func (f *Fuzzer) NumElements(atLeast, atMost int) *Fuzzer { if atLeast > atMost { panic("atLeast must be <= atMost") } if atLeast < 0 { panic("atLeast must be >= 0") } f.minElements = atLeast f.maxElements = atMost return f } func (f *Fuzzer) genElementCount() int { if f.minElements == f.maxElements { return f.minElements } return f.minElements + f.r.Intn(f.maxElements-f.minElements+1) } func (f *Fuzzer) genShouldFill() bool { return f.r.Float64() >= f.nilChance } // MaxDepth sets the maximum number of recursive fuzz calls that will be made // before stopping. This includes struct members, pointers, and map and slice // elements. func (f *Fuzzer) MaxDepth(d int) *Fuzzer { f.maxDepth = d return f } // Skip fields which match the supplied pattern. Call this multiple times if needed // This is useful to skip XXX_ fields generated by protobuf func (f *Fuzzer) SkipFieldsWithPattern(pattern *regexp.Regexp) *Fuzzer { f.skipFieldPatterns = append(f.skipFieldPatterns, pattern) return f } // Fuzz recursively fills all of obj's fields with something random. First // this tries to find a custom fuzz function (see Funcs). If there is no // custom function this tests whether the object implements fuzz.Interface and, // if so, calls Fuzz on it to fuzz itself. If that fails, this will see if // there is a default fuzz function provided by this package. If all of that // fails, this will generate random values for all primitive fields and then // recurse for all non-primitives. // // This is safe for cyclic or tree-like structs, up to a limit. Use the // MaxDepth method to adjust how deep you need it to recurse. // // obj must be a pointer. Only exported (public) fields can be set (thanks, // golang :/ ) Intended for tests, so will panic on bad input or unimplemented // fields. func (f *Fuzzer) Fuzz(obj interface{}) { v := reflect.ValueOf(obj) if v.Kind() != reflect.Ptr { panic("needed ptr!") } v = v.Elem() f.fuzzWithContext(v, 0) } // FuzzNoCustom is just like Fuzz, except that any custom fuzz function for // obj's type will not be called and obj will not be tested for fuzz.Interface // conformance. This applies only to obj and not other instances of obj's // type. // Not safe for cyclic or tree-like structs! // obj must be a pointer. Only exported (public) fields can be set (thanks, golang :/ ) // Intended for tests, so will panic on bad input or unimplemented fields. func (f *Fuzzer) FuzzNoCustom(obj interface{}) { v := reflect.ValueOf(obj) if v.Kind() != reflect.Ptr { panic("needed ptr!") } v = v.Elem() f.fuzzWithContext(v, flagNoCustomFuzz) } const ( // Do not try to find a custom fuzz function. Does not apply recursively. flagNoCustomFuzz uint64 = 1 << iota ) func (f *Fuzzer) fuzzWithContext(v reflect.Value, flags uint64) { fc := &fuzzerContext{fuzzer: f} fc.doFuzz(v, flags) } // fuzzerContext carries context about a single fuzzing run, which lets Fuzzer // be thread-safe. type fuzzerContext struct { fuzzer *Fuzzer curDepth int } func (fc *fuzzerContext) doFuzz(v reflect.Value, flags uint64) { if fc.curDepth >= fc.fuzzer.maxDepth { return } fc.curDepth++ defer func() { fc.curDepth-- }() if !v.CanSet() { return } if flags&flagNoCustomFuzz == 0 { // Check for both pointer and non-pointer custom functions. if v.CanAddr() && fc.tryCustom(v.Addr()) { return } if fc.tryCustom(v) { return } } if fn, ok := fillFuncMap[v.Kind()]; ok { fn(v, fc.fuzzer.r) return } switch v.Kind() { case reflect.Map: if fc.fuzzer.genShouldFill() { v.Set(reflect.MakeMap(v.Type())) n := fc.fuzzer.genElementCount() for i := 0; i < n; i++ { key := reflect.New(v.Type().Key()).Elem() fc.doFuzz(key, 0) val := reflect.New(v.Type().Elem()).Elem() fc.doFuzz(val, 0) v.SetMapIndex(key, val) } return } v.Set(reflect.Zero(v.Type())) case reflect.Ptr: if fc.fuzzer.genShouldFill() { v.Set(reflect.New(v.Type().Elem())) fc.doFuzz(v.Elem(), 0) return } v.Set(reflect.Zero(v.Type())) case reflect.Slice: if fc.fuzzer.genShouldFill() { n := fc.fuzzer.genElementCount() v.Set(reflect.MakeSlice(v.Type(), n, n)) for i := 0; i < n; i++ { fc.doFuzz(v.Index(i), 0) } return } v.Set(reflect.Zero(v.Type())) case reflect.Array: if fc.fuzzer.genShouldFill() { n := v.Len() for i := 0; i < n; i++ { fc.doFuzz(v.Index(i), 0) } return } v.Set(reflect.Zero(v.Type())) case reflect.Struct: for i := 0; i < v.NumField(); i++ { skipField := false fieldName := v.Type().Field(i).Name for _, pattern := range fc.fuzzer.skipFieldPatterns { if pattern.MatchString(fieldName) { skipField = true break } } if !skipField { fc.doFuzz(v.Field(i), 0) } } case reflect.Chan: fallthrough case reflect.Func: fallthrough case reflect.Interface: fallthrough default: panic(fmt.Sprintf("Can't handle %#v", v.Interface())) } } // tryCustom searches for custom handlers, and returns true iff it finds a match // and successfully randomizes v. func (fc *fuzzerContext) tryCustom(v reflect.Value) bool { // First: see if we have a fuzz function for it. doCustom, ok := fc.fuzzer.fuzzFuncs[v.Type()] if !ok { // Second: see if it can fuzz itself. if v.CanInterface() { intf := v.Interface() if fuzzable, ok := intf.(Interface); ok { fuzzable.Fuzz(Continue{fc: fc, Rand: fc.fuzzer.r}) return true } } // Finally: see if there is a default fuzz function. doCustom, ok = fc.fuzzer.defaultFuzzFuncs[v.Type()] if !ok { return false } } switch v.Kind() { case reflect.Ptr: if v.IsNil() { if !v.CanSet() { return false } v.Set(reflect.New(v.Type().Elem())) } case reflect.Map: if v.IsNil() { if !v.CanSet() { return false } v.Set(reflect.MakeMap(v.Type())) } default: return false } doCustom.Call([]reflect.Value{v, reflect.ValueOf(Continue{ fc: fc, Rand: fc.fuzzer.r, })}) return true } // Interface represents an object that knows how to fuzz itself. Any time we // find a type that implements this interface we will delegate the act of // fuzzing itself. type Interface interface { Fuzz(c Continue) } // Continue can be passed to custom fuzzing functions to allow them to use // the correct source of randomness and to continue fuzzing their members. type Continue struct { fc *fuzzerContext // For convenience, Continue implements rand.Rand via embedding. // Use this for generating any randomness if you want your fuzzing // to be repeatable for a given seed. *rand.Rand } // Fuzz continues fuzzing obj. obj must be a pointer. func (c Continue) Fuzz(obj interface{}) { v := reflect.ValueOf(obj) if v.Kind() != reflect.Ptr { panic("needed ptr!") } v = v.Elem() c.fc.doFuzz(v, 0) } // FuzzNoCustom continues fuzzing obj, except that any custom fuzz function for // obj's type will not be called and obj will not be tested for fuzz.Interface // conformance. This applies only to obj and not other instances of obj's // type. func (c Continue) FuzzNoCustom(obj interface{}) { v := reflect.ValueOf(obj) if v.Kind() != reflect.Ptr { panic("needed ptr!") } v = v.Elem() c.fc.doFuzz(v, flagNoCustomFuzz) } // RandString makes a random string up to 20 characters long. The returned string // may include a variety of (valid) UTF-8 encodings. func (c Continue) RandString() string { return randString(c.Rand) } // RandUint64 makes random 64 bit numbers. // Weirdly, rand doesn't have a function that gives you 64 random bits. func (c Continue) RandUint64() uint64 { return randUint64(c.Rand) } // RandBool returns true or false randomly. func (c Continue) RandBool() bool { return randBool(c.Rand) } func fuzzInt(v reflect.Value, r *rand.Rand) { v.SetInt(int64(randUint64(r))) } func fuzzUint(v reflect.Value, r *rand.Rand) { v.SetUint(randUint64(r)) } func fuzzTime(t *time.Time, c Continue) { var sec, nsec int64 // Allow for about 1000 years of random time values, which keeps things // like JSON parsing reasonably happy. sec = c.Rand.Int63n(1000 * 365 * 24 * 60 * 60) c.Fuzz(&nsec) *t = time.Unix(sec, nsec) } var fillFuncMap = map[reflect.Kind]func(reflect.Value, *rand.Rand){ reflect.Bool: func(v reflect.Value, r *rand.Rand) { v.SetBool(randBool(r)) }, reflect.Int: fuzzInt, reflect.Int8: fuzzInt, reflect.Int16: fuzzInt, reflect.Int32: fuzzInt, reflect.Int64: fuzzInt, reflect.Uint: fuzzUint, reflect.Uint8: fuzzUint, reflect.Uint16: fuzzUint, reflect.Uint32: fuzzUint, reflect.Uint64: fuzzUint, reflect.Uintptr: fuzzUint, reflect.Float32: func(v reflect.Value, r *rand.Rand) { v.SetFloat(float64(r.Float32())) }, reflect.Float64: func(v reflect.Value, r *rand.Rand) { v.SetFloat(r.Float64()) }, reflect.Complex64: func(v reflect.Value, r *rand.Rand) { v.SetComplex(complex128(complex(r.Float32(), r.Float32()))) }, reflect.Complex128: func(v reflect.Value, r *rand.Rand) { v.SetComplex(complex(r.Float64(), r.Float64())) }, reflect.String: func(v reflect.Value, r *rand.Rand) { v.SetString(randString(r)) }, reflect.UnsafePointer: func(v reflect.Value, r *rand.Rand) { panic("unimplemented") }, } // randBool returns true or false randomly. func randBool(r *rand.Rand) bool { return r.Int31()&(1<<30) == 0 } type int63nPicker interface { Int63n(int64) int64 } // UnicodeRange describes a sequential range of unicode characters. // Last must be numerically greater than First. type UnicodeRange struct { First, Last rune } // UnicodeRanges describes an arbitrary number of sequential ranges of unicode characters. // To be useful, each range must have at least one character (First <= Last) and // there must be at least one range. type UnicodeRanges []UnicodeRange // choose returns a random unicode character from the given range, using the // given randomness source. func (ur UnicodeRange) choose(r int63nPicker) rune { count := int64(ur.Last - ur.First + 1) return ur.First + rune(r.Int63n(count)) } // CustomStringFuzzFunc constructs a FuzzFunc which produces random strings. // Each character is selected from the range ur. If there are no characters // in the range (cr.Last < cr.First), this will panic. func (ur UnicodeRange) CustomStringFuzzFunc() func(s *string, c Continue) { ur.check() return func(s *string, c Continue) { *s = ur.randString(c.Rand) } } // check is a function that used to check whether the first of ur(UnicodeRange) // is greater than the last one. func (ur UnicodeRange) check() { if ur.Last < ur.First { panic("The last encoding must be greater than the first one.") } } // randString of UnicodeRange makes a random string up to 20 characters long. // Each character is selected form ur(UnicodeRange). func (ur UnicodeRange) randString(r *rand.Rand) string { n := r.Intn(20) sb := strings.Builder{} sb.Grow(n) for i := 0; i < n; i++ { sb.WriteRune(ur.choose(r)) } return sb.String() } // defaultUnicodeRanges sets a default unicode range when user do not set // CustomStringFuzzFunc() but wants fuzz string. var defaultUnicodeRanges = UnicodeRanges{ {' ', '~'}, // ASCII characters {'\u00a0', '\u02af'}, // Multi-byte encoded characters {'\u4e00', '\u9fff'}, // Common CJK (even longer encodings) } // CustomStringFuzzFunc constructs a FuzzFunc which produces random strings. // Each character is selected from one of the ranges of ur(UnicodeRanges). // Each range has an equal probability of being chosen. If there are no ranges, // or a selected range has no characters (.Last < .First), this will panic. // Do not modify any of the ranges in ur after calling this function. func (ur UnicodeRanges) CustomStringFuzzFunc() func(s *string, c Continue) { // Check unicode ranges slice is empty. if len(ur) == 0 { panic("UnicodeRanges is empty.") } // if not empty, each range should be checked. for i := range ur { ur[i].check() } return func(s *string, c Continue) { *s = ur.randString(c.Rand) } } // randString of UnicodeRanges makes a random string up to 20 characters long. // Each character is selected form one of the ranges of ur(UnicodeRanges), // and each range has an equal probability of being chosen. func (ur UnicodeRanges) randString(r *rand.Rand) string { n := r.Intn(20) sb := strings.Builder{} sb.Grow(n) for i := 0; i < n; i++ { sb.WriteRune(ur[r.Intn(len(ur))].choose(r)) } return sb.String() } // randString makes a random string up to 20 characters long. The returned string // may include a variety of (valid) UTF-8 encodings. func randString(r *rand.Rand) string { return defaultUnicodeRanges.randString(r) } // randUint64 makes random 64 bit numbers. // Weirdly, rand doesn't have a function that gives you 64 random bits. func randUint64(r *rand.Rand) uint64 { return uint64(r.Uint32())<<32 | uint64(r.Uint32()) } ================================================ FILE: vendor/github.com/google/shlex/COPYING ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/google/shlex/README ================================================ go-shlex is a simple lexer for go that supports shell-style quoting, commenting, and escaping. ================================================ FILE: vendor/github.com/google/shlex/shlex.go ================================================ /* Copyright 2012 Google Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* Package shlex implements a simple lexer which splits input in to tokens using shell-style rules for quoting and commenting. The basic use case uses the default ASCII lexer to split a string into sub-strings: shlex.Split("one \"two three\" four") -> []string{"one", "two three", "four"} To process a stream of strings: l := NewLexer(os.Stdin) for ; token, err := l.Next(); err != nil { // process token } To access the raw token stream (which includes tokens for comments): t := NewTokenizer(os.Stdin) for ; token, err := t.Next(); err != nil { // process token } */ package shlex import ( "bufio" "fmt" "io" "strings" ) // TokenType is a top-level token classification: A word, space, comment, unknown. type TokenType int // runeTokenClass is the type of a UTF-8 character classification: A quote, space, escape. type runeTokenClass int // the internal state used by the lexer state machine type lexerState int // Token is a (type, value) pair representing a lexographical token. type Token struct { tokenType TokenType value string } // Equal reports whether tokens a, and b, are equal. // Two tokens are equal if both their types and values are equal. A nil token can // never be equal to another token. func (a *Token) Equal(b *Token) bool { if a == nil || b == nil { return false } if a.tokenType != b.tokenType { return false } return a.value == b.value } // Named classes of UTF-8 runes const ( spaceRunes = " \t\r\n" escapingQuoteRunes = `"` nonEscapingQuoteRunes = "'" escapeRunes = `\` commentRunes = "#" ) // Classes of rune token const ( unknownRuneClass runeTokenClass = iota spaceRuneClass escapingQuoteRuneClass nonEscapingQuoteRuneClass escapeRuneClass commentRuneClass eofRuneClass ) // Classes of lexographic token const ( UnknownToken TokenType = iota WordToken SpaceToken CommentToken ) // Lexer state machine states const ( startState lexerState = iota // no runes have been seen inWordState // processing regular runes in a word escapingState // we have just consumed an escape rune; the next rune is literal escapingQuotedState // we have just consumed an escape rune within a quoted string quotingEscapingState // we are within a quoted string that supports escaping ("...") quotingState // we are within a string that does not support escaping ('...') commentState // we are within a comment (everything following an unquoted or unescaped # ) // tokenClassifier is used for classifying rune characters. type tokenClassifier map[rune]runeTokenClass func (typeMap tokenClassifier) addRuneClass(runes string, tokenType runeTokenClass) { for _, runeChar := range runes { typeMap[runeChar] = tokenType } } // newDefaultClassifier creates a new classifier for ASCII characters. func newDefaultClassifier() tokenClassifier { t := tokenClassifier{} t.addRuneClass(spaceRunes, spaceRuneClass) t.addRuneClass(escapingQuoteRunes, escapingQuoteRuneClass) t.addRuneClass(nonEscapingQuoteRunes, nonEscapingQuoteRuneClass) t.addRuneClass(escapeRunes, escapeRuneClass) t.addRuneClass(commentRunes, commentRuneClass) return t } // ClassifyRune classifiees a rune func (t tokenClassifier) ClassifyRune(runeVal rune) runeTokenClass { return t[runeVal] } // Lexer turns an input stream into a sequence of tokens. Whitespace and comments are skipped. type Lexer Tokenizer // NewLexer creates a new lexer from an input stream. func NewLexer(r io.Reader) *Lexer { return (*Lexer)(NewTokenizer(r)) } // Next returns the next word, or an error. If there are no more words, // the error will be io.EOF. func (l *Lexer) Next() (string, error) { for { token, err := (*Tokenizer)(l).Next() if err != nil { return "", err } switch token.tokenType { case WordToken: return token.value, nil case CommentToken: // skip comments default: return "", fmt.Errorf("Unknown token type: %v", token.tokenType) } } } // Tokenizer turns an input stream into a sequence of typed tokens type Tokenizer struct { input bufio.Reader classifier tokenClassifier } // NewTokenizer creates a new tokenizer from an input stream. func NewTokenizer(r io.Reader) *Tokenizer { input := bufio.NewReader(r) classifier := newDefaultClassifier() return &Tokenizer{ input: *input, classifier: classifier} } // scanStream scans the stream for the next token using the internal state machine. // It will panic if it encounters a rune which it does not know how to handle. func (t *Tokenizer) scanStream() (*Token, error) { state := startState var tokenType TokenType var value []rune var nextRune rune var nextRuneType runeTokenClass var err error for { nextRune, _, err = t.input.ReadRune() nextRuneType = t.classifier.ClassifyRune(nextRune) if err == io.EOF { nextRuneType = eofRuneClass err = nil } else if err != nil { return nil, err } switch state { case startState: // no runes read yet { switch nextRuneType { case eofRuneClass: { return nil, io.EOF } case spaceRuneClass: { } case escapingQuoteRuneClass: { tokenType = WordToken state = quotingEscapingState } case nonEscapingQuoteRuneClass: { tokenType = WordToken state = quotingState } case escapeRuneClass: { tokenType = WordToken state = escapingState } case commentRuneClass: { tokenType = CommentToken state = commentState } default: { tokenType = WordToken value = append(value, nextRune) state = inWordState } } } case inWordState: // in a regular word { switch nextRuneType { case eofRuneClass: { token := &Token{ tokenType: tokenType, value: string(value)} return token, err } case spaceRuneClass: { token := &Token{ tokenType: tokenType, value: string(value)} return token, err } case escapingQuoteRuneClass: { state = quotingEscapingState } case nonEscapingQuoteRuneClass: { state = quotingState } case escapeRuneClass: { state = escapingState } default: { value = append(value, nextRune) } } } case escapingState: // the rune after an escape character { switch nextRuneType { case eofRuneClass: { err = fmt.Errorf("EOF found after escape character") token := &Token{ tokenType: tokenType, value: string(value)} return token, err } default: { state = inWordState value = append(value, nextRune) } } } case escapingQuotedState: // the next rune after an escape character, in double quotes { switch nextRuneType { case eofRuneClass: { err = fmt.Errorf("EOF found after escape character") token := &Token{ tokenType: tokenType, value: string(value)} return token, err } default: { state = quotingEscapingState value = append(value, nextRune) } } } case quotingEscapingState: // in escaping double quotes { switch nextRuneType { case eofRuneClass: { err = fmt.Errorf("EOF found when expecting closing quote") token := &Token{ tokenType: tokenType, value: string(value)} return token, err } case escapingQuoteRuneClass: { state = inWordState } case escapeRuneClass: { state = escapingQuotedState } default: { value = append(value, nextRune) } } } case quotingState: // in non-escaping single quotes { switch nextRuneType { case eofRuneClass: { err = fmt.Errorf("EOF found when expecting closing quote") token := &Token{ tokenType: tokenType, value: string(value)} return token, err } case nonEscapingQuoteRuneClass: { state = inWordState } default: { value = append(value, nextRune) } } } case commentState: // in a comment { switch nextRuneType { case eofRuneClass: { token := &Token{ tokenType: tokenType, value: string(value)} return token, err } case spaceRuneClass: { if nextRune == '\n' { state = startState token := &Token{ tokenType: tokenType, value: string(value)} return token, err } else { value = append(value, nextRune) } } default: { value = append(value, nextRune) } } } default: { return nil, fmt.Errorf("Unexpected state: %v", state) } } } } // Next returns the next token in the stream. func (t *Tokenizer) Next() (*Token, error) { return t.scanStream() } // Split partitions a string into a slice of strings. func Split(s string) ([]string, error) { l := NewLexer(strings.NewReader(s)) subStrings := make([]string, 0) for { word, err := l.Next() if err != nil { if err == io.EOF { return subStrings, nil } return subStrings, err } subStrings = append(subStrings, word) } } ================================================ FILE: vendor/github.com/google/uuid/.travis.yml ================================================ language: go go: - 1.4.3 - 1.5.3 - tip script: - go test -v ./... ================================================ FILE: vendor/github.com/google/uuid/CONTRIBUTING.md ================================================ # How to contribute We definitely welcome patches and contribution to this project! ### Legal requirements In order to protect both you and ourselves, you will need to sign the [Contributor License Agreement](https://cla.developers.google.com/clas). You may have already signed it for other Google projects. ================================================ FILE: vendor/github.com/google/uuid/CONTRIBUTORS ================================================ Paul Borman bmatsuo shawnps theory jboverfelt dsymonds cd1 wallclockbuilder dansouza ================================================ FILE: vendor/github.com/google/uuid/LICENSE ================================================ Copyright (c) 2009,2014 Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/google/uuid/README.md ================================================ # uuid ![build status](https://travis-ci.org/google/uuid.svg?branch=master) The uuid package generates and inspects UUIDs based on [RFC 4122](http://tools.ietf.org/html/rfc4122) and DCE 1.1: Authentication and Security Services. This package is based on the github.com/pborman/uuid package (previously named code.google.com/p/go-uuid). It differs from these earlier packages in that a UUID is a 16 byte array rather than a byte slice. One loss due to this change is the ability to represent an invalid UUID (vs a NIL UUID). ###### Install `go get github.com/google/uuid` ###### Documentation [![GoDoc](https://godoc.org/github.com/google/uuid?status.svg)](http://godoc.org/github.com/google/uuid) Full `go doc` style documentation for the package can be viewed online without installing this package by using the GoDoc site here: http://pkg.go.dev/github.com/google/uuid ================================================ FILE: vendor/github.com/google/uuid/dce.go ================================================ // Copyright 2016 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package uuid import ( "encoding/binary" "fmt" "os" ) // A Domain represents a Version 2 domain type Domain byte // Domain constants for DCE Security (Version 2) UUIDs. const ( Person = Domain(0) Group = Domain(1) Org = Domain(2) ) // NewDCESecurity returns a DCE Security (Version 2) UUID. // // The domain should be one of Person, Group or Org. // On a POSIX system the id should be the users UID for the Person // domain and the users GID for the Group. The meaning of id for // the domain Org or on non-POSIX systems is site defined. // // For a given domain/id pair the same token may be returned for up to // 7 minutes and 10 seconds. func NewDCESecurity(domain Domain, id uint32) (UUID, error) { uuid, err := NewUUID() if err == nil { uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2 uuid[9] = byte(domain) binary.BigEndian.PutUint32(uuid[0:], id) } return uuid, err } // NewDCEPerson returns a DCE Security (Version 2) UUID in the person // domain with the id returned by os.Getuid. // // NewDCESecurity(Person, uint32(os.Getuid())) func NewDCEPerson() (UUID, error) { return NewDCESecurity(Person, uint32(os.Getuid())) } // NewDCEGroup returns a DCE Security (Version 2) UUID in the group // domain with the id returned by os.Getgid. // // NewDCESecurity(Group, uint32(os.Getgid())) func NewDCEGroup() (UUID, error) { return NewDCESecurity(Group, uint32(os.Getgid())) } // Domain returns the domain for a Version 2 UUID. Domains are only defined // for Version 2 UUIDs. func (uuid UUID) Domain() Domain { return Domain(uuid[9]) } // ID returns the id for a Version 2 UUID. IDs are only defined for Version 2 // UUIDs. func (uuid UUID) ID() uint32 { return binary.BigEndian.Uint32(uuid[0:4]) } func (d Domain) String() string { switch d { case Person: return "Person" case Group: return "Group" case Org: return "Org" } return fmt.Sprintf("Domain%d", int(d)) } ================================================ FILE: vendor/github.com/google/uuid/doc.go ================================================ // Copyright 2016 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package uuid generates and inspects UUIDs. // // UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security // Services. // // A UUID is a 16 byte (128 bit) array. UUIDs may be used as keys to // maps or compared directly. package uuid ================================================ FILE: vendor/github.com/google/uuid/hash.go ================================================ // Copyright 2016 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package uuid import ( "crypto/md5" "crypto/sha1" "hash" ) // Well known namespace IDs and UUIDs var ( NameSpaceDNS = Must(Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8")) NameSpaceURL = Must(Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8")) NameSpaceOID = Must(Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8")) NameSpaceX500 = Must(Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8")) Nil UUID // empty UUID, all zeros ) // NewHash returns a new UUID derived from the hash of space concatenated with // data generated by h. The hash should be at least 16 byte in length. The // first 16 bytes of the hash are used to form the UUID. The version of the // UUID will be the lower 4 bits of version. NewHash is used to implement // NewMD5 and NewSHA1. func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID { h.Reset() h.Write(space[:]) //nolint:errcheck h.Write(data) //nolint:errcheck s := h.Sum(nil) var uuid UUID copy(uuid[:], s) uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4) uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant return uuid } // NewMD5 returns a new MD5 (Version 3) UUID based on the // supplied name space and data. It is the same as calling: // // NewHash(md5.New(), space, data, 3) func NewMD5(space UUID, data []byte) UUID { return NewHash(md5.New(), space, data, 3) } // NewSHA1 returns a new SHA1 (Version 5) UUID based on the // supplied name space and data. It is the same as calling: // // NewHash(sha1.New(), space, data, 5) func NewSHA1(space UUID, data []byte) UUID { return NewHash(sha1.New(), space, data, 5) } ================================================ FILE: vendor/github.com/google/uuid/marshal.go ================================================ // Copyright 2016 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package uuid import "fmt" // MarshalText implements encoding.TextMarshaler. func (uuid UUID) MarshalText() ([]byte, error) { var js [36]byte encodeHex(js[:], uuid) return js[:], nil } // UnmarshalText implements encoding.TextUnmarshaler. func (uuid *UUID) UnmarshalText(data []byte) error { id, err := ParseBytes(data) if err != nil { return err } *uuid = id return nil } // MarshalBinary implements encoding.BinaryMarshaler. func (uuid UUID) MarshalBinary() ([]byte, error) { return uuid[:], nil } // UnmarshalBinary implements encoding.BinaryUnmarshaler. func (uuid *UUID) UnmarshalBinary(data []byte) error { if len(data) != 16 { return fmt.Errorf("invalid UUID (got %d bytes)", len(data)) } copy(uuid[:], data) return nil } ================================================ FILE: vendor/github.com/google/uuid/node.go ================================================ // Copyright 2016 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package uuid import ( "sync" ) var ( nodeMu sync.Mutex ifname string // name of interface being used nodeID [6]byte // hardware for version 1 UUIDs zeroID [6]byte // nodeID with only 0's ) // NodeInterface returns the name of the interface from which the NodeID was // derived. The interface "user" is returned if the NodeID was set by // SetNodeID. func NodeInterface() string { defer nodeMu.Unlock() nodeMu.Lock() return ifname } // SetNodeInterface selects the hardware address to be used for Version 1 UUIDs. // If name is "" then the first usable interface found will be used or a random // Node ID will be generated. If a named interface cannot be found then false // is returned. // // SetNodeInterface never fails when name is "". func SetNodeInterface(name string) bool { defer nodeMu.Unlock() nodeMu.Lock() return setNodeInterface(name) } func setNodeInterface(name string) bool { iname, addr := getHardwareInterface(name) // null implementation for js if iname != "" && addr != nil { ifname = iname copy(nodeID[:], addr) return true } // We found no interfaces with a valid hardware address. If name // does not specify a specific interface generate a random Node ID // (section 4.1.6) if name == "" { ifname = "random" randomBits(nodeID[:]) return true } return false } // NodeID returns a slice of a copy of the current Node ID, setting the Node ID // if not already set. func NodeID() []byte { defer nodeMu.Unlock() nodeMu.Lock() if nodeID == zeroID { setNodeInterface("") } nid := nodeID return nid[:] } // SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes // of id are used. If id is less than 6 bytes then false is returned and the // Node ID is not set. func SetNodeID(id []byte) bool { if len(id) < 6 { return false } defer nodeMu.Unlock() nodeMu.Lock() copy(nodeID[:], id) ifname = "user" return true } // NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is // not valid. The NodeID is only well defined for version 1 and 2 UUIDs. func (uuid UUID) NodeID() []byte { var node [6]byte copy(node[:], uuid[10:]) return node[:] } ================================================ FILE: vendor/github.com/google/uuid/node_js.go ================================================ // Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build js package uuid // getHardwareInterface returns nil values for the JS version of the code. // This remvoves the "net" dependency, because it is not used in the browser. // Using the "net" library inflates the size of the transpiled JS code by 673k bytes. func getHardwareInterface(name string) (string, []byte) { return "", nil } ================================================ FILE: vendor/github.com/google/uuid/node_net.go ================================================ // Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build !js package uuid import "net" var interfaces []net.Interface // cached list of interfaces // getHardwareInterface returns the name and hardware address of interface name. // If name is "" then the name and hardware address of one of the system's // interfaces is returned. If no interfaces are found (name does not exist or // there are no interfaces) then "", nil is returned. // // Only addresses of at least 6 bytes are returned. func getHardwareInterface(name string) (string, []byte) { if interfaces == nil { var err error interfaces, err = net.Interfaces() if err != nil { return "", nil } } for _, ifs := range interfaces { if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) { return ifs.Name, ifs.HardwareAddr } } return "", nil } ================================================ FILE: vendor/github.com/google/uuid/null.go ================================================ // Copyright 2021 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package uuid import ( "bytes" "database/sql/driver" "encoding/json" "fmt" ) var jsonNull = []byte("null") // NullUUID represents a UUID that may be null. // NullUUID implements the SQL driver.Scanner interface so // it can be used as a scan destination: // // var u uuid.NullUUID // err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&u) // ... // if u.Valid { // // use u.UUID // } else { // // NULL value // } // type NullUUID struct { UUID UUID Valid bool // Valid is true if UUID is not NULL } // Scan implements the SQL driver.Scanner interface. func (nu *NullUUID) Scan(value interface{}) error { if value == nil { nu.UUID, nu.Valid = Nil, false return nil } err := nu.UUID.Scan(value) if err != nil { nu.Valid = false return err } nu.Valid = true return nil } // Value implements the driver Valuer interface. func (nu NullUUID) Value() (driver.Value, error) { if !nu.Valid { return nil, nil } // Delegate to UUID Value function return nu.UUID.Value() } // MarshalBinary implements encoding.BinaryMarshaler. func (nu NullUUID) MarshalBinary() ([]byte, error) { if nu.Valid { return nu.UUID[:], nil } return []byte(nil), nil } // UnmarshalBinary implements encoding.BinaryUnmarshaler. func (nu *NullUUID) UnmarshalBinary(data []byte) error { if len(data) != 16 { return fmt.Errorf("invalid UUID (got %d bytes)", len(data)) } copy(nu.UUID[:], data) nu.Valid = true return nil } // MarshalText implements encoding.TextMarshaler. func (nu NullUUID) MarshalText() ([]byte, error) { if nu.Valid { return nu.UUID.MarshalText() } return jsonNull, nil } // UnmarshalText implements encoding.TextUnmarshaler. func (nu *NullUUID) UnmarshalText(data []byte) error { id, err := ParseBytes(data) if err != nil { nu.Valid = false return err } nu.UUID = id nu.Valid = true return nil } // MarshalJSON implements json.Marshaler. func (nu NullUUID) MarshalJSON() ([]byte, error) { if nu.Valid { return json.Marshal(nu.UUID) } return jsonNull, nil } // UnmarshalJSON implements json.Unmarshaler. func (nu *NullUUID) UnmarshalJSON(data []byte) error { if bytes.Equal(data, jsonNull) { *nu = NullUUID{} return nil // valid null UUID } err := json.Unmarshal(data, &nu.UUID) nu.Valid = err == nil return err } ================================================ FILE: vendor/github.com/google/uuid/sql.go ================================================ // Copyright 2016 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package uuid import ( "database/sql/driver" "fmt" ) // Scan implements sql.Scanner so UUIDs can be read from databases transparently. // Currently, database types that map to string and []byte are supported. Please // consult database-specific driver documentation for matching types. func (uuid *UUID) Scan(src interface{}) error { switch src := src.(type) { case nil: return nil case string: // if an empty UUID comes from a table, we return a null UUID if src == "" { return nil } // see Parse for required string format u, err := Parse(src) if err != nil { return fmt.Errorf("Scan: %v", err) } *uuid = u case []byte: // if an empty UUID comes from a table, we return a null UUID if len(src) == 0 { return nil } // assumes a simple slice of bytes if 16 bytes // otherwise attempts to parse if len(src) != 16 { return uuid.Scan(string(src)) } copy((*uuid)[:], src) default: return fmt.Errorf("Scan: unable to scan type %T into UUID", src) } return nil } // Value implements sql.Valuer so that UUIDs can be written to databases // transparently. Currently, UUIDs map to strings. Please consult // database-specific driver documentation for matching types. func (uuid UUID) Value() (driver.Value, error) { return uuid.String(), nil } ================================================ FILE: vendor/github.com/google/uuid/time.go ================================================ // Copyright 2016 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package uuid import ( "encoding/binary" "sync" "time" ) // A Time represents a time as the number of 100's of nanoseconds since 15 Oct // 1582. type Time int64 const ( lillian = 2299160 // Julian day of 15 Oct 1582 unix = 2440587 // Julian day of 1 Jan 1970 epoch = unix - lillian // Days between epochs g1582 = epoch * 86400 // seconds between epochs g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs ) var ( timeMu sync.Mutex lasttime uint64 // last time we returned clockSeq uint16 // clock sequence for this run timeNow = time.Now // for testing ) // UnixTime converts t the number of seconds and nanoseconds using the Unix // epoch of 1 Jan 1970. func (t Time) UnixTime() (sec, nsec int64) { sec = int64(t - g1582ns100) nsec = (sec % 10000000) * 100 sec /= 10000000 return sec, nsec } // GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and // clock sequence as well as adjusting the clock sequence as needed. An error // is returned if the current time cannot be determined. func GetTime() (Time, uint16, error) { defer timeMu.Unlock() timeMu.Lock() return getTime() } func getTime() (Time, uint16, error) { t := timeNow() // If we don't have a clock sequence already, set one. if clockSeq == 0 { setClockSequence(-1) } now := uint64(t.UnixNano()/100) + g1582ns100 // If time has gone backwards with this clock sequence then we // increment the clock sequence if now <= lasttime { clockSeq = ((clockSeq + 1) & 0x3fff) | 0x8000 } lasttime = now return Time(now), clockSeq, nil } // ClockSequence returns the current clock sequence, generating one if not // already set. The clock sequence is only used for Version 1 UUIDs. // // The uuid package does not use global static storage for the clock sequence or // the last time a UUID was generated. Unless SetClockSequence is used, a new // random clock sequence is generated the first time a clock sequence is // requested by ClockSequence, GetTime, or NewUUID. (section 4.2.1.1) func ClockSequence() int { defer timeMu.Unlock() timeMu.Lock() return clockSequence() } func clockSequence() int { if clockSeq == 0 { setClockSequence(-1) } return int(clockSeq & 0x3fff) } // SetClockSequence sets the clock sequence to the lower 14 bits of seq. Setting to // -1 causes a new sequence to be generated. func SetClockSequence(seq int) { defer timeMu.Unlock() timeMu.Lock() setClockSequence(seq) } func setClockSequence(seq int) { if seq == -1 { var b [2]byte randomBits(b[:]) // clock sequence seq = int(b[0])<<8 | int(b[1]) } oldSeq := clockSeq clockSeq = uint16(seq&0x3fff) | 0x8000 // Set our variant if oldSeq != clockSeq { lasttime = 0 } } // Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in // uuid. The time is only defined for version 1 and 2 UUIDs. func (uuid UUID) Time() Time { time := int64(binary.BigEndian.Uint32(uuid[0:4])) time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32 time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48 return Time(time) } // ClockSequence returns the clock sequence encoded in uuid. // The clock sequence is only well defined for version 1 and 2 UUIDs. func (uuid UUID) ClockSequence() int { return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff } ================================================ FILE: vendor/github.com/google/uuid/util.go ================================================ // Copyright 2016 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package uuid import ( "io" ) // randomBits completely fills slice b with random data. func randomBits(b []byte) { if _, err := io.ReadFull(rander, b); err != nil { panic(err.Error()) // rand should never fail } } // xvalues returns the value of a byte as a hexadecimal digit or 255. var xvalues = [256]byte{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, } // xtob converts hex characters x1 and x2 into a byte. func xtob(x1, x2 byte) (byte, bool) { b1 := xvalues[x1] b2 := xvalues[x2] return (b1 << 4) | b2, b1 != 255 && b2 != 255 } ================================================ FILE: vendor/github.com/google/uuid/uuid.go ================================================ // Copyright 2018 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package uuid import ( "bytes" "crypto/rand" "encoding/hex" "errors" "fmt" "io" "strings" "sync" ) // A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC // 4122. type UUID [16]byte // A Version represents a UUID's version. type Version byte // A Variant represents a UUID's variant. type Variant byte // Constants returned by Variant. const ( Invalid = Variant(iota) // Invalid UUID RFC4122 // The variant specified in RFC4122 Reserved // Reserved, NCS backward compatibility. Microsoft // Reserved, Microsoft Corporation backward compatibility. Future // Reserved for future definition. ) const randPoolSize = 16 * 16 var ( rander = rand.Reader // random function poolEnabled = false poolMu sync.Mutex poolPos = randPoolSize // protected with poolMu pool [randPoolSize]byte // protected with poolMu ) type invalidLengthError struct{ len int } func (err invalidLengthError) Error() string { return fmt.Sprintf("invalid UUID length: %d", err.len) } // IsInvalidLengthError is matcher function for custom error invalidLengthError func IsInvalidLengthError(err error) bool { _, ok := err.(invalidLengthError) return ok } // Parse decodes s into a UUID or returns an error. Both the standard UUID // forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the // Microsoft encoding {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} and the raw hex // encoding: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx. func Parse(s string) (UUID, error) { var uuid UUID switch len(s) { // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx case 36: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx case 36 + 9: if strings.ToLower(s[:9]) != "urn:uuid:" { return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9]) } s = s[9:] // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} case 36 + 2: s = s[1:] // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx case 32: var ok bool for i := range uuid { uuid[i], ok = xtob(s[i*2], s[i*2+1]) if !ok { return uuid, errors.New("invalid UUID format") } } return uuid, nil default: return uuid, invalidLengthError{len(s)} } // s is now at least 36 bytes long // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { return uuid, errors.New("invalid UUID format") } for i, x := range [16]int{ 0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34} { v, ok := xtob(s[x], s[x+1]) if !ok { return uuid, errors.New("invalid UUID format") } uuid[i] = v } return uuid, nil } // ParseBytes is like Parse, except it parses a byte slice instead of a string. func ParseBytes(b []byte) (UUID, error) { var uuid UUID switch len(b) { case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx if !bytes.Equal(bytes.ToLower(b[:9]), []byte("urn:uuid:")) { return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9]) } b = b[9:] case 36 + 2: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} b = b[1:] case 32: // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx var ok bool for i := 0; i < 32; i += 2 { uuid[i/2], ok = xtob(b[i], b[i+1]) if !ok { return uuid, errors.New("invalid UUID format") } } return uuid, nil default: return uuid, invalidLengthError{len(b)} } // s is now at least 36 bytes long // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' { return uuid, errors.New("invalid UUID format") } for i, x := range [16]int{ 0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34} { v, ok := xtob(b[x], b[x+1]) if !ok { return uuid, errors.New("invalid UUID format") } uuid[i] = v } return uuid, nil } // MustParse is like Parse but panics if the string cannot be parsed. // It simplifies safe initialization of global variables holding compiled UUIDs. func MustParse(s string) UUID { uuid, err := Parse(s) if err != nil { panic(`uuid: Parse(` + s + `): ` + err.Error()) } return uuid } // FromBytes creates a new UUID from a byte slice. Returns an error if the slice // does not have a length of 16. The bytes are copied from the slice. func FromBytes(b []byte) (uuid UUID, err error) { err = uuid.UnmarshalBinary(b) return uuid, err } // Must returns uuid if err is nil and panics otherwise. func Must(uuid UUID, err error) UUID { if err != nil { panic(err) } return uuid } // String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx // , or "" if uuid is invalid. func (uuid UUID) String() string { var buf [36]byte encodeHex(buf[:], uuid) return string(buf[:]) } // URN returns the RFC 2141 URN form of uuid, // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid. func (uuid UUID) URN() string { var buf [36 + 9]byte copy(buf[:], "urn:uuid:") encodeHex(buf[9:], uuid) return string(buf[:]) } func encodeHex(dst []byte, uuid UUID) { hex.Encode(dst, uuid[:4]) dst[8] = '-' hex.Encode(dst[9:13], uuid[4:6]) dst[13] = '-' hex.Encode(dst[14:18], uuid[6:8]) dst[18] = '-' hex.Encode(dst[19:23], uuid[8:10]) dst[23] = '-' hex.Encode(dst[24:], uuid[10:]) } // Variant returns the variant encoded in uuid. func (uuid UUID) Variant() Variant { switch { case (uuid[8] & 0xc0) == 0x80: return RFC4122 case (uuid[8] & 0xe0) == 0xc0: return Microsoft case (uuid[8] & 0xe0) == 0xe0: return Future default: return Reserved } } // Version returns the version of uuid. func (uuid UUID) Version() Version { return Version(uuid[6] >> 4) } func (v Version) String() string { if v > 15 { return fmt.Sprintf("BAD_VERSION_%d", v) } return fmt.Sprintf("VERSION_%d", v) } func (v Variant) String() string { switch v { case RFC4122: return "RFC4122" case Reserved: return "Reserved" case Microsoft: return "Microsoft" case Future: return "Future" case Invalid: return "Invalid" } return fmt.Sprintf("BadVariant%d", int(v)) } // SetRand sets the random number generator to r, which implements io.Reader. // If r.Read returns an error when the package requests random data then // a panic will be issued. // // Calling SetRand with nil sets the random number generator to the default // generator. func SetRand(r io.Reader) { if r == nil { rander = rand.Reader return } rander = r } // EnableRandPool enables internal randomness pool used for Random // (Version 4) UUID generation. The pool contains random bytes read from // the random number generator on demand in batches. Enabling the pool // may improve the UUID generation throughput significantly. // // Since the pool is stored on the Go heap, this feature may be a bad fit // for security sensitive applications. // // Both EnableRandPool and DisableRandPool are not thread-safe and should // only be called when there is no possibility that New or any other // UUID Version 4 generation function will be called concurrently. func EnableRandPool() { poolEnabled = true } // DisableRandPool disables the randomness pool if it was previously // enabled with EnableRandPool. // // Both EnableRandPool and DisableRandPool are not thread-safe and should // only be called when there is no possibility that New or any other // UUID Version 4 generation function will be called concurrently. func DisableRandPool() { poolEnabled = false defer poolMu.Unlock() poolMu.Lock() poolPos = randPoolSize } ================================================ FILE: vendor/github.com/google/uuid/version1.go ================================================ // Copyright 2016 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package uuid import ( "encoding/binary" ) // NewUUID returns a Version 1 UUID based on the current NodeID and clock // sequence, and the current time. If the NodeID has not been set by SetNodeID // or SetNodeInterface then it will be set automatically. If the NodeID cannot // be set NewUUID returns nil. If clock sequence has not been set by // SetClockSequence then it will be set automatically. If GetTime fails to // return the current NewUUID returns nil and an error. // // In most cases, New should be used. func NewUUID() (UUID, error) { var uuid UUID now, seq, err := GetTime() if err != nil { return uuid, err } timeLow := uint32(now & 0xffffffff) timeMid := uint16((now >> 32) & 0xffff) timeHi := uint16((now >> 48) & 0x0fff) timeHi |= 0x1000 // Version 1 binary.BigEndian.PutUint32(uuid[0:], timeLow) binary.BigEndian.PutUint16(uuid[4:], timeMid) binary.BigEndian.PutUint16(uuid[6:], timeHi) binary.BigEndian.PutUint16(uuid[8:], seq) nodeMu.Lock() if nodeID == zeroID { setNodeInterface("") } copy(uuid[10:], nodeID[:]) nodeMu.Unlock() return uuid, nil } ================================================ FILE: vendor/github.com/google/uuid/version4.go ================================================ // Copyright 2016 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package uuid import "io" // New creates a new random UUID or panics. New is equivalent to // the expression // // uuid.Must(uuid.NewRandom()) func New() UUID { return Must(NewRandom()) } // NewString creates a new random UUID and returns it as a string or panics. // NewString is equivalent to the expression // // uuid.New().String() func NewString() string { return Must(NewRandom()).String() } // NewRandom returns a Random (Version 4) UUID. // // The strength of the UUIDs is based on the strength of the crypto/rand // package. // // Uses the randomness pool if it was enabled with EnableRandPool. // // A note about uniqueness derived from the UUID Wikipedia entry: // // Randomly generated UUIDs have 122 random bits. One's annual risk of being // hit by a meteorite is estimated to be one chance in 17 billion, that // means the probability is about 0.00000000006 (6 × 10−11), // equivalent to the odds of creating a few tens of trillions of UUIDs in a // year and having one duplicate. func NewRandom() (UUID, error) { if !poolEnabled { return NewRandomFromReader(rander) } return newRandomFromPool() } // NewRandomFromReader returns a UUID based on bytes read from a given io.Reader. func NewRandomFromReader(r io.Reader) (UUID, error) { var uuid UUID _, err := io.ReadFull(r, uuid[:]) if err != nil { return Nil, err } uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 return uuid, nil } func newRandomFromPool() (UUID, error) { var uuid UUID poolMu.Lock() if poolPos == randPoolSize { _, err := io.ReadFull(rander, pool[:]) if err != nil { poolMu.Unlock() return Nil, err } poolPos = 0 } copy(uuid[:], pool[poolPos:(poolPos+16)]) poolPos += 16 poolMu.Unlock() uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 return uuid, nil } ================================================ FILE: vendor/github.com/gorilla/websocket/.gitignore ================================================ # Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe .idea/ *.iml ================================================ FILE: vendor/github.com/gorilla/websocket/AUTHORS ================================================ # This is the official list of Gorilla WebSocket authors for copyright # purposes. # # Please keep the list sorted. Gary Burd Google LLC (https://opensource.google.com/) Joachim Bauch ================================================ FILE: vendor/github.com/gorilla/websocket/LICENSE ================================================ Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/gorilla/websocket/README.md ================================================ # Gorilla WebSocket [![GoDoc](https://godoc.org/github.com/gorilla/websocket?status.svg)](https://godoc.org/github.com/gorilla/websocket) [![CircleCI](https://circleci.com/gh/gorilla/websocket.svg?style=svg)](https://circleci.com/gh/gorilla/websocket) Gorilla WebSocket is a [Go](http://golang.org/) implementation of the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. ### Documentation * [API Reference](https://pkg.go.dev/github.com/gorilla/websocket?tab=doc) * [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat) * [Command example](https://github.com/gorilla/websocket/tree/master/examples/command) * [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo) * [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch) ### Status The Gorilla WebSocket package provides a complete and tested implementation of the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The package API is stable. ### Installation go get github.com/gorilla/websocket ### Protocol Compliance The Gorilla WebSocket package passes the server tests in the [Autobahn Test Suite](https://github.com/crossbario/autobahn-testsuite) using the application in the [examples/autobahn subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn). ### Gorilla WebSocket compared with other packages
github.com/gorilla golang.org/x/net
RFC 6455 Features
Passes Autobahn Test SuiteYesNo
Receive fragmented messageYesNo, see note 1
Send close messageYesNo
Send pings and receive pongsYesNo
Get the type of a received data messageYesYes, see note 2
Other Features
Compression ExtensionsExperimentalNo
Read message using io.ReaderYesNo, see note 3
Write message using io.WriteCloserYesNo, see note 3
Notes: 1. Large messages are fragmented in [Chrome's new WebSocket implementation](http://www.ietf.org/mail-archive/web/hybi/current/msg10503.html). 2. The application can get the type of a received data message by implementing a [Codec marshal](http://godoc.org/golang.org/x/net/websocket#Codec.Marshal) function. 3. The go.net io.Reader and io.Writer operate across WebSocket frame boundaries. Read returns when the input buffer is full or a frame boundary is encountered. Each call to Write sends a single frame message. The Gorilla io.Reader and io.WriteCloser operate on a single WebSocket message. ================================================ FILE: vendor/github.com/gorilla/websocket/client.go ================================================ // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package websocket import ( "bytes" "context" "crypto/tls" "errors" "io" "io/ioutil" "net" "net/http" "net/http/httptrace" "net/url" "strings" "time" ) // ErrBadHandshake is returned when the server response to opening handshake is // invalid. var ErrBadHandshake = errors.New("websocket: bad handshake") var errInvalidCompression = errors.New("websocket: invalid compression negotiation") // NewClient creates a new client connection using the given net connection. // The URL u specifies the host and request URI. Use requestHeader to specify // the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies // (Cookie). Use the response.Header to get the selected subprotocol // (Sec-WebSocket-Protocol) and cookies (Set-Cookie). // // If the WebSocket handshake fails, ErrBadHandshake is returned along with a // non-nil *http.Response so that callers can handle redirects, authentication, // etc. // // Deprecated: Use Dialer instead. func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) { d := Dialer{ ReadBufferSize: readBufSize, WriteBufferSize: writeBufSize, NetDial: func(net, addr string) (net.Conn, error) { return netConn, nil }, } return d.Dial(u.String(), requestHeader) } // A Dialer contains options for connecting to WebSocket server. type Dialer struct { // NetDial specifies the dial function for creating TCP connections. If // NetDial is nil, net.Dial is used. NetDial func(network, addr string) (net.Conn, error) // NetDialContext specifies the dial function for creating TCP connections. If // NetDialContext is nil, net.DialContext is used. NetDialContext func(ctx context.Context, network, addr string) (net.Conn, error) // Proxy specifies a function to return a proxy for a given // Request. If the function returns a non-nil error, the // request is aborted with the provided error. // If Proxy is nil or returns a nil *URL, no proxy is used. Proxy func(*http.Request) (*url.URL, error) // TLSClientConfig specifies the TLS configuration to use with tls.Client. // If nil, the default configuration is used. TLSClientConfig *tls.Config // HandshakeTimeout specifies the duration for the handshake to complete. HandshakeTimeout time.Duration // ReadBufferSize and WriteBufferSize specify I/O buffer sizes in bytes. If a buffer // size is zero, then a useful default size is used. The I/O buffer sizes // do not limit the size of the messages that can be sent or received. ReadBufferSize, WriteBufferSize int // WriteBufferPool is a pool of buffers for write operations. If the value // is not set, then write buffers are allocated to the connection for the // lifetime of the connection. // // A pool is most useful when the application has a modest volume of writes // across a large number of connections. // // Applications should use a single pool for each unique value of // WriteBufferSize. WriteBufferPool BufferPool // Subprotocols specifies the client's requested subprotocols. Subprotocols []string // EnableCompression specifies if the client should attempt to negotiate // per message compression (RFC 7692). Setting this value to true does not // guarantee that compression will be supported. Currently only "no context // takeover" modes are supported. EnableCompression bool // Jar specifies the cookie jar. // If Jar is nil, cookies are not sent in requests and ignored // in responses. Jar http.CookieJar } // Dial creates a new client connection by calling DialContext with a background context. func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) { return d.DialContext(context.Background(), urlStr, requestHeader) } var errMalformedURL = errors.New("malformed ws or wss URL") func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) { hostPort = u.Host hostNoPort = u.Host if i := strings.LastIndex(u.Host, ":"); i > strings.LastIndex(u.Host, "]") { hostNoPort = hostNoPort[:i] } else { switch u.Scheme { case "wss": hostPort += ":443" case "https": hostPort += ":443" default: hostPort += ":80" } } return hostPort, hostNoPort } // DefaultDialer is a dialer with all fields set to the default values. var DefaultDialer = &Dialer{ Proxy: http.ProxyFromEnvironment, HandshakeTimeout: 45 * time.Second, } // nilDialer is dialer to use when receiver is nil. var nilDialer = *DefaultDialer // DialContext creates a new client connection. Use requestHeader to specify the // origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie). // Use the response.Header to get the selected subprotocol // (Sec-WebSocket-Protocol) and cookies (Set-Cookie). // // The context will be used in the request and in the Dialer. // // If the WebSocket handshake fails, ErrBadHandshake is returned along with a // non-nil *http.Response so that callers can handle redirects, authentication, // etcetera. The response body may not contain the entire response and does not // need to be closed by the application. func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) { if d == nil { d = &nilDialer } challengeKey, err := generateChallengeKey() if err != nil { return nil, nil, err } u, err := url.Parse(urlStr) if err != nil { return nil, nil, err } switch u.Scheme { case "ws": u.Scheme = "http" case "wss": u.Scheme = "https" default: return nil, nil, errMalformedURL } if u.User != nil { // User name and password are not allowed in websocket URIs. return nil, nil, errMalformedURL } req := &http.Request{ Method: "GET", URL: u, Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1, Header: make(http.Header), Host: u.Host, } req = req.WithContext(ctx) // Set the cookies present in the cookie jar of the dialer if d.Jar != nil { for _, cookie := range d.Jar.Cookies(u) { req.AddCookie(cookie) } } // Set the request headers using the capitalization for names and values in // RFC examples. Although the capitalization shouldn't matter, there are // servers that depend on it. The Header.Set method is not used because the // method canonicalizes the header names. req.Header["Upgrade"] = []string{"websocket"} req.Header["Connection"] = []string{"Upgrade"} req.Header["Sec-WebSocket-Key"] = []string{challengeKey} req.Header["Sec-WebSocket-Version"] = []string{"13"} if len(d.Subprotocols) > 0 { req.Header["Sec-WebSocket-Protocol"] = []string{strings.Join(d.Subprotocols, ", ")} } for k, vs := range requestHeader { switch { case k == "Host": if len(vs) > 0 { req.Host = vs[0] } case k == "Upgrade" || k == "Connection" || k == "Sec-Websocket-Key" || k == "Sec-Websocket-Version" || k == "Sec-Websocket-Extensions" || (k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0): return nil, nil, errors.New("websocket: duplicate header not allowed: " + k) case k == "Sec-Websocket-Protocol": req.Header["Sec-WebSocket-Protocol"] = vs default: req.Header[k] = vs } } if d.EnableCompression { req.Header["Sec-WebSocket-Extensions"] = []string{"permessage-deflate; server_no_context_takeover; client_no_context_takeover"} } if d.HandshakeTimeout != 0 { var cancel func() ctx, cancel = context.WithTimeout(ctx, d.HandshakeTimeout) defer cancel() } // Get network dial function. var netDial func(network, add string) (net.Conn, error) if d.NetDialContext != nil { netDial = func(network, addr string) (net.Conn, error) { return d.NetDialContext(ctx, network, addr) } } else if d.NetDial != nil { netDial = d.NetDial } else { netDialer := &net.Dialer{} netDial = func(network, addr string) (net.Conn, error) { return netDialer.DialContext(ctx, network, addr) } } // If needed, wrap the dial function to set the connection deadline. if deadline, ok := ctx.Deadline(); ok { forwardDial := netDial netDial = func(network, addr string) (net.Conn, error) { c, err := forwardDial(network, addr) if err != nil { return nil, err } err = c.SetDeadline(deadline) if err != nil { c.Close() return nil, err } return c, nil } } // If needed, wrap the dial function to connect through a proxy. if d.Proxy != nil { proxyURL, err := d.Proxy(req) if err != nil { return nil, nil, err } if proxyURL != nil { dialer, err := proxy_FromURL(proxyURL, netDialerFunc(netDial)) if err != nil { return nil, nil, err } netDial = dialer.Dial } } hostPort, hostNoPort := hostPortNoPort(u) trace := httptrace.ContextClientTrace(ctx) if trace != nil && trace.GetConn != nil { trace.GetConn(hostPort) } netConn, err := netDial("tcp", hostPort) if trace != nil && trace.GotConn != nil { trace.GotConn(httptrace.GotConnInfo{ Conn: netConn, }) } if err != nil { return nil, nil, err } defer func() { if netConn != nil { netConn.Close() } }() if u.Scheme == "https" { cfg := cloneTLSConfig(d.TLSClientConfig) if cfg.ServerName == "" { cfg.ServerName = hostNoPort } tlsConn := tls.Client(netConn, cfg) netConn = tlsConn var err error if trace != nil { err = doHandshakeWithTrace(trace, tlsConn, cfg) } else { err = doHandshake(tlsConn, cfg) } if err != nil { return nil, nil, err } } conn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize, d.WriteBufferPool, nil, nil) if err := req.Write(netConn); err != nil { return nil, nil, err } if trace != nil && trace.GotFirstResponseByte != nil { if peek, err := conn.br.Peek(1); err == nil && len(peek) == 1 { trace.GotFirstResponseByte() } } resp, err := http.ReadResponse(conn.br, req) if err != nil { return nil, nil, err } if d.Jar != nil { if rc := resp.Cookies(); len(rc) > 0 { d.Jar.SetCookies(u, rc) } } if resp.StatusCode != 101 || !strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") || !strings.EqualFold(resp.Header.Get("Connection"), "upgrade") || resp.Header.Get("Sec-Websocket-Accept") != computeAcceptKey(challengeKey) { // Before closing the network connection on return from this // function, slurp up some of the response to aid application // debugging. buf := make([]byte, 1024) n, _ := io.ReadFull(resp.Body, buf) resp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n])) return nil, resp, ErrBadHandshake } for _, ext := range parseExtensions(resp.Header) { if ext[""] != "permessage-deflate" { continue } _, snct := ext["server_no_context_takeover"] _, cnct := ext["client_no_context_takeover"] if !snct || !cnct { return nil, resp, errInvalidCompression } conn.newCompressionWriter = compressNoContextTakeover conn.newDecompressionReader = decompressNoContextTakeover break } resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{})) conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol") netConn.SetDeadline(time.Time{}) netConn = nil // to avoid close in defer. return conn, resp, nil } func doHandshake(tlsConn *tls.Conn, cfg *tls.Config) error { if err := tlsConn.Handshake(); err != nil { return err } if !cfg.InsecureSkipVerify { if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil { return err } } return nil } ================================================ FILE: vendor/github.com/gorilla/websocket/client_clone.go ================================================ // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build go1.8 package websocket import "crypto/tls" func cloneTLSConfig(cfg *tls.Config) *tls.Config { if cfg == nil { return &tls.Config{} } return cfg.Clone() } ================================================ FILE: vendor/github.com/gorilla/websocket/client_clone_legacy.go ================================================ // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build !go1.8 package websocket import "crypto/tls" // cloneTLSConfig clones all public fields except the fields // SessionTicketsDisabled and SessionTicketKey. This avoids copying the // sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a // config in active use. func cloneTLSConfig(cfg *tls.Config) *tls.Config { if cfg == nil { return &tls.Config{} } return &tls.Config{ Rand: cfg.Rand, Time: cfg.Time, Certificates: cfg.Certificates, NameToCertificate: cfg.NameToCertificate, GetCertificate: cfg.GetCertificate, RootCAs: cfg.RootCAs, NextProtos: cfg.NextProtos, ServerName: cfg.ServerName, ClientAuth: cfg.ClientAuth, ClientCAs: cfg.ClientCAs, InsecureSkipVerify: cfg.InsecureSkipVerify, CipherSuites: cfg.CipherSuites, PreferServerCipherSuites: cfg.PreferServerCipherSuites, ClientSessionCache: cfg.ClientSessionCache, MinVersion: cfg.MinVersion, MaxVersion: cfg.MaxVersion, CurvePreferences: cfg.CurvePreferences, } } ================================================ FILE: vendor/github.com/gorilla/websocket/compression.go ================================================ // Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package websocket import ( "compress/flate" "errors" "io" "strings" "sync" ) const ( minCompressionLevel = -2 // flate.HuffmanOnly not defined in Go < 1.6 maxCompressionLevel = flate.BestCompression defaultCompressionLevel = 1 ) var ( flateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool flateReaderPool = sync.Pool{New: func() interface{} { return flate.NewReader(nil) }} ) func decompressNoContextTakeover(r io.Reader) io.ReadCloser { const tail = // Add four bytes as specified in RFC "\x00\x00\xff\xff" + // Add final block to squelch unexpected EOF error from flate reader. "\x01\x00\x00\xff\xff" fr, _ := flateReaderPool.Get().(io.ReadCloser) fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil) return &flateReadWrapper{fr} } func isValidCompressionLevel(level int) bool { return minCompressionLevel <= level && level <= maxCompressionLevel } func compressNoContextTakeover(w io.WriteCloser, level int) io.WriteCloser { p := &flateWriterPools[level-minCompressionLevel] tw := &truncWriter{w: w} fw, _ := p.Get().(*flate.Writer) if fw == nil { fw, _ = flate.NewWriter(tw, level) } else { fw.Reset(tw) } return &flateWriteWrapper{fw: fw, tw: tw, p: p} } // truncWriter is an io.Writer that writes all but the last four bytes of the // stream to another io.Writer. type truncWriter struct { w io.WriteCloser n int p [4]byte } func (w *truncWriter) Write(p []byte) (int, error) { n := 0 // fill buffer first for simplicity. if w.n < len(w.p) { n = copy(w.p[w.n:], p) p = p[n:] w.n += n if len(p) == 0 { return n, nil } } m := len(p) if m > len(w.p) { m = len(w.p) } if nn, err := w.w.Write(w.p[:m]); err != nil { return n + nn, err } copy(w.p[:], w.p[m:]) copy(w.p[len(w.p)-m:], p[len(p)-m:]) nn, err := w.w.Write(p[:len(p)-m]) return n + nn, err } type flateWriteWrapper struct { fw *flate.Writer tw *truncWriter p *sync.Pool } func (w *flateWriteWrapper) Write(p []byte) (int, error) { if w.fw == nil { return 0, errWriteClosed } return w.fw.Write(p) } func (w *flateWriteWrapper) Close() error { if w.fw == nil { return errWriteClosed } err1 := w.fw.Flush() w.p.Put(w.fw) w.fw = nil if w.tw.p != [4]byte{0, 0, 0xff, 0xff} { return errors.New("websocket: internal error, unexpected bytes at end of flate stream") } err2 := w.tw.w.Close() if err1 != nil { return err1 } return err2 } type flateReadWrapper struct { fr io.ReadCloser } func (r *flateReadWrapper) Read(p []byte) (int, error) { if r.fr == nil { return 0, io.ErrClosedPipe } n, err := r.fr.Read(p) if err == io.EOF { // Preemptively place the reader back in the pool. This helps with // scenarios where the application does not call NextReader() soon after // this final read. r.Close() } return n, err } func (r *flateReadWrapper) Close() error { if r.fr == nil { return io.ErrClosedPipe } err := r.fr.Close() flateReaderPool.Put(r.fr) r.fr = nil return err } ================================================ FILE: vendor/github.com/gorilla/websocket/conn.go ================================================ // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package websocket import ( "bufio" "encoding/binary" "errors" "io" "io/ioutil" "math/rand" "net" "strconv" "sync" "time" "unicode/utf8" ) const ( // Frame header byte 0 bits from Section 5.2 of RFC 6455 finalBit = 1 << 7 rsv1Bit = 1 << 6 rsv2Bit = 1 << 5 rsv3Bit = 1 << 4 // Frame header byte 1 bits from Section 5.2 of RFC 6455 maskBit = 1 << 7 maxFrameHeaderSize = 2 + 8 + 4 // Fixed header + length + mask maxControlFramePayloadSize = 125 writeWait = time.Second defaultReadBufferSize = 4096 defaultWriteBufferSize = 4096 continuationFrame = 0 noFrame = -1 ) // Close codes defined in RFC 6455, section 11.7. const ( CloseNormalClosure = 1000 CloseGoingAway = 1001 CloseProtocolError = 1002 CloseUnsupportedData = 1003 CloseNoStatusReceived = 1005 CloseAbnormalClosure = 1006 CloseInvalidFramePayloadData = 1007 ClosePolicyViolation = 1008 CloseMessageTooBig = 1009 CloseMandatoryExtension = 1010 CloseInternalServerErr = 1011 CloseServiceRestart = 1012 CloseTryAgainLater = 1013 CloseTLSHandshake = 1015 ) // The message types are defined in RFC 6455, section 11.8. const ( // TextMessage denotes a text data message. The text message payload is // interpreted as UTF-8 encoded text data. TextMessage = 1 // BinaryMessage denotes a binary data message. BinaryMessage = 2 // CloseMessage denotes a close control message. The optional message // payload contains a numeric code and text. Use the FormatCloseMessage // function to format a close message payload. CloseMessage = 8 // PingMessage denotes a ping control message. The optional message payload // is UTF-8 encoded text. PingMessage = 9 // PongMessage denotes a pong control message. The optional message payload // is UTF-8 encoded text. PongMessage = 10 ) // ErrCloseSent is returned when the application writes a message to the // connection after sending a close message. var ErrCloseSent = errors.New("websocket: close sent") // ErrReadLimit is returned when reading a message that is larger than the // read limit set for the connection. var ErrReadLimit = errors.New("websocket: read limit exceeded") // netError satisfies the net Error interface. type netError struct { msg string temporary bool timeout bool } func (e *netError) Error() string { return e.msg } func (e *netError) Temporary() bool { return e.temporary } func (e *netError) Timeout() bool { return e.timeout } // CloseError represents a close message. type CloseError struct { // Code is defined in RFC 6455, section 11.7. Code int // Text is the optional text payload. Text string } func (e *CloseError) Error() string { s := []byte("websocket: close ") s = strconv.AppendInt(s, int64(e.Code), 10) switch e.Code { case CloseNormalClosure: s = append(s, " (normal)"...) case CloseGoingAway: s = append(s, " (going away)"...) case CloseProtocolError: s = append(s, " (protocol error)"...) case CloseUnsupportedData: s = append(s, " (unsupported data)"...) case CloseNoStatusReceived: s = append(s, " (no status)"...) case CloseAbnormalClosure: s = append(s, " (abnormal closure)"...) case CloseInvalidFramePayloadData: s = append(s, " (invalid payload data)"...) case ClosePolicyViolation: s = append(s, " (policy violation)"...) case CloseMessageTooBig: s = append(s, " (message too big)"...) case CloseMandatoryExtension: s = append(s, " (mandatory extension missing)"...) case CloseInternalServerErr: s = append(s, " (internal server error)"...) case CloseTLSHandshake: s = append(s, " (TLS handshake error)"...) } if e.Text != "" { s = append(s, ": "...) s = append(s, e.Text...) } return string(s) } // IsCloseError returns boolean indicating whether the error is a *CloseError // with one of the specified codes. func IsCloseError(err error, codes ...int) bool { if e, ok := err.(*CloseError); ok { for _, code := range codes { if e.Code == code { return true } } } return false } // IsUnexpectedCloseError returns boolean indicating whether the error is a // *CloseError with a code not in the list of expected codes. func IsUnexpectedCloseError(err error, expectedCodes ...int) bool { if e, ok := err.(*CloseError); ok { for _, code := range expectedCodes { if e.Code == code { return false } } return true } return false } var ( errWriteTimeout = &netError{msg: "websocket: write timeout", timeout: true, temporary: true} errUnexpectedEOF = &CloseError{Code: CloseAbnormalClosure, Text: io.ErrUnexpectedEOF.Error()} errBadWriteOpCode = errors.New("websocket: bad write message type") errWriteClosed = errors.New("websocket: write closed") errInvalidControlFrame = errors.New("websocket: invalid control frame") ) func newMaskKey() [4]byte { n := rand.Uint32() return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)} } func hideTempErr(err error) error { if e, ok := err.(net.Error); ok && e.Temporary() { err = &netError{msg: e.Error(), timeout: e.Timeout()} } return err } func isControl(frameType int) bool { return frameType == CloseMessage || frameType == PingMessage || frameType == PongMessage } func isData(frameType int) bool { return frameType == TextMessage || frameType == BinaryMessage } var validReceivedCloseCodes = map[int]bool{ // see http://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number CloseNormalClosure: true, CloseGoingAway: true, CloseProtocolError: true, CloseUnsupportedData: true, CloseNoStatusReceived: false, CloseAbnormalClosure: false, CloseInvalidFramePayloadData: true, ClosePolicyViolation: true, CloseMessageTooBig: true, CloseMandatoryExtension: true, CloseInternalServerErr: true, CloseServiceRestart: true, CloseTryAgainLater: true, CloseTLSHandshake: false, } func isValidReceivedCloseCode(code int) bool { return validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999) } // BufferPool represents a pool of buffers. The *sync.Pool type satisfies this // interface. The type of the value stored in a pool is not specified. type BufferPool interface { // Get gets a value from the pool or returns nil if the pool is empty. Get() interface{} // Put adds a value to the pool. Put(interface{}) } // writePoolData is the type added to the write buffer pool. This wrapper is // used to prevent applications from peeking at and depending on the values // added to the pool. type writePoolData struct{ buf []byte } // The Conn type represents a WebSocket connection. type Conn struct { conn net.Conn isServer bool subprotocol string // Write fields mu chan struct{} // used as mutex to protect write to conn writeBuf []byte // frame is constructed in this buffer. writePool BufferPool writeBufSize int writeDeadline time.Time writer io.WriteCloser // the current writer returned to the application isWriting bool // for best-effort concurrent write detection writeErrMu sync.Mutex writeErr error enableWriteCompression bool compressionLevel int newCompressionWriter func(io.WriteCloser, int) io.WriteCloser // Read fields reader io.ReadCloser // the current reader returned to the application readErr error br *bufio.Reader // bytes remaining in current frame. // set setReadRemaining to safely update this value and prevent overflow readRemaining int64 readFinal bool // true the current message has more frames. readLength int64 // Message size. readLimit int64 // Maximum message size. readMaskPos int readMaskKey [4]byte handlePong func(string) error handlePing func(string) error handleClose func(int, string) error readErrCount int messageReader *messageReader // the current low-level reader readDecompress bool // whether last read frame had RSV1 set newDecompressionReader func(io.Reader) io.ReadCloser } func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, writeBufferPool BufferPool, br *bufio.Reader, writeBuf []byte) *Conn { if br == nil { if readBufferSize == 0 { readBufferSize = defaultReadBufferSize } else if readBufferSize < maxControlFramePayloadSize { // must be large enough for control frame readBufferSize = maxControlFramePayloadSize } br = bufio.NewReaderSize(conn, readBufferSize) } if writeBufferSize <= 0 { writeBufferSize = defaultWriteBufferSize } writeBufferSize += maxFrameHeaderSize if writeBuf == nil && writeBufferPool == nil { writeBuf = make([]byte, writeBufferSize) } mu := make(chan struct{}, 1) mu <- struct{}{} c := &Conn{ isServer: isServer, br: br, conn: conn, mu: mu, readFinal: true, writeBuf: writeBuf, writePool: writeBufferPool, writeBufSize: writeBufferSize, enableWriteCompression: true, compressionLevel: defaultCompressionLevel, } c.SetCloseHandler(nil) c.SetPingHandler(nil) c.SetPongHandler(nil) return c } // setReadRemaining tracks the number of bytes remaining on the connection. If n // overflows, an ErrReadLimit is returned. func (c *Conn) setReadRemaining(n int64) error { if n < 0 { return ErrReadLimit } c.readRemaining = n return nil } // Subprotocol returns the negotiated protocol for the connection. func (c *Conn) Subprotocol() string { return c.subprotocol } // Close closes the underlying network connection without sending or waiting // for a close message. func (c *Conn) Close() error { return c.conn.Close() } // LocalAddr returns the local network address. func (c *Conn) LocalAddr() net.Addr { return c.conn.LocalAddr() } // RemoteAddr returns the remote network address. func (c *Conn) RemoteAddr() net.Addr { return c.conn.RemoteAddr() } // Write methods func (c *Conn) writeFatal(err error) error { err = hideTempErr(err) c.writeErrMu.Lock() if c.writeErr == nil { c.writeErr = err } c.writeErrMu.Unlock() return err } func (c *Conn) read(n int) ([]byte, error) { p, err := c.br.Peek(n) if err == io.EOF { err = errUnexpectedEOF } c.br.Discard(len(p)) return p, err } func (c *Conn) write(frameType int, deadline time.Time, buf0, buf1 []byte) error { <-c.mu defer func() { c.mu <- struct{}{} }() c.writeErrMu.Lock() err := c.writeErr c.writeErrMu.Unlock() if err != nil { return err } c.conn.SetWriteDeadline(deadline) if len(buf1) == 0 { _, err = c.conn.Write(buf0) } else { err = c.writeBufs(buf0, buf1) } if err != nil { return c.writeFatal(err) } if frameType == CloseMessage { c.writeFatal(ErrCloseSent) } return nil } // WriteControl writes a control message with the given deadline. The allowed // message types are CloseMessage, PingMessage and PongMessage. func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) error { if !isControl(messageType) { return errBadWriteOpCode } if len(data) > maxControlFramePayloadSize { return errInvalidControlFrame } b0 := byte(messageType) | finalBit b1 := byte(len(data)) if !c.isServer { b1 |= maskBit } buf := make([]byte, 0, maxFrameHeaderSize+maxControlFramePayloadSize) buf = append(buf, b0, b1) if c.isServer { buf = append(buf, data...) } else { key := newMaskKey() buf = append(buf, key[:]...) buf = append(buf, data...) maskBytes(key, 0, buf[6:]) } d := 1000 * time.Hour if !deadline.IsZero() { d = deadline.Sub(time.Now()) if d < 0 { return errWriteTimeout } } timer := time.NewTimer(d) select { case <-c.mu: timer.Stop() case <-timer.C: return errWriteTimeout } defer func() { c.mu <- struct{}{} }() c.writeErrMu.Lock() err := c.writeErr c.writeErrMu.Unlock() if err != nil { return err } c.conn.SetWriteDeadline(deadline) _, err = c.conn.Write(buf) if err != nil { return c.writeFatal(err) } if messageType == CloseMessage { c.writeFatal(ErrCloseSent) } return err } // beginMessage prepares a connection and message writer for a new message. func (c *Conn) beginMessage(mw *messageWriter, messageType int) error { // Close previous writer if not already closed by the application. It's // probably better to return an error in this situation, but we cannot // change this without breaking existing applications. if c.writer != nil { c.writer.Close() c.writer = nil } if !isControl(messageType) && !isData(messageType) { return errBadWriteOpCode } c.writeErrMu.Lock() err := c.writeErr c.writeErrMu.Unlock() if err != nil { return err } mw.c = c mw.frameType = messageType mw.pos = maxFrameHeaderSize if c.writeBuf == nil { wpd, ok := c.writePool.Get().(writePoolData) if ok { c.writeBuf = wpd.buf } else { c.writeBuf = make([]byte, c.writeBufSize) } } return nil } // NextWriter returns a writer for the next message to send. The writer's Close // method flushes the complete message to the network. // // There can be at most one open writer on a connection. NextWriter closes the // previous writer if the application has not already done so. // // All message types (TextMessage, BinaryMessage, CloseMessage, PingMessage and // PongMessage) are supported. func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) { var mw messageWriter if err := c.beginMessage(&mw, messageType); err != nil { return nil, err } c.writer = &mw if c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) { w := c.newCompressionWriter(c.writer, c.compressionLevel) mw.compress = true c.writer = w } return c.writer, nil } type messageWriter struct { c *Conn compress bool // whether next call to flushFrame should set RSV1 pos int // end of data in writeBuf. frameType int // type of the current frame. err error } func (w *messageWriter) endMessage(err error) error { if w.err != nil { return err } c := w.c w.err = err c.writer = nil if c.writePool != nil { c.writePool.Put(writePoolData{buf: c.writeBuf}) c.writeBuf = nil } return err } // flushFrame writes buffered data and extra as a frame to the network. The // final argument indicates that this is the last frame in the message. func (w *messageWriter) flushFrame(final bool, extra []byte) error { c := w.c length := w.pos - maxFrameHeaderSize + len(extra) // Check for invalid control frames. if isControl(w.frameType) && (!final || length > maxControlFramePayloadSize) { return w.endMessage(errInvalidControlFrame) } b0 := byte(w.frameType) if final { b0 |= finalBit } if w.compress { b0 |= rsv1Bit } w.compress = false b1 := byte(0) if !c.isServer { b1 |= maskBit } // Assume that the frame starts at beginning of c.writeBuf. framePos := 0 if c.isServer { // Adjust up if mask not included in the header. framePos = 4 } switch { case length >= 65536: c.writeBuf[framePos] = b0 c.writeBuf[framePos+1] = b1 | 127 binary.BigEndian.PutUint64(c.writeBuf[framePos+2:], uint64(length)) case length > 125: framePos += 6 c.writeBuf[framePos] = b0 c.writeBuf[framePos+1] = b1 | 126 binary.BigEndian.PutUint16(c.writeBuf[framePos+2:], uint16(length)) default: framePos += 8 c.writeBuf[framePos] = b0 c.writeBuf[framePos+1] = b1 | byte(length) } if !c.isServer { key := newMaskKey() copy(c.writeBuf[maxFrameHeaderSize-4:], key[:]) maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:w.pos]) if len(extra) > 0 { return w.endMessage(c.writeFatal(errors.New("websocket: internal error, extra used in client mode"))) } } // Write the buffers to the connection with best-effort detection of // concurrent writes. See the concurrency section in the package // documentation for more info. if c.isWriting { panic("concurrent write to websocket connection") } c.isWriting = true err := c.write(w.frameType, c.writeDeadline, c.writeBuf[framePos:w.pos], extra) if !c.isWriting { panic("concurrent write to websocket connection") } c.isWriting = false if err != nil { return w.endMessage(err) } if final { w.endMessage(errWriteClosed) return nil } // Setup for next frame. w.pos = maxFrameHeaderSize w.frameType = continuationFrame return nil } func (w *messageWriter) ncopy(max int) (int, error) { n := len(w.c.writeBuf) - w.pos if n <= 0 { if err := w.flushFrame(false, nil); err != nil { return 0, err } n = len(w.c.writeBuf) - w.pos } if n > max { n = max } return n, nil } func (w *messageWriter) Write(p []byte) (int, error) { if w.err != nil { return 0, w.err } if len(p) > 2*len(w.c.writeBuf) && w.c.isServer { // Don't buffer large messages. err := w.flushFrame(false, p) if err != nil { return 0, err } return len(p), nil } nn := len(p) for len(p) > 0 { n, err := w.ncopy(len(p)) if err != nil { return 0, err } copy(w.c.writeBuf[w.pos:], p[:n]) w.pos += n p = p[n:] } return nn, nil } func (w *messageWriter) WriteString(p string) (int, error) { if w.err != nil { return 0, w.err } nn := len(p) for len(p) > 0 { n, err := w.ncopy(len(p)) if err != nil { return 0, err } copy(w.c.writeBuf[w.pos:], p[:n]) w.pos += n p = p[n:] } return nn, nil } func (w *messageWriter) ReadFrom(r io.Reader) (nn int64, err error) { if w.err != nil { return 0, w.err } for { if w.pos == len(w.c.writeBuf) { err = w.flushFrame(false, nil) if err != nil { break } } var n int n, err = r.Read(w.c.writeBuf[w.pos:]) w.pos += n nn += int64(n) if err != nil { if err == io.EOF { err = nil } break } } return nn, err } func (w *messageWriter) Close() error { if w.err != nil { return w.err } return w.flushFrame(true, nil) } // WritePreparedMessage writes prepared message into connection. func (c *Conn) WritePreparedMessage(pm *PreparedMessage) error { frameType, frameData, err := pm.frame(prepareKey{ isServer: c.isServer, compress: c.newCompressionWriter != nil && c.enableWriteCompression && isData(pm.messageType), compressionLevel: c.compressionLevel, }) if err != nil { return err } if c.isWriting { panic("concurrent write to websocket connection") } c.isWriting = true err = c.write(frameType, c.writeDeadline, frameData, nil) if !c.isWriting { panic("concurrent write to websocket connection") } c.isWriting = false return err } // WriteMessage is a helper method for getting a writer using NextWriter, // writing the message and closing the writer. func (c *Conn) WriteMessage(messageType int, data []byte) error { if c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) { // Fast path with no allocations and single frame. var mw messageWriter if err := c.beginMessage(&mw, messageType); err != nil { return err } n := copy(c.writeBuf[mw.pos:], data) mw.pos += n data = data[n:] return mw.flushFrame(true, data) } w, err := c.NextWriter(messageType) if err != nil { return err } if _, err = w.Write(data); err != nil { return err } return w.Close() } // SetWriteDeadline sets the write deadline on the underlying network // connection. After a write has timed out, the websocket state is corrupt and // all future writes will return an error. A zero value for t means writes will // not time out. func (c *Conn) SetWriteDeadline(t time.Time) error { c.writeDeadline = t return nil } // Read methods func (c *Conn) advanceFrame() (int, error) { // 1. Skip remainder of previous frame. if c.readRemaining > 0 { if _, err := io.CopyN(ioutil.Discard, c.br, c.readRemaining); err != nil { return noFrame, err } } // 2. Read and parse first two bytes of frame header. p, err := c.read(2) if err != nil { return noFrame, err } final := p[0]&finalBit != 0 frameType := int(p[0] & 0xf) mask := p[1]&maskBit != 0 c.setReadRemaining(int64(p[1] & 0x7f)) c.readDecompress = false if c.newDecompressionReader != nil && (p[0]&rsv1Bit) != 0 { c.readDecompress = true p[0] &^= rsv1Bit } if rsv := p[0] & (rsv1Bit | rsv2Bit | rsv3Bit); rsv != 0 { return noFrame, c.handleProtocolError("unexpected reserved bits 0x" + strconv.FormatInt(int64(rsv), 16)) } switch frameType { case CloseMessage, PingMessage, PongMessage: if c.readRemaining > maxControlFramePayloadSize { return noFrame, c.handleProtocolError("control frame length > 125") } if !final { return noFrame, c.handleProtocolError("control frame not final") } case TextMessage, BinaryMessage: if !c.readFinal { return noFrame, c.handleProtocolError("message start before final message frame") } c.readFinal = final case continuationFrame: if c.readFinal { return noFrame, c.handleProtocolError("continuation after final message frame") } c.readFinal = final default: return noFrame, c.handleProtocolError("unknown opcode " + strconv.Itoa(frameType)) } // 3. Read and parse frame length as per // https://tools.ietf.org/html/rfc6455#section-5.2 // // The length of the "Payload data", in bytes: if 0-125, that is the payload // length. // - If 126, the following 2 bytes interpreted as a 16-bit unsigned // integer are the payload length. // - If 127, the following 8 bytes interpreted as // a 64-bit unsigned integer (the most significant bit MUST be 0) are the // payload length. Multibyte length quantities are expressed in network byte // order. switch c.readRemaining { case 126: p, err := c.read(2) if err != nil { return noFrame, err } if err := c.setReadRemaining(int64(binary.BigEndian.Uint16(p))); err != nil { return noFrame, err } case 127: p, err := c.read(8) if err != nil { return noFrame, err } if err := c.setReadRemaining(int64(binary.BigEndian.Uint64(p))); err != nil { return noFrame, err } } // 4. Handle frame masking. if mask != c.isServer { return noFrame, c.handleProtocolError("incorrect mask flag") } if mask { c.readMaskPos = 0 p, err := c.read(len(c.readMaskKey)) if err != nil { return noFrame, err } copy(c.readMaskKey[:], p) } // 5. For text and binary messages, enforce read limit and return. if frameType == continuationFrame || frameType == TextMessage || frameType == BinaryMessage { c.readLength += c.readRemaining // Don't allow readLength to overflow in the presence of a large readRemaining // counter. if c.readLength < 0 { return noFrame, ErrReadLimit } if c.readLimit > 0 && c.readLength > c.readLimit { c.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, ""), time.Now().Add(writeWait)) return noFrame, ErrReadLimit } return frameType, nil } // 6. Read control frame payload. var payload []byte if c.readRemaining > 0 { payload, err = c.read(int(c.readRemaining)) c.setReadRemaining(0) if err != nil { return noFrame, err } if c.isServer { maskBytes(c.readMaskKey, 0, payload) } } // 7. Process control frame payload. switch frameType { case PongMessage: if err := c.handlePong(string(payload)); err != nil { return noFrame, err } case PingMessage: if err := c.handlePing(string(payload)); err != nil { return noFrame, err } case CloseMessage: closeCode := CloseNoStatusReceived closeText := "" if len(payload) >= 2 { closeCode = int(binary.BigEndian.Uint16(payload)) if !isValidReceivedCloseCode(closeCode) { return noFrame, c.handleProtocolError("invalid close code") } closeText = string(payload[2:]) if !utf8.ValidString(closeText) { return noFrame, c.handleProtocolError("invalid utf8 payload in close frame") } } if err := c.handleClose(closeCode, closeText); err != nil { return noFrame, err } return noFrame, &CloseError{Code: closeCode, Text: closeText} } return frameType, nil } func (c *Conn) handleProtocolError(message string) error { c.WriteControl(CloseMessage, FormatCloseMessage(CloseProtocolError, message), time.Now().Add(writeWait)) return errors.New("websocket: " + message) } // NextReader returns the next data message received from the peer. The // returned messageType is either TextMessage or BinaryMessage. // // There can be at most one open reader on a connection. NextReader discards // the previous message if the application has not already consumed it. // // Applications must break out of the application's read loop when this method // returns a non-nil error value. Errors returned from this method are // permanent. Once this method returns a non-nil error, all subsequent calls to // this method return the same error. func (c *Conn) NextReader() (messageType int, r io.Reader, err error) { // Close previous reader, only relevant for decompression. if c.reader != nil { c.reader.Close() c.reader = nil } c.messageReader = nil c.readLength = 0 for c.readErr == nil { frameType, err := c.advanceFrame() if err != nil { c.readErr = hideTempErr(err) break } if frameType == TextMessage || frameType == BinaryMessage { c.messageReader = &messageReader{c} c.reader = c.messageReader if c.readDecompress { c.reader = c.newDecompressionReader(c.reader) } return frameType, c.reader, nil } } // Applications that do handle the error returned from this method spin in // tight loop on connection failure. To help application developers detect // this error, panic on repeated reads to the failed connection. c.readErrCount++ if c.readErrCount >= 1000 { panic("repeated read on failed websocket connection") } return noFrame, nil, c.readErr } type messageReader struct{ c *Conn } func (r *messageReader) Read(b []byte) (int, error) { c := r.c if c.messageReader != r { return 0, io.EOF } for c.readErr == nil { if c.readRemaining > 0 { if int64(len(b)) > c.readRemaining { b = b[:c.readRemaining] } n, err := c.br.Read(b) c.readErr = hideTempErr(err) if c.isServer { c.readMaskPos = maskBytes(c.readMaskKey, c.readMaskPos, b[:n]) } rem := c.readRemaining rem -= int64(n) c.setReadRemaining(rem) if c.readRemaining > 0 && c.readErr == io.EOF { c.readErr = errUnexpectedEOF } return n, c.readErr } if c.readFinal { c.messageReader = nil return 0, io.EOF } frameType, err := c.advanceFrame() switch { case err != nil: c.readErr = hideTempErr(err) case frameType == TextMessage || frameType == BinaryMessage: c.readErr = errors.New("websocket: internal error, unexpected text or binary in Reader") } } err := c.readErr if err == io.EOF && c.messageReader == r { err = errUnexpectedEOF } return 0, err } func (r *messageReader) Close() error { return nil } // ReadMessage is a helper method for getting a reader using NextReader and // reading from that reader to a buffer. func (c *Conn) ReadMessage() (messageType int, p []byte, err error) { var r io.Reader messageType, r, err = c.NextReader() if err != nil { return messageType, nil, err } p, err = ioutil.ReadAll(r) return messageType, p, err } // SetReadDeadline sets the read deadline on the underlying network connection. // After a read has timed out, the websocket connection state is corrupt and // all future reads will return an error. A zero value for t means reads will // not time out. func (c *Conn) SetReadDeadline(t time.Time) error { return c.conn.SetReadDeadline(t) } // SetReadLimit sets the maximum size in bytes for a message read from the peer. If a // message exceeds the limit, the connection sends a close message to the peer // and returns ErrReadLimit to the application. func (c *Conn) SetReadLimit(limit int64) { c.readLimit = limit } // CloseHandler returns the current close handler func (c *Conn) CloseHandler() func(code int, text string) error { return c.handleClose } // SetCloseHandler sets the handler for close messages received from the peer. // The code argument to h is the received close code or CloseNoStatusReceived // if the close message is empty. The default close handler sends a close // message back to the peer. // // The handler function is called from the NextReader, ReadMessage and message // reader Read methods. The application must read the connection to process // close messages as described in the section on Control Messages above. // // The connection read methods return a CloseError when a close message is // received. Most applications should handle close messages as part of their // normal error handling. Applications should only set a close handler when the // application must perform some action before sending a close message back to // the peer. func (c *Conn) SetCloseHandler(h func(code int, text string) error) { if h == nil { h = func(code int, text string) error { message := FormatCloseMessage(code, "") c.WriteControl(CloseMessage, message, time.Now().Add(writeWait)) return nil } } c.handleClose = h } // PingHandler returns the current ping handler func (c *Conn) PingHandler() func(appData string) error { return c.handlePing } // SetPingHandler sets the handler for ping messages received from the peer. // The appData argument to h is the PING message application data. The default // ping handler sends a pong to the peer. // // The handler function is called from the NextReader, ReadMessage and message // reader Read methods. The application must read the connection to process // ping messages as described in the section on Control Messages above. func (c *Conn) SetPingHandler(h func(appData string) error) { if h == nil { h = func(message string) error { err := c.WriteControl(PongMessage, []byte(message), time.Now().Add(writeWait)) if err == ErrCloseSent { return nil } else if e, ok := err.(net.Error); ok && e.Temporary() { return nil } return err } } c.handlePing = h } // PongHandler returns the current pong handler func (c *Conn) PongHandler() func(appData string) error { return c.handlePong } // SetPongHandler sets the handler for pong messages received from the peer. // The appData argument to h is the PONG message application data. The default // pong handler does nothing. // // The handler function is called from the NextReader, ReadMessage and message // reader Read methods. The application must read the connection to process // pong messages as described in the section on Control Messages above. func (c *Conn) SetPongHandler(h func(appData string) error) { if h == nil { h = func(string) error { return nil } } c.handlePong = h } // UnderlyingConn returns the internal net.Conn. This can be used to further // modifications to connection specific flags. func (c *Conn) UnderlyingConn() net.Conn { return c.conn } // EnableWriteCompression enables and disables write compression of // subsequent text and binary messages. This function is a noop if // compression was not negotiated with the peer. func (c *Conn) EnableWriteCompression(enable bool) { c.enableWriteCompression = enable } // SetCompressionLevel sets the flate compression level for subsequent text and // binary messages. This function is a noop if compression was not negotiated // with the peer. See the compress/flate package for a description of // compression levels. func (c *Conn) SetCompressionLevel(level int) error { if !isValidCompressionLevel(level) { return errors.New("websocket: invalid compression level") } c.compressionLevel = level return nil } // FormatCloseMessage formats closeCode and text as a WebSocket close message. // An empty message is returned for code CloseNoStatusReceived. func FormatCloseMessage(closeCode int, text string) []byte { if closeCode == CloseNoStatusReceived { // Return empty message because it's illegal to send // CloseNoStatusReceived. Return non-nil value in case application // checks for nil. return []byte{} } buf := make([]byte, 2+len(text)) binary.BigEndian.PutUint16(buf, uint16(closeCode)) copy(buf[2:], text) return buf } ================================================ FILE: vendor/github.com/gorilla/websocket/conn_write.go ================================================ // Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build go1.8 package websocket import "net" func (c *Conn) writeBufs(bufs ...[]byte) error { b := net.Buffers(bufs) _, err := b.WriteTo(c.conn) return err } ================================================ FILE: vendor/github.com/gorilla/websocket/conn_write_legacy.go ================================================ // Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build !go1.8 package websocket func (c *Conn) writeBufs(bufs ...[]byte) error { for _, buf := range bufs { if len(buf) > 0 { if _, err := c.conn.Write(buf); err != nil { return err } } } return nil } ================================================ FILE: vendor/github.com/gorilla/websocket/doc.go ================================================ // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package websocket implements the WebSocket protocol defined in RFC 6455. // // Overview // // The Conn type represents a WebSocket connection. A server application calls // the Upgrader.Upgrade method from an HTTP request handler to get a *Conn: // // var upgrader = websocket.Upgrader{ // ReadBufferSize: 1024, // WriteBufferSize: 1024, // } // // func handler(w http.ResponseWriter, r *http.Request) { // conn, err := upgrader.Upgrade(w, r, nil) // if err != nil { // log.Println(err) // return // } // ... Use conn to send and receive messages. // } // // Call the connection's WriteMessage and ReadMessage methods to send and // receive messages as a slice of bytes. This snippet of code shows how to echo // messages using these methods: // // for { // messageType, p, err := conn.ReadMessage() // if err != nil { // log.Println(err) // return // } // if err := conn.WriteMessage(messageType, p); err != nil { // log.Println(err) // return // } // } // // In above snippet of code, p is a []byte and messageType is an int with value // websocket.BinaryMessage or websocket.TextMessage. // // An application can also send and receive messages using the io.WriteCloser // and io.Reader interfaces. To send a message, call the connection NextWriter // method to get an io.WriteCloser, write the message to the writer and close // the writer when done. To receive a message, call the connection NextReader // method to get an io.Reader and read until io.EOF is returned. This snippet // shows how to echo messages using the NextWriter and NextReader methods: // // for { // messageType, r, err := conn.NextReader() // if err != nil { // return // } // w, err := conn.NextWriter(messageType) // if err != nil { // return err // } // if _, err := io.Copy(w, r); err != nil { // return err // } // if err := w.Close(); err != nil { // return err // } // } // // Data Messages // // The WebSocket protocol distinguishes between text and binary data messages. // Text messages are interpreted as UTF-8 encoded text. The interpretation of // binary messages is left to the application. // // This package uses the TextMessage and BinaryMessage integer constants to // identify the two data message types. The ReadMessage and NextReader methods // return the type of the received message. The messageType argument to the // WriteMessage and NextWriter methods specifies the type of a sent message. // // It is the application's responsibility to ensure that text messages are // valid UTF-8 encoded text. // // Control Messages // // The WebSocket protocol defines three types of control messages: close, ping // and pong. Call the connection WriteControl, WriteMessage or NextWriter // methods to send a control message to the peer. // // Connections handle received close messages by calling the handler function // set with the SetCloseHandler method and by returning a *CloseError from the // NextReader, ReadMessage or the message Read method. The default close // handler sends a close message to the peer. // // Connections handle received ping messages by calling the handler function // set with the SetPingHandler method. The default ping handler sends a pong // message to the peer. // // Connections handle received pong messages by calling the handler function // set with the SetPongHandler method. The default pong handler does nothing. // If an application sends ping messages, then the application should set a // pong handler to receive the corresponding pong. // // The control message handler functions are called from the NextReader, // ReadMessage and message reader Read methods. The default close and ping // handlers can block these methods for a short time when the handler writes to // the connection. // // The application must read the connection to process close, ping and pong // messages sent from the peer. If the application is not otherwise interested // in messages from the peer, then the application should start a goroutine to // read and discard messages from the peer. A simple example is: // // func readLoop(c *websocket.Conn) { // for { // if _, _, err := c.NextReader(); err != nil { // c.Close() // break // } // } // } // // Concurrency // // Connections support one concurrent reader and one concurrent writer. // // Applications are responsible for ensuring that no more than one goroutine // calls the write methods (NextWriter, SetWriteDeadline, WriteMessage, // WriteJSON, EnableWriteCompression, SetCompressionLevel) concurrently and // that no more than one goroutine calls the read methods (NextReader, // SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler) // concurrently. // // The Close and WriteControl methods can be called concurrently with all other // methods. // // Origin Considerations // // Web browsers allow Javascript applications to open a WebSocket connection to // any host. It's up to the server to enforce an origin policy using the Origin // request header sent by the browser. // // The Upgrader calls the function specified in the CheckOrigin field to check // the origin. If the CheckOrigin function returns false, then the Upgrade // method fails the WebSocket handshake with HTTP status 403. // // If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail // the handshake if the Origin request header is present and the Origin host is // not equal to the Host request header. // // The deprecated package-level Upgrade function does not perform origin // checking. The application is responsible for checking the Origin header // before calling the Upgrade function. // // Buffers // // Connections buffer network input and output to reduce the number // of system calls when reading or writing messages. // // Write buffers are also used for constructing WebSocket frames. See RFC 6455, // Section 5 for a discussion of message framing. A WebSocket frame header is // written to the network each time a write buffer is flushed to the network. // Decreasing the size of the write buffer can increase the amount of framing // overhead on the connection. // // The buffer sizes in bytes are specified by the ReadBufferSize and // WriteBufferSize fields in the Dialer and Upgrader. The Dialer uses a default // size of 4096 when a buffer size field is set to zero. The Upgrader reuses // buffers created by the HTTP server when a buffer size field is set to zero. // The HTTP server buffers have a size of 4096 at the time of this writing. // // The buffer sizes do not limit the size of a message that can be read or // written by a connection. // // Buffers are held for the lifetime of the connection by default. If the // Dialer or Upgrader WriteBufferPool field is set, then a connection holds the // write buffer only when writing a message. // // Applications should tune the buffer sizes to balance memory use and // performance. Increasing the buffer size uses more memory, but can reduce the // number of system calls to read or write the network. In the case of writing, // increasing the buffer size can reduce the number of frame headers written to // the network. // // Some guidelines for setting buffer parameters are: // // Limit the buffer sizes to the maximum expected message size. Buffers larger // than the largest message do not provide any benefit. // // Depending on the distribution of message sizes, setting the buffer size to // a value less than the maximum expected message size can greatly reduce memory // use with a small impact on performance. Here's an example: If 99% of the // messages are smaller than 256 bytes and the maximum message size is 512 // bytes, then a buffer size of 256 bytes will result in 1.01 more system calls // than a buffer size of 512 bytes. The memory savings is 50%. // // A write buffer pool is useful when the application has a modest number // writes over a large number of connections. when buffers are pooled, a larger // buffer size has a reduced impact on total memory use and has the benefit of // reducing system calls and frame overhead. // // Compression EXPERIMENTAL // // Per message compression extensions (RFC 7692) are experimentally supported // by this package in a limited capacity. Setting the EnableCompression option // to true in Dialer or Upgrader will attempt to negotiate per message deflate // support. // // var upgrader = websocket.Upgrader{ // EnableCompression: true, // } // // If compression was successfully negotiated with the connection's peer, any // message received in compressed form will be automatically decompressed. // All Read methods will return uncompressed bytes. // // Per message compression of messages written to a connection can be enabled // or disabled by calling the corresponding Conn method: // // conn.EnableWriteCompression(false) // // Currently this package does not support compression with "context takeover". // This means that messages must be compressed and decompressed in isolation, // without retaining sliding window or dictionary state across messages. For // more details refer to RFC 7692. // // Use of compression is experimental and may result in decreased performance. package websocket ================================================ FILE: vendor/github.com/gorilla/websocket/join.go ================================================ // Copyright 2019 The Gorilla WebSocket Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package websocket import ( "io" "strings" ) // JoinMessages concatenates received messages to create a single io.Reader. // The string term is appended to each message. The returned reader does not // support concurrent calls to the Read method. func JoinMessages(c *Conn, term string) io.Reader { return &joinReader{c: c, term: term} } type joinReader struct { c *Conn term string r io.Reader } func (r *joinReader) Read(p []byte) (int, error) { if r.r == nil { var err error _, r.r, err = r.c.NextReader() if err != nil { return 0, err } if r.term != "" { r.r = io.MultiReader(r.r, strings.NewReader(r.term)) } } n, err := r.r.Read(p) if err == io.EOF { err = nil r.r = nil } return n, err } ================================================ FILE: vendor/github.com/gorilla/websocket/json.go ================================================ // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package websocket import ( "encoding/json" "io" ) // WriteJSON writes the JSON encoding of v as a message. // // Deprecated: Use c.WriteJSON instead. func WriteJSON(c *Conn, v interface{}) error { return c.WriteJSON(v) } // WriteJSON writes the JSON encoding of v as a message. // // See the documentation for encoding/json Marshal for details about the // conversion of Go values to JSON. func (c *Conn) WriteJSON(v interface{}) error { w, err := c.NextWriter(TextMessage) if err != nil { return err } err1 := json.NewEncoder(w).Encode(v) err2 := w.Close() if err1 != nil { return err1 } return err2 } // ReadJSON reads the next JSON-encoded message from the connection and stores // it in the value pointed to by v. // // Deprecated: Use c.ReadJSON instead. func ReadJSON(c *Conn, v interface{}) error { return c.ReadJSON(v) } // ReadJSON reads the next JSON-encoded message from the connection and stores // it in the value pointed to by v. // // See the documentation for the encoding/json Unmarshal function for details // about the conversion of JSON to a Go value. func (c *Conn) ReadJSON(v interface{}) error { _, r, err := c.NextReader() if err != nil { return err } err = json.NewDecoder(r).Decode(v) if err == io.EOF { // One value is expected in the message. err = io.ErrUnexpectedEOF } return err } ================================================ FILE: vendor/github.com/gorilla/websocket/mask.go ================================================ // Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of // this source code is governed by a BSD-style license that can be found in the // LICENSE file. // +build !appengine package websocket import "unsafe" const wordSize = int(unsafe.Sizeof(uintptr(0))) func maskBytes(key [4]byte, pos int, b []byte) int { // Mask one byte at a time for small buffers. if len(b) < 2*wordSize { for i := range b { b[i] ^= key[pos&3] pos++ } return pos & 3 } // Mask one byte at a time to word boundary. if n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 { n = wordSize - n for i := range b[:n] { b[i] ^= key[pos&3] pos++ } b = b[n:] } // Create aligned word size key. var k [wordSize]byte for i := range k { k[i] = key[(pos+i)&3] } kw := *(*uintptr)(unsafe.Pointer(&k)) // Mask one word at a time. n := (len(b) / wordSize) * wordSize for i := 0; i < n; i += wordSize { *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw } // Mask one byte at a time for remaining bytes. b = b[n:] for i := range b { b[i] ^= key[pos&3] pos++ } return pos & 3 } ================================================ FILE: vendor/github.com/gorilla/websocket/mask_safe.go ================================================ // Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of // this source code is governed by a BSD-style license that can be found in the // LICENSE file. // +build appengine package websocket func maskBytes(key [4]byte, pos int, b []byte) int { for i := range b { b[i] ^= key[pos&3] pos++ } return pos & 3 } ================================================ FILE: vendor/github.com/gorilla/websocket/prepared.go ================================================ // Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package websocket import ( "bytes" "net" "sync" "time" ) // PreparedMessage caches on the wire representations of a message payload. // Use PreparedMessage to efficiently send a message payload to multiple // connections. PreparedMessage is especially useful when compression is used // because the CPU and memory expensive compression operation can be executed // once for a given set of compression options. type PreparedMessage struct { messageType int data []byte mu sync.Mutex frames map[prepareKey]*preparedFrame } // prepareKey defines a unique set of options to cache prepared frames in PreparedMessage. type prepareKey struct { isServer bool compress bool compressionLevel int } // preparedFrame contains data in wire representation. type preparedFrame struct { once sync.Once data []byte } // NewPreparedMessage returns an initialized PreparedMessage. You can then send // it to connection using WritePreparedMessage method. Valid wire // representation will be calculated lazily only once for a set of current // connection options. func NewPreparedMessage(messageType int, data []byte) (*PreparedMessage, error) { pm := &PreparedMessage{ messageType: messageType, frames: make(map[prepareKey]*preparedFrame), data: data, } // Prepare a plain server frame. _, frameData, err := pm.frame(prepareKey{isServer: true, compress: false}) if err != nil { return nil, err } // To protect against caller modifying the data argument, remember the data // copied to the plain server frame. pm.data = frameData[len(frameData)-len(data):] return pm, nil } func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) { pm.mu.Lock() frame, ok := pm.frames[key] if !ok { frame = &preparedFrame{} pm.frames[key] = frame } pm.mu.Unlock() var err error frame.once.Do(func() { // Prepare a frame using a 'fake' connection. // TODO: Refactor code in conn.go to allow more direct construction of // the frame. mu := make(chan struct{}, 1) mu <- struct{}{} var nc prepareConn c := &Conn{ conn: &nc, mu: mu, isServer: key.isServer, compressionLevel: key.compressionLevel, enableWriteCompression: true, writeBuf: make([]byte, defaultWriteBufferSize+maxFrameHeaderSize), } if key.compress { c.newCompressionWriter = compressNoContextTakeover } err = c.WriteMessage(pm.messageType, pm.data) frame.data = nc.buf.Bytes() }) return pm.messageType, frame.data, err } type prepareConn struct { buf bytes.Buffer net.Conn } func (pc *prepareConn) Write(p []byte) (int, error) { return pc.buf.Write(p) } func (pc *prepareConn) SetWriteDeadline(t time.Time) error { return nil } ================================================ FILE: vendor/github.com/gorilla/websocket/proxy.go ================================================ // Copyright 2017 The Gorilla WebSocket Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package websocket import ( "bufio" "encoding/base64" "errors" "net" "net/http" "net/url" "strings" ) type netDialerFunc func(network, addr string) (net.Conn, error) func (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) { return fn(network, addr) } func init() { proxy_RegisterDialerType("http", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) { return &httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDialer.Dial}, nil }) } type httpProxyDialer struct { proxyURL *url.URL forwardDial func(network, addr string) (net.Conn, error) } func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error) { hostPort, _ := hostPortNoPort(hpd.proxyURL) conn, err := hpd.forwardDial(network, hostPort) if err != nil { return nil, err } connectHeader := make(http.Header) if user := hpd.proxyURL.User; user != nil { proxyUser := user.Username() if proxyPassword, passwordSet := user.Password(); passwordSet { credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword)) connectHeader.Set("Proxy-Authorization", "Basic "+credential) } } connectReq := &http.Request{ Method: "CONNECT", URL: &url.URL{Opaque: addr}, Host: addr, Header: connectHeader, } if err := connectReq.Write(conn); err != nil { conn.Close() return nil, err } // Read response. It's OK to use and discard buffered reader here becaue // the remote server does not speak until spoken to. br := bufio.NewReader(conn) resp, err := http.ReadResponse(br, connectReq) if err != nil { conn.Close() return nil, err } if resp.StatusCode != 200 { conn.Close() f := strings.SplitN(resp.Status, " ", 2) return nil, errors.New(f[1]) } return conn, nil } ================================================ FILE: vendor/github.com/gorilla/websocket/server.go ================================================ // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package websocket import ( "bufio" "errors" "io" "net/http" "net/url" "strings" "time" ) // HandshakeError describes an error with the handshake from the peer. type HandshakeError struct { message string } func (e HandshakeError) Error() string { return e.message } // Upgrader specifies parameters for upgrading an HTTP connection to a // WebSocket connection. type Upgrader struct { // HandshakeTimeout specifies the duration for the handshake to complete. HandshakeTimeout time.Duration // ReadBufferSize and WriteBufferSize specify I/O buffer sizes in bytes. If a buffer // size is zero, then buffers allocated by the HTTP server are used. The // I/O buffer sizes do not limit the size of the messages that can be sent // or received. ReadBufferSize, WriteBufferSize int // WriteBufferPool is a pool of buffers for write operations. If the value // is not set, then write buffers are allocated to the connection for the // lifetime of the connection. // // A pool is most useful when the application has a modest volume of writes // across a large number of connections. // // Applications should use a single pool for each unique value of // WriteBufferSize. WriteBufferPool BufferPool // Subprotocols specifies the server's supported protocols in order of // preference. If this field is not nil, then the Upgrade method negotiates a // subprotocol by selecting the first match in this list with a protocol // requested by the client. If there's no match, then no protocol is // negotiated (the Sec-Websocket-Protocol header is not included in the // handshake response). Subprotocols []string // Error specifies the function for generating HTTP error responses. If Error // is nil, then http.Error is used to generate the HTTP response. Error func(w http.ResponseWriter, r *http.Request, status int, reason error) // CheckOrigin returns true if the request Origin header is acceptable. If // CheckOrigin is nil, then a safe default is used: return false if the // Origin request header is present and the origin host is not equal to // request Host header. // // A CheckOrigin function should carefully validate the request origin to // prevent cross-site request forgery. CheckOrigin func(r *http.Request) bool // EnableCompression specify if the server should attempt to negotiate per // message compression (RFC 7692). Setting this value to true does not // guarantee that compression will be supported. Currently only "no context // takeover" modes are supported. EnableCompression bool } func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) { err := HandshakeError{reason} if u.Error != nil { u.Error(w, r, status, err) } else { w.Header().Set("Sec-Websocket-Version", "13") http.Error(w, http.StatusText(status), status) } return nil, err } // checkSameOrigin returns true if the origin is not set or is equal to the request host. func checkSameOrigin(r *http.Request) bool { origin := r.Header["Origin"] if len(origin) == 0 { return true } u, err := url.Parse(origin[0]) if err != nil { return false } return equalASCIIFold(u.Host, r.Host) } func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string { if u.Subprotocols != nil { clientProtocols := Subprotocols(r) for _, serverProtocol := range u.Subprotocols { for _, clientProtocol := range clientProtocols { if clientProtocol == serverProtocol { return clientProtocol } } } } else if responseHeader != nil { return responseHeader.Get("Sec-Websocket-Protocol") } return "" } // Upgrade upgrades the HTTP server connection to the WebSocket protocol. // // The responseHeader is included in the response to the client's upgrade // request. Use the responseHeader to specify cookies (Set-Cookie) and the // application negotiated subprotocol (Sec-WebSocket-Protocol). // // If the upgrade fails, then Upgrade replies to the client with an HTTP error // response. func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) { const badHandshake = "websocket: the client is not using the websocket protocol: " if !tokenListContainsValue(r.Header, "Connection", "upgrade") { return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'upgrade' token not found in 'Connection' header") } if !tokenListContainsValue(r.Header, "Upgrade", "websocket") { return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'websocket' token not found in 'Upgrade' header") } if r.Method != "GET" { return u.returnError(w, r, http.StatusMethodNotAllowed, badHandshake+"request method is not GET") } if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") { return u.returnError(w, r, http.StatusBadRequest, "websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header") } if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok { return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific 'Sec-WebSocket-Extensions' headers are unsupported") } checkOrigin := u.CheckOrigin if checkOrigin == nil { checkOrigin = checkSameOrigin } if !checkOrigin(r) { return u.returnError(w, r, http.StatusForbidden, "websocket: request origin not allowed by Upgrader.CheckOrigin") } challengeKey := r.Header.Get("Sec-Websocket-Key") if challengeKey == "" { return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'Sec-WebSocket-Key' header is missing or blank") } subprotocol := u.selectSubprotocol(r, responseHeader) // Negotiate PMCE var compress bool if u.EnableCompression { for _, ext := range parseExtensions(r.Header) { if ext[""] != "permessage-deflate" { continue } compress = true break } } h, ok := w.(http.Hijacker) if !ok { return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker") } var brw *bufio.ReadWriter netConn, brw, err := h.Hijack() if err != nil { return u.returnError(w, r, http.StatusInternalServerError, err.Error()) } if brw.Reader.Buffered() > 0 { netConn.Close() return nil, errors.New("websocket: client sent data before handshake is complete") } var br *bufio.Reader if u.ReadBufferSize == 0 && bufioReaderSize(netConn, brw.Reader) > 256 { // Reuse hijacked buffered reader as connection reader. br = brw.Reader } buf := bufioWriterBuffer(netConn, brw.Writer) var writeBuf []byte if u.WriteBufferPool == nil && u.WriteBufferSize == 0 && len(buf) >= maxFrameHeaderSize+256 { // Reuse hijacked write buffer as connection buffer. writeBuf = buf } c := newConn(netConn, true, u.ReadBufferSize, u.WriteBufferSize, u.WriteBufferPool, br, writeBuf) c.subprotocol = subprotocol if compress { c.newCompressionWriter = compressNoContextTakeover c.newDecompressionReader = decompressNoContextTakeover } // Use larger of hijacked buffer and connection write buffer for header. p := buf if len(c.writeBuf) > len(p) { p = c.writeBuf } p = p[:0] p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...) p = append(p, computeAcceptKey(challengeKey)...) p = append(p, "\r\n"...) if c.subprotocol != "" { p = append(p, "Sec-WebSocket-Protocol: "...) p = append(p, c.subprotocol...) p = append(p, "\r\n"...) } if compress { p = append(p, "Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover\r\n"...) } for k, vs := range responseHeader { if k == "Sec-Websocket-Protocol" { continue } for _, v := range vs { p = append(p, k...) p = append(p, ": "...) for i := 0; i < len(v); i++ { b := v[i] if b <= 31 { // prevent response splitting. b = ' ' } p = append(p, b) } p = append(p, "\r\n"...) } } p = append(p, "\r\n"...) // Clear deadlines set by HTTP server. netConn.SetDeadline(time.Time{}) if u.HandshakeTimeout > 0 { netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout)) } if _, err = netConn.Write(p); err != nil { netConn.Close() return nil, err } if u.HandshakeTimeout > 0 { netConn.SetWriteDeadline(time.Time{}) } return c, nil } // Upgrade upgrades the HTTP server connection to the WebSocket protocol. // // Deprecated: Use websocket.Upgrader instead. // // Upgrade does not perform origin checking. The application is responsible for // checking the Origin header before calling Upgrade. An example implementation // of the same origin policy check is: // // if req.Header.Get("Origin") != "http://"+req.Host { // http.Error(w, "Origin not allowed", http.StatusForbidden) // return // } // // If the endpoint supports subprotocols, then the application is responsible // for negotiating the protocol used on the connection. Use the Subprotocols() // function to get the subprotocols requested by the client. Use the // Sec-Websocket-Protocol response header to specify the subprotocol selected // by the application. // // The responseHeader is included in the response to the client's upgrade // request. Use the responseHeader to specify cookies (Set-Cookie) and the // negotiated subprotocol (Sec-Websocket-Protocol). // // The connection buffers IO to the underlying network connection. The // readBufSize and writeBufSize parameters specify the size of the buffers to // use. Messages can be larger than the buffers. // // If the request is not a valid WebSocket handshake, then Upgrade returns an // error of type HandshakeError. Applications should handle this error by // replying to the client with an HTTP error response. func Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header, readBufSize, writeBufSize int) (*Conn, error) { u := Upgrader{ReadBufferSize: readBufSize, WriteBufferSize: writeBufSize} u.Error = func(w http.ResponseWriter, r *http.Request, status int, reason error) { // don't return errors to maintain backwards compatibility } u.CheckOrigin = func(r *http.Request) bool { // allow all connections by default return true } return u.Upgrade(w, r, responseHeader) } // Subprotocols returns the subprotocols requested by the client in the // Sec-Websocket-Protocol header. func Subprotocols(r *http.Request) []string { h := strings.TrimSpace(r.Header.Get("Sec-Websocket-Protocol")) if h == "" { return nil } protocols := strings.Split(h, ",") for i := range protocols { protocols[i] = strings.TrimSpace(protocols[i]) } return protocols } // IsWebSocketUpgrade returns true if the client requested upgrade to the // WebSocket protocol. func IsWebSocketUpgrade(r *http.Request) bool { return tokenListContainsValue(r.Header, "Connection", "upgrade") && tokenListContainsValue(r.Header, "Upgrade", "websocket") } // bufioReaderSize size returns the size of a bufio.Reader. func bufioReaderSize(originalReader io.Reader, br *bufio.Reader) int { // This code assumes that peek on a reset reader returns // bufio.Reader.buf[:0]. // TODO: Use bufio.Reader.Size() after Go 1.10 br.Reset(originalReader) if p, err := br.Peek(0); err == nil { return cap(p) } return 0 } // writeHook is an io.Writer that records the last slice passed to it vio // io.Writer.Write. type writeHook struct { p []byte } func (wh *writeHook) Write(p []byte) (int, error) { wh.p = p return len(p), nil } // bufioWriterBuffer grabs the buffer from a bufio.Writer. func bufioWriterBuffer(originalWriter io.Writer, bw *bufio.Writer) []byte { // This code assumes that bufio.Writer.buf[:1] is passed to the // bufio.Writer's underlying writer. var wh writeHook bw.Reset(&wh) bw.WriteByte(0) bw.Flush() bw.Reset(originalWriter) return wh.p[:cap(wh.p)] } ================================================ FILE: vendor/github.com/gorilla/websocket/trace.go ================================================ // +build go1.8 package websocket import ( "crypto/tls" "net/http/httptrace" ) func doHandshakeWithTrace(trace *httptrace.ClientTrace, tlsConn *tls.Conn, cfg *tls.Config) error { if trace.TLSHandshakeStart != nil { trace.TLSHandshakeStart() } err := doHandshake(tlsConn, cfg) if trace.TLSHandshakeDone != nil { trace.TLSHandshakeDone(tlsConn.ConnectionState(), err) } return err } ================================================ FILE: vendor/github.com/gorilla/websocket/trace_17.go ================================================ // +build !go1.8 package websocket import ( "crypto/tls" "net/http/httptrace" ) func doHandshakeWithTrace(trace *httptrace.ClientTrace, tlsConn *tls.Conn, cfg *tls.Config) error { return doHandshake(tlsConn, cfg) } ================================================ FILE: vendor/github.com/gorilla/websocket/util.go ================================================ // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package websocket import ( "crypto/rand" "crypto/sha1" "encoding/base64" "io" "net/http" "strings" "unicode/utf8" ) var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11") func computeAcceptKey(challengeKey string) string { h := sha1.New() h.Write([]byte(challengeKey)) h.Write(keyGUID) return base64.StdEncoding.EncodeToString(h.Sum(nil)) } func generateChallengeKey() (string, error) { p := make([]byte, 16) if _, err := io.ReadFull(rand.Reader, p); err != nil { return "", err } return base64.StdEncoding.EncodeToString(p), nil } // Token octets per RFC 2616. var isTokenOctet = [256]bool{ '!': true, '#': true, '$': true, '%': true, '&': true, '\'': true, '*': true, '+': true, '-': true, '.': true, '0': true, '1': true, '2': true, '3': true, '4': true, '5': true, '6': true, '7': true, '8': true, '9': true, 'A': true, 'B': true, 'C': true, 'D': true, 'E': true, 'F': true, 'G': true, 'H': true, 'I': true, 'J': true, 'K': true, 'L': true, 'M': true, 'N': true, 'O': true, 'P': true, 'Q': true, 'R': true, 'S': true, 'T': true, 'U': true, 'W': true, 'V': true, 'X': true, 'Y': true, 'Z': true, '^': true, '_': true, '`': true, 'a': true, 'b': true, 'c': true, 'd': true, 'e': true, 'f': true, 'g': true, 'h': true, 'i': true, 'j': true, 'k': true, 'l': true, 'm': true, 'n': true, 'o': true, 'p': true, 'q': true, 'r': true, 's': true, 't': true, 'u': true, 'v': true, 'w': true, 'x': true, 'y': true, 'z': true, '|': true, '~': true, } // skipSpace returns a slice of the string s with all leading RFC 2616 linear // whitespace removed. func skipSpace(s string) (rest string) { i := 0 for ; i < len(s); i++ { if b := s[i]; b != ' ' && b != '\t' { break } } return s[i:] } // nextToken returns the leading RFC 2616 token of s and the string following // the token. func nextToken(s string) (token, rest string) { i := 0 for ; i < len(s); i++ { if !isTokenOctet[s[i]] { break } } return s[:i], s[i:] } // nextTokenOrQuoted returns the leading token or quoted string per RFC 2616 // and the string following the token or quoted string. func nextTokenOrQuoted(s string) (value string, rest string) { if !strings.HasPrefix(s, "\"") { return nextToken(s) } s = s[1:] for i := 0; i < len(s); i++ { switch s[i] { case '"': return s[:i], s[i+1:] case '\\': p := make([]byte, len(s)-1) j := copy(p, s[:i]) escape := true for i = i + 1; i < len(s); i++ { b := s[i] switch { case escape: escape = false p[j] = b j++ case b == '\\': escape = true case b == '"': return string(p[:j]), s[i+1:] default: p[j] = b j++ } } return "", "" } } return "", "" } // equalASCIIFold returns true if s is equal to t with ASCII case folding as // defined in RFC 4790. func equalASCIIFold(s, t string) bool { for s != "" && t != "" { sr, size := utf8.DecodeRuneInString(s) s = s[size:] tr, size := utf8.DecodeRuneInString(t) t = t[size:] if sr == tr { continue } if 'A' <= sr && sr <= 'Z' { sr = sr + 'a' - 'A' } if 'A' <= tr && tr <= 'Z' { tr = tr + 'a' - 'A' } if sr != tr { return false } } return s == t } // tokenListContainsValue returns true if the 1#token header with the given // name contains a token equal to value with ASCII case folding. func tokenListContainsValue(header http.Header, name string, value string) bool { headers: for _, s := range header[name] { for { var t string t, s = nextToken(skipSpace(s)) if t == "" { continue headers } s = skipSpace(s) if s != "" && s[0] != ',' { continue headers } if equalASCIIFold(t, value) { return true } if s == "" { continue headers } s = s[1:] } } return false } // parseExtensions parses WebSocket extensions from a header. func parseExtensions(header http.Header) []map[string]string { // From RFC 6455: // // Sec-WebSocket-Extensions = extension-list // extension-list = 1#extension // extension = extension-token *( ";" extension-param ) // extension-token = registered-token // registered-token = token // extension-param = token [ "=" (token | quoted-string) ] // ;When using the quoted-string syntax variant, the value // ;after quoted-string unescaping MUST conform to the // ;'token' ABNF. var result []map[string]string headers: for _, s := range header["Sec-Websocket-Extensions"] { for { var t string t, s = nextToken(skipSpace(s)) if t == "" { continue headers } ext := map[string]string{"": t} for { s = skipSpace(s) if !strings.HasPrefix(s, ";") { break } var k string k, s = nextToken(skipSpace(s[1:])) if k == "" { continue headers } s = skipSpace(s) var v string if strings.HasPrefix(s, "=") { v, s = nextTokenOrQuoted(skipSpace(s[1:])) s = skipSpace(s) } if s != "" && s[0] != ',' && s[0] != ';' { continue headers } ext[k] = v } if s != "" && s[0] != ',' { continue headers } result = append(result, ext) if s == "" { continue headers } s = s[1:] } } return result } ================================================ FILE: vendor/github.com/gorilla/websocket/x_net_proxy.go ================================================ // Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT. //go:generate bundle -o x_net_proxy.go golang.org/x/net/proxy // Package proxy provides support for a variety of protocols to proxy network // data. // package websocket import ( "errors" "io" "net" "net/url" "os" "strconv" "strings" "sync" ) type proxy_direct struct{} // Direct is a direct proxy: one that makes network connections directly. var proxy_Direct = proxy_direct{} func (proxy_direct) Dial(network, addr string) (net.Conn, error) { return net.Dial(network, addr) } // A PerHost directs connections to a default Dialer unless the host name // requested matches one of a number of exceptions. type proxy_PerHost struct { def, bypass proxy_Dialer bypassNetworks []*net.IPNet bypassIPs []net.IP bypassZones []string bypassHosts []string } // NewPerHost returns a PerHost Dialer that directs connections to either // defaultDialer or bypass, depending on whether the connection matches one of // the configured rules. func proxy_NewPerHost(defaultDialer, bypass proxy_Dialer) *proxy_PerHost { return &proxy_PerHost{ def: defaultDialer, bypass: bypass, } } // Dial connects to the address addr on the given network through either // defaultDialer or bypass. func (p *proxy_PerHost) Dial(network, addr string) (c net.Conn, err error) { host, _, err := net.SplitHostPort(addr) if err != nil { return nil, err } return p.dialerForRequest(host).Dial(network, addr) } func (p *proxy_PerHost) dialerForRequest(host string) proxy_Dialer { if ip := net.ParseIP(host); ip != nil { for _, net := range p.bypassNetworks { if net.Contains(ip) { return p.bypass } } for _, bypassIP := range p.bypassIPs { if bypassIP.Equal(ip) { return p.bypass } } return p.def } for _, zone := range p.bypassZones { if strings.HasSuffix(host, zone) { return p.bypass } if host == zone[1:] { // For a zone ".example.com", we match "example.com" // too. return p.bypass } } for _, bypassHost := range p.bypassHosts { if bypassHost == host { return p.bypass } } return p.def } // AddFromString parses a string that contains comma-separated values // specifying hosts that should use the bypass proxy. Each value is either an // IP address, a CIDR range, a zone (*.example.com) or a host name // (localhost). A best effort is made to parse the string and errors are // ignored. func (p *proxy_PerHost) AddFromString(s string) { hosts := strings.Split(s, ",") for _, host := range hosts { host = strings.TrimSpace(host) if len(host) == 0 { continue } if strings.Contains(host, "/") { // We assume that it's a CIDR address like 127.0.0.0/8 if _, net, err := net.ParseCIDR(host); err == nil { p.AddNetwork(net) } continue } if ip := net.ParseIP(host); ip != nil { p.AddIP(ip) continue } if strings.HasPrefix(host, "*.") { p.AddZone(host[1:]) continue } p.AddHost(host) } } // AddIP specifies an IP address that will use the bypass proxy. Note that // this will only take effect if a literal IP address is dialed. A connection // to a named host will never match an IP. func (p *proxy_PerHost) AddIP(ip net.IP) { p.bypassIPs = append(p.bypassIPs, ip) } // AddNetwork specifies an IP range that will use the bypass proxy. Note that // this will only take effect if a literal IP address is dialed. A connection // to a named host will never match. func (p *proxy_PerHost) AddNetwork(net *net.IPNet) { p.bypassNetworks = append(p.bypassNetworks, net) } // AddZone specifies a DNS suffix that will use the bypass proxy. A zone of // "example.com" matches "example.com" and all of its subdomains. func (p *proxy_PerHost) AddZone(zone string) { if strings.HasSuffix(zone, ".") { zone = zone[:len(zone)-1] } if !strings.HasPrefix(zone, ".") { zone = "." + zone } p.bypassZones = append(p.bypassZones, zone) } // AddHost specifies a host name that will use the bypass proxy. func (p *proxy_PerHost) AddHost(host string) { if strings.HasSuffix(host, ".") { host = host[:len(host)-1] } p.bypassHosts = append(p.bypassHosts, host) } // A Dialer is a means to establish a connection. type proxy_Dialer interface { // Dial connects to the given address via the proxy. Dial(network, addr string) (c net.Conn, err error) } // Auth contains authentication parameters that specific Dialers may require. type proxy_Auth struct { User, Password string } // FromEnvironment returns the dialer specified by the proxy related variables in // the environment. func proxy_FromEnvironment() proxy_Dialer { allProxy := proxy_allProxyEnv.Get() if len(allProxy) == 0 { return proxy_Direct } proxyURL, err := url.Parse(allProxy) if err != nil { return proxy_Direct } proxy, err := proxy_FromURL(proxyURL, proxy_Direct) if err != nil { return proxy_Direct } noProxy := proxy_noProxyEnv.Get() if len(noProxy) == 0 { return proxy } perHost := proxy_NewPerHost(proxy, proxy_Direct) perHost.AddFromString(noProxy) return perHost } // proxySchemes is a map from URL schemes to a function that creates a Dialer // from a URL with such a scheme. var proxy_proxySchemes map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error) // RegisterDialerType takes a URL scheme and a function to generate Dialers from // a URL with that scheme and a forwarding Dialer. Registered schemes are used // by FromURL. func proxy_RegisterDialerType(scheme string, f func(*url.URL, proxy_Dialer) (proxy_Dialer, error)) { if proxy_proxySchemes == nil { proxy_proxySchemes = make(map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error)) } proxy_proxySchemes[scheme] = f } // FromURL returns a Dialer given a URL specification and an underlying // Dialer for it to make network requests. func proxy_FromURL(u *url.URL, forward proxy_Dialer) (proxy_Dialer, error) { var auth *proxy_Auth if u.User != nil { auth = new(proxy_Auth) auth.User = u.User.Username() if p, ok := u.User.Password(); ok { auth.Password = p } } switch u.Scheme { case "socks5": return proxy_SOCKS5("tcp", u.Host, auth, forward) } // If the scheme doesn't match any of the built-in schemes, see if it // was registered by another package. if proxy_proxySchemes != nil { if f, ok := proxy_proxySchemes[u.Scheme]; ok { return f(u, forward) } } return nil, errors.New("proxy: unknown scheme: " + u.Scheme) } var ( proxy_allProxyEnv = &proxy_envOnce{ names: []string{"ALL_PROXY", "all_proxy"}, } proxy_noProxyEnv = &proxy_envOnce{ names: []string{"NO_PROXY", "no_proxy"}, } ) // envOnce looks up an environment variable (optionally by multiple // names) once. It mitigates expensive lookups on some platforms // (e.g. Windows). // (Borrowed from net/http/transport.go) type proxy_envOnce struct { names []string once sync.Once val string } func (e *proxy_envOnce) Get() string { e.once.Do(e.init) return e.val } func (e *proxy_envOnce) init() { for _, n := range e.names { e.val = os.Getenv(n) if e.val != "" { return } } } // SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address // with an optional username and password. See RFC 1928 and RFC 1929. func proxy_SOCKS5(network, addr string, auth *proxy_Auth, forward proxy_Dialer) (proxy_Dialer, error) { s := &proxy_socks5{ network: network, addr: addr, forward: forward, } if auth != nil { s.user = auth.User s.password = auth.Password } return s, nil } type proxy_socks5 struct { user, password string network, addr string forward proxy_Dialer } const proxy_socks5Version = 5 const ( proxy_socks5AuthNone = 0 proxy_socks5AuthPassword = 2 ) const proxy_socks5Connect = 1 const ( proxy_socks5IP4 = 1 proxy_socks5Domain = 3 proxy_socks5IP6 = 4 ) var proxy_socks5Errors = []string{ "", "general failure", "connection forbidden", "network unreachable", "host unreachable", "connection refused", "TTL expired", "command not supported", "address type not supported", } // Dial connects to the address addr on the given network via the SOCKS5 proxy. func (s *proxy_socks5) Dial(network, addr string) (net.Conn, error) { switch network { case "tcp", "tcp6", "tcp4": default: return nil, errors.New("proxy: no support for SOCKS5 proxy connections of type " + network) } conn, err := s.forward.Dial(s.network, s.addr) if err != nil { return nil, err } if err := s.connect(conn, addr); err != nil { conn.Close() return nil, err } return conn, nil } // connect takes an existing connection to a socks5 proxy server, // and commands the server to extend that connection to target, // which must be a canonical address with a host and port. func (s *proxy_socks5) connect(conn net.Conn, target string) error { host, portStr, err := net.SplitHostPort(target) if err != nil { return err } port, err := strconv.Atoi(portStr) if err != nil { return errors.New("proxy: failed to parse port number: " + portStr) } if port < 1 || port > 0xffff { return errors.New("proxy: port number out of range: " + portStr) } // the size here is just an estimate buf := make([]byte, 0, 6+len(host)) buf = append(buf, proxy_socks5Version) if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 { buf = append(buf, 2 /* num auth methods */, proxy_socks5AuthNone, proxy_socks5AuthPassword) } else { buf = append(buf, 1 /* num auth methods */, proxy_socks5AuthNone) } if _, err := conn.Write(buf); err != nil { return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error()) } if _, err := io.ReadFull(conn, buf[:2]); err != nil { return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error()) } if buf[0] != 5 { return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0]))) } if buf[1] == 0xff { return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication") } // See RFC 1929 if buf[1] == proxy_socks5AuthPassword { buf = buf[:0] buf = append(buf, 1 /* password protocol version */) buf = append(buf, uint8(len(s.user))) buf = append(buf, s.user...) buf = append(buf, uint8(len(s.password))) buf = append(buf, s.password...) if _, err := conn.Write(buf); err != nil { return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) } if _, err := io.ReadFull(conn, buf[:2]); err != nil { return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) } if buf[1] != 0 { return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password") } } buf = buf[:0] buf = append(buf, proxy_socks5Version, proxy_socks5Connect, 0 /* reserved */) if ip := net.ParseIP(host); ip != nil { if ip4 := ip.To4(); ip4 != nil { buf = append(buf, proxy_socks5IP4) ip = ip4 } else { buf = append(buf, proxy_socks5IP6) } buf = append(buf, ip...) } else { if len(host) > 255 { return errors.New("proxy: destination host name too long: " + host) } buf = append(buf, proxy_socks5Domain) buf = append(buf, byte(len(host))) buf = append(buf, host...) } buf = append(buf, byte(port>>8), byte(port)) if _, err := conn.Write(buf); err != nil { return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) } if _, err := io.ReadFull(conn, buf[:4]); err != nil { return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) } failure := "unknown error" if int(buf[1]) < len(proxy_socks5Errors) { failure = proxy_socks5Errors[buf[1]] } if len(failure) > 0 { return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure) } bytesToDiscard := 0 switch buf[3] { case proxy_socks5IP4: bytesToDiscard = net.IPv4len case proxy_socks5IP6: bytesToDiscard = net.IPv6len case proxy_socks5Domain: _, err := io.ReadFull(conn, buf[:1]) if err != nil { return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error()) } bytesToDiscard = int(buf[0]) default: return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr) } if cap(buf) < bytesToDiscard { buf = make([]byte, bytesToDiscard) } else { buf = buf[:bytesToDiscard] } if _, err := io.ReadFull(conn, buf); err != nil { return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error()) } // Also need to discard the port number if _, err := io.ReadFull(conn, buf[:2]); err != nil { return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error()) } return nil } ================================================ FILE: vendor/github.com/gregjones/httpcache/.travis.yml ================================================ sudo: false language: go go: - 1.6.x - 1.7.x - 1.8.x - 1.9.x - master matrix: allow_failures: - go: master fast_finish: true install: - # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step). script: - go get -t -v ./... - diff -u <(echo -n) <(gofmt -d .) - go tool vet . - go test -v -race ./... ================================================ FILE: vendor/github.com/gregjones/httpcache/LICENSE.txt ================================================ Copyright © 2012 Greg Jones (greg.jones@gmail.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/gregjones/httpcache/README.md ================================================ httpcache ========= [![Build Status](https://travis-ci.org/gregjones/httpcache.svg?branch=master)](https://travis-ci.org/gregjones/httpcache) [![GoDoc](https://godoc.org/github.com/gregjones/httpcache?status.svg)](https://godoc.org/github.com/gregjones/httpcache) Package httpcache provides a http.RoundTripper implementation that works as a mostly [RFC 7234](https://tools.ietf.org/html/rfc7234) compliant cache for http responses. It is only suitable for use as a 'private' cache (i.e. for a web-browser or an API-client and not for a shared proxy). Cache Backends -------------- - The built-in 'memory' cache stores responses in an in-memory map. - [`github.com/gregjones/httpcache/diskcache`](https://github.com/gregjones/httpcache/tree/master/diskcache) provides a filesystem-backed cache using the [diskv](https://github.com/peterbourgon/diskv) library. - [`github.com/gregjones/httpcache/memcache`](https://github.com/gregjones/httpcache/tree/master/memcache) provides memcache implementations, for both App Engine and 'normal' memcache servers. - [`sourcegraph.com/sourcegraph/s3cache`](https://sourcegraph.com/github.com/sourcegraph/s3cache) uses Amazon S3 for storage. - [`github.com/gregjones/httpcache/leveldbcache`](https://github.com/gregjones/httpcache/tree/master/leveldbcache) provides a filesystem-backed cache using [leveldb](https://github.com/syndtr/goleveldb/leveldb). - [`github.com/die-net/lrucache`](https://github.com/die-net/lrucache) provides an in-memory cache that will evict least-recently used entries. - [`github.com/die-net/lrucache/twotier`](https://github.com/die-net/lrucache/tree/master/twotier) allows caches to be combined, for example to use lrucache above with a persistent disk-cache. - [`github.com/birkelund/boltdbcache`](https://github.com/birkelund/boltdbcache) provides a BoltDB implementation (based on the [bbolt](https://github.com/coreos/bbolt) fork). License ------- - [MIT License](LICENSE.txt) ================================================ FILE: vendor/github.com/gregjones/httpcache/httpcache.go ================================================ // Package httpcache provides a http.RoundTripper implementation that works as a // mostly RFC-compliant cache for http responses. // // It is only suitable for use as a 'private' cache (i.e. for a web-browser or an API-client // and not for a shared proxy). // package httpcache import ( "bufio" "bytes" "errors" "io" "io/ioutil" "net/http" "net/http/httputil" "strings" "sync" "time" ) const ( stale = iota fresh transparent // XFromCache is the header added to responses that are returned from the cache XFromCache = "X-From-Cache" ) // A Cache interface is used by the Transport to store and retrieve responses. type Cache interface { // Get returns the []byte representation of a cached response and a bool // set to true if the value isn't empty Get(key string) (responseBytes []byte, ok bool) // Set stores the []byte representation of a response against a key Set(key string, responseBytes []byte) // Delete removes the value associated with the key Delete(key string) } // cacheKey returns the cache key for req. func cacheKey(req *http.Request) string { if req.Method == http.MethodGet { return req.URL.String() } else { return req.Method + " " + req.URL.String() } } // CachedResponse returns the cached http.Response for req if present, and nil // otherwise. func CachedResponse(c Cache, req *http.Request) (resp *http.Response, err error) { cachedVal, ok := c.Get(cacheKey(req)) if !ok { return } b := bytes.NewBuffer(cachedVal) return http.ReadResponse(bufio.NewReader(b), req) } // MemoryCache is an implemtation of Cache that stores responses in an in-memory map. type MemoryCache struct { mu sync.RWMutex items map[string][]byte } // Get returns the []byte representation of the response and true if present, false if not func (c *MemoryCache) Get(key string) (resp []byte, ok bool) { c.mu.RLock() resp, ok = c.items[key] c.mu.RUnlock() return resp, ok } // Set saves response resp to the cache with key func (c *MemoryCache) Set(key string, resp []byte) { c.mu.Lock() c.items[key] = resp c.mu.Unlock() } // Delete removes key from the cache func (c *MemoryCache) Delete(key string) { c.mu.Lock() delete(c.items, key) c.mu.Unlock() } // NewMemoryCache returns a new Cache that will store items in an in-memory map func NewMemoryCache() *MemoryCache { c := &MemoryCache{items: map[string][]byte{}} return c } // Transport is an implementation of http.RoundTripper that will return values from a cache // where possible (avoiding a network request) and will additionally add validators (etag/if-modified-since) // to repeated requests allowing servers to return 304 / Not Modified type Transport struct { // The RoundTripper interface actually used to make requests // If nil, http.DefaultTransport is used Transport http.RoundTripper Cache Cache // If true, responses returned from the cache will be given an extra header, X-From-Cache MarkCachedResponses bool } // NewTransport returns a new Transport with the // provided Cache implementation and MarkCachedResponses set to true func NewTransport(c Cache) *Transport { return &Transport{Cache: c, MarkCachedResponses: true} } // Client returns an *http.Client that caches responses. func (t *Transport) Client() *http.Client { return &http.Client{Transport: t} } // varyMatches will return false unless all of the cached values for the headers listed in Vary // match the new request func varyMatches(cachedResp *http.Response, req *http.Request) bool { for _, header := range headerAllCommaSepValues(cachedResp.Header, "vary") { header = http.CanonicalHeaderKey(header) if header != "" && req.Header.Get(header) != cachedResp.Header.Get("X-Varied-"+header) { return false } } return true } // RoundTrip takes a Request and returns a Response // // If there is a fresh Response already in cache, then it will be returned without connecting to // the server. // // If there is a stale Response, then any validators it contains will be set on the new request // to give the server a chance to respond with NotModified. If this happens, then the cached Response // will be returned. func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error) { cacheKey := cacheKey(req) cacheable := (req.Method == "GET" || req.Method == "HEAD") && req.Header.Get("range") == "" var cachedResp *http.Response if cacheable { cachedResp, err = CachedResponse(t.Cache, req) } else { // Need to invalidate an existing value t.Cache.Delete(cacheKey) } transport := t.Transport if transport == nil { transport = http.DefaultTransport } if cacheable && cachedResp != nil && err == nil { if t.MarkCachedResponses { cachedResp.Header.Set(XFromCache, "1") } if varyMatches(cachedResp, req) { // Can only use cached value if the new request doesn't Vary significantly freshness := getFreshness(cachedResp.Header, req.Header) if freshness == fresh { return cachedResp, nil } if freshness == stale { var req2 *http.Request // Add validators if caller hasn't already done so etag := cachedResp.Header.Get("etag") if etag != "" && req.Header.Get("etag") == "" { req2 = cloneRequest(req) req2.Header.Set("if-none-match", etag) } lastModified := cachedResp.Header.Get("last-modified") if lastModified != "" && req.Header.Get("last-modified") == "" { if req2 == nil { req2 = cloneRequest(req) } req2.Header.Set("if-modified-since", lastModified) } if req2 != nil { req = req2 } } } resp, err = transport.RoundTrip(req) if err == nil && req.Method == "GET" && resp.StatusCode == http.StatusNotModified { // Replace the 304 response with the one from cache, but update with some new headers endToEndHeaders := getEndToEndHeaders(resp.Header) for _, header := range endToEndHeaders { cachedResp.Header[header] = resp.Header[header] } resp = cachedResp } else if (err != nil || (cachedResp != nil && resp.StatusCode >= 500)) && req.Method == "GET" && canStaleOnError(cachedResp.Header, req.Header) { // In case of transport failure and stale-if-error activated, returns cached content // when available return cachedResp, nil } else { if err != nil || resp.StatusCode != http.StatusOK { t.Cache.Delete(cacheKey) } if err != nil { return nil, err } } } else { reqCacheControl := parseCacheControl(req.Header) if _, ok := reqCacheControl["only-if-cached"]; ok { resp = newGatewayTimeoutResponse(req) } else { resp, err = transport.RoundTrip(req) if err != nil { return nil, err } } } if cacheable && canStore(parseCacheControl(req.Header), parseCacheControl(resp.Header)) { for _, varyKey := range headerAllCommaSepValues(resp.Header, "vary") { varyKey = http.CanonicalHeaderKey(varyKey) fakeHeader := "X-Varied-" + varyKey reqValue := req.Header.Get(varyKey) if reqValue != "" { resp.Header.Set(fakeHeader, reqValue) } } switch req.Method { case "GET": // Delay caching until EOF is reached. resp.Body = &cachingReadCloser{ R: resp.Body, OnEOF: func(r io.Reader) { resp := *resp resp.Body = ioutil.NopCloser(r) respBytes, err := httputil.DumpResponse(&resp, true) if err == nil { t.Cache.Set(cacheKey, respBytes) } }, } default: respBytes, err := httputil.DumpResponse(resp, true) if err == nil { t.Cache.Set(cacheKey, respBytes) } } } else { t.Cache.Delete(cacheKey) } return resp, nil } // ErrNoDateHeader indicates that the HTTP headers contained no Date header. var ErrNoDateHeader = errors.New("no Date header") // Date parses and returns the value of the Date header. func Date(respHeaders http.Header) (date time.Time, err error) { dateHeader := respHeaders.Get("date") if dateHeader == "" { err = ErrNoDateHeader return } return time.Parse(time.RFC1123, dateHeader) } type realClock struct{} func (c *realClock) since(d time.Time) time.Duration { return time.Since(d) } type timer interface { since(d time.Time) time.Duration } var clock timer = &realClock{} // getFreshness will return one of fresh/stale/transparent based on the cache-control // values of the request and the response // // fresh indicates the response can be returned // stale indicates that the response needs validating before it is returned // transparent indicates the response should not be used to fulfil the request // // Because this is only a private cache, 'public' and 'private' in cache-control aren't // signficant. Similarly, smax-age isn't used. func getFreshness(respHeaders, reqHeaders http.Header) (freshness int) { respCacheControl := parseCacheControl(respHeaders) reqCacheControl := parseCacheControl(reqHeaders) if _, ok := reqCacheControl["no-cache"]; ok { return transparent } if _, ok := respCacheControl["no-cache"]; ok { return stale } if _, ok := reqCacheControl["only-if-cached"]; ok { return fresh } date, err := Date(respHeaders) if err != nil { return stale } currentAge := clock.since(date) var lifetime time.Duration var zeroDuration time.Duration // If a response includes both an Expires header and a max-age directive, // the max-age directive overrides the Expires header, even if the Expires header is more restrictive. if maxAge, ok := respCacheControl["max-age"]; ok { lifetime, err = time.ParseDuration(maxAge + "s") if err != nil { lifetime = zeroDuration } } else { expiresHeader := respHeaders.Get("Expires") if expiresHeader != "" { expires, err := time.Parse(time.RFC1123, expiresHeader) if err != nil { lifetime = zeroDuration } else { lifetime = expires.Sub(date) } } } if maxAge, ok := reqCacheControl["max-age"]; ok { // the client is willing to accept a response whose age is no greater than the specified time in seconds lifetime, err = time.ParseDuration(maxAge + "s") if err != nil { lifetime = zeroDuration } } if minfresh, ok := reqCacheControl["min-fresh"]; ok { // the client wants a response that will still be fresh for at least the specified number of seconds. minfreshDuration, err := time.ParseDuration(minfresh + "s") if err == nil { currentAge = time.Duration(currentAge + minfreshDuration) } } if maxstale, ok := reqCacheControl["max-stale"]; ok { // Indicates that the client is willing to accept a response that has exceeded its expiration time. // If max-stale is assigned a value, then the client is willing to accept a response that has exceeded // its expiration time by no more than the specified number of seconds. // If no value is assigned to max-stale, then the client is willing to accept a stale response of any age. // // Responses served only because of a max-stale value are supposed to have a Warning header added to them, // but that seems like a hassle, and is it actually useful? If so, then there needs to be a different // return-value available here. if maxstale == "" { return fresh } maxstaleDuration, err := time.ParseDuration(maxstale + "s") if err == nil { currentAge = time.Duration(currentAge - maxstaleDuration) } } if lifetime > currentAge { return fresh } return stale } // Returns true if either the request or the response includes the stale-if-error // cache control extension: https://tools.ietf.org/html/rfc5861 func canStaleOnError(respHeaders, reqHeaders http.Header) bool { respCacheControl := parseCacheControl(respHeaders) reqCacheControl := parseCacheControl(reqHeaders) var err error lifetime := time.Duration(-1) if staleMaxAge, ok := respCacheControl["stale-if-error"]; ok { if staleMaxAge != "" { lifetime, err = time.ParseDuration(staleMaxAge + "s") if err != nil { return false } } else { return true } } if staleMaxAge, ok := reqCacheControl["stale-if-error"]; ok { if staleMaxAge != "" { lifetime, err = time.ParseDuration(staleMaxAge + "s") if err != nil { return false } } else { return true } } if lifetime >= 0 { date, err := Date(respHeaders) if err != nil { return false } currentAge := clock.since(date) if lifetime > currentAge { return true } } return false } func getEndToEndHeaders(respHeaders http.Header) []string { // These headers are always hop-by-hop hopByHopHeaders := map[string]struct{}{ "Connection": struct{}{}, "Keep-Alive": struct{}{}, "Proxy-Authenticate": struct{}{}, "Proxy-Authorization": struct{}{}, "Te": struct{}{}, "Trailers": struct{}{}, "Transfer-Encoding": struct{}{}, "Upgrade": struct{}{}, } for _, extra := range strings.Split(respHeaders.Get("connection"), ",") { // any header listed in connection, if present, is also considered hop-by-hop if strings.Trim(extra, " ") != "" { hopByHopHeaders[http.CanonicalHeaderKey(extra)] = struct{}{} } } endToEndHeaders := []string{} for respHeader, _ := range respHeaders { if _, ok := hopByHopHeaders[respHeader]; !ok { endToEndHeaders = append(endToEndHeaders, respHeader) } } return endToEndHeaders } func canStore(reqCacheControl, respCacheControl cacheControl) (canStore bool) { if _, ok := respCacheControl["no-store"]; ok { return false } if _, ok := reqCacheControl["no-store"]; ok { return false } return true } func newGatewayTimeoutResponse(req *http.Request) *http.Response { var braw bytes.Buffer braw.WriteString("HTTP/1.1 504 Gateway Timeout\r\n\r\n") resp, err := http.ReadResponse(bufio.NewReader(&braw), req) if err != nil { panic(err) } return resp } // cloneRequest returns a clone of the provided *http.Request. // The clone is a shallow copy of the struct and its Header map. // (This function copyright goauth2 authors: https://code.google.com/p/goauth2) func cloneRequest(r *http.Request) *http.Request { // shallow copy of the struct r2 := new(http.Request) *r2 = *r // deep copy of the Header r2.Header = make(http.Header) for k, s := range r.Header { r2.Header[k] = s } return r2 } type cacheControl map[string]string func parseCacheControl(headers http.Header) cacheControl { cc := cacheControl{} ccHeader := headers.Get("Cache-Control") for _, part := range strings.Split(ccHeader, ",") { part = strings.Trim(part, " ") if part == "" { continue } if strings.ContainsRune(part, '=') { keyval := strings.Split(part, "=") cc[strings.Trim(keyval[0], " ")] = strings.Trim(keyval[1], ",") } else { cc[part] = "" } } return cc } // headerAllCommaSepValues returns all comma-separated values (each // with whitespace trimmed) for header name in headers. According to // Section 4.2 of the HTTP/1.1 spec // (http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2), // values from multiple occurrences of a header should be concatenated, if // the header's value is a comma-separated list. func headerAllCommaSepValues(headers http.Header, name string) []string { var vals []string for _, val := range headers[http.CanonicalHeaderKey(name)] { fields := strings.Split(val, ",") for i, f := range fields { fields[i] = strings.TrimSpace(f) } vals = append(vals, fields...) } return vals } // cachingReadCloser is a wrapper around ReadCloser R that calls OnEOF // handler with a full copy of the content read from R when EOF is // reached. type cachingReadCloser struct { // Underlying ReadCloser. R io.ReadCloser // OnEOF is called with a copy of the content of R when EOF is reached. OnEOF func(io.Reader) buf bytes.Buffer // buf stores a copy of the content of R. } // Read reads the next len(p) bytes from R or until R is drained. The // return value n is the number of bytes read. If R has no data to // return, err is io.EOF and OnEOF is called with a full copy of what // has been read so far. func (r *cachingReadCloser) Read(p []byte) (n int, err error) { n, err = r.R.Read(p) r.buf.Write(p[:n]) if err == io.EOF { r.OnEOF(bytes.NewReader(r.buf.Bytes())) } return n, err } func (r *cachingReadCloser) Close() error { return r.R.Close() } // NewMemoryCacheTransport returns a new Transport using the in-memory cache implementation func NewMemoryCacheTransport() *Transport { c := NewMemoryCache() t := NewTransport(c) return t } ================================================ FILE: vendor/github.com/imdario/mergo/.deepsource.toml ================================================ version = 1 test_patterns = [ "*_test.go" ] [[analyzers]] name = "go" enabled = true [analyzers.meta] import_path = "github.com/imdario/mergo" ================================================ FILE: vendor/github.com/imdario/mergo/.gitignore ================================================ #### joe made this: http://goel.io/joe #### go #### # Binaries for programs and plugins *.exe *.dll *.so *.dylib # Test binary, build with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 .glide/ #### vim #### # Swap [._]*.s[a-v][a-z] [._]*.sw[a-p] [._]s[a-v][a-z] [._]sw[a-p] # Session Session.vim # Temporary .netrwhist *~ # Auto-generated tag files tags ================================================ FILE: vendor/github.com/imdario/mergo/.travis.yml ================================================ language: go arch: - amd64 - ppc64le install: - go get -t - go get golang.org/x/tools/cmd/cover - go get github.com/mattn/goveralls script: - go test -race -v ./... after_script: - $HOME/gopath/bin/goveralls -service=travis-ci -repotoken $COVERALLS_TOKEN ================================================ FILE: vendor/github.com/imdario/mergo/CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at i@dario.im. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ ================================================ FILE: vendor/github.com/imdario/mergo/LICENSE ================================================ Copyright (c) 2013 Dario Castañé. All rights reserved. Copyright (c) 2012 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/imdario/mergo/README.md ================================================ # Mergo [![GoDoc][3]][4] [![GitHub release][5]][6] [![GoCard][7]][8] [![Build Status][1]][2] [![Coverage Status][9]][10] [![Sourcegraph][11]][12] [![FOSSA Status][13]][14] [![Become my sponsor][15]][16] [1]: https://travis-ci.org/imdario/mergo.png [2]: https://travis-ci.org/imdario/mergo [3]: https://godoc.org/github.com/imdario/mergo?status.svg [4]: https://godoc.org/github.com/imdario/mergo [5]: https://img.shields.io/github/release/imdario/mergo.svg [6]: https://github.com/imdario/mergo/releases [7]: https://goreportcard.com/badge/imdario/mergo [8]: https://goreportcard.com/report/github.com/imdario/mergo [9]: https://coveralls.io/repos/github/imdario/mergo/badge.svg?branch=master [10]: https://coveralls.io/github/imdario/mergo?branch=master [11]: https://sourcegraph.com/github.com/imdario/mergo/-/badge.svg [12]: https://sourcegraph.com/github.com/imdario/mergo?badge [13]: https://app.fossa.io/api/projects/git%2Bgithub.com%2Fimdario%2Fmergo.svg?type=shield [14]: https://app.fossa.io/projects/git%2Bgithub.com%2Fimdario%2Fmergo?ref=badge_shield [15]: https://img.shields.io/github/sponsors/imdario [16]: https://github.com/sponsors/imdario A helper to merge structs and maps in Golang. Useful for configuration default values, avoiding messy if-statements. Mergo merges same-type structs and maps by setting default values in zero-value fields. Mergo won't merge unexported (private) fields. It will do recursively any exported one. It also won't merge structs inside maps (because they are not addressable using Go reflection). Also a lovely [comune](http://en.wikipedia.org/wiki/Mergo) (municipality) in the Province of Ancona in the Italian region of Marche. ## Status It is ready for production use. [It is used in several projects by Docker, Google, The Linux Foundation, VMWare, Shopify, Microsoft, etc](https://github.com/imdario/mergo#mergo-in-the-wild). ### Important note Please keep in mind that a problematic PR broke [0.3.9](//github.com/imdario/mergo/releases/tag/0.3.9). I reverted it in [0.3.10](//github.com/imdario/mergo/releases/tag/0.3.10), and I consider it stable but not bug-free. Also, this version adds support for go modules. Keep in mind that in [0.3.2](//github.com/imdario/mergo/releases/tag/0.3.2), Mergo changed `Merge()`and `Map()` signatures to support [transformers](#transformers). I added an optional/variadic argument so that it won't break the existing code. If you were using Mergo before April 6th, 2015, please check your project works as intended after updating your local copy with ```go get -u github.com/imdario/mergo```. I apologize for any issue caused by its previous behavior and any future bug that Mergo could cause in existing projects after the change (release 0.2.0). ### Donations If Mergo is useful to you, consider buying me a coffee, a beer, or making a monthly donation to allow me to keep building great free software. :heart_eyes: Buy Me a Coffee at ko-fi.com Donate using Liberapay Become my sponsor ### Mergo in the wild - [cli/cli](https://github.com/cli/cli) - [moby/moby](https://github.com/moby/moby) - [kubernetes/kubernetes](https://github.com/kubernetes/kubernetes) - [vmware/dispatch](https://github.com/vmware/dispatch) - [Shopify/themekit](https://github.com/Shopify/themekit) - [imdario/zas](https://github.com/imdario/zas) - [matcornic/hermes](https://github.com/matcornic/hermes) - [OpenBazaar/openbazaar-go](https://github.com/OpenBazaar/openbazaar-go) - [kataras/iris](https://github.com/kataras/iris) - [michaelsauter/crane](https://github.com/michaelsauter/crane) - [go-task/task](https://github.com/go-task/task) - [sensu/uchiwa](https://github.com/sensu/uchiwa) - [ory/hydra](https://github.com/ory/hydra) - [sisatech/vcli](https://github.com/sisatech/vcli) - [dairycart/dairycart](https://github.com/dairycart/dairycart) - [projectcalico/felix](https://github.com/projectcalico/felix) - [resin-os/balena](https://github.com/resin-os/balena) - [go-kivik/kivik](https://github.com/go-kivik/kivik) - [Telefonica/govice](https://github.com/Telefonica/govice) - [supergiant/supergiant](supergiant/supergiant) - [SergeyTsalkov/brooce](https://github.com/SergeyTsalkov/brooce) - [soniah/dnsmadeeasy](https://github.com/soniah/dnsmadeeasy) - [ohsu-comp-bio/funnel](https://github.com/ohsu-comp-bio/funnel) - [EagerIO/Stout](https://github.com/EagerIO/Stout) - [lynndylanhurley/defsynth-api](https://github.com/lynndylanhurley/defsynth-api) - [russross/canvasassignments](https://github.com/russross/canvasassignments) - [rdegges/cryptly-api](https://github.com/rdegges/cryptly-api) - [casualjim/exeggutor](https://github.com/casualjim/exeggutor) - [divshot/gitling](https://github.com/divshot/gitling) - [RWJMurphy/gorl](https://github.com/RWJMurphy/gorl) - [andrerocker/deploy42](https://github.com/andrerocker/deploy42) - [elwinar/rambler](https://github.com/elwinar/rambler) - [tmaiaroto/gopartman](https://github.com/tmaiaroto/gopartman) - [jfbus/impressionist](https://github.com/jfbus/impressionist) - [Jmeyering/zealot](https://github.com/Jmeyering/zealot) - [godep-migrator/rigger-host](https://github.com/godep-migrator/rigger-host) - [Dronevery/MultiwaySwitch-Go](https://github.com/Dronevery/MultiwaySwitch-Go) - [thoas/picfit](https://github.com/thoas/picfit) - [mantasmatelis/whooplist-server](https://github.com/mantasmatelis/whooplist-server) - [jnuthong/item_search](https://github.com/jnuthong/item_search) - [bukalapak/snowboard](https://github.com/bukalapak/snowboard) - [containerssh/containerssh](https://github.com/containerssh/containerssh) - [goreleaser/goreleaser](https://github.com/goreleaser/goreleaser) - [tjpnz/structbot](https://github.com/tjpnz/structbot) ## Install go get github.com/imdario/mergo // use in your .go code import ( "github.com/imdario/mergo" ) ## Usage You can only merge same-type structs with exported fields initialized as zero value of their type and same-types maps. Mergo won't merge unexported (private) fields but will do recursively any exported one. It won't merge empty structs value as [they are zero values](https://golang.org/ref/spec#The_zero_value) too. Also, maps will be merged recursively except for structs inside maps (because they are not addressable using Go reflection). ```go if err := mergo.Merge(&dst, src); err != nil { // ... } ``` Also, you can merge overwriting values using the transformer `WithOverride`. ```go if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil { // ... } ``` Additionally, you can map a `map[string]interface{}` to a struct (and otherwise, from struct to map), following the same restrictions as in `Merge()`. Keys are capitalized to find each corresponding exported field. ```go if err := mergo.Map(&dst, srcMap); err != nil { // ... } ``` Warning: if you map a struct to map, it won't do it recursively. Don't expect Mergo to map struct members of your struct as `map[string]interface{}`. They will be just assigned as values. Here is a nice example: ```go package main import ( "fmt" "github.com/imdario/mergo" ) type Foo struct { A string B int64 } func main() { src := Foo{ A: "one", B: 2, } dest := Foo{ A: "two", } mergo.Merge(&dest, src) fmt.Println(dest) // Will print // {two 2} } ``` Note: if test are failing due missing package, please execute: go get gopkg.in/yaml.v3 ### Transformers Transformers allow to merge specific types differently than in the default behavior. In other words, now you can customize how some types are merged. For example, `time.Time` is a struct; it doesn't have zero value but IsZero can return true because it has fields with zero value. How can we merge a non-zero `time.Time`? ```go package main import ( "fmt" "github.com/imdario/mergo" "reflect" "time" ) type timeTransformer struct { } func (t timeTransformer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error { if typ == reflect.TypeOf(time.Time{}) { return func(dst, src reflect.Value) error { if dst.CanSet() { isZero := dst.MethodByName("IsZero") result := isZero.Call([]reflect.Value{}) if result[0].Bool() { dst.Set(src) } } return nil } } return nil } type Snapshot struct { Time time.Time // ... } func main() { src := Snapshot{time.Now()} dest := Snapshot{} mergo.Merge(&dest, src, mergo.WithTransformers(timeTransformer{})) fmt.Println(dest) // Will print // { 2018-01-12 01:15:00 +0000 UTC m=+0.000000001 } } ``` ## Contact me If I can help you, you have an idea or you are using Mergo in your projects, don't hesitate to drop me a line (or a pull request): [@im_dario](https://twitter.com/im_dario) ## About Written by [Dario Castañé](http://dario.im). ## License [BSD 3-Clause](http://opensource.org/licenses/BSD-3-Clause) license, as [Go language](http://golang.org/LICENSE). [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fimdario%2Fmergo.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fimdario%2Fmergo?ref=badge_large) ================================================ FILE: vendor/github.com/imdario/mergo/doc.go ================================================ // Copyright 2013 Dario Castañé. All rights reserved. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. /* A helper to merge structs and maps in Golang. Useful for configuration default values, avoiding messy if-statements. Mergo merges same-type structs and maps by setting default values in zero-value fields. Mergo won't merge unexported (private) fields. It will do recursively any exported one. It also won't merge structs inside maps (because they are not addressable using Go reflection). Status It is ready for production use. It is used in several projects by Docker, Google, The Linux Foundation, VMWare, Shopify, etc. Important note Please keep in mind that a problematic PR broke 0.3.9. We reverted it in 0.3.10. We consider 0.3.10 as stable but not bug-free. . Also, this version adds suppot for go modules. Keep in mind that in 0.3.2, Mergo changed Merge() and Map() signatures to support transformers. We added an optional/variadic argument so that it won't break the existing code. If you were using Mergo before April 6th, 2015, please check your project works as intended after updating your local copy with go get -u github.com/imdario/mergo. I apologize for any issue caused by its previous behavior and any future bug that Mergo could cause in existing projects after the change (release 0.2.0). Install Do your usual installation procedure: go get github.com/imdario/mergo // use in your .go code import ( "github.com/imdario/mergo" ) Usage You can only merge same-type structs with exported fields initialized as zero value of their type and same-types maps. Mergo won't merge unexported (private) fields but will do recursively any exported one. It won't merge empty structs value as they are zero values too. Also, maps will be merged recursively except for structs inside maps (because they are not addressable using Go reflection). if err := mergo.Merge(&dst, src); err != nil { // ... } Also, you can merge overwriting values using the transformer WithOverride. if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil { // ... } Additionally, you can map a map[string]interface{} to a struct (and otherwise, from struct to map), following the same restrictions as in Merge(). Keys are capitalized to find each corresponding exported field. if err := mergo.Map(&dst, srcMap); err != nil { // ... } Warning: if you map a struct to map, it won't do it recursively. Don't expect Mergo to map struct members of your struct as map[string]interface{}. They will be just assigned as values. Here is a nice example: package main import ( "fmt" "github.com/imdario/mergo" ) type Foo struct { A string B int64 } func main() { src := Foo{ A: "one", B: 2, } dest := Foo{ A: "two", } mergo.Merge(&dest, src) fmt.Println(dest) // Will print // {two 2} } Transformers Transformers allow to merge specific types differently than in the default behavior. In other words, now you can customize how some types are merged. For example, time.Time is a struct; it doesn't have zero value but IsZero can return true because it has fields with zero value. How can we merge a non-zero time.Time? package main import ( "fmt" "github.com/imdario/mergo" "reflect" "time" ) type timeTransformer struct { } func (t timeTransformer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error { if typ == reflect.TypeOf(time.Time{}) { return func(dst, src reflect.Value) error { if dst.CanSet() { isZero := dst.MethodByName("IsZero") result := isZero.Call([]reflect.Value{}) if result[0].Bool() { dst.Set(src) } } return nil } } return nil } type Snapshot struct { Time time.Time // ... } func main() { src := Snapshot{time.Now()} dest := Snapshot{} mergo.Merge(&dest, src, mergo.WithTransformers(timeTransformer{})) fmt.Println(dest) // Will print // { 2018-01-12 01:15:00 +0000 UTC m=+0.000000001 } } Contact me If I can help you, you have an idea or you are using Mergo in your projects, don't hesitate to drop me a line (or a pull request): https://twitter.com/im_dario About Written by Dario Castañé: https://da.rio.hn License BSD 3-Clause license, as Go language. */ package mergo ================================================ FILE: vendor/github.com/imdario/mergo/map.go ================================================ // Copyright 2014 Dario Castañé. All rights reserved. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Based on src/pkg/reflect/deepequal.go from official // golang's stdlib. package mergo import ( "fmt" "reflect" "unicode" "unicode/utf8" ) func changeInitialCase(s string, mapper func(rune) rune) string { if s == "" { return s } r, n := utf8.DecodeRuneInString(s) return string(mapper(r)) + s[n:] } func isExported(field reflect.StructField) bool { r, _ := utf8.DecodeRuneInString(field.Name) return r >= 'A' && r <= 'Z' } // Traverses recursively both values, assigning src's fields values to dst. // The map argument tracks comparisons that have already been seen, which allows // short circuiting on recursive types. func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) { overwrite := config.Overwrite if dst.CanAddr() { addr := dst.UnsafeAddr() h := 17 * addr seen := visited[h] typ := dst.Type() for p := seen; p != nil; p = p.next { if p.ptr == addr && p.typ == typ { return nil } } // Remember, remember... visited[h] = &visit{addr, typ, seen} } zeroValue := reflect.Value{} switch dst.Kind() { case reflect.Map: dstMap := dst.Interface().(map[string]interface{}) for i, n := 0, src.NumField(); i < n; i++ { srcType := src.Type() field := srcType.Field(i) if !isExported(field) { continue } fieldName := field.Name fieldName = changeInitialCase(fieldName, unicode.ToLower) if v, ok := dstMap[fieldName]; !ok || (isEmptyValue(reflect.ValueOf(v)) || overwrite) { dstMap[fieldName] = src.Field(i).Interface() } } case reflect.Ptr: if dst.IsNil() { v := reflect.New(dst.Type().Elem()) dst.Set(v) } dst = dst.Elem() fallthrough case reflect.Struct: srcMap := src.Interface().(map[string]interface{}) for key := range srcMap { config.overwriteWithEmptyValue = true srcValue := srcMap[key] fieldName := changeInitialCase(key, unicode.ToUpper) dstElement := dst.FieldByName(fieldName) if dstElement == zeroValue { // We discard it because the field doesn't exist. continue } srcElement := reflect.ValueOf(srcValue) dstKind := dstElement.Kind() srcKind := srcElement.Kind() if srcKind == reflect.Ptr && dstKind != reflect.Ptr { srcElement = srcElement.Elem() srcKind = reflect.TypeOf(srcElement.Interface()).Kind() } else if dstKind == reflect.Ptr { // Can this work? I guess it can't. if srcKind != reflect.Ptr && srcElement.CanAddr() { srcPtr := srcElement.Addr() srcElement = reflect.ValueOf(srcPtr) srcKind = reflect.Ptr } } if !srcElement.IsValid() { continue } if srcKind == dstKind { if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil { return } } else if dstKind == reflect.Interface && dstElement.Kind() == reflect.Interface { if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil { return } } else if srcKind == reflect.Map { if err = deepMap(dstElement, srcElement, visited, depth+1, config); err != nil { return } } else { return fmt.Errorf("type mismatch on %s field: found %v, expected %v", fieldName, srcKind, dstKind) } } } return } // Map sets fields' values in dst from src. // src can be a map with string keys or a struct. dst must be the opposite: // if src is a map, dst must be a valid pointer to struct. If src is a struct, // dst must be map[string]interface{}. // It won't merge unexported (private) fields and will do recursively // any exported field. // If dst is a map, keys will be src fields' names in lower camel case. // Missing key in src that doesn't match a field in dst will be skipped. This // doesn't apply if dst is a map. // This is separated method from Merge because it is cleaner and it keeps sane // semantics: merging equal types, mapping different (restricted) types. func Map(dst, src interface{}, opts ...func(*Config)) error { return _map(dst, src, opts...) } // MapWithOverwrite will do the same as Map except that non-empty dst attributes will be overridden by // non-empty src attribute values. // Deprecated: Use Map(…) with WithOverride func MapWithOverwrite(dst, src interface{}, opts ...func(*Config)) error { return _map(dst, src, append(opts, WithOverride)...) } func _map(dst, src interface{}, opts ...func(*Config)) error { if dst != nil && reflect.ValueOf(dst).Kind() != reflect.Ptr { return ErrNonPointerAgument } var ( vDst, vSrc reflect.Value err error ) config := &Config{} for _, opt := range opts { opt(config) } if vDst, vSrc, err = resolveValues(dst, src); err != nil { return err } // To be friction-less, we redirect equal-type arguments // to deepMerge. Only because arguments can be anything. if vSrc.Kind() == vDst.Kind() { return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config) } switch vSrc.Kind() { case reflect.Struct: if vDst.Kind() != reflect.Map { return ErrExpectedMapAsDestination } case reflect.Map: if vDst.Kind() != reflect.Struct { return ErrExpectedStructAsDestination } default: return ErrNotSupported } return deepMap(vDst, vSrc, make(map[uintptr]*visit), 0, config) } ================================================ FILE: vendor/github.com/imdario/mergo/merge.go ================================================ // Copyright 2013 Dario Castañé. All rights reserved. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Based on src/pkg/reflect/deepequal.go from official // golang's stdlib. package mergo import ( "fmt" "reflect" ) func hasMergeableFields(dst reflect.Value) (exported bool) { for i, n := 0, dst.NumField(); i < n; i++ { field := dst.Type().Field(i) if field.Anonymous && dst.Field(i).Kind() == reflect.Struct { exported = exported || hasMergeableFields(dst.Field(i)) } else if isExportedComponent(&field) { exported = exported || len(field.PkgPath) == 0 } } return } func isExportedComponent(field *reflect.StructField) bool { pkgPath := field.PkgPath if len(pkgPath) > 0 { return false } c := field.Name[0] if 'a' <= c && c <= 'z' || c == '_' { return false } return true } type Config struct { Overwrite bool AppendSlice bool TypeCheck bool Transformers Transformers overwriteWithEmptyValue bool overwriteSliceWithEmptyValue bool sliceDeepCopy bool debug bool } type Transformers interface { Transformer(reflect.Type) func(dst, src reflect.Value) error } // Traverses recursively both values, assigning src's fields values to dst. // The map argument tracks comparisons that have already been seen, which allows // short circuiting on recursive types. func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) { overwrite := config.Overwrite typeCheck := config.TypeCheck overwriteWithEmptySrc := config.overwriteWithEmptyValue overwriteSliceWithEmptySrc := config.overwriteSliceWithEmptyValue sliceDeepCopy := config.sliceDeepCopy if !src.IsValid() { return } if dst.CanAddr() { addr := dst.UnsafeAddr() h := 17 * addr seen := visited[h] typ := dst.Type() for p := seen; p != nil; p = p.next { if p.ptr == addr && p.typ == typ { return nil } } // Remember, remember... visited[h] = &visit{addr, typ, seen} } if config.Transformers != nil && !isReflectNil(dst) && dst.IsValid() { if fn := config.Transformers.Transformer(dst.Type()); fn != nil { err = fn(dst, src) return } } switch dst.Kind() { case reflect.Struct: if hasMergeableFields(dst) { for i, n := 0, dst.NumField(); i < n; i++ { if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, config); err != nil { return } } } else { if dst.CanSet() && (isReflectNil(dst) || overwrite) && (!isEmptyValue(src) || overwriteWithEmptySrc) { dst.Set(src) } } case reflect.Map: if dst.IsNil() && !src.IsNil() { if dst.CanSet() { dst.Set(reflect.MakeMap(dst.Type())) } else { dst = src return } } if src.Kind() != reflect.Map { if overwrite { dst.Set(src) } return } for _, key := range src.MapKeys() { srcElement := src.MapIndex(key) if !srcElement.IsValid() { continue } dstElement := dst.MapIndex(key) switch srcElement.Kind() { case reflect.Chan, reflect.Func, reflect.Map, reflect.Interface, reflect.Slice: if srcElement.IsNil() { if overwrite { dst.SetMapIndex(key, srcElement) } continue } fallthrough default: if !srcElement.CanInterface() { continue } switch reflect.TypeOf(srcElement.Interface()).Kind() { case reflect.Struct: fallthrough case reflect.Ptr: fallthrough case reflect.Map: srcMapElm := srcElement dstMapElm := dstElement if srcMapElm.CanInterface() { srcMapElm = reflect.ValueOf(srcMapElm.Interface()) if dstMapElm.IsValid() { dstMapElm = reflect.ValueOf(dstMapElm.Interface()) } } if err = deepMerge(dstMapElm, srcMapElm, visited, depth+1, config); err != nil { return } case reflect.Slice: srcSlice := reflect.ValueOf(srcElement.Interface()) var dstSlice reflect.Value if !dstElement.IsValid() || dstElement.IsNil() { dstSlice = reflect.MakeSlice(srcSlice.Type(), 0, srcSlice.Len()) } else { dstSlice = reflect.ValueOf(dstElement.Interface()) } if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice && !sliceDeepCopy { if typeCheck && srcSlice.Type() != dstSlice.Type() { return fmt.Errorf("cannot override two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type()) } dstSlice = srcSlice } else if config.AppendSlice { if srcSlice.Type() != dstSlice.Type() { return fmt.Errorf("cannot append two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type()) } dstSlice = reflect.AppendSlice(dstSlice, srcSlice) } else if sliceDeepCopy { i := 0 for ; i < srcSlice.Len() && i < dstSlice.Len(); i++ { srcElement := srcSlice.Index(i) dstElement := dstSlice.Index(i) if srcElement.CanInterface() { srcElement = reflect.ValueOf(srcElement.Interface()) } if dstElement.CanInterface() { dstElement = reflect.ValueOf(dstElement.Interface()) } if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil { return } } } dst.SetMapIndex(key, dstSlice) } } if dstElement.IsValid() && !isEmptyValue(dstElement) && (reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map || reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Slice) { continue } if srcElement.IsValid() && ((srcElement.Kind() != reflect.Ptr && overwrite) || !dstElement.IsValid() || isEmptyValue(dstElement)) { if dst.IsNil() { dst.Set(reflect.MakeMap(dst.Type())) } dst.SetMapIndex(key, srcElement) } } case reflect.Slice: if !dst.CanSet() { break } if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice && !sliceDeepCopy { dst.Set(src) } else if config.AppendSlice { if src.Type() != dst.Type() { return fmt.Errorf("cannot append two slice with different type (%s, %s)", src.Type(), dst.Type()) } dst.Set(reflect.AppendSlice(dst, src)) } else if sliceDeepCopy { for i := 0; i < src.Len() && i < dst.Len(); i++ { srcElement := src.Index(i) dstElement := dst.Index(i) if srcElement.CanInterface() { srcElement = reflect.ValueOf(srcElement.Interface()) } if dstElement.CanInterface() { dstElement = reflect.ValueOf(dstElement.Interface()) } if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil { return } } } case reflect.Ptr: fallthrough case reflect.Interface: if isReflectNil(src) { if overwriteWithEmptySrc && dst.CanSet() && src.Type().AssignableTo(dst.Type()) { dst.Set(src) } break } if src.Kind() != reflect.Interface { if dst.IsNil() || (src.Kind() != reflect.Ptr && overwrite) { if dst.CanSet() && (overwrite || isEmptyValue(dst)) { dst.Set(src) } } else if src.Kind() == reflect.Ptr { if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil { return } } else if dst.Elem().Type() == src.Type() { if err = deepMerge(dst.Elem(), src, visited, depth+1, config); err != nil { return } } else { return ErrDifferentArgumentsTypes } break } if dst.IsNil() || overwrite { if dst.CanSet() && (overwrite || isEmptyValue(dst)) { dst.Set(src) } break } if dst.Elem().Kind() == src.Elem().Kind() { if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil { return } break } default: mustSet := (isEmptyValue(dst) || overwrite) && (!isEmptyValue(src) || overwriteWithEmptySrc) if mustSet { if dst.CanSet() { dst.Set(src) } else { dst = src } } } return } // Merge will fill any empty for value type attributes on the dst struct using corresponding // src attributes if they themselves are not empty. dst and src must be valid same-type structs // and dst must be a pointer to struct. // It won't merge unexported (private) fields and will do recursively any exported field. func Merge(dst, src interface{}, opts ...func(*Config)) error { return merge(dst, src, opts...) } // MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overridden by // non-empty src attribute values. // Deprecated: use Merge(…) with WithOverride func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error { return merge(dst, src, append(opts, WithOverride)...) } // WithTransformers adds transformers to merge, allowing to customize the merging of some types. func WithTransformers(transformers Transformers) func(*Config) { return func(config *Config) { config.Transformers = transformers } } // WithOverride will make merge override non-empty dst attributes with non-empty src attributes values. func WithOverride(config *Config) { config.Overwrite = true } // WithOverwriteWithEmptyValue will make merge override non empty dst attributes with empty src attributes values. func WithOverwriteWithEmptyValue(config *Config) { config.Overwrite = true config.overwriteWithEmptyValue = true } // WithOverrideEmptySlice will make merge override empty dst slice with empty src slice. func WithOverrideEmptySlice(config *Config) { config.overwriteSliceWithEmptyValue = true } // WithAppendSlice will make merge append slices instead of overwriting it. func WithAppendSlice(config *Config) { config.AppendSlice = true } // WithTypeCheck will make merge check types while overwriting it (must be used with WithOverride). func WithTypeCheck(config *Config) { config.TypeCheck = true } // WithSliceDeepCopy will merge slice element one by one with Overwrite flag. func WithSliceDeepCopy(config *Config) { config.sliceDeepCopy = true config.Overwrite = true } func merge(dst, src interface{}, opts ...func(*Config)) error { if dst != nil && reflect.ValueOf(dst).Kind() != reflect.Ptr { return ErrNonPointerAgument } var ( vDst, vSrc reflect.Value err error ) config := &Config{} for _, opt := range opts { opt(config) } if vDst, vSrc, err = resolveValues(dst, src); err != nil { return err } if vDst.Type() != vSrc.Type() { return ErrDifferentArgumentsTypes } return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config) } // IsReflectNil is the reflect value provided nil func isReflectNil(v reflect.Value) bool { k := v.Kind() switch k { case reflect.Interface, reflect.Slice, reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr: // Both interface and slice are nil if first word is 0. // Both are always bigger than a word; assume flagIndir. return v.IsNil() default: return false } } ================================================ FILE: vendor/github.com/imdario/mergo/mergo.go ================================================ // Copyright 2013 Dario Castañé. All rights reserved. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Based on src/pkg/reflect/deepequal.go from official // golang's stdlib. package mergo import ( "errors" "reflect" ) // Errors reported by Mergo when it finds invalid arguments. var ( ErrNilArguments = errors.New("src and dst must not be nil") ErrDifferentArgumentsTypes = errors.New("src and dst must be of same type") ErrNotSupported = errors.New("only structs, maps, and slices are supported") ErrExpectedMapAsDestination = errors.New("dst was expected to be a map") ErrExpectedStructAsDestination = errors.New("dst was expected to be a struct") ErrNonPointerAgument = errors.New("dst must be a pointer") ) // During deepMerge, must keep track of checks that are // in progress. The comparison algorithm assumes that all // checks in progress are true when it reencounters them. // Visited are stored in a map indexed by 17 * a1 + a2; type visit struct { ptr uintptr typ reflect.Type next *visit } // From src/pkg/encoding/json/encode.go. func isEmptyValue(v reflect.Value) bool { switch v.Kind() { case reflect.Array, reflect.Map, reflect.Slice, reflect.String: return v.Len() == 0 case reflect.Bool: return !v.Bool() case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return v.Int() == 0 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return v.Uint() == 0 case reflect.Float32, reflect.Float64: return v.Float() == 0 case reflect.Interface, reflect.Ptr: if v.IsNil() { return true } return isEmptyValue(v.Elem()) case reflect.Func: return v.IsNil() case reflect.Invalid: return true } return false } func resolveValues(dst, src interface{}) (vDst, vSrc reflect.Value, err error) { if dst == nil || src == nil { err = ErrNilArguments return } vDst = reflect.ValueOf(dst).Elem() if vDst.Kind() != reflect.Struct && vDst.Kind() != reflect.Map && vDst.Kind() != reflect.Slice { err = ErrNotSupported return } vSrc = reflect.ValueOf(src) // We check if vSrc is a pointer to dereference it. if vSrc.Kind() == reflect.Ptr { vSrc = vSrc.Elem() } return } ================================================ FILE: vendor/github.com/inconshreveable/mousetrap/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2022 Alan Shreve (@inconshreveable) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/inconshreveable/mousetrap/README.md ================================================ # mousetrap mousetrap is a tiny library that answers a single question. On a Windows machine, was the process invoked by someone double clicking on the executable file while browsing in explorer? ### Motivation Windows developers unfamiliar with command line tools will often "double-click" the executable for a tool. Because most CLI tools print the help and then exit when invoked without arguments, this is often very frustrating for those users. mousetrap provides a way to detect these invocations so that you can provide more helpful behavior and instructions on how to run the CLI tool. To see what this looks like, both from an organizational and a technical perspective, see https://inconshreveable.com/09-09-2014/sweat-the-small-stuff/ ### The interface The library exposes a single interface: func StartedByExplorer() (bool) ================================================ FILE: vendor/github.com/inconshreveable/mousetrap/trap_others.go ================================================ //go:build !windows // +build !windows package mousetrap // StartedByExplorer returns true if the program was invoked by the user // double-clicking on the executable from explorer.exe // // It is conservative and returns false if any of the internal calls fail. // It does not guarantee that the program was run from a terminal. It only can tell you // whether it was launched from explorer.exe // // On non-Windows platforms, it always returns false. func StartedByExplorer() bool { return false } ================================================ FILE: vendor/github.com/inconshreveable/mousetrap/trap_windows.go ================================================ package mousetrap import ( "syscall" "unsafe" ) func getProcessEntry(pid int) (*syscall.ProcessEntry32, error) { snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPPROCESS, 0) if err != nil { return nil, err } defer syscall.CloseHandle(snapshot) var procEntry syscall.ProcessEntry32 procEntry.Size = uint32(unsafe.Sizeof(procEntry)) if err = syscall.Process32First(snapshot, &procEntry); err != nil { return nil, err } for { if procEntry.ProcessID == uint32(pid) { return &procEntry, nil } err = syscall.Process32Next(snapshot, &procEntry) if err != nil { return nil, err } } } // StartedByExplorer returns true if the program was invoked by the user double-clicking // on the executable from explorer.exe // // It is conservative and returns false if any of the internal calls fail. // It does not guarantee that the program was run from a terminal. It only can tell you // whether it was launched from explorer.exe func StartedByExplorer() bool { pe, err := getProcessEntry(syscall.Getppid()) if err != nil { return false } return "explorer.exe" == syscall.UTF16ToString(pe.ExeFile[:]) } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/LICENSE ================================================ MIT License Copyright (c) 2018 jedib0t Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/table/README.md ================================================ # Table [![Go Reference](https://pkg.go.dev/badge/github.com/jedib0t/go-pretty/v6/table.svg)](https://pkg.go.dev/github.com/jedib0t/go-pretty/v6/table) Pretty-print tables into ASCII/Unicode strings. - Add Rows one-by-one or as a group (`AppendRow`/`AppendRows`) - Add Header(s) and Footer(s) (`AppendHeader`/`AppendFooter`) - Add a Separator manually after any Row (`AppendSeparator`) - Auto Index Rows (1, 2, 3 ...) and Columns (A, B, C, ...) (`SetAutoIndex`) - Auto Merge - Cells in a Row (`RowConfig.AutoMerge`) - Columns (`ColumnConfig.AutoMerge`) - Limit the length of - Rows (`SetAllowedRowLength`) - Columns (`ColumnConfig.Width*`) - Page results by a specified number of Lines (`SetPageSize`) - Alignment - Horizontal & Vertical - Auto (horizontal) Align (numeric columns aligned Right) - Custom (horizontal) Align per column (`ColumnConfig.Align*`) - Custom (vertical) VAlign per column with multi-line cell support (`ColumnConfig.VAlign*`) - Mirror output to an `io.Writer` (ex. `os.StdOut`) (`SetOutputMirror`) - Sort by one or more Columns (`SortBy`) - Suppress/hide columns with no content (`SuppressEmptyColumns`) - Customizable Cell rendering per Column (`ColumnConfig.Transformer*`) - Hide any columns that you don't want displayed (`ColumnConfig.Hidden`) - Reset Headers/Rows/Footers at will to reuse the same Table Writer (`Reset*`) - Completely customizable styles (`SetStyle`/`Style`) - Many ready-to-use styles: [style.go](style.go) - Colorize Headers/Body/Footers using [../text/color.go](../text/color.go) - Custom text-case for Headers/Body/Footers - Enable separators between each row - Render table without a Border - and a lot more... - Render as: - (ASCII/Unicode) Table - CSV - HTML Table (with custom CSS Class) - Markdown Table ``` +---------------------------------------------------------------------+ | Game of Thrones + +-----+------------+-----------+--------+-----------------------------+ | # | FIRST NAME | LAST NAME | SALARY | | +-----+------------+-----------+--------+-----------------------------+ | 1 | Arya | Stark | 3000 | | | 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! | | 300 | Tyrion | Lannister | 5000 | | +-----+------------+-----------+--------+-----------------------------+ | | | TOTAL | 10000 | | +-----+------------+-----------+--------+-----------------------------+ ``` A demonstration of all the capabilities can be found here: [../cmd/demo-table](../cmd/demo-table) If you want very specific examples, read ahead. **Hint**: I've tried to ensure that almost all supported use-cases are covered by unit-tests and that they print the table rendered. Run `go test -v github.com/jedib0t/go-pretty/v6/table` to see the test outputs and help you figure out how to do something. # Examples All the examples below are going to start with the following block, although nothing except a single Row is mandatory for the `Render()` function to render something: ```golang package main import ( "os" "github.com/jedib0t/go-pretty/v6/table" ) func main() { t := table.NewWriter() t.SetOutputMirror(os.Stdout) t.AppendHeader(table.Row{"#", "First Name", "Last Name", "Salary"}) t.AppendRows([]table.Row{ {1, "Arya", "Stark", 3000}, {20, "Jon", "Snow", 2000, "You know nothing, Jon Snow!"}, }) t.AppendSeparator() t.AppendRow([]interface{}{300, "Tyrion", "Lannister", 5000}) t.AppendFooter(table.Row{"", "", "Total", 10000}) t.Render() } ``` Running the above will result in: ``` +-----+------------+-----------+--------+-----------------------------+ | # | FIRST NAME | LAST NAME | SALARY | | +-----+------------+-----------+--------+-----------------------------+ | 1 | Arya | Stark | 3000 | | | 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! | +-----+------------+-----------+--------+-----------------------------+ | 300 | Tyrion | Lannister | 5000 | | +-----+------------+-----------+--------+-----------------------------+ | | | TOTAL | 10000 | | +-----+------------+-----------+--------+-----------------------------+ ``` ## Styles You can customize almost every single thing about the table above. The previous example just defaulted to `StyleDefault` during `Render()`. You can use a ready-to-use style (as in [style.go](style.go)) or customize it as you want. ### Ready-to-use Styles Table comes with a bunch of ready-to-use Styles that make the table look really good. Set or Change the style using: ```golang t.SetStyle(table.StyleLight) t.Render() ``` to get: ``` ┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐ │ # │ FIRST NAME │ LAST NAME │ SALARY │ │ ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤ │ 1 │ Arya │ Stark │ 3000 │ │ │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │ ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤ │ 300 │ Tyrion │ Lannister │ 5000 │ │ ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤ │ │ │ TOTAL │ 10000 │ │ └─────┴────────────┴───────────┴────────┴─────────────────────────────┘ ``` Or if you want to use a full-color mode, and don't care for boxes, use: ```golang t.SetStyle(table.StyleColoredBright) t.Render() ``` to get: Colored Table ### Roll your own Style You can also roll your own style: ```golang t.SetStyle(table.Style{ Name: "myNewStyle", Box: table.BoxStyle{ BottomLeft: "\\", BottomRight: "/", BottomSeparator: "v", Left: "[", LeftSeparator: "{", MiddleHorizontal: "-", MiddleSeparator: "+", MiddleVertical: "|", PaddingLeft: "<", PaddingRight: ">", Right: "]", RightSeparator: "}", TopLeft: "(", TopRight: ")", TopSeparator: "^", UnfinishedRow: " ~~~", }, Color: table.ColorOptions{ IndexColumn: text.Colors{text.BgCyan, text.FgBlack}, Footer: text.Colors{text.BgCyan, text.FgBlack}, Header: text.Colors{text.BgHiCyan, text.FgBlack}, Row: text.Colors{text.BgHiWhite, text.FgBlack}, RowAlternate: text.Colors{text.BgWhite, text.FgBlack}, }, Format: table.FormatOptions{ Footer: text.FormatUpper, Header: text.FormatUpper, Row: text.FormatDefault, }, Options: table.Options{ DrawBorder: true, SeparateColumns: true, SeparateFooter: true, SeparateHeader: true, SeparateRows: false, }, }) ``` Or you can use one of the ready-to-use Styles, and just make a few tweaks: ```golang t.SetStyle(table.StyleLight) t.Style().Color.Header = text.Colors{text.BgHiCyan, text.FgBlack} t.Style().Color.IndexColumn = text.Colors{text.BgHiCyan, text.FgBlack} t.Style().Format.Footer = text.FormatLower t.Style().Options.DrawBorder = false ``` ## Auto-Merge You can auto-merge cells horizontally and vertically, but you have request for it specifically for each row/column using `RowConfig` or `ColumnConfig`. ```golang rowConfigAutoMerge := table.RowConfig{AutoMerge: true} t := table.NewWriter() t.AppendHeader(table.Row{"Node IP", "Pods", "Namespace", "Container", "RCE", "RCE"}, rowConfigAutoMerge) t.AppendHeader(table.Row{"", "", "", "", "EXE", "RUN"}) t.AppendRow(table.Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "Y", "Y"}, rowConfigAutoMerge) t.AppendRow(table.Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "N"}, rowConfigAutoMerge) t.AppendRow(table.Row{"1.1.1.1", "Pod 1A", "NS 1B", "C 3", "N", "N"}, rowConfigAutoMerge) t.AppendRow(table.Row{"1.1.1.1", "Pod 1B", "NS 2", "C 4", "N", "N"}, rowConfigAutoMerge) t.AppendRow(table.Row{"1.1.1.1", "Pod 1B", "NS 2", "C 5", "Y", "N"}, rowConfigAutoMerge) t.AppendRow(table.Row{"2.2.2.2", "Pod 2", "NS 3", "C 6", "Y", "Y"}, rowConfigAutoMerge) t.AppendRow(table.Row{"2.2.2.2", "Pod 2", "NS 3", "C 7", "Y", "Y"}, rowConfigAutoMerge) t.AppendFooter(table.Row{"", "", "", 7, 5, 3}) t.SetAutoIndex(true) t.SetColumnConfigs([]table.ColumnConfig{ {Number: 1, AutoMerge: true}, {Number: 2, AutoMerge: true}, {Number: 3, AutoMerge: true}, {Number: 4, AutoMerge: true}, {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter}, {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter}, }) t.SetOutputMirror(os.Stdout) t.SetStyle(table.StyleLight) t.Style().Options.SeparateRows = true fmt.Println(t.Render()) ``` to get: ``` ┌───┬─────────┬────────┬───────────┬───────────┬───────────┐ │ │ NODE IP │ PODS │ NAMESPACE │ CONTAINER │ RCE │ │ │ │ │ │ ├─────┬─────┤ │ │ │ │ │ │ EXE │ RUN │ ├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┤ │ 1 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 1 │ Y │ ├───┤ │ │ ├───────────┼─────┬─────┤ │ 2 │ │ │ │ C 2 │ Y │ N │ ├───┤ │ ├───────────┼───────────┼─────┴─────┤ │ 3 │ │ │ NS 1B │ C 3 │ N │ ├───┤ ├────────┼───────────┼───────────┼───────────┤ │ 4 │ │ Pod 1B │ NS 2 │ C 4 │ N │ ├───┤ │ │ ├───────────┼─────┬─────┤ │ 5 │ │ │ │ C 5 │ Y │ N │ ├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┤ │ 6 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 6 │ Y │ ├───┤ │ │ ├───────────┼───────────┤ │ 7 │ │ │ │ C 7 │ Y │ ├───┼─────────┼────────┼───────────┼───────────┼─────┬─────┤ │ │ │ │ │ 7 │ 5 │ 3 │ └───┴─────────┴────────┴───────────┴───────────┴─────┴─────┘ ``` ## Paging You can limit then number of lines rendered in a single "Page". This logic can handle rows with multiple lines too. Here is a simple example: ```golang t.SetPageSize(1) t.Render() ``` to get: ``` +-----+------------+-----------+--------+-----------------------------+ | # | FIRST NAME | LAST NAME | SALARY | | +-----+------------+-----------+--------+-----------------------------+ | 1 | Arya | Stark | 3000 | | +-----+------------+-----------+--------+-----------------------------+ | | | TOTAL | 10000 | | +-----+------------+-----------+--------+-----------------------------+ +-----+------------+-----------+--------+-----------------------------+ | # | FIRST NAME | LAST NAME | SALARY | | +-----+------------+-----------+--------+-----------------------------+ | 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! | +-----+------------+-----------+--------+-----------------------------+ | | | TOTAL | 10000 | | +-----+------------+-----------+--------+-----------------------------+ +-----+------------+-----------+--------+-----------------------------+ | # | FIRST NAME | LAST NAME | SALARY | | +-----+------------+-----------+--------+-----------------------------+ | 300 | Tyrion | Lannister | 5000 | | +-----+------------+-----------+--------+-----------------------------+ | | | TOTAL | 10000 | | +-----+------------+-----------+--------+-----------------------------+ ``` ## Sorting Sorting can be done on one or more columns. The following code will make the rows be sorted first by "First Name" and then by "Last Name" (in case of similar "First Name" entries). ```golang t.SortBy([]table.SortBy{ {Name: "First Name", Mode: table.Asc}, {Name: "Last Name", Mode: table.Asc}, }) ``` ## Wrapping (or) Row/Column Width restrictions You can restrict the maximum (text) width for a Row: ```golang t.SetAllowedRowLength(50) t.Render() ``` to get: ``` +-----+------------+-----------+--------+------- ~ | # | FIRST NAME | LAST NAME | SALARY | ~ +-----+------------+-----------+--------+------- ~ | 1 | Arya | Stark | 3000 | ~ | 20 | Jon | Snow | 2000 | You kn ~ +-----+------------+-----------+--------+------- ~ | 300 | Tyrion | Lannister | 5000 | ~ +-----+------------+-----------+--------+------- ~ | | | TOTAL | 10000 | ~ +-----+------------+-----------+--------+------- ~ ``` ## Column Control - Alignment, Colors, Width and more You can control a lot of things about individual cells/columns which overrides global properties/styles using the `SetColumnConfig()` interface: - Alignment (horizontal & vertical) - Colorization - Transform individual cells based on the content - Visibility - Width (minimum & maximum) ```golang nameTransformer := text.Transformer(func(val interface{}) string { return text.Bold.Sprint(val) }) t.SetColumnConfigs([]ColumnConfig{ { Name: "First Name", Align: text.AlignLeft, AlignFooter: text.AlignLeft, AlignHeader: text.AlignLeft, Colors: text.Colors{text.BgBlack, text.FgRed}, ColorsHeader: text.Colors{text.BgRed, text.FgBlack, text.Bold}, ColorsFooter: text.Colors{text.BgRed, text.FgBlack}, Hidden: false, Transformer: nameTransformer, TransformerFooter: nameTransformer, TransformerHeader: nameTransformer, VAlign: text.VAlignMiddle, VAlignFooter: text.VAlignTop, VAlignHeader: text.VAlignBottom, WidthMin: 6, WidthMax: 64, } }) ``` ## Render As ... Tables can be rendered in other common formats such as: ### ... CSV ```golang t.RenderCSV() ``` to get: ``` ,First Name,Last Name,Salary, 1,Arya,Stark,3000, 20,Jon,Snow,2000,"You know nothing\, Jon Snow!" 300,Tyrion,Lannister,5000, ,,Total,10000, ``` ### ... HTML Table ```golang t.Style().HTML = table.HTMLOptions{ CSSClass: "game-of-thrones", EmptyColumn: " ", EscapeText: true, Newline: "
", } t.RenderHTML() ``` to get: ```html
# First Name Last Name Salary  
1 Arya Stark 3000  
20 Jon Snow 2000 You know nothing, Jon Snow!
300 Tyrion Lannister 5000  
    Total 10000  
``` ### ... Markdown Table ```golang t.RenderMarkdown() ``` to get: ```markdown | # | First Name | Last Name | Salary | | | ---:| --- | --- | ---:| --- | | 1 | Arya | Stark | 3000 | | | 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! | | 300 | Tyrion | Lannister | 5000 | | | | | Total | 10000 | | ``` ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/table/config.go ================================================ package table import ( "github.com/jedib0t/go-pretty/v6/text" ) // ColumnConfig contains configurations that determine and modify the way the // contents of the column get rendered. type ColumnConfig struct { // Name is the name of the Column as it appears in the first Header row. // If a Header is not provided, or the name is not found in the header, this // will not work. Name string // Number is the Column # from left. When specified, it overrides the Name // property. If you know the exact Column number, use this instead of Name. Number int // Align defines the horizontal alignment Align text.Align // AlignFooter defines the horizontal alignment of Footer rows AlignFooter text.Align // AlignHeader defines the horizontal alignment of Header rows AlignHeader text.Align // AutoMerge merges cells with similar values and prevents separators from // being drawn. Caveats: // * VAlign is applied on the individual cell and not on the merged cell // * Does not work in CSV/HTML/Markdown render modes // * Does not work well with horizontal auto-merge (RowConfig.AutoMerge) // // Works best when: // * Style().Options.SeparateRows == true // * Style().Color.Row == Style().Color.RowAlternate (or not set) AutoMerge bool // Colors defines the colors to be used on the column Colors text.Colors // ColorsFooter defines the colors to be used on the column in Footer rows ColorsFooter text.Colors // ColorsHeader defines the colors to be used on the column in Header rows ColorsHeader text.Colors // Hidden when set to true will prevent the column from being rendered. // This is useful in cases like needing a column for sorting, but not for // display. Hidden bool // Transformer is a custom-function that changes the way the value gets // rendered to the console. Refer to text/transformer.go for ready-to-use // Transformer functions. Transformer text.Transformer // TransformerFooter is like Transformer but for Footer rows TransformerFooter text.Transformer // TransformerHeader is like Transformer but for Header rows TransformerHeader text.Transformer // VAlign defines the vertical alignment VAlign text.VAlign // VAlignFooter defines the vertical alignment in Footer rows VAlignFooter text.VAlign // VAlignHeader defines the vertical alignment in Header rows VAlignHeader text.VAlign // WidthMax defines the maximum character length of the column WidthMax int // WidthEnforcer enforces the WidthMax value on the column contents; // default: text.WrapText WidthMaxEnforcer WidthEnforcer // WidthMin defines the minimum character length of the column WidthMin int } func (c ColumnConfig) getWidthMaxEnforcer() WidthEnforcer { if c.WidthMax == 0 { return widthEnforcerNone } if c.WidthMaxEnforcer != nil { return c.WidthMaxEnforcer } return text.WrapText } // RowConfig contains configurations that determine and modify the way the // contents of a row get rendered. type RowConfig struct { // AutoMerge merges cells with similar values and prevents separators from // being drawn. Caveats: // * Align is overridden to text.AlignCenter on the merged cell (unless set // by AutoMergeAlign value below) // * Does not work in CSV/HTML/Markdown render modes // * Does not work well with vertical auto-merge (ColumnConfig.AutoMerge) AutoMerge bool // Alignment to use on a merge (defaults to text.AlignCenter) AutoMergeAlign text.Align } func (rc RowConfig) getAutoMergeAlign() text.Align { if rc.AutoMergeAlign == text.AlignDefault { return text.AlignCenter } return rc.AutoMergeAlign } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/table/render.go ================================================ package table import ( "fmt" "strings" "unicode/utf8" "github.com/jedib0t/go-pretty/v6/text" ) // Render renders the Table in a human-readable "pretty" format. Example: // // ┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐ // │ # │ FIRST NAME │ LAST NAME │ SALARY │ │ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤ // │ 1 │ Arya │ Stark │ 3000 │ │ // │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │ // │ 300 │ Tyrion │ Lannister │ 5000 │ │ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤ // │ │ │ TOTAL │ 10000 │ │ // └─────┴────────────┴───────────┴────────┴─────────────────────────────┘ func (t *Table) Render() string { t.initForRender() var out strings.Builder if t.numColumns > 0 { t.renderTitle(&out) // top-most border t.renderRowsBorderTop(&out) // header rows t.renderRowsHeader(&out) // (data) rows t.renderRows(&out, t.rows, renderHint{}) // footer rows t.renderRowsFooter(&out) // bottom-most border t.renderRowsBorderBottom(&out) // caption if t.caption != "" { out.WriteRune('\n') out.WriteString(t.caption) } } return t.render(&out) } func (t *Table) renderColumn(out *strings.Builder, row rowStr, colIdx int, maxColumnLength int, hint renderHint) int { numColumnsRendered := 1 // when working on the first column, and autoIndex is true, insert a new // column with the row number on it. if colIdx == 0 && t.autoIndex { hintAutoIndex := hint hintAutoIndex.isAutoIndexColumn = true t.renderColumnAutoIndex(out, hintAutoIndex) } // when working on column number 2 or more, render the column separator if colIdx > 0 { t.renderColumnSeparator(out, row, colIdx, hint) } // extract the text, convert-case if not-empty and align horizontally mergeVertically := t.shouldMergeCellsVertically(colIdx, hint) var colStr string if mergeVertically { // leave colStr empty; align will expand the column as necessary } else if colIdx < len(row) { colStr = t.getFormat(hint).Apply(row[colIdx]) } align := t.getAlign(colIdx, hint) // if horizontal cell merges are enabled, look ahead and see how many cells // have the same content and merge them all until a cell with a different // content is found; override alignment to Center in this case rowConfig := t.getRowConfig(hint) if rowConfig.AutoMerge && !hint.isSeparatorRow { // get the real row to consider all lines in each column instead of just // looking at the current "line" rowUnwrapped := t.getRow(hint.rowNumber-1, hint) for idx := colIdx + 1; idx < len(rowUnwrapped); idx++ { if rowUnwrapped[colIdx] != rowUnwrapped[idx] { break } align = rowConfig.getAutoMergeAlign() maxColumnLength += t.getMaxColumnLengthForMerging(idx) numColumnsRendered++ } } colStr = align.Apply(colStr, maxColumnLength) // pad both sides of the column if !hint.isSeparatorRow || (hint.isSeparatorRow && mergeVertically) { colStr = t.style.Box.PaddingLeft + colStr + t.style.Box.PaddingRight } t.renderColumnColorized(out, colIdx, colStr, hint) return colIdx + numColumnsRendered } func (t *Table) renderColumnAutoIndex(out *strings.Builder, hint renderHint) { var outAutoIndex strings.Builder outAutoIndex.Grow(t.maxColumnLengths[0]) if hint.isSeparatorRow { numChars := t.autoIndexVIndexMaxLength + utf8.RuneCountInString(t.style.Box.PaddingLeft) + utf8.RuneCountInString(t.style.Box.PaddingRight) chars := t.style.Box.MiddleHorizontal if hint.isAutoIndexColumn && hint.isHeaderOrFooterSeparator() { chars = text.RepeatAndTrim(" ", len(t.style.Box.MiddleHorizontal)) } outAutoIndex.WriteString(text.RepeatAndTrim(chars, numChars)) } else { outAutoIndex.WriteString(t.style.Box.PaddingLeft) rowNumStr := fmt.Sprint(hint.rowNumber) if hint.isHeaderRow || hint.isFooterRow || hint.rowLineNumber > 1 { rowNumStr = strings.Repeat(" ", t.autoIndexVIndexMaxLength) } outAutoIndex.WriteString(text.AlignRight.Apply(rowNumStr, t.autoIndexVIndexMaxLength)) outAutoIndex.WriteString(t.style.Box.PaddingRight) } if t.style.Color.IndexColumn != nil { colors := t.style.Color.IndexColumn if hint.isFooterRow { colors = t.style.Color.Footer } out.WriteString(colors.Sprint(outAutoIndex.String())) } else { out.WriteString(outAutoIndex.String()) } hint.isAutoIndexColumn = true t.renderColumnSeparator(out, rowStr{}, 0, hint) } func (t *Table) renderColumnColorized(out *strings.Builder, colIdx int, colStr string, hint renderHint) { colors := t.getColumnColors(colIdx, hint) if colors != nil { out.WriteString(colors.Sprint(colStr)) } else if hint.isHeaderRow && t.style.Color.Header != nil { out.WriteString(t.style.Color.Header.Sprint(colStr)) } else if hint.isFooterRow && t.style.Color.Footer != nil { out.WriteString(t.style.Color.Footer.Sprint(colStr)) } else if hint.isRegularRow() { if colIdx == t.indexColumn-1 && t.style.Color.IndexColumn != nil { out.WriteString(t.style.Color.IndexColumn.Sprint(colStr)) } else if hint.rowNumber%2 == 0 && t.style.Color.RowAlternate != nil { out.WriteString(t.style.Color.RowAlternate.Sprint(colStr)) } else if t.style.Color.Row != nil { out.WriteString(t.style.Color.Row.Sprint(colStr)) } else { out.WriteString(colStr) } } else { out.WriteString(colStr) } } func (t *Table) renderColumnSeparator(out *strings.Builder, row rowStr, colIdx int, hint renderHint) { if t.style.Options.SeparateColumns { separator := t.getColumnSeparator(row, colIdx, hint) colors := t.getSeparatorColors(hint) if colors.EscapeSeq() != "" { out.WriteString(colors.Sprint(separator)) } else { out.WriteString(separator) } } } func (t *Table) renderLine(out *strings.Builder, row rowStr, hint renderHint) { // if the output has content, it means that this call is working on line // number 2 or more; separate them with a newline if out.Len() > 0 { out.WriteRune('\n') } // use a brand-new strings.Builder if a row length limit has been set var outLine *strings.Builder if t.allowedRowLength > 0 { outLine = &strings.Builder{} } else { outLine = out } // grow the strings.Builder to the maximum possible row length outLine.Grow(t.maxRowLength) nextColIdx := 0 t.renderMarginLeft(outLine, hint) for colIdx, maxColumnLength := range t.maxColumnLengths { if colIdx != nextColIdx { continue } nextColIdx = t.renderColumn(outLine, row, colIdx, maxColumnLength, hint) } t.renderMarginRight(outLine, hint) // merge the strings.Builder objects if a new one was created earlier if outLine != out { t.renderLineMergeOutputs(out, outLine) } // if a page size has been set, and said number of lines has already // been rendered, and the header is not being rendered right now, render // the header all over again with a spacing line if hint.isRegularRow() { t.numLinesRendered++ if t.pageSize > 0 && t.numLinesRendered%t.pageSize == 0 && !hint.isLastLineOfLastRow() { t.renderRowsFooter(out) t.renderRowsBorderBottom(out) out.WriteString(t.style.Box.PageSeparator) t.renderRowsBorderTop(out) t.renderRowsHeader(out) } } } func (t *Table) renderLineMergeOutputs(out *strings.Builder, outLine *strings.Builder) { outLineStr := outLine.String() if text.RuneWidthWithoutEscSequences(outLineStr) > t.allowedRowLength { trimLength := t.allowedRowLength - utf8.RuneCountInString(t.style.Box.UnfinishedRow) if trimLength > 0 { out.WriteString(text.Trim(outLineStr, trimLength)) out.WriteString(t.style.Box.UnfinishedRow) } } else { out.WriteString(outLineStr) } } func (t *Table) renderMarginLeft(out *strings.Builder, hint renderHint) { out.WriteString(t.style.Format.Direction.Modifier()) if t.style.Options.DrawBorder { border := t.getBorderLeft(hint) colors := t.getBorderColors(hint) if colors.EscapeSeq() != "" { out.WriteString(colors.Sprint(border)) } else { out.WriteString(border) } } } func (t *Table) renderMarginRight(out *strings.Builder, hint renderHint) { if t.style.Options.DrawBorder { border := t.getBorderRight(hint) colors := t.getBorderColors(hint) if colors.EscapeSeq() != "" { out.WriteString(colors.Sprint(border)) } else { out.WriteString(border) } } } func (t *Table) renderRow(out *strings.Builder, row rowStr, hint renderHint) { if len(row) > 0 { // fit every column into the allowedColumnLength/maxColumnLength limit // and in the process find the max. number of lines in any column in // this row colMaxLines, rowWrapped := t.wrapRow(row) // if there is just 1 line in all columns, add the row as such; else // split each column into individual lines and render them one-by-one if colMaxLines == 1 { hint.isLastLineOfRow = true t.renderLine(out, rowWrapped, hint) } else { // convert one row into N # of rows based on colMaxLines rowLines := make([]rowStr, len(row)) for colIdx, colStr := range rowWrapped { rowLines[colIdx] = t.getVAlign(colIdx, hint).ApplyStr(colStr, colMaxLines) } for colLineIdx := 0; colLineIdx < colMaxLines; colLineIdx++ { rowLine := make(rowStr, len(rowLines)) for colIdx, colLines := range rowLines { rowLine[colIdx] = colLines[colLineIdx] } hint.isLastLineOfRow = colLineIdx == colMaxLines-1 hint.rowLineNumber = colLineIdx + 1 t.renderLine(out, rowLine, hint) } } } } func (t *Table) renderRowSeparator(out *strings.Builder, hint renderHint) { if hint.isBorderTop || hint.isBorderBottom { if !t.style.Options.DrawBorder { return } } else if hint.isHeaderRow && !t.style.Options.SeparateHeader { return } else if hint.isFooterRow && !t.style.Options.SeparateFooter { return } hint.isSeparatorRow = true t.renderLine(out, t.rowSeparator, hint) } func (t *Table) renderRows(out *strings.Builder, rows []rowStr, hint renderHint) { for rowIdx, row := range rows { hint.isFirstRow = rowIdx == 0 hint.isLastRow = rowIdx == len(rows)-1 hint.rowNumber = rowIdx + 1 t.renderRow(out, row, hint) if (t.style.Options.SeparateRows && rowIdx < len(rows)-1) || // last row before footer (t.separators[rowIdx] && rowIdx != len(rows)-1) { // manually added separator not after last row hint.isFirstRow = false t.renderRowSeparator(out, hint) } } } func (t *Table) renderRowsBorderBottom(out *strings.Builder) { if len(t.rowsFooter) > 0 { t.renderRowSeparator(out, renderHint{isBorderBottom: true, isFooterRow: true, rowNumber: len(t.rowsFooter)}) } else { t.renderRowSeparator(out, renderHint{isBorderBottom: true, isFooterRow: false, rowNumber: len(t.rows)}) } } func (t *Table) renderRowsBorderTop(out *strings.Builder) { if len(t.rowsHeader) > 0 || t.autoIndex { t.renderRowSeparator(out, renderHint{isBorderTop: true, isHeaderRow: true, rowNumber: 0}) } else { t.renderRowSeparator(out, renderHint{isBorderTop: true, isHeaderRow: false, rowNumber: 0}) } } func (t *Table) renderRowsFooter(out *strings.Builder) { if len(t.rowsFooter) > 0 { t.renderRowSeparator(out, renderHint{ isFooterRow: true, isFirstRow: true, isSeparatorRow: true, }) t.renderRows(out, t.rowsFooter, renderHint{isFooterRow: true}) } } func (t *Table) renderRowsHeader(out *strings.Builder) { if len(t.rowsHeader) > 0 || t.autoIndex { hintSeparator := renderHint{isHeaderRow: true, isLastRow: true, isSeparatorRow: true} if len(t.rowsHeader) > 0 { t.renderRows(out, t.rowsHeader, renderHint{isHeaderRow: true}) hintSeparator.rowNumber = len(t.rowsHeader) } else if t.autoIndex { t.renderRow(out, t.getAutoIndexColumnIDs(), renderHint{isAutoIndexRow: true, isHeaderRow: true}) hintSeparator.rowNumber = 1 } t.renderRowSeparator(out, hintSeparator) } } func (t *Table) renderTitle(out *strings.Builder) { if t.title != "" { colors := t.style.Title.Colors colorsBorder := t.getBorderColors(renderHint{isTitleRow: true}) rowLength := t.maxRowLength if t.allowedRowLength != 0 && t.allowedRowLength < rowLength { rowLength = t.allowedRowLength } if t.style.Options.DrawBorder { lenBorder := rowLength - text.RuneWidthWithoutEscSequences(t.style.Box.TopLeft+t.style.Box.TopRight) out.WriteString(colorsBorder.Sprint(t.style.Box.TopLeft)) out.WriteString(colorsBorder.Sprint(text.RepeatAndTrim(t.style.Box.MiddleHorizontal, lenBorder))) out.WriteString(colorsBorder.Sprint(t.style.Box.TopRight)) } lenText := rowLength - text.RuneWidthWithoutEscSequences(t.style.Box.PaddingLeft+t.style.Box.PaddingRight) if t.style.Options.DrawBorder { lenText -= text.RuneWidthWithoutEscSequences(t.style.Box.Left + t.style.Box.Right) } titleText := text.WrapText(t.title, lenText) for _, titleLine := range strings.Split(titleText, "\n") { t.renderTitleLine(out, lenText, titleLine, colors, colorsBorder) } } } func (t *Table) renderTitleLine(out *strings.Builder, lenText int, titleLine string, colors text.Colors, colorsBorder text.Colors) { titleLine = strings.TrimSpace(titleLine) titleLine = t.style.Title.Format.Apply(titleLine) titleLine = t.style.Title.Align.Apply(titleLine, lenText) titleLine = t.style.Box.PaddingLeft + titleLine + t.style.Box.PaddingRight if out.Len() > 0 { out.WriteRune('\n') } if t.style.Options.DrawBorder { out.WriteString(colorsBorder.Sprint(t.style.Box.Left)) } out.WriteString(colors.Sprint(titleLine)) if t.style.Options.DrawBorder { out.WriteString(colorsBorder.Sprint(t.style.Box.Right)) } } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/table/render_csv.go ================================================ package table import ( "fmt" "strings" "unicode/utf8" ) // RenderCSV renders the Table in CSV format. Example: // // #,First Name,Last Name,Salary, // 1,Arya,Stark,3000, // 20,Jon,Snow,2000,"You know nothing\, Jon Snow!" // 300,Tyrion,Lannister,5000, // ,,Total,10000, func (t *Table) RenderCSV() string { t.initForRender() var out strings.Builder if t.numColumns > 0 { if t.title != "" { out.WriteString(t.title) } if t.autoIndex && len(t.rowsHeader) == 0 { t.csvRenderRow(&out, t.getAutoIndexColumnIDs(), renderHint{isAutoIndexRow: true, isHeaderRow: true}) } t.csvRenderRows(&out, t.rowsHeader, renderHint{isHeaderRow: true}) t.csvRenderRows(&out, t.rows, renderHint{}) t.csvRenderRows(&out, t.rowsFooter, renderHint{isFooterRow: true}) if t.caption != "" { out.WriteRune('\n') out.WriteString(t.caption) } } return t.render(&out) } func (t *Table) csvFixCommas(str string) string { return strings.Replace(str, ",", "\\,", -1) } func (t *Table) csvFixDoubleQuotes(str string) string { return strings.Replace(str, "\"", "\\\"", -1) } func (t *Table) csvRenderRow(out *strings.Builder, row rowStr, hint renderHint) { // when working on line number 2 or more, insert a newline first if out.Len() > 0 { out.WriteRune('\n') } // generate the columns to render in CSV format and append to "out" for colIdx, colStr := range row { // auto-index column if colIdx == 0 && t.autoIndex { if hint.isRegularRow() { out.WriteString(fmt.Sprint(hint.rowNumber)) } out.WriteRune(',') } if colIdx > 0 { out.WriteRune(',') } if strings.ContainsAny(colStr, "\",\n") { out.WriteRune('"') out.WriteString(t.csvFixCommas(t.csvFixDoubleQuotes(colStr))) out.WriteRune('"') } else if utf8.RuneCountInString(colStr) > 0 { out.WriteString(colStr) } } for colIdx := len(row); colIdx < t.numColumns; colIdx++ { out.WriteRune(',') } } func (t *Table) csvRenderRows(out *strings.Builder, rows []rowStr, hint renderHint) { for rowIdx, row := range rows { hint.rowNumber = rowIdx + 1 t.csvRenderRow(out, row, hint) } } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/table/render_hint.go ================================================ package table // renderHint has hints for the Render*() logic type renderHint struct { isAutoIndexColumn bool // auto-index column? isAutoIndexRow bool // auto-index row? isBorderBottom bool // bottom-border? isBorderTop bool // top-border? isFirstRow bool // first-row of header/footer/regular-rows? isFooterRow bool // footer row? isHeaderRow bool // header row? isLastLineOfRow bool // last-line of the current row? isLastRow bool // last-row of header/footer/regular-rows? isSeparatorRow bool // separator row? isTitleRow bool // title row? rowLineNumber int // the line number for a multi-line row rowNumber int // the row number/index } func (h *renderHint) isBorderOrSeparator() bool { return h.isBorderTop || h.isSeparatorRow || h.isBorderBottom } func (h *renderHint) isRegularRow() bool { return !h.isHeaderRow && !h.isFooterRow } func (h *renderHint) isRegularNonSeparatorRow() bool { return !h.isHeaderRow && !h.isFooterRow && !h.isSeparatorRow } func (h *renderHint) isHeaderOrFooterSeparator() bool { return h.isSeparatorRow && !h.isBorderBottom && !h.isBorderTop && ((h.isHeaderRow && !h.isLastRow) || (h.isFooterRow && (!h.isFirstRow || h.rowNumber > 0))) } func (h *renderHint) isLastLineOfLastRow() bool { return h.isLastLineOfRow && h.isLastRow } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/table/render_html.go ================================================ package table import ( "fmt" "html" "strings" ) const ( // DefaultHTMLCSSClass stores the css-class to use when none-provided via // SetHTMLCSSClass(cssClass string). DefaultHTMLCSSClass = "go-pretty-table" ) // RenderHTML renders the Table in HTML format. Example: // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
#First NameLast NameSalary 
1AryaStark3000 
20JonSnow2000You know nothing, Jon Snow!
300TyrionLannister5000 
  Total10000 
func (t *Table) RenderHTML() string { t.initForRender() var out strings.Builder if t.numColumns > 0 { out.WriteString("\n") t.htmlRenderTitle(&out) t.htmlRenderRowsHeader(&out) t.htmlRenderRows(&out, t.rows, renderHint{}) t.htmlRenderRowsFooter(&out) t.htmlRenderCaption(&out) out.WriteString("
") } return t.render(&out) } func (t *Table) htmlGetColStrAndTag(row rowStr, colIdx int, hint renderHint) (string, string) { // get the column contents var colStr string if colIdx < len(row) { colStr = row[colIdx] } // header uses "th" instead of "td" colTagName := "td" if hint.isHeaderRow { colTagName = "th" } return colStr, colTagName } func (t *Table) htmlRenderCaption(out *strings.Builder) { if t.caption != "" { out.WriteString(" ") out.WriteString(t.caption) out.WriteString("\n") } } func (t *Table) htmlRenderColumn(out *strings.Builder, colStr string) { if t.style.HTML.EscapeText { colStr = html.EscapeString(colStr) } if t.style.HTML.Newline != "\n" { colStr = strings.Replace(colStr, "\n", t.style.HTML.Newline, -1) } out.WriteString(colStr) } func (t *Table) htmlRenderColumnAttributes(out *strings.Builder, colIdx int, hint renderHint) { // determine the HTML "align"/"valign" property values align := t.getAlign(colIdx, hint).HTMLProperty() vAlign := t.getVAlign(colIdx, hint).HTMLProperty() // determine the HTML "class" property values for the colors class := t.getColumnColors(colIdx, hint).HTMLProperty() if align != "" { out.WriteRune(' ') out.WriteString(align) } if class != "" { out.WriteRune(' ') out.WriteString(class) } if vAlign != "" { out.WriteRune(' ') out.WriteString(vAlign) } } func (t *Table) htmlRenderColumnAutoIndex(out *strings.Builder, hint renderHint) { if hint.isHeaderRow { out.WriteString(" ") out.WriteString(t.style.HTML.EmptyColumn) out.WriteString("\n") } else if hint.isFooterRow { out.WriteString(" ") out.WriteString(t.style.HTML.EmptyColumn) out.WriteString("\n") } else { out.WriteString(" ") out.WriteString(fmt.Sprint(hint.rowNumber)) out.WriteString("\n") } } func (t *Table) htmlRenderRow(out *strings.Builder, row rowStr, hint renderHint) { out.WriteString(" \n") for colIdx := 0; colIdx < t.numColumns; colIdx++ { // auto-index column if colIdx == 0 && t.autoIndex { t.htmlRenderColumnAutoIndex(out, hint) } colStr, colTagName := t.htmlGetColStrAndTag(row, colIdx, hint) // write the row out.WriteString(" <") out.WriteString(colTagName) t.htmlRenderColumnAttributes(out, colIdx, hint) out.WriteString(">") if len(colStr) == 0 { out.WriteString(t.style.HTML.EmptyColumn) } else { t.htmlRenderColumn(out, colStr) } out.WriteString("\n") } out.WriteString(" \n") } func (t *Table) htmlRenderRows(out *strings.Builder, rows []rowStr, hint renderHint) { if len(rows) > 0 { // determine that tag to use based on the type of the row rowsTag := "tbody" if hint.isHeaderRow { rowsTag = "thead" } else if hint.isFooterRow { rowsTag = "tfoot" } var renderedTagOpen, shouldRenderTagClose bool for idx, row := range rows { hint.rowNumber = idx + 1 if len(row) > 0 { if !renderedTagOpen { out.WriteString(" <") out.WriteString(rowsTag) out.WriteString(">\n") renderedTagOpen = true } t.htmlRenderRow(out, row, hint) shouldRenderTagClose = true } } if shouldRenderTagClose { out.WriteString(" \n") } } } func (t *Table) htmlRenderRowsFooter(out *strings.Builder) { if len(t.rowsFooter) > 0 { t.htmlRenderRows(out, t.rowsFooter, renderHint{isFooterRow: true}) } } func (t *Table) htmlRenderRowsHeader(out *strings.Builder) { if len(t.rowsHeader) > 0 { t.htmlRenderRows(out, t.rowsHeader, renderHint{isHeaderRow: true}) } else if t.autoIndex { hint := renderHint{isAutoIndexRow: true, isHeaderRow: true} t.htmlRenderRows(out, []rowStr{t.getAutoIndexColumnIDs()}, hint) } } func (t *Table) htmlRenderTitle(out *strings.Builder) { if t.title != "" { align := t.style.Title.Align.HTMLProperty() colors := t.style.Title.Colors.HTMLProperty() title := t.style.Title.Format.Apply(t.title) out.WriteString(" ') out.WriteString(title) out.WriteString("\n") } } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/table/render_init.go ================================================ package table import ( "fmt" "strings" "github.com/jedib0t/go-pretty/v6/text" ) func (t *Table) analyzeAndStringify(row Row, hint renderHint) rowStr { // update t.numColumns if this row is the longest seen till now if len(row) > t.numColumns { // init the slice for the first time; and pad it the rest of the time if t.numColumns == 0 { t.columnIsNonNumeric = make([]bool, len(row)) } else { t.columnIsNonNumeric = append(t.columnIsNonNumeric, make([]bool, len(row)-t.numColumns)...) } // update t.numColumns t.numColumns = len(row) } // convert each column to string and figure out if it has non-numeric data rowOut := make(rowStr, len(row)) for colIdx, col := range row { // if the column is not a number, keep track of it if !hint.isHeaderRow && !hint.isFooterRow && !t.columnIsNonNumeric[colIdx] && !isNumber(col) { t.columnIsNonNumeric[colIdx] = true } rowOut[colIdx] = t.analyzeAndStringifyColumn(colIdx, col, hint) } return rowOut } func (t *Table) analyzeAndStringifyColumn(colIdx int, col interface{}, hint renderHint) string { // convert to a string and store it in the row var colStr string if transformer := t.getColumnTransformer(colIdx, hint); transformer != nil { colStr = transformer(col) } else if colStrVal, ok := col.(string); ok { colStr = colStrVal } else { colStr = fmt.Sprint(col) } colStr = strings.ReplaceAll(colStr, "\t", " ") colStr = strings.ReplaceAll(colStr, "\r", "") return fmt.Sprintf("%s%s", t.style.Format.Direction.Modifier(), colStr) } func (t *Table) extractMaxColumnLengths(rows []rowStr, hint renderHint) { for rowIdx, row := range rows { hint.rowNumber = rowIdx + 1 t.extractMaxColumnLengthsFromRow(row, t.getMergedColumnIndices(row, hint)) } } func (t *Table) extractMaxColumnLengthsFromRow(row rowStr, mci mergedColumnIndices) { for colIdx, colStr := range row { longestLineLen := text.LongestLineLen(colStr) maxColWidth := t.getColumnWidthMax(colIdx) if maxColWidth > 0 && maxColWidth < longestLineLen { longestLineLen = maxColWidth } mergedColumnsLength := mci.mergedLength(colIdx, t.maxColumnLengths) if longestLineLen > mergedColumnsLength { if mergedColumnsLength > 0 { t.extractMaxColumnLengthsFromRowForMergedColumns(colIdx, longestLineLen, mci) } else { t.maxColumnLengths[colIdx] = longestLineLen } } else if maxColWidth == 0 && longestLineLen > t.maxColumnLengths[colIdx] { t.maxColumnLengths[colIdx] = longestLineLen } } } func (t *Table) extractMaxColumnLengthsFromRowForMergedColumns(colIdx int, mergedColumnLength int, mci mergedColumnIndices) { numMergedColumns := mci.len(colIdx) mergedColumnLength -= (numMergedColumns - 1) * text.RuneWidthWithoutEscSequences(t.style.Box.MiddleSeparator) maxLengthSplitAcrossColumns := mergedColumnLength / numMergedColumns if maxLengthSplitAcrossColumns > t.maxColumnLengths[colIdx] { t.maxColumnLengths[colIdx] = maxLengthSplitAcrossColumns } for otherColIdx := range mci[colIdx] { if maxLengthSplitAcrossColumns > t.maxColumnLengths[otherColIdx] { t.maxColumnLengths[otherColIdx] = maxLengthSplitAcrossColumns } } } func (t *Table) initForRender() { // pick a default style if none was set until now t.Style() // reset rendering state t.reset() // initialize the column configs and normalize them t.initForRenderColumnConfigs() // initialize and stringify all the raw rows t.initForRenderRows() // find the longest continuous line in each column t.initForRenderColumnLengths() // generate a separator row and calculate maximum row length t.initForRenderRowSeparator() // reset the counter for the number of lines rendered t.numLinesRendered = 0 } func (t *Table) initForRenderColumnConfigs() { t.columnConfigMap = map[int]ColumnConfig{} for _, colCfg := range t.columnConfigs { // find the column number if none provided; this logic can work only if // a header row is present and has a column with the given name if colCfg.Number == 0 { for _, row := range t.rowsHeaderRaw { colCfg.Number = row.findColumnNumber(colCfg.Name) if colCfg.Number > 0 { break } } } if colCfg.Number > 0 { t.columnConfigMap[colCfg.Number-1] = colCfg } } } func (t *Table) initForRenderColumnLengths() { t.maxColumnLengths = make([]int, t.numColumns) t.extractMaxColumnLengths(t.rowsHeader, renderHint{isHeaderRow: true}) t.extractMaxColumnLengths(t.rows, renderHint{}) t.extractMaxColumnLengths(t.rowsFooter, renderHint{isFooterRow: true}) // increase the column lengths if any are under the limits for colIdx := range t.maxColumnLengths { minWidth := t.getColumnWidthMin(colIdx) if minWidth > 0 && t.maxColumnLengths[colIdx] < minWidth { t.maxColumnLengths[colIdx] = minWidth } } } func (t *Table) initForRenderHideColumns() { if !t.hasHiddenColumns() { return } colIdxMap := t.hideColumns() // re-create columnIsNonNumeric with new column indices columnIsNonNumeric := make([]bool, t.numColumns) for oldColIdx, nonNumeric := range t.columnIsNonNumeric { if newColIdx, ok := colIdxMap[oldColIdx]; ok { columnIsNonNumeric[newColIdx] = nonNumeric } } t.columnIsNonNumeric = columnIsNonNumeric // re-create columnConfigMap with new column indices columnConfigMap := make(map[int]ColumnConfig) for oldColIdx, cc := range t.columnConfigMap { if newColIdx, ok := colIdxMap[oldColIdx]; ok { columnConfigMap[newColIdx] = cc } } t.columnConfigMap = columnConfigMap } func (t *Table) initForRenderRows() { // auto-index: calc the index column's max length t.autoIndexVIndexMaxLength = len(fmt.Sprint(len(t.rowsRaw))) // stringify all the rows to make it easy to render if t.rowPainter != nil { t.rowsColors = make([]text.Colors, len(t.rowsRaw)) } t.rows = t.initForRenderRowsStringify(t.rowsRaw, renderHint{}) t.rowsFooter = t.initForRenderRowsStringify(t.rowsFooterRaw, renderHint{isFooterRow: true}) t.rowsHeader = t.initForRenderRowsStringify(t.rowsHeaderRaw, renderHint{isHeaderRow: true}) // sort the rows as requested t.initForRenderSortRows() // suppress columns without any content t.initForRenderSuppressColumns() // strip out hidden columns t.initForRenderHideColumns() } func (t *Table) initForRenderRowsStringify(rows []Row, hint renderHint) []rowStr { rowsStr := make([]rowStr, len(rows)) for idx, row := range rows { if t.rowPainter != nil && hint.isRegularRow() { t.rowsColors[idx] = t.rowPainter(row) } rowsStr[idx] = t.analyzeAndStringify(row, hint) } return rowsStr } func (t *Table) initForRenderRowSeparator() { t.maxRowLength = 0 if t.autoIndex { t.maxRowLength += text.RuneWidthWithoutEscSequences(t.style.Box.PaddingLeft) t.maxRowLength += len(fmt.Sprint(len(t.rows))) t.maxRowLength += text.RuneWidthWithoutEscSequences(t.style.Box.PaddingRight) if t.style.Options.SeparateColumns { t.maxRowLength += text.RuneWidthWithoutEscSequences(t.style.Box.MiddleSeparator) } } if t.style.Options.SeparateColumns { t.maxRowLength += text.RuneWidthWithoutEscSequences(t.style.Box.MiddleSeparator) * (t.numColumns - 1) } t.rowSeparator = make(rowStr, t.numColumns) for colIdx, maxColumnLength := range t.maxColumnLengths { maxColumnLength += text.RuneWidthWithoutEscSequences(t.style.Box.PaddingLeft + t.style.Box.PaddingRight) t.maxRowLength += maxColumnLength t.rowSeparator[colIdx] = text.RepeatAndTrim(t.style.Box.MiddleHorizontal, maxColumnLength) } if t.style.Options.DrawBorder { t.maxRowLength += text.RuneWidthWithoutEscSequences(t.style.Box.Left + t.style.Box.Right) } } func (t *Table) initForRenderSortRows() { if len(t.sortBy) == 0 { return } // sort the rows sortedRowIndices := t.getSortedRowIndices() sortedRows := make([]rowStr, len(t.rows)) for idx := range t.rows { sortedRows[idx] = t.rows[sortedRowIndices[idx]] } t.rows = sortedRows // sort the rowsColors if len(t.rowsColors) > 0 { sortedRowsColors := make([]text.Colors, len(t.rows)) for idx := range t.rows { sortedRowsColors[idx] = t.rowsColors[sortedRowIndices[idx]] } t.rowsColors = sortedRowsColors } } func (t *Table) initForRenderSuppressColumns() { shouldSuppressColumn := func(colIdx int) bool { for _, row := range t.rows { if colIdx < len(row) && row[colIdx] != "" { return false } } return true } if t.suppressEmptyColumns { for colIdx := 0; colIdx < t.numColumns; colIdx++ { if shouldSuppressColumn(colIdx) { cc := t.columnConfigMap[colIdx] cc.Hidden = true t.columnConfigMap[colIdx] = cc } } } } // reset initializes all the variables used to maintain rendering information // that are written to in this file func (t *Table) reset() { t.autoIndexVIndexMaxLength = 0 t.columnConfigMap = nil t.columnIsNonNumeric = nil t.maxColumnLengths = nil t.maxRowLength = 0 t.numColumns = 0 t.numLinesRendered = 0 t.rowSeparator = nil t.rows = nil t.rowsColors = nil t.rowsFooter = nil t.rowsHeader = nil } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/table/render_markdown.go ================================================ package table import ( "fmt" "strings" ) // RenderMarkdown renders the Table in Markdown format. Example: // // | # | First Name | Last Name | Salary | | // | ---:| --- | --- | ---:| --- | // | 1 | Arya | Stark | 3000 | | // | 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! | // | 300 | Tyrion | Lannister | 5000 | | // | | | Total | 10000 | | func (t *Table) RenderMarkdown() string { t.initForRender() var out strings.Builder if t.numColumns > 0 { t.markdownRenderTitle(&out) t.markdownRenderRowsHeader(&out) t.markdownRenderRows(&out, t.rows, renderHint{}) t.markdownRenderRowsFooter(&out) t.markdownRenderCaption(&out) } return t.render(&out) } func (t *Table) markdownRenderCaption(out *strings.Builder) { if t.caption != "" { out.WriteRune('\n') out.WriteRune('_') out.WriteString(t.caption) out.WriteRune('_') } } func (t *Table) markdownRenderRow(out *strings.Builder, row rowStr, hint renderHint) { // when working on line number 2 or more, insert a newline first if out.Len() > 0 { out.WriteRune('\n') } // render each column up to the max. columns seen in all the rows out.WriteRune('|') for colIdx := 0; colIdx < t.numColumns; colIdx++ { t.markdownRenderRowAutoIndex(out, colIdx, hint) if hint.isSeparatorRow { out.WriteString(t.getAlign(colIdx, hint).MarkdownProperty()) } else { var colStr string if colIdx < len(row) { colStr = row[colIdx] } out.WriteRune(' ') colStr = strings.ReplaceAll(colStr, "|", "\\|") colStr = strings.ReplaceAll(colStr, "\n", "
") out.WriteString(colStr) out.WriteRune(' ') } out.WriteRune('|') } } func (t *Table) markdownRenderRowAutoIndex(out *strings.Builder, colIdx int, hint renderHint) { if colIdx == 0 && t.autoIndex { out.WriteRune(' ') if hint.isSeparatorRow { out.WriteString("---:") } else if hint.isRegularRow() { out.WriteString(fmt.Sprintf("%d ", hint.rowNumber)) } out.WriteRune('|') } } func (t *Table) markdownRenderRows(out *strings.Builder, rows []rowStr, hint renderHint) { if len(rows) > 0 { for idx, row := range rows { hint.rowNumber = idx + 1 t.markdownRenderRow(out, row, hint) if idx == len(rows)-1 && hint.isHeaderRow { t.markdownRenderRow(out, t.rowSeparator, renderHint{isSeparatorRow: true}) } } } } func (t *Table) markdownRenderRowsFooter(out *strings.Builder) { t.markdownRenderRows(out, t.rowsFooter, renderHint{isFooterRow: true}) } func (t *Table) markdownRenderRowsHeader(out *strings.Builder) { if len(t.rowsHeader) > 0 { t.markdownRenderRows(out, t.rowsHeader, renderHint{isHeaderRow: true}) } else if t.autoIndex { t.markdownRenderRows(out, []rowStr{t.getAutoIndexColumnIDs()}, renderHint{isAutoIndexRow: true, isHeaderRow: true}) } } func (t *Table) markdownRenderTitle(out *strings.Builder) { if t.title != "" { out.WriteString("# ") out.WriteString(t.title) } } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/table/render_tsv.go ================================================ package table import ( "fmt" "strings" ) func (t *Table) RenderTSV() string { t.initForRender() var out strings.Builder if t.numColumns > 0 { if t.title != "" { out.WriteString(t.title) } if t.autoIndex && len(t.rowsHeader) == 0 { t.tsvRenderRow(&out, t.getAutoIndexColumnIDs(), renderHint{isAutoIndexRow: true, isHeaderRow: true}) } t.tsvRenderRows(&out, t.rowsHeader, renderHint{isHeaderRow: true}) t.tsvRenderRows(&out, t.rows, renderHint{}) t.tsvRenderRows(&out, t.rowsFooter, renderHint{isFooterRow: true}) if t.caption != "" { out.WriteRune('\n') out.WriteString(t.caption) } } return t.render(&out) } func (t *Table) tsvRenderRow(out *strings.Builder, row rowStr, hint renderHint) { if out.Len() > 0 { out.WriteRune('\n') } for idx, col := range row { if idx == 0 && t.autoIndex { if hint.isRegularRow() { out.WriteString(fmt.Sprint(hint.rowNumber)) } out.WriteRune('\t') } if idx > 0 { out.WriteRune('\t') } if strings.ContainsAny(col, "\t\n\"") || strings.Contains(col, " ") { out.WriteString(fmt.Sprintf("\"%s\"", t.tsvFixDoubleQuotes(col))) } else { out.WriteString(col) } } for colIdx := len(row); colIdx < t.numColumns; colIdx++ { out.WriteRune('\t') } } func (t *Table) tsvFixDoubleQuotes(str string) string { return strings.Replace(str, "\"", "\"\"", -1) } func (t *Table) tsvRenderRows(out *strings.Builder, rows []rowStr, hint renderHint) { for idx, row := range rows { hint.rowNumber = idx + 1 t.tsvRenderRow(out, row, hint) } } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/table/sort.go ================================================ package table import ( "sort" "strconv" ) // SortBy defines What to sort (Column Name or Number), and How to sort (Mode). type SortBy struct { // Name is the name of the Column as it appears in the first Header row. // If a Header is not provided, or the name is not found in the header, this // will not work. Name string // Number is the Column # from left. When specified, it overrides the Name // property. If you know the exact Column number, use this instead of Name. Number int // Mode tells the Writer how to Sort. Asc/Dsc/etc. Mode SortMode } // SortMode defines How to sort. type SortMode int const ( // Asc sorts the column in Ascending order alphabetically. Asc SortMode = iota // AscNumeric sorts the column in Ascending order numerically. AscNumeric // Dsc sorts the column in Descending order alphabetically. Dsc // DscNumeric sorts the column in Descending order numerically. DscNumeric ) type rowsSorter struct { rows []rowStr sortBy []SortBy sortedIndices []int } // getSortedRowIndices sorts and returns the row indices in Sorted order as // directed by Table.sortBy which can be set using Table.SortBy(...) func (t *Table) getSortedRowIndices() []int { sortedIndices := make([]int, len(t.rows)) for idx := range t.rows { sortedIndices[idx] = idx } if t.sortBy != nil && len(t.sortBy) > 0 { sort.Sort(rowsSorter{ rows: t.rows, sortBy: t.parseSortBy(t.sortBy), sortedIndices: sortedIndices, }) } return sortedIndices } func (t *Table) parseSortBy(sortBy []SortBy) []SortBy { var resSortBy []SortBy for _, col := range sortBy { colNum := 0 if col.Number > 0 && col.Number <= t.numColumns { colNum = col.Number } else if col.Name != "" && len(t.rowsHeader) > 0 { for idx, colName := range t.rowsHeader[0] { if col.Name == colName { colNum = idx + 1 break } } } if colNum > 0 { resSortBy = append(resSortBy, SortBy{ Name: col.Name, Number: colNum, Mode: col.Mode, }) } } return resSortBy } func (rs rowsSorter) Len() int { return len(rs.rows) } func (rs rowsSorter) Swap(i, j int) { rs.sortedIndices[i], rs.sortedIndices[j] = rs.sortedIndices[j], rs.sortedIndices[i] } func (rs rowsSorter) Less(i, j int) bool { realI, realJ := rs.sortedIndices[i], rs.sortedIndices[j] for _, col := range rs.sortBy { rowI, rowJ, colIdx := rs.rows[realI], rs.rows[realJ], col.Number-1 if colIdx < len(rowI) && colIdx < len(rowJ) { shouldContinue, returnValue := rs.lessColumns(rowI, rowJ, colIdx, col) if !shouldContinue { return returnValue } } } return false } func (rs rowsSorter) lessColumns(rowI rowStr, rowJ rowStr, colIdx int, col SortBy) (bool, bool) { if rowI[colIdx] == rowJ[colIdx] { return true, false } else if col.Mode == Asc { return false, rowI[colIdx] < rowJ[colIdx] } else if col.Mode == Dsc { return false, rowI[colIdx] > rowJ[colIdx] } iVal, iErr := strconv.ParseFloat(rowI[colIdx], 64) jVal, jErr := strconv.ParseFloat(rowJ[colIdx], 64) if iErr == nil && jErr == nil { if col.Mode == AscNumeric { return false, iVal < jVal } else if col.Mode == DscNumeric { return false, jVal < iVal } } return true, false } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/table/style.go ================================================ package table import ( "github.com/jedib0t/go-pretty/v6/text" ) // Style declares how to render the Table and provides very fine-grained control // on how the Table gets rendered on the Console. type Style struct { Name string // name of the Style Box BoxStyle // characters to use for the boxes Color ColorOptions // colors to use for the rows and columns Format FormatOptions // formatting options for the rows and columns HTML HTMLOptions // rendering options for HTML mode Options Options // misc. options for the table Title TitleOptions // formation options for the title text } var ( // StyleDefault renders a Table like below: // +-----+------------+-----------+--------+-----------------------------+ // | # | FIRST NAME | LAST NAME | SALARY | | // +-----+------------+-----------+--------+-----------------------------+ // | 1 | Arya | Stark | 3000 | | // | 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! | // | 300 | Tyrion | Lannister | 5000 | | // +-----+------------+-----------+--------+-----------------------------+ // | | | TOTAL | 10000 | | // +-----+------------+-----------+--------+-----------------------------+ StyleDefault = Style{ Name: "StyleDefault", Box: StyleBoxDefault, Color: ColorOptionsDefault, Format: FormatOptionsDefault, HTML: DefaultHTMLOptions, Options: OptionsDefault, Title: TitleOptionsDefault, } // StyleBold renders a Table like below: // ┏━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ // ┃ # ┃ FIRST NAME ┃ LAST NAME ┃ SALARY ┃ ┃ // ┣━━━━━╋━━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ // ┃ 1 ┃ Arya ┃ Stark ┃ 3000 ┃ ┃ // ┃ 20 ┃ Jon ┃ Snow ┃ 2000 ┃ You know nothing, Jon Snow! ┃ // ┃ 300 ┃ Tyrion ┃ Lannister ┃ 5000 ┃ ┃ // ┣━━━━━╋━━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ // ┃ ┃ ┃ TOTAL ┃ 10000 ┃ ┃ // ┗━━━━━┻━━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ StyleBold = Style{ Name: "StyleBold", Box: StyleBoxBold, Color: ColorOptionsDefault, Format: FormatOptionsDefault, HTML: DefaultHTMLOptions, Options: OptionsDefault, Title: TitleOptionsDefault, } // StyleColoredBright renders a Table without any borders or separators, // and with Black text on Cyan background for Header/Footer and // White background for other rows. StyleColoredBright = Style{ Name: "StyleColoredBright", Box: StyleBoxDefault, Color: ColorOptionsBright, Format: FormatOptionsDefault, HTML: DefaultHTMLOptions, Options: OptionsNoBordersAndSeparators, Title: TitleOptionsDark, } // StyleColoredDark renders a Table without any borders or separators, and // with Header/Footer in Cyan text and other rows with White text, all on // Black background. StyleColoredDark = Style{ Name: "StyleColoredDark", Box: StyleBoxDefault, Color: ColorOptionsDark, Format: FormatOptionsDefault, HTML: DefaultHTMLOptions, Options: OptionsNoBordersAndSeparators, Title: TitleOptionsBright, } // StyleColoredBlackOnBlueWhite renders a Table without any borders or // separators, and with Black text on Blue background for Header/Footer and // White background for other rows. StyleColoredBlackOnBlueWhite = Style{ Name: "StyleColoredBlackOnBlueWhite", Box: StyleBoxDefault, Color: ColorOptionsBlackOnBlueWhite, Format: FormatOptionsDefault, HTML: DefaultHTMLOptions, Options: OptionsNoBordersAndSeparators, Title: TitleOptionsBlueOnBlack, } // StyleColoredBlackOnCyanWhite renders a Table without any borders or // separators, and with Black text on Cyan background for Header/Footer and // White background for other rows. StyleColoredBlackOnCyanWhite = Style{ Name: "StyleColoredBlackOnCyanWhite", Box: StyleBoxDefault, Color: ColorOptionsBlackOnCyanWhite, Format: FormatOptionsDefault, HTML: DefaultHTMLOptions, Options: OptionsNoBordersAndSeparators, Title: TitleOptionsCyanOnBlack, } // StyleColoredBlackOnGreenWhite renders a Table without any borders or // separators, and with Black text on Green background for Header/Footer and // White background for other rows. StyleColoredBlackOnGreenWhite = Style{ Name: "StyleColoredBlackOnGreenWhite", Box: StyleBoxDefault, Color: ColorOptionsBlackOnGreenWhite, Format: FormatOptionsDefault, HTML: DefaultHTMLOptions, Options: OptionsNoBordersAndSeparators, Title: TitleOptionsGreenOnBlack, } // StyleColoredBlackOnMagentaWhite renders a Table without any borders or // separators, and with Black text on Magenta background for Header/Footer and // White background for other rows. StyleColoredBlackOnMagentaWhite = Style{ Name: "StyleColoredBlackOnMagentaWhite", Box: StyleBoxDefault, Color: ColorOptionsBlackOnMagentaWhite, Format: FormatOptionsDefault, HTML: DefaultHTMLOptions, Options: OptionsNoBordersAndSeparators, Title: TitleOptionsMagentaOnBlack, } // StyleColoredBlackOnYellowWhite renders a Table without any borders or // separators, and with Black text on Yellow background for Header/Footer and // White background for other rows. StyleColoredBlackOnYellowWhite = Style{ Name: "StyleColoredBlackOnYellowWhite", Box: StyleBoxDefault, Color: ColorOptionsBlackOnYellowWhite, Format: FormatOptionsDefault, HTML: DefaultHTMLOptions, Options: OptionsNoBordersAndSeparators, Title: TitleOptionsYellowOnBlack, } // StyleColoredBlackOnRedWhite renders a Table without any borders or // separators, and with Black text on Red background for Header/Footer and // White background for other rows. StyleColoredBlackOnRedWhite = Style{ Name: "StyleColoredBlackOnRedWhite", Box: StyleBoxDefault, Color: ColorOptionsBlackOnRedWhite, Format: FormatOptionsDefault, HTML: DefaultHTMLOptions, Options: OptionsNoBordersAndSeparators, Title: TitleOptionsRedOnBlack, } // StyleColoredBlueWhiteOnBlack renders a Table without any borders or // separators, and with Header/Footer in Blue text and other rows with // White text, all on Black background. StyleColoredBlueWhiteOnBlack = Style{ Name: "StyleColoredBlueWhiteOnBlack", Box: StyleBoxDefault, Color: ColorOptionsBlueWhiteOnBlack, Format: FormatOptionsDefault, HTML: DefaultHTMLOptions, Options: OptionsNoBordersAndSeparators, Title: TitleOptionsBlackOnBlue, } // StyleColoredCyanWhiteOnBlack renders a Table without any borders or // separators, and with Header/Footer in Cyan text and other rows with // White text, all on Black background. StyleColoredCyanWhiteOnBlack = Style{ Name: "StyleColoredCyanWhiteOnBlack", Box: StyleBoxDefault, Color: ColorOptionsCyanWhiteOnBlack, Format: FormatOptionsDefault, HTML: DefaultHTMLOptions, Options: OptionsNoBordersAndSeparators, Title: TitleOptionsBlackOnCyan, } // StyleColoredGreenWhiteOnBlack renders a Table without any borders or // separators, and with Header/Footer in Green text and other rows with // White text, all on Black background. StyleColoredGreenWhiteOnBlack = Style{ Name: "StyleColoredGreenWhiteOnBlack", Box: StyleBoxDefault, Color: ColorOptionsGreenWhiteOnBlack, Format: FormatOptionsDefault, HTML: DefaultHTMLOptions, Options: OptionsNoBordersAndSeparators, Title: TitleOptionsBlackOnGreen, } // StyleColoredMagentaWhiteOnBlack renders a Table without any borders or // separators, and with Header/Footer in Magenta text and other rows with // White text, all on Black background. StyleColoredMagentaWhiteOnBlack = Style{ Name: "StyleColoredMagentaWhiteOnBlack", Box: StyleBoxDefault, Color: ColorOptionsMagentaWhiteOnBlack, Format: FormatOptionsDefault, HTML: DefaultHTMLOptions, Options: OptionsNoBordersAndSeparators, Title: TitleOptionsBlackOnMagenta, } // StyleColoredRedWhiteOnBlack renders a Table without any borders or // separators, and with Header/Footer in Red text and other rows with // White text, all on Black background. StyleColoredRedWhiteOnBlack = Style{ Name: "StyleColoredRedWhiteOnBlack", Box: StyleBoxDefault, Color: ColorOptionsRedWhiteOnBlack, Format: FormatOptionsDefault, HTML: DefaultHTMLOptions, Options: OptionsNoBordersAndSeparators, Title: TitleOptionsBlackOnRed, } // StyleColoredYellowWhiteOnBlack renders a Table without any borders or // separators, and with Header/Footer in Yellow text and other rows with // White text, all on Black background. StyleColoredYellowWhiteOnBlack = Style{ Name: "StyleColoredYellowWhiteOnBlack", Box: StyleBoxDefault, Color: ColorOptionsYellowWhiteOnBlack, Format: FormatOptionsDefault, HTML: DefaultHTMLOptions, Options: OptionsNoBordersAndSeparators, Title: TitleOptionsBlackOnYellow, } // StyleDouble renders a Table like below: // ╔═════╦════════════╦═══════════╦════════╦═════════════════════════════╗ // ║ # ║ FIRST NAME ║ LAST NAME ║ SALARY ║ ║ // ╠═════╬════════════╬═══════════╬════════╬═════════════════════════════╣ // ║ 1 ║ Arya ║ Stark ║ 3000 ║ ║ // ║ 20 ║ Jon ║ Snow ║ 2000 ║ You know nothing, Jon Snow! ║ // ║ 300 ║ Tyrion ║ Lannister ║ 5000 ║ ║ // ╠═════╬════════════╬═══════════╬════════╬═════════════════════════════╣ // ║ ║ ║ TOTAL ║ 10000 ║ ║ // ╚═════╩════════════╩═══════════╩════════╩═════════════════════════════╝ StyleDouble = Style{ Name: "StyleDouble", Box: StyleBoxDouble, Color: ColorOptionsDefault, Format: FormatOptionsDefault, HTML: DefaultHTMLOptions, Options: OptionsDefault, Title: TitleOptionsDefault, } // StyleLight renders a Table like below: // ┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐ // │ # │ FIRST NAME │ LAST NAME │ SALARY │ │ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤ // │ 1 │ Arya │ Stark │ 3000 │ │ // │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │ // │ 300 │ Tyrion │ Lannister │ 5000 │ │ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤ // │ │ │ TOTAL │ 10000 │ │ // └─────┴────────────┴───────────┴────────┴─────────────────────────────┘ StyleLight = Style{ Name: "StyleLight", Box: StyleBoxLight, Color: ColorOptionsDefault, Format: FormatOptionsDefault, HTML: DefaultHTMLOptions, Options: OptionsDefault, Title: TitleOptionsDefault, } // StyleRounded renders a Table like below: // ╭─────┬────────────┬───────────┬────────┬─────────────────────────────╮ // │ # │ FIRST NAME │ LAST NAME │ SALARY │ │ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤ // │ 1 │ Arya │ Stark │ 3000 │ │ // │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │ // │ 300 │ Tyrion │ Lannister │ 5000 │ │ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤ // │ │ │ TOTAL │ 10000 │ │ // ╰─────┴────────────┴───────────┴────────┴─────────────────────────────╯ StyleRounded = Style{ Name: "StyleRounded", Box: StyleBoxRounded, Color: ColorOptionsDefault, Format: FormatOptionsDefault, HTML: DefaultHTMLOptions, Options: OptionsDefault, Title: TitleOptionsDefault, } // styleTest renders a Table like below: // (-----^------------^-----------^--------^-----------------------------) // [< #>||||< >] // {-----+------------+-----------+--------+-----------------------------} // [< 1>|||< 3000>|< >] // [< 20>|||< 2000>|] // [<300>|||< 5000>|< >] // {-----+------------+-----------+--------+-----------------------------} // [< >|< >||< 10000>|< >] // \-----v------------v-----------v--------v-----------------------------/ styleTest = Style{ Name: "styleTest", Box: styleBoxTest, Color: ColorOptionsDefault, Format: FormatOptionsDefault, HTML: DefaultHTMLOptions, Options: OptionsDefault, Title: TitleOptionsDefault, } ) // BoxStyle defines the characters/strings to use to render the borders and // separators for the Table. type BoxStyle struct { BottomLeft string BottomRight string BottomSeparator string EmptySeparator string Left string LeftSeparator string MiddleHorizontal string MiddleSeparator string MiddleVertical string PaddingLeft string PaddingRight string PageSeparator string Right string RightSeparator string TopLeft string TopRight string TopSeparator string UnfinishedRow string } var ( // StyleBoxDefault defines a Boxed-Table like below: // +-----+------------+-----------+--------+-----------------------------+ // | # | FIRST NAME | LAST NAME | SALARY | | // +-----+------------+-----------+--------+-----------------------------+ // | 1 | Arya | Stark | 3000 | | // | 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! | // | 300 | Tyrion | Lannister | 5000 | | // +-----+------------+-----------+--------+-----------------------------+ // | | | TOTAL | 10000 | | // +-----+------------+-----------+--------+-----------------------------+ StyleBoxDefault = BoxStyle{ BottomLeft: "+", BottomRight: "+", BottomSeparator: "+", EmptySeparator: text.RepeatAndTrim(" ", text.RuneWidthWithoutEscSequences("+")), Left: "|", LeftSeparator: "+", MiddleHorizontal: "-", MiddleSeparator: "+", MiddleVertical: "|", PaddingLeft: " ", PaddingRight: " ", PageSeparator: "\n", Right: "|", RightSeparator: "+", TopLeft: "+", TopRight: "+", TopSeparator: "+", UnfinishedRow: " ~", } // StyleBoxBold defines a Boxed-Table like below: // ┏━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ // ┃ # ┃ FIRST NAME ┃ LAST NAME ┃ SALARY ┃ ┃ // ┣━━━━━╋━━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ // ┃ 1 ┃ Arya ┃ Stark ┃ 3000 ┃ ┃ // ┃ 20 ┃ Jon ┃ Snow ┃ 2000 ┃ You know nothing, Jon Snow! ┃ // ┃ 300 ┃ Tyrion ┃ Lannister ┃ 5000 ┃ ┃ // ┣━━━━━╋━━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ // ┃ ┃ ┃ TOTAL ┃ 10000 ┃ ┃ // ┗━━━━━┻━━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ StyleBoxBold = BoxStyle{ BottomLeft: "┗", BottomRight: "┛", BottomSeparator: "┻", EmptySeparator: text.RepeatAndTrim(" ", text.RuneWidthWithoutEscSequences("╋")), Left: "┃", LeftSeparator: "┣", MiddleHorizontal: "━", MiddleSeparator: "╋", MiddleVertical: "┃", PaddingLeft: " ", PaddingRight: " ", PageSeparator: "\n", Right: "┃", RightSeparator: "┫", TopLeft: "┏", TopRight: "┓", TopSeparator: "┳", UnfinishedRow: " ≈", } // StyleBoxDouble defines a Boxed-Table like below: // ╔═════╦════════════╦═══════════╦════════╦═════════════════════════════╗ // ║ # ║ FIRST NAME ║ LAST NAME ║ SALARY ║ ║ // ╠═════╬════════════╬═══════════╬════════╬═════════════════════════════╣ // ║ 1 ║ Arya ║ Stark ║ 3000 ║ ║ // ║ 20 ║ Jon ║ Snow ║ 2000 ║ You know nothing, Jon Snow! ║ // ║ 300 ║ Tyrion ║ Lannister ║ 5000 ║ ║ // ╠═════╬════════════╬═══════════╬════════╬═════════════════════════════╣ // ║ ║ ║ TOTAL ║ 10000 ║ ║ // ╚═════╩════════════╩═══════════╩════════╩═════════════════════════════╝ StyleBoxDouble = BoxStyle{ BottomLeft: "╚", BottomRight: "╝", BottomSeparator: "╩", EmptySeparator: text.RepeatAndTrim(" ", text.RuneWidthWithoutEscSequences("╬")), Left: "║", LeftSeparator: "╠", MiddleHorizontal: "═", MiddleSeparator: "╬", MiddleVertical: "║", PaddingLeft: " ", PaddingRight: " ", PageSeparator: "\n", Right: "║", RightSeparator: "╣", TopLeft: "╔", TopRight: "╗", TopSeparator: "╦", UnfinishedRow: " ≈", } // StyleBoxLight defines a Boxed-Table like below: // ┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐ // │ # │ FIRST NAME │ LAST NAME │ SALARY │ │ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤ // │ 1 │ Arya │ Stark │ 3000 │ │ // │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │ // │ 300 │ Tyrion │ Lannister │ 5000 │ │ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤ // │ │ │ TOTAL │ 10000 │ │ // └─────┴────────────┴───────────┴────────┴─────────────────────────────┘ StyleBoxLight = BoxStyle{ BottomLeft: "└", BottomRight: "┘", BottomSeparator: "┴", EmptySeparator: text.RepeatAndTrim(" ", text.RuneWidthWithoutEscSequences("┼")), Left: "│", LeftSeparator: "├", MiddleHorizontal: "─", MiddleSeparator: "┼", MiddleVertical: "│", PaddingLeft: " ", PaddingRight: " ", PageSeparator: "\n", Right: "│", RightSeparator: "┤", TopLeft: "┌", TopRight: "┐", TopSeparator: "┬", UnfinishedRow: " ≈", } // StyleBoxRounded defines a Boxed-Table like below: // ╭─────┬────────────┬───────────┬────────┬─────────────────────────────╮ // │ # │ FIRST NAME │ LAST NAME │ SALARY │ │ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤ // │ 1 │ Arya │ Stark │ 3000 │ │ // │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │ // │ 300 │ Tyrion │ Lannister │ 5000 │ │ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤ // │ │ │ TOTAL │ 10000 │ │ // ╰─────┴────────────┴───────────┴────────┴─────────────────────────────╯ StyleBoxRounded = BoxStyle{ BottomLeft: "╰", BottomRight: "╯", BottomSeparator: "┴", EmptySeparator: text.RepeatAndTrim(" ", text.RuneWidthWithoutEscSequences("┼")), Left: "│", LeftSeparator: "├", MiddleHorizontal: "─", MiddleSeparator: "┼", MiddleVertical: "│", PaddingLeft: " ", PaddingRight: " ", PageSeparator: "\n", Right: "│", RightSeparator: "┤", TopLeft: "╭", TopRight: "╮", TopSeparator: "┬", UnfinishedRow: " ≈", } // styleBoxTest defines a Boxed-Table like below: // (-----^------------^-----------^--------^-----------------------------) // [< #>||||< >] // {-----+------------+-----------+--------+-----------------------------} // [< 1>|||< 3000>|< >] // [< 20>|||< 2000>|] // [<300>|||< 5000>|< >] // {-----+------------+-----------+--------+-----------------------------} // [< >|< >||< 10000>|< >] // \-----v------------v-----------v--------v-----------------------------/ styleBoxTest = BoxStyle{ BottomLeft: "\\", BottomRight: "/", BottomSeparator: "v", EmptySeparator: text.RepeatAndTrim(" ", text.RuneWidthWithoutEscSequences("+")), Left: "[", LeftSeparator: "{", MiddleHorizontal: "--", MiddleSeparator: "+", MiddleVertical: "|", PaddingLeft: "<", PaddingRight: ">", PageSeparator: "\n", Right: "]", RightSeparator: "}", TopLeft: "(", TopRight: ")", TopSeparator: "^", UnfinishedRow: " ~~~", } ) // ColorOptions defines the ANSI colors to use for parts of the Table. type ColorOptions struct { Border text.Colors // borders (if nil, uses one of the below) Footer text.Colors // footer row(s) colors Header text.Colors // header row(s) colors IndexColumn text.Colors // index-column colors (row #, etc.) Row text.Colors // regular row(s) colors RowAlternate text.Colors // regular row(s) colors for the even-numbered rows Separator text.Colors // separators (if nil, uses one of the above) } var ( // ColorOptionsDefault defines sensible ANSI color options - basically NONE. ColorOptionsDefault = ColorOptions{} // ColorOptionsBright renders dark text on bright background. ColorOptionsBright = ColorOptionsBlackOnCyanWhite // ColorOptionsDark renders bright text on dark background. ColorOptionsDark = ColorOptionsCyanWhiteOnBlack // ColorOptionsBlackOnBlueWhite renders Black text on Blue/White background. ColorOptionsBlackOnBlueWhite = ColorOptions{ Footer: text.Colors{text.BgBlue, text.FgBlack}, Header: text.Colors{text.BgHiBlue, text.FgBlack}, IndexColumn: text.Colors{text.BgHiBlue, text.FgBlack}, Row: text.Colors{text.BgHiWhite, text.FgBlack}, RowAlternate: text.Colors{text.BgWhite, text.FgBlack}, } // ColorOptionsBlackOnCyanWhite renders Black text on Cyan/White background. ColorOptionsBlackOnCyanWhite = ColorOptions{ Footer: text.Colors{text.BgCyan, text.FgBlack}, Header: text.Colors{text.BgHiCyan, text.FgBlack}, IndexColumn: text.Colors{text.BgHiCyan, text.FgBlack}, Row: text.Colors{text.BgHiWhite, text.FgBlack}, RowAlternate: text.Colors{text.BgWhite, text.FgBlack}, } // ColorOptionsBlackOnGreenWhite renders Black text on Green/White // background. ColorOptionsBlackOnGreenWhite = ColorOptions{ Footer: text.Colors{text.BgGreen, text.FgBlack}, Header: text.Colors{text.BgHiGreen, text.FgBlack}, IndexColumn: text.Colors{text.BgHiGreen, text.FgBlack}, Row: text.Colors{text.BgHiWhite, text.FgBlack}, RowAlternate: text.Colors{text.BgWhite, text.FgBlack}, } // ColorOptionsBlackOnMagentaWhite renders Black text on Magenta/White // background. ColorOptionsBlackOnMagentaWhite = ColorOptions{ Footer: text.Colors{text.BgMagenta, text.FgBlack}, Header: text.Colors{text.BgHiMagenta, text.FgBlack}, IndexColumn: text.Colors{text.BgHiMagenta, text.FgBlack}, Row: text.Colors{text.BgHiWhite, text.FgBlack}, RowAlternate: text.Colors{text.BgWhite, text.FgBlack}, } // ColorOptionsBlackOnRedWhite renders Black text on Red/White background. ColorOptionsBlackOnRedWhite = ColorOptions{ Footer: text.Colors{text.BgRed, text.FgBlack}, Header: text.Colors{text.BgHiRed, text.FgBlack}, IndexColumn: text.Colors{text.BgHiRed, text.FgBlack}, Row: text.Colors{text.BgHiWhite, text.FgBlack}, RowAlternate: text.Colors{text.BgWhite, text.FgBlack}, } // ColorOptionsBlackOnYellowWhite renders Black text on Yellow/White // background. ColorOptionsBlackOnYellowWhite = ColorOptions{ Footer: text.Colors{text.BgYellow, text.FgBlack}, Header: text.Colors{text.BgHiYellow, text.FgBlack}, IndexColumn: text.Colors{text.BgHiYellow, text.FgBlack}, Row: text.Colors{text.BgHiWhite, text.FgBlack}, RowAlternate: text.Colors{text.BgWhite, text.FgBlack}, } // ColorOptionsBlueWhiteOnBlack renders Blue/White text on Black background. ColorOptionsBlueWhiteOnBlack = ColorOptions{ Footer: text.Colors{text.FgBlue, text.BgHiBlack}, Header: text.Colors{text.FgHiBlue, text.BgHiBlack}, IndexColumn: text.Colors{text.FgHiBlue, text.BgHiBlack}, Row: text.Colors{text.FgHiWhite, text.BgBlack}, RowAlternate: text.Colors{text.FgWhite, text.BgBlack}, } // ColorOptionsCyanWhiteOnBlack renders Cyan/White text on Black background. ColorOptionsCyanWhiteOnBlack = ColorOptions{ Footer: text.Colors{text.FgCyan, text.BgHiBlack}, Header: text.Colors{text.FgHiCyan, text.BgHiBlack}, IndexColumn: text.Colors{text.FgHiCyan, text.BgHiBlack}, Row: text.Colors{text.FgHiWhite, text.BgBlack}, RowAlternate: text.Colors{text.FgWhite, text.BgBlack}, } // ColorOptionsGreenWhiteOnBlack renders Green/White text on Black // background. ColorOptionsGreenWhiteOnBlack = ColorOptions{ Footer: text.Colors{text.FgGreen, text.BgHiBlack}, Header: text.Colors{text.FgHiGreen, text.BgHiBlack}, IndexColumn: text.Colors{text.FgHiGreen, text.BgHiBlack}, Row: text.Colors{text.FgHiWhite, text.BgBlack}, RowAlternate: text.Colors{text.FgWhite, text.BgBlack}, } // ColorOptionsMagentaWhiteOnBlack renders Magenta/White text on Black // background. ColorOptionsMagentaWhiteOnBlack = ColorOptions{ Footer: text.Colors{text.FgMagenta, text.BgHiBlack}, Header: text.Colors{text.FgHiMagenta, text.BgHiBlack}, IndexColumn: text.Colors{text.FgHiMagenta, text.BgHiBlack}, Row: text.Colors{text.FgHiWhite, text.BgBlack}, RowAlternate: text.Colors{text.FgWhite, text.BgBlack}, } // ColorOptionsRedWhiteOnBlack renders Red/White text on Black background. ColorOptionsRedWhiteOnBlack = ColorOptions{ Footer: text.Colors{text.FgRed, text.BgHiBlack}, Header: text.Colors{text.FgHiRed, text.BgHiBlack}, IndexColumn: text.Colors{text.FgHiRed, text.BgHiBlack}, Row: text.Colors{text.FgHiWhite, text.BgBlack}, RowAlternate: text.Colors{text.FgWhite, text.BgBlack}, } // ColorOptionsYellowWhiteOnBlack renders Yellow/White text on Black // background. ColorOptionsYellowWhiteOnBlack = ColorOptions{ Footer: text.Colors{text.FgYellow, text.BgHiBlack}, Header: text.Colors{text.FgHiYellow, text.BgHiBlack}, IndexColumn: text.Colors{text.FgHiYellow, text.BgHiBlack}, Row: text.Colors{text.FgHiWhite, text.BgBlack}, RowAlternate: text.Colors{text.FgWhite, text.BgBlack}, } ) // FormatOptions defines the text-formatting to perform on parts of the Table. type FormatOptions struct { Direction text.Direction // (forced) BiDi direction for each Column Footer text.Format // footer row(s) text format Header text.Format // header row(s) text format Row text.Format // (data) row(s) text format } // FormatOptionsDefault defines sensible formatting options. var FormatOptionsDefault = FormatOptions{ Footer: text.FormatUpper, Header: text.FormatUpper, Row: text.FormatDefault, } // HTMLOptions defines the global options to control HTML rendering. type HTMLOptions struct { CSSClass string // CSS class to set on the overall tag EmptyColumn string // string to replace "" columns with (entire content being "") EscapeText bool // escape text into HTML-safe content? Newline string // string to replace "\n" characters with } // DefaultHTMLOptions defines sensible HTML rendering defaults. var DefaultHTMLOptions = HTMLOptions{ CSSClass: DefaultHTMLCSSClass, EmptyColumn: " ", EscapeText: true, Newline: "
", } // Options defines the global options that determine how the Table is // rendered. type Options struct { // DoNotColorBordersAndSeparators disables coloring all the borders and row // or column separators. DoNotColorBordersAndSeparators bool // DrawBorder enables or disables drawing the border around the Table. // Example of a table where it is disabled: // # │ FIRST NAME │ LAST NAME │ SALARY │ // ─────┼────────────┼───────────┼────────┼───────────────────────────── // 1 │ Arya │ Stark │ 3000 │ // 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! // 300 │ Tyrion │ Lannister │ 5000 │ // ─────┼────────────┼───────────┼────────┼───────────────────────────── // │ │ TOTAL │ 10000 │ DrawBorder bool // SeparateColumns enables or disable drawing border between columns. // Example of a table where it is disabled: // ┌─────────────────────────────────────────────────────────────────┐ // │ # FIRST NAME LAST NAME SALARY │ // ├─────────────────────────────────────────────────────────────────┤ // │ 1 Arya Stark 3000 │ // │ 20 Jon Snow 2000 You know nothing, Jon Snow! │ // │ 300 Tyrion Lannister 5000 │ // │ TOTAL 10000 │ // └─────────────────────────────────────────────────────────────────┘ SeparateColumns bool // SeparateFooter enables or disable drawing border between the footer and // the rows. Example of a table where it is disabled: // ┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐ // │ # │ FIRST NAME │ LAST NAME │ SALARY │ │ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤ // │ 1 │ Arya │ Stark │ 3000 │ │ // │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │ // │ 300 │ Tyrion │ Lannister │ 5000 │ │ // │ │ │ TOTAL │ 10000 │ │ // └─────┴────────────┴───────────┴────────┴─────────────────────────────┘ SeparateFooter bool // SeparateHeader enables or disable drawing border between the header and // the rows. Example of a table where it is disabled: // ┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐ // │ # │ FIRST NAME │ LAST NAME │ SALARY │ │ // │ 1 │ Arya │ Stark │ 3000 │ │ // │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │ // │ 300 │ Tyrion │ Lannister │ 5000 │ │ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤ // │ │ │ TOTAL │ 10000 │ │ // └─────┴────────────┴───────────┴────────┴─────────────────────────────┘ SeparateHeader bool // SeparateRows enables or disables drawing separators between each row. // Example of a table where it is enabled: // ┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐ // │ # │ FIRST NAME │ LAST NAME │ SALARY │ │ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤ // │ 1 │ Arya │ Stark │ 3000 │ │ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤ // │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤ // │ 300 │ Tyrion │ Lannister │ 5000 │ │ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤ // │ │ │ TOTAL │ 10000 │ │ // └─────┴────────────┴───────────┴────────┴─────────────────────────────┘ SeparateRows bool } var ( // OptionsDefault defines sensible global options. OptionsDefault = Options{ DrawBorder: true, SeparateColumns: true, SeparateFooter: true, SeparateHeader: true, SeparateRows: false, } // OptionsNoBorders sets up a table without any borders. OptionsNoBorders = Options{ DrawBorder: false, SeparateColumns: true, SeparateFooter: true, SeparateHeader: true, SeparateRows: false, } // OptionsNoBordersAndSeparators sets up a table without any borders or // separators. OptionsNoBordersAndSeparators = Options{ DrawBorder: false, SeparateColumns: false, SeparateFooter: false, SeparateHeader: false, SeparateRows: false, } ) // TitleOptions defines the way the title text is to be rendered. type TitleOptions struct { Align text.Align Colors text.Colors Format text.Format } var ( // TitleOptionsDefault defines sensible title options - basically NONE. TitleOptionsDefault = TitleOptions{} // TitleOptionsBright renders Bright Bold text on Dark background. TitleOptionsBright = TitleOptionsBlackOnCyan // TitleOptionsDark renders Dark Bold text on Bright background. TitleOptionsDark = TitleOptionsCyanOnBlack // TitleOptionsBlackOnBlue renders Black text on Blue background. TitleOptionsBlackOnBlue = TitleOptions{ Colors: append(ColorOptionsBlackOnBlueWhite.Header, text.Bold), } // TitleOptionsBlackOnCyan renders Black Bold text on Cyan background. TitleOptionsBlackOnCyan = TitleOptions{ Colors: append(ColorOptionsBlackOnCyanWhite.Header, text.Bold), } // TitleOptionsBlackOnGreen renders Black Bold text onGreen background. TitleOptionsBlackOnGreen = TitleOptions{ Colors: append(ColorOptionsBlackOnGreenWhite.Header, text.Bold), } // TitleOptionsBlackOnMagenta renders Black Bold text on Magenta background. TitleOptionsBlackOnMagenta = TitleOptions{ Colors: append(ColorOptionsBlackOnMagentaWhite.Header, text.Bold), } // TitleOptionsBlackOnRed renders Black Bold text on Red background. TitleOptionsBlackOnRed = TitleOptions{ Colors: append(ColorOptionsBlackOnRedWhite.Header, text.Bold), } // TitleOptionsBlackOnYellow renders Black Bold text on Yellow background. TitleOptionsBlackOnYellow = TitleOptions{ Colors: append(ColorOptionsBlackOnYellowWhite.Header, text.Bold), } // TitleOptionsBlueOnBlack renders Blue Bold text on Black background. TitleOptionsBlueOnBlack = TitleOptions{ Colors: append(ColorOptionsBlueWhiteOnBlack.Header, text.Bold), } // TitleOptionsCyanOnBlack renders Cyan Bold text on Black background. TitleOptionsCyanOnBlack = TitleOptions{ Colors: append(ColorOptionsCyanWhiteOnBlack.Header, text.Bold), } // TitleOptionsGreenOnBlack renders Green Bold text on Black background. TitleOptionsGreenOnBlack = TitleOptions{ Colors: append(ColorOptionsGreenWhiteOnBlack.Header, text.Bold), } // TitleOptionsMagentaOnBlack renders Magenta Bold text on Black background. TitleOptionsMagentaOnBlack = TitleOptions{ Colors: append(ColorOptionsMagentaWhiteOnBlack.Header, text.Bold), } // TitleOptionsRedOnBlack renders Red Bold text on Black background. TitleOptionsRedOnBlack = TitleOptions{ Colors: append(ColorOptionsRedWhiteOnBlack.Header, text.Bold), } // TitleOptionsYellowOnBlack renders Yellow Bold text on Black background. TitleOptionsYellowOnBlack = TitleOptions{ Colors: append(ColorOptionsYellowWhiteOnBlack.Header, text.Bold), } ) ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/table/table.go ================================================ package table import ( "fmt" "io" "strings" "github.com/jedib0t/go-pretty/v6/text" ) // Row defines a single row in the Table. type Row []interface{} func (r Row) findColumnNumber(colName string) int { for colIdx, col := range r { if fmt.Sprint(col) == colName { return colIdx + 1 } } return 0 } // RowPainter is a custom function that takes a Row as input and returns the // text.Colors{} to use on the entire row type RowPainter func(row Row) text.Colors // rowStr defines a single row in the Table comprised of just string objects. type rowStr []string // areEqual returns true if the contents of the 2 given columns are the same func (row rowStr) areEqual(colIdx1 int, colIdx2 int) bool { return colIdx1 >= 0 && colIdx2 < len(row) && row[colIdx1] == row[colIdx2] } // Table helps print a 2-dimensional array in a human-readable pretty-table. type Table struct { // allowedRowLength is the max allowed length for a row (or line of output) allowedRowLength int // enable automatic indexing of the rows and columns like a spreadsheet? autoIndex bool // autoIndexVIndexMaxLength denotes the length in chars for the last row autoIndexVIndexMaxLength int // caption stores the text to be rendered just below the table; and doesn't // get used when rendered as a CSV caption string // columnIsNonNumeric stores if a column contains non-numbers in all rows columnIsNonNumeric []bool // columnConfigs stores the custom-configuration for 1 or more columns columnConfigs []ColumnConfig // columnConfigMap stores the custom-configuration by column // number and is generated before rendering columnConfigMap map[int]ColumnConfig // htmlCSSClass stores the HTML CSS Class to use on the
node htmlCSSClass string // indexColumn stores the number of the column considered as the "index" indexColumn int // maxColumnLengths stores the length of the longest line in each column maxColumnLengths []int // maxRowLength stores the length of the longest row maxRowLength int // numColumns stores the (max.) number of columns seen numColumns int // numLinesRendered keeps track of the number of lines rendered and helps in // paginating long tables numLinesRendered int // outputMirror stores an io.Writer where the "Render" functions would write outputMirror io.Writer // pageSize stores the maximum lines to render before rendering the header // again (to denote a page break) - useful when you are dealing with really // long tables pageSize int // rows stores the rows that make up the body (in string form) rows []rowStr // rowsColors stores the text.Colors over-rides for each row as defined by // rowPainter rowsColors []text.Colors // rowsConfigs stores RowConfig for each row rowsConfigMap map[int]RowConfig // rowsRaw stores the rows that make up the body rowsRaw []Row // rowsFooter stores the rows that make up the footer (in string form) rowsFooter []rowStr // rowsFooterConfigs stores RowConfig for each footer row rowsFooterConfigMap map[int]RowConfig // rowsFooterRaw stores the rows that make up the footer rowsFooterRaw []Row // rowsHeader stores the rows that make up the header (in string form) rowsHeader []rowStr // rowsHeaderConfigs stores RowConfig for each header row rowsHeaderConfigMap map[int]RowConfig // rowsHeaderRaw stores the rows that make up the header rowsHeaderRaw []Row // rowPainter is a custom function that given a Row, returns the colors to // use on the entire row rowPainter RowPainter // rowSeparator is a dummy row that contains the separator columns (dashes // that make up the separator between header/body/footer rowSeparator rowStr // separators is used to keep track of all rowIndices after which a // separator has to be rendered separators map[int]bool // sortBy stores a map of Column sortBy []SortBy // style contains all the strings used to draw the table, and more style *Style // suppressEmptyColumns hides columns which have no content on all regular // rows suppressEmptyColumns bool // title contains the text to appear above the table title string } // AppendFooter appends the row to the List of footers to render. // // Only the first item in the "config" will be tagged against this row. func (t *Table) AppendFooter(row Row, config ...RowConfig) { t.rowsFooterRaw = append(t.rowsFooterRaw, row) if len(config) > 0 { if t.rowsFooterConfigMap == nil { t.rowsFooterConfigMap = make(map[int]RowConfig) } t.rowsFooterConfigMap[len(t.rowsFooterRaw)-1] = config[0] } } // AppendHeader appends the row to the List of headers to render. // // Only the first item in the "config" will be tagged against this row. func (t *Table) AppendHeader(row Row, config ...RowConfig) { t.rowsHeaderRaw = append(t.rowsHeaderRaw, row) if len(config) > 0 { if t.rowsHeaderConfigMap == nil { t.rowsHeaderConfigMap = make(map[int]RowConfig) } t.rowsHeaderConfigMap[len(t.rowsHeaderRaw)-1] = config[0] } } // AppendRow appends the row to the List of rows to render. // // Only the first item in the "config" will be tagged against this row. func (t *Table) AppendRow(row Row, config ...RowConfig) { t.rowsRaw = append(t.rowsRaw, row) if len(config) > 0 { if t.rowsConfigMap == nil { t.rowsConfigMap = make(map[int]RowConfig) } t.rowsConfigMap[len(t.rowsRaw)-1] = config[0] } } // AppendRows appends the rows to the List of rows to render. // // Only the first item in the "config" will be tagged against all the rows. func (t *Table) AppendRows(rows []Row, config ...RowConfig) { for _, row := range rows { t.AppendRow(row, config...) } } // AppendSeparator helps render a separator row after the current last row. You // could call this function over and over, but it will be a no-op unless you // call AppendRow or AppendRows in between. Likewise, if the last thing you // append is a separator, it will not be rendered in addition to the usual table // separator. // // ****************************************************************************** // Please note the following caveats: // 1. SetPageSize(): this may end up creating consecutive separator rows near // the end of a page or at the beginning of a page // 2. SortBy(): since SortBy could inherently alter the ordering of rows, the // separators may not appear after the row it was originally intended to // follow // // ****************************************************************************** func (t *Table) AppendSeparator() { if t.separators == nil { t.separators = make(map[int]bool) } if len(t.rowsRaw) > 0 { t.separators[len(t.rowsRaw)-1] = true } } // Length returns the number of rows to be rendered. func (t *Table) Length() int { return len(t.rowsRaw) } // ResetFooters resets and clears all the Footer rows appended earlier. func (t *Table) ResetFooters() { t.rowsFooterRaw = nil } // ResetHeaders resets and clears all the Header rows appended earlier. func (t *Table) ResetHeaders() { t.rowsHeaderRaw = nil } // ResetRows resets and clears all the rows appended earlier. func (t *Table) ResetRows() { t.rowsRaw = nil t.separators = nil } // SetAllowedRowLength sets the maximum allowed length or a row (or line of // output) when rendered as a table. Rows that are longer than this limit will // be "snipped" to the length. Length has to be a positive value to take effect. func (t *Table) SetAllowedRowLength(length int) { t.allowedRowLength = length } // SetAutoIndex adds a generated header with columns such as "A", "B", "C", etc. // and a leading column with the row number similar to what you'd see on any // spreadsheet application. NOTE: Appending a Header will void this // functionality. func (t *Table) SetAutoIndex(autoIndex bool) { t.autoIndex = autoIndex } // SetCaption sets the text to be rendered just below the table. This will not // show up when the Table is rendered as a CSV. func (t *Table) SetCaption(format string, a ...interface{}) { t.caption = fmt.Sprintf(format, a...) } // SetColumnConfigs sets the configs for each Column. func (t *Table) SetColumnConfigs(configs []ColumnConfig) { t.columnConfigs = configs } // SetHTMLCSSClass sets the HTML CSS Class to use on the
node // when rendering the Table in HTML format. // // Deprecated: in favor of Style().HTML.CSSClass func (t *Table) SetHTMLCSSClass(cssClass string) { t.htmlCSSClass = cssClass } // SetIndexColumn sets the given Column # as the column that has the row // "Number". Valid values range from 1 to N. Note that this is not 0-indexed. func (t *Table) SetIndexColumn(colNum int) { t.indexColumn = colNum } // SetOutputMirror sets an io.Writer for all the Render functions to "Write" to // in addition to returning a string. func (t *Table) SetOutputMirror(mirror io.Writer) { t.outputMirror = mirror } // SetPageSize sets the maximum number of lines to render before rendering the // header rows again. This can be useful when dealing with tables containing a // long list of rows that can span pages. Please note that the pagination logic // will not consider Header/Footer lines for paging. func (t *Table) SetPageSize(numLines int) { t.pageSize = numLines } // SetRowPainter sets the RowPainter function which determines the colors to use // on a row. Before rendering, this function is invoked on all rows and the // color of each row is determined. This color takes precedence over other ways // to set color (ColumnConfig.Color*, SetColor*()). func (t *Table) SetRowPainter(painter RowPainter) { t.rowPainter = painter } // SetStyle overrides the DefaultStyle with the provided one. func (t *Table) SetStyle(style Style) { t.style = &style } // SetTitle sets the title text to be rendered above the table. func (t *Table) SetTitle(format string, a ...interface{}) { t.title = fmt.Sprintf(format, a...) } // SortBy sets the rules for sorting the Rows in the order specified. i.e., the // first SortBy instruction takes precedence over the second and so on. Any // duplicate instructions on the same column will be discarded while sorting. func (t *Table) SortBy(sortBy []SortBy) { t.sortBy = sortBy } // Style returns the current style. func (t *Table) Style() *Style { if t.style == nil { tempStyle := StyleDefault t.style = &tempStyle } return t.style } // SuppressEmptyColumns hides columns when the column is empty in ALL the // regular rows. func (t *Table) SuppressEmptyColumns() { t.suppressEmptyColumns = true } func (t *Table) getAlign(colIdx int, hint renderHint) text.Align { align := text.AlignDefault if cfg, ok := t.columnConfigMap[colIdx]; ok { if hint.isHeaderRow { align = cfg.AlignHeader } else if hint.isFooterRow { align = cfg.AlignFooter } else { align = cfg.Align } } if align == text.AlignDefault { if !t.columnIsNonNumeric[colIdx] { align = text.AlignRight } else if hint.isAutoIndexRow { align = text.AlignCenter } } return align } func (t *Table) getAutoIndexColumnIDs() rowStr { row := make(rowStr, t.numColumns) for colIdx := range row { row[colIdx] = AutoIndexColumnID(colIdx) } return row } func (t *Table) getBorderColors(hint renderHint) text.Colors { if t.style.Options.DoNotColorBordersAndSeparators { return nil } else if t.style.Color.Border != nil { return t.style.Color.Border } else if hint.isTitleRow { return t.style.Title.Colors } else if hint.isHeaderRow { return t.style.Color.Header } else if hint.isFooterRow { return t.style.Color.Footer } else if t.autoIndex { return t.style.Color.IndexColumn } else if hint.rowNumber%2 == 0 && t.style.Color.RowAlternate != nil { return t.style.Color.RowAlternate } return t.style.Color.Row } func (t *Table) getBorderLeft(hint renderHint) string { border := t.style.Box.Left if hint.isBorderTop { if t.title != "" { border = t.style.Box.LeftSeparator } else { border = t.style.Box.TopLeft } } else if hint.isBorderBottom { border = t.style.Box.BottomLeft } else if hint.isSeparatorRow { if t.autoIndex && hint.isHeaderOrFooterSeparator() { border = t.style.Box.Left } else if !t.autoIndex && t.shouldMergeCellsVertically(0, hint) { border = t.style.Box.Left } else { border = t.style.Box.LeftSeparator } } return border } func (t *Table) getBorderRight(hint renderHint) string { border := t.style.Box.Right if hint.isBorderTop { if t.title != "" { border = t.style.Box.RightSeparator } else { border = t.style.Box.TopRight } } else if hint.isBorderBottom { border = t.style.Box.BottomRight } else if hint.isSeparatorRow { if t.shouldMergeCellsVertically(t.numColumns-1, hint) { border = t.style.Box.Right } else { border = t.style.Box.RightSeparator } } return border } func (t *Table) getColumnColors(colIdx int, hint renderHint) text.Colors { if hint.isBorderOrSeparator() { if colors := t.getColumnColorsForBorderOrSeparator(hint); colors != nil { return colors } } if t.rowPainter != nil && hint.isRegularNonSeparatorRow() && !t.isIndexColumn(colIdx, hint) { if colors := t.rowsColors[hint.rowNumber-1]; colors != nil { return colors } } if cfg, ok := t.columnConfigMap[colIdx]; ok { if hint.isSeparatorRow { return nil } else if hint.isHeaderRow { return cfg.ColorsHeader } else if hint.isFooterRow { return cfg.ColorsFooter } return cfg.Colors } return nil } func (t *Table) getColumnColorsForBorderOrSeparator(hint renderHint) text.Colors { if t.style.Options.DoNotColorBordersAndSeparators { return text.Colors{} // not nil to force caller to paint with no colors } if (hint.isBorderBottom || hint.isBorderTop) && t.style.Color.Border != nil { return t.style.Color.Border } if hint.isSeparatorRow && t.style.Color.Separator != nil { return t.style.Color.Separator } return nil } func (t *Table) getColumnSeparator(row rowStr, colIdx int, hint renderHint) string { separator := t.style.Box.MiddleVertical if hint.isSeparatorRow { if hint.isBorderTop { if t.shouldMergeCellsHorizontallyBelow(row, colIdx, hint) { separator = t.style.Box.MiddleHorizontal } else { separator = t.style.Box.TopSeparator } } else if hint.isBorderBottom { if t.shouldMergeCellsHorizontallyAbove(row, colIdx, hint) { separator = t.style.Box.MiddleHorizontal } else { separator = t.style.Box.BottomSeparator } } else { sm1 := t.shouldMergeCellsHorizontallyAbove(row, colIdx, hint) sm2 := t.shouldMergeCellsHorizontallyBelow(row, colIdx, hint) separator = t.getColumnSeparatorNonBorder(sm1, sm2, colIdx, hint) } } return separator } func (t *Table) getColumnSeparatorNonBorder(mergeCellsAbove bool, mergeCellsBelow bool, colIdx int, hint renderHint) string { mergeNextCol := t.shouldMergeCellsVertically(colIdx, hint) if hint.isAutoIndexColumn { return t.getColumnSeparatorNonBorderAutoIndex(mergeNextCol, hint) } mergeCurrCol := t.shouldMergeCellsVertically(colIdx-1, hint) return t.getColumnSeparatorNonBorderNonAutoIndex(mergeCellsAbove, mergeCellsBelow, mergeCurrCol, mergeNextCol) } func (t *Table) getColumnSeparatorNonBorderAutoIndex(mergeNextCol bool, hint renderHint) string { if hint.isHeaderOrFooterSeparator() { if mergeNextCol { return t.style.Box.MiddleVertical } return t.style.Box.LeftSeparator } else if mergeNextCol { return t.style.Box.RightSeparator } return t.style.Box.MiddleSeparator } func (t *Table) getColumnSeparatorNonBorderNonAutoIndex(mergeCellsAbove bool, mergeCellsBelow bool, mergeCurrCol bool, mergeNextCol bool) string { if mergeCellsAbove && mergeCellsBelow && mergeCurrCol && mergeNextCol { return t.style.Box.EmptySeparator } else if mergeCellsAbove && mergeCellsBelow { return t.style.Box.MiddleHorizontal } else if mergeCellsAbove { return t.style.Box.TopSeparator } else if mergeCellsBelow { return t.style.Box.BottomSeparator } else if mergeCurrCol && mergeNextCol { return t.style.Box.MiddleVertical } else if mergeCurrCol { return t.style.Box.LeftSeparator } else if mergeNextCol { return t.style.Box.RightSeparator } return t.style.Box.MiddleSeparator } func (t *Table) getColumnTransformer(colIdx int, hint renderHint) text.Transformer { var transformer text.Transformer if cfg, ok := t.columnConfigMap[colIdx]; ok { if hint.isHeaderRow { transformer = cfg.TransformerHeader } else if hint.isFooterRow { transformer = cfg.TransformerFooter } else { transformer = cfg.Transformer } } return transformer } func (t *Table) getColumnWidthMax(colIdx int) int { if cfg, ok := t.columnConfigMap[colIdx]; ok { return cfg.WidthMax } return 0 } func (t *Table) getColumnWidthMin(colIdx int) int { if cfg, ok := t.columnConfigMap[colIdx]; ok { return cfg.WidthMin } return 0 } func (t *Table) getFormat(hint renderHint) text.Format { if hint.isSeparatorRow { return text.FormatDefault } else if hint.isHeaderRow { return t.style.Format.Header } else if hint.isFooterRow { return t.style.Format.Footer } return t.style.Format.Row } func (t *Table) getMaxColumnLengthForMerging(colIdx int) int { maxColumnLength := t.maxColumnLengths[colIdx] maxColumnLength += text.RuneWidthWithoutEscSequences(t.style.Box.PaddingRight + t.style.Box.PaddingLeft) if t.style.Options.SeparateColumns { maxColumnLength += text.RuneWidthWithoutEscSequences(t.style.Box.EmptySeparator) } return maxColumnLength } // getMergedColumnIndices returns a map of colIdx values to all the other colIdx // values (that are being merged) and their lengths. func (t *Table) getMergedColumnIndices(row rowStr, hint renderHint) mergedColumnIndices { if !t.getRowConfig(hint).AutoMerge { return nil } mci := make(mergedColumnIndices) for colIdx := 0; colIdx < t.numColumns-1; colIdx++ { // look backward for otherColIdx := colIdx - 1; colIdx >= 0 && otherColIdx >= 0; otherColIdx-- { if row[colIdx] != row[otherColIdx] { break } mci.safeAppend(colIdx, otherColIdx) } // look forward for otherColIdx := colIdx + 1; colIdx < len(row) && otherColIdx < len(row); otherColIdx++ { if row[colIdx] != row[otherColIdx] { break } mci.safeAppend(colIdx, otherColIdx) } } return mci } func (t *Table) getRow(rowIdx int, hint renderHint) rowStr { switch { case hint.isHeaderRow: if rowIdx >= 0 && rowIdx < len(t.rowsHeader) { return t.rowsHeader[rowIdx] } case hint.isFooterRow: if rowIdx >= 0 && rowIdx < len(t.rowsFooter) { return t.rowsFooter[rowIdx] } default: if rowIdx >= 0 && rowIdx < len(t.rows) { return t.rows[rowIdx] } } return rowStr{} } func (t *Table) getRowConfig(hint renderHint) RowConfig { rowIdx := hint.rowNumber - 1 if rowIdx < 0 { rowIdx = 0 } switch { case hint.isHeaderRow: return t.rowsHeaderConfigMap[rowIdx] case hint.isFooterRow: return t.rowsFooterConfigMap[rowIdx] default: return t.rowsConfigMap[rowIdx] } } func (t *Table) getSeparatorColors(hint renderHint) text.Colors { if t.style.Options.DoNotColorBordersAndSeparators { return nil } else if (hint.isBorderBottom || hint.isBorderTop) && t.style.Color.Border != nil { return t.style.Color.Border } else if t.style.Color.Separator != nil { return t.style.Color.Separator } else if hint.isHeaderRow { return t.style.Color.Header } else if hint.isFooterRow { return t.style.Color.Footer } else if hint.isAutoIndexColumn { return t.style.Color.IndexColumn } else if hint.rowNumber > 0 && hint.rowNumber%2 == 0 { return t.style.Color.RowAlternate } return t.style.Color.Row } func (t *Table) getVAlign(colIdx int, hint renderHint) text.VAlign { vAlign := text.VAlignDefault if cfg, ok := t.columnConfigMap[colIdx]; ok { if hint.isHeaderRow { vAlign = cfg.VAlignHeader } else if hint.isFooterRow { vAlign = cfg.VAlignFooter } else { vAlign = cfg.VAlign } } return vAlign } func (t *Table) hasHiddenColumns() bool { for _, cc := range t.columnConfigMap { if cc.Hidden { return true } } return false } func (t *Table) hideColumns() map[int]int { colIdxMap := make(map[int]int) numColumns := 0 hideColumnsInRows := func(rows []rowStr) []rowStr { var rsp []rowStr for _, row := range rows { var rowNew rowStr for colIdx, col := range row { cc := t.columnConfigMap[colIdx] if !cc.Hidden { rowNew = append(rowNew, col) colIdxMap[colIdx] = len(rowNew) - 1 } } if len(rowNew) > numColumns { numColumns = len(rowNew) } rsp = append(rsp, rowNew) } return rsp } // hide columns as directed t.rows = hideColumnsInRows(t.rows) t.rowsFooter = hideColumnsInRows(t.rowsFooter) t.rowsHeader = hideColumnsInRows(t.rowsHeader) // reset numColumns to the new number of columns t.numColumns = numColumns return colIdxMap } func (t *Table) isIndexColumn(colIdx int, hint renderHint) bool { return t.indexColumn == colIdx+1 || hint.isAutoIndexColumn } func (t *Table) render(out *strings.Builder) string { outStr := out.String() if t.outputMirror != nil && len(outStr) > 0 { _, _ = t.outputMirror.Write([]byte(outStr)) _, _ = t.outputMirror.Write([]byte("\n")) } return outStr } func (t *Table) shouldMergeCellsHorizontallyAbove(row rowStr, colIdx int, hint renderHint) bool { if hint.isAutoIndexColumn || hint.isAutoIndexRow { return false } rowConfig := t.getRowConfig(hint) if hint.isSeparatorRow { if hint.isHeaderRow && hint.rowNumber == 1 { rowConfig = t.getRowConfig(hint) row = t.getRow(hint.rowNumber-1, hint) } else if hint.isFooterRow && hint.isFirstRow { rowConfig = t.getRowConfig(renderHint{isLastRow: true, rowNumber: len(t.rows)}) row = t.getRow(len(t.rows)-1, renderHint{}) } else if hint.isFooterRow && hint.isBorderBottom { row = t.getRow(len(t.rowsFooter)-1, renderHint{isFooterRow: true}) } else { row = t.getRow(hint.rowNumber-1, hint) } } if rowConfig.AutoMerge { return row.areEqual(colIdx-1, colIdx) } return false } func (t *Table) shouldMergeCellsHorizontallyBelow(row rowStr, colIdx int, hint renderHint) bool { if hint.isAutoIndexColumn || hint.isAutoIndexRow { return false } var rowConfig RowConfig if hint.isSeparatorRow { if hint.isRegularRow() { rowConfig = t.getRowConfig(renderHint{rowNumber: hint.rowNumber + 1}) row = t.getRow(hint.rowNumber, renderHint{}) } else if hint.isHeaderRow && hint.rowNumber == 0 { rowConfig = t.getRowConfig(renderHint{isHeaderRow: true, rowNumber: 1}) row = t.getRow(0, hint) } else if hint.isHeaderRow && hint.isLastRow { rowConfig = t.getRowConfig(renderHint{rowNumber: 1}) row = t.getRow(0, renderHint{}) } else if hint.isHeaderRow { rowConfig = t.getRowConfig(renderHint{isHeaderRow: true, rowNumber: hint.rowNumber + 1}) row = t.getRow(hint.rowNumber, hint) } else if hint.isFooterRow && hint.rowNumber >= 0 { rowConfig = t.getRowConfig(renderHint{isFooterRow: true, rowNumber: 1}) row = t.getRow(hint.rowNumber, renderHint{isFooterRow: true}) } } if rowConfig.AutoMerge { return row.areEqual(colIdx-1, colIdx) } return false } func (t *Table) shouldMergeCellsVertically(colIdx int, hint renderHint) bool { if t.columnConfigMap[colIdx].AutoMerge && colIdx < t.numColumns { if hint.isSeparatorRow { rowPrev := t.getRow(hint.rowNumber-1, hint) rowNext := t.getRow(hint.rowNumber, hint) if colIdx < len(rowPrev) && colIdx < len(rowNext) { return rowPrev[colIdx] == rowNext[colIdx] } } else { rowPrev := t.getRow(hint.rowNumber-2, hint) rowCurr := t.getRow(hint.rowNumber-1, hint) if colIdx < len(rowPrev) && colIdx < len(rowCurr) { return rowPrev[colIdx] == rowCurr[colIdx] } } } return false } func (t *Table) wrapRow(row rowStr) (int, rowStr) { colMaxLines := 0 rowWrapped := make(rowStr, len(row)) for colIdx, colStr := range row { widthEnforcer := t.columnConfigMap[colIdx].getWidthMaxEnforcer() maxWidth := t.getColumnWidthMax(colIdx) if maxWidth == 0 { maxWidth = t.maxColumnLengths[colIdx] } rowWrapped[colIdx] = widthEnforcer(colStr, maxWidth) colNumLines := strings.Count(rowWrapped[colIdx], "\n") + 1 if colNumLines > colMaxLines { colMaxLines = colNumLines } } return colMaxLines, rowWrapped } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/table/util.go ================================================ package table import ( "reflect" ) // AutoIndexColumnID returns a unique Column ID/Name for the given Column Number. // The functionality is similar to what you get in an Excel spreadsheet w.r.t. // the Column ID/Name. func AutoIndexColumnID(colIdx int) string { charIdx := colIdx % 26 out := string(rune(65 + charIdx)) colIdx = colIdx / 26 if colIdx > 0 { return AutoIndexColumnID(colIdx-1) + out } return out } // WidthEnforcer is a function that helps enforce a width condition on a string. type WidthEnforcer func(col string, maxLen int) string // widthEnforcerNone returns the input string as is without any modifications. func widthEnforcerNone(col string, _ int) string { return col } // isNumber returns true if the argument is a numeric type; false otherwise. func isNumber(x interface{}) bool { if x == nil { return false } switch reflect.TypeOf(x).Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64: return true } return false } type mergedColumnIndices map[int]map[int]bool func (m mergedColumnIndices) mergedLength(colIdx int, maxColumnLengths []int) int { mergedLength := maxColumnLengths[colIdx] for otherColIdx := range m[colIdx] { mergedLength += maxColumnLengths[otherColIdx] } return mergedLength } func (m mergedColumnIndices) len(colIdx int) int { return len(m[colIdx]) + 1 } func (m mergedColumnIndices) safeAppend(colIdx, otherColIdx int) { // map if m[colIdx] == nil { m[colIdx] = make(map[int]bool) } m[colIdx][otherColIdx] = true // reverse map if m[otherColIdx] == nil { m[otherColIdx] = make(map[int]bool) } m[otherColIdx][colIdx] = true } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/table/writer.go ================================================ package table import ( "io" ) // Writer declares the interfaces that can be used to set up and render a table. type Writer interface { AppendFooter(row Row, configs ...RowConfig) AppendHeader(row Row, configs ...RowConfig) AppendRow(row Row, configs ...RowConfig) AppendRows(rows []Row, configs ...RowConfig) AppendSeparator() Length() int Render() string RenderCSV() string RenderHTML() string RenderMarkdown() string RenderTSV() string ResetFooters() ResetHeaders() ResetRows() SetAllowedRowLength(length int) SetAutoIndex(autoIndex bool) SetCaption(format string, a ...interface{}) SetColumnConfigs(configs []ColumnConfig) SetIndexColumn(colNum int) SetOutputMirror(mirror io.Writer) SetPageSize(numLines int) SetRowPainter(painter RowPainter) SetStyle(style Style) SetTitle(format string, a ...interface{}) SortBy(sortBy []SortBy) Style() *Style SuppressEmptyColumns() // deprecated; in favor of Style().HTML.CSSClass SetHTMLCSSClass(cssClass string) } // NewWriter initializes and returns a Writer. func NewWriter() Writer { return &Table{} } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/text/README.md ================================================ # text [![Go Reference](https://pkg.go.dev/badge/github.com/jedib0t/go-pretty/v6.svg)](https://pkg.go.dev/github.com/jedib0t/go-pretty/v6/text) Package with utility functions to manipulate strings/text. Used heavily in the other packages in this repo ([list](../list), [progress](../progress), and [table](../table)). ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/text/align.go ================================================ package text import ( "fmt" "strconv" "strings" "unicode/utf8" ) // Align denotes how text is to be aligned horizontally. type Align int // Align enumerations const ( AlignDefault Align = iota // same as AlignLeft AlignLeft // "left " AlignCenter // " center " AlignJustify // "justify it" AlignRight // " right" ) // Apply aligns the text as directed. For ex.: // - AlignDefault.Apply("Jon Snow", 12) returns "Jon Snow " // - AlignLeft.Apply("Jon Snow", 12) returns "Jon Snow " // - AlignCenter.Apply("Jon Snow", 12) returns " Jon Snow " // - AlignJustify.Apply("Jon Snow", 12) returns "Jon Snow" // - AlignRight.Apply("Jon Snow", 12) returns " Jon Snow" func (a Align) Apply(text string, maxLength int) string { text = a.trimString(text) sLen := utf8.RuneCountInString(text) sLenWoE := RuneWidthWithoutEscSequences(text) numEscChars := sLen - sLenWoE // now, align the text switch a { case AlignDefault, AlignLeft: return fmt.Sprintf("%-"+strconv.Itoa(maxLength+numEscChars)+"s", text) case AlignCenter: if sLenWoE < maxLength { // left pad with half the number of spaces needed before using %text return fmt.Sprintf("%"+strconv.Itoa(maxLength+numEscChars)+"s", text+strings.Repeat(" ", (maxLength-sLenWoE)/2)) } case AlignJustify: return a.justifyText(text, sLenWoE, maxLength) } return fmt.Sprintf("%"+strconv.Itoa(maxLength+numEscChars)+"s", text) } // HTMLProperty returns the equivalent HTML horizontal-align tag property. func (a Align) HTMLProperty() string { switch a { case AlignLeft: return "align=\"left\"" case AlignCenter: return "align=\"center\"" case AlignJustify: return "align=\"justify\"" case AlignRight: return "align=\"right\"" default: return "" } } // MarkdownProperty returns the equivalent Markdown horizontal-align separator. func (a Align) MarkdownProperty() string { switch a { case AlignLeft: return ":--- " case AlignCenter: return ":---:" case AlignRight: return " ---:" default: return " --- " } } func (a Align) justifyText(text string, textLength int, maxLength int) string { // split the text into individual words wordsUnfiltered := strings.Split(text, " ") words := Filter(wordsUnfiltered, func(item string) bool { return item != "" }) // empty string implies spaces for maxLength if len(words) == 0 { return strings.Repeat(" ", maxLength) } // get the number of spaces to insert into the text numSpacesNeeded := maxLength - textLength + strings.Count(text, " ") numSpacesNeededBetweenWords := 0 if len(words) > 1 { numSpacesNeededBetweenWords = numSpacesNeeded / (len(words) - 1) } // create the output string word by word with spaces in between var outText strings.Builder outText.Grow(maxLength) for idx, word := range words { if idx > 0 { // insert spaces only after the first word if idx == len(words)-1 { // insert all the remaining space before the last word outText.WriteString(strings.Repeat(" ", numSpacesNeeded)) numSpacesNeeded = 0 } else { // insert the determined number of spaces between each word outText.WriteString(strings.Repeat(" ", numSpacesNeededBetweenWords)) // and reduce the number of spaces needed after this numSpacesNeeded -= numSpacesNeededBetweenWords } } outText.WriteString(word) if idx == len(words)-1 && numSpacesNeeded > 0 { outText.WriteString(strings.Repeat(" ", numSpacesNeeded)) } } return outText.String() } func (a Align) trimString(text string) string { switch a { case AlignDefault, AlignLeft: if strings.HasSuffix(text, " ") { return strings.TrimRight(text, " ") } case AlignRight: if strings.HasPrefix(text, " ") { return strings.TrimLeft(text, " ") } default: if strings.HasPrefix(text, " ") || strings.HasSuffix(text, " ") { return strings.Trim(text, " ") } } return text } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/text/ansi.go ================================================ package text import "strings" // ANSICodesSupported will be true on consoles where ANSI Escape Codes/Sequences // are supported. var ANSICodesSupported = areANSICodesSupported() // Escape encodes the string with the ANSI Escape Sequence. // For ex.: // // Escape("Ghost", "") == "Ghost" // Escape("Ghost", "\x1b[91m") == "\x1b[91mGhost\x1b[0m" // Escape("\x1b[94mGhost\x1b[0mLady", "\x1b[91m") == "\x1b[94mGhost\x1b[0m\x1b[91mLady\x1b[0m" // Escape("Nymeria\x1b[94mGhost\x1b[0mLady", "\x1b[91m") == "\x1b[91mNymeria\x1b[94mGhost\x1b[0m\x1b[91mLady\x1b[0m" // Escape("Nymeria \x1b[94mGhost\x1b[0m Lady", "\x1b[91m") == "\x1b[91mNymeria \x1b[94mGhost\x1b[0m\x1b[91m Lady\x1b[0m" func Escape(str string, escapeSeq string) string { out := "" if !strings.HasPrefix(str, EscapeStart) { out += escapeSeq } out += strings.Replace(str, EscapeReset, EscapeReset+escapeSeq, -1) if !strings.HasSuffix(out, EscapeReset) { out += EscapeReset } if strings.Contains(out, escapeSeq+EscapeReset) { out = strings.Replace(out, escapeSeq+EscapeReset, "", -1) } return out } // StripEscape strips all ANSI Escape Sequence from the string. // For ex.: // // StripEscape("Ghost") == "Ghost" // StripEscape("\x1b[91mGhost\x1b[0m") == "Ghost" // StripEscape("\x1b[94mGhost\x1b[0m\x1b[91mLady\x1b[0m") == "GhostLady" // StripEscape("\x1b[91mNymeria\x1b[94mGhost\x1b[0m\x1b[91mLady\x1b[0m") == "NymeriaGhostLady" // StripEscape("\x1b[91mNymeria \x1b[94mGhost\x1b[0m\x1b[91m Lady\x1b[0m") == "Nymeria Ghost Lady" func StripEscape(str string) string { var out strings.Builder out.Grow(RuneWidthWithoutEscSequences(str)) isEscSeq := false for _, sChr := range str { if sChr == EscapeStartRune { isEscSeq = true } if !isEscSeq { out.WriteRune(sChr) } if isEscSeq && sChr == EscapeStopRune { isEscSeq = false } } return out.String() } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/text/ansi_unix.go ================================================ //go:build !windows // +build !windows package text func areANSICodesSupported() bool { return true } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/text/ansi_windows.go ================================================ //go:build windows // +build windows package text import ( "os" "sync" "golang.org/x/sys/windows" ) var enableVTPMutex = sync.Mutex{} func areANSICodesSupported() bool { enableVTPMutex.Lock() defer enableVTPMutex.Unlock() outHandle := windows.Handle(os.Stdout.Fd()) var outMode uint32 if err := windows.GetConsoleMode(outHandle, &outMode); err == nil { if outMode&windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0 { return true } if err := windows.SetConsoleMode(outHandle, outMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil { return true } } return false } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/text/color.go ================================================ package text import ( "fmt" "sort" "strconv" "strings" "sync" ) var colorsEnabled = areANSICodesSupported() // DisableColors (forcefully) disables color coding globally. func DisableColors() { colorsEnabled = false } // EnableColors (forcefully) enables color coding globally. func EnableColors() { colorsEnabled = true } // The logic here is inspired from github.com/fatih/color; the following is // the bare minimum logic required to print Colored to the console. // The differences: // * This one caches the escape sequences for cases with multiple colors // * This one handles cases where the incoming already has colors in the // form of escape sequences; in which case, text that does not have any // escape sequences are colored/escaped // Color represents a single color to render with. type Color int // Base colors -- attributes in reality const ( Reset Color = iota Bold Faint Italic Underline BlinkSlow BlinkRapid ReverseVideo Concealed CrossedOut ) // Foreground colors const ( FgBlack Color = iota + 30 FgRed FgGreen FgYellow FgBlue FgMagenta FgCyan FgWhite ) // Foreground Hi-Intensity colors const ( FgHiBlack Color = iota + 90 FgHiRed FgHiGreen FgHiYellow FgHiBlue FgHiMagenta FgHiCyan FgHiWhite ) // Background colors const ( BgBlack Color = iota + 40 BgRed BgGreen BgYellow BgBlue BgMagenta BgCyan BgWhite ) // Background Hi-Intensity colors const ( BgHiBlack Color = iota + 100 BgHiRed BgHiGreen BgHiYellow BgHiBlue BgHiMagenta BgHiCyan BgHiWhite ) // EscapeSeq returns the ANSI escape sequence for the color. func (c Color) EscapeSeq() string { return EscapeStart + strconv.Itoa(int(c)) + EscapeStop } // HTMLProperty returns the "class" attribute for the color. func (c Color) HTMLProperty() string { out := "" if class, ok := colorCSSClassMap[c]; ok { out = fmt.Sprintf("class=\"%s\"", class) } return out } // Sprint colorizes and prints the given string(s). func (c Color) Sprint(a ...interface{}) string { return colorize(fmt.Sprint(a...), c.EscapeSeq()) } // Sprintf formats and colorizes and prints the given string(s). func (c Color) Sprintf(format string, a ...interface{}) string { return colorize(fmt.Sprintf(format, a...), c.EscapeSeq()) } // Colors represents an array of Color objects to render with. // Example: Colors{FgCyan, BgBlack} type Colors []Color // colorsSeqMap caches the escape sequence for a set of colors var colorsSeqMap = sync.Map{} // EscapeSeq returns the ANSI escape sequence for the colors set. func (c Colors) EscapeSeq() string { if len(c) == 0 { return "" } colorsKey := fmt.Sprintf("%#v", c) escapeSeq, ok := colorsSeqMap.Load(colorsKey) if !ok || escapeSeq == "" { colorNums := make([]string, len(c)) for idx, color := range c { colorNums[idx] = strconv.Itoa(int(color)) } escapeSeq = EscapeStart + strings.Join(colorNums, ";") + EscapeStop colorsSeqMap.Store(colorsKey, escapeSeq) } return escapeSeq.(string) } // HTMLProperty returns the "class" attribute for the colors. func (c Colors) HTMLProperty() string { if len(c) == 0 { return "" } var classes []string for _, color := range c { if class, ok := colorCSSClassMap[color]; ok { classes = append(classes, class) } } if len(classes) > 1 { sort.Strings(classes) } return fmt.Sprintf("class=\"%s\"", strings.Join(classes, " ")) } // Sprint colorizes and prints the given string(s). func (c Colors) Sprint(a ...interface{}) string { return colorize(fmt.Sprint(a...), c.EscapeSeq()) } // Sprintf formats and colorizes and prints the given string(s). func (c Colors) Sprintf(format string, a ...interface{}) string { return colorize(fmt.Sprintf(format, a...), c.EscapeSeq()) } func colorize(s string, escapeSeq string) string { if !colorsEnabled || escapeSeq == "" { return s } return Escape(s, escapeSeq) } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/text/color_html.go ================================================ package text // colorCSSClassMap contains the equivalent CSS-class for all colors var colorCSSClassMap = map[Color]string{ Bold: "bold", Faint: "faint", Italic: "italic", Underline: "underline", BlinkSlow: "blink-slow", BlinkRapid: "blink-rapid", ReverseVideo: "reverse-video", Concealed: "concealed", CrossedOut: "crossed-out", FgBlack: "fg-black", FgRed: "fg-red", FgGreen: "fg-green", FgYellow: "fg-yellow", FgBlue: "fg-blue", FgMagenta: "fg-magenta", FgCyan: "fg-cyan", FgWhite: "fg-white", FgHiBlack: "fg-hi-black", FgHiRed: "fg-hi-red", FgHiGreen: "fg-hi-green", FgHiYellow: "fg-hi-yellow", FgHiBlue: "fg-hi-blue", FgHiMagenta: "fg-hi-magenta", FgHiCyan: "fg-hi-cyan", FgHiWhite: "fg-hi-white", BgBlack: "bg-black", BgRed: "bg-red", BgGreen: "bg-green", BgYellow: "bg-yellow", BgBlue: "bg-blue", BgMagenta: "bg-magenta", BgCyan: "bg-cyan", BgWhite: "bg-white", BgHiBlack: "bg-hi-black", BgHiRed: "bg-hi-red", BgHiGreen: "bg-hi-green", BgHiYellow: "bg-hi-yellow", BgHiBlue: "bg-hi-blue", BgHiMagenta: "bg-hi-magenta", BgHiCyan: "bg-hi-cyan", BgHiWhite: "bg-hi-white", } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/text/cursor.go ================================================ package text import ( "fmt" ) // Cursor helps move the cursor on the console in multiple directions. type Cursor rune const ( // CursorDown helps move the Cursor Down X lines CursorDown Cursor = 'B' // CursorLeft helps move the Cursor Left X characters CursorLeft Cursor = 'D' // CursorRight helps move the Cursor Right X characters CursorRight Cursor = 'C' // CursorUp helps move the Cursor Up X lines CursorUp Cursor = 'A' // EraseLine helps erase all characters to the Right of the Cursor in the // current line EraseLine Cursor = 'K' ) // Sprint prints the Escape Sequence to move the Cursor once. func (c Cursor) Sprint() string { return fmt.Sprintf("%s%c", EscapeStart, c) } // Sprintn prints the Escape Sequence to move the Cursor "n" times. func (c Cursor) Sprintn(n int) string { if c == EraseLine { return c.Sprint() } return fmt.Sprintf("%s%d%c", EscapeStart, n, c) } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/text/direction.go ================================================ package text // Direction defines the overall flow of text. Similar to bidi.Direction, but // simplified and specific to this package. type Direction int // Available Directions. const ( Default Direction = iota LeftToRight RightToLeft ) // Modifier returns a character to force the given direction for the text that // follows the modifier. func (d Direction) Modifier() string { switch d { case LeftToRight: return "\u202a" case RightToLeft: return "\u202b" } return "" } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/text/escape.go ================================================ package text import "strings" // Constants const ( CSIStartRune = rune(91) // [ CSIStopRune = 'm' EscapeReset = EscapeStart + "0" + EscapeStop EscapeStart = "\x1b[" EscapeStartRune = rune(27) // \x1b EscapeStop = "m" EscapeStopRune = 'm' OSIStartRune = rune(93) // ] OSIStopRune = '\\' ) type escKind int const ( escKindUnknown escKind = iota escKindCSI escKindOSI ) type escSeq struct { isIn bool content strings.Builder kind escKind } func (e *escSeq) InspectRune(r rune) { if !e.isIn && r == EscapeStartRune { e.isIn = true e.kind = escKindUnknown e.content.Reset() e.content.WriteRune(r) } else if e.isIn { switch { case e.kind == escKindUnknown && r == CSIStartRune: e.kind = escKindCSI case e.kind == escKindUnknown && r == OSIStartRune: e.kind = escKindOSI case e.kind == escKindCSI && r == CSIStopRune || e.kind == escKindOSI && r == OSIStopRune: e.isIn = false e.kind = escKindUnknown } e.content.WriteRune(r) } } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/text/filter.go ================================================ package text // Filter filters the slice 's' to items which return truth when passed to 'f'. func Filter(s []string, f func(string) bool) []string { var out []string for _, item := range s { if f(item) { out = append(out, item) } } return out } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/text/format.go ================================================ package text import ( "strings" "unicode" ) // Format lets you transform the text in supported methods while keeping escape // sequences in the string intact and untouched. type Format int // Format enumerations const ( FormatDefault Format = iota // default_Case FormatLower // lower FormatTitle // Title FormatUpper // UPPER ) // Apply converts the text as directed. func (tc Format) Apply(text string) string { switch tc { case FormatLower: return strings.ToLower(text) case FormatTitle: return toTitle(text) case FormatUpper: return toUpper(text) default: return text } } func toTitle(text string) string { prev, inEscSeq := ' ', false return strings.Map( func(r rune) rune { if r == EscapeStartRune { inEscSeq = true } if !inEscSeq { if isSeparator(prev) { prev = r r = unicode.ToUpper(r) } else { prev = r } } if inEscSeq && r == EscapeStopRune { inEscSeq = false } return r }, text, ) } func toUpper(text string) string { inEscSeq := false return strings.Map( func(r rune) rune { if r == EscapeStartRune { inEscSeq = true } if !inEscSeq { r = unicode.ToUpper(r) } if inEscSeq && r == EscapeStopRune { inEscSeq = false } return r }, text, ) } // isSeparator returns true if the given rune is a separator. This function is // lifted straight out of the standard library @ strings/strings.go. func isSeparator(r rune) bool { // ASCII alphanumerics and underscore are not separators if r <= 0x7F { switch { case '0' <= r && r <= '9': return false case 'a' <= r && r <= 'z': return false case 'A' <= r && r <= 'Z': return false case r == '_': return false } return true } // Letters and digits are not separators if unicode.IsLetter(r) || unicode.IsDigit(r) { return false } // Otherwise, all we can do for now is treat spaces as separators. return unicode.IsSpace(r) } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/text/hyperlink.go ================================================ package text import "fmt" func Hyperlink(url, text string) string { if url == "" { return text } if text == "" { return url } // source https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda return fmt.Sprintf("\x1b]8;;%s\x1b\\%s\x1b]8;;\x1b\\", url, text) } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/text/string.go ================================================ package text import ( "strings" "unicode/utf8" "github.com/mattn/go-runewidth" ) // RuneWidth stuff var ( rwCondition = runewidth.NewCondition() ) // InsertEveryN inserts the rune every N characters in the string. For ex.: // // InsertEveryN("Ghost", '-', 1) == "G-h-o-s-t" // InsertEveryN("Ghost", '-', 2) == "Gh-os-t" // InsertEveryN("Ghost", '-', 3) == "Gho-st" // InsertEveryN("Ghost", '-', 4) == "Ghos-t" // InsertEveryN("Ghost", '-', 5) == "Ghost" func InsertEveryN(str string, runeToInsert rune, n int) string { if n <= 0 { return str } sLen := RuneWidthWithoutEscSequences(str) var out strings.Builder out.Grow(sLen + (sLen / n)) outLen, eSeq := 0, escSeq{} for idx, c := range str { if eSeq.isIn { eSeq.InspectRune(c) out.WriteRune(c) continue } eSeq.InspectRune(c) if !eSeq.isIn && outLen > 0 && (outLen%n) == 0 && idx != sLen { out.WriteRune(runeToInsert) } out.WriteRune(c) if !eSeq.isIn { outLen += RuneWidth(c) } } return out.String() } // LongestLineLen returns the length of the longest "line" within the // argument string. For ex.: // // LongestLineLen("Ghost!\nCome back here!\nRight now!") == 15 func LongestLineLen(str string) int { maxLength, currLength, eSeq := 0, 0, escSeq{} for _, c := range str { if eSeq.isIn { eSeq.InspectRune(c) continue } eSeq.InspectRune(c) if c == '\n' { if currLength > maxLength { maxLength = currLength } currLength = 0 } else if !eSeq.isIn { currLength += RuneWidth(c) } } if currLength > maxLength { maxLength = currLength } return maxLength } // OverrideRuneWidthEastAsianWidth can *probably* help with alignment, and // length calculation issues when dealing with Unicode character-set and a // non-English language set in the LANG variable. // // Set this to 'false' to force the "runewidth" library to pretend to deal with // English character-set. Be warned that if the text/content you are dealing // with contains East Asian character-set, this may result in unexpected // behavior. // // References: // * https://github.com/mattn/go-runewidth/issues/64#issuecomment-1221642154 // * https://github.com/jedib0t/go-pretty/issues/220 // * https://github.com/jedib0t/go-pretty/issues/204 func OverrideRuneWidthEastAsianWidth(val bool) { rwCondition.EastAsianWidth = val } // Pad pads the given string with as many characters as needed to make it as // long as specified (maxLen). This function does not count escape sequences // while calculating length of the string. Ex.: // // Pad("Ghost", 0, ' ') == "Ghost" // Pad("Ghost", 3, ' ') == "Ghost" // Pad("Ghost", 5, ' ') == "Ghost" // Pad("Ghost", 7, ' ') == "Ghost " // Pad("Ghost", 10, '.') == "Ghost....." func Pad(str string, maxLen int, paddingChar rune) string { strLen := RuneWidthWithoutEscSequences(str) if strLen < maxLen { str += strings.Repeat(string(paddingChar), maxLen-strLen) } return str } // RepeatAndTrim repeats the given string until it is as long as maxRunes. // For ex.: // // RepeatAndTrim("", 5) == "" // RepeatAndTrim("Ghost", 0) == "" // RepeatAndTrim("Ghost", 5) == "Ghost" // RepeatAndTrim("Ghost", 7) == "GhostGh" // RepeatAndTrim("Ghost", 10) == "GhostGhost" func RepeatAndTrim(str string, maxRunes int) string { if str == "" || maxRunes == 0 { return "" } else if maxRunes == utf8.RuneCountInString(str) { return str } repeatedS := strings.Repeat(str, int(maxRunes/utf8.RuneCountInString(str))+1) return Trim(repeatedS, maxRunes) } // RuneCount is similar to utf8.RuneCountInString, except for the fact that it // ignores escape sequences while counting. For ex.: // // RuneCount("") == 0 // RuneCount("Ghost") == 5 // RuneCount("\x1b[33mGhost\x1b[0m") == 5 // RuneCount("\x1b[33mGhost\x1b[0") == 5 // // Deprecated: in favor of RuneWidthWithoutEscSequences func RuneCount(str string) int { return RuneWidthWithoutEscSequences(str) } // RuneWidth returns the mostly accurate character-width of the rune. This is // not 100% accurate as the character width is usually dependent on the // typeface (font) used in the console/terminal. For ex.: // // RuneWidth('A') == 1 // RuneWidth('ツ') == 2 // RuneWidth('⊙') == 1 // RuneWidth('︿') == 2 // RuneWidth(0x27) == 0 func RuneWidth(r rune) int { return rwCondition.RuneWidth(r) } // RuneWidthWithoutEscSequences is similar to RuneWidth, except for the fact // that it ignores escape sequences while counting. For ex.: // // RuneWidthWithoutEscSequences("") == 0 // RuneWidthWithoutEscSequences("Ghost") == 5 // RuneWidthWithoutEscSequences("\x1b[33mGhost\x1b[0m") == 5 // RuneWidthWithoutEscSequences("\x1b[33mGhost\x1b[0") == 5 func RuneWidthWithoutEscSequences(str string) int { count, eSeq := 0, escSeq{} for _, c := range str { if eSeq.isIn { eSeq.InspectRune(c) continue } eSeq.InspectRune(c) if !eSeq.isIn { count += RuneWidth(c) } } return count } // Snip returns the given string with a fixed length. For ex.: // // Snip("Ghost", 0, "~") == "Ghost" // Snip("Ghost", 1, "~") == "~" // Snip("Ghost", 3, "~") == "Gh~" // Snip("Ghost", 5, "~") == "Ghost" // Snip("Ghost", 7, "~") == "Ghost " // Snip("\x1b[33mGhost\x1b[0m", 7, "~") == "\x1b[33mGhost\x1b[0m " func Snip(str string, length int, snipIndicator string) string { if length > 0 { lenStr := RuneWidthWithoutEscSequences(str) if lenStr > length { lenStrFinal := length - RuneWidthWithoutEscSequences(snipIndicator) return Trim(str, lenStrFinal) + snipIndicator } } return str } // Trim trims a string to the given length while ignoring escape sequences. For // ex.: // // Trim("Ghost", 3) == "Gho" // Trim("Ghost", 6) == "Ghost" // Trim("\x1b[33mGhost\x1b[0m", 3) == "\x1b[33mGho\x1b[0m" // Trim("\x1b[33mGhost\x1b[0m", 6) == "\x1b[33mGhost\x1b[0m" func Trim(str string, maxLen int) string { if maxLen <= 0 { return "" } var out strings.Builder out.Grow(maxLen) outLen, eSeq := 0, escSeq{} for _, sChr := range str { if eSeq.isIn { eSeq.InspectRune(sChr) out.WriteRune(sChr) continue } eSeq.InspectRune(sChr) if eSeq.isIn { out.WriteRune(sChr) continue } if outLen < maxLen { outLen++ out.WriteRune(sChr) continue } } return out.String() } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/text/transformer.go ================================================ package text import ( "bytes" "encoding/json" "fmt" "strconv" "strings" "time" ) // Transformer related constants const ( unixTimeMinMilliseconds = int64(10000000000) unixTimeMinMicroseconds = unixTimeMinMilliseconds * 1000 unixTimeMinNanoSeconds = unixTimeMinMicroseconds * 1000 ) // Transformer related variables var ( colorsNumberPositive = Colors{FgHiGreen} colorsNumberNegative = Colors{FgHiRed} colorsNumberZero = Colors{} colorsURL = Colors{Underline, FgBlue} rfc3339Milli = "2006-01-02T15:04:05.000Z07:00" rfc3339Micro = "2006-01-02T15:04:05.000000Z07:00" possibleTimeLayouts = []string{ time.RFC3339, rfc3339Milli, // strfmt.DateTime.String()'s default layout rfc3339Micro, time.RFC3339Nano, } ) // Transformer helps format the contents of an object to the user's liking. type Transformer func(val interface{}) string // NewNumberTransformer returns a number Transformer that: // - transforms the number as directed by 'format' (ex.: %.2f) // - colors negative values Red // - colors positive values Green func NewNumberTransformer(format string) Transformer { return func(val interface{}) string { if valStr := transformInt(format, val); valStr != "" { return valStr } if valStr := transformUint(format, val); valStr != "" { return valStr } if valStr := transformFloat(format, val); valStr != "" { return valStr } return fmt.Sprint(val) } } func transformInt(format string, val interface{}) string { transform := func(val int64) string { if val < 0 { return colorsNumberNegative.Sprintf("-"+format, -val) } if val > 0 { return colorsNumberPositive.Sprintf(format, val) } return colorsNumberZero.Sprintf(format, val) } if number, ok := val.(int); ok { return transform(int64(number)) } if number, ok := val.(int8); ok { return transform(int64(number)) } if number, ok := val.(int16); ok { return transform(int64(number)) } if number, ok := val.(int32); ok { return transform(int64(number)) } if number, ok := val.(int64); ok { return transform(number) } return "" } func transformUint(format string, val interface{}) string { transform := func(val uint64) string { if val > 0 { return colorsNumberPositive.Sprintf(format, val) } return colorsNumberZero.Sprintf(format, val) } if number, ok := val.(uint); ok { return transform(uint64(number)) } if number, ok := val.(uint8); ok { return transform(uint64(number)) } if number, ok := val.(uint16); ok { return transform(uint64(number)) } if number, ok := val.(uint32); ok { return transform(uint64(number)) } if number, ok := val.(uint64); ok { return transform(number) } return "" } func transformFloat(format string, val interface{}) string { transform := func(val float64) string { if val < 0 { return colorsNumberNegative.Sprintf("-"+format, -val) } if val > 0 { return colorsNumberPositive.Sprintf(format, val) } return colorsNumberZero.Sprintf(format, val) } if number, ok := val.(float32); ok { return transform(float64(number)) } if number, ok := val.(float64); ok { return transform(number) } return "" } // NewJSONTransformer returns a Transformer that can format a JSON string or an // object into pretty-indented JSON-strings. func NewJSONTransformer(prefix string, indent string) Transformer { return func(val interface{}) string { if valStr, ok := val.(string); ok { var b bytes.Buffer if err := json.Indent(&b, []byte(strings.TrimSpace(valStr)), prefix, indent); err == nil { return b.String() } } else if b, err := json.MarshalIndent(val, prefix, indent); err == nil { return string(b) } return fmt.Sprintf("%#v", val) } } // NewTimeTransformer returns a Transformer that can format a timestamp (a // time.Time) into a well-defined time format defined using the provided layout // (ex.: time.RFC3339). // // If a non-nil location value is provided, the time will be localized to that // location (use time.Local to get localized timestamps). func NewTimeTransformer(layout string, location *time.Location) Transformer { return func(val interface{}) string { rsp := fmt.Sprint(val) if valTime, ok := val.(time.Time); ok { rsp = formatTime(valTime, layout, location) } else { // cycle through some supported layouts to see if the string form // of the object matches any of these layouts for _, possibleTimeLayout := range possibleTimeLayouts { if valTime, err := time.Parse(possibleTimeLayout, rsp); err == nil { rsp = formatTime(valTime, layout, location) break } } } return rsp } } // NewUnixTimeTransformer returns a Transformer that can format a unix-timestamp // into a well-defined time format as defined by 'layout'. This can handle // unix-time in Seconds, MilliSeconds, Microseconds and Nanoseconds. // // If a non-nil location value is provided, the time will be localized to that // location (use time.Local to get localized timestamps). func NewUnixTimeTransformer(layout string, location *time.Location) Transformer { transformer := NewTimeTransformer(layout, location) return func(val interface{}) string { if unixTime, ok := val.(int64); ok { return formatTimeUnix(unixTime, transformer) } else if unixTimeStr, ok := val.(string); ok { if unixTime, err := strconv.ParseInt(unixTimeStr, 10, 64); err == nil { return formatTimeUnix(unixTime, transformer) } } return fmt.Sprint(val) } } // NewURLTransformer returns a Transformer that can format and pretty print a string // that contains a URL (the text is underlined and colored Blue). func NewURLTransformer(colors ...Color) Transformer { colorsToUse := colorsURL if len(colors) > 0 { colorsToUse = colors } return func(val interface{}) string { return colorsToUse.Sprint(val) } } func formatTime(t time.Time, layout string, location *time.Location) string { rsp := "" if t.Unix() > 0 { if location != nil { t = t.In(location) } rsp = t.Format(layout) } return rsp } func formatTimeUnix(unixTime int64, timeTransformer Transformer) string { if unixTime >= unixTimeMinNanoSeconds { unixTime = unixTime / time.Second.Nanoseconds() } else if unixTime >= unixTimeMinMicroseconds { unixTime = unixTime / (time.Second.Nanoseconds() / 1000) } else if unixTime >= unixTimeMinMilliseconds { unixTime = unixTime / (time.Second.Nanoseconds() / 1000000) } return timeTransformer(time.Unix(unixTime, 0)) } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/text/valign.go ================================================ package text import "strings" // VAlign denotes how text is to be aligned vertically. type VAlign int // VAlign enumerations const ( VAlignDefault VAlign = iota // same as VAlignTop VAlignTop // "top\n\n" VAlignMiddle // "\nmiddle\n" VAlignBottom // "\n\nbottom" ) // Apply aligns the lines vertically. For ex.: // - VAlignTop.Apply({"Game", "Of", "Thrones"}, 5) // returns {"Game", "Of", "Thrones", "", ""} // - VAlignMiddle.Apply({"Game", "Of", "Thrones"}, 5) // returns {"", "Game", "Of", "Thrones", ""} // - VAlignBottom.Apply({"Game", "Of", "Thrones"}, 5) // returns {"", "", "Game", "Of", "Thrones"} func (va VAlign) Apply(lines []string, maxLines int) []string { if len(lines) == maxLines { return lines } else if len(lines) > maxLines { maxLines = len(lines) } insertIdx := 0 if va == VAlignMiddle { insertIdx = int(maxLines-len(lines)) / 2 } else if va == VAlignBottom { insertIdx = maxLines - len(lines) } linesOut := strings.Split(strings.Repeat("\n", maxLines-1), "\n") for idx, line := range lines { linesOut[idx+insertIdx] = line } return linesOut } // ApplyStr aligns the string (of 1 or more lines) vertically. For ex.: // - VAlignTop.ApplyStr("Game\nOf\nThrones", 5) // returns {"Game", "Of", "Thrones", "", ""} // - VAlignMiddle.ApplyStr("Game\nOf\nThrones", 5) // returns {"", "Game", "Of", "Thrones", ""} // - VAlignBottom.ApplyStr("Game\nOf\nThrones", 5) // returns {"", "", "Game", "Of", "Thrones"} func (va VAlign) ApplyStr(text string, maxLines int) []string { return va.Apply(strings.Split(text, "\n"), maxLines) } // HTMLProperty returns the equivalent HTML vertical-align tag property. func (va VAlign) HTMLProperty() string { switch va { case VAlignTop: return "valign=\"top\"" case VAlignMiddle: return "valign=\"middle\"" case VAlignBottom: return "valign=\"bottom\"" default: return "" } } ================================================ FILE: vendor/github.com/jedib0t/go-pretty/v6/text/wrap.go ================================================ package text import ( "strings" "unicode/utf8" ) // WrapHard wraps a string to the given length using a newline. Handles strings // with ANSI escape sequences (such as text color) without breaking the text // formatting. Breaks all words that go beyond the line boundary. // // For examples, refer to the unit-tests or GoDoc examples. func WrapHard(str string, wrapLen int) string { if wrapLen <= 0 { return "" } str = strings.Replace(str, "\t", " ", -1) sLen := utf8.RuneCountInString(str) if sLen <= wrapLen { return str } out := &strings.Builder{} out.Grow(sLen + (sLen / wrapLen)) for idx, paragraph := range strings.Split(str, "\n\n") { if idx > 0 { out.WriteString("\n\n") } wrapHard(paragraph, wrapLen, out) } return out.String() } // WrapSoft wraps a string to the given length using a newline. Handles strings // with ANSI escape sequences (such as text color) without breaking the text // formatting. Tries to move words that go beyond the line boundary to the next // line. // // For examples, refer to the unit-tests or GoDoc examples. func WrapSoft(str string, wrapLen int) string { if wrapLen <= 0 { return "" } str = strings.Replace(str, "\t", " ", -1) sLen := utf8.RuneCountInString(str) if sLen <= wrapLen { return str } out := &strings.Builder{} out.Grow(sLen + (sLen / wrapLen)) for idx, paragraph := range strings.Split(str, "\n\n") { if idx > 0 { out.WriteString("\n\n") } wrapSoft(paragraph, wrapLen, out) } return out.String() } // WrapText is very similar to WrapHard except for one minor difference. Unlike // WrapHard which discards line-breaks and respects only paragraph-breaks, this // function respects line-breaks too. // // For examples, refer to the unit-tests or GoDoc examples. func WrapText(str string, wrapLen int) string { if wrapLen <= 0 { return "" } var out strings.Builder sLen := utf8.RuneCountInString(str) out.Grow(sLen + (sLen / wrapLen)) lineIdx, isEscSeq, lastEscSeq := 0, false, "" for _, char := range str { if char == EscapeStartRune { isEscSeq = true lastEscSeq = "" } if isEscSeq { lastEscSeq += string(char) } appendChar(char, wrapLen, &lineIdx, isEscSeq, lastEscSeq, &out) if isEscSeq && char == EscapeStopRune { isEscSeq = false } if lastEscSeq == EscapeReset { lastEscSeq = "" } } if lastEscSeq != "" && lastEscSeq != EscapeReset { out.WriteString(EscapeReset) } return out.String() } func appendChar(char rune, wrapLen int, lineLen *int, inEscSeq bool, lastSeenEscSeq string, out *strings.Builder) { // handle reaching the end of the line as dictated by wrapLen or by finding // a newline character if (*lineLen == wrapLen && !inEscSeq && char != '\n') || (char == '\n') { if lastSeenEscSeq != "" { // terminate escape sequence and the line; and restart the escape // sequence in the next line out.WriteString(EscapeReset) out.WriteRune('\n') out.WriteString(lastSeenEscSeq) } else { // just start a new line out.WriteRune('\n') } // reset line index to 0th character *lineLen = 0 } // if the rune is not a new line, output it if char != '\n' { out.WriteRune(char) // increment the line index if not in the middle of an escape sequence if !inEscSeq { *lineLen++ } } } func appendWord(word string, lineIdx *int, lastSeenEscSeq string, wrapLen int, out *strings.Builder) { inEscSeq := false for _, char := range word { if char == EscapeStartRune { inEscSeq = true lastSeenEscSeq = "" } if inEscSeq { lastSeenEscSeq += string(char) } appendChar(char, wrapLen, lineIdx, inEscSeq, lastSeenEscSeq, out) if inEscSeq && char == EscapeStopRune { inEscSeq = false } if lastSeenEscSeq == EscapeReset { lastSeenEscSeq = "" } } } func extractOpenEscapeSeq(str string) string { escapeSeq, inEscSeq := "", false for _, char := range str { if char == EscapeStartRune { inEscSeq = true escapeSeq = "" } if inEscSeq { escapeSeq += string(char) } if char == EscapeStopRune { inEscSeq = false } } if escapeSeq == EscapeReset { escapeSeq = "" } return escapeSeq } func terminateLine(wrapLen int, lineLen *int, lastSeenEscSeq string, out *strings.Builder) { if *lineLen < wrapLen { out.WriteString(strings.Repeat(" ", wrapLen-*lineLen)) } // something is already on the line; terminate it if lastSeenEscSeq != "" { out.WriteString(EscapeReset) } out.WriteRune('\n') out.WriteString(lastSeenEscSeq) *lineLen = 0 } func terminateOutput(lastSeenEscSeq string, out *strings.Builder) { if lastSeenEscSeq != "" && lastSeenEscSeq != EscapeReset && !strings.HasSuffix(out.String(), EscapeReset) { out.WriteString(EscapeReset) } } func wrapHard(paragraph string, wrapLen int, out *strings.Builder) { lineLen, lastSeenEscSeq := 0, "" words := strings.Fields(paragraph) for wordIdx, word := range words { escSeq := extractOpenEscapeSeq(word) if escSeq != "" { lastSeenEscSeq = escSeq } if lineLen > 0 { out.WriteRune(' ') lineLen++ } wordLen := RuneWidthWithoutEscSequences(word) if lineLen+wordLen <= wrapLen { // word fits within the line out.WriteString(word) lineLen += wordLen } else { // word doesn't fit within the line; hard-wrap appendWord(word, &lineLen, lastSeenEscSeq, wrapLen, out) } // end of line; but more words incoming if lineLen == wrapLen && wordIdx < len(words)-1 { terminateLine(wrapLen, &lineLen, lastSeenEscSeq, out) } } terminateOutput(lastSeenEscSeq, out) } func wrapSoft(paragraph string, wrapLen int, out *strings.Builder) { lineLen, lastSeenEscSeq := 0, "" words := strings.Fields(paragraph) for wordIdx, word := range words { escSeq := extractOpenEscapeSeq(word) if escSeq != "" { lastSeenEscSeq = escSeq } spacing, spacingLen := wrapSoftSpacing(lineLen) wordLen := RuneWidthWithoutEscSequences(word) if lineLen+spacingLen+wordLen <= wrapLen { // word fits within the line out.WriteString(spacing) out.WriteString(word) lineLen += spacingLen + wordLen } else { // word doesn't fit within the line lineLen = wrapSoftLastWordInLine(wrapLen, lineLen, lastSeenEscSeq, wordLen, word, out) } // end of line; but more words incoming if lineLen == wrapLen && wordIdx < len(words)-1 { terminateLine(wrapLen, &lineLen, lastSeenEscSeq, out) } } terminateOutput(lastSeenEscSeq, out) } func wrapSoftLastWordInLine(wrapLen int, lineLen int, lastSeenEscSeq string, wordLen int, word string, out *strings.Builder) int { if lineLen > 0 { // something is already on the line; terminate it terminateLine(wrapLen, &lineLen, lastSeenEscSeq, out) } if wordLen <= wrapLen { // word fits within a single line out.WriteString(word) lineLen = wordLen } else { // word doesn't fit within a single line; hard-wrap appendWord(word, &lineLen, lastSeenEscSeq, wrapLen, out) } return lineLen } func wrapSoftSpacing(lineLen int) (string, int) { spacing, spacingLen := "", 0 if lineLen > 0 { spacing, spacingLen = " ", 1 } return spacing, spacingLen } ================================================ FILE: vendor/github.com/josharian/intern/README.md ================================================ Docs: https://godoc.org/github.com/josharian/intern See also [Go issue 5160](https://golang.org/issue/5160). License: MIT ================================================ FILE: vendor/github.com/josharian/intern/intern.go ================================================ // Package intern interns strings. // Interning is best effort only. // Interned strings may be removed automatically // at any time without notification. // All functions may be called concurrently // with themselves and each other. package intern import "sync" var ( pool sync.Pool = sync.Pool{ New: func() interface{} { return make(map[string]string) }, } ) // String returns s, interned. func String(s string) string { m := pool.Get().(map[string]string) c, ok := m[s] if ok { pool.Put(m) return c } m[s] = s pool.Put(m) return s } // Bytes returns b converted to a string, interned. func Bytes(b []byte) string { m := pool.Get().(map[string]string) c, ok := m[string(b)] if ok { pool.Put(m) return c } s := string(b) m[s] = s pool.Put(m) return s } ================================================ FILE: vendor/github.com/josharian/intern/license.md ================================================ MIT License Copyright (c) 2019 Josh Bleecher Snyder Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/kennygrant/sanitize/.gitignore ================================================ # Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe ================================================ FILE: vendor/github.com/kennygrant/sanitize/.travis.yml ================================================ language: go ================================================ FILE: vendor/github.com/kennygrant/sanitize/LICENSE ================================================ Copyright (c) 2017 Mechanism Design. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/kennygrant/sanitize/README.md ================================================ sanitize [![GoDoc](https://godoc.org/github.com/kennygrant/sanitize?status.svg)](https://godoc.org/github.com/kennygrant/sanitize) [![Go Report Card](https://goreportcard.com/badge/github.com/kennygrant/sanitize)](https://goreportcard.com/report/github.com/kennygrant/sanitize) [![CircleCI](https://circleci.com/gh/kennygrant/sanitize.svg?style=svg)](https://circleci.com/gh/kennygrant/sanitize) ======== Package sanitize provides functions to sanitize html and paths with go (golang). FUNCTIONS ```go sanitize.Accents(s string) string ``` Accents replaces a set of accented characters with ascii equivalents. ```go sanitize.BaseName(s string) string ``` BaseName makes a string safe to use in a file name, producing a sanitized basename replacing . or / with -. Unlike Name no attempt is made to normalise text as a path. ```go sanitize.HTML(s string) string ``` HTML strips html tags with a very simple parser, replace common entities, and escape < and > in the result. The result is intended to be used as plain text. ```go sanitize.HTMLAllowing(s string, args...[]string) (string, error) ``` HTMLAllowing parses html and allow certain tags and attributes from the lists optionally specified by args - args[0] is a list of allowed tags, args[1] is a list of allowed attributes. If either is missing default sets are used. ```go sanitize.Name(s string) string ``` Name makes a string safe to use in a file name by first finding the path basename, then replacing non-ascii characters. ```go sanitize.Path(s string) string ``` Path makes a string safe to use as an url path. Changes ------- Version 1.2 Adjusted HTML function to avoid linter warning Added more tests from https://githubengineering.com/githubs-post-csp-journey/ Chnaged name of license file Added badges and change log to readme Version 1.1 Fixed type in comments. Merge pull request from Povilas Balzaravicius Pawka - replace br tags with newline even when they contain a space Version 1.0 First release ================================================ FILE: vendor/github.com/kennygrant/sanitize/sanitize.go ================================================ // Package sanitize provides functions for sanitizing text. package sanitize import ( "bytes" "html" "html/template" "io" "path" "regexp" "strings" parser "golang.org/x/net/html" ) var ( ignoreTags = []string{"title", "script", "style", "iframe", "frame", "frameset", "noframes", "noembed", "embed", "applet", "object", "base"} defaultTags = []string{"h1", "h2", "h3", "h4", "h5", "h6", "div", "span", "hr", "p", "br", "b", "i", "strong", "em", "ol", "ul", "li", "a", "img", "pre", "code", "blockquote", "article", "section"} defaultAttributes = []string{"id", "class", "src", "href", "title", "alt", "name", "rel"} ) // HTMLAllowing sanitizes html, allowing some tags. // Arrays of allowed tags and allowed attributes may optionally be passed as the second and third arguments. func HTMLAllowing(s string, args ...[]string) (string, error) { allowedTags := defaultTags if len(args) > 0 { allowedTags = args[0] } allowedAttributes := defaultAttributes if len(args) > 1 { allowedAttributes = args[1] } // Parse the html tokenizer := parser.NewTokenizer(strings.NewReader(s)) buffer := bytes.NewBufferString("") ignore := "" for { tokenType := tokenizer.Next() token := tokenizer.Token() switch tokenType { case parser.ErrorToken: err := tokenizer.Err() if err == io.EOF { return buffer.String(), nil } return "", err case parser.StartTagToken: if len(ignore) == 0 && includes(allowedTags, token.Data) { token.Attr = cleanAttributes(token.Attr, allowedAttributes) buffer.WriteString(token.String()) } else if includes(ignoreTags, token.Data) { ignore = token.Data } case parser.SelfClosingTagToken: if len(ignore) == 0 && includes(allowedTags, token.Data) { token.Attr = cleanAttributes(token.Attr, allowedAttributes) buffer.WriteString(token.String()) } else if token.Data == ignore { ignore = "" } case parser.EndTagToken: if len(ignore) == 0 && includes(allowedTags, token.Data) { token.Attr = []parser.Attribute{} buffer.WriteString(token.String()) } else if token.Data == ignore { ignore = "" } case parser.TextToken: // We allow text content through, unless ignoring this entire tag and its contents (including other tags) if ignore == "" { buffer.WriteString(token.String()) } case parser.CommentToken: // We ignore comments by default case parser.DoctypeToken: // We ignore doctypes by default - html5 does not require them and this is intended for sanitizing snippets of text default: // We ignore unknown token types by default } } } // HTML strips html tags, replace common entities, and escapes <>&;'" in the result. // Note the returned text may contain entities as it is escaped by HTMLEscapeString, and most entities are not translated. func HTML(s string) (output string) { // Shortcut strings with no tags in them if !strings.ContainsAny(s, "<>") { output = s } else { // First remove line breaks etc as these have no meaning outside html tags (except pre) // this means pre sections will lose formatting... but will result in less unintentional paras. s = strings.Replace(s, "\n", "", -1) // Then replace line breaks with newlines, to preserve that formatting s = strings.Replace(s, "

", "\n", -1) s = strings.Replace(s, "
", "\n", -1) s = strings.Replace(s, "
", "\n", -1) s = strings.Replace(s, "
", "\n", -1) s = strings.Replace(s, "
", "\n", -1) // Walk through the string removing all tags b := bytes.NewBufferString("") inTag := false for _, r := range s { switch r { case '<': inTag = true case '>': inTag = false default: if !inTag { b.WriteRune(r) } } } output = b.String() } // Remove a few common harmless entities, to arrive at something more like plain text output = strings.Replace(output, "‘", "'", -1) output = strings.Replace(output, "’", "'", -1) output = strings.Replace(output, "“", "\"", -1) output = strings.Replace(output, "”", "\"", -1) output = strings.Replace(output, " ", " ", -1) output = strings.Replace(output, """, "\"", -1) output = strings.Replace(output, "'", "'", -1) // Translate some entities into their plain text equivalent (for example accents, if encoded as entities) output = html.UnescapeString(output) // In case we have missed any tags above, escape the text - removes <, >, &, ' and ". output = template.HTMLEscapeString(output) // After processing, remove some harmless entities &, ' and " which are encoded by HTMLEscapeString output = strings.Replace(output, """, "\"", -1) output = strings.Replace(output, "'", "'", -1) output = strings.Replace(output, "& ", "& ", -1) // NB space after output = strings.Replace(output, "&amp; ", "& ", -1) // NB space after return output } // We are very restrictive as this is intended for ascii url slugs var illegalPath = regexp.MustCompile(`[^[:alnum:]\~\-\./]`) // Path makes a string safe to use as a URL path, // removing accents and replacing separators with -. // The path may still start at / and is not intended // for use as a file system path without prefix. func Path(s string) string { // Start with lowercase string filePath := strings.ToLower(s) filePath = strings.Replace(filePath, "..", "", -1) filePath = path.Clean(filePath) // Remove illegal characters for paths, flattening accents // and replacing some common separators with - filePath = cleanString(filePath, illegalPath) // NB this may be of length 0, caller must check return filePath } // Remove all other unrecognised characters apart from var illegalName = regexp.MustCompile(`[^[:alnum:]-.]`) // Name makes a string safe to use in a file name by first finding the path basename, then replacing non-ascii characters. func Name(s string) string { // Start with lowercase string fileName := strings.ToLower(s) fileName = path.Clean(path.Base(fileName)) // Remove illegal characters for names, replacing some common separators with - fileName = cleanString(fileName, illegalName) // NB this may be of length 0, caller must check return fileName } // Replace these separators with - var baseNameSeparators = regexp.MustCompile(`[./]`) // BaseName makes a string safe to use in a file name, producing a sanitized basename replacing . or / with -. // No attempt is made to normalise a path or normalise case. func BaseName(s string) string { // Replace certain joining characters with a dash baseName := baseNameSeparators.ReplaceAllString(s, "-") // Remove illegal characters for names, replacing some common separators with - baseName = cleanString(baseName, illegalName) // NB this may be of length 0, caller must check return baseName } // A very limited list of transliterations to catch common european names translated to urls. // This set could be expanded with at least caps and many more characters. var transliterations = map[rune]string{ 'À': "A", 'Á': "A", 'Â': "A", 'Ã': "A", 'Ä': "A", 'Å': "AA", 'Æ': "AE", 'Ç': "C", 'È': "E", 'É': "E", 'Ê': "E", 'Ë': "E", 'Ì': "I", 'Í': "I", 'Î': "I", 'Ï': "I", 'Ð': "D", 'Ł': "L", 'Ñ': "N", 'Ò': "O", 'Ó': "O", 'Ô': "O", 'Õ': "O", 'Ö': "OE", 'Ø': "OE", 'Œ': "OE", 'Ù': "U", 'Ú': "U", 'Ü': "UE", 'Û': "U", 'Ý': "Y", 'Þ': "TH", 'ẞ': "SS", 'à': "a", 'á': "a", 'â': "a", 'ã': "a", 'ä': "ae", 'å': "aa", 'æ': "ae", 'ç': "c", 'è': "e", 'é': "e", 'ê': "e", 'ë': "e", 'ì': "i", 'í': "i", 'î': "i", 'ï': "i", 'ð': "d", 'ł': "l", 'ñ': "n", 'ń': "n", 'ò': "o", 'ó': "o", 'ô': "o", 'õ': "o", 'ō': "o", 'ö': "oe", 'ø': "oe", 'œ': "oe", 'ś': "s", 'ù': "u", 'ú': "u", 'û': "u", 'ū': "u", 'ü': "ue", 'ý': "y", 'ÿ': "y", 'ż': "z", 'þ': "th", 'ß': "ss", } // Accents replaces a set of accented characters with ascii equivalents. func Accents(s string) string { // Replace some common accent characters b := bytes.NewBufferString("") for _, c := range s { // Check transliterations first if val, ok := transliterations[c]; ok { b.WriteString(val) } else { b.WriteRune(c) } } return b.String() } var ( // If the attribute contains data: or javascript: anywhere, ignore it // we don't allow this in attributes as it is so frequently used for xss // NB we allow spaces in the value, and lowercase. illegalAttr = regexp.MustCompile(`(d\s*a\s*t\s*a|j\s*a\s*v\s*a\s*s\s*c\s*r\s*i\s*p\s*t\s*)\s*:`) // We are far more restrictive with href attributes. legalHrefAttr = regexp.MustCompile(`\A[/#][^/\\]?|mailto:|http://|https://`) ) // cleanAttributes returns an array of attributes after removing malicious ones. func cleanAttributes(a []parser.Attribute, allowed []string) []parser.Attribute { if len(a) == 0 { return a } var cleaned []parser.Attribute for _, attr := range a { if includes(allowed, attr.Key) { val := strings.ToLower(attr.Val) // Check for illegal attribute values if illegalAttr.FindString(val) != "" { attr.Val = "" } // Check for legal href values - / mailto:// http:// or https:// if attr.Key == "href" { if legalHrefAttr.FindString(val) == "" { attr.Val = "" } } // If we still have an attribute, append it to the array if attr.Val != "" { cleaned = append(cleaned, attr) } } } return cleaned } // A list of characters we consider separators in normal strings and replace with our canonical separator - rather than removing. var ( separators = regexp.MustCompile(`[ &_=+:]`) dashes = regexp.MustCompile(`[\-]+`) ) // cleanString replaces separators with - and removes characters listed in the regexp provided from string. // Accents, spaces, and all characters not in A-Za-z0-9 are replaced. func cleanString(s string, r *regexp.Regexp) string { // Remove any trailing space to avoid ending on - s = strings.Trim(s, " ") // Flatten accents first so that if we remove non-ascii we still get a legible name s = Accents(s) // Replace certain joining characters with a dash s = separators.ReplaceAllString(s, "-") // Remove all other unrecognised characters - NB we do allow any printable characters s = r.ReplaceAllString(s, "") // Remove any multiple dashes caused by replacements above s = dashes.ReplaceAllString(s, "-") return s } // includes checks for inclusion of a string in a []string. func includes(a []string, s string) bool { for _, as := range a { if as == s { return true } } return false } ================================================ FILE: vendor/github.com/klauspost/compress/.gitattributes ================================================ * -text *.bin -text -diff ================================================ FILE: vendor/github.com/klauspost/compress/.gitignore ================================================ # Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof /s2/cmd/_s2sx/sfx-exe # Linux perf files perf.data perf.data.old # gdb history .gdb_history ================================================ FILE: vendor/github.com/klauspost/compress/.goreleaser.yml ================================================ # This is an example goreleaser.yaml file with some sane defaults. # Make sure to check the documentation at http://goreleaser.com before: hooks: - ./gen.sh - go install mvdan.cc/garble@v0.10.1 builds: - id: "s2c" binary: s2c main: ./s2/cmd/s2c/main.go flags: - -trimpath env: - CGO_ENABLED=0 goos: - aix - linux - freebsd - netbsd - windows - darwin goarch: - 386 - amd64 - arm - arm64 - ppc64 - ppc64le - mips64 - mips64le goarm: - 7 gobinary: garble - id: "s2d" binary: s2d main: ./s2/cmd/s2d/main.go flags: - -trimpath env: - CGO_ENABLED=0 goos: - aix - linux - freebsd - netbsd - windows - darwin goarch: - 386 - amd64 - arm - arm64 - ppc64 - ppc64le - mips64 - mips64le goarm: - 7 gobinary: garble - id: "s2sx" binary: s2sx main: ./s2/cmd/_s2sx/main.go flags: - -modfile=s2sx.mod - -trimpath env: - CGO_ENABLED=0 goos: - aix - linux - freebsd - netbsd - windows - darwin goarch: - 386 - amd64 - arm - arm64 - ppc64 - ppc64le - mips64 - mips64le goarm: - 7 gobinary: garble archives: - id: s2-binaries name_template: "s2-{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" format_overrides: - goos: windows format: zip files: - unpack/* - s2/LICENSE - s2/README.md checksum: name_template: 'checksums.txt' snapshot: name_template: "{{ .Tag }}-next" changelog: sort: asc filters: exclude: - '^doc:' - '^docs:' - '^test:' - '^tests:' - '^Update\sREADME.md' nfpms: - file_name_template: "s2_package__{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" vendor: Klaus Post homepage: https://github.com/klauspost/compress maintainer: Klaus Post description: S2 Compression Tool license: BSD 3-Clause formats: - deb - rpm ================================================ FILE: vendor/github.com/klauspost/compress/LICENSE ================================================ Copyright (c) 2012 The Go Authors. All rights reserved. Copyright (c) 2019 Klaus Post. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------ Files: gzhttp/* Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2016-2017 The New York Times Company Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ------------------ Files: s2/cmd/internal/readahead/* The MIT License (MIT) Copyright (c) 2015 Klaus Post Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------- Files: snappy/* Files: internal/snapref/* Copyright (c) 2011 The Snappy-Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------- Files: s2/cmd/internal/filepathx/* Copyright 2016 The filepathx Authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/klauspost/compress/README.md ================================================ # compress This package provides various compression algorithms. * [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and decompression in pure Go. * [S2](https://github.com/klauspost/compress/tree/master/s2#s2-compression) is a high performance replacement for Snappy. * Optimized [deflate](https://godoc.org/github.com/klauspost/compress/flate) packages which can be used as a dropin replacement for [gzip](https://godoc.org/github.com/klauspost/compress/gzip), [zip](https://godoc.org/github.com/klauspost/compress/zip) and [zlib](https://godoc.org/github.com/klauspost/compress/zlib). * [snappy](https://github.com/klauspost/compress/tree/master/snappy) is a drop-in replacement for `github.com/golang/snappy` offering better compression and concurrent streams. * [huff0](https://github.com/klauspost/compress/tree/master/huff0) and [FSE](https://github.com/klauspost/compress/tree/master/fse) implementations for raw entropy encoding. * [gzhttp](https://github.com/klauspost/compress/tree/master/gzhttp) Provides client and server wrappers for handling gzipped requests efficiently. * [pgzip](https://github.com/klauspost/pgzip) is a separate package that provides a very fast parallel gzip implementation. [![Go Reference](https://pkg.go.dev/badge/klauspost/compress.svg)](https://pkg.go.dev/github.com/klauspost/compress?tab=subdirectories) [![Go](https://github.com/klauspost/compress/actions/workflows/go.yml/badge.svg)](https://github.com/klauspost/compress/actions/workflows/go.yml) [![Sourcegraph Badge](https://sourcegraph.com/github.com/klauspost/compress/-/badge.svg)](https://sourcegraph.com/github.com/klauspost/compress?badge) # changelog * Oct 22nd, 2023 - [v1.17.2](https://github.com/klauspost/compress/releases/tag/v1.17.2) * zstd: Fix rare *CORRUPTION* output in "best" mode. See https://github.com/klauspost/compress/pull/876 * Oct 14th, 2023 - [v1.17.1](https://github.com/klauspost/compress/releases/tag/v1.17.1) * s2: Fix S2 "best" dictionary wrong encoding by @klauspost in https://github.com/klauspost/compress/pull/871 * flate: Reduce allocations in decompressor and minor code improvements by @fakefloordiv in https://github.com/klauspost/compress/pull/869 * s2: Fix EstimateBlockSize on 6&7 length input by @klauspost in https://github.com/klauspost/compress/pull/867 * Sept 19th, 2023 - [v1.17.0](https://github.com/klauspost/compress/releases/tag/v1.17.0) * Add experimental dictionary builder https://github.com/klauspost/compress/pull/853 * Add xerial snappy read/writer https://github.com/klauspost/compress/pull/838 * flate: Add limited window compression https://github.com/klauspost/compress/pull/843 * s2: Do 2 overlapping match checks https://github.com/klauspost/compress/pull/839 * flate: Add amd64 assembly matchlen https://github.com/klauspost/compress/pull/837 * gzip: Copy bufio.Reader on Reset by @thatguystone in https://github.com/klauspost/compress/pull/860 * July 1st, 2023 - [v1.16.7](https://github.com/klauspost/compress/releases/tag/v1.16.7) * zstd: Fix default level first dictionary encode https://github.com/klauspost/compress/pull/829 * s2: add GetBufferCapacity() method by @GiedriusS in https://github.com/klauspost/compress/pull/832 * June 13, 2023 - [v1.16.6](https://github.com/klauspost/compress/releases/tag/v1.16.6) * zstd: correctly ignore WithEncoderPadding(1) by @ianlancetaylor in https://github.com/klauspost/compress/pull/806 * zstd: Add amd64 match length assembly https://github.com/klauspost/compress/pull/824 * gzhttp: Handle informational headers by @rtribotte in https://github.com/klauspost/compress/pull/815 * s2: Improve Better compression slightly https://github.com/klauspost/compress/pull/663 * Apr 16, 2023 - [v1.16.5](https://github.com/klauspost/compress/releases/tag/v1.16.5) * zstd: readByte needs to use io.ReadFull by @jnoxon in https://github.com/klauspost/compress/pull/802 * gzip: Fix WriterTo after initial read https://github.com/klauspost/compress/pull/804 * Apr 5, 2023 - [v1.16.4](https://github.com/klauspost/compress/releases/tag/v1.16.4) * zstd: Improve zstd best efficiency by @greatroar and @klauspost in https://github.com/klauspost/compress/pull/784 * zstd: Respect WithAllLitEntropyCompression https://github.com/klauspost/compress/pull/792 * zstd: Fix amd64 not always detecting corrupt data https://github.com/klauspost/compress/pull/785 * zstd: Various minor improvements by @greatroar in https://github.com/klauspost/compress/pull/788 https://github.com/klauspost/compress/pull/794 https://github.com/klauspost/compress/pull/795 * s2: Fix huge block overflow https://github.com/klauspost/compress/pull/779 * s2: Allow CustomEncoder fallback https://github.com/klauspost/compress/pull/780 * gzhttp: Suppport ResponseWriter Unwrap() in gzhttp handler by @jgimenez in https://github.com/klauspost/compress/pull/799 * Mar 13, 2023 - [v1.16.1](https://github.com/klauspost/compress/releases/tag/v1.16.1) * zstd: Speed up + improve best encoder by @greatroar in https://github.com/klauspost/compress/pull/776 * gzhttp: Add optional [BREACH mitigation](https://github.com/klauspost/compress/tree/master/gzhttp#breach-mitigation). https://github.com/klauspost/compress/pull/762 https://github.com/klauspost/compress/pull/768 https://github.com/klauspost/compress/pull/769 https://github.com/klauspost/compress/pull/770 https://github.com/klauspost/compress/pull/767 * s2: Add Intel LZ4s converter https://github.com/klauspost/compress/pull/766 * zstd: Minor bug fixes https://github.com/klauspost/compress/pull/771 https://github.com/klauspost/compress/pull/772 https://github.com/klauspost/compress/pull/773 * huff0: Speed up compress1xDo by @greatroar in https://github.com/klauspost/compress/pull/774 * Feb 26, 2023 - [v1.16.0](https://github.com/klauspost/compress/releases/tag/v1.16.0) * s2: Add [Dictionary](https://github.com/klauspost/compress/tree/master/s2#dictionaries) support. https://github.com/klauspost/compress/pull/685 * s2: Add Compression Size Estimate. https://github.com/klauspost/compress/pull/752 * s2: Add support for custom stream encoder. https://github.com/klauspost/compress/pull/755 * s2: Add LZ4 block converter. https://github.com/klauspost/compress/pull/748 * s2: Support io.ReaderAt in ReadSeeker. https://github.com/klauspost/compress/pull/747 * s2c/s2sx: Use concurrent decoding. https://github.com/klauspost/compress/pull/746
See changes to v1.15.x * Jan 21st, 2023 (v1.15.15) * deflate: Improve level 7-9 by @klauspost in https://github.com/klauspost/compress/pull/739 * zstd: Add delta encoding support by @greatroar in https://github.com/klauspost/compress/pull/728 * zstd: Various speed improvements by @greatroar https://github.com/klauspost/compress/pull/741 https://github.com/klauspost/compress/pull/734 https://github.com/klauspost/compress/pull/736 https://github.com/klauspost/compress/pull/744 https://github.com/klauspost/compress/pull/743 https://github.com/klauspost/compress/pull/745 * gzhttp: Add SuffixETag() and DropETag() options to prevent ETag collisions on compressed responses by @willbicks in https://github.com/klauspost/compress/pull/740 * Jan 3rd, 2023 (v1.15.14) * flate: Improve speed in big stateless blocks https://github.com/klauspost/compress/pull/718 * zstd: Minor speed tweaks by @greatroar in https://github.com/klauspost/compress/pull/716 https://github.com/klauspost/compress/pull/720 * export NoGzipResponseWriter for custom ResponseWriter wrappers by @harshavardhana in https://github.com/klauspost/compress/pull/722 * s2: Add example for indexing and existing stream https://github.com/klauspost/compress/pull/723 * Dec 11, 2022 (v1.15.13) * zstd: Add [MaxEncodedSize](https://pkg.go.dev/github.com/klauspost/compress@v1.15.13/zstd#Encoder.MaxEncodedSize) to encoder https://github.com/klauspost/compress/pull/691 * zstd: Various tweaks and improvements https://github.com/klauspost/compress/pull/693 https://github.com/klauspost/compress/pull/695 https://github.com/klauspost/compress/pull/696 https://github.com/klauspost/compress/pull/701 https://github.com/klauspost/compress/pull/702 https://github.com/klauspost/compress/pull/703 https://github.com/klauspost/compress/pull/704 https://github.com/klauspost/compress/pull/705 https://github.com/klauspost/compress/pull/706 https://github.com/klauspost/compress/pull/707 https://github.com/klauspost/compress/pull/708 * Oct 26, 2022 (v1.15.12) * zstd: Tweak decoder allocs. https://github.com/klauspost/compress/pull/680 * gzhttp: Always delete `HeaderNoCompression` https://github.com/klauspost/compress/pull/683 * Sept 26, 2022 (v1.15.11) * flate: Improve level 1-3 compression https://github.com/klauspost/compress/pull/678 * zstd: Improve "best" compression by @nightwolfz in https://github.com/klauspost/compress/pull/677 * zstd: Fix+reduce decompression allocations https://github.com/klauspost/compress/pull/668 * zstd: Fix non-effective noescape tag https://github.com/klauspost/compress/pull/667 * Sept 16, 2022 (v1.15.10) * zstd: Add [WithDecodeAllCapLimit](https://pkg.go.dev/github.com/klauspost/compress@v1.15.10/zstd#WithDecodeAllCapLimit) https://github.com/klauspost/compress/pull/649 * Add Go 1.19 - deprecate Go 1.16 https://github.com/klauspost/compress/pull/651 * flate: Improve level 5+6 compression https://github.com/klauspost/compress/pull/656 * zstd: Improve "better" compresssion https://github.com/klauspost/compress/pull/657 * s2: Improve "best" compression https://github.com/klauspost/compress/pull/658 * s2: Improve "better" compression. https://github.com/klauspost/compress/pull/635 * s2: Slightly faster non-assembly decompression https://github.com/klauspost/compress/pull/646 * Use arrays for constant size copies https://github.com/klauspost/compress/pull/659 * July 21, 2022 (v1.15.9) * zstd: Fix decoder crash on amd64 (no BMI) on invalid input https://github.com/klauspost/compress/pull/645 * zstd: Disable decoder extended memory copies (amd64) due to possible crashes https://github.com/klauspost/compress/pull/644 * zstd: Allow single segments up to "max decoded size" by @klauspost in https://github.com/klauspost/compress/pull/643 * July 13, 2022 (v1.15.8) * gzip: fix stack exhaustion bug in Reader.Read https://github.com/klauspost/compress/pull/641 * s2: Add Index header trim/restore https://github.com/klauspost/compress/pull/638 * zstd: Optimize seqdeq amd64 asm by @greatroar in https://github.com/klauspost/compress/pull/636 * zstd: Improve decoder memcopy https://github.com/klauspost/compress/pull/637 * huff0: Pass a single bitReader pointer to asm by @greatroar in https://github.com/klauspost/compress/pull/634 * zstd: Branchless getBits for amd64 w/o BMI2 by @greatroar in https://github.com/klauspost/compress/pull/640 * gzhttp: Remove header before writing https://github.com/klauspost/compress/pull/639 * June 29, 2022 (v1.15.7) * s2: Fix absolute forward seeks https://github.com/klauspost/compress/pull/633 * zip: Merge upstream https://github.com/klauspost/compress/pull/631 * zip: Re-add zip64 fix https://github.com/klauspost/compress/pull/624 * zstd: translate fseDecoder.buildDtable into asm by @WojciechMula in https://github.com/klauspost/compress/pull/598 * flate: Faster histograms https://github.com/klauspost/compress/pull/620 * deflate: Use compound hcode https://github.com/klauspost/compress/pull/622 * June 3, 2022 (v1.15.6) * s2: Improve coding for long, close matches https://github.com/klauspost/compress/pull/613 * s2c: Add Snappy/S2 stream recompression https://github.com/klauspost/compress/pull/611 * zstd: Always use configured block size https://github.com/klauspost/compress/pull/605 * zstd: Fix incorrect hash table placement for dict encoding in default https://github.com/klauspost/compress/pull/606 * zstd: Apply default config to ZipDecompressor without options https://github.com/klauspost/compress/pull/608 * gzhttp: Exclude more common archive formats https://github.com/klauspost/compress/pull/612 * s2: Add ReaderIgnoreCRC https://github.com/klauspost/compress/pull/609 * s2: Remove sanity load on index creation https://github.com/klauspost/compress/pull/607 * snappy: Use dedicated function for scoring https://github.com/klauspost/compress/pull/614 * s2c+s2d: Use official snappy framed extension https://github.com/klauspost/compress/pull/610 * May 25, 2022 (v1.15.5) * s2: Add concurrent stream decompression https://github.com/klauspost/compress/pull/602 * s2: Fix final emit oob read crash on amd64 https://github.com/klauspost/compress/pull/601 * huff0: asm implementation of Decompress1X by @WojciechMula https://github.com/klauspost/compress/pull/596 * zstd: Use 1 less goroutine for stream decoding https://github.com/klauspost/compress/pull/588 * zstd: Copy literal in 16 byte blocks when possible https://github.com/klauspost/compress/pull/592 * zstd: Speed up when WithDecoderLowmem(false) https://github.com/klauspost/compress/pull/599 * zstd: faster next state update in BMI2 version of decode by @WojciechMula in https://github.com/klauspost/compress/pull/593 * huff0: Do not check max size when reading table. https://github.com/klauspost/compress/pull/586 * flate: Inplace hashing for level 7-9 by @klauspost in https://github.com/klauspost/compress/pull/590 * May 11, 2022 (v1.15.4) * huff0: decompress directly into output by @WojciechMula in [#577](https://github.com/klauspost/compress/pull/577) * inflate: Keep dict on stack [#581](https://github.com/klauspost/compress/pull/581) * zstd: Faster decoding memcopy in asm [#583](https://github.com/klauspost/compress/pull/583) * zstd: Fix ignored crc [#580](https://github.com/klauspost/compress/pull/580) * May 5, 2022 (v1.15.3) * zstd: Allow to ignore checksum checking by @WojciechMula [#572](https://github.com/klauspost/compress/pull/572) * s2: Fix incorrect seek for io.SeekEnd in [#575](https://github.com/klauspost/compress/pull/575) * Apr 26, 2022 (v1.15.2) * zstd: Add x86-64 assembly for decompression on streams and blocks. Contributed by [@WojciechMula](https://github.com/WojciechMula). Typically 2x faster. [#528](https://github.com/klauspost/compress/pull/528) [#531](https://github.com/klauspost/compress/pull/531) [#545](https://github.com/klauspost/compress/pull/545) [#537](https://github.com/klauspost/compress/pull/537) * zstd: Add options to ZipDecompressor and fixes [#539](https://github.com/klauspost/compress/pull/539) * s2: Use sorted search for index [#555](https://github.com/klauspost/compress/pull/555) * Minimum version is Go 1.16, added CI test on 1.18. * Mar 11, 2022 (v1.15.1) * huff0: Add x86 assembly of Decode4X by @WojciechMula in [#512](https://github.com/klauspost/compress/pull/512) * zstd: Reuse zip decoders in [#514](https://github.com/klauspost/compress/pull/514) * zstd: Detect extra block data and report as corrupted in [#520](https://github.com/klauspost/compress/pull/520) * zstd: Handle zero sized frame content size stricter in [#521](https://github.com/klauspost/compress/pull/521) * zstd: Add stricter block size checks in [#523](https://github.com/klauspost/compress/pull/523) * Mar 3, 2022 (v1.15.0) * zstd: Refactor decoder by @klauspost in [#498](https://github.com/klauspost/compress/pull/498) * zstd: Add stream encoding without goroutines by @klauspost in [#505](https://github.com/klauspost/compress/pull/505) * huff0: Prevent single blocks exceeding 16 bits by @klauspost in[#507](https://github.com/klauspost/compress/pull/507) * flate: Inline literal emission by @klauspost in [#509](https://github.com/klauspost/compress/pull/509) * gzhttp: Add zstd to transport by @klauspost in [#400](https://github.com/klauspost/compress/pull/400) * gzhttp: Make content-type optional by @klauspost in [#510](https://github.com/klauspost/compress/pull/510) Both compression and decompression now supports "synchronous" stream operations. This means that whenever "concurrency" is set to 1, they will operate without spawning goroutines. Stream decompression is now faster on asynchronous, since the goroutine allocation much more effectively splits the workload. On typical streams this will typically use 2 cores fully for decompression. When a stream has finished decoding no goroutines will be left over, so decoders can now safely be pooled and still be garbage collected. While the release has been extensively tested, it is recommended to testing when upgrading.
See changes to v1.14.x * Feb 22, 2022 (v1.14.4) * flate: Fix rare huffman only (-2) corruption. [#503](https://github.com/klauspost/compress/pull/503) * zip: Update deprecated CreateHeaderRaw to correctly call CreateRaw by @saracen in [#502](https://github.com/klauspost/compress/pull/502) * zip: don't read data descriptor early by @saracen in [#501](https://github.com/klauspost/compress/pull/501) #501 * huff0: Use static decompression buffer up to 30% faster by @klauspost in [#499](https://github.com/klauspost/compress/pull/499) [#500](https://github.com/klauspost/compress/pull/500) * Feb 17, 2022 (v1.14.3) * flate: Improve fastest levels compression speed ~10% more throughput. [#482](https://github.com/klauspost/compress/pull/482) [#489](https://github.com/klauspost/compress/pull/489) [#490](https://github.com/klauspost/compress/pull/490) [#491](https://github.com/klauspost/compress/pull/491) [#494](https://github.com/klauspost/compress/pull/494) [#478](https://github.com/klauspost/compress/pull/478) * flate: Faster decompression speed, ~5-10%. [#483](https://github.com/klauspost/compress/pull/483) * s2: Faster compression with Go v1.18 and amd64 microarch level 3+. [#484](https://github.com/klauspost/compress/pull/484) [#486](https://github.com/klauspost/compress/pull/486) * Jan 25, 2022 (v1.14.2) * zstd: improve header decoder by @dsnet [#476](https://github.com/klauspost/compress/pull/476) * zstd: Add bigger default blocks [#469](https://github.com/klauspost/compress/pull/469) * zstd: Remove unused decompression buffer [#470](https://github.com/klauspost/compress/pull/470) * zstd: Fix logically dead code by @ningmingxiao [#472](https://github.com/klauspost/compress/pull/472) * flate: Improve level 7-9 [#471](https://github.com/klauspost/compress/pull/471) [#473](https://github.com/klauspost/compress/pull/473) * zstd: Add noasm tag for xxhash [#475](https://github.com/klauspost/compress/pull/475) * Jan 11, 2022 (v1.14.1) * s2: Add stream index in [#462](https://github.com/klauspost/compress/pull/462) * flate: Speed and efficiency improvements in [#439](https://github.com/klauspost/compress/pull/439) [#461](https://github.com/klauspost/compress/pull/461) [#455](https://github.com/klauspost/compress/pull/455) [#452](https://github.com/klauspost/compress/pull/452) [#458](https://github.com/klauspost/compress/pull/458) * zstd: Performance improvement in [#420]( https://github.com/klauspost/compress/pull/420) [#456](https://github.com/klauspost/compress/pull/456) [#437](https://github.com/klauspost/compress/pull/437) [#467](https://github.com/klauspost/compress/pull/467) [#468](https://github.com/klauspost/compress/pull/468) * zstd: add arm64 xxhash assembly in [#464](https://github.com/klauspost/compress/pull/464) * Add garbled for binaries for s2 in [#445](https://github.com/klauspost/compress/pull/445)
See changes to v1.13.x * Aug 30, 2021 (v1.13.5) * gz/zlib/flate: Alias stdlib errors [#425](https://github.com/klauspost/compress/pull/425) * s2: Add block support to commandline tools [#413](https://github.com/klauspost/compress/pull/413) * zstd: pooledZipWriter should return Writers to the same pool [#426](https://github.com/klauspost/compress/pull/426) * Removed golang/snappy as external dependency for tests [#421](https://github.com/klauspost/compress/pull/421) * Aug 12, 2021 (v1.13.4) * Add [snappy replacement package](https://github.com/klauspost/compress/tree/master/snappy). * zstd: Fix incorrect encoding in "best" mode [#415](https://github.com/klauspost/compress/pull/415) * Aug 3, 2021 (v1.13.3) * zstd: Improve Best compression [#404](https://github.com/klauspost/compress/pull/404) * zstd: Fix WriteTo error forwarding [#411](https://github.com/klauspost/compress/pull/411) * gzhttp: Return http.HandlerFunc instead of http.Handler. Unlikely breaking change. [#406](https://github.com/klauspost/compress/pull/406) * s2sx: Fix max size error [#399](https://github.com/klauspost/compress/pull/399) * zstd: Add optional stream content size on reset [#401](https://github.com/klauspost/compress/pull/401) * zstd: use SpeedBestCompression for level >= 10 [#410](https://github.com/klauspost/compress/pull/410) * Jun 14, 2021 (v1.13.1) * s2: Add full Snappy output support [#396](https://github.com/klauspost/compress/pull/396) * zstd: Add configurable [Decoder window](https://pkg.go.dev/github.com/klauspost/compress/zstd#WithDecoderMaxWindow) size [#394](https://github.com/klauspost/compress/pull/394) * gzhttp: Add header to skip compression [#389](https://github.com/klauspost/compress/pull/389) * s2: Improve speed with bigger output margin [#395](https://github.com/klauspost/compress/pull/395) * Jun 3, 2021 (v1.13.0) * Added [gzhttp](https://github.com/klauspost/compress/tree/master/gzhttp#gzip-handler) which allows wrapping HTTP servers and clients with GZIP compressors. * zstd: Detect short invalid signatures [#382](https://github.com/klauspost/compress/pull/382) * zstd: Spawn decoder goroutine only if needed. [#380](https://github.com/klauspost/compress/pull/380)
See changes to v1.12.x * May 25, 2021 (v1.12.3) * deflate: Better/faster Huffman encoding [#374](https://github.com/klauspost/compress/pull/374) * deflate: Allocate less for history. [#375](https://github.com/klauspost/compress/pull/375) * zstd: Forward read errors [#373](https://github.com/klauspost/compress/pull/373) * Apr 27, 2021 (v1.12.2) * zstd: Improve better/best compression [#360](https://github.com/klauspost/compress/pull/360) [#364](https://github.com/klauspost/compress/pull/364) [#365](https://github.com/klauspost/compress/pull/365) * zstd: Add helpers to compress/decompress zstd inside zip files [#363](https://github.com/klauspost/compress/pull/363) * deflate: Improve level 5+6 compression [#367](https://github.com/klauspost/compress/pull/367) * s2: Improve better/best compression [#358](https://github.com/klauspost/compress/pull/358) [#359](https://github.com/klauspost/compress/pull/358) * s2: Load after checking src limit on amd64. [#362](https://github.com/klauspost/compress/pull/362) * s2sx: Limit max executable size [#368](https://github.com/klauspost/compress/pull/368) * Apr 14, 2021 (v1.12.1) * snappy package removed. Upstream added as dependency. * s2: Better compression in "best" mode [#353](https://github.com/klauspost/compress/pull/353) * s2sx: Add stdin input and detect pre-compressed from signature [#352](https://github.com/klauspost/compress/pull/352) * s2c/s2d: Add http as possible input [#348](https://github.com/klauspost/compress/pull/348) * s2c/s2d/s2sx: Always truncate when writing files [#352](https://github.com/klauspost/compress/pull/352) * zstd: Reduce memory usage further when using [WithLowerEncoderMem](https://pkg.go.dev/github.com/klauspost/compress/zstd#WithLowerEncoderMem) [#346](https://github.com/klauspost/compress/pull/346) * s2: Fix potential problem with amd64 assembly and profilers [#349](https://github.com/klauspost/compress/pull/349)
See changes to v1.11.x * Mar 26, 2021 (v1.11.13) * zstd: Big speedup on small dictionary encodes [#344](https://github.com/klauspost/compress/pull/344) [#345](https://github.com/klauspost/compress/pull/345) * zstd: Add [WithLowerEncoderMem](https://pkg.go.dev/github.com/klauspost/compress/zstd#WithLowerEncoderMem) encoder option [#336](https://github.com/klauspost/compress/pull/336) * deflate: Improve entropy compression [#338](https://github.com/klauspost/compress/pull/338) * s2: Clean up and minor performance improvement in best [#341](https://github.com/klauspost/compress/pull/341) * Mar 5, 2021 (v1.11.12) * s2: Add `s2sx` binary that creates [self extracting archives](https://github.com/klauspost/compress/tree/master/s2#s2sx-self-extracting-archives). * s2: Speed up decompression on non-assembly platforms [#328](https://github.com/klauspost/compress/pull/328) * Mar 1, 2021 (v1.11.9) * s2: Add ARM64 decompression assembly. Around 2x output speed. [#324](https://github.com/klauspost/compress/pull/324) * s2: Improve "better" speed and efficiency. [#325](https://github.com/klauspost/compress/pull/325) * s2: Fix binaries. * Feb 25, 2021 (v1.11.8) * s2: Fixed occational out-of-bounds write on amd64. Upgrade recommended. * s2: Add AMD64 assembly for better mode. 25-50% faster. [#315](https://github.com/klauspost/compress/pull/315) * s2: Less upfront decoder allocation. [#322](https://github.com/klauspost/compress/pull/322) * zstd: Faster "compression" of incompressible data. [#314](https://github.com/klauspost/compress/pull/314) * zip: Fix zip64 headers. [#313](https://github.com/klauspost/compress/pull/313) * Jan 14, 2021 (v1.11.7) * Use Bytes() interface to get bytes across packages. [#309](https://github.com/klauspost/compress/pull/309) * s2: Add 'best' compression option. [#310](https://github.com/klauspost/compress/pull/310) * s2: Add ReaderMaxBlockSize, changes `s2.NewReader` signature to include varargs. [#311](https://github.com/klauspost/compress/pull/311) * s2: Fix crash on small better buffers. [#308](https://github.com/klauspost/compress/pull/308) * s2: Clean up decoder. [#312](https://github.com/klauspost/compress/pull/312) * Jan 7, 2021 (v1.11.6) * zstd: Make decoder allocations smaller [#306](https://github.com/klauspost/compress/pull/306) * zstd: Free Decoder resources when Reset is called with a nil io.Reader [#305](https://github.com/klauspost/compress/pull/305) * Dec 20, 2020 (v1.11.4) * zstd: Add Best compression mode [#304](https://github.com/klauspost/compress/pull/304) * Add header decoder [#299](https://github.com/klauspost/compress/pull/299) * s2: Add uncompressed stream option [#297](https://github.com/klauspost/compress/pull/297) * Simplify/speed up small blocks with known max size. [#300](https://github.com/klauspost/compress/pull/300) * zstd: Always reset literal dict encoder [#303](https://github.com/klauspost/compress/pull/303) * Nov 15, 2020 (v1.11.3) * inflate: 10-15% faster decompression [#293](https://github.com/klauspost/compress/pull/293) * zstd: Tweak DecodeAll default allocation [#295](https://github.com/klauspost/compress/pull/295) * Oct 11, 2020 (v1.11.2) * s2: Fix out of bounds read in "better" block compression [#291](https://github.com/klauspost/compress/pull/291) * Oct 1, 2020 (v1.11.1) * zstd: Set allLitEntropy true in default configuration [#286](https://github.com/klauspost/compress/pull/286) * Sept 8, 2020 (v1.11.0) * zstd: Add experimental compression [dictionaries](https://github.com/klauspost/compress/tree/master/zstd#dictionaries) [#281](https://github.com/klauspost/compress/pull/281) * zstd: Fix mixed Write and ReadFrom calls [#282](https://github.com/klauspost/compress/pull/282) * inflate/gz: Limit variable shifts, ~5% faster decompression [#274](https://github.com/klauspost/compress/pull/274)
See changes to v1.10.x * July 8, 2020 (v1.10.11) * zstd: Fix extra block when compressing with ReadFrom. [#278](https://github.com/klauspost/compress/pull/278) * huff0: Also populate compression table when reading decoding table. [#275](https://github.com/klauspost/compress/pull/275) * June 23, 2020 (v1.10.10) * zstd: Skip entropy compression in fastest mode when no matches. [#270](https://github.com/klauspost/compress/pull/270) * June 16, 2020 (v1.10.9): * zstd: API change for specifying dictionaries. See [#268](https://github.com/klauspost/compress/pull/268) * zip: update CreateHeaderRaw to handle zip64 fields. [#266](https://github.com/klauspost/compress/pull/266) * Fuzzit tests removed. The service has been purchased and is no longer available. * June 5, 2020 (v1.10.8): * 1.15x faster zstd block decompression. [#265](https://github.com/klauspost/compress/pull/265) * June 1, 2020 (v1.10.7): * Added zstd decompression [dictionary support](https://github.com/klauspost/compress/tree/master/zstd#dictionaries) * Increase zstd decompression speed up to 1.19x. [#259](https://github.com/klauspost/compress/pull/259) * Remove internal reset call in zstd compression and reduce allocations. [#263](https://github.com/klauspost/compress/pull/263) * May 21, 2020: (v1.10.6) * zstd: Reduce allocations while decoding. [#258](https://github.com/klauspost/compress/pull/258), [#252](https://github.com/klauspost/compress/pull/252) * zstd: Stricter decompression checks. * April 12, 2020: (v1.10.5) * s2-commands: Flush output when receiving SIGINT. [#239](https://github.com/klauspost/compress/pull/239) * Apr 8, 2020: (v1.10.4) * zstd: Minor/special case optimizations. [#251](https://github.com/klauspost/compress/pull/251), [#250](https://github.com/klauspost/compress/pull/250), [#249](https://github.com/klauspost/compress/pull/249), [#247](https://github.com/klauspost/compress/pull/247) * Mar 11, 2020: (v1.10.3) * s2: Use S2 encoder in pure Go mode for Snappy output as well. [#245](https://github.com/klauspost/compress/pull/245) * s2: Fix pure Go block encoder. [#244](https://github.com/klauspost/compress/pull/244) * zstd: Added "better compression" mode. [#240](https://github.com/klauspost/compress/pull/240) * zstd: Improve speed of fastest compression mode by 5-10% [#241](https://github.com/klauspost/compress/pull/241) * zstd: Skip creating encoders when not needed. [#238](https://github.com/klauspost/compress/pull/238) * Feb 27, 2020: (v1.10.2) * Close to 50% speedup in inflate (gzip/zip decompression). [#236](https://github.com/klauspost/compress/pull/236) [#234](https://github.com/klauspost/compress/pull/234) [#232](https://github.com/klauspost/compress/pull/232) * Reduce deflate level 1-6 memory usage up to 59%. [#227](https://github.com/klauspost/compress/pull/227) * Feb 18, 2020: (v1.10.1) * Fix zstd crash when resetting multiple times without sending data. [#226](https://github.com/klauspost/compress/pull/226) * deflate: Fix dictionary use on level 1-6. [#224](https://github.com/klauspost/compress/pull/224) * Remove deflate writer reference when closing. [#224](https://github.com/klauspost/compress/pull/224) * Feb 4, 2020: (v1.10.0) * Add optional dictionary to [stateless deflate](https://pkg.go.dev/github.com/klauspost/compress/flate?tab=doc#StatelessDeflate). Breaking change, send `nil` for previous behaviour. [#216](https://github.com/klauspost/compress/pull/216) * Fix buffer overflow on repeated small block deflate. [#218](https://github.com/klauspost/compress/pull/218) * Allow copying content from an existing ZIP file without decompressing+compressing. [#214](https://github.com/klauspost/compress/pull/214) * Added [S2](https://github.com/klauspost/compress/tree/master/s2#s2-compression) AMD64 assembler and various optimizations. Stream speed >10GB/s. [#186](https://github.com/klauspost/compress/pull/186)
See changes prior to v1.10.0 * Jan 20,2020 (v1.9.8) Optimize gzip/deflate with better size estimates and faster table generation. [#207](https://github.com/klauspost/compress/pull/207) by [luyu6056](https://github.com/luyu6056), [#206](https://github.com/klauspost/compress/pull/206). * Jan 11, 2020: S2 Encode/Decode will use provided buffer if capacity is big enough. [#204](https://github.com/klauspost/compress/pull/204) * Jan 5, 2020: (v1.9.7) Fix another zstd regression in v1.9.5 - v1.9.6 removed. * Jan 4, 2020: (v1.9.6) Regression in v1.9.5 fixed causing corrupt zstd encodes in rare cases. * Jan 4, 2020: Faster IO in [s2c + s2d commandline tools](https://github.com/klauspost/compress/tree/master/s2#commandline-tools) compression/decompression. [#192](https://github.com/klauspost/compress/pull/192) * Dec 29, 2019: Removed v1.9.5 since fuzz tests showed a compatibility problem with the reference zstandard decoder. * Dec 29, 2019: (v1.9.5) zstd: 10-20% faster block compression. [#199](https://github.com/klauspost/compress/pull/199) * Dec 29, 2019: [zip](https://godoc.org/github.com/klauspost/compress/zip) package updated with latest Go features * Dec 29, 2019: zstd: Single segment flag condintions tweaked. [#197](https://github.com/klauspost/compress/pull/197) * Dec 18, 2019: s2: Faster compression when ReadFrom is used. [#198](https://github.com/klauspost/compress/pull/198) * Dec 10, 2019: s2: Fix repeat length output when just above at 16MB limit. * Dec 10, 2019: zstd: Add function to get decoder as io.ReadCloser. [#191](https://github.com/klauspost/compress/pull/191) * Dec 3, 2019: (v1.9.4) S2: limit max repeat length. [#188](https://github.com/klauspost/compress/pull/188) * Dec 3, 2019: Add [WithNoEntropyCompression](https://godoc.org/github.com/klauspost/compress/zstd#WithNoEntropyCompression) to zstd [#187](https://github.com/klauspost/compress/pull/187) * Dec 3, 2019: Reduce memory use for tests. Check for leaked goroutines. * Nov 28, 2019 (v1.9.3) Less allocations in stateless deflate. * Nov 28, 2019: 5-20% Faster huff0 decode. Impacts zstd as well. [#184](https://github.com/klauspost/compress/pull/184) * Nov 12, 2019 (v1.9.2) Added [Stateless Compression](#stateless-compression) for gzip/deflate. * Nov 12, 2019: Fixed zstd decompression of large single blocks. [#180](https://github.com/klauspost/compress/pull/180) * Nov 11, 2019: Set default [s2c](https://github.com/klauspost/compress/tree/master/s2#commandline-tools) block size to 4MB. * Nov 11, 2019: Reduce inflate memory use by 1KB. * Nov 10, 2019: Less allocations in deflate bit writer. * Nov 10, 2019: Fix inconsistent error returned by zstd decoder. * Oct 28, 2019 (v1.9.1) ztsd: Fix crash when compressing blocks. [#174](https://github.com/klauspost/compress/pull/174) * Oct 24, 2019 (v1.9.0) zstd: Fix rare data corruption [#173](https://github.com/klauspost/compress/pull/173) * Oct 24, 2019 zstd: Fix huff0 out of buffer write [#171](https://github.com/klauspost/compress/pull/171) and always return errors [#172](https://github.com/klauspost/compress/pull/172) * Oct 10, 2019: Big deflate rewrite, 30-40% faster with better compression [#105](https://github.com/klauspost/compress/pull/105)
See changes prior to v1.9.0 * Oct 10, 2019: (v1.8.6) zstd: Allow partial reads to get flushed data. [#169](https://github.com/klauspost/compress/pull/169) * Oct 3, 2019: Fix inconsistent results on broken zstd streams. * Sep 25, 2019: Added `-rm` (remove source files) and `-q` (no output except errors) to `s2c` and `s2d` [commands](https://github.com/klauspost/compress/tree/master/s2#commandline-tools) * Sep 16, 2019: (v1.8.4) Add `s2c` and `s2d` [commandline tools](https://github.com/klauspost/compress/tree/master/s2#commandline-tools). * Sep 10, 2019: (v1.8.3) Fix s2 decoder [Skip](https://godoc.org/github.com/klauspost/compress/s2#Reader.Skip). * Sep 7, 2019: zstd: Added [WithWindowSize](https://godoc.org/github.com/klauspost/compress/zstd#WithWindowSize), contributed by [ianwilkes](https://github.com/ianwilkes). * Sep 5, 2019: (v1.8.2) Add [WithZeroFrames](https://godoc.org/github.com/klauspost/compress/zstd#WithZeroFrames) which adds full zero payload block encoding option. * Sep 5, 2019: Lazy initialization of zstandard predefined en/decoder tables. * Aug 26, 2019: (v1.8.1) S2: 1-2% compression increase in "better" compression mode. * Aug 26, 2019: zstd: Check maximum size of Huffman 1X compressed literals while decoding. * Aug 24, 2019: (v1.8.0) Added [S2 compression](https://github.com/klauspost/compress/tree/master/s2#s2-compression), a high performance replacement for Snappy. * Aug 21, 2019: (v1.7.6) Fixed minor issues found by fuzzer. One could lead to zstd not decompressing. * Aug 18, 2019: Add [fuzzit](https://fuzzit.dev/) continuous fuzzing. * Aug 14, 2019: zstd: Skip incompressible data 2x faster. [#147](https://github.com/klauspost/compress/pull/147) * Aug 4, 2019 (v1.7.5): Better literal compression. [#146](https://github.com/klauspost/compress/pull/146) * Aug 4, 2019: Faster zstd compression. [#143](https://github.com/klauspost/compress/pull/143) [#144](https://github.com/klauspost/compress/pull/144) * Aug 4, 2019: Faster zstd decompression. [#145](https://github.com/klauspost/compress/pull/145) [#143](https://github.com/klauspost/compress/pull/143) [#142](https://github.com/klauspost/compress/pull/142) * July 15, 2019 (v1.7.4): Fix double EOF block in rare cases on zstd encoder. * July 15, 2019 (v1.7.3): Minor speedup/compression increase in default zstd encoder. * July 14, 2019: zstd decoder: Fix decompression error on multiple uses with mixed content. * July 7, 2019 (v1.7.2): Snappy update, zstd decoder potential race fix. * June 17, 2019: zstd decompression bugfix. * June 17, 2019: fix 32 bit builds. * June 17, 2019: Easier use in modules (less dependencies). * June 9, 2019: New stronger "default" [zstd](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression mode. Matches zstd default compression ratio. * June 5, 2019: 20-40% throughput in [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and better compression. * June 5, 2019: deflate/gzip compression: Reduce memory usage of lower compression levels. * June 2, 2019: Added [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression! * May 25, 2019: deflate/gzip: 10% faster bit writer, mostly visible in lower levels. * Apr 22, 2019: [zstd](https://github.com/klauspost/compress/tree/master/zstd#zstd) decompression added. * Aug 1, 2018: Added [huff0 README](https://github.com/klauspost/compress/tree/master/huff0#huff0-entropy-compression). * Jul 8, 2018: Added [Performance Update 2018](#performance-update-2018) below. * Jun 23, 2018: Merged [Go 1.11 inflate optimizations](https://go-review.googlesource.com/c/go/+/102235). Go 1.9 is now required. Backwards compatible version tagged with [v1.3.0](https://github.com/klauspost/compress/releases/tag/v1.3.0). * Apr 2, 2018: Added [huff0](https://godoc.org/github.com/klauspost/compress/huff0) en/decoder. Experimental for now, API may change. * Mar 4, 2018: Added [FSE Entropy](https://godoc.org/github.com/klauspost/compress/fse) en/decoder. Experimental for now, API may change. * Nov 3, 2017: Add compression [Estimate](https://godoc.org/github.com/klauspost/compress#Estimate) function. * May 28, 2017: Reduce allocations when resetting decoder. * Apr 02, 2017: Change back to official crc32, since changes were merged in Go 1.7. * Jan 14, 2017: Reduce stack pressure due to array copies. See [Issue #18625](https://github.com/golang/go/issues/18625). * Oct 25, 2016: Level 2-4 have been rewritten and now offers significantly better performance than before. * Oct 20, 2016: Port zlib changes from Go 1.7 to fix zlib writer issue. Please update. * Oct 16, 2016: Go 1.7 changes merged. Apples to apples this package is a few percent faster, but has a significantly better balance between speed and compression per level. * Mar 24, 2016: Always attempt Huffman encoding on level 4-7. This improves base 64 encoded data compression. * Mar 24, 2016: Small speedup for level 1-3. * Feb 19, 2016: Faster bit writer, level -2 is 15% faster, level 1 is 4% faster. * Feb 19, 2016: Handle small payloads faster in level 1-3. * Feb 19, 2016: Added faster level 2 + 3 compression modes. * Feb 19, 2016: [Rebalanced compression levels](https://blog.klauspost.com/rebalancing-deflate-compression-levels/), so there is a more even progresssion in terms of compression. New default level is 5. * Feb 14, 2016: Snappy: Merge upstream changes. * Feb 14, 2016: Snappy: Fix aggressive skipping. * Feb 14, 2016: Snappy: Update benchmark. * Feb 13, 2016: Deflate: Fixed assembler problem that could lead to sub-optimal compression. * Feb 12, 2016: Snappy: Added AMD64 SSE 4.2 optimizations to matching, which makes easy to compress material run faster. Typical speedup is around 25%. * Feb 9, 2016: Added Snappy package fork. This version is 5-7% faster, much more on hard to compress content. * Jan 30, 2016: Optimize level 1 to 3 by not considering static dictionary or storing uncompressed. ~4-5% speedup. * Jan 16, 2016: Optimization on deflate level 1,2,3 compression. * Jan 8 2016: Merge [CL 18317](https://go-review.googlesource.com/#/c/18317): fix reading, writing of zip64 archives. * Dec 8 2015: Make level 1 and -2 deterministic even if write size differs. * Dec 8 2015: Split encoding functions, so hashing and matching can potentially be inlined. 1-3% faster on AMD64. 5% faster on other platforms. * Dec 8 2015: Fixed rare [one byte out-of bounds read](https://github.com/klauspost/compress/issues/20). Please update! * Nov 23 2015: Optimization on token writer. ~2-4% faster. Contributed by [@dsnet](https://github.com/dsnet). * Nov 20 2015: Small optimization to bit writer on 64 bit systems. * Nov 17 2015: Fixed out-of-bound errors if the underlying Writer returned an error. See [#15](https://github.com/klauspost/compress/issues/15). * Nov 12 2015: Added [io.WriterTo](https://golang.org/pkg/io/#WriterTo) support to gzip/inflate. * Nov 11 2015: Merged [CL 16669](https://go-review.googlesource.com/#/c/16669/4): archive/zip: enable overriding (de)compressors per file * Oct 15 2015: Added skipping on uncompressible data. Random data speed up >5x.
# deflate usage The packages are drop-in replacements for standard libraries. Simply replace the import path to use them: | old import | new import | Documentation |--------------------|-----------------------------------------|--------------------| | `compress/gzip` | `github.com/klauspost/compress/gzip` | [gzip](https://pkg.go.dev/github.com/klauspost/compress/gzip?tab=doc) | `compress/zlib` | `github.com/klauspost/compress/zlib` | [zlib](https://pkg.go.dev/github.com/klauspost/compress/zlib?tab=doc) | `archive/zip` | `github.com/klauspost/compress/zip` | [zip](https://pkg.go.dev/github.com/klauspost/compress/zip?tab=doc) | `compress/flate` | `github.com/klauspost/compress/flate` | [flate](https://pkg.go.dev/github.com/klauspost/compress/flate?tab=doc) * Optimized [deflate](https://godoc.org/github.com/klauspost/compress/flate) packages which can be used as a dropin replacement for [gzip](https://godoc.org/github.com/klauspost/compress/gzip), [zip](https://godoc.org/github.com/klauspost/compress/zip) and [zlib](https://godoc.org/github.com/klauspost/compress/zlib). You may also be interested in [pgzip](https://github.com/klauspost/pgzip), which is a drop in replacement for gzip, which support multithreaded compression on big files and the optimized [crc32](https://github.com/klauspost/crc32) package used by these packages. The packages contains the same as the standard library, so you can use the godoc for that: [gzip](http://golang.org/pkg/compress/gzip/), [zip](http://golang.org/pkg/archive/zip/), [zlib](http://golang.org/pkg/compress/zlib/), [flate](http://golang.org/pkg/compress/flate/). Currently there is only minor speedup on decompression (mostly CRC32 calculation). Memory usage is typically 1MB for a Writer. stdlib is in the same range. If you expect to have a lot of concurrently allocated Writers consider using the stateless compress described below. For compression performance, see: [this spreadsheet](https://docs.google.com/spreadsheets/d/1nuNE2nPfuINCZJRMt6wFWhKpToF95I47XjSsc-1rbPQ/edit?usp=sharing). # Stateless compression This package offers stateless compression as a special option for gzip/deflate. It will do compression but without maintaining any state between Write calls. This means there will be no memory kept between Write calls, but compression and speed will be suboptimal. This is only relevant in cases where you expect to run many thousands of compressors concurrently, but with very little activity. This is *not* intended for regular web servers serving individual requests. Because of this, the size of actual Write calls will affect output size. In gzip, specify level `-3` / `gzip.StatelessCompression` to enable. For direct deflate use, NewStatelessWriter and StatelessDeflate are available. See [documentation](https://godoc.org/github.com/klauspost/compress/flate#NewStatelessWriter) A `bufio.Writer` can of course be used to control write sizes. For example, to use a 4KB buffer: ``` // replace 'ioutil.Discard' with your output. gzw, err := gzip.NewWriterLevel(ioutil.Discard, gzip.StatelessCompression) if err != nil { return err } defer gzw.Close() w := bufio.NewWriterSize(gzw, 4096) defer w.Flush() // Write to 'w' ``` This will only use up to 4KB in memory when the writer is idle. Compression is almost always worse than the fastest compression level and each write will allocate (a little) memory. # Performance Update 2018 It has been a while since we have been looking at the speed of this package compared to the standard library, so I thought I would re-do my tests and give some overall recommendations based on the current state. All benchmarks have been performed with Go 1.10 on my Desktop Intel(R) Core(TM) i7-2600 CPU @3.40GHz. Since I last ran the tests, I have gotten more RAM, which means tests with big files are no longer limited by my SSD. The raw results are in my [updated spreadsheet](https://docs.google.com/spreadsheets/d/1nuNE2nPfuINCZJRMt6wFWhKpToF95I47XjSsc-1rbPQ/edit?usp=sharing). Due to cgo changes and upstream updates i could not get the cgo version of gzip to compile. Instead I included the [zstd](https://github.com/datadog/zstd) cgo implementation. If I get cgo gzip to work again, I might replace the results in the sheet. The columns to take note of are: *MB/s* - the throughput. *Reduction* - the data size reduction in percent of the original. *Rel Speed* relative speed compared to the standard library at the same level. *Smaller* - how many percent smaller is the compressed output compared to stdlib. Negative means the output was bigger. *Loss* means the loss (or gain) in compression as a percentage difference of the input. The `gzstd` (standard library gzip) and `gzkp` (this package gzip) only uses one CPU core. [`pgzip`](https://github.com/klauspost/pgzip), [`bgzf`](https://github.com/biogo/hts/tree/master/bgzf) uses all 4 cores. [`zstd`](https://github.com/DataDog/zstd) uses one core, and is a beast (but not Go, yet). ## Overall differences. There appears to be a roughly 5-10% speed advantage over the standard library when comparing at similar compression levels. The biggest difference you will see is the result of [re-balancing](https://blog.klauspost.com/rebalancing-deflate-compression-levels/) the compression levels. I wanted by library to give a smoother transition between the compression levels than the standard library. This package attempts to provide a more smooth transition, where "1" is taking a lot of shortcuts, "5" is the reasonable trade-off and "9" is the "give me the best compression", and the values in between gives something reasonable in between. The standard library has big differences in levels 1-4, but levels 5-9 having no significant gains - often spending a lot more time than can be justified by the achieved compression. There are links to all the test data in the [spreadsheet](https://docs.google.com/spreadsheets/d/1nuNE2nPfuINCZJRMt6wFWhKpToF95I47XjSsc-1rbPQ/edit?usp=sharing) in the top left field on each tab. ## Web Content This test set aims to emulate typical use in a web server. The test-set is 4GB data in 53k files, and is a mixture of (mostly) HTML, JS, CSS. Since level 1 and 9 are close to being the same code, they are quite close. But looking at the levels in-between the differences are quite big. Looking at level 6, this package is 88% faster, but will output about 6% more data. For a web server, this means you can serve 88% more data, but have to pay for 6% more bandwidth. You can draw your own conclusions on what would be the most expensive for your case. ## Object files This test is for typical data files stored on a server. In this case it is a collection of Go precompiled objects. They are very compressible. The picture is similar to the web content, but with small differences since this is very compressible. Levels 2-3 offer good speed, but is sacrificing quite a bit of compression. The standard library seems suboptimal on level 3 and 4 - offering both worse compression and speed than level 6 & 7 of this package respectively. ## Highly Compressible File This is a JSON file with very high redundancy. The reduction starts at 95% on level 1, so in real life terms we are dealing with something like a highly redundant stream of data, etc. It is definitely visible that we are dealing with specialized content here, so the results are very scattered. This package does not do very well at levels 1-4, but picks up significantly at level 5 and levels 7 and 8 offering great speed for the achieved compression. So if you know you content is extremely compressible you might want to go slightly higher than the defaults. The standard library has a huge gap between levels 3 and 4 in terms of speed (2.75x slowdown), so it offers little "middle ground". ## Medium-High Compressible This is a pretty common test corpus: [enwik9](http://mattmahoney.net/dc/textdata.html). It contains the first 10^9 bytes of the English Wikipedia dump on Mar. 3, 2006. This is a very good test of typical text based compression and more data heavy streams. We see a similar picture here as in "Web Content". On equal levels some compression is sacrificed for more speed. Level 5 seems to be the best trade-off between speed and size, beating stdlib level 3 in both. ## Medium Compressible I will combine two test sets, one [10GB file set](http://mattmahoney.net/dc/10gb.html) and a VM disk image (~8GB). Both contain different data types and represent a typical backup scenario. The most notable thing is how quickly the standard library drops to very low compression speeds around level 5-6 without any big gains in compression. Since this type of data is fairly common, this does not seem like good behavior. ## Un-compressible Content This is mainly a test of how good the algorithms are at detecting un-compressible input. The standard library only offers this feature with very conservative settings at level 1. Obviously there is no reason for the algorithms to try to compress input that cannot be compressed. The only downside is that it might skip some compressible data on false detections. ## Huffman only compression This compression library adds a special compression level, named `HuffmanOnly`, which allows near linear time compression. This is done by completely disabling matching of previous data, and only reduce the number of bits to represent each character. This means that often used characters, like 'e' and ' ' (space) in text use the fewest bits to represent, and rare characters like '¤' takes more bits to represent. For more information see [wikipedia](https://en.wikipedia.org/wiki/Huffman_coding) or this nice [video](https://youtu.be/ZdooBTdW5bM). Since this type of compression has much less variance, the compression speed is mostly unaffected by the input data, and is usually more than *180MB/s* for a single core. The downside is that the compression ratio is usually considerably worse than even the fastest conventional compression. The compression ratio can never be better than 8:1 (12.5%). The linear time compression can be used as a "better than nothing" mode, where you cannot risk the encoder to slow down on some content. For comparison, the size of the "Twain" text is *233460 bytes* (+29% vs. level 1) and encode speed is 144MB/s (4.5x level 1). So in this case you trade a 30% size increase for a 4 times speedup. For more information see my blog post on [Fast Linear Time Compression](http://blog.klauspost.com/constant-time-gzipzip-compression/). This is implemented on Go 1.7 as "Huffman Only" mode, though not exposed for gzip. # Other packages Here are other packages of good quality and pure Go (no cgo wrappers or autoconverted code): * [github.com/pierrec/lz4](https://github.com/pierrec/lz4) - strong multithreaded LZ4 compression. * [github.com/cosnicolaou/pbzip2](https://github.com/cosnicolaou/pbzip2) - multithreaded bzip2 decompression. * [github.com/dsnet/compress](https://github.com/dsnet/compress) - brotli decompression, bzip2 writer. * [github.com/ronanh/intcomp](https://github.com/ronanh/intcomp) - Integer compression. * [github.com/spenczar/fpc](https://github.com/spenczar/fpc) - Float compression. * [github.com/minio/zipindex](https://github.com/minio/zipindex) - External ZIP directory index. * [github.com/ybirader/pzip](https://github.com/ybirader/pzip) - Fast concurrent zip archiver and extractor. # license This code is licensed under the same conditions as the original Go code. See LICENSE file. ================================================ FILE: vendor/github.com/klauspost/compress/SECURITY.md ================================================ # Security Policy ## Supported Versions Security updates are applied only to the latest release. ## Vulnerability Definition A security vulnerability is a bug that with certain input triggers a crash or an infinite loop. Most calls will have varying execution time and only in rare cases will slow operation be considered a security vulnerability. Corrupted output generally is not considered a security vulnerability, unless independent operations are able to affect each other. Note that not all functionality is re-entrant and safe to use concurrently. Out-of-memory crashes only applies if the en/decoder uses an abnormal amount of memory, with appropriate options applied, to limit maximum window size, concurrency, etc. However, if you are in doubt you are welcome to file a security issue. It is assumed that all callers are trusted, meaning internal data exposed through reflection or inspection of returned data structures is not considered a vulnerability. Vulnerabilities resulting from compiler/assembler errors should be reported upstream. Depending on the severity this package may or may not implement a workaround. ## Reporting a Vulnerability If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released. Please disclose it at [security advisory](https://github.com/klauspost/compress/security/advisories/new). If possible please provide a minimal reproducer. If the issue only applies to a single platform, it would be helpful to provide access to that. This project is maintained by a team of volunteers on a reasonable-effort basis. As such, vulnerabilities will be disclosed in a best effort base. ================================================ FILE: vendor/github.com/klauspost/compress/compressible.go ================================================ package compress import "math" // Estimate returns a normalized compressibility estimate of block b. // Values close to zero are likely uncompressible. // Values above 0.1 are likely to be compressible. // Values above 0.5 are very compressible. // Very small lengths will return 0. func Estimate(b []byte) float64 { if len(b) < 16 { return 0 } // Correctly predicted order 1 hits := 0 lastMatch := false var o1 [256]byte var hist [256]int c1 := byte(0) for _, c := range b { if c == o1[c1] { // We only count a hit if there was two correct predictions in a row. if lastMatch { hits++ } lastMatch = true } else { lastMatch = false } o1[c1] = c c1 = c hist[c]++ } // Use x^0.6 to give better spread prediction := math.Pow(float64(hits)/float64(len(b)), 0.6) // Calculate histogram distribution variance := float64(0) avg := float64(len(b)) / 256 for _, v := range hist { Δ := float64(v) - avg variance += Δ * Δ } stddev := math.Sqrt(float64(variance)) / float64(len(b)) exp := math.Sqrt(1 / float64(len(b))) // Subtract expected stddev stddev -= exp if stddev < 0 { stddev = 0 } stddev *= 1 + exp // Use x^0.4 to give better spread entropy := math.Pow(stddev, 0.4) // 50/50 weight between prediction and histogram distribution return math.Pow((prediction+entropy)/2, 0.9) } // ShannonEntropyBits returns the number of bits minimum required to represent // an entropy encoding of the input bytes. // https://en.wiktionary.org/wiki/Shannon_entropy func ShannonEntropyBits(b []byte) int { if len(b) == 0 { return 0 } var hist [256]int for _, c := range b { hist[c]++ } shannon := float64(0) invTotal := 1.0 / float64(len(b)) for _, v := range hist[:] { if v > 0 { n := float64(v) shannon += math.Ceil(-math.Log2(n*invTotal) * n) } } return int(math.Ceil(shannon)) } ================================================ FILE: vendor/github.com/klauspost/compress/flate/deflate.go ================================================ // Copyright 2009 The Go Authors. All rights reserved. // Copyright (c) 2015 Klaus Post // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package flate import ( "encoding/binary" "errors" "fmt" "io" "math" ) const ( NoCompression = 0 BestSpeed = 1 BestCompression = 9 DefaultCompression = -1 // HuffmanOnly disables Lempel-Ziv match searching and only performs Huffman // entropy encoding. This mode is useful in compressing data that has // already been compressed with an LZ style algorithm (e.g. Snappy or LZ4) // that lacks an entropy encoder. Compression gains are achieved when // certain bytes in the input stream occur more frequently than others. // // Note that HuffmanOnly produces a compressed output that is // RFC 1951 compliant. That is, any valid DEFLATE decompressor will // continue to be able to decompress this output. HuffmanOnly = -2 ConstantCompression = HuffmanOnly // compatibility alias. logWindowSize = 15 windowSize = 1 << logWindowSize windowMask = windowSize - 1 logMaxOffsetSize = 15 // Standard DEFLATE minMatchLength = 4 // The smallest match that the compressor looks for maxMatchLength = 258 // The longest match for the compressor minOffsetSize = 1 // The shortest offset that makes any sense // The maximum number of tokens we will encode at the time. // Smaller sizes usually creates less optimal blocks. // Bigger can make context switching slow. // We use this for levels 7-9, so we make it big. maxFlateBlockTokens = 1 << 15 maxStoreBlockSize = 65535 hashBits = 17 // After 17 performance degrades hashSize = 1 << hashBits hashMask = (1 << hashBits) - 1 hashShift = (hashBits + minMatchLength - 1) / minMatchLength maxHashOffset = 1 << 28 skipNever = math.MaxInt32 debugDeflate = false ) type compressionLevel struct { good, lazy, nice, chain, fastSkipHashing, level int } // Compression levels have been rebalanced from zlib deflate defaults // to give a bigger spread in speed and compression. // See https://blog.klauspost.com/rebalancing-deflate-compression-levels/ var levels = []compressionLevel{ {}, // 0 // Level 1-6 uses specialized algorithm - values not used {0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 2}, {0, 0, 0, 0, 0, 3}, {0, 0, 0, 0, 0, 4}, {0, 0, 0, 0, 0, 5}, {0, 0, 0, 0, 0, 6}, // Levels 7-9 use increasingly more lazy matching // and increasingly stringent conditions for "good enough". {8, 12, 16, 24, skipNever, 7}, {16, 30, 40, 64, skipNever, 8}, {32, 258, 258, 1024, skipNever, 9}, } // advancedState contains state for the advanced levels, with bigger hash tables, etc. type advancedState struct { // deflate state length int offset int maxInsertIndex int chainHead int hashOffset int ii uint16 // position of last match, intended to overflow to reset. // input window: unprocessed data is window[index:windowEnd] index int hashMatch [maxMatchLength + minMatchLength]uint32 // Input hash chains // hashHead[hashValue] contains the largest inputIndex with the specified hash value // If hashHead[hashValue] is within the current window, then // hashPrev[hashHead[hashValue] & windowMask] contains the previous index // with the same hash value. hashHead [hashSize]uint32 hashPrev [windowSize]uint32 } type compressor struct { compressionLevel h *huffmanEncoder w *huffmanBitWriter // compression algorithm fill func(*compressor, []byte) int // copy data to window step func(*compressor) // process window window []byte windowEnd int blockStart int // window index where current tokens start err error // queued output tokens tokens tokens fast fastEnc state *advancedState sync bool // requesting flush byteAvailable bool // if true, still need to process window[index-1]. } func (d *compressor) fillDeflate(b []byte) int { s := d.state if s.index >= 2*windowSize-(minMatchLength+maxMatchLength) { // shift the window by windowSize //copy(d.window[:], d.window[windowSize:2*windowSize]) *(*[windowSize]byte)(d.window) = *(*[windowSize]byte)(d.window[windowSize:]) s.index -= windowSize d.windowEnd -= windowSize if d.blockStart >= windowSize { d.blockStart -= windowSize } else { d.blockStart = math.MaxInt32 } s.hashOffset += windowSize if s.hashOffset > maxHashOffset { delta := s.hashOffset - 1 s.hashOffset -= delta s.chainHead -= delta // Iterate over slices instead of arrays to avoid copying // the entire table onto the stack (Issue #18625). for i, v := range s.hashPrev[:] { if int(v) > delta { s.hashPrev[i] = uint32(int(v) - delta) } else { s.hashPrev[i] = 0 } } for i, v := range s.hashHead[:] { if int(v) > delta { s.hashHead[i] = uint32(int(v) - delta) } else { s.hashHead[i] = 0 } } } } n := copy(d.window[d.windowEnd:], b) d.windowEnd += n return n } func (d *compressor) writeBlock(tok *tokens, index int, eof bool) error { if index > 0 || eof { var window []byte if d.blockStart <= index { window = d.window[d.blockStart:index] } d.blockStart = index //d.w.writeBlock(tok, eof, window) d.w.writeBlockDynamic(tok, eof, window, d.sync) return d.w.err } return nil } // writeBlockSkip writes the current block and uses the number of tokens // to determine if the block should be stored on no matches, or // only huffman encoded. func (d *compressor) writeBlockSkip(tok *tokens, index int, eof bool) error { if index > 0 || eof { if d.blockStart <= index { window := d.window[d.blockStart:index] // If we removed less than a 64th of all literals // we huffman compress the block. if int(tok.n) > len(window)-int(tok.n>>6) { d.w.writeBlockHuff(eof, window, d.sync) } else { // Write a dynamic huffman block. d.w.writeBlockDynamic(tok, eof, window, d.sync) } } else { d.w.writeBlock(tok, eof, nil) } d.blockStart = index return d.w.err } return nil } // fillWindow will fill the current window with the supplied // dictionary and calculate all hashes. // This is much faster than doing a full encode. // Should only be used after a start/reset. func (d *compressor) fillWindow(b []byte) { // Do not fill window if we are in store-only or huffman mode. if d.level <= 0 { return } if d.fast != nil { // encode the last data, but discard the result if len(b) > maxMatchOffset { b = b[len(b)-maxMatchOffset:] } d.fast.Encode(&d.tokens, b) d.tokens.Reset() return } s := d.state // If we are given too much, cut it. if len(b) > windowSize { b = b[len(b)-windowSize:] } // Add all to window. n := copy(d.window[d.windowEnd:], b) // Calculate 256 hashes at the time (more L1 cache hits) loops := (n + 256 - minMatchLength) / 256 for j := 0; j < loops; j++ { startindex := j * 256 end := startindex + 256 + minMatchLength - 1 if end > n { end = n } tocheck := d.window[startindex:end] dstSize := len(tocheck) - minMatchLength + 1 if dstSize <= 0 { continue } dst := s.hashMatch[:dstSize] bulkHash4(tocheck, dst) var newH uint32 for i, val := range dst { di := i + startindex newH = val & hashMask // Get previous value with the same hash. // Our chain should point to the previous value. s.hashPrev[di&windowMask] = s.hashHead[newH] // Set the head of the hash chain to us. s.hashHead[newH] = uint32(di + s.hashOffset) } } // Update window information. d.windowEnd += n s.index = n } // Try to find a match starting at index whose length is greater than prevSize. // We only look at chainCount possibilities before giving up. // pos = s.index, prevHead = s.chainHead-s.hashOffset, prevLength=minMatchLength-1, lookahead func (d *compressor) findMatch(pos int, prevHead int, lookahead int) (length, offset int, ok bool) { minMatchLook := maxMatchLength if lookahead < minMatchLook { minMatchLook = lookahead } win := d.window[0 : pos+minMatchLook] // We quit when we get a match that's at least nice long nice := len(win) - pos if d.nice < nice { nice = d.nice } // If we've got a match that's good enough, only look in 1/4 the chain. tries := d.chain length = minMatchLength - 1 wEnd := win[pos+length] wPos := win[pos:] minIndex := pos - windowSize if minIndex < 0 { minIndex = 0 } offset = 0 if d.chain < 100 { for i := prevHead; tries > 0; tries-- { if wEnd == win[i+length] { n := matchLen(win[i:i+minMatchLook], wPos) if n > length { length = n offset = pos - i ok = true if n >= nice { // The match is good enough that we don't try to find a better one. break } wEnd = win[pos+n] } } if i <= minIndex { // hashPrev[i & windowMask] has already been overwritten, so stop now. break } i = int(d.state.hashPrev[i&windowMask]) - d.state.hashOffset if i < minIndex { break } } return } // Minimum gain to accept a match. cGain := 4 // Some like it higher (CSV), some like it lower (JSON) const baseCost = 3 // Base is 4 bytes at with an additional cost. // Matches must be better than this. for i := prevHead; tries > 0; tries-- { if wEnd == win[i+length] { n := matchLen(win[i:i+minMatchLook], wPos) if n > length { // Calculate gain. Estimate newGain := d.h.bitLengthRaw(wPos[:n]) - int(offsetExtraBits[offsetCode(uint32(pos-i))]) - baseCost - int(lengthExtraBits[lengthCodes[(n-3)&255]]) //fmt.Println("gain:", newGain, "prev:", cGain, "raw:", d.h.bitLengthRaw(wPos[:n]), "this-len:", n, "prev-len:", length) if newGain > cGain { length = n offset = pos - i cGain = newGain ok = true if n >= nice { // The match is good enough that we don't try to find a better one. break } wEnd = win[pos+n] } } } if i <= minIndex { // hashPrev[i & windowMask] has already been overwritten, so stop now. break } i = int(d.state.hashPrev[i&windowMask]) - d.state.hashOffset if i < minIndex { break } } return } func (d *compressor) writeStoredBlock(buf []byte) error { if d.w.writeStoredHeader(len(buf), false); d.w.err != nil { return d.w.err } d.w.writeBytes(buf) return d.w.err } // hash4 returns a hash representation of the first 4 bytes // of the supplied slice. // The caller must ensure that len(b) >= 4. func hash4(b []byte) uint32 { return hash4u(binary.LittleEndian.Uint32(b), hashBits) } // hash4 returns the hash of u to fit in a hash table with h bits. // Preferably h should be a constant and should always be <32. func hash4u(u uint32, h uint8) uint32 { return (u * prime4bytes) >> (32 - h) } // bulkHash4 will compute hashes using the same // algorithm as hash4 func bulkHash4(b []byte, dst []uint32) { if len(b) < 4 { return } hb := binary.LittleEndian.Uint32(b) dst[0] = hash4u(hb, hashBits) end := len(b) - 4 + 1 for i := 1; i < end; i++ { hb = (hb >> 8) | uint32(b[i+3])<<24 dst[i] = hash4u(hb, hashBits) } } func (d *compressor) initDeflate() { d.window = make([]byte, 2*windowSize) d.byteAvailable = false d.err = nil if d.state == nil { return } s := d.state s.index = 0 s.hashOffset = 1 s.length = minMatchLength - 1 s.offset = 0 s.chainHead = -1 } // deflateLazy is the same as deflate, but with d.fastSkipHashing == skipNever, // meaning it always has lazy matching on. func (d *compressor) deflateLazy() { s := d.state // Sanity enables additional runtime tests. // It's intended to be used during development // to supplement the currently ad-hoc unit tests. const sanity = debugDeflate if d.windowEnd-s.index < minMatchLength+maxMatchLength && !d.sync { return } if d.windowEnd != s.index && d.chain > 100 { // Get literal huffman coder. if d.h == nil { d.h = newHuffmanEncoder(maxFlateBlockTokens) } var tmp [256]uint16 for _, v := range d.window[s.index:d.windowEnd] { tmp[v]++ } d.h.generate(tmp[:], 15) } s.maxInsertIndex = d.windowEnd - (minMatchLength - 1) for { if sanity && s.index > d.windowEnd { panic("index > windowEnd") } lookahead := d.windowEnd - s.index if lookahead < minMatchLength+maxMatchLength { if !d.sync { return } if sanity && s.index > d.windowEnd { panic("index > windowEnd") } if lookahead == 0 { // Flush current output block if any. if d.byteAvailable { // There is still one pending token that needs to be flushed d.tokens.AddLiteral(d.window[s.index-1]) d.byteAvailable = false } if d.tokens.n > 0 { if d.err = d.writeBlock(&d.tokens, s.index, false); d.err != nil { return } d.tokens.Reset() } return } } if s.index < s.maxInsertIndex { // Update the hash hash := hash4(d.window[s.index:]) ch := s.hashHead[hash] s.chainHead = int(ch) s.hashPrev[s.index&windowMask] = ch s.hashHead[hash] = uint32(s.index + s.hashOffset) } prevLength := s.length prevOffset := s.offset s.length = minMatchLength - 1 s.offset = 0 minIndex := s.index - windowSize if minIndex < 0 { minIndex = 0 } if s.chainHead-s.hashOffset >= minIndex && lookahead > prevLength && prevLength < d.lazy { if newLength, newOffset, ok := d.findMatch(s.index, s.chainHead-s.hashOffset, lookahead); ok { s.length = newLength s.offset = newOffset } } if prevLength >= minMatchLength && s.length <= prevLength { // No better match, but check for better match at end... // // Skip forward a number of bytes. // Offset of 2 seems to yield best results. 3 is sometimes better. const checkOff = 2 // Check all, except full length if prevLength < maxMatchLength-checkOff { prevIndex := s.index - 1 if prevIndex+prevLength < s.maxInsertIndex { end := lookahead if lookahead > maxMatchLength+checkOff { end = maxMatchLength + checkOff } end += prevIndex // Hash at match end. h := hash4(d.window[prevIndex+prevLength:]) ch2 := int(s.hashHead[h]) - s.hashOffset - prevLength if prevIndex-ch2 != prevOffset && ch2 > minIndex+checkOff { length := matchLen(d.window[prevIndex+checkOff:end], d.window[ch2+checkOff:]) // It seems like a pure length metric is best. if length > prevLength { prevLength = length prevOffset = prevIndex - ch2 // Extend back... for i := checkOff - 1; i >= 0; i-- { if prevLength >= maxMatchLength || d.window[prevIndex+i] != d.window[ch2+i] { // Emit tokens we "owe" for j := 0; j <= i; j++ { d.tokens.AddLiteral(d.window[prevIndex+j]) if d.tokens.n == maxFlateBlockTokens { // The block includes the current character if d.err = d.writeBlock(&d.tokens, s.index, false); d.err != nil { return } d.tokens.Reset() } s.index++ if s.index < s.maxInsertIndex { h := hash4(d.window[s.index:]) ch := s.hashHead[h] s.chainHead = int(ch) s.hashPrev[s.index&windowMask] = ch s.hashHead[h] = uint32(s.index + s.hashOffset) } } break } else { prevLength++ } } } else if false { // Check one further ahead. // Only rarely better, disabled for now. prevIndex++ h := hash4(d.window[prevIndex+prevLength:]) ch2 := int(s.hashHead[h]) - s.hashOffset - prevLength if prevIndex-ch2 != prevOffset && ch2 > minIndex+checkOff { length := matchLen(d.window[prevIndex+checkOff:end], d.window[ch2+checkOff:]) // It seems like a pure length metric is best. if length > prevLength+checkOff { prevLength = length prevOffset = prevIndex - ch2 prevIndex-- // Extend back... for i := checkOff; i >= 0; i-- { if prevLength >= maxMatchLength || d.window[prevIndex+i] != d.window[ch2+i-1] { // Emit tokens we "owe" for j := 0; j <= i; j++ { d.tokens.AddLiteral(d.window[prevIndex+j]) if d.tokens.n == maxFlateBlockTokens { // The block includes the current character if d.err = d.writeBlock(&d.tokens, s.index, false); d.err != nil { return } d.tokens.Reset() } s.index++ if s.index < s.maxInsertIndex { h := hash4(d.window[s.index:]) ch := s.hashHead[h] s.chainHead = int(ch) s.hashPrev[s.index&windowMask] = ch s.hashHead[h] = uint32(s.index + s.hashOffset) } } break } else { prevLength++ } } } } } } } } // There was a match at the previous step, and the current match is // not better. Output the previous match. d.tokens.AddMatch(uint32(prevLength-3), uint32(prevOffset-minOffsetSize)) // Insert in the hash table all strings up to the end of the match. // index and index-1 are already inserted. If there is not enough // lookahead, the last two strings are not inserted into the hash // table. newIndex := s.index + prevLength - 1 // Calculate missing hashes end := newIndex if end > s.maxInsertIndex { end = s.maxInsertIndex } end += minMatchLength - 1 startindex := s.index + 1 if startindex > s.maxInsertIndex { startindex = s.maxInsertIndex } tocheck := d.window[startindex:end] dstSize := len(tocheck) - minMatchLength + 1 if dstSize > 0 { dst := s.hashMatch[:dstSize] bulkHash4(tocheck, dst) var newH uint32 for i, val := range dst { di := i + startindex newH = val & hashMask // Get previous value with the same hash. // Our chain should point to the previous value. s.hashPrev[di&windowMask] = s.hashHead[newH] // Set the head of the hash chain to us. s.hashHead[newH] = uint32(di + s.hashOffset) } } s.index = newIndex d.byteAvailable = false s.length = minMatchLength - 1 if d.tokens.n == maxFlateBlockTokens { // The block includes the current character if d.err = d.writeBlock(&d.tokens, s.index, false); d.err != nil { return } d.tokens.Reset() } s.ii = 0 } else { // Reset, if we got a match this run. if s.length >= minMatchLength { s.ii = 0 } // We have a byte waiting. Emit it. if d.byteAvailable { s.ii++ d.tokens.AddLiteral(d.window[s.index-1]) if d.tokens.n == maxFlateBlockTokens { if d.err = d.writeBlock(&d.tokens, s.index, false); d.err != nil { return } d.tokens.Reset() } s.index++ // If we have a long run of no matches, skip additional bytes // Resets when s.ii overflows after 64KB. if n := int(s.ii) - d.chain; n > 0 { n = 1 + int(n>>6) for j := 0; j < n; j++ { if s.index >= d.windowEnd-1 { break } d.tokens.AddLiteral(d.window[s.index-1]) if d.tokens.n == maxFlateBlockTokens { if d.err = d.writeBlock(&d.tokens, s.index, false); d.err != nil { return } d.tokens.Reset() } // Index... if s.index < s.maxInsertIndex { h := hash4(d.window[s.index:]) ch := s.hashHead[h] s.chainHead = int(ch) s.hashPrev[s.index&windowMask] = ch s.hashHead[h] = uint32(s.index + s.hashOffset) } s.index++ } // Flush last byte d.tokens.AddLiteral(d.window[s.index-1]) d.byteAvailable = false // s.length = minMatchLength - 1 // not needed, since s.ii is reset above, so it should never be > minMatchLength if d.tokens.n == maxFlateBlockTokens { if d.err = d.writeBlock(&d.tokens, s.index, false); d.err != nil { return } d.tokens.Reset() } } } else { s.index++ d.byteAvailable = true } } } } func (d *compressor) store() { if d.windowEnd > 0 && (d.windowEnd == maxStoreBlockSize || d.sync) { d.err = d.writeStoredBlock(d.window[:d.windowEnd]) d.windowEnd = 0 } } // fillWindow will fill the buffer with data for huffman-only compression. // The number of bytes copied is returned. func (d *compressor) fillBlock(b []byte) int { n := copy(d.window[d.windowEnd:], b) d.windowEnd += n return n } // storeHuff will compress and store the currently added data, // if enough has been accumulated or we at the end of the stream. // Any error that occurred will be in d.err func (d *compressor) storeHuff() { if d.windowEnd < len(d.window) && !d.sync || d.windowEnd == 0 { return } d.w.writeBlockHuff(false, d.window[:d.windowEnd], d.sync) d.err = d.w.err d.windowEnd = 0 } // storeFast will compress and store the currently added data, // if enough has been accumulated or we at the end of the stream. // Any error that occurred will be in d.err func (d *compressor) storeFast() { // We only compress if we have maxStoreBlockSize. if d.windowEnd < len(d.window) { if !d.sync { return } // Handle extremely small sizes. if d.windowEnd < 128 { if d.windowEnd == 0 { return } if d.windowEnd <= 32 { d.err = d.writeStoredBlock(d.window[:d.windowEnd]) } else { d.w.writeBlockHuff(false, d.window[:d.windowEnd], true) d.err = d.w.err } d.tokens.Reset() d.windowEnd = 0 d.fast.Reset() return } } d.fast.Encode(&d.tokens, d.window[:d.windowEnd]) // If we made zero matches, store the block as is. if d.tokens.n == 0 { d.err = d.writeStoredBlock(d.window[:d.windowEnd]) // If we removed less than 1/16th, huffman compress the block. } else if int(d.tokens.n) > d.windowEnd-(d.windowEnd>>4) { d.w.writeBlockHuff(false, d.window[:d.windowEnd], d.sync) d.err = d.w.err } else { d.w.writeBlockDynamic(&d.tokens, false, d.window[:d.windowEnd], d.sync) d.err = d.w.err } d.tokens.Reset() d.windowEnd = 0 } // write will add input byte to the stream. // Unless an error occurs all bytes will be consumed. func (d *compressor) write(b []byte) (n int, err error) { if d.err != nil { return 0, d.err } n = len(b) for len(b) > 0 { if d.windowEnd == len(d.window) || d.sync { d.step(d) } b = b[d.fill(d, b):] if d.err != nil { return 0, d.err } } return n, d.err } func (d *compressor) syncFlush() error { d.sync = true if d.err != nil { return d.err } d.step(d) if d.err == nil { d.w.writeStoredHeader(0, false) d.w.flush() d.err = d.w.err } d.sync = false return d.err } func (d *compressor) init(w io.Writer, level int) (err error) { d.w = newHuffmanBitWriter(w) switch { case level == NoCompression: d.window = make([]byte, maxStoreBlockSize) d.fill = (*compressor).fillBlock d.step = (*compressor).store case level == ConstantCompression: d.w.logNewTablePenalty = 10 d.window = make([]byte, 32<<10) d.fill = (*compressor).fillBlock d.step = (*compressor).storeHuff case level == DefaultCompression: level = 5 fallthrough case level >= 1 && level <= 6: d.w.logNewTablePenalty = 7 d.fast = newFastEnc(level) d.window = make([]byte, maxStoreBlockSize) d.fill = (*compressor).fillBlock d.step = (*compressor).storeFast case 7 <= level && level <= 9: d.w.logNewTablePenalty = 8 d.state = &advancedState{} d.compressionLevel = levels[level] d.initDeflate() d.fill = (*compressor).fillDeflate d.step = (*compressor).deflateLazy case -level >= MinCustomWindowSize && -level <= MaxCustomWindowSize: d.w.logNewTablePenalty = 7 d.fast = &fastEncL5Window{maxOffset: int32(-level), cur: maxStoreBlockSize} d.window = make([]byte, maxStoreBlockSize) d.fill = (*compressor).fillBlock d.step = (*compressor).storeFast default: return fmt.Errorf("flate: invalid compression level %d: want value in range [-2, 9]", level) } d.level = level return nil } // reset the state of the compressor. func (d *compressor) reset(w io.Writer) { d.w.reset(w) d.sync = false d.err = nil // We only need to reset a few things for Snappy. if d.fast != nil { d.fast.Reset() d.windowEnd = 0 d.tokens.Reset() return } switch d.compressionLevel.chain { case 0: // level was NoCompression or ConstantCompresssion. d.windowEnd = 0 default: s := d.state s.chainHead = -1 for i := range s.hashHead { s.hashHead[i] = 0 } for i := range s.hashPrev { s.hashPrev[i] = 0 } s.hashOffset = 1 s.index, d.windowEnd = 0, 0 d.blockStart, d.byteAvailable = 0, false d.tokens.Reset() s.length = minMatchLength - 1 s.offset = 0 s.ii = 0 s.maxInsertIndex = 0 } } func (d *compressor) close() error { if d.err != nil { return d.err } d.sync = true d.step(d) if d.err != nil { return d.err } if d.w.writeStoredHeader(0, true); d.w.err != nil { return d.w.err } d.w.flush() d.w.reset(nil) return d.w.err } // NewWriter returns a new Writer compressing data at the given level. // Following zlib, levels range from 1 (BestSpeed) to 9 (BestCompression); // higher levels typically run slower but compress more. // Level 0 (NoCompression) does not attempt any compression; it only adds the // necessary DEFLATE framing. // Level -1 (DefaultCompression) uses the default compression level. // Level -2 (ConstantCompression) will use Huffman compression only, giving // a very fast compression for all types of input, but sacrificing considerable // compression efficiency. // // If level is in the range [-2, 9] then the error returned will be nil. // Otherwise the error returned will be non-nil. func NewWriter(w io.Writer, level int) (*Writer, error) { var dw Writer if err := dw.d.init(w, level); err != nil { return nil, err } return &dw, nil } // NewWriterDict is like NewWriter but initializes the new // Writer with a preset dictionary. The returned Writer behaves // as if the dictionary had been written to it without producing // any compressed output. The compressed data written to w // can only be decompressed by a Reader initialized with the // same dictionary. func NewWriterDict(w io.Writer, level int, dict []byte) (*Writer, error) { zw, err := NewWriter(w, level) if err != nil { return nil, err } zw.d.fillWindow(dict) zw.dict = append(zw.dict, dict...) // duplicate dictionary for Reset method. return zw, err } // MinCustomWindowSize is the minimum window size that can be sent to NewWriterWindow. const MinCustomWindowSize = 32 // MaxCustomWindowSize is the maximum custom window that can be sent to NewWriterWindow. const MaxCustomWindowSize = windowSize // NewWriterWindow returns a new Writer compressing data with a custom window size. // windowSize must be from MinCustomWindowSize to MaxCustomWindowSize. func NewWriterWindow(w io.Writer, windowSize int) (*Writer, error) { if windowSize < MinCustomWindowSize { return nil, errors.New("flate: requested window size less than MinWindowSize") } if windowSize > MaxCustomWindowSize { return nil, errors.New("flate: requested window size bigger than MaxCustomWindowSize") } var dw Writer if err := dw.d.init(w, -windowSize); err != nil { return nil, err } return &dw, nil } // A Writer takes data written to it and writes the compressed // form of that data to an underlying writer (see NewWriter). type Writer struct { d compressor dict []byte } // Write writes data to w, which will eventually write the // compressed form of data to its underlying writer. func (w *Writer) Write(data []byte) (n int, err error) { return w.d.write(data) } // Flush flushes any pending data to the underlying writer. // It is useful mainly in compressed network protocols, to ensure that // a remote reader has enough data to reconstruct a packet. // Flush does not return until the data has been written. // Calling Flush when there is no pending data still causes the Writer // to emit a sync marker of at least 4 bytes. // If the underlying writer returns an error, Flush returns that error. // // In the terminology of the zlib library, Flush is equivalent to Z_SYNC_FLUSH. func (w *Writer) Flush() error { // For more about flushing: // http://www.bolet.org/~pornin/deflate-flush.html return w.d.syncFlush() } // Close flushes and closes the writer. func (w *Writer) Close() error { return w.d.close() } // Reset discards the writer's state and makes it equivalent to // the result of NewWriter or NewWriterDict called with dst // and w's level and dictionary. func (w *Writer) Reset(dst io.Writer) { if len(w.dict) > 0 { // w was created with NewWriterDict w.d.reset(dst) if dst != nil { w.d.fillWindow(w.dict) } } else { // w was created with NewWriter w.d.reset(dst) } } // ResetDict discards the writer's state and makes it equivalent to // the result of NewWriter or NewWriterDict called with dst // and w's level, but sets a specific dictionary. func (w *Writer) ResetDict(dst io.Writer, dict []byte) { w.dict = dict w.d.reset(dst) w.d.fillWindow(w.dict) } ================================================ FILE: vendor/github.com/klauspost/compress/flate/dict_decoder.go ================================================ // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package flate // dictDecoder implements the LZ77 sliding dictionary as used in decompression. // LZ77 decompresses data through sequences of two forms of commands: // // - Literal insertions: Runs of one or more symbols are inserted into the data // stream as is. This is accomplished through the writeByte method for a // single symbol, or combinations of writeSlice/writeMark for multiple symbols. // Any valid stream must start with a literal insertion if no preset dictionary // is used. // // - Backward copies: Runs of one or more symbols are copied from previously // emitted data. Backward copies come as the tuple (dist, length) where dist // determines how far back in the stream to copy from and length determines how // many bytes to copy. Note that it is valid for the length to be greater than // the distance. Since LZ77 uses forward copies, that situation is used to // perform a form of run-length encoding on repeated runs of symbols. // The writeCopy and tryWriteCopy are used to implement this command. // // For performance reasons, this implementation performs little to no sanity // checks about the arguments. As such, the invariants documented for each // method call must be respected. type dictDecoder struct { hist []byte // Sliding window history // Invariant: 0 <= rdPos <= wrPos <= len(hist) wrPos int // Current output position in buffer rdPos int // Have emitted hist[:rdPos] already full bool // Has a full window length been written yet? } // init initializes dictDecoder to have a sliding window dictionary of the given // size. If a preset dict is provided, it will initialize the dictionary with // the contents of dict. func (dd *dictDecoder) init(size int, dict []byte) { *dd = dictDecoder{hist: dd.hist} if cap(dd.hist) < size { dd.hist = make([]byte, size) } dd.hist = dd.hist[:size] if len(dict) > len(dd.hist) { dict = dict[len(dict)-len(dd.hist):] } dd.wrPos = copy(dd.hist, dict) if dd.wrPos == len(dd.hist) { dd.wrPos = 0 dd.full = true } dd.rdPos = dd.wrPos } // histSize reports the total amount of historical data in the dictionary. func (dd *dictDecoder) histSize() int { if dd.full { return len(dd.hist) } return dd.wrPos } // availRead reports the number of bytes that can be flushed by readFlush. func (dd *dictDecoder) availRead() int { return dd.wrPos - dd.rdPos } // availWrite reports the available amount of output buffer space. func (dd *dictDecoder) availWrite() int { return len(dd.hist) - dd.wrPos } // writeSlice returns a slice of the available buffer to write data to. // // This invariant will be kept: len(s) <= availWrite() func (dd *dictDecoder) writeSlice() []byte { return dd.hist[dd.wrPos:] } // writeMark advances the writer pointer by cnt. // // This invariant must be kept: 0 <= cnt <= availWrite() func (dd *dictDecoder) writeMark(cnt int) { dd.wrPos += cnt } // writeByte writes a single byte to the dictionary. // // This invariant must be kept: 0 < availWrite() func (dd *dictDecoder) writeByte(c byte) { dd.hist[dd.wrPos] = c dd.wrPos++ } // writeCopy copies a string at a given (dist, length) to the output. // This returns the number of bytes copied and may be less than the requested // length if the available space in the output buffer is too small. // // This invariant must be kept: 0 < dist <= histSize() func (dd *dictDecoder) writeCopy(dist, length int) int { dstBase := dd.wrPos dstPos := dstBase srcPos := dstPos - dist endPos := dstPos + length if endPos > len(dd.hist) { endPos = len(dd.hist) } // Copy non-overlapping section after destination position. // // This section is non-overlapping in that the copy length for this section // is always less than or equal to the backwards distance. This can occur // if a distance refers to data that wraps-around in the buffer. // Thus, a backwards copy is performed here; that is, the exact bytes in // the source prior to the copy is placed in the destination. if srcPos < 0 { srcPos += len(dd.hist) dstPos += copy(dd.hist[dstPos:endPos], dd.hist[srcPos:]) srcPos = 0 } // Copy possibly overlapping section before destination position. // // This section can overlap if the copy length for this section is larger // than the backwards distance. This is allowed by LZ77 so that repeated // strings can be succinctly represented using (dist, length) pairs. // Thus, a forwards copy is performed here; that is, the bytes copied is // possibly dependent on the resulting bytes in the destination as the copy // progresses along. This is functionally equivalent to the following: // // for i := 0; i < endPos-dstPos; i++ { // dd.hist[dstPos+i] = dd.hist[srcPos+i] // } // dstPos = endPos // for dstPos < endPos { dstPos += copy(dd.hist[dstPos:endPos], dd.hist[srcPos:dstPos]) } dd.wrPos = dstPos return dstPos - dstBase } // tryWriteCopy tries to copy a string at a given (distance, length) to the // output. This specialized version is optimized for short distances. // // This method is designed to be inlined for performance reasons. // // This invariant must be kept: 0 < dist <= histSize() func (dd *dictDecoder) tryWriteCopy(dist, length int) int { dstPos := dd.wrPos endPos := dstPos + length if dstPos < dist || endPos > len(dd.hist) { return 0 } dstBase := dstPos srcPos := dstPos - dist // Copy possibly overlapping section before destination position. loop: dstPos += copy(dd.hist[dstPos:endPos], dd.hist[srcPos:dstPos]) if dstPos < endPos { goto loop // Avoid for-loop so that this function can be inlined } dd.wrPos = dstPos return dstPos - dstBase } // readFlush returns a slice of the historical buffer that is ready to be // emitted to the user. The data returned by readFlush must be fully consumed // before calling any other dictDecoder methods. func (dd *dictDecoder) readFlush() []byte { toRead := dd.hist[dd.rdPos:dd.wrPos] dd.rdPos = dd.wrPos if dd.wrPos == len(dd.hist) { dd.wrPos, dd.rdPos = 0, 0 dd.full = true } return toRead } ================================================ FILE: vendor/github.com/klauspost/compress/flate/fast_encoder.go ================================================ // Copyright 2011 The Snappy-Go Authors. All rights reserved. // Modified for deflate by Klaus Post (c) 2015. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package flate import ( "encoding/binary" "fmt" ) type fastEnc interface { Encode(dst *tokens, src []byte) Reset() } func newFastEnc(level int) fastEnc { switch level { case 1: return &fastEncL1{fastGen: fastGen{cur: maxStoreBlockSize}} case 2: return &fastEncL2{fastGen: fastGen{cur: maxStoreBlockSize}} case 3: return &fastEncL3{fastGen: fastGen{cur: maxStoreBlockSize}} case 4: return &fastEncL4{fastGen: fastGen{cur: maxStoreBlockSize}} case 5: return &fastEncL5{fastGen: fastGen{cur: maxStoreBlockSize}} case 6: return &fastEncL6{fastGen: fastGen{cur: maxStoreBlockSize}} default: panic("invalid level specified") } } const ( tableBits = 15 // Bits used in the table tableSize = 1 << tableBits // Size of the table tableShift = 32 - tableBits // Right-shift to get the tableBits most significant bits of a uint32. baseMatchOffset = 1 // The smallest match offset baseMatchLength = 3 // The smallest match length per the RFC section 3.2.5 maxMatchOffset = 1 << 15 // The largest match offset bTableBits = 17 // Bits used in the big tables bTableSize = 1 << bTableBits // Size of the table allocHistory = maxStoreBlockSize * 5 // Size to preallocate for history. bufferReset = (1 << 31) - allocHistory - maxStoreBlockSize - 1 // Reset the buffer offset when reaching this. ) const ( prime3bytes = 506832829 prime4bytes = 2654435761 prime5bytes = 889523592379 prime6bytes = 227718039650203 prime7bytes = 58295818150454627 prime8bytes = 0xcf1bbcdcb7a56463 ) func load3232(b []byte, i int32) uint32 { return binary.LittleEndian.Uint32(b[i:]) } func load6432(b []byte, i int32) uint64 { return binary.LittleEndian.Uint64(b[i:]) } type tableEntry struct { offset int32 } // fastGen maintains the table for matches, // and the previous byte block for level 2. // This is the generic implementation. type fastGen struct { hist []byte cur int32 } func (e *fastGen) addBlock(src []byte) int32 { // check if we have space already if len(e.hist)+len(src) > cap(e.hist) { if cap(e.hist) == 0 { e.hist = make([]byte, 0, allocHistory) } else { if cap(e.hist) < maxMatchOffset*2 { panic("unexpected buffer size") } // Move down offset := int32(len(e.hist)) - maxMatchOffset // copy(e.hist[0:maxMatchOffset], e.hist[offset:]) *(*[maxMatchOffset]byte)(e.hist) = *(*[maxMatchOffset]byte)(e.hist[offset:]) e.cur += offset e.hist = e.hist[:maxMatchOffset] } } s := int32(len(e.hist)) e.hist = append(e.hist, src...) return s } type tableEntryPrev struct { Cur tableEntry Prev tableEntry } // hash7 returns the hash of the lowest 7 bytes of u to fit in a hash table with h bits. // Preferably h should be a constant and should always be <64. func hash7(u uint64, h uint8) uint32 { return uint32(((u << (64 - 56)) * prime7bytes) >> ((64 - h) & reg8SizeMask64)) } // hashLen returns a hash of the lowest mls bytes of with length output bits. // mls must be >=3 and <=8. Any other value will return hash for 4 bytes. // length should always be < 32. // Preferably length and mls should be a constant for inlining. func hashLen(u uint64, length, mls uint8) uint32 { switch mls { case 3: return (uint32(u<<8) * prime3bytes) >> (32 - length) case 5: return uint32(((u << (64 - 40)) * prime5bytes) >> (64 - length)) case 6: return uint32(((u << (64 - 48)) * prime6bytes) >> (64 - length)) case 7: return uint32(((u << (64 - 56)) * prime7bytes) >> (64 - length)) case 8: return uint32((u * prime8bytes) >> (64 - length)) default: return (uint32(u) * prime4bytes) >> (32 - length) } } // matchlen will return the match length between offsets and t in src. // The maximum length returned is maxMatchLength - 4. // It is assumed that s > t, that t >=0 and s < len(src). func (e *fastGen) matchlen(s, t int32, src []byte) int32 { if debugDecode { if t >= s { panic(fmt.Sprint("t >=s:", t, s)) } if int(s) >= len(src) { panic(fmt.Sprint("s >= len(src):", s, len(src))) } if t < 0 { panic(fmt.Sprint("t < 0:", t)) } if s-t > maxMatchOffset { panic(fmt.Sprint(s, "-", t, "(", s-t, ") > maxMatchLength (", maxMatchOffset, ")")) } } s1 := int(s) + maxMatchLength - 4 if s1 > len(src) { s1 = len(src) } // Extend the match to be as long as possible. return int32(matchLen(src[s:s1], src[t:])) } // matchlenLong will return the match length between offsets and t in src. // It is assumed that s > t, that t >=0 and s < len(src). func (e *fastGen) matchlenLong(s, t int32, src []byte) int32 { if debugDeflate { if t >= s { panic(fmt.Sprint("t >=s:", t, s)) } if int(s) >= len(src) { panic(fmt.Sprint("s >= len(src):", s, len(src))) } if t < 0 { panic(fmt.Sprint("t < 0:", t)) } if s-t > maxMatchOffset { panic(fmt.Sprint(s, "-", t, "(", s-t, ") > maxMatchLength (", maxMatchOffset, ")")) } } // Extend the match to be as long as possible. return int32(matchLen(src[s:], src[t:])) } // Reset the encoding table. func (e *fastGen) Reset() { if cap(e.hist) < allocHistory { e.hist = make([]byte, 0, allocHistory) } // We offset current position so everything will be out of reach. // If we are above the buffer reset it will be cleared anyway since len(hist) == 0. if e.cur <= bufferReset { e.cur += maxMatchOffset + int32(len(e.hist)) } e.hist = e.hist[:0] } ================================================ FILE: vendor/github.com/klauspost/compress/flate/huffman_bit_writer.go ================================================ // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package flate import ( "encoding/binary" "fmt" "io" "math" ) const ( // The largest offset code. offsetCodeCount = 30 // The special code used to mark the end of a block. endBlockMarker = 256 // The first length code. lengthCodesStart = 257 // The number of codegen codes. codegenCodeCount = 19 badCode = 255 // maxPredefinedTokens is the maximum number of tokens // where we check if fixed size is smaller. maxPredefinedTokens = 250 // bufferFlushSize indicates the buffer size // after which bytes are flushed to the writer. // Should preferably be a multiple of 6, since // we accumulate 6 bytes between writes to the buffer. bufferFlushSize = 246 ) // Minimum length code that emits bits. const lengthExtraBitsMinCode = 8 // The number of extra bits needed by length code X - LENGTH_CODES_START. var lengthExtraBits = [32]uint8{ /* 257 */ 0, 0, 0, /* 260 */ 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, /* 270 */ 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, /* 280 */ 4, 5, 5, 5, 5, 0, } // The length indicated by length code X - LENGTH_CODES_START. var lengthBase = [32]uint8{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 255, } // Minimum offset code that emits bits. const offsetExtraBitsMinCode = 4 // offset code word extra bits. var offsetExtraBits = [32]int8{ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, /* extended window */ 14, 14, } var offsetCombined = [32]uint32{} func init() { var offsetBase = [32]uint32{ /* normal deflate */ 0x000000, 0x000001, 0x000002, 0x000003, 0x000004, 0x000006, 0x000008, 0x00000c, 0x000010, 0x000018, 0x000020, 0x000030, 0x000040, 0x000060, 0x000080, 0x0000c0, 0x000100, 0x000180, 0x000200, 0x000300, 0x000400, 0x000600, 0x000800, 0x000c00, 0x001000, 0x001800, 0x002000, 0x003000, 0x004000, 0x006000, /* extended window */ 0x008000, 0x00c000, } for i := range offsetCombined[:] { // Don't use extended window values... if offsetExtraBits[i] == 0 || offsetBase[i] > 0x006000 { continue } offsetCombined[i] = uint32(offsetExtraBits[i]) | (offsetBase[i] << 8) } } // The odd order in which the codegen code sizes are written. var codegenOrder = []uint32{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15} type huffmanBitWriter struct { // writer is the underlying writer. // Do not use it directly; use the write method, which ensures // that Write errors are sticky. writer io.Writer // Data waiting to be written is bytes[0:nbytes] // and then the low nbits of bits. bits uint64 nbits uint8 nbytes uint8 lastHuffMan bool literalEncoding *huffmanEncoder tmpLitEncoding *huffmanEncoder offsetEncoding *huffmanEncoder codegenEncoding *huffmanEncoder err error lastHeader int // Set between 0 (reused block can be up to 2x the size) logNewTablePenalty uint bytes [256 + 8]byte literalFreq [lengthCodesStart + 32]uint16 offsetFreq [32]uint16 codegenFreq [codegenCodeCount]uint16 // codegen must have an extra space for the final symbol. codegen [literalCount + offsetCodeCount + 1]uint8 } // Huffman reuse. // // The huffmanBitWriter supports reusing huffman tables and thereby combining block sections. // // This is controlled by several variables: // // If lastHeader is non-zero the Huffman table can be reused. // This also indicates that a Huffman table has been generated that can output all // possible symbols. // It also indicates that an EOB has not yet been emitted, so if a new tabel is generated // an EOB with the previous table must be written. // // If lastHuffMan is set, a table for outputting literals has been generated and offsets are invalid. // // An incoming block estimates the output size of a new table using a 'fresh' by calculating the // optimal size and adding a penalty in 'logNewTablePenalty'. // A Huffman table is not optimal, which is why we add a penalty, and generating a new table // is slower both for compression and decompression. func newHuffmanBitWriter(w io.Writer) *huffmanBitWriter { return &huffmanBitWriter{ writer: w, literalEncoding: newHuffmanEncoder(literalCount), tmpLitEncoding: newHuffmanEncoder(literalCount), codegenEncoding: newHuffmanEncoder(codegenCodeCount), offsetEncoding: newHuffmanEncoder(offsetCodeCount), } } func (w *huffmanBitWriter) reset(writer io.Writer) { w.writer = writer w.bits, w.nbits, w.nbytes, w.err = 0, 0, 0, nil w.lastHeader = 0 w.lastHuffMan = false } func (w *huffmanBitWriter) canReuse(t *tokens) (ok bool) { a := t.offHist[:offsetCodeCount] b := w.offsetEncoding.codes b = b[:len(a)] for i, v := range a { if v != 0 && b[i].zero() { return false } } a = t.extraHist[:literalCount-256] b = w.literalEncoding.codes[256:literalCount] b = b[:len(a)] for i, v := range a { if v != 0 && b[i].zero() { return false } } a = t.litHist[:256] b = w.literalEncoding.codes[:len(a)] for i, v := range a { if v != 0 && b[i].zero() { return false } } return true } func (w *huffmanBitWriter) flush() { if w.err != nil { w.nbits = 0 return } if w.lastHeader > 0 { // We owe an EOB w.writeCode(w.literalEncoding.codes[endBlockMarker]) w.lastHeader = 0 } n := w.nbytes for w.nbits != 0 { w.bytes[n] = byte(w.bits) w.bits >>= 8 if w.nbits > 8 { // Avoid underflow w.nbits -= 8 } else { w.nbits = 0 } n++ } w.bits = 0 w.write(w.bytes[:n]) w.nbytes = 0 } func (w *huffmanBitWriter) write(b []byte) { if w.err != nil { return } _, w.err = w.writer.Write(b) } func (w *huffmanBitWriter) writeBits(b int32, nb uint8) { w.bits |= uint64(b) << (w.nbits & 63) w.nbits += nb if w.nbits >= 48 { w.writeOutBits() } } func (w *huffmanBitWriter) writeBytes(bytes []byte) { if w.err != nil { return } n := w.nbytes if w.nbits&7 != 0 { w.err = InternalError("writeBytes with unfinished bits") return } for w.nbits != 0 { w.bytes[n] = byte(w.bits) w.bits >>= 8 w.nbits -= 8 n++ } if n != 0 { w.write(w.bytes[:n]) } w.nbytes = 0 w.write(bytes) } // RFC 1951 3.2.7 specifies a special run-length encoding for specifying // the literal and offset lengths arrays (which are concatenated into a single // array). This method generates that run-length encoding. // // The result is written into the codegen array, and the frequencies // of each code is written into the codegenFreq array. // Codes 0-15 are single byte codes. Codes 16-18 are followed by additional // information. Code badCode is an end marker // // numLiterals The number of literals in literalEncoding // numOffsets The number of offsets in offsetEncoding // litenc, offenc The literal and offset encoder to use func (w *huffmanBitWriter) generateCodegen(numLiterals int, numOffsets int, litEnc, offEnc *huffmanEncoder) { for i := range w.codegenFreq { w.codegenFreq[i] = 0 } // Note that we are using codegen both as a temporary variable for holding // a copy of the frequencies, and as the place where we put the result. // This is fine because the output is always shorter than the input used // so far. codegen := w.codegen[:] // cache // Copy the concatenated code sizes to codegen. Put a marker at the end. cgnl := codegen[:numLiterals] for i := range cgnl { cgnl[i] = litEnc.codes[i].len() } cgnl = codegen[numLiterals : numLiterals+numOffsets] for i := range cgnl { cgnl[i] = offEnc.codes[i].len() } codegen[numLiterals+numOffsets] = badCode size := codegen[0] count := 1 outIndex := 0 for inIndex := 1; size != badCode; inIndex++ { // INVARIANT: We have seen "count" copies of size that have not yet // had output generated for them. nextSize := codegen[inIndex] if nextSize == size { count++ continue } // We need to generate codegen indicating "count" of size. if size != 0 { codegen[outIndex] = size outIndex++ w.codegenFreq[size]++ count-- for count >= 3 { n := 6 if n > count { n = count } codegen[outIndex] = 16 outIndex++ codegen[outIndex] = uint8(n - 3) outIndex++ w.codegenFreq[16]++ count -= n } } else { for count >= 11 { n := 138 if n > count { n = count } codegen[outIndex] = 18 outIndex++ codegen[outIndex] = uint8(n - 11) outIndex++ w.codegenFreq[18]++ count -= n } if count >= 3 { // count >= 3 && count <= 10 codegen[outIndex] = 17 outIndex++ codegen[outIndex] = uint8(count - 3) outIndex++ w.codegenFreq[17]++ count = 0 } } count-- for ; count >= 0; count-- { codegen[outIndex] = size outIndex++ w.codegenFreq[size]++ } // Set up invariant for next time through the loop. size = nextSize count = 1 } // Marker indicating the end of the codegen. codegen[outIndex] = badCode } func (w *huffmanBitWriter) codegens() int { numCodegens := len(w.codegenFreq) for numCodegens > 4 && w.codegenFreq[codegenOrder[numCodegens-1]] == 0 { numCodegens-- } return numCodegens } func (w *huffmanBitWriter) headerSize() (size, numCodegens int) { numCodegens = len(w.codegenFreq) for numCodegens > 4 && w.codegenFreq[codegenOrder[numCodegens-1]] == 0 { numCodegens-- } return 3 + 5 + 5 + 4 + (3 * numCodegens) + w.codegenEncoding.bitLength(w.codegenFreq[:]) + int(w.codegenFreq[16])*2 + int(w.codegenFreq[17])*3 + int(w.codegenFreq[18])*7, numCodegens } // dynamicSize returns the size of dynamically encoded data in bits. func (w *huffmanBitWriter) dynamicReuseSize(litEnc, offEnc *huffmanEncoder) (size int) { size = litEnc.bitLength(w.literalFreq[:]) + offEnc.bitLength(w.offsetFreq[:]) return size } // dynamicSize returns the size of dynamically encoded data in bits. func (w *huffmanBitWriter) dynamicSize(litEnc, offEnc *huffmanEncoder, extraBits int) (size, numCodegens int) { header, numCodegens := w.headerSize() size = header + litEnc.bitLength(w.literalFreq[:]) + offEnc.bitLength(w.offsetFreq[:]) + extraBits return size, numCodegens } // extraBitSize will return the number of bits that will be written // as "extra" bits on matches. func (w *huffmanBitWriter) extraBitSize() int { total := 0 for i, n := range w.literalFreq[257:literalCount] { total += int(n) * int(lengthExtraBits[i&31]) } for i, n := range w.offsetFreq[:offsetCodeCount] { total += int(n) * int(offsetExtraBits[i&31]) } return total } // fixedSize returns the size of dynamically encoded data in bits. func (w *huffmanBitWriter) fixedSize(extraBits int) int { return 3 + fixedLiteralEncoding.bitLength(w.literalFreq[:]) + fixedOffsetEncoding.bitLength(w.offsetFreq[:]) + extraBits } // storedSize calculates the stored size, including header. // The function returns the size in bits and whether the block // fits inside a single block. func (w *huffmanBitWriter) storedSize(in []byte) (int, bool) { if in == nil { return 0, false } if len(in) <= maxStoreBlockSize { return (len(in) + 5) * 8, true } return 0, false } func (w *huffmanBitWriter) writeCode(c hcode) { // The function does not get inlined if we "& 63" the shift. w.bits |= c.code64() << (w.nbits & 63) w.nbits += c.len() if w.nbits >= 48 { w.writeOutBits() } } // writeOutBits will write bits to the buffer. func (w *huffmanBitWriter) writeOutBits() { bits := w.bits w.bits >>= 48 w.nbits -= 48 n := w.nbytes // We over-write, but faster... binary.LittleEndian.PutUint64(w.bytes[n:], bits) n += 6 if n >= bufferFlushSize { if w.err != nil { n = 0 return } w.write(w.bytes[:n]) n = 0 } w.nbytes = n } // Write the header of a dynamic Huffman block to the output stream. // // numLiterals The number of literals specified in codegen // numOffsets The number of offsets specified in codegen // numCodegens The number of codegens used in codegen func (w *huffmanBitWriter) writeDynamicHeader(numLiterals int, numOffsets int, numCodegens int, isEof bool) { if w.err != nil { return } var firstBits int32 = 4 if isEof { firstBits = 5 } w.writeBits(firstBits, 3) w.writeBits(int32(numLiterals-257), 5) w.writeBits(int32(numOffsets-1), 5) w.writeBits(int32(numCodegens-4), 4) for i := 0; i < numCodegens; i++ { value := uint(w.codegenEncoding.codes[codegenOrder[i]].len()) w.writeBits(int32(value), 3) } i := 0 for { var codeWord = uint32(w.codegen[i]) i++ if codeWord == badCode { break } w.writeCode(w.codegenEncoding.codes[codeWord]) switch codeWord { case 16: w.writeBits(int32(w.codegen[i]), 2) i++ case 17: w.writeBits(int32(w.codegen[i]), 3) i++ case 18: w.writeBits(int32(w.codegen[i]), 7) i++ } } } // writeStoredHeader will write a stored header. // If the stored block is only used for EOF, // it is replaced with a fixed huffman block. func (w *huffmanBitWriter) writeStoredHeader(length int, isEof bool) { if w.err != nil { return } if w.lastHeader > 0 { // We owe an EOB w.writeCode(w.literalEncoding.codes[endBlockMarker]) w.lastHeader = 0 } // To write EOF, use a fixed encoding block. 10 bits instead of 5 bytes. if length == 0 && isEof { w.writeFixedHeader(isEof) // EOB: 7 bits, value: 0 w.writeBits(0, 7) w.flush() return } var flag int32 if isEof { flag = 1 } w.writeBits(flag, 3) w.flush() w.writeBits(int32(length), 16) w.writeBits(int32(^uint16(length)), 16) } func (w *huffmanBitWriter) writeFixedHeader(isEof bool) { if w.err != nil { return } if w.lastHeader > 0 { // We owe an EOB w.writeCode(w.literalEncoding.codes[endBlockMarker]) w.lastHeader = 0 } // Indicate that we are a fixed Huffman block var value int32 = 2 if isEof { value = 3 } w.writeBits(value, 3) } // writeBlock will write a block of tokens with the smallest encoding. // The original input can be supplied, and if the huffman encoded data // is larger than the original bytes, the data will be written as a // stored block. // If the input is nil, the tokens will always be Huffman encoded. func (w *huffmanBitWriter) writeBlock(tokens *tokens, eof bool, input []byte) { if w.err != nil { return } tokens.AddEOB() if w.lastHeader > 0 { // We owe an EOB w.writeCode(w.literalEncoding.codes[endBlockMarker]) w.lastHeader = 0 } numLiterals, numOffsets := w.indexTokens(tokens, false) w.generate() var extraBits int storedSize, storable := w.storedSize(input) if storable { extraBits = w.extraBitSize() } // Figure out smallest code. // Fixed Huffman baseline. var literalEncoding = fixedLiteralEncoding var offsetEncoding = fixedOffsetEncoding var size = math.MaxInt32 if tokens.n < maxPredefinedTokens { size = w.fixedSize(extraBits) } // Dynamic Huffman? var numCodegens int // Generate codegen and codegenFrequencies, which indicates how to encode // the literalEncoding and the offsetEncoding. w.generateCodegen(numLiterals, numOffsets, w.literalEncoding, w.offsetEncoding) w.codegenEncoding.generate(w.codegenFreq[:], 7) dynamicSize, numCodegens := w.dynamicSize(w.literalEncoding, w.offsetEncoding, extraBits) if dynamicSize < size { size = dynamicSize literalEncoding = w.literalEncoding offsetEncoding = w.offsetEncoding } // Stored bytes? if storable && storedSize <= size { w.writeStoredHeader(len(input), eof) w.writeBytes(input) return } // Huffman. if literalEncoding == fixedLiteralEncoding { w.writeFixedHeader(eof) } else { w.writeDynamicHeader(numLiterals, numOffsets, numCodegens, eof) } // Write the tokens. w.writeTokens(tokens.Slice(), literalEncoding.codes, offsetEncoding.codes) } // writeBlockDynamic encodes a block using a dynamic Huffman table. // This should be used if the symbols used have a disproportionate // histogram distribution. // If input is supplied and the compression savings are below 1/16th of the // input size the block is stored. func (w *huffmanBitWriter) writeBlockDynamic(tokens *tokens, eof bool, input []byte, sync bool) { if w.err != nil { return } sync = sync || eof if sync { tokens.AddEOB() } // We cannot reuse pure huffman table, and must mark as EOF. if (w.lastHuffMan || eof) && w.lastHeader > 0 { // We will not try to reuse. w.writeCode(w.literalEncoding.codes[endBlockMarker]) w.lastHeader = 0 w.lastHuffMan = false } // fillReuse enables filling of empty values. // This will make encodings always reusable without testing. // However, this does not appear to benefit on most cases. const fillReuse = false // Check if we can reuse... if !fillReuse && w.lastHeader > 0 && !w.canReuse(tokens) { w.writeCode(w.literalEncoding.codes[endBlockMarker]) w.lastHeader = 0 } numLiterals, numOffsets := w.indexTokens(tokens, !sync) extraBits := 0 ssize, storable := w.storedSize(input) const usePrefs = true if storable || w.lastHeader > 0 { extraBits = w.extraBitSize() } var size int // Check if we should reuse. if w.lastHeader > 0 { // Estimate size for using a new table. // Use the previous header size as the best estimate. newSize := w.lastHeader + tokens.EstimatedBits() newSize += int(w.literalEncoding.codes[endBlockMarker].len()) + newSize>>w.logNewTablePenalty // The estimated size is calculated as an optimal table. // We add a penalty to make it more realistic and re-use a bit more. reuseSize := w.dynamicReuseSize(w.literalEncoding, w.offsetEncoding) + extraBits // Check if a new table is better. if newSize < reuseSize { // Write the EOB we owe. w.writeCode(w.literalEncoding.codes[endBlockMarker]) size = newSize w.lastHeader = 0 } else { size = reuseSize } if tokens.n < maxPredefinedTokens { if preSize := w.fixedSize(extraBits) + 7; usePrefs && preSize < size { // Check if we get a reasonable size decrease. if storable && ssize <= size { w.writeStoredHeader(len(input), eof) w.writeBytes(input) return } w.writeFixedHeader(eof) if !sync { tokens.AddEOB() } w.writeTokens(tokens.Slice(), fixedLiteralEncoding.codes, fixedOffsetEncoding.codes) return } } // Check if we get a reasonable size decrease. if storable && ssize <= size { w.writeStoredHeader(len(input), eof) w.writeBytes(input) return } } // We want a new block/table if w.lastHeader == 0 { if fillReuse && !sync { w.fillTokens() numLiterals, numOffsets = maxNumLit, maxNumDist } else { w.literalFreq[endBlockMarker] = 1 } w.generate() // Generate codegen and codegenFrequencies, which indicates how to encode // the literalEncoding and the offsetEncoding. w.generateCodegen(numLiterals, numOffsets, w.literalEncoding, w.offsetEncoding) w.codegenEncoding.generate(w.codegenFreq[:], 7) var numCodegens int if fillReuse && !sync { // Reindex for accurate size... w.indexTokens(tokens, true) } size, numCodegens = w.dynamicSize(w.literalEncoding, w.offsetEncoding, extraBits) // Store predefined, if we don't get a reasonable improvement. if tokens.n < maxPredefinedTokens { if preSize := w.fixedSize(extraBits); usePrefs && preSize <= size { // Store bytes, if we don't get an improvement. if storable && ssize <= preSize { w.writeStoredHeader(len(input), eof) w.writeBytes(input) return } w.writeFixedHeader(eof) if !sync { tokens.AddEOB() } w.writeTokens(tokens.Slice(), fixedLiteralEncoding.codes, fixedOffsetEncoding.codes) return } } if storable && ssize <= size { // Store bytes, if we don't get an improvement. w.writeStoredHeader(len(input), eof) w.writeBytes(input) return } // Write Huffman table. w.writeDynamicHeader(numLiterals, numOffsets, numCodegens, eof) if !sync { w.lastHeader, _ = w.headerSize() } w.lastHuffMan = false } if sync { w.lastHeader = 0 } // Write the tokens. w.writeTokens(tokens.Slice(), w.literalEncoding.codes, w.offsetEncoding.codes) } func (w *huffmanBitWriter) fillTokens() { for i, v := range w.literalFreq[:literalCount] { if v == 0 { w.literalFreq[i] = 1 } } for i, v := range w.offsetFreq[:offsetCodeCount] { if v == 0 { w.offsetFreq[i] = 1 } } } // indexTokens indexes a slice of tokens, and updates // literalFreq and offsetFreq, and generates literalEncoding // and offsetEncoding. // The number of literal and offset tokens is returned. func (w *huffmanBitWriter) indexTokens(t *tokens, filled bool) (numLiterals, numOffsets int) { //copy(w.literalFreq[:], t.litHist[:]) *(*[256]uint16)(w.literalFreq[:]) = t.litHist //copy(w.literalFreq[256:], t.extraHist[:]) *(*[32]uint16)(w.literalFreq[256:]) = t.extraHist w.offsetFreq = t.offHist if t.n == 0 { return } if filled { return maxNumLit, maxNumDist } // get the number of literals numLiterals = len(w.literalFreq) for w.literalFreq[numLiterals-1] == 0 { numLiterals-- } // get the number of offsets numOffsets = len(w.offsetFreq) for numOffsets > 0 && w.offsetFreq[numOffsets-1] == 0 { numOffsets-- } if numOffsets == 0 { // We haven't found a single match. If we want to go with the dynamic encoding, // we should count at least one offset to be sure that the offset huffman tree could be encoded. w.offsetFreq[0] = 1 numOffsets = 1 } return } func (w *huffmanBitWriter) generate() { w.literalEncoding.generate(w.literalFreq[:literalCount], 15) w.offsetEncoding.generate(w.offsetFreq[:offsetCodeCount], 15) } // writeTokens writes a slice of tokens to the output. // codes for literal and offset encoding must be supplied. func (w *huffmanBitWriter) writeTokens(tokens []token, leCodes, oeCodes []hcode) { if w.err != nil { return } if len(tokens) == 0 { return } // Only last token should be endBlockMarker. var deferEOB bool if tokens[len(tokens)-1] == endBlockMarker { tokens = tokens[:len(tokens)-1] deferEOB = true } // Create slices up to the next power of two to avoid bounds checks. lits := leCodes[:256] offs := oeCodes[:32] lengths := leCodes[lengthCodesStart:] lengths = lengths[:32] // Go 1.16 LOVES having these on stack. bits, nbits, nbytes := w.bits, w.nbits, w.nbytes for _, t := range tokens { if t < 256 { //w.writeCode(lits[t.literal()]) c := lits[t] bits |= c.code64() << (nbits & 63) nbits += c.len() if nbits >= 48 { binary.LittleEndian.PutUint64(w.bytes[nbytes:], bits) //*(*uint64)(unsafe.Pointer(&w.bytes[nbytes])) = bits bits >>= 48 nbits -= 48 nbytes += 6 if nbytes >= bufferFlushSize { if w.err != nil { nbytes = 0 return } _, w.err = w.writer.Write(w.bytes[:nbytes]) nbytes = 0 } } continue } // Write the length length := t.length() lengthCode := lengthCode(length) & 31 if false { w.writeCode(lengths[lengthCode]) } else { // inlined c := lengths[lengthCode] bits |= c.code64() << (nbits & 63) nbits += c.len() if nbits >= 48 { binary.LittleEndian.PutUint64(w.bytes[nbytes:], bits) //*(*uint64)(unsafe.Pointer(&w.bytes[nbytes])) = bits bits >>= 48 nbits -= 48 nbytes += 6 if nbytes >= bufferFlushSize { if w.err != nil { nbytes = 0 return } _, w.err = w.writer.Write(w.bytes[:nbytes]) nbytes = 0 } } } if lengthCode >= lengthExtraBitsMinCode { extraLengthBits := lengthExtraBits[lengthCode] //w.writeBits(extraLength, extraLengthBits) extraLength := int32(length - lengthBase[lengthCode]) bits |= uint64(extraLength) << (nbits & 63) nbits += extraLengthBits if nbits >= 48 { binary.LittleEndian.PutUint64(w.bytes[nbytes:], bits) //*(*uint64)(unsafe.Pointer(&w.bytes[nbytes])) = bits bits >>= 48 nbits -= 48 nbytes += 6 if nbytes >= bufferFlushSize { if w.err != nil { nbytes = 0 return } _, w.err = w.writer.Write(w.bytes[:nbytes]) nbytes = 0 } } } // Write the offset offset := t.offset() offsetCode := (offset >> 16) & 31 if false { w.writeCode(offs[offsetCode]) } else { // inlined c := offs[offsetCode] bits |= c.code64() << (nbits & 63) nbits += c.len() if nbits >= 48 { binary.LittleEndian.PutUint64(w.bytes[nbytes:], bits) //*(*uint64)(unsafe.Pointer(&w.bytes[nbytes])) = bits bits >>= 48 nbits -= 48 nbytes += 6 if nbytes >= bufferFlushSize { if w.err != nil { nbytes = 0 return } _, w.err = w.writer.Write(w.bytes[:nbytes]) nbytes = 0 } } } if offsetCode >= offsetExtraBitsMinCode { offsetComb := offsetCombined[offsetCode] //w.writeBits(extraOffset, extraOffsetBits) bits |= uint64((offset-(offsetComb>>8))&matchOffsetOnlyMask) << (nbits & 63) nbits += uint8(offsetComb) if nbits >= 48 { binary.LittleEndian.PutUint64(w.bytes[nbytes:], bits) //*(*uint64)(unsafe.Pointer(&w.bytes[nbytes])) = bits bits >>= 48 nbits -= 48 nbytes += 6 if nbytes >= bufferFlushSize { if w.err != nil { nbytes = 0 return } _, w.err = w.writer.Write(w.bytes[:nbytes]) nbytes = 0 } } } } // Restore... w.bits, w.nbits, w.nbytes = bits, nbits, nbytes if deferEOB { w.writeCode(leCodes[endBlockMarker]) } } // huffOffset is a static offset encoder used for huffman only encoding. // It can be reused since we will not be encoding offset values. var huffOffset *huffmanEncoder func init() { w := newHuffmanBitWriter(nil) w.offsetFreq[0] = 1 huffOffset = newHuffmanEncoder(offsetCodeCount) huffOffset.generate(w.offsetFreq[:offsetCodeCount], 15) } // writeBlockHuff encodes a block of bytes as either // Huffman encoded literals or uncompressed bytes if the // results only gains very little from compression. func (w *huffmanBitWriter) writeBlockHuff(eof bool, input []byte, sync bool) { if w.err != nil { return } // Clear histogram for i := range w.literalFreq[:] { w.literalFreq[i] = 0 } if !w.lastHuffMan { for i := range w.offsetFreq[:] { w.offsetFreq[i] = 0 } } const numLiterals = endBlockMarker + 1 const numOffsets = 1 // Add everything as literals // We have to estimate the header size. // Assume header is around 70 bytes: // https://stackoverflow.com/a/25454430 const guessHeaderSizeBits = 70 * 8 histogram(input, w.literalFreq[:numLiterals]) ssize, storable := w.storedSize(input) if storable && len(input) > 1024 { // Quick check for incompressible content. abs := float64(0) avg := float64(len(input)) / 256 max := float64(len(input) * 2) for _, v := range w.literalFreq[:256] { diff := float64(v) - avg abs += diff * diff if abs > max { break } } if abs < max { if debugDeflate { fmt.Println("stored", abs, "<", max) } // No chance we can compress this... w.writeStoredHeader(len(input), eof) w.writeBytes(input) return } } w.literalFreq[endBlockMarker] = 1 w.tmpLitEncoding.generate(w.literalFreq[:numLiterals], 15) estBits := w.tmpLitEncoding.canReuseBits(w.literalFreq[:numLiterals]) if estBits < math.MaxInt32 { estBits += w.lastHeader if w.lastHeader == 0 { estBits += guessHeaderSizeBits } estBits += estBits >> w.logNewTablePenalty } // Store bytes, if we don't get a reasonable improvement. if storable && ssize <= estBits { if debugDeflate { fmt.Println("stored,", ssize, "<=", estBits) } w.writeStoredHeader(len(input), eof) w.writeBytes(input) return } if w.lastHeader > 0 { reuseSize := w.literalEncoding.canReuseBits(w.literalFreq[:256]) if estBits < reuseSize { if debugDeflate { fmt.Println("NOT reusing, reuse:", reuseSize/8, "> new:", estBits/8, "header est:", w.lastHeader/8, "bytes") } // We owe an EOB w.writeCode(w.literalEncoding.codes[endBlockMarker]) w.lastHeader = 0 } else if debugDeflate { fmt.Println("reusing, reuse:", reuseSize/8, "> new:", estBits/8, "- header est:", w.lastHeader/8) } } count := 0 if w.lastHeader == 0 { // Use the temp encoding, so swap. w.literalEncoding, w.tmpLitEncoding = w.tmpLitEncoding, w.literalEncoding // Generate codegen and codegenFrequencies, which indicates how to encode // the literalEncoding and the offsetEncoding. w.generateCodegen(numLiterals, numOffsets, w.literalEncoding, huffOffset) w.codegenEncoding.generate(w.codegenFreq[:], 7) numCodegens := w.codegens() // Huffman. w.writeDynamicHeader(numLiterals, numOffsets, numCodegens, eof) w.lastHuffMan = true w.lastHeader, _ = w.headerSize() if debugDeflate { count += w.lastHeader fmt.Println("header:", count/8) } } encoding := w.literalEncoding.codes[:256] // Go 1.16 LOVES having these on stack. At least 1.5x the speed. bits, nbits, nbytes := w.bits, w.nbits, w.nbytes if debugDeflate { count -= int(nbytes)*8 + int(nbits) } // Unroll, write 3 codes/loop. // Fastest number of unrolls. for len(input) > 3 { // We must have at least 48 bits free. if nbits >= 8 { n := nbits >> 3 binary.LittleEndian.PutUint64(w.bytes[nbytes:], bits) bits >>= (n * 8) & 63 nbits -= n * 8 nbytes += n } if nbytes >= bufferFlushSize { if w.err != nil { nbytes = 0 return } if debugDeflate { count += int(nbytes) * 8 } _, w.err = w.writer.Write(w.bytes[:nbytes]) nbytes = 0 } a, b := encoding[input[0]], encoding[input[1]] bits |= a.code64() << (nbits & 63) bits |= b.code64() << ((nbits + a.len()) & 63) c := encoding[input[2]] nbits += b.len() + a.len() bits |= c.code64() << (nbits & 63) nbits += c.len() input = input[3:] } // Remaining... for _, t := range input { if nbits >= 48 { binary.LittleEndian.PutUint64(w.bytes[nbytes:], bits) //*(*uint64)(unsafe.Pointer(&w.bytes[nbytes])) = bits bits >>= 48 nbits -= 48 nbytes += 6 if nbytes >= bufferFlushSize { if w.err != nil { nbytes = 0 return } if debugDeflate { count += int(nbytes) * 8 } _, w.err = w.writer.Write(w.bytes[:nbytes]) nbytes = 0 } } // Bitwriting inlined, ~30% speedup c := encoding[t] bits |= c.code64() << (nbits & 63) nbits += c.len() if debugDeflate { count += int(c.len()) } } // Restore... w.bits, w.nbits, w.nbytes = bits, nbits, nbytes if debugDeflate { nb := count + int(nbytes)*8 + int(nbits) fmt.Println("wrote", nb, "bits,", nb/8, "bytes.") } // Flush if needed to have space. if w.nbits >= 48 { w.writeOutBits() } if eof || sync { w.writeCode(w.literalEncoding.codes[endBlockMarker]) w.lastHeader = 0 w.lastHuffMan = false } } ================================================ FILE: vendor/github.com/klauspost/compress/flate/huffman_code.go ================================================ // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package flate import ( "math" "math/bits" ) const ( maxBitsLimit = 16 // number of valid literals literalCount = 286 ) // hcode is a huffman code with a bit code and bit length. type hcode uint32 func (h hcode) len() uint8 { return uint8(h) } func (h hcode) code64() uint64 { return uint64(h >> 8) } func (h hcode) zero() bool { return h == 0 } type huffmanEncoder struct { codes []hcode bitCount [17]int32 // Allocate a reusable buffer with the longest possible frequency table. // Possible lengths are codegenCodeCount, offsetCodeCount and literalCount. // The largest of these is literalCount, so we allocate for that case. freqcache [literalCount + 1]literalNode } type literalNode struct { literal uint16 freq uint16 } // A levelInfo describes the state of the constructed tree for a given depth. type levelInfo struct { // Our level. for better printing level int32 // The frequency of the last node at this level lastFreq int32 // The frequency of the next character to add to this level nextCharFreq int32 // The frequency of the next pair (from level below) to add to this level. // Only valid if the "needed" value of the next lower level is 0. nextPairFreq int32 // The number of chains remaining to generate for this level before moving // up to the next level needed int32 } // set sets the code and length of an hcode. func (h *hcode) set(code uint16, length uint8) { *h = hcode(length) | (hcode(code) << 8) } func newhcode(code uint16, length uint8) hcode { return hcode(length) | (hcode(code) << 8) } func reverseBits(number uint16, bitLength byte) uint16 { return bits.Reverse16(number << ((16 - bitLength) & 15)) } func maxNode() literalNode { return literalNode{math.MaxUint16, math.MaxUint16} } func newHuffmanEncoder(size int) *huffmanEncoder { // Make capacity to next power of two. c := uint(bits.Len32(uint32(size - 1))) return &huffmanEncoder{codes: make([]hcode, size, 1<= 3 // The cases of 0, 1, and 2 literals are handled by special case code. // // list An array of the literals with non-zero frequencies // // and their associated frequencies. The array is in order of increasing // frequency, and has as its last element a special element with frequency // MaxInt32 // // maxBits The maximum number of bits that should be used to encode any literal. // // Must be less than 16. // // return An integer array in which array[i] indicates the number of literals // // that should be encoded in i bits. func (h *huffmanEncoder) bitCounts(list []literalNode, maxBits int32) []int32 { if maxBits >= maxBitsLimit { panic("flate: maxBits too large") } n := int32(len(list)) list = list[0 : n+1] list[n] = maxNode() // The tree can't have greater depth than n - 1, no matter what. This // saves a little bit of work in some small cases if maxBits > n-1 { maxBits = n - 1 } // Create information about each of the levels. // A bogus "Level 0" whose sole purpose is so that // level1.prev.needed==0. This makes level1.nextPairFreq // be a legitimate value that never gets chosen. var levels [maxBitsLimit]levelInfo // leafCounts[i] counts the number of literals at the left // of ancestors of the rightmost node at level i. // leafCounts[i][j] is the number of literals at the left // of the level j ancestor. var leafCounts [maxBitsLimit][maxBitsLimit]int32 // Descending to only have 1 bounds check. l2f := int32(list[2].freq) l1f := int32(list[1].freq) l0f := int32(list[0].freq) + int32(list[1].freq) for level := int32(1); level <= maxBits; level++ { // For every level, the first two items are the first two characters. // We initialize the levels as if we had already figured this out. levels[level] = levelInfo{ level: level, lastFreq: l1f, nextCharFreq: l2f, nextPairFreq: l0f, } leafCounts[level][level] = 2 if level == 1 { levels[level].nextPairFreq = math.MaxInt32 } } // We need a total of 2*n - 2 items at top level and have already generated 2. levels[maxBits].needed = 2*n - 4 level := uint32(maxBits) for level < 16 { l := &levels[level] if l.nextPairFreq == math.MaxInt32 && l.nextCharFreq == math.MaxInt32 { // We've run out of both leafs and pairs. // End all calculations for this level. // To make sure we never come back to this level or any lower level, // set nextPairFreq impossibly large. l.needed = 0 levels[level+1].nextPairFreq = math.MaxInt32 level++ continue } prevFreq := l.lastFreq if l.nextCharFreq < l.nextPairFreq { // The next item on this row is a leaf node. n := leafCounts[level][level] + 1 l.lastFreq = l.nextCharFreq // Lower leafCounts are the same of the previous node. leafCounts[level][level] = n e := list[n] if e.literal < math.MaxUint16 { l.nextCharFreq = int32(e.freq) } else { l.nextCharFreq = math.MaxInt32 } } else { // The next item on this row is a pair from the previous row. // nextPairFreq isn't valid until we generate two // more values in the level below l.lastFreq = l.nextPairFreq // Take leaf counts from the lower level, except counts[level] remains the same. if true { save := leafCounts[level][level] leafCounts[level] = leafCounts[level-1] leafCounts[level][level] = save } else { copy(leafCounts[level][:level], leafCounts[level-1][:level]) } levels[l.level-1].needed = 2 } if l.needed--; l.needed == 0 { // We've done everything we need to do for this level. // Continue calculating one level up. Fill in nextPairFreq // of that level with the sum of the two nodes we've just calculated on // this level. if l.level == maxBits { // All done! break } levels[l.level+1].nextPairFreq = prevFreq + l.lastFreq level++ } else { // If we stole from below, move down temporarily to replenish it. for levels[level-1].needed > 0 { level-- } } } // Somethings is wrong if at the end, the top level is null or hasn't used // all of the leaves. if leafCounts[maxBits][maxBits] != n { panic("leafCounts[maxBits][maxBits] != n") } bitCount := h.bitCount[:maxBits+1] bits := 1 counts := &leafCounts[maxBits] for level := maxBits; level > 0; level-- { // chain.leafCount gives the number of literals requiring at least "bits" // bits to encode. bitCount[bits] = counts[level] - counts[level-1] bits++ } return bitCount } // Look at the leaves and assign them a bit count and an encoding as specified // in RFC 1951 3.2.2 func (h *huffmanEncoder) assignEncodingAndSize(bitCount []int32, list []literalNode) { code := uint16(0) for n, bits := range bitCount { code <<= 1 if n == 0 || bits == 0 { continue } // The literals list[len(list)-bits] .. list[len(list)-bits] // are encoded using "bits" bits, and get the values // code, code + 1, .... The code values are // assigned in literal order (not frequency order). chunk := list[len(list)-int(bits):] sortByLiteral(chunk) for _, node := range chunk { h.codes[node.literal] = newhcode(reverseBits(code, uint8(n)), uint8(n)) code++ } list = list[0 : len(list)-int(bits)] } } // Update this Huffman Code object to be the minimum code for the specified frequency count. // // freq An array of frequencies, in which frequency[i] gives the frequency of literal i. // maxBits The maximum number of bits to use for any literal. func (h *huffmanEncoder) generate(freq []uint16, maxBits int32) { list := h.freqcache[:len(freq)+1] codes := h.codes[:len(freq)] // Number of non-zero literals count := 0 // Set list to be the set of all non-zero literals and their frequencies for i, f := range freq { if f != 0 { list[count] = literalNode{uint16(i), f} count++ } else { codes[i] = 0 } } list[count] = literalNode{} list = list[:count] if count <= 2 { // Handle the small cases here, because they are awkward for the general case code. With // two or fewer literals, everything has bit length 1. for i, node := range list { // "list" is in order of increasing literal value. h.codes[node.literal].set(uint16(i), 1) } return } sortByFreq(list) // Get the number of literals for each bit count bitCount := h.bitCounts(list, maxBits) // And do the assignment h.assignEncodingAndSize(bitCount, list) } // atLeastOne clamps the result between 1 and 15. func atLeastOne(v float32) float32 { if v < 1 { return 1 } if v > 15 { return 15 } return v } func histogram(b []byte, h []uint16) { if true && len(b) >= 8<<10 { // Split for bigger inputs histogramSplit(b, h) } else { h = h[:256] for _, t := range b { h[t]++ } } } func histogramSplit(b []byte, h []uint16) { // Tested, and slightly faster than 2-way. // Writing to separate arrays and combining is also slightly slower. h = h[:256] for len(b)&3 != 0 { h[b[0]]++ b = b[1:] } n := len(b) / 4 x, y, z, w := b[:n], b[n:], b[n+n:], b[n+n+n:] y, z, w = y[:len(x)], z[:len(x)], w[:len(x)] for i, t := range x { v0 := &h[t] v1 := &h[y[i]] v3 := &h[w[i]] v2 := &h[z[i]] *v0++ *v1++ *v2++ *v3++ } } ================================================ FILE: vendor/github.com/klauspost/compress/flate/huffman_sortByFreq.go ================================================ // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package flate // Sort sorts data. // It makes one call to data.Len to determine n, and O(n*log(n)) calls to // data.Less and data.Swap. The sort is not guaranteed to be stable. func sortByFreq(data []literalNode) { n := len(data) quickSortByFreq(data, 0, n, maxDepth(n)) } func quickSortByFreq(data []literalNode, a, b, maxDepth int) { for b-a > 12 { // Use ShellSort for slices <= 12 elements if maxDepth == 0 { heapSort(data, a, b) return } maxDepth-- mlo, mhi := doPivotByFreq(data, a, b) // Avoiding recursion on the larger subproblem guarantees // a stack depth of at most lg(b-a). if mlo-a < b-mhi { quickSortByFreq(data, a, mlo, maxDepth) a = mhi // i.e., quickSortByFreq(data, mhi, b) } else { quickSortByFreq(data, mhi, b, maxDepth) b = mlo // i.e., quickSortByFreq(data, a, mlo) } } if b-a > 1 { // Do ShellSort pass with gap 6 // It could be written in this simplified form cause b-a <= 12 for i := a + 6; i < b; i++ { if data[i].freq == data[i-6].freq && data[i].literal < data[i-6].literal || data[i].freq < data[i-6].freq { data[i], data[i-6] = data[i-6], data[i] } } insertionSortByFreq(data, a, b) } } func doPivotByFreq(data []literalNode, lo, hi int) (midlo, midhi int) { m := int(uint(lo+hi) >> 1) // Written like this to avoid integer overflow. if hi-lo > 40 { // Tukey's ``Ninther,'' median of three medians of three. s := (hi - lo) / 8 medianOfThreeSortByFreq(data, lo, lo+s, lo+2*s) medianOfThreeSortByFreq(data, m, m-s, m+s) medianOfThreeSortByFreq(data, hi-1, hi-1-s, hi-1-2*s) } medianOfThreeSortByFreq(data, lo, m, hi-1) // Invariants are: // data[lo] = pivot (set up by ChoosePivot) // data[lo < i < a] < pivot // data[a <= i < b] <= pivot // data[b <= i < c] unexamined // data[c <= i < hi-1] > pivot // data[hi-1] >= pivot pivot := lo a, c := lo+1, hi-1 for ; a < c && (data[a].freq == data[pivot].freq && data[a].literal < data[pivot].literal || data[a].freq < data[pivot].freq); a++ { } b := a for { for ; b < c && (data[pivot].freq == data[b].freq && data[pivot].literal > data[b].literal || data[pivot].freq > data[b].freq); b++ { // data[b] <= pivot } for ; b < c && (data[pivot].freq == data[c-1].freq && data[pivot].literal < data[c-1].literal || data[pivot].freq < data[c-1].freq); c-- { // data[c-1] > pivot } if b >= c { break } // data[b] > pivot; data[c-1] <= pivot data[b], data[c-1] = data[c-1], data[b] b++ c-- } // If hi-c<3 then there are duplicates (by property of median of nine). // Let's be a bit more conservative, and set border to 5. protect := hi-c < 5 if !protect && hi-c < (hi-lo)/4 { // Lets test some points for equality to pivot dups := 0 if data[pivot].freq == data[hi-1].freq && data[pivot].literal > data[hi-1].literal || data[pivot].freq > data[hi-1].freq { // data[hi-1] = pivot data[c], data[hi-1] = data[hi-1], data[c] c++ dups++ } if data[b-1].freq == data[pivot].freq && data[b-1].literal > data[pivot].literal || data[b-1].freq > data[pivot].freq { // data[b-1] = pivot b-- dups++ } // m-lo = (hi-lo)/2 > 6 // b-lo > (hi-lo)*3/4-1 > 8 // ==> m < b ==> data[m] <= pivot if data[m].freq == data[pivot].freq && data[m].literal > data[pivot].literal || data[m].freq > data[pivot].freq { // data[m] = pivot data[m], data[b-1] = data[b-1], data[m] b-- dups++ } // if at least 2 points are equal to pivot, assume skewed distribution protect = dups > 1 } if protect { // Protect against a lot of duplicates // Add invariant: // data[a <= i < b] unexamined // data[b <= i < c] = pivot for { for ; a < b && (data[b-1].freq == data[pivot].freq && data[b-1].literal > data[pivot].literal || data[b-1].freq > data[pivot].freq); b-- { // data[b] == pivot } for ; a < b && (data[a].freq == data[pivot].freq && data[a].literal < data[pivot].literal || data[a].freq < data[pivot].freq); a++ { // data[a] < pivot } if a >= b { break } // data[a] == pivot; data[b-1] < pivot data[a], data[b-1] = data[b-1], data[a] a++ b-- } } // Swap pivot into middle data[pivot], data[b-1] = data[b-1], data[pivot] return b - 1, c } // Insertion sort func insertionSortByFreq(data []literalNode, a, b int) { for i := a + 1; i < b; i++ { for j := i; j > a && (data[j].freq == data[j-1].freq && data[j].literal < data[j-1].literal || data[j].freq < data[j-1].freq); j-- { data[j], data[j-1] = data[j-1], data[j] } } } // quickSortByFreq, loosely following Bentley and McIlroy, // ``Engineering a Sort Function,'' SP&E November 1993. // medianOfThreeSortByFreq moves the median of the three values data[m0], data[m1], data[m2] into data[m1]. func medianOfThreeSortByFreq(data []literalNode, m1, m0, m2 int) { // sort 3 elements if data[m1].freq == data[m0].freq && data[m1].literal < data[m0].literal || data[m1].freq < data[m0].freq { data[m1], data[m0] = data[m0], data[m1] } // data[m0] <= data[m1] if data[m2].freq == data[m1].freq && data[m2].literal < data[m1].literal || data[m2].freq < data[m1].freq { data[m2], data[m1] = data[m1], data[m2] // data[m0] <= data[m2] && data[m1] < data[m2] if data[m1].freq == data[m0].freq && data[m1].literal < data[m0].literal || data[m1].freq < data[m0].freq { data[m1], data[m0] = data[m0], data[m1] } } // now data[m0] <= data[m1] <= data[m2] } ================================================ FILE: vendor/github.com/klauspost/compress/flate/huffman_sortByLiteral.go ================================================ // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package flate // Sort sorts data. // It makes one call to data.Len to determine n, and O(n*log(n)) calls to // data.Less and data.Swap. The sort is not guaranteed to be stable. func sortByLiteral(data []literalNode) { n := len(data) quickSort(data, 0, n, maxDepth(n)) } func quickSort(data []literalNode, a, b, maxDepth int) { for b-a > 12 { // Use ShellSort for slices <= 12 elements if maxDepth == 0 { heapSort(data, a, b) return } maxDepth-- mlo, mhi := doPivot(data, a, b) // Avoiding recursion on the larger subproblem guarantees // a stack depth of at most lg(b-a). if mlo-a < b-mhi { quickSort(data, a, mlo, maxDepth) a = mhi // i.e., quickSort(data, mhi, b) } else { quickSort(data, mhi, b, maxDepth) b = mlo // i.e., quickSort(data, a, mlo) } } if b-a > 1 { // Do ShellSort pass with gap 6 // It could be written in this simplified form cause b-a <= 12 for i := a + 6; i < b; i++ { if data[i].literal < data[i-6].literal { data[i], data[i-6] = data[i-6], data[i] } } insertionSort(data, a, b) } } func heapSort(data []literalNode, a, b int) { first := a lo := 0 hi := b - a // Build heap with greatest element at top. for i := (hi - 1) / 2; i >= 0; i-- { siftDown(data, i, hi, first) } // Pop elements, largest first, into end of data. for i := hi - 1; i >= 0; i-- { data[first], data[first+i] = data[first+i], data[first] siftDown(data, lo, i, first) } } // siftDown implements the heap property on data[lo, hi). // first is an offset into the array where the root of the heap lies. func siftDown(data []literalNode, lo, hi, first int) { root := lo for { child := 2*root + 1 if child >= hi { break } if child+1 < hi && data[first+child].literal < data[first+child+1].literal { child++ } if data[first+root].literal > data[first+child].literal { return } data[first+root], data[first+child] = data[first+child], data[first+root] root = child } } func doPivot(data []literalNode, lo, hi int) (midlo, midhi int) { m := int(uint(lo+hi) >> 1) // Written like this to avoid integer overflow. if hi-lo > 40 { // Tukey's ``Ninther,'' median of three medians of three. s := (hi - lo) / 8 medianOfThree(data, lo, lo+s, lo+2*s) medianOfThree(data, m, m-s, m+s) medianOfThree(data, hi-1, hi-1-s, hi-1-2*s) } medianOfThree(data, lo, m, hi-1) // Invariants are: // data[lo] = pivot (set up by ChoosePivot) // data[lo < i < a] < pivot // data[a <= i < b] <= pivot // data[b <= i < c] unexamined // data[c <= i < hi-1] > pivot // data[hi-1] >= pivot pivot := lo a, c := lo+1, hi-1 for ; a < c && data[a].literal < data[pivot].literal; a++ { } b := a for { for ; b < c && data[pivot].literal > data[b].literal; b++ { // data[b] <= pivot } for ; b < c && data[pivot].literal < data[c-1].literal; c-- { // data[c-1] > pivot } if b >= c { break } // data[b] > pivot; data[c-1] <= pivot data[b], data[c-1] = data[c-1], data[b] b++ c-- } // If hi-c<3 then there are duplicates (by property of median of nine). // Let's be a bit more conservative, and set border to 5. protect := hi-c < 5 if !protect && hi-c < (hi-lo)/4 { // Lets test some points for equality to pivot dups := 0 if data[pivot].literal > data[hi-1].literal { // data[hi-1] = pivot data[c], data[hi-1] = data[hi-1], data[c] c++ dups++ } if data[b-1].literal > data[pivot].literal { // data[b-1] = pivot b-- dups++ } // m-lo = (hi-lo)/2 > 6 // b-lo > (hi-lo)*3/4-1 > 8 // ==> m < b ==> data[m] <= pivot if data[m].literal > data[pivot].literal { // data[m] = pivot data[m], data[b-1] = data[b-1], data[m] b-- dups++ } // if at least 2 points are equal to pivot, assume skewed distribution protect = dups > 1 } if protect { // Protect against a lot of duplicates // Add invariant: // data[a <= i < b] unexamined // data[b <= i < c] = pivot for { for ; a < b && data[b-1].literal > data[pivot].literal; b-- { // data[b] == pivot } for ; a < b && data[a].literal < data[pivot].literal; a++ { // data[a] < pivot } if a >= b { break } // data[a] == pivot; data[b-1] < pivot data[a], data[b-1] = data[b-1], data[a] a++ b-- } } // Swap pivot into middle data[pivot], data[b-1] = data[b-1], data[pivot] return b - 1, c } // Insertion sort func insertionSort(data []literalNode, a, b int) { for i := a + 1; i < b; i++ { for j := i; j > a && data[j].literal < data[j-1].literal; j-- { data[j], data[j-1] = data[j-1], data[j] } } } // maxDepth returns a threshold at which quicksort should switch // to heapsort. It returns 2*ceil(lg(n+1)). func maxDepth(n int) int { var depth int for i := n; i > 0; i >>= 1 { depth++ } return depth * 2 } // medianOfThree moves the median of the three values data[m0], data[m1], data[m2] into data[m1]. func medianOfThree(data []literalNode, m1, m0, m2 int) { // sort 3 elements if data[m1].literal < data[m0].literal { data[m1], data[m0] = data[m0], data[m1] } // data[m0] <= data[m1] if data[m2].literal < data[m1].literal { data[m2], data[m1] = data[m1], data[m2] // data[m0] <= data[m2] && data[m1] < data[m2] if data[m1].literal < data[m0].literal { data[m1], data[m0] = data[m0], data[m1] } } // now data[m0] <= data[m1] <= data[m2] } ================================================ FILE: vendor/github.com/klauspost/compress/flate/inflate.go ================================================ // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package flate implements the DEFLATE compressed data format, described in // RFC 1951. The gzip and zlib packages implement access to DEFLATE-based file // formats. package flate import ( "bufio" "compress/flate" "fmt" "io" "math/bits" "sync" ) const ( maxCodeLen = 16 // max length of Huffman code maxCodeLenMask = 15 // mask for max length of Huffman code // The next three numbers come from the RFC section 3.2.7, with the // additional proviso in section 3.2.5 which implies that distance codes // 30 and 31 should never occur in compressed data. maxNumLit = 286 maxNumDist = 30 numCodes = 19 // number of codes in Huffman meta-code debugDecode = false ) // Value of length - 3 and extra bits. type lengthExtra struct { length, extra uint8 } var decCodeToLen = [32]lengthExtra{{length: 0x0, extra: 0x0}, {length: 0x1, extra: 0x0}, {length: 0x2, extra: 0x0}, {length: 0x3, extra: 0x0}, {length: 0x4, extra: 0x0}, {length: 0x5, extra: 0x0}, {length: 0x6, extra: 0x0}, {length: 0x7, extra: 0x0}, {length: 0x8, extra: 0x1}, {length: 0xa, extra: 0x1}, {length: 0xc, extra: 0x1}, {length: 0xe, extra: 0x1}, {length: 0x10, extra: 0x2}, {length: 0x14, extra: 0x2}, {length: 0x18, extra: 0x2}, {length: 0x1c, extra: 0x2}, {length: 0x20, extra: 0x3}, {length: 0x28, extra: 0x3}, {length: 0x30, extra: 0x3}, {length: 0x38, extra: 0x3}, {length: 0x40, extra: 0x4}, {length: 0x50, extra: 0x4}, {length: 0x60, extra: 0x4}, {length: 0x70, extra: 0x4}, {length: 0x80, extra: 0x5}, {length: 0xa0, extra: 0x5}, {length: 0xc0, extra: 0x5}, {length: 0xe0, extra: 0x5}, {length: 0xff, extra: 0x0}, {length: 0x0, extra: 0x0}, {length: 0x0, extra: 0x0}, {length: 0x0, extra: 0x0}} var bitMask32 = [32]uint32{ 0, 1, 3, 7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0x1ffff, 0x3ffff, 0x7FFFF, 0xfFFFF, 0x1fFFFF, 0x3fFFFF, 0x7fFFFF, 0xffFFFF, 0x1ffFFFF, 0x3ffFFFF, 0x7ffFFFF, 0xfffFFFF, 0x1fffFFFF, 0x3fffFFFF, 0x7fffFFFF, } // up to 32 bits // Initialize the fixedHuffmanDecoder only once upon first use. var fixedOnce sync.Once var fixedHuffmanDecoder huffmanDecoder // A CorruptInputError reports the presence of corrupt input at a given offset. type CorruptInputError = flate.CorruptInputError // An InternalError reports an error in the flate code itself. type InternalError string func (e InternalError) Error() string { return "flate: internal error: " + string(e) } // A ReadError reports an error encountered while reading input. // // Deprecated: No longer returned. type ReadError = flate.ReadError // A WriteError reports an error encountered while writing output. // // Deprecated: No longer returned. type WriteError = flate.WriteError // Resetter resets a ReadCloser returned by NewReader or NewReaderDict to // to switch to a new underlying Reader. This permits reusing a ReadCloser // instead of allocating a new one. type Resetter interface { // Reset discards any buffered data and resets the Resetter as if it was // newly initialized with the given reader. Reset(r io.Reader, dict []byte) error } // The data structure for decoding Huffman tables is based on that of // zlib. There is a lookup table of a fixed bit width (huffmanChunkBits), // For codes smaller than the table width, there are multiple entries // (each combination of trailing bits has the same value). For codes // larger than the table width, the table contains a link to an overflow // table. The width of each entry in the link table is the maximum code // size minus the chunk width. // // Note that you can do a lookup in the table even without all bits // filled. Since the extra bits are zero, and the DEFLATE Huffman codes // have the property that shorter codes come before longer ones, the // bit length estimate in the result is a lower bound on the actual // number of bits. // // See the following: // http://www.gzip.org/algorithm.txt // chunk & 15 is number of bits // chunk >> 4 is value, including table link const ( huffmanChunkBits = 9 huffmanNumChunks = 1 << huffmanChunkBits huffmanCountMask = 15 huffmanValueShift = 4 ) type huffmanDecoder struct { maxRead int // the maximum number of bits we can read and not overread chunks *[huffmanNumChunks]uint16 // chunks as described above links [][]uint16 // overflow links linkMask uint32 // mask the width of the link table } // Initialize Huffman decoding tables from array of code lengths. // Following this function, h is guaranteed to be initialized into a complete // tree (i.e., neither over-subscribed nor under-subscribed). The exception is a // degenerate case where the tree has only a single symbol with length 1. Empty // trees are permitted. func (h *huffmanDecoder) init(lengths []int) bool { // Sanity enables additional runtime tests during Huffman // table construction. It's intended to be used during // development to supplement the currently ad-hoc unit tests. const sanity = false if h.chunks == nil { h.chunks = new([huffmanNumChunks]uint16) } if h.maxRead != 0 { *h = huffmanDecoder{chunks: h.chunks, links: h.links} } // Count number of codes of each length, // compute maxRead and max length. var count [maxCodeLen]int var min, max int for _, n := range lengths { if n == 0 { continue } if min == 0 || n < min { min = n } if n > max { max = n } count[n&maxCodeLenMask]++ } // Empty tree. The decompressor.huffSym function will fail later if the tree // is used. Technically, an empty tree is only valid for the HDIST tree and // not the HCLEN and HLIT tree. However, a stream with an empty HCLEN tree // is guaranteed to fail since it will attempt to use the tree to decode the // codes for the HLIT and HDIST trees. Similarly, an empty HLIT tree is // guaranteed to fail later since the compressed data section must be // composed of at least one symbol (the end-of-block marker). if max == 0 { return true } code := 0 var nextcode [maxCodeLen]int for i := min; i <= max; i++ { code <<= 1 nextcode[i&maxCodeLenMask] = code code += count[i&maxCodeLenMask] } // Check that the coding is complete (i.e., that we've // assigned all 2-to-the-max possible bit sequences). // Exception: To be compatible with zlib, we also need to // accept degenerate single-code codings. See also // TestDegenerateHuffmanCoding. if code != 1< huffmanChunkBits { numLinks := 1 << (uint(max) - huffmanChunkBits) h.linkMask = uint32(numLinks - 1) // create link tables link := nextcode[huffmanChunkBits+1] >> 1 if cap(h.links) < huffmanNumChunks-link { h.links = make([][]uint16, huffmanNumChunks-link) } else { h.links = h.links[:huffmanNumChunks-link] } for j := uint(link); j < huffmanNumChunks; j++ { reverse := int(bits.Reverse16(uint16(j))) reverse >>= uint(16 - huffmanChunkBits) off := j - uint(link) if sanity && h.chunks[reverse] != 0 { panic("impossible: overwriting existing chunk") } h.chunks[reverse] = uint16(off<>= uint(16 - n) if n <= huffmanChunkBits { for off := reverse; off < len(h.chunks); off += 1 << uint(n) { // We should never need to overwrite // an existing chunk. Also, 0 is // never a valid chunk, because the // lower 4 "count" bits should be // between 1 and 15. if sanity && h.chunks[off] != 0 { panic("impossible: overwriting existing chunk") } h.chunks[off] = chunk } } else { j := reverse & (huffmanNumChunks - 1) if sanity && h.chunks[j]&huffmanCountMask != huffmanChunkBits+1 { // Longer codes should have been // associated with a link table above. panic("impossible: not an indirect chunk") } value := h.chunks[j] >> huffmanValueShift linktab := h.links[value] reverse >>= huffmanChunkBits for off := reverse; off < len(linktab); off += 1 << uint(n-huffmanChunkBits) { if sanity && linktab[off] != 0 { panic("impossible: overwriting existing chunk") } linktab[off] = chunk } } } if sanity { // Above we've sanity checked that we never overwrote // an existing entry. Here we additionally check that // we filled the tables completely. for i, chunk := range h.chunks { if chunk == 0 { // As an exception, in the degenerate // single-code case, we allow odd // chunks to be missing. if code == 1 && i%2 == 1 { continue } panic("impossible: missing chunk") } } for _, linktab := range h.links { for _, chunk := range linktab { if chunk == 0 { panic("impossible: missing chunk") } } } } return true } // Reader is the actual read interface needed by NewReader. // If the passed in io.Reader does not also have ReadByte, // the NewReader will introduce its own buffering. type Reader interface { io.Reader io.ByteReader } type step uint8 const ( copyData step = iota + 1 nextBlock huffmanBytesBuffer huffmanBytesReader huffmanBufioReader huffmanStringsReader huffmanGenericReader ) // Decompress state. type decompressor struct { // Input source. r Reader roffset int64 // Huffman decoders for literal/length, distance. h1, h2 huffmanDecoder // Length arrays used to define Huffman codes. bits *[maxNumLit + maxNumDist]int codebits *[numCodes]int // Output history, buffer. dict dictDecoder // Next step in the decompression, // and decompression state. step step stepState int err error toRead []byte hl, hd *huffmanDecoder copyLen int copyDist int // Temporary buffer (avoids repeated allocation). buf [4]byte // Input bits, in top of b. b uint32 nb uint final bool } func (f *decompressor) nextBlock() { for f.nb < 1+2 { if f.err = f.moreBits(); f.err != nil { return } } f.final = f.b&1 == 1 f.b >>= 1 typ := f.b & 3 f.b >>= 2 f.nb -= 1 + 2 switch typ { case 0: f.dataBlock() if debugDecode { fmt.Println("stored block") } case 1: // compressed, fixed Huffman tables f.hl = &fixedHuffmanDecoder f.hd = nil f.huffmanBlockDecoder() if debugDecode { fmt.Println("predefinied huffman block") } case 2: // compressed, dynamic Huffman tables if f.err = f.readHuffman(); f.err != nil { break } f.hl = &f.h1 f.hd = &f.h2 f.huffmanBlockDecoder() if debugDecode { fmt.Println("dynamic huffman block") } default: // 3 is reserved. if debugDecode { fmt.Println("reserved data block encountered") } f.err = CorruptInputError(f.roffset) } } func (f *decompressor) Read(b []byte) (int, error) { for { if len(f.toRead) > 0 { n := copy(b, f.toRead) f.toRead = f.toRead[n:] if len(f.toRead) == 0 { return n, f.err } return n, nil } if f.err != nil { return 0, f.err } f.doStep() if f.err != nil && len(f.toRead) == 0 { f.toRead = f.dict.readFlush() // Flush what's left in case of error } } } // WriteTo implements the io.WriteTo interface for io.Copy and friends. func (f *decompressor) WriteTo(w io.Writer) (int64, error) { total := int64(0) flushed := false for { if len(f.toRead) > 0 { n, err := w.Write(f.toRead) total += int64(n) if err != nil { f.err = err return total, err } if n != len(f.toRead) { return total, io.ErrShortWrite } f.toRead = f.toRead[:0] } if f.err != nil && flushed { if f.err == io.EOF { return total, nil } return total, f.err } if f.err == nil { f.doStep() } if len(f.toRead) == 0 && f.err != nil && !flushed { f.toRead = f.dict.readFlush() // Flush what's left in case of error flushed = true } } } func (f *decompressor) Close() error { if f.err == io.EOF { return nil } return f.err } // RFC 1951 section 3.2.7. // Compression with dynamic Huffman codes var codeOrder = [...]int{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15} func (f *decompressor) readHuffman() error { // HLIT[5], HDIST[5], HCLEN[4]. for f.nb < 5+5+4 { if err := f.moreBits(); err != nil { return err } } nlit := int(f.b&0x1F) + 257 if nlit > maxNumLit { if debugDecode { fmt.Println("nlit > maxNumLit", nlit) } return CorruptInputError(f.roffset) } f.b >>= 5 ndist := int(f.b&0x1F) + 1 if ndist > maxNumDist { if debugDecode { fmt.Println("ndist > maxNumDist", ndist) } return CorruptInputError(f.roffset) } f.b >>= 5 nclen := int(f.b&0xF) + 4 // numCodes is 19, so nclen is always valid. f.b >>= 4 f.nb -= 5 + 5 + 4 // (HCLEN+4)*3 bits: code lengths in the magic codeOrder order. for i := 0; i < nclen; i++ { for f.nb < 3 { if err := f.moreBits(); err != nil { return err } } f.codebits[codeOrder[i]] = int(f.b & 0x7) f.b >>= 3 f.nb -= 3 } for i := nclen; i < len(codeOrder); i++ { f.codebits[codeOrder[i]] = 0 } if !f.h1.init(f.codebits[0:]) { if debugDecode { fmt.Println("init codebits failed") } return CorruptInputError(f.roffset) } // HLIT + 257 code lengths, HDIST + 1 code lengths, // using the code length Huffman code. for i, n := 0, nlit+ndist; i < n; { x, err := f.huffSym(&f.h1) if err != nil { return err } if x < 16 { // Actual length. f.bits[i] = x i++ continue } // Repeat previous length or zero. var rep int var nb uint var b int switch x { default: return InternalError("unexpected length code") case 16: rep = 3 nb = 2 if i == 0 { if debugDecode { fmt.Println("i==0") } return CorruptInputError(f.roffset) } b = f.bits[i-1] case 17: rep = 3 nb = 3 b = 0 case 18: rep = 11 nb = 7 b = 0 } for f.nb < nb { if err := f.moreBits(); err != nil { if debugDecode { fmt.Println("morebits:", err) } return err } } rep += int(f.b & uint32(1<<(nb®SizeMaskUint32)-1)) f.b >>= nb & regSizeMaskUint32 f.nb -= nb if i+rep > n { if debugDecode { fmt.Println("i+rep > n", i, rep, n) } return CorruptInputError(f.roffset) } for j := 0; j < rep; j++ { f.bits[i] = b i++ } } if !f.h1.init(f.bits[0:nlit]) || !f.h2.init(f.bits[nlit:nlit+ndist]) { if debugDecode { fmt.Println("init2 failed") } return CorruptInputError(f.roffset) } // As an optimization, we can initialize the maxRead bits to read at a time // for the HLIT tree to the length of the EOB marker since we know that // every block must terminate with one. This preserves the property that // we never read any extra bytes after the end of the DEFLATE stream. if f.h1.maxRead < f.bits[endBlockMarker] { f.h1.maxRead = f.bits[endBlockMarker] } if !f.final { // If not the final block, the smallest block possible is // a predefined table, BTYPE=01, with a single EOB marker. // This will take up 3 + 7 bits. f.h1.maxRead += 10 } return nil } // Copy a single uncompressed data block from input to output. func (f *decompressor) dataBlock() { // Uncompressed. // Discard current half-byte. left := (f.nb) & 7 f.nb -= left f.b >>= left offBytes := f.nb >> 3 // Unfilled values will be overwritten. f.buf[0] = uint8(f.b) f.buf[1] = uint8(f.b >> 8) f.buf[2] = uint8(f.b >> 16) f.buf[3] = uint8(f.b >> 24) f.roffset += int64(offBytes) f.nb, f.b = 0, 0 // Length then ones-complement of length. nr, err := io.ReadFull(f.r, f.buf[offBytes:4]) f.roffset += int64(nr) if err != nil { f.err = noEOF(err) return } n := uint16(f.buf[0]) | uint16(f.buf[1])<<8 nn := uint16(f.buf[2]) | uint16(f.buf[3])<<8 if nn != ^n { if debugDecode { ncomp := ^n fmt.Println("uint16(nn) != uint16(^n)", nn, ncomp) } f.err = CorruptInputError(f.roffset) return } if n == 0 { f.toRead = f.dict.readFlush() f.finishBlock() return } f.copyLen = int(n) f.copyData() } // copyData copies f.copyLen bytes from the underlying reader into f.hist. // It pauses for reads when f.hist is full. func (f *decompressor) copyData() { buf := f.dict.writeSlice() if len(buf) > f.copyLen { buf = buf[:f.copyLen] } cnt, err := io.ReadFull(f.r, buf) f.roffset += int64(cnt) f.copyLen -= cnt f.dict.writeMark(cnt) if err != nil { f.err = noEOF(err) return } if f.dict.availWrite() == 0 || f.copyLen > 0 { f.toRead = f.dict.readFlush() f.step = copyData return } f.finishBlock() } func (f *decompressor) finishBlock() { if f.final { if f.dict.availRead() > 0 { f.toRead = f.dict.readFlush() } f.err = io.EOF } f.step = nextBlock } func (f *decompressor) doStep() { switch f.step { case copyData: f.copyData() case nextBlock: f.nextBlock() case huffmanBytesBuffer: f.huffmanBytesBuffer() case huffmanBytesReader: f.huffmanBytesReader() case huffmanBufioReader: f.huffmanBufioReader() case huffmanStringsReader: f.huffmanStringsReader() case huffmanGenericReader: f.huffmanGenericReader() default: panic("BUG: unexpected step state") } } // noEOF returns err, unless err == io.EOF, in which case it returns io.ErrUnexpectedEOF. func noEOF(e error) error { if e == io.EOF { return io.ErrUnexpectedEOF } return e } func (f *decompressor) moreBits() error { c, err := f.r.ReadByte() if err != nil { return noEOF(err) } f.roffset++ f.b |= uint32(c) << (f.nb & regSizeMaskUint32) f.nb += 8 return nil } // Read the next Huffman-encoded symbol from f according to h. func (f *decompressor) huffSym(h *huffmanDecoder) (int, error) { // Since a huffmanDecoder can be empty or be composed of a degenerate tree // with single element, huffSym must error on these two edge cases. In both // cases, the chunks slice will be 0 for the invalid sequence, leading it // satisfy the n == 0 check below. n := uint(h.maxRead) // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, // but is smart enough to keep local variables in registers, so use nb and b, // inline call to moreBits and reassign b,nb back to f on return. nb, b := f.nb, f.b for { for nb < n { c, err := f.r.ReadByte() if err != nil { f.b = b f.nb = nb return 0, noEOF(err) } f.roffset++ b |= uint32(c) << (nb & regSizeMaskUint32) nb += 8 } chunk := h.chunks[b&(huffmanNumChunks-1)] n = uint(chunk & huffmanCountMask) if n > huffmanChunkBits { chunk = h.links[chunk>>huffmanValueShift][(b>>huffmanChunkBits)&h.linkMask] n = uint(chunk & huffmanCountMask) } if n <= nb { if n == 0 { f.b = b f.nb = nb if debugDecode { fmt.Println("huffsym: n==0") } f.err = CorruptInputError(f.roffset) return 0, f.err } f.b = b >> (n & regSizeMaskUint32) f.nb = nb - n return int(chunk >> huffmanValueShift), nil } } } func makeReader(r io.Reader) Reader { if rr, ok := r.(Reader); ok { return rr } return bufio.NewReader(r) } func fixedHuffmanDecoderInit() { fixedOnce.Do(func() { // These come from the RFC section 3.2.6. var bits [288]int for i := 0; i < 144; i++ { bits[i] = 8 } for i := 144; i < 256; i++ { bits[i] = 9 } for i := 256; i < 280; i++ { bits[i] = 7 } for i := 280; i < 288; i++ { bits[i] = 8 } fixedHuffmanDecoder.init(bits[:]) }) } func (f *decompressor) Reset(r io.Reader, dict []byte) error { *f = decompressor{ r: makeReader(r), bits: f.bits, codebits: f.codebits, h1: f.h1, h2: f.h2, dict: f.dict, step: nextBlock, } f.dict.init(maxMatchOffset, dict) return nil } // NewReader returns a new ReadCloser that can be used // to read the uncompressed version of r. // If r does not also implement io.ByteReader, // the decompressor may read more data than necessary from r. // It is the caller's responsibility to call Close on the ReadCloser // when finished reading. // // The ReadCloser returned by NewReader also implements Resetter. func NewReader(r io.Reader) io.ReadCloser { fixedHuffmanDecoderInit() var f decompressor f.r = makeReader(r) f.bits = new([maxNumLit + maxNumDist]int) f.codebits = new([numCodes]int) f.step = nextBlock f.dict.init(maxMatchOffset, nil) return &f } // NewReaderDict is like NewReader but initializes the reader // with a preset dictionary. The returned Reader behaves as if // the uncompressed data stream started with the given dictionary, // which has already been read. NewReaderDict is typically used // to read data compressed by NewWriterDict. // // The ReadCloser returned by NewReader also implements Resetter. func NewReaderDict(r io.Reader, dict []byte) io.ReadCloser { fixedHuffmanDecoderInit() var f decompressor f.r = makeReader(r) f.bits = new([maxNumLit + maxNumDist]int) f.codebits = new([numCodes]int) f.step = nextBlock f.dict.init(maxMatchOffset, dict) return &f } ================================================ FILE: vendor/github.com/klauspost/compress/flate/inflate_gen.go ================================================ // Code generated by go generate gen_inflate.go. DO NOT EDIT. package flate import ( "bufio" "bytes" "fmt" "math/bits" "strings" ) // Decode a single Huffman block from f. // hl and hd are the Huffman states for the lit/length values // and the distance values, respectively. If hd == nil, using the // fixed distance encoding associated with fixed Huffman blocks. func (f *decompressor) huffmanBytesBuffer() { const ( stateInit = iota // Zero value must be stateInit stateDict ) fr := f.r.(*bytes.Buffer) // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, // but is smart enough to keep local variables in registers, so use nb and b, // inline call to moreBits and reassign b,nb back to f on return. fnb, fb, dict := f.nb, f.b, &f.dict switch f.stepState { case stateInit: goto readLiteral case stateDict: goto copyHistory } readLiteral: // Read literal and/or (length, distance) according to RFC section 3.2.3. { var v int { // Inlined v, err := f.huffSym(f.hl) // Since a huffmanDecoder can be empty or be composed of a degenerate tree // with single element, huffSym must error on these two edge cases. In both // cases, the chunks slice will be 0 for the invalid sequence, leading it // satisfy the n == 0 check below. n := uint(f.hl.maxRead) for { for fnb < n { c, err := fr.ReadByte() if err != nil { f.b, f.nb = fb, fnb f.err = noEOF(err) return } f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 } chunk := f.hl.chunks[fb&(huffmanNumChunks-1)] n = uint(chunk & huffmanCountMask) if n > huffmanChunkBits { chunk = f.hl.links[chunk>>huffmanValueShift][(fb>>huffmanChunkBits)&f.hl.linkMask] n = uint(chunk & huffmanCountMask) } if n <= fnb { if n == 0 { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("huffsym: n==0") } f.err = CorruptInputError(f.roffset) return } fb = fb >> (n & regSizeMaskUint32) fnb = fnb - n v = int(chunk >> huffmanValueShift) break } } } var length int switch { case v < 256: dict.writeByte(byte(v)) if dict.availWrite() == 0 { f.toRead = dict.readFlush() f.step = huffmanBytesBuffer f.stepState = stateInit f.b, f.nb = fb, fnb return } goto readLiteral case v == 256: f.b, f.nb = fb, fnb f.finishBlock() return // otherwise, reference to older data case v < 265: length = v - (257 - 3) case v < maxNumLit: val := decCodeToLen[(v - 257)] length = int(val.length) + 3 n := uint(val.extra) for fnb < n { c, err := fr.ReadByte() if err != nil { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("morebits n>0:", err) } f.err = err return } f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 } length += int(fb & bitMask32[n]) fb >>= n & regSizeMaskUint32 fnb -= n default: if debugDecode { fmt.Println(v, ">= maxNumLit") } f.err = CorruptInputError(f.roffset) f.b, f.nb = fb, fnb return } var dist uint32 if f.hd == nil { for fnb < 5 { c, err := fr.ReadByte() if err != nil { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("morebits f.nb<5:", err) } f.err = err return } f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 } dist = uint32(bits.Reverse8(uint8(fb & 0x1F << 3))) fb >>= 5 fnb -= 5 } else { // Since a huffmanDecoder can be empty or be composed of a degenerate tree // with single element, huffSym must error on these two edge cases. In both // cases, the chunks slice will be 0 for the invalid sequence, leading it // satisfy the n == 0 check below. n := uint(f.hd.maxRead) // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, // but is smart enough to keep local variables in registers, so use nb and b, // inline call to moreBits and reassign b,nb back to f on return. for { for fnb < n { c, err := fr.ReadByte() if err != nil { f.b, f.nb = fb, fnb f.err = noEOF(err) return } f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 } chunk := f.hd.chunks[fb&(huffmanNumChunks-1)] n = uint(chunk & huffmanCountMask) if n > huffmanChunkBits { chunk = f.hd.links[chunk>>huffmanValueShift][(fb>>huffmanChunkBits)&f.hd.linkMask] n = uint(chunk & huffmanCountMask) } if n <= fnb { if n == 0 { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("huffsym: n==0") } f.err = CorruptInputError(f.roffset) return } fb = fb >> (n & regSizeMaskUint32) fnb = fnb - n dist = uint32(chunk >> huffmanValueShift) break } } } switch { case dist < 4: dist++ case dist < maxNumDist: nb := uint(dist-2) >> 1 // have 1 bit in bottom of dist, need nb more. extra := (dist & 1) << (nb & regSizeMaskUint32) for fnb < nb { c, err := fr.ReadByte() if err != nil { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("morebits f.nb>= nb & regSizeMaskUint32 fnb -= nb dist = 1<<((nb+1)®SizeMaskUint32) + 1 + extra // slower: dist = bitMask32[nb+1] + 2 + extra default: f.b, f.nb = fb, fnb if debugDecode { fmt.Println("dist too big:", dist, maxNumDist) } f.err = CorruptInputError(f.roffset) return } // No check on length; encoding can be prescient. if dist > uint32(dict.histSize()) { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("dist > dict.histSize():", dist, dict.histSize()) } f.err = CorruptInputError(f.roffset) return } f.copyLen, f.copyDist = length, int(dist) goto copyHistory } copyHistory: // Perform a backwards copy according to RFC section 3.2.3. { cnt := dict.tryWriteCopy(f.copyDist, f.copyLen) if cnt == 0 { cnt = dict.writeCopy(f.copyDist, f.copyLen) } f.copyLen -= cnt if dict.availWrite() == 0 || f.copyLen > 0 { f.toRead = dict.readFlush() f.step = huffmanBytesBuffer // We need to continue this work f.stepState = stateDict f.b, f.nb = fb, fnb return } goto readLiteral } // Not reached } // Decode a single Huffman block from f. // hl and hd are the Huffman states for the lit/length values // and the distance values, respectively. If hd == nil, using the // fixed distance encoding associated with fixed Huffman blocks. func (f *decompressor) huffmanBytesReader() { const ( stateInit = iota // Zero value must be stateInit stateDict ) fr := f.r.(*bytes.Reader) // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, // but is smart enough to keep local variables in registers, so use nb and b, // inline call to moreBits and reassign b,nb back to f on return. fnb, fb, dict := f.nb, f.b, &f.dict switch f.stepState { case stateInit: goto readLiteral case stateDict: goto copyHistory } readLiteral: // Read literal and/or (length, distance) according to RFC section 3.2.3. { var v int { // Inlined v, err := f.huffSym(f.hl) // Since a huffmanDecoder can be empty or be composed of a degenerate tree // with single element, huffSym must error on these two edge cases. In both // cases, the chunks slice will be 0 for the invalid sequence, leading it // satisfy the n == 0 check below. n := uint(f.hl.maxRead) for { for fnb < n { c, err := fr.ReadByte() if err != nil { f.b, f.nb = fb, fnb f.err = noEOF(err) return } f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 } chunk := f.hl.chunks[fb&(huffmanNumChunks-1)] n = uint(chunk & huffmanCountMask) if n > huffmanChunkBits { chunk = f.hl.links[chunk>>huffmanValueShift][(fb>>huffmanChunkBits)&f.hl.linkMask] n = uint(chunk & huffmanCountMask) } if n <= fnb { if n == 0 { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("huffsym: n==0") } f.err = CorruptInputError(f.roffset) return } fb = fb >> (n & regSizeMaskUint32) fnb = fnb - n v = int(chunk >> huffmanValueShift) break } } } var length int switch { case v < 256: dict.writeByte(byte(v)) if dict.availWrite() == 0 { f.toRead = dict.readFlush() f.step = huffmanBytesReader f.stepState = stateInit f.b, f.nb = fb, fnb return } goto readLiteral case v == 256: f.b, f.nb = fb, fnb f.finishBlock() return // otherwise, reference to older data case v < 265: length = v - (257 - 3) case v < maxNumLit: val := decCodeToLen[(v - 257)] length = int(val.length) + 3 n := uint(val.extra) for fnb < n { c, err := fr.ReadByte() if err != nil { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("morebits n>0:", err) } f.err = err return } f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 } length += int(fb & bitMask32[n]) fb >>= n & regSizeMaskUint32 fnb -= n default: if debugDecode { fmt.Println(v, ">= maxNumLit") } f.err = CorruptInputError(f.roffset) f.b, f.nb = fb, fnb return } var dist uint32 if f.hd == nil { for fnb < 5 { c, err := fr.ReadByte() if err != nil { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("morebits f.nb<5:", err) } f.err = err return } f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 } dist = uint32(bits.Reverse8(uint8(fb & 0x1F << 3))) fb >>= 5 fnb -= 5 } else { // Since a huffmanDecoder can be empty or be composed of a degenerate tree // with single element, huffSym must error on these two edge cases. In both // cases, the chunks slice will be 0 for the invalid sequence, leading it // satisfy the n == 0 check below. n := uint(f.hd.maxRead) // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, // but is smart enough to keep local variables in registers, so use nb and b, // inline call to moreBits and reassign b,nb back to f on return. for { for fnb < n { c, err := fr.ReadByte() if err != nil { f.b, f.nb = fb, fnb f.err = noEOF(err) return } f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 } chunk := f.hd.chunks[fb&(huffmanNumChunks-1)] n = uint(chunk & huffmanCountMask) if n > huffmanChunkBits { chunk = f.hd.links[chunk>>huffmanValueShift][(fb>>huffmanChunkBits)&f.hd.linkMask] n = uint(chunk & huffmanCountMask) } if n <= fnb { if n == 0 { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("huffsym: n==0") } f.err = CorruptInputError(f.roffset) return } fb = fb >> (n & regSizeMaskUint32) fnb = fnb - n dist = uint32(chunk >> huffmanValueShift) break } } } switch { case dist < 4: dist++ case dist < maxNumDist: nb := uint(dist-2) >> 1 // have 1 bit in bottom of dist, need nb more. extra := (dist & 1) << (nb & regSizeMaskUint32) for fnb < nb { c, err := fr.ReadByte() if err != nil { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("morebits f.nb>= nb & regSizeMaskUint32 fnb -= nb dist = 1<<((nb+1)®SizeMaskUint32) + 1 + extra // slower: dist = bitMask32[nb+1] + 2 + extra default: f.b, f.nb = fb, fnb if debugDecode { fmt.Println("dist too big:", dist, maxNumDist) } f.err = CorruptInputError(f.roffset) return } // No check on length; encoding can be prescient. if dist > uint32(dict.histSize()) { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("dist > dict.histSize():", dist, dict.histSize()) } f.err = CorruptInputError(f.roffset) return } f.copyLen, f.copyDist = length, int(dist) goto copyHistory } copyHistory: // Perform a backwards copy according to RFC section 3.2.3. { cnt := dict.tryWriteCopy(f.copyDist, f.copyLen) if cnt == 0 { cnt = dict.writeCopy(f.copyDist, f.copyLen) } f.copyLen -= cnt if dict.availWrite() == 0 || f.copyLen > 0 { f.toRead = dict.readFlush() f.step = huffmanBytesReader // We need to continue this work f.stepState = stateDict f.b, f.nb = fb, fnb return } goto readLiteral } // Not reached } // Decode a single Huffman block from f. // hl and hd are the Huffman states for the lit/length values // and the distance values, respectively. If hd == nil, using the // fixed distance encoding associated with fixed Huffman blocks. func (f *decompressor) huffmanBufioReader() { const ( stateInit = iota // Zero value must be stateInit stateDict ) fr := f.r.(*bufio.Reader) // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, // but is smart enough to keep local variables in registers, so use nb and b, // inline call to moreBits and reassign b,nb back to f on return. fnb, fb, dict := f.nb, f.b, &f.dict switch f.stepState { case stateInit: goto readLiteral case stateDict: goto copyHistory } readLiteral: // Read literal and/or (length, distance) according to RFC section 3.2.3. { var v int { // Inlined v, err := f.huffSym(f.hl) // Since a huffmanDecoder can be empty or be composed of a degenerate tree // with single element, huffSym must error on these two edge cases. In both // cases, the chunks slice will be 0 for the invalid sequence, leading it // satisfy the n == 0 check below. n := uint(f.hl.maxRead) for { for fnb < n { c, err := fr.ReadByte() if err != nil { f.b, f.nb = fb, fnb f.err = noEOF(err) return } f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 } chunk := f.hl.chunks[fb&(huffmanNumChunks-1)] n = uint(chunk & huffmanCountMask) if n > huffmanChunkBits { chunk = f.hl.links[chunk>>huffmanValueShift][(fb>>huffmanChunkBits)&f.hl.linkMask] n = uint(chunk & huffmanCountMask) } if n <= fnb { if n == 0 { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("huffsym: n==0") } f.err = CorruptInputError(f.roffset) return } fb = fb >> (n & regSizeMaskUint32) fnb = fnb - n v = int(chunk >> huffmanValueShift) break } } } var length int switch { case v < 256: dict.writeByte(byte(v)) if dict.availWrite() == 0 { f.toRead = dict.readFlush() f.step = huffmanBufioReader f.stepState = stateInit f.b, f.nb = fb, fnb return } goto readLiteral case v == 256: f.b, f.nb = fb, fnb f.finishBlock() return // otherwise, reference to older data case v < 265: length = v - (257 - 3) case v < maxNumLit: val := decCodeToLen[(v - 257)] length = int(val.length) + 3 n := uint(val.extra) for fnb < n { c, err := fr.ReadByte() if err != nil { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("morebits n>0:", err) } f.err = err return } f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 } length += int(fb & bitMask32[n]) fb >>= n & regSizeMaskUint32 fnb -= n default: if debugDecode { fmt.Println(v, ">= maxNumLit") } f.err = CorruptInputError(f.roffset) f.b, f.nb = fb, fnb return } var dist uint32 if f.hd == nil { for fnb < 5 { c, err := fr.ReadByte() if err != nil { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("morebits f.nb<5:", err) } f.err = err return } f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 } dist = uint32(bits.Reverse8(uint8(fb & 0x1F << 3))) fb >>= 5 fnb -= 5 } else { // Since a huffmanDecoder can be empty or be composed of a degenerate tree // with single element, huffSym must error on these two edge cases. In both // cases, the chunks slice will be 0 for the invalid sequence, leading it // satisfy the n == 0 check below. n := uint(f.hd.maxRead) // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, // but is smart enough to keep local variables in registers, so use nb and b, // inline call to moreBits and reassign b,nb back to f on return. for { for fnb < n { c, err := fr.ReadByte() if err != nil { f.b, f.nb = fb, fnb f.err = noEOF(err) return } f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 } chunk := f.hd.chunks[fb&(huffmanNumChunks-1)] n = uint(chunk & huffmanCountMask) if n > huffmanChunkBits { chunk = f.hd.links[chunk>>huffmanValueShift][(fb>>huffmanChunkBits)&f.hd.linkMask] n = uint(chunk & huffmanCountMask) } if n <= fnb { if n == 0 { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("huffsym: n==0") } f.err = CorruptInputError(f.roffset) return } fb = fb >> (n & regSizeMaskUint32) fnb = fnb - n dist = uint32(chunk >> huffmanValueShift) break } } } switch { case dist < 4: dist++ case dist < maxNumDist: nb := uint(dist-2) >> 1 // have 1 bit in bottom of dist, need nb more. extra := (dist & 1) << (nb & regSizeMaskUint32) for fnb < nb { c, err := fr.ReadByte() if err != nil { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("morebits f.nb>= nb & regSizeMaskUint32 fnb -= nb dist = 1<<((nb+1)®SizeMaskUint32) + 1 + extra // slower: dist = bitMask32[nb+1] + 2 + extra default: f.b, f.nb = fb, fnb if debugDecode { fmt.Println("dist too big:", dist, maxNumDist) } f.err = CorruptInputError(f.roffset) return } // No check on length; encoding can be prescient. if dist > uint32(dict.histSize()) { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("dist > dict.histSize():", dist, dict.histSize()) } f.err = CorruptInputError(f.roffset) return } f.copyLen, f.copyDist = length, int(dist) goto copyHistory } copyHistory: // Perform a backwards copy according to RFC section 3.2.3. { cnt := dict.tryWriteCopy(f.copyDist, f.copyLen) if cnt == 0 { cnt = dict.writeCopy(f.copyDist, f.copyLen) } f.copyLen -= cnt if dict.availWrite() == 0 || f.copyLen > 0 { f.toRead = dict.readFlush() f.step = huffmanBufioReader // We need to continue this work f.stepState = stateDict f.b, f.nb = fb, fnb return } goto readLiteral } // Not reached } // Decode a single Huffman block from f. // hl and hd are the Huffman states for the lit/length values // and the distance values, respectively. If hd == nil, using the // fixed distance encoding associated with fixed Huffman blocks. func (f *decompressor) huffmanStringsReader() { const ( stateInit = iota // Zero value must be stateInit stateDict ) fr := f.r.(*strings.Reader) // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, // but is smart enough to keep local variables in registers, so use nb and b, // inline call to moreBits and reassign b,nb back to f on return. fnb, fb, dict := f.nb, f.b, &f.dict switch f.stepState { case stateInit: goto readLiteral case stateDict: goto copyHistory } readLiteral: // Read literal and/or (length, distance) according to RFC section 3.2.3. { var v int { // Inlined v, err := f.huffSym(f.hl) // Since a huffmanDecoder can be empty or be composed of a degenerate tree // with single element, huffSym must error on these two edge cases. In both // cases, the chunks slice will be 0 for the invalid sequence, leading it // satisfy the n == 0 check below. n := uint(f.hl.maxRead) for { for fnb < n { c, err := fr.ReadByte() if err != nil { f.b, f.nb = fb, fnb f.err = noEOF(err) return } f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 } chunk := f.hl.chunks[fb&(huffmanNumChunks-1)] n = uint(chunk & huffmanCountMask) if n > huffmanChunkBits { chunk = f.hl.links[chunk>>huffmanValueShift][(fb>>huffmanChunkBits)&f.hl.linkMask] n = uint(chunk & huffmanCountMask) } if n <= fnb { if n == 0 { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("huffsym: n==0") } f.err = CorruptInputError(f.roffset) return } fb = fb >> (n & regSizeMaskUint32) fnb = fnb - n v = int(chunk >> huffmanValueShift) break } } } var length int switch { case v < 256: dict.writeByte(byte(v)) if dict.availWrite() == 0 { f.toRead = dict.readFlush() f.step = huffmanStringsReader f.stepState = stateInit f.b, f.nb = fb, fnb return } goto readLiteral case v == 256: f.b, f.nb = fb, fnb f.finishBlock() return // otherwise, reference to older data case v < 265: length = v - (257 - 3) case v < maxNumLit: val := decCodeToLen[(v - 257)] length = int(val.length) + 3 n := uint(val.extra) for fnb < n { c, err := fr.ReadByte() if err != nil { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("morebits n>0:", err) } f.err = err return } f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 } length += int(fb & bitMask32[n]) fb >>= n & regSizeMaskUint32 fnb -= n default: if debugDecode { fmt.Println(v, ">= maxNumLit") } f.err = CorruptInputError(f.roffset) f.b, f.nb = fb, fnb return } var dist uint32 if f.hd == nil { for fnb < 5 { c, err := fr.ReadByte() if err != nil { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("morebits f.nb<5:", err) } f.err = err return } f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 } dist = uint32(bits.Reverse8(uint8(fb & 0x1F << 3))) fb >>= 5 fnb -= 5 } else { // Since a huffmanDecoder can be empty or be composed of a degenerate tree // with single element, huffSym must error on these two edge cases. In both // cases, the chunks slice will be 0 for the invalid sequence, leading it // satisfy the n == 0 check below. n := uint(f.hd.maxRead) // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, // but is smart enough to keep local variables in registers, so use nb and b, // inline call to moreBits and reassign b,nb back to f on return. for { for fnb < n { c, err := fr.ReadByte() if err != nil { f.b, f.nb = fb, fnb f.err = noEOF(err) return } f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 } chunk := f.hd.chunks[fb&(huffmanNumChunks-1)] n = uint(chunk & huffmanCountMask) if n > huffmanChunkBits { chunk = f.hd.links[chunk>>huffmanValueShift][(fb>>huffmanChunkBits)&f.hd.linkMask] n = uint(chunk & huffmanCountMask) } if n <= fnb { if n == 0 { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("huffsym: n==0") } f.err = CorruptInputError(f.roffset) return } fb = fb >> (n & regSizeMaskUint32) fnb = fnb - n dist = uint32(chunk >> huffmanValueShift) break } } } switch { case dist < 4: dist++ case dist < maxNumDist: nb := uint(dist-2) >> 1 // have 1 bit in bottom of dist, need nb more. extra := (dist & 1) << (nb & regSizeMaskUint32) for fnb < nb { c, err := fr.ReadByte() if err != nil { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("morebits f.nb>= nb & regSizeMaskUint32 fnb -= nb dist = 1<<((nb+1)®SizeMaskUint32) + 1 + extra // slower: dist = bitMask32[nb+1] + 2 + extra default: f.b, f.nb = fb, fnb if debugDecode { fmt.Println("dist too big:", dist, maxNumDist) } f.err = CorruptInputError(f.roffset) return } // No check on length; encoding can be prescient. if dist > uint32(dict.histSize()) { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("dist > dict.histSize():", dist, dict.histSize()) } f.err = CorruptInputError(f.roffset) return } f.copyLen, f.copyDist = length, int(dist) goto copyHistory } copyHistory: // Perform a backwards copy according to RFC section 3.2.3. { cnt := dict.tryWriteCopy(f.copyDist, f.copyLen) if cnt == 0 { cnt = dict.writeCopy(f.copyDist, f.copyLen) } f.copyLen -= cnt if dict.availWrite() == 0 || f.copyLen > 0 { f.toRead = dict.readFlush() f.step = huffmanStringsReader // We need to continue this work f.stepState = stateDict f.b, f.nb = fb, fnb return } goto readLiteral } // Not reached } // Decode a single Huffman block from f. // hl and hd are the Huffman states for the lit/length values // and the distance values, respectively. If hd == nil, using the // fixed distance encoding associated with fixed Huffman blocks. func (f *decompressor) huffmanGenericReader() { const ( stateInit = iota // Zero value must be stateInit stateDict ) fr := f.r.(Reader) // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, // but is smart enough to keep local variables in registers, so use nb and b, // inline call to moreBits and reassign b,nb back to f on return. fnb, fb, dict := f.nb, f.b, &f.dict switch f.stepState { case stateInit: goto readLiteral case stateDict: goto copyHistory } readLiteral: // Read literal and/or (length, distance) according to RFC section 3.2.3. { var v int { // Inlined v, err := f.huffSym(f.hl) // Since a huffmanDecoder can be empty or be composed of a degenerate tree // with single element, huffSym must error on these two edge cases. In both // cases, the chunks slice will be 0 for the invalid sequence, leading it // satisfy the n == 0 check below. n := uint(f.hl.maxRead) for { for fnb < n { c, err := fr.ReadByte() if err != nil { f.b, f.nb = fb, fnb f.err = noEOF(err) return } f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 } chunk := f.hl.chunks[fb&(huffmanNumChunks-1)] n = uint(chunk & huffmanCountMask) if n > huffmanChunkBits { chunk = f.hl.links[chunk>>huffmanValueShift][(fb>>huffmanChunkBits)&f.hl.linkMask] n = uint(chunk & huffmanCountMask) } if n <= fnb { if n == 0 { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("huffsym: n==0") } f.err = CorruptInputError(f.roffset) return } fb = fb >> (n & regSizeMaskUint32) fnb = fnb - n v = int(chunk >> huffmanValueShift) break } } } var length int switch { case v < 256: dict.writeByte(byte(v)) if dict.availWrite() == 0 { f.toRead = dict.readFlush() f.step = huffmanGenericReader f.stepState = stateInit f.b, f.nb = fb, fnb return } goto readLiteral case v == 256: f.b, f.nb = fb, fnb f.finishBlock() return // otherwise, reference to older data case v < 265: length = v - (257 - 3) case v < maxNumLit: val := decCodeToLen[(v - 257)] length = int(val.length) + 3 n := uint(val.extra) for fnb < n { c, err := fr.ReadByte() if err != nil { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("morebits n>0:", err) } f.err = err return } f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 } length += int(fb & bitMask32[n]) fb >>= n & regSizeMaskUint32 fnb -= n default: if debugDecode { fmt.Println(v, ">= maxNumLit") } f.err = CorruptInputError(f.roffset) f.b, f.nb = fb, fnb return } var dist uint32 if f.hd == nil { for fnb < 5 { c, err := fr.ReadByte() if err != nil { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("morebits f.nb<5:", err) } f.err = err return } f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 } dist = uint32(bits.Reverse8(uint8(fb & 0x1F << 3))) fb >>= 5 fnb -= 5 } else { // Since a huffmanDecoder can be empty or be composed of a degenerate tree // with single element, huffSym must error on these two edge cases. In both // cases, the chunks slice will be 0 for the invalid sequence, leading it // satisfy the n == 0 check below. n := uint(f.hd.maxRead) // Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers, // but is smart enough to keep local variables in registers, so use nb and b, // inline call to moreBits and reassign b,nb back to f on return. for { for fnb < n { c, err := fr.ReadByte() if err != nil { f.b, f.nb = fb, fnb f.err = noEOF(err) return } f.roffset++ fb |= uint32(c) << (fnb & regSizeMaskUint32) fnb += 8 } chunk := f.hd.chunks[fb&(huffmanNumChunks-1)] n = uint(chunk & huffmanCountMask) if n > huffmanChunkBits { chunk = f.hd.links[chunk>>huffmanValueShift][(fb>>huffmanChunkBits)&f.hd.linkMask] n = uint(chunk & huffmanCountMask) } if n <= fnb { if n == 0 { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("huffsym: n==0") } f.err = CorruptInputError(f.roffset) return } fb = fb >> (n & regSizeMaskUint32) fnb = fnb - n dist = uint32(chunk >> huffmanValueShift) break } } } switch { case dist < 4: dist++ case dist < maxNumDist: nb := uint(dist-2) >> 1 // have 1 bit in bottom of dist, need nb more. extra := (dist & 1) << (nb & regSizeMaskUint32) for fnb < nb { c, err := fr.ReadByte() if err != nil { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("morebits f.nb>= nb & regSizeMaskUint32 fnb -= nb dist = 1<<((nb+1)®SizeMaskUint32) + 1 + extra // slower: dist = bitMask32[nb+1] + 2 + extra default: f.b, f.nb = fb, fnb if debugDecode { fmt.Println("dist too big:", dist, maxNumDist) } f.err = CorruptInputError(f.roffset) return } // No check on length; encoding can be prescient. if dist > uint32(dict.histSize()) { f.b, f.nb = fb, fnb if debugDecode { fmt.Println("dist > dict.histSize():", dist, dict.histSize()) } f.err = CorruptInputError(f.roffset) return } f.copyLen, f.copyDist = length, int(dist) goto copyHistory } copyHistory: // Perform a backwards copy according to RFC section 3.2.3. { cnt := dict.tryWriteCopy(f.copyDist, f.copyLen) if cnt == 0 { cnt = dict.writeCopy(f.copyDist, f.copyLen) } f.copyLen -= cnt if dict.availWrite() == 0 || f.copyLen > 0 { f.toRead = dict.readFlush() f.step = huffmanGenericReader // We need to continue this work f.stepState = stateDict f.b, f.nb = fb, fnb return } goto readLiteral } // Not reached } func (f *decompressor) huffmanBlockDecoder() { switch f.r.(type) { case *bytes.Buffer: f.huffmanBytesBuffer() case *bytes.Reader: f.huffmanBytesReader() case *bufio.Reader: f.huffmanBufioReader() case *strings.Reader: f.huffmanStringsReader() case Reader: f.huffmanGenericReader() default: f.huffmanGenericReader() } } ================================================ FILE: vendor/github.com/klauspost/compress/flate/level1.go ================================================ package flate import ( "encoding/binary" "fmt" "math/bits" ) // fastGen maintains the table for matches, // and the previous byte block for level 2. // This is the generic implementation. type fastEncL1 struct { fastGen table [tableSize]tableEntry } // EncodeL1 uses a similar algorithm to level 1 func (e *fastEncL1) Encode(dst *tokens, src []byte) { const ( inputMargin = 12 - 1 minNonLiteralBlockSize = 1 + 1 + inputMargin hashBytes = 5 ) if debugDeflate && e.cur < 0 { panic(fmt.Sprint("e.cur < 0: ", e.cur)) } // Protect against e.cur wraparound. for e.cur >= bufferReset { if len(e.hist) == 0 { for i := range e.table[:] { e.table[i] = tableEntry{} } e.cur = maxMatchOffset break } // Shift down everything in the table that isn't already too far away. minOff := e.cur + int32(len(e.hist)) - maxMatchOffset for i := range e.table[:] { v := e.table[i].offset if v <= minOff { v = 0 } else { v = v - e.cur + maxMatchOffset } e.table[i].offset = v } e.cur = maxMatchOffset } s := e.addBlock(src) // This check isn't in the Snappy implementation, but there, the caller // instead of the callee handles this case. if len(src) < minNonLiteralBlockSize { // We do not fill the token table. // This will be picked up by caller. dst.n = uint16(len(src)) return } // Override src src = e.hist nextEmit := s // sLimit is when to stop looking for offset/length copies. The inputMargin // lets us use a fast path for emitLiteral in the main loop, while we are // looking for copies. sLimit := int32(len(src) - inputMargin) // nextEmit is where in src the next emitLiteral should start from. cv := load6432(src, s) for { const skipLog = 5 const doEvery = 2 nextS := s var candidate tableEntry for { nextHash := hashLen(cv, tableBits, hashBytes) candidate = e.table[nextHash] nextS = s + doEvery + (s-nextEmit)>>skipLog if nextS > sLimit { goto emitRemainder } now := load6432(src, nextS) e.table[nextHash] = tableEntry{offset: s + e.cur} nextHash = hashLen(now, tableBits, hashBytes) offset := s - (candidate.offset - e.cur) if offset < maxMatchOffset && uint32(cv) == load3232(src, candidate.offset-e.cur) { e.table[nextHash] = tableEntry{offset: nextS + e.cur} break } // Do one right away... cv = now s = nextS nextS++ candidate = e.table[nextHash] now >>= 8 e.table[nextHash] = tableEntry{offset: s + e.cur} offset = s - (candidate.offset - e.cur) if offset < maxMatchOffset && uint32(cv) == load3232(src, candidate.offset-e.cur) { e.table[nextHash] = tableEntry{offset: nextS + e.cur} break } cv = now s = nextS } // A 4-byte match has been found. We'll later see if more than 4 bytes // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit // them as literal bytes. for { // Invariant: we have a 4-byte match at s, and no need to emit any // literal bytes prior to s. // Extend the 4-byte match as long as possible. t := candidate.offset - e.cur var l = int32(4) if false { l = e.matchlenLong(s+4, t+4, src) + 4 } else { // inlined: a := src[s+4:] b := src[t+4:] for len(a) >= 8 { if diff := binary.LittleEndian.Uint64(a) ^ binary.LittleEndian.Uint64(b); diff != 0 { l += int32(bits.TrailingZeros64(diff) >> 3) break } l += 8 a = a[8:] b = b[8:] } if len(a) < 8 { b = b[:len(a)] for i := range a { if a[i] != b[i] { break } l++ } } } // Extend backwards for t > 0 && s > nextEmit && src[t-1] == src[s-1] { s-- t-- l++ } if nextEmit < s { if false { emitLiteral(dst, src[nextEmit:s]) } else { for _, v := range src[nextEmit:s] { dst.tokens[dst.n] = token(v) dst.litHist[v]++ dst.n++ } } } // Save the match found if false { dst.AddMatchLong(l, uint32(s-t-baseMatchOffset)) } else { // Inlined... xoffset := uint32(s - t - baseMatchOffset) xlength := l oc := offsetCode(xoffset) xoffset |= oc << 16 for xlength > 0 { xl := xlength if xl > 258 { if xl > 258+baseMatchLength { xl = 258 } else { xl = 258 - baseMatchLength } } xlength -= xl xl -= baseMatchLength dst.extraHist[lengthCodes1[uint8(xl)]]++ dst.offHist[oc]++ dst.tokens[dst.n] = token(matchType | uint32(xl)<= s { s = nextS + 1 } if s >= sLimit { // Index first pair after match end. if int(s+l+8) < len(src) { cv := load6432(src, s) e.table[hashLen(cv, tableBits, hashBytes)] = tableEntry{offset: s + e.cur} } goto emitRemainder } // We could immediately start working at s now, but to improve // compression we first update the hash table at s-2 and at s. If // another emitCopy is not our next move, also calculate nextHash // at s+1. At least on GOARCH=amd64, these three hash calculations // are faster as one load64 call (with some shifts) instead of // three load32 calls. x := load6432(src, s-2) o := e.cur + s - 2 prevHash := hashLen(x, tableBits, hashBytes) e.table[prevHash] = tableEntry{offset: o} x >>= 16 currHash := hashLen(x, tableBits, hashBytes) candidate = e.table[currHash] e.table[currHash] = tableEntry{offset: o + 2} offset := s - (candidate.offset - e.cur) if offset > maxMatchOffset || uint32(x) != load3232(src, candidate.offset-e.cur) { cv = x >> 8 s++ break } } } emitRemainder: if int(nextEmit) < len(src) { // If nothing was added, don't encode literals. if dst.n == 0 { return } emitLiteral(dst, src[nextEmit:]) } } ================================================ FILE: vendor/github.com/klauspost/compress/flate/level2.go ================================================ package flate import "fmt" // fastGen maintains the table for matches, // and the previous byte block for level 2. // This is the generic implementation. type fastEncL2 struct { fastGen table [bTableSize]tableEntry } // EncodeL2 uses a similar algorithm to level 1, but is capable // of matching across blocks giving better compression at a small slowdown. func (e *fastEncL2) Encode(dst *tokens, src []byte) { const ( inputMargin = 12 - 1 minNonLiteralBlockSize = 1 + 1 + inputMargin hashBytes = 5 ) if debugDeflate && e.cur < 0 { panic(fmt.Sprint("e.cur < 0: ", e.cur)) } // Protect against e.cur wraparound. for e.cur >= bufferReset { if len(e.hist) == 0 { for i := range e.table[:] { e.table[i] = tableEntry{} } e.cur = maxMatchOffset break } // Shift down everything in the table that isn't already too far away. minOff := e.cur + int32(len(e.hist)) - maxMatchOffset for i := range e.table[:] { v := e.table[i].offset if v <= minOff { v = 0 } else { v = v - e.cur + maxMatchOffset } e.table[i].offset = v } e.cur = maxMatchOffset } s := e.addBlock(src) // This check isn't in the Snappy implementation, but there, the caller // instead of the callee handles this case. if len(src) < minNonLiteralBlockSize { // We do not fill the token table. // This will be picked up by caller. dst.n = uint16(len(src)) return } // Override src src = e.hist nextEmit := s // sLimit is when to stop looking for offset/length copies. The inputMargin // lets us use a fast path for emitLiteral in the main loop, while we are // looking for copies. sLimit := int32(len(src) - inputMargin) // nextEmit is where in src the next emitLiteral should start from. cv := load6432(src, s) for { // When should we start skipping if we haven't found matches in a long while. const skipLog = 5 const doEvery = 2 nextS := s var candidate tableEntry for { nextHash := hashLen(cv, bTableBits, hashBytes) s = nextS nextS = s + doEvery + (s-nextEmit)>>skipLog if nextS > sLimit { goto emitRemainder } candidate = e.table[nextHash] now := load6432(src, nextS) e.table[nextHash] = tableEntry{offset: s + e.cur} nextHash = hashLen(now, bTableBits, hashBytes) offset := s - (candidate.offset - e.cur) if offset < maxMatchOffset && uint32(cv) == load3232(src, candidate.offset-e.cur) { e.table[nextHash] = tableEntry{offset: nextS + e.cur} break } // Do one right away... cv = now s = nextS nextS++ candidate = e.table[nextHash] now >>= 8 e.table[nextHash] = tableEntry{offset: s + e.cur} offset = s - (candidate.offset - e.cur) if offset < maxMatchOffset && uint32(cv) == load3232(src, candidate.offset-e.cur) { break } cv = now } // A 4-byte match has been found. We'll later see if more than 4 bytes // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit // them as literal bytes. // Call emitCopy, and then see if another emitCopy could be our next // move. Repeat until we find no match for the input immediately after // what was consumed by the last emitCopy call. // // If we exit this loop normally then we need to call emitLiteral next, // though we don't yet know how big the literal will be. We handle that // by proceeding to the next iteration of the main loop. We also can // exit this loop via goto if we get close to exhausting the input. for { // Invariant: we have a 4-byte match at s, and no need to emit any // literal bytes prior to s. // Extend the 4-byte match as long as possible. t := candidate.offset - e.cur l := e.matchlenLong(s+4, t+4, src) + 4 // Extend backwards for t > 0 && s > nextEmit && src[t-1] == src[s-1] { s-- t-- l++ } if nextEmit < s { if false { emitLiteral(dst, src[nextEmit:s]) } else { for _, v := range src[nextEmit:s] { dst.tokens[dst.n] = token(v) dst.litHist[v]++ dst.n++ } } } dst.AddMatchLong(l, uint32(s-t-baseMatchOffset)) s += l nextEmit = s if nextS >= s { s = nextS + 1 } if s >= sLimit { // Index first pair after match end. if int(s+l+8) < len(src) { cv := load6432(src, s) e.table[hashLen(cv, bTableBits, hashBytes)] = tableEntry{offset: s + e.cur} } goto emitRemainder } // Store every second hash in-between, but offset by 1. for i := s - l + 2; i < s-5; i += 7 { x := load6432(src, i) nextHash := hashLen(x, bTableBits, hashBytes) e.table[nextHash] = tableEntry{offset: e.cur + i} // Skip one x >>= 16 nextHash = hashLen(x, bTableBits, hashBytes) e.table[nextHash] = tableEntry{offset: e.cur + i + 2} // Skip one x >>= 16 nextHash = hashLen(x, bTableBits, hashBytes) e.table[nextHash] = tableEntry{offset: e.cur + i + 4} } // We could immediately start working at s now, but to improve // compression we first update the hash table at s-2 to s. If // another emitCopy is not our next move, also calculate nextHash // at s+1. At least on GOARCH=amd64, these three hash calculations // are faster as one load64 call (with some shifts) instead of // three load32 calls. x := load6432(src, s-2) o := e.cur + s - 2 prevHash := hashLen(x, bTableBits, hashBytes) prevHash2 := hashLen(x>>8, bTableBits, hashBytes) e.table[prevHash] = tableEntry{offset: o} e.table[prevHash2] = tableEntry{offset: o + 1} currHash := hashLen(x>>16, bTableBits, hashBytes) candidate = e.table[currHash] e.table[currHash] = tableEntry{offset: o + 2} offset := s - (candidate.offset - e.cur) if offset > maxMatchOffset || uint32(x>>16) != load3232(src, candidate.offset-e.cur) { cv = x >> 24 s++ break } } } emitRemainder: if int(nextEmit) < len(src) { // If nothing was added, don't encode literals. if dst.n == 0 { return } emitLiteral(dst, src[nextEmit:]) } } ================================================ FILE: vendor/github.com/klauspost/compress/flate/level3.go ================================================ package flate import "fmt" // fastEncL3 type fastEncL3 struct { fastGen table [1 << 16]tableEntryPrev } // Encode uses a similar algorithm to level 2, will check up to two candidates. func (e *fastEncL3) Encode(dst *tokens, src []byte) { const ( inputMargin = 12 - 1 minNonLiteralBlockSize = 1 + 1 + inputMargin tableBits = 16 tableSize = 1 << tableBits hashBytes = 5 ) if debugDeflate && e.cur < 0 { panic(fmt.Sprint("e.cur < 0: ", e.cur)) } // Protect against e.cur wraparound. for e.cur >= bufferReset { if len(e.hist) == 0 { for i := range e.table[:] { e.table[i] = tableEntryPrev{} } e.cur = maxMatchOffset break } // Shift down everything in the table that isn't already too far away. minOff := e.cur + int32(len(e.hist)) - maxMatchOffset for i := range e.table[:] { v := e.table[i] if v.Cur.offset <= minOff { v.Cur.offset = 0 } else { v.Cur.offset = v.Cur.offset - e.cur + maxMatchOffset } if v.Prev.offset <= minOff { v.Prev.offset = 0 } else { v.Prev.offset = v.Prev.offset - e.cur + maxMatchOffset } e.table[i] = v } e.cur = maxMatchOffset } s := e.addBlock(src) // Skip if too small. if len(src) < minNonLiteralBlockSize { // We do not fill the token table. // This will be picked up by caller. dst.n = uint16(len(src)) return } // Override src src = e.hist nextEmit := s // sLimit is when to stop looking for offset/length copies. The inputMargin // lets us use a fast path for emitLiteral in the main loop, while we are // looking for copies. sLimit := int32(len(src) - inputMargin) // nextEmit is where in src the next emitLiteral should start from. cv := load6432(src, s) for { const skipLog = 7 nextS := s var candidate tableEntry for { nextHash := hashLen(cv, tableBits, hashBytes) s = nextS nextS = s + 1 + (s-nextEmit)>>skipLog if nextS > sLimit { goto emitRemainder } candidates := e.table[nextHash] now := load6432(src, nextS) // Safe offset distance until s + 4... minOffset := e.cur + s - (maxMatchOffset - 4) e.table[nextHash] = tableEntryPrev{Prev: candidates.Cur, Cur: tableEntry{offset: s + e.cur}} // Check both candidates candidate = candidates.Cur if candidate.offset < minOffset { cv = now // Previous will also be invalid, we have nothing. continue } if uint32(cv) == load3232(src, candidate.offset-e.cur) { if candidates.Prev.offset < minOffset || uint32(cv) != load3232(src, candidates.Prev.offset-e.cur) { break } // Both match and are valid, pick longest. offset := s - (candidate.offset - e.cur) o2 := s - (candidates.Prev.offset - e.cur) l1, l2 := matchLen(src[s+4:], src[s-offset+4:]), matchLen(src[s+4:], src[s-o2+4:]) if l2 > l1 { candidate = candidates.Prev } break } else { // We only check if value mismatches. // Offset will always be invalid in other cases. candidate = candidates.Prev if candidate.offset > minOffset && uint32(cv) == load3232(src, candidate.offset-e.cur) { break } } cv = now } // Call emitCopy, and then see if another emitCopy could be our next // move. Repeat until we find no match for the input immediately after // what was consumed by the last emitCopy call. // // If we exit this loop normally then we need to call emitLiteral next, // though we don't yet know how big the literal will be. We handle that // by proceeding to the next iteration of the main loop. We also can // exit this loop via goto if we get close to exhausting the input. for { // Invariant: we have a 4-byte match at s, and no need to emit any // literal bytes prior to s. // Extend the 4-byte match as long as possible. // t := candidate.offset - e.cur l := e.matchlenLong(s+4, t+4, src) + 4 // Extend backwards for t > 0 && s > nextEmit && src[t-1] == src[s-1] { s-- t-- l++ } if nextEmit < s { if false { emitLiteral(dst, src[nextEmit:s]) } else { for _, v := range src[nextEmit:s] { dst.tokens[dst.n] = token(v) dst.litHist[v]++ dst.n++ } } } dst.AddMatchLong(l, uint32(s-t-baseMatchOffset)) s += l nextEmit = s if nextS >= s { s = nextS + 1 } if s >= sLimit { t += l // Index first pair after match end. if int(t+8) < len(src) && t > 0 { cv = load6432(src, t) nextHash := hashLen(cv, tableBits, hashBytes) e.table[nextHash] = tableEntryPrev{ Prev: e.table[nextHash].Cur, Cur: tableEntry{offset: e.cur + t}, } } goto emitRemainder } // Store every 5th hash in-between. for i := s - l + 2; i < s-5; i += 6 { nextHash := hashLen(load6432(src, i), tableBits, hashBytes) e.table[nextHash] = tableEntryPrev{ Prev: e.table[nextHash].Cur, Cur: tableEntry{offset: e.cur + i}} } // We could immediately start working at s now, but to improve // compression we first update the hash table at s-2 to s. x := load6432(src, s-2) prevHash := hashLen(x, tableBits, hashBytes) e.table[prevHash] = tableEntryPrev{ Prev: e.table[prevHash].Cur, Cur: tableEntry{offset: e.cur + s - 2}, } x >>= 8 prevHash = hashLen(x, tableBits, hashBytes) e.table[prevHash] = tableEntryPrev{ Prev: e.table[prevHash].Cur, Cur: tableEntry{offset: e.cur + s - 1}, } x >>= 8 currHash := hashLen(x, tableBits, hashBytes) candidates := e.table[currHash] cv = x e.table[currHash] = tableEntryPrev{ Prev: candidates.Cur, Cur: tableEntry{offset: s + e.cur}, } // Check both candidates candidate = candidates.Cur minOffset := e.cur + s - (maxMatchOffset - 4) if candidate.offset > minOffset { if uint32(cv) == load3232(src, candidate.offset-e.cur) { // Found a match... continue } candidate = candidates.Prev if candidate.offset > minOffset && uint32(cv) == load3232(src, candidate.offset-e.cur) { // Match at prev... continue } } cv = x >> 8 s++ break } } emitRemainder: if int(nextEmit) < len(src) { // If nothing was added, don't encode literals. if dst.n == 0 { return } emitLiteral(dst, src[nextEmit:]) } } ================================================ FILE: vendor/github.com/klauspost/compress/flate/level4.go ================================================ package flate import "fmt" type fastEncL4 struct { fastGen table [tableSize]tableEntry bTable [tableSize]tableEntry } func (e *fastEncL4) Encode(dst *tokens, src []byte) { const ( inputMargin = 12 - 1 minNonLiteralBlockSize = 1 + 1 + inputMargin hashShortBytes = 4 ) if debugDeflate && e.cur < 0 { panic(fmt.Sprint("e.cur < 0: ", e.cur)) } // Protect against e.cur wraparound. for e.cur >= bufferReset { if len(e.hist) == 0 { for i := range e.table[:] { e.table[i] = tableEntry{} } for i := range e.bTable[:] { e.bTable[i] = tableEntry{} } e.cur = maxMatchOffset break } // Shift down everything in the table that isn't already too far away. minOff := e.cur + int32(len(e.hist)) - maxMatchOffset for i := range e.table[:] { v := e.table[i].offset if v <= minOff { v = 0 } else { v = v - e.cur + maxMatchOffset } e.table[i].offset = v } for i := range e.bTable[:] { v := e.bTable[i].offset if v <= minOff { v = 0 } else { v = v - e.cur + maxMatchOffset } e.bTable[i].offset = v } e.cur = maxMatchOffset } s := e.addBlock(src) // This check isn't in the Snappy implementation, but there, the caller // instead of the callee handles this case. if len(src) < minNonLiteralBlockSize { // We do not fill the token table. // This will be picked up by caller. dst.n = uint16(len(src)) return } // Override src src = e.hist nextEmit := s // sLimit is when to stop looking for offset/length copies. The inputMargin // lets us use a fast path for emitLiteral in the main loop, while we are // looking for copies. sLimit := int32(len(src) - inputMargin) // nextEmit is where in src the next emitLiteral should start from. cv := load6432(src, s) for { const skipLog = 6 const doEvery = 1 nextS := s var t int32 for { nextHashS := hashLen(cv, tableBits, hashShortBytes) nextHashL := hash7(cv, tableBits) s = nextS nextS = s + doEvery + (s-nextEmit)>>skipLog if nextS > sLimit { goto emitRemainder } // Fetch a short+long candidate sCandidate := e.table[nextHashS] lCandidate := e.bTable[nextHashL] next := load6432(src, nextS) entry := tableEntry{offset: s + e.cur} e.table[nextHashS] = entry e.bTable[nextHashL] = entry t = lCandidate.offset - e.cur if s-t < maxMatchOffset && uint32(cv) == load3232(src, lCandidate.offset-e.cur) { // We got a long match. Use that. break } t = sCandidate.offset - e.cur if s-t < maxMatchOffset && uint32(cv) == load3232(src, sCandidate.offset-e.cur) { // Found a 4 match... lCandidate = e.bTable[hash7(next, tableBits)] // If the next long is a candidate, check if we should use that instead... lOff := nextS - (lCandidate.offset - e.cur) if lOff < maxMatchOffset && load3232(src, lCandidate.offset-e.cur) == uint32(next) { l1, l2 := matchLen(src[s+4:], src[t+4:]), matchLen(src[nextS+4:], src[nextS-lOff+4:]) if l2 > l1 { s = nextS t = lCandidate.offset - e.cur } } break } cv = next } // A 4-byte match has been found. We'll later see if more than 4 bytes // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit // them as literal bytes. // Extend the 4-byte match as long as possible. l := e.matchlenLong(s+4, t+4, src) + 4 // Extend backwards for t > 0 && s > nextEmit && src[t-1] == src[s-1] { s-- t-- l++ } if nextEmit < s { if false { emitLiteral(dst, src[nextEmit:s]) } else { for _, v := range src[nextEmit:s] { dst.tokens[dst.n] = token(v) dst.litHist[v]++ dst.n++ } } } if debugDeflate { if t >= s { panic("s-t") } if (s - t) > maxMatchOffset { panic(fmt.Sprintln("mmo", t)) } if l < baseMatchLength { panic("bml") } } dst.AddMatchLong(l, uint32(s-t-baseMatchOffset)) s += l nextEmit = s if nextS >= s { s = nextS + 1 } if s >= sLimit { // Index first pair after match end. if int(s+8) < len(src) { cv := load6432(src, s) e.table[hashLen(cv, tableBits, hashShortBytes)] = tableEntry{offset: s + e.cur} e.bTable[hash7(cv, tableBits)] = tableEntry{offset: s + e.cur} } goto emitRemainder } // Store every 3rd hash in-between if true { i := nextS if i < s-1 { cv := load6432(src, i) t := tableEntry{offset: i + e.cur} t2 := tableEntry{offset: t.offset + 1} e.bTable[hash7(cv, tableBits)] = t e.bTable[hash7(cv>>8, tableBits)] = t2 e.table[hashLen(cv>>8, tableBits, hashShortBytes)] = t2 i += 3 for ; i < s-1; i += 3 { cv := load6432(src, i) t := tableEntry{offset: i + e.cur} t2 := tableEntry{offset: t.offset + 1} e.bTable[hash7(cv, tableBits)] = t e.bTable[hash7(cv>>8, tableBits)] = t2 e.table[hashLen(cv>>8, tableBits, hashShortBytes)] = t2 } } } // We could immediately start working at s now, but to improve // compression we first update the hash table at s-1 and at s. x := load6432(src, s-1) o := e.cur + s - 1 prevHashS := hashLen(x, tableBits, hashShortBytes) prevHashL := hash7(x, tableBits) e.table[prevHashS] = tableEntry{offset: o} e.bTable[prevHashL] = tableEntry{offset: o} cv = x >> 8 } emitRemainder: if int(nextEmit) < len(src) { // If nothing was added, don't encode literals. if dst.n == 0 { return } emitLiteral(dst, src[nextEmit:]) } } ================================================ FILE: vendor/github.com/klauspost/compress/flate/level5.go ================================================ package flate import "fmt" type fastEncL5 struct { fastGen table [tableSize]tableEntry bTable [tableSize]tableEntryPrev } func (e *fastEncL5) Encode(dst *tokens, src []byte) { const ( inputMargin = 12 - 1 minNonLiteralBlockSize = 1 + 1 + inputMargin hashShortBytes = 4 ) if debugDeflate && e.cur < 0 { panic(fmt.Sprint("e.cur < 0: ", e.cur)) } // Protect against e.cur wraparound. for e.cur >= bufferReset { if len(e.hist) == 0 { for i := range e.table[:] { e.table[i] = tableEntry{} } for i := range e.bTable[:] { e.bTable[i] = tableEntryPrev{} } e.cur = maxMatchOffset break } // Shift down everything in the table that isn't already too far away. minOff := e.cur + int32(len(e.hist)) - maxMatchOffset for i := range e.table[:] { v := e.table[i].offset if v <= minOff { v = 0 } else { v = v - e.cur + maxMatchOffset } e.table[i].offset = v } for i := range e.bTable[:] { v := e.bTable[i] if v.Cur.offset <= minOff { v.Cur.offset = 0 v.Prev.offset = 0 } else { v.Cur.offset = v.Cur.offset - e.cur + maxMatchOffset if v.Prev.offset <= minOff { v.Prev.offset = 0 } else { v.Prev.offset = v.Prev.offset - e.cur + maxMatchOffset } } e.bTable[i] = v } e.cur = maxMatchOffset } s := e.addBlock(src) // This check isn't in the Snappy implementation, but there, the caller // instead of the callee handles this case. if len(src) < minNonLiteralBlockSize { // We do not fill the token table. // This will be picked up by caller. dst.n = uint16(len(src)) return } // Override src src = e.hist nextEmit := s // sLimit is when to stop looking for offset/length copies. The inputMargin // lets us use a fast path for emitLiteral in the main loop, while we are // looking for copies. sLimit := int32(len(src) - inputMargin) // nextEmit is where in src the next emitLiteral should start from. cv := load6432(src, s) for { const skipLog = 6 const doEvery = 1 nextS := s var l int32 var t int32 for { nextHashS := hashLen(cv, tableBits, hashShortBytes) nextHashL := hash7(cv, tableBits) s = nextS nextS = s + doEvery + (s-nextEmit)>>skipLog if nextS > sLimit { goto emitRemainder } // Fetch a short+long candidate sCandidate := e.table[nextHashS] lCandidate := e.bTable[nextHashL] next := load6432(src, nextS) entry := tableEntry{offset: s + e.cur} e.table[nextHashS] = entry eLong := &e.bTable[nextHashL] eLong.Cur, eLong.Prev = entry, eLong.Cur nextHashS = hashLen(next, tableBits, hashShortBytes) nextHashL = hash7(next, tableBits) t = lCandidate.Cur.offset - e.cur if s-t < maxMatchOffset { if uint32(cv) == load3232(src, lCandidate.Cur.offset-e.cur) { // Store the next match e.table[nextHashS] = tableEntry{offset: nextS + e.cur} eLong := &e.bTable[nextHashL] eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur t2 := lCandidate.Prev.offset - e.cur if s-t2 < maxMatchOffset && uint32(cv) == load3232(src, lCandidate.Prev.offset-e.cur) { l = e.matchlen(s+4, t+4, src) + 4 ml1 := e.matchlen(s+4, t2+4, src) + 4 if ml1 > l { t = t2 l = ml1 break } } break } t = lCandidate.Prev.offset - e.cur if s-t < maxMatchOffset && uint32(cv) == load3232(src, lCandidate.Prev.offset-e.cur) { // Store the next match e.table[nextHashS] = tableEntry{offset: nextS + e.cur} eLong := &e.bTable[nextHashL] eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur break } } t = sCandidate.offset - e.cur if s-t < maxMatchOffset && uint32(cv) == load3232(src, sCandidate.offset-e.cur) { // Found a 4 match... l = e.matchlen(s+4, t+4, src) + 4 lCandidate = e.bTable[nextHashL] // Store the next match e.table[nextHashS] = tableEntry{offset: nextS + e.cur} eLong := &e.bTable[nextHashL] eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur // If the next long is a candidate, use that... t2 := lCandidate.Cur.offset - e.cur if nextS-t2 < maxMatchOffset { if load3232(src, lCandidate.Cur.offset-e.cur) == uint32(next) { ml := e.matchlen(nextS+4, t2+4, src) + 4 if ml > l { t = t2 s = nextS l = ml break } } // If the previous long is a candidate, use that... t2 = lCandidate.Prev.offset - e.cur if nextS-t2 < maxMatchOffset && load3232(src, lCandidate.Prev.offset-e.cur) == uint32(next) { ml := e.matchlen(nextS+4, t2+4, src) + 4 if ml > l { t = t2 s = nextS l = ml break } } } break } cv = next } // A 4-byte match has been found. We'll later see if more than 4 bytes // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit // them as literal bytes. if l == 0 { // Extend the 4-byte match as long as possible. l = e.matchlenLong(s+4, t+4, src) + 4 } else if l == maxMatchLength { l += e.matchlenLong(s+l, t+l, src) } // Try to locate a better match by checking the end of best match... if sAt := s + l; l < 30 && sAt < sLimit { // Allow some bytes at the beginning to mismatch. // Sweet spot is 2/3 bytes depending on input. // 3 is only a little better when it is but sometimes a lot worse. // The skipped bytes are tested in Extend backwards, // and still picked up as part of the match if they do. const skipBeginning = 2 eLong := e.bTable[hash7(load6432(src, sAt), tableBits)].Cur.offset t2 := eLong - e.cur - l + skipBeginning s2 := s + skipBeginning off := s2 - t2 if t2 >= 0 && off < maxMatchOffset && off > 0 { if l2 := e.matchlenLong(s2, t2, src); l2 > l { t = t2 l = l2 s = s2 } } } // Extend backwards for t > 0 && s > nextEmit && src[t-1] == src[s-1] { s-- t-- l++ } if nextEmit < s { if false { emitLiteral(dst, src[nextEmit:s]) } else { for _, v := range src[nextEmit:s] { dst.tokens[dst.n] = token(v) dst.litHist[v]++ dst.n++ } } } if debugDeflate { if t >= s { panic(fmt.Sprintln("s-t", s, t)) } if (s - t) > maxMatchOffset { panic(fmt.Sprintln("mmo", s-t)) } if l < baseMatchLength { panic("bml") } } dst.AddMatchLong(l, uint32(s-t-baseMatchOffset)) s += l nextEmit = s if nextS >= s { s = nextS + 1 } if s >= sLimit { goto emitRemainder } // Store every 3rd hash in-between. if true { const hashEvery = 3 i := s - l + 1 if i < s-1 { cv := load6432(src, i) t := tableEntry{offset: i + e.cur} e.table[hashLen(cv, tableBits, hashShortBytes)] = t eLong := &e.bTable[hash7(cv, tableBits)] eLong.Cur, eLong.Prev = t, eLong.Cur // Do an long at i+1 cv >>= 8 t = tableEntry{offset: t.offset + 1} eLong = &e.bTable[hash7(cv, tableBits)] eLong.Cur, eLong.Prev = t, eLong.Cur // We only have enough bits for a short entry at i+2 cv >>= 8 t = tableEntry{offset: t.offset + 1} e.table[hashLen(cv, tableBits, hashShortBytes)] = t // Skip one - otherwise we risk hitting 's' i += 4 for ; i < s-1; i += hashEvery { cv := load6432(src, i) t := tableEntry{offset: i + e.cur} t2 := tableEntry{offset: t.offset + 1} eLong := &e.bTable[hash7(cv, tableBits)] eLong.Cur, eLong.Prev = t, eLong.Cur e.table[hashLen(cv>>8, tableBits, hashShortBytes)] = t2 } } } // We could immediately start working at s now, but to improve // compression we first update the hash table at s-1 and at s. x := load6432(src, s-1) o := e.cur + s - 1 prevHashS := hashLen(x, tableBits, hashShortBytes) prevHashL := hash7(x, tableBits) e.table[prevHashS] = tableEntry{offset: o} eLong := &e.bTable[prevHashL] eLong.Cur, eLong.Prev = tableEntry{offset: o}, eLong.Cur cv = x >> 8 } emitRemainder: if int(nextEmit) < len(src) { // If nothing was added, don't encode literals. if dst.n == 0 { return } emitLiteral(dst, src[nextEmit:]) } } // fastEncL5Window is a level 5 encoder, // but with a custom window size. type fastEncL5Window struct { hist []byte cur int32 maxOffset int32 table [tableSize]tableEntry bTable [tableSize]tableEntryPrev } func (e *fastEncL5Window) Encode(dst *tokens, src []byte) { const ( inputMargin = 12 - 1 minNonLiteralBlockSize = 1 + 1 + inputMargin hashShortBytes = 4 ) maxMatchOffset := e.maxOffset if debugDeflate && e.cur < 0 { panic(fmt.Sprint("e.cur < 0: ", e.cur)) } // Protect against e.cur wraparound. for e.cur >= bufferReset { if len(e.hist) == 0 { for i := range e.table[:] { e.table[i] = tableEntry{} } for i := range e.bTable[:] { e.bTable[i] = tableEntryPrev{} } e.cur = maxMatchOffset break } // Shift down everything in the table that isn't already too far away. minOff := e.cur + int32(len(e.hist)) - maxMatchOffset for i := range e.table[:] { v := e.table[i].offset if v <= minOff { v = 0 } else { v = v - e.cur + maxMatchOffset } e.table[i].offset = v } for i := range e.bTable[:] { v := e.bTable[i] if v.Cur.offset <= minOff { v.Cur.offset = 0 v.Prev.offset = 0 } else { v.Cur.offset = v.Cur.offset - e.cur + maxMatchOffset if v.Prev.offset <= minOff { v.Prev.offset = 0 } else { v.Prev.offset = v.Prev.offset - e.cur + maxMatchOffset } } e.bTable[i] = v } e.cur = maxMatchOffset } s := e.addBlock(src) // This check isn't in the Snappy implementation, but there, the caller // instead of the callee handles this case. if len(src) < minNonLiteralBlockSize { // We do not fill the token table. // This will be picked up by caller. dst.n = uint16(len(src)) return } // Override src src = e.hist nextEmit := s // sLimit is when to stop looking for offset/length copies. The inputMargin // lets us use a fast path for emitLiteral in the main loop, while we are // looking for copies. sLimit := int32(len(src) - inputMargin) // nextEmit is where in src the next emitLiteral should start from. cv := load6432(src, s) for { const skipLog = 6 const doEvery = 1 nextS := s var l int32 var t int32 for { nextHashS := hashLen(cv, tableBits, hashShortBytes) nextHashL := hash7(cv, tableBits) s = nextS nextS = s + doEvery + (s-nextEmit)>>skipLog if nextS > sLimit { goto emitRemainder } // Fetch a short+long candidate sCandidate := e.table[nextHashS] lCandidate := e.bTable[nextHashL] next := load6432(src, nextS) entry := tableEntry{offset: s + e.cur} e.table[nextHashS] = entry eLong := &e.bTable[nextHashL] eLong.Cur, eLong.Prev = entry, eLong.Cur nextHashS = hashLen(next, tableBits, hashShortBytes) nextHashL = hash7(next, tableBits) t = lCandidate.Cur.offset - e.cur if s-t < maxMatchOffset { if uint32(cv) == load3232(src, lCandidate.Cur.offset-e.cur) { // Store the next match e.table[nextHashS] = tableEntry{offset: nextS + e.cur} eLong := &e.bTable[nextHashL] eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur t2 := lCandidate.Prev.offset - e.cur if s-t2 < maxMatchOffset && uint32(cv) == load3232(src, lCandidate.Prev.offset-e.cur) { l = e.matchlen(s+4, t+4, src) + 4 ml1 := e.matchlen(s+4, t2+4, src) + 4 if ml1 > l { t = t2 l = ml1 break } } break } t = lCandidate.Prev.offset - e.cur if s-t < maxMatchOffset && uint32(cv) == load3232(src, lCandidate.Prev.offset-e.cur) { // Store the next match e.table[nextHashS] = tableEntry{offset: nextS + e.cur} eLong := &e.bTable[nextHashL] eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur break } } t = sCandidate.offset - e.cur if s-t < maxMatchOffset && uint32(cv) == load3232(src, sCandidate.offset-e.cur) { // Found a 4 match... l = e.matchlen(s+4, t+4, src) + 4 lCandidate = e.bTable[nextHashL] // Store the next match e.table[nextHashS] = tableEntry{offset: nextS + e.cur} eLong := &e.bTable[nextHashL] eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur // If the next long is a candidate, use that... t2 := lCandidate.Cur.offset - e.cur if nextS-t2 < maxMatchOffset { if load3232(src, lCandidate.Cur.offset-e.cur) == uint32(next) { ml := e.matchlen(nextS+4, t2+4, src) + 4 if ml > l { t = t2 s = nextS l = ml break } } // If the previous long is a candidate, use that... t2 = lCandidate.Prev.offset - e.cur if nextS-t2 < maxMatchOffset && load3232(src, lCandidate.Prev.offset-e.cur) == uint32(next) { ml := e.matchlen(nextS+4, t2+4, src) + 4 if ml > l { t = t2 s = nextS l = ml break } } } break } cv = next } // A 4-byte match has been found. We'll later see if more than 4 bytes // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit // them as literal bytes. if l == 0 { // Extend the 4-byte match as long as possible. l = e.matchlenLong(s+4, t+4, src) + 4 } else if l == maxMatchLength { l += e.matchlenLong(s+l, t+l, src) } // Try to locate a better match by checking the end of best match... if sAt := s + l; l < 30 && sAt < sLimit { // Allow some bytes at the beginning to mismatch. // Sweet spot is 2/3 bytes depending on input. // 3 is only a little better when it is but sometimes a lot worse. // The skipped bytes are tested in Extend backwards, // and still picked up as part of the match if they do. const skipBeginning = 2 eLong := e.bTable[hash7(load6432(src, sAt), tableBits)].Cur.offset t2 := eLong - e.cur - l + skipBeginning s2 := s + skipBeginning off := s2 - t2 if t2 >= 0 && off < maxMatchOffset && off > 0 { if l2 := e.matchlenLong(s2, t2, src); l2 > l { t = t2 l = l2 s = s2 } } } // Extend backwards for t > 0 && s > nextEmit && src[t-1] == src[s-1] { s-- t-- l++ } if nextEmit < s { if false { emitLiteral(dst, src[nextEmit:s]) } else { for _, v := range src[nextEmit:s] { dst.tokens[dst.n] = token(v) dst.litHist[v]++ dst.n++ } } } if debugDeflate { if t >= s { panic(fmt.Sprintln("s-t", s, t)) } if (s - t) > maxMatchOffset { panic(fmt.Sprintln("mmo", s-t)) } if l < baseMatchLength { panic("bml") } } dst.AddMatchLong(l, uint32(s-t-baseMatchOffset)) s += l nextEmit = s if nextS >= s { s = nextS + 1 } if s >= sLimit { goto emitRemainder } // Store every 3rd hash in-between. if true { const hashEvery = 3 i := s - l + 1 if i < s-1 { cv := load6432(src, i) t := tableEntry{offset: i + e.cur} e.table[hashLen(cv, tableBits, hashShortBytes)] = t eLong := &e.bTable[hash7(cv, tableBits)] eLong.Cur, eLong.Prev = t, eLong.Cur // Do an long at i+1 cv >>= 8 t = tableEntry{offset: t.offset + 1} eLong = &e.bTable[hash7(cv, tableBits)] eLong.Cur, eLong.Prev = t, eLong.Cur // We only have enough bits for a short entry at i+2 cv >>= 8 t = tableEntry{offset: t.offset + 1} e.table[hashLen(cv, tableBits, hashShortBytes)] = t // Skip one - otherwise we risk hitting 's' i += 4 for ; i < s-1; i += hashEvery { cv := load6432(src, i) t := tableEntry{offset: i + e.cur} t2 := tableEntry{offset: t.offset + 1} eLong := &e.bTable[hash7(cv, tableBits)] eLong.Cur, eLong.Prev = t, eLong.Cur e.table[hashLen(cv>>8, tableBits, hashShortBytes)] = t2 } } } // We could immediately start working at s now, but to improve // compression we first update the hash table at s-1 and at s. x := load6432(src, s-1) o := e.cur + s - 1 prevHashS := hashLen(x, tableBits, hashShortBytes) prevHashL := hash7(x, tableBits) e.table[prevHashS] = tableEntry{offset: o} eLong := &e.bTable[prevHashL] eLong.Cur, eLong.Prev = tableEntry{offset: o}, eLong.Cur cv = x >> 8 } emitRemainder: if int(nextEmit) < len(src) { // If nothing was added, don't encode literals. if dst.n == 0 { return } emitLiteral(dst, src[nextEmit:]) } } // Reset the encoding table. func (e *fastEncL5Window) Reset() { // We keep the same allocs, since we are compressing the same block sizes. if cap(e.hist) < allocHistory { e.hist = make([]byte, 0, allocHistory) } // We offset current position so everything will be out of reach. // If we are above the buffer reset it will be cleared anyway since len(hist) == 0. if e.cur <= int32(bufferReset) { e.cur += e.maxOffset + int32(len(e.hist)) } e.hist = e.hist[:0] } func (e *fastEncL5Window) addBlock(src []byte) int32 { // check if we have space already maxMatchOffset := e.maxOffset if len(e.hist)+len(src) > cap(e.hist) { if cap(e.hist) == 0 { e.hist = make([]byte, 0, allocHistory) } else { if cap(e.hist) < int(maxMatchOffset*2) { panic("unexpected buffer size") } // Move down offset := int32(len(e.hist)) - maxMatchOffset copy(e.hist[0:maxMatchOffset], e.hist[offset:]) e.cur += offset e.hist = e.hist[:maxMatchOffset] } } s := int32(len(e.hist)) e.hist = append(e.hist, src...) return s } // matchlen will return the match length between offsets and t in src. // The maximum length returned is maxMatchLength - 4. // It is assumed that s > t, that t >=0 and s < len(src). func (e *fastEncL5Window) matchlen(s, t int32, src []byte) int32 { if debugDecode { if t >= s { panic(fmt.Sprint("t >=s:", t, s)) } if int(s) >= len(src) { panic(fmt.Sprint("s >= len(src):", s, len(src))) } if t < 0 { panic(fmt.Sprint("t < 0:", t)) } if s-t > e.maxOffset { panic(fmt.Sprint(s, "-", t, "(", s-t, ") > maxMatchLength (", maxMatchOffset, ")")) } } s1 := int(s) + maxMatchLength - 4 if s1 > len(src) { s1 = len(src) } // Extend the match to be as long as possible. return int32(matchLen(src[s:s1], src[t:])) } // matchlenLong will return the match length between offsets and t in src. // It is assumed that s > t, that t >=0 and s < len(src). func (e *fastEncL5Window) matchlenLong(s, t int32, src []byte) int32 { if debugDeflate { if t >= s { panic(fmt.Sprint("t >=s:", t, s)) } if int(s) >= len(src) { panic(fmt.Sprint("s >= len(src):", s, len(src))) } if t < 0 { panic(fmt.Sprint("t < 0:", t)) } if s-t > e.maxOffset { panic(fmt.Sprint(s, "-", t, "(", s-t, ") > maxMatchLength (", maxMatchOffset, ")")) } } // Extend the match to be as long as possible. return int32(matchLen(src[s:], src[t:])) } ================================================ FILE: vendor/github.com/klauspost/compress/flate/level6.go ================================================ package flate import "fmt" type fastEncL6 struct { fastGen table [tableSize]tableEntry bTable [tableSize]tableEntryPrev } func (e *fastEncL6) Encode(dst *tokens, src []byte) { const ( inputMargin = 12 - 1 minNonLiteralBlockSize = 1 + 1 + inputMargin hashShortBytes = 4 ) if debugDeflate && e.cur < 0 { panic(fmt.Sprint("e.cur < 0: ", e.cur)) } // Protect against e.cur wraparound. for e.cur >= bufferReset { if len(e.hist) == 0 { for i := range e.table[:] { e.table[i] = tableEntry{} } for i := range e.bTable[:] { e.bTable[i] = tableEntryPrev{} } e.cur = maxMatchOffset break } // Shift down everything in the table that isn't already too far away. minOff := e.cur + int32(len(e.hist)) - maxMatchOffset for i := range e.table[:] { v := e.table[i].offset if v <= minOff { v = 0 } else { v = v - e.cur + maxMatchOffset } e.table[i].offset = v } for i := range e.bTable[:] { v := e.bTable[i] if v.Cur.offset <= minOff { v.Cur.offset = 0 v.Prev.offset = 0 } else { v.Cur.offset = v.Cur.offset - e.cur + maxMatchOffset if v.Prev.offset <= minOff { v.Prev.offset = 0 } else { v.Prev.offset = v.Prev.offset - e.cur + maxMatchOffset } } e.bTable[i] = v } e.cur = maxMatchOffset } s := e.addBlock(src) // This check isn't in the Snappy implementation, but there, the caller // instead of the callee handles this case. if len(src) < minNonLiteralBlockSize { // We do not fill the token table. // This will be picked up by caller. dst.n = uint16(len(src)) return } // Override src src = e.hist nextEmit := s // sLimit is when to stop looking for offset/length copies. The inputMargin // lets us use a fast path for emitLiteral in the main loop, while we are // looking for copies. sLimit := int32(len(src) - inputMargin) // nextEmit is where in src the next emitLiteral should start from. cv := load6432(src, s) // Repeat MUST be > 1 and within range repeat := int32(1) for { const skipLog = 7 const doEvery = 1 nextS := s var l int32 var t int32 for { nextHashS := hashLen(cv, tableBits, hashShortBytes) nextHashL := hash7(cv, tableBits) s = nextS nextS = s + doEvery + (s-nextEmit)>>skipLog if nextS > sLimit { goto emitRemainder } // Fetch a short+long candidate sCandidate := e.table[nextHashS] lCandidate := e.bTable[nextHashL] next := load6432(src, nextS) entry := tableEntry{offset: s + e.cur} e.table[nextHashS] = entry eLong := &e.bTable[nextHashL] eLong.Cur, eLong.Prev = entry, eLong.Cur // Calculate hashes of 'next' nextHashS = hashLen(next, tableBits, hashShortBytes) nextHashL = hash7(next, tableBits) t = lCandidate.Cur.offset - e.cur if s-t < maxMatchOffset { if uint32(cv) == load3232(src, lCandidate.Cur.offset-e.cur) { // Long candidate matches at least 4 bytes. // Store the next match e.table[nextHashS] = tableEntry{offset: nextS + e.cur} eLong := &e.bTable[nextHashL] eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur // Check the previous long candidate as well. t2 := lCandidate.Prev.offset - e.cur if s-t2 < maxMatchOffset && uint32(cv) == load3232(src, lCandidate.Prev.offset-e.cur) { l = e.matchlen(s+4, t+4, src) + 4 ml1 := e.matchlen(s+4, t2+4, src) + 4 if ml1 > l { t = t2 l = ml1 break } } break } // Current value did not match, but check if previous long value does. t = lCandidate.Prev.offset - e.cur if s-t < maxMatchOffset && uint32(cv) == load3232(src, lCandidate.Prev.offset-e.cur) { // Store the next match e.table[nextHashS] = tableEntry{offset: nextS + e.cur} eLong := &e.bTable[nextHashL] eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur break } } t = sCandidate.offset - e.cur if s-t < maxMatchOffset && uint32(cv) == load3232(src, sCandidate.offset-e.cur) { // Found a 4 match... l = e.matchlen(s+4, t+4, src) + 4 // Look up next long candidate (at nextS) lCandidate = e.bTable[nextHashL] // Store the next match e.table[nextHashS] = tableEntry{offset: nextS + e.cur} eLong := &e.bTable[nextHashL] eLong.Cur, eLong.Prev = tableEntry{offset: nextS + e.cur}, eLong.Cur // Check repeat at s + repOff const repOff = 1 t2 := s - repeat + repOff if load3232(src, t2) == uint32(cv>>(8*repOff)) { ml := e.matchlen(s+4+repOff, t2+4, src) + 4 if ml > l { t = t2 l = ml s += repOff // Not worth checking more. break } } // If the next long is a candidate, use that... t2 = lCandidate.Cur.offset - e.cur if nextS-t2 < maxMatchOffset { if load3232(src, lCandidate.Cur.offset-e.cur) == uint32(next) { ml := e.matchlen(nextS+4, t2+4, src) + 4 if ml > l { t = t2 s = nextS l = ml // This is ok, but check previous as well. } } // If the previous long is a candidate, use that... t2 = lCandidate.Prev.offset - e.cur if nextS-t2 < maxMatchOffset && load3232(src, lCandidate.Prev.offset-e.cur) == uint32(next) { ml := e.matchlen(nextS+4, t2+4, src) + 4 if ml > l { t = t2 s = nextS l = ml break } } } break } cv = next } // A 4-byte match has been found. We'll later see if more than 4 bytes // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit // them as literal bytes. // Extend the 4-byte match as long as possible. if l == 0 { l = e.matchlenLong(s+4, t+4, src) + 4 } else if l == maxMatchLength { l += e.matchlenLong(s+l, t+l, src) } // Try to locate a better match by checking the end-of-match... if sAt := s + l; sAt < sLimit { // Allow some bytes at the beginning to mismatch. // Sweet spot is 2/3 bytes depending on input. // 3 is only a little better when it is but sometimes a lot worse. // The skipped bytes are tested in Extend backwards, // and still picked up as part of the match if they do. const skipBeginning = 2 eLong := &e.bTable[hash7(load6432(src, sAt), tableBits)] // Test current t2 := eLong.Cur.offset - e.cur - l + skipBeginning s2 := s + skipBeginning off := s2 - t2 if off < maxMatchOffset { if off > 0 && t2 >= 0 { if l2 := e.matchlenLong(s2, t2, src); l2 > l { t = t2 l = l2 s = s2 } } // Test next: t2 = eLong.Prev.offset - e.cur - l + skipBeginning off := s2 - t2 if off > 0 && off < maxMatchOffset && t2 >= 0 { if l2 := e.matchlenLong(s2, t2, src); l2 > l { t = t2 l = l2 s = s2 } } } } // Extend backwards for t > 0 && s > nextEmit && src[t-1] == src[s-1] { s-- t-- l++ } if nextEmit < s { if false { emitLiteral(dst, src[nextEmit:s]) } else { for _, v := range src[nextEmit:s] { dst.tokens[dst.n] = token(v) dst.litHist[v]++ dst.n++ } } } if false { if t >= s { panic(fmt.Sprintln("s-t", s, t)) } if (s - t) > maxMatchOffset { panic(fmt.Sprintln("mmo", s-t)) } if l < baseMatchLength { panic("bml") } } dst.AddMatchLong(l, uint32(s-t-baseMatchOffset)) repeat = s - t s += l nextEmit = s if nextS >= s { s = nextS + 1 } if s >= sLimit { // Index after match end. for i := nextS + 1; i < int32(len(src))-8; i += 2 { cv := load6432(src, i) e.table[hashLen(cv, tableBits, hashShortBytes)] = tableEntry{offset: i + e.cur} eLong := &e.bTable[hash7(cv, tableBits)] eLong.Cur, eLong.Prev = tableEntry{offset: i + e.cur}, eLong.Cur } goto emitRemainder } // Store every long hash in-between and every second short. if true { for i := nextS + 1; i < s-1; i += 2 { cv := load6432(src, i) t := tableEntry{offset: i + e.cur} t2 := tableEntry{offset: t.offset + 1} eLong := &e.bTable[hash7(cv, tableBits)] eLong2 := &e.bTable[hash7(cv>>8, tableBits)] e.table[hashLen(cv, tableBits, hashShortBytes)] = t eLong.Cur, eLong.Prev = t, eLong.Cur eLong2.Cur, eLong2.Prev = t2, eLong2.Cur } } // We could immediately start working at s now, but to improve // compression we first update the hash table at s-1 and at s. cv = load6432(src, s) } emitRemainder: if int(nextEmit) < len(src) { // If nothing was added, don't encode literals. if dst.n == 0 { return } emitLiteral(dst, src[nextEmit:]) } } ================================================ FILE: vendor/github.com/klauspost/compress/flate/matchlen_amd64.go ================================================ //go:build amd64 && !appengine && !noasm && gc // +build amd64,!appengine,!noasm,gc // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. package flate // matchLen returns how many bytes match in a and b // // It assumes that: // // len(a) <= len(b) and len(a) > 0 // //go:noescape func matchLen(a []byte, b []byte) int ================================================ FILE: vendor/github.com/klauspost/compress/flate/matchlen_amd64.s ================================================ // Copied from S2 implementation. //go:build !appengine && !noasm && gc && !noasm #include "textflag.h" // func matchLen(a []byte, b []byte) int // Requires: BMI TEXT ·matchLen(SB), NOSPLIT, $0-56 MOVQ a_base+0(FP), AX MOVQ b_base+24(FP), CX MOVQ a_len+8(FP), DX // matchLen XORL SI, SI CMPL DX, $0x08 JB matchlen_match4_standalone matchlen_loopback_standalone: MOVQ (AX)(SI*1), BX XORQ (CX)(SI*1), BX TESTQ BX, BX JZ matchlen_loop_standalone #ifdef GOAMD64_v3 TZCNTQ BX, BX #else BSFQ BX, BX #endif SARQ $0x03, BX LEAL (SI)(BX*1), SI JMP gen_match_len_end matchlen_loop_standalone: LEAL -8(DX), DX LEAL 8(SI), SI CMPL DX, $0x08 JAE matchlen_loopback_standalone matchlen_match4_standalone: CMPL DX, $0x04 JB matchlen_match2_standalone MOVL (AX)(SI*1), BX CMPL (CX)(SI*1), BX JNE matchlen_match2_standalone LEAL -4(DX), DX LEAL 4(SI), SI matchlen_match2_standalone: CMPL DX, $0x02 JB matchlen_match1_standalone MOVW (AX)(SI*1), BX CMPW (CX)(SI*1), BX JNE matchlen_match1_standalone LEAL -2(DX), DX LEAL 2(SI), SI matchlen_match1_standalone: CMPL DX, $0x01 JB gen_match_len_end MOVB (AX)(SI*1), BL CMPB (CX)(SI*1), BL JNE gen_match_len_end INCL SI gen_match_len_end: MOVQ SI, ret+48(FP) RET ================================================ FILE: vendor/github.com/klauspost/compress/flate/matchlen_generic.go ================================================ //go:build !amd64 || appengine || !gc || noasm // +build !amd64 appengine !gc noasm // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. package flate import ( "encoding/binary" "math/bits" ) // matchLen returns the maximum common prefix length of a and b. // a must be the shortest of the two. func matchLen(a, b []byte) (n int) { for ; len(a) >= 8 && len(b) >= 8; a, b = a[8:], b[8:] { diff := binary.LittleEndian.Uint64(a) ^ binary.LittleEndian.Uint64(b) if diff != 0 { return n + bits.TrailingZeros64(diff)>>3 } n += 8 } for i := range a { if a[i] != b[i] { break } n++ } return n } ================================================ FILE: vendor/github.com/klauspost/compress/flate/regmask_amd64.go ================================================ package flate const ( // Masks for shifts with register sizes of the shift value. // This can be used to work around the x86 design of shifting by mod register size. // It can be used when a variable shift is always smaller than the register size. // reg8SizeMaskX - shift value is 8 bits, shifted is X reg8SizeMask8 = 7 reg8SizeMask16 = 15 reg8SizeMask32 = 31 reg8SizeMask64 = 63 // reg16SizeMaskX - shift value is 16 bits, shifted is X reg16SizeMask8 = reg8SizeMask8 reg16SizeMask16 = reg8SizeMask16 reg16SizeMask32 = reg8SizeMask32 reg16SizeMask64 = reg8SizeMask64 // reg32SizeMaskX - shift value is 32 bits, shifted is X reg32SizeMask8 = reg8SizeMask8 reg32SizeMask16 = reg8SizeMask16 reg32SizeMask32 = reg8SizeMask32 reg32SizeMask64 = reg8SizeMask64 // reg64SizeMaskX - shift value is 64 bits, shifted is X reg64SizeMask8 = reg8SizeMask8 reg64SizeMask16 = reg8SizeMask16 reg64SizeMask32 = reg8SizeMask32 reg64SizeMask64 = reg8SizeMask64 // regSizeMaskUintX - shift value is uint, shifted is X regSizeMaskUint8 = reg8SizeMask8 regSizeMaskUint16 = reg8SizeMask16 regSizeMaskUint32 = reg8SizeMask32 regSizeMaskUint64 = reg8SizeMask64 ) ================================================ FILE: vendor/github.com/klauspost/compress/flate/regmask_other.go ================================================ //go:build !amd64 // +build !amd64 package flate const ( // Masks for shifts with register sizes of the shift value. // This can be used to work around the x86 design of shifting by mod register size. // It can be used when a variable shift is always smaller than the register size. // reg8SizeMaskX - shift value is 8 bits, shifted is X reg8SizeMask8 = 0xff reg8SizeMask16 = 0xff reg8SizeMask32 = 0xff reg8SizeMask64 = 0xff // reg16SizeMaskX - shift value is 16 bits, shifted is X reg16SizeMask8 = 0xffff reg16SizeMask16 = 0xffff reg16SizeMask32 = 0xffff reg16SizeMask64 = 0xffff // reg32SizeMaskX - shift value is 32 bits, shifted is X reg32SizeMask8 = 0xffffffff reg32SizeMask16 = 0xffffffff reg32SizeMask32 = 0xffffffff reg32SizeMask64 = 0xffffffff // reg64SizeMaskX - shift value is 64 bits, shifted is X reg64SizeMask8 = 0xffffffffffffffff reg64SizeMask16 = 0xffffffffffffffff reg64SizeMask32 = 0xffffffffffffffff reg64SizeMask64 = 0xffffffffffffffff // regSizeMaskUintX - shift value is uint, shifted is X regSizeMaskUint8 = ^uint(0) regSizeMaskUint16 = ^uint(0) regSizeMaskUint32 = ^uint(0) regSizeMaskUint64 = ^uint(0) ) ================================================ FILE: vendor/github.com/klauspost/compress/flate/stateless.go ================================================ package flate import ( "io" "math" "sync" ) const ( maxStatelessBlock = math.MaxInt16 // dictionary will be taken from maxStatelessBlock, so limit it. maxStatelessDict = 8 << 10 slTableBits = 13 slTableSize = 1 << slTableBits slTableShift = 32 - slTableBits ) type statelessWriter struct { dst io.Writer closed bool } func (s *statelessWriter) Close() error { if s.closed { return nil } s.closed = true // Emit EOF block return StatelessDeflate(s.dst, nil, true, nil) } func (s *statelessWriter) Write(p []byte) (n int, err error) { err = StatelessDeflate(s.dst, p, false, nil) if err != nil { return 0, err } return len(p), nil } func (s *statelessWriter) Reset(w io.Writer) { s.dst = w s.closed = false } // NewStatelessWriter will do compression but without maintaining any state // between Write calls. // There will be no memory kept between Write calls, // but compression and speed will be suboptimal. // Because of this, the size of actual Write calls will affect output size. func NewStatelessWriter(dst io.Writer) io.WriteCloser { return &statelessWriter{dst: dst} } // bitWriterPool contains bit writers that can be reused. var bitWriterPool = sync.Pool{ New: func() interface{} { return newHuffmanBitWriter(nil) }, } // StatelessDeflate allows compressing directly to a Writer without retaining state. // When returning everything will be flushed. // Up to 8KB of an optional dictionary can be given which is presumed to precede the block. // Longer dictionaries will be truncated and will still produce valid output. // Sending nil dictionary is perfectly fine. func StatelessDeflate(out io.Writer, in []byte, eof bool, dict []byte) error { var dst tokens bw := bitWriterPool.Get().(*huffmanBitWriter) bw.reset(out) defer func() { // don't keep a reference to our output bw.reset(nil) bitWriterPool.Put(bw) }() if eof && len(in) == 0 { // Just write an EOF block. // Could be faster... bw.writeStoredHeader(0, true) bw.flush() return bw.err } // Truncate dict if len(dict) > maxStatelessDict { dict = dict[len(dict)-maxStatelessDict:] } // For subsequent loops, keep shallow dict reference to avoid alloc+copy. var inDict []byte for len(in) > 0 { todo := in if len(inDict) > 0 { if len(todo) > maxStatelessBlock-maxStatelessDict { todo = todo[:maxStatelessBlock-maxStatelessDict] } } else if len(todo) > maxStatelessBlock-len(dict) { todo = todo[:maxStatelessBlock-len(dict)] } inOrg := in in = in[len(todo):] uncompressed := todo if len(dict) > 0 { // combine dict and source bufLen := len(todo) + len(dict) combined := make([]byte, bufLen) copy(combined, dict) copy(combined[len(dict):], todo) todo = combined } // Compress if len(inDict) == 0 { statelessEnc(&dst, todo, int16(len(dict))) } else { statelessEnc(&dst, inDict[:maxStatelessDict+len(todo)], maxStatelessDict) } isEof := eof && len(in) == 0 if dst.n == 0 { bw.writeStoredHeader(len(uncompressed), isEof) if bw.err != nil { return bw.err } bw.writeBytes(uncompressed) } else if int(dst.n) > len(uncompressed)-len(uncompressed)>>4 { // If we removed less than 1/16th, huffman compress the block. bw.writeBlockHuff(isEof, uncompressed, len(in) == 0) } else { bw.writeBlockDynamic(&dst, isEof, uncompressed, len(in) == 0) } if len(in) > 0 { // Retain a dict if we have more inDict = inOrg[len(uncompressed)-maxStatelessDict:] dict = nil dst.Reset() } if bw.err != nil { return bw.err } } if !eof { // Align, only a stored block can do that. bw.writeStoredHeader(0, false) } bw.flush() return bw.err } func hashSL(u uint32) uint32 { return (u * 0x1e35a7bd) >> slTableShift } func load3216(b []byte, i int16) uint32 { // Help the compiler eliminate bounds checks on the read so it can be done in a single read. b = b[i:] b = b[:4] return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 } func load6416(b []byte, i int16) uint64 { // Help the compiler eliminate bounds checks on the read so it can be done in a single read. b = b[i:] b = b[:8] return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 } func statelessEnc(dst *tokens, src []byte, startAt int16) { const ( inputMargin = 12 - 1 minNonLiteralBlockSize = 1 + 1 + inputMargin ) type tableEntry struct { offset int16 } var table [slTableSize]tableEntry // This check isn't in the Snappy implementation, but there, the caller // instead of the callee handles this case. if len(src)-int(startAt) < minNonLiteralBlockSize { // We do not fill the token table. // This will be picked up by caller. dst.n = 0 return } // Index until startAt if startAt > 0 { cv := load3232(src, 0) for i := int16(0); i < startAt; i++ { table[hashSL(cv)] = tableEntry{offset: i} cv = (cv >> 8) | (uint32(src[i+4]) << 24) } } s := startAt + 1 nextEmit := startAt // sLimit is when to stop looking for offset/length copies. The inputMargin // lets us use a fast path for emitLiteral in the main loop, while we are // looking for copies. sLimit := int16(len(src) - inputMargin) // nextEmit is where in src the next emitLiteral should start from. cv := load3216(src, s) for { const skipLog = 5 const doEvery = 2 nextS := s var candidate tableEntry for { nextHash := hashSL(cv) candidate = table[nextHash] nextS = s + doEvery + (s-nextEmit)>>skipLog if nextS > sLimit || nextS <= 0 { goto emitRemainder } now := load6416(src, nextS) table[nextHash] = tableEntry{offset: s} nextHash = hashSL(uint32(now)) if cv == load3216(src, candidate.offset) { table[nextHash] = tableEntry{offset: nextS} break } // Do one right away... cv = uint32(now) s = nextS nextS++ candidate = table[nextHash] now >>= 8 table[nextHash] = tableEntry{offset: s} if cv == load3216(src, candidate.offset) { table[nextHash] = tableEntry{offset: nextS} break } cv = uint32(now) s = nextS } // A 4-byte match has been found. We'll later see if more than 4 bytes // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit // them as literal bytes. for { // Invariant: we have a 4-byte match at s, and no need to emit any // literal bytes prior to s. // Extend the 4-byte match as long as possible. t := candidate.offset l := int16(matchLen(src[s+4:], src[t+4:]) + 4) // Extend backwards for t > 0 && s > nextEmit && src[t-1] == src[s-1] { s-- t-- l++ } if nextEmit < s { if false { emitLiteral(dst, src[nextEmit:s]) } else { for _, v := range src[nextEmit:s] { dst.tokens[dst.n] = token(v) dst.litHist[v]++ dst.n++ } } } // Save the match found dst.AddMatchLong(int32(l), uint32(s-t-baseMatchOffset)) s += l nextEmit = s if nextS >= s { s = nextS + 1 } if s >= sLimit { goto emitRemainder } // We could immediately start working at s now, but to improve // compression we first update the hash table at s-2 and at s. If // another emitCopy is not our next move, also calculate nextHash // at s+1. At least on GOARCH=amd64, these three hash calculations // are faster as one load64 call (with some shifts) instead of // three load32 calls. x := load6416(src, s-2) o := s - 2 prevHash := hashSL(uint32(x)) table[prevHash] = tableEntry{offset: o} x >>= 16 currHash := hashSL(uint32(x)) candidate = table[currHash] table[currHash] = tableEntry{offset: o + 2} if uint32(x) != load3216(src, candidate.offset) { cv = uint32(x >> 8) s++ break } } } emitRemainder: if int(nextEmit) < len(src) { // If nothing was added, don't encode literals. if dst.n == 0 { return } emitLiteral(dst, src[nextEmit:]) } } ================================================ FILE: vendor/github.com/klauspost/compress/flate/token.go ================================================ // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package flate import ( "bytes" "encoding/binary" "fmt" "io" "math" ) const ( // bits 0-16 xoffset = offset - MIN_OFFSET_SIZE, or literal - 16 bits // bits 16-22 offsetcode - 5 bits // bits 22-30 xlength = length - MIN_MATCH_LENGTH - 8 bits // bits 30-32 type 0 = literal 1=EOF 2=Match 3=Unused - 2 bits lengthShift = 22 offsetMask = 1<maxnumlit offHist [32]uint16 // offset codes litHist [256]uint16 // codes 0->255 nFilled int n uint16 // Must be able to contain maxStoreBlockSize tokens [maxStoreBlockSize + 1]token } func (t *tokens) Reset() { if t.n == 0 { return } t.n = 0 t.nFilled = 0 for i := range t.litHist[:] { t.litHist[i] = 0 } for i := range t.extraHist[:] { t.extraHist[i] = 0 } for i := range t.offHist[:] { t.offHist[i] = 0 } } func (t *tokens) Fill() { if t.n == 0 { return } for i, v := range t.litHist[:] { if v == 0 { t.litHist[i] = 1 t.nFilled++ } } for i, v := range t.extraHist[:literalCount-256] { if v == 0 { t.nFilled++ t.extraHist[i] = 1 } } for i, v := range t.offHist[:offsetCodeCount] { if v == 0 { t.offHist[i] = 1 } } } func indexTokens(in []token) tokens { var t tokens t.indexTokens(in) return t } func (t *tokens) indexTokens(in []token) { t.Reset() for _, tok := range in { if tok < matchType { t.AddLiteral(tok.literal()) continue } t.AddMatch(uint32(tok.length()), tok.offset()&matchOffsetOnlyMask) } } // emitLiteral writes a literal chunk and returns the number of bytes written. func emitLiteral(dst *tokens, lit []byte) { for _, v := range lit { dst.tokens[dst.n] = token(v) dst.litHist[v]++ dst.n++ } } func (t *tokens) AddLiteral(lit byte) { t.tokens[t.n] = token(lit) t.litHist[lit]++ t.n++ } // from https://stackoverflow.com/a/28730362 func mFastLog2(val float32) float32 { ux := int32(math.Float32bits(val)) log2 := (float32)(((ux >> 23) & 255) - 128) ux &= -0x7f800001 ux += 127 << 23 uval := math.Float32frombits(uint32(ux)) log2 += ((-0.34484843)*uval+2.02466578)*uval - 0.67487759 return log2 } // EstimatedBits will return an minimum size estimated by an *optimal* // compression of the block. // The size of the block func (t *tokens) EstimatedBits() int { shannon := float32(0) bits := int(0) nMatches := 0 total := int(t.n) + t.nFilled if total > 0 { invTotal := 1.0 / float32(total) for _, v := range t.litHist[:] { if v > 0 { n := float32(v) shannon += atLeastOne(-mFastLog2(n*invTotal)) * n } } // Just add 15 for EOB shannon += 15 for i, v := range t.extraHist[1 : literalCount-256] { if v > 0 { n := float32(v) shannon += atLeastOne(-mFastLog2(n*invTotal)) * n bits += int(lengthExtraBits[i&31]) * int(v) nMatches += int(v) } } } if nMatches > 0 { invTotal := 1.0 / float32(nMatches) for i, v := range t.offHist[:offsetCodeCount] { if v > 0 { n := float32(v) shannon += atLeastOne(-mFastLog2(n*invTotal)) * n bits += int(offsetExtraBits[i&31]) * int(v) } } } return int(shannon) + bits } // AddMatch adds a match to the tokens. // This function is very sensitive to inlining and right on the border. func (t *tokens) AddMatch(xlength uint32, xoffset uint32) { if debugDeflate { if xlength >= maxMatchLength+baseMatchLength { panic(fmt.Errorf("invalid length: %v", xlength)) } if xoffset >= maxMatchOffset+baseMatchOffset { panic(fmt.Errorf("invalid offset: %v", xoffset)) } } oCode := offsetCode(xoffset) xoffset |= oCode << 16 t.extraHist[lengthCodes1[uint8(xlength)]]++ t.offHist[oCode&31]++ t.tokens[t.n] = token(matchType | xlength<= maxMatchOffset+baseMatchOffset { panic(fmt.Errorf("invalid offset: %v", xoffset)) } } oc := offsetCode(xoffset) xoffset |= oc << 16 for xlength > 0 { xl := xlength if xl > 258 { // We need to have at least baseMatchLength left over for next loop. if xl > 258+baseMatchLength { xl = 258 } else { xl = 258 - baseMatchLength } } xlength -= xl xl -= baseMatchLength t.extraHist[lengthCodes1[uint8(xl)]]++ t.offHist[oc&31]++ t.tokens[t.n] = token(matchType | uint32(xl)<> lengthShift) } // Convert length to code. func lengthCode(len uint8) uint8 { return lengthCodes[len] } // Returns the offset code corresponding to a specific offset func offsetCode(off uint32) uint32 { if false { if off < uint32(len(offsetCodes)) { return offsetCodes[off&255] } else if off>>7 < uint32(len(offsetCodes)) { return offsetCodes[(off>>7)&255] + 14 } else { return offsetCodes[(off>>14)&255] + 28 } } if off < uint32(len(offsetCodes)) { return offsetCodes[uint8(off)] } return offsetCodes14[uint8(off>>7)] } ================================================ FILE: vendor/github.com/klauspost/compress/fse/README.md ================================================ # Finite State Entropy This package provides Finite State Entropy encoding and decoding. Finite State Entropy (also referenced as [tANS](https://en.wikipedia.org/wiki/Asymmetric_numeral_systems#tANS)) encoding provides a fast near-optimal symbol encoding/decoding for byte blocks as implemented in [zstandard](https://github.com/facebook/zstd). This can be used for compressing input with a lot of similar input values to the smallest number of bytes. This does not perform any multi-byte [dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder) as LZ coders, but it can be used as a secondary step to compressors (like Snappy) that does not do entropy encoding. * [Godoc documentation](https://godoc.org/github.com/klauspost/compress/fse) ## News * Feb 2018: First implementation released. Consider this beta software for now. # Usage This package provides a low level interface that allows to compress single independent blocks. Each block is separate, and there is no built in integrity checks. This means that the caller should keep track of block sizes and also do checksums if needed. Compressing a block is done via the [`Compress`](https://godoc.org/github.com/klauspost/compress/fse#Compress) function. You must provide input and will receive the output and maybe an error. These error values can be returned: | Error | Description | |---------------------|-----------------------------------------------------------------------------| | `` | Everything ok, output is returned | | `ErrIncompressible` | Returned when input is judged to be too hard to compress | | `ErrUseRLE` | Returned from the compressor when the input is a single byte value repeated | | `(error)` | An internal error occurred. | As can be seen above there are errors that will be returned even under normal operation so it is important to handle these. To reduce allocations you can provide a [`Scratch`](https://godoc.org/github.com/klauspost/compress/fse#Scratch) object that can be re-used for successive calls. Both compression and decompression accepts a `Scratch` object, and the same object can be used for both. Be aware, that when re-using a `Scratch` object that the *output* buffer is also re-used, so if you are still using this you must set the `Out` field in the scratch to nil. The same buffer is used for compression and decompression output. Decompressing is done by calling the [`Decompress`](https://godoc.org/github.com/klauspost/compress/fse#Decompress) function. You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back your input was likely corrupted. It is important to note that a successful decoding does *not* mean your output matches your original input. There are no integrity checks, so relying on errors from the decompressor does not assure your data is valid. For more detailed usage, see examples in the [godoc documentation](https://godoc.org/github.com/klauspost/compress/fse#pkg-examples). # Performance A lot of factors are affecting speed. Block sizes and compressibility of the material are primary factors. All compression functions are currently only running on the calling goroutine so only one core will be used per block. The compressor is significantly faster if symbols are kept as small as possible. The highest byte value of the input is used to reduce some of the processing, so if all your input is above byte value 64 for instance, it may be beneficial to transpose all your input values down by 64. With moderate block sizes around 64k speed are typically 200MB/s per core for compression and around 300MB/s decompression speed. The same hardware typically does Huffman (deflate) encoding at 125MB/s and decompression at 100MB/s. # Plans At one point, more internals will be exposed to facilitate more "expert" usage of the components. A streaming interface is also likely to be implemented. Likely compatible with [FSE stream format](https://github.com/Cyan4973/FiniteStateEntropy/blob/dev/programs/fileio.c#L261). # Contributing Contributions are always welcome. Be aware that adding public functions will require good justification and breaking changes will likely not be accepted. If in doubt open an issue before writing the PR. ================================================ FILE: vendor/github.com/klauspost/compress/fse/bitreader.go ================================================ // Copyright 2018 Klaus Post. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Based on work Copyright (c) 2013, Yann Collet, released under BSD License. package fse import ( "encoding/binary" "errors" "io" ) // bitReader reads a bitstream in reverse. // The last set bit indicates the start of the stream and is used // for aligning the input. type bitReader struct { in []byte off uint // next byte to read is at in[off - 1] value uint64 bitsRead uint8 } // init initializes and resets the bit reader. func (b *bitReader) init(in []byte) error { if len(in) < 1 { return errors.New("corrupt stream: too short") } b.in = in b.off = uint(len(in)) // The highest bit of the last byte indicates where to start v := in[len(in)-1] if v == 0 { return errors.New("corrupt stream, did not find end of stream") } b.bitsRead = 64 b.value = 0 if len(in) >= 8 { b.fillFastStart() } else { b.fill() b.fill() } b.bitsRead += 8 - uint8(highBits(uint32(v))) return nil } // getBits will return n bits. n can be 0. func (b *bitReader) getBits(n uint8) uint16 { if n == 0 || b.bitsRead >= 64 { return 0 } return b.getBitsFast(n) } // getBitsFast requires that at least one bit is requested every time. // There are no checks if the buffer is filled. func (b *bitReader) getBitsFast(n uint8) uint16 { const regMask = 64 - 1 v := uint16((b.value << (b.bitsRead & regMask)) >> ((regMask + 1 - n) & regMask)) b.bitsRead += n return v } // fillFast() will make sure at least 32 bits are available. // There must be at least 4 bytes available. func (b *bitReader) fillFast() { if b.bitsRead < 32 { return } // 2 bounds checks. v := b.in[b.off-4:] v = v[:4] low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24) b.value = (b.value << 32) | uint64(low) b.bitsRead -= 32 b.off -= 4 } // fill() will make sure at least 32 bits are available. func (b *bitReader) fill() { if b.bitsRead < 32 { return } if b.off > 4 { v := b.in[b.off-4:] v = v[:4] low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24) b.value = (b.value << 32) | uint64(low) b.bitsRead -= 32 b.off -= 4 return } for b.off > 0 { b.value = (b.value << 8) | uint64(b.in[b.off-1]) b.bitsRead -= 8 b.off-- } } // fillFastStart() assumes the bitreader is empty and there is at least 8 bytes to read. func (b *bitReader) fillFastStart() { // Do single re-slice to avoid bounds checks. b.value = binary.LittleEndian.Uint64(b.in[b.off-8:]) b.bitsRead = 0 b.off -= 8 } // finished returns true if all bits have been read from the bit stream. func (b *bitReader) finished() bool { return b.bitsRead >= 64 && b.off == 0 } // close the bitstream and returns an error if out-of-buffer reads occurred. func (b *bitReader) close() error { // Release reference. b.in = nil if b.bitsRead > 64 { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: vendor/github.com/klauspost/compress/fse/bitwriter.go ================================================ // Copyright 2018 Klaus Post. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Based on work Copyright (c) 2013, Yann Collet, released under BSD License. package fse import "fmt" // bitWriter will write bits. // First bit will be LSB of the first byte of output. type bitWriter struct { bitContainer uint64 nBits uint8 out []byte } // bitMask16 is bitmasks. Has extra to avoid bounds check. var bitMask16 = [32]uint16{ 0, 1, 3, 7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF} /* up to 16 bits */ // addBits16NC will add up to 16 bits. // It will not check if there is space for them, // so the caller must ensure that it has flushed recently. func (b *bitWriter) addBits16NC(value uint16, bits uint8) { b.bitContainer |= uint64(value&bitMask16[bits&31]) << (b.nBits & 63) b.nBits += bits } // addBits16Clean will add up to 16 bits. value may not contain more set bits than indicated. // It will not check if there is space for them, so the caller must ensure that it has flushed recently. func (b *bitWriter) addBits16Clean(value uint16, bits uint8) { b.bitContainer |= uint64(value) << (b.nBits & 63) b.nBits += bits } // addBits16ZeroNC will add up to 16 bits. // It will not check if there is space for them, // so the caller must ensure that it has flushed recently. // This is fastest if bits can be zero. func (b *bitWriter) addBits16ZeroNC(value uint16, bits uint8) { if bits == 0 { return } value <<= (16 - bits) & 15 value >>= (16 - bits) & 15 b.bitContainer |= uint64(value) << (b.nBits & 63) b.nBits += bits } // flush will flush all pending full bytes. // There will be at least 56 bits available for writing when this has been called. // Using flush32 is faster, but leaves less space for writing. func (b *bitWriter) flush() { v := b.nBits >> 3 switch v { case 0: case 1: b.out = append(b.out, byte(b.bitContainer), ) case 2: b.out = append(b.out, byte(b.bitContainer), byte(b.bitContainer>>8), ) case 3: b.out = append(b.out, byte(b.bitContainer), byte(b.bitContainer>>8), byte(b.bitContainer>>16), ) case 4: b.out = append(b.out, byte(b.bitContainer), byte(b.bitContainer>>8), byte(b.bitContainer>>16), byte(b.bitContainer>>24), ) case 5: b.out = append(b.out, byte(b.bitContainer), byte(b.bitContainer>>8), byte(b.bitContainer>>16), byte(b.bitContainer>>24), byte(b.bitContainer>>32), ) case 6: b.out = append(b.out, byte(b.bitContainer), byte(b.bitContainer>>8), byte(b.bitContainer>>16), byte(b.bitContainer>>24), byte(b.bitContainer>>32), byte(b.bitContainer>>40), ) case 7: b.out = append(b.out, byte(b.bitContainer), byte(b.bitContainer>>8), byte(b.bitContainer>>16), byte(b.bitContainer>>24), byte(b.bitContainer>>32), byte(b.bitContainer>>40), byte(b.bitContainer>>48), ) case 8: b.out = append(b.out, byte(b.bitContainer), byte(b.bitContainer>>8), byte(b.bitContainer>>16), byte(b.bitContainer>>24), byte(b.bitContainer>>32), byte(b.bitContainer>>40), byte(b.bitContainer>>48), byte(b.bitContainer>>56), ) default: panic(fmt.Errorf("bits (%d) > 64", b.nBits)) } b.bitContainer >>= v << 3 b.nBits &= 7 } // flush32 will flush out, so there are at least 32 bits available for writing. func (b *bitWriter) flush32() { if b.nBits < 32 { return } b.out = append(b.out, byte(b.bitContainer), byte(b.bitContainer>>8), byte(b.bitContainer>>16), byte(b.bitContainer>>24)) b.nBits -= 32 b.bitContainer >>= 32 } // flushAlign will flush remaining full bytes and align to next byte boundary. func (b *bitWriter) flushAlign() { nbBytes := (b.nBits + 7) >> 3 for i := uint8(0); i < nbBytes; i++ { b.out = append(b.out, byte(b.bitContainer>>(i*8))) } b.nBits = 0 b.bitContainer = 0 } // close will write the alignment bit and write the final byte(s) // to the output. func (b *bitWriter) close() { // End mark b.addBits16Clean(1, 1) // flush until next byte. b.flushAlign() } // reset and continue writing by appending to out. func (b *bitWriter) reset(out []byte) { b.bitContainer = 0 b.nBits = 0 b.out = out } ================================================ FILE: vendor/github.com/klauspost/compress/fse/bytereader.go ================================================ // Copyright 2018 Klaus Post. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Based on work Copyright (c) 2013, Yann Collet, released under BSD License. package fse // byteReader provides a byte reader that reads // little endian values from a byte stream. // The input stream is manually advanced. // The reader performs no bounds checks. type byteReader struct { b []byte off int } // init will initialize the reader and set the input. func (b *byteReader) init(in []byte) { b.b = in b.off = 0 } // advance the stream b n bytes. func (b *byteReader) advance(n uint) { b.off += int(n) } // Uint32 returns a little endian uint32 starting at current offset. func (b byteReader) Uint32() uint32 { b2 := b.b[b.off:] b2 = b2[:4] v3 := uint32(b2[3]) v2 := uint32(b2[2]) v1 := uint32(b2[1]) v0 := uint32(b2[0]) return v0 | (v1 << 8) | (v2 << 16) | (v3 << 24) } // unread returns the unread portion of the input. func (b byteReader) unread() []byte { return b.b[b.off:] } // remain will return the number of bytes remaining. func (b byteReader) remain() int { return len(b.b) - b.off } ================================================ FILE: vendor/github.com/klauspost/compress/fse/compress.go ================================================ // Copyright 2018 Klaus Post. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Based on work Copyright (c) 2013, Yann Collet, released under BSD License. package fse import ( "errors" "fmt" ) // Compress the input bytes. Input must be < 2GB. // Provide a Scratch buffer to avoid memory allocations. // Note that the output is also kept in the scratch buffer. // If input is too hard to compress, ErrIncompressible is returned. // If input is a single byte value repeated ErrUseRLE is returned. func Compress(in []byte, s *Scratch) ([]byte, error) { if len(in) <= 1 { return nil, ErrIncompressible } if len(in) > (2<<30)-1 { return nil, errors.New("input too big, must be < 2GB") } s, err := s.prepare(in) if err != nil { return nil, err } // Create histogram, if none was provided. maxCount := s.maxCount if maxCount == 0 { maxCount = s.countSimple(in) } // Reset for next run. s.clearCount = true s.maxCount = 0 if maxCount == len(in) { // One symbol, use RLE return nil, ErrUseRLE } if maxCount == 1 || maxCount < (len(in)>>7) { // Each symbol present maximum once or too well distributed. return nil, ErrIncompressible } s.optimalTableLog() err = s.normalizeCount() if err != nil { return nil, err } err = s.writeCount() if err != nil { return nil, err } if false { err = s.validateNorm() if err != nil { return nil, err } } err = s.buildCTable() if err != nil { return nil, err } err = s.compress(in) if err != nil { return nil, err } s.Out = s.bw.out // Check if we compressed. if len(s.Out) >= len(in) { return nil, ErrIncompressible } return s.Out, nil } // cState contains the compression state of a stream. type cState struct { bw *bitWriter stateTable []uint16 state uint16 } // init will initialize the compression state to the first symbol of the stream. func (c *cState) init(bw *bitWriter, ct *cTable, tableLog uint8, first symbolTransform) { c.bw = bw c.stateTable = ct.stateTable nbBitsOut := (first.deltaNbBits + (1 << 15)) >> 16 im := int32((nbBitsOut << 16) - first.deltaNbBits) lu := (im >> nbBitsOut) + first.deltaFindState c.state = c.stateTable[lu] } // encode the output symbol provided and write it to the bitstream. func (c *cState) encode(symbolTT symbolTransform) { nbBitsOut := (uint32(c.state) + symbolTT.deltaNbBits) >> 16 dstState := int32(c.state>>(nbBitsOut&15)) + symbolTT.deltaFindState c.bw.addBits16NC(c.state, uint8(nbBitsOut)) c.state = c.stateTable[dstState] } // encode the output symbol provided and write it to the bitstream. func (c *cState) encodeZero(symbolTT symbolTransform) { nbBitsOut := (uint32(c.state) + symbolTT.deltaNbBits) >> 16 dstState := int32(c.state>>(nbBitsOut&15)) + symbolTT.deltaFindState c.bw.addBits16ZeroNC(c.state, uint8(nbBitsOut)) c.state = c.stateTable[dstState] } // flush will write the tablelog to the output and flush the remaining full bytes. func (c *cState) flush(tableLog uint8) { c.bw.flush32() c.bw.addBits16NC(c.state, tableLog) c.bw.flush() } // compress is the main compression loop that will encode the input from the last byte to the first. func (s *Scratch) compress(src []byte) error { if len(src) <= 2 { return errors.New("compress: src too small") } tt := s.ct.symbolTT[:256] s.bw.reset(s.Out) // Our two states each encodes every second byte. // Last byte encoded (first byte decoded) will always be encoded by c1. var c1, c2 cState // Encode so remaining size is divisible by 4. ip := len(src) if ip&1 == 1 { c1.init(&s.bw, &s.ct, s.actualTableLog, tt[src[ip-1]]) c2.init(&s.bw, &s.ct, s.actualTableLog, tt[src[ip-2]]) c1.encodeZero(tt[src[ip-3]]) ip -= 3 } else { c2.init(&s.bw, &s.ct, s.actualTableLog, tt[src[ip-1]]) c1.init(&s.bw, &s.ct, s.actualTableLog, tt[src[ip-2]]) ip -= 2 } if ip&2 != 0 { c2.encodeZero(tt[src[ip-1]]) c1.encodeZero(tt[src[ip-2]]) ip -= 2 } src = src[:ip] // Main compression loop. switch { case !s.zeroBits && s.actualTableLog <= 8: // We can encode 4 symbols without requiring a flush. // We do not need to check if any output is 0 bits. for ; len(src) >= 4; src = src[:len(src)-4] { s.bw.flush32() v3, v2, v1, v0 := src[len(src)-4], src[len(src)-3], src[len(src)-2], src[len(src)-1] c2.encode(tt[v0]) c1.encode(tt[v1]) c2.encode(tt[v2]) c1.encode(tt[v3]) } case !s.zeroBits: // We do not need to check if any output is 0 bits. for ; len(src) >= 4; src = src[:len(src)-4] { s.bw.flush32() v3, v2, v1, v0 := src[len(src)-4], src[len(src)-3], src[len(src)-2], src[len(src)-1] c2.encode(tt[v0]) c1.encode(tt[v1]) s.bw.flush32() c2.encode(tt[v2]) c1.encode(tt[v3]) } case s.actualTableLog <= 8: // We can encode 4 symbols without requiring a flush for ; len(src) >= 4; src = src[:len(src)-4] { s.bw.flush32() v3, v2, v1, v0 := src[len(src)-4], src[len(src)-3], src[len(src)-2], src[len(src)-1] c2.encodeZero(tt[v0]) c1.encodeZero(tt[v1]) c2.encodeZero(tt[v2]) c1.encodeZero(tt[v3]) } default: for ; len(src) >= 4; src = src[:len(src)-4] { s.bw.flush32() v3, v2, v1, v0 := src[len(src)-4], src[len(src)-3], src[len(src)-2], src[len(src)-1] c2.encodeZero(tt[v0]) c1.encodeZero(tt[v1]) s.bw.flush32() c2.encodeZero(tt[v2]) c1.encodeZero(tt[v3]) } } // Flush final state. // Used to initialize state when decoding. c2.flush(s.actualTableLog) c1.flush(s.actualTableLog) s.bw.close() return nil } // writeCount will write the normalized histogram count to header. // This is read back by readNCount. func (s *Scratch) writeCount() error { var ( tableLog = s.actualTableLog tableSize = 1 << tableLog previous0 bool charnum uint16 maxHeaderSize = ((int(s.symbolLen)*int(tableLog) + 4 + 2) >> 3) + 3 // Write Table Size bitStream = uint32(tableLog - minTablelog) bitCount = uint(4) remaining = int16(tableSize + 1) /* +1 for extra accuracy */ threshold = int16(tableSize) nbBits = uint(tableLog + 1) ) if cap(s.Out) < maxHeaderSize { s.Out = make([]byte, 0, s.br.remain()+maxHeaderSize) } outP := uint(0) out := s.Out[:maxHeaderSize] // stops at 1 for remaining > 1 { if previous0 { start := charnum for s.norm[charnum] == 0 { charnum++ } for charnum >= start+24 { start += 24 bitStream += uint32(0xFFFF) << bitCount out[outP] = byte(bitStream) out[outP+1] = byte(bitStream >> 8) outP += 2 bitStream >>= 16 } for charnum >= start+3 { start += 3 bitStream += 3 << bitCount bitCount += 2 } bitStream += uint32(charnum-start) << bitCount bitCount += 2 if bitCount > 16 { out[outP] = byte(bitStream) out[outP+1] = byte(bitStream >> 8) outP += 2 bitStream >>= 16 bitCount -= 16 } } count := s.norm[charnum] charnum++ max := (2*threshold - 1) - remaining if count < 0 { remaining += count } else { remaining -= count } count++ // +1 for extra accuracy if count >= threshold { count += max // [0..max[ [max..threshold[ (...) [threshold+max 2*threshold[ } bitStream += uint32(count) << bitCount bitCount += nbBits if count < max { bitCount-- } previous0 = count == 1 if remaining < 1 { return errors.New("internal error: remaining<1") } for remaining < threshold { nbBits-- threshold >>= 1 } if bitCount > 16 { out[outP] = byte(bitStream) out[outP+1] = byte(bitStream >> 8) outP += 2 bitStream >>= 16 bitCount -= 16 } } out[outP] = byte(bitStream) out[outP+1] = byte(bitStream >> 8) outP += (bitCount + 7) / 8 if charnum > s.symbolLen { return errors.New("internal error: charnum > s.symbolLen") } s.Out = out[:outP] return nil } // symbolTransform contains the state transform for a symbol. type symbolTransform struct { deltaFindState int32 deltaNbBits uint32 } // String prints values as a human readable string. func (s symbolTransform) String() string { return fmt.Sprintf("dnbits: %08x, fs:%d", s.deltaNbBits, s.deltaFindState) } // cTable contains tables used for compression. type cTable struct { tableSymbol []byte stateTable []uint16 symbolTT []symbolTransform } // allocCtable will allocate tables needed for compression. // If existing tables a re big enough, they are simply re-used. func (s *Scratch) allocCtable() { tableSize := 1 << s.actualTableLog // get tableSymbol that is big enough. if cap(s.ct.tableSymbol) < tableSize { s.ct.tableSymbol = make([]byte, tableSize) } s.ct.tableSymbol = s.ct.tableSymbol[:tableSize] ctSize := tableSize if cap(s.ct.stateTable) < ctSize { s.ct.stateTable = make([]uint16, ctSize) } s.ct.stateTable = s.ct.stateTable[:ctSize] if cap(s.ct.symbolTT) < 256 { s.ct.symbolTT = make([]symbolTransform, 256) } s.ct.symbolTT = s.ct.symbolTT[:256] } // buildCTable will populate the compression table so it is ready to be used. func (s *Scratch) buildCTable() error { tableSize := uint32(1 << s.actualTableLog) highThreshold := tableSize - 1 var cumul [maxSymbolValue + 2]int16 s.allocCtable() tableSymbol := s.ct.tableSymbol[:tableSize] // symbol start positions { cumul[0] = 0 for ui, v := range s.norm[:s.symbolLen-1] { u := byte(ui) // one less than reference if v == -1 { // Low proba symbol cumul[u+1] = cumul[u] + 1 tableSymbol[highThreshold] = u highThreshold-- } else { cumul[u+1] = cumul[u] + v } } // Encode last symbol separately to avoid overflowing u u := int(s.symbolLen - 1) v := s.norm[s.symbolLen-1] if v == -1 { // Low proba symbol cumul[u+1] = cumul[u] + 1 tableSymbol[highThreshold] = byte(u) highThreshold-- } else { cumul[u+1] = cumul[u] + v } if uint32(cumul[s.symbolLen]) != tableSize { return fmt.Errorf("internal error: expected cumul[s.symbolLen] (%d) == tableSize (%d)", cumul[s.symbolLen], tableSize) } cumul[s.symbolLen] = int16(tableSize) + 1 } // Spread symbols s.zeroBits = false { step := tableStep(tableSize) tableMask := tableSize - 1 var position uint32 // if any symbol > largeLimit, we may have 0 bits output. largeLimit := int16(1 << (s.actualTableLog - 1)) for ui, v := range s.norm[:s.symbolLen] { symbol := byte(ui) if v > largeLimit { s.zeroBits = true } for nbOccurrences := int16(0); nbOccurrences < v; nbOccurrences++ { tableSymbol[position] = symbol position = (position + step) & tableMask for position > highThreshold { position = (position + step) & tableMask } /* Low proba area */ } } // Check if we have gone through all positions if position != 0 { return errors.New("position!=0") } } // Build table table := s.ct.stateTable { tsi := int(tableSize) for u, v := range tableSymbol { // TableU16 : sorted by symbol order; gives next state value table[cumul[v]] = uint16(tsi + u) cumul[v]++ } } // Build Symbol Transformation Table { total := int16(0) symbolTT := s.ct.symbolTT[:s.symbolLen] tableLog := s.actualTableLog tl := (uint32(tableLog) << 16) - (1 << tableLog) for i, v := range s.norm[:s.symbolLen] { switch v { case 0: case -1, 1: symbolTT[i].deltaNbBits = tl symbolTT[i].deltaFindState = int32(total - 1) total++ default: maxBitsOut := uint32(tableLog) - highBits(uint32(v-1)) minStatePlus := uint32(v) << maxBitsOut symbolTT[i].deltaNbBits = (maxBitsOut << 16) - minStatePlus symbolTT[i].deltaFindState = int32(total - v) total += v } } if total != int16(tableSize) { return fmt.Errorf("total mismatch %d (got) != %d (want)", total, tableSize) } } return nil } // countSimple will create a simple histogram in s.count. // Returns the biggest count. // Does not update s.clearCount. func (s *Scratch) countSimple(in []byte) (max int) { for _, v := range in { s.count[v]++ } m, symlen := uint32(0), s.symbolLen for i, v := range s.count[:] { if v == 0 { continue } if v > m { m = v } symlen = uint16(i) + 1 } s.symbolLen = symlen return int(m) } // minTableLog provides the minimum logSize to safely represent a distribution. func (s *Scratch) minTableLog() uint8 { minBitsSrc := highBits(uint32(s.br.remain()-1)) + 1 minBitsSymbols := highBits(uint32(s.symbolLen-1)) + 2 if minBitsSrc < minBitsSymbols { return uint8(minBitsSrc) } return uint8(minBitsSymbols) } // optimalTableLog calculates and sets the optimal tableLog in s.actualTableLog func (s *Scratch) optimalTableLog() { tableLog := s.TableLog minBits := s.minTableLog() maxBitsSrc := uint8(highBits(uint32(s.br.remain()-1))) - 2 if maxBitsSrc < tableLog { // Accuracy can be reduced tableLog = maxBitsSrc } if minBits > tableLog { tableLog = minBits } // Need a minimum to safely represent all symbol values if tableLog < minTablelog { tableLog = minTablelog } if tableLog > maxTableLog { tableLog = maxTableLog } s.actualTableLog = tableLog } var rtbTable = [...]uint32{0, 473195, 504333, 520860, 550000, 700000, 750000, 830000} // normalizeCount will normalize the count of the symbols so // the total is equal to the table size. func (s *Scratch) normalizeCount() error { var ( tableLog = s.actualTableLog scale = 62 - uint64(tableLog) step = (1 << 62) / uint64(s.br.remain()) vStep = uint64(1) << (scale - 20) stillToDistribute = int16(1 << tableLog) largest int largestP int16 lowThreshold = (uint32)(s.br.remain() >> tableLog) ) for i, cnt := range s.count[:s.symbolLen] { // already handled // if (count[s] == s.length) return 0; /* rle special case */ if cnt == 0 { s.norm[i] = 0 continue } if cnt <= lowThreshold { s.norm[i] = -1 stillToDistribute-- } else { proba := (int16)((uint64(cnt) * step) >> scale) if proba < 8 { restToBeat := vStep * uint64(rtbTable[proba]) v := uint64(cnt)*step - (uint64(proba) << scale) if v > restToBeat { proba++ } } if proba > largestP { largestP = proba largest = i } s.norm[i] = proba stillToDistribute -= proba } } if -stillToDistribute >= (s.norm[largest] >> 1) { // corner case, need another normalization method return s.normalizeCount2() } s.norm[largest] += stillToDistribute return nil } // Secondary normalization method. // To be used when primary method fails. func (s *Scratch) normalizeCount2() error { const notYetAssigned = -2 var ( distributed uint32 total = uint32(s.br.remain()) tableLog = s.actualTableLog lowThreshold = total >> tableLog lowOne = (total * 3) >> (tableLog + 1) ) for i, cnt := range s.count[:s.symbolLen] { if cnt == 0 { s.norm[i] = 0 continue } if cnt <= lowThreshold { s.norm[i] = -1 distributed++ total -= cnt continue } if cnt <= lowOne { s.norm[i] = 1 distributed++ total -= cnt continue } s.norm[i] = notYetAssigned } toDistribute := (1 << tableLog) - distributed if (total / toDistribute) > lowOne { // risk of rounding to zero lowOne = (total * 3) / (toDistribute * 2) for i, cnt := range s.count[:s.symbolLen] { if (s.norm[i] == notYetAssigned) && (cnt <= lowOne) { s.norm[i] = 1 distributed++ total -= cnt continue } } toDistribute = (1 << tableLog) - distributed } if distributed == uint32(s.symbolLen)+1 { // all values are pretty poor; // probably incompressible data (should have already been detected); // find max, then give all remaining points to max var maxV int var maxC uint32 for i, cnt := range s.count[:s.symbolLen] { if cnt > maxC { maxV = i maxC = cnt } } s.norm[maxV] += int16(toDistribute) return nil } if total == 0 { // all of the symbols were low enough for the lowOne or lowThreshold for i := uint32(0); toDistribute > 0; i = (i + 1) % (uint32(s.symbolLen)) { if s.norm[i] > 0 { toDistribute-- s.norm[i]++ } } return nil } var ( vStepLog = 62 - uint64(tableLog) mid = uint64((1 << (vStepLog - 1)) - 1) rStep = (((1 << vStepLog) * uint64(toDistribute)) + mid) / uint64(total) // scale on remaining tmpTotal = mid ) for i, cnt := range s.count[:s.symbolLen] { if s.norm[i] == notYetAssigned { var ( end = tmpTotal + uint64(cnt)*rStep sStart = uint32(tmpTotal >> vStepLog) sEnd = uint32(end >> vStepLog) weight = sEnd - sStart ) if weight < 1 { return errors.New("weight < 1") } s.norm[i] = int16(weight) tmpTotal = end } } return nil } // validateNorm validates the normalized histogram table. func (s *Scratch) validateNorm() (err error) { var total int for _, v := range s.norm[:s.symbolLen] { if v >= 0 { total += int(v) } else { total -= int(v) } } defer func() { if err == nil { return } fmt.Printf("selected TableLog: %d, Symbol length: %d\n", s.actualTableLog, s.symbolLen) for i, v := range s.norm[:s.symbolLen] { fmt.Printf("%3d: %5d -> %4d \n", i, s.count[i], v) } }() if total != (1 << s.actualTableLog) { return fmt.Errorf("warning: Total == %d != %d", total, 1< tablelogAbsoluteMax { return errors.New("tableLog too large") } bitStream >>= 4 bitCount := uint(4) s.actualTableLog = uint8(nbBits) remaining := int32((1 << nbBits) + 1) threshold := int32(1 << nbBits) gotTotal := int32(0) nbBits++ for remaining > 1 { if previous0 { n0 := charnum for (bitStream & 0xFFFF) == 0xFFFF { n0 += 24 if b.off < iend-5 { b.advance(2) bitStream = b.Uint32() >> bitCount } else { bitStream >>= 16 bitCount += 16 } } for (bitStream & 3) == 3 { n0 += 3 bitStream >>= 2 bitCount += 2 } n0 += uint16(bitStream & 3) bitCount += 2 if n0 > maxSymbolValue { return errors.New("maxSymbolValue too small") } for charnum < n0 { s.norm[charnum&0xff] = 0 charnum++ } if b.off <= iend-7 || b.off+int(bitCount>>3) <= iend-4 { b.advance(bitCount >> 3) bitCount &= 7 bitStream = b.Uint32() >> bitCount } else { bitStream >>= 2 } } max := (2*(threshold) - 1) - (remaining) var count int32 if (int32(bitStream) & (threshold - 1)) < max { count = int32(bitStream) & (threshold - 1) bitCount += nbBits - 1 } else { count = int32(bitStream) & (2*threshold - 1) if count >= threshold { count -= max } bitCount += nbBits } count-- // extra accuracy if count < 0 { // -1 means +1 remaining += count gotTotal -= count } else { remaining -= count gotTotal += count } s.norm[charnum&0xff] = int16(count) charnum++ previous0 = count == 0 for remaining < threshold { nbBits-- threshold >>= 1 } if b.off <= iend-7 || b.off+int(bitCount>>3) <= iend-4 { b.advance(bitCount >> 3) bitCount &= 7 } else { bitCount -= (uint)(8 * (len(b.b) - 4 - b.off)) b.off = len(b.b) - 4 } bitStream = b.Uint32() >> (bitCount & 31) } s.symbolLen = charnum if s.symbolLen <= 1 { return fmt.Errorf("symbolLen (%d) too small", s.symbolLen) } if s.symbolLen > maxSymbolValue+1 { return fmt.Errorf("symbolLen (%d) too big", s.symbolLen) } if remaining != 1 { return fmt.Errorf("corruption detected (remaining %d != 1)", remaining) } if bitCount > 32 { return fmt.Errorf("corruption detected (bitCount %d > 32)", bitCount) } if gotTotal != 1<> 3) return nil } // decSymbol contains information about a state entry, // Including the state offset base, the output symbol and // the number of bits to read for the low part of the destination state. type decSymbol struct { newState uint16 symbol uint8 nbBits uint8 } // allocDtable will allocate decoding tables if they are not big enough. func (s *Scratch) allocDtable() { tableSize := 1 << s.actualTableLog if cap(s.decTable) < tableSize { s.decTable = make([]decSymbol, tableSize) } s.decTable = s.decTable[:tableSize] if cap(s.ct.tableSymbol) < 256 { s.ct.tableSymbol = make([]byte, 256) } s.ct.tableSymbol = s.ct.tableSymbol[:256] if cap(s.ct.stateTable) < 256 { s.ct.stateTable = make([]uint16, 256) } s.ct.stateTable = s.ct.stateTable[:256] } // buildDtable will build the decoding table. func (s *Scratch) buildDtable() error { tableSize := uint32(1 << s.actualTableLog) highThreshold := tableSize - 1 s.allocDtable() symbolNext := s.ct.stateTable[:256] // Init, lay down lowprob symbols s.zeroBits = false { largeLimit := int16(1 << (s.actualTableLog - 1)) for i, v := range s.norm[:s.symbolLen] { if v == -1 { s.decTable[highThreshold].symbol = uint8(i) highThreshold-- symbolNext[i] = 1 } else { if v >= largeLimit { s.zeroBits = true } symbolNext[i] = uint16(v) } } } // Spread symbols { tableMask := tableSize - 1 step := tableStep(tableSize) position := uint32(0) for ss, v := range s.norm[:s.symbolLen] { for i := 0; i < int(v); i++ { s.decTable[position].symbol = uint8(ss) position = (position + step) & tableMask for position > highThreshold { // lowprob area position = (position + step) & tableMask } } } if position != 0 { // position must reach all cells once, otherwise normalizedCounter is incorrect return errors.New("corrupted input (position != 0)") } } // Build Decoding table { tableSize := uint16(1 << s.actualTableLog) for u, v := range s.decTable { symbol := v.symbol nextState := symbolNext[symbol] symbolNext[symbol] = nextState + 1 nBits := s.actualTableLog - byte(highBits(uint32(nextState))) s.decTable[u].nbBits = nBits newState := (nextState << nBits) - tableSize if newState >= tableSize { return fmt.Errorf("newState (%d) outside table size (%d)", newState, tableSize) } if newState == uint16(u) && nBits == 0 { // Seems weird that this is possible with nbits > 0. return fmt.Errorf("newState (%d) == oldState (%d) and no bits", newState, u) } s.decTable[u].newState = newState } } return nil } // decompress will decompress the bitstream. // If the buffer is over-read an error is returned. func (s *Scratch) decompress() error { br := &s.bits if err := br.init(s.br.unread()); err != nil { return err } var s1, s2 decoder // Initialize and decode first state and symbol. s1.init(br, s.decTable, s.actualTableLog) s2.init(br, s.decTable, s.actualTableLog) // Use temp table to avoid bound checks/append penalty. var tmp = s.ct.tableSymbol[:256] var off uint8 // Main part if !s.zeroBits { for br.off >= 8 { br.fillFast() tmp[off+0] = s1.nextFast() tmp[off+1] = s2.nextFast() br.fillFast() tmp[off+2] = s1.nextFast() tmp[off+3] = s2.nextFast() off += 4 // When off is 0, we have overflowed and should write. if off == 0 { s.Out = append(s.Out, tmp...) if len(s.Out) >= s.DecompressLimit { return fmt.Errorf("output size (%d) > DecompressLimit (%d)", len(s.Out), s.DecompressLimit) } } } } else { for br.off >= 8 { br.fillFast() tmp[off+0] = s1.next() tmp[off+1] = s2.next() br.fillFast() tmp[off+2] = s1.next() tmp[off+3] = s2.next() off += 4 if off == 0 { s.Out = append(s.Out, tmp...) // When off is 0, we have overflowed and should write. if len(s.Out) >= s.DecompressLimit { return fmt.Errorf("output size (%d) > DecompressLimit (%d)", len(s.Out), s.DecompressLimit) } } } } s.Out = append(s.Out, tmp[:off]...) // Final bits, a bit more expensive check for { if s1.finished() { s.Out = append(s.Out, s1.final(), s2.final()) break } br.fill() s.Out = append(s.Out, s1.next()) if s2.finished() { s.Out = append(s.Out, s2.final(), s1.final()) break } s.Out = append(s.Out, s2.next()) if len(s.Out) >= s.DecompressLimit { return fmt.Errorf("output size (%d) > DecompressLimit (%d)", len(s.Out), s.DecompressLimit) } } return br.close() } // decoder keeps track of the current state and updates it from the bitstream. type decoder struct { state uint16 br *bitReader dt []decSymbol } // init will initialize the decoder and read the first state from the stream. func (d *decoder) init(in *bitReader, dt []decSymbol, tableLog uint8) { d.dt = dt d.br = in d.state = in.getBits(tableLog) } // next returns the next symbol and sets the next state. // At least tablelog bits must be available in the bit reader. func (d *decoder) next() uint8 { n := &d.dt[d.state] lowBits := d.br.getBits(n.nbBits) d.state = n.newState + lowBits return n.symbol } // finished returns true if all bits have been read from the bitstream // and the next state would require reading bits from the input. func (d *decoder) finished() bool { return d.br.finished() && d.dt[d.state].nbBits > 0 } // final returns the current state symbol without decoding the next. func (d *decoder) final() uint8 { return d.dt[d.state].symbol } // nextFast returns the next symbol and sets the next state. // This can only be used if no symbols are 0 bits. // At least tablelog bits must be available in the bit reader. func (d *decoder) nextFast() uint8 { n := d.dt[d.state] lowBits := d.br.getBitsFast(n.nbBits) d.state = n.newState + lowBits return n.symbol } ================================================ FILE: vendor/github.com/klauspost/compress/fse/fse.go ================================================ // Copyright 2018 Klaus Post. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Based on work Copyright (c) 2013, Yann Collet, released under BSD License. // Package fse provides Finite State Entropy encoding and decoding. // // Finite State Entropy encoding provides a fast near-optimal symbol encoding/decoding // for byte blocks as implemented in zstd. // // See https://github.com/klauspost/compress/tree/master/fse for more information. package fse import ( "errors" "fmt" "math/bits" ) const ( /*!MEMORY_USAGE : * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) * Increasing memory usage improves compression ratio * Reduced memory usage can improve speed, due to cache effect * Recommended max value is 14, for 16KB, which nicely fits into Intel x86 L1 cache */ maxMemoryUsage = 14 defaultMemoryUsage = 13 maxTableLog = maxMemoryUsage - 2 maxTablesize = 1 << maxTableLog defaultTablelog = defaultMemoryUsage - 2 minTablelog = 5 maxSymbolValue = 255 ) var ( // ErrIncompressible is returned when input is judged to be too hard to compress. ErrIncompressible = errors.New("input is not compressible") // ErrUseRLE is returned from the compressor when the input is a single byte value repeated. ErrUseRLE = errors.New("input is single value repeated") ) // Scratch provides temporary storage for compression and decompression. type Scratch struct { // Private count [maxSymbolValue + 1]uint32 norm [maxSymbolValue + 1]int16 br byteReader bits bitReader bw bitWriter ct cTable // Compression tables. decTable []decSymbol // Decompression table. maxCount int // count of the most probable symbol // Per block parameters. // These can be used to override compression parameters of the block. // Do not touch, unless you know what you are doing. // Out is output buffer. // If the scratch is re-used before the caller is done processing the output, // set this field to nil. // Otherwise the output buffer will be re-used for next Compression/Decompression step // and allocation will be avoided. Out []byte // DecompressLimit limits the maximum decoded size acceptable. // If > 0 decompression will stop when approximately this many bytes // has been decoded. // If 0, maximum size will be 2GB. DecompressLimit int symbolLen uint16 // Length of active part of the symbol table. actualTableLog uint8 // Selected tablelog. zeroBits bool // no bits has prob > 50%. clearCount bool // clear count // MaxSymbolValue will override the maximum symbol value of the next block. MaxSymbolValue uint8 // TableLog will attempt to override the tablelog for the next block. TableLog uint8 } // Histogram allows to populate the histogram and skip that step in the compression, // It otherwise allows to inspect the histogram when compression is done. // To indicate that you have populated the histogram call HistogramFinished // with the value of the highest populated symbol, as well as the number of entries // in the most populated entry. These are accepted at face value. // The returned slice will always be length 256. func (s *Scratch) Histogram() []uint32 { return s.count[:] } // HistogramFinished can be called to indicate that the histogram has been populated. // maxSymbol is the index of the highest set symbol of the next data segment. // maxCount is the number of entries in the most populated entry. // These are accepted at face value. func (s *Scratch) HistogramFinished(maxSymbol uint8, maxCount int) { s.maxCount = maxCount s.symbolLen = uint16(maxSymbol) + 1 s.clearCount = maxCount != 0 } // prepare will prepare and allocate scratch tables used for both compression and decompression. func (s *Scratch) prepare(in []byte) (*Scratch, error) { if s == nil { s = &Scratch{} } if s.MaxSymbolValue == 0 { s.MaxSymbolValue = 255 } if s.TableLog == 0 { s.TableLog = defaultTablelog } if s.TableLog > maxTableLog { return nil, fmt.Errorf("tableLog (%d) > maxTableLog (%d)", s.TableLog, maxTableLog) } if cap(s.Out) == 0 { s.Out = make([]byte, 0, len(in)) } if s.clearCount && s.maxCount == 0 { for i := range s.count { s.count[i] = 0 } s.clearCount = false } s.br.init(in) if s.DecompressLimit == 0 { // Max size 2GB. s.DecompressLimit = (2 << 30) - 1 } return s, nil } // tableStep returns the next table index. func tableStep(tableSize uint32) uint32 { return (tableSize >> 1) + (tableSize >> 3) + 3 } func highBits(val uint32) (n uint32) { return uint32(bits.Len32(val) - 1) } ================================================ FILE: vendor/github.com/klauspost/compress/gen.sh ================================================ #!/bin/sh cd s2/cmd/_s2sx/ || exit 1 go generate . ================================================ FILE: vendor/github.com/klauspost/compress/huff0/.gitignore ================================================ /huff0-fuzz.zip ================================================ FILE: vendor/github.com/klauspost/compress/huff0/README.md ================================================ # Huff0 entropy compression This package provides Huff0 encoding and decoding as used in zstd. [Huff0](https://github.com/Cyan4973/FiniteStateEntropy#new-generation-entropy-coders), a Huffman codec designed for modern CPU, featuring OoO (Out of Order) operations on multiple ALU (Arithmetic Logic Unit), achieving extremely fast compression and decompression speeds. This can be used for compressing input with a lot of similar input values to the smallest number of bytes. This does not perform any multi-byte [dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder) as LZ coders, but it can be used as a secondary step to compressors (like Snappy) that does not do entropy encoding. * [Godoc documentation](https://godoc.org/github.com/klauspost/compress/huff0) ## News This is used as part of the [zstandard](https://github.com/klauspost/compress/tree/master/zstd#zstd) compression and decompression package. This ensures that most functionality is well tested. # Usage This package provides a low level interface that allows to compress single independent blocks. Each block is separate, and there is no built in integrity checks. This means that the caller should keep track of block sizes and also do checksums if needed. Compressing a block is done via the [`Compress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress1X) and [`Compress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Compress4X) functions. You must provide input and will receive the output and maybe an error. These error values can be returned: | Error | Description | |---------------------|-----------------------------------------------------------------------------| | `` | Everything ok, output is returned | | `ErrIncompressible` | Returned when input is judged to be too hard to compress | | `ErrUseRLE` | Returned from the compressor when the input is a single byte value repeated | | `ErrTooBig` | Returned if the input block exceeds the maximum allowed size (128 Kib) | | `(error)` | An internal error occurred. | As can be seen above some of there are errors that will be returned even under normal operation so it is important to handle these. To reduce allocations you can provide a [`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object that can be re-used for successive calls. Both compression and decompression accepts a `Scratch` object, and the same object can be used for both. Be aware, that when re-using a `Scratch` object that the *output* buffer is also re-used, so if you are still using this you must set the `Out` field in the scratch to nil. The same buffer is used for compression and decompression output. The `Scratch` object will retain state that allows to re-use previous tables for encoding and decoding. ## Tables and re-use Huff0 allows for reusing tables from the previous block to save space if that is expected to give better/faster results. The Scratch object allows you to set a [`ReusePolicy`](https://godoc.org/github.com/klauspost/compress/huff0#ReusePolicy) that controls this behaviour. See the documentation for details. This can be altered between each block. Do however note that this information is *not* stored in the output block and it is up to the users of the package to record whether [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable) should be called, based on the boolean reported back from the CompressXX call. If you want to store the table separate from the data, you can access them as `OutData` and `OutTable` on the [`Scratch`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch) object. ## Decompressing The first part of decoding is to initialize the decoding table through [`ReadTable`](https://godoc.org/github.com/klauspost/compress/huff0#ReadTable). This will initialize the decoding tables. You can supply the complete block to `ReadTable` and it will return the data part of the block which can be given to the decompressor. Decompressing is done by calling the [`Decompress1X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress1X) or [`Decompress4X`](https://godoc.org/github.com/klauspost/compress/huff0#Scratch.Decompress4X) function. For concurrently decompressing content with a fixed table a stateless [`Decoder`](https://godoc.org/github.com/klauspost/compress/huff0#Decoder) can be requested which will remain correct as long as the scratch is unchanged. The capacity of the provided slice indicates the expected output size. You must provide the output from the compression stage, at exactly the size you got back. If you receive an error back your input was likely corrupted. It is important to note that a successful decoding does *not* mean your output matches your original input. There are no integrity checks, so relying on errors from the decompressor does not assure your data is valid. # Contributing Contributions are always welcome. Be aware that adding public functions will require good justification and breaking changes will likely not be accepted. If in doubt open an issue before writing the PR. ================================================ FILE: vendor/github.com/klauspost/compress/huff0/bitreader.go ================================================ // Copyright 2018 Klaus Post. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Based on work Copyright (c) 2013, Yann Collet, released under BSD License. package huff0 import ( "encoding/binary" "errors" "fmt" "io" ) // bitReader reads a bitstream in reverse. // The last set bit indicates the start of the stream and is used // for aligning the input. type bitReaderBytes struct { in []byte off uint // next byte to read is at in[off - 1] value uint64 bitsRead uint8 } // init initializes and resets the bit reader. func (b *bitReaderBytes) init(in []byte) error { if len(in) < 1 { return errors.New("corrupt stream: too short") } b.in = in b.off = uint(len(in)) // The highest bit of the last byte indicates where to start v := in[len(in)-1] if v == 0 { return errors.New("corrupt stream, did not find end of stream") } b.bitsRead = 64 b.value = 0 if len(in) >= 8 { b.fillFastStart() } else { b.fill() b.fill() } b.advance(8 - uint8(highBit32(uint32(v)))) return nil } // peekBitsFast requires that at least one bit is requested every time. // There are no checks if the buffer is filled. func (b *bitReaderBytes) peekByteFast() uint8 { got := uint8(b.value >> 56) return got } func (b *bitReaderBytes) advance(n uint8) { b.bitsRead += n b.value <<= n & 63 } // fillFast() will make sure at least 32 bits are available. // There must be at least 4 bytes available. func (b *bitReaderBytes) fillFast() { if b.bitsRead < 32 { return } // 2 bounds checks. v := b.in[b.off-4 : b.off] low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24) b.value |= uint64(low) << (b.bitsRead - 32) b.bitsRead -= 32 b.off -= 4 } // fillFastStart() assumes the bitReaderBytes is empty and there is at least 8 bytes to read. func (b *bitReaderBytes) fillFastStart() { // Do single re-slice to avoid bounds checks. b.value = binary.LittleEndian.Uint64(b.in[b.off-8:]) b.bitsRead = 0 b.off -= 8 } // fill() will make sure at least 32 bits are available. func (b *bitReaderBytes) fill() { if b.bitsRead < 32 { return } if b.off > 4 { v := b.in[b.off-4 : b.off] low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24) b.value |= uint64(low) << (b.bitsRead - 32) b.bitsRead -= 32 b.off -= 4 return } for b.off > 0 { b.value |= uint64(b.in[b.off-1]) << (b.bitsRead - 8) b.bitsRead -= 8 b.off-- } } // finished returns true if all bits have been read from the bit stream. func (b *bitReaderBytes) finished() bool { return b.off == 0 && b.bitsRead >= 64 } func (b *bitReaderBytes) remaining() uint { return b.off*8 + uint(64-b.bitsRead) } // close the bitstream and returns an error if out-of-buffer reads occurred. func (b *bitReaderBytes) close() error { // Release reference. b.in = nil if b.remaining() > 0 { return fmt.Errorf("corrupt input: %d bits remain on stream", b.remaining()) } if b.bitsRead > 64 { return io.ErrUnexpectedEOF } return nil } // bitReaderShifted reads a bitstream in reverse. // The last set bit indicates the start of the stream and is used // for aligning the input. type bitReaderShifted struct { in []byte off uint // next byte to read is at in[off - 1] value uint64 bitsRead uint8 } // init initializes and resets the bit reader. func (b *bitReaderShifted) init(in []byte) error { if len(in) < 1 { return errors.New("corrupt stream: too short") } b.in = in b.off = uint(len(in)) // The highest bit of the last byte indicates where to start v := in[len(in)-1] if v == 0 { return errors.New("corrupt stream, did not find end of stream") } b.bitsRead = 64 b.value = 0 if len(in) >= 8 { b.fillFastStart() } else { b.fill() b.fill() } b.advance(8 - uint8(highBit32(uint32(v)))) return nil } // peekBitsFast requires that at least one bit is requested every time. // There are no checks if the buffer is filled. func (b *bitReaderShifted) peekBitsFast(n uint8) uint16 { return uint16(b.value >> ((64 - n) & 63)) } func (b *bitReaderShifted) advance(n uint8) { b.bitsRead += n b.value <<= n & 63 } // fillFast() will make sure at least 32 bits are available. // There must be at least 4 bytes available. func (b *bitReaderShifted) fillFast() { if b.bitsRead < 32 { return } // 2 bounds checks. v := b.in[b.off-4 : b.off] low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24) b.value |= uint64(low) << ((b.bitsRead - 32) & 63) b.bitsRead -= 32 b.off -= 4 } // fillFastStart() assumes the bitReaderShifted is empty and there is at least 8 bytes to read. func (b *bitReaderShifted) fillFastStart() { // Do single re-slice to avoid bounds checks. b.value = binary.LittleEndian.Uint64(b.in[b.off-8:]) b.bitsRead = 0 b.off -= 8 } // fill() will make sure at least 32 bits are available. func (b *bitReaderShifted) fill() { if b.bitsRead < 32 { return } if b.off > 4 { v := b.in[b.off-4 : b.off] low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24) b.value |= uint64(low) << ((b.bitsRead - 32) & 63) b.bitsRead -= 32 b.off -= 4 return } for b.off > 0 { b.value |= uint64(b.in[b.off-1]) << ((b.bitsRead - 8) & 63) b.bitsRead -= 8 b.off-- } } func (b *bitReaderShifted) remaining() uint { return b.off*8 + uint(64-b.bitsRead) } // close the bitstream and returns an error if out-of-buffer reads occurred. func (b *bitReaderShifted) close() error { // Release reference. b.in = nil if b.remaining() > 0 { return fmt.Errorf("corrupt input: %d bits remain on stream", b.remaining()) } if b.bitsRead > 64 { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: vendor/github.com/klauspost/compress/huff0/bitwriter.go ================================================ // Copyright 2018 Klaus Post. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Based on work Copyright (c) 2013, Yann Collet, released under BSD License. package huff0 // bitWriter will write bits. // First bit will be LSB of the first byte of output. type bitWriter struct { bitContainer uint64 nBits uint8 out []byte } // addBits16Clean will add up to 16 bits. value may not contain more set bits than indicated. // It will not check if there is space for them, so the caller must ensure that it has flushed recently. func (b *bitWriter) addBits16Clean(value uint16, bits uint8) { b.bitContainer |= uint64(value) << (b.nBits & 63) b.nBits += bits } // encSymbol will add up to 16 bits. value may not contain more set bits than indicated. // It will not check if there is space for them, so the caller must ensure that it has flushed recently. func (b *bitWriter) encSymbol(ct cTable, symbol byte) { enc := ct[symbol] b.bitContainer |= uint64(enc.val) << (b.nBits & 63) if false { if enc.nBits == 0 { panic("nbits 0") } } b.nBits += enc.nBits } // encTwoSymbols will add up to 32 bits. value may not contain more set bits than indicated. // It will not check if there is space for them, so the caller must ensure that it has flushed recently. func (b *bitWriter) encTwoSymbols(ct cTable, av, bv byte) { encA := ct[av] encB := ct[bv] sh := b.nBits & 63 combined := uint64(encA.val) | (uint64(encB.val) << (encA.nBits & 63)) b.bitContainer |= combined << sh if false { if encA.nBits == 0 { panic("nbitsA 0") } if encB.nBits == 0 { panic("nbitsB 0") } } b.nBits += encA.nBits + encB.nBits } // encFourSymbols adds up to 32 bits from four symbols. // It will not check if there is space for them, // so the caller must ensure that b has been flushed recently. func (b *bitWriter) encFourSymbols(encA, encB, encC, encD cTableEntry) { bitsA := encA.nBits bitsB := bitsA + encB.nBits bitsC := bitsB + encC.nBits bitsD := bitsC + encD.nBits combined := uint64(encA.val) | (uint64(encB.val) << (bitsA & 63)) | (uint64(encC.val) << (bitsB & 63)) | (uint64(encD.val) << (bitsC & 63)) b.bitContainer |= combined << (b.nBits & 63) b.nBits += bitsD } // flush32 will flush out, so there are at least 32 bits available for writing. func (b *bitWriter) flush32() { if b.nBits < 32 { return } b.out = append(b.out, byte(b.bitContainer), byte(b.bitContainer>>8), byte(b.bitContainer>>16), byte(b.bitContainer>>24)) b.nBits -= 32 b.bitContainer >>= 32 } // flushAlign will flush remaining full bytes and align to next byte boundary. func (b *bitWriter) flushAlign() { nbBytes := (b.nBits + 7) >> 3 for i := uint8(0); i < nbBytes; i++ { b.out = append(b.out, byte(b.bitContainer>>(i*8))) } b.nBits = 0 b.bitContainer = 0 } // close will write the alignment bit and write the final byte(s) // to the output. func (b *bitWriter) close() { // End mark b.addBits16Clean(1, 1) // flush until next byte. b.flushAlign() } ================================================ FILE: vendor/github.com/klauspost/compress/huff0/bytereader.go ================================================ // Copyright 2018 Klaus Post. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Based on work Copyright (c) 2013, Yann Collet, released under BSD License. package huff0 // byteReader provides a byte reader that reads // little endian values from a byte stream. // The input stream is manually advanced. // The reader performs no bounds checks. type byteReader struct { b []byte off int } // init will initialize the reader and set the input. func (b *byteReader) init(in []byte) { b.b = in b.off = 0 } // Int32 returns a little endian int32 starting at current offset. func (b byteReader) Int32() int32 { v3 := int32(b.b[b.off+3]) v2 := int32(b.b[b.off+2]) v1 := int32(b.b[b.off+1]) v0 := int32(b.b[b.off]) return (v3 << 24) | (v2 << 16) | (v1 << 8) | v0 } // Uint32 returns a little endian uint32 starting at current offset. func (b byteReader) Uint32() uint32 { v3 := uint32(b.b[b.off+3]) v2 := uint32(b.b[b.off+2]) v1 := uint32(b.b[b.off+1]) v0 := uint32(b.b[b.off]) return (v3 << 24) | (v2 << 16) | (v1 << 8) | v0 } // remain will return the number of bytes remaining. func (b byteReader) remain() int { return len(b.b) - b.off } ================================================ FILE: vendor/github.com/klauspost/compress/huff0/compress.go ================================================ package huff0 import ( "fmt" "math" "runtime" "sync" ) // Compress1X will compress the input. // The output can be decoded using Decompress1X. // Supply a Scratch object. The scratch object contains state about re-use, // So when sharing across independent encodes, be sure to set the re-use policy. func Compress1X(in []byte, s *Scratch) (out []byte, reUsed bool, err error) { s, err = s.prepare(in) if err != nil { return nil, false, err } return compress(in, s, s.compress1X) } // Compress4X will compress the input. The input is split into 4 independent blocks // and compressed similar to Compress1X. // The output can be decoded using Decompress4X. // Supply a Scratch object. The scratch object contains state about re-use, // So when sharing across independent encodes, be sure to set the re-use policy. func Compress4X(in []byte, s *Scratch) (out []byte, reUsed bool, err error) { s, err = s.prepare(in) if err != nil { return nil, false, err } if false { // TODO: compress4Xp only slightly faster. const parallelThreshold = 8 << 10 if len(in) < parallelThreshold || runtime.GOMAXPROCS(0) == 1 { return compress(in, s, s.compress4X) } return compress(in, s, s.compress4Xp) } return compress(in, s, s.compress4X) } func compress(in []byte, s *Scratch, compressor func(src []byte) ([]byte, error)) (out []byte, reUsed bool, err error) { // Nuke previous table if we cannot reuse anyway. if s.Reuse == ReusePolicyNone { s.prevTable = s.prevTable[:0] } // Create histogram, if none was provided. maxCount := s.maxCount var canReuse = false if maxCount == 0 { maxCount, canReuse = s.countSimple(in) } else { canReuse = s.canUseTable(s.prevTable) } // We want the output size to be less than this: wantSize := len(in) if s.WantLogLess > 0 { wantSize -= wantSize >> s.WantLogLess } // Reset for next run. s.clearCount = true s.maxCount = 0 if maxCount >= len(in) { if maxCount > len(in) { return nil, false, fmt.Errorf("maxCount (%d) > length (%d)", maxCount, len(in)) } if len(in) == 1 { return nil, false, ErrIncompressible } // One symbol, use RLE return nil, false, ErrUseRLE } if maxCount == 1 || maxCount < (len(in)>>7) { // Each symbol present maximum once or too well distributed. return nil, false, ErrIncompressible } if s.Reuse == ReusePolicyMust && !canReuse { // We must reuse, but we can't. return nil, false, ErrIncompressible } if (s.Reuse == ReusePolicyPrefer || s.Reuse == ReusePolicyMust) && canReuse { keepTable := s.cTable keepTL := s.actualTableLog s.cTable = s.prevTable s.actualTableLog = s.prevTableLog s.Out, err = compressor(in) s.cTable = keepTable s.actualTableLog = keepTL if err == nil && len(s.Out) < wantSize { s.OutData = s.Out return s.Out, true, nil } if s.Reuse == ReusePolicyMust { return nil, false, ErrIncompressible } // Do not attempt to re-use later. s.prevTable = s.prevTable[:0] } // Calculate new table. err = s.buildCTable() if err != nil { return nil, false, err } if false && !s.canUseTable(s.cTable) { panic("invalid table generated") } if s.Reuse == ReusePolicyAllow && canReuse { hSize := len(s.Out) oldSize := s.prevTable.estimateSize(s.count[:s.symbolLen]) newSize := s.cTable.estimateSize(s.count[:s.symbolLen]) if oldSize <= hSize+newSize || hSize+12 >= wantSize { // Retain cTable even if we re-use. keepTable := s.cTable keepTL := s.actualTableLog s.cTable = s.prevTable s.actualTableLog = s.prevTableLog s.Out, err = compressor(in) // Restore ctable. s.cTable = keepTable s.actualTableLog = keepTL if err != nil { return nil, false, err } if len(s.Out) >= wantSize { return nil, false, ErrIncompressible } s.OutData = s.Out return s.Out, true, nil } } // Use new table err = s.cTable.write(s) if err != nil { s.OutTable = nil return nil, false, err } s.OutTable = s.Out // Compress using new table s.Out, err = compressor(in) if err != nil { s.OutTable = nil return nil, false, err } if len(s.Out) >= wantSize { s.OutTable = nil return nil, false, ErrIncompressible } // Move current table into previous. s.prevTable, s.prevTableLog, s.cTable = s.cTable, s.actualTableLog, s.prevTable[:0] s.OutData = s.Out[len(s.OutTable):] return s.Out, false, nil } // EstimateSizes will estimate the data sizes func EstimateSizes(in []byte, s *Scratch) (tableSz, dataSz, reuseSz int, err error) { s, err = s.prepare(in) if err != nil { return 0, 0, 0, err } // Create histogram, if none was provided. tableSz, dataSz, reuseSz = -1, -1, -1 maxCount := s.maxCount var canReuse = false if maxCount == 0 { maxCount, canReuse = s.countSimple(in) } else { canReuse = s.canUseTable(s.prevTable) } // We want the output size to be less than this: wantSize := len(in) if s.WantLogLess > 0 { wantSize -= wantSize >> s.WantLogLess } // Reset for next run. s.clearCount = true s.maxCount = 0 if maxCount >= len(in) { if maxCount > len(in) { return 0, 0, 0, fmt.Errorf("maxCount (%d) > length (%d)", maxCount, len(in)) } if len(in) == 1 { return 0, 0, 0, ErrIncompressible } // One symbol, use RLE return 0, 0, 0, ErrUseRLE } if maxCount == 1 || maxCount < (len(in)>>7) { // Each symbol present maximum once or too well distributed. return 0, 0, 0, ErrIncompressible } // Calculate new table. err = s.buildCTable() if err != nil { return 0, 0, 0, err } if false && !s.canUseTable(s.cTable) { panic("invalid table generated") } tableSz, err = s.cTable.estTableSize(s) if err != nil { return 0, 0, 0, err } if canReuse { reuseSz = s.prevTable.estimateSize(s.count[:s.symbolLen]) } dataSz = s.cTable.estimateSize(s.count[:s.symbolLen]) // Restore return tableSz, dataSz, reuseSz, nil } func (s *Scratch) compress1X(src []byte) ([]byte, error) { return s.compress1xDo(s.Out, src), nil } func (s *Scratch) compress1xDo(dst, src []byte) []byte { var bw = bitWriter{out: dst} // N is length divisible by 4. n := len(src) n -= n & 3 cTable := s.cTable[:256] // Encode last bytes. for i := len(src) & 3; i > 0; i-- { bw.encSymbol(cTable, src[n+i-1]) } n -= 4 if s.actualTableLog <= 8 { for ; n >= 0; n -= 4 { tmp := src[n : n+4] // tmp should be len 4 bw.flush32() bw.encFourSymbols(cTable[tmp[3]], cTable[tmp[2]], cTable[tmp[1]], cTable[tmp[0]]) } } else { for ; n >= 0; n -= 4 { tmp := src[n : n+4] // tmp should be len 4 bw.flush32() bw.encTwoSymbols(cTable, tmp[3], tmp[2]) bw.flush32() bw.encTwoSymbols(cTable, tmp[1], tmp[0]) } } bw.close() return bw.out } var sixZeros [6]byte func (s *Scratch) compress4X(src []byte) ([]byte, error) { if len(src) < 12 { return nil, ErrIncompressible } segmentSize := (len(src) + 3) / 4 // Add placeholder for output length offsetIdx := len(s.Out) s.Out = append(s.Out, sixZeros[:]...) for i := 0; i < 4; i++ { toDo := src if len(toDo) > segmentSize { toDo = toDo[:segmentSize] } src = src[len(toDo):] idx := len(s.Out) s.Out = s.compress1xDo(s.Out, toDo) if len(s.Out)-idx > math.MaxUint16 { // We cannot store the size in the jump table return nil, ErrIncompressible } // Write compressed length as little endian before block. if i < 3 { // Last length is not written. length := len(s.Out) - idx s.Out[i*2+offsetIdx] = byte(length) s.Out[i*2+offsetIdx+1] = byte(length >> 8) } } return s.Out, nil } // compress4Xp will compress 4 streams using separate goroutines. func (s *Scratch) compress4Xp(src []byte) ([]byte, error) { if len(src) < 12 { return nil, ErrIncompressible } // Add placeholder for output length s.Out = s.Out[:6] segmentSize := (len(src) + 3) / 4 var wg sync.WaitGroup wg.Add(4) for i := 0; i < 4; i++ { toDo := src if len(toDo) > segmentSize { toDo = toDo[:segmentSize] } src = src[len(toDo):] // Separate goroutine for each block. go func(i int) { s.tmpOut[i] = s.compress1xDo(s.tmpOut[i][:0], toDo) wg.Done() }(i) } wg.Wait() for i := 0; i < 4; i++ { o := s.tmpOut[i] if len(o) > math.MaxUint16 { // We cannot store the size in the jump table return nil, ErrIncompressible } // Write compressed length as little endian before block. if i < 3 { // Last length is not written. s.Out[i*2] = byte(len(o)) s.Out[i*2+1] = byte(len(o) >> 8) } // Write output. s.Out = append(s.Out, o...) } return s.Out, nil } // countSimple will create a simple histogram in s.count. // Returns the biggest count. // Does not update s.clearCount. func (s *Scratch) countSimple(in []byte) (max int, reuse bool) { reuse = true for _, v := range in { s.count[v]++ } m := uint32(0) if len(s.prevTable) > 0 { for i, v := range s.count[:] { if v == 0 { continue } if v > m { m = v } s.symbolLen = uint16(i) + 1 if i >= len(s.prevTable) { reuse = false } else if s.prevTable[i].nBits == 0 { reuse = false } } return int(m), reuse } for i, v := range s.count[:] { if v == 0 { continue } if v > m { m = v } s.symbolLen = uint16(i) + 1 } return int(m), false } func (s *Scratch) canUseTable(c cTable) bool { if len(c) < int(s.symbolLen) { return false } for i, v := range s.count[:s.symbolLen] { if v != 0 && c[i].nBits == 0 { return false } } return true } //lint:ignore U1000 used for debugging func (s *Scratch) validateTable(c cTable) bool { if len(c) < int(s.symbolLen) { return false } for i, v := range s.count[:s.symbolLen] { if v != 0 { if c[i].nBits == 0 { return false } if c[i].nBits > s.actualTableLog { return false } } } return true } // minTableLog provides the minimum logSize to safely represent a distribution. func (s *Scratch) minTableLog() uint8 { minBitsSrc := highBit32(uint32(s.br.remain())) + 1 minBitsSymbols := highBit32(uint32(s.symbolLen-1)) + 2 if minBitsSrc < minBitsSymbols { return uint8(minBitsSrc) } return uint8(minBitsSymbols) } // optimalTableLog calculates and sets the optimal tableLog in s.actualTableLog func (s *Scratch) optimalTableLog() { tableLog := s.TableLog minBits := s.minTableLog() maxBitsSrc := uint8(highBit32(uint32(s.br.remain()-1))) - 1 if maxBitsSrc < tableLog { // Accuracy can be reduced tableLog = maxBitsSrc } if minBits > tableLog { tableLog = minBits } // Need a minimum to safely represent all symbol values if tableLog < minTablelog { tableLog = minTablelog } if tableLog > tableLogMax { tableLog = tableLogMax } s.actualTableLog = tableLog } type cTableEntry struct { val uint16 nBits uint8 // We have 8 bits extra } const huffNodesMask = huffNodesLen - 1 func (s *Scratch) buildCTable() error { s.optimalTableLog() s.huffSort() if cap(s.cTable) < maxSymbolValue+1 { s.cTable = make([]cTableEntry, s.symbolLen, maxSymbolValue+1) } else { s.cTable = s.cTable[:s.symbolLen] for i := range s.cTable { s.cTable[i] = cTableEntry{} } } var startNode = int16(s.symbolLen) nonNullRank := s.symbolLen - 1 nodeNb := startNode huffNode := s.nodes[1 : huffNodesLen+1] // This overlays the slice above, but allows "-1" index lookups. // Different from reference implementation. huffNode0 := s.nodes[0 : huffNodesLen+1] for huffNode[nonNullRank].count() == 0 { nonNullRank-- } lowS := int16(nonNullRank) nodeRoot := nodeNb + lowS - 1 lowN := nodeNb huffNode[nodeNb].setCount(huffNode[lowS].count() + huffNode[lowS-1].count()) huffNode[lowS].setParent(nodeNb) huffNode[lowS-1].setParent(nodeNb) nodeNb++ lowS -= 2 for n := nodeNb; n <= nodeRoot; n++ { huffNode[n].setCount(1 << 30) } // fake entry, strong barrier huffNode0[0].setCount(1 << 31) // create parents for nodeNb <= nodeRoot { var n1, n2 int16 if huffNode0[lowS+1].count() < huffNode0[lowN+1].count() { n1 = lowS lowS-- } else { n1 = lowN lowN++ } if huffNode0[lowS+1].count() < huffNode0[lowN+1].count() { n2 = lowS lowS-- } else { n2 = lowN lowN++ } huffNode[nodeNb].setCount(huffNode0[n1+1].count() + huffNode0[n2+1].count()) huffNode0[n1+1].setParent(nodeNb) huffNode0[n2+1].setParent(nodeNb) nodeNb++ } // distribute weights (unlimited tree height) huffNode[nodeRoot].setNbBits(0) for n := nodeRoot - 1; n >= startNode; n-- { huffNode[n].setNbBits(huffNode[huffNode[n].parent()].nbBits() + 1) } for n := uint16(0); n <= nonNullRank; n++ { huffNode[n].setNbBits(huffNode[huffNode[n].parent()].nbBits() + 1) } s.actualTableLog = s.setMaxHeight(int(nonNullRank)) maxNbBits := s.actualTableLog // fill result into tree (val, nbBits) if maxNbBits > tableLogMax { return fmt.Errorf("internal error: maxNbBits (%d) > tableLogMax (%d)", maxNbBits, tableLogMax) } var nbPerRank [tableLogMax + 1]uint16 var valPerRank [16]uint16 for _, v := range huffNode[:nonNullRank+1] { nbPerRank[v.nbBits()]++ } // determine stating value per rank { min := uint16(0) for n := maxNbBits; n > 0; n-- { // get starting value within each rank valPerRank[n] = min min += nbPerRank[n] min >>= 1 } } // push nbBits per symbol, symbol order for _, v := range huffNode[:nonNullRank+1] { s.cTable[v.symbol()].nBits = v.nbBits() } // assign value within rank, symbol order t := s.cTable[:s.symbolLen] for n, val := range t { nbits := val.nBits & 15 v := valPerRank[nbits] t[n].val = v valPerRank[nbits] = v + 1 } return nil } // huffSort will sort symbols, decreasing order. func (s *Scratch) huffSort() { type rankPos struct { base uint32 current uint32 } // Clear nodes nodes := s.nodes[:huffNodesLen+1] s.nodes = nodes nodes = nodes[1 : huffNodesLen+1] // Sort into buckets based on length of symbol count. var rank [32]rankPos for _, v := range s.count[:s.symbolLen] { r := highBit32(v+1) & 31 rank[r].base++ } // maxBitLength is log2(BlockSizeMax) + 1 const maxBitLength = 18 + 1 for n := maxBitLength; n > 0; n-- { rank[n-1].base += rank[n].base } for n := range rank[:maxBitLength] { rank[n].current = rank[n].base } for n, c := range s.count[:s.symbolLen] { r := (highBit32(c+1) + 1) & 31 pos := rank[r].current rank[r].current++ prev := nodes[(pos-1)&huffNodesMask] for pos > rank[r].base && c > prev.count() { nodes[pos&huffNodesMask] = prev pos-- prev = nodes[(pos-1)&huffNodesMask] } nodes[pos&huffNodesMask] = makeNodeElt(c, byte(n)) } } func (s *Scratch) setMaxHeight(lastNonNull int) uint8 { maxNbBits := s.actualTableLog huffNode := s.nodes[1 : huffNodesLen+1] //huffNode = huffNode[: huffNodesLen] largestBits := huffNode[lastNonNull].nbBits() // early exit : no elt > maxNbBits if largestBits <= maxNbBits { return largestBits } totalCost := int(0) baseCost := int(1) << (largestBits - maxNbBits) n := uint32(lastNonNull) for huffNode[n].nbBits() > maxNbBits { totalCost += baseCost - (1 << (largestBits - huffNode[n].nbBits())) huffNode[n].setNbBits(maxNbBits) n-- } // n stops at huffNode[n].nbBits <= maxNbBits for huffNode[n].nbBits() == maxNbBits { n-- } // n end at index of smallest symbol using < maxNbBits // renorm totalCost totalCost >>= largestBits - maxNbBits /* note : totalCost is necessarily a multiple of baseCost */ // repay normalized cost { const noSymbol = 0xF0F0F0F0 var rankLast [tableLogMax + 2]uint32 for i := range rankLast[:] { rankLast[i] = noSymbol } // Get pos of last (smallest) symbol per rank { currentNbBits := maxNbBits for pos := int(n); pos >= 0; pos-- { if huffNode[pos].nbBits() >= currentNbBits { continue } currentNbBits = huffNode[pos].nbBits() // < maxNbBits rankLast[maxNbBits-currentNbBits] = uint32(pos) } } for totalCost > 0 { nBitsToDecrease := uint8(highBit32(uint32(totalCost))) + 1 for ; nBitsToDecrease > 1; nBitsToDecrease-- { highPos := rankLast[nBitsToDecrease] lowPos := rankLast[nBitsToDecrease-1] if highPos == noSymbol { continue } if lowPos == noSymbol { break } highTotal := huffNode[highPos].count() lowTotal := 2 * huffNode[lowPos].count() if highTotal <= lowTotal { break } } // only triggered when no more rank 1 symbol left => find closest one (note : there is necessarily at least one !) // HUF_MAX_TABLELOG test just to please gcc 5+; but it should not be necessary // FIXME: try to remove for (nBitsToDecrease <= tableLogMax) && (rankLast[nBitsToDecrease] == noSymbol) { nBitsToDecrease++ } totalCost -= 1 << (nBitsToDecrease - 1) if rankLast[nBitsToDecrease-1] == noSymbol { // this rank is no longer empty rankLast[nBitsToDecrease-1] = rankLast[nBitsToDecrease] } huffNode[rankLast[nBitsToDecrease]].setNbBits(1 + huffNode[rankLast[nBitsToDecrease]].nbBits()) if rankLast[nBitsToDecrease] == 0 { /* special case, reached largest symbol */ rankLast[nBitsToDecrease] = noSymbol } else { rankLast[nBitsToDecrease]-- if huffNode[rankLast[nBitsToDecrease]].nbBits() != maxNbBits-nBitsToDecrease { rankLast[nBitsToDecrease] = noSymbol /* this rank is now empty */ } } } for totalCost < 0 { /* Sometimes, cost correction overshoot */ if rankLast[1] == noSymbol { /* special case : no rank 1 symbol (using maxNbBits-1); let's create one from largest rank 0 (using maxNbBits) */ for huffNode[n].nbBits() == maxNbBits { n-- } huffNode[n+1].setNbBits(huffNode[n+1].nbBits() - 1) rankLast[1] = n + 1 totalCost++ continue } huffNode[rankLast[1]+1].setNbBits(huffNode[rankLast[1]+1].nbBits() - 1) rankLast[1]++ totalCost++ } } return maxNbBits } // A nodeElt is the fields // // count uint32 // parent uint16 // symbol byte // nbBits uint8 // // in some order, all squashed into an integer so that the compiler // always loads and stores entire nodeElts instead of separate fields. type nodeElt uint64 func makeNodeElt(count uint32, symbol byte) nodeElt { return nodeElt(count) | nodeElt(symbol)<<48 } func (e *nodeElt) count() uint32 { return uint32(*e) } func (e *nodeElt) parent() uint16 { return uint16(*e >> 32) } func (e *nodeElt) symbol() byte { return byte(*e >> 48) } func (e *nodeElt) nbBits() uint8 { return uint8(*e >> 56) } func (e *nodeElt) setCount(c uint32) { *e = (*e)&0xffffffff00000000 | nodeElt(c) } func (e *nodeElt) setParent(p int16) { *e = (*e)&0xffff0000ffffffff | nodeElt(uint16(p))<<32 } func (e *nodeElt) setNbBits(n uint8) { *e = (*e)&0x00ffffffffffffff | nodeElt(n)<<56 } ================================================ FILE: vendor/github.com/klauspost/compress/huff0/decompress.go ================================================ package huff0 import ( "errors" "fmt" "io" "sync" "github.com/klauspost/compress/fse" ) type dTable struct { single []dEntrySingle } // single-symbols decoding type dEntrySingle struct { entry uint16 } // Uses special code for all tables that are < 8 bits. const use8BitTables = true // ReadTable will read a table from the input. // The size of the input may be larger than the table definition. // Any content remaining after the table definition will be returned. // If no Scratch is provided a new one is allocated. // The returned Scratch can be used for encoding or decoding input using this table. func ReadTable(in []byte, s *Scratch) (s2 *Scratch, remain []byte, err error) { s, err = s.prepare(nil) if err != nil { return s, nil, err } if len(in) <= 1 { return s, nil, errors.New("input too small for table") } iSize := in[0] in = in[1:] if iSize >= 128 { // Uncompressed oSize := iSize - 127 iSize = (oSize + 1) / 2 if int(iSize) > len(in) { return s, nil, errors.New("input too small for table") } for n := uint8(0); n < oSize; n += 2 { v := in[n/2] s.huffWeight[n] = v >> 4 s.huffWeight[n+1] = v & 15 } s.symbolLen = uint16(oSize) in = in[iSize:] } else { if len(in) < int(iSize) { return s, nil, fmt.Errorf("input too small for table, want %d bytes, have %d", iSize, len(in)) } // FSE compressed weights s.fse.DecompressLimit = 255 hw := s.huffWeight[:] s.fse.Out = hw b, err := fse.Decompress(in[:iSize], s.fse) s.fse.Out = nil if err != nil { return s, nil, fmt.Errorf("fse decompress returned: %w", err) } if len(b) > 255 { return s, nil, errors.New("corrupt input: output table too large") } s.symbolLen = uint16(len(b)) in = in[iSize:] } // collect weight stats var rankStats [16]uint32 weightTotal := uint32(0) for _, v := range s.huffWeight[:s.symbolLen] { if v > tableLogMax { return s, nil, errors.New("corrupt input: weight too large") } v2 := v & 15 rankStats[v2]++ // (1 << (v2-1)) is slower since the compiler cannot prove that v2 isn't 0. weightTotal += (1 << v2) >> 1 } if weightTotal == 0 { return s, nil, errors.New("corrupt input: weights zero") } // get last non-null symbol weight (implied, total must be 2^n) { tableLog := highBit32(weightTotal) + 1 if tableLog > tableLogMax { return s, nil, errors.New("corrupt input: tableLog too big") } s.actualTableLog = uint8(tableLog) // determine last weight { total := uint32(1) << tableLog rest := total - weightTotal verif := uint32(1) << highBit32(rest) lastWeight := highBit32(rest) + 1 if verif != rest { // last value must be a clean power of 2 return s, nil, errors.New("corrupt input: last value not power of two") } s.huffWeight[s.symbolLen] = uint8(lastWeight) s.symbolLen++ rankStats[lastWeight]++ } } if (rankStats[1] < 2) || (rankStats[1]&1 != 0) { // by construction : at least 2 elts of rank 1, must be even return s, nil, errors.New("corrupt input: min elt size, even check failed ") } // TODO: Choose between single/double symbol decoding // Calculate starting value for each rank { var nextRankStart uint32 for n := uint8(1); n < s.actualTableLog+1; n++ { current := nextRankStart nextRankStart += rankStats[n] << (n - 1) rankStats[n] = current } } // fill DTable (always full size) tSize := 1 << tableLogMax if len(s.dt.single) != tSize { s.dt.single = make([]dEntrySingle, tSize) } cTable := s.prevTable if cap(cTable) < maxSymbolValue+1 { cTable = make([]cTableEntry, 0, maxSymbolValue+1) } cTable = cTable[:maxSymbolValue+1] s.prevTable = cTable[:s.symbolLen] s.prevTableLog = s.actualTableLog for n, w := range s.huffWeight[:s.symbolLen] { if w == 0 { cTable[n] = cTableEntry{ val: 0, nBits: 0, } continue } length := (uint32(1) << w) >> 1 d := dEntrySingle{ entry: uint16(s.actualTableLog+1-w) | (uint16(n) << 8), } rank := &rankStats[w] cTable[n] = cTableEntry{ val: uint16(*rank >> (w - 1)), nBits: uint8(d.entry), } single := s.dt.single[*rank : *rank+length] for i := range single { single[i] = d } *rank += length } return s, in, nil } // Decompress1X will decompress a 1X encoded stream. // The length of the supplied input must match the end of a block exactly. // Before this is called, the table must be initialized with ReadTable unless // the encoder re-used the table. // deprecated: Use the stateless Decoder() to get a concurrent version. func (s *Scratch) Decompress1X(in []byte) (out []byte, err error) { if cap(s.Out) < s.MaxDecodedSize { s.Out = make([]byte, s.MaxDecodedSize) } s.Out = s.Out[:0:s.MaxDecodedSize] s.Out, err = s.Decoder().Decompress1X(s.Out, in) return s.Out, err } // Decompress4X will decompress a 4X encoded stream. // Before this is called, the table must be initialized with ReadTable unless // the encoder re-used the table. // The length of the supplied input must match the end of a block exactly. // The destination size of the uncompressed data must be known and provided. // deprecated: Use the stateless Decoder() to get a concurrent version. func (s *Scratch) Decompress4X(in []byte, dstSize int) (out []byte, err error) { if dstSize > s.MaxDecodedSize { return nil, ErrMaxDecodedSizeExceeded } if cap(s.Out) < dstSize { s.Out = make([]byte, s.MaxDecodedSize) } s.Out = s.Out[:0:dstSize] s.Out, err = s.Decoder().Decompress4X(s.Out, in) return s.Out, err } // Decoder will return a stateless decoder that can be used by multiple // decompressors concurrently. // Before this is called, the table must be initialized with ReadTable. // The Decoder is still linked to the scratch buffer so that cannot be reused. // However, it is safe to discard the scratch. func (s *Scratch) Decoder() *Decoder { return &Decoder{ dt: s.dt, actualTableLog: s.actualTableLog, bufs: &s.decPool, } } // Decoder provides stateless decoding. type Decoder struct { dt dTable actualTableLog uint8 bufs *sync.Pool } func (d *Decoder) buffer() *[4][256]byte { buf, ok := d.bufs.Get().(*[4][256]byte) if ok { return buf } return &[4][256]byte{} } // decompress1X8Bit will decompress a 1X encoded stream with tablelog <= 8. // The cap of the output buffer will be the maximum decompressed size. // The length of the supplied input must match the end of a block exactly. func (d *Decoder) decompress1X8Bit(dst, src []byte) ([]byte, error) { if d.actualTableLog == 8 { return d.decompress1X8BitExactly(dst, src) } var br bitReaderBytes err := br.init(src) if err != nil { return dst, err } maxDecodedSize := cap(dst) dst = dst[:0] // Avoid bounds check by always having full sized table. dt := d.dt.single[:256] // Use temp table to avoid bound checks/append penalty. bufs := d.buffer() buf := &bufs[0] var off uint8 switch d.actualTableLog { case 8: const shift = 0 for br.off >= 4 { br.fillFast() v := dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+0] = uint8(v.entry >> 8) v = dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+1] = uint8(v.entry >> 8) v = dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+2] = uint8(v.entry >> 8) v = dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+3] = uint8(v.entry >> 8) off += 4 if off == 0 { if len(dst)+256 > maxDecodedSize { br.close() d.bufs.Put(bufs) return nil, ErrMaxDecodedSizeExceeded } dst = append(dst, buf[:]...) } } case 7: const shift = 8 - 7 for br.off >= 4 { br.fillFast() v := dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+0] = uint8(v.entry >> 8) v = dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+1] = uint8(v.entry >> 8) v = dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+2] = uint8(v.entry >> 8) v = dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+3] = uint8(v.entry >> 8) off += 4 if off == 0 { if len(dst)+256 > maxDecodedSize { br.close() d.bufs.Put(bufs) return nil, ErrMaxDecodedSizeExceeded } dst = append(dst, buf[:]...) } } case 6: const shift = 8 - 6 for br.off >= 4 { br.fillFast() v := dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+0] = uint8(v.entry >> 8) v = dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+1] = uint8(v.entry >> 8) v = dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+2] = uint8(v.entry >> 8) v = dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+3] = uint8(v.entry >> 8) off += 4 if off == 0 { if len(dst)+256 > maxDecodedSize { d.bufs.Put(bufs) br.close() return nil, ErrMaxDecodedSizeExceeded } dst = append(dst, buf[:]...) } } case 5: const shift = 8 - 5 for br.off >= 4 { br.fillFast() v := dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+0] = uint8(v.entry >> 8) v = dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+1] = uint8(v.entry >> 8) v = dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+2] = uint8(v.entry >> 8) v = dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+3] = uint8(v.entry >> 8) off += 4 if off == 0 { if len(dst)+256 > maxDecodedSize { d.bufs.Put(bufs) br.close() return nil, ErrMaxDecodedSizeExceeded } dst = append(dst, buf[:]...) } } case 4: const shift = 8 - 4 for br.off >= 4 { br.fillFast() v := dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+0] = uint8(v.entry >> 8) v = dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+1] = uint8(v.entry >> 8) v = dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+2] = uint8(v.entry >> 8) v = dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+3] = uint8(v.entry >> 8) off += 4 if off == 0 { if len(dst)+256 > maxDecodedSize { d.bufs.Put(bufs) br.close() return nil, ErrMaxDecodedSizeExceeded } dst = append(dst, buf[:]...) } } case 3: const shift = 8 - 3 for br.off >= 4 { br.fillFast() v := dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+0] = uint8(v.entry >> 8) v = dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+1] = uint8(v.entry >> 8) v = dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+2] = uint8(v.entry >> 8) v = dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+3] = uint8(v.entry >> 8) off += 4 if off == 0 { if len(dst)+256 > maxDecodedSize { d.bufs.Put(bufs) br.close() return nil, ErrMaxDecodedSizeExceeded } dst = append(dst, buf[:]...) } } case 2: const shift = 8 - 2 for br.off >= 4 { br.fillFast() v := dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+0] = uint8(v.entry >> 8) v = dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+1] = uint8(v.entry >> 8) v = dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+2] = uint8(v.entry >> 8) v = dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+3] = uint8(v.entry >> 8) off += 4 if off == 0 { if len(dst)+256 > maxDecodedSize { d.bufs.Put(bufs) br.close() return nil, ErrMaxDecodedSizeExceeded } dst = append(dst, buf[:]...) } } case 1: const shift = 8 - 1 for br.off >= 4 { br.fillFast() v := dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+0] = uint8(v.entry >> 8) v = dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+1] = uint8(v.entry >> 8) v = dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+2] = uint8(v.entry >> 8) v = dt[uint8(br.value>>(56+shift))] br.advance(uint8(v.entry)) buf[off+3] = uint8(v.entry >> 8) off += 4 if off == 0 { if len(dst)+256 > maxDecodedSize { d.bufs.Put(bufs) br.close() return nil, ErrMaxDecodedSizeExceeded } dst = append(dst, buf[:]...) } } default: d.bufs.Put(bufs) return nil, fmt.Errorf("invalid tablelog: %d", d.actualTableLog) } if len(dst)+int(off) > maxDecodedSize { d.bufs.Put(bufs) br.close() return nil, ErrMaxDecodedSizeExceeded } dst = append(dst, buf[:off]...) // br < 4, so uint8 is fine bitsLeft := int8(uint8(br.off)*8 + (64 - br.bitsRead)) shift := (8 - d.actualTableLog) & 7 for bitsLeft > 0 { if br.bitsRead >= 64-8 { for br.off > 0 { br.value |= uint64(br.in[br.off-1]) << (br.bitsRead - 8) br.bitsRead -= 8 br.off-- } } if len(dst) >= maxDecodedSize { br.close() d.bufs.Put(bufs) return nil, ErrMaxDecodedSizeExceeded } v := dt[br.peekByteFast()>>shift] nBits := uint8(v.entry) br.advance(nBits) bitsLeft -= int8(nBits) dst = append(dst, uint8(v.entry>>8)) } d.bufs.Put(bufs) return dst, br.close() } // decompress1X8Bit will decompress a 1X encoded stream with tablelog <= 8. // The cap of the output buffer will be the maximum decompressed size. // The length of the supplied input must match the end of a block exactly. func (d *Decoder) decompress1X8BitExactly(dst, src []byte) ([]byte, error) { var br bitReaderBytes err := br.init(src) if err != nil { return dst, err } maxDecodedSize := cap(dst) dst = dst[:0] // Avoid bounds check by always having full sized table. dt := d.dt.single[:256] // Use temp table to avoid bound checks/append penalty. bufs := d.buffer() buf := &bufs[0] var off uint8 const shift = 56 //fmt.Printf("mask: %b, tl:%d\n", mask, d.actualTableLog) for br.off >= 4 { br.fillFast() v := dt[uint8(br.value>>shift)] br.advance(uint8(v.entry)) buf[off+0] = uint8(v.entry >> 8) v = dt[uint8(br.value>>shift)] br.advance(uint8(v.entry)) buf[off+1] = uint8(v.entry >> 8) v = dt[uint8(br.value>>shift)] br.advance(uint8(v.entry)) buf[off+2] = uint8(v.entry >> 8) v = dt[uint8(br.value>>shift)] br.advance(uint8(v.entry)) buf[off+3] = uint8(v.entry >> 8) off += 4 if off == 0 { if len(dst)+256 > maxDecodedSize { d.bufs.Put(bufs) br.close() return nil, ErrMaxDecodedSizeExceeded } dst = append(dst, buf[:]...) } } if len(dst)+int(off) > maxDecodedSize { d.bufs.Put(bufs) br.close() return nil, ErrMaxDecodedSizeExceeded } dst = append(dst, buf[:off]...) // br < 4, so uint8 is fine bitsLeft := int8(uint8(br.off)*8 + (64 - br.bitsRead)) for bitsLeft > 0 { if br.bitsRead >= 64-8 { for br.off > 0 { br.value |= uint64(br.in[br.off-1]) << (br.bitsRead - 8) br.bitsRead -= 8 br.off-- } } if len(dst) >= maxDecodedSize { d.bufs.Put(bufs) br.close() return nil, ErrMaxDecodedSizeExceeded } v := dt[br.peekByteFast()] nBits := uint8(v.entry) br.advance(nBits) bitsLeft -= int8(nBits) dst = append(dst, uint8(v.entry>>8)) } d.bufs.Put(bufs) return dst, br.close() } // Decompress4X will decompress a 4X encoded stream. // The length of the supplied input must match the end of a block exactly. // The *capacity* of the dst slice must match the destination size of // the uncompressed data exactly. func (d *Decoder) decompress4X8bit(dst, src []byte) ([]byte, error) { if d.actualTableLog == 8 { return d.decompress4X8bitExactly(dst, src) } var br [4]bitReaderBytes start := 6 for i := 0; i < 3; i++ { length := int(src[i*2]) | (int(src[i*2+1]) << 8) if start+length >= len(src) { return nil, errors.New("truncated input (or invalid offset)") } err := br[i].init(src[start : start+length]) if err != nil { return nil, err } start += length } err := br[3].init(src[start:]) if err != nil { return nil, err } // destination, offset to match first output dstSize := cap(dst) dst = dst[:dstSize] out := dst dstEvery := (dstSize + 3) / 4 shift := (56 + (8 - d.actualTableLog)) & 63 const tlSize = 1 << 8 single := d.dt.single[:tlSize] // Use temp table to avoid bound checks/append penalty. buf := d.buffer() var off uint8 var decoded int // Decode 4 values from each decoder/loop. const bufoff = 256 for { if br[0].off < 4 || br[1].off < 4 || br[2].off < 4 || br[3].off < 4 { break } { // Interleave 2 decodes. const stream = 0 const stream2 = 1 br1 := &br[stream] br2 := &br[stream2] br1.fillFast() br2.fillFast() v := single[uint8(br1.value>>shift)].entry v2 := single[uint8(br2.value>>shift)].entry br1.bitsRead += uint8(v) br1.value <<= v & 63 br2.bitsRead += uint8(v2) br2.value <<= v2 & 63 buf[stream][off] = uint8(v >> 8) buf[stream2][off] = uint8(v2 >> 8) v = single[uint8(br1.value>>shift)].entry v2 = single[uint8(br2.value>>shift)].entry br1.bitsRead += uint8(v) br1.value <<= v & 63 br2.bitsRead += uint8(v2) br2.value <<= v2 & 63 buf[stream][off+1] = uint8(v >> 8) buf[stream2][off+1] = uint8(v2 >> 8) v = single[uint8(br1.value>>shift)].entry v2 = single[uint8(br2.value>>shift)].entry br1.bitsRead += uint8(v) br1.value <<= v & 63 br2.bitsRead += uint8(v2) br2.value <<= v2 & 63 buf[stream][off+2] = uint8(v >> 8) buf[stream2][off+2] = uint8(v2 >> 8) v = single[uint8(br1.value>>shift)].entry v2 = single[uint8(br2.value>>shift)].entry br1.bitsRead += uint8(v) br1.value <<= v & 63 br2.bitsRead += uint8(v2) br2.value <<= v2 & 63 buf[stream][off+3] = uint8(v >> 8) buf[stream2][off+3] = uint8(v2 >> 8) } { const stream = 2 const stream2 = 3 br1 := &br[stream] br2 := &br[stream2] br1.fillFast() br2.fillFast() v := single[uint8(br1.value>>shift)].entry v2 := single[uint8(br2.value>>shift)].entry br1.bitsRead += uint8(v) br1.value <<= v & 63 br2.bitsRead += uint8(v2) br2.value <<= v2 & 63 buf[stream][off] = uint8(v >> 8) buf[stream2][off] = uint8(v2 >> 8) v = single[uint8(br1.value>>shift)].entry v2 = single[uint8(br2.value>>shift)].entry br1.bitsRead += uint8(v) br1.value <<= v & 63 br2.bitsRead += uint8(v2) br2.value <<= v2 & 63 buf[stream][off+1] = uint8(v >> 8) buf[stream2][off+1] = uint8(v2 >> 8) v = single[uint8(br1.value>>shift)].entry v2 = single[uint8(br2.value>>shift)].entry br1.bitsRead += uint8(v) br1.value <<= v & 63 br2.bitsRead += uint8(v2) br2.value <<= v2 & 63 buf[stream][off+2] = uint8(v >> 8) buf[stream2][off+2] = uint8(v2 >> 8) v = single[uint8(br1.value>>shift)].entry v2 = single[uint8(br2.value>>shift)].entry br1.bitsRead += uint8(v) br1.value <<= v & 63 br2.bitsRead += uint8(v2) br2.value <<= v2 & 63 buf[stream][off+3] = uint8(v >> 8) buf[stream2][off+3] = uint8(v2 >> 8) } off += 4 if off == 0 { if bufoff > dstEvery { d.bufs.Put(buf) return nil, errors.New("corruption detected: stream overrun 1") } // There must at least be 3 buffers left. if len(out)-bufoff < dstEvery*3 { d.bufs.Put(buf) return nil, errors.New("corruption detected: stream overrun 2") } //copy(out, buf[0][:]) //copy(out[dstEvery:], buf[1][:]) //copy(out[dstEvery*2:], buf[2][:]) *(*[bufoff]byte)(out) = buf[0] *(*[bufoff]byte)(out[dstEvery:]) = buf[1] *(*[bufoff]byte)(out[dstEvery*2:]) = buf[2] *(*[bufoff]byte)(out[dstEvery*3:]) = buf[3] out = out[bufoff:] decoded += bufoff * 4 } } if off > 0 { ioff := int(off) if len(out) < dstEvery*3+ioff { d.bufs.Put(buf) return nil, errors.New("corruption detected: stream overrun 3") } copy(out, buf[0][:off]) copy(out[dstEvery:], buf[1][:off]) copy(out[dstEvery*2:], buf[2][:off]) copy(out[dstEvery*3:], buf[3][:off]) decoded += int(off) * 4 out = out[off:] } // Decode remaining. // Decode remaining. remainBytes := dstEvery - (decoded / 4) for i := range br { offset := dstEvery * i endsAt := offset + remainBytes if endsAt > len(out) { endsAt = len(out) } br := &br[i] bitsLeft := br.remaining() for bitsLeft > 0 { if br.finished() { d.bufs.Put(buf) return nil, io.ErrUnexpectedEOF } if br.bitsRead >= 56 { if br.off >= 4 { v := br.in[br.off-4:] v = v[:4] low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24) br.value |= uint64(low) << (br.bitsRead - 32) br.bitsRead -= 32 br.off -= 4 } else { for br.off > 0 { br.value |= uint64(br.in[br.off-1]) << (br.bitsRead - 8) br.bitsRead -= 8 br.off-- } } } // end inline... if offset >= endsAt { d.bufs.Put(buf) return nil, errors.New("corruption detected: stream overrun 4") } // Read value and increment offset. v := single[uint8(br.value>>shift)].entry nBits := uint8(v) br.advance(nBits) bitsLeft -= uint(nBits) out[offset] = uint8(v >> 8) offset++ } if offset != endsAt { d.bufs.Put(buf) return nil, fmt.Errorf("corruption detected: short output block %d, end %d != %d", i, offset, endsAt) } decoded += offset - dstEvery*i err = br.close() if err != nil { d.bufs.Put(buf) return nil, err } } d.bufs.Put(buf) if dstSize != decoded { return nil, errors.New("corruption detected: short output block") } return dst, nil } // Decompress4X will decompress a 4X encoded stream. // The length of the supplied input must match the end of a block exactly. // The *capacity* of the dst slice must match the destination size of // the uncompressed data exactly. func (d *Decoder) decompress4X8bitExactly(dst, src []byte) ([]byte, error) { var br [4]bitReaderBytes start := 6 for i := 0; i < 3; i++ { length := int(src[i*2]) | (int(src[i*2+1]) << 8) if start+length >= len(src) { return nil, errors.New("truncated input (or invalid offset)") } err := br[i].init(src[start : start+length]) if err != nil { return nil, err } start += length } err := br[3].init(src[start:]) if err != nil { return nil, err } // destination, offset to match first output dstSize := cap(dst) dst = dst[:dstSize] out := dst dstEvery := (dstSize + 3) / 4 const shift = 56 const tlSize = 1 << 8 single := d.dt.single[:tlSize] // Use temp table to avoid bound checks/append penalty. buf := d.buffer() var off uint8 var decoded int // Decode 4 values from each decoder/loop. const bufoff = 256 for { if br[0].off < 4 || br[1].off < 4 || br[2].off < 4 || br[3].off < 4 { break } { // Interleave 2 decodes. const stream = 0 const stream2 = 1 br1 := &br[stream] br2 := &br[stream2] br1.fillFast() br2.fillFast() v := single[uint8(br1.value>>shift)].entry v2 := single[uint8(br2.value>>shift)].entry br1.bitsRead += uint8(v) br1.value <<= v & 63 br2.bitsRead += uint8(v2) br2.value <<= v2 & 63 buf[stream][off] = uint8(v >> 8) buf[stream2][off] = uint8(v2 >> 8) v = single[uint8(br1.value>>shift)].entry v2 = single[uint8(br2.value>>shift)].entry br1.bitsRead += uint8(v) br1.value <<= v & 63 br2.bitsRead += uint8(v2) br2.value <<= v2 & 63 buf[stream][off+1] = uint8(v >> 8) buf[stream2][off+1] = uint8(v2 >> 8) v = single[uint8(br1.value>>shift)].entry v2 = single[uint8(br2.value>>shift)].entry br1.bitsRead += uint8(v) br1.value <<= v & 63 br2.bitsRead += uint8(v2) br2.value <<= v2 & 63 buf[stream][off+2] = uint8(v >> 8) buf[stream2][off+2] = uint8(v2 >> 8) v = single[uint8(br1.value>>shift)].entry v2 = single[uint8(br2.value>>shift)].entry br1.bitsRead += uint8(v) br1.value <<= v & 63 br2.bitsRead += uint8(v2) br2.value <<= v2 & 63 buf[stream][off+3] = uint8(v >> 8) buf[stream2][off+3] = uint8(v2 >> 8) } { const stream = 2 const stream2 = 3 br1 := &br[stream] br2 := &br[stream2] br1.fillFast() br2.fillFast() v := single[uint8(br1.value>>shift)].entry v2 := single[uint8(br2.value>>shift)].entry br1.bitsRead += uint8(v) br1.value <<= v & 63 br2.bitsRead += uint8(v2) br2.value <<= v2 & 63 buf[stream][off] = uint8(v >> 8) buf[stream2][off] = uint8(v2 >> 8) v = single[uint8(br1.value>>shift)].entry v2 = single[uint8(br2.value>>shift)].entry br1.bitsRead += uint8(v) br1.value <<= v & 63 br2.bitsRead += uint8(v2) br2.value <<= v2 & 63 buf[stream][off+1] = uint8(v >> 8) buf[stream2][off+1] = uint8(v2 >> 8) v = single[uint8(br1.value>>shift)].entry v2 = single[uint8(br2.value>>shift)].entry br1.bitsRead += uint8(v) br1.value <<= v & 63 br2.bitsRead += uint8(v2) br2.value <<= v2 & 63 buf[stream][off+2] = uint8(v >> 8) buf[stream2][off+2] = uint8(v2 >> 8) v = single[uint8(br1.value>>shift)].entry v2 = single[uint8(br2.value>>shift)].entry br1.bitsRead += uint8(v) br1.value <<= v & 63 br2.bitsRead += uint8(v2) br2.value <<= v2 & 63 buf[stream][off+3] = uint8(v >> 8) buf[stream2][off+3] = uint8(v2 >> 8) } off += 4 if off == 0 { if bufoff > dstEvery { d.bufs.Put(buf) return nil, errors.New("corruption detected: stream overrun 1") } // There must at least be 3 buffers left. if len(out)-bufoff < dstEvery*3 { d.bufs.Put(buf) return nil, errors.New("corruption detected: stream overrun 2") } //copy(out, buf[0][:]) //copy(out[dstEvery:], buf[1][:]) //copy(out[dstEvery*2:], buf[2][:]) // copy(out[dstEvery*3:], buf[3][:]) *(*[bufoff]byte)(out) = buf[0] *(*[bufoff]byte)(out[dstEvery:]) = buf[1] *(*[bufoff]byte)(out[dstEvery*2:]) = buf[2] *(*[bufoff]byte)(out[dstEvery*3:]) = buf[3] out = out[bufoff:] decoded += bufoff * 4 } } if off > 0 { ioff := int(off) if len(out) < dstEvery*3+ioff { return nil, errors.New("corruption detected: stream overrun 3") } copy(out, buf[0][:off]) copy(out[dstEvery:], buf[1][:off]) copy(out[dstEvery*2:], buf[2][:off]) copy(out[dstEvery*3:], buf[3][:off]) decoded += int(off) * 4 out = out[off:] } // Decode remaining. remainBytes := dstEvery - (decoded / 4) for i := range br { offset := dstEvery * i endsAt := offset + remainBytes if endsAt > len(out) { endsAt = len(out) } br := &br[i] bitsLeft := br.remaining() for bitsLeft > 0 { if br.finished() { d.bufs.Put(buf) return nil, io.ErrUnexpectedEOF } if br.bitsRead >= 56 { if br.off >= 4 { v := br.in[br.off-4:] v = v[:4] low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24) br.value |= uint64(low) << (br.bitsRead - 32) br.bitsRead -= 32 br.off -= 4 } else { for br.off > 0 { br.value |= uint64(br.in[br.off-1]) << (br.bitsRead - 8) br.bitsRead -= 8 br.off-- } } } // end inline... if offset >= endsAt { d.bufs.Put(buf) return nil, errors.New("corruption detected: stream overrun 4") } // Read value and increment offset. v := single[br.peekByteFast()].entry nBits := uint8(v) br.advance(nBits) bitsLeft -= uint(nBits) out[offset] = uint8(v >> 8) offset++ } if offset != endsAt { d.bufs.Put(buf) return nil, fmt.Errorf("corruption detected: short output block %d, end %d != %d", i, offset, endsAt) } decoded += offset - dstEvery*i err = br.close() if err != nil { d.bufs.Put(buf) return nil, err } } d.bufs.Put(buf) if dstSize != decoded { return nil, errors.New("corruption detected: short output block") } return dst, nil } // matches will compare a decoding table to a coding table. // Errors are written to the writer. // Nothing will be written if table is ok. func (s *Scratch) matches(ct cTable, w io.Writer) { if s == nil || len(s.dt.single) == 0 { return } dt := s.dt.single[:1<>8) == byte(sym) { fmt.Fprintf(w, "symbol %x has decoder, but no encoder\n", sym) errs++ break } } if errs == 0 { broken-- } continue } // Unused bits in input ub := tablelog - enc.nBits top := enc.val << ub // decoder looks at top bits. dec := dt[top] if uint8(dec.entry) != enc.nBits { fmt.Fprintf(w, "symbol 0x%x bit size mismatch (enc: %d, dec:%d).\n", sym, enc.nBits, uint8(dec.entry)) errs++ } if uint8(dec.entry>>8) != uint8(sym) { fmt.Fprintf(w, "symbol 0x%x decoder output mismatch (enc: %d, dec:%d).\n", sym, sym, uint8(dec.entry>>8)) errs++ } if errs > 0 { fmt.Fprintf(w, "%d errros in base, stopping\n", errs) continue } // Ensure that all combinations are covered. for i := uint16(0); i < (1 << ub); i++ { vval := top | i dec := dt[vval] if uint8(dec.entry) != enc.nBits { fmt.Fprintf(w, "symbol 0x%x bit size mismatch (enc: %d, dec:%d).\n", vval, enc.nBits, uint8(dec.entry)) errs++ } if uint8(dec.entry>>8) != uint8(sym) { fmt.Fprintf(w, "symbol 0x%x decoder output mismatch (enc: %d, dec:%d).\n", vval, sym, uint8(dec.entry>>8)) errs++ } if errs > 20 { fmt.Fprintf(w, "%d errros, stopping\n", errs) break } } if errs == 0 { ok++ broken-- } } if broken > 0 { fmt.Fprintf(w, "%d broken, %d ok\n", broken, ok) } } ================================================ FILE: vendor/github.com/klauspost/compress/huff0/decompress_amd64.go ================================================ //go:build amd64 && !appengine && !noasm && gc // +build amd64,!appengine,!noasm,gc // This file contains the specialisation of Decoder.Decompress4X // and Decoder.Decompress1X that use an asm implementation of thir main loops. package huff0 import ( "errors" "fmt" "github.com/klauspost/compress/internal/cpuinfo" ) // decompress4x_main_loop_x86 is an x86 assembler implementation // of Decompress4X when tablelog > 8. // //go:noescape func decompress4x_main_loop_amd64(ctx *decompress4xContext) // decompress4x_8b_loop_x86 is an x86 assembler implementation // of Decompress4X when tablelog <= 8 which decodes 4 entries // per loop. // //go:noescape func decompress4x_8b_main_loop_amd64(ctx *decompress4xContext) // fallback8BitSize is the size where using Go version is faster. const fallback8BitSize = 800 type decompress4xContext struct { pbr *[4]bitReaderShifted peekBits uint8 out *byte dstEvery int tbl *dEntrySingle decoded int limit *byte } // Decompress4X will decompress a 4X encoded stream. // The length of the supplied input must match the end of a block exactly. // The *capacity* of the dst slice must match the destination size of // the uncompressed data exactly. func (d *Decoder) Decompress4X(dst, src []byte) ([]byte, error) { if len(d.dt.single) == 0 { return nil, errors.New("no table loaded") } if len(src) < 6+(4*1) { return nil, errors.New("input too small") } use8BitTables := d.actualTableLog <= 8 if cap(dst) < fallback8BitSize && use8BitTables { return d.decompress4X8bit(dst, src) } var br [4]bitReaderShifted // Decode "jump table" start := 6 for i := 0; i < 3; i++ { length := int(src[i*2]) | (int(src[i*2+1]) << 8) if start+length >= len(src) { return nil, errors.New("truncated input (or invalid offset)") } err := br[i].init(src[start : start+length]) if err != nil { return nil, err } start += length } err := br[3].init(src[start:]) if err != nil { return nil, err } // destination, offset to match first output dstSize := cap(dst) dst = dst[:dstSize] out := dst dstEvery := (dstSize + 3) / 4 const tlSize = 1 << tableLogMax const tlMask = tlSize - 1 single := d.dt.single[:tlSize] var decoded int if len(out) > 4*4 && !(br[0].off < 4 || br[1].off < 4 || br[2].off < 4 || br[3].off < 4) { ctx := decompress4xContext{ pbr: &br, peekBits: uint8((64 - d.actualTableLog) & 63), // see: bitReaderShifted.peekBitsFast() out: &out[0], dstEvery: dstEvery, tbl: &single[0], limit: &out[dstEvery-4], // Always stop decoding when first buffer gets here to avoid writing OOB on last. } if use8BitTables { decompress4x_8b_main_loop_amd64(&ctx) } else { decompress4x_main_loop_amd64(&ctx) } decoded = ctx.decoded out = out[decoded/4:] } // Decode remaining. remainBytes := dstEvery - (decoded / 4) for i := range br { offset := dstEvery * i endsAt := offset + remainBytes if endsAt > len(out) { endsAt = len(out) } br := &br[i] bitsLeft := br.remaining() for bitsLeft > 0 { br.fill() if offset >= endsAt { return nil, errors.New("corruption detected: stream overrun 4") } // Read value and increment offset. val := br.peekBitsFast(d.actualTableLog) v := single[val&tlMask].entry nBits := uint8(v) br.advance(nBits) bitsLeft -= uint(nBits) out[offset] = uint8(v >> 8) offset++ } if offset != endsAt { return nil, fmt.Errorf("corruption detected: short output block %d, end %d != %d", i, offset, endsAt) } decoded += offset - dstEvery*i err = br.close() if err != nil { return nil, err } } if dstSize != decoded { return nil, errors.New("corruption detected: short output block") } return dst, nil } // decompress4x_main_loop_x86 is an x86 assembler implementation // of Decompress1X when tablelog > 8. // //go:noescape func decompress1x_main_loop_amd64(ctx *decompress1xContext) // decompress4x_main_loop_x86 is an x86 with BMI2 assembler implementation // of Decompress1X when tablelog > 8. // //go:noescape func decompress1x_main_loop_bmi2(ctx *decompress1xContext) type decompress1xContext struct { pbr *bitReaderShifted peekBits uint8 out *byte outCap int tbl *dEntrySingle decoded int } // Error reported by asm implementations const error_max_decoded_size_exeeded = -1 // Decompress1X will decompress a 1X encoded stream. // The cap of the output buffer will be the maximum decompressed size. // The length of the supplied input must match the end of a block exactly. func (d *Decoder) Decompress1X(dst, src []byte) ([]byte, error) { if len(d.dt.single) == 0 { return nil, errors.New("no table loaded") } var br bitReaderShifted err := br.init(src) if err != nil { return dst, err } maxDecodedSize := cap(dst) dst = dst[:maxDecodedSize] const tlSize = 1 << tableLogMax const tlMask = tlSize - 1 if maxDecodedSize >= 4 { ctx := decompress1xContext{ pbr: &br, out: &dst[0], outCap: maxDecodedSize, peekBits: uint8((64 - d.actualTableLog) & 63), // see: bitReaderShifted.peekBitsFast() tbl: &d.dt.single[0], } if cpuinfo.HasBMI2() { decompress1x_main_loop_bmi2(&ctx) } else { decompress1x_main_loop_amd64(&ctx) } if ctx.decoded == error_max_decoded_size_exeeded { return nil, ErrMaxDecodedSizeExceeded } dst = dst[:ctx.decoded] } // br < 8, so uint8 is fine bitsLeft := uint8(br.off)*8 + 64 - br.bitsRead for bitsLeft > 0 { br.fill() if len(dst) >= maxDecodedSize { br.close() return nil, ErrMaxDecodedSizeExceeded } v := d.dt.single[br.peekBitsFast(d.actualTableLog)&tlMask] nBits := uint8(v.entry) br.advance(nBits) bitsLeft -= nBits dst = append(dst, uint8(v.entry>>8)) } return dst, br.close() } ================================================ FILE: vendor/github.com/klauspost/compress/huff0/decompress_amd64.s ================================================ // Code generated by command: go run gen.go -out ../decompress_amd64.s -pkg=huff0. DO NOT EDIT. //go:build amd64 && !appengine && !noasm && gc // func decompress4x_main_loop_amd64(ctx *decompress4xContext) TEXT ·decompress4x_main_loop_amd64(SB), $0-8 // Preload values MOVQ ctx+0(FP), AX MOVBQZX 8(AX), DI MOVQ 16(AX), BX MOVQ 48(AX), SI MOVQ 24(AX), R8 MOVQ 32(AX), R9 MOVQ (AX), R10 // Main loop main_loop: XORL DX, DX CMPQ BX, SI SETGE DL // br0.fillFast32() MOVQ 32(R10), R11 MOVBQZX 40(R10), R12 CMPQ R12, $0x20 JBE skip_fill0 MOVQ 24(R10), AX SUBQ $0x20, R12 SUBQ $0x04, AX MOVQ (R10), R13 // b.value |= uint64(low) << (b.bitsRead & 63) MOVL (AX)(R13*1), R13 MOVQ R12, CX SHLQ CL, R13 MOVQ AX, 24(R10) ORQ R13, R11 // exhausted += (br0.off < 4) CMPQ AX, $0x04 ADCB $+0, DL skip_fill0: // val0 := br0.peekTopBits(peekBits) MOVQ R11, R13 MOVQ DI, CX SHRQ CL, R13 // v0 := table[val0&mask] MOVW (R9)(R13*2), CX // br0.advance(uint8(v0.entry) MOVB CH, AL SHLQ CL, R11 ADDB CL, R12 // val1 := br0.peekTopBits(peekBits) MOVQ DI, CX MOVQ R11, R13 SHRQ CL, R13 // v1 := table[val1&mask] MOVW (R9)(R13*2), CX // br0.advance(uint8(v1.entry)) MOVB CH, AH SHLQ CL, R11 ADDB CL, R12 // these two writes get coalesced // out[id * dstEvery + 0] = uint8(v0.entry >> 8) // out[id * dstEvery + 1] = uint8(v1.entry >> 8) MOVW AX, (BX) // update the bitreader structure MOVQ R11, 32(R10) MOVB R12, 40(R10) // br1.fillFast32() MOVQ 80(R10), R11 MOVBQZX 88(R10), R12 CMPQ R12, $0x20 JBE skip_fill1 MOVQ 72(R10), AX SUBQ $0x20, R12 SUBQ $0x04, AX MOVQ 48(R10), R13 // b.value |= uint64(low) << (b.bitsRead & 63) MOVL (AX)(R13*1), R13 MOVQ R12, CX SHLQ CL, R13 MOVQ AX, 72(R10) ORQ R13, R11 // exhausted += (br1.off < 4) CMPQ AX, $0x04 ADCB $+0, DL skip_fill1: // val0 := br1.peekTopBits(peekBits) MOVQ R11, R13 MOVQ DI, CX SHRQ CL, R13 // v0 := table[val0&mask] MOVW (R9)(R13*2), CX // br1.advance(uint8(v0.entry) MOVB CH, AL SHLQ CL, R11 ADDB CL, R12 // val1 := br1.peekTopBits(peekBits) MOVQ DI, CX MOVQ R11, R13 SHRQ CL, R13 // v1 := table[val1&mask] MOVW (R9)(R13*2), CX // br1.advance(uint8(v1.entry)) MOVB CH, AH SHLQ CL, R11 ADDB CL, R12 // these two writes get coalesced // out[id * dstEvery + 0] = uint8(v0.entry >> 8) // out[id * dstEvery + 1] = uint8(v1.entry >> 8) MOVW AX, (BX)(R8*1) // update the bitreader structure MOVQ R11, 80(R10) MOVB R12, 88(R10) // br2.fillFast32() MOVQ 128(R10), R11 MOVBQZX 136(R10), R12 CMPQ R12, $0x20 JBE skip_fill2 MOVQ 120(R10), AX SUBQ $0x20, R12 SUBQ $0x04, AX MOVQ 96(R10), R13 // b.value |= uint64(low) << (b.bitsRead & 63) MOVL (AX)(R13*1), R13 MOVQ R12, CX SHLQ CL, R13 MOVQ AX, 120(R10) ORQ R13, R11 // exhausted += (br2.off < 4) CMPQ AX, $0x04 ADCB $+0, DL skip_fill2: // val0 := br2.peekTopBits(peekBits) MOVQ R11, R13 MOVQ DI, CX SHRQ CL, R13 // v0 := table[val0&mask] MOVW (R9)(R13*2), CX // br2.advance(uint8(v0.entry) MOVB CH, AL SHLQ CL, R11 ADDB CL, R12 // val1 := br2.peekTopBits(peekBits) MOVQ DI, CX MOVQ R11, R13 SHRQ CL, R13 // v1 := table[val1&mask] MOVW (R9)(R13*2), CX // br2.advance(uint8(v1.entry)) MOVB CH, AH SHLQ CL, R11 ADDB CL, R12 // these two writes get coalesced // out[id * dstEvery + 0] = uint8(v0.entry >> 8) // out[id * dstEvery + 1] = uint8(v1.entry >> 8) MOVW AX, (BX)(R8*2) // update the bitreader structure MOVQ R11, 128(R10) MOVB R12, 136(R10) // br3.fillFast32() MOVQ 176(R10), R11 MOVBQZX 184(R10), R12 CMPQ R12, $0x20 JBE skip_fill3 MOVQ 168(R10), AX SUBQ $0x20, R12 SUBQ $0x04, AX MOVQ 144(R10), R13 // b.value |= uint64(low) << (b.bitsRead & 63) MOVL (AX)(R13*1), R13 MOVQ R12, CX SHLQ CL, R13 MOVQ AX, 168(R10) ORQ R13, R11 // exhausted += (br3.off < 4) CMPQ AX, $0x04 ADCB $+0, DL skip_fill3: // val0 := br3.peekTopBits(peekBits) MOVQ R11, R13 MOVQ DI, CX SHRQ CL, R13 // v0 := table[val0&mask] MOVW (R9)(R13*2), CX // br3.advance(uint8(v0.entry) MOVB CH, AL SHLQ CL, R11 ADDB CL, R12 // val1 := br3.peekTopBits(peekBits) MOVQ DI, CX MOVQ R11, R13 SHRQ CL, R13 // v1 := table[val1&mask] MOVW (R9)(R13*2), CX // br3.advance(uint8(v1.entry)) MOVB CH, AH SHLQ CL, R11 ADDB CL, R12 // these two writes get coalesced // out[id * dstEvery + 0] = uint8(v0.entry >> 8) // out[id * dstEvery + 1] = uint8(v1.entry >> 8) LEAQ (R8)(R8*2), CX MOVW AX, (BX)(CX*1) // update the bitreader structure MOVQ R11, 176(R10) MOVB R12, 184(R10) ADDQ $0x02, BX TESTB DL, DL JZ main_loop MOVQ ctx+0(FP), AX SUBQ 16(AX), BX SHLQ $0x02, BX MOVQ BX, 40(AX) RET // func decompress4x_8b_main_loop_amd64(ctx *decompress4xContext) TEXT ·decompress4x_8b_main_loop_amd64(SB), $0-8 // Preload values MOVQ ctx+0(FP), CX MOVBQZX 8(CX), DI MOVQ 16(CX), BX MOVQ 48(CX), SI MOVQ 24(CX), R8 MOVQ 32(CX), R9 MOVQ (CX), R10 // Main loop main_loop: XORL DX, DX CMPQ BX, SI SETGE DL // br0.fillFast32() MOVQ 32(R10), R11 MOVBQZX 40(R10), R12 CMPQ R12, $0x20 JBE skip_fill0 MOVQ 24(R10), R13 SUBQ $0x20, R12 SUBQ $0x04, R13 MOVQ (R10), R14 // b.value |= uint64(low) << (b.bitsRead & 63) MOVL (R13)(R14*1), R14 MOVQ R12, CX SHLQ CL, R14 MOVQ R13, 24(R10) ORQ R14, R11 // exhausted += (br0.off < 4) CMPQ R13, $0x04 ADCB $+0, DL skip_fill0: // val0 := br0.peekTopBits(peekBits) MOVQ R11, R13 MOVQ DI, CX SHRQ CL, R13 // v0 := table[val0&mask] MOVW (R9)(R13*2), CX // br0.advance(uint8(v0.entry) MOVB CH, AL SHLQ CL, R11 ADDB CL, R12 // val1 := br0.peekTopBits(peekBits) MOVQ R11, R13 MOVQ DI, CX SHRQ CL, R13 // v1 := table[val0&mask] MOVW (R9)(R13*2), CX // br0.advance(uint8(v1.entry) MOVB CH, AH SHLQ CL, R11 ADDB CL, R12 BSWAPL AX // val2 := br0.peekTopBits(peekBits) MOVQ R11, R13 MOVQ DI, CX SHRQ CL, R13 // v2 := table[val0&mask] MOVW (R9)(R13*2), CX // br0.advance(uint8(v2.entry) MOVB CH, AH SHLQ CL, R11 ADDB CL, R12 // val3 := br0.peekTopBits(peekBits) MOVQ R11, R13 MOVQ DI, CX SHRQ CL, R13 // v3 := table[val0&mask] MOVW (R9)(R13*2), CX // br0.advance(uint8(v3.entry) MOVB CH, AL SHLQ CL, R11 ADDB CL, R12 BSWAPL AX // these four writes get coalesced // out[id * dstEvery + 0] = uint8(v0.entry >> 8) // out[id * dstEvery + 1] = uint8(v1.entry >> 8) // out[id * dstEvery + 3] = uint8(v2.entry >> 8) // out[id * dstEvery + 4] = uint8(v3.entry >> 8) MOVL AX, (BX) // update the bitreader structure MOVQ R11, 32(R10) MOVB R12, 40(R10) // br1.fillFast32() MOVQ 80(R10), R11 MOVBQZX 88(R10), R12 CMPQ R12, $0x20 JBE skip_fill1 MOVQ 72(R10), R13 SUBQ $0x20, R12 SUBQ $0x04, R13 MOVQ 48(R10), R14 // b.value |= uint64(low) << (b.bitsRead & 63) MOVL (R13)(R14*1), R14 MOVQ R12, CX SHLQ CL, R14 MOVQ R13, 72(R10) ORQ R14, R11 // exhausted += (br1.off < 4) CMPQ R13, $0x04 ADCB $+0, DL skip_fill1: // val0 := br1.peekTopBits(peekBits) MOVQ R11, R13 MOVQ DI, CX SHRQ CL, R13 // v0 := table[val0&mask] MOVW (R9)(R13*2), CX // br1.advance(uint8(v0.entry) MOVB CH, AL SHLQ CL, R11 ADDB CL, R12 // val1 := br1.peekTopBits(peekBits) MOVQ R11, R13 MOVQ DI, CX SHRQ CL, R13 // v1 := table[val0&mask] MOVW (R9)(R13*2), CX // br1.advance(uint8(v1.entry) MOVB CH, AH SHLQ CL, R11 ADDB CL, R12 BSWAPL AX // val2 := br1.peekTopBits(peekBits) MOVQ R11, R13 MOVQ DI, CX SHRQ CL, R13 // v2 := table[val0&mask] MOVW (R9)(R13*2), CX // br1.advance(uint8(v2.entry) MOVB CH, AH SHLQ CL, R11 ADDB CL, R12 // val3 := br1.peekTopBits(peekBits) MOVQ R11, R13 MOVQ DI, CX SHRQ CL, R13 // v3 := table[val0&mask] MOVW (R9)(R13*2), CX // br1.advance(uint8(v3.entry) MOVB CH, AL SHLQ CL, R11 ADDB CL, R12 BSWAPL AX // these four writes get coalesced // out[id * dstEvery + 0] = uint8(v0.entry >> 8) // out[id * dstEvery + 1] = uint8(v1.entry >> 8) // out[id * dstEvery + 3] = uint8(v2.entry >> 8) // out[id * dstEvery + 4] = uint8(v3.entry >> 8) MOVL AX, (BX)(R8*1) // update the bitreader structure MOVQ R11, 80(R10) MOVB R12, 88(R10) // br2.fillFast32() MOVQ 128(R10), R11 MOVBQZX 136(R10), R12 CMPQ R12, $0x20 JBE skip_fill2 MOVQ 120(R10), R13 SUBQ $0x20, R12 SUBQ $0x04, R13 MOVQ 96(R10), R14 // b.value |= uint64(low) << (b.bitsRead & 63) MOVL (R13)(R14*1), R14 MOVQ R12, CX SHLQ CL, R14 MOVQ R13, 120(R10) ORQ R14, R11 // exhausted += (br2.off < 4) CMPQ R13, $0x04 ADCB $+0, DL skip_fill2: // val0 := br2.peekTopBits(peekBits) MOVQ R11, R13 MOVQ DI, CX SHRQ CL, R13 // v0 := table[val0&mask] MOVW (R9)(R13*2), CX // br2.advance(uint8(v0.entry) MOVB CH, AL SHLQ CL, R11 ADDB CL, R12 // val1 := br2.peekTopBits(peekBits) MOVQ R11, R13 MOVQ DI, CX SHRQ CL, R13 // v1 := table[val0&mask] MOVW (R9)(R13*2), CX // br2.advance(uint8(v1.entry) MOVB CH, AH SHLQ CL, R11 ADDB CL, R12 BSWAPL AX // val2 := br2.peekTopBits(peekBits) MOVQ R11, R13 MOVQ DI, CX SHRQ CL, R13 // v2 := table[val0&mask] MOVW (R9)(R13*2), CX // br2.advance(uint8(v2.entry) MOVB CH, AH SHLQ CL, R11 ADDB CL, R12 // val3 := br2.peekTopBits(peekBits) MOVQ R11, R13 MOVQ DI, CX SHRQ CL, R13 // v3 := table[val0&mask] MOVW (R9)(R13*2), CX // br2.advance(uint8(v3.entry) MOVB CH, AL SHLQ CL, R11 ADDB CL, R12 BSWAPL AX // these four writes get coalesced // out[id * dstEvery + 0] = uint8(v0.entry >> 8) // out[id * dstEvery + 1] = uint8(v1.entry >> 8) // out[id * dstEvery + 3] = uint8(v2.entry >> 8) // out[id * dstEvery + 4] = uint8(v3.entry >> 8) MOVL AX, (BX)(R8*2) // update the bitreader structure MOVQ R11, 128(R10) MOVB R12, 136(R10) // br3.fillFast32() MOVQ 176(R10), R11 MOVBQZX 184(R10), R12 CMPQ R12, $0x20 JBE skip_fill3 MOVQ 168(R10), R13 SUBQ $0x20, R12 SUBQ $0x04, R13 MOVQ 144(R10), R14 // b.value |= uint64(low) << (b.bitsRead & 63) MOVL (R13)(R14*1), R14 MOVQ R12, CX SHLQ CL, R14 MOVQ R13, 168(R10) ORQ R14, R11 // exhausted += (br3.off < 4) CMPQ R13, $0x04 ADCB $+0, DL skip_fill3: // val0 := br3.peekTopBits(peekBits) MOVQ R11, R13 MOVQ DI, CX SHRQ CL, R13 // v0 := table[val0&mask] MOVW (R9)(R13*2), CX // br3.advance(uint8(v0.entry) MOVB CH, AL SHLQ CL, R11 ADDB CL, R12 // val1 := br3.peekTopBits(peekBits) MOVQ R11, R13 MOVQ DI, CX SHRQ CL, R13 // v1 := table[val0&mask] MOVW (R9)(R13*2), CX // br3.advance(uint8(v1.entry) MOVB CH, AH SHLQ CL, R11 ADDB CL, R12 BSWAPL AX // val2 := br3.peekTopBits(peekBits) MOVQ R11, R13 MOVQ DI, CX SHRQ CL, R13 // v2 := table[val0&mask] MOVW (R9)(R13*2), CX // br3.advance(uint8(v2.entry) MOVB CH, AH SHLQ CL, R11 ADDB CL, R12 // val3 := br3.peekTopBits(peekBits) MOVQ R11, R13 MOVQ DI, CX SHRQ CL, R13 // v3 := table[val0&mask] MOVW (R9)(R13*2), CX // br3.advance(uint8(v3.entry) MOVB CH, AL SHLQ CL, R11 ADDB CL, R12 BSWAPL AX // these four writes get coalesced // out[id * dstEvery + 0] = uint8(v0.entry >> 8) // out[id * dstEvery + 1] = uint8(v1.entry >> 8) // out[id * dstEvery + 3] = uint8(v2.entry >> 8) // out[id * dstEvery + 4] = uint8(v3.entry >> 8) LEAQ (R8)(R8*2), CX MOVL AX, (BX)(CX*1) // update the bitreader structure MOVQ R11, 176(R10) MOVB R12, 184(R10) ADDQ $0x04, BX TESTB DL, DL JZ main_loop MOVQ ctx+0(FP), AX SUBQ 16(AX), BX SHLQ $0x02, BX MOVQ BX, 40(AX) RET // func decompress1x_main_loop_amd64(ctx *decompress1xContext) TEXT ·decompress1x_main_loop_amd64(SB), $0-8 MOVQ ctx+0(FP), CX MOVQ 16(CX), DX MOVQ 24(CX), BX CMPQ BX, $0x04 JB error_max_decoded_size_exceeded LEAQ (DX)(BX*1), BX MOVQ (CX), SI MOVQ (SI), R8 MOVQ 24(SI), R9 MOVQ 32(SI), R10 MOVBQZX 40(SI), R11 MOVQ 32(CX), SI MOVBQZX 8(CX), DI JMP loop_condition main_loop: // Check if we have room for 4 bytes in the output buffer LEAQ 4(DX), CX CMPQ CX, BX JGE error_max_decoded_size_exceeded // Decode 4 values CMPQ R11, $0x20 JL bitReader_fillFast_1_end SUBQ $0x20, R11 SUBQ $0x04, R9 MOVL (R8)(R9*1), R12 MOVQ R11, CX SHLQ CL, R12 ORQ R12, R10 bitReader_fillFast_1_end: MOVQ DI, CX MOVQ R10, R12 SHRQ CL, R12 MOVW (SI)(R12*2), CX MOVB CH, AL MOVBQZX CL, CX ADDQ CX, R11 SHLQ CL, R10 MOVQ DI, CX MOVQ R10, R12 SHRQ CL, R12 MOVW (SI)(R12*2), CX MOVB CH, AH MOVBQZX CL, CX ADDQ CX, R11 SHLQ CL, R10 BSWAPL AX CMPQ R11, $0x20 JL bitReader_fillFast_2_end SUBQ $0x20, R11 SUBQ $0x04, R9 MOVL (R8)(R9*1), R12 MOVQ R11, CX SHLQ CL, R12 ORQ R12, R10 bitReader_fillFast_2_end: MOVQ DI, CX MOVQ R10, R12 SHRQ CL, R12 MOVW (SI)(R12*2), CX MOVB CH, AH MOVBQZX CL, CX ADDQ CX, R11 SHLQ CL, R10 MOVQ DI, CX MOVQ R10, R12 SHRQ CL, R12 MOVW (SI)(R12*2), CX MOVB CH, AL MOVBQZX CL, CX ADDQ CX, R11 SHLQ CL, R10 BSWAPL AX // Store the decoded values MOVL AX, (DX) ADDQ $0x04, DX loop_condition: CMPQ R9, $0x08 JGE main_loop // Update ctx structure MOVQ ctx+0(FP), AX SUBQ 16(AX), DX MOVQ DX, 40(AX) MOVQ (AX), AX MOVQ R9, 24(AX) MOVQ R10, 32(AX) MOVB R11, 40(AX) RET // Report error error_max_decoded_size_exceeded: MOVQ ctx+0(FP), AX MOVQ $-1, CX MOVQ CX, 40(AX) RET // func decompress1x_main_loop_bmi2(ctx *decompress1xContext) // Requires: BMI2 TEXT ·decompress1x_main_loop_bmi2(SB), $0-8 MOVQ ctx+0(FP), CX MOVQ 16(CX), DX MOVQ 24(CX), BX CMPQ BX, $0x04 JB error_max_decoded_size_exceeded LEAQ (DX)(BX*1), BX MOVQ (CX), SI MOVQ (SI), R8 MOVQ 24(SI), R9 MOVQ 32(SI), R10 MOVBQZX 40(SI), R11 MOVQ 32(CX), SI MOVBQZX 8(CX), DI JMP loop_condition main_loop: // Check if we have room for 4 bytes in the output buffer LEAQ 4(DX), CX CMPQ CX, BX JGE error_max_decoded_size_exceeded // Decode 4 values CMPQ R11, $0x20 JL bitReader_fillFast_1_end SUBQ $0x20, R11 SUBQ $0x04, R9 MOVL (R8)(R9*1), CX SHLXQ R11, CX, CX ORQ CX, R10 bitReader_fillFast_1_end: SHRXQ DI, R10, CX MOVW (SI)(CX*2), CX MOVB CH, AL MOVBQZX CL, CX ADDQ CX, R11 SHLXQ CX, R10, R10 SHRXQ DI, R10, CX MOVW (SI)(CX*2), CX MOVB CH, AH MOVBQZX CL, CX ADDQ CX, R11 SHLXQ CX, R10, R10 BSWAPL AX CMPQ R11, $0x20 JL bitReader_fillFast_2_end SUBQ $0x20, R11 SUBQ $0x04, R9 MOVL (R8)(R9*1), CX SHLXQ R11, CX, CX ORQ CX, R10 bitReader_fillFast_2_end: SHRXQ DI, R10, CX MOVW (SI)(CX*2), CX MOVB CH, AH MOVBQZX CL, CX ADDQ CX, R11 SHLXQ CX, R10, R10 SHRXQ DI, R10, CX MOVW (SI)(CX*2), CX MOVB CH, AL MOVBQZX CL, CX ADDQ CX, R11 SHLXQ CX, R10, R10 BSWAPL AX // Store the decoded values MOVL AX, (DX) ADDQ $0x04, DX loop_condition: CMPQ R9, $0x08 JGE main_loop // Update ctx structure MOVQ ctx+0(FP), AX SUBQ 16(AX), DX MOVQ DX, 40(AX) MOVQ (AX), AX MOVQ R9, 24(AX) MOVQ R10, 32(AX) MOVB R11, 40(AX) RET // Report error error_max_decoded_size_exceeded: MOVQ ctx+0(FP), AX MOVQ $-1, CX MOVQ CX, 40(AX) RET ================================================ FILE: vendor/github.com/klauspost/compress/huff0/decompress_generic.go ================================================ //go:build !amd64 || appengine || !gc || noasm // +build !amd64 appengine !gc noasm // This file contains a generic implementation of Decoder.Decompress4X. package huff0 import ( "errors" "fmt" ) // Decompress4X will decompress a 4X encoded stream. // The length of the supplied input must match the end of a block exactly. // The *capacity* of the dst slice must match the destination size of // the uncompressed data exactly. func (d *Decoder) Decompress4X(dst, src []byte) ([]byte, error) { if len(d.dt.single) == 0 { return nil, errors.New("no table loaded") } if len(src) < 6+(4*1) { return nil, errors.New("input too small") } if use8BitTables && d.actualTableLog <= 8 { return d.decompress4X8bit(dst, src) } var br [4]bitReaderShifted // Decode "jump table" start := 6 for i := 0; i < 3; i++ { length := int(src[i*2]) | (int(src[i*2+1]) << 8) if start+length >= len(src) { return nil, errors.New("truncated input (or invalid offset)") } err := br[i].init(src[start : start+length]) if err != nil { return nil, err } start += length } err := br[3].init(src[start:]) if err != nil { return nil, err } // destination, offset to match first output dstSize := cap(dst) dst = dst[:dstSize] out := dst dstEvery := (dstSize + 3) / 4 const tlSize = 1 << tableLogMax const tlMask = tlSize - 1 single := d.dt.single[:tlSize] // Use temp table to avoid bound checks/append penalty. buf := d.buffer() var off uint8 var decoded int // Decode 2 values from each decoder/loop. const bufoff = 256 for { if br[0].off < 4 || br[1].off < 4 || br[2].off < 4 || br[3].off < 4 { break } { const stream = 0 const stream2 = 1 br[stream].fillFast() br[stream2].fillFast() val := br[stream].peekBitsFast(d.actualTableLog) val2 := br[stream2].peekBitsFast(d.actualTableLog) v := single[val&tlMask] v2 := single[val2&tlMask] br[stream].advance(uint8(v.entry)) br[stream2].advance(uint8(v2.entry)) buf[stream][off] = uint8(v.entry >> 8) buf[stream2][off] = uint8(v2.entry >> 8) val = br[stream].peekBitsFast(d.actualTableLog) val2 = br[stream2].peekBitsFast(d.actualTableLog) v = single[val&tlMask] v2 = single[val2&tlMask] br[stream].advance(uint8(v.entry)) br[stream2].advance(uint8(v2.entry)) buf[stream][off+1] = uint8(v.entry >> 8) buf[stream2][off+1] = uint8(v2.entry >> 8) } { const stream = 2 const stream2 = 3 br[stream].fillFast() br[stream2].fillFast() val := br[stream].peekBitsFast(d.actualTableLog) val2 := br[stream2].peekBitsFast(d.actualTableLog) v := single[val&tlMask] v2 := single[val2&tlMask] br[stream].advance(uint8(v.entry)) br[stream2].advance(uint8(v2.entry)) buf[stream][off] = uint8(v.entry >> 8) buf[stream2][off] = uint8(v2.entry >> 8) val = br[stream].peekBitsFast(d.actualTableLog) val2 = br[stream2].peekBitsFast(d.actualTableLog) v = single[val&tlMask] v2 = single[val2&tlMask] br[stream].advance(uint8(v.entry)) br[stream2].advance(uint8(v2.entry)) buf[stream][off+1] = uint8(v.entry >> 8) buf[stream2][off+1] = uint8(v2.entry >> 8) } off += 2 if off == 0 { if bufoff > dstEvery { d.bufs.Put(buf) return nil, errors.New("corruption detected: stream overrun 1") } // There must at least be 3 buffers left. if len(out)-bufoff < dstEvery*3 { d.bufs.Put(buf) return nil, errors.New("corruption detected: stream overrun 2") } //copy(out, buf[0][:]) //copy(out[dstEvery:], buf[1][:]) //copy(out[dstEvery*2:], buf[2][:]) //copy(out[dstEvery*3:], buf[3][:]) *(*[bufoff]byte)(out) = buf[0] *(*[bufoff]byte)(out[dstEvery:]) = buf[1] *(*[bufoff]byte)(out[dstEvery*2:]) = buf[2] *(*[bufoff]byte)(out[dstEvery*3:]) = buf[3] out = out[bufoff:] decoded += bufoff * 4 } } if off > 0 { ioff := int(off) if len(out) < dstEvery*3+ioff { d.bufs.Put(buf) return nil, errors.New("corruption detected: stream overrun 3") } copy(out, buf[0][:off]) copy(out[dstEvery:], buf[1][:off]) copy(out[dstEvery*2:], buf[2][:off]) copy(out[dstEvery*3:], buf[3][:off]) decoded += int(off) * 4 out = out[off:] } // Decode remaining. remainBytes := dstEvery - (decoded / 4) for i := range br { offset := dstEvery * i endsAt := offset + remainBytes if endsAt > len(out) { endsAt = len(out) } br := &br[i] bitsLeft := br.remaining() for bitsLeft > 0 { br.fill() if offset >= endsAt { d.bufs.Put(buf) return nil, errors.New("corruption detected: stream overrun 4") } // Read value and increment offset. val := br.peekBitsFast(d.actualTableLog) v := single[val&tlMask].entry nBits := uint8(v) br.advance(nBits) bitsLeft -= uint(nBits) out[offset] = uint8(v >> 8) offset++ } if offset != endsAt { d.bufs.Put(buf) return nil, fmt.Errorf("corruption detected: short output block %d, end %d != %d", i, offset, endsAt) } decoded += offset - dstEvery*i err = br.close() if err != nil { return nil, err } } d.bufs.Put(buf) if dstSize != decoded { return nil, errors.New("corruption detected: short output block") } return dst, nil } // Decompress1X will decompress a 1X encoded stream. // The cap of the output buffer will be the maximum decompressed size. // The length of the supplied input must match the end of a block exactly. func (d *Decoder) Decompress1X(dst, src []byte) ([]byte, error) { if len(d.dt.single) == 0 { return nil, errors.New("no table loaded") } if use8BitTables && d.actualTableLog <= 8 { return d.decompress1X8Bit(dst, src) } var br bitReaderShifted err := br.init(src) if err != nil { return dst, err } maxDecodedSize := cap(dst) dst = dst[:0] // Avoid bounds check by always having full sized table. const tlSize = 1 << tableLogMax const tlMask = tlSize - 1 dt := d.dt.single[:tlSize] // Use temp table to avoid bound checks/append penalty. bufs := d.buffer() buf := &bufs[0] var off uint8 for br.off >= 8 { br.fillFast() v := dt[br.peekBitsFast(d.actualTableLog)&tlMask] br.advance(uint8(v.entry)) buf[off+0] = uint8(v.entry >> 8) v = dt[br.peekBitsFast(d.actualTableLog)&tlMask] br.advance(uint8(v.entry)) buf[off+1] = uint8(v.entry >> 8) // Refill br.fillFast() v = dt[br.peekBitsFast(d.actualTableLog)&tlMask] br.advance(uint8(v.entry)) buf[off+2] = uint8(v.entry >> 8) v = dt[br.peekBitsFast(d.actualTableLog)&tlMask] br.advance(uint8(v.entry)) buf[off+3] = uint8(v.entry >> 8) off += 4 if off == 0 { if len(dst)+256 > maxDecodedSize { br.close() d.bufs.Put(bufs) return nil, ErrMaxDecodedSizeExceeded } dst = append(dst, buf[:]...) } } if len(dst)+int(off) > maxDecodedSize { d.bufs.Put(bufs) br.close() return nil, ErrMaxDecodedSizeExceeded } dst = append(dst, buf[:off]...) // br < 8, so uint8 is fine bitsLeft := uint8(br.off)*8 + 64 - br.bitsRead for bitsLeft > 0 { br.fill() if false && br.bitsRead >= 32 { if br.off >= 4 { v := br.in[br.off-4:] v = v[:4] low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24) br.value = (br.value << 32) | uint64(low) br.bitsRead -= 32 br.off -= 4 } else { for br.off > 0 { br.value = (br.value << 8) | uint64(br.in[br.off-1]) br.bitsRead -= 8 br.off-- } } } if len(dst) >= maxDecodedSize { d.bufs.Put(bufs) br.close() return nil, ErrMaxDecodedSizeExceeded } v := d.dt.single[br.peekBitsFast(d.actualTableLog)&tlMask] nBits := uint8(v.entry) br.advance(nBits) bitsLeft -= nBits dst = append(dst, uint8(v.entry>>8)) } d.bufs.Put(bufs) return dst, br.close() } ================================================ FILE: vendor/github.com/klauspost/compress/huff0/huff0.go ================================================ // Package huff0 provides fast huffman encoding as used in zstd. // // See README.md at https://github.com/klauspost/compress/tree/master/huff0 for details. package huff0 import ( "errors" "fmt" "math" "math/bits" "sync" "github.com/klauspost/compress/fse" ) const ( maxSymbolValue = 255 // zstandard limits tablelog to 11, see: // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#huffman-tree-description tableLogMax = 11 tableLogDefault = 11 minTablelog = 5 huffNodesLen = 512 // BlockSizeMax is maximum input size for a single block uncompressed. BlockSizeMax = 1<<18 - 1 ) var ( // ErrIncompressible is returned when input is judged to be too hard to compress. ErrIncompressible = errors.New("input is not compressible") // ErrUseRLE is returned from the compressor when the input is a single byte value repeated. ErrUseRLE = errors.New("input is single value repeated") // ErrTooBig is return if input is too large for a single block. ErrTooBig = errors.New("input too big") // ErrMaxDecodedSizeExceeded is return if input is too large for a single block. ErrMaxDecodedSizeExceeded = errors.New("maximum output size exceeded") ) type ReusePolicy uint8 const ( // ReusePolicyAllow will allow reuse if it produces smaller output. ReusePolicyAllow ReusePolicy = iota // ReusePolicyPrefer will re-use aggressively if possible. // This will not check if a new table will produce smaller output, // except if the current table is impossible to use or // compressed output is bigger than input. ReusePolicyPrefer // ReusePolicyNone will disable re-use of tables. // This is slightly faster than ReusePolicyAllow but may produce larger output. ReusePolicyNone // ReusePolicyMust must allow reuse and produce smaller output. ReusePolicyMust ) type Scratch struct { count [maxSymbolValue + 1]uint32 // Per block parameters. // These can be used to override compression parameters of the block. // Do not touch, unless you know what you are doing. // Out is output buffer. // If the scratch is re-used before the caller is done processing the output, // set this field to nil. // Otherwise the output buffer will be re-used for next Compression/Decompression step // and allocation will be avoided. Out []byte // OutTable will contain the table data only, if a new table has been generated. // Slice of the returned data. OutTable []byte // OutData will contain the compressed data. // Slice of the returned data. OutData []byte // MaxDecodedSize will set the maximum allowed output size. // This value will automatically be set to BlockSizeMax if not set. // Decoders will return ErrMaxDecodedSizeExceeded is this limit is exceeded. MaxDecodedSize int br byteReader // MaxSymbolValue will override the maximum symbol value of the next block. MaxSymbolValue uint8 // TableLog will attempt to override the tablelog for the next block. // Must be <= 11 and >= 5. TableLog uint8 // Reuse will specify the reuse policy Reuse ReusePolicy // WantLogLess allows to specify a log 2 reduction that should at least be achieved, // otherwise the block will be returned as incompressible. // The reduction should then at least be (input size >> WantLogLess) // If WantLogLess == 0 any improvement will do. WantLogLess uint8 symbolLen uint16 // Length of active part of the symbol table. maxCount int // count of the most probable symbol clearCount bool // clear count actualTableLog uint8 // Selected tablelog. prevTableLog uint8 // Tablelog for previous table prevTable cTable // Table used for previous compression. cTable cTable // compression table dt dTable // decompression table nodes []nodeElt tmpOut [4][]byte fse *fse.Scratch decPool sync.Pool // *[4][256]byte buffers. huffWeight [maxSymbolValue + 1]byte } // TransferCTable will transfer the previously used compression table. func (s *Scratch) TransferCTable(src *Scratch) { if cap(s.prevTable) < len(src.prevTable) { s.prevTable = make(cTable, 0, maxSymbolValue+1) } s.prevTable = s.prevTable[:len(src.prevTable)] copy(s.prevTable, src.prevTable) s.prevTableLog = src.prevTableLog } func (s *Scratch) prepare(in []byte) (*Scratch, error) { if len(in) > BlockSizeMax { return nil, ErrTooBig } if s == nil { s = &Scratch{} } if s.MaxSymbolValue == 0 { s.MaxSymbolValue = maxSymbolValue } if s.TableLog == 0 { s.TableLog = tableLogDefault } if s.TableLog > tableLogMax || s.TableLog < minTablelog { return nil, fmt.Errorf(" invalid tableLog %d (%d -> %d)", s.TableLog, minTablelog, tableLogMax) } if s.MaxDecodedSize <= 0 || s.MaxDecodedSize > BlockSizeMax { s.MaxDecodedSize = BlockSizeMax } if s.clearCount && s.maxCount == 0 { for i := range s.count { s.count[i] = 0 } s.clearCount = false } if cap(s.Out) == 0 { s.Out = make([]byte, 0, len(in)) } s.Out = s.Out[:0] s.OutTable = nil s.OutData = nil if cap(s.nodes) < huffNodesLen+1 { s.nodes = make([]nodeElt, 0, huffNodesLen+1) } s.nodes = s.nodes[:0] if s.fse == nil { s.fse = &fse.Scratch{} } s.br.init(in) return s, nil } type cTable []cTableEntry func (c cTable) write(s *Scratch) error { var ( // precomputed conversion table bitsToWeight [tableLogMax + 1]byte huffLog = s.actualTableLog // last weight is not saved. maxSymbolValue = uint8(s.symbolLen - 1) huffWeight = s.huffWeight[:256] ) const ( maxFSETableLog = 6 ) // convert to weight bitsToWeight[0] = 0 for n := uint8(1); n < huffLog+1; n++ { bitsToWeight[n] = huffLog + 1 - n } // Acquire histogram for FSE. hist := s.fse.Histogram() hist = hist[:256] for i := range hist[:16] { hist[i] = 0 } for n := uint8(0); n < maxSymbolValue; n++ { v := bitsToWeight[c[n].nBits] & 15 huffWeight[n] = v hist[v]++ } // FSE compress if feasible. if maxSymbolValue >= 2 { huffMaxCnt := uint32(0) huffMax := uint8(0) for i, v := range hist[:16] { if v == 0 { continue } huffMax = byte(i) if v > huffMaxCnt { huffMaxCnt = v } } s.fse.HistogramFinished(huffMax, int(huffMaxCnt)) s.fse.TableLog = maxFSETableLog b, err := fse.Compress(huffWeight[:maxSymbolValue], s.fse) if err == nil && len(b) < int(s.symbolLen>>1) { s.Out = append(s.Out, uint8(len(b))) s.Out = append(s.Out, b...) return nil } // Unable to compress (RLE/uncompressible) } // write raw values as 4-bits (max : 15) if maxSymbolValue > (256 - 128) { // should not happen : likely means source cannot be compressed return ErrIncompressible } op := s.Out // special case, pack weights 4 bits/weight. op = append(op, 128|(maxSymbolValue-1)) // be sure it doesn't cause msan issue in final combination huffWeight[maxSymbolValue] = 0 for n := uint16(0); n < uint16(maxSymbolValue); n += 2 { op = append(op, (huffWeight[n]<<4)|huffWeight[n+1]) } s.Out = op return nil } func (c cTable) estTableSize(s *Scratch) (sz int, err error) { var ( // precomputed conversion table bitsToWeight [tableLogMax + 1]byte huffLog = s.actualTableLog // last weight is not saved. maxSymbolValue = uint8(s.symbolLen - 1) huffWeight = s.huffWeight[:256] ) const ( maxFSETableLog = 6 ) // convert to weight bitsToWeight[0] = 0 for n := uint8(1); n < huffLog+1; n++ { bitsToWeight[n] = huffLog + 1 - n } // Acquire histogram for FSE. hist := s.fse.Histogram() hist = hist[:256] for i := range hist[:16] { hist[i] = 0 } for n := uint8(0); n < maxSymbolValue; n++ { v := bitsToWeight[c[n].nBits] & 15 huffWeight[n] = v hist[v]++ } // FSE compress if feasible. if maxSymbolValue >= 2 { huffMaxCnt := uint32(0) huffMax := uint8(0) for i, v := range hist[:16] { if v == 0 { continue } huffMax = byte(i) if v > huffMaxCnt { huffMaxCnt = v } } s.fse.HistogramFinished(huffMax, int(huffMaxCnt)) s.fse.TableLog = maxFSETableLog b, err := fse.Compress(huffWeight[:maxSymbolValue], s.fse) if err == nil && len(b) < int(s.symbolLen>>1) { sz += 1 + len(b) return sz, nil } // Unable to compress (RLE/uncompressible) } // write raw values as 4-bits (max : 15) if maxSymbolValue > (256 - 128) { // should not happen : likely means source cannot be compressed return 0, ErrIncompressible } // special case, pack weights 4 bits/weight. sz += 1 + int(maxSymbolValue/2) return sz, nil } // estimateSize returns the estimated size in bytes of the input represented in the // histogram supplied. func (c cTable) estimateSize(hist []uint32) int { nbBits := uint32(7) for i, v := range c[:len(hist)] { nbBits += uint32(v.nBits) * hist[i] } return int(nbBits >> 3) } // minSize returns the minimum possible size considering the shannon limit. func (s *Scratch) minSize(total int) int { nbBits := float64(7) fTotal := float64(total) for _, v := range s.count[:s.symbolLen] { n := float64(v) if n > 0 { nbBits += math.Log2(fTotal/n) * n } } return int(nbBits) >> 3 } func highBit32(val uint32) (n uint32) { return uint32(bits.Len32(val) - 1) } ================================================ FILE: vendor/github.com/klauspost/compress/internal/cpuinfo/cpuinfo.go ================================================ // Package cpuinfo gives runtime info about the current CPU. // // This is a very limited module meant for use internally // in this project. For more versatile solution check // https://github.com/klauspost/cpuid. package cpuinfo // HasBMI1 checks whether an x86 CPU supports the BMI1 extension. func HasBMI1() bool { return hasBMI1 } // HasBMI2 checks whether an x86 CPU supports the BMI2 extension. func HasBMI2() bool { return hasBMI2 } // DisableBMI2 will disable BMI2, for testing purposes. // Call returned function to restore previous state. func DisableBMI2() func() { old := hasBMI2 hasBMI2 = false return func() { hasBMI2 = old } } // HasBMI checks whether an x86 CPU supports both BMI1 and BMI2 extensions. func HasBMI() bool { return HasBMI1() && HasBMI2() } var hasBMI1 bool var hasBMI2 bool ================================================ FILE: vendor/github.com/klauspost/compress/internal/cpuinfo/cpuinfo_amd64.go ================================================ //go:build amd64 && !appengine && !noasm && gc // +build amd64,!appengine,!noasm,gc package cpuinfo // go:noescape func x86extensions() (bmi1, bmi2 bool) func init() { hasBMI1, hasBMI2 = x86extensions() } ================================================ FILE: vendor/github.com/klauspost/compress/internal/cpuinfo/cpuinfo_amd64.s ================================================ // +build !appengine // +build gc // +build !noasm #include "textflag.h" #include "funcdata.h" #include "go_asm.h" TEXT ·x86extensions(SB), NOSPLIT, $0 // 1. determine max EAX value XORQ AX, AX CPUID CMPQ AX, $7 JB unsupported // 2. EAX = 7, ECX = 0 --- see Table 3-8 "Information Returned by CPUID Instruction" MOVQ $7, AX MOVQ $0, CX CPUID BTQ $3, BX // bit 3 = BMI1 SETCS AL BTQ $8, BX // bit 8 = BMI2 SETCS AH MOVB AL, bmi1+0(FP) MOVB AH, bmi2+1(FP) RET unsupported: XORQ AX, AX MOVB AL, bmi1+0(FP) MOVB AL, bmi2+1(FP) RET ================================================ FILE: vendor/github.com/klauspost/compress/internal/snapref/LICENSE ================================================ Copyright (c) 2011 The Snappy-Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/klauspost/compress/internal/snapref/decode.go ================================================ // Copyright 2011 The Snappy-Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package snapref import ( "encoding/binary" "errors" "io" ) var ( // ErrCorrupt reports that the input is invalid. ErrCorrupt = errors.New("snappy: corrupt input") // ErrTooLarge reports that the uncompressed length is too large. ErrTooLarge = errors.New("snappy: decoded block is too large") // ErrUnsupported reports that the input isn't supported. ErrUnsupported = errors.New("snappy: unsupported input") errUnsupportedLiteralLength = errors.New("snappy: unsupported literal length") ) // DecodedLen returns the length of the decoded block. func DecodedLen(src []byte) (int, error) { v, _, err := decodedLen(src) return v, err } // decodedLen returns the length of the decoded block and the number of bytes // that the length header occupied. func decodedLen(src []byte) (blockLen, headerLen int, err error) { v, n := binary.Uvarint(src) if n <= 0 || v > 0xffffffff { return 0, 0, ErrCorrupt } const wordSize = 32 << (^uint(0) >> 32 & 1) if wordSize == 32 && v > 0x7fffffff { return 0, 0, ErrTooLarge } return int(v), n, nil } const ( decodeErrCodeCorrupt = 1 decodeErrCodeUnsupportedLiteralLength = 2 ) // Decode returns the decoded form of src. The returned slice may be a sub- // slice of dst if dst was large enough to hold the entire decoded block. // Otherwise, a newly allocated slice will be returned. // // The dst and src must not overlap. It is valid to pass a nil dst. // // Decode handles the Snappy block format, not the Snappy stream format. func Decode(dst, src []byte) ([]byte, error) { dLen, s, err := decodedLen(src) if err != nil { return nil, err } if dLen <= len(dst) { dst = dst[:dLen] } else { dst = make([]byte, dLen) } switch decode(dst, src[s:]) { case 0: return dst, nil case decodeErrCodeUnsupportedLiteralLength: return nil, errUnsupportedLiteralLength } return nil, ErrCorrupt } // NewReader returns a new Reader that decompresses from r, using the framing // format described at // https://github.com/google/snappy/blob/master/framing_format.txt func NewReader(r io.Reader) *Reader { return &Reader{ r: r, decoded: make([]byte, maxBlockSize), buf: make([]byte, maxEncodedLenOfMaxBlockSize+checksumSize), } } // Reader is an io.Reader that can read Snappy-compressed bytes. // // Reader handles the Snappy stream format, not the Snappy block format. type Reader struct { r io.Reader err error decoded []byte buf []byte // decoded[i:j] contains decoded bytes that have not yet been passed on. i, j int readHeader bool } // Reset discards any buffered data, resets all state, and switches the Snappy // reader to read from r. This permits reusing a Reader rather than allocating // a new one. func (r *Reader) Reset(reader io.Reader) { r.r = reader r.err = nil r.i = 0 r.j = 0 r.readHeader = false } func (r *Reader) readFull(p []byte, allowEOF bool) (ok bool) { if _, r.err = io.ReadFull(r.r, p); r.err != nil { if r.err == io.ErrUnexpectedEOF || (r.err == io.EOF && !allowEOF) { r.err = ErrCorrupt } return false } return true } func (r *Reader) fill() error { for r.i >= r.j { if !r.readFull(r.buf[:4], true) { return r.err } chunkType := r.buf[0] if !r.readHeader { if chunkType != chunkTypeStreamIdentifier { r.err = ErrCorrupt return r.err } r.readHeader = true } chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16 if chunkLen > len(r.buf) { r.err = ErrUnsupported return r.err } // The chunk types are specified at // https://github.com/google/snappy/blob/master/framing_format.txt switch chunkType { case chunkTypeCompressedData: // Section 4.2. Compressed data (chunk type 0x00). if chunkLen < checksumSize { r.err = ErrCorrupt return r.err } buf := r.buf[:chunkLen] if !r.readFull(buf, false) { return r.err } checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 buf = buf[checksumSize:] n, err := DecodedLen(buf) if err != nil { r.err = err return r.err } if n > len(r.decoded) { r.err = ErrCorrupt return r.err } if _, err := Decode(r.decoded, buf); err != nil { r.err = err return r.err } if crc(r.decoded[:n]) != checksum { r.err = ErrCorrupt return r.err } r.i, r.j = 0, n continue case chunkTypeUncompressedData: // Section 4.3. Uncompressed data (chunk type 0x01). if chunkLen < checksumSize { r.err = ErrCorrupt return r.err } buf := r.buf[:checksumSize] if !r.readFull(buf, false) { return r.err } checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 // Read directly into r.decoded instead of via r.buf. n := chunkLen - checksumSize if n > len(r.decoded) { r.err = ErrCorrupt return r.err } if !r.readFull(r.decoded[:n], false) { return r.err } if crc(r.decoded[:n]) != checksum { r.err = ErrCorrupt return r.err } r.i, r.j = 0, n continue case chunkTypeStreamIdentifier: // Section 4.1. Stream identifier (chunk type 0xff). if chunkLen != len(magicBody) { r.err = ErrCorrupt return r.err } if !r.readFull(r.buf[:len(magicBody)], false) { return r.err } for i := 0; i < len(magicBody); i++ { if r.buf[i] != magicBody[i] { r.err = ErrCorrupt return r.err } } continue } if chunkType <= 0x7f { // Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f). r.err = ErrUnsupported return r.err } // Section 4.4 Padding (chunk type 0xfe). // Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd). if !r.readFull(r.buf[:chunkLen], false) { return r.err } } return nil } // Read satisfies the io.Reader interface. func (r *Reader) Read(p []byte) (int, error) { if r.err != nil { return 0, r.err } if err := r.fill(); err != nil { return 0, err } n := copy(p, r.decoded[r.i:r.j]) r.i += n return n, nil } // ReadByte satisfies the io.ByteReader interface. func (r *Reader) ReadByte() (byte, error) { if r.err != nil { return 0, r.err } if err := r.fill(); err != nil { return 0, err } c := r.decoded[r.i] r.i++ return c, nil } ================================================ FILE: vendor/github.com/klauspost/compress/internal/snapref/decode_other.go ================================================ // Copyright 2016 The Snappy-Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package snapref // decode writes the decoding of src to dst. It assumes that the varint-encoded // length of the decompressed bytes has already been read, and that len(dst) // equals that length. // // It returns 0 on success or a decodeErrCodeXxx error code on failure. func decode(dst, src []byte) int { var d, s, offset, length int for s < len(src) { switch src[s] & 0x03 { case tagLiteral: x := uint32(src[s] >> 2) switch { case x < 60: s++ case x == 60: s += 2 if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. return decodeErrCodeCorrupt } x = uint32(src[s-1]) case x == 61: s += 3 if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. return decodeErrCodeCorrupt } x = uint32(src[s-2]) | uint32(src[s-1])<<8 case x == 62: s += 4 if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. return decodeErrCodeCorrupt } x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16 case x == 63: s += 5 if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. return decodeErrCodeCorrupt } x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24 } length = int(x) + 1 if length <= 0 { return decodeErrCodeUnsupportedLiteralLength } if length > len(dst)-d || length > len(src)-s { return decodeErrCodeCorrupt } copy(dst[d:], src[s:s+length]) d += length s += length continue case tagCopy1: s += 2 if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. return decodeErrCodeCorrupt } length = 4 + int(src[s-2])>>2&0x7 offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1])) case tagCopy2: s += 3 if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. return decodeErrCodeCorrupt } length = 1 + int(src[s-3])>>2 offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8) case tagCopy4: s += 5 if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. return decodeErrCodeCorrupt } length = 1 + int(src[s-5])>>2 offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24) } if offset <= 0 || d < offset || length > len(dst)-d { return decodeErrCodeCorrupt } // Copy from an earlier sub-slice of dst to a later sub-slice. // If no overlap, use the built-in copy: if offset >= length { copy(dst[d:d+length], dst[d-offset:]) d += length continue } // Unlike the built-in copy function, this byte-by-byte copy always runs // forwards, even if the slices overlap. Conceptually, this is: // // d += forwardCopy(dst[d:d+length], dst[d-offset:]) // // We align the slices into a and b and show the compiler they are the same size. // This allows the loop to run without bounds checks. a := dst[d : d+length] b := dst[d-offset:] b = b[:len(a)] for i := range a { a[i] = b[i] } d += length } if d != len(dst) { return decodeErrCodeCorrupt } return 0 } ================================================ FILE: vendor/github.com/klauspost/compress/internal/snapref/encode.go ================================================ // Copyright 2011 The Snappy-Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package snapref import ( "encoding/binary" "errors" "io" ) // Encode returns the encoded form of src. The returned slice may be a sub- // slice of dst if dst was large enough to hold the entire encoded block. // Otherwise, a newly allocated slice will be returned. // // The dst and src must not overlap. It is valid to pass a nil dst. // // Encode handles the Snappy block format, not the Snappy stream format. func Encode(dst, src []byte) []byte { if n := MaxEncodedLen(len(src)); n < 0 { panic(ErrTooLarge) } else if len(dst) < n { dst = make([]byte, n) } // The block starts with the varint-encoded length of the decompressed bytes. d := binary.PutUvarint(dst, uint64(len(src))) for len(src) > 0 { p := src src = nil if len(p) > maxBlockSize { p, src = p[:maxBlockSize], p[maxBlockSize:] } if len(p) < minNonLiteralBlockSize { d += emitLiteral(dst[d:], p) } else { d += encodeBlock(dst[d:], p) } } return dst[:d] } // inputMargin is the minimum number of extra input bytes to keep, inside // encodeBlock's inner loop. On some architectures, this margin lets us // implement a fast path for emitLiteral, where the copy of short (<= 16 byte) // literals can be implemented as a single load to and store from a 16-byte // register. That literal's actual length can be as short as 1 byte, so this // can copy up to 15 bytes too much, but that's OK as subsequent iterations of // the encoding loop will fix up the copy overrun, and this inputMargin ensures // that we don't overrun the dst and src buffers. const inputMargin = 16 - 1 // minNonLiteralBlockSize is the minimum size of the input to encodeBlock that // could be encoded with a copy tag. This is the minimum with respect to the // algorithm used by encodeBlock, not a minimum enforced by the file format. // // The encoded output must start with at least a 1 byte literal, as there are // no previous bytes to copy. A minimal (1 byte) copy after that, generated // from an emitCopy call in encodeBlock's main loop, would require at least // another inputMargin bytes, for the reason above: we want any emitLiteral // calls inside encodeBlock's main loop to use the fast path if possible, which // requires being able to overrun by inputMargin bytes. Thus, // minNonLiteralBlockSize equals 1 + 1 + inputMargin. // // The C++ code doesn't use this exact threshold, but it could, as discussed at // https://groups.google.com/d/topic/snappy-compression/oGbhsdIJSJ8/discussion // The difference between Go (2+inputMargin) and C++ (inputMargin) is purely an // optimization. It should not affect the encoded form. This is tested by // TestSameEncodingAsCppShortCopies. const minNonLiteralBlockSize = 1 + 1 + inputMargin // MaxEncodedLen returns the maximum length of a snappy block, given its // uncompressed length. // // It will return a negative value if srcLen is too large to encode. func MaxEncodedLen(srcLen int) int { n := uint64(srcLen) if n > 0xffffffff { return -1 } // Compressed data can be defined as: // compressed := item* literal* // item := literal* copy // // The trailing literal sequence has a space blowup of at most 62/60 // since a literal of length 60 needs one tag byte + one extra byte // for length information. // // Item blowup is trickier to measure. Suppose the "copy" op copies // 4 bytes of data. Because of a special check in the encoding code, // we produce a 4-byte copy only if the offset is < 65536. Therefore // the copy op takes 3 bytes to encode, and this type of item leads // to at most the 62/60 blowup for representing literals. // // Suppose the "copy" op copies 5 bytes of data. If the offset is big // enough, it will take 5 bytes to encode the copy op. Therefore the // worst case here is a one-byte literal followed by a five-byte copy. // That is, 6 bytes of input turn into 7 bytes of "compressed" data. // // This last factor dominates the blowup, so the final estimate is: n = 32 + n + n/6 if n > 0xffffffff { return -1 } return int(n) } var errClosed = errors.New("snappy: Writer is closed") // NewWriter returns a new Writer that compresses to w. // // The Writer returned does not buffer writes. There is no need to Flush or // Close such a Writer. // // Deprecated: the Writer returned is not suitable for many small writes, only // for few large writes. Use NewBufferedWriter instead, which is efficient // regardless of the frequency and shape of the writes, and remember to Close // that Writer when done. func NewWriter(w io.Writer) *Writer { return &Writer{ w: w, obuf: make([]byte, obufLen), } } // NewBufferedWriter returns a new Writer that compresses to w, using the // framing format described at // https://github.com/google/snappy/blob/master/framing_format.txt // // The Writer returned buffers writes. Users must call Close to guarantee all // data has been forwarded to the underlying io.Writer. They may also call // Flush zero or more times before calling Close. func NewBufferedWriter(w io.Writer) *Writer { return &Writer{ w: w, ibuf: make([]byte, 0, maxBlockSize), obuf: make([]byte, obufLen), } } // Writer is an io.Writer that can write Snappy-compressed bytes. // // Writer handles the Snappy stream format, not the Snappy block format. type Writer struct { w io.Writer err error // ibuf is a buffer for the incoming (uncompressed) bytes. // // Its use is optional. For backwards compatibility, Writers created by the // NewWriter function have ibuf == nil, do not buffer incoming bytes, and // therefore do not need to be Flush'ed or Close'd. ibuf []byte // obuf is a buffer for the outgoing (compressed) bytes. obuf []byte // wroteStreamHeader is whether we have written the stream header. wroteStreamHeader bool } // Reset discards the writer's state and switches the Snappy writer to write to // w. This permits reusing a Writer rather than allocating a new one. func (w *Writer) Reset(writer io.Writer) { w.w = writer w.err = nil if w.ibuf != nil { w.ibuf = w.ibuf[:0] } w.wroteStreamHeader = false } // Write satisfies the io.Writer interface. func (w *Writer) Write(p []byte) (nRet int, errRet error) { if w.ibuf == nil { // Do not buffer incoming bytes. This does not perform or compress well // if the caller of Writer.Write writes many small slices. This // behavior is therefore deprecated, but still supported for backwards // compatibility with code that doesn't explicitly Flush or Close. return w.write(p) } // The remainder of this method is based on bufio.Writer.Write from the // standard library. for len(p) > (cap(w.ibuf)-len(w.ibuf)) && w.err == nil { var n int if len(w.ibuf) == 0 { // Large write, empty buffer. // Write directly from p to avoid copy. n, _ = w.write(p) } else { n = copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p) w.ibuf = w.ibuf[:len(w.ibuf)+n] w.Flush() } nRet += n p = p[n:] } if w.err != nil { return nRet, w.err } n := copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p) w.ibuf = w.ibuf[:len(w.ibuf)+n] nRet += n return nRet, nil } func (w *Writer) write(p []byte) (nRet int, errRet error) { if w.err != nil { return 0, w.err } for len(p) > 0 { obufStart := len(magicChunk) if !w.wroteStreamHeader { w.wroteStreamHeader = true copy(w.obuf, magicChunk) obufStart = 0 } var uncompressed []byte if len(p) > maxBlockSize { uncompressed, p = p[:maxBlockSize], p[maxBlockSize:] } else { uncompressed, p = p, nil } checksum := crc(uncompressed) // Compress the buffer, discarding the result if the improvement // isn't at least 12.5%. compressed := Encode(w.obuf[obufHeaderLen:], uncompressed) chunkType := uint8(chunkTypeCompressedData) chunkLen := 4 + len(compressed) obufEnd := obufHeaderLen + len(compressed) if len(compressed) >= len(uncompressed)-len(uncompressed)/8 { chunkType = chunkTypeUncompressedData chunkLen = 4 + len(uncompressed) obufEnd = obufHeaderLen } // Fill in the per-chunk header that comes before the body. w.obuf[len(magicChunk)+0] = chunkType w.obuf[len(magicChunk)+1] = uint8(chunkLen >> 0) w.obuf[len(magicChunk)+2] = uint8(chunkLen >> 8) w.obuf[len(magicChunk)+3] = uint8(chunkLen >> 16) w.obuf[len(magicChunk)+4] = uint8(checksum >> 0) w.obuf[len(magicChunk)+5] = uint8(checksum >> 8) w.obuf[len(magicChunk)+6] = uint8(checksum >> 16) w.obuf[len(magicChunk)+7] = uint8(checksum >> 24) if _, err := w.w.Write(w.obuf[obufStart:obufEnd]); err != nil { w.err = err return nRet, err } if chunkType == chunkTypeUncompressedData { if _, err := w.w.Write(uncompressed); err != nil { w.err = err return nRet, err } } nRet += len(uncompressed) } return nRet, nil } // Flush flushes the Writer to its underlying io.Writer. func (w *Writer) Flush() error { if w.err != nil { return w.err } if len(w.ibuf) == 0 { return nil } w.write(w.ibuf) w.ibuf = w.ibuf[:0] return w.err } // Close calls Flush and then closes the Writer. func (w *Writer) Close() error { w.Flush() ret := w.err if w.err == nil { w.err = errClosed } return ret } ================================================ FILE: vendor/github.com/klauspost/compress/internal/snapref/encode_other.go ================================================ // Copyright 2016 The Snappy-Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package snapref func load32(b []byte, i int) uint32 { b = b[i : i+4 : len(b)] // Help the compiler eliminate bounds checks on the next line. return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 } func load64(b []byte, i int) uint64 { b = b[i : i+8 : len(b)] // Help the compiler eliminate bounds checks on the next line. return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 } // emitLiteral writes a literal chunk and returns the number of bytes written. // // It assumes that: // // dst is long enough to hold the encoded bytes // 1 <= len(lit) && len(lit) <= 65536 func emitLiteral(dst, lit []byte) int { i, n := 0, uint(len(lit)-1) switch { case n < 60: dst[0] = uint8(n)<<2 | tagLiteral i = 1 case n < 1<<8: dst[0] = 60<<2 | tagLiteral dst[1] = uint8(n) i = 2 default: dst[0] = 61<<2 | tagLiteral dst[1] = uint8(n) dst[2] = uint8(n >> 8) i = 3 } return i + copy(dst[i:], lit) } // emitCopy writes a copy chunk and returns the number of bytes written. // // It assumes that: // // dst is long enough to hold the encoded bytes // 1 <= offset && offset <= 65535 // 4 <= length && length <= 65535 func emitCopy(dst []byte, offset, length int) int { i := 0 // The maximum length for a single tagCopy1 or tagCopy2 op is 64 bytes. The // threshold for this loop is a little higher (at 68 = 64 + 4), and the // length emitted down below is is a little lower (at 60 = 64 - 4), because // it's shorter to encode a length 67 copy as a length 60 tagCopy2 followed // by a length 7 tagCopy1 (which encodes as 3+2 bytes) than to encode it as // a length 64 tagCopy2 followed by a length 3 tagCopy2 (which encodes as // 3+3 bytes). The magic 4 in the 64±4 is because the minimum length for a // tagCopy1 op is 4 bytes, which is why a length 3 copy has to be an // encodes-as-3-bytes tagCopy2 instead of an encodes-as-2-bytes tagCopy1. for length >= 68 { // Emit a length 64 copy, encoded as 3 bytes. dst[i+0] = 63<<2 | tagCopy2 dst[i+1] = uint8(offset) dst[i+2] = uint8(offset >> 8) i += 3 length -= 64 } if length > 64 { // Emit a length 60 copy, encoded as 3 bytes. dst[i+0] = 59<<2 | tagCopy2 dst[i+1] = uint8(offset) dst[i+2] = uint8(offset >> 8) i += 3 length -= 60 } if length >= 12 || offset >= 2048 { // Emit the remaining copy, encoded as 3 bytes. dst[i+0] = uint8(length-1)<<2 | tagCopy2 dst[i+1] = uint8(offset) dst[i+2] = uint8(offset >> 8) return i + 3 } // Emit the remaining copy, encoded as 2 bytes. dst[i+0] = uint8(offset>>8)<<5 | uint8(length-4)<<2 | tagCopy1 dst[i+1] = uint8(offset) return i + 2 } func hash(u, shift uint32) uint32 { return (u * 0x1e35a7bd) >> shift } // EncodeBlockInto exposes encodeBlock but checks dst size. func EncodeBlockInto(dst, src []byte) (d int) { if MaxEncodedLen(len(src)) > len(dst) { return 0 } // encodeBlock breaks on too big blocks, so split. for len(src) > 0 { p := src src = nil if len(p) > maxBlockSize { p, src = p[:maxBlockSize], p[maxBlockSize:] } if len(p) < minNonLiteralBlockSize { d += emitLiteral(dst[d:], p) } else { d += encodeBlock(dst[d:], p) } } return d } // encodeBlock encodes a non-empty src to a guaranteed-large-enough dst. It // assumes that the varint-encoded length of the decompressed bytes has already // been written. // // It also assumes that: // // len(dst) >= MaxEncodedLen(len(src)) && // minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize func encodeBlock(dst, src []byte) (d int) { // Initialize the hash table. Its size ranges from 1<<8 to 1<<14 inclusive. // The table element type is uint16, as s < sLimit and sLimit < len(src) // and len(src) <= maxBlockSize and maxBlockSize == 65536. const ( maxTableSize = 1 << 14 // tableMask is redundant, but helps the compiler eliminate bounds // checks. tableMask = maxTableSize - 1 ) shift := uint32(32 - 8) for tableSize := 1 << 8; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 { shift-- } // In Go, all array elements are zero-initialized, so there is no advantage // to a smaller tableSize per se. However, it matches the C++ algorithm, // and in the asm versions of this code, we can get away with zeroing only // the first tableSize elements. var table [maxTableSize]uint16 // sLimit is when to stop looking for offset/length copies. The inputMargin // lets us use a fast path for emitLiteral in the main loop, while we are // looking for copies. sLimit := len(src) - inputMargin // nextEmit is where in src the next emitLiteral should start from. nextEmit := 0 // The encoded form must start with a literal, as there are no previous // bytes to copy, so we start looking for hash matches at s == 1. s := 1 nextHash := hash(load32(src, s), shift) for { // Copied from the C++ snappy implementation: // // Heuristic match skipping: If 32 bytes are scanned with no matches // found, start looking only at every other byte. If 32 more bytes are // scanned (or skipped), look at every third byte, etc.. When a match // is found, immediately go back to looking at every byte. This is a // small loss (~5% performance, ~0.1% density) for compressible data // due to more bookkeeping, but for non-compressible data (such as // JPEG) it's a huge win since the compressor quickly "realizes" the // data is incompressible and doesn't bother looking for matches // everywhere. // // The "skip" variable keeps track of how many bytes there are since // the last match; dividing it by 32 (ie. right-shifting by five) gives // the number of bytes to move ahead for each iteration. skip := 32 nextS := s candidate := 0 for { s = nextS bytesBetweenHashLookups := skip >> 5 nextS = s + bytesBetweenHashLookups skip += bytesBetweenHashLookups if nextS > sLimit { goto emitRemainder } candidate = int(table[nextHash&tableMask]) table[nextHash&tableMask] = uint16(s) nextHash = hash(load32(src, nextS), shift) if load32(src, s) == load32(src, candidate) { break } } // A 4-byte match has been found. We'll later see if more than 4 bytes // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit // them as literal bytes. d += emitLiteral(dst[d:], src[nextEmit:s]) // Call emitCopy, and then see if another emitCopy could be our next // move. Repeat until we find no match for the input immediately after // what was consumed by the last emitCopy call. // // If we exit this loop normally then we need to call emitLiteral next, // though we don't yet know how big the literal will be. We handle that // by proceeding to the next iteration of the main loop. We also can // exit this loop via goto if we get close to exhausting the input. for { // Invariant: we have a 4-byte match at s, and no need to emit any // literal bytes prior to s. base := s // Extend the 4-byte match as long as possible. // // This is an inlined version of: // s = extendMatch(src, candidate+4, s+4) s += 4 for i := candidate + 4; s < len(src) && src[i] == src[s]; i, s = i+1, s+1 { } d += emitCopy(dst[d:], base-candidate, s-base) nextEmit = s if s >= sLimit { goto emitRemainder } // We could immediately start working at s now, but to improve // compression we first update the hash table at s-1 and at s. If // another emitCopy is not our next move, also calculate nextHash // at s+1. At least on GOARCH=amd64, these three hash calculations // are faster as one load64 call (with some shifts) instead of // three load32 calls. x := load64(src, s-1) prevHash := hash(uint32(x>>0), shift) table[prevHash&tableMask] = uint16(s - 1) currHash := hash(uint32(x>>8), shift) candidate = int(table[currHash&tableMask]) table[currHash&tableMask] = uint16(s) if uint32(x>>8) != load32(src, candidate) { nextHash = hash(uint32(x>>16), shift) s++ break } } } emitRemainder: if nextEmit < len(src) { d += emitLiteral(dst[d:], src[nextEmit:]) } return d } ================================================ FILE: vendor/github.com/klauspost/compress/internal/snapref/snappy.go ================================================ // Copyright 2011 The Snappy-Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package snapref implements the Snappy compression format. It aims for very // high speeds and reasonable compression. // // There are actually two Snappy formats: block and stream. They are related, // but different: trying to decompress block-compressed data as a Snappy stream // will fail, and vice versa. The block format is the Decode and Encode // functions and the stream format is the Reader and Writer types. // // The block format, the more common case, is used when the complete size (the // number of bytes) of the original data is known upfront, at the time // compression starts. The stream format, also known as the framing format, is // for when that isn't always true. // // The canonical, C++ implementation is at https://github.com/google/snappy and // it only implements the block format. package snapref import ( "hash/crc32" ) /* Each encoded block begins with the varint-encoded length of the decoded data, followed by a sequence of chunks. Chunks begin and end on byte boundaries. The first byte of each chunk is broken into its 2 least and 6 most significant bits called l and m: l ranges in [0, 4) and m ranges in [0, 64). l is the chunk tag. Zero means a literal tag. All other values mean a copy tag. For literal tags: - If m < 60, the next 1 + m bytes are literal bytes. - Otherwise, let n be the little-endian unsigned integer denoted by the next m - 59 bytes. The next 1 + n bytes after that are literal bytes. For copy tags, length bytes are copied from offset bytes ago, in the style of Lempel-Ziv compression algorithms. In particular: - For l == 1, the offset ranges in [0, 1<<11) and the length in [4, 12). The length is 4 + the low 3 bits of m. The high 3 bits of m form bits 8-10 of the offset. The next byte is bits 0-7 of the offset. - For l == 2, the offset ranges in [0, 1<<16) and the length in [1, 65). The length is 1 + m. The offset is the little-endian unsigned integer denoted by the next 2 bytes. - For l == 3, this tag is a legacy format that is no longer issued by most encoders. Nonetheless, the offset ranges in [0, 1<<32) and the length in [1, 65). The length is 1 + m. The offset is the little-endian unsigned integer denoted by the next 4 bytes. */ const ( tagLiteral = 0x00 tagCopy1 = 0x01 tagCopy2 = 0x02 tagCopy4 = 0x03 ) const ( checksumSize = 4 chunkHeaderSize = 4 magicChunk = "\xff\x06\x00\x00" + magicBody magicBody = "sNaPpY" // maxBlockSize is the maximum size of the input to encodeBlock. It is not // part of the wire format per se, but some parts of the encoder assume // that an offset fits into a uint16. // // Also, for the framing format (Writer type instead of Encode function), // https://github.com/google/snappy/blob/master/framing_format.txt says // that "the uncompressed data in a chunk must be no longer than 65536 // bytes". maxBlockSize = 65536 // maxEncodedLenOfMaxBlockSize equals MaxEncodedLen(maxBlockSize), but is // hard coded to be a const instead of a variable, so that obufLen can also // be a const. Their equivalence is confirmed by // TestMaxEncodedLenOfMaxBlockSize. maxEncodedLenOfMaxBlockSize = 76490 obufHeaderLen = len(magicChunk) + checksumSize + chunkHeaderSize obufLen = obufHeaderLen + maxEncodedLenOfMaxBlockSize ) const ( chunkTypeCompressedData = 0x00 chunkTypeUncompressedData = 0x01 chunkTypePadding = 0xfe chunkTypeStreamIdentifier = 0xff ) var crcTable = crc32.MakeTable(crc32.Castagnoli) // crc implements the checksum specified in section 3 of // https://github.com/google/snappy/blob/master/framing_format.txt func crc(b []byte) uint32 { c := crc32.Update(0, crcTable, b) return uint32(c>>15|c<<17) + 0xa282ead8 } ================================================ FILE: vendor/github.com/klauspost/compress/s2sx.mod ================================================ module github.com/klauspost/compress go 1.16 ================================================ FILE: vendor/github.com/klauspost/compress/s2sx.sum ================================================ ================================================ FILE: vendor/github.com/klauspost/compress/zstd/README.md ================================================ # zstd [Zstandard](https://facebook.github.io/zstd/) is a real-time compression algorithm, providing high compression ratios. It offers a very wide range of compression / speed trade-off, while being backed by a very fast decoder. A high performance compression algorithm is implemented. For now focused on speed. This package provides [compression](#Compressor) to and [decompression](#Decompressor) of Zstandard content. This package is pure Go and without use of "unsafe". The `zstd` package is provided as open source software using a Go standard license. Currently the package is heavily optimized for 64 bit processors and will be significantly slower on 32 bit processors. For seekable zstd streams, see [this excellent package](https://github.com/SaveTheRbtz/zstd-seekable-format-go). ## Installation Install using `go get -u github.com/klauspost/compress`. The package is located in `github.com/klauspost/compress/zstd`. [![Go Reference](https://pkg.go.dev/badge/github.com/klauspost/compress/zstd.svg)](https://pkg.go.dev/github.com/klauspost/compress/zstd) ## Compressor ### Status: STABLE - there may always be subtle bugs, a wide variety of content has been tested and the library is actively used by several projects. This library is being [fuzz-tested](https://github.com/klauspost/compress-fuzz) for all updates. There may still be specific combinations of data types/size/settings that could lead to edge cases, so as always, testing is recommended. For now, a high speed (fastest) and medium-fast (default) compressor has been implemented. * The "Fastest" compression ratio is roughly equivalent to zstd level 1. * The "Default" compression ratio is roughly equivalent to zstd level 3 (default). * The "Better" compression ratio is roughly equivalent to zstd level 7. * The "Best" compression ratio is roughly equivalent to zstd level 11. In terms of speed, it is typically 2x as fast as the stdlib deflate/gzip in its fastest mode. The compression ratio compared to stdlib is around level 3, but usually 3x as fast. ### Usage An Encoder can be used for either compressing a stream via the `io.WriteCloser` interface supported by the Encoder or as multiple independent tasks via the `EncodeAll` function. Smaller encodes are encouraged to use the EncodeAll function. Use `NewWriter` to create a new instance that can be used for both. To create a writer with default options, do like this: ```Go // Compress input to output. func Compress(in io.Reader, out io.Writer) error { enc, err := zstd.NewWriter(out) if err != nil { return err } _, err = io.Copy(enc, in) if err != nil { enc.Close() return err } return enc.Close() } ``` Now you can encode by writing data to `enc`. The output will be finished writing when `Close()` is called. Even if your encode fails, you should still call `Close()` to release any resources that may be held up. The above is fine for big encodes. However, whenever possible try to *reuse* the writer. To reuse the encoder, you can use the `Reset(io.Writer)` function to change to another output. This will allow the encoder to reuse all resources and avoid wasteful allocations. Currently stream encoding has 'light' concurrency, meaning up to 2 goroutines can be working on part of a stream. This is independent of the `WithEncoderConcurrency(n)`, but that is likely to change in the future. So if you want to limit concurrency for future updates, specify the concurrency you would like. If you would like stream encoding to be done without spawning async goroutines, use `WithEncoderConcurrency(1)` which will compress input as each block is completed, blocking on writes until each has completed. You can specify your desired compression level using `WithEncoderLevel()` option. Currently only pre-defined compression settings can be specified. #### Future Compatibility Guarantees This will be an evolving project. When using this package it is important to note that both the compression efficiency and speed may change. The goal will be to keep the default efficiency at the default zstd (level 3). However the encoding should never be assumed to remain the same, and you should not use hashes of compressed output for similarity checks. The Encoder can be assumed to produce the same output from the exact same code version. However, the may be modes in the future that break this, although they will not be enabled without an explicit option. This encoder is not designed to (and will probably never) output the exact same bitstream as the reference encoder. Also note, that the cgo decompressor currently does not [report all errors on invalid input](https://github.com/DataDog/zstd/issues/59), [omits error checks](https://github.com/DataDog/zstd/issues/61), [ignores checksums](https://github.com/DataDog/zstd/issues/43) and seems to ignore concatenated streams, even though [it is part of the spec](https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#frames). #### Blocks For compressing small blocks, the returned encoder has a function called `EncodeAll(src, dst []byte) []byte`. `EncodeAll` will encode all input in src and append it to dst. This function can be called concurrently. Each call will only run on a same goroutine as the caller. Encoded blocks can be concatenated and the result will be the combined input stream. Data compressed with EncodeAll can be decoded with the Decoder, using either a stream or `DecodeAll`. Especially when encoding blocks you should take special care to reuse the encoder. This will effectively make it run without allocations after a warmup period. To make it run completely without allocations, supply a destination buffer with space for all content. ```Go import "github.com/klauspost/compress/zstd" // Create a writer that caches compressors. // For this operation type we supply a nil Reader. var encoder, _ = zstd.NewWriter(nil) // Compress a buffer. // If you have a destination buffer, the allocation in the call can also be eliminated. func Compress(src []byte) []byte { return encoder.EncodeAll(src, make([]byte, 0, len(src))) } ``` You can control the maximum number of concurrent encodes using the `WithEncoderConcurrency(n)` option when creating the writer. Using the Encoder for both a stream and individual blocks concurrently is safe. ### Performance I have collected some speed examples to compare speed and compression against other compressors. * `file` is the input file. * `out` is the compressor used. `zskp` is this package. `zstd` is the Datadog cgo library. `gzstd/gzkp` is gzip standard and this library. * `level` is the compression level used. For `zskp` level 1 is "fastest", level 2 is "default"; 3 is "better", 4 is "best". * `insize`/`outsize` is the input/output size. * `millis` is the number of milliseconds used for compression. * `mb/s` is megabytes (2^20 bytes) per second. ``` Silesia Corpus: http://sun.aei.polsl.pl/~sdeor/corpus/silesia.zip This package: file out level insize outsize millis mb/s silesia.tar zskp 1 211947520 73821326 634 318.47 silesia.tar zskp 2 211947520 67655404 1508 133.96 silesia.tar zskp 3 211947520 64746933 3000 67.37 silesia.tar zskp 4 211947520 60073508 16926 11.94 cgo zstd: silesia.tar zstd 1 211947520 73605392 543 371.56 silesia.tar zstd 3 211947520 66793289 864 233.68 silesia.tar zstd 6 211947520 62916450 1913 105.66 silesia.tar zstd 9 211947520 60212393 5063 39.92 gzip, stdlib/this package: silesia.tar gzstd 1 211947520 80007735 1498 134.87 silesia.tar gzkp 1 211947520 80088272 1009 200.31 GOB stream of binary data. Highly compressible. https://files.klauspost.com/compress/gob-stream.7z file out level insize outsize millis mb/s gob-stream zskp 1 1911399616 233948096 3230 564.34 gob-stream zskp 2 1911399616 203997694 4997 364.73 gob-stream zskp 3 1911399616 173526523 13435 135.68 gob-stream zskp 4 1911399616 162195235 47559 38.33 gob-stream zstd 1 1911399616 249810424 2637 691.26 gob-stream zstd 3 1911399616 208192146 3490 522.31 gob-stream zstd 6 1911399616 193632038 6687 272.56 gob-stream zstd 9 1911399616 177620386 16175 112.70 gob-stream gzstd 1 1911399616 357382013 9046 201.49 gob-stream gzkp 1 1911399616 359136669 4885 373.08 The test data for the Large Text Compression Benchmark is the first 10^9 bytes of the English Wikipedia dump on Mar. 3, 2006. http://mattmahoney.net/dc/textdata.html file out level insize outsize millis mb/s enwik9 zskp 1 1000000000 343833605 3687 258.64 enwik9 zskp 2 1000000000 317001237 7672 124.29 enwik9 zskp 3 1000000000 291915823 15923 59.89 enwik9 zskp 4 1000000000 261710291 77697 12.27 enwik9 zstd 1 1000000000 358072021 3110 306.65 enwik9 zstd 3 1000000000 313734672 4784 199.35 enwik9 zstd 6 1000000000 295138875 10290 92.68 enwik9 zstd 9 1000000000 278348700 28549 33.40 enwik9 gzstd 1 1000000000 382578136 8608 110.78 enwik9 gzkp 1 1000000000 382781160 5628 169.45 Highly compressible JSON file. https://files.klauspost.com/compress/github-june-2days-2019.json.zst file out level insize outsize millis mb/s github-june-2days-2019.json zskp 1 6273951764 697439532 9789 611.17 github-june-2days-2019.json zskp 2 6273951764 610876538 18553 322.49 github-june-2days-2019.json zskp 3 6273951764 517662858 44186 135.41 github-june-2days-2019.json zskp 4 6273951764 464617114 165373 36.18 github-june-2days-2019.json zstd 1 6273951764 766284037 8450 708.00 github-june-2days-2019.json zstd 3 6273951764 661889476 10927 547.57 github-june-2days-2019.json zstd 6 6273951764 642756859 22996 260.18 github-june-2days-2019.json zstd 9 6273951764 601974523 52413 114.16 github-june-2days-2019.json gzstd 1 6273951764 1164397768 26793 223.32 github-june-2days-2019.json gzkp 1 6273951764 1120631856 17693 338.16 VM Image, Linux mint with a few installed applications: https://files.klauspost.com/compress/rawstudio-mint14.7z file out level insize outsize millis mb/s rawstudio-mint14.tar zskp 1 8558382592 3718400221 18206 448.29 rawstudio-mint14.tar zskp 2 8558382592 3326118337 37074 220.15 rawstudio-mint14.tar zskp 3 8558382592 3163842361 87306 93.49 rawstudio-mint14.tar zskp 4 8558382592 2970480650 783862 10.41 rawstudio-mint14.tar zstd 1 8558382592 3609250104 17136 476.27 rawstudio-mint14.tar zstd 3 8558382592 3341679997 29262 278.92 rawstudio-mint14.tar zstd 6 8558382592 3235846406 77904 104.77 rawstudio-mint14.tar zstd 9 8558382592 3160778861 140946 57.91 rawstudio-mint14.tar gzstd 1 8558382592 3926234992 51345 158.96 rawstudio-mint14.tar gzkp 1 8558382592 3960117298 36722 222.26 CSV data: https://files.klauspost.com/compress/nyc-taxi-data-10M.csv.zst file out level insize outsize millis mb/s nyc-taxi-data-10M.csv zskp 1 3325605752 641319332 9462 335.17 nyc-taxi-data-10M.csv zskp 2 3325605752 588976126 17570 180.50 nyc-taxi-data-10M.csv zskp 3 3325605752 529329260 32432 97.79 nyc-taxi-data-10M.csv zskp 4 3325605752 474949772 138025 22.98 nyc-taxi-data-10M.csv zstd 1 3325605752 687399637 8233 385.18 nyc-taxi-data-10M.csv zstd 3 3325605752 598514411 10065 315.07 nyc-taxi-data-10M.csv zstd 6 3325605752 570522953 20038 158.27 nyc-taxi-data-10M.csv zstd 9 3325605752 517554797 64565 49.12 nyc-taxi-data-10M.csv gzstd 1 3325605752 928654908 21270 149.11 nyc-taxi-data-10M.csv gzkp 1 3325605752 922273214 13929 227.68 ``` ## Decompressor Staus: STABLE - there may still be subtle bugs, but a wide variety of content has been tested. This library is being continuously [fuzz-tested](https://github.com/klauspost/compress-fuzz), kindly supplied by [fuzzit.dev](https://fuzzit.dev/). The main purpose of the fuzz testing is to ensure that it is not possible to crash the decoder, or run it past its limits with ANY input provided. ### Usage The package has been designed for two main usages, big streams of data and smaller in-memory buffers. There are two main usages of the package for these. Both of them are accessed by creating a `Decoder`. For streaming use a simple setup could look like this: ```Go import "github.com/klauspost/compress/zstd" func Decompress(in io.Reader, out io.Writer) error { d, err := zstd.NewReader(in) if err != nil { return err } defer d.Close() // Copy content... _, err = io.Copy(out, d) return err } ``` It is important to use the "Close" function when you no longer need the Reader to stop running goroutines, when running with default settings. Goroutines will exit once an error has been returned, including `io.EOF` at the end of a stream. Streams are decoded concurrently in 4 asynchronous stages to give the best possible throughput. However, if you prefer synchronous decompression, use `WithDecoderConcurrency(1)` which will decompress data as it is being requested only. For decoding buffers, it could look something like this: ```Go import "github.com/klauspost/compress/zstd" // Create a reader that caches decompressors. // For this operation type we supply a nil Reader. var decoder, _ = zstd.NewReader(nil, zstd.WithDecoderConcurrency(0)) // Decompress a buffer. We don't supply a destination buffer, // so it will be allocated by the decoder. func Decompress(src []byte) ([]byte, error) { return decoder.DecodeAll(src, nil) } ``` Both of these cases should provide the functionality needed. The decoder can be used for *concurrent* decompression of multiple buffers. By default 4 decompressors will be created. It will only allow a certain number of concurrent operations to run. To tweak that yourself use the `WithDecoderConcurrency(n)` option when creating the decoder. It is possible to use `WithDecoderConcurrency(0)` to create GOMAXPROCS decoders. ### Dictionaries Data compressed with [dictionaries](https://github.com/facebook/zstd#the-case-for-small-data-compression) can be decompressed. Dictionaries are added individually to Decoders. Dictionaries are generated by the `zstd --train` command and contains an initial state for the decoder. To add a dictionary use the `WithDecoderDicts(dicts ...[]byte)` option with the dictionary data. Several dictionaries can be added at once. The dictionary will be used automatically for the data that specifies them. A re-used Decoder will still contain the dictionaries registered. When registering multiple dictionaries with the same ID, the last one will be used. It is possible to use dictionaries when compressing data. To enable a dictionary use `WithEncoderDict(dict []byte)`. Here only one dictionary will be used and it will likely be used even if it doesn't improve compression. The used dictionary must be used to decompress the content. For any real gains, the dictionary should be built with similar data. If an unsuitable dictionary is used the output may be slightly larger than using no dictionary. Use the [zstd commandline tool](https://github.com/facebook/zstd/releases) to build a dictionary from sample data. For information see [zstd dictionary information](https://github.com/facebook/zstd#the-case-for-small-data-compression). For now there is a fixed startup performance penalty for compressing content with dictionaries. This will likely be improved over time. Just be aware to test performance when implementing. ### Allocation-less operation The decoder has been designed to operate without allocations after a warmup. This means that you should *store* the decoder for best performance. To re-use a stream decoder, use the `Reset(r io.Reader) error` to switch to another stream. A decoder can safely be re-used even if the previous stream failed. To release the resources, you must call the `Close()` function on a decoder. After this it can *no longer be reused*, but all running goroutines will be stopped. So you *must* use this if you will no longer need the Reader. For decompressing smaller buffers a single decoder can be used. When decoding buffers, you can supply a destination slice with length 0 and your expected capacity. In this case no unneeded allocations should be made. ### Concurrency The buffer decoder does everything on the same goroutine and does nothing concurrently. It can however decode several buffers concurrently. Use `WithDecoderConcurrency(n)` to limit that. The stream decoder will create goroutines that: 1) Reads input and splits the input into blocks. 2) Decompression of literals. 3) Decompression of sequences. 4) Reconstruction of output stream. So effectively this also means the decoder will "read ahead" and prepare data to always be available for output. The concurrency level will, for streams, determine how many blocks ahead the compression will start. Since "blocks" are quite dependent on the output of the previous block stream decoding will only have limited concurrency. In practice this means that concurrency is often limited to utilizing about 3 cores effectively. ### Benchmarks The first two are streaming decodes and the last are smaller inputs. Running on AMD Ryzen 9 3950X 16-Core Processor. AMD64 assembly used. ``` BenchmarkDecoderSilesia-32 5 206878840 ns/op 1024.50 MB/s 49808 B/op 43 allocs/op BenchmarkDecoderEnwik9-32 1 1271809000 ns/op 786.28 MB/s 72048 B/op 52 allocs/op Concurrent blocks, performance: BenchmarkDecoder_DecodeAllParallel/kppkn.gtb.zst-32 67356 17857 ns/op 10321.96 MB/s 22.48 pct 102 B/op 0 allocs/op BenchmarkDecoder_DecodeAllParallel/geo.protodata.zst-32 266656 4421 ns/op 26823.21 MB/s 11.89 pct 19 B/op 0 allocs/op BenchmarkDecoder_DecodeAllParallel/plrabn12.txt.zst-32 20992 56842 ns/op 8477.17 MB/s 39.90 pct 754 B/op 0 allocs/op BenchmarkDecoder_DecodeAllParallel/lcet10.txt.zst-32 27456 43932 ns/op 9714.01 MB/s 33.27 pct 524 B/op 0 allocs/op BenchmarkDecoder_DecodeAllParallel/asyoulik.txt.zst-32 78432 15047 ns/op 8319.15 MB/s 40.34 pct 66 B/op 0 allocs/op BenchmarkDecoder_DecodeAllParallel/alice29.txt.zst-32 65800 18436 ns/op 8249.63 MB/s 37.75 pct 88 B/op 0 allocs/op BenchmarkDecoder_DecodeAllParallel/html_x_4.zst-32 102993 11523 ns/op 35546.09 MB/s 3.637 pct 143 B/op 0 allocs/op BenchmarkDecoder_DecodeAllParallel/paper-100k.pdf.zst-32 1000000 1070 ns/op 95720.98 MB/s 80.53 pct 3 B/op 0 allocs/op BenchmarkDecoder_DecodeAllParallel/fireworks.jpeg.zst-32 749802 1752 ns/op 70272.35 MB/s 100.0 pct 5 B/op 0 allocs/op BenchmarkDecoder_DecodeAllParallel/urls.10K.zst-32 22640 52934 ns/op 13263.37 MB/s 26.25 pct 1014 B/op 0 allocs/op BenchmarkDecoder_DecodeAllParallel/html.zst-32 226412 5232 ns/op 19572.27 MB/s 14.49 pct 20 B/op 0 allocs/op BenchmarkDecoder_DecodeAllParallel/comp-data.bin.zst-32 923041 1276 ns/op 3194.71 MB/s 31.26 pct 0 B/op 0 allocs/op ``` This reflects the performance around May 2022, but this may be out of date. ## Zstd inside ZIP files It is possible to use zstandard to compress individual files inside zip archives. While this isn't widely supported it can be useful for internal files. To support the compression and decompression of these files you must register a compressor and decompressor. It is highly recommended registering the (de)compressors on individual zip Reader/Writer and NOT use the global registration functions. The main reason for this is that 2 registrations from different packages will result in a panic. It is a good idea to only have a single compressor and decompressor, since they can be used for multiple zip files concurrently, and using a single instance will allow reusing some resources. See [this example](https://pkg.go.dev/github.com/klauspost/compress/zstd#example-ZipCompressor) for how to compress and decompress files inside zip archives. # Contributions Contributions are always welcome. For new features/fixes, remember to add tests and for performance enhancements include benchmarks. For general feedback and experience reports, feel free to open an issue or write me on [Twitter](https://twitter.com/sh0dan). This package includes the excellent [`github.com/cespare/xxhash`](https://github.com/cespare/xxhash) package Copyright (c) 2016 Caleb Spare. ================================================ FILE: vendor/github.com/klauspost/compress/zstd/bitreader.go ================================================ // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. // Based on work by Yann Collet, released under BSD License. package zstd import ( "encoding/binary" "errors" "fmt" "io" "math/bits" ) // bitReader reads a bitstream in reverse. // The last set bit indicates the start of the stream and is used // for aligning the input. type bitReader struct { in []byte value uint64 // Maybe use [16]byte, but shifting is awkward. bitsRead uint8 } // init initializes and resets the bit reader. func (b *bitReader) init(in []byte) error { if len(in) < 1 { return errors.New("corrupt stream: too short") } b.in = in // The highest bit of the last byte indicates where to start v := in[len(in)-1] if v == 0 { return errors.New("corrupt stream, did not find end of stream") } b.bitsRead = 64 b.value = 0 if len(in) >= 8 { b.fillFastStart() } else { b.fill() b.fill() } b.bitsRead += 8 - uint8(highBits(uint32(v))) return nil } // getBits will return n bits. n can be 0. func (b *bitReader) getBits(n uint8) int { if n == 0 /*|| b.bitsRead >= 64 */ { return 0 } return int(b.get32BitsFast(n)) } // get32BitsFast requires that at least one bit is requested every time. // There are no checks if the buffer is filled. func (b *bitReader) get32BitsFast(n uint8) uint32 { const regMask = 64 - 1 v := uint32((b.value << (b.bitsRead & regMask)) >> ((regMask + 1 - n) & regMask)) b.bitsRead += n return v } // fillFast() will make sure at least 32 bits are available. // There must be at least 4 bytes available. func (b *bitReader) fillFast() { if b.bitsRead < 32 { return } v := b.in[len(b.in)-4:] b.in = b.in[:len(b.in)-4] low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24) b.value = (b.value << 32) | uint64(low) b.bitsRead -= 32 } // fillFastStart() assumes the bitreader is empty and there is at least 8 bytes to read. func (b *bitReader) fillFastStart() { v := b.in[len(b.in)-8:] b.in = b.in[:len(b.in)-8] b.value = binary.LittleEndian.Uint64(v) b.bitsRead = 0 } // fill() will make sure at least 32 bits are available. func (b *bitReader) fill() { if b.bitsRead < 32 { return } if len(b.in) >= 4 { v := b.in[len(b.in)-4:] b.in = b.in[:len(b.in)-4] low := (uint32(v[0])) | (uint32(v[1]) << 8) | (uint32(v[2]) << 16) | (uint32(v[3]) << 24) b.value = (b.value << 32) | uint64(low) b.bitsRead -= 32 return } b.bitsRead -= uint8(8 * len(b.in)) for len(b.in) > 0 { b.value = (b.value << 8) | uint64(b.in[len(b.in)-1]) b.in = b.in[:len(b.in)-1] } } // finished returns true if all bits have been read from the bit stream. func (b *bitReader) finished() bool { return len(b.in) == 0 && b.bitsRead >= 64 } // overread returns true if more bits have been requested than is on the stream. func (b *bitReader) overread() bool { return b.bitsRead > 64 } // remain returns the number of bits remaining. func (b *bitReader) remain() uint { return 8*uint(len(b.in)) + 64 - uint(b.bitsRead) } // close the bitstream and returns an error if out-of-buffer reads occurred. func (b *bitReader) close() error { // Release reference. b.in = nil if !b.finished() { return fmt.Errorf("%d extra bits on block, should be 0", b.remain()) } if b.bitsRead > 64 { return io.ErrUnexpectedEOF } return nil } func highBits(val uint32) (n uint32) { return uint32(bits.Len32(val) - 1) } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/bitwriter.go ================================================ // Copyright 2018 Klaus Post. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Based on work Copyright (c) 2013, Yann Collet, released under BSD License. package zstd // bitWriter will write bits. // First bit will be LSB of the first byte of output. type bitWriter struct { bitContainer uint64 nBits uint8 out []byte } // bitMask16 is bitmasks. Has extra to avoid bounds check. var bitMask16 = [32]uint16{ 0, 1, 3, 7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF} /* up to 16 bits */ var bitMask32 = [32]uint32{ 0, 1, 3, 7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0x1ffff, 0x3ffff, 0x7FFFF, 0xfFFFF, 0x1fFFFF, 0x3fFFFF, 0x7fFFFF, 0xffFFFF, 0x1ffFFFF, 0x3ffFFFF, 0x7ffFFFF, 0xfffFFFF, 0x1fffFFFF, 0x3fffFFFF, 0x7fffFFFF, } // up to 32 bits // addBits16NC will add up to 16 bits. // It will not check if there is space for them, // so the caller must ensure that it has flushed recently. func (b *bitWriter) addBits16NC(value uint16, bits uint8) { b.bitContainer |= uint64(value&bitMask16[bits&31]) << (b.nBits & 63) b.nBits += bits } // addBits32NC will add up to 31 bits. // It will not check if there is space for them, // so the caller must ensure that it has flushed recently. func (b *bitWriter) addBits32NC(value uint32, bits uint8) { b.bitContainer |= uint64(value&bitMask32[bits&31]) << (b.nBits & 63) b.nBits += bits } // addBits64NC will add up to 64 bits. // There must be space for 32 bits. func (b *bitWriter) addBits64NC(value uint64, bits uint8) { if bits <= 31 { b.addBits32Clean(uint32(value), bits) return } b.addBits32Clean(uint32(value), 32) b.flush32() b.addBits32Clean(uint32(value>>32), bits-32) } // addBits32Clean will add up to 32 bits. // It will not check if there is space for them. // The input must not contain more bits than specified. func (b *bitWriter) addBits32Clean(value uint32, bits uint8) { b.bitContainer |= uint64(value) << (b.nBits & 63) b.nBits += bits } // addBits16Clean will add up to 16 bits. value may not contain more set bits than indicated. // It will not check if there is space for them, so the caller must ensure that it has flushed recently. func (b *bitWriter) addBits16Clean(value uint16, bits uint8) { b.bitContainer |= uint64(value) << (b.nBits & 63) b.nBits += bits } // flush32 will flush out, so there are at least 32 bits available for writing. func (b *bitWriter) flush32() { if b.nBits < 32 { return } b.out = append(b.out, byte(b.bitContainer), byte(b.bitContainer>>8), byte(b.bitContainer>>16), byte(b.bitContainer>>24)) b.nBits -= 32 b.bitContainer >>= 32 } // flushAlign will flush remaining full bytes and align to next byte boundary. func (b *bitWriter) flushAlign() { nbBytes := (b.nBits + 7) >> 3 for i := uint8(0); i < nbBytes; i++ { b.out = append(b.out, byte(b.bitContainer>>(i*8))) } b.nBits = 0 b.bitContainer = 0 } // close will write the alignment bit and write the final byte(s) // to the output. func (b *bitWriter) close() { // End mark b.addBits16Clean(1, 1) // flush until next byte. b.flushAlign() } // reset and continue writing by appending to out. func (b *bitWriter) reset(out []byte) { b.bitContainer = 0 b.nBits = 0 b.out = out } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/blockdec.go ================================================ // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. // Based on work by Yann Collet, released under BSD License. package zstd import ( "bytes" "encoding/binary" "errors" "fmt" "hash/crc32" "io" "os" "path/filepath" "sync" "github.com/klauspost/compress/huff0" "github.com/klauspost/compress/zstd/internal/xxhash" ) type blockType uint8 //go:generate stringer -type=blockType,literalsBlockType,seqCompMode,tableIndex const ( blockTypeRaw blockType = iota blockTypeRLE blockTypeCompressed blockTypeReserved ) type literalsBlockType uint8 const ( literalsBlockRaw literalsBlockType = iota literalsBlockRLE literalsBlockCompressed literalsBlockTreeless ) const ( // maxCompressedBlockSize is the biggest allowed compressed block size (128KB) maxCompressedBlockSize = 128 << 10 compressedBlockOverAlloc = 16 maxCompressedBlockSizeAlloc = 128<<10 + compressedBlockOverAlloc // Maximum possible block size (all Raw+Uncompressed). maxBlockSize = (1 << 21) - 1 maxMatchLen = 131074 maxSequences = 0x7f00 + 0xffff // We support slightly less than the reference decoder to be able to // use ints on 32 bit archs. maxOffsetBits = 30 ) var ( huffDecoderPool = sync.Pool{New: func() interface{} { return &huff0.Scratch{} }} fseDecoderPool = sync.Pool{New: func() interface{} { return &fseDecoder{} }} ) type blockDec struct { // Raw source data of the block. data []byte dataStorage []byte // Destination of the decoded data. dst []byte // Buffer for literals data. literalBuf []byte // Window size of the block. WindowSize uint64 err error // Check against this crc, if hasCRC is true. checkCRC uint32 hasCRC bool // Frame to use for singlethreaded decoding. // Should not be used by the decoder itself since parent may be another frame. localFrame *frameDec sequence []seqVals async struct { newHist *history literals []byte seqData []byte seqSize int // Size of uncompressed sequences fcs uint64 } // Block is RLE, this is the size. RLESize uint32 Type blockType // Is this the last block of a frame? Last bool // Use less memory lowMem bool } func (b *blockDec) String() string { if b == nil { return "" } return fmt.Sprintf("Steam Size: %d, Type: %v, Last: %t, Window: %d", len(b.data), b.Type, b.Last, b.WindowSize) } func newBlockDec(lowMem bool) *blockDec { b := blockDec{ lowMem: lowMem, } return &b } // reset will reset the block. // Input must be a start of a block and will be at the end of the block when returned. func (b *blockDec) reset(br byteBuffer, windowSize uint64) error { b.WindowSize = windowSize tmp, err := br.readSmall(3) if err != nil { println("Reading block header:", err) return err } bh := uint32(tmp[0]) | (uint32(tmp[1]) << 8) | (uint32(tmp[2]) << 16) b.Last = bh&1 != 0 b.Type = blockType((bh >> 1) & 3) // find size. cSize := int(bh >> 3) maxSize := maxCompressedBlockSizeAlloc switch b.Type { case blockTypeReserved: return ErrReservedBlockType case blockTypeRLE: if cSize > maxCompressedBlockSize || cSize > int(b.WindowSize) { if debugDecoder { printf("rle block too big: csize:%d block: %+v\n", uint64(cSize), b) } return ErrWindowSizeExceeded } b.RLESize = uint32(cSize) if b.lowMem { maxSize = cSize } cSize = 1 case blockTypeCompressed: if debugDecoder { println("Data size on stream:", cSize) } b.RLESize = 0 maxSize = maxCompressedBlockSizeAlloc if windowSize < maxCompressedBlockSize && b.lowMem { maxSize = int(windowSize) + compressedBlockOverAlloc } if cSize > maxCompressedBlockSize || uint64(cSize) > b.WindowSize { if debugDecoder { printf("compressed block too big: csize:%d block: %+v\n", uint64(cSize), b) } return ErrCompressedSizeTooBig } // Empty compressed blocks must at least be 2 bytes // for Literals_Block_Type and one for Sequences_Section_Header. if cSize < 2 { return ErrBlockTooSmall } case blockTypeRaw: if cSize > maxCompressedBlockSize || cSize > int(b.WindowSize) { if debugDecoder { printf("rle block too big: csize:%d block: %+v\n", uint64(cSize), b) } return ErrWindowSizeExceeded } b.RLESize = 0 // We do not need a destination for raw blocks. maxSize = -1 default: panic("Invalid block type") } // Read block data. if _, ok := br.(*byteBuf); !ok && cap(b.dataStorage) < cSize { // byteBuf doesn't need a destination buffer. if b.lowMem || cSize > maxCompressedBlockSize { b.dataStorage = make([]byte, 0, cSize+compressedBlockOverAlloc) } else { b.dataStorage = make([]byte, 0, maxCompressedBlockSizeAlloc) } } b.data, err = br.readBig(cSize, b.dataStorage) if err != nil { if debugDecoder { println("Reading block:", err, "(", cSize, ")", len(b.data)) printf("%T", br) } return err } if cap(b.dst) <= maxSize { b.dst = make([]byte, 0, maxSize+1) } return nil } // sendEOF will make the decoder send EOF on this frame. func (b *blockDec) sendErr(err error) { b.Last = true b.Type = blockTypeReserved b.err = err } // Close will release resources. // Closed blockDec cannot be reset. func (b *blockDec) Close() { } // decodeBuf func (b *blockDec) decodeBuf(hist *history) error { switch b.Type { case blockTypeRLE: if cap(b.dst) < int(b.RLESize) { if b.lowMem { b.dst = make([]byte, b.RLESize) } else { b.dst = make([]byte, maxCompressedBlockSize) } } b.dst = b.dst[:b.RLESize] v := b.data[0] for i := range b.dst { b.dst[i] = v } hist.appendKeep(b.dst) return nil case blockTypeRaw: hist.appendKeep(b.data) return nil case blockTypeCompressed: saved := b.dst // Append directly to history if hist.ignoreBuffer == 0 { b.dst = hist.b hist.b = nil } else { b.dst = b.dst[:0] } err := b.decodeCompressed(hist) if debugDecoder { println("Decompressed to total", len(b.dst), "bytes, hash:", xxhash.Sum64(b.dst), "error:", err) } if hist.ignoreBuffer == 0 { hist.b = b.dst b.dst = saved } else { hist.appendKeep(b.dst) } return err case blockTypeReserved: // Used for returning errors. return b.err default: panic("Invalid block type") } } func (b *blockDec) decodeLiterals(in []byte, hist *history) (remain []byte, err error) { // There must be at least one byte for Literals_Block_Type and one for Sequences_Section_Header if len(in) < 2 { return in, ErrBlockTooSmall } litType := literalsBlockType(in[0] & 3) var litRegenSize int var litCompSize int sizeFormat := (in[0] >> 2) & 3 var fourStreams bool var literals []byte switch litType { case literalsBlockRaw, literalsBlockRLE: switch sizeFormat { case 0, 2: // Regenerated_Size uses 5 bits (0-31). Literals_Section_Header uses 1 byte. litRegenSize = int(in[0] >> 3) in = in[1:] case 1: // Regenerated_Size uses 12 bits (0-4095). Literals_Section_Header uses 2 bytes. litRegenSize = int(in[0]>>4) + (int(in[1]) << 4) in = in[2:] case 3: // Regenerated_Size uses 20 bits (0-1048575). Literals_Section_Header uses 3 bytes. if len(in) < 3 { println("too small: litType:", litType, " sizeFormat", sizeFormat, len(in)) return in, ErrBlockTooSmall } litRegenSize = int(in[0]>>4) + (int(in[1]) << 4) + (int(in[2]) << 12) in = in[3:] } case literalsBlockCompressed, literalsBlockTreeless: switch sizeFormat { case 0, 1: // Both Regenerated_Size and Compressed_Size use 10 bits (0-1023). if len(in) < 3 { println("too small: litType:", litType, " sizeFormat", sizeFormat, len(in)) return in, ErrBlockTooSmall } n := uint64(in[0]>>4) + (uint64(in[1]) << 4) + (uint64(in[2]) << 12) litRegenSize = int(n & 1023) litCompSize = int(n >> 10) fourStreams = sizeFormat == 1 in = in[3:] case 2: fourStreams = true if len(in) < 4 { println("too small: litType:", litType, " sizeFormat", sizeFormat, len(in)) return in, ErrBlockTooSmall } n := uint64(in[0]>>4) + (uint64(in[1]) << 4) + (uint64(in[2]) << 12) + (uint64(in[3]) << 20) litRegenSize = int(n & 16383) litCompSize = int(n >> 14) in = in[4:] case 3: fourStreams = true if len(in) < 5 { println("too small: litType:", litType, " sizeFormat", sizeFormat, len(in)) return in, ErrBlockTooSmall } n := uint64(in[0]>>4) + (uint64(in[1]) << 4) + (uint64(in[2]) << 12) + (uint64(in[3]) << 20) + (uint64(in[4]) << 28) litRegenSize = int(n & 262143) litCompSize = int(n >> 18) in = in[5:] } } if debugDecoder { println("literals type:", litType, "litRegenSize:", litRegenSize, "litCompSize:", litCompSize, "sizeFormat:", sizeFormat, "4X:", fourStreams) } if litRegenSize > int(b.WindowSize) || litRegenSize > maxCompressedBlockSize { return in, ErrWindowSizeExceeded } switch litType { case literalsBlockRaw: if len(in) < litRegenSize { println("too small: litType:", litType, " sizeFormat", sizeFormat, "remain:", len(in), "want:", litRegenSize) return in, ErrBlockTooSmall } literals = in[:litRegenSize] in = in[litRegenSize:] //printf("Found %d uncompressed literals\n", litRegenSize) case literalsBlockRLE: if len(in) < 1 { println("too small: litType:", litType, " sizeFormat", sizeFormat, "remain:", len(in), "want:", 1) return in, ErrBlockTooSmall } if cap(b.literalBuf) < litRegenSize { if b.lowMem { b.literalBuf = make([]byte, litRegenSize, litRegenSize+compressedBlockOverAlloc) } else { b.literalBuf = make([]byte, litRegenSize, maxCompressedBlockSize+compressedBlockOverAlloc) } } literals = b.literalBuf[:litRegenSize] v := in[0] for i := range literals { literals[i] = v } in = in[1:] if debugDecoder { printf("Found %d RLE compressed literals\n", litRegenSize) } case literalsBlockTreeless: if len(in) < litCompSize { println("too small: litType:", litType, " sizeFormat", sizeFormat, "remain:", len(in), "want:", litCompSize) return in, ErrBlockTooSmall } // Store compressed literals, so we defer decoding until we get history. literals = in[:litCompSize] in = in[litCompSize:] if debugDecoder { printf("Found %d compressed literals\n", litCompSize) } huff := hist.huffTree if huff == nil { return in, errors.New("literal block was treeless, but no history was defined") } // Ensure we have space to store it. if cap(b.literalBuf) < litRegenSize { if b.lowMem { b.literalBuf = make([]byte, 0, litRegenSize+compressedBlockOverAlloc) } else { b.literalBuf = make([]byte, 0, maxCompressedBlockSize+compressedBlockOverAlloc) } } var err error // Use our out buffer. huff.MaxDecodedSize = litRegenSize if fourStreams { literals, err = huff.Decoder().Decompress4X(b.literalBuf[:0:litRegenSize], literals) } else { literals, err = huff.Decoder().Decompress1X(b.literalBuf[:0:litRegenSize], literals) } // Make sure we don't leak our literals buffer if err != nil { println("decompressing literals:", err) return in, err } if len(literals) != litRegenSize { return in, fmt.Errorf("literal output size mismatch want %d, got %d", litRegenSize, len(literals)) } case literalsBlockCompressed: if len(in) < litCompSize { println("too small: litType:", litType, " sizeFormat", sizeFormat, "remain:", len(in), "want:", litCompSize) return in, ErrBlockTooSmall } literals = in[:litCompSize] in = in[litCompSize:] // Ensure we have space to store it. if cap(b.literalBuf) < litRegenSize { if b.lowMem { b.literalBuf = make([]byte, 0, litRegenSize+compressedBlockOverAlloc) } else { b.literalBuf = make([]byte, 0, maxCompressedBlockSize+compressedBlockOverAlloc) } } huff := hist.huffTree if huff == nil || (hist.dict != nil && huff == hist.dict.litEnc) { huff = huffDecoderPool.Get().(*huff0.Scratch) if huff == nil { huff = &huff0.Scratch{} } } var err error if debugDecoder { println("huff table input:", len(literals), "CRC:", crc32.ChecksumIEEE(literals)) } huff, literals, err = huff0.ReadTable(literals, huff) if err != nil { println("reading huffman table:", err) return in, err } hist.huffTree = huff huff.MaxDecodedSize = litRegenSize // Use our out buffer. if fourStreams { literals, err = huff.Decoder().Decompress4X(b.literalBuf[:0:litRegenSize], literals) } else { literals, err = huff.Decoder().Decompress1X(b.literalBuf[:0:litRegenSize], literals) } if err != nil { println("decoding compressed literals:", err) return in, err } // Make sure we don't leak our literals buffer if len(literals) != litRegenSize { return in, fmt.Errorf("literal output size mismatch want %d, got %d", litRegenSize, len(literals)) } // Re-cap to get extra size. literals = b.literalBuf[:len(literals)] if debugDecoder { printf("Decompressed %d literals into %d bytes\n", litCompSize, litRegenSize) } } hist.decoders.literals = literals return in, nil } // decodeCompressed will start decompressing a block. func (b *blockDec) decodeCompressed(hist *history) error { in := b.data in, err := b.decodeLiterals(in, hist) if err != nil { return err } err = b.prepareSequences(in, hist) if err != nil { return err } if hist.decoders.nSeqs == 0 { b.dst = append(b.dst, hist.decoders.literals...) return nil } before := len(hist.decoders.out) err = hist.decoders.decodeSync(hist.b[hist.ignoreBuffer:]) if err != nil { return err } if hist.decoders.maxSyncLen > 0 { hist.decoders.maxSyncLen += uint64(before) hist.decoders.maxSyncLen -= uint64(len(hist.decoders.out)) } b.dst = hist.decoders.out hist.recentOffsets = hist.decoders.prevOffset return nil } func (b *blockDec) prepareSequences(in []byte, hist *history) (err error) { if debugDecoder { printf("prepareSequences: %d byte(s) input\n", len(in)) } // Decode Sequences // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#sequences-section if len(in) < 1 { return ErrBlockTooSmall } var nSeqs int seqHeader := in[0] switch { case seqHeader < 128: nSeqs = int(seqHeader) in = in[1:] case seqHeader < 255: if len(in) < 2 { return ErrBlockTooSmall } nSeqs = int(seqHeader-128)<<8 | int(in[1]) in = in[2:] case seqHeader == 255: if len(in) < 3 { return ErrBlockTooSmall } nSeqs = 0x7f00 + int(in[1]) + (int(in[2]) << 8) in = in[3:] } if nSeqs == 0 && len(in) != 0 { // When no sequences, there should not be any more data... if debugDecoder { printf("prepareSequences: 0 sequences, but %d byte(s) left on stream\n", len(in)) } return ErrUnexpectedBlockSize } var seqs = &hist.decoders seqs.nSeqs = nSeqs if nSeqs > 0 { if len(in) < 1 { return ErrBlockTooSmall } br := byteReader{b: in, off: 0} compMode := br.Uint8() br.advance(1) if debugDecoder { printf("Compression modes: 0b%b", compMode) } for i := uint(0); i < 3; i++ { mode := seqCompMode((compMode >> (6 - i*2)) & 3) if debugDecoder { println("Table", tableIndex(i), "is", mode) } var seq *sequenceDec switch tableIndex(i) { case tableLiteralLengths: seq = &seqs.litLengths case tableOffsets: seq = &seqs.offsets case tableMatchLengths: seq = &seqs.matchLengths default: panic("unknown table") } switch mode { case compModePredefined: if seq.fse != nil && !seq.fse.preDefined { fseDecoderPool.Put(seq.fse) } seq.fse = &fsePredef[i] case compModeRLE: if br.remain() < 1 { return ErrBlockTooSmall } v := br.Uint8() br.advance(1) if seq.fse == nil || seq.fse.preDefined { seq.fse = fseDecoderPool.Get().(*fseDecoder) } symb, err := decSymbolValue(v, symbolTableX[i]) if err != nil { printf("RLE Transform table (%v) error: %v", tableIndex(i), err) return err } seq.fse.setRLE(symb) if debugDecoder { printf("RLE set to 0x%x, code: %v", symb, v) } case compModeFSE: println("Reading table for", tableIndex(i)) if seq.fse == nil || seq.fse.preDefined { seq.fse = fseDecoderPool.Get().(*fseDecoder) } err := seq.fse.readNCount(&br, uint16(maxTableSymbol[i])) if err != nil { println("Read table error:", err) return err } err = seq.fse.transform(symbolTableX[i]) if err != nil { println("Transform table error:", err) return err } if debugDecoder { println("Read table ok", "symbolLen:", seq.fse.symbolLen) } case compModeRepeat: seq.repeat = true } if br.overread() { return io.ErrUnexpectedEOF } } in = br.unread() } if debugDecoder { println("Literals:", len(seqs.literals), "hash:", xxhash.Sum64(seqs.literals), "and", seqs.nSeqs, "sequences.") } if nSeqs == 0 { if len(b.sequence) > 0 { b.sequence = b.sequence[:0] } return nil } br := seqs.br if br == nil { br = &bitReader{} } if err := br.init(in); err != nil { return err } if err := seqs.initialize(br, hist, b.dst); err != nil { println("initializing sequences:", err) return err } // Extract blocks... if false && hist.dict == nil { fatalErr := func(err error) { if err != nil { panic(err) } } fn := fmt.Sprintf("n-%d-lits-%d-prev-%d-%d-%d-win-%d.blk", hist.decoders.nSeqs, len(hist.decoders.literals), hist.recentOffsets[0], hist.recentOffsets[1], hist.recentOffsets[2], hist.windowSize) var buf bytes.Buffer fatalErr(binary.Write(&buf, binary.LittleEndian, hist.decoders.litLengths.fse)) fatalErr(binary.Write(&buf, binary.LittleEndian, hist.decoders.matchLengths.fse)) fatalErr(binary.Write(&buf, binary.LittleEndian, hist.decoders.offsets.fse)) buf.Write(in) os.WriteFile(filepath.Join("testdata", "seqs", fn), buf.Bytes(), os.ModePerm) } return nil } func (b *blockDec) decodeSequences(hist *history) error { if cap(b.sequence) < hist.decoders.nSeqs { if b.lowMem { b.sequence = make([]seqVals, 0, hist.decoders.nSeqs) } else { b.sequence = make([]seqVals, 0, 0x7F00+0xffff) } } b.sequence = b.sequence[:hist.decoders.nSeqs] if hist.decoders.nSeqs == 0 { hist.decoders.seqSize = len(hist.decoders.literals) return nil } hist.decoders.windowSize = hist.windowSize hist.decoders.prevOffset = hist.recentOffsets err := hist.decoders.decode(b.sequence) hist.recentOffsets = hist.decoders.prevOffset return err } func (b *blockDec) executeSequences(hist *history) error { hbytes := hist.b if len(hbytes) > hist.windowSize { hbytes = hbytes[len(hbytes)-hist.windowSize:] // We do not need history anymore. if hist.dict != nil { hist.dict.content = nil } } hist.decoders.windowSize = hist.windowSize hist.decoders.out = b.dst[:0] err := hist.decoders.execute(b.sequence, hbytes) if err != nil { return err } return b.updateHistory(hist) } func (b *blockDec) updateHistory(hist *history) error { if len(b.data) > maxCompressedBlockSize { return fmt.Errorf("compressed block size too large (%d)", len(b.data)) } // Set output and release references. b.dst = hist.decoders.out hist.recentOffsets = hist.decoders.prevOffset if b.Last { // if last block we don't care about history. println("Last block, no history returned") hist.b = hist.b[:0] return nil } else { hist.append(b.dst) if debugDecoder { println("Finished block with ", len(b.sequence), "sequences. Added", len(b.dst), "to history, now length", len(hist.b)) } } hist.decoders.out, hist.decoders.literals = nil, nil return nil } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/blockenc.go ================================================ // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. // Based on work by Yann Collet, released under BSD License. package zstd import ( "errors" "fmt" "math" "math/bits" "github.com/klauspost/compress/huff0" ) type blockEnc struct { size int literals []byte sequences []seq coders seqCoders litEnc *huff0.Scratch dictLitEnc *huff0.Scratch wr bitWriter extraLits int output []byte recentOffsets [3]uint32 prevRecentOffsets [3]uint32 last bool lowMem bool } // init should be used once the block has been created. // If called more than once, the effect is the same as calling reset. func (b *blockEnc) init() { if b.lowMem { // 1K literals if cap(b.literals) < 1<<10 { b.literals = make([]byte, 0, 1<<10) } const defSeqs = 20 if cap(b.sequences) < defSeqs { b.sequences = make([]seq, 0, defSeqs) } // 1K if cap(b.output) < 1<<10 { b.output = make([]byte, 0, 1<<10) } } else { if cap(b.literals) < maxCompressedBlockSize { b.literals = make([]byte, 0, maxCompressedBlockSize) } const defSeqs = 2000 if cap(b.sequences) < defSeqs { b.sequences = make([]seq, 0, defSeqs) } if cap(b.output) < maxCompressedBlockSize { b.output = make([]byte, 0, maxCompressedBlockSize) } } if b.coders.mlEnc == nil { b.coders.mlEnc = &fseEncoder{} b.coders.mlPrev = &fseEncoder{} b.coders.ofEnc = &fseEncoder{} b.coders.ofPrev = &fseEncoder{} b.coders.llEnc = &fseEncoder{} b.coders.llPrev = &fseEncoder{} } b.litEnc = &huff0.Scratch{WantLogLess: 4} b.reset(nil) } // initNewEncode can be used to reset offsets and encoders to the initial state. func (b *blockEnc) initNewEncode() { b.recentOffsets = [3]uint32{1, 4, 8} b.litEnc.Reuse = huff0.ReusePolicyNone b.coders.setPrev(nil, nil, nil) } // reset will reset the block for a new encode, but in the same stream, // meaning that state will be carried over, but the block content is reset. // If a previous block is provided, the recent offsets are carried over. func (b *blockEnc) reset(prev *blockEnc) { b.extraLits = 0 b.literals = b.literals[:0] b.size = 0 b.sequences = b.sequences[:0] b.output = b.output[:0] b.last = false if prev != nil { b.recentOffsets = prev.prevRecentOffsets } b.dictLitEnc = nil } // reset will reset the block for a new encode, but in the same stream, // meaning that state will be carried over, but the block content is reset. // If a previous block is provided, the recent offsets are carried over. func (b *blockEnc) swapEncoders(prev *blockEnc) { b.coders.swap(&prev.coders) b.litEnc, prev.litEnc = prev.litEnc, b.litEnc } // blockHeader contains the information for a block header. type blockHeader uint32 // setLast sets the 'last' indicator on a block. func (h *blockHeader) setLast(b bool) { if b { *h = *h | 1 } else { const mask = (1 << 24) - 2 *h = *h & mask } } // setSize will store the compressed size of a block. func (h *blockHeader) setSize(v uint32) { const mask = 7 *h = (*h)&mask | blockHeader(v<<3) } // setType sets the block type. func (h *blockHeader) setType(t blockType) { const mask = 1 | (((1 << 24) - 1) ^ 7) *h = (*h & mask) | blockHeader(t<<1) } // appendTo will append the block header to a slice. func (h blockHeader) appendTo(b []byte) []byte { return append(b, uint8(h), uint8(h>>8), uint8(h>>16)) } // String returns a string representation of the block. func (h blockHeader) String() string { return fmt.Sprintf("Type: %d, Size: %d, Last:%t", (h>>1)&3, h>>3, h&1 == 1) } // literalsHeader contains literals header information. type literalsHeader uint64 // setType can be used to set the type of literal block. func (h *literalsHeader) setType(t literalsBlockType) { const mask = math.MaxUint64 - 3 *h = (*h & mask) | literalsHeader(t) } // setSize can be used to set a single size, for uncompressed and RLE content. func (h *literalsHeader) setSize(regenLen int) { inBits := bits.Len32(uint32(regenLen)) // Only retain 2 bits const mask = 3 lh := uint64(*h & mask) switch { case inBits < 5: lh |= (uint64(regenLen) << 3) | (1 << 60) if debugEncoder { got := int(lh>>3) & 0xff if got != regenLen { panic(fmt.Sprint("litRegenSize = ", regenLen, "(want) != ", got, "(got)")) } } case inBits < 12: lh |= (1 << 2) | (uint64(regenLen) << 4) | (2 << 60) case inBits < 20: lh |= (3 << 2) | (uint64(regenLen) << 4) | (3 << 60) default: panic(fmt.Errorf("internal error: block too big (%d)", regenLen)) } *h = literalsHeader(lh) } // setSizes will set the size of a compressed literals section and the input length. func (h *literalsHeader) setSizes(compLen, inLen int, single bool) { compBits, inBits := bits.Len32(uint32(compLen)), bits.Len32(uint32(inLen)) // Only retain 2 bits const mask = 3 lh := uint64(*h & mask) switch { case compBits <= 10 && inBits <= 10: if !single { lh |= 1 << 2 } lh |= (uint64(inLen) << 4) | (uint64(compLen) << (10 + 4)) | (3 << 60) if debugEncoder { const mmask = (1 << 24) - 1 n := (lh >> 4) & mmask if int(n&1023) != inLen { panic(fmt.Sprint("regensize:", int(n&1023), "!=", inLen, inBits)) } if int(n>>10) != compLen { panic(fmt.Sprint("compsize:", int(n>>10), "!=", compLen, compBits)) } } case compBits <= 14 && inBits <= 14: lh |= (2 << 2) | (uint64(inLen) << 4) | (uint64(compLen) << (14 + 4)) | (4 << 60) if single { panic("single stream used with more than 10 bits length.") } case compBits <= 18 && inBits <= 18: lh |= (3 << 2) | (uint64(inLen) << 4) | (uint64(compLen) << (18 + 4)) | (5 << 60) if single { panic("single stream used with more than 10 bits length.") } default: panic("internal error: block too big") } *h = literalsHeader(lh) } // appendTo will append the literals header to a byte slice. func (h literalsHeader) appendTo(b []byte) []byte { size := uint8(h >> 60) switch size { case 1: b = append(b, uint8(h)) case 2: b = append(b, uint8(h), uint8(h>>8)) case 3: b = append(b, uint8(h), uint8(h>>8), uint8(h>>16)) case 4: b = append(b, uint8(h), uint8(h>>8), uint8(h>>16), uint8(h>>24)) case 5: b = append(b, uint8(h), uint8(h>>8), uint8(h>>16), uint8(h>>24), uint8(h>>32)) default: panic(fmt.Errorf("internal error: literalsHeader has invalid size (%d)", size)) } return b } // size returns the output size with currently set values. func (h literalsHeader) size() int { return int(h >> 60) } func (h literalsHeader) String() string { return fmt.Sprintf("Type: %d, SizeFormat: %d, Size: 0x%d, Bytes:%d", literalsBlockType(h&3), (h>>2)&3, h&((1<<60)-1)>>4, h>>60) } // pushOffsets will push the recent offsets to the backup store. func (b *blockEnc) pushOffsets() { b.prevRecentOffsets = b.recentOffsets } // pushOffsets will push the recent offsets to the backup store. func (b *blockEnc) popOffsets() { b.recentOffsets = b.prevRecentOffsets } // matchOffset will adjust recent offsets and return the adjusted one, // if it matches a previous offset. func (b *blockEnc) matchOffset(offset, lits uint32) uint32 { // Check if offset is one of the recent offsets. // Adjusts the output offset accordingly. // Gives a tiny bit of compression, typically around 1%. if true { if lits > 0 { switch offset { case b.recentOffsets[0]: offset = 1 case b.recentOffsets[1]: b.recentOffsets[1] = b.recentOffsets[0] b.recentOffsets[0] = offset offset = 2 case b.recentOffsets[2]: b.recentOffsets[2] = b.recentOffsets[1] b.recentOffsets[1] = b.recentOffsets[0] b.recentOffsets[0] = offset offset = 3 default: b.recentOffsets[2] = b.recentOffsets[1] b.recentOffsets[1] = b.recentOffsets[0] b.recentOffsets[0] = offset offset += 3 } } else { switch offset { case b.recentOffsets[1]: b.recentOffsets[1] = b.recentOffsets[0] b.recentOffsets[0] = offset offset = 1 case b.recentOffsets[2]: b.recentOffsets[2] = b.recentOffsets[1] b.recentOffsets[1] = b.recentOffsets[0] b.recentOffsets[0] = offset offset = 2 case b.recentOffsets[0] - 1: b.recentOffsets[2] = b.recentOffsets[1] b.recentOffsets[1] = b.recentOffsets[0] b.recentOffsets[0] = offset offset = 3 default: b.recentOffsets[2] = b.recentOffsets[1] b.recentOffsets[1] = b.recentOffsets[0] b.recentOffsets[0] = offset offset += 3 } } } else { offset += 3 } return offset } // encodeRaw can be used to set the output to a raw representation of supplied bytes. func (b *blockEnc) encodeRaw(a []byte) { var bh blockHeader bh.setLast(b.last) bh.setSize(uint32(len(a))) bh.setType(blockTypeRaw) b.output = bh.appendTo(b.output[:0]) b.output = append(b.output, a...) if debugEncoder { println("Adding RAW block, length", len(a), "last:", b.last) } } // encodeRaw can be used to set the output to a raw representation of supplied bytes. func (b *blockEnc) encodeRawTo(dst, src []byte) []byte { var bh blockHeader bh.setLast(b.last) bh.setSize(uint32(len(src))) bh.setType(blockTypeRaw) dst = bh.appendTo(dst) dst = append(dst, src...) if debugEncoder { println("Adding RAW block, length", len(src), "last:", b.last) } return dst } // encodeLits can be used if the block is only litLen. func (b *blockEnc) encodeLits(lits []byte, raw bool) error { var bh blockHeader bh.setLast(b.last) bh.setSize(uint32(len(lits))) // Don't compress extremely small blocks if len(lits) < 8 || (len(lits) < 32 && b.dictLitEnc == nil) || raw { if debugEncoder { println("Adding RAW block, length", len(lits), "last:", b.last) } bh.setType(blockTypeRaw) b.output = bh.appendTo(b.output) b.output = append(b.output, lits...) return nil } var ( out []byte reUsed, single bool err error ) if b.dictLitEnc != nil { b.litEnc.TransferCTable(b.dictLitEnc) b.litEnc.Reuse = huff0.ReusePolicyAllow b.dictLitEnc = nil } if len(lits) >= 1024 { // Use 4 Streams. out, reUsed, err = huff0.Compress4X(lits, b.litEnc) } else if len(lits) > 16 { // Use 1 stream single = true out, reUsed, err = huff0.Compress1X(lits, b.litEnc) } else { err = huff0.ErrIncompressible } if err == nil && len(out)+5 > len(lits) { // If we are close, we may still be worse or equal to raw. var lh literalsHeader lh.setSizes(len(out), len(lits), single) if len(out)+lh.size() >= len(lits) { err = huff0.ErrIncompressible } } switch err { case huff0.ErrIncompressible: if debugEncoder { println("Adding RAW block, length", len(lits), "last:", b.last) } bh.setType(blockTypeRaw) b.output = bh.appendTo(b.output) b.output = append(b.output, lits...) return nil case huff0.ErrUseRLE: if debugEncoder { println("Adding RLE block, length", len(lits)) } bh.setType(blockTypeRLE) b.output = bh.appendTo(b.output) b.output = append(b.output, lits[0]) return nil case nil: default: return err } // Compressed... // Now, allow reuse b.litEnc.Reuse = huff0.ReusePolicyAllow bh.setType(blockTypeCompressed) var lh literalsHeader if reUsed { if debugEncoder { println("Reused tree, compressed to", len(out)) } lh.setType(literalsBlockTreeless) } else { if debugEncoder { println("New tree, compressed to", len(out), "tree size:", len(b.litEnc.OutTable)) } lh.setType(literalsBlockCompressed) } // Set sizes lh.setSizes(len(out), len(lits), single) bh.setSize(uint32(len(out) + lh.size() + 1)) // Write block headers. b.output = bh.appendTo(b.output) b.output = lh.appendTo(b.output) // Add compressed data. b.output = append(b.output, out...) // No sequences. b.output = append(b.output, 0) return nil } // fuzzFseEncoder can be used to fuzz the FSE encoder. func fuzzFseEncoder(data []byte) int { if len(data) > maxSequences || len(data) < 2 { return 0 } enc := fseEncoder{} hist := enc.Histogram() maxSym := uint8(0) for i, v := range data { v = v & 63 data[i] = v hist[v]++ if v > maxSym { maxSym = v } } if maxSym == 0 { // All 0 return 0 } maxCount := func(a []uint32) int { var max uint32 for _, v := range a { if v > max { max = v } } return int(max) } cnt := maxCount(hist[:maxSym]) if cnt == len(data) { // RLE return 0 } enc.HistogramFinished(maxSym, cnt) err := enc.normalizeCount(len(data)) if err != nil { return 0 } _, err = enc.writeCount(nil) if err != nil { panic(err) } return 1 } // encode will encode the block and append the output in b.output. // Previous offset codes must be pushed if more blocks are expected. func (b *blockEnc) encode(org []byte, raw, rawAllLits bool) error { if len(b.sequences) == 0 { return b.encodeLits(b.literals, rawAllLits) } // We want some difference to at least account for the headers. saved := b.size - len(b.literals) - (b.size >> 6) if saved < 16 { if org == nil { return errIncompressible } b.popOffsets() return b.encodeLits(org, rawAllLits) } var bh blockHeader var lh literalsHeader bh.setLast(b.last) bh.setType(blockTypeCompressed) // Store offset of the block header. Needed when we know the size. bhOffset := len(b.output) b.output = bh.appendTo(b.output) var ( out []byte reUsed, single bool err error ) if b.dictLitEnc != nil { b.litEnc.TransferCTable(b.dictLitEnc) b.litEnc.Reuse = huff0.ReusePolicyAllow b.dictLitEnc = nil } if len(b.literals) >= 1024 && !raw { // Use 4 Streams. out, reUsed, err = huff0.Compress4X(b.literals, b.litEnc) } else if len(b.literals) > 16 && !raw { // Use 1 stream single = true out, reUsed, err = huff0.Compress1X(b.literals, b.litEnc) } else { err = huff0.ErrIncompressible } if err == nil && len(out)+5 > len(b.literals) { // If we are close, we may still be worse or equal to raw. var lh literalsHeader lh.setSize(len(b.literals)) szRaw := lh.size() lh.setSizes(len(out), len(b.literals), single) szComp := lh.size() if len(out)+szComp >= len(b.literals)+szRaw { err = huff0.ErrIncompressible } } switch err { case huff0.ErrIncompressible: lh.setType(literalsBlockRaw) lh.setSize(len(b.literals)) b.output = lh.appendTo(b.output) b.output = append(b.output, b.literals...) if debugEncoder { println("Adding literals RAW, length", len(b.literals)) } case huff0.ErrUseRLE: lh.setType(literalsBlockRLE) lh.setSize(len(b.literals)) b.output = lh.appendTo(b.output) b.output = append(b.output, b.literals[0]) if debugEncoder { println("Adding literals RLE") } case nil: // Compressed litLen... if reUsed { if debugEncoder { println("reused tree") } lh.setType(literalsBlockTreeless) } else { if debugEncoder { println("new tree, size:", len(b.litEnc.OutTable)) } lh.setType(literalsBlockCompressed) if debugEncoder { _, _, err := huff0.ReadTable(out, nil) if err != nil { panic(err) } } } lh.setSizes(len(out), len(b.literals), single) if debugEncoder { printf("Compressed %d literals to %d bytes", len(b.literals), len(out)) println("Adding literal header:", lh) } b.output = lh.appendTo(b.output) b.output = append(b.output, out...) b.litEnc.Reuse = huff0.ReusePolicyAllow if debugEncoder { println("Adding literals compressed") } default: if debugEncoder { println("Adding literals ERROR:", err) } return err } // Sequence compression // Write the number of sequences switch { case len(b.sequences) < 128: b.output = append(b.output, uint8(len(b.sequences))) case len(b.sequences) < 0x7f00: // TODO: this could be wrong n := len(b.sequences) b.output = append(b.output, 128+uint8(n>>8), uint8(n)) default: n := len(b.sequences) - 0x7f00 b.output = append(b.output, 255, uint8(n), uint8(n>>8)) } if debugEncoder { println("Encoding", len(b.sequences), "sequences") } b.genCodes() llEnc := b.coders.llEnc ofEnc := b.coders.ofEnc mlEnc := b.coders.mlEnc err = llEnc.normalizeCount(len(b.sequences)) if err != nil { return err } err = ofEnc.normalizeCount(len(b.sequences)) if err != nil { return err } err = mlEnc.normalizeCount(len(b.sequences)) if err != nil { return err } // Choose the best compression mode for each type. // Will evaluate the new vs predefined and previous. chooseComp := func(cur, prev, preDef *fseEncoder) (*fseEncoder, seqCompMode) { // See if predefined/previous is better hist := cur.count[:cur.symbolLen] nSize := cur.approxSize(hist) + cur.maxHeaderSize() predefSize := preDef.approxSize(hist) prevSize := prev.approxSize(hist) // Add a small penalty for new encoders. // Don't bother with extremely small (<2 byte gains). nSize = nSize + (nSize+2*8*16)>>4 switch { case predefSize <= prevSize && predefSize <= nSize || forcePreDef: if debugEncoder { println("Using predefined", predefSize>>3, "<=", nSize>>3) } return preDef, compModePredefined case prevSize <= nSize: if debugEncoder { println("Using previous", prevSize>>3, "<=", nSize>>3) } return prev, compModeRepeat default: if debugEncoder { println("Using new, predef", predefSize>>3, ". previous:", prevSize>>3, ">", nSize>>3, "header max:", cur.maxHeaderSize()>>3, "bytes") println("tl:", cur.actualTableLog, "symbolLen:", cur.symbolLen, "norm:", cur.norm[:cur.symbolLen], "hist", cur.count[:cur.symbolLen]) } return cur, compModeFSE } } // Write compression mode var mode uint8 if llEnc.useRLE { mode |= uint8(compModeRLE) << 6 llEnc.setRLE(b.sequences[0].llCode) if debugEncoder { println("llEnc.useRLE") } } else { var m seqCompMode llEnc, m = chooseComp(llEnc, b.coders.llPrev, &fsePredefEnc[tableLiteralLengths]) mode |= uint8(m) << 6 } if ofEnc.useRLE { mode |= uint8(compModeRLE) << 4 ofEnc.setRLE(b.sequences[0].ofCode) if debugEncoder { println("ofEnc.useRLE") } } else { var m seqCompMode ofEnc, m = chooseComp(ofEnc, b.coders.ofPrev, &fsePredefEnc[tableOffsets]) mode |= uint8(m) << 4 } if mlEnc.useRLE { mode |= uint8(compModeRLE) << 2 mlEnc.setRLE(b.sequences[0].mlCode) if debugEncoder { println("mlEnc.useRLE, code: ", b.sequences[0].mlCode, "value", b.sequences[0].matchLen) } } else { var m seqCompMode mlEnc, m = chooseComp(mlEnc, b.coders.mlPrev, &fsePredefEnc[tableMatchLengths]) mode |= uint8(m) << 2 } b.output = append(b.output, mode) if debugEncoder { printf("Compression modes: 0b%b", mode) } b.output, err = llEnc.writeCount(b.output) if err != nil { return err } start := len(b.output) b.output, err = ofEnc.writeCount(b.output) if err != nil { return err } if false { println("block:", b.output[start:], "tablelog", ofEnc.actualTableLog, "maxcount:", ofEnc.maxCount) fmt.Printf("selected TableLog: %d, Symbol length: %d\n", ofEnc.actualTableLog, ofEnc.symbolLen) for i, v := range ofEnc.norm[:ofEnc.symbolLen] { fmt.Printf("%3d: %5d -> %4d \n", i, ofEnc.count[i], v) } } b.output, err = mlEnc.writeCount(b.output) if err != nil { return err } // Maybe in block? wr := &b.wr wr.reset(b.output) var ll, of, ml cState // Current sequence seq := len(b.sequences) - 1 s := b.sequences[seq] llEnc.setBits(llBitsTable[:]) mlEnc.setBits(mlBitsTable[:]) ofEnc.setBits(nil) llTT, ofTT, mlTT := llEnc.ct.symbolTT[:256], ofEnc.ct.symbolTT[:256], mlEnc.ct.symbolTT[:256] // We have 3 bounds checks here (and in the loop). // Since we are iterating backwards it is kinda hard to avoid. llB, ofB, mlB := llTT[s.llCode], ofTT[s.ofCode], mlTT[s.mlCode] ll.init(wr, &llEnc.ct, llB) of.init(wr, &ofEnc.ct, ofB) wr.flush32() ml.init(wr, &mlEnc.ct, mlB) // Each of these lookups also generates a bounds check. wr.addBits32NC(s.litLen, llB.outBits) wr.addBits32NC(s.matchLen, mlB.outBits) wr.flush32() wr.addBits32NC(s.offset, ofB.outBits) if debugSequences { println("Encoded seq", seq, s, "codes:", s.llCode, s.mlCode, s.ofCode, "states:", ll.state, ml.state, of.state, "bits:", llB, mlB, ofB) } seq-- // Store sequences in reverse... for seq >= 0 { s = b.sequences[seq] ofB := ofTT[s.ofCode] wr.flush32() // tablelog max is below 8 for each, so it will fill max 24 bits. //of.encode(ofB) nbBitsOut := (uint32(of.state) + ofB.deltaNbBits) >> 16 dstState := int32(of.state>>(nbBitsOut&15)) + int32(ofB.deltaFindState) wr.addBits16NC(of.state, uint8(nbBitsOut)) of.state = of.stateTable[dstState] // Accumulate extra bits. outBits := ofB.outBits & 31 extraBits := uint64(s.offset & bitMask32[outBits]) extraBitsN := outBits mlB := mlTT[s.mlCode] //ml.encode(mlB) nbBitsOut = (uint32(ml.state) + mlB.deltaNbBits) >> 16 dstState = int32(ml.state>>(nbBitsOut&15)) + int32(mlB.deltaFindState) wr.addBits16NC(ml.state, uint8(nbBitsOut)) ml.state = ml.stateTable[dstState] outBits = mlB.outBits & 31 extraBits = extraBits<> 16 dstState = int32(ll.state>>(nbBitsOut&15)) + int32(llB.deltaFindState) wr.addBits16NC(ll.state, uint8(nbBitsOut)) ll.state = ll.stateTable[dstState] outBits = llB.outBits & 31 extraBits = extraBits<= b.size { // Discard and encode as raw block. b.output = b.encodeRawTo(b.output[:bhOffset], org) b.popOffsets() b.litEnc.Reuse = huff0.ReusePolicyNone return nil } // Size is output minus block header. bh.setSize(uint32(len(b.output)-bhOffset) - 3) if debugEncoder { println("Rewriting block header", bh) } _ = bh.appendTo(b.output[bhOffset:bhOffset]) b.coders.setPrev(llEnc, mlEnc, ofEnc) return nil } var errIncompressible = errors.New("incompressible") func (b *blockEnc) genCodes() { if len(b.sequences) == 0 { // nothing to do return } if len(b.sequences) > math.MaxUint16 { panic("can only encode up to 64K sequences") } // No bounds checks after here: llH := b.coders.llEnc.Histogram() ofH := b.coders.ofEnc.Histogram() mlH := b.coders.mlEnc.Histogram() for i := range llH { llH[i] = 0 } for i := range ofH { ofH[i] = 0 } for i := range mlH { mlH[i] = 0 } var llMax, ofMax, mlMax uint8 for i := range b.sequences { seq := &b.sequences[i] v := llCode(seq.litLen) seq.llCode = v llH[v]++ if v > llMax { llMax = v } v = ofCode(seq.offset) seq.ofCode = v ofH[v]++ if v > ofMax { ofMax = v } v = mlCode(seq.matchLen) seq.mlCode = v mlH[v]++ if v > mlMax { mlMax = v if debugAsserts && mlMax > maxMatchLengthSymbol { panic(fmt.Errorf("mlMax > maxMatchLengthSymbol (%d), matchlen: %d", mlMax, seq.matchLen)) } } } maxCount := func(a []uint32) int { var max uint32 for _, v := range a { if v > max { max = v } } return int(max) } if debugAsserts && mlMax > maxMatchLengthSymbol { panic(fmt.Errorf("mlMax > maxMatchLengthSymbol (%d)", mlMax)) } if debugAsserts && ofMax > maxOffsetBits { panic(fmt.Errorf("ofMax > maxOffsetBits (%d)", ofMax)) } if debugAsserts && llMax > maxLiteralLengthSymbol { panic(fmt.Errorf("llMax > maxLiteralLengthSymbol (%d)", llMax)) } b.coders.mlEnc.HistogramFinished(mlMax, maxCount(mlH[:mlMax+1])) b.coders.ofEnc.HistogramFinished(ofMax, maxCount(ofH[:ofMax+1])) b.coders.llEnc.HistogramFinished(llMax, maxCount(llH[:llMax+1])) } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/blocktype_string.go ================================================ // Code generated by "stringer -type=blockType,literalsBlockType,seqCompMode,tableIndex"; DO NOT EDIT. package zstd import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[blockTypeRaw-0] _ = x[blockTypeRLE-1] _ = x[blockTypeCompressed-2] _ = x[blockTypeReserved-3] } const _blockType_name = "blockTypeRawblockTypeRLEblockTypeCompressedblockTypeReserved" var _blockType_index = [...]uint8{0, 12, 24, 43, 60} func (i blockType) String() string { if i >= blockType(len(_blockType_index)-1) { return "blockType(" + strconv.FormatInt(int64(i), 10) + ")" } return _blockType_name[_blockType_index[i]:_blockType_index[i+1]] } func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[literalsBlockRaw-0] _ = x[literalsBlockRLE-1] _ = x[literalsBlockCompressed-2] _ = x[literalsBlockTreeless-3] } const _literalsBlockType_name = "literalsBlockRawliteralsBlockRLEliteralsBlockCompressedliteralsBlockTreeless" var _literalsBlockType_index = [...]uint8{0, 16, 32, 55, 76} func (i literalsBlockType) String() string { if i >= literalsBlockType(len(_literalsBlockType_index)-1) { return "literalsBlockType(" + strconv.FormatInt(int64(i), 10) + ")" } return _literalsBlockType_name[_literalsBlockType_index[i]:_literalsBlockType_index[i+1]] } func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[compModePredefined-0] _ = x[compModeRLE-1] _ = x[compModeFSE-2] _ = x[compModeRepeat-3] } const _seqCompMode_name = "compModePredefinedcompModeRLEcompModeFSEcompModeRepeat" var _seqCompMode_index = [...]uint8{0, 18, 29, 40, 54} func (i seqCompMode) String() string { if i >= seqCompMode(len(_seqCompMode_index)-1) { return "seqCompMode(" + strconv.FormatInt(int64(i), 10) + ")" } return _seqCompMode_name[_seqCompMode_index[i]:_seqCompMode_index[i+1]] } func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[tableLiteralLengths-0] _ = x[tableOffsets-1] _ = x[tableMatchLengths-2] } const _tableIndex_name = "tableLiteralLengthstableOffsetstableMatchLengths" var _tableIndex_index = [...]uint8{0, 19, 31, 48} func (i tableIndex) String() string { if i >= tableIndex(len(_tableIndex_index)-1) { return "tableIndex(" + strconv.FormatInt(int64(i), 10) + ")" } return _tableIndex_name[_tableIndex_index[i]:_tableIndex_index[i+1]] } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/bytebuf.go ================================================ // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. // Based on work by Yann Collet, released under BSD License. package zstd import ( "fmt" "io" ) type byteBuffer interface { // Read up to 8 bytes. // Returns io.ErrUnexpectedEOF if this cannot be satisfied. readSmall(n int) ([]byte, error) // Read >8 bytes. // MAY use the destination slice. readBig(n int, dst []byte) ([]byte, error) // Read a single byte. readByte() (byte, error) // Skip n bytes. skipN(n int64) error } // in-memory buffer type byteBuf []byte func (b *byteBuf) readSmall(n int) ([]byte, error) { if debugAsserts && n > 8 { panic(fmt.Errorf("small read > 8 (%d). use readBig", n)) } bb := *b if len(bb) < n { return nil, io.ErrUnexpectedEOF } r := bb[:n] *b = bb[n:] return r, nil } func (b *byteBuf) readBig(n int, dst []byte) ([]byte, error) { bb := *b if len(bb) < n { return nil, io.ErrUnexpectedEOF } r := bb[:n] *b = bb[n:] return r, nil } func (b *byteBuf) readByte() (byte, error) { bb := *b if len(bb) < 1 { return 0, io.ErrUnexpectedEOF } r := bb[0] *b = bb[1:] return r, nil } func (b *byteBuf) skipN(n int64) error { bb := *b if n < 0 { return fmt.Errorf("negative skip (%d) requested", n) } if int64(len(bb)) < n { return io.ErrUnexpectedEOF } *b = bb[n:] return nil } // wrapper around a reader. type readerWrapper struct { r io.Reader tmp [8]byte } func (r *readerWrapper) readSmall(n int) ([]byte, error) { if debugAsserts && n > 8 { panic(fmt.Errorf("small read > 8 (%d). use readBig", n)) } n2, err := io.ReadFull(r.r, r.tmp[:n]) // We only really care about the actual bytes read. if err != nil { if err == io.EOF { return nil, io.ErrUnexpectedEOF } if debugDecoder { println("readSmall: got", n2, "want", n, "err", err) } return nil, err } return r.tmp[:n], nil } func (r *readerWrapper) readBig(n int, dst []byte) ([]byte, error) { if cap(dst) < n { dst = make([]byte, n) } n2, err := io.ReadFull(r.r, dst[:n]) if err == io.EOF && n > 0 { err = io.ErrUnexpectedEOF } return dst[:n2], err } func (r *readerWrapper) readByte() (byte, error) { n2, err := io.ReadFull(r.r, r.tmp[:1]) if err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } return 0, err } if n2 != 1 { return 0, io.ErrUnexpectedEOF } return r.tmp[0], nil } func (r *readerWrapper) skipN(n int64) error { n2, err := io.CopyN(io.Discard, r.r, n) if n2 != n { err = io.ErrUnexpectedEOF } return err } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/bytereader.go ================================================ // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. // Based on work by Yann Collet, released under BSD License. package zstd // byteReader provides a byte reader that reads // little endian values from a byte stream. // The input stream is manually advanced. // The reader performs no bounds checks. type byteReader struct { b []byte off int } // advance the stream b n bytes. func (b *byteReader) advance(n uint) { b.off += int(n) } // overread returns whether we have advanced too far. func (b *byteReader) overread() bool { return b.off > len(b.b) } // Int32 returns a little endian int32 starting at current offset. func (b byteReader) Int32() int32 { b2 := b.b[b.off:] b2 = b2[:4] v3 := int32(b2[3]) v2 := int32(b2[2]) v1 := int32(b2[1]) v0 := int32(b2[0]) return v0 | (v1 << 8) | (v2 << 16) | (v3 << 24) } // Uint8 returns the next byte func (b *byteReader) Uint8() uint8 { v := b.b[b.off] return v } // Uint32 returns a little endian uint32 starting at current offset. func (b byteReader) Uint32() uint32 { if r := b.remain(); r < 4 { // Very rare v := uint32(0) for i := 1; i <= r; i++ { v = (v << 8) | uint32(b.b[len(b.b)-i]) } return v } b2 := b.b[b.off:] b2 = b2[:4] v3 := uint32(b2[3]) v2 := uint32(b2[2]) v1 := uint32(b2[1]) v0 := uint32(b2[0]) return v0 | (v1 << 8) | (v2 << 16) | (v3 << 24) } // Uint32NC returns a little endian uint32 starting at current offset. // The caller must be sure if there are at least 4 bytes left. func (b byteReader) Uint32NC() uint32 { b2 := b.b[b.off:] b2 = b2[:4] v3 := uint32(b2[3]) v2 := uint32(b2[2]) v1 := uint32(b2[1]) v0 := uint32(b2[0]) return v0 | (v1 << 8) | (v2 << 16) | (v3 << 24) } // unread returns the unread portion of the input. func (b byteReader) unread() []byte { return b.b[b.off:] } // remain will return the number of bytes remaining. func (b byteReader) remain() int { return len(b.b) - b.off } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/decodeheader.go ================================================ // Copyright 2020+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. package zstd import ( "encoding/binary" "errors" "io" ) // HeaderMaxSize is the maximum size of a Frame and Block Header. // If less is sent to Header.Decode it *may* still contain enough information. const HeaderMaxSize = 14 + 3 // Header contains information about the first frame and block within that. type Header struct { // SingleSegment specifies whether the data is to be decompressed into a // single contiguous memory segment. // It implies that WindowSize is invalid and that FrameContentSize is valid. SingleSegment bool // WindowSize is the window of data to keep while decoding. // Will only be set if SingleSegment is false. WindowSize uint64 // Dictionary ID. // If 0, no dictionary. DictionaryID uint32 // HasFCS specifies whether FrameContentSize has a valid value. HasFCS bool // FrameContentSize is the expected uncompressed size of the entire frame. FrameContentSize uint64 // Skippable will be true if the frame is meant to be skipped. // This implies that FirstBlock.OK is false. Skippable bool // SkippableID is the user-specific ID for the skippable frame. // Valid values are between 0 to 15, inclusive. SkippableID int // SkippableSize is the length of the user data to skip following // the header. SkippableSize uint32 // HeaderSize is the raw size of the frame header. // // For normal frames, it includes the size of the magic number and // the size of the header (per section 3.1.1.1). // It does not include the size for any data blocks (section 3.1.1.2) nor // the size for the trailing content checksum. // // For skippable frames, this counts the size of the magic number // along with the size of the size field of the payload. // It does not include the size of the skippable payload itself. // The total frame size is the HeaderSize plus the SkippableSize. HeaderSize int // First block information. FirstBlock struct { // OK will be set if first block could be decoded. OK bool // Is this the last block of a frame? Last bool // Is the data compressed? // If true CompressedSize will be populated. // Unfortunately DecompressedSize cannot be determined // without decoding the blocks. Compressed bool // DecompressedSize is the expected decompressed size of the block. // Will be 0 if it cannot be determined. DecompressedSize int // CompressedSize of the data in the block. // Does not include the block header. // Will be equal to DecompressedSize if not Compressed. CompressedSize int } // If set there is a checksum present for the block content. // The checksum field at the end is always 4 bytes long. HasCheckSum bool } // Decode the header from the beginning of the stream. // This will decode the frame header and the first block header if enough bytes are provided. // It is recommended to provide at least HeaderMaxSize bytes. // If the frame header cannot be read an error will be returned. // If there isn't enough input, io.ErrUnexpectedEOF is returned. // The FirstBlock.OK will indicate if enough information was available to decode the first block header. func (h *Header) Decode(in []byte) error { *h = Header{} if len(in) < 4 { return io.ErrUnexpectedEOF } h.HeaderSize += 4 b, in := in[:4], in[4:] if string(b) != frameMagic { if string(b[1:4]) != skippableFrameMagic || b[0]&0xf0 != 0x50 { return ErrMagicMismatch } if len(in) < 4 { return io.ErrUnexpectedEOF } h.HeaderSize += 4 h.Skippable = true h.SkippableID = int(b[0] & 0xf) h.SkippableSize = binary.LittleEndian.Uint32(in) return nil } // Read Window_Descriptor // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#window_descriptor if len(in) < 1 { return io.ErrUnexpectedEOF } fhd, in := in[0], in[1:] h.HeaderSize++ h.SingleSegment = fhd&(1<<5) != 0 h.HasCheckSum = fhd&(1<<2) != 0 if fhd&(1<<3) != 0 { return errors.New("reserved bit set on frame header") } if !h.SingleSegment { if len(in) < 1 { return io.ErrUnexpectedEOF } var wd byte wd, in = in[0], in[1:] h.HeaderSize++ windowLog := 10 + (wd >> 3) windowBase := uint64(1) << windowLog windowAdd := (windowBase / 8) * uint64(wd&0x7) h.WindowSize = windowBase + windowAdd } // Read Dictionary_ID // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#dictionary_id if size := fhd & 3; size != 0 { if size == 3 { size = 4 } if len(in) < int(size) { return io.ErrUnexpectedEOF } b, in = in[:size], in[size:] h.HeaderSize += int(size) switch len(b) { case 1: h.DictionaryID = uint32(b[0]) case 2: h.DictionaryID = uint32(b[0]) | (uint32(b[1]) << 8) case 4: h.DictionaryID = uint32(b[0]) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24) } } // Read Frame_Content_Size // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#frame_content_size var fcsSize int v := fhd >> 6 switch v { case 0: if h.SingleSegment { fcsSize = 1 } default: fcsSize = 1 << v } if fcsSize > 0 { h.HasFCS = true if len(in) < fcsSize { return io.ErrUnexpectedEOF } b, in = in[:fcsSize], in[fcsSize:] h.HeaderSize += int(fcsSize) switch len(b) { case 1: h.FrameContentSize = uint64(b[0]) case 2: // When FCS_Field_Size is 2, the offset of 256 is added. h.FrameContentSize = uint64(b[0]) | (uint64(b[1]) << 8) + 256 case 4: h.FrameContentSize = uint64(b[0]) | (uint64(b[1]) << 8) | (uint64(b[2]) << 16) | (uint64(b[3]) << 24) case 8: d1 := uint32(b[0]) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24) d2 := uint32(b[4]) | (uint32(b[5]) << 8) | (uint32(b[6]) << 16) | (uint32(b[7]) << 24) h.FrameContentSize = uint64(d1) | (uint64(d2) << 32) } } // Frame Header done, we will not fail from now on. if len(in) < 3 { return nil } tmp := in[:3] bh := uint32(tmp[0]) | (uint32(tmp[1]) << 8) | (uint32(tmp[2]) << 16) h.FirstBlock.Last = bh&1 != 0 blockType := blockType((bh >> 1) & 3) // find size. cSize := int(bh >> 3) switch blockType { case blockTypeReserved: return nil case blockTypeRLE: h.FirstBlock.Compressed = true h.FirstBlock.DecompressedSize = cSize h.FirstBlock.CompressedSize = 1 case blockTypeCompressed: h.FirstBlock.Compressed = true h.FirstBlock.CompressedSize = cSize case blockTypeRaw: h.FirstBlock.DecompressedSize = cSize h.FirstBlock.CompressedSize = cSize default: panic("Invalid block type") } h.FirstBlock.OK = true return nil } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/decoder.go ================================================ // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. // Based on work by Yann Collet, released under BSD License. package zstd import ( "context" "encoding/binary" "io" "sync" "github.com/klauspost/compress/zstd/internal/xxhash" ) // Decoder provides decoding of zstandard streams. // The decoder has been designed to operate without allocations after a warmup. // This means that you should store the decoder for best performance. // To re-use a stream decoder, use the Reset(r io.Reader) error to switch to another stream. // A decoder can safely be re-used even if the previous stream failed. // To release the resources, you must call the Close() function on a decoder. type Decoder struct { o decoderOptions // Unreferenced decoders, ready for use. decoders chan *blockDec // Current read position used for Reader functionality. current decoderState // sync stream decoding syncStream struct { decodedFrame uint64 br readerWrapper enabled bool inFrame bool dstBuf []byte } frame *frameDec // Custom dictionaries. dicts map[uint32]*dict // streamWg is the waitgroup for all streams streamWg sync.WaitGroup } // decoderState is used for maintaining state when the decoder // is used for streaming. type decoderState struct { // current block being written to stream. decodeOutput // output in order to be written to stream. output chan decodeOutput // cancel remaining output. cancel context.CancelFunc // crc of current frame crc *xxhash.Digest flushed bool } var ( // Check the interfaces we want to support. _ = io.WriterTo(&Decoder{}) _ = io.Reader(&Decoder{}) ) // NewReader creates a new decoder. // A nil Reader can be provided in which case Reset can be used to start a decode. // // A Decoder can be used in two modes: // // 1) As a stream, or // 2) For stateless decoding using DecodeAll. // // Only a single stream can be decoded concurrently, but the same decoder // can run multiple concurrent stateless decodes. It is even possible to // use stateless decodes while a stream is being decoded. // // The Reset function can be used to initiate a new stream, which is will considerably // reduce the allocations normally caused by NewReader. func NewReader(r io.Reader, opts ...DOption) (*Decoder, error) { initPredefined() var d Decoder d.o.setDefault() for _, o := range opts { err := o(&d.o) if err != nil { return nil, err } } d.current.crc = xxhash.New() d.current.flushed = true if r == nil { d.current.err = ErrDecoderNilInput } // Transfer option dicts. d.dicts = make(map[uint32]*dict, len(d.o.dicts)) for _, dc := range d.o.dicts { d.dicts[dc.id] = dc } d.o.dicts = nil // Create decoders d.decoders = make(chan *blockDec, d.o.concurrent) for i := 0; i < d.o.concurrent; i++ { dec := newBlockDec(d.o.lowMem) dec.localFrame = newFrameDec(d.o) d.decoders <- dec } if r == nil { return &d, nil } return &d, d.Reset(r) } // Read bytes from the decompressed stream into p. // Returns the number of bytes written and any error that occurred. // When the stream is done, io.EOF will be returned. func (d *Decoder) Read(p []byte) (int, error) { var n int for { if len(d.current.b) > 0 { filled := copy(p, d.current.b) p = p[filled:] d.current.b = d.current.b[filled:] n += filled } if len(p) == 0 { break } if len(d.current.b) == 0 { // We have an error and no more data if d.current.err != nil { break } if !d.nextBlock(n == 0) { return n, d.current.err } } } if len(d.current.b) > 0 { if debugDecoder { println("returning", n, "still bytes left:", len(d.current.b)) } // Only return error at end of block return n, nil } if d.current.err != nil { d.drainOutput() } if debugDecoder { println("returning", n, d.current.err, len(d.decoders)) } return n, d.current.err } // Reset will reset the decoder the supplied stream after the current has finished processing. // Note that this functionality cannot be used after Close has been called. // Reset can be called with a nil reader to release references to the previous reader. // After being called with a nil reader, no other operations than Reset or DecodeAll or Close // should be used. func (d *Decoder) Reset(r io.Reader) error { if d.current.err == ErrDecoderClosed { return d.current.err } d.drainOutput() d.syncStream.br.r = nil if r == nil { d.current.err = ErrDecoderNilInput if len(d.current.b) > 0 { d.current.b = d.current.b[:0] } d.current.flushed = true return nil } // If bytes buffer and < 5MB, do sync decoding anyway. if bb, ok := r.(byter); ok && bb.Len() < d.o.decodeBufsBelow && !d.o.limitToCap { bb2 := bb if debugDecoder { println("*bytes.Buffer detected, doing sync decode, len:", bb.Len()) } b := bb2.Bytes() var dst []byte if cap(d.syncStream.dstBuf) > 0 { dst = d.syncStream.dstBuf[:0] } dst, err := d.DecodeAll(b, dst) if err == nil { err = io.EOF } // Save output buffer d.syncStream.dstBuf = dst d.current.b = dst d.current.err = err d.current.flushed = true if debugDecoder { println("sync decode to", len(dst), "bytes, err:", err) } return nil } // Remove current block. d.stashDecoder() d.current.decodeOutput = decodeOutput{} d.current.err = nil d.current.flushed = false d.current.d = nil d.syncStream.dstBuf = nil // Ensure no-one else is still running... d.streamWg.Wait() if d.frame == nil { d.frame = newFrameDec(d.o) } if d.o.concurrent == 1 { return d.startSyncDecoder(r) } d.current.output = make(chan decodeOutput, d.o.concurrent) ctx, cancel := context.WithCancel(context.Background()) d.current.cancel = cancel d.streamWg.Add(1) go d.startStreamDecoder(ctx, r, d.current.output) return nil } // drainOutput will drain the output until errEndOfStream is sent. func (d *Decoder) drainOutput() { if d.current.cancel != nil { if debugDecoder { println("cancelling current") } d.current.cancel() d.current.cancel = nil } if d.current.d != nil { if debugDecoder { printf("re-adding current decoder %p, decoders: %d", d.current.d, len(d.decoders)) } d.decoders <- d.current.d d.current.d = nil d.current.b = nil } if d.current.output == nil || d.current.flushed { println("current already flushed") return } for v := range d.current.output { if v.d != nil { if debugDecoder { printf("re-adding decoder %p", v.d) } d.decoders <- v.d } } d.current.output = nil d.current.flushed = true } // WriteTo writes data to w until there's no more data to write or when an error occurs. // The return value n is the number of bytes written. // Any error encountered during the write is also returned. func (d *Decoder) WriteTo(w io.Writer) (int64, error) { var n int64 for { if len(d.current.b) > 0 { n2, err2 := w.Write(d.current.b) n += int64(n2) if err2 != nil && (d.current.err == nil || d.current.err == io.EOF) { d.current.err = err2 } else if n2 != len(d.current.b) { d.current.err = io.ErrShortWrite } } if d.current.err != nil { break } d.nextBlock(true) } err := d.current.err if err != nil { d.drainOutput() } if err == io.EOF { err = nil } return n, err } // DecodeAll allows stateless decoding of a blob of bytes. // Output will be appended to dst, so if the destination size is known // you can pre-allocate the destination slice to avoid allocations. // DecodeAll can be used concurrently. // The Decoder concurrency limits will be respected. func (d *Decoder) DecodeAll(input, dst []byte) ([]byte, error) { if d.decoders == nil { return dst, ErrDecoderClosed } // Grab a block decoder and frame decoder. block := <-d.decoders frame := block.localFrame initialSize := len(dst) defer func() { if debugDecoder { printf("re-adding decoder: %p", block) } frame.rawInput = nil frame.bBuf = nil if frame.history.decoders.br != nil { frame.history.decoders.br.in = nil } d.decoders <- block }() frame.bBuf = input for { frame.history.reset() err := frame.reset(&frame.bBuf) if err != nil { if err == io.EOF { if debugDecoder { println("frame reset return EOF") } return dst, nil } return dst, err } if err = d.setDict(frame); err != nil { return nil, err } if frame.WindowSize > d.o.maxWindowSize { if debugDecoder { println("window size exceeded:", frame.WindowSize, ">", d.o.maxWindowSize) } return dst, ErrWindowSizeExceeded } if frame.FrameContentSize != fcsUnknown { if frame.FrameContentSize > d.o.maxDecodedSize-uint64(len(dst)-initialSize) { if debugDecoder { println("decoder size exceeded; fcs:", frame.FrameContentSize, "> mcs:", d.o.maxDecodedSize-uint64(len(dst)-initialSize), "len:", len(dst)) } return dst, ErrDecoderSizeExceeded } if d.o.limitToCap && frame.FrameContentSize > uint64(cap(dst)-len(dst)) { if debugDecoder { println("decoder size exceeded; fcs:", frame.FrameContentSize, "> (cap-len)", cap(dst)-len(dst)) } return dst, ErrDecoderSizeExceeded } if cap(dst)-len(dst) < int(frame.FrameContentSize) { dst2 := make([]byte, len(dst), len(dst)+int(frame.FrameContentSize)+compressedBlockOverAlloc) copy(dst2, dst) dst = dst2 } } if cap(dst) == 0 && !d.o.limitToCap { // Allocate len(input) * 2 by default if nothing is provided // and we didn't get frame content size. size := len(input) * 2 // Cap to 1 MB. if size > 1<<20 { size = 1 << 20 } if uint64(size) > d.o.maxDecodedSize { size = int(d.o.maxDecodedSize) } dst = make([]byte, 0, size) } dst, err = frame.runDecoder(dst, block) if err != nil { return dst, err } if uint64(len(dst)-initialSize) > d.o.maxDecodedSize { return dst, ErrDecoderSizeExceeded } if len(frame.bBuf) == 0 { if debugDecoder { println("frame dbuf empty") } break } } return dst, nil } // nextBlock returns the next block. // If an error occurs d.err will be set. // Optionally the function can block for new output. // If non-blocking mode is used the returned boolean will be false // if no data was available without blocking. func (d *Decoder) nextBlock(blocking bool) (ok bool) { if d.current.err != nil { // Keep error state. return false } d.current.b = d.current.b[:0] // SYNC: if d.syncStream.enabled { if !blocking { return false } ok = d.nextBlockSync() if !ok { d.stashDecoder() } return ok } //ASYNC: d.stashDecoder() if blocking { d.current.decodeOutput, ok = <-d.current.output } else { select { case d.current.decodeOutput, ok = <-d.current.output: default: return false } } if !ok { // This should not happen, so signal error state... d.current.err = io.ErrUnexpectedEOF return false } next := d.current.decodeOutput if next.d != nil && next.d.async.newHist != nil { d.current.crc.Reset() } if debugDecoder { var tmp [4]byte binary.LittleEndian.PutUint32(tmp[:], uint32(xxhash.Sum64(next.b))) println("got", len(d.current.b), "bytes, error:", d.current.err, "data crc:", tmp) } if d.o.ignoreChecksum { return true } if len(next.b) > 0 { d.current.crc.Write(next.b) } if next.err == nil && next.d != nil && next.d.hasCRC { got := uint32(d.current.crc.Sum64()) if got != next.d.checkCRC { if debugDecoder { printf("CRC Check Failed: %08x (got) != %08x (on stream)\n", got, next.d.checkCRC) } d.current.err = ErrCRCMismatch } else { if debugDecoder { printf("CRC ok %08x\n", got) } } } return true } func (d *Decoder) nextBlockSync() (ok bool) { if d.current.d == nil { d.current.d = <-d.decoders } for len(d.current.b) == 0 { if !d.syncStream.inFrame { d.frame.history.reset() d.current.err = d.frame.reset(&d.syncStream.br) if d.current.err == nil { d.current.err = d.setDict(d.frame) } if d.current.err != nil { return false } if d.frame.WindowSize > d.o.maxDecodedSize || d.frame.WindowSize > d.o.maxWindowSize { d.current.err = ErrDecoderSizeExceeded return false } d.syncStream.decodedFrame = 0 d.syncStream.inFrame = true } d.current.err = d.frame.next(d.current.d) if d.current.err != nil { return false } d.frame.history.ensureBlock() if debugDecoder { println("History trimmed:", len(d.frame.history.b), "decoded already:", d.syncStream.decodedFrame) } histBefore := len(d.frame.history.b) d.current.err = d.current.d.decodeBuf(&d.frame.history) if d.current.err != nil { println("error after:", d.current.err) return false } d.current.b = d.frame.history.b[histBefore:] if debugDecoder { println("history after:", len(d.frame.history.b)) } // Check frame size (before CRC) d.syncStream.decodedFrame += uint64(len(d.current.b)) if d.syncStream.decodedFrame > d.frame.FrameContentSize { if debugDecoder { printf("DecodedFrame (%d) > FrameContentSize (%d)\n", d.syncStream.decodedFrame, d.frame.FrameContentSize) } d.current.err = ErrFrameSizeExceeded return false } // Check FCS if d.current.d.Last && d.frame.FrameContentSize != fcsUnknown && d.syncStream.decodedFrame != d.frame.FrameContentSize { if debugDecoder { printf("DecodedFrame (%d) != FrameContentSize (%d)\n", d.syncStream.decodedFrame, d.frame.FrameContentSize) } d.current.err = ErrFrameSizeMismatch return false } // Update/Check CRC if d.frame.HasCheckSum { if !d.o.ignoreChecksum { d.frame.crc.Write(d.current.b) } if d.current.d.Last { if !d.o.ignoreChecksum { d.current.err = d.frame.checkCRC() } else { d.current.err = d.frame.consumeCRC() } if d.current.err != nil { println("CRC error:", d.current.err) return false } } } d.syncStream.inFrame = !d.current.d.Last } return true } func (d *Decoder) stashDecoder() { if d.current.d != nil { if debugDecoder { printf("re-adding current decoder %p", d.current.d) } d.decoders <- d.current.d d.current.d = nil } } // Close will release all resources. // It is NOT possible to reuse the decoder after this. func (d *Decoder) Close() { if d.current.err == ErrDecoderClosed { return } d.drainOutput() if d.current.cancel != nil { d.current.cancel() d.streamWg.Wait() d.current.cancel = nil } if d.decoders != nil { close(d.decoders) for dec := range d.decoders { dec.Close() } d.decoders = nil } if d.current.d != nil { d.current.d.Close() d.current.d = nil } d.current.err = ErrDecoderClosed } // IOReadCloser returns the decoder as an io.ReadCloser for convenience. // Any changes to the decoder will be reflected, so the returned ReadCloser // can be reused along with the decoder. // io.WriterTo is also supported by the returned ReadCloser. func (d *Decoder) IOReadCloser() io.ReadCloser { return closeWrapper{d: d} } // closeWrapper wraps a function call as a closer. type closeWrapper struct { d *Decoder } // WriteTo forwards WriteTo calls to the decoder. func (c closeWrapper) WriteTo(w io.Writer) (n int64, err error) { return c.d.WriteTo(w) } // Read forwards read calls to the decoder. func (c closeWrapper) Read(p []byte) (n int, err error) { return c.d.Read(p) } // Close closes the decoder. func (c closeWrapper) Close() error { c.d.Close() return nil } type decodeOutput struct { d *blockDec b []byte err error } func (d *Decoder) startSyncDecoder(r io.Reader) error { d.frame.history.reset() d.syncStream.br = readerWrapper{r: r} d.syncStream.inFrame = false d.syncStream.enabled = true d.syncStream.decodedFrame = 0 return nil } // Create Decoder: // ASYNC: // Spawn 3 go routines. // 0: Read frames and decode block literals. // 1: Decode sequences. // 2: Execute sequences, send to output. func (d *Decoder) startStreamDecoder(ctx context.Context, r io.Reader, output chan decodeOutput) { defer d.streamWg.Done() br := readerWrapper{r: r} var seqDecode = make(chan *blockDec, d.o.concurrent) var seqExecute = make(chan *blockDec, d.o.concurrent) // Async 1: Decode sequences... go func() { var hist history var hasErr bool for block := range seqDecode { if hasErr { if block != nil { seqExecute <- block } continue } if block.async.newHist != nil { if debugDecoder { println("Async 1: new history, recent:", block.async.newHist.recentOffsets) } hist.reset() hist.decoders = block.async.newHist.decoders hist.recentOffsets = block.async.newHist.recentOffsets hist.windowSize = block.async.newHist.windowSize if block.async.newHist.dict != nil { hist.setDict(block.async.newHist.dict) } } if block.err != nil || block.Type != blockTypeCompressed { hasErr = block.err != nil seqExecute <- block continue } hist.decoders.literals = block.async.literals block.err = block.prepareSequences(block.async.seqData, &hist) if debugDecoder && block.err != nil { println("prepareSequences returned:", block.err) } hasErr = block.err != nil if block.err == nil { block.err = block.decodeSequences(&hist) if debugDecoder && block.err != nil { println("decodeSequences returned:", block.err) } hasErr = block.err != nil // block.async.sequence = hist.decoders.seq[:hist.decoders.nSeqs] block.async.seqSize = hist.decoders.seqSize } seqExecute <- block } close(seqExecute) hist.reset() }() var wg sync.WaitGroup wg.Add(1) // Async 3: Execute sequences... frameHistCache := d.frame.history.b go func() { var hist history var decodedFrame uint64 var fcs uint64 var hasErr bool for block := range seqExecute { out := decodeOutput{err: block.err, d: block} if block.err != nil || hasErr { hasErr = true output <- out continue } if block.async.newHist != nil { if debugDecoder { println("Async 2: new history") } hist.reset() hist.windowSize = block.async.newHist.windowSize hist.allocFrameBuffer = block.async.newHist.allocFrameBuffer if block.async.newHist.dict != nil { hist.setDict(block.async.newHist.dict) } if cap(hist.b) < hist.allocFrameBuffer { if cap(frameHistCache) >= hist.allocFrameBuffer { hist.b = frameHistCache } else { hist.b = make([]byte, 0, hist.allocFrameBuffer) println("Alloc history sized", hist.allocFrameBuffer) } } hist.b = hist.b[:0] fcs = block.async.fcs decodedFrame = 0 } do := decodeOutput{err: block.err, d: block} switch block.Type { case blockTypeRLE: if debugDecoder { println("add rle block length:", block.RLESize) } if cap(block.dst) < int(block.RLESize) { if block.lowMem { block.dst = make([]byte, block.RLESize) } else { block.dst = make([]byte, maxCompressedBlockSize) } } block.dst = block.dst[:block.RLESize] v := block.data[0] for i := range block.dst { block.dst[i] = v } hist.append(block.dst) do.b = block.dst case blockTypeRaw: if debugDecoder { println("add raw block length:", len(block.data)) } hist.append(block.data) do.b = block.data case blockTypeCompressed: if debugDecoder { println("execute with history length:", len(hist.b), "window:", hist.windowSize) } hist.decoders.seqSize = block.async.seqSize hist.decoders.literals = block.async.literals do.err = block.executeSequences(&hist) hasErr = do.err != nil if debugDecoder && hasErr { println("executeSequences returned:", do.err) } do.b = block.dst } if !hasErr { decodedFrame += uint64(len(do.b)) if decodedFrame > fcs { println("fcs exceeded", block.Last, fcs, decodedFrame) do.err = ErrFrameSizeExceeded hasErr = true } else if block.Last && fcs != fcsUnknown && decodedFrame != fcs { do.err = ErrFrameSizeMismatch hasErr = true } else { if debugDecoder { println("fcs ok", block.Last, fcs, decodedFrame) } } } output <- do } close(output) frameHistCache = hist.b wg.Done() if debugDecoder { println("decoder goroutines finished") } hist.reset() }() var hist history decodeStream: for { var hasErr bool hist.reset() decodeBlock := func(block *blockDec) { if hasErr { if block != nil { seqDecode <- block } return } if block.err != nil || block.Type != blockTypeCompressed { hasErr = block.err != nil seqDecode <- block return } remain, err := block.decodeLiterals(block.data, &hist) block.err = err hasErr = block.err != nil if err == nil { block.async.literals = hist.decoders.literals block.async.seqData = remain } else if debugDecoder { println("decodeLiterals error:", err) } seqDecode <- block } frame := d.frame if debugDecoder { println("New frame...") } var historySent bool frame.history.reset() err := frame.reset(&br) if debugDecoder && err != nil { println("Frame decoder returned", err) } if err == nil { err = d.setDict(frame) } if err == nil && d.frame.WindowSize > d.o.maxWindowSize { if debugDecoder { println("decoder size exceeded, fws:", d.frame.WindowSize, "> mws:", d.o.maxWindowSize) } err = ErrDecoderSizeExceeded } if err != nil { select { case <-ctx.Done(): case dec := <-d.decoders: dec.sendErr(err) decodeBlock(dec) } break decodeStream } // Go through all blocks of the frame. for { var dec *blockDec select { case <-ctx.Done(): break decodeStream case dec = <-d.decoders: // Once we have a decoder, we MUST return it. } err := frame.next(dec) if !historySent { h := frame.history if debugDecoder { println("Alloc History:", h.allocFrameBuffer) } hist.reset() if h.dict != nil { hist.setDict(h.dict) } dec.async.newHist = &h dec.async.fcs = frame.FrameContentSize historySent = true } else { dec.async.newHist = nil } if debugDecoder && err != nil { println("next block returned error:", err) } dec.err = err dec.hasCRC = false if dec.Last && frame.HasCheckSum && err == nil { crc, err := frame.rawInput.readSmall(4) if len(crc) < 4 { if err == nil { err = io.ErrUnexpectedEOF } println("CRC missing?", err) dec.err = err } else { dec.checkCRC = binary.LittleEndian.Uint32(crc) dec.hasCRC = true if debugDecoder { printf("found crc to check: %08x\n", dec.checkCRC) } } } err = dec.err last := dec.Last decodeBlock(dec) if err != nil { break decodeStream } if last { break } } } close(seqDecode) wg.Wait() hist.reset() d.frame.history.b = frameHistCache } func (d *Decoder) setDict(frame *frameDec) (err error) { dict, ok := d.dicts[frame.DictionaryID] if ok { if debugDecoder { println("setting dict", frame.DictionaryID) } frame.history.setDict(dict) } else if frame.DictionaryID != 0 { // A zero or missing dictionary id is ambiguous: // either dictionary zero, or no dictionary. In particular, // zstd --patch-from uses this id for the source file, // so only return an error if the dictionary id is not zero. err = ErrUnknownDictionary } return err } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/decoder_options.go ================================================ // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. // Based on work by Yann Collet, released under BSD License. package zstd import ( "errors" "fmt" "math/bits" "runtime" ) // DOption is an option for creating a decoder. type DOption func(*decoderOptions) error // options retains accumulated state of multiple options. type decoderOptions struct { lowMem bool concurrent int maxDecodedSize uint64 maxWindowSize uint64 dicts []*dict ignoreChecksum bool limitToCap bool decodeBufsBelow int } func (o *decoderOptions) setDefault() { *o = decoderOptions{ // use less ram: true for now, but may change. lowMem: true, concurrent: runtime.GOMAXPROCS(0), maxWindowSize: MaxWindowSize, decodeBufsBelow: 128 << 10, } if o.concurrent > 4 { o.concurrent = 4 } o.maxDecodedSize = 64 << 30 } // WithDecoderLowmem will set whether to use a lower amount of memory, // but possibly have to allocate more while running. func WithDecoderLowmem(b bool) DOption { return func(o *decoderOptions) error { o.lowMem = b; return nil } } // WithDecoderConcurrency sets the number of created decoders. // When decoding block with DecodeAll, this will limit the number // of possible concurrently running decodes. // When decoding streams, this will limit the number of // inflight blocks. // When decoding streams and setting maximum to 1, // no async decoding will be done. // When a value of 0 is provided GOMAXPROCS will be used. // By default this will be set to 4 or GOMAXPROCS, whatever is lower. func WithDecoderConcurrency(n int) DOption { return func(o *decoderOptions) error { if n < 0 { return errors.New("concurrency must be at least 1") } if n == 0 { o.concurrent = runtime.GOMAXPROCS(0) } else { o.concurrent = n } return nil } } // WithDecoderMaxMemory allows to set a maximum decoded size for in-memory // non-streaming operations or maximum window size for streaming operations. // This can be used to control memory usage of potentially hostile content. // Maximum is 1 << 63 bytes. Default is 64GiB. func WithDecoderMaxMemory(n uint64) DOption { return func(o *decoderOptions) error { if n == 0 { return errors.New("WithDecoderMaxMemory must be at least 1") } if n > 1<<63 { return errors.New("WithDecoderMaxmemory must be less than 1 << 63") } o.maxDecodedSize = n return nil } } // WithDecoderDicts allows to register one or more dictionaries for the decoder. // // Each slice in dict must be in the [dictionary format] produced by // "zstd --train" from the Zstandard reference implementation. // // If several dictionaries with the same ID are provided, the last one will be used. // // [dictionary format]: https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#dictionary-format func WithDecoderDicts(dicts ...[]byte) DOption { return func(o *decoderOptions) error { for _, b := range dicts { d, err := loadDict(b) if err != nil { return err } o.dicts = append(o.dicts, d) } return nil } } // WithDecoderDictRaw registers a dictionary that may be used by the decoder. // The slice content can be arbitrary data. func WithDecoderDictRaw(id uint32, content []byte) DOption { return func(o *decoderOptions) error { if bits.UintSize > 32 && uint(len(content)) > dictMaxLength { return fmt.Errorf("dictionary of size %d > 2GiB too large", len(content)) } o.dicts = append(o.dicts, &dict{id: id, content: content, offsets: [3]int{1, 4, 8}}) return nil } } // WithDecoderMaxWindow allows to set a maximum window size for decodes. // This allows rejecting packets that will cause big memory usage. // The Decoder will likely allocate more memory based on the WithDecoderLowmem setting. // If WithDecoderMaxMemory is set to a lower value, that will be used. // Default is 512MB, Maximum is ~3.75 TB as per zstandard spec. func WithDecoderMaxWindow(size uint64) DOption { return func(o *decoderOptions) error { if size < MinWindowSize { return errors.New("WithMaxWindowSize must be at least 1KB, 1024 bytes") } if size > (1<<41)+7*(1<<38) { return errors.New("WithMaxWindowSize must be less than (1<<41) + 7*(1<<38) ~ 3.75TB") } o.maxWindowSize = size return nil } } // WithDecodeAllCapLimit will limit DecodeAll to decoding cap(dst)-len(dst) bytes, // or any size set in WithDecoderMaxMemory. // This can be used to limit decoding to a specific maximum output size. // Disabled by default. func WithDecodeAllCapLimit(b bool) DOption { return func(o *decoderOptions) error { o.limitToCap = b return nil } } // WithDecodeBuffersBelow will fully decode readers that have a // `Bytes() []byte` and `Len() int` interface similar to bytes.Buffer. // This typically uses less allocations but will have the full decompressed object in memory. // Note that DecodeAllCapLimit will disable this, as well as giving a size of 0 or less. // Default is 128KiB. func WithDecodeBuffersBelow(size int) DOption { return func(o *decoderOptions) error { o.decodeBufsBelow = size return nil } } // IgnoreChecksum allows to forcibly ignore checksum checking. func IgnoreChecksum(b bool) DOption { return func(o *decoderOptions) error { o.ignoreChecksum = b return nil } } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/dict.go ================================================ package zstd import ( "bytes" "encoding/binary" "errors" "fmt" "io" "math" "sort" "github.com/klauspost/compress/huff0" ) type dict struct { id uint32 litEnc *huff0.Scratch llDec, ofDec, mlDec sequenceDec offsets [3]int content []byte } const dictMagic = "\x37\xa4\x30\xec" // Maximum dictionary size for the reference implementation (1.5.3) is 2 GiB. const dictMaxLength = 1 << 31 // ID returns the dictionary id or 0 if d is nil. func (d *dict) ID() uint32 { if d == nil { return 0 } return d.id } // ContentSize returns the dictionary content size or 0 if d is nil. func (d *dict) ContentSize() int { if d == nil { return 0 } return len(d.content) } // Content returns the dictionary content. func (d *dict) Content() []byte { if d == nil { return nil } return d.content } // Offsets returns the initial offsets. func (d *dict) Offsets() [3]int { if d == nil { return [3]int{} } return d.offsets } // LitEncoder returns the literal encoder. func (d *dict) LitEncoder() *huff0.Scratch { if d == nil { return nil } return d.litEnc } // Load a dictionary as described in // https://github.com/facebook/zstd/blob/master/doc/zstd_compression_format.md#dictionary-format func loadDict(b []byte) (*dict, error) { // Check static field size. if len(b) <= 8+(3*4) { return nil, io.ErrUnexpectedEOF } d := dict{ llDec: sequenceDec{fse: &fseDecoder{}}, ofDec: sequenceDec{fse: &fseDecoder{}}, mlDec: sequenceDec{fse: &fseDecoder{}}, } if string(b[:4]) != dictMagic { return nil, ErrMagicMismatch } d.id = binary.LittleEndian.Uint32(b[4:8]) if d.id == 0 { return nil, errors.New("dictionaries cannot have ID 0") } // Read literal table var err error d.litEnc, b, err = huff0.ReadTable(b[8:], nil) if err != nil { return nil, fmt.Errorf("loading literal table: %w", err) } d.litEnc.Reuse = huff0.ReusePolicyMust br := byteReader{ b: b, off: 0, } readDec := func(i tableIndex, dec *fseDecoder) error { if err := dec.readNCount(&br, uint16(maxTableSymbol[i])); err != nil { return err } if br.overread() { return io.ErrUnexpectedEOF } err = dec.transform(symbolTableX[i]) if err != nil { println("Transform table error:", err) return err } if debugDecoder || debugEncoder { println("Read table ok", "symbolLen:", dec.symbolLen) } // Set decoders as predefined so they aren't reused. dec.preDefined = true return nil } if err := readDec(tableOffsets, d.ofDec.fse); err != nil { return nil, err } if err := readDec(tableMatchLengths, d.mlDec.fse); err != nil { return nil, err } if err := readDec(tableLiteralLengths, d.llDec.fse); err != nil { return nil, err } if br.remain() < 12 { return nil, io.ErrUnexpectedEOF } d.offsets[0] = int(br.Uint32()) br.advance(4) d.offsets[1] = int(br.Uint32()) br.advance(4) d.offsets[2] = int(br.Uint32()) br.advance(4) if d.offsets[0] <= 0 || d.offsets[1] <= 0 || d.offsets[2] <= 0 { return nil, errors.New("invalid offset in dictionary") } d.content = make([]byte, br.remain()) copy(d.content, br.unread()) if d.offsets[0] > len(d.content) || d.offsets[1] > len(d.content) || d.offsets[2] > len(d.content) { return nil, fmt.Errorf("initial offset bigger than dictionary content size %d, offsets: %v", len(d.content), d.offsets) } return &d, nil } // InspectDictionary loads a zstd dictionary and provides functions to inspect the content. func InspectDictionary(b []byte) (interface { ID() uint32 ContentSize() int Content() []byte Offsets() [3]int LitEncoder() *huff0.Scratch }, error) { initPredefined() d, err := loadDict(b) return d, err } type BuildDictOptions struct { // Dictionary ID. ID uint32 // Content to use to create dictionary tables. Contents [][]byte // History to use for all blocks. History []byte // Offsets to use. Offsets [3]int // CompatV155 will make the dictionary compatible with Zstd v1.5.5 and earlier. // See https://github.com/facebook/zstd/issues/3724 CompatV155 bool // Use the specified encoder level. // The dictionary will be built using the specified encoder level, // which will reflect speed and make the dictionary tailored for that level. // If not set SpeedBestCompression will be used. Level EncoderLevel // DebugOut will write stats and other details here if set. DebugOut io.Writer } func BuildDict(o BuildDictOptions) ([]byte, error) { initPredefined() hist := o.History contents := o.Contents debug := o.DebugOut != nil println := func(args ...interface{}) { if o.DebugOut != nil { fmt.Fprintln(o.DebugOut, args...) } } printf := func(s string, args ...interface{}) { if o.DebugOut != nil { fmt.Fprintf(o.DebugOut, s, args...) } } print := func(args ...interface{}) { if o.DebugOut != nil { fmt.Fprint(o.DebugOut, args...) } } if int64(len(hist)) > dictMaxLength { return nil, fmt.Errorf("dictionary of size %d > %d", len(hist), int64(dictMaxLength)) } if len(hist) < 8 { return nil, fmt.Errorf("dictionary of size %d < %d", len(hist), 8) } if len(contents) == 0 { return nil, errors.New("no content provided") } d := dict{ id: o.ID, litEnc: nil, llDec: sequenceDec{}, ofDec: sequenceDec{}, mlDec: sequenceDec{}, offsets: o.Offsets, content: hist, } block := blockEnc{lowMem: false} block.init() enc := encoder(&bestFastEncoder{fastBase: fastBase{maxMatchOff: int32(maxMatchLen), bufferReset: math.MaxInt32 - int32(maxMatchLen*2), lowMem: false}}) if o.Level != 0 { eOpts := encoderOptions{ level: o.Level, blockSize: maxMatchLen, windowSize: maxMatchLen, dict: &d, lowMem: false, } enc = eOpts.encoder() } else { o.Level = SpeedBestCompression } var ( remain [256]int ll [256]int ml [256]int of [256]int ) addValues := func(dst *[256]int, src []byte) { for _, v := range src { dst[v]++ } } addHist := func(dst *[256]int, src *[256]uint32) { for i, v := range src { dst[i] += int(v) } } seqs := 0 nUsed := 0 litTotal := 0 newOffsets := make(map[uint32]int, 1000) for _, b := range contents { block.reset(nil) if len(b) < 8 { continue } nUsed++ enc.Reset(&d, true) enc.Encode(&block, b) addValues(&remain, block.literals) litTotal += len(block.literals) seqs += len(block.sequences) block.genCodes() addHist(&ll, block.coders.llEnc.Histogram()) addHist(&ml, block.coders.mlEnc.Histogram()) addHist(&of, block.coders.ofEnc.Histogram()) for i, seq := range block.sequences { if i > 3 { break } offset := seq.offset if offset == 0 { continue } if offset > 3 { newOffsets[offset-3]++ } else { newOffsets[uint32(o.Offsets[offset-1])]++ } } } // Find most used offsets. var sortedOffsets []uint32 for k := range newOffsets { sortedOffsets = append(sortedOffsets, k) } sort.Slice(sortedOffsets, func(i, j int) bool { a, b := sortedOffsets[i], sortedOffsets[j] if a == b { // Prefer the longer offset return sortedOffsets[i] > sortedOffsets[j] } return newOffsets[sortedOffsets[i]] > newOffsets[sortedOffsets[j]] }) if len(sortedOffsets) > 3 { if debug { print("Offsets:") for i, v := range sortedOffsets { if i > 20 { break } printf("[%d: %d],", v, newOffsets[v]) } println("") } sortedOffsets = sortedOffsets[:3] } for i, v := range sortedOffsets { o.Offsets[i] = int(v) } if debug { println("New repeat offsets", o.Offsets) } if nUsed == 0 || seqs == 0 { return nil, fmt.Errorf("%d blocks, %d sequences found", nUsed, seqs) } if debug { println("Sequences:", seqs, "Blocks:", nUsed, "Literals:", litTotal) } if seqs/nUsed < 512 { // Use 512 as minimum. nUsed = seqs / 512 } copyHist := func(dst *fseEncoder, src *[256]int) ([]byte, error) { hist := dst.Histogram() var maxSym uint8 var maxCount int var fakeLength int for i, v := range src { if v > 0 { v = v / nUsed if v == 0 { v = 1 } } if v > maxCount { maxCount = v } if v != 0 { maxSym = uint8(i) } fakeLength += v hist[i] = uint32(v) } dst.HistogramFinished(maxSym, maxCount) dst.reUsed = false dst.useRLE = false err := dst.normalizeCount(fakeLength) if err != nil { return nil, err } if debug { println("RAW:", dst.count[:maxSym+1], "NORM:", dst.norm[:maxSym+1], "LEN:", fakeLength) } return dst.writeCount(nil) } if debug { print("Literal lengths: ") } llTable, err := copyHist(block.coders.llEnc, &ll) if err != nil { return nil, err } if debug { print("Match lengths: ") } mlTable, err := copyHist(block.coders.mlEnc, &ml) if err != nil { return nil, err } if debug { print("Offsets: ") } ofTable, err := copyHist(block.coders.ofEnc, &of) if err != nil { return nil, err } // Literal table avgSize := litTotal if avgSize > huff0.BlockSizeMax/2 { avgSize = huff0.BlockSizeMax / 2 } huffBuff := make([]byte, 0, avgSize) // Target size div := litTotal / avgSize if div < 1 { div = 1 } if debug { println("Huffman weights:") } for i, n := range remain[:] { if n > 0 { n = n / div // Allow all entries to be represented. if n == 0 { n = 1 } huffBuff = append(huffBuff, bytes.Repeat([]byte{byte(i)}, n)...) if debug { printf("[%d: %d], ", i, n) } } } if o.CompatV155 && remain[255]/div == 0 { huffBuff = append(huffBuff, 255) } scratch := &huff0.Scratch{TableLog: 11} for tries := 0; tries < 255; tries++ { scratch = &huff0.Scratch{TableLog: 11} _, _, err = huff0.Compress1X(huffBuff, scratch) if err == nil { break } if debug { printf("Try %d: Huffman error: %v\n", tries+1, err) } huffBuff = huffBuff[:0] if tries == 250 { if debug { println("Huffman: Bailing out with predefined table") } // Bail out.... Just generate something huffBuff = append(huffBuff, bytes.Repeat([]byte{255}, 10000)...) for i := 0; i < 128; i++ { huffBuff = append(huffBuff, byte(i)) } continue } if errors.Is(err, huff0.ErrIncompressible) { // Try truncating least common. for i, n := range remain[:] { if n > 0 { n = n / (div * (i + 1)) if n > 0 { huffBuff = append(huffBuff, bytes.Repeat([]byte{byte(i)}, n)...) } } } if o.CompatV155 && len(huffBuff) > 0 && huffBuff[len(huffBuff)-1] != 255 { huffBuff = append(huffBuff, 255) } if len(huffBuff) == 0 { huffBuff = append(huffBuff, 0, 255) } } if errors.Is(err, huff0.ErrUseRLE) { for i, n := range remain[:] { n = n / (div * (i + 1)) // Allow all entries to be represented. if n == 0 { n = 1 } huffBuff = append(huffBuff, bytes.Repeat([]byte{byte(i)}, n)...) } } } var out bytes.Buffer out.Write([]byte(dictMagic)) out.Write(binary.LittleEndian.AppendUint32(nil, o.ID)) out.Write(scratch.OutTable) if debug { println("huff table:", len(scratch.OutTable), "bytes") println("of table:", len(ofTable), "bytes") println("ml table:", len(mlTable), "bytes") println("ll table:", len(llTable), "bytes") } out.Write(ofTable) out.Write(mlTable) out.Write(llTable) out.Write(binary.LittleEndian.AppendUint32(nil, uint32(o.Offsets[0]))) out.Write(binary.LittleEndian.AppendUint32(nil, uint32(o.Offsets[1]))) out.Write(binary.LittleEndian.AppendUint32(nil, uint32(o.Offsets[2]))) out.Write(hist) if debug { _, err := loadDict(out.Bytes()) if err != nil { panic(err) } i, err := InspectDictionary(out.Bytes()) if err != nil { panic(err) } println("ID:", i.ID()) println("Content size:", i.ContentSize()) println("Encoder:", i.LitEncoder() != nil) println("Offsets:", i.Offsets()) var totalSize int for _, b := range contents { totalSize += len(b) } encWith := func(opts ...EOption) int { enc, err := NewWriter(nil, opts...) if err != nil { panic(err) } defer enc.Close() var dst []byte var totalSize int for _, b := range contents { dst = enc.EncodeAll(b, dst[:0]) totalSize += len(dst) } return totalSize } plain := encWith(WithEncoderLevel(o.Level)) withDict := encWith(WithEncoderLevel(o.Level), WithEncoderDict(out.Bytes())) println("Input size:", totalSize) println("Plain Compressed:", plain) println("Dict Compressed:", withDict) println("Saved:", plain-withDict, (plain-withDict)/len(contents), "bytes per input (rounded down)") } return out.Bytes(), nil } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/enc_base.go ================================================ package zstd import ( "fmt" "math/bits" "github.com/klauspost/compress/zstd/internal/xxhash" ) const ( dictShardBits = 6 ) type fastBase struct { // cur is the offset at the start of hist cur int32 // maximum offset. Should be at least 2x block size. maxMatchOff int32 bufferReset int32 hist []byte crc *xxhash.Digest tmp [8]byte blk *blockEnc lastDictID uint32 lowMem bool } // CRC returns the underlying CRC writer. func (e *fastBase) CRC() *xxhash.Digest { return e.crc } // AppendCRC will append the CRC to the destination slice and return it. func (e *fastBase) AppendCRC(dst []byte) []byte { crc := e.crc.Sum(e.tmp[:0]) dst = append(dst, crc[7], crc[6], crc[5], crc[4]) return dst } // WindowSize returns the window size of the encoder, // or a window size small enough to contain the input size, if > 0. func (e *fastBase) WindowSize(size int64) int32 { if size > 0 && size < int64(e.maxMatchOff) { b := int32(1) << uint(bits.Len(uint(size))) // Keep minimum window. if b < 1024 { b = 1024 } return b } return e.maxMatchOff } // Block returns the current block. func (e *fastBase) Block() *blockEnc { return e.blk } func (e *fastBase) addBlock(src []byte) int32 { if debugAsserts && e.cur > e.bufferReset { panic(fmt.Sprintf("ecur (%d) > buffer reset (%d)", e.cur, e.bufferReset)) } // check if we have space already if len(e.hist)+len(src) > cap(e.hist) { if cap(e.hist) == 0 { e.ensureHist(len(src)) } else { if cap(e.hist) < int(e.maxMatchOff+maxCompressedBlockSize) { panic(fmt.Errorf("unexpected buffer cap %d, want at least %d with window %d", cap(e.hist), e.maxMatchOff+maxCompressedBlockSize, e.maxMatchOff)) } // Move down offset := int32(len(e.hist)) - e.maxMatchOff copy(e.hist[0:e.maxMatchOff], e.hist[offset:]) e.cur += offset e.hist = e.hist[:e.maxMatchOff] } } s := int32(len(e.hist)) e.hist = append(e.hist, src...) return s } // ensureHist will ensure that history can keep at least this many bytes. func (e *fastBase) ensureHist(n int) { if cap(e.hist) >= n { return } l := e.maxMatchOff if (e.lowMem && e.maxMatchOff > maxCompressedBlockSize) || e.maxMatchOff <= maxCompressedBlockSize { l += maxCompressedBlockSize } else { l += e.maxMatchOff } // Make it at least 1MB. if l < 1<<20 && !e.lowMem { l = 1 << 20 } // Make it at least the requested size. if l < int32(n) { l = int32(n) } e.hist = make([]byte, 0, l) } // useBlock will replace the block with the provided one, // but transfer recent offsets from the previous. func (e *fastBase) UseBlock(enc *blockEnc) { enc.reset(e.blk) e.blk = enc } func (e *fastBase) matchlen(s, t int32, src []byte) int32 { if debugAsserts { if s < 0 { err := fmt.Sprintf("s (%d) < 0", s) panic(err) } if t < 0 { err := fmt.Sprintf("s (%d) < 0", s) panic(err) } if s-t > e.maxMatchOff { err := fmt.Sprintf("s (%d) - t (%d) > maxMatchOff (%d)", s, t, e.maxMatchOff) panic(err) } if len(src)-int(s) > maxCompressedBlockSize { panic(fmt.Sprintf("len(src)-s (%d) > maxCompressedBlockSize (%d)", len(src)-int(s), maxCompressedBlockSize)) } } return int32(matchLen(src[s:], src[t:])) } // Reset the encoding table. func (e *fastBase) resetBase(d *dict, singleBlock bool) { if e.blk == nil { e.blk = &blockEnc{lowMem: e.lowMem} e.blk.init() } else { e.blk.reset(nil) } e.blk.initNewEncode() if e.crc == nil { e.crc = xxhash.New() } else { e.crc.Reset() } e.blk.dictLitEnc = nil if d != nil { low := e.lowMem if singleBlock { e.lowMem = true } e.ensureHist(d.ContentSize() + maxCompressedBlockSize) e.lowMem = low } // We offset current position so everything will be out of reach. // If above reset line, history will be purged. if e.cur < e.bufferReset { e.cur += e.maxMatchOff + int32(len(e.hist)) } e.hist = e.hist[:0] if d != nil { // Set offsets (currently not used) for i, off := range d.offsets { e.blk.recentOffsets[i] = uint32(off) e.blk.prevRecentOffsets[i] = e.blk.recentOffsets[i] } // Transfer litenc. e.blk.dictLitEnc = d.litEnc e.hist = append(e.hist, d.content...) } } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/enc_best.go ================================================ // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. // Based on work by Yann Collet, released under BSD License. package zstd import ( "bytes" "fmt" "github.com/klauspost/compress" ) const ( bestLongTableBits = 22 // Bits used in the long match table bestLongTableSize = 1 << bestLongTableBits // Size of the table bestLongLen = 8 // Bytes used for table hash // Note: Increasing the short table bits or making the hash shorter // can actually lead to compression degradation since it will 'steal' more from the // long match table and match offsets are quite big. // This greatly depends on the type of input. bestShortTableBits = 18 // Bits used in the short match table bestShortTableSize = 1 << bestShortTableBits // Size of the table bestShortLen = 4 // Bytes used for table hash ) type match struct { offset int32 s int32 length int32 rep int32 est int32 } const highScore = maxMatchLen * 8 // estBits will estimate output bits from predefined tables. func (m *match) estBits(bitsPerByte int32) { mlc := mlCode(uint32(m.length - zstdMinMatch)) var ofc uint8 if m.rep < 0 { ofc = ofCode(uint32(m.s-m.offset) + 3) } else { ofc = ofCode(uint32(m.rep) & 3) } // Cost, excluding ofTT, mlTT := fsePredefEnc[tableOffsets].ct.symbolTT[ofc], fsePredefEnc[tableMatchLengths].ct.symbolTT[mlc] // Add cost of match encoding... m.est = int32(ofTT.outBits + mlTT.outBits) m.est += int32(ofTT.deltaNbBits>>16 + mlTT.deltaNbBits>>16) // Subtract savings compared to literal encoding... m.est -= (m.length * bitsPerByte) >> 10 if m.est > 0 { // Unlikely gain.. m.length = 0 m.est = highScore } } // bestFastEncoder uses 2 tables, one for short matches (5 bytes) and one for long matches. // The long match table contains the previous entry with the same hash, // effectively making it a "chain" of length 2. // When we find a long match we choose between the two values and select the longest. // When we find a short match, after checking the long, we check if we can find a long at n+1 // and that it is longer (lazy matching). type bestFastEncoder struct { fastBase table [bestShortTableSize]prevEntry longTable [bestLongTableSize]prevEntry dictTable []prevEntry dictLongTable []prevEntry } // Encode improves compression... func (e *bestFastEncoder) Encode(blk *blockEnc, src []byte) { const ( // Input margin is the number of bytes we read (8) // and the maximum we will read ahead (2) inputMargin = 8 + 4 minNonLiteralBlockSize = 16 ) // Protect against e.cur wraparound. for e.cur >= e.bufferReset-int32(len(e.hist)) { if len(e.hist) == 0 { e.table = [bestShortTableSize]prevEntry{} e.longTable = [bestLongTableSize]prevEntry{} e.cur = e.maxMatchOff break } // Shift down everything in the table that isn't already too far away. minOff := e.cur + int32(len(e.hist)) - e.maxMatchOff for i := range e.table[:] { v := e.table[i].offset v2 := e.table[i].prev if v < minOff { v = 0 v2 = 0 } else { v = v - e.cur + e.maxMatchOff if v2 < minOff { v2 = 0 } else { v2 = v2 - e.cur + e.maxMatchOff } } e.table[i] = prevEntry{ offset: v, prev: v2, } } for i := range e.longTable[:] { v := e.longTable[i].offset v2 := e.longTable[i].prev if v < minOff { v = 0 v2 = 0 } else { v = v - e.cur + e.maxMatchOff if v2 < minOff { v2 = 0 } else { v2 = v2 - e.cur + e.maxMatchOff } } e.longTable[i] = prevEntry{ offset: v, prev: v2, } } e.cur = e.maxMatchOff break } s := e.addBlock(src) blk.size = len(src) if len(src) < minNonLiteralBlockSize { blk.extraLits = len(src) blk.literals = blk.literals[:len(src)] copy(blk.literals, src) return } // Use this to estimate literal cost. // Scaled by 10 bits. bitsPerByte := int32((compress.ShannonEntropyBits(src) * 1024) / len(src)) // Huffman can never go < 1 bit/byte if bitsPerByte < 1024 { bitsPerByte = 1024 } // Override src src = e.hist sLimit := int32(len(src)) - inputMargin const kSearchStrength = 10 // nextEmit is where in src the next emitLiteral should start from. nextEmit := s // Relative offsets offset1 := int32(blk.recentOffsets[0]) offset2 := int32(blk.recentOffsets[1]) offset3 := int32(blk.recentOffsets[2]) addLiterals := func(s *seq, until int32) { if until == nextEmit { return } blk.literals = append(blk.literals, src[nextEmit:until]...) s.litLen = uint32(until - nextEmit) } if debugEncoder { println("recent offsets:", blk.recentOffsets) } encodeLoop: for { // We allow the encoder to optionally turn off repeat offsets across blocks canRepeat := len(blk.sequences) > 2 if debugAsserts && canRepeat && offset1 == 0 { panic("offset0 was 0") } const goodEnough = 250 cv := load6432(src, s) nextHashL := hashLen(cv, bestLongTableBits, bestLongLen) nextHashS := hashLen(cv, bestShortTableBits, bestShortLen) candidateL := e.longTable[nextHashL] candidateS := e.table[nextHashS] // Set m to a match at offset if it looks like that will improve compression. improve := func(m *match, offset int32, s int32, first uint32, rep int32) { delta := s - offset if delta >= e.maxMatchOff || delta <= 0 || load3232(src, offset) != first { return } if debugAsserts { if offset >= s { panic(fmt.Sprintf("offset: %d - s:%d - rep: %d - cur :%d - max: %d", offset, s, rep, e.cur, e.maxMatchOff)) } if !bytes.Equal(src[s:s+4], src[offset:offset+4]) { panic(fmt.Sprintf("first match mismatch: %v != %v, first: %08x", src[s:s+4], src[offset:offset+4], first)) } } // Try to quick reject if we already have a long match. if m.length > 16 { left := len(src) - int(m.s+m.length) // If we are too close to the end, keep as is. if left <= 0 { return } checkLen := m.length - (s - m.s) - 8 if left > 2 && checkLen > 4 { // Check 4 bytes, 4 bytes from the end of the current match. a := load3232(src, offset+checkLen) b := load3232(src, s+checkLen) if a != b { return } } } l := 4 + e.matchlen(s+4, offset+4, src) if true { // Extend candidate match backwards as far as possible. tMin := s - e.maxMatchOff if tMin < 0 { tMin = 0 } for offset > tMin && s > nextEmit && src[offset-1] == src[s-1] && l < maxMatchLength { s-- offset-- l++ } } cand := match{offset: offset, s: s, length: l, rep: rep} cand.estBits(bitsPerByte) if m.est >= highScore || cand.est-m.est+(cand.s-m.s)*bitsPerByte>>10 < 0 { *m = cand } } best := match{s: s, est: highScore} improve(&best, candidateL.offset-e.cur, s, uint32(cv), -1) improve(&best, candidateL.prev-e.cur, s, uint32(cv), -1) improve(&best, candidateS.offset-e.cur, s, uint32(cv), -1) improve(&best, candidateS.prev-e.cur, s, uint32(cv), -1) if canRepeat && best.length < goodEnough { if s == nextEmit { // Check repeats straight after a match. improve(&best, s-offset2, s, uint32(cv), 1|4) improve(&best, s-offset3, s, uint32(cv), 2|4) if offset1 > 1 { improve(&best, s-(offset1-1), s, uint32(cv), 3|4) } } // If either no match or a non-repeat match, check at + 1 if best.rep <= 0 { cv32 := uint32(cv >> 8) spp := s + 1 improve(&best, spp-offset1, spp, cv32, 1) improve(&best, spp-offset2, spp, cv32, 2) improve(&best, spp-offset3, spp, cv32, 3) if best.rep < 0 { cv32 = uint32(cv >> 24) spp += 2 improve(&best, spp-offset1, spp, cv32, 1) improve(&best, spp-offset2, spp, cv32, 2) improve(&best, spp-offset3, spp, cv32, 3) } } } // Load next and check... e.longTable[nextHashL] = prevEntry{offset: s + e.cur, prev: candidateL.offset} e.table[nextHashS] = prevEntry{offset: s + e.cur, prev: candidateS.offset} index0 := s + 1 // Look far ahead, unless we have a really long match already... if best.length < goodEnough { // No match found, move forward on input, no need to check forward... if best.length < 4 { s += 1 + (s-nextEmit)>>(kSearchStrength-1) if s >= sLimit { break encodeLoop } continue } candidateS = e.table[hashLen(cv>>8, bestShortTableBits, bestShortLen)] cv = load6432(src, s+1) cv2 := load6432(src, s+2) candidateL = e.longTable[hashLen(cv, bestLongTableBits, bestLongLen)] candidateL2 := e.longTable[hashLen(cv2, bestLongTableBits, bestLongLen)] // Short at s+1 improve(&best, candidateS.offset-e.cur, s+1, uint32(cv), -1) // Long at s+1, s+2 improve(&best, candidateL.offset-e.cur, s+1, uint32(cv), -1) improve(&best, candidateL.prev-e.cur, s+1, uint32(cv), -1) improve(&best, candidateL2.offset-e.cur, s+2, uint32(cv2), -1) improve(&best, candidateL2.prev-e.cur, s+2, uint32(cv2), -1) if false { // Short at s+3. // Too often worse... improve(&best, e.table[hashLen(cv2>>8, bestShortTableBits, bestShortLen)].offset-e.cur, s+3, uint32(cv2>>8), -1) } // Start check at a fixed offset to allow for a few mismatches. // For this compression level 2 yields the best results. // We cannot do this if we have already indexed this position. const skipBeginning = 2 if best.s > s-skipBeginning { // See if we can find a better match by checking where the current best ends. // Use that offset to see if we can find a better full match. if sAt := best.s + best.length; sAt < sLimit { nextHashL := hashLen(load6432(src, sAt), bestLongTableBits, bestLongLen) candidateEnd := e.longTable[nextHashL] if off := candidateEnd.offset - e.cur - best.length + skipBeginning; off >= 0 { improve(&best, off, best.s+skipBeginning, load3232(src, best.s+skipBeginning), -1) if off := candidateEnd.prev - e.cur - best.length + skipBeginning; off >= 0 { improve(&best, off, best.s+skipBeginning, load3232(src, best.s+skipBeginning), -1) } } } } } if debugAsserts { if !bytes.Equal(src[best.s:best.s+best.length], src[best.offset:best.offset+best.length]) { panic(fmt.Sprintf("match mismatch: %v != %v", src[best.s:best.s+best.length], src[best.offset:best.offset+best.length])) } } // We have a match, we can store the forward value if best.rep > 0 { var seq seq seq.matchLen = uint32(best.length - zstdMinMatch) if debugAsserts && s < nextEmit { panic("s < nextEmit") } addLiterals(&seq, best.s) // Repeat. If bit 4 is set, this is a non-lit repeat. seq.offset = uint32(best.rep & 3) if debugSequences { println("repeat sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) // Index old s + 1 -> s - 1 s = best.s + best.length nextEmit = s // Index skipped... end := s if s > sLimit+4 { end = sLimit + 4 } off := index0 + e.cur for index0 < end { cv0 := load6432(src, index0) h0 := hashLen(cv0, bestLongTableBits, bestLongLen) h1 := hashLen(cv0, bestShortTableBits, bestShortLen) e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset} e.table[h1] = prevEntry{offset: off, prev: e.table[h1].offset} off++ index0++ } switch best.rep { case 2, 4 | 1: offset1, offset2 = offset2, offset1 case 3, 4 | 2: offset1, offset2, offset3 = offset3, offset1, offset2 case 4 | 3: offset1, offset2, offset3 = offset1-1, offset1, offset2 } if s >= sLimit { if debugEncoder { println("repeat ended", s, best.length) } break encodeLoop } continue } // A 4-byte match has been found. Update recent offsets. // We'll later see if more than 4 bytes. s = best.s t := best.offset offset1, offset2, offset3 = s-t, offset1, offset2 if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && int(offset1) > len(src) { panic("invalid offset") } // Write our sequence var seq seq l := best.length seq.litLen = uint32(s - nextEmit) seq.matchLen = uint32(l - zstdMinMatch) if seq.litLen > 0 { blk.literals = append(blk.literals, src[nextEmit:s]...) } seq.offset = uint32(s-t) + 3 s += l if debugSequences { println("sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) nextEmit = s // Index old s + 1 -> s - 1 or sLimit end := s if s > sLimit-4 { end = sLimit - 4 } off := index0 + e.cur for index0 < end { cv0 := load6432(src, index0) h0 := hashLen(cv0, bestLongTableBits, bestLongLen) h1 := hashLen(cv0, bestShortTableBits, bestShortLen) e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset} e.table[h1] = prevEntry{offset: off, prev: e.table[h1].offset} index0++ off++ } if s >= sLimit { break encodeLoop } } if int(nextEmit) < len(src) { blk.literals = append(blk.literals, src[nextEmit:]...) blk.extraLits = len(src) - int(nextEmit) } blk.recentOffsets[0] = uint32(offset1) blk.recentOffsets[1] = uint32(offset2) blk.recentOffsets[2] = uint32(offset3) if debugEncoder { println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits) } } // EncodeNoHist will encode a block with no history and no following blocks. // Most notable difference is that src will not be copied for history and // we do not need to check for max match length. func (e *bestFastEncoder) EncodeNoHist(blk *blockEnc, src []byte) { e.ensureHist(len(src)) e.Encode(blk, src) } // Reset will reset and set a dictionary if not nil func (e *bestFastEncoder) Reset(d *dict, singleBlock bool) { e.resetBase(d, singleBlock) if d == nil { return } // Init or copy dict table if len(e.dictTable) != len(e.table) || d.id != e.lastDictID { if len(e.dictTable) != len(e.table) { e.dictTable = make([]prevEntry, len(e.table)) } end := int32(len(d.content)) - 8 + e.maxMatchOff for i := e.maxMatchOff; i < end; i += 4 { const hashLog = bestShortTableBits cv := load6432(d.content, i-e.maxMatchOff) nextHash := hashLen(cv, hashLog, bestShortLen) // 0 -> 4 nextHash1 := hashLen(cv>>8, hashLog, bestShortLen) // 1 -> 5 nextHash2 := hashLen(cv>>16, hashLog, bestShortLen) // 2 -> 6 nextHash3 := hashLen(cv>>24, hashLog, bestShortLen) // 3 -> 7 e.dictTable[nextHash] = prevEntry{ prev: e.dictTable[nextHash].offset, offset: i, } e.dictTable[nextHash1] = prevEntry{ prev: e.dictTable[nextHash1].offset, offset: i + 1, } e.dictTable[nextHash2] = prevEntry{ prev: e.dictTable[nextHash2].offset, offset: i + 2, } e.dictTable[nextHash3] = prevEntry{ prev: e.dictTable[nextHash3].offset, offset: i + 3, } } e.lastDictID = d.id } // Init or copy dict table if len(e.dictLongTable) != len(e.longTable) || d.id != e.lastDictID { if len(e.dictLongTable) != len(e.longTable) { e.dictLongTable = make([]prevEntry, len(e.longTable)) } if len(d.content) >= 8 { cv := load6432(d.content, 0) h := hashLen(cv, bestLongTableBits, bestLongLen) e.dictLongTable[h] = prevEntry{ offset: e.maxMatchOff, prev: e.dictLongTable[h].offset, } end := int32(len(d.content)) - 8 + e.maxMatchOff off := 8 // First to read for i := e.maxMatchOff + 1; i < end; i++ { cv = cv>>8 | (uint64(d.content[off]) << 56) h := hashLen(cv, bestLongTableBits, bestLongLen) e.dictLongTable[h] = prevEntry{ offset: i, prev: e.dictLongTable[h].offset, } off++ } } e.lastDictID = d.id } // Reset table to initial state copy(e.longTable[:], e.dictLongTable) e.cur = e.maxMatchOff // Reset table to initial state copy(e.table[:], e.dictTable) } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/enc_better.go ================================================ // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. // Based on work by Yann Collet, released under BSD License. package zstd import "fmt" const ( betterLongTableBits = 19 // Bits used in the long match table betterLongTableSize = 1 << betterLongTableBits // Size of the table betterLongLen = 8 // Bytes used for table hash // Note: Increasing the short table bits or making the hash shorter // can actually lead to compression degradation since it will 'steal' more from the // long match table and match offsets are quite big. // This greatly depends on the type of input. betterShortTableBits = 13 // Bits used in the short match table betterShortTableSize = 1 << betterShortTableBits // Size of the table betterShortLen = 5 // Bytes used for table hash betterLongTableShardCnt = 1 << (betterLongTableBits - dictShardBits) // Number of shards in the table betterLongTableShardSize = betterLongTableSize / betterLongTableShardCnt // Size of an individual shard betterShortTableShardCnt = 1 << (betterShortTableBits - dictShardBits) // Number of shards in the table betterShortTableShardSize = betterShortTableSize / betterShortTableShardCnt // Size of an individual shard ) type prevEntry struct { offset int32 prev int32 } // betterFastEncoder uses 2 tables, one for short matches (5 bytes) and one for long matches. // The long match table contains the previous entry with the same hash, // effectively making it a "chain" of length 2. // When we find a long match we choose between the two values and select the longest. // When we find a short match, after checking the long, we check if we can find a long at n+1 // and that it is longer (lazy matching). type betterFastEncoder struct { fastBase table [betterShortTableSize]tableEntry longTable [betterLongTableSize]prevEntry } type betterFastEncoderDict struct { betterFastEncoder dictTable []tableEntry dictLongTable []prevEntry shortTableShardDirty [betterShortTableShardCnt]bool longTableShardDirty [betterLongTableShardCnt]bool allDirty bool } // Encode improves compression... func (e *betterFastEncoder) Encode(blk *blockEnc, src []byte) { const ( // Input margin is the number of bytes we read (8) // and the maximum we will read ahead (2) inputMargin = 8 + 2 minNonLiteralBlockSize = 16 ) // Protect against e.cur wraparound. for e.cur >= e.bufferReset-int32(len(e.hist)) { if len(e.hist) == 0 { e.table = [betterShortTableSize]tableEntry{} e.longTable = [betterLongTableSize]prevEntry{} e.cur = e.maxMatchOff break } // Shift down everything in the table that isn't already too far away. minOff := e.cur + int32(len(e.hist)) - e.maxMatchOff for i := range e.table[:] { v := e.table[i].offset if v < minOff { v = 0 } else { v = v - e.cur + e.maxMatchOff } e.table[i].offset = v } for i := range e.longTable[:] { v := e.longTable[i].offset v2 := e.longTable[i].prev if v < minOff { v = 0 v2 = 0 } else { v = v - e.cur + e.maxMatchOff if v2 < minOff { v2 = 0 } else { v2 = v2 - e.cur + e.maxMatchOff } } e.longTable[i] = prevEntry{ offset: v, prev: v2, } } e.cur = e.maxMatchOff break } s := e.addBlock(src) blk.size = len(src) if len(src) < minNonLiteralBlockSize { blk.extraLits = len(src) blk.literals = blk.literals[:len(src)] copy(blk.literals, src) return } // Override src src = e.hist sLimit := int32(len(src)) - inputMargin // stepSize is the number of bytes to skip on every main loop iteration. // It should be >= 1. const stepSize = 1 const kSearchStrength = 9 // nextEmit is where in src the next emitLiteral should start from. nextEmit := s cv := load6432(src, s) // Relative offsets offset1 := int32(blk.recentOffsets[0]) offset2 := int32(blk.recentOffsets[1]) addLiterals := func(s *seq, until int32) { if until == nextEmit { return } blk.literals = append(blk.literals, src[nextEmit:until]...) s.litLen = uint32(until - nextEmit) } if debugEncoder { println("recent offsets:", blk.recentOffsets) } encodeLoop: for { var t int32 // We allow the encoder to optionally turn off repeat offsets across blocks canRepeat := len(blk.sequences) > 2 var matched, index0 int32 for { if debugAsserts && canRepeat && offset1 == 0 { panic("offset0 was 0") } nextHashL := hashLen(cv, betterLongTableBits, betterLongLen) nextHashS := hashLen(cv, betterShortTableBits, betterShortLen) candidateL := e.longTable[nextHashL] candidateS := e.table[nextHashS] const repOff = 1 repIndex := s - offset1 + repOff off := s + e.cur e.longTable[nextHashL] = prevEntry{offset: off, prev: candidateL.offset} e.table[nextHashS] = tableEntry{offset: off, val: uint32(cv)} index0 = s + 1 if canRepeat { if repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>(repOff*8)) { // Consider history as well. var seq seq lenght := 4 + e.matchlen(s+4+repOff, repIndex+4, src) seq.matchLen = uint32(lenght - zstdMinMatch) // We might be able to match backwards. // Extend as long as we can. start := s + repOff // We end the search early, so we don't risk 0 literals // and have to do special offset treatment. startLimit := nextEmit + 1 tMin := s - e.maxMatchOff if tMin < 0 { tMin = 0 } for repIndex > tMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch-1 { repIndex-- start-- seq.matchLen++ } addLiterals(&seq, start) // rep 0 seq.offset = 1 if debugSequences { println("repeat sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) // Index match start+1 (long) -> s - 1 index0 := s + repOff s += lenght + repOff nextEmit = s if s >= sLimit { if debugEncoder { println("repeat ended", s, lenght) } break encodeLoop } // Index skipped... for index0 < s-1 { cv0 := load6432(src, index0) cv1 := cv0 >> 8 h0 := hashLen(cv0, betterLongTableBits, betterLongLen) off := index0 + e.cur e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset} e.table[hashLen(cv1, betterShortTableBits, betterShortLen)] = tableEntry{offset: off + 1, val: uint32(cv1)} index0 += 2 } cv = load6432(src, s) continue } const repOff2 = 1 // We deviate from the reference encoder and also check offset 2. // Still slower and not much better, so disabled. // repIndex = s - offset2 + repOff2 if false && repIndex >= 0 && load6432(src, repIndex) == load6432(src, s+repOff) { // Consider history as well. var seq seq lenght := 8 + e.matchlen(s+8+repOff2, repIndex+8, src) seq.matchLen = uint32(lenght - zstdMinMatch) // We might be able to match backwards. // Extend as long as we can. start := s + repOff2 // We end the search early, so we don't risk 0 literals // and have to do special offset treatment. startLimit := nextEmit + 1 tMin := s - e.maxMatchOff if tMin < 0 { tMin = 0 } for repIndex > tMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch-1 { repIndex-- start-- seq.matchLen++ } addLiterals(&seq, start) // rep 2 seq.offset = 2 if debugSequences { println("repeat sequence 2", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) s += lenght + repOff2 nextEmit = s if s >= sLimit { if debugEncoder { println("repeat ended", s, lenght) } break encodeLoop } // Index skipped... for index0 < s-1 { cv0 := load6432(src, index0) cv1 := cv0 >> 8 h0 := hashLen(cv0, betterLongTableBits, betterLongLen) off := index0 + e.cur e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset} e.table[hashLen(cv1, betterShortTableBits, betterShortLen)] = tableEntry{offset: off + 1, val: uint32(cv1)} index0 += 2 } cv = load6432(src, s) // Swap offsets offset1, offset2 = offset2, offset1 continue } } // Find the offsets of our two matches. coffsetL := candidateL.offset - e.cur coffsetLP := candidateL.prev - e.cur // Check if we have a long match. if s-coffsetL < e.maxMatchOff && cv == load6432(src, coffsetL) { // Found a long match, at least 8 bytes. matched = e.matchlen(s+8, coffsetL+8, src) + 8 t = coffsetL if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && s-t > e.maxMatchOff { panic("s - t >e.maxMatchOff") } if debugMatches { println("long match") } if s-coffsetLP < e.maxMatchOff && cv == load6432(src, coffsetLP) { // Found a long match, at least 8 bytes. prevMatch := e.matchlen(s+8, coffsetLP+8, src) + 8 if prevMatch > matched { matched = prevMatch t = coffsetLP } if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && s-t > e.maxMatchOff { panic("s - t >e.maxMatchOff") } if debugMatches { println("long match") } } break } // Check if we have a long match on prev. if s-coffsetLP < e.maxMatchOff && cv == load6432(src, coffsetLP) { // Found a long match, at least 8 bytes. matched = e.matchlen(s+8, coffsetLP+8, src) + 8 t = coffsetLP if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && s-t > e.maxMatchOff { panic("s - t >e.maxMatchOff") } if debugMatches { println("long match") } break } coffsetS := candidateS.offset - e.cur // Check if we have a short match. if s-coffsetS < e.maxMatchOff && uint32(cv) == candidateS.val { // found a regular match matched = e.matchlen(s+4, coffsetS+4, src) + 4 // See if we can find a long match at s+1 const checkAt = 1 cv := load6432(src, s+checkAt) nextHashL = hashLen(cv, betterLongTableBits, betterLongLen) candidateL = e.longTable[nextHashL] coffsetL = candidateL.offset - e.cur // We can store it, since we have at least a 4 byte match. e.longTable[nextHashL] = prevEntry{offset: s + checkAt + e.cur, prev: candidateL.offset} if s-coffsetL < e.maxMatchOff && cv == load6432(src, coffsetL) { // Found a long match, at least 8 bytes. matchedNext := e.matchlen(s+8+checkAt, coffsetL+8, src) + 8 if matchedNext > matched { t = coffsetL s += checkAt matched = matchedNext if debugMatches { println("long match (after short)") } break } } // Check prev long... coffsetL = candidateL.prev - e.cur if s-coffsetL < e.maxMatchOff && cv == load6432(src, coffsetL) { // Found a long match, at least 8 bytes. matchedNext := e.matchlen(s+8+checkAt, coffsetL+8, src) + 8 if matchedNext > matched { t = coffsetL s += checkAt matched = matchedNext if debugMatches { println("prev long match (after short)") } break } } t = coffsetS if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && s-t > e.maxMatchOff { panic("s - t >e.maxMatchOff") } if debugAsserts && t < 0 { panic("t<0") } if debugMatches { println("short match") } break } // No match found, move forward in input. s += stepSize + ((s - nextEmit) >> (kSearchStrength - 1)) if s >= sLimit { break encodeLoop } cv = load6432(src, s) } // Try to find a better match by searching for a long match at the end of the current best match if s+matched < sLimit { // Allow some bytes at the beginning to mismatch. // Sweet spot is around 3 bytes, but depends on input. // The skipped bytes are tested in Extend backwards, // and still picked up as part of the match if they do. const skipBeginning = 3 nextHashL := hashLen(load6432(src, s+matched), betterLongTableBits, betterLongLen) s2 := s + skipBeginning cv := load3232(src, s2) candidateL := e.longTable[nextHashL] coffsetL := candidateL.offset - e.cur - matched + skipBeginning if coffsetL >= 0 && coffsetL < s2 && s2-coffsetL < e.maxMatchOff && cv == load3232(src, coffsetL) { // Found a long match, at least 4 bytes. matchedNext := e.matchlen(s2+4, coffsetL+4, src) + 4 if matchedNext > matched { t = coffsetL s = s2 matched = matchedNext if debugMatches { println("long match at end-of-match") } } } // Check prev long... if true { coffsetL = candidateL.prev - e.cur - matched + skipBeginning if coffsetL >= 0 && coffsetL < s2 && s2-coffsetL < e.maxMatchOff && cv == load3232(src, coffsetL) { // Found a long match, at least 4 bytes. matchedNext := e.matchlen(s2+4, coffsetL+4, src) + 4 if matchedNext > matched { t = coffsetL s = s2 matched = matchedNext if debugMatches { println("prev long match at end-of-match") } } } } } // A match has been found. Update recent offsets. offset2 = offset1 offset1 = s - t if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && canRepeat && int(offset1) > len(src) { panic("invalid offset") } // Extend the n-byte match as long as possible. l := matched // Extend backwards tMin := s - e.maxMatchOff if tMin < 0 { tMin = 0 } for t > tMin && s > nextEmit && src[t-1] == src[s-1] && l < maxMatchLength { s-- t-- l++ } // Write our sequence var seq seq seq.litLen = uint32(s - nextEmit) seq.matchLen = uint32(l - zstdMinMatch) if seq.litLen > 0 { blk.literals = append(blk.literals, src[nextEmit:s]...) } seq.offset = uint32(s-t) + 3 s += l if debugSequences { println("sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) nextEmit = s if s >= sLimit { break encodeLoop } // Index match start+1 (long) -> s - 1 off := index0 + e.cur for index0 < s-1 { cv0 := load6432(src, index0) cv1 := cv0 >> 8 h0 := hashLen(cv0, betterLongTableBits, betterLongLen) e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset} e.table[hashLen(cv1, betterShortTableBits, betterShortLen)] = tableEntry{offset: off + 1, val: uint32(cv1)} index0 += 2 off += 2 } cv = load6432(src, s) if !canRepeat { continue } // Check offset 2 for { o2 := s - offset2 if load3232(src, o2) != uint32(cv) { // Do regular search break } // Store this, since we have it. nextHashL := hashLen(cv, betterLongTableBits, betterLongLen) nextHashS := hashLen(cv, betterShortTableBits, betterShortLen) // We have at least 4 byte match. // No need to check backwards. We come straight from a match l := 4 + e.matchlen(s+4, o2+4, src) e.longTable[nextHashL] = prevEntry{offset: s + e.cur, prev: e.longTable[nextHashL].offset} e.table[nextHashS] = tableEntry{offset: s + e.cur, val: uint32(cv)} seq.matchLen = uint32(l) - zstdMinMatch seq.litLen = 0 // Since litlen is always 0, this is offset 1. seq.offset = 1 s += l nextEmit = s if debugSequences { println("sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) // Swap offset 1 and 2. offset1, offset2 = offset2, offset1 if s >= sLimit { // Finished break encodeLoop } cv = load6432(src, s) } } if int(nextEmit) < len(src) { blk.literals = append(blk.literals, src[nextEmit:]...) blk.extraLits = len(src) - int(nextEmit) } blk.recentOffsets[0] = uint32(offset1) blk.recentOffsets[1] = uint32(offset2) if debugEncoder { println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits) } } // EncodeNoHist will encode a block with no history and no following blocks. // Most notable difference is that src will not be copied for history and // we do not need to check for max match length. func (e *betterFastEncoder) EncodeNoHist(blk *blockEnc, src []byte) { e.ensureHist(len(src)) e.Encode(blk, src) } // Encode improves compression... func (e *betterFastEncoderDict) Encode(blk *blockEnc, src []byte) { const ( // Input margin is the number of bytes we read (8) // and the maximum we will read ahead (2) inputMargin = 8 + 2 minNonLiteralBlockSize = 16 ) // Protect against e.cur wraparound. for e.cur >= e.bufferReset-int32(len(e.hist)) { if len(e.hist) == 0 { for i := range e.table[:] { e.table[i] = tableEntry{} } for i := range e.longTable[:] { e.longTable[i] = prevEntry{} } e.cur = e.maxMatchOff e.allDirty = true break } // Shift down everything in the table that isn't already too far away. minOff := e.cur + int32(len(e.hist)) - e.maxMatchOff for i := range e.table[:] { v := e.table[i].offset if v < minOff { v = 0 } else { v = v - e.cur + e.maxMatchOff } e.table[i].offset = v } for i := range e.longTable[:] { v := e.longTable[i].offset v2 := e.longTable[i].prev if v < minOff { v = 0 v2 = 0 } else { v = v - e.cur + e.maxMatchOff if v2 < minOff { v2 = 0 } else { v2 = v2 - e.cur + e.maxMatchOff } } e.longTable[i] = prevEntry{ offset: v, prev: v2, } } e.allDirty = true e.cur = e.maxMatchOff break } s := e.addBlock(src) blk.size = len(src) if len(src) < minNonLiteralBlockSize { blk.extraLits = len(src) blk.literals = blk.literals[:len(src)] copy(blk.literals, src) return } // Override src src = e.hist sLimit := int32(len(src)) - inputMargin // stepSize is the number of bytes to skip on every main loop iteration. // It should be >= 1. const stepSize = 1 const kSearchStrength = 9 // nextEmit is where in src the next emitLiteral should start from. nextEmit := s cv := load6432(src, s) // Relative offsets offset1 := int32(blk.recentOffsets[0]) offset2 := int32(blk.recentOffsets[1]) addLiterals := func(s *seq, until int32) { if until == nextEmit { return } blk.literals = append(blk.literals, src[nextEmit:until]...) s.litLen = uint32(until - nextEmit) } if debugEncoder { println("recent offsets:", blk.recentOffsets) } encodeLoop: for { var t int32 // We allow the encoder to optionally turn off repeat offsets across blocks canRepeat := len(blk.sequences) > 2 var matched, index0 int32 for { if debugAsserts && canRepeat && offset1 == 0 { panic("offset0 was 0") } nextHashL := hashLen(cv, betterLongTableBits, betterLongLen) nextHashS := hashLen(cv, betterShortTableBits, betterShortLen) candidateL := e.longTable[nextHashL] candidateS := e.table[nextHashS] const repOff = 1 repIndex := s - offset1 + repOff off := s + e.cur e.longTable[nextHashL] = prevEntry{offset: off, prev: candidateL.offset} e.markLongShardDirty(nextHashL) e.table[nextHashS] = tableEntry{offset: off, val: uint32(cv)} e.markShortShardDirty(nextHashS) index0 = s + 1 if canRepeat { if repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>(repOff*8)) { // Consider history as well. var seq seq lenght := 4 + e.matchlen(s+4+repOff, repIndex+4, src) seq.matchLen = uint32(lenght - zstdMinMatch) // We might be able to match backwards. // Extend as long as we can. start := s + repOff // We end the search early, so we don't risk 0 literals // and have to do special offset treatment. startLimit := nextEmit + 1 tMin := s - e.maxMatchOff if tMin < 0 { tMin = 0 } for repIndex > tMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch-1 { repIndex-- start-- seq.matchLen++ } addLiterals(&seq, start) // rep 0 seq.offset = 1 if debugSequences { println("repeat sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) // Index match start+1 (long) -> s - 1 s += lenght + repOff nextEmit = s if s >= sLimit { if debugEncoder { println("repeat ended", s, lenght) } break encodeLoop } // Index skipped... for index0 < s-1 { cv0 := load6432(src, index0) cv1 := cv0 >> 8 h0 := hashLen(cv0, betterLongTableBits, betterLongLen) off := index0 + e.cur e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset} e.markLongShardDirty(h0) h1 := hashLen(cv1, betterShortTableBits, betterShortLen) e.table[h1] = tableEntry{offset: off + 1, val: uint32(cv1)} e.markShortShardDirty(h1) index0 += 2 } cv = load6432(src, s) continue } const repOff2 = 1 // We deviate from the reference encoder and also check offset 2. // Still slower and not much better, so disabled. // repIndex = s - offset2 + repOff2 if false && repIndex >= 0 && load6432(src, repIndex) == load6432(src, s+repOff) { // Consider history as well. var seq seq lenght := 8 + e.matchlen(s+8+repOff2, repIndex+8, src) seq.matchLen = uint32(lenght - zstdMinMatch) // We might be able to match backwards. // Extend as long as we can. start := s + repOff2 // We end the search early, so we don't risk 0 literals // and have to do special offset treatment. startLimit := nextEmit + 1 tMin := s - e.maxMatchOff if tMin < 0 { tMin = 0 } for repIndex > tMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch-1 { repIndex-- start-- seq.matchLen++ } addLiterals(&seq, start) // rep 2 seq.offset = 2 if debugSequences { println("repeat sequence 2", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) s += lenght + repOff2 nextEmit = s if s >= sLimit { if debugEncoder { println("repeat ended", s, lenght) } break encodeLoop } // Index skipped... for index0 < s-1 { cv0 := load6432(src, index0) cv1 := cv0 >> 8 h0 := hashLen(cv0, betterLongTableBits, betterLongLen) off := index0 + e.cur e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset} e.markLongShardDirty(h0) h1 := hashLen(cv1, betterShortTableBits, betterShortLen) e.table[h1] = tableEntry{offset: off + 1, val: uint32(cv1)} e.markShortShardDirty(h1) index0 += 2 } cv = load6432(src, s) // Swap offsets offset1, offset2 = offset2, offset1 continue } } // Find the offsets of our two matches. coffsetL := candidateL.offset - e.cur coffsetLP := candidateL.prev - e.cur // Check if we have a long match. if s-coffsetL < e.maxMatchOff && cv == load6432(src, coffsetL) { // Found a long match, at least 8 bytes. matched = e.matchlen(s+8, coffsetL+8, src) + 8 t = coffsetL if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && s-t > e.maxMatchOff { panic("s - t >e.maxMatchOff") } if debugMatches { println("long match") } if s-coffsetLP < e.maxMatchOff && cv == load6432(src, coffsetLP) { // Found a long match, at least 8 bytes. prevMatch := e.matchlen(s+8, coffsetLP+8, src) + 8 if prevMatch > matched { matched = prevMatch t = coffsetLP } if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && s-t > e.maxMatchOff { panic("s - t >e.maxMatchOff") } if debugMatches { println("long match") } } break } // Check if we have a long match on prev. if s-coffsetLP < e.maxMatchOff && cv == load6432(src, coffsetLP) { // Found a long match, at least 8 bytes. matched = e.matchlen(s+8, coffsetLP+8, src) + 8 t = coffsetLP if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && s-t > e.maxMatchOff { panic("s - t >e.maxMatchOff") } if debugMatches { println("long match") } break } coffsetS := candidateS.offset - e.cur // Check if we have a short match. if s-coffsetS < e.maxMatchOff && uint32(cv) == candidateS.val { // found a regular match matched = e.matchlen(s+4, coffsetS+4, src) + 4 // See if we can find a long match at s+1 const checkAt = 1 cv := load6432(src, s+checkAt) nextHashL = hashLen(cv, betterLongTableBits, betterLongLen) candidateL = e.longTable[nextHashL] coffsetL = candidateL.offset - e.cur // We can store it, since we have at least a 4 byte match. e.longTable[nextHashL] = prevEntry{offset: s + checkAt + e.cur, prev: candidateL.offset} e.markLongShardDirty(nextHashL) if s-coffsetL < e.maxMatchOff && cv == load6432(src, coffsetL) { // Found a long match, at least 8 bytes. matchedNext := e.matchlen(s+8+checkAt, coffsetL+8, src) + 8 if matchedNext > matched { t = coffsetL s += checkAt matched = matchedNext if debugMatches { println("long match (after short)") } break } } // Check prev long... coffsetL = candidateL.prev - e.cur if s-coffsetL < e.maxMatchOff && cv == load6432(src, coffsetL) { // Found a long match, at least 8 bytes. matchedNext := e.matchlen(s+8+checkAt, coffsetL+8, src) + 8 if matchedNext > matched { t = coffsetL s += checkAt matched = matchedNext if debugMatches { println("prev long match (after short)") } break } } t = coffsetS if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && s-t > e.maxMatchOff { panic("s - t >e.maxMatchOff") } if debugAsserts && t < 0 { panic("t<0") } if debugMatches { println("short match") } break } // No match found, move forward in input. s += stepSize + ((s - nextEmit) >> (kSearchStrength - 1)) if s >= sLimit { break encodeLoop } cv = load6432(src, s) } // Try to find a better match by searching for a long match at the end of the current best match if s+matched < sLimit { nextHashL := hashLen(load6432(src, s+matched), betterLongTableBits, betterLongLen) cv := load3232(src, s) candidateL := e.longTable[nextHashL] coffsetL := candidateL.offset - e.cur - matched if coffsetL >= 0 && coffsetL < s && s-coffsetL < e.maxMatchOff && cv == load3232(src, coffsetL) { // Found a long match, at least 4 bytes. matchedNext := e.matchlen(s+4, coffsetL+4, src) + 4 if matchedNext > matched { t = coffsetL matched = matchedNext if debugMatches { println("long match at end-of-match") } } } // Check prev long... if true { coffsetL = candidateL.prev - e.cur - matched if coffsetL >= 0 && coffsetL < s && s-coffsetL < e.maxMatchOff && cv == load3232(src, coffsetL) { // Found a long match, at least 4 bytes. matchedNext := e.matchlen(s+4, coffsetL+4, src) + 4 if matchedNext > matched { t = coffsetL matched = matchedNext if debugMatches { println("prev long match at end-of-match") } } } } } // A match has been found. Update recent offsets. offset2 = offset1 offset1 = s - t if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && canRepeat && int(offset1) > len(src) { panic("invalid offset") } // Extend the n-byte match as long as possible. l := matched // Extend backwards tMin := s - e.maxMatchOff if tMin < 0 { tMin = 0 } for t > tMin && s > nextEmit && src[t-1] == src[s-1] && l < maxMatchLength { s-- t-- l++ } // Write our sequence var seq seq seq.litLen = uint32(s - nextEmit) seq.matchLen = uint32(l - zstdMinMatch) if seq.litLen > 0 { blk.literals = append(blk.literals, src[nextEmit:s]...) } seq.offset = uint32(s-t) + 3 s += l if debugSequences { println("sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) nextEmit = s if s >= sLimit { break encodeLoop } // Index match start+1 (long) -> s - 1 off := index0 + e.cur for index0 < s-1 { cv0 := load6432(src, index0) cv1 := cv0 >> 8 h0 := hashLen(cv0, betterLongTableBits, betterLongLen) e.longTable[h0] = prevEntry{offset: off, prev: e.longTable[h0].offset} e.markLongShardDirty(h0) h1 := hashLen(cv1, betterShortTableBits, betterShortLen) e.table[h1] = tableEntry{offset: off + 1, val: uint32(cv1)} e.markShortShardDirty(h1) index0 += 2 off += 2 } cv = load6432(src, s) if !canRepeat { continue } // Check offset 2 for { o2 := s - offset2 if load3232(src, o2) != uint32(cv) { // Do regular search break } // Store this, since we have it. nextHashL := hashLen(cv, betterLongTableBits, betterLongLen) nextHashS := hashLen(cv, betterShortTableBits, betterShortLen) // We have at least 4 byte match. // No need to check backwards. We come straight from a match l := 4 + e.matchlen(s+4, o2+4, src) e.longTable[nextHashL] = prevEntry{offset: s + e.cur, prev: e.longTable[nextHashL].offset} e.markLongShardDirty(nextHashL) e.table[nextHashS] = tableEntry{offset: s + e.cur, val: uint32(cv)} e.markShortShardDirty(nextHashS) seq.matchLen = uint32(l) - zstdMinMatch seq.litLen = 0 // Since litlen is always 0, this is offset 1. seq.offset = 1 s += l nextEmit = s if debugSequences { println("sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) // Swap offset 1 and 2. offset1, offset2 = offset2, offset1 if s >= sLimit { // Finished break encodeLoop } cv = load6432(src, s) } } if int(nextEmit) < len(src) { blk.literals = append(blk.literals, src[nextEmit:]...) blk.extraLits = len(src) - int(nextEmit) } blk.recentOffsets[0] = uint32(offset1) blk.recentOffsets[1] = uint32(offset2) if debugEncoder { println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits) } } // ResetDict will reset and set a dictionary if not nil func (e *betterFastEncoder) Reset(d *dict, singleBlock bool) { e.resetBase(d, singleBlock) if d != nil { panic("betterFastEncoder: Reset with dict") } } // ResetDict will reset and set a dictionary if not nil func (e *betterFastEncoderDict) Reset(d *dict, singleBlock bool) { e.resetBase(d, singleBlock) if d == nil { return } // Init or copy dict table if len(e.dictTable) != len(e.table) || d.id != e.lastDictID { if len(e.dictTable) != len(e.table) { e.dictTable = make([]tableEntry, len(e.table)) } end := int32(len(d.content)) - 8 + e.maxMatchOff for i := e.maxMatchOff; i < end; i += 4 { const hashLog = betterShortTableBits cv := load6432(d.content, i-e.maxMatchOff) nextHash := hashLen(cv, hashLog, betterShortLen) // 0 -> 4 nextHash1 := hashLen(cv>>8, hashLog, betterShortLen) // 1 -> 5 nextHash2 := hashLen(cv>>16, hashLog, betterShortLen) // 2 -> 6 nextHash3 := hashLen(cv>>24, hashLog, betterShortLen) // 3 -> 7 e.dictTable[nextHash] = tableEntry{ val: uint32(cv), offset: i, } e.dictTable[nextHash1] = tableEntry{ val: uint32(cv >> 8), offset: i + 1, } e.dictTable[nextHash2] = tableEntry{ val: uint32(cv >> 16), offset: i + 2, } e.dictTable[nextHash3] = tableEntry{ val: uint32(cv >> 24), offset: i + 3, } } e.lastDictID = d.id e.allDirty = true } // Init or copy dict table if len(e.dictLongTable) != len(e.longTable) || d.id != e.lastDictID { if len(e.dictLongTable) != len(e.longTable) { e.dictLongTable = make([]prevEntry, len(e.longTable)) } if len(d.content) >= 8 { cv := load6432(d.content, 0) h := hashLen(cv, betterLongTableBits, betterLongLen) e.dictLongTable[h] = prevEntry{ offset: e.maxMatchOff, prev: e.dictLongTable[h].offset, } end := int32(len(d.content)) - 8 + e.maxMatchOff off := 8 // First to read for i := e.maxMatchOff + 1; i < end; i++ { cv = cv>>8 | (uint64(d.content[off]) << 56) h := hashLen(cv, betterLongTableBits, betterLongLen) e.dictLongTable[h] = prevEntry{ offset: i, prev: e.dictLongTable[h].offset, } off++ } } e.lastDictID = d.id e.allDirty = true } // Reset table to initial state { dirtyShardCnt := 0 if !e.allDirty { for i := range e.shortTableShardDirty { if e.shortTableShardDirty[i] { dirtyShardCnt++ } } } const shardCnt = betterShortTableShardCnt const shardSize = betterShortTableShardSize if e.allDirty || dirtyShardCnt > shardCnt*4/6 { copy(e.table[:], e.dictTable) for i := range e.shortTableShardDirty { e.shortTableShardDirty[i] = false } } else { for i := range e.shortTableShardDirty { if !e.shortTableShardDirty[i] { continue } copy(e.table[i*shardSize:(i+1)*shardSize], e.dictTable[i*shardSize:(i+1)*shardSize]) e.shortTableShardDirty[i] = false } } } { dirtyShardCnt := 0 if !e.allDirty { for i := range e.shortTableShardDirty { if e.shortTableShardDirty[i] { dirtyShardCnt++ } } } const shardCnt = betterLongTableShardCnt const shardSize = betterLongTableShardSize if e.allDirty || dirtyShardCnt > shardCnt*4/6 { copy(e.longTable[:], e.dictLongTable) for i := range e.longTableShardDirty { e.longTableShardDirty[i] = false } } else { for i := range e.longTableShardDirty { if !e.longTableShardDirty[i] { continue } copy(e.longTable[i*shardSize:(i+1)*shardSize], e.dictLongTable[i*shardSize:(i+1)*shardSize]) e.longTableShardDirty[i] = false } } } e.cur = e.maxMatchOff e.allDirty = false } func (e *betterFastEncoderDict) markLongShardDirty(entryNum uint32) { e.longTableShardDirty[entryNum/betterLongTableShardSize] = true } func (e *betterFastEncoderDict) markShortShardDirty(entryNum uint32) { e.shortTableShardDirty[entryNum/betterShortTableShardSize] = true } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/enc_dfast.go ================================================ // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. // Based on work by Yann Collet, released under BSD License. package zstd import "fmt" const ( dFastLongTableBits = 17 // Bits used in the long match table dFastLongTableSize = 1 << dFastLongTableBits // Size of the table dFastLongTableMask = dFastLongTableSize - 1 // Mask for table indices. Redundant, but can eliminate bounds checks. dFastLongLen = 8 // Bytes used for table hash dLongTableShardCnt = 1 << (dFastLongTableBits - dictShardBits) // Number of shards in the table dLongTableShardSize = dFastLongTableSize / tableShardCnt // Size of an individual shard dFastShortTableBits = tableBits // Bits used in the short match table dFastShortTableSize = 1 << dFastShortTableBits // Size of the table dFastShortTableMask = dFastShortTableSize - 1 // Mask for table indices. Redundant, but can eliminate bounds checks. dFastShortLen = 5 // Bytes used for table hash ) type doubleFastEncoder struct { fastEncoder longTable [dFastLongTableSize]tableEntry } type doubleFastEncoderDict struct { fastEncoderDict longTable [dFastLongTableSize]tableEntry dictLongTable []tableEntry longTableShardDirty [dLongTableShardCnt]bool } // Encode mimmics functionality in zstd_dfast.c func (e *doubleFastEncoder) Encode(blk *blockEnc, src []byte) { const ( // Input margin is the number of bytes we read (8) // and the maximum we will read ahead (2) inputMargin = 8 + 2 minNonLiteralBlockSize = 16 ) // Protect against e.cur wraparound. for e.cur >= e.bufferReset-int32(len(e.hist)) { if len(e.hist) == 0 { e.table = [dFastShortTableSize]tableEntry{} e.longTable = [dFastLongTableSize]tableEntry{} e.cur = e.maxMatchOff break } // Shift down everything in the table that isn't already too far away. minOff := e.cur + int32(len(e.hist)) - e.maxMatchOff for i := range e.table[:] { v := e.table[i].offset if v < minOff { v = 0 } else { v = v - e.cur + e.maxMatchOff } e.table[i].offset = v } for i := range e.longTable[:] { v := e.longTable[i].offset if v < minOff { v = 0 } else { v = v - e.cur + e.maxMatchOff } e.longTable[i].offset = v } e.cur = e.maxMatchOff break } s := e.addBlock(src) blk.size = len(src) if len(src) < minNonLiteralBlockSize { blk.extraLits = len(src) blk.literals = blk.literals[:len(src)] copy(blk.literals, src) return } // Override src src = e.hist sLimit := int32(len(src)) - inputMargin // stepSize is the number of bytes to skip on every main loop iteration. // It should be >= 1. const stepSize = 1 const kSearchStrength = 8 // nextEmit is where in src the next emitLiteral should start from. nextEmit := s cv := load6432(src, s) // Relative offsets offset1 := int32(blk.recentOffsets[0]) offset2 := int32(blk.recentOffsets[1]) addLiterals := func(s *seq, until int32) { if until == nextEmit { return } blk.literals = append(blk.literals, src[nextEmit:until]...) s.litLen = uint32(until - nextEmit) } if debugEncoder { println("recent offsets:", blk.recentOffsets) } encodeLoop: for { var t int32 // We allow the encoder to optionally turn off repeat offsets across blocks canRepeat := len(blk.sequences) > 2 for { if debugAsserts && canRepeat && offset1 == 0 { panic("offset0 was 0") } nextHashL := hashLen(cv, dFastLongTableBits, dFastLongLen) nextHashS := hashLen(cv, dFastShortTableBits, dFastShortLen) candidateL := e.longTable[nextHashL] candidateS := e.table[nextHashS] const repOff = 1 repIndex := s - offset1 + repOff entry := tableEntry{offset: s + e.cur, val: uint32(cv)} e.longTable[nextHashL] = entry e.table[nextHashS] = entry if canRepeat { if repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>(repOff*8)) { // Consider history as well. var seq seq lenght := 4 + e.matchlen(s+4+repOff, repIndex+4, src) seq.matchLen = uint32(lenght - zstdMinMatch) // We might be able to match backwards. // Extend as long as we can. start := s + repOff // We end the search early, so we don't risk 0 literals // and have to do special offset treatment. startLimit := nextEmit + 1 tMin := s - e.maxMatchOff if tMin < 0 { tMin = 0 } for repIndex > tMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch-1 { repIndex-- start-- seq.matchLen++ } addLiterals(&seq, start) // rep 0 seq.offset = 1 if debugSequences { println("repeat sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) s += lenght + repOff nextEmit = s if s >= sLimit { if debugEncoder { println("repeat ended", s, lenght) } break encodeLoop } cv = load6432(src, s) continue } } // Find the offsets of our two matches. coffsetL := s - (candidateL.offset - e.cur) coffsetS := s - (candidateS.offset - e.cur) // Check if we have a long match. if coffsetL < e.maxMatchOff && uint32(cv) == candidateL.val { // Found a long match, likely at least 8 bytes. // Reference encoder checks all 8 bytes, we only check 4, // but the likelihood of both the first 4 bytes and the hash matching should be enough. t = candidateL.offset - e.cur if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && s-t > e.maxMatchOff { panic("s - t >e.maxMatchOff") } if debugMatches { println("long match") } break } // Check if we have a short match. if coffsetS < e.maxMatchOff && uint32(cv) == candidateS.val { // found a regular match // See if we can find a long match at s+1 const checkAt = 1 cv := load6432(src, s+checkAt) nextHashL = hashLen(cv, dFastLongTableBits, dFastLongLen) candidateL = e.longTable[nextHashL] coffsetL = s - (candidateL.offset - e.cur) + checkAt // We can store it, since we have at least a 4 byte match. e.longTable[nextHashL] = tableEntry{offset: s + checkAt + e.cur, val: uint32(cv)} if coffsetL < e.maxMatchOff && uint32(cv) == candidateL.val { // Found a long match, likely at least 8 bytes. // Reference encoder checks all 8 bytes, we only check 4, // but the likelihood of both the first 4 bytes and the hash matching should be enough. t = candidateL.offset - e.cur s += checkAt if debugMatches { println("long match (after short)") } break } t = candidateS.offset - e.cur if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && s-t > e.maxMatchOff { panic("s - t >e.maxMatchOff") } if debugAsserts && t < 0 { panic("t<0") } if debugMatches { println("short match") } break } // No match found, move forward in input. s += stepSize + ((s - nextEmit) >> (kSearchStrength - 1)) if s >= sLimit { break encodeLoop } cv = load6432(src, s) } // A 4-byte match has been found. Update recent offsets. // We'll later see if more than 4 bytes. offset2 = offset1 offset1 = s - t if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && canRepeat && int(offset1) > len(src) { panic("invalid offset") } // Extend the 4-byte match as long as possible. l := e.matchlen(s+4, t+4, src) + 4 // Extend backwards tMin := s - e.maxMatchOff if tMin < 0 { tMin = 0 } for t > tMin && s > nextEmit && src[t-1] == src[s-1] && l < maxMatchLength { s-- t-- l++ } // Write our sequence var seq seq seq.litLen = uint32(s - nextEmit) seq.matchLen = uint32(l - zstdMinMatch) if seq.litLen > 0 { blk.literals = append(blk.literals, src[nextEmit:s]...) } seq.offset = uint32(s-t) + 3 s += l if debugSequences { println("sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) nextEmit = s if s >= sLimit { break encodeLoop } // Index match start+1 (long) and start+2 (short) index0 := s - l + 1 // Index match end-2 (long) and end-1 (short) index1 := s - 2 cv0 := load6432(src, index0) cv1 := load6432(src, index1) te0 := tableEntry{offset: index0 + e.cur, val: uint32(cv0)} te1 := tableEntry{offset: index1 + e.cur, val: uint32(cv1)} e.longTable[hashLen(cv0, dFastLongTableBits, dFastLongLen)] = te0 e.longTable[hashLen(cv1, dFastLongTableBits, dFastLongLen)] = te1 cv0 >>= 8 cv1 >>= 8 te0.offset++ te1.offset++ te0.val = uint32(cv0) te1.val = uint32(cv1) e.table[hashLen(cv0, dFastShortTableBits, dFastShortLen)] = te0 e.table[hashLen(cv1, dFastShortTableBits, dFastShortLen)] = te1 cv = load6432(src, s) if !canRepeat { continue } // Check offset 2 for { o2 := s - offset2 if load3232(src, o2) != uint32(cv) { // Do regular search break } // Store this, since we have it. nextHashS := hashLen(cv, dFastShortTableBits, dFastShortLen) nextHashL := hashLen(cv, dFastLongTableBits, dFastLongLen) // We have at least 4 byte match. // No need to check backwards. We come straight from a match l := 4 + e.matchlen(s+4, o2+4, src) entry := tableEntry{offset: s + e.cur, val: uint32(cv)} e.longTable[nextHashL] = entry e.table[nextHashS] = entry seq.matchLen = uint32(l) - zstdMinMatch seq.litLen = 0 // Since litlen is always 0, this is offset 1. seq.offset = 1 s += l nextEmit = s if debugSequences { println("sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) // Swap offset 1 and 2. offset1, offset2 = offset2, offset1 if s >= sLimit { // Finished break encodeLoop } cv = load6432(src, s) } } if int(nextEmit) < len(src) { blk.literals = append(blk.literals, src[nextEmit:]...) blk.extraLits = len(src) - int(nextEmit) } blk.recentOffsets[0] = uint32(offset1) blk.recentOffsets[1] = uint32(offset2) if debugEncoder { println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits) } } // EncodeNoHist will encode a block with no history and no following blocks. // Most notable difference is that src will not be copied for history and // we do not need to check for max match length. func (e *doubleFastEncoder) EncodeNoHist(blk *blockEnc, src []byte) { const ( // Input margin is the number of bytes we read (8) // and the maximum we will read ahead (2) inputMargin = 8 + 2 minNonLiteralBlockSize = 16 ) // Protect against e.cur wraparound. if e.cur >= e.bufferReset { for i := range e.table[:] { e.table[i] = tableEntry{} } for i := range e.longTable[:] { e.longTable[i] = tableEntry{} } e.cur = e.maxMatchOff } s := int32(0) blk.size = len(src) if len(src) < minNonLiteralBlockSize { blk.extraLits = len(src) blk.literals = blk.literals[:len(src)] copy(blk.literals, src) return } // Override src sLimit := int32(len(src)) - inputMargin // stepSize is the number of bytes to skip on every main loop iteration. // It should be >= 1. const stepSize = 1 const kSearchStrength = 8 // nextEmit is where in src the next emitLiteral should start from. nextEmit := s cv := load6432(src, s) // Relative offsets offset1 := int32(blk.recentOffsets[0]) offset2 := int32(blk.recentOffsets[1]) addLiterals := func(s *seq, until int32) { if until == nextEmit { return } blk.literals = append(blk.literals, src[nextEmit:until]...) s.litLen = uint32(until - nextEmit) } if debugEncoder { println("recent offsets:", blk.recentOffsets) } encodeLoop: for { var t int32 for { nextHashL := hashLen(cv, dFastLongTableBits, dFastLongLen) nextHashS := hashLen(cv, dFastShortTableBits, dFastShortLen) candidateL := e.longTable[nextHashL] candidateS := e.table[nextHashS] const repOff = 1 repIndex := s - offset1 + repOff entry := tableEntry{offset: s + e.cur, val: uint32(cv)} e.longTable[nextHashL] = entry e.table[nextHashS] = entry if len(blk.sequences) > 2 { if load3232(src, repIndex) == uint32(cv>>(repOff*8)) { // Consider history as well. var seq seq //length := 4 + e.matchlen(s+4+repOff, repIndex+4, src) length := 4 + int32(matchLen(src[s+4+repOff:], src[repIndex+4:])) seq.matchLen = uint32(length - zstdMinMatch) // We might be able to match backwards. // Extend as long as we can. start := s + repOff // We end the search early, so we don't risk 0 literals // and have to do special offset treatment. startLimit := nextEmit + 1 tMin := s - e.maxMatchOff if tMin < 0 { tMin = 0 } for repIndex > tMin && start > startLimit && src[repIndex-1] == src[start-1] { repIndex-- start-- seq.matchLen++ } addLiterals(&seq, start) // rep 0 seq.offset = 1 if debugSequences { println("repeat sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) s += length + repOff nextEmit = s if s >= sLimit { if debugEncoder { println("repeat ended", s, length) } break encodeLoop } cv = load6432(src, s) continue } } // Find the offsets of our two matches. coffsetL := s - (candidateL.offset - e.cur) coffsetS := s - (candidateS.offset - e.cur) // Check if we have a long match. if coffsetL < e.maxMatchOff && uint32(cv) == candidateL.val { // Found a long match, likely at least 8 bytes. // Reference encoder checks all 8 bytes, we only check 4, // but the likelihood of both the first 4 bytes and the hash matching should be enough. t = candidateL.offset - e.cur if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d). cur: %d", s, t, e.cur)) } if debugAsserts && s-t > e.maxMatchOff { panic("s - t >e.maxMatchOff") } if debugMatches { println("long match") } break } // Check if we have a short match. if coffsetS < e.maxMatchOff && uint32(cv) == candidateS.val { // found a regular match // See if we can find a long match at s+1 const checkAt = 1 cv := load6432(src, s+checkAt) nextHashL = hashLen(cv, dFastLongTableBits, dFastLongLen) candidateL = e.longTable[nextHashL] coffsetL = s - (candidateL.offset - e.cur) + checkAt // We can store it, since we have at least a 4 byte match. e.longTable[nextHashL] = tableEntry{offset: s + checkAt + e.cur, val: uint32(cv)} if coffsetL < e.maxMatchOff && uint32(cv) == candidateL.val { // Found a long match, likely at least 8 bytes. // Reference encoder checks all 8 bytes, we only check 4, // but the likelihood of both the first 4 bytes and the hash matching should be enough. t = candidateL.offset - e.cur s += checkAt if debugMatches { println("long match (after short)") } break } t = candidateS.offset - e.cur if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && s-t > e.maxMatchOff { panic("s - t >e.maxMatchOff") } if debugAsserts && t < 0 { panic("t<0") } if debugMatches { println("short match") } break } // No match found, move forward in input. s += stepSize + ((s - nextEmit) >> (kSearchStrength - 1)) if s >= sLimit { break encodeLoop } cv = load6432(src, s) } // A 4-byte match has been found. Update recent offsets. // We'll later see if more than 4 bytes. offset2 = offset1 offset1 = s - t if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } // Extend the 4-byte match as long as possible. //l := e.matchlen(s+4, t+4, src) + 4 l := int32(matchLen(src[s+4:], src[t+4:])) + 4 // Extend backwards tMin := s - e.maxMatchOff if tMin < 0 { tMin = 0 } for t > tMin && s > nextEmit && src[t-1] == src[s-1] { s-- t-- l++ } // Write our sequence var seq seq seq.litLen = uint32(s - nextEmit) seq.matchLen = uint32(l - zstdMinMatch) if seq.litLen > 0 { blk.literals = append(blk.literals, src[nextEmit:s]...) } seq.offset = uint32(s-t) + 3 s += l if debugSequences { println("sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) nextEmit = s if s >= sLimit { break encodeLoop } // Index match start+1 (long) and start+2 (short) index0 := s - l + 1 // Index match end-2 (long) and end-1 (short) index1 := s - 2 cv0 := load6432(src, index0) cv1 := load6432(src, index1) te0 := tableEntry{offset: index0 + e.cur, val: uint32(cv0)} te1 := tableEntry{offset: index1 + e.cur, val: uint32(cv1)} e.longTable[hashLen(cv0, dFastLongTableBits, dFastLongLen)] = te0 e.longTable[hashLen(cv1, dFastLongTableBits, dFastLongLen)] = te1 cv0 >>= 8 cv1 >>= 8 te0.offset++ te1.offset++ te0.val = uint32(cv0) te1.val = uint32(cv1) e.table[hashLen(cv0, dFastShortTableBits, dFastShortLen)] = te0 e.table[hashLen(cv1, dFastShortTableBits, dFastShortLen)] = te1 cv = load6432(src, s) if len(blk.sequences) <= 2 { continue } // Check offset 2 for { o2 := s - offset2 if load3232(src, o2) != uint32(cv) { // Do regular search break } // Store this, since we have it. nextHashS := hashLen(cv1>>8, dFastShortTableBits, dFastShortLen) nextHashL := hashLen(cv, dFastLongTableBits, dFastLongLen) // We have at least 4 byte match. // No need to check backwards. We come straight from a match //l := 4 + e.matchlen(s+4, o2+4, src) l := 4 + int32(matchLen(src[s+4:], src[o2+4:])) entry := tableEntry{offset: s + e.cur, val: uint32(cv)} e.longTable[nextHashL] = entry e.table[nextHashS] = entry seq.matchLen = uint32(l) - zstdMinMatch seq.litLen = 0 // Since litlen is always 0, this is offset 1. seq.offset = 1 s += l nextEmit = s if debugSequences { println("sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) // Swap offset 1 and 2. offset1, offset2 = offset2, offset1 if s >= sLimit { // Finished break encodeLoop } cv = load6432(src, s) } } if int(nextEmit) < len(src) { blk.literals = append(blk.literals, src[nextEmit:]...) blk.extraLits = len(src) - int(nextEmit) } if debugEncoder { println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits) } // We do not store history, so we must offset e.cur to avoid false matches for next user. if e.cur < e.bufferReset { e.cur += int32(len(src)) } } // Encode will encode the content, with a dictionary if initialized for it. func (e *doubleFastEncoderDict) Encode(blk *blockEnc, src []byte) { const ( // Input margin is the number of bytes we read (8) // and the maximum we will read ahead (2) inputMargin = 8 + 2 minNonLiteralBlockSize = 16 ) // Protect against e.cur wraparound. for e.cur >= e.bufferReset-int32(len(e.hist)) { if len(e.hist) == 0 { for i := range e.table[:] { e.table[i] = tableEntry{} } for i := range e.longTable[:] { e.longTable[i] = tableEntry{} } e.markAllShardsDirty() e.cur = e.maxMatchOff break } // Shift down everything in the table that isn't already too far away. minOff := e.cur + int32(len(e.hist)) - e.maxMatchOff for i := range e.table[:] { v := e.table[i].offset if v < minOff { v = 0 } else { v = v - e.cur + e.maxMatchOff } e.table[i].offset = v } for i := range e.longTable[:] { v := e.longTable[i].offset if v < minOff { v = 0 } else { v = v - e.cur + e.maxMatchOff } e.longTable[i].offset = v } e.markAllShardsDirty() e.cur = e.maxMatchOff break } s := e.addBlock(src) blk.size = len(src) if len(src) < minNonLiteralBlockSize { blk.extraLits = len(src) blk.literals = blk.literals[:len(src)] copy(blk.literals, src) return } // Override src src = e.hist sLimit := int32(len(src)) - inputMargin // stepSize is the number of bytes to skip on every main loop iteration. // It should be >= 1. const stepSize = 1 const kSearchStrength = 8 // nextEmit is where in src the next emitLiteral should start from. nextEmit := s cv := load6432(src, s) // Relative offsets offset1 := int32(blk.recentOffsets[0]) offset2 := int32(blk.recentOffsets[1]) addLiterals := func(s *seq, until int32) { if until == nextEmit { return } blk.literals = append(blk.literals, src[nextEmit:until]...) s.litLen = uint32(until - nextEmit) } if debugEncoder { println("recent offsets:", blk.recentOffsets) } encodeLoop: for { var t int32 // We allow the encoder to optionally turn off repeat offsets across blocks canRepeat := len(blk.sequences) > 2 for { if debugAsserts && canRepeat && offset1 == 0 { panic("offset0 was 0") } nextHashL := hashLen(cv, dFastLongTableBits, dFastLongLen) nextHashS := hashLen(cv, dFastShortTableBits, dFastShortLen) candidateL := e.longTable[nextHashL] candidateS := e.table[nextHashS] const repOff = 1 repIndex := s - offset1 + repOff entry := tableEntry{offset: s + e.cur, val: uint32(cv)} e.longTable[nextHashL] = entry e.markLongShardDirty(nextHashL) e.table[nextHashS] = entry e.markShardDirty(nextHashS) if canRepeat { if repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>(repOff*8)) { // Consider history as well. var seq seq lenght := 4 + e.matchlen(s+4+repOff, repIndex+4, src) seq.matchLen = uint32(lenght - zstdMinMatch) // We might be able to match backwards. // Extend as long as we can. start := s + repOff // We end the search early, so we don't risk 0 literals // and have to do special offset treatment. startLimit := nextEmit + 1 tMin := s - e.maxMatchOff if tMin < 0 { tMin = 0 } for repIndex > tMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch-1 { repIndex-- start-- seq.matchLen++ } addLiterals(&seq, start) // rep 0 seq.offset = 1 if debugSequences { println("repeat sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) s += lenght + repOff nextEmit = s if s >= sLimit { if debugEncoder { println("repeat ended", s, lenght) } break encodeLoop } cv = load6432(src, s) continue } } // Find the offsets of our two matches. coffsetL := s - (candidateL.offset - e.cur) coffsetS := s - (candidateS.offset - e.cur) // Check if we have a long match. if coffsetL < e.maxMatchOff && uint32(cv) == candidateL.val { // Found a long match, likely at least 8 bytes. // Reference encoder checks all 8 bytes, we only check 4, // but the likelihood of both the first 4 bytes and the hash matching should be enough. t = candidateL.offset - e.cur if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && s-t > e.maxMatchOff { panic("s - t >e.maxMatchOff") } if debugMatches { println("long match") } break } // Check if we have a short match. if coffsetS < e.maxMatchOff && uint32(cv) == candidateS.val { // found a regular match // See if we can find a long match at s+1 const checkAt = 1 cv := load6432(src, s+checkAt) nextHashL = hashLen(cv, dFastLongTableBits, dFastLongLen) candidateL = e.longTable[nextHashL] coffsetL = s - (candidateL.offset - e.cur) + checkAt // We can store it, since we have at least a 4 byte match. e.longTable[nextHashL] = tableEntry{offset: s + checkAt + e.cur, val: uint32(cv)} e.markLongShardDirty(nextHashL) if coffsetL < e.maxMatchOff && uint32(cv) == candidateL.val { // Found a long match, likely at least 8 bytes. // Reference encoder checks all 8 bytes, we only check 4, // but the likelihood of both the first 4 bytes and the hash matching should be enough. t = candidateL.offset - e.cur s += checkAt if debugMatches { println("long match (after short)") } break } t = candidateS.offset - e.cur if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && s-t > e.maxMatchOff { panic("s - t >e.maxMatchOff") } if debugAsserts && t < 0 { panic("t<0") } if debugMatches { println("short match") } break } // No match found, move forward in input. s += stepSize + ((s - nextEmit) >> (kSearchStrength - 1)) if s >= sLimit { break encodeLoop } cv = load6432(src, s) } // A 4-byte match has been found. Update recent offsets. // We'll later see if more than 4 bytes. offset2 = offset1 offset1 = s - t if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && canRepeat && int(offset1) > len(src) { panic("invalid offset") } // Extend the 4-byte match as long as possible. l := e.matchlen(s+4, t+4, src) + 4 // Extend backwards tMin := s - e.maxMatchOff if tMin < 0 { tMin = 0 } for t > tMin && s > nextEmit && src[t-1] == src[s-1] && l < maxMatchLength { s-- t-- l++ } // Write our sequence var seq seq seq.litLen = uint32(s - nextEmit) seq.matchLen = uint32(l - zstdMinMatch) if seq.litLen > 0 { blk.literals = append(blk.literals, src[nextEmit:s]...) } seq.offset = uint32(s-t) + 3 s += l if debugSequences { println("sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) nextEmit = s if s >= sLimit { break encodeLoop } // Index match start+1 (long) and start+2 (short) index0 := s - l + 1 // Index match end-2 (long) and end-1 (short) index1 := s - 2 cv0 := load6432(src, index0) cv1 := load6432(src, index1) te0 := tableEntry{offset: index0 + e.cur, val: uint32(cv0)} te1 := tableEntry{offset: index1 + e.cur, val: uint32(cv1)} longHash1 := hashLen(cv0, dFastLongTableBits, dFastLongLen) longHash2 := hashLen(cv1, dFastLongTableBits, dFastLongLen) e.longTable[longHash1] = te0 e.longTable[longHash2] = te1 e.markLongShardDirty(longHash1) e.markLongShardDirty(longHash2) cv0 >>= 8 cv1 >>= 8 te0.offset++ te1.offset++ te0.val = uint32(cv0) te1.val = uint32(cv1) hashVal1 := hashLen(cv0, dFastShortTableBits, dFastShortLen) hashVal2 := hashLen(cv1, dFastShortTableBits, dFastShortLen) e.table[hashVal1] = te0 e.markShardDirty(hashVal1) e.table[hashVal2] = te1 e.markShardDirty(hashVal2) cv = load6432(src, s) if !canRepeat { continue } // Check offset 2 for { o2 := s - offset2 if load3232(src, o2) != uint32(cv) { // Do regular search break } // Store this, since we have it. nextHashL := hashLen(cv, dFastLongTableBits, dFastLongLen) nextHashS := hashLen(cv, dFastShortTableBits, dFastShortLen) // We have at least 4 byte match. // No need to check backwards. We come straight from a match l := 4 + e.matchlen(s+4, o2+4, src) entry := tableEntry{offset: s + e.cur, val: uint32(cv)} e.longTable[nextHashL] = entry e.markLongShardDirty(nextHashL) e.table[nextHashS] = entry e.markShardDirty(nextHashS) seq.matchLen = uint32(l) - zstdMinMatch seq.litLen = 0 // Since litlen is always 0, this is offset 1. seq.offset = 1 s += l nextEmit = s if debugSequences { println("sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) // Swap offset 1 and 2. offset1, offset2 = offset2, offset1 if s >= sLimit { // Finished break encodeLoop } cv = load6432(src, s) } } if int(nextEmit) < len(src) { blk.literals = append(blk.literals, src[nextEmit:]...) blk.extraLits = len(src) - int(nextEmit) } blk.recentOffsets[0] = uint32(offset1) blk.recentOffsets[1] = uint32(offset2) if debugEncoder { println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits) } // If we encoded more than 64K mark all dirty. if len(src) > 64<<10 { e.markAllShardsDirty() } } // ResetDict will reset and set a dictionary if not nil func (e *doubleFastEncoder) Reset(d *dict, singleBlock bool) { e.fastEncoder.Reset(d, singleBlock) if d != nil { panic("doubleFastEncoder: Reset with dict not supported") } } // ResetDict will reset and set a dictionary if not nil func (e *doubleFastEncoderDict) Reset(d *dict, singleBlock bool) { allDirty := e.allDirty e.fastEncoderDict.Reset(d, singleBlock) if d == nil { return } // Init or copy dict table if len(e.dictLongTable) != len(e.longTable) || d.id != e.lastDictID { if len(e.dictLongTable) != len(e.longTable) { e.dictLongTable = make([]tableEntry, len(e.longTable)) } if len(d.content) >= 8 { cv := load6432(d.content, 0) e.dictLongTable[hashLen(cv, dFastLongTableBits, dFastLongLen)] = tableEntry{ val: uint32(cv), offset: e.maxMatchOff, } end := int32(len(d.content)) - 8 + e.maxMatchOff for i := e.maxMatchOff + 1; i < end; i++ { cv = cv>>8 | (uint64(d.content[i-e.maxMatchOff+7]) << 56) e.dictLongTable[hashLen(cv, dFastLongTableBits, dFastLongLen)] = tableEntry{ val: uint32(cv), offset: i, } } } e.lastDictID = d.id allDirty = true } // Reset table to initial state e.cur = e.maxMatchOff dirtyShardCnt := 0 if !allDirty { for i := range e.longTableShardDirty { if e.longTableShardDirty[i] { dirtyShardCnt++ } } } if allDirty || dirtyShardCnt > dLongTableShardCnt/2 { //copy(e.longTable[:], e.dictLongTable) e.longTable = *(*[dFastLongTableSize]tableEntry)(e.dictLongTable) for i := range e.longTableShardDirty { e.longTableShardDirty[i] = false } return } for i := range e.longTableShardDirty { if !e.longTableShardDirty[i] { continue } // copy(e.longTable[i*dLongTableShardSize:(i+1)*dLongTableShardSize], e.dictLongTable[i*dLongTableShardSize:(i+1)*dLongTableShardSize]) *(*[dLongTableShardSize]tableEntry)(e.longTable[i*dLongTableShardSize:]) = *(*[dLongTableShardSize]tableEntry)(e.dictLongTable[i*dLongTableShardSize:]) e.longTableShardDirty[i] = false } } func (e *doubleFastEncoderDict) markLongShardDirty(entryNum uint32) { e.longTableShardDirty[entryNum/dLongTableShardSize] = true } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/enc_fast.go ================================================ // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. // Based on work by Yann Collet, released under BSD License. package zstd import ( "fmt" ) const ( tableBits = 15 // Bits used in the table tableSize = 1 << tableBits // Size of the table tableShardCnt = 1 << (tableBits - dictShardBits) // Number of shards in the table tableShardSize = tableSize / tableShardCnt // Size of an individual shard tableFastHashLen = 6 tableMask = tableSize - 1 // Mask for table indices. Redundant, but can eliminate bounds checks. maxMatchLength = 131074 ) type tableEntry struct { val uint32 offset int32 } type fastEncoder struct { fastBase table [tableSize]tableEntry } type fastEncoderDict struct { fastEncoder dictTable []tableEntry tableShardDirty [tableShardCnt]bool allDirty bool } // Encode mimmics functionality in zstd_fast.c func (e *fastEncoder) Encode(blk *blockEnc, src []byte) { const ( inputMargin = 8 minNonLiteralBlockSize = 1 + 1 + inputMargin ) // Protect against e.cur wraparound. for e.cur >= e.bufferReset-int32(len(e.hist)) { if len(e.hist) == 0 { for i := range e.table[:] { e.table[i] = tableEntry{} } e.cur = e.maxMatchOff break } // Shift down everything in the table that isn't already too far away. minOff := e.cur + int32(len(e.hist)) - e.maxMatchOff for i := range e.table[:] { v := e.table[i].offset if v < minOff { v = 0 } else { v = v - e.cur + e.maxMatchOff } e.table[i].offset = v } e.cur = e.maxMatchOff break } s := e.addBlock(src) blk.size = len(src) if len(src) < minNonLiteralBlockSize { blk.extraLits = len(src) blk.literals = blk.literals[:len(src)] copy(blk.literals, src) return } // Override src src = e.hist sLimit := int32(len(src)) - inputMargin // stepSize is the number of bytes to skip on every main loop iteration. // It should be >= 2. const stepSize = 2 // TEMPLATE const hashLog = tableBits // seems global, but would be nice to tweak. const kSearchStrength = 6 // nextEmit is where in src the next emitLiteral should start from. nextEmit := s cv := load6432(src, s) // Relative offsets offset1 := int32(blk.recentOffsets[0]) offset2 := int32(blk.recentOffsets[1]) addLiterals := func(s *seq, until int32) { if until == nextEmit { return } blk.literals = append(blk.literals, src[nextEmit:until]...) s.litLen = uint32(until - nextEmit) } if debugEncoder { println("recent offsets:", blk.recentOffsets) } encodeLoop: for { // t will contain the match offset when we find one. // When existing the search loop, we have already checked 4 bytes. var t int32 // We will not use repeat offsets across blocks. // By not using them for the first 3 matches canRepeat := len(blk.sequences) > 2 for { if debugAsserts && canRepeat && offset1 == 0 { panic("offset0 was 0") } nextHash := hashLen(cv, hashLog, tableFastHashLen) nextHash2 := hashLen(cv>>8, hashLog, tableFastHashLen) candidate := e.table[nextHash] candidate2 := e.table[nextHash2] repIndex := s - offset1 + 2 e.table[nextHash] = tableEntry{offset: s + e.cur, val: uint32(cv)} e.table[nextHash2] = tableEntry{offset: s + e.cur + 1, val: uint32(cv >> 8)} if canRepeat && repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>16) { // Consider history as well. var seq seq length := 4 + e.matchlen(s+6, repIndex+4, src) seq.matchLen = uint32(length - zstdMinMatch) // We might be able to match backwards. // Extend as long as we can. start := s + 2 // We end the search early, so we don't risk 0 literals // and have to do special offset treatment. startLimit := nextEmit + 1 sMin := s - e.maxMatchOff if sMin < 0 { sMin = 0 } for repIndex > sMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch { repIndex-- start-- seq.matchLen++ } addLiterals(&seq, start) // rep 0 seq.offset = 1 if debugSequences { println("repeat sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) s += length + 2 nextEmit = s if s >= sLimit { if debugEncoder { println("repeat ended", s, length) } break encodeLoop } cv = load6432(src, s) continue } coffset0 := s - (candidate.offset - e.cur) coffset1 := s - (candidate2.offset - e.cur) + 1 if coffset0 < e.maxMatchOff && uint32(cv) == candidate.val { // found a regular match t = candidate.offset - e.cur if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && s-t > e.maxMatchOff { panic("s - t >e.maxMatchOff") } break } if coffset1 < e.maxMatchOff && uint32(cv>>8) == candidate2.val { // found a regular match t = candidate2.offset - e.cur s++ if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && s-t > e.maxMatchOff { panic("s - t >e.maxMatchOff") } if debugAsserts && t < 0 { panic("t<0") } break } s += stepSize + ((s - nextEmit) >> (kSearchStrength - 1)) if s >= sLimit { break encodeLoop } cv = load6432(src, s) } // A 4-byte match has been found. We'll later see if more than 4 bytes. offset2 = offset1 offset1 = s - t if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && canRepeat && int(offset1) > len(src) { panic("invalid offset") } // Extend the 4-byte match as long as possible. l := e.matchlen(s+4, t+4, src) + 4 // Extend backwards tMin := s - e.maxMatchOff if tMin < 0 { tMin = 0 } for t > tMin && s > nextEmit && src[t-1] == src[s-1] && l < maxMatchLength { s-- t-- l++ } // Write our sequence. var seq seq seq.litLen = uint32(s - nextEmit) seq.matchLen = uint32(l - zstdMinMatch) if seq.litLen > 0 { blk.literals = append(blk.literals, src[nextEmit:s]...) } // Don't use repeat offsets seq.offset = uint32(s-t) + 3 s += l if debugSequences { println("sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) nextEmit = s if s >= sLimit { break encodeLoop } cv = load6432(src, s) // Check offset 2 if o2 := s - offset2; canRepeat && load3232(src, o2) == uint32(cv) { // We have at least 4 byte match. // No need to check backwards. We come straight from a match l := 4 + e.matchlen(s+4, o2+4, src) // Store this, since we have it. nextHash := hashLen(cv, hashLog, tableFastHashLen) e.table[nextHash] = tableEntry{offset: s + e.cur, val: uint32(cv)} seq.matchLen = uint32(l) - zstdMinMatch seq.litLen = 0 // Since litlen is always 0, this is offset 1. seq.offset = 1 s += l nextEmit = s if debugSequences { println("sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) // Swap offset 1 and 2. offset1, offset2 = offset2, offset1 if s >= sLimit { break encodeLoop } // Prepare next loop. cv = load6432(src, s) } } if int(nextEmit) < len(src) { blk.literals = append(blk.literals, src[nextEmit:]...) blk.extraLits = len(src) - int(nextEmit) } blk.recentOffsets[0] = uint32(offset1) blk.recentOffsets[1] = uint32(offset2) if debugEncoder { println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits) } } // EncodeNoHist will encode a block with no history and no following blocks. // Most notable difference is that src will not be copied for history and // we do not need to check for max match length. func (e *fastEncoder) EncodeNoHist(blk *blockEnc, src []byte) { const ( inputMargin = 8 minNonLiteralBlockSize = 1 + 1 + inputMargin ) if debugEncoder { if len(src) > maxCompressedBlockSize { panic("src too big") } } // Protect against e.cur wraparound. if e.cur >= e.bufferReset { for i := range e.table[:] { e.table[i] = tableEntry{} } e.cur = e.maxMatchOff } s := int32(0) blk.size = len(src) if len(src) < minNonLiteralBlockSize { blk.extraLits = len(src) blk.literals = blk.literals[:len(src)] copy(blk.literals, src) return } sLimit := int32(len(src)) - inputMargin // stepSize is the number of bytes to skip on every main loop iteration. // It should be >= 2. const stepSize = 2 // TEMPLATE const hashLog = tableBits // seems global, but would be nice to tweak. const kSearchStrength = 6 // nextEmit is where in src the next emitLiteral should start from. nextEmit := s cv := load6432(src, s) // Relative offsets offset1 := int32(blk.recentOffsets[0]) offset2 := int32(blk.recentOffsets[1]) addLiterals := func(s *seq, until int32) { if until == nextEmit { return } blk.literals = append(blk.literals, src[nextEmit:until]...) s.litLen = uint32(until - nextEmit) } if debugEncoder { println("recent offsets:", blk.recentOffsets) } encodeLoop: for { // t will contain the match offset when we find one. // When existing the search loop, we have already checked 4 bytes. var t int32 // We will not use repeat offsets across blocks. // By not using them for the first 3 matches for { nextHash := hashLen(cv, hashLog, tableFastHashLen) nextHash2 := hashLen(cv>>8, hashLog, tableFastHashLen) candidate := e.table[nextHash] candidate2 := e.table[nextHash2] repIndex := s - offset1 + 2 e.table[nextHash] = tableEntry{offset: s + e.cur, val: uint32(cv)} e.table[nextHash2] = tableEntry{offset: s + e.cur + 1, val: uint32(cv >> 8)} if len(blk.sequences) > 2 && load3232(src, repIndex) == uint32(cv>>16) { // Consider history as well. var seq seq length := 4 + e.matchlen(s+6, repIndex+4, src) seq.matchLen = uint32(length - zstdMinMatch) // We might be able to match backwards. // Extend as long as we can. start := s + 2 // We end the search early, so we don't risk 0 literals // and have to do special offset treatment. startLimit := nextEmit + 1 sMin := s - e.maxMatchOff if sMin < 0 { sMin = 0 } for repIndex > sMin && start > startLimit && src[repIndex-1] == src[start-1] { repIndex-- start-- seq.matchLen++ } addLiterals(&seq, start) // rep 0 seq.offset = 1 if debugSequences { println("repeat sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) s += length + 2 nextEmit = s if s >= sLimit { if debugEncoder { println("repeat ended", s, length) } break encodeLoop } cv = load6432(src, s) continue } coffset0 := s - (candidate.offset - e.cur) coffset1 := s - (candidate2.offset - e.cur) + 1 if coffset0 < e.maxMatchOff && uint32(cv) == candidate.val { // found a regular match t = candidate.offset - e.cur if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && s-t > e.maxMatchOff { panic("s - t >e.maxMatchOff") } if debugAsserts && t < 0 { panic(fmt.Sprintf("t (%d) < 0, candidate.offset: %d, e.cur: %d, coffset0: %d, e.maxMatchOff: %d", t, candidate.offset, e.cur, coffset0, e.maxMatchOff)) } break } if coffset1 < e.maxMatchOff && uint32(cv>>8) == candidate2.val { // found a regular match t = candidate2.offset - e.cur s++ if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && s-t > e.maxMatchOff { panic("s - t >e.maxMatchOff") } if debugAsserts && t < 0 { panic("t<0") } break } s += stepSize + ((s - nextEmit) >> (kSearchStrength - 1)) if s >= sLimit { break encodeLoop } cv = load6432(src, s) } // A 4-byte match has been found. We'll later see if more than 4 bytes. offset2 = offset1 offset1 = s - t if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && t < 0 { panic(fmt.Sprintf("t (%d) < 0 ", t)) } // Extend the 4-byte match as long as possible. l := e.matchlen(s+4, t+4, src) + 4 // Extend backwards tMin := s - e.maxMatchOff if tMin < 0 { tMin = 0 } for t > tMin && s > nextEmit && src[t-1] == src[s-1] { s-- t-- l++ } // Write our sequence. var seq seq seq.litLen = uint32(s - nextEmit) seq.matchLen = uint32(l - zstdMinMatch) if seq.litLen > 0 { blk.literals = append(blk.literals, src[nextEmit:s]...) } // Don't use repeat offsets seq.offset = uint32(s-t) + 3 s += l if debugSequences { println("sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) nextEmit = s if s >= sLimit { break encodeLoop } cv = load6432(src, s) // Check offset 2 if o2 := s - offset2; len(blk.sequences) > 2 && load3232(src, o2) == uint32(cv) { // We have at least 4 byte match. // No need to check backwards. We come straight from a match l := 4 + e.matchlen(s+4, o2+4, src) // Store this, since we have it. nextHash := hashLen(cv, hashLog, tableFastHashLen) e.table[nextHash] = tableEntry{offset: s + e.cur, val: uint32(cv)} seq.matchLen = uint32(l) - zstdMinMatch seq.litLen = 0 // Since litlen is always 0, this is offset 1. seq.offset = 1 s += l nextEmit = s if debugSequences { println("sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) // Swap offset 1 and 2. offset1, offset2 = offset2, offset1 if s >= sLimit { break encodeLoop } // Prepare next loop. cv = load6432(src, s) } } if int(nextEmit) < len(src) { blk.literals = append(blk.literals, src[nextEmit:]...) blk.extraLits = len(src) - int(nextEmit) } if debugEncoder { println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits) } // We do not store history, so we must offset e.cur to avoid false matches for next user. if e.cur < e.bufferReset { e.cur += int32(len(src)) } } // Encode will encode the content, with a dictionary if initialized for it. func (e *fastEncoderDict) Encode(blk *blockEnc, src []byte) { const ( inputMargin = 8 minNonLiteralBlockSize = 1 + 1 + inputMargin ) if e.allDirty || len(src) > 32<<10 { e.fastEncoder.Encode(blk, src) e.allDirty = true return } // Protect against e.cur wraparound. for e.cur >= e.bufferReset-int32(len(e.hist)) { if len(e.hist) == 0 { e.table = [tableSize]tableEntry{} e.cur = e.maxMatchOff break } // Shift down everything in the table that isn't already too far away. minOff := e.cur + int32(len(e.hist)) - e.maxMatchOff for i := range e.table[:] { v := e.table[i].offset if v < minOff { v = 0 } else { v = v - e.cur + e.maxMatchOff } e.table[i].offset = v } e.cur = e.maxMatchOff break } s := e.addBlock(src) blk.size = len(src) if len(src) < minNonLiteralBlockSize { blk.extraLits = len(src) blk.literals = blk.literals[:len(src)] copy(blk.literals, src) return } // Override src src = e.hist sLimit := int32(len(src)) - inputMargin // stepSize is the number of bytes to skip on every main loop iteration. // It should be >= 2. const stepSize = 2 // TEMPLATE const hashLog = tableBits // seems global, but would be nice to tweak. const kSearchStrength = 7 // nextEmit is where in src the next emitLiteral should start from. nextEmit := s cv := load6432(src, s) // Relative offsets offset1 := int32(blk.recentOffsets[0]) offset2 := int32(blk.recentOffsets[1]) addLiterals := func(s *seq, until int32) { if until == nextEmit { return } blk.literals = append(blk.literals, src[nextEmit:until]...) s.litLen = uint32(until - nextEmit) } if debugEncoder { println("recent offsets:", blk.recentOffsets) } encodeLoop: for { // t will contain the match offset when we find one. // When existing the search loop, we have already checked 4 bytes. var t int32 // We will not use repeat offsets across blocks. // By not using them for the first 3 matches canRepeat := len(blk.sequences) > 2 for { if debugAsserts && canRepeat && offset1 == 0 { panic("offset0 was 0") } nextHash := hashLen(cv, hashLog, tableFastHashLen) nextHash2 := hashLen(cv>>8, hashLog, tableFastHashLen) candidate := e.table[nextHash] candidate2 := e.table[nextHash2] repIndex := s - offset1 + 2 e.table[nextHash] = tableEntry{offset: s + e.cur, val: uint32(cv)} e.markShardDirty(nextHash) e.table[nextHash2] = tableEntry{offset: s + e.cur + 1, val: uint32(cv >> 8)} e.markShardDirty(nextHash2) if canRepeat && repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>16) { // Consider history as well. var seq seq length := 4 + e.matchlen(s+6, repIndex+4, src) seq.matchLen = uint32(length - zstdMinMatch) // We might be able to match backwards. // Extend as long as we can. start := s + 2 // We end the search early, so we don't risk 0 literals // and have to do special offset treatment. startLimit := nextEmit + 1 sMin := s - e.maxMatchOff if sMin < 0 { sMin = 0 } for repIndex > sMin && start > startLimit && src[repIndex-1] == src[start-1] && seq.matchLen < maxMatchLength-zstdMinMatch { repIndex-- start-- seq.matchLen++ } addLiterals(&seq, start) // rep 0 seq.offset = 1 if debugSequences { println("repeat sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) s += length + 2 nextEmit = s if s >= sLimit { if debugEncoder { println("repeat ended", s, length) } break encodeLoop } cv = load6432(src, s) continue } coffset0 := s - (candidate.offset - e.cur) coffset1 := s - (candidate2.offset - e.cur) + 1 if coffset0 < e.maxMatchOff && uint32(cv) == candidate.val { // found a regular match t = candidate.offset - e.cur if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && s-t > e.maxMatchOff { panic("s - t >e.maxMatchOff") } break } if coffset1 < e.maxMatchOff && uint32(cv>>8) == candidate2.val { // found a regular match t = candidate2.offset - e.cur s++ if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && s-t > e.maxMatchOff { panic("s - t >e.maxMatchOff") } if debugAsserts && t < 0 { panic("t<0") } break } s += stepSize + ((s - nextEmit) >> (kSearchStrength - 1)) if s >= sLimit { break encodeLoop } cv = load6432(src, s) } // A 4-byte match has been found. We'll later see if more than 4 bytes. offset2 = offset1 offset1 = s - t if debugAsserts && s <= t { panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } if debugAsserts && canRepeat && int(offset1) > len(src) { panic("invalid offset") } // Extend the 4-byte match as long as possible. l := e.matchlen(s+4, t+4, src) + 4 // Extend backwards tMin := s - e.maxMatchOff if tMin < 0 { tMin = 0 } for t > tMin && s > nextEmit && src[t-1] == src[s-1] && l < maxMatchLength { s-- t-- l++ } // Write our sequence. var seq seq seq.litLen = uint32(s - nextEmit) seq.matchLen = uint32(l - zstdMinMatch) if seq.litLen > 0 { blk.literals = append(blk.literals, src[nextEmit:s]...) } // Don't use repeat offsets seq.offset = uint32(s-t) + 3 s += l if debugSequences { println("sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) nextEmit = s if s >= sLimit { break encodeLoop } cv = load6432(src, s) // Check offset 2 if o2 := s - offset2; canRepeat && load3232(src, o2) == uint32(cv) { // We have at least 4 byte match. // No need to check backwards. We come straight from a match l := 4 + e.matchlen(s+4, o2+4, src) // Store this, since we have it. nextHash := hashLen(cv, hashLog, tableFastHashLen) e.table[nextHash] = tableEntry{offset: s + e.cur, val: uint32(cv)} e.markShardDirty(nextHash) seq.matchLen = uint32(l) - zstdMinMatch seq.litLen = 0 // Since litlen is always 0, this is offset 1. seq.offset = 1 s += l nextEmit = s if debugSequences { println("sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) // Swap offset 1 and 2. offset1, offset2 = offset2, offset1 if s >= sLimit { break encodeLoop } // Prepare next loop. cv = load6432(src, s) } } if int(nextEmit) < len(src) { blk.literals = append(blk.literals, src[nextEmit:]...) blk.extraLits = len(src) - int(nextEmit) } blk.recentOffsets[0] = uint32(offset1) blk.recentOffsets[1] = uint32(offset2) if debugEncoder { println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits) } } // ResetDict will reset and set a dictionary if not nil func (e *fastEncoder) Reset(d *dict, singleBlock bool) { e.resetBase(d, singleBlock) if d != nil { panic("fastEncoder: Reset with dict") } } // ResetDict will reset and set a dictionary if not nil func (e *fastEncoderDict) Reset(d *dict, singleBlock bool) { e.resetBase(d, singleBlock) if d == nil { return } // Init or copy dict table if len(e.dictTable) != len(e.table) || d.id != e.lastDictID { if len(e.dictTable) != len(e.table) { e.dictTable = make([]tableEntry, len(e.table)) } if true { end := e.maxMatchOff + int32(len(d.content)) - 8 for i := e.maxMatchOff; i < end; i += 2 { const hashLog = tableBits cv := load6432(d.content, i-e.maxMatchOff) nextHash := hashLen(cv, hashLog, tableFastHashLen) // 0 -> 6 nextHash1 := hashLen(cv>>8, hashLog, tableFastHashLen) // 1 -> 7 e.dictTable[nextHash] = tableEntry{ val: uint32(cv), offset: i, } e.dictTable[nextHash1] = tableEntry{ val: uint32(cv >> 8), offset: i + 1, } } } e.lastDictID = d.id e.allDirty = true } e.cur = e.maxMatchOff dirtyShardCnt := 0 if !e.allDirty { for i := range e.tableShardDirty { if e.tableShardDirty[i] { dirtyShardCnt++ } } } const shardCnt = tableShardCnt const shardSize = tableShardSize if e.allDirty || dirtyShardCnt > shardCnt*4/6 { //copy(e.table[:], e.dictTable) e.table = *(*[tableSize]tableEntry)(e.dictTable) for i := range e.tableShardDirty { e.tableShardDirty[i] = false } e.allDirty = false return } for i := range e.tableShardDirty { if !e.tableShardDirty[i] { continue } //copy(e.table[i*shardSize:(i+1)*shardSize], e.dictTable[i*shardSize:(i+1)*shardSize]) *(*[shardSize]tableEntry)(e.table[i*shardSize:]) = *(*[shardSize]tableEntry)(e.dictTable[i*shardSize:]) e.tableShardDirty[i] = false } e.allDirty = false } func (e *fastEncoderDict) markAllShardsDirty() { e.allDirty = true } func (e *fastEncoderDict) markShardDirty(entryNum uint32) { e.tableShardDirty[entryNum/tableShardSize] = true } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/encoder.go ================================================ // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. // Based on work by Yann Collet, released under BSD License. package zstd import ( "crypto/rand" "fmt" "io" "math" rdebug "runtime/debug" "sync" "github.com/klauspost/compress/zstd/internal/xxhash" ) // Encoder provides encoding to Zstandard. // An Encoder can be used for either compressing a stream via the // io.WriteCloser interface supported by the Encoder or as multiple independent // tasks via the EncodeAll function. // Smaller encodes are encouraged to use the EncodeAll function. // Use NewWriter to create a new instance. type Encoder struct { o encoderOptions encoders chan encoder state encoderState init sync.Once } type encoder interface { Encode(blk *blockEnc, src []byte) EncodeNoHist(blk *blockEnc, src []byte) Block() *blockEnc CRC() *xxhash.Digest AppendCRC([]byte) []byte WindowSize(size int64) int32 UseBlock(*blockEnc) Reset(d *dict, singleBlock bool) } type encoderState struct { w io.Writer filling []byte current []byte previous []byte encoder encoder writing *blockEnc err error writeErr error nWritten int64 nInput int64 frameContentSize int64 headerWritten bool eofWritten bool fullFrameWritten bool // This waitgroup indicates an encode is running. wg sync.WaitGroup // This waitgroup indicates we have a block encoding/writing. wWg sync.WaitGroup } // NewWriter will create a new Zstandard encoder. // If the encoder will be used for encoding blocks a nil writer can be used. func NewWriter(w io.Writer, opts ...EOption) (*Encoder, error) { initPredefined() var e Encoder e.o.setDefault() for _, o := range opts { err := o(&e.o) if err != nil { return nil, err } } if w != nil { e.Reset(w) } return &e, nil } func (e *Encoder) initialize() { if e.o.concurrent == 0 { e.o.setDefault() } e.encoders = make(chan encoder, e.o.concurrent) for i := 0; i < e.o.concurrent; i++ { enc := e.o.encoder() e.encoders <- enc } } // Reset will re-initialize the writer and new writes will encode to the supplied writer // as a new, independent stream. func (e *Encoder) Reset(w io.Writer) { s := &e.state s.wg.Wait() s.wWg.Wait() if cap(s.filling) == 0 { s.filling = make([]byte, 0, e.o.blockSize) } if e.o.concurrent > 1 { if cap(s.current) == 0 { s.current = make([]byte, 0, e.o.blockSize) } if cap(s.previous) == 0 { s.previous = make([]byte, 0, e.o.blockSize) } s.current = s.current[:0] s.previous = s.previous[:0] if s.writing == nil { s.writing = &blockEnc{lowMem: e.o.lowMem} s.writing.init() } s.writing.initNewEncode() } if s.encoder == nil { s.encoder = e.o.encoder() } s.filling = s.filling[:0] s.encoder.Reset(e.o.dict, false) s.headerWritten = false s.eofWritten = false s.fullFrameWritten = false s.w = w s.err = nil s.nWritten = 0 s.nInput = 0 s.writeErr = nil s.frameContentSize = 0 } // ResetContentSize will reset and set a content size for the next stream. // If the bytes written does not match the size given an error will be returned // when calling Close(). // This is removed when Reset is called. // Sizes <= 0 results in no content size set. func (e *Encoder) ResetContentSize(w io.Writer, size int64) { e.Reset(w) if size >= 0 { e.state.frameContentSize = size } } // Write data to the encoder. // Input data will be buffered and as the buffer fills up // content will be compressed and written to the output. // When done writing, use Close to flush the remaining output // and write CRC if requested. func (e *Encoder) Write(p []byte) (n int, err error) { s := &e.state for len(p) > 0 { if len(p)+len(s.filling) < e.o.blockSize { if e.o.crc { _, _ = s.encoder.CRC().Write(p) } s.filling = append(s.filling, p...) return n + len(p), nil } add := p if len(p)+len(s.filling) > e.o.blockSize { add = add[:e.o.blockSize-len(s.filling)] } if e.o.crc { _, _ = s.encoder.CRC().Write(add) } s.filling = append(s.filling, add...) p = p[len(add):] n += len(add) if len(s.filling) < e.o.blockSize { return n, nil } err := e.nextBlock(false) if err != nil { return n, err } if debugAsserts && len(s.filling) > 0 { panic(len(s.filling)) } } return n, nil } // nextBlock will synchronize and start compressing input in e.state.filling. // If an error has occurred during encoding it will be returned. func (e *Encoder) nextBlock(final bool) error { s := &e.state // Wait for current block. s.wg.Wait() if s.err != nil { return s.err } if len(s.filling) > e.o.blockSize { return fmt.Errorf("block > maxStoreBlockSize") } if !s.headerWritten { // If we have a single block encode, do a sync compression. if final && len(s.filling) == 0 && !e.o.fullZero { s.headerWritten = true s.fullFrameWritten = true s.eofWritten = true return nil } if final && len(s.filling) > 0 { s.current = e.EncodeAll(s.filling, s.current[:0]) var n2 int n2, s.err = s.w.Write(s.current) if s.err != nil { return s.err } s.nWritten += int64(n2) s.nInput += int64(len(s.filling)) s.current = s.current[:0] s.filling = s.filling[:0] s.headerWritten = true s.fullFrameWritten = true s.eofWritten = true return nil } var tmp [maxHeaderSize]byte fh := frameHeader{ ContentSize: uint64(s.frameContentSize), WindowSize: uint32(s.encoder.WindowSize(s.frameContentSize)), SingleSegment: false, Checksum: e.o.crc, DictID: e.o.dict.ID(), } dst := fh.appendTo(tmp[:0]) s.headerWritten = true s.wWg.Wait() var n2 int n2, s.err = s.w.Write(dst) if s.err != nil { return s.err } s.nWritten += int64(n2) } if s.eofWritten { // Ensure we only write it once. final = false } if len(s.filling) == 0 { // Final block, but no data. if final { enc := s.encoder blk := enc.Block() blk.reset(nil) blk.last = true blk.encodeRaw(nil) s.wWg.Wait() _, s.err = s.w.Write(blk.output) s.nWritten += int64(len(blk.output)) s.eofWritten = true } return s.err } // SYNC: if e.o.concurrent == 1 { src := s.filling s.nInput += int64(len(s.filling)) if debugEncoder { println("Adding sync block,", len(src), "bytes, final:", final) } enc := s.encoder blk := enc.Block() blk.reset(nil) enc.Encode(blk, src) blk.last = final if final { s.eofWritten = true } s.err = blk.encode(src, e.o.noEntropy, !e.o.allLitEntropy) if s.err != nil { return s.err } _, s.err = s.w.Write(blk.output) s.nWritten += int64(len(blk.output)) s.filling = s.filling[:0] return s.err } // Move blocks forward. s.filling, s.current, s.previous = s.previous[:0], s.filling, s.current s.nInput += int64(len(s.current)) s.wg.Add(1) go func(src []byte) { if debugEncoder { println("Adding block,", len(src), "bytes, final:", final) } defer func() { if r := recover(); r != nil { s.err = fmt.Errorf("panic while encoding: %v", r) rdebug.PrintStack() } s.wg.Done() }() enc := s.encoder blk := enc.Block() enc.Encode(blk, src) blk.last = final if final { s.eofWritten = true } // Wait for pending writes. s.wWg.Wait() if s.writeErr != nil { s.err = s.writeErr return } // Transfer encoders from previous write block. blk.swapEncoders(s.writing) // Transfer recent offsets to next. enc.UseBlock(s.writing) s.writing = blk s.wWg.Add(1) go func() { defer func() { if r := recover(); r != nil { s.writeErr = fmt.Errorf("panic while encoding/writing: %v", r) rdebug.PrintStack() } s.wWg.Done() }() s.writeErr = blk.encode(src, e.o.noEntropy, !e.o.allLitEntropy) if s.writeErr != nil { return } _, s.writeErr = s.w.Write(blk.output) s.nWritten += int64(len(blk.output)) }() }(s.current) return nil } // ReadFrom reads data from r until EOF or error. // The return value n is the number of bytes read. // Any error except io.EOF encountered during the read is also returned. // // The Copy function uses ReaderFrom if available. func (e *Encoder) ReadFrom(r io.Reader) (n int64, err error) { if debugEncoder { println("Using ReadFrom") } // Flush any current writes. if len(e.state.filling) > 0 { if err := e.nextBlock(false); err != nil { return 0, err } } e.state.filling = e.state.filling[:e.o.blockSize] src := e.state.filling for { n2, err := r.Read(src) if e.o.crc { _, _ = e.state.encoder.CRC().Write(src[:n2]) } // src is now the unfilled part... src = src[n2:] n += int64(n2) switch err { case io.EOF: e.state.filling = e.state.filling[:len(e.state.filling)-len(src)] if debugEncoder { println("ReadFrom: got EOF final block:", len(e.state.filling)) } return n, nil case nil: default: if debugEncoder { println("ReadFrom: got error:", err) } e.state.err = err return n, err } if len(src) > 0 { if debugEncoder { println("ReadFrom: got space left in source:", len(src)) } continue } err = e.nextBlock(false) if err != nil { return n, err } e.state.filling = e.state.filling[:e.o.blockSize] src = e.state.filling } } // Flush will send the currently written data to output // and block until everything has been written. // This should only be used on rare occasions where pushing the currently queued data is critical. func (e *Encoder) Flush() error { s := &e.state if len(s.filling) > 0 { err := e.nextBlock(false) if err != nil { return err } } s.wg.Wait() s.wWg.Wait() if s.err != nil { return s.err } return s.writeErr } // Close will flush the final output and close the stream. // The function will block until everything has been written. // The Encoder can still be re-used after calling this. func (e *Encoder) Close() error { s := &e.state if s.encoder == nil { return nil } err := e.nextBlock(true) if err != nil { return err } if s.frameContentSize > 0 { if s.nInput != s.frameContentSize { return fmt.Errorf("frame content size %d given, but %d bytes was written", s.frameContentSize, s.nInput) } } if e.state.fullFrameWritten { return s.err } s.wg.Wait() s.wWg.Wait() if s.err != nil { return s.err } if s.writeErr != nil { return s.writeErr } // Write CRC if e.o.crc && s.err == nil { // heap alloc. var tmp [4]byte _, s.err = s.w.Write(s.encoder.AppendCRC(tmp[:0])) s.nWritten += 4 } // Add padding with content from crypto/rand.Reader if s.err == nil && e.o.pad > 0 { add := calcSkippableFrame(s.nWritten, int64(e.o.pad)) frame, err := skippableFrame(s.filling[:0], add, rand.Reader) if err != nil { return err } _, s.err = s.w.Write(frame) } return s.err } // EncodeAll will encode all input in src and append it to dst. // This function can be called concurrently, but each call will only run on a single goroutine. // If empty input is given, nothing is returned, unless WithZeroFrames is specified. // Encoded blocks can be concatenated and the result will be the combined input stream. // Data compressed with EncodeAll can be decoded with the Decoder, // using either a stream or DecodeAll. func (e *Encoder) EncodeAll(src, dst []byte) []byte { if len(src) == 0 { if e.o.fullZero { // Add frame header. fh := frameHeader{ ContentSize: 0, WindowSize: MinWindowSize, SingleSegment: true, // Adding a checksum would be a waste of space. Checksum: false, DictID: 0, } dst = fh.appendTo(dst) // Write raw block as last one only. var blk blockHeader blk.setSize(0) blk.setType(blockTypeRaw) blk.setLast(true) dst = blk.appendTo(dst) } return dst } e.init.Do(e.initialize) enc := <-e.encoders defer func() { // Release encoder reference to last block. // If a non-single block is needed the encoder will reset again. e.encoders <- enc }() // Use single segments when above minimum window and below window size. single := len(src) <= e.o.windowSize && len(src) > MinWindowSize if e.o.single != nil { single = *e.o.single } fh := frameHeader{ ContentSize: uint64(len(src)), WindowSize: uint32(enc.WindowSize(int64(len(src)))), SingleSegment: single, Checksum: e.o.crc, DictID: e.o.dict.ID(), } // If less than 1MB, allocate a buffer up front. if len(dst) == 0 && cap(dst) == 0 && len(src) < 1<<20 && !e.o.lowMem { dst = make([]byte, 0, len(src)) } dst = fh.appendTo(dst) // If we can do everything in one block, prefer that. if len(src) <= e.o.blockSize { enc.Reset(e.o.dict, true) // Slightly faster with no history and everything in one block. if e.o.crc { _, _ = enc.CRC().Write(src) } blk := enc.Block() blk.last = true if e.o.dict == nil { enc.EncodeNoHist(blk, src) } else { enc.Encode(blk, src) } // If we got the exact same number of literals as input, // assume the literals cannot be compressed. oldout := blk.output // Output directly to dst blk.output = dst err := blk.encode(src, e.o.noEntropy, !e.o.allLitEntropy) if err != nil { panic(err) } dst = blk.output blk.output = oldout } else { enc.Reset(e.o.dict, false) blk := enc.Block() for len(src) > 0 { todo := src if len(todo) > e.o.blockSize { todo = todo[:e.o.blockSize] } src = src[len(todo):] if e.o.crc { _, _ = enc.CRC().Write(todo) } blk.pushOffsets() enc.Encode(blk, todo) if len(src) == 0 { blk.last = true } err := blk.encode(todo, e.o.noEntropy, !e.o.allLitEntropy) if err != nil { panic(err) } dst = append(dst, blk.output...) blk.reset(nil) } } if e.o.crc { dst = enc.AppendCRC(dst) } // Add padding with content from crypto/rand.Reader if e.o.pad > 0 { add := calcSkippableFrame(int64(len(dst)), int64(e.o.pad)) var err error dst, err = skippableFrame(dst, add, rand.Reader) if err != nil { panic(err) } } return dst } // MaxEncodedSize returns the expected maximum // size of an encoded block or stream. func (e *Encoder) MaxEncodedSize(size int) int { frameHeader := 4 + 2 // magic + frame header & window descriptor if e.o.dict != nil { frameHeader += 4 } // Frame content size: if size < 256 { frameHeader++ } else if size < 65536+256 { frameHeader += 2 } else if size < math.MaxInt32 { frameHeader += 4 } else { frameHeader += 8 } // Final crc if e.o.crc { frameHeader += 4 } // Max overhead is 3 bytes/block. // There cannot be 0 blocks. blocks := (size + e.o.blockSize) / e.o.blockSize // Combine, add padding. maxSz := frameHeader + 3*blocks + size if e.o.pad > 1 { maxSz += calcSkippableFrame(int64(maxSz), int64(e.o.pad)) } return maxSz } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/encoder_options.go ================================================ package zstd import ( "errors" "fmt" "math" "math/bits" "runtime" "strings" ) // EOption is an option for creating a encoder. type EOption func(*encoderOptions) error // options retains accumulated state of multiple options. type encoderOptions struct { concurrent int level EncoderLevel single *bool pad int blockSize int windowSize int crc bool fullZero bool noEntropy bool allLitEntropy bool customWindow bool customALEntropy bool customBlockSize bool lowMem bool dict *dict } func (o *encoderOptions) setDefault() { *o = encoderOptions{ concurrent: runtime.GOMAXPROCS(0), crc: true, single: nil, blockSize: maxCompressedBlockSize, windowSize: 8 << 20, level: SpeedDefault, allLitEntropy: false, lowMem: false, } } // encoder returns an encoder with the selected options. func (o encoderOptions) encoder() encoder { switch o.level { case SpeedFastest: if o.dict != nil { return &fastEncoderDict{fastEncoder: fastEncoder{fastBase: fastBase{maxMatchOff: int32(o.windowSize), bufferReset: math.MaxInt32 - int32(o.windowSize*2), lowMem: o.lowMem}}} } return &fastEncoder{fastBase: fastBase{maxMatchOff: int32(o.windowSize), bufferReset: math.MaxInt32 - int32(o.windowSize*2), lowMem: o.lowMem}} case SpeedDefault: if o.dict != nil { return &doubleFastEncoderDict{fastEncoderDict: fastEncoderDict{fastEncoder: fastEncoder{fastBase: fastBase{maxMatchOff: int32(o.windowSize), bufferReset: math.MaxInt32 - int32(o.windowSize*2), lowMem: o.lowMem}}}} } return &doubleFastEncoder{fastEncoder: fastEncoder{fastBase: fastBase{maxMatchOff: int32(o.windowSize), bufferReset: math.MaxInt32 - int32(o.windowSize*2), lowMem: o.lowMem}}} case SpeedBetterCompression: if o.dict != nil { return &betterFastEncoderDict{betterFastEncoder: betterFastEncoder{fastBase: fastBase{maxMatchOff: int32(o.windowSize), bufferReset: math.MaxInt32 - int32(o.windowSize*2), lowMem: o.lowMem}}} } return &betterFastEncoder{fastBase: fastBase{maxMatchOff: int32(o.windowSize), bufferReset: math.MaxInt32 - int32(o.windowSize*2), lowMem: o.lowMem}} case SpeedBestCompression: return &bestFastEncoder{fastBase: fastBase{maxMatchOff: int32(o.windowSize), bufferReset: math.MaxInt32 - int32(o.windowSize*2), lowMem: o.lowMem}} } panic("unknown compression level") } // WithEncoderCRC will add CRC value to output. // Output will be 4 bytes larger. func WithEncoderCRC(b bool) EOption { return func(o *encoderOptions) error { o.crc = b; return nil } } // WithEncoderConcurrency will set the concurrency, // meaning the maximum number of encoders to run concurrently. // The value supplied must be at least 1. // For streams, setting a value of 1 will disable async compression. // By default this will be set to GOMAXPROCS. func WithEncoderConcurrency(n int) EOption { return func(o *encoderOptions) error { if n <= 0 { return fmt.Errorf("concurrency must be at least 1") } o.concurrent = n return nil } } // WithWindowSize will set the maximum allowed back-reference distance. // The value must be a power of two between MinWindowSize and MaxWindowSize. // A larger value will enable better compression but allocate more memory and, // for above-default values, take considerably longer. // The default value is determined by the compression level. func WithWindowSize(n int) EOption { return func(o *encoderOptions) error { switch { case n < MinWindowSize: return fmt.Errorf("window size must be at least %d", MinWindowSize) case n > MaxWindowSize: return fmt.Errorf("window size must be at most %d", MaxWindowSize) case (n & (n - 1)) != 0: return errors.New("window size must be a power of 2") } o.windowSize = n o.customWindow = true if o.blockSize > o.windowSize { o.blockSize = o.windowSize o.customBlockSize = true } return nil } } // WithEncoderPadding will add padding to all output so the size will be a multiple of n. // This can be used to obfuscate the exact output size or make blocks of a certain size. // The contents will be a skippable frame, so it will be invisible by the decoder. // n must be > 0 and <= 1GB, 1<<30 bytes. // The padded area will be filled with data from crypto/rand.Reader. // If `EncodeAll` is used with data already in the destination, the total size will be multiple of this. func WithEncoderPadding(n int) EOption { return func(o *encoderOptions) error { if n <= 0 { return fmt.Errorf("padding must be at least 1") } // No need to waste our time. if n == 1 { n = 0 } if n > 1<<30 { return fmt.Errorf("padding must less than 1GB (1<<30 bytes) ") } o.pad = n return nil } } // EncoderLevel predefines encoder compression levels. // Only use the constants made available, since the actual mapping // of these values are very likely to change and your compression could change // unpredictably when upgrading the library. type EncoderLevel int const ( speedNotSet EncoderLevel = iota // SpeedFastest will choose the fastest reasonable compression. // This is roughly equivalent to the fastest Zstandard mode. SpeedFastest // SpeedDefault is the default "pretty fast" compression option. // This is roughly equivalent to the default Zstandard mode (level 3). SpeedDefault // SpeedBetterCompression will yield better compression than the default. // Currently it is about zstd level 7-8 with ~ 2x-3x the default CPU usage. // By using this, notice that CPU usage may go up in the future. SpeedBetterCompression // SpeedBestCompression will choose the best available compression option. // This will offer the best compression no matter the CPU cost. SpeedBestCompression // speedLast should be kept as the last actual compression option. // The is not for external usage, but is used to keep track of the valid options. speedLast ) // EncoderLevelFromString will convert a string representation of an encoding level back // to a compression level. The compare is not case sensitive. // If the string wasn't recognized, (false, SpeedDefault) will be returned. func EncoderLevelFromString(s string) (bool, EncoderLevel) { for l := speedNotSet + 1; l < speedLast; l++ { if strings.EqualFold(s, l.String()) { return true, l } } return false, SpeedDefault } // EncoderLevelFromZstd will return an encoder level that closest matches the compression // ratio of a specific zstd compression level. // Many input values will provide the same compression level. func EncoderLevelFromZstd(level int) EncoderLevel { switch { case level < 3: return SpeedFastest case level >= 3 && level < 6: return SpeedDefault case level >= 6 && level < 10: return SpeedBetterCompression default: return SpeedBestCompression } } // String provides a string representation of the compression level. func (e EncoderLevel) String() string { switch e { case SpeedFastest: return "fastest" case SpeedDefault: return "default" case SpeedBetterCompression: return "better" case SpeedBestCompression: return "best" default: return "invalid" } } // WithEncoderLevel specifies a predefined compression level. func WithEncoderLevel(l EncoderLevel) EOption { return func(o *encoderOptions) error { switch { case l <= speedNotSet || l >= speedLast: return fmt.Errorf("unknown encoder level") } o.level = l if !o.customWindow { switch o.level { case SpeedFastest: o.windowSize = 4 << 20 if !o.customBlockSize { o.blockSize = 1 << 16 } case SpeedDefault: o.windowSize = 8 << 20 case SpeedBetterCompression: o.windowSize = 16 << 20 case SpeedBestCompression: o.windowSize = 32 << 20 } } if !o.customALEntropy { o.allLitEntropy = l > SpeedDefault } return nil } } // WithZeroFrames will encode 0 length input as full frames. // This can be needed for compatibility with zstandard usage, // but is not needed for this package. func WithZeroFrames(b bool) EOption { return func(o *encoderOptions) error { o.fullZero = b return nil } } // WithAllLitEntropyCompression will apply entropy compression if no matches are found. // Disabling this will skip incompressible data faster, but in cases with no matches but // skewed character distribution compression is lost. // Default value depends on the compression level selected. func WithAllLitEntropyCompression(b bool) EOption { return func(o *encoderOptions) error { o.customALEntropy = true o.allLitEntropy = b return nil } } // WithNoEntropyCompression will always skip entropy compression of literals. // This can be useful if content has matches, but unlikely to benefit from entropy // compression. Usually the slight speed improvement is not worth enabling this. func WithNoEntropyCompression(b bool) EOption { return func(o *encoderOptions) error { o.noEntropy = b return nil } } // WithSingleSegment will set the "single segment" flag when EncodeAll is used. // If this flag is set, data must be regenerated within a single continuous memory segment. // In this case, Window_Descriptor byte is skipped, but Frame_Content_Size is necessarily present. // As a consequence, the decoder must allocate a memory segment of size equal or larger than size of your content. // In order to preserve the decoder from unreasonable memory requirements, // a decoder is allowed to reject a compressed frame which requests a memory size beyond decoder's authorized range. // For broader compatibility, decoders are recommended to support memory sizes of at least 8 MB. // This is only a recommendation, each decoder is free to support higher or lower limits, depending on local limitations. // If this is not specified, block encodes will automatically choose this based on the input size and the window size. // This setting has no effect on streamed encodes. func WithSingleSegment(b bool) EOption { return func(o *encoderOptions) error { o.single = &b return nil } } // WithLowerEncoderMem will trade in some memory cases trade less memory usage for // slower encoding speed. // This will not change the window size which is the primary function for reducing // memory usage. See WithWindowSize. func WithLowerEncoderMem(b bool) EOption { return func(o *encoderOptions) error { o.lowMem = b return nil } } // WithEncoderDict allows to register a dictionary that will be used for the encode. // // The slice dict must be in the [dictionary format] produced by // "zstd --train" from the Zstandard reference implementation. // // The encoder *may* choose to use no dictionary instead for certain payloads. // // [dictionary format]: https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#dictionary-format func WithEncoderDict(dict []byte) EOption { return func(o *encoderOptions) error { d, err := loadDict(dict) if err != nil { return err } o.dict = d return nil } } // WithEncoderDictRaw registers a dictionary that may be used by the encoder. // // The slice content may contain arbitrary data. It will be used as an initial // history. func WithEncoderDictRaw(id uint32, content []byte) EOption { return func(o *encoderOptions) error { if bits.UintSize > 32 && uint(len(content)) > dictMaxLength { return fmt.Errorf("dictionary of size %d > 2GiB too large", len(content)) } o.dict = &dict{id: id, content: content, offsets: [3]int{1, 4, 8}} return nil } } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/framedec.go ================================================ // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. // Based on work by Yann Collet, released under BSD License. package zstd import ( "encoding/binary" "encoding/hex" "errors" "io" "github.com/klauspost/compress/zstd/internal/xxhash" ) type frameDec struct { o decoderOptions crc *xxhash.Digest WindowSize uint64 // Frame history passed between blocks history history rawInput byteBuffer // Byte buffer that can be reused for small input blocks. bBuf byteBuf FrameContentSize uint64 DictionaryID uint32 HasCheckSum bool SingleSegment bool } const ( // MinWindowSize is the minimum Window Size, which is 1 KB. MinWindowSize = 1 << 10 // MaxWindowSize is the maximum encoder window size // and the default decoder maximum window size. MaxWindowSize = 1 << 29 ) const ( frameMagic = "\x28\xb5\x2f\xfd" skippableFrameMagic = "\x2a\x4d\x18" ) func newFrameDec(o decoderOptions) *frameDec { if o.maxWindowSize > o.maxDecodedSize { o.maxWindowSize = o.maxDecodedSize } d := frameDec{ o: o, } return &d } // reset will read the frame header and prepare for block decoding. // If nothing can be read from the input, io.EOF will be returned. // Any other error indicated that the stream contained data, but // there was a problem. func (d *frameDec) reset(br byteBuffer) error { d.HasCheckSum = false d.WindowSize = 0 var signature [4]byte for { var err error // Check if we can read more... b, err := br.readSmall(1) switch err { case io.EOF, io.ErrUnexpectedEOF: return io.EOF case nil: signature[0] = b[0] default: return err } // Read the rest, don't allow io.ErrUnexpectedEOF b, err = br.readSmall(3) switch err { case io.EOF: return io.EOF case nil: copy(signature[1:], b) default: return err } if string(signature[1:4]) != skippableFrameMagic || signature[0]&0xf0 != 0x50 { if debugDecoder { println("Not skippable", hex.EncodeToString(signature[:]), hex.EncodeToString([]byte(skippableFrameMagic))) } // Break if not skippable frame. break } // Read size to skip b, err = br.readSmall(4) if err != nil { if debugDecoder { println("Reading Frame Size", err) } return err } n := uint32(b[0]) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24) println("Skipping frame with", n, "bytes.") err = br.skipN(int64(n)) if err != nil { if debugDecoder { println("Reading discarded frame", err) } return err } } if string(signature[:]) != frameMagic { if debugDecoder { println("Got magic numbers: ", signature, "want:", []byte(frameMagic)) } return ErrMagicMismatch } // Read Frame_Header_Descriptor fhd, err := br.readByte() if err != nil { if debugDecoder { println("Reading Frame_Header_Descriptor", err) } return err } d.SingleSegment = fhd&(1<<5) != 0 if fhd&(1<<3) != 0 { return errors.New("reserved bit set on frame header") } // Read Window_Descriptor // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#window_descriptor d.WindowSize = 0 if !d.SingleSegment { wd, err := br.readByte() if err != nil { if debugDecoder { println("Reading Window_Descriptor", err) } return err } printf("raw: %x, mantissa: %d, exponent: %d\n", wd, wd&7, wd>>3) windowLog := 10 + (wd >> 3) windowBase := uint64(1) << windowLog windowAdd := (windowBase / 8) * uint64(wd&0x7) d.WindowSize = windowBase + windowAdd } // Read Dictionary_ID // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#dictionary_id d.DictionaryID = 0 if size := fhd & 3; size != 0 { if size == 3 { size = 4 } b, err := br.readSmall(int(size)) if err != nil { println("Reading Dictionary_ID", err) return err } var id uint32 switch len(b) { case 1: id = uint32(b[0]) case 2: id = uint32(b[0]) | (uint32(b[1]) << 8) case 4: id = uint32(b[0]) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24) } if debugDecoder { println("Dict size", size, "ID:", id) } d.DictionaryID = id } // Read Frame_Content_Size // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#frame_content_size var fcsSize int v := fhd >> 6 switch v { case 0: if d.SingleSegment { fcsSize = 1 } default: fcsSize = 1 << v } d.FrameContentSize = fcsUnknown if fcsSize > 0 { b, err := br.readSmall(fcsSize) if err != nil { println("Reading Frame content", err) return err } switch len(b) { case 1: d.FrameContentSize = uint64(b[0]) case 2: // When FCS_Field_Size is 2, the offset of 256 is added. d.FrameContentSize = uint64(b[0]) | (uint64(b[1]) << 8) + 256 case 4: d.FrameContentSize = uint64(b[0]) | (uint64(b[1]) << 8) | (uint64(b[2]) << 16) | (uint64(b[3]) << 24) case 8: d1 := uint32(b[0]) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24) d2 := uint32(b[4]) | (uint32(b[5]) << 8) | (uint32(b[6]) << 16) | (uint32(b[7]) << 24) d.FrameContentSize = uint64(d1) | (uint64(d2) << 32) } if debugDecoder { println("Read FCS:", d.FrameContentSize) } } // Move this to shared. d.HasCheckSum = fhd&(1<<2) != 0 if d.HasCheckSum { if d.crc == nil { d.crc = xxhash.New() } d.crc.Reset() } if d.WindowSize > d.o.maxWindowSize { if debugDecoder { printf("window size %d > max %d\n", d.WindowSize, d.o.maxWindowSize) } return ErrWindowSizeExceeded } if d.WindowSize == 0 && d.SingleSegment { // We may not need window in this case. d.WindowSize = d.FrameContentSize if d.WindowSize < MinWindowSize { d.WindowSize = MinWindowSize } if d.WindowSize > d.o.maxDecodedSize { if debugDecoder { printf("window size %d > max %d\n", d.WindowSize, d.o.maxWindowSize) } return ErrDecoderSizeExceeded } } // The minimum Window_Size is 1 KB. if d.WindowSize < MinWindowSize { if debugDecoder { println("got window size: ", d.WindowSize) } return ErrWindowSizeTooSmall } d.history.windowSize = int(d.WindowSize) if !d.o.lowMem || d.history.windowSize < maxBlockSize { // Alloc 2x window size if not low-mem, or window size below 2MB. d.history.allocFrameBuffer = d.history.windowSize * 2 } else { if d.o.lowMem { // Alloc with 1MB extra. d.history.allocFrameBuffer = d.history.windowSize + maxBlockSize/2 } else { // Alloc with 2MB extra. d.history.allocFrameBuffer = d.history.windowSize + maxBlockSize } } if debugDecoder { println("Frame: Dict:", d.DictionaryID, "FrameContentSize:", d.FrameContentSize, "singleseg:", d.SingleSegment, "window:", d.WindowSize, "crc:", d.HasCheckSum) } // history contains input - maybe we do something d.rawInput = br return nil } // next will start decoding the next block from stream. func (d *frameDec) next(block *blockDec) error { if debugDecoder { println("decoding new block") } err := block.reset(d.rawInput, d.WindowSize) if err != nil { println("block error:", err) // Signal the frame decoder we have a problem. block.sendErr(err) return err } return nil } // checkCRC will check the checksum, assuming the frame has one. // Will return ErrCRCMismatch if crc check failed, otherwise nil. func (d *frameDec) checkCRC() error { // We can overwrite upper tmp now buf, err := d.rawInput.readSmall(4) if err != nil { println("CRC missing?", err) return err } want := binary.LittleEndian.Uint32(buf[:4]) got := uint32(d.crc.Sum64()) if got != want { if debugDecoder { printf("CRC check failed: got %08x, want %08x\n", got, want) } return ErrCRCMismatch } if debugDecoder { printf("CRC ok %08x\n", got) } return nil } // consumeCRC skips over the checksum, assuming the frame has one. func (d *frameDec) consumeCRC() error { _, err := d.rawInput.readSmall(4) if err != nil { println("CRC missing?", err) } return err } // runDecoder will run the decoder for the remainder of the frame. func (d *frameDec) runDecoder(dst []byte, dec *blockDec) ([]byte, error) { saved := d.history.b // We use the history for output to avoid copying it. d.history.b = dst d.history.ignoreBuffer = len(dst) // Store input length, so we only check new data. crcStart := len(dst) d.history.decoders.maxSyncLen = 0 if d.o.limitToCap { d.history.decoders.maxSyncLen = uint64(cap(dst) - len(dst)) } if d.FrameContentSize != fcsUnknown { if !d.o.limitToCap || d.FrameContentSize+uint64(len(dst)) < d.history.decoders.maxSyncLen { d.history.decoders.maxSyncLen = d.FrameContentSize + uint64(len(dst)) } if d.history.decoders.maxSyncLen > d.o.maxDecodedSize { if debugDecoder { println("maxSyncLen:", d.history.decoders.maxSyncLen, "> maxDecodedSize:", d.o.maxDecodedSize) } return dst, ErrDecoderSizeExceeded } if debugDecoder { println("maxSyncLen:", d.history.decoders.maxSyncLen) } if !d.o.limitToCap && uint64(cap(dst)) < d.history.decoders.maxSyncLen { // Alloc for output dst2 := make([]byte, len(dst), d.history.decoders.maxSyncLen+compressedBlockOverAlloc) copy(dst2, dst) dst = dst2 } } var err error for { err = dec.reset(d.rawInput, d.WindowSize) if err != nil { break } if debugDecoder { println("next block:", dec) } err = dec.decodeBuf(&d.history) if err != nil { break } if uint64(len(d.history.b)-crcStart) > d.o.maxDecodedSize { println("runDecoder: maxDecodedSize exceeded", uint64(len(d.history.b)-crcStart), ">", d.o.maxDecodedSize) err = ErrDecoderSizeExceeded break } if d.o.limitToCap && len(d.history.b) > cap(dst) { println("runDecoder: cap exceeded", uint64(len(d.history.b)), ">", cap(dst)) err = ErrDecoderSizeExceeded break } if uint64(len(d.history.b)-crcStart) > d.FrameContentSize { println("runDecoder: FrameContentSize exceeded", uint64(len(d.history.b)-crcStart), ">", d.FrameContentSize) err = ErrFrameSizeExceeded break } if dec.Last { break } if debugDecoder { println("runDecoder: FrameContentSize", uint64(len(d.history.b)-crcStart), "<=", d.FrameContentSize) } } dst = d.history.b if err == nil { if d.FrameContentSize != fcsUnknown && uint64(len(d.history.b)-crcStart) != d.FrameContentSize { err = ErrFrameSizeMismatch } else if d.HasCheckSum { if d.o.ignoreChecksum { err = d.consumeCRC() } else { d.crc.Write(dst[crcStart:]) err = d.checkCRC() } } } d.history.b = saved return dst, err } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/frameenc.go ================================================ // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. // Based on work by Yann Collet, released under BSD License. package zstd import ( "encoding/binary" "fmt" "io" "math" "math/bits" ) type frameHeader struct { ContentSize uint64 WindowSize uint32 SingleSegment bool Checksum bool DictID uint32 } const maxHeaderSize = 14 func (f frameHeader) appendTo(dst []byte) []byte { dst = append(dst, frameMagic...) var fhd uint8 if f.Checksum { fhd |= 1 << 2 } if f.SingleSegment { fhd |= 1 << 5 } var dictIDContent []byte if f.DictID > 0 { var tmp [4]byte if f.DictID < 256 { fhd |= 1 tmp[0] = uint8(f.DictID) dictIDContent = tmp[:1] } else if f.DictID < 1<<16 { fhd |= 2 binary.LittleEndian.PutUint16(tmp[:2], uint16(f.DictID)) dictIDContent = tmp[:2] } else { fhd |= 3 binary.LittleEndian.PutUint32(tmp[:4], f.DictID) dictIDContent = tmp[:4] } } var fcs uint8 if f.ContentSize >= 256 { fcs++ } if f.ContentSize >= 65536+256 { fcs++ } if f.ContentSize >= 0xffffffff { fcs++ } fhd |= fcs << 6 dst = append(dst, fhd) if !f.SingleSegment { const winLogMin = 10 windowLog := (bits.Len32(f.WindowSize-1) - winLogMin) << 3 dst = append(dst, uint8(windowLog)) } if f.DictID > 0 { dst = append(dst, dictIDContent...) } switch fcs { case 0: if f.SingleSegment { dst = append(dst, uint8(f.ContentSize)) } // Unless SingleSegment is set, framessizes < 256 are nto stored. case 1: f.ContentSize -= 256 dst = append(dst, uint8(f.ContentSize), uint8(f.ContentSize>>8)) case 2: dst = append(dst, uint8(f.ContentSize), uint8(f.ContentSize>>8), uint8(f.ContentSize>>16), uint8(f.ContentSize>>24)) case 3: dst = append(dst, uint8(f.ContentSize), uint8(f.ContentSize>>8), uint8(f.ContentSize>>16), uint8(f.ContentSize>>24), uint8(f.ContentSize>>32), uint8(f.ContentSize>>40), uint8(f.ContentSize>>48), uint8(f.ContentSize>>56)) default: panic("invalid fcs") } return dst } const skippableFrameHeader = 4 + 4 // calcSkippableFrame will return a total size to be added for written // to be divisible by multiple. // The value will always be > skippableFrameHeader. // The function will panic if written < 0 or wantMultiple <= 0. func calcSkippableFrame(written, wantMultiple int64) int { if wantMultiple <= 0 { panic("wantMultiple <= 0") } if written < 0 { panic("written < 0") } leftOver := written % wantMultiple if leftOver == 0 { return 0 } toAdd := wantMultiple - leftOver for toAdd < skippableFrameHeader { toAdd += wantMultiple } return int(toAdd) } // skippableFrame will add a skippable frame with a total size of bytes. // total should be >= skippableFrameHeader and < math.MaxUint32. func skippableFrame(dst []byte, total int, r io.Reader) ([]byte, error) { if total == 0 { return dst, nil } if total < skippableFrameHeader { return dst, fmt.Errorf("requested skippable frame (%d) < 8", total) } if int64(total) > math.MaxUint32 { return dst, fmt.Errorf("requested skippable frame (%d) > max uint32", total) } dst = append(dst, 0x50, 0x2a, 0x4d, 0x18) f := uint32(total - skippableFrameHeader) dst = append(dst, uint8(f), uint8(f>>8), uint8(f>>16), uint8(f>>24)) start := len(dst) dst = append(dst, make([]byte, f)...) _, err := io.ReadFull(r, dst[start:]) return dst, err } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/fse_decoder.go ================================================ // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. // Based on work by Yann Collet, released under BSD License. package zstd import ( "encoding/binary" "errors" "fmt" "io" ) const ( tablelogAbsoluteMax = 9 ) const ( /*!MEMORY_USAGE : * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) * Increasing memory usage improves compression ratio * Reduced memory usage can improve speed, due to cache effect * Recommended max value is 14, for 16KB, which nicely fits into Intel x86 L1 cache */ maxMemoryUsage = tablelogAbsoluteMax + 2 maxTableLog = maxMemoryUsage - 2 maxTablesize = 1 << maxTableLog maxTableMask = (1 << maxTableLog) - 1 minTablelog = 5 maxSymbolValue = 255 ) // fseDecoder provides temporary storage for compression and decompression. type fseDecoder struct { dt [maxTablesize]decSymbol // Decompression table. symbolLen uint16 // Length of active part of the symbol table. actualTableLog uint8 // Selected tablelog. maxBits uint8 // Maximum number of additional bits // used for table creation to avoid allocations. stateTable [256]uint16 norm [maxSymbolValue + 1]int16 preDefined bool } // tableStep returns the next table index. func tableStep(tableSize uint32) uint32 { return (tableSize >> 1) + (tableSize >> 3) + 3 } // readNCount will read the symbol distribution so decoding tables can be constructed. func (s *fseDecoder) readNCount(b *byteReader, maxSymbol uint16) error { var ( charnum uint16 previous0 bool ) if b.remain() < 4 { return errors.New("input too small") } bitStream := b.Uint32NC() nbBits := uint((bitStream & 0xF) + minTablelog) // extract tableLog if nbBits > tablelogAbsoluteMax { println("Invalid tablelog:", nbBits) return errors.New("tableLog too large") } bitStream >>= 4 bitCount := uint(4) s.actualTableLog = uint8(nbBits) remaining := int32((1 << nbBits) + 1) threshold := int32(1 << nbBits) gotTotal := int32(0) nbBits++ for remaining > 1 && charnum <= maxSymbol { if previous0 { //println("prev0") n0 := charnum for (bitStream & 0xFFFF) == 0xFFFF { //println("24 x 0") n0 += 24 if r := b.remain(); r > 5 { b.advance(2) // The check above should make sure we can read 32 bits bitStream = b.Uint32NC() >> bitCount } else { // end of bit stream bitStream >>= 16 bitCount += 16 } } //printf("bitstream: %d, 0b%b", bitStream&3, bitStream) for (bitStream & 3) == 3 { n0 += 3 bitStream >>= 2 bitCount += 2 } n0 += uint16(bitStream & 3) bitCount += 2 if n0 > maxSymbolValue { return errors.New("maxSymbolValue too small") } //println("inserting ", n0-charnum, "zeroes from idx", charnum, "ending before", n0) for charnum < n0 { s.norm[uint8(charnum)] = 0 charnum++ } if r := b.remain(); r >= 7 || r-int(bitCount>>3) >= 4 { b.advance(bitCount >> 3) bitCount &= 7 // The check above should make sure we can read 32 bits bitStream = b.Uint32NC() >> bitCount } else { bitStream >>= 2 } } max := (2*threshold - 1) - remaining var count int32 if int32(bitStream)&(threshold-1) < max { count = int32(bitStream) & (threshold - 1) if debugAsserts && nbBits < 1 { panic("nbBits underflow") } bitCount += nbBits - 1 } else { count = int32(bitStream) & (2*threshold - 1) if count >= threshold { count -= max } bitCount += nbBits } // extra accuracy count-- if count < 0 { // -1 means +1 remaining += count gotTotal -= count } else { remaining -= count gotTotal += count } s.norm[charnum&0xff] = int16(count) charnum++ previous0 = count == 0 for remaining < threshold { nbBits-- threshold >>= 1 } if r := b.remain(); r >= 7 || r-int(bitCount>>3) >= 4 { b.advance(bitCount >> 3) bitCount &= 7 // The check above should make sure we can read 32 bits bitStream = b.Uint32NC() >> (bitCount & 31) } else { bitCount -= (uint)(8 * (len(b.b) - 4 - b.off)) b.off = len(b.b) - 4 bitStream = b.Uint32() >> (bitCount & 31) } } s.symbolLen = charnum if s.symbolLen <= 1 { return fmt.Errorf("symbolLen (%d) too small", s.symbolLen) } if s.symbolLen > maxSymbolValue+1 { return fmt.Errorf("symbolLen (%d) too big", s.symbolLen) } if remaining != 1 { return fmt.Errorf("corruption detected (remaining %d != 1)", remaining) } if bitCount > 32 { return fmt.Errorf("corruption detected (bitCount %d > 32)", bitCount) } if gotTotal != 1<> 3) return s.buildDtable() } func (s *fseDecoder) mustReadFrom(r io.Reader) { fatalErr := func(err error) { if err != nil { panic(err) } } // dt [maxTablesize]decSymbol // Decompression table. // symbolLen uint16 // Length of active part of the symbol table. // actualTableLog uint8 // Selected tablelog. // maxBits uint8 // Maximum number of additional bits // // used for table creation to avoid allocations. // stateTable [256]uint16 // norm [maxSymbolValue + 1]int16 // preDefined bool fatalErr(binary.Read(r, binary.LittleEndian, &s.dt)) fatalErr(binary.Read(r, binary.LittleEndian, &s.symbolLen)) fatalErr(binary.Read(r, binary.LittleEndian, &s.actualTableLog)) fatalErr(binary.Read(r, binary.LittleEndian, &s.maxBits)) fatalErr(binary.Read(r, binary.LittleEndian, &s.stateTable)) fatalErr(binary.Read(r, binary.LittleEndian, &s.norm)) fatalErr(binary.Read(r, binary.LittleEndian, &s.preDefined)) } // decSymbol contains information about a state entry, // Including the state offset base, the output symbol and // the number of bits to read for the low part of the destination state. // Using a composite uint64 is faster than a struct with separate members. type decSymbol uint64 func newDecSymbol(nbits, addBits uint8, newState uint16, baseline uint32) decSymbol { return decSymbol(nbits) | (decSymbol(addBits) << 8) | (decSymbol(newState) << 16) | (decSymbol(baseline) << 32) } func (d decSymbol) nbBits() uint8 { return uint8(d) } func (d decSymbol) addBits() uint8 { return uint8(d >> 8) } func (d decSymbol) newState() uint16 { return uint16(d >> 16) } func (d decSymbol) baselineInt() int { return int(d >> 32) } func (d *decSymbol) setNBits(nBits uint8) { const mask = 0xffffffffffffff00 *d = (*d & mask) | decSymbol(nBits) } func (d *decSymbol) setAddBits(addBits uint8) { const mask = 0xffffffffffff00ff *d = (*d & mask) | (decSymbol(addBits) << 8) } func (d *decSymbol) setNewState(state uint16) { const mask = 0xffffffff0000ffff *d = (*d & mask) | decSymbol(state)<<16 } func (d *decSymbol) setExt(addBits uint8, baseline uint32) { const mask = 0xffff00ff *d = (*d & mask) | (decSymbol(addBits) << 8) | (decSymbol(baseline) << 32) } // decSymbolValue returns the transformed decSymbol for the given symbol. func decSymbolValue(symb uint8, t []baseOffset) (decSymbol, error) { if int(symb) >= len(t) { return 0, fmt.Errorf("rle symbol %d >= max %d", symb, len(t)) } lu := t[symb] return newDecSymbol(0, lu.addBits, 0, lu.baseLine), nil } // setRLE will set the decoder til RLE mode. func (s *fseDecoder) setRLE(symbol decSymbol) { s.actualTableLog = 0 s.maxBits = symbol.addBits() s.dt[0] = symbol } // transform will transform the decoder table into a table usable for // decoding without having to apply the transformation while decoding. // The state will contain the base value and the number of bits to read. func (s *fseDecoder) transform(t []baseOffset) error { tableSize := uint16(1 << s.actualTableLog) s.maxBits = 0 for i, v := range s.dt[:tableSize] { add := v.addBits() if int(add) >= len(t) { return fmt.Errorf("invalid decoding table entry %d, symbol %d >= max (%d)", i, v.addBits(), len(t)) } lu := t[add] if lu.addBits > s.maxBits { s.maxBits = lu.addBits } v.setExt(lu.addBits, lu.baseLine) s.dt[i] = v } return nil } type fseState struct { dt []decSymbol state decSymbol } // Initialize and decodeAsync first state and symbol. func (s *fseState) init(br *bitReader, tableLog uint8, dt []decSymbol) { s.dt = dt br.fill() s.state = dt[br.getBits(tableLog)] } // final returns the current state symbol without decoding the next. func (s decSymbol) final() (int, uint8) { return s.baselineInt(), s.addBits() } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.go ================================================ //go:build amd64 && !appengine && !noasm && gc // +build amd64,!appengine,!noasm,gc package zstd import ( "fmt" ) type buildDtableAsmContext struct { // inputs stateTable *uint16 norm *int16 dt *uint64 // outputs --- set by the procedure in the case of error; // for interpretation please see the error handling part below errParam1 uint64 errParam2 uint64 } // buildDtable_asm is an x86 assembly implementation of fseDecoder.buildDtable. // Function returns non-zero exit code on error. // //go:noescape func buildDtable_asm(s *fseDecoder, ctx *buildDtableAsmContext) int // please keep in sync with _generate/gen_fse.go const ( errorCorruptedNormalizedCounter = 1 errorNewStateTooBig = 2 errorNewStateNoBits = 3 ) // buildDtable will build the decoding table. func (s *fseDecoder) buildDtable() error { ctx := buildDtableAsmContext{ stateTable: &s.stateTable[0], norm: &s.norm[0], dt: (*uint64)(&s.dt[0]), } code := buildDtable_asm(s, &ctx) if code != 0 { switch code { case errorCorruptedNormalizedCounter: position := ctx.errParam1 return fmt.Errorf("corrupted input (position=%d, expected 0)", position) case errorNewStateTooBig: newState := decSymbol(ctx.errParam1) size := ctx.errParam2 return fmt.Errorf("newState (%d) outside table size (%d)", newState, size) case errorNewStateNoBits: newState := decSymbol(ctx.errParam1) oldState := decSymbol(ctx.errParam2) return fmt.Errorf("newState (%d) == oldState (%d) and no bits", newState, oldState) default: return fmt.Errorf("buildDtable_asm returned unhandled nonzero code = %d", code) } } return nil } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.s ================================================ // Code generated by command: go run gen_fse.go -out ../fse_decoder_amd64.s -pkg=zstd. DO NOT EDIT. //go:build !appengine && !noasm && gc && !noasm // func buildDtable_asm(s *fseDecoder, ctx *buildDtableAsmContext) int TEXT ·buildDtable_asm(SB), $0-24 MOVQ ctx+8(FP), CX MOVQ s+0(FP), DI // Load values MOVBQZX 4098(DI), DX XORQ AX, AX BTSQ DX, AX MOVQ (CX), BX MOVQ 16(CX), SI LEAQ -1(AX), R8 MOVQ 8(CX), CX MOVWQZX 4096(DI), DI // End load values // Init, lay down lowprob symbols XORQ R9, R9 JMP init_main_loop_condition init_main_loop: MOVWQSX (CX)(R9*2), R10 CMPW R10, $-1 JNE do_not_update_high_threshold MOVB R9, 1(SI)(R8*8) DECQ R8 MOVQ $0x0000000000000001, R10 do_not_update_high_threshold: MOVW R10, (BX)(R9*2) INCQ R9 init_main_loop_condition: CMPQ R9, DI JL init_main_loop // Spread symbols // Calculate table step MOVQ AX, R9 SHRQ $0x01, R9 MOVQ AX, R10 SHRQ $0x03, R10 LEAQ 3(R9)(R10*1), R9 // Fill add bits values LEAQ -1(AX), R10 XORQ R11, R11 XORQ R12, R12 JMP spread_main_loop_condition spread_main_loop: XORQ R13, R13 MOVWQSX (CX)(R12*2), R14 JMP spread_inner_loop_condition spread_inner_loop: MOVB R12, 1(SI)(R11*8) adjust_position: ADDQ R9, R11 ANDQ R10, R11 CMPQ R11, R8 JG adjust_position INCQ R13 spread_inner_loop_condition: CMPQ R13, R14 JL spread_inner_loop INCQ R12 spread_main_loop_condition: CMPQ R12, DI JL spread_main_loop TESTQ R11, R11 JZ spread_check_ok MOVQ ctx+8(FP), AX MOVQ R11, 24(AX) MOVQ $+1, ret+16(FP) RET spread_check_ok: // Build Decoding table XORQ DI, DI build_table_main_table: MOVBQZX 1(SI)(DI*8), CX MOVWQZX (BX)(CX*2), R8 LEAQ 1(R8), R9 MOVW R9, (BX)(CX*2) MOVQ R8, R9 BSRQ R9, R9 MOVQ DX, CX SUBQ R9, CX SHLQ CL, R8 SUBQ AX, R8 MOVB CL, (SI)(DI*8) MOVW R8, 2(SI)(DI*8) CMPQ R8, AX JLE build_table_check1_ok MOVQ ctx+8(FP), CX MOVQ R8, 24(CX) MOVQ AX, 32(CX) MOVQ $+2, ret+16(FP) RET build_table_check1_ok: TESTB CL, CL JNZ build_table_check2_ok CMPW R8, DI JNE build_table_check2_ok MOVQ ctx+8(FP), AX MOVQ R8, 24(AX) MOVQ DI, 32(AX) MOVQ $+3, ret+16(FP) RET build_table_check2_ok: INCQ DI CMPQ DI, AX JL build_table_main_table MOVQ $+0, ret+16(FP) RET ================================================ FILE: vendor/github.com/klauspost/compress/zstd/fse_decoder_generic.go ================================================ //go:build !amd64 || appengine || !gc || noasm // +build !amd64 appengine !gc noasm package zstd import ( "errors" "fmt" ) // buildDtable will build the decoding table. func (s *fseDecoder) buildDtable() error { tableSize := uint32(1 << s.actualTableLog) highThreshold := tableSize - 1 symbolNext := s.stateTable[:256] // Init, lay down lowprob symbols { for i, v := range s.norm[:s.symbolLen] { if v == -1 { s.dt[highThreshold].setAddBits(uint8(i)) highThreshold-- symbolNext[i] = 1 } else { symbolNext[i] = uint16(v) } } } // Spread symbols { tableMask := tableSize - 1 step := tableStep(tableSize) position := uint32(0) for ss, v := range s.norm[:s.symbolLen] { for i := 0; i < int(v); i++ { s.dt[position].setAddBits(uint8(ss)) position = (position + step) & tableMask for position > highThreshold { // lowprob area position = (position + step) & tableMask } } } if position != 0 { // position must reach all cells once, otherwise normalizedCounter is incorrect return errors.New("corrupted input (position != 0)") } } // Build Decoding table { tableSize := uint16(1 << s.actualTableLog) for u, v := range s.dt[:tableSize] { symbol := v.addBits() nextState := symbolNext[symbol] symbolNext[symbol] = nextState + 1 nBits := s.actualTableLog - byte(highBits(uint32(nextState))) s.dt[u&maxTableMask].setNBits(nBits) newState := (nextState << nBits) - tableSize if newState > tableSize { return fmt.Errorf("newState (%d) outside table size (%d)", newState, tableSize) } if newState == uint16(u) && nBits == 0 { // Seems weird that this is possible with nbits > 0. return fmt.Errorf("newState (%d) == oldState (%d) and no bits", newState, u) } s.dt[u&maxTableMask].setNewState(newState) } } return nil } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/fse_encoder.go ================================================ // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. // Based on work by Yann Collet, released under BSD License. package zstd import ( "errors" "fmt" "math" ) const ( // For encoding we only support up to maxEncTableLog = 8 maxEncTablesize = 1 << maxTableLog maxEncTableMask = (1 << maxTableLog) - 1 minEncTablelog = 5 maxEncSymbolValue = maxMatchLengthSymbol ) // Scratch provides temporary storage for compression and decompression. type fseEncoder struct { symbolLen uint16 // Length of active part of the symbol table. actualTableLog uint8 // Selected tablelog. ct cTable // Compression tables. maxCount int // count of the most probable symbol zeroBits bool // no bits has prob > 50%. clearCount bool // clear count useRLE bool // This encoder is for RLE preDefined bool // This encoder is predefined. reUsed bool // Set to know when the encoder has been reused. rleVal uint8 // RLE Symbol maxBits uint8 // Maximum output bits after transform. // TODO: Technically zstd should be fine with 64 bytes. count [256]uint32 norm [256]int16 } // cTable contains tables used for compression. type cTable struct { tableSymbol []byte stateTable []uint16 symbolTT []symbolTransform } // symbolTransform contains the state transform for a symbol. type symbolTransform struct { deltaNbBits uint32 deltaFindState int16 outBits uint8 } // String prints values as a human readable string. func (s symbolTransform) String() string { return fmt.Sprintf("{deltabits: %08x, findstate:%d outbits:%d}", s.deltaNbBits, s.deltaFindState, s.outBits) } // Histogram allows to populate the histogram and skip that step in the compression, // It otherwise allows to inspect the histogram when compression is done. // To indicate that you have populated the histogram call HistogramFinished // with the value of the highest populated symbol, as well as the number of entries // in the most populated entry. These are accepted at face value. func (s *fseEncoder) Histogram() *[256]uint32 { return &s.count } // HistogramFinished can be called to indicate that the histogram has been populated. // maxSymbol is the index of the highest set symbol of the next data segment. // maxCount is the number of entries in the most populated entry. // These are accepted at face value. func (s *fseEncoder) HistogramFinished(maxSymbol uint8, maxCount int) { s.maxCount = maxCount s.symbolLen = uint16(maxSymbol) + 1 s.clearCount = maxCount != 0 } // allocCtable will allocate tables needed for compression. // If existing tables a re big enough, they are simply re-used. func (s *fseEncoder) allocCtable() { tableSize := 1 << s.actualTableLog // get tableSymbol that is big enough. if cap(s.ct.tableSymbol) < tableSize { s.ct.tableSymbol = make([]byte, tableSize) } s.ct.tableSymbol = s.ct.tableSymbol[:tableSize] ctSize := tableSize if cap(s.ct.stateTable) < ctSize { s.ct.stateTable = make([]uint16, ctSize) } s.ct.stateTable = s.ct.stateTable[:ctSize] if cap(s.ct.symbolTT) < 256 { s.ct.symbolTT = make([]symbolTransform, 256) } s.ct.symbolTT = s.ct.symbolTT[:256] } // buildCTable will populate the compression table so it is ready to be used. func (s *fseEncoder) buildCTable() error { tableSize := uint32(1 << s.actualTableLog) highThreshold := tableSize - 1 var cumul [256]int16 s.allocCtable() tableSymbol := s.ct.tableSymbol[:tableSize] // symbol start positions { cumul[0] = 0 for ui, v := range s.norm[:s.symbolLen-1] { u := byte(ui) // one less than reference if v == -1 { // Low proba symbol cumul[u+1] = cumul[u] + 1 tableSymbol[highThreshold] = u highThreshold-- } else { cumul[u+1] = cumul[u] + v } } // Encode last symbol separately to avoid overflowing u u := int(s.symbolLen - 1) v := s.norm[s.symbolLen-1] if v == -1 { // Low proba symbol cumul[u+1] = cumul[u] + 1 tableSymbol[highThreshold] = byte(u) highThreshold-- } else { cumul[u+1] = cumul[u] + v } if uint32(cumul[s.symbolLen]) != tableSize { return fmt.Errorf("internal error: expected cumul[s.symbolLen] (%d) == tableSize (%d)", cumul[s.symbolLen], tableSize) } cumul[s.symbolLen] = int16(tableSize) + 1 } // Spread symbols s.zeroBits = false { step := tableStep(tableSize) tableMask := tableSize - 1 var position uint32 // if any symbol > largeLimit, we may have 0 bits output. largeLimit := int16(1 << (s.actualTableLog - 1)) for ui, v := range s.norm[:s.symbolLen] { symbol := byte(ui) if v > largeLimit { s.zeroBits = true } for nbOccurrences := int16(0); nbOccurrences < v; nbOccurrences++ { tableSymbol[position] = symbol position = (position + step) & tableMask for position > highThreshold { position = (position + step) & tableMask } /* Low proba area */ } } // Check if we have gone through all positions if position != 0 { return errors.New("position!=0") } } // Build table table := s.ct.stateTable { tsi := int(tableSize) for u, v := range tableSymbol { // TableU16 : sorted by symbol order; gives next state value table[cumul[v]] = uint16(tsi + u) cumul[v]++ } } // Build Symbol Transformation Table { total := int16(0) symbolTT := s.ct.symbolTT[:s.symbolLen] tableLog := s.actualTableLog tl := (uint32(tableLog) << 16) - (1 << tableLog) for i, v := range s.norm[:s.symbolLen] { switch v { case 0: case -1, 1: symbolTT[i].deltaNbBits = tl symbolTT[i].deltaFindState = total - 1 total++ default: maxBitsOut := uint32(tableLog) - highBit(uint32(v-1)) minStatePlus := uint32(v) << maxBitsOut symbolTT[i].deltaNbBits = (maxBitsOut << 16) - minStatePlus symbolTT[i].deltaFindState = total - v total += v } } if total != int16(tableSize) { return fmt.Errorf("total mismatch %d (got) != %d (want)", total, tableSize) } } return nil } var rtbTable = [...]uint32{0, 473195, 504333, 520860, 550000, 700000, 750000, 830000} func (s *fseEncoder) setRLE(val byte) { s.allocCtable() s.actualTableLog = 0 s.ct.stateTable = s.ct.stateTable[:1] s.ct.symbolTT[val] = symbolTransform{ deltaFindState: 0, deltaNbBits: 0, } if debugEncoder { println("setRLE: val", val, "symbolTT", s.ct.symbolTT[val]) } s.rleVal = val s.useRLE = true } // setBits will set output bits for the transform. // if nil is provided, the number of bits is equal to the index. func (s *fseEncoder) setBits(transform []byte) { if s.reUsed || s.preDefined { return } if s.useRLE { if transform == nil { s.ct.symbolTT[s.rleVal].outBits = s.rleVal s.maxBits = s.rleVal return } s.maxBits = transform[s.rleVal] s.ct.symbolTT[s.rleVal].outBits = s.maxBits return } if transform == nil { for i := range s.ct.symbolTT[:s.symbolLen] { s.ct.symbolTT[i].outBits = uint8(i) } s.maxBits = uint8(s.symbolLen - 1) return } s.maxBits = 0 for i, v := range transform[:s.symbolLen] { s.ct.symbolTT[i].outBits = v if v > s.maxBits { // We could assume bits always going up, but we play safe. s.maxBits = v } } } // normalizeCount will normalize the count of the symbols so // the total is equal to the table size. // If successful, compression tables will also be made ready. func (s *fseEncoder) normalizeCount(length int) error { if s.reUsed { return nil } s.optimalTableLog(length) var ( tableLog = s.actualTableLog scale = 62 - uint64(tableLog) step = (1 << 62) / uint64(length) vStep = uint64(1) << (scale - 20) stillToDistribute = int16(1 << tableLog) largest int largestP int16 lowThreshold = (uint32)(length >> tableLog) ) if s.maxCount == length { s.useRLE = true return nil } s.useRLE = false for i, cnt := range s.count[:s.symbolLen] { // already handled // if (count[s] == s.length) return 0; /* rle special case */ if cnt == 0 { s.norm[i] = 0 continue } if cnt <= lowThreshold { s.norm[i] = -1 stillToDistribute-- } else { proba := (int16)((uint64(cnt) * step) >> scale) if proba < 8 { restToBeat := vStep * uint64(rtbTable[proba]) v := uint64(cnt)*step - (uint64(proba) << scale) if v > restToBeat { proba++ } } if proba > largestP { largestP = proba largest = i } s.norm[i] = proba stillToDistribute -= proba } } if -stillToDistribute >= (s.norm[largest] >> 1) { // corner case, need another normalization method err := s.normalizeCount2(length) if err != nil { return err } if debugAsserts { err = s.validateNorm() if err != nil { return err } } return s.buildCTable() } s.norm[largest] += stillToDistribute if debugAsserts { err := s.validateNorm() if err != nil { return err } } return s.buildCTable() } // Secondary normalization method. // To be used when primary method fails. func (s *fseEncoder) normalizeCount2(length int) error { const notYetAssigned = -2 var ( distributed uint32 total = uint32(length) tableLog = s.actualTableLog lowThreshold = total >> tableLog lowOne = (total * 3) >> (tableLog + 1) ) for i, cnt := range s.count[:s.symbolLen] { if cnt == 0 { s.norm[i] = 0 continue } if cnt <= lowThreshold { s.norm[i] = -1 distributed++ total -= cnt continue } if cnt <= lowOne { s.norm[i] = 1 distributed++ total -= cnt continue } s.norm[i] = notYetAssigned } toDistribute := (1 << tableLog) - distributed if (total / toDistribute) > lowOne { // risk of rounding to zero lowOne = (total * 3) / (toDistribute * 2) for i, cnt := range s.count[:s.symbolLen] { if (s.norm[i] == notYetAssigned) && (cnt <= lowOne) { s.norm[i] = 1 distributed++ total -= cnt continue } } toDistribute = (1 << tableLog) - distributed } if distributed == uint32(s.symbolLen)+1 { // all values are pretty poor; // probably incompressible data (should have already been detected); // find max, then give all remaining points to max var maxV int var maxC uint32 for i, cnt := range s.count[:s.symbolLen] { if cnt > maxC { maxV = i maxC = cnt } } s.norm[maxV] += int16(toDistribute) return nil } if total == 0 { // all of the symbols were low enough for the lowOne or lowThreshold for i := uint32(0); toDistribute > 0; i = (i + 1) % (uint32(s.symbolLen)) { if s.norm[i] > 0 { toDistribute-- s.norm[i]++ } } return nil } var ( vStepLog = 62 - uint64(tableLog) mid = uint64((1 << (vStepLog - 1)) - 1) rStep = (((1 << vStepLog) * uint64(toDistribute)) + mid) / uint64(total) // scale on remaining tmpTotal = mid ) for i, cnt := range s.count[:s.symbolLen] { if s.norm[i] == notYetAssigned { var ( end = tmpTotal + uint64(cnt)*rStep sStart = uint32(tmpTotal >> vStepLog) sEnd = uint32(end >> vStepLog) weight = sEnd - sStart ) if weight < 1 { return errors.New("weight < 1") } s.norm[i] = int16(weight) tmpTotal = end } } return nil } // optimalTableLog calculates and sets the optimal tableLog in s.actualTableLog func (s *fseEncoder) optimalTableLog(length int) { tableLog := uint8(maxEncTableLog) minBitsSrc := highBit(uint32(length)) + 1 minBitsSymbols := highBit(uint32(s.symbolLen-1)) + 2 minBits := uint8(minBitsSymbols) if minBitsSrc < minBitsSymbols { minBits = uint8(minBitsSrc) } maxBitsSrc := uint8(highBit(uint32(length-1))) - 2 if maxBitsSrc < tableLog { // Accuracy can be reduced tableLog = maxBitsSrc } if minBits > tableLog { tableLog = minBits } // Need a minimum to safely represent all symbol values if tableLog < minEncTablelog { tableLog = minEncTablelog } if tableLog > maxEncTableLog { tableLog = maxEncTableLog } s.actualTableLog = tableLog } // validateNorm validates the normalized histogram table. func (s *fseEncoder) validateNorm() (err error) { var total int for _, v := range s.norm[:s.symbolLen] { if v >= 0 { total += int(v) } else { total -= int(v) } } defer func() { if err == nil { return } fmt.Printf("selected TableLog: %d, Symbol length: %d\n", s.actualTableLog, s.symbolLen) for i, v := range s.norm[:s.symbolLen] { fmt.Printf("%3d: %5d -> %4d \n", i, s.count[i], v) } }() if total != (1 << s.actualTableLog) { return fmt.Errorf("warning: Total == %d != %d", total, 1<> 3) + 3 + 2 // Write Table Size bitStream = uint32(tableLog - minEncTablelog) bitCount = uint(4) remaining = int16(tableSize + 1) /* +1 for extra accuracy */ threshold = int16(tableSize) nbBits = uint(tableLog + 1) outP = len(out) ) if cap(out) < outP+maxHeaderSize { out = append(out, make([]byte, maxHeaderSize*3)...) out = out[:len(out)-maxHeaderSize*3] } out = out[:outP+maxHeaderSize] // stops at 1 for remaining > 1 { if previous0 { start := charnum for s.norm[charnum] == 0 { charnum++ } for charnum >= start+24 { start += 24 bitStream += uint32(0xFFFF) << bitCount out[outP] = byte(bitStream) out[outP+1] = byte(bitStream >> 8) outP += 2 bitStream >>= 16 } for charnum >= start+3 { start += 3 bitStream += 3 << bitCount bitCount += 2 } bitStream += uint32(charnum-start) << bitCount bitCount += 2 if bitCount > 16 { out[outP] = byte(bitStream) out[outP+1] = byte(bitStream >> 8) outP += 2 bitStream >>= 16 bitCount -= 16 } } count := s.norm[charnum] charnum++ max := (2*threshold - 1) - remaining if count < 0 { remaining += count } else { remaining -= count } count++ // +1 for extra accuracy if count >= threshold { count += max // [0..max[ [max..threshold[ (...) [threshold+max 2*threshold[ } bitStream += uint32(count) << bitCount bitCount += nbBits if count < max { bitCount-- } previous0 = count == 1 if remaining < 1 { return nil, errors.New("internal error: remaining < 1") } for remaining < threshold { nbBits-- threshold >>= 1 } if bitCount > 16 { out[outP] = byte(bitStream) out[outP+1] = byte(bitStream >> 8) outP += 2 bitStream >>= 16 bitCount -= 16 } } if outP+2 > len(out) { return nil, fmt.Errorf("internal error: %d > %d, maxheader: %d, sl: %d, tl: %d, normcount: %v", outP+2, len(out), maxHeaderSize, s.symbolLen, int(tableLog), s.norm[:s.symbolLen]) } out[outP] = byte(bitStream) out[outP+1] = byte(bitStream >> 8) outP += int((bitCount + 7) / 8) if charnum > s.symbolLen { return nil, errors.New("internal error: charnum > s.symbolLen") } return out[:outP], nil } // Approximate symbol cost, as fractional value, using fixed-point format (accuracyLog fractional bits) // note 1 : assume symbolValue is valid (<= maxSymbolValue) // note 2 : if freq[symbolValue]==0, @return a fake cost of tableLog+1 bits * func (s *fseEncoder) bitCost(symbolValue uint8, accuracyLog uint32) uint32 { minNbBits := s.ct.symbolTT[symbolValue].deltaNbBits >> 16 threshold := (minNbBits + 1) << 16 if debugAsserts { if !(s.actualTableLog < 16) { panic("!s.actualTableLog < 16") } // ensure enough room for renormalization double shift if !(uint8(accuracyLog) < 31-s.actualTableLog) { panic("!uint8(accuracyLog) < 31-s.actualTableLog") } } tableSize := uint32(1) << s.actualTableLog deltaFromThreshold := threshold - (s.ct.symbolTT[symbolValue].deltaNbBits + tableSize) // linear interpolation (very approximate) normalizedDeltaFromThreshold := (deltaFromThreshold << accuracyLog) >> s.actualTableLog bitMultiplier := uint32(1) << accuracyLog if debugAsserts { if s.ct.symbolTT[symbolValue].deltaNbBits+tableSize > threshold { panic("s.ct.symbolTT[symbolValue].deltaNbBits+tableSize > threshold") } if normalizedDeltaFromThreshold > bitMultiplier { panic("normalizedDeltaFromThreshold > bitMultiplier") } } return (minNbBits+1)*bitMultiplier - normalizedDeltaFromThreshold } // Returns the cost in bits of encoding the distribution in count using ctable. // Histogram should only be up to the last non-zero symbol. // Returns an -1 if ctable cannot represent all the symbols in count. func (s *fseEncoder) approxSize(hist []uint32) uint32 { if int(s.symbolLen) < len(hist) { // More symbols than we have. return math.MaxUint32 } if s.useRLE { // We will never reuse RLE encoders. return math.MaxUint32 } const kAccuracyLog = 8 badCost := (uint32(s.actualTableLog) + 1) << kAccuracyLog var cost uint32 for i, v := range hist { if v == 0 { continue } if s.norm[i] == 0 { return math.MaxUint32 } bitCost := s.bitCost(uint8(i), kAccuracyLog) if bitCost > badCost { return math.MaxUint32 } cost += v * bitCost } return cost >> kAccuracyLog } // maxHeaderSize returns the maximum header size in bits. // This is not exact size, but we want a penalty for new tables anyway. func (s *fseEncoder) maxHeaderSize() uint32 { if s.preDefined { return 0 } if s.useRLE { return 8 } return (((uint32(s.symbolLen) * uint32(s.actualTableLog)) >> 3) + 3) * 8 } // cState contains the compression state of a stream. type cState struct { bw *bitWriter stateTable []uint16 state uint16 } // init will initialize the compression state to the first symbol of the stream. func (c *cState) init(bw *bitWriter, ct *cTable, first symbolTransform) { c.bw = bw c.stateTable = ct.stateTable if len(c.stateTable) == 1 { // RLE c.stateTable[0] = uint16(0) c.state = 0 return } nbBitsOut := (first.deltaNbBits + (1 << 15)) >> 16 im := int32((nbBitsOut << 16) - first.deltaNbBits) lu := (im >> nbBitsOut) + int32(first.deltaFindState) c.state = c.stateTable[lu] } // flush will write the tablelog to the output and flush the remaining full bytes. func (c *cState) flush(tableLog uint8) { c.bw.flush32() c.bw.addBits16NC(c.state, tableLog) } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/fse_predefined.go ================================================ // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. // Based on work by Yann Collet, released under BSD License. package zstd import ( "fmt" "math" "sync" ) var ( // fsePredef are the predefined fse tables as defined here: // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#default-distributions // These values are already transformed. fsePredef [3]fseDecoder // fsePredefEnc are the predefined encoder based on fse tables as defined here: // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#default-distributions // These values are already transformed. fsePredefEnc [3]fseEncoder // symbolTableX contain the transformations needed for each type as defined in // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#the-codes-for-literals-lengths-match-lengths-and-offsets symbolTableX [3][]baseOffset // maxTableSymbol is the biggest supported symbol for each table type // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#the-codes-for-literals-lengths-match-lengths-and-offsets maxTableSymbol = [3]uint8{tableLiteralLengths: maxLiteralLengthSymbol, tableOffsets: maxOffsetLengthSymbol, tableMatchLengths: maxMatchLengthSymbol} // bitTables is the bits table for each table. bitTables = [3][]byte{tableLiteralLengths: llBitsTable[:], tableOffsets: nil, tableMatchLengths: mlBitsTable[:]} ) type tableIndex uint8 const ( // indexes for fsePredef and symbolTableX tableLiteralLengths tableIndex = 0 tableOffsets tableIndex = 1 tableMatchLengths tableIndex = 2 maxLiteralLengthSymbol = 35 maxOffsetLengthSymbol = 30 maxMatchLengthSymbol = 52 ) // baseOffset is used for calculating transformations. type baseOffset struct { baseLine uint32 addBits uint8 } // fillBase will precalculate base offsets with the given bit distributions. func fillBase(dst []baseOffset, base uint32, bits ...uint8) { if len(bits) != len(dst) { panic(fmt.Sprintf("len(dst) (%d) != len(bits) (%d)", len(dst), len(bits))) } for i, bit := range bits { if base > math.MaxInt32 { panic("invalid decoding table, base overflows int32") } dst[i] = baseOffset{ baseLine: base, addBits: bit, } base += 1 << bit } } var predef sync.Once func initPredefined() { predef.Do(func() { // Literals length codes tmp := make([]baseOffset, 36) for i := range tmp[:16] { tmp[i] = baseOffset{ baseLine: uint32(i), addBits: 0, } } fillBase(tmp[16:], 16, 1, 1, 1, 1, 2, 2, 3, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) symbolTableX[tableLiteralLengths] = tmp // Match length codes tmp = make([]baseOffset, 53) for i := range tmp[:32] { tmp[i] = baseOffset{ // The transformation adds the 3 length. baseLine: uint32(i) + 3, addBits: 0, } } fillBase(tmp[32:], 35, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) symbolTableX[tableMatchLengths] = tmp // Offset codes tmp = make([]baseOffset, maxOffsetBits+1) tmp[1] = baseOffset{ baseLine: 1, addBits: 1, } fillBase(tmp[2:], 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30) symbolTableX[tableOffsets] = tmp // Fill predefined tables and transform them. // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#default-distributions for i := range fsePredef[:] { f := &fsePredef[i] switch tableIndex(i) { case tableLiteralLengths: // https://github.com/facebook/zstd/blob/ededcfca57366461021c922720878c81a5854a0a/lib/decompress/zstd_decompress_block.c#L243 f.actualTableLog = 6 copy(f.norm[:], []int16{4, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 1, 1, 1, 1, 1, -1, -1, -1, -1}) f.symbolLen = 36 case tableOffsets: // https://github.com/facebook/zstd/blob/ededcfca57366461021c922720878c81a5854a0a/lib/decompress/zstd_decompress_block.c#L281 f.actualTableLog = 5 copy(f.norm[:], []int16{ 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1}) f.symbolLen = 29 case tableMatchLengths: //https://github.com/facebook/zstd/blob/ededcfca57366461021c922720878c81a5854a0a/lib/decompress/zstd_decompress_block.c#L304 f.actualTableLog = 6 copy(f.norm[:], []int16{ 1, 4, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1}) f.symbolLen = 53 } if err := f.buildDtable(); err != nil { panic(fmt.Errorf("building table %v: %v", tableIndex(i), err)) } if err := f.transform(symbolTableX[i]); err != nil { panic(fmt.Errorf("building table %v: %v", tableIndex(i), err)) } f.preDefined = true // Create encoder as well enc := &fsePredefEnc[i] copy(enc.norm[:], f.norm[:]) enc.symbolLen = f.symbolLen enc.actualTableLog = f.actualTableLog if err := enc.buildCTable(); err != nil { panic(fmt.Errorf("building encoding table %v: %v", tableIndex(i), err)) } enc.setBits(bitTables[i]) enc.preDefined = true } }) } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/hash.go ================================================ // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. // Based on work by Yann Collet, released under BSD License. package zstd const ( prime3bytes = 506832829 prime4bytes = 2654435761 prime5bytes = 889523592379 prime6bytes = 227718039650203 prime7bytes = 58295818150454627 prime8bytes = 0xcf1bbcdcb7a56463 ) // hashLen returns a hash of the lowest mls bytes of with length output bits. // mls must be >=3 and <=8. Any other value will return hash for 4 bytes. // length should always be < 32. // Preferably length and mls should be a constant for inlining. func hashLen(u uint64, length, mls uint8) uint32 { switch mls { case 3: return (uint32(u<<8) * prime3bytes) >> (32 - length) case 5: return uint32(((u << (64 - 40)) * prime5bytes) >> (64 - length)) case 6: return uint32(((u << (64 - 48)) * prime6bytes) >> (64 - length)) case 7: return uint32(((u << (64 - 56)) * prime7bytes) >> (64 - length)) case 8: return uint32((u * prime8bytes) >> (64 - length)) default: return (uint32(u) * prime4bytes) >> (32 - length) } } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/history.go ================================================ // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. // Based on work by Yann Collet, released under BSD License. package zstd import ( "github.com/klauspost/compress/huff0" ) // history contains the information transferred between blocks. type history struct { // Literal decompression huffTree *huff0.Scratch // Sequence decompression decoders sequenceDecs recentOffsets [3]int // History buffer... b []byte // ignoreBuffer is meant to ignore a number of bytes // when checking for matches in history ignoreBuffer int windowSize int allocFrameBuffer int // needed? error bool dict *dict } // reset will reset the history to initial state of a frame. // The history must already have been initialized to the desired size. func (h *history) reset() { h.b = h.b[:0] h.ignoreBuffer = 0 h.error = false h.recentOffsets = [3]int{1, 4, 8} h.decoders.freeDecoders() h.decoders = sequenceDecs{br: h.decoders.br} h.freeHuffDecoder() h.huffTree = nil h.dict = nil //printf("history created: %+v (l: %d, c: %d)", *h, len(h.b), cap(h.b)) } func (h *history) freeHuffDecoder() { if h.huffTree != nil { if h.dict == nil || h.dict.litEnc != h.huffTree { huffDecoderPool.Put(h.huffTree) h.huffTree = nil } } } func (h *history) setDict(dict *dict) { if dict == nil { return } h.dict = dict h.decoders.litLengths = dict.llDec h.decoders.offsets = dict.ofDec h.decoders.matchLengths = dict.mlDec h.decoders.dict = dict.content h.recentOffsets = dict.offsets h.huffTree = dict.litEnc } // append bytes to history. // This function will make sure there is space for it, // if the buffer has been allocated with enough extra space. func (h *history) append(b []byte) { if len(b) >= h.windowSize { // Discard all history by simply overwriting h.b = h.b[:h.windowSize] copy(h.b, b[len(b)-h.windowSize:]) return } // If there is space, append it. if len(b) < cap(h.b)-len(h.b) { h.b = append(h.b, b...) return } // Move data down so we only have window size left. // We know we have less than window size in b at this point. discard := len(b) + len(h.b) - h.windowSize copy(h.b, h.b[discard:]) h.b = h.b[:h.windowSize] copy(h.b[h.windowSize-len(b):], b) } // ensureBlock will ensure there is space for at least one block... func (h *history) ensureBlock() { if cap(h.b) < h.allocFrameBuffer { h.b = make([]byte, 0, h.allocFrameBuffer) return } avail := cap(h.b) - len(h.b) if avail >= h.windowSize || avail > maxCompressedBlockSize { return } // Move data down so we only have window size left. // We know we have less than window size in b at this point. discard := len(h.b) - h.windowSize copy(h.b, h.b[discard:]) h.b = h.b[:h.windowSize] } // append bytes to history without ever discarding anything. func (h *history) appendKeep(b []byte) { h.b = append(h.b, b...) } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/internal/xxhash/LICENSE.txt ================================================ Copyright (c) 2016 Caleb Spare MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/klauspost/compress/zstd/internal/xxhash/README.md ================================================ # xxhash VENDORED: Go to [github.com/cespare/xxhash](https://github.com/cespare/xxhash) for original package. xxhash is a Go implementation of the 64-bit [xxHash] algorithm, XXH64. This is a high-quality hashing algorithm that is much faster than anything in the Go standard library. This package provides a straightforward API: ``` func Sum64(b []byte) uint64 func Sum64String(s string) uint64 type Digest struct{ ... } func New() *Digest ``` The `Digest` type implements hash.Hash64. Its key methods are: ``` func (*Digest) Write([]byte) (int, error) func (*Digest) WriteString(string) (int, error) func (*Digest) Sum64() uint64 ``` The package is written with optimized pure Go and also contains even faster assembly implementations for amd64 and arm64. If desired, the `purego` build tag opts into using the Go code even on those architectures. [xxHash]: http://cyan4973.github.io/xxHash/ ## Compatibility This package is in a module and the latest code is in version 2 of the module. You need a version of Go with at least "minimal module compatibility" to use github.com/cespare/xxhash/v2: * 1.9.7+ for Go 1.9 * 1.10.3+ for Go 1.10 * Go 1.11 or later I recommend using the latest release of Go. ## Benchmarks Here are some quick benchmarks comparing the pure-Go and assembly implementations of Sum64. | input size | purego | asm | | ---------- | --------- | --------- | | 4 B | 1.3 GB/s | 1.2 GB/s | | 16 B | 2.9 GB/s | 3.5 GB/s | | 100 B | 6.9 GB/s | 8.1 GB/s | | 4 KB | 11.7 GB/s | 16.7 GB/s | | 10 MB | 12.0 GB/s | 17.3 GB/s | These numbers were generated on Ubuntu 20.04 with an Intel Xeon Platinum 8252C CPU using the following commands under Go 1.19.2: ``` benchstat <(go test -tags purego -benchtime 500ms -count 15 -bench 'Sum64$') benchstat <(go test -benchtime 500ms -count 15 -bench 'Sum64$') ``` ## Projects using this package - [InfluxDB](https://github.com/influxdata/influxdb) - [Prometheus](https://github.com/prometheus/prometheus) - [VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics) - [FreeCache](https://github.com/coocood/freecache) - [FastCache](https://github.com/VictoriaMetrics/fastcache) ================================================ FILE: vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash.go ================================================ // Package xxhash implements the 64-bit variant of xxHash (XXH64) as described // at http://cyan4973.github.io/xxHash/. // THIS IS VENDORED: Go to github.com/cespare/xxhash for original package. package xxhash import ( "encoding/binary" "errors" "math/bits" ) const ( prime1 uint64 = 11400714785074694791 prime2 uint64 = 14029467366897019727 prime3 uint64 = 1609587929392839161 prime4 uint64 = 9650029242287828579 prime5 uint64 = 2870177450012600261 ) // Store the primes in an array as well. // // The consts are used when possible in Go code to avoid MOVs but we need a // contiguous array of the assembly code. var primes = [...]uint64{prime1, prime2, prime3, prime4, prime5} // Digest implements hash.Hash64. type Digest struct { v1 uint64 v2 uint64 v3 uint64 v4 uint64 total uint64 mem [32]byte n int // how much of mem is used } // New creates a new Digest that computes the 64-bit xxHash algorithm. func New() *Digest { var d Digest d.Reset() return &d } // Reset clears the Digest's state so that it can be reused. func (d *Digest) Reset() { d.v1 = primes[0] + prime2 d.v2 = prime2 d.v3 = 0 d.v4 = -primes[0] d.total = 0 d.n = 0 } // Size always returns 8 bytes. func (d *Digest) Size() int { return 8 } // BlockSize always returns 32 bytes. func (d *Digest) BlockSize() int { return 32 } // Write adds more data to d. It always returns len(b), nil. func (d *Digest) Write(b []byte) (n int, err error) { n = len(b) d.total += uint64(n) memleft := d.mem[d.n&(len(d.mem)-1):] if d.n+n < 32 { // This new data doesn't even fill the current block. copy(memleft, b) d.n += n return } if d.n > 0 { // Finish off the partial block. c := copy(memleft, b) d.v1 = round(d.v1, u64(d.mem[0:8])) d.v2 = round(d.v2, u64(d.mem[8:16])) d.v3 = round(d.v3, u64(d.mem[16:24])) d.v4 = round(d.v4, u64(d.mem[24:32])) b = b[c:] d.n = 0 } if len(b) >= 32 { // One or more full blocks left. nw := writeBlocks(d, b) b = b[nw:] } // Store any remaining partial block. copy(d.mem[:], b) d.n = len(b) return } // Sum appends the current hash to b and returns the resulting slice. func (d *Digest) Sum(b []byte) []byte { s := d.Sum64() return append( b, byte(s>>56), byte(s>>48), byte(s>>40), byte(s>>32), byte(s>>24), byte(s>>16), byte(s>>8), byte(s), ) } // Sum64 returns the current hash. func (d *Digest) Sum64() uint64 { var h uint64 if d.total >= 32 { v1, v2, v3, v4 := d.v1, d.v2, d.v3, d.v4 h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4) h = mergeRound(h, v1) h = mergeRound(h, v2) h = mergeRound(h, v3) h = mergeRound(h, v4) } else { h = d.v3 + prime5 } h += d.total b := d.mem[:d.n&(len(d.mem)-1)] for ; len(b) >= 8; b = b[8:] { k1 := round(0, u64(b[:8])) h ^= k1 h = rol27(h)*prime1 + prime4 } if len(b) >= 4 { h ^= uint64(u32(b[:4])) * prime1 h = rol23(h)*prime2 + prime3 b = b[4:] } for ; len(b) > 0; b = b[1:] { h ^= uint64(b[0]) * prime5 h = rol11(h) * prime1 } h ^= h >> 33 h *= prime2 h ^= h >> 29 h *= prime3 h ^= h >> 32 return h } const ( magic = "xxh\x06" marshaledSize = len(magic) + 8*5 + 32 ) // MarshalBinary implements the encoding.BinaryMarshaler interface. func (d *Digest) MarshalBinary() ([]byte, error) { b := make([]byte, 0, marshaledSize) b = append(b, magic...) b = appendUint64(b, d.v1) b = appendUint64(b, d.v2) b = appendUint64(b, d.v3) b = appendUint64(b, d.v4) b = appendUint64(b, d.total) b = append(b, d.mem[:d.n]...) b = b[:len(b)+len(d.mem)-d.n] return b, nil } // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. func (d *Digest) UnmarshalBinary(b []byte) error { if len(b) < len(magic) || string(b[:len(magic)]) != magic { return errors.New("xxhash: invalid hash state identifier") } if len(b) != marshaledSize { return errors.New("xxhash: invalid hash state size") } b = b[len(magic):] b, d.v1 = consumeUint64(b) b, d.v2 = consumeUint64(b) b, d.v3 = consumeUint64(b) b, d.v4 = consumeUint64(b) b, d.total = consumeUint64(b) copy(d.mem[:], b) d.n = int(d.total % uint64(len(d.mem))) return nil } func appendUint64(b []byte, x uint64) []byte { var a [8]byte binary.LittleEndian.PutUint64(a[:], x) return append(b, a[:]...) } func consumeUint64(b []byte) ([]byte, uint64) { x := u64(b) return b[8:], x } func u64(b []byte) uint64 { return binary.LittleEndian.Uint64(b) } func u32(b []byte) uint32 { return binary.LittleEndian.Uint32(b) } func round(acc, input uint64) uint64 { acc += input * prime2 acc = rol31(acc) acc *= prime1 return acc } func mergeRound(acc, val uint64) uint64 { val = round(0, val) acc ^= val acc = acc*prime1 + prime4 return acc } func rol1(x uint64) uint64 { return bits.RotateLeft64(x, 1) } func rol7(x uint64) uint64 { return bits.RotateLeft64(x, 7) } func rol11(x uint64) uint64 { return bits.RotateLeft64(x, 11) } func rol12(x uint64) uint64 { return bits.RotateLeft64(x, 12) } func rol18(x uint64) uint64 { return bits.RotateLeft64(x, 18) } func rol23(x uint64) uint64 { return bits.RotateLeft64(x, 23) } func rol27(x uint64) uint64 { return bits.RotateLeft64(x, 27) } func rol31(x uint64) uint64 { return bits.RotateLeft64(x, 31) } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_amd64.s ================================================ //go:build !appengine && gc && !purego && !noasm // +build !appengine // +build gc // +build !purego // +build !noasm #include "textflag.h" // Registers: #define h AX #define d AX #define p SI // pointer to advance through b #define n DX #define end BX // loop end #define v1 R8 #define v2 R9 #define v3 R10 #define v4 R11 #define x R12 #define prime1 R13 #define prime2 R14 #define prime4 DI #define round(acc, x) \ IMULQ prime2, x \ ADDQ x, acc \ ROLQ $31, acc \ IMULQ prime1, acc // round0 performs the operation x = round(0, x). #define round0(x) \ IMULQ prime2, x \ ROLQ $31, x \ IMULQ prime1, x // mergeRound applies a merge round on the two registers acc and x. // It assumes that prime1, prime2, and prime4 have been loaded. #define mergeRound(acc, x) \ round0(x) \ XORQ x, acc \ IMULQ prime1, acc \ ADDQ prime4, acc // blockLoop processes as many 32-byte blocks as possible, // updating v1, v2, v3, and v4. It assumes that there is at least one block // to process. #define blockLoop() \ loop: \ MOVQ +0(p), x \ round(v1, x) \ MOVQ +8(p), x \ round(v2, x) \ MOVQ +16(p), x \ round(v3, x) \ MOVQ +24(p), x \ round(v4, x) \ ADDQ $32, p \ CMPQ p, end \ JLE loop // func Sum64(b []byte) uint64 TEXT ·Sum64(SB), NOSPLIT|NOFRAME, $0-32 // Load fixed primes. MOVQ ·primes+0(SB), prime1 MOVQ ·primes+8(SB), prime2 MOVQ ·primes+24(SB), prime4 // Load slice. MOVQ b_base+0(FP), p MOVQ b_len+8(FP), n LEAQ (p)(n*1), end // The first loop limit will be len(b)-32. SUBQ $32, end // Check whether we have at least one block. CMPQ n, $32 JLT noBlocks // Set up initial state (v1, v2, v3, v4). MOVQ prime1, v1 ADDQ prime2, v1 MOVQ prime2, v2 XORQ v3, v3 XORQ v4, v4 SUBQ prime1, v4 blockLoop() MOVQ v1, h ROLQ $1, h MOVQ v2, x ROLQ $7, x ADDQ x, h MOVQ v3, x ROLQ $12, x ADDQ x, h MOVQ v4, x ROLQ $18, x ADDQ x, h mergeRound(h, v1) mergeRound(h, v2) mergeRound(h, v3) mergeRound(h, v4) JMP afterBlocks noBlocks: MOVQ ·primes+32(SB), h afterBlocks: ADDQ n, h ADDQ $24, end CMPQ p, end JG try4 loop8: MOVQ (p), x ADDQ $8, p round0(x) XORQ x, h ROLQ $27, h IMULQ prime1, h ADDQ prime4, h CMPQ p, end JLE loop8 try4: ADDQ $4, end CMPQ p, end JG try1 MOVL (p), x ADDQ $4, p IMULQ prime1, x XORQ x, h ROLQ $23, h IMULQ prime2, h ADDQ ·primes+16(SB), h try1: ADDQ $4, end CMPQ p, end JGE finalize loop1: MOVBQZX (p), x ADDQ $1, p IMULQ ·primes+32(SB), x XORQ x, h ROLQ $11, h IMULQ prime1, h CMPQ p, end JL loop1 finalize: MOVQ h, x SHRQ $33, x XORQ x, h IMULQ prime2, h MOVQ h, x SHRQ $29, x XORQ x, h IMULQ ·primes+16(SB), h MOVQ h, x SHRQ $32, x XORQ x, h MOVQ h, ret+24(FP) RET // func writeBlocks(d *Digest, b []byte) int TEXT ·writeBlocks(SB), NOSPLIT|NOFRAME, $0-40 // Load fixed primes needed for round. MOVQ ·primes+0(SB), prime1 MOVQ ·primes+8(SB), prime2 // Load slice. MOVQ b_base+8(FP), p MOVQ b_len+16(FP), n LEAQ (p)(n*1), end SUBQ $32, end // Load vN from d. MOVQ s+0(FP), d MOVQ 0(d), v1 MOVQ 8(d), v2 MOVQ 16(d), v3 MOVQ 24(d), v4 // We don't need to check the loop condition here; this function is // always called with at least one block of data to process. blockLoop() // Copy vN back to d. MOVQ v1, 0(d) MOVQ v2, 8(d) MOVQ v3, 16(d) MOVQ v4, 24(d) // The number of bytes written is p minus the old base pointer. SUBQ b_base+8(FP), p MOVQ p, ret+32(FP) RET ================================================ FILE: vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_arm64.s ================================================ //go:build !appengine && gc && !purego && !noasm // +build !appengine // +build gc // +build !purego // +build !noasm #include "textflag.h" // Registers: #define digest R1 #define h R2 // return value #define p R3 // input pointer #define n R4 // input length #define nblocks R5 // n / 32 #define prime1 R7 #define prime2 R8 #define prime3 R9 #define prime4 R10 #define prime5 R11 #define v1 R12 #define v2 R13 #define v3 R14 #define v4 R15 #define x1 R20 #define x2 R21 #define x3 R22 #define x4 R23 #define round(acc, x) \ MADD prime2, acc, x, acc \ ROR $64-31, acc \ MUL prime1, acc // round0 performs the operation x = round(0, x). #define round0(x) \ MUL prime2, x \ ROR $64-31, x \ MUL prime1, x #define mergeRound(acc, x) \ round0(x) \ EOR x, acc \ MADD acc, prime4, prime1, acc // blockLoop processes as many 32-byte blocks as possible, // updating v1, v2, v3, and v4. It assumes that n >= 32. #define blockLoop() \ LSR $5, n, nblocks \ PCALIGN $16 \ loop: \ LDP.P 16(p), (x1, x2) \ LDP.P 16(p), (x3, x4) \ round(v1, x1) \ round(v2, x2) \ round(v3, x3) \ round(v4, x4) \ SUB $1, nblocks \ CBNZ nblocks, loop // func Sum64(b []byte) uint64 TEXT ·Sum64(SB), NOSPLIT|NOFRAME, $0-32 LDP b_base+0(FP), (p, n) LDP ·primes+0(SB), (prime1, prime2) LDP ·primes+16(SB), (prime3, prime4) MOVD ·primes+32(SB), prime5 CMP $32, n CSEL LT, prime5, ZR, h // if n < 32 { h = prime5 } else { h = 0 } BLT afterLoop ADD prime1, prime2, v1 MOVD prime2, v2 MOVD $0, v3 NEG prime1, v4 blockLoop() ROR $64-1, v1, x1 ROR $64-7, v2, x2 ADD x1, x2 ROR $64-12, v3, x3 ROR $64-18, v4, x4 ADD x3, x4 ADD x2, x4, h mergeRound(h, v1) mergeRound(h, v2) mergeRound(h, v3) mergeRound(h, v4) afterLoop: ADD n, h TBZ $4, n, try8 LDP.P 16(p), (x1, x2) round0(x1) // NOTE: here and below, sequencing the EOR after the ROR (using a // rotated register) is worth a small but measurable speedup for small // inputs. ROR $64-27, h EOR x1 @> 64-27, h, h MADD h, prime4, prime1, h round0(x2) ROR $64-27, h EOR x2 @> 64-27, h, h MADD h, prime4, prime1, h try8: TBZ $3, n, try4 MOVD.P 8(p), x1 round0(x1) ROR $64-27, h EOR x1 @> 64-27, h, h MADD h, prime4, prime1, h try4: TBZ $2, n, try2 MOVWU.P 4(p), x2 MUL prime1, x2 ROR $64-23, h EOR x2 @> 64-23, h, h MADD h, prime3, prime2, h try2: TBZ $1, n, try1 MOVHU.P 2(p), x3 AND $255, x3, x1 LSR $8, x3, x2 MUL prime5, x1 ROR $64-11, h EOR x1 @> 64-11, h, h MUL prime1, h MUL prime5, x2 ROR $64-11, h EOR x2 @> 64-11, h, h MUL prime1, h try1: TBZ $0, n, finalize MOVBU (p), x4 MUL prime5, x4 ROR $64-11, h EOR x4 @> 64-11, h, h MUL prime1, h finalize: EOR h >> 33, h MUL prime2, h EOR h >> 29, h MUL prime3, h EOR h >> 32, h MOVD h, ret+24(FP) RET // func writeBlocks(d *Digest, b []byte) int TEXT ·writeBlocks(SB), NOSPLIT|NOFRAME, $0-40 LDP ·primes+0(SB), (prime1, prime2) // Load state. Assume v[1-4] are stored contiguously. MOVD d+0(FP), digest LDP 0(digest), (v1, v2) LDP 16(digest), (v3, v4) LDP b_base+8(FP), (p, n) blockLoop() // Store updated state. STP (v1, v2), 0(digest) STP (v3, v4), 16(digest) BIC $31, n MOVD n, ret+32(FP) RET ================================================ FILE: vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_asm.go ================================================ //go:build (amd64 || arm64) && !appengine && gc && !purego && !noasm // +build amd64 arm64 // +build !appengine // +build gc // +build !purego // +build !noasm package xxhash // Sum64 computes the 64-bit xxHash digest of b. // //go:noescape func Sum64(b []byte) uint64 //go:noescape func writeBlocks(s *Digest, b []byte) int ================================================ FILE: vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_other.go ================================================ //go:build (!amd64 && !arm64) || appengine || !gc || purego || noasm // +build !amd64,!arm64 appengine !gc purego noasm package xxhash // Sum64 computes the 64-bit xxHash digest of b. func Sum64(b []byte) uint64 { // A simpler version would be // d := New() // d.Write(b) // return d.Sum64() // but this is faster, particularly for small inputs. n := len(b) var h uint64 if n >= 32 { v1 := primes[0] + prime2 v2 := prime2 v3 := uint64(0) v4 := -primes[0] for len(b) >= 32 { v1 = round(v1, u64(b[0:8:len(b)])) v2 = round(v2, u64(b[8:16:len(b)])) v3 = round(v3, u64(b[16:24:len(b)])) v4 = round(v4, u64(b[24:32:len(b)])) b = b[32:len(b):len(b)] } h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4) h = mergeRound(h, v1) h = mergeRound(h, v2) h = mergeRound(h, v3) h = mergeRound(h, v4) } else { h = prime5 } h += uint64(n) for ; len(b) >= 8; b = b[8:] { k1 := round(0, u64(b[:8])) h ^= k1 h = rol27(h)*prime1 + prime4 } if len(b) >= 4 { h ^= uint64(u32(b[:4])) * prime1 h = rol23(h)*prime2 + prime3 b = b[4:] } for ; len(b) > 0; b = b[1:] { h ^= uint64(b[0]) * prime5 h = rol11(h) * prime1 } h ^= h >> 33 h *= prime2 h ^= h >> 29 h *= prime3 h ^= h >> 32 return h } func writeBlocks(d *Digest, b []byte) int { v1, v2, v3, v4 := d.v1, d.v2, d.v3, d.v4 n := len(b) for len(b) >= 32 { v1 = round(v1, u64(b[0:8:len(b)])) v2 = round(v2, u64(b[8:16:len(b)])) v3 = round(v3, u64(b[16:24:len(b)])) v4 = round(v4, u64(b[24:32:len(b)])) b = b[32:len(b):len(b)] } d.v1, d.v2, d.v3, d.v4 = v1, v2, v3, v4 return n - len(b) } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/internal/xxhash/xxhash_safe.go ================================================ package xxhash // Sum64String computes the 64-bit xxHash digest of s. func Sum64String(s string) uint64 { return Sum64([]byte(s)) } // WriteString adds more data to d. It always returns len(s), nil. func (d *Digest) WriteString(s string) (n int, err error) { return d.Write([]byte(s)) } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/matchlen_amd64.go ================================================ //go:build amd64 && !appengine && !noasm && gc // +build amd64,!appengine,!noasm,gc // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. package zstd // matchLen returns how many bytes match in a and b // // It assumes that: // // len(a) <= len(b) and len(a) > 0 // //go:noescape func matchLen(a []byte, b []byte) int ================================================ FILE: vendor/github.com/klauspost/compress/zstd/matchlen_amd64.s ================================================ // Copied from S2 implementation. //go:build !appengine && !noasm && gc && !noasm #include "textflag.h" // func matchLen(a []byte, b []byte) int // Requires: BMI TEXT ·matchLen(SB), NOSPLIT, $0-56 MOVQ a_base+0(FP), AX MOVQ b_base+24(FP), CX MOVQ a_len+8(FP), DX // matchLen XORL SI, SI CMPL DX, $0x08 JB matchlen_match4_standalone matchlen_loopback_standalone: MOVQ (AX)(SI*1), BX XORQ (CX)(SI*1), BX TESTQ BX, BX JZ matchlen_loop_standalone #ifdef GOAMD64_v3 TZCNTQ BX, BX #else BSFQ BX, BX #endif SARQ $0x03, BX LEAL (SI)(BX*1), SI JMP gen_match_len_end matchlen_loop_standalone: LEAL -8(DX), DX LEAL 8(SI), SI CMPL DX, $0x08 JAE matchlen_loopback_standalone matchlen_match4_standalone: CMPL DX, $0x04 JB matchlen_match2_standalone MOVL (AX)(SI*1), BX CMPL (CX)(SI*1), BX JNE matchlen_match2_standalone LEAL -4(DX), DX LEAL 4(SI), SI matchlen_match2_standalone: CMPL DX, $0x02 JB matchlen_match1_standalone MOVW (AX)(SI*1), BX CMPW (CX)(SI*1), BX JNE matchlen_match1_standalone LEAL -2(DX), DX LEAL 2(SI), SI matchlen_match1_standalone: CMPL DX, $0x01 JB gen_match_len_end MOVB (AX)(SI*1), BL CMPB (CX)(SI*1), BL JNE gen_match_len_end INCL SI gen_match_len_end: MOVQ SI, ret+48(FP) RET ================================================ FILE: vendor/github.com/klauspost/compress/zstd/matchlen_generic.go ================================================ //go:build !amd64 || appengine || !gc || noasm // +build !amd64 appengine !gc noasm // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. package zstd import ( "encoding/binary" "math/bits" ) // matchLen returns the maximum common prefix length of a and b. // a must be the shortest of the two. func matchLen(a, b []byte) (n int) { for ; len(a) >= 8 && len(b) >= 8; a, b = a[8:], b[8:] { diff := binary.LittleEndian.Uint64(a) ^ binary.LittleEndian.Uint64(b) if diff != 0 { return n + bits.TrailingZeros64(diff)>>3 } n += 8 } for i := range a { if a[i] != b[i] { break } n++ } return n } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/seqdec.go ================================================ // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. // Based on work by Yann Collet, released under BSD License. package zstd import ( "errors" "fmt" "io" ) type seq struct { litLen uint32 matchLen uint32 offset uint32 // Codes are stored here for the encoder // so they only have to be looked up once. llCode, mlCode, ofCode uint8 } type seqVals struct { ll, ml, mo int } func (s seq) String() string { if s.offset <= 3 { if s.offset == 0 { return fmt.Sprint("litLen:", s.litLen, ", matchLen:", s.matchLen+zstdMinMatch, ", offset: INVALID (0)") } return fmt.Sprint("litLen:", s.litLen, ", matchLen:", s.matchLen+zstdMinMatch, ", offset:", s.offset, " (repeat)") } return fmt.Sprint("litLen:", s.litLen, ", matchLen:", s.matchLen+zstdMinMatch, ", offset:", s.offset-3, " (new)") } type seqCompMode uint8 const ( compModePredefined seqCompMode = iota compModeRLE compModeFSE compModeRepeat ) type sequenceDec struct { // decoder keeps track of the current state and updates it from the bitstream. fse *fseDecoder state fseState repeat bool } // init the state of the decoder with input from stream. func (s *sequenceDec) init(br *bitReader) error { if s.fse == nil { return errors.New("sequence decoder not defined") } s.state.init(br, s.fse.actualTableLog, s.fse.dt[:1< cap(s.out) { addBytes := s.seqSize + len(s.out) s.out = append(s.out, make([]byte, addBytes)...) s.out = s.out[:len(s.out)-addBytes] } if debugDecoder { printf("Execute %d seqs with hist %d, dict %d, literals: %d into %d bytes\n", len(seqs), len(hist), len(s.dict), len(s.literals), s.seqSize) } var t = len(s.out) out := s.out[:t+s.seqSize] for _, seq := range seqs { // Add literals copy(out[t:], s.literals[:seq.ll]) t += seq.ll s.literals = s.literals[seq.ll:] // Copy from dictionary... if seq.mo > t+len(hist) || seq.mo > s.windowSize { if len(s.dict) == 0 { return fmt.Errorf("match offset (%d) bigger than current history (%d)", seq.mo, t+len(hist)) } // we may be in dictionary. dictO := len(s.dict) - (seq.mo - (t + len(hist))) if dictO < 0 || dictO >= len(s.dict) { return fmt.Errorf("match offset (%d) bigger than current history+dict (%d)", seq.mo, t+len(hist)+len(s.dict)) } end := dictO + seq.ml if end > len(s.dict) { n := len(s.dict) - dictO copy(out[t:], s.dict[dictO:]) t += n seq.ml -= n } else { copy(out[t:], s.dict[dictO:end]) t += end - dictO continue } } // Copy from history. if v := seq.mo - t; v > 0 { // v is the start position in history from end. start := len(hist) - v if seq.ml > v { // Some goes into current block. // Copy remainder of history copy(out[t:], hist[start:]) t += v seq.ml -= v } else { copy(out[t:], hist[start:start+seq.ml]) t += seq.ml continue } } // We must be in current buffer now if seq.ml > 0 { start := t - seq.mo if seq.ml <= t-start { // No overlap copy(out[t:], out[start:start+seq.ml]) t += seq.ml continue } else { // Overlapping copy // Extend destination slice and copy one byte at the time. src := out[start : start+seq.ml] dst := out[t:] dst = dst[:len(src)] t += len(src) // Destination is the space we just added. for i := range src { dst[i] = src[i] } } } } // Add final literals copy(out[t:], s.literals) if debugDecoder { t += len(s.literals) if t != len(out) { panic(fmt.Errorf("length mismatch, want %d, got %d, ss: %d", len(out), t, s.seqSize)) } } s.out = out return nil } // decode sequences from the stream with the provided history. func (s *sequenceDecs) decodeSync(hist []byte) error { supported, err := s.decodeSyncSimple(hist) if supported { return err } br := s.br seqs := s.nSeqs startSize := len(s.out) // Grab full sizes tables, to avoid bounds checks. llTable, mlTable, ofTable := s.litLengths.fse.dt[:maxTablesize], s.matchLengths.fse.dt[:maxTablesize], s.offsets.fse.dt[:maxTablesize] llState, mlState, ofState := s.litLengths.state.state, s.matchLengths.state.state, s.offsets.state.state out := s.out maxBlockSize := maxCompressedBlockSize if s.windowSize < maxBlockSize { maxBlockSize = s.windowSize } if debugDecoder { println("decodeSync: decoding", seqs, "sequences", br.remain(), "bits remain on stream") } for i := seqs - 1; i >= 0; i-- { if br.overread() { printf("reading sequence %d, exceeded available data. Overread by %d\n", seqs-i, -br.remain()) return io.ErrUnexpectedEOF } var ll, mo, ml int if len(br.in) > 4+((maxOffsetBits+16+16)>>3) { // inlined function: // ll, mo, ml = s.nextFast(br, llState, mlState, ofState) // Final will not read from stream. var llB, mlB, moB uint8 ll, llB = llState.final() ml, mlB = mlState.final() mo, moB = ofState.final() // extra bits are stored in reverse order. br.fillFast() mo += br.getBits(moB) if s.maxBits > 32 { br.fillFast() } ml += br.getBits(mlB) ll += br.getBits(llB) if moB > 1 { s.prevOffset[2] = s.prevOffset[1] s.prevOffset[1] = s.prevOffset[0] s.prevOffset[0] = mo } else { // mo = s.adjustOffset(mo, ll, moB) // Inlined for rather big speedup if ll == 0 { // There is an exception though, when current sequence's literals_length = 0. // In this case, repeated offsets are shifted by one, so an offset_value of 1 means Repeated_Offset2, // an offset_value of 2 means Repeated_Offset3, and an offset_value of 3 means Repeated_Offset1 - 1_byte. mo++ } if mo == 0 { mo = s.prevOffset[0] } else { var temp int if mo == 3 { temp = s.prevOffset[0] - 1 } else { temp = s.prevOffset[mo] } if temp == 0 { // 0 is not valid; input is corrupted; force offset to 1 println("WARNING: temp was 0") temp = 1 } if mo != 1 { s.prevOffset[2] = s.prevOffset[1] } s.prevOffset[1] = s.prevOffset[0] s.prevOffset[0] = temp mo = temp } } br.fillFast() } else { ll, mo, ml = s.next(br, llState, mlState, ofState) br.fill() } if debugSequences { println("Seq", seqs-i-1, "Litlen:", ll, "mo:", mo, "(abs) ml:", ml) } if ll > len(s.literals) { return fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", ll, len(s.literals)) } size := ll + ml + len(out) if size-startSize > maxBlockSize { return fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) } if size > cap(out) { // Not enough size, which can happen under high volume block streaming conditions // but could be if destination slice is too small for sync operations. // over-allocating here can create a large amount of GC pressure so we try to keep // it as contained as possible used := len(out) - startSize addBytes := 256 + ll + ml + used>>2 // Clamp to max block size. if used+addBytes > maxBlockSize { addBytes = maxBlockSize - used } out = append(out, make([]byte, addBytes)...) out = out[:len(out)-addBytes] } if ml > maxMatchLen { return fmt.Errorf("match len (%d) bigger than max allowed length", ml) } // Add literals out = append(out, s.literals[:ll]...) s.literals = s.literals[ll:] if mo == 0 && ml > 0 { return fmt.Errorf("zero matchoff and matchlen (%d) > 0", ml) } if mo > len(out)+len(hist) || mo > s.windowSize { if len(s.dict) == 0 { return fmt.Errorf("match offset (%d) bigger than current history (%d)", mo, len(out)+len(hist)-startSize) } // we may be in dictionary. dictO := len(s.dict) - (mo - (len(out) + len(hist))) if dictO < 0 || dictO >= len(s.dict) { return fmt.Errorf("match offset (%d) bigger than current history (%d)", mo, len(out)+len(hist)-startSize) } end := dictO + ml if end > len(s.dict) { out = append(out, s.dict[dictO:]...) ml -= len(s.dict) - dictO } else { out = append(out, s.dict[dictO:end]...) mo = 0 ml = 0 } } // Copy from history. // TODO: Blocks without history could be made to ignore this completely. if v := mo - len(out); v > 0 { // v is the start position in history from end. start := len(hist) - v if ml > v { // Some goes into current block. // Copy remainder of history out = append(out, hist[start:]...) ml -= v } else { out = append(out, hist[start:start+ml]...) ml = 0 } } // We must be in current buffer now if ml > 0 { start := len(out) - mo if ml <= len(out)-start { // No overlap out = append(out, out[start:start+ml]...) } else { // Overlapping copy // Extend destination slice and copy one byte at the time. out = out[:len(out)+ml] src := out[start : start+ml] // Destination is the space we just added. dst := out[len(out)-ml:] dst = dst[:len(src)] for i := range src { dst[i] = src[i] } } } if i == 0 { // This is the last sequence, so we shouldn't update state. break } // Manually inlined, ~ 5-20% faster // Update all 3 states at once. Approx 20% faster. nBits := llState.nbBits() + mlState.nbBits() + ofState.nbBits() if nBits == 0 { llState = llTable[llState.newState()&maxTableMask] mlState = mlTable[mlState.newState()&maxTableMask] ofState = ofTable[ofState.newState()&maxTableMask] } else { bits := br.get32BitsFast(nBits) lowBits := uint16(bits >> ((ofState.nbBits() + mlState.nbBits()) & 31)) llState = llTable[(llState.newState()+lowBits)&maxTableMask] lowBits = uint16(bits >> (ofState.nbBits() & 31)) lowBits &= bitMask[mlState.nbBits()&15] mlState = mlTable[(mlState.newState()+lowBits)&maxTableMask] lowBits = uint16(bits) & bitMask[ofState.nbBits()&15] ofState = ofTable[(ofState.newState()+lowBits)&maxTableMask] } } if size := len(s.literals) + len(out) - startSize; size > maxBlockSize { return fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) } // Add final literals s.out = append(out, s.literals...) return br.close() } var bitMask [16]uint16 func init() { for i := range bitMask[:] { bitMask[i] = uint16((1 << uint(i)) - 1) } } func (s *sequenceDecs) next(br *bitReader, llState, mlState, ofState decSymbol) (ll, mo, ml int) { // Final will not read from stream. ll, llB := llState.final() ml, mlB := mlState.final() mo, moB := ofState.final() // extra bits are stored in reverse order. br.fill() mo += br.getBits(moB) if s.maxBits > 32 { br.fill() } // matchlength+literal length, max 32 bits ml += br.getBits(mlB) ll += br.getBits(llB) mo = s.adjustOffset(mo, ll, moB) return } func (s *sequenceDecs) adjustOffset(offset, litLen int, offsetB uint8) int { if offsetB > 1 { s.prevOffset[2] = s.prevOffset[1] s.prevOffset[1] = s.prevOffset[0] s.prevOffset[0] = offset return offset } if litLen == 0 { // There is an exception though, when current sequence's literals_length = 0. // In this case, repeated offsets are shifted by one, so an offset_value of 1 means Repeated_Offset2, // an offset_value of 2 means Repeated_Offset3, and an offset_value of 3 means Repeated_Offset1 - 1_byte. offset++ } if offset == 0 { return s.prevOffset[0] } var temp int if offset == 3 { temp = s.prevOffset[0] - 1 } else { temp = s.prevOffset[offset] } if temp == 0 { // 0 is not valid; input is corrupted; force offset to 1 println("temp was 0") temp = 1 } if offset != 1 { s.prevOffset[2] = s.prevOffset[1] } s.prevOffset[1] = s.prevOffset[0] s.prevOffset[0] = temp return temp } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go ================================================ //go:build amd64 && !appengine && !noasm && gc // +build amd64,!appengine,!noasm,gc package zstd import ( "fmt" "io" "github.com/klauspost/compress/internal/cpuinfo" ) type decodeSyncAsmContext struct { llTable []decSymbol mlTable []decSymbol ofTable []decSymbol llState uint64 mlState uint64 ofState uint64 iteration int litRemain int out []byte outPosition int literals []byte litPosition int history []byte windowSize int ll int // set on error (not for all errors, please refer to _generate/gen.go) ml int // set on error (not for all errors, please refer to _generate/gen.go) mo int // set on error (not for all errors, please refer to _generate/gen.go) } // sequenceDecs_decodeSync_amd64 implements the main loop of sequenceDecs.decodeSync in x86 asm. // // Please refer to seqdec_generic.go for the reference implementation. // //go:noescape func sequenceDecs_decodeSync_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int // sequenceDecs_decodeSync_bmi2 implements the main loop of sequenceDecs.decodeSync in x86 asm with BMI2 extensions. // //go:noescape func sequenceDecs_decodeSync_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int // sequenceDecs_decodeSync_safe_amd64 does the same as above, but does not write more than output buffer. // //go:noescape func sequenceDecs_decodeSync_safe_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int // sequenceDecs_decodeSync_safe_bmi2 does the same as above, but does not write more than output buffer. // //go:noescape func sequenceDecs_decodeSync_safe_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int // decode sequences from the stream with the provided history but without a dictionary. func (s *sequenceDecs) decodeSyncSimple(hist []byte) (bool, error) { if len(s.dict) > 0 { return false, nil } if s.maxSyncLen == 0 && cap(s.out)-len(s.out) < maxCompressedBlockSize { return false, nil } // FIXME: Using unsafe memory copies leads to rare, random crashes // with fuzz testing. It is therefore disabled for now. const useSafe = true /* useSafe := false if s.maxSyncLen == 0 && cap(s.out)-len(s.out) < maxCompressedBlockSizeAlloc { useSafe = true } if s.maxSyncLen > 0 && cap(s.out)-len(s.out)-compressedBlockOverAlloc < int(s.maxSyncLen) { useSafe = true } if cap(s.literals) < len(s.literals)+compressedBlockOverAlloc { useSafe = true } */ br := s.br maxBlockSize := maxCompressedBlockSize if s.windowSize < maxBlockSize { maxBlockSize = s.windowSize } ctx := decodeSyncAsmContext{ llTable: s.litLengths.fse.dt[:maxTablesize], mlTable: s.matchLengths.fse.dt[:maxTablesize], ofTable: s.offsets.fse.dt[:maxTablesize], llState: uint64(s.litLengths.state.state), mlState: uint64(s.matchLengths.state.state), ofState: uint64(s.offsets.state.state), iteration: s.nSeqs - 1, litRemain: len(s.literals), out: s.out, outPosition: len(s.out), literals: s.literals, windowSize: s.windowSize, history: hist, } s.seqSize = 0 startSize := len(s.out) var errCode int if cpuinfo.HasBMI2() { if useSafe { errCode = sequenceDecs_decodeSync_safe_bmi2(s, br, &ctx) } else { errCode = sequenceDecs_decodeSync_bmi2(s, br, &ctx) } } else { if useSafe { errCode = sequenceDecs_decodeSync_safe_amd64(s, br, &ctx) } else { errCode = sequenceDecs_decodeSync_amd64(s, br, &ctx) } } switch errCode { case noError: break case errorMatchLenOfsMismatch: return true, fmt.Errorf("zero matchoff and matchlen (%d) > 0", ctx.ml) case errorMatchLenTooBig: return true, fmt.Errorf("match len (%d) bigger than max allowed length", ctx.ml) case errorMatchOffTooBig: return true, fmt.Errorf("match offset (%d) bigger than current history (%d)", ctx.mo, ctx.outPosition+len(hist)-startSize) case errorNotEnoughLiterals: return true, fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", ctx.ll, ctx.litRemain+ctx.ll) case errorOverread: return true, io.ErrUnexpectedEOF case errorNotEnoughSpace: size := ctx.outPosition + ctx.ll + ctx.ml if debugDecoder { println("msl:", s.maxSyncLen, "cap", cap(s.out), "bef:", startSize, "sz:", size-startSize, "mbs:", maxBlockSize, "outsz:", cap(s.out)-startSize) } return true, fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) default: return true, fmt.Errorf("sequenceDecs_decode returned erronous code %d", errCode) } s.seqSize += ctx.litRemain if s.seqSize > maxBlockSize { return true, fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) } err := br.close() if err != nil { printf("Closing sequences: %v, %+v\n", err, *br) return true, err } s.literals = s.literals[ctx.litPosition:] t := ctx.outPosition s.out = s.out[:t] // Add final literals s.out = append(s.out, s.literals...) if debugDecoder { t += len(s.literals) if t != len(s.out) { panic(fmt.Errorf("length mismatch, want %d, got %d", len(s.out), t)) } } return true, nil } // -------------------------------------------------------------------------------- type decodeAsmContext struct { llTable []decSymbol mlTable []decSymbol ofTable []decSymbol llState uint64 mlState uint64 ofState uint64 iteration int seqs []seqVals litRemain int } const noError = 0 // error reported when mo == 0 && ml > 0 const errorMatchLenOfsMismatch = 1 // error reported when ml > maxMatchLen const errorMatchLenTooBig = 2 // error reported when mo > available history or mo > s.windowSize const errorMatchOffTooBig = 3 // error reported when the sum of literal lengths exeeceds the literal buffer size const errorNotEnoughLiterals = 4 // error reported when capacity of `out` is too small const errorNotEnoughSpace = 5 // error reported when bits are overread. const errorOverread = 6 // sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm. // // Please refer to seqdec_generic.go for the reference implementation. // //go:noescape func sequenceDecs_decode_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int // sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm. // // Please refer to seqdec_generic.go for the reference implementation. // //go:noescape func sequenceDecs_decode_56_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int // sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm with BMI2 extensions. // //go:noescape func sequenceDecs_decode_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int // sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm with BMI2 extensions. // //go:noescape func sequenceDecs_decode_56_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int // decode sequences from the stream without the provided history. func (s *sequenceDecs) decode(seqs []seqVals) error { br := s.br maxBlockSize := maxCompressedBlockSize if s.windowSize < maxBlockSize { maxBlockSize = s.windowSize } ctx := decodeAsmContext{ llTable: s.litLengths.fse.dt[:maxTablesize], mlTable: s.matchLengths.fse.dt[:maxTablesize], ofTable: s.offsets.fse.dt[:maxTablesize], llState: uint64(s.litLengths.state.state), mlState: uint64(s.matchLengths.state.state), ofState: uint64(s.offsets.state.state), seqs: seqs, iteration: len(seqs) - 1, litRemain: len(s.literals), } if debugDecoder { println("decode: decoding", len(seqs), "sequences", br.remain(), "bits remain on stream") } s.seqSize = 0 lte56bits := s.maxBits+s.offsets.fse.actualTableLog+s.matchLengths.fse.actualTableLog+s.litLengths.fse.actualTableLog <= 56 var errCode int if cpuinfo.HasBMI2() { if lte56bits { errCode = sequenceDecs_decode_56_bmi2(s, br, &ctx) } else { errCode = sequenceDecs_decode_bmi2(s, br, &ctx) } } else { if lte56bits { errCode = sequenceDecs_decode_56_amd64(s, br, &ctx) } else { errCode = sequenceDecs_decode_amd64(s, br, &ctx) } } if errCode != 0 { i := len(seqs) - ctx.iteration - 1 switch errCode { case errorMatchLenOfsMismatch: ml := ctx.seqs[i].ml return fmt.Errorf("zero matchoff and matchlen (%d) > 0", ml) case errorMatchLenTooBig: ml := ctx.seqs[i].ml return fmt.Errorf("match len (%d) bigger than max allowed length", ml) case errorNotEnoughLiterals: ll := ctx.seqs[i].ll return fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", ll, ctx.litRemain+ll) case errorOverread: return io.ErrUnexpectedEOF } return fmt.Errorf("sequenceDecs_decode_amd64 returned erronous code %d", errCode) } if ctx.litRemain < 0 { return fmt.Errorf("literal count is too big: total available %d, total requested %d", len(s.literals), len(s.literals)-ctx.litRemain) } s.seqSize += ctx.litRemain if s.seqSize > maxBlockSize { return fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) } if debugDecoder { println("decode: ", br.remain(), "bits remain on stream. code:", errCode) } err := br.close() if err != nil { printf("Closing sequences: %v, %+v\n", err, *br) } return err } // -------------------------------------------------------------------------------- type executeAsmContext struct { seqs []seqVals seqIndex int out []byte history []byte literals []byte outPosition int litPosition int windowSize int } // sequenceDecs_executeSimple_amd64 implements the main loop of sequenceDecs.executeSimple in x86 asm. // // Returns false if a match offset is too big. // // Please refer to seqdec_generic.go for the reference implementation. // //go:noescape func sequenceDecs_executeSimple_amd64(ctx *executeAsmContext) bool // Same as above, but with safe memcopies // //go:noescape func sequenceDecs_executeSimple_safe_amd64(ctx *executeAsmContext) bool // executeSimple handles cases when dictionary is not used. func (s *sequenceDecs) executeSimple(seqs []seqVals, hist []byte) error { // Ensure we have enough output size... if len(s.out)+s.seqSize+compressedBlockOverAlloc > cap(s.out) { addBytes := s.seqSize + len(s.out) + compressedBlockOverAlloc s.out = append(s.out, make([]byte, addBytes)...) s.out = s.out[:len(s.out)-addBytes] } if debugDecoder { printf("Execute %d seqs with literals: %d into %d bytes\n", len(seqs), len(s.literals), s.seqSize) } var t = len(s.out) out := s.out[:t+s.seqSize] ctx := executeAsmContext{ seqs: seqs, seqIndex: 0, out: out, history: hist, outPosition: t, litPosition: 0, literals: s.literals, windowSize: s.windowSize, } var ok bool if cap(s.literals) < len(s.literals)+compressedBlockOverAlloc { ok = sequenceDecs_executeSimple_safe_amd64(&ctx) } else { ok = sequenceDecs_executeSimple_amd64(&ctx) } if !ok { return fmt.Errorf("match offset (%d) bigger than current history (%d)", seqs[ctx.seqIndex].mo, ctx.outPosition+len(hist)) } s.literals = s.literals[ctx.litPosition:] t = ctx.outPosition // Add final literals copy(out[t:], s.literals) if debugDecoder { t += len(s.literals) if t != len(out) { panic(fmt.Errorf("length mismatch, want %d, got %d, ss: %d", len(out), t, s.seqSize)) } } s.out = out return nil } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s ================================================ // Code generated by command: go run gen.go -out ../seqdec_amd64.s -pkg=zstd. DO NOT EDIT. //go:build !appengine && !noasm && gc && !noasm // func sequenceDecs_decode_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int // Requires: CMOV TEXT ·sequenceDecs_decode_amd64(SB), $8-32 MOVQ br+8(FP), CX MOVQ 24(CX), DX MOVBQZX 32(CX), BX MOVQ (CX), AX MOVQ 8(CX), SI ADDQ SI, AX MOVQ AX, (SP) MOVQ ctx+16(FP), AX MOVQ 72(AX), DI MOVQ 80(AX), R8 MOVQ 88(AX), R9 MOVQ 104(AX), R10 MOVQ s+0(FP), AX MOVQ 144(AX), R11 MOVQ 152(AX), R12 MOVQ 160(AX), R13 sequenceDecs_decode_amd64_main_loop: MOVQ (SP), R14 // Fill bitreader to have enough for the offset and match length. CMPQ SI, $0x08 JL sequenceDecs_decode_amd64_fill_byte_by_byte MOVQ BX, AX SHRQ $0x03, AX SUBQ AX, R14 MOVQ (R14), DX SUBQ AX, SI ANDQ $0x07, BX JMP sequenceDecs_decode_amd64_fill_end sequenceDecs_decode_amd64_fill_byte_by_byte: CMPQ SI, $0x00 JLE sequenceDecs_decode_amd64_fill_check_overread CMPQ BX, $0x07 JLE sequenceDecs_decode_amd64_fill_end SHLQ $0x08, DX SUBQ $0x01, R14 SUBQ $0x01, SI SUBQ $0x08, BX MOVBQZX (R14), AX ORQ AX, DX JMP sequenceDecs_decode_amd64_fill_byte_by_byte sequenceDecs_decode_amd64_fill_check_overread: CMPQ BX, $0x40 JA error_overread sequenceDecs_decode_amd64_fill_end: // Update offset MOVQ R9, AX MOVQ BX, CX MOVQ DX, R15 SHLQ CL, R15 MOVB AH, CL SHRQ $0x20, AX TESTQ CX, CX JZ sequenceDecs_decode_amd64_of_update_zero ADDQ CX, BX CMPQ BX, $0x40 JA sequenceDecs_decode_amd64_of_update_zero CMPQ CX, $0x40 JAE sequenceDecs_decode_amd64_of_update_zero NEGQ CX SHRQ CL, R15 ADDQ R15, AX sequenceDecs_decode_amd64_of_update_zero: MOVQ AX, 16(R10) // Update match length MOVQ R8, AX MOVQ BX, CX MOVQ DX, R15 SHLQ CL, R15 MOVB AH, CL SHRQ $0x20, AX TESTQ CX, CX JZ sequenceDecs_decode_amd64_ml_update_zero ADDQ CX, BX CMPQ BX, $0x40 JA sequenceDecs_decode_amd64_ml_update_zero CMPQ CX, $0x40 JAE sequenceDecs_decode_amd64_ml_update_zero NEGQ CX SHRQ CL, R15 ADDQ R15, AX sequenceDecs_decode_amd64_ml_update_zero: MOVQ AX, 8(R10) // Fill bitreader to have enough for the remaining CMPQ SI, $0x08 JL sequenceDecs_decode_amd64_fill_2_byte_by_byte MOVQ BX, AX SHRQ $0x03, AX SUBQ AX, R14 MOVQ (R14), DX SUBQ AX, SI ANDQ $0x07, BX JMP sequenceDecs_decode_amd64_fill_2_end sequenceDecs_decode_amd64_fill_2_byte_by_byte: CMPQ SI, $0x00 JLE sequenceDecs_decode_amd64_fill_2_check_overread CMPQ BX, $0x07 JLE sequenceDecs_decode_amd64_fill_2_end SHLQ $0x08, DX SUBQ $0x01, R14 SUBQ $0x01, SI SUBQ $0x08, BX MOVBQZX (R14), AX ORQ AX, DX JMP sequenceDecs_decode_amd64_fill_2_byte_by_byte sequenceDecs_decode_amd64_fill_2_check_overread: CMPQ BX, $0x40 JA error_overread sequenceDecs_decode_amd64_fill_2_end: // Update literal length MOVQ DI, AX MOVQ BX, CX MOVQ DX, R15 SHLQ CL, R15 MOVB AH, CL SHRQ $0x20, AX TESTQ CX, CX JZ sequenceDecs_decode_amd64_ll_update_zero ADDQ CX, BX CMPQ BX, $0x40 JA sequenceDecs_decode_amd64_ll_update_zero CMPQ CX, $0x40 JAE sequenceDecs_decode_amd64_ll_update_zero NEGQ CX SHRQ CL, R15 ADDQ R15, AX sequenceDecs_decode_amd64_ll_update_zero: MOVQ AX, (R10) // Fill bitreader for state updates MOVQ R14, (SP) MOVQ R9, AX SHRQ $0x08, AX MOVBQZX AL, AX MOVQ ctx+16(FP), CX CMPQ 96(CX), $0x00 JZ sequenceDecs_decode_amd64_skip_update // Update Literal Length State MOVBQZX DI, R14 SHRQ $0x10, DI MOVWQZX DI, DI LEAQ (BX)(R14*1), CX MOVQ DX, R15 MOVQ CX, BX ROLQ CL, R15 MOVL $0x00000001, BP MOVB R14, CL SHLL CL, BP DECL BP ANDQ BP, R15 ADDQ R15, DI // Load ctx.llTable MOVQ ctx+16(FP), CX MOVQ (CX), CX MOVQ (CX)(DI*8), DI // Update Match Length State MOVBQZX R8, R14 SHRQ $0x10, R8 MOVWQZX R8, R8 LEAQ (BX)(R14*1), CX MOVQ DX, R15 MOVQ CX, BX ROLQ CL, R15 MOVL $0x00000001, BP MOVB R14, CL SHLL CL, BP DECL BP ANDQ BP, R15 ADDQ R15, R8 // Load ctx.mlTable MOVQ ctx+16(FP), CX MOVQ 24(CX), CX MOVQ (CX)(R8*8), R8 // Update Offset State MOVBQZX R9, R14 SHRQ $0x10, R9 MOVWQZX R9, R9 LEAQ (BX)(R14*1), CX MOVQ DX, R15 MOVQ CX, BX ROLQ CL, R15 MOVL $0x00000001, BP MOVB R14, CL SHLL CL, BP DECL BP ANDQ BP, R15 ADDQ R15, R9 // Load ctx.ofTable MOVQ ctx+16(FP), CX MOVQ 48(CX), CX MOVQ (CX)(R9*8), R9 sequenceDecs_decode_amd64_skip_update: // Adjust offset MOVQ 16(R10), CX CMPQ AX, $0x01 JBE sequenceDecs_decode_amd64_adjust_offsetB_1_or_0 MOVQ R12, R13 MOVQ R11, R12 MOVQ CX, R11 JMP sequenceDecs_decode_amd64_after_adjust sequenceDecs_decode_amd64_adjust_offsetB_1_or_0: CMPQ (R10), $0x00000000 JNE sequenceDecs_decode_amd64_adjust_offset_maybezero INCQ CX JMP sequenceDecs_decode_amd64_adjust_offset_nonzero sequenceDecs_decode_amd64_adjust_offset_maybezero: TESTQ CX, CX JNZ sequenceDecs_decode_amd64_adjust_offset_nonzero MOVQ R11, CX JMP sequenceDecs_decode_amd64_after_adjust sequenceDecs_decode_amd64_adjust_offset_nonzero: CMPQ CX, $0x01 JB sequenceDecs_decode_amd64_adjust_zero JEQ sequenceDecs_decode_amd64_adjust_one CMPQ CX, $0x02 JA sequenceDecs_decode_amd64_adjust_three JMP sequenceDecs_decode_amd64_adjust_two sequenceDecs_decode_amd64_adjust_zero: MOVQ R11, AX JMP sequenceDecs_decode_amd64_adjust_test_temp_valid sequenceDecs_decode_amd64_adjust_one: MOVQ R12, AX JMP sequenceDecs_decode_amd64_adjust_test_temp_valid sequenceDecs_decode_amd64_adjust_two: MOVQ R13, AX JMP sequenceDecs_decode_amd64_adjust_test_temp_valid sequenceDecs_decode_amd64_adjust_three: LEAQ -1(R11), AX sequenceDecs_decode_amd64_adjust_test_temp_valid: TESTQ AX, AX JNZ sequenceDecs_decode_amd64_adjust_temp_valid MOVQ $0x00000001, AX sequenceDecs_decode_amd64_adjust_temp_valid: CMPQ CX, $0x01 CMOVQNE R12, R13 MOVQ R11, R12 MOVQ AX, R11 MOVQ AX, CX sequenceDecs_decode_amd64_after_adjust: MOVQ CX, 16(R10) // Check values MOVQ 8(R10), AX MOVQ (R10), R14 LEAQ (AX)(R14*1), R15 MOVQ s+0(FP), BP ADDQ R15, 256(BP) MOVQ ctx+16(FP), R15 SUBQ R14, 128(R15) JS error_not_enough_literals CMPQ AX, $0x00020002 JA sequenceDecs_decode_amd64_error_match_len_too_big TESTQ CX, CX JNZ sequenceDecs_decode_amd64_match_len_ofs_ok TESTQ AX, AX JNZ sequenceDecs_decode_amd64_error_match_len_ofs_mismatch sequenceDecs_decode_amd64_match_len_ofs_ok: ADDQ $0x18, R10 MOVQ ctx+16(FP), AX DECQ 96(AX) JNS sequenceDecs_decode_amd64_main_loop MOVQ s+0(FP), AX MOVQ R11, 144(AX) MOVQ R12, 152(AX) MOVQ R13, 160(AX) MOVQ br+8(FP), AX MOVQ DX, 24(AX) MOVB BL, 32(AX) MOVQ SI, 8(AX) // Return success MOVQ $0x00000000, ret+24(FP) RET // Return with match length error sequenceDecs_decode_amd64_error_match_len_ofs_mismatch: MOVQ $0x00000001, ret+24(FP) RET // Return with match too long error sequenceDecs_decode_amd64_error_match_len_too_big: MOVQ $0x00000002, ret+24(FP) RET // Return with match offset too long error MOVQ $0x00000003, ret+24(FP) RET // Return with not enough literals error error_not_enough_literals: MOVQ $0x00000004, ret+24(FP) RET // Return with overread error error_overread: MOVQ $0x00000006, ret+24(FP) RET // func sequenceDecs_decode_56_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int // Requires: CMOV TEXT ·sequenceDecs_decode_56_amd64(SB), $8-32 MOVQ br+8(FP), CX MOVQ 24(CX), DX MOVBQZX 32(CX), BX MOVQ (CX), AX MOVQ 8(CX), SI ADDQ SI, AX MOVQ AX, (SP) MOVQ ctx+16(FP), AX MOVQ 72(AX), DI MOVQ 80(AX), R8 MOVQ 88(AX), R9 MOVQ 104(AX), R10 MOVQ s+0(FP), AX MOVQ 144(AX), R11 MOVQ 152(AX), R12 MOVQ 160(AX), R13 sequenceDecs_decode_56_amd64_main_loop: MOVQ (SP), R14 // Fill bitreader to have enough for the offset and match length. CMPQ SI, $0x08 JL sequenceDecs_decode_56_amd64_fill_byte_by_byte MOVQ BX, AX SHRQ $0x03, AX SUBQ AX, R14 MOVQ (R14), DX SUBQ AX, SI ANDQ $0x07, BX JMP sequenceDecs_decode_56_amd64_fill_end sequenceDecs_decode_56_amd64_fill_byte_by_byte: CMPQ SI, $0x00 JLE sequenceDecs_decode_56_amd64_fill_check_overread CMPQ BX, $0x07 JLE sequenceDecs_decode_56_amd64_fill_end SHLQ $0x08, DX SUBQ $0x01, R14 SUBQ $0x01, SI SUBQ $0x08, BX MOVBQZX (R14), AX ORQ AX, DX JMP sequenceDecs_decode_56_amd64_fill_byte_by_byte sequenceDecs_decode_56_amd64_fill_check_overread: CMPQ BX, $0x40 JA error_overread sequenceDecs_decode_56_amd64_fill_end: // Update offset MOVQ R9, AX MOVQ BX, CX MOVQ DX, R15 SHLQ CL, R15 MOVB AH, CL SHRQ $0x20, AX TESTQ CX, CX JZ sequenceDecs_decode_56_amd64_of_update_zero ADDQ CX, BX CMPQ BX, $0x40 JA sequenceDecs_decode_56_amd64_of_update_zero CMPQ CX, $0x40 JAE sequenceDecs_decode_56_amd64_of_update_zero NEGQ CX SHRQ CL, R15 ADDQ R15, AX sequenceDecs_decode_56_amd64_of_update_zero: MOVQ AX, 16(R10) // Update match length MOVQ R8, AX MOVQ BX, CX MOVQ DX, R15 SHLQ CL, R15 MOVB AH, CL SHRQ $0x20, AX TESTQ CX, CX JZ sequenceDecs_decode_56_amd64_ml_update_zero ADDQ CX, BX CMPQ BX, $0x40 JA sequenceDecs_decode_56_amd64_ml_update_zero CMPQ CX, $0x40 JAE sequenceDecs_decode_56_amd64_ml_update_zero NEGQ CX SHRQ CL, R15 ADDQ R15, AX sequenceDecs_decode_56_amd64_ml_update_zero: MOVQ AX, 8(R10) // Update literal length MOVQ DI, AX MOVQ BX, CX MOVQ DX, R15 SHLQ CL, R15 MOVB AH, CL SHRQ $0x20, AX TESTQ CX, CX JZ sequenceDecs_decode_56_amd64_ll_update_zero ADDQ CX, BX CMPQ BX, $0x40 JA sequenceDecs_decode_56_amd64_ll_update_zero CMPQ CX, $0x40 JAE sequenceDecs_decode_56_amd64_ll_update_zero NEGQ CX SHRQ CL, R15 ADDQ R15, AX sequenceDecs_decode_56_amd64_ll_update_zero: MOVQ AX, (R10) // Fill bitreader for state updates MOVQ R14, (SP) MOVQ R9, AX SHRQ $0x08, AX MOVBQZX AL, AX MOVQ ctx+16(FP), CX CMPQ 96(CX), $0x00 JZ sequenceDecs_decode_56_amd64_skip_update // Update Literal Length State MOVBQZX DI, R14 SHRQ $0x10, DI MOVWQZX DI, DI LEAQ (BX)(R14*1), CX MOVQ DX, R15 MOVQ CX, BX ROLQ CL, R15 MOVL $0x00000001, BP MOVB R14, CL SHLL CL, BP DECL BP ANDQ BP, R15 ADDQ R15, DI // Load ctx.llTable MOVQ ctx+16(FP), CX MOVQ (CX), CX MOVQ (CX)(DI*8), DI // Update Match Length State MOVBQZX R8, R14 SHRQ $0x10, R8 MOVWQZX R8, R8 LEAQ (BX)(R14*1), CX MOVQ DX, R15 MOVQ CX, BX ROLQ CL, R15 MOVL $0x00000001, BP MOVB R14, CL SHLL CL, BP DECL BP ANDQ BP, R15 ADDQ R15, R8 // Load ctx.mlTable MOVQ ctx+16(FP), CX MOVQ 24(CX), CX MOVQ (CX)(R8*8), R8 // Update Offset State MOVBQZX R9, R14 SHRQ $0x10, R9 MOVWQZX R9, R9 LEAQ (BX)(R14*1), CX MOVQ DX, R15 MOVQ CX, BX ROLQ CL, R15 MOVL $0x00000001, BP MOVB R14, CL SHLL CL, BP DECL BP ANDQ BP, R15 ADDQ R15, R9 // Load ctx.ofTable MOVQ ctx+16(FP), CX MOVQ 48(CX), CX MOVQ (CX)(R9*8), R9 sequenceDecs_decode_56_amd64_skip_update: // Adjust offset MOVQ 16(R10), CX CMPQ AX, $0x01 JBE sequenceDecs_decode_56_amd64_adjust_offsetB_1_or_0 MOVQ R12, R13 MOVQ R11, R12 MOVQ CX, R11 JMP sequenceDecs_decode_56_amd64_after_adjust sequenceDecs_decode_56_amd64_adjust_offsetB_1_or_0: CMPQ (R10), $0x00000000 JNE sequenceDecs_decode_56_amd64_adjust_offset_maybezero INCQ CX JMP sequenceDecs_decode_56_amd64_adjust_offset_nonzero sequenceDecs_decode_56_amd64_adjust_offset_maybezero: TESTQ CX, CX JNZ sequenceDecs_decode_56_amd64_adjust_offset_nonzero MOVQ R11, CX JMP sequenceDecs_decode_56_amd64_after_adjust sequenceDecs_decode_56_amd64_adjust_offset_nonzero: CMPQ CX, $0x01 JB sequenceDecs_decode_56_amd64_adjust_zero JEQ sequenceDecs_decode_56_amd64_adjust_one CMPQ CX, $0x02 JA sequenceDecs_decode_56_amd64_adjust_three JMP sequenceDecs_decode_56_amd64_adjust_two sequenceDecs_decode_56_amd64_adjust_zero: MOVQ R11, AX JMP sequenceDecs_decode_56_amd64_adjust_test_temp_valid sequenceDecs_decode_56_amd64_adjust_one: MOVQ R12, AX JMP sequenceDecs_decode_56_amd64_adjust_test_temp_valid sequenceDecs_decode_56_amd64_adjust_two: MOVQ R13, AX JMP sequenceDecs_decode_56_amd64_adjust_test_temp_valid sequenceDecs_decode_56_amd64_adjust_three: LEAQ -1(R11), AX sequenceDecs_decode_56_amd64_adjust_test_temp_valid: TESTQ AX, AX JNZ sequenceDecs_decode_56_amd64_adjust_temp_valid MOVQ $0x00000001, AX sequenceDecs_decode_56_amd64_adjust_temp_valid: CMPQ CX, $0x01 CMOVQNE R12, R13 MOVQ R11, R12 MOVQ AX, R11 MOVQ AX, CX sequenceDecs_decode_56_amd64_after_adjust: MOVQ CX, 16(R10) // Check values MOVQ 8(R10), AX MOVQ (R10), R14 LEAQ (AX)(R14*1), R15 MOVQ s+0(FP), BP ADDQ R15, 256(BP) MOVQ ctx+16(FP), R15 SUBQ R14, 128(R15) JS error_not_enough_literals CMPQ AX, $0x00020002 JA sequenceDecs_decode_56_amd64_error_match_len_too_big TESTQ CX, CX JNZ sequenceDecs_decode_56_amd64_match_len_ofs_ok TESTQ AX, AX JNZ sequenceDecs_decode_56_amd64_error_match_len_ofs_mismatch sequenceDecs_decode_56_amd64_match_len_ofs_ok: ADDQ $0x18, R10 MOVQ ctx+16(FP), AX DECQ 96(AX) JNS sequenceDecs_decode_56_amd64_main_loop MOVQ s+0(FP), AX MOVQ R11, 144(AX) MOVQ R12, 152(AX) MOVQ R13, 160(AX) MOVQ br+8(FP), AX MOVQ DX, 24(AX) MOVB BL, 32(AX) MOVQ SI, 8(AX) // Return success MOVQ $0x00000000, ret+24(FP) RET // Return with match length error sequenceDecs_decode_56_amd64_error_match_len_ofs_mismatch: MOVQ $0x00000001, ret+24(FP) RET // Return with match too long error sequenceDecs_decode_56_amd64_error_match_len_too_big: MOVQ $0x00000002, ret+24(FP) RET // Return with match offset too long error MOVQ $0x00000003, ret+24(FP) RET // Return with not enough literals error error_not_enough_literals: MOVQ $0x00000004, ret+24(FP) RET // Return with overread error error_overread: MOVQ $0x00000006, ret+24(FP) RET // func sequenceDecs_decode_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int // Requires: BMI, BMI2, CMOV TEXT ·sequenceDecs_decode_bmi2(SB), $8-32 MOVQ br+8(FP), BX MOVQ 24(BX), AX MOVBQZX 32(BX), DX MOVQ (BX), CX MOVQ 8(BX), BX ADDQ BX, CX MOVQ CX, (SP) MOVQ ctx+16(FP), CX MOVQ 72(CX), SI MOVQ 80(CX), DI MOVQ 88(CX), R8 MOVQ 104(CX), R9 MOVQ s+0(FP), CX MOVQ 144(CX), R10 MOVQ 152(CX), R11 MOVQ 160(CX), R12 sequenceDecs_decode_bmi2_main_loop: MOVQ (SP), R13 // Fill bitreader to have enough for the offset and match length. CMPQ BX, $0x08 JL sequenceDecs_decode_bmi2_fill_byte_by_byte MOVQ DX, CX SHRQ $0x03, CX SUBQ CX, R13 MOVQ (R13), AX SUBQ CX, BX ANDQ $0x07, DX JMP sequenceDecs_decode_bmi2_fill_end sequenceDecs_decode_bmi2_fill_byte_by_byte: CMPQ BX, $0x00 JLE sequenceDecs_decode_bmi2_fill_check_overread CMPQ DX, $0x07 JLE sequenceDecs_decode_bmi2_fill_end SHLQ $0x08, AX SUBQ $0x01, R13 SUBQ $0x01, BX SUBQ $0x08, DX MOVBQZX (R13), CX ORQ CX, AX JMP sequenceDecs_decode_bmi2_fill_byte_by_byte sequenceDecs_decode_bmi2_fill_check_overread: CMPQ DX, $0x40 JA error_overread sequenceDecs_decode_bmi2_fill_end: // Update offset MOVQ $0x00000808, CX BEXTRQ CX, R8, R14 MOVQ AX, R15 LEAQ (DX)(R14*1), CX ROLQ CL, R15 BZHIQ R14, R15, R15 MOVQ CX, DX MOVQ R8, CX SHRQ $0x20, CX ADDQ R15, CX MOVQ CX, 16(R9) // Update match length MOVQ $0x00000808, CX BEXTRQ CX, DI, R14 MOVQ AX, R15 LEAQ (DX)(R14*1), CX ROLQ CL, R15 BZHIQ R14, R15, R15 MOVQ CX, DX MOVQ DI, CX SHRQ $0x20, CX ADDQ R15, CX MOVQ CX, 8(R9) // Fill bitreader to have enough for the remaining CMPQ BX, $0x08 JL sequenceDecs_decode_bmi2_fill_2_byte_by_byte MOVQ DX, CX SHRQ $0x03, CX SUBQ CX, R13 MOVQ (R13), AX SUBQ CX, BX ANDQ $0x07, DX JMP sequenceDecs_decode_bmi2_fill_2_end sequenceDecs_decode_bmi2_fill_2_byte_by_byte: CMPQ BX, $0x00 JLE sequenceDecs_decode_bmi2_fill_2_check_overread CMPQ DX, $0x07 JLE sequenceDecs_decode_bmi2_fill_2_end SHLQ $0x08, AX SUBQ $0x01, R13 SUBQ $0x01, BX SUBQ $0x08, DX MOVBQZX (R13), CX ORQ CX, AX JMP sequenceDecs_decode_bmi2_fill_2_byte_by_byte sequenceDecs_decode_bmi2_fill_2_check_overread: CMPQ DX, $0x40 JA error_overread sequenceDecs_decode_bmi2_fill_2_end: // Update literal length MOVQ $0x00000808, CX BEXTRQ CX, SI, R14 MOVQ AX, R15 LEAQ (DX)(R14*1), CX ROLQ CL, R15 BZHIQ R14, R15, R15 MOVQ CX, DX MOVQ SI, CX SHRQ $0x20, CX ADDQ R15, CX MOVQ CX, (R9) // Fill bitreader for state updates MOVQ R13, (SP) MOVQ $0x00000808, CX BEXTRQ CX, R8, R13 MOVQ ctx+16(FP), CX CMPQ 96(CX), $0x00 JZ sequenceDecs_decode_bmi2_skip_update LEAQ (SI)(DI*1), R14 ADDQ R8, R14 MOVBQZX R14, R14 LEAQ (DX)(R14*1), CX MOVQ AX, R15 MOVQ CX, DX ROLQ CL, R15 BZHIQ R14, R15, R15 // Update Offset State BZHIQ R8, R15, CX SHRXQ R8, R15, R15 MOVQ $0x00001010, R14 BEXTRQ R14, R8, R8 ADDQ CX, R8 // Load ctx.ofTable MOVQ ctx+16(FP), CX MOVQ 48(CX), CX MOVQ (CX)(R8*8), R8 // Update Match Length State BZHIQ DI, R15, CX SHRXQ DI, R15, R15 MOVQ $0x00001010, R14 BEXTRQ R14, DI, DI ADDQ CX, DI // Load ctx.mlTable MOVQ ctx+16(FP), CX MOVQ 24(CX), CX MOVQ (CX)(DI*8), DI // Update Literal Length State BZHIQ SI, R15, CX MOVQ $0x00001010, R14 BEXTRQ R14, SI, SI ADDQ CX, SI // Load ctx.llTable MOVQ ctx+16(FP), CX MOVQ (CX), CX MOVQ (CX)(SI*8), SI sequenceDecs_decode_bmi2_skip_update: // Adjust offset MOVQ 16(R9), CX CMPQ R13, $0x01 JBE sequenceDecs_decode_bmi2_adjust_offsetB_1_or_0 MOVQ R11, R12 MOVQ R10, R11 MOVQ CX, R10 JMP sequenceDecs_decode_bmi2_after_adjust sequenceDecs_decode_bmi2_adjust_offsetB_1_or_0: CMPQ (R9), $0x00000000 JNE sequenceDecs_decode_bmi2_adjust_offset_maybezero INCQ CX JMP sequenceDecs_decode_bmi2_adjust_offset_nonzero sequenceDecs_decode_bmi2_adjust_offset_maybezero: TESTQ CX, CX JNZ sequenceDecs_decode_bmi2_adjust_offset_nonzero MOVQ R10, CX JMP sequenceDecs_decode_bmi2_after_adjust sequenceDecs_decode_bmi2_adjust_offset_nonzero: CMPQ CX, $0x01 JB sequenceDecs_decode_bmi2_adjust_zero JEQ sequenceDecs_decode_bmi2_adjust_one CMPQ CX, $0x02 JA sequenceDecs_decode_bmi2_adjust_three JMP sequenceDecs_decode_bmi2_adjust_two sequenceDecs_decode_bmi2_adjust_zero: MOVQ R10, R13 JMP sequenceDecs_decode_bmi2_adjust_test_temp_valid sequenceDecs_decode_bmi2_adjust_one: MOVQ R11, R13 JMP sequenceDecs_decode_bmi2_adjust_test_temp_valid sequenceDecs_decode_bmi2_adjust_two: MOVQ R12, R13 JMP sequenceDecs_decode_bmi2_adjust_test_temp_valid sequenceDecs_decode_bmi2_adjust_three: LEAQ -1(R10), R13 sequenceDecs_decode_bmi2_adjust_test_temp_valid: TESTQ R13, R13 JNZ sequenceDecs_decode_bmi2_adjust_temp_valid MOVQ $0x00000001, R13 sequenceDecs_decode_bmi2_adjust_temp_valid: CMPQ CX, $0x01 CMOVQNE R11, R12 MOVQ R10, R11 MOVQ R13, R10 MOVQ R13, CX sequenceDecs_decode_bmi2_after_adjust: MOVQ CX, 16(R9) // Check values MOVQ 8(R9), R13 MOVQ (R9), R14 LEAQ (R13)(R14*1), R15 MOVQ s+0(FP), BP ADDQ R15, 256(BP) MOVQ ctx+16(FP), R15 SUBQ R14, 128(R15) JS error_not_enough_literals CMPQ R13, $0x00020002 JA sequenceDecs_decode_bmi2_error_match_len_too_big TESTQ CX, CX JNZ sequenceDecs_decode_bmi2_match_len_ofs_ok TESTQ R13, R13 JNZ sequenceDecs_decode_bmi2_error_match_len_ofs_mismatch sequenceDecs_decode_bmi2_match_len_ofs_ok: ADDQ $0x18, R9 MOVQ ctx+16(FP), CX DECQ 96(CX) JNS sequenceDecs_decode_bmi2_main_loop MOVQ s+0(FP), CX MOVQ R10, 144(CX) MOVQ R11, 152(CX) MOVQ R12, 160(CX) MOVQ br+8(FP), CX MOVQ AX, 24(CX) MOVB DL, 32(CX) MOVQ BX, 8(CX) // Return success MOVQ $0x00000000, ret+24(FP) RET // Return with match length error sequenceDecs_decode_bmi2_error_match_len_ofs_mismatch: MOVQ $0x00000001, ret+24(FP) RET // Return with match too long error sequenceDecs_decode_bmi2_error_match_len_too_big: MOVQ $0x00000002, ret+24(FP) RET // Return with match offset too long error MOVQ $0x00000003, ret+24(FP) RET // Return with not enough literals error error_not_enough_literals: MOVQ $0x00000004, ret+24(FP) RET // Return with overread error error_overread: MOVQ $0x00000006, ret+24(FP) RET // func sequenceDecs_decode_56_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int // Requires: BMI, BMI2, CMOV TEXT ·sequenceDecs_decode_56_bmi2(SB), $8-32 MOVQ br+8(FP), BX MOVQ 24(BX), AX MOVBQZX 32(BX), DX MOVQ (BX), CX MOVQ 8(BX), BX ADDQ BX, CX MOVQ CX, (SP) MOVQ ctx+16(FP), CX MOVQ 72(CX), SI MOVQ 80(CX), DI MOVQ 88(CX), R8 MOVQ 104(CX), R9 MOVQ s+0(FP), CX MOVQ 144(CX), R10 MOVQ 152(CX), R11 MOVQ 160(CX), R12 sequenceDecs_decode_56_bmi2_main_loop: MOVQ (SP), R13 // Fill bitreader to have enough for the offset and match length. CMPQ BX, $0x08 JL sequenceDecs_decode_56_bmi2_fill_byte_by_byte MOVQ DX, CX SHRQ $0x03, CX SUBQ CX, R13 MOVQ (R13), AX SUBQ CX, BX ANDQ $0x07, DX JMP sequenceDecs_decode_56_bmi2_fill_end sequenceDecs_decode_56_bmi2_fill_byte_by_byte: CMPQ BX, $0x00 JLE sequenceDecs_decode_56_bmi2_fill_check_overread CMPQ DX, $0x07 JLE sequenceDecs_decode_56_bmi2_fill_end SHLQ $0x08, AX SUBQ $0x01, R13 SUBQ $0x01, BX SUBQ $0x08, DX MOVBQZX (R13), CX ORQ CX, AX JMP sequenceDecs_decode_56_bmi2_fill_byte_by_byte sequenceDecs_decode_56_bmi2_fill_check_overread: CMPQ DX, $0x40 JA error_overread sequenceDecs_decode_56_bmi2_fill_end: // Update offset MOVQ $0x00000808, CX BEXTRQ CX, R8, R14 MOVQ AX, R15 LEAQ (DX)(R14*1), CX ROLQ CL, R15 BZHIQ R14, R15, R15 MOVQ CX, DX MOVQ R8, CX SHRQ $0x20, CX ADDQ R15, CX MOVQ CX, 16(R9) // Update match length MOVQ $0x00000808, CX BEXTRQ CX, DI, R14 MOVQ AX, R15 LEAQ (DX)(R14*1), CX ROLQ CL, R15 BZHIQ R14, R15, R15 MOVQ CX, DX MOVQ DI, CX SHRQ $0x20, CX ADDQ R15, CX MOVQ CX, 8(R9) // Update literal length MOVQ $0x00000808, CX BEXTRQ CX, SI, R14 MOVQ AX, R15 LEAQ (DX)(R14*1), CX ROLQ CL, R15 BZHIQ R14, R15, R15 MOVQ CX, DX MOVQ SI, CX SHRQ $0x20, CX ADDQ R15, CX MOVQ CX, (R9) // Fill bitreader for state updates MOVQ R13, (SP) MOVQ $0x00000808, CX BEXTRQ CX, R8, R13 MOVQ ctx+16(FP), CX CMPQ 96(CX), $0x00 JZ sequenceDecs_decode_56_bmi2_skip_update LEAQ (SI)(DI*1), R14 ADDQ R8, R14 MOVBQZX R14, R14 LEAQ (DX)(R14*1), CX MOVQ AX, R15 MOVQ CX, DX ROLQ CL, R15 BZHIQ R14, R15, R15 // Update Offset State BZHIQ R8, R15, CX SHRXQ R8, R15, R15 MOVQ $0x00001010, R14 BEXTRQ R14, R8, R8 ADDQ CX, R8 // Load ctx.ofTable MOVQ ctx+16(FP), CX MOVQ 48(CX), CX MOVQ (CX)(R8*8), R8 // Update Match Length State BZHIQ DI, R15, CX SHRXQ DI, R15, R15 MOVQ $0x00001010, R14 BEXTRQ R14, DI, DI ADDQ CX, DI // Load ctx.mlTable MOVQ ctx+16(FP), CX MOVQ 24(CX), CX MOVQ (CX)(DI*8), DI // Update Literal Length State BZHIQ SI, R15, CX MOVQ $0x00001010, R14 BEXTRQ R14, SI, SI ADDQ CX, SI // Load ctx.llTable MOVQ ctx+16(FP), CX MOVQ (CX), CX MOVQ (CX)(SI*8), SI sequenceDecs_decode_56_bmi2_skip_update: // Adjust offset MOVQ 16(R9), CX CMPQ R13, $0x01 JBE sequenceDecs_decode_56_bmi2_adjust_offsetB_1_or_0 MOVQ R11, R12 MOVQ R10, R11 MOVQ CX, R10 JMP sequenceDecs_decode_56_bmi2_after_adjust sequenceDecs_decode_56_bmi2_adjust_offsetB_1_or_0: CMPQ (R9), $0x00000000 JNE sequenceDecs_decode_56_bmi2_adjust_offset_maybezero INCQ CX JMP sequenceDecs_decode_56_bmi2_adjust_offset_nonzero sequenceDecs_decode_56_bmi2_adjust_offset_maybezero: TESTQ CX, CX JNZ sequenceDecs_decode_56_bmi2_adjust_offset_nonzero MOVQ R10, CX JMP sequenceDecs_decode_56_bmi2_after_adjust sequenceDecs_decode_56_bmi2_adjust_offset_nonzero: CMPQ CX, $0x01 JB sequenceDecs_decode_56_bmi2_adjust_zero JEQ sequenceDecs_decode_56_bmi2_adjust_one CMPQ CX, $0x02 JA sequenceDecs_decode_56_bmi2_adjust_three JMP sequenceDecs_decode_56_bmi2_adjust_two sequenceDecs_decode_56_bmi2_adjust_zero: MOVQ R10, R13 JMP sequenceDecs_decode_56_bmi2_adjust_test_temp_valid sequenceDecs_decode_56_bmi2_adjust_one: MOVQ R11, R13 JMP sequenceDecs_decode_56_bmi2_adjust_test_temp_valid sequenceDecs_decode_56_bmi2_adjust_two: MOVQ R12, R13 JMP sequenceDecs_decode_56_bmi2_adjust_test_temp_valid sequenceDecs_decode_56_bmi2_adjust_three: LEAQ -1(R10), R13 sequenceDecs_decode_56_bmi2_adjust_test_temp_valid: TESTQ R13, R13 JNZ sequenceDecs_decode_56_bmi2_adjust_temp_valid MOVQ $0x00000001, R13 sequenceDecs_decode_56_bmi2_adjust_temp_valid: CMPQ CX, $0x01 CMOVQNE R11, R12 MOVQ R10, R11 MOVQ R13, R10 MOVQ R13, CX sequenceDecs_decode_56_bmi2_after_adjust: MOVQ CX, 16(R9) // Check values MOVQ 8(R9), R13 MOVQ (R9), R14 LEAQ (R13)(R14*1), R15 MOVQ s+0(FP), BP ADDQ R15, 256(BP) MOVQ ctx+16(FP), R15 SUBQ R14, 128(R15) JS error_not_enough_literals CMPQ R13, $0x00020002 JA sequenceDecs_decode_56_bmi2_error_match_len_too_big TESTQ CX, CX JNZ sequenceDecs_decode_56_bmi2_match_len_ofs_ok TESTQ R13, R13 JNZ sequenceDecs_decode_56_bmi2_error_match_len_ofs_mismatch sequenceDecs_decode_56_bmi2_match_len_ofs_ok: ADDQ $0x18, R9 MOVQ ctx+16(FP), CX DECQ 96(CX) JNS sequenceDecs_decode_56_bmi2_main_loop MOVQ s+0(FP), CX MOVQ R10, 144(CX) MOVQ R11, 152(CX) MOVQ R12, 160(CX) MOVQ br+8(FP), CX MOVQ AX, 24(CX) MOVB DL, 32(CX) MOVQ BX, 8(CX) // Return success MOVQ $0x00000000, ret+24(FP) RET // Return with match length error sequenceDecs_decode_56_bmi2_error_match_len_ofs_mismatch: MOVQ $0x00000001, ret+24(FP) RET // Return with match too long error sequenceDecs_decode_56_bmi2_error_match_len_too_big: MOVQ $0x00000002, ret+24(FP) RET // Return with match offset too long error MOVQ $0x00000003, ret+24(FP) RET // Return with not enough literals error error_not_enough_literals: MOVQ $0x00000004, ret+24(FP) RET // Return with overread error error_overread: MOVQ $0x00000006, ret+24(FP) RET // func sequenceDecs_executeSimple_amd64(ctx *executeAsmContext) bool // Requires: SSE TEXT ·sequenceDecs_executeSimple_amd64(SB), $8-9 MOVQ ctx+0(FP), R10 MOVQ 8(R10), CX TESTQ CX, CX JZ empty_seqs MOVQ (R10), AX MOVQ 24(R10), DX MOVQ 32(R10), BX MOVQ 80(R10), SI MOVQ 104(R10), DI MOVQ 120(R10), R8 MOVQ 56(R10), R9 MOVQ 64(R10), R10 ADDQ R10, R9 // seqsBase += 24 * seqIndex LEAQ (DX)(DX*2), R11 SHLQ $0x03, R11 ADDQ R11, AX // outBase += outPosition ADDQ DI, BX main_loop: MOVQ (AX), R11 MOVQ 16(AX), R12 MOVQ 8(AX), R13 // Copy literals TESTQ R11, R11 JZ check_offset XORQ R14, R14 copy_1: MOVUPS (SI)(R14*1), X0 MOVUPS X0, (BX)(R14*1) ADDQ $0x10, R14 CMPQ R14, R11 JB copy_1 ADDQ R11, SI ADDQ R11, BX ADDQ R11, DI // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize) check_offset: LEAQ (DI)(R10*1), R11 CMPQ R12, R11 JG error_match_off_too_big CMPQ R12, R8 JG error_match_off_too_big // Copy match from history MOVQ R12, R11 SUBQ DI, R11 JLS copy_match MOVQ R9, R14 SUBQ R11, R14 CMPQ R13, R11 JG copy_all_from_history MOVQ R13, R11 SUBQ $0x10, R11 JB copy_4_small copy_4_loop: MOVUPS (R14), X0 MOVUPS X0, (BX) ADDQ $0x10, R14 ADDQ $0x10, BX SUBQ $0x10, R11 JAE copy_4_loop LEAQ 16(R14)(R11*1), R14 LEAQ 16(BX)(R11*1), BX MOVUPS -16(R14), X0 MOVUPS X0, -16(BX) JMP copy_4_end copy_4_small: CMPQ R13, $0x03 JE copy_4_move_3 CMPQ R13, $0x08 JB copy_4_move_4through7 JMP copy_4_move_8through16 copy_4_move_3: MOVW (R14), R11 MOVB 2(R14), R12 MOVW R11, (BX) MOVB R12, 2(BX) ADDQ R13, R14 ADDQ R13, BX JMP copy_4_end copy_4_move_4through7: MOVL (R14), R11 MOVL -4(R14)(R13*1), R12 MOVL R11, (BX) MOVL R12, -4(BX)(R13*1) ADDQ R13, R14 ADDQ R13, BX JMP copy_4_end copy_4_move_8through16: MOVQ (R14), R11 MOVQ -8(R14)(R13*1), R12 MOVQ R11, (BX) MOVQ R12, -8(BX)(R13*1) ADDQ R13, R14 ADDQ R13, BX copy_4_end: ADDQ R13, DI ADDQ $0x18, AX INCQ DX CMPQ DX, CX JB main_loop JMP loop_finished copy_all_from_history: MOVQ R11, R15 SUBQ $0x10, R15 JB copy_5_small copy_5_loop: MOVUPS (R14), X0 MOVUPS X0, (BX) ADDQ $0x10, R14 ADDQ $0x10, BX SUBQ $0x10, R15 JAE copy_5_loop LEAQ 16(R14)(R15*1), R14 LEAQ 16(BX)(R15*1), BX MOVUPS -16(R14), X0 MOVUPS X0, -16(BX) JMP copy_5_end copy_5_small: CMPQ R11, $0x03 JE copy_5_move_3 JB copy_5_move_1or2 CMPQ R11, $0x08 JB copy_5_move_4through7 JMP copy_5_move_8through16 copy_5_move_1or2: MOVB (R14), R15 MOVB -1(R14)(R11*1), BP MOVB R15, (BX) MOVB BP, -1(BX)(R11*1) ADDQ R11, R14 ADDQ R11, BX JMP copy_5_end copy_5_move_3: MOVW (R14), R15 MOVB 2(R14), BP MOVW R15, (BX) MOVB BP, 2(BX) ADDQ R11, R14 ADDQ R11, BX JMP copy_5_end copy_5_move_4through7: MOVL (R14), R15 MOVL -4(R14)(R11*1), BP MOVL R15, (BX) MOVL BP, -4(BX)(R11*1) ADDQ R11, R14 ADDQ R11, BX JMP copy_5_end copy_5_move_8through16: MOVQ (R14), R15 MOVQ -8(R14)(R11*1), BP MOVQ R15, (BX) MOVQ BP, -8(BX)(R11*1) ADDQ R11, R14 ADDQ R11, BX copy_5_end: ADDQ R11, DI SUBQ R11, R13 // Copy match from the current buffer copy_match: MOVQ BX, R11 SUBQ R12, R11 // ml <= mo CMPQ R13, R12 JA copy_overlapping_match // Copy non-overlapping match ADDQ R13, DI MOVQ BX, R12 ADDQ R13, BX copy_2: MOVUPS (R11), X0 MOVUPS X0, (R12) ADDQ $0x10, R11 ADDQ $0x10, R12 SUBQ $0x10, R13 JHI copy_2 JMP handle_loop // Copy overlapping match copy_overlapping_match: ADDQ R13, DI copy_slow_3: MOVB (R11), R12 MOVB R12, (BX) INCQ R11 INCQ BX DECQ R13 JNZ copy_slow_3 handle_loop: ADDQ $0x18, AX INCQ DX CMPQ DX, CX JB main_loop loop_finished: // Return value MOVB $0x01, ret+8(FP) // Update the context MOVQ ctx+0(FP), AX MOVQ DX, 24(AX) MOVQ DI, 104(AX) SUBQ 80(AX), SI MOVQ SI, 112(AX) RET error_match_off_too_big: // Return value MOVB $0x00, ret+8(FP) // Update the context MOVQ ctx+0(FP), AX MOVQ DX, 24(AX) MOVQ DI, 104(AX) SUBQ 80(AX), SI MOVQ SI, 112(AX) RET empty_seqs: // Return value MOVB $0x01, ret+8(FP) RET // func sequenceDecs_executeSimple_safe_amd64(ctx *executeAsmContext) bool // Requires: SSE TEXT ·sequenceDecs_executeSimple_safe_amd64(SB), $8-9 MOVQ ctx+0(FP), R10 MOVQ 8(R10), CX TESTQ CX, CX JZ empty_seqs MOVQ (R10), AX MOVQ 24(R10), DX MOVQ 32(R10), BX MOVQ 80(R10), SI MOVQ 104(R10), DI MOVQ 120(R10), R8 MOVQ 56(R10), R9 MOVQ 64(R10), R10 ADDQ R10, R9 // seqsBase += 24 * seqIndex LEAQ (DX)(DX*2), R11 SHLQ $0x03, R11 ADDQ R11, AX // outBase += outPosition ADDQ DI, BX main_loop: MOVQ (AX), R11 MOVQ 16(AX), R12 MOVQ 8(AX), R13 // Copy literals TESTQ R11, R11 JZ check_offset MOVQ R11, R14 SUBQ $0x10, R14 JB copy_1_small copy_1_loop: MOVUPS (SI), X0 MOVUPS X0, (BX) ADDQ $0x10, SI ADDQ $0x10, BX SUBQ $0x10, R14 JAE copy_1_loop LEAQ 16(SI)(R14*1), SI LEAQ 16(BX)(R14*1), BX MOVUPS -16(SI), X0 MOVUPS X0, -16(BX) JMP copy_1_end copy_1_small: CMPQ R11, $0x03 JE copy_1_move_3 JB copy_1_move_1or2 CMPQ R11, $0x08 JB copy_1_move_4through7 JMP copy_1_move_8through16 copy_1_move_1or2: MOVB (SI), R14 MOVB -1(SI)(R11*1), R15 MOVB R14, (BX) MOVB R15, -1(BX)(R11*1) ADDQ R11, SI ADDQ R11, BX JMP copy_1_end copy_1_move_3: MOVW (SI), R14 MOVB 2(SI), R15 MOVW R14, (BX) MOVB R15, 2(BX) ADDQ R11, SI ADDQ R11, BX JMP copy_1_end copy_1_move_4through7: MOVL (SI), R14 MOVL -4(SI)(R11*1), R15 MOVL R14, (BX) MOVL R15, -4(BX)(R11*1) ADDQ R11, SI ADDQ R11, BX JMP copy_1_end copy_1_move_8through16: MOVQ (SI), R14 MOVQ -8(SI)(R11*1), R15 MOVQ R14, (BX) MOVQ R15, -8(BX)(R11*1) ADDQ R11, SI ADDQ R11, BX copy_1_end: ADDQ R11, DI // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize) check_offset: LEAQ (DI)(R10*1), R11 CMPQ R12, R11 JG error_match_off_too_big CMPQ R12, R8 JG error_match_off_too_big // Copy match from history MOVQ R12, R11 SUBQ DI, R11 JLS copy_match MOVQ R9, R14 SUBQ R11, R14 CMPQ R13, R11 JG copy_all_from_history MOVQ R13, R11 SUBQ $0x10, R11 JB copy_4_small copy_4_loop: MOVUPS (R14), X0 MOVUPS X0, (BX) ADDQ $0x10, R14 ADDQ $0x10, BX SUBQ $0x10, R11 JAE copy_4_loop LEAQ 16(R14)(R11*1), R14 LEAQ 16(BX)(R11*1), BX MOVUPS -16(R14), X0 MOVUPS X0, -16(BX) JMP copy_4_end copy_4_small: CMPQ R13, $0x03 JE copy_4_move_3 CMPQ R13, $0x08 JB copy_4_move_4through7 JMP copy_4_move_8through16 copy_4_move_3: MOVW (R14), R11 MOVB 2(R14), R12 MOVW R11, (BX) MOVB R12, 2(BX) ADDQ R13, R14 ADDQ R13, BX JMP copy_4_end copy_4_move_4through7: MOVL (R14), R11 MOVL -4(R14)(R13*1), R12 MOVL R11, (BX) MOVL R12, -4(BX)(R13*1) ADDQ R13, R14 ADDQ R13, BX JMP copy_4_end copy_4_move_8through16: MOVQ (R14), R11 MOVQ -8(R14)(R13*1), R12 MOVQ R11, (BX) MOVQ R12, -8(BX)(R13*1) ADDQ R13, R14 ADDQ R13, BX copy_4_end: ADDQ R13, DI ADDQ $0x18, AX INCQ DX CMPQ DX, CX JB main_loop JMP loop_finished copy_all_from_history: MOVQ R11, R15 SUBQ $0x10, R15 JB copy_5_small copy_5_loop: MOVUPS (R14), X0 MOVUPS X0, (BX) ADDQ $0x10, R14 ADDQ $0x10, BX SUBQ $0x10, R15 JAE copy_5_loop LEAQ 16(R14)(R15*1), R14 LEAQ 16(BX)(R15*1), BX MOVUPS -16(R14), X0 MOVUPS X0, -16(BX) JMP copy_5_end copy_5_small: CMPQ R11, $0x03 JE copy_5_move_3 JB copy_5_move_1or2 CMPQ R11, $0x08 JB copy_5_move_4through7 JMP copy_5_move_8through16 copy_5_move_1or2: MOVB (R14), R15 MOVB -1(R14)(R11*1), BP MOVB R15, (BX) MOVB BP, -1(BX)(R11*1) ADDQ R11, R14 ADDQ R11, BX JMP copy_5_end copy_5_move_3: MOVW (R14), R15 MOVB 2(R14), BP MOVW R15, (BX) MOVB BP, 2(BX) ADDQ R11, R14 ADDQ R11, BX JMP copy_5_end copy_5_move_4through7: MOVL (R14), R15 MOVL -4(R14)(R11*1), BP MOVL R15, (BX) MOVL BP, -4(BX)(R11*1) ADDQ R11, R14 ADDQ R11, BX JMP copy_5_end copy_5_move_8through16: MOVQ (R14), R15 MOVQ -8(R14)(R11*1), BP MOVQ R15, (BX) MOVQ BP, -8(BX)(R11*1) ADDQ R11, R14 ADDQ R11, BX copy_5_end: ADDQ R11, DI SUBQ R11, R13 // Copy match from the current buffer copy_match: MOVQ BX, R11 SUBQ R12, R11 // ml <= mo CMPQ R13, R12 JA copy_overlapping_match // Copy non-overlapping match ADDQ R13, DI MOVQ R13, R12 SUBQ $0x10, R12 JB copy_2_small copy_2_loop: MOVUPS (R11), X0 MOVUPS X0, (BX) ADDQ $0x10, R11 ADDQ $0x10, BX SUBQ $0x10, R12 JAE copy_2_loop LEAQ 16(R11)(R12*1), R11 LEAQ 16(BX)(R12*1), BX MOVUPS -16(R11), X0 MOVUPS X0, -16(BX) JMP copy_2_end copy_2_small: CMPQ R13, $0x03 JE copy_2_move_3 JB copy_2_move_1or2 CMPQ R13, $0x08 JB copy_2_move_4through7 JMP copy_2_move_8through16 copy_2_move_1or2: MOVB (R11), R12 MOVB -1(R11)(R13*1), R14 MOVB R12, (BX) MOVB R14, -1(BX)(R13*1) ADDQ R13, R11 ADDQ R13, BX JMP copy_2_end copy_2_move_3: MOVW (R11), R12 MOVB 2(R11), R14 MOVW R12, (BX) MOVB R14, 2(BX) ADDQ R13, R11 ADDQ R13, BX JMP copy_2_end copy_2_move_4through7: MOVL (R11), R12 MOVL -4(R11)(R13*1), R14 MOVL R12, (BX) MOVL R14, -4(BX)(R13*1) ADDQ R13, R11 ADDQ R13, BX JMP copy_2_end copy_2_move_8through16: MOVQ (R11), R12 MOVQ -8(R11)(R13*1), R14 MOVQ R12, (BX) MOVQ R14, -8(BX)(R13*1) ADDQ R13, R11 ADDQ R13, BX copy_2_end: JMP handle_loop // Copy overlapping match copy_overlapping_match: ADDQ R13, DI copy_slow_3: MOVB (R11), R12 MOVB R12, (BX) INCQ R11 INCQ BX DECQ R13 JNZ copy_slow_3 handle_loop: ADDQ $0x18, AX INCQ DX CMPQ DX, CX JB main_loop loop_finished: // Return value MOVB $0x01, ret+8(FP) // Update the context MOVQ ctx+0(FP), AX MOVQ DX, 24(AX) MOVQ DI, 104(AX) SUBQ 80(AX), SI MOVQ SI, 112(AX) RET error_match_off_too_big: // Return value MOVB $0x00, ret+8(FP) // Update the context MOVQ ctx+0(FP), AX MOVQ DX, 24(AX) MOVQ DI, 104(AX) SUBQ 80(AX), SI MOVQ SI, 112(AX) RET empty_seqs: // Return value MOVB $0x01, ret+8(FP) RET // func sequenceDecs_decodeSync_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int // Requires: CMOV, SSE TEXT ·sequenceDecs_decodeSync_amd64(SB), $64-32 MOVQ br+8(FP), CX MOVQ 24(CX), DX MOVBQZX 32(CX), BX MOVQ (CX), AX MOVQ 8(CX), SI ADDQ SI, AX MOVQ AX, (SP) MOVQ ctx+16(FP), AX MOVQ 72(AX), DI MOVQ 80(AX), R8 MOVQ 88(AX), R9 XORQ CX, CX MOVQ CX, 8(SP) MOVQ CX, 16(SP) MOVQ CX, 24(SP) MOVQ 112(AX), R10 MOVQ 128(AX), CX MOVQ CX, 32(SP) MOVQ 144(AX), R11 MOVQ 136(AX), R12 MOVQ 200(AX), CX MOVQ CX, 56(SP) MOVQ 176(AX), CX MOVQ CX, 48(SP) MOVQ 184(AX), AX MOVQ AX, 40(SP) MOVQ 40(SP), AX ADDQ AX, 48(SP) // Calculate poiter to s.out[cap(s.out)] (a past-end pointer) ADDQ R10, 32(SP) // outBase += outPosition ADDQ R12, R10 sequenceDecs_decodeSync_amd64_main_loop: MOVQ (SP), R13 // Fill bitreader to have enough for the offset and match length. CMPQ SI, $0x08 JL sequenceDecs_decodeSync_amd64_fill_byte_by_byte MOVQ BX, AX SHRQ $0x03, AX SUBQ AX, R13 MOVQ (R13), DX SUBQ AX, SI ANDQ $0x07, BX JMP sequenceDecs_decodeSync_amd64_fill_end sequenceDecs_decodeSync_amd64_fill_byte_by_byte: CMPQ SI, $0x00 JLE sequenceDecs_decodeSync_amd64_fill_check_overread CMPQ BX, $0x07 JLE sequenceDecs_decodeSync_amd64_fill_end SHLQ $0x08, DX SUBQ $0x01, R13 SUBQ $0x01, SI SUBQ $0x08, BX MOVBQZX (R13), AX ORQ AX, DX JMP sequenceDecs_decodeSync_amd64_fill_byte_by_byte sequenceDecs_decodeSync_amd64_fill_check_overread: CMPQ BX, $0x40 JA error_overread sequenceDecs_decodeSync_amd64_fill_end: // Update offset MOVQ R9, AX MOVQ BX, CX MOVQ DX, R14 SHLQ CL, R14 MOVB AH, CL SHRQ $0x20, AX TESTQ CX, CX JZ sequenceDecs_decodeSync_amd64_of_update_zero ADDQ CX, BX CMPQ BX, $0x40 JA sequenceDecs_decodeSync_amd64_of_update_zero CMPQ CX, $0x40 JAE sequenceDecs_decodeSync_amd64_of_update_zero NEGQ CX SHRQ CL, R14 ADDQ R14, AX sequenceDecs_decodeSync_amd64_of_update_zero: MOVQ AX, 8(SP) // Update match length MOVQ R8, AX MOVQ BX, CX MOVQ DX, R14 SHLQ CL, R14 MOVB AH, CL SHRQ $0x20, AX TESTQ CX, CX JZ sequenceDecs_decodeSync_amd64_ml_update_zero ADDQ CX, BX CMPQ BX, $0x40 JA sequenceDecs_decodeSync_amd64_ml_update_zero CMPQ CX, $0x40 JAE sequenceDecs_decodeSync_amd64_ml_update_zero NEGQ CX SHRQ CL, R14 ADDQ R14, AX sequenceDecs_decodeSync_amd64_ml_update_zero: MOVQ AX, 16(SP) // Fill bitreader to have enough for the remaining CMPQ SI, $0x08 JL sequenceDecs_decodeSync_amd64_fill_2_byte_by_byte MOVQ BX, AX SHRQ $0x03, AX SUBQ AX, R13 MOVQ (R13), DX SUBQ AX, SI ANDQ $0x07, BX JMP sequenceDecs_decodeSync_amd64_fill_2_end sequenceDecs_decodeSync_amd64_fill_2_byte_by_byte: CMPQ SI, $0x00 JLE sequenceDecs_decodeSync_amd64_fill_2_check_overread CMPQ BX, $0x07 JLE sequenceDecs_decodeSync_amd64_fill_2_end SHLQ $0x08, DX SUBQ $0x01, R13 SUBQ $0x01, SI SUBQ $0x08, BX MOVBQZX (R13), AX ORQ AX, DX JMP sequenceDecs_decodeSync_amd64_fill_2_byte_by_byte sequenceDecs_decodeSync_amd64_fill_2_check_overread: CMPQ BX, $0x40 JA error_overread sequenceDecs_decodeSync_amd64_fill_2_end: // Update literal length MOVQ DI, AX MOVQ BX, CX MOVQ DX, R14 SHLQ CL, R14 MOVB AH, CL SHRQ $0x20, AX TESTQ CX, CX JZ sequenceDecs_decodeSync_amd64_ll_update_zero ADDQ CX, BX CMPQ BX, $0x40 JA sequenceDecs_decodeSync_amd64_ll_update_zero CMPQ CX, $0x40 JAE sequenceDecs_decodeSync_amd64_ll_update_zero NEGQ CX SHRQ CL, R14 ADDQ R14, AX sequenceDecs_decodeSync_amd64_ll_update_zero: MOVQ AX, 24(SP) // Fill bitreader for state updates MOVQ R13, (SP) MOVQ R9, AX SHRQ $0x08, AX MOVBQZX AL, AX MOVQ ctx+16(FP), CX CMPQ 96(CX), $0x00 JZ sequenceDecs_decodeSync_amd64_skip_update // Update Literal Length State MOVBQZX DI, R13 SHRQ $0x10, DI MOVWQZX DI, DI LEAQ (BX)(R13*1), CX MOVQ DX, R14 MOVQ CX, BX ROLQ CL, R14 MOVL $0x00000001, R15 MOVB R13, CL SHLL CL, R15 DECL R15 ANDQ R15, R14 ADDQ R14, DI // Load ctx.llTable MOVQ ctx+16(FP), CX MOVQ (CX), CX MOVQ (CX)(DI*8), DI // Update Match Length State MOVBQZX R8, R13 SHRQ $0x10, R8 MOVWQZX R8, R8 LEAQ (BX)(R13*1), CX MOVQ DX, R14 MOVQ CX, BX ROLQ CL, R14 MOVL $0x00000001, R15 MOVB R13, CL SHLL CL, R15 DECL R15 ANDQ R15, R14 ADDQ R14, R8 // Load ctx.mlTable MOVQ ctx+16(FP), CX MOVQ 24(CX), CX MOVQ (CX)(R8*8), R8 // Update Offset State MOVBQZX R9, R13 SHRQ $0x10, R9 MOVWQZX R9, R9 LEAQ (BX)(R13*1), CX MOVQ DX, R14 MOVQ CX, BX ROLQ CL, R14 MOVL $0x00000001, R15 MOVB R13, CL SHLL CL, R15 DECL R15 ANDQ R15, R14 ADDQ R14, R9 // Load ctx.ofTable MOVQ ctx+16(FP), CX MOVQ 48(CX), CX MOVQ (CX)(R9*8), R9 sequenceDecs_decodeSync_amd64_skip_update: // Adjust offset MOVQ s+0(FP), CX MOVQ 8(SP), R13 CMPQ AX, $0x01 JBE sequenceDecs_decodeSync_amd64_adjust_offsetB_1_or_0 MOVUPS 144(CX), X0 MOVQ R13, 144(CX) MOVUPS X0, 152(CX) JMP sequenceDecs_decodeSync_amd64_after_adjust sequenceDecs_decodeSync_amd64_adjust_offsetB_1_or_0: CMPQ 24(SP), $0x00000000 JNE sequenceDecs_decodeSync_amd64_adjust_offset_maybezero INCQ R13 JMP sequenceDecs_decodeSync_amd64_adjust_offset_nonzero sequenceDecs_decodeSync_amd64_adjust_offset_maybezero: TESTQ R13, R13 JNZ sequenceDecs_decodeSync_amd64_adjust_offset_nonzero MOVQ 144(CX), R13 JMP sequenceDecs_decodeSync_amd64_after_adjust sequenceDecs_decodeSync_amd64_adjust_offset_nonzero: MOVQ R13, AX XORQ R14, R14 MOVQ $-1, R15 CMPQ R13, $0x03 CMOVQEQ R14, AX CMOVQEQ R15, R14 ADDQ 144(CX)(AX*8), R14 JNZ sequenceDecs_decodeSync_amd64_adjust_temp_valid MOVQ $0x00000001, R14 sequenceDecs_decodeSync_amd64_adjust_temp_valid: CMPQ R13, $0x01 JZ sequenceDecs_decodeSync_amd64_adjust_skip MOVQ 152(CX), AX MOVQ AX, 160(CX) sequenceDecs_decodeSync_amd64_adjust_skip: MOVQ 144(CX), AX MOVQ AX, 152(CX) MOVQ R14, 144(CX) MOVQ R14, R13 sequenceDecs_decodeSync_amd64_after_adjust: MOVQ R13, 8(SP) // Check values MOVQ 16(SP), AX MOVQ 24(SP), CX LEAQ (AX)(CX*1), R14 MOVQ s+0(FP), R15 ADDQ R14, 256(R15) MOVQ ctx+16(FP), R14 SUBQ CX, 104(R14) JS error_not_enough_literals CMPQ AX, $0x00020002 JA sequenceDecs_decodeSync_amd64_error_match_len_too_big TESTQ R13, R13 JNZ sequenceDecs_decodeSync_amd64_match_len_ofs_ok TESTQ AX, AX JNZ sequenceDecs_decodeSync_amd64_error_match_len_ofs_mismatch sequenceDecs_decodeSync_amd64_match_len_ofs_ok: MOVQ 24(SP), AX MOVQ 8(SP), CX MOVQ 16(SP), R13 // Check if we have enough space in s.out LEAQ (AX)(R13*1), R14 ADDQ R10, R14 CMPQ R14, 32(SP) JA error_not_enough_space // Copy literals TESTQ AX, AX JZ check_offset XORQ R14, R14 copy_1: MOVUPS (R11)(R14*1), X0 MOVUPS X0, (R10)(R14*1) ADDQ $0x10, R14 CMPQ R14, AX JB copy_1 ADDQ AX, R11 ADDQ AX, R10 ADDQ AX, R12 // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize) check_offset: MOVQ R12, AX ADDQ 40(SP), AX CMPQ CX, AX JG error_match_off_too_big CMPQ CX, 56(SP) JG error_match_off_too_big // Copy match from history MOVQ CX, AX SUBQ R12, AX JLS copy_match MOVQ 48(SP), R14 SUBQ AX, R14 CMPQ R13, AX JG copy_all_from_history MOVQ R13, AX SUBQ $0x10, AX JB copy_4_small copy_4_loop: MOVUPS (R14), X0 MOVUPS X0, (R10) ADDQ $0x10, R14 ADDQ $0x10, R10 SUBQ $0x10, AX JAE copy_4_loop LEAQ 16(R14)(AX*1), R14 LEAQ 16(R10)(AX*1), R10 MOVUPS -16(R14), X0 MOVUPS X0, -16(R10) JMP copy_4_end copy_4_small: CMPQ R13, $0x03 JE copy_4_move_3 CMPQ R13, $0x08 JB copy_4_move_4through7 JMP copy_4_move_8through16 copy_4_move_3: MOVW (R14), AX MOVB 2(R14), CL MOVW AX, (R10) MOVB CL, 2(R10) ADDQ R13, R14 ADDQ R13, R10 JMP copy_4_end copy_4_move_4through7: MOVL (R14), AX MOVL -4(R14)(R13*1), CX MOVL AX, (R10) MOVL CX, -4(R10)(R13*1) ADDQ R13, R14 ADDQ R13, R10 JMP copy_4_end copy_4_move_8through16: MOVQ (R14), AX MOVQ -8(R14)(R13*1), CX MOVQ AX, (R10) MOVQ CX, -8(R10)(R13*1) ADDQ R13, R14 ADDQ R13, R10 copy_4_end: ADDQ R13, R12 JMP handle_loop JMP loop_finished copy_all_from_history: MOVQ AX, R15 SUBQ $0x10, R15 JB copy_5_small copy_5_loop: MOVUPS (R14), X0 MOVUPS X0, (R10) ADDQ $0x10, R14 ADDQ $0x10, R10 SUBQ $0x10, R15 JAE copy_5_loop LEAQ 16(R14)(R15*1), R14 LEAQ 16(R10)(R15*1), R10 MOVUPS -16(R14), X0 MOVUPS X0, -16(R10) JMP copy_5_end copy_5_small: CMPQ AX, $0x03 JE copy_5_move_3 JB copy_5_move_1or2 CMPQ AX, $0x08 JB copy_5_move_4through7 JMP copy_5_move_8through16 copy_5_move_1or2: MOVB (R14), R15 MOVB -1(R14)(AX*1), BP MOVB R15, (R10) MOVB BP, -1(R10)(AX*1) ADDQ AX, R14 ADDQ AX, R10 JMP copy_5_end copy_5_move_3: MOVW (R14), R15 MOVB 2(R14), BP MOVW R15, (R10) MOVB BP, 2(R10) ADDQ AX, R14 ADDQ AX, R10 JMP copy_5_end copy_5_move_4through7: MOVL (R14), R15 MOVL -4(R14)(AX*1), BP MOVL R15, (R10) MOVL BP, -4(R10)(AX*1) ADDQ AX, R14 ADDQ AX, R10 JMP copy_5_end copy_5_move_8through16: MOVQ (R14), R15 MOVQ -8(R14)(AX*1), BP MOVQ R15, (R10) MOVQ BP, -8(R10)(AX*1) ADDQ AX, R14 ADDQ AX, R10 copy_5_end: ADDQ AX, R12 SUBQ AX, R13 // Copy match from the current buffer copy_match: MOVQ R10, AX SUBQ CX, AX // ml <= mo CMPQ R13, CX JA copy_overlapping_match // Copy non-overlapping match ADDQ R13, R12 MOVQ R10, CX ADDQ R13, R10 copy_2: MOVUPS (AX), X0 MOVUPS X0, (CX) ADDQ $0x10, AX ADDQ $0x10, CX SUBQ $0x10, R13 JHI copy_2 JMP handle_loop // Copy overlapping match copy_overlapping_match: ADDQ R13, R12 copy_slow_3: MOVB (AX), CL MOVB CL, (R10) INCQ AX INCQ R10 DECQ R13 JNZ copy_slow_3 handle_loop: MOVQ ctx+16(FP), AX DECQ 96(AX) JNS sequenceDecs_decodeSync_amd64_main_loop loop_finished: MOVQ br+8(FP), AX MOVQ DX, 24(AX) MOVB BL, 32(AX) MOVQ SI, 8(AX) // Update the context MOVQ ctx+16(FP), AX MOVQ R12, 136(AX) MOVQ 144(AX), CX SUBQ CX, R11 MOVQ R11, 168(AX) // Return success MOVQ $0x00000000, ret+24(FP) RET // Return with match length error sequenceDecs_decodeSync_amd64_error_match_len_ofs_mismatch: MOVQ 16(SP), AX MOVQ ctx+16(FP), CX MOVQ AX, 216(CX) MOVQ $0x00000001, ret+24(FP) RET // Return with match too long error sequenceDecs_decodeSync_amd64_error_match_len_too_big: MOVQ ctx+16(FP), AX MOVQ 16(SP), CX MOVQ CX, 216(AX) MOVQ $0x00000002, ret+24(FP) RET // Return with match offset too long error error_match_off_too_big: MOVQ ctx+16(FP), AX MOVQ 8(SP), CX MOVQ CX, 224(AX) MOVQ R12, 136(AX) MOVQ $0x00000003, ret+24(FP) RET // Return with not enough literals error error_not_enough_literals: MOVQ ctx+16(FP), AX MOVQ 24(SP), CX MOVQ CX, 208(AX) MOVQ $0x00000004, ret+24(FP) RET // Return with overread error error_overread: MOVQ $0x00000006, ret+24(FP) RET // Return with not enough output space error error_not_enough_space: MOVQ ctx+16(FP), AX MOVQ 24(SP), CX MOVQ CX, 208(AX) MOVQ 16(SP), CX MOVQ CX, 216(AX) MOVQ R12, 136(AX) MOVQ $0x00000005, ret+24(FP) RET // func sequenceDecs_decodeSync_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int // Requires: BMI, BMI2, CMOV, SSE TEXT ·sequenceDecs_decodeSync_bmi2(SB), $64-32 MOVQ br+8(FP), BX MOVQ 24(BX), AX MOVBQZX 32(BX), DX MOVQ (BX), CX MOVQ 8(BX), BX ADDQ BX, CX MOVQ CX, (SP) MOVQ ctx+16(FP), CX MOVQ 72(CX), SI MOVQ 80(CX), DI MOVQ 88(CX), R8 XORQ R9, R9 MOVQ R9, 8(SP) MOVQ R9, 16(SP) MOVQ R9, 24(SP) MOVQ 112(CX), R9 MOVQ 128(CX), R10 MOVQ R10, 32(SP) MOVQ 144(CX), R10 MOVQ 136(CX), R11 MOVQ 200(CX), R12 MOVQ R12, 56(SP) MOVQ 176(CX), R12 MOVQ R12, 48(SP) MOVQ 184(CX), CX MOVQ CX, 40(SP) MOVQ 40(SP), CX ADDQ CX, 48(SP) // Calculate poiter to s.out[cap(s.out)] (a past-end pointer) ADDQ R9, 32(SP) // outBase += outPosition ADDQ R11, R9 sequenceDecs_decodeSync_bmi2_main_loop: MOVQ (SP), R12 // Fill bitreader to have enough for the offset and match length. CMPQ BX, $0x08 JL sequenceDecs_decodeSync_bmi2_fill_byte_by_byte MOVQ DX, CX SHRQ $0x03, CX SUBQ CX, R12 MOVQ (R12), AX SUBQ CX, BX ANDQ $0x07, DX JMP sequenceDecs_decodeSync_bmi2_fill_end sequenceDecs_decodeSync_bmi2_fill_byte_by_byte: CMPQ BX, $0x00 JLE sequenceDecs_decodeSync_bmi2_fill_check_overread CMPQ DX, $0x07 JLE sequenceDecs_decodeSync_bmi2_fill_end SHLQ $0x08, AX SUBQ $0x01, R12 SUBQ $0x01, BX SUBQ $0x08, DX MOVBQZX (R12), CX ORQ CX, AX JMP sequenceDecs_decodeSync_bmi2_fill_byte_by_byte sequenceDecs_decodeSync_bmi2_fill_check_overread: CMPQ DX, $0x40 JA error_overread sequenceDecs_decodeSync_bmi2_fill_end: // Update offset MOVQ $0x00000808, CX BEXTRQ CX, R8, R13 MOVQ AX, R14 LEAQ (DX)(R13*1), CX ROLQ CL, R14 BZHIQ R13, R14, R14 MOVQ CX, DX MOVQ R8, CX SHRQ $0x20, CX ADDQ R14, CX MOVQ CX, 8(SP) // Update match length MOVQ $0x00000808, CX BEXTRQ CX, DI, R13 MOVQ AX, R14 LEAQ (DX)(R13*1), CX ROLQ CL, R14 BZHIQ R13, R14, R14 MOVQ CX, DX MOVQ DI, CX SHRQ $0x20, CX ADDQ R14, CX MOVQ CX, 16(SP) // Fill bitreader to have enough for the remaining CMPQ BX, $0x08 JL sequenceDecs_decodeSync_bmi2_fill_2_byte_by_byte MOVQ DX, CX SHRQ $0x03, CX SUBQ CX, R12 MOVQ (R12), AX SUBQ CX, BX ANDQ $0x07, DX JMP sequenceDecs_decodeSync_bmi2_fill_2_end sequenceDecs_decodeSync_bmi2_fill_2_byte_by_byte: CMPQ BX, $0x00 JLE sequenceDecs_decodeSync_bmi2_fill_2_check_overread CMPQ DX, $0x07 JLE sequenceDecs_decodeSync_bmi2_fill_2_end SHLQ $0x08, AX SUBQ $0x01, R12 SUBQ $0x01, BX SUBQ $0x08, DX MOVBQZX (R12), CX ORQ CX, AX JMP sequenceDecs_decodeSync_bmi2_fill_2_byte_by_byte sequenceDecs_decodeSync_bmi2_fill_2_check_overread: CMPQ DX, $0x40 JA error_overread sequenceDecs_decodeSync_bmi2_fill_2_end: // Update literal length MOVQ $0x00000808, CX BEXTRQ CX, SI, R13 MOVQ AX, R14 LEAQ (DX)(R13*1), CX ROLQ CL, R14 BZHIQ R13, R14, R14 MOVQ CX, DX MOVQ SI, CX SHRQ $0x20, CX ADDQ R14, CX MOVQ CX, 24(SP) // Fill bitreader for state updates MOVQ R12, (SP) MOVQ $0x00000808, CX BEXTRQ CX, R8, R12 MOVQ ctx+16(FP), CX CMPQ 96(CX), $0x00 JZ sequenceDecs_decodeSync_bmi2_skip_update LEAQ (SI)(DI*1), R13 ADDQ R8, R13 MOVBQZX R13, R13 LEAQ (DX)(R13*1), CX MOVQ AX, R14 MOVQ CX, DX ROLQ CL, R14 BZHIQ R13, R14, R14 // Update Offset State BZHIQ R8, R14, CX SHRXQ R8, R14, R14 MOVQ $0x00001010, R13 BEXTRQ R13, R8, R8 ADDQ CX, R8 // Load ctx.ofTable MOVQ ctx+16(FP), CX MOVQ 48(CX), CX MOVQ (CX)(R8*8), R8 // Update Match Length State BZHIQ DI, R14, CX SHRXQ DI, R14, R14 MOVQ $0x00001010, R13 BEXTRQ R13, DI, DI ADDQ CX, DI // Load ctx.mlTable MOVQ ctx+16(FP), CX MOVQ 24(CX), CX MOVQ (CX)(DI*8), DI // Update Literal Length State BZHIQ SI, R14, CX MOVQ $0x00001010, R13 BEXTRQ R13, SI, SI ADDQ CX, SI // Load ctx.llTable MOVQ ctx+16(FP), CX MOVQ (CX), CX MOVQ (CX)(SI*8), SI sequenceDecs_decodeSync_bmi2_skip_update: // Adjust offset MOVQ s+0(FP), CX MOVQ 8(SP), R13 CMPQ R12, $0x01 JBE sequenceDecs_decodeSync_bmi2_adjust_offsetB_1_or_0 MOVUPS 144(CX), X0 MOVQ R13, 144(CX) MOVUPS X0, 152(CX) JMP sequenceDecs_decodeSync_bmi2_after_adjust sequenceDecs_decodeSync_bmi2_adjust_offsetB_1_or_0: CMPQ 24(SP), $0x00000000 JNE sequenceDecs_decodeSync_bmi2_adjust_offset_maybezero INCQ R13 JMP sequenceDecs_decodeSync_bmi2_adjust_offset_nonzero sequenceDecs_decodeSync_bmi2_adjust_offset_maybezero: TESTQ R13, R13 JNZ sequenceDecs_decodeSync_bmi2_adjust_offset_nonzero MOVQ 144(CX), R13 JMP sequenceDecs_decodeSync_bmi2_after_adjust sequenceDecs_decodeSync_bmi2_adjust_offset_nonzero: MOVQ R13, R12 XORQ R14, R14 MOVQ $-1, R15 CMPQ R13, $0x03 CMOVQEQ R14, R12 CMOVQEQ R15, R14 ADDQ 144(CX)(R12*8), R14 JNZ sequenceDecs_decodeSync_bmi2_adjust_temp_valid MOVQ $0x00000001, R14 sequenceDecs_decodeSync_bmi2_adjust_temp_valid: CMPQ R13, $0x01 JZ sequenceDecs_decodeSync_bmi2_adjust_skip MOVQ 152(CX), R12 MOVQ R12, 160(CX) sequenceDecs_decodeSync_bmi2_adjust_skip: MOVQ 144(CX), R12 MOVQ R12, 152(CX) MOVQ R14, 144(CX) MOVQ R14, R13 sequenceDecs_decodeSync_bmi2_after_adjust: MOVQ R13, 8(SP) // Check values MOVQ 16(SP), CX MOVQ 24(SP), R12 LEAQ (CX)(R12*1), R14 MOVQ s+0(FP), R15 ADDQ R14, 256(R15) MOVQ ctx+16(FP), R14 SUBQ R12, 104(R14) JS error_not_enough_literals CMPQ CX, $0x00020002 JA sequenceDecs_decodeSync_bmi2_error_match_len_too_big TESTQ R13, R13 JNZ sequenceDecs_decodeSync_bmi2_match_len_ofs_ok TESTQ CX, CX JNZ sequenceDecs_decodeSync_bmi2_error_match_len_ofs_mismatch sequenceDecs_decodeSync_bmi2_match_len_ofs_ok: MOVQ 24(SP), CX MOVQ 8(SP), R12 MOVQ 16(SP), R13 // Check if we have enough space in s.out LEAQ (CX)(R13*1), R14 ADDQ R9, R14 CMPQ R14, 32(SP) JA error_not_enough_space // Copy literals TESTQ CX, CX JZ check_offset XORQ R14, R14 copy_1: MOVUPS (R10)(R14*1), X0 MOVUPS X0, (R9)(R14*1) ADDQ $0x10, R14 CMPQ R14, CX JB copy_1 ADDQ CX, R10 ADDQ CX, R9 ADDQ CX, R11 // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize) check_offset: MOVQ R11, CX ADDQ 40(SP), CX CMPQ R12, CX JG error_match_off_too_big CMPQ R12, 56(SP) JG error_match_off_too_big // Copy match from history MOVQ R12, CX SUBQ R11, CX JLS copy_match MOVQ 48(SP), R14 SUBQ CX, R14 CMPQ R13, CX JG copy_all_from_history MOVQ R13, CX SUBQ $0x10, CX JB copy_4_small copy_4_loop: MOVUPS (R14), X0 MOVUPS X0, (R9) ADDQ $0x10, R14 ADDQ $0x10, R9 SUBQ $0x10, CX JAE copy_4_loop LEAQ 16(R14)(CX*1), R14 LEAQ 16(R9)(CX*1), R9 MOVUPS -16(R14), X0 MOVUPS X0, -16(R9) JMP copy_4_end copy_4_small: CMPQ R13, $0x03 JE copy_4_move_3 CMPQ R13, $0x08 JB copy_4_move_4through7 JMP copy_4_move_8through16 copy_4_move_3: MOVW (R14), CX MOVB 2(R14), R12 MOVW CX, (R9) MOVB R12, 2(R9) ADDQ R13, R14 ADDQ R13, R9 JMP copy_4_end copy_4_move_4through7: MOVL (R14), CX MOVL -4(R14)(R13*1), R12 MOVL CX, (R9) MOVL R12, -4(R9)(R13*1) ADDQ R13, R14 ADDQ R13, R9 JMP copy_4_end copy_4_move_8through16: MOVQ (R14), CX MOVQ -8(R14)(R13*1), R12 MOVQ CX, (R9) MOVQ R12, -8(R9)(R13*1) ADDQ R13, R14 ADDQ R13, R9 copy_4_end: ADDQ R13, R11 JMP handle_loop JMP loop_finished copy_all_from_history: MOVQ CX, R15 SUBQ $0x10, R15 JB copy_5_small copy_5_loop: MOVUPS (R14), X0 MOVUPS X0, (R9) ADDQ $0x10, R14 ADDQ $0x10, R9 SUBQ $0x10, R15 JAE copy_5_loop LEAQ 16(R14)(R15*1), R14 LEAQ 16(R9)(R15*1), R9 MOVUPS -16(R14), X0 MOVUPS X0, -16(R9) JMP copy_5_end copy_5_small: CMPQ CX, $0x03 JE copy_5_move_3 JB copy_5_move_1or2 CMPQ CX, $0x08 JB copy_5_move_4through7 JMP copy_5_move_8through16 copy_5_move_1or2: MOVB (R14), R15 MOVB -1(R14)(CX*1), BP MOVB R15, (R9) MOVB BP, -1(R9)(CX*1) ADDQ CX, R14 ADDQ CX, R9 JMP copy_5_end copy_5_move_3: MOVW (R14), R15 MOVB 2(R14), BP MOVW R15, (R9) MOVB BP, 2(R9) ADDQ CX, R14 ADDQ CX, R9 JMP copy_5_end copy_5_move_4through7: MOVL (R14), R15 MOVL -4(R14)(CX*1), BP MOVL R15, (R9) MOVL BP, -4(R9)(CX*1) ADDQ CX, R14 ADDQ CX, R9 JMP copy_5_end copy_5_move_8through16: MOVQ (R14), R15 MOVQ -8(R14)(CX*1), BP MOVQ R15, (R9) MOVQ BP, -8(R9)(CX*1) ADDQ CX, R14 ADDQ CX, R9 copy_5_end: ADDQ CX, R11 SUBQ CX, R13 // Copy match from the current buffer copy_match: MOVQ R9, CX SUBQ R12, CX // ml <= mo CMPQ R13, R12 JA copy_overlapping_match // Copy non-overlapping match ADDQ R13, R11 MOVQ R9, R12 ADDQ R13, R9 copy_2: MOVUPS (CX), X0 MOVUPS X0, (R12) ADDQ $0x10, CX ADDQ $0x10, R12 SUBQ $0x10, R13 JHI copy_2 JMP handle_loop // Copy overlapping match copy_overlapping_match: ADDQ R13, R11 copy_slow_3: MOVB (CX), R12 MOVB R12, (R9) INCQ CX INCQ R9 DECQ R13 JNZ copy_slow_3 handle_loop: MOVQ ctx+16(FP), CX DECQ 96(CX) JNS sequenceDecs_decodeSync_bmi2_main_loop loop_finished: MOVQ br+8(FP), CX MOVQ AX, 24(CX) MOVB DL, 32(CX) MOVQ BX, 8(CX) // Update the context MOVQ ctx+16(FP), AX MOVQ R11, 136(AX) MOVQ 144(AX), CX SUBQ CX, R10 MOVQ R10, 168(AX) // Return success MOVQ $0x00000000, ret+24(FP) RET // Return with match length error sequenceDecs_decodeSync_bmi2_error_match_len_ofs_mismatch: MOVQ 16(SP), AX MOVQ ctx+16(FP), CX MOVQ AX, 216(CX) MOVQ $0x00000001, ret+24(FP) RET // Return with match too long error sequenceDecs_decodeSync_bmi2_error_match_len_too_big: MOVQ ctx+16(FP), AX MOVQ 16(SP), CX MOVQ CX, 216(AX) MOVQ $0x00000002, ret+24(FP) RET // Return with match offset too long error error_match_off_too_big: MOVQ ctx+16(FP), AX MOVQ 8(SP), CX MOVQ CX, 224(AX) MOVQ R11, 136(AX) MOVQ $0x00000003, ret+24(FP) RET // Return with not enough literals error error_not_enough_literals: MOVQ ctx+16(FP), AX MOVQ 24(SP), CX MOVQ CX, 208(AX) MOVQ $0x00000004, ret+24(FP) RET // Return with overread error error_overread: MOVQ $0x00000006, ret+24(FP) RET // Return with not enough output space error error_not_enough_space: MOVQ ctx+16(FP), AX MOVQ 24(SP), CX MOVQ CX, 208(AX) MOVQ 16(SP), CX MOVQ CX, 216(AX) MOVQ R11, 136(AX) MOVQ $0x00000005, ret+24(FP) RET // func sequenceDecs_decodeSync_safe_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int // Requires: CMOV, SSE TEXT ·sequenceDecs_decodeSync_safe_amd64(SB), $64-32 MOVQ br+8(FP), CX MOVQ 24(CX), DX MOVBQZX 32(CX), BX MOVQ (CX), AX MOVQ 8(CX), SI ADDQ SI, AX MOVQ AX, (SP) MOVQ ctx+16(FP), AX MOVQ 72(AX), DI MOVQ 80(AX), R8 MOVQ 88(AX), R9 XORQ CX, CX MOVQ CX, 8(SP) MOVQ CX, 16(SP) MOVQ CX, 24(SP) MOVQ 112(AX), R10 MOVQ 128(AX), CX MOVQ CX, 32(SP) MOVQ 144(AX), R11 MOVQ 136(AX), R12 MOVQ 200(AX), CX MOVQ CX, 56(SP) MOVQ 176(AX), CX MOVQ CX, 48(SP) MOVQ 184(AX), AX MOVQ AX, 40(SP) MOVQ 40(SP), AX ADDQ AX, 48(SP) // Calculate poiter to s.out[cap(s.out)] (a past-end pointer) ADDQ R10, 32(SP) // outBase += outPosition ADDQ R12, R10 sequenceDecs_decodeSync_safe_amd64_main_loop: MOVQ (SP), R13 // Fill bitreader to have enough for the offset and match length. CMPQ SI, $0x08 JL sequenceDecs_decodeSync_safe_amd64_fill_byte_by_byte MOVQ BX, AX SHRQ $0x03, AX SUBQ AX, R13 MOVQ (R13), DX SUBQ AX, SI ANDQ $0x07, BX JMP sequenceDecs_decodeSync_safe_amd64_fill_end sequenceDecs_decodeSync_safe_amd64_fill_byte_by_byte: CMPQ SI, $0x00 JLE sequenceDecs_decodeSync_safe_amd64_fill_check_overread CMPQ BX, $0x07 JLE sequenceDecs_decodeSync_safe_amd64_fill_end SHLQ $0x08, DX SUBQ $0x01, R13 SUBQ $0x01, SI SUBQ $0x08, BX MOVBQZX (R13), AX ORQ AX, DX JMP sequenceDecs_decodeSync_safe_amd64_fill_byte_by_byte sequenceDecs_decodeSync_safe_amd64_fill_check_overread: CMPQ BX, $0x40 JA error_overread sequenceDecs_decodeSync_safe_amd64_fill_end: // Update offset MOVQ R9, AX MOVQ BX, CX MOVQ DX, R14 SHLQ CL, R14 MOVB AH, CL SHRQ $0x20, AX TESTQ CX, CX JZ sequenceDecs_decodeSync_safe_amd64_of_update_zero ADDQ CX, BX CMPQ BX, $0x40 JA sequenceDecs_decodeSync_safe_amd64_of_update_zero CMPQ CX, $0x40 JAE sequenceDecs_decodeSync_safe_amd64_of_update_zero NEGQ CX SHRQ CL, R14 ADDQ R14, AX sequenceDecs_decodeSync_safe_amd64_of_update_zero: MOVQ AX, 8(SP) // Update match length MOVQ R8, AX MOVQ BX, CX MOVQ DX, R14 SHLQ CL, R14 MOVB AH, CL SHRQ $0x20, AX TESTQ CX, CX JZ sequenceDecs_decodeSync_safe_amd64_ml_update_zero ADDQ CX, BX CMPQ BX, $0x40 JA sequenceDecs_decodeSync_safe_amd64_ml_update_zero CMPQ CX, $0x40 JAE sequenceDecs_decodeSync_safe_amd64_ml_update_zero NEGQ CX SHRQ CL, R14 ADDQ R14, AX sequenceDecs_decodeSync_safe_amd64_ml_update_zero: MOVQ AX, 16(SP) // Fill bitreader to have enough for the remaining CMPQ SI, $0x08 JL sequenceDecs_decodeSync_safe_amd64_fill_2_byte_by_byte MOVQ BX, AX SHRQ $0x03, AX SUBQ AX, R13 MOVQ (R13), DX SUBQ AX, SI ANDQ $0x07, BX JMP sequenceDecs_decodeSync_safe_amd64_fill_2_end sequenceDecs_decodeSync_safe_amd64_fill_2_byte_by_byte: CMPQ SI, $0x00 JLE sequenceDecs_decodeSync_safe_amd64_fill_2_check_overread CMPQ BX, $0x07 JLE sequenceDecs_decodeSync_safe_amd64_fill_2_end SHLQ $0x08, DX SUBQ $0x01, R13 SUBQ $0x01, SI SUBQ $0x08, BX MOVBQZX (R13), AX ORQ AX, DX JMP sequenceDecs_decodeSync_safe_amd64_fill_2_byte_by_byte sequenceDecs_decodeSync_safe_amd64_fill_2_check_overread: CMPQ BX, $0x40 JA error_overread sequenceDecs_decodeSync_safe_amd64_fill_2_end: // Update literal length MOVQ DI, AX MOVQ BX, CX MOVQ DX, R14 SHLQ CL, R14 MOVB AH, CL SHRQ $0x20, AX TESTQ CX, CX JZ sequenceDecs_decodeSync_safe_amd64_ll_update_zero ADDQ CX, BX CMPQ BX, $0x40 JA sequenceDecs_decodeSync_safe_amd64_ll_update_zero CMPQ CX, $0x40 JAE sequenceDecs_decodeSync_safe_amd64_ll_update_zero NEGQ CX SHRQ CL, R14 ADDQ R14, AX sequenceDecs_decodeSync_safe_amd64_ll_update_zero: MOVQ AX, 24(SP) // Fill bitreader for state updates MOVQ R13, (SP) MOVQ R9, AX SHRQ $0x08, AX MOVBQZX AL, AX MOVQ ctx+16(FP), CX CMPQ 96(CX), $0x00 JZ sequenceDecs_decodeSync_safe_amd64_skip_update // Update Literal Length State MOVBQZX DI, R13 SHRQ $0x10, DI MOVWQZX DI, DI LEAQ (BX)(R13*1), CX MOVQ DX, R14 MOVQ CX, BX ROLQ CL, R14 MOVL $0x00000001, R15 MOVB R13, CL SHLL CL, R15 DECL R15 ANDQ R15, R14 ADDQ R14, DI // Load ctx.llTable MOVQ ctx+16(FP), CX MOVQ (CX), CX MOVQ (CX)(DI*8), DI // Update Match Length State MOVBQZX R8, R13 SHRQ $0x10, R8 MOVWQZX R8, R8 LEAQ (BX)(R13*1), CX MOVQ DX, R14 MOVQ CX, BX ROLQ CL, R14 MOVL $0x00000001, R15 MOVB R13, CL SHLL CL, R15 DECL R15 ANDQ R15, R14 ADDQ R14, R8 // Load ctx.mlTable MOVQ ctx+16(FP), CX MOVQ 24(CX), CX MOVQ (CX)(R8*8), R8 // Update Offset State MOVBQZX R9, R13 SHRQ $0x10, R9 MOVWQZX R9, R9 LEAQ (BX)(R13*1), CX MOVQ DX, R14 MOVQ CX, BX ROLQ CL, R14 MOVL $0x00000001, R15 MOVB R13, CL SHLL CL, R15 DECL R15 ANDQ R15, R14 ADDQ R14, R9 // Load ctx.ofTable MOVQ ctx+16(FP), CX MOVQ 48(CX), CX MOVQ (CX)(R9*8), R9 sequenceDecs_decodeSync_safe_amd64_skip_update: // Adjust offset MOVQ s+0(FP), CX MOVQ 8(SP), R13 CMPQ AX, $0x01 JBE sequenceDecs_decodeSync_safe_amd64_adjust_offsetB_1_or_0 MOVUPS 144(CX), X0 MOVQ R13, 144(CX) MOVUPS X0, 152(CX) JMP sequenceDecs_decodeSync_safe_amd64_after_adjust sequenceDecs_decodeSync_safe_amd64_adjust_offsetB_1_or_0: CMPQ 24(SP), $0x00000000 JNE sequenceDecs_decodeSync_safe_amd64_adjust_offset_maybezero INCQ R13 JMP sequenceDecs_decodeSync_safe_amd64_adjust_offset_nonzero sequenceDecs_decodeSync_safe_amd64_adjust_offset_maybezero: TESTQ R13, R13 JNZ sequenceDecs_decodeSync_safe_amd64_adjust_offset_nonzero MOVQ 144(CX), R13 JMP sequenceDecs_decodeSync_safe_amd64_after_adjust sequenceDecs_decodeSync_safe_amd64_adjust_offset_nonzero: MOVQ R13, AX XORQ R14, R14 MOVQ $-1, R15 CMPQ R13, $0x03 CMOVQEQ R14, AX CMOVQEQ R15, R14 ADDQ 144(CX)(AX*8), R14 JNZ sequenceDecs_decodeSync_safe_amd64_adjust_temp_valid MOVQ $0x00000001, R14 sequenceDecs_decodeSync_safe_amd64_adjust_temp_valid: CMPQ R13, $0x01 JZ sequenceDecs_decodeSync_safe_amd64_adjust_skip MOVQ 152(CX), AX MOVQ AX, 160(CX) sequenceDecs_decodeSync_safe_amd64_adjust_skip: MOVQ 144(CX), AX MOVQ AX, 152(CX) MOVQ R14, 144(CX) MOVQ R14, R13 sequenceDecs_decodeSync_safe_amd64_after_adjust: MOVQ R13, 8(SP) // Check values MOVQ 16(SP), AX MOVQ 24(SP), CX LEAQ (AX)(CX*1), R14 MOVQ s+0(FP), R15 ADDQ R14, 256(R15) MOVQ ctx+16(FP), R14 SUBQ CX, 104(R14) JS error_not_enough_literals CMPQ AX, $0x00020002 JA sequenceDecs_decodeSync_safe_amd64_error_match_len_too_big TESTQ R13, R13 JNZ sequenceDecs_decodeSync_safe_amd64_match_len_ofs_ok TESTQ AX, AX JNZ sequenceDecs_decodeSync_safe_amd64_error_match_len_ofs_mismatch sequenceDecs_decodeSync_safe_amd64_match_len_ofs_ok: MOVQ 24(SP), AX MOVQ 8(SP), CX MOVQ 16(SP), R13 // Check if we have enough space in s.out LEAQ (AX)(R13*1), R14 ADDQ R10, R14 CMPQ R14, 32(SP) JA error_not_enough_space // Copy literals TESTQ AX, AX JZ check_offset MOVQ AX, R14 SUBQ $0x10, R14 JB copy_1_small copy_1_loop: MOVUPS (R11), X0 MOVUPS X0, (R10) ADDQ $0x10, R11 ADDQ $0x10, R10 SUBQ $0x10, R14 JAE copy_1_loop LEAQ 16(R11)(R14*1), R11 LEAQ 16(R10)(R14*1), R10 MOVUPS -16(R11), X0 MOVUPS X0, -16(R10) JMP copy_1_end copy_1_small: CMPQ AX, $0x03 JE copy_1_move_3 JB copy_1_move_1or2 CMPQ AX, $0x08 JB copy_1_move_4through7 JMP copy_1_move_8through16 copy_1_move_1or2: MOVB (R11), R14 MOVB -1(R11)(AX*1), R15 MOVB R14, (R10) MOVB R15, -1(R10)(AX*1) ADDQ AX, R11 ADDQ AX, R10 JMP copy_1_end copy_1_move_3: MOVW (R11), R14 MOVB 2(R11), R15 MOVW R14, (R10) MOVB R15, 2(R10) ADDQ AX, R11 ADDQ AX, R10 JMP copy_1_end copy_1_move_4through7: MOVL (R11), R14 MOVL -4(R11)(AX*1), R15 MOVL R14, (R10) MOVL R15, -4(R10)(AX*1) ADDQ AX, R11 ADDQ AX, R10 JMP copy_1_end copy_1_move_8through16: MOVQ (R11), R14 MOVQ -8(R11)(AX*1), R15 MOVQ R14, (R10) MOVQ R15, -8(R10)(AX*1) ADDQ AX, R11 ADDQ AX, R10 copy_1_end: ADDQ AX, R12 // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize) check_offset: MOVQ R12, AX ADDQ 40(SP), AX CMPQ CX, AX JG error_match_off_too_big CMPQ CX, 56(SP) JG error_match_off_too_big // Copy match from history MOVQ CX, AX SUBQ R12, AX JLS copy_match MOVQ 48(SP), R14 SUBQ AX, R14 CMPQ R13, AX JG copy_all_from_history MOVQ R13, AX SUBQ $0x10, AX JB copy_4_small copy_4_loop: MOVUPS (R14), X0 MOVUPS X0, (R10) ADDQ $0x10, R14 ADDQ $0x10, R10 SUBQ $0x10, AX JAE copy_4_loop LEAQ 16(R14)(AX*1), R14 LEAQ 16(R10)(AX*1), R10 MOVUPS -16(R14), X0 MOVUPS X0, -16(R10) JMP copy_4_end copy_4_small: CMPQ R13, $0x03 JE copy_4_move_3 CMPQ R13, $0x08 JB copy_4_move_4through7 JMP copy_4_move_8through16 copy_4_move_3: MOVW (R14), AX MOVB 2(R14), CL MOVW AX, (R10) MOVB CL, 2(R10) ADDQ R13, R14 ADDQ R13, R10 JMP copy_4_end copy_4_move_4through7: MOVL (R14), AX MOVL -4(R14)(R13*1), CX MOVL AX, (R10) MOVL CX, -4(R10)(R13*1) ADDQ R13, R14 ADDQ R13, R10 JMP copy_4_end copy_4_move_8through16: MOVQ (R14), AX MOVQ -8(R14)(R13*1), CX MOVQ AX, (R10) MOVQ CX, -8(R10)(R13*1) ADDQ R13, R14 ADDQ R13, R10 copy_4_end: ADDQ R13, R12 JMP handle_loop JMP loop_finished copy_all_from_history: MOVQ AX, R15 SUBQ $0x10, R15 JB copy_5_small copy_5_loop: MOVUPS (R14), X0 MOVUPS X0, (R10) ADDQ $0x10, R14 ADDQ $0x10, R10 SUBQ $0x10, R15 JAE copy_5_loop LEAQ 16(R14)(R15*1), R14 LEAQ 16(R10)(R15*1), R10 MOVUPS -16(R14), X0 MOVUPS X0, -16(R10) JMP copy_5_end copy_5_small: CMPQ AX, $0x03 JE copy_5_move_3 JB copy_5_move_1or2 CMPQ AX, $0x08 JB copy_5_move_4through7 JMP copy_5_move_8through16 copy_5_move_1or2: MOVB (R14), R15 MOVB -1(R14)(AX*1), BP MOVB R15, (R10) MOVB BP, -1(R10)(AX*1) ADDQ AX, R14 ADDQ AX, R10 JMP copy_5_end copy_5_move_3: MOVW (R14), R15 MOVB 2(R14), BP MOVW R15, (R10) MOVB BP, 2(R10) ADDQ AX, R14 ADDQ AX, R10 JMP copy_5_end copy_5_move_4through7: MOVL (R14), R15 MOVL -4(R14)(AX*1), BP MOVL R15, (R10) MOVL BP, -4(R10)(AX*1) ADDQ AX, R14 ADDQ AX, R10 JMP copy_5_end copy_5_move_8through16: MOVQ (R14), R15 MOVQ -8(R14)(AX*1), BP MOVQ R15, (R10) MOVQ BP, -8(R10)(AX*1) ADDQ AX, R14 ADDQ AX, R10 copy_5_end: ADDQ AX, R12 SUBQ AX, R13 // Copy match from the current buffer copy_match: MOVQ R10, AX SUBQ CX, AX // ml <= mo CMPQ R13, CX JA copy_overlapping_match // Copy non-overlapping match ADDQ R13, R12 MOVQ R13, CX SUBQ $0x10, CX JB copy_2_small copy_2_loop: MOVUPS (AX), X0 MOVUPS X0, (R10) ADDQ $0x10, AX ADDQ $0x10, R10 SUBQ $0x10, CX JAE copy_2_loop LEAQ 16(AX)(CX*1), AX LEAQ 16(R10)(CX*1), R10 MOVUPS -16(AX), X0 MOVUPS X0, -16(R10) JMP copy_2_end copy_2_small: CMPQ R13, $0x03 JE copy_2_move_3 JB copy_2_move_1or2 CMPQ R13, $0x08 JB copy_2_move_4through7 JMP copy_2_move_8through16 copy_2_move_1or2: MOVB (AX), CL MOVB -1(AX)(R13*1), R14 MOVB CL, (R10) MOVB R14, -1(R10)(R13*1) ADDQ R13, AX ADDQ R13, R10 JMP copy_2_end copy_2_move_3: MOVW (AX), CX MOVB 2(AX), R14 MOVW CX, (R10) MOVB R14, 2(R10) ADDQ R13, AX ADDQ R13, R10 JMP copy_2_end copy_2_move_4through7: MOVL (AX), CX MOVL -4(AX)(R13*1), R14 MOVL CX, (R10) MOVL R14, -4(R10)(R13*1) ADDQ R13, AX ADDQ R13, R10 JMP copy_2_end copy_2_move_8through16: MOVQ (AX), CX MOVQ -8(AX)(R13*1), R14 MOVQ CX, (R10) MOVQ R14, -8(R10)(R13*1) ADDQ R13, AX ADDQ R13, R10 copy_2_end: JMP handle_loop // Copy overlapping match copy_overlapping_match: ADDQ R13, R12 copy_slow_3: MOVB (AX), CL MOVB CL, (R10) INCQ AX INCQ R10 DECQ R13 JNZ copy_slow_3 handle_loop: MOVQ ctx+16(FP), AX DECQ 96(AX) JNS sequenceDecs_decodeSync_safe_amd64_main_loop loop_finished: MOVQ br+8(FP), AX MOVQ DX, 24(AX) MOVB BL, 32(AX) MOVQ SI, 8(AX) // Update the context MOVQ ctx+16(FP), AX MOVQ R12, 136(AX) MOVQ 144(AX), CX SUBQ CX, R11 MOVQ R11, 168(AX) // Return success MOVQ $0x00000000, ret+24(FP) RET // Return with match length error sequenceDecs_decodeSync_safe_amd64_error_match_len_ofs_mismatch: MOVQ 16(SP), AX MOVQ ctx+16(FP), CX MOVQ AX, 216(CX) MOVQ $0x00000001, ret+24(FP) RET // Return with match too long error sequenceDecs_decodeSync_safe_amd64_error_match_len_too_big: MOVQ ctx+16(FP), AX MOVQ 16(SP), CX MOVQ CX, 216(AX) MOVQ $0x00000002, ret+24(FP) RET // Return with match offset too long error error_match_off_too_big: MOVQ ctx+16(FP), AX MOVQ 8(SP), CX MOVQ CX, 224(AX) MOVQ R12, 136(AX) MOVQ $0x00000003, ret+24(FP) RET // Return with not enough literals error error_not_enough_literals: MOVQ ctx+16(FP), AX MOVQ 24(SP), CX MOVQ CX, 208(AX) MOVQ $0x00000004, ret+24(FP) RET // Return with overread error error_overread: MOVQ $0x00000006, ret+24(FP) RET // Return with not enough output space error error_not_enough_space: MOVQ ctx+16(FP), AX MOVQ 24(SP), CX MOVQ CX, 208(AX) MOVQ 16(SP), CX MOVQ CX, 216(AX) MOVQ R12, 136(AX) MOVQ $0x00000005, ret+24(FP) RET // func sequenceDecs_decodeSync_safe_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int // Requires: BMI, BMI2, CMOV, SSE TEXT ·sequenceDecs_decodeSync_safe_bmi2(SB), $64-32 MOVQ br+8(FP), BX MOVQ 24(BX), AX MOVBQZX 32(BX), DX MOVQ (BX), CX MOVQ 8(BX), BX ADDQ BX, CX MOVQ CX, (SP) MOVQ ctx+16(FP), CX MOVQ 72(CX), SI MOVQ 80(CX), DI MOVQ 88(CX), R8 XORQ R9, R9 MOVQ R9, 8(SP) MOVQ R9, 16(SP) MOVQ R9, 24(SP) MOVQ 112(CX), R9 MOVQ 128(CX), R10 MOVQ R10, 32(SP) MOVQ 144(CX), R10 MOVQ 136(CX), R11 MOVQ 200(CX), R12 MOVQ R12, 56(SP) MOVQ 176(CX), R12 MOVQ R12, 48(SP) MOVQ 184(CX), CX MOVQ CX, 40(SP) MOVQ 40(SP), CX ADDQ CX, 48(SP) // Calculate poiter to s.out[cap(s.out)] (a past-end pointer) ADDQ R9, 32(SP) // outBase += outPosition ADDQ R11, R9 sequenceDecs_decodeSync_safe_bmi2_main_loop: MOVQ (SP), R12 // Fill bitreader to have enough for the offset and match length. CMPQ BX, $0x08 JL sequenceDecs_decodeSync_safe_bmi2_fill_byte_by_byte MOVQ DX, CX SHRQ $0x03, CX SUBQ CX, R12 MOVQ (R12), AX SUBQ CX, BX ANDQ $0x07, DX JMP sequenceDecs_decodeSync_safe_bmi2_fill_end sequenceDecs_decodeSync_safe_bmi2_fill_byte_by_byte: CMPQ BX, $0x00 JLE sequenceDecs_decodeSync_safe_bmi2_fill_check_overread CMPQ DX, $0x07 JLE sequenceDecs_decodeSync_safe_bmi2_fill_end SHLQ $0x08, AX SUBQ $0x01, R12 SUBQ $0x01, BX SUBQ $0x08, DX MOVBQZX (R12), CX ORQ CX, AX JMP sequenceDecs_decodeSync_safe_bmi2_fill_byte_by_byte sequenceDecs_decodeSync_safe_bmi2_fill_check_overread: CMPQ DX, $0x40 JA error_overread sequenceDecs_decodeSync_safe_bmi2_fill_end: // Update offset MOVQ $0x00000808, CX BEXTRQ CX, R8, R13 MOVQ AX, R14 LEAQ (DX)(R13*1), CX ROLQ CL, R14 BZHIQ R13, R14, R14 MOVQ CX, DX MOVQ R8, CX SHRQ $0x20, CX ADDQ R14, CX MOVQ CX, 8(SP) // Update match length MOVQ $0x00000808, CX BEXTRQ CX, DI, R13 MOVQ AX, R14 LEAQ (DX)(R13*1), CX ROLQ CL, R14 BZHIQ R13, R14, R14 MOVQ CX, DX MOVQ DI, CX SHRQ $0x20, CX ADDQ R14, CX MOVQ CX, 16(SP) // Fill bitreader to have enough for the remaining CMPQ BX, $0x08 JL sequenceDecs_decodeSync_safe_bmi2_fill_2_byte_by_byte MOVQ DX, CX SHRQ $0x03, CX SUBQ CX, R12 MOVQ (R12), AX SUBQ CX, BX ANDQ $0x07, DX JMP sequenceDecs_decodeSync_safe_bmi2_fill_2_end sequenceDecs_decodeSync_safe_bmi2_fill_2_byte_by_byte: CMPQ BX, $0x00 JLE sequenceDecs_decodeSync_safe_bmi2_fill_2_check_overread CMPQ DX, $0x07 JLE sequenceDecs_decodeSync_safe_bmi2_fill_2_end SHLQ $0x08, AX SUBQ $0x01, R12 SUBQ $0x01, BX SUBQ $0x08, DX MOVBQZX (R12), CX ORQ CX, AX JMP sequenceDecs_decodeSync_safe_bmi2_fill_2_byte_by_byte sequenceDecs_decodeSync_safe_bmi2_fill_2_check_overread: CMPQ DX, $0x40 JA error_overread sequenceDecs_decodeSync_safe_bmi2_fill_2_end: // Update literal length MOVQ $0x00000808, CX BEXTRQ CX, SI, R13 MOVQ AX, R14 LEAQ (DX)(R13*1), CX ROLQ CL, R14 BZHIQ R13, R14, R14 MOVQ CX, DX MOVQ SI, CX SHRQ $0x20, CX ADDQ R14, CX MOVQ CX, 24(SP) // Fill bitreader for state updates MOVQ R12, (SP) MOVQ $0x00000808, CX BEXTRQ CX, R8, R12 MOVQ ctx+16(FP), CX CMPQ 96(CX), $0x00 JZ sequenceDecs_decodeSync_safe_bmi2_skip_update LEAQ (SI)(DI*1), R13 ADDQ R8, R13 MOVBQZX R13, R13 LEAQ (DX)(R13*1), CX MOVQ AX, R14 MOVQ CX, DX ROLQ CL, R14 BZHIQ R13, R14, R14 // Update Offset State BZHIQ R8, R14, CX SHRXQ R8, R14, R14 MOVQ $0x00001010, R13 BEXTRQ R13, R8, R8 ADDQ CX, R8 // Load ctx.ofTable MOVQ ctx+16(FP), CX MOVQ 48(CX), CX MOVQ (CX)(R8*8), R8 // Update Match Length State BZHIQ DI, R14, CX SHRXQ DI, R14, R14 MOVQ $0x00001010, R13 BEXTRQ R13, DI, DI ADDQ CX, DI // Load ctx.mlTable MOVQ ctx+16(FP), CX MOVQ 24(CX), CX MOVQ (CX)(DI*8), DI // Update Literal Length State BZHIQ SI, R14, CX MOVQ $0x00001010, R13 BEXTRQ R13, SI, SI ADDQ CX, SI // Load ctx.llTable MOVQ ctx+16(FP), CX MOVQ (CX), CX MOVQ (CX)(SI*8), SI sequenceDecs_decodeSync_safe_bmi2_skip_update: // Adjust offset MOVQ s+0(FP), CX MOVQ 8(SP), R13 CMPQ R12, $0x01 JBE sequenceDecs_decodeSync_safe_bmi2_adjust_offsetB_1_or_0 MOVUPS 144(CX), X0 MOVQ R13, 144(CX) MOVUPS X0, 152(CX) JMP sequenceDecs_decodeSync_safe_bmi2_after_adjust sequenceDecs_decodeSync_safe_bmi2_adjust_offsetB_1_or_0: CMPQ 24(SP), $0x00000000 JNE sequenceDecs_decodeSync_safe_bmi2_adjust_offset_maybezero INCQ R13 JMP sequenceDecs_decodeSync_safe_bmi2_adjust_offset_nonzero sequenceDecs_decodeSync_safe_bmi2_adjust_offset_maybezero: TESTQ R13, R13 JNZ sequenceDecs_decodeSync_safe_bmi2_adjust_offset_nonzero MOVQ 144(CX), R13 JMP sequenceDecs_decodeSync_safe_bmi2_after_adjust sequenceDecs_decodeSync_safe_bmi2_adjust_offset_nonzero: MOVQ R13, R12 XORQ R14, R14 MOVQ $-1, R15 CMPQ R13, $0x03 CMOVQEQ R14, R12 CMOVQEQ R15, R14 ADDQ 144(CX)(R12*8), R14 JNZ sequenceDecs_decodeSync_safe_bmi2_adjust_temp_valid MOVQ $0x00000001, R14 sequenceDecs_decodeSync_safe_bmi2_adjust_temp_valid: CMPQ R13, $0x01 JZ sequenceDecs_decodeSync_safe_bmi2_adjust_skip MOVQ 152(CX), R12 MOVQ R12, 160(CX) sequenceDecs_decodeSync_safe_bmi2_adjust_skip: MOVQ 144(CX), R12 MOVQ R12, 152(CX) MOVQ R14, 144(CX) MOVQ R14, R13 sequenceDecs_decodeSync_safe_bmi2_after_adjust: MOVQ R13, 8(SP) // Check values MOVQ 16(SP), CX MOVQ 24(SP), R12 LEAQ (CX)(R12*1), R14 MOVQ s+0(FP), R15 ADDQ R14, 256(R15) MOVQ ctx+16(FP), R14 SUBQ R12, 104(R14) JS error_not_enough_literals CMPQ CX, $0x00020002 JA sequenceDecs_decodeSync_safe_bmi2_error_match_len_too_big TESTQ R13, R13 JNZ sequenceDecs_decodeSync_safe_bmi2_match_len_ofs_ok TESTQ CX, CX JNZ sequenceDecs_decodeSync_safe_bmi2_error_match_len_ofs_mismatch sequenceDecs_decodeSync_safe_bmi2_match_len_ofs_ok: MOVQ 24(SP), CX MOVQ 8(SP), R12 MOVQ 16(SP), R13 // Check if we have enough space in s.out LEAQ (CX)(R13*1), R14 ADDQ R9, R14 CMPQ R14, 32(SP) JA error_not_enough_space // Copy literals TESTQ CX, CX JZ check_offset MOVQ CX, R14 SUBQ $0x10, R14 JB copy_1_small copy_1_loop: MOVUPS (R10), X0 MOVUPS X0, (R9) ADDQ $0x10, R10 ADDQ $0x10, R9 SUBQ $0x10, R14 JAE copy_1_loop LEAQ 16(R10)(R14*1), R10 LEAQ 16(R9)(R14*1), R9 MOVUPS -16(R10), X0 MOVUPS X0, -16(R9) JMP copy_1_end copy_1_small: CMPQ CX, $0x03 JE copy_1_move_3 JB copy_1_move_1or2 CMPQ CX, $0x08 JB copy_1_move_4through7 JMP copy_1_move_8through16 copy_1_move_1or2: MOVB (R10), R14 MOVB -1(R10)(CX*1), R15 MOVB R14, (R9) MOVB R15, -1(R9)(CX*1) ADDQ CX, R10 ADDQ CX, R9 JMP copy_1_end copy_1_move_3: MOVW (R10), R14 MOVB 2(R10), R15 MOVW R14, (R9) MOVB R15, 2(R9) ADDQ CX, R10 ADDQ CX, R9 JMP copy_1_end copy_1_move_4through7: MOVL (R10), R14 MOVL -4(R10)(CX*1), R15 MOVL R14, (R9) MOVL R15, -4(R9)(CX*1) ADDQ CX, R10 ADDQ CX, R9 JMP copy_1_end copy_1_move_8through16: MOVQ (R10), R14 MOVQ -8(R10)(CX*1), R15 MOVQ R14, (R9) MOVQ R15, -8(R9)(CX*1) ADDQ CX, R10 ADDQ CX, R9 copy_1_end: ADDQ CX, R11 // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize) check_offset: MOVQ R11, CX ADDQ 40(SP), CX CMPQ R12, CX JG error_match_off_too_big CMPQ R12, 56(SP) JG error_match_off_too_big // Copy match from history MOVQ R12, CX SUBQ R11, CX JLS copy_match MOVQ 48(SP), R14 SUBQ CX, R14 CMPQ R13, CX JG copy_all_from_history MOVQ R13, CX SUBQ $0x10, CX JB copy_4_small copy_4_loop: MOVUPS (R14), X0 MOVUPS X0, (R9) ADDQ $0x10, R14 ADDQ $0x10, R9 SUBQ $0x10, CX JAE copy_4_loop LEAQ 16(R14)(CX*1), R14 LEAQ 16(R9)(CX*1), R9 MOVUPS -16(R14), X0 MOVUPS X0, -16(R9) JMP copy_4_end copy_4_small: CMPQ R13, $0x03 JE copy_4_move_3 CMPQ R13, $0x08 JB copy_4_move_4through7 JMP copy_4_move_8through16 copy_4_move_3: MOVW (R14), CX MOVB 2(R14), R12 MOVW CX, (R9) MOVB R12, 2(R9) ADDQ R13, R14 ADDQ R13, R9 JMP copy_4_end copy_4_move_4through7: MOVL (R14), CX MOVL -4(R14)(R13*1), R12 MOVL CX, (R9) MOVL R12, -4(R9)(R13*1) ADDQ R13, R14 ADDQ R13, R9 JMP copy_4_end copy_4_move_8through16: MOVQ (R14), CX MOVQ -8(R14)(R13*1), R12 MOVQ CX, (R9) MOVQ R12, -8(R9)(R13*1) ADDQ R13, R14 ADDQ R13, R9 copy_4_end: ADDQ R13, R11 JMP handle_loop JMP loop_finished copy_all_from_history: MOVQ CX, R15 SUBQ $0x10, R15 JB copy_5_small copy_5_loop: MOVUPS (R14), X0 MOVUPS X0, (R9) ADDQ $0x10, R14 ADDQ $0x10, R9 SUBQ $0x10, R15 JAE copy_5_loop LEAQ 16(R14)(R15*1), R14 LEAQ 16(R9)(R15*1), R9 MOVUPS -16(R14), X0 MOVUPS X0, -16(R9) JMP copy_5_end copy_5_small: CMPQ CX, $0x03 JE copy_5_move_3 JB copy_5_move_1or2 CMPQ CX, $0x08 JB copy_5_move_4through7 JMP copy_5_move_8through16 copy_5_move_1or2: MOVB (R14), R15 MOVB -1(R14)(CX*1), BP MOVB R15, (R9) MOVB BP, -1(R9)(CX*1) ADDQ CX, R14 ADDQ CX, R9 JMP copy_5_end copy_5_move_3: MOVW (R14), R15 MOVB 2(R14), BP MOVW R15, (R9) MOVB BP, 2(R9) ADDQ CX, R14 ADDQ CX, R9 JMP copy_5_end copy_5_move_4through7: MOVL (R14), R15 MOVL -4(R14)(CX*1), BP MOVL R15, (R9) MOVL BP, -4(R9)(CX*1) ADDQ CX, R14 ADDQ CX, R9 JMP copy_5_end copy_5_move_8through16: MOVQ (R14), R15 MOVQ -8(R14)(CX*1), BP MOVQ R15, (R9) MOVQ BP, -8(R9)(CX*1) ADDQ CX, R14 ADDQ CX, R9 copy_5_end: ADDQ CX, R11 SUBQ CX, R13 // Copy match from the current buffer copy_match: MOVQ R9, CX SUBQ R12, CX // ml <= mo CMPQ R13, R12 JA copy_overlapping_match // Copy non-overlapping match ADDQ R13, R11 MOVQ R13, R12 SUBQ $0x10, R12 JB copy_2_small copy_2_loop: MOVUPS (CX), X0 MOVUPS X0, (R9) ADDQ $0x10, CX ADDQ $0x10, R9 SUBQ $0x10, R12 JAE copy_2_loop LEAQ 16(CX)(R12*1), CX LEAQ 16(R9)(R12*1), R9 MOVUPS -16(CX), X0 MOVUPS X0, -16(R9) JMP copy_2_end copy_2_small: CMPQ R13, $0x03 JE copy_2_move_3 JB copy_2_move_1or2 CMPQ R13, $0x08 JB copy_2_move_4through7 JMP copy_2_move_8through16 copy_2_move_1or2: MOVB (CX), R12 MOVB -1(CX)(R13*1), R14 MOVB R12, (R9) MOVB R14, -1(R9)(R13*1) ADDQ R13, CX ADDQ R13, R9 JMP copy_2_end copy_2_move_3: MOVW (CX), R12 MOVB 2(CX), R14 MOVW R12, (R9) MOVB R14, 2(R9) ADDQ R13, CX ADDQ R13, R9 JMP copy_2_end copy_2_move_4through7: MOVL (CX), R12 MOVL -4(CX)(R13*1), R14 MOVL R12, (R9) MOVL R14, -4(R9)(R13*1) ADDQ R13, CX ADDQ R13, R9 JMP copy_2_end copy_2_move_8through16: MOVQ (CX), R12 MOVQ -8(CX)(R13*1), R14 MOVQ R12, (R9) MOVQ R14, -8(R9)(R13*1) ADDQ R13, CX ADDQ R13, R9 copy_2_end: JMP handle_loop // Copy overlapping match copy_overlapping_match: ADDQ R13, R11 copy_slow_3: MOVB (CX), R12 MOVB R12, (R9) INCQ CX INCQ R9 DECQ R13 JNZ copy_slow_3 handle_loop: MOVQ ctx+16(FP), CX DECQ 96(CX) JNS sequenceDecs_decodeSync_safe_bmi2_main_loop loop_finished: MOVQ br+8(FP), CX MOVQ AX, 24(CX) MOVB DL, 32(CX) MOVQ BX, 8(CX) // Update the context MOVQ ctx+16(FP), AX MOVQ R11, 136(AX) MOVQ 144(AX), CX SUBQ CX, R10 MOVQ R10, 168(AX) // Return success MOVQ $0x00000000, ret+24(FP) RET // Return with match length error sequenceDecs_decodeSync_safe_bmi2_error_match_len_ofs_mismatch: MOVQ 16(SP), AX MOVQ ctx+16(FP), CX MOVQ AX, 216(CX) MOVQ $0x00000001, ret+24(FP) RET // Return with match too long error sequenceDecs_decodeSync_safe_bmi2_error_match_len_too_big: MOVQ ctx+16(FP), AX MOVQ 16(SP), CX MOVQ CX, 216(AX) MOVQ $0x00000002, ret+24(FP) RET // Return with match offset too long error error_match_off_too_big: MOVQ ctx+16(FP), AX MOVQ 8(SP), CX MOVQ CX, 224(AX) MOVQ R11, 136(AX) MOVQ $0x00000003, ret+24(FP) RET // Return with not enough literals error error_not_enough_literals: MOVQ ctx+16(FP), AX MOVQ 24(SP), CX MOVQ CX, 208(AX) MOVQ $0x00000004, ret+24(FP) RET // Return with overread error error_overread: MOVQ $0x00000006, ret+24(FP) RET // Return with not enough output space error error_not_enough_space: MOVQ ctx+16(FP), AX MOVQ 24(SP), CX MOVQ CX, 208(AX) MOVQ 16(SP), CX MOVQ CX, 216(AX) MOVQ R11, 136(AX) MOVQ $0x00000005, ret+24(FP) RET ================================================ FILE: vendor/github.com/klauspost/compress/zstd/seqdec_generic.go ================================================ //go:build !amd64 || appengine || !gc || noasm // +build !amd64 appengine !gc noasm package zstd import ( "fmt" "io" ) // decode sequences from the stream with the provided history but without dictionary. func (s *sequenceDecs) decodeSyncSimple(hist []byte) (bool, error) { return false, nil } // decode sequences from the stream without the provided history. func (s *sequenceDecs) decode(seqs []seqVals) error { br := s.br // Grab full sizes tables, to avoid bounds checks. llTable, mlTable, ofTable := s.litLengths.fse.dt[:maxTablesize], s.matchLengths.fse.dt[:maxTablesize], s.offsets.fse.dt[:maxTablesize] llState, mlState, ofState := s.litLengths.state.state, s.matchLengths.state.state, s.offsets.state.state s.seqSize = 0 litRemain := len(s.literals) maxBlockSize := maxCompressedBlockSize if s.windowSize < maxBlockSize { maxBlockSize = s.windowSize } for i := range seqs { var ll, mo, ml int if len(br.in) > 4+((maxOffsetBits+16+16)>>3) { // inlined function: // ll, mo, ml = s.nextFast(br, llState, mlState, ofState) // Final will not read from stream. var llB, mlB, moB uint8 ll, llB = llState.final() ml, mlB = mlState.final() mo, moB = ofState.final() // extra bits are stored in reverse order. br.fillFast() mo += br.getBits(moB) if s.maxBits > 32 { br.fillFast() } ml += br.getBits(mlB) ll += br.getBits(llB) if moB > 1 { s.prevOffset[2] = s.prevOffset[1] s.prevOffset[1] = s.prevOffset[0] s.prevOffset[0] = mo } else { // mo = s.adjustOffset(mo, ll, moB) // Inlined for rather big speedup if ll == 0 { // There is an exception though, when current sequence's literals_length = 0. // In this case, repeated offsets are shifted by one, so an offset_value of 1 means Repeated_Offset2, // an offset_value of 2 means Repeated_Offset3, and an offset_value of 3 means Repeated_Offset1 - 1_byte. mo++ } if mo == 0 { mo = s.prevOffset[0] } else { var temp int if mo == 3 { temp = s.prevOffset[0] - 1 } else { temp = s.prevOffset[mo] } if temp == 0 { // 0 is not valid; input is corrupted; force offset to 1 println("WARNING: temp was 0") temp = 1 } if mo != 1 { s.prevOffset[2] = s.prevOffset[1] } s.prevOffset[1] = s.prevOffset[0] s.prevOffset[0] = temp mo = temp } } br.fillFast() } else { if br.overread() { if debugDecoder { printf("reading sequence %d, exceeded available data\n", i) } return io.ErrUnexpectedEOF } ll, mo, ml = s.next(br, llState, mlState, ofState) br.fill() } if debugSequences { println("Seq", i, "Litlen:", ll, "mo:", mo, "(abs) ml:", ml) } // Evaluate. // We might be doing this async, so do it early. if mo == 0 && ml > 0 { return fmt.Errorf("zero matchoff and matchlen (%d) > 0", ml) } if ml > maxMatchLen { return fmt.Errorf("match len (%d) bigger than max allowed length", ml) } s.seqSize += ll + ml if s.seqSize > maxBlockSize { return fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) } litRemain -= ll if litRemain < 0 { return fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", ll, litRemain+ll) } seqs[i] = seqVals{ ll: ll, ml: ml, mo: mo, } if i == len(seqs)-1 { // This is the last sequence, so we shouldn't update state. break } // Manually inlined, ~ 5-20% faster // Update all 3 states at once. Approx 20% faster. nBits := llState.nbBits() + mlState.nbBits() + ofState.nbBits() if nBits == 0 { llState = llTable[llState.newState()&maxTableMask] mlState = mlTable[mlState.newState()&maxTableMask] ofState = ofTable[ofState.newState()&maxTableMask] } else { bits := br.get32BitsFast(nBits) lowBits := uint16(bits >> ((ofState.nbBits() + mlState.nbBits()) & 31)) llState = llTable[(llState.newState()+lowBits)&maxTableMask] lowBits = uint16(bits >> (ofState.nbBits() & 31)) lowBits &= bitMask[mlState.nbBits()&15] mlState = mlTable[(mlState.newState()+lowBits)&maxTableMask] lowBits = uint16(bits) & bitMask[ofState.nbBits()&15] ofState = ofTable[(ofState.newState()+lowBits)&maxTableMask] } } s.seqSize += litRemain if s.seqSize > maxBlockSize { return fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) } err := br.close() if err != nil { printf("Closing sequences: %v, %+v\n", err, *br) } return err } // executeSimple handles cases when a dictionary is not used. func (s *sequenceDecs) executeSimple(seqs []seqVals, hist []byte) error { // Ensure we have enough output size... if len(s.out)+s.seqSize > cap(s.out) { addBytes := s.seqSize + len(s.out) s.out = append(s.out, make([]byte, addBytes)...) s.out = s.out[:len(s.out)-addBytes] } if debugDecoder { printf("Execute %d seqs with literals: %d into %d bytes\n", len(seqs), len(s.literals), s.seqSize) } var t = len(s.out) out := s.out[:t+s.seqSize] for _, seq := range seqs { // Add literals copy(out[t:], s.literals[:seq.ll]) t += seq.ll s.literals = s.literals[seq.ll:] // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize { return fmt.Errorf("match offset (%d) bigger than current history (%d)", seq.mo, t+len(hist)) } // Copy from history. if v := seq.mo - t; v > 0 { // v is the start position in history from end. start := len(hist) - v if seq.ml > v { // Some goes into the current block. // Copy remainder of history copy(out[t:], hist[start:]) t += v seq.ml -= v } else { copy(out[t:], hist[start:start+seq.ml]) t += seq.ml continue } } // We must be in the current buffer now if seq.ml > 0 { start := t - seq.mo if seq.ml <= t-start { // No overlap copy(out[t:], out[start:start+seq.ml]) t += seq.ml } else { // Overlapping copy // Extend destination slice and copy one byte at the time. src := out[start : start+seq.ml] dst := out[t:] dst = dst[:len(src)] t += len(src) // Destination is the space we just added. for i := range src { dst[i] = src[i] } } } } // Add final literals copy(out[t:], s.literals) if debugDecoder { t += len(s.literals) if t != len(out) { panic(fmt.Errorf("length mismatch, want %d, got %d, ss: %d", len(out), t, s.seqSize)) } } s.out = out return nil } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/seqenc.go ================================================ // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. // Based on work by Yann Collet, released under BSD License. package zstd import "math/bits" type seqCoders struct { llEnc, ofEnc, mlEnc *fseEncoder llPrev, ofPrev, mlPrev *fseEncoder } // swap coders with another (block). func (s *seqCoders) swap(other *seqCoders) { *s, *other = *other, *s } // setPrev will update the previous encoders to the actually used ones // and make sure a fresh one is in the main slot. func (s *seqCoders) setPrev(ll, ml, of *fseEncoder) { compareSwap := func(used *fseEncoder, current, prev **fseEncoder) { // We used the new one, more current to history and reuse the previous history if *current == used { *prev, *current = *current, *prev c := *current p := *prev c.reUsed = false p.reUsed = true return } if used == *prev { return } // Ensure we cannot reuse by accident prevEnc := *prev prevEnc.symbolLen = 0 } compareSwap(ll, &s.llEnc, &s.llPrev) compareSwap(ml, &s.mlEnc, &s.mlPrev) compareSwap(of, &s.ofEnc, &s.ofPrev) } func highBit(val uint32) (n uint32) { return uint32(bits.Len32(val) - 1) } var llCodeTable = [64]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24} // Up to 6 bits const maxLLCode = 35 // llBitsTable translates from ll code to number of bits. var llBitsTable = [maxLLCode + 1]byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} // llCode returns the code that represents the literal length requested. func llCode(litLength uint32) uint8 { const llDeltaCode = 19 if litLength <= 63 { // Compiler insists on bounds check (Go 1.12) return llCodeTable[litLength&63] } return uint8(highBit(litLength)) + llDeltaCode } var mlCodeTable = [128]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42} // Up to 6 bits const maxMLCode = 52 // mlBitsTable translates from ml code to number of bits. var mlBitsTable = [maxMLCode + 1]byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} // note : mlBase = matchLength - MINMATCH; // because it's the format it's stored in seqStore->sequences func mlCode(mlBase uint32) uint8 { const mlDeltaCode = 36 if mlBase <= 127 { // Compiler insists on bounds check (Go 1.12) return mlCodeTable[mlBase&127] } return uint8(highBit(mlBase)) + mlDeltaCode } func ofCode(offset uint32) uint8 { // A valid offset will always be > 0. return uint8(bits.Len32(offset) - 1) } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/snappy.go ================================================ // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. // Based on work by Yann Collet, released under BSD License. package zstd import ( "encoding/binary" "errors" "hash/crc32" "io" "github.com/klauspost/compress/huff0" snappy "github.com/klauspost/compress/internal/snapref" ) const ( snappyTagLiteral = 0x00 snappyTagCopy1 = 0x01 snappyTagCopy2 = 0x02 snappyTagCopy4 = 0x03 ) const ( snappyChecksumSize = 4 snappyMagicBody = "sNaPpY" // snappyMaxBlockSize is the maximum size of the input to encodeBlock. It is not // part of the wire format per se, but some parts of the encoder assume // that an offset fits into a uint16. // // Also, for the framing format (Writer type instead of Encode function), // https://github.com/google/snappy/blob/master/framing_format.txt says // that "the uncompressed data in a chunk must be no longer than 65536 // bytes". snappyMaxBlockSize = 65536 // snappyMaxEncodedLenOfMaxBlockSize equals MaxEncodedLen(snappyMaxBlockSize), but is // hard coded to be a const instead of a variable, so that obufLen can also // be a const. Their equivalence is confirmed by // TestMaxEncodedLenOfMaxBlockSize. snappyMaxEncodedLenOfMaxBlockSize = 76490 ) const ( chunkTypeCompressedData = 0x00 chunkTypeUncompressedData = 0x01 chunkTypePadding = 0xfe chunkTypeStreamIdentifier = 0xff ) var ( // ErrSnappyCorrupt reports that the input is invalid. ErrSnappyCorrupt = errors.New("snappy: corrupt input") // ErrSnappyTooLarge reports that the uncompressed length is too large. ErrSnappyTooLarge = errors.New("snappy: decoded block is too large") // ErrSnappyUnsupported reports that the input isn't supported. ErrSnappyUnsupported = errors.New("snappy: unsupported input") errUnsupportedLiteralLength = errors.New("snappy: unsupported literal length") ) // SnappyConverter can read SnappyConverter-compressed streams and convert them to zstd. // Conversion is done by converting the stream directly from Snappy without intermediate // full decoding. // Therefore the compression ratio is much less than what can be done by a full decompression // and compression, and a faulty Snappy stream may lead to a faulty Zstandard stream without // any errors being generated. // No CRC value is being generated and not all CRC values of the Snappy stream are checked. // However, it provides really fast recompression of Snappy streams. // The converter can be reused to avoid allocations, even after errors. type SnappyConverter struct { r io.Reader err error buf []byte block *blockEnc } // Convert the Snappy stream supplied in 'in' and write the zStandard stream to 'w'. // If any error is detected on the Snappy stream it is returned. // The number of bytes written is returned. func (r *SnappyConverter) Convert(in io.Reader, w io.Writer) (int64, error) { initPredefined() r.err = nil r.r = in if r.block == nil { r.block = &blockEnc{} r.block.init() } r.block.initNewEncode() if len(r.buf) != snappyMaxEncodedLenOfMaxBlockSize+snappyChecksumSize { r.buf = make([]byte, snappyMaxEncodedLenOfMaxBlockSize+snappyChecksumSize) } r.block.litEnc.Reuse = huff0.ReusePolicyNone var written int64 var readHeader bool { header := frameHeader{WindowSize: snappyMaxBlockSize}.appendTo(r.buf[:0]) var n int n, r.err = w.Write(header) if r.err != nil { return written, r.err } written += int64(n) } for { if !r.readFull(r.buf[:4], true) { // Add empty last block r.block.reset(nil) r.block.last = true err := r.block.encodeLits(r.block.literals, false) if err != nil { return written, err } n, err := w.Write(r.block.output) if err != nil { return written, err } written += int64(n) return written, r.err } chunkType := r.buf[0] if !readHeader { if chunkType != chunkTypeStreamIdentifier { println("chunkType != chunkTypeStreamIdentifier", chunkType) r.err = ErrSnappyCorrupt return written, r.err } readHeader = true } chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16 if chunkLen > len(r.buf) { println("chunkLen > len(r.buf)", chunkType) r.err = ErrSnappyUnsupported return written, r.err } // The chunk types are specified at // https://github.com/google/snappy/blob/master/framing_format.txt switch chunkType { case chunkTypeCompressedData: // Section 4.2. Compressed data (chunk type 0x00). if chunkLen < snappyChecksumSize { println("chunkLen < snappyChecksumSize", chunkLen, snappyChecksumSize) r.err = ErrSnappyCorrupt return written, r.err } buf := r.buf[:chunkLen] if !r.readFull(buf, false) { return written, r.err } //checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 buf = buf[snappyChecksumSize:] n, hdr, err := snappyDecodedLen(buf) if err != nil { r.err = err return written, r.err } buf = buf[hdr:] if n > snappyMaxBlockSize { println("n > snappyMaxBlockSize", n, snappyMaxBlockSize) r.err = ErrSnappyCorrupt return written, r.err } r.block.reset(nil) r.block.pushOffsets() if err := decodeSnappy(r.block, buf); err != nil { r.err = err return written, r.err } if r.block.size+r.block.extraLits != n { printf("invalid size, want %d, got %d\n", n, r.block.size+r.block.extraLits) r.err = ErrSnappyCorrupt return written, r.err } err = r.block.encode(nil, false, false) switch err { case errIncompressible: r.block.popOffsets() r.block.reset(nil) r.block.literals, err = snappy.Decode(r.block.literals[:n], r.buf[snappyChecksumSize:chunkLen]) if err != nil { return written, err } err = r.block.encodeLits(r.block.literals, false) if err != nil { return written, err } case nil: default: return written, err } n, r.err = w.Write(r.block.output) if r.err != nil { return written, err } written += int64(n) continue case chunkTypeUncompressedData: if debugEncoder { println("Uncompressed, chunklen", chunkLen) } // Section 4.3. Uncompressed data (chunk type 0x01). if chunkLen < snappyChecksumSize { println("chunkLen < snappyChecksumSize", chunkLen, snappyChecksumSize) r.err = ErrSnappyCorrupt return written, r.err } r.block.reset(nil) buf := r.buf[:snappyChecksumSize] if !r.readFull(buf, false) { return written, r.err } checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 // Read directly into r.decoded instead of via r.buf. n := chunkLen - snappyChecksumSize if n > snappyMaxBlockSize { println("n > snappyMaxBlockSize", n, snappyMaxBlockSize) r.err = ErrSnappyCorrupt return written, r.err } r.block.literals = r.block.literals[:n] if !r.readFull(r.block.literals, false) { return written, r.err } if snappyCRC(r.block.literals) != checksum { println("literals crc mismatch") r.err = ErrSnappyCorrupt return written, r.err } err := r.block.encodeLits(r.block.literals, false) if err != nil { return written, err } n, r.err = w.Write(r.block.output) if r.err != nil { return written, err } written += int64(n) continue case chunkTypeStreamIdentifier: if debugEncoder { println("stream id", chunkLen, len(snappyMagicBody)) } // Section 4.1. Stream identifier (chunk type 0xff). if chunkLen != len(snappyMagicBody) { println("chunkLen != len(snappyMagicBody)", chunkLen, len(snappyMagicBody)) r.err = ErrSnappyCorrupt return written, r.err } if !r.readFull(r.buf[:len(snappyMagicBody)], false) { return written, r.err } for i := 0; i < len(snappyMagicBody); i++ { if r.buf[i] != snappyMagicBody[i] { println("r.buf[i] != snappyMagicBody[i]", r.buf[i], snappyMagicBody[i], i) r.err = ErrSnappyCorrupt return written, r.err } } continue } if chunkType <= 0x7f { // Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f). println("chunkType <= 0x7f") r.err = ErrSnappyUnsupported return written, r.err } // Section 4.4 Padding (chunk type 0xfe). // Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd). if !r.readFull(r.buf[:chunkLen], false) { return written, r.err } } } // decodeSnappy writes the decoding of src to dst. It assumes that the varint-encoded // length of the decompressed bytes has already been read. func decodeSnappy(blk *blockEnc, src []byte) error { //decodeRef(make([]byte, snappyMaxBlockSize), src) var s, length int lits := blk.extraLits var offset uint32 for s < len(src) { switch src[s] & 0x03 { case snappyTagLiteral: x := uint32(src[s] >> 2) switch { case x < 60: s++ case x == 60: s += 2 if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. println("uint(s) > uint(len(src)", s, src) return ErrSnappyCorrupt } x = uint32(src[s-1]) case x == 61: s += 3 if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. println("uint(s) > uint(len(src)", s, src) return ErrSnappyCorrupt } x = uint32(src[s-2]) | uint32(src[s-1])<<8 case x == 62: s += 4 if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. println("uint(s) > uint(len(src)", s, src) return ErrSnappyCorrupt } x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16 case x == 63: s += 5 if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. println("uint(s) > uint(len(src)", s, src) return ErrSnappyCorrupt } x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24 } if x > snappyMaxBlockSize { println("x > snappyMaxBlockSize", x, snappyMaxBlockSize) return ErrSnappyCorrupt } length = int(x) + 1 if length <= 0 { println("length <= 0 ", length) return errUnsupportedLiteralLength } //if length > snappyMaxBlockSize-d || uint32(length) > len(src)-s { // return ErrSnappyCorrupt //} blk.literals = append(blk.literals, src[s:s+length]...) //println(length, "litLen") lits += length s += length continue case snappyTagCopy1: s += 2 if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. println("uint(s) > uint(len(src)", s, len(src)) return ErrSnappyCorrupt } length = 4 + int(src[s-2])>>2&0x7 offset = uint32(src[s-2])&0xe0<<3 | uint32(src[s-1]) case snappyTagCopy2: s += 3 if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. println("uint(s) > uint(len(src)", s, len(src)) return ErrSnappyCorrupt } length = 1 + int(src[s-3])>>2 offset = uint32(src[s-2]) | uint32(src[s-1])<<8 case snappyTagCopy4: s += 5 if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. println("uint(s) > uint(len(src)", s, len(src)) return ErrSnappyCorrupt } length = 1 + int(src[s-5])>>2 offset = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24 } if offset <= 0 || blk.size+lits < int(offset) /*|| length > len(blk)-d */ { println("offset <= 0 || blk.size+lits < int(offset)", offset, blk.size+lits, int(offset), blk.size, lits) return ErrSnappyCorrupt } // Check if offset is one of the recent offsets. // Adjusts the output offset accordingly. // Gives a tiny bit of compression, typically around 1%. if false { offset = blk.matchOffset(offset, uint32(lits)) } else { offset += 3 } blk.sequences = append(blk.sequences, seq{ litLen: uint32(lits), offset: offset, matchLen: uint32(length) - zstdMinMatch, }) blk.size += length + lits lits = 0 } blk.extraLits = lits return nil } func (r *SnappyConverter) readFull(p []byte, allowEOF bool) (ok bool) { if _, r.err = io.ReadFull(r.r, p); r.err != nil { if r.err == io.ErrUnexpectedEOF || (r.err == io.EOF && !allowEOF) { r.err = ErrSnappyCorrupt } return false } return true } var crcTable = crc32.MakeTable(crc32.Castagnoli) // crc implements the checksum specified in section 3 of // https://github.com/google/snappy/blob/master/framing_format.txt func snappyCRC(b []byte) uint32 { c := crc32.Update(0, crcTable, b) return c>>15 | c<<17 + 0xa282ead8 } // snappyDecodedLen returns the length of the decoded block and the number of bytes // that the length header occupied. func snappyDecodedLen(src []byte) (blockLen, headerLen int, err error) { v, n := binary.Uvarint(src) if n <= 0 || v > 0xffffffff { return 0, 0, ErrSnappyCorrupt } const wordSize = 32 << (^uint(0) >> 32 & 1) if wordSize == 32 && v > 0x7fffffff { return 0, 0, ErrSnappyTooLarge } return int(v), n, nil } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/zip.go ================================================ // Copyright 2019+ Klaus Post. All rights reserved. // License information can be found in the LICENSE file. package zstd import ( "errors" "io" "sync" ) // ZipMethodWinZip is the method for Zstandard compressed data inside Zip files for WinZip. // See https://www.winzip.com/win/en/comp_info.html const ZipMethodWinZip = 93 // ZipMethodPKWare is the original method number used by PKWARE to indicate Zstandard compression. // Deprecated: This has been deprecated by PKWARE, use ZipMethodWinZip instead for compression. // See https://pkware.cachefly.net/webdocs/APPNOTE/APPNOTE-6.3.9.TXT const ZipMethodPKWare = 20 // zipReaderPool is the default reader pool. var zipReaderPool = sync.Pool{New: func() interface{} { z, err := NewReader(nil, WithDecoderLowmem(true), WithDecoderMaxWindow(128<<20), WithDecoderConcurrency(1)) if err != nil { panic(err) } return z }} // newZipReader creates a pooled zip decompressor. func newZipReader(opts ...DOption) func(r io.Reader) io.ReadCloser { pool := &zipReaderPool if len(opts) > 0 { opts = append([]DOption{WithDecoderLowmem(true), WithDecoderMaxWindow(128 << 20)}, opts...) // Force concurrency 1 opts = append(opts, WithDecoderConcurrency(1)) // Create our own pool pool = &sync.Pool{} } return func(r io.Reader) io.ReadCloser { dec, ok := pool.Get().(*Decoder) if ok { dec.Reset(r) } else { d, err := NewReader(r, opts...) if err != nil { panic(err) } dec = d } return &pooledZipReader{dec: dec, pool: pool} } } type pooledZipReader struct { mu sync.Mutex // guards Close and Read pool *sync.Pool dec *Decoder } func (r *pooledZipReader) Read(p []byte) (n int, err error) { r.mu.Lock() defer r.mu.Unlock() if r.dec == nil { return 0, errors.New("read after close or EOF") } dec, err := r.dec.Read(p) if err == io.EOF { r.dec.Reset(nil) r.pool.Put(r.dec) r.dec = nil } return dec, err } func (r *pooledZipReader) Close() error { r.mu.Lock() defer r.mu.Unlock() var err error if r.dec != nil { err = r.dec.Reset(nil) r.pool.Put(r.dec) r.dec = nil } return err } type pooledZipWriter struct { mu sync.Mutex // guards Close and Read enc *Encoder pool *sync.Pool } func (w *pooledZipWriter) Write(p []byte) (n int, err error) { w.mu.Lock() defer w.mu.Unlock() if w.enc == nil { return 0, errors.New("Write after Close") } return w.enc.Write(p) } func (w *pooledZipWriter) Close() error { w.mu.Lock() defer w.mu.Unlock() var err error if w.enc != nil { err = w.enc.Close() w.pool.Put(w.enc) w.enc = nil } return err } // ZipCompressor returns a compressor that can be registered with zip libraries. // The provided encoder options will be used on all encodes. func ZipCompressor(opts ...EOption) func(w io.Writer) (io.WriteCloser, error) { var pool sync.Pool return func(w io.Writer) (io.WriteCloser, error) { enc, ok := pool.Get().(*Encoder) if ok { enc.Reset(w) } else { var err error enc, err = NewWriter(w, opts...) if err != nil { return nil, err } } return &pooledZipWriter{enc: enc, pool: &pool}, nil } } // ZipDecompressor returns a decompressor that can be registered with zip libraries. // See ZipCompressor for example. // Options can be specified. WithDecoderConcurrency(1) is forced, // and by default a 128MB maximum decompression window is specified. // The window size can be overridden if required. func ZipDecompressor(opts ...DOption) func(r io.Reader) io.ReadCloser { return newZipReader(opts...) } ================================================ FILE: vendor/github.com/klauspost/compress/zstd/zstd.go ================================================ // Package zstd provides decompression of zstandard files. // // For advanced usage and examples, go to the README: https://github.com/klauspost/compress/tree/master/zstd#zstd package zstd import ( "bytes" "encoding/binary" "errors" "log" "math" ) // enable debug printing const debug = false // enable encoding debug printing const debugEncoder = debug // enable decoding debug printing const debugDecoder = debug // Enable extra assertions. const debugAsserts = debug || false // print sequence details const debugSequences = false // print detailed matching information const debugMatches = false // force encoder to use predefined tables. const forcePreDef = false // zstdMinMatch is the minimum zstd match length. const zstdMinMatch = 3 // fcsUnknown is used for unknown frame content size. const fcsUnknown = math.MaxUint64 var ( // ErrReservedBlockType is returned when a reserved block type is found. // Typically this indicates wrong or corrupted input. ErrReservedBlockType = errors.New("invalid input: reserved block type encountered") // ErrCompressedSizeTooBig is returned when a block is bigger than allowed. // Typically this indicates wrong or corrupted input. ErrCompressedSizeTooBig = errors.New("invalid input: compressed size too big") // ErrBlockTooSmall is returned when a block is too small to be decoded. // Typically returned on invalid input. ErrBlockTooSmall = errors.New("block too small") // ErrUnexpectedBlockSize is returned when a block has unexpected size. // Typically returned on invalid input. ErrUnexpectedBlockSize = errors.New("unexpected block size") // ErrMagicMismatch is returned when a "magic" number isn't what is expected. // Typically this indicates wrong or corrupted input. ErrMagicMismatch = errors.New("invalid input: magic number mismatch") // ErrWindowSizeExceeded is returned when a reference exceeds the valid window size. // Typically this indicates wrong or corrupted input. ErrWindowSizeExceeded = errors.New("window size exceeded") // ErrWindowSizeTooSmall is returned when no window size is specified. // Typically this indicates wrong or corrupted input. ErrWindowSizeTooSmall = errors.New("invalid input: window size was too small") // ErrDecoderSizeExceeded is returned if decompressed size exceeds the configured limit. ErrDecoderSizeExceeded = errors.New("decompressed size exceeds configured limit") // ErrUnknownDictionary is returned if the dictionary ID is unknown. ErrUnknownDictionary = errors.New("unknown dictionary") // ErrFrameSizeExceeded is returned if the stated frame size is exceeded. // This is only returned if SingleSegment is specified on the frame. ErrFrameSizeExceeded = errors.New("frame size exceeded") // ErrFrameSizeMismatch is returned if the stated frame size does not match the expected size. // This is only returned if SingleSegment is specified on the frame. ErrFrameSizeMismatch = errors.New("frame size does not match size on stream") // ErrCRCMismatch is returned if CRC mismatches. ErrCRCMismatch = errors.New("CRC check failed") // ErrDecoderClosed will be returned if the Decoder was used after // Close has been called. ErrDecoderClosed = errors.New("decoder used after Close") // ErrDecoderNilInput is returned when a nil Reader was provided // and an operation other than Reset/DecodeAll/Close was attempted. ErrDecoderNilInput = errors.New("nil input provided as reader") ) func println(a ...interface{}) { if debug || debugDecoder || debugEncoder { log.Println(a...) } } func printf(format string, a ...interface{}) { if debug || debugDecoder || debugEncoder { log.Printf(format, a...) } } func load3232(b []byte, i int32) uint32 { return binary.LittleEndian.Uint32(b[:len(b):len(b)][i:]) } func load6432(b []byte, i int32) uint64 { return binary.LittleEndian.Uint64(b[:len(b):len(b)][i:]) } type byter interface { Bytes() []byte Len() int } var _ byter = &bytes.Buffer{} ================================================ FILE: vendor/github.com/klauspost/pgzip/.gitignore ================================================ # Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof ================================================ FILE: vendor/github.com/klauspost/pgzip/.travis.yml ================================================ language: go sudo: false os: - linux - osx go: - 1.9.x - 1.10.x - master script: - go test -v -cpu=1,2,4 . - go test -v -cpu=2 -race -short . matrix: allow_failures: - go: 'master' fast_finish: true ================================================ FILE: vendor/github.com/klauspost/pgzip/GO_LICENSE ================================================ Copyright (c) 2012 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/klauspost/pgzip/LICENSE ================================================ MIT License Copyright (c) 2014 Klaus Post Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/klauspost/pgzip/README.md ================================================ pgzip ===== Go parallel gzip compression/decompression. This is a fully gzip compatible drop in replacement for "compress/gzip". This will split compression into blocks that are compressed in parallel. This can be useful for compressing big amounts of data. The output is a standard gzip file. The gzip decompression is modified so it decompresses ahead of the current reader. This means that reads will be non-blocking if the decompressor can keep ahead of your code reading from it. CRC calculation also takes place in a separate goroutine. You should only use this if you are (de)compressing big amounts of data, say **more than 1MB** at the time, otherwise you will not see any benefit, and it will likely be faster to use the internal gzip library or [this package](https://github.com/klauspost/compress). It is important to note that this library creates and reads *standard gzip files*. You do not have to match the compressor/decompressor to get the described speedups, and the gzip files are fully compatible with other gzip readers/writers. A golang variant of this is [bgzf](https://godoc.org/github.com/biogo/hts/bgzf), which has the same feature, as well as seeking in the resulting file. The only drawback is a slightly bigger overhead compared to this and pure gzip. See a comparison below. [![GoDoc][1]][2] [![Build Status][3]][4] [1]: https://godoc.org/github.com/klauspost/pgzip?status.svg [2]: https://godoc.org/github.com/klauspost/pgzip [3]: https://travis-ci.org/klauspost/pgzip.svg [4]: https://travis-ci.org/klauspost/pgzip Installation ==== ```go get github.com/klauspost/pgzip/...``` You might need to get/update the dependencies: ``` go get -u github.com/klauspost/compress ``` Usage ==== [Godoc Doumentation](https://godoc.org/github.com/klauspost/pgzip) To use as a replacement for gzip, exchange ```import "compress/gzip"``` with ```import gzip "github.com/klauspost/pgzip"```. # Changes * Oct 6, 2016: Fixed an issue if the destination writer returned an error. * Oct 6, 2016: Better buffer reuse, should now generate less garbage. * Oct 6, 2016: Output does not change based on write sizes. * Dec 8, 2015: Decoder now supports the io.WriterTo interface, giving a speedup and less GC pressure. * Oct 9, 2015: Reduced allocations by ~35 by using sync.Pool. ~15% overall speedup. Changes in [github.com/klauspost/compress](https://github.com/klauspost/compress#changelog) are also carried over, so see that for more changes. ## Compression The simplest way to use this is to simply do the same as you would when using [compress/gzip](http://golang.org/pkg/compress/gzip). To change the block size, use the added (*pgzip.Writer).SetConcurrency(blockSize, blocks int) function. With this you can control the approximate size of your blocks, as well as how many you want to be processing in parallel. Default values for this is SetConcurrency(1MB, runtime.GOMAXPROCS(0)), meaning blocks are split at 1 MB and up to the number of CPU threads blocks can be processing at once before the writer blocks. Example: ``` var b bytes.Buffer w := gzip.NewWriter(&b) w.SetConcurrency(100000, 10) w.Write([]byte("hello, world\n")) w.Close() ``` To get any performance gains, you should at least be compressing more than 1 megabyte of data at the time. You should at least have a block size of 100k and at least a number of blocks that match the number of cores your would like to utilize, but about twice the number of blocks would be the best. Another side effect of this is, that it is likely to speed up your other code, since writes to the compressor only blocks if the compressor is already compressing the number of blocks you have specified. This also means you don't have worry about buffering input to the compressor. ## Decompression Decompression works similar to compression. That means that you simply call pgzip the same way as you would call [compress/gzip](http://golang.org/pkg/compress/gzip). The only difference is that if you want to specify your own readahead, you have to use `pgzip.NewReaderN(r io.Reader, blockSize, blocks int)` to get a reader with your custom blocksizes. The `blockSize` is the size of each block decoded, and `blocks` is the maximum number of blocks that is decoded ahead. See [Example on playground](http://play.golang.org/p/uHv1B5NbDh) Performance ==== ## Compression See my blog post in [Benchmarks of Golang Gzip](https://blog.klauspost.com/go-gzipdeflate-benchmarks/). Compression cost is usually about 0.2% with default settings with a block size of 250k. Example with GOMAXPROC set to 32 (16 core CPU) Content is [Matt Mahoneys 10GB corpus](http://mattmahoney.net/dc/10gb.html). Compression level 6. Compressor | MB/sec | speedup | size | size overhead (lower=better) ------------|----------|---------|------|--------- [gzip](http://golang.org/pkg/compress/gzip) (golang) | 15.44MB/s (1 thread) | 1.0x | 4781329307 | 0% [gzip](http://github.com/klauspost/compress/gzip) (klauspost) | 135.04MB/s (1 thread) | 8.74x | 4894858258 | +2.37% [pgzip](https://github.com/klauspost/pgzip) (klauspost) | 1573.23MB/s| 101.9x | 4902285651 | +2.53% [bgzf](https://godoc.org/github.com/biogo/hts/bgzf) (biogo) | 361.40MB/s | 23.4x | 4869686090 | +1.85% [pargzip](https://godoc.org/github.com/golang/build/pargzip) (builder) | 306.01MB/s | 19.8x | 4786890417 | +0.12% pgzip also contains a [linear time compression](https://github.com/klauspost/compress#linear-time-compression-huffman-only) mode, that will allow compression at ~250MB per core per second, independent of the content. See the [complete sheet](https://docs.google.com/spreadsheets/d/1nuNE2nPfuINCZJRMt6wFWhKpToF95I47XjSsc-1rbPQ/edit?usp=sharing) for different content types and compression settings. ## Decompression The decompression speedup is there because it allows you to do other work while the decompression is taking place. In the example above, the numbers are as follows on a 4 CPU machine: Decompressor | Time | Speedup -------------|------|-------- [gzip](http://golang.org/pkg/compress/gzip) (golang) | 1m28.85s | 0% [pgzip](https://github.com/klauspost/pgzip) (golang) | 43.48s | 104% But wait, since gzip decompression is inherently singlethreaded (aside from CRC calculation) how can it be more than 100% faster? Because pgzip due to its design also acts as a buffer. When using unbuffered gzip, you are also waiting for io when you are decompressing. If the gzip decoder can keep up, it will always have data ready for your reader, and you will not be waiting for input to the gzip decompressor to complete. This is pretty much an optimal situation for pgzip, but it reflects most common usecases for CPU intensive gzip usage. I haven't included [bgzf](https://godoc.org/github.com/biogo/hts/bgzf) in this comparison, since it only can decompress files created by a compatible encoder, and therefore cannot be considered a generic gzip decompressor. But if you are able to compress your files with a bgzf compatible program, you can expect it to scale beyond 100%. # License This contains large portions of code from the go repository - see GO_LICENSE for more information. The changes are released under MIT License. See LICENSE for more information. ================================================ FILE: vendor/github.com/klauspost/pgzip/gunzip.go ================================================ // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package pgzip implements reading and writing of gzip format compressed files, // as specified in RFC 1952. // // This is a drop in replacement for "compress/gzip". // This will split compression into blocks that are compressed in parallel. // This can be useful for compressing big amounts of data. // The gzip decompression has not been modified, but remains in the package, // so you can use it as a complete replacement for "compress/gzip". // // See more at https://github.com/klauspost/pgzip package pgzip import ( "bufio" "errors" "hash" "hash/crc32" "io" "sync" "time" "github.com/klauspost/compress/flate" ) const ( gzipID1 = 0x1f gzipID2 = 0x8b gzipDeflate = 8 flagText = 1 << 0 flagHdrCrc = 1 << 1 flagExtra = 1 << 2 flagName = 1 << 3 flagComment = 1 << 4 ) func makeReader(r io.Reader) flate.Reader { if rr, ok := r.(flate.Reader); ok { return rr } return bufio.NewReader(r) } var ( // ErrChecksum is returned when reading GZIP data that has an invalid checksum. ErrChecksum = errors.New("gzip: invalid checksum") // ErrHeader is returned when reading GZIP data that has an invalid header. ErrHeader = errors.New("gzip: invalid header") ) // The gzip file stores a header giving metadata about the compressed file. // That header is exposed as the fields of the Writer and Reader structs. type Header struct { Comment string // comment Extra []byte // "extra data" ModTime time.Time // modification time Name string // file name OS byte // operating system type } // A Reader is an io.Reader that can be read to retrieve // uncompressed data from a gzip-format compressed file. // // In general, a gzip file can be a concatenation of gzip files, // each with its own header. Reads from the Reader // return the concatenation of the uncompressed data of each. // Only the first header is recorded in the Reader fields. // // Gzip files store a length and checksum of the uncompressed data. // The Reader will return a ErrChecksum when Read // reaches the end of the uncompressed data if it does not // have the expected length or checksum. Clients should treat data // returned by Read as tentative until they receive the io.EOF // marking the end of the data. type Reader struct { Header r flate.Reader decompressor io.ReadCloser digest hash.Hash32 size uint32 flg byte buf [512]byte err error closeErr chan error multistream bool readAhead chan read roff int // read offset current []byte closeReader chan struct{} lastBlock bool blockSize int blocks int activeRA bool // Indication if readahead is active mu sync.Mutex // Lock for above blockPool chan []byte } type read struct { b []byte err error } // NewReader creates a new Reader reading the given reader. // The implementation buffers input and may read more data than necessary from r. // It is the caller's responsibility to call Close on the Reader when done. func NewReader(r io.Reader) (*Reader, error) { z := new(Reader) z.blocks = defaultBlocks z.blockSize = defaultBlockSize z.r = makeReader(r) z.digest = crc32.NewIEEE() z.multistream = true z.blockPool = make(chan []byte, z.blocks) for i := 0; i < z.blocks; i++ { z.blockPool <- make([]byte, z.blockSize) } if err := z.readHeader(true); err != nil { return nil, err } return z, nil } // NewReaderN creates a new Reader reading the given reader. // The implementation buffers input and may read more data than necessary from r. // It is the caller's responsibility to call Close on the Reader when done. // // With this you can control the approximate size of your blocks, // as well as how many blocks you want to have prefetched. // // Default values for this is blockSize = 250000, blocks = 16, // meaning up to 16 blocks of maximum 250000 bytes will be // prefetched. func NewReaderN(r io.Reader, blockSize, blocks int) (*Reader, error) { z := new(Reader) z.blocks = blocks z.blockSize = blockSize z.r = makeReader(r) z.digest = crc32.NewIEEE() z.multistream = true // Account for too small values if z.blocks <= 0 { z.blocks = defaultBlocks } if z.blockSize <= 512 { z.blockSize = defaultBlockSize } z.blockPool = make(chan []byte, z.blocks) for i := 0; i < z.blocks; i++ { z.blockPool <- make([]byte, z.blockSize) } if err := z.readHeader(true); err != nil { return nil, err } return z, nil } // Reset discards the Reader z's state and makes it equivalent to the // result of its original state from NewReader, but reading from r instead. // This permits reusing a Reader rather than allocating a new one. func (z *Reader) Reset(r io.Reader) error { z.killReadAhead() z.r = makeReader(r) z.digest = crc32.NewIEEE() z.size = 0 z.err = nil z.multistream = true // Account for uninitialized values if z.blocks <= 0 { z.blocks = defaultBlocks } if z.blockSize <= 512 { z.blockSize = defaultBlockSize } if z.blockPool == nil { z.blockPool = make(chan []byte, z.blocks) for i := 0; i < z.blocks; i++ { z.blockPool <- make([]byte, z.blockSize) } } return z.readHeader(true) } // Multistream controls whether the reader supports multistream files. // // If enabled (the default), the Reader expects the input to be a sequence // of individually gzipped data streams, each with its own header and // trailer, ending at EOF. The effect is that the concatenation of a sequence // of gzipped files is treated as equivalent to the gzip of the concatenation // of the sequence. This is standard behavior for gzip readers. // // Calling Multistream(false) disables this behavior; disabling the behavior // can be useful when reading file formats that distinguish individual gzip // data streams or mix gzip data streams with other data streams. // In this mode, when the Reader reaches the end of the data stream, // Read returns io.EOF. If the underlying reader implements io.ByteReader, // it will be left positioned just after the gzip stream. // To start the next stream, call z.Reset(r) followed by z.Multistream(false). // If there is no next stream, z.Reset(r) will return io.EOF. func (z *Reader) Multistream(ok bool) { z.multistream = ok } // GZIP (RFC 1952) is little-endian, unlike ZLIB (RFC 1950). func get4(p []byte) uint32 { return uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24 } func (z *Reader) readString() (string, error) { var err error needconv := false for i := 0; ; i++ { if i >= len(z.buf) { return "", ErrHeader } z.buf[i], err = z.r.ReadByte() if err != nil { return "", err } if z.buf[i] > 0x7f { needconv = true } if z.buf[i] == 0 { // GZIP (RFC 1952) specifies that strings are NUL-terminated ISO 8859-1 (Latin-1). if needconv { s := make([]rune, 0, i) for _, v := range z.buf[0:i] { s = append(s, rune(v)) } return string(s), nil } return string(z.buf[0:i]), nil } } } func (z *Reader) read2() (uint32, error) { _, err := io.ReadFull(z.r, z.buf[0:2]) if err != nil { return 0, err } return uint32(z.buf[0]) | uint32(z.buf[1])<<8, nil } func (z *Reader) readHeader(save bool) error { z.killReadAhead() _, err := io.ReadFull(z.r, z.buf[0:10]) if err != nil { return err } if z.buf[0] != gzipID1 || z.buf[1] != gzipID2 || z.buf[2] != gzipDeflate { return ErrHeader } z.flg = z.buf[3] if save { z.ModTime = time.Unix(int64(get4(z.buf[4:8])), 0) // z.buf[8] is xfl, ignored z.OS = z.buf[9] } z.digest.Reset() z.digest.Write(z.buf[0:10]) if z.flg&flagExtra != 0 { n, err := z.read2() if err != nil { return err } data := make([]byte, n) if _, err = io.ReadFull(z.r, data); err != nil { return err } if save { z.Extra = data } } var s string if z.flg&flagName != 0 { if s, err = z.readString(); err != nil { return err } if save { z.Name = s } } if z.flg&flagComment != 0 { if s, err = z.readString(); err != nil { return err } if save { z.Comment = s } } if z.flg&flagHdrCrc != 0 { n, err := z.read2() if err != nil { return err } sum := z.digest.Sum32() & 0xFFFF if n != sum { return ErrHeader } } z.digest.Reset() z.decompressor = flate.NewReader(z.r) z.doReadAhead() return nil } func (z *Reader) killReadAhead() error { z.mu.Lock() defer z.mu.Unlock() if z.activeRA { if z.closeReader != nil { close(z.closeReader) } // Wait for decompressor to be closed and return error, if any. e, ok := <-z.closeErr z.activeRA = false if !ok { // Channel is closed, so if there was any error it has already been returned. return nil } return e } return nil } // Starts readahead. // Will return on error (including io.EOF) // or when z.closeReader is closed. func (z *Reader) doReadAhead() { z.mu.Lock() defer z.mu.Unlock() z.activeRA = true if z.blocks <= 0 { z.blocks = defaultBlocks } if z.blockSize <= 512 { z.blockSize = defaultBlockSize } ra := make(chan read, z.blocks) z.readAhead = ra closeReader := make(chan struct{}, 0) z.closeReader = closeReader z.lastBlock = false closeErr := make(chan error, 1) z.closeErr = closeErr z.size = 0 z.roff = 0 z.current = nil decomp := z.decompressor go func() { defer func() { closeErr <- decomp.Close() close(closeErr) close(ra) }() // We hold a local reference to digest, since // it way be changed by reset. digest := z.digest var wg sync.WaitGroup for { var buf []byte select { case buf = <-z.blockPool: case <-closeReader: return } buf = buf[0:z.blockSize] // Try to fill the buffer n, err := io.ReadFull(decomp, buf) if err == io.ErrUnexpectedEOF { if n > 0 { err = nil } else { // If we got zero bytes, we need to establish if // we reached end of stream or truncated stream. _, err = decomp.Read([]byte{}) if err == io.EOF { err = nil } } } if n < len(buf) { buf = buf[0:n] } wg.Wait() wg.Add(1) go func() { digest.Write(buf) wg.Done() }() z.size += uint32(n) // If we return any error, out digest must be ready if err != nil { wg.Wait() } select { case z.readAhead <- read{b: buf, err: err}: case <-closeReader: // Sent on close, we don't care about the next results return } if err != nil { return } } }() } func (z *Reader) Read(p []byte) (n int, err error) { if z.err != nil { return 0, z.err } if len(p) == 0 { return 0, nil } for { if len(z.current) == 0 && !z.lastBlock { read := <-z.readAhead if read.err != nil { // If not nil, the reader will have exited z.closeReader = nil if read.err != io.EOF { z.err = read.err return } if read.err == io.EOF { z.lastBlock = true err = nil } } z.current = read.b z.roff = 0 } avail := z.current[z.roff:] if len(p) >= len(avail) { // If len(p) >= len(current), return all content of current n = copy(p, avail) z.blockPool <- z.current z.current = nil if z.lastBlock { err = io.EOF break } } else { // We copy as much as there is space for n = copy(p, avail) z.roff += n } return } // Finished file; check checksum + size. if _, err := io.ReadFull(z.r, z.buf[0:8]); err != nil { z.err = err return 0, err } crc32, isize := get4(z.buf[0:4]), get4(z.buf[4:8]) sum := z.digest.Sum32() if sum != crc32 || isize != z.size { z.err = ErrChecksum return 0, z.err } // File is ok; should we attempt reading one more? if !z.multistream { return 0, io.EOF } // Is there another? if err = z.readHeader(false); err != nil { z.err = err return } // Yes. Reset and read from it. return z.Read(p) } func (z *Reader) WriteTo(w io.Writer) (n int64, err error) { total := int64(0) for { if z.err != nil { return total, z.err } // We write both to output and digest. for { // Read from input read := <-z.readAhead if read.err != nil { // If not nil, the reader will have exited z.closeReader = nil if read.err != io.EOF { z.err = read.err return total, z.err } if read.err == io.EOF { z.lastBlock = true err = nil } } // Write what we got n, err := w.Write(read.b) if n != len(read.b) { return total, io.ErrShortWrite } total += int64(n) if err != nil { return total, err } // Put block back z.blockPool <- read.b if z.lastBlock { break } } // Finished file; check checksum + size. if _, err := io.ReadFull(z.r, z.buf[0:8]); err != nil { z.err = err return total, err } crc32, isize := get4(z.buf[0:4]), get4(z.buf[4:8]) sum := z.digest.Sum32() if sum != crc32 || isize != z.size { z.err = ErrChecksum return total, z.err } // File is ok; should we attempt reading one more? if !z.multistream { return total, nil } // Is there another? err = z.readHeader(false) if err == io.EOF { return total, nil } if err != nil { z.err = err return total, err } } } // Close closes the Reader. It does not close the underlying io.Reader. func (z *Reader) Close() error { return z.killReadAhead() } ================================================ FILE: vendor/github.com/klauspost/pgzip/gzip.go ================================================ // Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package pgzip import ( "bytes" "errors" "fmt" "hash" "hash/crc32" "io" "runtime" "sync" "time" "github.com/klauspost/compress/flate" ) const ( defaultBlockSize = 1 << 20 tailSize = 16384 defaultBlocks = 4 ) // These constants are copied from the flate package, so that code that imports // "compress/gzip" does not also have to import "compress/flate". const ( NoCompression = flate.NoCompression BestSpeed = flate.BestSpeed BestCompression = flate.BestCompression DefaultCompression = flate.DefaultCompression ConstantCompression = flate.ConstantCompression HuffmanOnly = flate.HuffmanOnly ) // A Writer is an io.WriteCloser. // Writes to a Writer are compressed and written to w. type Writer struct { Header w io.Writer level int wroteHeader bool blockSize int blocks int currentBuffer []byte prevTail []byte digest hash.Hash32 size int closed bool buf [10]byte errMu sync.RWMutex err error pushedErr chan struct{} results chan result dictFlatePool sync.Pool dstPool sync.Pool wg sync.WaitGroup } type result struct { result chan []byte notifyWritten chan struct{} } // Use SetConcurrency to finetune the concurrency level if needed. // // With this you can control the approximate size of your blocks, // as well as how many you want to be processing in parallel. // // Default values for this is SetConcurrency(defaultBlockSize, runtime.GOMAXPROCS(0)), // meaning blocks are split at 1 MB and up to the number of CPU threads // can be processing at once before the writer blocks. func (z *Writer) SetConcurrency(blockSize, blocks int) error { if blockSize <= tailSize { return fmt.Errorf("gzip: block size cannot be less than or equal to %d", tailSize) } if blocks <= 0 { return errors.New("gzip: blocks cannot be zero or less") } if blockSize == z.blockSize && blocks == z.blocks { return nil } z.blockSize = blockSize z.results = make(chan result, blocks) z.blocks = blocks z.dstPool.New = func() interface{} { return make([]byte, 0, blockSize+(blockSize)>>4) } return nil } // NewWriter returns a new Writer. // Writes to the returned writer are compressed and written to w. // // It is the caller's responsibility to call Close on the WriteCloser when done. // Writes may be buffered and not flushed until Close. // // Callers that wish to set the fields in Writer.Header must do so before // the first call to Write or Close. The Comment and Name header fields are // UTF-8 strings in Go, but the underlying format requires NUL-terminated ISO // 8859-1 (Latin-1). NUL or non-Latin-1 runes in those strings will lead to an // error on Write. func NewWriter(w io.Writer) *Writer { z, _ := NewWriterLevel(w, DefaultCompression) return z } // NewWriterLevel is like NewWriter but specifies the compression level instead // of assuming DefaultCompression. // // The compression level can be DefaultCompression, NoCompression, or any // integer value between BestSpeed and BestCompression inclusive. The error // returned will be nil if the level is valid. func NewWriterLevel(w io.Writer, level int) (*Writer, error) { if level < ConstantCompression || level > BestCompression { return nil, fmt.Errorf("gzip: invalid compression level: %d", level) } z := new(Writer) z.SetConcurrency(defaultBlockSize, runtime.GOMAXPROCS(0)) z.init(w, level) return z, nil } // This function must be used by goroutines to set an // error condition, since z.err access is restricted // to the callers goruotine. func (z *Writer) pushError(err error) { z.errMu.Lock() if z.err != nil { z.errMu.Unlock() return } z.err = err close(z.pushedErr) z.errMu.Unlock() } func (z *Writer) init(w io.Writer, level int) { z.wg.Wait() digest := z.digest if digest != nil { digest.Reset() } else { digest = crc32.NewIEEE() } z.Header = Header{OS: 255} z.w = w z.level = level z.digest = digest z.pushedErr = make(chan struct{}, 0) z.results = make(chan result, z.blocks) z.err = nil z.closed = false z.Comment = "" z.Extra = nil z.ModTime = time.Time{} z.wroteHeader = false z.currentBuffer = nil z.buf = [10]byte{} z.prevTail = nil z.size = 0 if z.dictFlatePool.New == nil { z.dictFlatePool.New = func() interface{} { f, _ := flate.NewWriterDict(w, level, nil) return f } } } // Reset discards the Writer z's state and makes it equivalent to the // result of its original state from NewWriter or NewWriterLevel, but // writing to w instead. This permits reusing a Writer rather than // allocating a new one. func (z *Writer) Reset(w io.Writer) { if z.results != nil && !z.closed { close(z.results) } z.SetConcurrency(defaultBlockSize, runtime.GOMAXPROCS(0)) z.init(w, z.level) } // GZIP (RFC 1952) is little-endian, unlike ZLIB (RFC 1950). func put2(p []byte, v uint16) { p[0] = uint8(v >> 0) p[1] = uint8(v >> 8) } func put4(p []byte, v uint32) { p[0] = uint8(v >> 0) p[1] = uint8(v >> 8) p[2] = uint8(v >> 16) p[3] = uint8(v >> 24) } // writeBytes writes a length-prefixed byte slice to z.w. func (z *Writer) writeBytes(b []byte) error { if len(b) > 0xffff { return errors.New("gzip.Write: Extra data is too large") } put2(z.buf[0:2], uint16(len(b))) _, err := z.w.Write(z.buf[0:2]) if err != nil { return err } _, err = z.w.Write(b) return err } // writeString writes a UTF-8 string s in GZIP's format to z.w. // GZIP (RFC 1952) specifies that strings are NUL-terminated ISO 8859-1 (Latin-1). func (z *Writer) writeString(s string) (err error) { // GZIP stores Latin-1 strings; error if non-Latin-1; convert if non-ASCII. needconv := false for _, v := range s { if v == 0 || v > 0xff { return errors.New("gzip.Write: non-Latin-1 header string") } if v > 0x7f { needconv = true } } if needconv { b := make([]byte, 0, len(s)) for _, v := range s { b = append(b, byte(v)) } _, err = z.w.Write(b) } else { _, err = io.WriteString(z.w, s) } if err != nil { return err } // GZIP strings are NUL-terminated. z.buf[0] = 0 _, err = z.w.Write(z.buf[0:1]) return err } // compressCurrent will compress the data currently buffered // This should only be called from the main writer/flush/closer func (z *Writer) compressCurrent(flush bool) { c := z.currentBuffer if len(c) > z.blockSize { // This can never happen through the public interface. panic("len(z.currentBuffer) > z.blockSize (most likely due to concurrent Write race)") } r := result{} r.result = make(chan []byte, 1) r.notifyWritten = make(chan struct{}, 0) // Reserve a result slot select { case z.results <- r: case <-z.pushedErr: return } z.wg.Add(1) tail := z.prevTail if len(c) > tailSize { buf := z.dstPool.Get().([]byte) // Put in .compressBlock // Copy tail from current buffer before handing the buffer over to the // compressBlock goroutine. buf = append(buf[:0], c[len(c)-tailSize:]...) z.prevTail = buf } else { z.prevTail = nil } go z.compressBlock(c, tail, r, z.closed) z.currentBuffer = z.dstPool.Get().([]byte) // Put in .compressBlock z.currentBuffer = z.currentBuffer[:0] // Wait if flushing if flush { <-r.notifyWritten } } // Returns an error if it has been set. // Cannot be used by functions that are from internal goroutines. func (z *Writer) checkError() error { z.errMu.RLock() err := z.err z.errMu.RUnlock() return err } // Write writes a compressed form of p to the underlying io.Writer. The // compressed bytes are not necessarily flushed to output until // the Writer is closed or Flush() is called. // // The function will return quickly, if there are unused buffers. // The sent slice (p) is copied, and the caller is free to re-use the buffer // when the function returns. // // Errors that occur during compression will be reported later, and a nil error // does not signify that the compression succeeded (since it is most likely still running) // That means that the call that returns an error may not be the call that caused it. // Only Flush and Close functions are guaranteed to return any errors up to that point. func (z *Writer) Write(p []byte) (int, error) { if err := z.checkError(); err != nil { return 0, err } // Write the GZIP header lazily. if !z.wroteHeader { z.wroteHeader = true z.buf[0] = gzipID1 z.buf[1] = gzipID2 z.buf[2] = gzipDeflate z.buf[3] = 0 if z.Extra != nil { z.buf[3] |= 0x04 } if z.Name != "" { z.buf[3] |= 0x08 } if z.Comment != "" { z.buf[3] |= 0x10 } put4(z.buf[4:8], uint32(z.ModTime.Unix())) if z.level == BestCompression { z.buf[8] = 2 } else if z.level == BestSpeed { z.buf[8] = 4 } else { z.buf[8] = 0 } z.buf[9] = z.OS var n int var err error n, err = z.w.Write(z.buf[0:10]) if err != nil { z.pushError(err) return n, err } if z.Extra != nil { err = z.writeBytes(z.Extra) if err != nil { z.pushError(err) return n, err } } if z.Name != "" { err = z.writeString(z.Name) if err != nil { z.pushError(err) return n, err } } if z.Comment != "" { err = z.writeString(z.Comment) if err != nil { z.pushError(err) return n, err } } // Start receiving data from compressors go func() { listen := z.results var failed bool for { r, ok := <-listen // If closed, we are finished. if !ok { return } if failed { close(r.notifyWritten) continue } buf := <-r.result n, err := z.w.Write(buf) if err != nil { z.pushError(err) close(r.notifyWritten) failed = true continue } if n != len(buf) { z.pushError(fmt.Errorf("gzip: short write %d should be %d", n, len(buf))) failed = true close(r.notifyWritten) continue } z.dstPool.Put(buf) close(r.notifyWritten) } }() z.currentBuffer = z.dstPool.Get().([]byte) z.currentBuffer = z.currentBuffer[:0] } q := p for len(q) > 0 { length := len(q) if length+len(z.currentBuffer) > z.blockSize { length = z.blockSize - len(z.currentBuffer) } z.digest.Write(q[:length]) z.currentBuffer = append(z.currentBuffer, q[:length]...) if len(z.currentBuffer) > z.blockSize { panic("z.currentBuffer too large (most likely due to concurrent Write race)") } if len(z.currentBuffer) == z.blockSize { z.compressCurrent(false) if err := z.checkError(); err != nil { return len(p) - len(q), err } } z.size += length q = q[length:] } return len(p), z.checkError() } // Step 1: compresses buffer to buffer // Step 2: send writer to channel // Step 3: Close result channel to indicate we are done func (z *Writer) compressBlock(p, prevTail []byte, r result, closed bool) { defer func() { close(r.result) z.wg.Done() }() buf := z.dstPool.Get().([]byte) // Corresponding Put in .Write's result writer dest := bytes.NewBuffer(buf[:0]) compressor := z.dictFlatePool.Get().(*flate.Writer) // Put below compressor.ResetDict(dest, prevTail) compressor.Write(p) z.dstPool.Put(p) // Corresponding Get in .Write and .compressCurrent err := compressor.Flush() if err != nil { z.pushError(err) return } if closed { err = compressor.Close() if err != nil { z.pushError(err) return } } z.dictFlatePool.Put(compressor) // Get above if prevTail != nil { z.dstPool.Put(prevTail) // Get in .compressCurrent } // Read back buffer buf = dest.Bytes() r.result <- buf } // Flush flushes any pending compressed data to the underlying writer. // // It is useful mainly in compressed network protocols, to ensure that // a remote reader has enough data to reconstruct a packet. Flush does // not return until the data has been written. If the underlying // writer returns an error, Flush returns that error. // // In the terminology of the zlib library, Flush is equivalent to Z_SYNC_FLUSH. func (z *Writer) Flush() error { if err := z.checkError(); err != nil { return err } if z.closed { return nil } if !z.wroteHeader { _, err := z.Write(nil) if err != nil { return err } } // We send current block to compression z.compressCurrent(true) return z.checkError() } // UncompressedSize will return the number of bytes written. // pgzip only, not a function in the official gzip package. func (z *Writer) UncompressedSize() int { return z.size } // Close closes the Writer, flushing any unwritten data to the underlying // io.Writer, but does not close the underlying io.Writer. func (z *Writer) Close() error { if err := z.checkError(); err != nil { return err } if z.closed { return nil } z.closed = true if !z.wroteHeader { z.Write(nil) if err := z.checkError(); err != nil { return err } } z.compressCurrent(true) if err := z.checkError(); err != nil { return err } close(z.results) put4(z.buf[0:4], z.digest.Sum32()) put4(z.buf[4:8], uint32(z.size)) _, err := z.w.Write(z.buf[0:8]) if err != nil { z.pushError(err) return err } return nil } ================================================ FILE: vendor/github.com/liggitt/tabwriter/.travis.yml ================================================ language: go go: - "1.8" - "1.9" - "1.10" - "1.11" - "1.12" - master script: go test -v ./... ================================================ FILE: vendor/github.com/liggitt/tabwriter/LICENSE ================================================ Copyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/liggitt/tabwriter/README.md ================================================ This repo is a drop-in replacement for the golang [text/tabwriter](https://golang.org/pkg/text/tabwriter/) package. It is based on that package at [cf2c2ea8](https://github.com/golang/go/tree/cf2c2ea89d09d486bb018b1817c5874388038c3a/src/text/tabwriter) and inherits its license. The following additional features are supported: * `RememberWidths` flag allows remembering maximum widths seen per column even after Flush() is called. * `RememberedWidths() []int` and `SetRememberedWidths([]int) *Writer` allows obtaining and transferring remembered column width between writers. ================================================ FILE: vendor/github.com/liggitt/tabwriter/tabwriter.go ================================================ // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package tabwriter implements a write filter (tabwriter.Writer) that // translates tabbed columns in input into properly aligned text. // // It is a drop-in replacement for the golang text/tabwriter package (https://golang.org/pkg/text/tabwriter), // based on that package at https://github.com/golang/go/tree/cf2c2ea89d09d486bb018b1817c5874388038c3a // with support for additional features. // // The package is using the Elastic Tabstops algorithm described at // http://nickgravgaard.com/elastictabstops/index.html. package tabwriter import ( "io" "unicode/utf8" ) // ---------------------------------------------------------------------------- // Filter implementation // A cell represents a segment of text terminated by tabs or line breaks. // The text itself is stored in a separate buffer; cell only describes the // segment's size in bytes, its width in runes, and whether it's an htab // ('\t') terminated cell. // type cell struct { size int // cell size in bytes width int // cell width in runes htab bool // true if the cell is terminated by an htab ('\t') } // A Writer is a filter that inserts padding around tab-delimited // columns in its input to align them in the output. // // The Writer treats incoming bytes as UTF-8-encoded text consisting // of cells terminated by horizontal ('\t') or vertical ('\v') tabs, // and newline ('\n') or formfeed ('\f') characters; both newline and // formfeed act as line breaks. // // Tab-terminated cells in contiguous lines constitute a column. The // Writer inserts padding as needed to make all cells in a column have // the same width, effectively aligning the columns. It assumes that // all characters have the same width, except for tabs for which a // tabwidth must be specified. Column cells must be tab-terminated, not // tab-separated: non-tab terminated trailing text at the end of a line // forms a cell but that cell is not part of an aligned column. // For instance, in this example (where | stands for a horizontal tab): // // aaaa|bbb|d // aa |b |dd // a | // aa |cccc|eee // // the b and c are in distinct columns (the b column is not contiguous // all the way). The d and e are not in a column at all (there's no // terminating tab, nor would the column be contiguous). // // The Writer assumes that all Unicode code points have the same width; // this may not be true in some fonts or if the string contains combining // characters. // // If DiscardEmptyColumns is set, empty columns that are terminated // entirely by vertical (or "soft") tabs are discarded. Columns // terminated by horizontal (or "hard") tabs are not affected by // this flag. // // If a Writer is configured to filter HTML, HTML tags and entities // are passed through. The widths of tags and entities are // assumed to be zero (tags) and one (entities) for formatting purposes. // // A segment of text may be escaped by bracketing it with Escape // characters. The tabwriter passes escaped text segments through // unchanged. In particular, it does not interpret any tabs or line // breaks within the segment. If the StripEscape flag is set, the // Escape characters are stripped from the output; otherwise they // are passed through as well. For the purpose of formatting, the // width of the escaped text is always computed excluding the Escape // characters. // // The formfeed character acts like a newline but it also terminates // all columns in the current line (effectively calling Flush). Tab- // terminated cells in the next line start new columns. Unless found // inside an HTML tag or inside an escaped text segment, formfeed // characters appear as newlines in the output. // // The Writer must buffer input internally, because proper spacing // of one line may depend on the cells in future lines. Clients must // call Flush when done calling Write. // type Writer struct { // configuration output io.Writer minwidth int tabwidth int padding int padbytes [8]byte flags uint // current state buf []byte // collected text excluding tabs or line breaks pos int // buffer position up to which cell.width of incomplete cell has been computed cell cell // current incomplete cell; cell.width is up to buf[pos] excluding ignored sections endChar byte // terminating char of escaped sequence (Escape for escapes, '>', ';' for HTML tags/entities, or 0) lines [][]cell // list of lines; each line is a list of cells widths []int // list of column widths in runes - re-used during formatting maxwidths []int // list of max column widths in runes } // addLine adds a new line. // flushed is a hint indicating whether the underlying writer was just flushed. // If so, the previous line is not likely to be a good indicator of the new line's cells. func (b *Writer) addLine(flushed bool) { // Grow slice instead of appending, // as that gives us an opportunity // to re-use an existing []cell. if n := len(b.lines) + 1; n <= cap(b.lines) { b.lines = b.lines[:n] b.lines[n-1] = b.lines[n-1][:0] } else { b.lines = append(b.lines, nil) } if !flushed { // The previous line is probably a good indicator // of how many cells the current line will have. // If the current line's capacity is smaller than that, // abandon it and make a new one. if n := len(b.lines); n >= 2 { if prev := len(b.lines[n-2]); prev > cap(b.lines[n-1]) { b.lines[n-1] = make([]cell, 0, prev) } } } } // Reset the current state. func (b *Writer) reset() { b.buf = b.buf[:0] b.pos = 0 b.cell = cell{} b.endChar = 0 b.lines = b.lines[0:0] b.widths = b.widths[0:0] b.addLine(true) } // Internal representation (current state): // // - all text written is appended to buf; tabs and line breaks are stripped away // - at any given time there is a (possibly empty) incomplete cell at the end // (the cell starts after a tab or line break) // - cell.size is the number of bytes belonging to the cell so far // - cell.width is text width in runes of that cell from the start of the cell to // position pos; html tags and entities are excluded from this width if html // filtering is enabled // - the sizes and widths of processed text are kept in the lines list // which contains a list of cells for each line // - the widths list is a temporary list with current widths used during // formatting; it is kept in Writer because it's re-used // // |<---------- size ---------->| // | | // |<- width ->|<- ignored ->| | // | | | | // [---processed---tab------------......] // ^ ^ ^ // | | | // buf start of incomplete cell pos // Formatting can be controlled with these flags. const ( // Ignore html tags and treat entities (starting with '&' // and ending in ';') as single characters (width = 1). FilterHTML uint = 1 << iota // Strip Escape characters bracketing escaped text segments // instead of passing them through unchanged with the text. StripEscape // Force right-alignment of cell content. // Default is left-alignment. AlignRight // Handle empty columns as if they were not present in // the input in the first place. DiscardEmptyColumns // Always use tabs for indentation columns (i.e., padding of // leading empty cells on the left) independent of padchar. TabIndent // Print a vertical bar ('|') between columns (after formatting). // Discarded columns appear as zero-width columns ("||"). Debug // Remember maximum widths seen per column even after Flush() is called. RememberWidths ) // A Writer must be initialized with a call to Init. The first parameter (output) // specifies the filter output. The remaining parameters control the formatting: // // minwidth minimal cell width including any padding // tabwidth width of tab characters (equivalent number of spaces) // padding padding added to a cell before computing its width // padchar ASCII char used for padding // if padchar == '\t', the Writer will assume that the // width of a '\t' in the formatted output is tabwidth, // and cells are left-aligned independent of align_left // (for correct-looking results, tabwidth must correspond // to the tab width in the viewer displaying the result) // flags formatting control // func (b *Writer) Init(output io.Writer, minwidth, tabwidth, padding int, padchar byte, flags uint) *Writer { if minwidth < 0 || tabwidth < 0 || padding < 0 { panic("negative minwidth, tabwidth, or padding") } b.output = output b.minwidth = minwidth b.tabwidth = tabwidth b.padding = padding for i := range b.padbytes { b.padbytes[i] = padchar } if padchar == '\t' { // tab padding enforces left-alignment flags &^= AlignRight } b.flags = flags b.reset() return b } // debugging support (keep code around) func (b *Writer) dump() { pos := 0 for i, line := range b.lines { print("(", i, ") ") for _, c := range line { print("[", string(b.buf[pos:pos+c.size]), "]") pos += c.size } print("\n") } print("\n") } // local error wrapper so we can distinguish errors we want to return // as errors from genuine panics (which we don't want to return as errors) type osError struct { err error } func (b *Writer) write0(buf []byte) { n, err := b.output.Write(buf) if n != len(buf) && err == nil { err = io.ErrShortWrite } if err != nil { panic(osError{err}) } } func (b *Writer) writeN(src []byte, n int) { for n > len(src) { b.write0(src) n -= len(src) } b.write0(src[0:n]) } var ( newline = []byte{'\n'} tabs = []byte("\t\t\t\t\t\t\t\t") ) func (b *Writer) writePadding(textw, cellw int, useTabs bool) { if b.padbytes[0] == '\t' || useTabs { // padding is done with tabs if b.tabwidth == 0 { return // tabs have no width - can't do any padding } // make cellw the smallest multiple of b.tabwidth cellw = (cellw + b.tabwidth - 1) / b.tabwidth * b.tabwidth n := cellw - textw // amount of padding if n < 0 { panic("internal error") } b.writeN(tabs, (n+b.tabwidth-1)/b.tabwidth) return } // padding is done with non-tab characters b.writeN(b.padbytes[0:], cellw-textw) } var vbar = []byte{'|'} func (b *Writer) writeLines(pos0 int, line0, line1 int) (pos int) { pos = pos0 for i := line0; i < line1; i++ { line := b.lines[i] // if TabIndent is set, use tabs to pad leading empty cells useTabs := b.flags&TabIndent != 0 for j, c := range line { if j > 0 && b.flags&Debug != 0 { // indicate column break b.write0(vbar) } if c.size == 0 { // empty cell if j < len(b.widths) { b.writePadding(c.width, b.widths[j], useTabs) } } else { // non-empty cell useTabs = false if b.flags&AlignRight == 0 { // align left b.write0(b.buf[pos : pos+c.size]) pos += c.size if j < len(b.widths) { b.writePadding(c.width, b.widths[j], false) } } else { // align right if j < len(b.widths) { b.writePadding(c.width, b.widths[j], false) } b.write0(b.buf[pos : pos+c.size]) pos += c.size } } } if i+1 == len(b.lines) { // last buffered line - we don't have a newline, so just write // any outstanding buffered data b.write0(b.buf[pos : pos+b.cell.size]) pos += b.cell.size } else { // not the last line - write newline b.write0(newline) } } return } // Format the text between line0 and line1 (excluding line1); pos // is the buffer position corresponding to the beginning of line0. // Returns the buffer position corresponding to the beginning of // line1 and an error, if any. // func (b *Writer) format(pos0 int, line0, line1 int) (pos int) { pos = pos0 column := len(b.widths) for this := line0; this < line1; this++ { line := b.lines[this] if column >= len(line)-1 { continue } // cell exists in this column => this line // has more cells than the previous line // (the last cell per line is ignored because cells are // tab-terminated; the last cell per line describes the // text before the newline/formfeed and does not belong // to a column) // print unprinted lines until beginning of block pos = b.writeLines(pos, line0, this) line0 = this // column block begin width := b.minwidth // minimal column width discardable := true // true if all cells in this column are empty and "soft" for ; this < line1; this++ { line = b.lines[this] if column >= len(line)-1 { break } // cell exists in this column c := line[column] // update width if w := c.width + b.padding; w > width { width = w } // update discardable if c.width > 0 || c.htab { discardable = false } } // column block end // discard empty columns if necessary if discardable && b.flags&DiscardEmptyColumns != 0 { width = 0 } if b.flags&RememberWidths != 0 { if len(b.maxwidths) < len(b.widths) { b.maxwidths = append(b.maxwidths, b.widths[len(b.maxwidths):]...) } switch { case len(b.maxwidths) == len(b.widths): b.maxwidths = append(b.maxwidths, width) case b.maxwidths[len(b.widths)] > width: width = b.maxwidths[len(b.widths)] case b.maxwidths[len(b.widths)] < width: b.maxwidths[len(b.widths)] = width } } // format and print all columns to the right of this column // (we know the widths of this column and all columns to the left) b.widths = append(b.widths, width) // push width pos = b.format(pos, line0, this) b.widths = b.widths[0 : len(b.widths)-1] // pop width line0 = this } // print unprinted lines until end return b.writeLines(pos, line0, line1) } // Append text to current cell. func (b *Writer) append(text []byte) { b.buf = append(b.buf, text...) b.cell.size += len(text) } // Update the cell width. func (b *Writer) updateWidth() { b.cell.width += utf8.RuneCount(b.buf[b.pos:]) b.pos = len(b.buf) } // To escape a text segment, bracket it with Escape characters. // For instance, the tab in this string "Ignore this tab: \xff\t\xff" // does not terminate a cell and constitutes a single character of // width one for formatting purposes. // // The value 0xff was chosen because it cannot appear in a valid UTF-8 sequence. // const Escape = '\xff' // Start escaped mode. func (b *Writer) startEscape(ch byte) { switch ch { case Escape: b.endChar = Escape case '<': b.endChar = '>' case '&': b.endChar = ';' } } // Terminate escaped mode. If the escaped text was an HTML tag, its width // is assumed to be zero for formatting purposes; if it was an HTML entity, // its width is assumed to be one. In all other cases, the width is the // unicode width of the text. // func (b *Writer) endEscape() { switch b.endChar { case Escape: b.updateWidth() if b.flags&StripEscape == 0 { b.cell.width -= 2 // don't count the Escape chars } case '>': // tag of zero width case ';': b.cell.width++ // entity, count as one rune } b.pos = len(b.buf) b.endChar = 0 } // Terminate the current cell by adding it to the list of cells of the // current line. Returns the number of cells in that line. // func (b *Writer) terminateCell(htab bool) int { b.cell.htab = htab line := &b.lines[len(b.lines)-1] *line = append(*line, b.cell) b.cell = cell{} return len(*line) } func handlePanic(err *error, op string) { if e := recover(); e != nil { if nerr, ok := e.(osError); ok { *err = nerr.err return } panic("tabwriter: panic during " + op) } } // RememberedWidths returns a copy of the remembered per-column maximum widths. // Requires use of the RememberWidths flag, and is not threadsafe. func (b *Writer) RememberedWidths() []int { retval := make([]int, len(b.maxwidths)) copy(retval, b.maxwidths) return retval } // SetRememberedWidths sets the remembered per-column maximum widths. // Requires use of the RememberWidths flag, and is not threadsafe. func (b *Writer) SetRememberedWidths(widths []int) *Writer { b.maxwidths = make([]int, len(widths)) copy(b.maxwidths, widths) return b } // Flush should be called after the last call to Write to ensure // that any data buffered in the Writer is written to output. Any // incomplete escape sequence at the end is considered // complete for formatting purposes. func (b *Writer) Flush() error { return b.flush() } func (b *Writer) flush() (err error) { defer b.reset() // even in the presence of errors defer handlePanic(&err, "Flush") // add current cell if not empty if b.cell.size > 0 { if b.endChar != 0 { // inside escape - terminate it even if incomplete b.endEscape() } b.terminateCell(false) } // format contents of buffer b.format(0, 0, len(b.lines)) return nil } var hbar = []byte("---\n") // Write writes buf to the writer b. // The only errors returned are ones encountered // while writing to the underlying output stream. // func (b *Writer) Write(buf []byte) (n int, err error) { defer handlePanic(&err, "Write") // split text into cells n = 0 for i, ch := range buf { if b.endChar == 0 { // outside escape switch ch { case '\t', '\v', '\n', '\f': // end of cell b.append(buf[n:i]) b.updateWidth() n = i + 1 // ch consumed ncells := b.terminateCell(ch == '\t') if ch == '\n' || ch == '\f' { // terminate line b.addLine(ch == '\f') if ch == '\f' || ncells == 1 { // A '\f' always forces a flush. Otherwise, if the previous // line has only one cell which does not have an impact on // the formatting of the following lines (the last cell per // line is ignored by format()), thus we can flush the // Writer contents. if err = b.Flush(); err != nil { return } if ch == '\f' && b.flags&Debug != 0 { // indicate section break b.write0(hbar) } } } case Escape: // start of escaped sequence b.append(buf[n:i]) b.updateWidth() n = i if b.flags&StripEscape != 0 { n++ // strip Escape } b.startEscape(Escape) case '<', '&': // possibly an html tag/entity if b.flags&FilterHTML != 0 { // begin of tag/entity b.append(buf[n:i]) b.updateWidth() n = i b.startEscape(ch) } } } else { // inside escape if ch == b.endChar { // end of tag/entity j := i + 1 if ch == Escape && b.flags&StripEscape != 0 { j = i // strip Escape } b.append(buf[n:j]) n = i + 1 // ch consumed b.endEscape() } } } // append leftover text b.append(buf[n:]) n = len(buf) return } // NewWriter allocates and initializes a new tabwriter.Writer. // The parameters are the same as for the Init function. // func NewWriter(output io.Writer, minwidth, tabwidth, padding int, padchar byte, flags uint) *Writer { return new(Writer).Init(output, minwidth, tabwidth, padding, padchar, flags) } ================================================ FILE: vendor/github.com/mailru/easyjson/LICENSE ================================================ Copyright (c) 2016 Mail.Ru Group Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/mailru/easyjson/buffer/pool.go ================================================ // Package buffer implements a buffer for serialization, consisting of a chain of []byte-s to // reduce copying and to allow reuse of individual chunks. package buffer import ( "io" "net" "sync" ) // PoolConfig contains configuration for the allocation and reuse strategy. type PoolConfig struct { StartSize int // Minimum chunk size that is allocated. PooledSize int // Minimum chunk size that is reused, reusing chunks too small will result in overhead. MaxSize int // Maximum chunk size that will be allocated. } var config = PoolConfig{ StartSize: 128, PooledSize: 512, MaxSize: 32768, } // Reuse pool: chunk size -> pool. var buffers = map[int]*sync.Pool{} func initBuffers() { for l := config.PooledSize; l <= config.MaxSize; l *= 2 { buffers[l] = new(sync.Pool) } } func init() { initBuffers() } // Init sets up a non-default pooling and allocation strategy. Should be run before serialization is done. func Init(cfg PoolConfig) { config = cfg initBuffers() } // putBuf puts a chunk to reuse pool if it can be reused. func putBuf(buf []byte) { size := cap(buf) if size < config.PooledSize { return } if c := buffers[size]; c != nil { c.Put(buf[:0]) } } // getBuf gets a chunk from reuse pool or creates a new one if reuse failed. func getBuf(size int) []byte { if size >= config.PooledSize { if c := buffers[size]; c != nil { v := c.Get() if v != nil { return v.([]byte) } } } return make([]byte, 0, size) } // Buffer is a buffer optimized for serialization without extra copying. type Buffer struct { // Buf is the current chunk that can be used for serialization. Buf []byte toPool []byte bufs [][]byte } // EnsureSpace makes sure that the current chunk contains at least s free bytes, // possibly creating a new chunk. func (b *Buffer) EnsureSpace(s int) { if cap(b.Buf)-len(b.Buf) < s { b.ensureSpaceSlow(s) } } func (b *Buffer) ensureSpaceSlow(s int) { l := len(b.Buf) if l > 0 { if cap(b.toPool) != cap(b.Buf) { // Chunk was reallocated, toPool can be pooled. putBuf(b.toPool) } if cap(b.bufs) == 0 { b.bufs = make([][]byte, 0, 8) } b.bufs = append(b.bufs, b.Buf) l = cap(b.toPool) * 2 } else { l = config.StartSize } if l > config.MaxSize { l = config.MaxSize } b.Buf = getBuf(l) b.toPool = b.Buf } // AppendByte appends a single byte to buffer. func (b *Buffer) AppendByte(data byte) { b.EnsureSpace(1) b.Buf = append(b.Buf, data) } // AppendBytes appends a byte slice to buffer. func (b *Buffer) AppendBytes(data []byte) { if len(data) <= cap(b.Buf)-len(b.Buf) { b.Buf = append(b.Buf, data...) // fast path } else { b.appendBytesSlow(data) } } func (b *Buffer) appendBytesSlow(data []byte) { for len(data) > 0 { b.EnsureSpace(1) sz := cap(b.Buf) - len(b.Buf) if sz > len(data) { sz = len(data) } b.Buf = append(b.Buf, data[:sz]...) data = data[sz:] } } // AppendString appends a string to buffer. func (b *Buffer) AppendString(data string) { if len(data) <= cap(b.Buf)-len(b.Buf) { b.Buf = append(b.Buf, data...) // fast path } else { b.appendStringSlow(data) } } func (b *Buffer) appendStringSlow(data string) { for len(data) > 0 { b.EnsureSpace(1) sz := cap(b.Buf) - len(b.Buf) if sz > len(data) { sz = len(data) } b.Buf = append(b.Buf, data[:sz]...) data = data[sz:] } } // Size computes the size of a buffer by adding sizes of every chunk. func (b *Buffer) Size() int { size := len(b.Buf) for _, buf := range b.bufs { size += len(buf) } return size } // DumpTo outputs the contents of a buffer to a writer and resets the buffer. func (b *Buffer) DumpTo(w io.Writer) (written int, err error) { bufs := net.Buffers(b.bufs) if len(b.Buf) > 0 { bufs = append(bufs, b.Buf) } n, err := bufs.WriteTo(w) for _, buf := range b.bufs { putBuf(buf) } putBuf(b.toPool) b.bufs = nil b.Buf = nil b.toPool = nil return int(n), err } // BuildBytes creates a single byte slice with all the contents of the buffer. Data is // copied if it does not fit in a single chunk. You can optionally provide one byte // slice as argument that it will try to reuse. func (b *Buffer) BuildBytes(reuse ...[]byte) []byte { if len(b.bufs) == 0 { ret := b.Buf b.toPool = nil b.Buf = nil return ret } var ret []byte size := b.Size() // If we got a buffer as argument and it is big enough, reuse it. if len(reuse) == 1 && cap(reuse[0]) >= size { ret = reuse[0][:0] } else { ret = make([]byte, 0, size) } for _, buf := range b.bufs { ret = append(ret, buf...) putBuf(buf) } ret = append(ret, b.Buf...) putBuf(b.toPool) b.bufs = nil b.toPool = nil b.Buf = nil return ret } type readCloser struct { offset int bufs [][]byte } func (r *readCloser) Read(p []byte) (n int, err error) { for _, buf := range r.bufs { // Copy as much as we can. x := copy(p[n:], buf[r.offset:]) n += x // Increment how much we filled. // Did we empty the whole buffer? if r.offset+x == len(buf) { // On to the next buffer. r.offset = 0 r.bufs = r.bufs[1:] // We can release this buffer. putBuf(buf) } else { r.offset += x } if n == len(p) { break } } // No buffers left or nothing read? if len(r.bufs) == 0 { err = io.EOF } return } func (r *readCloser) Close() error { // Release all remaining buffers. for _, buf := range r.bufs { putBuf(buf) } // In case Close gets called multiple times. r.bufs = nil return nil } // ReadCloser creates an io.ReadCloser with all the contents of the buffer. func (b *Buffer) ReadCloser() io.ReadCloser { ret := &readCloser{0, append(b.bufs, b.Buf)} b.bufs = nil b.toPool = nil b.Buf = nil return ret } ================================================ FILE: vendor/github.com/mailru/easyjson/jlexer/bytestostr.go ================================================ // This file will only be included to the build if neither // easyjson_nounsafe nor appengine build tag is set. See README notes // for more details. //+build !easyjson_nounsafe //+build !appengine package jlexer import ( "reflect" "unsafe" ) // bytesToStr creates a string pointing at the slice to avoid copying. // // Warning: the string returned by the function should be used with care, as the whole input data // chunk may be either blocked from being freed by GC because of a single string or the buffer.Data // may be garbage-collected even when the string exists. func bytesToStr(data []byte) string { h := (*reflect.SliceHeader)(unsafe.Pointer(&data)) shdr := reflect.StringHeader{Data: h.Data, Len: h.Len} return *(*string)(unsafe.Pointer(&shdr)) } ================================================ FILE: vendor/github.com/mailru/easyjson/jlexer/bytestostr_nounsafe.go ================================================ // This file is included to the build if any of the buildtags below // are defined. Refer to README notes for more details. //+build easyjson_nounsafe appengine package jlexer // bytesToStr creates a string normally from []byte // // Note that this method is roughly 1.5x slower than using the 'unsafe' method. func bytesToStr(data []byte) string { return string(data) } ================================================ FILE: vendor/github.com/mailru/easyjson/jlexer/error.go ================================================ package jlexer import "fmt" // LexerError implements the error interface and represents all possible errors that can be // generated during parsing the JSON data. type LexerError struct { Reason string Offset int Data string } func (l *LexerError) Error() string { return fmt.Sprintf("parse error: %s near offset %d of '%s'", l.Reason, l.Offset, l.Data) } ================================================ FILE: vendor/github.com/mailru/easyjson/jlexer/lexer.go ================================================ // Package jlexer contains a JSON lexer implementation. // // It is expected that it is mostly used with generated parser code, so the interface is tuned // for a parser that knows what kind of data is expected. package jlexer import ( "bytes" "encoding/base64" "encoding/json" "errors" "fmt" "io" "strconv" "unicode" "unicode/utf16" "unicode/utf8" "github.com/josharian/intern" ) // tokenKind determines type of a token. type tokenKind byte const ( tokenUndef tokenKind = iota // No token. tokenDelim // Delimiter: one of '{', '}', '[' or ']'. tokenString // A string literal, e.g. "abc\u1234" tokenNumber // Number literal, e.g. 1.5e5 tokenBool // Boolean literal: true or false. tokenNull // null keyword. ) // token describes a single token: type, position in the input and value. type token struct { kind tokenKind // Type of a token. boolValue bool // Value if a boolean literal token. byteValueCloned bool // true if byteValue was allocated and does not refer to original json body byteValue []byte // Raw value of a token. delimValue byte } // Lexer is a JSON lexer: it iterates over JSON tokens in a byte slice. type Lexer struct { Data []byte // Input data given to the lexer. start int // Start of the current token. pos int // Current unscanned position in the input stream. token token // Last scanned token, if token.kind != tokenUndef. firstElement bool // Whether current element is the first in array or an object. wantSep byte // A comma or a colon character, which need to occur before a token. UseMultipleErrors bool // If we want to use multiple errors. fatalError error // Fatal error occurred during lexing. It is usually a syntax error. multipleErrors []*LexerError // Semantic errors occurred during lexing. Marshalling will be continued after finding this errors. } // FetchToken scans the input for the next token. func (r *Lexer) FetchToken() { r.token.kind = tokenUndef r.start = r.pos // Check if r.Data has r.pos element // If it doesn't, it mean corrupted input data if len(r.Data) < r.pos { r.errParse("Unexpected end of data") return } // Determine the type of a token by skipping whitespace and reading the // first character. for _, c := range r.Data[r.pos:] { switch c { case ':', ',': if r.wantSep == c { r.pos++ r.start++ r.wantSep = 0 } else { r.errSyntax() } case ' ', '\t', '\r', '\n': r.pos++ r.start++ case '"': if r.wantSep != 0 { r.errSyntax() } r.token.kind = tokenString r.fetchString() return case '{', '[': if r.wantSep != 0 { r.errSyntax() } r.firstElement = true r.token.kind = tokenDelim r.token.delimValue = r.Data[r.pos] r.pos++ return case '}', ']': if !r.firstElement && (r.wantSep != ',') { r.errSyntax() } r.wantSep = 0 r.token.kind = tokenDelim r.token.delimValue = r.Data[r.pos] r.pos++ return case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-': if r.wantSep != 0 { r.errSyntax() } r.token.kind = tokenNumber r.fetchNumber() return case 'n': if r.wantSep != 0 { r.errSyntax() } r.token.kind = tokenNull r.fetchNull() return case 't': if r.wantSep != 0 { r.errSyntax() } r.token.kind = tokenBool r.token.boolValue = true r.fetchTrue() return case 'f': if r.wantSep != 0 { r.errSyntax() } r.token.kind = tokenBool r.token.boolValue = false r.fetchFalse() return default: r.errSyntax() return } } r.fatalError = io.EOF return } // isTokenEnd returns true if the char can follow a non-delimiter token func isTokenEnd(c byte) bool { return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '[' || c == ']' || c == '{' || c == '}' || c == ',' || c == ':' } // fetchNull fetches and checks remaining bytes of null keyword. func (r *Lexer) fetchNull() { r.pos += 4 if r.pos > len(r.Data) || r.Data[r.pos-3] != 'u' || r.Data[r.pos-2] != 'l' || r.Data[r.pos-1] != 'l' || (r.pos != len(r.Data) && !isTokenEnd(r.Data[r.pos])) { r.pos -= 4 r.errSyntax() } } // fetchTrue fetches and checks remaining bytes of true keyword. func (r *Lexer) fetchTrue() { r.pos += 4 if r.pos > len(r.Data) || r.Data[r.pos-3] != 'r' || r.Data[r.pos-2] != 'u' || r.Data[r.pos-1] != 'e' || (r.pos != len(r.Data) && !isTokenEnd(r.Data[r.pos])) { r.pos -= 4 r.errSyntax() } } // fetchFalse fetches and checks remaining bytes of false keyword. func (r *Lexer) fetchFalse() { r.pos += 5 if r.pos > len(r.Data) || r.Data[r.pos-4] != 'a' || r.Data[r.pos-3] != 'l' || r.Data[r.pos-2] != 's' || r.Data[r.pos-1] != 'e' || (r.pos != len(r.Data) && !isTokenEnd(r.Data[r.pos])) { r.pos -= 5 r.errSyntax() } } // fetchNumber scans a number literal token. func (r *Lexer) fetchNumber() { hasE := false afterE := false hasDot := false r.pos++ for i, c := range r.Data[r.pos:] { switch { case c >= '0' && c <= '9': afterE = false case c == '.' && !hasDot: hasDot = true case (c == 'e' || c == 'E') && !hasE: hasE = true hasDot = true afterE = true case (c == '+' || c == '-') && afterE: afterE = false default: r.pos += i if !isTokenEnd(c) { r.errSyntax() } else { r.token.byteValue = r.Data[r.start:r.pos] } return } } r.pos = len(r.Data) r.token.byteValue = r.Data[r.start:] } // findStringLen tries to scan into the string literal for ending quote char to determine required size. // The size will be exact if no escapes are present and may be inexact if there are escaped chars. func findStringLen(data []byte) (isValid bool, length int) { for { idx := bytes.IndexByte(data, '"') if idx == -1 { return false, len(data) } if idx == 0 || (idx > 0 && data[idx-1] != '\\') { return true, length + idx } // count \\\\\\\ sequences. even number of slashes means quote is not really escaped cnt := 1 for idx-cnt-1 >= 0 && data[idx-cnt-1] == '\\' { cnt++ } if cnt%2 == 0 { return true, length + idx } length += idx + 1 data = data[idx+1:] } } // unescapeStringToken performs unescaping of string token. // if no escaping is needed, original string is returned, otherwise - a new one allocated func (r *Lexer) unescapeStringToken() (err error) { data := r.token.byteValue var unescapedData []byte for { i := bytes.IndexByte(data, '\\') if i == -1 { break } escapedRune, escapedBytes, err := decodeEscape(data[i:]) if err != nil { r.errParse(err.Error()) return err } if unescapedData == nil { unescapedData = make([]byte, 0, len(r.token.byteValue)) } var d [4]byte s := utf8.EncodeRune(d[:], escapedRune) unescapedData = append(unescapedData, data[:i]...) unescapedData = append(unescapedData, d[:s]...) data = data[i+escapedBytes:] } if unescapedData != nil { r.token.byteValue = append(unescapedData, data...) r.token.byteValueCloned = true } return } // getu4 decodes \uXXXX from the beginning of s, returning the hex value, // or it returns -1. func getu4(s []byte) rune { if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { return -1 } var val rune for i := 2; i < len(s) && i < 6; i++ { var v byte c := s[i] switch c { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': v = c - '0' case 'a', 'b', 'c', 'd', 'e', 'f': v = c - 'a' + 10 case 'A', 'B', 'C', 'D', 'E', 'F': v = c - 'A' + 10 default: return -1 } val <<= 4 val |= rune(v) } return val } // decodeEscape processes a single escape sequence and returns number of bytes processed. func decodeEscape(data []byte) (decoded rune, bytesProcessed int, err error) { if len(data) < 2 { return 0, 0, errors.New("incorrect escape symbol \\ at the end of token") } c := data[1] switch c { case '"', '/', '\\': return rune(c), 2, nil case 'b': return '\b', 2, nil case 'f': return '\f', 2, nil case 'n': return '\n', 2, nil case 'r': return '\r', 2, nil case 't': return '\t', 2, nil case 'u': rr := getu4(data) if rr < 0 { return 0, 0, errors.New("incorrectly escaped \\uXXXX sequence") } read := 6 if utf16.IsSurrogate(rr) { rr1 := getu4(data[read:]) if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar { read += 6 rr = dec } else { rr = unicode.ReplacementChar } } return rr, read, nil } return 0, 0, errors.New("incorrectly escaped bytes") } // fetchString scans a string literal token. func (r *Lexer) fetchString() { r.pos++ data := r.Data[r.pos:] isValid, length := findStringLen(data) if !isValid { r.pos += length r.errParse("unterminated string literal") return } r.token.byteValue = data[:length] r.pos += length + 1 // skip closing '"' as well } // scanToken scans the next token if no token is currently available in the lexer. func (r *Lexer) scanToken() { if r.token.kind != tokenUndef || r.fatalError != nil { return } r.FetchToken() } // consume resets the current token to allow scanning the next one. func (r *Lexer) consume() { r.token.kind = tokenUndef r.token.byteValueCloned = false r.token.delimValue = 0 } // Ok returns true if no error (including io.EOF) was encountered during scanning. func (r *Lexer) Ok() bool { return r.fatalError == nil } const maxErrorContextLen = 13 func (r *Lexer) errParse(what string) { if r.fatalError == nil { var str string if len(r.Data)-r.pos <= maxErrorContextLen { str = string(r.Data) } else { str = string(r.Data[r.pos:r.pos+maxErrorContextLen-3]) + "..." } r.fatalError = &LexerError{ Reason: what, Offset: r.pos, Data: str, } } } func (r *Lexer) errSyntax() { r.errParse("syntax error") } func (r *Lexer) errInvalidToken(expected string) { if r.fatalError != nil { return } if r.UseMultipleErrors { r.pos = r.start r.consume() r.SkipRecursive() switch expected { case "[": r.token.delimValue = ']' r.token.kind = tokenDelim case "{": r.token.delimValue = '}' r.token.kind = tokenDelim } r.addNonfatalError(&LexerError{ Reason: fmt.Sprintf("expected %s", expected), Offset: r.start, Data: string(r.Data[r.start:r.pos]), }) return } var str string if len(r.token.byteValue) <= maxErrorContextLen { str = string(r.token.byteValue) } else { str = string(r.token.byteValue[:maxErrorContextLen-3]) + "..." } r.fatalError = &LexerError{ Reason: fmt.Sprintf("expected %s", expected), Offset: r.pos, Data: str, } } func (r *Lexer) GetPos() int { return r.pos } // Delim consumes a token and verifies that it is the given delimiter. func (r *Lexer) Delim(c byte) { if r.token.kind == tokenUndef && r.Ok() { r.FetchToken() } if !r.Ok() || r.token.delimValue != c { r.consume() // errInvalidToken can change token if UseMultipleErrors is enabled. r.errInvalidToken(string([]byte{c})) } else { r.consume() } } // IsDelim returns true if there was no scanning error and next token is the given delimiter. func (r *Lexer) IsDelim(c byte) bool { if r.token.kind == tokenUndef && r.Ok() { r.FetchToken() } return !r.Ok() || r.token.delimValue == c } // Null verifies that the next token is null and consumes it. func (r *Lexer) Null() { if r.token.kind == tokenUndef && r.Ok() { r.FetchToken() } if !r.Ok() || r.token.kind != tokenNull { r.errInvalidToken("null") } r.consume() } // IsNull returns true if the next token is a null keyword. func (r *Lexer) IsNull() bool { if r.token.kind == tokenUndef && r.Ok() { r.FetchToken() } return r.Ok() && r.token.kind == tokenNull } // Skip skips a single token. func (r *Lexer) Skip() { if r.token.kind == tokenUndef && r.Ok() { r.FetchToken() } r.consume() } // SkipRecursive skips next array or object completely, or just skips a single token if not // an array/object. // // Note: no syntax validation is performed on the skipped data. func (r *Lexer) SkipRecursive() { r.scanToken() var start, end byte startPos := r.start switch r.token.delimValue { case '{': start, end = '{', '}' case '[': start, end = '[', ']' default: r.consume() return } r.consume() level := 1 inQuotes := false wasEscape := false for i, c := range r.Data[r.pos:] { switch { case c == start && !inQuotes: level++ case c == end && !inQuotes: level-- if level == 0 { r.pos += i + 1 if !json.Valid(r.Data[startPos:r.pos]) { r.pos = len(r.Data) r.fatalError = &LexerError{ Reason: "skipped array/object json value is invalid", Offset: r.pos, Data: string(r.Data[r.pos:]), } } return } case c == '\\' && inQuotes: wasEscape = !wasEscape continue case c == '"' && inQuotes: inQuotes = wasEscape case c == '"': inQuotes = true } wasEscape = false } r.pos = len(r.Data) r.fatalError = &LexerError{ Reason: "EOF reached while skipping array/object or token", Offset: r.pos, Data: string(r.Data[r.pos:]), } } // Raw fetches the next item recursively as a data slice func (r *Lexer) Raw() []byte { r.SkipRecursive() if !r.Ok() { return nil } return r.Data[r.start:r.pos] } // IsStart returns whether the lexer is positioned at the start // of an input string. func (r *Lexer) IsStart() bool { return r.pos == 0 } // Consumed reads all remaining bytes from the input, publishing an error if // there is anything but whitespace remaining. func (r *Lexer) Consumed() { if r.pos > len(r.Data) || !r.Ok() { return } for _, c := range r.Data[r.pos:] { if c != ' ' && c != '\t' && c != '\r' && c != '\n' { r.AddError(&LexerError{ Reason: "invalid character '" + string(c) + "' after top-level value", Offset: r.pos, Data: string(r.Data[r.pos:]), }) return } r.pos++ r.start++ } } func (r *Lexer) unsafeString(skipUnescape bool) (string, []byte) { if r.token.kind == tokenUndef && r.Ok() { r.FetchToken() } if !r.Ok() || r.token.kind != tokenString { r.errInvalidToken("string") return "", nil } if !skipUnescape { if err := r.unescapeStringToken(); err != nil { r.errInvalidToken("string") return "", nil } } bytes := r.token.byteValue ret := bytesToStr(r.token.byteValue) r.consume() return ret, bytes } // UnsafeString returns the string value if the token is a string literal. // // Warning: returned string may point to the input buffer, so the string should not outlive // the input buffer. Intended pattern of usage is as an argument to a switch statement. func (r *Lexer) UnsafeString() string { ret, _ := r.unsafeString(false) return ret } // UnsafeBytes returns the byte slice if the token is a string literal. func (r *Lexer) UnsafeBytes() []byte { _, ret := r.unsafeString(false) return ret } // UnsafeFieldName returns current member name string token func (r *Lexer) UnsafeFieldName(skipUnescape bool) string { ret, _ := r.unsafeString(skipUnescape) return ret } // String reads a string literal. func (r *Lexer) String() string { if r.token.kind == tokenUndef && r.Ok() { r.FetchToken() } if !r.Ok() || r.token.kind != tokenString { r.errInvalidToken("string") return "" } if err := r.unescapeStringToken(); err != nil { r.errInvalidToken("string") return "" } var ret string if r.token.byteValueCloned { ret = bytesToStr(r.token.byteValue) } else { ret = string(r.token.byteValue) } r.consume() return ret } // StringIntern reads a string literal, and performs string interning on it. func (r *Lexer) StringIntern() string { if r.token.kind == tokenUndef && r.Ok() { r.FetchToken() } if !r.Ok() || r.token.kind != tokenString { r.errInvalidToken("string") return "" } if err := r.unescapeStringToken(); err != nil { r.errInvalidToken("string") return "" } ret := intern.Bytes(r.token.byteValue) r.consume() return ret } // Bytes reads a string literal and base64 decodes it into a byte slice. func (r *Lexer) Bytes() []byte { if r.token.kind == tokenUndef && r.Ok() { r.FetchToken() } if !r.Ok() || r.token.kind != tokenString { r.errInvalidToken("string") return nil } if err := r.unescapeStringToken(); err != nil { r.errInvalidToken("string") return nil } ret := make([]byte, base64.StdEncoding.DecodedLen(len(r.token.byteValue))) n, err := base64.StdEncoding.Decode(ret, r.token.byteValue) if err != nil { r.fatalError = &LexerError{ Reason: err.Error(), } return nil } r.consume() return ret[:n] } // Bool reads a true or false boolean keyword. func (r *Lexer) Bool() bool { if r.token.kind == tokenUndef && r.Ok() { r.FetchToken() } if !r.Ok() || r.token.kind != tokenBool { r.errInvalidToken("bool") return false } ret := r.token.boolValue r.consume() return ret } func (r *Lexer) number() string { if r.token.kind == tokenUndef && r.Ok() { r.FetchToken() } if !r.Ok() || r.token.kind != tokenNumber { r.errInvalidToken("number") return "" } ret := bytesToStr(r.token.byteValue) r.consume() return ret } func (r *Lexer) Uint8() uint8 { s := r.number() if !r.Ok() { return 0 } n, err := strconv.ParseUint(s, 10, 8) if err != nil { r.addNonfatalError(&LexerError{ Offset: r.start, Reason: err.Error(), Data: s, }) } return uint8(n) } func (r *Lexer) Uint16() uint16 { s := r.number() if !r.Ok() { return 0 } n, err := strconv.ParseUint(s, 10, 16) if err != nil { r.addNonfatalError(&LexerError{ Offset: r.start, Reason: err.Error(), Data: s, }) } return uint16(n) } func (r *Lexer) Uint32() uint32 { s := r.number() if !r.Ok() { return 0 } n, err := strconv.ParseUint(s, 10, 32) if err != nil { r.addNonfatalError(&LexerError{ Offset: r.start, Reason: err.Error(), Data: s, }) } return uint32(n) } func (r *Lexer) Uint64() uint64 { s := r.number() if !r.Ok() { return 0 } n, err := strconv.ParseUint(s, 10, 64) if err != nil { r.addNonfatalError(&LexerError{ Offset: r.start, Reason: err.Error(), Data: s, }) } return n } func (r *Lexer) Uint() uint { return uint(r.Uint64()) } func (r *Lexer) Int8() int8 { s := r.number() if !r.Ok() { return 0 } n, err := strconv.ParseInt(s, 10, 8) if err != nil { r.addNonfatalError(&LexerError{ Offset: r.start, Reason: err.Error(), Data: s, }) } return int8(n) } func (r *Lexer) Int16() int16 { s := r.number() if !r.Ok() { return 0 } n, err := strconv.ParseInt(s, 10, 16) if err != nil { r.addNonfatalError(&LexerError{ Offset: r.start, Reason: err.Error(), Data: s, }) } return int16(n) } func (r *Lexer) Int32() int32 { s := r.number() if !r.Ok() { return 0 } n, err := strconv.ParseInt(s, 10, 32) if err != nil { r.addNonfatalError(&LexerError{ Offset: r.start, Reason: err.Error(), Data: s, }) } return int32(n) } func (r *Lexer) Int64() int64 { s := r.number() if !r.Ok() { return 0 } n, err := strconv.ParseInt(s, 10, 64) if err != nil { r.addNonfatalError(&LexerError{ Offset: r.start, Reason: err.Error(), Data: s, }) } return n } func (r *Lexer) Int() int { return int(r.Int64()) } func (r *Lexer) Uint8Str() uint8 { s, b := r.unsafeString(false) if !r.Ok() { return 0 } n, err := strconv.ParseUint(s, 10, 8) if err != nil { r.addNonfatalError(&LexerError{ Offset: r.start, Reason: err.Error(), Data: string(b), }) } return uint8(n) } func (r *Lexer) Uint16Str() uint16 { s, b := r.unsafeString(false) if !r.Ok() { return 0 } n, err := strconv.ParseUint(s, 10, 16) if err != nil { r.addNonfatalError(&LexerError{ Offset: r.start, Reason: err.Error(), Data: string(b), }) } return uint16(n) } func (r *Lexer) Uint32Str() uint32 { s, b := r.unsafeString(false) if !r.Ok() { return 0 } n, err := strconv.ParseUint(s, 10, 32) if err != nil { r.addNonfatalError(&LexerError{ Offset: r.start, Reason: err.Error(), Data: string(b), }) } return uint32(n) } func (r *Lexer) Uint64Str() uint64 { s, b := r.unsafeString(false) if !r.Ok() { return 0 } n, err := strconv.ParseUint(s, 10, 64) if err != nil { r.addNonfatalError(&LexerError{ Offset: r.start, Reason: err.Error(), Data: string(b), }) } return n } func (r *Lexer) UintStr() uint { return uint(r.Uint64Str()) } func (r *Lexer) UintptrStr() uintptr { return uintptr(r.Uint64Str()) } func (r *Lexer) Int8Str() int8 { s, b := r.unsafeString(false) if !r.Ok() { return 0 } n, err := strconv.ParseInt(s, 10, 8) if err != nil { r.addNonfatalError(&LexerError{ Offset: r.start, Reason: err.Error(), Data: string(b), }) } return int8(n) } func (r *Lexer) Int16Str() int16 { s, b := r.unsafeString(false) if !r.Ok() { return 0 } n, err := strconv.ParseInt(s, 10, 16) if err != nil { r.addNonfatalError(&LexerError{ Offset: r.start, Reason: err.Error(), Data: string(b), }) } return int16(n) } func (r *Lexer) Int32Str() int32 { s, b := r.unsafeString(false) if !r.Ok() { return 0 } n, err := strconv.ParseInt(s, 10, 32) if err != nil { r.addNonfatalError(&LexerError{ Offset: r.start, Reason: err.Error(), Data: string(b), }) } return int32(n) } func (r *Lexer) Int64Str() int64 { s, b := r.unsafeString(false) if !r.Ok() { return 0 } n, err := strconv.ParseInt(s, 10, 64) if err != nil { r.addNonfatalError(&LexerError{ Offset: r.start, Reason: err.Error(), Data: string(b), }) } return n } func (r *Lexer) IntStr() int { return int(r.Int64Str()) } func (r *Lexer) Float32() float32 { s := r.number() if !r.Ok() { return 0 } n, err := strconv.ParseFloat(s, 32) if err != nil { r.addNonfatalError(&LexerError{ Offset: r.start, Reason: err.Error(), Data: s, }) } return float32(n) } func (r *Lexer) Float32Str() float32 { s, b := r.unsafeString(false) if !r.Ok() { return 0 } n, err := strconv.ParseFloat(s, 32) if err != nil { r.addNonfatalError(&LexerError{ Offset: r.start, Reason: err.Error(), Data: string(b), }) } return float32(n) } func (r *Lexer) Float64() float64 { s := r.number() if !r.Ok() { return 0 } n, err := strconv.ParseFloat(s, 64) if err != nil { r.addNonfatalError(&LexerError{ Offset: r.start, Reason: err.Error(), Data: s, }) } return n } func (r *Lexer) Float64Str() float64 { s, b := r.unsafeString(false) if !r.Ok() { return 0 } n, err := strconv.ParseFloat(s, 64) if err != nil { r.addNonfatalError(&LexerError{ Offset: r.start, Reason: err.Error(), Data: string(b), }) } return n } func (r *Lexer) Error() error { return r.fatalError } func (r *Lexer) AddError(e error) { if r.fatalError == nil { r.fatalError = e } } func (r *Lexer) AddNonFatalError(e error) { r.addNonfatalError(&LexerError{ Offset: r.start, Data: string(r.Data[r.start:r.pos]), Reason: e.Error(), }) } func (r *Lexer) addNonfatalError(err *LexerError) { if r.UseMultipleErrors { // We don't want to add errors with the same offset. if len(r.multipleErrors) != 0 && r.multipleErrors[len(r.multipleErrors)-1].Offset == err.Offset { return } r.multipleErrors = append(r.multipleErrors, err) return } r.fatalError = err } func (r *Lexer) GetNonFatalErrors() []*LexerError { return r.multipleErrors } // JsonNumber fetches and json.Number from 'encoding/json' package. // Both int, float or string, contains them are valid values func (r *Lexer) JsonNumber() json.Number { if r.token.kind == tokenUndef && r.Ok() { r.FetchToken() } if !r.Ok() { r.errInvalidToken("json.Number") return json.Number("") } switch r.token.kind { case tokenString: return json.Number(r.String()) case tokenNumber: return json.Number(r.Raw()) case tokenNull: r.Null() return json.Number("") default: r.errSyntax() return json.Number("") } } // Interface fetches an interface{} analogous to the 'encoding/json' package. func (r *Lexer) Interface() interface{} { if r.token.kind == tokenUndef && r.Ok() { r.FetchToken() } if !r.Ok() { return nil } switch r.token.kind { case tokenString: return r.String() case tokenNumber: return r.Float64() case tokenBool: return r.Bool() case tokenNull: r.Null() return nil } if r.token.delimValue == '{' { r.consume() ret := map[string]interface{}{} for !r.IsDelim('}') { key := r.String() r.WantColon() ret[key] = r.Interface() r.WantComma() } r.Delim('}') if r.Ok() { return ret } else { return nil } } else if r.token.delimValue == '[' { r.consume() ret := []interface{}{} for !r.IsDelim(']') { ret = append(ret, r.Interface()) r.WantComma() } r.Delim(']') if r.Ok() { return ret } else { return nil } } r.errSyntax() return nil } // WantComma requires a comma to be present before fetching next token. func (r *Lexer) WantComma() { r.wantSep = ',' r.firstElement = false } // WantColon requires a colon to be present before fetching next token. func (r *Lexer) WantColon() { r.wantSep = ':' r.firstElement = false } ================================================ FILE: vendor/github.com/mailru/easyjson/jwriter/writer.go ================================================ // Package jwriter contains a JSON writer. package jwriter import ( "io" "strconv" "unicode/utf8" "github.com/mailru/easyjson/buffer" ) // Flags describe various encoding options. The behavior may be actually implemented in the encoder, but // Flags field in Writer is used to set and pass them around. type Flags int const ( NilMapAsEmpty Flags = 1 << iota // Encode nil map as '{}' rather than 'null'. NilSliceAsEmpty // Encode nil slice as '[]' rather than 'null'. ) // Writer is a JSON writer. type Writer struct { Flags Flags Error error Buffer buffer.Buffer NoEscapeHTML bool } // Size returns the size of the data that was written out. func (w *Writer) Size() int { return w.Buffer.Size() } // DumpTo outputs the data to given io.Writer, resetting the buffer. func (w *Writer) DumpTo(out io.Writer) (written int, err error) { return w.Buffer.DumpTo(out) } // BuildBytes returns writer data as a single byte slice. You can optionally provide one byte slice // as argument that it will try to reuse. func (w *Writer) BuildBytes(reuse ...[]byte) ([]byte, error) { if w.Error != nil { return nil, w.Error } return w.Buffer.BuildBytes(reuse...), nil } // ReadCloser returns an io.ReadCloser that can be used to read the data. // ReadCloser also resets the buffer. func (w *Writer) ReadCloser() (io.ReadCloser, error) { if w.Error != nil { return nil, w.Error } return w.Buffer.ReadCloser(), nil } // RawByte appends raw binary data to the buffer. func (w *Writer) RawByte(c byte) { w.Buffer.AppendByte(c) } // RawByte appends raw binary data to the buffer. func (w *Writer) RawString(s string) { w.Buffer.AppendString(s) } // Raw appends raw binary data to the buffer or sets the error if it is given. Useful for // calling with results of MarshalJSON-like functions. func (w *Writer) Raw(data []byte, err error) { switch { case w.Error != nil: return case err != nil: w.Error = err case len(data) > 0: w.Buffer.AppendBytes(data) default: w.RawString("null") } } // RawText encloses raw binary data in quotes and appends in to the buffer. // Useful for calling with results of MarshalText-like functions. func (w *Writer) RawText(data []byte, err error) { switch { case w.Error != nil: return case err != nil: w.Error = err case len(data) > 0: w.String(string(data)) default: w.RawString("null") } } // Base64Bytes appends data to the buffer after base64 encoding it func (w *Writer) Base64Bytes(data []byte) { if data == nil { w.Buffer.AppendString("null") return } w.Buffer.AppendByte('"') w.base64(data) w.Buffer.AppendByte('"') } func (w *Writer) Uint8(n uint8) { w.Buffer.EnsureSpace(3) w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) } func (w *Writer) Uint16(n uint16) { w.Buffer.EnsureSpace(5) w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) } func (w *Writer) Uint32(n uint32) { w.Buffer.EnsureSpace(10) w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) } func (w *Writer) Uint(n uint) { w.Buffer.EnsureSpace(20) w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) } func (w *Writer) Uint64(n uint64) { w.Buffer.EnsureSpace(20) w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, n, 10) } func (w *Writer) Int8(n int8) { w.Buffer.EnsureSpace(4) w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) } func (w *Writer) Int16(n int16) { w.Buffer.EnsureSpace(6) w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) } func (w *Writer) Int32(n int32) { w.Buffer.EnsureSpace(11) w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) } func (w *Writer) Int(n int) { w.Buffer.EnsureSpace(21) w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) } func (w *Writer) Int64(n int64) { w.Buffer.EnsureSpace(21) w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, n, 10) } func (w *Writer) Uint8Str(n uint8) { w.Buffer.EnsureSpace(3) w.Buffer.Buf = append(w.Buffer.Buf, '"') w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) w.Buffer.Buf = append(w.Buffer.Buf, '"') } func (w *Writer) Uint16Str(n uint16) { w.Buffer.EnsureSpace(5) w.Buffer.Buf = append(w.Buffer.Buf, '"') w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) w.Buffer.Buf = append(w.Buffer.Buf, '"') } func (w *Writer) Uint32Str(n uint32) { w.Buffer.EnsureSpace(10) w.Buffer.Buf = append(w.Buffer.Buf, '"') w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) w.Buffer.Buf = append(w.Buffer.Buf, '"') } func (w *Writer) UintStr(n uint) { w.Buffer.EnsureSpace(20) w.Buffer.Buf = append(w.Buffer.Buf, '"') w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) w.Buffer.Buf = append(w.Buffer.Buf, '"') } func (w *Writer) Uint64Str(n uint64) { w.Buffer.EnsureSpace(20) w.Buffer.Buf = append(w.Buffer.Buf, '"') w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, n, 10) w.Buffer.Buf = append(w.Buffer.Buf, '"') } func (w *Writer) UintptrStr(n uintptr) { w.Buffer.EnsureSpace(20) w.Buffer.Buf = append(w.Buffer.Buf, '"') w.Buffer.Buf = strconv.AppendUint(w.Buffer.Buf, uint64(n), 10) w.Buffer.Buf = append(w.Buffer.Buf, '"') } func (w *Writer) Int8Str(n int8) { w.Buffer.EnsureSpace(4) w.Buffer.Buf = append(w.Buffer.Buf, '"') w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) w.Buffer.Buf = append(w.Buffer.Buf, '"') } func (w *Writer) Int16Str(n int16) { w.Buffer.EnsureSpace(6) w.Buffer.Buf = append(w.Buffer.Buf, '"') w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) w.Buffer.Buf = append(w.Buffer.Buf, '"') } func (w *Writer) Int32Str(n int32) { w.Buffer.EnsureSpace(11) w.Buffer.Buf = append(w.Buffer.Buf, '"') w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) w.Buffer.Buf = append(w.Buffer.Buf, '"') } func (w *Writer) IntStr(n int) { w.Buffer.EnsureSpace(21) w.Buffer.Buf = append(w.Buffer.Buf, '"') w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, int64(n), 10) w.Buffer.Buf = append(w.Buffer.Buf, '"') } func (w *Writer) Int64Str(n int64) { w.Buffer.EnsureSpace(21) w.Buffer.Buf = append(w.Buffer.Buf, '"') w.Buffer.Buf = strconv.AppendInt(w.Buffer.Buf, n, 10) w.Buffer.Buf = append(w.Buffer.Buf, '"') } func (w *Writer) Float32(n float32) { w.Buffer.EnsureSpace(20) w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 32) } func (w *Writer) Float32Str(n float32) { w.Buffer.EnsureSpace(20) w.Buffer.Buf = append(w.Buffer.Buf, '"') w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 32) w.Buffer.Buf = append(w.Buffer.Buf, '"') } func (w *Writer) Float64(n float64) { w.Buffer.EnsureSpace(20) w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, n, 'g', -1, 64) } func (w *Writer) Float64Str(n float64) { w.Buffer.EnsureSpace(20) w.Buffer.Buf = append(w.Buffer.Buf, '"') w.Buffer.Buf = strconv.AppendFloat(w.Buffer.Buf, float64(n), 'g', -1, 64) w.Buffer.Buf = append(w.Buffer.Buf, '"') } func (w *Writer) Bool(v bool) { w.Buffer.EnsureSpace(5) if v { w.Buffer.Buf = append(w.Buffer.Buf, "true"...) } else { w.Buffer.Buf = append(w.Buffer.Buf, "false"...) } } const chars = "0123456789abcdef" func getTable(falseValues ...int) [128]bool { table := [128]bool{} for i := 0; i < 128; i++ { table[i] = true } for _, v := range falseValues { table[v] = false } return table } var ( htmlEscapeTable = getTable(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, '"', '&', '<', '>', '\\') htmlNoEscapeTable = getTable(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, '"', '\\') ) func (w *Writer) String(s string) { w.Buffer.AppendByte('"') // Portions of the string that contain no escapes are appended as // byte slices. p := 0 // last non-escape symbol escapeTable := &htmlEscapeTable if w.NoEscapeHTML { escapeTable = &htmlNoEscapeTable } for i := 0; i < len(s); { c := s[i] if c < utf8.RuneSelf { if escapeTable[c] { // single-width character, no escaping is required i++ continue } w.Buffer.AppendString(s[p:i]) switch c { case '\t': w.Buffer.AppendString(`\t`) case '\r': w.Buffer.AppendString(`\r`) case '\n': w.Buffer.AppendString(`\n`) case '\\': w.Buffer.AppendString(`\\`) case '"': w.Buffer.AppendString(`\"`) default: w.Buffer.AppendString(`\u00`) w.Buffer.AppendByte(chars[c>>4]) w.Buffer.AppendByte(chars[c&0xf]) } i++ p = i continue } // broken utf runeValue, runeWidth := utf8.DecodeRuneInString(s[i:]) if runeValue == utf8.RuneError && runeWidth == 1 { w.Buffer.AppendString(s[p:i]) w.Buffer.AppendString(`\ufffd`) i++ p = i continue } // jsonp stuff - tab separator and line separator if runeValue == '\u2028' || runeValue == '\u2029' { w.Buffer.AppendString(s[p:i]) w.Buffer.AppendString(`\u202`) w.Buffer.AppendByte(chars[runeValue&0xf]) i += runeWidth p = i continue } i += runeWidth } w.Buffer.AppendString(s[p:]) w.Buffer.AppendByte('"') } const encode = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" const padChar = '=' func (w *Writer) base64(in []byte) { if len(in) == 0 { return } w.Buffer.EnsureSpace(((len(in)-1)/3 + 1) * 4) si := 0 n := (len(in) / 3) * 3 for si < n { // Convert 3x 8bit source bytes into 4 bytes val := uint(in[si+0])<<16 | uint(in[si+1])<<8 | uint(in[si+2]) w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>18&0x3F], encode[val>>12&0x3F], encode[val>>6&0x3F], encode[val&0x3F]) si += 3 } remain := len(in) - si if remain == 0 { return } // Add the remaining small block val := uint(in[si+0]) << 16 if remain == 2 { val |= uint(in[si+1]) << 8 } w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>18&0x3F], encode[val>>12&0x3F]) switch remain { case 2: w.Buffer.Buf = append(w.Buffer.Buf, encode[val>>6&0x3F], byte(padChar)) case 1: w.Buffer.Buf = append(w.Buffer.Buf, byte(padChar), byte(padChar)) } } ================================================ FILE: vendor/github.com/mattn/go-colorable/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2016 Yasuhiro Matsumoto Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/mattn/go-colorable/README.md ================================================ # go-colorable [![Build Status](https://github.com/mattn/go-colorable/workflows/test/badge.svg)](https://github.com/mattn/go-colorable/actions?query=workflow%3Atest) [![Codecov](https://codecov.io/gh/mattn/go-colorable/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-colorable) [![GoDoc](https://godoc.org/github.com/mattn/go-colorable?status.svg)](http://godoc.org/github.com/mattn/go-colorable) [![Go Report Card](https://goreportcard.com/badge/mattn/go-colorable)](https://goreportcard.com/report/mattn/go-colorable) Colorable writer for windows. For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.) This package is possible to handle escape sequence for ansi color on windows. ## Too Bad! ![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/bad.png) ## So Good! ![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/good.png) ## Usage ```go logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true}) logrus.SetOutput(colorable.NewColorableStdout()) logrus.Info("succeeded") logrus.Warn("not correct") logrus.Error("something error") logrus.Fatal("panic") ``` You can compile above code on non-windows OSs. ## Installation ``` $ go get github.com/mattn/go-colorable ``` # License MIT # Author Yasuhiro Matsumoto (a.k.a mattn) ================================================ FILE: vendor/github.com/mattn/go-colorable/colorable_appengine.go ================================================ //go:build appengine // +build appengine package colorable import ( "io" "os" _ "github.com/mattn/go-isatty" ) // NewColorable returns new instance of Writer which handles escape sequence. func NewColorable(file *os.File) io.Writer { if file == nil { panic("nil passed instead of *os.File to NewColorable()") } return file } // NewColorableStdout returns new instance of Writer which handles escape sequence for stdout. func NewColorableStdout() io.Writer { return os.Stdout } // NewColorableStderr returns new instance of Writer which handles escape sequence for stderr. func NewColorableStderr() io.Writer { return os.Stderr } // EnableColorsStdout enable colors if possible. func EnableColorsStdout(enabled *bool) func() { if enabled != nil { *enabled = true } return func() {} } ================================================ FILE: vendor/github.com/mattn/go-colorable/colorable_others.go ================================================ //go:build !windows && !appengine // +build !windows,!appengine package colorable import ( "io" "os" _ "github.com/mattn/go-isatty" ) // NewColorable returns new instance of Writer which handles escape sequence. func NewColorable(file *os.File) io.Writer { if file == nil { panic("nil passed instead of *os.File to NewColorable()") } return file } // NewColorableStdout returns new instance of Writer which handles escape sequence for stdout. func NewColorableStdout() io.Writer { return os.Stdout } // NewColorableStderr returns new instance of Writer which handles escape sequence for stderr. func NewColorableStderr() io.Writer { return os.Stderr } // EnableColorsStdout enable colors if possible. func EnableColorsStdout(enabled *bool) func() { if enabled != nil { *enabled = true } return func() {} } ================================================ FILE: vendor/github.com/mattn/go-colorable/colorable_windows.go ================================================ //go:build windows && !appengine // +build windows,!appengine package colorable import ( "bytes" "io" "math" "os" "strconv" "strings" "sync" "syscall" "unsafe" "github.com/mattn/go-isatty" ) const ( foregroundBlue = 0x1 foregroundGreen = 0x2 foregroundRed = 0x4 foregroundIntensity = 0x8 foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity) backgroundBlue = 0x10 backgroundGreen = 0x20 backgroundRed = 0x40 backgroundIntensity = 0x80 backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity) commonLvbUnderscore = 0x8000 cENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4 ) const ( genericRead = 0x80000000 genericWrite = 0x40000000 ) const ( consoleTextmodeBuffer = 0x1 ) type wchar uint16 type short int16 type dword uint32 type word uint16 type coord struct { x short y short } type smallRect struct { left short top short right short bottom short } type consoleScreenBufferInfo struct { size coord cursorPosition coord attributes word window smallRect maximumWindowSize coord } type consoleCursorInfo struct { size dword visible int32 } var ( kernel32 = syscall.NewLazyDLL("kernel32.dll") procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute") procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo") procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo") procSetConsoleTitle = kernel32.NewProc("SetConsoleTitleW") procGetConsoleMode = kernel32.NewProc("GetConsoleMode") procSetConsoleMode = kernel32.NewProc("SetConsoleMode") procCreateConsoleScreenBuffer = kernel32.NewProc("CreateConsoleScreenBuffer") ) // Writer provides colorable Writer to the console type Writer struct { out io.Writer handle syscall.Handle althandle syscall.Handle oldattr word oldpos coord rest bytes.Buffer mutex sync.Mutex } // NewColorable returns new instance of Writer which handles escape sequence from File. func NewColorable(file *os.File) io.Writer { if file == nil { panic("nil passed instead of *os.File to NewColorable()") } if isatty.IsTerminal(file.Fd()) { var mode uint32 if r, _, _ := procGetConsoleMode.Call(file.Fd(), uintptr(unsafe.Pointer(&mode))); r != 0 && mode&cENABLE_VIRTUAL_TERMINAL_PROCESSING != 0 { return file } var csbi consoleScreenBufferInfo handle := syscall.Handle(file.Fd()) procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) return &Writer{out: file, handle: handle, oldattr: csbi.attributes, oldpos: coord{0, 0}} } return file } // NewColorableStdout returns new instance of Writer which handles escape sequence for stdout. func NewColorableStdout() io.Writer { return NewColorable(os.Stdout) } // NewColorableStderr returns new instance of Writer which handles escape sequence for stderr. func NewColorableStderr() io.Writer { return NewColorable(os.Stderr) } var color256 = map[int]int{ 0: 0x000000, 1: 0x800000, 2: 0x008000, 3: 0x808000, 4: 0x000080, 5: 0x800080, 6: 0x008080, 7: 0xc0c0c0, 8: 0x808080, 9: 0xff0000, 10: 0x00ff00, 11: 0xffff00, 12: 0x0000ff, 13: 0xff00ff, 14: 0x00ffff, 15: 0xffffff, 16: 0x000000, 17: 0x00005f, 18: 0x000087, 19: 0x0000af, 20: 0x0000d7, 21: 0x0000ff, 22: 0x005f00, 23: 0x005f5f, 24: 0x005f87, 25: 0x005faf, 26: 0x005fd7, 27: 0x005fff, 28: 0x008700, 29: 0x00875f, 30: 0x008787, 31: 0x0087af, 32: 0x0087d7, 33: 0x0087ff, 34: 0x00af00, 35: 0x00af5f, 36: 0x00af87, 37: 0x00afaf, 38: 0x00afd7, 39: 0x00afff, 40: 0x00d700, 41: 0x00d75f, 42: 0x00d787, 43: 0x00d7af, 44: 0x00d7d7, 45: 0x00d7ff, 46: 0x00ff00, 47: 0x00ff5f, 48: 0x00ff87, 49: 0x00ffaf, 50: 0x00ffd7, 51: 0x00ffff, 52: 0x5f0000, 53: 0x5f005f, 54: 0x5f0087, 55: 0x5f00af, 56: 0x5f00d7, 57: 0x5f00ff, 58: 0x5f5f00, 59: 0x5f5f5f, 60: 0x5f5f87, 61: 0x5f5faf, 62: 0x5f5fd7, 63: 0x5f5fff, 64: 0x5f8700, 65: 0x5f875f, 66: 0x5f8787, 67: 0x5f87af, 68: 0x5f87d7, 69: 0x5f87ff, 70: 0x5faf00, 71: 0x5faf5f, 72: 0x5faf87, 73: 0x5fafaf, 74: 0x5fafd7, 75: 0x5fafff, 76: 0x5fd700, 77: 0x5fd75f, 78: 0x5fd787, 79: 0x5fd7af, 80: 0x5fd7d7, 81: 0x5fd7ff, 82: 0x5fff00, 83: 0x5fff5f, 84: 0x5fff87, 85: 0x5fffaf, 86: 0x5fffd7, 87: 0x5fffff, 88: 0x870000, 89: 0x87005f, 90: 0x870087, 91: 0x8700af, 92: 0x8700d7, 93: 0x8700ff, 94: 0x875f00, 95: 0x875f5f, 96: 0x875f87, 97: 0x875faf, 98: 0x875fd7, 99: 0x875fff, 100: 0x878700, 101: 0x87875f, 102: 0x878787, 103: 0x8787af, 104: 0x8787d7, 105: 0x8787ff, 106: 0x87af00, 107: 0x87af5f, 108: 0x87af87, 109: 0x87afaf, 110: 0x87afd7, 111: 0x87afff, 112: 0x87d700, 113: 0x87d75f, 114: 0x87d787, 115: 0x87d7af, 116: 0x87d7d7, 117: 0x87d7ff, 118: 0x87ff00, 119: 0x87ff5f, 120: 0x87ff87, 121: 0x87ffaf, 122: 0x87ffd7, 123: 0x87ffff, 124: 0xaf0000, 125: 0xaf005f, 126: 0xaf0087, 127: 0xaf00af, 128: 0xaf00d7, 129: 0xaf00ff, 130: 0xaf5f00, 131: 0xaf5f5f, 132: 0xaf5f87, 133: 0xaf5faf, 134: 0xaf5fd7, 135: 0xaf5fff, 136: 0xaf8700, 137: 0xaf875f, 138: 0xaf8787, 139: 0xaf87af, 140: 0xaf87d7, 141: 0xaf87ff, 142: 0xafaf00, 143: 0xafaf5f, 144: 0xafaf87, 145: 0xafafaf, 146: 0xafafd7, 147: 0xafafff, 148: 0xafd700, 149: 0xafd75f, 150: 0xafd787, 151: 0xafd7af, 152: 0xafd7d7, 153: 0xafd7ff, 154: 0xafff00, 155: 0xafff5f, 156: 0xafff87, 157: 0xafffaf, 158: 0xafffd7, 159: 0xafffff, 160: 0xd70000, 161: 0xd7005f, 162: 0xd70087, 163: 0xd700af, 164: 0xd700d7, 165: 0xd700ff, 166: 0xd75f00, 167: 0xd75f5f, 168: 0xd75f87, 169: 0xd75faf, 170: 0xd75fd7, 171: 0xd75fff, 172: 0xd78700, 173: 0xd7875f, 174: 0xd78787, 175: 0xd787af, 176: 0xd787d7, 177: 0xd787ff, 178: 0xd7af00, 179: 0xd7af5f, 180: 0xd7af87, 181: 0xd7afaf, 182: 0xd7afd7, 183: 0xd7afff, 184: 0xd7d700, 185: 0xd7d75f, 186: 0xd7d787, 187: 0xd7d7af, 188: 0xd7d7d7, 189: 0xd7d7ff, 190: 0xd7ff00, 191: 0xd7ff5f, 192: 0xd7ff87, 193: 0xd7ffaf, 194: 0xd7ffd7, 195: 0xd7ffff, 196: 0xff0000, 197: 0xff005f, 198: 0xff0087, 199: 0xff00af, 200: 0xff00d7, 201: 0xff00ff, 202: 0xff5f00, 203: 0xff5f5f, 204: 0xff5f87, 205: 0xff5faf, 206: 0xff5fd7, 207: 0xff5fff, 208: 0xff8700, 209: 0xff875f, 210: 0xff8787, 211: 0xff87af, 212: 0xff87d7, 213: 0xff87ff, 214: 0xffaf00, 215: 0xffaf5f, 216: 0xffaf87, 217: 0xffafaf, 218: 0xffafd7, 219: 0xffafff, 220: 0xffd700, 221: 0xffd75f, 222: 0xffd787, 223: 0xffd7af, 224: 0xffd7d7, 225: 0xffd7ff, 226: 0xffff00, 227: 0xffff5f, 228: 0xffff87, 229: 0xffffaf, 230: 0xffffd7, 231: 0xffffff, 232: 0x080808, 233: 0x121212, 234: 0x1c1c1c, 235: 0x262626, 236: 0x303030, 237: 0x3a3a3a, 238: 0x444444, 239: 0x4e4e4e, 240: 0x585858, 241: 0x626262, 242: 0x6c6c6c, 243: 0x767676, 244: 0x808080, 245: 0x8a8a8a, 246: 0x949494, 247: 0x9e9e9e, 248: 0xa8a8a8, 249: 0xb2b2b2, 250: 0xbcbcbc, 251: 0xc6c6c6, 252: 0xd0d0d0, 253: 0xdadada, 254: 0xe4e4e4, 255: 0xeeeeee, } // `\033]0;TITLESTR\007` func doTitleSequence(er *bytes.Reader) error { var c byte var err error c, err = er.ReadByte() if err != nil { return err } if c != '0' && c != '2' { return nil } c, err = er.ReadByte() if err != nil { return err } if c != ';' { return nil } title := make([]byte, 0, 80) for { c, err = er.ReadByte() if err != nil { return err } if c == 0x07 || c == '\n' { break } title = append(title, c) } if len(title) > 0 { title8, err := syscall.UTF16PtrFromString(string(title)) if err == nil { procSetConsoleTitle.Call(uintptr(unsafe.Pointer(title8))) } } return nil } // returns Atoi(s) unless s == "" in which case it returns def func atoiWithDefault(s string, def int) (int, error) { if s == "" { return def, nil } return strconv.Atoi(s) } // Write writes data on console func (w *Writer) Write(data []byte) (n int, err error) { w.mutex.Lock() defer w.mutex.Unlock() var csbi consoleScreenBufferInfo procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) handle := w.handle var er *bytes.Reader if w.rest.Len() > 0 { var rest bytes.Buffer w.rest.WriteTo(&rest) w.rest.Reset() rest.Write(data) er = bytes.NewReader(rest.Bytes()) } else { er = bytes.NewReader(data) } var plaintext bytes.Buffer loop: for { c1, err := er.ReadByte() if err != nil { plaintext.WriteTo(w.out) break loop } if c1 != 0x1b { plaintext.WriteByte(c1) continue } _, err = plaintext.WriteTo(w.out) if err != nil { break loop } c2, err := er.ReadByte() if err != nil { break loop } switch c2 { case '>': continue case ']': w.rest.WriteByte(c1) w.rest.WriteByte(c2) er.WriteTo(&w.rest) if bytes.IndexByte(w.rest.Bytes(), 0x07) == -1 { break loop } er = bytes.NewReader(w.rest.Bytes()[2:]) err := doTitleSequence(er) if err != nil { break loop } w.rest.Reset() continue // https://github.com/mattn/go-colorable/issues/27 case '7': procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) w.oldpos = csbi.cursorPosition continue case '8': procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&w.oldpos))) continue case 0x5b: // execute part after switch default: continue } w.rest.WriteByte(c1) w.rest.WriteByte(c2) er.WriteTo(&w.rest) var buf bytes.Buffer var m byte for i, c := range w.rest.Bytes()[2:] { if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { m = c er = bytes.NewReader(w.rest.Bytes()[2+i+1:]) w.rest.Reset() break } buf.Write([]byte(string(c))) } if m == 0 { break loop } switch m { case 'A': n, err = atoiWithDefault(buf.String(), 1) if err != nil { continue } procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) csbi.cursorPosition.y -= short(n) procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) case 'B': n, err = atoiWithDefault(buf.String(), 1) if err != nil { continue } procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) csbi.cursorPosition.y += short(n) procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) case 'C': n, err = atoiWithDefault(buf.String(), 1) if err != nil { continue } procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) csbi.cursorPosition.x += short(n) procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) case 'D': n, err = atoiWithDefault(buf.String(), 1) if err != nil { continue } procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) csbi.cursorPosition.x -= short(n) if csbi.cursorPosition.x < 0 { csbi.cursorPosition.x = 0 } procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) case 'E': n, err = strconv.Atoi(buf.String()) if err != nil { continue } procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) csbi.cursorPosition.x = 0 csbi.cursorPosition.y += short(n) procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) case 'F': n, err = strconv.Atoi(buf.String()) if err != nil { continue } procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) csbi.cursorPosition.x = 0 csbi.cursorPosition.y -= short(n) procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) case 'G': n, err = strconv.Atoi(buf.String()) if err != nil { continue } if n < 1 { n = 1 } procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) csbi.cursorPosition.x = short(n - 1) procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) case 'H', 'f': procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) if buf.Len() > 0 { token := strings.Split(buf.String(), ";") switch len(token) { case 1: n1, err := strconv.Atoi(token[0]) if err != nil { continue } csbi.cursorPosition.y = short(n1 - 1) case 2: n1, err := strconv.Atoi(token[0]) if err != nil { continue } n2, err := strconv.Atoi(token[1]) if err != nil { continue } csbi.cursorPosition.x = short(n2 - 1) csbi.cursorPosition.y = short(n1 - 1) } } else { csbi.cursorPosition.y = 0 } procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) case 'J': n := 0 if buf.Len() > 0 { n, err = strconv.Atoi(buf.String()) if err != nil { continue } } var count, written dword var cursor coord procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) switch n { case 0: cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.size.y-csbi.cursorPosition.y)*dword(csbi.size.x) case 1: cursor = coord{x: csbi.window.left, y: csbi.window.top} count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.window.top-csbi.cursorPosition.y)*dword(csbi.size.x) case 2: cursor = coord{x: csbi.window.left, y: csbi.window.top} count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.size.y-csbi.cursorPosition.y)*dword(csbi.size.x) } procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) case 'K': n := 0 if buf.Len() > 0 { n, err = strconv.Atoi(buf.String()) if err != nil { continue } } procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) var cursor coord var count, written dword switch n { case 0: cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} count = dword(csbi.size.x - csbi.cursorPosition.x) case 1: cursor = coord{x: csbi.window.left, y: csbi.cursorPosition.y} count = dword(csbi.size.x - csbi.cursorPosition.x) case 2: cursor = coord{x: csbi.window.left, y: csbi.cursorPosition.y} count = dword(csbi.size.x) } procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) case 'X': n := 0 if buf.Len() > 0 { n, err = strconv.Atoi(buf.String()) if err != nil { continue } } procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) var cursor coord var written dword cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) case 'm': procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) attr := csbi.attributes cs := buf.String() if cs == "" { procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(w.oldattr)) continue } token := strings.Split(cs, ";") for i := 0; i < len(token); i++ { ns := token[i] if n, err = strconv.Atoi(ns); err == nil { switch { case n == 0 || n == 100: attr = w.oldattr case n == 4: attr |= commonLvbUnderscore case (1 <= n && n <= 3) || n == 5: attr |= foregroundIntensity case n == 7 || n == 27: attr = (attr &^ (foregroundMask | backgroundMask)) | ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4) case n == 22: attr &^= foregroundIntensity case n == 24: attr &^= commonLvbUnderscore case 30 <= n && n <= 37: attr &= backgroundMask if (n-30)&1 != 0 { attr |= foregroundRed } if (n-30)&2 != 0 { attr |= foregroundGreen } if (n-30)&4 != 0 { attr |= foregroundBlue } case n == 38: // set foreground color. if i < len(token)-2 && (token[i+1] == "5" || token[i+1] == "05") { if n256, err := strconv.Atoi(token[i+2]); err == nil { if n256foreAttr == nil { n256setup() } attr &= backgroundMask attr |= n256foreAttr[n256%len(n256foreAttr)] i += 2 } } else if len(token) == 5 && token[i+1] == "2" { var r, g, b int r, _ = strconv.Atoi(token[i+2]) g, _ = strconv.Atoi(token[i+3]) b, _ = strconv.Atoi(token[i+4]) i += 4 if r > 127 { attr |= foregroundRed } if g > 127 { attr |= foregroundGreen } if b > 127 { attr |= foregroundBlue } } else { attr = attr & (w.oldattr & backgroundMask) } case n == 39: // reset foreground color. attr &= backgroundMask attr |= w.oldattr & foregroundMask case 40 <= n && n <= 47: attr &= foregroundMask if (n-40)&1 != 0 { attr |= backgroundRed } if (n-40)&2 != 0 { attr |= backgroundGreen } if (n-40)&4 != 0 { attr |= backgroundBlue } case n == 48: // set background color. if i < len(token)-2 && token[i+1] == "5" { if n256, err := strconv.Atoi(token[i+2]); err == nil { if n256backAttr == nil { n256setup() } attr &= foregroundMask attr |= n256backAttr[n256%len(n256backAttr)] i += 2 } } else if len(token) == 5 && token[i+1] == "2" { var r, g, b int r, _ = strconv.Atoi(token[i+2]) g, _ = strconv.Atoi(token[i+3]) b, _ = strconv.Atoi(token[i+4]) i += 4 if r > 127 { attr |= backgroundRed } if g > 127 { attr |= backgroundGreen } if b > 127 { attr |= backgroundBlue } } else { attr = attr & (w.oldattr & foregroundMask) } case n == 49: // reset foreground color. attr &= foregroundMask attr |= w.oldattr & backgroundMask case 90 <= n && n <= 97: attr = (attr & backgroundMask) attr |= foregroundIntensity if (n-90)&1 != 0 { attr |= foregroundRed } if (n-90)&2 != 0 { attr |= foregroundGreen } if (n-90)&4 != 0 { attr |= foregroundBlue } case 100 <= n && n <= 107: attr = (attr & foregroundMask) attr |= backgroundIntensity if (n-100)&1 != 0 { attr |= backgroundRed } if (n-100)&2 != 0 { attr |= backgroundGreen } if (n-100)&4 != 0 { attr |= backgroundBlue } } procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(attr)) } } case 'h': var ci consoleCursorInfo cs := buf.String() if cs == "5>" { procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) ci.visible = 0 procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) } else if cs == "?25" { procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) ci.visible = 1 procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) } else if cs == "?1049" { if w.althandle == 0 { h, _, _ := procCreateConsoleScreenBuffer.Call(uintptr(genericRead|genericWrite), 0, 0, uintptr(consoleTextmodeBuffer), 0, 0) w.althandle = syscall.Handle(h) if w.althandle != 0 { handle = w.althandle } } } case 'l': var ci consoleCursorInfo cs := buf.String() if cs == "5>" { procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) ci.visible = 1 procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) } else if cs == "?25" { procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) ci.visible = 0 procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) } else if cs == "?1049" { if w.althandle != 0 { syscall.CloseHandle(w.althandle) w.althandle = 0 handle = w.handle } } case 's': procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) w.oldpos = csbi.cursorPosition case 'u': procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&w.oldpos))) } } return len(data), nil } type consoleColor struct { rgb int red bool green bool blue bool intensity bool } func (c consoleColor) foregroundAttr() (attr word) { if c.red { attr |= foregroundRed } if c.green { attr |= foregroundGreen } if c.blue { attr |= foregroundBlue } if c.intensity { attr |= foregroundIntensity } return } func (c consoleColor) backgroundAttr() (attr word) { if c.red { attr |= backgroundRed } if c.green { attr |= backgroundGreen } if c.blue { attr |= backgroundBlue } if c.intensity { attr |= backgroundIntensity } return } var color16 = []consoleColor{ {0x000000, false, false, false, false}, {0x000080, false, false, true, false}, {0x008000, false, true, false, false}, {0x008080, false, true, true, false}, {0x800000, true, false, false, false}, {0x800080, true, false, true, false}, {0x808000, true, true, false, false}, {0xc0c0c0, true, true, true, false}, {0x808080, false, false, false, true}, {0x0000ff, false, false, true, true}, {0x00ff00, false, true, false, true}, {0x00ffff, false, true, true, true}, {0xff0000, true, false, false, true}, {0xff00ff, true, false, true, true}, {0xffff00, true, true, false, true}, {0xffffff, true, true, true, true}, } type hsv struct { h, s, v float32 } func (a hsv) dist(b hsv) float32 { dh := a.h - b.h switch { case dh > 0.5: dh = 1 - dh case dh < -0.5: dh = -1 - dh } ds := a.s - b.s dv := a.v - b.v return float32(math.Sqrt(float64(dh*dh + ds*ds + dv*dv))) } func toHSV(rgb int) hsv { r, g, b := float32((rgb&0xFF0000)>>16)/256.0, float32((rgb&0x00FF00)>>8)/256.0, float32(rgb&0x0000FF)/256.0 min, max := minmax3f(r, g, b) h := max - min if h > 0 { if max == r { h = (g - b) / h if h < 0 { h += 6 } } else if max == g { h = 2 + (b-r)/h } else { h = 4 + (r-g)/h } } h /= 6.0 s := max - min if max != 0 { s /= max } v := max return hsv{h: h, s: s, v: v} } type hsvTable []hsv func toHSVTable(rgbTable []consoleColor) hsvTable { t := make(hsvTable, len(rgbTable)) for i, c := range rgbTable { t[i] = toHSV(c.rgb) } return t } func (t hsvTable) find(rgb int) consoleColor { hsv := toHSV(rgb) n := 7 l := float32(5.0) for i, p := range t { d := hsv.dist(p) if d < l { l, n = d, i } } return color16[n] } func minmax3f(a, b, c float32) (min, max float32) { if a < b { if b < c { return a, c } else if a < c { return a, b } else { return c, b } } else { if a < c { return b, c } else if b < c { return b, a } else { return c, a } } } var n256foreAttr []word var n256backAttr []word func n256setup() { n256foreAttr = make([]word, 256) n256backAttr = make([]word, 256) t := toHSVTable(color16) for i, rgb := range color256 { c := t.find(rgb) n256foreAttr[i] = c.foregroundAttr() n256backAttr[i] = c.backgroundAttr() } } // EnableColorsStdout enable colors if possible. func EnableColorsStdout(enabled *bool) func() { var mode uint32 h := os.Stdout.Fd() if r, _, _ := procGetConsoleMode.Call(h, uintptr(unsafe.Pointer(&mode))); r != 0 { if r, _, _ = procSetConsoleMode.Call(h, uintptr(mode|cENABLE_VIRTUAL_TERMINAL_PROCESSING)); r != 0 { if enabled != nil { *enabled = true } return func() { procSetConsoleMode.Call(h, uintptr(mode)) } } } if enabled != nil { *enabled = true } return func() {} } ================================================ FILE: vendor/github.com/mattn/go-colorable/go.test.sh ================================================ #!/usr/bin/env bash set -e echo "" > coverage.txt for d in $(go list ./... | grep -v vendor); do go test -race -coverprofile=profile.out -covermode=atomic "$d" if [ -f profile.out ]; then cat profile.out >> coverage.txt rm profile.out fi done ================================================ FILE: vendor/github.com/mattn/go-colorable/noncolorable.go ================================================ package colorable import ( "bytes" "io" ) // NonColorable holds writer but removes escape sequence. type NonColorable struct { out io.Writer } // NewNonColorable returns new instance of Writer which removes escape sequence from Writer. func NewNonColorable(w io.Writer) io.Writer { return &NonColorable{out: w} } // Write writes data on console func (w *NonColorable) Write(data []byte) (n int, err error) { er := bytes.NewReader(data) var plaintext bytes.Buffer loop: for { c1, err := er.ReadByte() if err != nil { plaintext.WriteTo(w.out) break loop } if c1 != 0x1b { plaintext.WriteByte(c1) continue } _, err = plaintext.WriteTo(w.out) if err != nil { break loop } c2, err := er.ReadByte() if err != nil { break loop } if c2 != 0x5b { continue } for { c, err := er.ReadByte() if err != nil { break loop } if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { break } } } return len(data), nil } ================================================ FILE: vendor/github.com/mattn/go-isatty/LICENSE ================================================ Copyright (c) Yasuhiro MATSUMOTO MIT License (Expat) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/mattn/go-isatty/README.md ================================================ # go-isatty [![Godoc Reference](https://godoc.org/github.com/mattn/go-isatty?status.svg)](http://godoc.org/github.com/mattn/go-isatty) [![Codecov](https://codecov.io/gh/mattn/go-isatty/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-isatty) [![Coverage Status](https://coveralls.io/repos/github/mattn/go-isatty/badge.svg?branch=master)](https://coveralls.io/github/mattn/go-isatty?branch=master) [![Go Report Card](https://goreportcard.com/badge/mattn/go-isatty)](https://goreportcard.com/report/mattn/go-isatty) isatty for golang ## Usage ```go package main import ( "fmt" "github.com/mattn/go-isatty" "os" ) func main() { if isatty.IsTerminal(os.Stdout.Fd()) { fmt.Println("Is Terminal") } else if isatty.IsCygwinTerminal(os.Stdout.Fd()) { fmt.Println("Is Cygwin/MSYS2 Terminal") } else { fmt.Println("Is Not Terminal") } } ``` ## Installation ``` $ go get github.com/mattn/go-isatty ``` ## License MIT ## Author Yasuhiro Matsumoto (a.k.a mattn) ## Thanks * k-takata: base idea for IsCygwinTerminal https://github.com/k-takata/go-iscygpty ================================================ FILE: vendor/github.com/mattn/go-isatty/doc.go ================================================ // Package isatty implements interface to isatty package isatty ================================================ FILE: vendor/github.com/mattn/go-isatty/go.test.sh ================================================ #!/usr/bin/env bash set -e echo "" > coverage.txt for d in $(go list ./... | grep -v vendor); do go test -race -coverprofile=profile.out -covermode=atomic "$d" if [ -f profile.out ]; then cat profile.out >> coverage.txt rm profile.out fi done ================================================ FILE: vendor/github.com/mattn/go-isatty/isatty_bsd.go ================================================ //go:build (darwin || freebsd || openbsd || netbsd || dragonfly) && !appengine // +build darwin freebsd openbsd netbsd dragonfly // +build !appengine package isatty import "golang.org/x/sys/unix" // IsTerminal return true if the file descriptor is terminal. func IsTerminal(fd uintptr) bool { _, err := unix.IoctlGetTermios(int(fd), unix.TIOCGETA) return err == nil } // IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 // terminal. This is also always false on this environment. func IsCygwinTerminal(fd uintptr) bool { return false } ================================================ FILE: vendor/github.com/mattn/go-isatty/isatty_others.go ================================================ //go:build appengine || js || nacl || wasm // +build appengine js nacl wasm package isatty // IsTerminal returns true if the file descriptor is terminal which // is always false on js and appengine classic which is a sandboxed PaaS. func IsTerminal(fd uintptr) bool { return false } // IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2 // terminal. This is also always false on this environment. func IsCygwinTerminal(fd uintptr) bool { return false } ================================================ FILE: vendor/github.com/mattn/go-isatty/isatty_plan9.go ================================================ //go:build plan9 // +build plan9 package isatty import ( "syscall" ) // IsTerminal returns true if the given file descriptor is a terminal. func IsTerminal(fd uintptr) bool { path, err := syscall.Fd2path(int(fd)) if err != nil { return false } return path == "/dev/cons" || path == "/mnt/term/dev/cons" } // IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 // terminal. This is also always false on this environment. func IsCygwinTerminal(fd uintptr) bool { return false } ================================================ FILE: vendor/github.com/mattn/go-isatty/isatty_solaris.go ================================================ //go:build solaris && !appengine // +build solaris,!appengine package isatty import ( "golang.org/x/sys/unix" ) // IsTerminal returns true if the given file descriptor is a terminal. // see: https://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libc/port/gen/isatty.c func IsTerminal(fd uintptr) bool { _, err := unix.IoctlGetTermio(int(fd), unix.TCGETA) return err == nil } // IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 // terminal. This is also always false on this environment. func IsCygwinTerminal(fd uintptr) bool { return false } ================================================ FILE: vendor/github.com/mattn/go-isatty/isatty_tcgets.go ================================================ //go:build (linux || aix || zos) && !appengine // +build linux aix zos // +build !appengine package isatty import "golang.org/x/sys/unix" // IsTerminal return true if the file descriptor is terminal. func IsTerminal(fd uintptr) bool { _, err := unix.IoctlGetTermios(int(fd), unix.TCGETS) return err == nil } // IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 // terminal. This is also always false on this environment. func IsCygwinTerminal(fd uintptr) bool { return false } ================================================ FILE: vendor/github.com/mattn/go-isatty/isatty_windows.go ================================================ //go:build windows && !appengine // +build windows,!appengine package isatty import ( "errors" "strings" "syscall" "unicode/utf16" "unsafe" ) const ( objectNameInfo uintptr = 1 fileNameInfo = 2 fileTypePipe = 3 ) var ( kernel32 = syscall.NewLazyDLL("kernel32.dll") ntdll = syscall.NewLazyDLL("ntdll.dll") procGetConsoleMode = kernel32.NewProc("GetConsoleMode") procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx") procGetFileType = kernel32.NewProc("GetFileType") procNtQueryObject = ntdll.NewProc("NtQueryObject") ) func init() { // Check if GetFileInformationByHandleEx is available. if procGetFileInformationByHandleEx.Find() != nil { procGetFileInformationByHandleEx = nil } } // IsTerminal return true if the file descriptor is terminal. func IsTerminal(fd uintptr) bool { var st uint32 r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) return r != 0 && e == 0 } // Check pipe name is used for cygwin/msys2 pty. // Cygwin/MSYS2 PTY has a name like: // \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master func isCygwinPipeName(name string) bool { token := strings.Split(name, "-") if len(token) < 5 { return false } if token[0] != `\msys` && token[0] != `\cygwin` && token[0] != `\Device\NamedPipe\msys` && token[0] != `\Device\NamedPipe\cygwin` { return false } if token[1] == "" { return false } if !strings.HasPrefix(token[2], "pty") { return false } if token[3] != `from` && token[3] != `to` { return false } if token[4] != "master" { return false } return true } // getFileNameByHandle use the undocomented ntdll NtQueryObject to get file full name from file handler // since GetFileInformationByHandleEx is not available under windows Vista and still some old fashion // guys are using Windows XP, this is a workaround for those guys, it will also work on system from // Windows vista to 10 // see https://stackoverflow.com/a/18792477 for details func getFileNameByHandle(fd uintptr) (string, error) { if procNtQueryObject == nil { return "", errors.New("ntdll.dll: NtQueryObject not supported") } var buf [4 + syscall.MAX_PATH]uint16 var result int r, _, e := syscall.Syscall6(procNtQueryObject.Addr(), 5, fd, objectNameInfo, uintptr(unsafe.Pointer(&buf)), uintptr(2*len(buf)), uintptr(unsafe.Pointer(&result)), 0) if r != 0 { return "", e } return string(utf16.Decode(buf[4 : 4+buf[0]/2])), nil } // IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2 // terminal. func IsCygwinTerminal(fd uintptr) bool { if procGetFileInformationByHandleEx == nil { name, err := getFileNameByHandle(fd) if err != nil { return false } return isCygwinPipeName(name) } // Cygwin/msys's pty is a pipe. ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0) if ft != fileTypePipe || e != 0 { return false } var buf [2 + syscall.MAX_PATH]uint16 r, _, e := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), 4, fd, fileNameInfo, uintptr(unsafe.Pointer(&buf)), uintptr(len(buf)*2), 0, 0) if r == 0 || e != 0 { return false } l := *(*uint32)(unsafe.Pointer(&buf)) return isCygwinPipeName(string(utf16.Decode(buf[2 : 2+l/2]))) } ================================================ FILE: vendor/github.com/mattn/go-runewidth/.travis.yml ================================================ language: go sudo: false go: - 1.13.x - tip before_install: - go get -t -v ./... script: - go generate - git diff --cached --exit-code - ./go.test.sh after_success: - bash <(curl -s https://codecov.io/bash) ================================================ FILE: vendor/github.com/mattn/go-runewidth/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2016 Yasuhiro Matsumoto Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/mattn/go-runewidth/README.md ================================================ go-runewidth ============ [![Build Status](https://travis-ci.org/mattn/go-runewidth.png?branch=master)](https://travis-ci.org/mattn/go-runewidth) [![Codecov](https://codecov.io/gh/mattn/go-runewidth/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-runewidth) [![GoDoc](https://godoc.org/github.com/mattn/go-runewidth?status.svg)](http://godoc.org/github.com/mattn/go-runewidth) [![Go Report Card](https://goreportcard.com/badge/github.com/mattn/go-runewidth)](https://goreportcard.com/report/github.com/mattn/go-runewidth) Provides functions to get fixed width of the character or string. Usage ----- ```go runewidth.StringWidth("つのだ☆HIRO") == 12 ``` Author ------ Yasuhiro Matsumoto License ------- under the MIT License: http://mattn.mit-license.org/2013 ================================================ FILE: vendor/github.com/mattn/go-runewidth/go.test.sh ================================================ #!/usr/bin/env bash set -e echo "" > coverage.txt for d in $(go list ./... | grep -v vendor); do go test -race -coverprofile=profile.out -covermode=atomic "$d" if [ -f profile.out ]; then cat profile.out >> coverage.txt rm profile.out fi done ================================================ FILE: vendor/github.com/mattn/go-runewidth/runewidth.go ================================================ package runewidth import ( "os" "github.com/rivo/uniseg" ) //go:generate go run script/generate.go var ( // EastAsianWidth will be set true if the current locale is CJK EastAsianWidth bool // StrictEmojiNeutral should be set false if handle broken fonts StrictEmojiNeutral bool = true // DefaultCondition is a condition in current locale DefaultCondition = &Condition{ EastAsianWidth: false, StrictEmojiNeutral: true, } ) func init() { handleEnv() } func handleEnv() { env := os.Getenv("RUNEWIDTH_EASTASIAN") if env == "" { EastAsianWidth = IsEastAsian() } else { EastAsianWidth = env == "1" } // update DefaultCondition DefaultCondition.EastAsianWidth = EastAsianWidth } type interval struct { first rune last rune } type table []interval func inTables(r rune, ts ...table) bool { for _, t := range ts { if inTable(r, t) { return true } } return false } func inTable(r rune, t table) bool { if r < t[0].first { return false } bot := 0 top := len(t) - 1 for top >= bot { mid := (bot + top) >> 1 switch { case t[mid].last < r: bot = mid + 1 case t[mid].first > r: top = mid - 1 default: return true } } return false } var private = table{ {0x00E000, 0x00F8FF}, {0x0F0000, 0x0FFFFD}, {0x100000, 0x10FFFD}, } var nonprint = table{ {0x0000, 0x001F}, {0x007F, 0x009F}, {0x00AD, 0x00AD}, {0x070F, 0x070F}, {0x180B, 0x180E}, {0x200B, 0x200F}, {0x2028, 0x202E}, {0x206A, 0x206F}, {0xD800, 0xDFFF}, {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB}, {0xFFFE, 0xFFFF}, } // Condition have flag EastAsianWidth whether the current locale is CJK or not. type Condition struct { EastAsianWidth bool StrictEmojiNeutral bool } // NewCondition return new instance of Condition which is current locale. func NewCondition() *Condition { return &Condition{ EastAsianWidth: EastAsianWidth, StrictEmojiNeutral: StrictEmojiNeutral, } } // RuneWidth returns the number of cells in r. // See http://www.unicode.org/reports/tr11/ func (c *Condition) RuneWidth(r rune) int { // optimized version, verified by TestRuneWidthChecksums() if !c.EastAsianWidth { switch { case r < 0x20 || r > 0x10FFFF: return 0 case (r >= 0x7F && r <= 0x9F) || r == 0xAD: // nonprint return 0 case r < 0x300: return 1 case inTable(r, narrow): return 1 case inTables(r, nonprint, combining): return 0 case inTable(r, doublewidth): return 2 default: return 1 } } else { switch { case r < 0 || r > 0x10FFFF || inTables(r, nonprint, combining): return 0 case inTable(r, narrow): return 1 case inTables(r, ambiguous, doublewidth): return 2 case !c.StrictEmojiNeutral && inTables(r, ambiguous, emoji, narrow): return 2 default: return 1 } } } // StringWidth return width as you can see func (c *Condition) StringWidth(s string) (width int) { g := uniseg.NewGraphemes(s) for g.Next() { var chWidth int for _, r := range g.Runes() { chWidth = c.RuneWidth(r) if chWidth > 0 { break // Our best guess at this point is to use the width of the first non-zero-width rune. } } width += chWidth } return } // Truncate return string truncated with w cells func (c *Condition) Truncate(s string, w int, tail string) string { if c.StringWidth(s) <= w { return s } w -= c.StringWidth(tail) var width int pos := len(s) g := uniseg.NewGraphemes(s) for g.Next() { var chWidth int for _, r := range g.Runes() { chWidth = c.RuneWidth(r) if chWidth > 0 { break // See StringWidth() for details. } } if width+chWidth > w { pos, _ = g.Positions() break } width += chWidth } return s[:pos] + tail } // Wrap return string wrapped with w cells func (c *Condition) Wrap(s string, w int) string { width := 0 out := "" for _, r := range []rune(s) { cw := c.RuneWidth(r) if r == '\n' { out += string(r) width = 0 continue } else if width+cw > w { out += "\n" width = 0 out += string(r) width += cw continue } out += string(r) width += cw } return out } // FillLeft return string filled in left by spaces in w cells func (c *Condition) FillLeft(s string, w int) string { width := c.StringWidth(s) count := w - width if count > 0 { b := make([]byte, count) for i := range b { b[i] = ' ' } return string(b) + s } return s } // FillRight return string filled in left by spaces in w cells func (c *Condition) FillRight(s string, w int) string { width := c.StringWidth(s) count := w - width if count > 0 { b := make([]byte, count) for i := range b { b[i] = ' ' } return s + string(b) } return s } // RuneWidth returns the number of cells in r. // See http://www.unicode.org/reports/tr11/ func RuneWidth(r rune) int { return DefaultCondition.RuneWidth(r) } // IsAmbiguousWidth returns whether is ambiguous width or not. func IsAmbiguousWidth(r rune) bool { return inTables(r, private, ambiguous) } // IsNeutralWidth returns whether is neutral width or not. func IsNeutralWidth(r rune) bool { return inTable(r, neutral) } // StringWidth return width as you can see func StringWidth(s string) (width int) { return DefaultCondition.StringWidth(s) } // Truncate return string truncated with w cells func Truncate(s string, w int, tail string) string { return DefaultCondition.Truncate(s, w, tail) } // Wrap return string wrapped with w cells func Wrap(s string, w int) string { return DefaultCondition.Wrap(s, w) } // FillLeft return string filled in left by spaces in w cells func FillLeft(s string, w int) string { return DefaultCondition.FillLeft(s, w) } // FillRight return string filled in left by spaces in w cells func FillRight(s string, w int) string { return DefaultCondition.FillRight(s, w) } ================================================ FILE: vendor/github.com/mattn/go-runewidth/runewidth_appengine.go ================================================ // +build appengine package runewidth // IsEastAsian return true if the current locale is CJK func IsEastAsian() bool { return false } ================================================ FILE: vendor/github.com/mattn/go-runewidth/runewidth_js.go ================================================ // +build js // +build !appengine package runewidth func IsEastAsian() bool { // TODO: Implement this for the web. Detect east asian in a compatible way, and return true. return false } ================================================ FILE: vendor/github.com/mattn/go-runewidth/runewidth_posix.go ================================================ // +build !windows // +build !js // +build !appengine package runewidth import ( "os" "regexp" "strings" ) var reLoc = regexp.MustCompile(`^[a-z][a-z][a-z]?(?:_[A-Z][A-Z])?\.(.+)`) var mblenTable = map[string]int{ "utf-8": 6, "utf8": 6, "jis": 8, "eucjp": 3, "euckr": 2, "euccn": 2, "sjis": 2, "cp932": 2, "cp51932": 2, "cp936": 2, "cp949": 2, "cp950": 2, "big5": 2, "gbk": 2, "gb2312": 2, } func isEastAsian(locale string) bool { charset := strings.ToLower(locale) r := reLoc.FindStringSubmatch(locale) if len(r) == 2 { charset = strings.ToLower(r[1]) } if strings.HasSuffix(charset, "@cjk_narrow") { return false } for pos, b := range []byte(charset) { if b == '@' { charset = charset[:pos] break } } max := 1 if m, ok := mblenTable[charset]; ok { max = m } if max > 1 && (charset[0] != 'u' || strings.HasPrefix(locale, "ja") || strings.HasPrefix(locale, "ko") || strings.HasPrefix(locale, "zh")) { return true } return false } // IsEastAsian return true if the current locale is CJK func IsEastAsian() bool { locale := os.Getenv("LC_ALL") if locale == "" { locale = os.Getenv("LC_CTYPE") } if locale == "" { locale = os.Getenv("LANG") } // ignore C locale if locale == "POSIX" || locale == "C" { return false } if len(locale) > 1 && locale[0] == 'C' && (locale[1] == '.' || locale[1] == '-') { return false } return isEastAsian(locale) } ================================================ FILE: vendor/github.com/mattn/go-runewidth/runewidth_table.go ================================================ // Code generated by script/generate.go. DO NOT EDIT. package runewidth var combining = table{ {0x0300, 0x036F}, {0x0483, 0x0489}, {0x07EB, 0x07F3}, {0x0C00, 0x0C00}, {0x0C04, 0x0C04}, {0x0D00, 0x0D01}, {0x135D, 0x135F}, {0x1A7F, 0x1A7F}, {0x1AB0, 0x1AC0}, {0x1B6B, 0x1B73}, {0x1DC0, 0x1DF9}, {0x1DFB, 0x1DFF}, {0x20D0, 0x20F0}, {0x2CEF, 0x2CF1}, {0x2DE0, 0x2DFF}, {0x3099, 0x309A}, {0xA66F, 0xA672}, {0xA674, 0xA67D}, {0xA69E, 0xA69F}, {0xA6F0, 0xA6F1}, {0xA8E0, 0xA8F1}, {0xFE20, 0xFE2F}, {0x101FD, 0x101FD}, {0x10376, 0x1037A}, {0x10EAB, 0x10EAC}, {0x10F46, 0x10F50}, {0x11300, 0x11301}, {0x1133B, 0x1133C}, {0x11366, 0x1136C}, {0x11370, 0x11374}, {0x16AF0, 0x16AF4}, {0x1D165, 0x1D169}, {0x1D16D, 0x1D172}, {0x1D17B, 0x1D182}, {0x1D185, 0x1D18B}, {0x1D1AA, 0x1D1AD}, {0x1D242, 0x1D244}, {0x1E000, 0x1E006}, {0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, {0x1E023, 0x1E024}, {0x1E026, 0x1E02A}, {0x1E8D0, 0x1E8D6}, } var doublewidth = table{ {0x1100, 0x115F}, {0x231A, 0x231B}, {0x2329, 0x232A}, {0x23E9, 0x23EC}, {0x23F0, 0x23F0}, {0x23F3, 0x23F3}, {0x25FD, 0x25FE}, {0x2614, 0x2615}, {0x2648, 0x2653}, {0x267F, 0x267F}, {0x2693, 0x2693}, {0x26A1, 0x26A1}, {0x26AA, 0x26AB}, {0x26BD, 0x26BE}, {0x26C4, 0x26C5}, {0x26CE, 0x26CE}, {0x26D4, 0x26D4}, {0x26EA, 0x26EA}, {0x26F2, 0x26F3}, {0x26F5, 0x26F5}, {0x26FA, 0x26FA}, {0x26FD, 0x26FD}, {0x2705, 0x2705}, {0x270A, 0x270B}, {0x2728, 0x2728}, {0x274C, 0x274C}, {0x274E, 0x274E}, {0x2753, 0x2755}, {0x2757, 0x2757}, {0x2795, 0x2797}, {0x27B0, 0x27B0}, {0x27BF, 0x27BF}, {0x2B1B, 0x2B1C}, {0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x2E80, 0x2E99}, {0x2E9B, 0x2EF3}, {0x2F00, 0x2FD5}, {0x2FF0, 0x2FFB}, {0x3000, 0x303E}, {0x3041, 0x3096}, {0x3099, 0x30FF}, {0x3105, 0x312F}, {0x3131, 0x318E}, {0x3190, 0x31E3}, {0x31F0, 0x321E}, {0x3220, 0x3247}, {0x3250, 0x4DBF}, {0x4E00, 0xA48C}, {0xA490, 0xA4C6}, {0xA960, 0xA97C}, {0xAC00, 0xD7A3}, {0xF900, 0xFAFF}, {0xFE10, 0xFE19}, {0xFE30, 0xFE52}, {0xFE54, 0xFE66}, {0xFE68, 0xFE6B}, {0xFF01, 0xFF60}, {0xFFE0, 0xFFE6}, {0x16FE0, 0x16FE4}, {0x16FF0, 0x16FF1}, {0x17000, 0x187F7}, {0x18800, 0x18CD5}, {0x18D00, 0x18D08}, {0x1B000, 0x1B11E}, {0x1B150, 0x1B152}, {0x1B164, 0x1B167}, {0x1B170, 0x1B2FB}, {0x1F004, 0x1F004}, {0x1F0CF, 0x1F0CF}, {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A}, {0x1F200, 0x1F202}, {0x1F210, 0x1F23B}, {0x1F240, 0x1F248}, {0x1F250, 0x1F251}, {0x1F260, 0x1F265}, {0x1F300, 0x1F320}, {0x1F32D, 0x1F335}, {0x1F337, 0x1F37C}, {0x1F37E, 0x1F393}, {0x1F3A0, 0x1F3CA}, {0x1F3CF, 0x1F3D3}, {0x1F3E0, 0x1F3F0}, {0x1F3F4, 0x1F3F4}, {0x1F3F8, 0x1F43E}, {0x1F440, 0x1F440}, {0x1F442, 0x1F4FC}, {0x1F4FF, 0x1F53D}, {0x1F54B, 0x1F54E}, {0x1F550, 0x1F567}, {0x1F57A, 0x1F57A}, {0x1F595, 0x1F596}, {0x1F5A4, 0x1F5A4}, {0x1F5FB, 0x1F64F}, {0x1F680, 0x1F6C5}, {0x1F6CC, 0x1F6CC}, {0x1F6D0, 0x1F6D2}, {0x1F6D5, 0x1F6D7}, {0x1F6EB, 0x1F6EC}, {0x1F6F4, 0x1F6FC}, {0x1F7E0, 0x1F7EB}, {0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945}, {0x1F947, 0x1F978}, {0x1F97A, 0x1F9CB}, {0x1F9CD, 0x1F9FF}, {0x1FA70, 0x1FA74}, {0x1FA78, 0x1FA7A}, {0x1FA80, 0x1FA86}, {0x1FA90, 0x1FAA8}, {0x1FAB0, 0x1FAB6}, {0x1FAC0, 0x1FAC2}, {0x1FAD0, 0x1FAD6}, {0x20000, 0x2FFFD}, {0x30000, 0x3FFFD}, } var ambiguous = table{ {0x00A1, 0x00A1}, {0x00A4, 0x00A4}, {0x00A7, 0x00A8}, {0x00AA, 0x00AA}, {0x00AD, 0x00AE}, {0x00B0, 0x00B4}, {0x00B6, 0x00BA}, {0x00BC, 0x00BF}, {0x00C6, 0x00C6}, {0x00D0, 0x00D0}, {0x00D7, 0x00D8}, {0x00DE, 0x00E1}, {0x00E6, 0x00E6}, {0x00E8, 0x00EA}, {0x00EC, 0x00ED}, {0x00F0, 0x00F0}, {0x00F2, 0x00F3}, {0x00F7, 0x00FA}, {0x00FC, 0x00FC}, {0x00FE, 0x00FE}, {0x0101, 0x0101}, {0x0111, 0x0111}, {0x0113, 0x0113}, {0x011B, 0x011B}, {0x0126, 0x0127}, {0x012B, 0x012B}, {0x0131, 0x0133}, {0x0138, 0x0138}, {0x013F, 0x0142}, {0x0144, 0x0144}, {0x0148, 0x014B}, {0x014D, 0x014D}, {0x0152, 0x0153}, {0x0166, 0x0167}, {0x016B, 0x016B}, {0x01CE, 0x01CE}, {0x01D0, 0x01D0}, {0x01D2, 0x01D2}, {0x01D4, 0x01D4}, {0x01D6, 0x01D6}, {0x01D8, 0x01D8}, {0x01DA, 0x01DA}, {0x01DC, 0x01DC}, {0x0251, 0x0251}, {0x0261, 0x0261}, {0x02C4, 0x02C4}, {0x02C7, 0x02C7}, {0x02C9, 0x02CB}, {0x02CD, 0x02CD}, {0x02D0, 0x02D0}, {0x02D8, 0x02DB}, {0x02DD, 0x02DD}, {0x02DF, 0x02DF}, {0x0300, 0x036F}, {0x0391, 0x03A1}, {0x03A3, 0x03A9}, {0x03B1, 0x03C1}, {0x03C3, 0x03C9}, {0x0401, 0x0401}, {0x0410, 0x044F}, {0x0451, 0x0451}, {0x2010, 0x2010}, {0x2013, 0x2016}, {0x2018, 0x2019}, {0x201C, 0x201D}, {0x2020, 0x2022}, {0x2024, 0x2027}, {0x2030, 0x2030}, {0x2032, 0x2033}, {0x2035, 0x2035}, {0x203B, 0x203B}, {0x203E, 0x203E}, {0x2074, 0x2074}, {0x207F, 0x207F}, {0x2081, 0x2084}, {0x20AC, 0x20AC}, {0x2103, 0x2103}, {0x2105, 0x2105}, {0x2109, 0x2109}, {0x2113, 0x2113}, {0x2116, 0x2116}, {0x2121, 0x2122}, {0x2126, 0x2126}, {0x212B, 0x212B}, {0x2153, 0x2154}, {0x215B, 0x215E}, {0x2160, 0x216B}, {0x2170, 0x2179}, {0x2189, 0x2189}, {0x2190, 0x2199}, {0x21B8, 0x21B9}, {0x21D2, 0x21D2}, {0x21D4, 0x21D4}, {0x21E7, 0x21E7}, {0x2200, 0x2200}, {0x2202, 0x2203}, {0x2207, 0x2208}, {0x220B, 0x220B}, {0x220F, 0x220F}, {0x2211, 0x2211}, {0x2215, 0x2215}, {0x221A, 0x221A}, {0x221D, 0x2220}, {0x2223, 0x2223}, {0x2225, 0x2225}, {0x2227, 0x222C}, {0x222E, 0x222E}, {0x2234, 0x2237}, {0x223C, 0x223D}, {0x2248, 0x2248}, {0x224C, 0x224C}, {0x2252, 0x2252}, {0x2260, 0x2261}, {0x2264, 0x2267}, {0x226A, 0x226B}, {0x226E, 0x226F}, {0x2282, 0x2283}, {0x2286, 0x2287}, {0x2295, 0x2295}, {0x2299, 0x2299}, {0x22A5, 0x22A5}, {0x22BF, 0x22BF}, {0x2312, 0x2312}, {0x2460, 0x24E9}, {0x24EB, 0x254B}, {0x2550, 0x2573}, {0x2580, 0x258F}, {0x2592, 0x2595}, {0x25A0, 0x25A1}, {0x25A3, 0x25A9}, {0x25B2, 0x25B3}, {0x25B6, 0x25B7}, {0x25BC, 0x25BD}, {0x25C0, 0x25C1}, {0x25C6, 0x25C8}, {0x25CB, 0x25CB}, {0x25CE, 0x25D1}, {0x25E2, 0x25E5}, {0x25EF, 0x25EF}, {0x2605, 0x2606}, {0x2609, 0x2609}, {0x260E, 0x260F}, {0x261C, 0x261C}, {0x261E, 0x261E}, {0x2640, 0x2640}, {0x2642, 0x2642}, {0x2660, 0x2661}, {0x2663, 0x2665}, {0x2667, 0x266A}, {0x266C, 0x266D}, {0x266F, 0x266F}, {0x269E, 0x269F}, {0x26BF, 0x26BF}, {0x26C6, 0x26CD}, {0x26CF, 0x26D3}, {0x26D5, 0x26E1}, {0x26E3, 0x26E3}, {0x26E8, 0x26E9}, {0x26EB, 0x26F1}, {0x26F4, 0x26F4}, {0x26F6, 0x26F9}, {0x26FB, 0x26FC}, {0x26FE, 0x26FF}, {0x273D, 0x273D}, {0x2776, 0x277F}, {0x2B56, 0x2B59}, {0x3248, 0x324F}, {0xE000, 0xF8FF}, {0xFE00, 0xFE0F}, {0xFFFD, 0xFFFD}, {0x1F100, 0x1F10A}, {0x1F110, 0x1F12D}, {0x1F130, 0x1F169}, {0x1F170, 0x1F18D}, {0x1F18F, 0x1F190}, {0x1F19B, 0x1F1AC}, {0xE0100, 0xE01EF}, {0xF0000, 0xFFFFD}, {0x100000, 0x10FFFD}, } var narrow = table{ {0x0020, 0x007E}, {0x00A2, 0x00A3}, {0x00A5, 0x00A6}, {0x00AC, 0x00AC}, {0x00AF, 0x00AF}, {0x27E6, 0x27ED}, {0x2985, 0x2986}, } var neutral = table{ {0x0000, 0x001F}, {0x007F, 0x00A0}, {0x00A9, 0x00A9}, {0x00AB, 0x00AB}, {0x00B5, 0x00B5}, {0x00BB, 0x00BB}, {0x00C0, 0x00C5}, {0x00C7, 0x00CF}, {0x00D1, 0x00D6}, {0x00D9, 0x00DD}, {0x00E2, 0x00E5}, {0x00E7, 0x00E7}, {0x00EB, 0x00EB}, {0x00EE, 0x00EF}, {0x00F1, 0x00F1}, {0x00F4, 0x00F6}, {0x00FB, 0x00FB}, {0x00FD, 0x00FD}, {0x00FF, 0x0100}, {0x0102, 0x0110}, {0x0112, 0x0112}, {0x0114, 0x011A}, {0x011C, 0x0125}, {0x0128, 0x012A}, {0x012C, 0x0130}, {0x0134, 0x0137}, {0x0139, 0x013E}, {0x0143, 0x0143}, {0x0145, 0x0147}, {0x014C, 0x014C}, {0x014E, 0x0151}, {0x0154, 0x0165}, {0x0168, 0x016A}, {0x016C, 0x01CD}, {0x01CF, 0x01CF}, {0x01D1, 0x01D1}, {0x01D3, 0x01D3}, {0x01D5, 0x01D5}, {0x01D7, 0x01D7}, {0x01D9, 0x01D9}, {0x01DB, 0x01DB}, {0x01DD, 0x0250}, {0x0252, 0x0260}, {0x0262, 0x02C3}, {0x02C5, 0x02C6}, {0x02C8, 0x02C8}, {0x02CC, 0x02CC}, {0x02CE, 0x02CF}, {0x02D1, 0x02D7}, {0x02DC, 0x02DC}, {0x02DE, 0x02DE}, {0x02E0, 0x02FF}, {0x0370, 0x0377}, {0x037A, 0x037F}, {0x0384, 0x038A}, {0x038C, 0x038C}, {0x038E, 0x0390}, {0x03AA, 0x03B0}, {0x03C2, 0x03C2}, {0x03CA, 0x0400}, {0x0402, 0x040F}, {0x0450, 0x0450}, {0x0452, 0x052F}, {0x0531, 0x0556}, {0x0559, 0x058A}, {0x058D, 0x058F}, {0x0591, 0x05C7}, {0x05D0, 0x05EA}, {0x05EF, 0x05F4}, {0x0600, 0x061C}, {0x061E, 0x070D}, {0x070F, 0x074A}, {0x074D, 0x07B1}, {0x07C0, 0x07FA}, {0x07FD, 0x082D}, {0x0830, 0x083E}, {0x0840, 0x085B}, {0x085E, 0x085E}, {0x0860, 0x086A}, {0x08A0, 0x08B4}, {0x08B6, 0x08C7}, {0x08D3, 0x0983}, {0x0985, 0x098C}, {0x098F, 0x0990}, {0x0993, 0x09A8}, {0x09AA, 0x09B0}, {0x09B2, 0x09B2}, {0x09B6, 0x09B9}, {0x09BC, 0x09C4}, {0x09C7, 0x09C8}, {0x09CB, 0x09CE}, {0x09D7, 0x09D7}, {0x09DC, 0x09DD}, {0x09DF, 0x09E3}, {0x09E6, 0x09FE}, {0x0A01, 0x0A03}, {0x0A05, 0x0A0A}, {0x0A0F, 0x0A10}, {0x0A13, 0x0A28}, {0x0A2A, 0x0A30}, {0x0A32, 0x0A33}, {0x0A35, 0x0A36}, {0x0A38, 0x0A39}, {0x0A3C, 0x0A3C}, {0x0A3E, 0x0A42}, {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A51, 0x0A51}, {0x0A59, 0x0A5C}, {0x0A5E, 0x0A5E}, {0x0A66, 0x0A76}, {0x0A81, 0x0A83}, {0x0A85, 0x0A8D}, {0x0A8F, 0x0A91}, {0x0A93, 0x0AA8}, {0x0AAA, 0x0AB0}, {0x0AB2, 0x0AB3}, {0x0AB5, 0x0AB9}, {0x0ABC, 0x0AC5}, {0x0AC7, 0x0AC9}, {0x0ACB, 0x0ACD}, {0x0AD0, 0x0AD0}, {0x0AE0, 0x0AE3}, {0x0AE6, 0x0AF1}, {0x0AF9, 0x0AFF}, {0x0B01, 0x0B03}, {0x0B05, 0x0B0C}, {0x0B0F, 0x0B10}, {0x0B13, 0x0B28}, {0x0B2A, 0x0B30}, {0x0B32, 0x0B33}, {0x0B35, 0x0B39}, {0x0B3C, 0x0B44}, {0x0B47, 0x0B48}, {0x0B4B, 0x0B4D}, {0x0B55, 0x0B57}, {0x0B5C, 0x0B5D}, {0x0B5F, 0x0B63}, {0x0B66, 0x0B77}, {0x0B82, 0x0B83}, {0x0B85, 0x0B8A}, {0x0B8E, 0x0B90}, {0x0B92, 0x0B95}, {0x0B99, 0x0B9A}, {0x0B9C, 0x0B9C}, {0x0B9E, 0x0B9F}, {0x0BA3, 0x0BA4}, {0x0BA8, 0x0BAA}, {0x0BAE, 0x0BB9}, {0x0BBE, 0x0BC2}, {0x0BC6, 0x0BC8}, {0x0BCA, 0x0BCD}, {0x0BD0, 0x0BD0}, {0x0BD7, 0x0BD7}, {0x0BE6, 0x0BFA}, {0x0C00, 0x0C0C}, {0x0C0E, 0x0C10}, {0x0C12, 0x0C28}, {0x0C2A, 0x0C39}, {0x0C3D, 0x0C44}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, {0x0C55, 0x0C56}, {0x0C58, 0x0C5A}, {0x0C60, 0x0C63}, {0x0C66, 0x0C6F}, {0x0C77, 0x0C8C}, {0x0C8E, 0x0C90}, {0x0C92, 0x0CA8}, {0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9}, {0x0CBC, 0x0CC4}, {0x0CC6, 0x0CC8}, {0x0CCA, 0x0CCD}, {0x0CD5, 0x0CD6}, {0x0CDE, 0x0CDE}, {0x0CE0, 0x0CE3}, {0x0CE6, 0x0CEF}, {0x0CF1, 0x0CF2}, {0x0D00, 0x0D0C}, {0x0D0E, 0x0D10}, {0x0D12, 0x0D44}, {0x0D46, 0x0D48}, {0x0D4A, 0x0D4F}, {0x0D54, 0x0D63}, {0x0D66, 0x0D7F}, {0x0D81, 0x0D83}, {0x0D85, 0x0D96}, {0x0D9A, 0x0DB1}, {0x0DB3, 0x0DBB}, {0x0DBD, 0x0DBD}, {0x0DC0, 0x0DC6}, {0x0DCA, 0x0DCA}, {0x0DCF, 0x0DD4}, {0x0DD6, 0x0DD6}, {0x0DD8, 0x0DDF}, {0x0DE6, 0x0DEF}, {0x0DF2, 0x0DF4}, {0x0E01, 0x0E3A}, {0x0E3F, 0x0E5B}, {0x0E81, 0x0E82}, {0x0E84, 0x0E84}, {0x0E86, 0x0E8A}, {0x0E8C, 0x0EA3}, {0x0EA5, 0x0EA5}, {0x0EA7, 0x0EBD}, {0x0EC0, 0x0EC4}, {0x0EC6, 0x0EC6}, {0x0EC8, 0x0ECD}, {0x0ED0, 0x0ED9}, {0x0EDC, 0x0EDF}, {0x0F00, 0x0F47}, {0x0F49, 0x0F6C}, {0x0F71, 0x0F97}, {0x0F99, 0x0FBC}, {0x0FBE, 0x0FCC}, {0x0FCE, 0x0FDA}, {0x1000, 0x10C5}, {0x10C7, 0x10C7}, {0x10CD, 0x10CD}, {0x10D0, 0x10FF}, {0x1160, 0x1248}, {0x124A, 0x124D}, {0x1250, 0x1256}, {0x1258, 0x1258}, {0x125A, 0x125D}, {0x1260, 0x1288}, {0x128A, 0x128D}, {0x1290, 0x12B0}, {0x12B2, 0x12B5}, {0x12B8, 0x12BE}, {0x12C0, 0x12C0}, {0x12C2, 0x12C5}, {0x12C8, 0x12D6}, {0x12D8, 0x1310}, {0x1312, 0x1315}, {0x1318, 0x135A}, {0x135D, 0x137C}, {0x1380, 0x1399}, {0x13A0, 0x13F5}, {0x13F8, 0x13FD}, {0x1400, 0x169C}, {0x16A0, 0x16F8}, {0x1700, 0x170C}, {0x170E, 0x1714}, {0x1720, 0x1736}, {0x1740, 0x1753}, {0x1760, 0x176C}, {0x176E, 0x1770}, {0x1772, 0x1773}, {0x1780, 0x17DD}, {0x17E0, 0x17E9}, {0x17F0, 0x17F9}, {0x1800, 0x180E}, {0x1810, 0x1819}, {0x1820, 0x1878}, {0x1880, 0x18AA}, {0x18B0, 0x18F5}, {0x1900, 0x191E}, {0x1920, 0x192B}, {0x1930, 0x193B}, {0x1940, 0x1940}, {0x1944, 0x196D}, {0x1970, 0x1974}, {0x1980, 0x19AB}, {0x19B0, 0x19C9}, {0x19D0, 0x19DA}, {0x19DE, 0x1A1B}, {0x1A1E, 0x1A5E}, {0x1A60, 0x1A7C}, {0x1A7F, 0x1A89}, {0x1A90, 0x1A99}, {0x1AA0, 0x1AAD}, {0x1AB0, 0x1AC0}, {0x1B00, 0x1B4B}, {0x1B50, 0x1B7C}, {0x1B80, 0x1BF3}, {0x1BFC, 0x1C37}, {0x1C3B, 0x1C49}, {0x1C4D, 0x1C88}, {0x1C90, 0x1CBA}, {0x1CBD, 0x1CC7}, {0x1CD0, 0x1CFA}, {0x1D00, 0x1DF9}, {0x1DFB, 0x1F15}, {0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, {0x1F48, 0x1F4D}, {0x1F50, 0x1F57}, {0x1F59, 0x1F59}, {0x1F5B, 0x1F5B}, {0x1F5D, 0x1F5D}, {0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4}, {0x1FB6, 0x1FC4}, {0x1FC6, 0x1FD3}, {0x1FD6, 0x1FDB}, {0x1FDD, 0x1FEF}, {0x1FF2, 0x1FF4}, {0x1FF6, 0x1FFE}, {0x2000, 0x200F}, {0x2011, 0x2012}, {0x2017, 0x2017}, {0x201A, 0x201B}, {0x201E, 0x201F}, {0x2023, 0x2023}, {0x2028, 0x202F}, {0x2031, 0x2031}, {0x2034, 0x2034}, {0x2036, 0x203A}, {0x203C, 0x203D}, {0x203F, 0x2064}, {0x2066, 0x2071}, {0x2075, 0x207E}, {0x2080, 0x2080}, {0x2085, 0x208E}, {0x2090, 0x209C}, {0x20A0, 0x20A8}, {0x20AA, 0x20AB}, {0x20AD, 0x20BF}, {0x20D0, 0x20F0}, {0x2100, 0x2102}, {0x2104, 0x2104}, {0x2106, 0x2108}, {0x210A, 0x2112}, {0x2114, 0x2115}, {0x2117, 0x2120}, {0x2123, 0x2125}, {0x2127, 0x212A}, {0x212C, 0x2152}, {0x2155, 0x215A}, {0x215F, 0x215F}, {0x216C, 0x216F}, {0x217A, 0x2188}, {0x218A, 0x218B}, {0x219A, 0x21B7}, {0x21BA, 0x21D1}, {0x21D3, 0x21D3}, {0x21D5, 0x21E6}, {0x21E8, 0x21FF}, {0x2201, 0x2201}, {0x2204, 0x2206}, {0x2209, 0x220A}, {0x220C, 0x220E}, {0x2210, 0x2210}, {0x2212, 0x2214}, {0x2216, 0x2219}, {0x221B, 0x221C}, {0x2221, 0x2222}, {0x2224, 0x2224}, {0x2226, 0x2226}, {0x222D, 0x222D}, {0x222F, 0x2233}, {0x2238, 0x223B}, {0x223E, 0x2247}, {0x2249, 0x224B}, {0x224D, 0x2251}, {0x2253, 0x225F}, {0x2262, 0x2263}, {0x2268, 0x2269}, {0x226C, 0x226D}, {0x2270, 0x2281}, {0x2284, 0x2285}, {0x2288, 0x2294}, {0x2296, 0x2298}, {0x229A, 0x22A4}, {0x22A6, 0x22BE}, {0x22C0, 0x2311}, {0x2313, 0x2319}, {0x231C, 0x2328}, {0x232B, 0x23E8}, {0x23ED, 0x23EF}, {0x23F1, 0x23F2}, {0x23F4, 0x2426}, {0x2440, 0x244A}, {0x24EA, 0x24EA}, {0x254C, 0x254F}, {0x2574, 0x257F}, {0x2590, 0x2591}, {0x2596, 0x259F}, {0x25A2, 0x25A2}, {0x25AA, 0x25B1}, {0x25B4, 0x25B5}, {0x25B8, 0x25BB}, {0x25BE, 0x25BF}, {0x25C2, 0x25C5}, {0x25C9, 0x25CA}, {0x25CC, 0x25CD}, {0x25D2, 0x25E1}, {0x25E6, 0x25EE}, {0x25F0, 0x25FC}, {0x25FF, 0x2604}, {0x2607, 0x2608}, {0x260A, 0x260D}, {0x2610, 0x2613}, {0x2616, 0x261B}, {0x261D, 0x261D}, {0x261F, 0x263F}, {0x2641, 0x2641}, {0x2643, 0x2647}, {0x2654, 0x265F}, {0x2662, 0x2662}, {0x2666, 0x2666}, {0x266B, 0x266B}, {0x266E, 0x266E}, {0x2670, 0x267E}, {0x2680, 0x2692}, {0x2694, 0x269D}, {0x26A0, 0x26A0}, {0x26A2, 0x26A9}, {0x26AC, 0x26BC}, {0x26C0, 0x26C3}, {0x26E2, 0x26E2}, {0x26E4, 0x26E7}, {0x2700, 0x2704}, {0x2706, 0x2709}, {0x270C, 0x2727}, {0x2729, 0x273C}, {0x273E, 0x274B}, {0x274D, 0x274D}, {0x274F, 0x2752}, {0x2756, 0x2756}, {0x2758, 0x2775}, {0x2780, 0x2794}, {0x2798, 0x27AF}, {0x27B1, 0x27BE}, {0x27C0, 0x27E5}, {0x27EE, 0x2984}, {0x2987, 0x2B1A}, {0x2B1D, 0x2B4F}, {0x2B51, 0x2B54}, {0x2B5A, 0x2B73}, {0x2B76, 0x2B95}, {0x2B97, 0x2C2E}, {0x2C30, 0x2C5E}, {0x2C60, 0x2CF3}, {0x2CF9, 0x2D25}, {0x2D27, 0x2D27}, {0x2D2D, 0x2D2D}, {0x2D30, 0x2D67}, {0x2D6F, 0x2D70}, {0x2D7F, 0x2D96}, {0x2DA0, 0x2DA6}, {0x2DA8, 0x2DAE}, {0x2DB0, 0x2DB6}, {0x2DB8, 0x2DBE}, {0x2DC0, 0x2DC6}, {0x2DC8, 0x2DCE}, {0x2DD0, 0x2DD6}, {0x2DD8, 0x2DDE}, {0x2DE0, 0x2E52}, {0x303F, 0x303F}, {0x4DC0, 0x4DFF}, {0xA4D0, 0xA62B}, {0xA640, 0xA6F7}, {0xA700, 0xA7BF}, {0xA7C2, 0xA7CA}, {0xA7F5, 0xA82C}, {0xA830, 0xA839}, {0xA840, 0xA877}, {0xA880, 0xA8C5}, {0xA8CE, 0xA8D9}, {0xA8E0, 0xA953}, {0xA95F, 0xA95F}, {0xA980, 0xA9CD}, {0xA9CF, 0xA9D9}, {0xA9DE, 0xA9FE}, {0xAA00, 0xAA36}, {0xAA40, 0xAA4D}, {0xAA50, 0xAA59}, {0xAA5C, 0xAAC2}, {0xAADB, 0xAAF6}, {0xAB01, 0xAB06}, {0xAB09, 0xAB0E}, {0xAB11, 0xAB16}, {0xAB20, 0xAB26}, {0xAB28, 0xAB2E}, {0xAB30, 0xAB6B}, {0xAB70, 0xABED}, {0xABF0, 0xABF9}, {0xD7B0, 0xD7C6}, {0xD7CB, 0xD7FB}, {0xD800, 0xDFFF}, {0xFB00, 0xFB06}, {0xFB13, 0xFB17}, {0xFB1D, 0xFB36}, {0xFB38, 0xFB3C}, {0xFB3E, 0xFB3E}, {0xFB40, 0xFB41}, {0xFB43, 0xFB44}, {0xFB46, 0xFBC1}, {0xFBD3, 0xFD3F}, {0xFD50, 0xFD8F}, {0xFD92, 0xFDC7}, {0xFDF0, 0xFDFD}, {0xFE20, 0xFE2F}, {0xFE70, 0xFE74}, {0xFE76, 0xFEFC}, {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFC}, {0x10000, 0x1000B}, {0x1000D, 0x10026}, {0x10028, 0x1003A}, {0x1003C, 0x1003D}, {0x1003F, 0x1004D}, {0x10050, 0x1005D}, {0x10080, 0x100FA}, {0x10100, 0x10102}, {0x10107, 0x10133}, {0x10137, 0x1018E}, {0x10190, 0x1019C}, {0x101A0, 0x101A0}, {0x101D0, 0x101FD}, {0x10280, 0x1029C}, {0x102A0, 0x102D0}, {0x102E0, 0x102FB}, {0x10300, 0x10323}, {0x1032D, 0x1034A}, {0x10350, 0x1037A}, {0x10380, 0x1039D}, {0x1039F, 0x103C3}, {0x103C8, 0x103D5}, {0x10400, 0x1049D}, {0x104A0, 0x104A9}, {0x104B0, 0x104D3}, {0x104D8, 0x104FB}, {0x10500, 0x10527}, {0x10530, 0x10563}, {0x1056F, 0x1056F}, {0x10600, 0x10736}, {0x10740, 0x10755}, {0x10760, 0x10767}, {0x10800, 0x10805}, {0x10808, 0x10808}, {0x1080A, 0x10835}, {0x10837, 0x10838}, {0x1083C, 0x1083C}, {0x1083F, 0x10855}, {0x10857, 0x1089E}, {0x108A7, 0x108AF}, {0x108E0, 0x108F2}, {0x108F4, 0x108F5}, {0x108FB, 0x1091B}, {0x1091F, 0x10939}, {0x1093F, 0x1093F}, {0x10980, 0x109B7}, {0x109BC, 0x109CF}, {0x109D2, 0x10A03}, {0x10A05, 0x10A06}, {0x10A0C, 0x10A13}, {0x10A15, 0x10A17}, {0x10A19, 0x10A35}, {0x10A38, 0x10A3A}, {0x10A3F, 0x10A48}, {0x10A50, 0x10A58}, {0x10A60, 0x10A9F}, {0x10AC0, 0x10AE6}, {0x10AEB, 0x10AF6}, {0x10B00, 0x10B35}, {0x10B39, 0x10B55}, {0x10B58, 0x10B72}, {0x10B78, 0x10B91}, {0x10B99, 0x10B9C}, {0x10BA9, 0x10BAF}, {0x10C00, 0x10C48}, {0x10C80, 0x10CB2}, {0x10CC0, 0x10CF2}, {0x10CFA, 0x10D27}, {0x10D30, 0x10D39}, {0x10E60, 0x10E7E}, {0x10E80, 0x10EA9}, {0x10EAB, 0x10EAD}, {0x10EB0, 0x10EB1}, {0x10F00, 0x10F27}, {0x10F30, 0x10F59}, {0x10FB0, 0x10FCB}, {0x10FE0, 0x10FF6}, {0x11000, 0x1104D}, {0x11052, 0x1106F}, {0x1107F, 0x110C1}, {0x110CD, 0x110CD}, {0x110D0, 0x110E8}, {0x110F0, 0x110F9}, {0x11100, 0x11134}, {0x11136, 0x11147}, {0x11150, 0x11176}, {0x11180, 0x111DF}, {0x111E1, 0x111F4}, {0x11200, 0x11211}, {0x11213, 0x1123E}, {0x11280, 0x11286}, {0x11288, 0x11288}, {0x1128A, 0x1128D}, {0x1128F, 0x1129D}, {0x1129F, 0x112A9}, {0x112B0, 0x112EA}, {0x112F0, 0x112F9}, {0x11300, 0x11303}, {0x11305, 0x1130C}, {0x1130F, 0x11310}, {0x11313, 0x11328}, {0x1132A, 0x11330}, {0x11332, 0x11333}, {0x11335, 0x11339}, {0x1133B, 0x11344}, {0x11347, 0x11348}, {0x1134B, 0x1134D}, {0x11350, 0x11350}, {0x11357, 0x11357}, {0x1135D, 0x11363}, {0x11366, 0x1136C}, {0x11370, 0x11374}, {0x11400, 0x1145B}, {0x1145D, 0x11461}, {0x11480, 0x114C7}, {0x114D0, 0x114D9}, {0x11580, 0x115B5}, {0x115B8, 0x115DD}, {0x11600, 0x11644}, {0x11650, 0x11659}, {0x11660, 0x1166C}, {0x11680, 0x116B8}, {0x116C0, 0x116C9}, {0x11700, 0x1171A}, {0x1171D, 0x1172B}, {0x11730, 0x1173F}, {0x11800, 0x1183B}, {0x118A0, 0x118F2}, {0x118FF, 0x11906}, {0x11909, 0x11909}, {0x1190C, 0x11913}, {0x11915, 0x11916}, {0x11918, 0x11935}, {0x11937, 0x11938}, {0x1193B, 0x11946}, {0x11950, 0x11959}, {0x119A0, 0x119A7}, {0x119AA, 0x119D7}, {0x119DA, 0x119E4}, {0x11A00, 0x11A47}, {0x11A50, 0x11AA2}, {0x11AC0, 0x11AF8}, {0x11C00, 0x11C08}, {0x11C0A, 0x11C36}, {0x11C38, 0x11C45}, {0x11C50, 0x11C6C}, {0x11C70, 0x11C8F}, {0x11C92, 0x11CA7}, {0x11CA9, 0x11CB6}, {0x11D00, 0x11D06}, {0x11D08, 0x11D09}, {0x11D0B, 0x11D36}, {0x11D3A, 0x11D3A}, {0x11D3C, 0x11D3D}, {0x11D3F, 0x11D47}, {0x11D50, 0x11D59}, {0x11D60, 0x11D65}, {0x11D67, 0x11D68}, {0x11D6A, 0x11D8E}, {0x11D90, 0x11D91}, {0x11D93, 0x11D98}, {0x11DA0, 0x11DA9}, {0x11EE0, 0x11EF8}, {0x11FB0, 0x11FB0}, {0x11FC0, 0x11FF1}, {0x11FFF, 0x12399}, {0x12400, 0x1246E}, {0x12470, 0x12474}, {0x12480, 0x12543}, {0x13000, 0x1342E}, {0x13430, 0x13438}, {0x14400, 0x14646}, {0x16800, 0x16A38}, {0x16A40, 0x16A5E}, {0x16A60, 0x16A69}, {0x16A6E, 0x16A6F}, {0x16AD0, 0x16AED}, {0x16AF0, 0x16AF5}, {0x16B00, 0x16B45}, {0x16B50, 0x16B59}, {0x16B5B, 0x16B61}, {0x16B63, 0x16B77}, {0x16B7D, 0x16B8F}, {0x16E40, 0x16E9A}, {0x16F00, 0x16F4A}, {0x16F4F, 0x16F87}, {0x16F8F, 0x16F9F}, {0x1BC00, 0x1BC6A}, {0x1BC70, 0x1BC7C}, {0x1BC80, 0x1BC88}, {0x1BC90, 0x1BC99}, {0x1BC9C, 0x1BCA3}, {0x1D000, 0x1D0F5}, {0x1D100, 0x1D126}, {0x1D129, 0x1D1E8}, {0x1D200, 0x1D245}, {0x1D2E0, 0x1D2F3}, {0x1D300, 0x1D356}, {0x1D360, 0x1D378}, {0x1D400, 0x1D454}, {0x1D456, 0x1D49C}, {0x1D49E, 0x1D49F}, {0x1D4A2, 0x1D4A2}, {0x1D4A5, 0x1D4A6}, {0x1D4A9, 0x1D4AC}, {0x1D4AE, 0x1D4B9}, {0x1D4BB, 0x1D4BB}, {0x1D4BD, 0x1D4C3}, {0x1D4C5, 0x1D505}, {0x1D507, 0x1D50A}, {0x1D50D, 0x1D514}, {0x1D516, 0x1D51C}, {0x1D51E, 0x1D539}, {0x1D53B, 0x1D53E}, {0x1D540, 0x1D544}, {0x1D546, 0x1D546}, {0x1D54A, 0x1D550}, {0x1D552, 0x1D6A5}, {0x1D6A8, 0x1D7CB}, {0x1D7CE, 0x1DA8B}, {0x1DA9B, 0x1DA9F}, {0x1DAA1, 0x1DAAF}, {0x1E000, 0x1E006}, {0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, {0x1E023, 0x1E024}, {0x1E026, 0x1E02A}, {0x1E100, 0x1E12C}, {0x1E130, 0x1E13D}, {0x1E140, 0x1E149}, {0x1E14E, 0x1E14F}, {0x1E2C0, 0x1E2F9}, {0x1E2FF, 0x1E2FF}, {0x1E800, 0x1E8C4}, {0x1E8C7, 0x1E8D6}, {0x1E900, 0x1E94B}, {0x1E950, 0x1E959}, {0x1E95E, 0x1E95F}, {0x1EC71, 0x1ECB4}, {0x1ED01, 0x1ED3D}, {0x1EE00, 0x1EE03}, {0x1EE05, 0x1EE1F}, {0x1EE21, 0x1EE22}, {0x1EE24, 0x1EE24}, {0x1EE27, 0x1EE27}, {0x1EE29, 0x1EE32}, {0x1EE34, 0x1EE37}, {0x1EE39, 0x1EE39}, {0x1EE3B, 0x1EE3B}, {0x1EE42, 0x1EE42}, {0x1EE47, 0x1EE47}, {0x1EE49, 0x1EE49}, {0x1EE4B, 0x1EE4B}, {0x1EE4D, 0x1EE4F}, {0x1EE51, 0x1EE52}, {0x1EE54, 0x1EE54}, {0x1EE57, 0x1EE57}, {0x1EE59, 0x1EE59}, {0x1EE5B, 0x1EE5B}, {0x1EE5D, 0x1EE5D}, {0x1EE5F, 0x1EE5F}, {0x1EE61, 0x1EE62}, {0x1EE64, 0x1EE64}, {0x1EE67, 0x1EE6A}, {0x1EE6C, 0x1EE72}, {0x1EE74, 0x1EE77}, {0x1EE79, 0x1EE7C}, {0x1EE7E, 0x1EE7E}, {0x1EE80, 0x1EE89}, {0x1EE8B, 0x1EE9B}, {0x1EEA1, 0x1EEA3}, {0x1EEA5, 0x1EEA9}, {0x1EEAB, 0x1EEBB}, {0x1EEF0, 0x1EEF1}, {0x1F000, 0x1F003}, {0x1F005, 0x1F02B}, {0x1F030, 0x1F093}, {0x1F0A0, 0x1F0AE}, {0x1F0B1, 0x1F0BF}, {0x1F0C1, 0x1F0CE}, {0x1F0D1, 0x1F0F5}, {0x1F10B, 0x1F10F}, {0x1F12E, 0x1F12F}, {0x1F16A, 0x1F16F}, {0x1F1AD, 0x1F1AD}, {0x1F1E6, 0x1F1FF}, {0x1F321, 0x1F32C}, {0x1F336, 0x1F336}, {0x1F37D, 0x1F37D}, {0x1F394, 0x1F39F}, {0x1F3CB, 0x1F3CE}, {0x1F3D4, 0x1F3DF}, {0x1F3F1, 0x1F3F3}, {0x1F3F5, 0x1F3F7}, {0x1F43F, 0x1F43F}, {0x1F441, 0x1F441}, {0x1F4FD, 0x1F4FE}, {0x1F53E, 0x1F54A}, {0x1F54F, 0x1F54F}, {0x1F568, 0x1F579}, {0x1F57B, 0x1F594}, {0x1F597, 0x1F5A3}, {0x1F5A5, 0x1F5FA}, {0x1F650, 0x1F67F}, {0x1F6C6, 0x1F6CB}, {0x1F6CD, 0x1F6CF}, {0x1F6D3, 0x1F6D4}, {0x1F6E0, 0x1F6EA}, {0x1F6F0, 0x1F6F3}, {0x1F700, 0x1F773}, {0x1F780, 0x1F7D8}, {0x1F800, 0x1F80B}, {0x1F810, 0x1F847}, {0x1F850, 0x1F859}, {0x1F860, 0x1F887}, {0x1F890, 0x1F8AD}, {0x1F8B0, 0x1F8B1}, {0x1F900, 0x1F90B}, {0x1F93B, 0x1F93B}, {0x1F946, 0x1F946}, {0x1FA00, 0x1FA53}, {0x1FA60, 0x1FA6D}, {0x1FB00, 0x1FB92}, {0x1FB94, 0x1FBCA}, {0x1FBF0, 0x1FBF9}, {0xE0001, 0xE0001}, {0xE0020, 0xE007F}, } var emoji = table{ {0x203C, 0x203C}, {0x2049, 0x2049}, {0x2122, 0x2122}, {0x2139, 0x2139}, {0x2194, 0x2199}, {0x21A9, 0x21AA}, {0x231A, 0x231B}, {0x2328, 0x2328}, {0x2388, 0x2388}, {0x23CF, 0x23CF}, {0x23E9, 0x23F3}, {0x23F8, 0x23FA}, {0x24C2, 0x24C2}, {0x25AA, 0x25AB}, {0x25B6, 0x25B6}, {0x25C0, 0x25C0}, {0x25FB, 0x25FE}, {0x2600, 0x2605}, {0x2607, 0x2612}, {0x2614, 0x2685}, {0x2690, 0x2705}, {0x2708, 0x2712}, {0x2714, 0x2714}, {0x2716, 0x2716}, {0x271D, 0x271D}, {0x2721, 0x2721}, {0x2728, 0x2728}, {0x2733, 0x2734}, {0x2744, 0x2744}, {0x2747, 0x2747}, {0x274C, 0x274C}, {0x274E, 0x274E}, {0x2753, 0x2755}, {0x2757, 0x2757}, {0x2763, 0x2767}, {0x2795, 0x2797}, {0x27A1, 0x27A1}, {0x27B0, 0x27B0}, {0x27BF, 0x27BF}, {0x2934, 0x2935}, {0x2B05, 0x2B07}, {0x2B1B, 0x2B1C}, {0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x3030, 0x3030}, {0x303D, 0x303D}, {0x3297, 0x3297}, {0x3299, 0x3299}, {0x1F000, 0x1F0FF}, {0x1F10D, 0x1F10F}, {0x1F12F, 0x1F12F}, {0x1F16C, 0x1F171}, {0x1F17E, 0x1F17F}, {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A}, {0x1F1AD, 0x1F1E5}, {0x1F201, 0x1F20F}, {0x1F21A, 0x1F21A}, {0x1F22F, 0x1F22F}, {0x1F232, 0x1F23A}, {0x1F23C, 0x1F23F}, {0x1F249, 0x1F3FA}, {0x1F400, 0x1F53D}, {0x1F546, 0x1F64F}, {0x1F680, 0x1F6FF}, {0x1F774, 0x1F77F}, {0x1F7D5, 0x1F7FF}, {0x1F80C, 0x1F80F}, {0x1F848, 0x1F84F}, {0x1F85A, 0x1F85F}, {0x1F888, 0x1F88F}, {0x1F8AE, 0x1F8FF}, {0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945}, {0x1F947, 0x1FAFF}, {0x1FC00, 0x1FFFD}, } ================================================ FILE: vendor/github.com/mattn/go-runewidth/runewidth_windows.go ================================================ // +build windows // +build !appengine package runewidth import ( "syscall" ) var ( kernel32 = syscall.NewLazyDLL("kernel32") procGetConsoleOutputCP = kernel32.NewProc("GetConsoleOutputCP") ) // IsEastAsian return true if the current locale is CJK func IsEastAsian() bool { r1, _, _ := procGetConsoleOutputCP.Call() if r1 == 0 { return false } switch int(r1) { case 932, 51932, 936, 949, 950: return true } return false } ================================================ FILE: vendor/github.com/mattn/go-shellwords/.travis.yml ================================================ arch: - amd64 - ppc64le language: go sudo: false go: - tip before_install: - go get -t -v ./... script: - ./go.test.sh after_success: - bash <(curl -s https://codecov.io/bash) ================================================ FILE: vendor/github.com/mattn/go-shellwords/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2017 Yasuhiro Matsumoto Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/mattn/go-shellwords/README.md ================================================ # go-shellwords [![codecov](https://codecov.io/gh/mattn/go-shellwords/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-shellwords) [![Build Status](https://travis-ci.org/mattn/go-shellwords.svg?branch=master)](https://travis-ci.org/mattn/go-shellwords) [![PkgGoDev](https://pkg.go.dev/badge/github.com/mattn/go-shellwords)](https://pkg.go.dev/github.com/mattn/go-shellwords) [![ci](https://github.com/mattn/go-shellwords/ci/badge.svg)](https://github.com/mattn/go-shellwords/actions) Parse line as shell words. ## Usage ```go args, err := shellwords.Parse("./foo --bar=baz") // args should be ["./foo", "--bar=baz"] ``` ```go envs, args, err := shellwords.ParseWithEnvs("FOO=foo BAR=baz ./foo --bar=baz") // envs should be ["FOO=foo", "BAR=baz"] // args should be ["./foo", "--bar=baz"] ``` ```go os.Setenv("FOO", "bar") p := shellwords.NewParser() p.ParseEnv = true args, err := p.Parse("./foo $FOO") // args should be ["./foo", "bar"] ``` ```go p := shellwords.NewParser() p.ParseBacktick = true args, err := p.Parse("./foo `echo $SHELL`") // args should be ["./foo", "/bin/bash"] ``` ```go shellwords.ParseBacktick = true p := shellwords.NewParser() args, err := p.Parse("./foo `echo $SHELL`") // args should be ["./foo", "/bin/bash"] ``` # Thanks This is based on cpan module [Parse::CommandLine](https://metacpan.org/pod/Parse::CommandLine). # License under the MIT License: http://mattn.mit-license.org/2017 # Author Yasuhiro Matsumoto (a.k.a mattn) ================================================ FILE: vendor/github.com/mattn/go-shellwords/go.test.sh ================================================ #!/usr/bin/env bash set -e echo "" > coverage.txt for d in $(go list ./... | grep -v vendor); do go test -coverprofile=profile.out -covermode=atomic "$d" if [ -f profile.out ]; then cat profile.out >> coverage.txt rm profile.out fi done ================================================ FILE: vendor/github.com/mattn/go-shellwords/shellwords.go ================================================ package shellwords import ( "bytes" "errors" "os" "strings" "unicode" ) var ( ParseEnv bool = false ParseBacktick bool = false ) func isSpace(r rune) bool { switch r { case ' ', '\t', '\r', '\n': return true } return false } func replaceEnv(getenv func(string) string, s string) string { if getenv == nil { getenv = os.Getenv } var buf bytes.Buffer rs := []rune(s) for i := 0; i < len(rs); i++ { r := rs[i] if r == '\\' { i++ if i == len(rs) { break } buf.WriteRune(rs[i]) continue } else if r == '$' { i++ if i == len(rs) { buf.WriteRune(r) break } if rs[i] == 0x7b { i++ p := i for ; i < len(rs); i++ { r = rs[i] if r == '\\' { i++ if i == len(rs) { return s } continue } if r == 0x7d || (!unicode.IsLetter(r) && r != '_' && !unicode.IsDigit(r)) { break } } if r != 0x7d { return s } if i > p { buf.WriteString(getenv(s[p:i])) } } else { p := i for ; i < len(rs); i++ { r := rs[i] if r == '\\' { i++ if i == len(rs) { return s } continue } if !unicode.IsLetter(r) && r != '_' && !unicode.IsDigit(r) { break } } if i > p { buf.WriteString(getenv(s[p:i])) i-- } else { buf.WriteString(s[p:]) } } } else { buf.WriteRune(r) } } return buf.String() } type Parser struct { ParseEnv bool ParseBacktick bool Position int Dir string // If ParseEnv is true, use this for getenv. // If nil, use os.Getenv. Getenv func(string) string } func NewParser() *Parser { return &Parser{ ParseEnv: ParseEnv, ParseBacktick: ParseBacktick, Position: 0, Dir: "", } } type argType int const ( argNo argType = iota argSingle argQuoted ) func (p *Parser) Parse(line string) ([]string, error) { args := []string{} buf := "" var escaped, doubleQuoted, singleQuoted, backQuote, dollarQuote bool backtick := "" pos := -1 got := argNo i := -1 loop: for _, r := range line { i++ if escaped { buf += string(r) escaped = false got = argSingle continue } if r == '\\' { if singleQuoted { buf += string(r) } else { escaped = true } continue } if isSpace(r) { if singleQuoted || doubleQuoted || backQuote || dollarQuote { buf += string(r) backtick += string(r) } else if got != argNo { if p.ParseEnv { if got == argSingle { parser := &Parser{ParseEnv: false, ParseBacktick: false, Position: 0, Dir: p.Dir} strs, err := parser.Parse(replaceEnv(p.Getenv, buf)) if err != nil { return nil, err } args = append(args, strs...) } else { args = append(args, replaceEnv(p.Getenv, buf)) } } else { args = append(args, buf) } buf = "" got = argNo } continue } switch r { case '`': if !singleQuoted && !doubleQuoted && !dollarQuote { if p.ParseBacktick { if backQuote { out, err := shellRun(backtick, p.Dir) if err != nil { return nil, err } buf = buf[:len(buf)-len(backtick)] + out } backtick = "" backQuote = !backQuote continue } backtick = "" backQuote = !backQuote } case ')': if !singleQuoted && !doubleQuoted && !backQuote { if p.ParseBacktick { if dollarQuote { out, err := shellRun(backtick, p.Dir) if err != nil { return nil, err } buf = buf[:len(buf)-len(backtick)-2] + out } backtick = "" dollarQuote = !dollarQuote continue } backtick = "" dollarQuote = !dollarQuote } case '(': if !singleQuoted && !doubleQuoted && !backQuote { if !dollarQuote && strings.HasSuffix(buf, "$") { dollarQuote = true buf += "(" continue } else { return nil, errors.New("invalid command line string") } } case '"': if !singleQuoted && !dollarQuote { if doubleQuoted { got = argQuoted } doubleQuoted = !doubleQuoted continue } case '\'': if !doubleQuoted && !dollarQuote { if singleQuoted { got = argQuoted } singleQuoted = !singleQuoted continue } case ';', '&', '|', '<', '>': if !(escaped || singleQuoted || doubleQuoted || backQuote || dollarQuote) { if r == '>' && len(buf) > 0 { if c := buf[0]; '0' <= c && c <= '9' { i -= 1 got = argNo } } pos = i break loop } } got = argSingle buf += string(r) if backQuote || dollarQuote { backtick += string(r) } } if got != argNo { if p.ParseEnv { if got == argSingle { parser := &Parser{ParseEnv: false, ParseBacktick: false, Position: 0, Dir: p.Dir} strs, err := parser.Parse(replaceEnv(p.Getenv, buf)) if err != nil { return nil, err } args = append(args, strs...) } else { args = append(args, replaceEnv(p.Getenv, buf)) } } else { args = append(args, buf) } } if escaped || singleQuoted || doubleQuoted || backQuote || dollarQuote { return nil, errors.New("invalid command line string") } p.Position = pos return args, nil } func (p *Parser) ParseWithEnvs(line string) (envs []string, args []string, err error) { _args, err := p.Parse(line) if err != nil { return nil, nil, err } envs = []string{} args = []string{} parsingEnv := true for _, arg := range _args { if parsingEnv && isEnv(arg) { envs = append(envs, arg) } else { if parsingEnv { parsingEnv = false } args = append(args, arg) } } return envs, args, nil } func isEnv(arg string) bool { return len(strings.Split(arg, "=")) == 2 } func Parse(line string) ([]string, error) { return NewParser().Parse(line) } func ParseWithEnvs(line string) (envs []string, args []string, err error) { return NewParser().ParseWithEnvs(line) } ================================================ FILE: vendor/github.com/mattn/go-shellwords/util_posix.go ================================================ // +build !windows package shellwords import ( "fmt" "os" "os/exec" "strings" ) func shellRun(line, dir string) (string, error) { var shell string if shell = os.Getenv("SHELL"); shell == "" { shell = "/bin/sh" } cmd := exec.Command(shell, "-c", line) if dir != "" { cmd.Dir = dir } b, err := cmd.Output() if err != nil { if eerr, ok := err.(*exec.ExitError); ok { b = eerr.Stderr } return "", fmt.Errorf("%s: %w", string(b), err) } return strings.TrimSpace(string(b)), nil } ================================================ FILE: vendor/github.com/mattn/go-shellwords/util_windows.go ================================================ // +build windows package shellwords import ( "fmt" "os" "os/exec" "strings" ) func shellRun(line, dir string) (string, error) { var shell string if shell = os.Getenv("COMSPEC"); shell == "" { shell = "cmd" } cmd := exec.Command(shell, "/c", line) if dir != "" { cmd.Dir = dir } b, err := cmd.Output() if err != nil { if eerr, ok := err.(*exec.ExitError); ok { b = eerr.Stderr } return "", fmt.Errorf("%s: %w", string(b), err) } return strings.TrimSpace(string(b)), nil } ================================================ FILE: vendor/github.com/mattn/go-tty/.travis.yml ================================================ language: go sudo: false go: - tip ================================================ FILE: vendor/github.com/mattn/go-tty/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2018 Yasuhiro Matsumoto Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/mattn/go-tty/README.md ================================================ # go-tty Simple tty utility ## Usage ```go tty, err := tty.Open() if err != nil { log.Fatal(err) } defer tty.Close() for { r, err := tty.ReadRune() if err != nil { log.Fatal(err) } // handle key event } ``` if you are on windows and want to display ANSI colors, use go-colorable. ```go tty, err := tty.Open() if err != nil { log.Fatal(err) } defer tty.Close() out := colorable.NewColorable(tty.Output()) fmt.Fprintln(out, "\x1b[2J") ``` ## Installation ``` $ go get github.com/mattn/go-tty ``` ## License MIT ## Author Yasuhiro Matsumoto (a.k.a mattn) ================================================ FILE: vendor/github.com/mattn/go-tty/tty.go ================================================ package tty import ( "os" "strings" "unicode" ) func Open() (*TTY, error) { return open("/dev/tty") } func OpenDevice(path string) (*TTY, error) { return open(path) } func (tty *TTY) Raw() (func() error, error) { return tty.raw() } func (tty *TTY) MustRaw() func() error { f, err := tty.raw() if err != nil { panic(err.Error()) } return f } func (tty *TTY) Buffered() bool { return tty.buffered() } func (tty *TTY) ReadRune() (rune, error) { return tty.readRune() } func (tty *TTY) Close() error { return tty.close() } func (tty *TTY) Size() (int, int, error) { return tty.size() } func (tty *TTY) SizePixel() (int, int, int, int, error) { return tty.sizePixel() } func (tty *TTY) Input() *os.File { return tty.input() } func (tty *TTY) Output() *os.File { return tty.output() } // Display types. const ( displayNone = iota displayRune displayMask ) func (tty *TTY) readString(displayType int) (string, error) { rs := []rune{} loop: for { r, err := tty.readRune() if err != nil { return "", err } switch r { case 13: break loop case 8, 127: if len(rs) > 0 { rs = rs[:len(rs)-1] if displayType != displayNone { tty.Output().WriteString("\b \b") } } default: if unicode.IsPrint(r) { rs = append(rs, r) switch displayType { case displayRune: tty.Output().WriteString(string(r)) case displayMask: tty.Output().WriteString("*") } } } } return string(rs), nil } func (tty *TTY) ReadString() (string, error) { defer tty.Output().WriteString("\n") return tty.readString(displayRune) } func (tty *TTY) ReadPassword() (string, error) { defer tty.Output().WriteString("\n") return tty.readString(displayMask) } func (tty *TTY) ReadPasswordNoEcho() (string, error) { defer tty.Output().WriteString("\n") return tty.readString(displayNone) } func (tty *TTY) ReadPasswordClear() (string, error) { s, err := tty.readString(displayMask) tty.Output().WriteString( strings.Repeat("\b", len(s)) + strings.Repeat(" ", len(s)) + strings.Repeat("\b", len(s))) return s, err } type WINSIZE struct { W int H int } func (tty *TTY) SIGWINCH() <-chan WINSIZE { return tty.sigwinch() } ================================================ FILE: vendor/github.com/mattn/go-tty/tty_bsd.go ================================================ // +build darwin dragonfly freebsd netbsd openbsd package tty import ( "syscall" ) const ( ioctlReadTermios = syscall.TIOCGETA ioctlWriteTermios = syscall.TIOCSETA ) ================================================ FILE: vendor/github.com/mattn/go-tty/tty_linux.go ================================================ // +build linux package tty const ( ioctlReadTermios = 0x5401 // syscall.TCGETS ioctlWriteTermios = 0x5402 // syscall.TCSETS ) ================================================ FILE: vendor/github.com/mattn/go-tty/tty_plan9.go ================================================ package tty import ( "bufio" "errors" "os" "syscall" ) type TTY struct { in *os.File bin *bufio.Reader out *os.File } func open(path string) (*TTY, error) { tty := new(TTY) in, err := os.Open("/dev/cons") if err != nil { return nil, err } tty.in = in tty.bin = bufio.NewReader(in) out, err := os.OpenFile("/dev/cons", syscall.O_WRONLY, 0) if err != nil { return nil, err } tty.out = out return tty, nil } func (tty *TTY) buffered() bool { return tty.bin.Buffered() > 0 } func (tty *TTY) readRune() (rune, error) { r, _, err := tty.bin.ReadRune() return r, err } func (tty *TTY) close() (err error) { if err2 := tty.in.Close(); err2 != nil { err = err2 } if err2 := tty.out.Close(); err2 != nil { err = err2 } return } func (tty *TTY) size() (int, int, error) { return 80, 24, nil } func (tty *TTY) sizePixel() (int, int, int, int, error) { x, y, _ := tty.size() return x, y, -1, -1, errors.New("no implemented method for querying size in pixels on Plan 9") } func (tty *TTY) input() *os.File { return tty.in } func (tty *TTY) output() *os.File { return tty.out } ================================================ FILE: vendor/github.com/mattn/go-tty/tty_sys5.go ================================================ // +build solaris package tty import ( "golang.org/x/sys/unix" ) const ( ioctlReadTermios = unix.TCGETA ioctlWriteTermios = unix.TCSETA ) ================================================ FILE: vendor/github.com/mattn/go-tty/tty_unix.go ================================================ // +build !windows // +build !plan9 package tty import ( "bufio" "os" "os/signal" "syscall" "unsafe" "golang.org/x/sys/unix" ) type TTY struct { in *os.File bin *bufio.Reader out *os.File termios syscall.Termios ss chan os.Signal } func open(path string) (*TTY, error) { tty := new(TTY) in, err := os.Open(path) if err != nil { return nil, err } tty.in = in tty.bin = bufio.NewReader(in) out, err := os.OpenFile(path, syscall.O_WRONLY, 0) if err != nil { return nil, err } tty.out = out if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(tty.in.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&tty.termios))); err != 0 { return nil, err } newios := tty.termios newios.Iflag &^= syscall.ISTRIP | syscall.INLCR | syscall.ICRNL | syscall.IGNCR | syscall.IXOFF newios.Lflag &^= syscall.ECHO | syscall.ICANON /*| syscall.ISIG*/ if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(tty.in.Fd()), ioctlWriteTermios, uintptr(unsafe.Pointer(&newios))); err != 0 { return nil, err } tty.ss = make(chan os.Signal, 1) return tty, nil } func (tty *TTY) buffered() bool { return tty.bin.Buffered() > 0 } func (tty *TTY) readRune() (rune, error) { r, _, err := tty.bin.ReadRune() return r, err } func (tty *TTY) close() error { signal.Stop(tty.ss) close(tty.ss) _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(tty.in.Fd()), ioctlWriteTermios, uintptr(unsafe.Pointer(&tty.termios))) return err } func (tty *TTY) size() (int, int, error) { x, y, _, _, err := tty.sizePixel() return x, y, err } func (tty *TTY) sizePixel() (int, int, int, int, error) { var dim [4]uint16 if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(tty.out.Fd()), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dim))); err != 0 { return -1, -1, -1, -1, err } return int(dim[1]), int(dim[0]), int(dim[2]), int(dim[3]), nil } func (tty *TTY) input() *os.File { return tty.in } func (tty *TTY) output() *os.File { return tty.out } func (tty *TTY) raw() (func() error, error) { termios, err := unix.IoctlGetTermios(int(tty.in.Fd()), ioctlReadTermios) if err != nil { return nil, err } backup := *termios termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON termios.Oflag &^= unix.OPOST termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN termios.Cflag &^= unix.CSIZE | unix.PARENB termios.Cflag |= unix.CS8 termios.Cc[unix.VMIN] = 1 termios.Cc[unix.VTIME] = 0 if err := unix.IoctlSetTermios(int(tty.in.Fd()), ioctlWriteTermios, termios); err != nil { return nil, err } return func() error { if err := unix.IoctlSetTermios(int(tty.in.Fd()), ioctlWriteTermios, &backup); err != nil { return err } return nil }, nil } func (tty *TTY) sigwinch() <-chan WINSIZE { signal.Notify(tty.ss, syscall.SIGWINCH) ws := make(chan WINSIZE) go func() { defer close(ws) for sig := range tty.ss { if sig != syscall.SIGWINCH { continue } w, h, err := tty.size() if err != nil { continue } // send but do not block for it select { case ws <- WINSIZE{W: w, H: h}: default: } } }() return ws } ================================================ FILE: vendor/github.com/mattn/go-tty/tty_windows.go ================================================ // +build windows package tty import ( "context" "errors" "os" "syscall" "unsafe" "github.com/mattn/go-isatty" ) const ( rightAltPressed = 1 leftAltPressed = 2 rightCtrlPressed = 4 leftCtrlPressed = 8 shiftPressed = 0x0010 ctrlPressed = rightCtrlPressed | leftCtrlPressed altPressed = rightAltPressed | leftAltPressed ) const ( enableProcessedInput = 0x1 enableLineInput = 0x2 enableEchoInput = 0x4 enableWindowInput = 0x8 enableMouseInput = 0x10 enableInsertMode = 0x20 enableQuickEditMode = 0x40 enableExtendedFlag = 0x80 enableProcessedOutput = 1 enableWrapAtEolOutput = 2 keyEvent = 0x1 mouseEvent = 0x2 windowBufferSizeEvent = 0x4 ) var kernel32 = syscall.NewLazyDLL("kernel32.dll") var ( procAllocConsole = kernel32.NewProc("AllocConsole") procSetStdHandle = kernel32.NewProc("SetStdHandle") procGetStdHandle = kernel32.NewProc("GetStdHandle") procSetConsoleScreenBufferSize = kernel32.NewProc("SetConsoleScreenBufferSize") procCreateConsoleScreenBuffer = kernel32.NewProc("CreateConsoleScreenBuffer") procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") procWriteConsoleOutputCharacter = kernel32.NewProc("WriteConsoleOutputCharacterW") procWriteConsoleOutputAttribute = kernel32.NewProc("WriteConsoleOutputAttribute") procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo") procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo") procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") procReadConsoleInput = kernel32.NewProc("ReadConsoleInputW") procGetConsoleMode = kernel32.NewProc("GetConsoleMode") procSetConsoleMode = kernel32.NewProc("SetConsoleMode") procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute") procScrollConsoleScreenBuffer = kernel32.NewProc("ScrollConsoleScreenBufferW") ) type wchar uint16 type short int16 type dword uint32 type word uint16 type coord struct { x short y short } type smallRect struct { left short top short right short bottom short } type consoleScreenBufferInfo struct { size coord cursorPosition coord attributes word window smallRect maximumWindowSize coord } type consoleCursorInfo struct { size dword visible int32 } type inputRecord struct { eventType word _ [2]byte event [16]byte } type keyEventRecord struct { keyDown int32 repeatCount word virtualKeyCode word virtualScanCode word unicodeChar wchar controlKeyState dword } type windowBufferSizeRecord struct { size coord } type mouseEventRecord struct { mousePos coord buttonState dword controlKeyState dword eventFlags dword } type charInfo struct { unicodeChar wchar attributes word } type TTY struct { in *os.File out *os.File st uint32 rs []rune ws chan WINSIZE sigwinchCtx context.Context sigwinchCtxCancel context.CancelFunc } func readConsoleInput(fd uintptr, record *inputRecord) (err error) { var w uint32 r1, _, err := procReadConsoleInput.Call(fd, uintptr(unsafe.Pointer(record)), 1, uintptr(unsafe.Pointer(&w))) if r1 == 0 { return err } return nil } func open(path string) (*TTY, error) { tty := new(TTY) if false && isatty.IsTerminal(os.Stdin.Fd()) { tty.in = os.Stdin } else { in, err := syscall.Open("CONIN$", syscall.O_RDWR, 0) if err != nil { return nil, err } tty.in = os.NewFile(uintptr(in), "/dev/tty") } if isatty.IsTerminal(os.Stdout.Fd()) { tty.out = os.Stdout } else { procAllocConsole.Call() out, err := syscall.Open("CONOUT$", syscall.O_RDWR, 0) if err != nil { return nil, err } tty.out = os.NewFile(uintptr(out), "/dev/tty") } h := tty.in.Fd() var st uint32 r1, _, err := procGetConsoleMode.Call(h, uintptr(unsafe.Pointer(&st))) if r1 == 0 { return nil, err } tty.st = st st &^= enableEchoInput st &^= enableInsertMode st &^= enableLineInput st &^= enableMouseInput st &^= enableWindowInput st &^= enableExtendedFlag st &^= enableQuickEditMode // ignore error procSetConsoleMode.Call(h, uintptr(st)) tty.ws = make(chan WINSIZE) tty.sigwinchCtx, tty.sigwinchCtxCancel = context.WithCancel(context.Background()) return tty, nil } func (tty *TTY) buffered() bool { return len(tty.rs) > 0 } func (tty *TTY) readRune() (rune, error) { if len(tty.rs) > 0 { r := tty.rs[0] tty.rs = tty.rs[1:] return r, nil } var ir inputRecord err := readConsoleInput(tty.in.Fd(), &ir) if err != nil { return 0, err } switch ir.eventType { case windowBufferSizeEvent: wr := (*windowBufferSizeRecord)(unsafe.Pointer(&ir.event)) ws := WINSIZE{ W: int(wr.size.x), H: int(wr.size.y), } if err := tty.sigwinchCtx.Err(); err != nil { // closing // the following select might panic without this guard close return 0, err } select { case tty.ws <- ws: case <-tty.sigwinchCtx.Done(): return 0, tty.sigwinchCtx.Err() default: return 0, nil // no one is currently trying to read } case keyEvent: kr := (*keyEventRecord)(unsafe.Pointer(&ir.event)) if kr.keyDown != 0 { if kr.controlKeyState&altPressed != 0 && kr.unicodeChar > 0 { tty.rs = []rune{rune(kr.unicodeChar)} return rune(0x1b), nil } if kr.unicodeChar > 0 { if kr.controlKeyState&shiftPressed != 0 { switch kr.unicodeChar { case 0x09: tty.rs = []rune{0x5b, 0x5a} return rune(0x1b), nil } } return rune(kr.unicodeChar), nil } vk := kr.virtualKeyCode if kr.controlKeyState&ctrlPressed != 0 { switch vk { case 0x21: // ctrl-page-up tty.rs = []rune{0x5b, 0x35, 0x3B, 0x35, 0x7e} return rune(0x1b), nil case 0x22: // ctrl-page-down tty.rs = []rune{0x5b, 0x36, 0x3B, 0x35, 0x7e} return rune(0x1b), nil case 0x23: // ctrl-end tty.rs = []rune{0x5b, 0x31, 0x3B, 0x35, 0x46} return rune(0x1b), nil case 0x24: // ctrl-home tty.rs = []rune{0x5b, 0x31, 0x3B, 0x35, 0x48} return rune(0x1b), nil case 0x25: // ctrl-left tty.rs = []rune{0x5b, 0x31, 0x3B, 0x35, 0x44} return rune(0x1b), nil case 0x26: // ctrl-up tty.rs = []rune{0x5b, 0x31, 0x3B, 0x35, 0x41} return rune(0x1b), nil case 0x27: // ctrl-right tty.rs = []rune{0x5b, 0x31, 0x3B, 0x35, 0x43} return rune(0x1b), nil case 0x28: // ctrl-down tty.rs = []rune{0x5b, 0x31, 0x3B, 0x35, 0x42} return rune(0x1b), nil case 0x2e: // ctrl-delete tty.rs = []rune{0x5b, 0x33, 0x3B, 0x35, 0x7e} return rune(0x1b), nil } } switch vk { case 0x21: // page-up tty.rs = []rune{0x5b, 0x35, 0x7e} return rune(0x1b), nil case 0x22: // page-down tty.rs = []rune{0x5b, 0x36, 0x7e} return rune(0x1b), nil case 0x23: // end tty.rs = []rune{0x5b, 0x46} return rune(0x1b), nil case 0x24: // home tty.rs = []rune{0x5b, 0x48} return rune(0x1b), nil case 0x25: // left tty.rs = []rune{0x5b, 0x44} return rune(0x1b), nil case 0x26: // up tty.rs = []rune{0x5b, 0x41} return rune(0x1b), nil case 0x27: // right tty.rs = []rune{0x5b, 0x43} return rune(0x1b), nil case 0x28: // down tty.rs = []rune{0x5b, 0x42} return rune(0x1b), nil case 0x2e: // delete tty.rs = []rune{0x5b, 0x33, 0x7e} return rune(0x1b), nil case 0x70, 0x71, 0x72, 0x73: // F1,F2,F3,F4 tty.rs = []rune{0x5b, 0x4f, rune(vk) - 0x20} return rune(0x1b), nil case 0x074, 0x75, 0x76, 0x77: // F5,F6,F7,F8 tty.rs = []rune{0x5b, 0x31, rune(vk) - 0x3f, 0x7e} return rune(0x1b), nil case 0x78, 0x79: // F9,F10 tty.rs = []rune{0x5b, 0x32, rune(vk) - 0x48, 0x7e} return rune(0x1b), nil case 0x7a, 0x7b: // F11,F12 tty.rs = []rune{0x5b, 0x32, rune(vk) - 0x47, 0x7e} return rune(0x1b), nil } return 0, nil } } return 0, nil } func (tty *TTY) close() error { procSetConsoleMode.Call(tty.in.Fd(), uintptr(tty.st)) tty.sigwinchCtxCancel() close(tty.ws) return nil } func (tty *TTY) size() (int, int, error) { var csbi consoleScreenBufferInfo r1, _, err := procGetConsoleScreenBufferInfo.Call(tty.out.Fd(), uintptr(unsafe.Pointer(&csbi))) if r1 == 0 { return 0, 0, err } return int(csbi.window.right - csbi.window.left + 1), int(csbi.window.bottom - csbi.window.top + 1), nil } func (tty *TTY) sizePixel() (int, int, int, int, error) { x, y, err := tty.size() if err != nil { x = -1 y = -1 } return x, y, -1, -1, errors.New("no implemented method for querying size in pixels on Windows") } func (tty *TTY) input() *os.File { return tty.in } func (tty *TTY) output() *os.File { return tty.out } func (tty *TTY) raw() (func() error, error) { var st uint32 r1, _, err := procGetConsoleMode.Call(tty.in.Fd(), uintptr(unsafe.Pointer(&st))) if r1 == 0 { return nil, err } mode := st &^ (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput) r1, _, err = procSetConsoleMode.Call(tty.in.Fd(), uintptr(mode)) if r1 == 0 { return nil, err } return func() error { r1, _, err := procSetConsoleMode.Call(tty.in.Fd(), uintptr(st)) if r1 == 0 { return err } return nil }, nil } func (tty *TTY) sigwinch() <-chan WINSIZE { return tty.ws } ================================================ FILE: vendor/github.com/mitchellh/go-homedir/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2013 Mitchell Hashimoto Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/mitchellh/go-homedir/README.md ================================================ # go-homedir This is a Go library for detecting the user's home directory without the use of cgo, so the library can be used in cross-compilation environments. Usage is incredibly simple, just call `homedir.Dir()` to get the home directory for a user, and `homedir.Expand()` to expand the `~` in a path to the home directory. **Why not just use `os/user`?** The built-in `os/user` package requires cgo on Darwin systems. This means that any Go code that uses that package cannot cross compile. But 99% of the time the use for `os/user` is just to retrieve the home directory, which we can do for the current user without cgo. This library does that, enabling cross-compilation. ================================================ FILE: vendor/github.com/mitchellh/go-homedir/homedir.go ================================================ package homedir import ( "bytes" "errors" "os" "os/exec" "path/filepath" "runtime" "strconv" "strings" "sync" ) // DisableCache will disable caching of the home directory. Caching is enabled // by default. var DisableCache bool var homedirCache string var cacheLock sync.RWMutex // Dir returns the home directory for the executing user. // // This uses an OS-specific method for discovering the home directory. // An error is returned if a home directory cannot be detected. func Dir() (string, error) { if !DisableCache { cacheLock.RLock() cached := homedirCache cacheLock.RUnlock() if cached != "" { return cached, nil } } cacheLock.Lock() defer cacheLock.Unlock() var result string var err error if runtime.GOOS == "windows" { result, err = dirWindows() } else { // Unix-like system, so just assume Unix result, err = dirUnix() } if err != nil { return "", err } homedirCache = result return result, nil } // Expand expands the path to include the home directory if the path // is prefixed with `~`. If it isn't prefixed with `~`, the path is // returned as-is. func Expand(path string) (string, error) { if len(path) == 0 { return path, nil } if path[0] != '~' { return path, nil } if len(path) > 1 && path[1] != '/' && path[1] != '\\' { return "", errors.New("cannot expand user-specific home dir") } dir, err := Dir() if err != nil { return "", err } return filepath.Join(dir, path[1:]), nil } // Reset clears the cache, forcing the next call to Dir to re-detect // the home directory. This generally never has to be called, but can be // useful in tests if you're modifying the home directory via the HOME // env var or something. func Reset() { cacheLock.Lock() defer cacheLock.Unlock() homedirCache = "" } func dirUnix() (string, error) { homeEnv := "HOME" if runtime.GOOS == "plan9" { // On plan9, env vars are lowercase. homeEnv = "home" } // First prefer the HOME environmental variable if home := os.Getenv(homeEnv); home != "" { return home, nil } var stdout bytes.Buffer // If that fails, try OS specific commands if runtime.GOOS == "darwin" { cmd := exec.Command("sh", "-c", `dscl -q . -read /Users/"$(whoami)" NFSHomeDirectory | sed 's/^[^ ]*: //'`) cmd.Stdout = &stdout if err := cmd.Run(); err == nil { result := strings.TrimSpace(stdout.String()) if result != "" { return result, nil } } } else { cmd := exec.Command("getent", "passwd", strconv.Itoa(os.Getuid())) cmd.Stdout = &stdout if err := cmd.Run(); err != nil { // If the error is ErrNotFound, we ignore it. Otherwise, return it. if err != exec.ErrNotFound { return "", err } } else { if passwd := strings.TrimSpace(stdout.String()); passwd != "" { // username:password:uid:gid:gecos:home:shell passwdParts := strings.SplitN(passwd, ":", 7) if len(passwdParts) > 5 { return passwdParts[5], nil } } } } // If all else fails, try the shell stdout.Reset() cmd := exec.Command("sh", "-c", "cd && pwd") cmd.Stdout = &stdout if err := cmd.Run(); err != nil { return "", err } result := strings.TrimSpace(stdout.String()) if result == "" { return "", errors.New("blank output when reading home directory") } return result, nil } func dirWindows() (string, error) { // First prefer the HOME environmental variable if home := os.Getenv("HOME"); home != "" { return home, nil } // Prefer standard environment variable USERPROFILE if home := os.Getenv("USERPROFILE"); home != "" { return home, nil } drive := os.Getenv("HOMEDRIVE") path := os.Getenv("HOMEPATH") home := drive + path if drive == "" || path == "" { return "", errors.New("HOMEDRIVE, HOMEPATH, or USERPROFILE are blank") } return home, nil } ================================================ FILE: vendor/github.com/mitchellh/mapstructure/CHANGELOG.md ================================================ ## 1.4.3 * Fix cases where `json.Number` didn't decode properly [GH-261] ## 1.4.2 * Custom name matchers to support any sort of casing, formatting, etc. for field names. [GH-250] * Fix possible panic in ComposeDecodeHookFunc [GH-251] ## 1.4.1 * Fix regression where `*time.Time` value would be set to empty and not be sent to decode hooks properly [GH-232] ## 1.4.0 * A new decode hook type `DecodeHookFuncValue` has been added that has access to the full values. [GH-183] * Squash is now supported with embedded fields that are struct pointers [GH-205] * Empty strings will convert to 0 for all numeric types when weakly decoding [GH-206] ## 1.3.3 * Decoding maps from maps creates a settable value for decode hooks [GH-203] ## 1.3.2 * Decode into interface type with a struct value is supported [GH-187] ## 1.3.1 * Squash should only squash embedded structs. [GH-194] ## 1.3.0 * Added `",omitempty"` support. This will ignore zero values in the source structure when encoding. [GH-145] ## 1.2.3 * Fix duplicate entries in Keys list with pointer values. [GH-185] ## 1.2.2 * Do not add unsettable (unexported) values to the unused metadata key or "remain" value. [GH-150] ## 1.2.1 * Go modules checksum mismatch fix ## 1.2.0 * Added support to capture unused values in a field using the `",remain"` value in the mapstructure tag. There is an example to showcase usage. * Added `DecoderConfig` option to always squash embedded structs * `json.Number` can decode into `uint` types * Empty slices are preserved and not replaced with nil slices * Fix panic that can occur in when decoding a map into a nil slice of structs * Improved package documentation for godoc ## 1.1.2 * Fix error when decode hook decodes interface implementation into interface type. [GH-140] ## 1.1.1 * Fix panic that can happen in `decodePtr` ## 1.1.0 * Added `StringToIPHookFunc` to convert `string` to `net.IP` and `net.IPNet` [GH-133] * Support struct to struct decoding [GH-137] * If source map value is nil, then destination map value is nil (instead of empty) * If source slice value is nil, then destination slice value is nil (instead of empty) * If source pointer is nil, then destination pointer is set to nil (instead of allocated zero value of type) ## 1.0.0 * Initial tagged stable release. ================================================ FILE: vendor/github.com/mitchellh/mapstructure/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2013 Mitchell Hashimoto Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/mitchellh/mapstructure/README.md ================================================ # mapstructure [![Godoc](https://godoc.org/github.com/mitchellh/mapstructure?status.svg)](https://godoc.org/github.com/mitchellh/mapstructure) mapstructure is a Go library for decoding generic map values to structures and vice versa, while providing helpful error handling. This library is most useful when decoding values from some data stream (JSON, Gob, etc.) where you don't _quite_ know the structure of the underlying data until you read a part of it. You can therefore read a `map[string]interface{}` and use this library to decode it into the proper underlying native Go structure. ## Installation Standard `go get`: ``` $ go get github.com/mitchellh/mapstructure ``` ## Usage & Example For usage and examples see the [Godoc](http://godoc.org/github.com/mitchellh/mapstructure). The `Decode` function has examples associated with it there. ## But Why?! Go offers fantastic standard libraries for decoding formats such as JSON. The standard method is to have a struct pre-created, and populate that struct from the bytes of the encoded format. This is great, but the problem is if you have configuration or an encoding that changes slightly depending on specific fields. For example, consider this JSON: ```json { "type": "person", "name": "Mitchell" } ``` Perhaps we can't populate a specific structure without first reading the "type" field from the JSON. We could always do two passes over the decoding of the JSON (reading the "type" first, and the rest later). However, it is much simpler to just decode this into a `map[string]interface{}` structure, read the "type" key, then use something like this library to decode it into the proper structure. ================================================ FILE: vendor/github.com/mitchellh/mapstructure/decode_hooks.go ================================================ package mapstructure import ( "encoding" "errors" "fmt" "net" "reflect" "strconv" "strings" "time" ) // typedDecodeHook takes a raw DecodeHookFunc (an interface{}) and turns // it into the proper DecodeHookFunc type, such as DecodeHookFuncType. func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc { // Create variables here so we can reference them with the reflect pkg var f1 DecodeHookFuncType var f2 DecodeHookFuncKind var f3 DecodeHookFuncValue // Fill in the variables into this interface and the rest is done // automatically using the reflect package. potential := []interface{}{f1, f2, f3} v := reflect.ValueOf(h) vt := v.Type() for _, raw := range potential { pt := reflect.ValueOf(raw).Type() if vt.ConvertibleTo(pt) { return v.Convert(pt).Interface() } } return nil } // DecodeHookExec executes the given decode hook. This should be used // since it'll naturally degrade to the older backwards compatible DecodeHookFunc // that took reflect.Kind instead of reflect.Type. func DecodeHookExec( raw DecodeHookFunc, from reflect.Value, to reflect.Value) (interface{}, error) { switch f := typedDecodeHook(raw).(type) { case DecodeHookFuncType: return f(from.Type(), to.Type(), from.Interface()) case DecodeHookFuncKind: return f(from.Kind(), to.Kind(), from.Interface()) case DecodeHookFuncValue: return f(from, to) default: return nil, errors.New("invalid decode hook signature") } } // ComposeDecodeHookFunc creates a single DecodeHookFunc that // automatically composes multiple DecodeHookFuncs. // // The composed funcs are called in order, with the result of the // previous transformation. func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc { return func(f reflect.Value, t reflect.Value) (interface{}, error) { var err error data := f.Interface() newFrom := f for _, f1 := range fs { data, err = DecodeHookExec(f1, newFrom, t) if err != nil { return nil, err } newFrom = reflect.ValueOf(data) } return data, nil } } // StringToSliceHookFunc returns a DecodeHookFunc that converts // string to []string by splitting on the given sep. func StringToSliceHookFunc(sep string) DecodeHookFunc { return func( f reflect.Kind, t reflect.Kind, data interface{}) (interface{}, error) { if f != reflect.String || t != reflect.Slice { return data, nil } raw := data.(string) if raw == "" { return []string{}, nil } return strings.Split(raw, sep), nil } } // StringToTimeDurationHookFunc returns a DecodeHookFunc that converts // strings to time.Duration. func StringToTimeDurationHookFunc() DecodeHookFunc { return func( f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { if f.Kind() != reflect.String { return data, nil } if t != reflect.TypeOf(time.Duration(5)) { return data, nil } // Convert it by parsing return time.ParseDuration(data.(string)) } } // StringToIPHookFunc returns a DecodeHookFunc that converts // strings to net.IP func StringToIPHookFunc() DecodeHookFunc { return func( f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { if f.Kind() != reflect.String { return data, nil } if t != reflect.TypeOf(net.IP{}) { return data, nil } // Convert it by parsing ip := net.ParseIP(data.(string)) if ip == nil { return net.IP{}, fmt.Errorf("failed parsing ip %v", data) } return ip, nil } } // StringToIPNetHookFunc returns a DecodeHookFunc that converts // strings to net.IPNet func StringToIPNetHookFunc() DecodeHookFunc { return func( f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { if f.Kind() != reflect.String { return data, nil } if t != reflect.TypeOf(net.IPNet{}) { return data, nil } // Convert it by parsing _, net, err := net.ParseCIDR(data.(string)) return net, err } } // StringToTimeHookFunc returns a DecodeHookFunc that converts // strings to time.Time. func StringToTimeHookFunc(layout string) DecodeHookFunc { return func( f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { if f.Kind() != reflect.String { return data, nil } if t != reflect.TypeOf(time.Time{}) { return data, nil } // Convert it by parsing return time.Parse(layout, data.(string)) } } // WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to // the decoder. // // Note that this is significantly different from the WeaklyTypedInput option // of the DecoderConfig. func WeaklyTypedHook( f reflect.Kind, t reflect.Kind, data interface{}) (interface{}, error) { dataVal := reflect.ValueOf(data) switch t { case reflect.String: switch f { case reflect.Bool: if dataVal.Bool() { return "1", nil } return "0", nil case reflect.Float32: return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil case reflect.Int: return strconv.FormatInt(dataVal.Int(), 10), nil case reflect.Slice: dataType := dataVal.Type() elemKind := dataType.Elem().Kind() if elemKind == reflect.Uint8 { return string(dataVal.Interface().([]uint8)), nil } case reflect.Uint: return strconv.FormatUint(dataVal.Uint(), 10), nil } } return data, nil } func RecursiveStructToMapHookFunc() DecodeHookFunc { return func(f reflect.Value, t reflect.Value) (interface{}, error) { if f.Kind() != reflect.Struct { return f.Interface(), nil } var i interface{} = struct{}{} if t.Type() != reflect.TypeOf(&i).Elem() { return f.Interface(), nil } m := make(map[string]interface{}) t.Set(reflect.ValueOf(m)) return f.Interface(), nil } } // TextUnmarshallerHookFunc returns a DecodeHookFunc that applies // strings to the UnmarshalText function, when the target type // implements the encoding.TextUnmarshaler interface func TextUnmarshallerHookFunc() DecodeHookFuncType { return func( f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { if f.Kind() != reflect.String { return data, nil } result := reflect.New(t).Interface() unmarshaller, ok := result.(encoding.TextUnmarshaler) if !ok { return data, nil } if err := unmarshaller.UnmarshalText([]byte(data.(string))); err != nil { return nil, err } return result, nil } } ================================================ FILE: vendor/github.com/mitchellh/mapstructure/error.go ================================================ package mapstructure import ( "errors" "fmt" "sort" "strings" ) // Error implements the error interface and can represents multiple // errors that occur in the course of a single decode. type Error struct { Errors []string } func (e *Error) Error() string { points := make([]string, len(e.Errors)) for i, err := range e.Errors { points[i] = fmt.Sprintf("* %s", err) } sort.Strings(points) return fmt.Sprintf( "%d error(s) decoding:\n\n%s", len(e.Errors), strings.Join(points, "\n")) } // WrappedErrors implements the errwrap.Wrapper interface to make this // return value more useful with the errwrap and go-multierror libraries. func (e *Error) WrappedErrors() []error { if e == nil { return nil } result := make([]error, len(e.Errors)) for i, e := range e.Errors { result[i] = errors.New(e) } return result } func appendErrors(errors []string, err error) []string { switch e := err.(type) { case *Error: return append(errors, e.Errors...) default: return append(errors, e.Error()) } } ================================================ FILE: vendor/github.com/mitchellh/mapstructure/mapstructure.go ================================================ // Package mapstructure exposes functionality to convert one arbitrary // Go type into another, typically to convert a map[string]interface{} // into a native Go structure. // // The Go structure can be arbitrarily complex, containing slices, // other structs, etc. and the decoder will properly decode nested // maps and so on into the proper structures in the native Go struct. // See the examples to see what the decoder is capable of. // // The simplest function to start with is Decode. // // Field Tags // // When decoding to a struct, mapstructure will use the field name by // default to perform the mapping. For example, if a struct has a field // "Username" then mapstructure will look for a key in the source value // of "username" (case insensitive). // // type User struct { // Username string // } // // You can change the behavior of mapstructure by using struct tags. // The default struct tag that mapstructure looks for is "mapstructure" // but you can customize it using DecoderConfig. // // Renaming Fields // // To rename the key that mapstructure looks for, use the "mapstructure" // tag and set a value directly. For example, to change the "username" example // above to "user": // // type User struct { // Username string `mapstructure:"user"` // } // // Embedded Structs and Squashing // // Embedded structs are treated as if they're another field with that name. // By default, the two structs below are equivalent when decoding with // mapstructure: // // type Person struct { // Name string // } // // type Friend struct { // Person // } // // type Friend struct { // Person Person // } // // This would require an input that looks like below: // // map[string]interface{}{ // "person": map[string]interface{}{"name": "alice"}, // } // // If your "person" value is NOT nested, then you can append ",squash" to // your tag value and mapstructure will treat it as if the embedded struct // were part of the struct directly. Example: // // type Friend struct { // Person `mapstructure:",squash"` // } // // Now the following input would be accepted: // // map[string]interface{}{ // "name": "alice", // } // // When decoding from a struct to a map, the squash tag squashes the struct // fields into a single map. Using the example structs from above: // // Friend{Person: Person{Name: "alice"}} // // Will be decoded into a map: // // map[string]interface{}{ // "name": "alice", // } // // DecoderConfig has a field that changes the behavior of mapstructure // to always squash embedded structs. // // Remainder Values // // If there are any unmapped keys in the source value, mapstructure by // default will silently ignore them. You can error by setting ErrorUnused // in DecoderConfig. If you're using Metadata you can also maintain a slice // of the unused keys. // // You can also use the ",remain" suffix on your tag to collect all unused // values in a map. The field with this tag MUST be a map type and should // probably be a "map[string]interface{}" or "map[interface{}]interface{}". // See example below: // // type Friend struct { // Name string // Other map[string]interface{} `mapstructure:",remain"` // } // // Given the input below, Other would be populated with the other // values that weren't used (everything but "name"): // // map[string]interface{}{ // "name": "bob", // "address": "123 Maple St.", // } // // Omit Empty Values // // When decoding from a struct to any other value, you may use the // ",omitempty" suffix on your tag to omit that value if it equates to // the zero value. The zero value of all types is specified in the Go // specification. // // For example, the zero type of a numeric type is zero ("0"). If the struct // field value is zero and a numeric type, the field is empty, and it won't // be encoded into the destination type. // // type Source { // Age int `mapstructure:",omitempty"` // } // // Unexported fields // // Since unexported (private) struct fields cannot be set outside the package // where they are defined, the decoder will simply skip them. // // For this output type definition: // // type Exported struct { // private string // this unexported field will be skipped // Public string // } // // Using this map as input: // // map[string]interface{}{ // "private": "I will be ignored", // "Public": "I made it through!", // } // // The following struct will be decoded: // // type Exported struct { // private: "" // field is left with an empty string (zero value) // Public: "I made it through!" // } // // Other Configuration // // mapstructure is highly configurable. See the DecoderConfig struct // for other features and options that are supported. package mapstructure import ( "encoding/json" "errors" "fmt" "reflect" "sort" "strconv" "strings" ) // DecodeHookFunc is the callback function that can be used for // data transformations. See "DecodeHook" in the DecoderConfig // struct. // // The type must be one of DecodeHookFuncType, DecodeHookFuncKind, or // DecodeHookFuncValue. // Values are a superset of Types (Values can return types), and Types are a // superset of Kinds (Types can return Kinds) and are generally a richer thing // to use, but Kinds are simpler if you only need those. // // The reason DecodeHookFunc is multi-typed is for backwards compatibility: // we started with Kinds and then realized Types were the better solution, // but have a promise to not break backwards compat so we now support // both. type DecodeHookFunc interface{} // DecodeHookFuncType is a DecodeHookFunc which has complete information about // the source and target types. type DecodeHookFuncType func(reflect.Type, reflect.Type, interface{}) (interface{}, error) // DecodeHookFuncKind is a DecodeHookFunc which knows only the Kinds of the // source and target types. type DecodeHookFuncKind func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error) // DecodeHookFuncValue is a DecodeHookFunc which has complete access to both the source and target // values. type DecodeHookFuncValue func(from reflect.Value, to reflect.Value) (interface{}, error) // DecoderConfig is the configuration that is used to create a new decoder // and allows customization of various aspects of decoding. type DecoderConfig struct { // DecodeHook, if set, will be called before any decoding and any // type conversion (if WeaklyTypedInput is on). This lets you modify // the values before they're set down onto the resulting struct. The // DecodeHook is called for every map and value in the input. This means // that if a struct has embedded fields with squash tags the decode hook // is called only once with all of the input data, not once for each // embedded struct. // // If an error is returned, the entire decode will fail with that error. DecodeHook DecodeHookFunc // If ErrorUnused is true, then it is an error for there to exist // keys in the original map that were unused in the decoding process // (extra keys). ErrorUnused bool // ZeroFields, if set to true, will zero fields before writing them. // For example, a map will be emptied before decoded values are put in // it. If this is false, a map will be merged. ZeroFields bool // If WeaklyTypedInput is true, the decoder will make the following // "weak" conversions: // // - bools to string (true = "1", false = "0") // - numbers to string (base 10) // - bools to int/uint (true = 1, false = 0) // - strings to int/uint (base implied by prefix) // - int to bool (true if value != 0) // - string to bool (accepts: 1, t, T, TRUE, true, True, 0, f, F, // FALSE, false, False. Anything else is an error) // - empty array = empty map and vice versa // - negative numbers to overflowed uint values (base 10) // - slice of maps to a merged map // - single values are converted to slices if required. Each // element is weakly decoded. For example: "4" can become []int{4} // if the target type is an int slice. // WeaklyTypedInput bool // Squash will squash embedded structs. A squash tag may also be // added to an individual struct field using a tag. For example: // // type Parent struct { // Child `mapstructure:",squash"` // } Squash bool // Metadata is the struct that will contain extra metadata about // the decoding. If this is nil, then no metadata will be tracked. Metadata *Metadata // Result is a pointer to the struct that will contain the decoded // value. Result interface{} // The tag name that mapstructure reads for field names. This // defaults to "mapstructure" TagName string // MatchName is the function used to match the map key to the struct // field name or tag. Defaults to `strings.EqualFold`. This can be used // to implement case-sensitive tag values, support snake casing, etc. MatchName func(mapKey, fieldName string) bool } // A Decoder takes a raw interface value and turns it into structured // data, keeping track of rich error information along the way in case // anything goes wrong. Unlike the basic top-level Decode method, you can // more finely control how the Decoder behaves using the DecoderConfig // structure. The top-level Decode method is just a convenience that sets // up the most basic Decoder. type Decoder struct { config *DecoderConfig } // Metadata contains information about decoding a structure that // is tedious or difficult to get otherwise. type Metadata struct { // Keys are the keys of the structure which were successfully decoded Keys []string // Unused is a slice of keys that were found in the raw value but // weren't decoded since there was no matching field in the result interface Unused []string } // Decode takes an input structure and uses reflection to translate it to // the output structure. output must be a pointer to a map or struct. func Decode(input interface{}, output interface{}) error { config := &DecoderConfig{ Metadata: nil, Result: output, } decoder, err := NewDecoder(config) if err != nil { return err } return decoder.Decode(input) } // WeakDecode is the same as Decode but is shorthand to enable // WeaklyTypedInput. See DecoderConfig for more info. func WeakDecode(input, output interface{}) error { config := &DecoderConfig{ Metadata: nil, Result: output, WeaklyTypedInput: true, } decoder, err := NewDecoder(config) if err != nil { return err } return decoder.Decode(input) } // DecodeMetadata is the same as Decode, but is shorthand to // enable metadata collection. See DecoderConfig for more info. func DecodeMetadata(input interface{}, output interface{}, metadata *Metadata) error { config := &DecoderConfig{ Metadata: metadata, Result: output, } decoder, err := NewDecoder(config) if err != nil { return err } return decoder.Decode(input) } // WeakDecodeMetadata is the same as Decode, but is shorthand to // enable both WeaklyTypedInput and metadata collection. See // DecoderConfig for more info. func WeakDecodeMetadata(input interface{}, output interface{}, metadata *Metadata) error { config := &DecoderConfig{ Metadata: metadata, Result: output, WeaklyTypedInput: true, } decoder, err := NewDecoder(config) if err != nil { return err } return decoder.Decode(input) } // NewDecoder returns a new decoder for the given configuration. Once // a decoder has been returned, the same configuration must not be used // again. func NewDecoder(config *DecoderConfig) (*Decoder, error) { val := reflect.ValueOf(config.Result) if val.Kind() != reflect.Ptr { return nil, errors.New("result must be a pointer") } val = val.Elem() if !val.CanAddr() { return nil, errors.New("result must be addressable (a pointer)") } if config.Metadata != nil { if config.Metadata.Keys == nil { config.Metadata.Keys = make([]string, 0) } if config.Metadata.Unused == nil { config.Metadata.Unused = make([]string, 0) } } if config.TagName == "" { config.TagName = "mapstructure" } if config.MatchName == nil { config.MatchName = strings.EqualFold } result := &Decoder{ config: config, } return result, nil } // Decode decodes the given raw interface to the target pointer specified // by the configuration. func (d *Decoder) Decode(input interface{}) error { return d.decode("", input, reflect.ValueOf(d.config.Result).Elem()) } // Decodes an unknown data type into a specific reflection value. func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) error { var inputVal reflect.Value if input != nil { inputVal = reflect.ValueOf(input) // We need to check here if input is a typed nil. Typed nils won't // match the "input == nil" below so we check that here. if inputVal.Kind() == reflect.Ptr && inputVal.IsNil() { input = nil } } if input == nil { // If the data is nil, then we don't set anything, unless ZeroFields is set // to true. if d.config.ZeroFields { outVal.Set(reflect.Zero(outVal.Type())) if d.config.Metadata != nil && name != "" { d.config.Metadata.Keys = append(d.config.Metadata.Keys, name) } } return nil } if !inputVal.IsValid() { // If the input value is invalid, then we just set the value // to be the zero value. outVal.Set(reflect.Zero(outVal.Type())) if d.config.Metadata != nil && name != "" { d.config.Metadata.Keys = append(d.config.Metadata.Keys, name) } return nil } if d.config.DecodeHook != nil { // We have a DecodeHook, so let's pre-process the input. var err error input, err = DecodeHookExec(d.config.DecodeHook, inputVal, outVal) if err != nil { return fmt.Errorf("error decoding '%s': %s", name, err) } } var err error outputKind := getKind(outVal) addMetaKey := true switch outputKind { case reflect.Bool: err = d.decodeBool(name, input, outVal) case reflect.Interface: err = d.decodeBasic(name, input, outVal) case reflect.String: err = d.decodeString(name, input, outVal) case reflect.Int: err = d.decodeInt(name, input, outVal) case reflect.Uint: err = d.decodeUint(name, input, outVal) case reflect.Float32: err = d.decodeFloat(name, input, outVal) case reflect.Struct: err = d.decodeStruct(name, input, outVal) case reflect.Map: err = d.decodeMap(name, input, outVal) case reflect.Ptr: addMetaKey, err = d.decodePtr(name, input, outVal) case reflect.Slice: err = d.decodeSlice(name, input, outVal) case reflect.Array: err = d.decodeArray(name, input, outVal) case reflect.Func: err = d.decodeFunc(name, input, outVal) default: // If we reached this point then we weren't able to decode it return fmt.Errorf("%s: unsupported type: %s", name, outputKind) } // If we reached here, then we successfully decoded SOMETHING, so // mark the key as used if we're tracking metainput. if addMetaKey && d.config.Metadata != nil && name != "" { d.config.Metadata.Keys = append(d.config.Metadata.Keys, name) } return err } // This decodes a basic type (bool, int, string, etc.) and sets the // value to "data" of that type. func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value) error { if val.IsValid() && val.Elem().IsValid() { elem := val.Elem() // If we can't address this element, then its not writable. Instead, // we make a copy of the value (which is a pointer and therefore // writable), decode into that, and replace the whole value. copied := false if !elem.CanAddr() { copied = true // Make *T copy := reflect.New(elem.Type()) // *T = elem copy.Elem().Set(elem) // Set elem so we decode into it elem = copy } // Decode. If we have an error then return. We also return right // away if we're not a copy because that means we decoded directly. if err := d.decode(name, data, elem); err != nil || !copied { return err } // If we're a copy, we need to set te final result val.Set(elem.Elem()) return nil } dataVal := reflect.ValueOf(data) // If the input data is a pointer, and the assigned type is the dereference // of that exact pointer, then indirect it so that we can assign it. // Example: *string to string if dataVal.Kind() == reflect.Ptr && dataVal.Type().Elem() == val.Type() { dataVal = reflect.Indirect(dataVal) } if !dataVal.IsValid() { dataVal = reflect.Zero(val.Type()) } dataValType := dataVal.Type() if !dataValType.AssignableTo(val.Type()) { return fmt.Errorf( "'%s' expected type '%s', got '%s'", name, val.Type(), dataValType) } val.Set(dataVal) return nil } func (d *Decoder) decodeString(name string, data interface{}, val reflect.Value) error { dataVal := reflect.Indirect(reflect.ValueOf(data)) dataKind := getKind(dataVal) converted := true switch { case dataKind == reflect.String: val.SetString(dataVal.String()) case dataKind == reflect.Bool && d.config.WeaklyTypedInput: if dataVal.Bool() { val.SetString("1") } else { val.SetString("0") } case dataKind == reflect.Int && d.config.WeaklyTypedInput: val.SetString(strconv.FormatInt(dataVal.Int(), 10)) case dataKind == reflect.Uint && d.config.WeaklyTypedInput: val.SetString(strconv.FormatUint(dataVal.Uint(), 10)) case dataKind == reflect.Float32 && d.config.WeaklyTypedInput: val.SetString(strconv.FormatFloat(dataVal.Float(), 'f', -1, 64)) case dataKind == reflect.Slice && d.config.WeaklyTypedInput, dataKind == reflect.Array && d.config.WeaklyTypedInput: dataType := dataVal.Type() elemKind := dataType.Elem().Kind() switch elemKind { case reflect.Uint8: var uints []uint8 if dataKind == reflect.Array { uints = make([]uint8, dataVal.Len(), dataVal.Len()) for i := range uints { uints[i] = dataVal.Index(i).Interface().(uint8) } } else { uints = dataVal.Interface().([]uint8) } val.SetString(string(uints)) default: converted = false } default: converted = false } if !converted { return fmt.Errorf( "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", name, val.Type(), dataVal.Type(), data) } return nil } func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) error { dataVal := reflect.Indirect(reflect.ValueOf(data)) dataKind := getKind(dataVal) dataType := dataVal.Type() switch { case dataKind == reflect.Int: val.SetInt(dataVal.Int()) case dataKind == reflect.Uint: val.SetInt(int64(dataVal.Uint())) case dataKind == reflect.Float32: val.SetInt(int64(dataVal.Float())) case dataKind == reflect.Bool && d.config.WeaklyTypedInput: if dataVal.Bool() { val.SetInt(1) } else { val.SetInt(0) } case dataKind == reflect.String && d.config.WeaklyTypedInput: str := dataVal.String() if str == "" { str = "0" } i, err := strconv.ParseInt(str, 0, val.Type().Bits()) if err == nil { val.SetInt(i) } else { return fmt.Errorf("cannot parse '%s' as int: %s", name, err) } case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number": jn := data.(json.Number) i, err := jn.Int64() if err != nil { return fmt.Errorf( "error decoding json.Number into %s: %s", name, err) } val.SetInt(i) default: return fmt.Errorf( "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", name, val.Type(), dataVal.Type(), data) } return nil } func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) error { dataVal := reflect.Indirect(reflect.ValueOf(data)) dataKind := getKind(dataVal) dataType := dataVal.Type() switch { case dataKind == reflect.Int: i := dataVal.Int() if i < 0 && !d.config.WeaklyTypedInput { return fmt.Errorf("cannot parse '%s', %d overflows uint", name, i) } val.SetUint(uint64(i)) case dataKind == reflect.Uint: val.SetUint(dataVal.Uint()) case dataKind == reflect.Float32: f := dataVal.Float() if f < 0 && !d.config.WeaklyTypedInput { return fmt.Errorf("cannot parse '%s', %f overflows uint", name, f) } val.SetUint(uint64(f)) case dataKind == reflect.Bool && d.config.WeaklyTypedInput: if dataVal.Bool() { val.SetUint(1) } else { val.SetUint(0) } case dataKind == reflect.String && d.config.WeaklyTypedInput: str := dataVal.String() if str == "" { str = "0" } i, err := strconv.ParseUint(str, 0, val.Type().Bits()) if err == nil { val.SetUint(i) } else { return fmt.Errorf("cannot parse '%s' as uint: %s", name, err) } case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number": jn := data.(json.Number) i, err := strconv.ParseUint(string(jn), 0, 64) if err != nil { return fmt.Errorf( "error decoding json.Number into %s: %s", name, err) } val.SetUint(i) default: return fmt.Errorf( "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", name, val.Type(), dataVal.Type(), data) } return nil } func (d *Decoder) decodeBool(name string, data interface{}, val reflect.Value) error { dataVal := reflect.Indirect(reflect.ValueOf(data)) dataKind := getKind(dataVal) switch { case dataKind == reflect.Bool: val.SetBool(dataVal.Bool()) case dataKind == reflect.Int && d.config.WeaklyTypedInput: val.SetBool(dataVal.Int() != 0) case dataKind == reflect.Uint && d.config.WeaklyTypedInput: val.SetBool(dataVal.Uint() != 0) case dataKind == reflect.Float32 && d.config.WeaklyTypedInput: val.SetBool(dataVal.Float() != 0) case dataKind == reflect.String && d.config.WeaklyTypedInput: b, err := strconv.ParseBool(dataVal.String()) if err == nil { val.SetBool(b) } else if dataVal.String() == "" { val.SetBool(false) } else { return fmt.Errorf("cannot parse '%s' as bool: %s", name, err) } default: return fmt.Errorf( "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", name, val.Type(), dataVal.Type(), data) } return nil } func (d *Decoder) decodeFloat(name string, data interface{}, val reflect.Value) error { dataVal := reflect.Indirect(reflect.ValueOf(data)) dataKind := getKind(dataVal) dataType := dataVal.Type() switch { case dataKind == reflect.Int: val.SetFloat(float64(dataVal.Int())) case dataKind == reflect.Uint: val.SetFloat(float64(dataVal.Uint())) case dataKind == reflect.Float32: val.SetFloat(dataVal.Float()) case dataKind == reflect.Bool && d.config.WeaklyTypedInput: if dataVal.Bool() { val.SetFloat(1) } else { val.SetFloat(0) } case dataKind == reflect.String && d.config.WeaklyTypedInput: str := dataVal.String() if str == "" { str = "0" } f, err := strconv.ParseFloat(str, val.Type().Bits()) if err == nil { val.SetFloat(f) } else { return fmt.Errorf("cannot parse '%s' as float: %s", name, err) } case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number": jn := data.(json.Number) i, err := jn.Float64() if err != nil { return fmt.Errorf( "error decoding json.Number into %s: %s", name, err) } val.SetFloat(i) default: return fmt.Errorf( "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", name, val.Type(), dataVal.Type(), data) } return nil } func (d *Decoder) decodeMap(name string, data interface{}, val reflect.Value) error { valType := val.Type() valKeyType := valType.Key() valElemType := valType.Elem() // By default we overwrite keys in the current map valMap := val // If the map is nil or we're purposely zeroing fields, make a new map if valMap.IsNil() || d.config.ZeroFields { // Make a new map to hold our result mapType := reflect.MapOf(valKeyType, valElemType) valMap = reflect.MakeMap(mapType) } // Check input type and based on the input type jump to the proper func dataVal := reflect.Indirect(reflect.ValueOf(data)) switch dataVal.Kind() { case reflect.Map: return d.decodeMapFromMap(name, dataVal, val, valMap) case reflect.Struct: return d.decodeMapFromStruct(name, dataVal, val, valMap) case reflect.Array, reflect.Slice: if d.config.WeaklyTypedInput { return d.decodeMapFromSlice(name, dataVal, val, valMap) } fallthrough default: return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind()) } } func (d *Decoder) decodeMapFromSlice(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error { // Special case for BC reasons (covered by tests) if dataVal.Len() == 0 { val.Set(valMap) return nil } for i := 0; i < dataVal.Len(); i++ { err := d.decode( name+"["+strconv.Itoa(i)+"]", dataVal.Index(i).Interface(), val) if err != nil { return err } } return nil } func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error { valType := val.Type() valKeyType := valType.Key() valElemType := valType.Elem() // Accumulate errors errors := make([]string, 0) // If the input data is empty, then we just match what the input data is. if dataVal.Len() == 0 { if dataVal.IsNil() { if !val.IsNil() { val.Set(dataVal) } } else { // Set to empty allocated value val.Set(valMap) } return nil } for _, k := range dataVal.MapKeys() { fieldName := name + "[" + k.String() + "]" // First decode the key into the proper type currentKey := reflect.Indirect(reflect.New(valKeyType)) if err := d.decode(fieldName, k.Interface(), currentKey); err != nil { errors = appendErrors(errors, err) continue } // Next decode the data into the proper type v := dataVal.MapIndex(k).Interface() currentVal := reflect.Indirect(reflect.New(valElemType)) if err := d.decode(fieldName, v, currentVal); err != nil { errors = appendErrors(errors, err) continue } valMap.SetMapIndex(currentKey, currentVal) } // Set the built up map to the value val.Set(valMap) // If we had errors, return those if len(errors) > 0 { return &Error{errors} } return nil } func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error { typ := dataVal.Type() for i := 0; i < typ.NumField(); i++ { // Get the StructField first since this is a cheap operation. If the // field is unexported, then ignore it. f := typ.Field(i) if f.PkgPath != "" { continue } // Next get the actual value of this field and verify it is assignable // to the map value. v := dataVal.Field(i) if !v.Type().AssignableTo(valMap.Type().Elem()) { return fmt.Errorf("cannot assign type '%s' to map value field of type '%s'", v.Type(), valMap.Type().Elem()) } tagValue := f.Tag.Get(d.config.TagName) keyName := f.Name // If Squash is set in the config, we squash the field down. squash := d.config.Squash && v.Kind() == reflect.Struct && f.Anonymous // Determine the name of the key in the map if index := strings.Index(tagValue, ","); index != -1 { if tagValue[:index] == "-" { continue } // If "omitempty" is specified in the tag, it ignores empty values. if strings.Index(tagValue[index+1:], "omitempty") != -1 && isEmptyValue(v) { continue } // If "squash" is specified in the tag, we squash the field down. squash = !squash && strings.Index(tagValue[index+1:], "squash") != -1 if squash { // When squashing, the embedded type can be a pointer to a struct. if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct { v = v.Elem() } // The final type must be a struct if v.Kind() != reflect.Struct { return fmt.Errorf("cannot squash non-struct type '%s'", v.Type()) } } keyName = tagValue[:index] } else if len(tagValue) > 0 { if tagValue == "-" { continue } keyName = tagValue } switch v.Kind() { // this is an embedded struct, so handle it differently case reflect.Struct: x := reflect.New(v.Type()) x.Elem().Set(v) vType := valMap.Type() vKeyType := vType.Key() vElemType := vType.Elem() mType := reflect.MapOf(vKeyType, vElemType) vMap := reflect.MakeMap(mType) // Creating a pointer to a map so that other methods can completely // overwrite the map if need be (looking at you decodeMapFromMap). The // indirection allows the underlying map to be settable (CanSet() == true) // where as reflect.MakeMap returns an unsettable map. addrVal := reflect.New(vMap.Type()) reflect.Indirect(addrVal).Set(vMap) err := d.decode(keyName, x.Interface(), reflect.Indirect(addrVal)) if err != nil { return err } // the underlying map may have been completely overwritten so pull // it indirectly out of the enclosing value. vMap = reflect.Indirect(addrVal) if squash { for _, k := range vMap.MapKeys() { valMap.SetMapIndex(k, vMap.MapIndex(k)) } } else { valMap.SetMapIndex(reflect.ValueOf(keyName), vMap) } default: valMap.SetMapIndex(reflect.ValueOf(keyName), v) } } if val.CanAddr() { val.Set(valMap) } return nil } func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) (bool, error) { // If the input data is nil, then we want to just set the output // pointer to be nil as well. isNil := data == nil if !isNil { switch v := reflect.Indirect(reflect.ValueOf(data)); v.Kind() { case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: isNil = v.IsNil() } } if isNil { if !val.IsNil() && val.CanSet() { nilValue := reflect.New(val.Type()).Elem() val.Set(nilValue) } return true, nil } // Create an element of the concrete (non pointer) type and decode // into that. Then set the value of the pointer to this type. valType := val.Type() valElemType := valType.Elem() if val.CanSet() { realVal := val if realVal.IsNil() || d.config.ZeroFields { realVal = reflect.New(valElemType) } if err := d.decode(name, data, reflect.Indirect(realVal)); err != nil { return false, err } val.Set(realVal) } else { if err := d.decode(name, data, reflect.Indirect(val)); err != nil { return false, err } } return false, nil } func (d *Decoder) decodeFunc(name string, data interface{}, val reflect.Value) error { // Create an element of the concrete (non pointer) type and decode // into that. Then set the value of the pointer to this type. dataVal := reflect.Indirect(reflect.ValueOf(data)) if val.Type() != dataVal.Type() { return fmt.Errorf( "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", name, val.Type(), dataVal.Type(), data) } val.Set(dataVal) return nil } func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value) error { dataVal := reflect.Indirect(reflect.ValueOf(data)) dataValKind := dataVal.Kind() valType := val.Type() valElemType := valType.Elem() sliceType := reflect.SliceOf(valElemType) // If we have a non array/slice type then we first attempt to convert. if dataValKind != reflect.Array && dataValKind != reflect.Slice { if d.config.WeaklyTypedInput { switch { // Slice and array we use the normal logic case dataValKind == reflect.Slice, dataValKind == reflect.Array: break // Empty maps turn into empty slices case dataValKind == reflect.Map: if dataVal.Len() == 0 { val.Set(reflect.MakeSlice(sliceType, 0, 0)) return nil } // Create slice of maps of other sizes return d.decodeSlice(name, []interface{}{data}, val) case dataValKind == reflect.String && valElemType.Kind() == reflect.Uint8: return d.decodeSlice(name, []byte(dataVal.String()), val) // All other types we try to convert to the slice type // and "lift" it into it. i.e. a string becomes a string slice. default: // Just re-try this function with data as a slice. return d.decodeSlice(name, []interface{}{data}, val) } } return fmt.Errorf( "'%s': source data must be an array or slice, got %s", name, dataValKind) } // If the input value is nil, then don't allocate since empty != nil if dataVal.IsNil() { return nil } valSlice := val if valSlice.IsNil() || d.config.ZeroFields { // Make a new slice to hold our result, same size as the original data. valSlice = reflect.MakeSlice(sliceType, dataVal.Len(), dataVal.Len()) } // Accumulate any errors errors := make([]string, 0) for i := 0; i < dataVal.Len(); i++ { currentData := dataVal.Index(i).Interface() for valSlice.Len() <= i { valSlice = reflect.Append(valSlice, reflect.Zero(valElemType)) } currentField := valSlice.Index(i) fieldName := name + "[" + strconv.Itoa(i) + "]" if err := d.decode(fieldName, currentData, currentField); err != nil { errors = appendErrors(errors, err) } } // Finally, set the value to the slice we built up val.Set(valSlice) // If there were errors, we return those if len(errors) > 0 { return &Error{errors} } return nil } func (d *Decoder) decodeArray(name string, data interface{}, val reflect.Value) error { dataVal := reflect.Indirect(reflect.ValueOf(data)) dataValKind := dataVal.Kind() valType := val.Type() valElemType := valType.Elem() arrayType := reflect.ArrayOf(valType.Len(), valElemType) valArray := val if valArray.Interface() == reflect.Zero(valArray.Type()).Interface() || d.config.ZeroFields { // Check input type if dataValKind != reflect.Array && dataValKind != reflect.Slice { if d.config.WeaklyTypedInput { switch { // Empty maps turn into empty arrays case dataValKind == reflect.Map: if dataVal.Len() == 0 { val.Set(reflect.Zero(arrayType)) return nil } // All other types we try to convert to the array type // and "lift" it into it. i.e. a string becomes a string array. default: // Just re-try this function with data as a slice. return d.decodeArray(name, []interface{}{data}, val) } } return fmt.Errorf( "'%s': source data must be an array or slice, got %s", name, dataValKind) } if dataVal.Len() > arrayType.Len() { return fmt.Errorf( "'%s': expected source data to have length less or equal to %d, got %d", name, arrayType.Len(), dataVal.Len()) } // Make a new array to hold our result, same size as the original data. valArray = reflect.New(arrayType).Elem() } // Accumulate any errors errors := make([]string, 0) for i := 0; i < dataVal.Len(); i++ { currentData := dataVal.Index(i).Interface() currentField := valArray.Index(i) fieldName := name + "[" + strconv.Itoa(i) + "]" if err := d.decode(fieldName, currentData, currentField); err != nil { errors = appendErrors(errors, err) } } // Finally, set the value to the array we built up val.Set(valArray) // If there were errors, we return those if len(errors) > 0 { return &Error{errors} } return nil } func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value) error { dataVal := reflect.Indirect(reflect.ValueOf(data)) // If the type of the value to write to and the data match directly, // then we just set it directly instead of recursing into the structure. if dataVal.Type() == val.Type() { val.Set(dataVal) return nil } dataValKind := dataVal.Kind() switch dataValKind { case reflect.Map: return d.decodeStructFromMap(name, dataVal, val) case reflect.Struct: // Not the most efficient way to do this but we can optimize later if // we want to. To convert from struct to struct we go to map first // as an intermediary. // Make a new map to hold our result mapType := reflect.TypeOf((map[string]interface{})(nil)) mval := reflect.MakeMap(mapType) // Creating a pointer to a map so that other methods can completely // overwrite the map if need be (looking at you decodeMapFromMap). The // indirection allows the underlying map to be settable (CanSet() == true) // where as reflect.MakeMap returns an unsettable map. addrVal := reflect.New(mval.Type()) reflect.Indirect(addrVal).Set(mval) if err := d.decodeMapFromStruct(name, dataVal, reflect.Indirect(addrVal), mval); err != nil { return err } result := d.decodeStructFromMap(name, reflect.Indirect(addrVal), val) return result default: return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind()) } } func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) error { dataValType := dataVal.Type() if kind := dataValType.Key().Kind(); kind != reflect.String && kind != reflect.Interface { return fmt.Errorf( "'%s' needs a map with string keys, has '%s' keys", name, dataValType.Key().Kind()) } dataValKeys := make(map[reflect.Value]struct{}) dataValKeysUnused := make(map[interface{}]struct{}) for _, dataValKey := range dataVal.MapKeys() { dataValKeys[dataValKey] = struct{}{} dataValKeysUnused[dataValKey.Interface()] = struct{}{} } errors := make([]string, 0) // This slice will keep track of all the structs we'll be decoding. // There can be more than one struct if there are embedded structs // that are squashed. structs := make([]reflect.Value, 1, 5) structs[0] = val // Compile the list of all the fields that we're going to be decoding // from all the structs. type field struct { field reflect.StructField val reflect.Value } // remainField is set to a valid field set with the "remain" tag if // we are keeping track of remaining values. var remainField *field fields := []field{} for len(structs) > 0 { structVal := structs[0] structs = structs[1:] structType := structVal.Type() for i := 0; i < structType.NumField(); i++ { fieldType := structType.Field(i) fieldVal := structVal.Field(i) if fieldVal.Kind() == reflect.Ptr && fieldVal.Elem().Kind() == reflect.Struct { // Handle embedded struct pointers as embedded structs. fieldVal = fieldVal.Elem() } // If "squash" is specified in the tag, we squash the field down. squash := d.config.Squash && fieldVal.Kind() == reflect.Struct && fieldType.Anonymous remain := false // We always parse the tags cause we're looking for other tags too tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",") for _, tag := range tagParts[1:] { if tag == "squash" { squash = true break } if tag == "remain" { remain = true break } } if squash { if fieldVal.Kind() != reflect.Struct { errors = appendErrors(errors, fmt.Errorf("%s: unsupported type for squash: %s", fieldType.Name, fieldVal.Kind())) } else { structs = append(structs, fieldVal) } continue } // Build our field if remain { remainField = &field{fieldType, fieldVal} } else { // Normal struct field, store it away fields = append(fields, field{fieldType, fieldVal}) } } } // for fieldType, field := range fields { for _, f := range fields { field, fieldValue := f.field, f.val fieldName := field.Name tagValue := field.Tag.Get(d.config.TagName) tagValue = strings.SplitN(tagValue, ",", 2)[0] if tagValue != "" { fieldName = tagValue } rawMapKey := reflect.ValueOf(fieldName) rawMapVal := dataVal.MapIndex(rawMapKey) if !rawMapVal.IsValid() { // Do a slower search by iterating over each key and // doing case-insensitive search. for dataValKey := range dataValKeys { mK, ok := dataValKey.Interface().(string) if !ok { // Not a string key continue } if d.config.MatchName(mK, fieldName) { rawMapKey = dataValKey rawMapVal = dataVal.MapIndex(dataValKey) break } } if !rawMapVal.IsValid() { // There was no matching key in the map for the value in // the struct. Just ignore. continue } } if !fieldValue.IsValid() { // This should never happen panic("field is not valid") } // If we can't set the field, then it is unexported or something, // and we just continue onwards. if !fieldValue.CanSet() { continue } // Delete the key we're using from the unused map so we stop tracking delete(dataValKeysUnused, rawMapKey.Interface()) // If the name is empty string, then we're at the root, and we // don't dot-join the fields. if name != "" { fieldName = name + "." + fieldName } if err := d.decode(fieldName, rawMapVal.Interface(), fieldValue); err != nil { errors = appendErrors(errors, err) } } // If we have a "remain"-tagged field and we have unused keys then // we put the unused keys directly into the remain field. if remainField != nil && len(dataValKeysUnused) > 0 { // Build a map of only the unused values remain := map[interface{}]interface{}{} for key := range dataValKeysUnused { remain[key] = dataVal.MapIndex(reflect.ValueOf(key)).Interface() } // Decode it as-if we were just decoding this map onto our map. if err := d.decodeMap(name, remain, remainField.val); err != nil { errors = appendErrors(errors, err) } // Set the map to nil so we have none so that the next check will // not error (ErrorUnused) dataValKeysUnused = nil } if d.config.ErrorUnused && len(dataValKeysUnused) > 0 { keys := make([]string, 0, len(dataValKeysUnused)) for rawKey := range dataValKeysUnused { keys = append(keys, rawKey.(string)) } sort.Strings(keys) err := fmt.Errorf("'%s' has invalid keys: %s", name, strings.Join(keys, ", ")) errors = appendErrors(errors, err) } if len(errors) > 0 { return &Error{errors} } // Add the unused keys to the list of unused keys if we're tracking metadata if d.config.Metadata != nil { for rawKey := range dataValKeysUnused { key := rawKey.(string) if name != "" { key = name + "." + key } d.config.Metadata.Unused = append(d.config.Metadata.Unused, key) } } return nil } func isEmptyValue(v reflect.Value) bool { switch getKind(v) { case reflect.Array, reflect.Map, reflect.Slice, reflect.String: return v.Len() == 0 case reflect.Bool: return !v.Bool() case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return v.Int() == 0 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return v.Uint() == 0 case reflect.Float32, reflect.Float64: return v.Float() == 0 case reflect.Interface, reflect.Ptr: return v.IsNil() } return false } func getKind(val reflect.Value) reflect.Kind { kind := val.Kind() switch { case kind >= reflect.Int && kind <= reflect.Int64: return reflect.Int case kind >= reflect.Uint && kind <= reflect.Uint64: return reflect.Uint case kind >= reflect.Float32 && kind <= reflect.Float64: return reflect.Float32 default: return kind } } ================================================ FILE: vendor/github.com/moby/patternmatcher/LICENSE ================================================ Apache License Version 2.0, January 2004 https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS Copyright 2013-2018 Docker, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/moby/patternmatcher/NOTICE ================================================ Docker Copyright 2012-2017 Docker, Inc. This product includes software developed at Docker, Inc. (https://www.docker.com). The following is courtesy of our legal counsel: Use and transfer of Docker may be subject to certain restrictions by the United States and other governments. It is your responsibility to ensure that your use and/or transfer does not violate applicable laws. For more information, please see https://www.bis.doc.gov See also https://www.apache.org/dev/crypto.html and/or seek legal counsel. ================================================ FILE: vendor/github.com/moby/patternmatcher/patternmatcher.go ================================================ package patternmatcher import ( "errors" "os" "path/filepath" "regexp" "strings" "text/scanner" "unicode/utf8" ) // escapeBytes is a bitmap used to check whether a character should be escaped when creating the regex. var escapeBytes [8]byte // shouldEscape reports whether a rune should be escaped as part of the regex. // // This only includes characters that require escaping in regex but are also NOT valid filepath pattern characters. // Additionally, '\' is not excluded because there is specific logic to properly handle this, as it's a path separator // on Windows. // // Adapted from regexp::QuoteMeta in go stdlib. // See https://cs.opensource.google/go/go/+/refs/tags/go1.17.2:src/regexp/regexp.go;l=703-715;drc=refs%2Ftags%2Fgo1.17.2 func shouldEscape(b rune) bool { return b < utf8.RuneSelf && escapeBytes[b%8]&(1<<(b/8)) != 0 } func init() { for _, b := range []byte(`.+()|{}$`) { escapeBytes[b%8] |= 1 << (b / 8) } } // PatternMatcher allows checking paths against a list of patterns type PatternMatcher struct { patterns []*Pattern exclusions bool } // New creates a new matcher object for specific patterns that can // be used later to match against patterns against paths func New(patterns []string) (*PatternMatcher, error) { pm := &PatternMatcher{ patterns: make([]*Pattern, 0, len(patterns)), } for _, p := range patterns { // Eliminate leading and trailing whitespace. p = strings.TrimSpace(p) if p == "" { continue } p = filepath.Clean(p) newp := &Pattern{} if p[0] == '!' { if len(p) == 1 { return nil, errors.New("illegal exclusion pattern: \"!\"") } newp.exclusion = true p = p[1:] pm.exclusions = true } // Do some syntax checking on the pattern. // filepath's Match() has some really weird rules that are inconsistent // so instead of trying to dup their logic, just call Match() for its // error state and if there is an error in the pattern return it. // If this becomes an issue we can remove this since its really only // needed in the error (syntax) case - which isn't really critical. if _, err := filepath.Match(p, "."); err != nil { return nil, err } newp.cleanedPattern = p newp.dirs = strings.Split(p, string(os.PathSeparator)) pm.patterns = append(pm.patterns, newp) } return pm, nil } // Matches returns true if "file" matches any of the patterns // and isn't excluded by any of the subsequent patterns. // // The "file" argument should be a slash-delimited path. // // Matches is not safe to call concurrently. // // Deprecated: This implementation is buggy (it only checks a single parent dir // against the pattern) and will be removed soon. Use either // MatchesOrParentMatches or MatchesUsingParentResults instead. func (pm *PatternMatcher) Matches(file string) (bool, error) { matched := false file = filepath.FromSlash(file) parentPath := filepath.Dir(file) parentPathDirs := strings.Split(parentPath, string(os.PathSeparator)) for _, pattern := range pm.patterns { // Skip evaluation if this is an inclusion and the filename // already matched the pattern, or it's an exclusion and it has // not matched the pattern yet. if pattern.exclusion != matched { continue } match, err := pattern.match(file) if err != nil { return false, err } if !match && parentPath != "." { // Check to see if the pattern matches one of our parent dirs. if len(pattern.dirs) <= len(parentPathDirs) { match, _ = pattern.match(strings.Join(parentPathDirs[:len(pattern.dirs)], string(os.PathSeparator))) } } if match { matched = !pattern.exclusion } } return matched, nil } // MatchesOrParentMatches returns true if "file" matches any of the patterns // and isn't excluded by any of the subsequent patterns. // // The "file" argument should be a slash-delimited path. // // Matches is not safe to call concurrently. func (pm *PatternMatcher) MatchesOrParentMatches(file string) (bool, error) { matched := false file = filepath.FromSlash(file) parentPath := filepath.Dir(file) parentPathDirs := strings.Split(parentPath, string(os.PathSeparator)) for _, pattern := range pm.patterns { // Skip evaluation if this is an inclusion and the filename // already matched the pattern, or it's an exclusion and it has // not matched the pattern yet. if pattern.exclusion != matched { continue } match, err := pattern.match(file) if err != nil { return false, err } if !match && parentPath != "." { // Check to see if the pattern matches one of our parent dirs. for i := range parentPathDirs { match, _ = pattern.match(strings.Join(parentPathDirs[:i+1], string(os.PathSeparator))) if match { break } } } if match { matched = !pattern.exclusion } } return matched, nil } // MatchesUsingParentResult returns true if "file" matches any of the patterns // and isn't excluded by any of the subsequent patterns. The functionality is // the same as Matches, but as an optimization, the caller keeps track of // whether the parent directory matched. // // The "file" argument should be a slash-delimited path. // // MatchesUsingParentResult is not safe to call concurrently. // // Deprecated: this function does behave correctly in some cases (see // https://github.com/docker/buildx/issues/850). // // Use MatchesUsingParentResults instead. func (pm *PatternMatcher) MatchesUsingParentResult(file string, parentMatched bool) (bool, error) { matched := parentMatched file = filepath.FromSlash(file) for _, pattern := range pm.patterns { // Skip evaluation if this is an inclusion and the filename // already matched the pattern, or it's an exclusion and it has // not matched the pattern yet. if pattern.exclusion != matched { continue } match, err := pattern.match(file) if err != nil { return false, err } if match { matched = !pattern.exclusion } } return matched, nil } // MatchInfo tracks information about parent dir matches while traversing a // filesystem. type MatchInfo struct { parentMatched []bool } // MatchesUsingParentResults returns true if "file" matches any of the patterns // and isn't excluded by any of the subsequent patterns. The functionality is // the same as Matches, but as an optimization, the caller passes in // intermediate results from matching the parent directory. // // The "file" argument should be a slash-delimited path. // // MatchesUsingParentResults is not safe to call concurrently. func (pm *PatternMatcher) MatchesUsingParentResults(file string, parentMatchInfo MatchInfo) (bool, MatchInfo, error) { parentMatched := parentMatchInfo.parentMatched if len(parentMatched) != 0 && len(parentMatched) != len(pm.patterns) { return false, MatchInfo{}, errors.New("wrong number of values in parentMatched") } file = filepath.FromSlash(file) matched := false matchInfo := MatchInfo{ parentMatched: make([]bool, len(pm.patterns)), } for i, pattern := range pm.patterns { match := false // If the parent matched this pattern, we don't need to recheck. if len(parentMatched) != 0 { match = parentMatched[i] } if !match { // Skip evaluation if this is an inclusion and the filename // already matched the pattern, or it's an exclusion and it has // not matched the pattern yet. if pattern.exclusion != matched { continue } var err error match, err = pattern.match(file) if err != nil { return false, matchInfo, err } // If the zero value of MatchInfo was passed in, we don't have // any information about the parent dir's match results, and we // apply the same logic as MatchesOrParentMatches. if !match && len(parentMatched) == 0 { if parentPath := filepath.Dir(file); parentPath != "." { parentPathDirs := strings.Split(parentPath, string(os.PathSeparator)) // Check to see if the pattern matches one of our parent dirs. for i := range parentPathDirs { match, _ = pattern.match(strings.Join(parentPathDirs[:i+1], string(os.PathSeparator))) if match { break } } } } } matchInfo.parentMatched[i] = match if match { matched = !pattern.exclusion } } return matched, matchInfo, nil } // Exclusions returns true if any of the patterns define exclusions func (pm *PatternMatcher) Exclusions() bool { return pm.exclusions } // Patterns returns array of active patterns func (pm *PatternMatcher) Patterns() []*Pattern { return pm.patterns } // Pattern defines a single regexp used to filter file paths. type Pattern struct { matchType matchType cleanedPattern string dirs []string regexp *regexp.Regexp exclusion bool } type matchType int const ( unknownMatch matchType = iota exactMatch prefixMatch suffixMatch regexpMatch ) func (p *Pattern) String() string { return p.cleanedPattern } // Exclusion returns true if this pattern defines exclusion func (p *Pattern) Exclusion() bool { return p.exclusion } func (p *Pattern) match(path string) (bool, error) { if p.matchType == unknownMatch { if err := p.compile(string(os.PathSeparator)); err != nil { return false, filepath.ErrBadPattern } } switch p.matchType { case exactMatch: return path == p.cleanedPattern, nil case prefixMatch: // strip trailing ** return strings.HasPrefix(path, p.cleanedPattern[:len(p.cleanedPattern)-2]), nil case suffixMatch: // strip leading ** suffix := p.cleanedPattern[2:] if strings.HasSuffix(path, suffix) { return true, nil } // **/foo matches "foo" return suffix[0] == os.PathSeparator && path == suffix[1:], nil case regexpMatch: return p.regexp.MatchString(path), nil } return false, nil } func (p *Pattern) compile(sl string) error { regStr := "^" pattern := p.cleanedPattern // Go through the pattern and convert it to a regexp. // We use a scanner so we can support utf-8 chars. var scan scanner.Scanner scan.Init(strings.NewReader(pattern)) escSL := sl if sl == `\` { escSL += `\` } p.matchType = exactMatch for i := 0; scan.Peek() != scanner.EOF; i++ { ch := scan.Next() if ch == '*' { if scan.Peek() == '*' { // is some flavor of "**" scan.Next() // Treat **/ as ** so eat the "/" if string(scan.Peek()) == sl { scan.Next() } if scan.Peek() == scanner.EOF { // is "**EOF" - to align with .gitignore just accept all if p.matchType == exactMatch { p.matchType = prefixMatch } else { regStr += ".*" p.matchType = regexpMatch } } else { // is "**" // Note that this allows for any # of /'s (even 0) because // the .* will eat everything, even /'s regStr += "(.*" + escSL + ")?" p.matchType = regexpMatch } if i == 0 { p.matchType = suffixMatch } } else { // is "*" so map it to anything but "/" regStr += "[^" + escSL + "]*" p.matchType = regexpMatch } } else if ch == '?' { // "?" is any char except "/" regStr += "[^" + escSL + "]" p.matchType = regexpMatch } else if shouldEscape(ch) { // Escape some regexp special chars that have no meaning // in golang's filepath.Match regStr += `\` + string(ch) } else if ch == '\\' { // escape next char. Note that a trailing \ in the pattern // will be left alone (but need to escape it) if sl == `\` { // On windows map "\" to "\\", meaning an escaped backslash, // and then just continue because filepath.Match on // Windows doesn't allow escaping at all regStr += escSL continue } if scan.Peek() != scanner.EOF { regStr += `\` + string(scan.Next()) p.matchType = regexpMatch } else { regStr += `\` } } else if ch == '[' || ch == ']' { regStr += string(ch) p.matchType = regexpMatch } else { regStr += string(ch) } } if p.matchType != regexpMatch { return nil } regStr += "$" re, err := regexp.Compile(regStr) if err != nil { return err } p.regexp = re p.matchType = regexpMatch return nil } // Matches returns true if file matches any of the patterns // and isn't excluded by any of the subsequent patterns. // // This implementation is buggy (it only checks a single parent dir against the // pattern) and will be removed soon. Use MatchesOrParentMatches instead. func Matches(file string, patterns []string) (bool, error) { pm, err := New(patterns) if err != nil { return false, err } file = filepath.Clean(file) if file == "." { // Don't let them exclude everything, kind of silly. return false, nil } return pm.Matches(file) } // MatchesOrParentMatches returns true if file matches any of the patterns // and isn't excluded by any of the subsequent patterns. func MatchesOrParentMatches(file string, patterns []string) (bool, error) { pm, err := New(patterns) if err != nil { return false, err } file = filepath.Clean(file) if file == "." { // Don't let them exclude everything, kind of silly. return false, nil } return pm.MatchesOrParentMatches(file) } ================================================ FILE: vendor/github.com/moby/spdystream/CONTRIBUTING.md ================================================ # Contributing to SpdyStream Want to hack on spdystream? Awesome! Here are instructions to get you started. SpdyStream is a part of the [Docker](https://docker.io) project, and follows the same rules and principles. If you're already familiar with the way Docker does things, you'll feel right at home. Otherwise, go read [Docker's contributions guidelines](https://github.com/dotcloud/docker/blob/master/CONTRIBUTING.md). Happy hacking! ================================================ FILE: vendor/github.com/moby/spdystream/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/moby/spdystream/MAINTAINERS ================================================ # Spdystream maintainers file # # This file describes who runs the moby/spdystream project and how. # This is a living document - if you see something out of date or missing, speak up! # # It is structured to be consumable by both humans and programs. # To extract its contents programmatically, use any TOML-compliant parser. # # This file is compiled into the MAINTAINERS file in docker/opensource. # [Org] [Org."Core maintainers"] people = [ "adisky", "dims", "dmcgowan", ] [people] # A reference list of all people associated with the project. # All other sections should refer to people by their canonical key # in the people section. # ADD YOURSELF HERE IN ALPHABETICAL ORDER [people.adisky] Name = "Aditi Sharma" Email = "adi.sky17@gmail.com" GitHub = "adisky" [people.dims] Name = "Davanum Srinivas" Email = "davanum@gmail.com" GitHub = "dims" [people.dmcgowan] Name = "Derek McGowan" Email = "derek@mcg.dev" GitHub = "dmcgowan" ================================================ FILE: vendor/github.com/moby/spdystream/NOTICE ================================================ SpdyStream Copyright 2014-2021 Docker Inc. This product includes software developed at Docker Inc. (https://www.docker.com/). ================================================ FILE: vendor/github.com/moby/spdystream/README.md ================================================ # SpdyStream A multiplexed stream library using spdy ## Usage Client example (connecting to mirroring server without auth) ```go package main import ( "fmt" "github.com/moby/spdystream" "net" "net/http" ) func main() { conn, err := net.Dial("tcp", "localhost:8080") if err != nil { panic(err) } spdyConn, err := spdystream.NewConnection(conn, false) if err != nil { panic(err) } go spdyConn.Serve(spdystream.NoOpStreamHandler) stream, err := spdyConn.CreateStream(http.Header{}, nil, false) if err != nil { panic(err) } stream.Wait() fmt.Fprint(stream, "Writing to stream") buf := make([]byte, 25) stream.Read(buf) fmt.Println(string(buf)) stream.Close() } ``` Server example (mirroring server without auth) ```go package main import ( "github.com/moby/spdystream" "net" ) func main() { listener, err := net.Listen("tcp", "localhost:8080") if err != nil { panic(err) } for { conn, err := listener.Accept() if err != nil { panic(err) } spdyConn, err := spdystream.NewConnection(conn, true) if err != nil { panic(err) } go spdyConn.Serve(spdystream.MirrorStreamHandler) } } ``` ## Copyright and license Copyright 2013-2021 Docker, inc. Released under the [Apache 2.0 license](LICENSE). ================================================ FILE: vendor/github.com/moby/spdystream/connection.go ================================================ /* Copyright 2014-2021 Docker Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package spdystream import ( "errors" "fmt" "io" "net" "net/http" "sync" "time" "github.com/moby/spdystream/spdy" ) var ( ErrInvalidStreamId = errors.New("Invalid stream id") ErrTimeout = errors.New("Timeout occurred") ErrReset = errors.New("Stream reset") ErrWriteClosedStream = errors.New("Write on closed stream") ) const ( FRAME_WORKERS = 5 QUEUE_SIZE = 50 ) type StreamHandler func(stream *Stream) type AuthHandler func(header http.Header, slot uint8, parent uint32) bool type idleAwareFramer struct { f *spdy.Framer conn *Connection writeLock sync.Mutex resetChan chan struct{} setTimeoutLock sync.Mutex setTimeoutChan chan time.Duration timeout time.Duration } func newIdleAwareFramer(framer *spdy.Framer) *idleAwareFramer { iaf := &idleAwareFramer{ f: framer, resetChan: make(chan struct{}, 2), // setTimeoutChan needs to be buffered to avoid deadlocks when calling setIdleTimeout at about // the same time the connection is being closed setTimeoutChan: make(chan time.Duration, 1), } return iaf } func (i *idleAwareFramer) monitor() { var ( timer *time.Timer expired <-chan time.Time resetChan = i.resetChan setTimeoutChan = i.setTimeoutChan ) Loop: for { select { case timeout := <-i.setTimeoutChan: i.timeout = timeout if timeout == 0 { if timer != nil { timer.Stop() } } else { if timer == nil { timer = time.NewTimer(timeout) expired = timer.C } else { timer.Reset(timeout) } } case <-resetChan: if timer != nil && i.timeout > 0 { timer.Reset(i.timeout) } case <-expired: i.conn.streamCond.L.Lock() streams := i.conn.streams i.conn.streams = make(map[spdy.StreamId]*Stream) i.conn.streamCond.Broadcast() i.conn.streamCond.L.Unlock() go func() { for _, stream := range streams { stream.resetStream() } i.conn.Close() }() case <-i.conn.closeChan: if timer != nil { timer.Stop() } // Start a goroutine to drain resetChan. This is needed because we've seen // some unit tests with large numbers of goroutines get into a situation // where resetChan fills up, at least 1 call to Write() is still trying to // send to resetChan, the connection gets closed, and this case statement // attempts to grab the write lock that Write() already has, causing a // deadlock. // // See https://github.com/moby/spdystream/issues/49 for more details. go func() { for range resetChan { } }() go func() { for range setTimeoutChan { } }() i.writeLock.Lock() close(resetChan) i.resetChan = nil i.writeLock.Unlock() i.setTimeoutLock.Lock() close(i.setTimeoutChan) i.setTimeoutChan = nil i.setTimeoutLock.Unlock() break Loop } } // Drain resetChan for range resetChan { } } func (i *idleAwareFramer) WriteFrame(frame spdy.Frame) error { i.writeLock.Lock() defer i.writeLock.Unlock() if i.resetChan == nil { return io.EOF } err := i.f.WriteFrame(frame) if err != nil { return err } i.resetChan <- struct{}{} return nil } func (i *idleAwareFramer) ReadFrame() (spdy.Frame, error) { frame, err := i.f.ReadFrame() if err != nil { return nil, err } // resetChan should never be closed since it is only closed // when the connection has closed its closeChan. This closure // only occurs after all Reads have finished // TODO (dmcgowan): refactor relationship into connection i.resetChan <- struct{}{} return frame, nil } func (i *idleAwareFramer) setIdleTimeout(timeout time.Duration) { i.setTimeoutLock.Lock() defer i.setTimeoutLock.Unlock() if i.setTimeoutChan == nil { return } i.setTimeoutChan <- timeout } type Connection struct { conn net.Conn framer *idleAwareFramer closeChan chan bool goneAway bool lastStreamChan chan<- *Stream goAwayTimeout time.Duration closeTimeout time.Duration streamLock *sync.RWMutex streamCond *sync.Cond streams map[spdy.StreamId]*Stream nextIdLock sync.Mutex receiveIdLock sync.Mutex nextStreamId spdy.StreamId receivedStreamId spdy.StreamId pingIdLock sync.Mutex pingId uint32 pingChans map[uint32]chan error shutdownLock sync.Mutex shutdownChan chan error hasShutdown bool // for testing https://github.com/moby/spdystream/pull/56 dataFrameHandler func(*spdy.DataFrame) error } // NewConnection creates a new spdy connection from an existing // network connection. func NewConnection(conn net.Conn, server bool) (*Connection, error) { framer, framerErr := spdy.NewFramer(conn, conn) if framerErr != nil { return nil, framerErr } idleAwareFramer := newIdleAwareFramer(framer) var sid spdy.StreamId var rid spdy.StreamId var pid uint32 if server { sid = 2 rid = 1 pid = 2 } else { sid = 1 rid = 2 pid = 1 } streamLock := new(sync.RWMutex) streamCond := sync.NewCond(streamLock) session := &Connection{ conn: conn, framer: idleAwareFramer, closeChan: make(chan bool), goAwayTimeout: time.Duration(0), closeTimeout: time.Duration(0), streamLock: streamLock, streamCond: streamCond, streams: make(map[spdy.StreamId]*Stream), nextStreamId: sid, receivedStreamId: rid, pingId: pid, pingChans: make(map[uint32]chan error), shutdownChan: make(chan error), } session.dataFrameHandler = session.handleDataFrame idleAwareFramer.conn = session go idleAwareFramer.monitor() return session, nil } // Ping sends a ping frame across the connection and // returns the response time func (s *Connection) Ping() (time.Duration, error) { pid := s.pingId s.pingIdLock.Lock() if s.pingId > 0x7ffffffe { s.pingId = s.pingId - 0x7ffffffe } else { s.pingId = s.pingId + 2 } s.pingIdLock.Unlock() pingChan := make(chan error) s.pingChans[pid] = pingChan defer delete(s.pingChans, pid) frame := &spdy.PingFrame{Id: pid} startTime := time.Now() writeErr := s.framer.WriteFrame(frame) if writeErr != nil { return time.Duration(0), writeErr } select { case <-s.closeChan: return time.Duration(0), errors.New("connection closed") case err, ok := <-pingChan: if ok && err != nil { return time.Duration(0), err } break } return time.Since(startTime), nil } // Serve handles frames sent from the server, including reply frames // which are needed to fully initiate connections. Both clients and servers // should call Serve in a separate goroutine before creating streams. func (s *Connection) Serve(newHandler StreamHandler) { // use a WaitGroup to wait for all frames to be drained after receiving // go-away. var wg sync.WaitGroup // Parition queues to ensure stream frames are handled // by the same worker, ensuring order is maintained frameQueues := make([]*PriorityFrameQueue, FRAME_WORKERS) for i := 0; i < FRAME_WORKERS; i++ { frameQueues[i] = NewPriorityFrameQueue(QUEUE_SIZE) // Ensure frame queue is drained when connection is closed go func(frameQueue *PriorityFrameQueue) { <-s.closeChan frameQueue.Drain() }(frameQueues[i]) wg.Add(1) go func(frameQueue *PriorityFrameQueue) { // let the WaitGroup know this worker is done defer wg.Done() s.frameHandler(frameQueue, newHandler) }(frameQueues[i]) } var ( partitionRoundRobin int goAwayFrame *spdy.GoAwayFrame ) Loop: for { readFrame, err := s.framer.ReadFrame() if err != nil { if err != io.EOF { debugMessage("frame read error: %s", err) } else { debugMessage("(%p) EOF received", s) } break } var priority uint8 var partition int switch frame := readFrame.(type) { case *spdy.SynStreamFrame: if s.checkStreamFrame(frame) { priority = frame.Priority partition = int(frame.StreamId % FRAME_WORKERS) debugMessage("(%p) Add stream frame: %d ", s, frame.StreamId) s.addStreamFrame(frame) } else { debugMessage("(%p) Rejected stream frame: %d ", s, frame.StreamId) continue } case *spdy.SynReplyFrame: priority = s.getStreamPriority(frame.StreamId) partition = int(frame.StreamId % FRAME_WORKERS) case *spdy.DataFrame: priority = s.getStreamPriority(frame.StreamId) partition = int(frame.StreamId % FRAME_WORKERS) case *spdy.RstStreamFrame: priority = s.getStreamPriority(frame.StreamId) partition = int(frame.StreamId % FRAME_WORKERS) case *spdy.HeadersFrame: priority = s.getStreamPriority(frame.StreamId) partition = int(frame.StreamId % FRAME_WORKERS) case *spdy.PingFrame: priority = 0 partition = partitionRoundRobin partitionRoundRobin = (partitionRoundRobin + 1) % FRAME_WORKERS case *spdy.GoAwayFrame: // hold on to the go away frame and exit the loop goAwayFrame = frame break Loop default: priority = 7 partition = partitionRoundRobin partitionRoundRobin = (partitionRoundRobin + 1) % FRAME_WORKERS } frameQueues[partition].Push(readFrame, priority) } close(s.closeChan) // wait for all frame handler workers to indicate they've drained their queues // before handling the go away frame wg.Wait() if goAwayFrame != nil { s.handleGoAwayFrame(goAwayFrame) } // now it's safe to close remote channels and empty s.streams s.streamCond.L.Lock() // notify streams that they're now closed, which will // unblock any stream Read() calls for _, stream := range s.streams { stream.closeRemoteChannels() } s.streams = make(map[spdy.StreamId]*Stream) s.streamCond.Broadcast() s.streamCond.L.Unlock() } func (s *Connection) frameHandler(frameQueue *PriorityFrameQueue, newHandler StreamHandler) { for { popFrame := frameQueue.Pop() if popFrame == nil { return } var frameErr error switch frame := popFrame.(type) { case *spdy.SynStreamFrame: frameErr = s.handleStreamFrame(frame, newHandler) case *spdy.SynReplyFrame: frameErr = s.handleReplyFrame(frame) case *spdy.DataFrame: frameErr = s.dataFrameHandler(frame) case *spdy.RstStreamFrame: frameErr = s.handleResetFrame(frame) case *spdy.HeadersFrame: frameErr = s.handleHeaderFrame(frame) case *spdy.PingFrame: frameErr = s.handlePingFrame(frame) case *spdy.GoAwayFrame: frameErr = s.handleGoAwayFrame(frame) default: frameErr = fmt.Errorf("unhandled frame type: %T", frame) } if frameErr != nil { debugMessage("frame handling error: %s", frameErr) } } } func (s *Connection) getStreamPriority(streamId spdy.StreamId) uint8 { stream, streamOk := s.getStream(streamId) if !streamOk { return 7 } return stream.priority } func (s *Connection) addStreamFrame(frame *spdy.SynStreamFrame) { var parent *Stream if frame.AssociatedToStreamId != spdy.StreamId(0) { parent, _ = s.getStream(frame.AssociatedToStreamId) } stream := &Stream{ streamId: frame.StreamId, parent: parent, conn: s, startChan: make(chan error), headers: frame.Headers, finished: (frame.CFHeader.Flags & spdy.ControlFlagUnidirectional) != 0x00, replyCond: sync.NewCond(new(sync.Mutex)), dataChan: make(chan []byte), headerChan: make(chan http.Header), closeChan: make(chan bool), priority: frame.Priority, } if frame.CFHeader.Flags&spdy.ControlFlagFin != 0x00 { stream.closeRemoteChannels() } s.addStream(stream) } // checkStreamFrame checks to see if a stream frame is allowed. // If the stream is invalid, then a reset frame with protocol error // will be returned. func (s *Connection) checkStreamFrame(frame *spdy.SynStreamFrame) bool { s.receiveIdLock.Lock() defer s.receiveIdLock.Unlock() if s.goneAway { return false } validationErr := s.validateStreamId(frame.StreamId) if validationErr != nil { go func() { resetErr := s.sendResetFrame(spdy.ProtocolError, frame.StreamId) if resetErr != nil { debugMessage("reset error: %s", resetErr) } }() return false } return true } func (s *Connection) handleStreamFrame(frame *spdy.SynStreamFrame, newHandler StreamHandler) error { stream, ok := s.getStream(frame.StreamId) if !ok { return fmt.Errorf("Missing stream: %d", frame.StreamId) } newHandler(stream) return nil } func (s *Connection) handleReplyFrame(frame *spdy.SynReplyFrame) error { debugMessage("(%p) Reply frame received for %d", s, frame.StreamId) stream, streamOk := s.getStream(frame.StreamId) if !streamOk { debugMessage("Reply frame gone away for %d", frame.StreamId) // Stream has already gone away return nil } if stream.replied { // Stream has already received reply return nil } stream.replied = true // TODO Check for error if (frame.CFHeader.Flags & spdy.ControlFlagFin) != 0x00 { s.remoteStreamFinish(stream) } close(stream.startChan) return nil } func (s *Connection) handleResetFrame(frame *spdy.RstStreamFrame) error { stream, streamOk := s.getStream(frame.StreamId) if !streamOk { // Stream has already been removed return nil } s.removeStream(stream) stream.closeRemoteChannels() if !stream.replied { stream.replied = true stream.startChan <- ErrReset close(stream.startChan) } stream.finishLock.Lock() stream.finished = true stream.finishLock.Unlock() return nil } func (s *Connection) handleHeaderFrame(frame *spdy.HeadersFrame) error { stream, streamOk := s.getStream(frame.StreamId) if !streamOk { // Stream has already gone away return nil } if !stream.replied { // No reply received...Protocol error? return nil } // TODO limit headers while not blocking (use buffered chan or goroutine?) select { case <-stream.closeChan: return nil case stream.headerChan <- frame.Headers: } if (frame.CFHeader.Flags & spdy.ControlFlagFin) != 0x00 { s.remoteStreamFinish(stream) } return nil } func (s *Connection) handleDataFrame(frame *spdy.DataFrame) error { debugMessage("(%p) Data frame received for %d", s, frame.StreamId) stream, streamOk := s.getStream(frame.StreamId) if !streamOk { debugMessage("(%p) Data frame gone away for %d", s, frame.StreamId) // Stream has already gone away return nil } if !stream.replied { debugMessage("(%p) Data frame not replied %d", s, frame.StreamId) // No reply received...Protocol error? return nil } debugMessage("(%p) (%d) Data frame handling", stream, stream.streamId) if len(frame.Data) > 0 { stream.dataLock.RLock() select { case <-stream.closeChan: debugMessage("(%p) (%d) Data frame not sent (stream shut down)", stream, stream.streamId) case stream.dataChan <- frame.Data: debugMessage("(%p) (%d) Data frame sent", stream, stream.streamId) } stream.dataLock.RUnlock() } if (frame.Flags & spdy.DataFlagFin) != 0x00 { s.remoteStreamFinish(stream) } return nil } func (s *Connection) handlePingFrame(frame *spdy.PingFrame) error { if s.pingId&0x01 != frame.Id&0x01 { return s.framer.WriteFrame(frame) } pingChan, pingOk := s.pingChans[frame.Id] if pingOk { close(pingChan) } return nil } func (s *Connection) handleGoAwayFrame(frame *spdy.GoAwayFrame) error { debugMessage("(%p) Go away received", s) s.receiveIdLock.Lock() if s.goneAway { s.receiveIdLock.Unlock() return nil } s.goneAway = true s.receiveIdLock.Unlock() if s.lastStreamChan != nil { stream, _ := s.getStream(frame.LastGoodStreamId) go func() { s.lastStreamChan <- stream }() } // Do not block frame handler waiting for closure go s.shutdown(s.goAwayTimeout) return nil } func (s *Connection) remoteStreamFinish(stream *Stream) { stream.closeRemoteChannels() stream.finishLock.Lock() if stream.finished { // Stream is fully closed, cleanup s.removeStream(stream) } stream.finishLock.Unlock() } // CreateStream creates a new spdy stream using the parameters for // creating the stream frame. The stream frame will be sent upon // calling this function, however this function does not wait for // the reply frame. If waiting for the reply is desired, use // the stream Wait or WaitTimeout function on the stream returned // by this function. func (s *Connection) CreateStream(headers http.Header, parent *Stream, fin bool) (*Stream, error) { // MUST synchronize stream creation (all the way to writing the frame) // as stream IDs **MUST** increase monotonically. s.nextIdLock.Lock() defer s.nextIdLock.Unlock() streamId := s.getNextStreamId() if streamId == 0 { return nil, fmt.Errorf("Unable to get new stream id") } stream := &Stream{ streamId: streamId, parent: parent, conn: s, startChan: make(chan error), headers: headers, dataChan: make(chan []byte), headerChan: make(chan http.Header), closeChan: make(chan bool), } debugMessage("(%p) (%p) Create stream", s, stream) s.addStream(stream) return stream, s.sendStream(stream, fin) } func (s *Connection) shutdown(closeTimeout time.Duration) { // TODO Ensure this isn't called multiple times s.shutdownLock.Lock() if s.hasShutdown { s.shutdownLock.Unlock() return } s.hasShutdown = true s.shutdownLock.Unlock() var timeout <-chan time.Time if closeTimeout > time.Duration(0) { timeout = time.After(closeTimeout) } streamsClosed := make(chan bool) go func() { s.streamCond.L.Lock() for len(s.streams) > 0 { debugMessage("Streams opened: %d, %#v", len(s.streams), s.streams) s.streamCond.Wait() } s.streamCond.L.Unlock() close(streamsClosed) }() var err error select { case <-streamsClosed: // No active streams, close should be safe err = s.conn.Close() case <-timeout: // Force ungraceful close err = s.conn.Close() // Wait for cleanup to clear active streams <-streamsClosed } if err != nil { duration := 10 * time.Minute time.AfterFunc(duration, func() { select { case err, ok := <-s.shutdownChan: if ok { debugMessage("Unhandled close error after %s: %s", duration, err) } default: } }) s.shutdownChan <- err } close(s.shutdownChan) } // Closes spdy connection by sending GoAway frame and initiating shutdown func (s *Connection) Close() error { s.receiveIdLock.Lock() if s.goneAway { s.receiveIdLock.Unlock() return nil } s.goneAway = true s.receiveIdLock.Unlock() var lastStreamId spdy.StreamId if s.receivedStreamId > 2 { lastStreamId = s.receivedStreamId - 2 } goAwayFrame := &spdy.GoAwayFrame{ LastGoodStreamId: lastStreamId, Status: spdy.GoAwayOK, } err := s.framer.WriteFrame(goAwayFrame) go s.shutdown(s.closeTimeout) if err != nil { return err } return nil } // CloseWait closes the connection and waits for shutdown // to finish. Note the underlying network Connection // is not closed until the end of shutdown. func (s *Connection) CloseWait() error { closeErr := s.Close() if closeErr != nil { return closeErr } shutdownErr, ok := <-s.shutdownChan if ok { return shutdownErr } return nil } // Wait waits for the connection to finish shutdown or for // the wait timeout duration to expire. This needs to be // called either after Close has been called or the GOAWAYFRAME // has been received. If the wait timeout is 0, this function // will block until shutdown finishes. If wait is never called // and a shutdown error occurs, that error will be logged as an // unhandled error. func (s *Connection) Wait(waitTimeout time.Duration) error { var timeout <-chan time.Time if waitTimeout > time.Duration(0) { timeout = time.After(waitTimeout) } select { case err, ok := <-s.shutdownChan: if ok { return err } case <-timeout: return ErrTimeout } return nil } // NotifyClose registers a channel to be called when the remote // peer inidicates connection closure. The last stream to be // received by the remote will be sent on the channel. The notify // timeout will determine the duration between go away received // and the connection being closed. func (s *Connection) NotifyClose(c chan<- *Stream, timeout time.Duration) { s.goAwayTimeout = timeout s.lastStreamChan = c } // SetCloseTimeout sets the amount of time close will wait for // streams to finish before terminating the underlying network // connection. Setting the timeout to 0 will cause close to // wait forever, which is the default. func (s *Connection) SetCloseTimeout(timeout time.Duration) { s.closeTimeout = timeout } // SetIdleTimeout sets the amount of time the connection may sit idle before // it is forcefully terminated. func (s *Connection) SetIdleTimeout(timeout time.Duration) { s.framer.setIdleTimeout(timeout) } func (s *Connection) sendHeaders(headers http.Header, stream *Stream, fin bool) error { var flags spdy.ControlFlags if fin { flags = spdy.ControlFlagFin } headerFrame := &spdy.HeadersFrame{ StreamId: stream.streamId, Headers: headers, CFHeader: spdy.ControlFrameHeader{Flags: flags}, } return s.framer.WriteFrame(headerFrame) } func (s *Connection) sendReply(headers http.Header, stream *Stream, fin bool) error { var flags spdy.ControlFlags if fin { flags = spdy.ControlFlagFin } replyFrame := &spdy.SynReplyFrame{ StreamId: stream.streamId, Headers: headers, CFHeader: spdy.ControlFrameHeader{Flags: flags}, } return s.framer.WriteFrame(replyFrame) } func (s *Connection) sendResetFrame(status spdy.RstStreamStatus, streamId spdy.StreamId) error { resetFrame := &spdy.RstStreamFrame{ StreamId: streamId, Status: status, } return s.framer.WriteFrame(resetFrame) } func (s *Connection) sendReset(status spdy.RstStreamStatus, stream *Stream) error { return s.sendResetFrame(status, stream.streamId) } func (s *Connection) sendStream(stream *Stream, fin bool) error { var flags spdy.ControlFlags if fin { flags = spdy.ControlFlagFin stream.finished = true } var parentId spdy.StreamId if stream.parent != nil { parentId = stream.parent.streamId } streamFrame := &spdy.SynStreamFrame{ StreamId: spdy.StreamId(stream.streamId), AssociatedToStreamId: spdy.StreamId(parentId), Headers: stream.headers, CFHeader: spdy.ControlFrameHeader{Flags: flags}, } return s.framer.WriteFrame(streamFrame) } // getNextStreamId returns the next sequential id // every call should produce a unique value or an error func (s *Connection) getNextStreamId() spdy.StreamId { sid := s.nextStreamId if sid > 0x7fffffff { return 0 } s.nextStreamId = s.nextStreamId + 2 return sid } // PeekNextStreamId returns the next sequential id and keeps the next id untouched func (s *Connection) PeekNextStreamId() spdy.StreamId { sid := s.nextStreamId return sid } func (s *Connection) validateStreamId(rid spdy.StreamId) error { if rid > 0x7fffffff || rid < s.receivedStreamId { return ErrInvalidStreamId } s.receivedStreamId = rid + 2 return nil } func (s *Connection) addStream(stream *Stream) { s.streamCond.L.Lock() s.streams[stream.streamId] = stream debugMessage("(%p) (%p) Stream added, broadcasting: %d", s, stream, stream.streamId) s.streamCond.Broadcast() s.streamCond.L.Unlock() } func (s *Connection) removeStream(stream *Stream) { s.streamCond.L.Lock() delete(s.streams, stream.streamId) debugMessage("(%p) (%p) Stream removed, broadcasting: %d", s, stream, stream.streamId) s.streamCond.Broadcast() s.streamCond.L.Unlock() } func (s *Connection) getStream(streamId spdy.StreamId) (stream *Stream, ok bool) { s.streamLock.RLock() stream, ok = s.streams[streamId] s.streamLock.RUnlock() return } // FindStream looks up the given stream id and either waits for the // stream to be found or returns nil if the stream id is no longer // valid. func (s *Connection) FindStream(streamId uint32) *Stream { var stream *Stream var ok bool s.streamCond.L.Lock() stream, ok = s.streams[spdy.StreamId(streamId)] debugMessage("(%p) Found stream %d? %t", s, spdy.StreamId(streamId), ok) for !ok && streamId >= uint32(s.receivedStreamId) { s.streamCond.Wait() stream, ok = s.streams[spdy.StreamId(streamId)] } s.streamCond.L.Unlock() return stream } func (s *Connection) CloseChan() <-chan bool { return s.closeChan } ================================================ FILE: vendor/github.com/moby/spdystream/handlers.go ================================================ /* Copyright 2014-2021 Docker Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package spdystream import ( "io" "net/http" ) // MirrorStreamHandler mirrors all streams. func MirrorStreamHandler(stream *Stream) { replyErr := stream.SendReply(http.Header{}, false) if replyErr != nil { return } go func() { io.Copy(stream, stream) stream.Close() }() go func() { for { header, receiveErr := stream.ReceiveHeader() if receiveErr != nil { return } sendErr := stream.SendHeader(header, false) if sendErr != nil { return } } }() } // NoopStreamHandler does nothing when stream connects. func NoOpStreamHandler(stream *Stream) { stream.SendReply(http.Header{}, false) } ================================================ FILE: vendor/github.com/moby/spdystream/priority.go ================================================ /* Copyright 2014-2021 Docker Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package spdystream import ( "container/heap" "sync" "github.com/moby/spdystream/spdy" ) type prioritizedFrame struct { frame spdy.Frame priority uint8 insertId uint64 } type frameQueue []*prioritizedFrame func (fq frameQueue) Len() int { return len(fq) } func (fq frameQueue) Less(i, j int) bool { if fq[i].priority == fq[j].priority { return fq[i].insertId < fq[j].insertId } return fq[i].priority < fq[j].priority } func (fq frameQueue) Swap(i, j int) { fq[i], fq[j] = fq[j], fq[i] } func (fq *frameQueue) Push(x interface{}) { *fq = append(*fq, x.(*prioritizedFrame)) } func (fq *frameQueue) Pop() interface{} { old := *fq n := len(old) *fq = old[0 : n-1] return old[n-1] } type PriorityFrameQueue struct { queue *frameQueue c *sync.Cond size int nextInsertId uint64 drain bool } func NewPriorityFrameQueue(size int) *PriorityFrameQueue { queue := make(frameQueue, 0, size) heap.Init(&queue) return &PriorityFrameQueue{ queue: &queue, size: size, c: sync.NewCond(&sync.Mutex{}), } } func (q *PriorityFrameQueue) Push(frame spdy.Frame, priority uint8) { q.c.L.Lock() defer q.c.L.Unlock() for q.queue.Len() >= q.size { q.c.Wait() } pFrame := &prioritizedFrame{ frame: frame, priority: priority, insertId: q.nextInsertId, } q.nextInsertId = q.nextInsertId + 1 heap.Push(q.queue, pFrame) q.c.Signal() } func (q *PriorityFrameQueue) Pop() spdy.Frame { q.c.L.Lock() defer q.c.L.Unlock() for q.queue.Len() == 0 { if q.drain { return nil } q.c.Wait() } frame := heap.Pop(q.queue).(*prioritizedFrame).frame q.c.Signal() return frame } func (q *PriorityFrameQueue) Drain() { q.c.L.Lock() defer q.c.L.Unlock() q.drain = true q.c.Broadcast() } ================================================ FILE: vendor/github.com/moby/spdystream/spdy/dictionary.go ================================================ /* Copyright 2014-2021 Docker Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package spdy // headerDictionary is the dictionary sent to the zlib compressor/decompressor. var headerDictionary = []byte{ 0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, 0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, 0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, 0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, 0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, 0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, 0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, 0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, 0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, 0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, 0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, 0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, 0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, 0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, 0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, 0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, 0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, 0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, 0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, 0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, 0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, 0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, 0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, 0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, 0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, 0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, 0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, 0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, 0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, 0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, 0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, 0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, 0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, 0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, 0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, 0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, 0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, 0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, 0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, 0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, 0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, 0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, 0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, 0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, 0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, 0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, 0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, 0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, 0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, 0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, 0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, 0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, 0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, 0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, 0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, 0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, 0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, 0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, 0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, 0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, 0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, 0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, 0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e, } ================================================ FILE: vendor/github.com/moby/spdystream/spdy/read.go ================================================ /* Copyright 2014-2021 Docker Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package spdy import ( "compress/zlib" "encoding/binary" "io" "net/http" "strings" ) func (frame *SynStreamFrame) read(h ControlFrameHeader, f *Framer) error { return f.readSynStreamFrame(h, frame) } func (frame *SynReplyFrame) read(h ControlFrameHeader, f *Framer) error { return f.readSynReplyFrame(h, frame) } func (frame *RstStreamFrame) read(h ControlFrameHeader, f *Framer) error { frame.CFHeader = h if err := binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { return err } if err := binary.Read(f.r, binary.BigEndian, &frame.Status); err != nil { return err } if frame.Status == 0 { return &Error{InvalidControlFrame, frame.StreamId} } if frame.StreamId == 0 { return &Error{ZeroStreamId, 0} } return nil } func (frame *SettingsFrame) read(h ControlFrameHeader, f *Framer) error { frame.CFHeader = h var numSettings uint32 if err := binary.Read(f.r, binary.BigEndian, &numSettings); err != nil { return err } frame.FlagIdValues = make([]SettingsFlagIdValue, numSettings) for i := uint32(0); i < numSettings; i++ { if err := binary.Read(f.r, binary.BigEndian, &frame.FlagIdValues[i].Id); err != nil { return err } frame.FlagIdValues[i].Flag = SettingsFlag((frame.FlagIdValues[i].Id & 0xff000000) >> 24) frame.FlagIdValues[i].Id &= 0xffffff if err := binary.Read(f.r, binary.BigEndian, &frame.FlagIdValues[i].Value); err != nil { return err } } return nil } func (frame *PingFrame) read(h ControlFrameHeader, f *Framer) error { frame.CFHeader = h if err := binary.Read(f.r, binary.BigEndian, &frame.Id); err != nil { return err } if frame.Id == 0 { return &Error{ZeroStreamId, 0} } if frame.CFHeader.Flags != 0 { return &Error{InvalidControlFrame, StreamId(frame.Id)} } return nil } func (frame *GoAwayFrame) read(h ControlFrameHeader, f *Framer) error { frame.CFHeader = h if err := binary.Read(f.r, binary.BigEndian, &frame.LastGoodStreamId); err != nil { return err } if frame.CFHeader.Flags != 0 { return &Error{InvalidControlFrame, frame.LastGoodStreamId} } if frame.CFHeader.length != 8 { return &Error{InvalidControlFrame, frame.LastGoodStreamId} } if err := binary.Read(f.r, binary.BigEndian, &frame.Status); err != nil { return err } return nil } func (frame *HeadersFrame) read(h ControlFrameHeader, f *Framer) error { return f.readHeadersFrame(h, frame) } func (frame *WindowUpdateFrame) read(h ControlFrameHeader, f *Framer) error { frame.CFHeader = h if err := binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { return err } if frame.CFHeader.Flags != 0 { return &Error{InvalidControlFrame, frame.StreamId} } if frame.CFHeader.length != 8 { return &Error{InvalidControlFrame, frame.StreamId} } if err := binary.Read(f.r, binary.BigEndian, &frame.DeltaWindowSize); err != nil { return err } return nil } func newControlFrame(frameType ControlFrameType) (controlFrame, error) { ctor, ok := cframeCtor[frameType] if !ok { return nil, &Error{Err: InvalidControlFrame} } return ctor(), nil } var cframeCtor = map[ControlFrameType]func() controlFrame{ TypeSynStream: func() controlFrame { return new(SynStreamFrame) }, TypeSynReply: func() controlFrame { return new(SynReplyFrame) }, TypeRstStream: func() controlFrame { return new(RstStreamFrame) }, TypeSettings: func() controlFrame { return new(SettingsFrame) }, TypePing: func() controlFrame { return new(PingFrame) }, TypeGoAway: func() controlFrame { return new(GoAwayFrame) }, TypeHeaders: func() controlFrame { return new(HeadersFrame) }, TypeWindowUpdate: func() controlFrame { return new(WindowUpdateFrame) }, } func (f *Framer) uncorkHeaderDecompressor(payloadSize int64) error { if f.headerDecompressor != nil { f.headerReader.N = payloadSize return nil } f.headerReader = io.LimitedReader{R: f.r, N: payloadSize} decompressor, err := zlib.NewReaderDict(&f.headerReader, []byte(headerDictionary)) if err != nil { return err } f.headerDecompressor = decompressor return nil } // ReadFrame reads SPDY encoded data and returns a decompressed Frame. func (f *Framer) ReadFrame() (Frame, error) { var firstWord uint32 if err := binary.Read(f.r, binary.BigEndian, &firstWord); err != nil { return nil, err } if firstWord&0x80000000 != 0 { frameType := ControlFrameType(firstWord & 0xffff) version := uint16(firstWord >> 16 & 0x7fff) return f.parseControlFrame(version, frameType) } return f.parseDataFrame(StreamId(firstWord & 0x7fffffff)) } func (f *Framer) parseControlFrame(version uint16, frameType ControlFrameType) (Frame, error) { var length uint32 if err := binary.Read(f.r, binary.BigEndian, &length); err != nil { return nil, err } flags := ControlFlags((length & 0xff000000) >> 24) length &= 0xffffff header := ControlFrameHeader{version, frameType, flags, length} cframe, err := newControlFrame(frameType) if err != nil { return nil, err } if err = cframe.read(header, f); err != nil { return nil, err } return cframe, nil } func parseHeaderValueBlock(r io.Reader, streamId StreamId) (http.Header, error) { var numHeaders uint32 if err := binary.Read(r, binary.BigEndian, &numHeaders); err != nil { return nil, err } var e error h := make(http.Header, int(numHeaders)) for i := 0; i < int(numHeaders); i++ { var length uint32 if err := binary.Read(r, binary.BigEndian, &length); err != nil { return nil, err } nameBytes := make([]byte, length) if _, err := io.ReadFull(r, nameBytes); err != nil { return nil, err } name := string(nameBytes) if name != strings.ToLower(name) { e = &Error{UnlowercasedHeaderName, streamId} name = strings.ToLower(name) } if h[name] != nil { e = &Error{DuplicateHeaders, streamId} } if err := binary.Read(r, binary.BigEndian, &length); err != nil { return nil, err } value := make([]byte, length) if _, err := io.ReadFull(r, value); err != nil { return nil, err } valueList := strings.Split(string(value), headerValueSeparator) for _, v := range valueList { h.Add(name, v) } } if e != nil { return h, e } return h, nil } func (f *Framer) readSynStreamFrame(h ControlFrameHeader, frame *SynStreamFrame) error { frame.CFHeader = h var err error if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { return err } if err = binary.Read(f.r, binary.BigEndian, &frame.AssociatedToStreamId); err != nil { return err } if err = binary.Read(f.r, binary.BigEndian, &frame.Priority); err != nil { return err } frame.Priority >>= 5 if err = binary.Read(f.r, binary.BigEndian, &frame.Slot); err != nil { return err } reader := f.r if !f.headerCompressionDisabled { err := f.uncorkHeaderDecompressor(int64(h.length - 10)) if err != nil { return err } reader = f.headerDecompressor } frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) { err = &Error{WrongCompressedPayloadSize, 0} } if err != nil { return err } for h := range frame.Headers { if invalidReqHeaders[h] { return &Error{InvalidHeaderPresent, frame.StreamId} } } if frame.StreamId == 0 { return &Error{ZeroStreamId, 0} } return nil } func (f *Framer) readSynReplyFrame(h ControlFrameHeader, frame *SynReplyFrame) error { frame.CFHeader = h var err error if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { return err } reader := f.r if !f.headerCompressionDisabled { err := f.uncorkHeaderDecompressor(int64(h.length - 4)) if err != nil { return err } reader = f.headerDecompressor } frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) { err = &Error{WrongCompressedPayloadSize, 0} } if err != nil { return err } for h := range frame.Headers { if invalidRespHeaders[h] { return &Error{InvalidHeaderPresent, frame.StreamId} } } if frame.StreamId == 0 { return &Error{ZeroStreamId, 0} } return nil } func (f *Framer) readHeadersFrame(h ControlFrameHeader, frame *HeadersFrame) error { frame.CFHeader = h var err error if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { return err } reader := f.r if !f.headerCompressionDisabled { err := f.uncorkHeaderDecompressor(int64(h.length - 4)) if err != nil { return err } reader = f.headerDecompressor } frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) { err = &Error{WrongCompressedPayloadSize, 0} } if err != nil { return err } var invalidHeaders map[string]bool if frame.StreamId%2 == 0 { invalidHeaders = invalidReqHeaders } else { invalidHeaders = invalidRespHeaders } for h := range frame.Headers { if invalidHeaders[h] { return &Error{InvalidHeaderPresent, frame.StreamId} } } if frame.StreamId == 0 { return &Error{ZeroStreamId, 0} } return nil } func (f *Framer) parseDataFrame(streamId StreamId) (*DataFrame, error) { var length uint32 if err := binary.Read(f.r, binary.BigEndian, &length); err != nil { return nil, err } var frame DataFrame frame.StreamId = streamId frame.Flags = DataFlags(length >> 24) length &= 0xffffff frame.Data = make([]byte, length) if _, err := io.ReadFull(f.r, frame.Data); err != nil { return nil, err } if frame.StreamId == 0 { return nil, &Error{ZeroStreamId, 0} } return &frame, nil } ================================================ FILE: vendor/github.com/moby/spdystream/spdy/types.go ================================================ /* Copyright 2014-2021 Docker Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package spdy implements the SPDY protocol (currently SPDY/3), described in // http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3. package spdy import ( "bytes" "compress/zlib" "io" "net/http" ) // Version is the protocol version number that this package implements. const Version = 3 // ControlFrameType stores the type field in a control frame header. type ControlFrameType uint16 const ( TypeSynStream ControlFrameType = 0x0001 TypeSynReply ControlFrameType = 0x0002 TypeRstStream ControlFrameType = 0x0003 TypeSettings ControlFrameType = 0x0004 TypePing ControlFrameType = 0x0006 TypeGoAway ControlFrameType = 0x0007 TypeHeaders ControlFrameType = 0x0008 TypeWindowUpdate ControlFrameType = 0x0009 ) // ControlFlags are the flags that can be set on a control frame. type ControlFlags uint8 const ( ControlFlagFin ControlFlags = 0x01 ControlFlagUnidirectional ControlFlags = 0x02 ControlFlagSettingsClearSettings ControlFlags = 0x01 ) // DataFlags are the flags that can be set on a data frame. type DataFlags uint8 const ( DataFlagFin DataFlags = 0x01 ) // MaxDataLength is the maximum number of bytes that can be stored in one frame. const MaxDataLength = 1<<24 - 1 // headerValueSepator separates multiple header values. const headerValueSeparator = "\x00" // Frame is a single SPDY frame in its unpacked in-memory representation. Use // Framer to read and write it. type Frame interface { write(f *Framer) error } // ControlFrameHeader contains all the fields in a control frame header, // in its unpacked in-memory representation. type ControlFrameHeader struct { // Note, high bit is the "Control" bit. version uint16 // spdy version number frameType ControlFrameType Flags ControlFlags length uint32 // length of data field } type controlFrame interface { Frame read(h ControlFrameHeader, f *Framer) error } // StreamId represents a 31-bit value identifying the stream. type StreamId uint32 // SynStreamFrame is the unpacked, in-memory representation of a SYN_STREAM // frame. type SynStreamFrame struct { CFHeader ControlFrameHeader StreamId StreamId AssociatedToStreamId StreamId // stream id for a stream which this stream is associated to Priority uint8 // priority of this frame (3-bit) Slot uint8 // index in the server's credential vector of the client certificate Headers http.Header } // SynReplyFrame is the unpacked, in-memory representation of a SYN_REPLY frame. type SynReplyFrame struct { CFHeader ControlFrameHeader StreamId StreamId Headers http.Header } // RstStreamStatus represents the status that led to a RST_STREAM. type RstStreamStatus uint32 const ( ProtocolError RstStreamStatus = iota + 1 InvalidStream RefusedStream UnsupportedVersion Cancel InternalError FlowControlError StreamInUse StreamAlreadyClosed InvalidCredentials FrameTooLarge ) // RstStreamFrame is the unpacked, in-memory representation of a RST_STREAM // frame. type RstStreamFrame struct { CFHeader ControlFrameHeader StreamId StreamId Status RstStreamStatus } // SettingsFlag represents a flag in a SETTINGS frame. type SettingsFlag uint8 const ( FlagSettingsPersistValue SettingsFlag = 0x1 FlagSettingsPersisted SettingsFlag = 0x2 ) // SettingsFlag represents the id of an id/value pair in a SETTINGS frame. type SettingsId uint32 const ( SettingsUploadBandwidth SettingsId = iota + 1 SettingsDownloadBandwidth SettingsRoundTripTime SettingsMaxConcurrentStreams SettingsCurrentCwnd SettingsDownloadRetransRate SettingsInitialWindowSize SettingsClientCretificateVectorSize ) // SettingsFlagIdValue is the unpacked, in-memory representation of the // combined flag/id/value for a setting in a SETTINGS frame. type SettingsFlagIdValue struct { Flag SettingsFlag Id SettingsId Value uint32 } // SettingsFrame is the unpacked, in-memory representation of a SPDY // SETTINGS frame. type SettingsFrame struct { CFHeader ControlFrameHeader FlagIdValues []SettingsFlagIdValue } // PingFrame is the unpacked, in-memory representation of a PING frame. type PingFrame struct { CFHeader ControlFrameHeader Id uint32 // unique id for this ping, from server is even, from client is odd. } // GoAwayStatus represents the status in a GoAwayFrame. type GoAwayStatus uint32 const ( GoAwayOK GoAwayStatus = iota GoAwayProtocolError GoAwayInternalError ) // GoAwayFrame is the unpacked, in-memory representation of a GOAWAY frame. type GoAwayFrame struct { CFHeader ControlFrameHeader LastGoodStreamId StreamId // last stream id which was accepted by sender Status GoAwayStatus } // HeadersFrame is the unpacked, in-memory representation of a HEADERS frame. type HeadersFrame struct { CFHeader ControlFrameHeader StreamId StreamId Headers http.Header } // WindowUpdateFrame is the unpacked, in-memory representation of a // WINDOW_UPDATE frame. type WindowUpdateFrame struct { CFHeader ControlFrameHeader StreamId StreamId DeltaWindowSize uint32 // additional number of bytes to existing window size } // TODO: Implement credential frame and related methods. // DataFrame is the unpacked, in-memory representation of a DATA frame. type DataFrame struct { // Note, high bit is the "Control" bit. Should be 0 for data frames. StreamId StreamId Flags DataFlags Data []byte // payload data of this frame } // A SPDY specific error. type ErrorCode string const ( UnlowercasedHeaderName ErrorCode = "header was not lowercased" DuplicateHeaders ErrorCode = "multiple headers with same name" WrongCompressedPayloadSize ErrorCode = "compressed payload size was incorrect" UnknownFrameType ErrorCode = "unknown frame type" InvalidControlFrame ErrorCode = "invalid control frame" InvalidDataFrame ErrorCode = "invalid data frame" InvalidHeaderPresent ErrorCode = "frame contained invalid header" ZeroStreamId ErrorCode = "stream id zero is disallowed" ) // Error contains both the type of error and additional values. StreamId is 0 // if Error is not associated with a stream. type Error struct { Err ErrorCode StreamId StreamId } func (e *Error) Error() string { return string(e.Err) } var invalidReqHeaders = map[string]bool{ "Connection": true, "Host": true, "Keep-Alive": true, "Proxy-Connection": true, "Transfer-Encoding": true, } var invalidRespHeaders = map[string]bool{ "Connection": true, "Keep-Alive": true, "Proxy-Connection": true, "Transfer-Encoding": true, } // Framer handles serializing/deserializing SPDY frames, including compressing/ // decompressing payloads. type Framer struct { headerCompressionDisabled bool w io.Writer headerBuf *bytes.Buffer headerCompressor *zlib.Writer r io.Reader headerReader io.LimitedReader headerDecompressor io.ReadCloser } // NewFramer allocates a new Framer for a given SPDY connection, represented by // a io.Writer and io.Reader. Note that Framer will read and write individual fields // from/to the Reader and Writer, so the caller should pass in an appropriately // buffered implementation to optimize performance. func NewFramer(w io.Writer, r io.Reader) (*Framer, error) { compressBuf := new(bytes.Buffer) compressor, err := zlib.NewWriterLevelDict(compressBuf, zlib.BestCompression, []byte(headerDictionary)) if err != nil { return nil, err } framer := &Framer{ w: w, headerBuf: compressBuf, headerCompressor: compressor, r: r, } return framer, nil } ================================================ FILE: vendor/github.com/moby/spdystream/spdy/write.go ================================================ /* Copyright 2014-2021 Docker Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package spdy import ( "encoding/binary" "io" "net/http" "strings" ) func (frame *SynStreamFrame) write(f *Framer) error { return f.writeSynStreamFrame(frame) } func (frame *SynReplyFrame) write(f *Framer) error { return f.writeSynReplyFrame(frame) } func (frame *RstStreamFrame) write(f *Framer) (err error) { if frame.StreamId == 0 { return &Error{ZeroStreamId, 0} } frame.CFHeader.version = Version frame.CFHeader.frameType = TypeRstStream frame.CFHeader.Flags = 0 frame.CFHeader.length = 8 // Serialize frame to Writer. if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { return } if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { return } if frame.Status == 0 { return &Error{InvalidControlFrame, frame.StreamId} } if err = binary.Write(f.w, binary.BigEndian, frame.Status); err != nil { return } return } func (frame *SettingsFrame) write(f *Framer) (err error) { frame.CFHeader.version = Version frame.CFHeader.frameType = TypeSettings frame.CFHeader.length = uint32(len(frame.FlagIdValues)*8 + 4) // Serialize frame to Writer. if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { return } if err = binary.Write(f.w, binary.BigEndian, uint32(len(frame.FlagIdValues))); err != nil { return } for _, flagIdValue := range frame.FlagIdValues { flagId := uint32(flagIdValue.Flag)<<24 | uint32(flagIdValue.Id) if err = binary.Write(f.w, binary.BigEndian, flagId); err != nil { return } if err = binary.Write(f.w, binary.BigEndian, flagIdValue.Value); err != nil { return } } return } func (frame *PingFrame) write(f *Framer) (err error) { if frame.Id == 0 { return &Error{ZeroStreamId, 0} } frame.CFHeader.version = Version frame.CFHeader.frameType = TypePing frame.CFHeader.Flags = 0 frame.CFHeader.length = 4 // Serialize frame to Writer. if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { return } if err = binary.Write(f.w, binary.BigEndian, frame.Id); err != nil { return } return } func (frame *GoAwayFrame) write(f *Framer) (err error) { frame.CFHeader.version = Version frame.CFHeader.frameType = TypeGoAway frame.CFHeader.Flags = 0 frame.CFHeader.length = 8 // Serialize frame to Writer. if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { return } if err = binary.Write(f.w, binary.BigEndian, frame.LastGoodStreamId); err != nil { return } if err = binary.Write(f.w, binary.BigEndian, frame.Status); err != nil { return } return nil } func (frame *HeadersFrame) write(f *Framer) error { return f.writeHeadersFrame(frame) } func (frame *WindowUpdateFrame) write(f *Framer) (err error) { frame.CFHeader.version = Version frame.CFHeader.frameType = TypeWindowUpdate frame.CFHeader.Flags = 0 frame.CFHeader.length = 8 // Serialize frame to Writer. if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { return } if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { return } if err = binary.Write(f.w, binary.BigEndian, frame.DeltaWindowSize); err != nil { return } return nil } func (frame *DataFrame) write(f *Framer) error { return f.writeDataFrame(frame) } // WriteFrame writes a frame. func (f *Framer) WriteFrame(frame Frame) error { return frame.write(f) } func writeControlFrameHeader(w io.Writer, h ControlFrameHeader) error { if err := binary.Write(w, binary.BigEndian, 0x8000|h.version); err != nil { return err } if err := binary.Write(w, binary.BigEndian, h.frameType); err != nil { return err } flagsAndLength := uint32(h.Flags)<<24 | h.length if err := binary.Write(w, binary.BigEndian, flagsAndLength); err != nil { return err } return nil } func writeHeaderValueBlock(w io.Writer, h http.Header) (n int, err error) { n = 0 if err = binary.Write(w, binary.BigEndian, uint32(len(h))); err != nil { return } n += 2 for name, values := range h { if err = binary.Write(w, binary.BigEndian, uint32(len(name))); err != nil { return } n += 2 name = strings.ToLower(name) if _, err = io.WriteString(w, name); err != nil { return } n += len(name) v := strings.Join(values, headerValueSeparator) if err = binary.Write(w, binary.BigEndian, uint32(len(v))); err != nil { return } n += 2 if _, err = io.WriteString(w, v); err != nil { return } n += len(v) } return } func (f *Framer) writeSynStreamFrame(frame *SynStreamFrame) (err error) { if frame.StreamId == 0 { return &Error{ZeroStreamId, 0} } // Marshal the headers. var writer io.Writer = f.headerBuf if !f.headerCompressionDisabled { writer = f.headerCompressor } if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { return } if !f.headerCompressionDisabled { f.headerCompressor.Flush() } // Set ControlFrameHeader. frame.CFHeader.version = Version frame.CFHeader.frameType = TypeSynStream frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 10) // Serialize frame to Writer. if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { return err } if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { return err } if err = binary.Write(f.w, binary.BigEndian, frame.AssociatedToStreamId); err != nil { return err } if err = binary.Write(f.w, binary.BigEndian, frame.Priority<<5); err != nil { return err } if err = binary.Write(f.w, binary.BigEndian, frame.Slot); err != nil { return err } if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { return err } f.headerBuf.Reset() return nil } func (f *Framer) writeSynReplyFrame(frame *SynReplyFrame) (err error) { if frame.StreamId == 0 { return &Error{ZeroStreamId, 0} } // Marshal the headers. var writer io.Writer = f.headerBuf if !f.headerCompressionDisabled { writer = f.headerCompressor } if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { return } if !f.headerCompressionDisabled { f.headerCompressor.Flush() } // Set ControlFrameHeader. frame.CFHeader.version = Version frame.CFHeader.frameType = TypeSynReply frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 4) // Serialize frame to Writer. if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { return } if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { return } if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { return } f.headerBuf.Reset() return } func (f *Framer) writeHeadersFrame(frame *HeadersFrame) (err error) { if frame.StreamId == 0 { return &Error{ZeroStreamId, 0} } // Marshal the headers. var writer io.Writer = f.headerBuf if !f.headerCompressionDisabled { writer = f.headerCompressor } if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { return } if !f.headerCompressionDisabled { f.headerCompressor.Flush() } // Set ControlFrameHeader. frame.CFHeader.version = Version frame.CFHeader.frameType = TypeHeaders frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 4) // Serialize frame to Writer. if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { return } if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { return } if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { return } f.headerBuf.Reset() return } func (f *Framer) writeDataFrame(frame *DataFrame) (err error) { if frame.StreamId == 0 { return &Error{ZeroStreamId, 0} } if frame.StreamId&0x80000000 != 0 || len(frame.Data) > MaxDataLength { return &Error{InvalidDataFrame, frame.StreamId} } // Serialize frame to Writer. if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { return } flagsAndLength := uint32(frame.Flags)<<24 | uint32(len(frame.Data)) if err = binary.Write(f.w, binary.BigEndian, flagsAndLength); err != nil { return } if _, err = f.w.Write(frame.Data); err != nil { return } return nil } ================================================ FILE: vendor/github.com/moby/spdystream/stream.go ================================================ /* Copyright 2014-2021 Docker Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package spdystream import ( "errors" "fmt" "io" "net" "net/http" "sync" "time" "github.com/moby/spdystream/spdy" ) var ( ErrUnreadPartialData = errors.New("unread partial data") ) type Stream struct { streamId spdy.StreamId parent *Stream conn *Connection startChan chan error dataLock sync.RWMutex dataChan chan []byte unread []byte priority uint8 headers http.Header headerChan chan http.Header finishLock sync.Mutex finished bool replyCond *sync.Cond replied bool closeLock sync.Mutex closeChan chan bool } // WriteData writes data to stream, sending a dataframe per call func (s *Stream) WriteData(data []byte, fin bool) error { s.waitWriteReply() var flags spdy.DataFlags if fin { flags = spdy.DataFlagFin s.finishLock.Lock() if s.finished { s.finishLock.Unlock() return ErrWriteClosedStream } s.finished = true s.finishLock.Unlock() } dataFrame := &spdy.DataFrame{ StreamId: s.streamId, Flags: flags, Data: data, } debugMessage("(%p) (%d) Writing data frame", s, s.streamId) return s.conn.framer.WriteFrame(dataFrame) } // Write writes bytes to a stream, calling write data for each call. func (s *Stream) Write(data []byte) (n int, err error) { err = s.WriteData(data, false) if err == nil { n = len(data) } return } // Read reads bytes from a stream, a single read will never get more // than what is sent on a single data frame, but a multiple calls to // read may get data from the same data frame. func (s *Stream) Read(p []byte) (n int, err error) { if s.unread == nil { select { case <-s.closeChan: return 0, io.EOF case read, ok := <-s.dataChan: if !ok { return 0, io.EOF } s.unread = read } } n = copy(p, s.unread) if n < len(s.unread) { s.unread = s.unread[n:] } else { s.unread = nil } return } // ReadData reads an entire data frame and returns the byte array // from the data frame. If there is unread data from the result // of a Read call, this function will return an ErrUnreadPartialData. func (s *Stream) ReadData() ([]byte, error) { debugMessage("(%p) Reading data from %d", s, s.streamId) if s.unread != nil { return nil, ErrUnreadPartialData } select { case <-s.closeChan: return nil, io.EOF case read, ok := <-s.dataChan: if !ok { return nil, io.EOF } return read, nil } } func (s *Stream) waitWriteReply() { if s.replyCond != nil { s.replyCond.L.Lock() for !s.replied { s.replyCond.Wait() } s.replyCond.L.Unlock() } } // Wait waits for the stream to receive a reply. func (s *Stream) Wait() error { return s.WaitTimeout(time.Duration(0)) } // WaitTimeout waits for the stream to receive a reply or for timeout. // When the timeout is reached, ErrTimeout will be returned. func (s *Stream) WaitTimeout(timeout time.Duration) error { var timeoutChan <-chan time.Time if timeout > time.Duration(0) { timeoutChan = time.After(timeout) } select { case err := <-s.startChan: if err != nil { return err } break case <-timeoutChan: return ErrTimeout } return nil } // Close closes the stream by sending an empty data frame with the // finish flag set, indicating this side is finished with the stream. func (s *Stream) Close() error { select { case <-s.closeChan: // Stream is now fully closed s.conn.removeStream(s) default: break } return s.WriteData([]byte{}, true) } // Reset sends a reset frame, putting the stream into the fully closed state. func (s *Stream) Reset() error { s.conn.removeStream(s) return s.resetStream() } func (s *Stream) resetStream() error { // Always call closeRemoteChannels, even if s.finished is already true. // This makes it so that stream.Close() followed by stream.Reset() allows // stream.Read() to unblock. s.closeRemoteChannels() s.finishLock.Lock() if s.finished { s.finishLock.Unlock() return nil } s.finished = true s.finishLock.Unlock() resetFrame := &spdy.RstStreamFrame{ StreamId: s.streamId, Status: spdy.Cancel, } return s.conn.framer.WriteFrame(resetFrame) } // CreateSubStream creates a stream using the current as the parent func (s *Stream) CreateSubStream(headers http.Header, fin bool) (*Stream, error) { return s.conn.CreateStream(headers, s, fin) } // SetPriority sets the stream priority, does not affect the // remote priority of this stream after Open has been called. // Valid values are 0 through 7, 0 being the highest priority // and 7 the lowest. func (s *Stream) SetPriority(priority uint8) { s.priority = priority } // SendHeader sends a header frame across the stream func (s *Stream) SendHeader(headers http.Header, fin bool) error { return s.conn.sendHeaders(headers, s, fin) } // SendReply sends a reply on a stream, only valid to be called once // when handling a new stream func (s *Stream) SendReply(headers http.Header, fin bool) error { if s.replyCond == nil { return errors.New("cannot reply on initiated stream") } s.replyCond.L.Lock() defer s.replyCond.L.Unlock() if s.replied { return nil } err := s.conn.sendReply(headers, s, fin) if err != nil { return err } s.replied = true s.replyCond.Broadcast() return nil } // Refuse sends a reset frame with the status refuse, only // valid to be called once when handling a new stream. This // may be used to indicate that a stream is not allowed // when http status codes are not being used. func (s *Stream) Refuse() error { if s.replied { return nil } s.replied = true return s.conn.sendReset(spdy.RefusedStream, s) } // Cancel sends a reset frame with the status canceled. This // can be used at any time by the creator of the Stream to // indicate the stream is no longer needed. func (s *Stream) Cancel() error { return s.conn.sendReset(spdy.Cancel, s) } // ReceiveHeader receives a header sent on the other side // of the stream. This function will block until a header // is received or stream is closed. func (s *Stream) ReceiveHeader() (http.Header, error) { select { case <-s.closeChan: break case header, ok := <-s.headerChan: if !ok { return nil, fmt.Errorf("header chan closed") } return header, nil } return nil, fmt.Errorf("stream closed") } // Parent returns the parent stream func (s *Stream) Parent() *Stream { return s.parent } // Headers returns the headers used to create the stream func (s *Stream) Headers() http.Header { return s.headers } // String returns the string version of stream using the // streamId to uniquely identify the stream func (s *Stream) String() string { return fmt.Sprintf("stream:%d", s.streamId) } // Identifier returns a 32 bit identifier for the stream func (s *Stream) Identifier() uint32 { return uint32(s.streamId) } // IsFinished returns whether the stream has finished // sending data func (s *Stream) IsFinished() bool { return s.finished } // Implement net.Conn interface func (s *Stream) LocalAddr() net.Addr { return s.conn.conn.LocalAddr() } func (s *Stream) RemoteAddr() net.Addr { return s.conn.conn.RemoteAddr() } // TODO set per stream values instead of connection-wide func (s *Stream) SetDeadline(t time.Time) error { return s.conn.conn.SetDeadline(t) } func (s *Stream) SetReadDeadline(t time.Time) error { return s.conn.conn.SetReadDeadline(t) } func (s *Stream) SetWriteDeadline(t time.Time) error { return s.conn.conn.SetWriteDeadline(t) } func (s *Stream) closeRemoteChannels() { s.closeLock.Lock() defer s.closeLock.Unlock() select { case <-s.closeChan: default: close(s.closeChan) } } ================================================ FILE: vendor/github.com/moby/spdystream/utils.go ================================================ /* Copyright 2014-2021 Docker Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package spdystream import ( "log" "os" ) var ( DEBUG = os.Getenv("DEBUG") ) func debugMessage(fmt string, args ...interface{}) { if DEBUG != "" { log.Printf(fmt, args...) } } ================================================ FILE: vendor/github.com/moby/sys/sequential/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/moby/sys/sequential/doc.go ================================================ // Package sequential provides a set of functions for managing sequential // files on Windows. // // The origin of these functions are the golang OS and windows packages, // slightly modified to only cope with files, not directories due to the // specific use case. // // The alteration is to allow a file on Windows to be opened with // FILE_FLAG_SEQUENTIAL_SCAN (particular for docker load), to avoid eating // the standby list, particularly when accessing large files such as layer.tar. // // For non-Windows platforms, the package provides wrappers for the equivalents // in the os packages. They are passthrough on Unix platforms, and only relevant // on Windows. package sequential ================================================ FILE: vendor/github.com/moby/sys/sequential/sequential_unix.go ================================================ //go:build !windows // +build !windows package sequential import "os" // Create creates the named file with mode 0666 (before umask), truncating // it if it already exists. If successful, methods on the returned // File can be used for I/O; the associated file descriptor has mode // O_RDWR. // If there is an error, it will be of type *PathError. func Create(name string) (*os.File, error) { return os.Create(name) } // Open opens the named file for reading. If successful, methods on // the returned file can be used for reading; the associated file // descriptor has mode O_RDONLY. // If there is an error, it will be of type *PathError. func Open(name string) (*os.File, error) { return os.Open(name) } // OpenFile is the generalized open call; most users will use Open // or Create instead. It opens the named file with specified flag // (O_RDONLY etc.) and perm, (0666 etc.) if applicable. If successful, // methods on the returned File can be used for I/O. // If there is an error, it will be of type *PathError. func OpenFile(name string, flag int, perm os.FileMode) (*os.File, error) { return os.OpenFile(name, flag, perm) } // CreateTemp creates a new temporary file in the directory dir // with a name beginning with prefix, opens the file for reading // and writing, and returns the resulting *os.File. // If dir is the empty string, TempFile uses the default directory // for temporary files (see os.TempDir). // Multiple programs calling TempFile simultaneously // will not choose the same file. The caller can use f.Name() // to find the pathname of the file. It is the caller's responsibility // to remove the file when no longer needed. func CreateTemp(dir, prefix string) (f *os.File, err error) { return os.CreateTemp(dir, prefix) } ================================================ FILE: vendor/github.com/moby/sys/sequential/sequential_windows.go ================================================ package sequential import ( "os" "path/filepath" "strconv" "sync" "syscall" "time" "unsafe" "golang.org/x/sys/windows" ) // Create creates the named file with mode 0666 (before umask), truncating // it if it already exists. If successful, methods on the returned // File can be used for I/O; the associated file descriptor has mode // O_RDWR. // If there is an error, it will be of type *PathError. func Create(name string) (*os.File, error) { return OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0) } // Open opens the named file for reading. If successful, methods on // the returned file can be used for reading; the associated file // descriptor has mode O_RDONLY. // If there is an error, it will be of type *PathError. func Open(name string) (*os.File, error) { return OpenFile(name, os.O_RDONLY, 0) } // OpenFile is the generalized open call; most users will use Open // or Create instead. // If there is an error, it will be of type *PathError. func OpenFile(name string, flag int, _ os.FileMode) (*os.File, error) { if name == "" { return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOENT} } r, err := openFileSequential(name, flag, 0) if err == nil { return r, nil } return nil, &os.PathError{Op: "open", Path: name, Err: err} } func openFileSequential(name string, flag int, _ os.FileMode) (file *os.File, err error) { r, e := openSequential(name, flag|windows.O_CLOEXEC, 0) if e != nil { return nil, e } return os.NewFile(uintptr(r), name), nil } func makeInheritSa() *windows.SecurityAttributes { var sa windows.SecurityAttributes sa.Length = uint32(unsafe.Sizeof(sa)) sa.InheritHandle = 1 return &sa } func openSequential(path string, mode int, _ uint32) (fd windows.Handle, err error) { if len(path) == 0 { return windows.InvalidHandle, windows.ERROR_FILE_NOT_FOUND } pathp, err := windows.UTF16PtrFromString(path) if err != nil { return windows.InvalidHandle, err } var access uint32 switch mode & (windows.O_RDONLY | windows.O_WRONLY | windows.O_RDWR) { case windows.O_RDONLY: access = windows.GENERIC_READ case windows.O_WRONLY: access = windows.GENERIC_WRITE case windows.O_RDWR: access = windows.GENERIC_READ | windows.GENERIC_WRITE } if mode&windows.O_CREAT != 0 { access |= windows.GENERIC_WRITE } if mode&windows.O_APPEND != 0 { access &^= windows.GENERIC_WRITE access |= windows.FILE_APPEND_DATA } sharemode := uint32(windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE) var sa *windows.SecurityAttributes if mode&windows.O_CLOEXEC == 0 { sa = makeInheritSa() } var createmode uint32 switch { case mode&(windows.O_CREAT|windows.O_EXCL) == (windows.O_CREAT | windows.O_EXCL): createmode = windows.CREATE_NEW case mode&(windows.O_CREAT|windows.O_TRUNC) == (windows.O_CREAT | windows.O_TRUNC): createmode = windows.CREATE_ALWAYS case mode&windows.O_CREAT == windows.O_CREAT: createmode = windows.OPEN_ALWAYS case mode&windows.O_TRUNC == windows.O_TRUNC: createmode = windows.TRUNCATE_EXISTING default: createmode = windows.OPEN_EXISTING } // Use FILE_FLAG_SEQUENTIAL_SCAN rather than FILE_ATTRIBUTE_NORMAL as implemented in golang. // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx const fileFlagSequentialScan = 0x08000000 // FILE_FLAG_SEQUENTIAL_SCAN h, e := windows.CreateFile(pathp, access, sharemode, sa, createmode, fileFlagSequentialScan, 0) return h, e } // Helpers for CreateTemp var rand uint32 var randmu sync.Mutex func reseed() uint32 { return uint32(time.Now().UnixNano() + int64(os.Getpid())) } func nextSuffix() string { randmu.Lock() r := rand if r == 0 { r = reseed() } r = r*1664525 + 1013904223 // constants from Numerical Recipes rand = r randmu.Unlock() return strconv.Itoa(int(1e9 + r%1e9))[1:] } // CreateTemp is a copy of os.CreateTemp, modified to use sequential // file access. Below is the original comment from golang: // TempFile creates a new temporary file in the directory dir // with a name beginning with prefix, opens the file for reading // and writing, and returns the resulting *os.File. // If dir is the empty string, TempFile uses the default directory // for temporary files (see os.TempDir). // Multiple programs calling TempFile simultaneously // will not choose the same file. The caller can use f.Name() // to find the pathname of the file. It is the caller's responsibility // to remove the file when no longer needed. func CreateTemp(dir, prefix string) (f *os.File, err error) { if dir == "" { dir = os.TempDir() } nconflict := 0 for i := 0; i < 10000; i++ { name := filepath.Join(dir, prefix+nextSuffix()) f, err = OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o600) if os.IsExist(err) { if nconflict++; nconflict > 10 { randmu.Lock() rand = reseed() randmu.Unlock() } continue } break } return } ================================================ FILE: vendor/github.com/moby/sys/user/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/moby/sys/user/lookup_unix.go ================================================ //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris // +build darwin dragonfly freebsd linux netbsd openbsd solaris package user import ( "io" "os" "strconv" "golang.org/x/sys/unix" ) // Unix-specific path to the passwd and group formatted files. const ( unixPasswdPath = "/etc/passwd" unixGroupPath = "/etc/group" ) // LookupUser looks up a user by their username in /etc/passwd. If the user // cannot be found (or there is no /etc/passwd file on the filesystem), then // LookupUser returns an error. func LookupUser(username string) (User, error) { return lookupUserFunc(func(u User) bool { return u.Name == username }) } // LookupUid looks up a user by their user id in /etc/passwd. If the user cannot // be found (or there is no /etc/passwd file on the filesystem), then LookupId // returns an error. func LookupUid(uid int) (User, error) { return lookupUserFunc(func(u User) bool { return u.Uid == uid }) } func lookupUserFunc(filter func(u User) bool) (User, error) { // Get operating system-specific passwd reader-closer. passwd, err := GetPasswd() if err != nil { return User{}, err } defer passwd.Close() // Get the users. users, err := ParsePasswdFilter(passwd, filter) if err != nil { return User{}, err } // No user entries found. if len(users) == 0 { return User{}, ErrNoPasswdEntries } // Assume the first entry is the "correct" one. return users[0], nil } // LookupGroup looks up a group by its name in /etc/group. If the group cannot // be found (or there is no /etc/group file on the filesystem), then LookupGroup // returns an error. func LookupGroup(groupname string) (Group, error) { return lookupGroupFunc(func(g Group) bool { return g.Name == groupname }) } // LookupGid looks up a group by its group id in /etc/group. If the group cannot // be found (or there is no /etc/group file on the filesystem), then LookupGid // returns an error. func LookupGid(gid int) (Group, error) { return lookupGroupFunc(func(g Group) bool { return g.Gid == gid }) } func lookupGroupFunc(filter func(g Group) bool) (Group, error) { // Get operating system-specific group reader-closer. group, err := GetGroup() if err != nil { return Group{}, err } defer group.Close() // Get the users. groups, err := ParseGroupFilter(group, filter) if err != nil { return Group{}, err } // No user entries found. if len(groups) == 0 { return Group{}, ErrNoGroupEntries } // Assume the first entry is the "correct" one. return groups[0], nil } func GetPasswdPath() (string, error) { return unixPasswdPath, nil } func GetPasswd() (io.ReadCloser, error) { return os.Open(unixPasswdPath) } func GetGroupPath() (string, error) { return unixGroupPath, nil } func GetGroup() (io.ReadCloser, error) { return os.Open(unixGroupPath) } // CurrentUser looks up the current user by their user id in /etc/passwd. If the // user cannot be found (or there is no /etc/passwd file on the filesystem), // then CurrentUser returns an error. func CurrentUser() (User, error) { return LookupUid(unix.Getuid()) } // CurrentGroup looks up the current user's group by their primary group id's // entry in /etc/passwd. If the group cannot be found (or there is no // /etc/group file on the filesystem), then CurrentGroup returns an error. func CurrentGroup() (Group, error) { return LookupGid(unix.Getgid()) } func currentUserSubIDs(fileName string) ([]SubID, error) { u, err := CurrentUser() if err != nil { return nil, err } filter := func(entry SubID) bool { return entry.Name == u.Name || entry.Name == strconv.Itoa(u.Uid) } return ParseSubIDFileFilter(fileName, filter) } func CurrentUserSubUIDs() ([]SubID, error) { return currentUserSubIDs("/etc/subuid") } func CurrentUserSubGIDs() ([]SubID, error) { return currentUserSubIDs("/etc/subgid") } func CurrentProcessUIDMap() ([]IDMap, error) { return ParseIDMapFile("/proc/self/uid_map") } func CurrentProcessGIDMap() ([]IDMap, error) { return ParseIDMapFile("/proc/self/gid_map") } ================================================ FILE: vendor/github.com/moby/sys/user/user.go ================================================ package user import ( "bufio" "bytes" "errors" "fmt" "io" "os" "strconv" "strings" ) const ( minID = 0 maxID = 1<<31 - 1 // for 32-bit systems compatibility ) var ( // ErrNoPasswdEntries is returned if no matching entries were found in /etc/group. ErrNoPasswdEntries = errors.New("no matching entries in passwd file") // ErrNoGroupEntries is returned if no matching entries were found in /etc/passwd. ErrNoGroupEntries = errors.New("no matching entries in group file") // ErrRange is returned if a UID or GID is outside of the valid range. ErrRange = fmt.Errorf("uids and gids must be in range %d-%d", minID, maxID) ) type User struct { Name string Pass string Uid int Gid int Gecos string Home string Shell string } type Group struct { Name string Pass string Gid int List []string } // SubID represents an entry in /etc/sub{u,g}id type SubID struct { Name string SubID int64 Count int64 } // IDMap represents an entry in /proc/PID/{u,g}id_map type IDMap struct { ID int64 ParentID int64 Count int64 } func parseLine(line []byte, v ...interface{}) { parseParts(bytes.Split(line, []byte(":")), v...) } func parseParts(parts [][]byte, v ...interface{}) { if len(parts) == 0 { return } for i, p := range parts { // Ignore cases where we don't have enough fields to populate the arguments. // Some configuration files like to misbehave. if len(v) <= i { break } // Use the type of the argument to figure out how to parse it, scanf() style. // This is legit. switch e := v[i].(type) { case *string: *e = string(p) case *int: // "numbers", with conversion errors ignored because of some misbehaving configuration files. *e, _ = strconv.Atoi(string(p)) case *int64: *e, _ = strconv.ParseInt(string(p), 10, 64) case *[]string: // Comma-separated lists. if len(p) != 0 { *e = strings.Split(string(p), ",") } else { *e = []string{} } default: // Someone goof'd when writing code using this function. Scream so they can hear us. panic(fmt.Sprintf("parseLine only accepts {*string, *int, *int64, *[]string} as arguments! %#v is not a pointer!", e)) } } } func ParsePasswdFile(path string) ([]User, error) { passwd, err := os.Open(path) if err != nil { return nil, err } defer passwd.Close() return ParsePasswd(passwd) } func ParsePasswd(passwd io.Reader) ([]User, error) { return ParsePasswdFilter(passwd, nil) } func ParsePasswdFileFilter(path string, filter func(User) bool) ([]User, error) { passwd, err := os.Open(path) if err != nil { return nil, err } defer passwd.Close() return ParsePasswdFilter(passwd, filter) } func ParsePasswdFilter(r io.Reader, filter func(User) bool) ([]User, error) { if r == nil { return nil, errors.New("nil source for passwd-formatted data") } var ( s = bufio.NewScanner(r) out = []User{} ) for s.Scan() { line := bytes.TrimSpace(s.Bytes()) if len(line) == 0 { continue } // see: man 5 passwd // name:password:UID:GID:GECOS:directory:shell // Name:Pass:Uid:Gid:Gecos:Home:Shell // root:x:0:0:root:/root:/bin/bash // adm:x:3:4:adm:/var/adm:/bin/false p := User{} parseLine(line, &p.Name, &p.Pass, &p.Uid, &p.Gid, &p.Gecos, &p.Home, &p.Shell) if filter == nil || filter(p) { out = append(out, p) } } if err := s.Err(); err != nil { return nil, err } return out, nil } func ParseGroupFile(path string) ([]Group, error) { group, err := os.Open(path) if err != nil { return nil, err } defer group.Close() return ParseGroup(group) } func ParseGroup(group io.Reader) ([]Group, error) { return ParseGroupFilter(group, nil) } func ParseGroupFileFilter(path string, filter func(Group) bool) ([]Group, error) { group, err := os.Open(path) if err != nil { return nil, err } defer group.Close() return ParseGroupFilter(group, filter) } func ParseGroupFilter(r io.Reader, filter func(Group) bool) ([]Group, error) { if r == nil { return nil, errors.New("nil source for group-formatted data") } rd := bufio.NewReader(r) out := []Group{} // Read the file line-by-line. for { var ( isPrefix bool wholeLine []byte err error ) // Read the next line. We do so in chunks (as much as reader's // buffer is able to keep), check if we read enough columns // already on each step and store final result in wholeLine. for { var line []byte line, isPrefix, err = rd.ReadLine() if err != nil { // We should return no error if EOF is reached // without a match. if err == io.EOF { err = nil } return out, err } // Simple common case: line is short enough to fit in a // single reader's buffer. if !isPrefix && len(wholeLine) == 0 { wholeLine = line break } wholeLine = append(wholeLine, line...) // Check if we read the whole line already. if !isPrefix { break } } // There's no spec for /etc/passwd or /etc/group, but we try to follow // the same rules as the glibc parser, which allows comments and blank // space at the beginning of a line. wholeLine = bytes.TrimSpace(wholeLine) if len(wholeLine) == 0 || wholeLine[0] == '#' { continue } // see: man 5 group // group_name:password:GID:user_list // Name:Pass:Gid:List // root:x:0:root // adm:x:4:root,adm,daemon p := Group{} parseLine(wholeLine, &p.Name, &p.Pass, &p.Gid, &p.List) if filter == nil || filter(p) { out = append(out, p) } } } type ExecUser struct { Uid int Gid int Sgids []int Home string } // GetExecUserPath is a wrapper for GetExecUser. It reads data from each of the // given file paths and uses that data as the arguments to GetExecUser. If the // files cannot be opened for any reason, the error is ignored and a nil // io.Reader is passed instead. func GetExecUserPath(userSpec string, defaults *ExecUser, passwdPath, groupPath string) (*ExecUser, error) { var passwd, group io.Reader if passwdFile, err := os.Open(passwdPath); err == nil { passwd = passwdFile defer passwdFile.Close() } if groupFile, err := os.Open(groupPath); err == nil { group = groupFile defer groupFile.Close() } return GetExecUser(userSpec, defaults, passwd, group) } // GetExecUser parses a user specification string (using the passwd and group // readers as sources for /etc/passwd and /etc/group data, respectively). In // the case of blank fields or missing data from the sources, the values in // defaults is used. // // GetExecUser will return an error if a user or group literal could not be // found in any entry in passwd and group respectively. // // Examples of valid user specifications are: // - "" // - "user" // - "uid" // - "user:group" // - "uid:gid // - "user:gid" // - "uid:group" // // It should be noted that if you specify a numeric user or group id, they will // not be evaluated as usernames (only the metadata will be filled). So attempting // to parse a user with user.Name = "1337" will produce the user with a UID of // 1337. func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (*ExecUser, error) { if defaults == nil { defaults = new(ExecUser) } // Copy over defaults. user := &ExecUser{ Uid: defaults.Uid, Gid: defaults.Gid, Sgids: defaults.Sgids, Home: defaults.Home, } // Sgids slice *cannot* be nil. if user.Sgids == nil { user.Sgids = []int{} } // Allow for userArg to have either "user" syntax, or optionally "user:group" syntax var userArg, groupArg string parseLine([]byte(userSpec), &userArg, &groupArg) // Convert userArg and groupArg to be numeric, so we don't have to execute // Atoi *twice* for each iteration over lines. uidArg, uidErr := strconv.Atoi(userArg) gidArg, gidErr := strconv.Atoi(groupArg) // Find the matching user. users, err := ParsePasswdFilter(passwd, func(u User) bool { if userArg == "" { // Default to current state of the user. return u.Uid == user.Uid } if uidErr == nil { // If the userArg is numeric, always treat it as a UID. return uidArg == u.Uid } return u.Name == userArg }) // If we can't find the user, we have to bail. if err != nil && passwd != nil { if userArg == "" { userArg = strconv.Itoa(user.Uid) } return nil, fmt.Errorf("unable to find user %s: %w", userArg, err) } var matchedUserName string if len(users) > 0 { // First match wins, even if there's more than one matching entry. matchedUserName = users[0].Name user.Uid = users[0].Uid user.Gid = users[0].Gid user.Home = users[0].Home } else if userArg != "" { // If we can't find a user with the given username, the only other valid // option is if it's a numeric username with no associated entry in passwd. if uidErr != nil { // Not numeric. return nil, fmt.Errorf("unable to find user %s: %w", userArg, ErrNoPasswdEntries) } user.Uid = uidArg // Must be inside valid uid range. if user.Uid < minID || user.Uid > maxID { return nil, ErrRange } // Okay, so it's numeric. We can just roll with this. } // On to the groups. If we matched a username, we need to do this because of // the supplementary group IDs. if groupArg != "" || matchedUserName != "" { groups, err := ParseGroupFilter(group, func(g Group) bool { // If the group argument isn't explicit, we'll just search for it. if groupArg == "" { // Check if user is a member of this group. for _, u := range g.List { if u == matchedUserName { return true } } return false } if gidErr == nil { // If the groupArg is numeric, always treat it as a GID. return gidArg == g.Gid } return g.Name == groupArg }) if err != nil && group != nil { return nil, fmt.Errorf("unable to find groups for spec %v: %w", matchedUserName, err) } // Only start modifying user.Gid if it is in explicit form. if groupArg != "" { if len(groups) > 0 { // First match wins, even if there's more than one matching entry. user.Gid = groups[0].Gid } else { // If we can't find a group with the given name, the only other valid // option is if it's a numeric group name with no associated entry in group. if gidErr != nil { // Not numeric. return nil, fmt.Errorf("unable to find group %s: %w", groupArg, ErrNoGroupEntries) } user.Gid = gidArg // Must be inside valid gid range. if user.Gid < minID || user.Gid > maxID { return nil, ErrRange } // Okay, so it's numeric. We can just roll with this. } } else if len(groups) > 0 { // Supplementary group ids only make sense if in the implicit form. user.Sgids = make([]int, len(groups)) for i, group := range groups { user.Sgids[i] = group.Gid } } } return user, nil } // GetAdditionalGroups looks up a list of groups by name or group id // against the given /etc/group formatted data. If a group name cannot // be found, an error will be returned. If a group id cannot be found, // or the given group data is nil, the id will be returned as-is // provided it is in the legal range. func GetAdditionalGroups(additionalGroups []string, group io.Reader) ([]int, error) { groups := []Group{} if group != nil { var err error groups, err = ParseGroupFilter(group, func(g Group) bool { for _, ag := range additionalGroups { if g.Name == ag || strconv.Itoa(g.Gid) == ag { return true } } return false }) if err != nil { return nil, fmt.Errorf("Unable to find additional groups %v: %w", additionalGroups, err) } } gidMap := make(map[int]struct{}) for _, ag := range additionalGroups { var found bool for _, g := range groups { // if we found a matched group either by name or gid, take the // first matched as correct if g.Name == ag || strconv.Itoa(g.Gid) == ag { if _, ok := gidMap[g.Gid]; !ok { gidMap[g.Gid] = struct{}{} found = true break } } } // we asked for a group but didn't find it. let's check to see // if we wanted a numeric group if !found { gid, err := strconv.ParseInt(ag, 10, 64) if err != nil { // Not a numeric ID either. return nil, fmt.Errorf("Unable to find group %s: %w", ag, ErrNoGroupEntries) } // Ensure gid is inside gid range. if gid < minID || gid > maxID { return nil, ErrRange } gidMap[int(gid)] = struct{}{} } } gids := []int{} for gid := range gidMap { gids = append(gids, gid) } return gids, nil } // GetAdditionalGroupsPath is a wrapper around GetAdditionalGroups // that opens the groupPath given and gives it as an argument to // GetAdditionalGroups. func GetAdditionalGroupsPath(additionalGroups []string, groupPath string) ([]int, error) { var group io.Reader if groupFile, err := os.Open(groupPath); err == nil { group = groupFile defer groupFile.Close() } return GetAdditionalGroups(additionalGroups, group) } func ParseSubIDFile(path string) ([]SubID, error) { subid, err := os.Open(path) if err != nil { return nil, err } defer subid.Close() return ParseSubID(subid) } func ParseSubID(subid io.Reader) ([]SubID, error) { return ParseSubIDFilter(subid, nil) } func ParseSubIDFileFilter(path string, filter func(SubID) bool) ([]SubID, error) { subid, err := os.Open(path) if err != nil { return nil, err } defer subid.Close() return ParseSubIDFilter(subid, filter) } func ParseSubIDFilter(r io.Reader, filter func(SubID) bool) ([]SubID, error) { if r == nil { return nil, errors.New("nil source for subid-formatted data") } var ( s = bufio.NewScanner(r) out = []SubID{} ) for s.Scan() { line := bytes.TrimSpace(s.Bytes()) if len(line) == 0 { continue } // see: man 5 subuid p := SubID{} parseLine(line, &p.Name, &p.SubID, &p.Count) if filter == nil || filter(p) { out = append(out, p) } } if err := s.Err(); err != nil { return nil, err } return out, nil } func ParseIDMapFile(path string) ([]IDMap, error) { r, err := os.Open(path) if err != nil { return nil, err } defer r.Close() return ParseIDMap(r) } func ParseIDMap(r io.Reader) ([]IDMap, error) { return ParseIDMapFilter(r, nil) } func ParseIDMapFileFilter(path string, filter func(IDMap) bool) ([]IDMap, error) { r, err := os.Open(path) if err != nil { return nil, err } defer r.Close() return ParseIDMapFilter(r, filter) } func ParseIDMapFilter(r io.Reader, filter func(IDMap) bool) ([]IDMap, error) { if r == nil { return nil, errors.New("nil source for idmap-formatted data") } var ( s = bufio.NewScanner(r) out = []IDMap{} ) for s.Scan() { line := bytes.TrimSpace(s.Bytes()) if len(line) == 0 { continue } // see: man 7 user_namespaces p := IDMap{} parseParts(bytes.Fields(line), &p.ID, &p.ParentID, &p.Count) if filter == nil || filter(p) { out = append(out, p) } } if err := s.Err(); err != nil { return nil, err } return out, nil } ================================================ FILE: vendor/github.com/moby/sys/user/user_fuzzer.go ================================================ //go:build gofuzz // +build gofuzz package user import ( "io" "strings" ) func IsDivisbleBy(n int, divisibleby int) bool { return (n % divisibleby) == 0 } func FuzzUser(data []byte) int { if len(data) == 0 { return -1 } if !IsDivisbleBy(len(data), 5) { return -1 } var divided [][]byte chunkSize := len(data) / 5 for i := 0; i < len(data); i += chunkSize { end := i + chunkSize divided = append(divided, data[i:end]) } _, _ = ParsePasswdFilter(strings.NewReader(string(divided[0])), nil) var passwd, group io.Reader group = strings.NewReader(string(divided[1])) _, _ = GetAdditionalGroups([]string{string(divided[2])}, group) passwd = strings.NewReader(string(divided[3])) _, _ = GetExecUser(string(divided[4]), nil, passwd, group) return 1 } ================================================ FILE: vendor/github.com/modern-go/concurrent/.gitignore ================================================ /coverage.txt ================================================ FILE: vendor/github.com/modern-go/concurrent/.travis.yml ================================================ language: go go: - 1.8.x - 1.x before_install: - go get -t -v ./... script: - ./test.sh after_success: - bash <(curl -s https://codecov.io/bash) ================================================ FILE: vendor/github.com/modern-go/concurrent/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/modern-go/concurrent/README.md ================================================ # concurrent [![Sourcegraph](https://sourcegraph.com/github.com/modern-go/concurrent/-/badge.svg)](https://sourcegraph.com/github.com/modern-go/concurrent?badge) [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/modern-go/concurrent) [![Build Status](https://travis-ci.org/modern-go/concurrent.svg?branch=master)](https://travis-ci.org/modern-go/concurrent) [![codecov](https://codecov.io/gh/modern-go/concurrent/branch/master/graph/badge.svg)](https://codecov.io/gh/modern-go/concurrent) [![rcard](https://goreportcard.com/badge/github.com/modern-go/concurrent)](https://goreportcard.com/report/github.com/modern-go/concurrent) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://raw.githubusercontent.com/modern-go/concurrent/master/LICENSE) * concurrent.Map: backport sync.Map for go below 1.9 * concurrent.Executor: goroutine with explicit ownership and cancellable # concurrent.Map because sync.Map is only available in go 1.9, we can use concurrent.Map to make code portable ```go m := concurrent.NewMap() m.Store("hello", "world") elem, found := m.Load("hello") // elem will be "world" // found will be true ``` # concurrent.Executor ```go executor := concurrent.NewUnboundedExecutor() executor.Go(func(ctx context.Context) { everyMillisecond := time.NewTicker(time.Millisecond) for { select { case <-ctx.Done(): fmt.Println("goroutine exited") return case <-everyMillisecond.C: // do something } } }) time.Sleep(time.Second) executor.StopAndWaitForever() fmt.Println("executor stopped") ``` attach goroutine to executor instance, so that we can * cancel it by stop the executor with Stop/StopAndWait/StopAndWaitForever * handle panic by callback: the default behavior will no longer crash your application ================================================ FILE: vendor/github.com/modern-go/concurrent/executor.go ================================================ package concurrent import "context" // Executor replace go keyword to start a new goroutine // the goroutine should cancel itself if the context passed in has been cancelled // the goroutine started by the executor, is owned by the executor // we can cancel all executors owned by the executor just by stop the executor itself // however Executor interface does not Stop method, the one starting and owning executor // should use the concrete type of executor, instead of this interface. type Executor interface { // Go starts a new goroutine controlled by the context Go(handler func(ctx context.Context)) } ================================================ FILE: vendor/github.com/modern-go/concurrent/go_above_19.go ================================================ //+build go1.9 package concurrent import "sync" // Map is a wrapper for sync.Map introduced in go1.9 type Map struct { sync.Map } // NewMap creates a thread safe Map func NewMap() *Map { return &Map{} } ================================================ FILE: vendor/github.com/modern-go/concurrent/go_below_19.go ================================================ //+build !go1.9 package concurrent import "sync" // Map implements a thread safe map for go version below 1.9 using mutex type Map struct { lock sync.RWMutex data map[interface{}]interface{} } // NewMap creates a thread safe map func NewMap() *Map { return &Map{ data: make(map[interface{}]interface{}, 32), } } // Load is same as sync.Map Load func (m *Map) Load(key interface{}) (elem interface{}, found bool) { m.lock.RLock() elem, found = m.data[key] m.lock.RUnlock() return } // Load is same as sync.Map Store func (m *Map) Store(key interface{}, elem interface{}) { m.lock.Lock() m.data[key] = elem m.lock.Unlock() } ================================================ FILE: vendor/github.com/modern-go/concurrent/log.go ================================================ package concurrent import ( "os" "log" "io/ioutil" ) // ErrorLogger is used to print out error, can be set to writer other than stderr var ErrorLogger = log.New(os.Stderr, "", 0) // InfoLogger is used to print informational message, default to off var InfoLogger = log.New(ioutil.Discard, "", 0) ================================================ FILE: vendor/github.com/modern-go/concurrent/test.sh ================================================ #!/usr/bin/env bash set -e echo "" > coverage.txt for d in $(go list ./... | grep -v vendor); do go test -coverprofile=profile.out -coverpkg=github.com/modern-go/concurrent $d if [ -f profile.out ]; then cat profile.out >> coverage.txt rm profile.out fi done ================================================ FILE: vendor/github.com/modern-go/concurrent/unbounded_executor.go ================================================ package concurrent import ( "context" "fmt" "runtime" "runtime/debug" "sync" "time" "reflect" ) // HandlePanic logs goroutine panic by default var HandlePanic = func(recovered interface{}, funcName string) { ErrorLogger.Println(fmt.Sprintf("%s panic: %v", funcName, recovered)) ErrorLogger.Println(string(debug.Stack())) } // UnboundedExecutor is a executor without limits on counts of alive goroutines // it tracks the goroutine started by it, and can cancel them when shutdown type UnboundedExecutor struct { ctx context.Context cancel context.CancelFunc activeGoroutinesMutex *sync.Mutex activeGoroutines map[string]int HandlePanic func(recovered interface{}, funcName string) } // GlobalUnboundedExecutor has the life cycle of the program itself // any goroutine want to be shutdown before main exit can be started from this executor // GlobalUnboundedExecutor expects the main function to call stop // it does not magically knows the main function exits var GlobalUnboundedExecutor = NewUnboundedExecutor() // NewUnboundedExecutor creates a new UnboundedExecutor, // UnboundedExecutor can not be created by &UnboundedExecutor{} // HandlePanic can be set with a callback to override global HandlePanic func NewUnboundedExecutor() *UnboundedExecutor { ctx, cancel := context.WithCancel(context.TODO()) return &UnboundedExecutor{ ctx: ctx, cancel: cancel, activeGoroutinesMutex: &sync.Mutex{}, activeGoroutines: map[string]int{}, } } // Go starts a new goroutine and tracks its lifecycle. // Panic will be recovered and logged automatically, except for StopSignal func (executor *UnboundedExecutor) Go(handler func(ctx context.Context)) { pc := reflect.ValueOf(handler).Pointer() f := runtime.FuncForPC(pc) funcName := f.Name() file, line := f.FileLine(pc) executor.activeGoroutinesMutex.Lock() defer executor.activeGoroutinesMutex.Unlock() startFrom := fmt.Sprintf("%s:%d", file, line) executor.activeGoroutines[startFrom] += 1 go func() { defer func() { recovered := recover() // if you want to quit a goroutine without trigger HandlePanic // use runtime.Goexit() to quit if recovered != nil { if executor.HandlePanic == nil { HandlePanic(recovered, funcName) } else { executor.HandlePanic(recovered, funcName) } } executor.activeGoroutinesMutex.Lock() executor.activeGoroutines[startFrom] -= 1 executor.activeGoroutinesMutex.Unlock() }() handler(executor.ctx) }() } // Stop cancel all goroutines started by this executor without wait func (executor *UnboundedExecutor) Stop() { executor.cancel() } // StopAndWaitForever cancel all goroutines started by this executor and // wait until all goroutines exited func (executor *UnboundedExecutor) StopAndWaitForever() { executor.StopAndWait(context.Background()) } // StopAndWait cancel all goroutines started by this executor and wait. // Wait can be cancelled by the context passed in. func (executor *UnboundedExecutor) StopAndWait(ctx context.Context) { executor.cancel() for { oneHundredMilliseconds := time.NewTimer(time.Millisecond * 100) select { case <-oneHundredMilliseconds.C: if executor.checkNoActiveGoroutines() { return } case <-ctx.Done(): return } } } func (executor *UnboundedExecutor) checkNoActiveGoroutines() bool { executor.activeGoroutinesMutex.Lock() defer executor.activeGoroutinesMutex.Unlock() for startFrom, count := range executor.activeGoroutines { if count > 0 { InfoLogger.Println("UnboundedExecutor is still waiting goroutines to quit", "startFrom", startFrom, "count", count) return false } } return true } ================================================ FILE: vendor/github.com/mohae/deepcopy/.gitignore ================================================ # Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *~ *.out *.log ================================================ FILE: vendor/github.com/mohae/deepcopy/.travis.yml ================================================ language: go go: - tip matrix: allow_failures: - go: tip script: - go test ./... ================================================ FILE: vendor/github.com/mohae/deepcopy/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2014 Joel Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/mohae/deepcopy/README.md ================================================ deepCopy ======== [![GoDoc](https://godoc.org/github.com/mohae/deepcopy?status.svg)](https://godoc.org/github.com/mohae/deepcopy)[![Build Status](https://travis-ci.org/mohae/deepcopy.png)](https://travis-ci.org/mohae/deepcopy) DeepCopy makes deep copies of things: unexported field values are not copied. ## Usage cpy := deepcopy.Copy(orig) ================================================ FILE: vendor/github.com/mohae/deepcopy/deepcopy.go ================================================ // deepcopy makes deep copies of things. A standard copy will copy the // pointers: deep copy copies the values pointed to. Unexported field // values are not copied. // // Copyright (c)2014-2016, Joel Scoble (github.com/mohae), all rights reserved. // License: MIT, for more details check the included LICENSE file. package deepcopy import ( "reflect" "time" ) // Interface for delegating copy process to type type Interface interface { DeepCopy() interface{} } // Iface is an alias to Copy; this exists for backwards compatibility reasons. func Iface(iface interface{}) interface{} { return Copy(iface) } // Copy creates a deep copy of whatever is passed to it and returns the copy // in an interface{}. The returned value will need to be asserted to the // correct type. func Copy(src interface{}) interface{} { if src == nil { return nil } // Make the interface a reflect.Value original := reflect.ValueOf(src) // Make a copy of the same type as the original. cpy := reflect.New(original.Type()).Elem() // Recursively copy the original. copyRecursive(original, cpy) // Return the copy as an interface. return cpy.Interface() } // copyRecursive does the actual copying of the interface. It currently has // limited support for what it can handle. Add as needed. func copyRecursive(original, cpy reflect.Value) { // check for implement deepcopy.Interface if original.CanInterface() { if copier, ok := original.Interface().(Interface); ok { cpy.Set(reflect.ValueOf(copier.DeepCopy())) return } } // handle according to original's Kind switch original.Kind() { case reflect.Ptr: // Get the actual value being pointed to. originalValue := original.Elem() // if it isn't valid, return. if !originalValue.IsValid() { return } cpy.Set(reflect.New(originalValue.Type())) copyRecursive(originalValue, cpy.Elem()) case reflect.Interface: // If this is a nil, don't do anything if original.IsNil() { return } // Get the value for the interface, not the pointer. originalValue := original.Elem() // Get the value by calling Elem(). copyValue := reflect.New(originalValue.Type()).Elem() copyRecursive(originalValue, copyValue) cpy.Set(copyValue) case reflect.Struct: t, ok := original.Interface().(time.Time) if ok { cpy.Set(reflect.ValueOf(t)) return } // Go through each field of the struct and copy it. for i := 0; i < original.NumField(); i++ { // The Type's StructField for a given field is checked to see if StructField.PkgPath // is set to determine if the field is exported or not because CanSet() returns false // for settable fields. I'm not sure why. -mohae if original.Type().Field(i).PkgPath != "" { continue } copyRecursive(original.Field(i), cpy.Field(i)) } case reflect.Slice: if original.IsNil() { return } // Make a new slice and copy each element. cpy.Set(reflect.MakeSlice(original.Type(), original.Len(), original.Cap())) for i := 0; i < original.Len(); i++ { copyRecursive(original.Index(i), cpy.Index(i)) } case reflect.Map: if original.IsNil() { return } cpy.Set(reflect.MakeMap(original.Type())) for _, key := range original.MapKeys() { originalValue := original.MapIndex(key) copyValue := reflect.New(originalValue.Type()).Elem() copyRecursive(originalValue, copyValue) copyKey := Copy(key.Interface()) cpy.SetMapIndex(reflect.ValueOf(copyKey), copyValue) } default: cpy.Set(original) } } ================================================ FILE: vendor/github.com/monochromegane/go-gitignore/.travis.yml ================================================ language: go go: - 1.14.x - master script: - go test -v ./... ================================================ FILE: vendor/github.com/monochromegane/go-gitignore/LICENSE ================================================ The MIT License (MIT) Copyright (c) [2015] [go-gitignore] Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/monochromegane/go-gitignore/README.md ================================================ # go-gitignore [![Build Status](https://travis-ci.org/monochromegane/go-gitignore.svg)](https://travis-ci.org/monochromegane/go-gitignore) A fast gitignore matching library for Go. This library use simple tree index for matching, so keep fast if gitignore file has many pattern. ## Usage ```go gitignore, _ := gitignore.NewGitIgnore("/path/to/gitignore") path := "/path/to/file" isDir := false gitignore.Match(path, isDir) ``` ### Specify base directory go-gitignore treat `path` as a base directory. If you want to specify other base (e.g. current directory and Global gitignore), you can like the following. ```go gitignore, _ := gitignore.NewGitIgnore("/home/you/.gitignore", ".") ``` ### From io.Reader go-gitignore can initialize from io.Reader. ```go gitignore, _ := gitignore.NewGitIgnoreFromReader(base, reader) ``` ## Simple tree index go-gitignore parse gitignore file, and generate a simple tree index for matching like the following. ``` . ├── accept │   ├── absolute │   │   └── depth │   │   ├── initial │   │   └── other │   └── relative │   └── depth │   ├── initial │   └── other └── ignore ├── absolute │   └── depth │   ├── initial │   └── other └── relative └── depth ├── initial └── other ``` ## Features - Support absolute path (/path/to/ignore) - Support relative path (path/to/ignore) - Support accept pattern (!path/to/accept) - Support directory pattern (path/to/directory/) - Support glob pattern (path/to/\*.txt) *note: glob pattern* go-gitignore use [filepath.Match](https://golang.org/pkg/path/filepath/#Match) for matching meta char pattern, so not support recursive pattern (path/`**`/file). ## Installation ```sh $ go get github.com/monochromegane/go-gitignore ``` ## Contribution 1. Fork it 2. Create a feature branch 3. Commit your changes 4. Rebase your local changes against the master branch 5. Run test suite with the `go test ./...` command and confirm that it passes 6. Run `gofmt -s` 7. Create new Pull Request ## License [MIT](https://github.com/monochromegane/go-gitignore/blob/master/LICENSE) ## Author [monochromegane](https://github.com/monochromegane) ================================================ FILE: vendor/github.com/monochromegane/go-gitignore/depth_holder.go ================================================ package gitignore import "strings" const ( asc = iota desc ) type depthPatternHolder struct { patterns depthPatterns order int } func newDepthPatternHolder(order int) depthPatternHolder { return depthPatternHolder{ patterns: depthPatterns{m: map[int]initialPatternHolder{}}, order: order, } } func (h *depthPatternHolder) add(pattern string) { count := strings.Count(strings.Trim(pattern, "/"), "/") h.patterns.set(count+1, pattern) } func (h depthPatternHolder) match(path string, isDir bool) bool { if h.patterns.size() == 0 { return false } for depth := 1; ; depth++ { var part string var isLast, isDirCurrent bool if h.order == asc { part, isLast = cutN(path, depth) if isLast { isDirCurrent = isDir } else { isDirCurrent = false } } else { part, isLast = cutLastN(path, depth) isDirCurrent = isDir } if patterns, ok := h.patterns.get(depth); ok { if patterns.match(part, isDirCurrent) { return true } } if isLast { break } } return false } type depthPatterns struct { m map[int]initialPatternHolder } func (p *depthPatterns) set(depth int, pattern string) { if ps, ok := p.m[depth]; ok { ps.add(pattern) } else { holder := newInitialPatternHolder() holder.add(pattern) p.m[depth] = holder } } func (p depthPatterns) get(depth int) (initialPatternHolder, bool) { patterns, ok := p.m[depth] return patterns, ok } func (p depthPatterns) size() int { return len(p.m) } ================================================ FILE: vendor/github.com/monochromegane/go-gitignore/full_scan_patterns.go ================================================ package gitignore import "strings" // Only benchmark use type fullScanPatterns struct { absolute patterns relative patterns } func newFullScanPatterns() *fullScanPatterns { return &fullScanPatterns{ absolute: patterns{}, relative: patterns{}, } } func (ps *fullScanPatterns) add(pattern string) { if strings.HasPrefix(pattern, "/") { ps.absolute.add(newPattern(pattern)) } else { ps.relative.add(newPattern(pattern)) } } func (ps fullScanPatterns) match(path string, isDir bool) bool { if ps.absolute.match(path, isDir) { return true } return ps.relative.match(path, isDir) } ================================================ FILE: vendor/github.com/monochromegane/go-gitignore/gitignore.go ================================================ package gitignore import ( "bufio" "io" "os" "path/filepath" "strings" ) type IgnoreMatcher interface { Match(path string, isDir bool) bool } type DummyIgnoreMatcher bool func (d DummyIgnoreMatcher) Match(path string, isDir bool) bool { return bool(d) } type gitIgnore struct { ignorePatterns scanStrategy acceptPatterns scanStrategy path string } func NewGitIgnore(gitignore string, base ...string) (IgnoreMatcher, error) { var path string if len(base) > 0 { path = base[0] } else { path = filepath.Dir(gitignore) } file, err := os.Open(gitignore) if err != nil { return nil, err } defer file.Close() return NewGitIgnoreFromReader(path, file), nil } func NewGitIgnoreFromReader(path string, r io.Reader) IgnoreMatcher { g := gitIgnore{ ignorePatterns: newIndexScanPatterns(), acceptPatterns: newIndexScanPatterns(), path: path, } scanner := bufio.NewScanner(r) for scanner.Scan() { line := strings.Trim(scanner.Text(), " ") if len(line) == 0 || strings.HasPrefix(line, "#") { continue } if strings.HasPrefix(line, `\#`) { line = strings.TrimPrefix(line, `\`) } if strings.HasPrefix(line, "!") { g.acceptPatterns.add(strings.TrimPrefix(line, "!")) } else { g.ignorePatterns.add(line) } } return g } func (g gitIgnore) Match(path string, isDir bool) bool { relativePath, err := filepath.Rel(g.path, path) if err != nil { return false } relativePath = filepath.ToSlash(relativePath) if g.acceptPatterns.match(relativePath, isDir) { return false } return g.ignorePatterns.match(relativePath, isDir) } ================================================ FILE: vendor/github.com/monochromegane/go-gitignore/index_scan_patterns.go ================================================ package gitignore import "strings" type indexScanPatterns struct { absolute depthPatternHolder relative depthPatternHolder } func newIndexScanPatterns() *indexScanPatterns { return &indexScanPatterns{ absolute: newDepthPatternHolder(asc), relative: newDepthPatternHolder(desc), } } func (ps *indexScanPatterns) add(pattern string) { if strings.HasPrefix(pattern, "/") { ps.absolute.add(pattern) } else { ps.relative.add(pattern) } } func (ps indexScanPatterns) match(path string, isDir bool) bool { if ps.absolute.match(path, isDir) { return true } return ps.relative.match(path, isDir) } type scanStrategy interface { add(pattern string) match(path string, isDir bool) bool } ================================================ FILE: vendor/github.com/monochromegane/go-gitignore/initial_holder.go ================================================ package gitignore import "strings" const initials = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ." type initialPatternHolder struct { patterns initialPatterns otherPatterns *patterns } func newInitialPatternHolder() initialPatternHolder { return initialPatternHolder{ patterns: initialPatterns{m: map[byte]*patterns{}}, otherPatterns: &patterns{}, } } func (h *initialPatternHolder) add(pattern string) { trimedPattern := strings.TrimPrefix(pattern, "/") if strings.IndexAny(trimedPattern[0:1], initials) != -1 { h.patterns.set(trimedPattern[0], newPatternForEqualizedPath(pattern)) } else { h.otherPatterns.add(newPatternForEqualizedPath(pattern)) } } func (h initialPatternHolder) match(path string, isDir bool) bool { if h.patterns.size() == 0 && h.otherPatterns.size() == 0 { return false } if patterns, ok := h.patterns.get(path[0]); ok { if patterns.match(path, isDir) { return true } } return h.otherPatterns.match(path, isDir) } type initialPatterns struct { m map[byte]*patterns } func (p *initialPatterns) set(initial byte, pattern pattern) { if ps, ok := p.m[initial]; ok { ps.add(pattern) } else { patterns := &patterns{} patterns.add(pattern) p.m[initial] = patterns } } func (p initialPatterns) get(initial byte) (*patterns, bool) { patterns, ok := p.m[initial] return patterns, ok } func (p initialPatterns) size() int { return len(p.m) } ================================================ FILE: vendor/github.com/monochromegane/go-gitignore/match.go ================================================ package gitignore import "path/filepath" type pathMatcher interface { match(path string) bool } type simpleMatcher struct { path string } func (m simpleMatcher) match(path string) bool { return m.path == path } type filepathMatcher struct { path string } func (m filepathMatcher) match(path string) bool { match, _ := filepath.Match(m.path, path) return match } ================================================ FILE: vendor/github.com/monochromegane/go-gitignore/pattern.go ================================================ package gitignore import ( "path/filepath" "strings" ) var Separator = string(filepath.Separator) type pattern struct { hasRootPrefix bool hasDirSuffix bool pathDepth int matcher pathMatcher onlyEqualizedPath bool } func newPattern(path string) pattern { hasRootPrefix := path[0] == '/' hasDirSuffix := path[len(path)-1] == '/' var pathDepth int if !hasRootPrefix { pathDepth = strings.Count(path, "/") } var matcher pathMatcher matchingPath := strings.Trim(path, "/") if hasMeta(path) { matcher = filepathMatcher{path: matchingPath} } else { matcher = simpleMatcher{path: matchingPath} } return pattern{ hasRootPrefix: hasRootPrefix, hasDirSuffix: hasDirSuffix, pathDepth: pathDepth, matcher: matcher, } } func newPatternForEqualizedPath(path string) pattern { pattern := newPattern(path) pattern.onlyEqualizedPath = true return pattern } func (p pattern) match(path string, isDir bool) bool { if p.hasDirSuffix && !isDir { return false } var targetPath string if p.hasRootPrefix || p.onlyEqualizedPath { // absolute pattern or only equalized path mode targetPath = path } else { // relative pattern targetPath = p.equalizeDepth(path) } return p.matcher.match(targetPath) } func (p pattern) equalizeDepth(path string) string { equalizedPath, _ := cutLastN(path, p.pathDepth+1) return equalizedPath } ================================================ FILE: vendor/github.com/monochromegane/go-gitignore/patterns.go ================================================ package gitignore type patterns struct { patterns []pattern } func (ps *patterns) add(pattern pattern) { ps.patterns = append(ps.patterns, pattern) } func (ps *patterns) size() int { return len(ps.patterns) } func (ps patterns) match(path string, isDir bool) bool { for _, p := range ps.patterns { if match := p.match(path, isDir); match { return true } } return false } ================================================ FILE: vendor/github.com/monochromegane/go-gitignore/util.go ================================================ package gitignore import ( "os" "strings" ) func cutN(path string, n int) (string, bool) { isLast := true var i, count int for i < len(path)-1 { if os.IsPathSeparator(path[i]) { count++ if count >= n { isLast = false break } } i++ } return path[:i+1], isLast } func cutLastN(path string, n int) (string, bool) { isLast := true i := len(path) - 1 var count int for i >= 0 { if os.IsPathSeparator(path[i]) { count++ if count >= n { isLast = false break } } i-- } return path[i+1:], isLast } func hasMeta(path string) bool { return strings.IndexAny(path, "*?[") >= 0 } ================================================ FILE: vendor/github.com/morikuni/aec/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2016 Taihei Morikuni Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/morikuni/aec/README.md ================================================ # aec [![GoDoc](https://godoc.org/github.com/morikuni/aec?status.svg)](https://godoc.org/github.com/morikuni/aec) Go wrapper for ANSI escape code. ## Install ```bash go get github.com/morikuni/aec ``` ## Features ANSI escape codes depend on terminal environment. Some of these features may not work. Check supported Font-Style/Font-Color features with [checkansi](./checkansi). [Wikipedia](https://en.wikipedia.org/wiki/ANSI_escape_code) for more detail. ### Cursor - `Up(n)` - `Down(n)` - `Right(n)` - `Left(n)` - `NextLine(n)` - `PreviousLine(n)` - `Column(col)` - `Position(row, col)` - `Save` - `Restore` - `Hide` - `Show` - `Report` ### Erase - `EraseDisplay(mode)` - `EraseLine(mode)` ### Scroll - `ScrollUp(n)` - `ScrollDown(n)` ### Font Style - `Bold` - `Faint` - `Italic` - `Underline` - `BlinkSlow` - `BlinkRapid` - `Inverse` - `Conceal` - `CrossOut` - `Frame` - `Encircle` - `Overline` ### Font Color Foreground color. - `DefaultF` - `BlackF` - `RedF` - `GreenF` - `YellowF` - `BlueF` - `MagentaF` - `CyanF` - `WhiteF` - `LightBlackF` - `LightRedF` - `LightGreenF` - `LightYellowF` - `LightBlueF` - `LightMagentaF` - `LightCyanF` - `LightWhiteF` - `Color3BitF(color)` - `Color8BitF(color)` - `FullColorF(r, g, b)` Background color. - `DefaultB` - `BlackB` - `RedB` - `GreenB` - `YellowB` - `BlueB` - `MagentaB` - `CyanB` - `WhiteB` - `LightBlackB` - `LightRedB` - `LightGreenB` - `LightYellowB` - `LightBlueB` - `LightMagentaB` - `LightCyanB` - `LightWhiteB` - `Color3BitB(color)` - `Color8BitB(color)` - `FullColorB(r, g, b)` ### Color Converter 24bit RGB color to ANSI color. - `NewRGB3Bit(r, g, b)` - `NewRGB8Bit(r, g, b)` ### Builder To mix these features. ```go custom := aec.EmptyBuilder.Right(2).RGB8BitF(128, 255, 64).RedB().ANSI custom.Apply("Hello World") ``` ## Usage 1. Create ANSI by `aec.XXX().With(aec.YYY())` or `aec.EmptyBuilder.XXX().YYY().ANSI` 2. Print ANSI by `fmt.Print(ansi, "some string", aec.Reset)` or `fmt.Print(ansi.Apply("some string"))` `aec.Reset` should be added when using font style or font color features. ## Example Simple progressbar. ![sample](./sample.gif) ```go package main import ( "fmt" "strings" "time" "github.com/morikuni/aec" ) func main() { const n = 20 builder := aec.EmptyBuilder up2 := aec.Up(2) col := aec.Column(n + 2) bar := aec.Color8BitF(aec.NewRGB8Bit(64, 255, 64)) label := builder.LightRedF().Underline().With(col).Right(1).ANSI // for up2 fmt.Println() fmt.Println() for i := 0; i <= n; i++ { fmt.Print(up2) fmt.Println(label.Apply(fmt.Sprint(i, "/", n))) fmt.Print("[") fmt.Print(bar.Apply(strings.Repeat("=", i))) fmt.Println(col.Apply("]")) time.Sleep(100 * time.Millisecond) } } ``` ## License [MIT](./LICENSE) ================================================ FILE: vendor/github.com/morikuni/aec/aec.go ================================================ package aec import "fmt" // EraseMode is listed in a variable EraseModes. type EraseMode uint var ( // EraseModes is a list of EraseMode. EraseModes struct { // All erase all. All EraseMode // Head erase to head. Head EraseMode // Tail erase to tail. Tail EraseMode } // Save saves the cursor position. Save ANSI // Restore restores the cursor position. Restore ANSI // Hide hides the cursor. Hide ANSI // Show shows the cursor. Show ANSI // Report reports the cursor position. Report ANSI ) // Up moves up the cursor. func Up(n uint) ANSI { if n == 0 { return empty } return newAnsi(fmt.Sprintf(esc+"%dA", n)) } // Down moves down the cursor. func Down(n uint) ANSI { if n == 0 { return empty } return newAnsi(fmt.Sprintf(esc+"%dB", n)) } // Right moves right the cursor. func Right(n uint) ANSI { if n == 0 { return empty } return newAnsi(fmt.Sprintf(esc+"%dC", n)) } // Left moves left the cursor. func Left(n uint) ANSI { if n == 0 { return empty } return newAnsi(fmt.Sprintf(esc+"%dD", n)) } // NextLine moves down the cursor to head of a line. func NextLine(n uint) ANSI { if n == 0 { return empty } return newAnsi(fmt.Sprintf(esc+"%dE", n)) } // PreviousLine moves up the cursor to head of a line. func PreviousLine(n uint) ANSI { if n == 0 { return empty } return newAnsi(fmt.Sprintf(esc+"%dF", n)) } // Column set the cursor position to a given column. func Column(col uint) ANSI { return newAnsi(fmt.Sprintf(esc+"%dG", col)) } // Position set the cursor position to a given absolute position. func Position(row, col uint) ANSI { return newAnsi(fmt.Sprintf(esc+"%d;%dH", row, col)) } // EraseDisplay erases display by given EraseMode. func EraseDisplay(m EraseMode) ANSI { return newAnsi(fmt.Sprintf(esc+"%dJ", m)) } // EraseLine erases lines by given EraseMode. func EraseLine(m EraseMode) ANSI { return newAnsi(fmt.Sprintf(esc+"%dK", m)) } // ScrollUp scrolls up the page. func ScrollUp(n int) ANSI { if n == 0 { return empty } return newAnsi(fmt.Sprintf(esc+"%dS", n)) } // ScrollDown scrolls down the page. func ScrollDown(n int) ANSI { if n == 0 { return empty } return newAnsi(fmt.Sprintf(esc+"%dT", n)) } func init() { EraseModes = struct { All EraseMode Head EraseMode Tail EraseMode }{ Tail: 0, Head: 1, All: 2, } Save = newAnsi(esc + "s") Restore = newAnsi(esc + "u") Hide = newAnsi(esc + "?25l") Show = newAnsi(esc + "?25h") Report = newAnsi(esc + "6n") } ================================================ FILE: vendor/github.com/morikuni/aec/ansi.go ================================================ package aec import ( "fmt" "strings" ) const esc = "\x1b[" // Reset resets SGR effect. const Reset string = "\x1b[0m" var empty = newAnsi("") // ANSI represents ANSI escape code. type ANSI interface { fmt.Stringer // With adapts given ANSIs. With(...ANSI) ANSI // Apply wraps given string in ANSI. Apply(string) string } type ansiImpl string func newAnsi(s string) *ansiImpl { r := ansiImpl(s) return &r } func (a *ansiImpl) With(ansi ...ANSI) ANSI { return concat(append([]ANSI{a}, ansi...)) } func (a *ansiImpl) Apply(s string) string { return a.String() + s + Reset } func (a *ansiImpl) String() string { return string(*a) } // Apply wraps given string in ANSIs. func Apply(s string, ansi ...ANSI) string { if len(ansi) == 0 { return s } return concat(ansi).Apply(s) } func concat(ansi []ANSI) ANSI { strs := make([]string, 0, len(ansi)) for _, p := range ansi { strs = append(strs, p.String()) } return newAnsi(strings.Join(strs, "")) } ================================================ FILE: vendor/github.com/morikuni/aec/builder.go ================================================ package aec // Builder is a lightweight syntax to construct customized ANSI. type Builder struct { ANSI ANSI } // EmptyBuilder is an initialized Builder. var EmptyBuilder *Builder // NewBuilder creates a Builder from existing ANSI. func NewBuilder(a ...ANSI) *Builder { return &Builder{concat(a)} } // With is a syntax for With. func (builder *Builder) With(a ...ANSI) *Builder { return NewBuilder(builder.ANSI.With(a...)) } // Up is a syntax for Up. func (builder *Builder) Up(n uint) *Builder { return builder.With(Up(n)) } // Down is a syntax for Down. func (builder *Builder) Down(n uint) *Builder { return builder.With(Down(n)) } // Right is a syntax for Right. func (builder *Builder) Right(n uint) *Builder { return builder.With(Right(n)) } // Left is a syntax for Left. func (builder *Builder) Left(n uint) *Builder { return builder.With(Left(n)) } // NextLine is a syntax for NextLine. func (builder *Builder) NextLine(n uint) *Builder { return builder.With(NextLine(n)) } // PreviousLine is a syntax for PreviousLine. func (builder *Builder) PreviousLine(n uint) *Builder { return builder.With(PreviousLine(n)) } // Column is a syntax for Column. func (builder *Builder) Column(col uint) *Builder { return builder.With(Column(col)) } // Position is a syntax for Position. func (builder *Builder) Position(row, col uint) *Builder { return builder.With(Position(row, col)) } // EraseDisplay is a syntax for EraseDisplay. func (builder *Builder) EraseDisplay(m EraseMode) *Builder { return builder.With(EraseDisplay(m)) } // EraseLine is a syntax for EraseLine. func (builder *Builder) EraseLine(m EraseMode) *Builder { return builder.With(EraseLine(m)) } // ScrollUp is a syntax for ScrollUp. func (builder *Builder) ScrollUp(n int) *Builder { return builder.With(ScrollUp(n)) } // ScrollDown is a syntax for ScrollDown. func (builder *Builder) ScrollDown(n int) *Builder { return builder.With(ScrollDown(n)) } // Save is a syntax for Save. func (builder *Builder) Save() *Builder { return builder.With(Save) } // Restore is a syntax for Restore. func (builder *Builder) Restore() *Builder { return builder.With(Restore) } // Hide is a syntax for Hide. func (builder *Builder) Hide() *Builder { return builder.With(Hide) } // Show is a syntax for Show. func (builder *Builder) Show() *Builder { return builder.With(Show) } // Report is a syntax for Report. func (builder *Builder) Report() *Builder { return builder.With(Report) } // Bold is a syntax for Bold. func (builder *Builder) Bold() *Builder { return builder.With(Bold) } // Faint is a syntax for Faint. func (builder *Builder) Faint() *Builder { return builder.With(Faint) } // Italic is a syntax for Italic. func (builder *Builder) Italic() *Builder { return builder.With(Italic) } // Underline is a syntax for Underline. func (builder *Builder) Underline() *Builder { return builder.With(Underline) } // BlinkSlow is a syntax for BlinkSlow. func (builder *Builder) BlinkSlow() *Builder { return builder.With(BlinkSlow) } // BlinkRapid is a syntax for BlinkRapid. func (builder *Builder) BlinkRapid() *Builder { return builder.With(BlinkRapid) } // Inverse is a syntax for Inverse. func (builder *Builder) Inverse() *Builder { return builder.With(Inverse) } // Conceal is a syntax for Conceal. func (builder *Builder) Conceal() *Builder { return builder.With(Conceal) } // CrossOut is a syntax for CrossOut. func (builder *Builder) CrossOut() *Builder { return builder.With(CrossOut) } // BlackF is a syntax for BlackF. func (builder *Builder) BlackF() *Builder { return builder.With(BlackF) } // RedF is a syntax for RedF. func (builder *Builder) RedF() *Builder { return builder.With(RedF) } // GreenF is a syntax for GreenF. func (builder *Builder) GreenF() *Builder { return builder.With(GreenF) } // YellowF is a syntax for YellowF. func (builder *Builder) YellowF() *Builder { return builder.With(YellowF) } // BlueF is a syntax for BlueF. func (builder *Builder) BlueF() *Builder { return builder.With(BlueF) } // MagentaF is a syntax for MagentaF. func (builder *Builder) MagentaF() *Builder { return builder.With(MagentaF) } // CyanF is a syntax for CyanF. func (builder *Builder) CyanF() *Builder { return builder.With(CyanF) } // WhiteF is a syntax for WhiteF. func (builder *Builder) WhiteF() *Builder { return builder.With(WhiteF) } // DefaultF is a syntax for DefaultF. func (builder *Builder) DefaultF() *Builder { return builder.With(DefaultF) } // BlackB is a syntax for BlackB. func (builder *Builder) BlackB() *Builder { return builder.With(BlackB) } // RedB is a syntax for RedB. func (builder *Builder) RedB() *Builder { return builder.With(RedB) } // GreenB is a syntax for GreenB. func (builder *Builder) GreenB() *Builder { return builder.With(GreenB) } // YellowB is a syntax for YellowB. func (builder *Builder) YellowB() *Builder { return builder.With(YellowB) } // BlueB is a syntax for BlueB. func (builder *Builder) BlueB() *Builder { return builder.With(BlueB) } // MagentaB is a syntax for MagentaB. func (builder *Builder) MagentaB() *Builder { return builder.With(MagentaB) } // CyanB is a syntax for CyanB. func (builder *Builder) CyanB() *Builder { return builder.With(CyanB) } // WhiteB is a syntax for WhiteB. func (builder *Builder) WhiteB() *Builder { return builder.With(WhiteB) } // DefaultB is a syntax for DefaultB. func (builder *Builder) DefaultB() *Builder { return builder.With(DefaultB) } // Frame is a syntax for Frame. func (builder *Builder) Frame() *Builder { return builder.With(Frame) } // Encircle is a syntax for Encircle. func (builder *Builder) Encircle() *Builder { return builder.With(Encircle) } // Overline is a syntax for Overline. func (builder *Builder) Overline() *Builder { return builder.With(Overline) } // LightBlackF is a syntax for LightBlueF. func (builder *Builder) LightBlackF() *Builder { return builder.With(LightBlackF) } // LightRedF is a syntax for LightRedF. func (builder *Builder) LightRedF() *Builder { return builder.With(LightRedF) } // LightGreenF is a syntax for LightGreenF. func (builder *Builder) LightGreenF() *Builder { return builder.With(LightGreenF) } // LightYellowF is a syntax for LightYellowF. func (builder *Builder) LightYellowF() *Builder { return builder.With(LightYellowF) } // LightBlueF is a syntax for LightBlueF. func (builder *Builder) LightBlueF() *Builder { return builder.With(LightBlueF) } // LightMagentaF is a syntax for LightMagentaF. func (builder *Builder) LightMagentaF() *Builder { return builder.With(LightMagentaF) } // LightCyanF is a syntax for LightCyanF. func (builder *Builder) LightCyanF() *Builder { return builder.With(LightCyanF) } // LightWhiteF is a syntax for LightWhiteF. func (builder *Builder) LightWhiteF() *Builder { return builder.With(LightWhiteF) } // LightBlackB is a syntax for LightBlackB. func (builder *Builder) LightBlackB() *Builder { return builder.With(LightBlackB) } // LightRedB is a syntax for LightRedB. func (builder *Builder) LightRedB() *Builder { return builder.With(LightRedB) } // LightGreenB is a syntax for LightGreenB. func (builder *Builder) LightGreenB() *Builder { return builder.With(LightGreenB) } // LightYellowB is a syntax for LightYellowB. func (builder *Builder) LightYellowB() *Builder { return builder.With(LightYellowB) } // LightBlueB is a syntax for LightBlueB. func (builder *Builder) LightBlueB() *Builder { return builder.With(LightBlueB) } // LightMagentaB is a syntax for LightMagentaB. func (builder *Builder) LightMagentaB() *Builder { return builder.With(LightMagentaB) } // LightCyanB is a syntax for LightCyanB. func (builder *Builder) LightCyanB() *Builder { return builder.With(LightCyanB) } // LightWhiteB is a syntax for LightWhiteB. func (builder *Builder) LightWhiteB() *Builder { return builder.With(LightWhiteB) } // Color3BitF is a syntax for Color3BitF. func (builder *Builder) Color3BitF(c RGB3Bit) *Builder { return builder.With(Color3BitF(c)) } // Color3BitB is a syntax for Color3BitB. func (builder *Builder) Color3BitB(c RGB3Bit) *Builder { return builder.With(Color3BitB(c)) } // Color8BitF is a syntax for Color8BitF. func (builder *Builder) Color8BitF(c RGB8Bit) *Builder { return builder.With(Color8BitF(c)) } // Color8BitB is a syntax for Color8BitB. func (builder *Builder) Color8BitB(c RGB8Bit) *Builder { return builder.With(Color8BitB(c)) } // FullColorF is a syntax for FullColorF. func (builder *Builder) FullColorF(r, g, b uint8) *Builder { return builder.With(FullColorF(r, g, b)) } // FullColorB is a syntax for FullColorB. func (builder *Builder) FullColorB(r, g, b uint8) *Builder { return builder.With(FullColorB(r, g, b)) } // RGB3BitF is a syntax for Color3BitF with NewRGB3Bit. func (builder *Builder) RGB3BitF(r, g, b uint8) *Builder { return builder.Color3BitF(NewRGB3Bit(r, g, b)) } // RGB3BitB is a syntax for Color3BitB with NewRGB3Bit. func (builder *Builder) RGB3BitB(r, g, b uint8) *Builder { return builder.Color3BitB(NewRGB3Bit(r, g, b)) } // RGB8BitF is a syntax for Color8BitF with NewRGB8Bit. func (builder *Builder) RGB8BitF(r, g, b uint8) *Builder { return builder.Color8BitF(NewRGB8Bit(r, g, b)) } // RGB8BitB is a syntax for Color8BitB with NewRGB8Bit. func (builder *Builder) RGB8BitB(r, g, b uint8) *Builder { return builder.Color8BitB(NewRGB8Bit(r, g, b)) } func init() { EmptyBuilder = &Builder{empty} } ================================================ FILE: vendor/github.com/morikuni/aec/sgr.go ================================================ package aec import ( "fmt" ) // RGB3Bit is a 3bit RGB color. type RGB3Bit uint8 // RGB8Bit is a 8bit RGB color. type RGB8Bit uint8 func newSGR(n uint) ANSI { return newAnsi(fmt.Sprintf(esc+"%dm", n)) } // NewRGB3Bit create a RGB3Bit from given RGB. func NewRGB3Bit(r, g, b uint8) RGB3Bit { return RGB3Bit((r >> 7) | ((g >> 6) & 0x2) | ((b >> 5) & 0x4)) } // NewRGB8Bit create a RGB8Bit from given RGB. func NewRGB8Bit(r, g, b uint8) RGB8Bit { return RGB8Bit(16 + 36*(r/43) + 6*(g/43) + b/43) } // Color3BitF set the foreground color of text. func Color3BitF(c RGB3Bit) ANSI { return newAnsi(fmt.Sprintf(esc+"%dm", c+30)) } // Color3BitB set the background color of text. func Color3BitB(c RGB3Bit) ANSI { return newAnsi(fmt.Sprintf(esc+"%dm", c+40)) } // Color8BitF set the foreground color of text. func Color8BitF(c RGB8Bit) ANSI { return newAnsi(fmt.Sprintf(esc+"38;5;%dm", c)) } // Color8BitB set the background color of text. func Color8BitB(c RGB8Bit) ANSI { return newAnsi(fmt.Sprintf(esc+"48;5;%dm", c)) } // FullColorF set the foreground color of text. func FullColorF(r, g, b uint8) ANSI { return newAnsi(fmt.Sprintf(esc+"38;2;%d;%d;%dm", r, g, b)) } // FullColorB set the foreground color of text. func FullColorB(r, g, b uint8) ANSI { return newAnsi(fmt.Sprintf(esc+"48;2;%d;%d;%dm", r, g, b)) } // Style var ( // Bold set the text style to bold or increased intensity. Bold ANSI // Faint set the text style to faint. Faint ANSI // Italic set the text style to italic. Italic ANSI // Underline set the text style to underline. Underline ANSI // BlinkSlow set the text style to slow blink. BlinkSlow ANSI // BlinkRapid set the text style to rapid blink. BlinkRapid ANSI // Inverse swap the foreground color and background color. Inverse ANSI // Conceal set the text style to conceal. Conceal ANSI // CrossOut set the text style to crossed out. CrossOut ANSI // Frame set the text style to framed. Frame ANSI // Encircle set the text style to encircled. Encircle ANSI // Overline set the text style to overlined. Overline ANSI ) // Foreground color of text. var ( // DefaultF is the default color of foreground. DefaultF ANSI // Normal color BlackF ANSI RedF ANSI GreenF ANSI YellowF ANSI BlueF ANSI MagentaF ANSI CyanF ANSI WhiteF ANSI // Light color LightBlackF ANSI LightRedF ANSI LightGreenF ANSI LightYellowF ANSI LightBlueF ANSI LightMagentaF ANSI LightCyanF ANSI LightWhiteF ANSI ) // Background color of text. var ( // DefaultB is the default color of background. DefaultB ANSI // Normal color BlackB ANSI RedB ANSI GreenB ANSI YellowB ANSI BlueB ANSI MagentaB ANSI CyanB ANSI WhiteB ANSI // Light color LightBlackB ANSI LightRedB ANSI LightGreenB ANSI LightYellowB ANSI LightBlueB ANSI LightMagentaB ANSI LightCyanB ANSI LightWhiteB ANSI ) func init() { Bold = newSGR(1) Faint = newSGR(2) Italic = newSGR(3) Underline = newSGR(4) BlinkSlow = newSGR(5) BlinkRapid = newSGR(6) Inverse = newSGR(7) Conceal = newSGR(8) CrossOut = newSGR(9) BlackF = newSGR(30) RedF = newSGR(31) GreenF = newSGR(32) YellowF = newSGR(33) BlueF = newSGR(34) MagentaF = newSGR(35) CyanF = newSGR(36) WhiteF = newSGR(37) DefaultF = newSGR(39) BlackB = newSGR(40) RedB = newSGR(41) GreenB = newSGR(42) YellowB = newSGR(43) BlueB = newSGR(44) MagentaB = newSGR(45) CyanB = newSGR(46) WhiteB = newSGR(47) DefaultB = newSGR(49) Frame = newSGR(51) Encircle = newSGR(52) Overline = newSGR(53) LightBlackF = newSGR(90) LightRedF = newSGR(91) LightGreenF = newSGR(92) LightYellowF = newSGR(93) LightBlueF = newSGR(94) LightMagentaF = newSGR(95) LightCyanF = newSGR(96) LightWhiteF = newSGR(97) LightBlackB = newSGR(100) LightRedB = newSGR(101) LightGreenB = newSGR(102) LightYellowB = newSGR(103) LightBlueB = newSGR(104) LightMagentaB = newSGR(105) LightCyanB = newSGR(106) LightWhiteB = newSGR(107) } ================================================ FILE: vendor/github.com/munnerz/goautoneg/LICENSE ================================================ Copyright (c) 2011, Open Knowledge Foundation Ltd. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the Open Knowledge Foundation Ltd. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/munnerz/goautoneg/Makefile ================================================ include $(GOROOT)/src/Make.inc TARG=bitbucket.org/ww/goautoneg GOFILES=autoneg.go include $(GOROOT)/src/Make.pkg format: gofmt -w *.go docs: gomake clean godoc ${TARG} > README.txt ================================================ FILE: vendor/github.com/munnerz/goautoneg/README.txt ================================================ PACKAGE package goautoneg import "bitbucket.org/ww/goautoneg" HTTP Content-Type Autonegotiation. The functions in this package implement the behaviour specified in http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html Copyright (c) 2011, Open Knowledge Foundation Ltd. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the Open Knowledge Foundation Ltd. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. FUNCTIONS func Negotiate(header string, alternatives []string) (content_type string) Negotiate the most appropriate content_type given the accept header and a list of alternatives. func ParseAccept(header string) (accept []Accept) Parse an Accept Header string returning a sorted list of clauses TYPES type Accept struct { Type, SubType string Q float32 Params map[string]string } Structure to represent a clause in an HTTP Accept Header SUBDIRECTORIES .hg ================================================ FILE: vendor/github.com/munnerz/goautoneg/autoneg.go ================================================ /* HTTP Content-Type Autonegotiation. The functions in this package implement the behaviour specified in http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html Copyright (c) 2011, Open Knowledge Foundation Ltd. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the Open Knowledge Foundation Ltd. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package goautoneg import ( "sort" "strconv" "strings" ) // Structure to represent a clause in an HTTP Accept Header type Accept struct { Type, SubType string Q float64 Params map[string]string } // acceptSlice is defined to implement sort interface. type acceptSlice []Accept func (slice acceptSlice) Len() int { return len(slice) } func (slice acceptSlice) Less(i, j int) bool { ai, aj := slice[i], slice[j] if ai.Q > aj.Q { return true } if ai.Type != "*" && aj.Type == "*" { return true } if ai.SubType != "*" && aj.SubType == "*" { return true } return false } func (slice acceptSlice) Swap(i, j int) { slice[i], slice[j] = slice[j], slice[i] } func stringTrimSpaceCutset(r rune) bool { return r == ' ' } func nextSplitElement(s, sep string) (item string, remaining string) { if index := strings.Index(s, sep); index != -1 { return s[:index], s[index+1:] } return s, "" } // Parse an Accept Header string returning a sorted list // of clauses func ParseAccept(header string) acceptSlice { partsCount := 0 remaining := header for len(remaining) > 0 { partsCount++ _, remaining = nextSplitElement(remaining, ",") } accept := make(acceptSlice, 0, partsCount) remaining = header var part string for len(remaining) > 0 { part, remaining = nextSplitElement(remaining, ",") part = strings.TrimFunc(part, stringTrimSpaceCutset) a := Accept{ Q: 1.0, } sp, remainingPart := nextSplitElement(part, ";") sp0, spRemaining := nextSplitElement(sp, "/") a.Type = strings.TrimFunc(sp0, stringTrimSpaceCutset) switch { case len(spRemaining) == 0: if a.Type == "*" { a.SubType = "*" } else { continue } default: var sp1 string sp1, spRemaining = nextSplitElement(spRemaining, "/") if len(spRemaining) > 0 { continue } a.SubType = strings.TrimFunc(sp1, stringTrimSpaceCutset) } if len(remainingPart) == 0 { accept = append(accept, a) continue } a.Params = make(map[string]string) for len(remainingPart) > 0 { sp, remainingPart = nextSplitElement(remainingPart, ";") sp0, spRemaining = nextSplitElement(sp, "=") if len(spRemaining) == 0 { continue } var sp1 string sp1, spRemaining = nextSplitElement(spRemaining, "=") if len(spRemaining) != 0 { continue } token := strings.TrimFunc(sp0, stringTrimSpaceCutset) if token == "q" { a.Q, _ = strconv.ParseFloat(sp1, 32) } else { a.Params[token] = strings.TrimFunc(sp1, stringTrimSpaceCutset) } } accept = append(accept, a) } sort.Sort(accept) return accept } // Negotiate the most appropriate content_type given the accept header // and a list of alternatives. func Negotiate(header string, alternatives []string) (content_type string) { asp := make([][]string, 0, len(alternatives)) for _, ctype := range alternatives { asp = append(asp, strings.SplitN(ctype, "/", 2)) } for _, clause := range ParseAccept(header) { for i, ctsp := range asp { if clause.Type == ctsp[0] && clause.SubType == ctsp[1] { content_type = alternatives[i] return } if clause.Type == ctsp[0] && clause.SubType == "*" { content_type = alternatives[i] return } if clause.Type == "*" && clause.SubType == "*" { content_type = alternatives[i] return } } } return } ================================================ FILE: vendor/github.com/oasdiff/yaml/.gitignore ================================================ # OSX leaves these everywhere on SMB shares ._* # Eclipse files .classpath .project .settings/** # Emacs save files *~ # Vim-related files [._]*.s[a-w][a-z] [._]s[a-w][a-z] *.un~ Session.vim .netrwhist # Go test binaries *.test ================================================ FILE: vendor/github.com/oasdiff/yaml/.golangci.toml ================================================ [run] timeout = "120s" [output] format = "colored-line-number" [linters] enable = [ "gocyclo", "unconvert", "goimports", "unused", "unused", "vetshadow", "nakedret", "errcheck", "revive", "ineffassign", "goconst", "vet", "unparam", "gofmt" ] [issues] exclude-use-default = false ================================================ FILE: vendor/github.com/oasdiff/yaml/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2014 Sam Ghods Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Copyright (c) 2012 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/oasdiff/yaml/README.md ================================================ # YAML marshaling and unmarshaling support for Go [![Lint](https://github.com/invopop/yaml/actions/workflows/lint.yaml/badge.svg)](https://github.com/invopop/yaml/actions/workflows/lint.yaml) [![Test Go](https://github.com/invopop/yaml/actions/workflows/test.yaml/badge.svg)](https://github.com/invopop/yaml/actions/workflows/test.yaml) [![Go Report Card](https://goreportcard.com/badge/github.com/invopop/yaml)](https://goreportcard.com/report/github.com/invopop/yaml) ![Latest Tag](https://img.shields.io/github/v/tag/invopop/yaml) ## Fork This fork is an improved version of the invopop/yaml package, designed to include line and column location information for YAML elements during unmarshalling. To include location information use ```UnmarshalWithOrigin``` instead of ```Unmarshal```. The heavy lifting is done by the underlying [oasdiff/yaml3](https://github.com/oasdiff/yaml3) package. ## Introduction A wrapper around [go-yaml](https://github.com/go-yaml/yaml) designed to enable a better way of handling YAML when marshaling to and from structs. This is a fork and split of the original [ghodss/yaml](https://github.com/ghodss/yaml) repository which no longer appears to be maintained. In short, this library first converts YAML to JSON using go-yaml and then uses `json.Marshal` and `json.Unmarshal` to convert to or from the struct. This means that it effectively reuses the JSON struct tags as well as the custom JSON methods `MarshalJSON` and `UnmarshalJSON` unlike go-yaml. For a detailed overview of the rationale behind this method, [see this blog post](https://web.archive.org/web/20150812020634/http://ghodss.com/2014/the-right-way-to-handle-yaml-in-golang/). ## Compatibility This package uses [go-yaml](https://github.com/go-yaml/yaml) and therefore supports [everything go-yaml supports](https://github.com/go-yaml/yaml#compatibility). Tested against Go versions 1.14 and onwards. ## Caveats **Caveat #1:** When using `yaml.Marshal` and `yaml.Unmarshal`, binary data should NOT be preceded with the `!!binary` YAML tag. If you do, go-yaml will convert the binary data from base64 to native binary data, which is not compatible with JSON. You can still use binary in your YAML files though - just store them without the `!!binary` tag and decode the base64 in your code (e.g. in the custom JSON methods `MarshalJSON` and `UnmarshalJSON`). This also has the benefit that your YAML and your JSON binary data will be decoded exactly the same way. As an example: ``` BAD: exampleKey: !!binary gIGC GOOD: exampleKey: gIGC ... and decode the base64 data in your code. ``` **Caveat #2:** When using `YAMLToJSON` directly, maps with keys that are maps will result in an error since this is not supported by JSON. This error will occur in `Unmarshal` as well since you can't unmarshal map keys anyways since struct fields can't be keys. ## Installation and usage To install, run: ``` $ go get github.com/invopop/yaml ``` And import using: ``` import "github.com/invopop/yaml" ``` Usage is very similar to the JSON library: ```go package main import ( "fmt" "github.com/invopop/yaml" ) type Person struct { Name string `json:"name"` // Affects YAML field names too. Age int `json:"age"` } func main() { // Marshal a Person struct to YAML. p := Person{"John", 30} y, err := yaml.Marshal(p) if err != nil { fmt.Printf("err: %v\n", err) return } fmt.Println(string(y)) /* Output: age: 30 name: John */ // Unmarshal the YAML back into a Person struct. var p2 Person err = yaml.Unmarshal(y, &p2) if err != nil { fmt.Printf("err: %v\n", err) return } fmt.Println(p2) /* Output: {John 30} */ } ``` `yaml.YAMLToJSON` and `yaml.JSONToYAML` methods are also available: ```go package main import ( "fmt" "github.com/invopop/yaml" ) func main() { j := []byte(`{"name": "John", "age": 30}`) y, err := yaml.JSONToYAML(j) if err != nil { fmt.Printf("err: %v\n", err) return } fmt.Println(string(y)) /* Output: name: John age: 30 */ j2, err := yaml.YAMLToJSON(y) if err != nil { fmt.Printf("err: %v\n", err) return } fmt.Println(string(j2)) /* Output: {"age":30,"name":"John"} */ } ``` ================================================ FILE: vendor/github.com/oasdiff/yaml/fields.go ================================================ // Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package yaml import ( "bytes" "encoding" "encoding/json" "reflect" "sort" "strings" "sync" "unicode" "unicode/utf8" ) // indirect walks down v allocating pointers as needed, // until it gets to a non-pointer. // if it encounters an Unmarshaler, indirect stops and returns that. // if decodingNull is true, indirect stops at the last pointer so it can be set to nil. func indirect(v reflect.Value, decodingNull bool) (json.Unmarshaler, encoding.TextUnmarshaler, reflect.Value) { // If v is a named type and is addressable, // start with its address, so that if the type has pointer methods, // we find them. if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() { v = v.Addr() } for { // Load value from interface, but only if the result will be // usefully addressable. if v.Kind() == reflect.Interface && !v.IsNil() { e := v.Elem() if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) { v = e continue } } if v.Kind() != reflect.Ptr { break } if v.Elem().Kind() != reflect.Ptr && decodingNull && v.CanSet() { break } if v.IsNil() { if v.CanSet() { v.Set(reflect.New(v.Type().Elem())) } else { v = reflect.New(v.Type().Elem()) } } if v.Type().NumMethod() > 0 { if u, ok := v.Interface().(json.Unmarshaler); ok { return u, nil, reflect.Value{} } if u, ok := v.Interface().(encoding.TextUnmarshaler); ok { return nil, u, reflect.Value{} } } v = v.Elem() } return nil, nil, v } // A field represents a single field found in a struct. type field struct { name string nameBytes []byte // []byte(name) equalFold func(s, t []byte) bool // bytes.EqualFold or equivalent tag bool index []int typ reflect.Type omitEmpty bool quoted bool } func fillField(f field) field { f.nameBytes = []byte(f.name) f.equalFold = foldFunc(f.nameBytes) return f } // byName sorts field by name, breaking ties with depth, // then breaking ties with "name came from json tag", then // breaking ties with index sequence. type byName []field func (x byName) Len() int { return len(x) } func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x byName) Less(i, j int) bool { if x[i].name != x[j].name { return x[i].name < x[j].name } if len(x[i].index) != len(x[j].index) { return len(x[i].index) < len(x[j].index) } if x[i].tag != x[j].tag { return x[i].tag } return byIndex(x).Less(i, j) } // byIndex sorts field by index sequence. type byIndex []field func (x byIndex) Len() int { return len(x) } func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x byIndex) Less(i, j int) bool { for k, xik := range x[i].index { if k >= len(x[j].index) { return false } if xik != x[j].index[k] { return xik < x[j].index[k] } } return len(x[i].index) < len(x[j].index) } // typeFields returns a list of fields that JSON should recognize for the given type. // The algorithm is breadth-first search over the set of structs to include - the top struct // and then any reachable anonymous structs. func typeFields(t reflect.Type) []field { // Anonymous fields to explore at the current level and the next. current := []field{} next := []field{{typ: t}} // Count of queued names for current level and the next. var count, nextCount map[reflect.Type]int // Types already visited at an earlier level. visited := map[reflect.Type]bool{} // Fields found. var fields []field for len(next) > 0 { current, next = next, current[:0] count, nextCount = nextCount, map[reflect.Type]int{} for _, f := range current { if visited[f.typ] { continue } visited[f.typ] = true // Scan f.typ for fields to include. for i := 0; i < f.typ.NumField(); i++ { sf := f.typ.Field(i) if sf.PkgPath != "" { // unexported continue } tag := sf.Tag.Get("json") if tag == "-" { continue } name, opts := parseTag(tag) if !isValidTag(name) { name = "" } index := make([]int, len(f.index)+1) copy(index, f.index) index[len(f.index)] = i ft := sf.Type if ft.Name() == "" && ft.Kind() == reflect.Ptr { // Follow pointer. ft = ft.Elem() } // Record found field and index sequence. if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct { tagged := name != "" if name == "" { name = sf.Name } fields = append(fields, fillField(field{ name: name, tag: tagged, index: index, typ: ft, omitEmpty: opts.Contains("omitempty"), quoted: opts.Contains("string"), })) if count[f.typ] > 1 { // If there were multiple instances, add a second, // so that the annihilation code will see a duplicate. // It only cares about the distinction between 1 or 2, // so don't bother generating any more copies. fields = append(fields, fields[len(fields)-1]) } continue } // Record new anonymous struct to explore in next round. nextCount[ft]++ if nextCount[ft] == 1 { next = append(next, fillField(field{name: ft.Name(), index: index, typ: ft})) } } } } sort.Sort(byName(fields)) // Delete all fields that are hidden by the Go rules for embedded fields, // except that fields with JSON tags are promoted. // The fields are sorted in primary order of name, secondary order // of field index length. Loop over names; for each name, delete // hidden fields by choosing the one dominant field that survives. out := fields[:0] for advance, i := 0, 0; i < len(fields); i += advance { // One iteration per name. // Find the sequence of fields with the name of this first field. fi := fields[i] name := fi.name for advance = 1; i+advance < len(fields); advance++ { fj := fields[i+advance] if fj.name != name { break } } if advance == 1 { // Only one field with this name out = append(out, fi) continue } dominant, ok := dominantField(fields[i : i+advance]) if ok { out = append(out, dominant) } } fields = out sort.Sort(byIndex(fields)) return fields } // dominantField looks through the fields, all of which are known to // have the same name, to find the single field that dominates the // others using Go's embedding rules, modified by the presence of // JSON tags. If there are multiple top-level fields, the boolean // will be false: This condition is an error in Go and we skip all // the fields. func dominantField(fields []field) (field, bool) { // The fields are sorted in increasing index-length order. The winner // must therefore be one with the shortest index length. Drop all // longer entries, which is easy: just truncate the slice. length := len(fields[0].index) tagged := -1 // Index of first tagged field. for i, f := range fields { if len(f.index) > length { fields = fields[:i] break } if f.tag { if tagged >= 0 { // Multiple tagged fields at the same level: conflict. // Return no field. return field{}, false } tagged = i } } if tagged >= 0 { return fields[tagged], true } // All remaining fields have the same length. If there's more than one, // we have a conflict (two fields named "X" at the same level) and we // return no field. if len(fields) > 1 { return field{}, false } return fields[0], true } var fieldCache struct { sync.RWMutex m map[reflect.Type][]field } // cachedTypeFields is like typeFields but uses a cache to avoid repeated work. func cachedTypeFields(t reflect.Type) []field { fieldCache.RLock() f := fieldCache.m[t] fieldCache.RUnlock() if f != nil { return f } // Compute fields without lock. // Might duplicate effort but won't hold other computations back. f = typeFields(t) if f == nil { f = []field{} } fieldCache.Lock() if fieldCache.m == nil { fieldCache.m = map[reflect.Type][]field{} } fieldCache.m[t] = f fieldCache.Unlock() return f } func isValidTag(s string) bool { if s == "" { return false } for _, c := range s { switch { case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c): // Backslash and quote chars are reserved, but // otherwise any punctuation chars are allowed // in a tag name. default: if !unicode.IsLetter(c) && !unicode.IsDigit(c) { return false } } } return true } const ( caseMask = ^byte(0x20) // Mask to ignore case in ASCII. kelvin = '\u212a' smallLongEss = '\u017f' ) // foldFunc returns one of four different case folding equivalence // functions, from most general (and slow) to fastest: // // 1) bytes.EqualFold, if the key s contains any non-ASCII UTF-8 // 2) equalFoldRight, if s contains special folding ASCII ('k', 'K', 's', 'S') // 3) asciiEqualFold, no special, but includes non-letters (including _) // 4) simpleLetterEqualFold, no specials, no non-letters. // // The letters S and K are special because they map to 3 runes, not just 2: // - S maps to s and to U+017F 'ſ' Latin small letter long s // - k maps to K and to U+212A 'K' Kelvin sign // // See http://play.golang.org/p/tTxjOc0OGo // // The returned function is specialized for matching against s and // should only be given s. It's not curried for performance reasons. func foldFunc(s []byte) func(s, t []byte) bool { nonLetter := false special := false // special letter for _, b := range s { if b >= utf8.RuneSelf { return bytes.EqualFold } upper := b & caseMask if upper < 'A' || upper > 'Z' { nonLetter = true } else if upper == 'K' || upper == 'S' { // See above for why these letters are special. special = true } } if special { return equalFoldRight } if nonLetter { return asciiEqualFold } return simpleLetterEqualFold } // equalFoldRight is a specialization of bytes.EqualFold when s is // known to be all ASCII (including punctuation), but contains an 's', // 'S', 'k', or 'K', requiring a Unicode fold on the bytes in t. // See comments on foldFunc. func equalFoldRight(s, t []byte) bool { for _, sb := range s { if len(t) == 0 { return false } tb := t[0] if tb < utf8.RuneSelf { if sb != tb { sbUpper := sb & caseMask if 'A' <= sbUpper && sbUpper <= 'Z' { if sbUpper != tb&caseMask { return false } } else { return false } } t = t[1:] continue } // sb is ASCII and t is not. t must be either kelvin // sign or long s; sb must be s, S, k, or K. tr, size := utf8.DecodeRune(t) switch sb { case 's', 'S': if tr != smallLongEss { return false } case 'k', 'K': if tr != kelvin { return false } default: return false } t = t[size:] } return len(t) <= 0 } // asciiEqualFold is a specialization of bytes.EqualFold for use when // s is all ASCII (but may contain non-letters) and contains no // special-folding letters. // See comments on foldFunc. func asciiEqualFold(s, t []byte) bool { if len(s) != len(t) { return false } for i, sb := range s { tb := t[i] if sb == tb { continue } if ('a' <= sb && sb <= 'z') || ('A' <= sb && sb <= 'Z') { if sb&caseMask != tb&caseMask { return false } } else { return false } } return true } // simpleLetterEqualFold is a specialization of bytes.EqualFold for // use when s is all ASCII letters (no underscores, etc) and also // doesn't contain 'k', 'K', 's', or 'S'. // See comments on foldFunc. func simpleLetterEqualFold(s, t []byte) bool { if len(s) != len(t) { return false } for i, b := range s { if b&caseMask != t[i]&caseMask { return false } } return true } // tagOptions is the string following a comma in a struct field's "json" // tag, or the empty string. It does not include the leading comma. type tagOptions string // parseTag splits a struct field's json tag into its name and // comma-separated options. func parseTag(tag string) (string, tagOptions) { if idx := strings.Index(tag, ","); idx != -1 { return tag[:idx], tagOptions(tag[idx+1:]) } return tag, tagOptions("") } // Contains reports whether a comma-separated list of options // contains a particular substr flag. substr must be surrounded by a // string boundary or commas. func (o tagOptions) Contains(optionName string) bool { if len(o) == 0 { return false } s := string(o) for s != "" { var next string i := strings.Index(s, ",") if i >= 0 { s, next = s[:i], s[i+1:] } if s == optionName { return true } s = next } return false } ================================================ FILE: vendor/github.com/oasdiff/yaml/yaml.go ================================================ // Package yaml provides a wrapper around go-yaml designed to enable a better // way of handling YAML when marshaling to and from structs. // // In short, this package first converts YAML to JSON using go-yaml and then // uses json.Marshal and json.Unmarshal to convert to or from the struct. This // means that it effectively reuses the JSON struct tags as well as the custom // JSON methods MarshalJSON and UnmarshalJSON unlike go-yaml. package yaml // import "github.com/invopop/yaml" import ( "bytes" "encoding/json" "errors" "fmt" "io" "reflect" "strconv" "github.com/oasdiff/yaml3" ) // Marshal the object into JSON then converts JSON to YAML and returns the // YAML. func Marshal(o interface{}) ([]byte, error) { j, err := json.Marshal(o) if err != nil { return nil, fmt.Errorf("error marshaling into JSON: %v", err) } y, err := JSONToYAML(j) if err != nil { return nil, fmt.Errorf("error converting JSON to YAML: %v", err) } return y, nil } // JSONOpt is a decoding option for decoding from JSON format. type JSONOpt func(*json.Decoder) *json.Decoder // YAMLOpt is a decoding option for decoding from YAML format. type YAMLOpt func(*yaml.Decoder) *yaml.Decoder // Unmarshal converts YAML to JSON then uses JSON to unmarshal into an object, // optionally configuring the behavior of the JSON unmarshal. func Unmarshal(y []byte, o interface{}, opts ...JSONOpt) error { return UnmarshalWithOrigin(y, o, false, opts...) } // UnmarshalWithOrigin is like Unmarshal but if withOrigin is true, it will // include the origin information in the output. func UnmarshalWithOrigin(y []byte, o interface{}, withOrigin bool, opts ...JSONOpt) error { dec := yaml.NewDecoder(bytes.NewReader(y)) dec.Origin(withOrigin) return unmarshal(dec, o, opts) } func unmarshal(dec *yaml.Decoder, o interface{}, opts []JSONOpt) error { vo := reflect.ValueOf(o) j, err := yamlToJSON(dec, &vo) if err != nil { return fmt.Errorf("error converting YAML to JSON: %v", err) } err = jsonUnmarshal(bytes.NewReader(j), o, opts...) if err != nil { return fmt.Errorf("error unmarshaling JSON: %v", err) } return nil } // jsonUnmarshal unmarshals the JSON byte stream from the given reader into the // object, optionally applying decoder options prior to decoding. We are not // using json.Unmarshal directly as we want the chance to pass in non-default // options. func jsonUnmarshal(r io.Reader, o interface{}, opts ...JSONOpt) error { d := json.NewDecoder(r) for _, opt := range opts { d = opt(d) } if err := d.Decode(&o); err != nil { return fmt.Errorf("while decoding JSON: %v", err) } return nil } // JSONToYAML converts JSON to YAML. func JSONToYAML(j []byte) ([]byte, error) { // Convert the JSON to an object. var jsonObj interface{} // We are using yaml.Unmarshal here (instead of json.Unmarshal) because the // Go JSON library doesn't try to pick the right number type (int, float, // etc.) when unmarshalling to interface{}, it just picks float64 // universally. go-yaml does go through the effort of picking the right // number type, so we can preserve number type throughout this process. err := yaml.Unmarshal(j, &jsonObj) if err != nil { return nil, err } // Marshal this object into YAML. return yaml.Marshal(jsonObj) } // YAMLToJSON converts YAML to JSON. Since JSON is a subset of YAML, // passing JSON through this method should be a no-op. // // Things YAML can do that are not supported by JSON: // - In YAML you can have binary and null keys in your maps. These are invalid // in JSON. (int and float keys are converted to strings.) // - Binary data in YAML with the !!binary tag is not supported. If you want to // use binary data with this library, encode the data as base64 as usual but do // not use the !!binary tag in your YAML. This will ensure the original base64 // encoded data makes it all the way through to the JSON. func YAMLToJSON(y []byte) ([]byte, error) { //nolint:revive dec := yaml.NewDecoder(bytes.NewReader(y)) return yamlToJSON(dec, nil) } func yamlToJSON(dec *yaml.Decoder, jsonTarget *reflect.Value) ([]byte, error) { // Convert the YAML to an object. var yamlObj interface{} if err := dec.Decode(&yamlObj); err != nil { // Functionality changed in v3 which means we need to ignore EOF error. // See https://github.com/go-yaml/yaml/issues/639 if !errors.Is(err, io.EOF) { return nil, err } } // YAML objects are not completely compatible with JSON objects (e.g. you // can have non-string keys in YAML). So, convert the YAML-compatible object // to a JSON-compatible object, failing with an error if irrecoverable // incompatibilities happen along the way. jsonObj, err := convertToJSONableObject(yamlObj, jsonTarget) if err != nil { return nil, err } // Convert this object to JSON and return the data. return json.Marshal(jsonObj) } func convertToJSONableObject(yamlObj interface{}, jsonTarget *reflect.Value) (interface{}, error) { //nolint:gocyclo var err error // Resolve jsonTarget to a concrete value (i.e. not a pointer or an // interface). We pass decodingNull as false because we're not actually // decoding into the value, we're just checking if the ultimate target is a // string. if jsonTarget != nil { ju, tu, pv := indirect(*jsonTarget, false) // We have a JSON or Text Umarshaler at this level, so we can't be trying // to decode into a string. if ju != nil || tu != nil { jsonTarget = nil } else { jsonTarget = &pv } } // go-yaml v3 changed from v2 and now will provide map[string]interface{} by // default and map[interface{}]interface{} when none of the keys strings. // To get around this, we run a pre-loop to convert the map. // JSON only supports strings as keys, so we must convert. switch typedYAMLObj := yamlObj.(type) { case map[interface{}]interface{}: // From my reading of go-yaml v2 (specifically the resolve function), // keys can only have the types string, int, int64, float64, binary // (unsupported), or null (unsupported). strMap := make(map[string]interface{}) for k, v := range typedYAMLObj { // Resolve the key to a string first. var keyString string switch typedKey := k.(type) { case string: keyString = typedKey case int: keyString = strconv.Itoa(typedKey) case int64: // go-yaml will only return an int64 as a key if the system // architecture is 32-bit and the key's value is between 32-bit // and 64-bit. Otherwise the key type will simply be int. keyString = strconv.FormatInt(typedKey, 10) case float64: // Float64 is now supported in keys keyString = strconv.FormatFloat(typedKey, 'g', -1, 64) case bool: if typedKey { keyString = "true" } else { keyString = "false" } default: return nil, fmt.Errorf("unsupported map key of type: %s, key: %+#v, value: %+#v", reflect.TypeOf(k), k, v) } strMap[keyString] = v } // replace yamlObj with our new string map yamlObj = strMap } // If yamlObj is a number or a boolean, check if jsonTarget is a string - // if so, coerce. Else return normal. // If yamlObj is a map or array, find the field that each key is // unmarshaling to, and when you recurse pass the reflect.Value for that // field back into this function. switch typedYAMLObj := yamlObj.(type) { case map[string]interface{}: for k, v := range typedYAMLObj { // jsonTarget should be a struct or a map. If it's a struct, find // the field it's going to map to and pass its reflect.Value. If // it's a map, find the element type of the map and pass the // reflect.Value created from that type. If it's neither, just pass // nil - JSON conversion will error for us if it's a real issue. if jsonTarget != nil { t := *jsonTarget if t.Kind() == reflect.Struct { keyBytes := []byte(k) // Find the field that the JSON library would use. var f *field fields := cachedTypeFields(t.Type()) for i := range fields { ff := &fields[i] if bytes.Equal(ff.nameBytes, keyBytes) { f = ff break } // Do case-insensitive comparison. if f == nil && ff.equalFold(ff.nameBytes, keyBytes) { f = ff } } if f != nil { // Find the reflect.Value of the most preferential // struct field. jtf := t.Field(f.index[0]) typedYAMLObj[k], err = convertToJSONableObject(v, &jtf) if err != nil { return nil, err } continue } } else if t.Kind() == reflect.Map { // Create a zero value of the map's element type to use as // the JSON target. jtv := reflect.Zero(t.Type().Elem()) typedYAMLObj[k], err = convertToJSONableObject(v, &jtv) if err != nil { return nil, err } continue } } typedYAMLObj[k], err = convertToJSONableObject(v, nil) if err != nil { return nil, err } } return typedYAMLObj, nil case []interface{}: // We need to recurse into arrays in case there are any // map[interface{}]interface{}'s inside and to convert any // numbers to strings. // If jsonTarget is a slice (which it really should be), find the // thing it's going to map to. If it's not a slice, just pass nil // - JSON conversion will error for us if it's a real issue. var jsonSliceElemValue *reflect.Value if jsonTarget != nil { t := *jsonTarget if t.Kind() == reflect.Slice { // By default slices point to nil, but we need a reflect.Value // pointing to a value of the slice type, so we create one here. ev := reflect.Indirect(reflect.New(t.Type().Elem())) jsonSliceElemValue = &ev } } // Make and use a new array. arr := make([]interface{}, len(typedYAMLObj)) for i, v := range typedYAMLObj { arr[i], err = convertToJSONableObject(v, jsonSliceElemValue) if err != nil { return nil, err } } return arr, nil default: // If the target type is a string and the YAML type is a number, // convert the YAML type to a string. if jsonTarget != nil && (*jsonTarget).Kind() == reflect.String { // Based on my reading of go-yaml, it may return int, int64, // float64, or uint64. var s string switch typedVal := typedYAMLObj.(type) { case int: s = strconv.FormatInt(int64(typedVal), 10) case int64: s = strconv.FormatInt(typedVal, 10) case float64: s = strconv.FormatFloat(typedVal, 'g', -1, 64) case uint64: s = strconv.FormatUint(typedVal, 10) case bool: if typedVal { s = "true" } else { s = "false" } } if len(s) > 0 { yamlObj = interface{}(s) } } return yamlObj, nil } } ================================================ FILE: vendor/github.com/oasdiff/yaml3/LICENSE ================================================ This project is covered by two different licenses: MIT and Apache. #### MIT License #### The following files were ported to Go from C files of libyaml, and thus are still covered by their original MIT license, with the additional copyright staring in 2011 when the project was ported over: apic.go emitterc.go parserc.go readerc.go scannerc.go writerc.go yamlh.go yamlprivateh.go Copyright (c) 2006-2010 Kirill Simonov Copyright (c) 2006-2011 Kirill Simonov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ### Apache License ### All the remaining project files are covered by the Apache license: Copyright (c) 2011-2019 Canonical Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/oasdiff/yaml3/NOTICE ================================================ Copyright 2011-2016 Canonical Ltd. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/oasdiff/yaml3/README.md ================================================ # YAML support for the Go language Fork ---- This fork is an improved version of the go-yaml/yaml package, designed to include line and column location information for YAML elements during unmarshalling. Introduction ------------ The yaml package enables Go programs to comfortably encode and decode YAML values. It was developed within [Canonical](https://www.canonical.com) as part of the [juju](https://juju.ubuntu.com) project, and is based on a pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML) C library to parse and generate YAML data quickly and reliably. Compatibility ------------- The yaml package supports most of YAML 1.2, but preserves some behavior from 1.1 for backwards compatibility. Specifically, as of v3 of the yaml package: - YAML 1.1 bools (_yes/no, on/off_) are supported as long as they are being decoded into a typed bool value. Otherwise they behave as a string. Booleans in YAML 1.2 are _true/false_ only. - Octals encode and decode as _0777_ per YAML 1.1, rather than _0o777_ as specified in YAML 1.2, because most parsers still use the old format. Octals in the _0o777_ format are supported though, so new files work. - Does not support base-60 floats. These are gone from YAML 1.2, and were actually never supported by this package as it's clearly a poor choice. and offers backwards compatibility with YAML 1.1 in some cases. 1.2, including support for anchors, tags, map merging, etc. Multi-document unmarshalling is not yet implemented, and base-60 floats from YAML 1.1 are purposefully not supported since they're a poor design and are gone in YAML 1.2. Installation and usage ---------------------- The import path for the package is *gopkg.in/yaml.v3*. To install it, run: go get gopkg.in/yaml.v3 API documentation ----------------- If opened in a browser, the import path itself leads to the API documentation: - [https://gopkg.in/yaml.v3](https://gopkg.in/yaml.v3) API stability ------------- The package API for yaml v3 will remain stable as described in [gopkg.in](https://gopkg.in). License ------- The yaml package is licensed under the MIT and Apache License 2.0 licenses. Please see the LICENSE file for details. Example ------- ```Go package main import ( "fmt" "log" "oasdiff/yaml" ) var data = ` a: Easy! b: c: 2 d: [3, 4] ` // Note: struct fields must be public in order for unmarshal to // correctly populate the data. type T struct { A string B struct { RenamedC int `yaml:"c"` D []int `yaml:",flow"` } } func main() { t := T{} err := yaml.Unmarshal([]byte(data), &t) if err != nil { log.Fatalf("error: %v", err) } fmt.Printf("--- t:\n%v\n\n", t) d, err := yaml.Marshal(&t) if err != nil { log.Fatalf("error: %v", err) } fmt.Printf("--- t dump:\n%s\n\n", string(d)) m := make(map[interface{}]interface{}) err = yaml.Unmarshal([]byte(data), &m) if err != nil { log.Fatalf("error: %v", err) } fmt.Printf("--- m:\n%v\n\n", m) d, err = yaml.Marshal(&m) if err != nil { log.Fatalf("error: %v", err) } fmt.Printf("--- m dump:\n%s\n\n", string(d)) } ``` This example will generate the following output: ``` --- t: {Easy! {2 [3 4]}} --- t dump: a: Easy! b: c: 2 d: [3, 4] --- m: map[a:Easy! b:map[c:2 d:[3 4]]] --- m dump: a: Easy! b: c: 2 d: - 3 - 4 ``` ================================================ FILE: vendor/github.com/oasdiff/yaml3/apic.go ================================================ // // Copyright (c) 2011-2019 Canonical Ltd // Copyright (c) 2006-2010 Kirill Simonov // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package yaml import ( "io" ) func yaml_insert_token(parser *yaml_parser_t, pos int, token *yaml_token_t) { //fmt.Println("yaml_insert_token", "pos:", pos, "typ:", token.typ, "head:", parser.tokens_head, "len:", len(parser.tokens)) // Check if we can move the queue at the beginning of the buffer. if parser.tokens_head > 0 && len(parser.tokens) == cap(parser.tokens) { if parser.tokens_head != len(parser.tokens) { copy(parser.tokens, parser.tokens[parser.tokens_head:]) } parser.tokens = parser.tokens[:len(parser.tokens)-parser.tokens_head] parser.tokens_head = 0 } parser.tokens = append(parser.tokens, *token) if pos < 0 { return } copy(parser.tokens[parser.tokens_head+pos+1:], parser.tokens[parser.tokens_head+pos:]) parser.tokens[parser.tokens_head+pos] = *token } // Create a new parser object. func yaml_parser_initialize(parser *yaml_parser_t) bool { *parser = yaml_parser_t{ raw_buffer: make([]byte, 0, input_raw_buffer_size), buffer: make([]byte, 0, input_buffer_size), } return true } // Destroy a parser object. func yaml_parser_delete(parser *yaml_parser_t) { *parser = yaml_parser_t{} } // String read handler. func yaml_string_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { if parser.input_pos == len(parser.input) { return 0, io.EOF } n = copy(buffer, parser.input[parser.input_pos:]) parser.input_pos += n return n, nil } // Reader read handler. func yaml_reader_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { return parser.input_reader.Read(buffer) } // Set a string input. func yaml_parser_set_input_string(parser *yaml_parser_t, input []byte) { if parser.read_handler != nil { panic("must set the input source only once") } parser.read_handler = yaml_string_read_handler parser.input = input parser.input_pos = 0 } // Set a file input. func yaml_parser_set_input_reader(parser *yaml_parser_t, r io.Reader) { if parser.read_handler != nil { panic("must set the input source only once") } parser.read_handler = yaml_reader_read_handler parser.input_reader = r } // Set the source encoding. func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) { if parser.encoding != yaml_ANY_ENCODING { panic("must set the encoding only once") } parser.encoding = encoding } // Create a new emitter object. func yaml_emitter_initialize(emitter *yaml_emitter_t) { *emitter = yaml_emitter_t{ buffer: make([]byte, output_buffer_size), raw_buffer: make([]byte, 0, output_raw_buffer_size), states: make([]yaml_emitter_state_t, 0, initial_stack_size), events: make([]yaml_event_t, 0, initial_queue_size), best_width: -1, } } // Destroy an emitter object. func yaml_emitter_delete(emitter *yaml_emitter_t) { *emitter = yaml_emitter_t{} } // String write handler. func yaml_string_write_handler(emitter *yaml_emitter_t, buffer []byte) error { *emitter.output_buffer = append(*emitter.output_buffer, buffer...) return nil } // yaml_writer_write_handler uses emitter.output_writer to write the // emitted text. func yaml_writer_write_handler(emitter *yaml_emitter_t, buffer []byte) error { _, err := emitter.output_writer.Write(buffer) return err } // Set a string output. func yaml_emitter_set_output_string(emitter *yaml_emitter_t, output_buffer *[]byte) { if emitter.write_handler != nil { panic("must set the output target only once") } emitter.write_handler = yaml_string_write_handler emitter.output_buffer = output_buffer } // Set a file output. func yaml_emitter_set_output_writer(emitter *yaml_emitter_t, w io.Writer) { if emitter.write_handler != nil { panic("must set the output target only once") } emitter.write_handler = yaml_writer_write_handler emitter.output_writer = w } // Set the output encoding. func yaml_emitter_set_encoding(emitter *yaml_emitter_t, encoding yaml_encoding_t) { if emitter.encoding != yaml_ANY_ENCODING { panic("must set the output encoding only once") } emitter.encoding = encoding } // Set the canonical output style. func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) { emitter.canonical = canonical } // Set the indentation increment. func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) { if indent < 2 || indent > 9 { indent = 2 } emitter.best_indent = indent } // Set the preferred line width. func yaml_emitter_set_width(emitter *yaml_emitter_t, width int) { if width < 0 { width = -1 } emitter.best_width = width } // Set if unescaped non-ASCII characters are allowed. func yaml_emitter_set_unicode(emitter *yaml_emitter_t, unicode bool) { emitter.unicode = unicode } // Set the preferred line break character. func yaml_emitter_set_break(emitter *yaml_emitter_t, line_break yaml_break_t) { emitter.line_break = line_break } ///* // * Destroy a token object. // */ // //YAML_DECLARE(void) //yaml_token_delete(yaml_token_t *token) //{ // assert(token); // Non-NULL token object expected. // // switch (token.type) // { // case YAML_TAG_DIRECTIVE_TOKEN: // yaml_free(token.data.tag_directive.handle); // yaml_free(token.data.tag_directive.prefix); // break; // // case YAML_ALIAS_TOKEN: // yaml_free(token.data.alias.value); // break; // // case YAML_ANCHOR_TOKEN: // yaml_free(token.data.anchor.value); // break; // // case YAML_TAG_TOKEN: // yaml_free(token.data.tag.handle); // yaml_free(token.data.tag.suffix); // break; // // case YAML_SCALAR_TOKEN: // yaml_free(token.data.scalar.value); // break; // // default: // break; // } // // memset(token, 0, sizeof(yaml_token_t)); //} // ///* // * Check if a string is a valid UTF-8 sequence. // * // * Check 'reader.c' for more details on UTF-8 encoding. // */ // //static int //yaml_check_utf8(yaml_char_t *start, size_t length) //{ // yaml_char_t *end = start+length; // yaml_char_t *pointer = start; // // while (pointer < end) { // unsigned char octet; // unsigned int width; // unsigned int value; // size_t k; // // octet = pointer[0]; // width = (octet & 0x80) == 0x00 ? 1 : // (octet & 0xE0) == 0xC0 ? 2 : // (octet & 0xF0) == 0xE0 ? 3 : // (octet & 0xF8) == 0xF0 ? 4 : 0; // value = (octet & 0x80) == 0x00 ? octet & 0x7F : // (octet & 0xE0) == 0xC0 ? octet & 0x1F : // (octet & 0xF0) == 0xE0 ? octet & 0x0F : // (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0; // if (!width) return 0; // if (pointer+width > end) return 0; // for (k = 1; k < width; k ++) { // octet = pointer[k]; // if ((octet & 0xC0) != 0x80) return 0; // value = (value << 6) + (octet & 0x3F); // } // if (!((width == 1) || // (width == 2 && value >= 0x80) || // (width == 3 && value >= 0x800) || // (width == 4 && value >= 0x10000))) return 0; // // pointer += width; // } // // return 1; //} // // Create STREAM-START. func yaml_stream_start_event_initialize(event *yaml_event_t, encoding yaml_encoding_t) { *event = yaml_event_t{ typ: yaml_STREAM_START_EVENT, encoding: encoding, } } // Create STREAM-END. func yaml_stream_end_event_initialize(event *yaml_event_t) { *event = yaml_event_t{ typ: yaml_STREAM_END_EVENT, } } // Create DOCUMENT-START. func yaml_document_start_event_initialize( event *yaml_event_t, version_directive *yaml_version_directive_t, tag_directives []yaml_tag_directive_t, implicit bool, ) { *event = yaml_event_t{ typ: yaml_DOCUMENT_START_EVENT, version_directive: version_directive, tag_directives: tag_directives, implicit: implicit, } } // Create DOCUMENT-END. func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) { *event = yaml_event_t{ typ: yaml_DOCUMENT_END_EVENT, implicit: implicit, } } // Create ALIAS. func yaml_alias_event_initialize(event *yaml_event_t, anchor []byte) bool { *event = yaml_event_t{ typ: yaml_ALIAS_EVENT, anchor: anchor, } return true } // Create SCALAR. func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool { *event = yaml_event_t{ typ: yaml_SCALAR_EVENT, anchor: anchor, tag: tag, value: value, implicit: plain_implicit, quoted_implicit: quoted_implicit, style: yaml_style_t(style), } return true } // Create SEQUENCE-START. func yaml_sequence_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_sequence_style_t) bool { *event = yaml_event_t{ typ: yaml_SEQUENCE_START_EVENT, anchor: anchor, tag: tag, implicit: implicit, style: yaml_style_t(style), } return true } // Create SEQUENCE-END. func yaml_sequence_end_event_initialize(event *yaml_event_t) bool { *event = yaml_event_t{ typ: yaml_SEQUENCE_END_EVENT, } return true } // Create MAPPING-START. func yaml_mapping_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_mapping_style_t) { *event = yaml_event_t{ typ: yaml_MAPPING_START_EVENT, anchor: anchor, tag: tag, implicit: implicit, style: yaml_style_t(style), } } // Create MAPPING-END. func yaml_mapping_end_event_initialize(event *yaml_event_t) { *event = yaml_event_t{ typ: yaml_MAPPING_END_EVENT, } } // Destroy an event object. func yaml_event_delete(event *yaml_event_t) { *event = yaml_event_t{} } ///* // * Create a document object. // */ // //YAML_DECLARE(int) //yaml_document_initialize(document *yaml_document_t, // version_directive *yaml_version_directive_t, // tag_directives_start *yaml_tag_directive_t, // tag_directives_end *yaml_tag_directive_t, // start_implicit int, end_implicit int) //{ // struct { // error yaml_error_type_t // } context // struct { // start *yaml_node_t // end *yaml_node_t // top *yaml_node_t // } nodes = { NULL, NULL, NULL } // version_directive_copy *yaml_version_directive_t = NULL // struct { // start *yaml_tag_directive_t // end *yaml_tag_directive_t // top *yaml_tag_directive_t // } tag_directives_copy = { NULL, NULL, NULL } // value yaml_tag_directive_t = { NULL, NULL } // mark yaml_mark_t = { 0, 0, 0 } // // assert(document) // Non-NULL document object is expected. // assert((tag_directives_start && tag_directives_end) || // (tag_directives_start == tag_directives_end)) // // Valid tag directives are expected. // // if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error // // if (version_directive) { // version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t)) // if (!version_directive_copy) goto error // version_directive_copy.major = version_directive.major // version_directive_copy.minor = version_directive.minor // } // // if (tag_directives_start != tag_directives_end) { // tag_directive *yaml_tag_directive_t // if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE)) // goto error // for (tag_directive = tag_directives_start // tag_directive != tag_directives_end; tag_directive ++) { // assert(tag_directive.handle) // assert(tag_directive.prefix) // if (!yaml_check_utf8(tag_directive.handle, // strlen((char *)tag_directive.handle))) // goto error // if (!yaml_check_utf8(tag_directive.prefix, // strlen((char *)tag_directive.prefix))) // goto error // value.handle = yaml_strdup(tag_directive.handle) // value.prefix = yaml_strdup(tag_directive.prefix) // if (!value.handle || !value.prefix) goto error // if (!PUSH(&context, tag_directives_copy, value)) // goto error // value.handle = NULL // value.prefix = NULL // } // } // // DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy, // tag_directives_copy.start, tag_directives_copy.top, // start_implicit, end_implicit, mark, mark) // // return 1 // //error: // STACK_DEL(&context, nodes) // yaml_free(version_directive_copy) // while (!STACK_EMPTY(&context, tag_directives_copy)) { // value yaml_tag_directive_t = POP(&context, tag_directives_copy) // yaml_free(value.handle) // yaml_free(value.prefix) // } // STACK_DEL(&context, tag_directives_copy) // yaml_free(value.handle) // yaml_free(value.prefix) // // return 0 //} // ///* // * Destroy a document object. // */ // //YAML_DECLARE(void) //yaml_document_delete(document *yaml_document_t) //{ // struct { // error yaml_error_type_t // } context // tag_directive *yaml_tag_directive_t // // context.error = YAML_NO_ERROR // Eliminate a compiler warning. // // assert(document) // Non-NULL document object is expected. // // while (!STACK_EMPTY(&context, document.nodes)) { // node yaml_node_t = POP(&context, document.nodes) // yaml_free(node.tag) // switch (node.type) { // case YAML_SCALAR_NODE: // yaml_free(node.data.scalar.value) // break // case YAML_SEQUENCE_NODE: // STACK_DEL(&context, node.data.sequence.items) // break // case YAML_MAPPING_NODE: // STACK_DEL(&context, node.data.mapping.pairs) // break // default: // assert(0) // Should not happen. // } // } // STACK_DEL(&context, document.nodes) // // yaml_free(document.version_directive) // for (tag_directive = document.tag_directives.start // tag_directive != document.tag_directives.end // tag_directive++) { // yaml_free(tag_directive.handle) // yaml_free(tag_directive.prefix) // } // yaml_free(document.tag_directives.start) // // memset(document, 0, sizeof(yaml_document_t)) //} // ///** // * Get a document node. // */ // //YAML_DECLARE(yaml_node_t *) //yaml_document_get_node(document *yaml_document_t, index int) //{ // assert(document) // Non-NULL document object is expected. // // if (index > 0 && document.nodes.start + index <= document.nodes.top) { // return document.nodes.start + index - 1 // } // return NULL //} // ///** // * Get the root object. // */ // //YAML_DECLARE(yaml_node_t *) //yaml_document_get_root_node(document *yaml_document_t) //{ // assert(document) // Non-NULL document object is expected. // // if (document.nodes.top != document.nodes.start) { // return document.nodes.start // } // return NULL //} // ///* // * Add a scalar node to a document. // */ // //YAML_DECLARE(int) //yaml_document_add_scalar(document *yaml_document_t, // tag *yaml_char_t, value *yaml_char_t, length int, // style yaml_scalar_style_t) //{ // struct { // error yaml_error_type_t // } context // mark yaml_mark_t = { 0, 0, 0 } // tag_copy *yaml_char_t = NULL // value_copy *yaml_char_t = NULL // node yaml_node_t // // assert(document) // Non-NULL document object is expected. // assert(value) // Non-NULL value is expected. // // if (!tag) { // tag = (yaml_char_t *)YAML_DEFAULT_SCALAR_TAG // } // // if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error // tag_copy = yaml_strdup(tag) // if (!tag_copy) goto error // // if (length < 0) { // length = strlen((char *)value) // } // // if (!yaml_check_utf8(value, length)) goto error // value_copy = yaml_malloc(length+1) // if (!value_copy) goto error // memcpy(value_copy, value, length) // value_copy[length] = '\0' // // SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark) // if (!PUSH(&context, document.nodes, node)) goto error // // return document.nodes.top - document.nodes.start // //error: // yaml_free(tag_copy) // yaml_free(value_copy) // // return 0 //} // ///* // * Add a sequence node to a document. // */ // //YAML_DECLARE(int) //yaml_document_add_sequence(document *yaml_document_t, // tag *yaml_char_t, style yaml_sequence_style_t) //{ // struct { // error yaml_error_type_t // } context // mark yaml_mark_t = { 0, 0, 0 } // tag_copy *yaml_char_t = NULL // struct { // start *yaml_node_item_t // end *yaml_node_item_t // top *yaml_node_item_t // } items = { NULL, NULL, NULL } // node yaml_node_t // // assert(document) // Non-NULL document object is expected. // // if (!tag) { // tag = (yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG // } // // if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error // tag_copy = yaml_strdup(tag) // if (!tag_copy) goto error // // if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error // // SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end, // style, mark, mark) // if (!PUSH(&context, document.nodes, node)) goto error // // return document.nodes.top - document.nodes.start // //error: // STACK_DEL(&context, items) // yaml_free(tag_copy) // // return 0 //} // ///* // * Add a mapping node to a document. // */ // //YAML_DECLARE(int) //yaml_document_add_mapping(document *yaml_document_t, // tag *yaml_char_t, style yaml_mapping_style_t) //{ // struct { // error yaml_error_type_t // } context // mark yaml_mark_t = { 0, 0, 0 } // tag_copy *yaml_char_t = NULL // struct { // start *yaml_node_pair_t // end *yaml_node_pair_t // top *yaml_node_pair_t // } pairs = { NULL, NULL, NULL } // node yaml_node_t // // assert(document) // Non-NULL document object is expected. // // if (!tag) { // tag = (yaml_char_t *)YAML_DEFAULT_MAPPING_TAG // } // // if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error // tag_copy = yaml_strdup(tag) // if (!tag_copy) goto error // // if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error // // MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end, // style, mark, mark) // if (!PUSH(&context, document.nodes, node)) goto error // // return document.nodes.top - document.nodes.start // //error: // STACK_DEL(&context, pairs) // yaml_free(tag_copy) // // return 0 //} // ///* // * Append an item to a sequence node. // */ // //YAML_DECLARE(int) //yaml_document_append_sequence_item(document *yaml_document_t, // sequence int, item int) //{ // struct { // error yaml_error_type_t // } context // // assert(document) // Non-NULL document is required. // assert(sequence > 0 // && document.nodes.start + sequence <= document.nodes.top) // // Valid sequence id is required. // assert(document.nodes.start[sequence-1].type == YAML_SEQUENCE_NODE) // // A sequence node is required. // assert(item > 0 && document.nodes.start + item <= document.nodes.top) // // Valid item id is required. // // if (!PUSH(&context, // document.nodes.start[sequence-1].data.sequence.items, item)) // return 0 // // return 1 //} // ///* // * Append a pair of a key and a value to a mapping node. // */ // //YAML_DECLARE(int) //yaml_document_append_mapping_pair(document *yaml_document_t, // mapping int, key int, value int) //{ // struct { // error yaml_error_type_t // } context // // pair yaml_node_pair_t // // assert(document) // Non-NULL document is required. // assert(mapping > 0 // && document.nodes.start + mapping <= document.nodes.top) // // Valid mapping id is required. // assert(document.nodes.start[mapping-1].type == YAML_MAPPING_NODE) // // A mapping node is required. // assert(key > 0 && document.nodes.start + key <= document.nodes.top) // // Valid key id is required. // assert(value > 0 && document.nodes.start + value <= document.nodes.top) // // Valid value id is required. // // pair.key = key // pair.value = value // // if (!PUSH(&context, // document.nodes.start[mapping-1].data.mapping.pairs, pair)) // return 0 // // return 1 //} // // ================================================ FILE: vendor/github.com/oasdiff/yaml3/decode.go ================================================ // // Copyright (c) 2011-2019 Canonical Ltd // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package yaml import ( "encoding" "encoding/base64" "fmt" "io" "math" "reflect" "strconv" "time" ) // ---------------------------------------------------------------------------- // Parser, produces a node tree out of a libyaml event stream. type parser struct { parser yaml_parser_t event yaml_event_t doc *Node anchors map[string]*Node doneInit bool textless bool } func newParser(b []byte) *parser { p := parser{} if !yaml_parser_initialize(&p.parser) { panic("failed to initialize YAML emitter") } if len(b) == 0 { b = []byte{'\n'} } yaml_parser_set_input_string(&p.parser, b) return &p } func newParserFromReader(r io.Reader) *parser { p := parser{} if !yaml_parser_initialize(&p.parser) { panic("failed to initialize YAML emitter") } yaml_parser_set_input_reader(&p.parser, r) return &p } func (p *parser) init() { if p.doneInit { return } p.anchors = make(map[string]*Node) p.expect(yaml_STREAM_START_EVENT) p.doneInit = true } func (p *parser) destroy() { if p.event.typ != yaml_NO_EVENT { yaml_event_delete(&p.event) } yaml_parser_delete(&p.parser) } // expect consumes an event from the event stream and // checks that it's of the expected type. func (p *parser) expect(e yaml_event_type_t) { if p.event.typ == yaml_NO_EVENT { if !yaml_parser_parse(&p.parser, &p.event) { p.fail() } } if p.event.typ == yaml_STREAM_END_EVENT { failf("attempted to go past the end of stream; corrupted value?") } if p.event.typ != e { p.parser.problem = fmt.Sprintf("expected %s event but got %s", e, p.event.typ) p.fail() } yaml_event_delete(&p.event) p.event.typ = yaml_NO_EVENT } // peek peeks at the next event in the event stream, // puts the results into p.event and returns the event type. func (p *parser) peek() yaml_event_type_t { if p.event.typ != yaml_NO_EVENT { return p.event.typ } // It's curious choice from the underlying API to generally return a // positive result on success, but on this case return true in an error // scenario. This was the source of bugs in the past (issue #666). if !yaml_parser_parse(&p.parser, &p.event) || p.parser.error != yaml_NO_ERROR { p.fail() } return p.event.typ } func (p *parser) fail() { var where string var line int if p.parser.context_mark.line != 0 { line = p.parser.context_mark.line // Scanner errors don't iterate line before returning error if p.parser.error == yaml_SCANNER_ERROR { line++ } } else if p.parser.problem_mark.line != 0 { line = p.parser.problem_mark.line // Scanner errors don't iterate line before returning error if p.parser.error == yaml_SCANNER_ERROR { line++ } } if line != 0 { where = "line " + strconv.Itoa(line) + ": " } var msg string if len(p.parser.problem) > 0 { msg = p.parser.problem } else { msg = "unknown problem parsing YAML content" } failf("%s%s", where, msg) } func (p *parser) anchor(n *Node, anchor []byte) { if anchor != nil { n.Anchor = string(anchor) p.anchors[n.Anchor] = n } } func (p *parser) parse() *Node { p.init() switch p.peek() { case yaml_SCALAR_EVENT: return p.scalar() case yaml_ALIAS_EVENT: return p.alias() case yaml_MAPPING_START_EVENT: return p.mapping() case yaml_SEQUENCE_START_EVENT: return p.sequence() case yaml_DOCUMENT_START_EVENT: return p.document() case yaml_STREAM_END_EVENT: // Happens when attempting to decode an empty buffer. return nil case yaml_TAIL_COMMENT_EVENT: panic("internal error: unexpected tail comment event (please report)") default: panic("internal error: attempted to parse unknown event (please report): " + p.event.typ.String()) } } func (p *parser) node(kind Kind, defaultTag, tag, value string) *Node { var style Style if tag != "" && tag != "!" { tag = shortTag(tag) style = TaggedStyle } else if defaultTag != "" { tag = defaultTag } else if kind == ScalarNode { tag, _ = resolve("", value) } n := &Node{ Kind: kind, Tag: tag, Value: value, Style: style, } if !p.textless { n.Line = p.event.start_mark.line + 1 n.Column = p.event.start_mark.column + 1 n.HeadComment = string(p.event.head_comment) n.LineComment = string(p.event.line_comment) n.FootComment = string(p.event.foot_comment) } return n } func (p *parser) parseChild(parent *Node) *Node { child := p.parse() parent.Content = append(parent.Content, child) return child } func (p *parser) document() *Node { n := p.node(DocumentNode, "", "", "") p.doc = n p.expect(yaml_DOCUMENT_START_EVENT) p.parseChild(n) if p.peek() == yaml_DOCUMENT_END_EVENT { n.FootComment = string(p.event.foot_comment) } p.expect(yaml_DOCUMENT_END_EVENT) return n } func (p *parser) alias() *Node { n := p.node(AliasNode, "", "", string(p.event.anchor)) n.Alias = p.anchors[n.Value] if n.Alias == nil { failf("unknown anchor '%s' referenced", n.Value) } p.expect(yaml_ALIAS_EVENT) return n } func (p *parser) scalar() *Node { var parsedStyle = p.event.scalar_style() var nodeStyle Style switch { case parsedStyle&yaml_DOUBLE_QUOTED_SCALAR_STYLE != 0: nodeStyle = DoubleQuotedStyle case parsedStyle&yaml_SINGLE_QUOTED_SCALAR_STYLE != 0: nodeStyle = SingleQuotedStyle case parsedStyle&yaml_LITERAL_SCALAR_STYLE != 0: nodeStyle = LiteralStyle case parsedStyle&yaml_FOLDED_SCALAR_STYLE != 0: nodeStyle = FoldedStyle } var nodeValue = string(p.event.value) var nodeTag = string(p.event.tag) var defaultTag string if nodeStyle == 0 { if nodeValue == "<<" { defaultTag = mergeTag } } else { defaultTag = strTag } n := p.node(ScalarNode, defaultTag, nodeTag, nodeValue) n.Style |= nodeStyle p.anchor(n, p.event.anchor) p.expect(yaml_SCALAR_EVENT) return n } func (p *parser) sequence() *Node { n := p.node(SequenceNode, seqTag, string(p.event.tag), "") if p.event.sequence_style()&yaml_FLOW_SEQUENCE_STYLE != 0 { n.Style |= FlowStyle } p.anchor(n, p.event.anchor) p.expect(yaml_SEQUENCE_START_EVENT) for p.peek() != yaml_SEQUENCE_END_EVENT { p.parseChild(n) } n.LineComment = string(p.event.line_comment) n.FootComment = string(p.event.foot_comment) p.expect(yaml_SEQUENCE_END_EVENT) return n } func (p *parser) mapping() *Node { n := p.node(MappingNode, mapTag, string(p.event.tag), "") block := true if p.event.mapping_style()&yaml_FLOW_MAPPING_STYLE != 0 { block = false n.Style |= FlowStyle } p.anchor(n, p.event.anchor) p.expect(yaml_MAPPING_START_EVENT) for p.peek() != yaml_MAPPING_END_EVENT { k := p.parseChild(n) if block && k.FootComment != "" { // Must be a foot comment for the prior value when being dedented. if len(n.Content) > 2 { n.Content[len(n.Content)-3].FootComment = k.FootComment k.FootComment = "" } } v := p.parseChild(n) if k.FootComment == "" && v.FootComment != "" { k.FootComment = v.FootComment v.FootComment = "" } if p.peek() == yaml_TAIL_COMMENT_EVENT { if k.FootComment == "" { k.FootComment = string(p.event.foot_comment) } p.expect(yaml_TAIL_COMMENT_EVENT) } } n.LineComment = string(p.event.line_comment) n.FootComment = string(p.event.foot_comment) if n.Style&FlowStyle == 0 && n.FootComment != "" && len(n.Content) > 1 { n.Content[len(n.Content)-2].FootComment = n.FootComment n.FootComment = "" } p.expect(yaml_MAPPING_END_EVENT) return n } // ---------------------------------------------------------------------------- // Decoder, unmarshals a node into a provided value. type decoder struct { doc *Node aliases map[*Node]bool terrors []string stringMapType reflect.Type generalMapType reflect.Type knownFields bool origin bool uniqueKeys bool decodeCount int aliasCount int aliasDepth int mergedFields map[interface{}]bool } var ( nodeType = reflect.TypeOf(Node{}) durationType = reflect.TypeOf(time.Duration(0)) stringMapType = reflect.TypeOf(map[string]interface{}{}) generalMapType = reflect.TypeOf(map[interface{}]interface{}{}) ifaceType = generalMapType.Elem() timeType = reflect.TypeOf(time.Time{}) ptrTimeType = reflect.TypeOf(&time.Time{}) ) func newDecoder() *decoder { d := &decoder{ stringMapType: stringMapType, generalMapType: generalMapType, uniqueKeys: true, } d.aliases = make(map[*Node]bool) return d } func (d *decoder) terror(n *Node, tag string, out reflect.Value) { if n.Tag != "" { tag = n.Tag } value := n.Value if tag != seqTag && tag != mapTag { if len(value) > 10 { value = " `" + value[:7] + "...`" } else { value = " `" + value + "`" } } d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.Line, shortTag(tag), value, out.Type())) } func (d *decoder) callUnmarshaler(n *Node, u Unmarshaler) (good bool) { err := u.UnmarshalYAML(n) if e, ok := err.(*TypeError); ok { d.terrors = append(d.terrors, e.Errors...) return false } if err != nil { fail(err) } return true } func (d *decoder) callObsoleteUnmarshaler(n *Node, u obsoleteUnmarshaler) (good bool) { terrlen := len(d.terrors) err := u.UnmarshalYAML(func(v interface{}) (err error) { defer handleErr(&err) d.unmarshal(n, reflect.ValueOf(v)) if len(d.terrors) > terrlen { issues := d.terrors[terrlen:] d.terrors = d.terrors[:terrlen] return &TypeError{issues} } return nil }) if e, ok := err.(*TypeError); ok { d.terrors = append(d.terrors, e.Errors...) return false } if err != nil { fail(err) } return true } // d.prepare initializes and dereferences pointers and calls UnmarshalYAML // if a value is found to implement it. // It returns the initialized and dereferenced out value, whether // unmarshalling was already done by UnmarshalYAML, and if so whether // its types unmarshalled appropriately. // // If n holds a null value, prepare returns before doing anything. func (d *decoder) prepare(n *Node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) { if n.ShortTag() == nullTag { return out, false, false } again := true for again { again = false if out.Kind() == reflect.Ptr { if out.IsNil() { out.Set(reflect.New(out.Type().Elem())) } out = out.Elem() again = true } if out.CanAddr() { outi := out.Addr().Interface() if u, ok := outi.(Unmarshaler); ok { good = d.callUnmarshaler(n, u) return out, true, good } if u, ok := outi.(obsoleteUnmarshaler); ok { good = d.callObsoleteUnmarshaler(n, u) return out, true, good } } } return out, false, false } func (d *decoder) fieldByIndex(n *Node, v reflect.Value, index []int) (field reflect.Value) { if n.ShortTag() == nullTag { return reflect.Value{} } for _, num := range index { for { if v.Kind() == reflect.Ptr { if v.IsNil() { v.Set(reflect.New(v.Type().Elem())) } v = v.Elem() continue } break } v = v.Field(num) } return v } const ( // 400,000 decode operations is ~500kb of dense object declarations, or // ~5kb of dense object declarations with 10000% alias expansion alias_ratio_range_low = 400000 // 4,000,000 decode operations is ~5MB of dense object declarations, or // ~4.5MB of dense object declarations with 10% alias expansion alias_ratio_range_high = 4000000 // alias_ratio_range is the range over which we scale allowed alias ratios alias_ratio_range = float64(alias_ratio_range_high - alias_ratio_range_low) ) func allowedAliasRatio(decodeCount int) float64 { switch { case decodeCount <= alias_ratio_range_low: // allow 99% to come from alias expansion for small-to-medium documents return 0.99 case decodeCount >= alias_ratio_range_high: // allow 10% to come from alias expansion for very large documents return 0.10 default: // scale smoothly from 99% down to 10% over the range. // this maps to 396,000 - 400,000 allowed alias-driven decodes over the range. // 400,000 decode operations is ~100MB of allocations in worst-case scenarios (single-item maps). return 0.99 - 0.89*(float64(decodeCount-alias_ratio_range_low)/alias_ratio_range) } } func (d *decoder) unmarshal(n *Node, out reflect.Value) (good bool) { d.decodeCount++ if d.aliasDepth > 0 { d.aliasCount++ } if d.aliasCount > 100 && d.decodeCount > 1000 && float64(d.aliasCount)/float64(d.decodeCount) > allowedAliasRatio(d.decodeCount) { failf("document contains excessive aliasing") } if out.Type() == nodeType { out.Set(reflect.ValueOf(n).Elem()) return true } switch n.Kind { case DocumentNode: return d.document(n, out) case AliasNode: return d.alias(n, out) } out, unmarshaled, good := d.prepare(n, out) if unmarshaled { return good } switch n.Kind { case ScalarNode: good = d.scalar(n, out) case MappingNode: good = d.mapping(n, out) case SequenceNode: good = d.sequence(n, out) case 0: if n.IsZero() { return d.null(out) } fallthrough default: failf("cannot decode node with unknown kind %d", n.Kind) } return good } func (d *decoder) document(n *Node, out reflect.Value) (good bool) { if len(n.Content) == 1 { d.doc = n d.unmarshal(n.Content[0], out) return true } return false } func (d *decoder) alias(n *Node, out reflect.Value) (good bool) { if d.aliases[n] { // TODO this could actually be allowed in some circumstances. failf("anchor '%s' value contains itself", n.Value) } d.aliases[n] = true d.aliasDepth++ good = d.unmarshal(n.Alias, out) d.aliasDepth-- delete(d.aliases, n) return good } var zeroValue reflect.Value func resetMap(out reflect.Value) { for _, k := range out.MapKeys() { out.SetMapIndex(k, zeroValue) } } func (d *decoder) null(out reflect.Value) bool { if out.CanAddr() { switch out.Kind() { case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: out.Set(reflect.Zero(out.Type())) return true } } return false } func (d *decoder) scalar(n *Node, out reflect.Value) bool { var tag string var resolved interface{} if n.indicatedString() { tag = strTag resolved = n.Value } else { tag, resolved = resolve(n.Tag, n.Value) if tag == binaryTag { data, err := base64.StdEncoding.DecodeString(resolved.(string)) if err != nil { failf("!!binary value contains invalid base64 data") } resolved = string(data) } } if resolved == nil { return d.null(out) } if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() { // We've resolved to exactly the type we want, so use that. out.Set(resolvedv) return true } // Perhaps we can use the value as a TextUnmarshaler to // set its value. if out.CanAddr() { u, ok := out.Addr().Interface().(encoding.TextUnmarshaler) if ok { var text []byte if tag == binaryTag { text = []byte(resolved.(string)) } else { // We let any value be unmarshaled into TextUnmarshaler. // That might be more lax than we'd like, but the // TextUnmarshaler itself should bowl out any dubious values. text = []byte(n.Value) } err := u.UnmarshalText(text) if err != nil { fail(err) } return true } } switch out.Kind() { case reflect.String: if tag == binaryTag { out.SetString(resolved.(string)) return true } out.SetString(n.Value) return true case reflect.Interface: out.Set(reflect.ValueOf(resolved)) return true case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: // This used to work in v2, but it's very unfriendly. isDuration := out.Type() == durationType switch resolved := resolved.(type) { case int: if !isDuration && !out.OverflowInt(int64(resolved)) { out.SetInt(int64(resolved)) return true } case int64: if !isDuration && !out.OverflowInt(resolved) { out.SetInt(resolved) return true } case uint64: if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { out.SetInt(int64(resolved)) return true } case float64: if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { out.SetInt(int64(resolved)) return true } case string: if out.Type() == durationType { d, err := time.ParseDuration(resolved) if err == nil { out.SetInt(int64(d)) return true } } } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: switch resolved := resolved.(type) { case int: if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { out.SetUint(uint64(resolved)) return true } case int64: if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { out.SetUint(uint64(resolved)) return true } case uint64: if !out.OverflowUint(uint64(resolved)) { out.SetUint(uint64(resolved)) return true } case float64: if resolved <= math.MaxUint64 && !out.OverflowUint(uint64(resolved)) { out.SetUint(uint64(resolved)) return true } } case reflect.Bool: switch resolved := resolved.(type) { case bool: out.SetBool(resolved) return true case string: // This offers some compatibility with the 1.1 spec (https://yaml.org/type/bool.html). // It only works if explicitly attempting to unmarshal into a typed bool value. switch resolved { case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON": out.SetBool(true) return true case "n", "N", "no", "No", "NO", "off", "Off", "OFF": out.SetBool(false) return true } } case reflect.Float32, reflect.Float64: switch resolved := resolved.(type) { case int: out.SetFloat(float64(resolved)) return true case int64: out.SetFloat(float64(resolved)) return true case uint64: out.SetFloat(float64(resolved)) return true case float64: out.SetFloat(resolved) return true } case reflect.Struct: if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() { out.Set(resolvedv) return true } case reflect.Ptr: panic("yaml internal error: please report the issue") } d.terror(n, tag, out) return false } func settableValueOf(i interface{}) reflect.Value { v := reflect.ValueOf(i) sv := reflect.New(v.Type()).Elem() sv.Set(v) return sv } func (d *decoder) sequence(n *Node, out reflect.Value) (good bool) { l := len(n.Content) var iface reflect.Value switch out.Kind() { case reflect.Slice: out.Set(reflect.MakeSlice(out.Type(), l, l)) case reflect.Array: if l != out.Len() { failf("invalid array: want %d elements but got %d", out.Len(), l) } case reflect.Interface: // No type hints. Will have to use a generic sequence. iface = out out = settableValueOf(make([]interface{}, l)) default: d.terror(n, seqTag, out) return false } et := out.Type().Elem() j := 0 for i := 0; i < l; i++ { e := reflect.New(et).Elem() if d.origin { addOriginInSeq(n.Content[i]) } if ok := d.unmarshal(n.Content[i], e); ok { out.Index(j).Set(e) j++ } } if out.Kind() != reflect.Array { out.Set(out.Slice(0, j)) } if iface.IsValid() { iface.Set(out) } return true } func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) { l := len(n.Content) if d.uniqueKeys { nerrs := len(d.terrors) for i := 0; i < l; i += 2 { ni := n.Content[i] for j := i + 2; j < l; j += 2 { nj := n.Content[j] if ni.Kind == nj.Kind && ni.Value == nj.Value { d.terrors = append(d.terrors, fmt.Sprintf("line %d: mapping key %#v already defined at line %d", nj.Line, nj.Value, ni.Line)) } } } if len(d.terrors) > nerrs { return false } } switch out.Kind() { case reflect.Struct: return d.mappingStruct(n, out) case reflect.Map: // okay case reflect.Interface: iface := out if isStringMap(n) { out = reflect.MakeMap(d.stringMapType) } else { out = reflect.MakeMap(d.generalMapType) } iface.Set(out) default: d.terror(n, mapTag, out) return false } outt := out.Type() kt := outt.Key() et := outt.Elem() stringMapType := d.stringMapType generalMapType := d.generalMapType if outt.Elem() == ifaceType { if outt.Key().Kind() == reflect.String { d.stringMapType = outt } else if outt.Key() == ifaceType { d.generalMapType = outt } } mergedFields := d.mergedFields d.mergedFields = nil var mergeNode *Node mapIsNew := false if out.IsNil() { out.Set(reflect.MakeMap(outt)) mapIsNew = true } for i := 0; i < l; i += 2 { if isMerge(n.Content[i]) { mergeNode = n.Content[i+1] continue } k := reflect.New(kt).Elem() if d.unmarshal(n.Content[i], k) { if mergedFields != nil { ki := k.Interface() if mergedFields[ki] { continue } mergedFields[ki] = true } kkind := k.Kind() if kkind == reflect.Interface { kkind = k.Elem().Kind() } if kkind == reflect.Map || kkind == reflect.Slice { failf("invalid map key: %#v", k.Interface()) } e := reflect.New(et).Elem() if d.origin { addOriginInMap(n.Content[i], n.Content[i+1]) } if d.unmarshal(n.Content[i+1], e) || n.Content[i+1].ShortTag() == nullTag && (mapIsNew || !out.MapIndex(k).IsValid()) { out.SetMapIndex(k, e) } } } d.mergedFields = mergedFields if mergeNode != nil { d.merge(n, mergeNode, out) } d.stringMapType = stringMapType d.generalMapType = generalMapType return true } func isStringMap(n *Node) bool { if n.Kind != MappingNode { return false } l := len(n.Content) for i := 0; i < l; i += 2 { shortTag := n.Content[i].ShortTag() if shortTag != strTag && shortTag != mergeTag { return false } } return true } func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { sinfo, err := getStructInfo(out.Type()) if err != nil { panic(err) } var inlineMap reflect.Value var elemType reflect.Type if sinfo.InlineMap != -1 { inlineMap = out.Field(sinfo.InlineMap) elemType = inlineMap.Type().Elem() } for _, index := range sinfo.InlineUnmarshalers { field := d.fieldByIndex(n, out, index) d.prepare(n, field) } mergedFields := d.mergedFields d.mergedFields = nil var mergeNode *Node var doneFields []bool if d.uniqueKeys { doneFields = make([]bool, len(sinfo.FieldsList)) } name := settableValueOf("") l := len(n.Content) for i := 0; i < l; i += 2 { ni := n.Content[i] if isMerge(ni) { mergeNode = n.Content[i+1] continue } if !d.unmarshal(ni, name) { continue } sname := name.String() if mergedFields != nil { if mergedFields[sname] { continue } mergedFields[sname] = true } if info, ok := sinfo.FieldsMap[sname]; ok { if d.uniqueKeys { if doneFields[info.Id] { d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.Line, name.String(), out.Type())) continue } doneFields[info.Id] = true } var field reflect.Value if info.Inline == nil { field = out.Field(info.Num) } else { field = d.fieldByIndex(n, out, info.Inline) } d.unmarshal(n.Content[i+1], field) } else if sinfo.InlineMap != -1 { if inlineMap.IsNil() { inlineMap.Set(reflect.MakeMap(inlineMap.Type())) } value := reflect.New(elemType).Elem() d.unmarshal(n.Content[i+1], value) inlineMap.SetMapIndex(name, value) } else if d.knownFields { d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type())) } } d.mergedFields = mergedFields if mergeNode != nil { d.merge(n, mergeNode, out) } return true } func failWantMap() { failf("map merge requires map or sequence of maps as the value") } func (d *decoder) merge(parent *Node, merge *Node, out reflect.Value) { mergedFields := d.mergedFields if mergedFields == nil { d.mergedFields = make(map[interface{}]bool) for i := 0; i < len(parent.Content); i += 2 { k := reflect.New(ifaceType).Elem() if d.unmarshal(parent.Content[i], k) { d.mergedFields[k.Interface()] = true } } } switch merge.Kind { case MappingNode: d.unmarshal(merge, out) case AliasNode: if merge.Alias != nil && merge.Alias.Kind != MappingNode { failWantMap() } d.unmarshal(merge, out) case SequenceNode: for i := 0; i < len(merge.Content); i++ { ni := merge.Content[i] if ni.Kind == AliasNode { if ni.Alias != nil && ni.Alias.Kind != MappingNode { failWantMap() } } else if ni.Kind != MappingNode { failWantMap() } d.unmarshal(ni, out) } default: failWantMap() } d.mergedFields = mergedFields } func isMerge(n *Node) bool { return n.Kind == ScalarNode && n.Value == "<<" && (n.Tag == "" || n.Tag == "!" || shortTag(n.Tag) == mergeTag) } ================================================ FILE: vendor/github.com/oasdiff/yaml3/emitterc.go ================================================ // // Copyright (c) 2011-2019 Canonical Ltd // Copyright (c) 2006-2010 Kirill Simonov // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package yaml import ( "bytes" "fmt" ) // Flush the buffer if needed. func flush(emitter *yaml_emitter_t) bool { if emitter.buffer_pos+5 >= len(emitter.buffer) { return yaml_emitter_flush(emitter) } return true } // Put a character to the output buffer. func put(emitter *yaml_emitter_t, value byte) bool { if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { return false } emitter.buffer[emitter.buffer_pos] = value emitter.buffer_pos++ emitter.column++ return true } // Put a line break to the output buffer. func put_break(emitter *yaml_emitter_t) bool { if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { return false } switch emitter.line_break { case yaml_CR_BREAK: emitter.buffer[emitter.buffer_pos] = '\r' emitter.buffer_pos += 1 case yaml_LN_BREAK: emitter.buffer[emitter.buffer_pos] = '\n' emitter.buffer_pos += 1 case yaml_CRLN_BREAK: emitter.buffer[emitter.buffer_pos+0] = '\r' emitter.buffer[emitter.buffer_pos+1] = '\n' emitter.buffer_pos += 2 default: panic("unknown line break setting") } if emitter.column == 0 { emitter.space_above = true } emitter.column = 0 emitter.line++ // [Go] Do this here and below and drop from everywhere else (see commented lines). emitter.indention = true return true } // Copy a character from a string into buffer. func write(emitter *yaml_emitter_t, s []byte, i *int) bool { if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { return false } p := emitter.buffer_pos w := width(s[*i]) switch w { case 4: emitter.buffer[p+3] = s[*i+3] fallthrough case 3: emitter.buffer[p+2] = s[*i+2] fallthrough case 2: emitter.buffer[p+1] = s[*i+1] fallthrough case 1: emitter.buffer[p+0] = s[*i+0] default: panic("unknown character width") } emitter.column++ emitter.buffer_pos += w *i += w return true } // Write a whole string into buffer. func write_all(emitter *yaml_emitter_t, s []byte) bool { for i := 0; i < len(s); { if !write(emitter, s, &i) { return false } } return true } // Copy a line break character from a string into buffer. func write_break(emitter *yaml_emitter_t, s []byte, i *int) bool { if s[*i] == '\n' { if !put_break(emitter) { return false } *i++ } else { if !write(emitter, s, i) { return false } if emitter.column == 0 { emitter.space_above = true } emitter.column = 0 emitter.line++ // [Go] Do this here and above and drop from everywhere else (see commented lines). emitter.indention = true } return true } // Set an emitter error and return false. func yaml_emitter_set_emitter_error(emitter *yaml_emitter_t, problem string) bool { emitter.error = yaml_EMITTER_ERROR emitter.problem = problem return false } // Emit an event. func yaml_emitter_emit(emitter *yaml_emitter_t, event *yaml_event_t) bool { emitter.events = append(emitter.events, *event) for !yaml_emitter_need_more_events(emitter) { event := &emitter.events[emitter.events_head] if !yaml_emitter_analyze_event(emitter, event) { return false } if !yaml_emitter_state_machine(emitter, event) { return false } yaml_event_delete(event) emitter.events_head++ } return true } // Check if we need to accumulate more events before emitting. // // We accumulate extra // - 1 event for DOCUMENT-START // - 2 events for SEQUENCE-START // - 3 events for MAPPING-START // func yaml_emitter_need_more_events(emitter *yaml_emitter_t) bool { if emitter.events_head == len(emitter.events) { return true } var accumulate int switch emitter.events[emitter.events_head].typ { case yaml_DOCUMENT_START_EVENT: accumulate = 1 break case yaml_SEQUENCE_START_EVENT: accumulate = 2 break case yaml_MAPPING_START_EVENT: accumulate = 3 break default: return false } if len(emitter.events)-emitter.events_head > accumulate { return false } var level int for i := emitter.events_head; i < len(emitter.events); i++ { switch emitter.events[i].typ { case yaml_STREAM_START_EVENT, yaml_DOCUMENT_START_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT: level++ case yaml_STREAM_END_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_END_EVENT, yaml_MAPPING_END_EVENT: level-- } if level == 0 { return false } } return true } // Append a directive to the directives stack. func yaml_emitter_append_tag_directive(emitter *yaml_emitter_t, value *yaml_tag_directive_t, allow_duplicates bool) bool { for i := 0; i < len(emitter.tag_directives); i++ { if bytes.Equal(value.handle, emitter.tag_directives[i].handle) { if allow_duplicates { return true } return yaml_emitter_set_emitter_error(emitter, "duplicate %TAG directive") } } // [Go] Do we actually need to copy this given garbage collection // and the lack of deallocating destructors? tag_copy := yaml_tag_directive_t{ handle: make([]byte, len(value.handle)), prefix: make([]byte, len(value.prefix)), } copy(tag_copy.handle, value.handle) copy(tag_copy.prefix, value.prefix) emitter.tag_directives = append(emitter.tag_directives, tag_copy) return true } // Increase the indentation level. func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool) bool { emitter.indents = append(emitter.indents, emitter.indent) if emitter.indent < 0 { if flow { emitter.indent = emitter.best_indent } else { emitter.indent = 0 } } else if !indentless { // [Go] This was changed so that indentations are more regular. if emitter.states[len(emitter.states)-1] == yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE { // The first indent inside a sequence will just skip the "- " indicator. emitter.indent += 2 } else { // Everything else aligns to the chosen indentation. emitter.indent = emitter.best_indent*((emitter.indent+emitter.best_indent)/emitter.best_indent) } } return true } // State dispatcher. func yaml_emitter_state_machine(emitter *yaml_emitter_t, event *yaml_event_t) bool { switch emitter.state { default: case yaml_EMIT_STREAM_START_STATE: return yaml_emitter_emit_stream_start(emitter, event) case yaml_EMIT_FIRST_DOCUMENT_START_STATE: return yaml_emitter_emit_document_start(emitter, event, true) case yaml_EMIT_DOCUMENT_START_STATE: return yaml_emitter_emit_document_start(emitter, event, false) case yaml_EMIT_DOCUMENT_CONTENT_STATE: return yaml_emitter_emit_document_content(emitter, event) case yaml_EMIT_DOCUMENT_END_STATE: return yaml_emitter_emit_document_end(emitter, event) case yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE: return yaml_emitter_emit_flow_sequence_item(emitter, event, true, false) case yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE: return yaml_emitter_emit_flow_sequence_item(emitter, event, false, true) case yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE: return yaml_emitter_emit_flow_sequence_item(emitter, event, false, false) case yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE: return yaml_emitter_emit_flow_mapping_key(emitter, event, true, false) case yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE: return yaml_emitter_emit_flow_mapping_key(emitter, event, false, true) case yaml_EMIT_FLOW_MAPPING_KEY_STATE: return yaml_emitter_emit_flow_mapping_key(emitter, event, false, false) case yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE: return yaml_emitter_emit_flow_mapping_value(emitter, event, true) case yaml_EMIT_FLOW_MAPPING_VALUE_STATE: return yaml_emitter_emit_flow_mapping_value(emitter, event, false) case yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE: return yaml_emitter_emit_block_sequence_item(emitter, event, true) case yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE: return yaml_emitter_emit_block_sequence_item(emitter, event, false) case yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE: return yaml_emitter_emit_block_mapping_key(emitter, event, true) case yaml_EMIT_BLOCK_MAPPING_KEY_STATE: return yaml_emitter_emit_block_mapping_key(emitter, event, false) case yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE: return yaml_emitter_emit_block_mapping_value(emitter, event, true) case yaml_EMIT_BLOCK_MAPPING_VALUE_STATE: return yaml_emitter_emit_block_mapping_value(emitter, event, false) case yaml_EMIT_END_STATE: return yaml_emitter_set_emitter_error(emitter, "expected nothing after STREAM-END") } panic("invalid emitter state") } // Expect STREAM-START. func yaml_emitter_emit_stream_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { if event.typ != yaml_STREAM_START_EVENT { return yaml_emitter_set_emitter_error(emitter, "expected STREAM-START") } if emitter.encoding == yaml_ANY_ENCODING { emitter.encoding = event.encoding if emitter.encoding == yaml_ANY_ENCODING { emitter.encoding = yaml_UTF8_ENCODING } } if emitter.best_indent < 2 || emitter.best_indent > 9 { emitter.best_indent = 2 } if emitter.best_width >= 0 && emitter.best_width <= emitter.best_indent*2 { emitter.best_width = 80 } if emitter.best_width < 0 { emitter.best_width = 1<<31 - 1 } if emitter.line_break == yaml_ANY_BREAK { emitter.line_break = yaml_LN_BREAK } emitter.indent = -1 emitter.line = 0 emitter.column = 0 emitter.whitespace = true emitter.indention = true emitter.space_above = true emitter.foot_indent = -1 if emitter.encoding != yaml_UTF8_ENCODING { if !yaml_emitter_write_bom(emitter) { return false } } emitter.state = yaml_EMIT_FIRST_DOCUMENT_START_STATE return true } // Expect DOCUMENT-START or STREAM-END. func yaml_emitter_emit_document_start(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { if event.typ == yaml_DOCUMENT_START_EVENT { if event.version_directive != nil { if !yaml_emitter_analyze_version_directive(emitter, event.version_directive) { return false } } for i := 0; i < len(event.tag_directives); i++ { tag_directive := &event.tag_directives[i] if !yaml_emitter_analyze_tag_directive(emitter, tag_directive) { return false } if !yaml_emitter_append_tag_directive(emitter, tag_directive, false) { return false } } for i := 0; i < len(default_tag_directives); i++ { tag_directive := &default_tag_directives[i] if !yaml_emitter_append_tag_directive(emitter, tag_directive, true) { return false } } implicit := event.implicit if !first || emitter.canonical { implicit = false } if emitter.open_ended && (event.version_directive != nil || len(event.tag_directives) > 0) { if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { return false } if !yaml_emitter_write_indent(emitter) { return false } } if event.version_directive != nil { implicit = false if !yaml_emitter_write_indicator(emitter, []byte("%YAML"), true, false, false) { return false } if !yaml_emitter_write_indicator(emitter, []byte("1.1"), true, false, false) { return false } if !yaml_emitter_write_indent(emitter) { return false } } if len(event.tag_directives) > 0 { implicit = false for i := 0; i < len(event.tag_directives); i++ { tag_directive := &event.tag_directives[i] if !yaml_emitter_write_indicator(emitter, []byte("%TAG"), true, false, false) { return false } if !yaml_emitter_write_tag_handle(emitter, tag_directive.handle) { return false } if !yaml_emitter_write_tag_content(emitter, tag_directive.prefix, true) { return false } if !yaml_emitter_write_indent(emitter) { return false } } } if yaml_emitter_check_empty_document(emitter) { implicit = false } if !implicit { if !yaml_emitter_write_indent(emitter) { return false } if !yaml_emitter_write_indicator(emitter, []byte("---"), true, false, false) { return false } if emitter.canonical || true { if !yaml_emitter_write_indent(emitter) { return false } } } if len(emitter.head_comment) > 0 { if !yaml_emitter_process_head_comment(emitter) { return false } if !put_break(emitter) { return false } } emitter.state = yaml_EMIT_DOCUMENT_CONTENT_STATE return true } if event.typ == yaml_STREAM_END_EVENT { if emitter.open_ended { if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { return false } if !yaml_emitter_write_indent(emitter) { return false } } if !yaml_emitter_flush(emitter) { return false } emitter.state = yaml_EMIT_END_STATE return true } return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-START or STREAM-END") } // Expect the root node. func yaml_emitter_emit_document_content(emitter *yaml_emitter_t, event *yaml_event_t) bool { emitter.states = append(emitter.states, yaml_EMIT_DOCUMENT_END_STATE) if !yaml_emitter_process_head_comment(emitter) { return false } if !yaml_emitter_emit_node(emitter, event, true, false, false, false) { return false } if !yaml_emitter_process_line_comment(emitter) { return false } if !yaml_emitter_process_foot_comment(emitter) { return false } return true } // Expect DOCUMENT-END. func yaml_emitter_emit_document_end(emitter *yaml_emitter_t, event *yaml_event_t) bool { if event.typ != yaml_DOCUMENT_END_EVENT { return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-END") } // [Go] Force document foot separation. emitter.foot_indent = 0 if !yaml_emitter_process_foot_comment(emitter) { return false } emitter.foot_indent = -1 if !yaml_emitter_write_indent(emitter) { return false } if !event.implicit { // [Go] Allocate the slice elsewhere. if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { return false } if !yaml_emitter_write_indent(emitter) { return false } } if !yaml_emitter_flush(emitter) { return false } emitter.state = yaml_EMIT_DOCUMENT_START_STATE emitter.tag_directives = emitter.tag_directives[:0] return true } // Expect a flow item node. func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first, trail bool) bool { if first { if !yaml_emitter_write_indicator(emitter, []byte{'['}, true, true, false) { return false } if !yaml_emitter_increase_indent(emitter, true, false) { return false } emitter.flow_level++ } if event.typ == yaml_SEQUENCE_END_EVENT { if emitter.canonical && !first && !trail { if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { return false } } emitter.flow_level-- emitter.indent = emitter.indents[len(emitter.indents)-1] emitter.indents = emitter.indents[:len(emitter.indents)-1] if emitter.column == 0 || emitter.canonical && !first { if !yaml_emitter_write_indent(emitter) { return false } } if !yaml_emitter_write_indicator(emitter, []byte{']'}, false, false, false) { return false } if !yaml_emitter_process_line_comment(emitter) { return false } if !yaml_emitter_process_foot_comment(emitter) { return false } emitter.state = emitter.states[len(emitter.states)-1] emitter.states = emitter.states[:len(emitter.states)-1] return true } if !first && !trail { if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { return false } } if !yaml_emitter_process_head_comment(emitter) { return false } if emitter.column == 0 { if !yaml_emitter_write_indent(emitter) { return false } } if emitter.canonical || emitter.column > emitter.best_width { if !yaml_emitter_write_indent(emitter) { return false } } if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 { emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE) } else { emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE) } if !yaml_emitter_emit_node(emitter, event, false, true, false, false) { return false } if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 { if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { return false } } if !yaml_emitter_process_line_comment(emitter) { return false } if !yaml_emitter_process_foot_comment(emitter) { return false } return true } // Expect a flow key node. func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first, trail bool) bool { if first { if !yaml_emitter_write_indicator(emitter, []byte{'{'}, true, true, false) { return false } if !yaml_emitter_increase_indent(emitter, true, false) { return false } emitter.flow_level++ } if event.typ == yaml_MAPPING_END_EVENT { if (emitter.canonical || len(emitter.head_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0) && !first && !trail { if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { return false } } if !yaml_emitter_process_head_comment(emitter) { return false } emitter.flow_level-- emitter.indent = emitter.indents[len(emitter.indents)-1] emitter.indents = emitter.indents[:len(emitter.indents)-1] if emitter.canonical && !first { if !yaml_emitter_write_indent(emitter) { return false } } if !yaml_emitter_write_indicator(emitter, []byte{'}'}, false, false, false) { return false } if !yaml_emitter_process_line_comment(emitter) { return false } if !yaml_emitter_process_foot_comment(emitter) { return false } emitter.state = emitter.states[len(emitter.states)-1] emitter.states = emitter.states[:len(emitter.states)-1] return true } if !first && !trail { if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { return false } } if !yaml_emitter_process_head_comment(emitter) { return false } if emitter.column == 0 { if !yaml_emitter_write_indent(emitter) { return false } } if emitter.canonical || emitter.column > emitter.best_width { if !yaml_emitter_write_indent(emitter) { return false } } if !emitter.canonical && yaml_emitter_check_simple_key(emitter) { emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE) return yaml_emitter_emit_node(emitter, event, false, false, true, true) } if !yaml_emitter_write_indicator(emitter, []byte{'?'}, true, false, false) { return false } emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_VALUE_STATE) return yaml_emitter_emit_node(emitter, event, false, false, true, false) } // Expect a flow value node. func yaml_emitter_emit_flow_mapping_value(emitter *yaml_emitter_t, event *yaml_event_t, simple bool) bool { if simple { if !yaml_emitter_write_indicator(emitter, []byte{':'}, false, false, false) { return false } } else { if emitter.canonical || emitter.column > emitter.best_width { if !yaml_emitter_write_indent(emitter) { return false } } if !yaml_emitter_write_indicator(emitter, []byte{':'}, true, false, false) { return false } } if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 { emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE) } else { emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_KEY_STATE) } if !yaml_emitter_emit_node(emitter, event, false, false, true, false) { return false } if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 { if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { return false } } if !yaml_emitter_process_line_comment(emitter) { return false } if !yaml_emitter_process_foot_comment(emitter) { return false } return true } // Expect a block item node. func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { if first { if !yaml_emitter_increase_indent(emitter, false, false) { return false } } if event.typ == yaml_SEQUENCE_END_EVENT { emitter.indent = emitter.indents[len(emitter.indents)-1] emitter.indents = emitter.indents[:len(emitter.indents)-1] emitter.state = emitter.states[len(emitter.states)-1] emitter.states = emitter.states[:len(emitter.states)-1] return true } if !yaml_emitter_process_head_comment(emitter) { return false } if !yaml_emitter_write_indent(emitter) { return false } if !yaml_emitter_write_indicator(emitter, []byte{'-'}, true, false, true) { return false } emitter.states = append(emitter.states, yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE) if !yaml_emitter_emit_node(emitter, event, false, true, false, false) { return false } if !yaml_emitter_process_line_comment(emitter) { return false } if !yaml_emitter_process_foot_comment(emitter) { return false } return true } // Expect a block key node. func yaml_emitter_emit_block_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { if first { if !yaml_emitter_increase_indent(emitter, false, false) { return false } } if !yaml_emitter_process_head_comment(emitter) { return false } if event.typ == yaml_MAPPING_END_EVENT { emitter.indent = emitter.indents[len(emitter.indents)-1] emitter.indents = emitter.indents[:len(emitter.indents)-1] emitter.state = emitter.states[len(emitter.states)-1] emitter.states = emitter.states[:len(emitter.states)-1] return true } if !yaml_emitter_write_indent(emitter) { return false } if len(emitter.line_comment) > 0 { // [Go] A line comment was provided for the key. That's unusual as the // scanner associates line comments with the value. Either way, // save the line comment and render it appropriately later. emitter.key_line_comment = emitter.line_comment emitter.line_comment = nil } if yaml_emitter_check_simple_key(emitter) { emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE) return yaml_emitter_emit_node(emitter, event, false, false, true, true) } if !yaml_emitter_write_indicator(emitter, []byte{'?'}, true, false, true) { return false } emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_VALUE_STATE) return yaml_emitter_emit_node(emitter, event, false, false, true, false) } // Expect a block value node. func yaml_emitter_emit_block_mapping_value(emitter *yaml_emitter_t, event *yaml_event_t, simple bool) bool { if simple { if !yaml_emitter_write_indicator(emitter, []byte{':'}, false, false, false) { return false } } else { if !yaml_emitter_write_indent(emitter) { return false } if !yaml_emitter_write_indicator(emitter, []byte{':'}, true, false, true) { return false } } if len(emitter.key_line_comment) > 0 { // [Go] Line comments are generally associated with the value, but when there's // no value on the same line as a mapping key they end up attached to the // key itself. if event.typ == yaml_SCALAR_EVENT { if len(emitter.line_comment) == 0 { // A scalar is coming and it has no line comments by itself yet, // so just let it handle the line comment as usual. If it has a // line comment, we can't have both so the one from the key is lost. emitter.line_comment = emitter.key_line_comment emitter.key_line_comment = nil } } else if event.sequence_style() != yaml_FLOW_SEQUENCE_STYLE && (event.typ == yaml_MAPPING_START_EVENT || event.typ == yaml_SEQUENCE_START_EVENT) { // An indented block follows, so write the comment right now. emitter.line_comment, emitter.key_line_comment = emitter.key_line_comment, emitter.line_comment if !yaml_emitter_process_line_comment(emitter) { return false } emitter.line_comment, emitter.key_line_comment = emitter.key_line_comment, emitter.line_comment } } emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_KEY_STATE) if !yaml_emitter_emit_node(emitter, event, false, false, true, false) { return false } if !yaml_emitter_process_line_comment(emitter) { return false } if !yaml_emitter_process_foot_comment(emitter) { return false } return true } func yaml_emitter_silent_nil_event(emitter *yaml_emitter_t, event *yaml_event_t) bool { return event.typ == yaml_SCALAR_EVENT && event.implicit && !emitter.canonical && len(emitter.scalar_data.value) == 0 } // Expect a node. func yaml_emitter_emit_node(emitter *yaml_emitter_t, event *yaml_event_t, root bool, sequence bool, mapping bool, simple_key bool) bool { emitter.root_context = root emitter.sequence_context = sequence emitter.mapping_context = mapping emitter.simple_key_context = simple_key switch event.typ { case yaml_ALIAS_EVENT: return yaml_emitter_emit_alias(emitter, event) case yaml_SCALAR_EVENT: return yaml_emitter_emit_scalar(emitter, event) case yaml_SEQUENCE_START_EVENT: return yaml_emitter_emit_sequence_start(emitter, event) case yaml_MAPPING_START_EVENT: return yaml_emitter_emit_mapping_start(emitter, event) default: return yaml_emitter_set_emitter_error(emitter, fmt.Sprintf("expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS, but got %v", event.typ)) } } // Expect ALIAS. func yaml_emitter_emit_alias(emitter *yaml_emitter_t, event *yaml_event_t) bool { if !yaml_emitter_process_anchor(emitter) { return false } emitter.state = emitter.states[len(emitter.states)-1] emitter.states = emitter.states[:len(emitter.states)-1] return true } // Expect SCALAR. func yaml_emitter_emit_scalar(emitter *yaml_emitter_t, event *yaml_event_t) bool { if !yaml_emitter_select_scalar_style(emitter, event) { return false } if !yaml_emitter_process_anchor(emitter) { return false } if !yaml_emitter_process_tag(emitter) { return false } if !yaml_emitter_increase_indent(emitter, true, false) { return false } if !yaml_emitter_process_scalar(emitter) { return false } emitter.indent = emitter.indents[len(emitter.indents)-1] emitter.indents = emitter.indents[:len(emitter.indents)-1] emitter.state = emitter.states[len(emitter.states)-1] emitter.states = emitter.states[:len(emitter.states)-1] return true } // Expect SEQUENCE-START. func yaml_emitter_emit_sequence_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { if !yaml_emitter_process_anchor(emitter) { return false } if !yaml_emitter_process_tag(emitter) { return false } if emitter.flow_level > 0 || emitter.canonical || event.sequence_style() == yaml_FLOW_SEQUENCE_STYLE || yaml_emitter_check_empty_sequence(emitter) { emitter.state = yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE } else { emitter.state = yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE } return true } // Expect MAPPING-START. func yaml_emitter_emit_mapping_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { if !yaml_emitter_process_anchor(emitter) { return false } if !yaml_emitter_process_tag(emitter) { return false } if emitter.flow_level > 0 || emitter.canonical || event.mapping_style() == yaml_FLOW_MAPPING_STYLE || yaml_emitter_check_empty_mapping(emitter) { emitter.state = yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE } else { emitter.state = yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE } return true } // Check if the document content is an empty scalar. func yaml_emitter_check_empty_document(emitter *yaml_emitter_t) bool { return false // [Go] Huh? } // Check if the next events represent an empty sequence. func yaml_emitter_check_empty_sequence(emitter *yaml_emitter_t) bool { if len(emitter.events)-emitter.events_head < 2 { return false } return emitter.events[emitter.events_head].typ == yaml_SEQUENCE_START_EVENT && emitter.events[emitter.events_head+1].typ == yaml_SEQUENCE_END_EVENT } // Check if the next events represent an empty mapping. func yaml_emitter_check_empty_mapping(emitter *yaml_emitter_t) bool { if len(emitter.events)-emitter.events_head < 2 { return false } return emitter.events[emitter.events_head].typ == yaml_MAPPING_START_EVENT && emitter.events[emitter.events_head+1].typ == yaml_MAPPING_END_EVENT } // Check if the next node can be expressed as a simple key. func yaml_emitter_check_simple_key(emitter *yaml_emitter_t) bool { length := 0 switch emitter.events[emitter.events_head].typ { case yaml_ALIAS_EVENT: length += len(emitter.anchor_data.anchor) case yaml_SCALAR_EVENT: if emitter.scalar_data.multiline { return false } length += len(emitter.anchor_data.anchor) + len(emitter.tag_data.handle) + len(emitter.tag_data.suffix) + len(emitter.scalar_data.value) case yaml_SEQUENCE_START_EVENT: if !yaml_emitter_check_empty_sequence(emitter) { return false } length += len(emitter.anchor_data.anchor) + len(emitter.tag_data.handle) + len(emitter.tag_data.suffix) case yaml_MAPPING_START_EVENT: if !yaml_emitter_check_empty_mapping(emitter) { return false } length += len(emitter.anchor_data.anchor) + len(emitter.tag_data.handle) + len(emitter.tag_data.suffix) default: return false } return length <= 128 } // Determine an acceptable scalar style. func yaml_emitter_select_scalar_style(emitter *yaml_emitter_t, event *yaml_event_t) bool { no_tag := len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0 if no_tag && !event.implicit && !event.quoted_implicit { return yaml_emitter_set_emitter_error(emitter, "neither tag nor implicit flags are specified") } style := event.scalar_style() if style == yaml_ANY_SCALAR_STYLE { style = yaml_PLAIN_SCALAR_STYLE } if emitter.canonical { style = yaml_DOUBLE_QUOTED_SCALAR_STYLE } if emitter.simple_key_context && emitter.scalar_data.multiline { style = yaml_DOUBLE_QUOTED_SCALAR_STYLE } if style == yaml_PLAIN_SCALAR_STYLE { if emitter.flow_level > 0 && !emitter.scalar_data.flow_plain_allowed || emitter.flow_level == 0 && !emitter.scalar_data.block_plain_allowed { style = yaml_SINGLE_QUOTED_SCALAR_STYLE } if len(emitter.scalar_data.value) == 0 && (emitter.flow_level > 0 || emitter.simple_key_context) { style = yaml_SINGLE_QUOTED_SCALAR_STYLE } if no_tag && !event.implicit { style = yaml_SINGLE_QUOTED_SCALAR_STYLE } } if style == yaml_SINGLE_QUOTED_SCALAR_STYLE { if !emitter.scalar_data.single_quoted_allowed { style = yaml_DOUBLE_QUOTED_SCALAR_STYLE } } if style == yaml_LITERAL_SCALAR_STYLE || style == yaml_FOLDED_SCALAR_STYLE { if !emitter.scalar_data.block_allowed || emitter.flow_level > 0 || emitter.simple_key_context { style = yaml_DOUBLE_QUOTED_SCALAR_STYLE } } if no_tag && !event.quoted_implicit && style != yaml_PLAIN_SCALAR_STYLE { emitter.tag_data.handle = []byte{'!'} } emitter.scalar_data.style = style return true } // Write an anchor. func yaml_emitter_process_anchor(emitter *yaml_emitter_t) bool { if emitter.anchor_data.anchor == nil { return true } c := []byte{'&'} if emitter.anchor_data.alias { c[0] = '*' } if !yaml_emitter_write_indicator(emitter, c, true, false, false) { return false } return yaml_emitter_write_anchor(emitter, emitter.anchor_data.anchor) } // Write a tag. func yaml_emitter_process_tag(emitter *yaml_emitter_t) bool { if len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0 { return true } if len(emitter.tag_data.handle) > 0 { if !yaml_emitter_write_tag_handle(emitter, emitter.tag_data.handle) { return false } if len(emitter.tag_data.suffix) > 0 { if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) { return false } } } else { // [Go] Allocate these slices elsewhere. if !yaml_emitter_write_indicator(emitter, []byte("!<"), true, false, false) { return false } if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) { return false } if !yaml_emitter_write_indicator(emitter, []byte{'>'}, false, false, false) { return false } } return true } // Write a scalar. func yaml_emitter_process_scalar(emitter *yaml_emitter_t) bool { switch emitter.scalar_data.style { case yaml_PLAIN_SCALAR_STYLE: return yaml_emitter_write_plain_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) case yaml_SINGLE_QUOTED_SCALAR_STYLE: return yaml_emitter_write_single_quoted_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) case yaml_DOUBLE_QUOTED_SCALAR_STYLE: return yaml_emitter_write_double_quoted_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) case yaml_LITERAL_SCALAR_STYLE: return yaml_emitter_write_literal_scalar(emitter, emitter.scalar_data.value) case yaml_FOLDED_SCALAR_STYLE: return yaml_emitter_write_folded_scalar(emitter, emitter.scalar_data.value) } panic("unknown scalar style") } // Write a head comment. func yaml_emitter_process_head_comment(emitter *yaml_emitter_t) bool { if len(emitter.tail_comment) > 0 { if !yaml_emitter_write_indent(emitter) { return false } if !yaml_emitter_write_comment(emitter, emitter.tail_comment) { return false } emitter.tail_comment = emitter.tail_comment[:0] emitter.foot_indent = emitter.indent if emitter.foot_indent < 0 { emitter.foot_indent = 0 } } if len(emitter.head_comment) == 0 { return true } if !yaml_emitter_write_indent(emitter) { return false } if !yaml_emitter_write_comment(emitter, emitter.head_comment) { return false } emitter.head_comment = emitter.head_comment[:0] return true } // Write an line comment. func yaml_emitter_process_line_comment(emitter *yaml_emitter_t) bool { if len(emitter.line_comment) == 0 { return true } if !emitter.whitespace { if !put(emitter, ' ') { return false } } if !yaml_emitter_write_comment(emitter, emitter.line_comment) { return false } emitter.line_comment = emitter.line_comment[:0] return true } // Write a foot comment. func yaml_emitter_process_foot_comment(emitter *yaml_emitter_t) bool { if len(emitter.foot_comment) == 0 { return true } if !yaml_emitter_write_indent(emitter) { return false } if !yaml_emitter_write_comment(emitter, emitter.foot_comment) { return false } emitter.foot_comment = emitter.foot_comment[:0] emitter.foot_indent = emitter.indent if emitter.foot_indent < 0 { emitter.foot_indent = 0 } return true } // Check if a %YAML directive is valid. func yaml_emitter_analyze_version_directive(emitter *yaml_emitter_t, version_directive *yaml_version_directive_t) bool { if version_directive.major != 1 || version_directive.minor != 1 { return yaml_emitter_set_emitter_error(emitter, "incompatible %YAML directive") } return true } // Check if a %TAG directive is valid. func yaml_emitter_analyze_tag_directive(emitter *yaml_emitter_t, tag_directive *yaml_tag_directive_t) bool { handle := tag_directive.handle prefix := tag_directive.prefix if len(handle) == 0 { return yaml_emitter_set_emitter_error(emitter, "tag handle must not be empty") } if handle[0] != '!' { return yaml_emitter_set_emitter_error(emitter, "tag handle must start with '!'") } if handle[len(handle)-1] != '!' { return yaml_emitter_set_emitter_error(emitter, "tag handle must end with '!'") } for i := 1; i < len(handle)-1; i += width(handle[i]) { if !is_alpha(handle, i) { return yaml_emitter_set_emitter_error(emitter, "tag handle must contain alphanumerical characters only") } } if len(prefix) == 0 { return yaml_emitter_set_emitter_error(emitter, "tag prefix must not be empty") } return true } // Check if an anchor is valid. func yaml_emitter_analyze_anchor(emitter *yaml_emitter_t, anchor []byte, alias bool) bool { if len(anchor) == 0 { problem := "anchor value must not be empty" if alias { problem = "alias value must not be empty" } return yaml_emitter_set_emitter_error(emitter, problem) } for i := 0; i < len(anchor); i += width(anchor[i]) { if !is_alpha(anchor, i) { problem := "anchor value must contain alphanumerical characters only" if alias { problem = "alias value must contain alphanumerical characters only" } return yaml_emitter_set_emitter_error(emitter, problem) } } emitter.anchor_data.anchor = anchor emitter.anchor_data.alias = alias return true } // Check if a tag is valid. func yaml_emitter_analyze_tag(emitter *yaml_emitter_t, tag []byte) bool { if len(tag) == 0 { return yaml_emitter_set_emitter_error(emitter, "tag value must not be empty") } for i := 0; i < len(emitter.tag_directives); i++ { tag_directive := &emitter.tag_directives[i] if bytes.HasPrefix(tag, tag_directive.prefix) { emitter.tag_data.handle = tag_directive.handle emitter.tag_data.suffix = tag[len(tag_directive.prefix):] return true } } emitter.tag_data.suffix = tag return true } // Check if a scalar is valid. func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool { var ( block_indicators = false flow_indicators = false line_breaks = false special_characters = false tab_characters = false leading_space = false leading_break = false trailing_space = false trailing_break = false break_space = false space_break = false preceded_by_whitespace = false followed_by_whitespace = false previous_space = false previous_break = false ) emitter.scalar_data.value = value if len(value) == 0 { emitter.scalar_data.multiline = false emitter.scalar_data.flow_plain_allowed = false emitter.scalar_data.block_plain_allowed = true emitter.scalar_data.single_quoted_allowed = true emitter.scalar_data.block_allowed = false return true } if len(value) >= 3 && ((value[0] == '-' && value[1] == '-' && value[2] == '-') || (value[0] == '.' && value[1] == '.' && value[2] == '.')) { block_indicators = true flow_indicators = true } preceded_by_whitespace = true for i, w := 0, 0; i < len(value); i += w { w = width(value[i]) followed_by_whitespace = i+w >= len(value) || is_blank(value, i+w) if i == 0 { switch value[i] { case '#', ',', '[', ']', '{', '}', '&', '*', '!', '|', '>', '\'', '"', '%', '@', '`': flow_indicators = true block_indicators = true case '?', ':': flow_indicators = true if followed_by_whitespace { block_indicators = true } case '-': if followed_by_whitespace { flow_indicators = true block_indicators = true } } } else { switch value[i] { case ',', '?', '[', ']', '{', '}': flow_indicators = true case ':': flow_indicators = true if followed_by_whitespace { block_indicators = true } case '#': if preceded_by_whitespace { flow_indicators = true block_indicators = true } } } if value[i] == '\t' { tab_characters = true } else if !is_printable(value, i) || !is_ascii(value, i) && !emitter.unicode { special_characters = true } if is_space(value, i) { if i == 0 { leading_space = true } if i+width(value[i]) == len(value) { trailing_space = true } if previous_break { break_space = true } previous_space = true previous_break = false } else if is_break(value, i) { line_breaks = true if i == 0 { leading_break = true } if i+width(value[i]) == len(value) { trailing_break = true } if previous_space { space_break = true } previous_space = false previous_break = true } else { previous_space = false previous_break = false } // [Go]: Why 'z'? Couldn't be the end of the string as that's the loop condition. preceded_by_whitespace = is_blankz(value, i) } emitter.scalar_data.multiline = line_breaks emitter.scalar_data.flow_plain_allowed = true emitter.scalar_data.block_plain_allowed = true emitter.scalar_data.single_quoted_allowed = true emitter.scalar_data.block_allowed = true if leading_space || leading_break || trailing_space || trailing_break { emitter.scalar_data.flow_plain_allowed = false emitter.scalar_data.block_plain_allowed = false } if trailing_space { emitter.scalar_data.block_allowed = false } if break_space { emitter.scalar_data.flow_plain_allowed = false emitter.scalar_data.block_plain_allowed = false emitter.scalar_data.single_quoted_allowed = false } if space_break || tab_characters || special_characters { emitter.scalar_data.flow_plain_allowed = false emitter.scalar_data.block_plain_allowed = false emitter.scalar_data.single_quoted_allowed = false } if space_break || special_characters { emitter.scalar_data.block_allowed = false } if line_breaks { emitter.scalar_data.flow_plain_allowed = false emitter.scalar_data.block_plain_allowed = false } if flow_indicators { emitter.scalar_data.flow_plain_allowed = false } if block_indicators { emitter.scalar_data.block_plain_allowed = false } return true } // Check if the event data is valid. func yaml_emitter_analyze_event(emitter *yaml_emitter_t, event *yaml_event_t) bool { emitter.anchor_data.anchor = nil emitter.tag_data.handle = nil emitter.tag_data.suffix = nil emitter.scalar_data.value = nil if len(event.head_comment) > 0 { emitter.head_comment = event.head_comment } if len(event.line_comment) > 0 { emitter.line_comment = event.line_comment } if len(event.foot_comment) > 0 { emitter.foot_comment = event.foot_comment } if len(event.tail_comment) > 0 { emitter.tail_comment = event.tail_comment } switch event.typ { case yaml_ALIAS_EVENT: if !yaml_emitter_analyze_anchor(emitter, event.anchor, true) { return false } case yaml_SCALAR_EVENT: if len(event.anchor) > 0 { if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { return false } } if len(event.tag) > 0 && (emitter.canonical || (!event.implicit && !event.quoted_implicit)) { if !yaml_emitter_analyze_tag(emitter, event.tag) { return false } } if !yaml_emitter_analyze_scalar(emitter, event.value) { return false } case yaml_SEQUENCE_START_EVENT: if len(event.anchor) > 0 { if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { return false } } if len(event.tag) > 0 && (emitter.canonical || !event.implicit) { if !yaml_emitter_analyze_tag(emitter, event.tag) { return false } } case yaml_MAPPING_START_EVENT: if len(event.anchor) > 0 { if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { return false } } if len(event.tag) > 0 && (emitter.canonical || !event.implicit) { if !yaml_emitter_analyze_tag(emitter, event.tag) { return false } } } return true } // Write the BOM character. func yaml_emitter_write_bom(emitter *yaml_emitter_t) bool { if !flush(emitter) { return false } pos := emitter.buffer_pos emitter.buffer[pos+0] = '\xEF' emitter.buffer[pos+1] = '\xBB' emitter.buffer[pos+2] = '\xBF' emitter.buffer_pos += 3 return true } func yaml_emitter_write_indent(emitter *yaml_emitter_t) bool { indent := emitter.indent if indent < 0 { indent = 0 } if !emitter.indention || emitter.column > indent || (emitter.column == indent && !emitter.whitespace) { if !put_break(emitter) { return false } } if emitter.foot_indent == indent { if !put_break(emitter) { return false } } for emitter.column < indent { if !put(emitter, ' ') { return false } } emitter.whitespace = true //emitter.indention = true emitter.space_above = false emitter.foot_indent = -1 return true } func yaml_emitter_write_indicator(emitter *yaml_emitter_t, indicator []byte, need_whitespace, is_whitespace, is_indention bool) bool { if need_whitespace && !emitter.whitespace { if !put(emitter, ' ') { return false } } if !write_all(emitter, indicator) { return false } emitter.whitespace = is_whitespace emitter.indention = (emitter.indention && is_indention) emitter.open_ended = false return true } func yaml_emitter_write_anchor(emitter *yaml_emitter_t, value []byte) bool { if !write_all(emitter, value) { return false } emitter.whitespace = false emitter.indention = false return true } func yaml_emitter_write_tag_handle(emitter *yaml_emitter_t, value []byte) bool { if !emitter.whitespace { if !put(emitter, ' ') { return false } } if !write_all(emitter, value) { return false } emitter.whitespace = false emitter.indention = false return true } func yaml_emitter_write_tag_content(emitter *yaml_emitter_t, value []byte, need_whitespace bool) bool { if need_whitespace && !emitter.whitespace { if !put(emitter, ' ') { return false } } for i := 0; i < len(value); { var must_write bool switch value[i] { case ';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '_', '.', '~', '*', '\'', '(', ')', '[', ']': must_write = true default: must_write = is_alpha(value, i) } if must_write { if !write(emitter, value, &i) { return false } } else { w := width(value[i]) for k := 0; k < w; k++ { octet := value[i] i++ if !put(emitter, '%') { return false } c := octet >> 4 if c < 10 { c += '0' } else { c += 'A' - 10 } if !put(emitter, c) { return false } c = octet & 0x0f if c < 10 { c += '0' } else { c += 'A' - 10 } if !put(emitter, c) { return false } } } } emitter.whitespace = false emitter.indention = false return true } func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { if len(value) > 0 && !emitter.whitespace { if !put(emitter, ' ') { return false } } spaces := false breaks := false for i := 0; i < len(value); { if is_space(value, i) { if allow_breaks && !spaces && emitter.column > emitter.best_width && !is_space(value, i+1) { if !yaml_emitter_write_indent(emitter) { return false } i += width(value[i]) } else { if !write(emitter, value, &i) { return false } } spaces = true } else if is_break(value, i) { if !breaks && value[i] == '\n' { if !put_break(emitter) { return false } } if !write_break(emitter, value, &i) { return false } //emitter.indention = true breaks = true } else { if breaks { if !yaml_emitter_write_indent(emitter) { return false } } if !write(emitter, value, &i) { return false } emitter.indention = false spaces = false breaks = false } } if len(value) > 0 { emitter.whitespace = false } emitter.indention = false if emitter.root_context { emitter.open_ended = true } return true } func yaml_emitter_write_single_quoted_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { if !yaml_emitter_write_indicator(emitter, []byte{'\''}, true, false, false) { return false } spaces := false breaks := false for i := 0; i < len(value); { if is_space(value, i) { if allow_breaks && !spaces && emitter.column > emitter.best_width && i > 0 && i < len(value)-1 && !is_space(value, i+1) { if !yaml_emitter_write_indent(emitter) { return false } i += width(value[i]) } else { if !write(emitter, value, &i) { return false } } spaces = true } else if is_break(value, i) { if !breaks && value[i] == '\n' { if !put_break(emitter) { return false } } if !write_break(emitter, value, &i) { return false } //emitter.indention = true breaks = true } else { if breaks { if !yaml_emitter_write_indent(emitter) { return false } } if value[i] == '\'' { if !put(emitter, '\'') { return false } } if !write(emitter, value, &i) { return false } emitter.indention = false spaces = false breaks = false } } if !yaml_emitter_write_indicator(emitter, []byte{'\''}, false, false, false) { return false } emitter.whitespace = false emitter.indention = false return true } func yaml_emitter_write_double_quoted_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { spaces := false if !yaml_emitter_write_indicator(emitter, []byte{'"'}, true, false, false) { return false } for i := 0; i < len(value); { if !is_printable(value, i) || (!emitter.unicode && !is_ascii(value, i)) || is_bom(value, i) || is_break(value, i) || value[i] == '"' || value[i] == '\\' { octet := value[i] var w int var v rune switch { case octet&0x80 == 0x00: w, v = 1, rune(octet&0x7F) case octet&0xE0 == 0xC0: w, v = 2, rune(octet&0x1F) case octet&0xF0 == 0xE0: w, v = 3, rune(octet&0x0F) case octet&0xF8 == 0xF0: w, v = 4, rune(octet&0x07) } for k := 1; k < w; k++ { octet = value[i+k] v = (v << 6) + (rune(octet) & 0x3F) } i += w if !put(emitter, '\\') { return false } var ok bool switch v { case 0x00: ok = put(emitter, '0') case 0x07: ok = put(emitter, 'a') case 0x08: ok = put(emitter, 'b') case 0x09: ok = put(emitter, 't') case 0x0A: ok = put(emitter, 'n') case 0x0b: ok = put(emitter, 'v') case 0x0c: ok = put(emitter, 'f') case 0x0d: ok = put(emitter, 'r') case 0x1b: ok = put(emitter, 'e') case 0x22: ok = put(emitter, '"') case 0x5c: ok = put(emitter, '\\') case 0x85: ok = put(emitter, 'N') case 0xA0: ok = put(emitter, '_') case 0x2028: ok = put(emitter, 'L') case 0x2029: ok = put(emitter, 'P') default: if v <= 0xFF { ok = put(emitter, 'x') w = 2 } else if v <= 0xFFFF { ok = put(emitter, 'u') w = 4 } else { ok = put(emitter, 'U') w = 8 } for k := (w - 1) * 4; ok && k >= 0; k -= 4 { digit := byte((v >> uint(k)) & 0x0F) if digit < 10 { ok = put(emitter, digit+'0') } else { ok = put(emitter, digit+'A'-10) } } } if !ok { return false } spaces = false } else if is_space(value, i) { if allow_breaks && !spaces && emitter.column > emitter.best_width && i > 0 && i < len(value)-1 { if !yaml_emitter_write_indent(emitter) { return false } if is_space(value, i+1) { if !put(emitter, '\\') { return false } } i += width(value[i]) } else if !write(emitter, value, &i) { return false } spaces = true } else { if !write(emitter, value, &i) { return false } spaces = false } } if !yaml_emitter_write_indicator(emitter, []byte{'"'}, false, false, false) { return false } emitter.whitespace = false emitter.indention = false return true } func yaml_emitter_write_block_scalar_hints(emitter *yaml_emitter_t, value []byte) bool { if is_space(value, 0) || is_break(value, 0) { indent_hint := []byte{'0' + byte(emitter.best_indent)} if !yaml_emitter_write_indicator(emitter, indent_hint, false, false, false) { return false } } emitter.open_ended = false var chomp_hint [1]byte if len(value) == 0 { chomp_hint[0] = '-' } else { i := len(value) - 1 for value[i]&0xC0 == 0x80 { i-- } if !is_break(value, i) { chomp_hint[0] = '-' } else if i == 0 { chomp_hint[0] = '+' emitter.open_ended = true } else { i-- for value[i]&0xC0 == 0x80 { i-- } if is_break(value, i) { chomp_hint[0] = '+' emitter.open_ended = true } } } if chomp_hint[0] != 0 { if !yaml_emitter_write_indicator(emitter, chomp_hint[:], false, false, false) { return false } } return true } func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bool { if !yaml_emitter_write_indicator(emitter, []byte{'|'}, true, false, false) { return false } if !yaml_emitter_write_block_scalar_hints(emitter, value) { return false } if !yaml_emitter_process_line_comment(emitter) { return false } //emitter.indention = true emitter.whitespace = true breaks := true for i := 0; i < len(value); { if is_break(value, i) { if !write_break(emitter, value, &i) { return false } //emitter.indention = true breaks = true } else { if breaks { if !yaml_emitter_write_indent(emitter) { return false } } if !write(emitter, value, &i) { return false } emitter.indention = false breaks = false } } return true } func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) bool { if !yaml_emitter_write_indicator(emitter, []byte{'>'}, true, false, false) { return false } if !yaml_emitter_write_block_scalar_hints(emitter, value) { return false } if !yaml_emitter_process_line_comment(emitter) { return false } //emitter.indention = true emitter.whitespace = true breaks := true leading_spaces := true for i := 0; i < len(value); { if is_break(value, i) { if !breaks && !leading_spaces && value[i] == '\n' { k := 0 for is_break(value, k) { k += width(value[k]) } if !is_blankz(value, k) { if !put_break(emitter) { return false } } } if !write_break(emitter, value, &i) { return false } //emitter.indention = true breaks = true } else { if breaks { if !yaml_emitter_write_indent(emitter) { return false } leading_spaces = is_blank(value, i) } if !breaks && is_space(value, i) && !is_space(value, i+1) && emitter.column > emitter.best_width { if !yaml_emitter_write_indent(emitter) { return false } i += width(value[i]) } else { if !write(emitter, value, &i) { return false } } emitter.indention = false breaks = false } } return true } func yaml_emitter_write_comment(emitter *yaml_emitter_t, comment []byte) bool { breaks := false pound := false for i := 0; i < len(comment); { if is_break(comment, i) { if !write_break(emitter, comment, &i) { return false } //emitter.indention = true breaks = true pound = false } else { if breaks && !yaml_emitter_write_indent(emitter) { return false } if !pound { if comment[i] != '#' && (!put(emitter, '#') || !put(emitter, ' ')) { return false } pound = true } if !write(emitter, comment, &i) { return false } emitter.indention = false breaks = false } } if !breaks && !put_break(emitter) { return false } emitter.whitespace = true //emitter.indention = true return true } ================================================ FILE: vendor/github.com/oasdiff/yaml3/encode.go ================================================ // // Copyright (c) 2011-2019 Canonical Ltd // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package yaml import ( "encoding" "fmt" "io" "reflect" "regexp" "sort" "strconv" "strings" "time" "unicode/utf8" ) type encoder struct { emitter yaml_emitter_t event yaml_event_t out []byte flow bool indent int doneInit bool } func newEncoder() *encoder { e := &encoder{} yaml_emitter_initialize(&e.emitter) yaml_emitter_set_output_string(&e.emitter, &e.out) yaml_emitter_set_unicode(&e.emitter, true) return e } func newEncoderWithWriter(w io.Writer) *encoder { e := &encoder{} yaml_emitter_initialize(&e.emitter) yaml_emitter_set_output_writer(&e.emitter, w) yaml_emitter_set_unicode(&e.emitter, true) return e } func (e *encoder) init() { if e.doneInit { return } if e.indent == 0 { e.indent = 4 } e.emitter.best_indent = e.indent yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING) e.emit() e.doneInit = true } func (e *encoder) finish() { e.emitter.open_ended = false yaml_stream_end_event_initialize(&e.event) e.emit() } func (e *encoder) destroy() { yaml_emitter_delete(&e.emitter) } func (e *encoder) emit() { // This will internally delete the e.event value. e.must(yaml_emitter_emit(&e.emitter, &e.event)) } func (e *encoder) must(ok bool) { if !ok { msg := e.emitter.problem if msg == "" { msg = "unknown problem generating YAML content" } failf("%s", msg) } } func (e *encoder) marshalDoc(tag string, in reflect.Value) { e.init() var node *Node if in.IsValid() { node, _ = in.Interface().(*Node) } if node != nil && node.Kind == DocumentNode { e.nodev(in) } else { yaml_document_start_event_initialize(&e.event, nil, nil, true) e.emit() e.marshal(tag, in) yaml_document_end_event_initialize(&e.event, true) e.emit() } } func (e *encoder) marshal(tag string, in reflect.Value) { tag = shortTag(tag) if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() { e.nilv() return } iface := in.Interface() switch value := iface.(type) { case *Node: e.nodev(in) return case Node: if !in.CanAddr() { var n = reflect.New(in.Type()).Elem() n.Set(in) in = n } e.nodev(in.Addr()) return case time.Time: e.timev(tag, in) return case *time.Time: e.timev(tag, in.Elem()) return case time.Duration: e.stringv(tag, reflect.ValueOf(value.String())) return case Marshaler: v, err := value.MarshalYAML() if err != nil { fail(err) } if v == nil { e.nilv() return } e.marshal(tag, reflect.ValueOf(v)) return case encoding.TextMarshaler: text, err := value.MarshalText() if err != nil { fail(err) } in = reflect.ValueOf(string(text)) case nil: e.nilv() return } switch in.Kind() { case reflect.Interface: e.marshal(tag, in.Elem()) case reflect.Map: e.mapv(tag, in) case reflect.Ptr: e.marshal(tag, in.Elem()) case reflect.Struct: e.structv(tag, in) case reflect.Slice, reflect.Array: e.slicev(tag, in) case reflect.String: e.stringv(tag, in) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: e.intv(tag, in) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: e.uintv(tag, in) case reflect.Float32, reflect.Float64: e.floatv(tag, in) case reflect.Bool: e.boolv(tag, in) default: panic("cannot marshal type: " + in.Type().String()) } } func (e *encoder) mapv(tag string, in reflect.Value) { e.mappingv(tag, func() { keys := keyList(in.MapKeys()) sort.Sort(keys) for _, k := range keys { e.marshal("", k) e.marshal("", in.MapIndex(k)) } }) } func (e *encoder) fieldByIndex(v reflect.Value, index []int) (field reflect.Value) { for _, num := range index { for { if v.Kind() == reflect.Ptr { if v.IsNil() { return reflect.Value{} } v = v.Elem() continue } break } v = v.Field(num) } return v } func (e *encoder) structv(tag string, in reflect.Value) { sinfo, err := getStructInfo(in.Type()) if err != nil { panic(err) } e.mappingv(tag, func() { for _, info := range sinfo.FieldsList { var value reflect.Value if info.Inline == nil { value = in.Field(info.Num) } else { value = e.fieldByIndex(in, info.Inline) if !value.IsValid() { continue } } if info.OmitEmpty && isZero(value) { continue } e.marshal("", reflect.ValueOf(info.Key)) e.flow = info.Flow e.marshal("", value) } if sinfo.InlineMap >= 0 { m := in.Field(sinfo.InlineMap) if m.Len() > 0 { e.flow = false keys := keyList(m.MapKeys()) sort.Sort(keys) for _, k := range keys { if _, found := sinfo.FieldsMap[k.String()]; found { panic(fmt.Sprintf("cannot have key %q in inlined map: conflicts with struct field", k.String())) } e.marshal("", k) e.flow = false e.marshal("", m.MapIndex(k)) } } } }) } func (e *encoder) mappingv(tag string, f func()) { implicit := tag == "" style := yaml_BLOCK_MAPPING_STYLE if e.flow { e.flow = false style = yaml_FLOW_MAPPING_STYLE } yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style) e.emit() f() yaml_mapping_end_event_initialize(&e.event) e.emit() } func (e *encoder) slicev(tag string, in reflect.Value) { implicit := tag == "" style := yaml_BLOCK_SEQUENCE_STYLE if e.flow { e.flow = false style = yaml_FLOW_SEQUENCE_STYLE } e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) e.emit() n := in.Len() for i := 0; i < n; i++ { e.marshal("", in.Index(i)) } e.must(yaml_sequence_end_event_initialize(&e.event)) e.emit() } // isBase60 returns whether s is in base 60 notation as defined in YAML 1.1. // // The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported // in YAML 1.2 and by this package, but these should be marshalled quoted for // the time being for compatibility with other parsers. func isBase60Float(s string) (result bool) { // Fast path. if s == "" { return false } c := s[0] if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 { return false } // Do the full match. return base60float.MatchString(s) } // From http://yaml.org/type/float.html, except the regular expression there // is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix. var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`) // isOldBool returns whether s is bool notation as defined in YAML 1.1. // // We continue to force strings that YAML 1.1 would interpret as booleans to be // rendered as quotes strings so that the marshalled output valid for YAML 1.1 // parsing. func isOldBool(s string) (result bool) { switch s { case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON", "n", "N", "no", "No", "NO", "off", "Off", "OFF": return true default: return false } } func (e *encoder) stringv(tag string, in reflect.Value) { var style yaml_scalar_style_t s := in.String() canUsePlain := true switch { case !utf8.ValidString(s): if tag == binaryTag { failf("explicitly tagged !!binary data must be base64-encoded") } if tag != "" { failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) } // It can't be encoded directly as YAML so use a binary tag // and encode it as base64. tag = binaryTag s = encodeBase64(s) case tag == "": // Check to see if it would resolve to a specific // tag when encoded unquoted. If it doesn't, // there's no need to quote it. rtag, _ := resolve("", s) canUsePlain = rtag == strTag && !(isBase60Float(s) || isOldBool(s)) } // Note: it's possible for user code to emit invalid YAML // if they explicitly specify a tag and a string containing // text that's incompatible with that tag. switch { case strings.Contains(s, "\n"): if e.flow { style = yaml_DOUBLE_QUOTED_SCALAR_STYLE } else { style = yaml_LITERAL_SCALAR_STYLE } case canUsePlain: style = yaml_PLAIN_SCALAR_STYLE default: style = yaml_DOUBLE_QUOTED_SCALAR_STYLE } e.emitScalar(s, "", tag, style, nil, nil, nil, nil) } func (e *encoder) boolv(tag string, in reflect.Value) { var s string if in.Bool() { s = "true" } else { s = "false" } e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) } func (e *encoder) intv(tag string, in reflect.Value) { s := strconv.FormatInt(in.Int(), 10) e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) } func (e *encoder) uintv(tag string, in reflect.Value) { s := strconv.FormatUint(in.Uint(), 10) e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) } func (e *encoder) timev(tag string, in reflect.Value) { t := in.Interface().(time.Time) s := t.Format(time.RFC3339Nano) e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) } func (e *encoder) floatv(tag string, in reflect.Value) { // Issue #352: When formatting, use the precision of the underlying value precision := 64 if in.Kind() == reflect.Float32 { precision = 32 } s := strconv.FormatFloat(in.Float(), 'g', -1, precision) switch s { case "+Inf": s = ".inf" case "-Inf": s = "-.inf" case "NaN": s = ".nan" } e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) } func (e *encoder) nilv() { e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) } func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t, head, line, foot, tail []byte) { // TODO Kill this function. Replace all initialize calls by their underlining Go literals. implicit := tag == "" if !implicit { tag = longTag(tag) } e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) e.event.head_comment = head e.event.line_comment = line e.event.foot_comment = foot e.event.tail_comment = tail e.emit() } func (e *encoder) nodev(in reflect.Value) { e.node(in.Interface().(*Node), "") } func (e *encoder) node(node *Node, tail string) { // Zero nodes behave as nil. if node.Kind == 0 && node.IsZero() { e.nilv() return } // If the tag was not explicitly requested, and dropping it won't change the // implicit tag of the value, don't include it in the presentation. var tag = node.Tag var stag = shortTag(tag) var forceQuoting bool if tag != "" && node.Style&TaggedStyle == 0 { if node.Kind == ScalarNode { if stag == strTag && node.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0 { tag = "" } else { rtag, _ := resolve("", node.Value) if rtag == stag { tag = "" } else if stag == strTag { tag = "" forceQuoting = true } } } else { var rtag string switch node.Kind { case MappingNode: rtag = mapTag case SequenceNode: rtag = seqTag } if rtag == stag { tag = "" } } } switch node.Kind { case DocumentNode: yaml_document_start_event_initialize(&e.event, nil, nil, true) e.event.head_comment = []byte(node.HeadComment) e.emit() for _, node := range node.Content { e.node(node, "") } yaml_document_end_event_initialize(&e.event, true) e.event.foot_comment = []byte(node.FootComment) e.emit() case SequenceNode: style := yaml_BLOCK_SEQUENCE_STYLE if node.Style&FlowStyle != 0 { style = yaml_FLOW_SEQUENCE_STYLE } e.must(yaml_sequence_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style)) e.event.head_comment = []byte(node.HeadComment) e.emit() for _, node := range node.Content { e.node(node, "") } e.must(yaml_sequence_end_event_initialize(&e.event)) e.event.line_comment = []byte(node.LineComment) e.event.foot_comment = []byte(node.FootComment) e.emit() case MappingNode: style := yaml_BLOCK_MAPPING_STYLE if node.Style&FlowStyle != 0 { style = yaml_FLOW_MAPPING_STYLE } yaml_mapping_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style) e.event.tail_comment = []byte(tail) e.event.head_comment = []byte(node.HeadComment) e.emit() // The tail logic below moves the foot comment of prior keys to the following key, // since the value for each key may be a nested structure and the foot needs to be // processed only the entirety of the value is streamed. The last tail is processed // with the mapping end event. var tail string for i := 0; i+1 < len(node.Content); i += 2 { k := node.Content[i] foot := k.FootComment if foot != "" { kopy := *k kopy.FootComment = "" k = &kopy } e.node(k, tail) tail = foot v := node.Content[i+1] e.node(v, "") } yaml_mapping_end_event_initialize(&e.event) e.event.tail_comment = []byte(tail) e.event.line_comment = []byte(node.LineComment) e.event.foot_comment = []byte(node.FootComment) e.emit() case AliasNode: yaml_alias_event_initialize(&e.event, []byte(node.Value)) e.event.head_comment = []byte(node.HeadComment) e.event.line_comment = []byte(node.LineComment) e.event.foot_comment = []byte(node.FootComment) e.emit() case ScalarNode: value := node.Value if !utf8.ValidString(value) { if stag == binaryTag { failf("explicitly tagged !!binary data must be base64-encoded") } if stag != "" { failf("cannot marshal invalid UTF-8 data as %s", stag) } // It can't be encoded directly as YAML so use a binary tag // and encode it as base64. tag = binaryTag value = encodeBase64(value) } style := yaml_PLAIN_SCALAR_STYLE switch { case node.Style&DoubleQuotedStyle != 0: style = yaml_DOUBLE_QUOTED_SCALAR_STYLE case node.Style&SingleQuotedStyle != 0: style = yaml_SINGLE_QUOTED_SCALAR_STYLE case node.Style&LiteralStyle != 0: style = yaml_LITERAL_SCALAR_STYLE case node.Style&FoldedStyle != 0: style = yaml_FOLDED_SCALAR_STYLE case strings.Contains(value, "\n"): style = yaml_LITERAL_SCALAR_STYLE case forceQuoting: style = yaml_DOUBLE_QUOTED_SCALAR_STYLE } e.emitScalar(value, node.Anchor, tag, style, []byte(node.HeadComment), []byte(node.LineComment), []byte(node.FootComment), []byte(tail)) default: failf("cannot encode node with unknown kind %d", node.Kind) } } ================================================ FILE: vendor/github.com/oasdiff/yaml3/origin.go ================================================ package yaml import "fmt" const originTag = "__origin__" func isScalar(n *Node) bool { return n.Kind == ScalarNode } func addOriginInSeq(n *Node) *Node { if n.Kind != MappingNode { return n } // in case of a sequence, we use the first element as the key return addOrigin(n.Content[0], n) } func addOriginInMap(key, n *Node) *Node { if n.Kind != MappingNode { return n } return addOrigin(key, n) } func addOrigin(key, n *Node) *Node { if isOrigin(key) { return n } n.Content = append(n.Content, getNamedMap(originTag, append(getKeyLocation(key), getNamedMap("fields", getFieldLocations(n))...))...) return n } func getFieldLocations(n *Node) []*Node { l := len(n.Content) size := 0 for i := 0; i < l; i += 2 { if isScalar(n.Content[i+1]) { size += 2 } } nodes := make([]*Node, 0, size) for i := 0; i < l; i += 2 { if isScalar(n.Content[i+1]) { nodes = append(nodes, getNodeLocation(n.Content[i])...) } } return nodes } // isOrigin returns true if the key is an "origin" element // the current implementation is not optimal, as it relies on the key's line number // a better design would be to use a dedicated field in the Node struct func isOrigin(key *Node) bool { return key.Line == 0 } func getNodeLocation(n *Node) []*Node { return getNamedMap(n.Value, getLocationObject(n)) } func getKeyLocation(n *Node) []*Node { return getNamedMap("key", getLocationObject(n)) } func getNamedMap(title string, content []*Node) []*Node { if len(content) == 0 { return nil } return []*Node{ { Kind: ScalarNode, Tag: "!!str", Value: title, }, getMap(content), } } func getMap(content []*Node) *Node { return &Node{ Kind: MappingNode, Tag: "!!map", Content: content, } } func getLocationObject(key *Node) []*Node { return []*Node{ { Kind: ScalarNode, Tag: "!!str", Value: "line", }, { Kind: ScalarNode, Tag: "!!int", Value: fmt.Sprintf("%d", key.Line), }, { Kind: ScalarNode, Tag: "!!str", Value: "column", }, { Kind: ScalarNode, Tag: "!!int", Value: fmt.Sprintf("%d", key.Column), }, { Kind: ScalarNode, Tag: "!!str", Value: "name", }, { Kind: ScalarNode, Tag: "!!string", Value: key.Value, }, } } ================================================ FILE: vendor/github.com/oasdiff/yaml3/parserc.go ================================================ // // Copyright (c) 2011-2019 Canonical Ltd // Copyright (c) 2006-2010 Kirill Simonov // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package yaml import ( "bytes" ) // The parser implements the following grammar: // // stream ::= STREAM-START implicit_document? explicit_document* STREAM-END // implicit_document ::= block_node DOCUMENT-END* // explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* // block_node_or_indentless_sequence ::= // ALIAS // | properties (block_content | indentless_block_sequence)? // | block_content // | indentless_block_sequence // block_node ::= ALIAS // | properties block_content? // | block_content // flow_node ::= ALIAS // | properties flow_content? // | flow_content // properties ::= TAG ANCHOR? | ANCHOR TAG? // block_content ::= block_collection | flow_collection | SCALAR // flow_content ::= flow_collection | SCALAR // block_collection ::= block_sequence | block_mapping // flow_collection ::= flow_sequence | flow_mapping // block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END // indentless_sequence ::= (BLOCK-ENTRY block_node?)+ // block_mapping ::= BLOCK-MAPPING_START // ((KEY block_node_or_indentless_sequence?)? // (VALUE block_node_or_indentless_sequence?)?)* // BLOCK-END // flow_sequence ::= FLOW-SEQUENCE-START // (flow_sequence_entry FLOW-ENTRY)* // flow_sequence_entry? // FLOW-SEQUENCE-END // flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // flow_mapping ::= FLOW-MAPPING-START // (flow_mapping_entry FLOW-ENTRY)* // flow_mapping_entry? // FLOW-MAPPING-END // flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // Peek the next token in the token queue. func peek_token(parser *yaml_parser_t) *yaml_token_t { if parser.token_available || yaml_parser_fetch_more_tokens(parser) { token := &parser.tokens[parser.tokens_head] yaml_parser_unfold_comments(parser, token) return token } return nil } // yaml_parser_unfold_comments walks through the comments queue and joins all // comments behind the position of the provided token into the respective // top-level comment slices in the parser. func yaml_parser_unfold_comments(parser *yaml_parser_t, token *yaml_token_t) { for parser.comments_head < len(parser.comments) && token.start_mark.index >= parser.comments[parser.comments_head].token_mark.index { comment := &parser.comments[parser.comments_head] if len(comment.head) > 0 { if token.typ == yaml_BLOCK_END_TOKEN { // No heads on ends, so keep comment.head for a follow up token. break } if len(parser.head_comment) > 0 { parser.head_comment = append(parser.head_comment, '\n') } parser.head_comment = append(parser.head_comment, comment.head...) } if len(comment.foot) > 0 { if len(parser.foot_comment) > 0 { parser.foot_comment = append(parser.foot_comment, '\n') } parser.foot_comment = append(parser.foot_comment, comment.foot...) } if len(comment.line) > 0 { if len(parser.line_comment) > 0 { parser.line_comment = append(parser.line_comment, '\n') } parser.line_comment = append(parser.line_comment, comment.line...) } *comment = yaml_comment_t{} parser.comments_head++ } } // Remove the next token from the queue (must be called after peek_token). func skip_token(parser *yaml_parser_t) { parser.token_available = false parser.tokens_parsed++ parser.stream_end_produced = parser.tokens[parser.tokens_head].typ == yaml_STREAM_END_TOKEN parser.tokens_head++ } // Get the next event. func yaml_parser_parse(parser *yaml_parser_t, event *yaml_event_t) bool { // Erase the event object. *event = yaml_event_t{} // No events after the end of the stream or error. if parser.stream_end_produced || parser.error != yaml_NO_ERROR || parser.state == yaml_PARSE_END_STATE { return true } // Generate the next event. return yaml_parser_state_machine(parser, event) } // Set parser error. func yaml_parser_set_parser_error(parser *yaml_parser_t, problem string, problem_mark yaml_mark_t) bool { parser.error = yaml_PARSER_ERROR parser.problem = problem parser.problem_mark = problem_mark return false } func yaml_parser_set_parser_error_context(parser *yaml_parser_t, context string, context_mark yaml_mark_t, problem string, problem_mark yaml_mark_t) bool { parser.error = yaml_PARSER_ERROR parser.context = context parser.context_mark = context_mark parser.problem = problem parser.problem_mark = problem_mark return false } // State dispatcher. func yaml_parser_state_machine(parser *yaml_parser_t, event *yaml_event_t) bool { //trace("yaml_parser_state_machine", "state:", parser.state.String()) switch parser.state { case yaml_PARSE_STREAM_START_STATE: return yaml_parser_parse_stream_start(parser, event) case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE: return yaml_parser_parse_document_start(parser, event, true) case yaml_PARSE_DOCUMENT_START_STATE: return yaml_parser_parse_document_start(parser, event, false) case yaml_PARSE_DOCUMENT_CONTENT_STATE: return yaml_parser_parse_document_content(parser, event) case yaml_PARSE_DOCUMENT_END_STATE: return yaml_parser_parse_document_end(parser, event) case yaml_PARSE_BLOCK_NODE_STATE: return yaml_parser_parse_node(parser, event, true, false) case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE: return yaml_parser_parse_node(parser, event, true, true) case yaml_PARSE_FLOW_NODE_STATE: return yaml_parser_parse_node(parser, event, false, false) case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE: return yaml_parser_parse_block_sequence_entry(parser, event, true) case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE: return yaml_parser_parse_block_sequence_entry(parser, event, false) case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE: return yaml_parser_parse_indentless_sequence_entry(parser, event) case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE: return yaml_parser_parse_block_mapping_key(parser, event, true) case yaml_PARSE_BLOCK_MAPPING_KEY_STATE: return yaml_parser_parse_block_mapping_key(parser, event, false) case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE: return yaml_parser_parse_block_mapping_value(parser, event) case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE: return yaml_parser_parse_flow_sequence_entry(parser, event, true) case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE: return yaml_parser_parse_flow_sequence_entry(parser, event, false) case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE: return yaml_parser_parse_flow_sequence_entry_mapping_key(parser, event) case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE: return yaml_parser_parse_flow_sequence_entry_mapping_value(parser, event) case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE: return yaml_parser_parse_flow_sequence_entry_mapping_end(parser, event) case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE: return yaml_parser_parse_flow_mapping_key(parser, event, true) case yaml_PARSE_FLOW_MAPPING_KEY_STATE: return yaml_parser_parse_flow_mapping_key(parser, event, false) case yaml_PARSE_FLOW_MAPPING_VALUE_STATE: return yaml_parser_parse_flow_mapping_value(parser, event, false) case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE: return yaml_parser_parse_flow_mapping_value(parser, event, true) default: panic("invalid parser state") } } // Parse the production: // stream ::= STREAM-START implicit_document? explicit_document* STREAM-END // ************ func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { return false } if token.typ != yaml_STREAM_START_TOKEN { return yaml_parser_set_parser_error(parser, "did not find expected ", token.start_mark) } parser.state = yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE *event = yaml_event_t{ typ: yaml_STREAM_START_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, encoding: token.encoding, } skip_token(parser) return true } // Parse the productions: // implicit_document ::= block_node DOCUMENT-END* // * // explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* // ************************* func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t, implicit bool) bool { token := peek_token(parser) if token == nil { return false } // Parse extra document end indicators. if !implicit { for token.typ == yaml_DOCUMENT_END_TOKEN { skip_token(parser) token = peek_token(parser) if token == nil { return false } } } if implicit && token.typ != yaml_VERSION_DIRECTIVE_TOKEN && token.typ != yaml_TAG_DIRECTIVE_TOKEN && token.typ != yaml_DOCUMENT_START_TOKEN && token.typ != yaml_STREAM_END_TOKEN { // Parse an implicit document. if !yaml_parser_process_directives(parser, nil, nil) { return false } parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE) parser.state = yaml_PARSE_BLOCK_NODE_STATE var head_comment []byte if len(parser.head_comment) > 0 { // [Go] Scan the header comment backwards, and if an empty line is found, break // the header so the part before the last empty line goes into the // document header, while the bottom of it goes into a follow up event. for i := len(parser.head_comment) - 1; i > 0; i-- { if parser.head_comment[i] == '\n' { if i == len(parser.head_comment)-1 { head_comment = parser.head_comment[:i] parser.head_comment = parser.head_comment[i+1:] break } else if parser.head_comment[i-1] == '\n' { head_comment = parser.head_comment[:i-1] parser.head_comment = parser.head_comment[i+1:] break } } } } *event = yaml_event_t{ typ: yaml_DOCUMENT_START_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, head_comment: head_comment, } } else if token.typ != yaml_STREAM_END_TOKEN { // Parse an explicit document. var version_directive *yaml_version_directive_t var tag_directives []yaml_tag_directive_t start_mark := token.start_mark if !yaml_parser_process_directives(parser, &version_directive, &tag_directives) { return false } token = peek_token(parser) if token == nil { return false } if token.typ != yaml_DOCUMENT_START_TOKEN { yaml_parser_set_parser_error(parser, "did not find expected ", token.start_mark) return false } parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE) parser.state = yaml_PARSE_DOCUMENT_CONTENT_STATE end_mark := token.end_mark *event = yaml_event_t{ typ: yaml_DOCUMENT_START_EVENT, start_mark: start_mark, end_mark: end_mark, version_directive: version_directive, tag_directives: tag_directives, implicit: false, } skip_token(parser) } else { // Parse the stream end. parser.state = yaml_PARSE_END_STATE *event = yaml_event_t{ typ: yaml_STREAM_END_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, } skip_token(parser) } return true } // Parse the productions: // explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* // *********** // func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { return false } if token.typ == yaml_VERSION_DIRECTIVE_TOKEN || token.typ == yaml_TAG_DIRECTIVE_TOKEN || token.typ == yaml_DOCUMENT_START_TOKEN || token.typ == yaml_DOCUMENT_END_TOKEN || token.typ == yaml_STREAM_END_TOKEN { parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] return yaml_parser_process_empty_scalar(parser, event, token.start_mark) } return yaml_parser_parse_node(parser, event, true, false) } // Parse the productions: // implicit_document ::= block_node DOCUMENT-END* // ************* // explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* // func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { return false } start_mark := token.start_mark end_mark := token.start_mark implicit := true if token.typ == yaml_DOCUMENT_END_TOKEN { end_mark = token.end_mark skip_token(parser) implicit = false } parser.tag_directives = parser.tag_directives[:0] parser.state = yaml_PARSE_DOCUMENT_START_STATE *event = yaml_event_t{ typ: yaml_DOCUMENT_END_EVENT, start_mark: start_mark, end_mark: end_mark, implicit: implicit, } yaml_parser_set_event_comments(parser, event) if len(event.head_comment) > 0 && len(event.foot_comment) == 0 { event.foot_comment = event.head_comment event.head_comment = nil } return true } func yaml_parser_set_event_comments(parser *yaml_parser_t, event *yaml_event_t) { event.head_comment = parser.head_comment event.line_comment = parser.line_comment event.foot_comment = parser.foot_comment parser.head_comment = nil parser.line_comment = nil parser.foot_comment = nil parser.tail_comment = nil parser.stem_comment = nil } // Parse the productions: // block_node_or_indentless_sequence ::= // ALIAS // ***** // | properties (block_content | indentless_block_sequence)? // ********** * // | block_content | indentless_block_sequence // * // block_node ::= ALIAS // ***** // | properties block_content? // ********** * // | block_content // * // flow_node ::= ALIAS // ***** // | properties flow_content? // ********** * // | flow_content // * // properties ::= TAG ANCHOR? | ANCHOR TAG? // ************************* // block_content ::= block_collection | flow_collection | SCALAR // ****** // flow_content ::= flow_collection | SCALAR // ****** func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, indentless_sequence bool) bool { //defer trace("yaml_parser_parse_node", "block:", block, "indentless_sequence:", indentless_sequence)() token := peek_token(parser) if token == nil { return false } if token.typ == yaml_ALIAS_TOKEN { parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] *event = yaml_event_t{ typ: yaml_ALIAS_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, anchor: token.value, } yaml_parser_set_event_comments(parser, event) skip_token(parser) return true } start_mark := token.start_mark end_mark := token.start_mark var tag_token bool var tag_handle, tag_suffix, anchor []byte var tag_mark yaml_mark_t if token.typ == yaml_ANCHOR_TOKEN { anchor = token.value start_mark = token.start_mark end_mark = token.end_mark skip_token(parser) token = peek_token(parser) if token == nil { return false } if token.typ == yaml_TAG_TOKEN { tag_token = true tag_handle = token.value tag_suffix = token.suffix tag_mark = token.start_mark end_mark = token.end_mark skip_token(parser) token = peek_token(parser) if token == nil { return false } } } else if token.typ == yaml_TAG_TOKEN { tag_token = true tag_handle = token.value tag_suffix = token.suffix start_mark = token.start_mark tag_mark = token.start_mark end_mark = token.end_mark skip_token(parser) token = peek_token(parser) if token == nil { return false } if token.typ == yaml_ANCHOR_TOKEN { anchor = token.value end_mark = token.end_mark skip_token(parser) token = peek_token(parser) if token == nil { return false } } } var tag []byte if tag_token { if len(tag_handle) == 0 { tag = tag_suffix tag_suffix = nil } else { for i := range parser.tag_directives { if bytes.Equal(parser.tag_directives[i].handle, tag_handle) { tag = append([]byte(nil), parser.tag_directives[i].prefix...) tag = append(tag, tag_suffix...) break } } if len(tag) == 0 { yaml_parser_set_parser_error_context(parser, "while parsing a node", start_mark, "found undefined tag handle", tag_mark) return false } } } implicit := len(tag) == 0 if indentless_sequence && token.typ == yaml_BLOCK_ENTRY_TOKEN { end_mark = token.end_mark parser.state = yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE *event = yaml_event_t{ typ: yaml_SEQUENCE_START_EVENT, start_mark: start_mark, end_mark: end_mark, anchor: anchor, tag: tag, implicit: implicit, style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE), } return true } if token.typ == yaml_SCALAR_TOKEN { var plain_implicit, quoted_implicit bool end_mark = token.end_mark if (len(tag) == 0 && token.style == yaml_PLAIN_SCALAR_STYLE) || (len(tag) == 1 && tag[0] == '!') { plain_implicit = true } else if len(tag) == 0 { quoted_implicit = true } parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] *event = yaml_event_t{ typ: yaml_SCALAR_EVENT, start_mark: start_mark, end_mark: end_mark, anchor: anchor, tag: tag, value: token.value, implicit: plain_implicit, quoted_implicit: quoted_implicit, style: yaml_style_t(token.style), } yaml_parser_set_event_comments(parser, event) skip_token(parser) return true } if token.typ == yaml_FLOW_SEQUENCE_START_TOKEN { // [Go] Some of the events below can be merged as they differ only on style. end_mark = token.end_mark parser.state = yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE *event = yaml_event_t{ typ: yaml_SEQUENCE_START_EVENT, start_mark: start_mark, end_mark: end_mark, anchor: anchor, tag: tag, implicit: implicit, style: yaml_style_t(yaml_FLOW_SEQUENCE_STYLE), } yaml_parser_set_event_comments(parser, event) return true } if token.typ == yaml_FLOW_MAPPING_START_TOKEN { end_mark = token.end_mark parser.state = yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE *event = yaml_event_t{ typ: yaml_MAPPING_START_EVENT, start_mark: start_mark, end_mark: end_mark, anchor: anchor, tag: tag, implicit: implicit, style: yaml_style_t(yaml_FLOW_MAPPING_STYLE), } yaml_parser_set_event_comments(parser, event) return true } if block && token.typ == yaml_BLOCK_SEQUENCE_START_TOKEN { end_mark = token.end_mark parser.state = yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE *event = yaml_event_t{ typ: yaml_SEQUENCE_START_EVENT, start_mark: start_mark, end_mark: end_mark, anchor: anchor, tag: tag, implicit: implicit, style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE), } if parser.stem_comment != nil { event.head_comment = parser.stem_comment parser.stem_comment = nil } return true } if block && token.typ == yaml_BLOCK_MAPPING_START_TOKEN { end_mark = token.end_mark parser.state = yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE *event = yaml_event_t{ typ: yaml_MAPPING_START_EVENT, start_mark: start_mark, end_mark: end_mark, anchor: anchor, tag: tag, implicit: implicit, style: yaml_style_t(yaml_BLOCK_MAPPING_STYLE), } if parser.stem_comment != nil { event.head_comment = parser.stem_comment parser.stem_comment = nil } return true } if len(anchor) > 0 || len(tag) > 0 { parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] *event = yaml_event_t{ typ: yaml_SCALAR_EVENT, start_mark: start_mark, end_mark: end_mark, anchor: anchor, tag: tag, implicit: implicit, quoted_implicit: false, style: yaml_style_t(yaml_PLAIN_SCALAR_STYLE), } return true } context := "while parsing a flow node" if block { context = "while parsing a block node" } yaml_parser_set_parser_error_context(parser, context, start_mark, "did not find expected node content", token.start_mark) return false } // Parse the productions: // block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END // ******************** *********** * ********* // func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) if token == nil { return false } parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } token := peek_token(parser) if token == nil { return false } if token.typ == yaml_BLOCK_ENTRY_TOKEN { mark := token.end_mark prior_head_len := len(parser.head_comment) skip_token(parser) yaml_parser_split_stem_comment(parser, prior_head_len) token = peek_token(parser) if token == nil { return false } if token.typ != yaml_BLOCK_ENTRY_TOKEN && token.typ != yaml_BLOCK_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE) return yaml_parser_parse_node(parser, event, true, false) } else { parser.state = yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE return yaml_parser_process_empty_scalar(parser, event, mark) } } if token.typ == yaml_BLOCK_END_TOKEN { parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] parser.marks = parser.marks[:len(parser.marks)-1] *event = yaml_event_t{ typ: yaml_SEQUENCE_END_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, } skip_token(parser) return true } context_mark := parser.marks[len(parser.marks)-1] parser.marks = parser.marks[:len(parser.marks)-1] return yaml_parser_set_parser_error_context(parser, "while parsing a block collection", context_mark, "did not find expected '-' indicator", token.start_mark) } // Parse the productions: // indentless_sequence ::= (BLOCK-ENTRY block_node?)+ // *********** * func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { return false } if token.typ == yaml_BLOCK_ENTRY_TOKEN { mark := token.end_mark prior_head_len := len(parser.head_comment) skip_token(parser) yaml_parser_split_stem_comment(parser, prior_head_len) token = peek_token(parser) if token == nil { return false } if token.typ != yaml_BLOCK_ENTRY_TOKEN && token.typ != yaml_KEY_TOKEN && token.typ != yaml_VALUE_TOKEN && token.typ != yaml_BLOCK_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE) return yaml_parser_parse_node(parser, event, true, false) } parser.state = yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE return yaml_parser_process_empty_scalar(parser, event, mark) } parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] *event = yaml_event_t{ typ: yaml_SEQUENCE_END_EVENT, start_mark: token.start_mark, end_mark: token.start_mark, // [Go] Shouldn't this be token.end_mark? } return true } // Split stem comment from head comment. // // When a sequence or map is found under a sequence entry, the former head comment // is assigned to the underlying sequence or map as a whole, not the individual // sequence or map entry as would be expected otherwise. To handle this case the // previous head comment is moved aside as the stem comment. func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) { if stem_len == 0 { return } token := peek_token(parser) if token == nil || token.typ != yaml_BLOCK_SEQUENCE_START_TOKEN && token.typ != yaml_BLOCK_MAPPING_START_TOKEN { return } parser.stem_comment = parser.head_comment[:stem_len] if len(parser.head_comment) == stem_len { parser.head_comment = nil } else { // Copy suffix to prevent very strange bugs if someone ever appends // further bytes to the prefix in the stem_comment slice above. parser.head_comment = append([]byte(nil), parser.head_comment[stem_len+1:]...) } } // Parse the productions: // block_mapping ::= BLOCK-MAPPING_START // ******************* // ((KEY block_node_or_indentless_sequence?)? // *** * // (VALUE block_node_or_indentless_sequence?)?)* // // BLOCK-END // ********* // func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) if token == nil { return false } parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } token := peek_token(parser) if token == nil { return false } // [Go] A tail comment was left from the prior mapping value processed. Emit an event // as it needs to be processed with that value and not the following key. if len(parser.tail_comment) > 0 { *event = yaml_event_t{ typ: yaml_TAIL_COMMENT_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, foot_comment: parser.tail_comment, } parser.tail_comment = nil return true } if token.typ == yaml_KEY_TOKEN { mark := token.end_mark skip_token(parser) token = peek_token(parser) if token == nil { return false } if token.typ != yaml_KEY_TOKEN && token.typ != yaml_VALUE_TOKEN && token.typ != yaml_BLOCK_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_BLOCK_MAPPING_VALUE_STATE) return yaml_parser_parse_node(parser, event, true, true) } else { parser.state = yaml_PARSE_BLOCK_MAPPING_VALUE_STATE return yaml_parser_process_empty_scalar(parser, event, mark) } } else if token.typ == yaml_BLOCK_END_TOKEN { parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] parser.marks = parser.marks[:len(parser.marks)-1] *event = yaml_event_t{ typ: yaml_MAPPING_END_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, } yaml_parser_set_event_comments(parser, event) skip_token(parser) return true } context_mark := parser.marks[len(parser.marks)-1] parser.marks = parser.marks[:len(parser.marks)-1] return yaml_parser_set_parser_error_context(parser, "while parsing a block mapping", context_mark, "did not find expected key", token.start_mark) } // Parse the productions: // block_mapping ::= BLOCK-MAPPING_START // // ((KEY block_node_or_indentless_sequence?)? // // (VALUE block_node_or_indentless_sequence?)?)* // ***** * // BLOCK-END // // func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { return false } if token.typ == yaml_VALUE_TOKEN { mark := token.end_mark skip_token(parser) token = peek_token(parser) if token == nil { return false } if token.typ != yaml_KEY_TOKEN && token.typ != yaml_VALUE_TOKEN && token.typ != yaml_BLOCK_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_BLOCK_MAPPING_KEY_STATE) return yaml_parser_parse_node(parser, event, true, true) } parser.state = yaml_PARSE_BLOCK_MAPPING_KEY_STATE return yaml_parser_process_empty_scalar(parser, event, mark) } parser.state = yaml_PARSE_BLOCK_MAPPING_KEY_STATE return yaml_parser_process_empty_scalar(parser, event, token.start_mark) } // Parse the productions: // flow_sequence ::= FLOW-SEQUENCE-START // ******************* // (flow_sequence_entry FLOW-ENTRY)* // * ********** // flow_sequence_entry? // * // FLOW-SEQUENCE-END // ***************** // flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // * // func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) if token == nil { return false } parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } token := peek_token(parser) if token == nil { return false } if token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { if !first { if token.typ == yaml_FLOW_ENTRY_TOKEN { skip_token(parser) token = peek_token(parser) if token == nil { return false } } else { context_mark := parser.marks[len(parser.marks)-1] parser.marks = parser.marks[:len(parser.marks)-1] return yaml_parser_set_parser_error_context(parser, "while parsing a flow sequence", context_mark, "did not find expected ',' or ']'", token.start_mark) } } if token.typ == yaml_KEY_TOKEN { parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE *event = yaml_event_t{ typ: yaml_MAPPING_START_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, implicit: true, style: yaml_style_t(yaml_FLOW_MAPPING_STYLE), } skip_token(parser) return true } else if token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE) return yaml_parser_parse_node(parser, event, false, false) } } parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] parser.marks = parser.marks[:len(parser.marks)-1] *event = yaml_event_t{ typ: yaml_SEQUENCE_END_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, } yaml_parser_set_event_comments(parser, event) skip_token(parser) return true } // // Parse the productions: // flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // *** * // func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { return false } if token.typ != yaml_VALUE_TOKEN && token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE) return yaml_parser_parse_node(parser, event, false, false) } mark := token.end_mark skip_token(parser) parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE return yaml_parser_process_empty_scalar(parser, event, mark) } // Parse the productions: // flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // ***** * // func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { return false } if token.typ == yaml_VALUE_TOKEN { skip_token(parser) token := peek_token(parser) if token == nil { return false } if token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE) return yaml_parser_parse_node(parser, event, false, false) } } parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE return yaml_parser_process_empty_scalar(parser, event, token.start_mark) } // Parse the productions: // flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // * // func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, event *yaml_event_t) bool { token := peek_token(parser) if token == nil { return false } parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE *event = yaml_event_t{ typ: yaml_MAPPING_END_EVENT, start_mark: token.start_mark, end_mark: token.start_mark, // [Go] Shouldn't this be end_mark? } return true } // Parse the productions: // flow_mapping ::= FLOW-MAPPING-START // ****************** // (flow_mapping_entry FLOW-ENTRY)* // * ********** // flow_mapping_entry? // ****************** // FLOW-MAPPING-END // **************** // flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // * *** * // func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { if first { token := peek_token(parser) parser.marks = append(parser.marks, token.start_mark) skip_token(parser) } token := peek_token(parser) if token == nil { return false } if token.typ != yaml_FLOW_MAPPING_END_TOKEN { if !first { if token.typ == yaml_FLOW_ENTRY_TOKEN { skip_token(parser) token = peek_token(parser) if token == nil { return false } } else { context_mark := parser.marks[len(parser.marks)-1] parser.marks = parser.marks[:len(parser.marks)-1] return yaml_parser_set_parser_error_context(parser, "while parsing a flow mapping", context_mark, "did not find expected ',' or '}'", token.start_mark) } } if token.typ == yaml_KEY_TOKEN { skip_token(parser) token = peek_token(parser) if token == nil { return false } if token.typ != yaml_VALUE_TOKEN && token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_MAPPING_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_VALUE_STATE) return yaml_parser_parse_node(parser, event, false, false) } else { parser.state = yaml_PARSE_FLOW_MAPPING_VALUE_STATE return yaml_parser_process_empty_scalar(parser, event, token.start_mark) } } else if token.typ != yaml_FLOW_MAPPING_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE) return yaml_parser_parse_node(parser, event, false, false) } } parser.state = parser.states[len(parser.states)-1] parser.states = parser.states[:len(parser.states)-1] parser.marks = parser.marks[:len(parser.marks)-1] *event = yaml_event_t{ typ: yaml_MAPPING_END_EVENT, start_mark: token.start_mark, end_mark: token.end_mark, } yaml_parser_set_event_comments(parser, event) skip_token(parser) return true } // Parse the productions: // flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? // * ***** * // func yaml_parser_parse_flow_mapping_value(parser *yaml_parser_t, event *yaml_event_t, empty bool) bool { token := peek_token(parser) if token == nil { return false } if empty { parser.state = yaml_PARSE_FLOW_MAPPING_KEY_STATE return yaml_parser_process_empty_scalar(parser, event, token.start_mark) } if token.typ == yaml_VALUE_TOKEN { skip_token(parser) token = peek_token(parser) if token == nil { return false } if token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_MAPPING_END_TOKEN { parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_KEY_STATE) return yaml_parser_parse_node(parser, event, false, false) } } parser.state = yaml_PARSE_FLOW_MAPPING_KEY_STATE return yaml_parser_process_empty_scalar(parser, event, token.start_mark) } // Generate an empty scalar event. func yaml_parser_process_empty_scalar(parser *yaml_parser_t, event *yaml_event_t, mark yaml_mark_t) bool { *event = yaml_event_t{ typ: yaml_SCALAR_EVENT, start_mark: mark, end_mark: mark, value: nil, // Empty implicit: true, style: yaml_style_t(yaml_PLAIN_SCALAR_STYLE), } return true } var default_tag_directives = []yaml_tag_directive_t{ {[]byte("!"), []byte("!")}, {[]byte("!!"), []byte("tag:yaml.org,2002:")}, } // Parse directives. func yaml_parser_process_directives(parser *yaml_parser_t, version_directive_ref **yaml_version_directive_t, tag_directives_ref *[]yaml_tag_directive_t) bool { var version_directive *yaml_version_directive_t var tag_directives []yaml_tag_directive_t token := peek_token(parser) if token == nil { return false } for token.typ == yaml_VERSION_DIRECTIVE_TOKEN || token.typ == yaml_TAG_DIRECTIVE_TOKEN { if token.typ == yaml_VERSION_DIRECTIVE_TOKEN { if version_directive != nil { yaml_parser_set_parser_error(parser, "found duplicate %YAML directive", token.start_mark) return false } if token.major != 1 || token.minor != 1 { yaml_parser_set_parser_error(parser, "found incompatible YAML document", token.start_mark) return false } version_directive = &yaml_version_directive_t{ major: token.major, minor: token.minor, } } else if token.typ == yaml_TAG_DIRECTIVE_TOKEN { value := yaml_tag_directive_t{ handle: token.value, prefix: token.prefix, } if !yaml_parser_append_tag_directive(parser, value, false, token.start_mark) { return false } tag_directives = append(tag_directives, value) } skip_token(parser) token = peek_token(parser) if token == nil { return false } } for i := range default_tag_directives { if !yaml_parser_append_tag_directive(parser, default_tag_directives[i], true, token.start_mark) { return false } } if version_directive_ref != nil { *version_directive_ref = version_directive } if tag_directives_ref != nil { *tag_directives_ref = tag_directives } return true } // Append a tag directive to the directives stack. func yaml_parser_append_tag_directive(parser *yaml_parser_t, value yaml_tag_directive_t, allow_duplicates bool, mark yaml_mark_t) bool { for i := range parser.tag_directives { if bytes.Equal(value.handle, parser.tag_directives[i].handle) { if allow_duplicates { return true } return yaml_parser_set_parser_error(parser, "found duplicate %TAG directive", mark) } } // [Go] I suspect the copy is unnecessary. This was likely done // because there was no way to track ownership of the data. value_copy := yaml_tag_directive_t{ handle: make([]byte, len(value.handle)), prefix: make([]byte, len(value.prefix)), } copy(value_copy.handle, value.handle) copy(value_copy.prefix, value.prefix) parser.tag_directives = append(parser.tag_directives, value_copy) return true } ================================================ FILE: vendor/github.com/oasdiff/yaml3/readerc.go ================================================ // // Copyright (c) 2011-2019 Canonical Ltd // Copyright (c) 2006-2010 Kirill Simonov // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package yaml import ( "io" ) // Set the reader error and return 0. func yaml_parser_set_reader_error(parser *yaml_parser_t, problem string, offset int, value int) bool { parser.error = yaml_READER_ERROR parser.problem = problem parser.problem_offset = offset parser.problem_value = value return false } // Byte order marks. const ( bom_UTF8 = "\xef\xbb\xbf" bom_UTF16LE = "\xff\xfe" bom_UTF16BE = "\xfe\xff" ) // Determine the input stream encoding by checking the BOM symbol. If no BOM is // found, the UTF-8 encoding is assumed. Return 1 on success, 0 on failure. func yaml_parser_determine_encoding(parser *yaml_parser_t) bool { // Ensure that we had enough bytes in the raw buffer. for !parser.eof && len(parser.raw_buffer)-parser.raw_buffer_pos < 3 { if !yaml_parser_update_raw_buffer(parser) { return false } } // Determine the encoding. buf := parser.raw_buffer pos := parser.raw_buffer_pos avail := len(buf) - pos if avail >= 2 && buf[pos] == bom_UTF16LE[0] && buf[pos+1] == bom_UTF16LE[1] { parser.encoding = yaml_UTF16LE_ENCODING parser.raw_buffer_pos += 2 parser.offset += 2 } else if avail >= 2 && buf[pos] == bom_UTF16BE[0] && buf[pos+1] == bom_UTF16BE[1] { parser.encoding = yaml_UTF16BE_ENCODING parser.raw_buffer_pos += 2 parser.offset += 2 } else if avail >= 3 && buf[pos] == bom_UTF8[0] && buf[pos+1] == bom_UTF8[1] && buf[pos+2] == bom_UTF8[2] { parser.encoding = yaml_UTF8_ENCODING parser.raw_buffer_pos += 3 parser.offset += 3 } else { parser.encoding = yaml_UTF8_ENCODING } return true } // Update the raw buffer. func yaml_parser_update_raw_buffer(parser *yaml_parser_t) bool { size_read := 0 // Return if the raw buffer is full. if parser.raw_buffer_pos == 0 && len(parser.raw_buffer) == cap(parser.raw_buffer) { return true } // Return on EOF. if parser.eof { return true } // Move the remaining bytes in the raw buffer to the beginning. if parser.raw_buffer_pos > 0 && parser.raw_buffer_pos < len(parser.raw_buffer) { copy(parser.raw_buffer, parser.raw_buffer[parser.raw_buffer_pos:]) } parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)-parser.raw_buffer_pos] parser.raw_buffer_pos = 0 // Call the read handler to fill the buffer. size_read, err := parser.read_handler(parser, parser.raw_buffer[len(parser.raw_buffer):cap(parser.raw_buffer)]) parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)+size_read] if err == io.EOF { parser.eof = true } else if err != nil { return yaml_parser_set_reader_error(parser, "input error: "+err.Error(), parser.offset, -1) } return true } // Ensure that the buffer contains at least `length` characters. // Return true on success, false on failure. // // The length is supposed to be significantly less that the buffer size. func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool { if parser.read_handler == nil { panic("read handler must be set") } // [Go] This function was changed to guarantee the requested length size at EOF. // The fact we need to do this is pretty awful, but the description above implies // for that to be the case, and there are tests // If the EOF flag is set and the raw buffer is empty, do nothing. if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) { // [Go] ACTUALLY! Read the documentation of this function above. // This is just broken. To return true, we need to have the // given length in the buffer. Not doing that means every single // check that calls this function to make sure the buffer has a // given length is Go) panicking; or C) accessing invalid memory. //return true } // Return if the buffer contains enough characters. if parser.unread >= length { return true } // Determine the input encoding if it is not known yet. if parser.encoding == yaml_ANY_ENCODING { if !yaml_parser_determine_encoding(parser) { return false } } // Move the unread characters to the beginning of the buffer. buffer_len := len(parser.buffer) if parser.buffer_pos > 0 && parser.buffer_pos < buffer_len { copy(parser.buffer, parser.buffer[parser.buffer_pos:]) buffer_len -= parser.buffer_pos parser.buffer_pos = 0 } else if parser.buffer_pos == buffer_len { buffer_len = 0 parser.buffer_pos = 0 } // Open the whole buffer for writing, and cut it before returning. parser.buffer = parser.buffer[:cap(parser.buffer)] // Fill the buffer until it has enough characters. first := true for parser.unread < length { // Fill the raw buffer if necessary. if !first || parser.raw_buffer_pos == len(parser.raw_buffer) { if !yaml_parser_update_raw_buffer(parser) { parser.buffer = parser.buffer[:buffer_len] return false } } first = false // Decode the raw buffer. inner: for parser.raw_buffer_pos != len(parser.raw_buffer) { var value rune var width int raw_unread := len(parser.raw_buffer) - parser.raw_buffer_pos // Decode the next character. switch parser.encoding { case yaml_UTF8_ENCODING: // Decode a UTF-8 character. Check RFC 3629 // (http://www.ietf.org/rfc/rfc3629.txt) for more details. // // The following table (taken from the RFC) is used for // decoding. // // Char. number range | UTF-8 octet sequence // (hexadecimal) | (binary) // --------------------+------------------------------------ // 0000 0000-0000 007F | 0xxxxxxx // 0000 0080-0000 07FF | 110xxxxx 10xxxxxx // 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx // 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx // // Additionally, the characters in the range 0xD800-0xDFFF // are prohibited as they are reserved for use with UTF-16 // surrogate pairs. // Determine the length of the UTF-8 sequence. octet := parser.raw_buffer[parser.raw_buffer_pos] switch { case octet&0x80 == 0x00: width = 1 case octet&0xE0 == 0xC0: width = 2 case octet&0xF0 == 0xE0: width = 3 case octet&0xF8 == 0xF0: width = 4 default: // The leading octet is invalid. return yaml_parser_set_reader_error(parser, "invalid leading UTF-8 octet", parser.offset, int(octet)) } // Check if the raw buffer contains an incomplete character. if width > raw_unread { if parser.eof { return yaml_parser_set_reader_error(parser, "incomplete UTF-8 octet sequence", parser.offset, -1) } break inner } // Decode the leading octet. switch { case octet&0x80 == 0x00: value = rune(octet & 0x7F) case octet&0xE0 == 0xC0: value = rune(octet & 0x1F) case octet&0xF0 == 0xE0: value = rune(octet & 0x0F) case octet&0xF8 == 0xF0: value = rune(octet & 0x07) default: value = 0 } // Check and decode the trailing octets. for k := 1; k < width; k++ { octet = parser.raw_buffer[parser.raw_buffer_pos+k] // Check if the octet is valid. if (octet & 0xC0) != 0x80 { return yaml_parser_set_reader_error(parser, "invalid trailing UTF-8 octet", parser.offset+k, int(octet)) } // Decode the octet. value = (value << 6) + rune(octet&0x3F) } // Check the length of the sequence against the value. switch { case width == 1: case width == 2 && value >= 0x80: case width == 3 && value >= 0x800: case width == 4 && value >= 0x10000: default: return yaml_parser_set_reader_error(parser, "invalid length of a UTF-8 sequence", parser.offset, -1) } // Check the range of the value. if value >= 0xD800 && value <= 0xDFFF || value > 0x10FFFF { return yaml_parser_set_reader_error(parser, "invalid Unicode character", parser.offset, int(value)) } case yaml_UTF16LE_ENCODING, yaml_UTF16BE_ENCODING: var low, high int if parser.encoding == yaml_UTF16LE_ENCODING { low, high = 0, 1 } else { low, high = 1, 0 } // The UTF-16 encoding is not as simple as one might // naively think. Check RFC 2781 // (http://www.ietf.org/rfc/rfc2781.txt). // // Normally, two subsequent bytes describe a Unicode // character. However a special technique (called a // surrogate pair) is used for specifying character // values larger than 0xFFFF. // // A surrogate pair consists of two pseudo-characters: // high surrogate area (0xD800-0xDBFF) // low surrogate area (0xDC00-0xDFFF) // // The following formulas are used for decoding // and encoding characters using surrogate pairs: // // U = U' + 0x10000 (0x01 00 00 <= U <= 0x10 FF FF) // U' = yyyyyyyyyyxxxxxxxxxx (0 <= U' <= 0x0F FF FF) // W1 = 110110yyyyyyyyyy // W2 = 110111xxxxxxxxxx // // where U is the character value, W1 is the high surrogate // area, W2 is the low surrogate area. // Check for incomplete UTF-16 character. if raw_unread < 2 { if parser.eof { return yaml_parser_set_reader_error(parser, "incomplete UTF-16 character", parser.offset, -1) } break inner } // Get the character. value = rune(parser.raw_buffer[parser.raw_buffer_pos+low]) + (rune(parser.raw_buffer[parser.raw_buffer_pos+high]) << 8) // Check for unexpected low surrogate area. if value&0xFC00 == 0xDC00 { return yaml_parser_set_reader_error(parser, "unexpected low surrogate area", parser.offset, int(value)) } // Check for a high surrogate area. if value&0xFC00 == 0xD800 { width = 4 // Check for incomplete surrogate pair. if raw_unread < 4 { if parser.eof { return yaml_parser_set_reader_error(parser, "incomplete UTF-16 surrogate pair", parser.offset, -1) } break inner } // Get the next character. value2 := rune(parser.raw_buffer[parser.raw_buffer_pos+low+2]) + (rune(parser.raw_buffer[parser.raw_buffer_pos+high+2]) << 8) // Check for a low surrogate area. if value2&0xFC00 != 0xDC00 { return yaml_parser_set_reader_error(parser, "expected low surrogate area", parser.offset+2, int(value2)) } // Generate the value of the surrogate pair. value = 0x10000 + ((value & 0x3FF) << 10) + (value2 & 0x3FF) } else { width = 2 } default: panic("impossible") } // Check if the character is in the allowed range: // #x9 | #xA | #xD | [#x20-#x7E] (8 bit) // | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD] (16 bit) // | [#x10000-#x10FFFF] (32 bit) switch { case value == 0x09: case value == 0x0A: case value == 0x0D: case value >= 0x20 && value <= 0x7E: case value == 0x85: case value >= 0xA0 && value <= 0xD7FF: case value >= 0xE000 && value <= 0xFFFD: case value >= 0x10000 && value <= 0x10FFFF: default: return yaml_parser_set_reader_error(parser, "control characters are not allowed", parser.offset, int(value)) } // Move the raw pointers. parser.raw_buffer_pos += width parser.offset += width // Finally put the character into the buffer. if value <= 0x7F { // 0000 0000-0000 007F . 0xxxxxxx parser.buffer[buffer_len+0] = byte(value) buffer_len += 1 } else if value <= 0x7FF { // 0000 0080-0000 07FF . 110xxxxx 10xxxxxx parser.buffer[buffer_len+0] = byte(0xC0 + (value >> 6)) parser.buffer[buffer_len+1] = byte(0x80 + (value & 0x3F)) buffer_len += 2 } else if value <= 0xFFFF { // 0000 0800-0000 FFFF . 1110xxxx 10xxxxxx 10xxxxxx parser.buffer[buffer_len+0] = byte(0xE0 + (value >> 12)) parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 6) & 0x3F)) parser.buffer[buffer_len+2] = byte(0x80 + (value & 0x3F)) buffer_len += 3 } else { // 0001 0000-0010 FFFF . 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx parser.buffer[buffer_len+0] = byte(0xF0 + (value >> 18)) parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 12) & 0x3F)) parser.buffer[buffer_len+2] = byte(0x80 + ((value >> 6) & 0x3F)) parser.buffer[buffer_len+3] = byte(0x80 + (value & 0x3F)) buffer_len += 4 } parser.unread++ } // On EOF, put NUL into the buffer and return. if parser.eof { parser.buffer[buffer_len] = 0 buffer_len++ parser.unread++ break } } // [Go] Read the documentation of this function above. To return true, // we need to have the given length in the buffer. Not doing that means // every single check that calls this function to make sure the buffer // has a given length is Go) panicking; or C) accessing invalid memory. // This happens here due to the EOF above breaking early. for buffer_len < length { parser.buffer[buffer_len] = 0 buffer_len++ } parser.buffer = parser.buffer[:buffer_len] return true } ================================================ FILE: vendor/github.com/oasdiff/yaml3/resolve.go ================================================ // // Copyright (c) 2011-2019 Canonical Ltd // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package yaml import ( "encoding/base64" "math" "regexp" "strconv" "strings" "time" ) type resolveMapItem struct { value interface{} tag string } var resolveTable = make([]byte, 256) var resolveMap = make(map[string]resolveMapItem) func init() { t := resolveTable t[int('+')] = 'S' // Sign t[int('-')] = 'S' for _, c := range "0123456789" { t[int(c)] = 'D' // Digit } for _, c := range "yYnNtTfFoO~" { t[int(c)] = 'M' // In map } t[int('.')] = '.' // Float (potentially in map) var resolveMapList = []struct { v interface{} tag string l []string }{ {true, boolTag, []string{"true", "True", "TRUE"}}, {false, boolTag, []string{"false", "False", "FALSE"}}, {nil, nullTag, []string{"", "~", "null", "Null", "NULL"}}, {math.NaN(), floatTag, []string{".nan", ".NaN", ".NAN"}}, {math.Inf(+1), floatTag, []string{".inf", ".Inf", ".INF"}}, {math.Inf(+1), floatTag, []string{"+.inf", "+.Inf", "+.INF"}}, {math.Inf(-1), floatTag, []string{"-.inf", "-.Inf", "-.INF"}}, {"<<", mergeTag, []string{"<<"}}, } m := resolveMap for _, item := range resolveMapList { for _, s := range item.l { m[s] = resolveMapItem{item.v, item.tag} } } } const ( nullTag = "!!null" boolTag = "!!bool" strTag = "!!str" intTag = "!!int" floatTag = "!!float" timestampTag = "!!timestamp" seqTag = "!!seq" mapTag = "!!map" binaryTag = "!!binary" mergeTag = "!!merge" ) var longTags = make(map[string]string) var shortTags = make(map[string]string) func init() { for _, stag := range []string{nullTag, boolTag, strTag, intTag, floatTag, timestampTag, seqTag, mapTag, binaryTag, mergeTag} { ltag := longTag(stag) longTags[stag] = ltag shortTags[ltag] = stag } } const longTagPrefix = "tag:yaml.org,2002:" func shortTag(tag string) string { if strings.HasPrefix(tag, longTagPrefix) { if stag, ok := shortTags[tag]; ok { return stag } return "!!" + tag[len(longTagPrefix):] } return tag } func longTag(tag string) string { if strings.HasPrefix(tag, "!!") { if ltag, ok := longTags[tag]; ok { return ltag } return longTagPrefix + tag[2:] } return tag } func resolvableTag(tag string) bool { switch tag { case "", strTag, boolTag, intTag, floatTag, nullTag, timestampTag: return true } return false } var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`) func resolve(tag string, in string) (rtag string, out interface{}) { tag = shortTag(tag) if !resolvableTag(tag) { return tag, in } defer func() { switch tag { case "", rtag, strTag, binaryTag: return case floatTag: if rtag == intTag { switch v := out.(type) { case int64: rtag = floatTag out = float64(v) return case int: rtag = floatTag out = float64(v) return } } } failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag)) }() // Any data is accepted as a !!str or !!binary. // Otherwise, the prefix is enough of a hint about what it might be. hint := byte('N') if in != "" { hint = resolveTable[in[0]] } if hint != 0 && tag != strTag && tag != binaryTag { // Handle things we can lookup in a map. if item, ok := resolveMap[in]; ok { return item.tag, item.value } // Base 60 floats are a bad idea, were dropped in YAML 1.2, and // are purposefully unsupported here. They're still quoted on // the way out for compatibility with other parser, though. switch hint { case 'M': // We've already checked the map above. case '.': // Not in the map, so maybe a normal float. floatv, err := strconv.ParseFloat(in, 64) if err == nil { return floatTag, floatv } case 'D', 'S': // Int, float, or timestamp. // Only try values as a timestamp if the value is unquoted or there's an explicit // !!timestamp tag. if tag == "" || tag == timestampTag { t, ok := parseTimestamp(in) if ok { return timestampTag, t } } plain := strings.Replace(in, "_", "", -1) intv, err := strconv.ParseInt(plain, 0, 64) if err == nil { if intv == int64(int(intv)) { return intTag, int(intv) } else { return intTag, intv } } uintv, err := strconv.ParseUint(plain, 0, 64) if err == nil { return intTag, uintv } if yamlStyleFloat.MatchString(plain) { floatv, err := strconv.ParseFloat(plain, 64) if err == nil { return floatTag, floatv } } if strings.HasPrefix(plain, "0b") { intv, err := strconv.ParseInt(plain[2:], 2, 64) if err == nil { if intv == int64(int(intv)) { return intTag, int(intv) } else { return intTag, intv } } uintv, err := strconv.ParseUint(plain[2:], 2, 64) if err == nil { return intTag, uintv } } else if strings.HasPrefix(plain, "-0b") { intv, err := strconv.ParseInt("-"+plain[3:], 2, 64) if err == nil { if true || intv == int64(int(intv)) { return intTag, int(intv) } else { return intTag, intv } } } // Octals as introduced in version 1.2 of the spec. // Octals from the 1.1 spec, spelled as 0777, are still // decoded by default in v3 as well for compatibility. // May be dropped in v4 depending on how usage evolves. if strings.HasPrefix(plain, "0o") { intv, err := strconv.ParseInt(plain[2:], 8, 64) if err == nil { if intv == int64(int(intv)) { return intTag, int(intv) } else { return intTag, intv } } uintv, err := strconv.ParseUint(plain[2:], 8, 64) if err == nil { return intTag, uintv } } else if strings.HasPrefix(plain, "-0o") { intv, err := strconv.ParseInt("-"+plain[3:], 8, 64) if err == nil { if true || intv == int64(int(intv)) { return intTag, int(intv) } else { return intTag, intv } } } default: panic("internal error: missing handler for resolver table: " + string(rune(hint)) + " (with " + in + ")") } } return strTag, in } // encodeBase64 encodes s as base64 that is broken up into multiple lines // as appropriate for the resulting length. func encodeBase64(s string) string { const lineLen = 70 encLen := base64.StdEncoding.EncodedLen(len(s)) lines := encLen/lineLen + 1 buf := make([]byte, encLen*2+lines) in := buf[0:encLen] out := buf[encLen:] base64.StdEncoding.Encode(in, []byte(s)) k := 0 for i := 0; i < len(in); i += lineLen { j := i + lineLen if j > len(in) { j = len(in) } k += copy(out[k:], in[i:j]) if lines > 1 { out[k] = '\n' k++ } } return string(out[:k]) } // This is a subset of the formats allowed by the regular expression // defined at http://yaml.org/type/timestamp.html. var allowedTimestampFormats = []string{ "2006-1-2T15:4:5.999999999Z07:00", // RCF3339Nano with short date fields. "2006-1-2t15:4:5.999999999Z07:00", // RFC3339Nano with short date fields and lower-case "t". "2006-1-2 15:4:5.999999999", // space separated with no time zone "2006-1-2", // date only // Notable exception: time.Parse cannot handle: "2001-12-14 21:59:43.10 -5" // from the set of examples. } // parseTimestamp parses s as a timestamp string and // returns the timestamp and reports whether it succeeded. // Timestamp formats are defined at http://yaml.org/type/timestamp.html func parseTimestamp(s string) (time.Time, bool) { // TODO write code to check all the formats supported by // http://yaml.org/type/timestamp.html instead of using time.Parse. // Quick check: all date formats start with YYYY-. i := 0 for ; i < len(s); i++ { if c := s[i]; c < '0' || c > '9' { break } } if i != 4 || i == len(s) || s[i] != '-' { return time.Time{}, false } for _, format := range allowedTimestampFormats { if t, err := time.Parse(format, s); err == nil { return t, true } } return time.Time{}, false } ================================================ FILE: vendor/github.com/oasdiff/yaml3/scannerc.go ================================================ // // Copyright (c) 2011-2019 Canonical Ltd // Copyright (c) 2006-2010 Kirill Simonov // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package yaml import ( "bytes" "fmt" ) // Introduction // ************ // // The following notes assume that you are familiar with the YAML specification // (http://yaml.org/spec/1.2/spec.html). We mostly follow it, although in // some cases we are less restrictive that it requires. // // The process of transforming a YAML stream into a sequence of events is // divided on two steps: Scanning and Parsing. // // The Scanner transforms the input stream into a sequence of tokens, while the // parser transform the sequence of tokens produced by the Scanner into a // sequence of parsing events. // // The Scanner is rather clever and complicated. The Parser, on the contrary, // is a straightforward implementation of a recursive-descendant parser (or, // LL(1) parser, as it is usually called). // // Actually there are two issues of Scanning that might be called "clever", the // rest is quite straightforward. The issues are "block collection start" and // "simple keys". Both issues are explained below in details. // // Here the Scanning step is explained and implemented. We start with the list // of all the tokens produced by the Scanner together with short descriptions. // // Now, tokens: // // STREAM-START(encoding) # The stream start. // STREAM-END # The stream end. // VERSION-DIRECTIVE(major,minor) # The '%YAML' directive. // TAG-DIRECTIVE(handle,prefix) # The '%TAG' directive. // DOCUMENT-START # '---' // DOCUMENT-END # '...' // BLOCK-SEQUENCE-START # Indentation increase denoting a block // BLOCK-MAPPING-START # sequence or a block mapping. // BLOCK-END # Indentation decrease. // FLOW-SEQUENCE-START # '[' // FLOW-SEQUENCE-END # ']' // BLOCK-SEQUENCE-START # '{' // BLOCK-SEQUENCE-END # '}' // BLOCK-ENTRY # '-' // FLOW-ENTRY # ',' // KEY # '?' or nothing (simple keys). // VALUE # ':' // ALIAS(anchor) # '*anchor' // ANCHOR(anchor) # '&anchor' // TAG(handle,suffix) # '!handle!suffix' // SCALAR(value,style) # A scalar. // // The following two tokens are "virtual" tokens denoting the beginning and the // end of the stream: // // STREAM-START(encoding) // STREAM-END // // We pass the information about the input stream encoding with the // STREAM-START token. // // The next two tokens are responsible for tags: // // VERSION-DIRECTIVE(major,minor) // TAG-DIRECTIVE(handle,prefix) // // Example: // // %YAML 1.1 // %TAG ! !foo // %TAG !yaml! tag:yaml.org,2002: // --- // // The correspoding sequence of tokens: // // STREAM-START(utf-8) // VERSION-DIRECTIVE(1,1) // TAG-DIRECTIVE("!","!foo") // TAG-DIRECTIVE("!yaml","tag:yaml.org,2002:") // DOCUMENT-START // STREAM-END // // Note that the VERSION-DIRECTIVE and TAG-DIRECTIVE tokens occupy a whole // line. // // The document start and end indicators are represented by: // // DOCUMENT-START // DOCUMENT-END // // Note that if a YAML stream contains an implicit document (without '---' // and '...' indicators), no DOCUMENT-START and DOCUMENT-END tokens will be // produced. // // In the following examples, we present whole documents together with the // produced tokens. // // 1. An implicit document: // // 'a scalar' // // Tokens: // // STREAM-START(utf-8) // SCALAR("a scalar",single-quoted) // STREAM-END // // 2. An explicit document: // // --- // 'a scalar' // ... // // Tokens: // // STREAM-START(utf-8) // DOCUMENT-START // SCALAR("a scalar",single-quoted) // DOCUMENT-END // STREAM-END // // 3. Several documents in a stream: // // 'a scalar' // --- // 'another scalar' // --- // 'yet another scalar' // // Tokens: // // STREAM-START(utf-8) // SCALAR("a scalar",single-quoted) // DOCUMENT-START // SCALAR("another scalar",single-quoted) // DOCUMENT-START // SCALAR("yet another scalar",single-quoted) // STREAM-END // // We have already introduced the SCALAR token above. The following tokens are // used to describe aliases, anchors, tag, and scalars: // // ALIAS(anchor) // ANCHOR(anchor) // TAG(handle,suffix) // SCALAR(value,style) // // The following series of examples illustrate the usage of these tokens: // // 1. A recursive sequence: // // &A [ *A ] // // Tokens: // // STREAM-START(utf-8) // ANCHOR("A") // FLOW-SEQUENCE-START // ALIAS("A") // FLOW-SEQUENCE-END // STREAM-END // // 2. A tagged scalar: // // !!float "3.14" # A good approximation. // // Tokens: // // STREAM-START(utf-8) // TAG("!!","float") // SCALAR("3.14",double-quoted) // STREAM-END // // 3. Various scalar styles: // // --- # Implicit empty plain scalars do not produce tokens. // --- a plain scalar // --- 'a single-quoted scalar' // --- "a double-quoted scalar" // --- |- // a literal scalar // --- >- // a folded // scalar // // Tokens: // // STREAM-START(utf-8) // DOCUMENT-START // DOCUMENT-START // SCALAR("a plain scalar",plain) // DOCUMENT-START // SCALAR("a single-quoted scalar",single-quoted) // DOCUMENT-START // SCALAR("a double-quoted scalar",double-quoted) // DOCUMENT-START // SCALAR("a literal scalar",literal) // DOCUMENT-START // SCALAR("a folded scalar",folded) // STREAM-END // // Now it's time to review collection-related tokens. We will start with // flow collections: // // FLOW-SEQUENCE-START // FLOW-SEQUENCE-END // FLOW-MAPPING-START // FLOW-MAPPING-END // FLOW-ENTRY // KEY // VALUE // // The tokens FLOW-SEQUENCE-START, FLOW-SEQUENCE-END, FLOW-MAPPING-START, and // FLOW-MAPPING-END represent the indicators '[', ']', '{', and '}' // correspondingly. FLOW-ENTRY represent the ',' indicator. Finally the // indicators '?' and ':', which are used for denoting mapping keys and values, // are represented by the KEY and VALUE tokens. // // The following examples show flow collections: // // 1. A flow sequence: // // [item 1, item 2, item 3] // // Tokens: // // STREAM-START(utf-8) // FLOW-SEQUENCE-START // SCALAR("item 1",plain) // FLOW-ENTRY // SCALAR("item 2",plain) // FLOW-ENTRY // SCALAR("item 3",plain) // FLOW-SEQUENCE-END // STREAM-END // // 2. A flow mapping: // // { // a simple key: a value, # Note that the KEY token is produced. // ? a complex key: another value, // } // // Tokens: // // STREAM-START(utf-8) // FLOW-MAPPING-START // KEY // SCALAR("a simple key",plain) // VALUE // SCALAR("a value",plain) // FLOW-ENTRY // KEY // SCALAR("a complex key",plain) // VALUE // SCALAR("another value",plain) // FLOW-ENTRY // FLOW-MAPPING-END // STREAM-END // // A simple key is a key which is not denoted by the '?' indicator. Note that // the Scanner still produce the KEY token whenever it encounters a simple key. // // For scanning block collections, the following tokens are used (note that we // repeat KEY and VALUE here): // // BLOCK-SEQUENCE-START // BLOCK-MAPPING-START // BLOCK-END // BLOCK-ENTRY // KEY // VALUE // // The tokens BLOCK-SEQUENCE-START and BLOCK-MAPPING-START denote indentation // increase that precedes a block collection (cf. the INDENT token in Python). // The token BLOCK-END denote indentation decrease that ends a block collection // (cf. the DEDENT token in Python). However YAML has some syntax pecularities // that makes detections of these tokens more complex. // // The tokens BLOCK-ENTRY, KEY, and VALUE are used to represent the indicators // '-', '?', and ':' correspondingly. // // The following examples show how the tokens BLOCK-SEQUENCE-START, // BLOCK-MAPPING-START, and BLOCK-END are emitted by the Scanner: // // 1. Block sequences: // // - item 1 // - item 2 // - // - item 3.1 // - item 3.2 // - // key 1: value 1 // key 2: value 2 // // Tokens: // // STREAM-START(utf-8) // BLOCK-SEQUENCE-START // BLOCK-ENTRY // SCALAR("item 1",plain) // BLOCK-ENTRY // SCALAR("item 2",plain) // BLOCK-ENTRY // BLOCK-SEQUENCE-START // BLOCK-ENTRY // SCALAR("item 3.1",plain) // BLOCK-ENTRY // SCALAR("item 3.2",plain) // BLOCK-END // BLOCK-ENTRY // BLOCK-MAPPING-START // KEY // SCALAR("key 1",plain) // VALUE // SCALAR("value 1",plain) // KEY // SCALAR("key 2",plain) // VALUE // SCALAR("value 2",plain) // BLOCK-END // BLOCK-END // STREAM-END // // 2. Block mappings: // // a simple key: a value # The KEY token is produced here. // ? a complex key // : another value // a mapping: // key 1: value 1 // key 2: value 2 // a sequence: // - item 1 // - item 2 // // Tokens: // // STREAM-START(utf-8) // BLOCK-MAPPING-START // KEY // SCALAR("a simple key",plain) // VALUE // SCALAR("a value",plain) // KEY // SCALAR("a complex key",plain) // VALUE // SCALAR("another value",plain) // KEY // SCALAR("a mapping",plain) // BLOCK-MAPPING-START // KEY // SCALAR("key 1",plain) // VALUE // SCALAR("value 1",plain) // KEY // SCALAR("key 2",plain) // VALUE // SCALAR("value 2",plain) // BLOCK-END // KEY // SCALAR("a sequence",plain) // VALUE // BLOCK-SEQUENCE-START // BLOCK-ENTRY // SCALAR("item 1",plain) // BLOCK-ENTRY // SCALAR("item 2",plain) // BLOCK-END // BLOCK-END // STREAM-END // // YAML does not always require to start a new block collection from a new // line. If the current line contains only '-', '?', and ':' indicators, a new // block collection may start at the current line. The following examples // illustrate this case: // // 1. Collections in a sequence: // // - - item 1 // - item 2 // - key 1: value 1 // key 2: value 2 // - ? complex key // : complex value // // Tokens: // // STREAM-START(utf-8) // BLOCK-SEQUENCE-START // BLOCK-ENTRY // BLOCK-SEQUENCE-START // BLOCK-ENTRY // SCALAR("item 1",plain) // BLOCK-ENTRY // SCALAR("item 2",plain) // BLOCK-END // BLOCK-ENTRY // BLOCK-MAPPING-START // KEY // SCALAR("key 1",plain) // VALUE // SCALAR("value 1",plain) // KEY // SCALAR("key 2",plain) // VALUE // SCALAR("value 2",plain) // BLOCK-END // BLOCK-ENTRY // BLOCK-MAPPING-START // KEY // SCALAR("complex key") // VALUE // SCALAR("complex value") // BLOCK-END // BLOCK-END // STREAM-END // // 2. Collections in a mapping: // // ? a sequence // : - item 1 // - item 2 // ? a mapping // : key 1: value 1 // key 2: value 2 // // Tokens: // // STREAM-START(utf-8) // BLOCK-MAPPING-START // KEY // SCALAR("a sequence",plain) // VALUE // BLOCK-SEQUENCE-START // BLOCK-ENTRY // SCALAR("item 1",plain) // BLOCK-ENTRY // SCALAR("item 2",plain) // BLOCK-END // KEY // SCALAR("a mapping",plain) // VALUE // BLOCK-MAPPING-START // KEY // SCALAR("key 1",plain) // VALUE // SCALAR("value 1",plain) // KEY // SCALAR("key 2",plain) // VALUE // SCALAR("value 2",plain) // BLOCK-END // BLOCK-END // STREAM-END // // YAML also permits non-indented sequences if they are included into a block // mapping. In this case, the token BLOCK-SEQUENCE-START is not produced: // // key: // - item 1 # BLOCK-SEQUENCE-START is NOT produced here. // - item 2 // // Tokens: // // STREAM-START(utf-8) // BLOCK-MAPPING-START // KEY // SCALAR("key",plain) // VALUE // BLOCK-ENTRY // SCALAR("item 1",plain) // BLOCK-ENTRY // SCALAR("item 2",plain) // BLOCK-END // // Ensure that the buffer contains the required number of characters. // Return true on success, false on failure (reader error or memory error). func cache(parser *yaml_parser_t, length int) bool { // [Go] This was inlined: !cache(A, B) -> unread < B && !update(A, B) return parser.unread >= length || yaml_parser_update_buffer(parser, length) } // Advance the buffer pointer. func skip(parser *yaml_parser_t) { if !is_blank(parser.buffer, parser.buffer_pos) { parser.newlines = 0 } parser.mark.index++ parser.mark.column++ parser.unread-- parser.buffer_pos += width(parser.buffer[parser.buffer_pos]) } func skip_line(parser *yaml_parser_t) { if is_crlf(parser.buffer, parser.buffer_pos) { parser.mark.index += 2 parser.mark.column = 0 parser.mark.line++ parser.unread -= 2 parser.buffer_pos += 2 parser.newlines++ } else if is_break(parser.buffer, parser.buffer_pos) { parser.mark.index++ parser.mark.column = 0 parser.mark.line++ parser.unread-- parser.buffer_pos += width(parser.buffer[parser.buffer_pos]) parser.newlines++ } } // Copy a character to a string buffer and advance pointers. func read(parser *yaml_parser_t, s []byte) []byte { if !is_blank(parser.buffer, parser.buffer_pos) { parser.newlines = 0 } w := width(parser.buffer[parser.buffer_pos]) if w == 0 { panic("invalid character sequence") } if len(s) == 0 { s = make([]byte, 0, 32) } if w == 1 && len(s)+w <= cap(s) { s = s[:len(s)+1] s[len(s)-1] = parser.buffer[parser.buffer_pos] parser.buffer_pos++ } else { s = append(s, parser.buffer[parser.buffer_pos:parser.buffer_pos+w]...) parser.buffer_pos += w } parser.mark.index++ parser.mark.column++ parser.unread-- return s } // Copy a line break character to a string buffer and advance pointers. func read_line(parser *yaml_parser_t, s []byte) []byte { buf := parser.buffer pos := parser.buffer_pos switch { case buf[pos] == '\r' && buf[pos+1] == '\n': // CR LF . LF s = append(s, '\n') parser.buffer_pos += 2 parser.mark.index++ parser.unread-- case buf[pos] == '\r' || buf[pos] == '\n': // CR|LF . LF s = append(s, '\n') parser.buffer_pos += 1 case buf[pos] == '\xC2' && buf[pos+1] == '\x85': // NEL . LF s = append(s, '\n') parser.buffer_pos += 2 case buf[pos] == '\xE2' && buf[pos+1] == '\x80' && (buf[pos+2] == '\xA8' || buf[pos+2] == '\xA9'): // LS|PS . LS|PS s = append(s, buf[parser.buffer_pos:pos+3]...) parser.buffer_pos += 3 default: return s } parser.mark.index++ parser.mark.column = 0 parser.mark.line++ parser.unread-- parser.newlines++ return s } // Get the next token. func yaml_parser_scan(parser *yaml_parser_t, token *yaml_token_t) bool { // Erase the token object. *token = yaml_token_t{} // [Go] Is this necessary? // No tokens after STREAM-END or error. if parser.stream_end_produced || parser.error != yaml_NO_ERROR { return true } // Ensure that the tokens queue contains enough tokens. if !parser.token_available { if !yaml_parser_fetch_more_tokens(parser) { return false } } // Fetch the next token from the queue. *token = parser.tokens[parser.tokens_head] parser.tokens_head++ parser.tokens_parsed++ parser.token_available = false if token.typ == yaml_STREAM_END_TOKEN { parser.stream_end_produced = true } return true } // Set the scanner error and return false. func yaml_parser_set_scanner_error(parser *yaml_parser_t, context string, context_mark yaml_mark_t, problem string) bool { parser.error = yaml_SCANNER_ERROR parser.context = context parser.context_mark = context_mark parser.problem = problem parser.problem_mark = parser.mark return false } func yaml_parser_set_scanner_tag_error(parser *yaml_parser_t, directive bool, context_mark yaml_mark_t, problem string) bool { context := "while parsing a tag" if directive { context = "while parsing a %TAG directive" } return yaml_parser_set_scanner_error(parser, context, context_mark, problem) } func trace(args ...interface{}) func() { pargs := append([]interface{}{"+++"}, args...) fmt.Println(pargs...) pargs = append([]interface{}{"---"}, args...) return func() { fmt.Println(pargs...) } } // Ensure that the tokens queue contains at least one token which can be // returned to the Parser. func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool { // While we need more tokens to fetch, do it. for { // [Go] The comment parsing logic requires a lookahead of two tokens // so that foot comments may be parsed in time of associating them // with the tokens that are parsed before them, and also for line // comments to be transformed into head comments in some edge cases. if parser.tokens_head < len(parser.tokens)-2 { // If a potential simple key is at the head position, we need to fetch // the next token to disambiguate it. head_tok_idx, ok := parser.simple_keys_by_tok[parser.tokens_parsed] if !ok { break } else if valid, ok := yaml_simple_key_is_valid(parser, &parser.simple_keys[head_tok_idx]); !ok { return false } else if !valid { break } } // Fetch the next token. if !yaml_parser_fetch_next_token(parser) { return false } } parser.token_available = true return true } // The dispatcher for token fetchers. func yaml_parser_fetch_next_token(parser *yaml_parser_t) (ok bool) { // Ensure that the buffer is initialized. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } // Check if we just started scanning. Fetch STREAM-START then. if !parser.stream_start_produced { return yaml_parser_fetch_stream_start(parser) } scan_mark := parser.mark // Eat whitespaces and comments until we reach the next token. if !yaml_parser_scan_to_next_token(parser) { return false } // [Go] While unrolling indents, transform the head comments of prior // indentation levels observed after scan_start into foot comments at // the respective indexes. // Check the indentation level against the current column. if !yaml_parser_unroll_indent(parser, parser.mark.column, scan_mark) { return false } // Ensure that the buffer contains at least 4 characters. 4 is the length // of the longest indicators ('--- ' and '... '). if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { return false } // Is it the end of the stream? if is_z(parser.buffer, parser.buffer_pos) { return yaml_parser_fetch_stream_end(parser) } // Is it a directive? if parser.mark.column == 0 && parser.buffer[parser.buffer_pos] == '%' { return yaml_parser_fetch_directive(parser) } buf := parser.buffer pos := parser.buffer_pos // Is it the document start indicator? if parser.mark.column == 0 && buf[pos] == '-' && buf[pos+1] == '-' && buf[pos+2] == '-' && is_blankz(buf, pos+3) { return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_START_TOKEN) } // Is it the document end indicator? if parser.mark.column == 0 && buf[pos] == '.' && buf[pos+1] == '.' && buf[pos+2] == '.' && is_blankz(buf, pos+3) { return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_END_TOKEN) } comment_mark := parser.mark if len(parser.tokens) > 0 && (parser.flow_level == 0 && buf[pos] == ':' || parser.flow_level > 0 && buf[pos] == ',') { // Associate any following comments with the prior token. comment_mark = parser.tokens[len(parser.tokens)-1].start_mark } defer func() { if !ok { return } if len(parser.tokens) > 0 && parser.tokens[len(parser.tokens)-1].typ == yaml_BLOCK_ENTRY_TOKEN { // Sequence indicators alone have no line comments. It becomes // a head comment for whatever follows. return } if !yaml_parser_scan_line_comment(parser, comment_mark) { ok = false return } }() // Is it the flow sequence start indicator? if buf[pos] == '[' { return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_SEQUENCE_START_TOKEN) } // Is it the flow mapping start indicator? if parser.buffer[parser.buffer_pos] == '{' { return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_MAPPING_START_TOKEN) } // Is it the flow sequence end indicator? if parser.buffer[parser.buffer_pos] == ']' { return yaml_parser_fetch_flow_collection_end(parser, yaml_FLOW_SEQUENCE_END_TOKEN) } // Is it the flow mapping end indicator? if parser.buffer[parser.buffer_pos] == '}' { return yaml_parser_fetch_flow_collection_end(parser, yaml_FLOW_MAPPING_END_TOKEN) } // Is it the flow entry indicator? if parser.buffer[parser.buffer_pos] == ',' { return yaml_parser_fetch_flow_entry(parser) } // Is it the block entry indicator? if parser.buffer[parser.buffer_pos] == '-' && is_blankz(parser.buffer, parser.buffer_pos+1) { return yaml_parser_fetch_block_entry(parser) } // Is it the key indicator? if parser.buffer[parser.buffer_pos] == '?' && (parser.flow_level > 0 || is_blankz(parser.buffer, parser.buffer_pos+1)) { return yaml_parser_fetch_key(parser) } // Is it the value indicator? if parser.buffer[parser.buffer_pos] == ':' && (parser.flow_level > 0 || is_blankz(parser.buffer, parser.buffer_pos+1)) { return yaml_parser_fetch_value(parser) } // Is it an alias? if parser.buffer[parser.buffer_pos] == '*' { return yaml_parser_fetch_anchor(parser, yaml_ALIAS_TOKEN) } // Is it an anchor? if parser.buffer[parser.buffer_pos] == '&' { return yaml_parser_fetch_anchor(parser, yaml_ANCHOR_TOKEN) } // Is it a tag? if parser.buffer[parser.buffer_pos] == '!' { return yaml_parser_fetch_tag(parser) } // Is it a literal scalar? if parser.buffer[parser.buffer_pos] == '|' && parser.flow_level == 0 { return yaml_parser_fetch_block_scalar(parser, true) } // Is it a folded scalar? if parser.buffer[parser.buffer_pos] == '>' && parser.flow_level == 0 { return yaml_parser_fetch_block_scalar(parser, false) } // Is it a single-quoted scalar? if parser.buffer[parser.buffer_pos] == '\'' { return yaml_parser_fetch_flow_scalar(parser, true) } // Is it a double-quoted scalar? if parser.buffer[parser.buffer_pos] == '"' { return yaml_parser_fetch_flow_scalar(parser, false) } // Is it a plain scalar? // // A plain scalar may start with any non-blank characters except // // '-', '?', ':', ',', '[', ']', '{', '}', // '#', '&', '*', '!', '|', '>', '\'', '\"', // '%', '@', '`'. // // In the block context (and, for the '-' indicator, in the flow context // too), it may also start with the characters // // '-', '?', ':' // // if it is followed by a non-space character. // // The last rule is more restrictive than the specification requires. // [Go] TODO Make this logic more reasonable. //switch parser.buffer[parser.buffer_pos] { //case '-', '?', ':', ',', '?', '-', ',', ':', ']', '[', '}', '{', '&', '#', '!', '*', '>', '|', '"', '\'', '@', '%', '-', '`': //} if !(is_blankz(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == '-' || parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '[' || parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '{' || parser.buffer[parser.buffer_pos] == '}' || parser.buffer[parser.buffer_pos] == '#' || parser.buffer[parser.buffer_pos] == '&' || parser.buffer[parser.buffer_pos] == '*' || parser.buffer[parser.buffer_pos] == '!' || parser.buffer[parser.buffer_pos] == '|' || parser.buffer[parser.buffer_pos] == '>' || parser.buffer[parser.buffer_pos] == '\'' || parser.buffer[parser.buffer_pos] == '"' || parser.buffer[parser.buffer_pos] == '%' || parser.buffer[parser.buffer_pos] == '@' || parser.buffer[parser.buffer_pos] == '`') || (parser.buffer[parser.buffer_pos] == '-' && !is_blank(parser.buffer, parser.buffer_pos+1)) || (parser.flow_level == 0 && (parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':') && !is_blankz(parser.buffer, parser.buffer_pos+1)) { return yaml_parser_fetch_plain_scalar(parser) } // If we don't determine the token type so far, it is an error. return yaml_parser_set_scanner_error(parser, "while scanning for the next token", parser.mark, "found character that cannot start any token") } func yaml_simple_key_is_valid(parser *yaml_parser_t, simple_key *yaml_simple_key_t) (valid, ok bool) { if !simple_key.possible { return false, true } // The 1.2 specification says: // // "If the ? indicator is omitted, parsing needs to see past the // implicit key to recognize it as such. To limit the amount of // lookahead required, the “:” indicator must appear at most 1024 // Unicode characters beyond the start of the key. In addition, the key // is restricted to a single line." // if simple_key.mark.line < parser.mark.line || simple_key.mark.index+1024 < parser.mark.index { // Check if the potential simple key to be removed is required. if simple_key.required { return false, yaml_parser_set_scanner_error(parser, "while scanning a simple key", simple_key.mark, "could not find expected ':'") } simple_key.possible = false return false, true } return true, true } // Check if a simple key may start at the current position and add it if // needed. func yaml_parser_save_simple_key(parser *yaml_parser_t) bool { // A simple key is required at the current position if the scanner is in // the block context and the current column coincides with the indentation // level. required := parser.flow_level == 0 && parser.indent == parser.mark.column // // If the current position may start a simple key, save it. // if parser.simple_key_allowed { simple_key := yaml_simple_key_t{ possible: true, required: required, token_number: parser.tokens_parsed + (len(parser.tokens) - parser.tokens_head), mark: parser.mark, } if !yaml_parser_remove_simple_key(parser) { return false } parser.simple_keys[len(parser.simple_keys)-1] = simple_key parser.simple_keys_by_tok[simple_key.token_number] = len(parser.simple_keys) - 1 } return true } // Remove a potential simple key at the current flow level. func yaml_parser_remove_simple_key(parser *yaml_parser_t) bool { i := len(parser.simple_keys) - 1 if parser.simple_keys[i].possible { // If the key is required, it is an error. if parser.simple_keys[i].required { return yaml_parser_set_scanner_error(parser, "while scanning a simple key", parser.simple_keys[i].mark, "could not find expected ':'") } // Remove the key from the stack. parser.simple_keys[i].possible = false delete(parser.simple_keys_by_tok, parser.simple_keys[i].token_number) } return true } // max_flow_level limits the flow_level const max_flow_level = 10000 // Increase the flow level and resize the simple key list if needed. func yaml_parser_increase_flow_level(parser *yaml_parser_t) bool { // Reset the simple key on the next level. parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{ possible: false, required: false, token_number: parser.tokens_parsed + (len(parser.tokens) - parser.tokens_head), mark: parser.mark, }) // Increase the flow level. parser.flow_level++ if parser.flow_level > max_flow_level { return yaml_parser_set_scanner_error(parser, "while increasing flow level", parser.simple_keys[len(parser.simple_keys)-1].mark, fmt.Sprintf("exceeded max depth of %d", max_flow_level)) } return true } // Decrease the flow level. func yaml_parser_decrease_flow_level(parser *yaml_parser_t) bool { if parser.flow_level > 0 { parser.flow_level-- last := len(parser.simple_keys) - 1 delete(parser.simple_keys_by_tok, parser.simple_keys[last].token_number) parser.simple_keys = parser.simple_keys[:last] } return true } // max_indents limits the indents stack size const max_indents = 10000 // Push the current indentation level to the stack and set the new level // the current column is greater than the indentation level. In this case, // append or insert the specified token into the token queue. func yaml_parser_roll_indent(parser *yaml_parser_t, column, number int, typ yaml_token_type_t, mark yaml_mark_t) bool { // In the flow context, do nothing. if parser.flow_level > 0 { return true } if parser.indent < column { // Push the current indentation level to the stack and set the new // indentation level. parser.indents = append(parser.indents, parser.indent) parser.indent = column if len(parser.indents) > max_indents { return yaml_parser_set_scanner_error(parser, "while increasing indent level", parser.simple_keys[len(parser.simple_keys)-1].mark, fmt.Sprintf("exceeded max depth of %d", max_indents)) } // Create a token and insert it into the queue. token := yaml_token_t{ typ: typ, start_mark: mark, end_mark: mark, } if number > -1 { number -= parser.tokens_parsed } yaml_insert_token(parser, number, &token) } return true } // Pop indentation levels from the indents stack until the current level // becomes less or equal to the column. For each indentation level, append // the BLOCK-END token. func yaml_parser_unroll_indent(parser *yaml_parser_t, column int, scan_mark yaml_mark_t) bool { // In the flow context, do nothing. if parser.flow_level > 0 { return true } block_mark := scan_mark block_mark.index-- // Loop through the indentation levels in the stack. for parser.indent > column { // [Go] Reposition the end token before potential following // foot comments of parent blocks. For that, search // backwards for recent comments that were at the same // indent as the block that is ending now. stop_index := block_mark.index for i := len(parser.comments) - 1; i >= 0; i-- { comment := &parser.comments[i] if comment.end_mark.index < stop_index { // Don't go back beyond the start of the comment/whitespace scan, unless column < 0. // If requested indent column is < 0, then the document is over and everything else // is a foot anyway. break } if comment.start_mark.column == parser.indent+1 { // This is a good match. But maybe there's a former comment // at that same indent level, so keep searching. block_mark = comment.start_mark } // While the end of the former comment matches with // the start of the following one, we know there's // nothing in between and scanning is still safe. stop_index = comment.scan_mark.index } // Create a token and append it to the queue. token := yaml_token_t{ typ: yaml_BLOCK_END_TOKEN, start_mark: block_mark, end_mark: block_mark, } yaml_insert_token(parser, -1, &token) // Pop the indentation level. parser.indent = parser.indents[len(parser.indents)-1] parser.indents = parser.indents[:len(parser.indents)-1] } return true } // Initialize the scanner and produce the STREAM-START token. func yaml_parser_fetch_stream_start(parser *yaml_parser_t) bool { // Set the initial indentation. parser.indent = -1 // Initialize the simple key stack. parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{}) parser.simple_keys_by_tok = make(map[int]int) // A simple key is allowed at the beginning of the stream. parser.simple_key_allowed = true // We have started. parser.stream_start_produced = true // Create the STREAM-START token and append it to the queue. token := yaml_token_t{ typ: yaml_STREAM_START_TOKEN, start_mark: parser.mark, end_mark: parser.mark, encoding: parser.encoding, } yaml_insert_token(parser, -1, &token) return true } // Produce the STREAM-END token and shut down the scanner. func yaml_parser_fetch_stream_end(parser *yaml_parser_t) bool { // Force new line. if parser.mark.column != 0 { parser.mark.column = 0 parser.mark.line++ } // Reset the indentation level. if !yaml_parser_unroll_indent(parser, -1, parser.mark) { return false } // Reset simple keys. if !yaml_parser_remove_simple_key(parser) { return false } parser.simple_key_allowed = false // Create the STREAM-END token and append it to the queue. token := yaml_token_t{ typ: yaml_STREAM_END_TOKEN, start_mark: parser.mark, end_mark: parser.mark, } yaml_insert_token(parser, -1, &token) return true } // Produce a VERSION-DIRECTIVE or TAG-DIRECTIVE token. func yaml_parser_fetch_directive(parser *yaml_parser_t) bool { // Reset the indentation level. if !yaml_parser_unroll_indent(parser, -1, parser.mark) { return false } // Reset simple keys. if !yaml_parser_remove_simple_key(parser) { return false } parser.simple_key_allowed = false // Create the YAML-DIRECTIVE or TAG-DIRECTIVE token. token := yaml_token_t{} if !yaml_parser_scan_directive(parser, &token) { return false } // Append the token to the queue. yaml_insert_token(parser, -1, &token) return true } // Produce the DOCUMENT-START or DOCUMENT-END token. func yaml_parser_fetch_document_indicator(parser *yaml_parser_t, typ yaml_token_type_t) bool { // Reset the indentation level. if !yaml_parser_unroll_indent(parser, -1, parser.mark) { return false } // Reset simple keys. if !yaml_parser_remove_simple_key(parser) { return false } parser.simple_key_allowed = false // Consume the token. start_mark := parser.mark skip(parser) skip(parser) skip(parser) end_mark := parser.mark // Create the DOCUMENT-START or DOCUMENT-END token. token := yaml_token_t{ typ: typ, start_mark: start_mark, end_mark: end_mark, } // Append the token to the queue. yaml_insert_token(parser, -1, &token) return true } // Produce the FLOW-SEQUENCE-START or FLOW-MAPPING-START token. func yaml_parser_fetch_flow_collection_start(parser *yaml_parser_t, typ yaml_token_type_t) bool { // The indicators '[' and '{' may start a simple key. if !yaml_parser_save_simple_key(parser) { return false } // Increase the flow level. if !yaml_parser_increase_flow_level(parser) { return false } // A simple key may follow the indicators '[' and '{'. parser.simple_key_allowed = true // Consume the token. start_mark := parser.mark skip(parser) end_mark := parser.mark // Create the FLOW-SEQUENCE-START of FLOW-MAPPING-START token. token := yaml_token_t{ typ: typ, start_mark: start_mark, end_mark: end_mark, } // Append the token to the queue. yaml_insert_token(parser, -1, &token) return true } // Produce the FLOW-SEQUENCE-END or FLOW-MAPPING-END token. func yaml_parser_fetch_flow_collection_end(parser *yaml_parser_t, typ yaml_token_type_t) bool { // Reset any potential simple key on the current flow level. if !yaml_parser_remove_simple_key(parser) { return false } // Decrease the flow level. if !yaml_parser_decrease_flow_level(parser) { return false } // No simple keys after the indicators ']' and '}'. parser.simple_key_allowed = false // Consume the token. start_mark := parser.mark skip(parser) end_mark := parser.mark // Create the FLOW-SEQUENCE-END of FLOW-MAPPING-END token. token := yaml_token_t{ typ: typ, start_mark: start_mark, end_mark: end_mark, } // Append the token to the queue. yaml_insert_token(parser, -1, &token) return true } // Produce the FLOW-ENTRY token. func yaml_parser_fetch_flow_entry(parser *yaml_parser_t) bool { // Reset any potential simple keys on the current flow level. if !yaml_parser_remove_simple_key(parser) { return false } // Simple keys are allowed after ','. parser.simple_key_allowed = true // Consume the token. start_mark := parser.mark skip(parser) end_mark := parser.mark // Create the FLOW-ENTRY token and append it to the queue. token := yaml_token_t{ typ: yaml_FLOW_ENTRY_TOKEN, start_mark: start_mark, end_mark: end_mark, } yaml_insert_token(parser, -1, &token) return true } // Produce the BLOCK-ENTRY token. func yaml_parser_fetch_block_entry(parser *yaml_parser_t) bool { // Check if the scanner is in the block context. if parser.flow_level == 0 { // Check if we are allowed to start a new entry. if !parser.simple_key_allowed { return yaml_parser_set_scanner_error(parser, "", parser.mark, "block sequence entries are not allowed in this context") } // Add the BLOCK-SEQUENCE-START token if needed. if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_SEQUENCE_START_TOKEN, parser.mark) { return false } } else { // It is an error for the '-' indicator to occur in the flow context, // but we let the Parser detect and report about it because the Parser // is able to point to the context. } // Reset any potential simple keys on the current flow level. if !yaml_parser_remove_simple_key(parser) { return false } // Simple keys are allowed after '-'. parser.simple_key_allowed = true // Consume the token. start_mark := parser.mark skip(parser) end_mark := parser.mark // Create the BLOCK-ENTRY token and append it to the queue. token := yaml_token_t{ typ: yaml_BLOCK_ENTRY_TOKEN, start_mark: start_mark, end_mark: end_mark, } yaml_insert_token(parser, -1, &token) return true } // Produce the KEY token. func yaml_parser_fetch_key(parser *yaml_parser_t) bool { // In the block context, additional checks are required. if parser.flow_level == 0 { // Check if we are allowed to start a new key (not nessesary simple). if !parser.simple_key_allowed { return yaml_parser_set_scanner_error(parser, "", parser.mark, "mapping keys are not allowed in this context") } // Add the BLOCK-MAPPING-START token if needed. if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_MAPPING_START_TOKEN, parser.mark) { return false } } // Reset any potential simple keys on the current flow level. if !yaml_parser_remove_simple_key(parser) { return false } // Simple keys are allowed after '?' in the block context. parser.simple_key_allowed = parser.flow_level == 0 // Consume the token. start_mark := parser.mark skip(parser) end_mark := parser.mark // Create the KEY token and append it to the queue. token := yaml_token_t{ typ: yaml_KEY_TOKEN, start_mark: start_mark, end_mark: end_mark, } yaml_insert_token(parser, -1, &token) return true } // Produce the VALUE token. func yaml_parser_fetch_value(parser *yaml_parser_t) bool { simple_key := &parser.simple_keys[len(parser.simple_keys)-1] // Have we found a simple key? if valid, ok := yaml_simple_key_is_valid(parser, simple_key); !ok { return false } else if valid { // Create the KEY token and insert it into the queue. token := yaml_token_t{ typ: yaml_KEY_TOKEN, start_mark: simple_key.mark, end_mark: simple_key.mark, } yaml_insert_token(parser, simple_key.token_number-parser.tokens_parsed, &token) // In the block context, we may need to add the BLOCK-MAPPING-START token. if !yaml_parser_roll_indent(parser, simple_key.mark.column, simple_key.token_number, yaml_BLOCK_MAPPING_START_TOKEN, simple_key.mark) { return false } // Remove the simple key. simple_key.possible = false delete(parser.simple_keys_by_tok, simple_key.token_number) // A simple key cannot follow another simple key. parser.simple_key_allowed = false } else { // The ':' indicator follows a complex key. // In the block context, extra checks are required. if parser.flow_level == 0 { // Check if we are allowed to start a complex value. if !parser.simple_key_allowed { return yaml_parser_set_scanner_error(parser, "", parser.mark, "mapping values are not allowed in this context") } // Add the BLOCK-MAPPING-START token if needed. if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_MAPPING_START_TOKEN, parser.mark) { return false } } // Simple keys after ':' are allowed in the block context. parser.simple_key_allowed = parser.flow_level == 0 } // Consume the token. start_mark := parser.mark skip(parser) end_mark := parser.mark // Create the VALUE token and append it to the queue. token := yaml_token_t{ typ: yaml_VALUE_TOKEN, start_mark: start_mark, end_mark: end_mark, } yaml_insert_token(parser, -1, &token) return true } // Produce the ALIAS or ANCHOR token. func yaml_parser_fetch_anchor(parser *yaml_parser_t, typ yaml_token_type_t) bool { // An anchor or an alias could be a simple key. if !yaml_parser_save_simple_key(parser) { return false } // A simple key cannot follow an anchor or an alias. parser.simple_key_allowed = false // Create the ALIAS or ANCHOR token and append it to the queue. var token yaml_token_t if !yaml_parser_scan_anchor(parser, &token, typ) { return false } yaml_insert_token(parser, -1, &token) return true } // Produce the TAG token. func yaml_parser_fetch_tag(parser *yaml_parser_t) bool { // A tag could be a simple key. if !yaml_parser_save_simple_key(parser) { return false } // A simple key cannot follow a tag. parser.simple_key_allowed = false // Create the TAG token and append it to the queue. var token yaml_token_t if !yaml_parser_scan_tag(parser, &token) { return false } yaml_insert_token(parser, -1, &token) return true } // Produce the SCALAR(...,literal) or SCALAR(...,folded) tokens. func yaml_parser_fetch_block_scalar(parser *yaml_parser_t, literal bool) bool { // Remove any potential simple keys. if !yaml_parser_remove_simple_key(parser) { return false } // A simple key may follow a block scalar. parser.simple_key_allowed = true // Create the SCALAR token and append it to the queue. var token yaml_token_t if !yaml_parser_scan_block_scalar(parser, &token, literal) { return false } yaml_insert_token(parser, -1, &token) return true } // Produce the SCALAR(...,single-quoted) or SCALAR(...,double-quoted) tokens. func yaml_parser_fetch_flow_scalar(parser *yaml_parser_t, single bool) bool { // A plain scalar could be a simple key. if !yaml_parser_save_simple_key(parser) { return false } // A simple key cannot follow a flow scalar. parser.simple_key_allowed = false // Create the SCALAR token and append it to the queue. var token yaml_token_t if !yaml_parser_scan_flow_scalar(parser, &token, single) { return false } yaml_insert_token(parser, -1, &token) return true } // Produce the SCALAR(...,plain) token. func yaml_parser_fetch_plain_scalar(parser *yaml_parser_t) bool { // A plain scalar could be a simple key. if !yaml_parser_save_simple_key(parser) { return false } // A simple key cannot follow a flow scalar. parser.simple_key_allowed = false // Create the SCALAR token and append it to the queue. var token yaml_token_t if !yaml_parser_scan_plain_scalar(parser, &token) { return false } yaml_insert_token(parser, -1, &token) return true } // Eat whitespaces and comments until the next token is found. func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool { scan_mark := parser.mark // Until the next token is not found. for { // Allow the BOM mark to start a line. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } if parser.mark.column == 0 && is_bom(parser.buffer, parser.buffer_pos) { skip(parser) } // Eat whitespaces. // Tabs are allowed: // - in the flow context // - in the block context, but not at the beginning of the line or // after '-', '?', or ':' (complex value). if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for parser.buffer[parser.buffer_pos] == ' ' || ((parser.flow_level > 0 || !parser.simple_key_allowed) && parser.buffer[parser.buffer_pos] == '\t') { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Check if we just had a line comment under a sequence entry that // looks more like a header to the following content. Similar to this: // // - # The comment // - Some data // // If so, transform the line comment to a head comment and reposition. if len(parser.comments) > 0 && len(parser.tokens) > 1 { tokenA := parser.tokens[len(parser.tokens)-2] tokenB := parser.tokens[len(parser.tokens)-1] comment := &parser.comments[len(parser.comments)-1] if tokenA.typ == yaml_BLOCK_SEQUENCE_START_TOKEN && tokenB.typ == yaml_BLOCK_ENTRY_TOKEN && len(comment.line) > 0 && !is_break(parser.buffer, parser.buffer_pos) { // If it was in the prior line, reposition so it becomes a // header of the follow up token. Otherwise, keep it in place // so it becomes a header of the former. comment.head = comment.line comment.line = nil if comment.start_mark.line == parser.mark.line-1 { comment.token_mark = parser.mark } } } // Eat a comment until a line break. if parser.buffer[parser.buffer_pos] == '#' { if !yaml_parser_scan_comments(parser, scan_mark) { return false } } // If it is a line break, eat it. if is_break(parser.buffer, parser.buffer_pos) { if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } skip_line(parser) // In the block context, a new line may start a simple key. if parser.flow_level == 0 { parser.simple_key_allowed = true } } else { break // We have found a token. } } return true } // Scan a YAML-DIRECTIVE or TAG-DIRECTIVE token. // // Scope: // %YAML 1.1 # a comment \n // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // %TAG !yaml! tag:yaml.org,2002: \n // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool { // Eat '%'. start_mark := parser.mark skip(parser) // Scan the directive name. var name []byte if !yaml_parser_scan_directive_name(parser, start_mark, &name) { return false } // Is it a YAML directive? if bytes.Equal(name, []byte("YAML")) { // Scan the VERSION directive value. var major, minor int8 if !yaml_parser_scan_version_directive_value(parser, start_mark, &major, &minor) { return false } end_mark := parser.mark // Create a VERSION-DIRECTIVE token. *token = yaml_token_t{ typ: yaml_VERSION_DIRECTIVE_TOKEN, start_mark: start_mark, end_mark: end_mark, major: major, minor: minor, } // Is it a TAG directive? } else if bytes.Equal(name, []byte("TAG")) { // Scan the TAG directive value. var handle, prefix []byte if !yaml_parser_scan_tag_directive_value(parser, start_mark, &handle, &prefix) { return false } end_mark := parser.mark // Create a TAG-DIRECTIVE token. *token = yaml_token_t{ typ: yaml_TAG_DIRECTIVE_TOKEN, start_mark: start_mark, end_mark: end_mark, value: handle, prefix: prefix, } // Unknown directive. } else { yaml_parser_set_scanner_error(parser, "while scanning a directive", start_mark, "found unknown directive name") return false } // Eat the rest of the line including any comments. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for is_blank(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } if parser.buffer[parser.buffer_pos] == '#' { // [Go] Discard this inline comment for the time being. //if !yaml_parser_scan_line_comment(parser, start_mark) { // return false //} for !is_breakz(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } } // Check if we are at the end of the line. if !is_breakz(parser.buffer, parser.buffer_pos) { yaml_parser_set_scanner_error(parser, "while scanning a directive", start_mark, "did not find expected comment or line break") return false } // Eat a line break. if is_break(parser.buffer, parser.buffer_pos) { if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } skip_line(parser) } return true } // Scan the directive name. // // Scope: // %YAML 1.1 # a comment \n // ^^^^ // %TAG !yaml! tag:yaml.org,2002: \n // ^^^ // func yaml_parser_scan_directive_name(parser *yaml_parser_t, start_mark yaml_mark_t, name *[]byte) bool { // Consume the directive name. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } var s []byte for is_alpha(parser.buffer, parser.buffer_pos) { s = read(parser, s) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Check if the name is empty. if len(s) == 0 { yaml_parser_set_scanner_error(parser, "while scanning a directive", start_mark, "could not find expected directive name") return false } // Check for an blank character after the name. if !is_blankz(parser.buffer, parser.buffer_pos) { yaml_parser_set_scanner_error(parser, "while scanning a directive", start_mark, "found unexpected non-alphabetical character") return false } *name = s return true } // Scan the value of VERSION-DIRECTIVE. // // Scope: // %YAML 1.1 # a comment \n // ^^^^^^ func yaml_parser_scan_version_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, major, minor *int8) bool { // Eat whitespaces. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for is_blank(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Consume the major version number. if !yaml_parser_scan_version_directive_number(parser, start_mark, major) { return false } // Eat '.'. if parser.buffer[parser.buffer_pos] != '.' { return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", start_mark, "did not find expected digit or '.' character") } skip(parser) // Consume the minor version number. if !yaml_parser_scan_version_directive_number(parser, start_mark, minor) { return false } return true } const max_number_length = 2 // Scan the version number of VERSION-DIRECTIVE. // // Scope: // %YAML 1.1 # a comment \n // ^ // %YAML 1.1 # a comment \n // ^ func yaml_parser_scan_version_directive_number(parser *yaml_parser_t, start_mark yaml_mark_t, number *int8) bool { // Repeat while the next character is digit. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } var value, length int8 for is_digit(parser.buffer, parser.buffer_pos) { // Check if the number is too long. length++ if length > max_number_length { return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", start_mark, "found extremely long version number") } value = value*10 + int8(as_digit(parser.buffer, parser.buffer_pos)) skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Check if the number was present. if length == 0 { return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", start_mark, "did not find expected version number") } *number = value return true } // Scan the value of a TAG-DIRECTIVE token. // // Scope: // %TAG !yaml! tag:yaml.org,2002: \n // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // func yaml_parser_scan_tag_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, handle, prefix *[]byte) bool { var handle_value, prefix_value []byte // Eat whitespaces. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for is_blank(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Scan a handle. if !yaml_parser_scan_tag_handle(parser, true, start_mark, &handle_value) { return false } // Expect a whitespace. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } if !is_blank(parser.buffer, parser.buffer_pos) { yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive", start_mark, "did not find expected whitespace") return false } // Eat whitespaces. for is_blank(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Scan a prefix. if !yaml_parser_scan_tag_uri(parser, true, nil, start_mark, &prefix_value) { return false } // Expect a whitespace or line break. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } if !is_blankz(parser.buffer, parser.buffer_pos) { yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive", start_mark, "did not find expected whitespace or line break") return false } *handle = handle_value *prefix = prefix_value return true } func yaml_parser_scan_anchor(parser *yaml_parser_t, token *yaml_token_t, typ yaml_token_type_t) bool { var s []byte // Eat the indicator character. start_mark := parser.mark skip(parser) // Consume the value. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for is_alpha(parser.buffer, parser.buffer_pos) { s = read(parser, s) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } end_mark := parser.mark /* * Check if length of the anchor is greater than 0 and it is followed by * a whitespace character or one of the indicators: * * '?', ':', ',', ']', '}', '%', '@', '`'. */ if len(s) == 0 || !(is_blankz(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '}' || parser.buffer[parser.buffer_pos] == '%' || parser.buffer[parser.buffer_pos] == '@' || parser.buffer[parser.buffer_pos] == '`') { context := "while scanning an alias" if typ == yaml_ANCHOR_TOKEN { context = "while scanning an anchor" } yaml_parser_set_scanner_error(parser, context, start_mark, "did not find expected alphabetic or numeric character") return false } // Create a token. *token = yaml_token_t{ typ: typ, start_mark: start_mark, end_mark: end_mark, value: s, } return true } /* * Scan a TAG token. */ func yaml_parser_scan_tag(parser *yaml_parser_t, token *yaml_token_t) bool { var handle, suffix []byte start_mark := parser.mark // Check if the tag is in the canonical form. if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } if parser.buffer[parser.buffer_pos+1] == '<' { // Keep the handle as '' // Eat '!<' skip(parser) skip(parser) // Consume the tag value. if !yaml_parser_scan_tag_uri(parser, false, nil, start_mark, &suffix) { return false } // Check for '>' and eat it. if parser.buffer[parser.buffer_pos] != '>' { yaml_parser_set_scanner_error(parser, "while scanning a tag", start_mark, "did not find the expected '>'") return false } skip(parser) } else { // The tag has either the '!suffix' or the '!handle!suffix' form. // First, try to scan a handle. if !yaml_parser_scan_tag_handle(parser, false, start_mark, &handle) { return false } // Check if it is, indeed, handle. if handle[0] == '!' && len(handle) > 1 && handle[len(handle)-1] == '!' { // Scan the suffix now. if !yaml_parser_scan_tag_uri(parser, false, nil, start_mark, &suffix) { return false } } else { // It wasn't a handle after all. Scan the rest of the tag. if !yaml_parser_scan_tag_uri(parser, false, handle, start_mark, &suffix) { return false } // Set the handle to '!'. handle = []byte{'!'} // A special case: the '!' tag. Set the handle to '' and the // suffix to '!'. if len(suffix) == 0 { handle, suffix = suffix, handle } } } // Check the character which ends the tag. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } if !is_blankz(parser.buffer, parser.buffer_pos) { yaml_parser_set_scanner_error(parser, "while scanning a tag", start_mark, "did not find expected whitespace or line break") return false } end_mark := parser.mark // Create a token. *token = yaml_token_t{ typ: yaml_TAG_TOKEN, start_mark: start_mark, end_mark: end_mark, value: handle, suffix: suffix, } return true } // Scan a tag handle. func yaml_parser_scan_tag_handle(parser *yaml_parser_t, directive bool, start_mark yaml_mark_t, handle *[]byte) bool { // Check the initial '!' character. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } if parser.buffer[parser.buffer_pos] != '!' { yaml_parser_set_scanner_tag_error(parser, directive, start_mark, "did not find expected '!'") return false } var s []byte // Copy the '!' character. s = read(parser, s) // Copy all subsequent alphabetical and numerical characters. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for is_alpha(parser.buffer, parser.buffer_pos) { s = read(parser, s) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Check if the trailing character is '!' and copy it. if parser.buffer[parser.buffer_pos] == '!' { s = read(parser, s) } else { // It's either the '!' tag or not really a tag handle. If it's a %TAG // directive, it's an error. If it's a tag token, it must be a part of URI. if directive && string(s) != "!" { yaml_parser_set_scanner_tag_error(parser, directive, start_mark, "did not find expected '!'") return false } } *handle = s return true } // Scan a tag. func yaml_parser_scan_tag_uri(parser *yaml_parser_t, directive bool, head []byte, start_mark yaml_mark_t, uri *[]byte) bool { //size_t length = head ? strlen((char *)head) : 0 var s []byte hasTag := len(head) > 0 // Copy the head if needed. // // Note that we don't copy the leading '!' character. if len(head) > 1 { s = append(s, head[1:]...) } // Scan the tag. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } // The set of characters that may appear in URI is as follows: // // '0'-'9', 'A'-'Z', 'a'-'z', '_', '-', ';', '/', '?', ':', '@', '&', // '=', '+', '$', ',', '.', '!', '~', '*', '\'', '(', ')', '[', ']', // '%'. // [Go] TODO Convert this into more reasonable logic. for is_alpha(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == ';' || parser.buffer[parser.buffer_pos] == '/' || parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == '@' || parser.buffer[parser.buffer_pos] == '&' || parser.buffer[parser.buffer_pos] == '=' || parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '$' || parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '.' || parser.buffer[parser.buffer_pos] == '!' || parser.buffer[parser.buffer_pos] == '~' || parser.buffer[parser.buffer_pos] == '*' || parser.buffer[parser.buffer_pos] == '\'' || parser.buffer[parser.buffer_pos] == '(' || parser.buffer[parser.buffer_pos] == ')' || parser.buffer[parser.buffer_pos] == '[' || parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '%' { // Check if it is a URI-escape sequence. if parser.buffer[parser.buffer_pos] == '%' { if !yaml_parser_scan_uri_escapes(parser, directive, start_mark, &s) { return false } } else { s = read(parser, s) } if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } hasTag = true } if !hasTag { yaml_parser_set_scanner_tag_error(parser, directive, start_mark, "did not find expected tag URI") return false } *uri = s return true } // Decode an URI-escape sequence corresponding to a single UTF-8 character. func yaml_parser_scan_uri_escapes(parser *yaml_parser_t, directive bool, start_mark yaml_mark_t, s *[]byte) bool { // Decode the required number of characters. w := 1024 for w > 0 { // Check for a URI-escaped octet. if parser.unread < 3 && !yaml_parser_update_buffer(parser, 3) { return false } if !(parser.buffer[parser.buffer_pos] == '%' && is_hex(parser.buffer, parser.buffer_pos+1) && is_hex(parser.buffer, parser.buffer_pos+2)) { return yaml_parser_set_scanner_tag_error(parser, directive, start_mark, "did not find URI escaped octet") } // Get the octet. octet := byte((as_hex(parser.buffer, parser.buffer_pos+1) << 4) + as_hex(parser.buffer, parser.buffer_pos+2)) // If it is the leading octet, determine the length of the UTF-8 sequence. if w == 1024 { w = width(octet) if w == 0 { return yaml_parser_set_scanner_tag_error(parser, directive, start_mark, "found an incorrect leading UTF-8 octet") } } else { // Check if the trailing octet is correct. if octet&0xC0 != 0x80 { return yaml_parser_set_scanner_tag_error(parser, directive, start_mark, "found an incorrect trailing UTF-8 octet") } } // Copy the octet and move the pointers. *s = append(*s, octet) skip(parser) skip(parser) skip(parser) w-- } return true } // Scan a block scalar. func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, literal bool) bool { // Eat the indicator '|' or '>'. start_mark := parser.mark skip(parser) // Scan the additional block scalar indicators. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } // Check for a chomping indicator. var chomping, increment int if parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '-' { // Set the chomping method and eat the indicator. if parser.buffer[parser.buffer_pos] == '+' { chomping = +1 } else { chomping = -1 } skip(parser) // Check for an indentation indicator. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } if is_digit(parser.buffer, parser.buffer_pos) { // Check that the indentation is greater than 0. if parser.buffer[parser.buffer_pos] == '0' { yaml_parser_set_scanner_error(parser, "while scanning a block scalar", start_mark, "found an indentation indicator equal to 0") return false } // Get the indentation level and eat the indicator. increment = as_digit(parser.buffer, parser.buffer_pos) skip(parser) } } else if is_digit(parser.buffer, parser.buffer_pos) { // Do the same as above, but in the opposite order. if parser.buffer[parser.buffer_pos] == '0' { yaml_parser_set_scanner_error(parser, "while scanning a block scalar", start_mark, "found an indentation indicator equal to 0") return false } increment = as_digit(parser.buffer, parser.buffer_pos) skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } if parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '-' { if parser.buffer[parser.buffer_pos] == '+' { chomping = +1 } else { chomping = -1 } skip(parser) } } // Eat whitespaces and comments to the end of the line. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for is_blank(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } if parser.buffer[parser.buffer_pos] == '#' { if !yaml_parser_scan_line_comment(parser, start_mark) { return false } for !is_breakz(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } } // Check if we are at the end of the line. if !is_breakz(parser.buffer, parser.buffer_pos) { yaml_parser_set_scanner_error(parser, "while scanning a block scalar", start_mark, "did not find expected comment or line break") return false } // Eat a line break. if is_break(parser.buffer, parser.buffer_pos) { if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } skip_line(parser) } end_mark := parser.mark // Set the indentation level if it was specified. var indent int if increment > 0 { if parser.indent >= 0 { indent = parser.indent + increment } else { indent = increment } } // Scan the leading line breaks and determine the indentation level if needed. var s, leading_break, trailing_breaks []byte if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) { return false } // Scan the block scalar content. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } var leading_blank, trailing_blank bool for parser.mark.column == indent && !is_z(parser.buffer, parser.buffer_pos) { // We are at the beginning of a non-empty line. // Is it a trailing whitespace? trailing_blank = is_blank(parser.buffer, parser.buffer_pos) // Check if we need to fold the leading line break. if !literal && !leading_blank && !trailing_blank && len(leading_break) > 0 && leading_break[0] == '\n' { // Do we need to join the lines by space? if len(trailing_breaks) == 0 { s = append(s, ' ') } } else { s = append(s, leading_break...) } leading_break = leading_break[:0] // Append the remaining line breaks. s = append(s, trailing_breaks...) trailing_breaks = trailing_breaks[:0] // Is it a leading whitespace? leading_blank = is_blank(parser.buffer, parser.buffer_pos) // Consume the current line. for !is_breakz(parser.buffer, parser.buffer_pos) { s = read(parser, s) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Consume the line break. if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } leading_break = read_line(parser, leading_break) // Eat the following indentation spaces and line breaks. if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) { return false } } // Chomp the tail. if chomping != -1 { s = append(s, leading_break...) } if chomping == 1 { s = append(s, trailing_breaks...) } // Create a token. *token = yaml_token_t{ typ: yaml_SCALAR_TOKEN, start_mark: start_mark, end_mark: end_mark, value: s, style: yaml_LITERAL_SCALAR_STYLE, } if !literal { token.style = yaml_FOLDED_SCALAR_STYLE } return true } // Scan indentation spaces and line breaks for a block scalar. Determine the // indentation level if needed. func yaml_parser_scan_block_scalar_breaks(parser *yaml_parser_t, indent *int, breaks *[]byte, start_mark yaml_mark_t, end_mark *yaml_mark_t) bool { *end_mark = parser.mark // Eat the indentation spaces and line breaks. max_indent := 0 for { // Eat the indentation spaces. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for (*indent == 0 || parser.mark.column < *indent) && is_space(parser.buffer, parser.buffer_pos) { skip(parser) if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } if parser.mark.column > max_indent { max_indent = parser.mark.column } // Check for a tab character messing the indentation. if (*indent == 0 || parser.mark.column < *indent) && is_tab(parser.buffer, parser.buffer_pos) { return yaml_parser_set_scanner_error(parser, "while scanning a block scalar", start_mark, "found a tab character where an indentation space is expected") } // Have we found a non-empty line? if !is_break(parser.buffer, parser.buffer_pos) { break } // Consume the line break. if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } // [Go] Should really be returning breaks instead. *breaks = read_line(parser, *breaks) *end_mark = parser.mark } // Determine the indentation level if needed. if *indent == 0 { *indent = max_indent if *indent < parser.indent+1 { *indent = parser.indent + 1 } if *indent < 1 { *indent = 1 } } return true } // Scan a quoted scalar. func yaml_parser_scan_flow_scalar(parser *yaml_parser_t, token *yaml_token_t, single bool) bool { // Eat the left quote. start_mark := parser.mark skip(parser) // Consume the content of the quoted scalar. var s, leading_break, trailing_breaks, whitespaces []byte for { // Check that there are no document indicators at the beginning of the line. if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { return false } if parser.mark.column == 0 && ((parser.buffer[parser.buffer_pos+0] == '-' && parser.buffer[parser.buffer_pos+1] == '-' && parser.buffer[parser.buffer_pos+2] == '-') || (parser.buffer[parser.buffer_pos+0] == '.' && parser.buffer[parser.buffer_pos+1] == '.' && parser.buffer[parser.buffer_pos+2] == '.')) && is_blankz(parser.buffer, parser.buffer_pos+3) { yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar", start_mark, "found unexpected document indicator") return false } // Check for EOF. if is_z(parser.buffer, parser.buffer_pos) { yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar", start_mark, "found unexpected end of stream") return false } // Consume non-blank characters. leading_blanks := false for !is_blankz(parser.buffer, parser.buffer_pos) { if single && parser.buffer[parser.buffer_pos] == '\'' && parser.buffer[parser.buffer_pos+1] == '\'' { // Is is an escaped single quote. s = append(s, '\'') skip(parser) skip(parser) } else if single && parser.buffer[parser.buffer_pos] == '\'' { // It is a right single quote. break } else if !single && parser.buffer[parser.buffer_pos] == '"' { // It is a right double quote. break } else if !single && parser.buffer[parser.buffer_pos] == '\\' && is_break(parser.buffer, parser.buffer_pos+1) { // It is an escaped line break. if parser.unread < 3 && !yaml_parser_update_buffer(parser, 3) { return false } skip(parser) skip_line(parser) leading_blanks = true break } else if !single && parser.buffer[parser.buffer_pos] == '\\' { // It is an escape sequence. code_length := 0 // Check the escape character. switch parser.buffer[parser.buffer_pos+1] { case '0': s = append(s, 0) case 'a': s = append(s, '\x07') case 'b': s = append(s, '\x08') case 't', '\t': s = append(s, '\x09') case 'n': s = append(s, '\x0A') case 'v': s = append(s, '\x0B') case 'f': s = append(s, '\x0C') case 'r': s = append(s, '\x0D') case 'e': s = append(s, '\x1B') case ' ': s = append(s, '\x20') case '"': s = append(s, '"') case '\'': s = append(s, '\'') case '\\': s = append(s, '\\') case 'N': // NEL (#x85) s = append(s, '\xC2') s = append(s, '\x85') case '_': // #xA0 s = append(s, '\xC2') s = append(s, '\xA0') case 'L': // LS (#x2028) s = append(s, '\xE2') s = append(s, '\x80') s = append(s, '\xA8') case 'P': // PS (#x2029) s = append(s, '\xE2') s = append(s, '\x80') s = append(s, '\xA9') case 'x': code_length = 2 case 'u': code_length = 4 case 'U': code_length = 8 default: yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", start_mark, "found unknown escape character") return false } skip(parser) skip(parser) // Consume an arbitrary escape code. if code_length > 0 { var value int // Scan the character value. if parser.unread < code_length && !yaml_parser_update_buffer(parser, code_length) { return false } for k := 0; k < code_length; k++ { if !is_hex(parser.buffer, parser.buffer_pos+k) { yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", start_mark, "did not find expected hexdecimal number") return false } value = (value << 4) + as_hex(parser.buffer, parser.buffer_pos+k) } // Check the value and write the character. if (value >= 0xD800 && value <= 0xDFFF) || value > 0x10FFFF { yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", start_mark, "found invalid Unicode character escape code") return false } if value <= 0x7F { s = append(s, byte(value)) } else if value <= 0x7FF { s = append(s, byte(0xC0+(value>>6))) s = append(s, byte(0x80+(value&0x3F))) } else if value <= 0xFFFF { s = append(s, byte(0xE0+(value>>12))) s = append(s, byte(0x80+((value>>6)&0x3F))) s = append(s, byte(0x80+(value&0x3F))) } else { s = append(s, byte(0xF0+(value>>18))) s = append(s, byte(0x80+((value>>12)&0x3F))) s = append(s, byte(0x80+((value>>6)&0x3F))) s = append(s, byte(0x80+(value&0x3F))) } // Advance the pointer. for k := 0; k < code_length; k++ { skip(parser) } } } else { // It is a non-escaped non-blank character. s = read(parser, s) } if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } } if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } // Check if we are at the end of the scalar. if single { if parser.buffer[parser.buffer_pos] == '\'' { break } } else { if parser.buffer[parser.buffer_pos] == '"' { break } } // Consume blank characters. for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) { if is_blank(parser.buffer, parser.buffer_pos) { // Consume a space or a tab character. if !leading_blanks { whitespaces = read(parser, whitespaces) } else { skip(parser) } } else { if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } // Check if it is a first line break. if !leading_blanks { whitespaces = whitespaces[:0] leading_break = read_line(parser, leading_break) leading_blanks = true } else { trailing_breaks = read_line(parser, trailing_breaks) } } if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Join the whitespaces or fold line breaks. if leading_blanks { // Do we need to fold line breaks? if len(leading_break) > 0 && leading_break[0] == '\n' { if len(trailing_breaks) == 0 { s = append(s, ' ') } else { s = append(s, trailing_breaks...) } } else { s = append(s, leading_break...) s = append(s, trailing_breaks...) } trailing_breaks = trailing_breaks[:0] leading_break = leading_break[:0] } else { s = append(s, whitespaces...) whitespaces = whitespaces[:0] } } // Eat the right quote. skip(parser) end_mark := parser.mark // Create a token. *token = yaml_token_t{ typ: yaml_SCALAR_TOKEN, start_mark: start_mark, end_mark: end_mark, value: s, style: yaml_SINGLE_QUOTED_SCALAR_STYLE, } if !single { token.style = yaml_DOUBLE_QUOTED_SCALAR_STYLE } return true } // Scan a plain scalar. func yaml_parser_scan_plain_scalar(parser *yaml_parser_t, token *yaml_token_t) bool { var s, leading_break, trailing_breaks, whitespaces []byte var leading_blanks bool var indent = parser.indent + 1 start_mark := parser.mark end_mark := parser.mark // Consume the content of the plain scalar. for { // Check for a document indicator. if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { return false } if parser.mark.column == 0 && ((parser.buffer[parser.buffer_pos+0] == '-' && parser.buffer[parser.buffer_pos+1] == '-' && parser.buffer[parser.buffer_pos+2] == '-') || (parser.buffer[parser.buffer_pos+0] == '.' && parser.buffer[parser.buffer_pos+1] == '.' && parser.buffer[parser.buffer_pos+2] == '.')) && is_blankz(parser.buffer, parser.buffer_pos+3) { break } // Check for a comment. if parser.buffer[parser.buffer_pos] == '#' { break } // Consume non-blank characters. for !is_blankz(parser.buffer, parser.buffer_pos) { // Check for indicators that may end a plain scalar. if (parser.buffer[parser.buffer_pos] == ':' && is_blankz(parser.buffer, parser.buffer_pos+1)) || (parser.flow_level > 0 && (parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == '[' || parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '{' || parser.buffer[parser.buffer_pos] == '}')) { break } // Check if we need to join whitespaces and breaks. if leading_blanks || len(whitespaces) > 0 { if leading_blanks { // Do we need to fold line breaks? if leading_break[0] == '\n' { if len(trailing_breaks) == 0 { s = append(s, ' ') } else { s = append(s, trailing_breaks...) } } else { s = append(s, leading_break...) s = append(s, trailing_breaks...) } trailing_breaks = trailing_breaks[:0] leading_break = leading_break[:0] leading_blanks = false } else { s = append(s, whitespaces...) whitespaces = whitespaces[:0] } } // Copy the character. s = read(parser, s) end_mark = parser.mark if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } } // Is it the end? if !(is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos)) { break } // Consume blank characters. if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) { if is_blank(parser.buffer, parser.buffer_pos) { // Check for tab characters that abuse indentation. if leading_blanks && parser.mark.column < indent && is_tab(parser.buffer, parser.buffer_pos) { yaml_parser_set_scanner_error(parser, "while scanning a plain scalar", start_mark, "found a tab character that violates indentation") return false } // Consume a space or a tab character. if !leading_blanks { whitespaces = read(parser, whitespaces) } else { skip(parser) } } else { if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } // Check if it is a first line break. if !leading_blanks { whitespaces = whitespaces[:0] leading_break = read_line(parser, leading_break) leading_blanks = true } else { trailing_breaks = read_line(parser, trailing_breaks) } } if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } } // Check indentation level. if parser.flow_level == 0 && parser.mark.column < indent { break } } // Create a token. *token = yaml_token_t{ typ: yaml_SCALAR_TOKEN, start_mark: start_mark, end_mark: end_mark, value: s, style: yaml_PLAIN_SCALAR_STYLE, } // Note that we change the 'simple_key_allowed' flag. if leading_blanks { parser.simple_key_allowed = true } return true } func yaml_parser_scan_line_comment(parser *yaml_parser_t, token_mark yaml_mark_t) bool { if parser.newlines > 0 { return true } var start_mark yaml_mark_t var text []byte for peek := 0; peek < 512; peek++ { if parser.unread < peek+1 && !yaml_parser_update_buffer(parser, peek+1) { break } if is_blank(parser.buffer, parser.buffer_pos+peek) { continue } if parser.buffer[parser.buffer_pos+peek] == '#' { seen := parser.mark.index+peek for { if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } if is_breakz(parser.buffer, parser.buffer_pos) { if parser.mark.index >= seen { break } if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } skip_line(parser) } else if parser.mark.index >= seen { if len(text) == 0 { start_mark = parser.mark } text = read(parser, text) } else { skip(parser) } } } break } if len(text) > 0 { parser.comments = append(parser.comments, yaml_comment_t{ token_mark: token_mark, start_mark: start_mark, line: text, }) } return true } func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) bool { token := parser.tokens[len(parser.tokens)-1] if token.typ == yaml_FLOW_ENTRY_TOKEN && len(parser.tokens) > 1 { token = parser.tokens[len(parser.tokens)-2] } var token_mark = token.start_mark var start_mark yaml_mark_t var next_indent = parser.indent if next_indent < 0 { next_indent = 0 } var recent_empty = false var first_empty = parser.newlines <= 1 var line = parser.mark.line var column = parser.mark.column var text []byte // The foot line is the place where a comment must start to // still be considered as a foot of the prior content. // If there's some content in the currently parsed line, then // the foot is the line below it. var foot_line = -1 if scan_mark.line > 0 { foot_line = parser.mark.line-parser.newlines+1 if parser.newlines == 0 && parser.mark.column > 1 { foot_line++ } } var peek = 0 for ; peek < 512; peek++ { if parser.unread < peek+1 && !yaml_parser_update_buffer(parser, peek+1) { break } column++ if is_blank(parser.buffer, parser.buffer_pos+peek) { continue } c := parser.buffer[parser.buffer_pos+peek] var close_flow = parser.flow_level > 0 && (c == ']' || c == '}') if close_flow || is_breakz(parser.buffer, parser.buffer_pos+peek) { // Got line break or terminator. if close_flow || !recent_empty { if close_flow || first_empty && (start_mark.line == foot_line && token.typ != yaml_VALUE_TOKEN || start_mark.column-1 < next_indent) { // This is the first empty line and there were no empty lines before, // so this initial part of the comment is a foot of the prior token // instead of being a head for the following one. Split it up. // Alternatively, this might also be the last comment inside a flow // scope, so it must be a footer. if len(text) > 0 { if start_mark.column-1 < next_indent { // If dedented it's unrelated to the prior token. token_mark = start_mark } parser.comments = append(parser.comments, yaml_comment_t{ scan_mark: scan_mark, token_mark: token_mark, start_mark: start_mark, end_mark: yaml_mark_t{parser.mark.index + peek, line, column}, foot: text, }) scan_mark = yaml_mark_t{parser.mark.index + peek, line, column} token_mark = scan_mark text = nil } } else { if len(text) > 0 && parser.buffer[parser.buffer_pos+peek] != 0 { text = append(text, '\n') } } } if !is_break(parser.buffer, parser.buffer_pos+peek) { break } first_empty = false recent_empty = true column = 0 line++ continue } if len(text) > 0 && (close_flow || column-1 < next_indent && column != start_mark.column) { // The comment at the different indentation is a foot of the // preceding data rather than a head of the upcoming one. parser.comments = append(parser.comments, yaml_comment_t{ scan_mark: scan_mark, token_mark: token_mark, start_mark: start_mark, end_mark: yaml_mark_t{parser.mark.index + peek, line, column}, foot: text, }) scan_mark = yaml_mark_t{parser.mark.index + peek, line, column} token_mark = scan_mark text = nil } if parser.buffer[parser.buffer_pos+peek] != '#' { break } if len(text) == 0 { start_mark = yaml_mark_t{parser.mark.index + peek, line, column} } else { text = append(text, '\n') } recent_empty = false // Consume until after the consumed comment line. seen := parser.mark.index+peek for { if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { return false } if is_breakz(parser.buffer, parser.buffer_pos) { if parser.mark.index >= seen { break } if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { return false } skip_line(parser) } else if parser.mark.index >= seen { text = read(parser, text) } else { skip(parser) } } peek = 0 column = 0 line = parser.mark.line next_indent = parser.indent if next_indent < 0 { next_indent = 0 } } if len(text) > 0 { parser.comments = append(parser.comments, yaml_comment_t{ scan_mark: scan_mark, token_mark: start_mark, start_mark: start_mark, end_mark: yaml_mark_t{parser.mark.index + peek - 1, line, column}, head: text, }) } return true } ================================================ FILE: vendor/github.com/oasdiff/yaml3/sorter.go ================================================ // // Copyright (c) 2011-2019 Canonical Ltd // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package yaml import ( "reflect" "unicode" ) type keyList []reflect.Value func (l keyList) Len() int { return len(l) } func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } func (l keyList) Less(i, j int) bool { a := l[i] b := l[j] ak := a.Kind() bk := b.Kind() for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() { a = a.Elem() ak = a.Kind() } for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() { b = b.Elem() bk = b.Kind() } af, aok := keyFloat(a) bf, bok := keyFloat(b) if aok && bok { if af != bf { return af < bf } if ak != bk { return ak < bk } return numLess(a, b) } if ak != reflect.String || bk != reflect.String { return ak < bk } ar, br := []rune(a.String()), []rune(b.String()) digits := false for i := 0; i < len(ar) && i < len(br); i++ { if ar[i] == br[i] { digits = unicode.IsDigit(ar[i]) continue } al := unicode.IsLetter(ar[i]) bl := unicode.IsLetter(br[i]) if al && bl { return ar[i] < br[i] } if al || bl { if digits { return al } else { return bl } } var ai, bi int var an, bn int64 if ar[i] == '0' || br[i] == '0' { for j := i - 1; j >= 0 && unicode.IsDigit(ar[j]); j-- { if ar[j] != '0' { an = 1 bn = 1 break } } } for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ { an = an*10 + int64(ar[ai]-'0') } for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ { bn = bn*10 + int64(br[bi]-'0') } if an != bn { return an < bn } if ai != bi { return ai < bi } return ar[i] < br[i] } return len(ar) < len(br) } // keyFloat returns a float value for v if it is a number/bool // and whether it is a number/bool or not. func keyFloat(v reflect.Value) (f float64, ok bool) { switch v.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return float64(v.Int()), true case reflect.Float32, reflect.Float64: return v.Float(), true case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return float64(v.Uint()), true case reflect.Bool: if v.Bool() { return 1, true } return 0, true } return 0, false } // numLess returns whether a < b. // a and b must necessarily have the same kind. func numLess(a, b reflect.Value) bool { switch a.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return a.Int() < b.Int() case reflect.Float32, reflect.Float64: return a.Float() < b.Float() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return a.Uint() < b.Uint() case reflect.Bool: return !a.Bool() && b.Bool() } panic("not a number") } ================================================ FILE: vendor/github.com/oasdiff/yaml3/writerc.go ================================================ // // Copyright (c) 2011-2019 Canonical Ltd // Copyright (c) 2006-2010 Kirill Simonov // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package yaml // Set the writer error and return false. func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool { emitter.error = yaml_WRITER_ERROR emitter.problem = problem return false } // Flush the output buffer. func yaml_emitter_flush(emitter *yaml_emitter_t) bool { if emitter.write_handler == nil { panic("write handler not set") } // Check if the buffer is empty. if emitter.buffer_pos == 0 { return true } if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil { return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) } emitter.buffer_pos = 0 return true } ================================================ FILE: vendor/github.com/oasdiff/yaml3/yaml.go ================================================ // // Copyright (c) 2011-2019 Canonical Ltd // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package yaml implements YAML support for the Go language. // // Source code and other details for the project are available at GitHub: // // https://github.com/go-yaml/yaml package yaml import ( "errors" "fmt" "io" "reflect" "strings" "sync" "unicode/utf8" ) // The Unmarshaler interface may be implemented by types to customize their // behavior when being unmarshaled from a YAML document. type Unmarshaler interface { UnmarshalYAML(value *Node) error } type obsoleteUnmarshaler interface { UnmarshalYAML(unmarshal func(interface{}) error) error } // The Marshaler interface may be implemented by types to customize their // behavior when being marshaled into a YAML document. The returned value // is marshaled in place of the original value implementing Marshaler. // // If an error is returned by MarshalYAML, the marshaling procedure stops // and returns with the provided error. type Marshaler interface { MarshalYAML() (interface{}, error) } // Unmarshal decodes the first document found within the in byte slice // and assigns decoded values into the out value. // // Maps and pointers (to a struct, string, int, etc) are accepted as out // values. If an internal pointer within a struct is not initialized, // the yaml package will initialize it if necessary for unmarshalling // the provided data. The out parameter must not be nil. // // The type of the decoded values should be compatible with the respective // values in out. If one or more values cannot be decoded due to a type // mismatches, decoding continues partially until the end of the YAML // content, and a *yaml.TypeError is returned with details for all // missed values. // // Struct fields are only unmarshalled if they are exported (have an // upper case first letter), and are unmarshalled using the field name // lowercased as the default key. Custom keys may be defined via the // "yaml" name in the field tag: the content preceding the first comma // is used as the key, and the following comma-separated options are // used to tweak the marshalling process (see Marshal). // Conflicting names result in a runtime error. // // For example: // // type T struct { // F int `yaml:"a,omitempty"` // B int // } // var t T // yaml.Unmarshal([]byte("a: 1\nb: 2"), &t) // // See the documentation of Marshal for the format of tags and a list of // supported tag options. func Unmarshal(in []byte, out interface{}) (err error) { return unmarshal(in, out, false) } // A Decoder reads and decodes YAML values from an input stream. type Decoder struct { parser *parser knownFields bool origin bool } // NewDecoder returns a new decoder that reads from r. // // The decoder introduces its own buffering and may read // data from r beyond the YAML values requested. func NewDecoder(r io.Reader) *Decoder { return &Decoder{ parser: newParserFromReader(r), } } // KnownFields ensures that the keys in decoded mappings to // exist as fields in the struct being decoded into. func (dec *Decoder) KnownFields(enable bool) { dec.knownFields = enable } // Origin enables the recording of the line and column of the // decoded values in the YAML content. func (dec *Decoder) Origin(enable bool) { dec.origin = enable } // Decode reads the next YAML-encoded value from its input // and stores it in the value pointed to by v. // // See the documentation for Unmarshal for details about the // conversion of YAML into a Go value. func (dec *Decoder) Decode(v interface{}) (err error) { d := newDecoder() d.knownFields = dec.knownFields d.origin = dec.origin defer handleErr(&err) node := dec.parser.parse() if node == nil { return io.EOF } out := reflect.ValueOf(v) if out.Kind() == reflect.Ptr && !out.IsNil() { out = out.Elem() } d.unmarshal(node, out) if len(d.terrors) > 0 { return &TypeError{d.terrors} } return nil } // Decode decodes the node and stores its data into the value pointed to by v. // // See the documentation for Unmarshal for details about the // conversion of YAML into a Go value. func (n *Node) Decode(v interface{}) (err error) { d := newDecoder() defer handleErr(&err) out := reflect.ValueOf(v) if out.Kind() == reflect.Ptr && !out.IsNil() { out = out.Elem() } d.unmarshal(n, out) if len(d.terrors) > 0 { return &TypeError{d.terrors} } return nil } func unmarshal(in []byte, out interface{}, strict bool) (err error) { defer handleErr(&err) d := newDecoder() p := newParser(in) defer p.destroy() node := p.parse() if node != nil { v := reflect.ValueOf(out) if v.Kind() == reflect.Ptr && !v.IsNil() { v = v.Elem() } d.unmarshal(node, v) } if len(d.terrors) > 0 { return &TypeError{d.terrors} } return nil } // Marshal serializes the value provided into a YAML document. The structure // of the generated document will reflect the structure of the value itself. // Maps and pointers (to struct, string, int, etc) are accepted as the in value. // // Struct fields are only marshalled if they are exported (have an upper case // first letter), and are marshalled using the field name lowercased as the // default key. Custom keys may be defined via the "yaml" name in the field // tag: the content preceding the first comma is used as the key, and the // following comma-separated options are used to tweak the marshalling process. // Conflicting names result in a runtime error. // // The field tag format accepted is: // // `(...) yaml:"[][,[,]]" (...)` // // The following flags are currently supported: // // omitempty Only include the field if it's not set to the zero // value for the type or to empty slices or maps. // Zero valued structs will be omitted if all their public // fields are zero, unless they implement an IsZero // method (see the IsZeroer interface type), in which // case the field will be excluded if IsZero returns true. // // flow Marshal using a flow style (useful for structs, // sequences and maps). // // inline Inline the field, which must be a struct or a map, // causing all of its fields or keys to be processed as if // they were part of the outer struct. For maps, keys must // not conflict with the yaml keys of other struct fields. // // In addition, if the key is "-", the field is ignored. // // For example: // // type T struct { // F int `yaml:"a,omitempty"` // B int // } // yaml.Marshal(&T{B: 2}) // Returns "b: 2\n" // yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n" func Marshal(in interface{}) (out []byte, err error) { defer handleErr(&err) e := newEncoder() defer e.destroy() e.marshalDoc("", reflect.ValueOf(in)) e.finish() out = e.out return } // An Encoder writes YAML values to an output stream. type Encoder struct { encoder *encoder } // NewEncoder returns a new encoder that writes to w. // The Encoder should be closed after use to flush all data // to w. func NewEncoder(w io.Writer) *Encoder { return &Encoder{ encoder: newEncoderWithWriter(w), } } // Encode writes the YAML encoding of v to the stream. // If multiple items are encoded to the stream, the // second and subsequent document will be preceded // with a "---" document separator, but the first will not. // // See the documentation for Marshal for details about the conversion of Go // values to YAML. func (e *Encoder) Encode(v interface{}) (err error) { defer handleErr(&err) e.encoder.marshalDoc("", reflect.ValueOf(v)) return nil } // Encode encodes value v and stores its representation in n. // // See the documentation for Marshal for details about the // conversion of Go values into YAML. func (n *Node) Encode(v interface{}) (err error) { defer handleErr(&err) e := newEncoder() defer e.destroy() e.marshalDoc("", reflect.ValueOf(v)) e.finish() p := newParser(e.out) p.textless = true defer p.destroy() doc := p.parse() *n = *doc.Content[0] return nil } // SetIndent changes the used indentation used when encoding. func (e *Encoder) SetIndent(spaces int) { if spaces < 0 { panic("yaml: cannot indent to a negative number of spaces") } e.encoder.indent = spaces } // Close closes the encoder by writing any remaining data. // It does not write a stream terminating string "...". func (e *Encoder) Close() (err error) { defer handleErr(&err) e.encoder.finish() return nil } func handleErr(err *error) { if v := recover(); v != nil { if e, ok := v.(yamlError); ok { *err = e.err } else { panic(v) } } } type yamlError struct { err error } func fail(err error) { panic(yamlError{err}) } func failf(format string, args ...interface{}) { panic(yamlError{fmt.Errorf("yaml: "+format, args...)}) } // A TypeError is returned by Unmarshal when one or more fields in // the YAML document cannot be properly decoded into the requested // types. When this error is returned, the value is still // unmarshaled partially. type TypeError struct { Errors []string } func (e *TypeError) Error() string { return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n ")) } type Kind uint32 const ( DocumentNode Kind = 1 << iota SequenceNode MappingNode ScalarNode AliasNode ) type Style uint32 const ( TaggedStyle Style = 1 << iota DoubleQuotedStyle SingleQuotedStyle LiteralStyle FoldedStyle FlowStyle ) // Node represents an element in the YAML document hierarchy. While documents // are typically encoded and decoded into higher level types, such as structs // and maps, Node is an intermediate representation that allows detailed // control over the content being decoded or encoded. // // It's worth noting that although Node offers access into details such as // line numbers, colums, and comments, the content when re-encoded will not // have its original textual representation preserved. An effort is made to // render the data plesantly, and to preserve comments near the data they // describe, though. // // Values that make use of the Node type interact with the yaml package in the // same way any other type would do, by encoding and decoding yaml data // directly or indirectly into them. // // For example: // // var person struct { // Name string // Address yaml.Node // } // err := yaml.Unmarshal(data, &person) // // Or by itself: // // var person Node // err := yaml.Unmarshal(data, &person) type Node struct { // Kind defines whether the node is a document, a mapping, a sequence, // a scalar value, or an alias to another node. The specific data type of // scalar nodes may be obtained via the ShortTag and LongTag methods. Kind Kind // Style allows customizing the apperance of the node in the tree. Style Style // Tag holds the YAML tag defining the data type for the value. // When decoding, this field will always be set to the resolved tag, // even when it wasn't explicitly provided in the YAML content. // When encoding, if this field is unset the value type will be // implied from the node properties, and if it is set, it will only // be serialized into the representation if TaggedStyle is used or // the implicit tag diverges from the provided one. Tag string // Value holds the unescaped and unquoted represenation of the value. Value string // Anchor holds the anchor name for this node, which allows aliases to point to it. Anchor string // Alias holds the node that this alias points to. Only valid when Kind is AliasNode. Alias *Node // Content holds contained nodes for documents, mappings, and sequences. Content []*Node // HeadComment holds any comments in the lines preceding the node and // not separated by an empty line. HeadComment string // LineComment holds any comments at the end of the line where the node is in. LineComment string // FootComment holds any comments following the node and before empty lines. FootComment string // Line and Column hold the node position in the decoded YAML text. // These fields are not respected when encoding the node. Line int Column int } // IsZero returns whether the node has all of its fields unset. func (n *Node) IsZero() bool { return n.Kind == 0 && n.Style == 0 && n.Tag == "" && n.Value == "" && n.Anchor == "" && n.Alias == nil && n.Content == nil && n.HeadComment == "" && n.LineComment == "" && n.FootComment == "" && n.Line == 0 && n.Column == 0 } // LongTag returns the long form of the tag that indicates the data type for // the node. If the Tag field isn't explicitly defined, one will be computed // based on the node properties. func (n *Node) LongTag() string { return longTag(n.ShortTag()) } // ShortTag returns the short form of the YAML tag that indicates data type for // the node. If the Tag field isn't explicitly defined, one will be computed // based on the node properties. func (n *Node) ShortTag() string { if n.indicatedString() { return strTag } if n.Tag == "" || n.Tag == "!" { switch n.Kind { case MappingNode: return mapTag case SequenceNode: return seqTag case AliasNode: if n.Alias != nil { return n.Alias.ShortTag() } case ScalarNode: tag, _ := resolve("", n.Value) return tag case 0: // Special case to make the zero value convenient. if n.IsZero() { return nullTag } } return "" } return shortTag(n.Tag) } func (n *Node) indicatedString() bool { return n.Kind == ScalarNode && (shortTag(n.Tag) == strTag || (n.Tag == "" || n.Tag == "!") && n.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0) } // SetString is a convenience function that sets the node to a string value // and defines its style in a pleasant way depending on its content. func (n *Node) SetString(s string) { n.Kind = ScalarNode if utf8.ValidString(s) { n.Value = s n.Tag = strTag } else { n.Value = encodeBase64(s) n.Tag = binaryTag } if strings.Contains(n.Value, "\n") { n.Style = LiteralStyle } } // -------------------------------------------------------------------------- // Maintain a mapping of keys to structure field indexes // The code in this section was copied from mgo/bson. // structInfo holds details for the serialization of fields of // a given struct. type structInfo struct { FieldsMap map[string]fieldInfo FieldsList []fieldInfo // InlineMap is the number of the field in the struct that // contains an ,inline map, or -1 if there's none. InlineMap int // InlineUnmarshalers holds indexes to inlined fields that // contain unmarshaler values. InlineUnmarshalers [][]int } type fieldInfo struct { Key string Num int OmitEmpty bool Flow bool // Id holds the unique field identifier, so we can cheaply // check for field duplicates without maintaining an extra map. Id int // Inline holds the field index if the field is part of an inlined struct. Inline []int } var structMap = make(map[reflect.Type]*structInfo) var fieldMapMutex sync.RWMutex var unmarshalerType reflect.Type func init() { var v Unmarshaler unmarshalerType = reflect.ValueOf(&v).Elem().Type() } func getStructInfo(st reflect.Type) (*structInfo, error) { fieldMapMutex.RLock() sinfo, found := structMap[st] fieldMapMutex.RUnlock() if found { return sinfo, nil } n := st.NumField() fieldsMap := make(map[string]fieldInfo) fieldsList := make([]fieldInfo, 0, n) inlineMap := -1 inlineUnmarshalers := [][]int(nil) for i := 0; i != n; i++ { field := st.Field(i) if field.PkgPath != "" && !field.Anonymous { continue // Private field } info := fieldInfo{Num: i} tag := field.Tag.Get("yaml") if tag == "" && strings.Index(string(field.Tag), ":") < 0 { tag = string(field.Tag) } if tag == "-" { continue } inline := false fields := strings.Split(tag, ",") if len(fields) > 1 { for _, flag := range fields[1:] { switch flag { case "omitempty": info.OmitEmpty = true case "flow": info.Flow = true case "inline": inline = true default: return nil, errors.New(fmt.Sprintf("unsupported flag %q in tag %q of type %s", flag, tag, st)) } } tag = fields[0] } if inline { switch field.Type.Kind() { case reflect.Map: if inlineMap >= 0 { return nil, errors.New("multiple ,inline maps in struct " + st.String()) } if field.Type.Key() != reflect.TypeOf("") { return nil, errors.New("option ,inline needs a map with string keys in struct " + st.String()) } inlineMap = info.Num case reflect.Struct, reflect.Ptr: ftype := field.Type for ftype.Kind() == reflect.Ptr { ftype = ftype.Elem() } if ftype.Kind() != reflect.Struct { return nil, errors.New("option ,inline may only be used on a struct or map field") } if reflect.PtrTo(ftype).Implements(unmarshalerType) { inlineUnmarshalers = append(inlineUnmarshalers, []int{i}) } else { sinfo, err := getStructInfo(ftype) if err != nil { return nil, err } for _, index := range sinfo.InlineUnmarshalers { inlineUnmarshalers = append(inlineUnmarshalers, append([]int{i}, index...)) } for _, finfo := range sinfo.FieldsList { if _, found := fieldsMap[finfo.Key]; found { msg := "duplicated key '" + finfo.Key + "' in struct " + st.String() return nil, errors.New(msg) } if finfo.Inline == nil { finfo.Inline = []int{i, finfo.Num} } else { finfo.Inline = append([]int{i}, finfo.Inline...) } finfo.Id = len(fieldsList) fieldsMap[finfo.Key] = finfo fieldsList = append(fieldsList, finfo) } } default: return nil, errors.New("option ,inline may only be used on a struct or map field") } continue } if tag != "" { info.Key = tag } else { info.Key = strings.ToLower(field.Name) } if _, found = fieldsMap[info.Key]; found { msg := "duplicated key '" + info.Key + "' in struct " + st.String() return nil, errors.New(msg) } info.Id = len(fieldsList) fieldsList = append(fieldsList, info) fieldsMap[info.Key] = info } sinfo = &structInfo{ FieldsMap: fieldsMap, FieldsList: fieldsList, InlineMap: inlineMap, InlineUnmarshalers: inlineUnmarshalers, } fieldMapMutex.Lock() structMap[st] = sinfo fieldMapMutex.Unlock() return sinfo, nil } // IsZeroer is used to check whether an object is zero to // determine whether it should be omitted when marshaling // with the omitempty flag. One notable implementation // is time.Time. type IsZeroer interface { IsZero() bool } func isZero(v reflect.Value) bool { kind := v.Kind() if z, ok := v.Interface().(IsZeroer); ok { if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() { return true } return z.IsZero() } switch kind { case reflect.String: return len(v.String()) == 0 case reflect.Interface, reflect.Ptr: return v.IsNil() case reflect.Slice: return v.Len() == 0 case reflect.Map: return v.Len() == 0 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return v.Int() == 0 case reflect.Float32, reflect.Float64: return v.Float() == 0 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return v.Uint() == 0 case reflect.Bool: return !v.Bool() case reflect.Struct: vt := v.Type() for i := v.NumField() - 1; i >= 0; i-- { if vt.Field(i).PkgPath != "" { continue // Private field } if !isZero(v.Field(i)) { return false } } return true } return false } ================================================ FILE: vendor/github.com/oasdiff/yaml3/yamlh.go ================================================ // // Copyright (c) 2011-2019 Canonical Ltd // Copyright (c) 2006-2010 Kirill Simonov // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package yaml import ( "fmt" "io" ) // The version directive data. type yaml_version_directive_t struct { major int8 // The major version number. minor int8 // The minor version number. } // The tag directive data. type yaml_tag_directive_t struct { handle []byte // The tag handle. prefix []byte // The tag prefix. } type yaml_encoding_t int // The stream encoding. const ( // Let the parser choose the encoding. yaml_ANY_ENCODING yaml_encoding_t = iota yaml_UTF8_ENCODING // The default UTF-8 encoding. yaml_UTF16LE_ENCODING // The UTF-16-LE encoding with BOM. yaml_UTF16BE_ENCODING // The UTF-16-BE encoding with BOM. ) type yaml_break_t int // Line break types. const ( // Let the parser choose the break type. yaml_ANY_BREAK yaml_break_t = iota yaml_CR_BREAK // Use CR for line breaks (Mac style). yaml_LN_BREAK // Use LN for line breaks (Unix style). yaml_CRLN_BREAK // Use CR LN for line breaks (DOS style). ) type yaml_error_type_t int // Many bad things could happen with the parser and emitter. const ( // No error is produced. yaml_NO_ERROR yaml_error_type_t = iota yaml_MEMORY_ERROR // Cannot allocate or reallocate a block of memory. yaml_READER_ERROR // Cannot read or decode the input stream. yaml_SCANNER_ERROR // Cannot scan the input stream. yaml_PARSER_ERROR // Cannot parse the input stream. yaml_COMPOSER_ERROR // Cannot compose a YAML document. yaml_WRITER_ERROR // Cannot write to the output stream. yaml_EMITTER_ERROR // Cannot emit a YAML stream. ) // The pointer position. type yaml_mark_t struct { index int // The position index. line int // The position line. column int // The position column. } // Node Styles type yaml_style_t int8 type yaml_scalar_style_t yaml_style_t // Scalar styles. const ( // Let the emitter choose the style. yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = 0 yaml_PLAIN_SCALAR_STYLE yaml_scalar_style_t = 1 << iota // The plain scalar style. yaml_SINGLE_QUOTED_SCALAR_STYLE // The single-quoted scalar style. yaml_DOUBLE_QUOTED_SCALAR_STYLE // The double-quoted scalar style. yaml_LITERAL_SCALAR_STYLE // The literal scalar style. yaml_FOLDED_SCALAR_STYLE // The folded scalar style. ) type yaml_sequence_style_t yaml_style_t // Sequence styles. const ( // Let the emitter choose the style. yaml_ANY_SEQUENCE_STYLE yaml_sequence_style_t = iota yaml_BLOCK_SEQUENCE_STYLE // The block sequence style. yaml_FLOW_SEQUENCE_STYLE // The flow sequence style. ) type yaml_mapping_style_t yaml_style_t // Mapping styles. const ( // Let the emitter choose the style. yaml_ANY_MAPPING_STYLE yaml_mapping_style_t = iota yaml_BLOCK_MAPPING_STYLE // The block mapping style. yaml_FLOW_MAPPING_STYLE // The flow mapping style. ) // Tokens type yaml_token_type_t int // Token types. const ( // An empty token. yaml_NO_TOKEN yaml_token_type_t = iota yaml_STREAM_START_TOKEN // A STREAM-START token. yaml_STREAM_END_TOKEN // A STREAM-END token. yaml_VERSION_DIRECTIVE_TOKEN // A VERSION-DIRECTIVE token. yaml_TAG_DIRECTIVE_TOKEN // A TAG-DIRECTIVE token. yaml_DOCUMENT_START_TOKEN // A DOCUMENT-START token. yaml_DOCUMENT_END_TOKEN // A DOCUMENT-END token. yaml_BLOCK_SEQUENCE_START_TOKEN // A BLOCK-SEQUENCE-START token. yaml_BLOCK_MAPPING_START_TOKEN // A BLOCK-SEQUENCE-END token. yaml_BLOCK_END_TOKEN // A BLOCK-END token. yaml_FLOW_SEQUENCE_START_TOKEN // A FLOW-SEQUENCE-START token. yaml_FLOW_SEQUENCE_END_TOKEN // A FLOW-SEQUENCE-END token. yaml_FLOW_MAPPING_START_TOKEN // A FLOW-MAPPING-START token. yaml_FLOW_MAPPING_END_TOKEN // A FLOW-MAPPING-END token. yaml_BLOCK_ENTRY_TOKEN // A BLOCK-ENTRY token. yaml_FLOW_ENTRY_TOKEN // A FLOW-ENTRY token. yaml_KEY_TOKEN // A KEY token. yaml_VALUE_TOKEN // A VALUE token. yaml_ALIAS_TOKEN // An ALIAS token. yaml_ANCHOR_TOKEN // An ANCHOR token. yaml_TAG_TOKEN // A TAG token. yaml_SCALAR_TOKEN // A SCALAR token. ) func (tt yaml_token_type_t) String() string { switch tt { case yaml_NO_TOKEN: return "yaml_NO_TOKEN" case yaml_STREAM_START_TOKEN: return "yaml_STREAM_START_TOKEN" case yaml_STREAM_END_TOKEN: return "yaml_STREAM_END_TOKEN" case yaml_VERSION_DIRECTIVE_TOKEN: return "yaml_VERSION_DIRECTIVE_TOKEN" case yaml_TAG_DIRECTIVE_TOKEN: return "yaml_TAG_DIRECTIVE_TOKEN" case yaml_DOCUMENT_START_TOKEN: return "yaml_DOCUMENT_START_TOKEN" case yaml_DOCUMENT_END_TOKEN: return "yaml_DOCUMENT_END_TOKEN" case yaml_BLOCK_SEQUENCE_START_TOKEN: return "yaml_BLOCK_SEQUENCE_START_TOKEN" case yaml_BLOCK_MAPPING_START_TOKEN: return "yaml_BLOCK_MAPPING_START_TOKEN" case yaml_BLOCK_END_TOKEN: return "yaml_BLOCK_END_TOKEN" case yaml_FLOW_SEQUENCE_START_TOKEN: return "yaml_FLOW_SEQUENCE_START_TOKEN" case yaml_FLOW_SEQUENCE_END_TOKEN: return "yaml_FLOW_SEQUENCE_END_TOKEN" case yaml_FLOW_MAPPING_START_TOKEN: return "yaml_FLOW_MAPPING_START_TOKEN" case yaml_FLOW_MAPPING_END_TOKEN: return "yaml_FLOW_MAPPING_END_TOKEN" case yaml_BLOCK_ENTRY_TOKEN: return "yaml_BLOCK_ENTRY_TOKEN" case yaml_FLOW_ENTRY_TOKEN: return "yaml_FLOW_ENTRY_TOKEN" case yaml_KEY_TOKEN: return "yaml_KEY_TOKEN" case yaml_VALUE_TOKEN: return "yaml_VALUE_TOKEN" case yaml_ALIAS_TOKEN: return "yaml_ALIAS_TOKEN" case yaml_ANCHOR_TOKEN: return "yaml_ANCHOR_TOKEN" case yaml_TAG_TOKEN: return "yaml_TAG_TOKEN" case yaml_SCALAR_TOKEN: return "yaml_SCALAR_TOKEN" } return "" } // The token structure. type yaml_token_t struct { // The token type. typ yaml_token_type_t // The start/end of the token. start_mark, end_mark yaml_mark_t // The stream encoding (for yaml_STREAM_START_TOKEN). encoding yaml_encoding_t // The alias/anchor/scalar value or tag/tag directive handle // (for yaml_ALIAS_TOKEN, yaml_ANCHOR_TOKEN, yaml_SCALAR_TOKEN, yaml_TAG_TOKEN, yaml_TAG_DIRECTIVE_TOKEN). value []byte // The tag suffix (for yaml_TAG_TOKEN). suffix []byte // The tag directive prefix (for yaml_TAG_DIRECTIVE_TOKEN). prefix []byte // The scalar style (for yaml_SCALAR_TOKEN). style yaml_scalar_style_t // The version directive major/minor (for yaml_VERSION_DIRECTIVE_TOKEN). major, minor int8 } // Events type yaml_event_type_t int8 // Event types. const ( // An empty event. yaml_NO_EVENT yaml_event_type_t = iota yaml_STREAM_START_EVENT // A STREAM-START event. yaml_STREAM_END_EVENT // A STREAM-END event. yaml_DOCUMENT_START_EVENT // A DOCUMENT-START event. yaml_DOCUMENT_END_EVENT // A DOCUMENT-END event. yaml_ALIAS_EVENT // An ALIAS event. yaml_SCALAR_EVENT // A SCALAR event. yaml_SEQUENCE_START_EVENT // A SEQUENCE-START event. yaml_SEQUENCE_END_EVENT // A SEQUENCE-END event. yaml_MAPPING_START_EVENT // A MAPPING-START event. yaml_MAPPING_END_EVENT // A MAPPING-END event. yaml_TAIL_COMMENT_EVENT ) var eventStrings = []string{ yaml_NO_EVENT: "none", yaml_STREAM_START_EVENT: "stream start", yaml_STREAM_END_EVENT: "stream end", yaml_DOCUMENT_START_EVENT: "document start", yaml_DOCUMENT_END_EVENT: "document end", yaml_ALIAS_EVENT: "alias", yaml_SCALAR_EVENT: "scalar", yaml_SEQUENCE_START_EVENT: "sequence start", yaml_SEQUENCE_END_EVENT: "sequence end", yaml_MAPPING_START_EVENT: "mapping start", yaml_MAPPING_END_EVENT: "mapping end", yaml_TAIL_COMMENT_EVENT: "tail comment", } func (e yaml_event_type_t) String() string { if e < 0 || int(e) >= len(eventStrings) { return fmt.Sprintf("unknown event %d", e) } return eventStrings[e] } // The event structure. type yaml_event_t struct { // The event type. typ yaml_event_type_t // The start and end of the event. start_mark, end_mark yaml_mark_t // The document encoding (for yaml_STREAM_START_EVENT). encoding yaml_encoding_t // The version directive (for yaml_DOCUMENT_START_EVENT). version_directive *yaml_version_directive_t // The list of tag directives (for yaml_DOCUMENT_START_EVENT). tag_directives []yaml_tag_directive_t // The comments head_comment []byte line_comment []byte foot_comment []byte tail_comment []byte // The anchor (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_ALIAS_EVENT). anchor []byte // The tag (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). tag []byte // The scalar value (for yaml_SCALAR_EVENT). value []byte // Is the document start/end indicator implicit, or the tag optional? // (for yaml_DOCUMENT_START_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_SCALAR_EVENT). implicit bool // Is the tag optional for any non-plain style? (for yaml_SCALAR_EVENT). quoted_implicit bool // The style (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). style yaml_style_t } func (e *yaml_event_t) scalar_style() yaml_scalar_style_t { return yaml_scalar_style_t(e.style) } func (e *yaml_event_t) sequence_style() yaml_sequence_style_t { return yaml_sequence_style_t(e.style) } func (e *yaml_event_t) mapping_style() yaml_mapping_style_t { return yaml_mapping_style_t(e.style) } // Nodes const ( yaml_NULL_TAG = "tag:yaml.org,2002:null" // The tag !!null with the only possible value: null. yaml_BOOL_TAG = "tag:yaml.org,2002:bool" // The tag !!bool with the values: true and false. yaml_STR_TAG = "tag:yaml.org,2002:str" // The tag !!str for string values. yaml_INT_TAG = "tag:yaml.org,2002:int" // The tag !!int for integer values. yaml_FLOAT_TAG = "tag:yaml.org,2002:float" // The tag !!float for float values. yaml_TIMESTAMP_TAG = "tag:yaml.org,2002:timestamp" // The tag !!timestamp for date and time values. yaml_SEQ_TAG = "tag:yaml.org,2002:seq" // The tag !!seq is used to denote sequences. yaml_MAP_TAG = "tag:yaml.org,2002:map" // The tag !!map is used to denote mapping. // Not in original libyaml. yaml_BINARY_TAG = "tag:yaml.org,2002:binary" yaml_MERGE_TAG = "tag:yaml.org,2002:merge" yaml_DEFAULT_SCALAR_TAG = yaml_STR_TAG // The default scalar tag is !!str. yaml_DEFAULT_SEQUENCE_TAG = yaml_SEQ_TAG // The default sequence tag is !!seq. yaml_DEFAULT_MAPPING_TAG = yaml_MAP_TAG // The default mapping tag is !!map. ) type yaml_node_type_t int // Node types. const ( // An empty node. yaml_NO_NODE yaml_node_type_t = iota yaml_SCALAR_NODE // A scalar node. yaml_SEQUENCE_NODE // A sequence node. yaml_MAPPING_NODE // A mapping node. ) // An element of a sequence node. type yaml_node_item_t int // An element of a mapping node. type yaml_node_pair_t struct { key int // The key of the element. value int // The value of the element. } // The node structure. type yaml_node_t struct { typ yaml_node_type_t // The node type. tag []byte // The node tag. // The node data. // The scalar parameters (for yaml_SCALAR_NODE). scalar struct { value []byte // The scalar value. length int // The length of the scalar value. style yaml_scalar_style_t // The scalar style. } // The sequence parameters (for YAML_SEQUENCE_NODE). sequence struct { items_data []yaml_node_item_t // The stack of sequence items. style yaml_sequence_style_t // The sequence style. } // The mapping parameters (for yaml_MAPPING_NODE). mapping struct { pairs_data []yaml_node_pair_t // The stack of mapping pairs (key, value). pairs_start *yaml_node_pair_t // The beginning of the stack. pairs_end *yaml_node_pair_t // The end of the stack. pairs_top *yaml_node_pair_t // The top of the stack. style yaml_mapping_style_t // The mapping style. } start_mark yaml_mark_t // The beginning of the node. end_mark yaml_mark_t // The end of the node. } // The document structure. type yaml_document_t struct { // The document nodes. nodes []yaml_node_t // The version directive. version_directive *yaml_version_directive_t // The list of tag directives. tag_directives_data []yaml_tag_directive_t tag_directives_start int // The beginning of the tag directives list. tag_directives_end int // The end of the tag directives list. start_implicit int // Is the document start indicator implicit? end_implicit int // Is the document end indicator implicit? // The start/end of the document. start_mark, end_mark yaml_mark_t } // The prototype of a read handler. // // The read handler is called when the parser needs to read more bytes from the // source. The handler should write not more than size bytes to the buffer. // The number of written bytes should be set to the size_read variable. // // [in,out] data A pointer to an application data specified by // yaml_parser_set_input(). // [out] buffer The buffer to write the data from the source. // [in] size The size of the buffer. // [out] size_read The actual number of bytes read from the source. // // On success, the handler should return 1. If the handler failed, // the returned value should be 0. On EOF, the handler should set the // size_read to 0 and return 1. type yaml_read_handler_t func(parser *yaml_parser_t, buffer []byte) (n int, err error) // This structure holds information about a potential simple key. type yaml_simple_key_t struct { possible bool // Is a simple key possible? required bool // Is a simple key required? token_number int // The number of the token. mark yaml_mark_t // The position mark. } // The states of the parser. type yaml_parser_state_t int const ( yaml_PARSE_STREAM_START_STATE yaml_parser_state_t = iota yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE // Expect the beginning of an implicit document. yaml_PARSE_DOCUMENT_START_STATE // Expect DOCUMENT-START. yaml_PARSE_DOCUMENT_CONTENT_STATE // Expect the content of a document. yaml_PARSE_DOCUMENT_END_STATE // Expect DOCUMENT-END. yaml_PARSE_BLOCK_NODE_STATE // Expect a block node. yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE // Expect a block node or indentless sequence. yaml_PARSE_FLOW_NODE_STATE // Expect a flow node. yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a block sequence. yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE // Expect an entry of a block sequence. yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE // Expect an entry of an indentless sequence. yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping. yaml_PARSE_BLOCK_MAPPING_KEY_STATE // Expect a block mapping key. yaml_PARSE_BLOCK_MAPPING_VALUE_STATE // Expect a block mapping value. yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a flow sequence. yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE // Expect an entry of a flow sequence. yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE // Expect a key of an ordered mapping. yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE // Expect a value of an ordered mapping. yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE // Expect the and of an ordered mapping entry. yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. yaml_PARSE_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. yaml_PARSE_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE // Expect an empty value of a flow mapping. yaml_PARSE_END_STATE // Expect nothing. ) func (ps yaml_parser_state_t) String() string { switch ps { case yaml_PARSE_STREAM_START_STATE: return "yaml_PARSE_STREAM_START_STATE" case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE: return "yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE" case yaml_PARSE_DOCUMENT_START_STATE: return "yaml_PARSE_DOCUMENT_START_STATE" case yaml_PARSE_DOCUMENT_CONTENT_STATE: return "yaml_PARSE_DOCUMENT_CONTENT_STATE" case yaml_PARSE_DOCUMENT_END_STATE: return "yaml_PARSE_DOCUMENT_END_STATE" case yaml_PARSE_BLOCK_NODE_STATE: return "yaml_PARSE_BLOCK_NODE_STATE" case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE: return "yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE" case yaml_PARSE_FLOW_NODE_STATE: return "yaml_PARSE_FLOW_NODE_STATE" case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE: return "yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE" case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE: return "yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE" case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE: return "yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE" case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE: return "yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE" case yaml_PARSE_BLOCK_MAPPING_KEY_STATE: return "yaml_PARSE_BLOCK_MAPPING_KEY_STATE" case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE: return "yaml_PARSE_BLOCK_MAPPING_VALUE_STATE" case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE: return "yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE" case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE: return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE" case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE: return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE" case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE: return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE" case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE: return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE" case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE: return "yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE" case yaml_PARSE_FLOW_MAPPING_KEY_STATE: return "yaml_PARSE_FLOW_MAPPING_KEY_STATE" case yaml_PARSE_FLOW_MAPPING_VALUE_STATE: return "yaml_PARSE_FLOW_MAPPING_VALUE_STATE" case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE: return "yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE" case yaml_PARSE_END_STATE: return "yaml_PARSE_END_STATE" } return "" } // This structure holds aliases data. type yaml_alias_data_t struct { anchor []byte // The anchor. index int // The node id. mark yaml_mark_t // The anchor mark. } // The parser structure. // // All members are internal. Manage the structure using the // yaml_parser_ family of functions. type yaml_parser_t struct { // Error handling error yaml_error_type_t // Error type. problem string // Error description. // The byte about which the problem occurred. problem_offset int problem_value int problem_mark yaml_mark_t // The error context. context string context_mark yaml_mark_t // Reader stuff read_handler yaml_read_handler_t // Read handler. input_reader io.Reader // File input data. input []byte // String input data. input_pos int eof bool // EOF flag buffer []byte // The working buffer. buffer_pos int // The current position of the buffer. unread int // The number of unread characters in the buffer. newlines int // The number of line breaks since last non-break/non-blank character raw_buffer []byte // The raw buffer. raw_buffer_pos int // The current position of the buffer. encoding yaml_encoding_t // The input encoding. offset int // The offset of the current position (in bytes). mark yaml_mark_t // The mark of the current position. // Comments head_comment []byte // The current head comments line_comment []byte // The current line comments foot_comment []byte // The current foot comments tail_comment []byte // Foot comment that happens at the end of a block. stem_comment []byte // Comment in item preceding a nested structure (list inside list item, etc) comments []yaml_comment_t // The folded comments for all parsed tokens comments_head int // Scanner stuff stream_start_produced bool // Have we started to scan the input stream? stream_end_produced bool // Have we reached the end of the input stream? flow_level int // The number of unclosed '[' and '{' indicators. tokens []yaml_token_t // The tokens queue. tokens_head int // The head of the tokens queue. tokens_parsed int // The number of tokens fetched from the queue. token_available bool // Does the tokens queue contain a token ready for dequeueing. indent int // The current indentation level. indents []int // The indentation levels stack. simple_key_allowed bool // May a simple key occur at the current position? simple_keys []yaml_simple_key_t // The stack of simple keys. simple_keys_by_tok map[int]int // possible simple_key indexes indexed by token_number // Parser stuff state yaml_parser_state_t // The current parser state. states []yaml_parser_state_t // The parser states stack. marks []yaml_mark_t // The stack of marks. tag_directives []yaml_tag_directive_t // The list of TAG directives. // Dumper stuff aliases []yaml_alias_data_t // The alias data. document *yaml_document_t // The currently parsed document. } type yaml_comment_t struct { scan_mark yaml_mark_t // Position where scanning for comments started token_mark yaml_mark_t // Position after which tokens will be associated with this comment start_mark yaml_mark_t // Position of '#' comment mark end_mark yaml_mark_t // Position where comment terminated head []byte line []byte foot []byte } // Emitter Definitions // The prototype of a write handler. // // The write handler is called when the emitter needs to flush the accumulated // characters to the output. The handler should write @a size bytes of the // @a buffer to the output. // // @param[in,out] data A pointer to an application data specified by // yaml_emitter_set_output(). // @param[in] buffer The buffer with bytes to be written. // @param[in] size The size of the buffer. // // @returns On success, the handler should return @c 1. If the handler failed, // the returned value should be @c 0. // type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error type yaml_emitter_state_t int // The emitter states. const ( // Expect STREAM-START. yaml_EMIT_STREAM_START_STATE yaml_emitter_state_t = iota yaml_EMIT_FIRST_DOCUMENT_START_STATE // Expect the first DOCUMENT-START or STREAM-END. yaml_EMIT_DOCUMENT_START_STATE // Expect DOCUMENT-START or STREAM-END. yaml_EMIT_DOCUMENT_CONTENT_STATE // Expect the content of a document. yaml_EMIT_DOCUMENT_END_STATE // Expect DOCUMENT-END. yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a flow sequence. yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE // Expect the next item of a flow sequence, with the comma already written out yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE // Expect an item of a flow sequence. yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE // Expect the next key of a flow mapping, with the comma already written out yaml_EMIT_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a flow mapping. yaml_EMIT_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a block sequence. yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE // Expect an item of a block sequence. yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping. yaml_EMIT_BLOCK_MAPPING_KEY_STATE // Expect the key of a block mapping. yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a block mapping. yaml_EMIT_BLOCK_MAPPING_VALUE_STATE // Expect a value of a block mapping. yaml_EMIT_END_STATE // Expect nothing. ) // The emitter structure. // // All members are internal. Manage the structure using the @c yaml_emitter_ // family of functions. type yaml_emitter_t struct { // Error handling error yaml_error_type_t // Error type. problem string // Error description. // Writer stuff write_handler yaml_write_handler_t // Write handler. output_buffer *[]byte // String output data. output_writer io.Writer // File output data. buffer []byte // The working buffer. buffer_pos int // The current position of the buffer. raw_buffer []byte // The raw buffer. raw_buffer_pos int // The current position of the buffer. encoding yaml_encoding_t // The stream encoding. // Emitter stuff canonical bool // If the output is in the canonical style? best_indent int // The number of indentation spaces. best_width int // The preferred width of the output lines. unicode bool // Allow unescaped non-ASCII characters? line_break yaml_break_t // The preferred line break. state yaml_emitter_state_t // The current emitter state. states []yaml_emitter_state_t // The stack of states. events []yaml_event_t // The event queue. events_head int // The head of the event queue. indents []int // The stack of indentation levels. tag_directives []yaml_tag_directive_t // The list of tag directives. indent int // The current indentation level. flow_level int // The current flow level. root_context bool // Is it the document root context? sequence_context bool // Is it a sequence context? mapping_context bool // Is it a mapping context? simple_key_context bool // Is it a simple mapping key context? line int // The current line. column int // The current column. whitespace bool // If the last character was a whitespace? indention bool // If the last character was an indentation character (' ', '-', '?', ':')? open_ended bool // If an explicit document end is required? space_above bool // Is there's an empty line above? foot_indent int // The indent used to write the foot comment above, or -1 if none. // Anchor analysis. anchor_data struct { anchor []byte // The anchor value. alias bool // Is it an alias? } // Tag analysis. tag_data struct { handle []byte // The tag handle. suffix []byte // The tag suffix. } // Scalar analysis. scalar_data struct { value []byte // The scalar value. multiline bool // Does the scalar contain line breaks? flow_plain_allowed bool // Can the scalar be expessed in the flow plain style? block_plain_allowed bool // Can the scalar be expressed in the block plain style? single_quoted_allowed bool // Can the scalar be expressed in the single quoted style? block_allowed bool // Can the scalar be expressed in the literal or folded styles? style yaml_scalar_style_t // The output style. } // Comments head_comment []byte line_comment []byte foot_comment []byte tail_comment []byte key_line_comment []byte // Dumper stuff opened bool // If the stream was already opened? closed bool // If the stream was already closed? // The information associated with the document nodes. anchors *struct { references int // The number of references. anchor int // The anchor id. serialized bool // If the node has been emitted? } last_anchor_id int // The last assigned anchor id. document *yaml_document_t // The currently emitted document. } ================================================ FILE: vendor/github.com/oasdiff/yaml3/yamlprivateh.go ================================================ // // Copyright (c) 2011-2019 Canonical Ltd // Copyright (c) 2006-2010 Kirill Simonov // // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package yaml const ( // The size of the input raw buffer. input_raw_buffer_size = 512 // The size of the input buffer. // It should be possible to decode the whole raw buffer. input_buffer_size = input_raw_buffer_size * 3 // The size of the output buffer. output_buffer_size = 128 // The size of the output raw buffer. // It should be possible to encode the whole output buffer. output_raw_buffer_size = (output_buffer_size*2 + 2) // The size of other stacks and queues. initial_stack_size = 16 initial_queue_size = 16 initial_string_size = 16 ) // Check if the character at the specified position is an alphabetical // character, a digit, '_', or '-'. func is_alpha(b []byte, i int) bool { return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'Z' || b[i] >= 'a' && b[i] <= 'z' || b[i] == '_' || b[i] == '-' } // Check if the character at the specified position is a digit. func is_digit(b []byte, i int) bool { return b[i] >= '0' && b[i] <= '9' } // Get the value of a digit. func as_digit(b []byte, i int) int { return int(b[i]) - '0' } // Check if the character at the specified position is a hex-digit. func is_hex(b []byte, i int) bool { return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'F' || b[i] >= 'a' && b[i] <= 'f' } // Get the value of a hex-digit. func as_hex(b []byte, i int) int { bi := b[i] if bi >= 'A' && bi <= 'F' { return int(bi) - 'A' + 10 } if bi >= 'a' && bi <= 'f' { return int(bi) - 'a' + 10 } return int(bi) - '0' } // Check if the character is ASCII. func is_ascii(b []byte, i int) bool { return b[i] <= 0x7F } // Check if the character at the start of the buffer can be printed unescaped. func is_printable(b []byte, i int) bool { return ((b[i] == 0x0A) || // . == #x0A (b[i] >= 0x20 && b[i] <= 0x7E) || // #x20 <= . <= #x7E (b[i] == 0xC2 && b[i+1] >= 0xA0) || // #0xA0 <= . <= #xD7FF (b[i] > 0xC2 && b[i] < 0xED) || (b[i] == 0xED && b[i+1] < 0xA0) || (b[i] == 0xEE) || (b[i] == 0xEF && // #xE000 <= . <= #xFFFD !(b[i+1] == 0xBB && b[i+2] == 0xBF) && // && . != #xFEFF !(b[i+1] == 0xBF && (b[i+2] == 0xBE || b[i+2] == 0xBF)))) } // Check if the character at the specified position is NUL. func is_z(b []byte, i int) bool { return b[i] == 0x00 } // Check if the beginning of the buffer is a BOM. func is_bom(b []byte, i int) bool { return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF } // Check if the character at the specified position is space. func is_space(b []byte, i int) bool { return b[i] == ' ' } // Check if the character at the specified position is tab. func is_tab(b []byte, i int) bool { return b[i] == '\t' } // Check if the character at the specified position is blank (space or tab). func is_blank(b []byte, i int) bool { //return is_space(b, i) || is_tab(b, i) return b[i] == ' ' || b[i] == '\t' } // Check if the character at the specified position is a line break. func is_break(b []byte, i int) bool { return (b[i] == '\r' || // CR (#xD) b[i] == '\n' || // LF (#xA) b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9) // PS (#x2029) } func is_crlf(b []byte, i int) bool { return b[i] == '\r' && b[i+1] == '\n' } // Check if the character is a line break or NUL. func is_breakz(b []byte, i int) bool { //return is_break(b, i) || is_z(b, i) return ( // is_break: b[i] == '\r' || // CR (#xD) b[i] == '\n' || // LF (#xA) b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) // is_z: b[i] == 0) } // Check if the character is a line break, space, or NUL. func is_spacez(b []byte, i int) bool { //return is_space(b, i) || is_breakz(b, i) return ( // is_space: b[i] == ' ' || // is_breakz: b[i] == '\r' || // CR (#xD) b[i] == '\n' || // LF (#xA) b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) b[i] == 0) } // Check if the character is a line break, space, tab, or NUL. func is_blankz(b []byte, i int) bool { //return is_blank(b, i) || is_breakz(b, i) return ( // is_blank: b[i] == ' ' || b[i] == '\t' || // is_breakz: b[i] == '\r' || // CR (#xD) b[i] == '\n' || // LF (#xA) b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) b[i] == 0) } // Determine the width of the character. func width(b byte) int { // Don't replace these by a switch without first // confirming that it is being inlined. if b&0x80 == 0x00 { return 1 } if b&0xE0 == 0xC0 { return 2 } if b&0xF0 == 0xE0 { return 3 } if b&0xF8 == 0xF0 { return 4 } return 0 } ================================================ FILE: vendor/github.com/opencontainers/go-digest/.mailmap ================================================ Aaron Lehmann Derek McGowan Stephen J Day Haibing Zhou ================================================ FILE: vendor/github.com/opencontainers/go-digest/.pullapprove.yml ================================================ version: 2 requirements: signed_off_by: required: true always_pending: title_regex: '^WIP' explanation: 'Work in progress...' group_defaults: required: 2 approve_by_comment: enabled: true approve_regex: '^LGTM' reject_regex: '^Rejected' reset_on_push: enabled: true author_approval: ignored: true conditions: branches: - master groups: go-digest: teams: - go-digest-maintainers ================================================ FILE: vendor/github.com/opencontainers/go-digest/.travis.yml ================================================ language: go go: - 1.12.x - 1.13.x - master ================================================ FILE: vendor/github.com/opencontainers/go-digest/CONTRIBUTING.md ================================================ # Contributing to Docker open source projects Want to hack on this project? Awesome! Here are instructions to get you started. This project is a part of the [Docker](https://www.docker.com) project, and follows the same rules and principles. If you're already familiar with the way Docker does things, you'll feel right at home. Otherwise, go read Docker's [contributions guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md), [issue triaging](https://github.com/docker/docker/blob/master/project/ISSUE-TRIAGE.md), [review process](https://github.com/docker/docker/blob/master/project/REVIEWING.md) and [branches and tags](https://github.com/docker/docker/blob/master/project/BRANCHES-AND-TAGS.md). For an in-depth description of our contribution process, visit the contributors guide: [Understand how to contribute](https://docs.docker.com/opensource/workflow/make-a-contribution/) ### Sign your work The sign-off is a simple line at the end of the explanation for the patch. Your signature certifies that you wrote the patch or otherwise have the right to pass it on as an open-source patch. The rules are pretty simple: if you can certify the below (from [developercertificate.org](http://developercertificate.org/)): ``` Developer Certificate of Origin Version 1.1 Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 1 Letterman Drive Suite D4700 San Francisco, CA, 94129 Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. ``` Then you just add a line to every git commit message: Signed-off-by: Joe Smith Use your real name (sorry, no pseudonyms or anonymous contributions.) If you set your `user.name` and `user.email` git configs, you can sign your commit automatically with `git commit -s`. ================================================ FILE: vendor/github.com/opencontainers/go-digest/LICENSE ================================================ Apache License Version 2.0, January 2004 https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS Copyright 2019, 2020 OCI Contributors Copyright 2016 Docker, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/opencontainers/go-digest/LICENSE.docs ================================================ Attribution-ShareAlike 4.0 International ======================================================================= Creative Commons Corporation ("Creative Commons") is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an "as-is" basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. Using Creative Commons Public Licenses Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC- licensed material, or material used under an exception or limitation to copyright. More considerations for licensors: wiki.creativecommons.org/Considerations_for_licensors Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor's permission is not necessary for any reason--for example, because of any applicable exception or limitation to copyright--then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More_considerations for the public: wiki.creativecommons.org/Considerations_for_licensees ======================================================================= Creative Commons Attribution-ShareAlike 4.0 International Public License By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-ShareAlike 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. Section 1 -- Definitions. a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. c. BY-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses, approved by Creative Commons as essentially the equivalent of this Public License. d. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. e. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. f. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. g. License Elements means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this Public License are Attribution and ShareAlike. h. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. i. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. j. Licensor means the individual(s) or entity(ies) granting rights under this Public License. k. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. l. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. m. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. Section 2 -- Scope. a. License grant. 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: a. reproduce and Share the Licensed Material, in whole or in part; and b. produce, reproduce, and Share Adapted Material. 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 3. Term. The term of this Public License is specified in Section 6(a). 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a) (4) never produces Adapted Material. 5. Downstream recipients. a. Offer from the Licensor -- Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. b. Additional offer from the Licensor -- Adapted Material. Every recipient of Adapted Material from You automatically receives an offer from the Licensor to exercise the Licensed Rights in the Adapted Material under the conditions of the Adapter's License You apply. c. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). b. Other rights. 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 2. Patent and trademark rights are not licensed under this Public License. 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. Section 3 -- License Conditions. Your exercise of the Licensed Rights is expressly made subject to the following conditions. a. Attribution. 1. If You Share the Licensed Material (including in modified form), You must: a. retain the following if it is supplied by the Licensor with the Licensed Material: i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); ii. a copyright notice; iii. a notice that refers to this Public License; iv. a notice that refers to the disclaimer of warranties; v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; b. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and c. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. b. ShareAlike. In addition to the conditions in Section 3(a), if You Share Adapted Material You produce, the following conditions also apply. 1. The Adapter's License You apply must be a Creative Commons license with the same License Elements, this version or later, or a BY-SA Compatible License. 2. You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition in any reasonable manner based on the medium, means, and context in which You Share Adapted Material. 3. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, Adapted Material that restrict exercise of the rights granted under the Adapter's License You apply. Section 4 -- Sui Generis Database Rights. Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material, including for purposes of Section 3(b); and c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. Section 5 -- Disclaimer of Warranties and Limitation of Liability. a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. Section 6 -- Term and Termination. a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 2. upon express reinstatement by the Licensor. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. Section 7 -- Other Terms and Conditions. a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. Section 8 -- Interpretation. a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. ======================================================================= Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the "Licensor." Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark "Creative Commons" or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. Creative Commons may be contacted at creativecommons.org. ================================================ FILE: vendor/github.com/opencontainers/go-digest/MAINTAINERS ================================================ Derek McGowan (@dmcgowan) Stephen Day (@stevvooe) Vincent Batts (@vbatts) Akihiro Suda (@AkihiroSuda) Sebastiaan van Stijn (@thaJeztah) ================================================ FILE: vendor/github.com/opencontainers/go-digest/README.md ================================================ # go-digest [![GoDoc](https://godoc.org/github.com/opencontainers/go-digest?status.svg)](https://godoc.org/github.com/opencontainers/go-digest) [![Go Report Card](https://goreportcard.com/badge/github.com/opencontainers/go-digest)](https://goreportcard.com/report/github.com/opencontainers/go-digest) [![Build Status](https://travis-ci.org/opencontainers/go-digest.svg?branch=master)](https://travis-ci.org/opencontainers/go-digest) Common digest package used across the container ecosystem. Please see the [godoc](https://godoc.org/github.com/opencontainers/go-digest) for more information. # What is a digest? A digest is just a [hash](https://en.wikipedia.org/wiki/Hash_function). The most common use case for a digest is to create a content identifier for use in [Content Addressable Storage](https://en.wikipedia.org/wiki/Content-addressable_storage) systems: ```go id := digest.FromBytes([]byte("my content")) ``` In the example above, the id can be used to uniquely identify the byte slice "my content". This allows two disparate applications to agree on a verifiable identifier without having to trust one another. An identifying digest can be verified, as follows: ```go if id != digest.FromBytes([]byte("my content")) { return errors.New("the content has changed!") } ``` A `Verifier` type can be used to handle cases where an `io.Reader` makes more sense: ```go rd := getContent() verifier := id.Verifier() io.Copy(verifier, rd) if !verifier.Verified() { return errors.New("the content has changed!") } ``` Using [Merkle DAGs](https://en.wikipedia.org/wiki/Merkle_tree), this can power a rich, safe, content distribution system. # Usage While the [godoc](https://godoc.org/github.com/opencontainers/go-digest) is considered the best resource, a few important items need to be called out when using this package. 1. Make sure to import the hash implementations into your application or the package will panic. You should have something like the following in the main (or other entrypoint) of your application: ```go import ( _ "crypto/sha256" _ "crypto/sha512" ) ``` This may seem inconvenient but it allows you replace the hash implementations with others, such as https://github.com/stevvooe/resumable. 2. Even though `digest.Digest` may be assemblable as a string, _always_ verify your input with `digest.Parse` or use `Digest.Validate` when accepting untrusted input. While there are measures to avoid common problems, this will ensure you have valid digests in the rest of your application. 3. While alternative encodings of hash values (digests) are possible (for example, base64), this package deals exclusively with hex-encoded digests. # Stability The Go API, at this stage, is considered stable, unless otherwise noted. As always, before using a package export, read the [godoc](https://godoc.org/github.com/opencontainers/go-digest). # Contributing This package is considered fairly complete. It has been in production in thousands (millions?) of deployments and is fairly battle-hardened. New additions will be met with skepticism. If you think there is a missing feature, please file a bug clearly describing the problem and the alternatives you tried before submitting a PR. ## Code of Conduct Participation in the OpenContainers community is governed by [OpenContainer's Code of Conduct][code-of-conduct]. ## Security If you find an issue, please follow the [security][security] protocol to report it. # Copyright and license Copyright © 2019, 2020 OCI Contributors Copyright © 2016 Docker, Inc. All rights reserved, except as follows. Code is released under the [Apache 2.0 license](LICENSE). This `README.md` file and the [`CONTRIBUTING.md`](CONTRIBUTING.md) file are licensed under the Creative Commons Attribution 4.0 International License under the terms and conditions set forth in the file [`LICENSE.docs`](LICENSE.docs). You may obtain a duplicate copy of the same license, titled CC BY-SA 4.0, at http://creativecommons.org/licenses/by-sa/4.0/. [security]: https://github.com/opencontainers/org/blob/master/security [code-of-conduct]: https://github.com/opencontainers/org/blob/master/CODE_OF_CONDUCT.md ================================================ FILE: vendor/github.com/opencontainers/go-digest/algorithm.go ================================================ // Copyright 2019, 2020 OCI Contributors // Copyright 2017 Docker, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package digest import ( "crypto" "fmt" "hash" "io" "regexp" ) // Algorithm identifies and implementation of a digester by an identifier. // Note the that this defines both the hash algorithm used and the string // encoding. type Algorithm string // supported digest types const ( SHA256 Algorithm = "sha256" // sha256 with hex encoding (lower case only) SHA384 Algorithm = "sha384" // sha384 with hex encoding (lower case only) SHA512 Algorithm = "sha512" // sha512 with hex encoding (lower case only) // Canonical is the primary digest algorithm used with the distribution // project. Other digests may be used but this one is the primary storage // digest. Canonical = SHA256 ) var ( // TODO(stevvooe): Follow the pattern of the standard crypto package for // registration of digests. Effectively, we are a registerable set and // common symbol access. // algorithms maps values to hash.Hash implementations. Other algorithms // may be available but they cannot be calculated by the digest package. algorithms = map[Algorithm]crypto.Hash{ SHA256: crypto.SHA256, SHA384: crypto.SHA384, SHA512: crypto.SHA512, } // anchoredEncodedRegexps contains anchored regular expressions for hex-encoded digests. // Note that /A-F/ disallowed. anchoredEncodedRegexps = map[Algorithm]*regexp.Regexp{ SHA256: regexp.MustCompile(`^[a-f0-9]{64}$`), SHA384: regexp.MustCompile(`^[a-f0-9]{96}$`), SHA512: regexp.MustCompile(`^[a-f0-9]{128}$`), } ) // Available returns true if the digest type is available for use. If this // returns false, Digester and Hash will return nil. func (a Algorithm) Available() bool { h, ok := algorithms[a] if !ok { return false } // check availability of the hash, as well return h.Available() } func (a Algorithm) String() string { return string(a) } // Size returns number of bytes returned by the hash. func (a Algorithm) Size() int { h, ok := algorithms[a] if !ok { return 0 } return h.Size() } // Set implemented to allow use of Algorithm as a command line flag. func (a *Algorithm) Set(value string) error { if value == "" { *a = Canonical } else { // just do a type conversion, support is queried with Available. *a = Algorithm(value) } if !a.Available() { return ErrDigestUnsupported } return nil } // Digester returns a new digester for the specified algorithm. If the algorithm // does not have a digester implementation, nil will be returned. This can be // checked by calling Available before calling Digester. func (a Algorithm) Digester() Digester { return &digester{ alg: a, hash: a.Hash(), } } // Hash returns a new hash as used by the algorithm. If not available, the // method will panic. Check Algorithm.Available() before calling. func (a Algorithm) Hash() hash.Hash { if !a.Available() { // Empty algorithm string is invalid if a == "" { panic(fmt.Sprintf("empty digest algorithm, validate before calling Algorithm.Hash()")) } // NOTE(stevvooe): A missing hash is usually a programming error that // must be resolved at compile time. We don't import in the digest // package to allow users to choose their hash implementation (such as // when using stevvooe/resumable or a hardware accelerated package). // // Applications that may want to resolve the hash at runtime should // call Algorithm.Available before call Algorithm.Hash(). panic(fmt.Sprintf("%v not available (make sure it is imported)", a)) } return algorithms[a].New() } // Encode encodes the raw bytes of a digest, typically from a hash.Hash, into // the encoded portion of the digest. func (a Algorithm) Encode(d []byte) string { // TODO(stevvooe): Currently, all algorithms use a hex encoding. When we // add support for back registration, we can modify this accordingly. return fmt.Sprintf("%x", d) } // FromReader returns the digest of the reader using the algorithm. func (a Algorithm) FromReader(rd io.Reader) (Digest, error) { digester := a.Digester() if _, err := io.Copy(digester.Hash(), rd); err != nil { return "", err } return digester.Digest(), nil } // FromBytes digests the input and returns a Digest. func (a Algorithm) FromBytes(p []byte) Digest { digester := a.Digester() if _, err := digester.Hash().Write(p); err != nil { // Writes to a Hash should never fail. None of the existing // hash implementations in the stdlib or hashes vendored // here can return errors from Write. Having a panic in this // condition instead of having FromBytes return an error value // avoids unnecessary error handling paths in all callers. panic("write to hash function returned error: " + err.Error()) } return digester.Digest() } // FromString digests the string input and returns a Digest. func (a Algorithm) FromString(s string) Digest { return a.FromBytes([]byte(s)) } // Validate validates the encoded portion string func (a Algorithm) Validate(encoded string) error { r, ok := anchoredEncodedRegexps[a] if !ok { return ErrDigestUnsupported } // Digests much always be hex-encoded, ensuring that their hex portion will // always be size*2 if a.Size()*2 != len(encoded) { return ErrDigestInvalidLength } if r.MatchString(encoded) { return nil } return ErrDigestInvalidFormat } ================================================ FILE: vendor/github.com/opencontainers/go-digest/digest.go ================================================ // Copyright 2019, 2020 OCI Contributors // Copyright 2017 Docker, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package digest import ( "fmt" "hash" "io" "regexp" "strings" ) // Digest allows simple protection of hex formatted digest strings, prefixed // by their algorithm. Strings of type Digest have some guarantee of being in // the correct format and it provides quick access to the components of a // digest string. // // The following is an example of the contents of Digest types: // // sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc // // This allows to abstract the digest behind this type and work only in those // terms. type Digest string // NewDigest returns a Digest from alg and a hash.Hash object. func NewDigest(alg Algorithm, h hash.Hash) Digest { return NewDigestFromBytes(alg, h.Sum(nil)) } // NewDigestFromBytes returns a new digest from the byte contents of p. // Typically, this can come from hash.Hash.Sum(...) or xxx.SumXXX(...) // functions. This is also useful for rebuilding digests from binary // serializations. func NewDigestFromBytes(alg Algorithm, p []byte) Digest { return NewDigestFromEncoded(alg, alg.Encode(p)) } // NewDigestFromHex is deprecated. Please use NewDigestFromEncoded. func NewDigestFromHex(alg, hex string) Digest { return NewDigestFromEncoded(Algorithm(alg), hex) } // NewDigestFromEncoded returns a Digest from alg and the encoded digest. func NewDigestFromEncoded(alg Algorithm, encoded string) Digest { return Digest(fmt.Sprintf("%s:%s", alg, encoded)) } // DigestRegexp matches valid digest types. var DigestRegexp = regexp.MustCompile(`[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+`) // DigestRegexpAnchored matches valid digest types, anchored to the start and end of the match. var DigestRegexpAnchored = regexp.MustCompile(`^` + DigestRegexp.String() + `$`) var ( // ErrDigestInvalidFormat returned when digest format invalid. ErrDigestInvalidFormat = fmt.Errorf("invalid checksum digest format") // ErrDigestInvalidLength returned when digest has invalid length. ErrDigestInvalidLength = fmt.Errorf("invalid checksum digest length") // ErrDigestUnsupported returned when the digest algorithm is unsupported. ErrDigestUnsupported = fmt.Errorf("unsupported digest algorithm") ) // Parse parses s and returns the validated digest object. An error will // be returned if the format is invalid. func Parse(s string) (Digest, error) { d := Digest(s) return d, d.Validate() } // FromReader consumes the content of rd until io.EOF, returning canonical digest. func FromReader(rd io.Reader) (Digest, error) { return Canonical.FromReader(rd) } // FromBytes digests the input and returns a Digest. func FromBytes(p []byte) Digest { return Canonical.FromBytes(p) } // FromString digests the input and returns a Digest. func FromString(s string) Digest { return Canonical.FromString(s) } // Validate checks that the contents of d is a valid digest, returning an // error if not. func (d Digest) Validate() error { s := string(d) i := strings.Index(s, ":") if i <= 0 || i+1 == len(s) { return ErrDigestInvalidFormat } algorithm, encoded := Algorithm(s[:i]), s[i+1:] if !algorithm.Available() { if !DigestRegexpAnchored.MatchString(s) { return ErrDigestInvalidFormat } return ErrDigestUnsupported } return algorithm.Validate(encoded) } // Algorithm returns the algorithm portion of the digest. This will panic if // the underlying digest is not in a valid format. func (d Digest) Algorithm() Algorithm { return Algorithm(d[:d.sepIndex()]) } // Verifier returns a writer object that can be used to verify a stream of // content against the digest. If the digest is invalid, the method will panic. func (d Digest) Verifier() Verifier { return hashVerifier{ hash: d.Algorithm().Hash(), digest: d, } } // Encoded returns the encoded portion of the digest. This will panic if the // underlying digest is not in a valid format. func (d Digest) Encoded() string { return string(d[d.sepIndex()+1:]) } // Hex is deprecated. Please use Digest.Encoded. func (d Digest) Hex() string { return d.Encoded() } func (d Digest) String() string { return string(d) } func (d Digest) sepIndex() int { i := strings.Index(string(d), ":") if i < 0 { panic(fmt.Sprintf("no ':' separator in digest %q", d)) } return i } ================================================ FILE: vendor/github.com/opencontainers/go-digest/digester.go ================================================ // Copyright 2019, 2020 OCI Contributors // Copyright 2017 Docker, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package digest import "hash" // Digester calculates the digest of written data. Writes should go directly // to the return value of Hash, while calling Digest will return the current // value of the digest. type Digester interface { Hash() hash.Hash // provides direct access to underlying hash instance. Digest() Digest } // digester provides a simple digester definition that embeds a hasher. type digester struct { alg Algorithm hash hash.Hash } func (d *digester) Hash() hash.Hash { return d.hash } func (d *digester) Digest() Digest { return NewDigest(d.alg, d.hash) } ================================================ FILE: vendor/github.com/opencontainers/go-digest/doc.go ================================================ // Copyright 2019, 2020 OCI Contributors // Copyright 2017 Docker, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package digest provides a generalized type to opaquely represent message // digests and their operations within the registry. The Digest type is // designed to serve as a flexible identifier in a content-addressable system. // More importantly, it provides tools and wrappers to work with // hash.Hash-based digests with little effort. // // Basics // // The format of a digest is simply a string with two parts, dubbed the // "algorithm" and the "digest", separated by a colon: // // : // // An example of a sha256 digest representation follows: // // sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc // // The "algorithm" portion defines both the hashing algorithm used to calculate // the digest and the encoding of the resulting digest, which defaults to "hex" // if not otherwise specified. Currently, all supported algorithms have their // digests encoded in hex strings. // // In the example above, the string "sha256" is the algorithm and the hex bytes // are the "digest". // // Because the Digest type is simply a string, once a valid Digest is // obtained, comparisons are cheap, quick and simple to express with the // standard equality operator. // // Verification // // The main benefit of using the Digest type is simple verification against a // given digest. The Verifier interface, modeled after the stdlib hash.Hash // interface, provides a common write sink for digest verification. After // writing is complete, calling the Verifier.Verified method will indicate // whether or not the stream of bytes matches the target digest. // // Missing Features // // In addition to the above, we intend to add the following features to this // package: // // 1. A Digester type that supports write sink digest calculation. // // 2. Suspend and resume of ongoing digest calculations to support efficient digest verification in the registry. // package digest ================================================ FILE: vendor/github.com/opencontainers/go-digest/verifiers.go ================================================ // Copyright 2019, 2020 OCI Contributors // Copyright 2017 Docker, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package digest import ( "hash" "io" ) // Verifier presents a general verification interface to be used with message // digests and other byte stream verifications. Users instantiate a Verifier // from one of the various methods, write the data under test to it then check // the result with the Verified method. type Verifier interface { io.Writer // Verified will return true if the content written to Verifier matches // the digest. Verified() bool } type hashVerifier struct { digest Digest hash hash.Hash } func (hv hashVerifier) Write(p []byte) (n int, err error) { return hv.hash.Write(p) } func (hv hashVerifier) Verified() bool { return hv.digest == NewDigest(hv.digest.Algorithm(), hv.hash) } ================================================ FILE: vendor/github.com/opencontainers/image-spec/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS Copyright 2016 The Linux Foundation. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/opencontainers/image-spec/specs-go/v1/annotations.go ================================================ // Copyright 2016 The Linux Foundation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package v1 const ( // AnnotationCreated is the annotation key for the date and time on which the image was built (date-time string as defined by RFC 3339). AnnotationCreated = "org.opencontainers.image.created" // AnnotationAuthors is the annotation key for the contact details of the people or organization responsible for the image (freeform string). AnnotationAuthors = "org.opencontainers.image.authors" // AnnotationURL is the annotation key for the URL to find more information on the image. AnnotationURL = "org.opencontainers.image.url" // AnnotationDocumentation is the annotation key for the URL to get documentation on the image. AnnotationDocumentation = "org.opencontainers.image.documentation" // AnnotationSource is the annotation key for the URL to get source code for building the image. AnnotationSource = "org.opencontainers.image.source" // AnnotationVersion is the annotation key for the version of the packaged software. // The version MAY match a label or tag in the source code repository. // The version MAY be Semantic versioning-compatible. AnnotationVersion = "org.opencontainers.image.version" // AnnotationRevision is the annotation key for the source control revision identifier for the packaged software. AnnotationRevision = "org.opencontainers.image.revision" // AnnotationVendor is the annotation key for the name of the distributing entity, organization or individual. AnnotationVendor = "org.opencontainers.image.vendor" // AnnotationLicenses is the annotation key for the license(s) under which contained software is distributed as an SPDX License Expression. AnnotationLicenses = "org.opencontainers.image.licenses" // AnnotationRefName is the annotation key for the name of the reference for a target. // SHOULD only be considered valid when on descriptors on `index.json` within image layout. AnnotationRefName = "org.opencontainers.image.ref.name" // AnnotationTitle is the annotation key for the human-readable title of the image. AnnotationTitle = "org.opencontainers.image.title" // AnnotationDescription is the annotation key for the human-readable description of the software packaged in the image. AnnotationDescription = "org.opencontainers.image.description" // AnnotationBaseImageDigest is the annotation key for the digest of the image's base image. AnnotationBaseImageDigest = "org.opencontainers.image.base.digest" // AnnotationBaseImageName is the annotation key for the image reference of the image's base image. AnnotationBaseImageName = "org.opencontainers.image.base.name" ) ================================================ FILE: vendor/github.com/opencontainers/image-spec/specs-go/v1/config.go ================================================ // Copyright 2016 The Linux Foundation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package v1 import ( "time" digest "github.com/opencontainers/go-digest" ) // ImageConfig defines the execution parameters which should be used as a base when running a container using an image. type ImageConfig struct { // User defines the username or UID which the process in the container should run as. User string `json:"User,omitempty"` // ExposedPorts a set of ports to expose from a container running this image. ExposedPorts map[string]struct{} `json:"ExposedPorts,omitempty"` // Env is a list of environment variables to be used in a container. Env []string `json:"Env,omitempty"` // Entrypoint defines a list of arguments to use as the command to execute when the container starts. Entrypoint []string `json:"Entrypoint,omitempty"` // Cmd defines the default arguments to the entrypoint of the container. Cmd []string `json:"Cmd,omitempty"` // Volumes is a set of directories describing where the process is likely write data specific to a container instance. Volumes map[string]struct{} `json:"Volumes,omitempty"` // WorkingDir sets the current working directory of the entrypoint process in the container. WorkingDir string `json:"WorkingDir,omitempty"` // Labels contains arbitrary metadata for the container. Labels map[string]string `json:"Labels,omitempty"` // StopSignal contains the system call signal that will be sent to the container to exit. StopSignal string `json:"StopSignal,omitempty"` // ArgsEscaped // // Deprecated: This field is present only for legacy compatibility with // Docker and should not be used by new image builders. It is used by Docker // for Windows images to indicate that the `Entrypoint` or `Cmd` or both, // contains only a single element array, that is a pre-escaped, and combined // into a single string `CommandLine`. If `true` the value in `Entrypoint` or // `Cmd` should be used as-is to avoid double escaping. // https://github.com/opencontainers/image-spec/pull/892 ArgsEscaped bool `json:"ArgsEscaped,omitempty"` } // RootFS describes a layer content addresses type RootFS struct { // Type is the type of the rootfs. Type string `json:"type"` // DiffIDs is an array of layer content hashes (DiffIDs), in order from bottom-most to top-most. DiffIDs []digest.Digest `json:"diff_ids"` } // History describes the history of a layer. type History struct { // Created is the combined date and time at which the layer was created, formatted as defined by RFC 3339, section 5.6. Created *time.Time `json:"created,omitempty"` // CreatedBy is the command which created the layer. CreatedBy string `json:"created_by,omitempty"` // Author is the author of the build point. Author string `json:"author,omitempty"` // Comment is a custom message set when creating the layer. Comment string `json:"comment,omitempty"` // EmptyLayer is used to mark if the history item created a filesystem diff. EmptyLayer bool `json:"empty_layer,omitempty"` } // Image is the JSON structure which describes some basic information about the image. // This provides the `application/vnd.oci.image.config.v1+json` mediatype when marshalled to JSON. type Image struct { // Created is the combined date and time at which the image was created, formatted as defined by RFC 3339, section 5.6. Created *time.Time `json:"created,omitempty"` // Author defines the name and/or email address of the person or entity which created and is responsible for maintaining the image. Author string `json:"author,omitempty"` // Platform describes the platform which the image in the manifest runs on. Platform // Config defines the execution parameters which should be used as a base when running a container using the image. Config ImageConfig `json:"config,omitempty"` // RootFS references the layer content addresses used by the image. RootFS RootFS `json:"rootfs"` // History describes the history of each layer. History []History `json:"history,omitempty"` } ================================================ FILE: vendor/github.com/opencontainers/image-spec/specs-go/v1/descriptor.go ================================================ // Copyright 2016-2022 The Linux Foundation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package v1 import digest "github.com/opencontainers/go-digest" // Descriptor describes the disposition of targeted content. // This structure provides `application/vnd.oci.descriptor.v1+json` mediatype // when marshalled to JSON. type Descriptor struct { // MediaType is the media type of the object this schema refers to. MediaType string `json:"mediaType"` // Digest is the digest of the targeted content. Digest digest.Digest `json:"digest"` // Size specifies the size in bytes of the blob. Size int64 `json:"size"` // URLs specifies a list of URLs from which this object MAY be downloaded URLs []string `json:"urls,omitempty"` // Annotations contains arbitrary metadata relating to the targeted content. Annotations map[string]string `json:"annotations,omitempty"` // Data is an embedding of the targeted content. This is encoded as a base64 // string when marshalled to JSON (automatically, by encoding/json). If // present, Data can be used directly to avoid fetching the targeted content. Data []byte `json:"data,omitempty"` // Platform describes the platform which the image in the manifest runs on. // // This should only be used when referring to a manifest. Platform *Platform `json:"platform,omitempty"` // ArtifactType is the IANA media type of this artifact. ArtifactType string `json:"artifactType,omitempty"` } // Platform describes the platform which the image in the manifest runs on. type Platform struct { // Architecture field specifies the CPU architecture, for example // `amd64` or `ppc64le`. Architecture string `json:"architecture"` // OS specifies the operating system, for example `linux` or `windows`. OS string `json:"os"` // OSVersion is an optional field specifying the operating system // version, for example on Windows `10.0.14393.1066`. OSVersion string `json:"os.version,omitempty"` // OSFeatures is an optional field specifying an array of strings, // each listing a required OS feature (for example on Windows `win32k`). OSFeatures []string `json:"os.features,omitempty"` // Variant is an optional field specifying a variant of the CPU, for // example `v7` to specify ARMv7 when architecture is `arm`. Variant string `json:"variant,omitempty"` } // DescriptorEmptyJSON is the descriptor of a blob with content of `{}`. var DescriptorEmptyJSON = Descriptor{ MediaType: MediaTypeEmptyJSON, Digest: `sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a`, Size: 2, Data: []byte(`{}`), } ================================================ FILE: vendor/github.com/opencontainers/image-spec/specs-go/v1/index.go ================================================ // Copyright 2016 The Linux Foundation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package v1 import "github.com/opencontainers/image-spec/specs-go" // Index references manifests for various platforms. // This structure provides `application/vnd.oci.image.index.v1+json` mediatype when marshalled to JSON. type Index struct { specs.Versioned // MediaType specifies the type of this document data structure e.g. `application/vnd.oci.image.index.v1+json` MediaType string `json:"mediaType,omitempty"` // ArtifactType specifies the IANA media type of artifact when the manifest is used for an artifact. ArtifactType string `json:"artifactType,omitempty"` // Manifests references platform specific manifests. Manifests []Descriptor `json:"manifests"` // Subject is an optional link from the image manifest to another manifest forming an association between the image manifest and the other manifest. Subject *Descriptor `json:"subject,omitempty"` // Annotations contains arbitrary metadata for the image index. Annotations map[string]string `json:"annotations,omitempty"` } ================================================ FILE: vendor/github.com/opencontainers/image-spec/specs-go/v1/layout.go ================================================ // Copyright 2016 The Linux Foundation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package v1 const ( // ImageLayoutFile is the file name containing ImageLayout in an OCI Image Layout ImageLayoutFile = "oci-layout" // ImageLayoutVersion is the version of ImageLayout ImageLayoutVersion = "1.0.0" // ImageIndexFile is the file name of the entry point for references and descriptors in an OCI Image Layout ImageIndexFile = "index.json" // ImageBlobsDir is the directory name containing content addressable blobs in an OCI Image Layout ImageBlobsDir = "blobs" ) // ImageLayout is the structure in the "oci-layout" file, found in the root // of an OCI Image-layout directory. type ImageLayout struct { Version string `json:"imageLayoutVersion"` } ================================================ FILE: vendor/github.com/opencontainers/image-spec/specs-go/v1/manifest.go ================================================ // Copyright 2016-2022 The Linux Foundation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package v1 import "github.com/opencontainers/image-spec/specs-go" // Manifest provides `application/vnd.oci.image.manifest.v1+json` mediatype structure when marshalled to JSON. type Manifest struct { specs.Versioned // MediaType specifies the type of this document data structure e.g. `application/vnd.oci.image.manifest.v1+json` MediaType string `json:"mediaType,omitempty"` // ArtifactType specifies the IANA media type of artifact when the manifest is used for an artifact. ArtifactType string `json:"artifactType,omitempty"` // Config references a configuration object for a container, by digest. // The referenced configuration object is a JSON blob that the runtime uses to set up the container. Config Descriptor `json:"config"` // Layers is an indexed list of layers referenced by the manifest. Layers []Descriptor `json:"layers"` // Subject is an optional link from the image manifest to another manifest forming an association between the image manifest and the other manifest. Subject *Descriptor `json:"subject,omitempty"` // Annotations contains arbitrary metadata for the image manifest. Annotations map[string]string `json:"annotations,omitempty"` } ================================================ FILE: vendor/github.com/opencontainers/image-spec/specs-go/v1/mediatype.go ================================================ // Copyright 2016 The Linux Foundation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package v1 const ( // MediaTypeDescriptor specifies the media type for a content descriptor. MediaTypeDescriptor = "application/vnd.oci.descriptor.v1+json" // MediaTypeLayoutHeader specifies the media type for the oci-layout. MediaTypeLayoutHeader = "application/vnd.oci.layout.header.v1+json" // MediaTypeImageManifest specifies the media type for an image manifest. MediaTypeImageManifest = "application/vnd.oci.image.manifest.v1+json" // MediaTypeImageIndex specifies the media type for an image index. MediaTypeImageIndex = "application/vnd.oci.image.index.v1+json" // MediaTypeImageLayer is the media type used for layers referenced by the manifest. MediaTypeImageLayer = "application/vnd.oci.image.layer.v1.tar" // MediaTypeImageLayerGzip is the media type used for gzipped layers // referenced by the manifest. MediaTypeImageLayerGzip = "application/vnd.oci.image.layer.v1.tar+gzip" // MediaTypeImageLayerZstd is the media type used for zstd compressed // layers referenced by the manifest. MediaTypeImageLayerZstd = "application/vnd.oci.image.layer.v1.tar+zstd" // MediaTypeImageLayerNonDistributable is the media type for layers referenced by // the manifest but with distribution restrictions. // // Deprecated: Non-distributable layers are deprecated, and not recommended // for future use. Implementations SHOULD NOT produce new non-distributable // layers. // https://github.com/opencontainers/image-spec/pull/965 MediaTypeImageLayerNonDistributable = "application/vnd.oci.image.layer.nondistributable.v1.tar" // MediaTypeImageLayerNonDistributableGzip is the media type for // gzipped layers referenced by the manifest but with distribution // restrictions. // // Deprecated: Non-distributable layers are deprecated, and not recommended // for future use. Implementations SHOULD NOT produce new non-distributable // layers. // https://github.com/opencontainers/image-spec/pull/965 MediaTypeImageLayerNonDistributableGzip = "application/vnd.oci.image.layer.nondistributable.v1.tar+gzip" // MediaTypeImageLayerNonDistributableZstd is the media type for zstd // compressed layers referenced by the manifest but with distribution // restrictions. // // Deprecated: Non-distributable layers are deprecated, and not recommended // for future use. Implementations SHOULD NOT produce new non-distributable // layers. // https://github.com/opencontainers/image-spec/pull/965 MediaTypeImageLayerNonDistributableZstd = "application/vnd.oci.image.layer.nondistributable.v1.tar+zstd" // MediaTypeImageConfig specifies the media type for the image configuration. MediaTypeImageConfig = "application/vnd.oci.image.config.v1+json" // MediaTypeEmptyJSON specifies the media type for an unused blob containing the value `{}` MediaTypeEmptyJSON = "application/vnd.oci.empty.v1+json" ) ================================================ FILE: vendor/github.com/opencontainers/image-spec/specs-go/version.go ================================================ // Copyright 2016 The Linux Foundation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package specs import "fmt" const ( // VersionMajor is for an API incompatible changes VersionMajor = 1 // VersionMinor is for functionality in a backwards-compatible manner VersionMinor = 1 // VersionPatch is for backwards-compatible bug fixes VersionPatch = 0 // VersionDev indicates development branch. Releases will be empty string. VersionDev = "-rc.5" ) // Version is the specification version that the package types support. var Version = fmt.Sprintf("%d.%d.%d%s", VersionMajor, VersionMinor, VersionPatch, VersionDev) ================================================ FILE: vendor/github.com/opencontainers/image-spec/specs-go/versioned.go ================================================ // Copyright 2016 The Linux Foundation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package specs // Versioned provides a struct with the manifest schemaVersion and mediaType. // Incoming content with unknown schema version can be decoded against this // struct to check the version. type Versioned struct { // SchemaVersion is the image manifest schema that this image follows SchemaVersion int `json:"schemaVersion"` } ================================================ FILE: vendor/github.com/perimeterx/marshmallow/.gitignore ================================================ /.idea coverage.out profile.out ================================================ FILE: vendor/github.com/perimeterx/marshmallow/CHANGELOG.md ================================================ # Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [[1.1.5](https://github.com/PerimeterX/marshmallow/compare/v1.1.4...v1.1.5)] - 2023-07-03 ### Added - Support for reporting errors from `HandleJSONData` - [info](https://github.com/PerimeterX/marshmallow/issues/27). ## [[1.1.4](https://github.com/PerimeterX/marshmallow/compare/v1.1.3...v1.1.4)] - 2022-11-10 ### Fixed - Fixed problem with nested object implementing JSONDataHandler with skipPopulateStruct - [info](https://github.com/PerimeterX/marshmallow/issues/18). - Fixed problem with nested object implementing JSONDataHandler with skipPopulateStruct in ModeFailOverToOriginalValue - [info](https://github.com/PerimeterX/marshmallow/issues/19). ## [[1.1.3](https://github.com/PerimeterX/marshmallow/compare/v1.1.2...v1.1.3)] - 2022-08-31 ### Added - Support for excluding known fields from the result map - [info](https://github.com/PerimeterX/marshmallow/issues/16). ## [[1.1.2](https://github.com/PerimeterX/marshmallow/compare/v1.1.1...v1.1.2)] - 2022-08-23 ### Added - Support capturing nested unknown fields - [info](https://github.com/PerimeterX/marshmallow/issues/15). ## [[1.1.1](https://github.com/PerimeterX/marshmallow/compare/v1.1.0...v1.1.1)] - 2022-08-21 ### Fixed - Fix parsing bug for unknown nested fields - [info](https://github.com/PerimeterX/marshmallow/issues/12). ## [[1.1.0](https://github.com/PerimeterX/marshmallow/compare/v0.0.1...v1.1.0)] - 2022-07-10 ### Fixed - Fixed an issue with embedded fields - [info](https://github.com/PerimeterX/marshmallow/issues/9). ## [[0.0.1](https://github.com/PerimeterX/marshmallow/tree/v0.0.1)] - 2022-04-21 ### Added - All functionality from our internal repository, after it has been stabilized on production for several months - [info](https://www.perimeterx.com/tech-blog/2022/boosting-up-json-performance-of-unstructured-structs-in-go/). ================================================ FILE: vendor/github.com/perimeterx/marshmallow/CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience * Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or email address, without their explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [opensource-conduct@humansecurity.com](mailto:opensource-conduct@humansecurity.com). All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations]. [homepage]: https://www.contributor-covenant.org [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html [Mozilla CoC]: https://github.com/mozilla/diversity [FAQ]: https://www.contributor-covenant.org/faq [translations]: https://www.contributor-covenant.org/translations ================================================ FILE: vendor/github.com/perimeterx/marshmallow/CONTRIBUTING.md ================================================ # How To Contribute We'd love to accept your patches and contributions to this project. There are just a few guidelines you need to follow which are described in detail below. ## 1. Fork this repo You should create a fork of this project in your account and work from there. You can create a fork by clicking the fork button in GitHub. ## 2. One feature, one branch Work for each new feature/issue should occur in its own branch. To create a new branch from the command line: ```shell git checkout -b my-new-feature ``` where "my-new-feature" describes what you're working on. ## 3. Add unit tests If your contribution modifies existing or adds new code please add corresponding unit tests for this. ## 4. Ensure that the build passes Run ```shell go test -v ``` and check that there are no errors. ## 5. Add documentation for new or updated functionality Please review the [README.md](README.md) file in this project to see if they are impacted by your change and update them accordingly. ## 6. Add to CHANGELOG.md Any notable changes should be recorded in the CHANGELOG.md following the [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) conventions. ## 7. Submit a pull request and describe the change Push your changes to your branch and open a pull request against the parent repo on GitHub. The project administrators will review your pull request and respond with feedback. # How your contribution gets merged Upon pull request submission, your code will be reviewed by the maintainers. They will confirm at least the following: - Tests run successfully (unit, coverage, style). - Contribution policy has been followed. A (human) reviewer will need to sign off on your pull request before it can be merged. ================================================ FILE: vendor/github.com/perimeterx/marshmallow/LICENSE ================================================ MIT License Copyright (c) 2022 PerimeterX Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/perimeterx/marshmallow/README.md ================================================ # Marshmallow ![Marshmallow Campfire](https://raw.githubusercontent.com/PerimeterX/marshmallow/assets/campfire.png) [![CodeQL Status](https://img.shields.io/github/actions/workflow/status/perimeterx/marshmallow/codeql.yml?branch=main&logo=github&label=CodeQL)](https://github.com/PerimeterX/marshmallow/actions/workflows/codeql.yml?query=branch%3Amain++) [![Run Tests](https://img.shields.io/github/actions/workflow/status/perimeterx/marshmallow/go.yml?branch=main&logo=github&label=Run%20Tests)](https://github.com/PerimeterX/marshmallow/actions/workflows/go.yml?query=branch%3Amain) [![Dependency Review](https://img.shields.io/github/actions/workflow/status/perimeterx/marshmallow/dependency-review.yml?logo=github&label=Dependency%20Review)](https://github.com/PerimeterX/marshmallow/actions/workflows/dependency-review.yml?query=branch%3Amain) [![Go Report Card](https://goreportcard.com/badge/github.com/perimeterx/marshmallow)](https://goreportcard.com/report/github.com/perimeterx/marshmallow) ![Manual Code Coverage](https://img.shields.io/badge/coverage-92.6%25-green) [![Go Reference](https://pkg.go.dev/badge/github.com/perimeterx/marshmallow.svg)](https://pkg.go.dev/github.com/perimeterx/marshmallow) [![Licence](https://img.shields.io/github/license/perimeterx/marshmallow)](LICENSE) [![Latest Release](https://img.shields.io/github/v/release/perimeterx/marshmallow)](https://github.com/PerimeterX/marshmallow/releases) ![Top Languages](https://img.shields.io/github/languages/top/perimeterx/marshmallow) [![Issues](https://img.shields.io/github/issues-closed/perimeterx/marshmallow?color=%238250df&logo=github)](https://github.com/PerimeterX/marshmallow/issues) [![Pull Requests](https://img.shields.io/github/issues-pr-closed-raw/perimeterx/marshmallow?color=%238250df&label=merged%20pull%20requests&logo=github)](https://github.com/PerimeterX/marshmallow/pulls) [![Commits](https://img.shields.io/github/last-commit/perimeterx/marshmallow)](https://github.com/PerimeterX/marshmallow/commits/main) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md) marshmallow-gopher Marshmallow package provides a simple API to perform flexible and performant JSON unmarshalling in Go. Marshmallow specializes in dealing with **unstructured struct** - when some fields are known and some aren't, with zero performance overhead nor extra coding needed. While unmarshalling, marshmallow allows fully retaining the original data and access it via a typed struct and a dynamic map. ## Contents - [Install](#install) - [Usage](#usage) - [Performance Benchmark And Alternatives](#performance-benchmark-and-alternatives) - [When Should I Use Marshmallow](#when-should-i-use-marshmallow) - [API](#api) ## Install ```sh go get -u github.com/perimeterx/marshmallow ``` ## Usage ```go package main import ( "fmt" "github.com/perimeterx/marshmallow" ) func main() { v := struct { Foo string `json:"foo"` Boo []int `json:"boo"` }{} result, err := marshmallow.Unmarshal([]byte(`{"foo":"bar","boo":[1,2,3],"goo":12.6}`), &v) fmt.Printf("v=%+v, result=%+v, err=%v", v, result, err) // Output: v={Foo:bar Boo:[1 2 3]}, result=map[boo:[1 2 3] foo:bar goo:12.6], err= } ``` **Examples can be found [here](example_test.go)** ## Performance Benchmark And Alternatives Marshmallow performs best when dealing with mixed data - when some fields are known and some are unknown. More info [below](#when-should-i-use-marshmallow). Other solutions are available for this kind of use case, each solution is explained and documented in the link below. The full benchmark test can be found [here](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go). |Benchmark|Iterations|Time/Iteration|Bytes Allocated|Allocations| |--|--|--|--|--| |[unmarshall twice](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go#L40)|228693|5164 ns/op|1640 B/op|51 allocs/op| |[raw map](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go#L66)|232236|5116 ns/op|2296 B/op|53 allocs/op| |[go codec](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go#L121)|388442|3077 ns/op|2512 B/op|37 allocs/op| |[marshmallow](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go#L16)|626168|1853 ns/op|608 B/op|18 allocs/op| |[marshmallow without populating struct](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go#L162)|678616|1751 ns/op|608 B/op|18 allocs/op| ![marshmallow performance comparison](https://raw.githubusercontent.com/PerimeterX/marshmallow/e45088ca20d4ea5be4143d418d12da63a68d6dfd/performance-chart.svg) **Marshmallow provides the best performance (up to X3 faster) while not requiring any extra coding.** In fact, marshmallow performs as fast as normal `json.Unmarshal` call, however, such a call causes loss of data for all the fields that did not match the given struct. With marshmallow you never lose any data. |Benchmark|Iterations|Time/Iteration|Bytes Allocated|Allocations| |--|--|--|--|--| |[marshmallow](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go#L16)|626168|1853 ns/op|608 B/op|18 allocs/op| |[native library](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go#L143)|652106|1845 ns/op|304 B/op|11 allocs/op| |[marshmallow without populating struct](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go#L162)|678616|1751 ns/op|608 B/op|18 allocs/op| ## When Should I Use Marshmallow Marshmallow is best suited for use cases where you are interested in all the input data, but you have predetermined information only about a subset of it. For instance, if you plan to reference two specific fields from the data, then iterate all the data and apply some generic logic. How does it look with the native library: ```go func isAllowedToDrive(data []byte) (bool, error) { result := make(map[string]interface{}) err := json.Unmarshal(data, &result) if err != nil { return false, err } age, ok := result["age"] if !ok { return false, nil } a, ok := age.(float64) if !ok { return false, nil } if a < 17 { return false, nil } hasDriversLicense, ok := result["has_drivers_license"] if !ok { return false, nil } h, ok := hasDriversLicense.(bool) if !ok { return false, nil } if !h { return false, nil } for key := range result { if strings.Contains(key, "prior_conviction") { return false, nil } } return true, nil } ``` And with marshmallow: ```go func isAllowedToDrive(data []byte) (bool, error) { v := struct { Age int `json:"age"` HasDriversLicense bool `json:"has_drivers_license"` }{} result, err := marshmallow.Unmarshal(data, &v) if err != nil { return false, err } if v.Age < 17 || !v.HasDriversLicense { return false, nil } for key := range result { if strings.Contains(key, "prior_conviction") { return false, nil } } return true, nil } ``` ## API Marshmallow exposes two main API functions - [Unmarshal](https://github.com/PerimeterX/marshmallow/blob/0e0218ab860be8a4b5f57f5ff239f281c250c5da/unmarshal.go#L27) and [UnmarshalFromJSONMap](https://github.com/PerimeterX/marshmallow/blob/0e0218ab860be8a4b5f57f5ff239f281c250c5da/unmarshal_from_json_map.go#L37). While unmarshalling, marshmallow supports the following optional options: * Setting the mode for handling invalid data using the [WithMode](https://github.com/PerimeterX/marshmallow/blob/0e0218ab860be8a4b5f57f5ff239f281c250c5da/options.go#L30) function. * Excluding known fields from the result map using the [WithExcludeKnownFieldsFromMap](https://github.com/PerimeterX/marshmallow/blob/457669ae9973895584f2636eabfc104140d3b700/options.go#L50) function. * Skipping struct population to boost performance using the [WithSkipPopulateStruct](https://github.com/PerimeterX/marshmallow/blob/0e0218ab860be8a4b5f57f5ff239f281c250c5da/options.go#L41) function. In order to capture unknown nested fields, structs must implement [JSONDataErrorHandler](https://github.com/PerimeterX/marshmallow/blob/195c994aa6e3e0852601ad9cf65bcddef0dd7479/options.go#L76). More info [here](https://github.com/PerimeterX/marshmallow/issues/15). Marshmallow also supports caching of refection information using [EnableCache](https://github.com/PerimeterX/marshmallow/blob/d3500aa5b0f330942b178b155da933c035dd3906/cache.go#L40) and [EnableCustomCache](https://github.com/PerimeterX/marshmallow/blob/d3500aa5b0f330942b178b155da933c035dd3906/cache.go#L35). ## Contact and Contribute Reporting issues and requesting features may be done in our [GitHub issues page](https://github.com/PerimeterX/marshmallow/issues). Discussions may be conducted in our [GitHub discussions page](https://github.com/PerimeterX/marshmallow/discussions). For any further questions or comments you can reach us out at [open-source@humansecurity.com](mailto:open-source@humansecurity.com). Any type of contribution is warmly welcome and appreciated ❤️ Please read our [contribution](CONTRIBUTING.md) guide for more info. If you're looking for something to get started with, tou can always follow our [issues page](https://github.com/PerimeterX/marshmallow/issues) and look for [good first issue](https://github.com/PerimeterX/marshmallow/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) and [help wanted](https://github.com/PerimeterX/marshmallow/issues?q=is%3Aissue+label%3A%22help+wanted%22+is%3Aopen) labels. ## Marshmallow Logo Marshmallow logo and assets by [Adva Rom](https://www.linkedin.com/in/adva-rom-7a6738127/) are licensed under a Creative Commons Attribution 4.0 International License.
![Marshmallow Logo](https://raw.githubusercontent.com/PerimeterX/marshmallow/assets/marshmallow.png) ================================================ FILE: vendor/github.com/perimeterx/marshmallow/cache.go ================================================ // Copyright 2022 PerimeterX. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. package marshmallow import ( "reflect" "sync" ) // Cache allows unmarshalling to use a cached version of refection information about types. // Cache interface follows the implementation of sync.Map, but you may wrap any cache implementation // to match it. This allows you to control max cache size, eviction policies and any other caching aspect. type Cache interface { // Load returns the value stored in the map for a key, or nil if no value is present. // The ok result indicates whether value was found in the map. Load(key interface{}) (interface{}, bool) // Store sets the value for a key. Store(key, value interface{}) } // EnableCustomCache enables unmarshalling cache. It allows reuse of refection information about types needed // to perform the unmarshalling. A use of such cache can boost up unmarshalling by x1.4. // Check out benchmark_test.go for an example. // // EnableCustomCache is not thread safe! Do not use it while performing unmarshalling, or it will // cause an unsafe race condition. Typically, EnableCustomCache should be called once when the process boots. // // Caching is disabled by default. The use of this function allows enabling it and controlling the // behavior of the cache. Typically, the use of sync.Map should be good enough. The caching mechanism // stores a single map per struct type. If you plan to unmarshal a huge amount of distinct // struct it may get to consume a lot of resources, in which case you have the control to choose // the caching implementation you like and its setup. func EnableCustomCache(c Cache) { cache = c } // EnableCache enables unmarshalling cache with default implementation. More info at EnableCustomCache. func EnableCache() { EnableCustomCache(&sync.Map{}) } var cache Cache func cacheLookup(t reflect.Type) map[string]reflectionInfo { if cache == nil { return nil } value, exists := cache.Load(t) if !exists { return nil } result, _ := value.(map[string]reflectionInfo) return result } func cacheStore(t reflect.Type, fields map[string]reflectionInfo) { if cache == nil { return } cache.Store(t, fields) } ================================================ FILE: vendor/github.com/perimeterx/marshmallow/doc.go ================================================ /* Package marshmallow provides a simple API to perform flexible and performant JSON unmarshalling. Unlike other packages, marshmallow supports unmarshalling of some known and some unknown fields with zero performance overhead nor extra coding needed. While unmarshalling, marshmallow allows fully retaining the original data and access it via a typed struct and a dynamic map. https://github.com/perimeterx/marshmallow */ package marshmallow ================================================ FILE: vendor/github.com/perimeterx/marshmallow/errors.go ================================================ // Copyright 2022 PerimeterX. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. package marshmallow import ( "errors" "fmt" "github.com/mailru/easyjson/jlexer" "reflect" "strings" ) var ( // ErrInvalidInput indicates the input JSON is invalid ErrInvalidInput = errors.New("invalid JSON input") // ErrInvalidValue indicates the target struct has invalid type ErrInvalidValue = errors.New("unexpected non struct value") ) // MultipleLexerError indicates one or more unmarshalling errors during JSON bytes decode type MultipleLexerError struct { Errors []*jlexer.LexerError } func (m *MultipleLexerError) Error() string { errs := make([]string, len(m.Errors)) for i, lexerError := range m.Errors { errs[i] = lexerError.Error() } return strings.Join(errs, ", ") } // MultipleError indicates one or more unmarshalling errors during JSON map decode type MultipleError struct { Errors []error } func (m *MultipleError) Error() string { errs := make([]string, len(m.Errors)) for i, lexerError := range m.Errors { errs[i] = lexerError.Error() } return strings.Join(errs, ", ") } // ParseError indicates a JSON map decode error type ParseError struct { Reason string Path string } func (p *ParseError) Error() string { return fmt.Sprintf("parse error: %s in %s", p.Reason, p.Path) } func newUnexpectedTypeParseError(expectedType reflect.Type, path []string) *ParseError { return &ParseError{ Reason: fmt.Sprintf("expected type %s", externalTypeName(expectedType)), Path: strings.Join(path, "."), } } func newUnsupportedTypeParseError(unsupportedType reflect.Type, path []string) *ParseError { return &ParseError{ Reason: fmt.Sprintf("unsupported type %s", externalTypeName(unsupportedType)), Path: strings.Join(path, "."), } } func addUnexpectedTypeLexerError(lexer *jlexer.Lexer, expectedType reflect.Type) { lexer.AddNonFatalError(fmt.Errorf("expected type %s", externalTypeName(expectedType))) } func addUnsupportedTypeLexerError(lexer *jlexer.Lexer, unsupportedType reflect.Type) { lexer.AddNonFatalError(fmt.Errorf("unsupported type %s", externalTypeName(unsupportedType))) } func externalTypeName(t reflect.Type) string { switch t.Kind() { case reflect.String: return "string" case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: return "number" case reflect.Bool: return "boolean" case reflect.Array, reflect.Slice: return "array" case reflect.Interface: return "any" case reflect.Map, reflect.Struct: return "object" case reflect.Ptr: return externalTypeName(t.Elem()) } return "invalid" } ================================================ FILE: vendor/github.com/perimeterx/marshmallow/options.go ================================================ // Copyright 2022 PerimeterX. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. package marshmallow // Mode dictates the unmarshalling mode. // Each mode is self documented below. type Mode uint8 const ( // ModeFailOnFirstError is the default mode. It makes unmarshalling terminate // immediately on any kind of error. This error will then be returned. ModeFailOnFirstError Mode = iota // ModeAllowMultipleErrors mode makes unmarshalling keep decoding even if // errors are encountered. In case of such error, the erroneous value will be omitted from the result. // Eventually, all errors will all be returned, alongside the partial result. ModeAllowMultipleErrors // ModeFailOverToOriginalValue mode makes unmarshalling keep decoding even if // errors are encountered. In case of such error, the original external value be placed in the // result data, even though it does not meet the schematic requirements. // Eventually, all errors will be returned, alongside the full result. Note that the result map // will contain values that do not match the struct schema. ModeFailOverToOriginalValue ) // WithMode is an UnmarshalOption function to set the unmarshalling mode. func WithMode(mode Mode) UnmarshalOption { return func(options *unmarshalOptions) { options.mode = mode } } // WithSkipPopulateStruct is an UnmarshalOption function to set the skipPopulateStruct option. // Skipping populate struct is set to false by default. // If you do not intend to use the struct value once unmarshalling is finished, set this // option to true to boost performance. This would mean the struct fields will not be set // with values, but rather it will only be used as the target schema when populating the result map. func WithSkipPopulateStruct(skipPopulateStruct bool) UnmarshalOption { return func(options *unmarshalOptions) { options.skipPopulateStruct = skipPopulateStruct } } // WithExcludeKnownFieldsFromMap is an UnmarshalOption function to set the excludeKnownFieldsFromMap option. // Exclude known fields flag is set to false by default. // When the flag is set to true, fields specified in the input struct (known fields) will be excluded from the result map func WithExcludeKnownFieldsFromMap(excludeKnownFields bool) UnmarshalOption { return func(options *unmarshalOptions) { options.excludeKnownFieldsFromMap = excludeKnownFields } } type UnmarshalOption func(*unmarshalOptions) type unmarshalOptions struct { mode Mode skipPopulateStruct bool excludeKnownFieldsFromMap bool } func buildUnmarshalOptions(options []UnmarshalOption) *unmarshalOptions { result := &unmarshalOptions{} for _, option := range options { option(result) } return result } // JSONDataErrorHandler allow types to handle JSON data as maps. // Types should implement this interface if they wish to act on the map representation of parsed JSON input. // This is mainly used to allow nested objects to capture unknown fields and leverage marshmallow's abilities. // If HandleJSONData returns an error, it will be propagated as an unmarshal error type JSONDataErrorHandler interface { HandleJSONData(data map[string]interface{}) error } // Deprecated: use JSONDataErrorHandler instead type JSONDataHandler interface { HandleJSONData(data map[string]interface{}) } func asJSONDataHandler(value interface{}) (func(map[string]interface{}) error, bool) { if handler, ok := value.(JSONDataErrorHandler); ok { return handler.HandleJSONData, true } if handler, ok := value.(JSONDataHandler); ok { return func(m map[string]interface{}) error { handler.HandleJSONData(m) return nil }, true } return nil, false } ================================================ FILE: vendor/github.com/perimeterx/marshmallow/reflection.go ================================================ // Copyright 2022 PerimeterX. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. package marshmallow import ( "encoding/json" "reflect" "strings" ) var unmarshalerType = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem() type reflectionInfo struct { path []int t reflect.Type } func (r reflectionInfo) field(target reflect.Value) reflect.Value { current := target for _, i := range r.path { current = current.Field(i) } return current } func mapStructFields(target interface{}) map[string]reflectionInfo { t := reflectStructType(target) result := cacheLookup(t) if result != nil { return result } result = make(map[string]reflectionInfo, t.NumField()) mapTypeFields(t, result, nil) cacheStore(t, result) return result } func mapTypeFields(t reflect.Type, result map[string]reflectionInfo, path []int) { num := t.NumField() for i := 0; i < num; i++ { field := t.Field(i) fieldPath := append(path, i) if field.Anonymous && field.Type.Kind() == reflect.Struct { mapTypeFields(field.Type, result, fieldPath) continue } name := field.Tag.Get("json") if name == "" || name == "-" { continue } if index := strings.Index(name, ","); index > -1 { name = name[:index] } result[name] = reflectionInfo{ path: fieldPath, t: field.Type, } } } func reflectStructValue(target interface{}) reflect.Value { v := reflect.ValueOf(target) for v.Kind() == reflect.Ptr { v = v.Elem() } return v } func reflectStructType(target interface{}) reflect.Type { t := reflect.TypeOf(target) for t.Kind() == reflect.Ptr { t = t.Elem() } return t } var primitiveConverters = map[reflect.Kind]func(v interface{}) (interface{}, bool){ reflect.Bool: func(v interface{}) (interface{}, bool) { res, ok := v.(bool) return res, ok }, reflect.Int: func(v interface{}) (interface{}, bool) { res, ok := v.(float64) if ok { return int(res), true } return v, false }, reflect.Int8: func(v interface{}) (interface{}, bool) { res, ok := v.(float64) if ok { return int8(res), true } return v, false }, reflect.Int16: func(v interface{}) (interface{}, bool) { res, ok := v.(float64) if ok { return int16(res), true } return v, false }, reflect.Int32: func(v interface{}) (interface{}, bool) { res, ok := v.(float64) if ok { return int32(res), true } return v, false }, reflect.Int64: func(v interface{}) (interface{}, bool) { res, ok := v.(float64) if ok { return int64(res), true } return v, false }, reflect.Uint: func(v interface{}) (interface{}, bool) { res, ok := v.(float64) if ok { return uint(res), true } return v, false }, reflect.Uint8: func(v interface{}) (interface{}, bool) { res, ok := v.(float64) if ok { return uint8(res), true } return v, false }, reflect.Uint16: func(v interface{}) (interface{}, bool) { res, ok := v.(float64) if ok { return uint16(res), true } return v, false }, reflect.Uint32: func(v interface{}) (interface{}, bool) { res, ok := v.(float64) if ok { return uint32(res), true } return v, false }, reflect.Uint64: func(v interface{}) (interface{}, bool) { res, ok := v.(float64) if ok { return uint64(res), true } return v, false }, reflect.Float32: func(v interface{}) (interface{}, bool) { res, ok := v.(float64) if ok { return float32(res), true } return v, false }, reflect.Float64: func(v interface{}) (interface{}, bool) { res, ok := v.(float64) if ok { return res, true } return v, false }, reflect.Interface: func(v interface{}) (interface{}, bool) { return v, true }, reflect.String: func(v interface{}) (interface{}, bool) { res, ok := v.(string) return res, ok }, } func assignValue(field reflect.Value, value interface{}) { if value == nil { return } reflectValue := reflect.ValueOf(value) if reflectValue.Type().AssignableTo(field.Type()) { field.Set(reflectValue) } } func isValidValue(v interface{}) bool { value := reflect.ValueOf(v) return value.Kind() == reflect.Ptr && value.Elem().Kind() == reflect.Struct && !value.IsNil() } func safeReflectValue(t reflect.Type, v interface{}) reflect.Value { if v == nil { return reflect.Zero(t) } return reflect.ValueOf(v) } ================================================ FILE: vendor/github.com/perimeterx/marshmallow/unmarshal.go ================================================ // Copyright 2022 PerimeterX. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. package marshmallow import ( "encoding/json" "github.com/mailru/easyjson/jlexer" "reflect" ) // Unmarshal parses the JSON-encoded object in data and stores the values // in the struct pointed to by v and in the returned map. // If v is nil or not a pointer to a struct, Unmarshal returns an ErrInvalidValue. // If data is not a valid JSON or not a JSON object Unmarshal returns an ErrInvalidInput. // // Unmarshal follows the rules of json.Unmarshal with the following exceptions: // - All input fields are stored in the resulting map, including fields that do not exist in the // struct pointed by v. // - Unmarshal only operates on JSON object inputs. It will reject all other types of input // by returning ErrInvalidInput. // - Unmarshal only operates on struct values. It will reject all other types of v by // returning ErrInvalidValue. // - Unmarshal supports three types of Mode values. Each mode is self documented and affects // how Unmarshal behaves. func Unmarshal(data []byte, v interface{}, options ...UnmarshalOption) (map[string]interface{}, error) { if !isValidValue(v) { return nil, ErrInvalidValue } opts := buildUnmarshalOptions(options) useMultipleErrors := opts.mode == ModeAllowMultipleErrors || opts.mode == ModeFailOverToOriginalValue d := &decoder{options: opts, lexer: &jlexer.Lexer{Data: data, UseMultipleErrors: useMultipleErrors}} result := make(map[string]interface{}) if d.lexer.IsNull() { d.lexer.Skip() } else if !d.lexer.IsDelim('{') { return nil, ErrInvalidInput } else { d.populateStruct(false, v, result) } d.lexer.Consumed() if useMultipleErrors { errors := d.lexer.GetNonFatalErrors() if len(errors) == 0 { return result, nil } return result, &MultipleLexerError{Errors: errors} } err := d.lexer.Error() if err != nil { return nil, err } return result, nil } type decoder struct { options *unmarshalOptions lexer *jlexer.Lexer } func (d *decoder) populateStruct(forcePopulate bool, structInstance interface{}, result map[string]interface{}) (interface{}, bool) { doPopulate := !d.options.skipPopulateStruct || forcePopulate var structValue reflect.Value if doPopulate { structValue = reflectStructValue(structInstance) } fields := mapStructFields(structInstance) var clone map[string]interface{} if d.options.mode == ModeFailOverToOriginalValue { clone = make(map[string]interface{}, len(fields)) } d.lexer.Delim('{') for !d.lexer.IsDelim('}') { key := d.lexer.UnsafeFieldName(false) d.lexer.WantColon() refInfo, exists := fields[key] if exists { value, isValidType := d.valueByReflectType(refInfo.t) if isValidType { if value != nil && doPopulate { field := refInfo.field(structValue) assignValue(field, value) } if !d.options.excludeKnownFieldsFromMap { if result != nil { result[key] = value } if clone != nil { clone[key] = value } } } else { switch d.options.mode { case ModeFailOnFirstError: return nil, false case ModeFailOverToOriginalValue: if !forcePopulate { result[key] = value } else { clone[key] = value d.lexer.WantComma() d.drainLexerMap(clone) return clone, false } } } } else { value := d.lexer.Interface() if result != nil { result[key] = value } if clone != nil { clone[key] = value } } d.lexer.WantComma() } d.lexer.Delim('}') return structInstance, true } func (d *decoder) valueByReflectType(t reflect.Type) (interface{}, bool) { if t.Implements(unmarshalerType) { result := reflect.New(t.Elem()).Interface() d.valueFromCustomUnmarshaler(result.(json.Unmarshaler)) return result, true } if reflect.PtrTo(t).Implements(unmarshalerType) { value := reflect.New(t) d.valueFromCustomUnmarshaler(value.Interface().(json.Unmarshaler)) return value.Elem().Interface(), true } kind := t.Kind() if converter := primitiveConverters[kind]; converter != nil { v := d.lexer.Interface() if v == nil { return nil, true } converted, ok := converter(v) if !ok { addUnexpectedTypeLexerError(d.lexer, t) return v, false } return converted, true } switch kind { case reflect.Slice: return d.buildSlice(t) case reflect.Array: return d.buildArray(t) case reflect.Map: return d.buildMap(t) case reflect.Struct: value, valid := d.buildStruct(t) if value == nil { return nil, valid } if !valid { return value, false } return reflect.ValueOf(value).Elem().Interface(), valid case reflect.Ptr: if t.Elem().Kind() == reflect.Struct { return d.buildStruct(t.Elem()) } value, valid := d.valueByReflectType(t.Elem()) if value == nil { return nil, valid } if !valid { return value, false } result := reflect.New(reflect.TypeOf(value)) result.Elem().Set(reflect.ValueOf(value)) return result.Interface(), valid } addUnsupportedTypeLexerError(d.lexer, t) return nil, false } func (d *decoder) buildSlice(sliceType reflect.Type) (interface{}, bool) { if d.lexer.IsNull() { d.lexer.Skip() return nil, true } if !d.lexer.IsDelim('[') { addUnexpectedTypeLexerError(d.lexer, sliceType) return d.lexer.Interface(), false } elemType := sliceType.Elem() d.lexer.Delim('[') var sliceValue reflect.Value if !d.lexer.IsDelim(']') { sliceValue = reflect.MakeSlice(sliceType, 0, 4) } else { sliceValue = reflect.MakeSlice(sliceType, 0, 0) } for !d.lexer.IsDelim(']') { current, valid := d.valueByReflectType(elemType) if !valid { if d.options.mode != ModeFailOverToOriginalValue { d.drainLexerArray(nil) return nil, true } result := d.cloneReflectArray(sliceValue, -1) result = append(result, current) return d.drainLexerArray(result), true } sliceValue = reflect.Append(sliceValue, safeReflectValue(elemType, current)) d.lexer.WantComma() } d.lexer.Delim(']') return sliceValue.Interface(), true } func (d *decoder) buildArray(arrayType reflect.Type) (interface{}, bool) { if d.lexer.IsNull() { d.lexer.Skip() return nil, true } if !d.lexer.IsDelim('[') { addUnexpectedTypeLexerError(d.lexer, arrayType) return d.lexer.Interface(), false } elemType := arrayType.Elem() arrayValue := reflect.New(arrayType).Elem() d.lexer.Delim('[') for i := 0; !d.lexer.IsDelim(']'); i++ { current, valid := d.valueByReflectType(elemType) if !valid { if d.options.mode != ModeFailOverToOriginalValue { d.drainLexerArray(nil) return nil, true } result := d.cloneReflectArray(arrayValue, i) result = append(result, current) return d.drainLexerArray(result), true } if current != nil { arrayValue.Index(i).Set(reflect.ValueOf(current)) } d.lexer.WantComma() } d.lexer.Delim(']') return arrayValue.Interface(), true } func (d *decoder) buildMap(mapType reflect.Type) (interface{}, bool) { if d.lexer.IsNull() { d.lexer.Skip() return nil, true } if !d.lexer.IsDelim('{') { addUnexpectedTypeLexerError(d.lexer, mapType) return d.lexer.Interface(), false } d.lexer.Delim('{') keyType := mapType.Key() valueType := mapType.Elem() mapValue := reflect.MakeMap(mapType) for !d.lexer.IsDelim('}') { key, valid := d.valueByReflectType(keyType) if !valid { if d.options.mode != ModeFailOverToOriginalValue { d.lexer.WantColon() d.lexer.Interface() d.lexer.WantComma() d.drainLexerMap(make(map[string]interface{})) return nil, true } strKey, _ := key.(string) d.lexer.WantColon() value := d.lexer.Interface() result := d.cloneReflectMap(mapValue) result[strKey] = value d.lexer.WantComma() d.drainLexerMap(result) return result, true } d.lexer.WantColon() value, valid := d.valueByReflectType(valueType) if !valid { if d.options.mode != ModeFailOverToOriginalValue { d.lexer.WantComma() d.drainLexerMap(make(map[string]interface{})) return nil, true } strKey, _ := key.(string) result := d.cloneReflectMap(mapValue) result[strKey] = value d.lexer.WantComma() d.drainLexerMap(result) return result, true } mapValue.SetMapIndex(safeReflectValue(keyType, key), safeReflectValue(valueType, value)) d.lexer.WantComma() } d.lexer.Delim('}') return mapValue.Interface(), true } func (d *decoder) buildStruct(structType reflect.Type) (interface{}, bool) { if d.lexer.IsNull() { d.lexer.Skip() return nil, true } if !d.lexer.IsDelim('{') { addUnexpectedTypeLexerError(d.lexer, structType) return d.lexer.Interface(), false } value := reflect.New(structType).Interface() handler, ok := asJSONDataHandler(value) if !ok { return d.populateStruct(true, value, nil) } data := make(map[string]interface{}) result, valid := d.populateStruct(true, value, data) if !valid { return result, false } err := handler(data) if err != nil { d.lexer.AddNonFatalError(err) return result, false } return result, true } func (d *decoder) valueFromCustomUnmarshaler(unmarshaler json.Unmarshaler) { data := d.lexer.Raw() if !d.lexer.Ok() { return } err := unmarshaler.UnmarshalJSON(data) if err != nil { d.lexer.AddNonFatalError(err) } } func (d *decoder) cloneReflectArray(value reflect.Value, length int) []interface{} { if length == -1 { length = value.Len() } result := make([]interface{}, length) for i := 0; i < length; i++ { result[i] = value.Index(i).Interface() } return result } func (d *decoder) cloneReflectMap(mapValue reflect.Value) map[string]interface{} { l := mapValue.Len() result := make(map[string]interface{}, l) for _, key := range mapValue.MapKeys() { value := mapValue.MapIndex(key) strKey, _ := key.Interface().(string) result[strKey] = value.Interface() } return result } func (d *decoder) drainLexerArray(target []interface{}) interface{} { d.lexer.WantComma() for !d.lexer.IsDelim(']') { current := d.lexer.Interface() target = append(target, current) d.lexer.WantComma() } d.lexer.Delim(']') return target } func (d *decoder) drainLexerMap(target map[string]interface{}) { for !d.lexer.IsDelim('}') { key := d.lexer.String() d.lexer.WantColon() value := d.lexer.Interface() target[key] = value d.lexer.WantComma() } d.lexer.Delim('}') } ================================================ FILE: vendor/github.com/perimeterx/marshmallow/unmarshal_from_json_map.go ================================================ // Copyright 2022 PerimeterX. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. package marshmallow import ( "reflect" ) // UnmarshalerFromJSONMap is the interface implemented by types // that can unmarshal a JSON description of themselves. // In case you want to implement custom unmarshalling, json.Unmarshaler only supports // receiving the data as []byte. However, while unmarshalling from JSON map, // the data is not available as a raw []byte and converting to it will significantly // hurt performance. Thus, if you wish to implement a custom unmarshalling on a type // that is being unmarshalled from a JSON map, you need to implement // UnmarshalerFromJSONMap interface. type UnmarshalerFromJSONMap interface { UnmarshalJSONFromMap(data interface{}) error } // UnmarshalFromJSONMap parses the JSON map data and stores the values // in the struct pointed to by v and in the returned map. // If v is nil or not a pointer to a struct, UnmarshalFromJSONMap returns an ErrInvalidValue. // // UnmarshalFromJSONMap follows the rules of json.Unmarshal with the following exceptions: // - All input fields are stored in the resulting map, including fields that do not exist in the // struct pointed by v. // - UnmarshalFromJSONMap receive a JSON map instead of raw bytes. The given input map is assumed // to be a JSON map, meaning it should only contain the following types: bool, string, float64, // []interface, and map[string]interface{}. Other types will cause decoding to return unexpected results. // - UnmarshalFromJSONMap only operates on struct values. It will reject all other types of v by // returning ErrInvalidValue. // - UnmarshalFromJSONMap supports three types of Mode values. Each mode is self documented and affects // how UnmarshalFromJSONMap behaves. func UnmarshalFromJSONMap(data map[string]interface{}, v interface{}, options ...UnmarshalOption) (map[string]interface{}, error) { if !isValidValue(v) { return nil, ErrInvalidValue } opts := buildUnmarshalOptions(options) d := &mapDecoder{options: opts} result := make(map[string]interface{}) if data != nil { d.populateStruct(false, nil, data, v, result) } if opts.mode == ModeAllowMultipleErrors || opts.mode == ModeFailOverToOriginalValue { if len(d.errs) == 0 { return result, nil } return result, &MultipleError{Errors: d.errs} } if d.err != nil { return nil, d.err } return result, nil } var unmarshalerFromJSONMapType = reflect.TypeOf((*UnmarshalerFromJSONMap)(nil)).Elem() type mapDecoder struct { options *unmarshalOptions err error errs []error } func (m *mapDecoder) populateStruct(forcePopulate bool, path []string, data map[string]interface{}, structInstance interface{}, result map[string]interface{}) (interface{}, bool) { doPopulate := !m.options.skipPopulateStruct || forcePopulate var structValue reflect.Value if doPopulate { structValue = reflectStructValue(structInstance) } fields := mapStructFields(structInstance) for key, inputValue := range data { refInfo, exists := fields[key] if exists { value, isValidType := m.valueByReflectType(append(path, key), inputValue, refInfo.t) if isValidType { if value != nil && doPopulate { field := refInfo.field(structValue) assignValue(field, value) } if !m.options.excludeKnownFieldsFromMap { if result != nil { result[key] = value } } } else { switch m.options.mode { case ModeFailOnFirstError: return nil, false case ModeFailOverToOriginalValue: if !forcePopulate { result[key] = value } else { return data, false } } } } else { if result != nil { result[key] = inputValue } } } return structInstance, true } func (m *mapDecoder) valueByReflectType(path []string, v interface{}, t reflect.Type) (interface{}, bool) { if t.Implements(unmarshalerFromJSONMapType) { result := reflect.New(t.Elem()).Interface() m.valueFromCustomUnmarshaler(v, result.(UnmarshalerFromJSONMap)) return result, true } if reflect.PtrTo(t).Implements(unmarshalerFromJSONMapType) { value := reflect.New(t) m.valueFromCustomUnmarshaler(v, value.Interface().(UnmarshalerFromJSONMap)) return value.Elem().Interface(), true } kind := t.Kind() if converter := primitiveConverters[kind]; converter != nil { if v == nil { return nil, true } converted, ok := converter(v) if !ok { m.addError(newUnexpectedTypeParseError(t, path)) return v, false } return converted, true } switch kind { case reflect.Slice: return m.buildSlice(path, v, t) case reflect.Array: return m.buildArray(path, v, t) case reflect.Map: return m.buildMap(path, v, t) case reflect.Struct: value, valid := m.buildStruct(path, v, t) if value == nil { return nil, valid } if !valid { return value, false } return reflect.ValueOf(value).Elem().Interface(), valid case reflect.Ptr: if t.Elem().Kind() == reflect.Struct { return m.buildStruct(path, v, t.Elem()) } value, valid := m.valueByReflectType(path, v, t.Elem()) if value == nil { return nil, valid } if !valid { return value, false } result := reflect.New(reflect.TypeOf(value)) result.Elem().Set(reflect.ValueOf(value)) return result.Interface(), valid } m.addError(newUnsupportedTypeParseError(t, path)) return nil, false } func (m *mapDecoder) buildSlice(path []string, v interface{}, sliceType reflect.Type) (interface{}, bool) { if v == nil { return nil, true } arr, ok := v.([]interface{}) if !ok { m.addError(newUnexpectedTypeParseError(sliceType, path)) return v, false } elemType := sliceType.Elem() var sliceValue reflect.Value if len(arr) > 0 { sliceValue = reflect.MakeSlice(sliceType, 0, 4) } else { sliceValue = reflect.MakeSlice(sliceType, 0, 0) } for _, element := range arr { current, valid := m.valueByReflectType(path, element, elemType) if !valid { if m.options.mode != ModeFailOverToOriginalValue { return nil, true } return v, true } sliceValue = reflect.Append(sliceValue, safeReflectValue(elemType, current)) } return sliceValue.Interface(), true } func (m *mapDecoder) buildArray(path []string, v interface{}, arrayType reflect.Type) (interface{}, bool) { if v == nil { return nil, true } arr, ok := v.([]interface{}) if !ok { m.addError(newUnexpectedTypeParseError(arrayType, path)) return v, false } elemType := arrayType.Elem() arrayValue := reflect.New(arrayType).Elem() for i, element := range arr { current, valid := m.valueByReflectType(path, element, elemType) if !valid { if m.options.mode != ModeFailOverToOriginalValue { return nil, true } return v, true } if current != nil { arrayValue.Index(i).Set(reflect.ValueOf(current)) } } return arrayValue.Interface(), true } func (m *mapDecoder) buildMap(path []string, v interface{}, mapType reflect.Type) (interface{}, bool) { if v == nil { return nil, true } mp, ok := v.(map[string]interface{}) if !ok { m.addError(newUnexpectedTypeParseError(mapType, path)) return v, false } keyType := mapType.Key() valueType := mapType.Elem() mapValue := reflect.MakeMap(mapType) for inputKey, inputValue := range mp { keyPath := append(path, inputKey) key, valid := m.valueByReflectType(keyPath, inputKey, keyType) if !valid { if m.options.mode != ModeFailOverToOriginalValue { return nil, true } return v, true } value, valid := m.valueByReflectType(keyPath, inputValue, valueType) if !valid { if m.options.mode != ModeFailOverToOriginalValue { return nil, true } return v, true } mapValue.SetMapIndex(safeReflectValue(keyType, key), safeReflectValue(valueType, value)) } return mapValue.Interface(), true } func (m *mapDecoder) buildStruct(path []string, v interface{}, structType reflect.Type) (interface{}, bool) { if v == nil { return nil, true } mp, ok := v.(map[string]interface{}) if !ok { m.addError(newUnexpectedTypeParseError(structType, path)) return v, false } value := reflect.New(structType).Interface() handler, ok := asJSONDataHandler(value) if !ok { return m.populateStruct(true, path, mp, value, nil) } data := make(map[string]interface{}) result, valid := m.populateStruct(true, path, mp, value, data) if !valid { return result, false } err := handler(data) if err != nil { m.addError(err) return result, false } return result, true } func (m *mapDecoder) valueFromCustomUnmarshaler(data interface{}, unmarshaler UnmarshalerFromJSONMap) { err := unmarshaler.UnmarshalJSONFromMap(data) if err != nil { m.addError(err) } } func (m *mapDecoder) addError(err error) { if m.options.mode == ModeFailOnFirstError { m.err = err } else { m.errs = append(m.errs, err) } } ================================================ FILE: vendor/github.com/peterbourgon/diskv/LICENSE ================================================ Copyright (c) 2011-2012 Peter Bourgon Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/peterbourgon/diskv/README.md ================================================ # What is diskv? Diskv (disk-vee) is a simple, persistent key-value store written in the Go language. It starts with an incredibly simple API for storing arbitrary data on a filesystem by key, and builds several layers of performance-enhancing abstraction on top. The end result is a conceptually simple, but highly performant, disk-backed storage system. [![Build Status][1]][2] [1]: https://drone.io/github.com/peterbourgon/diskv/status.png [2]: https://drone.io/github.com/peterbourgon/diskv/latest # Installing Install [Go 1][3], either [from source][4] or [with a prepackaged binary][5]. Then, ```bash $ go get github.com/peterbourgon/diskv ``` [3]: http://golang.org [4]: http://golang.org/doc/install/source [5]: http://golang.org/doc/install # Usage ```go package main import ( "fmt" "github.com/peterbourgon/diskv" ) func main() { // Simplest transform function: put all the data files into the base dir. flatTransform := func(s string) []string { return []string{} } // Initialize a new diskv store, rooted at "my-data-dir", with a 1MB cache. d := diskv.New(diskv.Options{ BasePath: "my-data-dir", Transform: flatTransform, CacheSizeMax: 1024 * 1024, }) // Write three bytes to the key "alpha". key := "alpha" d.Write(key, []byte{'1', '2', '3'}) // Read the value back out of the store. value, _ := d.Read(key) fmt.Printf("%v\n", value) // Erase the key+value from the store (and the disk). d.Erase(key) } ``` More complex examples can be found in the "examples" subdirectory. # Theory ## Basic idea At its core, diskv is a map of a key (`string`) to arbitrary data (`[]byte`). The data is written to a single file on disk, with the same name as the key. The key determines where that file will be stored, via a user-provided `TransformFunc`, which takes a key and returns a slice (`[]string`) corresponding to a path list where the key file will be stored. The simplest TransformFunc, ```go func SimpleTransform (key string) []string { return []string{} } ``` will place all keys in the same, base directory. The design is inspired by [Redis diskstore][6]; a TransformFunc which emulates the default diskstore behavior is available in the content-addressable-storage example. [6]: http://groups.google.com/group/redis-db/browse_thread/thread/d444bc786689bde9?pli=1 **Note** that your TransformFunc should ensure that one valid key doesn't transform to a subset of another valid key. That is, it shouldn't be possible to construct valid keys that resolve to directory names. As a concrete example, if your TransformFunc splits on every 3 characters, then ```go d.Write("abcabc", val) // OK: written to /abc/abc/abcabc d.Write("abc", val) // Error: attempted write to /abc/abc, but it's a directory ``` This will be addressed in an upcoming version of diskv. Probably the most important design principle behind diskv is that your data is always flatly available on the disk. diskv will never do anything that would prevent you from accessing, copying, backing up, or otherwise interacting with your data via common UNIX commandline tools. ## Adding a cache An in-memory caching layer is provided by combining the BasicStore functionality with a simple map structure, and keeping it up-to-date as appropriate. Since the map structure in Go is not threadsafe, it's combined with a RWMutex to provide safe concurrent access. ## Adding order diskv is a key-value store and therefore inherently unordered. An ordering system can be injected into the store by passing something which satisfies the diskv.Index interface. (A default implementation, using Google's [btree][7] package, is provided.) Basically, diskv keeps an ordered (by a user-provided Less function) index of the keys, which can be queried. [7]: https://github.com/google/btree ## Adding compression Something which implements the diskv.Compression interface may be passed during store creation, so that all Writes and Reads are filtered through a compression/decompression pipeline. Several default implementations, using stdlib compression algorithms, are provided. Note that data is cached compressed; the cost of decompression is borne with each Read. ## Streaming diskv also now provides ReadStream and WriteStream methods, to allow very large data to be handled efficiently. # Future plans * Needs plenty of robust testing: huge datasets, etc... * More thorough benchmarking * Your suggestions for use-cases I haven't thought of ================================================ FILE: vendor/github.com/peterbourgon/diskv/compression.go ================================================ package diskv import ( "compress/flate" "compress/gzip" "compress/zlib" "io" ) // Compression is an interface that Diskv uses to implement compression of // data. Writer takes a destination io.Writer and returns a WriteCloser that // compresses all data written through it. Reader takes a source io.Reader and // returns a ReadCloser that decompresses all data read through it. You may // define these methods on your own type, or use one of the NewCompression // helpers. type Compression interface { Writer(dst io.Writer) (io.WriteCloser, error) Reader(src io.Reader) (io.ReadCloser, error) } // NewGzipCompression returns a Gzip-based Compression. func NewGzipCompression() Compression { return NewGzipCompressionLevel(flate.DefaultCompression) } // NewGzipCompressionLevel returns a Gzip-based Compression with the given level. func NewGzipCompressionLevel(level int) Compression { return &genericCompression{ wf: func(w io.Writer) (io.WriteCloser, error) { return gzip.NewWriterLevel(w, level) }, rf: func(r io.Reader) (io.ReadCloser, error) { return gzip.NewReader(r) }, } } // NewZlibCompression returns a Zlib-based Compression. func NewZlibCompression() Compression { return NewZlibCompressionLevel(flate.DefaultCompression) } // NewZlibCompressionLevel returns a Zlib-based Compression with the given level. func NewZlibCompressionLevel(level int) Compression { return NewZlibCompressionLevelDict(level, nil) } // NewZlibCompressionLevelDict returns a Zlib-based Compression with the given // level, based on the given dictionary. func NewZlibCompressionLevelDict(level int, dict []byte) Compression { return &genericCompression{ func(w io.Writer) (io.WriteCloser, error) { return zlib.NewWriterLevelDict(w, level, dict) }, func(r io.Reader) (io.ReadCloser, error) { return zlib.NewReaderDict(r, dict) }, } } type genericCompression struct { wf func(w io.Writer) (io.WriteCloser, error) rf func(r io.Reader) (io.ReadCloser, error) } func (g *genericCompression) Writer(dst io.Writer) (io.WriteCloser, error) { return g.wf(dst) } func (g *genericCompression) Reader(src io.Reader) (io.ReadCloser, error) { return g.rf(src) } ================================================ FILE: vendor/github.com/peterbourgon/diskv/diskv.go ================================================ // Diskv (disk-vee) is a simple, persistent, key-value store. // It stores all data flatly on the filesystem. package diskv import ( "bytes" "errors" "fmt" "io" "io/ioutil" "os" "path/filepath" "strings" "sync" "syscall" ) const ( defaultBasePath = "diskv" defaultFilePerm os.FileMode = 0666 defaultPathPerm os.FileMode = 0777 ) var ( defaultTransform = func(s string) []string { return []string{} } errCanceled = errors.New("canceled") errEmptyKey = errors.New("empty key") errBadKey = errors.New("bad key") errImportDirectory = errors.New("can't import a directory") ) // TransformFunction transforms a key into a slice of strings, with each // element in the slice representing a directory in the file path where the // key's entry will eventually be stored. // // For example, if TransformFunc transforms "abcdef" to ["ab", "cde", "f"], // the final location of the data file will be /ab/cde/f/abcdef type TransformFunction func(s string) []string // Options define a set of properties that dictate Diskv behavior. // All values are optional. type Options struct { BasePath string Transform TransformFunction CacheSizeMax uint64 // bytes PathPerm os.FileMode FilePerm os.FileMode // If TempDir is set, it will enable filesystem atomic writes by // writing temporary files to that location before being moved // to BasePath. // Note that TempDir MUST be on the same device/partition as // BasePath. TempDir string Index Index IndexLess LessFunction Compression Compression } // Diskv implements the Diskv interface. You shouldn't construct Diskv // structures directly; instead, use the New constructor. type Diskv struct { Options mu sync.RWMutex cache map[string][]byte cacheSize uint64 } // New returns an initialized Diskv structure, ready to use. // If the path identified by baseDir already contains data, // it will be accessible, but not yet cached. func New(o Options) *Diskv { if o.BasePath == "" { o.BasePath = defaultBasePath } if o.Transform == nil { o.Transform = defaultTransform } if o.PathPerm == 0 { o.PathPerm = defaultPathPerm } if o.FilePerm == 0 { o.FilePerm = defaultFilePerm } d := &Diskv{ Options: o, cache: map[string][]byte{}, cacheSize: 0, } if d.Index != nil && d.IndexLess != nil { d.Index.Initialize(d.IndexLess, d.Keys(nil)) } return d } // Write synchronously writes the key-value pair to disk, making it immediately // available for reads. Write relies on the filesystem to perform an eventual // sync to physical media. If you need stronger guarantees, see WriteStream. func (d *Diskv) Write(key string, val []byte) error { return d.WriteStream(key, bytes.NewBuffer(val), false) } // WriteStream writes the data represented by the io.Reader to the disk, under // the provided key. If sync is true, WriteStream performs an explicit sync on // the file as soon as it's written. // // bytes.Buffer provides io.Reader semantics for basic data types. func (d *Diskv) WriteStream(key string, r io.Reader, sync bool) error { if len(key) <= 0 { return errEmptyKey } d.mu.Lock() defer d.mu.Unlock() return d.writeStreamWithLock(key, r, sync) } // createKeyFileWithLock either creates the key file directly, or // creates a temporary file in TempDir if it is set. func (d *Diskv) createKeyFileWithLock(key string) (*os.File, error) { if d.TempDir != "" { if err := os.MkdirAll(d.TempDir, d.PathPerm); err != nil { return nil, fmt.Errorf("temp mkdir: %s", err) } f, err := ioutil.TempFile(d.TempDir, "") if err != nil { return nil, fmt.Errorf("temp file: %s", err) } if err := f.Chmod(d.FilePerm); err != nil { f.Close() // error deliberately ignored os.Remove(f.Name()) // error deliberately ignored return nil, fmt.Errorf("chmod: %s", err) } return f, nil } mode := os.O_WRONLY | os.O_CREATE | os.O_TRUNC // overwrite if exists f, err := os.OpenFile(d.completeFilename(key), mode, d.FilePerm) if err != nil { return nil, fmt.Errorf("open file: %s", err) } return f, nil } // writeStream does no input validation checking. func (d *Diskv) writeStreamWithLock(key string, r io.Reader, sync bool) error { if err := d.ensurePathWithLock(key); err != nil { return fmt.Errorf("ensure path: %s", err) } f, err := d.createKeyFileWithLock(key) if err != nil { return fmt.Errorf("create key file: %s", err) } wc := io.WriteCloser(&nopWriteCloser{f}) if d.Compression != nil { wc, err = d.Compression.Writer(f) if err != nil { f.Close() // error deliberately ignored os.Remove(f.Name()) // error deliberately ignored return fmt.Errorf("compression writer: %s", err) } } if _, err := io.Copy(wc, r); err != nil { f.Close() // error deliberately ignored os.Remove(f.Name()) // error deliberately ignored return fmt.Errorf("i/o copy: %s", err) } if err := wc.Close(); err != nil { f.Close() // error deliberately ignored os.Remove(f.Name()) // error deliberately ignored return fmt.Errorf("compression close: %s", err) } if sync { if err := f.Sync(); err != nil { f.Close() // error deliberately ignored os.Remove(f.Name()) // error deliberately ignored return fmt.Errorf("file sync: %s", err) } } if err := f.Close(); err != nil { return fmt.Errorf("file close: %s", err) } if f.Name() != d.completeFilename(key) { if err := os.Rename(f.Name(), d.completeFilename(key)); err != nil { os.Remove(f.Name()) // error deliberately ignored return fmt.Errorf("rename: %s", err) } } if d.Index != nil { d.Index.Insert(key) } d.bustCacheWithLock(key) // cache only on read return nil } // Import imports the source file into diskv under the destination key. If the // destination key already exists, it's overwritten. If move is true, the // source file is removed after a successful import. func (d *Diskv) Import(srcFilename, dstKey string, move bool) (err error) { if dstKey == "" { return errEmptyKey } if fi, err := os.Stat(srcFilename); err != nil { return err } else if fi.IsDir() { return errImportDirectory } d.mu.Lock() defer d.mu.Unlock() if err := d.ensurePathWithLock(dstKey); err != nil { return fmt.Errorf("ensure path: %s", err) } if move { if err := syscall.Rename(srcFilename, d.completeFilename(dstKey)); err == nil { d.bustCacheWithLock(dstKey) return nil } else if err != syscall.EXDEV { // If it failed due to being on a different device, fall back to copying return err } } f, err := os.Open(srcFilename) if err != nil { return err } defer f.Close() err = d.writeStreamWithLock(dstKey, f, false) if err == nil && move { err = os.Remove(srcFilename) } return err } // Read reads the key and returns the value. // If the key is available in the cache, Read won't touch the disk. // If the key is not in the cache, Read will have the side-effect of // lazily caching the value. func (d *Diskv) Read(key string) ([]byte, error) { rc, err := d.ReadStream(key, false) if err != nil { return []byte{}, err } defer rc.Close() return ioutil.ReadAll(rc) } // ReadStream reads the key and returns the value (data) as an io.ReadCloser. // If the value is cached from a previous read, and direct is false, // ReadStream will use the cached value. Otherwise, it will return a handle to // the file on disk, and cache the data on read. // // If direct is true, ReadStream will lazily delete any cached value for the // key, and return a direct handle to the file on disk. // // If compression is enabled, ReadStream taps into the io.Reader stream prior // to decompression, and caches the compressed data. func (d *Diskv) ReadStream(key string, direct bool) (io.ReadCloser, error) { d.mu.RLock() defer d.mu.RUnlock() if val, ok := d.cache[key]; ok { if !direct { buf := bytes.NewBuffer(val) if d.Compression != nil { return d.Compression.Reader(buf) } return ioutil.NopCloser(buf), nil } go func() { d.mu.Lock() defer d.mu.Unlock() d.uncacheWithLock(key, uint64(len(val))) }() } return d.readWithRLock(key) } // read ignores the cache, and returns an io.ReadCloser representing the // decompressed data for the given key, streamed from the disk. Clients should // acquire a read lock on the Diskv and check the cache themselves before // calling read. func (d *Diskv) readWithRLock(key string) (io.ReadCloser, error) { filename := d.completeFilename(key) fi, err := os.Stat(filename) if err != nil { return nil, err } if fi.IsDir() { return nil, os.ErrNotExist } f, err := os.Open(filename) if err != nil { return nil, err } var r io.Reader if d.CacheSizeMax > 0 { r = newSiphon(f, d, key) } else { r = &closingReader{f} } var rc = io.ReadCloser(ioutil.NopCloser(r)) if d.Compression != nil { rc, err = d.Compression.Reader(r) if err != nil { return nil, err } } return rc, nil } // closingReader provides a Reader that automatically closes the // embedded ReadCloser when it reaches EOF type closingReader struct { rc io.ReadCloser } func (cr closingReader) Read(p []byte) (int, error) { n, err := cr.rc.Read(p) if err == io.EOF { if closeErr := cr.rc.Close(); closeErr != nil { return n, closeErr // close must succeed for Read to succeed } } return n, err } // siphon is like a TeeReader: it copies all data read through it to an // internal buffer, and moves that buffer to the cache at EOF. type siphon struct { f *os.File d *Diskv key string buf *bytes.Buffer } // newSiphon constructs a siphoning reader that represents the passed file. // When a successful series of reads ends in an EOF, the siphon will write // the buffered data to Diskv's cache under the given key. func newSiphon(f *os.File, d *Diskv, key string) io.Reader { return &siphon{ f: f, d: d, key: key, buf: &bytes.Buffer{}, } } // Read implements the io.Reader interface for siphon. func (s *siphon) Read(p []byte) (int, error) { n, err := s.f.Read(p) if err == nil { return s.buf.Write(p[0:n]) // Write must succeed for Read to succeed } if err == io.EOF { s.d.cacheWithoutLock(s.key, s.buf.Bytes()) // cache may fail if closeErr := s.f.Close(); closeErr != nil { return n, closeErr // close must succeed for Read to succeed } return n, err } return n, err } // Erase synchronously erases the given key from the disk and the cache. func (d *Diskv) Erase(key string) error { d.mu.Lock() defer d.mu.Unlock() d.bustCacheWithLock(key) // erase from index if d.Index != nil { d.Index.Delete(key) } // erase from disk filename := d.completeFilename(key) if s, err := os.Stat(filename); err == nil { if s.IsDir() { return errBadKey } if err = os.Remove(filename); err != nil { return err } } else { // Return err as-is so caller can do os.IsNotExist(err). return err } // clean up and return d.pruneDirsWithLock(key) return nil } // EraseAll will delete all of the data from the store, both in the cache and on // the disk. Note that EraseAll doesn't distinguish diskv-related data from non- // diskv-related data. Care should be taken to always specify a diskv base // directory that is exclusively for diskv data. func (d *Diskv) EraseAll() error { d.mu.Lock() defer d.mu.Unlock() d.cache = make(map[string][]byte) d.cacheSize = 0 if d.TempDir != "" { os.RemoveAll(d.TempDir) // errors ignored } return os.RemoveAll(d.BasePath) } // Has returns true if the given key exists. func (d *Diskv) Has(key string) bool { d.mu.Lock() defer d.mu.Unlock() if _, ok := d.cache[key]; ok { return true } filename := d.completeFilename(key) s, err := os.Stat(filename) if err != nil { return false } if s.IsDir() { return false } return true } // Keys returns a channel that will yield every key accessible by the store, // in undefined order. If a cancel channel is provided, closing it will // terminate and close the keys channel. func (d *Diskv) Keys(cancel <-chan struct{}) <-chan string { return d.KeysPrefix("", cancel) } // KeysPrefix returns a channel that will yield every key accessible by the // store with the given prefix, in undefined order. If a cancel channel is // provided, closing it will terminate and close the keys channel. If the // provided prefix is the empty string, all keys will be yielded. func (d *Diskv) KeysPrefix(prefix string, cancel <-chan struct{}) <-chan string { var prepath string if prefix == "" { prepath = d.BasePath } else { prepath = d.pathFor(prefix) } c := make(chan string) go func() { filepath.Walk(prepath, walker(c, prefix, cancel)) close(c) }() return c } // walker returns a function which satisfies the filepath.WalkFunc interface. // It sends every non-directory file entry down the channel c. func walker(c chan<- string, prefix string, cancel <-chan struct{}) filepath.WalkFunc { return func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() || !strings.HasPrefix(info.Name(), prefix) { return nil // "pass" } select { case c <- info.Name(): case <-cancel: return errCanceled } return nil } } // pathFor returns the absolute path for location on the filesystem where the // data for the given key will be stored. func (d *Diskv) pathFor(key string) string { return filepath.Join(d.BasePath, filepath.Join(d.Transform(key)...)) } // ensurePathWithLock is a helper function that generates all necessary // directories on the filesystem for the given key. func (d *Diskv) ensurePathWithLock(key string) error { return os.MkdirAll(d.pathFor(key), d.PathPerm) } // completeFilename returns the absolute path to the file for the given key. func (d *Diskv) completeFilename(key string) string { return filepath.Join(d.pathFor(key), key) } // cacheWithLock attempts to cache the given key-value pair in the store's // cache. It can fail if the value is larger than the cache's maximum size. func (d *Diskv) cacheWithLock(key string, val []byte) error { valueSize := uint64(len(val)) if err := d.ensureCacheSpaceWithLock(valueSize); err != nil { return fmt.Errorf("%s; not caching", err) } // be very strict about memory guarantees if (d.cacheSize + valueSize) > d.CacheSizeMax { panic(fmt.Sprintf("failed to make room for value (%d/%d)", valueSize, d.CacheSizeMax)) } d.cache[key] = val d.cacheSize += valueSize return nil } // cacheWithoutLock acquires the store's (write) mutex and calls cacheWithLock. func (d *Diskv) cacheWithoutLock(key string, val []byte) error { d.mu.Lock() defer d.mu.Unlock() return d.cacheWithLock(key, val) } func (d *Diskv) bustCacheWithLock(key string) { if val, ok := d.cache[key]; ok { d.uncacheWithLock(key, uint64(len(val))) } } func (d *Diskv) uncacheWithLock(key string, sz uint64) { d.cacheSize -= sz delete(d.cache, key) } // pruneDirsWithLock deletes empty directories in the path walk leading to the // key k. Typically this function is called after an Erase is made. func (d *Diskv) pruneDirsWithLock(key string) error { pathlist := d.Transform(key) for i := range pathlist { dir := filepath.Join(d.BasePath, filepath.Join(pathlist[:len(pathlist)-i]...)) // thanks to Steven Blenkinsop for this snippet switch fi, err := os.Stat(dir); true { case err != nil: return err case !fi.IsDir(): panic(fmt.Sprintf("corrupt dirstate at %s", dir)) } nlinks, err := filepath.Glob(filepath.Join(dir, "*")) if err != nil { return err } else if len(nlinks) > 0 { return nil // has subdirs -- do not prune } if err = os.Remove(dir); err != nil { return err } } return nil } // ensureCacheSpaceWithLock deletes entries from the cache in arbitrary order // until the cache has at least valueSize bytes available. func (d *Diskv) ensureCacheSpaceWithLock(valueSize uint64) error { if valueSize > d.CacheSizeMax { return fmt.Errorf("value size (%d bytes) too large for cache (%d bytes)", valueSize, d.CacheSizeMax) } safe := func() bool { return (d.cacheSize + valueSize) <= d.CacheSizeMax } for key, val := range d.cache { if safe() { break } d.uncacheWithLock(key, uint64(len(val))) } if !safe() { panic(fmt.Sprintf("%d bytes still won't fit in the cache! (max %d bytes)", valueSize, d.CacheSizeMax)) } return nil } // nopWriteCloser wraps an io.Writer and provides a no-op Close method to // satisfy the io.WriteCloser interface. type nopWriteCloser struct { io.Writer } func (wc *nopWriteCloser) Write(p []byte) (int, error) { return wc.Writer.Write(p) } func (wc *nopWriteCloser) Close() error { return nil } ================================================ FILE: vendor/github.com/peterbourgon/diskv/index.go ================================================ package diskv import ( "sync" "github.com/google/btree" ) // Index is a generic interface for things that can // provide an ordered list of keys. type Index interface { Initialize(less LessFunction, keys <-chan string) Insert(key string) Delete(key string) Keys(from string, n int) []string } // LessFunction is used to initialize an Index of keys in a specific order. type LessFunction func(string, string) bool // btreeString is a custom data type that satisfies the BTree Less interface, // making the strings it wraps sortable by the BTree package. type btreeString struct { s string l LessFunction } // Less satisfies the BTree.Less interface using the btreeString's LessFunction. func (s btreeString) Less(i btree.Item) bool { return s.l(s.s, i.(btreeString).s) } // BTreeIndex is an implementation of the Index interface using google/btree. type BTreeIndex struct { sync.RWMutex LessFunction *btree.BTree } // Initialize populates the BTree tree with data from the keys channel, // according to the passed less function. It's destructive to the BTreeIndex. func (i *BTreeIndex) Initialize(less LessFunction, keys <-chan string) { i.Lock() defer i.Unlock() i.LessFunction = less i.BTree = rebuild(less, keys) } // Insert inserts the given key (only) into the BTree tree. func (i *BTreeIndex) Insert(key string) { i.Lock() defer i.Unlock() if i.BTree == nil || i.LessFunction == nil { panic("uninitialized index") } i.BTree.ReplaceOrInsert(btreeString{s: key, l: i.LessFunction}) } // Delete removes the given key (only) from the BTree tree. func (i *BTreeIndex) Delete(key string) { i.Lock() defer i.Unlock() if i.BTree == nil || i.LessFunction == nil { panic("uninitialized index") } i.BTree.Delete(btreeString{s: key, l: i.LessFunction}) } // Keys yields a maximum of n keys in order. If the passed 'from' key is empty, // Keys will return the first n keys. If the passed 'from' key is non-empty, the // first key in the returned slice will be the key that immediately follows the // passed key, in key order. func (i *BTreeIndex) Keys(from string, n int) []string { i.RLock() defer i.RUnlock() if i.BTree == nil || i.LessFunction == nil { panic("uninitialized index") } if i.BTree.Len() <= 0 { return []string{} } btreeFrom := btreeString{s: from, l: i.LessFunction} skipFirst := true if len(from) <= 0 || !i.BTree.Has(btreeFrom) { // no such key, so fabricate an always-smallest item btreeFrom = btreeString{s: "", l: func(string, string) bool { return true }} skipFirst = false } keys := []string{} iterator := func(i btree.Item) bool { keys = append(keys, i.(btreeString).s) return len(keys) < n } i.BTree.AscendGreaterOrEqual(btreeFrom, iterator) if skipFirst && len(keys) > 0 { keys = keys[1:] } return keys } // rebuildIndex does the work of regenerating the index // with the given keys. func rebuild(less LessFunction, keys <-chan string) *btree.BTree { tree := btree.New(2) for key := range keys { tree.ReplaceOrInsert(btreeString{s: key, l: less}) } return tree } ================================================ FILE: vendor/github.com/pkg/errors/.gitignore ================================================ # Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof ================================================ FILE: vendor/github.com/pkg/errors/.travis.yml ================================================ language: go go_import_path: github.com/pkg/errors go: - 1.11.x - 1.12.x - 1.13.x - tip script: - make check ================================================ FILE: vendor/github.com/pkg/errors/LICENSE ================================================ Copyright (c) 2015, Dave Cheney All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/pkg/errors/Makefile ================================================ PKGS := github.com/pkg/errors SRCDIRS := $(shell go list -f '{{.Dir}}' $(PKGS)) GO := go check: test vet gofmt misspell unconvert staticcheck ineffassign unparam test: $(GO) test $(PKGS) vet: | test $(GO) vet $(PKGS) staticcheck: $(GO) get honnef.co/go/tools/cmd/staticcheck staticcheck -checks all $(PKGS) misspell: $(GO) get github.com/client9/misspell/cmd/misspell misspell \ -locale GB \ -error \ *.md *.go unconvert: $(GO) get github.com/mdempsky/unconvert unconvert -v $(PKGS) ineffassign: $(GO) get github.com/gordonklaus/ineffassign find $(SRCDIRS) -name '*.go' | xargs ineffassign pedantic: check errcheck unparam: $(GO) get mvdan.cc/unparam unparam ./... errcheck: $(GO) get github.com/kisielk/errcheck errcheck $(PKGS) gofmt: @echo Checking code is gofmted @test -z "$(shell gofmt -s -l -d -e $(SRCDIRS) | tee /dev/stderr)" ================================================ FILE: vendor/github.com/pkg/errors/README.md ================================================ # errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) [![Sourcegraph](https://sourcegraph.com/github.com/pkg/errors/-/badge.svg)](https://sourcegraph.com/github.com/pkg/errors?badge) Package errors provides simple error handling primitives. `go get github.com/pkg/errors` The traditional error handling idiom in Go is roughly akin to ```go if err != nil { return err } ``` which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error. ## Adding context to an error The errors.Wrap function returns a new error that adds context to the original error. For example ```go _, err := ioutil.ReadAll(r) if err != nil { return errors.Wrap(err, "read failed") } ``` ## Retrieving the cause of an error Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`. ```go type causer interface { Cause() error } ``` `errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example: ```go switch err := errors.Cause(err).(type) { case *MyError: // handle specifically default: // unknown error } ``` [Read the package documentation for more information](https://godoc.org/github.com/pkg/errors). ## Roadmap With the upcoming [Go2 error proposals](https://go.googlesource.com/proposal/+/master/design/go2draft.md) this package is moving into maintenance mode. The roadmap for a 1.0 release is as follows: - 0.9. Remove pre Go 1.9 and Go 1.10 support, address outstanding pull requests (if possible) - 1.0. Final release. ## Contributing Because of the Go2 errors changes, this package is not accepting proposals for new functionality. With that said, we welcome pull requests, bug fixes and issue reports. Before sending a PR, please discuss your change by raising an issue. ## License BSD-2-Clause ================================================ FILE: vendor/github.com/pkg/errors/appveyor.yml ================================================ version: build-{build}.{branch} clone_folder: C:\gopath\src\github.com\pkg\errors shallow_clone: true # for startup speed environment: GOPATH: C:\gopath platform: - x64 # http://www.appveyor.com/docs/installed-software install: # some helpful output for debugging builds - go version - go env # pre-installed MinGW at C:\MinGW is 32bit only # but MSYS2 at C:\msys64 has mingw64 - set PATH=C:\msys64\mingw64\bin;%PATH% - gcc --version - g++ --version build_script: - go install -v ./... test_script: - set PATH=C:\gopath\bin;%PATH% - go test -v ./... #artifacts: # - path: '%GOPATH%\bin\*.exe' deploy: off ================================================ FILE: vendor/github.com/pkg/errors/errors.go ================================================ // Package errors provides simple error handling primitives. // // The traditional error handling idiom in Go is roughly akin to // // if err != nil { // return err // } // // which when applied recursively up the call stack results in error reports // without context or debugging information. The errors package allows // programmers to add context to the failure path in their code in a way // that does not destroy the original value of the error. // // Adding context to an error // // The errors.Wrap function returns a new error that adds context to the // original error by recording a stack trace at the point Wrap is called, // together with the supplied message. For example // // _, err := ioutil.ReadAll(r) // if err != nil { // return errors.Wrap(err, "read failed") // } // // If additional control is required, the errors.WithStack and // errors.WithMessage functions destructure errors.Wrap into its component // operations: annotating an error with a stack trace and with a message, // respectively. // // Retrieving the cause of an error // // Using errors.Wrap constructs a stack of errors, adding context to the // preceding error. Depending on the nature of the error it may be necessary // to reverse the operation of errors.Wrap to retrieve the original error // for inspection. Any error value which implements this interface // // type causer interface { // Cause() error // } // // can be inspected by errors.Cause. errors.Cause will recursively retrieve // the topmost error that does not implement causer, which is assumed to be // the original cause. For example: // // switch err := errors.Cause(err).(type) { // case *MyError: // // handle specifically // default: // // unknown error // } // // Although the causer interface is not exported by this package, it is // considered a part of its stable public interface. // // Formatted printing of errors // // All error values returned from this package implement fmt.Formatter and can // be formatted by the fmt package. The following verbs are supported: // // %s print the error. If the error has a Cause it will be // printed recursively. // %v see %s // %+v extended format. Each Frame of the error's StackTrace will // be printed in detail. // // Retrieving the stack trace of an error or wrapper // // New, Errorf, Wrap, and Wrapf record a stack trace at the point they are // invoked. This information can be retrieved with the following interface: // // type stackTracer interface { // StackTrace() errors.StackTrace // } // // The returned errors.StackTrace type is defined as // // type StackTrace []Frame // // The Frame type represents a call site in the stack trace. Frame supports // the fmt.Formatter interface that can be used for printing information about // the stack trace of this error. For example: // // if err, ok := err.(stackTracer); ok { // for _, f := range err.StackTrace() { // fmt.Printf("%+s:%d\n", f, f) // } // } // // Although the stackTracer interface is not exported by this package, it is // considered a part of its stable public interface. // // See the documentation for Frame.Format for more details. package errors import ( "fmt" "io" ) // New returns an error with the supplied message. // New also records the stack trace at the point it was called. func New(message string) error { return &fundamental{ msg: message, stack: callers(), } } // Errorf formats according to a format specifier and returns the string // as a value that satisfies error. // Errorf also records the stack trace at the point it was called. func Errorf(format string, args ...interface{}) error { return &fundamental{ msg: fmt.Sprintf(format, args...), stack: callers(), } } // fundamental is an error that has a message and a stack, but no caller. type fundamental struct { msg string *stack } func (f *fundamental) Error() string { return f.msg } func (f *fundamental) Format(s fmt.State, verb rune) { switch verb { case 'v': if s.Flag('+') { io.WriteString(s, f.msg) f.stack.Format(s, verb) return } fallthrough case 's': io.WriteString(s, f.msg) case 'q': fmt.Fprintf(s, "%q", f.msg) } } // WithStack annotates err with a stack trace at the point WithStack was called. // If err is nil, WithStack returns nil. func WithStack(err error) error { if err == nil { return nil } return &withStack{ err, callers(), } } type withStack struct { error *stack } func (w *withStack) Cause() error { return w.error } // Unwrap provides compatibility for Go 1.13 error chains. func (w *withStack) Unwrap() error { return w.error } func (w *withStack) Format(s fmt.State, verb rune) { switch verb { case 'v': if s.Flag('+') { fmt.Fprintf(s, "%+v", w.Cause()) w.stack.Format(s, verb) return } fallthrough case 's': io.WriteString(s, w.Error()) case 'q': fmt.Fprintf(s, "%q", w.Error()) } } // Wrap returns an error annotating err with a stack trace // at the point Wrap is called, and the supplied message. // If err is nil, Wrap returns nil. func Wrap(err error, message string) error { if err == nil { return nil } err = &withMessage{ cause: err, msg: message, } return &withStack{ err, callers(), } } // Wrapf returns an error annotating err with a stack trace // at the point Wrapf is called, and the format specifier. // If err is nil, Wrapf returns nil. func Wrapf(err error, format string, args ...interface{}) error { if err == nil { return nil } err = &withMessage{ cause: err, msg: fmt.Sprintf(format, args...), } return &withStack{ err, callers(), } } // WithMessage annotates err with a new message. // If err is nil, WithMessage returns nil. func WithMessage(err error, message string) error { if err == nil { return nil } return &withMessage{ cause: err, msg: message, } } // WithMessagef annotates err with the format specifier. // If err is nil, WithMessagef returns nil. func WithMessagef(err error, format string, args ...interface{}) error { if err == nil { return nil } return &withMessage{ cause: err, msg: fmt.Sprintf(format, args...), } } type withMessage struct { cause error msg string } func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } func (w *withMessage) Cause() error { return w.cause } // Unwrap provides compatibility for Go 1.13 error chains. func (w *withMessage) Unwrap() error { return w.cause } func (w *withMessage) Format(s fmt.State, verb rune) { switch verb { case 'v': if s.Flag('+') { fmt.Fprintf(s, "%+v\n", w.Cause()) io.WriteString(s, w.msg) return } fallthrough case 's', 'q': io.WriteString(s, w.Error()) } } // Cause returns the underlying cause of the error, if possible. // An error value has a cause if it implements the following // interface: // // type causer interface { // Cause() error // } // // If the error does not implement Cause, the original error will // be returned. If the error is nil, nil will be returned without further // investigation. func Cause(err error) error { type causer interface { Cause() error } for err != nil { cause, ok := err.(causer) if !ok { break } err = cause.Cause() } return err } ================================================ FILE: vendor/github.com/pkg/errors/go113.go ================================================ // +build go1.13 package errors import ( stderrors "errors" ) // Is reports whether any error in err's chain matches target. // // The chain consists of err itself followed by the sequence of errors obtained by // repeatedly calling Unwrap. // // An error is considered to match a target if it is equal to that target or if // it implements a method Is(error) bool such that Is(target) returns true. func Is(err, target error) bool { return stderrors.Is(err, target) } // As finds the first error in err's chain that matches target, and if so, sets // target to that error value and returns true. // // The chain consists of err itself followed by the sequence of errors obtained by // repeatedly calling Unwrap. // // An error matches target if the error's concrete value is assignable to the value // pointed to by target, or if the error has a method As(interface{}) bool such that // As(target) returns true. In the latter case, the As method is responsible for // setting target. // // As will panic if target is not a non-nil pointer to either a type that implements // error, or to any interface type. As returns false if err is nil. func As(err error, target interface{}) bool { return stderrors.As(err, target) } // Unwrap returns the result of calling the Unwrap method on err, if err's // type contains an Unwrap method returning error. // Otherwise, Unwrap returns nil. func Unwrap(err error) error { return stderrors.Unwrap(err) } ================================================ FILE: vendor/github.com/pkg/errors/stack.go ================================================ package errors import ( "fmt" "io" "path" "runtime" "strconv" "strings" ) // Frame represents a program counter inside a stack frame. // For historical reasons if Frame is interpreted as a uintptr // its value represents the program counter + 1. type Frame uintptr // pc returns the program counter for this frame; // multiple frames may have the same PC value. func (f Frame) pc() uintptr { return uintptr(f) - 1 } // file returns the full path to the file that contains the // function for this Frame's pc. func (f Frame) file() string { fn := runtime.FuncForPC(f.pc()) if fn == nil { return "unknown" } file, _ := fn.FileLine(f.pc()) return file } // line returns the line number of source code of the // function for this Frame's pc. func (f Frame) line() int { fn := runtime.FuncForPC(f.pc()) if fn == nil { return 0 } _, line := fn.FileLine(f.pc()) return line } // name returns the name of this function, if known. func (f Frame) name() string { fn := runtime.FuncForPC(f.pc()) if fn == nil { return "unknown" } return fn.Name() } // Format formats the frame according to the fmt.Formatter interface. // // %s source file // %d source line // %n function name // %v equivalent to %s:%d // // Format accepts flags that alter the printing of some verbs, as follows: // // %+s function name and path of source file relative to the compile time // GOPATH separated by \n\t (\n\t) // %+v equivalent to %+s:%d func (f Frame) Format(s fmt.State, verb rune) { switch verb { case 's': switch { case s.Flag('+'): io.WriteString(s, f.name()) io.WriteString(s, "\n\t") io.WriteString(s, f.file()) default: io.WriteString(s, path.Base(f.file())) } case 'd': io.WriteString(s, strconv.Itoa(f.line())) case 'n': io.WriteString(s, funcname(f.name())) case 'v': f.Format(s, 's') io.WriteString(s, ":") f.Format(s, 'd') } } // MarshalText formats a stacktrace Frame as a text string. The output is the // same as that of fmt.Sprintf("%+v", f), but without newlines or tabs. func (f Frame) MarshalText() ([]byte, error) { name := f.name() if name == "unknown" { return []byte(name), nil } return []byte(fmt.Sprintf("%s %s:%d", name, f.file(), f.line())), nil } // StackTrace is stack of Frames from innermost (newest) to outermost (oldest). type StackTrace []Frame // Format formats the stack of Frames according to the fmt.Formatter interface. // // %s lists source files for each Frame in the stack // %v lists the source file and line number for each Frame in the stack // // Format accepts flags that alter the printing of some verbs, as follows: // // %+v Prints filename, function, and line number for each Frame in the stack. func (st StackTrace) Format(s fmt.State, verb rune) { switch verb { case 'v': switch { case s.Flag('+'): for _, f := range st { io.WriteString(s, "\n") f.Format(s, verb) } case s.Flag('#'): fmt.Fprintf(s, "%#v", []Frame(st)) default: st.formatSlice(s, verb) } case 's': st.formatSlice(s, verb) } } // formatSlice will format this StackTrace into the given buffer as a slice of // Frame, only valid when called with '%s' or '%v'. func (st StackTrace) formatSlice(s fmt.State, verb rune) { io.WriteString(s, "[") for i, f := range st { if i > 0 { io.WriteString(s, " ") } f.Format(s, verb) } io.WriteString(s, "]") } // stack represents a stack of program counters. type stack []uintptr func (s *stack) Format(st fmt.State, verb rune) { switch verb { case 'v': switch { case st.Flag('+'): for _, pc := range *s { f := Frame(pc) fmt.Fprintf(st, "\n%+v", f) } } } } func (s *stack) StackTrace() StackTrace { f := make([]Frame, len(*s)) for i := 0; i < len(f); i++ { f[i] = Frame((*s)[i]) } return f } func callers() *stack { const depth = 32 var pcs [depth]uintptr n := runtime.Callers(3, pcs[:]) var st stack = pcs[0:n] return &st } // funcname removes the path prefix component of a function's name reported by func.Name(). func funcname(name string) string { i := strings.LastIndex(name, "/") name = name[i+1:] i = strings.Index(name, ".") return name[i+1:] } ================================================ FILE: vendor/github.com/pkg/term/LICENSE ================================================ Copyright (c) 2014, David Cheney All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/pkg/term/termios/doc.go ================================================ // Package termios implements the low level termios(3) terminal line discipline facilities. // // For a higher level interface please use the github.com/pkg/term package. package termios ================================================ FILE: vendor/github.com/pkg/term/termios/ioctl.go ================================================ // +build !windows,!solaris package termios import "syscall" func ioctl(fd, request, argp uintptr) error { if _, _, e := syscall.Syscall6(syscall.SYS_IOCTL, fd, request, argp, 0, 0, 0); e != 0 { return e } return nil } ================================================ FILE: vendor/github.com/pkg/term/termios/ioctl_darwin.go ================================================ package termios const ( _IOC_PARAM_SHIFT = 13 _IOC_PARAM_MASK = (1 << _IOC_PARAM_SHIFT) - 1 ) func _IOC_PARM_LEN(ioctl uintptr) uintptr { return (ioctl >> 16) & _IOC_PARAM_MASK } ================================================ FILE: vendor/github.com/pkg/term/termios/ioctl_solaris.go ================================================ package termios import "golang.org/x/sys/unix" func ioctl(fd, request, argp uintptr) error { return unix.IoctlSetInt(int(fd), uint(request), int(argp)) } ================================================ FILE: vendor/github.com/pkg/term/termios/pty.go ================================================ // +build !windows package termios import ( "fmt" "os" "syscall" ) func open_device(path string) (uintptr, error) { fd, err := syscall.Open(path, syscall.O_NOCTTY|syscall.O_RDWR|syscall.O_CLOEXEC, 0666) if err != nil { return 0, fmt.Errorf("unable to open %q: %v", path, err) } return uintptr(fd), nil } // Pty returns a UNIX 98 pseudoterminal device. // Pty returns a pair of fds representing the master and slave pair. func Pty() (*os.File, *os.File, error) { ptm, err := open_pty_master() if err != nil { return nil, nil, err } sname, err := Ptsname(ptm) if err != nil { return nil, nil, err } err = grantpt(ptm) if err != nil { return nil, nil, err } err = unlockpt(ptm) if err != nil { return nil, nil, err } pts, err := open_device(sname) if err != nil { return nil, nil, err } return os.NewFile(uintptr(ptm), "ptm"), os.NewFile(uintptr(pts), sname), nil } ================================================ FILE: vendor/github.com/pkg/term/termios/pty_bsd.go ================================================ // +build dragonfly openbsd package termios // #include import "C" import "syscall" func open_pty_master() (uintptr, error) { rc := C.posix_openpt(syscall.O_NOCTTY | syscall.O_RDWR) if rc < 0 { return 0, syscall.Errno(rc) } return uintptr(rc), nil } func Ptsname(fd uintptr) (string, error) { slavename := C.GoString(C.ptsname(C.int(fd))) return slavename, nil } func grantpt(fd uintptr) error { rc := C.grantpt(C.int(fd)) if rc == 0 { return nil } return syscall.Errno(rc) } func unlockpt(fd uintptr) error { rc := C.unlockpt(C.int(fd)) if rc == 0 { return nil } return syscall.Errno(rc) } ================================================ FILE: vendor/github.com/pkg/term/termios/pty_darwin.go ================================================ package termios import ( "errors" "syscall" "unsafe" ) func open_pty_master() (uintptr, error) { return open_device("/dev/ptmx") } func Ptsname(fd uintptr) (string, error) { n := make([]byte, _IOC_PARM_LEN(syscall.TIOCPTYGNAME)) err := ioctl(fd, syscall.TIOCPTYGNAME, uintptr(unsafe.Pointer(&n[0]))) if err != nil { return "", err } for i, c := range n { if c == 0 { return string(n[:i]), nil } } return "", errors.New("TIOCPTYGNAME string not NUL-terminated") } func grantpt(fd uintptr) error { return ioctl(fd, syscall.TIOCPTYGRANT, 0) } func unlockpt(fd uintptr) error { return ioctl(fd, syscall.TIOCPTYUNLK, 0) } ================================================ FILE: vendor/github.com/pkg/term/termios/pty_freebsd.go ================================================ package termios import ( "fmt" "syscall" "unsafe" ) func posix_openpt(oflag int) (fd uintptr, err error) { // Copied from debian-golang-pty/pty_freebsd.go. r0, _, e1 := syscall.Syscall(syscall.SYS_POSIX_OPENPT, uintptr(oflag), 0, 0) fd = uintptr(r0) if e1 != 0 { err = e1 } return } func open_pty_master() (uintptr, error) { return posix_openpt(syscall.O_NOCTTY|syscall.O_RDWR|syscall.O_CLOEXEC) } func Ptsname(fd uintptr) (string, error) { var n uintptr err := ioctl(fd, syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))) if err != nil { return "", err } return fmt.Sprintf("/dev/pts/%d", n), nil } func grantpt(fd uintptr) error { var n uintptr return ioctl(fd, syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))) } func unlockpt(fd uintptr) error { return nil } ================================================ FILE: vendor/github.com/pkg/term/termios/pty_linux.go ================================================ package termios import ( "fmt" "syscall" "unsafe" ) func open_pty_master() (uintptr, error) { return open_device("/dev/ptmx") } func Ptsname(fd uintptr) (string, error) { var n uintptr err := ioctl(fd, syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))) if err != nil { return "", err } return fmt.Sprintf("/dev/pts/%d", n), nil } func grantpt(fd uintptr) error { var n uintptr return ioctl(fd, syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))) } func unlockpt(fd uintptr) error { var n uintptr return ioctl(fd, syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&n))) } ================================================ FILE: vendor/github.com/pkg/term/termios/pty_netbsd.go ================================================ package termios import ( "bytes" "golang.org/x/sys/unix" ) func open_pty_master() (uintptr, error) { fd, err := unix.Open("/dev/ptmx", unix.O_NOCTTY|unix.O_RDWR, 0666) if err != nil { return 0, err } return uintptr(fd), nil } func Ptsname(fd uintptr) (string, error) { ptm, err := unix.IoctlGetPtmget(int(fd), unix.TIOCPTSNAME) if err != nil { return "", err } return string(ptm.Sn[:bytes.IndexByte(ptm.Sn[:], 0)]), nil } func grantpt(fd uintptr) error { return unix.IoctlSetInt(int(fd), unix.TIOCGRANTPT, 0) } func unlockpt(fd uintptr) error { return nil } ================================================ FILE: vendor/github.com/pkg/term/termios/pty_solaris.go ================================================ package termios // #include import "C" import "syscall" func open_pty_master() (uintptr, error) { return open_device("/dev/ptmx") } func Ptsname(fd uintptr) (string, error) { slavename := C.GoString(C.ptsname(C.int(fd))) return slavename, nil } func grantpt(fd uintptr) error { rc := C.grantpt(C.int(fd)) if rc == 0 { return nil } else { return syscall.Errno(rc) } } func unlockpt(fd uintptr) error { rc := C.unlockpt(C.int(fd)) if rc == 0 { return nil } else { return syscall.Errno(rc) } } ================================================ FILE: vendor/github.com/pkg/term/termios/termios.go ================================================ // +build !windows package termios import ( "syscall" "unsafe" ) // Tiocmget returns the state of the MODEM bits. func Tiocmget(fd uintptr, status *int) error { return ioctl(fd, syscall.TIOCMGET, uintptr(unsafe.Pointer(status))) } // Tiocmset sets the state of the MODEM bits. func Tiocmset(fd uintptr, status *int) error { return ioctl(fd, syscall.TIOCMSET, uintptr(unsafe.Pointer(status))) } // Tiocmbis sets the indicated modem bits. func Tiocmbis(fd uintptr, status *int) error { return ioctl(fd, syscall.TIOCMBIS, uintptr(unsafe.Pointer(status))) } // Tiocmbic clears the indicated modem bits. func Tiocmbic(fd uintptr, status *int) error { return ioctl(fd, syscall.TIOCMBIC, uintptr(unsafe.Pointer(status))) } // Cfmakecbreak modifies attr for cbreak mode. func Cfmakecbreak(attr *syscall.Termios) { attr.Lflag &^= syscall.ECHO | syscall.ICANON attr.Cc[syscall.VMIN] = 1 attr.Cc[syscall.VTIME] = 0 } // Cfmakeraw modifies attr for raw mode. func Cfmakeraw(attr *syscall.Termios) { attr.Iflag &^= syscall.BRKINT | syscall.ICRNL | syscall.INPCK | syscall.ISTRIP | syscall.IXON attr.Oflag &^= syscall.OPOST attr.Cflag &^= syscall.CSIZE | syscall.PARENB attr.Cflag |= syscall.CS8 attr.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.IEXTEN | syscall.ISIG attr.Cc[syscall.VMIN] = 1 attr.Cc[syscall.VTIME] = 0 } ================================================ FILE: vendor/github.com/pkg/term/termios/termios_bsd.go ================================================ // +build darwin freebsd openbsd netbsd dragonfly package termios import ( "syscall" "time" "unsafe" ) const ( FREAD = 0x0001 FWRITE = 0x0002 IXON = 0x00000200 IXOFF = 0x00000400 IXANY = 0x00000800 CCTS_OFLOW = 0x00010000 CRTS_IFLOW = 0x00020000 CRTSCTS = CCTS_OFLOW | CRTS_IFLOW ) // Tcgetattr gets the current serial port settings. func Tcgetattr(fd uintptr, argp *syscall.Termios) error { return ioctl(fd, syscall.TIOCGETA, uintptr(unsafe.Pointer(argp))) } // Tcsetattr sets the current serial port settings. func Tcsetattr(fd, opt uintptr, argp *syscall.Termios) error { switch opt { case TCSANOW: opt = syscall.TIOCSETA case TCSADRAIN: opt = syscall.TIOCSETAW case TCSAFLUSH: opt = syscall.TIOCSETAF default: return syscall.EINVAL } return ioctl(fd, opt, uintptr(unsafe.Pointer(argp))) } // Tcsendbreak function transmits a continuous stream of zero-valued bits for // four-tenths of a second to the terminal referenced by fildes. The duration // parameter is ignored in this implementation. func Tcsendbreak(fd, duration uintptr) error { if err := ioctl(fd, syscall.TIOCSBRK, 0); err != nil { return err } time.Sleep(4 / 10 * time.Second) return ioctl(fd, syscall.TIOCCBRK, 0) } // Tcdrain waits until all output written to the terminal referenced by fd has been transmitted to the terminal. func Tcdrain(fd uintptr) error { return ioctl(fd, syscall.TIOCDRAIN, 0) } // Tcflush discards data written to the object referred to by fd but not transmitted, or data received but not read, depending on the value of which. func Tcflush(fd, which uintptr) error { var com int switch which { case syscall.TCIFLUSH: com = FREAD case syscall.TCOFLUSH: com = FWRITE case syscall.TCIOFLUSH: com = FREAD | FWRITE default: return syscall.EINVAL } return ioctl(fd, syscall.TIOCFLUSH, uintptr(unsafe.Pointer(&com))) } // Cfgetispeed returns the input baud rate stored in the termios structure. func Cfgetispeed(attr *syscall.Termios) uint32 { return uint32(attr.Ispeed) } // Cfgetospeed returns the output baud rate stored in the termios structure. func Cfgetospeed(attr *syscall.Termios) uint32 { return uint32(attr.Ospeed) } // Tiocinq returns the number of bytes in the input buffer. func Tiocinq(fd uintptr, argp *int) error { *argp = 0 return nil } // Tiocoutq return the number of bytes in the output buffer. func Tiocoutq(fd uintptr, argp *int) error { return ioctl(fd, syscall.TIOCOUTQ, uintptr(unsafe.Pointer(argp))) } ================================================ FILE: vendor/github.com/pkg/term/termios/termios_const.go ================================================ // +build !windows,!solaris package termios const ( TCIFLUSH = 0 TCOFLUSH = 1 TCIOFLUSH = 2 TCSANOW = 0 TCSADRAIN = 1 TCSAFLUSH = 2 ) ================================================ FILE: vendor/github.com/pkg/term/termios/termios_const_solaris.go ================================================ package termios // #include import "C" const ( TCIFLUSH = C.TCIFLUSH TCOFLUSH = C.TCOFLUSH TCIOFLUSH = C.TCIOFLUSH TCSANOW = C.TCSANOW TCSADRAIN = C.TCSADRAIN TCSAFLUSH = C.TCSAFLUSH ) ================================================ FILE: vendor/github.com/pkg/term/termios/termios_linux.go ================================================ package termios import ( "syscall" "unsafe" ) const ( TCSETS = 0x5402 TCSETSW = 0x5403 TCSETSF = 0x5404 TCFLSH = 0x540B TCSBRK = 0x5409 TCSBRKP = 0x5425 IXON = 0x00000400 IXANY = 0x00000800 IXOFF = 0x00001000 CRTSCTS = 0x80000000 ) // Tcgetattr gets the current serial port settings. func Tcgetattr(fd uintptr, argp *syscall.Termios) error { return ioctl(fd, syscall.TCGETS, uintptr(unsafe.Pointer(argp))) } // Tcsetattr sets the current serial port settings. func Tcsetattr(fd, action uintptr, argp *syscall.Termios) error { var request uintptr switch action { case TCSANOW: request = TCSETS case TCSADRAIN: request = TCSETSW case TCSAFLUSH: request = TCSETSF default: return syscall.EINVAL } return ioctl(fd, request, uintptr(unsafe.Pointer(argp))) } // Tcsendbreak transmits a continuous stream of zero-valued bits for a specific // duration, if the terminal is using asynchronous serial data transmission. If // duration is zero, it transmits zero-valued bits for at least 0.25 seconds, and not more that 0.5 seconds. // If duration is not zero, it sends zero-valued bits for some // implementation-defined length of time. func Tcsendbreak(fd, duration uintptr) error { return ioctl(fd, TCSBRKP, duration) } // Tcdrain waits until all output written to the object referred to by fd has been transmitted. func Tcdrain(fd uintptr) error { // simulate drain with TCSADRAIN var attr syscall.Termios if err := Tcgetattr(fd, &attr); err != nil { return err } return Tcsetattr(fd, TCSADRAIN, &attr) } // Tcflush discards data written to the object referred to by fd but not transmitted, or data received but not read, depending on the value of selector. func Tcflush(fd, selector uintptr) error { return ioctl(fd, TCFLSH, selector) } // Tiocinq returns the number of bytes in the input buffer. func Tiocinq(fd uintptr, argp *int) error { return ioctl(fd, syscall.TIOCINQ, uintptr(unsafe.Pointer(argp))) } // Tiocoutq return the number of bytes in the output buffer. func Tiocoutq(fd uintptr, argp *int) error { return ioctl(fd, syscall.TIOCOUTQ, uintptr(unsafe.Pointer(argp))) } // Cfgetispeed returns the input baud rate stored in the termios structure. func Cfgetispeed(attr *syscall.Termios) uint32 { return attr.Ispeed } // Cfgetospeed returns the output baud rate stored in the termios structure. func Cfgetospeed(attr *syscall.Termios) uint32 { return attr.Ospeed } ================================================ FILE: vendor/github.com/pkg/term/termios/termios_solaris.go ================================================ package termios // #include // typedef struct termios termios_t; import "C" import ( "syscall" "golang.org/x/sys/unix" "unsafe" ) const ( TCSETS = 0x5402 TCSETSW = 0x5403 TCSETSF = 0x5404 TCFLSH = 0x540B TCSBRK = 0x5409 TCSBRKP = 0x5425 IXON = 0x00000400 IXANY = 0x00000800 IXOFF = 0x00001000 CRTSCTS = 0x80000000 ) // See /usr/include/sys/termios.h const FIORDCHK = C.FIORDCHK // Tcgetattr gets the current serial port settings. func Tcgetattr(fd uintptr, argp *syscall.Termios) error { termios, err := unix.IoctlGetTermios(int(fd), unix.TCGETS) *argp = *(tiosToSyscall(termios)) return err } // Tcsetattr sets the current serial port settings. func Tcsetattr(fd, action uintptr, argp *syscall.Termios) error { return unix.IoctlSetTermios(int(fd), uint(action), tiosToUnix(argp)) } // Tcsendbreak transmits a continuous stream of zero-valued bits for a specific // duration, if the terminal is using asynchronous serial data transmission. If // duration is zero, it transmits zero-valued bits for at least 0.25 seconds, and not more that 0.5 seconds. // If duration is not zero, it sends zero-valued bits for some // implementation-defined length of time. func Tcsendbreak(fd, duration uintptr) error { return ioctl(fd, TCSBRKP, duration) } // Tcdrain waits until all output written to the object referred to by fd has been transmitted. func Tcdrain(fd uintptr) error { // simulate drain with TCSADRAIN var attr syscall.Termios if err := Tcgetattr(fd, &attr); err != nil { return err } return Tcsetattr(fd, TCSADRAIN, &attr) } // Tcflush discards data written to the object referred to by fd but not transmitted, or data received but not read, depending on the value of selector. func Tcflush(fd, selector uintptr) error { return ioctl(fd, TCFLSH, selector) } // Tiocinq returns the number of bytes in the input buffer. func Tiocinq(fd uintptr, argp *int) (err error) { *argp, err = unix.IoctlGetInt(int(fd), FIORDCHK) return err } // Tiocoutq return the number of bytes in the output buffer. func Tiocoutq(fd uintptr, argp *int) error { return ioctl(fd, syscall.TIOCOUTQ, uintptr(unsafe.Pointer(argp))) } // Cfgetispeed returns the input baud rate stored in the termios structure. func Cfgetispeed(attr *syscall.Termios) uint32 { solTermios := tiosToUnix(attr) return uint32(C.cfgetispeed((*C.termios_t)(unsafe.Pointer(solTermios)))) } // Cfsetispeed sets the input baud rate stored in the termios structure. func Cfsetispeed(attr *syscall.Termios, speed uintptr) error { solTermios := tiosToUnix(attr) _, err := C.cfsetispeed((*C.termios_t)(unsafe.Pointer(solTermios)), C.speed_t(speed)) return err } // Cfgetospeed returns the output baud rate stored in the termios structure. func Cfgetospeed(attr *syscall.Termios) uint32 { solTermios := tiosToUnix(attr) return uint32(C.cfgetospeed((*C.termios_t)(unsafe.Pointer(solTermios)))) } // Cfsetospeed sets the output baud rate stored in the termios structure. func Cfsetospeed(attr *syscall.Termios, speed uintptr) error { solTermios := tiosToUnix(attr) _, err := C.cfsetospeed((*C.termios_t)(unsafe.Pointer(solTermios)), C.speed_t(speed)) return err } // tiosToUnix copies a syscall.Termios to a x/sys/unix.Termios. // This is needed since type conversions between the two fail due to // more recent x/sys/unix.Termios renaming the padding field. func tiosToUnix(st *syscall.Termios) *unix.Termios { return &unix.Termios{ Iflag: st.Iflag, Oflag: st.Oflag, Cflag: st.Cflag, Lflag: st.Lflag, Cc: st.Cc, } } // tiosToSyscall copies a x/sys/unix.Termios to a syscall.Termios. func tiosToSyscall(ut *unix.Termios) *syscall.Termios { return &syscall.Termios{ Iflag: ut.Iflag, Oflag: ut.Oflag, Cflag: ut.Cflag, Lflag: ut.Lflag, Cc: ut.Cc, } } ================================================ FILE: vendor/github.com/pkg/term/termios/termios_windows.go ================================================ package termios ================================================ FILE: vendor/github.com/pmezard/go-difflib/LICENSE ================================================ Copyright (c) 2013, Patrick Mezard All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/pmezard/go-difflib/difflib/difflib.go ================================================ // Package difflib is a partial port of Python difflib module. // // It provides tools to compare sequences of strings and generate textual diffs. // // The following class and functions have been ported: // // - SequenceMatcher // // - unified_diff // // - context_diff // // Getting unified diffs was the main goal of the port. Keep in mind this code // is mostly suitable to output text differences in a human friendly way, there // are no guarantees generated diffs are consumable by patch(1). package difflib import ( "bufio" "bytes" "fmt" "io" "strings" ) func min(a, b int) int { if a < b { return a } return b } func max(a, b int) int { if a > b { return a } return b } func calculateRatio(matches, length int) float64 { if length > 0 { return 2.0 * float64(matches) / float64(length) } return 1.0 } type Match struct { A int B int Size int } type OpCode struct { Tag byte I1 int I2 int J1 int J2 int } // SequenceMatcher compares sequence of strings. The basic // algorithm predates, and is a little fancier than, an algorithm // published in the late 1980's by Ratcliff and Obershelp under the // hyperbolic name "gestalt pattern matching". The basic idea is to find // the longest contiguous matching subsequence that contains no "junk" // elements (R-O doesn't address junk). The same idea is then applied // recursively to the pieces of the sequences to the left and to the right // of the matching subsequence. This does not yield minimal edit // sequences, but does tend to yield matches that "look right" to people. // // SequenceMatcher tries to compute a "human-friendly diff" between two // sequences. Unlike e.g. UNIX(tm) diff, the fundamental notion is the // longest *contiguous* & junk-free matching subsequence. That's what // catches peoples' eyes. The Windows(tm) windiff has another interesting // notion, pairing up elements that appear uniquely in each sequence. // That, and the method here, appear to yield more intuitive difference // reports than does diff. This method appears to be the least vulnerable // to synching up on blocks of "junk lines", though (like blank lines in // ordinary text files, or maybe "

" lines in HTML files). That may be // because this is the only method of the 3 that has a *concept* of // "junk" . // // Timing: Basic R-O is cubic time worst case and quadratic time expected // case. SequenceMatcher is quadratic time for the worst case and has // expected-case behavior dependent in a complicated way on how many // elements the sequences have in common; best case time is linear. type SequenceMatcher struct { a []string b []string b2j map[string][]int IsJunk func(string) bool autoJunk bool bJunk map[string]struct{} matchingBlocks []Match fullBCount map[string]int bPopular map[string]struct{} opCodes []OpCode } func NewMatcher(a, b []string) *SequenceMatcher { m := SequenceMatcher{autoJunk: true} m.SetSeqs(a, b) return &m } func NewMatcherWithJunk(a, b []string, autoJunk bool, isJunk func(string) bool) *SequenceMatcher { m := SequenceMatcher{IsJunk: isJunk, autoJunk: autoJunk} m.SetSeqs(a, b) return &m } // Set two sequences to be compared. func (m *SequenceMatcher) SetSeqs(a, b []string) { m.SetSeq1(a) m.SetSeq2(b) } // Set the first sequence to be compared. The second sequence to be compared is // not changed. // // SequenceMatcher computes and caches detailed information about the second // sequence, so if you want to compare one sequence S against many sequences, // use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other // sequences. // // See also SetSeqs() and SetSeq2(). func (m *SequenceMatcher) SetSeq1(a []string) { if &a == &m.a { return } m.a = a m.matchingBlocks = nil m.opCodes = nil } // Set the second sequence to be compared. The first sequence to be compared is // not changed. func (m *SequenceMatcher) SetSeq2(b []string) { if &b == &m.b { return } m.b = b m.matchingBlocks = nil m.opCodes = nil m.fullBCount = nil m.chainB() } func (m *SequenceMatcher) chainB() { // Populate line -> index mapping b2j := map[string][]int{} for i, s := range m.b { indices := b2j[s] indices = append(indices, i) b2j[s] = indices } // Purge junk elements m.bJunk = map[string]struct{}{} if m.IsJunk != nil { junk := m.bJunk for s, _ := range b2j { if m.IsJunk(s) { junk[s] = struct{}{} } } for s, _ := range junk { delete(b2j, s) } } // Purge remaining popular elements popular := map[string]struct{}{} n := len(m.b) if m.autoJunk && n >= 200 { ntest := n/100 + 1 for s, indices := range b2j { if len(indices) > ntest { popular[s] = struct{}{} } } for s, _ := range popular { delete(b2j, s) } } m.bPopular = popular m.b2j = b2j } func (m *SequenceMatcher) isBJunk(s string) bool { _, ok := m.bJunk[s] return ok } // Find longest matching block in a[alo:ahi] and b[blo:bhi]. // // If IsJunk is not defined: // // Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where // alo <= i <= i+k <= ahi // blo <= j <= j+k <= bhi // and for all (i',j',k') meeting those conditions, // k >= k' // i <= i' // and if i == i', j <= j' // // In other words, of all maximal matching blocks, return one that // starts earliest in a, and of all those maximal matching blocks that // start earliest in a, return the one that starts earliest in b. // // If IsJunk is defined, first the longest matching block is // determined as above, but with the additional restriction that no // junk element appears in the block. Then that block is extended as // far as possible by matching (only) junk elements on both sides. So // the resulting block never matches on junk except as identical junk // happens to be adjacent to an "interesting" match. // // If no blocks match, return (alo, blo, 0). func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match { // CAUTION: stripping common prefix or suffix would be incorrect. // E.g., // ab // acab // Longest matching block is "ab", but if common prefix is // stripped, it's "a" (tied with "b"). UNIX(tm) diff does so // strip, so ends up claiming that ab is changed to acab by // inserting "ca" in the middle. That's minimal but unintuitive: // "it's obvious" that someone inserted "ac" at the front. // Windiff ends up at the same place as diff, but by pairing up // the unique 'b's and then matching the first two 'a's. besti, bestj, bestsize := alo, blo, 0 // find longest junk-free match // during an iteration of the loop, j2len[j] = length of longest // junk-free match ending with a[i-1] and b[j] j2len := map[int]int{} for i := alo; i != ahi; i++ { // look at all instances of a[i] in b; note that because // b2j has no junk keys, the loop is skipped if a[i] is junk newj2len := map[int]int{} for _, j := range m.b2j[m.a[i]] { // a[i] matches b[j] if j < blo { continue } if j >= bhi { break } k := j2len[j-1] + 1 newj2len[j] = k if k > bestsize { besti, bestj, bestsize = i-k+1, j-k+1, k } } j2len = newj2len } // Extend the best by non-junk elements on each end. In particular, // "popular" non-junk elements aren't in b2j, which greatly speeds // the inner loop above, but also means "the best" match so far // doesn't contain any junk *or* popular non-junk elements. for besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) && m.a[besti-1] == m.b[bestj-1] { besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 } for besti+bestsize < ahi && bestj+bestsize < bhi && !m.isBJunk(m.b[bestj+bestsize]) && m.a[besti+bestsize] == m.b[bestj+bestsize] { bestsize += 1 } // Now that we have a wholly interesting match (albeit possibly // empty!), we may as well suck up the matching junk on each // side of it too. Can't think of a good reason not to, and it // saves post-processing the (possibly considerable) expense of // figuring out what to do with it. In the case of an empty // interesting match, this is clearly the right thing to do, // because no other kind of match is possible in the regions. for besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) && m.a[besti-1] == m.b[bestj-1] { besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 } for besti+bestsize < ahi && bestj+bestsize < bhi && m.isBJunk(m.b[bestj+bestsize]) && m.a[besti+bestsize] == m.b[bestj+bestsize] { bestsize += 1 } return Match{A: besti, B: bestj, Size: bestsize} } // Return list of triples describing matching subsequences. // // Each triple is of the form (i, j, n), and means that // a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in // i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are // adjacent triples in the list, and the second is not the last triple in the // list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe // adjacent equal blocks. // // The last triple is a dummy, (len(a), len(b), 0), and is the only // triple with n==0. func (m *SequenceMatcher) GetMatchingBlocks() []Match { if m.matchingBlocks != nil { return m.matchingBlocks } var matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match matchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match { match := m.findLongestMatch(alo, ahi, blo, bhi) i, j, k := match.A, match.B, match.Size if match.Size > 0 { if alo < i && blo < j { matched = matchBlocks(alo, i, blo, j, matched) } matched = append(matched, match) if i+k < ahi && j+k < bhi { matched = matchBlocks(i+k, ahi, j+k, bhi, matched) } } return matched } matched := matchBlocks(0, len(m.a), 0, len(m.b), nil) // It's possible that we have adjacent equal blocks in the // matching_blocks list now. nonAdjacent := []Match{} i1, j1, k1 := 0, 0, 0 for _, b := range matched { // Is this block adjacent to i1, j1, k1? i2, j2, k2 := b.A, b.B, b.Size if i1+k1 == i2 && j1+k1 == j2 { // Yes, so collapse them -- this just increases the length of // the first block by the length of the second, and the first // block so lengthened remains the block to compare against. k1 += k2 } else { // Not adjacent. Remember the first block (k1==0 means it's // the dummy we started with), and make the second block the // new block to compare against. if k1 > 0 { nonAdjacent = append(nonAdjacent, Match{i1, j1, k1}) } i1, j1, k1 = i2, j2, k2 } } if k1 > 0 { nonAdjacent = append(nonAdjacent, Match{i1, j1, k1}) } nonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0}) m.matchingBlocks = nonAdjacent return m.matchingBlocks } // Return list of 5-tuples describing how to turn a into b. // // Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple // has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the // tuple preceding it, and likewise for j1 == the previous j2. // // The tags are characters, with these meanings: // // 'r' (replace): a[i1:i2] should be replaced by b[j1:j2] // // 'd' (delete): a[i1:i2] should be deleted, j1==j2 in this case. // // 'i' (insert): b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case. // // 'e' (equal): a[i1:i2] == b[j1:j2] func (m *SequenceMatcher) GetOpCodes() []OpCode { if m.opCodes != nil { return m.opCodes } i, j := 0, 0 matching := m.GetMatchingBlocks() opCodes := make([]OpCode, 0, len(matching)) for _, m := range matching { // invariant: we've pumped out correct diffs to change // a[:i] into b[:j], and the next matching block is // a[ai:ai+size] == b[bj:bj+size]. So we need to pump // out a diff to change a[i:ai] into b[j:bj], pump out // the matching block, and move (i,j) beyond the match ai, bj, size := m.A, m.B, m.Size tag := byte(0) if i < ai && j < bj { tag = 'r' } else if i < ai { tag = 'd' } else if j < bj { tag = 'i' } if tag > 0 { opCodes = append(opCodes, OpCode{tag, i, ai, j, bj}) } i, j = ai+size, bj+size // the list of matching blocks is terminated by a // sentinel with size 0 if size > 0 { opCodes = append(opCodes, OpCode{'e', ai, i, bj, j}) } } m.opCodes = opCodes return m.opCodes } // Isolate change clusters by eliminating ranges with no changes. // // Return a generator of groups with up to n lines of context. // Each group is in the same format as returned by GetOpCodes(). func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode { if n < 0 { n = 3 } codes := m.GetOpCodes() if len(codes) == 0 { codes = []OpCode{OpCode{'e', 0, 1, 0, 1}} } // Fixup leading and trailing groups if they show no changes. if codes[0].Tag == 'e' { c := codes[0] i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2} } if codes[len(codes)-1].Tag == 'e' { c := codes[len(codes)-1] i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)} } nn := n + n groups := [][]OpCode{} group := []OpCode{} for _, c := range codes { i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 // End the current group and start a new one whenever // there is a large range with no changes. if c.Tag == 'e' && i2-i1 > nn { group = append(group, OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)}) groups = append(groups, group) group = []OpCode{} i1, j1 = max(i1, i2-n), max(j1, j2-n) } group = append(group, OpCode{c.Tag, i1, i2, j1, j2}) } if len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') { groups = append(groups, group) } return groups } // Return a measure of the sequences' similarity (float in [0,1]). // // Where T is the total number of elements in both sequences, and // M is the number of matches, this is 2.0*M / T. // Note that this is 1 if the sequences are identical, and 0 if // they have nothing in common. // // .Ratio() is expensive to compute if you haven't already computed // .GetMatchingBlocks() or .GetOpCodes(), in which case you may // want to try .QuickRatio() or .RealQuickRation() first to get an // upper bound. func (m *SequenceMatcher) Ratio() float64 { matches := 0 for _, m := range m.GetMatchingBlocks() { matches += m.Size } return calculateRatio(matches, len(m.a)+len(m.b)) } // Return an upper bound on ratio() relatively quickly. // // This isn't defined beyond that it is an upper bound on .Ratio(), and // is faster to compute. func (m *SequenceMatcher) QuickRatio() float64 { // viewing a and b as multisets, set matches to the cardinality // of their intersection; this counts the number of matches // without regard to order, so is clearly an upper bound if m.fullBCount == nil { m.fullBCount = map[string]int{} for _, s := range m.b { m.fullBCount[s] = m.fullBCount[s] + 1 } } // avail[x] is the number of times x appears in 'b' less the // number of times we've seen it in 'a' so far ... kinda avail := map[string]int{} matches := 0 for _, s := range m.a { n, ok := avail[s] if !ok { n = m.fullBCount[s] } avail[s] = n - 1 if n > 0 { matches += 1 } } return calculateRatio(matches, len(m.a)+len(m.b)) } // Return an upper bound on ratio() very quickly. // // This isn't defined beyond that it is an upper bound on .Ratio(), and // is faster to compute than either .Ratio() or .QuickRatio(). func (m *SequenceMatcher) RealQuickRatio() float64 { la, lb := len(m.a), len(m.b) return calculateRatio(min(la, lb), la+lb) } // Convert range to the "ed" format func formatRangeUnified(start, stop int) string { // Per the diff spec at http://www.unix.org/single_unix_specification/ beginning := start + 1 // lines start numbering with one length := stop - start if length == 1 { return fmt.Sprintf("%d", beginning) } if length == 0 { beginning -= 1 // empty ranges begin at line just before the range } return fmt.Sprintf("%d,%d", beginning, length) } // Unified diff parameters type UnifiedDiff struct { A []string // First sequence lines FromFile string // First file name FromDate string // First file time B []string // Second sequence lines ToFile string // Second file name ToDate string // Second file time Eol string // Headers end of line, defaults to LF Context int // Number of context lines } // Compare two sequences of lines; generate the delta as a unified diff. // // Unified diffs are a compact way of showing line changes and a few // lines of context. The number of context lines is set by 'n' which // defaults to three. // // By default, the diff control lines (those with ---, +++, or @@) are // created with a trailing newline. This is helpful so that inputs // created from file.readlines() result in diffs that are suitable for // file.writelines() since both the inputs and outputs have trailing // newlines. // // For inputs that do not have trailing newlines, set the lineterm // argument to "" so that the output will be uniformly newline free. // // The unidiff format normally has a header for filenames and modification // times. Any or all of these may be specified using strings for // 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'. // The modification times are normally expressed in the ISO 8601 format. func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error { buf := bufio.NewWriter(writer) defer buf.Flush() wf := func(format string, args ...interface{}) error { _, err := buf.WriteString(fmt.Sprintf(format, args...)) return err } ws := func(s string) error { _, err := buf.WriteString(s) return err } if len(diff.Eol) == 0 { diff.Eol = "\n" } started := false m := NewMatcher(diff.A, diff.B) for _, g := range m.GetGroupedOpCodes(diff.Context) { if !started { started = true fromDate := "" if len(diff.FromDate) > 0 { fromDate = "\t" + diff.FromDate } toDate := "" if len(diff.ToDate) > 0 { toDate = "\t" + diff.ToDate } if diff.FromFile != "" || diff.ToFile != "" { err := wf("--- %s%s%s", diff.FromFile, fromDate, diff.Eol) if err != nil { return err } err = wf("+++ %s%s%s", diff.ToFile, toDate, diff.Eol) if err != nil { return err } } } first, last := g[0], g[len(g)-1] range1 := formatRangeUnified(first.I1, last.I2) range2 := formatRangeUnified(first.J1, last.J2) if err := wf("@@ -%s +%s @@%s", range1, range2, diff.Eol); err != nil { return err } for _, c := range g { i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 if c.Tag == 'e' { for _, line := range diff.A[i1:i2] { if err := ws(" " + line); err != nil { return err } } continue } if c.Tag == 'r' || c.Tag == 'd' { for _, line := range diff.A[i1:i2] { if err := ws("-" + line); err != nil { return err } } } if c.Tag == 'r' || c.Tag == 'i' { for _, line := range diff.B[j1:j2] { if err := ws("+" + line); err != nil { return err } } } } } return nil } // Like WriteUnifiedDiff but returns the diff a string. func GetUnifiedDiffString(diff UnifiedDiff) (string, error) { w := &bytes.Buffer{} err := WriteUnifiedDiff(w, diff) return string(w.Bytes()), err } // Convert range to the "ed" format. func formatRangeContext(start, stop int) string { // Per the diff spec at http://www.unix.org/single_unix_specification/ beginning := start + 1 // lines start numbering with one length := stop - start if length == 0 { beginning -= 1 // empty ranges begin at line just before the range } if length <= 1 { return fmt.Sprintf("%d", beginning) } return fmt.Sprintf("%d,%d", beginning, beginning+length-1) } type ContextDiff UnifiedDiff // Compare two sequences of lines; generate the delta as a context diff. // // Context diffs are a compact way of showing line changes and a few // lines of context. The number of context lines is set by diff.Context // which defaults to three. // // By default, the diff control lines (those with *** or ---) are // created with a trailing newline. // // For inputs that do not have trailing newlines, set the diff.Eol // argument to "" so that the output will be uniformly newline free. // // The context diff format normally has a header for filenames and // modification times. Any or all of these may be specified using // strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate. // The modification times are normally expressed in the ISO 8601 format. // If not specified, the strings default to blanks. func WriteContextDiff(writer io.Writer, diff ContextDiff) error { buf := bufio.NewWriter(writer) defer buf.Flush() var diffErr error wf := func(format string, args ...interface{}) { _, err := buf.WriteString(fmt.Sprintf(format, args...)) if diffErr == nil && err != nil { diffErr = err } } ws := func(s string) { _, err := buf.WriteString(s) if diffErr == nil && err != nil { diffErr = err } } if len(diff.Eol) == 0 { diff.Eol = "\n" } prefix := map[byte]string{ 'i': "+ ", 'd': "- ", 'r': "! ", 'e': " ", } started := false m := NewMatcher(diff.A, diff.B) for _, g := range m.GetGroupedOpCodes(diff.Context) { if !started { started = true fromDate := "" if len(diff.FromDate) > 0 { fromDate = "\t" + diff.FromDate } toDate := "" if len(diff.ToDate) > 0 { toDate = "\t" + diff.ToDate } if diff.FromFile != "" || diff.ToFile != "" { wf("*** %s%s%s", diff.FromFile, fromDate, diff.Eol) wf("--- %s%s%s", diff.ToFile, toDate, diff.Eol) } } first, last := g[0], g[len(g)-1] ws("***************" + diff.Eol) range1 := formatRangeContext(first.I1, last.I2) wf("*** %s ****%s", range1, diff.Eol) for _, c := range g { if c.Tag == 'r' || c.Tag == 'd' { for _, cc := range g { if cc.Tag == 'i' { continue } for _, line := range diff.A[cc.I1:cc.I2] { ws(prefix[cc.Tag] + line) } } break } } range2 := formatRangeContext(first.J1, last.J2) wf("--- %s ----%s", range2, diff.Eol) for _, c := range g { if c.Tag == 'r' || c.Tag == 'i' { for _, cc := range g { if cc.Tag == 'd' { continue } for _, line := range diff.B[cc.J1:cc.J2] { ws(prefix[cc.Tag] + line) } } break } } } return diffErr } // Like WriteContextDiff but returns the diff a string. func GetContextDiffString(diff ContextDiff) (string, error) { w := &bytes.Buffer{} err := WriteContextDiff(w, diff) return string(w.Bytes()), err } // Split a string on "\n" while preserving them. The output can be used // as input for UnifiedDiff and ContextDiff structures. func SplitLines(s string) []string { lines := strings.SplitAfter(s, "\n") lines[len(lines)-1] += "\n" return lines } ================================================ FILE: vendor/github.com/rivo/uniseg/LICENSE.txt ================================================ MIT License Copyright (c) 2019 Oliver Kuederle Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/rivo/uniseg/README.md ================================================ # Unicode Text Segmentation for Go [![Godoc Reference](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/rivo/uniseg) [![Go Report](https://img.shields.io/badge/go%20report-A%2B-brightgreen.svg)](https://goreportcard.com/report/github.com/rivo/uniseg) This Go package implements Unicode Text Segmentation according to [Unicode Standard Annex #29](http://unicode.org/reports/tr29/) (Unicode version 12.0.0). At this point, only the determination of grapheme cluster boundaries is implemented. ## Background In Go, [strings are read-only slices of bytes](https://blog.golang.org/strings). They can be turned into Unicode code points using the `for` loop or by casting: `[]rune(str)`. However, multiple code points may be combined into one user-perceived character or what the Unicode specification calls "grapheme cluster". Here are some examples: |String|Bytes (UTF-8)|Code points (runes)|Grapheme clusters| |-|-|-|-| |Käse|6 bytes: `4b 61 cc 88 73 65`|5 code points: `4b 61 308 73 65`|4 clusters: `[4b],[61 308],[73],[65]`| |🏳️‍🌈|14 bytes: `f0 9f 8f b3 ef b8 8f e2 80 8d f0 9f 8c 88`|4 code points: `1f3f3 fe0f 200d 1f308`|1 cluster: `[1f3f3 fe0f 200d 1f308]`| |🇩🇪|8 bytes: `f0 9f 87 a9 f0 9f 87 aa`|2 code points: `1f1e9 1f1ea`|1 cluster: `[1f1e9 1f1ea]`| This package provides a tool to iterate over these grapheme clusters. This may be used to determine the number of user-perceived characters, to split strings in their intended places, or to extract individual characters which form a unit. ## Installation ```bash go get github.com/rivo/uniseg ``` ## Basic Example ```go package uniseg import ( "fmt" "github.com/rivo/uniseg" ) func main() { gr := uniseg.NewGraphemes("👍🏼!") for gr.Next() { fmt.Printf("%x ", gr.Runes()) } // Output: [1f44d 1f3fc] [21] } ``` ## Documentation Refer to https://godoc.org/github.com/rivo/uniseg for the package's documentation. ## Dependencies This package does not depend on any packages outside the standard library. ## Your Feedback Add your issue here on GitHub. Feel free to get in touch if you have any questions. ## Version Version tags will be introduced once Golang modules are official. Consider this version 0.1. ================================================ FILE: vendor/github.com/rivo/uniseg/doc.go ================================================ /* Package uniseg implements Unicode Text Segmentation according to Unicode Standard Annex #29 (http://unicode.org/reports/tr29/). At this point, only the determination of grapheme cluster boundaries is implemented. */ package uniseg ================================================ FILE: vendor/github.com/rivo/uniseg/grapheme.go ================================================ package uniseg import "unicode/utf8" // The states of the grapheme cluster parser. const ( grAny = iota grCR grControlLF grL grLVV grLVTT grPrepend grExtendedPictographic grExtendedPictographicZWJ grRIOdd grRIEven ) // The grapheme cluster parser's breaking instructions. const ( grNoBoundary = iota grBoundary ) // The grapheme cluster parser's state transitions. Maps (state, property) to // (new state, breaking instruction, rule number). The breaking instruction // always refers to the boundary between the last and next code point. // // This map is queried as follows: // // 1. Find specific state + specific property. Stop if found. // 2. Find specific state + any property. // 3. Find any state + specific property. // 4. If only (2) or (3) (but not both) was found, stop. // 5. If both (2) and (3) were found, use state and breaking instruction from // the transition with the lower rule number, prefer (3) if rule numbers // are equal. Stop. // 6. Assume grAny and grBoundary. var grTransitions = map[[2]int][3]int{ // GB5 {grAny, prCR}: {grCR, grBoundary, 50}, {grAny, prLF}: {grControlLF, grBoundary, 50}, {grAny, prControl}: {grControlLF, grBoundary, 50}, // GB4 {grCR, prAny}: {grAny, grBoundary, 40}, {grControlLF, prAny}: {grAny, grBoundary, 40}, // GB3. {grCR, prLF}: {grAny, grNoBoundary, 30}, // GB6. {grAny, prL}: {grL, grBoundary, 9990}, {grL, prL}: {grL, grNoBoundary, 60}, {grL, prV}: {grLVV, grNoBoundary, 60}, {grL, prLV}: {grLVV, grNoBoundary, 60}, {grL, prLVT}: {grLVTT, grNoBoundary, 60}, // GB7. {grAny, prLV}: {grLVV, grBoundary, 9990}, {grAny, prV}: {grLVV, grBoundary, 9990}, {grLVV, prV}: {grLVV, grNoBoundary, 70}, {grLVV, prT}: {grLVTT, grNoBoundary, 70}, // GB8. {grAny, prLVT}: {grLVTT, grBoundary, 9990}, {grAny, prT}: {grLVTT, grBoundary, 9990}, {grLVTT, prT}: {grLVTT, grNoBoundary, 80}, // GB9. {grAny, prExtend}: {grAny, grNoBoundary, 90}, {grAny, prZWJ}: {grAny, grNoBoundary, 90}, // GB9a. {grAny, prSpacingMark}: {grAny, grNoBoundary, 91}, // GB9b. {grAny, prPreprend}: {grPrepend, grBoundary, 9990}, {grPrepend, prAny}: {grAny, grNoBoundary, 92}, // GB11. {grAny, prExtendedPictographic}: {grExtendedPictographic, grBoundary, 9990}, {grExtendedPictographic, prExtend}: {grExtendedPictographic, grNoBoundary, 110}, {grExtendedPictographic, prZWJ}: {grExtendedPictographicZWJ, grNoBoundary, 110}, {grExtendedPictographicZWJ, prExtendedPictographic}: {grExtendedPictographic, grNoBoundary, 110}, // GB12 / GB13. {grAny, prRegionalIndicator}: {grRIOdd, grBoundary, 9990}, {grRIOdd, prRegionalIndicator}: {grRIEven, grNoBoundary, 120}, {grRIEven, prRegionalIndicator}: {grRIOdd, grBoundary, 120}, } // Graphemes implements an iterator over Unicode extended grapheme clusters, // specified in the Unicode Standard Annex #29. Grapheme clusters correspond to // "user-perceived characters". These characters often consist of multiple // code points (e.g. the "woman kissing woman" emoji consists of 8 code points: // woman + ZWJ + heavy black heart (2 code points) + ZWJ + kiss mark + ZWJ + // woman) and the rules described in Annex #29 must be applied to group those // code points into clusters perceived by the user as one character. type Graphemes struct { // The code points over which this class iterates. codePoints []rune // The (byte-based) indices of the code points into the original string plus // len(original string). Thus, len(indices) = len(codePoints) + 1. indices []int // The current grapheme cluster to be returned. These are indices into // codePoints/indices. If start == end, we either haven't started iterating // yet (0) or the iteration has already completed (1). start, end int // The index of the next code point to be parsed. pos int // The current state of the code point parser. state int } // NewGraphemes returns a new grapheme cluster iterator. func NewGraphemes(s string) *Graphemes { l := utf8.RuneCountInString(s) codePoints := make([]rune, l) indices := make([]int, l+1) i := 0 for pos, r := range s { codePoints[i] = r indices[i] = pos i++ } indices[l] = len(s) g := &Graphemes{ codePoints: codePoints, indices: indices, } g.Next() // Parse ahead. return g } // Next advances the iterator by one grapheme cluster and returns false if no // clusters are left. This function must be called before the first cluster is // accessed. func (g *Graphemes) Next() bool { g.start = g.end // The state transition gives us a boundary instruction BEFORE the next code // point so we always need to stay ahead by one code point. // Parse the next code point. for g.pos <= len(g.codePoints) { // GB2. if g.pos == len(g.codePoints) { g.end = g.pos g.pos++ break } // Determine the property of the next character. nextProperty := property(g.codePoints[g.pos]) g.pos++ // Find the applicable transition. var boundary bool transition, ok := grTransitions[[2]int{g.state, nextProperty}] if ok { // We have a specific transition. We'll use it. g.state = transition[0] boundary = transition[1] == grBoundary } else { // No specific transition found. Try the less specific ones. transAnyProp, okAnyProp := grTransitions[[2]int{g.state, prAny}] transAnyState, okAnyState := grTransitions[[2]int{grAny, nextProperty}] if okAnyProp && okAnyState { // Both apply. We'll use a mix (see comments for grTransitions). g.state = transAnyState[0] boundary = transAnyState[1] == grBoundary if transAnyProp[2] < transAnyState[2] { g.state = transAnyProp[0] boundary = transAnyProp[1] == grBoundary } } else if okAnyProp { // We only have a specific state. g.state = transAnyProp[0] boundary = transAnyProp[1] == grBoundary // This branch will probably never be reached because okAnyState will // always be true given the current transition map. But we keep it here // for future modifications to the transition map where this may not be // true anymore. } else if okAnyState { // We only have a specific property. g.state = transAnyState[0] boundary = transAnyState[1] == grBoundary } else { // No known transition. GB999: Any x Any. g.state = grAny boundary = true } } // If we found a cluster boundary, let's stop here. The current cluster will // be the one that just ended. if g.pos-1 == 0 /* GB1 */ || boundary { g.end = g.pos - 1 break } } return g.start != g.end } // Runes returns a slice of runes (code points) which corresponds to the current // grapheme cluster. If the iterator is already past the end or Next() has not // yet been called, nil is returned. func (g *Graphemes) Runes() []rune { if g.start == g.end { return nil } return g.codePoints[g.start:g.end] } // Str returns a substring of the original string which corresponds to the // current grapheme cluster. If the iterator is already past the end or Next() // has not yet been called, an empty string is returned. func (g *Graphemes) Str() string { if g.start == g.end { return "" } return string(g.codePoints[g.start:g.end]) } // Bytes returns a byte slice which corresponds to the current grapheme cluster. // If the iterator is already past the end or Next() has not yet been called, // nil is returned. func (g *Graphemes) Bytes() []byte { if g.start == g.end { return nil } return []byte(string(g.codePoints[g.start:g.end])) } // Positions returns the interval of the current grapheme cluster as byte // positions into the original string. The first returned value "from" indexes // the first byte and the second returned value "to" indexes the first byte that // is not included anymore, i.e. str[from:to] is the current grapheme cluster of // the original string "str". If Next() has not yet been called, both values are // 0. If the iterator is already past the end, both values are 1. func (g *Graphemes) Positions() (int, int) { return g.indices[g.start], g.indices[g.end] } // Reset puts the iterator into its initial state such that the next call to // Next() sets it to the first grapheme cluster again. func (g *Graphemes) Reset() { g.start, g.end, g.pos, g.state = 0, 0, 0, grAny g.Next() // Parse ahead again. } // GraphemeClusterCount returns the number of user-perceived characters // (grapheme clusters) for the given string. To calculate this number, it // iterates through the string using the Graphemes iterator. func GraphemeClusterCount(s string) (n int) { g := NewGraphemes(s) for g.Next() { n++ } return } ================================================ FILE: vendor/github.com/rivo/uniseg/properties.go ================================================ package uniseg // The unicode properties. Only the ones needed in the context of this package // are included. const ( prAny = iota prPreprend prCR prLF prControl prExtend prRegionalIndicator prSpacingMark prL prV prT prLV prLVT prZWJ prExtendedPictographic ) // Maps code point ranges to their properties. In the context of this package, // any code point that is not contained may map to "prAny". The code point // ranges in this slice are numerically sorted. // // These ranges were taken from // http://www.unicode.org/Public/UCD/latest/ucd/auxiliary/GraphemeBreakProperty.txt // as well as // https://unicode.org/Public/emoji/latest/emoji-data.txt // ("Extended_Pictographic" only) on March 11, 2019. See // https://www.unicode.org/license.html for the Unicode license agreement. var codePoints = [][3]int{ {0x0000, 0x0009, prControl}, // Cc [10] .. {0x000A, 0x000A, prLF}, // Cc {0x000B, 0x000C, prControl}, // Cc [2] .. {0x000D, 0x000D, prCR}, // Cc {0x000E, 0x001F, prControl}, // Cc [18] .. {0x007F, 0x009F, prControl}, // Cc [33] .. {0x00A9, 0x00A9, prExtendedPictographic}, // 1.1 [1] (©️) copyright {0x00AD, 0x00AD, prControl}, // Cf SOFT HYPHEN {0x00AE, 0x00AE, prExtendedPictographic}, // 1.1 [1] (®️) registered {0x0300, 0x036F, prExtend}, // Mn [112] COMBINING GRAVE ACCENT..COMBINING LATIN SMALL LETTER X {0x0483, 0x0487, prExtend}, // Mn [5] COMBINING CYRILLIC TITLO..COMBINING CYRILLIC POKRYTIE {0x0488, 0x0489, prExtend}, // Me [2] COMBINING CYRILLIC HUNDRED THOUSANDS SIGN..COMBINING CYRILLIC MILLIONS SIGN {0x0591, 0x05BD, prExtend}, // Mn [45] HEBREW ACCENT ETNAHTA..HEBREW POINT METEG {0x05BF, 0x05BF, prExtend}, // Mn HEBREW POINT RAFE {0x05C1, 0x05C2, prExtend}, // Mn [2] HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT {0x05C4, 0x05C5, prExtend}, // Mn [2] HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT {0x05C7, 0x05C7, prExtend}, // Mn HEBREW POINT QAMATS QATAN {0x0600, 0x0605, prPreprend}, // Cf [6] ARABIC NUMBER SIGN..ARABIC NUMBER MARK ABOVE {0x0610, 0x061A, prExtend}, // Mn [11] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL KASRA {0x061C, 0x061C, prControl}, // Cf ARABIC LETTER MARK {0x064B, 0x065F, prExtend}, // Mn [21] ARABIC FATHATAN..ARABIC WAVY HAMZA BELOW {0x0670, 0x0670, prExtend}, // Mn ARABIC LETTER SUPERSCRIPT ALEF {0x06D6, 0x06DC, prExtend}, // Mn [7] ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA..ARABIC SMALL HIGH SEEN {0x06DD, 0x06DD, prPreprend}, // Cf ARABIC END OF AYAH {0x06DF, 0x06E4, prExtend}, // Mn [6] ARABIC SMALL HIGH ROUNDED ZERO..ARABIC SMALL HIGH MADDA {0x06E7, 0x06E8, prExtend}, // Mn [2] ARABIC SMALL HIGH YEH..ARABIC SMALL HIGH NOON {0x06EA, 0x06ED, prExtend}, // Mn [4] ARABIC EMPTY CENTRE LOW STOP..ARABIC SMALL LOW MEEM {0x070F, 0x070F, prPreprend}, // Cf SYRIAC ABBREVIATION MARK {0x0711, 0x0711, prExtend}, // Mn SYRIAC LETTER SUPERSCRIPT ALAPH {0x0730, 0x074A, prExtend}, // Mn [27] SYRIAC PTHAHA ABOVE..SYRIAC BARREKH {0x07A6, 0x07B0, prExtend}, // Mn [11] THAANA ABAFILI..THAANA SUKUN {0x07EB, 0x07F3, prExtend}, // Mn [9] NKO COMBINING SHORT HIGH TONE..NKO COMBINING DOUBLE DOT ABOVE {0x07FD, 0x07FD, prExtend}, // Mn NKO DANTAYALAN {0x0816, 0x0819, prExtend}, // Mn [4] SAMARITAN MARK IN..SAMARITAN MARK DAGESH {0x081B, 0x0823, prExtend}, // Mn [9] SAMARITAN MARK EPENTHETIC YUT..SAMARITAN VOWEL SIGN A {0x0825, 0x0827, prExtend}, // Mn [3] SAMARITAN VOWEL SIGN SHORT A..SAMARITAN VOWEL SIGN U {0x0829, 0x082D, prExtend}, // Mn [5] SAMARITAN VOWEL SIGN LONG I..SAMARITAN MARK NEQUDAA {0x0859, 0x085B, prExtend}, // Mn [3] MANDAIC AFFRICATION MARK..MANDAIC GEMINATION MARK {0x08D3, 0x08E1, prExtend}, // Mn [15] ARABIC SMALL LOW WAW..ARABIC SMALL HIGH SIGN SAFHA {0x08E2, 0x08E2, prPreprend}, // Cf ARABIC DISPUTED END OF AYAH {0x08E3, 0x0902, prExtend}, // Mn [32] ARABIC TURNED DAMMA BELOW..DEVANAGARI SIGN ANUSVARA {0x0903, 0x0903, prSpacingMark}, // Mc DEVANAGARI SIGN VISARGA {0x093A, 0x093A, prExtend}, // Mn DEVANAGARI VOWEL SIGN OE {0x093B, 0x093B, prSpacingMark}, // Mc DEVANAGARI VOWEL SIGN OOE {0x093C, 0x093C, prExtend}, // Mn DEVANAGARI SIGN NUKTA {0x093E, 0x0940, prSpacingMark}, // Mc [3] DEVANAGARI VOWEL SIGN AA..DEVANAGARI VOWEL SIGN II {0x0941, 0x0948, prExtend}, // Mn [8] DEVANAGARI VOWEL SIGN U..DEVANAGARI VOWEL SIGN AI {0x0949, 0x094C, prSpacingMark}, // Mc [4] DEVANAGARI VOWEL SIGN CANDRA O..DEVANAGARI VOWEL SIGN AU {0x094D, 0x094D, prExtend}, // Mn DEVANAGARI SIGN VIRAMA {0x094E, 0x094F, prSpacingMark}, // Mc [2] DEVANAGARI VOWEL SIGN PRISHTHAMATRA E..DEVANAGARI VOWEL SIGN AW {0x0951, 0x0957, prExtend}, // Mn [7] DEVANAGARI STRESS SIGN UDATTA..DEVANAGARI VOWEL SIGN UUE {0x0962, 0x0963, prExtend}, // Mn [2] DEVANAGARI VOWEL SIGN VOCALIC L..DEVANAGARI VOWEL SIGN VOCALIC LL {0x0981, 0x0981, prExtend}, // Mn BENGALI SIGN CANDRABINDU {0x0982, 0x0983, prSpacingMark}, // Mc [2] BENGALI SIGN ANUSVARA..BENGALI SIGN VISARGA {0x09BC, 0x09BC, prExtend}, // Mn BENGALI SIGN NUKTA {0x09BE, 0x09BE, prExtend}, // Mc BENGALI VOWEL SIGN AA {0x09BF, 0x09C0, prSpacingMark}, // Mc [2] BENGALI VOWEL SIGN I..BENGALI VOWEL SIGN II {0x09C1, 0x09C4, prExtend}, // Mn [4] BENGALI VOWEL SIGN U..BENGALI VOWEL SIGN VOCALIC RR {0x09C7, 0x09C8, prSpacingMark}, // Mc [2] BENGALI VOWEL SIGN E..BENGALI VOWEL SIGN AI {0x09CB, 0x09CC, prSpacingMark}, // Mc [2] BENGALI VOWEL SIGN O..BENGALI VOWEL SIGN AU {0x09CD, 0x09CD, prExtend}, // Mn BENGALI SIGN VIRAMA {0x09D7, 0x09D7, prExtend}, // Mc BENGALI AU LENGTH MARK {0x09E2, 0x09E3, prExtend}, // Mn [2] BENGALI VOWEL SIGN VOCALIC L..BENGALI VOWEL SIGN VOCALIC LL {0x09FE, 0x09FE, prExtend}, // Mn BENGALI SANDHI MARK {0x0A01, 0x0A02, prExtend}, // Mn [2] GURMUKHI SIGN ADAK BINDI..GURMUKHI SIGN BINDI {0x0A03, 0x0A03, prSpacingMark}, // Mc GURMUKHI SIGN VISARGA {0x0A3C, 0x0A3C, prExtend}, // Mn GURMUKHI SIGN NUKTA {0x0A3E, 0x0A40, prSpacingMark}, // Mc [3] GURMUKHI VOWEL SIGN AA..GURMUKHI VOWEL SIGN II {0x0A41, 0x0A42, prExtend}, // Mn [2] GURMUKHI VOWEL SIGN U..GURMUKHI VOWEL SIGN UU {0x0A47, 0x0A48, prExtend}, // Mn [2] GURMUKHI VOWEL SIGN EE..GURMUKHI VOWEL SIGN AI {0x0A4B, 0x0A4D, prExtend}, // Mn [3] GURMUKHI VOWEL SIGN OO..GURMUKHI SIGN VIRAMA {0x0A51, 0x0A51, prExtend}, // Mn GURMUKHI SIGN UDAAT {0x0A70, 0x0A71, prExtend}, // Mn [2] GURMUKHI TIPPI..GURMUKHI ADDAK {0x0A75, 0x0A75, prExtend}, // Mn GURMUKHI SIGN YAKASH {0x0A81, 0x0A82, prExtend}, // Mn [2] GUJARATI SIGN CANDRABINDU..GUJARATI SIGN ANUSVARA {0x0A83, 0x0A83, prSpacingMark}, // Mc GUJARATI SIGN VISARGA {0x0ABC, 0x0ABC, prExtend}, // Mn GUJARATI SIGN NUKTA {0x0ABE, 0x0AC0, prSpacingMark}, // Mc [3] GUJARATI VOWEL SIGN AA..GUJARATI VOWEL SIGN II {0x0AC1, 0x0AC5, prExtend}, // Mn [5] GUJARATI VOWEL SIGN U..GUJARATI VOWEL SIGN CANDRA E {0x0AC7, 0x0AC8, prExtend}, // Mn [2] GUJARATI VOWEL SIGN E..GUJARATI VOWEL SIGN AI {0x0AC9, 0x0AC9, prSpacingMark}, // Mc GUJARATI VOWEL SIGN CANDRA O {0x0ACB, 0x0ACC, prSpacingMark}, // Mc [2] GUJARATI VOWEL SIGN O..GUJARATI VOWEL SIGN AU {0x0ACD, 0x0ACD, prExtend}, // Mn GUJARATI SIGN VIRAMA {0x0AE2, 0x0AE3, prExtend}, // Mn [2] GUJARATI VOWEL SIGN VOCALIC L..GUJARATI VOWEL SIGN VOCALIC LL {0x0AFA, 0x0AFF, prExtend}, // Mn [6] GUJARATI SIGN SUKUN..GUJARATI SIGN TWO-CIRCLE NUKTA ABOVE {0x0B01, 0x0B01, prExtend}, // Mn ORIYA SIGN CANDRABINDU {0x0B02, 0x0B03, prSpacingMark}, // Mc [2] ORIYA SIGN ANUSVARA..ORIYA SIGN VISARGA {0x0B3C, 0x0B3C, prExtend}, // Mn ORIYA SIGN NUKTA {0x0B3E, 0x0B3E, prExtend}, // Mc ORIYA VOWEL SIGN AA {0x0B3F, 0x0B3F, prExtend}, // Mn ORIYA VOWEL SIGN I {0x0B40, 0x0B40, prSpacingMark}, // Mc ORIYA VOWEL SIGN II {0x0B41, 0x0B44, prExtend}, // Mn [4] ORIYA VOWEL SIGN U..ORIYA VOWEL SIGN VOCALIC RR {0x0B47, 0x0B48, prSpacingMark}, // Mc [2] ORIYA VOWEL SIGN E..ORIYA VOWEL SIGN AI {0x0B4B, 0x0B4C, prSpacingMark}, // Mc [2] ORIYA VOWEL SIGN O..ORIYA VOWEL SIGN AU {0x0B4D, 0x0B4D, prExtend}, // Mn ORIYA SIGN VIRAMA {0x0B56, 0x0B56, prExtend}, // Mn ORIYA AI LENGTH MARK {0x0B57, 0x0B57, prExtend}, // Mc ORIYA AU LENGTH MARK {0x0B62, 0x0B63, prExtend}, // Mn [2] ORIYA VOWEL SIGN VOCALIC L..ORIYA VOWEL SIGN VOCALIC LL {0x0B82, 0x0B82, prExtend}, // Mn TAMIL SIGN ANUSVARA {0x0BBE, 0x0BBE, prExtend}, // Mc TAMIL VOWEL SIGN AA {0x0BBF, 0x0BBF, prSpacingMark}, // Mc TAMIL VOWEL SIGN I {0x0BC0, 0x0BC0, prExtend}, // Mn TAMIL VOWEL SIGN II {0x0BC1, 0x0BC2, prSpacingMark}, // Mc [2] TAMIL VOWEL SIGN U..TAMIL VOWEL SIGN UU {0x0BC6, 0x0BC8, prSpacingMark}, // Mc [3] TAMIL VOWEL SIGN E..TAMIL VOWEL SIGN AI {0x0BCA, 0x0BCC, prSpacingMark}, // Mc [3] TAMIL VOWEL SIGN O..TAMIL VOWEL SIGN AU {0x0BCD, 0x0BCD, prExtend}, // Mn TAMIL SIGN VIRAMA {0x0BD7, 0x0BD7, prExtend}, // Mc TAMIL AU LENGTH MARK {0x0C00, 0x0C00, prExtend}, // Mn TELUGU SIGN COMBINING CANDRABINDU ABOVE {0x0C01, 0x0C03, prSpacingMark}, // Mc [3] TELUGU SIGN CANDRABINDU..TELUGU SIGN VISARGA {0x0C04, 0x0C04, prExtend}, // Mn TELUGU SIGN COMBINING ANUSVARA ABOVE {0x0C3E, 0x0C40, prExtend}, // Mn [3] TELUGU VOWEL SIGN AA..TELUGU VOWEL SIGN II {0x0C41, 0x0C44, prSpacingMark}, // Mc [4] TELUGU VOWEL SIGN U..TELUGU VOWEL SIGN VOCALIC RR {0x0C46, 0x0C48, prExtend}, // Mn [3] TELUGU VOWEL SIGN E..TELUGU VOWEL SIGN AI {0x0C4A, 0x0C4D, prExtend}, // Mn [4] TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA {0x0C55, 0x0C56, prExtend}, // Mn [2] TELUGU LENGTH MARK..TELUGU AI LENGTH MARK {0x0C62, 0x0C63, prExtend}, // Mn [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL {0x0C81, 0x0C81, prExtend}, // Mn KANNADA SIGN CANDRABINDU {0x0C82, 0x0C83, prSpacingMark}, // Mc [2] KANNADA SIGN ANUSVARA..KANNADA SIGN VISARGA {0x0CBC, 0x0CBC, prExtend}, // Mn KANNADA SIGN NUKTA {0x0CBE, 0x0CBE, prSpacingMark}, // Mc KANNADA VOWEL SIGN AA {0x0CBF, 0x0CBF, prExtend}, // Mn KANNADA VOWEL SIGN I {0x0CC0, 0x0CC1, prSpacingMark}, // Mc [2] KANNADA VOWEL SIGN II..KANNADA VOWEL SIGN U {0x0CC2, 0x0CC2, prExtend}, // Mc KANNADA VOWEL SIGN UU {0x0CC3, 0x0CC4, prSpacingMark}, // Mc [2] KANNADA VOWEL SIGN VOCALIC R..KANNADA VOWEL SIGN VOCALIC RR {0x0CC6, 0x0CC6, prExtend}, // Mn KANNADA VOWEL SIGN E {0x0CC7, 0x0CC8, prSpacingMark}, // Mc [2] KANNADA VOWEL SIGN EE..KANNADA VOWEL SIGN AI {0x0CCA, 0x0CCB, prSpacingMark}, // Mc [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO {0x0CCC, 0x0CCD, prExtend}, // Mn [2] KANNADA VOWEL SIGN AU..KANNADA SIGN VIRAMA {0x0CD5, 0x0CD6, prExtend}, // Mc [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK {0x0CE2, 0x0CE3, prExtend}, // Mn [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL {0x0D00, 0x0D01, prExtend}, // Mn [2] MALAYALAM SIGN COMBINING ANUSVARA ABOVE..MALAYALAM SIGN CANDRABINDU {0x0D02, 0x0D03, prSpacingMark}, // Mc [2] MALAYALAM SIGN ANUSVARA..MALAYALAM SIGN VISARGA {0x0D3B, 0x0D3C, prExtend}, // Mn [2] MALAYALAM SIGN VERTICAL BAR VIRAMA..MALAYALAM SIGN CIRCULAR VIRAMA {0x0D3E, 0x0D3E, prExtend}, // Mc MALAYALAM VOWEL SIGN AA {0x0D3F, 0x0D40, prSpacingMark}, // Mc [2] MALAYALAM VOWEL SIGN I..MALAYALAM VOWEL SIGN II {0x0D41, 0x0D44, prExtend}, // Mn [4] MALAYALAM VOWEL SIGN U..MALAYALAM VOWEL SIGN VOCALIC RR {0x0D46, 0x0D48, prSpacingMark}, // Mc [3] MALAYALAM VOWEL SIGN E..MALAYALAM VOWEL SIGN AI {0x0D4A, 0x0D4C, prSpacingMark}, // Mc [3] MALAYALAM VOWEL SIGN O..MALAYALAM VOWEL SIGN AU {0x0D4D, 0x0D4D, prExtend}, // Mn MALAYALAM SIGN VIRAMA {0x0D4E, 0x0D4E, prPreprend}, // Lo MALAYALAM LETTER DOT REPH {0x0D57, 0x0D57, prExtend}, // Mc MALAYALAM AU LENGTH MARK {0x0D62, 0x0D63, prExtend}, // Mn [2] MALAYALAM VOWEL SIGN VOCALIC L..MALAYALAM VOWEL SIGN VOCALIC LL {0x0D82, 0x0D83, prSpacingMark}, // Mc [2] SINHALA SIGN ANUSVARAYA..SINHALA SIGN VISARGAYA {0x0DCA, 0x0DCA, prExtend}, // Mn SINHALA SIGN AL-LAKUNA {0x0DCF, 0x0DCF, prExtend}, // Mc SINHALA VOWEL SIGN AELA-PILLA {0x0DD0, 0x0DD1, prSpacingMark}, // Mc [2] SINHALA VOWEL SIGN KETTI AEDA-PILLA..SINHALA VOWEL SIGN DIGA AEDA-PILLA {0x0DD2, 0x0DD4, prExtend}, // Mn [3] SINHALA VOWEL SIGN KETTI IS-PILLA..SINHALA VOWEL SIGN KETTI PAA-PILLA {0x0DD6, 0x0DD6, prExtend}, // Mn SINHALA VOWEL SIGN DIGA PAA-PILLA {0x0DD8, 0x0DDE, prSpacingMark}, // Mc [7] SINHALA VOWEL SIGN GAETTA-PILLA..SINHALA VOWEL SIGN KOMBUVA HAA GAYANUKITTA {0x0DDF, 0x0DDF, prExtend}, // Mc SINHALA VOWEL SIGN GAYANUKITTA {0x0DF2, 0x0DF3, prSpacingMark}, // Mc [2] SINHALA VOWEL SIGN DIGA GAETTA-PILLA..SINHALA VOWEL SIGN DIGA GAYANUKITTA {0x0E31, 0x0E31, prExtend}, // Mn THAI CHARACTER MAI HAN-AKAT {0x0E33, 0x0E33, prSpacingMark}, // Lo THAI CHARACTER SARA AM {0x0E34, 0x0E3A, prExtend}, // Mn [7] THAI CHARACTER SARA I..THAI CHARACTER PHINTHU {0x0E47, 0x0E4E, prExtend}, // Mn [8] THAI CHARACTER MAITAIKHU..THAI CHARACTER YAMAKKAN {0x0EB1, 0x0EB1, prExtend}, // Mn LAO VOWEL SIGN MAI KAN {0x0EB3, 0x0EB3, prSpacingMark}, // Lo LAO VOWEL SIGN AM {0x0EB4, 0x0EBC, prExtend}, // Mn [9] LAO VOWEL SIGN I..LAO SEMIVOWEL SIGN LO {0x0EC8, 0x0ECD, prExtend}, // Mn [6] LAO TONE MAI EK..LAO NIGGAHITA {0x0F18, 0x0F19, prExtend}, // Mn [2] TIBETAN ASTROLOGICAL SIGN -KHYUD PA..TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS {0x0F35, 0x0F35, prExtend}, // Mn TIBETAN MARK NGAS BZUNG NYI ZLA {0x0F37, 0x0F37, prExtend}, // Mn TIBETAN MARK NGAS BZUNG SGOR RTAGS {0x0F39, 0x0F39, prExtend}, // Mn TIBETAN MARK TSA -PHRU {0x0F3E, 0x0F3F, prSpacingMark}, // Mc [2] TIBETAN SIGN YAR TSHES..TIBETAN SIGN MAR TSHES {0x0F71, 0x0F7E, prExtend}, // Mn [14] TIBETAN VOWEL SIGN AA..TIBETAN SIGN RJES SU NGA RO {0x0F7F, 0x0F7F, prSpacingMark}, // Mc TIBETAN SIGN RNAM BCAD {0x0F80, 0x0F84, prExtend}, // Mn [5] TIBETAN VOWEL SIGN REVERSED I..TIBETAN MARK HALANTA {0x0F86, 0x0F87, prExtend}, // Mn [2] TIBETAN SIGN LCI RTAGS..TIBETAN SIGN YANG RTAGS {0x0F8D, 0x0F97, prExtend}, // Mn [11] TIBETAN SUBJOINED SIGN LCE TSA CAN..TIBETAN SUBJOINED LETTER JA {0x0F99, 0x0FBC, prExtend}, // Mn [36] TIBETAN SUBJOINED LETTER NYA..TIBETAN SUBJOINED LETTER FIXED-FORM RA {0x0FC6, 0x0FC6, prExtend}, // Mn TIBETAN SYMBOL PADMA GDAN {0x102D, 0x1030, prExtend}, // Mn [4] MYANMAR VOWEL SIGN I..MYANMAR VOWEL SIGN UU {0x1031, 0x1031, prSpacingMark}, // Mc MYANMAR VOWEL SIGN E {0x1032, 0x1037, prExtend}, // Mn [6] MYANMAR VOWEL SIGN AI..MYANMAR SIGN DOT BELOW {0x1039, 0x103A, prExtend}, // Mn [2] MYANMAR SIGN VIRAMA..MYANMAR SIGN ASAT {0x103B, 0x103C, prSpacingMark}, // Mc [2] MYANMAR CONSONANT SIGN MEDIAL YA..MYANMAR CONSONANT SIGN MEDIAL RA {0x103D, 0x103E, prExtend}, // Mn [2] MYANMAR CONSONANT SIGN MEDIAL WA..MYANMAR CONSONANT SIGN MEDIAL HA {0x1056, 0x1057, prSpacingMark}, // Mc [2] MYANMAR VOWEL SIGN VOCALIC R..MYANMAR VOWEL SIGN VOCALIC RR {0x1058, 0x1059, prExtend}, // Mn [2] MYANMAR VOWEL SIGN VOCALIC L..MYANMAR VOWEL SIGN VOCALIC LL {0x105E, 0x1060, prExtend}, // Mn [3] MYANMAR CONSONANT SIGN MON MEDIAL NA..MYANMAR CONSONANT SIGN MON MEDIAL LA {0x1071, 0x1074, prExtend}, // Mn [4] MYANMAR VOWEL SIGN GEBA KAREN I..MYANMAR VOWEL SIGN KAYAH EE {0x1082, 0x1082, prExtend}, // Mn MYANMAR CONSONANT SIGN SHAN MEDIAL WA {0x1084, 0x1084, prSpacingMark}, // Mc MYANMAR VOWEL SIGN SHAN E {0x1085, 0x1086, prExtend}, // Mn [2] MYANMAR VOWEL SIGN SHAN E ABOVE..MYANMAR VOWEL SIGN SHAN FINAL Y {0x108D, 0x108D, prExtend}, // Mn MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE {0x109D, 0x109D, prExtend}, // Mn MYANMAR VOWEL SIGN AITON AI {0x1100, 0x115F, prL}, // Lo [96] HANGUL CHOSEONG KIYEOK..HANGUL CHOSEONG FILLER {0x1160, 0x11A7, prV}, // Lo [72] HANGUL JUNGSEONG FILLER..HANGUL JUNGSEONG O-YAE {0x11A8, 0x11FF, prT}, // Lo [88] HANGUL JONGSEONG KIYEOK..HANGUL JONGSEONG SSANGNIEUN {0x135D, 0x135F, prExtend}, // Mn [3] ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK..ETHIOPIC COMBINING GEMINATION MARK {0x1712, 0x1714, prExtend}, // Mn [3] TAGALOG VOWEL SIGN I..TAGALOG SIGN VIRAMA {0x1732, 0x1734, prExtend}, // Mn [3] HANUNOO VOWEL SIGN I..HANUNOO SIGN PAMUDPOD {0x1752, 0x1753, prExtend}, // Mn [2] BUHID VOWEL SIGN I..BUHID VOWEL SIGN U {0x1772, 0x1773, prExtend}, // Mn [2] TAGBANWA VOWEL SIGN I..TAGBANWA VOWEL SIGN U {0x17B4, 0x17B5, prExtend}, // Mn [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA {0x17B6, 0x17B6, prSpacingMark}, // Mc KHMER VOWEL SIGN AA {0x17B7, 0x17BD, prExtend}, // Mn [7] KHMER VOWEL SIGN I..KHMER VOWEL SIGN UA {0x17BE, 0x17C5, prSpacingMark}, // Mc [8] KHMER VOWEL SIGN OE..KHMER VOWEL SIGN AU {0x17C6, 0x17C6, prExtend}, // Mn KHMER SIGN NIKAHIT {0x17C7, 0x17C8, prSpacingMark}, // Mc [2] KHMER SIGN REAHMUK..KHMER SIGN YUUKALEAPINTU {0x17C9, 0x17D3, prExtend}, // Mn [11] KHMER SIGN MUUSIKATOAN..KHMER SIGN BATHAMASAT {0x17DD, 0x17DD, prExtend}, // Mn KHMER SIGN ATTHACAN {0x180B, 0x180D, prExtend}, // Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE {0x180E, 0x180E, prControl}, // Cf MONGOLIAN VOWEL SEPARATOR {0x1885, 0x1886, prExtend}, // Mn [2] MONGOLIAN LETTER ALI GALI BALUDA..MONGOLIAN LETTER ALI GALI THREE BALUDA {0x18A9, 0x18A9, prExtend}, // Mn MONGOLIAN LETTER ALI GALI DAGALGA {0x1920, 0x1922, prExtend}, // Mn [3] LIMBU VOWEL SIGN A..LIMBU VOWEL SIGN U {0x1923, 0x1926, prSpacingMark}, // Mc [4] LIMBU VOWEL SIGN EE..LIMBU VOWEL SIGN AU {0x1927, 0x1928, prExtend}, // Mn [2] LIMBU VOWEL SIGN E..LIMBU VOWEL SIGN O {0x1929, 0x192B, prSpacingMark}, // Mc [3] LIMBU SUBJOINED LETTER YA..LIMBU SUBJOINED LETTER WA {0x1930, 0x1931, prSpacingMark}, // Mc [2] LIMBU SMALL LETTER KA..LIMBU SMALL LETTER NGA {0x1932, 0x1932, prExtend}, // Mn LIMBU SMALL LETTER ANUSVARA {0x1933, 0x1938, prSpacingMark}, // Mc [6] LIMBU SMALL LETTER TA..LIMBU SMALL LETTER LA {0x1939, 0x193B, prExtend}, // Mn [3] LIMBU SIGN MUKPHRENG..LIMBU SIGN SA-I {0x1A17, 0x1A18, prExtend}, // Mn [2] BUGINESE VOWEL SIGN I..BUGINESE VOWEL SIGN U {0x1A19, 0x1A1A, prSpacingMark}, // Mc [2] BUGINESE VOWEL SIGN E..BUGINESE VOWEL SIGN O {0x1A1B, 0x1A1B, prExtend}, // Mn BUGINESE VOWEL SIGN AE {0x1A55, 0x1A55, prSpacingMark}, // Mc TAI THAM CONSONANT SIGN MEDIAL RA {0x1A56, 0x1A56, prExtend}, // Mn TAI THAM CONSONANT SIGN MEDIAL LA {0x1A57, 0x1A57, prSpacingMark}, // Mc TAI THAM CONSONANT SIGN LA TANG LAI {0x1A58, 0x1A5E, prExtend}, // Mn [7] TAI THAM SIGN MAI KANG LAI..TAI THAM CONSONANT SIGN SA {0x1A60, 0x1A60, prExtend}, // Mn TAI THAM SIGN SAKOT {0x1A62, 0x1A62, prExtend}, // Mn TAI THAM VOWEL SIGN MAI SAT {0x1A65, 0x1A6C, prExtend}, // Mn [8] TAI THAM VOWEL SIGN I..TAI THAM VOWEL SIGN OA BELOW {0x1A6D, 0x1A72, prSpacingMark}, // Mc [6] TAI THAM VOWEL SIGN OY..TAI THAM VOWEL SIGN THAM AI {0x1A73, 0x1A7C, prExtend}, // Mn [10] TAI THAM VOWEL SIGN OA ABOVE..TAI THAM SIGN KHUEN-LUE KARAN {0x1A7F, 0x1A7F, prExtend}, // Mn TAI THAM COMBINING CRYPTOGRAMMIC DOT {0x1AB0, 0x1ABD, prExtend}, // Mn [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW {0x1ABE, 0x1ABE, prExtend}, // Me COMBINING PARENTHESES OVERLAY {0x1B00, 0x1B03, prExtend}, // Mn [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG {0x1B04, 0x1B04, prSpacingMark}, // Mc BALINESE SIGN BISAH {0x1B34, 0x1B34, prExtend}, // Mn BALINESE SIGN REREKAN {0x1B35, 0x1B35, prExtend}, // Mc BALINESE VOWEL SIGN TEDUNG {0x1B36, 0x1B3A, prExtend}, // Mn [5] BALINESE VOWEL SIGN ULU..BALINESE VOWEL SIGN RA REPA {0x1B3B, 0x1B3B, prSpacingMark}, // Mc BALINESE VOWEL SIGN RA REPA TEDUNG {0x1B3C, 0x1B3C, prExtend}, // Mn BALINESE VOWEL SIGN LA LENGA {0x1B3D, 0x1B41, prSpacingMark}, // Mc [5] BALINESE VOWEL SIGN LA LENGA TEDUNG..BALINESE VOWEL SIGN TALING REPA TEDUNG {0x1B42, 0x1B42, prExtend}, // Mn BALINESE VOWEL SIGN PEPET {0x1B43, 0x1B44, prSpacingMark}, // Mc [2] BALINESE VOWEL SIGN PEPET TEDUNG..BALINESE ADEG ADEG {0x1B6B, 0x1B73, prExtend}, // Mn [9] BALINESE MUSICAL SYMBOL COMBINING TEGEH..BALINESE MUSICAL SYMBOL COMBINING GONG {0x1B80, 0x1B81, prExtend}, // Mn [2] SUNDANESE SIGN PANYECEK..SUNDANESE SIGN PANGLAYAR {0x1B82, 0x1B82, prSpacingMark}, // Mc SUNDANESE SIGN PANGWISAD {0x1BA1, 0x1BA1, prSpacingMark}, // Mc SUNDANESE CONSONANT SIGN PAMINGKAL {0x1BA2, 0x1BA5, prExtend}, // Mn [4] SUNDANESE CONSONANT SIGN PANYAKRA..SUNDANESE VOWEL SIGN PANYUKU {0x1BA6, 0x1BA7, prSpacingMark}, // Mc [2] SUNDANESE VOWEL SIGN PANAELAENG..SUNDANESE VOWEL SIGN PANOLONG {0x1BA8, 0x1BA9, prExtend}, // Mn [2] SUNDANESE VOWEL SIGN PAMEPET..SUNDANESE VOWEL SIGN PANEULEUNG {0x1BAA, 0x1BAA, prSpacingMark}, // Mc SUNDANESE SIGN PAMAAEH {0x1BAB, 0x1BAD, prExtend}, // Mn [3] SUNDANESE SIGN VIRAMA..SUNDANESE CONSONANT SIGN PASANGAN WA {0x1BE6, 0x1BE6, prExtend}, // Mn BATAK SIGN TOMPI {0x1BE7, 0x1BE7, prSpacingMark}, // Mc BATAK VOWEL SIGN E {0x1BE8, 0x1BE9, prExtend}, // Mn [2] BATAK VOWEL SIGN PAKPAK E..BATAK VOWEL SIGN EE {0x1BEA, 0x1BEC, prSpacingMark}, // Mc [3] BATAK VOWEL SIGN I..BATAK VOWEL SIGN O {0x1BED, 0x1BED, prExtend}, // Mn BATAK VOWEL SIGN KARO O {0x1BEE, 0x1BEE, prSpacingMark}, // Mc BATAK VOWEL SIGN U {0x1BEF, 0x1BF1, prExtend}, // Mn [3] BATAK VOWEL SIGN U FOR SIMALUNGUN SA..BATAK CONSONANT SIGN H {0x1BF2, 0x1BF3, prSpacingMark}, // Mc [2] BATAK PANGOLAT..BATAK PANONGONAN {0x1C24, 0x1C2B, prSpacingMark}, // Mc [8] LEPCHA SUBJOINED LETTER YA..LEPCHA VOWEL SIGN UU {0x1C2C, 0x1C33, prExtend}, // Mn [8] LEPCHA VOWEL SIGN E..LEPCHA CONSONANT SIGN T {0x1C34, 0x1C35, prSpacingMark}, // Mc [2] LEPCHA CONSONANT SIGN NYIN-DO..LEPCHA CONSONANT SIGN KANG {0x1C36, 0x1C37, prExtend}, // Mn [2] LEPCHA SIGN RAN..LEPCHA SIGN NUKTA {0x1CD0, 0x1CD2, prExtend}, // Mn [3] VEDIC TONE KARSHANA..VEDIC TONE PRENKHA {0x1CD4, 0x1CE0, prExtend}, // Mn [13] VEDIC SIGN YAJURVEDIC MIDLINE SVARITA..VEDIC TONE RIGVEDIC KASHMIRI INDEPENDENT SVARITA {0x1CE1, 0x1CE1, prSpacingMark}, // Mc VEDIC TONE ATHARVAVEDIC INDEPENDENT SVARITA {0x1CE2, 0x1CE8, prExtend}, // Mn [7] VEDIC SIGN VISARGA SVARITA..VEDIC SIGN VISARGA ANUDATTA WITH TAIL {0x1CED, 0x1CED, prExtend}, // Mn VEDIC SIGN TIRYAK {0x1CF4, 0x1CF4, prExtend}, // Mn VEDIC TONE CANDRA ABOVE {0x1CF7, 0x1CF7, prSpacingMark}, // Mc VEDIC SIGN ATIKRAMA {0x1CF8, 0x1CF9, prExtend}, // Mn [2] VEDIC TONE RING ABOVE..VEDIC TONE DOUBLE RING ABOVE {0x1DC0, 0x1DF9, prExtend}, // Mn [58] COMBINING DOTTED GRAVE ACCENT..COMBINING WIDE INVERTED BRIDGE BELOW {0x1DFB, 0x1DFF, prExtend}, // Mn [5] COMBINING DELETION MARK..COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW {0x200B, 0x200B, prControl}, // Cf ZERO WIDTH SPACE {0x200C, 0x200C, prExtend}, // Cf ZERO WIDTH NON-JOINER {0x200D, 0x200D, prZWJ}, // Cf ZERO WIDTH JOINER {0x200E, 0x200F, prControl}, // Cf [2] LEFT-TO-RIGHT MARK..RIGHT-TO-LEFT MARK {0x2028, 0x2028, prControl}, // Zl LINE SEPARATOR {0x2029, 0x2029, prControl}, // Zp PARAGRAPH SEPARATOR {0x202A, 0x202E, prControl}, // Cf [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE {0x203C, 0x203C, prExtendedPictographic}, // 1.1 [1] (‼️) double exclamation mark {0x2049, 0x2049, prExtendedPictographic}, // 3.0 [1] (⁉️) exclamation question mark {0x2060, 0x2064, prControl}, // Cf [5] WORD JOINER..INVISIBLE PLUS {0x2065, 0x2065, prControl}, // Cn {0x2066, 0x206F, prControl}, // Cf [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES {0x20D0, 0x20DC, prExtend}, // Mn [13] COMBINING LEFT HARPOON ABOVE..COMBINING FOUR DOTS ABOVE {0x20DD, 0x20E0, prExtend}, // Me [4] COMBINING ENCLOSING CIRCLE..COMBINING ENCLOSING CIRCLE BACKSLASH {0x20E1, 0x20E1, prExtend}, // Mn COMBINING LEFT RIGHT ARROW ABOVE {0x20E2, 0x20E4, prExtend}, // Me [3] COMBINING ENCLOSING SCREEN..COMBINING ENCLOSING UPWARD POINTING TRIANGLE {0x20E5, 0x20F0, prExtend}, // Mn [12] COMBINING REVERSE SOLIDUS OVERLAY..COMBINING ASTERISK ABOVE {0x2122, 0x2122, prExtendedPictographic}, // 1.1 [1] (™️) trade mark {0x2139, 0x2139, prExtendedPictographic}, // 3.0 [1] (ℹ️) information {0x2194, 0x2199, prExtendedPictographic}, // 1.1 [6] (↔️..↙️) left-right arrow..down-left arrow {0x21A9, 0x21AA, prExtendedPictographic}, // 1.1 [2] (↩️..↪️) right arrow curving left..left arrow curving right {0x231A, 0x231B, prExtendedPictographic}, // 1.1 [2] (⌚..⌛) watch..hourglass done {0x2328, 0x2328, prExtendedPictographic}, // 1.1 [1] (⌨️) keyboard {0x2388, 0x2388, prExtendedPictographic}, // 3.0 [1] (⎈) HELM SYMBOL {0x23CF, 0x23CF, prExtendedPictographic}, // 4.0 [1] (⏏️) eject button {0x23E9, 0x23F3, prExtendedPictographic}, // 6.0 [11] (⏩..⏳) fast-forward button..hourglass not done {0x23F8, 0x23FA, prExtendedPictographic}, // 7.0 [3] (⏸️..⏺️) pause button..record button {0x24C2, 0x24C2, prExtendedPictographic}, // 1.1 [1] (Ⓜ️) circled M {0x25AA, 0x25AB, prExtendedPictographic}, // 1.1 [2] (▪️..▫️) black small square..white small square {0x25B6, 0x25B6, prExtendedPictographic}, // 1.1 [1] (▶️) play button {0x25C0, 0x25C0, prExtendedPictographic}, // 1.1 [1] (◀️) reverse button {0x25FB, 0x25FE, prExtendedPictographic}, // 3.2 [4] (◻️..◾) white medium square..black medium-small square {0x2600, 0x2605, prExtendedPictographic}, // 1.1 [6] (☀️..★) sun..BLACK STAR {0x2607, 0x2612, prExtendedPictographic}, // 1.1 [12] (☇..☒) LIGHTNING..BALLOT BOX WITH X {0x2614, 0x2615, prExtendedPictographic}, // 4.0 [2] (☔..☕) umbrella with rain drops..hot beverage {0x2616, 0x2617, prExtendedPictographic}, // 3.2 [2] (☖..☗) WHITE SHOGI PIECE..BLACK SHOGI PIECE {0x2618, 0x2618, prExtendedPictographic}, // 4.1 [1] (☘️) shamrock {0x2619, 0x2619, prExtendedPictographic}, // 3.0 [1] (☙) REVERSED ROTATED FLORAL HEART BULLET {0x261A, 0x266F, prExtendedPictographic}, // 1.1 [86] (☚..♯) BLACK LEFT POINTING INDEX..MUSIC SHARP SIGN {0x2670, 0x2671, prExtendedPictographic}, // 3.0 [2] (♰..♱) WEST SYRIAC CROSS..EAST SYRIAC CROSS {0x2672, 0x267D, prExtendedPictographic}, // 3.2 [12] (♲..♽) UNIVERSAL RECYCLING SYMBOL..PARTIALLY-RECYCLED PAPER SYMBOL {0x267E, 0x267F, prExtendedPictographic}, // 4.1 [2] (♾️..♿) infinity..wheelchair symbol {0x2680, 0x2685, prExtendedPictographic}, // 3.2 [6] (⚀..⚅) DIE FACE-1..DIE FACE-6 {0x2690, 0x2691, prExtendedPictographic}, // 4.0 [2] (⚐..⚑) WHITE FLAG..BLACK FLAG {0x2692, 0x269C, prExtendedPictographic}, // 4.1 [11] (⚒️..⚜️) hammer and pick..fleur-de-lis {0x269D, 0x269D, prExtendedPictographic}, // 5.1 [1] (⚝) OUTLINED WHITE STAR {0x269E, 0x269F, prExtendedPictographic}, // 5.2 [2] (⚞..⚟) THREE LINES CONVERGING RIGHT..THREE LINES CONVERGING LEFT {0x26A0, 0x26A1, prExtendedPictographic}, // 4.0 [2] (⚠️..⚡) warning..high voltage {0x26A2, 0x26B1, prExtendedPictographic}, // 4.1 [16] (⚢..⚱️) DOUBLED FEMALE SIGN..funeral urn {0x26B2, 0x26B2, prExtendedPictographic}, // 5.0 [1] (⚲) NEUTER {0x26B3, 0x26BC, prExtendedPictographic}, // 5.1 [10] (⚳..⚼) CERES..SESQUIQUADRATE {0x26BD, 0x26BF, prExtendedPictographic}, // 5.2 [3] (⚽..⚿) soccer ball..SQUARED KEY {0x26C0, 0x26C3, prExtendedPictographic}, // 5.1 [4] (⛀..⛃) WHITE DRAUGHTS MAN..BLACK DRAUGHTS KING {0x26C4, 0x26CD, prExtendedPictographic}, // 5.2 [10] (⛄..⛍) snowman without snow..DISABLED CAR {0x26CE, 0x26CE, prExtendedPictographic}, // 6.0 [1] (⛎) Ophiuchus {0x26CF, 0x26E1, prExtendedPictographic}, // 5.2 [19] (⛏️..⛡) pick..RESTRICTED LEFT ENTRY-2 {0x26E2, 0x26E2, prExtendedPictographic}, // 6.0 [1] (⛢) ASTRONOMICAL SYMBOL FOR URANUS {0x26E3, 0x26E3, prExtendedPictographic}, // 5.2 [1] (⛣) HEAVY CIRCLE WITH STROKE AND TWO DOTS ABOVE {0x26E4, 0x26E7, prExtendedPictographic}, // 6.0 [4] (⛤..⛧) PENTAGRAM..INVERTED PENTAGRAM {0x26E8, 0x26FF, prExtendedPictographic}, // 5.2 [24] (⛨..⛿) BLACK CROSS ON SHIELD..WHITE FLAG WITH HORIZONTAL MIDDLE BLACK STRIPE {0x2700, 0x2700, prExtendedPictographic}, // 7.0 [1] (✀) BLACK SAFETY SCISSORS {0x2701, 0x2704, prExtendedPictographic}, // 1.1 [4] (✁..✄) UPPER BLADE SCISSORS..WHITE SCISSORS {0x2705, 0x2705, prExtendedPictographic}, // 6.0 [1] (✅) check mark button {0x2708, 0x2709, prExtendedPictographic}, // 1.1 [2] (✈️..✉️) airplane..envelope {0x270A, 0x270B, prExtendedPictographic}, // 6.0 [2] (✊..✋) raised fist..raised hand {0x270C, 0x2712, prExtendedPictographic}, // 1.1 [7] (✌️..✒️) victory hand..black nib {0x2714, 0x2714, prExtendedPictographic}, // 1.1 [1] (✔️) check mark {0x2716, 0x2716, prExtendedPictographic}, // 1.1 [1] (✖️) multiplication sign {0x271D, 0x271D, prExtendedPictographic}, // 1.1 [1] (✝️) latin cross {0x2721, 0x2721, prExtendedPictographic}, // 1.1 [1] (✡️) star of David {0x2728, 0x2728, prExtendedPictographic}, // 6.0 [1] (✨) sparkles {0x2733, 0x2734, prExtendedPictographic}, // 1.1 [2] (✳️..✴️) eight-spoked asterisk..eight-pointed star {0x2744, 0x2744, prExtendedPictographic}, // 1.1 [1] (❄️) snowflake {0x2747, 0x2747, prExtendedPictographic}, // 1.1 [1] (❇️) sparkle {0x274C, 0x274C, prExtendedPictographic}, // 6.0 [1] (❌) cross mark {0x274E, 0x274E, prExtendedPictographic}, // 6.0 [1] (❎) cross mark button {0x2753, 0x2755, prExtendedPictographic}, // 6.0 [3] (❓..❕) question mark..white exclamation mark {0x2757, 0x2757, prExtendedPictographic}, // 5.2 [1] (❗) exclamation mark {0x2763, 0x2767, prExtendedPictographic}, // 1.1 [5] (❣️..❧) heart exclamation..ROTATED FLORAL HEART BULLET {0x2795, 0x2797, prExtendedPictographic}, // 6.0 [3] (➕..➗) plus sign..division sign {0x27A1, 0x27A1, prExtendedPictographic}, // 1.1 [1] (➡️) right arrow {0x27B0, 0x27B0, prExtendedPictographic}, // 6.0 [1] (➰) curly loop {0x27BF, 0x27BF, prExtendedPictographic}, // 6.0 [1] (➿) double curly loop {0x2934, 0x2935, prExtendedPictographic}, // 3.2 [2] (⤴️..⤵️) right arrow curving up..right arrow curving down {0x2B05, 0x2B07, prExtendedPictographic}, // 4.0 [3] (⬅️..⬇️) left arrow..down arrow {0x2B1B, 0x2B1C, prExtendedPictographic}, // 5.1 [2] (⬛..⬜) black large square..white large square {0x2B50, 0x2B50, prExtendedPictographic}, // 5.1 [1] (⭐) star {0x2B55, 0x2B55, prExtendedPictographic}, // 5.2 [1] (⭕) hollow red circle {0x2CEF, 0x2CF1, prExtend}, // Mn [3] COPTIC COMBINING NI ABOVE..COPTIC COMBINING SPIRITUS LENIS {0x2D7F, 0x2D7F, prExtend}, // Mn TIFINAGH CONSONANT JOINER {0x2DE0, 0x2DFF, prExtend}, // Mn [32] COMBINING CYRILLIC LETTER BE..COMBINING CYRILLIC LETTER IOTIFIED BIG YUS {0x302A, 0x302D, prExtend}, // Mn [4] IDEOGRAPHIC LEVEL TONE MARK..IDEOGRAPHIC ENTERING TONE MARK {0x302E, 0x302F, prExtend}, // Mc [2] HANGUL SINGLE DOT TONE MARK..HANGUL DOUBLE DOT TONE MARK {0x3030, 0x3030, prExtendedPictographic}, // 1.1 [1] (〰️) wavy dash {0x303D, 0x303D, prExtendedPictographic}, // 3.2 [1] (〽️) part alternation mark {0x3099, 0x309A, prExtend}, // Mn [2] COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK..COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK {0x3297, 0x3297, prExtendedPictographic}, // 1.1 [1] (㊗️) Japanese “congratulations” button {0x3299, 0x3299, prExtendedPictographic}, // 1.1 [1] (㊙️) Japanese “secret” button {0xA66F, 0xA66F, prExtend}, // Mn COMBINING CYRILLIC VZMET {0xA670, 0xA672, prExtend}, // Me [3] COMBINING CYRILLIC TEN MILLIONS SIGN..COMBINING CYRILLIC THOUSAND MILLIONS SIGN {0xA674, 0xA67D, prExtend}, // Mn [10] COMBINING CYRILLIC LETTER UKRAINIAN IE..COMBINING CYRILLIC PAYEROK {0xA69E, 0xA69F, prExtend}, // Mn [2] COMBINING CYRILLIC LETTER EF..COMBINING CYRILLIC LETTER IOTIFIED E {0xA6F0, 0xA6F1, prExtend}, // Mn [2] BAMUM COMBINING MARK KOQNDON..BAMUM COMBINING MARK TUKWENTIS {0xA802, 0xA802, prExtend}, // Mn SYLOTI NAGRI SIGN DVISVARA {0xA806, 0xA806, prExtend}, // Mn SYLOTI NAGRI SIGN HASANTA {0xA80B, 0xA80B, prExtend}, // Mn SYLOTI NAGRI SIGN ANUSVARA {0xA823, 0xA824, prSpacingMark}, // Mc [2] SYLOTI NAGRI VOWEL SIGN A..SYLOTI NAGRI VOWEL SIGN I {0xA825, 0xA826, prExtend}, // Mn [2] SYLOTI NAGRI VOWEL SIGN U..SYLOTI NAGRI VOWEL SIGN E {0xA827, 0xA827, prSpacingMark}, // Mc SYLOTI NAGRI VOWEL SIGN OO {0xA880, 0xA881, prSpacingMark}, // Mc [2] SAURASHTRA SIGN ANUSVARA..SAURASHTRA SIGN VISARGA {0xA8B4, 0xA8C3, prSpacingMark}, // Mc [16] SAURASHTRA CONSONANT SIGN HAARU..SAURASHTRA VOWEL SIGN AU {0xA8C4, 0xA8C5, prExtend}, // Mn [2] SAURASHTRA SIGN VIRAMA..SAURASHTRA SIGN CANDRABINDU {0xA8E0, 0xA8F1, prExtend}, // Mn [18] COMBINING DEVANAGARI DIGIT ZERO..COMBINING DEVANAGARI SIGN AVAGRAHA {0xA8FF, 0xA8FF, prExtend}, // Mn DEVANAGARI VOWEL SIGN AY {0xA926, 0xA92D, prExtend}, // Mn [8] KAYAH LI VOWEL UE..KAYAH LI TONE CALYA PLOPHU {0xA947, 0xA951, prExtend}, // Mn [11] REJANG VOWEL SIGN I..REJANG CONSONANT SIGN R {0xA952, 0xA953, prSpacingMark}, // Mc [2] REJANG CONSONANT SIGN H..REJANG VIRAMA {0xA960, 0xA97C, prL}, // Lo [29] HANGUL CHOSEONG TIKEUT-MIEUM..HANGUL CHOSEONG SSANGYEORINHIEUH {0xA980, 0xA982, prExtend}, // Mn [3] JAVANESE SIGN PANYANGGA..JAVANESE SIGN LAYAR {0xA983, 0xA983, prSpacingMark}, // Mc JAVANESE SIGN WIGNYAN {0xA9B3, 0xA9B3, prExtend}, // Mn JAVANESE SIGN CECAK TELU {0xA9B4, 0xA9B5, prSpacingMark}, // Mc [2] JAVANESE VOWEL SIGN TARUNG..JAVANESE VOWEL SIGN TOLONG {0xA9B6, 0xA9B9, prExtend}, // Mn [4] JAVANESE VOWEL SIGN WULU..JAVANESE VOWEL SIGN SUKU MENDUT {0xA9BA, 0xA9BB, prSpacingMark}, // Mc [2] JAVANESE VOWEL SIGN TALING..JAVANESE VOWEL SIGN DIRGA MURE {0xA9BC, 0xA9BD, prExtend}, // Mn [2] JAVANESE VOWEL SIGN PEPET..JAVANESE CONSONANT SIGN KERET {0xA9BE, 0xA9C0, prSpacingMark}, // Mc [3] JAVANESE CONSONANT SIGN PENGKAL..JAVANESE PANGKON {0xA9E5, 0xA9E5, prExtend}, // Mn MYANMAR SIGN SHAN SAW {0xAA29, 0xAA2E, prExtend}, // Mn [6] CHAM VOWEL SIGN AA..CHAM VOWEL SIGN OE {0xAA2F, 0xAA30, prSpacingMark}, // Mc [2] CHAM VOWEL SIGN O..CHAM VOWEL SIGN AI {0xAA31, 0xAA32, prExtend}, // Mn [2] CHAM VOWEL SIGN AU..CHAM VOWEL SIGN UE {0xAA33, 0xAA34, prSpacingMark}, // Mc [2] CHAM CONSONANT SIGN YA..CHAM CONSONANT SIGN RA {0xAA35, 0xAA36, prExtend}, // Mn [2] CHAM CONSONANT SIGN LA..CHAM CONSONANT SIGN WA {0xAA43, 0xAA43, prExtend}, // Mn CHAM CONSONANT SIGN FINAL NG {0xAA4C, 0xAA4C, prExtend}, // Mn CHAM CONSONANT SIGN FINAL M {0xAA4D, 0xAA4D, prSpacingMark}, // Mc CHAM CONSONANT SIGN FINAL H {0xAA7C, 0xAA7C, prExtend}, // Mn MYANMAR SIGN TAI LAING TONE-2 {0xAAB0, 0xAAB0, prExtend}, // Mn TAI VIET MAI KANG {0xAAB2, 0xAAB4, prExtend}, // Mn [3] TAI VIET VOWEL I..TAI VIET VOWEL U {0xAAB7, 0xAAB8, prExtend}, // Mn [2] TAI VIET MAI KHIT..TAI VIET VOWEL IA {0xAABE, 0xAABF, prExtend}, // Mn [2] TAI VIET VOWEL AM..TAI VIET TONE MAI EK {0xAAC1, 0xAAC1, prExtend}, // Mn TAI VIET TONE MAI THO {0xAAEB, 0xAAEB, prSpacingMark}, // Mc MEETEI MAYEK VOWEL SIGN II {0xAAEC, 0xAAED, prExtend}, // Mn [2] MEETEI MAYEK VOWEL SIGN UU..MEETEI MAYEK VOWEL SIGN AAI {0xAAEE, 0xAAEF, prSpacingMark}, // Mc [2] MEETEI MAYEK VOWEL SIGN AU..MEETEI MAYEK VOWEL SIGN AAU {0xAAF5, 0xAAF5, prSpacingMark}, // Mc MEETEI MAYEK VOWEL SIGN VISARGA {0xAAF6, 0xAAF6, prExtend}, // Mn MEETEI MAYEK VIRAMA {0xABE3, 0xABE4, prSpacingMark}, // Mc [2] MEETEI MAYEK VOWEL SIGN ONAP..MEETEI MAYEK VOWEL SIGN INAP {0xABE5, 0xABE5, prExtend}, // Mn MEETEI MAYEK VOWEL SIGN ANAP {0xABE6, 0xABE7, prSpacingMark}, // Mc [2] MEETEI MAYEK VOWEL SIGN YENAP..MEETEI MAYEK VOWEL SIGN SOUNAP {0xABE8, 0xABE8, prExtend}, // Mn MEETEI MAYEK VOWEL SIGN UNAP {0xABE9, 0xABEA, prSpacingMark}, // Mc [2] MEETEI MAYEK VOWEL SIGN CHEINAP..MEETEI MAYEK VOWEL SIGN NUNG {0xABEC, 0xABEC, prSpacingMark}, // Mc MEETEI MAYEK LUM IYEK {0xABED, 0xABED, prExtend}, // Mn MEETEI MAYEK APUN IYEK {0xAC00, 0xAC00, prLV}, // Lo HANGUL SYLLABLE GA {0xAC01, 0xAC1B, prLVT}, // Lo [27] HANGUL SYLLABLE GAG..HANGUL SYLLABLE GAH {0xAC1C, 0xAC1C, prLV}, // Lo HANGUL SYLLABLE GAE {0xAC1D, 0xAC37, prLVT}, // Lo [27] HANGUL SYLLABLE GAEG..HANGUL SYLLABLE GAEH {0xAC38, 0xAC38, prLV}, // Lo HANGUL SYLLABLE GYA {0xAC39, 0xAC53, prLVT}, // Lo [27] HANGUL SYLLABLE GYAG..HANGUL SYLLABLE GYAH {0xAC54, 0xAC54, prLV}, // Lo HANGUL SYLLABLE GYAE {0xAC55, 0xAC6F, prLVT}, // Lo [27] HANGUL SYLLABLE GYAEG..HANGUL SYLLABLE GYAEH {0xAC70, 0xAC70, prLV}, // Lo HANGUL SYLLABLE GEO {0xAC71, 0xAC8B, prLVT}, // Lo [27] HANGUL SYLLABLE GEOG..HANGUL SYLLABLE GEOH {0xAC8C, 0xAC8C, prLV}, // Lo HANGUL SYLLABLE GE {0xAC8D, 0xACA7, prLVT}, // Lo [27] HANGUL SYLLABLE GEG..HANGUL SYLLABLE GEH {0xACA8, 0xACA8, prLV}, // Lo HANGUL SYLLABLE GYEO {0xACA9, 0xACC3, prLVT}, // Lo [27] HANGUL SYLLABLE GYEOG..HANGUL SYLLABLE GYEOH {0xACC4, 0xACC4, prLV}, // Lo HANGUL SYLLABLE GYE {0xACC5, 0xACDF, prLVT}, // Lo [27] HANGUL SYLLABLE GYEG..HANGUL SYLLABLE GYEH {0xACE0, 0xACE0, prLV}, // Lo HANGUL SYLLABLE GO {0xACE1, 0xACFB, prLVT}, // Lo [27] HANGUL SYLLABLE GOG..HANGUL SYLLABLE GOH {0xACFC, 0xACFC, prLV}, // Lo HANGUL SYLLABLE GWA {0xACFD, 0xAD17, prLVT}, // Lo [27] HANGUL SYLLABLE GWAG..HANGUL SYLLABLE GWAH {0xAD18, 0xAD18, prLV}, // Lo HANGUL SYLLABLE GWAE {0xAD19, 0xAD33, prLVT}, // Lo [27] HANGUL SYLLABLE GWAEG..HANGUL SYLLABLE GWAEH {0xAD34, 0xAD34, prLV}, // Lo HANGUL SYLLABLE GOE {0xAD35, 0xAD4F, prLVT}, // Lo [27] HANGUL SYLLABLE GOEG..HANGUL SYLLABLE GOEH {0xAD50, 0xAD50, prLV}, // Lo HANGUL SYLLABLE GYO {0xAD51, 0xAD6B, prLVT}, // Lo [27] HANGUL SYLLABLE GYOG..HANGUL SYLLABLE GYOH {0xAD6C, 0xAD6C, prLV}, // Lo HANGUL SYLLABLE GU {0xAD6D, 0xAD87, prLVT}, // Lo [27] HANGUL SYLLABLE GUG..HANGUL SYLLABLE GUH {0xAD88, 0xAD88, prLV}, // Lo HANGUL SYLLABLE GWEO {0xAD89, 0xADA3, prLVT}, // Lo [27] HANGUL SYLLABLE GWEOG..HANGUL SYLLABLE GWEOH {0xADA4, 0xADA4, prLV}, // Lo HANGUL SYLLABLE GWE {0xADA5, 0xADBF, prLVT}, // Lo [27] HANGUL SYLLABLE GWEG..HANGUL SYLLABLE GWEH {0xADC0, 0xADC0, prLV}, // Lo HANGUL SYLLABLE GWI {0xADC1, 0xADDB, prLVT}, // Lo [27] HANGUL SYLLABLE GWIG..HANGUL SYLLABLE GWIH {0xADDC, 0xADDC, prLV}, // Lo HANGUL SYLLABLE GYU {0xADDD, 0xADF7, prLVT}, // Lo [27] HANGUL SYLLABLE GYUG..HANGUL SYLLABLE GYUH {0xADF8, 0xADF8, prLV}, // Lo HANGUL SYLLABLE GEU {0xADF9, 0xAE13, prLVT}, // Lo [27] HANGUL SYLLABLE GEUG..HANGUL SYLLABLE GEUH {0xAE14, 0xAE14, prLV}, // Lo HANGUL SYLLABLE GYI {0xAE15, 0xAE2F, prLVT}, // Lo [27] HANGUL SYLLABLE GYIG..HANGUL SYLLABLE GYIH {0xAE30, 0xAE30, prLV}, // Lo HANGUL SYLLABLE GI {0xAE31, 0xAE4B, prLVT}, // Lo [27] HANGUL SYLLABLE GIG..HANGUL SYLLABLE GIH {0xAE4C, 0xAE4C, prLV}, // Lo HANGUL SYLLABLE GGA {0xAE4D, 0xAE67, prLVT}, // Lo [27] HANGUL SYLLABLE GGAG..HANGUL SYLLABLE GGAH {0xAE68, 0xAE68, prLV}, // Lo HANGUL SYLLABLE GGAE {0xAE69, 0xAE83, prLVT}, // Lo [27] HANGUL SYLLABLE GGAEG..HANGUL SYLLABLE GGAEH {0xAE84, 0xAE84, prLV}, // Lo HANGUL SYLLABLE GGYA {0xAE85, 0xAE9F, prLVT}, // Lo [27] HANGUL SYLLABLE GGYAG..HANGUL SYLLABLE GGYAH {0xAEA0, 0xAEA0, prLV}, // Lo HANGUL SYLLABLE GGYAE {0xAEA1, 0xAEBB, prLVT}, // Lo [27] HANGUL SYLLABLE GGYAEG..HANGUL SYLLABLE GGYAEH {0xAEBC, 0xAEBC, prLV}, // Lo HANGUL SYLLABLE GGEO {0xAEBD, 0xAED7, prLVT}, // Lo [27] HANGUL SYLLABLE GGEOG..HANGUL SYLLABLE GGEOH {0xAED8, 0xAED8, prLV}, // Lo HANGUL SYLLABLE GGE {0xAED9, 0xAEF3, prLVT}, // Lo [27] HANGUL SYLLABLE GGEG..HANGUL SYLLABLE GGEH {0xAEF4, 0xAEF4, prLV}, // Lo HANGUL SYLLABLE GGYEO {0xAEF5, 0xAF0F, prLVT}, // Lo [27] HANGUL SYLLABLE GGYEOG..HANGUL SYLLABLE GGYEOH {0xAF10, 0xAF10, prLV}, // Lo HANGUL SYLLABLE GGYE {0xAF11, 0xAF2B, prLVT}, // Lo [27] HANGUL SYLLABLE GGYEG..HANGUL SYLLABLE GGYEH {0xAF2C, 0xAF2C, prLV}, // Lo HANGUL SYLLABLE GGO {0xAF2D, 0xAF47, prLVT}, // Lo [27] HANGUL SYLLABLE GGOG..HANGUL SYLLABLE GGOH {0xAF48, 0xAF48, prLV}, // Lo HANGUL SYLLABLE GGWA {0xAF49, 0xAF63, prLVT}, // Lo [27] HANGUL SYLLABLE GGWAG..HANGUL SYLLABLE GGWAH {0xAF64, 0xAF64, prLV}, // Lo HANGUL SYLLABLE GGWAE {0xAF65, 0xAF7F, prLVT}, // Lo [27] HANGUL SYLLABLE GGWAEG..HANGUL SYLLABLE GGWAEH {0xAF80, 0xAF80, prLV}, // Lo HANGUL SYLLABLE GGOE {0xAF81, 0xAF9B, prLVT}, // Lo [27] HANGUL SYLLABLE GGOEG..HANGUL SYLLABLE GGOEH {0xAF9C, 0xAF9C, prLV}, // Lo HANGUL SYLLABLE GGYO {0xAF9D, 0xAFB7, prLVT}, // Lo [27] HANGUL SYLLABLE GGYOG..HANGUL SYLLABLE GGYOH {0xAFB8, 0xAFB8, prLV}, // Lo HANGUL SYLLABLE GGU {0xAFB9, 0xAFD3, prLVT}, // Lo [27] HANGUL SYLLABLE GGUG..HANGUL SYLLABLE GGUH {0xAFD4, 0xAFD4, prLV}, // Lo HANGUL SYLLABLE GGWEO {0xAFD5, 0xAFEF, prLVT}, // Lo [27] HANGUL SYLLABLE GGWEOG..HANGUL SYLLABLE GGWEOH {0xAFF0, 0xAFF0, prLV}, // Lo HANGUL SYLLABLE GGWE {0xAFF1, 0xB00B, prLVT}, // Lo [27] HANGUL SYLLABLE GGWEG..HANGUL SYLLABLE GGWEH {0xB00C, 0xB00C, prLV}, // Lo HANGUL SYLLABLE GGWI {0xB00D, 0xB027, prLVT}, // Lo [27] HANGUL SYLLABLE GGWIG..HANGUL SYLLABLE GGWIH {0xB028, 0xB028, prLV}, // Lo HANGUL SYLLABLE GGYU {0xB029, 0xB043, prLVT}, // Lo [27] HANGUL SYLLABLE GGYUG..HANGUL SYLLABLE GGYUH {0xB044, 0xB044, prLV}, // Lo HANGUL SYLLABLE GGEU {0xB045, 0xB05F, prLVT}, // Lo [27] HANGUL SYLLABLE GGEUG..HANGUL SYLLABLE GGEUH {0xB060, 0xB060, prLV}, // Lo HANGUL SYLLABLE GGYI {0xB061, 0xB07B, prLVT}, // Lo [27] HANGUL SYLLABLE GGYIG..HANGUL SYLLABLE GGYIH {0xB07C, 0xB07C, prLV}, // Lo HANGUL SYLLABLE GGI {0xB07D, 0xB097, prLVT}, // Lo [27] HANGUL SYLLABLE GGIG..HANGUL SYLLABLE GGIH {0xB098, 0xB098, prLV}, // Lo HANGUL SYLLABLE NA {0xB099, 0xB0B3, prLVT}, // Lo [27] HANGUL SYLLABLE NAG..HANGUL SYLLABLE NAH {0xB0B4, 0xB0B4, prLV}, // Lo HANGUL SYLLABLE NAE {0xB0B5, 0xB0CF, prLVT}, // Lo [27] HANGUL SYLLABLE NAEG..HANGUL SYLLABLE NAEH {0xB0D0, 0xB0D0, prLV}, // Lo HANGUL SYLLABLE NYA {0xB0D1, 0xB0EB, prLVT}, // Lo [27] HANGUL SYLLABLE NYAG..HANGUL SYLLABLE NYAH {0xB0EC, 0xB0EC, prLV}, // Lo HANGUL SYLLABLE NYAE {0xB0ED, 0xB107, prLVT}, // Lo [27] HANGUL SYLLABLE NYAEG..HANGUL SYLLABLE NYAEH {0xB108, 0xB108, prLV}, // Lo HANGUL SYLLABLE NEO {0xB109, 0xB123, prLVT}, // Lo [27] HANGUL SYLLABLE NEOG..HANGUL SYLLABLE NEOH {0xB124, 0xB124, prLV}, // Lo HANGUL SYLLABLE NE {0xB125, 0xB13F, prLVT}, // Lo [27] HANGUL SYLLABLE NEG..HANGUL SYLLABLE NEH {0xB140, 0xB140, prLV}, // Lo HANGUL SYLLABLE NYEO {0xB141, 0xB15B, prLVT}, // Lo [27] HANGUL SYLLABLE NYEOG..HANGUL SYLLABLE NYEOH {0xB15C, 0xB15C, prLV}, // Lo HANGUL SYLLABLE NYE {0xB15D, 0xB177, prLVT}, // Lo [27] HANGUL SYLLABLE NYEG..HANGUL SYLLABLE NYEH {0xB178, 0xB178, prLV}, // Lo HANGUL SYLLABLE NO {0xB179, 0xB193, prLVT}, // Lo [27] HANGUL SYLLABLE NOG..HANGUL SYLLABLE NOH {0xB194, 0xB194, prLV}, // Lo HANGUL SYLLABLE NWA {0xB195, 0xB1AF, prLVT}, // Lo [27] HANGUL SYLLABLE NWAG..HANGUL SYLLABLE NWAH {0xB1B0, 0xB1B0, prLV}, // Lo HANGUL SYLLABLE NWAE {0xB1B1, 0xB1CB, prLVT}, // Lo [27] HANGUL SYLLABLE NWAEG..HANGUL SYLLABLE NWAEH {0xB1CC, 0xB1CC, prLV}, // Lo HANGUL SYLLABLE NOE {0xB1CD, 0xB1E7, prLVT}, // Lo [27] HANGUL SYLLABLE NOEG..HANGUL SYLLABLE NOEH {0xB1E8, 0xB1E8, prLV}, // Lo HANGUL SYLLABLE NYO {0xB1E9, 0xB203, prLVT}, // Lo [27] HANGUL SYLLABLE NYOG..HANGUL SYLLABLE NYOH {0xB204, 0xB204, prLV}, // Lo HANGUL SYLLABLE NU {0xB205, 0xB21F, prLVT}, // Lo [27] HANGUL SYLLABLE NUG..HANGUL SYLLABLE NUH {0xB220, 0xB220, prLV}, // Lo HANGUL SYLLABLE NWEO {0xB221, 0xB23B, prLVT}, // Lo [27] HANGUL SYLLABLE NWEOG..HANGUL SYLLABLE NWEOH {0xB23C, 0xB23C, prLV}, // Lo HANGUL SYLLABLE NWE {0xB23D, 0xB257, prLVT}, // Lo [27] HANGUL SYLLABLE NWEG..HANGUL SYLLABLE NWEH {0xB258, 0xB258, prLV}, // Lo HANGUL SYLLABLE NWI {0xB259, 0xB273, prLVT}, // Lo [27] HANGUL SYLLABLE NWIG..HANGUL SYLLABLE NWIH {0xB274, 0xB274, prLV}, // Lo HANGUL SYLLABLE NYU {0xB275, 0xB28F, prLVT}, // Lo [27] HANGUL SYLLABLE NYUG..HANGUL SYLLABLE NYUH {0xB290, 0xB290, prLV}, // Lo HANGUL SYLLABLE NEU {0xB291, 0xB2AB, prLVT}, // Lo [27] HANGUL SYLLABLE NEUG..HANGUL SYLLABLE NEUH {0xB2AC, 0xB2AC, prLV}, // Lo HANGUL SYLLABLE NYI {0xB2AD, 0xB2C7, prLVT}, // Lo [27] HANGUL SYLLABLE NYIG..HANGUL SYLLABLE NYIH {0xB2C8, 0xB2C8, prLV}, // Lo HANGUL SYLLABLE NI {0xB2C9, 0xB2E3, prLVT}, // Lo [27] HANGUL SYLLABLE NIG..HANGUL SYLLABLE NIH {0xB2E4, 0xB2E4, prLV}, // Lo HANGUL SYLLABLE DA {0xB2E5, 0xB2FF, prLVT}, // Lo [27] HANGUL SYLLABLE DAG..HANGUL SYLLABLE DAH {0xB300, 0xB300, prLV}, // Lo HANGUL SYLLABLE DAE {0xB301, 0xB31B, prLVT}, // Lo [27] HANGUL SYLLABLE DAEG..HANGUL SYLLABLE DAEH {0xB31C, 0xB31C, prLV}, // Lo HANGUL SYLLABLE DYA {0xB31D, 0xB337, prLVT}, // Lo [27] HANGUL SYLLABLE DYAG..HANGUL SYLLABLE DYAH {0xB338, 0xB338, prLV}, // Lo HANGUL SYLLABLE DYAE {0xB339, 0xB353, prLVT}, // Lo [27] HANGUL SYLLABLE DYAEG..HANGUL SYLLABLE DYAEH {0xB354, 0xB354, prLV}, // Lo HANGUL SYLLABLE DEO {0xB355, 0xB36F, prLVT}, // Lo [27] HANGUL SYLLABLE DEOG..HANGUL SYLLABLE DEOH {0xB370, 0xB370, prLV}, // Lo HANGUL SYLLABLE DE {0xB371, 0xB38B, prLVT}, // Lo [27] HANGUL SYLLABLE DEG..HANGUL SYLLABLE DEH {0xB38C, 0xB38C, prLV}, // Lo HANGUL SYLLABLE DYEO {0xB38D, 0xB3A7, prLVT}, // Lo [27] HANGUL SYLLABLE DYEOG..HANGUL SYLLABLE DYEOH {0xB3A8, 0xB3A8, prLV}, // Lo HANGUL SYLLABLE DYE {0xB3A9, 0xB3C3, prLVT}, // Lo [27] HANGUL SYLLABLE DYEG..HANGUL SYLLABLE DYEH {0xB3C4, 0xB3C4, prLV}, // Lo HANGUL SYLLABLE DO {0xB3C5, 0xB3DF, prLVT}, // Lo [27] HANGUL SYLLABLE DOG..HANGUL SYLLABLE DOH {0xB3E0, 0xB3E0, prLV}, // Lo HANGUL SYLLABLE DWA {0xB3E1, 0xB3FB, prLVT}, // Lo [27] HANGUL SYLLABLE DWAG..HANGUL SYLLABLE DWAH {0xB3FC, 0xB3FC, prLV}, // Lo HANGUL SYLLABLE DWAE {0xB3FD, 0xB417, prLVT}, // Lo [27] HANGUL SYLLABLE DWAEG..HANGUL SYLLABLE DWAEH {0xB418, 0xB418, prLV}, // Lo HANGUL SYLLABLE DOE {0xB419, 0xB433, prLVT}, // Lo [27] HANGUL SYLLABLE DOEG..HANGUL SYLLABLE DOEH {0xB434, 0xB434, prLV}, // Lo HANGUL SYLLABLE DYO {0xB435, 0xB44F, prLVT}, // Lo [27] HANGUL SYLLABLE DYOG..HANGUL SYLLABLE DYOH {0xB450, 0xB450, prLV}, // Lo HANGUL SYLLABLE DU {0xB451, 0xB46B, prLVT}, // Lo [27] HANGUL SYLLABLE DUG..HANGUL SYLLABLE DUH {0xB46C, 0xB46C, prLV}, // Lo HANGUL SYLLABLE DWEO {0xB46D, 0xB487, prLVT}, // Lo [27] HANGUL SYLLABLE DWEOG..HANGUL SYLLABLE DWEOH {0xB488, 0xB488, prLV}, // Lo HANGUL SYLLABLE DWE {0xB489, 0xB4A3, prLVT}, // Lo [27] HANGUL SYLLABLE DWEG..HANGUL SYLLABLE DWEH {0xB4A4, 0xB4A4, prLV}, // Lo HANGUL SYLLABLE DWI {0xB4A5, 0xB4BF, prLVT}, // Lo [27] HANGUL SYLLABLE DWIG..HANGUL SYLLABLE DWIH {0xB4C0, 0xB4C0, prLV}, // Lo HANGUL SYLLABLE DYU {0xB4C1, 0xB4DB, prLVT}, // Lo [27] HANGUL SYLLABLE DYUG..HANGUL SYLLABLE DYUH {0xB4DC, 0xB4DC, prLV}, // Lo HANGUL SYLLABLE DEU {0xB4DD, 0xB4F7, prLVT}, // Lo [27] HANGUL SYLLABLE DEUG..HANGUL SYLLABLE DEUH {0xB4F8, 0xB4F8, prLV}, // Lo HANGUL SYLLABLE DYI {0xB4F9, 0xB513, prLVT}, // Lo [27] HANGUL SYLLABLE DYIG..HANGUL SYLLABLE DYIH {0xB514, 0xB514, prLV}, // Lo HANGUL SYLLABLE DI {0xB515, 0xB52F, prLVT}, // Lo [27] HANGUL SYLLABLE DIG..HANGUL SYLLABLE DIH {0xB530, 0xB530, prLV}, // Lo HANGUL SYLLABLE DDA {0xB531, 0xB54B, prLVT}, // Lo [27] HANGUL SYLLABLE DDAG..HANGUL SYLLABLE DDAH {0xB54C, 0xB54C, prLV}, // Lo HANGUL SYLLABLE DDAE {0xB54D, 0xB567, prLVT}, // Lo [27] HANGUL SYLLABLE DDAEG..HANGUL SYLLABLE DDAEH {0xB568, 0xB568, prLV}, // Lo HANGUL SYLLABLE DDYA {0xB569, 0xB583, prLVT}, // Lo [27] HANGUL SYLLABLE DDYAG..HANGUL SYLLABLE DDYAH {0xB584, 0xB584, prLV}, // Lo HANGUL SYLLABLE DDYAE {0xB585, 0xB59F, prLVT}, // Lo [27] HANGUL SYLLABLE DDYAEG..HANGUL SYLLABLE DDYAEH {0xB5A0, 0xB5A0, prLV}, // Lo HANGUL SYLLABLE DDEO {0xB5A1, 0xB5BB, prLVT}, // Lo [27] HANGUL SYLLABLE DDEOG..HANGUL SYLLABLE DDEOH {0xB5BC, 0xB5BC, prLV}, // Lo HANGUL SYLLABLE DDE {0xB5BD, 0xB5D7, prLVT}, // Lo [27] HANGUL SYLLABLE DDEG..HANGUL SYLLABLE DDEH {0xB5D8, 0xB5D8, prLV}, // Lo HANGUL SYLLABLE DDYEO {0xB5D9, 0xB5F3, prLVT}, // Lo [27] HANGUL SYLLABLE DDYEOG..HANGUL SYLLABLE DDYEOH {0xB5F4, 0xB5F4, prLV}, // Lo HANGUL SYLLABLE DDYE {0xB5F5, 0xB60F, prLVT}, // Lo [27] HANGUL SYLLABLE DDYEG..HANGUL SYLLABLE DDYEH {0xB610, 0xB610, prLV}, // Lo HANGUL SYLLABLE DDO {0xB611, 0xB62B, prLVT}, // Lo [27] HANGUL SYLLABLE DDOG..HANGUL SYLLABLE DDOH {0xB62C, 0xB62C, prLV}, // Lo HANGUL SYLLABLE DDWA {0xB62D, 0xB647, prLVT}, // Lo [27] HANGUL SYLLABLE DDWAG..HANGUL SYLLABLE DDWAH {0xB648, 0xB648, prLV}, // Lo HANGUL SYLLABLE DDWAE {0xB649, 0xB663, prLVT}, // Lo [27] HANGUL SYLLABLE DDWAEG..HANGUL SYLLABLE DDWAEH {0xB664, 0xB664, prLV}, // Lo HANGUL SYLLABLE DDOE {0xB665, 0xB67F, prLVT}, // Lo [27] HANGUL SYLLABLE DDOEG..HANGUL SYLLABLE DDOEH {0xB680, 0xB680, prLV}, // Lo HANGUL SYLLABLE DDYO {0xB681, 0xB69B, prLVT}, // Lo [27] HANGUL SYLLABLE DDYOG..HANGUL SYLLABLE DDYOH {0xB69C, 0xB69C, prLV}, // Lo HANGUL SYLLABLE DDU {0xB69D, 0xB6B7, prLVT}, // Lo [27] HANGUL SYLLABLE DDUG..HANGUL SYLLABLE DDUH {0xB6B8, 0xB6B8, prLV}, // Lo HANGUL SYLLABLE DDWEO {0xB6B9, 0xB6D3, prLVT}, // Lo [27] HANGUL SYLLABLE DDWEOG..HANGUL SYLLABLE DDWEOH {0xB6D4, 0xB6D4, prLV}, // Lo HANGUL SYLLABLE DDWE {0xB6D5, 0xB6EF, prLVT}, // Lo [27] HANGUL SYLLABLE DDWEG..HANGUL SYLLABLE DDWEH {0xB6F0, 0xB6F0, prLV}, // Lo HANGUL SYLLABLE DDWI {0xB6F1, 0xB70B, prLVT}, // Lo [27] HANGUL SYLLABLE DDWIG..HANGUL SYLLABLE DDWIH {0xB70C, 0xB70C, prLV}, // Lo HANGUL SYLLABLE DDYU {0xB70D, 0xB727, prLVT}, // Lo [27] HANGUL SYLLABLE DDYUG..HANGUL SYLLABLE DDYUH {0xB728, 0xB728, prLV}, // Lo HANGUL SYLLABLE DDEU {0xB729, 0xB743, prLVT}, // Lo [27] HANGUL SYLLABLE DDEUG..HANGUL SYLLABLE DDEUH {0xB744, 0xB744, prLV}, // Lo HANGUL SYLLABLE DDYI {0xB745, 0xB75F, prLVT}, // Lo [27] HANGUL SYLLABLE DDYIG..HANGUL SYLLABLE DDYIH {0xB760, 0xB760, prLV}, // Lo HANGUL SYLLABLE DDI {0xB761, 0xB77B, prLVT}, // Lo [27] HANGUL SYLLABLE DDIG..HANGUL SYLLABLE DDIH {0xB77C, 0xB77C, prLV}, // Lo HANGUL SYLLABLE RA {0xB77D, 0xB797, prLVT}, // Lo [27] HANGUL SYLLABLE RAG..HANGUL SYLLABLE RAH {0xB798, 0xB798, prLV}, // Lo HANGUL SYLLABLE RAE {0xB799, 0xB7B3, prLVT}, // Lo [27] HANGUL SYLLABLE RAEG..HANGUL SYLLABLE RAEH {0xB7B4, 0xB7B4, prLV}, // Lo HANGUL SYLLABLE RYA {0xB7B5, 0xB7CF, prLVT}, // Lo [27] HANGUL SYLLABLE RYAG..HANGUL SYLLABLE RYAH {0xB7D0, 0xB7D0, prLV}, // Lo HANGUL SYLLABLE RYAE {0xB7D1, 0xB7EB, prLVT}, // Lo [27] HANGUL SYLLABLE RYAEG..HANGUL SYLLABLE RYAEH {0xB7EC, 0xB7EC, prLV}, // Lo HANGUL SYLLABLE REO {0xB7ED, 0xB807, prLVT}, // Lo [27] HANGUL SYLLABLE REOG..HANGUL SYLLABLE REOH {0xB808, 0xB808, prLV}, // Lo HANGUL SYLLABLE RE {0xB809, 0xB823, prLVT}, // Lo [27] HANGUL SYLLABLE REG..HANGUL SYLLABLE REH {0xB824, 0xB824, prLV}, // Lo HANGUL SYLLABLE RYEO {0xB825, 0xB83F, prLVT}, // Lo [27] HANGUL SYLLABLE RYEOG..HANGUL SYLLABLE RYEOH {0xB840, 0xB840, prLV}, // Lo HANGUL SYLLABLE RYE {0xB841, 0xB85B, prLVT}, // Lo [27] HANGUL SYLLABLE RYEG..HANGUL SYLLABLE RYEH {0xB85C, 0xB85C, prLV}, // Lo HANGUL SYLLABLE RO {0xB85D, 0xB877, prLVT}, // Lo [27] HANGUL SYLLABLE ROG..HANGUL SYLLABLE ROH {0xB878, 0xB878, prLV}, // Lo HANGUL SYLLABLE RWA {0xB879, 0xB893, prLVT}, // Lo [27] HANGUL SYLLABLE RWAG..HANGUL SYLLABLE RWAH {0xB894, 0xB894, prLV}, // Lo HANGUL SYLLABLE RWAE {0xB895, 0xB8AF, prLVT}, // Lo [27] HANGUL SYLLABLE RWAEG..HANGUL SYLLABLE RWAEH {0xB8B0, 0xB8B0, prLV}, // Lo HANGUL SYLLABLE ROE {0xB8B1, 0xB8CB, prLVT}, // Lo [27] HANGUL SYLLABLE ROEG..HANGUL SYLLABLE ROEH {0xB8CC, 0xB8CC, prLV}, // Lo HANGUL SYLLABLE RYO {0xB8CD, 0xB8E7, prLVT}, // Lo [27] HANGUL SYLLABLE RYOG..HANGUL SYLLABLE RYOH {0xB8E8, 0xB8E8, prLV}, // Lo HANGUL SYLLABLE RU {0xB8E9, 0xB903, prLVT}, // Lo [27] HANGUL SYLLABLE RUG..HANGUL SYLLABLE RUH {0xB904, 0xB904, prLV}, // Lo HANGUL SYLLABLE RWEO {0xB905, 0xB91F, prLVT}, // Lo [27] HANGUL SYLLABLE RWEOG..HANGUL SYLLABLE RWEOH {0xB920, 0xB920, prLV}, // Lo HANGUL SYLLABLE RWE {0xB921, 0xB93B, prLVT}, // Lo [27] HANGUL SYLLABLE RWEG..HANGUL SYLLABLE RWEH {0xB93C, 0xB93C, prLV}, // Lo HANGUL SYLLABLE RWI {0xB93D, 0xB957, prLVT}, // Lo [27] HANGUL SYLLABLE RWIG..HANGUL SYLLABLE RWIH {0xB958, 0xB958, prLV}, // Lo HANGUL SYLLABLE RYU {0xB959, 0xB973, prLVT}, // Lo [27] HANGUL SYLLABLE RYUG..HANGUL SYLLABLE RYUH {0xB974, 0xB974, prLV}, // Lo HANGUL SYLLABLE REU {0xB975, 0xB98F, prLVT}, // Lo [27] HANGUL SYLLABLE REUG..HANGUL SYLLABLE REUH {0xB990, 0xB990, prLV}, // Lo HANGUL SYLLABLE RYI {0xB991, 0xB9AB, prLVT}, // Lo [27] HANGUL SYLLABLE RYIG..HANGUL SYLLABLE RYIH {0xB9AC, 0xB9AC, prLV}, // Lo HANGUL SYLLABLE RI {0xB9AD, 0xB9C7, prLVT}, // Lo [27] HANGUL SYLLABLE RIG..HANGUL SYLLABLE RIH {0xB9C8, 0xB9C8, prLV}, // Lo HANGUL SYLLABLE MA {0xB9C9, 0xB9E3, prLVT}, // Lo [27] HANGUL SYLLABLE MAG..HANGUL SYLLABLE MAH {0xB9E4, 0xB9E4, prLV}, // Lo HANGUL SYLLABLE MAE {0xB9E5, 0xB9FF, prLVT}, // Lo [27] HANGUL SYLLABLE MAEG..HANGUL SYLLABLE MAEH {0xBA00, 0xBA00, prLV}, // Lo HANGUL SYLLABLE MYA {0xBA01, 0xBA1B, prLVT}, // Lo [27] HANGUL SYLLABLE MYAG..HANGUL SYLLABLE MYAH {0xBA1C, 0xBA1C, prLV}, // Lo HANGUL SYLLABLE MYAE {0xBA1D, 0xBA37, prLVT}, // Lo [27] HANGUL SYLLABLE MYAEG..HANGUL SYLLABLE MYAEH {0xBA38, 0xBA38, prLV}, // Lo HANGUL SYLLABLE MEO {0xBA39, 0xBA53, prLVT}, // Lo [27] HANGUL SYLLABLE MEOG..HANGUL SYLLABLE MEOH {0xBA54, 0xBA54, prLV}, // Lo HANGUL SYLLABLE ME {0xBA55, 0xBA6F, prLVT}, // Lo [27] HANGUL SYLLABLE MEG..HANGUL SYLLABLE MEH {0xBA70, 0xBA70, prLV}, // Lo HANGUL SYLLABLE MYEO {0xBA71, 0xBA8B, prLVT}, // Lo [27] HANGUL SYLLABLE MYEOG..HANGUL SYLLABLE MYEOH {0xBA8C, 0xBA8C, prLV}, // Lo HANGUL SYLLABLE MYE {0xBA8D, 0xBAA7, prLVT}, // Lo [27] HANGUL SYLLABLE MYEG..HANGUL SYLLABLE MYEH {0xBAA8, 0xBAA8, prLV}, // Lo HANGUL SYLLABLE MO {0xBAA9, 0xBAC3, prLVT}, // Lo [27] HANGUL SYLLABLE MOG..HANGUL SYLLABLE MOH {0xBAC4, 0xBAC4, prLV}, // Lo HANGUL SYLLABLE MWA {0xBAC5, 0xBADF, prLVT}, // Lo [27] HANGUL SYLLABLE MWAG..HANGUL SYLLABLE MWAH {0xBAE0, 0xBAE0, prLV}, // Lo HANGUL SYLLABLE MWAE {0xBAE1, 0xBAFB, prLVT}, // Lo [27] HANGUL SYLLABLE MWAEG..HANGUL SYLLABLE MWAEH {0xBAFC, 0xBAFC, prLV}, // Lo HANGUL SYLLABLE MOE {0xBAFD, 0xBB17, prLVT}, // Lo [27] HANGUL SYLLABLE MOEG..HANGUL SYLLABLE MOEH {0xBB18, 0xBB18, prLV}, // Lo HANGUL SYLLABLE MYO {0xBB19, 0xBB33, prLVT}, // Lo [27] HANGUL SYLLABLE MYOG..HANGUL SYLLABLE MYOH {0xBB34, 0xBB34, prLV}, // Lo HANGUL SYLLABLE MU {0xBB35, 0xBB4F, prLVT}, // Lo [27] HANGUL SYLLABLE MUG..HANGUL SYLLABLE MUH {0xBB50, 0xBB50, prLV}, // Lo HANGUL SYLLABLE MWEO {0xBB51, 0xBB6B, prLVT}, // Lo [27] HANGUL SYLLABLE MWEOG..HANGUL SYLLABLE MWEOH {0xBB6C, 0xBB6C, prLV}, // Lo HANGUL SYLLABLE MWE {0xBB6D, 0xBB87, prLVT}, // Lo [27] HANGUL SYLLABLE MWEG..HANGUL SYLLABLE MWEH {0xBB88, 0xBB88, prLV}, // Lo HANGUL SYLLABLE MWI {0xBB89, 0xBBA3, prLVT}, // Lo [27] HANGUL SYLLABLE MWIG..HANGUL SYLLABLE MWIH {0xBBA4, 0xBBA4, prLV}, // Lo HANGUL SYLLABLE MYU {0xBBA5, 0xBBBF, prLVT}, // Lo [27] HANGUL SYLLABLE MYUG..HANGUL SYLLABLE MYUH {0xBBC0, 0xBBC0, prLV}, // Lo HANGUL SYLLABLE MEU {0xBBC1, 0xBBDB, prLVT}, // Lo [27] HANGUL SYLLABLE MEUG..HANGUL SYLLABLE MEUH {0xBBDC, 0xBBDC, prLV}, // Lo HANGUL SYLLABLE MYI {0xBBDD, 0xBBF7, prLVT}, // Lo [27] HANGUL SYLLABLE MYIG..HANGUL SYLLABLE MYIH {0xBBF8, 0xBBF8, prLV}, // Lo HANGUL SYLLABLE MI {0xBBF9, 0xBC13, prLVT}, // Lo [27] HANGUL SYLLABLE MIG..HANGUL SYLLABLE MIH {0xBC14, 0xBC14, prLV}, // Lo HANGUL SYLLABLE BA {0xBC15, 0xBC2F, prLVT}, // Lo [27] HANGUL SYLLABLE BAG..HANGUL SYLLABLE BAH {0xBC30, 0xBC30, prLV}, // Lo HANGUL SYLLABLE BAE {0xBC31, 0xBC4B, prLVT}, // Lo [27] HANGUL SYLLABLE BAEG..HANGUL SYLLABLE BAEH {0xBC4C, 0xBC4C, prLV}, // Lo HANGUL SYLLABLE BYA {0xBC4D, 0xBC67, prLVT}, // Lo [27] HANGUL SYLLABLE BYAG..HANGUL SYLLABLE BYAH {0xBC68, 0xBC68, prLV}, // Lo HANGUL SYLLABLE BYAE {0xBC69, 0xBC83, prLVT}, // Lo [27] HANGUL SYLLABLE BYAEG..HANGUL SYLLABLE BYAEH {0xBC84, 0xBC84, prLV}, // Lo HANGUL SYLLABLE BEO {0xBC85, 0xBC9F, prLVT}, // Lo [27] HANGUL SYLLABLE BEOG..HANGUL SYLLABLE BEOH {0xBCA0, 0xBCA0, prLV}, // Lo HANGUL SYLLABLE BE {0xBCA1, 0xBCBB, prLVT}, // Lo [27] HANGUL SYLLABLE BEG..HANGUL SYLLABLE BEH {0xBCBC, 0xBCBC, prLV}, // Lo HANGUL SYLLABLE BYEO {0xBCBD, 0xBCD7, prLVT}, // Lo [27] HANGUL SYLLABLE BYEOG..HANGUL SYLLABLE BYEOH {0xBCD8, 0xBCD8, prLV}, // Lo HANGUL SYLLABLE BYE {0xBCD9, 0xBCF3, prLVT}, // Lo [27] HANGUL SYLLABLE BYEG..HANGUL SYLLABLE BYEH {0xBCF4, 0xBCF4, prLV}, // Lo HANGUL SYLLABLE BO {0xBCF5, 0xBD0F, prLVT}, // Lo [27] HANGUL SYLLABLE BOG..HANGUL SYLLABLE BOH {0xBD10, 0xBD10, prLV}, // Lo HANGUL SYLLABLE BWA {0xBD11, 0xBD2B, prLVT}, // Lo [27] HANGUL SYLLABLE BWAG..HANGUL SYLLABLE BWAH {0xBD2C, 0xBD2C, prLV}, // Lo HANGUL SYLLABLE BWAE {0xBD2D, 0xBD47, prLVT}, // Lo [27] HANGUL SYLLABLE BWAEG..HANGUL SYLLABLE BWAEH {0xBD48, 0xBD48, prLV}, // Lo HANGUL SYLLABLE BOE {0xBD49, 0xBD63, prLVT}, // Lo [27] HANGUL SYLLABLE BOEG..HANGUL SYLLABLE BOEH {0xBD64, 0xBD64, prLV}, // Lo HANGUL SYLLABLE BYO {0xBD65, 0xBD7F, prLVT}, // Lo [27] HANGUL SYLLABLE BYOG..HANGUL SYLLABLE BYOH {0xBD80, 0xBD80, prLV}, // Lo HANGUL SYLLABLE BU {0xBD81, 0xBD9B, prLVT}, // Lo [27] HANGUL SYLLABLE BUG..HANGUL SYLLABLE BUH {0xBD9C, 0xBD9C, prLV}, // Lo HANGUL SYLLABLE BWEO {0xBD9D, 0xBDB7, prLVT}, // Lo [27] HANGUL SYLLABLE BWEOG..HANGUL SYLLABLE BWEOH {0xBDB8, 0xBDB8, prLV}, // Lo HANGUL SYLLABLE BWE {0xBDB9, 0xBDD3, prLVT}, // Lo [27] HANGUL SYLLABLE BWEG..HANGUL SYLLABLE BWEH {0xBDD4, 0xBDD4, prLV}, // Lo HANGUL SYLLABLE BWI {0xBDD5, 0xBDEF, prLVT}, // Lo [27] HANGUL SYLLABLE BWIG..HANGUL SYLLABLE BWIH {0xBDF0, 0xBDF0, prLV}, // Lo HANGUL SYLLABLE BYU {0xBDF1, 0xBE0B, prLVT}, // Lo [27] HANGUL SYLLABLE BYUG..HANGUL SYLLABLE BYUH {0xBE0C, 0xBE0C, prLV}, // Lo HANGUL SYLLABLE BEU {0xBE0D, 0xBE27, prLVT}, // Lo [27] HANGUL SYLLABLE BEUG..HANGUL SYLLABLE BEUH {0xBE28, 0xBE28, prLV}, // Lo HANGUL SYLLABLE BYI {0xBE29, 0xBE43, prLVT}, // Lo [27] HANGUL SYLLABLE BYIG..HANGUL SYLLABLE BYIH {0xBE44, 0xBE44, prLV}, // Lo HANGUL SYLLABLE BI {0xBE45, 0xBE5F, prLVT}, // Lo [27] HANGUL SYLLABLE BIG..HANGUL SYLLABLE BIH {0xBE60, 0xBE60, prLV}, // Lo HANGUL SYLLABLE BBA {0xBE61, 0xBE7B, prLVT}, // Lo [27] HANGUL SYLLABLE BBAG..HANGUL SYLLABLE BBAH {0xBE7C, 0xBE7C, prLV}, // Lo HANGUL SYLLABLE BBAE {0xBE7D, 0xBE97, prLVT}, // Lo [27] HANGUL SYLLABLE BBAEG..HANGUL SYLLABLE BBAEH {0xBE98, 0xBE98, prLV}, // Lo HANGUL SYLLABLE BBYA {0xBE99, 0xBEB3, prLVT}, // Lo [27] HANGUL SYLLABLE BBYAG..HANGUL SYLLABLE BBYAH {0xBEB4, 0xBEB4, prLV}, // Lo HANGUL SYLLABLE BBYAE {0xBEB5, 0xBECF, prLVT}, // Lo [27] HANGUL SYLLABLE BBYAEG..HANGUL SYLLABLE BBYAEH {0xBED0, 0xBED0, prLV}, // Lo HANGUL SYLLABLE BBEO {0xBED1, 0xBEEB, prLVT}, // Lo [27] HANGUL SYLLABLE BBEOG..HANGUL SYLLABLE BBEOH {0xBEEC, 0xBEEC, prLV}, // Lo HANGUL SYLLABLE BBE {0xBEED, 0xBF07, prLVT}, // Lo [27] HANGUL SYLLABLE BBEG..HANGUL SYLLABLE BBEH {0xBF08, 0xBF08, prLV}, // Lo HANGUL SYLLABLE BBYEO {0xBF09, 0xBF23, prLVT}, // Lo [27] HANGUL SYLLABLE BBYEOG..HANGUL SYLLABLE BBYEOH {0xBF24, 0xBF24, prLV}, // Lo HANGUL SYLLABLE BBYE {0xBF25, 0xBF3F, prLVT}, // Lo [27] HANGUL SYLLABLE BBYEG..HANGUL SYLLABLE BBYEH {0xBF40, 0xBF40, prLV}, // Lo HANGUL SYLLABLE BBO {0xBF41, 0xBF5B, prLVT}, // Lo [27] HANGUL SYLLABLE BBOG..HANGUL SYLLABLE BBOH {0xBF5C, 0xBF5C, prLV}, // Lo HANGUL SYLLABLE BBWA {0xBF5D, 0xBF77, prLVT}, // Lo [27] HANGUL SYLLABLE BBWAG..HANGUL SYLLABLE BBWAH {0xBF78, 0xBF78, prLV}, // Lo HANGUL SYLLABLE BBWAE {0xBF79, 0xBF93, prLVT}, // Lo [27] HANGUL SYLLABLE BBWAEG..HANGUL SYLLABLE BBWAEH {0xBF94, 0xBF94, prLV}, // Lo HANGUL SYLLABLE BBOE {0xBF95, 0xBFAF, prLVT}, // Lo [27] HANGUL SYLLABLE BBOEG..HANGUL SYLLABLE BBOEH {0xBFB0, 0xBFB0, prLV}, // Lo HANGUL SYLLABLE BBYO {0xBFB1, 0xBFCB, prLVT}, // Lo [27] HANGUL SYLLABLE BBYOG..HANGUL SYLLABLE BBYOH {0xBFCC, 0xBFCC, prLV}, // Lo HANGUL SYLLABLE BBU {0xBFCD, 0xBFE7, prLVT}, // Lo [27] HANGUL SYLLABLE BBUG..HANGUL SYLLABLE BBUH {0xBFE8, 0xBFE8, prLV}, // Lo HANGUL SYLLABLE BBWEO {0xBFE9, 0xC003, prLVT}, // Lo [27] HANGUL SYLLABLE BBWEOG..HANGUL SYLLABLE BBWEOH {0xC004, 0xC004, prLV}, // Lo HANGUL SYLLABLE BBWE {0xC005, 0xC01F, prLVT}, // Lo [27] HANGUL SYLLABLE BBWEG..HANGUL SYLLABLE BBWEH {0xC020, 0xC020, prLV}, // Lo HANGUL SYLLABLE BBWI {0xC021, 0xC03B, prLVT}, // Lo [27] HANGUL SYLLABLE BBWIG..HANGUL SYLLABLE BBWIH {0xC03C, 0xC03C, prLV}, // Lo HANGUL SYLLABLE BBYU {0xC03D, 0xC057, prLVT}, // Lo [27] HANGUL SYLLABLE BBYUG..HANGUL SYLLABLE BBYUH {0xC058, 0xC058, prLV}, // Lo HANGUL SYLLABLE BBEU {0xC059, 0xC073, prLVT}, // Lo [27] HANGUL SYLLABLE BBEUG..HANGUL SYLLABLE BBEUH {0xC074, 0xC074, prLV}, // Lo HANGUL SYLLABLE BBYI {0xC075, 0xC08F, prLVT}, // Lo [27] HANGUL SYLLABLE BBYIG..HANGUL SYLLABLE BBYIH {0xC090, 0xC090, prLV}, // Lo HANGUL SYLLABLE BBI {0xC091, 0xC0AB, prLVT}, // Lo [27] HANGUL SYLLABLE BBIG..HANGUL SYLLABLE BBIH {0xC0AC, 0xC0AC, prLV}, // Lo HANGUL SYLLABLE SA {0xC0AD, 0xC0C7, prLVT}, // Lo [27] HANGUL SYLLABLE SAG..HANGUL SYLLABLE SAH {0xC0C8, 0xC0C8, prLV}, // Lo HANGUL SYLLABLE SAE {0xC0C9, 0xC0E3, prLVT}, // Lo [27] HANGUL SYLLABLE SAEG..HANGUL SYLLABLE SAEH {0xC0E4, 0xC0E4, prLV}, // Lo HANGUL SYLLABLE SYA {0xC0E5, 0xC0FF, prLVT}, // Lo [27] HANGUL SYLLABLE SYAG..HANGUL SYLLABLE SYAH {0xC100, 0xC100, prLV}, // Lo HANGUL SYLLABLE SYAE {0xC101, 0xC11B, prLVT}, // Lo [27] HANGUL SYLLABLE SYAEG..HANGUL SYLLABLE SYAEH {0xC11C, 0xC11C, prLV}, // Lo HANGUL SYLLABLE SEO {0xC11D, 0xC137, prLVT}, // Lo [27] HANGUL SYLLABLE SEOG..HANGUL SYLLABLE SEOH {0xC138, 0xC138, prLV}, // Lo HANGUL SYLLABLE SE {0xC139, 0xC153, prLVT}, // Lo [27] HANGUL SYLLABLE SEG..HANGUL SYLLABLE SEH {0xC154, 0xC154, prLV}, // Lo HANGUL SYLLABLE SYEO {0xC155, 0xC16F, prLVT}, // Lo [27] HANGUL SYLLABLE SYEOG..HANGUL SYLLABLE SYEOH {0xC170, 0xC170, prLV}, // Lo HANGUL SYLLABLE SYE {0xC171, 0xC18B, prLVT}, // Lo [27] HANGUL SYLLABLE SYEG..HANGUL SYLLABLE SYEH {0xC18C, 0xC18C, prLV}, // Lo HANGUL SYLLABLE SO {0xC18D, 0xC1A7, prLVT}, // Lo [27] HANGUL SYLLABLE SOG..HANGUL SYLLABLE SOH {0xC1A8, 0xC1A8, prLV}, // Lo HANGUL SYLLABLE SWA {0xC1A9, 0xC1C3, prLVT}, // Lo [27] HANGUL SYLLABLE SWAG..HANGUL SYLLABLE SWAH {0xC1C4, 0xC1C4, prLV}, // Lo HANGUL SYLLABLE SWAE {0xC1C5, 0xC1DF, prLVT}, // Lo [27] HANGUL SYLLABLE SWAEG..HANGUL SYLLABLE SWAEH {0xC1E0, 0xC1E0, prLV}, // Lo HANGUL SYLLABLE SOE {0xC1E1, 0xC1FB, prLVT}, // Lo [27] HANGUL SYLLABLE SOEG..HANGUL SYLLABLE SOEH {0xC1FC, 0xC1FC, prLV}, // Lo HANGUL SYLLABLE SYO {0xC1FD, 0xC217, prLVT}, // Lo [27] HANGUL SYLLABLE SYOG..HANGUL SYLLABLE SYOH {0xC218, 0xC218, prLV}, // Lo HANGUL SYLLABLE SU {0xC219, 0xC233, prLVT}, // Lo [27] HANGUL SYLLABLE SUG..HANGUL SYLLABLE SUH {0xC234, 0xC234, prLV}, // Lo HANGUL SYLLABLE SWEO {0xC235, 0xC24F, prLVT}, // Lo [27] HANGUL SYLLABLE SWEOG..HANGUL SYLLABLE SWEOH {0xC250, 0xC250, prLV}, // Lo HANGUL SYLLABLE SWE {0xC251, 0xC26B, prLVT}, // Lo [27] HANGUL SYLLABLE SWEG..HANGUL SYLLABLE SWEH {0xC26C, 0xC26C, prLV}, // Lo HANGUL SYLLABLE SWI {0xC26D, 0xC287, prLVT}, // Lo [27] HANGUL SYLLABLE SWIG..HANGUL SYLLABLE SWIH {0xC288, 0xC288, prLV}, // Lo HANGUL SYLLABLE SYU {0xC289, 0xC2A3, prLVT}, // Lo [27] HANGUL SYLLABLE SYUG..HANGUL SYLLABLE SYUH {0xC2A4, 0xC2A4, prLV}, // Lo HANGUL SYLLABLE SEU {0xC2A5, 0xC2BF, prLVT}, // Lo [27] HANGUL SYLLABLE SEUG..HANGUL SYLLABLE SEUH {0xC2C0, 0xC2C0, prLV}, // Lo HANGUL SYLLABLE SYI {0xC2C1, 0xC2DB, prLVT}, // Lo [27] HANGUL SYLLABLE SYIG..HANGUL SYLLABLE SYIH {0xC2DC, 0xC2DC, prLV}, // Lo HANGUL SYLLABLE SI {0xC2DD, 0xC2F7, prLVT}, // Lo [27] HANGUL SYLLABLE SIG..HANGUL SYLLABLE SIH {0xC2F8, 0xC2F8, prLV}, // Lo HANGUL SYLLABLE SSA {0xC2F9, 0xC313, prLVT}, // Lo [27] HANGUL SYLLABLE SSAG..HANGUL SYLLABLE SSAH {0xC314, 0xC314, prLV}, // Lo HANGUL SYLLABLE SSAE {0xC315, 0xC32F, prLVT}, // Lo [27] HANGUL SYLLABLE SSAEG..HANGUL SYLLABLE SSAEH {0xC330, 0xC330, prLV}, // Lo HANGUL SYLLABLE SSYA {0xC331, 0xC34B, prLVT}, // Lo [27] HANGUL SYLLABLE SSYAG..HANGUL SYLLABLE SSYAH {0xC34C, 0xC34C, prLV}, // Lo HANGUL SYLLABLE SSYAE {0xC34D, 0xC367, prLVT}, // Lo [27] HANGUL SYLLABLE SSYAEG..HANGUL SYLLABLE SSYAEH {0xC368, 0xC368, prLV}, // Lo HANGUL SYLLABLE SSEO {0xC369, 0xC383, prLVT}, // Lo [27] HANGUL SYLLABLE SSEOG..HANGUL SYLLABLE SSEOH {0xC384, 0xC384, prLV}, // Lo HANGUL SYLLABLE SSE {0xC385, 0xC39F, prLVT}, // Lo [27] HANGUL SYLLABLE SSEG..HANGUL SYLLABLE SSEH {0xC3A0, 0xC3A0, prLV}, // Lo HANGUL SYLLABLE SSYEO {0xC3A1, 0xC3BB, prLVT}, // Lo [27] HANGUL SYLLABLE SSYEOG..HANGUL SYLLABLE SSYEOH {0xC3BC, 0xC3BC, prLV}, // Lo HANGUL SYLLABLE SSYE {0xC3BD, 0xC3D7, prLVT}, // Lo [27] HANGUL SYLLABLE SSYEG..HANGUL SYLLABLE SSYEH {0xC3D8, 0xC3D8, prLV}, // Lo HANGUL SYLLABLE SSO {0xC3D9, 0xC3F3, prLVT}, // Lo [27] HANGUL SYLLABLE SSOG..HANGUL SYLLABLE SSOH {0xC3F4, 0xC3F4, prLV}, // Lo HANGUL SYLLABLE SSWA {0xC3F5, 0xC40F, prLVT}, // Lo [27] HANGUL SYLLABLE SSWAG..HANGUL SYLLABLE SSWAH {0xC410, 0xC410, prLV}, // Lo HANGUL SYLLABLE SSWAE {0xC411, 0xC42B, prLVT}, // Lo [27] HANGUL SYLLABLE SSWAEG..HANGUL SYLLABLE SSWAEH {0xC42C, 0xC42C, prLV}, // Lo HANGUL SYLLABLE SSOE {0xC42D, 0xC447, prLVT}, // Lo [27] HANGUL SYLLABLE SSOEG..HANGUL SYLLABLE SSOEH {0xC448, 0xC448, prLV}, // Lo HANGUL SYLLABLE SSYO {0xC449, 0xC463, prLVT}, // Lo [27] HANGUL SYLLABLE SSYOG..HANGUL SYLLABLE SSYOH {0xC464, 0xC464, prLV}, // Lo HANGUL SYLLABLE SSU {0xC465, 0xC47F, prLVT}, // Lo [27] HANGUL SYLLABLE SSUG..HANGUL SYLLABLE SSUH {0xC480, 0xC480, prLV}, // Lo HANGUL SYLLABLE SSWEO {0xC481, 0xC49B, prLVT}, // Lo [27] HANGUL SYLLABLE SSWEOG..HANGUL SYLLABLE SSWEOH {0xC49C, 0xC49C, prLV}, // Lo HANGUL SYLLABLE SSWE {0xC49D, 0xC4B7, prLVT}, // Lo [27] HANGUL SYLLABLE SSWEG..HANGUL SYLLABLE SSWEH {0xC4B8, 0xC4B8, prLV}, // Lo HANGUL SYLLABLE SSWI {0xC4B9, 0xC4D3, prLVT}, // Lo [27] HANGUL SYLLABLE SSWIG..HANGUL SYLLABLE SSWIH {0xC4D4, 0xC4D4, prLV}, // Lo HANGUL SYLLABLE SSYU {0xC4D5, 0xC4EF, prLVT}, // Lo [27] HANGUL SYLLABLE SSYUG..HANGUL SYLLABLE SSYUH {0xC4F0, 0xC4F0, prLV}, // Lo HANGUL SYLLABLE SSEU {0xC4F1, 0xC50B, prLVT}, // Lo [27] HANGUL SYLLABLE SSEUG..HANGUL SYLLABLE SSEUH {0xC50C, 0xC50C, prLV}, // Lo HANGUL SYLLABLE SSYI {0xC50D, 0xC527, prLVT}, // Lo [27] HANGUL SYLLABLE SSYIG..HANGUL SYLLABLE SSYIH {0xC528, 0xC528, prLV}, // Lo HANGUL SYLLABLE SSI {0xC529, 0xC543, prLVT}, // Lo [27] HANGUL SYLLABLE SSIG..HANGUL SYLLABLE SSIH {0xC544, 0xC544, prLV}, // Lo HANGUL SYLLABLE A {0xC545, 0xC55F, prLVT}, // Lo [27] HANGUL SYLLABLE AG..HANGUL SYLLABLE AH {0xC560, 0xC560, prLV}, // Lo HANGUL SYLLABLE AE {0xC561, 0xC57B, prLVT}, // Lo [27] HANGUL SYLLABLE AEG..HANGUL SYLLABLE AEH {0xC57C, 0xC57C, prLV}, // Lo HANGUL SYLLABLE YA {0xC57D, 0xC597, prLVT}, // Lo [27] HANGUL SYLLABLE YAG..HANGUL SYLLABLE YAH {0xC598, 0xC598, prLV}, // Lo HANGUL SYLLABLE YAE {0xC599, 0xC5B3, prLVT}, // Lo [27] HANGUL SYLLABLE YAEG..HANGUL SYLLABLE YAEH {0xC5B4, 0xC5B4, prLV}, // Lo HANGUL SYLLABLE EO {0xC5B5, 0xC5CF, prLVT}, // Lo [27] HANGUL SYLLABLE EOG..HANGUL SYLLABLE EOH {0xC5D0, 0xC5D0, prLV}, // Lo HANGUL SYLLABLE E {0xC5D1, 0xC5EB, prLVT}, // Lo [27] HANGUL SYLLABLE EG..HANGUL SYLLABLE EH {0xC5EC, 0xC5EC, prLV}, // Lo HANGUL SYLLABLE YEO {0xC5ED, 0xC607, prLVT}, // Lo [27] HANGUL SYLLABLE YEOG..HANGUL SYLLABLE YEOH {0xC608, 0xC608, prLV}, // Lo HANGUL SYLLABLE YE {0xC609, 0xC623, prLVT}, // Lo [27] HANGUL SYLLABLE YEG..HANGUL SYLLABLE YEH {0xC624, 0xC624, prLV}, // Lo HANGUL SYLLABLE O {0xC625, 0xC63F, prLVT}, // Lo [27] HANGUL SYLLABLE OG..HANGUL SYLLABLE OH {0xC640, 0xC640, prLV}, // Lo HANGUL SYLLABLE WA {0xC641, 0xC65B, prLVT}, // Lo [27] HANGUL SYLLABLE WAG..HANGUL SYLLABLE WAH {0xC65C, 0xC65C, prLV}, // Lo HANGUL SYLLABLE WAE {0xC65D, 0xC677, prLVT}, // Lo [27] HANGUL SYLLABLE WAEG..HANGUL SYLLABLE WAEH {0xC678, 0xC678, prLV}, // Lo HANGUL SYLLABLE OE {0xC679, 0xC693, prLVT}, // Lo [27] HANGUL SYLLABLE OEG..HANGUL SYLLABLE OEH {0xC694, 0xC694, prLV}, // Lo HANGUL SYLLABLE YO {0xC695, 0xC6AF, prLVT}, // Lo [27] HANGUL SYLLABLE YOG..HANGUL SYLLABLE YOH {0xC6B0, 0xC6B0, prLV}, // Lo HANGUL SYLLABLE U {0xC6B1, 0xC6CB, prLVT}, // Lo [27] HANGUL SYLLABLE UG..HANGUL SYLLABLE UH {0xC6CC, 0xC6CC, prLV}, // Lo HANGUL SYLLABLE WEO {0xC6CD, 0xC6E7, prLVT}, // Lo [27] HANGUL SYLLABLE WEOG..HANGUL SYLLABLE WEOH {0xC6E8, 0xC6E8, prLV}, // Lo HANGUL SYLLABLE WE {0xC6E9, 0xC703, prLVT}, // Lo [27] HANGUL SYLLABLE WEG..HANGUL SYLLABLE WEH {0xC704, 0xC704, prLV}, // Lo HANGUL SYLLABLE WI {0xC705, 0xC71F, prLVT}, // Lo [27] HANGUL SYLLABLE WIG..HANGUL SYLLABLE WIH {0xC720, 0xC720, prLV}, // Lo HANGUL SYLLABLE YU {0xC721, 0xC73B, prLVT}, // Lo [27] HANGUL SYLLABLE YUG..HANGUL SYLLABLE YUH {0xC73C, 0xC73C, prLV}, // Lo HANGUL SYLLABLE EU {0xC73D, 0xC757, prLVT}, // Lo [27] HANGUL SYLLABLE EUG..HANGUL SYLLABLE EUH {0xC758, 0xC758, prLV}, // Lo HANGUL SYLLABLE YI {0xC759, 0xC773, prLVT}, // Lo [27] HANGUL SYLLABLE YIG..HANGUL SYLLABLE YIH {0xC774, 0xC774, prLV}, // Lo HANGUL SYLLABLE I {0xC775, 0xC78F, prLVT}, // Lo [27] HANGUL SYLLABLE IG..HANGUL SYLLABLE IH {0xC790, 0xC790, prLV}, // Lo HANGUL SYLLABLE JA {0xC791, 0xC7AB, prLVT}, // Lo [27] HANGUL SYLLABLE JAG..HANGUL SYLLABLE JAH {0xC7AC, 0xC7AC, prLV}, // Lo HANGUL SYLLABLE JAE {0xC7AD, 0xC7C7, prLVT}, // Lo [27] HANGUL SYLLABLE JAEG..HANGUL SYLLABLE JAEH {0xC7C8, 0xC7C8, prLV}, // Lo HANGUL SYLLABLE JYA {0xC7C9, 0xC7E3, prLVT}, // Lo [27] HANGUL SYLLABLE JYAG..HANGUL SYLLABLE JYAH {0xC7E4, 0xC7E4, prLV}, // Lo HANGUL SYLLABLE JYAE {0xC7E5, 0xC7FF, prLVT}, // Lo [27] HANGUL SYLLABLE JYAEG..HANGUL SYLLABLE JYAEH {0xC800, 0xC800, prLV}, // Lo HANGUL SYLLABLE JEO {0xC801, 0xC81B, prLVT}, // Lo [27] HANGUL SYLLABLE JEOG..HANGUL SYLLABLE JEOH {0xC81C, 0xC81C, prLV}, // Lo HANGUL SYLLABLE JE {0xC81D, 0xC837, prLVT}, // Lo [27] HANGUL SYLLABLE JEG..HANGUL SYLLABLE JEH {0xC838, 0xC838, prLV}, // Lo HANGUL SYLLABLE JYEO {0xC839, 0xC853, prLVT}, // Lo [27] HANGUL SYLLABLE JYEOG..HANGUL SYLLABLE JYEOH {0xC854, 0xC854, prLV}, // Lo HANGUL SYLLABLE JYE {0xC855, 0xC86F, prLVT}, // Lo [27] HANGUL SYLLABLE JYEG..HANGUL SYLLABLE JYEH {0xC870, 0xC870, prLV}, // Lo HANGUL SYLLABLE JO {0xC871, 0xC88B, prLVT}, // Lo [27] HANGUL SYLLABLE JOG..HANGUL SYLLABLE JOH {0xC88C, 0xC88C, prLV}, // Lo HANGUL SYLLABLE JWA {0xC88D, 0xC8A7, prLVT}, // Lo [27] HANGUL SYLLABLE JWAG..HANGUL SYLLABLE JWAH {0xC8A8, 0xC8A8, prLV}, // Lo HANGUL SYLLABLE JWAE {0xC8A9, 0xC8C3, prLVT}, // Lo [27] HANGUL SYLLABLE JWAEG..HANGUL SYLLABLE JWAEH {0xC8C4, 0xC8C4, prLV}, // Lo HANGUL SYLLABLE JOE {0xC8C5, 0xC8DF, prLVT}, // Lo [27] HANGUL SYLLABLE JOEG..HANGUL SYLLABLE JOEH {0xC8E0, 0xC8E0, prLV}, // Lo HANGUL SYLLABLE JYO {0xC8E1, 0xC8FB, prLVT}, // Lo [27] HANGUL SYLLABLE JYOG..HANGUL SYLLABLE JYOH {0xC8FC, 0xC8FC, prLV}, // Lo HANGUL SYLLABLE JU {0xC8FD, 0xC917, prLVT}, // Lo [27] HANGUL SYLLABLE JUG..HANGUL SYLLABLE JUH {0xC918, 0xC918, prLV}, // Lo HANGUL SYLLABLE JWEO {0xC919, 0xC933, prLVT}, // Lo [27] HANGUL SYLLABLE JWEOG..HANGUL SYLLABLE JWEOH {0xC934, 0xC934, prLV}, // Lo HANGUL SYLLABLE JWE {0xC935, 0xC94F, prLVT}, // Lo [27] HANGUL SYLLABLE JWEG..HANGUL SYLLABLE JWEH {0xC950, 0xC950, prLV}, // Lo HANGUL SYLLABLE JWI {0xC951, 0xC96B, prLVT}, // Lo [27] HANGUL SYLLABLE JWIG..HANGUL SYLLABLE JWIH {0xC96C, 0xC96C, prLV}, // Lo HANGUL SYLLABLE JYU {0xC96D, 0xC987, prLVT}, // Lo [27] HANGUL SYLLABLE JYUG..HANGUL SYLLABLE JYUH {0xC988, 0xC988, prLV}, // Lo HANGUL SYLLABLE JEU {0xC989, 0xC9A3, prLVT}, // Lo [27] HANGUL SYLLABLE JEUG..HANGUL SYLLABLE JEUH {0xC9A4, 0xC9A4, prLV}, // Lo HANGUL SYLLABLE JYI {0xC9A5, 0xC9BF, prLVT}, // Lo [27] HANGUL SYLLABLE JYIG..HANGUL SYLLABLE JYIH {0xC9C0, 0xC9C0, prLV}, // Lo HANGUL SYLLABLE JI {0xC9C1, 0xC9DB, prLVT}, // Lo [27] HANGUL SYLLABLE JIG..HANGUL SYLLABLE JIH {0xC9DC, 0xC9DC, prLV}, // Lo HANGUL SYLLABLE JJA {0xC9DD, 0xC9F7, prLVT}, // Lo [27] HANGUL SYLLABLE JJAG..HANGUL SYLLABLE JJAH {0xC9F8, 0xC9F8, prLV}, // Lo HANGUL SYLLABLE JJAE {0xC9F9, 0xCA13, prLVT}, // Lo [27] HANGUL SYLLABLE JJAEG..HANGUL SYLLABLE JJAEH {0xCA14, 0xCA14, prLV}, // Lo HANGUL SYLLABLE JJYA {0xCA15, 0xCA2F, prLVT}, // Lo [27] HANGUL SYLLABLE JJYAG..HANGUL SYLLABLE JJYAH {0xCA30, 0xCA30, prLV}, // Lo HANGUL SYLLABLE JJYAE {0xCA31, 0xCA4B, prLVT}, // Lo [27] HANGUL SYLLABLE JJYAEG..HANGUL SYLLABLE JJYAEH {0xCA4C, 0xCA4C, prLV}, // Lo HANGUL SYLLABLE JJEO {0xCA4D, 0xCA67, prLVT}, // Lo [27] HANGUL SYLLABLE JJEOG..HANGUL SYLLABLE JJEOH {0xCA68, 0xCA68, prLV}, // Lo HANGUL SYLLABLE JJE {0xCA69, 0xCA83, prLVT}, // Lo [27] HANGUL SYLLABLE JJEG..HANGUL SYLLABLE JJEH {0xCA84, 0xCA84, prLV}, // Lo HANGUL SYLLABLE JJYEO {0xCA85, 0xCA9F, prLVT}, // Lo [27] HANGUL SYLLABLE JJYEOG..HANGUL SYLLABLE JJYEOH {0xCAA0, 0xCAA0, prLV}, // Lo HANGUL SYLLABLE JJYE {0xCAA1, 0xCABB, prLVT}, // Lo [27] HANGUL SYLLABLE JJYEG..HANGUL SYLLABLE JJYEH {0xCABC, 0xCABC, prLV}, // Lo HANGUL SYLLABLE JJO {0xCABD, 0xCAD7, prLVT}, // Lo [27] HANGUL SYLLABLE JJOG..HANGUL SYLLABLE JJOH {0xCAD8, 0xCAD8, prLV}, // Lo HANGUL SYLLABLE JJWA {0xCAD9, 0xCAF3, prLVT}, // Lo [27] HANGUL SYLLABLE JJWAG..HANGUL SYLLABLE JJWAH {0xCAF4, 0xCAF4, prLV}, // Lo HANGUL SYLLABLE JJWAE {0xCAF5, 0xCB0F, prLVT}, // Lo [27] HANGUL SYLLABLE JJWAEG..HANGUL SYLLABLE JJWAEH {0xCB10, 0xCB10, prLV}, // Lo HANGUL SYLLABLE JJOE {0xCB11, 0xCB2B, prLVT}, // Lo [27] HANGUL SYLLABLE JJOEG..HANGUL SYLLABLE JJOEH {0xCB2C, 0xCB2C, prLV}, // Lo HANGUL SYLLABLE JJYO {0xCB2D, 0xCB47, prLVT}, // Lo [27] HANGUL SYLLABLE JJYOG..HANGUL SYLLABLE JJYOH {0xCB48, 0xCB48, prLV}, // Lo HANGUL SYLLABLE JJU {0xCB49, 0xCB63, prLVT}, // Lo [27] HANGUL SYLLABLE JJUG..HANGUL SYLLABLE JJUH {0xCB64, 0xCB64, prLV}, // Lo HANGUL SYLLABLE JJWEO {0xCB65, 0xCB7F, prLVT}, // Lo [27] HANGUL SYLLABLE JJWEOG..HANGUL SYLLABLE JJWEOH {0xCB80, 0xCB80, prLV}, // Lo HANGUL SYLLABLE JJWE {0xCB81, 0xCB9B, prLVT}, // Lo [27] HANGUL SYLLABLE JJWEG..HANGUL SYLLABLE JJWEH {0xCB9C, 0xCB9C, prLV}, // Lo HANGUL SYLLABLE JJWI {0xCB9D, 0xCBB7, prLVT}, // Lo [27] HANGUL SYLLABLE JJWIG..HANGUL SYLLABLE JJWIH {0xCBB8, 0xCBB8, prLV}, // Lo HANGUL SYLLABLE JJYU {0xCBB9, 0xCBD3, prLVT}, // Lo [27] HANGUL SYLLABLE JJYUG..HANGUL SYLLABLE JJYUH {0xCBD4, 0xCBD4, prLV}, // Lo HANGUL SYLLABLE JJEU {0xCBD5, 0xCBEF, prLVT}, // Lo [27] HANGUL SYLLABLE JJEUG..HANGUL SYLLABLE JJEUH {0xCBF0, 0xCBF0, prLV}, // Lo HANGUL SYLLABLE JJYI {0xCBF1, 0xCC0B, prLVT}, // Lo [27] HANGUL SYLLABLE JJYIG..HANGUL SYLLABLE JJYIH {0xCC0C, 0xCC0C, prLV}, // Lo HANGUL SYLLABLE JJI {0xCC0D, 0xCC27, prLVT}, // Lo [27] HANGUL SYLLABLE JJIG..HANGUL SYLLABLE JJIH {0xCC28, 0xCC28, prLV}, // Lo HANGUL SYLLABLE CA {0xCC29, 0xCC43, prLVT}, // Lo [27] HANGUL SYLLABLE CAG..HANGUL SYLLABLE CAH {0xCC44, 0xCC44, prLV}, // Lo HANGUL SYLLABLE CAE {0xCC45, 0xCC5F, prLVT}, // Lo [27] HANGUL SYLLABLE CAEG..HANGUL SYLLABLE CAEH {0xCC60, 0xCC60, prLV}, // Lo HANGUL SYLLABLE CYA {0xCC61, 0xCC7B, prLVT}, // Lo [27] HANGUL SYLLABLE CYAG..HANGUL SYLLABLE CYAH {0xCC7C, 0xCC7C, prLV}, // Lo HANGUL SYLLABLE CYAE {0xCC7D, 0xCC97, prLVT}, // Lo [27] HANGUL SYLLABLE CYAEG..HANGUL SYLLABLE CYAEH {0xCC98, 0xCC98, prLV}, // Lo HANGUL SYLLABLE CEO {0xCC99, 0xCCB3, prLVT}, // Lo [27] HANGUL SYLLABLE CEOG..HANGUL SYLLABLE CEOH {0xCCB4, 0xCCB4, prLV}, // Lo HANGUL SYLLABLE CE {0xCCB5, 0xCCCF, prLVT}, // Lo [27] HANGUL SYLLABLE CEG..HANGUL SYLLABLE CEH {0xCCD0, 0xCCD0, prLV}, // Lo HANGUL SYLLABLE CYEO {0xCCD1, 0xCCEB, prLVT}, // Lo [27] HANGUL SYLLABLE CYEOG..HANGUL SYLLABLE CYEOH {0xCCEC, 0xCCEC, prLV}, // Lo HANGUL SYLLABLE CYE {0xCCED, 0xCD07, prLVT}, // Lo [27] HANGUL SYLLABLE CYEG..HANGUL SYLLABLE CYEH {0xCD08, 0xCD08, prLV}, // Lo HANGUL SYLLABLE CO {0xCD09, 0xCD23, prLVT}, // Lo [27] HANGUL SYLLABLE COG..HANGUL SYLLABLE COH {0xCD24, 0xCD24, prLV}, // Lo HANGUL SYLLABLE CWA {0xCD25, 0xCD3F, prLVT}, // Lo [27] HANGUL SYLLABLE CWAG..HANGUL SYLLABLE CWAH {0xCD40, 0xCD40, prLV}, // Lo HANGUL SYLLABLE CWAE {0xCD41, 0xCD5B, prLVT}, // Lo [27] HANGUL SYLLABLE CWAEG..HANGUL SYLLABLE CWAEH {0xCD5C, 0xCD5C, prLV}, // Lo HANGUL SYLLABLE COE {0xCD5D, 0xCD77, prLVT}, // Lo [27] HANGUL SYLLABLE COEG..HANGUL SYLLABLE COEH {0xCD78, 0xCD78, prLV}, // Lo HANGUL SYLLABLE CYO {0xCD79, 0xCD93, prLVT}, // Lo [27] HANGUL SYLLABLE CYOG..HANGUL SYLLABLE CYOH {0xCD94, 0xCD94, prLV}, // Lo HANGUL SYLLABLE CU {0xCD95, 0xCDAF, prLVT}, // Lo [27] HANGUL SYLLABLE CUG..HANGUL SYLLABLE CUH {0xCDB0, 0xCDB0, prLV}, // Lo HANGUL SYLLABLE CWEO {0xCDB1, 0xCDCB, prLVT}, // Lo [27] HANGUL SYLLABLE CWEOG..HANGUL SYLLABLE CWEOH {0xCDCC, 0xCDCC, prLV}, // Lo HANGUL SYLLABLE CWE {0xCDCD, 0xCDE7, prLVT}, // Lo [27] HANGUL SYLLABLE CWEG..HANGUL SYLLABLE CWEH {0xCDE8, 0xCDE8, prLV}, // Lo HANGUL SYLLABLE CWI {0xCDE9, 0xCE03, prLVT}, // Lo [27] HANGUL SYLLABLE CWIG..HANGUL SYLLABLE CWIH {0xCE04, 0xCE04, prLV}, // Lo HANGUL SYLLABLE CYU {0xCE05, 0xCE1F, prLVT}, // Lo [27] HANGUL SYLLABLE CYUG..HANGUL SYLLABLE CYUH {0xCE20, 0xCE20, prLV}, // Lo HANGUL SYLLABLE CEU {0xCE21, 0xCE3B, prLVT}, // Lo [27] HANGUL SYLLABLE CEUG..HANGUL SYLLABLE CEUH {0xCE3C, 0xCE3C, prLV}, // Lo HANGUL SYLLABLE CYI {0xCE3D, 0xCE57, prLVT}, // Lo [27] HANGUL SYLLABLE CYIG..HANGUL SYLLABLE CYIH {0xCE58, 0xCE58, prLV}, // Lo HANGUL SYLLABLE CI {0xCE59, 0xCE73, prLVT}, // Lo [27] HANGUL SYLLABLE CIG..HANGUL SYLLABLE CIH {0xCE74, 0xCE74, prLV}, // Lo HANGUL SYLLABLE KA {0xCE75, 0xCE8F, prLVT}, // Lo [27] HANGUL SYLLABLE KAG..HANGUL SYLLABLE KAH {0xCE90, 0xCE90, prLV}, // Lo HANGUL SYLLABLE KAE {0xCE91, 0xCEAB, prLVT}, // Lo [27] HANGUL SYLLABLE KAEG..HANGUL SYLLABLE KAEH {0xCEAC, 0xCEAC, prLV}, // Lo HANGUL SYLLABLE KYA {0xCEAD, 0xCEC7, prLVT}, // Lo [27] HANGUL SYLLABLE KYAG..HANGUL SYLLABLE KYAH {0xCEC8, 0xCEC8, prLV}, // Lo HANGUL SYLLABLE KYAE {0xCEC9, 0xCEE3, prLVT}, // Lo [27] HANGUL SYLLABLE KYAEG..HANGUL SYLLABLE KYAEH {0xCEE4, 0xCEE4, prLV}, // Lo HANGUL SYLLABLE KEO {0xCEE5, 0xCEFF, prLVT}, // Lo [27] HANGUL SYLLABLE KEOG..HANGUL SYLLABLE KEOH {0xCF00, 0xCF00, prLV}, // Lo HANGUL SYLLABLE KE {0xCF01, 0xCF1B, prLVT}, // Lo [27] HANGUL SYLLABLE KEG..HANGUL SYLLABLE KEH {0xCF1C, 0xCF1C, prLV}, // Lo HANGUL SYLLABLE KYEO {0xCF1D, 0xCF37, prLVT}, // Lo [27] HANGUL SYLLABLE KYEOG..HANGUL SYLLABLE KYEOH {0xCF38, 0xCF38, prLV}, // Lo HANGUL SYLLABLE KYE {0xCF39, 0xCF53, prLVT}, // Lo [27] HANGUL SYLLABLE KYEG..HANGUL SYLLABLE KYEH {0xCF54, 0xCF54, prLV}, // Lo HANGUL SYLLABLE KO {0xCF55, 0xCF6F, prLVT}, // Lo [27] HANGUL SYLLABLE KOG..HANGUL SYLLABLE KOH {0xCF70, 0xCF70, prLV}, // Lo HANGUL SYLLABLE KWA {0xCF71, 0xCF8B, prLVT}, // Lo [27] HANGUL SYLLABLE KWAG..HANGUL SYLLABLE KWAH {0xCF8C, 0xCF8C, prLV}, // Lo HANGUL SYLLABLE KWAE {0xCF8D, 0xCFA7, prLVT}, // Lo [27] HANGUL SYLLABLE KWAEG..HANGUL SYLLABLE KWAEH {0xCFA8, 0xCFA8, prLV}, // Lo HANGUL SYLLABLE KOE {0xCFA9, 0xCFC3, prLVT}, // Lo [27] HANGUL SYLLABLE KOEG..HANGUL SYLLABLE KOEH {0xCFC4, 0xCFC4, prLV}, // Lo HANGUL SYLLABLE KYO {0xCFC5, 0xCFDF, prLVT}, // Lo [27] HANGUL SYLLABLE KYOG..HANGUL SYLLABLE KYOH {0xCFE0, 0xCFE0, prLV}, // Lo HANGUL SYLLABLE KU {0xCFE1, 0xCFFB, prLVT}, // Lo [27] HANGUL SYLLABLE KUG..HANGUL SYLLABLE KUH {0xCFFC, 0xCFFC, prLV}, // Lo HANGUL SYLLABLE KWEO {0xCFFD, 0xD017, prLVT}, // Lo [27] HANGUL SYLLABLE KWEOG..HANGUL SYLLABLE KWEOH {0xD018, 0xD018, prLV}, // Lo HANGUL SYLLABLE KWE {0xD019, 0xD033, prLVT}, // Lo [27] HANGUL SYLLABLE KWEG..HANGUL SYLLABLE KWEH {0xD034, 0xD034, prLV}, // Lo HANGUL SYLLABLE KWI {0xD035, 0xD04F, prLVT}, // Lo [27] HANGUL SYLLABLE KWIG..HANGUL SYLLABLE KWIH {0xD050, 0xD050, prLV}, // Lo HANGUL SYLLABLE KYU {0xD051, 0xD06B, prLVT}, // Lo [27] HANGUL SYLLABLE KYUG..HANGUL SYLLABLE KYUH {0xD06C, 0xD06C, prLV}, // Lo HANGUL SYLLABLE KEU {0xD06D, 0xD087, prLVT}, // Lo [27] HANGUL SYLLABLE KEUG..HANGUL SYLLABLE KEUH {0xD088, 0xD088, prLV}, // Lo HANGUL SYLLABLE KYI {0xD089, 0xD0A3, prLVT}, // Lo [27] HANGUL SYLLABLE KYIG..HANGUL SYLLABLE KYIH {0xD0A4, 0xD0A4, prLV}, // Lo HANGUL SYLLABLE KI {0xD0A5, 0xD0BF, prLVT}, // Lo [27] HANGUL SYLLABLE KIG..HANGUL SYLLABLE KIH {0xD0C0, 0xD0C0, prLV}, // Lo HANGUL SYLLABLE TA {0xD0C1, 0xD0DB, prLVT}, // Lo [27] HANGUL SYLLABLE TAG..HANGUL SYLLABLE TAH {0xD0DC, 0xD0DC, prLV}, // Lo HANGUL SYLLABLE TAE {0xD0DD, 0xD0F7, prLVT}, // Lo [27] HANGUL SYLLABLE TAEG..HANGUL SYLLABLE TAEH {0xD0F8, 0xD0F8, prLV}, // Lo HANGUL SYLLABLE TYA {0xD0F9, 0xD113, prLVT}, // Lo [27] HANGUL SYLLABLE TYAG..HANGUL SYLLABLE TYAH {0xD114, 0xD114, prLV}, // Lo HANGUL SYLLABLE TYAE {0xD115, 0xD12F, prLVT}, // Lo [27] HANGUL SYLLABLE TYAEG..HANGUL SYLLABLE TYAEH {0xD130, 0xD130, prLV}, // Lo HANGUL SYLLABLE TEO {0xD131, 0xD14B, prLVT}, // Lo [27] HANGUL SYLLABLE TEOG..HANGUL SYLLABLE TEOH {0xD14C, 0xD14C, prLV}, // Lo HANGUL SYLLABLE TE {0xD14D, 0xD167, prLVT}, // Lo [27] HANGUL SYLLABLE TEG..HANGUL SYLLABLE TEH {0xD168, 0xD168, prLV}, // Lo HANGUL SYLLABLE TYEO {0xD169, 0xD183, prLVT}, // Lo [27] HANGUL SYLLABLE TYEOG..HANGUL SYLLABLE TYEOH {0xD184, 0xD184, prLV}, // Lo HANGUL SYLLABLE TYE {0xD185, 0xD19F, prLVT}, // Lo [27] HANGUL SYLLABLE TYEG..HANGUL SYLLABLE TYEH {0xD1A0, 0xD1A0, prLV}, // Lo HANGUL SYLLABLE TO {0xD1A1, 0xD1BB, prLVT}, // Lo [27] HANGUL SYLLABLE TOG..HANGUL SYLLABLE TOH {0xD1BC, 0xD1BC, prLV}, // Lo HANGUL SYLLABLE TWA {0xD1BD, 0xD1D7, prLVT}, // Lo [27] HANGUL SYLLABLE TWAG..HANGUL SYLLABLE TWAH {0xD1D8, 0xD1D8, prLV}, // Lo HANGUL SYLLABLE TWAE {0xD1D9, 0xD1F3, prLVT}, // Lo [27] HANGUL SYLLABLE TWAEG..HANGUL SYLLABLE TWAEH {0xD1F4, 0xD1F4, prLV}, // Lo HANGUL SYLLABLE TOE {0xD1F5, 0xD20F, prLVT}, // Lo [27] HANGUL SYLLABLE TOEG..HANGUL SYLLABLE TOEH {0xD210, 0xD210, prLV}, // Lo HANGUL SYLLABLE TYO {0xD211, 0xD22B, prLVT}, // Lo [27] HANGUL SYLLABLE TYOG..HANGUL SYLLABLE TYOH {0xD22C, 0xD22C, prLV}, // Lo HANGUL SYLLABLE TU {0xD22D, 0xD247, prLVT}, // Lo [27] HANGUL SYLLABLE TUG..HANGUL SYLLABLE TUH {0xD248, 0xD248, prLV}, // Lo HANGUL SYLLABLE TWEO {0xD249, 0xD263, prLVT}, // Lo [27] HANGUL SYLLABLE TWEOG..HANGUL SYLLABLE TWEOH {0xD264, 0xD264, prLV}, // Lo HANGUL SYLLABLE TWE {0xD265, 0xD27F, prLVT}, // Lo [27] HANGUL SYLLABLE TWEG..HANGUL SYLLABLE TWEH {0xD280, 0xD280, prLV}, // Lo HANGUL SYLLABLE TWI {0xD281, 0xD29B, prLVT}, // Lo [27] HANGUL SYLLABLE TWIG..HANGUL SYLLABLE TWIH {0xD29C, 0xD29C, prLV}, // Lo HANGUL SYLLABLE TYU {0xD29D, 0xD2B7, prLVT}, // Lo [27] HANGUL SYLLABLE TYUG..HANGUL SYLLABLE TYUH {0xD2B8, 0xD2B8, prLV}, // Lo HANGUL SYLLABLE TEU {0xD2B9, 0xD2D3, prLVT}, // Lo [27] HANGUL SYLLABLE TEUG..HANGUL SYLLABLE TEUH {0xD2D4, 0xD2D4, prLV}, // Lo HANGUL SYLLABLE TYI {0xD2D5, 0xD2EF, prLVT}, // Lo [27] HANGUL SYLLABLE TYIG..HANGUL SYLLABLE TYIH {0xD2F0, 0xD2F0, prLV}, // Lo HANGUL SYLLABLE TI {0xD2F1, 0xD30B, prLVT}, // Lo [27] HANGUL SYLLABLE TIG..HANGUL SYLLABLE TIH {0xD30C, 0xD30C, prLV}, // Lo HANGUL SYLLABLE PA {0xD30D, 0xD327, prLVT}, // Lo [27] HANGUL SYLLABLE PAG..HANGUL SYLLABLE PAH {0xD328, 0xD328, prLV}, // Lo HANGUL SYLLABLE PAE {0xD329, 0xD343, prLVT}, // Lo [27] HANGUL SYLLABLE PAEG..HANGUL SYLLABLE PAEH {0xD344, 0xD344, prLV}, // Lo HANGUL SYLLABLE PYA {0xD345, 0xD35F, prLVT}, // Lo [27] HANGUL SYLLABLE PYAG..HANGUL SYLLABLE PYAH {0xD360, 0xD360, prLV}, // Lo HANGUL SYLLABLE PYAE {0xD361, 0xD37B, prLVT}, // Lo [27] HANGUL SYLLABLE PYAEG..HANGUL SYLLABLE PYAEH {0xD37C, 0xD37C, prLV}, // Lo HANGUL SYLLABLE PEO {0xD37D, 0xD397, prLVT}, // Lo [27] HANGUL SYLLABLE PEOG..HANGUL SYLLABLE PEOH {0xD398, 0xD398, prLV}, // Lo HANGUL SYLLABLE PE {0xD399, 0xD3B3, prLVT}, // Lo [27] HANGUL SYLLABLE PEG..HANGUL SYLLABLE PEH {0xD3B4, 0xD3B4, prLV}, // Lo HANGUL SYLLABLE PYEO {0xD3B5, 0xD3CF, prLVT}, // Lo [27] HANGUL SYLLABLE PYEOG..HANGUL SYLLABLE PYEOH {0xD3D0, 0xD3D0, prLV}, // Lo HANGUL SYLLABLE PYE {0xD3D1, 0xD3EB, prLVT}, // Lo [27] HANGUL SYLLABLE PYEG..HANGUL SYLLABLE PYEH {0xD3EC, 0xD3EC, prLV}, // Lo HANGUL SYLLABLE PO {0xD3ED, 0xD407, prLVT}, // Lo [27] HANGUL SYLLABLE POG..HANGUL SYLLABLE POH {0xD408, 0xD408, prLV}, // Lo HANGUL SYLLABLE PWA {0xD409, 0xD423, prLVT}, // Lo [27] HANGUL SYLLABLE PWAG..HANGUL SYLLABLE PWAH {0xD424, 0xD424, prLV}, // Lo HANGUL SYLLABLE PWAE {0xD425, 0xD43F, prLVT}, // Lo [27] HANGUL SYLLABLE PWAEG..HANGUL SYLLABLE PWAEH {0xD440, 0xD440, prLV}, // Lo HANGUL SYLLABLE POE {0xD441, 0xD45B, prLVT}, // Lo [27] HANGUL SYLLABLE POEG..HANGUL SYLLABLE POEH {0xD45C, 0xD45C, prLV}, // Lo HANGUL SYLLABLE PYO {0xD45D, 0xD477, prLVT}, // Lo [27] HANGUL SYLLABLE PYOG..HANGUL SYLLABLE PYOH {0xD478, 0xD478, prLV}, // Lo HANGUL SYLLABLE PU {0xD479, 0xD493, prLVT}, // Lo [27] HANGUL SYLLABLE PUG..HANGUL SYLLABLE PUH {0xD494, 0xD494, prLV}, // Lo HANGUL SYLLABLE PWEO {0xD495, 0xD4AF, prLVT}, // Lo [27] HANGUL SYLLABLE PWEOG..HANGUL SYLLABLE PWEOH {0xD4B0, 0xD4B0, prLV}, // Lo HANGUL SYLLABLE PWE {0xD4B1, 0xD4CB, prLVT}, // Lo [27] HANGUL SYLLABLE PWEG..HANGUL SYLLABLE PWEH {0xD4CC, 0xD4CC, prLV}, // Lo HANGUL SYLLABLE PWI {0xD4CD, 0xD4E7, prLVT}, // Lo [27] HANGUL SYLLABLE PWIG..HANGUL SYLLABLE PWIH {0xD4E8, 0xD4E8, prLV}, // Lo HANGUL SYLLABLE PYU {0xD4E9, 0xD503, prLVT}, // Lo [27] HANGUL SYLLABLE PYUG..HANGUL SYLLABLE PYUH {0xD504, 0xD504, prLV}, // Lo HANGUL SYLLABLE PEU {0xD505, 0xD51F, prLVT}, // Lo [27] HANGUL SYLLABLE PEUG..HANGUL SYLLABLE PEUH {0xD520, 0xD520, prLV}, // Lo HANGUL SYLLABLE PYI {0xD521, 0xD53B, prLVT}, // Lo [27] HANGUL SYLLABLE PYIG..HANGUL SYLLABLE PYIH {0xD53C, 0xD53C, prLV}, // Lo HANGUL SYLLABLE PI {0xD53D, 0xD557, prLVT}, // Lo [27] HANGUL SYLLABLE PIG..HANGUL SYLLABLE PIH {0xD558, 0xD558, prLV}, // Lo HANGUL SYLLABLE HA {0xD559, 0xD573, prLVT}, // Lo [27] HANGUL SYLLABLE HAG..HANGUL SYLLABLE HAH {0xD574, 0xD574, prLV}, // Lo HANGUL SYLLABLE HAE {0xD575, 0xD58F, prLVT}, // Lo [27] HANGUL SYLLABLE HAEG..HANGUL SYLLABLE HAEH {0xD590, 0xD590, prLV}, // Lo HANGUL SYLLABLE HYA {0xD591, 0xD5AB, prLVT}, // Lo [27] HANGUL SYLLABLE HYAG..HANGUL SYLLABLE HYAH {0xD5AC, 0xD5AC, prLV}, // Lo HANGUL SYLLABLE HYAE {0xD5AD, 0xD5C7, prLVT}, // Lo [27] HANGUL SYLLABLE HYAEG..HANGUL SYLLABLE HYAEH {0xD5C8, 0xD5C8, prLV}, // Lo HANGUL SYLLABLE HEO {0xD5C9, 0xD5E3, prLVT}, // Lo [27] HANGUL SYLLABLE HEOG..HANGUL SYLLABLE HEOH {0xD5E4, 0xD5E4, prLV}, // Lo HANGUL SYLLABLE HE {0xD5E5, 0xD5FF, prLVT}, // Lo [27] HANGUL SYLLABLE HEG..HANGUL SYLLABLE HEH {0xD600, 0xD600, prLV}, // Lo HANGUL SYLLABLE HYEO {0xD601, 0xD61B, prLVT}, // Lo [27] HANGUL SYLLABLE HYEOG..HANGUL SYLLABLE HYEOH {0xD61C, 0xD61C, prLV}, // Lo HANGUL SYLLABLE HYE {0xD61D, 0xD637, prLVT}, // Lo [27] HANGUL SYLLABLE HYEG..HANGUL SYLLABLE HYEH {0xD638, 0xD638, prLV}, // Lo HANGUL SYLLABLE HO {0xD639, 0xD653, prLVT}, // Lo [27] HANGUL SYLLABLE HOG..HANGUL SYLLABLE HOH {0xD654, 0xD654, prLV}, // Lo HANGUL SYLLABLE HWA {0xD655, 0xD66F, prLVT}, // Lo [27] HANGUL SYLLABLE HWAG..HANGUL SYLLABLE HWAH {0xD670, 0xD670, prLV}, // Lo HANGUL SYLLABLE HWAE {0xD671, 0xD68B, prLVT}, // Lo [27] HANGUL SYLLABLE HWAEG..HANGUL SYLLABLE HWAEH {0xD68C, 0xD68C, prLV}, // Lo HANGUL SYLLABLE HOE {0xD68D, 0xD6A7, prLVT}, // Lo [27] HANGUL SYLLABLE HOEG..HANGUL SYLLABLE HOEH {0xD6A8, 0xD6A8, prLV}, // Lo HANGUL SYLLABLE HYO {0xD6A9, 0xD6C3, prLVT}, // Lo [27] HANGUL SYLLABLE HYOG..HANGUL SYLLABLE HYOH {0xD6C4, 0xD6C4, prLV}, // Lo HANGUL SYLLABLE HU {0xD6C5, 0xD6DF, prLVT}, // Lo [27] HANGUL SYLLABLE HUG..HANGUL SYLLABLE HUH {0xD6E0, 0xD6E0, prLV}, // Lo HANGUL SYLLABLE HWEO {0xD6E1, 0xD6FB, prLVT}, // Lo [27] HANGUL SYLLABLE HWEOG..HANGUL SYLLABLE HWEOH {0xD6FC, 0xD6FC, prLV}, // Lo HANGUL SYLLABLE HWE {0xD6FD, 0xD717, prLVT}, // Lo [27] HANGUL SYLLABLE HWEG..HANGUL SYLLABLE HWEH {0xD718, 0xD718, prLV}, // Lo HANGUL SYLLABLE HWI {0xD719, 0xD733, prLVT}, // Lo [27] HANGUL SYLLABLE HWIG..HANGUL SYLLABLE HWIH {0xD734, 0xD734, prLV}, // Lo HANGUL SYLLABLE HYU {0xD735, 0xD74F, prLVT}, // Lo [27] HANGUL SYLLABLE HYUG..HANGUL SYLLABLE HYUH {0xD750, 0xD750, prLV}, // Lo HANGUL SYLLABLE HEU {0xD751, 0xD76B, prLVT}, // Lo [27] HANGUL SYLLABLE HEUG..HANGUL SYLLABLE HEUH {0xD76C, 0xD76C, prLV}, // Lo HANGUL SYLLABLE HYI {0xD76D, 0xD787, prLVT}, // Lo [27] HANGUL SYLLABLE HYIG..HANGUL SYLLABLE HYIH {0xD788, 0xD788, prLV}, // Lo HANGUL SYLLABLE HI {0xD789, 0xD7A3, prLVT}, // Lo [27] HANGUL SYLLABLE HIG..HANGUL SYLLABLE HIH {0xD7B0, 0xD7C6, prV}, // Lo [23] HANGUL JUNGSEONG O-YEO..HANGUL JUNGSEONG ARAEA-E {0xD7CB, 0xD7FB, prT}, // Lo [49] HANGUL JONGSEONG NIEUN-RIEUL..HANGUL JONGSEONG PHIEUPH-THIEUTH {0xFB1E, 0xFB1E, prExtend}, // Mn HEBREW POINT JUDEO-SPANISH VARIKA {0xFE00, 0xFE0F, prExtend}, // Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16 {0xFE20, 0xFE2F, prExtend}, // Mn [16] COMBINING LIGATURE LEFT HALF..COMBINING CYRILLIC TITLO RIGHT HALF {0xFEFF, 0xFEFF, prControl}, // Cf ZERO WIDTH NO-BREAK SPACE {0xFF9E, 0xFF9F, prExtend}, // Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK {0xFFF0, 0xFFF8, prControl}, // Cn [9] .. {0xFFF9, 0xFFFB, prControl}, // Cf [3] INTERLINEAR ANNOTATION ANCHOR..INTERLINEAR ANNOTATION TERMINATOR {0x101FD, 0x101FD, prExtend}, // Mn PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE {0x102E0, 0x102E0, prExtend}, // Mn COPTIC EPACT THOUSANDS MARK {0x10376, 0x1037A, prExtend}, // Mn [5] COMBINING OLD PERMIC LETTER AN..COMBINING OLD PERMIC LETTER SII {0x10A01, 0x10A03, prExtend}, // Mn [3] KHAROSHTHI VOWEL SIGN I..KHAROSHTHI VOWEL SIGN VOCALIC R {0x10A05, 0x10A06, prExtend}, // Mn [2] KHAROSHTHI VOWEL SIGN E..KHAROSHTHI VOWEL SIGN O {0x10A0C, 0x10A0F, prExtend}, // Mn [4] KHAROSHTHI VOWEL LENGTH MARK..KHAROSHTHI SIGN VISARGA {0x10A38, 0x10A3A, prExtend}, // Mn [3] KHAROSHTHI SIGN BAR ABOVE..KHAROSHTHI SIGN DOT BELOW {0x10A3F, 0x10A3F, prExtend}, // Mn KHAROSHTHI VIRAMA {0x10AE5, 0x10AE6, prExtend}, // Mn [2] MANICHAEAN ABBREVIATION MARK ABOVE..MANICHAEAN ABBREVIATION MARK BELOW {0x10D24, 0x10D27, prExtend}, // Mn [4] HANIFI ROHINGYA SIGN HARBAHAY..HANIFI ROHINGYA SIGN TASSI {0x10F46, 0x10F50, prExtend}, // Mn [11] SOGDIAN COMBINING DOT BELOW..SOGDIAN COMBINING STROKE BELOW {0x11000, 0x11000, prSpacingMark}, // Mc BRAHMI SIGN CANDRABINDU {0x11001, 0x11001, prExtend}, // Mn BRAHMI SIGN ANUSVARA {0x11002, 0x11002, prSpacingMark}, // Mc BRAHMI SIGN VISARGA {0x11038, 0x11046, prExtend}, // Mn [15] BRAHMI VOWEL SIGN AA..BRAHMI VIRAMA {0x1107F, 0x11081, prExtend}, // Mn [3] BRAHMI NUMBER JOINER..KAITHI SIGN ANUSVARA {0x11082, 0x11082, prSpacingMark}, // Mc KAITHI SIGN VISARGA {0x110B0, 0x110B2, prSpacingMark}, // Mc [3] KAITHI VOWEL SIGN AA..KAITHI VOWEL SIGN II {0x110B3, 0x110B6, prExtend}, // Mn [4] KAITHI VOWEL SIGN U..KAITHI VOWEL SIGN AI {0x110B7, 0x110B8, prSpacingMark}, // Mc [2] KAITHI VOWEL SIGN O..KAITHI VOWEL SIGN AU {0x110B9, 0x110BA, prExtend}, // Mn [2] KAITHI SIGN VIRAMA..KAITHI SIGN NUKTA {0x110BD, 0x110BD, prPreprend}, // Cf KAITHI NUMBER SIGN {0x110CD, 0x110CD, prPreprend}, // Cf KAITHI NUMBER SIGN ABOVE {0x11100, 0x11102, prExtend}, // Mn [3] CHAKMA SIGN CANDRABINDU..CHAKMA SIGN VISARGA {0x11127, 0x1112B, prExtend}, // Mn [5] CHAKMA VOWEL SIGN A..CHAKMA VOWEL SIGN UU {0x1112C, 0x1112C, prSpacingMark}, // Mc CHAKMA VOWEL SIGN E {0x1112D, 0x11134, prExtend}, // Mn [8] CHAKMA VOWEL SIGN AI..CHAKMA MAAYYAA {0x11145, 0x11146, prSpacingMark}, // Mc [2] CHAKMA VOWEL SIGN AA..CHAKMA VOWEL SIGN EI {0x11173, 0x11173, prExtend}, // Mn MAHAJANI SIGN NUKTA {0x11180, 0x11181, prExtend}, // Mn [2] SHARADA SIGN CANDRABINDU..SHARADA SIGN ANUSVARA {0x11182, 0x11182, prSpacingMark}, // Mc SHARADA SIGN VISARGA {0x111B3, 0x111B5, prSpacingMark}, // Mc [3] SHARADA VOWEL SIGN AA..SHARADA VOWEL SIGN II {0x111B6, 0x111BE, prExtend}, // Mn [9] SHARADA VOWEL SIGN U..SHARADA VOWEL SIGN O {0x111BF, 0x111C0, prSpacingMark}, // Mc [2] SHARADA VOWEL SIGN AU..SHARADA SIGN VIRAMA {0x111C2, 0x111C3, prPreprend}, // Lo [2] SHARADA SIGN JIHVAMULIYA..SHARADA SIGN UPADHMANIYA {0x111C9, 0x111CC, prExtend}, // Mn [4] SHARADA SANDHI MARK..SHARADA EXTRA SHORT VOWEL MARK {0x1122C, 0x1122E, prSpacingMark}, // Mc [3] KHOJKI VOWEL SIGN AA..KHOJKI VOWEL SIGN II {0x1122F, 0x11231, prExtend}, // Mn [3] KHOJKI VOWEL SIGN U..KHOJKI VOWEL SIGN AI {0x11232, 0x11233, prSpacingMark}, // Mc [2] KHOJKI VOWEL SIGN O..KHOJKI VOWEL SIGN AU {0x11234, 0x11234, prExtend}, // Mn KHOJKI SIGN ANUSVARA {0x11235, 0x11235, prSpacingMark}, // Mc KHOJKI SIGN VIRAMA {0x11236, 0x11237, prExtend}, // Mn [2] KHOJKI SIGN NUKTA..KHOJKI SIGN SHADDA {0x1123E, 0x1123E, prExtend}, // Mn KHOJKI SIGN SUKUN {0x112DF, 0x112DF, prExtend}, // Mn KHUDAWADI SIGN ANUSVARA {0x112E0, 0x112E2, prSpacingMark}, // Mc [3] KHUDAWADI VOWEL SIGN AA..KHUDAWADI VOWEL SIGN II {0x112E3, 0x112EA, prExtend}, // Mn [8] KHUDAWADI VOWEL SIGN U..KHUDAWADI SIGN VIRAMA {0x11300, 0x11301, prExtend}, // Mn [2] GRANTHA SIGN COMBINING ANUSVARA ABOVE..GRANTHA SIGN CANDRABINDU {0x11302, 0x11303, prSpacingMark}, // Mc [2] GRANTHA SIGN ANUSVARA..GRANTHA SIGN VISARGA {0x1133B, 0x1133C, prExtend}, // Mn [2] COMBINING BINDU BELOW..GRANTHA SIGN NUKTA {0x1133E, 0x1133E, prExtend}, // Mc GRANTHA VOWEL SIGN AA {0x1133F, 0x1133F, prSpacingMark}, // Mc GRANTHA VOWEL SIGN I {0x11340, 0x11340, prExtend}, // Mn GRANTHA VOWEL SIGN II {0x11341, 0x11344, prSpacingMark}, // Mc [4] GRANTHA VOWEL SIGN U..GRANTHA VOWEL SIGN VOCALIC RR {0x11347, 0x11348, prSpacingMark}, // Mc [2] GRANTHA VOWEL SIGN EE..GRANTHA VOWEL SIGN AI {0x1134B, 0x1134D, prSpacingMark}, // Mc [3] GRANTHA VOWEL SIGN OO..GRANTHA SIGN VIRAMA {0x11357, 0x11357, prExtend}, // Mc GRANTHA AU LENGTH MARK {0x11362, 0x11363, prSpacingMark}, // Mc [2] GRANTHA VOWEL SIGN VOCALIC L..GRANTHA VOWEL SIGN VOCALIC LL {0x11366, 0x1136C, prExtend}, // Mn [7] COMBINING GRANTHA DIGIT ZERO..COMBINING GRANTHA DIGIT SIX {0x11370, 0x11374, prExtend}, // Mn [5] COMBINING GRANTHA LETTER A..COMBINING GRANTHA LETTER PA {0x11435, 0x11437, prSpacingMark}, // Mc [3] NEWA VOWEL SIGN AA..NEWA VOWEL SIGN II {0x11438, 0x1143F, prExtend}, // Mn [8] NEWA VOWEL SIGN U..NEWA VOWEL SIGN AI {0x11440, 0x11441, prSpacingMark}, // Mc [2] NEWA VOWEL SIGN O..NEWA VOWEL SIGN AU {0x11442, 0x11444, prExtend}, // Mn [3] NEWA SIGN VIRAMA..NEWA SIGN ANUSVARA {0x11445, 0x11445, prSpacingMark}, // Mc NEWA SIGN VISARGA {0x11446, 0x11446, prExtend}, // Mn NEWA SIGN NUKTA {0x1145E, 0x1145E, prExtend}, // Mn NEWA SANDHI MARK {0x114B0, 0x114B0, prExtend}, // Mc TIRHUTA VOWEL SIGN AA {0x114B1, 0x114B2, prSpacingMark}, // Mc [2] TIRHUTA VOWEL SIGN I..TIRHUTA VOWEL SIGN II {0x114B3, 0x114B8, prExtend}, // Mn [6] TIRHUTA VOWEL SIGN U..TIRHUTA VOWEL SIGN VOCALIC LL {0x114B9, 0x114B9, prSpacingMark}, // Mc TIRHUTA VOWEL SIGN E {0x114BA, 0x114BA, prExtend}, // Mn TIRHUTA VOWEL SIGN SHORT E {0x114BB, 0x114BC, prSpacingMark}, // Mc [2] TIRHUTA VOWEL SIGN AI..TIRHUTA VOWEL SIGN O {0x114BD, 0x114BD, prExtend}, // Mc TIRHUTA VOWEL SIGN SHORT O {0x114BE, 0x114BE, prSpacingMark}, // Mc TIRHUTA VOWEL SIGN AU {0x114BF, 0x114C0, prExtend}, // Mn [2] TIRHUTA SIGN CANDRABINDU..TIRHUTA SIGN ANUSVARA {0x114C1, 0x114C1, prSpacingMark}, // Mc TIRHUTA SIGN VISARGA {0x114C2, 0x114C3, prExtend}, // Mn [2] TIRHUTA SIGN VIRAMA..TIRHUTA SIGN NUKTA {0x115AF, 0x115AF, prExtend}, // Mc SIDDHAM VOWEL SIGN AA {0x115B0, 0x115B1, prSpacingMark}, // Mc [2] SIDDHAM VOWEL SIGN I..SIDDHAM VOWEL SIGN II {0x115B2, 0x115B5, prExtend}, // Mn [4] SIDDHAM VOWEL SIGN U..SIDDHAM VOWEL SIGN VOCALIC RR {0x115B8, 0x115BB, prSpacingMark}, // Mc [4] SIDDHAM VOWEL SIGN E..SIDDHAM VOWEL SIGN AU {0x115BC, 0x115BD, prExtend}, // Mn [2] SIDDHAM SIGN CANDRABINDU..SIDDHAM SIGN ANUSVARA {0x115BE, 0x115BE, prSpacingMark}, // Mc SIDDHAM SIGN VISARGA {0x115BF, 0x115C0, prExtend}, // Mn [2] SIDDHAM SIGN VIRAMA..SIDDHAM SIGN NUKTA {0x115DC, 0x115DD, prExtend}, // Mn [2] SIDDHAM VOWEL SIGN ALTERNATE U..SIDDHAM VOWEL SIGN ALTERNATE UU {0x11630, 0x11632, prSpacingMark}, // Mc [3] MODI VOWEL SIGN AA..MODI VOWEL SIGN II {0x11633, 0x1163A, prExtend}, // Mn [8] MODI VOWEL SIGN U..MODI VOWEL SIGN AI {0x1163B, 0x1163C, prSpacingMark}, // Mc [2] MODI VOWEL SIGN O..MODI VOWEL SIGN AU {0x1163D, 0x1163D, prExtend}, // Mn MODI SIGN ANUSVARA {0x1163E, 0x1163E, prSpacingMark}, // Mc MODI SIGN VISARGA {0x1163F, 0x11640, prExtend}, // Mn [2] MODI SIGN VIRAMA..MODI SIGN ARDHACANDRA {0x116AB, 0x116AB, prExtend}, // Mn TAKRI SIGN ANUSVARA {0x116AC, 0x116AC, prSpacingMark}, // Mc TAKRI SIGN VISARGA {0x116AD, 0x116AD, prExtend}, // Mn TAKRI VOWEL SIGN AA {0x116AE, 0x116AF, prSpacingMark}, // Mc [2] TAKRI VOWEL SIGN I..TAKRI VOWEL SIGN II {0x116B0, 0x116B5, prExtend}, // Mn [6] TAKRI VOWEL SIGN U..TAKRI VOWEL SIGN AU {0x116B6, 0x116B6, prSpacingMark}, // Mc TAKRI SIGN VIRAMA {0x116B7, 0x116B7, prExtend}, // Mn TAKRI SIGN NUKTA {0x1171D, 0x1171F, prExtend}, // Mn [3] AHOM CONSONANT SIGN MEDIAL LA..AHOM CONSONANT SIGN MEDIAL LIGATING RA {0x11720, 0x11721, prSpacingMark}, // Mc [2] AHOM VOWEL SIGN A..AHOM VOWEL SIGN AA {0x11722, 0x11725, prExtend}, // Mn [4] AHOM VOWEL SIGN I..AHOM VOWEL SIGN UU {0x11726, 0x11726, prSpacingMark}, // Mc AHOM VOWEL SIGN E {0x11727, 0x1172B, prExtend}, // Mn [5] AHOM VOWEL SIGN AW..AHOM SIGN KILLER {0x1182C, 0x1182E, prSpacingMark}, // Mc [3] DOGRA VOWEL SIGN AA..DOGRA VOWEL SIGN II {0x1182F, 0x11837, prExtend}, // Mn [9] DOGRA VOWEL SIGN U..DOGRA SIGN ANUSVARA {0x11838, 0x11838, prSpacingMark}, // Mc DOGRA SIGN VISARGA {0x11839, 0x1183A, prExtend}, // Mn [2] DOGRA SIGN VIRAMA..DOGRA SIGN NUKTA {0x119D1, 0x119D3, prSpacingMark}, // Mc [3] NANDINAGARI VOWEL SIGN AA..NANDINAGARI VOWEL SIGN II {0x119D4, 0x119D7, prExtend}, // Mn [4] NANDINAGARI VOWEL SIGN U..NANDINAGARI VOWEL SIGN VOCALIC RR {0x119DA, 0x119DB, prExtend}, // Mn [2] NANDINAGARI VOWEL SIGN E..NANDINAGARI VOWEL SIGN AI {0x119DC, 0x119DF, prSpacingMark}, // Mc [4] NANDINAGARI VOWEL SIGN O..NANDINAGARI SIGN VISARGA {0x119E0, 0x119E0, prExtend}, // Mn NANDINAGARI SIGN VIRAMA {0x119E4, 0x119E4, prSpacingMark}, // Mc NANDINAGARI VOWEL SIGN PRISHTHAMATRA E {0x11A01, 0x11A0A, prExtend}, // Mn [10] ZANABAZAR SQUARE VOWEL SIGN I..ZANABAZAR SQUARE VOWEL LENGTH MARK {0x11A33, 0x11A38, prExtend}, // Mn [6] ZANABAZAR SQUARE FINAL CONSONANT MARK..ZANABAZAR SQUARE SIGN ANUSVARA {0x11A39, 0x11A39, prSpacingMark}, // Mc ZANABAZAR SQUARE SIGN VISARGA {0x11A3A, 0x11A3A, prPreprend}, // Lo ZANABAZAR SQUARE CLUSTER-INITIAL LETTER RA {0x11A3B, 0x11A3E, prExtend}, // Mn [4] ZANABAZAR SQUARE CLUSTER-FINAL LETTER YA..ZANABAZAR SQUARE CLUSTER-FINAL LETTER VA {0x11A47, 0x11A47, prExtend}, // Mn ZANABAZAR SQUARE SUBJOINER {0x11A51, 0x11A56, prExtend}, // Mn [6] SOYOMBO VOWEL SIGN I..SOYOMBO VOWEL SIGN OE {0x11A57, 0x11A58, prSpacingMark}, // Mc [2] SOYOMBO VOWEL SIGN AI..SOYOMBO VOWEL SIGN AU {0x11A59, 0x11A5B, prExtend}, // Mn [3] SOYOMBO VOWEL SIGN VOCALIC R..SOYOMBO VOWEL LENGTH MARK {0x11A84, 0x11A89, prPreprend}, // Lo [6] SOYOMBO SIGN JIHVAMULIYA..SOYOMBO CLUSTER-INITIAL LETTER SA {0x11A8A, 0x11A96, prExtend}, // Mn [13] SOYOMBO FINAL CONSONANT SIGN G..SOYOMBO SIGN ANUSVARA {0x11A97, 0x11A97, prSpacingMark}, // Mc SOYOMBO SIGN VISARGA {0x11A98, 0x11A99, prExtend}, // Mn [2] SOYOMBO GEMINATION MARK..SOYOMBO SUBJOINER {0x11C2F, 0x11C2F, prSpacingMark}, // Mc BHAIKSUKI VOWEL SIGN AA {0x11C30, 0x11C36, prExtend}, // Mn [7] BHAIKSUKI VOWEL SIGN I..BHAIKSUKI VOWEL SIGN VOCALIC L {0x11C38, 0x11C3D, prExtend}, // Mn [6] BHAIKSUKI VOWEL SIGN E..BHAIKSUKI SIGN ANUSVARA {0x11C3E, 0x11C3E, prSpacingMark}, // Mc BHAIKSUKI SIGN VISARGA {0x11C3F, 0x11C3F, prExtend}, // Mn BHAIKSUKI SIGN VIRAMA {0x11C92, 0x11CA7, prExtend}, // Mn [22] MARCHEN SUBJOINED LETTER KA..MARCHEN SUBJOINED LETTER ZA {0x11CA9, 0x11CA9, prSpacingMark}, // Mc MARCHEN SUBJOINED LETTER YA {0x11CAA, 0x11CB0, prExtend}, // Mn [7] MARCHEN SUBJOINED LETTER RA..MARCHEN VOWEL SIGN AA {0x11CB1, 0x11CB1, prSpacingMark}, // Mc MARCHEN VOWEL SIGN I {0x11CB2, 0x11CB3, prExtend}, // Mn [2] MARCHEN VOWEL SIGN U..MARCHEN VOWEL SIGN E {0x11CB4, 0x11CB4, prSpacingMark}, // Mc MARCHEN VOWEL SIGN O {0x11CB5, 0x11CB6, prExtend}, // Mn [2] MARCHEN SIGN ANUSVARA..MARCHEN SIGN CANDRABINDU {0x11D31, 0x11D36, prExtend}, // Mn [6] MASARAM GONDI VOWEL SIGN AA..MASARAM GONDI VOWEL SIGN VOCALIC R {0x11D3A, 0x11D3A, prExtend}, // Mn MASARAM GONDI VOWEL SIGN E {0x11D3C, 0x11D3D, prExtend}, // Mn [2] MASARAM GONDI VOWEL SIGN AI..MASARAM GONDI VOWEL SIGN O {0x11D3F, 0x11D45, prExtend}, // Mn [7] MASARAM GONDI VOWEL SIGN AU..MASARAM GONDI VIRAMA {0x11D46, 0x11D46, prPreprend}, // Lo MASARAM GONDI REPHA {0x11D47, 0x11D47, prExtend}, // Mn MASARAM GONDI RA-KARA {0x11D8A, 0x11D8E, prSpacingMark}, // Mc [5] GUNJALA GONDI VOWEL SIGN AA..GUNJALA GONDI VOWEL SIGN UU {0x11D90, 0x11D91, prExtend}, // Mn [2] GUNJALA GONDI VOWEL SIGN EE..GUNJALA GONDI VOWEL SIGN AI {0x11D93, 0x11D94, prSpacingMark}, // Mc [2] GUNJALA GONDI VOWEL SIGN OO..GUNJALA GONDI VOWEL SIGN AU {0x11D95, 0x11D95, prExtend}, // Mn GUNJALA GONDI SIGN ANUSVARA {0x11D96, 0x11D96, prSpacingMark}, // Mc GUNJALA GONDI SIGN VISARGA {0x11D97, 0x11D97, prExtend}, // Mn GUNJALA GONDI VIRAMA {0x11EF3, 0x11EF4, prExtend}, // Mn [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U {0x11EF5, 0x11EF6, prSpacingMark}, // Mc [2] MAKASAR VOWEL SIGN E..MAKASAR VOWEL SIGN O {0x13430, 0x13438, prControl}, // Cf [9] EGYPTIAN HIEROGLYPH VERTICAL JOINER..EGYPTIAN HIEROGLYPH END SEGMENT {0x16AF0, 0x16AF4, prExtend}, // Mn [5] BASSA VAH COMBINING HIGH TONE..BASSA VAH COMBINING HIGH-LOW TONE {0x16B30, 0x16B36, prExtend}, // Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM {0x16F4F, 0x16F4F, prExtend}, // Mn MIAO SIGN CONSONANT MODIFIER BAR {0x16F51, 0x16F87, prSpacingMark}, // Mc [55] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN UI {0x16F8F, 0x16F92, prExtend}, // Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW {0x1BC9D, 0x1BC9E, prExtend}, // Mn [2] DUPLOYAN THICK LETTER SELECTOR..DUPLOYAN DOUBLE MARK {0x1BCA0, 0x1BCA3, prControl}, // Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP {0x1D165, 0x1D165, prExtend}, // Mc MUSICAL SYMBOL COMBINING STEM {0x1D166, 0x1D166, prSpacingMark}, // Mc MUSICAL SYMBOL COMBINING SPRECHGESANG STEM {0x1D167, 0x1D169, prExtend}, // Mn [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3 {0x1D16D, 0x1D16D, prSpacingMark}, // Mc MUSICAL SYMBOL COMBINING AUGMENTATION DOT {0x1D16E, 0x1D172, prExtend}, // Mc [5] MUSICAL SYMBOL COMBINING FLAG-1..MUSICAL SYMBOL COMBINING FLAG-5 {0x1D173, 0x1D17A, prControl}, // Cf [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE {0x1D17B, 0x1D182, prExtend}, // Mn [8] MUSICAL SYMBOL COMBINING ACCENT..MUSICAL SYMBOL COMBINING LOURE {0x1D185, 0x1D18B, prExtend}, // Mn [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE {0x1D1AA, 0x1D1AD, prExtend}, // Mn [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO {0x1D242, 0x1D244, prExtend}, // Mn [3] COMBINING GREEK MUSICAL TRISEME..COMBINING GREEK MUSICAL PENTASEME {0x1DA00, 0x1DA36, prExtend}, // Mn [55] SIGNWRITING HEAD RIM..SIGNWRITING AIR SUCKING IN {0x1DA3B, 0x1DA6C, prExtend}, // Mn [50] SIGNWRITING MOUTH CLOSED NEUTRAL..SIGNWRITING EXCITEMENT {0x1DA75, 0x1DA75, prExtend}, // Mn SIGNWRITING UPPER BODY TILTING FROM HIP JOINTS {0x1DA84, 0x1DA84, prExtend}, // Mn SIGNWRITING LOCATION HEAD NECK {0x1DA9B, 0x1DA9F, prExtend}, // Mn [5] SIGNWRITING FILL MODIFIER-2..SIGNWRITING FILL MODIFIER-6 {0x1DAA1, 0x1DAAF, prExtend}, // Mn [15] SIGNWRITING ROTATION MODIFIER-2..SIGNWRITING ROTATION MODIFIER-16 {0x1E000, 0x1E006, prExtend}, // Mn [7] COMBINING GLAGOLITIC LETTER AZU..COMBINING GLAGOLITIC LETTER ZHIVETE {0x1E008, 0x1E018, prExtend}, // Mn [17] COMBINING GLAGOLITIC LETTER ZEMLJA..COMBINING GLAGOLITIC LETTER HERU {0x1E01B, 0x1E021, prExtend}, // Mn [7] COMBINING GLAGOLITIC LETTER SHTA..COMBINING GLAGOLITIC LETTER YATI {0x1E023, 0x1E024, prExtend}, // Mn [2] COMBINING GLAGOLITIC LETTER YU..COMBINING GLAGOLITIC LETTER SMALL YUS {0x1E026, 0x1E02A, prExtend}, // Mn [5] COMBINING GLAGOLITIC LETTER YO..COMBINING GLAGOLITIC LETTER FITA {0x1E130, 0x1E136, prExtend}, // Mn [7] NYIAKENG PUACHUE HMONG TONE-B..NYIAKENG PUACHUE HMONG TONE-D {0x1E2EC, 0x1E2EF, prExtend}, // Mn [4] WANCHO TONE TUP..WANCHO TONE KOINI {0x1E8D0, 0x1E8D6, prExtend}, // Mn [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS {0x1E944, 0x1E94A, prExtend}, // Mn [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA {0x1F000, 0x1F02B, prExtendedPictographic}, // 5.1 [44] (🀀..🀫) MAHJONG TILE EAST WIND..MAHJONG TILE BACK {0x1F02C, 0x1F02F, prExtendedPictographic}, // NA [4] (🀬..🀯) .. {0x1F030, 0x1F093, prExtendedPictographic}, // 5.1[100] (🀰..🂓) DOMINO TILE HORIZONTAL BACK..DOMINO TILE VERTICAL-06-06 {0x1F094, 0x1F09F, prExtendedPictographic}, // NA [12] (🂔..🂟) .. {0x1F0A0, 0x1F0AE, prExtendedPictographic}, // 6.0 [15] (🂠..🂮) PLAYING CARD BACK..PLAYING CARD KING OF SPADES {0x1F0AF, 0x1F0B0, prExtendedPictographic}, // NA [2] (🂯..🂰) .. {0x1F0B1, 0x1F0BE, prExtendedPictographic}, // 6.0 [14] (🂱..🂾) PLAYING CARD ACE OF HEARTS..PLAYING CARD KING OF HEARTS {0x1F0BF, 0x1F0BF, prExtendedPictographic}, // 7.0 [1] (🂿) PLAYING CARD RED JOKER {0x1F0C0, 0x1F0C0, prExtendedPictographic}, // NA [1] (🃀) {0x1F0C1, 0x1F0CF, prExtendedPictographic}, // 6.0 [15] (🃁..🃏) PLAYING CARD ACE OF DIAMONDS..joker {0x1F0D0, 0x1F0D0, prExtendedPictographic}, // NA [1] (🃐) {0x1F0D1, 0x1F0DF, prExtendedPictographic}, // 6.0 [15] (🃑..🃟) PLAYING CARD ACE OF CLUBS..PLAYING CARD WHITE JOKER {0x1F0E0, 0x1F0F5, prExtendedPictographic}, // 7.0 [22] (🃠..🃵) PLAYING CARD FOOL..PLAYING CARD TRUMP-21 {0x1F0F6, 0x1F0FF, prExtendedPictographic}, // NA [10] (🃶..🃿) .. {0x1F10D, 0x1F10F, prExtendedPictographic}, // NA [3] (🄍..🄏) .. {0x1F12F, 0x1F12F, prExtendedPictographic}, // 11.0 [1] (🄯) COPYLEFT SYMBOL {0x1F16C, 0x1F16C, prExtendedPictographic}, // 12.0 [1] (🅬) RAISED MR SIGN {0x1F16D, 0x1F16F, prExtendedPictographic}, // NA [3] (🅭..🅯) .. {0x1F170, 0x1F171, prExtendedPictographic}, // 6.0 [2] (🅰️..🅱️) A button (blood type)..B button (blood type) {0x1F17E, 0x1F17E, prExtendedPictographic}, // 6.0 [1] (🅾️) O button (blood type) {0x1F17F, 0x1F17F, prExtendedPictographic}, // 5.2 [1] (🅿️) P button {0x1F18E, 0x1F18E, prExtendedPictographic}, // 6.0 [1] (🆎) AB button (blood type) {0x1F191, 0x1F19A, prExtendedPictographic}, // 6.0 [10] (🆑..🆚) CL button..VS button {0x1F1AD, 0x1F1E5, prExtendedPictographic}, // NA [57] (🆭..🇥) .. {0x1F1E6, 0x1F1FF, prRegionalIndicator}, // So [26] REGIONAL INDICATOR SYMBOL LETTER A..REGIONAL INDICATOR SYMBOL LETTER Z {0x1F201, 0x1F202, prExtendedPictographic}, // 6.0 [2] (🈁..🈂️) Japanese “here” button..Japanese “service charge” button {0x1F203, 0x1F20F, prExtendedPictographic}, // NA [13] (🈃..🈏) .. {0x1F21A, 0x1F21A, prExtendedPictographic}, // 5.2 [1] (🈚) Japanese “free of charge” button {0x1F22F, 0x1F22F, prExtendedPictographic}, // 5.2 [1] (🈯) Japanese “reserved” button {0x1F232, 0x1F23A, prExtendedPictographic}, // 6.0 [9] (🈲..🈺) Japanese “prohibited” button..Japanese “open for business” button {0x1F23C, 0x1F23F, prExtendedPictographic}, // NA [4] (🈼..🈿) .. {0x1F249, 0x1F24F, prExtendedPictographic}, // NA [7] (🉉..🉏) .. {0x1F250, 0x1F251, prExtendedPictographic}, // 6.0 [2] (🉐..🉑) Japanese “bargain” button..Japanese “acceptable” button {0x1F252, 0x1F25F, prExtendedPictographic}, // NA [14] (🉒..🉟) .. {0x1F260, 0x1F265, prExtendedPictographic}, // 10.0 [6] (🉠..🉥) ROUNDED SYMBOL FOR FU..ROUNDED SYMBOL FOR CAI {0x1F266, 0x1F2FF, prExtendedPictographic}, // NA[154] (🉦..🋿) .. {0x1F300, 0x1F320, prExtendedPictographic}, // 6.0 [33] (🌀..🌠) cyclone..shooting star {0x1F321, 0x1F32C, prExtendedPictographic}, // 7.0 [12] (🌡️..🌬️) thermometer..wind face {0x1F32D, 0x1F32F, prExtendedPictographic}, // 8.0 [3] (🌭..🌯) hot dog..burrito {0x1F330, 0x1F335, prExtendedPictographic}, // 6.0 [6] (🌰..🌵) chestnut..cactus {0x1F336, 0x1F336, prExtendedPictographic}, // 7.0 [1] (🌶️) hot pepper {0x1F337, 0x1F37C, prExtendedPictographic}, // 6.0 [70] (🌷..🍼) tulip..baby bottle {0x1F37D, 0x1F37D, prExtendedPictographic}, // 7.0 [1] (🍽️) fork and knife with plate {0x1F37E, 0x1F37F, prExtendedPictographic}, // 8.0 [2] (🍾..🍿) bottle with popping cork..popcorn {0x1F380, 0x1F393, prExtendedPictographic}, // 6.0 [20] (🎀..🎓) ribbon..graduation cap {0x1F394, 0x1F39F, prExtendedPictographic}, // 7.0 [12] (🎔..🎟️) HEART WITH TIP ON THE LEFT..admission tickets {0x1F3A0, 0x1F3C4, prExtendedPictographic}, // 6.0 [37] (🎠..🏄) carousel horse..person surfing {0x1F3C5, 0x1F3C5, prExtendedPictographic}, // 7.0 [1] (🏅) sports medal {0x1F3C6, 0x1F3CA, prExtendedPictographic}, // 6.0 [5] (🏆..🏊) trophy..person swimming {0x1F3CB, 0x1F3CE, prExtendedPictographic}, // 7.0 [4] (🏋️..🏎️) person lifting weights..racing car {0x1F3CF, 0x1F3D3, prExtendedPictographic}, // 8.0 [5] (🏏..🏓) cricket game..ping pong {0x1F3D4, 0x1F3DF, prExtendedPictographic}, // 7.0 [12] (🏔️..🏟️) snow-capped mountain..stadium {0x1F3E0, 0x1F3F0, prExtendedPictographic}, // 6.0 [17] (🏠..🏰) house..castle {0x1F3F1, 0x1F3F7, prExtendedPictographic}, // 7.0 [7] (🏱..🏷️) WHITE PENNANT..label {0x1F3F8, 0x1F3FA, prExtendedPictographic}, // 8.0 [3] (🏸..🏺) badminton..amphora {0x1F3FB, 0x1F3FF, prExtend}, // Sk [5] EMOJI MODIFIER FITZPATRICK TYPE-1-2..EMOJI MODIFIER FITZPATRICK TYPE-6 {0x1F400, 0x1F43E, prExtendedPictographic}, // 6.0 [63] (🐀..🐾) rat..paw prints {0x1F43F, 0x1F43F, prExtendedPictographic}, // 7.0 [1] (🐿️) chipmunk {0x1F440, 0x1F440, prExtendedPictographic}, // 6.0 [1] (👀) eyes {0x1F441, 0x1F441, prExtendedPictographic}, // 7.0 [1] (👁️) eye {0x1F442, 0x1F4F7, prExtendedPictographic}, // 6.0[182] (👂..📷) ear..camera {0x1F4F8, 0x1F4F8, prExtendedPictographic}, // 7.0 [1] (📸) camera with flash {0x1F4F9, 0x1F4FC, prExtendedPictographic}, // 6.0 [4] (📹..📼) video camera..videocassette {0x1F4FD, 0x1F4FE, prExtendedPictographic}, // 7.0 [2] (📽️..📾) film projector..PORTABLE STEREO {0x1F4FF, 0x1F4FF, prExtendedPictographic}, // 8.0 [1] (📿) prayer beads {0x1F500, 0x1F53D, prExtendedPictographic}, // 6.0 [62] (🔀..🔽) shuffle tracks button..downwards button {0x1F546, 0x1F54A, prExtendedPictographic}, // 7.0 [5] (🕆..🕊️) WHITE LATIN CROSS..dove {0x1F54B, 0x1F54F, prExtendedPictographic}, // 8.0 [5] (🕋..🕏) kaaba..BOWL OF HYGIEIA {0x1F550, 0x1F567, prExtendedPictographic}, // 6.0 [24] (🕐..🕧) one o’clock..twelve-thirty {0x1F568, 0x1F579, prExtendedPictographic}, // 7.0 [18] (🕨..🕹️) RIGHT SPEAKER..joystick {0x1F57A, 0x1F57A, prExtendedPictographic}, // 9.0 [1] (🕺) man dancing {0x1F57B, 0x1F5A3, prExtendedPictographic}, // 7.0 [41] (🕻..🖣) LEFT HAND TELEPHONE RECEIVER..BLACK DOWN POINTING BACKHAND INDEX {0x1F5A4, 0x1F5A4, prExtendedPictographic}, // 9.0 [1] (🖤) black heart {0x1F5A5, 0x1F5FA, prExtendedPictographic}, // 7.0 [86] (🖥️..🗺️) desktop computer..world map {0x1F5FB, 0x1F5FF, prExtendedPictographic}, // 6.0 [5] (🗻..🗿) mount fuji..moai {0x1F600, 0x1F600, prExtendedPictographic}, // 6.1 [1] (😀) grinning face {0x1F601, 0x1F610, prExtendedPictographic}, // 6.0 [16] (😁..😐) beaming face with smiling eyes..neutral face {0x1F611, 0x1F611, prExtendedPictographic}, // 6.1 [1] (😑) expressionless face {0x1F612, 0x1F614, prExtendedPictographic}, // 6.0 [3] (😒..😔) unamused face..pensive face {0x1F615, 0x1F615, prExtendedPictographic}, // 6.1 [1] (😕) confused face {0x1F616, 0x1F616, prExtendedPictographic}, // 6.0 [1] (😖) confounded face {0x1F617, 0x1F617, prExtendedPictographic}, // 6.1 [1] (😗) kissing face {0x1F618, 0x1F618, prExtendedPictographic}, // 6.0 [1] (😘) face blowing a kiss {0x1F619, 0x1F619, prExtendedPictographic}, // 6.1 [1] (😙) kissing face with smiling eyes {0x1F61A, 0x1F61A, prExtendedPictographic}, // 6.0 [1] (😚) kissing face with closed eyes {0x1F61B, 0x1F61B, prExtendedPictographic}, // 6.1 [1] (😛) face with tongue {0x1F61C, 0x1F61E, prExtendedPictographic}, // 6.0 [3] (😜..😞) winking face with tongue..disappointed face {0x1F61F, 0x1F61F, prExtendedPictographic}, // 6.1 [1] (😟) worried face {0x1F620, 0x1F625, prExtendedPictographic}, // 6.0 [6] (😠..😥) angry face..sad but relieved face {0x1F626, 0x1F627, prExtendedPictographic}, // 6.1 [2] (😦..😧) frowning face with open mouth..anguished face {0x1F628, 0x1F62B, prExtendedPictographic}, // 6.0 [4] (😨..😫) fearful face..tired face {0x1F62C, 0x1F62C, prExtendedPictographic}, // 6.1 [1] (😬) grimacing face {0x1F62D, 0x1F62D, prExtendedPictographic}, // 6.0 [1] (😭) loudly crying face {0x1F62E, 0x1F62F, prExtendedPictographic}, // 6.1 [2] (😮..😯) face with open mouth..hushed face {0x1F630, 0x1F633, prExtendedPictographic}, // 6.0 [4] (😰..😳) anxious face with sweat..flushed face {0x1F634, 0x1F634, prExtendedPictographic}, // 6.1 [1] (😴) sleeping face {0x1F635, 0x1F640, prExtendedPictographic}, // 6.0 [12] (😵..🙀) dizzy face..weary cat {0x1F641, 0x1F642, prExtendedPictographic}, // 7.0 [2] (🙁..🙂) slightly frowning face..slightly smiling face {0x1F643, 0x1F644, prExtendedPictographic}, // 8.0 [2] (🙃..🙄) upside-down face..face with rolling eyes {0x1F645, 0x1F64F, prExtendedPictographic}, // 6.0 [11] (🙅..🙏) person gesturing NO..folded hands {0x1F680, 0x1F6C5, prExtendedPictographic}, // 6.0 [70] (🚀..🛅) rocket..left luggage {0x1F6C6, 0x1F6CF, prExtendedPictographic}, // 7.0 [10] (🛆..🛏️) TRIANGLE WITH ROUNDED CORNERS..bed {0x1F6D0, 0x1F6D0, prExtendedPictographic}, // 8.0 [1] (🛐) place of worship {0x1F6D1, 0x1F6D2, prExtendedPictographic}, // 9.0 [2] (🛑..🛒) stop sign..shopping cart {0x1F6D3, 0x1F6D4, prExtendedPictographic}, // 10.0 [2] (🛓..🛔) STUPA..PAGODA {0x1F6D5, 0x1F6D5, prExtendedPictographic}, // 12.0 [1] (🛕) hindu temple {0x1F6D6, 0x1F6DF, prExtendedPictographic}, // NA [10] (🛖..🛟) .. {0x1F6E0, 0x1F6EC, prExtendedPictographic}, // 7.0 [13] (🛠️..🛬) hammer and wrench..airplane arrival {0x1F6ED, 0x1F6EF, prExtendedPictographic}, // NA [3] (🛭..🛯) .. {0x1F6F0, 0x1F6F3, prExtendedPictographic}, // 7.0 [4] (🛰️..🛳️) satellite..passenger ship {0x1F6F4, 0x1F6F6, prExtendedPictographic}, // 9.0 [3] (🛴..🛶) kick scooter..canoe {0x1F6F7, 0x1F6F8, prExtendedPictographic}, // 10.0 [2] (🛷..🛸) sled..flying saucer {0x1F6F9, 0x1F6F9, prExtendedPictographic}, // 11.0 [1] (🛹) skateboard {0x1F6FA, 0x1F6FA, prExtendedPictographic}, // 12.0 [1] (🛺) auto rickshaw {0x1F6FB, 0x1F6FF, prExtendedPictographic}, // NA [5] (🛻..🛿) .. {0x1F774, 0x1F77F, prExtendedPictographic}, // NA [12] (🝴..🝿) .. {0x1F7D5, 0x1F7D8, prExtendedPictographic}, // 11.0 [4] (🟕..🟘) CIRCLED TRIANGLE..NEGATIVE CIRCLED SQUARE {0x1F7D9, 0x1F7DF, prExtendedPictographic}, // NA [7] (🟙..🟟) .. {0x1F7E0, 0x1F7EB, prExtendedPictographic}, // 12.0 [12] (🟠..🟫) orange circle..brown square {0x1F7EC, 0x1F7FF, prExtendedPictographic}, // NA [20] (🟬..🟿) .. {0x1F80C, 0x1F80F, prExtendedPictographic}, // NA [4] (🠌..🠏) .. {0x1F848, 0x1F84F, prExtendedPictographic}, // NA [8] (🡈..🡏) .. {0x1F85A, 0x1F85F, prExtendedPictographic}, // NA [6] (🡚..🡟) .. {0x1F888, 0x1F88F, prExtendedPictographic}, // NA [8] (🢈..🢏) .. {0x1F8AE, 0x1F8FF, prExtendedPictographic}, // NA [82] (🢮..🣿) .. {0x1F90C, 0x1F90C, prExtendedPictographic}, // NA [1] (🤌) {0x1F90D, 0x1F90F, prExtendedPictographic}, // 12.0 [3] (🤍..🤏) white heart..pinching hand {0x1F910, 0x1F918, prExtendedPictographic}, // 8.0 [9] (🤐..🤘) zipper-mouth face..sign of the horns {0x1F919, 0x1F91E, prExtendedPictographic}, // 9.0 [6] (🤙..🤞) call me hand..crossed fingers {0x1F91F, 0x1F91F, prExtendedPictographic}, // 10.0 [1] (🤟) love-you gesture {0x1F920, 0x1F927, prExtendedPictographic}, // 9.0 [8] (🤠..🤧) cowboy hat face..sneezing face {0x1F928, 0x1F92F, prExtendedPictographic}, // 10.0 [8] (🤨..🤯) face with raised eyebrow..exploding head {0x1F930, 0x1F930, prExtendedPictographic}, // 9.0 [1] (🤰) pregnant woman {0x1F931, 0x1F932, prExtendedPictographic}, // 10.0 [2] (🤱..🤲) breast-feeding..palms up together {0x1F933, 0x1F93A, prExtendedPictographic}, // 9.0 [8] (🤳..🤺) selfie..person fencing {0x1F93C, 0x1F93E, prExtendedPictographic}, // 9.0 [3] (🤼..🤾) people wrestling..person playing handball {0x1F93F, 0x1F93F, prExtendedPictographic}, // 12.0 [1] (🤿) diving mask {0x1F940, 0x1F945, prExtendedPictographic}, // 9.0 [6] (🥀..🥅) wilted flower..goal net {0x1F947, 0x1F94B, prExtendedPictographic}, // 9.0 [5] (🥇..🥋) 1st place medal..martial arts uniform {0x1F94C, 0x1F94C, prExtendedPictographic}, // 10.0 [1] (🥌) curling stone {0x1F94D, 0x1F94F, prExtendedPictographic}, // 11.0 [3] (🥍..🥏) lacrosse..flying disc {0x1F950, 0x1F95E, prExtendedPictographic}, // 9.0 [15] (🥐..🥞) croissant..pancakes {0x1F95F, 0x1F96B, prExtendedPictographic}, // 10.0 [13] (🥟..🥫) dumpling..canned food {0x1F96C, 0x1F970, prExtendedPictographic}, // 11.0 [5] (🥬..🥰) leafy green..smiling face with hearts {0x1F971, 0x1F971, prExtendedPictographic}, // 12.0 [1] (🥱) yawning face {0x1F972, 0x1F972, prExtendedPictographic}, // NA [1] (🥲) {0x1F973, 0x1F976, prExtendedPictographic}, // 11.0 [4] (🥳..🥶) partying face..cold face {0x1F977, 0x1F979, prExtendedPictographic}, // NA [3] (🥷..🥹) .. {0x1F97A, 0x1F97A, prExtendedPictographic}, // 11.0 [1] (🥺) pleading face {0x1F97B, 0x1F97B, prExtendedPictographic}, // 12.0 [1] (🥻) sari {0x1F97C, 0x1F97F, prExtendedPictographic}, // 11.0 [4] (🥼..🥿) lab coat..flat shoe {0x1F980, 0x1F984, prExtendedPictographic}, // 8.0 [5] (🦀..🦄) crab..unicorn {0x1F985, 0x1F991, prExtendedPictographic}, // 9.0 [13] (🦅..🦑) eagle..squid {0x1F992, 0x1F997, prExtendedPictographic}, // 10.0 [6] (🦒..🦗) giraffe..cricket {0x1F998, 0x1F9A2, prExtendedPictographic}, // 11.0 [11] (🦘..🦢) kangaroo..swan {0x1F9A3, 0x1F9A4, prExtendedPictographic}, // NA [2] (🦣..🦤) .. {0x1F9A5, 0x1F9AA, prExtendedPictographic}, // 12.0 [6] (🦥..🦪) sloth..oyster {0x1F9AB, 0x1F9AD, prExtendedPictographic}, // NA [3] (🦫..🦭) .. {0x1F9AE, 0x1F9AF, prExtendedPictographic}, // 12.0 [2] (🦮..🦯) guide dog..probing cane {0x1F9B0, 0x1F9B9, prExtendedPictographic}, // 11.0 [10] (🦰..🦹) red hair..supervillain {0x1F9BA, 0x1F9BF, prExtendedPictographic}, // 12.0 [6] (🦺..🦿) safety vest..mechanical leg {0x1F9C0, 0x1F9C0, prExtendedPictographic}, // 8.0 [1] (🧀) cheese wedge {0x1F9C1, 0x1F9C2, prExtendedPictographic}, // 11.0 [2] (🧁..🧂) cupcake..salt {0x1F9C3, 0x1F9CA, prExtendedPictographic}, // 12.0 [8] (🧃..🧊) beverage box..ice cube {0x1F9CB, 0x1F9CC, prExtendedPictographic}, // NA [2] (🧋..🧌) .. {0x1F9CD, 0x1F9CF, prExtendedPictographic}, // 12.0 [3] (🧍..🧏) person standing..deaf person {0x1F9D0, 0x1F9E6, prExtendedPictographic}, // 10.0 [23] (🧐..🧦) face with monocle..socks {0x1F9E7, 0x1F9FF, prExtendedPictographic}, // 11.0 [25] (🧧..🧿) red envelope..nazar amulet {0x1FA00, 0x1FA53, prExtendedPictographic}, // 12.0 [84] (🨀..🩓) NEUTRAL CHESS KING..BLACK CHESS KNIGHT-BISHOP {0x1FA54, 0x1FA5F, prExtendedPictographic}, // NA [12] (🩔..🩟) .. {0x1FA60, 0x1FA6D, prExtendedPictographic}, // 11.0 [14] (🩠..🩭) XIANGQI RED GENERAL..XIANGQI BLACK SOLDIER {0x1FA6E, 0x1FA6F, prExtendedPictographic}, // NA [2] (🩮..🩯) .. {0x1FA70, 0x1FA73, prExtendedPictographic}, // 12.0 [4] (🩰..🩳) ballet shoes..shorts {0x1FA74, 0x1FA77, prExtendedPictographic}, // NA [4] (🩴..🩷) .. {0x1FA78, 0x1FA7A, prExtendedPictographic}, // 12.0 [3] (🩸..🩺) drop of blood..stethoscope {0x1FA7B, 0x1FA7F, prExtendedPictographic}, // NA [5] (🩻..🩿) .. {0x1FA80, 0x1FA82, prExtendedPictographic}, // 12.0 [3] (🪀..🪂) yo-yo..parachute {0x1FA83, 0x1FA8F, prExtendedPictographic}, // NA [13] (🪃..🪏) .. {0x1FA90, 0x1FA95, prExtendedPictographic}, // 12.0 [6] (🪐..🪕) ringed planet..banjo {0x1FA96, 0x1FFFD, prExtendedPictographic}, // NA[1384] (🪖..🿽) .. {0xE0000, 0xE0000, prControl}, // Cn {0xE0001, 0xE0001, prControl}, // Cf LANGUAGE TAG {0xE0002, 0xE001F, prControl}, // Cn [30] .. {0xE0020, 0xE007F, prExtend}, // Cf [96] TAG SPACE..CANCEL TAG {0xE0080, 0xE00FF, prControl}, // Cn [128] .. {0xE0100, 0xE01EF, prExtend}, // Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256 {0xE01F0, 0xE0FFF, prControl}, // Cn [3600] .. } // property returns the Unicode property value (see constants above) of the // given code point. func property(r rune) int { // Run a binary search. from := 0 to := len(codePoints) for to > from { middle := (from + to) / 2 cpRange := codePoints[middle] if int(r) < cpRange[0] { to = middle continue } if int(r) > cpRange[1] { from = middle + 1 continue } return cpRange[2] } return prAny } ================================================ FILE: vendor/github.com/russross/blackfriday/v2/.gitignore ================================================ *.out *.swp *.8 *.6 _obj _test* markdown tags ================================================ FILE: vendor/github.com/russross/blackfriday/v2/.travis.yml ================================================ sudo: false language: go go: - "1.10.x" - "1.11.x" - tip matrix: fast_finish: true allow_failures: - go: tip install: - # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step). script: - go get -t -v ./... - diff -u <(echo -n) <(gofmt -d -s .) - go tool vet . - go test -v ./... ================================================ FILE: vendor/github.com/russross/blackfriday/v2/LICENSE.txt ================================================ Blackfriday is distributed under the Simplified BSD License: > Copyright © 2011 Russ Ross > All rights reserved. > > Redistribution and use in source and binary forms, with or without > modification, are permitted provided that the following conditions > are met: > > 1. Redistributions of source code must retain the above copyright > notice, this list of conditions and the following disclaimer. > > 2. Redistributions in binary form must reproduce the above > copyright notice, this list of conditions and the following > disclaimer in the documentation and/or other materials provided with > the distribution. > > THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS > "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT > LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS > FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE > COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, > INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, > BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; > LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER > CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT > LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN > ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE > POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/russross/blackfriday/v2/README.md ================================================ Blackfriday [![Build Status][BuildV2SVG]][BuildV2URL] [![PkgGoDev][PkgGoDevV2SVG]][PkgGoDevV2URL] =========== Blackfriday is a [Markdown][1] processor implemented in [Go][2]. It is paranoid about its input (so you can safely feed it user-supplied data), it is fast, it supports common extensions (tables, smart punctuation substitutions, etc.), and it is safe for all utf-8 (unicode) input. HTML output is currently supported, along with Smartypants extensions. It started as a translation from C of [Sundown][3]. Installation ------------ Blackfriday is compatible with modern Go releases in module mode. With Go installed: go get github.com/russross/blackfriday/v2 will resolve and add the package to the current development module, then build and install it. Alternatively, you can achieve the same if you import it in a package: import "github.com/russross/blackfriday/v2" and `go get` without parameters. Legacy GOPATH mode is unsupported. Versions -------- Currently maintained and recommended version of Blackfriday is `v2`. It's being developed on its own branch: https://github.com/russross/blackfriday/tree/v2 and the documentation is available at https://pkg.go.dev/github.com/russross/blackfriday/v2. It is `go get`-able in module mode at `github.com/russross/blackfriday/v2`. Version 2 offers a number of improvements over v1: * Cleaned up API * A separate call to [`Parse`][4], which produces an abstract syntax tree for the document * Latest bug fixes * Flexibility to easily add your own rendering extensions Potential drawbacks: * Our benchmarks show v2 to be slightly slower than v1. Currently in the ballpark of around 15%. * API breakage. If you can't afford modifying your code to adhere to the new API and don't care too much about the new features, v2 is probably not for you. * Several bug fixes are trailing behind and still need to be forward-ported to v2. See issue [#348](https://github.com/russross/blackfriday/issues/348) for tracking. If you are still interested in the legacy `v1`, you can import it from `github.com/russross/blackfriday`. Documentation for the legacy v1 can be found here: https://pkg.go.dev/github.com/russross/blackfriday. Usage ----- For the most sensible markdown processing, it is as simple as getting your input into a byte slice and calling: ```go output := blackfriday.Run(input) ``` Your input will be parsed and the output rendered with a set of most popular extensions enabled. If you want the most basic feature set, corresponding with the bare Markdown specification, use: ```go output := blackfriday.Run(input, blackfriday.WithNoExtensions()) ``` ### Sanitize untrusted content Blackfriday itself does nothing to protect against malicious content. If you are dealing with user-supplied markdown, we recommend running Blackfriday's output through HTML sanitizer such as [Bluemonday][5]. Here's an example of simple usage of Blackfriday together with Bluemonday: ```go import ( "github.com/microcosm-cc/bluemonday" "github.com/russross/blackfriday/v2" ) // ... unsafe := blackfriday.Run(input) html := bluemonday.UGCPolicy().SanitizeBytes(unsafe) ``` ### Custom options If you want to customize the set of options, use `blackfriday.WithExtensions`, `blackfriday.WithRenderer` and `blackfriday.WithRefOverride`. ### `blackfriday-tool` You can also check out `blackfriday-tool` for a more complete example of how to use it. Download and install it using: go get github.com/russross/blackfriday-tool This is a simple command-line tool that allows you to process a markdown file using a standalone program. You can also browse the source directly on github if you are just looking for some example code: * Note that if you have not already done so, installing `blackfriday-tool` will be sufficient to download and install blackfriday in addition to the tool itself. The tool binary will be installed in `$GOPATH/bin`. This is a statically-linked binary that can be copied to wherever you need it without worrying about dependencies and library versions. ### Sanitized anchor names Blackfriday includes an algorithm for creating sanitized anchor names corresponding to a given input text. This algorithm is used to create anchors for headings when `AutoHeadingIDs` extension is enabled. The algorithm has a specification, so that other packages can create compatible anchor names and links to those anchors. The specification is located at https://pkg.go.dev/github.com/russross/blackfriday/v2#hdr-Sanitized_Anchor_Names. [`SanitizedAnchorName`](https://pkg.go.dev/github.com/russross/blackfriday/v2#SanitizedAnchorName) exposes this functionality, and can be used to create compatible links to the anchor names generated by blackfriday. This algorithm is also implemented in a small standalone package at [`github.com/shurcooL/sanitized_anchor_name`](https://pkg.go.dev/github.com/shurcooL/sanitized_anchor_name). It can be useful for clients that want a small package and don't need full functionality of blackfriday. Features -------- All features of Sundown are supported, including: * **Compatibility**. The Markdown v1.0.3 test suite passes with the `--tidy` option. Without `--tidy`, the differences are mostly in whitespace and entity escaping, where blackfriday is more consistent and cleaner. * **Common extensions**, including table support, fenced code blocks, autolinks, strikethroughs, non-strict emphasis, etc. * **Safety**. Blackfriday is paranoid when parsing, making it safe to feed untrusted user input without fear of bad things happening. The test suite stress tests this and there are no known inputs that make it crash. If you find one, please let me know and send me the input that does it. NOTE: "safety" in this context means *runtime safety only*. In order to protect yourself against JavaScript injection in untrusted content, see [this example](https://github.com/russross/blackfriday#sanitize-untrusted-content). * **Fast processing**. It is fast enough to render on-demand in most web applications without having to cache the output. * **Thread safety**. You can run multiple parsers in different goroutines without ill effect. There is no dependence on global shared state. * **Minimal dependencies**. Blackfriday only depends on standard library packages in Go. The source code is pretty self-contained, so it is easy to add to any project, including Google App Engine projects. * **Standards compliant**. Output successfully validates using the W3C validation tool for HTML 4.01 and XHTML 1.0 Transitional. Extensions ---------- In addition to the standard markdown syntax, this package implements the following extensions: * **Intra-word emphasis supression**. The `_` character is commonly used inside words when discussing code, so having markdown interpret it as an emphasis command is usually the wrong thing. Blackfriday lets you treat all emphasis markers as normal characters when they occur inside a word. * **Tables**. Tables can be created by drawing them in the input using a simple syntax: ``` Name | Age --------|------ Bob | 27 Alice | 23 ``` * **Fenced code blocks**. In addition to the normal 4-space indentation to mark code blocks, you can explicitly mark them and supply a language (to make syntax highlighting simple). Just mark it like this: ```go func getTrue() bool { return true } ``` You can use 3 or more backticks to mark the beginning of the block, and the same number to mark the end of the block. To preserve classes of fenced code blocks while using the bluemonday HTML sanitizer, use the following policy: ```go p := bluemonday.UGCPolicy() p.AllowAttrs("class").Matching(regexp.MustCompile("^language-[a-zA-Z0-9]+$")).OnElements("code") html := p.SanitizeBytes(unsafe) ``` * **Definition lists**. A simple definition list is made of a single-line term followed by a colon and the definition for that term. Cat : Fluffy animal everyone likes Internet : Vector of transmission for pictures of cats Terms must be separated from the previous definition by a blank line. * **Footnotes**. A marker in the text that will become a superscript number; a footnote definition that will be placed in a list of footnotes at the end of the document. A footnote looks like this: This is a footnote.[^1] [^1]: the footnote text. * **Autolinking**. Blackfriday can find URLs that have not been explicitly marked as links and turn them into links. * **Strikethrough**. Use two tildes (`~~`) to mark text that should be crossed out. * **Hard line breaks**. With this extension enabled newlines in the input translate into line breaks in the output. This extension is off by default. * **Smart quotes**. Smartypants-style punctuation substitution is supported, turning normal double- and single-quote marks into curly quotes, etc. * **LaTeX-style dash parsing** is an additional option, where `--` is translated into `–`, and `---` is translated into `—`. This differs from most smartypants processors, which turn a single hyphen into an ndash and a double hyphen into an mdash. * **Smart fractions**, where anything that looks like a fraction is translated into suitable HTML (instead of just a few special cases like most smartypant processors). For example, `4/5` becomes `45`, which renders as 45. Other renderers --------------- Blackfriday is structured to allow alternative rendering engines. Here are a few of note: * [github_flavored_markdown](https://pkg.go.dev/github.com/shurcooL/github_flavored_markdown): provides a GitHub Flavored Markdown renderer with fenced code block highlighting, clickable heading anchor links. It's not customizable, and its goal is to produce HTML output equivalent to the [GitHub Markdown API endpoint](https://developer.github.com/v3/markdown/#render-a-markdown-document-in-raw-mode), except the rendering is performed locally. * [markdownfmt](https://github.com/shurcooL/markdownfmt): like gofmt, but for markdown. * [LaTeX output](https://gitlab.com/ambrevar/blackfriday-latex): renders output as LaTeX. * [bfchroma](https://github.com/Depado/bfchroma/): provides convenience integration with the [Chroma](https://github.com/alecthomas/chroma) code highlighting library. bfchroma is only compatible with v2 of Blackfriday and provides a drop-in renderer ready to use with Blackfriday, as well as options and means for further customization. * [Blackfriday-Confluence](https://github.com/kentaro-m/blackfriday-confluence): provides a [Confluence Wiki Markup](https://confluence.atlassian.com/doc/confluence-wiki-markup-251003035.html) renderer. * [Blackfriday-Slack](https://github.com/karriereat/blackfriday-slack): converts markdown to slack message style TODO ---- * More unit testing * Improve Unicode support. It does not understand all Unicode rules (about what constitutes a letter, a punctuation symbol, etc.), so it may fail to detect word boundaries correctly in some instances. It is safe on all UTF-8 input. License ------- [Blackfriday is distributed under the Simplified BSD License](LICENSE.txt) [1]: https://daringfireball.net/projects/markdown/ "Markdown" [2]: https://golang.org/ "Go Language" [3]: https://github.com/vmg/sundown "Sundown" [4]: https://pkg.go.dev/github.com/russross/blackfriday/v2#Parse "Parse func" [5]: https://github.com/microcosm-cc/bluemonday "Bluemonday" [BuildV2SVG]: https://travis-ci.org/russross/blackfriday.svg?branch=v2 [BuildV2URL]: https://travis-ci.org/russross/blackfriday [PkgGoDevV2SVG]: https://pkg.go.dev/badge/github.com/russross/blackfriday/v2 [PkgGoDevV2URL]: https://pkg.go.dev/github.com/russross/blackfriday/v2 ================================================ FILE: vendor/github.com/russross/blackfriday/v2/block.go ================================================ // // Blackfriday Markdown Processor // Available at http://github.com/russross/blackfriday // // Copyright © 2011 Russ Ross . // Distributed under the Simplified BSD License. // See README.md for details. // // // Functions to parse block-level elements. // package blackfriday import ( "bytes" "html" "regexp" "strings" "unicode" ) const ( charEntity = "&(?:#x[a-f0-9]{1,8}|#[0-9]{1,8}|[a-z][a-z0-9]{1,31});" escapable = "[!\"#$%&'()*+,./:;<=>?@[\\\\\\]^_`{|}~-]" ) var ( reBackslashOrAmp = regexp.MustCompile("[\\&]") reEntityOrEscapedChar = regexp.MustCompile("(?i)\\\\" + escapable + "|" + charEntity) ) // Parse block-level data. // Note: this function and many that it calls assume that // the input buffer ends with a newline. func (p *Markdown) block(data []byte) { // this is called recursively: enforce a maximum depth if p.nesting >= p.maxNesting { return } p.nesting++ // parse out one block-level construct at a time for len(data) > 0 { // prefixed heading: // // # Heading 1 // ## Heading 2 // ... // ###### Heading 6 if p.isPrefixHeading(data) { data = data[p.prefixHeading(data):] continue } // block of preformatted HTML: // //

// ... //
if data[0] == '<' { if i := p.html(data, true); i > 0 { data = data[i:] continue } } // title block // // % stuff // % more stuff // % even more stuff if p.extensions&Titleblock != 0 { if data[0] == '%' { if i := p.titleBlock(data, true); i > 0 { data = data[i:] continue } } } // blank lines. note: returns the # of bytes to skip if i := p.isEmpty(data); i > 0 { data = data[i:] continue } // indented code block: // // func max(a, b int) int { // if a > b { // return a // } // return b // } if p.codePrefix(data) > 0 { data = data[p.code(data):] continue } // fenced code block: // // ``` go // func fact(n int) int { // if n <= 1 { // return n // } // return n * fact(n-1) // } // ``` if p.extensions&FencedCode != 0 { if i := p.fencedCodeBlock(data, true); i > 0 { data = data[i:] continue } } // horizontal rule: // // ------ // or // ****** // or // ______ if p.isHRule(data) { p.addBlock(HorizontalRule, nil) var i int for i = 0; i < len(data) && data[i] != '\n'; i++ { } data = data[i:] continue } // block quote: // // > A big quote I found somewhere // > on the web if p.quotePrefix(data) > 0 { data = data[p.quote(data):] continue } // table: // // Name | Age | Phone // ------|-----|--------- // Bob | 31 | 555-1234 // Alice | 27 | 555-4321 if p.extensions&Tables != 0 { if i := p.table(data); i > 0 { data = data[i:] continue } } // an itemized/unordered list: // // * Item 1 // * Item 2 // // also works with + or - if p.uliPrefix(data) > 0 { data = data[p.list(data, 0):] continue } // a numbered/ordered list: // // 1. Item 1 // 2. Item 2 if p.oliPrefix(data) > 0 { data = data[p.list(data, ListTypeOrdered):] continue } // definition lists: // // Term 1 // : Definition a // : Definition b // // Term 2 // : Definition c if p.extensions&DefinitionLists != 0 { if p.dliPrefix(data) > 0 { data = data[p.list(data, ListTypeDefinition):] continue } } // anything else must look like a normal paragraph // note: this finds underlined headings, too data = data[p.paragraph(data):] } p.nesting-- } func (p *Markdown) addBlock(typ NodeType, content []byte) *Node { p.closeUnmatchedBlocks() container := p.addChild(typ, 0) container.content = content return container } func (p *Markdown) isPrefixHeading(data []byte) bool { if data[0] != '#' { return false } if p.extensions&SpaceHeadings != 0 { level := 0 for level < 6 && level < len(data) && data[level] == '#' { level++ } if level == len(data) || data[level] != ' ' { return false } } return true } func (p *Markdown) prefixHeading(data []byte) int { level := 0 for level < 6 && level < len(data) && data[level] == '#' { level++ } i := skipChar(data, level, ' ') end := skipUntilChar(data, i, '\n') skip := end id := "" if p.extensions&HeadingIDs != 0 { j, k := 0, 0 // find start/end of heading id for j = i; j < end-1 && (data[j] != '{' || data[j+1] != '#'); j++ { } for k = j + 1; k < end && data[k] != '}'; k++ { } // extract heading id iff found if j < end && k < end { id = string(data[j+2 : k]) end = j skip = k + 1 for end > 0 && data[end-1] == ' ' { end-- } } } for end > 0 && data[end-1] == '#' { if isBackslashEscaped(data, end-1) { break } end-- } for end > 0 && data[end-1] == ' ' { end-- } if end > i { if id == "" && p.extensions&AutoHeadingIDs != 0 { id = SanitizedAnchorName(string(data[i:end])) } block := p.addBlock(Heading, data[i:end]) block.HeadingID = id block.Level = level } return skip } func (p *Markdown) isUnderlinedHeading(data []byte) int { // test of level 1 heading if data[0] == '=' { i := skipChar(data, 1, '=') i = skipChar(data, i, ' ') if i < len(data) && data[i] == '\n' { return 1 } return 0 } // test of level 2 heading if data[0] == '-' { i := skipChar(data, 1, '-') i = skipChar(data, i, ' ') if i < len(data) && data[i] == '\n' { return 2 } return 0 } return 0 } func (p *Markdown) titleBlock(data []byte, doRender bool) int { if data[0] != '%' { return 0 } splitData := bytes.Split(data, []byte("\n")) var i int for idx, b := range splitData { if !bytes.HasPrefix(b, []byte("%")) { i = idx // - 1 break } } data = bytes.Join(splitData[0:i], []byte("\n")) consumed := len(data) data = bytes.TrimPrefix(data, []byte("% ")) data = bytes.Replace(data, []byte("\n% "), []byte("\n"), -1) block := p.addBlock(Heading, data) block.Level = 1 block.IsTitleblock = true return consumed } func (p *Markdown) html(data []byte, doRender bool) int { var i, j int // identify the opening tag if data[0] != '<' { return 0 } curtag, tagfound := p.htmlFindTag(data[1:]) // handle special cases if !tagfound { // check for an HTML comment if size := p.htmlComment(data, doRender); size > 0 { return size } // check for an
tag if size := p.htmlHr(data, doRender); size > 0 { return size } // no special case recognized return 0 } // look for an unindented matching closing tag // followed by a blank line found := false /* closetag := []byte("\n") j = len(curtag) + 1 for !found { // scan for a closing tag at the beginning of a line if skip := bytes.Index(data[j:], closetag); skip >= 0 { j += skip + len(closetag) } else { break } // see if it is the only thing on the line if skip := p.isEmpty(data[j:]); skip > 0 { // see if it is followed by a blank line/eof j += skip if j >= len(data) { found = true i = j } else { if skip := p.isEmpty(data[j:]); skip > 0 { j += skip found = true i = j } } } } */ // if not found, try a second pass looking for indented match // but not if tag is "ins" or "del" (following original Markdown.pl) if !found && curtag != "ins" && curtag != "del" { i = 1 for i < len(data) { i++ for i < len(data) && !(data[i-1] == '<' && data[i] == '/') { i++ } if i+2+len(curtag) >= len(data) { break } j = p.htmlFindEnd(curtag, data[i-1:]) if j > 0 { i += j - 1 found = true break } } } if !found { return 0 } // the end of the block has been found if doRender { // trim newlines end := i for end > 0 && data[end-1] == '\n' { end-- } finalizeHTMLBlock(p.addBlock(HTMLBlock, data[:end])) } return i } func finalizeHTMLBlock(block *Node) { block.Literal = block.content block.content = nil } // HTML comment, lax form func (p *Markdown) htmlComment(data []byte, doRender bool) int { i := p.inlineHTMLComment(data) // needs to end with a blank line if j := p.isEmpty(data[i:]); j > 0 { size := i + j if doRender { // trim trailing newlines end := size for end > 0 && data[end-1] == '\n' { end-- } block := p.addBlock(HTMLBlock, data[:end]) finalizeHTMLBlock(block) } return size } return 0 } // HR, which is the only self-closing block tag considered func (p *Markdown) htmlHr(data []byte, doRender bool) int { if len(data) < 4 { return 0 } if data[0] != '<' || (data[1] != 'h' && data[1] != 'H') || (data[2] != 'r' && data[2] != 'R') { return 0 } if data[3] != ' ' && data[3] != '/' && data[3] != '>' { // not an
tag after all; at least not a valid one return 0 } i := 3 for i < len(data) && data[i] != '>' && data[i] != '\n' { i++ } if i < len(data) && data[i] == '>' { i++ if j := p.isEmpty(data[i:]); j > 0 { size := i + j if doRender { // trim newlines end := size for end > 0 && data[end-1] == '\n' { end-- } finalizeHTMLBlock(p.addBlock(HTMLBlock, data[:end])) } return size } } return 0 } func (p *Markdown) htmlFindTag(data []byte) (string, bool) { i := 0 for i < len(data) && isalnum(data[i]) { i++ } key := string(data[:i]) if _, ok := blockTags[key]; ok { return key, true } return "", false } func (p *Markdown) htmlFindEnd(tag string, data []byte) int { // assume data[0] == '<' && data[1] == '/' already tested if tag == "hr" { return 2 } // check if tag is a match closetag := []byte("") if !bytes.HasPrefix(data, closetag) { return 0 } i := len(closetag) // check that the rest of the line is blank skip := 0 if skip = p.isEmpty(data[i:]); skip == 0 { return 0 } i += skip skip = 0 if i >= len(data) { return i } if p.extensions&LaxHTMLBlocks != 0 { return i } if skip = p.isEmpty(data[i:]); skip == 0 { // following line must be blank return 0 } return i + skip } func (*Markdown) isEmpty(data []byte) int { // it is okay to call isEmpty on an empty buffer if len(data) == 0 { return 0 } var i int for i = 0; i < len(data) && data[i] != '\n'; i++ { if data[i] != ' ' && data[i] != '\t' { return 0 } } if i < len(data) && data[i] == '\n' { i++ } return i } func (*Markdown) isHRule(data []byte) bool { i := 0 // skip up to three spaces for i < 3 && data[i] == ' ' { i++ } // look at the hrule char if data[i] != '*' && data[i] != '-' && data[i] != '_' { return false } c := data[i] // the whole line must be the char or whitespace n := 0 for i < len(data) && data[i] != '\n' { switch { case data[i] == c: n++ case data[i] != ' ': return false } i++ } return n >= 3 } // isFenceLine checks if there's a fence line (e.g., ``` or ``` go) at the beginning of data, // and returns the end index if so, or 0 otherwise. It also returns the marker found. // If info is not nil, it gets set to the syntax specified in the fence line. func isFenceLine(data []byte, info *string, oldmarker string) (end int, marker string) { i, size := 0, 0 // skip up to three spaces for i < len(data) && i < 3 && data[i] == ' ' { i++ } // check for the marker characters: ~ or ` if i >= len(data) { return 0, "" } if data[i] != '~' && data[i] != '`' { return 0, "" } c := data[i] // the whole line must be the same char or whitespace for i < len(data) && data[i] == c { size++ i++ } // the marker char must occur at least 3 times if size < 3 { return 0, "" } marker = string(data[i-size : i]) // if this is the end marker, it must match the beginning marker if oldmarker != "" && marker != oldmarker { return 0, "" } // TODO(shurcooL): It's probably a good idea to simplify the 2 code paths here // into one, always get the info string, and discard it if the caller doesn't care. if info != nil { infoLength := 0 i = skipChar(data, i, ' ') if i >= len(data) { if i == len(data) { return i, marker } return 0, "" } infoStart := i if data[i] == '{' { i++ infoStart++ for i < len(data) && data[i] != '}' && data[i] != '\n' { infoLength++ i++ } if i >= len(data) || data[i] != '}' { return 0, "" } // strip all whitespace at the beginning and the end // of the {} block for infoLength > 0 && isspace(data[infoStart]) { infoStart++ infoLength-- } for infoLength > 0 && isspace(data[infoStart+infoLength-1]) { infoLength-- } i++ i = skipChar(data, i, ' ') } else { for i < len(data) && !isverticalspace(data[i]) { infoLength++ i++ } } *info = strings.TrimSpace(string(data[infoStart : infoStart+infoLength])) } if i == len(data) { return i, marker } if i > len(data) || data[i] != '\n' { return 0, "" } return i + 1, marker // Take newline into account. } // fencedCodeBlock returns the end index if data contains a fenced code block at the beginning, // or 0 otherwise. It writes to out if doRender is true, otherwise it has no side effects. // If doRender is true, a final newline is mandatory to recognize the fenced code block. func (p *Markdown) fencedCodeBlock(data []byte, doRender bool) int { var info string beg, marker := isFenceLine(data, &info, "") if beg == 0 || beg >= len(data) { return 0 } fenceLength := beg - 1 var work bytes.Buffer work.Write([]byte(info)) work.WriteByte('\n') for { // safe to assume beg < len(data) // check for the end of the code block fenceEnd, _ := isFenceLine(data[beg:], nil, marker) if fenceEnd != 0 { beg += fenceEnd break } // copy the current line end := skipUntilChar(data, beg, '\n') + 1 // did we reach the end of the buffer without a closing marker? if end >= len(data) { return 0 } // verbatim copy to the working buffer if doRender { work.Write(data[beg:end]) } beg = end } if doRender { block := p.addBlock(CodeBlock, work.Bytes()) // TODO: get rid of temp buffer block.IsFenced = true block.FenceLength = fenceLength finalizeCodeBlock(block) } return beg } func unescapeChar(str []byte) []byte { if str[0] == '\\' { return []byte{str[1]} } return []byte(html.UnescapeString(string(str))) } func unescapeString(str []byte) []byte { if reBackslashOrAmp.Match(str) { return reEntityOrEscapedChar.ReplaceAllFunc(str, unescapeChar) } return str } func finalizeCodeBlock(block *Node) { if block.IsFenced { newlinePos := bytes.IndexByte(block.content, '\n') firstLine := block.content[:newlinePos] rest := block.content[newlinePos+1:] block.Info = unescapeString(bytes.Trim(firstLine, "\n")) block.Literal = rest } else { block.Literal = block.content } block.content = nil } func (p *Markdown) table(data []byte) int { table := p.addBlock(Table, nil) i, columns := p.tableHeader(data) if i == 0 { p.tip = table.Parent table.Unlink() return 0 } p.addBlock(TableBody, nil) for i < len(data) { pipes, rowStart := 0, i for ; i < len(data) && data[i] != '\n'; i++ { if data[i] == '|' { pipes++ } } if pipes == 0 { i = rowStart break } // include the newline in data sent to tableRow if i < len(data) && data[i] == '\n' { i++ } p.tableRow(data[rowStart:i], columns, false) } return i } // check if the specified position is preceded by an odd number of backslashes func isBackslashEscaped(data []byte, i int) bool { backslashes := 0 for i-backslashes-1 >= 0 && data[i-backslashes-1] == '\\' { backslashes++ } return backslashes&1 == 1 } func (p *Markdown) tableHeader(data []byte) (size int, columns []CellAlignFlags) { i := 0 colCount := 1 for i = 0; i < len(data) && data[i] != '\n'; i++ { if data[i] == '|' && !isBackslashEscaped(data, i) { colCount++ } } // doesn't look like a table header if colCount == 1 { return } // include the newline in the data sent to tableRow j := i if j < len(data) && data[j] == '\n' { j++ } header := data[:j] // column count ignores pipes at beginning or end of line if data[0] == '|' { colCount-- } if i > 2 && data[i-1] == '|' && !isBackslashEscaped(data, i-1) { colCount-- } columns = make([]CellAlignFlags, colCount) // move on to the header underline i++ if i >= len(data) { return } if data[i] == '|' && !isBackslashEscaped(data, i) { i++ } i = skipChar(data, i, ' ') // each column header is of form: / *:?-+:? *|/ with # dashes + # colons >= 3 // and trailing | optional on last column col := 0 for i < len(data) && data[i] != '\n' { dashes := 0 if data[i] == ':' { i++ columns[col] |= TableAlignmentLeft dashes++ } for i < len(data) && data[i] == '-' { i++ dashes++ } if i < len(data) && data[i] == ':' { i++ columns[col] |= TableAlignmentRight dashes++ } for i < len(data) && data[i] == ' ' { i++ } if i == len(data) { return } // end of column test is messy switch { case dashes < 3: // not a valid column return case data[i] == '|' && !isBackslashEscaped(data, i): // marker found, now skip past trailing whitespace col++ i++ for i < len(data) && data[i] == ' ' { i++ } // trailing junk found after last column if col >= colCount && i < len(data) && data[i] != '\n' { return } case (data[i] != '|' || isBackslashEscaped(data, i)) && col+1 < colCount: // something else found where marker was required return case data[i] == '\n': // marker is optional for the last column col++ default: // trailing junk found after last column return } } if col != colCount { return } p.addBlock(TableHead, nil) p.tableRow(header, columns, true) size = i if size < len(data) && data[size] == '\n' { size++ } return } func (p *Markdown) tableRow(data []byte, columns []CellAlignFlags, header bool) { p.addBlock(TableRow, nil) i, col := 0, 0 if data[i] == '|' && !isBackslashEscaped(data, i) { i++ } for col = 0; col < len(columns) && i < len(data); col++ { for i < len(data) && data[i] == ' ' { i++ } cellStart := i for i < len(data) && (data[i] != '|' || isBackslashEscaped(data, i)) && data[i] != '\n' { i++ } cellEnd := i // skip the end-of-cell marker, possibly taking us past end of buffer i++ for cellEnd > cellStart && cellEnd-1 < len(data) && data[cellEnd-1] == ' ' { cellEnd-- } cell := p.addBlock(TableCell, data[cellStart:cellEnd]) cell.IsHeader = header cell.Align = columns[col] } // pad it out with empty columns to get the right number for ; col < len(columns); col++ { cell := p.addBlock(TableCell, nil) cell.IsHeader = header cell.Align = columns[col] } // silently ignore rows with too many cells } // returns blockquote prefix length func (p *Markdown) quotePrefix(data []byte) int { i := 0 for i < 3 && i < len(data) && data[i] == ' ' { i++ } if i < len(data) && data[i] == '>' { if i+1 < len(data) && data[i+1] == ' ' { return i + 2 } return i + 1 } return 0 } // blockquote ends with at least one blank line // followed by something without a blockquote prefix func (p *Markdown) terminateBlockquote(data []byte, beg, end int) bool { if p.isEmpty(data[beg:]) <= 0 { return false } if end >= len(data) { return true } return p.quotePrefix(data[end:]) == 0 && p.isEmpty(data[end:]) == 0 } // parse a blockquote fragment func (p *Markdown) quote(data []byte) int { block := p.addBlock(BlockQuote, nil) var raw bytes.Buffer beg, end := 0, 0 for beg < len(data) { end = beg // Step over whole lines, collecting them. While doing that, check for // fenced code and if one's found, incorporate it altogether, // irregardless of any contents inside it for end < len(data) && data[end] != '\n' { if p.extensions&FencedCode != 0 { if i := p.fencedCodeBlock(data[end:], false); i > 0 { // -1 to compensate for the extra end++ after the loop: end += i - 1 break } } end++ } if end < len(data) && data[end] == '\n' { end++ } if pre := p.quotePrefix(data[beg:]); pre > 0 { // skip the prefix beg += pre } else if p.terminateBlockquote(data, beg, end) { break } // this line is part of the blockquote raw.Write(data[beg:end]) beg = end } p.block(raw.Bytes()) p.finalize(block) return end } // returns prefix length for block code func (p *Markdown) codePrefix(data []byte) int { if len(data) >= 1 && data[0] == '\t' { return 1 } if len(data) >= 4 && data[0] == ' ' && data[1] == ' ' && data[2] == ' ' && data[3] == ' ' { return 4 } return 0 } func (p *Markdown) code(data []byte) int { var work bytes.Buffer i := 0 for i < len(data) { beg := i for i < len(data) && data[i] != '\n' { i++ } if i < len(data) && data[i] == '\n' { i++ } blankline := p.isEmpty(data[beg:i]) > 0 if pre := p.codePrefix(data[beg:i]); pre > 0 { beg += pre } else if !blankline { // non-empty, non-prefixed line breaks the pre i = beg break } // verbatim copy to the working buffer if blankline { work.WriteByte('\n') } else { work.Write(data[beg:i]) } } // trim all the \n off the end of work workbytes := work.Bytes() eol := len(workbytes) for eol > 0 && workbytes[eol-1] == '\n' { eol-- } if eol != len(workbytes) { work.Truncate(eol) } work.WriteByte('\n') block := p.addBlock(CodeBlock, work.Bytes()) // TODO: get rid of temp buffer block.IsFenced = false finalizeCodeBlock(block) return i } // returns unordered list item prefix func (p *Markdown) uliPrefix(data []byte) int { i := 0 // start with up to 3 spaces for i < len(data) && i < 3 && data[i] == ' ' { i++ } if i >= len(data)-1 { return 0 } // need one of {'*', '+', '-'} followed by a space or a tab if (data[i] != '*' && data[i] != '+' && data[i] != '-') || (data[i+1] != ' ' && data[i+1] != '\t') { return 0 } return i + 2 } // returns ordered list item prefix func (p *Markdown) oliPrefix(data []byte) int { i := 0 // start with up to 3 spaces for i < 3 && i < len(data) && data[i] == ' ' { i++ } // count the digits start := i for i < len(data) && data[i] >= '0' && data[i] <= '9' { i++ } if start == i || i >= len(data)-1 { return 0 } // we need >= 1 digits followed by a dot and a space or a tab if data[i] != '.' || !(data[i+1] == ' ' || data[i+1] == '\t') { return 0 } return i + 2 } // returns definition list item prefix func (p *Markdown) dliPrefix(data []byte) int { if len(data) < 2 { return 0 } i := 0 // need a ':' followed by a space or a tab if data[i] != ':' || !(data[i+1] == ' ' || data[i+1] == '\t') { return 0 } for i < len(data) && data[i] == ' ' { i++ } return i + 2 } // parse ordered or unordered list block func (p *Markdown) list(data []byte, flags ListType) int { i := 0 flags |= ListItemBeginningOfList block := p.addBlock(List, nil) block.ListFlags = flags block.Tight = true for i < len(data) { skip := p.listItem(data[i:], &flags) if flags&ListItemContainsBlock != 0 { block.ListData.Tight = false } i += skip if skip == 0 || flags&ListItemEndOfList != 0 { break } flags &= ^ListItemBeginningOfList } above := block.Parent finalizeList(block) p.tip = above return i } // Returns true if the list item is not the same type as its parent list func (p *Markdown) listTypeChanged(data []byte, flags *ListType) bool { if p.dliPrefix(data) > 0 && *flags&ListTypeDefinition == 0 { return true } else if p.oliPrefix(data) > 0 && *flags&ListTypeOrdered == 0 { return true } else if p.uliPrefix(data) > 0 && (*flags&ListTypeOrdered != 0 || *flags&ListTypeDefinition != 0) { return true } return false } // Returns true if block ends with a blank line, descending if needed // into lists and sublists. func endsWithBlankLine(block *Node) bool { // TODO: figure this out. Always false now. for block != nil { //if block.lastLineBlank { //return true //} t := block.Type if t == List || t == Item { block = block.LastChild } else { break } } return false } func finalizeList(block *Node) { block.open = false item := block.FirstChild for item != nil { // check for non-final list item ending with blank line: if endsWithBlankLine(item) && item.Next != nil { block.ListData.Tight = false break } // recurse into children of list item, to see if there are spaces // between any of them: subItem := item.FirstChild for subItem != nil { if endsWithBlankLine(subItem) && (item.Next != nil || subItem.Next != nil) { block.ListData.Tight = false break } subItem = subItem.Next } item = item.Next } } // Parse a single list item. // Assumes initial prefix is already removed if this is a sublist. func (p *Markdown) listItem(data []byte, flags *ListType) int { // keep track of the indentation of the first line itemIndent := 0 if data[0] == '\t' { itemIndent += 4 } else { for itemIndent < 3 && data[itemIndent] == ' ' { itemIndent++ } } var bulletChar byte = '*' i := p.uliPrefix(data) if i == 0 { i = p.oliPrefix(data) } else { bulletChar = data[i-2] } if i == 0 { i = p.dliPrefix(data) // reset definition term flag if i > 0 { *flags &= ^ListTypeTerm } } if i == 0 { // if in definition list, set term flag and continue if *flags&ListTypeDefinition != 0 { *flags |= ListTypeTerm } else { return 0 } } // skip leading whitespace on first line for i < len(data) && data[i] == ' ' { i++ } // find the end of the line line := i for i > 0 && i < len(data) && data[i-1] != '\n' { i++ } // get working buffer var raw bytes.Buffer // put the first line into the working buffer raw.Write(data[line:i]) line = i // process the following lines containsBlankLine := false sublist := 0 codeBlockMarker := "" gatherlines: for line < len(data) { i++ // find the end of this line for i < len(data) && data[i-1] != '\n' { i++ } // if it is an empty line, guess that it is part of this item // and move on to the next line if p.isEmpty(data[line:i]) > 0 { containsBlankLine = true line = i continue } // calculate the indentation indent := 0 indentIndex := 0 if data[line] == '\t' { indentIndex++ indent += 4 } else { for indent < 4 && line+indent < i && data[line+indent] == ' ' { indent++ indentIndex++ } } chunk := data[line+indentIndex : i] if p.extensions&FencedCode != 0 { // determine if in or out of codeblock // if in codeblock, ignore normal list processing _, marker := isFenceLine(chunk, nil, codeBlockMarker) if marker != "" { if codeBlockMarker == "" { // start of codeblock codeBlockMarker = marker } else { // end of codeblock. codeBlockMarker = "" } } // we are in a codeblock, write line, and continue if codeBlockMarker != "" || marker != "" { raw.Write(data[line+indentIndex : i]) line = i continue gatherlines } } // evaluate how this line fits in switch { // is this a nested list item? case (p.uliPrefix(chunk) > 0 && !p.isHRule(chunk)) || p.oliPrefix(chunk) > 0 || p.dliPrefix(chunk) > 0: // to be a nested list, it must be indented more // if not, it is either a different kind of list // or the next item in the same list if indent <= itemIndent { if p.listTypeChanged(chunk, flags) { *flags |= ListItemEndOfList } else if containsBlankLine { *flags |= ListItemContainsBlock } break gatherlines } if containsBlankLine { *flags |= ListItemContainsBlock } // is this the first item in the nested list? if sublist == 0 { sublist = raw.Len() } // is this a nested prefix heading? case p.isPrefixHeading(chunk): // if the heading is not indented, it is not nested in the list // and thus ends the list if containsBlankLine && indent < 4 { *flags |= ListItemEndOfList break gatherlines } *flags |= ListItemContainsBlock // anything following an empty line is only part // of this item if it is indented 4 spaces // (regardless of the indentation of the beginning of the item) case containsBlankLine && indent < 4: if *flags&ListTypeDefinition != 0 && i < len(data)-1 { // is the next item still a part of this list? next := i for next < len(data) && data[next] != '\n' { next++ } for next < len(data)-1 && data[next] == '\n' { next++ } if i < len(data)-1 && data[i] != ':' && data[next] != ':' { *flags |= ListItemEndOfList } } else { *flags |= ListItemEndOfList } break gatherlines // a blank line means this should be parsed as a block case containsBlankLine: raw.WriteByte('\n') *flags |= ListItemContainsBlock } // if this line was preceded by one or more blanks, // re-introduce the blank into the buffer if containsBlankLine { containsBlankLine = false raw.WriteByte('\n') } // add the line into the working buffer without prefix raw.Write(data[line+indentIndex : i]) line = i } rawBytes := raw.Bytes() block := p.addBlock(Item, nil) block.ListFlags = *flags block.Tight = false block.BulletChar = bulletChar block.Delimiter = '.' // Only '.' is possible in Markdown, but ')' will also be possible in CommonMark // render the contents of the list item if *flags&ListItemContainsBlock != 0 && *flags&ListTypeTerm == 0 { // intermediate render of block item, except for definition term if sublist > 0 { p.block(rawBytes[:sublist]) p.block(rawBytes[sublist:]) } else { p.block(rawBytes) } } else { // intermediate render of inline item if sublist > 0 { child := p.addChild(Paragraph, 0) child.content = rawBytes[:sublist] p.block(rawBytes[sublist:]) } else { child := p.addChild(Paragraph, 0) child.content = rawBytes } } return line } // render a single paragraph that has already been parsed out func (p *Markdown) renderParagraph(data []byte) { if len(data) == 0 { return } // trim leading spaces beg := 0 for data[beg] == ' ' { beg++ } end := len(data) // trim trailing newline if data[len(data)-1] == '\n' { end-- } // trim trailing spaces for end > beg && data[end-1] == ' ' { end-- } p.addBlock(Paragraph, data[beg:end]) } func (p *Markdown) paragraph(data []byte) int { // prev: index of 1st char of previous line // line: index of 1st char of current line // i: index of cursor/end of current line var prev, line, i int tabSize := TabSizeDefault if p.extensions&TabSizeEight != 0 { tabSize = TabSizeDouble } // keep going until we find something to mark the end of the paragraph for i < len(data) { // mark the beginning of the current line prev = line current := data[i:] line = i // did we find a reference or a footnote? If so, end a paragraph // preceding it and report that we have consumed up to the end of that // reference: if refEnd := isReference(p, current, tabSize); refEnd > 0 { p.renderParagraph(data[:i]) return i + refEnd } // did we find a blank line marking the end of the paragraph? if n := p.isEmpty(current); n > 0 { // did this blank line followed by a definition list item? if p.extensions&DefinitionLists != 0 { if i < len(data)-1 && data[i+1] == ':' { return p.list(data[prev:], ListTypeDefinition) } } p.renderParagraph(data[:i]) return i + n } // an underline under some text marks a heading, so our paragraph ended on prev line if i > 0 { if level := p.isUnderlinedHeading(current); level > 0 { // render the paragraph p.renderParagraph(data[:prev]) // ignore leading and trailing whitespace eol := i - 1 for prev < eol && data[prev] == ' ' { prev++ } for eol > prev && data[eol-1] == ' ' { eol-- } id := "" if p.extensions&AutoHeadingIDs != 0 { id = SanitizedAnchorName(string(data[prev:eol])) } block := p.addBlock(Heading, data[prev:eol]) block.Level = level block.HeadingID = id // find the end of the underline for i < len(data) && data[i] != '\n' { i++ } return i } } // if the next line starts a block of HTML, then the paragraph ends here if p.extensions&LaxHTMLBlocks != 0 { if data[i] == '<' && p.html(current, false) > 0 { // rewind to before the HTML block p.renderParagraph(data[:i]) return i } } // if there's a prefixed heading or a horizontal rule after this, paragraph is over if p.isPrefixHeading(current) || p.isHRule(current) { p.renderParagraph(data[:i]) return i } // if there's a fenced code block, paragraph is over if p.extensions&FencedCode != 0 { if p.fencedCodeBlock(current, false) > 0 { p.renderParagraph(data[:i]) return i } } // if there's a definition list item, prev line is a definition term if p.extensions&DefinitionLists != 0 { if p.dliPrefix(current) != 0 { ret := p.list(data[prev:], ListTypeDefinition) return ret } } // if there's a list after this, paragraph is over if p.extensions&NoEmptyLineBeforeBlock != 0 { if p.uliPrefix(current) != 0 || p.oliPrefix(current) != 0 || p.quotePrefix(current) != 0 || p.codePrefix(current) != 0 { p.renderParagraph(data[:i]) return i } } // otherwise, scan to the beginning of the next line nl := bytes.IndexByte(data[i:], '\n') if nl >= 0 { i += nl + 1 } else { i += len(data[i:]) } } p.renderParagraph(data[:i]) return i } func skipChar(data []byte, start int, char byte) int { i := start for i < len(data) && data[i] == char { i++ } return i } func skipUntilChar(text []byte, start int, char byte) int { i := start for i < len(text) && text[i] != char { i++ } return i } // SanitizedAnchorName returns a sanitized anchor name for the given text. // // It implements the algorithm specified in the package comment. func SanitizedAnchorName(text string) string { var anchorName []rune futureDash := false for _, r := range text { switch { case unicode.IsLetter(r) || unicode.IsNumber(r): if futureDash && len(anchorName) > 0 { anchorName = append(anchorName, '-') } futureDash = false anchorName = append(anchorName, unicode.ToLower(r)) default: futureDash = true } } return string(anchorName) } ================================================ FILE: vendor/github.com/russross/blackfriday/v2/doc.go ================================================ // Package blackfriday is a markdown processor. // // It translates plain text with simple formatting rules into an AST, which can // then be further processed to HTML (provided by Blackfriday itself) or other // formats (provided by the community). // // The simplest way to invoke Blackfriday is to call the Run function. It will // take a text input and produce a text output in HTML (or other format). // // A slightly more sophisticated way to use Blackfriday is to create a Markdown // processor and to call Parse, which returns a syntax tree for the input // document. You can leverage Blackfriday's parsing for content extraction from // markdown documents. You can assign a custom renderer and set various options // to the Markdown processor. // // If you're interested in calling Blackfriday from command line, see // https://github.com/russross/blackfriday-tool. // // Sanitized Anchor Names // // Blackfriday includes an algorithm for creating sanitized anchor names // corresponding to a given input text. This algorithm is used to create // anchors for headings when AutoHeadingIDs extension is enabled. The // algorithm is specified below, so that other packages can create // compatible anchor names and links to those anchors. // // The algorithm iterates over the input text, interpreted as UTF-8, // one Unicode code point (rune) at a time. All runes that are letters (category L) // or numbers (category N) are considered valid characters. They are mapped to // lower case, and included in the output. All other runes are considered // invalid characters. Invalid characters that precede the first valid character, // as well as invalid character that follow the last valid character // are dropped completely. All other sequences of invalid characters // between two valid characters are replaced with a single dash character '-'. // // SanitizedAnchorName exposes this functionality, and can be used to // create compatible links to the anchor names generated by blackfriday. // This algorithm is also implemented in a small standalone package at // github.com/shurcooL/sanitized_anchor_name. It can be useful for clients // that want a small package and don't need full functionality of blackfriday. package blackfriday // NOTE: Keep Sanitized Anchor Name algorithm in sync with package // github.com/shurcooL/sanitized_anchor_name. // Otherwise, users of sanitized_anchor_name will get anchor names // that are incompatible with those generated by blackfriday. ================================================ FILE: vendor/github.com/russross/blackfriday/v2/entities.go ================================================ package blackfriday // Extracted from https://html.spec.whatwg.org/multipage/entities.json var entities = map[string]bool{ "Æ": true, "Æ": true, "&": true, "&": true, "Á": true, "Á": true, "Ă": true, "Â": true, "Â": true, "А": true, "𝔄": true, "À": true, "À": true, "Α": true, "Ā": true, "⩓": true, "Ą": true, "𝔸": true, "⁡": true, "Å": true, "Å": true, "𝒜": true, "≔": true, "Ã": true, "Ã": true, "Ä": true, "Ä": true, "∖": true, "⫧": true, "⌆": true, "Б": true, "∵": true, "ℬ": true, "Β": true, "𝔅": true, "𝔹": true, "˘": true, "ℬ": true, "≎": true, "Ч": true, "©": true, "©": true, "Ć": true, "⋒": true, "ⅅ": true, "ℭ": true, "Č": true, "Ç": true, "Ç": true, "Ĉ": true, "∰": true, "Ċ": true, "¸": true, "·": true, "ℭ": true, "Χ": true, "⊙": true, "⊖": true, "⊕": true, "⊗": true, "∲": true, "”": true, "’": true, "∷": true, "⩴": true, "≡": true, "∯": true, "∮": true, "ℂ": true, "∐": true, "∳": true, "⨯": true, "𝒞": true, "⋓": true, "≍": true, "ⅅ": true, "⤑": true, "Ђ": true, "Ѕ": true, "Џ": true, "‡": true, "↡": true, "⫤": true, "Ď": true, "Д": true, "∇": true, "Δ": true, "𝔇": true, "´": true, "˙": true, "˝": true, "`": true, "˜": true, "⋄": true, "ⅆ": true, "𝔻": true, "¨": true, "⃜": true, "≐": true, "∯": true, "¨": true, "⇓": true, "⇐": true, "⇔": true, "⫤": true, "⟸": true, "⟺": true, "⟹": true, "⇒": true, "⊨": true, "⇑": true, "⇕": true, "∥": true, "↓": true, "⤓": true, "⇵": true, "̑": true, "⥐": true, "⥞": true, "↽": true, "⥖": true, "⥟": true, "⇁": true, "⥗": true, "⊤": true, "↧": true, "⇓": true, "𝒟": true, "Đ": true, "Ŋ": true, "Ð": true, "Ð": true, "É": true, "É": true, "Ě": true, "Ê": true, "Ê": true, "Э": true, "Ė": true, "𝔈": true, "È": true, "È": true, "∈": true, "Ē": true, "◻": true, "▫": true, "Ę": true, "𝔼": true, "Ε": true, "⩵": true, "≂": true, "⇌": true, "ℰ": true, "⩳": true, "Η": true, "Ë": true, "Ë": true, "∃": true, "ⅇ": true, "Ф": true, "𝔉": true, "◼": true, "▪": true, "𝔽": true, "∀": true, "ℱ": true, "ℱ": true, "Ѓ": true, ">": true, ">": true, "Γ": true, "Ϝ": true, "Ğ": true, "Ģ": true, "Ĝ": true, "Г": true, "Ġ": true, "𝔊": true, "⋙": true, "𝔾": true, "≥": true, "⋛": true, "≧": true, "⪢": true, "≷": true, "⩾": true, "≳": true, "𝒢": true, "≫": true, "Ъ": true, "ˇ": true, "^": true, "Ĥ": true, "ℌ": true, "ℋ": true, "ℍ": true, "─": true, "ℋ": true, "Ħ": true, "≎": true, "≏": true, "Е": true, "IJ": true, "Ё": true, "Í": true, "Í": true, "Î": true, "Î": true, "И": true, "İ": true, "ℑ": true, "Ì": true, "Ì": true, "ℑ": true, "Ī": true, "ⅈ": true, "⇒": true, "∬": true, "∫": true, "⋂": true, "⁣": true, "⁢": true, "Į": true, "𝕀": true, "Ι": true, "ℐ": true, "Ĩ": true, "І": true, "Ï": true, "Ï": true, "Ĵ": true, "Й": true, "𝔍": true, "𝕁": true, "𝒥": true, "Ј": true, "Є": true, "Х": true, "Ќ": true, "Κ": true, "Ķ": true, "К": true, "𝔎": true, "𝕂": true, "𝒦": true, "Љ": true, "<": true, "<": true, "Ĺ": true, "Λ": true, "⟪": true, "ℒ": true, "↞": true, "Ľ": true, "Ļ": true, "Л": true, "⟨": true, "←": true, "⇤": true, "⇆": true, "⌈": true, "⟦": true, "⥡": true, "⇃": true, "⥙": true, "⌊": true, "↔": true, "⥎": true, "⊣": true, "↤": true, "⥚": true, "⊲": true, "⧏": true, "⊴": true, "⥑": true, "⥠": true, "↿": true, "⥘": true, "↼": true, "⥒": true, "⇐": true, "⇔": true, "⋚": true, "≦": true, "≶": true, "⪡": true, "⩽": true, "≲": true, "𝔏": true, "⋘": true, "⇚": true, "Ŀ": true, "⟵": true, "⟷": true, "⟶": true, "⟸": true, "⟺": true, "⟹": true, "𝕃": true, "↙": true, "↘": true, "ℒ": true, "↰": true, "Ł": true, "≪": true, "⤅": true, "М": true, " ": true, "ℳ": true, "𝔐": true, "∓": true, "𝕄": true, "ℳ": true, "Μ": true, "Њ": true, "Ń": true, "Ň": true, "Ņ": true, "Н": true, "​": true, "​": true, "​": true, "​": true, "≫": true, "≪": true, " ": true, "𝔑": true, "⁠": true, " ": true, "ℕ": true, "⫬": true, "≢": true, "≭": true, "∦": true, "∉": true, "≠": true, "≂̸": true, "∄": true, "≯": true, "≱": true, "≧̸": true, "≫̸": true, "≹": true, "⩾̸": true, "≵": true, "≎̸": true, "≏̸": true, "⋪": true, "⧏̸": true, "⋬": true, "≮": true, "≰": true, "≸": true, "≪̸": true, "⩽̸": true, "≴": true, "⪢̸": true, "⪡̸": true, "⊀": true, "⪯̸": true, "⋠": true, "∌": true, "⋫": true, "⧐̸": true, "⋭": true, "⊏̸": true, "⋢": true, "⊐̸": true, "⋣": true, "⊂⃒": true, "⊈": true, "⊁": true, "⪰̸": true, "⋡": true, "≿̸": true, "⊃⃒": true, "⊉": true, "≁": true, "≄": true, "≇": true, "≉": true, "∤": true, "𝒩": true, "Ñ": true, "Ñ": true, "Ν": true, "Œ": true, "Ó": true, "Ó": true, "Ô": true, "Ô": true, "О": true, "Ő": true, "𝔒": true, "Ò": true, "Ò": true, "Ō": true, "Ω": true, "Ο": true, "𝕆": true, "“": true, "‘": true, "⩔": true, "𝒪": true, "Ø": true, "Ø": true, "Õ": true, "Õ": true, "⨷": true, "Ö": true, "Ö": true, "‾": true, "⏞": true, "⎴": true, "⏜": true, "∂": true, "П": true, "𝔓": true, "Φ": true, "Π": true, "±": true, "ℌ": true, "ℙ": true, "⪻": true, "≺": true, "⪯": true, "≼": true, "≾": true, "″": true, "∏": true, "∷": true, "∝": true, "𝒫": true, "Ψ": true, """: true, """: true, "𝔔": true, "ℚ": true, "𝒬": true, "⤐": true, "®": true, "®": true, "Ŕ": true, "⟫": true, "↠": true, "⤖": true, "Ř": true, "Ŗ": true, "Р": true, "ℜ": true, "∋": true, "⇋": true, "⥯": true, "ℜ": true, "Ρ": true, "⟩": true, "→": true, "⇥": true, "⇄": true, "⌉": true, "⟧": true, "⥝": true, "⇂": true, "⥕": true, "⌋": true, "⊢": true, "↦": true, "⥛": true, "⊳": true, "⧐": true, "⊵": true, "⥏": true, "⥜": true, "↾": true, "⥔": true, "⇀": true, "⥓": true, "⇒": true, "ℝ": true, "⥰": true, "⇛": true, "ℛ": true, "↱": true, "⧴": true, "Щ": true, "Ш": true, "Ь": true, "Ś": true, "⪼": true, "Š": true, "Ş": true, "Ŝ": true, "С": true, "𝔖": true, "↓": true, "←": true, "→": true, "↑": true, "Σ": true, "∘": true, "𝕊": true, "√": true, "□": true, "⊓": true, "⊏": true, "⊑": true, "⊐": true, "⊒": true, "⊔": true, "𝒮": true, "⋆": true, "⋐": true, "⋐": true, "⊆": true, "≻": true, "⪰": true, "≽": true, "≿": true, "∋": true, "∑": true, "⋑": true, "⊃": true, "⊇": true, "⋑": true, "Þ": true, "Þ": true, "™": true, "Ћ": true, "Ц": true, " ": true, "Τ": true, "Ť": true, "Ţ": true, "Т": true, "𝔗": true, "∴": true, "Θ": true, "  ": true, " ": true, "∼": true, "≃": true, "≅": true, "≈": true, "𝕋": true, "⃛": true, "𝒯": true, "Ŧ": true, "Ú": true, "Ú": true, "↟": true, "⥉": true, "Ў": true, "Ŭ": true, "Û": true, "Û": true, "У": true, "Ű": true, "𝔘": true, "Ù": true, "Ù": true, "Ū": true, "_": true, "⏟": true, "⎵": true, "⏝": true, "⋃": true, "⊎": true, "Ų": true, "𝕌": true, "↑": true, "⤒": true, "⇅": true, "↕": true, "⥮": true, "⊥": true, "↥": true, "⇑": true, "⇕": true, "↖": true, "↗": true, "ϒ": true, "Υ": true, "Ů": true, "𝒰": true, "Ũ": true, "Ü": true, "Ü": true, "⊫": true, "⫫": true, "В": true, "⊩": true, "⫦": true, "⋁": true, "‖": true, "‖": true, "∣": true, "|": true, "❘": true, "≀": true, " ": true, "𝔙": true, "𝕍": true, "𝒱": true, "⊪": true, "Ŵ": true, "⋀": true, "𝔚": true, "𝕎": true, "𝒲": true, "𝔛": true, "Ξ": true, "𝕏": true, "𝒳": true, "Я": true, "Ї": true, "Ю": true, "Ý": true, "Ý": true, "Ŷ": true, "Ы": true, "𝔜": true, "𝕐": true, "𝒴": true, "Ÿ": true, "Ж": true, "Ź": true, "Ž": true, "З": true, "Ż": true, "​": true, "Ζ": true, "ℨ": true, "ℤ": true, "𝒵": true, "á": true, "á": true, "ă": true, "∾": true, "∾̳": true, "∿": true, "â": true, "â": true, "´": true, "´": true, "а": true, "æ": true, "æ": true, "⁡": true, "𝔞": true, "à": true, "à": true, "ℵ": true, "ℵ": true, "α": true, "ā": true, "⨿": true, "&": true, "&": true, "∧": true, "⩕": true, "⩜": true, "⩘": true, "⩚": true, "∠": true, "⦤": true, "∠": true, "∡": true, "⦨": true, "⦩": true, "⦪": true, "⦫": true, "⦬": true, "⦭": true, "⦮": true, "⦯": true, "∟": true, "⊾": true, "⦝": true, "∢": true, "Å": true, "⍼": true, "ą": true, "𝕒": true, "≈": true, "⩰": true, "⩯": true, "≊": true, "≋": true, "'": true, "≈": true, "≊": true, "å": true, "å": true, "𝒶": true, "*": true, "≈": true, "≍": true, "ã": true, "ã": true, "ä": true, "ä": true, "∳": true, "⨑": true, "⫭": true, "≌": true, "϶": true, "‵": true, "∽": true, "⋍": true, "⊽": true, "⌅": true, "⌅": true, "⎵": true, "⎶": true, "≌": true, "б": true, "„": true, "∵": true, "∵": true, "⦰": true, "϶": true, "ℬ": true, "β": true, "ℶ": true, "≬": true, "𝔟": true, "⋂": true, "◯": true, "⋃": true, "⨀": true, "⨁": true, "⨂": true, "⨆": true, "★": true, "▽": true, "△": true, "⨄": true, "⋁": true, "⋀": true, "⤍": true, "⧫": true, "▪": true, "▴": true, "▾": true, "◂": true, "▸": true, "␣": true, "▒": true, "░": true, "▓": true, "█": true, "=⃥": true, "≡⃥": true, "⌐": true, "𝕓": true, "⊥": true, "⊥": true, "⋈": true, "╗": true, "╔": true, "╖": true, "╓": true, "═": true, "╦": true, "╩": true, "╤": true, "╧": true, "╝": true, "╚": true, "╜": true, "╙": true, "║": true, "╬": true, "╣": true, "╠": true, "╫": true, "╢": true, "╟": true, "⧉": true, "╕": true, "╒": true, "┐": true, "┌": true, "─": true, "╥": true, "╨": true, "┬": true, "┴": true, "⊟": true, "⊞": true, "⊠": true, "╛": true, "╘": true, "┘": true, "└": true, "│": true, "╪": true, "╡": true, "╞": true, "┼": true, "┤": true, "├": true, "‵": true, "˘": true, "¦": true, "¦": true, "𝒷": true, "⁏": true, "∽": true, "⋍": true, "\": true, "⧅": true, "⟈": true, "•": true, "•": true, "≎": true, "⪮": true, "≏": true, "≏": true, "ć": true, "∩": true, "⩄": true, "⩉": true, "⩋": true, "⩇": true, "⩀": true, "∩︀": true, "⁁": true, "ˇ": true, "⩍": true, "č": true, "ç": true, "ç": true, "ĉ": true, "⩌": true, "⩐": true, "ċ": true, "¸": true, "¸": true, "⦲": true, "¢": true, "¢": true, "·": true, "𝔠": true, "ч": true, "✓": true, "✓": true, "χ": true, "○": true, "⧃": true, "ˆ": true, "≗": true, "↺": true, "↻": true, "®": true, "Ⓢ": true, "⊛": true, "⊚": true, "⊝": true, "≗": true, "⨐": true, "⫯": true, "⧂": true, "♣": true, "♣": true, ":": true, "≔": true, "≔": true, ",": true, "@": true, "∁": true, "∘": true, "∁": true, "ℂ": true, "≅": true, "⩭": true, "∮": true, "𝕔": true, "∐": true, "©": true, "©": true, "℗": true, "↵": true, "✗": true, "𝒸": true, "⫏": true, "⫑": true, "⫐": true, "⫒": true, "⋯": true, "⤸": true, "⤵": true, "⋞": true, "⋟": true, "↶": true, "⤽": true, "∪": true, "⩈": true, "⩆": true, "⩊": true, "⊍": true, "⩅": true, "∪︀": true, "↷": true, "⤼": true, "⋞": true, "⋟": true, "⋎": true, "⋏": true, "¤": true, "¤": true, "↶": true, "↷": true, "⋎": true, "⋏": true, "∲": true, "∱": true, "⌭": true, "⇓": true, "⥥": true, "†": true, "ℸ": true, "↓": true, "‐": true, "⊣": true, "⤏": true, "˝": true, "ď": true, "д": true, "ⅆ": true, "‡": true, "⇊": true, "⩷": true, "°": true, "°": true, "δ": true, "⦱": true, "⥿": true, "𝔡": true, "⇃": true, "⇂": true, "⋄": true, "⋄": true, "♦": true, "♦": true, "¨": true, "ϝ": true, "⋲": true, "÷": true, "÷": true, "÷": true, "⋇": true, "⋇": true, "ђ": true, "⌞": true, "⌍": true, "$": true, "𝕕": true, "˙": true, "≐": true, "≑": true, "∸": true, "∔": true, "⊡": true, "⌆": true, "↓": true, "⇊": true, "⇃": true, "⇂": true, "⤐": true, "⌟": true, "⌌": true, "𝒹": true, "ѕ": true, "⧶": true, "đ": true, "⋱": true, "▿": true, "▾": true, "⇵": true, "⥯": true, "⦦": true, "џ": true, "⟿": true, "⩷": true, "≑": true, "é": true, "é": true, "⩮": true, "ě": true, "≖": true, "ê": true, "ê": true, "≕": true, "э": true, "ė": true, "ⅇ": true, "≒": true, "𝔢": true, "⪚": true, "è": true, "è": true, "⪖": true, "⪘": true, "⪙": true, "⏧": true, "ℓ": true, "⪕": true, "⪗": true, "ē": true, "∅": true, "∅": true, "∅": true, " ": true, " ": true, " ": true, "ŋ": true, " ": true, "ę": true, "𝕖": true, "⋕": true, "⧣": true, "⩱": true, "ε": true, "ε": true, "ϵ": true, "≖": true, "≕": true, "≂": true, "⪖": true, "⪕": true, "=": true, "≟": true, "≡": true, "⩸": true, "⧥": true, "≓": true, "⥱": true, "ℯ": true, "≐": true, "≂": true, "η": true, "ð": true, "ð": true, "ë": true, "ë": true, "€": true, "!": true, "∃": true, "ℰ": true, "ⅇ": true, "≒": true, "ф": true, "♀": true, "ffi": true, "ff": true, "ffl": true, "𝔣": true, "fi": true, "fj": true, "♭": true, "fl": true, "▱": true, "ƒ": true, "𝕗": true, "∀": true, "⋔": true, "⫙": true, "⨍": true, "½": true, "½": true, "⅓": true, "¼": true, "¼": true, "⅕": true, "⅙": true, "⅛": true, "⅔": true, "⅖": true, "¾": true, "¾": true, "⅗": true, "⅜": true, "⅘": true, "⅚": true, "⅝": true, "⅞": true, "⁄": true, "⌢": true, "𝒻": true, "≧": true, "⪌": true, "ǵ": true, "γ": true, "ϝ": true, "⪆": true, "ğ": true, "ĝ": true, "г": true, "ġ": true, "≥": true, "⋛": true, "≥": true, "≧": true, "⩾": true, "⩾": true, "⪩": true, "⪀": true, "⪂": true, "⪄": true, "⋛︀": true, "⪔": true, "𝔤": true, "≫": true, "⋙": true, "ℷ": true, "ѓ": true, "≷": true, "⪒": true, "⪥": true, "⪤": true, "≩": true, "⪊": true, "⪊": true, "⪈": true, "⪈": true, "≩": true, "⋧": true, "𝕘": true, "`": true, "ℊ": true, "≳": true, "⪎": true, "⪐": true, ">": true, ">": true, "⪧": true, "⩺": true, "⋗": true, "⦕": true, "⩼": true, "⪆": true, "⥸": true, "⋗": true, "⋛": true, "⪌": true, "≷": true, "≳": true, "≩︀": true, "≩︀": true, "⇔": true, " ": true, "½": true, "ℋ": true, "ъ": true, "↔": true, "⥈": true, "↭": true, "ℏ": true, "ĥ": true, "♥": true, "♥": true, "…": true, "⊹": true, "𝔥": true, "⤥": true, "⤦": true, "⇿": true, "∻": true, "↩": true, "↪": true, "𝕙": true, "―": true, "𝒽": true, "ℏ": true, "ħ": true, "⁃": true, "‐": true, "í": true, "í": true, "⁣": true, "î": true, "î": true, "и": true, "е": true, "¡": true, "¡": true, "⇔": true, "𝔦": true, "ì": true, "ì": true, "ⅈ": true, "⨌": true, "∭": true, "⧜": true, "℩": true, "ij": true, "ī": true, "ℑ": true, "ℐ": true, "ℑ": true, "ı": true, "⊷": true, "Ƶ": true, "∈": true, "℅": true, "∞": true, "⧝": true, "ı": true, "∫": true, "⊺": true, "ℤ": true, "⊺": true, "⨗": true, "⨼": true, "ё": true, "į": true, "𝕚": true, "ι": true, "⨼": true, "¿": true, "¿": true, "𝒾": true, "∈": true, "⋹": true, "⋵": true, "⋴": true, "⋳": true, "∈": true, "⁢": true, "ĩ": true, "і": true, "ï": true, "ï": true, "ĵ": true, "й": true, "𝔧": true, "ȷ": true, "𝕛": true, "𝒿": true, "ј": true, "є": true, "κ": true, "ϰ": true, "ķ": true, "к": true, "𝔨": true, "ĸ": true, "х": true, "ќ": true, "𝕜": true, "𝓀": true, "⇚": true, "⇐": true, "⤛": true, "⤎": true, "≦": true, "⪋": true, "⥢": true, "ĺ": true, "⦴": true, "ℒ": true, "λ": true, "⟨": true, "⦑": true, "⟨": true, "⪅": true, "«": true, "«": true, "←": true, "⇤": true, "⤟": true, "⤝": true, "↩": true, "↫": true, "⤹": true, "⥳": true, "↢": true, "⪫": true, "⤙": true, "⪭": true, "⪭︀": true, "⤌": true, "❲": true, "{": true, "[": true, "⦋": true, "⦏": true, "⦍": true, "ľ": true, "ļ": true, "⌈": true, "{": true, "л": true, "⤶": true, "“": true, "„": true, "⥧": true, "⥋": true, "↲": true, "≤": true, "←": true, "↢": true, "↽": true, "↼": true, "⇇": true, "↔": true, "⇆": true, "⇋": true, "↭": true, "⋋": true, "⋚": true, "≤": true, "≦": true, "⩽": true, "⩽": true, "⪨": true, "⩿": true, "⪁": true, "⪃": true, "⋚︀": true, "⪓": true, "⪅": true, "⋖": true, "⋚": true, "⪋": true, "≶": true, "≲": true, "⥼": true, "⌊": true, "𝔩": true, "≶": true, "⪑": true, "↽": true, "↼": true, "⥪": true, "▄": true, "љ": true, "≪": true, "⇇": true, "⌞": true, "⥫": true, "◺": true, "ŀ": true, "⎰": true, "⎰": true, "≨": true, "⪉": true, "⪉": true, "⪇": true, "⪇": true, "≨": true, "⋦": true, "⟬": true, "⇽": true, "⟦": true, "⟵": true, "⟷": true, "⟼": true, "⟶": true, "↫": true, "↬": true, "⦅": true, "𝕝": true, "⨭": true, "⨴": true, "∗": true, "_": true, "◊": true, "◊": true, "⧫": true, "(": true, "⦓": true, "⇆": true, "⌟": true, "⇋": true, "⥭": true, "‎": true, "⊿": true, "‹": true, "𝓁": true, "↰": true, "≲": true, "⪍": true, "⪏": true, "[": true, "‘": true, "‚": true, "ł": true, "<": true, "<": true, "⪦": true, "⩹": true, "⋖": true, "⋋": true, "⋉": true, "⥶": true, "⩻": true, "⦖": true, "◃": true, "⊴": true, "◂": true, "⥊": true, "⥦": true, "≨︀": true, "≨︀": true, "∺": true, "¯": true, "¯": true, "♂": true, "✠": true, "✠": true, "↦": true, "↦": true, "↧": true, "↤": true, "↥": true, "▮": true, "⨩": true, "м": true, "—": true, "∡": true, "𝔪": true, "℧": true, "µ": true, "µ": true, "∣": true, "*": true, "⫰": true, "·": true, "·": true, "−": true, "⊟": true, "∸": true, "⨪": true, "⫛": true, "…": true, "∓": true, "⊧": true, "𝕞": true, "∓": true, "𝓂": true, "∾": true, "μ": true, "⊸": true, "⊸": true, "⋙̸": true, "≫⃒": true, "≫̸": true, "⇍": true, "⇎": true, "⋘̸": true, "≪⃒": true, "≪̸": true, "⇏": true, "⊯": true, "⊮": true, "∇": true, "ń": true, "∠⃒": true, "≉": true, "⩰̸": true, "≋̸": true, "ʼn": true, "≉": true, "♮": true, "♮": true, "ℕ": true, " ": true, " ": true, "≎̸": true, "≏̸": true, "⩃": true, "ň": true, "ņ": true, "≇": true, "⩭̸": true, "⩂": true, "н": true, "–": true, "≠": true, "⇗": true, "⤤": true, "↗": true, "↗": true, "≐̸": true, "≢": true, "⤨": true, "≂̸": true, "∄": true, "∄": true, "𝔫": true, "≧̸": true, "≱": true, "≱": true, "≧̸": true, "⩾̸": true, "⩾̸": true, "≵": true, "≯": true, "≯": true, "⇎": true, "↮": true, "⫲": true, "∋": true, "⋼": true, "⋺": true, "∋": true, "њ": true, "⇍": true, "≦̸": true, "↚": true, "‥": true, "≰": true, "↚": true, "↮": true, "≰": true, "≦̸": true, "⩽̸": true, "⩽̸": true, "≮": true, "≴": true, "≮": true, "⋪": true, "⋬": true, "∤": true, "𝕟": true, "¬": true, "¬": true, "∉": true, "⋹̸": true, "⋵̸": true, "∉": true, "⋷": true, "⋶": true, "∌": true, "∌": true, "⋾": true, "⋽": true, "∦": true, "∦": true, "⫽⃥": true, "∂̸": true, "⨔": true, "⊀": true, "⋠": true, "⪯̸": true, "⊀": true, "⪯̸": true, "⇏": true, "↛": true, "⤳̸": true, "↝̸": true, "↛": true, "⋫": true, "⋭": true, "⊁": true, "⋡": true, "⪰̸": true, "𝓃": true, "∤": true, "∦": true, "≁": true, "≄": true, "≄": true, "∤": true, "∦": true, "⋢": true, "⋣": true, "⊄": true, "⫅̸": true, "⊈": true, "⊂⃒": true, "⊈": true, "⫅̸": true, "⊁": true, "⪰̸": true, "⊅": true, "⫆̸": true, "⊉": true, "⊃⃒": true, "⊉": true, "⫆̸": true, "≹": true, "ñ": true, "ñ": true, "≸": true, "⋪": true, "⋬": true, "⋫": true, "⋭": true, "ν": true, "#": true, "№": true, " ": true, "⊭": true, "⤄": true, "≍⃒": true, "⊬": true, "≥⃒": true, ">⃒": true, "⧞": true, "⤂": true, "≤⃒": true, "<⃒": true, "⊴⃒": true, "⤃": true, "⊵⃒": true, "∼⃒": true, "⇖": true, "⤣": true, "↖": true, "↖": true, "⤧": true, "Ⓢ": true, "ó": true, "ó": true, "⊛": true, "⊚": true, "ô": true, "ô": true, "о": true, "⊝": true, "ő": true, "⨸": true, "⊙": true, "⦼": true, "œ": true, "⦿": true, "𝔬": true, "˛": true, "ò": true, "ò": true, "⧁": true, "⦵": true, "Ω": true, "∮": true, "↺": true, "⦾": true, "⦻": true, "‾": true, "⧀": true, "ō": true, "ω": true, "ο": true, "⦶": true, "⊖": true, "𝕠": true, "⦷": true, "⦹": true, "⊕": true, "∨": true, "↻": true, "⩝": true, "ℴ": true, "ℴ": true, "ª": true, "ª": true, "º": true, "º": true, "⊶": true, "⩖": true, "⩗": true, "⩛": true, "ℴ": true, "ø": true, "ø": true, "⊘": true, "õ": true, "õ": true, "⊗": true, "⨶": true, "ö": true, "ö": true, "⌽": true, "∥": true, "¶": true, "¶": true, "∥": true, "⫳": true, "⫽": true, "∂": true, "п": true, "%": true, ".": true, "‰": true, "⊥": true, "‱": true, "𝔭": true, "φ": true, "ϕ": true, "ℳ": true, "☎": true, "π": true, "⋔": true, "ϖ": true, "ℏ": true, "ℎ": true, "ℏ": true, "+": true, "⨣": true, "⊞": true, "⨢": true, "∔": true, "⨥": true, "⩲": true, "±": true, "±": true, "⨦": true, "⨧": true, "±": true, "⨕": true, "𝕡": true, "£": true, "£": true, "≺": true, "⪳": true, "⪷": true, "≼": true, "⪯": true, "≺": true, "⪷": true, "≼": true, "⪯": true, "⪹": true, "⪵": true, "⋨": true, "≾": true, "′": true, "ℙ": true, "⪵": true, "⪹": true, "⋨": true, "∏": true, "⌮": true, "⌒": true, "⌓": true, "∝": true, "∝": true, "≾": true, "⊰": true, "𝓅": true, "ψ": true, " ": true, "𝔮": true, "⨌": true, "𝕢": true, "⁗": true, "𝓆": true, "ℍ": true, "⨖": true, "?": true, "≟": true, """: true, """: true, "⇛": true, "⇒": true, "⤜": true, "⤏": true, "⥤": true, "∽̱": true, "ŕ": true, "√": true, "⦳": true, "⟩": true, "⦒": true, "⦥": true, "⟩": true, "»": true, "»": true, "→": true, "⥵": true, "⇥": true, "⤠": true, "⤳": true, "⤞": true, "↪": true, "↬": true, "⥅": true, "⥴": true, "↣": true, "↝": true, "⤚": true, "∶": true, "ℚ": true, "⤍": true, "❳": true, "}": true, "]": true, "⦌": true, "⦎": true, "⦐": true, "ř": true, "ŗ": true, "⌉": true, "}": true, "р": true, "⤷": true, "⥩": true, "”": true, "”": true, "↳": true, "ℜ": true, "ℛ": true, "ℜ": true, "ℝ": true, "▭": true, "®": true, "®": true, "⥽": true, "⌋": true, "𝔯": true, "⇁": true, "⇀": true, "⥬": true, "ρ": true, "ϱ": true, "→": true, "↣": true, "⇁": true, "⇀": true, "⇄": true, "⇌": true, "⇉": true, "↝": true, "⋌": true, "˚": true, "≓": true, "⇄": true, "⇌": true, "‏": true, "⎱": true, "⎱": true, "⫮": true, "⟭": true, "⇾": true, "⟧": true, "⦆": true, "𝕣": true, "⨮": true, "⨵": true, ")": true, "⦔": true, "⨒": true, "⇉": true, "›": true, "𝓇": true, "↱": true, "]": true, "’": true, "’": true, "⋌": true, "⋊": true, "▹": true, "⊵": true, "▸": true, "⧎": true, "⥨": true, "℞": true, "ś": true, "‚": true, "≻": true, "⪴": true, "⪸": true, "š": true, "≽": true, "⪰": true, "ş": true, "ŝ": true, "⪶": true, "⪺": true, "⋩": true, "⨓": true, "≿": true, "с": true, "⋅": true, "⊡": true, "⩦": true, "⇘": true, "⤥": true, "↘": true, "↘": true, "§": true, "§": true, ";": true, "⤩": true, "∖": true, "∖": true, "✶": true, "𝔰": true, "⌢": true, "♯": true, "щ": true, "ш": true, "∣": true, "∥": true, "­": true, "­": true, "σ": true, "ς": true, "ς": true, "∼": true, "⩪": true, "≃": true, "≃": true, "⪞": true, "⪠": true, "⪝": true, "⪟": true, "≆": true, "⨤": true, "⥲": true, "←": true, "∖": true, "⨳": true, "⧤": true, "∣": true, "⌣": true, "⪪": true, "⪬": true, "⪬︀": true, "ь": true, "/": true, "⧄": true, "⌿": true, "𝕤": true, "♠": true, "♠": true, "∥": true, "⊓": true, "⊓︀": true, "⊔": true, "⊔︀": true, "⊏": true, "⊑": true, "⊏": true, "⊑": true, "⊐": true, "⊒": true, "⊐": true, "⊒": true, "□": true, "□": true, "▪": true, "▪": true, "→": true, "𝓈": true, "∖": true, "⌣": true, "⋆": true, "☆": true, "★": true, "ϵ": true, "ϕ": true, "¯": true, "⊂": true, "⫅": true, "⪽": true, "⊆": true, "⫃": true, "⫁": true, "⫋": true, "⊊": true, "⪿": true, "⥹": true, "⊂": true, "⊆": true, "⫅": true, "⊊": true, "⫋": true, "⫇": true, "⫕": true, "⫓": true, "≻": true, "⪸": true, "≽": true, "⪰": true, "⪺": true, "⪶": true, "⋩": true, "≿": true, "∑": true, "♪": true, "¹": true, "¹": true, "²": true, "²": true, "³": true, "³": true, "⊃": true, "⫆": true, "⪾": true, "⫘": true, "⊇": true, "⫄": true, "⟉": true, "⫗": true, "⥻": true, "⫂": true, "⫌": true, "⊋": true, "⫀": true, "⊃": true, "⊇": true, "⫆": true, "⊋": true, "⫌": true, "⫈": true, "⫔": true, "⫖": true, "⇙": true, "⤦": true, "↙": true, "↙": true, "⤪": true, "ß": true, "ß": true, "⌖": true, "τ": true, "⎴": true, "ť": true, "ţ": true, "т": true, "⃛": true, "⌕": true, "𝔱": true, "∴": true, "∴": true, "θ": true, "ϑ": true, "ϑ": true, "≈": true, "∼": true, " ": true, "≈": true, "∼": true, "þ": true, "þ": true, "˜": true, "×": true, "×": true, "⊠": true, "⨱": true, "⨰": true, "∭": true, "⤨": true, "⊤": true, "⌶": true, "⫱": true, "𝕥": true, "⫚": true, "⤩": true, "‴": true, "™": true, "▵": true, "▿": true, "◃": true, "⊴": true, "≜": true, "▹": true, "⊵": true, "◬": true, "≜": true, "⨺": true, "⨹": true, "⧍": true, "⨻": true, "⏢": true, "𝓉": true, "ц": true, "ћ": true, "ŧ": true, "≬": true, "↞": true, "↠": true, "⇑": true, "⥣": true, "ú": true, "ú": true, "↑": true, "ў": true, "ŭ": true, "û": true, "û": true, "у": true, "⇅": true, "ű": true, "⥮": true, "⥾": true, "𝔲": true, "ù": true, "ù": true, "↿": true, "↾": true, "▀": true, "⌜": true, "⌜": true, "⌏": true, "◸": true, "ū": true, "¨": true, "¨": true, "ų": true, "𝕦": true, "↑": true, "↕": true, "↿": true, "↾": true, "⊎": true, "υ": true, "ϒ": true, "υ": true, "⇈": true, "⌝": true, "⌝": true, "⌎": true, "ů": true, "◹": true, "𝓊": true, "⋰": true, "ũ": true, "▵": true, "▴": true, "⇈": true, "ü": true, "ü": true, "⦧": true, "⇕": true, "⫨": true, "⫩": true, "⊨": true, "⦜": true, "ϵ": true, "ϰ": true, "∅": true, "ϕ": true, "ϖ": true, "∝": true, "↕": true, "ϱ": true, "ς": true, "⊊︀": true, "⫋︀": true, "⊋︀": true, "⫌︀": true, "ϑ": true, "⊲": true, "⊳": true, "в": true, "⊢": true, "∨": true, "⊻": true, "≚": true, "⋮": true, "|": true, "|": true, "𝔳": true, "⊲": true, "⊂⃒": true, "⊃⃒": true, "𝕧": true, "∝": true, "⊳": true, "𝓋": true, "⫋︀": true, "⊊︀": true, "⫌︀": true, "⊋︀": true, "⦚": true, "ŵ": true, "⩟": true, "∧": true, "≙": true, "℘": true, "𝔴": true, "𝕨": true, "℘": true, "≀": true, "≀": true, "𝓌": true, "⋂": true, "◯": true, "⋃": true, "▽": true, "𝔵": true, "⟺": true, "⟷": true, "ξ": true, "⟸": true, "⟵": true, "⟼": true, "⋻": true, "⨀": true, "𝕩": true, "⨁": true, "⨂": true, "⟹": true, "⟶": true, "𝓍": true, "⨆": true, "⨄": true, "△": true, "⋁": true, "⋀": true, "ý": true, "ý": true, "я": true, "ŷ": true, "ы": true, "¥": true, "¥": true, "𝔶": true, "ї": true, "𝕪": true, "𝓎": true, "ю": true, "ÿ": true, "ÿ": true, "ź": true, "ž": true, "з": true, "ż": true, "ℨ": true, "ζ": true, "𝔷": true, "ж": true, "⇝": true, "𝕫": true, "𝓏": true, "‍": true, "‌": true, } ================================================ FILE: vendor/github.com/russross/blackfriday/v2/esc.go ================================================ package blackfriday import ( "html" "io" ) var htmlEscaper = [256][]byte{ '&': []byte("&"), '<': []byte("<"), '>': []byte(">"), '"': []byte("""), } func escapeHTML(w io.Writer, s []byte) { escapeEntities(w, s, false) } func escapeAllHTML(w io.Writer, s []byte) { escapeEntities(w, s, true) } func escapeEntities(w io.Writer, s []byte, escapeValidEntities bool) { var start, end int for end < len(s) { escSeq := htmlEscaper[s[end]] if escSeq != nil { isEntity, entityEnd := nodeIsEntity(s, end) if isEntity && !escapeValidEntities { w.Write(s[start : entityEnd+1]) start = entityEnd + 1 } else { w.Write(s[start:end]) w.Write(escSeq) start = end + 1 } } end++ } if start < len(s) && end <= len(s) { w.Write(s[start:end]) } } func nodeIsEntity(s []byte, end int) (isEntity bool, endEntityPos int) { isEntity = false endEntityPos = end + 1 if s[end] == '&' { for endEntityPos < len(s) { if s[endEntityPos] == ';' { if entities[string(s[end:endEntityPos+1])] { isEntity = true break } } if !isalnum(s[endEntityPos]) && s[endEntityPos] != '&' && s[endEntityPos] != '#' { break } endEntityPos++ } } return isEntity, endEntityPos } func escLink(w io.Writer, text []byte) { unesc := html.UnescapeString(string(text)) escapeHTML(w, []byte(unesc)) } ================================================ FILE: vendor/github.com/russross/blackfriday/v2/html.go ================================================ // // Blackfriday Markdown Processor // Available at http://github.com/russross/blackfriday // // Copyright © 2011 Russ Ross . // Distributed under the Simplified BSD License. // See README.md for details. // // // // HTML rendering backend // // package blackfriday import ( "bytes" "fmt" "io" "regexp" "strings" ) // HTMLFlags control optional behavior of HTML renderer. type HTMLFlags int // HTML renderer configuration options. const ( HTMLFlagsNone HTMLFlags = 0 SkipHTML HTMLFlags = 1 << iota // Skip preformatted HTML blocks SkipImages // Skip embedded images SkipLinks // Skip all links Safelink // Only link to trusted protocols NofollowLinks // Only link with rel="nofollow" NoreferrerLinks // Only link with rel="noreferrer" NoopenerLinks // Only link with rel="noopener" HrefTargetBlank // Add a blank target CompletePage // Generate a complete HTML page UseXHTML // Generate XHTML output instead of HTML FootnoteReturnLinks // Generate a link at the end of a footnote to return to the source Smartypants // Enable smart punctuation substitutions SmartypantsFractions // Enable smart fractions (with Smartypants) SmartypantsDashes // Enable smart dashes (with Smartypants) SmartypantsLatexDashes // Enable LaTeX-style dashes (with Smartypants) SmartypantsAngledQuotes // Enable angled double quotes (with Smartypants) for double quotes rendering SmartypantsQuotesNBSP // Enable « French guillemets » (with Smartypants) TOC // Generate a table of contents ) var ( htmlTagRe = regexp.MustCompile("(?i)^" + htmlTag) ) const ( htmlTag = "(?:" + openTag + "|" + closeTag + "|" + htmlComment + "|" + processingInstruction + "|" + declaration + "|" + cdata + ")" closeTag = "]" openTag = "<" + tagName + attribute + "*" + "\\s*/?>" attribute = "(?:" + "\\s+" + attributeName + attributeValueSpec + "?)" attributeValue = "(?:" + unquotedValue + "|" + singleQuotedValue + "|" + doubleQuotedValue + ")" attributeValueSpec = "(?:" + "\\s*=" + "\\s*" + attributeValue + ")" attributeName = "[a-zA-Z_:][a-zA-Z0-9:._-]*" cdata = "" declaration = "]*>" doubleQuotedValue = "\"[^\"]*\"" htmlComment = "|" processingInstruction = "[<][?].*?[?][>]" singleQuotedValue = "'[^']*'" tagName = "[A-Za-z][A-Za-z0-9-]*" unquotedValue = "[^\"'=<>`\\x00-\\x20]+" ) // HTMLRendererParameters is a collection of supplementary parameters tweaking // the behavior of various parts of HTML renderer. type HTMLRendererParameters struct { // Prepend this text to each relative URL. AbsolutePrefix string // Add this text to each footnote anchor, to ensure uniqueness. FootnoteAnchorPrefix string // Show this text inside the tag for a footnote return link, if the // HTML_FOOTNOTE_RETURN_LINKS flag is enabled. If blank, the string // [return] is used. FootnoteReturnLinkContents string // If set, add this text to the front of each Heading ID, to ensure // uniqueness. HeadingIDPrefix string // If set, add this text to the back of each Heading ID, to ensure uniqueness. HeadingIDSuffix string // Increase heading levels: if the offset is 1,

becomes

etc. // Negative offset is also valid. // Resulting levels are clipped between 1 and 6. HeadingLevelOffset int Title string // Document title (used if CompletePage is set) CSS string // Optional CSS file URL (used if CompletePage is set) Icon string // Optional icon file URL (used if CompletePage is set) Flags HTMLFlags // Flags allow customizing this renderer's behavior } // HTMLRenderer is a type that implements the Renderer interface for HTML output. // // Do not create this directly, instead use the NewHTMLRenderer function. type HTMLRenderer struct { HTMLRendererParameters closeTag string // how to end singleton tags: either " />" or ">" // Track heading IDs to prevent ID collision in a single generation. headingIDs map[string]int lastOutputLen int disableTags int sr *SPRenderer } const ( xhtmlClose = " />" htmlClose = ">" ) // NewHTMLRenderer creates and configures an HTMLRenderer object, which // satisfies the Renderer interface. func NewHTMLRenderer(params HTMLRendererParameters) *HTMLRenderer { // configure the rendering engine closeTag := htmlClose if params.Flags&UseXHTML != 0 { closeTag = xhtmlClose } if params.FootnoteReturnLinkContents == "" { // U+FE0E is VARIATION SELECTOR-15. // It suppresses automatic emoji presentation of the preceding // U+21A9 LEFTWARDS ARROW WITH HOOK on iOS and iPadOS. params.FootnoteReturnLinkContents = "↩\ufe0e" } return &HTMLRenderer{ HTMLRendererParameters: params, closeTag: closeTag, headingIDs: make(map[string]int), sr: NewSmartypantsRenderer(params.Flags), } } func isHTMLTag(tag []byte, tagname string) bool { found, _ := findHTMLTagPos(tag, tagname) return found } // Look for a character, but ignore it when it's in any kind of quotes, it // might be JavaScript func skipUntilCharIgnoreQuotes(html []byte, start int, char byte) int { inSingleQuote := false inDoubleQuote := false inGraveQuote := false i := start for i < len(html) { switch { case html[i] == char && !inSingleQuote && !inDoubleQuote && !inGraveQuote: return i case html[i] == '\'': inSingleQuote = !inSingleQuote case html[i] == '"': inDoubleQuote = !inDoubleQuote case html[i] == '`': inGraveQuote = !inGraveQuote } i++ } return start } func findHTMLTagPos(tag []byte, tagname string) (bool, int) { i := 0 if i < len(tag) && tag[0] != '<' { return false, -1 } i++ i = skipSpace(tag, i) if i < len(tag) && tag[i] == '/' { i++ } i = skipSpace(tag, i) j := 0 for ; i < len(tag); i, j = i+1, j+1 { if j >= len(tagname) { break } if strings.ToLower(string(tag[i]))[0] != tagname[j] { return false, -1 } } if i == len(tag) { return false, -1 } rightAngle := skipUntilCharIgnoreQuotes(tag, i, '>') if rightAngle >= i { return true, rightAngle } return false, -1 } func skipSpace(tag []byte, i int) int { for i < len(tag) && isspace(tag[i]) { i++ } return i } func isRelativeLink(link []byte) (yes bool) { // a tag begin with '#' if link[0] == '#' { return true } // link begin with '/' but not '//', the second maybe a protocol relative link if len(link) >= 2 && link[0] == '/' && link[1] != '/' { return true } // only the root '/' if len(link) == 1 && link[0] == '/' { return true } // current directory : begin with "./" if bytes.HasPrefix(link, []byte("./")) { return true } // parent directory : begin with "../" if bytes.HasPrefix(link, []byte("../")) { return true } return false } func (r *HTMLRenderer) ensureUniqueHeadingID(id string) string { for count, found := r.headingIDs[id]; found; count, found = r.headingIDs[id] { tmp := fmt.Sprintf("%s-%d", id, count+1) if _, tmpFound := r.headingIDs[tmp]; !tmpFound { r.headingIDs[id] = count + 1 id = tmp } else { id = id + "-1" } } if _, found := r.headingIDs[id]; !found { r.headingIDs[id] = 0 } return id } func (r *HTMLRenderer) addAbsPrefix(link []byte) []byte { if r.AbsolutePrefix != "" && isRelativeLink(link) && link[0] != '.' { newDest := r.AbsolutePrefix if link[0] != '/' { newDest += "/" } newDest += string(link) return []byte(newDest) } return link } func appendLinkAttrs(attrs []string, flags HTMLFlags, link []byte) []string { if isRelativeLink(link) { return attrs } val := []string{} if flags&NofollowLinks != 0 { val = append(val, "nofollow") } if flags&NoreferrerLinks != 0 { val = append(val, "noreferrer") } if flags&NoopenerLinks != 0 { val = append(val, "noopener") } if flags&HrefTargetBlank != 0 { attrs = append(attrs, "target=\"_blank\"") } if len(val) == 0 { return attrs } attr := fmt.Sprintf("rel=%q", strings.Join(val, " ")) return append(attrs, attr) } func isMailto(link []byte) bool { return bytes.HasPrefix(link, []byte("mailto:")) } func needSkipLink(flags HTMLFlags, dest []byte) bool { if flags&SkipLinks != 0 { return true } return flags&Safelink != 0 && !isSafeLink(dest) && !isMailto(dest) } func isSmartypantable(node *Node) bool { pt := node.Parent.Type return pt != Link && pt != CodeBlock && pt != Code } func appendLanguageAttr(attrs []string, info []byte) []string { if len(info) == 0 { return attrs } endOfLang := bytes.IndexAny(info, "\t ") if endOfLang < 0 { endOfLang = len(info) } return append(attrs, fmt.Sprintf("class=\"language-%s\"", info[:endOfLang])) } func (r *HTMLRenderer) tag(w io.Writer, name []byte, attrs []string) { w.Write(name) if len(attrs) > 0 { w.Write(spaceBytes) w.Write([]byte(strings.Join(attrs, " "))) } w.Write(gtBytes) r.lastOutputLen = 1 } func footnoteRef(prefix string, node *Node) []byte { urlFrag := prefix + string(slugify(node.Destination)) anchor := fmt.Sprintf(`%d`, urlFrag, node.NoteID) return []byte(fmt.Sprintf(`%s`, urlFrag, anchor)) } func footnoteItem(prefix string, slug []byte) []byte { return []byte(fmt.Sprintf(`
  • `, prefix, slug)) } func footnoteReturnLink(prefix, returnLink string, slug []byte) []byte { const format = ` %s` return []byte(fmt.Sprintf(format, prefix, slug, returnLink)) } func itemOpenCR(node *Node) bool { if node.Prev == nil { return false } ld := node.Parent.ListData return !ld.Tight && ld.ListFlags&ListTypeDefinition == 0 } func skipParagraphTags(node *Node) bool { grandparent := node.Parent.Parent if grandparent == nil || grandparent.Type != List { return false } tightOrTerm := grandparent.Tight || node.Parent.ListFlags&ListTypeTerm != 0 return grandparent.Type == List && tightOrTerm } func cellAlignment(align CellAlignFlags) string { switch align { case TableAlignmentLeft: return "left" case TableAlignmentRight: return "right" case TableAlignmentCenter: return "center" default: return "" } } func (r *HTMLRenderer) out(w io.Writer, text []byte) { if r.disableTags > 0 { w.Write(htmlTagRe.ReplaceAll(text, []byte{})) } else { w.Write(text) } r.lastOutputLen = len(text) } func (r *HTMLRenderer) cr(w io.Writer) { if r.lastOutputLen > 0 { r.out(w, nlBytes) } } var ( nlBytes = []byte{'\n'} gtBytes = []byte{'>'} spaceBytes = []byte{' '} ) var ( brTag = []byte("
    ") brXHTMLTag = []byte("
    ") emTag = []byte("") emCloseTag = []byte("") strongTag = []byte("") strongCloseTag = []byte("") delTag = []byte("") delCloseTag = []byte("") ttTag = []byte("") ttCloseTag = []byte("") aTag = []byte("") preTag = []byte("
    ")
    	preCloseTag        = []byte("
    ") codeTag = []byte("") codeCloseTag = []byte("") pTag = []byte("

    ") pCloseTag = []byte("

    ") blockquoteTag = []byte("
    ") blockquoteCloseTag = []byte("
    ") hrTag = []byte("
    ") hrXHTMLTag = []byte("
    ") ulTag = []byte("
      ") ulCloseTag = []byte("
    ") olTag = []byte("
      ") olCloseTag = []byte("
    ") dlTag = []byte("
    ") dlCloseTag = []byte("
    ") liTag = []byte("
  • ") liCloseTag = []byte("
  • ") ddTag = []byte("
    ") ddCloseTag = []byte("
    ") dtTag = []byte("
    ") dtCloseTag = []byte("
    ") tableTag = []byte("

    ") tableCloseTag = []byte("
    ") tdTag = []byte("") thTag = []byte("") theadTag = []byte("") theadCloseTag = []byte("") tbodyTag = []byte("") tbodyCloseTag = []byte("") trTag = []byte("") trCloseTag = []byte("") h1Tag = []byte("") h2Tag = []byte("") h3Tag = []byte("") h4Tag = []byte("") h5Tag = []byte("") h6Tag = []byte("") footnotesDivBytes = []byte("\n
    \n\n") footnotesCloseDivBytes = []byte("\n
    \n") ) func headingTagsFromLevel(level int) ([]byte, []byte) { if level <= 1 { return h1Tag, h1CloseTag } switch level { case 2: return h2Tag, h2CloseTag case 3: return h3Tag, h3CloseTag case 4: return h4Tag, h4CloseTag case 5: return h5Tag, h5CloseTag } return h6Tag, h6CloseTag } func (r *HTMLRenderer) outHRTag(w io.Writer) { if r.Flags&UseXHTML == 0 { r.out(w, hrTag) } else { r.out(w, hrXHTMLTag) } } // RenderNode is a default renderer of a single node of a syntax tree. For // block nodes it will be called twice: first time with entering=true, second // time with entering=false, so that it could know when it's working on an open // tag and when on close. It writes the result to w. // // The return value is a way to tell the calling walker to adjust its walk // pattern: e.g. it can terminate the traversal by returning Terminate. Or it // can ask the walker to skip a subtree of this node by returning SkipChildren. // The typical behavior is to return GoToNext, which asks for the usual // traversal to the next node. func (r *HTMLRenderer) RenderNode(w io.Writer, node *Node, entering bool) WalkStatus { attrs := []string{} switch node.Type { case Text: if r.Flags&Smartypants != 0 { var tmp bytes.Buffer escapeHTML(&tmp, node.Literal) r.sr.Process(w, tmp.Bytes()) } else { if node.Parent.Type == Link { escLink(w, node.Literal) } else { escapeHTML(w, node.Literal) } } case Softbreak: r.cr(w) // TODO: make it configurable via out(renderer.softbreak) case Hardbreak: if r.Flags&UseXHTML == 0 { r.out(w, brTag) } else { r.out(w, brXHTMLTag) } r.cr(w) case Emph: if entering { r.out(w, emTag) } else { r.out(w, emCloseTag) } case Strong: if entering { r.out(w, strongTag) } else { r.out(w, strongCloseTag) } case Del: if entering { r.out(w, delTag) } else { r.out(w, delCloseTag) } case HTMLSpan: if r.Flags&SkipHTML != 0 { break } r.out(w, node.Literal) case Link: // mark it but don't link it if it is not a safe link: no smartypants dest := node.LinkData.Destination if needSkipLink(r.Flags, dest) { if entering { r.out(w, ttTag) } else { r.out(w, ttCloseTag) } } else { if entering { dest = r.addAbsPrefix(dest) var hrefBuf bytes.Buffer hrefBuf.WriteString("href=\"") escLink(&hrefBuf, dest) hrefBuf.WriteByte('"') attrs = append(attrs, hrefBuf.String()) if node.NoteID != 0 { r.out(w, footnoteRef(r.FootnoteAnchorPrefix, node)) break } attrs = appendLinkAttrs(attrs, r.Flags, dest) if len(node.LinkData.Title) > 0 { var titleBuff bytes.Buffer titleBuff.WriteString("title=\"") escapeHTML(&titleBuff, node.LinkData.Title) titleBuff.WriteByte('"') attrs = append(attrs, titleBuff.String()) } r.tag(w, aTag, attrs) } else { if node.NoteID != 0 { break } r.out(w, aCloseTag) } } case Image: if r.Flags&SkipImages != 0 { return SkipChildren } if entering { dest := node.LinkData.Destination dest = r.addAbsPrefix(dest) if r.disableTags == 0 { //if options.safe && potentiallyUnsafe(dest) { //out(w, ``)
				//} else {
				r.out(w, []byte(`<img src=`)) } } case Code: r.out(w, codeTag) escapeAllHTML(w, node.Literal) r.out(w, codeCloseTag) case Document: break case Paragraph: if skipParagraphTags(node) { break } if entering { // TODO: untangle this clusterfuck about when the newlines need // to be added and when not. if node.Prev != nil { switch node.Prev.Type { case HTMLBlock, List, Paragraph, Heading, CodeBlock, BlockQuote, HorizontalRule: r.cr(w) } } if node.Parent.Type == BlockQuote && node.Prev == nil { r.cr(w) } r.out(w, pTag) } else { r.out(w, pCloseTag) if !(node.Parent.Type == Item && node.Next == nil) { r.cr(w) } } case BlockQuote: if entering { r.cr(w) r.out(w, blockquoteTag) } else { r.out(w, blockquoteCloseTag) r.cr(w) } case HTMLBlock: if r.Flags&SkipHTML != 0 { break } r.cr(w) r.out(w, node.Literal) r.cr(w) case Heading: headingLevel := r.HTMLRendererParameters.HeadingLevelOffset + node.Level openTag, closeTag := headingTagsFromLevel(headingLevel) if entering { if node.IsTitleblock { attrs = append(attrs, `class="title"`) } if node.HeadingID != "" { id := r.ensureUniqueHeadingID(node.HeadingID) if r.HeadingIDPrefix != "" { id = r.HeadingIDPrefix + id } if r.HeadingIDSuffix != "" { id = id + r.HeadingIDSuffix } attrs = append(attrs, fmt.Sprintf(`id="%s"`, id)) } r.cr(w) r.tag(w, openTag, attrs) } else { r.out(w, closeTag) if !(node.Parent.Type == Item && node.Next == nil) { r.cr(w) } } case HorizontalRule: r.cr(w) r.outHRTag(w) r.cr(w) case List: openTag := ulTag closeTag := ulCloseTag if node.ListFlags&ListTypeOrdered != 0 { openTag = olTag closeTag = olCloseTag } if node.ListFlags&ListTypeDefinition != 0 { openTag = dlTag closeTag = dlCloseTag } if entering { if node.IsFootnotesList { r.out(w, footnotesDivBytes) r.outHRTag(w) r.cr(w) } r.cr(w) if node.Parent.Type == Item && node.Parent.Parent.Tight { r.cr(w) } r.tag(w, openTag[:len(openTag)-1], attrs) r.cr(w) } else { r.out(w, closeTag) //cr(w) //if node.parent.Type != Item { // cr(w) //} if node.Parent.Type == Item && node.Next != nil { r.cr(w) } if node.Parent.Type == Document || node.Parent.Type == BlockQuote { r.cr(w) } if node.IsFootnotesList { r.out(w, footnotesCloseDivBytes) } } case Item: openTag := liTag closeTag := liCloseTag if node.ListFlags&ListTypeDefinition != 0 { openTag = ddTag closeTag = ddCloseTag } if node.ListFlags&ListTypeTerm != 0 { openTag = dtTag closeTag = dtCloseTag } if entering { if itemOpenCR(node) { r.cr(w) } if node.ListData.RefLink != nil { slug := slugify(node.ListData.RefLink) r.out(w, footnoteItem(r.FootnoteAnchorPrefix, slug)) break } r.out(w, openTag) } else { if node.ListData.RefLink != nil { slug := slugify(node.ListData.RefLink) if r.Flags&FootnoteReturnLinks != 0 { r.out(w, footnoteReturnLink(r.FootnoteAnchorPrefix, r.FootnoteReturnLinkContents, slug)) } } r.out(w, closeTag) r.cr(w) } case CodeBlock: attrs = appendLanguageAttr(attrs, node.Info) r.cr(w) r.out(w, preTag) r.tag(w, codeTag[:len(codeTag)-1], attrs) escapeAllHTML(w, node.Literal) r.out(w, codeCloseTag) r.out(w, preCloseTag) if node.Parent.Type != Item { r.cr(w) } case Table: if entering { r.cr(w) r.out(w, tableTag) } else { r.out(w, tableCloseTag) r.cr(w) } case TableCell: openTag := tdTag closeTag := tdCloseTag if node.IsHeader { openTag = thTag closeTag = thCloseTag } if entering { align := cellAlignment(node.Align) if align != "" { attrs = append(attrs, fmt.Sprintf(`align="%s"`, align)) } if node.Prev == nil { r.cr(w) } r.tag(w, openTag, attrs) } else { r.out(w, closeTag) r.cr(w) } case TableHead: if entering { r.cr(w) r.out(w, theadTag) } else { r.out(w, theadCloseTag) r.cr(w) } case TableBody: if entering { r.cr(w) r.out(w, tbodyTag) // XXX: this is to adhere to a rather silly test. Should fix test. if node.FirstChild == nil { r.cr(w) } } else { r.out(w, tbodyCloseTag) r.cr(w) } case TableRow: if entering { r.cr(w) r.out(w, trTag) } else { r.out(w, trCloseTag) r.cr(w) } default: panic("Unknown node type " + node.Type.String()) } return GoToNext } // RenderHeader writes HTML document preamble and TOC if requested. func (r *HTMLRenderer) RenderHeader(w io.Writer, ast *Node) { r.writeDocumentHeader(w) if r.Flags&TOC != 0 { r.writeTOC(w, ast) } } // RenderFooter writes HTML document footer. func (r *HTMLRenderer) RenderFooter(w io.Writer, ast *Node) { if r.Flags&CompletePage == 0 { return } io.WriteString(w, "\n\n\n") } func (r *HTMLRenderer) writeDocumentHeader(w io.Writer) { if r.Flags&CompletePage == 0 { return } ending := "" if r.Flags&UseXHTML != 0 { io.WriteString(w, "\n") io.WriteString(w, "\n") ending = " /" } else { io.WriteString(w, "\n") io.WriteString(w, "\n") } io.WriteString(w, "\n") io.WriteString(w, " ") if r.Flags&Smartypants != 0 { r.sr.Process(w, []byte(r.Title)) } else { escapeHTML(w, []byte(r.Title)) } io.WriteString(w, "\n") io.WriteString(w, " \n") io.WriteString(w, " \n") if r.CSS != "" { io.WriteString(w, " \n") } if r.Icon != "" { io.WriteString(w, " \n") } io.WriteString(w, "\n") io.WriteString(w, "\n\n") } func (r *HTMLRenderer) writeTOC(w io.Writer, ast *Node) { buf := bytes.Buffer{} inHeading := false tocLevel := 0 headingCount := 0 ast.Walk(func(node *Node, entering bool) WalkStatus { if node.Type == Heading && !node.HeadingData.IsTitleblock { inHeading = entering if entering { node.HeadingID = fmt.Sprintf("toc_%d", headingCount) if node.Level == tocLevel { buf.WriteString("\n\n
  • ") } else if node.Level < tocLevel { for node.Level < tocLevel { tocLevel-- buf.WriteString("
  • \n") } buf.WriteString("\n\n
  • ") } else { for node.Level > tocLevel { tocLevel++ buf.WriteString("\n") } if buf.Len() > 0 { io.WriteString(w, "\n") } r.lastOutputLen = buf.Len() } ================================================ FILE: vendor/github.com/russross/blackfriday/v2/inline.go ================================================ // // Blackfriday Markdown Processor // Available at http://github.com/russross/blackfriday // // Copyright © 2011 Russ Ross . // Distributed under the Simplified BSD License. // See README.md for details. // // // Functions to parse inline elements. // package blackfriday import ( "bytes" "regexp" "strconv" ) var ( urlRe = `((https?|ftp):\/\/|\/)[-A-Za-z0-9+&@#\/%?=~_|!:,.;\(\)]+` anchorRe = regexp.MustCompile(`^(]+")?\s?>` + urlRe + `<\/a>)`) // https://www.w3.org/TR/html5/syntax.html#character-references // highest unicode code point in 17 planes (2^20): 1,114,112d = // 7 dec digits or 6 hex digits // named entity references can be 2-31 characters with stuff like < // at one end and ∳ at the other. There // are also sometimes numbers at the end, although this isn't inherent // in the specification; there are never numbers anywhere else in // current character references, though; see ¾ and ▒, etc. // https://www.w3.org/TR/html5/syntax.html#named-character-references // // entity := "&" (named group | number ref) ";" // named group := [a-zA-Z]{2,31}[0-9]{0,2} // number ref := "#" (dec ref | hex ref) // dec ref := [0-9]{1,7} // hex ref := ("x" | "X") [0-9a-fA-F]{1,6} htmlEntityRe = regexp.MustCompile(`&([a-zA-Z]{2,31}[0-9]{0,2}|#([0-9]{1,7}|[xX][0-9a-fA-F]{1,6}));`) ) // Functions to parse text within a block // Each function returns the number of chars taken care of // data is the complete block being rendered // offset is the number of valid chars before the current cursor func (p *Markdown) inline(currBlock *Node, data []byte) { // handlers might call us recursively: enforce a maximum depth if p.nesting >= p.maxNesting || len(data) == 0 { return } p.nesting++ beg, end := 0, 0 for end < len(data) { handler := p.inlineCallback[data[end]] if handler != nil { if consumed, node := handler(p, data, end); consumed == 0 { // No action from the callback. end++ } else { // Copy inactive chars into the output. currBlock.AppendChild(text(data[beg:end])) if node != nil { currBlock.AppendChild(node) } // Skip past whatever the callback used. beg = end + consumed end = beg } } else { end++ } } if beg < len(data) { if data[end-1] == '\n' { end-- } currBlock.AppendChild(text(data[beg:end])) } p.nesting-- } // single and double emphasis parsing func emphasis(p *Markdown, data []byte, offset int) (int, *Node) { data = data[offset:] c := data[0] if len(data) > 2 && data[1] != c { // whitespace cannot follow an opening emphasis; // strikethrough only takes two characters '~~' if c == '~' || isspace(data[1]) { return 0, nil } ret, node := helperEmphasis(p, data[1:], c) if ret == 0 { return 0, nil } return ret + 1, node } if len(data) > 3 && data[1] == c && data[2] != c { if isspace(data[2]) { return 0, nil } ret, node := helperDoubleEmphasis(p, data[2:], c) if ret == 0 { return 0, nil } return ret + 2, node } if len(data) > 4 && data[1] == c && data[2] == c && data[3] != c { if c == '~' || isspace(data[3]) { return 0, nil } ret, node := helperTripleEmphasis(p, data, 3, c) if ret == 0 { return 0, nil } return ret + 3, node } return 0, nil } func codeSpan(p *Markdown, data []byte, offset int) (int, *Node) { data = data[offset:] nb := 0 // count the number of backticks in the delimiter for nb < len(data) && data[nb] == '`' { nb++ } // find the next delimiter i, end := 0, 0 for end = nb; end < len(data) && i < nb; end++ { if data[end] == '`' { i++ } else { i = 0 } } // no matching delimiter? if i < nb && end >= len(data) { return 0, nil } // trim outside whitespace fBegin := nb for fBegin < end && data[fBegin] == ' ' { fBegin++ } fEnd := end - nb for fEnd > fBegin && data[fEnd-1] == ' ' { fEnd-- } // render the code span if fBegin != fEnd { code := NewNode(Code) code.Literal = data[fBegin:fEnd] return end, code } return end, nil } // newline preceded by two spaces becomes
    func maybeLineBreak(p *Markdown, data []byte, offset int) (int, *Node) { origOffset := offset for offset < len(data) && data[offset] == ' ' { offset++ } if offset < len(data) && data[offset] == '\n' { if offset-origOffset >= 2 { return offset - origOffset + 1, NewNode(Hardbreak) } return offset - origOffset, nil } return 0, nil } // newline without two spaces works when HardLineBreak is enabled func lineBreak(p *Markdown, data []byte, offset int) (int, *Node) { if p.extensions&HardLineBreak != 0 { return 1, NewNode(Hardbreak) } return 0, nil } type linkType int const ( linkNormal linkType = iota linkImg linkDeferredFootnote linkInlineFootnote ) func isReferenceStyleLink(data []byte, pos int, t linkType) bool { if t == linkDeferredFootnote { return false } return pos < len(data)-1 && data[pos] == '[' && data[pos+1] != '^' } func maybeImage(p *Markdown, data []byte, offset int) (int, *Node) { if offset < len(data)-1 && data[offset+1] == '[' { return link(p, data, offset) } return 0, nil } func maybeInlineFootnote(p *Markdown, data []byte, offset int) (int, *Node) { if offset < len(data)-1 && data[offset+1] == '[' { return link(p, data, offset) } return 0, nil } // '[': parse a link or an image or a footnote func link(p *Markdown, data []byte, offset int) (int, *Node) { // no links allowed inside regular links, footnote, and deferred footnotes if p.insideLink && (offset > 0 && data[offset-1] == '[' || len(data)-1 > offset && data[offset+1] == '^') { return 0, nil } var t linkType switch { // special case: ![^text] == deferred footnote (that follows something with // an exclamation point) case p.extensions&Footnotes != 0 && len(data)-1 > offset && data[offset+1] == '^': t = linkDeferredFootnote // ![alt] == image case offset >= 0 && data[offset] == '!': t = linkImg offset++ // ^[text] == inline footnote // [^refId] == deferred footnote case p.extensions&Footnotes != 0: if offset >= 0 && data[offset] == '^' { t = linkInlineFootnote offset++ } else if len(data)-1 > offset && data[offset+1] == '^' { t = linkDeferredFootnote } // [text] == regular link default: t = linkNormal } data = data[offset:] var ( i = 1 noteID int title, link, altContent []byte textHasNl = false ) if t == linkDeferredFootnote { i++ } // look for the matching closing bracket for level := 1; level > 0 && i < len(data); i++ { switch { case data[i] == '\n': textHasNl = true case isBackslashEscaped(data, i): continue case data[i] == '[': level++ case data[i] == ']': level-- if level <= 0 { i-- // compensate for extra i++ in for loop } } } if i >= len(data) { return 0, nil } txtE := i i++ var footnoteNode *Node // skip any amount of whitespace or newline // (this is much more lax than original markdown syntax) for i < len(data) && isspace(data[i]) { i++ } // inline style link switch { case i < len(data) && data[i] == '(': // skip initial whitespace i++ for i < len(data) && isspace(data[i]) { i++ } linkB := i // look for link end: ' " ) findlinkend: for i < len(data) { switch { case data[i] == '\\': i += 2 case data[i] == ')' || data[i] == '\'' || data[i] == '"': break findlinkend default: i++ } } if i >= len(data) { return 0, nil } linkE := i // look for title end if present titleB, titleE := 0, 0 if data[i] == '\'' || data[i] == '"' { i++ titleB = i findtitleend: for i < len(data) { switch { case data[i] == '\\': i += 2 case data[i] == ')': break findtitleend default: i++ } } if i >= len(data) { return 0, nil } // skip whitespace after title titleE = i - 1 for titleE > titleB && isspace(data[titleE]) { titleE-- } // check for closing quote presence if data[titleE] != '\'' && data[titleE] != '"' { titleB, titleE = 0, 0 linkE = i } } // remove whitespace at the end of the link for linkE > linkB && isspace(data[linkE-1]) { linkE-- } // remove optional angle brackets around the link if data[linkB] == '<' { linkB++ } if data[linkE-1] == '>' { linkE-- } // build escaped link and title if linkE > linkB { link = data[linkB:linkE] } if titleE > titleB { title = data[titleB:titleE] } i++ // reference style link case isReferenceStyleLink(data, i, t): var id []byte altContentConsidered := false // look for the id i++ linkB := i for i < len(data) && data[i] != ']' { i++ } if i >= len(data) { return 0, nil } linkE := i // find the reference if linkB == linkE { if textHasNl { var b bytes.Buffer for j := 1; j < txtE; j++ { switch { case data[j] != '\n': b.WriteByte(data[j]) case data[j-1] != ' ': b.WriteByte(' ') } } id = b.Bytes() } else { id = data[1:txtE] altContentConsidered = true } } else { id = data[linkB:linkE] } // find the reference with matching id lr, ok := p.getRef(string(id)) if !ok { return 0, nil } // keep link and title from reference link = lr.link title = lr.title if altContentConsidered { altContent = lr.text } i++ // shortcut reference style link or reference or inline footnote default: var id []byte // craft the id if textHasNl { var b bytes.Buffer for j := 1; j < txtE; j++ { switch { case data[j] != '\n': b.WriteByte(data[j]) case data[j-1] != ' ': b.WriteByte(' ') } } id = b.Bytes() } else { if t == linkDeferredFootnote { id = data[2:txtE] // get rid of the ^ } else { id = data[1:txtE] } } footnoteNode = NewNode(Item) if t == linkInlineFootnote { // create a new reference noteID = len(p.notes) + 1 var fragment []byte if len(id) > 0 { if len(id) < 16 { fragment = make([]byte, len(id)) } else { fragment = make([]byte, 16) } copy(fragment, slugify(id)) } else { fragment = append([]byte("footnote-"), []byte(strconv.Itoa(noteID))...) } ref := &reference{ noteID: noteID, hasBlock: false, link: fragment, title: id, footnote: footnoteNode, } p.notes = append(p.notes, ref) link = ref.link title = ref.title } else { // find the reference with matching id lr, ok := p.getRef(string(id)) if !ok { return 0, nil } if t == linkDeferredFootnote { lr.noteID = len(p.notes) + 1 lr.footnote = footnoteNode p.notes = append(p.notes, lr) } // keep link and title from reference link = lr.link // if inline footnote, title == footnote contents title = lr.title noteID = lr.noteID } // rewind the whitespace i = txtE + 1 } var uLink []byte if t == linkNormal || t == linkImg { if len(link) > 0 { var uLinkBuf bytes.Buffer unescapeText(&uLinkBuf, link) uLink = uLinkBuf.Bytes() } // links need something to click on and somewhere to go if len(uLink) == 0 || (t == linkNormal && txtE <= 1) { return 0, nil } } // call the relevant rendering function var linkNode *Node switch t { case linkNormal: linkNode = NewNode(Link) linkNode.Destination = normalizeURI(uLink) linkNode.Title = title if len(altContent) > 0 { linkNode.AppendChild(text(altContent)) } else { // links cannot contain other links, so turn off link parsing // temporarily and recurse insideLink := p.insideLink p.insideLink = true p.inline(linkNode, data[1:txtE]) p.insideLink = insideLink } case linkImg: linkNode = NewNode(Image) linkNode.Destination = uLink linkNode.Title = title linkNode.AppendChild(text(data[1:txtE])) i++ case linkInlineFootnote, linkDeferredFootnote: linkNode = NewNode(Link) linkNode.Destination = link linkNode.Title = title linkNode.NoteID = noteID linkNode.Footnote = footnoteNode if t == linkInlineFootnote { i++ } default: return 0, nil } return i, linkNode } func (p *Markdown) inlineHTMLComment(data []byte) int { if len(data) < 5 { return 0 } if data[0] != '<' || data[1] != '!' || data[2] != '-' || data[3] != '-' { return 0 } i := 5 // scan for an end-of-comment marker, across lines if necessary for i < len(data) && !(data[i-2] == '-' && data[i-1] == '-' && data[i] == '>') { i++ } // no end-of-comment marker if i >= len(data) { return 0 } return i + 1 } func stripMailto(link []byte) []byte { if bytes.HasPrefix(link, []byte("mailto://")) { return link[9:] } else if bytes.HasPrefix(link, []byte("mailto:")) { return link[7:] } else { return link } } // autolinkType specifies a kind of autolink that gets detected. type autolinkType int // These are the possible flag values for the autolink renderer. const ( notAutolink autolinkType = iota normalAutolink emailAutolink ) // '<' when tags or autolinks are allowed func leftAngle(p *Markdown, data []byte, offset int) (int, *Node) { data = data[offset:] altype, end := tagLength(data) if size := p.inlineHTMLComment(data); size > 0 { end = size } if end > 2 { if altype != notAutolink { var uLink bytes.Buffer unescapeText(&uLink, data[1:end+1-2]) if uLink.Len() > 0 { link := uLink.Bytes() node := NewNode(Link) node.Destination = link if altype == emailAutolink { node.Destination = append([]byte("mailto:"), link...) } node.AppendChild(text(stripMailto(link))) return end, node } } else { htmlTag := NewNode(HTMLSpan) htmlTag.Literal = data[:end] return end, htmlTag } } return end, nil } // '\\' backslash escape var escapeChars = []byte("\\`*_{}[]()#+-.!:|&<>~") func escape(p *Markdown, data []byte, offset int) (int, *Node) { data = data[offset:] if len(data) > 1 { if p.extensions&BackslashLineBreak != 0 && data[1] == '\n' { return 2, NewNode(Hardbreak) } if bytes.IndexByte(escapeChars, data[1]) < 0 { return 0, nil } return 2, text(data[1:2]) } return 2, nil } func unescapeText(ob *bytes.Buffer, src []byte) { i := 0 for i < len(src) { org := i for i < len(src) && src[i] != '\\' { i++ } if i > org { ob.Write(src[org:i]) } if i+1 >= len(src) { break } ob.WriteByte(src[i+1]) i += 2 } } // '&' escaped when it doesn't belong to an entity // valid entities are assumed to be anything matching &#?[A-Za-z0-9]+; func entity(p *Markdown, data []byte, offset int) (int, *Node) { data = data[offset:] end := 1 if end < len(data) && data[end] == '#' { end++ } for end < len(data) && isalnum(data[end]) { end++ } if end < len(data) && data[end] == ';' { end++ // real entity } else { return 0, nil // lone '&' } ent := data[:end] // undo & escaping or it will be converted to &amp; by another // escaper in the renderer if bytes.Equal(ent, []byte("&")) { ent = []byte{'&'} } return end, text(ent) } func linkEndsWithEntity(data []byte, linkEnd int) bool { entityRanges := htmlEntityRe.FindAllIndex(data[:linkEnd], -1) return entityRanges != nil && entityRanges[len(entityRanges)-1][1] == linkEnd } // hasPrefixCaseInsensitive is a custom implementation of // strings.HasPrefix(strings.ToLower(s), prefix) // we rolled our own because ToLower pulls in a huge machinery of lowercasing // anything from Unicode and that's very slow. Since this func will only be // used on ASCII protocol prefixes, we can take shortcuts. func hasPrefixCaseInsensitive(s, prefix []byte) bool { if len(s) < len(prefix) { return false } delta := byte('a' - 'A') for i, b := range prefix { if b != s[i] && b != s[i]+delta { return false } } return true } var protocolPrefixes = [][]byte{ []byte("http://"), []byte("https://"), []byte("ftp://"), []byte("file://"), []byte("mailto:"), } const shortestPrefix = 6 // len("ftp://"), the shortest of the above func maybeAutoLink(p *Markdown, data []byte, offset int) (int, *Node) { // quick check to rule out most false hits if p.insideLink || len(data) < offset+shortestPrefix { return 0, nil } for _, prefix := range protocolPrefixes { endOfHead := offset + 8 // 8 is the len() of the longest prefix if endOfHead > len(data) { endOfHead = len(data) } if hasPrefixCaseInsensitive(data[offset:endOfHead], prefix) { return autoLink(p, data, offset) } } return 0, nil } func autoLink(p *Markdown, data []byte, offset int) (int, *Node) { // Now a more expensive check to see if we're not inside an anchor element anchorStart := offset offsetFromAnchor := 0 for anchorStart > 0 && data[anchorStart] != '<' { anchorStart-- offsetFromAnchor++ } anchorStr := anchorRe.Find(data[anchorStart:]) if anchorStr != nil { anchorClose := NewNode(HTMLSpan) anchorClose.Literal = anchorStr[offsetFromAnchor:] return len(anchorStr) - offsetFromAnchor, anchorClose } // scan backward for a word boundary rewind := 0 for offset-rewind > 0 && rewind <= 7 && isletter(data[offset-rewind-1]) { rewind++ } if rewind > 6 { // longest supported protocol is "mailto" which has 6 letters return 0, nil } origData := data data = data[offset-rewind:] if !isSafeLink(data) { return 0, nil } linkEnd := 0 for linkEnd < len(data) && !isEndOfLink(data[linkEnd]) { linkEnd++ } // Skip punctuation at the end of the link if (data[linkEnd-1] == '.' || data[linkEnd-1] == ',') && data[linkEnd-2] != '\\' { linkEnd-- } // But don't skip semicolon if it's a part of escaped entity: if data[linkEnd-1] == ';' && data[linkEnd-2] != '\\' && !linkEndsWithEntity(data, linkEnd) { linkEnd-- } // See if the link finishes with a punctuation sign that can be closed. var copen byte switch data[linkEnd-1] { case '"': copen = '"' case '\'': copen = '\'' case ')': copen = '(' case ']': copen = '[' case '}': copen = '{' default: copen = 0 } if copen != 0 { bufEnd := offset - rewind + linkEnd - 2 openDelim := 1 /* Try to close the final punctuation sign in this same line; * if we managed to close it outside of the URL, that means that it's * not part of the URL. If it closes inside the URL, that means it * is part of the URL. * * Examples: * * foo http://www.pokemon.com/Pikachu_(Electric) bar * => http://www.pokemon.com/Pikachu_(Electric) * * foo (http://www.pokemon.com/Pikachu_(Electric)) bar * => http://www.pokemon.com/Pikachu_(Electric) * * foo http://www.pokemon.com/Pikachu_(Electric)) bar * => http://www.pokemon.com/Pikachu_(Electric)) * * (foo http://www.pokemon.com/Pikachu_(Electric)) bar * => foo http://www.pokemon.com/Pikachu_(Electric) */ for bufEnd >= 0 && origData[bufEnd] != '\n' && openDelim != 0 { if origData[bufEnd] == data[linkEnd-1] { openDelim++ } if origData[bufEnd] == copen { openDelim-- } bufEnd-- } if openDelim == 0 { linkEnd-- } } var uLink bytes.Buffer unescapeText(&uLink, data[:linkEnd]) if uLink.Len() > 0 { node := NewNode(Link) node.Destination = uLink.Bytes() node.AppendChild(text(uLink.Bytes())) return linkEnd, node } return linkEnd, nil } func isEndOfLink(char byte) bool { return isspace(char) || char == '<' } var validUris = [][]byte{[]byte("http://"), []byte("https://"), []byte("ftp://"), []byte("mailto://")} var validPaths = [][]byte{[]byte("/"), []byte("./"), []byte("../")} func isSafeLink(link []byte) bool { for _, path := range validPaths { if len(link) >= len(path) && bytes.Equal(link[:len(path)], path) { if len(link) == len(path) { return true } else if isalnum(link[len(path)]) { return true } } } for _, prefix := range validUris { // TODO: handle unicode here // case-insensitive prefix test if len(link) > len(prefix) && bytes.Equal(bytes.ToLower(link[:len(prefix)]), prefix) && isalnum(link[len(prefix)]) { return true } } return false } // return the length of the given tag, or 0 is it's not valid func tagLength(data []byte) (autolink autolinkType, end int) { var i, j int // a valid tag can't be shorter than 3 chars if len(data) < 3 { return notAutolink, 0 } // begins with a '<' optionally followed by '/', followed by letter or number if data[0] != '<' { return notAutolink, 0 } if data[1] == '/' { i = 2 } else { i = 1 } if !isalnum(data[i]) { return notAutolink, 0 } // scheme test autolink = notAutolink // try to find the beginning of an URI for i < len(data) && (isalnum(data[i]) || data[i] == '.' || data[i] == '+' || data[i] == '-') { i++ } if i > 1 && i < len(data) && data[i] == '@' { if j = isMailtoAutoLink(data[i:]); j != 0 { return emailAutolink, i + j } } if i > 2 && i < len(data) && data[i] == ':' { autolink = normalAutolink i++ } // complete autolink test: no whitespace or ' or " switch { case i >= len(data): autolink = notAutolink case autolink != notAutolink: j = i for i < len(data) { if data[i] == '\\' { i += 2 } else if data[i] == '>' || data[i] == '\'' || data[i] == '"' || isspace(data[i]) { break } else { i++ } } if i >= len(data) { return autolink, 0 } if i > j && data[i] == '>' { return autolink, i + 1 } // one of the forbidden chars has been found autolink = notAutolink } i += bytes.IndexByte(data[i:], '>') if i < 0 { return autolink, 0 } return autolink, i + 1 } // look for the address part of a mail autolink and '>' // this is less strict than the original markdown e-mail address matching func isMailtoAutoLink(data []byte) int { nb := 0 // address is assumed to be: [-@._a-zA-Z0-9]+ with exactly one '@' for i := 0; i < len(data); i++ { if isalnum(data[i]) { continue } switch data[i] { case '@': nb++ case '-', '.', '_': break case '>': if nb == 1 { return i + 1 } return 0 default: return 0 } } return 0 } // look for the next emph char, skipping other constructs func helperFindEmphChar(data []byte, c byte) int { i := 0 for i < len(data) { for i < len(data) && data[i] != c && data[i] != '`' && data[i] != '[' { i++ } if i >= len(data) { return 0 } // do not count escaped chars if i != 0 && data[i-1] == '\\' { i++ continue } if data[i] == c { return i } if data[i] == '`' { // skip a code span tmpI := 0 i++ for i < len(data) && data[i] != '`' { if tmpI == 0 && data[i] == c { tmpI = i } i++ } if i >= len(data) { return tmpI } i++ } else if data[i] == '[' { // skip a link tmpI := 0 i++ for i < len(data) && data[i] != ']' { if tmpI == 0 && data[i] == c { tmpI = i } i++ } i++ for i < len(data) && (data[i] == ' ' || data[i] == '\n') { i++ } if i >= len(data) { return tmpI } if data[i] != '[' && data[i] != '(' { // not a link if tmpI > 0 { return tmpI } continue } cc := data[i] i++ for i < len(data) && data[i] != cc { if tmpI == 0 && data[i] == c { return i } i++ } if i >= len(data) { return tmpI } i++ } } return 0 } func helperEmphasis(p *Markdown, data []byte, c byte) (int, *Node) { i := 0 // skip one symbol if coming from emph3 if len(data) > 1 && data[0] == c && data[1] == c { i = 1 } for i < len(data) { length := helperFindEmphChar(data[i:], c) if length == 0 { return 0, nil } i += length if i >= len(data) { return 0, nil } if i+1 < len(data) && data[i+1] == c { i++ continue } if data[i] == c && !isspace(data[i-1]) { if p.extensions&NoIntraEmphasis != 0 { if !(i+1 == len(data) || isspace(data[i+1]) || ispunct(data[i+1])) { continue } } emph := NewNode(Emph) p.inline(emph, data[:i]) return i + 1, emph } } return 0, nil } func helperDoubleEmphasis(p *Markdown, data []byte, c byte) (int, *Node) { i := 0 for i < len(data) { length := helperFindEmphChar(data[i:], c) if length == 0 { return 0, nil } i += length if i+1 < len(data) && data[i] == c && data[i+1] == c && i > 0 && !isspace(data[i-1]) { nodeType := Strong if c == '~' { nodeType = Del } node := NewNode(nodeType) p.inline(node, data[:i]) return i + 2, node } i++ } return 0, nil } func helperTripleEmphasis(p *Markdown, data []byte, offset int, c byte) (int, *Node) { i := 0 origData := data data = data[offset:] for i < len(data) { length := helperFindEmphChar(data[i:], c) if length == 0 { return 0, nil } i += length // skip whitespace preceded symbols if data[i] != c || isspace(data[i-1]) { continue } switch { case i+2 < len(data) && data[i+1] == c && data[i+2] == c: // triple symbol found strong := NewNode(Strong) em := NewNode(Emph) strong.AppendChild(em) p.inline(em, data[:i]) return i + 3, strong case (i+1 < len(data) && data[i+1] == c): // double symbol found, hand over to emph1 length, node := helperEmphasis(p, origData[offset-2:], c) if length == 0 { return 0, nil } return length - 2, node default: // single symbol found, hand over to emph2 length, node := helperDoubleEmphasis(p, origData[offset-1:], c) if length == 0 { return 0, nil } return length - 1, node } } return 0, nil } func text(s []byte) *Node { node := NewNode(Text) node.Literal = s return node } func normalizeURI(s []byte) []byte { return s // TODO: implement } ================================================ FILE: vendor/github.com/russross/blackfriday/v2/markdown.go ================================================ // Blackfriday Markdown Processor // Available at http://github.com/russross/blackfriday // // Copyright © 2011 Russ Ross . // Distributed under the Simplified BSD License. // See README.md for details. package blackfriday import ( "bytes" "fmt" "io" "strings" "unicode/utf8" ) // // Markdown parsing and processing // // Version string of the package. Appears in the rendered document when // CompletePage flag is on. const Version = "2.0" // Extensions is a bitwise or'ed collection of enabled Blackfriday's // extensions. type Extensions int // These are the supported markdown parsing extensions. // OR these values together to select multiple extensions. const ( NoExtensions Extensions = 0 NoIntraEmphasis Extensions = 1 << iota // Ignore emphasis markers inside words Tables // Render tables FencedCode // Render fenced code blocks Autolink // Detect embedded URLs that are not explicitly marked Strikethrough // Strikethrough text using ~~test~~ LaxHTMLBlocks // Loosen up HTML block parsing rules SpaceHeadings // Be strict about prefix heading rules HardLineBreak // Translate newlines into line breaks TabSizeEight // Expand tabs to eight spaces instead of four Footnotes // Pandoc-style footnotes NoEmptyLineBeforeBlock // No need to insert an empty line to start a (code, quote, ordered list, unordered list) block HeadingIDs // specify heading IDs with {#id} Titleblock // Titleblock ala pandoc AutoHeadingIDs // Create the heading ID from the text BackslashLineBreak // Translate trailing backslashes into line breaks DefinitionLists // Render definition lists CommonHTMLFlags HTMLFlags = UseXHTML | Smartypants | SmartypantsFractions | SmartypantsDashes | SmartypantsLatexDashes CommonExtensions Extensions = NoIntraEmphasis | Tables | FencedCode | Autolink | Strikethrough | SpaceHeadings | HeadingIDs | BackslashLineBreak | DefinitionLists ) // ListType contains bitwise or'ed flags for list and list item objects. type ListType int // These are the possible flag values for the ListItem renderer. // Multiple flag values may be ORed together. // These are mostly of interest if you are writing a new output format. const ( ListTypeOrdered ListType = 1 << iota ListTypeDefinition ListTypeTerm ListItemContainsBlock ListItemBeginningOfList // TODO: figure out if this is of any use now ListItemEndOfList ) // CellAlignFlags holds a type of alignment in a table cell. type CellAlignFlags int // These are the possible flag values for the table cell renderer. // Only a single one of these values will be used; they are not ORed together. // These are mostly of interest if you are writing a new output format. const ( TableAlignmentLeft CellAlignFlags = 1 << iota TableAlignmentRight TableAlignmentCenter = (TableAlignmentLeft | TableAlignmentRight) ) // The size of a tab stop. const ( TabSizeDefault = 4 TabSizeDouble = 8 ) // blockTags is a set of tags that are recognized as HTML block tags. // Any of these can be included in markdown text without special escaping. var blockTags = map[string]struct{}{ "blockquote": {}, "del": {}, "div": {}, "dl": {}, "fieldset": {}, "form": {}, "h1": {}, "h2": {}, "h3": {}, "h4": {}, "h5": {}, "h6": {}, "iframe": {}, "ins": {}, "math": {}, "noscript": {}, "ol": {}, "pre": {}, "p": {}, "script": {}, "style": {}, "table": {}, "ul": {}, // HTML5 "address": {}, "article": {}, "aside": {}, "canvas": {}, "figcaption": {}, "figure": {}, "footer": {}, "header": {}, "hgroup": {}, "main": {}, "nav": {}, "output": {}, "progress": {}, "section": {}, "video": {}, } // Renderer is the rendering interface. This is mostly of interest if you are // implementing a new rendering format. // // Only an HTML implementation is provided in this repository, see the README // for external implementations. type Renderer interface { // RenderNode is the main rendering method. It will be called once for // every leaf node and twice for every non-leaf node (first with // entering=true, then with entering=false). The method should write its // rendition of the node to the supplied writer w. RenderNode(w io.Writer, node *Node, entering bool) WalkStatus // RenderHeader is a method that allows the renderer to produce some // content preceding the main body of the output document. The header is // understood in the broad sense here. For example, the default HTML // renderer will write not only the HTML document preamble, but also the // table of contents if it was requested. // // The method will be passed an entire document tree, in case a particular // implementation needs to inspect it to produce output. // // The output should be written to the supplied writer w. If your // implementation has no header to write, supply an empty implementation. RenderHeader(w io.Writer, ast *Node) // RenderFooter is a symmetric counterpart of RenderHeader. RenderFooter(w io.Writer, ast *Node) } // Callback functions for inline parsing. One such function is defined // for each character that triggers a response when parsing inline data. type inlineParser func(p *Markdown, data []byte, offset int) (int, *Node) // Markdown is a type that holds extensions and the runtime state used by // Parse, and the renderer. You can not use it directly, construct it with New. type Markdown struct { renderer Renderer referenceOverride ReferenceOverrideFunc refs map[string]*reference inlineCallback [256]inlineParser extensions Extensions nesting int maxNesting int insideLink bool // Footnotes need to be ordered as well as available to quickly check for // presence. If a ref is also a footnote, it's stored both in refs and here // in notes. Slice is nil if footnotes not enabled. notes []*reference doc *Node tip *Node // = doc oldTip *Node lastMatchedContainer *Node // = doc allClosed bool } func (p *Markdown) getRef(refid string) (ref *reference, found bool) { if p.referenceOverride != nil { r, overridden := p.referenceOverride(refid) if overridden { if r == nil { return nil, false } return &reference{ link: []byte(r.Link), title: []byte(r.Title), noteID: 0, hasBlock: false, text: []byte(r.Text)}, true } } // refs are case insensitive ref, found = p.refs[strings.ToLower(refid)] return ref, found } func (p *Markdown) finalize(block *Node) { above := block.Parent block.open = false p.tip = above } func (p *Markdown) addChild(node NodeType, offset uint32) *Node { return p.addExistingChild(NewNode(node), offset) } func (p *Markdown) addExistingChild(node *Node, offset uint32) *Node { for !p.tip.canContain(node.Type) { p.finalize(p.tip) } p.tip.AppendChild(node) p.tip = node return node } func (p *Markdown) closeUnmatchedBlocks() { if !p.allClosed { for p.oldTip != p.lastMatchedContainer { parent := p.oldTip.Parent p.finalize(p.oldTip) p.oldTip = parent } p.allClosed = true } } // // // Public interface // // // Reference represents the details of a link. // See the documentation in Options for more details on use-case. type Reference struct { // Link is usually the URL the reference points to. Link string // Title is the alternate text describing the link in more detail. Title string // Text is the optional text to override the ref with if the syntax used was // [refid][] Text string } // ReferenceOverrideFunc is expected to be called with a reference string and // return either a valid Reference type that the reference string maps to or // nil. If overridden is false, the default reference logic will be executed. // See the documentation in Options for more details on use-case. type ReferenceOverrideFunc func(reference string) (ref *Reference, overridden bool) // New constructs a Markdown processor. You can use the same With* functions as // for Run() to customize parser's behavior and the renderer. func New(opts ...Option) *Markdown { var p Markdown for _, opt := range opts { opt(&p) } p.refs = make(map[string]*reference) p.maxNesting = 16 p.insideLink = false docNode := NewNode(Document) p.doc = docNode p.tip = docNode p.oldTip = docNode p.lastMatchedContainer = docNode p.allClosed = true // register inline parsers p.inlineCallback[' '] = maybeLineBreak p.inlineCallback['*'] = emphasis p.inlineCallback['_'] = emphasis if p.extensions&Strikethrough != 0 { p.inlineCallback['~'] = emphasis } p.inlineCallback['`'] = codeSpan p.inlineCallback['\n'] = lineBreak p.inlineCallback['['] = link p.inlineCallback['<'] = leftAngle p.inlineCallback['\\'] = escape p.inlineCallback['&'] = entity p.inlineCallback['!'] = maybeImage p.inlineCallback['^'] = maybeInlineFootnote if p.extensions&Autolink != 0 { p.inlineCallback['h'] = maybeAutoLink p.inlineCallback['m'] = maybeAutoLink p.inlineCallback['f'] = maybeAutoLink p.inlineCallback['H'] = maybeAutoLink p.inlineCallback['M'] = maybeAutoLink p.inlineCallback['F'] = maybeAutoLink } if p.extensions&Footnotes != 0 { p.notes = make([]*reference, 0) } return &p } // Option customizes the Markdown processor's default behavior. type Option func(*Markdown) // WithRenderer allows you to override the default renderer. func WithRenderer(r Renderer) Option { return func(p *Markdown) { p.renderer = r } } // WithExtensions allows you to pick some of the many extensions provided by // Blackfriday. You can bitwise OR them. func WithExtensions(e Extensions) Option { return func(p *Markdown) { p.extensions = e } } // WithNoExtensions turns off all extensions and custom behavior. func WithNoExtensions() Option { return func(p *Markdown) { p.extensions = NoExtensions p.renderer = NewHTMLRenderer(HTMLRendererParameters{ Flags: HTMLFlagsNone, }) } } // WithRefOverride sets an optional function callback that is called every // time a reference is resolved. // // In Markdown, the link reference syntax can be made to resolve a link to // a reference instead of an inline URL, in one of the following ways: // // * [link text][refid] // * [refid][] // // Usually, the refid is defined at the bottom of the Markdown document. If // this override function is provided, the refid is passed to the override // function first, before consulting the defined refids at the bottom. If // the override function indicates an override did not occur, the refids at // the bottom will be used to fill in the link details. func WithRefOverride(o ReferenceOverrideFunc) Option { return func(p *Markdown) { p.referenceOverride = o } } // Run is the main entry point to Blackfriday. It parses and renders a // block of markdown-encoded text. // // The simplest invocation of Run takes one argument, input: // output := Run(input) // This will parse the input with CommonExtensions enabled and render it with // the default HTMLRenderer (with CommonHTMLFlags). // // Variadic arguments opts can customize the default behavior. Since Markdown // type does not contain exported fields, you can not use it directly. Instead, // use the With* functions. For example, this will call the most basic // functionality, with no extensions: // output := Run(input, WithNoExtensions()) // // You can use any number of With* arguments, even contradicting ones. They // will be applied in order of appearance and the latter will override the // former: // output := Run(input, WithNoExtensions(), WithExtensions(exts), // WithRenderer(yourRenderer)) func Run(input []byte, opts ...Option) []byte { r := NewHTMLRenderer(HTMLRendererParameters{ Flags: CommonHTMLFlags, }) optList := []Option{WithRenderer(r), WithExtensions(CommonExtensions)} optList = append(optList, opts...) parser := New(optList...) ast := parser.Parse(input) var buf bytes.Buffer parser.renderer.RenderHeader(&buf, ast) ast.Walk(func(node *Node, entering bool) WalkStatus { return parser.renderer.RenderNode(&buf, node, entering) }) parser.renderer.RenderFooter(&buf, ast) return buf.Bytes() } // Parse is an entry point to the parsing part of Blackfriday. It takes an // input markdown document and produces a syntax tree for its contents. This // tree can then be rendered with a default or custom renderer, or // analyzed/transformed by the caller to whatever non-standard needs they have. // The return value is the root node of the syntax tree. func (p *Markdown) Parse(input []byte) *Node { p.block(input) // Walk the tree and finish up some of unfinished blocks for p.tip != nil { p.finalize(p.tip) } // Walk the tree again and process inline markdown in each block p.doc.Walk(func(node *Node, entering bool) WalkStatus { if node.Type == Paragraph || node.Type == Heading || node.Type == TableCell { p.inline(node, node.content) node.content = nil } return GoToNext }) p.parseRefsToAST() return p.doc } func (p *Markdown) parseRefsToAST() { if p.extensions&Footnotes == 0 || len(p.notes) == 0 { return } p.tip = p.doc block := p.addBlock(List, nil) block.IsFootnotesList = true block.ListFlags = ListTypeOrdered flags := ListItemBeginningOfList // Note: this loop is intentionally explicit, not range-form. This is // because the body of the loop will append nested footnotes to p.notes and // we need to process those late additions. Range form would only walk over // the fixed initial set. for i := 0; i < len(p.notes); i++ { ref := p.notes[i] p.addExistingChild(ref.footnote, 0) block := ref.footnote block.ListFlags = flags | ListTypeOrdered block.RefLink = ref.link if ref.hasBlock { flags |= ListItemContainsBlock p.block(ref.title) } else { p.inline(block, ref.title) } flags &^= ListItemBeginningOfList | ListItemContainsBlock } above := block.Parent finalizeList(block) p.tip = above block.Walk(func(node *Node, entering bool) WalkStatus { if node.Type == Paragraph || node.Type == Heading { p.inline(node, node.content) node.content = nil } return GoToNext }) } // // Link references // // This section implements support for references that (usually) appear // as footnotes in a document, and can be referenced anywhere in the document. // The basic format is: // // [1]: http://www.google.com/ "Google" // [2]: http://www.github.com/ "Github" // // Anywhere in the document, the reference can be linked by referring to its // label, i.e., 1 and 2 in this example, as in: // // This library is hosted on [Github][2], a git hosting site. // // Actual footnotes as specified in Pandoc and supported by some other Markdown // libraries such as php-markdown are also taken care of. They look like this: // // This sentence needs a bit of further explanation.[^note] // // [^note]: This is the explanation. // // Footnotes should be placed at the end of the document in an ordered list. // Finally, there are inline footnotes such as: // // Inline footnotes^[Also supported.] provide a quick inline explanation, // but are rendered at the bottom of the document. // // reference holds all information necessary for a reference-style links or // footnotes. // // Consider this markdown with reference-style links: // // [link][ref] // // [ref]: /url/ "tooltip title" // // It will be ultimately converted to this HTML: // //

    link

    // // And a reference structure will be populated as follows: // // p.refs["ref"] = &reference{ // link: "/url/", // title: "tooltip title", // } // // Alternatively, reference can contain information about a footnote. Consider // this markdown: // // Text needing a footnote.[^a] // // [^a]: This is the note // // A reference structure will be populated as follows: // // p.refs["a"] = &reference{ // link: "a", // title: "This is the note", // noteID: , // } // // TODO: As you can see, it begs for splitting into two dedicated structures // for refs and for footnotes. type reference struct { link []byte title []byte noteID int // 0 if not a footnote ref hasBlock bool footnote *Node // a link to the Item node within a list of footnotes text []byte // only gets populated by refOverride feature with Reference.Text } func (r *reference) String() string { return fmt.Sprintf("{link: %q, title: %q, text: %q, noteID: %d, hasBlock: %v}", r.link, r.title, r.text, r.noteID, r.hasBlock) } // Check whether or not data starts with a reference link. // If so, it is parsed and stored in the list of references // (in the render struct). // Returns the number of bytes to skip to move past it, // or zero if the first line is not a reference. func isReference(p *Markdown, data []byte, tabSize int) int { // up to 3 optional leading spaces if len(data) < 4 { return 0 } i := 0 for i < 3 && data[i] == ' ' { i++ } noteID := 0 // id part: anything but a newline between brackets if data[i] != '[' { return 0 } i++ if p.extensions&Footnotes != 0 { if i < len(data) && data[i] == '^' { // we can set it to anything here because the proper noteIds will // be assigned later during the second pass. It just has to be != 0 noteID = 1 i++ } } idOffset := i for i < len(data) && data[i] != '\n' && data[i] != '\r' && data[i] != ']' { i++ } if i >= len(data) || data[i] != ']' { return 0 } idEnd := i // footnotes can have empty ID, like this: [^], but a reference can not be // empty like this: []. Break early if it's not a footnote and there's no ID if noteID == 0 && idOffset == idEnd { return 0 } // spacer: colon (space | tab)* newline? (space | tab)* i++ if i >= len(data) || data[i] != ':' { return 0 } i++ for i < len(data) && (data[i] == ' ' || data[i] == '\t') { i++ } if i < len(data) && (data[i] == '\n' || data[i] == '\r') { i++ if i < len(data) && data[i] == '\n' && data[i-1] == '\r' { i++ } } for i < len(data) && (data[i] == ' ' || data[i] == '\t') { i++ } if i >= len(data) { return 0 } var ( linkOffset, linkEnd int titleOffset, titleEnd int lineEnd int raw []byte hasBlock bool ) if p.extensions&Footnotes != 0 && noteID != 0 { linkOffset, linkEnd, raw, hasBlock = scanFootnote(p, data, i, tabSize) lineEnd = linkEnd } else { linkOffset, linkEnd, titleOffset, titleEnd, lineEnd = scanLinkRef(p, data, i) } if lineEnd == 0 { return 0 } // a valid ref has been found ref := &reference{ noteID: noteID, hasBlock: hasBlock, } if noteID > 0 { // reusing the link field for the id since footnotes don't have links ref.link = data[idOffset:idEnd] // if footnote, it's not really a title, it's the contained text ref.title = raw } else { ref.link = data[linkOffset:linkEnd] ref.title = data[titleOffset:titleEnd] } // id matches are case-insensitive id := string(bytes.ToLower(data[idOffset:idEnd])) p.refs[id] = ref return lineEnd } func scanLinkRef(p *Markdown, data []byte, i int) (linkOffset, linkEnd, titleOffset, titleEnd, lineEnd int) { // link: whitespace-free sequence, optionally between angle brackets if data[i] == '<' { i++ } linkOffset = i for i < len(data) && data[i] != ' ' && data[i] != '\t' && data[i] != '\n' && data[i] != '\r' { i++ } linkEnd = i if data[linkOffset] == '<' && data[linkEnd-1] == '>' { linkOffset++ linkEnd-- } // optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) for i < len(data) && (data[i] == ' ' || data[i] == '\t') { i++ } if i < len(data) && data[i] != '\n' && data[i] != '\r' && data[i] != '\'' && data[i] != '"' && data[i] != '(' { return } // compute end-of-line if i >= len(data) || data[i] == '\r' || data[i] == '\n' { lineEnd = i } if i+1 < len(data) && data[i] == '\r' && data[i+1] == '\n' { lineEnd++ } // optional (space|tab)* spacer after a newline if lineEnd > 0 { i = lineEnd + 1 for i < len(data) && (data[i] == ' ' || data[i] == '\t') { i++ } } // optional title: any non-newline sequence enclosed in '"() alone on its line if i+1 < len(data) && (data[i] == '\'' || data[i] == '"' || data[i] == '(') { i++ titleOffset = i // look for EOL for i < len(data) && data[i] != '\n' && data[i] != '\r' { i++ } if i+1 < len(data) && data[i] == '\n' && data[i+1] == '\r' { titleEnd = i + 1 } else { titleEnd = i } // step back i-- for i > titleOffset && (data[i] == ' ' || data[i] == '\t') { i-- } if i > titleOffset && (data[i] == '\'' || data[i] == '"' || data[i] == ')') { lineEnd = titleEnd titleEnd = i } } return } // The first bit of this logic is the same as Parser.listItem, but the rest // is much simpler. This function simply finds the entire block and shifts it // over by one tab if it is indeed a block (just returns the line if it's not). // blockEnd is the end of the section in the input buffer, and contents is the // extracted text that was shifted over one tab. It will need to be rendered at // the end of the document. func scanFootnote(p *Markdown, data []byte, i, indentSize int) (blockStart, blockEnd int, contents []byte, hasBlock bool) { if i == 0 || len(data) == 0 { return } // skip leading whitespace on first line for i < len(data) && data[i] == ' ' { i++ } blockStart = i // find the end of the line blockEnd = i for i < len(data) && data[i-1] != '\n' { i++ } // get working buffer var raw bytes.Buffer // put the first line into the working buffer raw.Write(data[blockEnd:i]) blockEnd = i // process the following lines containsBlankLine := false gatherLines: for blockEnd < len(data) { i++ // find the end of this line for i < len(data) && data[i-1] != '\n' { i++ } // if it is an empty line, guess that it is part of this item // and move on to the next line if p.isEmpty(data[blockEnd:i]) > 0 { containsBlankLine = true blockEnd = i continue } n := 0 if n = isIndented(data[blockEnd:i], indentSize); n == 0 { // this is the end of the block. // we don't want to include this last line in the index. break gatherLines } // if there were blank lines before this one, insert a new one now if containsBlankLine { raw.WriteByte('\n') containsBlankLine = false } // get rid of that first tab, write to buffer raw.Write(data[blockEnd+n : i]) hasBlock = true blockEnd = i } if data[blockEnd-1] != '\n' { raw.WriteByte('\n') } contents = raw.Bytes() return } // // // Miscellaneous helper functions // // // Test if a character is a punctuation symbol. // Taken from a private function in regexp in the stdlib. func ispunct(c byte) bool { for _, r := range []byte("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~") { if c == r { return true } } return false } // Test if a character is a whitespace character. func isspace(c byte) bool { return ishorizontalspace(c) || isverticalspace(c) } // Test if a character is a horizontal whitespace character. func ishorizontalspace(c byte) bool { return c == ' ' || c == '\t' } // Test if a character is a vertical character. func isverticalspace(c byte) bool { return c == '\n' || c == '\r' || c == '\f' || c == '\v' } // Test if a character is letter. func isletter(c byte) bool { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') } // Test if a character is a letter or a digit. // TODO: check when this is looking for ASCII alnum and when it should use unicode func isalnum(c byte) bool { return (c >= '0' && c <= '9') || isletter(c) } // Replace tab characters with spaces, aligning to the next TAB_SIZE column. // always ends output with a newline func expandTabs(out *bytes.Buffer, line []byte, tabSize int) { // first, check for common cases: no tabs, or only tabs at beginning of line i, prefix := 0, 0 slowcase := false for i = 0; i < len(line); i++ { if line[i] == '\t' { if prefix == i { prefix++ } else { slowcase = true break } } } // no need to decode runes if all tabs are at the beginning of the line if !slowcase { for i = 0; i < prefix*tabSize; i++ { out.WriteByte(' ') } out.Write(line[prefix:]) return } // the slow case: we need to count runes to figure out how // many spaces to insert for each tab column := 0 i = 0 for i < len(line) { start := i for i < len(line) && line[i] != '\t' { _, size := utf8.DecodeRune(line[i:]) i += size column++ } if i > start { out.Write(line[start:i]) } if i >= len(line) { break } for { out.WriteByte(' ') column++ if column%tabSize == 0 { break } } i++ } } // Find if a line counts as indented or not. // Returns number of characters the indent is (0 = not indented). func isIndented(data []byte, indentSize int) int { if len(data) == 0 { return 0 } if data[0] == '\t' { return 1 } if len(data) < indentSize { return 0 } for i := 0; i < indentSize; i++ { if data[i] != ' ' { return 0 } } return indentSize } // Create a url-safe slug for fragments func slugify(in []byte) []byte { if len(in) == 0 { return in } out := make([]byte, 0, len(in)) sym := false for _, ch := range in { if isalnum(ch) { sym = false out = append(out, ch) } else if sym { continue } else { out = append(out, '-') sym = true } } var a, b int var ch byte for a, ch = range out { if ch != '-' { break } } for b = len(out) - 1; b > 0; b-- { if out[b] != '-' { break } } return out[a : b+1] } ================================================ FILE: vendor/github.com/russross/blackfriday/v2/node.go ================================================ package blackfriday import ( "bytes" "fmt" ) // NodeType specifies a type of a single node of a syntax tree. Usually one // node (and its type) corresponds to a single markdown feature, e.g. emphasis // or code block. type NodeType int // Constants for identifying different types of nodes. See NodeType. const ( Document NodeType = iota BlockQuote List Item Paragraph Heading HorizontalRule Emph Strong Del Link Image Text HTMLBlock CodeBlock Softbreak Hardbreak Code HTMLSpan Table TableCell TableHead TableBody TableRow ) var nodeTypeNames = []string{ Document: "Document", BlockQuote: "BlockQuote", List: "List", Item: "Item", Paragraph: "Paragraph", Heading: "Heading", HorizontalRule: "HorizontalRule", Emph: "Emph", Strong: "Strong", Del: "Del", Link: "Link", Image: "Image", Text: "Text", HTMLBlock: "HTMLBlock", CodeBlock: "CodeBlock", Softbreak: "Softbreak", Hardbreak: "Hardbreak", Code: "Code", HTMLSpan: "HTMLSpan", Table: "Table", TableCell: "TableCell", TableHead: "TableHead", TableBody: "TableBody", TableRow: "TableRow", } func (t NodeType) String() string { return nodeTypeNames[t] } // ListData contains fields relevant to a List and Item node type. type ListData struct { ListFlags ListType Tight bool // Skip

    s around list item data if true BulletChar byte // '*', '+' or '-' in bullet lists Delimiter byte // '.' or ')' after the number in ordered lists RefLink []byte // If not nil, turns this list item into a footnote item and triggers different rendering IsFootnotesList bool // This is a list of footnotes } // LinkData contains fields relevant to a Link node type. type LinkData struct { Destination []byte // Destination is what goes into a href Title []byte // Title is the tooltip thing that goes in a title attribute NoteID int // NoteID contains a serial number of a footnote, zero if it's not a footnote Footnote *Node // If it's a footnote, this is a direct link to the footnote Node. Otherwise nil. } // CodeBlockData contains fields relevant to a CodeBlock node type. type CodeBlockData struct { IsFenced bool // Specifies whether it's a fenced code block or an indented one Info []byte // This holds the info string FenceChar byte FenceLength int FenceOffset int } // TableCellData contains fields relevant to a TableCell node type. type TableCellData struct { IsHeader bool // This tells if it's under the header row Align CellAlignFlags // This holds the value for align attribute } // HeadingData contains fields relevant to a Heading node type. type HeadingData struct { Level int // This holds the heading level number HeadingID string // This might hold heading ID, if present IsTitleblock bool // Specifies whether it's a title block } // Node is a single element in the abstract syntax tree of the parsed document. // It holds connections to the structurally neighboring nodes and, for certain // types of nodes, additional information that might be needed when rendering. type Node struct { Type NodeType // Determines the type of the node Parent *Node // Points to the parent FirstChild *Node // Points to the first child, if any LastChild *Node // Points to the last child, if any Prev *Node // Previous sibling; nil if it's the first child Next *Node // Next sibling; nil if it's the last child Literal []byte // Text contents of the leaf nodes HeadingData // Populated if Type is Heading ListData // Populated if Type is List CodeBlockData // Populated if Type is CodeBlock LinkData // Populated if Type is Link TableCellData // Populated if Type is TableCell content []byte // Markdown content of the block nodes open bool // Specifies an open block node that has not been finished to process yet } // NewNode allocates a node of a specified type. func NewNode(typ NodeType) *Node { return &Node{ Type: typ, open: true, } } func (n *Node) String() string { ellipsis := "" snippet := n.Literal if len(snippet) > 16 { snippet = snippet[:16] ellipsis = "..." } return fmt.Sprintf("%s: '%s%s'", n.Type, snippet, ellipsis) } // Unlink removes node 'n' from the tree. // It panics if the node is nil. func (n *Node) Unlink() { if n.Prev != nil { n.Prev.Next = n.Next } else if n.Parent != nil { n.Parent.FirstChild = n.Next } if n.Next != nil { n.Next.Prev = n.Prev } else if n.Parent != nil { n.Parent.LastChild = n.Prev } n.Parent = nil n.Next = nil n.Prev = nil } // AppendChild adds a node 'child' as a child of 'n'. // It panics if either node is nil. func (n *Node) AppendChild(child *Node) { child.Unlink() child.Parent = n if n.LastChild != nil { n.LastChild.Next = child child.Prev = n.LastChild n.LastChild = child } else { n.FirstChild = child n.LastChild = child } } // InsertBefore inserts 'sibling' immediately before 'n'. // It panics if either node is nil. func (n *Node) InsertBefore(sibling *Node) { sibling.Unlink() sibling.Prev = n.Prev if sibling.Prev != nil { sibling.Prev.Next = sibling } sibling.Next = n n.Prev = sibling sibling.Parent = n.Parent if sibling.Prev == nil { sibling.Parent.FirstChild = sibling } } // IsContainer returns true if 'n' can contain children. func (n *Node) IsContainer() bool { switch n.Type { case Document: fallthrough case BlockQuote: fallthrough case List: fallthrough case Item: fallthrough case Paragraph: fallthrough case Heading: fallthrough case Emph: fallthrough case Strong: fallthrough case Del: fallthrough case Link: fallthrough case Image: fallthrough case Table: fallthrough case TableHead: fallthrough case TableBody: fallthrough case TableRow: fallthrough case TableCell: return true default: return false } } // IsLeaf returns true if 'n' is a leaf node. func (n *Node) IsLeaf() bool { return !n.IsContainer() } func (n *Node) canContain(t NodeType) bool { if n.Type == List { return t == Item } if n.Type == Document || n.Type == BlockQuote || n.Type == Item { return t != Item } if n.Type == Table { return t == TableHead || t == TableBody } if n.Type == TableHead || n.Type == TableBody { return t == TableRow } if n.Type == TableRow { return t == TableCell } return false } // WalkStatus allows NodeVisitor to have some control over the tree traversal. // It is returned from NodeVisitor and different values allow Node.Walk to // decide which node to go to next. type WalkStatus int const ( // GoToNext is the default traversal of every node. GoToNext WalkStatus = iota // SkipChildren tells walker to skip all children of current node. SkipChildren // Terminate tells walker to terminate the traversal. Terminate ) // NodeVisitor is a callback to be called when traversing the syntax tree. // Called twice for every node: once with entering=true when the branch is // first visited, then with entering=false after all the children are done. type NodeVisitor func(node *Node, entering bool) WalkStatus // Walk is a convenience method that instantiates a walker and starts a // traversal of subtree rooted at n. func (n *Node) Walk(visitor NodeVisitor) { w := newNodeWalker(n) for w.current != nil { status := visitor(w.current, w.entering) switch status { case GoToNext: w.next() case SkipChildren: w.entering = false w.next() case Terminate: return } } } type nodeWalker struct { current *Node root *Node entering bool } func newNodeWalker(root *Node) *nodeWalker { return &nodeWalker{ current: root, root: root, entering: true, } } func (nw *nodeWalker) next() { if (!nw.current.IsContainer() || !nw.entering) && nw.current == nw.root { nw.current = nil return } if nw.entering && nw.current.IsContainer() { if nw.current.FirstChild != nil { nw.current = nw.current.FirstChild nw.entering = true } else { nw.entering = false } } else if nw.current.Next == nil { nw.current = nw.current.Parent nw.entering = false } else { nw.current = nw.current.Next nw.entering = true } } func dump(ast *Node) { fmt.Println(dumpString(ast)) } func dumpR(ast *Node, depth int) string { if ast == nil { return "" } indent := bytes.Repeat([]byte("\t"), depth) content := ast.Literal if content == nil { content = ast.content } result := fmt.Sprintf("%s%s(%q)\n", indent, ast.Type, content) for n := ast.FirstChild; n != nil; n = n.Next { result += dumpR(n, depth+1) } return result } func dumpString(ast *Node) string { return dumpR(ast, 0) } ================================================ FILE: vendor/github.com/russross/blackfriday/v2/smartypants.go ================================================ // // Blackfriday Markdown Processor // Available at http://github.com/russross/blackfriday // // Copyright © 2011 Russ Ross . // Distributed under the Simplified BSD License. // See README.md for details. // // // // SmartyPants rendering // // package blackfriday import ( "bytes" "io" ) // SPRenderer is a struct containing state of a Smartypants renderer. type SPRenderer struct { inSingleQuote bool inDoubleQuote bool callbacks [256]smartCallback } func wordBoundary(c byte) bool { return c == 0 || isspace(c) || ispunct(c) } func tolower(c byte) byte { if c >= 'A' && c <= 'Z' { return c - 'A' + 'a' } return c } func isdigit(c byte) bool { return c >= '0' && c <= '9' } func smartQuoteHelper(out *bytes.Buffer, previousChar byte, nextChar byte, quote byte, isOpen *bool, addNBSP bool) bool { // edge of the buffer is likely to be a tag that we don't get to see, // so we treat it like text sometimes // enumerate all sixteen possibilities for (previousChar, nextChar) // each can be one of {0, space, punct, other} switch { case previousChar == 0 && nextChar == 0: // context is not any help here, so toggle *isOpen = !*isOpen case isspace(previousChar) && nextChar == 0: // [ "] might be [ "foo...] *isOpen = true case ispunct(previousChar) && nextChar == 0: // [!"] hmm... could be [Run!"] or [("...] *isOpen = false case /* isnormal(previousChar) && */ nextChar == 0: // [a"] is probably a close *isOpen = false case previousChar == 0 && isspace(nextChar): // [" ] might be [...foo" ] *isOpen = false case isspace(previousChar) && isspace(nextChar): // [ " ] context is not any help here, so toggle *isOpen = !*isOpen case ispunct(previousChar) && isspace(nextChar): // [!" ] is probably a close *isOpen = false case /* isnormal(previousChar) && */ isspace(nextChar): // [a" ] this is one of the easy cases *isOpen = false case previousChar == 0 && ispunct(nextChar): // ["!] hmm... could be ["$1.95] or ["!...] *isOpen = false case isspace(previousChar) && ispunct(nextChar): // [ "!] looks more like [ "$1.95] *isOpen = true case ispunct(previousChar) && ispunct(nextChar): // [!"!] context is not any help here, so toggle *isOpen = !*isOpen case /* isnormal(previousChar) && */ ispunct(nextChar): // [a"!] is probably a close *isOpen = false case previousChar == 0 /* && isnormal(nextChar) */ : // ["a] is probably an open *isOpen = true case isspace(previousChar) /* && isnormal(nextChar) */ : // [ "a] this is one of the easy cases *isOpen = true case ispunct(previousChar) /* && isnormal(nextChar) */ : // [!"a] is probably an open *isOpen = true default: // [a'b] maybe a contraction? *isOpen = false } // Note that with the limited lookahead, this non-breaking // space will also be appended to single double quotes. if addNBSP && !*isOpen { out.WriteString(" ") } out.WriteByte('&') if *isOpen { out.WriteByte('l') } else { out.WriteByte('r') } out.WriteByte(quote) out.WriteString("quo;") if addNBSP && *isOpen { out.WriteString(" ") } return true } func (r *SPRenderer) smartSingleQuote(out *bytes.Buffer, previousChar byte, text []byte) int { if len(text) >= 2 { t1 := tolower(text[1]) if t1 == '\'' { nextChar := byte(0) if len(text) >= 3 { nextChar = text[2] } if smartQuoteHelper(out, previousChar, nextChar, 'd', &r.inDoubleQuote, false) { return 1 } } if (t1 == 's' || t1 == 't' || t1 == 'm' || t1 == 'd') && (len(text) < 3 || wordBoundary(text[2])) { out.WriteString("’") return 0 } if len(text) >= 3 { t2 := tolower(text[2]) if ((t1 == 'r' && t2 == 'e') || (t1 == 'l' && t2 == 'l') || (t1 == 'v' && t2 == 'e')) && (len(text) < 4 || wordBoundary(text[3])) { out.WriteString("’") return 0 } } } nextChar := byte(0) if len(text) > 1 { nextChar = text[1] } if smartQuoteHelper(out, previousChar, nextChar, 's', &r.inSingleQuote, false) { return 0 } out.WriteByte(text[0]) return 0 } func (r *SPRenderer) smartParens(out *bytes.Buffer, previousChar byte, text []byte) int { if len(text) >= 3 { t1 := tolower(text[1]) t2 := tolower(text[2]) if t1 == 'c' && t2 == ')' { out.WriteString("©") return 2 } if t1 == 'r' && t2 == ')' { out.WriteString("®") return 2 } if len(text) >= 4 && t1 == 't' && t2 == 'm' && text[3] == ')' { out.WriteString("™") return 3 } } out.WriteByte(text[0]) return 0 } func (r *SPRenderer) smartDash(out *bytes.Buffer, previousChar byte, text []byte) int { if len(text) >= 2 { if text[1] == '-' { out.WriteString("—") return 1 } if wordBoundary(previousChar) && wordBoundary(text[1]) { out.WriteString("–") return 0 } } out.WriteByte(text[0]) return 0 } func (r *SPRenderer) smartDashLatex(out *bytes.Buffer, previousChar byte, text []byte) int { if len(text) >= 3 && text[1] == '-' && text[2] == '-' { out.WriteString("—") return 2 } if len(text) >= 2 && text[1] == '-' { out.WriteString("–") return 1 } out.WriteByte(text[0]) return 0 } func (r *SPRenderer) smartAmpVariant(out *bytes.Buffer, previousChar byte, text []byte, quote byte, addNBSP bool) int { if bytes.HasPrefix(text, []byte(""")) { nextChar := byte(0) if len(text) >= 7 { nextChar = text[6] } if smartQuoteHelper(out, previousChar, nextChar, quote, &r.inDoubleQuote, addNBSP) { return 5 } } if bytes.HasPrefix(text, []byte("�")) { return 3 } out.WriteByte('&') return 0 } func (r *SPRenderer) smartAmp(angledQuotes, addNBSP bool) func(*bytes.Buffer, byte, []byte) int { var quote byte = 'd' if angledQuotes { quote = 'a' } return func(out *bytes.Buffer, previousChar byte, text []byte) int { return r.smartAmpVariant(out, previousChar, text, quote, addNBSP) } } func (r *SPRenderer) smartPeriod(out *bytes.Buffer, previousChar byte, text []byte) int { if len(text) >= 3 && text[1] == '.' && text[2] == '.' { out.WriteString("…") return 2 } if len(text) >= 5 && text[1] == ' ' && text[2] == '.' && text[3] == ' ' && text[4] == '.' { out.WriteString("…") return 4 } out.WriteByte(text[0]) return 0 } func (r *SPRenderer) smartBacktick(out *bytes.Buffer, previousChar byte, text []byte) int { if len(text) >= 2 && text[1] == '`' { nextChar := byte(0) if len(text) >= 3 { nextChar = text[2] } if smartQuoteHelper(out, previousChar, nextChar, 'd', &r.inDoubleQuote, false) { return 1 } } out.WriteByte(text[0]) return 0 } func (r *SPRenderer) smartNumberGeneric(out *bytes.Buffer, previousChar byte, text []byte) int { if wordBoundary(previousChar) && previousChar != '/' && len(text) >= 3 { // is it of the form digits/digits(word boundary)?, i.e., \d+/\d+\b // note: check for regular slash (/) or fraction slash (⁄, 0x2044, or 0xe2 81 84 in utf-8) // and avoid changing dates like 1/23/2005 into fractions. numEnd := 0 for len(text) > numEnd && isdigit(text[numEnd]) { numEnd++ } if numEnd == 0 { out.WriteByte(text[0]) return 0 } denStart := numEnd + 1 if len(text) > numEnd+3 && text[numEnd] == 0xe2 && text[numEnd+1] == 0x81 && text[numEnd+2] == 0x84 { denStart = numEnd + 3 } else if len(text) < numEnd+2 || text[numEnd] != '/' { out.WriteByte(text[0]) return 0 } denEnd := denStart for len(text) > denEnd && isdigit(text[denEnd]) { denEnd++ } if denEnd == denStart { out.WriteByte(text[0]) return 0 } if len(text) == denEnd || wordBoundary(text[denEnd]) && text[denEnd] != '/' { out.WriteString("") out.Write(text[:numEnd]) out.WriteString("") out.Write(text[denStart:denEnd]) out.WriteString("") return denEnd - 1 } } out.WriteByte(text[0]) return 0 } func (r *SPRenderer) smartNumber(out *bytes.Buffer, previousChar byte, text []byte) int { if wordBoundary(previousChar) && previousChar != '/' && len(text) >= 3 { if text[0] == '1' && text[1] == '/' && text[2] == '2' { if len(text) < 4 || wordBoundary(text[3]) && text[3] != '/' { out.WriteString("½") return 2 } } if text[0] == '1' && text[1] == '/' && text[2] == '4' { if len(text) < 4 || wordBoundary(text[3]) && text[3] != '/' || (len(text) >= 5 && tolower(text[3]) == 't' && tolower(text[4]) == 'h') { out.WriteString("¼") return 2 } } if text[0] == '3' && text[1] == '/' && text[2] == '4' { if len(text) < 4 || wordBoundary(text[3]) && text[3] != '/' || (len(text) >= 6 && tolower(text[3]) == 't' && tolower(text[4]) == 'h' && tolower(text[5]) == 's') { out.WriteString("¾") return 2 } } } out.WriteByte(text[0]) return 0 } func (r *SPRenderer) smartDoubleQuoteVariant(out *bytes.Buffer, previousChar byte, text []byte, quote byte) int { nextChar := byte(0) if len(text) > 1 { nextChar = text[1] } if !smartQuoteHelper(out, previousChar, nextChar, quote, &r.inDoubleQuote, false) { out.WriteString(""") } return 0 } func (r *SPRenderer) smartDoubleQuote(out *bytes.Buffer, previousChar byte, text []byte) int { return r.smartDoubleQuoteVariant(out, previousChar, text, 'd') } func (r *SPRenderer) smartAngledDoubleQuote(out *bytes.Buffer, previousChar byte, text []byte) int { return r.smartDoubleQuoteVariant(out, previousChar, text, 'a') } func (r *SPRenderer) smartLeftAngle(out *bytes.Buffer, previousChar byte, text []byte) int { i := 0 for i < len(text) && text[i] != '>' { i++ } out.Write(text[:i+1]) return i } type smartCallback func(out *bytes.Buffer, previousChar byte, text []byte) int // NewSmartypantsRenderer constructs a Smartypants renderer object. func NewSmartypantsRenderer(flags HTMLFlags) *SPRenderer { var ( r SPRenderer smartAmpAngled = r.smartAmp(true, false) smartAmpAngledNBSP = r.smartAmp(true, true) smartAmpRegular = r.smartAmp(false, false) smartAmpRegularNBSP = r.smartAmp(false, true) addNBSP = flags&SmartypantsQuotesNBSP != 0 ) if flags&SmartypantsAngledQuotes == 0 { r.callbacks['"'] = r.smartDoubleQuote if !addNBSP { r.callbacks['&'] = smartAmpRegular } else { r.callbacks['&'] = smartAmpRegularNBSP } } else { r.callbacks['"'] = r.smartAngledDoubleQuote if !addNBSP { r.callbacks['&'] = smartAmpAngled } else { r.callbacks['&'] = smartAmpAngledNBSP } } r.callbacks['\''] = r.smartSingleQuote r.callbacks['('] = r.smartParens if flags&SmartypantsDashes != 0 { if flags&SmartypantsLatexDashes == 0 { r.callbacks['-'] = r.smartDash } else { r.callbacks['-'] = r.smartDashLatex } } r.callbacks['.'] = r.smartPeriod if flags&SmartypantsFractions == 0 { r.callbacks['1'] = r.smartNumber r.callbacks['3'] = r.smartNumber } else { for ch := '1'; ch <= '9'; ch++ { r.callbacks[ch] = r.smartNumberGeneric } } r.callbacks['<'] = r.smartLeftAngle r.callbacks['`'] = r.smartBacktick return &r } // Process is the entry point of the Smartypants renderer. func (r *SPRenderer) Process(w io.Writer, text []byte) { mark := 0 for i := 0; i < len(text); i++ { if action := r.callbacks[text[i]]; action != nil { if i > mark { w.Write(text[mark:i]) } previousChar := byte(0) if i > 0 { previousChar = text[i-1] } var tmp bytes.Buffer i += action(&tmp, previousChar, text[i:]) w.Write(tmp.Bytes()) mark = i + 1 } } if mark < len(text) { w.Write(text[mark:]) } } ================================================ FILE: vendor/github.com/saintfish/chardet/2022.go ================================================ package chardet import ( "bytes" ) type recognizer2022 struct { charset string escapes [][]byte } func (r *recognizer2022) Match(input *recognizerInput) (output recognizerOutput) { return recognizerOutput{ Charset: r.charset, Confidence: r.matchConfidence(input.input), } } func (r *recognizer2022) matchConfidence(input []byte) int { var hits, misses, shifts int input: for i := 0; i < len(input); i++ { c := input[i] if c == 0x1B { for _, esc := range r.escapes { if bytes.HasPrefix(input[i+1:], esc) { hits++ i += len(esc) continue input } } misses++ } else if c == 0x0E || c == 0x0F { shifts++ } } if hits == 0 { return 0 } quality := (100*hits - 100*misses) / (hits + misses) if hits+shifts < 5 { quality -= (5 - (hits + shifts)) * 10 } if quality < 0 { quality = 0 } return quality } var escapeSequences_2022JP = [][]byte{ {0x24, 0x28, 0x43}, // KS X 1001:1992 {0x24, 0x28, 0x44}, // JIS X 212-1990 {0x24, 0x40}, // JIS C 6226-1978 {0x24, 0x41}, // GB 2312-80 {0x24, 0x42}, // JIS X 208-1983 {0x26, 0x40}, // JIS X 208 1990, 1997 {0x28, 0x42}, // ASCII {0x28, 0x48}, // JIS-Roman {0x28, 0x49}, // Half-width katakana {0x28, 0x4a}, // JIS-Roman {0x2e, 0x41}, // ISO 8859-1 {0x2e, 0x46}, // ISO 8859-7 } var escapeSequences_2022KR = [][]byte{ {0x24, 0x29, 0x43}, } var escapeSequences_2022CN = [][]byte{ {0x24, 0x29, 0x41}, // GB 2312-80 {0x24, 0x29, 0x47}, // CNS 11643-1992 Plane 1 {0x24, 0x2A, 0x48}, // CNS 11643-1992 Plane 2 {0x24, 0x29, 0x45}, // ISO-IR-165 {0x24, 0x2B, 0x49}, // CNS 11643-1992 Plane 3 {0x24, 0x2B, 0x4A}, // CNS 11643-1992 Plane 4 {0x24, 0x2B, 0x4B}, // CNS 11643-1992 Plane 5 {0x24, 0x2B, 0x4C}, // CNS 11643-1992 Plane 6 {0x24, 0x2B, 0x4D}, // CNS 11643-1992 Plane 7 {0x4e}, // SS2 {0x4f}, // SS3 } func newRecognizer_2022JP() *recognizer2022 { return &recognizer2022{ "ISO-2022-JP", escapeSequences_2022JP, } } func newRecognizer_2022KR() *recognizer2022 { return &recognizer2022{ "ISO-2022-KR", escapeSequences_2022KR, } } func newRecognizer_2022CN() *recognizer2022 { return &recognizer2022{ "ISO-2022-CN", escapeSequences_2022CN, } } ================================================ FILE: vendor/github.com/saintfish/chardet/AUTHORS ================================================ Sheng Yu (yusheng dot sjtu at gmail dot com) ================================================ FILE: vendor/github.com/saintfish/chardet/LICENSE ================================================ Copyright (c) 2012 chardet Authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Partial of the Software is derived from ICU project. See icu-license.html for license of the derivative portions. ================================================ FILE: vendor/github.com/saintfish/chardet/README.md ================================================ # chardet chardet is library to automatically detect [charset](http://en.wikipedia.org/wiki/Character_encoding) of texts for [Go programming language](http://golang.org/). It's based on the algorithm and data in [ICU](http://icu-project.org/)'s implementation. ## Documentation and Usage See [pkgdoc](https://pkg.go.dev/github.com/saintfish/chardet) ================================================ FILE: vendor/github.com/saintfish/chardet/detector.go ================================================ // Package chardet ports character set detection from ICU. package chardet import ( "errors" "sort" ) // Result contains all the information that charset detector gives. type Result struct { // IANA name of the detected charset. Charset string // IANA name of the detected language. It may be empty for some charsets. Language string // Confidence of the Result. Scale from 1 to 100. The bigger, the more confident. Confidence int } // Detector implements charset detection. type Detector struct { recognizers []recognizer stripTag bool } // List of charset recognizers var recognizers = []recognizer{ newRecognizer_utf8(), newRecognizer_utf16be(), newRecognizer_utf16le(), newRecognizer_utf32be(), newRecognizer_utf32le(), newRecognizer_8859_1_en(), newRecognizer_8859_1_da(), newRecognizer_8859_1_de(), newRecognizer_8859_1_es(), newRecognizer_8859_1_fr(), newRecognizer_8859_1_it(), newRecognizer_8859_1_nl(), newRecognizer_8859_1_no(), newRecognizer_8859_1_pt(), newRecognizer_8859_1_sv(), newRecognizer_8859_2_cs(), newRecognizer_8859_2_hu(), newRecognizer_8859_2_pl(), newRecognizer_8859_2_ro(), newRecognizer_8859_5_ru(), newRecognizer_8859_6_ar(), newRecognizer_8859_7_el(), newRecognizer_8859_8_I_he(), newRecognizer_8859_8_he(), newRecognizer_windows_1251(), newRecognizer_windows_1256(), newRecognizer_KOI8_R(), newRecognizer_8859_9_tr(), newRecognizer_sjis(), newRecognizer_gb_18030(), newRecognizer_euc_jp(), newRecognizer_euc_kr(), newRecognizer_big5(), newRecognizer_2022JP(), newRecognizer_2022KR(), newRecognizer_2022CN(), newRecognizer_IBM424_he_rtl(), newRecognizer_IBM424_he_ltr(), newRecognizer_IBM420_ar_rtl(), newRecognizer_IBM420_ar_ltr(), } // NewTextDetector creates a Detector for plain text. func NewTextDetector() *Detector { return &Detector{recognizers, false} } // NewHtmlDetector creates a Detector for Html. func NewHtmlDetector() *Detector { return &Detector{recognizers, true} } var ( NotDetectedError = errors.New("Charset not detected.") ) // DetectBest returns the Result with highest Confidence. func (d *Detector) DetectBest(b []byte) (r *Result, err error) { var all []Result if all, err = d.DetectAll(b); err == nil { r = &all[0] } return } // DetectAll returns all Results which have non-zero Confidence. The Results are sorted by Confidence in descending order. func (d *Detector) DetectAll(b []byte) ([]Result, error) { input := newRecognizerInput(b, d.stripTag) outputChan := make(chan recognizerOutput) for _, r := range d.recognizers { go matchHelper(r, input, outputChan) } outputs := make([]recognizerOutput, 0, len(d.recognizers)) for i := 0; i < len(d.recognizers); i++ { o := <-outputChan if o.Confidence > 0 { outputs = append(outputs, o) } } if len(outputs) == 0 { return nil, NotDetectedError } sort.Sort(recognizerOutputs(outputs)) dedupOutputs := make([]Result, 0, len(outputs)) foundCharsets := make(map[string]struct{}, len(outputs)) for _, o := range outputs { if _, found := foundCharsets[o.Charset]; !found { dedupOutputs = append(dedupOutputs, Result(o)) foundCharsets[o.Charset] = struct{}{} } } if len(dedupOutputs) == 0 { return nil, NotDetectedError } return dedupOutputs, nil } func matchHelper(r recognizer, input *recognizerInput, outputChan chan<- recognizerOutput) { outputChan <- r.Match(input) } type recognizerOutputs []recognizerOutput func (r recognizerOutputs) Len() int { return len(r) } func (r recognizerOutputs) Less(i, j int) bool { return r[i].Confidence > r[j].Confidence } func (r recognizerOutputs) Swap(i, j int) { r[i], r[j] = r[j], r[i] } ================================================ FILE: vendor/github.com/saintfish/chardet/icu-license.html ================================================ ICU License - ICU 1.8.1 and later

    ICU License - ICU 1.8.1 and later

    COPYRIGHT AND PERMISSION NOTICE

    Copyright (c) 1995-2012 International Business Machines Corporation and others

    All rights reserved.

    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, provided that the above copyright notice(s) and this permission notice appear in all copies of the Software and that both the above copyright notice(s) and this permission notice appear in supporting documentation.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

    Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization of the copyright holder.


    All trademarks and registered trademarks mentioned herein are the property of their respective owners.

    ================================================ FILE: vendor/github.com/saintfish/chardet/multi_byte.go ================================================ package chardet import ( "errors" "math" ) type recognizerMultiByte struct { charset string language string decoder charDecoder commonChars []uint16 } type charDecoder interface { DecodeOneChar([]byte) (c uint16, remain []byte, err error) } func (r *recognizerMultiByte) Match(input *recognizerInput) (output recognizerOutput) { return recognizerOutput{ Charset: r.charset, Language: r.language, Confidence: r.matchConfidence(input), } } func (r *recognizerMultiByte) matchConfidence(input *recognizerInput) int { raw := input.raw var c uint16 var err error var totalCharCount, badCharCount, singleByteCharCount, doubleByteCharCount, commonCharCount int for c, raw, err = r.decoder.DecodeOneChar(raw); len(raw) > 0; c, raw, err = r.decoder.DecodeOneChar(raw) { totalCharCount++ if err != nil { badCharCount++ } else if c <= 0xFF { singleByteCharCount++ } else { doubleByteCharCount++ if r.commonChars != nil && binarySearch(r.commonChars, c) { commonCharCount++ } } if badCharCount >= 2 && badCharCount*5 >= doubleByteCharCount { return 0 } } if doubleByteCharCount <= 10 && badCharCount == 0 { if doubleByteCharCount == 0 && totalCharCount < 10 { return 0 } else { return 10 } } if doubleByteCharCount < 20*badCharCount { return 0 } if r.commonChars == nil { confidence := 30 + doubleByteCharCount - 20*badCharCount if confidence > 100 { confidence = 100 } return confidence } maxVal := math.Log(float64(doubleByteCharCount) / 4) scaleFactor := 90 / maxVal confidence := int(math.Log(float64(commonCharCount)+1)*scaleFactor + 10) if confidence > 100 { confidence = 100 } if confidence < 0 { confidence = 0 } return confidence } func binarySearch(l []uint16, c uint16) bool { start := 0 end := len(l) - 1 for start <= end { mid := (start + end) / 2 if c == l[mid] { return true } else if c < l[mid] { end = mid - 1 } else { start = mid + 1 } } return false } var eobError = errors.New("End of input buffer") var badCharError = errors.New("Decode a bad char") type charDecoder_sjis struct { } func (charDecoder_sjis) DecodeOneChar(input []byte) (c uint16, remain []byte, err error) { if len(input) == 0 { return 0, nil, eobError } first := input[0] c = uint16(first) remain = input[1:] if first <= 0x7F || (first > 0xA0 && first <= 0xDF) { return } if len(remain) == 0 { return c, remain, badCharError } second := remain[0] remain = remain[1:] c = c<<8 | uint16(second) if (second >= 0x40 && second <= 0x7F) || (second >= 0x80 && second <= 0xFE) { } else { err = badCharError } return } var commonChars_sjis = []uint16{ 0x8140, 0x8141, 0x8142, 0x8145, 0x815b, 0x8169, 0x816a, 0x8175, 0x8176, 0x82a0, 0x82a2, 0x82a4, 0x82a9, 0x82aa, 0x82ab, 0x82ad, 0x82af, 0x82b1, 0x82b3, 0x82b5, 0x82b7, 0x82bd, 0x82be, 0x82c1, 0x82c4, 0x82c5, 0x82c6, 0x82c8, 0x82c9, 0x82cc, 0x82cd, 0x82dc, 0x82e0, 0x82e7, 0x82e8, 0x82e9, 0x82ea, 0x82f0, 0x82f1, 0x8341, 0x8343, 0x834e, 0x834f, 0x8358, 0x835e, 0x8362, 0x8367, 0x8375, 0x8376, 0x8389, 0x838a, 0x838b, 0x838d, 0x8393, 0x8e96, 0x93fa, 0x95aa, } func newRecognizer_sjis() *recognizerMultiByte { return &recognizerMultiByte{ "Shift_JIS", "ja", charDecoder_sjis{}, commonChars_sjis, } } type charDecoder_euc struct { } func (charDecoder_euc) DecodeOneChar(input []byte) (c uint16, remain []byte, err error) { if len(input) == 0 { return 0, nil, eobError } first := input[0] remain = input[1:] c = uint16(first) if first <= 0x8D { return uint16(first), remain, nil } if len(remain) == 0 { return 0, nil, eobError } second := remain[0] remain = remain[1:] c = c<<8 | uint16(second) if first >= 0xA1 && first <= 0xFE { if second < 0xA1 { err = badCharError } return } if first == 0x8E { if second < 0xA1 { err = badCharError } return } if first == 0x8F { if len(remain) == 0 { return 0, nil, eobError } third := remain[0] remain = remain[1:] c = c<<0 | uint16(third) if third < 0xa1 { err = badCharError } } return } var commonChars_euc_jp = []uint16{ 0xa1a1, 0xa1a2, 0xa1a3, 0xa1a6, 0xa1bc, 0xa1ca, 0xa1cb, 0xa1d6, 0xa1d7, 0xa4a2, 0xa4a4, 0xa4a6, 0xa4a8, 0xa4aa, 0xa4ab, 0xa4ac, 0xa4ad, 0xa4af, 0xa4b1, 0xa4b3, 0xa4b5, 0xa4b7, 0xa4b9, 0xa4bb, 0xa4bd, 0xa4bf, 0xa4c0, 0xa4c1, 0xa4c3, 0xa4c4, 0xa4c6, 0xa4c7, 0xa4c8, 0xa4c9, 0xa4ca, 0xa4cb, 0xa4ce, 0xa4cf, 0xa4d0, 0xa4de, 0xa4df, 0xa4e1, 0xa4e2, 0xa4e4, 0xa4e8, 0xa4e9, 0xa4ea, 0xa4eb, 0xa4ec, 0xa4ef, 0xa4f2, 0xa4f3, 0xa5a2, 0xa5a3, 0xa5a4, 0xa5a6, 0xa5a7, 0xa5aa, 0xa5ad, 0xa5af, 0xa5b0, 0xa5b3, 0xa5b5, 0xa5b7, 0xa5b8, 0xa5b9, 0xa5bf, 0xa5c3, 0xa5c6, 0xa5c7, 0xa5c8, 0xa5c9, 0xa5cb, 0xa5d0, 0xa5d5, 0xa5d6, 0xa5d7, 0xa5de, 0xa5e0, 0xa5e1, 0xa5e5, 0xa5e9, 0xa5ea, 0xa5eb, 0xa5ec, 0xa5ed, 0xa5f3, 0xb8a9, 0xb9d4, 0xbaee, 0xbbc8, 0xbef0, 0xbfb7, 0xc4ea, 0xc6fc, 0xc7bd, 0xcab8, 0xcaf3, 0xcbdc, 0xcdd1, } var commonChars_euc_kr = []uint16{ 0xb0a1, 0xb0b3, 0xb0c5, 0xb0cd, 0xb0d4, 0xb0e6, 0xb0ed, 0xb0f8, 0xb0fa, 0xb0fc, 0xb1b8, 0xb1b9, 0xb1c7, 0xb1d7, 0xb1e2, 0xb3aa, 0xb3bb, 0xb4c2, 0xb4cf, 0xb4d9, 0xb4eb, 0xb5a5, 0xb5b5, 0xb5bf, 0xb5c7, 0xb5e9, 0xb6f3, 0xb7af, 0xb7c2, 0xb7ce, 0xb8a6, 0xb8ae, 0xb8b6, 0xb8b8, 0xb8bb, 0xb8e9, 0xb9ab, 0xb9ae, 0xb9cc, 0xb9ce, 0xb9fd, 0xbab8, 0xbace, 0xbad0, 0xbaf1, 0xbbe7, 0xbbf3, 0xbbfd, 0xbcad, 0xbcba, 0xbcd2, 0xbcf6, 0xbdba, 0xbdc0, 0xbdc3, 0xbdc5, 0xbec6, 0xbec8, 0xbedf, 0xbeee, 0xbef8, 0xbefa, 0xbfa1, 0xbfa9, 0xbfc0, 0xbfe4, 0xbfeb, 0xbfec, 0xbff8, 0xc0a7, 0xc0af, 0xc0b8, 0xc0ba, 0xc0bb, 0xc0bd, 0xc0c7, 0xc0cc, 0xc0ce, 0xc0cf, 0xc0d6, 0xc0da, 0xc0e5, 0xc0fb, 0xc0fc, 0xc1a4, 0xc1a6, 0xc1b6, 0xc1d6, 0xc1df, 0xc1f6, 0xc1f8, 0xc4a1, 0xc5cd, 0xc6ae, 0xc7cf, 0xc7d1, 0xc7d2, 0xc7d8, 0xc7e5, 0xc8ad, } func newRecognizer_euc_jp() *recognizerMultiByte { return &recognizerMultiByte{ "EUC-JP", "ja", charDecoder_euc{}, commonChars_euc_jp, } } func newRecognizer_euc_kr() *recognizerMultiByte { return &recognizerMultiByte{ "EUC-KR", "ko", charDecoder_euc{}, commonChars_euc_kr, } } type charDecoder_big5 struct { } func (charDecoder_big5) DecodeOneChar(input []byte) (c uint16, remain []byte, err error) { if len(input) == 0 { return 0, nil, eobError } first := input[0] remain = input[1:] c = uint16(first) if first <= 0x7F || first == 0xFF { return } if len(remain) == 0 { return c, nil, eobError } second := remain[0] remain = remain[1:] c = c<<8 | uint16(second) if second < 0x40 || second == 0x7F || second == 0xFF { err = badCharError } return } var commonChars_big5 = []uint16{ 0xa140, 0xa141, 0xa142, 0xa143, 0xa147, 0xa149, 0xa175, 0xa176, 0xa440, 0xa446, 0xa447, 0xa448, 0xa451, 0xa454, 0xa457, 0xa464, 0xa46a, 0xa46c, 0xa477, 0xa4a3, 0xa4a4, 0xa4a7, 0xa4c1, 0xa4ce, 0xa4d1, 0xa4df, 0xa4e8, 0xa4fd, 0xa540, 0xa548, 0xa558, 0xa569, 0xa5cd, 0xa5e7, 0xa657, 0xa661, 0xa662, 0xa668, 0xa670, 0xa6a8, 0xa6b3, 0xa6b9, 0xa6d3, 0xa6db, 0xa6e6, 0xa6f2, 0xa740, 0xa751, 0xa759, 0xa7da, 0xa8a3, 0xa8a5, 0xa8ad, 0xa8d1, 0xa8d3, 0xa8e4, 0xa8fc, 0xa9c0, 0xa9d2, 0xa9f3, 0xaa6b, 0xaaba, 0xaabe, 0xaacc, 0xaafc, 0xac47, 0xac4f, 0xacb0, 0xacd2, 0xad59, 0xaec9, 0xafe0, 0xb0ea, 0xb16f, 0xb2b3, 0xb2c4, 0xb36f, 0xb44c, 0xb44e, 0xb54c, 0xb5a5, 0xb5bd, 0xb5d0, 0xb5d8, 0xb671, 0xb7ed, 0xb867, 0xb944, 0xbad8, 0xbb44, 0xbba1, 0xbdd1, 0xc2c4, 0xc3b9, 0xc440, 0xc45f, } func newRecognizer_big5() *recognizerMultiByte { return &recognizerMultiByte{ "Big5", "zh", charDecoder_big5{}, commonChars_big5, } } type charDecoder_gb_18030 struct { } func (charDecoder_gb_18030) DecodeOneChar(input []byte) (c uint16, remain []byte, err error) { if len(input) == 0 { return 0, nil, eobError } first := input[0] remain = input[1:] c = uint16(first) if first <= 0x80 { return } if len(remain) == 0 { return 0, nil, eobError } second := remain[0] remain = remain[1:] c = c<<8 | uint16(second) if first >= 0x81 && first <= 0xFE { if (second >= 0x40 && second <= 0x7E) || (second >= 0x80 && second <= 0xFE) { return } if second >= 0x30 && second <= 0x39 { if len(remain) == 0 { return 0, nil, eobError } third := remain[0] remain = remain[1:] if third >= 0x81 && third <= 0xFE { if len(remain) == 0 { return 0, nil, eobError } fourth := remain[0] remain = remain[1:] if fourth >= 0x30 && fourth <= 0x39 { c = c<<16 | uint16(third)<<8 | uint16(fourth) return } } } err = badCharError } return } var commonChars_gb_18030 = []uint16{ 0xa1a1, 0xa1a2, 0xa1a3, 0xa1a4, 0xa1b0, 0xa1b1, 0xa1f1, 0xa1f3, 0xa3a1, 0xa3ac, 0xa3ba, 0xb1a8, 0xb1b8, 0xb1be, 0xb2bb, 0xb3c9, 0xb3f6, 0xb4f3, 0xb5bd, 0xb5c4, 0xb5e3, 0xb6af, 0xb6d4, 0xb6e0, 0xb7a2, 0xb7a8, 0xb7bd, 0xb7d6, 0xb7dd, 0xb8b4, 0xb8df, 0xb8f6, 0xb9ab, 0xb9c9, 0xb9d8, 0xb9fa, 0xb9fd, 0xbacd, 0xbba7, 0xbbd6, 0xbbe1, 0xbbfa, 0xbcbc, 0xbcdb, 0xbcfe, 0xbdcc, 0xbecd, 0xbedd, 0xbfb4, 0xbfc6, 0xbfc9, 0xc0b4, 0xc0ed, 0xc1cb, 0xc2db, 0xc3c7, 0xc4dc, 0xc4ea, 0xc5cc, 0xc6f7, 0xc7f8, 0xc8ab, 0xc8cb, 0xc8d5, 0xc8e7, 0xc9cf, 0xc9fa, 0xcab1, 0xcab5, 0xcac7, 0xcad0, 0xcad6, 0xcaf5, 0xcafd, 0xccec, 0xcdf8, 0xceaa, 0xcec4, 0xced2, 0xcee5, 0xcfb5, 0xcfc2, 0xcfd6, 0xd0c2, 0xd0c5, 0xd0d0, 0xd0d4, 0xd1a7, 0xd2aa, 0xd2b2, 0xd2b5, 0xd2bb, 0xd2d4, 0xd3c3, 0xd3d0, 0xd3fd, 0xd4c2, 0xd4da, 0xd5e2, 0xd6d0, } func newRecognizer_gb_18030() *recognizerMultiByte { return &recognizerMultiByte{ "GB-18030", "zh", charDecoder_gb_18030{}, commonChars_gb_18030, } } ================================================ FILE: vendor/github.com/saintfish/chardet/recognizer.go ================================================ package chardet type recognizer interface { Match(*recognizerInput) recognizerOutput } type recognizerOutput Result type recognizerInput struct { raw []byte input []byte tagStripped bool byteStats []int hasC1Bytes bool } func newRecognizerInput(raw []byte, stripTag bool) *recognizerInput { input, stripped := mayStripInput(raw, stripTag) byteStats := computeByteStats(input) return &recognizerInput{ raw: raw, input: input, tagStripped: stripped, byteStats: byteStats, hasC1Bytes: computeHasC1Bytes(byteStats), } } func mayStripInput(raw []byte, stripTag bool) (out []byte, stripped bool) { const inputBufferSize = 8192 out = make([]byte, 0, inputBufferSize) var badTags, openTags int32 var inMarkup bool = false stripped = false if stripTag { stripped = true for _, c := range raw { if c == '<' { if inMarkup { badTags += 1 } inMarkup = true openTags += 1 } if !inMarkup { out = append(out, c) if len(out) >= inputBufferSize { break } } if c == '>' { inMarkup = false } } } if openTags < 5 || openTags/5 < badTags || (len(out) < 100 && len(raw) > 600) { limit := len(raw) if limit > inputBufferSize { limit = inputBufferSize } out = make([]byte, limit) copy(out, raw[:limit]) stripped = false } return } func computeByteStats(input []byte) []int { r := make([]int, 256) for _, c := range input { r[c] += 1 } return r } func computeHasC1Bytes(byteStats []int) bool { for _, count := range byteStats[0x80 : 0x9F+1] { if count > 0 { return true } } return false } ================================================ FILE: vendor/github.com/saintfish/chardet/single_byte.go ================================================ package chardet // Recognizer for single byte charset family type recognizerSingleByte struct { charset string hasC1ByteCharset string language string charMap *[256]byte ngram *[64]uint32 } func (r *recognizerSingleByte) Match(input *recognizerInput) recognizerOutput { var charset string = r.charset if input.hasC1Bytes && len(r.hasC1ByteCharset) > 0 { charset = r.hasC1ByteCharset } return recognizerOutput{ Charset: charset, Language: r.language, Confidence: r.parseNgram(input.input), } } type ngramState struct { ngram uint32 ignoreSpace bool ngramCount, ngramHit uint32 table *[64]uint32 } func newNgramState(table *[64]uint32) *ngramState { return &ngramState{ ngram: 0, ignoreSpace: false, ngramCount: 0, ngramHit: 0, table: table, } } func (s *ngramState) AddByte(b byte) { const ngramMask = 0xFFFFFF if !(b == 0x20 && s.ignoreSpace) { s.ngram = ((s.ngram << 8) | uint32(b)) & ngramMask s.ignoreSpace = (s.ngram == 0x20) s.ngramCount++ if s.lookup() { s.ngramHit++ } } s.ignoreSpace = (b == 0x20) } func (s *ngramState) HitRate() float32 { if s.ngramCount == 0 { return 0 } return float32(s.ngramHit) / float32(s.ngramCount) } func (s *ngramState) lookup() bool { var index int if s.table[index+32] <= s.ngram { index += 32 } if s.table[index+16] <= s.ngram { index += 16 } if s.table[index+8] <= s.ngram { index += 8 } if s.table[index+4] <= s.ngram { index += 4 } if s.table[index+2] <= s.ngram { index += 2 } if s.table[index+1] <= s.ngram { index += 1 } if s.table[index] > s.ngram { index -= 1 } if index < 0 || s.table[index] != s.ngram { return false } return true } func (r *recognizerSingleByte) parseNgram(input []byte) int { state := newNgramState(r.ngram) for _, inChar := range input { c := r.charMap[inChar] if c != 0 { state.AddByte(c) } } state.AddByte(0x20) rate := state.HitRate() if rate > 0.33 { return 98 } return int(rate * 300) } var charMap_8859_1 = [256]byte{ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xAA, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xB5, 0x20, 0x20, 0x20, 0x20, 0xBA, 0x20, 0x20, 0x20, 0x20, 0x20, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0x20, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0x20, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, } var ngrams_8859_1_en = [64]uint32{ 0x206120, 0x20616E, 0x206265, 0x20636F, 0x20666F, 0x206861, 0x206865, 0x20696E, 0x206D61, 0x206F66, 0x207072, 0x207265, 0x207361, 0x207374, 0x207468, 0x20746F, 0x207768, 0x616964, 0x616C20, 0x616E20, 0x616E64, 0x617320, 0x617420, 0x617465, 0x617469, 0x642061, 0x642074, 0x652061, 0x652073, 0x652074, 0x656420, 0x656E74, 0x657220, 0x657320, 0x666F72, 0x686174, 0x686520, 0x686572, 0x696420, 0x696E20, 0x696E67, 0x696F6E, 0x697320, 0x6E2061, 0x6E2074, 0x6E6420, 0x6E6720, 0x6E7420, 0x6F6620, 0x6F6E20, 0x6F7220, 0x726520, 0x727320, 0x732061, 0x732074, 0x736169, 0x737420, 0x742074, 0x746572, 0x746861, 0x746865, 0x74696F, 0x746F20, 0x747320, } var ngrams_8859_1_da = [64]uint32{ 0x206166, 0x206174, 0x206465, 0x20656E, 0x206572, 0x20666F, 0x206861, 0x206920, 0x206D65, 0x206F67, 0x2070E5, 0x207369, 0x207374, 0x207469, 0x207669, 0x616620, 0x616E20, 0x616E64, 0x617220, 0x617420, 0x646520, 0x64656E, 0x646572, 0x646574, 0x652073, 0x656420, 0x656465, 0x656E20, 0x656E64, 0x657220, 0x657265, 0x657320, 0x657420, 0x666F72, 0x676520, 0x67656E, 0x676572, 0x696765, 0x696C20, 0x696E67, 0x6B6520, 0x6B6B65, 0x6C6572, 0x6C6967, 0x6C6C65, 0x6D6564, 0x6E6465, 0x6E6520, 0x6E6720, 0x6E6765, 0x6F6720, 0x6F6D20, 0x6F7220, 0x70E520, 0x722064, 0x722065, 0x722073, 0x726520, 0x737465, 0x742073, 0x746520, 0x746572, 0x74696C, 0x766572, } var ngrams_8859_1_de = [64]uint32{ 0x20616E, 0x206175, 0x206265, 0x206461, 0x206465, 0x206469, 0x206569, 0x206765, 0x206861, 0x20696E, 0x206D69, 0x207363, 0x207365, 0x20756E, 0x207665, 0x20766F, 0x207765, 0x207A75, 0x626572, 0x636820, 0x636865, 0x636874, 0x646173, 0x64656E, 0x646572, 0x646965, 0x652064, 0x652073, 0x65696E, 0x656974, 0x656E20, 0x657220, 0x657320, 0x67656E, 0x68656E, 0x687420, 0x696368, 0x696520, 0x696E20, 0x696E65, 0x697420, 0x6C6963, 0x6C6C65, 0x6E2061, 0x6E2064, 0x6E2073, 0x6E6420, 0x6E6465, 0x6E6520, 0x6E6720, 0x6E6765, 0x6E7465, 0x722064, 0x726465, 0x726569, 0x736368, 0x737465, 0x742064, 0x746520, 0x74656E, 0x746572, 0x756E64, 0x756E67, 0x766572, } var ngrams_8859_1_es = [64]uint32{ 0x206120, 0x206361, 0x20636F, 0x206465, 0x20656C, 0x20656E, 0x206573, 0x20696E, 0x206C61, 0x206C6F, 0x207061, 0x20706F, 0x207072, 0x207175, 0x207265, 0x207365, 0x20756E, 0x207920, 0x612063, 0x612064, 0x612065, 0x61206C, 0x612070, 0x616369, 0x61646F, 0x616C20, 0x617220, 0x617320, 0x6369F3, 0x636F6E, 0x646520, 0x64656C, 0x646F20, 0x652064, 0x652065, 0x65206C, 0x656C20, 0x656E20, 0x656E74, 0x657320, 0x657374, 0x69656E, 0x69F36E, 0x6C6120, 0x6C6F73, 0x6E2065, 0x6E7465, 0x6F2064, 0x6F2065, 0x6F6E20, 0x6F7220, 0x6F7320, 0x706172, 0x717565, 0x726120, 0x726573, 0x732064, 0x732065, 0x732070, 0x736520, 0x746520, 0x746F20, 0x756520, 0xF36E20, } var ngrams_8859_1_fr = [64]uint32{ 0x206175, 0x20636F, 0x206461, 0x206465, 0x206475, 0x20656E, 0x206574, 0x206C61, 0x206C65, 0x207061, 0x20706F, 0x207072, 0x207175, 0x207365, 0x20736F, 0x20756E, 0x20E020, 0x616E74, 0x617469, 0x636520, 0x636F6E, 0x646520, 0x646573, 0x647520, 0x652061, 0x652063, 0x652064, 0x652065, 0x65206C, 0x652070, 0x652073, 0x656E20, 0x656E74, 0x657220, 0x657320, 0x657420, 0x657572, 0x696F6E, 0x697320, 0x697420, 0x6C6120, 0x6C6520, 0x6C6573, 0x6D656E, 0x6E2064, 0x6E6520, 0x6E7320, 0x6E7420, 0x6F6E20, 0x6F6E74, 0x6F7572, 0x717565, 0x72206C, 0x726520, 0x732061, 0x732064, 0x732065, 0x73206C, 0x732070, 0x742064, 0x746520, 0x74696F, 0x756520, 0x757220, } var ngrams_8859_1_it = [64]uint32{ 0x20616C, 0x206368, 0x20636F, 0x206465, 0x206469, 0x206520, 0x20696C, 0x20696E, 0x206C61, 0x207065, 0x207072, 0x20756E, 0x612063, 0x612064, 0x612070, 0x612073, 0x61746F, 0x636865, 0x636F6E, 0x64656C, 0x646920, 0x652061, 0x652063, 0x652064, 0x652069, 0x65206C, 0x652070, 0x652073, 0x656C20, 0x656C6C, 0x656E74, 0x657220, 0x686520, 0x692061, 0x692063, 0x692064, 0x692073, 0x696120, 0x696C20, 0x696E20, 0x696F6E, 0x6C6120, 0x6C6520, 0x6C6920, 0x6C6C61, 0x6E6520, 0x6E6920, 0x6E6F20, 0x6E7465, 0x6F2061, 0x6F2064, 0x6F2069, 0x6F2073, 0x6F6E20, 0x6F6E65, 0x706572, 0x726120, 0x726520, 0x736920, 0x746120, 0x746520, 0x746920, 0x746F20, 0x7A696F, } var ngrams_8859_1_nl = [64]uint32{ 0x20616C, 0x206265, 0x206461, 0x206465, 0x206469, 0x206565, 0x20656E, 0x206765, 0x206865, 0x20696E, 0x206D61, 0x206D65, 0x206F70, 0x207465, 0x207661, 0x207665, 0x20766F, 0x207765, 0x207A69, 0x61616E, 0x616172, 0x616E20, 0x616E64, 0x617220, 0x617420, 0x636874, 0x646520, 0x64656E, 0x646572, 0x652062, 0x652076, 0x65656E, 0x656572, 0x656E20, 0x657220, 0x657273, 0x657420, 0x67656E, 0x686574, 0x696520, 0x696E20, 0x696E67, 0x697320, 0x6E2062, 0x6E2064, 0x6E2065, 0x6E2068, 0x6E206F, 0x6E2076, 0x6E6465, 0x6E6720, 0x6F6E64, 0x6F6F72, 0x6F7020, 0x6F7220, 0x736368, 0x737465, 0x742064, 0x746520, 0x74656E, 0x746572, 0x76616E, 0x766572, 0x766F6F, } var ngrams_8859_1_no = [64]uint32{ 0x206174, 0x206176, 0x206465, 0x20656E, 0x206572, 0x20666F, 0x206861, 0x206920, 0x206D65, 0x206F67, 0x2070E5, 0x207365, 0x20736B, 0x20736F, 0x207374, 0x207469, 0x207669, 0x20E520, 0x616E64, 0x617220, 0x617420, 0x646520, 0x64656E, 0x646574, 0x652073, 0x656420, 0x656E20, 0x656E65, 0x657220, 0x657265, 0x657420, 0x657474, 0x666F72, 0x67656E, 0x696B6B, 0x696C20, 0x696E67, 0x6B6520, 0x6B6B65, 0x6C6520, 0x6C6C65, 0x6D6564, 0x6D656E, 0x6E2073, 0x6E6520, 0x6E6720, 0x6E6765, 0x6E6E65, 0x6F6720, 0x6F6D20, 0x6F7220, 0x70E520, 0x722073, 0x726520, 0x736F6D, 0x737465, 0x742073, 0x746520, 0x74656E, 0x746572, 0x74696C, 0x747420, 0x747465, 0x766572, } var ngrams_8859_1_pt = [64]uint32{ 0x206120, 0x20636F, 0x206461, 0x206465, 0x20646F, 0x206520, 0x206573, 0x206D61, 0x206E6F, 0x206F20, 0x207061, 0x20706F, 0x207072, 0x207175, 0x207265, 0x207365, 0x20756D, 0x612061, 0x612063, 0x612064, 0x612070, 0x616465, 0x61646F, 0x616C20, 0x617220, 0x617261, 0x617320, 0x636F6D, 0x636F6E, 0x646120, 0x646520, 0x646F20, 0x646F73, 0x652061, 0x652064, 0x656D20, 0x656E74, 0x657320, 0x657374, 0x696120, 0x696361, 0x6D656E, 0x6E7465, 0x6E746F, 0x6F2061, 0x6F2063, 0x6F2064, 0x6F2065, 0x6F2070, 0x6F7320, 0x706172, 0x717565, 0x726120, 0x726573, 0x732061, 0x732064, 0x732065, 0x732070, 0x737461, 0x746520, 0x746F20, 0x756520, 0xE36F20, 0xE7E36F, } var ngrams_8859_1_sv = [64]uint32{ 0x206174, 0x206176, 0x206465, 0x20656E, 0x2066F6, 0x206861, 0x206920, 0x20696E, 0x206B6F, 0x206D65, 0x206F63, 0x2070E5, 0x20736B, 0x20736F, 0x207374, 0x207469, 0x207661, 0x207669, 0x20E472, 0x616465, 0x616E20, 0x616E64, 0x617220, 0x617474, 0x636820, 0x646520, 0x64656E, 0x646572, 0x646574, 0x656420, 0x656E20, 0x657220, 0x657420, 0x66F672, 0x67656E, 0x696C6C, 0x696E67, 0x6B6120, 0x6C6C20, 0x6D6564, 0x6E2073, 0x6E6120, 0x6E6465, 0x6E6720, 0x6E6765, 0x6E696E, 0x6F6368, 0x6F6D20, 0x6F6E20, 0x70E520, 0x722061, 0x722073, 0x726120, 0x736B61, 0x736F6D, 0x742073, 0x746120, 0x746520, 0x746572, 0x74696C, 0x747420, 0x766172, 0xE47220, 0xF67220, } func newRecognizer_8859_1(language string, ngram *[64]uint32) *recognizerSingleByte { return &recognizerSingleByte{ charset: "ISO-8859-1", hasC1ByteCharset: "windows-1252", language: language, charMap: &charMap_8859_1, ngram: ngram, } } func newRecognizer_8859_1_en() *recognizerSingleByte { return newRecognizer_8859_1("en", &ngrams_8859_1_en) } func newRecognizer_8859_1_da() *recognizerSingleByte { return newRecognizer_8859_1("da", &ngrams_8859_1_da) } func newRecognizer_8859_1_de() *recognizerSingleByte { return newRecognizer_8859_1("de", &ngrams_8859_1_de) } func newRecognizer_8859_1_es() *recognizerSingleByte { return newRecognizer_8859_1("es", &ngrams_8859_1_es) } func newRecognizer_8859_1_fr() *recognizerSingleByte { return newRecognizer_8859_1("fr", &ngrams_8859_1_fr) } func newRecognizer_8859_1_it() *recognizerSingleByte { return newRecognizer_8859_1("it", &ngrams_8859_1_it) } func newRecognizer_8859_1_nl() *recognizerSingleByte { return newRecognizer_8859_1("nl", &ngrams_8859_1_nl) } func newRecognizer_8859_1_no() *recognizerSingleByte { return newRecognizer_8859_1("no", &ngrams_8859_1_no) } func newRecognizer_8859_1_pt() *recognizerSingleByte { return newRecognizer_8859_1("pt", &ngrams_8859_1_pt) } func newRecognizer_8859_1_sv() *recognizerSingleByte { return newRecognizer_8859_1("sv", &ngrams_8859_1_sv) } var charMap_8859_2 = [256]byte{ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xB1, 0x20, 0xB3, 0x20, 0xB5, 0xB6, 0x20, 0x20, 0xB9, 0xBA, 0xBB, 0xBC, 0x20, 0xBE, 0xBF, 0x20, 0xB1, 0x20, 0xB3, 0x20, 0xB5, 0xB6, 0xB7, 0x20, 0xB9, 0xBA, 0xBB, 0xBC, 0x20, 0xBE, 0xBF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0x20, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0x20, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0x20, } var ngrams_8859_2_cs = [64]uint32{ 0x206120, 0x206279, 0x20646F, 0x206A65, 0x206E61, 0x206E65, 0x206F20, 0x206F64, 0x20706F, 0x207072, 0x2070F8, 0x20726F, 0x207365, 0x20736F, 0x207374, 0x20746F, 0x207620, 0x207679, 0x207A61, 0x612070, 0x636520, 0x636820, 0x652070, 0x652073, 0x652076, 0x656D20, 0x656EED, 0x686F20, 0x686F64, 0x697374, 0x6A6520, 0x6B7465, 0x6C6520, 0x6C6920, 0x6E6120, 0x6EE920, 0x6EEC20, 0x6EED20, 0x6F2070, 0x6F646E, 0x6F6A69, 0x6F7374, 0x6F7520, 0x6F7661, 0x706F64, 0x706F6A, 0x70726F, 0x70F865, 0x736520, 0x736F75, 0x737461, 0x737469, 0x73746E, 0x746572, 0x746EED, 0x746F20, 0x752070, 0xBE6520, 0xE16EED, 0xE9686F, 0xED2070, 0xED2073, 0xED6D20, 0xF86564, } var ngrams_8859_2_hu = [64]uint32{ 0x206120, 0x20617A, 0x206265, 0x206567, 0x20656C, 0x206665, 0x206861, 0x20686F, 0x206973, 0x206B65, 0x206B69, 0x206BF6, 0x206C65, 0x206D61, 0x206D65, 0x206D69, 0x206E65, 0x20737A, 0x207465, 0x20E973, 0x612061, 0x61206B, 0x61206D, 0x612073, 0x616B20, 0x616E20, 0x617A20, 0x62616E, 0x62656E, 0x656779, 0x656B20, 0x656C20, 0x656C65, 0x656D20, 0x656E20, 0x657265, 0x657420, 0x657465, 0x657474, 0x677920, 0x686F67, 0x696E74, 0x697320, 0x6B2061, 0x6BF67A, 0x6D6567, 0x6D696E, 0x6E2061, 0x6E616B, 0x6E656B, 0x6E656D, 0x6E7420, 0x6F6779, 0x732061, 0x737A65, 0x737A74, 0x737AE1, 0x73E967, 0x742061, 0x747420, 0x74E173, 0x7A6572, 0xE16E20, 0xE97320, } var ngrams_8859_2_pl = [64]uint32{ 0x20637A, 0x20646F, 0x206920, 0x206A65, 0x206B6F, 0x206D61, 0x206D69, 0x206E61, 0x206E69, 0x206F64, 0x20706F, 0x207072, 0x207369, 0x207720, 0x207769, 0x207779, 0x207A20, 0x207A61, 0x612070, 0x612077, 0x616E69, 0x636820, 0x637A65, 0x637A79, 0x646F20, 0x647A69, 0x652070, 0x652073, 0x652077, 0x65207A, 0x65676F, 0x656A20, 0x656D20, 0x656E69, 0x676F20, 0x696120, 0x696520, 0x69656A, 0x6B6120, 0x6B6920, 0x6B6965, 0x6D6965, 0x6E6120, 0x6E6961, 0x6E6965, 0x6F2070, 0x6F7761, 0x6F7769, 0x706F6C, 0x707261, 0x70726F, 0x70727A, 0x727A65, 0x727A79, 0x7369EA, 0x736B69, 0x737461, 0x776965, 0x796368, 0x796D20, 0x7A6520, 0x7A6965, 0x7A7920, 0xF37720, } var ngrams_8859_2_ro = [64]uint32{ 0x206120, 0x206163, 0x206361, 0x206365, 0x20636F, 0x206375, 0x206465, 0x206469, 0x206C61, 0x206D61, 0x207065, 0x207072, 0x207365, 0x2073E3, 0x20756E, 0x20BA69, 0x20EE6E, 0x612063, 0x612064, 0x617265, 0x617420, 0x617465, 0x617520, 0x636172, 0x636F6E, 0x637520, 0x63E320, 0x646520, 0x652061, 0x652063, 0x652064, 0x652070, 0x652073, 0x656120, 0x656920, 0x656C65, 0x656E74, 0x657374, 0x692061, 0x692063, 0x692064, 0x692070, 0x696520, 0x696920, 0x696E20, 0x6C6120, 0x6C6520, 0x6C6F72, 0x6C7569, 0x6E6520, 0x6E7472, 0x6F7220, 0x70656E, 0x726520, 0x726561, 0x727520, 0x73E320, 0x746520, 0x747275, 0x74E320, 0x756920, 0x756C20, 0xBA6920, 0xEE6E20, } func newRecognizer_8859_2(language string, ngram *[64]uint32) *recognizerSingleByte { return &recognizerSingleByte{ charset: "ISO-8859-2", hasC1ByteCharset: "windows-1250", language: language, charMap: &charMap_8859_2, ngram: ngram, } } func newRecognizer_8859_2_cs() *recognizerSingleByte { return newRecognizer_8859_1("cs", &ngrams_8859_2_cs) } func newRecognizer_8859_2_hu() *recognizerSingleByte { return newRecognizer_8859_1("hu", &ngrams_8859_2_hu) } func newRecognizer_8859_2_pl() *recognizerSingleByte { return newRecognizer_8859_1("pl", &ngrams_8859_2_pl) } func newRecognizer_8859_2_ro() *recognizerSingleByte { return newRecognizer_8859_1("ro", &ngrams_8859_2_ro) } var charMap_8859_5 = [256]byte{ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0x20, 0xFE, 0xFF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0x20, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0x20, 0xFE, 0xFF, } var ngrams_8859_5_ru = [64]uint32{ 0x20D220, 0x20D2DE, 0x20D4DE, 0x20D7D0, 0x20D820, 0x20DAD0, 0x20DADE, 0x20DDD0, 0x20DDD5, 0x20DED1, 0x20DFDE, 0x20DFE0, 0x20E0D0, 0x20E1DE, 0x20E1E2, 0x20E2DE, 0x20E7E2, 0x20EDE2, 0xD0DDD8, 0xD0E2EC, 0xD3DE20, 0xD5DBEC, 0xD5DDD8, 0xD5E1E2, 0xD5E220, 0xD820DF, 0xD8D520, 0xD8D820, 0xD8EF20, 0xDBD5DD, 0xDBD820, 0xDBECDD, 0xDDD020, 0xDDD520, 0xDDD8D5, 0xDDD8EF, 0xDDDE20, 0xDDDED2, 0xDE20D2, 0xDE20DF, 0xDE20E1, 0xDED220, 0xDED2D0, 0xDED3DE, 0xDED920, 0xDEDBEC, 0xDEDC20, 0xDEE1E2, 0xDFDEDB, 0xDFE0D5, 0xDFE0D8, 0xDFE0DE, 0xE0D0D2, 0xE0D5D4, 0xE1E2D0, 0xE1E2D2, 0xE1E2D8, 0xE1EF20, 0xE2D5DB, 0xE2DE20, 0xE2DEE0, 0xE2EC20, 0xE7E2DE, 0xEBE520, } func newRecognizer_8859_5(language string, ngram *[64]uint32) *recognizerSingleByte { return &recognizerSingleByte{ charset: "ISO-8859-5", language: language, charMap: &charMap_8859_5, ngram: ngram, } } func newRecognizer_8859_5_ru() *recognizerSingleByte { return newRecognizer_8859_5("ru", &ngrams_8859_5_ru) } var charMap_8859_6 = [256]byte{ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0x20, 0x20, 0x20, 0x20, 0x20, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, } var ngrams_8859_6_ar = [64]uint32{ 0x20C7E4, 0x20C7E6, 0x20C8C7, 0x20D9E4, 0x20E1EA, 0x20E4E4, 0x20E5E6, 0x20E8C7, 0xC720C7, 0xC7C120, 0xC7CA20, 0xC7D120, 0xC7E420, 0xC7E4C3, 0xC7E4C7, 0xC7E4C8, 0xC7E4CA, 0xC7E4CC, 0xC7E4CD, 0xC7E4CF, 0xC7E4D3, 0xC7E4D9, 0xC7E4E2, 0xC7E4E5, 0xC7E4E8, 0xC7E4EA, 0xC7E520, 0xC7E620, 0xC7E6CA, 0xC820C7, 0xC920C7, 0xC920E1, 0xC920E4, 0xC920E5, 0xC920E8, 0xCA20C7, 0xCF20C7, 0xCFC920, 0xD120C7, 0xD1C920, 0xD320C7, 0xD920C7, 0xD9E4E9, 0xE1EA20, 0xE420C7, 0xE4C920, 0xE4E920, 0xE4EA20, 0xE520C7, 0xE5C720, 0xE5C920, 0xE5E620, 0xE620C7, 0xE720C7, 0xE7C720, 0xE8C7E4, 0xE8E620, 0xE920C7, 0xEA20C7, 0xEA20E5, 0xEA20E8, 0xEAC920, 0xEAD120, 0xEAE620, } func newRecognizer_8859_6(language string, ngram *[64]uint32) *recognizerSingleByte { return &recognizerSingleByte{ charset: "ISO-8859-6", language: language, charMap: &charMap_8859_6, ngram: ngram, } } func newRecognizer_8859_6_ar() *recognizerSingleByte { return newRecognizer_8859_6("ar", &ngrams_8859_6_ar) } var charMap_8859_7 = [256]byte{ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xA1, 0xA2, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xDC, 0x20, 0xDD, 0xDE, 0xDF, 0x20, 0xFC, 0x20, 0xFD, 0xFE, 0xC0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0x20, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0x20, } var ngrams_8859_7_el = [64]uint32{ 0x20E1ED, 0x20E1F0, 0x20E3E9, 0x20E4E9, 0x20E5F0, 0x20E720, 0x20EAE1, 0x20ECE5, 0x20EDE1, 0x20EF20, 0x20F0E1, 0x20F0EF, 0x20F0F1, 0x20F3F4, 0x20F3F5, 0x20F4E7, 0x20F4EF, 0xDFE120, 0xE120E1, 0xE120F4, 0xE1E920, 0xE1ED20, 0xE1F0FC, 0xE1F220, 0xE3E9E1, 0xE5E920, 0xE5F220, 0xE720F4, 0xE7ED20, 0xE7F220, 0xE920F4, 0xE9E120, 0xE9EADE, 0xE9F220, 0xEAE1E9, 0xEAE1F4, 0xECE520, 0xED20E1, 0xED20E5, 0xED20F0, 0xEDE120, 0xEFF220, 0xEFF520, 0xF0EFF5, 0xF0F1EF, 0xF0FC20, 0xF220E1, 0xF220E5, 0xF220EA, 0xF220F0, 0xF220F4, 0xF3E520, 0xF3E720, 0xF3F4EF, 0xF4E120, 0xF4E1E9, 0xF4E7ED, 0xF4E7F2, 0xF4E9EA, 0xF4EF20, 0xF4EFF5, 0xF4F9ED, 0xF9ED20, 0xFEED20, } func newRecognizer_8859_7(language string, ngram *[64]uint32) *recognizerSingleByte { return &recognizerSingleByte{ charset: "ISO-8859-7", hasC1ByteCharset: "windows-1253", language: language, charMap: &charMap_8859_7, ngram: ngram, } } func newRecognizer_8859_7_el() *recognizerSingleByte { return newRecognizer_8859_7("el", &ngrams_8859_7_el) } var charMap_8859_8 = [256]byte{ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xB5, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0x20, 0x20, 0x20, 0x20, 0x20, } var ngrams_8859_8_I_he = [64]uint32{ 0x20E0E5, 0x20E0E7, 0x20E0E9, 0x20E0FA, 0x20E1E9, 0x20E1EE, 0x20E4E0, 0x20E4E5, 0x20E4E9, 0x20E4EE, 0x20E4F2, 0x20E4F9, 0x20E4FA, 0x20ECE0, 0x20ECE4, 0x20EEE0, 0x20F2EC, 0x20F9EC, 0xE0FA20, 0xE420E0, 0xE420E1, 0xE420E4, 0xE420EC, 0xE420EE, 0xE420F9, 0xE4E5E0, 0xE5E020, 0xE5ED20, 0xE5EF20, 0xE5F820, 0xE5FA20, 0xE920E4, 0xE9E420, 0xE9E5FA, 0xE9E9ED, 0xE9ED20, 0xE9EF20, 0xE9F820, 0xE9FA20, 0xEC20E0, 0xEC20E4, 0xECE020, 0xECE420, 0xED20E0, 0xED20E1, 0xED20E4, 0xED20EC, 0xED20EE, 0xED20F9, 0xEEE420, 0xEF20E4, 0xF0E420, 0xF0E920, 0xF0E9ED, 0xF2EC20, 0xF820E4, 0xF8E9ED, 0xF9EC20, 0xFA20E0, 0xFA20E1, 0xFA20E4, 0xFA20EC, 0xFA20EE, 0xFA20F9, } var ngrams_8859_8_he = [64]uint32{ 0x20E0E5, 0x20E0EC, 0x20E4E9, 0x20E4EC, 0x20E4EE, 0x20E4F0, 0x20E9F0, 0x20ECF2, 0x20ECF9, 0x20EDE5, 0x20EDE9, 0x20EFE5, 0x20EFE9, 0x20F8E5, 0x20F8E9, 0x20FAE0, 0x20FAE5, 0x20FAE9, 0xE020E4, 0xE020EC, 0xE020ED, 0xE020FA, 0xE0E420, 0xE0E5E4, 0xE0EC20, 0xE0EE20, 0xE120E4, 0xE120ED, 0xE120FA, 0xE420E4, 0xE420E9, 0xE420EC, 0xE420ED, 0xE420EF, 0xE420F8, 0xE420FA, 0xE4EC20, 0xE5E020, 0xE5E420, 0xE7E020, 0xE9E020, 0xE9E120, 0xE9E420, 0xEC20E4, 0xEC20ED, 0xEC20FA, 0xECF220, 0xECF920, 0xEDE9E9, 0xEDE9F0, 0xEDE9F8, 0xEE20E4, 0xEE20ED, 0xEE20FA, 0xEEE120, 0xEEE420, 0xF2E420, 0xF920E4, 0xF920ED, 0xF920FA, 0xF9E420, 0xFAE020, 0xFAE420, 0xFAE5E9, } func newRecognizer_8859_8(language string, ngram *[64]uint32) *recognizerSingleByte { return &recognizerSingleByte{ charset: "ISO-8859-8", hasC1ByteCharset: "windows-1255", language: language, charMap: &charMap_8859_8, ngram: ngram, } } func newRecognizer_8859_8_I_he() *recognizerSingleByte { r := newRecognizer_8859_8("he", &ngrams_8859_8_I_he) r.charset = "ISO-8859-8-I" return r } func newRecognizer_8859_8_he() *recognizerSingleByte { return newRecognizer_8859_8("he", &ngrams_8859_8_he) } var charMap_8859_9 = [256]byte{ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xAA, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xB5, 0x20, 0x20, 0x20, 0x20, 0xBA, 0x20, 0x20, 0x20, 0x20, 0x20, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0x20, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0x69, 0xFE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0x20, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, } var ngrams_8859_9_tr = [64]uint32{ 0x206261, 0x206269, 0x206275, 0x206461, 0x206465, 0x206765, 0x206861, 0x20696C, 0x206B61, 0x206B6F, 0x206D61, 0x206F6C, 0x207361, 0x207461, 0x207665, 0x207961, 0x612062, 0x616B20, 0x616C61, 0x616D61, 0x616E20, 0x616EFD, 0x617220, 0x617261, 0x6172FD, 0x6173FD, 0x617961, 0x626972, 0x646120, 0x646520, 0x646920, 0x652062, 0x65206B, 0x656469, 0x656E20, 0x657220, 0x657269, 0x657369, 0x696C65, 0x696E20, 0x696E69, 0x697220, 0x6C616E, 0x6C6172, 0x6C6520, 0x6C6572, 0x6E2061, 0x6E2062, 0x6E206B, 0x6E6461, 0x6E6465, 0x6E6520, 0x6E6920, 0x6E696E, 0x6EFD20, 0x72696E, 0x72FD6E, 0x766520, 0x796120, 0x796F72, 0xFD6E20, 0xFD6E64, 0xFD6EFD, 0xFDF0FD, } func newRecognizer_8859_9(language string, ngram *[64]uint32) *recognizerSingleByte { return &recognizerSingleByte{ charset: "ISO-8859-9", hasC1ByteCharset: "windows-1254", language: language, charMap: &charMap_8859_9, ngram: ngram, } } func newRecognizer_8859_9_tr() *recognizerSingleByte { return newRecognizer_8859_9("tr", &ngrams_8859_9_tr) } var charMap_windows_1256 = [256]byte{ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x81, 0x20, 0x83, 0x20, 0x20, 0x20, 0x20, 0x88, 0x20, 0x8A, 0x20, 0x9C, 0x8D, 0x8E, 0x8F, 0x90, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x98, 0x20, 0x9A, 0x20, 0x9C, 0x20, 0x20, 0x9F, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xAA, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xB5, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0x20, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0x20, 0x20, 0x20, 0x20, 0xF4, 0x20, 0x20, 0x20, 0x20, 0xF9, 0x20, 0xFB, 0xFC, 0x20, 0x20, 0xFF, } var ngrams_windows_1256 = [64]uint32{ 0x20C7E1, 0x20C7E4, 0x20C8C7, 0x20DAE1, 0x20DDED, 0x20E1E1, 0x20E3E4, 0x20E6C7, 0xC720C7, 0xC7C120, 0xC7CA20, 0xC7D120, 0xC7E120, 0xC7E1C3, 0xC7E1C7, 0xC7E1C8, 0xC7E1CA, 0xC7E1CC, 0xC7E1CD, 0xC7E1CF, 0xC7E1D3, 0xC7E1DA, 0xC7E1DE, 0xC7E1E3, 0xC7E1E6, 0xC7E1ED, 0xC7E320, 0xC7E420, 0xC7E4CA, 0xC820C7, 0xC920C7, 0xC920DD, 0xC920E1, 0xC920E3, 0xC920E6, 0xCA20C7, 0xCF20C7, 0xCFC920, 0xD120C7, 0xD1C920, 0xD320C7, 0xDA20C7, 0xDAE1EC, 0xDDED20, 0xE120C7, 0xE1C920, 0xE1EC20, 0xE1ED20, 0xE320C7, 0xE3C720, 0xE3C920, 0xE3E420, 0xE420C7, 0xE520C7, 0xE5C720, 0xE6C7E1, 0xE6E420, 0xEC20C7, 0xED20C7, 0xED20E3, 0xED20E6, 0xEDC920, 0xEDD120, 0xEDE420, } func newRecognizer_windows_1256() *recognizerSingleByte { return &recognizerSingleByte{ charset: "windows-1256", language: "ar", charMap: &charMap_windows_1256, ngram: &ngrams_windows_1256, } } var charMap_windows_1251 = [256]byte{ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x90, 0x83, 0x20, 0x83, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x9A, 0x20, 0x9C, 0x9D, 0x9E, 0x9F, 0x90, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x9A, 0x20, 0x9C, 0x9D, 0x9E, 0x9F, 0x20, 0xA2, 0xA2, 0xBC, 0x20, 0xB4, 0x20, 0x20, 0xB8, 0x20, 0xBA, 0x20, 0x20, 0x20, 0x20, 0xBF, 0x20, 0x20, 0xB3, 0xB3, 0xB4, 0xB5, 0x20, 0x20, 0xB8, 0x20, 0xBA, 0x20, 0xBC, 0xBE, 0xBE, 0xBF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, } var ngrams_windows_1251 = [64]uint32{ 0x20E220, 0x20E2EE, 0x20E4EE, 0x20E7E0, 0x20E820, 0x20EAE0, 0x20EAEE, 0x20EDE0, 0x20EDE5, 0x20EEE1, 0x20EFEE, 0x20EFF0, 0x20F0E0, 0x20F1EE, 0x20F1F2, 0x20F2EE, 0x20F7F2, 0x20FDF2, 0xE0EDE8, 0xE0F2FC, 0xE3EE20, 0xE5EBFC, 0xE5EDE8, 0xE5F1F2, 0xE5F220, 0xE820EF, 0xE8E520, 0xE8E820, 0xE8FF20, 0xEBE5ED, 0xEBE820, 0xEBFCED, 0xEDE020, 0xEDE520, 0xEDE8E5, 0xEDE8FF, 0xEDEE20, 0xEDEEE2, 0xEE20E2, 0xEE20EF, 0xEE20F1, 0xEEE220, 0xEEE2E0, 0xEEE3EE, 0xEEE920, 0xEEEBFC, 0xEEEC20, 0xEEF1F2, 0xEFEEEB, 0xEFF0E5, 0xEFF0E8, 0xEFF0EE, 0xF0E0E2, 0xF0E5E4, 0xF1F2E0, 0xF1F2E2, 0xF1F2E8, 0xF1FF20, 0xF2E5EB, 0xF2EE20, 0xF2EEF0, 0xF2FC20, 0xF7F2EE, 0xFBF520, } func newRecognizer_windows_1251() *recognizerSingleByte { return &recognizerSingleByte{ charset: "windows-1251", language: "ar", charMap: &charMap_windows_1251, ngram: &ngrams_windows_1251, } } var charMap_KOI8_R = [256]byte{ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xA3, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xA3, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, } var ngrams_KOI8_R = [64]uint32{ 0x20C4CF, 0x20C920, 0x20CBC1, 0x20CBCF, 0x20CEC1, 0x20CEC5, 0x20CFC2, 0x20D0CF, 0x20D0D2, 0x20D2C1, 0x20D3CF, 0x20D3D4, 0x20D4CF, 0x20D720, 0x20D7CF, 0x20DAC1, 0x20DCD4, 0x20DED4, 0xC1CEC9, 0xC1D4D8, 0xC5CCD8, 0xC5CEC9, 0xC5D3D4, 0xC5D420, 0xC7CF20, 0xC920D0, 0xC9C520, 0xC9C920, 0xC9D120, 0xCCC5CE, 0xCCC920, 0xCCD8CE, 0xCEC120, 0xCEC520, 0xCEC9C5, 0xCEC9D1, 0xCECF20, 0xCECFD7, 0xCF20D0, 0xCF20D3, 0xCF20D7, 0xCFC7CF, 0xCFCA20, 0xCFCCD8, 0xCFCD20, 0xCFD3D4, 0xCFD720, 0xCFD7C1, 0xD0CFCC, 0xD0D2C5, 0xD0D2C9, 0xD0D2CF, 0xD2C1D7, 0xD2C5C4, 0xD3D120, 0xD3D4C1, 0xD3D4C9, 0xD3D4D7, 0xD4C5CC, 0xD4CF20, 0xD4CFD2, 0xD4D820, 0xD9C820, 0xDED4CF, } func newRecognizer_KOI8_R() *recognizerSingleByte { return &recognizerSingleByte{ charset: "KOI8-R", language: "ru", charMap: &charMap_KOI8_R, ngram: &ngrams_KOI8_R, } } var charMap_IBM424_he = [256]byte{ /* -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -A -B -C -D -E -F */ /* 0- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 1- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 2- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 3- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 4- */ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 5- */ 0x40, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 6- */ 0x40, 0x40, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 7- */ 0x40, 0x71, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x40, 0x40, /* 8- */ 0x40, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 9- */ 0x40, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* A- */ 0xA0, 0x40, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* B- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* C- */ 0x40, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* D- */ 0x40, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* E- */ 0x40, 0x40, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* F- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, } var ngrams_IBM424_he_rtl = [64]uint32{ 0x404146, 0x404148, 0x404151, 0x404171, 0x404251, 0x404256, 0x404541, 0x404546, 0x404551, 0x404556, 0x404562, 0x404569, 0x404571, 0x405441, 0x405445, 0x405641, 0x406254, 0x406954, 0x417140, 0x454041, 0x454042, 0x454045, 0x454054, 0x454056, 0x454069, 0x454641, 0x464140, 0x465540, 0x465740, 0x466840, 0x467140, 0x514045, 0x514540, 0x514671, 0x515155, 0x515540, 0x515740, 0x516840, 0x517140, 0x544041, 0x544045, 0x544140, 0x544540, 0x554041, 0x554042, 0x554045, 0x554054, 0x554056, 0x554069, 0x564540, 0x574045, 0x584540, 0x585140, 0x585155, 0x625440, 0x684045, 0x685155, 0x695440, 0x714041, 0x714042, 0x714045, 0x714054, 0x714056, 0x714069, } var ngrams_IBM424_he_ltr = [64]uint32{ 0x404146, 0x404154, 0x404551, 0x404554, 0x404556, 0x404558, 0x405158, 0x405462, 0x405469, 0x405546, 0x405551, 0x405746, 0x405751, 0x406846, 0x406851, 0x407141, 0x407146, 0x407151, 0x414045, 0x414054, 0x414055, 0x414071, 0x414540, 0x414645, 0x415440, 0x415640, 0x424045, 0x424055, 0x424071, 0x454045, 0x454051, 0x454054, 0x454055, 0x454057, 0x454068, 0x454071, 0x455440, 0x464140, 0x464540, 0x484140, 0x514140, 0x514240, 0x514540, 0x544045, 0x544055, 0x544071, 0x546240, 0x546940, 0x555151, 0x555158, 0x555168, 0x564045, 0x564055, 0x564071, 0x564240, 0x564540, 0x624540, 0x694045, 0x694055, 0x694071, 0x694540, 0x714140, 0x714540, 0x714651, } func newRecognizer_IBM424_he(charset string, ngram *[64]uint32) *recognizerSingleByte { return &recognizerSingleByte{ charset: charset, language: "he", charMap: &charMap_IBM424_he, ngram: ngram, } } func newRecognizer_IBM424_he_rtl() *recognizerSingleByte { return newRecognizer_IBM424_he("IBM424_rtl", &ngrams_IBM424_he_rtl) } func newRecognizer_IBM424_he_ltr() *recognizerSingleByte { return newRecognizer_IBM424_he("IBM424_ltr", &ngrams_IBM424_he_ltr) } var charMap_IBM420_ar = [256]byte{ /* -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -A -B -C -D -E -F */ /* 0- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 1- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 2- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 3- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 4- */ 0x40, 0x40, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 5- */ 0x40, 0x51, 0x52, 0x40, 0x40, 0x55, 0x56, 0x57, 0x58, 0x59, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 6- */ 0x40, 0x40, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 7- */ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 8- */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, /* 9- */ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, /* A- */ 0xA0, 0x40, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, /* B- */ 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0x40, 0x40, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, /* C- */ 0x40, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x40, 0xCB, 0x40, 0xCD, 0x40, 0xCF, /* D- */ 0x40, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, /* E- */ 0x40, 0x40, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xEA, 0xEB, 0x40, 0xED, 0xEE, 0xEF, /* F- */ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xFB, 0xFC, 0xFD, 0xFE, 0x40, } var ngrams_IBM420_ar_rtl = [64]uint32{ 0x4056B1, 0x4056BD, 0x405856, 0x409AB1, 0x40ABDC, 0x40B1B1, 0x40BBBD, 0x40CF56, 0x564056, 0x564640, 0x566340, 0x567540, 0x56B140, 0x56B149, 0x56B156, 0x56B158, 0x56B163, 0x56B167, 0x56B169, 0x56B173, 0x56B178, 0x56B19A, 0x56B1AD, 0x56B1BB, 0x56B1CF, 0x56B1DC, 0x56BB40, 0x56BD40, 0x56BD63, 0x584056, 0x624056, 0x6240AB, 0x6240B1, 0x6240BB, 0x6240CF, 0x634056, 0x734056, 0x736240, 0x754056, 0x756240, 0x784056, 0x9A4056, 0x9AB1DA, 0xABDC40, 0xB14056, 0xB16240, 0xB1DA40, 0xB1DC40, 0xBB4056, 0xBB5640, 0xBB6240, 0xBBBD40, 0xBD4056, 0xBF4056, 0xBF5640, 0xCF56B1, 0xCFBD40, 0xDA4056, 0xDC4056, 0xDC40BB, 0xDC40CF, 0xDC6240, 0xDC7540, 0xDCBD40, } var ngrams_IBM420_ar_ltr = [64]uint32{ 0x404656, 0x4056BB, 0x4056BF, 0x406273, 0x406275, 0x4062B1, 0x4062BB, 0x4062DC, 0x406356, 0x407556, 0x4075DC, 0x40B156, 0x40BB56, 0x40BD56, 0x40BDBB, 0x40BDCF, 0x40BDDC, 0x40DAB1, 0x40DCAB, 0x40DCB1, 0x49B156, 0x564056, 0x564058, 0x564062, 0x564063, 0x564073, 0x564075, 0x564078, 0x56409A, 0x5640B1, 0x5640BB, 0x5640BD, 0x5640BF, 0x5640DA, 0x5640DC, 0x565840, 0x56B156, 0x56CF40, 0x58B156, 0x63B156, 0x63BD56, 0x67B156, 0x69B156, 0x73B156, 0x78B156, 0x9AB156, 0xAB4062, 0xADB156, 0xB14062, 0xB15640, 0xB156CF, 0xB19A40, 0xB1B140, 0xBB4062, 0xBB40DC, 0xBBB156, 0xBD5640, 0xBDBB40, 0xCF4062, 0xCF40DC, 0xCFB156, 0xDAB19A, 0xDCAB40, 0xDCB156, } func newRecognizer_IBM420_ar(charset string, ngram *[64]uint32) *recognizerSingleByte { return &recognizerSingleByte{ charset: charset, language: "ar", charMap: &charMap_IBM420_ar, ngram: ngram, } } func newRecognizer_IBM420_ar_rtl() *recognizerSingleByte { return newRecognizer_IBM420_ar("IBM420_rtl", &ngrams_IBM420_ar_rtl) } func newRecognizer_IBM420_ar_ltr() *recognizerSingleByte { return newRecognizer_IBM420_ar("IBM420_ltr", &ngrams_IBM420_ar_ltr) } ================================================ FILE: vendor/github.com/saintfish/chardet/unicode.go ================================================ package chardet import ( "bytes" ) var ( utf16beBom = []byte{0xFE, 0xFF} utf16leBom = []byte{0xFF, 0xFE} utf32beBom = []byte{0x00, 0x00, 0xFE, 0xFF} utf32leBom = []byte{0xFF, 0xFE, 0x00, 0x00} ) type recognizerUtf16be struct { } func newRecognizer_utf16be() *recognizerUtf16be { return &recognizerUtf16be{} } func (*recognizerUtf16be) Match(input *recognizerInput) (output recognizerOutput) { output = recognizerOutput{ Charset: "UTF-16BE", } if bytes.HasPrefix(input.raw, utf16beBom) { output.Confidence = 100 } return } type recognizerUtf16le struct { } func newRecognizer_utf16le() *recognizerUtf16le { return &recognizerUtf16le{} } func (*recognizerUtf16le) Match(input *recognizerInput) (output recognizerOutput) { output = recognizerOutput{ Charset: "UTF-16LE", } if bytes.HasPrefix(input.raw, utf16leBom) && !bytes.HasPrefix(input.raw, utf32leBom) { output.Confidence = 100 } return } type recognizerUtf32 struct { name string bom []byte decodeChar func(input []byte) uint32 } func decodeUtf32be(input []byte) uint32 { return uint32(input[0])<<24 | uint32(input[1])<<16 | uint32(input[2])<<8 | uint32(input[3]) } func decodeUtf32le(input []byte) uint32 { return uint32(input[3])<<24 | uint32(input[2])<<16 | uint32(input[1])<<8 | uint32(input[0]) } func newRecognizer_utf32be() *recognizerUtf32 { return &recognizerUtf32{ "UTF-32BE", utf32beBom, decodeUtf32be, } } func newRecognizer_utf32le() *recognizerUtf32 { return &recognizerUtf32{ "UTF-32LE", utf32leBom, decodeUtf32le, } } func (r *recognizerUtf32) Match(input *recognizerInput) (output recognizerOutput) { output = recognizerOutput{ Charset: r.name, } hasBom := bytes.HasPrefix(input.raw, r.bom) var numValid, numInvalid uint32 for b := input.raw; len(b) >= 4; b = b[4:] { if c := r.decodeChar(b); c >= 0x10FFFF || (c >= 0xD800 && c <= 0xDFFF) { numInvalid++ } else { numValid++ } } if hasBom && numInvalid == 0 { output.Confidence = 100 } else if hasBom && numValid > numInvalid*10 { output.Confidence = 80 } else if numValid > 3 && numInvalid == 0 { output.Confidence = 100 } else if numValid > 0 && numInvalid == 0 { output.Confidence = 80 } else if numValid > numInvalid*10 { output.Confidence = 25 } return } ================================================ FILE: vendor/github.com/saintfish/chardet/utf8.go ================================================ package chardet import ( "bytes" ) var utf8Bom = []byte{0xEF, 0xBB, 0xBF} type recognizerUtf8 struct { } func newRecognizer_utf8() *recognizerUtf8 { return &recognizerUtf8{} } func (*recognizerUtf8) Match(input *recognizerInput) (output recognizerOutput) { output = recognizerOutput{ Charset: "UTF-8", } hasBom := bytes.HasPrefix(input.raw, utf8Bom) inputLen := len(input.raw) var numValid, numInvalid uint32 var trailBytes uint8 for i := 0; i < inputLen; i++ { c := input.raw[i] if c&0x80 == 0 { continue } if c&0xE0 == 0xC0 { trailBytes = 1 } else if c&0xF0 == 0xE0 { trailBytes = 2 } else if c&0xF8 == 0xF0 { trailBytes = 3 } else { numInvalid++ if numInvalid > 5 { break } trailBytes = 0 } for i++; i < inputLen; i++ { c = input.raw[i] if c&0xC0 != 0x80 { numInvalid++ break } if trailBytes--; trailBytes == 0 { numValid++ break } } } if hasBom && numInvalid == 0 { output.Confidence = 100 } else if hasBom && numValid > numInvalid*10 { output.Confidence = 80 } else if numValid > 3 && numInvalid == 0 { output.Confidence = 100 } else if numValid > 0 && numInvalid == 0 { output.Confidence = 80 } else if numValid == 0 && numInvalid == 0 { // Plain ASCII output.Confidence = 10 } else if numValid > numInvalid*10 { output.Confidence = 25 } return } ================================================ FILE: vendor/github.com/slimtoolkit/go-update/.gitignore ================================================ .DS_Store ================================================ FILE: vendor/github.com/slimtoolkit/go-update/LICENSE ================================================ Copyright 2015 Alan Shreve Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/github.com/slimtoolkit/go-update/README.md ================================================ # go-update: Build self-updating Go programs [![godoc reference](https://godoc.org/github.com/docker-slim/go-update?status.png)](https://godoc.org/github.com/docker-slim/go-update) ## Note The forked version will fix a few minor bugs and add a few new features (using updates for installs when the executables are't there yet or updating with a file path instead of a reader interface) ## Original Package Info Package update provides functionality to implement secure, self-updating Go programs (or other single-file targets) A program can update itself by replacing its executable file with a new version. It provides the flexibility to implement different updating user experiences like auto-updating, or manual user-initiated updates. It also boasts advanced features like binary patching and code signing verification. Example of updating from a URL: ```go import ( "fmt" "net/http" "github.com/docker-slim/go-update" ) func doUpdate(url string) error { resp, err := http.Get(url) if err != nil { return err } defer resp.Body.Close() err := update.Apply(resp.Body, update.Options{}) if err != nil { // error handling } return err } ``` ### Features - Cross platform support (Windows too!) - Binary patch application - Checksum verification - Code signing verification - Support for updating arbitrary files ### API Compatibility Promises The master branch of `go-update` is *not* guaranteed to have a stable API over time. For any production application, you should vendor your dependency on `go-update` with a tool like git submodules, [gb](http://getgb.io/) or [govendor](https://github.com/kardianos/govendor). The `go-update` package makes the following promises about API compatibility: 1. A list of all API-breaking changes will be documented in this README. 1. `go-update` will strive for as few API-breaking changes as possible. ### License Apache ================================================ FILE: vendor/github.com/slimtoolkit/go-update/apply.go ================================================ package update import ( "bytes" "crypto" "crypto/x509" "encoding/pem" "errors" "fmt" "io" "io/ioutil" "os" "path/filepath" "github.com/slimtoolkit/go-update/internal/osext" ) var ( openFile = os.OpenFile ) // Apply performs an update of the current executable (or opts.TargetFile, if set) with the contents of the given io.Reader. // // Apply performs the following actions to ensure a safe cross-platform update: // // 1. If configured, applies the contents of the update io.Reader as a binary patch. // // 2. If configured, computes the checksum of the new executable and verifies it matches. // // 3. If configured, verifies the signature with a public key. // // 4. Creates a new file, /path/to/.target.new with the TargetMode with the contents of the updated file // // 5. Renames /path/to/target to /path/to/.target.old // // 6. Renames /path/to/.target.new to /path/to/target // // 7. If the final rename is successful, deletes /path/to/.target.old, returns no error. On Windows, // the removal of /path/to/target.old always fails, so instead Apply hides the old file instead. // // 8. If the final rename fails, attempts to roll back by renaming /path/to/.target.old // back to /path/to/target. // // If the roll back operation fails, the file system is left in an inconsistent state (betweet steps 5 and 6) where // there is no new executable file and the old executable file could not be be moved to its original location. In this // case you should notify the user of the bad news and ask them to recover manually. Applications can determine whether // the rollback failed by calling RollbackError, see the documentation on that function for additional detail. func Apply(update io.Reader, opts Options) error { // validate verify := false switch { case opts.Signature != nil && opts.PublicKey != nil: // okay verify = true case opts.Signature != nil: return errors.New("no public key to verify signature with") case opts.PublicKey != nil: return errors.New("No signature to verify with") } // set defaults if opts.Hash == 0 { opts.Hash = crypto.SHA256 } if opts.Verifier == nil { opts.Verifier = NewECDSAVerifier() } if opts.TargetMode == 0 { opts.TargetMode = 0755 } // get target path var err error opts.TargetPath, err = opts.getPath() if err != nil { return err } targetExists := false if _, err := os.Stat(opts.TargetPath); err == nil { targetExists = true } var newBytes []byte if opts.Patcher != nil { if newBytes, err = opts.applyPatch(update); err != nil { return err } } else { // no patch to apply, go on through if newBytes, err = ioutil.ReadAll(update); err != nil { return err } } // verify checksum if requested if opts.Checksum != nil { if err = opts.verifyChecksum(newBytes); err != nil { return err } } if verify { if err = opts.verifySignature(newBytes); err != nil { return err } } // get the directory the executable exists in updateDir := filepath.Dir(opts.TargetPath) filename := filepath.Base(opts.TargetPath) // Copy the contents of newbinary to a new executable file newPath := filepath.Join(updateDir, fmt.Sprintf(".%s.new", filename)) fp, err := openFile(newPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, opts.TargetMode) if err != nil { return err } defer fp.Close() _, err = io.Copy(fp, bytes.NewReader(newBytes)) if err != nil { return err } // if we don't call fp.Close(), windows won't let us move the new executable // because the file will still be "in use" fp.Close() // this is where we'll move the executable to so that we can swap in the updated replacement oldPath := opts.OldSavePath removeOld := opts.OldSavePath == "" if removeOld { oldPath = filepath.Join(updateDir, fmt.Sprintf(".%s.old", filename)) } // delete any existing old exec file - this is necessary on Windows for two reasons: // 1. after a successful update, Windows can't remove the .old file because the process is still running // 2. windows rename operations fail if the destination file already exists _ = os.Remove(oldPath) if targetExists { // move the existing executable to a new file in the same directory err = os.Rename(opts.TargetPath, oldPath) if err != nil { return err } } // move the new exectuable in to become the new program err = os.Rename(newPath, opts.TargetPath) if err != nil { // move unsuccessful // // The filesystem is now in a bad state. We have successfully // moved the existing binary to a new location, but we couldn't move the new // binary to take its place. That means there is no file where the current executable binary // used to be! // Try to rollback by restoring the old binary to its original path. if targetExists { rerr := os.Rename(oldPath, opts.TargetPath) if rerr != nil { return &rollbackErr{err, rerr} } } return err } // move successful, remove the old binary if needed if removeOld { errRemove := os.Remove(oldPath) // windows has trouble with removing old binaries, so hide it instead if errRemove != nil { _ = hideFile(oldPath) } } return nil } // RollbackError takes an error value returned by Apply and returns the error, if any, // that occurred when attempting to roll back from a failed update. Applications should // always call this function on any non-nil errors returned by Apply. // // If no rollback was needed or if the rollback was successful, RollbackError returns nil, // otherwise it returns the error encountered when trying to roll back. func RollbackError(err error) error { if err == nil { return nil } if rerr, ok := err.(*rollbackErr); ok { return rerr.rollbackErr } return nil } type rollbackErr struct { error // original error rollbackErr error // error encountered while rolling back } type Options struct { // TargetPath defines the path to the file to update. // The emptry string means 'the executable file of the running program'. TargetPath string // Create TargetPath replacement with this file mode. If zero, defaults to 0755. TargetMode os.FileMode // Checksum of the new binary to verify against. If nil, no checksum or signature verification is done. Checksum []byte // Public key to use for signature verification. If nil, no signature verification is done. PublicKey crypto.PublicKey // Signature to verify the updated file. If nil, no signature verification is done. Signature []byte // Pluggable signature verification algorithm. If nil, ECDSA is used. Verifier Verifier // Use this hash function to generate the checksum. If not set, SHA256 is used. Hash crypto.Hash // If nil, treat the update as a complete replacement for the contents of the file at TargetPath. // If non-nil, treat the update contents as a patch and use this object to apply the patch. Patcher Patcher // Store the old executable file at this path after a successful update. // The empty string means the old executable file will be removed after the update. OldSavePath string } // CheckPermissions determines whether the process has the correct permissions to // perform the requested update. If the update can proceed, it returns nil, otherwise // it returns the error that would occur if an update were attempted. func (o *Options) CheckPermissions() error { // get the directory the file exists in path, err := o.getPath() if err != nil { return err } fileDir := filepath.Dir(path) fileName := filepath.Base(path) // attempt to open a file in the file's directory newPath := filepath.Join(fileDir, fmt.Sprintf(".%s.new", fileName)) fp, err := openFile(newPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, o.TargetMode) if err != nil { return err } fp.Close() _ = os.Remove(newPath) return nil } // SetPublicKeyPEM is a convenience method to set the PublicKey property // used for checking a completed update's signature by parsing a // Public Key formatted as PEM data. func (o *Options) SetPublicKeyPEM(pembytes []byte) error { block, _ := pem.Decode(pembytes) if block == nil { return errors.New("couldn't parse PEM data") } pub, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { return err } o.PublicKey = pub return nil } func (o *Options) getPath() (string, error) { if o.TargetPath == "" { return osext.Executable() } else { return o.TargetPath, nil } } func (o *Options) applyPatch(patch io.Reader) ([]byte, error) { // open the file to patch old, err := os.Open(o.TargetPath) if err != nil { return nil, err } defer old.Close() // apply the patch var applied bytes.Buffer if err = o.Patcher.Patch(old, &applied, patch); err != nil { return nil, err } return applied.Bytes(), nil } func (o *Options) verifyChecksum(updated []byte) error { checksum, err := checksumFor(o.Hash, updated) if err != nil { return err } if !bytes.Equal(o.Checksum, checksum) { return fmt.Errorf("Updated file has wrong checksum. Expected: %x, got: %x", o.Checksum, checksum) } return nil } func (o *Options) verifySignature(updated []byte) error { checksum, err := checksumFor(o.Hash, updated) if err != nil { return err } return o.Verifier.VerifySignature(checksum, o.Signature, o.Hash, o.PublicKey) } func checksumFor(h crypto.Hash, payload []byte) ([]byte, error) { if !h.Available() { return nil, errors.New("requested hash function not available") } hash := h.New() hash.Write(payload) // guaranteed not to error return hash.Sum([]byte{}), nil } ================================================ FILE: vendor/github.com/slimtoolkit/go-update/doc.go ================================================ /* Package update provides functionality to implement secure, self-updating Go programs (or other single-file targets). For complete updating solutions please see Equinox (https://equinox.io) and go-tuf (https://github.com/flynn/go-tuf). Basic Example This example shows how to update a program remotely from a URL. import ( "fmt" "net/http" "github.com/docker-slim/go-update" ) func doUpdate(url string) error { // request the new file resp, err := http.Get(url) if err != nil { return err } defer resp.Body.Close() err := update.Apply(resp.Body, update.Options{}) if err != nil { if rerr := update.RollbackError(err); rerr != nil { fmt.Println("Failed to rollback from bad update: %v", rerr) } } return err } Binary Patching Go binaries can often be large. It can be advantageous to only ship a binary patch to a client instead of the complete program text of a new version. This example shows how to update a program with a bsdiff binary patch. Other patch formats may be applied by implementing the Patcher interface. import ( "encoding/hex" "io" "github.com/docker-slim/go-update" ) func updateWithPatch(patch io.Reader) error { err := update.Apply(patch, update.Options{ Patcher: update.NewBSDiffPatcher() }) if err != nil { // error handling } return err } Checksum Verification Updating executable code on a computer can be a dangerous operation unless you take the appropriate steps to guarantee the authenticity of the new code. While checksum verification is important, it should always be combined with signature verification (next section) to guarantee that the code came from a trusted party. go-update validates SHA256 checksums by default, but this is pluggable via the Hash property on the Options struct. This example shows how to guarantee that the newly-updated binary is verified to have an appropriate checksum (that was otherwise retrived via a secure channel) specified as a hex string. import ( "crypto" _ "crypto/sha256" "encoding/hex" "io" "github.com/docker-slim/go-update" ) func updateWithChecksum(binary io.Reader, hexChecksum string) error { checksum, err := hex.DecodeString(hexChecksum) if err != nil { return err } err = update.Apply(binary, update.Options{ Hash: crypto.SHA256, // this is the default, you don't need to specify it Checksum: checksum, }) if err != nil { // error handling } return err } Cryptographic Signature Verification Cryptographic verification of new code from an update is an extremely important way to guarantee the security and integrity of your updates. Verification is performed by validating the signature of a hash of the new file. This means nothing changes if you apply your update with a patch. This example shows how to add signature verification to your updates. To make all of this work an application distributor must first create a public/private key pair and embed the public key into their application. When they issue a new release, the issuer must sign the new executable file with the private key and distribute the signature along with the update. import ( "crypto" _ "crypto/sha256" "encoding/hex" "io" "github.com/docker-slim/go-update" ) var publicKey = []byte(` -----BEGIN PUBLIC KEY----- MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEtrVmBxQvheRArXjg2vG1xIprWGuCyESx MMY8pjmjepSy2kuz+nl9aFLqmr+rDNdYvEBqQaZrYMc6k29gjvoQnQ== -----END PUBLIC KEY----- `) func verifiedUpdate(binary io.Reader, hexChecksum, hexSignature string) { checksum, err := hex.DecodeString(hexChecksum) if err != nil { return err } signature, err := hex.DecodeString(hexSignature) if err != nil { return err } opts := update.Options{ Checksum: checksum, Signature: signature, Hash: crypto.SHA256, // this is the default, you don't need to specify it Verifier: update.NewECDSAVerifier(), // this is the default, you don't need to specify it } err = opts.SetPublicKeyPEM(publicKey) if err != nil { return err } err = update.Apply(binary, opts) if err != nil { // error handling } return err } Building Single-File Go Binaries In order to update a Go application with go-update, you must distributed it as a single executable. This is often easy, but some applications require static assets (like HTML and CSS asset files or TLS certificates). In order to update applications like these, you'll want to make sure to embed those asset files into the distributed binary with a tool like go-bindata (my favorite): https://github.com/jteeuwen/go-bindata Non-Goals Mechanisms and protocols for determining whether an update should be applied and, if so, which one are out of scope for this package. Please consult go-tuf (https://github.com/flynn/go-tuf) or Equinox (https://equinox.io) for more complete solutions. go-update only works for self-updating applications that are distributed as a single binary, i.e. applications that do not have additional assets or dependency files. Updating application that are distributed as mutliple on-disk files is out of scope, although this may change in future versions of this library. */ package update ================================================ FILE: vendor/github.com/slimtoolkit/go-update/hide_noop.go ================================================ // +build !windows package update func hideFile(path string) error { return nil } ================================================ FILE: vendor/github.com/slimtoolkit/go-update/hide_windows.go ================================================ package update import ( "syscall" "unsafe" ) func hideFile(path string) error { kernel32 := syscall.NewLazyDLL("kernel32.dll") setFileAttributes := kernel32.NewProc("SetFileAttributesW") r1, _, err := setFileAttributes.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(path))), 2) if r1 == 0 { return err } else { return nil } } ================================================ FILE: vendor/github.com/slimtoolkit/go-update/internal/binarydist/License ================================================ Copyright 2012 Keith Rarick Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/slimtoolkit/go-update/internal/binarydist/Readme.md ================================================ # binarydist Package binarydist implements binary diff and patch as described on . It reads and writes files compatible with the tools there. Documentation at . ================================================ FILE: vendor/github.com/slimtoolkit/go-update/internal/binarydist/bzip2.go ================================================ package binarydist import ( "io" "os/exec" ) type bzip2Writer struct { c *exec.Cmd w io.WriteCloser } func (w bzip2Writer) Write(b []byte) (int, error) { return w.w.Write(b) } func (w bzip2Writer) Close() error { if err := w.w.Close(); err != nil { return err } return w.c.Wait() } // Package compress/bzip2 implements only decompression, // so we'll fake it by running bzip2 in another process. func newBzip2Writer(w io.Writer) (wc io.WriteCloser, err error) { var bw bzip2Writer bw.c = exec.Command("bzip2", "-c") bw.c.Stdout = w if bw.w, err = bw.c.StdinPipe(); err != nil { return nil, err } if err = bw.c.Start(); err != nil { return nil, err } return bw, nil } ================================================ FILE: vendor/github.com/slimtoolkit/go-update/internal/binarydist/diff.go ================================================ package binarydist import ( "bytes" "encoding/binary" "io" "io/ioutil" ) func swap(a []int, i, j int) { a[i], a[j] = a[j], a[i] } func split(I, V []int, start, length, h int) { var i, j, k, x, jj, kk int if length < 16 { for k = start; k < start+length; k += j { j = 1 x = V[I[k]+h] for i = 1; k+i < start+length; i++ { if V[I[k+i]+h] < x { x = V[I[k+i]+h] j = 0 } if V[I[k+i]+h] == x { swap(I, k+i, k+j) j++ } } for i = 0; i < j; i++ { V[I[k+i]] = k + j - 1 } if j == 1 { I[k] = -1 } } return } x = V[I[start+length/2]+h] jj = 0 kk = 0 for i = start; i < start+length; i++ { if V[I[i]+h] < x { jj++ } if V[I[i]+h] == x { kk++ } } jj += start kk += jj i = start j = 0 k = 0 for i < jj { if V[I[i]+h] < x { i++ } else if V[I[i]+h] == x { swap(I, i, jj+j) j++ } else { swap(I, i, kk+k) k++ } } for jj+j < kk { if V[I[jj+j]+h] == x { j++ } else { swap(I, jj+j, kk+k) k++ } } if jj > start { split(I, V, start, jj-start, h) } for i = 0; i < kk-jj; i++ { V[I[jj+i]] = kk - 1 } if jj == kk-1 { I[jj] = -1 } if start+length > kk { split(I, V, kk, start+length-kk, h) } } func qsufsort(obuf []byte) []int { var buckets [256]int var i, h int I := make([]int, len(obuf)+1) V := make([]int, len(obuf)+1) for _, c := range obuf { buckets[c]++ } for i = 1; i < 256; i++ { buckets[i] += buckets[i-1] } copy(buckets[1:], buckets[:]) buckets[0] = 0 for i, c := range obuf { buckets[c]++ I[buckets[c]] = i } I[0] = len(obuf) for i, c := range obuf { V[i] = buckets[c] } V[len(obuf)] = 0 for i = 1; i < 256; i++ { if buckets[i] == buckets[i-1]+1 { I[buckets[i]] = -1 } } I[0] = -1 for h = 1; I[0] != -(len(obuf) + 1); h += h { var n int for i = 0; i < len(obuf)+1; { if I[i] < 0 { n -= I[i] i -= I[i] } else { if n != 0 { I[i-n] = -n } n = V[I[i]] + 1 - i split(I, V, i, n, h) i += n n = 0 } } if n != 0 { I[i-n] = -n } } for i = 0; i < len(obuf)+1; i++ { I[V[i]] = i } return I } func matchlen(a, b []byte) (i int) { for i < len(a) && i < len(b) && a[i] == b[i] { i++ } return i } func search(I []int, obuf, nbuf []byte, st, en int) (pos, n int) { if en-st < 2 { x := matchlen(obuf[I[st]:], nbuf) y := matchlen(obuf[I[en]:], nbuf) if x > y { return I[st], x } else { return I[en], y } } x := st + (en-st)/2 if bytes.Compare(obuf[I[x]:], nbuf) < 0 { return search(I, obuf, nbuf, x, en) } else { return search(I, obuf, nbuf, st, x) } panic("unreached") } // Diff computes the difference between old and new, according to the bsdiff // algorithm, and writes the result to patch. func Diff(old, new io.Reader, patch io.Writer) error { obuf, err := ioutil.ReadAll(old) if err != nil { return err } nbuf, err := ioutil.ReadAll(new) if err != nil { return err } pbuf, err := diffBytes(obuf, nbuf) if err != nil { return err } _, err = patch.Write(pbuf) return err } func diffBytes(obuf, nbuf []byte) ([]byte, error) { var patch seekBuffer err := diff(obuf, nbuf, &patch) if err != nil { return nil, err } return patch.buf, nil } func diff(obuf, nbuf []byte, patch io.WriteSeeker) error { var lenf int I := qsufsort(obuf) db := make([]byte, len(nbuf)) eb := make([]byte, len(nbuf)) var dblen, eblen int var hdr header hdr.Magic = magic hdr.NewSize = int64(len(nbuf)) err := binary.Write(patch, signMagLittleEndian{}, &hdr) if err != nil { return err } // Compute the differences, writing ctrl as we go pfbz2, err := newBzip2Writer(patch) if err != nil { return err } var scan, pos, length int var lastscan, lastpos, lastoffset int for scan < len(nbuf) { var oldscore int scan += length for scsc := scan; scan < len(nbuf); scan++ { pos, length = search(I, obuf, nbuf[scan:], 0, len(obuf)) for ; scsc < scan+length; scsc++ { if scsc+lastoffset < len(obuf) && obuf[scsc+lastoffset] == nbuf[scsc] { oldscore++ } } if (length == oldscore && length != 0) || length > oldscore+8 { break } if scan+lastoffset < len(obuf) && obuf[scan+lastoffset] == nbuf[scan] { oldscore-- } } if length != oldscore || scan == len(nbuf) { var s, Sf int lenf = 0 for i := 0; lastscan+i < scan && lastpos+i < len(obuf); { if obuf[lastpos+i] == nbuf[lastscan+i] { s++ } i++ if s*2-i > Sf*2-lenf { Sf = s lenf = i } } lenb := 0 if scan < len(nbuf) { var s, Sb int for i := 1; (scan >= lastscan+i) && (pos >= i); i++ { if obuf[pos-i] == nbuf[scan-i] { s++ } if s*2-i > Sb*2-lenb { Sb = s lenb = i } } } if lastscan+lenf > scan-lenb { overlap := (lastscan + lenf) - (scan - lenb) s := 0 Ss := 0 lens := 0 for i := 0; i < overlap; i++ { if nbuf[lastscan+lenf-overlap+i] == obuf[lastpos+lenf-overlap+i] { s++ } if nbuf[scan-lenb+i] == obuf[pos-lenb+i] { s-- } if s > Ss { Ss = s lens = i + 1 } } lenf += lens - overlap lenb -= lens } for i := 0; i < lenf; i++ { db[dblen+i] = nbuf[lastscan+i] - obuf[lastpos+i] } for i := 0; i < (scan-lenb)-(lastscan+lenf); i++ { eb[eblen+i] = nbuf[lastscan+lenf+i] } dblen += lenf eblen += (scan - lenb) - (lastscan + lenf) err = binary.Write(pfbz2, signMagLittleEndian{}, int64(lenf)) if err != nil { pfbz2.Close() return err } val := (scan - lenb) - (lastscan + lenf) err = binary.Write(pfbz2, signMagLittleEndian{}, int64(val)) if err != nil { pfbz2.Close() return err } val = (pos - lenb) - (lastpos + lenf) err = binary.Write(pfbz2, signMagLittleEndian{}, int64(val)) if err != nil { pfbz2.Close() return err } lastscan = scan - lenb lastpos = pos - lenb lastoffset = pos - scan } } err = pfbz2.Close() if err != nil { return err } // Compute size of compressed ctrl data l64, err := patch.Seek(0, 1) if err != nil { return err } hdr.CtrlLen = int64(l64 - 32) // Write compressed diff data pfbz2, err = newBzip2Writer(patch) if err != nil { return err } n, err := pfbz2.Write(db[:dblen]) if err != nil { pfbz2.Close() return err } if n != dblen { pfbz2.Close() return io.ErrShortWrite } err = pfbz2.Close() if err != nil { return err } // Compute size of compressed diff data n64, err := patch.Seek(0, 1) if err != nil { return err } hdr.DiffLen = n64 - l64 // Write compressed extra data pfbz2, err = newBzip2Writer(patch) if err != nil { return err } n, err = pfbz2.Write(eb[:eblen]) if err != nil { pfbz2.Close() return err } if n != eblen { pfbz2.Close() return io.ErrShortWrite } err = pfbz2.Close() if err != nil { return err } // Seek to the beginning, write the header, and close the file _, err = patch.Seek(0, 0) if err != nil { return err } err = binary.Write(patch, signMagLittleEndian{}, &hdr) if err != nil { return err } return nil } ================================================ FILE: vendor/github.com/slimtoolkit/go-update/internal/binarydist/doc.go ================================================ // Package binarydist implements binary diff and patch as described on // http://www.daemonology.net/bsdiff/. It reads and writes files // compatible with the tools there. package binarydist var magic = [8]byte{'B', 'S', 'D', 'I', 'F', 'F', '4', '0'} // File format: // 0 8 "BSDIFF40" // 8 8 X // 16 8 Y // 24 8 sizeof(newfile) // 32 X bzip2(control block) // 32+X Y bzip2(diff block) // 32+X+Y ??? bzip2(extra block) // with control block a set of triples (x,y,z) meaning "add x bytes // from oldfile to x bytes from the diff block; copy y bytes from the // extra block; seek forwards in oldfile by z bytes". type header struct { Magic [8]byte CtrlLen int64 DiffLen int64 NewSize int64 } ================================================ FILE: vendor/github.com/slimtoolkit/go-update/internal/binarydist/encoding.go ================================================ package binarydist // SignMagLittleEndian is the numeric encoding used by the bsdiff tools. // It implements binary.ByteOrder using a sign-magnitude format // and little-endian byte order. Only methods Uint64 and String // have been written; the rest panic. type signMagLittleEndian struct{} func (signMagLittleEndian) Uint16(b []byte) uint16 { panic("unimplemented") } func (signMagLittleEndian) PutUint16(b []byte, v uint16) { panic("unimplemented") } func (signMagLittleEndian) Uint32(b []byte) uint32 { panic("unimplemented") } func (signMagLittleEndian) PutUint32(b []byte, v uint32) { panic("unimplemented") } func (signMagLittleEndian) Uint64(b []byte) uint64 { y := int64(b[0]) | int64(b[1])<<8 | int64(b[2])<<16 | int64(b[3])<<24 | int64(b[4])<<32 | int64(b[5])<<40 | int64(b[6])<<48 | int64(b[7]&0x7f)<<56 if b[7]&0x80 != 0 { y = -y } return uint64(y) } func (signMagLittleEndian) PutUint64(b []byte, v uint64) { x := int64(v) neg := x < 0 if neg { x = -x } b[0] = byte(x) b[1] = byte(x >> 8) b[2] = byte(x >> 16) b[3] = byte(x >> 24) b[4] = byte(x >> 32) b[5] = byte(x >> 40) b[6] = byte(x >> 48) b[7] = byte(x >> 56) if neg { b[7] |= 0x80 } } func (signMagLittleEndian) String() string { return "signMagLittleEndian" } ================================================ FILE: vendor/github.com/slimtoolkit/go-update/internal/binarydist/patch.go ================================================ package binarydist import ( "bytes" "compress/bzip2" "encoding/binary" "errors" "io" "io/ioutil" ) var ErrCorrupt = errors.New("corrupt patch") // Patch applies patch to old, according to the bspatch algorithm, // and writes the result to new. func Patch(old io.Reader, new io.Writer, patch io.Reader) error { var hdr header err := binary.Read(patch, signMagLittleEndian{}, &hdr) if err != nil { return err } if hdr.Magic != magic { return ErrCorrupt } if hdr.CtrlLen < 0 || hdr.DiffLen < 0 || hdr.NewSize < 0 { return ErrCorrupt } ctrlbuf := make([]byte, hdr.CtrlLen) _, err = io.ReadFull(patch, ctrlbuf) if err != nil { return err } cpfbz2 := bzip2.NewReader(bytes.NewReader(ctrlbuf)) diffbuf := make([]byte, hdr.DiffLen) _, err = io.ReadFull(patch, diffbuf) if err != nil { return err } dpfbz2 := bzip2.NewReader(bytes.NewReader(diffbuf)) // The entire rest of the file is the extra block. epfbz2 := bzip2.NewReader(patch) obuf, err := ioutil.ReadAll(old) if err != nil { return err } nbuf := make([]byte, hdr.NewSize) var oldpos, newpos int64 for newpos < hdr.NewSize { var ctrl struct{ Add, Copy, Seek int64 } err = binary.Read(cpfbz2, signMagLittleEndian{}, &ctrl) if err != nil { return err } // Sanity-check if newpos+ctrl.Add > hdr.NewSize { return ErrCorrupt } // Read diff string _, err = io.ReadFull(dpfbz2, nbuf[newpos:newpos+ctrl.Add]) if err != nil { return ErrCorrupt } // Add old data to diff string for i := int64(0); i < ctrl.Add; i++ { if oldpos+i >= 0 && oldpos+i < int64(len(obuf)) { nbuf[newpos+i] += obuf[oldpos+i] } } // Adjust pointers newpos += ctrl.Add oldpos += ctrl.Add // Sanity-check if newpos+ctrl.Copy > hdr.NewSize { return ErrCorrupt } // Read extra string _, err = io.ReadFull(epfbz2, nbuf[newpos:newpos+ctrl.Copy]) if err != nil { return ErrCorrupt } // Adjust pointers newpos += ctrl.Copy oldpos += ctrl.Seek } // Write the new file for len(nbuf) > 0 { n, err := new.Write(nbuf) if err != nil { return err } nbuf = nbuf[n:] } return nil } ================================================ FILE: vendor/github.com/slimtoolkit/go-update/internal/binarydist/seek.go ================================================ package binarydist import ( "errors" ) type seekBuffer struct { buf []byte pos int } func (b *seekBuffer) Write(p []byte) (n int, err error) { n = copy(b.buf[b.pos:], p) if n == len(p) { b.pos += n return n, nil } b.buf = append(b.buf, p[n:]...) b.pos += len(p) return len(p), nil } func (b *seekBuffer) Seek(offset int64, whence int) (ret int64, err error) { var abs int64 switch whence { case 0: abs = offset case 1: abs = int64(b.pos) + offset case 2: abs = int64(len(b.buf)) + offset default: return 0, errors.New("binarydist: invalid whence") } if abs < 0 { return 0, errors.New("binarydist: negative position") } if abs >= 1<<31 { return 0, errors.New("binarydist: position out of range") } b.pos = int(abs) return abs, nil } ================================================ FILE: vendor/github.com/slimtoolkit/go-update/internal/osext/LICENSE ================================================ Copyright (c) 2012 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/slimtoolkit/go-update/internal/osext/README.md ================================================ ### Extensions to the "os" package. ## Find the current Executable and ExecutableFolder. There is sometimes utility in finding the current executable file that is running. This can be used for upgrading the current executable or finding resources located relative to the executable file. Both working directory and the os.Args[0] value are arbitrary and cannot be relied on; os.Args[0] can be "faked". Multi-platform and supports: * Linux * OS X * Windows * Plan 9 * BSDs. ================================================ FILE: vendor/github.com/slimtoolkit/go-update/internal/osext/osext.go ================================================ // Copyright 2012 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Extensions to the standard "os" package. package osext import "path/filepath" // Executable returns an absolute path that can be used to // re-invoke the current program. // It may not be valid after the current program exits. func Executable() (string, error) { p, err := executable() return filepath.Clean(p), err } // Returns same path as Executable, returns just the folder // path. Excludes the executable name and any trailing slash. func ExecutableFolder() (string, error) { p, err := Executable() if err != nil { return "", err } return filepath.Dir(p), nil } ================================================ FILE: vendor/github.com/slimtoolkit/go-update/internal/osext/osext_plan9.go ================================================ // Copyright 2012 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package osext import ( "os" "strconv" "syscall" ) func executable() (string, error) { f, err := os.Open("/proc/" + strconv.Itoa(os.Getpid()) + "/text") if err != nil { return "", err } defer f.Close() return syscall.Fd2path(int(f.Fd())) } ================================================ FILE: vendor/github.com/slimtoolkit/go-update/internal/osext/osext_procfs.go ================================================ // Copyright 2012 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build linux netbsd openbsd solaris dragonfly package osext import ( "errors" "fmt" "os" "runtime" "strings" ) func executable() (string, error) { switch runtime.GOOS { case "linux": const deletedTag = " (deleted)" execpath, err := os.Readlink("/proc/self/exe") if err != nil { return execpath, err } execpath = strings.TrimSuffix(execpath, deletedTag) execpath = strings.TrimPrefix(execpath, deletedTag) return execpath, nil case "netbsd": return os.Readlink("/proc/curproc/exe") case "openbsd", "dragonfly": return os.Readlink("/proc/curproc/file") case "solaris": return os.Readlink(fmt.Sprintf("/proc/%d/path/a.out", os.Getpid())) } return "", errors.New("ExecPath not implemented for " + runtime.GOOS) } ================================================ FILE: vendor/github.com/slimtoolkit/go-update/internal/osext/osext_sysctl.go ================================================ // Copyright 2012 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build darwin freebsd package osext import ( "os" "path/filepath" "runtime" "syscall" "unsafe" ) var initCwd, initCwdErr = os.Getwd() func executable() (string, error) { var mib [4]int32 switch runtime.GOOS { case "freebsd": mib = [4]int32{1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1} case "darwin": mib = [4]int32{1 /* CTL_KERN */, 38 /* KERN_PROCARGS */, int32(os.Getpid()), -1} } n := uintptr(0) // Get length. _, _, errNum := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, 0, uintptr(unsafe.Pointer(&n)), 0, 0) if errNum != 0 { return "", errNum } if n == 0 { // This shouldn't happen. return "", nil } buf := make([]byte, n) _, _, errNum = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0, 0) if errNum != 0 { return "", errNum } if n == 0 { // This shouldn't happen. return "", nil } for i, v := range buf { if v == 0 { buf = buf[:i] break } } var err error execPath := string(buf) // execPath will not be empty due to above checks. // Try to get the absolute path if the execPath is not rooted. if execPath[0] != '/' { execPath, err = getAbs(execPath) if err != nil { return execPath, err } } // For darwin KERN_PROCARGS may return the path to a symlink rather than the // actual executable. if runtime.GOOS == "darwin" { if execPath, err = filepath.EvalSymlinks(execPath); err != nil { return execPath, err } } return execPath, nil } func getAbs(execPath string) (string, error) { if initCwdErr != nil { return execPath, initCwdErr } // The execPath may begin with a "../" or a "./" so clean it first. // Join the two paths, trailing and starting slashes undetermined, so use // the generic Join function. return filepath.Join(initCwd, filepath.Clean(execPath)), nil } ================================================ FILE: vendor/github.com/slimtoolkit/go-update/internal/osext/osext_windows.go ================================================ // Copyright 2012 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package osext import ( "syscall" "unicode/utf16" "unsafe" ) var ( kernel = syscall.MustLoadDLL("kernel32.dll") getModuleFileNameProc = kernel.MustFindProc("GetModuleFileNameW") ) // GetModuleFileName() with hModule = NULL func executable() (exePath string, err error) { return getModuleFileName() } func getModuleFileName() (string, error) { var n uint32 b := make([]uint16, syscall.MAX_PATH) size := uint32(len(b)) r0, _, e1 := getModuleFileNameProc.Call(0, uintptr(unsafe.Pointer(&b[0])), uintptr(size)) n = uint32(r0) if n == 0 { return "", e1 } return string(utf16.Decode(b[0:n])), nil } ================================================ FILE: vendor/github.com/slimtoolkit/go-update/patcher.go ================================================ package update import ( "io" "github.com/slimtoolkit/go-update/internal/binarydist" ) // Patcher defines an interface for applying binary patches to an old item to get an updated item. type Patcher interface { Patch(old io.Reader, new io.Writer, patch io.Reader) error } type patchFn func(io.Reader, io.Writer, io.Reader) error func (fn patchFn) Patch(old io.Reader, new io.Writer, patch io.Reader) error { return fn(old, new, patch) } // NewBSDifferPatcher returns a new Patcher that applies binary patches using // the bsdiff algorithm. See http://www.daemonology.net/bsdiff/ func NewBSDiffPatcher() Patcher { return patchFn(binarydist.Patch) } ================================================ FILE: vendor/github.com/slimtoolkit/go-update/verifier.go ================================================ package update import ( "crypto" "crypto/dsa" "crypto/ecdsa" "crypto/rsa" "encoding/asn1" "errors" "math/big" ) // Verifier defines an interface for verfiying an update's signature with a public key. type Verifier interface { VerifySignature(checksum, signature []byte, h crypto.Hash, publicKey crypto.PublicKey) error } type verifyFn func([]byte, []byte, crypto.Hash, crypto.PublicKey) error func (fn verifyFn) VerifySignature(checksum []byte, signature []byte, hash crypto.Hash, publicKey crypto.PublicKey) error { return fn(checksum, signature, hash, publicKey) } // NewRSAVerifier returns a Verifier that uses the RSA algorithm to verify updates. func NewRSAVerifier() Verifier { return verifyFn(func(checksum, signature []byte, hash crypto.Hash, publicKey crypto.PublicKey) error { key, ok := publicKey.(*rsa.PublicKey) if !ok { return errors.New("not a valid RSA public key") } return rsa.VerifyPKCS1v15(key, hash, checksum, signature) }) } type rsDER struct { R *big.Int S *big.Int } // NewECDSAVerifier returns a Verifier that uses the ECDSA algorithm to verify updates. func NewECDSAVerifier() Verifier { return verifyFn(func(checksum, signature []byte, hash crypto.Hash, publicKey crypto.PublicKey) error { key, ok := publicKey.(*ecdsa.PublicKey) if !ok { return errors.New("not a valid ECDSA public key") } var rs rsDER if _, err := asn1.Unmarshal(signature, &rs); err != nil { return err } if !ecdsa.Verify(key, checksum, rs.R, rs.S) { return errors.New("failed to verify ecsda signature") } return nil }) } // NewDSAVerifier returns a Verifier that uses the DSA algorithm to verify updates. func NewDSAVerifier() Verifier { return verifyFn(func(checksum, signature []byte, hash crypto.Hash, publicKey crypto.PublicKey) error { key, ok := publicKey.(*dsa.PublicKey) if !ok { return errors.New("not a valid DSA public key") } var rs rsDER if _, err := asn1.Unmarshal(signature, &rs); err != nil { return err } if !dsa.Verify(key, checksum, rs.R, rs.S) { return errors.New("failed to verify ecsda signature") } return nil }) } ================================================ FILE: vendor/github.com/slimtoolkit/uilive/.travis.yml ================================================ language: go sudo: false install: - go get ./... go: - 1.4 - tip ================================================ FILE: vendor/github.com/slimtoolkit/uilive/LICENSE ================================================ MIT License =========== Copyright (c) 2015, Greg Osuri Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/slimtoolkit/uilive/Makefile ================================================ test: @go test -race . examples: @go run -race ./example .PHONY: test examples ================================================ FILE: vendor/github.com/slimtoolkit/uilive/README.md ================================================ # uilive [![GoDoc](https://godoc.org/github.com/gosuri/uilive?status.svg)](https://godoc.org/github.com/gosuri/uilive) [![Build Status](https://travis-ci.org/gosuri/uilive.svg?branch=master)](https://travis-ci.org/gosuri/uilive) uilive is a go library for updating terminal output in realtime. It provides a buffered [io.Writer](https://golang.org/pkg/io/#Writer) that is flushed at a timed interval. uilive powers [uiprogress](https://github.com/gosuri/uiprogress). ## Usage Example Calling `uilive.New()` will create a new writer. To start rendering, simply call `writer.Start()` and update the ui by writing to the `writer`. Full source for the below example is in [example/main.go](example/main.go). ```go writer := uilive.New() // start listening for updates and render writer.Start() for i := 0; i <= 100; i++ { fmt.Fprintf(writer, "Downloading.. (%d/%d) GB\n", i, 100) time.Sleep(time.Millisecond * 5) } fmt.Fprintln(writer, "Finished: Downloaded 100GB") writer.Stop() // flush and stop rendering ``` The above will render ![example](doc/example.gif) ## Installation ```sh $ go get -v github.com/gosuri/uilive ``` ================================================ FILE: vendor/github.com/slimtoolkit/uilive/doc.go ================================================ // Package uilive provides a writer that live updates the terminal. It provides a buffered io.Writer that is flushed at a timed interval. package uilive ================================================ FILE: vendor/github.com/slimtoolkit/uilive/terminal_size.go ================================================ package uilive import ( "os" "runtime" "syscall" "unsafe" ) type windowSize struct { rows uint16 cols uint16 xPixels uint16 yPixels uint16 } var out *os.File var err error var sz windowSize func getTermSize() (int, int) { if runtime.GOOS == "openbsd" { out, err = os.OpenFile("/dev/tty", os.O_RDWR, 0) if err != nil { os.Exit(1) } } else { out, err = os.OpenFile("/dev/tty", os.O_WRONLY, 0) if err != nil { os.Exit(1) } } _, _, _ = syscall.Syscall(syscall.SYS_IOCTL, out.Fd(), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&sz))) return int(sz.cols), int(sz.rows) } ================================================ FILE: vendor/github.com/slimtoolkit/uilive/writer.go ================================================ package uilive import ( "bytes" "errors" "io" "os" "sync" "time" ) // ESC is the ASCII code for escape character const ESC = 27 // RefreshInterval is the default refresh interval to update the ui var RefreshInterval = time.Millisecond var overFlowHandled bool var termWidth int // Out is the default output writer for the Writer var Out = os.Stdout // ErrClosedPipe is the error returned when trying to writer is not listening var ErrClosedPipe = errors.New("uilive: read/write on closed pipe") // FdWriter is a writer with a file descriptor. type FdWriter interface { io.Writer Fd() uintptr } // Writer is a buffered the writer that updates the terminal. The contents of writer will be flushed on a timed interval or when Flush is called. type Writer struct { // Out is the writer to write to Out io.Writer // RefreshInterval is the time the UI sould refresh RefreshInterval time.Duration ticker *time.Ticker tdone chan bool buf bytes.Buffer mtx *sync.Mutex lineCount int } type bypass struct { writer *Writer } type newline struct { writer *Writer } // New returns a new Writer with defaults func New() *Writer { termWidth, _ = getTermSize() if termWidth != 0 { overFlowHandled = true } return &Writer{ Out: Out, RefreshInterval: RefreshInterval, mtx: &sync.Mutex{}, } } // Flush writes to the out and resets the buffer. It should be called after the last call to Write to ensure that any data buffered in the Writer is written to output. // Any incomplete escape sequence at the end is considered complete for formatting purposes. // An error is returned if the contents of the buffer cannot be written to the underlying output stream func (w *Writer) Flush() error { w.mtx.Lock() defer w.mtx.Unlock() // do nothing if buffer is empty if len(w.buf.Bytes()) == 0 { return nil } w.clearLines() lines := 0 var currentLine bytes.Buffer for _, b := range w.buf.Bytes() { if b == '\n' { lines++ currentLine.Reset() } else { currentLine.Write([]byte{b}) if overFlowHandled && currentLine.Len() > termWidth { lines++ currentLine.Reset() } } } w.lineCount = lines _, err := w.Out.Write(w.buf.Bytes()) w.buf.Reset() return err } // Start starts the listener in a non-blocking manner func (w *Writer) Start() { if w.ticker == nil { w.ticker = time.NewTicker(w.RefreshInterval) w.tdone = make(chan bool, 1) } go w.Listen() } // Stop stops the listener that updates the terminal func (w *Writer) Stop() { w.Flush() close(w.tdone) } // Listen listens for updates to the writer's buffer and flushes to the out provided. It blocks the runtime. func (w *Writer) Listen() { for { select { case <-w.ticker.C: if w.ticker != nil { w.Flush() } case <-w.tdone: w.mtx.Lock() w.ticker.Stop() w.ticker = nil w.mtx.Unlock() return } } } // Write save the contents of buf to the writer b. The only errors returned are ones encountered while writing to the underlying buffer. func (w *Writer) Write(buf []byte) (n int, err error) { w.mtx.Lock() defer w.mtx.Unlock() return w.buf.Write(buf) } // Bypass creates an io.Writer which allows non-buffered output to be written to the underlying output func (w *Writer) Bypass() io.Writer { return &bypass{writer: w} } func (b *bypass) Write(p []byte) (int, error) { b.writer.mtx.Lock() defer b.writer.mtx.Unlock() b.writer.clearLines() b.writer.lineCount = 0 return b.writer.Out.Write(p) } // Newline creates an io.Writer which allows buffered output to be written to the underlying output. This enable writing // to multiple lines at once. func (w *Writer) Newline() io.Writer { return &newline{writer: w} } func (n *newline) Write(p []byte) (int, error) { n.writer.mtx.Lock() defer n.writer.mtx.Unlock() return n.writer.buf.Write(p) } ================================================ FILE: vendor/github.com/slimtoolkit/uilive/writer_posix.go ================================================ // +build !windows package uilive import ( "fmt" "strings" ) // clear the line and move the cursor up var clear = fmt.Sprintf("%c[%dA%c[2K", ESC, 1, ESC) func (w *Writer) clearLines() { _, _ = fmt.Fprint(w.Out, strings.Repeat(clear, w.lineCount)) } ================================================ FILE: vendor/github.com/slimtoolkit/uilive/writer_windows.go ================================================ // +build windows package uilive import ( "fmt" "syscall" "unsafe" ) var kernel32 = syscall.NewLazyDLL("kernel32.dll") var ( procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute") ) // clear the line and move the cursor up var clear = fmt.Sprintf("%c[%dA%c[2K", ESC, 1, ESC) type short int16 type dword uint32 type word uint16 type coord struct { x short y short } type smallRect struct { left short top short right short bottom short } type consoleScreenBufferInfo struct { size coord cursorPosition coord attributes word window smallRect maximumWindowSize coord } func (w *Writer) clearLines() { f, ok := w.Out.(FdWriter) if ok && !isatty.IsTerminal(f.Fd()) { ok = false } if !ok { _, _ = fmt.Fprint(w.Out, strings.Repeat(clear, w.lineCount)) return } fd := f.Fd() var csbi consoleScreenBufferInfo procGetConsoleScreenBufferInfo.Call(fd, uintptr(unsafe.Pointer(&csbi))) for i := 0; i < w.lineCount; i++ { // move the cursor up csbi.cursorPosition.y-- procSetConsoleCursorPosition.Call(fd, uintptr(*(*int32)(unsafe.Pointer(&csbi.cursorPosition)))) // clear the line cursor := coord{ x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y, } var count, w dword count = dword(csbi.size.x) procFillConsoleOutputCharacter.Call(fd, uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&w))) } } ================================================ FILE: vendor/github.com/slimtoolkit/uiprogress/.gitignore ================================================ .DS_Store /bin/ /_gopath/ ================================================ FILE: vendor/github.com/slimtoolkit/uiprogress/.travis.yml ================================================ language: go sudo: false install: - go get ./... go: - 1.4 - tip ================================================ FILE: vendor/github.com/slimtoolkit/uiprogress/LICENSE ================================================ MIT License =========== Copyright (c) 2015, Greg Osuri Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/slimtoolkit/uiprogress/Makefile ================================================ test: @go test -race . @go test -race ./util/strutil examples: go run -race example/full/full.go go run -race example/incr/incr.go go run -race example/multi/multi.go go run -race example/simple/simple.go .PHONY: test examples ================================================ FILE: vendor/github.com/slimtoolkit/uiprogress/README.md ================================================ # uiprogress [![GoDoc](https://godoc.org/github.com/docker-slim/uiprogress?status.svg)](https://godoc.org/github.com/docker-slim/uiprogress) [![Build Status](https://travis-ci.org/docker-slim/uiprogress.svg?branch=master)](https://travis-ci.org/docker-slim/uiprogress) ## Note Fixing the os.Exit problem in its dependency and a few other things... Enhancements: * No default progress bar ## Original Package Info A Go library to render progress bars in terminal applications. It provides a set of flexible features with a customizable API. ![example](doc/example_full.gif) Progress bars improve readability for terminal applications with long outputs by providing a concise feedback loop. ### Features * __Multiple Bars__: uiprogress can render multiple progress bars that can be tracked concurrently * __Dynamic Addition__: Add additional progress bars any time, even after the progress tracking has started * __Prepend and Append Functions__: Append or prepend completion percent and time elapsed to the progress bars * __Custom Decorator Functions__: Add custom functions around the bar along with helper functions ### Usage To start listening for progress bars, call `uiprogress.Start()` and add a progress bar using `uiprogress.AddBar(total int)`. Update the progress using `bar.Incr()` or `bar.Set(n int)`. Full source code for the below example is available at [example/simple/simple.go](example/simple/simple.go) ```go uiprogress.Start() // start rendering bar := uiprogress.AddBar(100) // Add a new bar // optionally, append and prepend completion and elapsed time bar.AppendCompleted() bar.PrependElapsed() for bar.Incr() { time.Sleep(time.Millisecond * 20) } ``` This will render the below in the terminal ![example](doc/example_simple.gif) #### Using Custom Decorators You can also add a custom decorator function in addition to default `bar.AppendCompleted()` and `bar.PrependElapsed()` decorators. The below example tracks the current step for an application deploy progress. Source code for the below example is available at [example/full/full.go](example/full/full.go) ```go var steps = []string{"downloading source", "installing deps", "compiling", "packaging", "seeding database", "deploying", "staring servers"} bar := uiprogress.AddBar(len(steps)) // prepend the current step to the bar bar.PrependFunc(func(b *uiprogress.Bar) string { return "app: " + steps[b.Current()-1] }) for bar.Incr() { time.Sleep(time.Millisecond * 10) } ``` #### Rendering Multiple bars You can add multiple bars using `uiprogress.AddBar(n)`. The below example demonstrates updating multiple bars concurrently and adding a new bar later in the pipeline. Source for this example is available at [example/multi/multi.go](example/multi/multi.go) ```go waitTime := time.Millisecond * 100 uiprogress.Start() // start the progress bars in go routines var wg sync.WaitGroup bar1 := uiprogress.AddBar(20).AppendCompleted().PrependElapsed() wg.Add(1) go func() { defer wg.Done() for bar1.Incr() { time.Sleep(waitTime) } }() bar2 := uiprogress.AddBar(40).AppendCompleted().PrependElapsed() wg.Add(1) go func() { defer wg.Done() for bar2.Incr() { time.Sleep(waitTime) } }() time.Sleep(time.Second) bar3 := uiprogress.AddBar(20).PrependElapsed().AppendCompleted() wg.Add(1) go func() { defer wg.Done() for i := 1; i <= bar3.Total; i++ { bar3.Set(i) time.Sleep(waitTime) } }() // wait for all the go routines to finish wg.Wait() ``` This will produce ![example](doc/example_multi.gif) #### `Incr` counter [Bar.Incr()](https://godoc.org/github.com/docker-slim/uiprogress#Bar.Incr) is an atomic counter and can be used as a general tracker, making it ideal for tracking progress of work fanned out to a lots of go routines. The source code for the below example is available at [example/incr/incr.go](example/incr/incr.go) ```go runtime.GOMAXPROCS(runtime.NumCPU()) // use all available cpu cores // create a new bar and prepend the task progress to the bar and fanout into 1k go routines count := 1000 bar := uiprogress.AddBar(count).AppendCompleted().PrependElapsed() bar.PrependFunc(func(b *uiprogress.Bar) string { return fmt.Sprintf("Task (%d/%d)", b.Current(), count) }) uiprogress.Start() var wg sync.WaitGroup // fanout into go routines for i := 0; i < count; i++ { wg.Add(1) go func() { defer wg.Done() time.Sleep(time.Millisecond * time.Duration(rand.Intn(500))) bar.Incr() }() } time.Sleep(time.Second) // wait for a second for all the go routines to finish wg.Wait() uiprogress.Stop() ``` ### Installation ```sh $ go get -v github.com/docker-slim/uiprogress ``` ### Todos - [ ] Resize bars and decorators by auto detecting window's dimensions - [ ] Handle more progress bars than vertical screen allows ### License uiprogress is released under the MIT License. See [LICENSE](https://github.com/docker-slim/uiprogress/blob/master/LICENSE). ================================================ FILE: vendor/github.com/slimtoolkit/uiprogress/bar.go ================================================ package uiprogress import ( "bytes" "errors" "fmt" "sync" "time" "github.com/slimtoolkit/uiprogress/util/strutil" ) var ( // Fill is the default character representing completed progress Fill byte = '=' // Head is the default character that moves when progress is updated Head byte = '>' // Empty is the default character that represents the empty progress Empty byte = '-' // LeftEnd is the default character in the left most part of the progress indicator LeftEnd byte = '[' // RightEnd is the default character in the right most part of the progress indicator RightEnd byte = ']' // Width is the default width of the progress bar Width = 70 // ErrMaxCurrentReached is error when trying to set current value that exceeds the total value ErrMaxCurrentReached = errors.New("errors: current value is greater total value") ) // Bar represents a progress bar type Bar struct { // Total of the total for the progress bar Total int // LeftEnd is character in the left most part of the progress indicator. Defaults to '[' LeftEnd byte // RightEnd is character in the right most part of the progress indicator. Defaults to ']' RightEnd byte // Fill is the character representing completed progress. Defaults to '=' Fill byte // Head is the character that moves when progress is updated. Defaults to '>' Head byte // Empty is the character that represents the empty progress. Default is '-' Empty byte // TimeStated is time progress began TimeStarted time.Time // Width is the width of the progress bar Width int // timeElased is the time elapsed for the progress timeElapsed time.Duration current int mtx *sync.RWMutex appendFuncs []DecoratorFunc prependFuncs []DecoratorFunc } // DecoratorFunc is a function that can be prepended and appended to the progress bar type DecoratorFunc func(b *Bar) string // NewBar returns a new progress bar func NewBar(total int) *Bar { return &Bar{ Total: total, Width: Width, LeftEnd: LeftEnd, RightEnd: RightEnd, Head: Head, Fill: Fill, Empty: Empty, mtx: &sync.RWMutex{}, } } // Set the current count of the bar. It returns ErrMaxCurrentReached when trying n exceeds the total value. This is atomic operation and concurancy safe. func (b *Bar) Set(n int) error { b.mtx.Lock() defer b.mtx.Unlock() if n > b.Total { return ErrMaxCurrentReached } b.current = n return nil } // Incr increments the current value by 1, time elapsed to current time and returns true. It returns false if the cursor has reached or exceeds total value. func (b *Bar) Incr() bool { b.mtx.Lock() defer b.mtx.Unlock() n := b.current + 1 if n > b.Total { return false } var t time.Time if b.TimeStarted == t { b.TimeStarted = time.Now() } b.timeElapsed = time.Since(b.TimeStarted) b.current = n return true } // Current returns the current progress of the bar func (b *Bar) Current() int { b.mtx.RLock() defer b.mtx.RUnlock() return b.current } // AppendFunc runs the decorator function and renders the output on the right of the progress bar func (b *Bar) AppendFunc(f DecoratorFunc) *Bar { b.mtx.Lock() defer b.mtx.Unlock() b.appendFuncs = append(b.appendFuncs, f) return b } // AppendCompleted appends the completion percent to the progress bar func (b *Bar) AppendCompleted() *Bar { b.AppendFunc(func(b *Bar) string { return b.CompletedPercentString() }) return b } // AppendElapsed appends the time elapsed the be progress bar func (b *Bar) AppendElapsed() *Bar { b.AppendFunc(func(b *Bar) string { return strutil.PadLeft(b.TimeElapsedString(), 5, ' ') }) return b } // PrependFunc runs decorator function and render the output left the progress bar func (b *Bar) PrependFunc(f DecoratorFunc) *Bar { b.mtx.Lock() defer b.mtx.Unlock() b.prependFuncs = append(b.prependFuncs, f) return b } // PrependCompleted prepends the precent completed to the progress bar func (b *Bar) PrependCompleted() *Bar { b.PrependFunc(func(b *Bar) string { return b.CompletedPercentString() }) return b } // PrependElapsed prepends the time elapsed to the begining of the bar func (b *Bar) PrependElapsed() *Bar { b.PrependFunc(func(b *Bar) string { return strutil.PadLeft(b.TimeElapsedString(), 5, ' ') }) return b } // Bytes returns the byte presentation of the progress bar func (b *Bar) Bytes() []byte { completedWidth := int(float64(b.Width) * (b.CompletedPercent() / 100.00)) // add fill and empty bits var buf bytes.Buffer for i := 0; i < completedWidth; i++ { buf.WriteByte(b.Fill) } for i := 0; i < b.Width-completedWidth; i++ { buf.WriteByte(b.Empty) } // set head bit pb := buf.Bytes() if completedWidth > 0 && completedWidth < b.Width { pb[completedWidth-1] = b.Head } // set left and right ends bits pb[0], pb[len(pb)-1] = b.LeftEnd, b.RightEnd // render append functions to the right of the bar for _, f := range b.appendFuncs { pb = append(pb, ' ') pb = append(pb, []byte(f(b))...) } // render prepend functions to the left of the bar for _, f := range b.prependFuncs { args := []byte(f(b)) args = append(args, ' ') pb = append(args, pb...) } return pb } // String returns the string representation of the bar func (b *Bar) String() string { return string(b.Bytes()) } // CompletedPercent return the percent completed func (b *Bar) CompletedPercent() float64 { return (float64(b.Current()) / float64(b.Total)) * 100.00 } // CompletedPercentString returns the formatted string representation of the completed percent func (b *Bar) CompletedPercentString() string { return fmt.Sprintf("%3.f%%", b.CompletedPercent()) } // TimeElapsed returns the time elapsed func (b *Bar) TimeElapsed() time.Duration { b.mtx.RLock() defer b.mtx.RUnlock() return b.timeElapsed } // TimeElapsedString returns the formatted string represenation of the time elapsed func (b *Bar) TimeElapsedString() string { return strutil.PrettyTime(b.TimeElapsed()) } ================================================ FILE: vendor/github.com/slimtoolkit/uiprogress/doc.go ================================================ // Package uiprogress is a library to render progress bars in terminal applications package uiprogress ================================================ FILE: vendor/github.com/slimtoolkit/uiprogress/progress.go ================================================ package uiprogress import ( "fmt" "io" "os" "sync" "time" "github.com/slimtoolkit/uilive" ) // Out is the default writer to render progress bars to var Out = os.Stdout // RefreshInterval in the default time duration to wait for refreshing the output var RefreshInterval = time.Millisecond * 10 // defaultProgress is the default progress <- removing //var defaultProgress = New() // Progress represents the container that renders progress bars type Progress struct { // Out is the writer to render progress bars to Out io.Writer // Width is the width of the progress bars Width int // Bars is the collection of progress bars Bars []*Bar // RefreshInterval in the time duration to wait for refreshing the output RefreshInterval time.Duration lw *uilive.Writer ticker *time.Ticker tdone chan bool mtx *sync.RWMutex } // New returns a new progress bar with defaults func New() *Progress { lw := uilive.New() if lw == nil { return nil } lw.Out = Out return &Progress{ Width: Width, Out: Out, Bars: make([]*Bar, 0), RefreshInterval: RefreshInterval, tdone: make(chan bool), lw: uilive.New(), mtx: &sync.RWMutex{}, } } //removing... // AddBar creates a new progress bar and adds it to the default progress container //func AddBar(total int) *Bar { // return defaultProgress.AddBar(total) //} //removing... // Start starts the rendering the progress of progress bars using the DefaultProgress. It listens for updates using `bar.Set(n)` and new bars when added using `AddBar` //func Start() { // defaultProgress.Start() //} //removing... // Stop stops listening //func Stop() { // defaultProgress.Stop() //} //removing... // Listen listens for updates and renders the progress bars //func Listen() { // defaultProgress.Listen() //} func (p *Progress) SetOut(o io.Writer) { p.mtx.Lock() defer p.mtx.Unlock() p.Out = o p.lw.Out = o } func (p *Progress) SetRefreshInterval(interval time.Duration) { p.mtx.Lock() defer p.mtx.Unlock() p.RefreshInterval = interval } // AddBar creates a new progress bar and adds to the container func (p *Progress) AddBar(total int) *Bar { p.mtx.Lock() defer p.mtx.Unlock() bar := NewBar(total) bar.Width = p.Width p.Bars = append(p.Bars, bar) return bar } // Listen listens for updates and renders the progress bars func (p *Progress) Listen() { for { p.mtx.Lock() interval := p.RefreshInterval p.mtx.Unlock() select { case <-time.After(interval): p.print() case <-p.tdone: p.print() close(p.tdone) return } } } func (p *Progress) print() { p.mtx.Lock() defer p.mtx.Unlock() for _, bar := range p.Bars { fmt.Fprintln(p.lw, bar.String()) } p.lw.Flush() } // Start starts the rendering the progress of progress bars. It listens for updates using `bar.Set(n)` and new bars when added using `AddBar` func (p *Progress) Start() { go p.Listen() } // Stop stops listening func (p *Progress) Stop() { p.tdone <- true <-p.tdone } // Bypass returns a writer which allows non-buffered data to be written to the underlying output func (p *Progress) Bypass() io.Writer { return p.lw.Bypass() } ================================================ FILE: vendor/github.com/slimtoolkit/uiprogress/util/strutil/strutil.go ================================================ // Package strutil provides various utilities for manipulating strings package strutil import ( "bytes" "time" ) // PadRight returns a new string of a specified length in which the end of the current string is padded with spaces or with a specified Unicode character. func PadRight(str string, length int, pad byte) string { if len(str) >= length { return str } buf := bytes.NewBufferString(str) for i := 0; i < length-len(str); i++ { buf.WriteByte(pad) } return buf.String() } // PadLeft returns a new string of a specified length in which the beginning of the current string is padded with spaces or with a specified Unicode character. func PadLeft(str string, length int, pad byte) string { if len(str) >= length { return str } var buf bytes.Buffer for i := 0; i < length-len(str); i++ { buf.WriteByte(pad) } buf.WriteString(str) return buf.String() } // Resize resizes the string with the given length. It ellipses with '...' when the string's length exceeds // the desired length or pads spaces to the right of the string when length is smaller than desired func Resize(s string, length uint) string { n := int(length) if len(s) == n { return s } // Pads only when length of the string smaller than len needed s = PadRight(s, n, ' ') if len(s) > n { b := []byte(s) var buf bytes.Buffer for i := 0; i < n-3; i++ { buf.WriteByte(b[i]) } buf.WriteString("...") s = buf.String() } return s } // PrettyTime returns the string representation of the duration. It rounds the time duration to a second and returns a "---" when duration is 0 func PrettyTime(t time.Duration) string { if t == 0 { return "---" } return (t - (t % time.Second)).String() } ================================================ FILE: vendor/github.com/spf13/cobra/.gitignore ================================================ # Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go # Vim files https://github.com/github/gitignore/blob/master/Global/Vim.gitignore # swap [._]*.s[a-w][a-z] [._]s[a-w][a-z] # session Session.vim # temporary .netrwhist *~ # auto-generated tag files tags *.exe cobra.test bin .idea/ *.iml ================================================ FILE: vendor/github.com/spf13/cobra/.golangci.yml ================================================ # Copyright 2013-2023 The Cobra Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. run: deadline: 5m linters: disable-all: true enable: #- bodyclose - deadcode #- depguard #- dogsled #- dupl - errcheck #- exhaustive #- funlen - gas #- gochecknoinits - goconst #- gocritic #- gocyclo #- gofmt - goimports - golint #- gomnd #- goprintffuncname #- gosec #- gosimple - govet - ineffassign - interfacer #- lll - maligned - megacheck #- misspell #- nakedret #- noctx #- nolintlint #- rowserrcheck #- scopelint #- staticcheck - structcheck #- stylecheck #- typecheck - unconvert #- unparam #- unused - varcheck #- whitespace fast: false ================================================ FILE: vendor/github.com/spf13/cobra/.mailmap ================================================ Steve Francia Bjørn Erik Pedersen Fabiano Franz ================================================ FILE: vendor/github.com/spf13/cobra/CONDUCT.md ================================================ ## Cobra User Contract ### Versioning Cobra will follow a steady release cadence. Non breaking changes will be released as minor versions quarterly. Patch bug releases are at the discretion of the maintainers. Users can expect security patch fixes to be released within relatively short order of a CVE becoming known. For more information on security patch fixes see the CVE section below. Releases will follow [Semantic Versioning](https://semver.org/). Users tracking the Master branch should expect unpredictable breaking changes as the project continues to move forward. For stability, it is highly recommended to use a release. ### Backward Compatibility We will maintain two major releases in a moving window. The N-1 release will only receive bug fixes and security updates and will be dropped once N+1 is released. ### Deprecation Deprecation of Go versions or dependent packages will only occur in major releases. To reduce the change of this taking users by surprise, any large deprecation will be preceded by an announcement in the [#cobra slack channel](https://gophers.slack.com/archives/CD3LP1199) and an Issue on Github. ### CVE Maintainers will make every effort to release security patches in the case of a medium to high severity CVE directly impacting the library. The speed in which these patches reach a release is up to the discretion of the maintainers. A low severity CVE may be a lower priority than a high severity one. ### Communication Cobra maintainers will use GitHub issues and the [#cobra slack channel](https://gophers.slack.com/archives/CD3LP1199) as the primary means of communication with the community. This is to foster open communication with all users and contributors. ### Breaking Changes Breaking changes are generally allowed in the master branch, as this is the branch used to develop the next release of Cobra. There may be times, however, when master is closed for breaking changes. This is likely to happen as we near the release of a new version. Breaking changes are not allowed in release branches, as these represent minor versions that have already been released. These version have consumers who expect the APIs, behaviors, etc, to remain stable during the lifetime of the patch stream for the minor release. Examples of breaking changes include: - Removing or renaming exported constant, variable, type, or function. - Updating the version of critical libraries such as `spf13/pflag`, `spf13/viper` etc... - Some version updates may be acceptable for picking up bug fixes, but maintainers must exercise caution when reviewing. There may, at times, need to be exceptions where breaking changes are allowed in release branches. These are at the discretion of the project's maintainers, and must be carefully considered before merging. ### CI Testing Maintainers will ensure the Cobra test suite utilizes the current supported versions of Golang. ### Disclaimer Changes to this document and the contents therein are at the discretion of the maintainers. None of the contents of this document are legally binding in any way to the maintainers or the users. ================================================ FILE: vendor/github.com/spf13/cobra/CONTRIBUTING.md ================================================ # Contributing to Cobra Thank you so much for contributing to Cobra. We appreciate your time and help. Here are some guidelines to help you get started. ## Code of Conduct Be kind and respectful to the members of the community. Take time to educate others who are seeking help. Harassment of any kind will not be tolerated. ## Questions If you have questions regarding Cobra, feel free to ask it in the community [#cobra Slack channel][cobra-slack] ## Filing a bug or feature 1. Before filing an issue, please check the existing issues to see if a similar one was already opened. If there is one already opened, feel free to comment on it. 1. If you believe you've found a bug, please provide detailed steps of reproduction, the version of Cobra and anything else you believe will be useful to help troubleshoot it (e.g. OS environment, environment variables, etc...). Also state the current behavior vs. the expected behavior. 1. If you'd like to see a feature or an enhancement please open an issue with a clear title and description of what the feature is and why it would be beneficial to the project and its users. ## Submitting changes 1. CLA: Upon submitting a Pull Request (PR), contributors will be prompted to sign a CLA. Please sign the CLA :slightly_smiling_face: 1. Tests: If you are submitting code, please ensure you have adequate tests for the feature. Tests can be run via `go test ./...` or `make test`. 1. Since this is golang project, ensure the new code is properly formatted to ensure code consistency. Run `make all`. ### Quick steps to contribute 1. Fork the project. 1. Download your fork to your PC (`git clone https://github.com/your_username/cobra && cd cobra`) 1. Create your feature branch (`git checkout -b my-new-feature`) 1. Make changes and run tests (`make test`) 1. Add them to staging (`git add .`) 1. Commit your changes (`git commit -m 'Add some feature'`) 1. Push to the branch (`git push origin my-new-feature`) 1. Create new pull request [cobra-slack]: https://gophers.slack.com/archives/CD3LP1199 ================================================ FILE: vendor/github.com/spf13/cobra/LICENSE.txt ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. ================================================ FILE: vendor/github.com/spf13/cobra/MAINTAINERS ================================================ maintainers: - spf13 - johnSchnake - jpmcb - marckhouzam inactive: - anthonyfok - bep - bogem - broady - eparis - jharshman - wfernandes ================================================ FILE: vendor/github.com/spf13/cobra/Makefile ================================================ BIN="./bin" SRC=$(shell find . -name "*.go") ifeq (, $(shell which golangci-lint)) $(warning "could not find golangci-lint in $(PATH), run: curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh") endif .PHONY: fmt lint test install_deps clean default: all all: fmt test fmt: $(info ******************** checking formatting ********************) @test -z $(shell gofmt -l $(SRC)) || (gofmt -d $(SRC); exit 1) lint: $(info ******************** running lint tools ********************) golangci-lint run -v test: install_deps $(info ******************** running tests ********************) go test -v ./... richtest: install_deps $(info ******************** running tests with kyoh86/richgo ********************) richgo test -v ./... install_deps: $(info ******************** downloading dependencies ********************) go get -v ./... clean: rm -rf $(BIN) ================================================ FILE: vendor/github.com/spf13/cobra/README.md ================================================ ![cobra logo](assets/CobraMain.png) Cobra is a library for creating powerful modern CLI applications. Cobra is used in many Go projects such as [Kubernetes](https://kubernetes.io/), [Hugo](https://gohugo.io), and [GitHub CLI](https://github.com/cli/cli) to name a few. [This list](./projects_using_cobra.md) contains a more extensive list of projects using Cobra. [![](https://img.shields.io/github/actions/workflow/status/spf13/cobra/test.yml?branch=main&longCache=true&label=Test&logo=github%20actions&logoColor=fff)](https://github.com/spf13/cobra/actions?query=workflow%3ATest) [![Go Reference](https://pkg.go.dev/badge/github.com/spf13/cobra.svg)](https://pkg.go.dev/github.com/spf13/cobra) [![Go Report Card](https://goreportcard.com/badge/github.com/spf13/cobra)](https://goreportcard.com/report/github.com/spf13/cobra) [![Slack](https://img.shields.io/badge/Slack-cobra-brightgreen)](https://gophers.slack.com/archives/CD3LP1199) # Overview Cobra is a library providing a simple interface to create powerful modern CLI interfaces similar to git & go tools. Cobra provides: * Easy subcommand-based CLIs: `app server`, `app fetch`, etc. * Fully POSIX-compliant flags (including short & long versions) * Nested subcommands * Global, local and cascading flags * Intelligent suggestions (`app srver`... did you mean `app server`?) * Automatic help generation for commands and flags * Grouping help for subcommands * Automatic help flag recognition of `-h`, `--help`, etc. * Automatically generated shell autocomplete for your application (bash, zsh, fish, powershell) * Automatically generated man pages for your application * Command aliases so you can change things without breaking them * The flexibility to define your own help, usage, etc. * Optional seamless integration with [viper](https://github.com/spf13/viper) for 12-factor apps # Concepts Cobra is built on a structure of commands, arguments & flags. **Commands** represent actions, **Args** are things and **Flags** are modifiers for those actions. The best applications read like sentences when used, and as a result, users intuitively know how to interact with them. The pattern to follow is `APPNAME VERB NOUN --ADJECTIVE` or `APPNAME COMMAND ARG --FLAG`. A few good real world examples may better illustrate this point. In the following example, 'server' is a command, and 'port' is a flag: hugo server --port=1313 In this command we are telling Git to clone the url bare. git clone URL --bare ## Commands Command is the central point of the application. Each interaction that the application supports will be contained in a Command. A command can have children commands and optionally run an action. In the example above, 'server' is the command. [More about cobra.Command](https://pkg.go.dev/github.com/spf13/cobra#Command) ## Flags A flag is a way to modify the behavior of a command. Cobra supports fully POSIX-compliant flags as well as the Go [flag package](https://golang.org/pkg/flag/). A Cobra command can define flags that persist through to children commands and flags that are only available to that command. In the example above, 'port' is the flag. Flag functionality is provided by the [pflag library](https://github.com/spf13/pflag), a fork of the flag standard library which maintains the same interface while adding POSIX compliance. # Installing Using Cobra is easy. First, use `go get` to install the latest version of the library. ``` go get -u github.com/spf13/cobra@latest ``` Next, include Cobra in your application: ```go import "github.com/spf13/cobra" ``` # Usage `cobra-cli` is a command line program to generate cobra applications and command files. It will bootstrap your application scaffolding to rapidly develop a Cobra-based application. It is the easiest way to incorporate Cobra into your application. It can be installed by running: ``` go install github.com/spf13/cobra-cli@latest ``` For complete details on using the Cobra-CLI generator, please read [The Cobra Generator README](https://github.com/spf13/cobra-cli/blob/main/README.md) For complete details on using the Cobra library, please read the [The Cobra User Guide](user_guide.md). # License Cobra is released under the Apache 2.0 license. See [LICENSE.txt](https://github.com/spf13/cobra/blob/master/LICENSE.txt) ================================================ FILE: vendor/github.com/spf13/cobra/active_help.go ================================================ // Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cobra import ( "fmt" "os" "strings" ) const ( activeHelpMarker = "_activeHelp_ " // The below values should not be changed: programs will be using them explicitly // in their user documentation, and users will be using them explicitly. activeHelpEnvVarSuffix = "_ACTIVE_HELP" activeHelpGlobalEnvVar = "COBRA_ACTIVE_HELP" activeHelpGlobalDisable = "0" ) // AppendActiveHelp adds the specified string to the specified array to be used as ActiveHelp. // Such strings will be processed by the completion script and will be shown as ActiveHelp // to the user. // The array parameter should be the array that will contain the completions. // This function can be called multiple times before and/or after completions are added to // the array. Each time this function is called with the same array, the new // ActiveHelp line will be shown below the previous ones when completion is triggered. func AppendActiveHelp(compArray []string, activeHelpStr string) []string { return append(compArray, fmt.Sprintf("%s%s", activeHelpMarker, activeHelpStr)) } // GetActiveHelpConfig returns the value of the ActiveHelp environment variable // _ACTIVE_HELP where is the name of the root command in upper // case, with all - replaced by _. // It will always return "0" if the global environment variable COBRA_ACTIVE_HELP // is set to "0". func GetActiveHelpConfig(cmd *Command) string { activeHelpCfg := os.Getenv(activeHelpGlobalEnvVar) if activeHelpCfg != activeHelpGlobalDisable { activeHelpCfg = os.Getenv(activeHelpEnvVar(cmd.Root().Name())) } return activeHelpCfg } // activeHelpEnvVar returns the name of the program-specific ActiveHelp environment // variable. It has the format _ACTIVE_HELP where is the name of the // root command in upper case, with all - replaced by _. func activeHelpEnvVar(name string) string { // This format should not be changed: users will be using it explicitly. activeHelpEnvVar := strings.ToUpper(fmt.Sprintf("%s%s", name, activeHelpEnvVarSuffix)) return strings.ReplaceAll(activeHelpEnvVar, "-", "_") } ================================================ FILE: vendor/github.com/spf13/cobra/active_help.md ================================================ # Active Help Active Help is a framework provided by Cobra which allows a program to define messages (hints, warnings, etc) that will be printed during program usage. It aims to make it easier for your users to learn how to use your program. If configured by the program, Active Help is printed when the user triggers shell completion. For example, ``` bash-5.1$ helm repo add [tab] You must choose a name for the repo you are adding. bash-5.1$ bin/helm package [tab] Please specify the path to the chart to package bash-5.1$ bin/helm package [tab][tab] bin/ internal/ scripts/ pkg/ testdata/ ``` **Hint**: A good place to use Active Help messages is when the normal completion system does not provide any suggestions. In such cases, Active Help nicely supplements the normal shell completions to guide the user in knowing what is expected by the program. ## Supported shells Active Help is currently only supported for the following shells: - Bash (using [bash completion V2](shell_completions.md#bash-completion-v2) only). Note that bash 4.4 or higher is required for the prompt to appear when an Active Help message is printed. - Zsh ## Adding Active Help messages As Active Help uses the shell completion system, the implementation of Active Help messages is done by enhancing custom dynamic completions. If you are not familiar with dynamic completions, please refer to [Shell Completions](shell_completions.md). Adding Active Help is done through the use of the `cobra.AppendActiveHelp(...)` function, where the program repeatedly adds Active Help messages to the list of completions. Keep reading for details. ### Active Help for nouns Adding Active Help when completing a noun is done within the `ValidArgsFunction(...)` of a command. Please notice the use of `cobra.AppendActiveHelp(...)` in the following example: ```go cmd := &cobra.Command{ Use: "add [NAME] [URL]", Short: "add a chart repository", Args: require.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { return addRepo(args) }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { var comps []string if len(args) == 0 { comps = cobra.AppendActiveHelp(comps, "You must choose a name for the repo you are adding") } else if len(args) == 1 { comps = cobra.AppendActiveHelp(comps, "You must specify the URL for the repo you are adding") } else { comps = cobra.AppendActiveHelp(comps, "This command does not take any more arguments") } return comps, cobra.ShellCompDirectiveNoFileComp }, } ``` The example above defines the completions (none, in this specific example) as well as the Active Help messages for the `helm repo add` command. It yields the following behavior: ``` bash-5.1$ helm repo add [tab] You must choose a name for the repo you are adding bash-5.1$ helm repo add grafana [tab] You must specify the URL for the repo you are adding bash-5.1$ helm repo add grafana https://grafana.github.io/helm-charts [tab] This command does not take any more arguments ``` **Hint**: As can be seen in the above example, a good place to use Active Help messages is when the normal completion system does not provide any suggestions. In such cases, Active Help nicely supplements the normal shell completions. ### Active Help for flags Providing Active Help for flags is done in the same fashion as for nouns, but using the completion function registered for the flag. For example: ```go _ = cmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 2 { return cobra.AppendActiveHelp(nil, "You must first specify the chart to install before the --version flag can be completed"), cobra.ShellCompDirectiveNoFileComp } return compVersionFlag(args[1], toComplete) }) ``` The example above prints an Active Help message when not enough information was given by the user to complete the `--version` flag. ``` bash-5.1$ bin/helm install myrelease --version 2.0.[tab] You must first specify the chart to install before the --version flag can be completed bash-5.1$ bin/helm install myrelease bitnami/solr --version 2.0.[tab][tab] 2.0.1 2.0.2 2.0.3 ``` ## User control of Active Help You may want to allow your users to disable Active Help or choose between different levels of Active Help. It is entirely up to the program to define the type of configurability of Active Help that it wants to offer, if any. Allowing to configure Active Help is entirely optional; you can use Active Help in your program without doing anything about Active Help configuration. The way to configure Active Help is to use the program's Active Help environment variable. That variable is named `_ACTIVE_HELP` where `` is the name of your program in uppercase with any `-` replaced by an `_`. The variable should be set by the user to whatever Active Help configuration values are supported by the program. For example, say `helm` has chosen to support three levels for Active Help: `on`, `off`, `local`. Then a user would set the desired behavior to `local` by doing `export HELM_ACTIVE_HELP=local` in their shell. For simplicity, when in `cmd.ValidArgsFunction(...)` or a flag's completion function, the program should read the Active Help configuration using the `cobra.GetActiveHelpConfig(cmd)` function and select what Active Help messages should or should not be added (instead of reading the environment variable directly). For example: ```go ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { activeHelpLevel := cobra.GetActiveHelpConfig(cmd) var comps []string if len(args) == 0 { if activeHelpLevel != "off" { comps = cobra.AppendActiveHelp(comps, "You must choose a name for the repo you are adding") } } else if len(args) == 1 { if activeHelpLevel != "off" { comps = cobra.AppendActiveHelp(comps, "You must specify the URL for the repo you are adding") } } else { if activeHelpLevel == "local" { comps = cobra.AppendActiveHelp(comps, "This command does not take any more arguments") } } return comps, cobra.ShellCompDirectiveNoFileComp }, ``` **Note 1**: If the `_ACTIVE_HELP` environment variable is set to the string "0", Cobra will automatically disable all Active Help output (even if some output was specified by the program using the `cobra.AppendActiveHelp(...)` function). Using "0" can simplify your code in situations where you want to blindly disable Active Help without having to call `cobra.GetActiveHelpConfig(cmd)` explicitly. **Note 2**: If a user wants to disable Active Help for every single program based on Cobra, she can set the environment variable `COBRA_ACTIVE_HELP` to "0". In this case `cobra.GetActiveHelpConfig(cmd)` will return "0" no matter what the variable `_ACTIVE_HELP` is set to. **Note 3**: If the user does not set `_ACTIVE_HELP` or `COBRA_ACTIVE_HELP` (which will be a common case), the default value for the Active Help configuration returned by `cobra.GetActiveHelpConfig(cmd)` will be the empty string. ## Active Help with Cobra's default completion command Cobra provides a default `completion` command for programs that wish to use it. When using the default `completion` command, Active Help is configurable in the same fashion as described above using environment variables. You may wish to document this in more details for your users. ## Debugging Active Help Debugging your Active Help code is done in the same way as debugging your dynamic completion code, which is with Cobra's hidden `__complete` command. Please refer to [debugging shell completion](shell_completions.md#debugging) for details. When debugging with the `__complete` command, if you want to specify different Active Help configurations, you should use the active help environment variable. That variable is named `_ACTIVE_HELP` where any `-` is replaced by an `_`. For example, we can test deactivating some Active Help as shown below: ``` $ HELM_ACTIVE_HELP=1 bin/helm __complete install wordpress bitnami/h bitnami/haproxy bitnami/harbor _activeHelp_ WARNING: cannot re-use a name that is still in use :0 Completion ended with directive: ShellCompDirectiveDefault $ HELM_ACTIVE_HELP=0 bin/helm __complete install wordpress bitnami/h bitnami/haproxy bitnami/harbor :0 Completion ended with directive: ShellCompDirectiveDefault ``` ================================================ FILE: vendor/github.com/spf13/cobra/args.go ================================================ // Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cobra import ( "fmt" "strings" ) type PositionalArgs func(cmd *Command, args []string) error // legacyArgs validation has the following behaviour: // - root commands with no subcommands can take arbitrary arguments // - root commands with subcommands will do subcommand validity checking // - subcommands will always accept arbitrary arguments func legacyArgs(cmd *Command, args []string) error { // no subcommand, always take args if !cmd.HasSubCommands() { return nil } // root command with subcommands, do subcommand checking. if !cmd.HasParent() && len(args) > 0 { return fmt.Errorf("unknown command %q for %q%s", args[0], cmd.CommandPath(), cmd.findSuggestions(args[0])) } return nil } // NoArgs returns an error if any args are included. func NoArgs(cmd *Command, args []string) error { if len(args) > 0 { return fmt.Errorf("unknown command %q for %q", args[0], cmd.CommandPath()) } return nil } // OnlyValidArgs returns an error if there are any positional args that are not in // the `ValidArgs` field of `Command` func OnlyValidArgs(cmd *Command, args []string) error { if len(cmd.ValidArgs) > 0 { // Remove any description that may be included in ValidArgs. // A description is following a tab character. var validArgs []string for _, v := range cmd.ValidArgs { validArgs = append(validArgs, strings.Split(v, "\t")[0]) } for _, v := range args { if !stringInSlice(v, validArgs) { return fmt.Errorf("invalid argument %q for %q%s", v, cmd.CommandPath(), cmd.findSuggestions(args[0])) } } } return nil } // ArbitraryArgs never returns an error. func ArbitraryArgs(cmd *Command, args []string) error { return nil } // MinimumNArgs returns an error if there is not at least N args. func MinimumNArgs(n int) PositionalArgs { return func(cmd *Command, args []string) error { if len(args) < n { return fmt.Errorf("requires at least %d arg(s), only received %d", n, len(args)) } return nil } } // MaximumNArgs returns an error if there are more than N args. func MaximumNArgs(n int) PositionalArgs { return func(cmd *Command, args []string) error { if len(args) > n { return fmt.Errorf("accepts at most %d arg(s), received %d", n, len(args)) } return nil } } // ExactArgs returns an error if there are not exactly n args. func ExactArgs(n int) PositionalArgs { return func(cmd *Command, args []string) error { if len(args) != n { return fmt.Errorf("accepts %d arg(s), received %d", n, len(args)) } return nil } } // RangeArgs returns an error if the number of args is not within the expected range. func RangeArgs(min int, max int) PositionalArgs { return func(cmd *Command, args []string) error { if len(args) < min || len(args) > max { return fmt.Errorf("accepts between %d and %d arg(s), received %d", min, max, len(args)) } return nil } } // MatchAll allows combining several PositionalArgs to work in concert. func MatchAll(pargs ...PositionalArgs) PositionalArgs { return func(cmd *Command, args []string) error { for _, parg := range pargs { if err := parg(cmd, args); err != nil { return err } } return nil } } // ExactValidArgs returns an error if there are not exactly N positional args OR // there are any positional args that are not in the `ValidArgs` field of `Command` // // Deprecated: use MatchAll(ExactArgs(n), OnlyValidArgs) instead func ExactValidArgs(n int) PositionalArgs { return MatchAll(ExactArgs(n), OnlyValidArgs) } ================================================ FILE: vendor/github.com/spf13/cobra/bash_completions.go ================================================ // Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cobra import ( "bytes" "fmt" "io" "os" "sort" "strings" "github.com/spf13/pflag" ) // Annotations for Bash completion. const ( BashCompFilenameExt = "cobra_annotation_bash_completion_filename_extensions" BashCompCustom = "cobra_annotation_bash_completion_custom" BashCompOneRequiredFlag = "cobra_annotation_bash_completion_one_required_flag" BashCompSubdirsInDir = "cobra_annotation_bash_completion_subdirs_in_dir" ) func writePreamble(buf io.StringWriter, name string) { WriteStringAndCheck(buf, fmt.Sprintf("# bash completion for %-36s -*- shell-script -*-\n", name)) WriteStringAndCheck(buf, fmt.Sprintf(` __%[1]s_debug() { if [[ -n ${BASH_COMP_DEBUG_FILE:-} ]]; then echo "$*" >> "${BASH_COMP_DEBUG_FILE}" fi } # Homebrew on Macs have version 1.3 of bash-completion which doesn't include # _init_completion. This is a very minimal version of that function. __%[1]s_init_completion() { COMPREPLY=() _get_comp_words_by_ref "$@" cur prev words cword } __%[1]s_index_of_word() { local w word=$1 shift index=0 for w in "$@"; do [[ $w = "$word" ]] && return index=$((index+1)) done index=-1 } __%[1]s_contains_word() { local w word=$1; shift for w in "$@"; do [[ $w = "$word" ]] && return done return 1 } __%[1]s_handle_go_custom_completion() { __%[1]s_debug "${FUNCNAME[0]}: cur is ${cur}, words[*] is ${words[*]}, #words[@] is ${#words[@]}" local shellCompDirectiveError=%[3]d local shellCompDirectiveNoSpace=%[4]d local shellCompDirectiveNoFileComp=%[5]d local shellCompDirectiveFilterFileExt=%[6]d local shellCompDirectiveFilterDirs=%[7]d local out requestComp lastParam lastChar comp directive args # Prepare the command to request completions for the program. # Calling ${words[0]} instead of directly %[1]s allows to handle aliases args=("${words[@]:1}") # Disable ActiveHelp which is not supported for bash completion v1 requestComp="%[8]s=0 ${words[0]} %[2]s ${args[*]}" lastParam=${words[$((${#words[@]}-1))]} lastChar=${lastParam:$((${#lastParam}-1)):1} __%[1]s_debug "${FUNCNAME[0]}: lastParam ${lastParam}, lastChar ${lastChar}" if [ -z "${cur}" ] && [ "${lastChar}" != "=" ]; then # If the last parameter is complete (there is a space following it) # We add an extra empty parameter so we can indicate this to the go method. __%[1]s_debug "${FUNCNAME[0]}: Adding extra empty parameter" requestComp="${requestComp} \"\"" fi __%[1]s_debug "${FUNCNAME[0]}: calling ${requestComp}" # Use eval to handle any environment variables and such out=$(eval "${requestComp}" 2>/dev/null) # Extract the directive integer at the very end of the output following a colon (:) directive=${out##*:} # Remove the directive out=${out%%:*} if [ "${directive}" = "${out}" ]; then # There is not directive specified directive=0 fi __%[1]s_debug "${FUNCNAME[0]}: the completion directive is: ${directive}" __%[1]s_debug "${FUNCNAME[0]}: the completions are: ${out}" if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then # Error code. No completion. __%[1]s_debug "${FUNCNAME[0]}: received error from custom completion go code" return else if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then if [[ $(type -t compopt) = "builtin" ]]; then __%[1]s_debug "${FUNCNAME[0]}: activating no space" compopt -o nospace fi fi if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then if [[ $(type -t compopt) = "builtin" ]]; then __%[1]s_debug "${FUNCNAME[0]}: activating no file completion" compopt +o default fi fi fi if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then # File extension filtering local fullFilter filter filteringCmd # Do not use quotes around the $out variable or else newline # characters will be kept. for filter in ${out}; do fullFilter+="$filter|" done filteringCmd="_filedir $fullFilter" __%[1]s_debug "File filtering command: $filteringCmd" $filteringCmd elif [ $((directive & shellCompDirectiveFilterDirs)) -ne 0 ]; then # File completion for directories only local subdir # Use printf to strip any trailing newline subdir=$(printf "%%s" "${out}") if [ -n "$subdir" ]; then __%[1]s_debug "Listing directories in $subdir" __%[1]s_handle_subdirs_in_dir_flag "$subdir" else __%[1]s_debug "Listing directories in ." _filedir -d fi else while IFS='' read -r comp; do COMPREPLY+=("$comp") done < <(compgen -W "${out}" -- "$cur") fi } __%[1]s_handle_reply() { __%[1]s_debug "${FUNCNAME[0]}" local comp case $cur in -*) if [[ $(type -t compopt) = "builtin" ]]; then compopt -o nospace fi local allflags if [ ${#must_have_one_flag[@]} -ne 0 ]; then allflags=("${must_have_one_flag[@]}") else allflags=("${flags[*]} ${two_word_flags[*]}") fi while IFS='' read -r comp; do COMPREPLY+=("$comp") done < <(compgen -W "${allflags[*]}" -- "$cur") if [[ $(type -t compopt) = "builtin" ]]; then [[ "${COMPREPLY[0]}" == *= ]] || compopt +o nospace fi # complete after --flag=abc if [[ $cur == *=* ]]; then if [[ $(type -t compopt) = "builtin" ]]; then compopt +o nospace fi local index flag flag="${cur%%=*}" __%[1]s_index_of_word "${flag}" "${flags_with_completion[@]}" COMPREPLY=() if [[ ${index} -ge 0 ]]; then PREFIX="" cur="${cur#*=}" ${flags_completion[${index}]} if [ -n "${ZSH_VERSION:-}" ]; then # zsh completion needs --flag= prefix eval "COMPREPLY=( \"\${COMPREPLY[@]/#/${flag}=}\" )" fi fi fi if [[ -z "${flag_parsing_disabled}" ]]; then # If flag parsing is enabled, we have completed the flags and can return. # If flag parsing is disabled, we may not know all (or any) of the flags, so we fallthrough # to possibly call handle_go_custom_completion. return 0; fi ;; esac # check if we are handling a flag with special work handling local index __%[1]s_index_of_word "${prev}" "${flags_with_completion[@]}" if [[ ${index} -ge 0 ]]; then ${flags_completion[${index}]} return fi # we are parsing a flag and don't have a special handler, no completion if [[ ${cur} != "${words[cword]}" ]]; then return fi local completions completions=("${commands[@]}") if [[ ${#must_have_one_noun[@]} -ne 0 ]]; then completions+=("${must_have_one_noun[@]}") elif [[ -n "${has_completion_function}" ]]; then # if a go completion function is provided, defer to that function __%[1]s_handle_go_custom_completion fi if [[ ${#must_have_one_flag[@]} -ne 0 ]]; then completions+=("${must_have_one_flag[@]}") fi while IFS='' read -r comp; do COMPREPLY+=("$comp") done < <(compgen -W "${completions[*]}" -- "$cur") if [[ ${#COMPREPLY[@]} -eq 0 && ${#noun_aliases[@]} -gt 0 && ${#must_have_one_noun[@]} -ne 0 ]]; then while IFS='' read -r comp; do COMPREPLY+=("$comp") done < <(compgen -W "${noun_aliases[*]}" -- "$cur") fi if [[ ${#COMPREPLY[@]} -eq 0 ]]; then if declare -F __%[1]s_custom_func >/dev/null; then # try command name qualified custom func __%[1]s_custom_func else # otherwise fall back to unqualified for compatibility declare -F __custom_func >/dev/null && __custom_func fi fi # available in bash-completion >= 2, not always present on macOS if declare -F __ltrim_colon_completions >/dev/null; then __ltrim_colon_completions "$cur" fi # If there is only 1 completion and it is a flag with an = it will be completed # but we don't want a space after the = if [[ "${#COMPREPLY[@]}" -eq "1" ]] && [[ $(type -t compopt) = "builtin" ]] && [[ "${COMPREPLY[0]}" == --*= ]]; then compopt -o nospace fi } # The arguments should be in the form "ext1|ext2|extn" __%[1]s_handle_filename_extension_flag() { local ext="$1" _filedir "@(${ext})" } __%[1]s_handle_subdirs_in_dir_flag() { local dir="$1" pushd "${dir}" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1 || return } __%[1]s_handle_flag() { __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" # if a command required a flag, and we found it, unset must_have_one_flag() local flagname=${words[c]} local flagvalue="" # if the word contained an = if [[ ${words[c]} == *"="* ]]; then flagvalue=${flagname#*=} # take in as flagvalue after the = flagname=${flagname%%=*} # strip everything after the = flagname="${flagname}=" # but put the = back fi __%[1]s_debug "${FUNCNAME[0]}: looking for ${flagname}" if __%[1]s_contains_word "${flagname}" "${must_have_one_flag[@]}"; then must_have_one_flag=() fi # if you set a flag which only applies to this command, don't show subcommands if __%[1]s_contains_word "${flagname}" "${local_nonpersistent_flags[@]}"; then commands=() fi # keep flag value with flagname as flaghash # flaghash variable is an associative array which is only supported in bash > 3. if [[ -z "${BASH_VERSION:-}" || "${BASH_VERSINFO[0]:-}" -gt 3 ]]; then if [ -n "${flagvalue}" ] ; then flaghash[${flagname}]=${flagvalue} elif [ -n "${words[ $((c+1)) ]}" ] ; then flaghash[${flagname}]=${words[ $((c+1)) ]} else flaghash[${flagname}]="true" # pad "true" for bool flag fi fi # skip the argument to a two word flag if [[ ${words[c]} != *"="* ]] && __%[1]s_contains_word "${words[c]}" "${two_word_flags[@]}"; then __%[1]s_debug "${FUNCNAME[0]}: found a flag ${words[c]}, skip the next argument" c=$((c+1)) # if we are looking for a flags value, don't show commands if [[ $c -eq $cword ]]; then commands=() fi fi c=$((c+1)) } __%[1]s_handle_noun() { __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" if __%[1]s_contains_word "${words[c]}" "${must_have_one_noun[@]}"; then must_have_one_noun=() elif __%[1]s_contains_word "${words[c]}" "${noun_aliases[@]}"; then must_have_one_noun=() fi nouns+=("${words[c]}") c=$((c+1)) } __%[1]s_handle_command() { __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" local next_command if [[ -n ${last_command} ]]; then next_command="_${last_command}_${words[c]//:/__}" else if [[ $c -eq 0 ]]; then next_command="_%[1]s_root_command" else next_command="_${words[c]//:/__}" fi fi c=$((c+1)) __%[1]s_debug "${FUNCNAME[0]}: looking for ${next_command}" declare -F "$next_command" >/dev/null && $next_command } __%[1]s_handle_word() { if [[ $c -ge $cword ]]; then __%[1]s_handle_reply return fi __%[1]s_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" if [[ "${words[c]}" == -* ]]; then __%[1]s_handle_flag elif __%[1]s_contains_word "${words[c]}" "${commands[@]}"; then __%[1]s_handle_command elif [[ $c -eq 0 ]]; then __%[1]s_handle_command elif __%[1]s_contains_word "${words[c]}" "${command_aliases[@]}"; then # aliashash variable is an associative array which is only supported in bash > 3. if [[ -z "${BASH_VERSION:-}" || "${BASH_VERSINFO[0]:-}" -gt 3 ]]; then words[c]=${aliashash[${words[c]}]} __%[1]s_handle_command else __%[1]s_handle_noun fi else __%[1]s_handle_noun fi __%[1]s_handle_word } `, name, ShellCompNoDescRequestCmd, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, activeHelpEnvVar(name))) } func writePostscript(buf io.StringWriter, name string) { name = strings.ReplaceAll(name, ":", "__") WriteStringAndCheck(buf, fmt.Sprintf("__start_%s()\n", name)) WriteStringAndCheck(buf, fmt.Sprintf(`{ local cur prev words cword split declare -A flaghash 2>/dev/null || : declare -A aliashash 2>/dev/null || : if declare -F _init_completion >/dev/null 2>&1; then _init_completion -s || return else __%[1]s_init_completion -n "=" || return fi local c=0 local flag_parsing_disabled= local flags=() local two_word_flags=() local local_nonpersistent_flags=() local flags_with_completion=() local flags_completion=() local commands=("%[1]s") local command_aliases=() local must_have_one_flag=() local must_have_one_noun=() local has_completion_function="" local last_command="" local nouns=() local noun_aliases=() __%[1]s_handle_word } `, name)) WriteStringAndCheck(buf, fmt.Sprintf(`if [[ $(type -t compopt) = "builtin" ]]; then complete -o default -F __start_%s %s else complete -o default -o nospace -F __start_%s %s fi `, name, name, name, name)) WriteStringAndCheck(buf, "# ex: ts=4 sw=4 et filetype=sh\n") } func writeCommands(buf io.StringWriter, cmd *Command) { WriteStringAndCheck(buf, " commands=()\n") for _, c := range cmd.Commands() { if !c.IsAvailableCommand() && c != cmd.helpCommand { continue } WriteStringAndCheck(buf, fmt.Sprintf(" commands+=(%q)\n", c.Name())) writeCmdAliases(buf, c) } WriteStringAndCheck(buf, "\n") } func writeFlagHandler(buf io.StringWriter, name string, annotations map[string][]string, cmd *Command) { for key, value := range annotations { switch key { case BashCompFilenameExt: WriteStringAndCheck(buf, fmt.Sprintf(" flags_with_completion+=(%q)\n", name)) var ext string if len(value) > 0 { ext = fmt.Sprintf("__%s_handle_filename_extension_flag ", cmd.Root().Name()) + strings.Join(value, "|") } else { ext = "_filedir" } WriteStringAndCheck(buf, fmt.Sprintf(" flags_completion+=(%q)\n", ext)) case BashCompCustom: WriteStringAndCheck(buf, fmt.Sprintf(" flags_with_completion+=(%q)\n", name)) if len(value) > 0 { handlers := strings.Join(value, "; ") WriteStringAndCheck(buf, fmt.Sprintf(" flags_completion+=(%q)\n", handlers)) } else { WriteStringAndCheck(buf, " flags_completion+=(:)\n") } case BashCompSubdirsInDir: WriteStringAndCheck(buf, fmt.Sprintf(" flags_with_completion+=(%q)\n", name)) var ext string if len(value) == 1 { ext = fmt.Sprintf("__%s_handle_subdirs_in_dir_flag ", cmd.Root().Name()) + value[0] } else { ext = "_filedir -d" } WriteStringAndCheck(buf, fmt.Sprintf(" flags_completion+=(%q)\n", ext)) } } } const cbn = "\")\n" func writeShortFlag(buf io.StringWriter, flag *pflag.Flag, cmd *Command) { name := flag.Shorthand format := " " if len(flag.NoOptDefVal) == 0 { format += "two_word_" } format += "flags+=(\"-%s" + cbn WriteStringAndCheck(buf, fmt.Sprintf(format, name)) writeFlagHandler(buf, "-"+name, flag.Annotations, cmd) } func writeFlag(buf io.StringWriter, flag *pflag.Flag, cmd *Command) { name := flag.Name format := " flags+=(\"--%s" if len(flag.NoOptDefVal) == 0 { format += "=" } format += cbn WriteStringAndCheck(buf, fmt.Sprintf(format, name)) if len(flag.NoOptDefVal) == 0 { format = " two_word_flags+=(\"--%s" + cbn WriteStringAndCheck(buf, fmt.Sprintf(format, name)) } writeFlagHandler(buf, "--"+name, flag.Annotations, cmd) } func writeLocalNonPersistentFlag(buf io.StringWriter, flag *pflag.Flag) { name := flag.Name format := " local_nonpersistent_flags+=(\"--%[1]s" + cbn if len(flag.NoOptDefVal) == 0 { format += " local_nonpersistent_flags+=(\"--%[1]s=" + cbn } WriteStringAndCheck(buf, fmt.Sprintf(format, name)) if len(flag.Shorthand) > 0 { WriteStringAndCheck(buf, fmt.Sprintf(" local_nonpersistent_flags+=(\"-%s\")\n", flag.Shorthand)) } } // prepareCustomAnnotationsForFlags setup annotations for go completions for registered flags func prepareCustomAnnotationsForFlags(cmd *Command) { flagCompletionMutex.RLock() defer flagCompletionMutex.RUnlock() for flag := range flagCompletionFunctions { // Make sure the completion script calls the __*_go_custom_completion function for // every registered flag. We need to do this here (and not when the flag was registered // for completion) so that we can know the root command name for the prefix // of ___go_custom_completion if flag.Annotations == nil { flag.Annotations = map[string][]string{} } flag.Annotations[BashCompCustom] = []string{fmt.Sprintf("__%[1]s_handle_go_custom_completion", cmd.Root().Name())} } } func writeFlags(buf io.StringWriter, cmd *Command) { prepareCustomAnnotationsForFlags(cmd) WriteStringAndCheck(buf, ` flags=() two_word_flags=() local_nonpersistent_flags=() flags_with_completion=() flags_completion=() `) if cmd.DisableFlagParsing { WriteStringAndCheck(buf, " flag_parsing_disabled=1\n") } localNonPersistentFlags := cmd.LocalNonPersistentFlags() cmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) { if nonCompletableFlag(flag) { return } writeFlag(buf, flag, cmd) if len(flag.Shorthand) > 0 { writeShortFlag(buf, flag, cmd) } // localNonPersistentFlags are used to stop the completion of subcommands when one is set // if TraverseChildren is true we should allow to complete subcommands if localNonPersistentFlags.Lookup(flag.Name) != nil && !cmd.Root().TraverseChildren { writeLocalNonPersistentFlag(buf, flag) } }) cmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) { if nonCompletableFlag(flag) { return } writeFlag(buf, flag, cmd) if len(flag.Shorthand) > 0 { writeShortFlag(buf, flag, cmd) } }) WriteStringAndCheck(buf, "\n") } func writeRequiredFlag(buf io.StringWriter, cmd *Command) { WriteStringAndCheck(buf, " must_have_one_flag=()\n") flags := cmd.NonInheritedFlags() flags.VisitAll(func(flag *pflag.Flag) { if nonCompletableFlag(flag) { return } for key := range flag.Annotations { switch key { case BashCompOneRequiredFlag: format := " must_have_one_flag+=(\"--%s" if flag.Value.Type() != "bool" { format += "=" } format += cbn WriteStringAndCheck(buf, fmt.Sprintf(format, flag.Name)) if len(flag.Shorthand) > 0 { WriteStringAndCheck(buf, fmt.Sprintf(" must_have_one_flag+=(\"-%s"+cbn, flag.Shorthand)) } } } }) } func writeRequiredNouns(buf io.StringWriter, cmd *Command) { WriteStringAndCheck(buf, " must_have_one_noun=()\n") sort.Strings(cmd.ValidArgs) for _, value := range cmd.ValidArgs { // Remove any description that may be included following a tab character. // Descriptions are not supported by bash completion. value = strings.Split(value, "\t")[0] WriteStringAndCheck(buf, fmt.Sprintf(" must_have_one_noun+=(%q)\n", value)) } if cmd.ValidArgsFunction != nil { WriteStringAndCheck(buf, " has_completion_function=1\n") } } func writeCmdAliases(buf io.StringWriter, cmd *Command) { if len(cmd.Aliases) == 0 { return } sort.Strings(cmd.Aliases) WriteStringAndCheck(buf, fmt.Sprint(` if [[ -z "${BASH_VERSION:-}" || "${BASH_VERSINFO[0]:-}" -gt 3 ]]; then`, "\n")) for _, value := range cmd.Aliases { WriteStringAndCheck(buf, fmt.Sprintf(" command_aliases+=(%q)\n", value)) WriteStringAndCheck(buf, fmt.Sprintf(" aliashash[%q]=%q\n", value, cmd.Name())) } WriteStringAndCheck(buf, ` fi`) WriteStringAndCheck(buf, "\n") } func writeArgAliases(buf io.StringWriter, cmd *Command) { WriteStringAndCheck(buf, " noun_aliases=()\n") sort.Strings(cmd.ArgAliases) for _, value := range cmd.ArgAliases { WriteStringAndCheck(buf, fmt.Sprintf(" noun_aliases+=(%q)\n", value)) } } func gen(buf io.StringWriter, cmd *Command) { for _, c := range cmd.Commands() { if !c.IsAvailableCommand() && c != cmd.helpCommand { continue } gen(buf, c) } commandName := cmd.CommandPath() commandName = strings.ReplaceAll(commandName, " ", "_") commandName = strings.ReplaceAll(commandName, ":", "__") if cmd.Root() == cmd { WriteStringAndCheck(buf, fmt.Sprintf("_%s_root_command()\n{\n", commandName)) } else { WriteStringAndCheck(buf, fmt.Sprintf("_%s()\n{\n", commandName)) } WriteStringAndCheck(buf, fmt.Sprintf(" last_command=%q\n", commandName)) WriteStringAndCheck(buf, "\n") WriteStringAndCheck(buf, " command_aliases=()\n") WriteStringAndCheck(buf, "\n") writeCommands(buf, cmd) writeFlags(buf, cmd) writeRequiredFlag(buf, cmd) writeRequiredNouns(buf, cmd) writeArgAliases(buf, cmd) WriteStringAndCheck(buf, "}\n\n") } // GenBashCompletion generates bash completion file and writes to the passed writer. func (c *Command) GenBashCompletion(w io.Writer) error { buf := new(bytes.Buffer) writePreamble(buf, c.Name()) if len(c.BashCompletionFunction) > 0 { buf.WriteString(c.BashCompletionFunction + "\n") } gen(buf, c) writePostscript(buf, c.Name()) _, err := buf.WriteTo(w) return err } func nonCompletableFlag(flag *pflag.Flag) bool { return flag.Hidden || len(flag.Deprecated) > 0 } // GenBashCompletionFile generates bash completion file. func (c *Command) GenBashCompletionFile(filename string) error { outFile, err := os.Create(filename) if err != nil { return err } defer outFile.Close() return c.GenBashCompletion(outFile) } ================================================ FILE: vendor/github.com/spf13/cobra/bash_completions.md ================================================ # Generating Bash Completions For Your cobra.Command Please refer to [Shell Completions](shell_completions.md) for details. ## Bash legacy dynamic completions For backward compatibility, Cobra still supports its legacy dynamic completion solution (described below). Unlike the `ValidArgsFunction` solution, the legacy solution will only work for Bash shell-completion and not for other shells. This legacy solution can be used along-side `ValidArgsFunction` and `RegisterFlagCompletionFunc()`, as long as both solutions are not used for the same command. This provides a path to gradually migrate from the legacy solution to the new solution. **Note**: Cobra's default `completion` command uses bash completion V2. If you are currently using Cobra's legacy dynamic completion solution, you should not use the default `completion` command but continue using your own. The legacy solution allows you to inject bash functions into the bash completion script. Those bash functions are responsible for providing the completion choices for your own completions. Some code that works in kubernetes: ```bash const ( bash_completion_func = `__kubectl_parse_get() { local kubectl_output out if kubectl_output=$(kubectl get --no-headers "$1" 2>/dev/null); then out=($(echo "${kubectl_output}" | awk '{print $1}')) COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) ) fi } __kubectl_get_resource() { if [[ ${#nouns[@]} -eq 0 ]]; then return 1 fi __kubectl_parse_get ${nouns[${#nouns[@]} -1]} if [[ $? -eq 0 ]]; then return 0 fi } __kubectl_custom_func() { case ${last_command} in kubectl_get | kubectl_describe | kubectl_delete | kubectl_stop) __kubectl_get_resource return ;; *) ;; esac } `) ``` And then I set that in my command definition: ```go cmds := &cobra.Command{ Use: "kubectl", Short: "kubectl controls the Kubernetes cluster manager", Long: `kubectl controls the Kubernetes cluster manager. Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`, Run: runHelp, BashCompletionFunction: bash_completion_func, } ``` The `BashCompletionFunction` option is really only valid/useful on the root command. Doing the above will cause `__kubectl_custom_func()` (`___custom_func()`) to be called when the built in processor was unable to find a solution. In the case of kubernetes a valid command might look something like `kubectl get pod [mypod]`. If you type `kubectl get pod [tab][tab]` the `__kubectl_customc_func()` will run because the cobra.Command only understood "kubectl" and "get." `__kubectl_custom_func()` will see that the cobra.Command is "kubectl_get" and will thus call another helper `__kubectl_get_resource()`. `__kubectl_get_resource` will look at the 'nouns' collected. In our example the only noun will be `pod`. So it will call `__kubectl_parse_get pod`. `__kubectl_parse_get` will actually call out to kubernetes and get any pods. It will then set `COMPREPLY` to valid pods! Similarly, for flags: ```go annotation := make(map[string][]string) annotation[cobra.BashCompCustom] = []string{"__kubectl_get_namespaces"} flag := &pflag.Flag{ Name: "namespace", Usage: usage, Annotations: annotation, } cmd.Flags().AddFlag(flag) ``` In addition add the `__kubectl_get_namespaces` implementation in the `BashCompletionFunction` value, e.g.: ```bash __kubectl_get_namespaces() { local template template="{{ range .items }}{{ .metadata.name }} {{ end }}" local kubectl_out if kubectl_out=$(kubectl get -o template --template="${template}" namespace 2>/dev/null); then COMPREPLY=( $( compgen -W "${kubectl_out}[*]" -- "$cur" ) ) fi } ``` ================================================ FILE: vendor/github.com/spf13/cobra/bash_completionsV2.go ================================================ // Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cobra import ( "bytes" "fmt" "io" "os" ) func (c *Command) genBashCompletion(w io.Writer, includeDesc bool) error { buf := new(bytes.Buffer) genBashComp(buf, c.Name(), includeDesc) _, err := buf.WriteTo(w) return err } func genBashComp(buf io.StringWriter, name string, includeDesc bool) { compCmd := ShellCompRequestCmd if !includeDesc { compCmd = ShellCompNoDescRequestCmd } WriteStringAndCheck(buf, fmt.Sprintf(`# bash completion V2 for %-36[1]s -*- shell-script -*- __%[1]s_debug() { if [[ -n ${BASH_COMP_DEBUG_FILE-} ]]; then echo "$*" >> "${BASH_COMP_DEBUG_FILE}" fi } # Macs have bash3 for which the bash-completion package doesn't include # _init_completion. This is a minimal version of that function. __%[1]s_init_completion() { COMPREPLY=() _get_comp_words_by_ref "$@" cur prev words cword } # This function calls the %[1]s program to obtain the completion # results and the directive. It fills the 'out' and 'directive' vars. __%[1]s_get_completion_results() { local requestComp lastParam lastChar args # Prepare the command to request completions for the program. # Calling ${words[0]} instead of directly %[1]s allows to handle aliases args=("${words[@]:1}") requestComp="${words[0]} %[2]s ${args[*]}" lastParam=${words[$((${#words[@]}-1))]} lastChar=${lastParam:$((${#lastParam}-1)):1} __%[1]s_debug "lastParam ${lastParam}, lastChar ${lastChar}" if [[ -z ${cur} && ${lastChar} != = ]]; then # If the last parameter is complete (there is a space following it) # We add an extra empty parameter so we can indicate this to the go method. __%[1]s_debug "Adding extra empty parameter" requestComp="${requestComp} ''" fi # When completing a flag with an = (e.g., %[1]s -n=) # bash focuses on the part after the =, so we need to remove # the flag part from $cur if [[ ${cur} == -*=* ]]; then cur="${cur#*=}" fi __%[1]s_debug "Calling ${requestComp}" # Use eval to handle any environment variables and such out=$(eval "${requestComp}" 2>/dev/null) # Extract the directive integer at the very end of the output following a colon (:) directive=${out##*:} # Remove the directive out=${out%%:*} if [[ ${directive} == "${out}" ]]; then # There is not directive specified directive=0 fi __%[1]s_debug "The completion directive is: ${directive}" __%[1]s_debug "The completions are: ${out}" } __%[1]s_process_completion_results() { local shellCompDirectiveError=%[3]d local shellCompDirectiveNoSpace=%[4]d local shellCompDirectiveNoFileComp=%[5]d local shellCompDirectiveFilterFileExt=%[6]d local shellCompDirectiveFilterDirs=%[7]d local shellCompDirectiveKeepOrder=%[8]d if (((directive & shellCompDirectiveError) != 0)); then # Error code. No completion. __%[1]s_debug "Received error from custom completion go code" return else if (((directive & shellCompDirectiveNoSpace) != 0)); then if [[ $(type -t compopt) == builtin ]]; then __%[1]s_debug "Activating no space" compopt -o nospace else __%[1]s_debug "No space directive not supported in this version of bash" fi fi if (((directive & shellCompDirectiveKeepOrder) != 0)); then if [[ $(type -t compopt) == builtin ]]; then # no sort isn't supported for bash less than < 4.4 if [[ ${BASH_VERSINFO[0]} -lt 4 || ( ${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -lt 4 ) ]]; then __%[1]s_debug "No sort directive not supported in this version of bash" else __%[1]s_debug "Activating keep order" compopt -o nosort fi else __%[1]s_debug "No sort directive not supported in this version of bash" fi fi if (((directive & shellCompDirectiveNoFileComp) != 0)); then if [[ $(type -t compopt) == builtin ]]; then __%[1]s_debug "Activating no file completion" compopt +o default else __%[1]s_debug "No file completion directive not supported in this version of bash" fi fi fi # Separate activeHelp from normal completions local completions=() local activeHelp=() __%[1]s_extract_activeHelp if (((directive & shellCompDirectiveFilterFileExt) != 0)); then # File extension filtering local fullFilter filter filteringCmd # Do not use quotes around the $completions variable or else newline # characters will be kept. for filter in ${completions[*]}; do fullFilter+="$filter|" done filteringCmd="_filedir $fullFilter" __%[1]s_debug "File filtering command: $filteringCmd" $filteringCmd elif (((directive & shellCompDirectiveFilterDirs) != 0)); then # File completion for directories only local subdir subdir=${completions[0]} if [[ -n $subdir ]]; then __%[1]s_debug "Listing directories in $subdir" pushd "$subdir" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1 || return else __%[1]s_debug "Listing directories in ." _filedir -d fi else __%[1]s_handle_completion_types fi __%[1]s_handle_special_char "$cur" : __%[1]s_handle_special_char "$cur" = # Print the activeHelp statements before we finish if ((${#activeHelp[*]} != 0)); then printf "\n"; printf "%%s\n" "${activeHelp[@]}" printf "\n" # The prompt format is only available from bash 4.4. # We test if it is available before using it. if (x=${PS1@P}) 2> /dev/null; then printf "%%s" "${PS1@P}${COMP_LINE[@]}" else # Can't print the prompt. Just print the # text the user had typed, it is workable enough. printf "%%s" "${COMP_LINE[@]}" fi fi } # Separate activeHelp lines from real completions. # Fills the $activeHelp and $completions arrays. __%[1]s_extract_activeHelp() { local activeHelpMarker="%[9]s" local endIndex=${#activeHelpMarker} while IFS='' read -r comp; do if [[ ${comp:0:endIndex} == $activeHelpMarker ]]; then comp=${comp:endIndex} __%[1]s_debug "ActiveHelp found: $comp" if [[ -n $comp ]]; then activeHelp+=("$comp") fi else # Not an activeHelp line but a normal completion completions+=("$comp") fi done <<<"${out}" } __%[1]s_handle_completion_types() { __%[1]s_debug "__%[1]s_handle_completion_types: COMP_TYPE is $COMP_TYPE" case $COMP_TYPE in 37|42) # Type: menu-complete/menu-complete-backward and insert-completions # If the user requested inserting one completion at a time, or all # completions at once on the command-line we must remove the descriptions. # https://github.com/spf13/cobra/issues/1508 local tab=$'\t' comp while IFS='' read -r comp; do [[ -z $comp ]] && continue # Strip any description comp=${comp%%%%$tab*} # Only consider the completions that match if [[ $comp == "$cur"* ]]; then COMPREPLY+=("$comp") fi done < <(printf "%%s\n" "${completions[@]}") ;; *) # Type: complete (normal completion) __%[1]s_handle_standard_completion_case ;; esac } __%[1]s_handle_standard_completion_case() { local tab=$'\t' comp # Short circuit to optimize if we don't have descriptions if [[ "${completions[*]}" != *$tab* ]]; then IFS=$'\n' read -ra COMPREPLY -d '' < <(compgen -W "${completions[*]}" -- "$cur") return 0 fi local longest=0 local compline # Look for the longest completion so that we can format things nicely while IFS='' read -r compline; do [[ -z $compline ]] && continue # Strip any description before checking the length comp=${compline%%%%$tab*} # Only consider the completions that match [[ $comp == "$cur"* ]] || continue COMPREPLY+=("$compline") if ((${#comp}>longest)); then longest=${#comp} fi done < <(printf "%%s\n" "${completions[@]}") # If there is a single completion left, remove the description text if ((${#COMPREPLY[*]} == 1)); then __%[1]s_debug "COMPREPLY[0]: ${COMPREPLY[0]}" comp="${COMPREPLY[0]%%%%$tab*}" __%[1]s_debug "Removed description from single completion, which is now: ${comp}" COMPREPLY[0]=$comp else # Format the descriptions __%[1]s_format_comp_descriptions $longest fi } __%[1]s_handle_special_char() { local comp="$1" local char=$2 if [[ "$comp" == *${char}* && "$COMP_WORDBREAKS" == *${char}* ]]; then local word=${comp%%"${comp##*${char}}"} local idx=${#COMPREPLY[*]} while ((--idx >= 0)); do COMPREPLY[idx]=${COMPREPLY[idx]#"$word"} done fi } __%[1]s_format_comp_descriptions() { local tab=$'\t' local comp desc maxdesclength local longest=$1 local i ci for ci in ${!COMPREPLY[*]}; do comp=${COMPREPLY[ci]} # Properly format the description string which follows a tab character if there is one if [[ "$comp" == *$tab* ]]; then __%[1]s_debug "Original comp: $comp" desc=${comp#*$tab} comp=${comp%%%%$tab*} # $COLUMNS stores the current shell width. # Remove an extra 4 because we add 2 spaces and 2 parentheses. maxdesclength=$(( COLUMNS - longest - 4 )) # Make sure we can fit a description of at least 8 characters # if we are to align the descriptions. if ((maxdesclength > 8)); then # Add the proper number of spaces to align the descriptions for ((i = ${#comp} ; i < longest ; i++)); do comp+=" " done else # Don't pad the descriptions so we can fit more text after the completion maxdesclength=$(( COLUMNS - ${#comp} - 4 )) fi # If there is enough space for any description text, # truncate the descriptions that are too long for the shell width if ((maxdesclength > 0)); then if ((${#desc} > maxdesclength)); then desc=${desc:0:$(( maxdesclength - 1 ))} desc+="…" fi comp+=" ($desc)" fi COMPREPLY[ci]=$comp __%[1]s_debug "Final comp: $comp" fi done } __start_%[1]s() { local cur prev words cword split COMPREPLY=() # Call _init_completion from the bash-completion package # to prepare the arguments properly if declare -F _init_completion >/dev/null 2>&1; then _init_completion -n =: || return else __%[1]s_init_completion -n =: || return fi __%[1]s_debug __%[1]s_debug "========= starting completion logic ==========" __%[1]s_debug "cur is ${cur}, words[*] is ${words[*]}, #words[@] is ${#words[@]}, cword is $cword" # The user could have moved the cursor backwards on the command-line. # We need to trigger completion from the $cword location, so we need # to truncate the command-line ($words) up to the $cword location. words=("${words[@]:0:$cword+1}") __%[1]s_debug "Truncated words[*]: ${words[*]}," local out directive __%[1]s_get_completion_results __%[1]s_process_completion_results } if [[ $(type -t compopt) = "builtin" ]]; then complete -o default -F __start_%[1]s %[1]s else complete -o default -o nospace -F __start_%[1]s %[1]s fi # ex: ts=4 sw=4 et filetype=sh `, name, compCmd, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder, activeHelpMarker)) } // GenBashCompletionFileV2 generates Bash completion version 2. func (c *Command) GenBashCompletionFileV2(filename string, includeDesc bool) error { outFile, err := os.Create(filename) if err != nil { return err } defer outFile.Close() return c.GenBashCompletionV2(outFile, includeDesc) } // GenBashCompletionV2 generates Bash completion file version 2 // and writes it to the passed writer. func (c *Command) GenBashCompletionV2(w io.Writer, includeDesc bool) error { return c.genBashCompletion(w, includeDesc) } ================================================ FILE: vendor/github.com/spf13/cobra/cobra.go ================================================ // Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Commands similar to git, go tools and other modern CLI tools // inspired by go, go-Commander, gh and subcommand package cobra import ( "fmt" "io" "os" "reflect" "strconv" "strings" "text/template" "time" "unicode" ) var templateFuncs = template.FuncMap{ "trim": strings.TrimSpace, "trimRightSpace": trimRightSpace, "trimTrailingWhitespaces": trimRightSpace, "appendIfNotPresent": appendIfNotPresent, "rpad": rpad, "gt": Gt, "eq": Eq, } var initializers []func() var finalizers []func() const ( defaultPrefixMatching = false defaultCommandSorting = true defaultCaseInsensitive = false ) // EnablePrefixMatching allows to set automatic prefix matching. Automatic prefix matching can be a dangerous thing // to automatically enable in CLI tools. // Set this to true to enable it. var EnablePrefixMatching = defaultPrefixMatching // EnableCommandSorting controls sorting of the slice of commands, which is turned on by default. // To disable sorting, set it to false. var EnableCommandSorting = defaultCommandSorting // EnableCaseInsensitive allows case-insensitive commands names. (case sensitive by default) var EnableCaseInsensitive = defaultCaseInsensitive // MousetrapHelpText enables an information splash screen on Windows // if the CLI is started from explorer.exe. // To disable the mousetrap, just set this variable to blank string (""). // Works only on Microsoft Windows. var MousetrapHelpText = `This is a command line tool. You need to open cmd.exe and run it from there. ` // MousetrapDisplayDuration controls how long the MousetrapHelpText message is displayed on Windows // if the CLI is started from explorer.exe. Set to 0 to wait for the return key to be pressed. // To disable the mousetrap, just set MousetrapHelpText to blank string (""). // Works only on Microsoft Windows. var MousetrapDisplayDuration = 5 * time.Second // AddTemplateFunc adds a template function that's available to Usage and Help // template generation. func AddTemplateFunc(name string, tmplFunc interface{}) { templateFuncs[name] = tmplFunc } // AddTemplateFuncs adds multiple template functions that are available to Usage and // Help template generation. func AddTemplateFuncs(tmplFuncs template.FuncMap) { for k, v := range tmplFuncs { templateFuncs[k] = v } } // OnInitialize sets the passed functions to be run when each command's // Execute method is called. func OnInitialize(y ...func()) { initializers = append(initializers, y...) } // OnFinalize sets the passed functions to be run when each command's // Execute method is terminated. func OnFinalize(y ...func()) { finalizers = append(finalizers, y...) } // FIXME Gt is unused by cobra and should be removed in a version 2. It exists only for compatibility with users of cobra. // Gt takes two types and checks whether the first type is greater than the second. In case of types Arrays, Chans, // Maps and Slices, Gt will compare their lengths. Ints are compared directly while strings are first parsed as // ints and then compared. func Gt(a interface{}, b interface{}) bool { var left, right int64 av := reflect.ValueOf(a) switch av.Kind() { case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice: left = int64(av.Len()) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: left = av.Int() case reflect.String: left, _ = strconv.ParseInt(av.String(), 10, 64) } bv := reflect.ValueOf(b) switch bv.Kind() { case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice: right = int64(bv.Len()) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: right = bv.Int() case reflect.String: right, _ = strconv.ParseInt(bv.String(), 10, 64) } return left > right } // FIXME Eq is unused by cobra and should be removed in a version 2. It exists only for compatibility with users of cobra. // Eq takes two types and checks whether they are equal. Supported types are int and string. Unsupported types will panic. func Eq(a interface{}, b interface{}) bool { av := reflect.ValueOf(a) bv := reflect.ValueOf(b) switch av.Kind() { case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice: panic("Eq called on unsupported type") case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return av.Int() == bv.Int() case reflect.String: return av.String() == bv.String() } return false } func trimRightSpace(s string) string { return strings.TrimRightFunc(s, unicode.IsSpace) } // FIXME appendIfNotPresent is unused by cobra and should be removed in a version 2. It exists only for compatibility with users of cobra. // appendIfNotPresent will append stringToAppend to the end of s, but only if it's not yet present in s. func appendIfNotPresent(s, stringToAppend string) string { if strings.Contains(s, stringToAppend) { return s } return s + " " + stringToAppend } // rpad adds padding to the right of a string. func rpad(s string, padding int) string { formattedString := fmt.Sprintf("%%-%ds", padding) return fmt.Sprintf(formattedString, s) } // tmpl executes the given template text on data, writing the result to w. func tmpl(w io.Writer, text string, data interface{}) error { t := template.New("top") t.Funcs(templateFuncs) template.Must(t.Parse(text)) return t.Execute(w, data) } // ld compares two strings and returns the levenshtein distance between them. func ld(s, t string, ignoreCase bool) int { if ignoreCase { s = strings.ToLower(s) t = strings.ToLower(t) } d := make([][]int, len(s)+1) for i := range d { d[i] = make([]int, len(t)+1) } for i := range d { d[i][0] = i } for j := range d[0] { d[0][j] = j } for j := 1; j <= len(t); j++ { for i := 1; i <= len(s); i++ { if s[i-1] == t[j-1] { d[i][j] = d[i-1][j-1] } else { min := d[i-1][j] if d[i][j-1] < min { min = d[i][j-1] } if d[i-1][j-1] < min { min = d[i-1][j-1] } d[i][j] = min + 1 } } } return d[len(s)][len(t)] } func stringInSlice(a string, list []string) bool { for _, b := range list { if b == a { return true } } return false } // CheckErr prints the msg with the prefix 'Error:' and exits with error code 1. If the msg is nil, it does nothing. func CheckErr(msg interface{}) { if msg != nil { fmt.Fprintln(os.Stderr, "Error:", msg) os.Exit(1) } } // WriteStringAndCheck writes a string into a buffer, and checks if the error is not nil. func WriteStringAndCheck(b io.StringWriter, s string) { _, err := b.WriteString(s) CheckErr(err) } ================================================ FILE: vendor/github.com/spf13/cobra/command.go ================================================ // Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package cobra is a commander providing a simple interface to create powerful modern CLI interfaces. // In addition to providing an interface, Cobra simultaneously provides a controller to organize your application code. package cobra import ( "bytes" "context" "errors" "fmt" "io" "os" "path/filepath" "sort" "strings" flag "github.com/spf13/pflag" ) const FlagSetByCobraAnnotation = "cobra_annotation_flag_set_by_cobra" // FParseErrWhitelist configures Flag parse errors to be ignored type FParseErrWhitelist flag.ParseErrorsWhitelist // Group Structure to manage groups for commands type Group struct { ID string Title string } // Command is just that, a command for your application. // E.g. 'go run ...' - 'run' is the command. Cobra requires // you to define the usage and description as part of your command // definition to ensure usability. type Command struct { // Use is the one-line usage message. // Recommended syntax is as follows: // [ ] identifies an optional argument. Arguments that are not enclosed in brackets are required. // ... indicates that you can specify multiple values for the previous argument. // | indicates mutually exclusive information. You can use the argument to the left of the separator or the // argument to the right of the separator. You cannot use both arguments in a single use of the command. // { } delimits a set of mutually exclusive arguments when one of the arguments is required. If the arguments are // optional, they are enclosed in brackets ([ ]). // Example: add [-F file | -D dir]... [-f format] profile Use string // Aliases is an array of aliases that can be used instead of the first word in Use. Aliases []string // SuggestFor is an array of command names for which this command will be suggested - // similar to aliases but only suggests. SuggestFor []string // Short is the short description shown in the 'help' output. Short string // The group id under which this subcommand is grouped in the 'help' output of its parent. GroupID string // Long is the long message shown in the 'help ' output. Long string // Example is examples of how to use the command. Example string // ValidArgs is list of all valid non-flag arguments that are accepted in shell completions ValidArgs []string // ValidArgsFunction is an optional function that provides valid non-flag arguments for shell completion. // It is a dynamic version of using ValidArgs. // Only one of ValidArgs and ValidArgsFunction can be used for a command. ValidArgsFunction func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) // Expected arguments Args PositionalArgs // ArgAliases is List of aliases for ValidArgs. // These are not suggested to the user in the shell completion, // but accepted if entered manually. ArgAliases []string // BashCompletionFunction is custom bash functions used by the legacy bash autocompletion generator. // For portability with other shells, it is recommended to instead use ValidArgsFunction BashCompletionFunction string // Deprecated defines, if this command is deprecated and should print this string when used. Deprecated string // Annotations are key/value pairs that can be used by applications to identify or // group commands. Annotations map[string]string // Version defines the version for this command. If this value is non-empty and the command does not // define a "version" flag, a "version" boolean flag will be added to the command and, if specified, // will print content of the "Version" variable. A shorthand "v" flag will also be added if the // command does not define one. Version string // The *Run functions are executed in the following order: // * PersistentPreRun() // * PreRun() // * Run() // * PostRun() // * PersistentPostRun() // All functions get the same args, the arguments after the command name. // // PersistentPreRun: children of this command will inherit and execute. PersistentPreRun func(cmd *Command, args []string) // PersistentPreRunE: PersistentPreRun but returns an error. PersistentPreRunE func(cmd *Command, args []string) error // PreRun: children of this command will not inherit. PreRun func(cmd *Command, args []string) // PreRunE: PreRun but returns an error. PreRunE func(cmd *Command, args []string) error // Run: Typically the actual work function. Most commands will only implement this. Run func(cmd *Command, args []string) // RunE: Run but returns an error. RunE func(cmd *Command, args []string) error // PostRun: run after the Run command. PostRun func(cmd *Command, args []string) // PostRunE: PostRun but returns an error. PostRunE func(cmd *Command, args []string) error // PersistentPostRun: children of this command will inherit and execute after PostRun. PersistentPostRun func(cmd *Command, args []string) // PersistentPostRunE: PersistentPostRun but returns an error. PersistentPostRunE func(cmd *Command, args []string) error // groups for subcommands commandgroups []*Group // args is actual args parsed from flags. args []string // flagErrorBuf contains all error messages from pflag. flagErrorBuf *bytes.Buffer // flags is full set of flags. flags *flag.FlagSet // pflags contains persistent flags. pflags *flag.FlagSet // lflags contains local flags. lflags *flag.FlagSet // iflags contains inherited flags. iflags *flag.FlagSet // parentsPflags is all persistent flags of cmd's parents. parentsPflags *flag.FlagSet // globNormFunc is the global normalization function // that we can use on every pflag set and children commands globNormFunc func(f *flag.FlagSet, name string) flag.NormalizedName // usageFunc is usage func defined by user. usageFunc func(*Command) error // usageTemplate is usage template defined by user. usageTemplate string // flagErrorFunc is func defined by user and it's called when the parsing of // flags returns an error. flagErrorFunc func(*Command, error) error // helpTemplate is help template defined by user. helpTemplate string // helpFunc is help func defined by user. helpFunc func(*Command, []string) // helpCommand is command with usage 'help'. If it's not defined by user, // cobra uses default help command. helpCommand *Command // helpCommandGroupID is the group id for the helpCommand helpCommandGroupID string // completionCommandGroupID is the group id for the completion command completionCommandGroupID string // versionTemplate is the version template defined by user. versionTemplate string // inReader is a reader defined by the user that replaces stdin inReader io.Reader // outWriter is a writer defined by the user that replaces stdout outWriter io.Writer // errWriter is a writer defined by the user that replaces stderr errWriter io.Writer // FParseErrWhitelist flag parse errors to be ignored FParseErrWhitelist FParseErrWhitelist // CompletionOptions is a set of options to control the handling of shell completion CompletionOptions CompletionOptions // commandsAreSorted defines, if command slice are sorted or not. commandsAreSorted bool // commandCalledAs is the name or alias value used to call this command. commandCalledAs struct { name string called bool } ctx context.Context // commands is the list of commands supported by this program. commands []*Command // parent is a parent command for this command. parent *Command // Max lengths of commands' string lengths for use in padding. commandsMaxUseLen int commandsMaxCommandPathLen int commandsMaxNameLen int // TraverseChildren parses flags on all parents before executing child command. TraverseChildren bool // Hidden defines, if this command is hidden and should NOT show up in the list of available commands. Hidden bool // SilenceErrors is an option to quiet errors down stream. SilenceErrors bool // SilenceUsage is an option to silence usage when an error occurs. SilenceUsage bool // DisableFlagParsing disables the flag parsing. // If this is true all flags will be passed to the command as arguments. DisableFlagParsing bool // DisableAutoGenTag defines, if gen tag ("Auto generated by spf13/cobra...") // will be printed by generating docs for this command. DisableAutoGenTag bool // DisableFlagsInUseLine will disable the addition of [flags] to the usage // line of a command when printing help or generating docs DisableFlagsInUseLine bool // DisableSuggestions disables the suggestions based on Levenshtein distance // that go along with 'unknown command' messages. DisableSuggestions bool // SuggestionsMinimumDistance defines minimum levenshtein distance to display suggestions. // Must be > 0. SuggestionsMinimumDistance int } // Context returns underlying command context. If command was executed // with ExecuteContext or the context was set with SetContext, the // previously set context will be returned. Otherwise, nil is returned. // // Notice that a call to Execute and ExecuteC will replace a nil context of // a command with a context.Background, so a background context will be // returned by Context after one of these functions has been called. func (c *Command) Context() context.Context { return c.ctx } // SetContext sets context for the command. This context will be overwritten by // Command.ExecuteContext or Command.ExecuteContextC. func (c *Command) SetContext(ctx context.Context) { c.ctx = ctx } // SetArgs sets arguments for the command. It is set to os.Args[1:] by default, if desired, can be overridden // particularly useful when testing. func (c *Command) SetArgs(a []string) { c.args = a } // SetOutput sets the destination for usage and error messages. // If output is nil, os.Stderr is used. // Deprecated: Use SetOut and/or SetErr instead func (c *Command) SetOutput(output io.Writer) { c.outWriter = output c.errWriter = output } // SetOut sets the destination for usage messages. // If newOut is nil, os.Stdout is used. func (c *Command) SetOut(newOut io.Writer) { c.outWriter = newOut } // SetErr sets the destination for error messages. // If newErr is nil, os.Stderr is used. func (c *Command) SetErr(newErr io.Writer) { c.errWriter = newErr } // SetIn sets the source for input data // If newIn is nil, os.Stdin is used. func (c *Command) SetIn(newIn io.Reader) { c.inReader = newIn } // SetUsageFunc sets usage function. Usage can be defined by application. func (c *Command) SetUsageFunc(f func(*Command) error) { c.usageFunc = f } // SetUsageTemplate sets usage template. Can be defined by Application. func (c *Command) SetUsageTemplate(s string) { c.usageTemplate = s } // SetFlagErrorFunc sets a function to generate an error when flag parsing // fails. func (c *Command) SetFlagErrorFunc(f func(*Command, error) error) { c.flagErrorFunc = f } // SetHelpFunc sets help function. Can be defined by Application. func (c *Command) SetHelpFunc(f func(*Command, []string)) { c.helpFunc = f } // SetHelpCommand sets help command. func (c *Command) SetHelpCommand(cmd *Command) { c.helpCommand = cmd } // SetHelpCommandGroupID sets the group id of the help command. func (c *Command) SetHelpCommandGroupID(groupID string) { if c.helpCommand != nil { c.helpCommand.GroupID = groupID } // helpCommandGroupID is used if no helpCommand is defined by the user c.helpCommandGroupID = groupID } // SetCompletionCommandGroupID sets the group id of the completion command. func (c *Command) SetCompletionCommandGroupID(groupID string) { // completionCommandGroupID is used if no completion command is defined by the user c.Root().completionCommandGroupID = groupID } // SetHelpTemplate sets help template to be used. Application can use it to set custom template. func (c *Command) SetHelpTemplate(s string) { c.helpTemplate = s } // SetVersionTemplate sets version template to be used. Application can use it to set custom template. func (c *Command) SetVersionTemplate(s string) { c.versionTemplate = s } // SetGlobalNormalizationFunc sets a normalization function to all flag sets and also to child commands. // The user should not have a cyclic dependency on commands. func (c *Command) SetGlobalNormalizationFunc(n func(f *flag.FlagSet, name string) flag.NormalizedName) { c.Flags().SetNormalizeFunc(n) c.PersistentFlags().SetNormalizeFunc(n) c.globNormFunc = n for _, command := range c.commands { command.SetGlobalNormalizationFunc(n) } } // OutOrStdout returns output to stdout. func (c *Command) OutOrStdout() io.Writer { return c.getOut(os.Stdout) } // OutOrStderr returns output to stderr func (c *Command) OutOrStderr() io.Writer { return c.getOut(os.Stderr) } // ErrOrStderr returns output to stderr func (c *Command) ErrOrStderr() io.Writer { return c.getErr(os.Stderr) } // InOrStdin returns input to stdin func (c *Command) InOrStdin() io.Reader { return c.getIn(os.Stdin) } func (c *Command) getOut(def io.Writer) io.Writer { if c.outWriter != nil { return c.outWriter } if c.HasParent() { return c.parent.getOut(def) } return def } func (c *Command) getErr(def io.Writer) io.Writer { if c.errWriter != nil { return c.errWriter } if c.HasParent() { return c.parent.getErr(def) } return def } func (c *Command) getIn(def io.Reader) io.Reader { if c.inReader != nil { return c.inReader } if c.HasParent() { return c.parent.getIn(def) } return def } // UsageFunc returns either the function set by SetUsageFunc for this command // or a parent, or it returns a default usage function. func (c *Command) UsageFunc() (f func(*Command) error) { if c.usageFunc != nil { return c.usageFunc } if c.HasParent() { return c.Parent().UsageFunc() } return func(c *Command) error { c.mergePersistentFlags() err := tmpl(c.OutOrStderr(), c.UsageTemplate(), c) if err != nil { c.PrintErrln(err) } return err } } // Usage puts out the usage for the command. // Used when a user provides invalid input. // Can be defined by user by overriding UsageFunc. func (c *Command) Usage() error { return c.UsageFunc()(c) } // HelpFunc returns either the function set by SetHelpFunc for this command // or a parent, or it returns a function with default help behavior. func (c *Command) HelpFunc() func(*Command, []string) { if c.helpFunc != nil { return c.helpFunc } if c.HasParent() { return c.Parent().HelpFunc() } return func(c *Command, a []string) { c.mergePersistentFlags() // The help should be sent to stdout // See https://github.com/spf13/cobra/issues/1002 err := tmpl(c.OutOrStdout(), c.HelpTemplate(), c) if err != nil { c.PrintErrln(err) } } } // Help puts out the help for the command. // Used when a user calls help [command]. // Can be defined by user by overriding HelpFunc. func (c *Command) Help() error { c.HelpFunc()(c, []string{}) return nil } // UsageString returns usage string. func (c *Command) UsageString() string { // Storing normal writers tmpOutput := c.outWriter tmpErr := c.errWriter bb := new(bytes.Buffer) c.outWriter = bb c.errWriter = bb CheckErr(c.Usage()) // Setting things back to normal c.outWriter = tmpOutput c.errWriter = tmpErr return bb.String() } // FlagErrorFunc returns either the function set by SetFlagErrorFunc for this // command or a parent, or it returns a function which returns the original // error. func (c *Command) FlagErrorFunc() (f func(*Command, error) error) { if c.flagErrorFunc != nil { return c.flagErrorFunc } if c.HasParent() { return c.parent.FlagErrorFunc() } return func(c *Command, err error) error { return err } } var minUsagePadding = 25 // UsagePadding return padding for the usage. func (c *Command) UsagePadding() int { if c.parent == nil || minUsagePadding > c.parent.commandsMaxUseLen { return minUsagePadding } return c.parent.commandsMaxUseLen } var minCommandPathPadding = 11 // CommandPathPadding return padding for the command path. func (c *Command) CommandPathPadding() int { if c.parent == nil || minCommandPathPadding > c.parent.commandsMaxCommandPathLen { return minCommandPathPadding } return c.parent.commandsMaxCommandPathLen } var minNamePadding = 11 // NamePadding returns padding for the name. func (c *Command) NamePadding() int { if c.parent == nil || minNamePadding > c.parent.commandsMaxNameLen { return minNamePadding } return c.parent.commandsMaxNameLen } // UsageTemplate returns usage template for the command. func (c *Command) UsageTemplate() string { if c.usageTemplate != "" { return c.usageTemplate } if c.HasParent() { return c.parent.UsageTemplate() } return `Usage:{{if .Runnable}} {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}} {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}} Aliases: {{.NameAndAliases}}{{end}}{{if .HasExample}} Examples: {{.Example}}{{end}}{{if .HasAvailableSubCommands}}{{$cmds := .Commands}}{{if eq (len .Groups) 0}} Available Commands:{{range $cmds}}{{if (or .IsAvailableCommand (eq .Name "help"))}} {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{else}}{{range $group := .Groups}} {{.Title}}{{range $cmds}}{{if (and (eq .GroupID $group.ID) (or .IsAvailableCommand (eq .Name "help")))}} {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if not .AllChildCommandsHaveGroup}} Additional Commands:{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}} {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} Flags: {{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} Global Flags: {{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}} Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}} Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}} ` } // HelpTemplate return help template for the command. func (c *Command) HelpTemplate() string { if c.helpTemplate != "" { return c.helpTemplate } if c.HasParent() { return c.parent.HelpTemplate() } return `{{with (or .Long .Short)}}{{. | trimTrailingWhitespaces}} {{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}` } // VersionTemplate return version template for the command. func (c *Command) VersionTemplate() string { if c.versionTemplate != "" { return c.versionTemplate } if c.HasParent() { return c.parent.VersionTemplate() } return `{{with .Name}}{{printf "%s " .}}{{end}}{{printf "version %s" .Version}} ` } func hasNoOptDefVal(name string, fs *flag.FlagSet) bool { flag := fs.Lookup(name) if flag == nil { return false } return flag.NoOptDefVal != "" } func shortHasNoOptDefVal(name string, fs *flag.FlagSet) bool { if len(name) == 0 { return false } flag := fs.ShorthandLookup(name[:1]) if flag == nil { return false } return flag.NoOptDefVal != "" } func stripFlags(args []string, c *Command) []string { if len(args) == 0 { return args } c.mergePersistentFlags() commands := []string{} flags := c.Flags() Loop: for len(args) > 0 { s := args[0] args = args[1:] switch { case s == "--": // "--" terminates the flags break Loop case strings.HasPrefix(s, "--") && !strings.Contains(s, "=") && !hasNoOptDefVal(s[2:], flags): // If '--flag arg' then // delete arg from args. fallthrough // (do the same as below) case strings.HasPrefix(s, "-") && !strings.Contains(s, "=") && len(s) == 2 && !shortHasNoOptDefVal(s[1:], flags): // If '-f arg' then // delete 'arg' from args or break the loop if len(args) <= 1. if len(args) <= 1 { break Loop } else { args = args[1:] continue } case s != "" && !strings.HasPrefix(s, "-"): commands = append(commands, s) } } return commands } // argsMinusFirstX removes only the first x from args. Otherwise, commands that look like // openshift admin policy add-role-to-user admin my-user, lose the admin argument (arg[4]). // Special care needs to be taken not to remove a flag value. func (c *Command) argsMinusFirstX(args []string, x string) []string { if len(args) == 0 { return args } c.mergePersistentFlags() flags := c.Flags() Loop: for pos := 0; pos < len(args); pos++ { s := args[pos] switch { case s == "--": // -- means we have reached the end of the parseable args. Break out of the loop now. break Loop case strings.HasPrefix(s, "--") && !strings.Contains(s, "=") && !hasNoOptDefVal(s[2:], flags): fallthrough case strings.HasPrefix(s, "-") && !strings.Contains(s, "=") && len(s) == 2 && !shortHasNoOptDefVal(s[1:], flags): // This is a flag without a default value, and an equal sign is not used. Increment pos in order to skip // over the next arg, because that is the value of this flag. pos++ continue case !strings.HasPrefix(s, "-"): // This is not a flag or a flag value. Check to see if it matches what we're looking for, and if so, // return the args, excluding the one at this position. if s == x { ret := []string{} ret = append(ret, args[:pos]...) ret = append(ret, args[pos+1:]...) return ret } } } return args } func isFlagArg(arg string) bool { return ((len(arg) >= 3 && arg[0:2] == "--") || (len(arg) >= 2 && arg[0] == '-' && arg[1] != '-')) } // Find the target command given the args and command tree // Meant to be run on the highest node. Only searches down. func (c *Command) Find(args []string) (*Command, []string, error) { var innerfind func(*Command, []string) (*Command, []string) innerfind = func(c *Command, innerArgs []string) (*Command, []string) { argsWOflags := stripFlags(innerArgs, c) if len(argsWOflags) == 0 { return c, innerArgs } nextSubCmd := argsWOflags[0] cmd := c.findNext(nextSubCmd) if cmd != nil { return innerfind(cmd, c.argsMinusFirstX(innerArgs, nextSubCmd)) } return c, innerArgs } commandFound, a := innerfind(c, args) if commandFound.Args == nil { return commandFound, a, legacyArgs(commandFound, stripFlags(a, commandFound)) } return commandFound, a, nil } func (c *Command) findSuggestions(arg string) string { if c.DisableSuggestions { return "" } if c.SuggestionsMinimumDistance <= 0 { c.SuggestionsMinimumDistance = 2 } suggestionsString := "" if suggestions := c.SuggestionsFor(arg); len(suggestions) > 0 { suggestionsString += "\n\nDid you mean this?\n" for _, s := range suggestions { suggestionsString += fmt.Sprintf("\t%v\n", s) } } return suggestionsString } func (c *Command) findNext(next string) *Command { matches := make([]*Command, 0) for _, cmd := range c.commands { if commandNameMatches(cmd.Name(), next) || cmd.HasAlias(next) { cmd.commandCalledAs.name = next return cmd } if EnablePrefixMatching && cmd.hasNameOrAliasPrefix(next) { matches = append(matches, cmd) } } if len(matches) == 1 { return matches[0] } return nil } // Traverse the command tree to find the command, and parse args for // each parent. func (c *Command) Traverse(args []string) (*Command, []string, error) { flags := []string{} inFlag := false for i, arg := range args { switch { // A long flag with a space separated value case strings.HasPrefix(arg, "--") && !strings.Contains(arg, "="): // TODO: this isn't quite right, we should really check ahead for 'true' or 'false' inFlag = !hasNoOptDefVal(arg[2:], c.Flags()) flags = append(flags, arg) continue // A short flag with a space separated value case strings.HasPrefix(arg, "-") && !strings.Contains(arg, "=") && len(arg) == 2 && !shortHasNoOptDefVal(arg[1:], c.Flags()): inFlag = true flags = append(flags, arg) continue // The value for a flag case inFlag: inFlag = false flags = append(flags, arg) continue // A flag without a value, or with an `=` separated value case isFlagArg(arg): flags = append(flags, arg) continue } cmd := c.findNext(arg) if cmd == nil { return c, args, nil } if err := c.ParseFlags(flags); err != nil { return nil, args, err } return cmd.Traverse(args[i+1:]) } return c, args, nil } // SuggestionsFor provides suggestions for the typedName. func (c *Command) SuggestionsFor(typedName string) []string { suggestions := []string{} for _, cmd := range c.commands { if cmd.IsAvailableCommand() { levenshteinDistance := ld(typedName, cmd.Name(), true) suggestByLevenshtein := levenshteinDistance <= c.SuggestionsMinimumDistance suggestByPrefix := strings.HasPrefix(strings.ToLower(cmd.Name()), strings.ToLower(typedName)) if suggestByLevenshtein || suggestByPrefix { suggestions = append(suggestions, cmd.Name()) } for _, explicitSuggestion := range cmd.SuggestFor { if strings.EqualFold(typedName, explicitSuggestion) { suggestions = append(suggestions, cmd.Name()) } } } } return suggestions } // VisitParents visits all parents of the command and invokes fn on each parent. func (c *Command) VisitParents(fn func(*Command)) { if c.HasParent() { fn(c.Parent()) c.Parent().VisitParents(fn) } } // Root finds root command. func (c *Command) Root() *Command { if c.HasParent() { return c.Parent().Root() } return c } // ArgsLenAtDash will return the length of c.Flags().Args at the moment // when a -- was found during args parsing. func (c *Command) ArgsLenAtDash() int { return c.Flags().ArgsLenAtDash() } func (c *Command) execute(a []string) (err error) { if c == nil { return fmt.Errorf("Called Execute() on a nil Command") } if len(c.Deprecated) > 0 { c.Printf("Command %q is deprecated, %s\n", c.Name(), c.Deprecated) } // initialize help and version flag at the last point possible to allow for user // overriding c.InitDefaultHelpFlag() c.InitDefaultVersionFlag() err = c.ParseFlags(a) if err != nil { return c.FlagErrorFunc()(c, err) } // If help is called, regardless of other flags, return we want help. // Also say we need help if the command isn't runnable. helpVal, err := c.Flags().GetBool("help") if err != nil { // should be impossible to get here as we always declare a help // flag in InitDefaultHelpFlag() c.Println("\"help\" flag declared as non-bool. Please correct your code") return err } if helpVal { return flag.ErrHelp } // for back-compat, only add version flag behavior if version is defined if c.Version != "" { versionVal, err := c.Flags().GetBool("version") if err != nil { c.Println("\"version\" flag declared as non-bool. Please correct your code") return err } if versionVal { err := tmpl(c.OutOrStdout(), c.VersionTemplate(), c) if err != nil { c.Println(err) } return err } } if !c.Runnable() { return flag.ErrHelp } c.preRun() defer c.postRun() argWoFlags := c.Flags().Args() if c.DisableFlagParsing { argWoFlags = a } if err := c.ValidateArgs(argWoFlags); err != nil { return err } for p := c; p != nil; p = p.Parent() { if p.PersistentPreRunE != nil { if err := p.PersistentPreRunE(c, argWoFlags); err != nil { return err } break } else if p.PersistentPreRun != nil { p.PersistentPreRun(c, argWoFlags) break } } if c.PreRunE != nil { if err := c.PreRunE(c, argWoFlags); err != nil { return err } } else if c.PreRun != nil { c.PreRun(c, argWoFlags) } if err := c.ValidateRequiredFlags(); err != nil { return err } if err := c.ValidateFlagGroups(); err != nil { return err } if c.RunE != nil { if err := c.RunE(c, argWoFlags); err != nil { return err } } else { c.Run(c, argWoFlags) } if c.PostRunE != nil { if err := c.PostRunE(c, argWoFlags); err != nil { return err } } else if c.PostRun != nil { c.PostRun(c, argWoFlags) } for p := c; p != nil; p = p.Parent() { if p.PersistentPostRunE != nil { if err := p.PersistentPostRunE(c, argWoFlags); err != nil { return err } break } else if p.PersistentPostRun != nil { p.PersistentPostRun(c, argWoFlags) break } } return nil } func (c *Command) preRun() { for _, x := range initializers { x() } } func (c *Command) postRun() { for _, x := range finalizers { x() } } // ExecuteContext is the same as Execute(), but sets the ctx on the command. // Retrieve ctx by calling cmd.Context() inside your *Run lifecycle or ValidArgs // functions. func (c *Command) ExecuteContext(ctx context.Context) error { c.ctx = ctx return c.Execute() } // Execute uses the args (os.Args[1:] by default) // and run through the command tree finding appropriate matches // for commands and then corresponding flags. func (c *Command) Execute() error { _, err := c.ExecuteC() return err } // ExecuteContextC is the same as ExecuteC(), but sets the ctx on the command. // Retrieve ctx by calling cmd.Context() inside your *Run lifecycle or ValidArgs // functions. func (c *Command) ExecuteContextC(ctx context.Context) (*Command, error) { c.ctx = ctx return c.ExecuteC() } // ExecuteC executes the command. func (c *Command) ExecuteC() (cmd *Command, err error) { if c.ctx == nil { c.ctx = context.Background() } // Regardless of what command execute is called on, run on Root only if c.HasParent() { return c.Root().ExecuteC() } // windows hook if preExecHookFn != nil { preExecHookFn(c) } // initialize help at the last point to allow for user overriding c.InitDefaultHelpCmd() // initialize completion at the last point to allow for user overriding c.InitDefaultCompletionCmd() // Now that all commands have been created, let's make sure all groups // are properly created also c.checkCommandGroups() args := c.args // Workaround FAIL with "go test -v" or "cobra.test -test.v", see #155 if c.args == nil && filepath.Base(os.Args[0]) != "cobra.test" { args = os.Args[1:] } // initialize the hidden command to be used for shell completion c.initCompleteCmd(args) var flags []string if c.TraverseChildren { cmd, flags, err = c.Traverse(args) } else { cmd, flags, err = c.Find(args) } if err != nil { // If found parse to a subcommand and then failed, talk about the subcommand if cmd != nil { c = cmd } if !c.SilenceErrors { c.PrintErrln("Error:", err.Error()) c.PrintErrf("Run '%v --help' for usage.\n", c.CommandPath()) } return c, err } cmd.commandCalledAs.called = true if cmd.commandCalledAs.name == "" { cmd.commandCalledAs.name = cmd.Name() } // We have to pass global context to children command // if context is present on the parent command. if cmd.ctx == nil { cmd.ctx = c.ctx } err = cmd.execute(flags) if err != nil { // Always show help if requested, even if SilenceErrors is in // effect if errors.Is(err, flag.ErrHelp) { cmd.HelpFunc()(cmd, args) return cmd, nil } // If root command has SilenceErrors flagged, // all subcommands should respect it if !cmd.SilenceErrors && !c.SilenceErrors { c.PrintErrln("Error:", err.Error()) } // If root command has SilenceUsage flagged, // all subcommands should respect it if !cmd.SilenceUsage && !c.SilenceUsage { c.Println(cmd.UsageString()) } } return cmd, err } func (c *Command) ValidateArgs(args []string) error { if c.Args == nil { return ArbitraryArgs(c, args) } return c.Args(c, args) } // ValidateRequiredFlags validates all required flags are present and returns an error otherwise func (c *Command) ValidateRequiredFlags() error { if c.DisableFlagParsing { return nil } flags := c.Flags() missingFlagNames := []string{} flags.VisitAll(func(pflag *flag.Flag) { requiredAnnotation, found := pflag.Annotations[BashCompOneRequiredFlag] if !found { return } if (requiredAnnotation[0] == "true") && !pflag.Changed { missingFlagNames = append(missingFlagNames, pflag.Name) } }) if len(missingFlagNames) > 0 { return fmt.Errorf(`required flag(s) "%s" not set`, strings.Join(missingFlagNames, `", "`)) } return nil } // checkCommandGroups checks if a command has been added to a group that does not exists. // If so, we panic because it indicates a coding error that should be corrected. func (c *Command) checkCommandGroups() { for _, sub := range c.commands { // if Group is not defined let the developer know right away if sub.GroupID != "" && !c.ContainsGroup(sub.GroupID) { panic(fmt.Sprintf("group id '%s' is not defined for subcommand '%s'", sub.GroupID, sub.CommandPath())) } sub.checkCommandGroups() } } // InitDefaultHelpFlag adds default help flag to c. // It is called automatically by executing the c or by calling help and usage. // If c already has help flag, it will do nothing. func (c *Command) InitDefaultHelpFlag() { c.mergePersistentFlags() if c.Flags().Lookup("help") == nil { usage := "help for " if c.Name() == "" { usage += "this command" } else { usage += c.Name() } c.Flags().BoolP("help", "h", false, usage) _ = c.Flags().SetAnnotation("help", FlagSetByCobraAnnotation, []string{"true"}) } } // InitDefaultVersionFlag adds default version flag to c. // It is called automatically by executing the c. // If c already has a version flag, it will do nothing. // If c.Version is empty, it will do nothing. func (c *Command) InitDefaultVersionFlag() { if c.Version == "" { return } c.mergePersistentFlags() if c.Flags().Lookup("version") == nil { usage := "version for " if c.Name() == "" { usage += "this command" } else { usage += c.Name() } if c.Flags().ShorthandLookup("v") == nil { c.Flags().BoolP("version", "v", false, usage) } else { c.Flags().Bool("version", false, usage) } _ = c.Flags().SetAnnotation("version", FlagSetByCobraAnnotation, []string{"true"}) } } // InitDefaultHelpCmd adds default help command to c. // It is called automatically by executing the c or by calling help and usage. // If c already has help command or c has no subcommands, it will do nothing. func (c *Command) InitDefaultHelpCmd() { if !c.HasSubCommands() { return } if c.helpCommand == nil { c.helpCommand = &Command{ Use: "help [command]", Short: "Help about any command", Long: `Help provides help for any command in the application. Simply type ` + c.Name() + ` help [path to command] for full details.`, ValidArgsFunction: func(c *Command, args []string, toComplete string) ([]string, ShellCompDirective) { var completions []string cmd, _, e := c.Root().Find(args) if e != nil { return nil, ShellCompDirectiveNoFileComp } if cmd == nil { // Root help command. cmd = c.Root() } for _, subCmd := range cmd.Commands() { if subCmd.IsAvailableCommand() || subCmd == cmd.helpCommand { if strings.HasPrefix(subCmd.Name(), toComplete) { completions = append(completions, fmt.Sprintf("%s\t%s", subCmd.Name(), subCmd.Short)) } } } return completions, ShellCompDirectiveNoFileComp }, Run: func(c *Command, args []string) { cmd, _, e := c.Root().Find(args) if cmd == nil || e != nil { c.Printf("Unknown help topic %#q\n", args) CheckErr(c.Root().Usage()) } else { cmd.InitDefaultHelpFlag() // make possible 'help' flag to be shown cmd.InitDefaultVersionFlag() // make possible 'version' flag to be shown CheckErr(cmd.Help()) } }, GroupID: c.helpCommandGroupID, } } c.RemoveCommand(c.helpCommand) c.AddCommand(c.helpCommand) } // ResetCommands delete parent, subcommand and help command from c. func (c *Command) ResetCommands() { c.parent = nil c.commands = nil c.helpCommand = nil c.parentsPflags = nil } // Sorts commands by their names. type commandSorterByName []*Command func (c commandSorterByName) Len() int { return len(c) } func (c commandSorterByName) Swap(i, j int) { c[i], c[j] = c[j], c[i] } func (c commandSorterByName) Less(i, j int) bool { return c[i].Name() < c[j].Name() } // Commands returns a sorted slice of child commands. func (c *Command) Commands() []*Command { // do not sort commands if it already sorted or sorting was disabled if EnableCommandSorting && !c.commandsAreSorted { sort.Sort(commandSorterByName(c.commands)) c.commandsAreSorted = true } return c.commands } // AddCommand adds one or more commands to this parent command. func (c *Command) AddCommand(cmds ...*Command) { for i, x := range cmds { if cmds[i] == c { panic("Command can't be a child of itself") } cmds[i].parent = c // update max lengths usageLen := len(x.Use) if usageLen > c.commandsMaxUseLen { c.commandsMaxUseLen = usageLen } commandPathLen := len(x.CommandPath()) if commandPathLen > c.commandsMaxCommandPathLen { c.commandsMaxCommandPathLen = commandPathLen } nameLen := len(x.Name()) if nameLen > c.commandsMaxNameLen { c.commandsMaxNameLen = nameLen } // If global normalization function exists, update all children if c.globNormFunc != nil { x.SetGlobalNormalizationFunc(c.globNormFunc) } c.commands = append(c.commands, x) c.commandsAreSorted = false } } // Groups returns a slice of child command groups. func (c *Command) Groups() []*Group { return c.commandgroups } // AllChildCommandsHaveGroup returns if all subcommands are assigned to a group func (c *Command) AllChildCommandsHaveGroup() bool { for _, sub := range c.commands { if (sub.IsAvailableCommand() || sub == c.helpCommand) && sub.GroupID == "" { return false } } return true } // ContainsGroup return if groupID exists in the list of command groups. func (c *Command) ContainsGroup(groupID string) bool { for _, x := range c.commandgroups { if x.ID == groupID { return true } } return false } // AddGroup adds one or more command groups to this parent command. func (c *Command) AddGroup(groups ...*Group) { c.commandgroups = append(c.commandgroups, groups...) } // RemoveCommand removes one or more commands from a parent command. func (c *Command) RemoveCommand(cmds ...*Command) { commands := []*Command{} main: for _, command := range c.commands { for _, cmd := range cmds { if command == cmd { command.parent = nil continue main } } commands = append(commands, command) } c.commands = commands // recompute all lengths c.commandsMaxUseLen = 0 c.commandsMaxCommandPathLen = 0 c.commandsMaxNameLen = 0 for _, command := range c.commands { usageLen := len(command.Use) if usageLen > c.commandsMaxUseLen { c.commandsMaxUseLen = usageLen } commandPathLen := len(command.CommandPath()) if commandPathLen > c.commandsMaxCommandPathLen { c.commandsMaxCommandPathLen = commandPathLen } nameLen := len(command.Name()) if nameLen > c.commandsMaxNameLen { c.commandsMaxNameLen = nameLen } } } // Print is a convenience method to Print to the defined output, fallback to Stderr if not set. func (c *Command) Print(i ...interface{}) { fmt.Fprint(c.OutOrStderr(), i...) } // Println is a convenience method to Println to the defined output, fallback to Stderr if not set. func (c *Command) Println(i ...interface{}) { c.Print(fmt.Sprintln(i...)) } // Printf is a convenience method to Printf to the defined output, fallback to Stderr if not set. func (c *Command) Printf(format string, i ...interface{}) { c.Print(fmt.Sprintf(format, i...)) } // PrintErr is a convenience method to Print to the defined Err output, fallback to Stderr if not set. func (c *Command) PrintErr(i ...interface{}) { fmt.Fprint(c.ErrOrStderr(), i...) } // PrintErrln is a convenience method to Println to the defined Err output, fallback to Stderr if not set. func (c *Command) PrintErrln(i ...interface{}) { c.PrintErr(fmt.Sprintln(i...)) } // PrintErrf is a convenience method to Printf to the defined Err output, fallback to Stderr if not set. func (c *Command) PrintErrf(format string, i ...interface{}) { c.PrintErr(fmt.Sprintf(format, i...)) } // CommandPath returns the full path to this command. func (c *Command) CommandPath() string { if c.HasParent() { return c.Parent().CommandPath() + " " + c.Name() } return c.Name() } // UseLine puts out the full usage for a given command (including parents). func (c *Command) UseLine() string { var useline string if c.HasParent() { useline = c.parent.CommandPath() + " " + c.Use } else { useline = c.Use } if c.DisableFlagsInUseLine { return useline } if c.HasAvailableFlags() && !strings.Contains(useline, "[flags]") { useline += " [flags]" } return useline } // DebugFlags used to determine which flags have been assigned to which commands // and which persist. func (c *Command) DebugFlags() { c.Println("DebugFlags called on", c.Name()) var debugflags func(*Command) debugflags = func(x *Command) { if x.HasFlags() || x.HasPersistentFlags() { c.Println(x.Name()) } if x.HasFlags() { x.flags.VisitAll(func(f *flag.Flag) { if x.HasPersistentFlags() && x.persistentFlag(f.Name) != nil { c.Println(" -"+f.Shorthand+",", "--"+f.Name, "["+f.DefValue+"]", "", f.Value, " [LP]") } else { c.Println(" -"+f.Shorthand+",", "--"+f.Name, "["+f.DefValue+"]", "", f.Value, " [L]") } }) } if x.HasPersistentFlags() { x.pflags.VisitAll(func(f *flag.Flag) { if x.HasFlags() { if x.flags.Lookup(f.Name) == nil { c.Println(" -"+f.Shorthand+",", "--"+f.Name, "["+f.DefValue+"]", "", f.Value, " [P]") } } else { c.Println(" -"+f.Shorthand+",", "--"+f.Name, "["+f.DefValue+"]", "", f.Value, " [P]") } }) } c.Println(x.flagErrorBuf) if x.HasSubCommands() { for _, y := range x.commands { debugflags(y) } } } debugflags(c) } // Name returns the command's name: the first word in the use line. func (c *Command) Name() string { name := c.Use i := strings.Index(name, " ") if i >= 0 { name = name[:i] } return name } // HasAlias determines if a given string is an alias of the command. func (c *Command) HasAlias(s string) bool { for _, a := range c.Aliases { if commandNameMatches(a, s) { return true } } return false } // CalledAs returns the command name or alias that was used to invoke // this command or an empty string if the command has not been called. func (c *Command) CalledAs() string { if c.commandCalledAs.called { return c.commandCalledAs.name } return "" } // hasNameOrAliasPrefix returns true if the Name or any of aliases start // with prefix func (c *Command) hasNameOrAliasPrefix(prefix string) bool { if strings.HasPrefix(c.Name(), prefix) { c.commandCalledAs.name = c.Name() return true } for _, alias := range c.Aliases { if strings.HasPrefix(alias, prefix) { c.commandCalledAs.name = alias return true } } return false } // NameAndAliases returns a list of the command name and all aliases func (c *Command) NameAndAliases() string { return strings.Join(append([]string{c.Name()}, c.Aliases...), ", ") } // HasExample determines if the command has example. func (c *Command) HasExample() bool { return len(c.Example) > 0 } // Runnable determines if the command is itself runnable. func (c *Command) Runnable() bool { return c.Run != nil || c.RunE != nil } // HasSubCommands determines if the command has children commands. func (c *Command) HasSubCommands() bool { return len(c.commands) > 0 } // IsAvailableCommand determines if a command is available as a non-help command // (this includes all non deprecated/hidden commands). func (c *Command) IsAvailableCommand() bool { if len(c.Deprecated) != 0 || c.Hidden { return false } if c.HasParent() && c.Parent().helpCommand == c { return false } if c.Runnable() || c.HasAvailableSubCommands() { return true } return false } // IsAdditionalHelpTopicCommand determines if a command is an additional // help topic command; additional help topic command is determined by the // fact that it is NOT runnable/hidden/deprecated, and has no sub commands that // are runnable/hidden/deprecated. // Concrete example: https://github.com/spf13/cobra/issues/393#issuecomment-282741924. func (c *Command) IsAdditionalHelpTopicCommand() bool { // if a command is runnable, deprecated, or hidden it is not a 'help' command if c.Runnable() || len(c.Deprecated) != 0 || c.Hidden { return false } // if any non-help sub commands are found, the command is not a 'help' command for _, sub := range c.commands { if !sub.IsAdditionalHelpTopicCommand() { return false } } // the command either has no sub commands, or no non-help sub commands return true } // HasHelpSubCommands determines if a command has any available 'help' sub commands // that need to be shown in the usage/help default template under 'additional help // topics'. func (c *Command) HasHelpSubCommands() bool { // return true on the first found available 'help' sub command for _, sub := range c.commands { if sub.IsAdditionalHelpTopicCommand() { return true } } // the command either has no sub commands, or no available 'help' sub commands return false } // HasAvailableSubCommands determines if a command has available sub commands that // need to be shown in the usage/help default template under 'available commands'. func (c *Command) HasAvailableSubCommands() bool { // return true on the first found available (non deprecated/help/hidden) // sub command for _, sub := range c.commands { if sub.IsAvailableCommand() { return true } } // the command either has no sub commands, or no available (non deprecated/help/hidden) // sub commands return false } // HasParent determines if the command is a child command. func (c *Command) HasParent() bool { return c.parent != nil } // GlobalNormalizationFunc returns the global normalization function or nil if it doesn't exist. func (c *Command) GlobalNormalizationFunc() func(f *flag.FlagSet, name string) flag.NormalizedName { return c.globNormFunc } // Flags returns the complete FlagSet that applies // to this command (local and persistent declared here and by all parents). func (c *Command) Flags() *flag.FlagSet { if c.flags == nil { c.flags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) if c.flagErrorBuf == nil { c.flagErrorBuf = new(bytes.Buffer) } c.flags.SetOutput(c.flagErrorBuf) } return c.flags } // LocalNonPersistentFlags are flags specific to this command which will NOT persist to subcommands. func (c *Command) LocalNonPersistentFlags() *flag.FlagSet { persistentFlags := c.PersistentFlags() out := flag.NewFlagSet(c.Name(), flag.ContinueOnError) c.LocalFlags().VisitAll(func(f *flag.Flag) { if persistentFlags.Lookup(f.Name) == nil { out.AddFlag(f) } }) return out } // LocalFlags returns the local FlagSet specifically set in the current command. func (c *Command) LocalFlags() *flag.FlagSet { c.mergePersistentFlags() if c.lflags == nil { c.lflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) if c.flagErrorBuf == nil { c.flagErrorBuf = new(bytes.Buffer) } c.lflags.SetOutput(c.flagErrorBuf) } c.lflags.SortFlags = c.Flags().SortFlags if c.globNormFunc != nil { c.lflags.SetNormalizeFunc(c.globNormFunc) } addToLocal := func(f *flag.Flag) { // Add the flag if it is not a parent PFlag, or it shadows a parent PFlag if c.lflags.Lookup(f.Name) == nil && f != c.parentsPflags.Lookup(f.Name) { c.lflags.AddFlag(f) } } c.Flags().VisitAll(addToLocal) c.PersistentFlags().VisitAll(addToLocal) return c.lflags } // InheritedFlags returns all flags which were inherited from parent commands. func (c *Command) InheritedFlags() *flag.FlagSet { c.mergePersistentFlags() if c.iflags == nil { c.iflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) if c.flagErrorBuf == nil { c.flagErrorBuf = new(bytes.Buffer) } c.iflags.SetOutput(c.flagErrorBuf) } local := c.LocalFlags() if c.globNormFunc != nil { c.iflags.SetNormalizeFunc(c.globNormFunc) } c.parentsPflags.VisitAll(func(f *flag.Flag) { if c.iflags.Lookup(f.Name) == nil && local.Lookup(f.Name) == nil { c.iflags.AddFlag(f) } }) return c.iflags } // NonInheritedFlags returns all flags which were not inherited from parent commands. func (c *Command) NonInheritedFlags() *flag.FlagSet { return c.LocalFlags() } // PersistentFlags returns the persistent FlagSet specifically set in the current command. func (c *Command) PersistentFlags() *flag.FlagSet { if c.pflags == nil { c.pflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) if c.flagErrorBuf == nil { c.flagErrorBuf = new(bytes.Buffer) } c.pflags.SetOutput(c.flagErrorBuf) } return c.pflags } // ResetFlags deletes all flags from command. func (c *Command) ResetFlags() { c.flagErrorBuf = new(bytes.Buffer) c.flagErrorBuf.Reset() c.flags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) c.flags.SetOutput(c.flagErrorBuf) c.pflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) c.pflags.SetOutput(c.flagErrorBuf) c.lflags = nil c.iflags = nil c.parentsPflags = nil } // HasFlags checks if the command contains any flags (local plus persistent from the entire structure). func (c *Command) HasFlags() bool { return c.Flags().HasFlags() } // HasPersistentFlags checks if the command contains persistent flags. func (c *Command) HasPersistentFlags() bool { return c.PersistentFlags().HasFlags() } // HasLocalFlags checks if the command has flags specifically declared locally. func (c *Command) HasLocalFlags() bool { return c.LocalFlags().HasFlags() } // HasInheritedFlags checks if the command has flags inherited from its parent command. func (c *Command) HasInheritedFlags() bool { return c.InheritedFlags().HasFlags() } // HasAvailableFlags checks if the command contains any flags (local plus persistent from the entire // structure) which are not hidden or deprecated. func (c *Command) HasAvailableFlags() bool { return c.Flags().HasAvailableFlags() } // HasAvailablePersistentFlags checks if the command contains persistent flags which are not hidden or deprecated. func (c *Command) HasAvailablePersistentFlags() bool { return c.PersistentFlags().HasAvailableFlags() } // HasAvailableLocalFlags checks if the command has flags specifically declared locally which are not hidden // or deprecated. func (c *Command) HasAvailableLocalFlags() bool { return c.LocalFlags().HasAvailableFlags() } // HasAvailableInheritedFlags checks if the command has flags inherited from its parent command which are // not hidden or deprecated. func (c *Command) HasAvailableInheritedFlags() bool { return c.InheritedFlags().HasAvailableFlags() } // Flag climbs up the command tree looking for matching flag. func (c *Command) Flag(name string) (flag *flag.Flag) { flag = c.Flags().Lookup(name) if flag == nil { flag = c.persistentFlag(name) } return } // Recursively find matching persistent flag. func (c *Command) persistentFlag(name string) (flag *flag.Flag) { if c.HasPersistentFlags() { flag = c.PersistentFlags().Lookup(name) } if flag == nil { c.updateParentsPflags() flag = c.parentsPflags.Lookup(name) } return } // ParseFlags parses persistent flag tree and local flags. func (c *Command) ParseFlags(args []string) error { if c.DisableFlagParsing { return nil } if c.flagErrorBuf == nil { c.flagErrorBuf = new(bytes.Buffer) } beforeErrorBufLen := c.flagErrorBuf.Len() c.mergePersistentFlags() // do it here after merging all flags and just before parse c.Flags().ParseErrorsWhitelist = flag.ParseErrorsWhitelist(c.FParseErrWhitelist) err := c.Flags().Parse(args) // Print warnings if they occurred (e.g. deprecated flag messages). if c.flagErrorBuf.Len()-beforeErrorBufLen > 0 && err == nil { c.Print(c.flagErrorBuf.String()) } return err } // Parent returns a commands parent command. func (c *Command) Parent() *Command { return c.parent } // mergePersistentFlags merges c.PersistentFlags() to c.Flags() // and adds missing persistent flags of all parents. func (c *Command) mergePersistentFlags() { c.updateParentsPflags() c.Flags().AddFlagSet(c.PersistentFlags()) c.Flags().AddFlagSet(c.parentsPflags) } // updateParentsPflags updates c.parentsPflags by adding // new persistent flags of all parents. // If c.parentsPflags == nil, it makes new. func (c *Command) updateParentsPflags() { if c.parentsPflags == nil { c.parentsPflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) c.parentsPflags.SetOutput(c.flagErrorBuf) c.parentsPflags.SortFlags = false } if c.globNormFunc != nil { c.parentsPflags.SetNormalizeFunc(c.globNormFunc) } c.Root().PersistentFlags().AddFlagSet(flag.CommandLine) c.VisitParents(func(parent *Command) { c.parentsPflags.AddFlagSet(parent.PersistentFlags()) }) } // commandNameMatches checks if two command names are equal // taking into account case sensitivity according to // EnableCaseInsensitive global configuration. func commandNameMatches(s string, t string) bool { if EnableCaseInsensitive { return strings.EqualFold(s, t) } return s == t } ================================================ FILE: vendor/github.com/spf13/cobra/command_notwin.go ================================================ // Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build !windows // +build !windows package cobra var preExecHookFn func(*Command) ================================================ FILE: vendor/github.com/spf13/cobra/command_win.go ================================================ // Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //go:build windows // +build windows package cobra import ( "fmt" "os" "time" "github.com/inconshreveable/mousetrap" ) var preExecHookFn = preExecHook func preExecHook(c *Command) { if MousetrapHelpText != "" && mousetrap.StartedByExplorer() { c.Print(MousetrapHelpText) if MousetrapDisplayDuration > 0 { time.Sleep(MousetrapDisplayDuration) } else { c.Println("Press return to continue...") fmt.Scanln() } os.Exit(1) } } ================================================ FILE: vendor/github.com/spf13/cobra/completions.go ================================================ // Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cobra import ( "fmt" "os" "strings" "sync" "github.com/spf13/pflag" ) const ( // ShellCompRequestCmd is the name of the hidden command that is used to request // completion results from the program. It is used by the shell completion scripts. ShellCompRequestCmd = "__complete" // ShellCompNoDescRequestCmd is the name of the hidden command that is used to request // completion results without their description. It is used by the shell completion scripts. ShellCompNoDescRequestCmd = "__completeNoDesc" ) // Global map of flag completion functions. Make sure to use flagCompletionMutex before you try to read and write from it. var flagCompletionFunctions = map[*pflag.Flag]func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective){} // lock for reading and writing from flagCompletionFunctions var flagCompletionMutex = &sync.RWMutex{} // ShellCompDirective is a bit map representing the different behaviors the shell // can be instructed to have once completions have been provided. type ShellCompDirective int type flagCompError struct { subCommand string flagName string } func (e *flagCompError) Error() string { return "Subcommand '" + e.subCommand + "' does not support flag '" + e.flagName + "'" } const ( // ShellCompDirectiveError indicates an error occurred and completions should be ignored. ShellCompDirectiveError ShellCompDirective = 1 << iota // ShellCompDirectiveNoSpace indicates that the shell should not add a space // after the completion even if there is a single completion provided. ShellCompDirectiveNoSpace // ShellCompDirectiveNoFileComp indicates that the shell should not provide // file completion even when no completion is provided. ShellCompDirectiveNoFileComp // ShellCompDirectiveFilterFileExt indicates that the provided completions // should be used as file extension filters. // For flags, using Command.MarkFlagFilename() and Command.MarkPersistentFlagFilename() // is a shortcut to using this directive explicitly. The BashCompFilenameExt // annotation can also be used to obtain the same behavior for flags. ShellCompDirectiveFilterFileExt // ShellCompDirectiveFilterDirs indicates that only directory names should // be provided in file completion. To request directory names within another // directory, the returned completions should specify the directory within // which to search. The BashCompSubdirsInDir annotation can be used to // obtain the same behavior but only for flags. ShellCompDirectiveFilterDirs // ShellCompDirectiveKeepOrder indicates that the shell should preserve the order // in which the completions are provided ShellCompDirectiveKeepOrder // =========================================================================== // All directives using iota should be above this one. // For internal use. shellCompDirectiveMaxValue // ShellCompDirectiveDefault indicates to let the shell perform its default // behavior after completions have been provided. // This one must be last to avoid messing up the iota count. ShellCompDirectiveDefault ShellCompDirective = 0 ) const ( // Constants for the completion command compCmdName = "completion" compCmdNoDescFlagName = "no-descriptions" compCmdNoDescFlagDesc = "disable completion descriptions" compCmdNoDescFlagDefault = false ) // CompletionOptions are the options to control shell completion type CompletionOptions struct { // DisableDefaultCmd prevents Cobra from creating a default 'completion' command DisableDefaultCmd bool // DisableNoDescFlag prevents Cobra from creating the '--no-descriptions' flag // for shells that support completion descriptions DisableNoDescFlag bool // DisableDescriptions turns off all completion descriptions for shells // that support them DisableDescriptions bool // HiddenDefaultCmd makes the default 'completion' command hidden HiddenDefaultCmd bool } // NoFileCompletions can be used to disable file completion for commands that should // not trigger file completions. func NoFileCompletions(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { return nil, ShellCompDirectiveNoFileComp } // FixedCompletions can be used to create a completion function which always // returns the same results. func FixedCompletions(choices []string, directive ShellCompDirective) func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { return func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { return choices, directive } } // RegisterFlagCompletionFunc should be called to register a function to provide completion for a flag. func (c *Command) RegisterFlagCompletionFunc(flagName string, f func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective)) error { flag := c.Flag(flagName) if flag == nil { return fmt.Errorf("RegisterFlagCompletionFunc: flag '%s' does not exist", flagName) } flagCompletionMutex.Lock() defer flagCompletionMutex.Unlock() if _, exists := flagCompletionFunctions[flag]; exists { return fmt.Errorf("RegisterFlagCompletionFunc: flag '%s' already registered", flagName) } flagCompletionFunctions[flag] = f return nil } // Returns a string listing the different directive enabled in the specified parameter func (d ShellCompDirective) string() string { var directives []string if d&ShellCompDirectiveError != 0 { directives = append(directives, "ShellCompDirectiveError") } if d&ShellCompDirectiveNoSpace != 0 { directives = append(directives, "ShellCompDirectiveNoSpace") } if d&ShellCompDirectiveNoFileComp != 0 { directives = append(directives, "ShellCompDirectiveNoFileComp") } if d&ShellCompDirectiveFilterFileExt != 0 { directives = append(directives, "ShellCompDirectiveFilterFileExt") } if d&ShellCompDirectiveFilterDirs != 0 { directives = append(directives, "ShellCompDirectiveFilterDirs") } if d&ShellCompDirectiveKeepOrder != 0 { directives = append(directives, "ShellCompDirectiveKeepOrder") } if len(directives) == 0 { directives = append(directives, "ShellCompDirectiveDefault") } if d >= shellCompDirectiveMaxValue { return fmt.Sprintf("ERROR: unexpected ShellCompDirective value: %d", d) } return strings.Join(directives, ", ") } // initCompleteCmd adds a special hidden command that can be used to request custom completions. func (c *Command) initCompleteCmd(args []string) { completeCmd := &Command{ Use: fmt.Sprintf("%s [command-line]", ShellCompRequestCmd), Aliases: []string{ShellCompNoDescRequestCmd}, DisableFlagsInUseLine: true, Hidden: true, DisableFlagParsing: true, Args: MinimumNArgs(1), Short: "Request shell completion choices for the specified command-line", Long: fmt.Sprintf("%[2]s is a special command that is used by the shell completion logic\n%[1]s", "to request completion choices for the specified command-line.", ShellCompRequestCmd), Run: func(cmd *Command, args []string) { finalCmd, completions, directive, err := cmd.getCompletions(args) if err != nil { CompErrorln(err.Error()) // Keep going for multiple reasons: // 1- There could be some valid completions even though there was an error // 2- Even without completions, we need to print the directive } noDescriptions := (cmd.CalledAs() == ShellCompNoDescRequestCmd) for _, comp := range completions { if GetActiveHelpConfig(finalCmd) == activeHelpGlobalDisable { // Remove all activeHelp entries in this case if strings.HasPrefix(comp, activeHelpMarker) { continue } } if noDescriptions { // Remove any description that may be included following a tab character. comp = strings.Split(comp, "\t")[0] } // Make sure we only write the first line to the output. // This is needed if a description contains a linebreak. // Otherwise the shell scripts will interpret the other lines as new flags // and could therefore provide a wrong completion. comp = strings.Split(comp, "\n")[0] // Finally trim the completion. This is especially important to get rid // of a trailing tab when there are no description following it. // For example, a sub-command without a description should not be completed // with a tab at the end (or else zsh will show a -- following it // although there is no description). comp = strings.TrimSpace(comp) // Print each possible completion to stdout for the completion script to consume. fmt.Fprintln(finalCmd.OutOrStdout(), comp) } // As the last printout, print the completion directive for the completion script to parse. // The directive integer must be that last character following a single colon (:). // The completion script expects : fmt.Fprintf(finalCmd.OutOrStdout(), ":%d\n", directive) // Print some helpful info to stderr for the user to understand. // Output from stderr must be ignored by the completion script. fmt.Fprintf(finalCmd.ErrOrStderr(), "Completion ended with directive: %s\n", directive.string()) }, } c.AddCommand(completeCmd) subCmd, _, err := c.Find(args) if err != nil || subCmd.Name() != ShellCompRequestCmd { // Only create this special command if it is actually being called. // This reduces possible side-effects of creating such a command; // for example, having this command would cause problems to a // cobra program that only consists of the root command, since this // command would cause the root command to suddenly have a subcommand. c.RemoveCommand(completeCmd) } } func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDirective, error) { // The last argument, which is not completely typed by the user, // should not be part of the list of arguments toComplete := args[len(args)-1] trimmedArgs := args[:len(args)-1] var finalCmd *Command var finalArgs []string var err error // Find the real command for which completion must be performed // check if we need to traverse here to parse local flags on parent commands if c.Root().TraverseChildren { finalCmd, finalArgs, err = c.Root().Traverse(trimmedArgs) } else { // For Root commands that don't specify any value for their Args fields, when we call // Find(), if those Root commands don't have any sub-commands, they will accept arguments. // However, because we have added the __complete sub-command in the current code path, the // call to Find() -> legacyArgs() will return an error if there are any arguments. // To avoid this, we first remove the __complete command to get back to having no sub-commands. rootCmd := c.Root() if len(rootCmd.Commands()) == 1 { rootCmd.RemoveCommand(c) } finalCmd, finalArgs, err = rootCmd.Find(trimmedArgs) } if err != nil { // Unable to find the real command. E.g., someInvalidCmd return c, []string{}, ShellCompDirectiveDefault, fmt.Errorf("Unable to find a command for arguments: %v", trimmedArgs) } finalCmd.ctx = c.ctx // These flags are normally added when `execute()` is called on `finalCmd`, // however, when doing completion, we don't call `finalCmd.execute()`. // Let's add the --help and --version flag ourselves. finalCmd.InitDefaultHelpFlag() finalCmd.InitDefaultVersionFlag() // Check if we are doing flag value completion before parsing the flags. // This is important because if we are completing a flag value, we need to also // remove the flag name argument from the list of finalArgs or else the parsing // could fail due to an invalid value (incomplete) for the flag. flag, finalArgs, toComplete, flagErr := checkIfFlagCompletion(finalCmd, finalArgs, toComplete) // Check if interspersed is false or -- was set on a previous arg. // This works by counting the arguments. Normally -- is not counted as arg but // if -- was already set or interspersed is false and there is already one arg then // the extra added -- is counted as arg. flagCompletion := true _ = finalCmd.ParseFlags(append(finalArgs, "--")) newArgCount := finalCmd.Flags().NArg() // Parse the flags early so we can check if required flags are set if err = finalCmd.ParseFlags(finalArgs); err != nil { return finalCmd, []string{}, ShellCompDirectiveDefault, fmt.Errorf("Error while parsing flags from args %v: %s", finalArgs, err.Error()) } realArgCount := finalCmd.Flags().NArg() if newArgCount > realArgCount { // don't do flag completion (see above) flagCompletion = false } // Error while attempting to parse flags if flagErr != nil { // If error type is flagCompError and we don't want flagCompletion we should ignore the error if _, ok := flagErr.(*flagCompError); !(ok && !flagCompletion) { return finalCmd, []string{}, ShellCompDirectiveDefault, flagErr } } // Look for the --help or --version flags. If they are present, // there should be no further completions. if helpOrVersionFlagPresent(finalCmd) { return finalCmd, []string{}, ShellCompDirectiveNoFileComp, nil } // We only remove the flags from the arguments if DisableFlagParsing is not set. // This is important for commands which have requested to do their own flag completion. if !finalCmd.DisableFlagParsing { finalArgs = finalCmd.Flags().Args() } if flag != nil && flagCompletion { // Check if we are completing a flag value subject to annotations if validExts, present := flag.Annotations[BashCompFilenameExt]; present { if len(validExts) != 0 { // File completion filtered by extensions return finalCmd, validExts, ShellCompDirectiveFilterFileExt, nil } // The annotation requests simple file completion. There is no reason to do // that since it is the default behavior anyway. Let's ignore this annotation // in case the program also registered a completion function for this flag. // Even though it is a mistake on the program's side, let's be nice when we can. } if subDir, present := flag.Annotations[BashCompSubdirsInDir]; present { if len(subDir) == 1 { // Directory completion from within a directory return finalCmd, subDir, ShellCompDirectiveFilterDirs, nil } // Directory completion return finalCmd, []string{}, ShellCompDirectiveFilterDirs, nil } } var completions []string var directive ShellCompDirective // Enforce flag groups before doing flag completions finalCmd.enforceFlagGroupsForCompletion() // Note that we want to perform flagname completion even if finalCmd.DisableFlagParsing==true; // doing this allows for completion of persistent flag names even for commands that disable flag parsing. // // When doing completion of a flag name, as soon as an argument starts with // a '-' we know it is a flag. We cannot use isFlagArg() here as it requires // the flag name to be complete if flag == nil && len(toComplete) > 0 && toComplete[0] == '-' && !strings.Contains(toComplete, "=") && flagCompletion { // First check for required flags completions = completeRequireFlags(finalCmd, toComplete) // If we have not found any required flags, only then can we show regular flags if len(completions) == 0 { doCompleteFlags := func(flag *pflag.Flag) { if !flag.Changed || strings.Contains(flag.Value.Type(), "Slice") || strings.Contains(flag.Value.Type(), "Array") { // If the flag is not already present, or if it can be specified multiple times (Array or Slice) // we suggest it as a completion completions = append(completions, getFlagNameCompletions(flag, toComplete)...) } } // We cannot use finalCmd.Flags() because we may not have called ParsedFlags() for commands // that have set DisableFlagParsing; it is ParseFlags() that merges the inherited and // non-inherited flags. finalCmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) { doCompleteFlags(flag) }) finalCmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) { doCompleteFlags(flag) }) } directive = ShellCompDirectiveNoFileComp if len(completions) == 1 && strings.HasSuffix(completions[0], "=") { // If there is a single completion, the shell usually adds a space // after the completion. We don't want that if the flag ends with an = directive = ShellCompDirectiveNoSpace } if !finalCmd.DisableFlagParsing { // If DisableFlagParsing==false, we have completed the flags as known by Cobra; // we can return what we found. // If DisableFlagParsing==true, Cobra may not be aware of all flags, so we // let the logic continue to see if ValidArgsFunction needs to be called. return finalCmd, completions, directive, nil } } else { directive = ShellCompDirectiveDefault if flag == nil { foundLocalNonPersistentFlag := false // If TraverseChildren is true on the root command we don't check for // local flags because we can use a local flag on a parent command if !finalCmd.Root().TraverseChildren { // Check if there are any local, non-persistent flags on the command-line localNonPersistentFlags := finalCmd.LocalNonPersistentFlags() finalCmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) { if localNonPersistentFlags.Lookup(flag.Name) != nil && flag.Changed { foundLocalNonPersistentFlag = true } }) } // Complete subcommand names, including the help command if len(finalArgs) == 0 && !foundLocalNonPersistentFlag { // We only complete sub-commands if: // - there are no arguments on the command-line and // - there are no local, non-persistent flags on the command-line or TraverseChildren is true for _, subCmd := range finalCmd.Commands() { if subCmd.IsAvailableCommand() || subCmd == finalCmd.helpCommand { if strings.HasPrefix(subCmd.Name(), toComplete) { completions = append(completions, fmt.Sprintf("%s\t%s", subCmd.Name(), subCmd.Short)) } directive = ShellCompDirectiveNoFileComp } } } // Complete required flags even without the '-' prefix completions = append(completions, completeRequireFlags(finalCmd, toComplete)...) // Always complete ValidArgs, even if we are completing a subcommand name. // This is for commands that have both subcommands and ValidArgs. if len(finalCmd.ValidArgs) > 0 { if len(finalArgs) == 0 { // ValidArgs are only for the first argument for _, validArg := range finalCmd.ValidArgs { if strings.HasPrefix(validArg, toComplete) { completions = append(completions, validArg) } } directive = ShellCompDirectiveNoFileComp // If no completions were found within commands or ValidArgs, // see if there are any ArgAliases that should be completed. if len(completions) == 0 { for _, argAlias := range finalCmd.ArgAliases { if strings.HasPrefix(argAlias, toComplete) { completions = append(completions, argAlias) } } } } // If there are ValidArgs specified (even if they don't match), we stop completion. // Only one of ValidArgs or ValidArgsFunction can be used for a single command. return finalCmd, completions, directive, nil } // Let the logic continue so as to add any ValidArgsFunction completions, // even if we already found sub-commands. // This is for commands that have subcommands but also specify a ValidArgsFunction. } } // Find the completion function for the flag or command var completionFn func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) if flag != nil && flagCompletion { flagCompletionMutex.RLock() completionFn = flagCompletionFunctions[flag] flagCompletionMutex.RUnlock() } else { completionFn = finalCmd.ValidArgsFunction } if completionFn != nil { // Go custom completion defined for this flag or command. // Call the registered completion function to get the completions. var comps []string comps, directive = completionFn(finalCmd, finalArgs, toComplete) completions = append(completions, comps...) } return finalCmd, completions, directive, nil } func helpOrVersionFlagPresent(cmd *Command) bool { if versionFlag := cmd.Flags().Lookup("version"); versionFlag != nil && len(versionFlag.Annotations[FlagSetByCobraAnnotation]) > 0 && versionFlag.Changed { return true } if helpFlag := cmd.Flags().Lookup("help"); helpFlag != nil && len(helpFlag.Annotations[FlagSetByCobraAnnotation]) > 0 && helpFlag.Changed { return true } return false } func getFlagNameCompletions(flag *pflag.Flag, toComplete string) []string { if nonCompletableFlag(flag) { return []string{} } var completions []string flagName := "--" + flag.Name if strings.HasPrefix(flagName, toComplete) { // Flag without the = completions = append(completions, fmt.Sprintf("%s\t%s", flagName, flag.Usage)) // Why suggest both long forms: --flag and --flag= ? // This forces the user to *always* have to type either an = or a space after the flag name. // Let's be nice and avoid making users have to do that. // Since boolean flags and shortname flags don't show the = form, let's go that route and never show it. // The = form will still work, we just won't suggest it. // This also makes the list of suggested flags shorter as we avoid all the = forms. // // if len(flag.NoOptDefVal) == 0 { // // Flag requires a value, so it can be suffixed with = // flagName += "=" // completions = append(completions, fmt.Sprintf("%s\t%s", flagName, flag.Usage)) // } } flagName = "-" + flag.Shorthand if len(flag.Shorthand) > 0 && strings.HasPrefix(flagName, toComplete) { completions = append(completions, fmt.Sprintf("%s\t%s", flagName, flag.Usage)) } return completions } func completeRequireFlags(finalCmd *Command, toComplete string) []string { var completions []string doCompleteRequiredFlags := func(flag *pflag.Flag) { if _, present := flag.Annotations[BashCompOneRequiredFlag]; present { if !flag.Changed { // If the flag is not already present, we suggest it as a completion completions = append(completions, getFlagNameCompletions(flag, toComplete)...) } } } // We cannot use finalCmd.Flags() because we may not have called ParsedFlags() for commands // that have set DisableFlagParsing; it is ParseFlags() that merges the inherited and // non-inherited flags. finalCmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) { doCompleteRequiredFlags(flag) }) finalCmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) { doCompleteRequiredFlags(flag) }) return completions } func checkIfFlagCompletion(finalCmd *Command, args []string, lastArg string) (*pflag.Flag, []string, string, error) { if finalCmd.DisableFlagParsing { // We only do flag completion if we are allowed to parse flags // This is important for commands which have requested to do their own flag completion. return nil, args, lastArg, nil } var flagName string trimmedArgs := args flagWithEqual := false orgLastArg := lastArg // When doing completion of a flag name, as soon as an argument starts with // a '-' we know it is a flag. We cannot use isFlagArg() here as that function // requires the flag name to be complete if len(lastArg) > 0 && lastArg[0] == '-' { if index := strings.Index(lastArg, "="); index >= 0 { // Flag with an = if strings.HasPrefix(lastArg[:index], "--") { // Flag has full name flagName = lastArg[2:index] } else { // Flag is shorthand // We have to get the last shorthand flag name // e.g. `-asd` => d to provide the correct completion // https://github.com/spf13/cobra/issues/1257 flagName = lastArg[index-1 : index] } lastArg = lastArg[index+1:] flagWithEqual = true } else { // Normal flag completion return nil, args, lastArg, nil } } if len(flagName) == 0 { if len(args) > 0 { prevArg := args[len(args)-1] if isFlagArg(prevArg) { // Only consider the case where the flag does not contain an =. // If the flag contains an = it means it has already been fully processed, // so we don't need to deal with it here. if index := strings.Index(prevArg, "="); index < 0 { if strings.HasPrefix(prevArg, "--") { // Flag has full name flagName = prevArg[2:] } else { // Flag is shorthand // We have to get the last shorthand flag name // e.g. `-asd` => d to provide the correct completion // https://github.com/spf13/cobra/issues/1257 flagName = prevArg[len(prevArg)-1:] } // Remove the uncompleted flag or else there could be an error created // for an invalid value for that flag trimmedArgs = args[:len(args)-1] } } } } if len(flagName) == 0 { // Not doing flag completion return nil, trimmedArgs, lastArg, nil } flag := findFlag(finalCmd, flagName) if flag == nil { // Flag not supported by this command, the interspersed option might be set so return the original args return nil, args, orgLastArg, &flagCompError{subCommand: finalCmd.Name(), flagName: flagName} } if !flagWithEqual { if len(flag.NoOptDefVal) != 0 { // We had assumed dealing with a two-word flag but the flag is a boolean flag. // In that case, there is no value following it, so we are not really doing flag completion. // Reset everything to do noun completion. trimmedArgs = args flag = nil } } return flag, trimmedArgs, lastArg, nil } // InitDefaultCompletionCmd adds a default 'completion' command to c. // This function will do nothing if any of the following is true: // 1- the feature has been explicitly disabled by the program, // 2- c has no subcommands (to avoid creating one), // 3- c already has a 'completion' command provided by the program. func (c *Command) InitDefaultCompletionCmd() { if c.CompletionOptions.DisableDefaultCmd || !c.HasSubCommands() { return } for _, cmd := range c.commands { if cmd.Name() == compCmdName || cmd.HasAlias(compCmdName) { // A completion command is already available return } } haveNoDescFlag := !c.CompletionOptions.DisableNoDescFlag && !c.CompletionOptions.DisableDescriptions completionCmd := &Command{ Use: compCmdName, Short: "Generate the autocompletion script for the specified shell", Long: fmt.Sprintf(`Generate the autocompletion script for %[1]s for the specified shell. See each sub-command's help for details on how to use the generated script. `, c.Root().Name()), Args: NoArgs, ValidArgsFunction: NoFileCompletions, Hidden: c.CompletionOptions.HiddenDefaultCmd, GroupID: c.completionCommandGroupID, } c.AddCommand(completionCmd) out := c.OutOrStdout() noDesc := c.CompletionOptions.DisableDescriptions shortDesc := "Generate the autocompletion script for %s" bash := &Command{ Use: "bash", Short: fmt.Sprintf(shortDesc, "bash"), Long: fmt.Sprintf(`Generate the autocompletion script for the bash shell. This script depends on the 'bash-completion' package. If it is not installed already, you can install it via your OS's package manager. To load completions in your current shell session: source <(%[1]s completion bash) To load completions for every new session, execute once: #### Linux: %[1]s completion bash > /etc/bash_completion.d/%[1]s #### macOS: %[1]s completion bash > $(brew --prefix)/etc/bash_completion.d/%[1]s You will need to start a new shell for this setup to take effect. `, c.Root().Name()), Args: NoArgs, DisableFlagsInUseLine: true, ValidArgsFunction: NoFileCompletions, RunE: func(cmd *Command, args []string) error { return cmd.Root().GenBashCompletionV2(out, !noDesc) }, } if haveNoDescFlag { bash.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc) } zsh := &Command{ Use: "zsh", Short: fmt.Sprintf(shortDesc, "zsh"), Long: fmt.Sprintf(`Generate the autocompletion script for the zsh shell. If shell completion is not already enabled in your environment you will need to enable it. You can execute the following once: echo "autoload -U compinit; compinit" >> ~/.zshrc To load completions in your current shell session: source <(%[1]s completion zsh) To load completions for every new session, execute once: #### Linux: %[1]s completion zsh > "${fpath[1]}/_%[1]s" #### macOS: %[1]s completion zsh > $(brew --prefix)/share/zsh/site-functions/_%[1]s You will need to start a new shell for this setup to take effect. `, c.Root().Name()), Args: NoArgs, ValidArgsFunction: NoFileCompletions, RunE: func(cmd *Command, args []string) error { if noDesc { return cmd.Root().GenZshCompletionNoDesc(out) } return cmd.Root().GenZshCompletion(out) }, } if haveNoDescFlag { zsh.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc) } fish := &Command{ Use: "fish", Short: fmt.Sprintf(shortDesc, "fish"), Long: fmt.Sprintf(`Generate the autocompletion script for the fish shell. To load completions in your current shell session: %[1]s completion fish | source To load completions for every new session, execute once: %[1]s completion fish > ~/.config/fish/completions/%[1]s.fish You will need to start a new shell for this setup to take effect. `, c.Root().Name()), Args: NoArgs, ValidArgsFunction: NoFileCompletions, RunE: func(cmd *Command, args []string) error { return cmd.Root().GenFishCompletion(out, !noDesc) }, } if haveNoDescFlag { fish.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc) } powershell := &Command{ Use: "powershell", Short: fmt.Sprintf(shortDesc, "powershell"), Long: fmt.Sprintf(`Generate the autocompletion script for powershell. To load completions in your current shell session: %[1]s completion powershell | Out-String | Invoke-Expression To load completions for every new session, add the output of the above command to your powershell profile. `, c.Root().Name()), Args: NoArgs, ValidArgsFunction: NoFileCompletions, RunE: func(cmd *Command, args []string) error { if noDesc { return cmd.Root().GenPowerShellCompletion(out) } return cmd.Root().GenPowerShellCompletionWithDesc(out) }, } if haveNoDescFlag { powershell.Flags().BoolVar(&noDesc, compCmdNoDescFlagName, compCmdNoDescFlagDefault, compCmdNoDescFlagDesc) } completionCmd.AddCommand(bash, zsh, fish, powershell) } func findFlag(cmd *Command, name string) *pflag.Flag { flagSet := cmd.Flags() if len(name) == 1 { // First convert the short flag into a long flag // as the cmd.Flag() search only accepts long flags if short := flagSet.ShorthandLookup(name); short != nil { name = short.Name } else { set := cmd.InheritedFlags() if short = set.ShorthandLookup(name); short != nil { name = short.Name } else { return nil } } } return cmd.Flag(name) } // CompDebug prints the specified string to the same file as where the // completion script prints its logs. // Note that completion printouts should never be on stdout as they would // be wrongly interpreted as actual completion choices by the completion script. func CompDebug(msg string, printToStdErr bool) { msg = fmt.Sprintf("[Debug] %s", msg) // Such logs are only printed when the user has set the environment // variable BASH_COMP_DEBUG_FILE to the path of some file to be used. if path := os.Getenv("BASH_COMP_DEBUG_FILE"); path != "" { f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err == nil { defer f.Close() WriteStringAndCheck(f, msg) } } if printToStdErr { // Must print to stderr for this not to be read by the completion script. fmt.Fprint(os.Stderr, msg) } } // CompDebugln prints the specified string with a newline at the end // to the same file as where the completion script prints its logs. // Such logs are only printed when the user has set the environment // variable BASH_COMP_DEBUG_FILE to the path of some file to be used. func CompDebugln(msg string, printToStdErr bool) { CompDebug(fmt.Sprintf("%s\n", msg), printToStdErr) } // CompError prints the specified completion message to stderr. func CompError(msg string) { msg = fmt.Sprintf("[Error] %s", msg) CompDebug(msg, true) } // CompErrorln prints the specified completion message to stderr with a newline at the end. func CompErrorln(msg string) { CompError(fmt.Sprintf("%s\n", msg)) } ================================================ FILE: vendor/github.com/spf13/cobra/fish_completions.go ================================================ // Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cobra import ( "bytes" "fmt" "io" "os" "strings" ) func genFishComp(buf io.StringWriter, name string, includeDesc bool) { // Variables should not contain a '-' or ':' character nameForVar := name nameForVar = strings.ReplaceAll(nameForVar, "-", "_") nameForVar = strings.ReplaceAll(nameForVar, ":", "_") compCmd := ShellCompRequestCmd if !includeDesc { compCmd = ShellCompNoDescRequestCmd } WriteStringAndCheck(buf, fmt.Sprintf("# fish completion for %-36s -*- shell-script -*-\n", name)) WriteStringAndCheck(buf, fmt.Sprintf(` function __%[1]s_debug set -l file "$BASH_COMP_DEBUG_FILE" if test -n "$file" echo "$argv" >> $file end end function __%[1]s_perform_completion __%[1]s_debug "Starting __%[1]s_perform_completion" # Extract all args except the last one set -l args (commandline -opc) # Extract the last arg and escape it in case it is a space set -l lastArg (string escape -- (commandline -ct)) __%[1]s_debug "args: $args" __%[1]s_debug "last arg: $lastArg" # Disable ActiveHelp which is not supported for fish shell set -l requestComp "%[10]s=0 $args[1] %[3]s $args[2..-1] $lastArg" __%[1]s_debug "Calling $requestComp" set -l results (eval $requestComp 2> /dev/null) # Some programs may output extra empty lines after the directive. # Let's ignore them or else it will break completion. # Ref: https://github.com/spf13/cobra/issues/1279 for line in $results[-1..1] if test (string trim -- $line) = "" # Found an empty line, remove it set results $results[1..-2] else # Found non-empty line, we have our proper output break end end set -l comps $results[1..-2] set -l directiveLine $results[-1] # For Fish, when completing a flag with an = (e.g., -n=) # completions must be prefixed with the flag set -l flagPrefix (string match -r -- '-.*=' "$lastArg") __%[1]s_debug "Comps: $comps" __%[1]s_debug "DirectiveLine: $directiveLine" __%[1]s_debug "flagPrefix: $flagPrefix" for comp in $comps printf "%%s%%s\n" "$flagPrefix" "$comp" end printf "%%s\n" "$directiveLine" end # this function limits calls to __%[1]s_perform_completion, by caching the result behind $__%[1]s_perform_completion_once_result function __%[1]s_perform_completion_once __%[1]s_debug "Starting __%[1]s_perform_completion_once" if test -n "$__%[1]s_perform_completion_once_result" __%[1]s_debug "Seems like a valid result already exists, skipping __%[1]s_perform_completion" return 0 end set --global __%[1]s_perform_completion_once_result (__%[1]s_perform_completion) if test -z "$__%[1]s_perform_completion_once_result" __%[1]s_debug "No completions, probably due to a failure" return 1 end __%[1]s_debug "Performed completions and set __%[1]s_perform_completion_once_result" return 0 end # this function is used to clear the $__%[1]s_perform_completion_once_result variable after completions are run function __%[1]s_clear_perform_completion_once_result __%[1]s_debug "" __%[1]s_debug "========= clearing previously set __%[1]s_perform_completion_once_result variable ==========" set --erase __%[1]s_perform_completion_once_result __%[1]s_debug "Succesfully erased the variable __%[1]s_perform_completion_once_result" end function __%[1]s_requires_order_preservation __%[1]s_debug "" __%[1]s_debug "========= checking if order preservation is required ==========" __%[1]s_perform_completion_once if test -z "$__%[1]s_perform_completion_once_result" __%[1]s_debug "Error determining if order preservation is required" return 1 end set -l directive (string sub --start 2 $__%[1]s_perform_completion_once_result[-1]) __%[1]s_debug "Directive is: $directive" set -l shellCompDirectiveKeepOrder %[9]d set -l keeporder (math (math --scale 0 $directive / $shellCompDirectiveKeepOrder) %% 2) __%[1]s_debug "Keeporder is: $keeporder" if test $keeporder -ne 0 __%[1]s_debug "This does require order preservation" return 0 end __%[1]s_debug "This doesn't require order preservation" return 1 end # This function does two things: # - Obtain the completions and store them in the global __%[1]s_comp_results # - Return false if file completion should be performed function __%[1]s_prepare_completions __%[1]s_debug "" __%[1]s_debug "========= starting completion logic ==========" # Start fresh set --erase __%[1]s_comp_results __%[1]s_perform_completion_once __%[1]s_debug "Completion results: $__%[1]s_perform_completion_once_result" if test -z "$__%[1]s_perform_completion_once_result" __%[1]s_debug "No completion, probably due to a failure" # Might as well do file completion, in case it helps return 1 end set -l directive (string sub --start 2 $__%[1]s_perform_completion_once_result[-1]) set --global __%[1]s_comp_results $__%[1]s_perform_completion_once_result[1..-2] __%[1]s_debug "Completions are: $__%[1]s_comp_results" __%[1]s_debug "Directive is: $directive" set -l shellCompDirectiveError %[4]d set -l shellCompDirectiveNoSpace %[5]d set -l shellCompDirectiveNoFileComp %[6]d set -l shellCompDirectiveFilterFileExt %[7]d set -l shellCompDirectiveFilterDirs %[8]d if test -z "$directive" set directive 0 end set -l compErr (math (math --scale 0 $directive / $shellCompDirectiveError) %% 2) if test $compErr -eq 1 __%[1]s_debug "Received error directive: aborting." # Might as well do file completion, in case it helps return 1 end set -l filefilter (math (math --scale 0 $directive / $shellCompDirectiveFilterFileExt) %% 2) set -l dirfilter (math (math --scale 0 $directive / $shellCompDirectiveFilterDirs) %% 2) if test $filefilter -eq 1; or test $dirfilter -eq 1 __%[1]s_debug "File extension filtering or directory filtering not supported" # Do full file completion instead return 1 end set -l nospace (math (math --scale 0 $directive / $shellCompDirectiveNoSpace) %% 2) set -l nofiles (math (math --scale 0 $directive / $shellCompDirectiveNoFileComp) %% 2) __%[1]s_debug "nospace: $nospace, nofiles: $nofiles" # If we want to prevent a space, or if file completion is NOT disabled, # we need to count the number of valid completions. # To do so, we will filter on prefix as the completions we have received # may not already be filtered so as to allow fish to match on different # criteria than the prefix. if test $nospace -ne 0; or test $nofiles -eq 0 set -l prefix (commandline -t | string escape --style=regex) __%[1]s_debug "prefix: $prefix" set -l completions (string match -r -- "^$prefix.*" $__%[1]s_comp_results) set --global __%[1]s_comp_results $completions __%[1]s_debug "Filtered completions are: $__%[1]s_comp_results" # Important not to quote the variable for count to work set -l numComps (count $__%[1]s_comp_results) __%[1]s_debug "numComps: $numComps" if test $numComps -eq 1; and test $nospace -ne 0 # We must first split on \t to get rid of the descriptions to be # able to check what the actual completion will be. # We don't need descriptions anyway since there is only a single # real completion which the shell will expand immediately. set -l split (string split --max 1 \t $__%[1]s_comp_results[1]) # Fish won't add a space if the completion ends with any # of the following characters: @=/:., set -l lastChar (string sub -s -1 -- $split) if not string match -r -q "[@=/:.,]" -- "$lastChar" # In other cases, to support the "nospace" directive we trick the shell # by outputting an extra, longer completion. __%[1]s_debug "Adding second completion to perform nospace directive" set --global __%[1]s_comp_results $split[1] $split[1]. __%[1]s_debug "Completions are now: $__%[1]s_comp_results" end end if test $numComps -eq 0; and test $nofiles -eq 0 # To be consistent with bash and zsh, we only trigger file # completion when there are no other completions __%[1]s_debug "Requesting file completion" return 1 end end return 0 end # Since Fish completions are only loaded once the user triggers them, we trigger them ourselves # so we can properly delete any completions provided by another script. # Only do this if the program can be found, or else fish may print some errors; besides, # the existing completions will only be loaded if the program can be found. if type -q "%[2]s" # The space after the program name is essential to trigger completion for the program # and not completion of the program name itself. # Also, we use '> /dev/null 2>&1' since '&>' is not supported in older versions of fish. complete --do-complete "%[2]s " > /dev/null 2>&1 end # Remove any pre-existing completions for the program since we will be handling all of them. complete -c %[2]s -e # this will get called after the two calls below and clear the $__%[1]s_perform_completion_once_result global complete -c %[2]s -n '__%[1]s_clear_perform_completion_once_result' # The call to __%[1]s_prepare_completions will setup __%[1]s_comp_results # which provides the program's completion choices. # If this doesn't require order preservation, we don't use the -k flag complete -c %[2]s -n 'not __%[1]s_requires_order_preservation && __%[1]s_prepare_completions' -f -a '$__%[1]s_comp_results' # otherwise we use the -k flag complete -k -c %[2]s -n '__%[1]s_requires_order_preservation && __%[1]s_prepare_completions' -f -a '$__%[1]s_comp_results' `, nameForVar, name, compCmd, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder, activeHelpEnvVar(name))) } // GenFishCompletion generates fish completion file and writes to the passed writer. func (c *Command) GenFishCompletion(w io.Writer, includeDesc bool) error { buf := new(bytes.Buffer) genFishComp(buf, c.Name(), includeDesc) _, err := buf.WriteTo(w) return err } // GenFishCompletionFile generates fish completion file. func (c *Command) GenFishCompletionFile(filename string, includeDesc bool) error { outFile, err := os.Create(filename) if err != nil { return err } defer outFile.Close() return c.GenFishCompletion(outFile, includeDesc) } ================================================ FILE: vendor/github.com/spf13/cobra/fish_completions.md ================================================ ## Generating Fish Completions For Your cobra.Command Please refer to [Shell Completions](shell_completions.md) for details. ================================================ FILE: vendor/github.com/spf13/cobra/flag_groups.go ================================================ // Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cobra import ( "fmt" "sort" "strings" flag "github.com/spf13/pflag" ) const ( requiredAsGroup = "cobra_annotation_required_if_others_set" mutuallyExclusive = "cobra_annotation_mutually_exclusive" ) // MarkFlagsRequiredTogether marks the given flags with annotations so that Cobra errors // if the command is invoked with a subset (but not all) of the given flags. func (c *Command) MarkFlagsRequiredTogether(flagNames ...string) { c.mergePersistentFlags() for _, v := range flagNames { f := c.Flags().Lookup(v) if f == nil { panic(fmt.Sprintf("Failed to find flag %q and mark it as being required in a flag group", v)) } if err := c.Flags().SetAnnotation(v, requiredAsGroup, append(f.Annotations[requiredAsGroup], strings.Join(flagNames, " "))); err != nil { // Only errs if the flag isn't found. panic(err) } } } // MarkFlagsMutuallyExclusive marks the given flags with annotations so that Cobra errors // if the command is invoked with more than one flag from the given set of flags. func (c *Command) MarkFlagsMutuallyExclusive(flagNames ...string) { c.mergePersistentFlags() for _, v := range flagNames { f := c.Flags().Lookup(v) if f == nil { panic(fmt.Sprintf("Failed to find flag %q and mark it as being in a mutually exclusive flag group", v)) } // Each time this is called is a single new entry; this allows it to be a member of multiple groups if needed. if err := c.Flags().SetAnnotation(v, mutuallyExclusive, append(f.Annotations[mutuallyExclusive], strings.Join(flagNames, " "))); err != nil { panic(err) } } } // ValidateFlagGroups validates the mutuallyExclusive/requiredAsGroup logic and returns the // first error encountered. func (c *Command) ValidateFlagGroups() error { if c.DisableFlagParsing { return nil } flags := c.Flags() // groupStatus format is the list of flags as a unique ID, // then a map of each flag name and whether it is set or not. groupStatus := map[string]map[string]bool{} mutuallyExclusiveGroupStatus := map[string]map[string]bool{} flags.VisitAll(func(pflag *flag.Flag) { processFlagForGroupAnnotation(flags, pflag, requiredAsGroup, groupStatus) processFlagForGroupAnnotation(flags, pflag, mutuallyExclusive, mutuallyExclusiveGroupStatus) }) if err := validateRequiredFlagGroups(groupStatus); err != nil { return err } if err := validateExclusiveFlagGroups(mutuallyExclusiveGroupStatus); err != nil { return err } return nil } func hasAllFlags(fs *flag.FlagSet, flagnames ...string) bool { for _, fname := range flagnames { f := fs.Lookup(fname) if f == nil { return false } } return true } func processFlagForGroupAnnotation(flags *flag.FlagSet, pflag *flag.Flag, annotation string, groupStatus map[string]map[string]bool) { groupInfo, found := pflag.Annotations[annotation] if found { for _, group := range groupInfo { if groupStatus[group] == nil { flagnames := strings.Split(group, " ") // Only consider this flag group at all if all the flags are defined. if !hasAllFlags(flags, flagnames...) { continue } groupStatus[group] = map[string]bool{} for _, name := range flagnames { groupStatus[group][name] = false } } groupStatus[group][pflag.Name] = pflag.Changed } } } func validateRequiredFlagGroups(data map[string]map[string]bool) error { keys := sortedKeys(data) for _, flagList := range keys { flagnameAndStatus := data[flagList] unset := []string{} for flagname, isSet := range flagnameAndStatus { if !isSet { unset = append(unset, flagname) } } if len(unset) == len(flagnameAndStatus) || len(unset) == 0 { continue } // Sort values, so they can be tested/scripted against consistently. sort.Strings(unset) return fmt.Errorf("if any flags in the group [%v] are set they must all be set; missing %v", flagList, unset) } return nil } func validateExclusiveFlagGroups(data map[string]map[string]bool) error { keys := sortedKeys(data) for _, flagList := range keys { flagnameAndStatus := data[flagList] var set []string for flagname, isSet := range flagnameAndStatus { if isSet { set = append(set, flagname) } } if len(set) == 0 || len(set) == 1 { continue } // Sort values, so they can be tested/scripted against consistently. sort.Strings(set) return fmt.Errorf("if any flags in the group [%v] are set none of the others can be; %v were all set", flagList, set) } return nil } func sortedKeys(m map[string]map[string]bool) []string { keys := make([]string, len(m)) i := 0 for k := range m { keys[i] = k i++ } sort.Strings(keys) return keys } // enforceFlagGroupsForCompletion will do the following: // - when a flag in a group is present, other flags in the group will be marked required // - when a flag in a mutually exclusive group is present, other flags in the group will be marked as hidden // This allows the standard completion logic to behave appropriately for flag groups func (c *Command) enforceFlagGroupsForCompletion() { if c.DisableFlagParsing { return } flags := c.Flags() groupStatus := map[string]map[string]bool{} mutuallyExclusiveGroupStatus := map[string]map[string]bool{} c.Flags().VisitAll(func(pflag *flag.Flag) { processFlagForGroupAnnotation(flags, pflag, requiredAsGroup, groupStatus) processFlagForGroupAnnotation(flags, pflag, mutuallyExclusive, mutuallyExclusiveGroupStatus) }) // If a flag that is part of a group is present, we make all the other flags // of that group required so that the shell completion suggests them automatically for flagList, flagnameAndStatus := range groupStatus { for _, isSet := range flagnameAndStatus { if isSet { // One of the flags of the group is set, mark the other ones as required for _, fName := range strings.Split(flagList, " ") { _ = c.MarkFlagRequired(fName) } } } } // If a flag that is mutually exclusive to others is present, we hide the other // flags of that group so the shell completion does not suggest them for flagList, flagnameAndStatus := range mutuallyExclusiveGroupStatus { for flagName, isSet := range flagnameAndStatus { if isSet { // One of the flags of the mutually exclusive group is set, mark the other ones as hidden // Don't mark the flag that is already set as hidden because it may be an // array or slice flag and therefore must continue being suggested for _, fName := range strings.Split(flagList, " ") { if fName != flagName { flag := c.Flags().Lookup(fName) flag.Hidden = true } } } } } } ================================================ FILE: vendor/github.com/spf13/cobra/powershell_completions.go ================================================ // Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // The generated scripts require PowerShell v5.0+ (which comes Windows 10, but // can be downloaded separately for windows 7 or 8.1). package cobra import ( "bytes" "fmt" "io" "os" "strings" ) func genPowerShellComp(buf io.StringWriter, name string, includeDesc bool) { // Variables should not contain a '-' or ':' character nameForVar := name nameForVar = strings.Replace(nameForVar, "-", "_", -1) nameForVar = strings.Replace(nameForVar, ":", "_", -1) compCmd := ShellCompRequestCmd if !includeDesc { compCmd = ShellCompNoDescRequestCmd } WriteStringAndCheck(buf, fmt.Sprintf(`# powershell completion for %-36[1]s -*- shell-script -*- function __%[1]s_debug { if ($env:BASH_COMP_DEBUG_FILE) { "$args" | Out-File -Append -FilePath "$env:BASH_COMP_DEBUG_FILE" } } filter __%[1]s_escapeStringWithSpecialChars { `+" $_ -replace '\\s|#|@|\\$|;|,|''|\\{|\\}|\\(|\\)|\"|`|\\||<|>|&','`$&'"+` } [scriptblock]$__%[2]sCompleterBlock = { param( $WordToComplete, $CommandAst, $CursorPosition ) # Get the current command line and convert into a string $Command = $CommandAst.CommandElements $Command = "$Command" __%[1]s_debug "" __%[1]s_debug "========= starting completion logic ==========" __%[1]s_debug "WordToComplete: $WordToComplete Command: $Command CursorPosition: $CursorPosition" # The user could have moved the cursor backwards on the command-line. # We need to trigger completion from the $CursorPosition location, so we need # to truncate the command-line ($Command) up to the $CursorPosition location. # Make sure the $Command is longer then the $CursorPosition before we truncate. # This happens because the $Command does not include the last space. if ($Command.Length -gt $CursorPosition) { $Command=$Command.Substring(0,$CursorPosition) } __%[1]s_debug "Truncated command: $Command" $ShellCompDirectiveError=%[4]d $ShellCompDirectiveNoSpace=%[5]d $ShellCompDirectiveNoFileComp=%[6]d $ShellCompDirectiveFilterFileExt=%[7]d $ShellCompDirectiveFilterDirs=%[8]d $ShellCompDirectiveKeepOrder=%[9]d # Prepare the command to request completions for the program. # Split the command at the first space to separate the program and arguments. $Program,$Arguments = $Command.Split(" ",2) $RequestComp="$Program %[3]s $Arguments" __%[1]s_debug "RequestComp: $RequestComp" # we cannot use $WordToComplete because it # has the wrong values if the cursor was moved # so use the last argument if ($WordToComplete -ne "" ) { $WordToComplete = $Arguments.Split(" ")[-1] } __%[1]s_debug "New WordToComplete: $WordToComplete" # Check for flag with equal sign $IsEqualFlag = ($WordToComplete -Like "--*=*" ) if ( $IsEqualFlag ) { __%[1]s_debug "Completing equal sign flag" # Remove the flag part $Flag,$WordToComplete = $WordToComplete.Split("=",2) } if ( $WordToComplete -eq "" -And ( -Not $IsEqualFlag )) { # If the last parameter is complete (there is a space following it) # We add an extra empty parameter so we can indicate this to the go method. __%[1]s_debug "Adding extra empty parameter" # PowerShell 7.2+ changed the way how the arguments are passed to executables, # so for pre-7.2 or when Legacy argument passing is enabled we need to use `+" # `\"`\" to pass an empty argument, a \"\" or '' does not work!!!"+` if ($PSVersionTable.PsVersion -lt [version]'7.2.0' -or ($PSVersionTable.PsVersion -lt [version]'7.3.0' -and -not [ExperimentalFeature]::IsEnabled("PSNativeCommandArgumentPassing")) -or (($PSVersionTable.PsVersion -ge [version]'7.3.0' -or [ExperimentalFeature]::IsEnabled("PSNativeCommandArgumentPassing")) -and $PSNativeCommandArgumentPassing -eq 'Legacy')) { `+" $RequestComp=\"$RequestComp\" + ' `\"`\"'"+` } else { $RequestComp="$RequestComp" + ' ""' } } __%[1]s_debug "Calling $RequestComp" # First disable ActiveHelp which is not supported for Powershell $env:%[10]s=0 #call the command store the output in $out and redirect stderr and stdout to null # $Out is an array contains each line per element Invoke-Expression -OutVariable out "$RequestComp" 2>&1 | Out-Null # get directive from last line [int]$Directive = $Out[-1].TrimStart(':') if ($Directive -eq "") { # There is no directive specified $Directive = 0 } __%[1]s_debug "The completion directive is: $Directive" # remove directive (last element) from out $Out = $Out | Where-Object { $_ -ne $Out[-1] } __%[1]s_debug "The completions are: $Out" if (($Directive -band $ShellCompDirectiveError) -ne 0 ) { # Error code. No completion. __%[1]s_debug "Received error from custom completion go code" return } $Longest = 0 [Array]$Values = $Out | ForEach-Object { #Split the output in name and description `+" $Name, $Description = $_.Split(\"`t\",2)"+` __%[1]s_debug "Name: $Name Description: $Description" # Look for the longest completion so that we can format things nicely if ($Longest -lt $Name.Length) { $Longest = $Name.Length } # Set the description to a one space string if there is none set. # This is needed because the CompletionResult does not accept an empty string as argument if (-Not $Description) { $Description = " " } @{Name="$Name";Description="$Description"} } $Space = " " if (($Directive -band $ShellCompDirectiveNoSpace) -ne 0 ) { # remove the space here __%[1]s_debug "ShellCompDirectiveNoSpace is called" $Space = "" } if ((($Directive -band $ShellCompDirectiveFilterFileExt) -ne 0 ) -or (($Directive -band $ShellCompDirectiveFilterDirs) -ne 0 )) { __%[1]s_debug "ShellCompDirectiveFilterFileExt ShellCompDirectiveFilterDirs are not supported" # return here to prevent the completion of the extensions return } $Values = $Values | Where-Object { # filter the result $_.Name -like "$WordToComplete*" # Join the flag back if we have an equal sign flag if ( $IsEqualFlag ) { __%[1]s_debug "Join the equal sign flag back to the completion value" $_.Name = $Flag + "=" + $_.Name } } # we sort the values in ascending order by name if keep order isn't passed if (($Directive -band $ShellCompDirectiveKeepOrder) -eq 0 ) { $Values = $Values | Sort-Object -Property Name } if (($Directive -band $ShellCompDirectiveNoFileComp) -ne 0 ) { __%[1]s_debug "ShellCompDirectiveNoFileComp is called" if ($Values.Length -eq 0) { # Just print an empty string here so the # shell does not start to complete paths. # We cannot use CompletionResult here because # it does not accept an empty string as argument. "" return } } # Get the current mode $Mode = (Get-PSReadLineKeyHandler | Where-Object {$_.Key -eq "Tab" }).Function __%[1]s_debug "Mode: $Mode" $Values | ForEach-Object { # store temporary because switch will overwrite $_ $comp = $_ # PowerShell supports three different completion modes # - TabCompleteNext (default windows style - on each key press the next option is displayed) # - Complete (works like bash) # - MenuComplete (works like zsh) # You set the mode with Set-PSReadLineKeyHandler -Key Tab -Function # CompletionResult Arguments: # 1) CompletionText text to be used as the auto completion result # 2) ListItemText text to be displayed in the suggestion list # 3) ResultType type of completion result # 4) ToolTip text for the tooltip with details about the object switch ($Mode) { # bash like "Complete" { if ($Values.Length -eq 1) { __%[1]s_debug "Only one completion left" # insert space after value [System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)") } else { # Add the proper number of spaces to align the descriptions while($comp.Name.Length -lt $Longest) { $comp.Name = $comp.Name + " " } # Check for empty description and only add parentheses if needed if ($($comp.Description) -eq " " ) { $Description = "" } else { $Description = " ($($comp.Description))" } [System.Management.Automation.CompletionResult]::new("$($comp.Name)$Description", "$($comp.Name)$Description", 'ParameterValue', "$($comp.Description)") } } # zsh like "MenuComplete" { # insert space after value # MenuComplete will automatically show the ToolTip of # the highlighted value at the bottom of the suggestions. [System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)") } # TabCompleteNext and in case we get something unknown Default { # Like MenuComplete but we don't want to add a space here because # the user need to press space anyway to get the completion. # Description will not be shown because that's not possible with TabCompleteNext [System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars), "$($comp.Name)", 'ParameterValue', "$($comp.Description)") } } } } Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock $__%[2]sCompleterBlock `, name, nameForVar, compCmd, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder, activeHelpEnvVar(name))) } func (c *Command) genPowerShellCompletion(w io.Writer, includeDesc bool) error { buf := new(bytes.Buffer) genPowerShellComp(buf, c.Name(), includeDesc) _, err := buf.WriteTo(w) return err } func (c *Command) genPowerShellCompletionFile(filename string, includeDesc bool) error { outFile, err := os.Create(filename) if err != nil { return err } defer outFile.Close() return c.genPowerShellCompletion(outFile, includeDesc) } // GenPowerShellCompletionFile generates powershell completion file without descriptions. func (c *Command) GenPowerShellCompletionFile(filename string) error { return c.genPowerShellCompletionFile(filename, false) } // GenPowerShellCompletion generates powershell completion file without descriptions // and writes it to the passed writer. func (c *Command) GenPowerShellCompletion(w io.Writer) error { return c.genPowerShellCompletion(w, false) } // GenPowerShellCompletionFileWithDesc generates powershell completion file with descriptions. func (c *Command) GenPowerShellCompletionFileWithDesc(filename string) error { return c.genPowerShellCompletionFile(filename, true) } // GenPowerShellCompletionWithDesc generates powershell completion file with descriptions // and writes it to the passed writer. func (c *Command) GenPowerShellCompletionWithDesc(w io.Writer) error { return c.genPowerShellCompletion(w, true) } ================================================ FILE: vendor/github.com/spf13/cobra/powershell_completions.md ================================================ # Generating PowerShell Completions For Your Own cobra.Command Please refer to [Shell Completions](shell_completions.md#powershell-completions) for details. ================================================ FILE: vendor/github.com/spf13/cobra/projects_using_cobra.md ================================================ ## Projects using Cobra - [Allero](https://github.com/allero-io/allero) - [Arewefastyet](https://benchmark.vitess.io) - [Arduino CLI](https://github.com/arduino/arduino-cli) - [Bleve](https://blevesearch.com/) - [Cilium](https://cilium.io/) - [CloudQuery](https://github.com/cloudquery/cloudquery) - [CockroachDB](https://www.cockroachlabs.com/) - [Constellation](https://github.com/edgelesssys/constellation) - [Cosmos SDK](https://github.com/cosmos/cosmos-sdk) - [Datree](https://github.com/datreeio/datree) - [Delve](https://github.com/derekparker/delve) - [Docker (distribution)](https://github.com/docker/distribution) - [Etcd](https://etcd.io/) - [Gardener](https://github.com/gardener/gardenctl) - [Giant Swarm's gsctl](https://github.com/giantswarm/gsctl) - [Git Bump](https://github.com/erdaltsksn/git-bump) - [GitHub CLI](https://github.com/cli/cli) - [GitHub Labeler](https://github.com/erdaltsksn/gh-label) - [Golangci-lint](https://golangci-lint.run) - [GopherJS](https://github.com/gopherjs/gopherjs) - [GoReleaser](https://goreleaser.com) - [Helm](https://helm.sh) - [Hugo](https://gohugo.io) - [Infracost](https://github.com/infracost/infracost) - [Istio](https://istio.io) - [Kool](https://github.com/kool-dev/kool) - [Kubernetes](https://kubernetes.io/) - [Kubescape](https://github.com/kubescape/kubescape) - [KubeVirt](https://github.com/kubevirt/kubevirt) - [Linkerd](https://linkerd.io/) - [Mattermost-server](https://github.com/mattermost/mattermost-server) - [Mercure](https://mercure.rocks/) - [Meroxa CLI](https://github.com/meroxa/cli) - [Metal Stack CLI](https://github.com/metal-stack/metalctl) - [Moby (former Docker)](https://github.com/moby/moby) - [Moldy](https://github.com/Moldy-Community/moldy) - [Multi-gitter](https://github.com/lindell/multi-gitter) - [Nanobox](https://github.com/nanobox-io/nanobox)/[Nanopack](https://github.com/nanopack) - [nFPM](https://nfpm.goreleaser.com) - [Okteto](https://github.com/okteto/okteto) - [OpenShift](https://www.openshift.com/) - [Ory Hydra](https://github.com/ory/hydra) - [Ory Kratos](https://github.com/ory/kratos) - [Pixie](https://github.com/pixie-io/pixie) - [Polygon Edge](https://github.com/0xPolygon/polygon-edge) - [Pouch](https://github.com/alibaba/pouch) - [ProjectAtomic (enterprise)](https://www.projectatomic.io/) - [Prototool](https://github.com/uber/prototool) - [Pulumi](https://www.pulumi.com) - [QRcp](https://github.com/claudiodangelis/qrcp) - [Random](https://github.com/erdaltsksn/random) - [Rclone](https://rclone.org/) - [Scaleway CLI](https://github.com/scaleway/scaleway-cli) - [Sia](https://github.com/SiaFoundation/siad) - [Skaffold](https://skaffold.dev/) - [Tendermint](https://github.com/tendermint/tendermint) - [Twitch CLI](https://github.com/twitchdev/twitch-cli) - [UpCloud CLI (`upctl`)](https://github.com/UpCloudLtd/upcloud-cli) - [Vitess](https://vitess.io) - VMware's [Tanzu Community Edition](https://github.com/vmware-tanzu/community-edition) & [Tanzu Framework](https://github.com/vmware-tanzu/tanzu-framework) - [Werf](https://werf.io/) - [ZITADEL](https://github.com/zitadel/zitadel) ================================================ FILE: vendor/github.com/spf13/cobra/shell_completions.go ================================================ // Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cobra import ( "github.com/spf13/pflag" ) // MarkFlagRequired instructs the various shell completion implementations to // prioritize the named flag when performing completion, // and causes your command to report an error if invoked without the flag. func (c *Command) MarkFlagRequired(name string) error { return MarkFlagRequired(c.Flags(), name) } // MarkPersistentFlagRequired instructs the various shell completion implementations to // prioritize the named persistent flag when performing completion, // and causes your command to report an error if invoked without the flag. func (c *Command) MarkPersistentFlagRequired(name string) error { return MarkFlagRequired(c.PersistentFlags(), name) } // MarkFlagRequired instructs the various shell completion implementations to // prioritize the named flag when performing completion, // and causes your command to report an error if invoked without the flag. func MarkFlagRequired(flags *pflag.FlagSet, name string) error { return flags.SetAnnotation(name, BashCompOneRequiredFlag, []string{"true"}) } // MarkFlagFilename instructs the various shell completion implementations to // limit completions for the named flag to the specified file extensions. func (c *Command) MarkFlagFilename(name string, extensions ...string) error { return MarkFlagFilename(c.Flags(), name, extensions...) } // MarkFlagCustom adds the BashCompCustom annotation to the named flag, if it exists. // The bash completion script will call the bash function f for the flag. // // This will only work for bash completion. // It is recommended to instead use c.RegisterFlagCompletionFunc(...) which allows // to register a Go function which will work across all shells. func (c *Command) MarkFlagCustom(name string, f string) error { return MarkFlagCustom(c.Flags(), name, f) } // MarkPersistentFlagFilename instructs the various shell completion // implementations to limit completions for the named persistent flag to the // specified file extensions. func (c *Command) MarkPersistentFlagFilename(name string, extensions ...string) error { return MarkFlagFilename(c.PersistentFlags(), name, extensions...) } // MarkFlagFilename instructs the various shell completion implementations to // limit completions for the named flag to the specified file extensions. func MarkFlagFilename(flags *pflag.FlagSet, name string, extensions ...string) error { return flags.SetAnnotation(name, BashCompFilenameExt, extensions) } // MarkFlagCustom adds the BashCompCustom annotation to the named flag, if it exists. // The bash completion script will call the bash function f for the flag. // // This will only work for bash completion. // It is recommended to instead use c.RegisterFlagCompletionFunc(...) which allows // to register a Go function which will work across all shells. func MarkFlagCustom(flags *pflag.FlagSet, name string, f string) error { return flags.SetAnnotation(name, BashCompCustom, []string{f}) } // MarkFlagDirname instructs the various shell completion implementations to // limit completions for the named flag to directory names. func (c *Command) MarkFlagDirname(name string) error { return MarkFlagDirname(c.Flags(), name) } // MarkPersistentFlagDirname instructs the various shell completion // implementations to limit completions for the named persistent flag to // directory names. func (c *Command) MarkPersistentFlagDirname(name string) error { return MarkFlagDirname(c.PersistentFlags(), name) } // MarkFlagDirname instructs the various shell completion implementations to // limit completions for the named flag to directory names. func MarkFlagDirname(flags *pflag.FlagSet, name string) error { return flags.SetAnnotation(name, BashCompSubdirsInDir, []string{}) } ================================================ FILE: vendor/github.com/spf13/cobra/shell_completions.md ================================================ # Generating shell completions Cobra can generate shell completions for multiple shells. The currently supported shells are: - Bash - Zsh - fish - PowerShell Cobra will automatically provide your program with a fully functional `completion` command, similarly to how it provides the `help` command. ## Creating your own completion command If you do not wish to use the default `completion` command, you can choose to provide your own, which will take precedence over the default one. (This also provides backwards-compatibility with programs that already have their own `completion` command.) If you are using the `cobra-cli` generator, which can be found at [spf13/cobra-cli](https://github.com/spf13/cobra-cli), you can create a completion command by running ```bash cobra-cli add completion ``` and then modifying the generated `cmd/completion.go` file to look something like this (writing the shell script to stdout allows the most flexible use): ```go var completionCmd = &cobra.Command{ Use: "completion [bash|zsh|fish|powershell]", Short: "Generate completion script", Long: fmt.Sprintf(`To load completions: Bash: $ source <(%[1]s completion bash) # To load completions for each session, execute once: # Linux: $ %[1]s completion bash > /etc/bash_completion.d/%[1]s # macOS: $ %[1]s completion bash > $(brew --prefix)/etc/bash_completion.d/%[1]s Zsh: # If shell completion is not already enabled in your environment, # you will need to enable it. You can execute the following once: $ echo "autoload -U compinit; compinit" >> ~/.zshrc # To load completions for each session, execute once: $ %[1]s completion zsh > "${fpath[1]}/_%[1]s" # You will need to start a new shell for this setup to take effect. fish: $ %[1]s completion fish | source # To load completions for each session, execute once: $ %[1]s completion fish > ~/.config/fish/completions/%[1]s.fish PowerShell: PS> %[1]s completion powershell | Out-String | Invoke-Expression # To load completions for every new session, run: PS> %[1]s completion powershell > %[1]s.ps1 # and source this file from your PowerShell profile. `,cmd.Root().Name()), DisableFlagsInUseLine: true, ValidArgs: []string{"bash", "zsh", "fish", "powershell"}, Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), Run: func(cmd *cobra.Command, args []string) { switch args[0] { case "bash": cmd.Root().GenBashCompletion(os.Stdout) case "zsh": cmd.Root().GenZshCompletion(os.Stdout) case "fish": cmd.Root().GenFishCompletion(os.Stdout, true) case "powershell": cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout) } }, } ``` **Note:** The cobra generator may include messages printed to stdout, for example, if the config file is loaded; this will break the auto-completion script so must be removed. ## Adapting the default completion command Cobra provides a few options for the default `completion` command. To configure such options you must set the `CompletionOptions` field on the *root* command. To tell Cobra *not* to provide the default `completion` command: ``` rootCmd.CompletionOptions.DisableDefaultCmd = true ``` To tell Cobra to mark the default `completion` command as *hidden*: ``` rootCmd.CompletionOptions.HiddenDefaultCmd = true ``` To tell Cobra *not* to provide the user with the `--no-descriptions` flag to the completion sub-commands: ``` rootCmd.CompletionOptions.DisableNoDescFlag = true ``` To tell Cobra to completely disable descriptions for completions: ``` rootCmd.CompletionOptions.DisableDescriptions = true ``` # Customizing completions The generated completion scripts will automatically handle completing commands and flags. However, you can make your completions much more powerful by providing information to complete your program's nouns and flag values. ## Completion of nouns ### Static completion of nouns Cobra allows you to provide a pre-defined list of completion choices for your nouns using the `ValidArgs` field. For example, if you want `kubectl get [tab][tab]` to show a list of valid "nouns" you have to set them. Some simplified code from `kubectl get` looks like: ```go validArgs = []string{ "pod", "node", "service", "replicationcontroller" } cmd := &cobra.Command{ Use: "get [(-o|--output=)json|yaml|template|...] (RESOURCE [NAME] | RESOURCE/NAME ...)", Short: "Display one or many resources", Long: get_long, Example: get_example, Run: func(cmd *cobra.Command, args []string) { cobra.CheckErr(RunGet(f, out, cmd, args)) }, ValidArgs: validArgs, } ``` Notice we put the `ValidArgs` field on the `get` sub-command. Doing so will give results like: ```bash $ kubectl get [tab][tab] node pod replicationcontroller service ``` #### Aliases for nouns If your nouns have aliases, you can define them alongside `ValidArgs` using `ArgAliases`: ```go argAliases = []string { "pods", "nodes", "services", "svc", "replicationcontrollers", "rc" } cmd := &cobra.Command{ ... ValidArgs: validArgs, ArgAliases: argAliases } ``` The aliases are shown to the user on tab completion only if no completions were found within sub-commands or `ValidArgs`. ### Dynamic completion of nouns In some cases it is not possible to provide a list of completions in advance. Instead, the list of completions must be determined at execution-time. In a similar fashion as for static completions, you can use the `ValidArgsFunction` field to provide a Go function that Cobra will execute when it needs the list of completion choices for the nouns of a command. Note that either `ValidArgs` or `ValidArgsFunction` can be used for a single cobra command, but not both. Simplified code from `helm status` looks like: ```go cmd := &cobra.Command{ Use: "status RELEASE_NAME", Short: "Display the status of the named release", Long: status_long, RunE: func(cmd *cobra.Command, args []string) { RunGet(args[0]) }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { return nil, cobra.ShellCompDirectiveNoFileComp } return getReleasesFromCluster(toComplete), cobra.ShellCompDirectiveNoFileComp }, } ``` Where `getReleasesFromCluster()` is a Go function that obtains the list of current Helm releases running on the Kubernetes cluster. Notice we put the `ValidArgsFunction` on the `status` sub-command. Let's assume the Helm releases on the cluster are: `harbor`, `notary`, `rook` and `thanos` then this dynamic completion will give results like: ```bash $ helm status [tab][tab] harbor notary rook thanos ``` You may have noticed the use of `cobra.ShellCompDirective`. These directives are bit fields allowing to control some shell completion behaviors for your particular completion. You can combine them with the bit-or operator such as `cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp` ```go // Indicates that the shell will perform its default behavior after completions // have been provided (this implies none of the other directives). ShellCompDirectiveDefault // Indicates an error occurred and completions should be ignored. ShellCompDirectiveError // Indicates that the shell should not add a space after the completion, // even if there is a single completion provided. ShellCompDirectiveNoSpace // Indicates that the shell should not provide file completion even when // no completion is provided. ShellCompDirectiveNoFileComp // Indicates that the returned completions should be used as file extension filters. // For example, to complete only files of the form *.json or *.yaml: // return []string{"yaml", "json"}, ShellCompDirectiveFilterFileExt // For flags, using MarkFlagFilename() and MarkPersistentFlagFilename() // is a shortcut to using this directive explicitly. // ShellCompDirectiveFilterFileExt // Indicates that only directory names should be provided in file completion. // For example: // return nil, ShellCompDirectiveFilterDirs // For flags, using MarkFlagDirname() is a shortcut to using this directive explicitly. // // To request directory names within another directory, the returned completions // should specify a single directory name within which to search. For example, // to complete directories within "themes/": // return []string{"themes"}, ShellCompDirectiveFilterDirs // ShellCompDirectiveFilterDirs // ShellCompDirectiveKeepOrder indicates that the shell should preserve the order // in which the completions are provided ShellCompDirectiveKeepOrder ``` ***Note***: When using the `ValidArgsFunction`, Cobra will call your registered function after having parsed all flags and arguments provided in the command-line. You therefore don't need to do this parsing yourself. For example, when a user calls `helm status --namespace my-rook-ns [tab][tab]`, Cobra will call your registered `ValidArgsFunction` after having parsed the `--namespace` flag, as it would have done when calling the `RunE` function. #### Debugging Cobra achieves dynamic completion through the use of a hidden command called by the completion script. To debug your Go completion code, you can call this hidden command directly: ```bash $ helm __complete status har harbor :4 Completion ended with directive: ShellCompDirectiveNoFileComp # This is on stderr ``` ***Important:*** If the noun to complete is empty (when the user has not yet typed any letters of that noun), you must pass an empty parameter to the `__complete` command: ```bash $ helm __complete status "" harbor notary rook thanos :4 Completion ended with directive: ShellCompDirectiveNoFileComp # This is on stderr ``` Calling the `__complete` command directly allows you to run the Go debugger to troubleshoot your code. You can also add printouts to your code; Cobra provides the following functions to use for printouts in Go completion code: ```go // Prints to the completion script debug file (if BASH_COMP_DEBUG_FILE // is set to a file path) and optionally prints to stderr. cobra.CompDebug(msg string, printToStdErr bool) { cobra.CompDebugln(msg string, printToStdErr bool) // Prints to the completion script debug file (if BASH_COMP_DEBUG_FILE // is set to a file path) and to stderr. cobra.CompError(msg string) cobra.CompErrorln(msg string) ``` ***Important:*** You should **not** leave traces that print directly to stdout in your completion code as they will be interpreted as completion choices by the completion script. Instead, use the cobra-provided debugging traces functions mentioned above. ## Completions for flags ### Mark flags as required Most of the time completions will only show sub-commands. But if a flag is required to make a sub-command work, you probably want it to show up when the user types [tab][tab]. You can mark a flag as 'Required' like so: ```go cmd.MarkFlagRequired("pod") cmd.MarkFlagRequired("container") ``` and you'll get something like ```bash $ kubectl exec [tab][tab] -c --container= -p --pod= ``` ### Specify dynamic flag completion As for nouns, Cobra provides a way of defining dynamic completion of flags. To provide a Go function that Cobra will execute when it needs the list of completion choices for a flag, you must register the function using the `command.RegisterFlagCompletionFunc()` function. ```go flagName := "output" cmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return []string{"json", "table", "yaml"}, cobra.ShellCompDirectiveDefault }) ``` Notice that calling `RegisterFlagCompletionFunc()` is done through the `command` with which the flag is associated. In our example this dynamic completion will give results like so: ```bash $ helm status --output [tab][tab] json table yaml ``` #### Debugging You can also easily debug your Go completion code for flags: ```bash $ helm __complete status --output "" json table yaml :4 Completion ended with directive: ShellCompDirectiveNoFileComp # This is on stderr ``` ***Important:*** You should **not** leave traces that print to stdout in your completion code as they will be interpreted as completion choices by the completion script. Instead, use the cobra-provided debugging traces functions mentioned further above. ### Specify valid filename extensions for flags that take a filename To limit completions of flag values to file names with certain extensions you can either use the different `MarkFlagFilename()` functions or a combination of `RegisterFlagCompletionFunc()` and `ShellCompDirectiveFilterFileExt`, like so: ```go flagName := "output" cmd.MarkFlagFilename(flagName, "yaml", "json") ``` or ```go flagName := "output" cmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return []string{"yaml", "json"}, ShellCompDirectiveFilterFileExt}) ``` ### Limit flag completions to directory names To limit completions of flag values to directory names you can either use the `MarkFlagDirname()` functions or a combination of `RegisterFlagCompletionFunc()` and `ShellCompDirectiveFilterDirs`, like so: ```go flagName := "output" cmd.MarkFlagDirname(flagName) ``` or ```go flagName := "output" cmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return nil, cobra.ShellCompDirectiveFilterDirs }) ``` To limit completions of flag values to directory names *within another directory* you can use a combination of `RegisterFlagCompletionFunc()` and `ShellCompDirectiveFilterDirs` like so: ```go flagName := "output" cmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return []string{"themes"}, cobra.ShellCompDirectiveFilterDirs }) ``` ### Descriptions for completions Cobra provides support for completion descriptions. Such descriptions are supported for each shell (however, for bash, it is only available in the [completion V2 version](#bash-completion-v2)). For commands and flags, Cobra will provide the descriptions automatically, based on usage information. For example, using zsh: ``` $ helm s[tab] search -- search for a keyword in charts show -- show information of a chart status -- displays the status of the named release ``` while using fish: ``` $ helm s[tab] search (search for a keyword in charts) show (show information of a chart) status (displays the status of the named release) ``` Cobra allows you to add descriptions to your own completions. Simply add the description text after each completion, following a `\t` separator. This technique applies to completions returned by `ValidArgs`, `ValidArgsFunction` and `RegisterFlagCompletionFunc()`. For example: ```go ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return []string{"harbor\tAn image registry", "thanos\tLong-term metrics"}, cobra.ShellCompDirectiveNoFileComp }} ``` or ```go ValidArgs: []string{"bash\tCompletions for bash", "zsh\tCompletions for zsh"} ``` If you don't want to show descriptions in the completions, you can add `--no-descriptions` to the default `completion` command to disable them, like: ```bash $ source <(helm completion bash) $ helm completion [tab][tab] bash (generate autocompletion script for bash) powershell (generate autocompletion script for powershell) fish (generate autocompletion script for fish) zsh (generate autocompletion script for zsh) $ source <(helm completion bash --no-descriptions) $ helm completion [tab][tab] bash fish powershell zsh ``` ## Bash completions ### Dependencies The bash completion script generated by Cobra requires the `bash_completion` package. You should update the help text of your completion command to show how to install the `bash_completion` package ([Kubectl docs](https://kubernetes.io/docs/tasks/tools/install-kubectl/#enabling-shell-autocompletion)) ### Aliases You can also configure `bash` aliases for your program and they will also support completions. ```bash alias aliasname=origcommand complete -o default -F __start_origcommand aliasname # and now when you run `aliasname` completion will make # suggestions as it did for `origcommand`. $ aliasname completion firstcommand secondcommand ``` ### Bash legacy dynamic completions For backward compatibility, Cobra still supports its bash legacy dynamic completion solution. Please refer to [Bash Completions](bash_completions.md) for details. ### Bash completion V2 Cobra provides two versions for bash completion. The original bash completion (which started it all!) can be used by calling `GenBashCompletion()` or `GenBashCompletionFile()`. A new V2 bash completion version is also available. This version can be used by calling `GenBashCompletionV2()` or `GenBashCompletionFileV2()`. The V2 version does **not** support the legacy dynamic completion (see [Bash Completions](bash_completions.md)) but instead works only with the Go dynamic completion solution described in this document. Unless your program already uses the legacy dynamic completion solution, it is recommended that you use the bash completion V2 solution which provides the following extra features: - Supports completion descriptions (like the other shells) - Small completion script of less than 300 lines (v1 generates scripts of thousands of lines; `kubectl` for example has a bash v1 completion script of over 13K lines) - Streamlined user experience thanks to a completion behavior aligned with the other shells `Bash` completion V2 supports descriptions for completions. When calling `GenBashCompletionV2()` or `GenBashCompletionFileV2()` you must provide these functions with a parameter indicating if the completions should be annotated with a description; Cobra will provide the description automatically based on usage information. You can choose to make this option configurable by your users. ``` # With descriptions $ helm s[tab][tab] search (search for a keyword in charts) status (display the status of the named release) show (show information of a chart) # Without descriptions $ helm s[tab][tab] search show status ``` **Note**: Cobra's default `completion` command uses bash completion V2. If for some reason you need to use bash completion V1, you will need to implement your own `completion` command. ## Zsh completions Cobra supports native zsh completion generated from the root `cobra.Command`. The generated completion script should be put somewhere in your `$fpath` and be named `_`. You will need to start a new shell for the completions to become available. Zsh supports descriptions for completions. Cobra will provide the description automatically, based on usage information. Cobra provides a way to completely disable such descriptions by using `GenZshCompletionNoDesc()` or `GenZshCompletionFileNoDesc()`. You can choose to make this a configurable option to your users. ``` # With descriptions $ helm s[tab] search -- search for a keyword in charts show -- show information of a chart status -- displays the status of the named release # Without descriptions $ helm s[tab] search show status ``` *Note*: Because of backward-compatibility requirements, we were forced to have a different API to disable completion descriptions between `zsh` and `fish`. ### Limitations * Custom completions implemented in Bash scripting (legacy) are not supported and will be ignored for `zsh` (including the use of the `BashCompCustom` flag annotation). * You should instead use `ValidArgsFunction` and `RegisterFlagCompletionFunc()` which are portable to the different shells (`bash`, `zsh`, `fish`, `powershell`). * The function `MarkFlagCustom()` is not supported and will be ignored for `zsh`. * You should instead use `RegisterFlagCompletionFunc()`. ### Zsh completions standardization Cobra 1.1 standardized its zsh completion support to align it with its other shell completions. Although the API was kept backward-compatible, some small changes in behavior were introduced. Please refer to [Zsh Completions](zsh_completions.md) for details. ## fish completions Cobra supports native fish completions generated from the root `cobra.Command`. You can use the `command.GenFishCompletion()` or `command.GenFishCompletionFile()` functions. You must provide these functions with a parameter indicating if the completions should be annotated with a description; Cobra will provide the description automatically based on usage information. You can choose to make this option configurable by your users. ``` # With descriptions $ helm s[tab] search (search for a keyword in charts) show (show information of a chart) status (displays the status of the named release) # Without descriptions $ helm s[tab] search show status ``` *Note*: Because of backward-compatibility requirements, we were forced to have a different API to disable completion descriptions between `zsh` and `fish`. ### Limitations * Custom completions implemented in bash scripting (legacy) are not supported and will be ignored for `fish` (including the use of the `BashCompCustom` flag annotation). * You should instead use `ValidArgsFunction` and `RegisterFlagCompletionFunc()` which are portable to the different shells (`bash`, `zsh`, `fish`, `powershell`). * The function `MarkFlagCustom()` is not supported and will be ignored for `fish`. * You should instead use `RegisterFlagCompletionFunc()`. * The following flag completion annotations are not supported and will be ignored for `fish`: * `BashCompFilenameExt` (filtering by file extension) * `BashCompSubdirsInDir` (filtering by directory) * The functions corresponding to the above annotations are consequently not supported and will be ignored for `fish`: * `MarkFlagFilename()` and `MarkPersistentFlagFilename()` (filtering by file extension) * `MarkFlagDirname()` and `MarkPersistentFlagDirname()` (filtering by directory) * Similarly, the following completion directives are not supported and will be ignored for `fish`: * `ShellCompDirectiveFilterFileExt` (filtering by file extension) * `ShellCompDirectiveFilterDirs` (filtering by directory) ## PowerShell completions Cobra supports native PowerShell completions generated from the root `cobra.Command`. You can use the `command.GenPowerShellCompletion()` or `command.GenPowerShellCompletionFile()` functions. To include descriptions use `command.GenPowerShellCompletionWithDesc()` and `command.GenPowerShellCompletionFileWithDesc()`. Cobra will provide the description automatically based on usage information. You can choose to make this option configurable by your users. The script is designed to support all three PowerShell completion modes: * TabCompleteNext (default windows style - on each key press the next option is displayed) * Complete (works like bash) * MenuComplete (works like zsh) You set the mode with `Set-PSReadLineKeyHandler -Key Tab -Function `. Descriptions are only displayed when using the `Complete` or `MenuComplete` mode. Users need PowerShell version 5.0 or above, which comes with Windows 10 and can be downloaded separately for Windows 7 or 8.1. They can then write the completions to a file and source this file from their PowerShell profile, which is referenced by the `$Profile` environment variable. See `Get-Help about_Profiles` for more info about PowerShell profiles. ``` # With descriptions and Mode 'Complete' $ helm s[tab] search (search for a keyword in charts) show (show information of a chart) status (displays the status of the named release) # With descriptions and Mode 'MenuComplete' The description of the current selected value will be displayed below the suggestions. $ helm s[tab] search show status search for a keyword in charts # Without descriptions $ helm s[tab] search show status ``` ### Aliases You can also configure `powershell` aliases for your program and they will also support completions. ``` $ sal aliasname origcommand $ Register-ArgumentCompleter -CommandName 'aliasname' -ScriptBlock $__origcommandCompleterBlock # and now when you run `aliasname` completion will make # suggestions as it did for `origcommand`. $ aliasname completion firstcommand secondcommand ``` The name of the completer block variable is of the form `$__CompleterBlock` where every `-` and `:` in the program name have been replaced with `_`, to respect powershell naming syntax. ### Limitations * Custom completions implemented in bash scripting (legacy) are not supported and will be ignored for `powershell` (including the use of the `BashCompCustom` flag annotation). * You should instead use `ValidArgsFunction` and `RegisterFlagCompletionFunc()` which are portable to the different shells (`bash`, `zsh`, `fish`, `powershell`). * The function `MarkFlagCustom()` is not supported and will be ignored for `powershell`. * You should instead use `RegisterFlagCompletionFunc()`. * The following flag completion annotations are not supported and will be ignored for `powershell`: * `BashCompFilenameExt` (filtering by file extension) * `BashCompSubdirsInDir` (filtering by directory) * The functions corresponding to the above annotations are consequently not supported and will be ignored for `powershell`: * `MarkFlagFilename()` and `MarkPersistentFlagFilename()` (filtering by file extension) * `MarkFlagDirname()` and `MarkPersistentFlagDirname()` (filtering by directory) * Similarly, the following completion directives are not supported and will be ignored for `powershell`: * `ShellCompDirectiveFilterFileExt` (filtering by file extension) * `ShellCompDirectiveFilterDirs` (filtering by directory) ================================================ FILE: vendor/github.com/spf13/cobra/user_guide.md ================================================ # User Guide While you are welcome to provide your own organization, typically a Cobra-based application will follow the following organizational structure: ``` ▾ appName/ ▾ cmd/ add.go your.go commands.go here.go main.go ``` In a Cobra app, typically the main.go file is very bare. It serves one purpose: initializing Cobra. ```go package main import ( "{pathToYourApp}/cmd" ) func main() { cmd.Execute() } ``` ## Using the Cobra Generator Cobra-CLI is its own program that will create your application and add any commands you want. It's the easiest way to incorporate Cobra into your application. For complete details on using the Cobra generator, please refer to [The Cobra-CLI Generator README](https://github.com/spf13/cobra-cli/blob/main/README.md) ## Using the Cobra Library To manually implement Cobra you need to create a bare main.go file and a rootCmd file. You will optionally provide additional commands as you see fit. ### Create rootCmd Cobra doesn't require any special constructors. Simply create your commands. Ideally you place this in app/cmd/root.go: ```go var rootCmd = &cobra.Command{ Use: "hugo", Short: "Hugo is a very fast static site generator", Long: `A Fast and Flexible Static Site Generator built with love by spf13 and friends in Go. Complete documentation is available at https://gohugo.io/documentation/`, Run: func(cmd *cobra.Command, args []string) { // Do Stuff Here }, } func Execute() { if err := rootCmd.Execute(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } ``` You will additionally define flags and handle configuration in your init() function. For example cmd/root.go: ```go package cmd import ( "fmt" "os" "github.com/spf13/cobra" "github.com/spf13/viper" ) var ( // Used for flags. cfgFile string userLicense string rootCmd = &cobra.Command{ Use: "cobra-cli", Short: "A generator for Cobra based Applications", Long: `Cobra is a CLI library for Go that empowers applications. This application is a tool to generate the needed files to quickly create a Cobra application.`, } ) // Execute executes the root command. func Execute() error { return rootCmd.Execute() } func init() { cobra.OnInitialize(initConfig) rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobra.yaml)") rootCmd.PersistentFlags().StringP("author", "a", "YOUR NAME", "author name for copyright attribution") rootCmd.PersistentFlags().StringVarP(&userLicense, "license", "l", "", "name of license for the project") rootCmd.PersistentFlags().Bool("viper", true, "use Viper for configuration") viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author")) viper.BindPFlag("useViper", rootCmd.PersistentFlags().Lookup("viper")) viper.SetDefault("author", "NAME HERE ") viper.SetDefault("license", "apache") rootCmd.AddCommand(addCmd) rootCmd.AddCommand(initCmd) } func initConfig() { if cfgFile != "" { // Use config file from the flag. viper.SetConfigFile(cfgFile) } else { // Find home directory. home, err := os.UserHomeDir() cobra.CheckErr(err) // Search config in home directory with name ".cobra" (without extension). viper.AddConfigPath(home) viper.SetConfigType("yaml") viper.SetConfigName(".cobra") } viper.AutomaticEnv() if err := viper.ReadInConfig(); err == nil { fmt.Println("Using config file:", viper.ConfigFileUsed()) } } ``` ### Create your main.go With the root command you need to have your main function execute it. Execute should be run on the root for clarity, though it can be called on any command. In a Cobra app, typically the main.go file is very bare. It serves one purpose: to initialize Cobra. ```go package main import ( "{pathToYourApp}/cmd" ) func main() { cmd.Execute() } ``` ### Create additional commands Additional commands can be defined and typically are each given their own file inside of the cmd/ directory. If you wanted to create a version command you would create cmd/version.go and populate it with the following: ```go package cmd import ( "fmt" "github.com/spf13/cobra" ) func init() { rootCmd.AddCommand(versionCmd) } var versionCmd = &cobra.Command{ Use: "version", Short: "Print the version number of Hugo", Long: `All software has versions. This is Hugo's`, Run: func(cmd *cobra.Command, args []string) { fmt.Println("Hugo Static Site Generator v0.9 -- HEAD") }, } ``` ### Organizing subcommands A command may have subcommands which in turn may have other subcommands. This is achieved by using `AddCommand`. In some cases, especially in larger applications, each subcommand may be defined in its own go package. The suggested approach is for the parent command to use `AddCommand` to add its most immediate subcommands. For example, consider the following directory structure: ```text ├── cmd │   ├── root.go │   └── sub1 │   ├── sub1.go │   └── sub2 │   ├── leafA.go │   ├── leafB.go │   └── sub2.go └── main.go ``` In this case: * The `init` function of `root.go` adds the command defined in `sub1.go` to the root command. * The `init` function of `sub1.go` adds the command defined in `sub2.go` to the sub1 command. * The `init` function of `sub2.go` adds the commands defined in `leafA.go` and `leafB.go` to the sub2 command. This approach ensures the subcommands are always included at compile time while avoiding cyclic references. ### Returning and handling errors If you wish to return an error to the caller of a command, `RunE` can be used. ```go package cmd import ( "fmt" "github.com/spf13/cobra" ) func init() { rootCmd.AddCommand(tryCmd) } var tryCmd = &cobra.Command{ Use: "try", Short: "Try and possibly fail at something", RunE: func(cmd *cobra.Command, args []string) error { if err := someFunc(); err != nil { return err } return nil }, } ``` The error can then be caught at the execute function call. ## Working with Flags Flags provide modifiers to control how the action command operates. ### Assign flags to a command Since the flags are defined and used in different locations, we need to define a variable outside with the correct scope to assign the flag to work with. ```go var Verbose bool var Source string ``` There are two different approaches to assign a flag. ### Persistent Flags A flag can be 'persistent', meaning that this flag will be available to the command it's assigned to as well as every command under that command. For global flags, assign a flag as a persistent flag on the root. ```go rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output") ``` ### Local Flags A flag can also be assigned locally, which will only apply to that specific command. ```go localCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from") ``` ### Local Flag on Parent Commands By default, Cobra only parses local flags on the target command, and any local flags on parent commands are ignored. By enabling `Command.TraverseChildren`, Cobra will parse local flags on each command before executing the target command. ```go command := cobra.Command{ Use: "print [OPTIONS] [COMMANDS]", TraverseChildren: true, } ``` ### Bind Flags with Config You can also bind your flags with [viper](https://github.com/spf13/viper): ```go var author string func init() { rootCmd.PersistentFlags().StringVar(&author, "author", "YOUR NAME", "Author name for copyright attribution") viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author")) } ``` In this example, the persistent flag `author` is bound with `viper`. **Note**: the variable `author` will not be set to the value from config, when the `--author` flag is provided by user. More in [viper documentation](https://github.com/spf13/viper#working-with-flags). ### Required flags Flags are optional by default. If instead you wish your command to report an error when a flag has not been set, mark it as required: ```go rootCmd.Flags().StringVarP(&Region, "region", "r", "", "AWS region (required)") rootCmd.MarkFlagRequired("region") ``` Or, for persistent flags: ```go rootCmd.PersistentFlags().StringVarP(&Region, "region", "r", "", "AWS region (required)") rootCmd.MarkPersistentFlagRequired("region") ``` ### Flag Groups If you have different flags that must be provided together (e.g. if they provide the `--username` flag they MUST provide the `--password` flag as well) then Cobra can enforce that requirement: ```go rootCmd.Flags().StringVarP(&u, "username", "u", "", "Username (required if password is set)") rootCmd.Flags().StringVarP(&pw, "password", "p", "", "Password (required if username is set)") rootCmd.MarkFlagsRequiredTogether("username", "password") ``` You can also prevent different flags from being provided together if they represent mutually exclusive options such as specifying an output format as either `--json` or `--yaml` but never both: ```go rootCmd.Flags().BoolVar(&ofJson, "json", false, "Output in JSON") rootCmd.Flags().BoolVar(&ofYaml, "yaml", false, "Output in YAML") rootCmd.MarkFlagsMutuallyExclusive("json", "yaml") ``` In both of these cases: - both local and persistent flags can be used - **NOTE:** the group is only enforced on commands where every flag is defined - a flag may appear in multiple groups - a group may contain any number of flags ## Positional and Custom Arguments Validation of positional arguments can be specified using the `Args` field of `Command`. The following validators are built in: - Number of arguments: - `NoArgs` - report an error if there are any positional args. - `ArbitraryArgs` - accept any number of args. - `MinimumNArgs(int)` - report an error if less than N positional args are provided. - `MaximumNArgs(int)` - report an error if more than N positional args are provided. - `ExactArgs(int)` - report an error if there are not exactly N positional args. - `RangeArgs(min, max)` - report an error if the number of args is not between `min` and `max`. - Content of the arguments: - `OnlyValidArgs` - report an error if there are any positional args not specified in the `ValidArgs` field of `Command`, which can optionally be set to a list of valid values for positional args. If `Args` is undefined or `nil`, it defaults to `ArbitraryArgs`. Moreover, `MatchAll(pargs ...PositionalArgs)` enables combining existing checks with arbitrary other checks. For instance, if you want to report an error if there are not exactly N positional args OR if there are any positional args that are not in the `ValidArgs` field of `Command`, you can call `MatchAll` on `ExactArgs` and `OnlyValidArgs`, as shown below: ```go var cmd = &cobra.Command{ Short: "hello", Args: cobra.MatchAll(cobra.ExactArgs(2), cobra.OnlyValidArgs), Run: func(cmd *cobra.Command, args []string) { fmt.Println("Hello, World!") }, } ``` It is possible to set any custom validator that satisfies `func(cmd *cobra.Command, args []string) error`. For example: ```go var cmd = &cobra.Command{ Short: "hello", Args: func(cmd *cobra.Command, args []string) error { // Optionally run one of the validators provided by cobra if err := cobra.MinimumNArgs(1)(cmd, args); err != nil { return err } // Run the custom validation logic if myapp.IsValidColor(args[0]) { return nil } return fmt.Errorf("invalid color specified: %s", args[0]) }, Run: func(cmd *cobra.Command, args []string) { fmt.Println("Hello, World!") }, } ``` ## Example In the example below, we have defined three commands. Two are at the top level and one (cmdTimes) is a child of one of the top commands. In this case the root is not executable, meaning that a subcommand is required. This is accomplished by not providing a 'Run' for the 'rootCmd'. We have only defined one flag for a single command. More documentation about flags is available at https://github.com/spf13/pflag ```go package main import ( "fmt" "strings" "github.com/spf13/cobra" ) func main() { var echoTimes int var cmdPrint = &cobra.Command{ Use: "print [string to print]", Short: "Print anything to the screen", Long: `print is for printing anything back to the screen. For many years people have printed back to the screen.`, Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { fmt.Println("Print: " + strings.Join(args, " ")) }, } var cmdEcho = &cobra.Command{ Use: "echo [string to echo]", Short: "Echo anything to the screen", Long: `echo is for echoing anything back. Echo works a lot like print, except it has a child command.`, Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { fmt.Println("Echo: " + strings.Join(args, " ")) }, } var cmdTimes = &cobra.Command{ Use: "times [string to echo]", Short: "Echo anything to the screen more times", Long: `echo things multiple times back to the user by providing a count and a string.`, Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { for i := 0; i < echoTimes; i++ { fmt.Println("Echo: " + strings.Join(args, " ")) } }, } cmdTimes.Flags().IntVarP(&echoTimes, "times", "t", 1, "times to echo the input") var rootCmd = &cobra.Command{Use: "app"} rootCmd.AddCommand(cmdPrint, cmdEcho) cmdEcho.AddCommand(cmdTimes) rootCmd.Execute() } ``` For a more complete example of a larger application, please checkout [Hugo](https://gohugo.io/). ## Help Command Cobra automatically adds a help command to your application when you have subcommands. This will be called when a user runs 'app help'. Additionally, help will also support all other commands as input. Say, for instance, you have a command called 'create' without any additional configuration; Cobra will work when 'app help create' is called. Every command will automatically have the '--help' flag added. ### Example The following output is automatically generated by Cobra. Nothing beyond the command and flag definitions are needed. $ cobra-cli help Cobra is a CLI library for Go that empowers applications. This application is a tool to generate the needed files to quickly create a Cobra application. Usage: cobra-cli [command] Available Commands: add Add a command to a Cobra Application completion Generate the autocompletion script for the specified shell help Help about any command init Initialize a Cobra Application Flags: -a, --author string author name for copyright attribution (default "YOUR NAME") --config string config file (default is $HOME/.cobra.yaml) -h, --help help for cobra-cli -l, --license string name of license for the project --viper use Viper for configuration Use "cobra-cli [command] --help" for more information about a command. Help is just a command like any other. There is no special logic or behavior around it. In fact, you can provide your own if you want. ### Grouping commands in help Cobra supports grouping of available commands in the help output. To group commands, each group must be explicitly defined using `AddGroup()` on the parent command. Then a subcommand can be added to a group using the `GroupID` element of that subcommand. The groups will appear in the help output in the same order as they are defined using different calls to `AddGroup()`. If you use the generated `help` or `completion` commands, you can set their group ids using `SetHelpCommandGroupId()` and `SetCompletionCommandGroupId()` on the root command, respectively. ### Defining your own help You can provide your own Help command or your own template for the default command to use with the following functions: ```go cmd.SetHelpCommand(cmd *Command) cmd.SetHelpFunc(f func(*Command, []string)) cmd.SetHelpTemplate(s string) ``` The latter two will also apply to any children commands. ## Usage Message When the user provides an invalid flag or invalid command, Cobra responds by showing the user the 'usage'. ### Example You may recognize this from the help above. That's because the default help embeds the usage as part of its output. $ cobra-cli --invalid Error: unknown flag: --invalid Usage: cobra-cli [command] Available Commands: add Add a command to a Cobra Application completion Generate the autocompletion script for the specified shell help Help about any command init Initialize a Cobra Application Flags: -a, --author string author name for copyright attribution (default "YOUR NAME") --config string config file (default is $HOME/.cobra.yaml) -h, --help help for cobra-cli -l, --license string name of license for the project --viper use Viper for configuration Use "cobra [command] --help" for more information about a command. ### Defining your own usage You can provide your own usage function or template for Cobra to use. Like help, the function and template are overridable through public methods: ```go cmd.SetUsageFunc(f func(*Command) error) cmd.SetUsageTemplate(s string) ``` ## Version Flag Cobra adds a top-level '--version' flag if the Version field is set on the root command. Running an application with the '--version' flag will print the version to stdout using the version template. The template can be customized using the `cmd.SetVersionTemplate(s string)` function. ## PreRun and PostRun Hooks It is possible to run functions before or after the main `Run` function of your command. The `PersistentPreRun` and `PreRun` functions will be executed before `Run`. `PersistentPostRun` and `PostRun` will be executed after `Run`. The `Persistent*Run` functions will be inherited by children if they do not declare their own. These functions are run in the following order: - `PersistentPreRun` - `PreRun` - `Run` - `PostRun` - `PersistentPostRun` An example of two commands which use all of these features is below. When the subcommand is executed, it will run the root command's `PersistentPreRun` but not the root command's `PersistentPostRun`: ```go package main import ( "fmt" "github.com/spf13/cobra" ) func main() { var rootCmd = &cobra.Command{ Use: "root [sub]", Short: "My root command", PersistentPreRun: func(cmd *cobra.Command, args []string) { fmt.Printf("Inside rootCmd PersistentPreRun with args: %v\n", args) }, PreRun: func(cmd *cobra.Command, args []string) { fmt.Printf("Inside rootCmd PreRun with args: %v\n", args) }, Run: func(cmd *cobra.Command, args []string) { fmt.Printf("Inside rootCmd Run with args: %v\n", args) }, PostRun: func(cmd *cobra.Command, args []string) { fmt.Printf("Inside rootCmd PostRun with args: %v\n", args) }, PersistentPostRun: func(cmd *cobra.Command, args []string) { fmt.Printf("Inside rootCmd PersistentPostRun with args: %v\n", args) }, } var subCmd = &cobra.Command{ Use: "sub [no options!]", Short: "My subcommand", PreRun: func(cmd *cobra.Command, args []string) { fmt.Printf("Inside subCmd PreRun with args: %v\n", args) }, Run: func(cmd *cobra.Command, args []string) { fmt.Printf("Inside subCmd Run with args: %v\n", args) }, PostRun: func(cmd *cobra.Command, args []string) { fmt.Printf("Inside subCmd PostRun with args: %v\n", args) }, PersistentPostRun: func(cmd *cobra.Command, args []string) { fmt.Printf("Inside subCmd PersistentPostRun with args: %v\n", args) }, } rootCmd.AddCommand(subCmd) rootCmd.SetArgs([]string{""}) rootCmd.Execute() fmt.Println() rootCmd.SetArgs([]string{"sub", "arg1", "arg2"}) rootCmd.Execute() } ``` Output: ``` Inside rootCmd PersistentPreRun with args: [] Inside rootCmd PreRun with args: [] Inside rootCmd Run with args: [] Inside rootCmd PostRun with args: [] Inside rootCmd PersistentPostRun with args: [] Inside rootCmd PersistentPreRun with args: [arg1 arg2] Inside subCmd PreRun with args: [arg1 arg2] Inside subCmd Run with args: [arg1 arg2] Inside subCmd PostRun with args: [arg1 arg2] Inside subCmd PersistentPostRun with args: [arg1 arg2] ``` ## Suggestions when "unknown command" happens Cobra will print automatic suggestions when "unknown command" errors happen. This allows Cobra to behave similarly to the `git` command when a typo happens. For example: ``` $ hugo srever Error: unknown command "srever" for "hugo" Did you mean this? server Run 'hugo --help' for usage. ``` Suggestions are automatically generated based on existing subcommands and use an implementation of [Levenshtein distance](https://en.wikipedia.org/wiki/Levenshtein_distance). Every registered command that matches a minimum distance of 2 (ignoring case) will be displayed as a suggestion. If you need to disable suggestions or tweak the string distance in your command, use: ```go command.DisableSuggestions = true ``` or ```go command.SuggestionsMinimumDistance = 1 ``` You can also explicitly set names for which a given command will be suggested using the `SuggestFor` attribute. This allows suggestions for strings that are not close in terms of string distance, but make sense in your set of commands but for which you don't want aliases. Example: ``` $ kubectl remove Error: unknown command "remove" for "kubectl" Did you mean this? delete Run 'kubectl help' for usage. ``` ## Generating documentation for your command Cobra can generate documentation based on subcommands, flags, etc. Read more about it in the [docs generation documentation](doc/README.md). ## Generating shell completions Cobra can generate a shell-completion file for the following shells: bash, zsh, fish, PowerShell. If you add more information to your commands, these completions can be amazingly powerful and flexible. Read more about it in [Shell Completions](shell_completions.md). ## Providing Active Help Cobra makes use of the shell-completion system to define a framework allowing you to provide Active Help to your users. Active Help are messages (hints, warnings, etc) printed as the program is being used. Read more about it in [Active Help](active_help.md). ================================================ FILE: vendor/github.com/spf13/cobra/zsh_completions.go ================================================ // Copyright 2013-2023 The Cobra Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cobra import ( "bytes" "fmt" "io" "os" ) // GenZshCompletionFile generates zsh completion file including descriptions. func (c *Command) GenZshCompletionFile(filename string) error { return c.genZshCompletionFile(filename, true) } // GenZshCompletion generates zsh completion file including descriptions // and writes it to the passed writer. func (c *Command) GenZshCompletion(w io.Writer) error { return c.genZshCompletion(w, true) } // GenZshCompletionFileNoDesc generates zsh completion file without descriptions. func (c *Command) GenZshCompletionFileNoDesc(filename string) error { return c.genZshCompletionFile(filename, false) } // GenZshCompletionNoDesc generates zsh completion file without descriptions // and writes it to the passed writer. func (c *Command) GenZshCompletionNoDesc(w io.Writer) error { return c.genZshCompletion(w, false) } // MarkZshCompPositionalArgumentFile only worked for zsh and its behavior was // not consistent with Bash completion. It has therefore been disabled. // Instead, when no other completion is specified, file completion is done by // default for every argument. One can disable file completion on a per-argument // basis by using ValidArgsFunction and ShellCompDirectiveNoFileComp. // To achieve file extension filtering, one can use ValidArgsFunction and // ShellCompDirectiveFilterFileExt. // // Deprecated func (c *Command) MarkZshCompPositionalArgumentFile(argPosition int, patterns ...string) error { return nil } // MarkZshCompPositionalArgumentWords only worked for zsh. It has therefore // been disabled. // To achieve the same behavior across all shells, one can use // ValidArgs (for the first argument only) or ValidArgsFunction for // any argument (can include the first one also). // // Deprecated func (c *Command) MarkZshCompPositionalArgumentWords(argPosition int, words ...string) error { return nil } func (c *Command) genZshCompletionFile(filename string, includeDesc bool) error { outFile, err := os.Create(filename) if err != nil { return err } defer outFile.Close() return c.genZshCompletion(outFile, includeDesc) } func (c *Command) genZshCompletion(w io.Writer, includeDesc bool) error { buf := new(bytes.Buffer) genZshComp(buf, c.Name(), includeDesc) _, err := buf.WriteTo(w) return err } func genZshComp(buf io.StringWriter, name string, includeDesc bool) { compCmd := ShellCompRequestCmd if !includeDesc { compCmd = ShellCompNoDescRequestCmd } WriteStringAndCheck(buf, fmt.Sprintf(`#compdef %[1]s compdef _%[1]s %[1]s # zsh completion for %-36[1]s -*- shell-script -*- __%[1]s_debug() { local file="$BASH_COMP_DEBUG_FILE" if [[ -n ${file} ]]; then echo "$*" >> "${file}" fi } _%[1]s() { local shellCompDirectiveError=%[3]d local shellCompDirectiveNoSpace=%[4]d local shellCompDirectiveNoFileComp=%[5]d local shellCompDirectiveFilterFileExt=%[6]d local shellCompDirectiveFilterDirs=%[7]d local shellCompDirectiveKeepOrder=%[8]d local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace keepOrder local -a completions __%[1]s_debug "\n========= starting completion logic ==========" __%[1]s_debug "CURRENT: ${CURRENT}, words[*]: ${words[*]}" # The user could have moved the cursor backwards on the command-line. # We need to trigger completion from the $CURRENT location, so we need # to truncate the command-line ($words) up to the $CURRENT location. # (We cannot use $CURSOR as its value does not work when a command is an alias.) words=("${=words[1,CURRENT]}") __%[1]s_debug "Truncated words[*]: ${words[*]}," lastParam=${words[-1]} lastChar=${lastParam[-1]} __%[1]s_debug "lastParam: ${lastParam}, lastChar: ${lastChar}" # For zsh, when completing a flag with an = (e.g., %[1]s -n=) # completions must be prefixed with the flag setopt local_options BASH_REMATCH if [[ "${lastParam}" =~ '-.*=' ]]; then # We are dealing with a flag with an = flagPrefix="-P ${BASH_REMATCH}" fi # Prepare the command to obtain completions requestComp="${words[1]} %[2]s ${words[2,-1]}" if [ "${lastChar}" = "" ]; then # If the last parameter is complete (there is a space following it) # We add an extra empty parameter so we can indicate this to the go completion code. __%[1]s_debug "Adding extra empty parameter" requestComp="${requestComp} \"\"" fi __%[1]s_debug "About to call: eval ${requestComp}" # Use eval to handle any environment variables and such out=$(eval ${requestComp} 2>/dev/null) __%[1]s_debug "completion output: ${out}" # Extract the directive integer following a : from the last line local lastLine while IFS='\n' read -r line; do lastLine=${line} done < <(printf "%%s\n" "${out[@]}") __%[1]s_debug "last line: ${lastLine}" if [ "${lastLine[1]}" = : ]; then directive=${lastLine[2,-1]} # Remove the directive including the : and the newline local suffix (( suffix=${#lastLine}+2)) out=${out[1,-$suffix]} else # There is no directive specified. Leave $out as is. __%[1]s_debug "No directive found. Setting do default" directive=0 fi __%[1]s_debug "directive: ${directive}" __%[1]s_debug "completions: ${out}" __%[1]s_debug "flagPrefix: ${flagPrefix}" if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then __%[1]s_debug "Completion received error. Ignoring completions." return fi local activeHelpMarker="%[9]s" local endIndex=${#activeHelpMarker} local startIndex=$((${#activeHelpMarker}+1)) local hasActiveHelp=0 while IFS='\n' read -r comp; do # Check if this is an activeHelp statement (i.e., prefixed with $activeHelpMarker) if [ "${comp[1,$endIndex]}" = "$activeHelpMarker" ];then __%[1]s_debug "ActiveHelp found: $comp" comp="${comp[$startIndex,-1]}" if [ -n "$comp" ]; then compadd -x "${comp}" __%[1]s_debug "ActiveHelp will need delimiter" hasActiveHelp=1 fi continue fi if [ -n "$comp" ]; then # If requested, completions are returned with a description. # The description is preceded by a TAB character. # For zsh's _describe, we need to use a : instead of a TAB. # We first need to escape any : as part of the completion itself. comp=${comp//:/\\:} local tab="$(printf '\t')" comp=${comp//$tab/:} __%[1]s_debug "Adding completion: ${comp}" completions+=${comp} lastComp=$comp fi done < <(printf "%%s\n" "${out[@]}") # Add a delimiter after the activeHelp statements, but only if: # - there are completions following the activeHelp statements, or # - file completion will be performed (so there will be choices after the activeHelp) if [ $hasActiveHelp -eq 1 ]; then if [ ${#completions} -ne 0 ] || [ $((directive & shellCompDirectiveNoFileComp)) -eq 0 ]; then __%[1]s_debug "Adding activeHelp delimiter" compadd -x "--" hasActiveHelp=0 fi fi if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then __%[1]s_debug "Activating nospace." noSpace="-S ''" fi if [ $((directive & shellCompDirectiveKeepOrder)) -ne 0 ]; then __%[1]s_debug "Activating keep order." keepOrder="-V" fi if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then # File extension filtering local filteringCmd filteringCmd='_files' for filter in ${completions[@]}; do if [ ${filter[1]} != '*' ]; then # zsh requires a glob pattern to do file filtering filter="\*.$filter" fi filteringCmd+=" -g $filter" done filteringCmd+=" ${flagPrefix}" __%[1]s_debug "File filtering command: $filteringCmd" _arguments '*:filename:'"$filteringCmd" elif [ $((directive & shellCompDirectiveFilterDirs)) -ne 0 ]; then # File completion for directories only local subdir subdir="${completions[1]}" if [ -n "$subdir" ]; then __%[1]s_debug "Listing directories in $subdir" pushd "${subdir}" >/dev/null 2>&1 else __%[1]s_debug "Listing directories in ." fi local result _arguments '*:dirname:_files -/'" ${flagPrefix}" result=$? if [ -n "$subdir" ]; then popd >/dev/null 2>&1 fi return $result else __%[1]s_debug "Calling _describe" if eval _describe $keepOrder "completions" completions $flagPrefix $noSpace; then __%[1]s_debug "_describe found some completions" # Return the success of having called _describe return 0 else __%[1]s_debug "_describe did not find completions." __%[1]s_debug "Checking if we should do file completion." if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then __%[1]s_debug "deactivating file completion" # We must return an error code here to let zsh know that there were no # completions found by _describe; this is what will trigger other # matching algorithms to attempt to find completions. # For example zsh can match letters in the middle of words. return 1 else # Perform file completion __%[1]s_debug "Activating file completion" # We must return the result of this command, so it must be the # last command, or else we must store its result to return it. _arguments '*:filename:_files'" ${flagPrefix}" fi fi fi } # don't run the completion function when being source-ed or eval-ed if [ "$funcstack[1]" = "_%[1]s" ]; then _%[1]s fi `, name, compCmd, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder, activeHelpMarker)) } ================================================ FILE: vendor/github.com/spf13/cobra/zsh_completions.md ================================================ ## Generating Zsh Completion For Your cobra.Command Please refer to [Shell Completions](shell_completions.md) for details. ## Zsh completions standardization Cobra 1.1 standardized its zsh completion support to align it with its other shell completions. Although the API was kept backwards-compatible, some small changes in behavior were introduced. ### Deprecation summary See further below for more details on these deprecations. * `cmd.MarkZshCompPositionalArgumentFile(pos, []string{})` is no longer needed. It is therefore **deprecated** and silently ignored. * `cmd.MarkZshCompPositionalArgumentFile(pos, glob[])` is **deprecated** and silently ignored. * Instead use `ValidArgsFunction` with `ShellCompDirectiveFilterFileExt`. * `cmd.MarkZshCompPositionalArgumentWords()` is **deprecated** and silently ignored. * Instead use `ValidArgsFunction`. ### Behavioral changes **Noun completion** |Old behavior|New behavior| |---|---| |No file completion by default (opposite of bash)|File completion by default; use `ValidArgsFunction` with `ShellCompDirectiveNoFileComp` to turn off file completion on a per-argument basis| |Completion of flag names without the `-` prefix having been typed|Flag names are only completed if the user has typed the first `-`| `cmd.MarkZshCompPositionalArgumentFile(pos, []string{})` used to turn on file completion on a per-argument position basis|File completion for all arguments by default; `cmd.MarkZshCompPositionalArgumentFile()` is **deprecated** and silently ignored| |`cmd.MarkZshCompPositionalArgumentFile(pos, glob[])` used to turn on file completion **with glob filtering** on a per-argument position basis (zsh-specific)|`cmd.MarkZshCompPositionalArgumentFile()` is **deprecated** and silently ignored; use `ValidArgsFunction` with `ShellCompDirectiveFilterFileExt` for file **extension** filtering (not full glob filtering)| |`cmd.MarkZshCompPositionalArgumentWords(pos, words[])` used to provide completion choices on a per-argument position basis (zsh-specific)|`cmd.MarkZshCompPositionalArgumentWords()` is **deprecated** and silently ignored; use `ValidArgsFunction` to achieve the same behavior| **Flag-value completion** |Old behavior|New behavior| |---|---| |No file completion by default (opposite of bash)|File completion by default; use `RegisterFlagCompletionFunc()` with `ShellCompDirectiveNoFileComp` to turn off file completion| |`cmd.MarkFlagFilename(flag, []string{})` and similar used to turn on file completion|File completion by default; `cmd.MarkFlagFilename(flag, []string{})` no longer needed in this context and silently ignored| |`cmd.MarkFlagFilename(flag, glob[])` used to turn on file completion **with glob filtering** (syntax of `[]string{"*.yaml", "*.yml"}` incompatible with bash)|Will continue to work, however, support for bash syntax is added and should be used instead so as to work for all shells (`[]string{"yaml", "yml"}`)| |`cmd.MarkFlagDirname(flag)` only completes directories (zsh-specific)|Has been added for all shells| |Completion of a flag name does not repeat, unless flag is of type `*Array` or `*Slice` (not supported by bash)|Retained for `zsh` and added to `fish`| |Completion of a flag name does not provide the `=` form (unlike bash)|Retained for `zsh` and added to `fish`| **Improvements** * Custom completion support (`ValidArgsFunction` and `RegisterFlagCompletionFunc()`) * File completion by default if no other completions found * Handling of required flags * File extension filtering no longer mutually exclusive with bash usage * Completion of directory names *within* another directory * Support for `=` form of flags ================================================ FILE: vendor/github.com/spf13/pflag/.gitignore ================================================ .idea/* ================================================ FILE: vendor/github.com/spf13/pflag/.travis.yml ================================================ sudo: false language: go go: - 1.9.x - 1.10.x - 1.11.x - tip matrix: allow_failures: - go: tip install: - go get golang.org/x/lint/golint - export PATH=$GOPATH/bin:$PATH - go install ./... script: - verify/all.sh -v - go test ./... ================================================ FILE: vendor/github.com/spf13/pflag/LICENSE ================================================ Copyright (c) 2012 Alex Ogier. All rights reserved. Copyright (c) 2012 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/spf13/pflag/README.md ================================================ [![Build Status](https://travis-ci.org/spf13/pflag.svg?branch=master)](https://travis-ci.org/spf13/pflag) [![Go Report Card](https://goreportcard.com/badge/github.com/spf13/pflag)](https://goreportcard.com/report/github.com/spf13/pflag) [![GoDoc](https://godoc.org/github.com/spf13/pflag?status.svg)](https://godoc.org/github.com/spf13/pflag) ## Description pflag is a drop-in replacement for Go's flag package, implementing POSIX/GNU-style --flags. pflag is compatible with the [GNU extensions to the POSIX recommendations for command-line options][1]. For a more precise description, see the "Command-line flag syntax" section below. [1]: http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html pflag is available under the same style of BSD license as the Go language, which can be found in the LICENSE file. ## Installation pflag is available using the standard `go get` command. Install by running: go get github.com/spf13/pflag Run tests by running: go test github.com/spf13/pflag ## Usage pflag is a drop-in replacement of Go's native flag package. If you import pflag under the name "flag" then all code should continue to function with no changes. ``` go import flag "github.com/spf13/pflag" ``` There is one exception to this: if you directly instantiate the Flag struct there is one more field "Shorthand" that you will need to set. Most code never instantiates this struct directly, and instead uses functions such as String(), BoolVar(), and Var(), and is therefore unaffected. Define flags using flag.String(), Bool(), Int(), etc. This declares an integer flag, -flagname, stored in the pointer ip, with type *int. ``` go var ip *int = flag.Int("flagname", 1234, "help message for flagname") ``` If you like, you can bind the flag to a variable using the Var() functions. ``` go var flagvar int func init() { flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname") } ``` Or you can create custom flags that satisfy the Value interface (with pointer receivers) and couple them to flag parsing by ``` go flag.Var(&flagVal, "name", "help message for flagname") ``` For such flags, the default value is just the initial value of the variable. After all flags are defined, call ``` go flag.Parse() ``` to parse the command line into the defined flags. Flags may then be used directly. If you're using the flags themselves, they are all pointers; if you bind to variables, they're values. ``` go fmt.Println("ip has value ", *ip) fmt.Println("flagvar has value ", flagvar) ``` There are helper functions available to get the value stored in a Flag if you have a FlagSet but find it difficult to keep up with all of the pointers in your code. If you have a pflag.FlagSet with a flag called 'flagname' of type int you can use GetInt() to get the int value. But notice that 'flagname' must exist and it must be an int. GetString("flagname") will fail. ``` go i, err := flagset.GetInt("flagname") ``` After parsing, the arguments after the flag are available as the slice flag.Args() or individually as flag.Arg(i). The arguments are indexed from 0 through flag.NArg()-1. The pflag package also defines some new functions that are not in flag, that give one-letter shorthands for flags. You can use these by appending 'P' to the name of any function that defines a flag. ``` go var ip = flag.IntP("flagname", "f", 1234, "help message") var flagvar bool func init() { flag.BoolVarP(&flagvar, "boolname", "b", true, "help message") } flag.VarP(&flagVal, "varname", "v", "help message") ``` Shorthand letters can be used with single dashes on the command line. Boolean shorthand flags can be combined with other shorthand flags. The default set of command-line flags is controlled by top-level functions. The FlagSet type allows one to define independent sets of flags, such as to implement subcommands in a command-line interface. The methods of FlagSet are analogous to the top-level functions for the command-line flag set. ## Setting no option default values for flags After you create a flag it is possible to set the pflag.NoOptDefVal for the given flag. Doing this changes the meaning of the flag slightly. If a flag has a NoOptDefVal and the flag is set on the command line without an option the flag will be set to the NoOptDefVal. For example given: ``` go var ip = flag.IntP("flagname", "f", 1234, "help message") flag.Lookup("flagname").NoOptDefVal = "4321" ``` Would result in something like | Parsed Arguments | Resulting Value | | ------------- | ------------- | | --flagname=1357 | ip=1357 | | --flagname | ip=4321 | | [nothing] | ip=1234 | ## Command line flag syntax ``` --flag // boolean flags, or flags with no option default values --flag x // only on flags without a default value --flag=x ``` Unlike the flag package, a single dash before an option means something different than a double dash. Single dashes signify a series of shorthand letters for flags. All but the last shorthand letter must be boolean flags or a flag with a default value ``` // boolean or flags where the 'no option default value' is set -f -f=true -abc but -b true is INVALID // non-boolean and flags without a 'no option default value' -n 1234 -n=1234 -n1234 // mixed -abcs "hello" -absd="hello" -abcs1234 ``` Flag parsing stops after the terminator "--". Unlike the flag package, flags can be interspersed with arguments anywhere on the command line before this terminator. Integer flags accept 1234, 0664, 0x1234 and may be negative. Boolean flags (in their long form) accept 1, 0, t, f, true, false, TRUE, FALSE, True, False. Duration flags accept any input valid for time.ParseDuration. ## Mutating or "Normalizing" Flag names It is possible to set a custom flag name 'normalization function.' It allows flag names to be mutated both when created in the code and when used on the command line to some 'normalized' form. The 'normalized' form is used for comparison. Two examples of using the custom normalization func follow. **Example #1**: You want -, _, and . in flags to compare the same. aka --my-flag == --my_flag == --my.flag ``` go func wordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName { from := []string{"-", "_"} to := "." for _, sep := range from { name = strings.Replace(name, sep, to, -1) } return pflag.NormalizedName(name) } myFlagSet.SetNormalizeFunc(wordSepNormalizeFunc) ``` **Example #2**: You want to alias two flags. aka --old-flag-name == --new-flag-name ``` go func aliasNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName { switch name { case "old-flag-name": name = "new-flag-name" break } return pflag.NormalizedName(name) } myFlagSet.SetNormalizeFunc(aliasNormalizeFunc) ``` ## Deprecating a flag or its shorthand It is possible to deprecate a flag, or just its shorthand. Deprecating a flag/shorthand hides it from help text and prints a usage message when the deprecated flag/shorthand is used. **Example #1**: You want to deprecate a flag named "badflag" as well as inform the users what flag they should use instead. ```go // deprecate a flag by specifying its name and a usage message flags.MarkDeprecated("badflag", "please use --good-flag instead") ``` This hides "badflag" from help text, and prints `Flag --badflag has been deprecated, please use --good-flag instead` when "badflag" is used. **Example #2**: You want to keep a flag name "noshorthandflag" but deprecate its shortname "n". ```go // deprecate a flag shorthand by specifying its flag name and a usage message flags.MarkShorthandDeprecated("noshorthandflag", "please use --noshorthandflag only") ``` This hides the shortname "n" from help text, and prints `Flag shorthand -n has been deprecated, please use --noshorthandflag only` when the shorthand "n" is used. Note that usage message is essential here, and it should not be empty. ## Hidden flags It is possible to mark a flag as hidden, meaning it will still function as normal, however will not show up in usage/help text. **Example**: You have a flag named "secretFlag" that you need for internal use only and don't want it showing up in help text, or for its usage text to be available. ```go // hide a flag by specifying its name flags.MarkHidden("secretFlag") ``` ## Disable sorting of flags `pflag` allows you to disable sorting of flags for help and usage message. **Example**: ```go flags.BoolP("verbose", "v", false, "verbose output") flags.String("coolflag", "yeaah", "it's really cool flag") flags.Int("usefulflag", 777, "sometimes it's very useful") flags.SortFlags = false flags.PrintDefaults() ``` **Output**: ``` -v, --verbose verbose output --coolflag string it's really cool flag (default "yeaah") --usefulflag int sometimes it's very useful (default 777) ``` ## Supporting Go flags when using pflag In order to support flags defined using Go's `flag` package, they must be added to the `pflag` flagset. This is usually necessary to support flags defined by third-party dependencies (e.g. `golang/glog`). **Example**: You want to add the Go flags to the `CommandLine` flagset ```go import ( goflag "flag" flag "github.com/spf13/pflag" ) var ip *int = flag.Int("flagname", 1234, "help message for flagname") func main() { flag.CommandLine.AddGoFlagSet(goflag.CommandLine) flag.Parse() } ``` ## More info You can see the full reference documentation of the pflag package [at godoc.org][3], or through go's standard documentation system by running `godoc -http=:6060` and browsing to [http://localhost:6060/pkg/github.com/spf13/pflag][2] after installation. [2]: http://localhost:6060/pkg/github.com/spf13/pflag [3]: http://godoc.org/github.com/spf13/pflag ================================================ FILE: vendor/github.com/spf13/pflag/bool.go ================================================ package pflag import "strconv" // optional interface to indicate boolean flags that can be // supplied without "=value" text type boolFlag interface { Value IsBoolFlag() bool } // -- bool Value type boolValue bool func newBoolValue(val bool, p *bool) *boolValue { *p = val return (*boolValue)(p) } func (b *boolValue) Set(s string) error { v, err := strconv.ParseBool(s) *b = boolValue(v) return err } func (b *boolValue) Type() string { return "bool" } func (b *boolValue) String() string { return strconv.FormatBool(bool(*b)) } func (b *boolValue) IsBoolFlag() bool { return true } func boolConv(sval string) (interface{}, error) { return strconv.ParseBool(sval) } // GetBool return the bool value of a flag with the given name func (f *FlagSet) GetBool(name string) (bool, error) { val, err := f.getFlagType(name, "bool", boolConv) if err != nil { return false, err } return val.(bool), nil } // BoolVar defines a bool flag with specified name, default value, and usage string. // The argument p points to a bool variable in which to store the value of the flag. func (f *FlagSet) BoolVar(p *bool, name string, value bool, usage string) { f.BoolVarP(p, name, "", value, usage) } // BoolVarP is like BoolVar, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) BoolVarP(p *bool, name, shorthand string, value bool, usage string) { flag := f.VarPF(newBoolValue(value, p), name, shorthand, usage) flag.NoOptDefVal = "true" } // BoolVar defines a bool flag with specified name, default value, and usage string. // The argument p points to a bool variable in which to store the value of the flag. func BoolVar(p *bool, name string, value bool, usage string) { BoolVarP(p, name, "", value, usage) } // BoolVarP is like BoolVar, but accepts a shorthand letter that can be used after a single dash. func BoolVarP(p *bool, name, shorthand string, value bool, usage string) { flag := CommandLine.VarPF(newBoolValue(value, p), name, shorthand, usage) flag.NoOptDefVal = "true" } // Bool defines a bool flag with specified name, default value, and usage string. // The return value is the address of a bool variable that stores the value of the flag. func (f *FlagSet) Bool(name string, value bool, usage string) *bool { return f.BoolP(name, "", value, usage) } // BoolP is like Bool, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) BoolP(name, shorthand string, value bool, usage string) *bool { p := new(bool) f.BoolVarP(p, name, shorthand, value, usage) return p } // Bool defines a bool flag with specified name, default value, and usage string. // The return value is the address of a bool variable that stores the value of the flag. func Bool(name string, value bool, usage string) *bool { return BoolP(name, "", value, usage) } // BoolP is like Bool, but accepts a shorthand letter that can be used after a single dash. func BoolP(name, shorthand string, value bool, usage string) *bool { b := CommandLine.BoolP(name, shorthand, value, usage) return b } ================================================ FILE: vendor/github.com/spf13/pflag/bool_slice.go ================================================ package pflag import ( "io" "strconv" "strings" ) // -- boolSlice Value type boolSliceValue struct { value *[]bool changed bool } func newBoolSliceValue(val []bool, p *[]bool) *boolSliceValue { bsv := new(boolSliceValue) bsv.value = p *bsv.value = val return bsv } // Set converts, and assigns, the comma-separated boolean argument string representation as the []bool value of this flag. // If Set is called on a flag that already has a []bool assigned, the newly converted values will be appended. func (s *boolSliceValue) Set(val string) error { // remove all quote characters rmQuote := strings.NewReplacer(`"`, "", `'`, "", "`", "") // read flag arguments with CSV parser boolStrSlice, err := readAsCSV(rmQuote.Replace(val)) if err != nil && err != io.EOF { return err } // parse boolean values into slice out := make([]bool, 0, len(boolStrSlice)) for _, boolStr := range boolStrSlice { b, err := strconv.ParseBool(strings.TrimSpace(boolStr)) if err != nil { return err } out = append(out, b) } if !s.changed { *s.value = out } else { *s.value = append(*s.value, out...) } s.changed = true return nil } // Type returns a string that uniquely represents this flag's type. func (s *boolSliceValue) Type() string { return "boolSlice" } // String defines a "native" format for this boolean slice flag value. func (s *boolSliceValue) String() string { boolStrSlice := make([]string, len(*s.value)) for i, b := range *s.value { boolStrSlice[i] = strconv.FormatBool(b) } out, _ := writeAsCSV(boolStrSlice) return "[" + out + "]" } func (s *boolSliceValue) fromString(val string) (bool, error) { return strconv.ParseBool(val) } func (s *boolSliceValue) toString(val bool) string { return strconv.FormatBool(val) } func (s *boolSliceValue) Append(val string) error { i, err := s.fromString(val) if err != nil { return err } *s.value = append(*s.value, i) return nil } func (s *boolSliceValue) Replace(val []string) error { out := make([]bool, len(val)) for i, d := range val { var err error out[i], err = s.fromString(d) if err != nil { return err } } *s.value = out return nil } func (s *boolSliceValue) GetSlice() []string { out := make([]string, len(*s.value)) for i, d := range *s.value { out[i] = s.toString(d) } return out } func boolSliceConv(val string) (interface{}, error) { val = strings.Trim(val, "[]") // Empty string would cause a slice with one (empty) entry if len(val) == 0 { return []bool{}, nil } ss := strings.Split(val, ",") out := make([]bool, len(ss)) for i, t := range ss { var err error out[i], err = strconv.ParseBool(t) if err != nil { return nil, err } } return out, nil } // GetBoolSlice returns the []bool value of a flag with the given name. func (f *FlagSet) GetBoolSlice(name string) ([]bool, error) { val, err := f.getFlagType(name, "boolSlice", boolSliceConv) if err != nil { return []bool{}, err } return val.([]bool), nil } // BoolSliceVar defines a boolSlice flag with specified name, default value, and usage string. // The argument p points to a []bool variable in which to store the value of the flag. func (f *FlagSet) BoolSliceVar(p *[]bool, name string, value []bool, usage string) { f.VarP(newBoolSliceValue(value, p), name, "", usage) } // BoolSliceVarP is like BoolSliceVar, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) BoolSliceVarP(p *[]bool, name, shorthand string, value []bool, usage string) { f.VarP(newBoolSliceValue(value, p), name, shorthand, usage) } // BoolSliceVar defines a []bool flag with specified name, default value, and usage string. // The argument p points to a []bool variable in which to store the value of the flag. func BoolSliceVar(p *[]bool, name string, value []bool, usage string) { CommandLine.VarP(newBoolSliceValue(value, p), name, "", usage) } // BoolSliceVarP is like BoolSliceVar, but accepts a shorthand letter that can be used after a single dash. func BoolSliceVarP(p *[]bool, name, shorthand string, value []bool, usage string) { CommandLine.VarP(newBoolSliceValue(value, p), name, shorthand, usage) } // BoolSlice defines a []bool flag with specified name, default value, and usage string. // The return value is the address of a []bool variable that stores the value of the flag. func (f *FlagSet) BoolSlice(name string, value []bool, usage string) *[]bool { p := []bool{} f.BoolSliceVarP(&p, name, "", value, usage) return &p } // BoolSliceP is like BoolSlice, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) BoolSliceP(name, shorthand string, value []bool, usage string) *[]bool { p := []bool{} f.BoolSliceVarP(&p, name, shorthand, value, usage) return &p } // BoolSlice defines a []bool flag with specified name, default value, and usage string. // The return value is the address of a []bool variable that stores the value of the flag. func BoolSlice(name string, value []bool, usage string) *[]bool { return CommandLine.BoolSliceP(name, "", value, usage) } // BoolSliceP is like BoolSlice, but accepts a shorthand letter that can be used after a single dash. func BoolSliceP(name, shorthand string, value []bool, usage string) *[]bool { return CommandLine.BoolSliceP(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/bytes.go ================================================ package pflag import ( "encoding/base64" "encoding/hex" "fmt" "strings" ) // BytesHex adapts []byte for use as a flag. Value of flag is HEX encoded type bytesHexValue []byte // String implements pflag.Value.String. func (bytesHex bytesHexValue) String() string { return fmt.Sprintf("%X", []byte(bytesHex)) } // Set implements pflag.Value.Set. func (bytesHex *bytesHexValue) Set(value string) error { bin, err := hex.DecodeString(strings.TrimSpace(value)) if err != nil { return err } *bytesHex = bin return nil } // Type implements pflag.Value.Type. func (*bytesHexValue) Type() string { return "bytesHex" } func newBytesHexValue(val []byte, p *[]byte) *bytesHexValue { *p = val return (*bytesHexValue)(p) } func bytesHexConv(sval string) (interface{}, error) { bin, err := hex.DecodeString(sval) if err == nil { return bin, nil } return nil, fmt.Errorf("invalid string being converted to Bytes: %s %s", sval, err) } // GetBytesHex return the []byte value of a flag with the given name func (f *FlagSet) GetBytesHex(name string) ([]byte, error) { val, err := f.getFlagType(name, "bytesHex", bytesHexConv) if err != nil { return []byte{}, err } return val.([]byte), nil } // BytesHexVar defines an []byte flag with specified name, default value, and usage string. // The argument p points to an []byte variable in which to store the value of the flag. func (f *FlagSet) BytesHexVar(p *[]byte, name string, value []byte, usage string) { f.VarP(newBytesHexValue(value, p), name, "", usage) } // BytesHexVarP is like BytesHexVar, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) BytesHexVarP(p *[]byte, name, shorthand string, value []byte, usage string) { f.VarP(newBytesHexValue(value, p), name, shorthand, usage) } // BytesHexVar defines an []byte flag with specified name, default value, and usage string. // The argument p points to an []byte variable in which to store the value of the flag. func BytesHexVar(p *[]byte, name string, value []byte, usage string) { CommandLine.VarP(newBytesHexValue(value, p), name, "", usage) } // BytesHexVarP is like BytesHexVar, but accepts a shorthand letter that can be used after a single dash. func BytesHexVarP(p *[]byte, name, shorthand string, value []byte, usage string) { CommandLine.VarP(newBytesHexValue(value, p), name, shorthand, usage) } // BytesHex defines an []byte flag with specified name, default value, and usage string. // The return value is the address of an []byte variable that stores the value of the flag. func (f *FlagSet) BytesHex(name string, value []byte, usage string) *[]byte { p := new([]byte) f.BytesHexVarP(p, name, "", value, usage) return p } // BytesHexP is like BytesHex, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) BytesHexP(name, shorthand string, value []byte, usage string) *[]byte { p := new([]byte) f.BytesHexVarP(p, name, shorthand, value, usage) return p } // BytesHex defines an []byte flag with specified name, default value, and usage string. // The return value is the address of an []byte variable that stores the value of the flag. func BytesHex(name string, value []byte, usage string) *[]byte { return CommandLine.BytesHexP(name, "", value, usage) } // BytesHexP is like BytesHex, but accepts a shorthand letter that can be used after a single dash. func BytesHexP(name, shorthand string, value []byte, usage string) *[]byte { return CommandLine.BytesHexP(name, shorthand, value, usage) } // BytesBase64 adapts []byte for use as a flag. Value of flag is Base64 encoded type bytesBase64Value []byte // String implements pflag.Value.String. func (bytesBase64 bytesBase64Value) String() string { return base64.StdEncoding.EncodeToString([]byte(bytesBase64)) } // Set implements pflag.Value.Set. func (bytesBase64 *bytesBase64Value) Set(value string) error { bin, err := base64.StdEncoding.DecodeString(strings.TrimSpace(value)) if err != nil { return err } *bytesBase64 = bin return nil } // Type implements pflag.Value.Type. func (*bytesBase64Value) Type() string { return "bytesBase64" } func newBytesBase64Value(val []byte, p *[]byte) *bytesBase64Value { *p = val return (*bytesBase64Value)(p) } func bytesBase64ValueConv(sval string) (interface{}, error) { bin, err := base64.StdEncoding.DecodeString(sval) if err == nil { return bin, nil } return nil, fmt.Errorf("invalid string being converted to Bytes: %s %s", sval, err) } // GetBytesBase64 return the []byte value of a flag with the given name func (f *FlagSet) GetBytesBase64(name string) ([]byte, error) { val, err := f.getFlagType(name, "bytesBase64", bytesBase64ValueConv) if err != nil { return []byte{}, err } return val.([]byte), nil } // BytesBase64Var defines an []byte flag with specified name, default value, and usage string. // The argument p points to an []byte variable in which to store the value of the flag. func (f *FlagSet) BytesBase64Var(p *[]byte, name string, value []byte, usage string) { f.VarP(newBytesBase64Value(value, p), name, "", usage) } // BytesBase64VarP is like BytesBase64Var, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) BytesBase64VarP(p *[]byte, name, shorthand string, value []byte, usage string) { f.VarP(newBytesBase64Value(value, p), name, shorthand, usage) } // BytesBase64Var defines an []byte flag with specified name, default value, and usage string. // The argument p points to an []byte variable in which to store the value of the flag. func BytesBase64Var(p *[]byte, name string, value []byte, usage string) { CommandLine.VarP(newBytesBase64Value(value, p), name, "", usage) } // BytesBase64VarP is like BytesBase64Var, but accepts a shorthand letter that can be used after a single dash. func BytesBase64VarP(p *[]byte, name, shorthand string, value []byte, usage string) { CommandLine.VarP(newBytesBase64Value(value, p), name, shorthand, usage) } // BytesBase64 defines an []byte flag with specified name, default value, and usage string. // The return value is the address of an []byte variable that stores the value of the flag. func (f *FlagSet) BytesBase64(name string, value []byte, usage string) *[]byte { p := new([]byte) f.BytesBase64VarP(p, name, "", value, usage) return p } // BytesBase64P is like BytesBase64, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) BytesBase64P(name, shorthand string, value []byte, usage string) *[]byte { p := new([]byte) f.BytesBase64VarP(p, name, shorthand, value, usage) return p } // BytesBase64 defines an []byte flag with specified name, default value, and usage string. // The return value is the address of an []byte variable that stores the value of the flag. func BytesBase64(name string, value []byte, usage string) *[]byte { return CommandLine.BytesBase64P(name, "", value, usage) } // BytesBase64P is like BytesBase64, but accepts a shorthand letter that can be used after a single dash. func BytesBase64P(name, shorthand string, value []byte, usage string) *[]byte { return CommandLine.BytesBase64P(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/count.go ================================================ package pflag import "strconv" // -- count Value type countValue int func newCountValue(val int, p *int) *countValue { *p = val return (*countValue)(p) } func (i *countValue) Set(s string) error { // "+1" means that no specific value was passed, so increment if s == "+1" { *i = countValue(*i + 1) return nil } v, err := strconv.ParseInt(s, 0, 0) *i = countValue(v) return err } func (i *countValue) Type() string { return "count" } func (i *countValue) String() string { return strconv.Itoa(int(*i)) } func countConv(sval string) (interface{}, error) { i, err := strconv.Atoi(sval) if err != nil { return nil, err } return i, nil } // GetCount return the int value of a flag with the given name func (f *FlagSet) GetCount(name string) (int, error) { val, err := f.getFlagType(name, "count", countConv) if err != nil { return 0, err } return val.(int), nil } // CountVar defines a count flag with specified name, default value, and usage string. // The argument p points to an int variable in which to store the value of the flag. // A count flag will add 1 to its value every time it is found on the command line func (f *FlagSet) CountVar(p *int, name string, usage string) { f.CountVarP(p, name, "", usage) } // CountVarP is like CountVar only take a shorthand for the flag name. func (f *FlagSet) CountVarP(p *int, name, shorthand string, usage string) { flag := f.VarPF(newCountValue(0, p), name, shorthand, usage) flag.NoOptDefVal = "+1" } // CountVar like CountVar only the flag is placed on the CommandLine instead of a given flag set func CountVar(p *int, name string, usage string) { CommandLine.CountVar(p, name, usage) } // CountVarP is like CountVar only take a shorthand for the flag name. func CountVarP(p *int, name, shorthand string, usage string) { CommandLine.CountVarP(p, name, shorthand, usage) } // Count defines a count flag with specified name, default value, and usage string. // The return value is the address of an int variable that stores the value of the flag. // A count flag will add 1 to its value every time it is found on the command line func (f *FlagSet) Count(name string, usage string) *int { p := new(int) f.CountVarP(p, name, "", usage) return p } // CountP is like Count only takes a shorthand for the flag name. func (f *FlagSet) CountP(name, shorthand string, usage string) *int { p := new(int) f.CountVarP(p, name, shorthand, usage) return p } // Count defines a count flag with specified name, default value, and usage string. // The return value is the address of an int variable that stores the value of the flag. // A count flag will add 1 to its value evey time it is found on the command line func Count(name string, usage string) *int { return CommandLine.CountP(name, "", usage) } // CountP is like Count only takes a shorthand for the flag name. func CountP(name, shorthand string, usage string) *int { return CommandLine.CountP(name, shorthand, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/duration.go ================================================ package pflag import ( "time" ) // -- time.Duration Value type durationValue time.Duration func newDurationValue(val time.Duration, p *time.Duration) *durationValue { *p = val return (*durationValue)(p) } func (d *durationValue) Set(s string) error { v, err := time.ParseDuration(s) *d = durationValue(v) return err } func (d *durationValue) Type() string { return "duration" } func (d *durationValue) String() string { return (*time.Duration)(d).String() } func durationConv(sval string) (interface{}, error) { return time.ParseDuration(sval) } // GetDuration return the duration value of a flag with the given name func (f *FlagSet) GetDuration(name string) (time.Duration, error) { val, err := f.getFlagType(name, "duration", durationConv) if err != nil { return 0, err } return val.(time.Duration), nil } // DurationVar defines a time.Duration flag with specified name, default value, and usage string. // The argument p points to a time.Duration variable in which to store the value of the flag. func (f *FlagSet) DurationVar(p *time.Duration, name string, value time.Duration, usage string) { f.VarP(newDurationValue(value, p), name, "", usage) } // DurationVarP is like DurationVar, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) DurationVarP(p *time.Duration, name, shorthand string, value time.Duration, usage string) { f.VarP(newDurationValue(value, p), name, shorthand, usage) } // DurationVar defines a time.Duration flag with specified name, default value, and usage string. // The argument p points to a time.Duration variable in which to store the value of the flag. func DurationVar(p *time.Duration, name string, value time.Duration, usage string) { CommandLine.VarP(newDurationValue(value, p), name, "", usage) } // DurationVarP is like DurationVar, but accepts a shorthand letter that can be used after a single dash. func DurationVarP(p *time.Duration, name, shorthand string, value time.Duration, usage string) { CommandLine.VarP(newDurationValue(value, p), name, shorthand, usage) } // Duration defines a time.Duration flag with specified name, default value, and usage string. // The return value is the address of a time.Duration variable that stores the value of the flag. func (f *FlagSet) Duration(name string, value time.Duration, usage string) *time.Duration { p := new(time.Duration) f.DurationVarP(p, name, "", value, usage) return p } // DurationP is like Duration, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) DurationP(name, shorthand string, value time.Duration, usage string) *time.Duration { p := new(time.Duration) f.DurationVarP(p, name, shorthand, value, usage) return p } // Duration defines a time.Duration flag with specified name, default value, and usage string. // The return value is the address of a time.Duration variable that stores the value of the flag. func Duration(name string, value time.Duration, usage string) *time.Duration { return CommandLine.DurationP(name, "", value, usage) } // DurationP is like Duration, but accepts a shorthand letter that can be used after a single dash. func DurationP(name, shorthand string, value time.Duration, usage string) *time.Duration { return CommandLine.DurationP(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/duration_slice.go ================================================ package pflag import ( "fmt" "strings" "time" ) // -- durationSlice Value type durationSliceValue struct { value *[]time.Duration changed bool } func newDurationSliceValue(val []time.Duration, p *[]time.Duration) *durationSliceValue { dsv := new(durationSliceValue) dsv.value = p *dsv.value = val return dsv } func (s *durationSliceValue) Set(val string) error { ss := strings.Split(val, ",") out := make([]time.Duration, len(ss)) for i, d := range ss { var err error out[i], err = time.ParseDuration(d) if err != nil { return err } } if !s.changed { *s.value = out } else { *s.value = append(*s.value, out...) } s.changed = true return nil } func (s *durationSliceValue) Type() string { return "durationSlice" } func (s *durationSliceValue) String() string { out := make([]string, len(*s.value)) for i, d := range *s.value { out[i] = fmt.Sprintf("%s", d) } return "[" + strings.Join(out, ",") + "]" } func (s *durationSliceValue) fromString(val string) (time.Duration, error) { return time.ParseDuration(val) } func (s *durationSliceValue) toString(val time.Duration) string { return fmt.Sprintf("%s", val) } func (s *durationSliceValue) Append(val string) error { i, err := s.fromString(val) if err != nil { return err } *s.value = append(*s.value, i) return nil } func (s *durationSliceValue) Replace(val []string) error { out := make([]time.Duration, len(val)) for i, d := range val { var err error out[i], err = s.fromString(d) if err != nil { return err } } *s.value = out return nil } func (s *durationSliceValue) GetSlice() []string { out := make([]string, len(*s.value)) for i, d := range *s.value { out[i] = s.toString(d) } return out } func durationSliceConv(val string) (interface{}, error) { val = strings.Trim(val, "[]") // Empty string would cause a slice with one (empty) entry if len(val) == 0 { return []time.Duration{}, nil } ss := strings.Split(val, ",") out := make([]time.Duration, len(ss)) for i, d := range ss { var err error out[i], err = time.ParseDuration(d) if err != nil { return nil, err } } return out, nil } // GetDurationSlice returns the []time.Duration value of a flag with the given name func (f *FlagSet) GetDurationSlice(name string) ([]time.Duration, error) { val, err := f.getFlagType(name, "durationSlice", durationSliceConv) if err != nil { return []time.Duration{}, err } return val.([]time.Duration), nil } // DurationSliceVar defines a durationSlice flag with specified name, default value, and usage string. // The argument p points to a []time.Duration variable in which to store the value of the flag. func (f *FlagSet) DurationSliceVar(p *[]time.Duration, name string, value []time.Duration, usage string) { f.VarP(newDurationSliceValue(value, p), name, "", usage) } // DurationSliceVarP is like DurationSliceVar, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) DurationSliceVarP(p *[]time.Duration, name, shorthand string, value []time.Duration, usage string) { f.VarP(newDurationSliceValue(value, p), name, shorthand, usage) } // DurationSliceVar defines a duration[] flag with specified name, default value, and usage string. // The argument p points to a duration[] variable in which to store the value of the flag. func DurationSliceVar(p *[]time.Duration, name string, value []time.Duration, usage string) { CommandLine.VarP(newDurationSliceValue(value, p), name, "", usage) } // DurationSliceVarP is like DurationSliceVar, but accepts a shorthand letter that can be used after a single dash. func DurationSliceVarP(p *[]time.Duration, name, shorthand string, value []time.Duration, usage string) { CommandLine.VarP(newDurationSliceValue(value, p), name, shorthand, usage) } // DurationSlice defines a []time.Duration flag with specified name, default value, and usage string. // The return value is the address of a []time.Duration variable that stores the value of the flag. func (f *FlagSet) DurationSlice(name string, value []time.Duration, usage string) *[]time.Duration { p := []time.Duration{} f.DurationSliceVarP(&p, name, "", value, usage) return &p } // DurationSliceP is like DurationSlice, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) DurationSliceP(name, shorthand string, value []time.Duration, usage string) *[]time.Duration { p := []time.Duration{} f.DurationSliceVarP(&p, name, shorthand, value, usage) return &p } // DurationSlice defines a []time.Duration flag with specified name, default value, and usage string. // The return value is the address of a []time.Duration variable that stores the value of the flag. func DurationSlice(name string, value []time.Duration, usage string) *[]time.Duration { return CommandLine.DurationSliceP(name, "", value, usage) } // DurationSliceP is like DurationSlice, but accepts a shorthand letter that can be used after a single dash. func DurationSliceP(name, shorthand string, value []time.Duration, usage string) *[]time.Duration { return CommandLine.DurationSliceP(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/flag.go ================================================ // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. /* Package pflag is a drop-in replacement for Go's flag package, implementing POSIX/GNU-style --flags. pflag is compatible with the GNU extensions to the POSIX recommendations for command-line options. See http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html Usage: pflag is a drop-in replacement of Go's native flag package. If you import pflag under the name "flag" then all code should continue to function with no changes. import flag "github.com/spf13/pflag" There is one exception to this: if you directly instantiate the Flag struct there is one more field "Shorthand" that you will need to set. Most code never instantiates this struct directly, and instead uses functions such as String(), BoolVar(), and Var(), and is therefore unaffected. Define flags using flag.String(), Bool(), Int(), etc. This declares an integer flag, -flagname, stored in the pointer ip, with type *int. var ip = flag.Int("flagname", 1234, "help message for flagname") If you like, you can bind the flag to a variable using the Var() functions. var flagvar int func init() { flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname") } Or you can create custom flags that satisfy the Value interface (with pointer receivers) and couple them to flag parsing by flag.Var(&flagVal, "name", "help message for flagname") For such flags, the default value is just the initial value of the variable. After all flags are defined, call flag.Parse() to parse the command line into the defined flags. Flags may then be used directly. If you're using the flags themselves, they are all pointers; if you bind to variables, they're values. fmt.Println("ip has value ", *ip) fmt.Println("flagvar has value ", flagvar) After parsing, the arguments after the flag are available as the slice flag.Args() or individually as flag.Arg(i). The arguments are indexed from 0 through flag.NArg()-1. The pflag package also defines some new functions that are not in flag, that give one-letter shorthands for flags. You can use these by appending 'P' to the name of any function that defines a flag. var ip = flag.IntP("flagname", "f", 1234, "help message") var flagvar bool func init() { flag.BoolVarP(&flagvar, "boolname", "b", true, "help message") } flag.VarP(&flagval, "varname", "v", "help message") Shorthand letters can be used with single dashes on the command line. Boolean shorthand flags can be combined with other shorthand flags. Command line flag syntax: --flag // boolean flags only --flag=x Unlike the flag package, a single dash before an option means something different than a double dash. Single dashes signify a series of shorthand letters for flags. All but the last shorthand letter must be boolean flags. // boolean flags -f -abc // non-boolean flags -n 1234 -Ifile // mixed -abcs "hello" -abcn1234 Flag parsing stops after the terminator "--". Unlike the flag package, flags can be interspersed with arguments anywhere on the command line before this terminator. Integer flags accept 1234, 0664, 0x1234 and may be negative. Boolean flags (in their long form) accept 1, 0, t, f, true, false, TRUE, FALSE, True, False. Duration flags accept any input valid for time.ParseDuration. The default set of command-line flags is controlled by top-level functions. The FlagSet type allows one to define independent sets of flags, such as to implement subcommands in a command-line interface. The methods of FlagSet are analogous to the top-level functions for the command-line flag set. */ package pflag import ( "bytes" "errors" goflag "flag" "fmt" "io" "os" "sort" "strings" ) // ErrHelp is the error returned if the flag -help is invoked but no such flag is defined. var ErrHelp = errors.New("pflag: help requested") // ErrorHandling defines how to handle flag parsing errors. type ErrorHandling int const ( // ContinueOnError will return an err from Parse() if an error is found ContinueOnError ErrorHandling = iota // ExitOnError will call os.Exit(2) if an error is found when parsing ExitOnError // PanicOnError will panic() if an error is found when parsing flags PanicOnError ) // ParseErrorsWhitelist defines the parsing errors that can be ignored type ParseErrorsWhitelist struct { // UnknownFlags will ignore unknown flags errors and continue parsing rest of the flags UnknownFlags bool } // NormalizedName is a flag name that has been normalized according to rules // for the FlagSet (e.g. making '-' and '_' equivalent). type NormalizedName string // A FlagSet represents a set of defined flags. type FlagSet struct { // Usage is the function called when an error occurs while parsing flags. // The field is a function (not a method) that may be changed to point to // a custom error handler. Usage func() // SortFlags is used to indicate, if user wants to have sorted flags in // help/usage messages. SortFlags bool // ParseErrorsWhitelist is used to configure a whitelist of errors ParseErrorsWhitelist ParseErrorsWhitelist name string parsed bool actual map[NormalizedName]*Flag orderedActual []*Flag sortedActual []*Flag formal map[NormalizedName]*Flag orderedFormal []*Flag sortedFormal []*Flag shorthands map[byte]*Flag args []string // arguments after flags argsLenAtDash int // len(args) when a '--' was located when parsing, or -1 if no -- errorHandling ErrorHandling output io.Writer // nil means stderr; use out() accessor interspersed bool // allow interspersed option/non-option args normalizeNameFunc func(f *FlagSet, name string) NormalizedName addedGoFlagSets []*goflag.FlagSet } // A Flag represents the state of a flag. type Flag struct { Name string // name as it appears on command line Shorthand string // one-letter abbreviated flag Usage string // help message Value Value // value as set DefValue string // default value (as text); for usage message Changed bool // If the user set the value (or if left to default) NoOptDefVal string // default value (as text); if the flag is on the command line without any options Deprecated string // If this flag is deprecated, this string is the new or now thing to use Hidden bool // used by cobra.Command to allow flags to be hidden from help/usage text ShorthandDeprecated string // If the shorthand of this flag is deprecated, this string is the new or now thing to use Annotations map[string][]string // used by cobra.Command bash autocomple code } // Value is the interface to the dynamic value stored in a flag. // (The default value is represented as a string.) type Value interface { String() string Set(string) error Type() string } // SliceValue is a secondary interface to all flags which hold a list // of values. This allows full control over the value of list flags, // and avoids complicated marshalling and unmarshalling to csv. type SliceValue interface { // Append adds the specified value to the end of the flag value list. Append(string) error // Replace will fully overwrite any data currently in the flag value list. Replace([]string) error // GetSlice returns the flag value list as an array of strings. GetSlice() []string } // sortFlags returns the flags as a slice in lexicographical sorted order. func sortFlags(flags map[NormalizedName]*Flag) []*Flag { list := make(sort.StringSlice, len(flags)) i := 0 for k := range flags { list[i] = string(k) i++ } list.Sort() result := make([]*Flag, len(list)) for i, name := range list { result[i] = flags[NormalizedName(name)] } return result } // SetNormalizeFunc allows you to add a function which can translate flag names. // Flags added to the FlagSet will be translated and then when anything tries to // look up the flag that will also be translated. So it would be possible to create // a flag named "getURL" and have it translated to "geturl". A user could then pass // "--getUrl" which may also be translated to "geturl" and everything will work. func (f *FlagSet) SetNormalizeFunc(n func(f *FlagSet, name string) NormalizedName) { f.normalizeNameFunc = n f.sortedFormal = f.sortedFormal[:0] for fname, flag := range f.formal { nname := f.normalizeFlagName(flag.Name) if fname == nname { continue } flag.Name = string(nname) delete(f.formal, fname) f.formal[nname] = flag if _, set := f.actual[fname]; set { delete(f.actual, fname) f.actual[nname] = flag } } } // GetNormalizeFunc returns the previously set NormalizeFunc of a function which // does no translation, if not set previously. func (f *FlagSet) GetNormalizeFunc() func(f *FlagSet, name string) NormalizedName { if f.normalizeNameFunc != nil { return f.normalizeNameFunc } return func(f *FlagSet, name string) NormalizedName { return NormalizedName(name) } } func (f *FlagSet) normalizeFlagName(name string) NormalizedName { n := f.GetNormalizeFunc() return n(f, name) } func (f *FlagSet) out() io.Writer { if f.output == nil { return os.Stderr } return f.output } // SetOutput sets the destination for usage and error messages. // If output is nil, os.Stderr is used. func (f *FlagSet) SetOutput(output io.Writer) { f.output = output } // VisitAll visits the flags in lexicographical order or // in primordial order if f.SortFlags is false, calling fn for each. // It visits all flags, even those not set. func (f *FlagSet) VisitAll(fn func(*Flag)) { if len(f.formal) == 0 { return } var flags []*Flag if f.SortFlags { if len(f.formal) != len(f.sortedFormal) { f.sortedFormal = sortFlags(f.formal) } flags = f.sortedFormal } else { flags = f.orderedFormal } for _, flag := range flags { fn(flag) } } // HasFlags returns a bool to indicate if the FlagSet has any flags defined. func (f *FlagSet) HasFlags() bool { return len(f.formal) > 0 } // HasAvailableFlags returns a bool to indicate if the FlagSet has any flags // that are not hidden. func (f *FlagSet) HasAvailableFlags() bool { for _, flag := range f.formal { if !flag.Hidden { return true } } return false } // VisitAll visits the command-line flags in lexicographical order or // in primordial order if f.SortFlags is false, calling fn for each. // It visits all flags, even those not set. func VisitAll(fn func(*Flag)) { CommandLine.VisitAll(fn) } // Visit visits the flags in lexicographical order or // in primordial order if f.SortFlags is false, calling fn for each. // It visits only those flags that have been set. func (f *FlagSet) Visit(fn func(*Flag)) { if len(f.actual) == 0 { return } var flags []*Flag if f.SortFlags { if len(f.actual) != len(f.sortedActual) { f.sortedActual = sortFlags(f.actual) } flags = f.sortedActual } else { flags = f.orderedActual } for _, flag := range flags { fn(flag) } } // Visit visits the command-line flags in lexicographical order or // in primordial order if f.SortFlags is false, calling fn for each. // It visits only those flags that have been set. func Visit(fn func(*Flag)) { CommandLine.Visit(fn) } // Lookup returns the Flag structure of the named flag, returning nil if none exists. func (f *FlagSet) Lookup(name string) *Flag { return f.lookup(f.normalizeFlagName(name)) } // ShorthandLookup returns the Flag structure of the short handed flag, // returning nil if none exists. // It panics, if len(name) > 1. func (f *FlagSet) ShorthandLookup(name string) *Flag { if name == "" { return nil } if len(name) > 1 { msg := fmt.Sprintf("can not look up shorthand which is more than one ASCII character: %q", name) fmt.Fprintf(f.out(), msg) panic(msg) } c := name[0] return f.shorthands[c] } // lookup returns the Flag structure of the named flag, returning nil if none exists. func (f *FlagSet) lookup(name NormalizedName) *Flag { return f.formal[name] } // func to return a given type for a given flag name func (f *FlagSet) getFlagType(name string, ftype string, convFunc func(sval string) (interface{}, error)) (interface{}, error) { flag := f.Lookup(name) if flag == nil { err := fmt.Errorf("flag accessed but not defined: %s", name) return nil, err } if flag.Value.Type() != ftype { err := fmt.Errorf("trying to get %s value of flag of type %s", ftype, flag.Value.Type()) return nil, err } sval := flag.Value.String() result, err := convFunc(sval) if err != nil { return nil, err } return result, nil } // ArgsLenAtDash will return the length of f.Args at the moment when a -- was // found during arg parsing. This allows your program to know which args were // before the -- and which came after. func (f *FlagSet) ArgsLenAtDash() int { return f.argsLenAtDash } // MarkDeprecated indicated that a flag is deprecated in your program. It will // continue to function but will not show up in help or usage messages. Using // this flag will also print the given usageMessage. func (f *FlagSet) MarkDeprecated(name string, usageMessage string) error { flag := f.Lookup(name) if flag == nil { return fmt.Errorf("flag %q does not exist", name) } if usageMessage == "" { return fmt.Errorf("deprecated message for flag %q must be set", name) } flag.Deprecated = usageMessage flag.Hidden = true return nil } // MarkShorthandDeprecated will mark the shorthand of a flag deprecated in your // program. It will continue to function but will not show up in help or usage // messages. Using this flag will also print the given usageMessage. func (f *FlagSet) MarkShorthandDeprecated(name string, usageMessage string) error { flag := f.Lookup(name) if flag == nil { return fmt.Errorf("flag %q does not exist", name) } if usageMessage == "" { return fmt.Errorf("deprecated message for flag %q must be set", name) } flag.ShorthandDeprecated = usageMessage return nil } // MarkHidden sets a flag to 'hidden' in your program. It will continue to // function but will not show up in help or usage messages. func (f *FlagSet) MarkHidden(name string) error { flag := f.Lookup(name) if flag == nil { return fmt.Errorf("flag %q does not exist", name) } flag.Hidden = true return nil } // Lookup returns the Flag structure of the named command-line flag, // returning nil if none exists. func Lookup(name string) *Flag { return CommandLine.Lookup(name) } // ShorthandLookup returns the Flag structure of the short handed flag, // returning nil if none exists. func ShorthandLookup(name string) *Flag { return CommandLine.ShorthandLookup(name) } // Set sets the value of the named flag. func (f *FlagSet) Set(name, value string) error { normalName := f.normalizeFlagName(name) flag, ok := f.formal[normalName] if !ok { return fmt.Errorf("no such flag -%v", name) } err := flag.Value.Set(value) if err != nil { var flagName string if flag.Shorthand != "" && flag.ShorthandDeprecated == "" { flagName = fmt.Sprintf("-%s, --%s", flag.Shorthand, flag.Name) } else { flagName = fmt.Sprintf("--%s", flag.Name) } return fmt.Errorf("invalid argument %q for %q flag: %v", value, flagName, err) } if !flag.Changed { if f.actual == nil { f.actual = make(map[NormalizedName]*Flag) } f.actual[normalName] = flag f.orderedActual = append(f.orderedActual, flag) flag.Changed = true } if flag.Deprecated != "" { fmt.Fprintf(f.out(), "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated) } return nil } // SetAnnotation allows one to set arbitrary annotations on a flag in the FlagSet. // This is sometimes used by spf13/cobra programs which want to generate additional // bash completion information. func (f *FlagSet) SetAnnotation(name, key string, values []string) error { normalName := f.normalizeFlagName(name) flag, ok := f.formal[normalName] if !ok { return fmt.Errorf("no such flag -%v", name) } if flag.Annotations == nil { flag.Annotations = map[string][]string{} } flag.Annotations[key] = values return nil } // Changed returns true if the flag was explicitly set during Parse() and false // otherwise func (f *FlagSet) Changed(name string) bool { flag := f.Lookup(name) // If a flag doesn't exist, it wasn't changed.... if flag == nil { return false } return flag.Changed } // Set sets the value of the named command-line flag. func Set(name, value string) error { return CommandLine.Set(name, value) } // PrintDefaults prints, to standard error unless configured // otherwise, the default values of all defined flags in the set. func (f *FlagSet) PrintDefaults() { usages := f.FlagUsages() fmt.Fprint(f.out(), usages) } // defaultIsZeroValue returns true if the default value for this flag represents // a zero value. func (f *Flag) defaultIsZeroValue() bool { switch f.Value.(type) { case boolFlag: return f.DefValue == "false" case *durationValue: // Beginning in Go 1.7, duration zero values are "0s" return f.DefValue == "0" || f.DefValue == "0s" case *intValue, *int8Value, *int32Value, *int64Value, *uintValue, *uint8Value, *uint16Value, *uint32Value, *uint64Value, *countValue, *float32Value, *float64Value: return f.DefValue == "0" case *stringValue: return f.DefValue == "" case *ipValue, *ipMaskValue, *ipNetValue: return f.DefValue == "" case *intSliceValue, *stringSliceValue, *stringArrayValue: return f.DefValue == "[]" default: switch f.Value.String() { case "false": return true case "": return true case "": return true case "0": return true } return false } } // UnquoteUsage extracts a back-quoted name from the usage // string for a flag and returns it and the un-quoted usage. // Given "a `name` to show" it returns ("name", "a name to show"). // If there are no back quotes, the name is an educated guess of the // type of the flag's value, or the empty string if the flag is boolean. func UnquoteUsage(flag *Flag) (name string, usage string) { // Look for a back-quoted name, but avoid the strings package. usage = flag.Usage for i := 0; i < len(usage); i++ { if usage[i] == '`' { for j := i + 1; j < len(usage); j++ { if usage[j] == '`' { name = usage[i+1 : j] usage = usage[:i] + name + usage[j+1:] return name, usage } } break // Only one back quote; use type name. } } name = flag.Value.Type() switch name { case "bool": name = "" case "float64": name = "float" case "int64": name = "int" case "uint64": name = "uint" case "stringSlice": name = "strings" case "intSlice": name = "ints" case "uintSlice": name = "uints" case "boolSlice": name = "bools" } return } // Splits the string `s` on whitespace into an initial substring up to // `i` runes in length and the remainder. Will go `slop` over `i` if // that encompasses the entire string (which allows the caller to // avoid short orphan words on the final line). func wrapN(i, slop int, s string) (string, string) { if i+slop > len(s) { return s, "" } w := strings.LastIndexAny(s[:i], " \t\n") if w <= 0 { return s, "" } nlPos := strings.LastIndex(s[:i], "\n") if nlPos > 0 && nlPos < w { return s[:nlPos], s[nlPos+1:] } return s[:w], s[w+1:] } // Wraps the string `s` to a maximum width `w` with leading indent // `i`. The first line is not indented (this is assumed to be done by // caller). Pass `w` == 0 to do no wrapping func wrap(i, w int, s string) string { if w == 0 { return strings.Replace(s, "\n", "\n"+strings.Repeat(" ", i), -1) } // space between indent i and end of line width w into which // we should wrap the text. wrap := w - i var r, l string // Not enough space for sensible wrapping. Wrap as a block on // the next line instead. if wrap < 24 { i = 16 wrap = w - i r += "\n" + strings.Repeat(" ", i) } // If still not enough space then don't even try to wrap. if wrap < 24 { return strings.Replace(s, "\n", r, -1) } // Try to avoid short orphan words on the final line, by // allowing wrapN to go a bit over if that would fit in the // remainder of the line. slop := 5 wrap = wrap - slop // Handle first line, which is indented by the caller (or the // special case above) l, s = wrapN(wrap, slop, s) r = r + strings.Replace(l, "\n", "\n"+strings.Repeat(" ", i), -1) // Now wrap the rest for s != "" { var t string t, s = wrapN(wrap, slop, s) r = r + "\n" + strings.Repeat(" ", i) + strings.Replace(t, "\n", "\n"+strings.Repeat(" ", i), -1) } return r } // FlagUsagesWrapped returns a string containing the usage information // for all flags in the FlagSet. Wrapped to `cols` columns (0 for no // wrapping) func (f *FlagSet) FlagUsagesWrapped(cols int) string { buf := new(bytes.Buffer) lines := make([]string, 0, len(f.formal)) maxlen := 0 f.VisitAll(func(flag *Flag) { if flag.Hidden { return } line := "" if flag.Shorthand != "" && flag.ShorthandDeprecated == "" { line = fmt.Sprintf(" -%s, --%s", flag.Shorthand, flag.Name) } else { line = fmt.Sprintf(" --%s", flag.Name) } varname, usage := UnquoteUsage(flag) if varname != "" { line += " " + varname } if flag.NoOptDefVal != "" { switch flag.Value.Type() { case "string": line += fmt.Sprintf("[=\"%s\"]", flag.NoOptDefVal) case "bool": if flag.NoOptDefVal != "true" { line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) } case "count": if flag.NoOptDefVal != "+1" { line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) } default: line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) } } // This special character will be replaced with spacing once the // correct alignment is calculated line += "\x00" if len(line) > maxlen { maxlen = len(line) } line += usage if !flag.defaultIsZeroValue() { if flag.Value.Type() == "string" { line += fmt.Sprintf(" (default %q)", flag.DefValue) } else { line += fmt.Sprintf(" (default %s)", flag.DefValue) } } if len(flag.Deprecated) != 0 { line += fmt.Sprintf(" (DEPRECATED: %s)", flag.Deprecated) } lines = append(lines, line) }) for _, line := range lines { sidx := strings.Index(line, "\x00") spacing := strings.Repeat(" ", maxlen-sidx) // maxlen + 2 comes from + 1 for the \x00 and + 1 for the (deliberate) off-by-one in maxlen-sidx fmt.Fprintln(buf, line[:sidx], spacing, wrap(maxlen+2, cols, line[sidx+1:])) } return buf.String() } // FlagUsages returns a string containing the usage information for all flags in // the FlagSet func (f *FlagSet) FlagUsages() string { return f.FlagUsagesWrapped(0) } // PrintDefaults prints to standard error the default values of all defined command-line flags. func PrintDefaults() { CommandLine.PrintDefaults() } // defaultUsage is the default function to print a usage message. func defaultUsage(f *FlagSet) { fmt.Fprintf(f.out(), "Usage of %s:\n", f.name) f.PrintDefaults() } // NOTE: Usage is not just defaultUsage(CommandLine) // because it serves (via godoc flag Usage) as the example // for how to write your own usage function. // Usage prints to standard error a usage message documenting all defined command-line flags. // The function is a variable that may be changed to point to a custom function. // By default it prints a simple header and calls PrintDefaults; for details about the // format of the output and how to control it, see the documentation for PrintDefaults. var Usage = func() { fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) PrintDefaults() } // NFlag returns the number of flags that have been set. func (f *FlagSet) NFlag() int { return len(f.actual) } // NFlag returns the number of command-line flags that have been set. func NFlag() int { return len(CommandLine.actual) } // Arg returns the i'th argument. Arg(0) is the first remaining argument // after flags have been processed. func (f *FlagSet) Arg(i int) string { if i < 0 || i >= len(f.args) { return "" } return f.args[i] } // Arg returns the i'th command-line argument. Arg(0) is the first remaining argument // after flags have been processed. func Arg(i int) string { return CommandLine.Arg(i) } // NArg is the number of arguments remaining after flags have been processed. func (f *FlagSet) NArg() int { return len(f.args) } // NArg is the number of arguments remaining after flags have been processed. func NArg() int { return len(CommandLine.args) } // Args returns the non-flag arguments. func (f *FlagSet) Args() []string { return f.args } // Args returns the non-flag command-line arguments. func Args() []string { return CommandLine.args } // Var defines a flag with the specified name and usage string. The type and // value of the flag are represented by the first argument, of type Value, which // typically holds a user-defined implementation of Value. For instance, the // caller could create a flag that turns a comma-separated string into a slice // of strings by giving the slice the methods of Value; in particular, Set would // decompose the comma-separated string into the slice. func (f *FlagSet) Var(value Value, name string, usage string) { f.VarP(value, name, "", usage) } // VarPF is like VarP, but returns the flag created func (f *FlagSet) VarPF(value Value, name, shorthand, usage string) *Flag { // Remember the default value as a string; it won't change. flag := &Flag{ Name: name, Shorthand: shorthand, Usage: usage, Value: value, DefValue: value.String(), } f.AddFlag(flag) return flag } // VarP is like Var, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) VarP(value Value, name, shorthand, usage string) { f.VarPF(value, name, shorthand, usage) } // AddFlag will add the flag to the FlagSet func (f *FlagSet) AddFlag(flag *Flag) { normalizedFlagName := f.normalizeFlagName(flag.Name) _, alreadyThere := f.formal[normalizedFlagName] if alreadyThere { msg := fmt.Sprintf("%s flag redefined: %s", f.name, flag.Name) fmt.Fprintln(f.out(), msg) panic(msg) // Happens only if flags are declared with identical names } if f.formal == nil { f.formal = make(map[NormalizedName]*Flag) } flag.Name = string(normalizedFlagName) f.formal[normalizedFlagName] = flag f.orderedFormal = append(f.orderedFormal, flag) if flag.Shorthand == "" { return } if len(flag.Shorthand) > 1 { msg := fmt.Sprintf("%q shorthand is more than one ASCII character", flag.Shorthand) fmt.Fprintf(f.out(), msg) panic(msg) } if f.shorthands == nil { f.shorthands = make(map[byte]*Flag) } c := flag.Shorthand[0] used, alreadyThere := f.shorthands[c] if alreadyThere { msg := fmt.Sprintf("unable to redefine %q shorthand in %q flagset: it's already used for %q flag", c, f.name, used.Name) fmt.Fprintf(f.out(), msg) panic(msg) } f.shorthands[c] = flag } // AddFlagSet adds one FlagSet to another. If a flag is already present in f // the flag from newSet will be ignored. func (f *FlagSet) AddFlagSet(newSet *FlagSet) { if newSet == nil { return } newSet.VisitAll(func(flag *Flag) { if f.Lookup(flag.Name) == nil { f.AddFlag(flag) } }) } // Var defines a flag with the specified name and usage string. The type and // value of the flag are represented by the first argument, of type Value, which // typically holds a user-defined implementation of Value. For instance, the // caller could create a flag that turns a comma-separated string into a slice // of strings by giving the slice the methods of Value; in particular, Set would // decompose the comma-separated string into the slice. func Var(value Value, name string, usage string) { CommandLine.VarP(value, name, "", usage) } // VarP is like Var, but accepts a shorthand letter that can be used after a single dash. func VarP(value Value, name, shorthand, usage string) { CommandLine.VarP(value, name, shorthand, usage) } // failf prints to standard error a formatted error and usage message and // returns the error. func (f *FlagSet) failf(format string, a ...interface{}) error { err := fmt.Errorf(format, a...) if f.errorHandling != ContinueOnError { fmt.Fprintln(f.out(), err) f.usage() } return err } // usage calls the Usage method for the flag set, or the usage function if // the flag set is CommandLine. func (f *FlagSet) usage() { if f == CommandLine { Usage() } else if f.Usage == nil { defaultUsage(f) } else { f.Usage() } } //--unknown (args will be empty) //--unknown --next-flag ... (args will be --next-flag ...) //--unknown arg ... (args will be arg ...) func stripUnknownFlagValue(args []string) []string { if len(args) == 0 { //--unknown return args } first := args[0] if len(first) > 0 && first[0] == '-' { //--unknown --next-flag ... return args } //--unknown arg ... (args will be arg ...) if len(args) > 1 { return args[1:] } return nil } func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []string, err error) { a = args name := s[2:] if len(name) == 0 || name[0] == '-' || name[0] == '=' { err = f.failf("bad flag syntax: %s", s) return } split := strings.SplitN(name, "=", 2) name = split[0] flag, exists := f.formal[f.normalizeFlagName(name)] if !exists { switch { case name == "help": f.usage() return a, ErrHelp case f.ParseErrorsWhitelist.UnknownFlags: // --unknown=unknownval arg ... // we do not want to lose arg in this case if len(split) >= 2 { return a, nil } return stripUnknownFlagValue(a), nil default: err = f.failf("unknown flag: --%s", name) return } } var value string if len(split) == 2 { // '--flag=arg' value = split[1] } else if flag.NoOptDefVal != "" { // '--flag' (arg was optional) value = flag.NoOptDefVal } else if len(a) > 0 { // '--flag arg' value = a[0] a = a[1:] } else { // '--flag' (arg was required) err = f.failf("flag needs an argument: %s", s) return } err = fn(flag, value) if err != nil { f.failf(err.Error()) } return } func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parseFunc) (outShorts string, outArgs []string, err error) { outArgs = args if strings.HasPrefix(shorthands, "test.") { return } outShorts = shorthands[1:] c := shorthands[0] flag, exists := f.shorthands[c] if !exists { switch { case c == 'h': f.usage() err = ErrHelp return case f.ParseErrorsWhitelist.UnknownFlags: // '-f=arg arg ...' // we do not want to lose arg in this case if len(shorthands) > 2 && shorthands[1] == '=' { outShorts = "" return } outArgs = stripUnknownFlagValue(outArgs) return default: err = f.failf("unknown shorthand flag: %q in -%s", c, shorthands) return } } var value string if len(shorthands) > 2 && shorthands[1] == '=' { // '-f=arg' value = shorthands[2:] outShorts = "" } else if flag.NoOptDefVal != "" { // '-f' (arg was optional) value = flag.NoOptDefVal } else if len(shorthands) > 1 { // '-farg' value = shorthands[1:] outShorts = "" } else if len(args) > 0 { // '-f arg' value = args[0] outArgs = args[1:] } else { // '-f' (arg was required) err = f.failf("flag needs an argument: %q in -%s", c, shorthands) return } if flag.ShorthandDeprecated != "" { fmt.Fprintf(f.out(), "Flag shorthand -%s has been deprecated, %s\n", flag.Shorthand, flag.ShorthandDeprecated) } err = fn(flag, value) if err != nil { f.failf(err.Error()) } return } func (f *FlagSet) parseShortArg(s string, args []string, fn parseFunc) (a []string, err error) { a = args shorthands := s[1:] // "shorthands" can be a series of shorthand letters of flags (e.g. "-vvv"). for len(shorthands) > 0 { shorthands, a, err = f.parseSingleShortArg(shorthands, args, fn) if err != nil { return } } return } func (f *FlagSet) parseArgs(args []string, fn parseFunc) (err error) { for len(args) > 0 { s := args[0] args = args[1:] if len(s) == 0 || s[0] != '-' || len(s) == 1 { if !f.interspersed { f.args = append(f.args, s) f.args = append(f.args, args...) return nil } f.args = append(f.args, s) continue } if s[1] == '-' { if len(s) == 2 { // "--" terminates the flags f.argsLenAtDash = len(f.args) f.args = append(f.args, args...) break } args, err = f.parseLongArg(s, args, fn) } else { args, err = f.parseShortArg(s, args, fn) } if err != nil { return } } return } // Parse parses flag definitions from the argument list, which should not // include the command name. Must be called after all flags in the FlagSet // are defined and before flags are accessed by the program. // The return value will be ErrHelp if -help was set but not defined. func (f *FlagSet) Parse(arguments []string) error { if f.addedGoFlagSets != nil { for _, goFlagSet := range f.addedGoFlagSets { goFlagSet.Parse(nil) } } f.parsed = true if len(arguments) < 0 { return nil } f.args = make([]string, 0, len(arguments)) set := func(flag *Flag, value string) error { return f.Set(flag.Name, value) } err := f.parseArgs(arguments, set) if err != nil { switch f.errorHandling { case ContinueOnError: return err case ExitOnError: fmt.Println(err) os.Exit(2) case PanicOnError: panic(err) } } return nil } type parseFunc func(flag *Flag, value string) error // ParseAll parses flag definitions from the argument list, which should not // include the command name. The arguments for fn are flag and value. Must be // called after all flags in the FlagSet are defined and before flags are // accessed by the program. The return value will be ErrHelp if -help was set // but not defined. func (f *FlagSet) ParseAll(arguments []string, fn func(flag *Flag, value string) error) error { f.parsed = true f.args = make([]string, 0, len(arguments)) err := f.parseArgs(arguments, fn) if err != nil { switch f.errorHandling { case ContinueOnError: return err case ExitOnError: os.Exit(2) case PanicOnError: panic(err) } } return nil } // Parsed reports whether f.Parse has been called. func (f *FlagSet) Parsed() bool { return f.parsed } // Parse parses the command-line flags from os.Args[1:]. Must be called // after all flags are defined and before flags are accessed by the program. func Parse() { // Ignore errors; CommandLine is set for ExitOnError. CommandLine.Parse(os.Args[1:]) } // ParseAll parses the command-line flags from os.Args[1:] and called fn for each. // The arguments for fn are flag and value. Must be called after all flags are // defined and before flags are accessed by the program. func ParseAll(fn func(flag *Flag, value string) error) { // Ignore errors; CommandLine is set for ExitOnError. CommandLine.ParseAll(os.Args[1:], fn) } // SetInterspersed sets whether to support interspersed option/non-option arguments. func SetInterspersed(interspersed bool) { CommandLine.SetInterspersed(interspersed) } // Parsed returns true if the command-line flags have been parsed. func Parsed() bool { return CommandLine.Parsed() } // CommandLine is the default set of command-line flags, parsed from os.Args. var CommandLine = NewFlagSet(os.Args[0], ExitOnError) // NewFlagSet returns a new, empty flag set with the specified name, // error handling property and SortFlags set to true. func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet { f := &FlagSet{ name: name, errorHandling: errorHandling, argsLenAtDash: -1, interspersed: true, SortFlags: true, } return f } // SetInterspersed sets whether to support interspersed option/non-option arguments. func (f *FlagSet) SetInterspersed(interspersed bool) { f.interspersed = interspersed } // Init sets the name and error handling property for a flag set. // By default, the zero FlagSet uses an empty name and the // ContinueOnError error handling policy. func (f *FlagSet) Init(name string, errorHandling ErrorHandling) { f.name = name f.errorHandling = errorHandling f.argsLenAtDash = -1 } ================================================ FILE: vendor/github.com/spf13/pflag/float32.go ================================================ package pflag import "strconv" // -- float32 Value type float32Value float32 func newFloat32Value(val float32, p *float32) *float32Value { *p = val return (*float32Value)(p) } func (f *float32Value) Set(s string) error { v, err := strconv.ParseFloat(s, 32) *f = float32Value(v) return err } func (f *float32Value) Type() string { return "float32" } func (f *float32Value) String() string { return strconv.FormatFloat(float64(*f), 'g', -1, 32) } func float32Conv(sval string) (interface{}, error) { v, err := strconv.ParseFloat(sval, 32) if err != nil { return 0, err } return float32(v), nil } // GetFloat32 return the float32 value of a flag with the given name func (f *FlagSet) GetFloat32(name string) (float32, error) { val, err := f.getFlagType(name, "float32", float32Conv) if err != nil { return 0, err } return val.(float32), nil } // Float32Var defines a float32 flag with specified name, default value, and usage string. // The argument p points to a float32 variable in which to store the value of the flag. func (f *FlagSet) Float32Var(p *float32, name string, value float32, usage string) { f.VarP(newFloat32Value(value, p), name, "", usage) } // Float32VarP is like Float32Var, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Float32VarP(p *float32, name, shorthand string, value float32, usage string) { f.VarP(newFloat32Value(value, p), name, shorthand, usage) } // Float32Var defines a float32 flag with specified name, default value, and usage string. // The argument p points to a float32 variable in which to store the value of the flag. func Float32Var(p *float32, name string, value float32, usage string) { CommandLine.VarP(newFloat32Value(value, p), name, "", usage) } // Float32VarP is like Float32Var, but accepts a shorthand letter that can be used after a single dash. func Float32VarP(p *float32, name, shorthand string, value float32, usage string) { CommandLine.VarP(newFloat32Value(value, p), name, shorthand, usage) } // Float32 defines a float32 flag with specified name, default value, and usage string. // The return value is the address of a float32 variable that stores the value of the flag. func (f *FlagSet) Float32(name string, value float32, usage string) *float32 { p := new(float32) f.Float32VarP(p, name, "", value, usage) return p } // Float32P is like Float32, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Float32P(name, shorthand string, value float32, usage string) *float32 { p := new(float32) f.Float32VarP(p, name, shorthand, value, usage) return p } // Float32 defines a float32 flag with specified name, default value, and usage string. // The return value is the address of a float32 variable that stores the value of the flag. func Float32(name string, value float32, usage string) *float32 { return CommandLine.Float32P(name, "", value, usage) } // Float32P is like Float32, but accepts a shorthand letter that can be used after a single dash. func Float32P(name, shorthand string, value float32, usage string) *float32 { return CommandLine.Float32P(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/float32_slice.go ================================================ package pflag import ( "fmt" "strconv" "strings" ) // -- float32Slice Value type float32SliceValue struct { value *[]float32 changed bool } func newFloat32SliceValue(val []float32, p *[]float32) *float32SliceValue { isv := new(float32SliceValue) isv.value = p *isv.value = val return isv } func (s *float32SliceValue) Set(val string) error { ss := strings.Split(val, ",") out := make([]float32, len(ss)) for i, d := range ss { var err error var temp64 float64 temp64, err = strconv.ParseFloat(d, 32) if err != nil { return err } out[i] = float32(temp64) } if !s.changed { *s.value = out } else { *s.value = append(*s.value, out...) } s.changed = true return nil } func (s *float32SliceValue) Type() string { return "float32Slice" } func (s *float32SliceValue) String() string { out := make([]string, len(*s.value)) for i, d := range *s.value { out[i] = fmt.Sprintf("%f", d) } return "[" + strings.Join(out, ",") + "]" } func (s *float32SliceValue) fromString(val string) (float32, error) { t64, err := strconv.ParseFloat(val, 32) if err != nil { return 0, err } return float32(t64), nil } func (s *float32SliceValue) toString(val float32) string { return fmt.Sprintf("%f", val) } func (s *float32SliceValue) Append(val string) error { i, err := s.fromString(val) if err != nil { return err } *s.value = append(*s.value, i) return nil } func (s *float32SliceValue) Replace(val []string) error { out := make([]float32, len(val)) for i, d := range val { var err error out[i], err = s.fromString(d) if err != nil { return err } } *s.value = out return nil } func (s *float32SliceValue) GetSlice() []string { out := make([]string, len(*s.value)) for i, d := range *s.value { out[i] = s.toString(d) } return out } func float32SliceConv(val string) (interface{}, error) { val = strings.Trim(val, "[]") // Empty string would cause a slice with one (empty) entry if len(val) == 0 { return []float32{}, nil } ss := strings.Split(val, ",") out := make([]float32, len(ss)) for i, d := range ss { var err error var temp64 float64 temp64, err = strconv.ParseFloat(d, 32) if err != nil { return nil, err } out[i] = float32(temp64) } return out, nil } // GetFloat32Slice return the []float32 value of a flag with the given name func (f *FlagSet) GetFloat32Slice(name string) ([]float32, error) { val, err := f.getFlagType(name, "float32Slice", float32SliceConv) if err != nil { return []float32{}, err } return val.([]float32), nil } // Float32SliceVar defines a float32Slice flag with specified name, default value, and usage string. // The argument p points to a []float32 variable in which to store the value of the flag. func (f *FlagSet) Float32SliceVar(p *[]float32, name string, value []float32, usage string) { f.VarP(newFloat32SliceValue(value, p), name, "", usage) } // Float32SliceVarP is like Float32SliceVar, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Float32SliceVarP(p *[]float32, name, shorthand string, value []float32, usage string) { f.VarP(newFloat32SliceValue(value, p), name, shorthand, usage) } // Float32SliceVar defines a float32[] flag with specified name, default value, and usage string. // The argument p points to a float32[] variable in which to store the value of the flag. func Float32SliceVar(p *[]float32, name string, value []float32, usage string) { CommandLine.VarP(newFloat32SliceValue(value, p), name, "", usage) } // Float32SliceVarP is like Float32SliceVar, but accepts a shorthand letter that can be used after a single dash. func Float32SliceVarP(p *[]float32, name, shorthand string, value []float32, usage string) { CommandLine.VarP(newFloat32SliceValue(value, p), name, shorthand, usage) } // Float32Slice defines a []float32 flag with specified name, default value, and usage string. // The return value is the address of a []float32 variable that stores the value of the flag. func (f *FlagSet) Float32Slice(name string, value []float32, usage string) *[]float32 { p := []float32{} f.Float32SliceVarP(&p, name, "", value, usage) return &p } // Float32SliceP is like Float32Slice, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Float32SliceP(name, shorthand string, value []float32, usage string) *[]float32 { p := []float32{} f.Float32SliceVarP(&p, name, shorthand, value, usage) return &p } // Float32Slice defines a []float32 flag with specified name, default value, and usage string. // The return value is the address of a []float32 variable that stores the value of the flag. func Float32Slice(name string, value []float32, usage string) *[]float32 { return CommandLine.Float32SliceP(name, "", value, usage) } // Float32SliceP is like Float32Slice, but accepts a shorthand letter that can be used after a single dash. func Float32SliceP(name, shorthand string, value []float32, usage string) *[]float32 { return CommandLine.Float32SliceP(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/float64.go ================================================ package pflag import "strconv" // -- float64 Value type float64Value float64 func newFloat64Value(val float64, p *float64) *float64Value { *p = val return (*float64Value)(p) } func (f *float64Value) Set(s string) error { v, err := strconv.ParseFloat(s, 64) *f = float64Value(v) return err } func (f *float64Value) Type() string { return "float64" } func (f *float64Value) String() string { return strconv.FormatFloat(float64(*f), 'g', -1, 64) } func float64Conv(sval string) (interface{}, error) { return strconv.ParseFloat(sval, 64) } // GetFloat64 return the float64 value of a flag with the given name func (f *FlagSet) GetFloat64(name string) (float64, error) { val, err := f.getFlagType(name, "float64", float64Conv) if err != nil { return 0, err } return val.(float64), nil } // Float64Var defines a float64 flag with specified name, default value, and usage string. // The argument p points to a float64 variable in which to store the value of the flag. func (f *FlagSet) Float64Var(p *float64, name string, value float64, usage string) { f.VarP(newFloat64Value(value, p), name, "", usage) } // Float64VarP is like Float64Var, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Float64VarP(p *float64, name, shorthand string, value float64, usage string) { f.VarP(newFloat64Value(value, p), name, shorthand, usage) } // Float64Var defines a float64 flag with specified name, default value, and usage string. // The argument p points to a float64 variable in which to store the value of the flag. func Float64Var(p *float64, name string, value float64, usage string) { CommandLine.VarP(newFloat64Value(value, p), name, "", usage) } // Float64VarP is like Float64Var, but accepts a shorthand letter that can be used after a single dash. func Float64VarP(p *float64, name, shorthand string, value float64, usage string) { CommandLine.VarP(newFloat64Value(value, p), name, shorthand, usage) } // Float64 defines a float64 flag with specified name, default value, and usage string. // The return value is the address of a float64 variable that stores the value of the flag. func (f *FlagSet) Float64(name string, value float64, usage string) *float64 { p := new(float64) f.Float64VarP(p, name, "", value, usage) return p } // Float64P is like Float64, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Float64P(name, shorthand string, value float64, usage string) *float64 { p := new(float64) f.Float64VarP(p, name, shorthand, value, usage) return p } // Float64 defines a float64 flag with specified name, default value, and usage string. // The return value is the address of a float64 variable that stores the value of the flag. func Float64(name string, value float64, usage string) *float64 { return CommandLine.Float64P(name, "", value, usage) } // Float64P is like Float64, but accepts a shorthand letter that can be used after a single dash. func Float64P(name, shorthand string, value float64, usage string) *float64 { return CommandLine.Float64P(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/float64_slice.go ================================================ package pflag import ( "fmt" "strconv" "strings" ) // -- float64Slice Value type float64SliceValue struct { value *[]float64 changed bool } func newFloat64SliceValue(val []float64, p *[]float64) *float64SliceValue { isv := new(float64SliceValue) isv.value = p *isv.value = val return isv } func (s *float64SliceValue) Set(val string) error { ss := strings.Split(val, ",") out := make([]float64, len(ss)) for i, d := range ss { var err error out[i], err = strconv.ParseFloat(d, 64) if err != nil { return err } } if !s.changed { *s.value = out } else { *s.value = append(*s.value, out...) } s.changed = true return nil } func (s *float64SliceValue) Type() string { return "float64Slice" } func (s *float64SliceValue) String() string { out := make([]string, len(*s.value)) for i, d := range *s.value { out[i] = fmt.Sprintf("%f", d) } return "[" + strings.Join(out, ",") + "]" } func (s *float64SliceValue) fromString(val string) (float64, error) { return strconv.ParseFloat(val, 64) } func (s *float64SliceValue) toString(val float64) string { return fmt.Sprintf("%f", val) } func (s *float64SliceValue) Append(val string) error { i, err := s.fromString(val) if err != nil { return err } *s.value = append(*s.value, i) return nil } func (s *float64SliceValue) Replace(val []string) error { out := make([]float64, len(val)) for i, d := range val { var err error out[i], err = s.fromString(d) if err != nil { return err } } *s.value = out return nil } func (s *float64SliceValue) GetSlice() []string { out := make([]string, len(*s.value)) for i, d := range *s.value { out[i] = s.toString(d) } return out } func float64SliceConv(val string) (interface{}, error) { val = strings.Trim(val, "[]") // Empty string would cause a slice with one (empty) entry if len(val) == 0 { return []float64{}, nil } ss := strings.Split(val, ",") out := make([]float64, len(ss)) for i, d := range ss { var err error out[i], err = strconv.ParseFloat(d, 64) if err != nil { return nil, err } } return out, nil } // GetFloat64Slice return the []float64 value of a flag with the given name func (f *FlagSet) GetFloat64Slice(name string) ([]float64, error) { val, err := f.getFlagType(name, "float64Slice", float64SliceConv) if err != nil { return []float64{}, err } return val.([]float64), nil } // Float64SliceVar defines a float64Slice flag with specified name, default value, and usage string. // The argument p points to a []float64 variable in which to store the value of the flag. func (f *FlagSet) Float64SliceVar(p *[]float64, name string, value []float64, usage string) { f.VarP(newFloat64SliceValue(value, p), name, "", usage) } // Float64SliceVarP is like Float64SliceVar, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Float64SliceVarP(p *[]float64, name, shorthand string, value []float64, usage string) { f.VarP(newFloat64SliceValue(value, p), name, shorthand, usage) } // Float64SliceVar defines a float64[] flag with specified name, default value, and usage string. // The argument p points to a float64[] variable in which to store the value of the flag. func Float64SliceVar(p *[]float64, name string, value []float64, usage string) { CommandLine.VarP(newFloat64SliceValue(value, p), name, "", usage) } // Float64SliceVarP is like Float64SliceVar, but accepts a shorthand letter that can be used after a single dash. func Float64SliceVarP(p *[]float64, name, shorthand string, value []float64, usage string) { CommandLine.VarP(newFloat64SliceValue(value, p), name, shorthand, usage) } // Float64Slice defines a []float64 flag with specified name, default value, and usage string. // The return value is the address of a []float64 variable that stores the value of the flag. func (f *FlagSet) Float64Slice(name string, value []float64, usage string) *[]float64 { p := []float64{} f.Float64SliceVarP(&p, name, "", value, usage) return &p } // Float64SliceP is like Float64Slice, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Float64SliceP(name, shorthand string, value []float64, usage string) *[]float64 { p := []float64{} f.Float64SliceVarP(&p, name, shorthand, value, usage) return &p } // Float64Slice defines a []float64 flag with specified name, default value, and usage string. // The return value is the address of a []float64 variable that stores the value of the flag. func Float64Slice(name string, value []float64, usage string) *[]float64 { return CommandLine.Float64SliceP(name, "", value, usage) } // Float64SliceP is like Float64Slice, but accepts a shorthand letter that can be used after a single dash. func Float64SliceP(name, shorthand string, value []float64, usage string) *[]float64 { return CommandLine.Float64SliceP(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/golangflag.go ================================================ // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package pflag import ( goflag "flag" "reflect" "strings" ) // flagValueWrapper implements pflag.Value around a flag.Value. The main // difference here is the addition of the Type method that returns a string // name of the type. As this is generally unknown, we approximate that with // reflection. type flagValueWrapper struct { inner goflag.Value flagType string } // We are just copying the boolFlag interface out of goflag as that is what // they use to decide if a flag should get "true" when no arg is given. type goBoolFlag interface { goflag.Value IsBoolFlag() bool } func wrapFlagValue(v goflag.Value) Value { // If the flag.Value happens to also be a pflag.Value, just use it directly. if pv, ok := v.(Value); ok { return pv } pv := &flagValueWrapper{ inner: v, } t := reflect.TypeOf(v) if t.Kind() == reflect.Interface || t.Kind() == reflect.Ptr { t = t.Elem() } pv.flagType = strings.TrimSuffix(t.Name(), "Value") return pv } func (v *flagValueWrapper) String() string { return v.inner.String() } func (v *flagValueWrapper) Set(s string) error { return v.inner.Set(s) } func (v *flagValueWrapper) Type() string { return v.flagType } // PFlagFromGoFlag will return a *pflag.Flag given a *flag.Flag // If the *flag.Flag.Name was a single character (ex: `v`) it will be accessiblei // with both `-v` and `--v` in flags. If the golang flag was more than a single // character (ex: `verbose`) it will only be accessible via `--verbose` func PFlagFromGoFlag(goflag *goflag.Flag) *Flag { // Remember the default value as a string; it won't change. flag := &Flag{ Name: goflag.Name, Usage: goflag.Usage, Value: wrapFlagValue(goflag.Value), // Looks like golang flags don't set DefValue correctly :-( //DefValue: goflag.DefValue, DefValue: goflag.Value.String(), } // Ex: if the golang flag was -v, allow both -v and --v to work if len(flag.Name) == 1 { flag.Shorthand = flag.Name } if fv, ok := goflag.Value.(goBoolFlag); ok && fv.IsBoolFlag() { flag.NoOptDefVal = "true" } return flag } // AddGoFlag will add the given *flag.Flag to the pflag.FlagSet func (f *FlagSet) AddGoFlag(goflag *goflag.Flag) { if f.Lookup(goflag.Name) != nil { return } newflag := PFlagFromGoFlag(goflag) f.AddFlag(newflag) } // AddGoFlagSet will add the given *flag.FlagSet to the pflag.FlagSet func (f *FlagSet) AddGoFlagSet(newSet *goflag.FlagSet) { if newSet == nil { return } newSet.VisitAll(func(goflag *goflag.Flag) { f.AddGoFlag(goflag) }) if f.addedGoFlagSets == nil { f.addedGoFlagSets = make([]*goflag.FlagSet, 0) } f.addedGoFlagSets = append(f.addedGoFlagSets, newSet) } ================================================ FILE: vendor/github.com/spf13/pflag/int.go ================================================ package pflag import "strconv" // -- int Value type intValue int func newIntValue(val int, p *int) *intValue { *p = val return (*intValue)(p) } func (i *intValue) Set(s string) error { v, err := strconv.ParseInt(s, 0, 64) *i = intValue(v) return err } func (i *intValue) Type() string { return "int" } func (i *intValue) String() string { return strconv.Itoa(int(*i)) } func intConv(sval string) (interface{}, error) { return strconv.Atoi(sval) } // GetInt return the int value of a flag with the given name func (f *FlagSet) GetInt(name string) (int, error) { val, err := f.getFlagType(name, "int", intConv) if err != nil { return 0, err } return val.(int), nil } // IntVar defines an int flag with specified name, default value, and usage string. // The argument p points to an int variable in which to store the value of the flag. func (f *FlagSet) IntVar(p *int, name string, value int, usage string) { f.VarP(newIntValue(value, p), name, "", usage) } // IntVarP is like IntVar, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) IntVarP(p *int, name, shorthand string, value int, usage string) { f.VarP(newIntValue(value, p), name, shorthand, usage) } // IntVar defines an int flag with specified name, default value, and usage string. // The argument p points to an int variable in which to store the value of the flag. func IntVar(p *int, name string, value int, usage string) { CommandLine.VarP(newIntValue(value, p), name, "", usage) } // IntVarP is like IntVar, but accepts a shorthand letter that can be used after a single dash. func IntVarP(p *int, name, shorthand string, value int, usage string) { CommandLine.VarP(newIntValue(value, p), name, shorthand, usage) } // Int defines an int flag with specified name, default value, and usage string. // The return value is the address of an int variable that stores the value of the flag. func (f *FlagSet) Int(name string, value int, usage string) *int { p := new(int) f.IntVarP(p, name, "", value, usage) return p } // IntP is like Int, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) IntP(name, shorthand string, value int, usage string) *int { p := new(int) f.IntVarP(p, name, shorthand, value, usage) return p } // Int defines an int flag with specified name, default value, and usage string. // The return value is the address of an int variable that stores the value of the flag. func Int(name string, value int, usage string) *int { return CommandLine.IntP(name, "", value, usage) } // IntP is like Int, but accepts a shorthand letter that can be used after a single dash. func IntP(name, shorthand string, value int, usage string) *int { return CommandLine.IntP(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/int16.go ================================================ package pflag import "strconv" // -- int16 Value type int16Value int16 func newInt16Value(val int16, p *int16) *int16Value { *p = val return (*int16Value)(p) } func (i *int16Value) Set(s string) error { v, err := strconv.ParseInt(s, 0, 16) *i = int16Value(v) return err } func (i *int16Value) Type() string { return "int16" } func (i *int16Value) String() string { return strconv.FormatInt(int64(*i), 10) } func int16Conv(sval string) (interface{}, error) { v, err := strconv.ParseInt(sval, 0, 16) if err != nil { return 0, err } return int16(v), nil } // GetInt16 returns the int16 value of a flag with the given name func (f *FlagSet) GetInt16(name string) (int16, error) { val, err := f.getFlagType(name, "int16", int16Conv) if err != nil { return 0, err } return val.(int16), nil } // Int16Var defines an int16 flag with specified name, default value, and usage string. // The argument p points to an int16 variable in which to store the value of the flag. func (f *FlagSet) Int16Var(p *int16, name string, value int16, usage string) { f.VarP(newInt16Value(value, p), name, "", usage) } // Int16VarP is like Int16Var, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Int16VarP(p *int16, name, shorthand string, value int16, usage string) { f.VarP(newInt16Value(value, p), name, shorthand, usage) } // Int16Var defines an int16 flag with specified name, default value, and usage string. // The argument p points to an int16 variable in which to store the value of the flag. func Int16Var(p *int16, name string, value int16, usage string) { CommandLine.VarP(newInt16Value(value, p), name, "", usage) } // Int16VarP is like Int16Var, but accepts a shorthand letter that can be used after a single dash. func Int16VarP(p *int16, name, shorthand string, value int16, usage string) { CommandLine.VarP(newInt16Value(value, p), name, shorthand, usage) } // Int16 defines an int16 flag with specified name, default value, and usage string. // The return value is the address of an int16 variable that stores the value of the flag. func (f *FlagSet) Int16(name string, value int16, usage string) *int16 { p := new(int16) f.Int16VarP(p, name, "", value, usage) return p } // Int16P is like Int16, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Int16P(name, shorthand string, value int16, usage string) *int16 { p := new(int16) f.Int16VarP(p, name, shorthand, value, usage) return p } // Int16 defines an int16 flag with specified name, default value, and usage string. // The return value is the address of an int16 variable that stores the value of the flag. func Int16(name string, value int16, usage string) *int16 { return CommandLine.Int16P(name, "", value, usage) } // Int16P is like Int16, but accepts a shorthand letter that can be used after a single dash. func Int16P(name, shorthand string, value int16, usage string) *int16 { return CommandLine.Int16P(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/int32.go ================================================ package pflag import "strconv" // -- int32 Value type int32Value int32 func newInt32Value(val int32, p *int32) *int32Value { *p = val return (*int32Value)(p) } func (i *int32Value) Set(s string) error { v, err := strconv.ParseInt(s, 0, 32) *i = int32Value(v) return err } func (i *int32Value) Type() string { return "int32" } func (i *int32Value) String() string { return strconv.FormatInt(int64(*i), 10) } func int32Conv(sval string) (interface{}, error) { v, err := strconv.ParseInt(sval, 0, 32) if err != nil { return 0, err } return int32(v), nil } // GetInt32 return the int32 value of a flag with the given name func (f *FlagSet) GetInt32(name string) (int32, error) { val, err := f.getFlagType(name, "int32", int32Conv) if err != nil { return 0, err } return val.(int32), nil } // Int32Var defines an int32 flag with specified name, default value, and usage string. // The argument p points to an int32 variable in which to store the value of the flag. func (f *FlagSet) Int32Var(p *int32, name string, value int32, usage string) { f.VarP(newInt32Value(value, p), name, "", usage) } // Int32VarP is like Int32Var, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Int32VarP(p *int32, name, shorthand string, value int32, usage string) { f.VarP(newInt32Value(value, p), name, shorthand, usage) } // Int32Var defines an int32 flag with specified name, default value, and usage string. // The argument p points to an int32 variable in which to store the value of the flag. func Int32Var(p *int32, name string, value int32, usage string) { CommandLine.VarP(newInt32Value(value, p), name, "", usage) } // Int32VarP is like Int32Var, but accepts a shorthand letter that can be used after a single dash. func Int32VarP(p *int32, name, shorthand string, value int32, usage string) { CommandLine.VarP(newInt32Value(value, p), name, shorthand, usage) } // Int32 defines an int32 flag with specified name, default value, and usage string. // The return value is the address of an int32 variable that stores the value of the flag. func (f *FlagSet) Int32(name string, value int32, usage string) *int32 { p := new(int32) f.Int32VarP(p, name, "", value, usage) return p } // Int32P is like Int32, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Int32P(name, shorthand string, value int32, usage string) *int32 { p := new(int32) f.Int32VarP(p, name, shorthand, value, usage) return p } // Int32 defines an int32 flag with specified name, default value, and usage string. // The return value is the address of an int32 variable that stores the value of the flag. func Int32(name string, value int32, usage string) *int32 { return CommandLine.Int32P(name, "", value, usage) } // Int32P is like Int32, but accepts a shorthand letter that can be used after a single dash. func Int32P(name, shorthand string, value int32, usage string) *int32 { return CommandLine.Int32P(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/int32_slice.go ================================================ package pflag import ( "fmt" "strconv" "strings" ) // -- int32Slice Value type int32SliceValue struct { value *[]int32 changed bool } func newInt32SliceValue(val []int32, p *[]int32) *int32SliceValue { isv := new(int32SliceValue) isv.value = p *isv.value = val return isv } func (s *int32SliceValue) Set(val string) error { ss := strings.Split(val, ",") out := make([]int32, len(ss)) for i, d := range ss { var err error var temp64 int64 temp64, err = strconv.ParseInt(d, 0, 32) if err != nil { return err } out[i] = int32(temp64) } if !s.changed { *s.value = out } else { *s.value = append(*s.value, out...) } s.changed = true return nil } func (s *int32SliceValue) Type() string { return "int32Slice" } func (s *int32SliceValue) String() string { out := make([]string, len(*s.value)) for i, d := range *s.value { out[i] = fmt.Sprintf("%d", d) } return "[" + strings.Join(out, ",") + "]" } func (s *int32SliceValue) fromString(val string) (int32, error) { t64, err := strconv.ParseInt(val, 0, 32) if err != nil { return 0, err } return int32(t64), nil } func (s *int32SliceValue) toString(val int32) string { return fmt.Sprintf("%d", val) } func (s *int32SliceValue) Append(val string) error { i, err := s.fromString(val) if err != nil { return err } *s.value = append(*s.value, i) return nil } func (s *int32SliceValue) Replace(val []string) error { out := make([]int32, len(val)) for i, d := range val { var err error out[i], err = s.fromString(d) if err != nil { return err } } *s.value = out return nil } func (s *int32SliceValue) GetSlice() []string { out := make([]string, len(*s.value)) for i, d := range *s.value { out[i] = s.toString(d) } return out } func int32SliceConv(val string) (interface{}, error) { val = strings.Trim(val, "[]") // Empty string would cause a slice with one (empty) entry if len(val) == 0 { return []int32{}, nil } ss := strings.Split(val, ",") out := make([]int32, len(ss)) for i, d := range ss { var err error var temp64 int64 temp64, err = strconv.ParseInt(d, 0, 32) if err != nil { return nil, err } out[i] = int32(temp64) } return out, nil } // GetInt32Slice return the []int32 value of a flag with the given name func (f *FlagSet) GetInt32Slice(name string) ([]int32, error) { val, err := f.getFlagType(name, "int32Slice", int32SliceConv) if err != nil { return []int32{}, err } return val.([]int32), nil } // Int32SliceVar defines a int32Slice flag with specified name, default value, and usage string. // The argument p points to a []int32 variable in which to store the value of the flag. func (f *FlagSet) Int32SliceVar(p *[]int32, name string, value []int32, usage string) { f.VarP(newInt32SliceValue(value, p), name, "", usage) } // Int32SliceVarP is like Int32SliceVar, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Int32SliceVarP(p *[]int32, name, shorthand string, value []int32, usage string) { f.VarP(newInt32SliceValue(value, p), name, shorthand, usage) } // Int32SliceVar defines a int32[] flag with specified name, default value, and usage string. // The argument p points to a int32[] variable in which to store the value of the flag. func Int32SliceVar(p *[]int32, name string, value []int32, usage string) { CommandLine.VarP(newInt32SliceValue(value, p), name, "", usage) } // Int32SliceVarP is like Int32SliceVar, but accepts a shorthand letter that can be used after a single dash. func Int32SliceVarP(p *[]int32, name, shorthand string, value []int32, usage string) { CommandLine.VarP(newInt32SliceValue(value, p), name, shorthand, usage) } // Int32Slice defines a []int32 flag with specified name, default value, and usage string. // The return value is the address of a []int32 variable that stores the value of the flag. func (f *FlagSet) Int32Slice(name string, value []int32, usage string) *[]int32 { p := []int32{} f.Int32SliceVarP(&p, name, "", value, usage) return &p } // Int32SliceP is like Int32Slice, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Int32SliceP(name, shorthand string, value []int32, usage string) *[]int32 { p := []int32{} f.Int32SliceVarP(&p, name, shorthand, value, usage) return &p } // Int32Slice defines a []int32 flag with specified name, default value, and usage string. // The return value is the address of a []int32 variable that stores the value of the flag. func Int32Slice(name string, value []int32, usage string) *[]int32 { return CommandLine.Int32SliceP(name, "", value, usage) } // Int32SliceP is like Int32Slice, but accepts a shorthand letter that can be used after a single dash. func Int32SliceP(name, shorthand string, value []int32, usage string) *[]int32 { return CommandLine.Int32SliceP(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/int64.go ================================================ package pflag import "strconv" // -- int64 Value type int64Value int64 func newInt64Value(val int64, p *int64) *int64Value { *p = val return (*int64Value)(p) } func (i *int64Value) Set(s string) error { v, err := strconv.ParseInt(s, 0, 64) *i = int64Value(v) return err } func (i *int64Value) Type() string { return "int64" } func (i *int64Value) String() string { return strconv.FormatInt(int64(*i), 10) } func int64Conv(sval string) (interface{}, error) { return strconv.ParseInt(sval, 0, 64) } // GetInt64 return the int64 value of a flag with the given name func (f *FlagSet) GetInt64(name string) (int64, error) { val, err := f.getFlagType(name, "int64", int64Conv) if err != nil { return 0, err } return val.(int64), nil } // Int64Var defines an int64 flag with specified name, default value, and usage string. // The argument p points to an int64 variable in which to store the value of the flag. func (f *FlagSet) Int64Var(p *int64, name string, value int64, usage string) { f.VarP(newInt64Value(value, p), name, "", usage) } // Int64VarP is like Int64Var, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Int64VarP(p *int64, name, shorthand string, value int64, usage string) { f.VarP(newInt64Value(value, p), name, shorthand, usage) } // Int64Var defines an int64 flag with specified name, default value, and usage string. // The argument p points to an int64 variable in which to store the value of the flag. func Int64Var(p *int64, name string, value int64, usage string) { CommandLine.VarP(newInt64Value(value, p), name, "", usage) } // Int64VarP is like Int64Var, but accepts a shorthand letter that can be used after a single dash. func Int64VarP(p *int64, name, shorthand string, value int64, usage string) { CommandLine.VarP(newInt64Value(value, p), name, shorthand, usage) } // Int64 defines an int64 flag with specified name, default value, and usage string. // The return value is the address of an int64 variable that stores the value of the flag. func (f *FlagSet) Int64(name string, value int64, usage string) *int64 { p := new(int64) f.Int64VarP(p, name, "", value, usage) return p } // Int64P is like Int64, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Int64P(name, shorthand string, value int64, usage string) *int64 { p := new(int64) f.Int64VarP(p, name, shorthand, value, usage) return p } // Int64 defines an int64 flag with specified name, default value, and usage string. // The return value is the address of an int64 variable that stores the value of the flag. func Int64(name string, value int64, usage string) *int64 { return CommandLine.Int64P(name, "", value, usage) } // Int64P is like Int64, but accepts a shorthand letter that can be used after a single dash. func Int64P(name, shorthand string, value int64, usage string) *int64 { return CommandLine.Int64P(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/int64_slice.go ================================================ package pflag import ( "fmt" "strconv" "strings" ) // -- int64Slice Value type int64SliceValue struct { value *[]int64 changed bool } func newInt64SliceValue(val []int64, p *[]int64) *int64SliceValue { isv := new(int64SliceValue) isv.value = p *isv.value = val return isv } func (s *int64SliceValue) Set(val string) error { ss := strings.Split(val, ",") out := make([]int64, len(ss)) for i, d := range ss { var err error out[i], err = strconv.ParseInt(d, 0, 64) if err != nil { return err } } if !s.changed { *s.value = out } else { *s.value = append(*s.value, out...) } s.changed = true return nil } func (s *int64SliceValue) Type() string { return "int64Slice" } func (s *int64SliceValue) String() string { out := make([]string, len(*s.value)) for i, d := range *s.value { out[i] = fmt.Sprintf("%d", d) } return "[" + strings.Join(out, ",") + "]" } func (s *int64SliceValue) fromString(val string) (int64, error) { return strconv.ParseInt(val, 0, 64) } func (s *int64SliceValue) toString(val int64) string { return fmt.Sprintf("%d", val) } func (s *int64SliceValue) Append(val string) error { i, err := s.fromString(val) if err != nil { return err } *s.value = append(*s.value, i) return nil } func (s *int64SliceValue) Replace(val []string) error { out := make([]int64, len(val)) for i, d := range val { var err error out[i], err = s.fromString(d) if err != nil { return err } } *s.value = out return nil } func (s *int64SliceValue) GetSlice() []string { out := make([]string, len(*s.value)) for i, d := range *s.value { out[i] = s.toString(d) } return out } func int64SliceConv(val string) (interface{}, error) { val = strings.Trim(val, "[]") // Empty string would cause a slice with one (empty) entry if len(val) == 0 { return []int64{}, nil } ss := strings.Split(val, ",") out := make([]int64, len(ss)) for i, d := range ss { var err error out[i], err = strconv.ParseInt(d, 0, 64) if err != nil { return nil, err } } return out, nil } // GetInt64Slice return the []int64 value of a flag with the given name func (f *FlagSet) GetInt64Slice(name string) ([]int64, error) { val, err := f.getFlagType(name, "int64Slice", int64SliceConv) if err != nil { return []int64{}, err } return val.([]int64), nil } // Int64SliceVar defines a int64Slice flag with specified name, default value, and usage string. // The argument p points to a []int64 variable in which to store the value of the flag. func (f *FlagSet) Int64SliceVar(p *[]int64, name string, value []int64, usage string) { f.VarP(newInt64SliceValue(value, p), name, "", usage) } // Int64SliceVarP is like Int64SliceVar, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Int64SliceVarP(p *[]int64, name, shorthand string, value []int64, usage string) { f.VarP(newInt64SliceValue(value, p), name, shorthand, usage) } // Int64SliceVar defines a int64[] flag with specified name, default value, and usage string. // The argument p points to a int64[] variable in which to store the value of the flag. func Int64SliceVar(p *[]int64, name string, value []int64, usage string) { CommandLine.VarP(newInt64SliceValue(value, p), name, "", usage) } // Int64SliceVarP is like Int64SliceVar, but accepts a shorthand letter that can be used after a single dash. func Int64SliceVarP(p *[]int64, name, shorthand string, value []int64, usage string) { CommandLine.VarP(newInt64SliceValue(value, p), name, shorthand, usage) } // Int64Slice defines a []int64 flag with specified name, default value, and usage string. // The return value is the address of a []int64 variable that stores the value of the flag. func (f *FlagSet) Int64Slice(name string, value []int64, usage string) *[]int64 { p := []int64{} f.Int64SliceVarP(&p, name, "", value, usage) return &p } // Int64SliceP is like Int64Slice, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Int64SliceP(name, shorthand string, value []int64, usage string) *[]int64 { p := []int64{} f.Int64SliceVarP(&p, name, shorthand, value, usage) return &p } // Int64Slice defines a []int64 flag with specified name, default value, and usage string. // The return value is the address of a []int64 variable that stores the value of the flag. func Int64Slice(name string, value []int64, usage string) *[]int64 { return CommandLine.Int64SliceP(name, "", value, usage) } // Int64SliceP is like Int64Slice, but accepts a shorthand letter that can be used after a single dash. func Int64SliceP(name, shorthand string, value []int64, usage string) *[]int64 { return CommandLine.Int64SliceP(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/int8.go ================================================ package pflag import "strconv" // -- int8 Value type int8Value int8 func newInt8Value(val int8, p *int8) *int8Value { *p = val return (*int8Value)(p) } func (i *int8Value) Set(s string) error { v, err := strconv.ParseInt(s, 0, 8) *i = int8Value(v) return err } func (i *int8Value) Type() string { return "int8" } func (i *int8Value) String() string { return strconv.FormatInt(int64(*i), 10) } func int8Conv(sval string) (interface{}, error) { v, err := strconv.ParseInt(sval, 0, 8) if err != nil { return 0, err } return int8(v), nil } // GetInt8 return the int8 value of a flag with the given name func (f *FlagSet) GetInt8(name string) (int8, error) { val, err := f.getFlagType(name, "int8", int8Conv) if err != nil { return 0, err } return val.(int8), nil } // Int8Var defines an int8 flag with specified name, default value, and usage string. // The argument p points to an int8 variable in which to store the value of the flag. func (f *FlagSet) Int8Var(p *int8, name string, value int8, usage string) { f.VarP(newInt8Value(value, p), name, "", usage) } // Int8VarP is like Int8Var, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Int8VarP(p *int8, name, shorthand string, value int8, usage string) { f.VarP(newInt8Value(value, p), name, shorthand, usage) } // Int8Var defines an int8 flag with specified name, default value, and usage string. // The argument p points to an int8 variable in which to store the value of the flag. func Int8Var(p *int8, name string, value int8, usage string) { CommandLine.VarP(newInt8Value(value, p), name, "", usage) } // Int8VarP is like Int8Var, but accepts a shorthand letter that can be used after a single dash. func Int8VarP(p *int8, name, shorthand string, value int8, usage string) { CommandLine.VarP(newInt8Value(value, p), name, shorthand, usage) } // Int8 defines an int8 flag with specified name, default value, and usage string. // The return value is the address of an int8 variable that stores the value of the flag. func (f *FlagSet) Int8(name string, value int8, usage string) *int8 { p := new(int8) f.Int8VarP(p, name, "", value, usage) return p } // Int8P is like Int8, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Int8P(name, shorthand string, value int8, usage string) *int8 { p := new(int8) f.Int8VarP(p, name, shorthand, value, usage) return p } // Int8 defines an int8 flag with specified name, default value, and usage string. // The return value is the address of an int8 variable that stores the value of the flag. func Int8(name string, value int8, usage string) *int8 { return CommandLine.Int8P(name, "", value, usage) } // Int8P is like Int8, but accepts a shorthand letter that can be used after a single dash. func Int8P(name, shorthand string, value int8, usage string) *int8 { return CommandLine.Int8P(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/int_slice.go ================================================ package pflag import ( "fmt" "strconv" "strings" ) // -- intSlice Value type intSliceValue struct { value *[]int changed bool } func newIntSliceValue(val []int, p *[]int) *intSliceValue { isv := new(intSliceValue) isv.value = p *isv.value = val return isv } func (s *intSliceValue) Set(val string) error { ss := strings.Split(val, ",") out := make([]int, len(ss)) for i, d := range ss { var err error out[i], err = strconv.Atoi(d) if err != nil { return err } } if !s.changed { *s.value = out } else { *s.value = append(*s.value, out...) } s.changed = true return nil } func (s *intSliceValue) Type() string { return "intSlice" } func (s *intSliceValue) String() string { out := make([]string, len(*s.value)) for i, d := range *s.value { out[i] = fmt.Sprintf("%d", d) } return "[" + strings.Join(out, ",") + "]" } func (s *intSliceValue) Append(val string) error { i, err := strconv.Atoi(val) if err != nil { return err } *s.value = append(*s.value, i) return nil } func (s *intSliceValue) Replace(val []string) error { out := make([]int, len(val)) for i, d := range val { var err error out[i], err = strconv.Atoi(d) if err != nil { return err } } *s.value = out return nil } func (s *intSliceValue) GetSlice() []string { out := make([]string, len(*s.value)) for i, d := range *s.value { out[i] = strconv.Itoa(d) } return out } func intSliceConv(val string) (interface{}, error) { val = strings.Trim(val, "[]") // Empty string would cause a slice with one (empty) entry if len(val) == 0 { return []int{}, nil } ss := strings.Split(val, ",") out := make([]int, len(ss)) for i, d := range ss { var err error out[i], err = strconv.Atoi(d) if err != nil { return nil, err } } return out, nil } // GetIntSlice return the []int value of a flag with the given name func (f *FlagSet) GetIntSlice(name string) ([]int, error) { val, err := f.getFlagType(name, "intSlice", intSliceConv) if err != nil { return []int{}, err } return val.([]int), nil } // IntSliceVar defines a intSlice flag with specified name, default value, and usage string. // The argument p points to a []int variable in which to store the value of the flag. func (f *FlagSet) IntSliceVar(p *[]int, name string, value []int, usage string) { f.VarP(newIntSliceValue(value, p), name, "", usage) } // IntSliceVarP is like IntSliceVar, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) IntSliceVarP(p *[]int, name, shorthand string, value []int, usage string) { f.VarP(newIntSliceValue(value, p), name, shorthand, usage) } // IntSliceVar defines a int[] flag with specified name, default value, and usage string. // The argument p points to a int[] variable in which to store the value of the flag. func IntSliceVar(p *[]int, name string, value []int, usage string) { CommandLine.VarP(newIntSliceValue(value, p), name, "", usage) } // IntSliceVarP is like IntSliceVar, but accepts a shorthand letter that can be used after a single dash. func IntSliceVarP(p *[]int, name, shorthand string, value []int, usage string) { CommandLine.VarP(newIntSliceValue(value, p), name, shorthand, usage) } // IntSlice defines a []int flag with specified name, default value, and usage string. // The return value is the address of a []int variable that stores the value of the flag. func (f *FlagSet) IntSlice(name string, value []int, usage string) *[]int { p := []int{} f.IntSliceVarP(&p, name, "", value, usage) return &p } // IntSliceP is like IntSlice, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) IntSliceP(name, shorthand string, value []int, usage string) *[]int { p := []int{} f.IntSliceVarP(&p, name, shorthand, value, usage) return &p } // IntSlice defines a []int flag with specified name, default value, and usage string. // The return value is the address of a []int variable that stores the value of the flag. func IntSlice(name string, value []int, usage string) *[]int { return CommandLine.IntSliceP(name, "", value, usage) } // IntSliceP is like IntSlice, but accepts a shorthand letter that can be used after a single dash. func IntSliceP(name, shorthand string, value []int, usage string) *[]int { return CommandLine.IntSliceP(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/ip.go ================================================ package pflag import ( "fmt" "net" "strings" ) // -- net.IP value type ipValue net.IP func newIPValue(val net.IP, p *net.IP) *ipValue { *p = val return (*ipValue)(p) } func (i *ipValue) String() string { return net.IP(*i).String() } func (i *ipValue) Set(s string) error { ip := net.ParseIP(strings.TrimSpace(s)) if ip == nil { return fmt.Errorf("failed to parse IP: %q", s) } *i = ipValue(ip) return nil } func (i *ipValue) Type() string { return "ip" } func ipConv(sval string) (interface{}, error) { ip := net.ParseIP(sval) if ip != nil { return ip, nil } return nil, fmt.Errorf("invalid string being converted to IP address: %s", sval) } // GetIP return the net.IP value of a flag with the given name func (f *FlagSet) GetIP(name string) (net.IP, error) { val, err := f.getFlagType(name, "ip", ipConv) if err != nil { return nil, err } return val.(net.IP), nil } // IPVar defines an net.IP flag with specified name, default value, and usage string. // The argument p points to an net.IP variable in which to store the value of the flag. func (f *FlagSet) IPVar(p *net.IP, name string, value net.IP, usage string) { f.VarP(newIPValue(value, p), name, "", usage) } // IPVarP is like IPVar, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) IPVarP(p *net.IP, name, shorthand string, value net.IP, usage string) { f.VarP(newIPValue(value, p), name, shorthand, usage) } // IPVar defines an net.IP flag with specified name, default value, and usage string. // The argument p points to an net.IP variable in which to store the value of the flag. func IPVar(p *net.IP, name string, value net.IP, usage string) { CommandLine.VarP(newIPValue(value, p), name, "", usage) } // IPVarP is like IPVar, but accepts a shorthand letter that can be used after a single dash. func IPVarP(p *net.IP, name, shorthand string, value net.IP, usage string) { CommandLine.VarP(newIPValue(value, p), name, shorthand, usage) } // IP defines an net.IP flag with specified name, default value, and usage string. // The return value is the address of an net.IP variable that stores the value of the flag. func (f *FlagSet) IP(name string, value net.IP, usage string) *net.IP { p := new(net.IP) f.IPVarP(p, name, "", value, usage) return p } // IPP is like IP, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) IPP(name, shorthand string, value net.IP, usage string) *net.IP { p := new(net.IP) f.IPVarP(p, name, shorthand, value, usage) return p } // IP defines an net.IP flag with specified name, default value, and usage string. // The return value is the address of an net.IP variable that stores the value of the flag. func IP(name string, value net.IP, usage string) *net.IP { return CommandLine.IPP(name, "", value, usage) } // IPP is like IP, but accepts a shorthand letter that can be used after a single dash. func IPP(name, shorthand string, value net.IP, usage string) *net.IP { return CommandLine.IPP(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/ip_slice.go ================================================ package pflag import ( "fmt" "io" "net" "strings" ) // -- ipSlice Value type ipSliceValue struct { value *[]net.IP changed bool } func newIPSliceValue(val []net.IP, p *[]net.IP) *ipSliceValue { ipsv := new(ipSliceValue) ipsv.value = p *ipsv.value = val return ipsv } // Set converts, and assigns, the comma-separated IP argument string representation as the []net.IP value of this flag. // If Set is called on a flag that already has a []net.IP assigned, the newly converted values will be appended. func (s *ipSliceValue) Set(val string) error { // remove all quote characters rmQuote := strings.NewReplacer(`"`, "", `'`, "", "`", "") // read flag arguments with CSV parser ipStrSlice, err := readAsCSV(rmQuote.Replace(val)) if err != nil && err != io.EOF { return err } // parse ip values into slice out := make([]net.IP, 0, len(ipStrSlice)) for _, ipStr := range ipStrSlice { ip := net.ParseIP(strings.TrimSpace(ipStr)) if ip == nil { return fmt.Errorf("invalid string being converted to IP address: %s", ipStr) } out = append(out, ip) } if !s.changed { *s.value = out } else { *s.value = append(*s.value, out...) } s.changed = true return nil } // Type returns a string that uniquely represents this flag's type. func (s *ipSliceValue) Type() string { return "ipSlice" } // String defines a "native" format for this net.IP slice flag value. func (s *ipSliceValue) String() string { ipStrSlice := make([]string, len(*s.value)) for i, ip := range *s.value { ipStrSlice[i] = ip.String() } out, _ := writeAsCSV(ipStrSlice) return "[" + out + "]" } func (s *ipSliceValue) fromString(val string) (net.IP, error) { return net.ParseIP(strings.TrimSpace(val)), nil } func (s *ipSliceValue) toString(val net.IP) string { return val.String() } func (s *ipSliceValue) Append(val string) error { i, err := s.fromString(val) if err != nil { return err } *s.value = append(*s.value, i) return nil } func (s *ipSliceValue) Replace(val []string) error { out := make([]net.IP, len(val)) for i, d := range val { var err error out[i], err = s.fromString(d) if err != nil { return err } } *s.value = out return nil } func (s *ipSliceValue) GetSlice() []string { out := make([]string, len(*s.value)) for i, d := range *s.value { out[i] = s.toString(d) } return out } func ipSliceConv(val string) (interface{}, error) { val = strings.Trim(val, "[]") // Empty string would cause a slice with one (empty) entry if len(val) == 0 { return []net.IP{}, nil } ss := strings.Split(val, ",") out := make([]net.IP, len(ss)) for i, sval := range ss { ip := net.ParseIP(strings.TrimSpace(sval)) if ip == nil { return nil, fmt.Errorf("invalid string being converted to IP address: %s", sval) } out[i] = ip } return out, nil } // GetIPSlice returns the []net.IP value of a flag with the given name func (f *FlagSet) GetIPSlice(name string) ([]net.IP, error) { val, err := f.getFlagType(name, "ipSlice", ipSliceConv) if err != nil { return []net.IP{}, err } return val.([]net.IP), nil } // IPSliceVar defines a ipSlice flag with specified name, default value, and usage string. // The argument p points to a []net.IP variable in which to store the value of the flag. func (f *FlagSet) IPSliceVar(p *[]net.IP, name string, value []net.IP, usage string) { f.VarP(newIPSliceValue(value, p), name, "", usage) } // IPSliceVarP is like IPSliceVar, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) IPSliceVarP(p *[]net.IP, name, shorthand string, value []net.IP, usage string) { f.VarP(newIPSliceValue(value, p), name, shorthand, usage) } // IPSliceVar defines a []net.IP flag with specified name, default value, and usage string. // The argument p points to a []net.IP variable in which to store the value of the flag. func IPSliceVar(p *[]net.IP, name string, value []net.IP, usage string) { CommandLine.VarP(newIPSliceValue(value, p), name, "", usage) } // IPSliceVarP is like IPSliceVar, but accepts a shorthand letter that can be used after a single dash. func IPSliceVarP(p *[]net.IP, name, shorthand string, value []net.IP, usage string) { CommandLine.VarP(newIPSliceValue(value, p), name, shorthand, usage) } // IPSlice defines a []net.IP flag with specified name, default value, and usage string. // The return value is the address of a []net.IP variable that stores the value of that flag. func (f *FlagSet) IPSlice(name string, value []net.IP, usage string) *[]net.IP { p := []net.IP{} f.IPSliceVarP(&p, name, "", value, usage) return &p } // IPSliceP is like IPSlice, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) IPSliceP(name, shorthand string, value []net.IP, usage string) *[]net.IP { p := []net.IP{} f.IPSliceVarP(&p, name, shorthand, value, usage) return &p } // IPSlice defines a []net.IP flag with specified name, default value, and usage string. // The return value is the address of a []net.IP variable that stores the value of the flag. func IPSlice(name string, value []net.IP, usage string) *[]net.IP { return CommandLine.IPSliceP(name, "", value, usage) } // IPSliceP is like IPSlice, but accepts a shorthand letter that can be used after a single dash. func IPSliceP(name, shorthand string, value []net.IP, usage string) *[]net.IP { return CommandLine.IPSliceP(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/ipmask.go ================================================ package pflag import ( "fmt" "net" "strconv" ) // -- net.IPMask value type ipMaskValue net.IPMask func newIPMaskValue(val net.IPMask, p *net.IPMask) *ipMaskValue { *p = val return (*ipMaskValue)(p) } func (i *ipMaskValue) String() string { return net.IPMask(*i).String() } func (i *ipMaskValue) Set(s string) error { ip := ParseIPv4Mask(s) if ip == nil { return fmt.Errorf("failed to parse IP mask: %q", s) } *i = ipMaskValue(ip) return nil } func (i *ipMaskValue) Type() string { return "ipMask" } // ParseIPv4Mask written in IP form (e.g. 255.255.255.0). // This function should really belong to the net package. func ParseIPv4Mask(s string) net.IPMask { mask := net.ParseIP(s) if mask == nil { if len(s) != 8 { return nil } // net.IPMask.String() actually outputs things like ffffff00 // so write a horrible parser for that as well :-( m := []int{} for i := 0; i < 4; i++ { b := "0x" + s[2*i:2*i+2] d, err := strconv.ParseInt(b, 0, 0) if err != nil { return nil } m = append(m, int(d)) } s := fmt.Sprintf("%d.%d.%d.%d", m[0], m[1], m[2], m[3]) mask = net.ParseIP(s) if mask == nil { return nil } } return net.IPv4Mask(mask[12], mask[13], mask[14], mask[15]) } func parseIPv4Mask(sval string) (interface{}, error) { mask := ParseIPv4Mask(sval) if mask == nil { return nil, fmt.Errorf("unable to parse %s as net.IPMask", sval) } return mask, nil } // GetIPv4Mask return the net.IPv4Mask value of a flag with the given name func (f *FlagSet) GetIPv4Mask(name string) (net.IPMask, error) { val, err := f.getFlagType(name, "ipMask", parseIPv4Mask) if err != nil { return nil, err } return val.(net.IPMask), nil } // IPMaskVar defines an net.IPMask flag with specified name, default value, and usage string. // The argument p points to an net.IPMask variable in which to store the value of the flag. func (f *FlagSet) IPMaskVar(p *net.IPMask, name string, value net.IPMask, usage string) { f.VarP(newIPMaskValue(value, p), name, "", usage) } // IPMaskVarP is like IPMaskVar, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) IPMaskVarP(p *net.IPMask, name, shorthand string, value net.IPMask, usage string) { f.VarP(newIPMaskValue(value, p), name, shorthand, usage) } // IPMaskVar defines an net.IPMask flag with specified name, default value, and usage string. // The argument p points to an net.IPMask variable in which to store the value of the flag. func IPMaskVar(p *net.IPMask, name string, value net.IPMask, usage string) { CommandLine.VarP(newIPMaskValue(value, p), name, "", usage) } // IPMaskVarP is like IPMaskVar, but accepts a shorthand letter that can be used after a single dash. func IPMaskVarP(p *net.IPMask, name, shorthand string, value net.IPMask, usage string) { CommandLine.VarP(newIPMaskValue(value, p), name, shorthand, usage) } // IPMask defines an net.IPMask flag with specified name, default value, and usage string. // The return value is the address of an net.IPMask variable that stores the value of the flag. func (f *FlagSet) IPMask(name string, value net.IPMask, usage string) *net.IPMask { p := new(net.IPMask) f.IPMaskVarP(p, name, "", value, usage) return p } // IPMaskP is like IPMask, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) IPMaskP(name, shorthand string, value net.IPMask, usage string) *net.IPMask { p := new(net.IPMask) f.IPMaskVarP(p, name, shorthand, value, usage) return p } // IPMask defines an net.IPMask flag with specified name, default value, and usage string. // The return value is the address of an net.IPMask variable that stores the value of the flag. func IPMask(name string, value net.IPMask, usage string) *net.IPMask { return CommandLine.IPMaskP(name, "", value, usage) } // IPMaskP is like IP, but accepts a shorthand letter that can be used after a single dash. func IPMaskP(name, shorthand string, value net.IPMask, usage string) *net.IPMask { return CommandLine.IPMaskP(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/ipnet.go ================================================ package pflag import ( "fmt" "net" "strings" ) // IPNet adapts net.IPNet for use as a flag. type ipNetValue net.IPNet func (ipnet ipNetValue) String() string { n := net.IPNet(ipnet) return n.String() } func (ipnet *ipNetValue) Set(value string) error { _, n, err := net.ParseCIDR(strings.TrimSpace(value)) if err != nil { return err } *ipnet = ipNetValue(*n) return nil } func (*ipNetValue) Type() string { return "ipNet" } func newIPNetValue(val net.IPNet, p *net.IPNet) *ipNetValue { *p = val return (*ipNetValue)(p) } func ipNetConv(sval string) (interface{}, error) { _, n, err := net.ParseCIDR(strings.TrimSpace(sval)) if err == nil { return *n, nil } return nil, fmt.Errorf("invalid string being converted to IPNet: %s", sval) } // GetIPNet return the net.IPNet value of a flag with the given name func (f *FlagSet) GetIPNet(name string) (net.IPNet, error) { val, err := f.getFlagType(name, "ipNet", ipNetConv) if err != nil { return net.IPNet{}, err } return val.(net.IPNet), nil } // IPNetVar defines an net.IPNet flag with specified name, default value, and usage string. // The argument p points to an net.IPNet variable in which to store the value of the flag. func (f *FlagSet) IPNetVar(p *net.IPNet, name string, value net.IPNet, usage string) { f.VarP(newIPNetValue(value, p), name, "", usage) } // IPNetVarP is like IPNetVar, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) IPNetVarP(p *net.IPNet, name, shorthand string, value net.IPNet, usage string) { f.VarP(newIPNetValue(value, p), name, shorthand, usage) } // IPNetVar defines an net.IPNet flag with specified name, default value, and usage string. // The argument p points to an net.IPNet variable in which to store the value of the flag. func IPNetVar(p *net.IPNet, name string, value net.IPNet, usage string) { CommandLine.VarP(newIPNetValue(value, p), name, "", usage) } // IPNetVarP is like IPNetVar, but accepts a shorthand letter that can be used after a single dash. func IPNetVarP(p *net.IPNet, name, shorthand string, value net.IPNet, usage string) { CommandLine.VarP(newIPNetValue(value, p), name, shorthand, usage) } // IPNet defines an net.IPNet flag with specified name, default value, and usage string. // The return value is the address of an net.IPNet variable that stores the value of the flag. func (f *FlagSet) IPNet(name string, value net.IPNet, usage string) *net.IPNet { p := new(net.IPNet) f.IPNetVarP(p, name, "", value, usage) return p } // IPNetP is like IPNet, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) IPNetP(name, shorthand string, value net.IPNet, usage string) *net.IPNet { p := new(net.IPNet) f.IPNetVarP(p, name, shorthand, value, usage) return p } // IPNet defines an net.IPNet flag with specified name, default value, and usage string. // The return value is the address of an net.IPNet variable that stores the value of the flag. func IPNet(name string, value net.IPNet, usage string) *net.IPNet { return CommandLine.IPNetP(name, "", value, usage) } // IPNetP is like IPNet, but accepts a shorthand letter that can be used after a single dash. func IPNetP(name, shorthand string, value net.IPNet, usage string) *net.IPNet { return CommandLine.IPNetP(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/string.go ================================================ package pflag // -- string Value type stringValue string func newStringValue(val string, p *string) *stringValue { *p = val return (*stringValue)(p) } func (s *stringValue) Set(val string) error { *s = stringValue(val) return nil } func (s *stringValue) Type() string { return "string" } func (s *stringValue) String() string { return string(*s) } func stringConv(sval string) (interface{}, error) { return sval, nil } // GetString return the string value of a flag with the given name func (f *FlagSet) GetString(name string) (string, error) { val, err := f.getFlagType(name, "string", stringConv) if err != nil { return "", err } return val.(string), nil } // StringVar defines a string flag with specified name, default value, and usage string. // The argument p points to a string variable in which to store the value of the flag. func (f *FlagSet) StringVar(p *string, name string, value string, usage string) { f.VarP(newStringValue(value, p), name, "", usage) } // StringVarP is like StringVar, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) StringVarP(p *string, name, shorthand string, value string, usage string) { f.VarP(newStringValue(value, p), name, shorthand, usage) } // StringVar defines a string flag with specified name, default value, and usage string. // The argument p points to a string variable in which to store the value of the flag. func StringVar(p *string, name string, value string, usage string) { CommandLine.VarP(newStringValue(value, p), name, "", usage) } // StringVarP is like StringVar, but accepts a shorthand letter that can be used after a single dash. func StringVarP(p *string, name, shorthand string, value string, usage string) { CommandLine.VarP(newStringValue(value, p), name, shorthand, usage) } // String defines a string flag with specified name, default value, and usage string. // The return value is the address of a string variable that stores the value of the flag. func (f *FlagSet) String(name string, value string, usage string) *string { p := new(string) f.StringVarP(p, name, "", value, usage) return p } // StringP is like String, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) StringP(name, shorthand string, value string, usage string) *string { p := new(string) f.StringVarP(p, name, shorthand, value, usage) return p } // String defines a string flag with specified name, default value, and usage string. // The return value is the address of a string variable that stores the value of the flag. func String(name string, value string, usage string) *string { return CommandLine.StringP(name, "", value, usage) } // StringP is like String, but accepts a shorthand letter that can be used after a single dash. func StringP(name, shorthand string, value string, usage string) *string { return CommandLine.StringP(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/string_array.go ================================================ package pflag // -- stringArray Value type stringArrayValue struct { value *[]string changed bool } func newStringArrayValue(val []string, p *[]string) *stringArrayValue { ssv := new(stringArrayValue) ssv.value = p *ssv.value = val return ssv } func (s *stringArrayValue) Set(val string) error { if !s.changed { *s.value = []string{val} s.changed = true } else { *s.value = append(*s.value, val) } return nil } func (s *stringArrayValue) Append(val string) error { *s.value = append(*s.value, val) return nil } func (s *stringArrayValue) Replace(val []string) error { out := make([]string, len(val)) for i, d := range val { var err error out[i] = d if err != nil { return err } } *s.value = out return nil } func (s *stringArrayValue) GetSlice() []string { out := make([]string, len(*s.value)) for i, d := range *s.value { out[i] = d } return out } func (s *stringArrayValue) Type() string { return "stringArray" } func (s *stringArrayValue) String() string { str, _ := writeAsCSV(*s.value) return "[" + str + "]" } func stringArrayConv(sval string) (interface{}, error) { sval = sval[1 : len(sval)-1] // An empty string would cause a array with one (empty) string if len(sval) == 0 { return []string{}, nil } return readAsCSV(sval) } // GetStringArray return the []string value of a flag with the given name func (f *FlagSet) GetStringArray(name string) ([]string, error) { val, err := f.getFlagType(name, "stringArray", stringArrayConv) if err != nil { return []string{}, err } return val.([]string), nil } // StringArrayVar defines a string flag with specified name, default value, and usage string. // The argument p points to a []string variable in which to store the values of the multiple flags. // The value of each argument will not try to be separated by comma. Use a StringSlice for that. func (f *FlagSet) StringArrayVar(p *[]string, name string, value []string, usage string) { f.VarP(newStringArrayValue(value, p), name, "", usage) } // StringArrayVarP is like StringArrayVar, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) StringArrayVarP(p *[]string, name, shorthand string, value []string, usage string) { f.VarP(newStringArrayValue(value, p), name, shorthand, usage) } // StringArrayVar defines a string flag with specified name, default value, and usage string. // The argument p points to a []string variable in which to store the value of the flag. // The value of each argument will not try to be separated by comma. Use a StringSlice for that. func StringArrayVar(p *[]string, name string, value []string, usage string) { CommandLine.VarP(newStringArrayValue(value, p), name, "", usage) } // StringArrayVarP is like StringArrayVar, but accepts a shorthand letter that can be used after a single dash. func StringArrayVarP(p *[]string, name, shorthand string, value []string, usage string) { CommandLine.VarP(newStringArrayValue(value, p), name, shorthand, usage) } // StringArray defines a string flag with specified name, default value, and usage string. // The return value is the address of a []string variable that stores the value of the flag. // The value of each argument will not try to be separated by comma. Use a StringSlice for that. func (f *FlagSet) StringArray(name string, value []string, usage string) *[]string { p := []string{} f.StringArrayVarP(&p, name, "", value, usage) return &p } // StringArrayP is like StringArray, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) StringArrayP(name, shorthand string, value []string, usage string) *[]string { p := []string{} f.StringArrayVarP(&p, name, shorthand, value, usage) return &p } // StringArray defines a string flag with specified name, default value, and usage string. // The return value is the address of a []string variable that stores the value of the flag. // The value of each argument will not try to be separated by comma. Use a StringSlice for that. func StringArray(name string, value []string, usage string) *[]string { return CommandLine.StringArrayP(name, "", value, usage) } // StringArrayP is like StringArray, but accepts a shorthand letter that can be used after a single dash. func StringArrayP(name, shorthand string, value []string, usage string) *[]string { return CommandLine.StringArrayP(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/string_slice.go ================================================ package pflag import ( "bytes" "encoding/csv" "strings" ) // -- stringSlice Value type stringSliceValue struct { value *[]string changed bool } func newStringSliceValue(val []string, p *[]string) *stringSliceValue { ssv := new(stringSliceValue) ssv.value = p *ssv.value = val return ssv } func readAsCSV(val string) ([]string, error) { if val == "" { return []string{}, nil } stringReader := strings.NewReader(val) csvReader := csv.NewReader(stringReader) return csvReader.Read() } func writeAsCSV(vals []string) (string, error) { b := &bytes.Buffer{} w := csv.NewWriter(b) err := w.Write(vals) if err != nil { return "", err } w.Flush() return strings.TrimSuffix(b.String(), "\n"), nil } func (s *stringSliceValue) Set(val string) error { v, err := readAsCSV(val) if err != nil { return err } if !s.changed { *s.value = v } else { *s.value = append(*s.value, v...) } s.changed = true return nil } func (s *stringSliceValue) Type() string { return "stringSlice" } func (s *stringSliceValue) String() string { str, _ := writeAsCSV(*s.value) return "[" + str + "]" } func (s *stringSliceValue) Append(val string) error { *s.value = append(*s.value, val) return nil } func (s *stringSliceValue) Replace(val []string) error { *s.value = val return nil } func (s *stringSliceValue) GetSlice() []string { return *s.value } func stringSliceConv(sval string) (interface{}, error) { sval = sval[1 : len(sval)-1] // An empty string would cause a slice with one (empty) string if len(sval) == 0 { return []string{}, nil } return readAsCSV(sval) } // GetStringSlice return the []string value of a flag with the given name func (f *FlagSet) GetStringSlice(name string) ([]string, error) { val, err := f.getFlagType(name, "stringSlice", stringSliceConv) if err != nil { return []string{}, err } return val.([]string), nil } // StringSliceVar defines a string flag with specified name, default value, and usage string. // The argument p points to a []string variable in which to store the value of the flag. // Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly. // For example: // --ss="v1,v2" --ss="v3" // will result in // []string{"v1", "v2", "v3"} func (f *FlagSet) StringSliceVar(p *[]string, name string, value []string, usage string) { f.VarP(newStringSliceValue(value, p), name, "", usage) } // StringSliceVarP is like StringSliceVar, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) StringSliceVarP(p *[]string, name, shorthand string, value []string, usage string) { f.VarP(newStringSliceValue(value, p), name, shorthand, usage) } // StringSliceVar defines a string flag with specified name, default value, and usage string. // The argument p points to a []string variable in which to store the value of the flag. // Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly. // For example: // --ss="v1,v2" --ss="v3" // will result in // []string{"v1", "v2", "v3"} func StringSliceVar(p *[]string, name string, value []string, usage string) { CommandLine.VarP(newStringSliceValue(value, p), name, "", usage) } // StringSliceVarP is like StringSliceVar, but accepts a shorthand letter that can be used after a single dash. func StringSliceVarP(p *[]string, name, shorthand string, value []string, usage string) { CommandLine.VarP(newStringSliceValue(value, p), name, shorthand, usage) } // StringSlice defines a string flag with specified name, default value, and usage string. // The return value is the address of a []string variable that stores the value of the flag. // Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly. // For example: // --ss="v1,v2" --ss="v3" // will result in // []string{"v1", "v2", "v3"} func (f *FlagSet) StringSlice(name string, value []string, usage string) *[]string { p := []string{} f.StringSliceVarP(&p, name, "", value, usage) return &p } // StringSliceP is like StringSlice, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) StringSliceP(name, shorthand string, value []string, usage string) *[]string { p := []string{} f.StringSliceVarP(&p, name, shorthand, value, usage) return &p } // StringSlice defines a string flag with specified name, default value, and usage string. // The return value is the address of a []string variable that stores the value of the flag. // Compared to StringArray flags, StringSlice flags take comma-separated value as arguments and split them accordingly. // For example: // --ss="v1,v2" --ss="v3" // will result in // []string{"v1", "v2", "v3"} func StringSlice(name string, value []string, usage string) *[]string { return CommandLine.StringSliceP(name, "", value, usage) } // StringSliceP is like StringSlice, but accepts a shorthand letter that can be used after a single dash. func StringSliceP(name, shorthand string, value []string, usage string) *[]string { return CommandLine.StringSliceP(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/string_to_int.go ================================================ package pflag import ( "bytes" "fmt" "strconv" "strings" ) // -- stringToInt Value type stringToIntValue struct { value *map[string]int changed bool } func newStringToIntValue(val map[string]int, p *map[string]int) *stringToIntValue { ssv := new(stringToIntValue) ssv.value = p *ssv.value = val return ssv } // Format: a=1,b=2 func (s *stringToIntValue) Set(val string) error { ss := strings.Split(val, ",") out := make(map[string]int, len(ss)) for _, pair := range ss { kv := strings.SplitN(pair, "=", 2) if len(kv) != 2 { return fmt.Errorf("%s must be formatted as key=value", pair) } var err error out[kv[0]], err = strconv.Atoi(kv[1]) if err != nil { return err } } if !s.changed { *s.value = out } else { for k, v := range out { (*s.value)[k] = v } } s.changed = true return nil } func (s *stringToIntValue) Type() string { return "stringToInt" } func (s *stringToIntValue) String() string { var buf bytes.Buffer i := 0 for k, v := range *s.value { if i > 0 { buf.WriteRune(',') } buf.WriteString(k) buf.WriteRune('=') buf.WriteString(strconv.Itoa(v)) i++ } return "[" + buf.String() + "]" } func stringToIntConv(val string) (interface{}, error) { val = strings.Trim(val, "[]") // An empty string would cause an empty map if len(val) == 0 { return map[string]int{}, nil } ss := strings.Split(val, ",") out := make(map[string]int, len(ss)) for _, pair := range ss { kv := strings.SplitN(pair, "=", 2) if len(kv) != 2 { return nil, fmt.Errorf("%s must be formatted as key=value", pair) } var err error out[kv[0]], err = strconv.Atoi(kv[1]) if err != nil { return nil, err } } return out, nil } // GetStringToInt return the map[string]int value of a flag with the given name func (f *FlagSet) GetStringToInt(name string) (map[string]int, error) { val, err := f.getFlagType(name, "stringToInt", stringToIntConv) if err != nil { return map[string]int{}, err } return val.(map[string]int), nil } // StringToIntVar defines a string flag with specified name, default value, and usage string. // The argument p points to a map[string]int variable in which to store the values of the multiple flags. // The value of each argument will not try to be separated by comma func (f *FlagSet) StringToIntVar(p *map[string]int, name string, value map[string]int, usage string) { f.VarP(newStringToIntValue(value, p), name, "", usage) } // StringToIntVarP is like StringToIntVar, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) StringToIntVarP(p *map[string]int, name, shorthand string, value map[string]int, usage string) { f.VarP(newStringToIntValue(value, p), name, shorthand, usage) } // StringToIntVar defines a string flag with specified name, default value, and usage string. // The argument p points to a map[string]int variable in which to store the value of the flag. // The value of each argument will not try to be separated by comma func StringToIntVar(p *map[string]int, name string, value map[string]int, usage string) { CommandLine.VarP(newStringToIntValue(value, p), name, "", usage) } // StringToIntVarP is like StringToIntVar, but accepts a shorthand letter that can be used after a single dash. func StringToIntVarP(p *map[string]int, name, shorthand string, value map[string]int, usage string) { CommandLine.VarP(newStringToIntValue(value, p), name, shorthand, usage) } // StringToInt defines a string flag with specified name, default value, and usage string. // The return value is the address of a map[string]int variable that stores the value of the flag. // The value of each argument will not try to be separated by comma func (f *FlagSet) StringToInt(name string, value map[string]int, usage string) *map[string]int { p := map[string]int{} f.StringToIntVarP(&p, name, "", value, usage) return &p } // StringToIntP is like StringToInt, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) StringToIntP(name, shorthand string, value map[string]int, usage string) *map[string]int { p := map[string]int{} f.StringToIntVarP(&p, name, shorthand, value, usage) return &p } // StringToInt defines a string flag with specified name, default value, and usage string. // The return value is the address of a map[string]int variable that stores the value of the flag. // The value of each argument will not try to be separated by comma func StringToInt(name string, value map[string]int, usage string) *map[string]int { return CommandLine.StringToIntP(name, "", value, usage) } // StringToIntP is like StringToInt, but accepts a shorthand letter that can be used after a single dash. func StringToIntP(name, shorthand string, value map[string]int, usage string) *map[string]int { return CommandLine.StringToIntP(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/string_to_int64.go ================================================ package pflag import ( "bytes" "fmt" "strconv" "strings" ) // -- stringToInt64 Value type stringToInt64Value struct { value *map[string]int64 changed bool } func newStringToInt64Value(val map[string]int64, p *map[string]int64) *stringToInt64Value { ssv := new(stringToInt64Value) ssv.value = p *ssv.value = val return ssv } // Format: a=1,b=2 func (s *stringToInt64Value) Set(val string) error { ss := strings.Split(val, ",") out := make(map[string]int64, len(ss)) for _, pair := range ss { kv := strings.SplitN(pair, "=", 2) if len(kv) != 2 { return fmt.Errorf("%s must be formatted as key=value", pair) } var err error out[kv[0]], err = strconv.ParseInt(kv[1], 10, 64) if err != nil { return err } } if !s.changed { *s.value = out } else { for k, v := range out { (*s.value)[k] = v } } s.changed = true return nil } func (s *stringToInt64Value) Type() string { return "stringToInt64" } func (s *stringToInt64Value) String() string { var buf bytes.Buffer i := 0 for k, v := range *s.value { if i > 0 { buf.WriteRune(',') } buf.WriteString(k) buf.WriteRune('=') buf.WriteString(strconv.FormatInt(v, 10)) i++ } return "[" + buf.String() + "]" } func stringToInt64Conv(val string) (interface{}, error) { val = strings.Trim(val, "[]") // An empty string would cause an empty map if len(val) == 0 { return map[string]int64{}, nil } ss := strings.Split(val, ",") out := make(map[string]int64, len(ss)) for _, pair := range ss { kv := strings.SplitN(pair, "=", 2) if len(kv) != 2 { return nil, fmt.Errorf("%s must be formatted as key=value", pair) } var err error out[kv[0]], err = strconv.ParseInt(kv[1], 10, 64) if err != nil { return nil, err } } return out, nil } // GetStringToInt64 return the map[string]int64 value of a flag with the given name func (f *FlagSet) GetStringToInt64(name string) (map[string]int64, error) { val, err := f.getFlagType(name, "stringToInt64", stringToInt64Conv) if err != nil { return map[string]int64{}, err } return val.(map[string]int64), nil } // StringToInt64Var defines a string flag with specified name, default value, and usage string. // The argument p point64s to a map[string]int64 variable in which to store the values of the multiple flags. // The value of each argument will not try to be separated by comma func (f *FlagSet) StringToInt64Var(p *map[string]int64, name string, value map[string]int64, usage string) { f.VarP(newStringToInt64Value(value, p), name, "", usage) } // StringToInt64VarP is like StringToInt64Var, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) StringToInt64VarP(p *map[string]int64, name, shorthand string, value map[string]int64, usage string) { f.VarP(newStringToInt64Value(value, p), name, shorthand, usage) } // StringToInt64Var defines a string flag with specified name, default value, and usage string. // The argument p point64s to a map[string]int64 variable in which to store the value of the flag. // The value of each argument will not try to be separated by comma func StringToInt64Var(p *map[string]int64, name string, value map[string]int64, usage string) { CommandLine.VarP(newStringToInt64Value(value, p), name, "", usage) } // StringToInt64VarP is like StringToInt64Var, but accepts a shorthand letter that can be used after a single dash. func StringToInt64VarP(p *map[string]int64, name, shorthand string, value map[string]int64, usage string) { CommandLine.VarP(newStringToInt64Value(value, p), name, shorthand, usage) } // StringToInt64 defines a string flag with specified name, default value, and usage string. // The return value is the address of a map[string]int64 variable that stores the value of the flag. // The value of each argument will not try to be separated by comma func (f *FlagSet) StringToInt64(name string, value map[string]int64, usage string) *map[string]int64 { p := map[string]int64{} f.StringToInt64VarP(&p, name, "", value, usage) return &p } // StringToInt64P is like StringToInt64, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) StringToInt64P(name, shorthand string, value map[string]int64, usage string) *map[string]int64 { p := map[string]int64{} f.StringToInt64VarP(&p, name, shorthand, value, usage) return &p } // StringToInt64 defines a string flag with specified name, default value, and usage string. // The return value is the address of a map[string]int64 variable that stores the value of the flag. // The value of each argument will not try to be separated by comma func StringToInt64(name string, value map[string]int64, usage string) *map[string]int64 { return CommandLine.StringToInt64P(name, "", value, usage) } // StringToInt64P is like StringToInt64, but accepts a shorthand letter that can be used after a single dash. func StringToInt64P(name, shorthand string, value map[string]int64, usage string) *map[string]int64 { return CommandLine.StringToInt64P(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/string_to_string.go ================================================ package pflag import ( "bytes" "encoding/csv" "fmt" "strings" ) // -- stringToString Value type stringToStringValue struct { value *map[string]string changed bool } func newStringToStringValue(val map[string]string, p *map[string]string) *stringToStringValue { ssv := new(stringToStringValue) ssv.value = p *ssv.value = val return ssv } // Format: a=1,b=2 func (s *stringToStringValue) Set(val string) error { var ss []string n := strings.Count(val, "=") switch n { case 0: return fmt.Errorf("%s must be formatted as key=value", val) case 1: ss = append(ss, strings.Trim(val, `"`)) default: r := csv.NewReader(strings.NewReader(val)) var err error ss, err = r.Read() if err != nil { return err } } out := make(map[string]string, len(ss)) for _, pair := range ss { kv := strings.SplitN(pair, "=", 2) if len(kv) != 2 { return fmt.Errorf("%s must be formatted as key=value", pair) } out[kv[0]] = kv[1] } if !s.changed { *s.value = out } else { for k, v := range out { (*s.value)[k] = v } } s.changed = true return nil } func (s *stringToStringValue) Type() string { return "stringToString" } func (s *stringToStringValue) String() string { records := make([]string, 0, len(*s.value)>>1) for k, v := range *s.value { records = append(records, k+"="+v) } var buf bytes.Buffer w := csv.NewWriter(&buf) if err := w.Write(records); err != nil { panic(err) } w.Flush() return "[" + strings.TrimSpace(buf.String()) + "]" } func stringToStringConv(val string) (interface{}, error) { val = strings.Trim(val, "[]") // An empty string would cause an empty map if len(val) == 0 { return map[string]string{}, nil } r := csv.NewReader(strings.NewReader(val)) ss, err := r.Read() if err != nil { return nil, err } out := make(map[string]string, len(ss)) for _, pair := range ss { kv := strings.SplitN(pair, "=", 2) if len(kv) != 2 { return nil, fmt.Errorf("%s must be formatted as key=value", pair) } out[kv[0]] = kv[1] } return out, nil } // GetStringToString return the map[string]string value of a flag with the given name func (f *FlagSet) GetStringToString(name string) (map[string]string, error) { val, err := f.getFlagType(name, "stringToString", stringToStringConv) if err != nil { return map[string]string{}, err } return val.(map[string]string), nil } // StringToStringVar defines a string flag with specified name, default value, and usage string. // The argument p points to a map[string]string variable in which to store the values of the multiple flags. // The value of each argument will not try to be separated by comma func (f *FlagSet) StringToStringVar(p *map[string]string, name string, value map[string]string, usage string) { f.VarP(newStringToStringValue(value, p), name, "", usage) } // StringToStringVarP is like StringToStringVar, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) StringToStringVarP(p *map[string]string, name, shorthand string, value map[string]string, usage string) { f.VarP(newStringToStringValue(value, p), name, shorthand, usage) } // StringToStringVar defines a string flag with specified name, default value, and usage string. // The argument p points to a map[string]string variable in which to store the value of the flag. // The value of each argument will not try to be separated by comma func StringToStringVar(p *map[string]string, name string, value map[string]string, usage string) { CommandLine.VarP(newStringToStringValue(value, p), name, "", usage) } // StringToStringVarP is like StringToStringVar, but accepts a shorthand letter that can be used after a single dash. func StringToStringVarP(p *map[string]string, name, shorthand string, value map[string]string, usage string) { CommandLine.VarP(newStringToStringValue(value, p), name, shorthand, usage) } // StringToString defines a string flag with specified name, default value, and usage string. // The return value is the address of a map[string]string variable that stores the value of the flag. // The value of each argument will not try to be separated by comma func (f *FlagSet) StringToString(name string, value map[string]string, usage string) *map[string]string { p := map[string]string{} f.StringToStringVarP(&p, name, "", value, usage) return &p } // StringToStringP is like StringToString, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) StringToStringP(name, shorthand string, value map[string]string, usage string) *map[string]string { p := map[string]string{} f.StringToStringVarP(&p, name, shorthand, value, usage) return &p } // StringToString defines a string flag with specified name, default value, and usage string. // The return value is the address of a map[string]string variable that stores the value of the flag. // The value of each argument will not try to be separated by comma func StringToString(name string, value map[string]string, usage string) *map[string]string { return CommandLine.StringToStringP(name, "", value, usage) } // StringToStringP is like StringToString, but accepts a shorthand letter that can be used after a single dash. func StringToStringP(name, shorthand string, value map[string]string, usage string) *map[string]string { return CommandLine.StringToStringP(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/uint.go ================================================ package pflag import "strconv" // -- uint Value type uintValue uint func newUintValue(val uint, p *uint) *uintValue { *p = val return (*uintValue)(p) } func (i *uintValue) Set(s string) error { v, err := strconv.ParseUint(s, 0, 64) *i = uintValue(v) return err } func (i *uintValue) Type() string { return "uint" } func (i *uintValue) String() string { return strconv.FormatUint(uint64(*i), 10) } func uintConv(sval string) (interface{}, error) { v, err := strconv.ParseUint(sval, 0, 0) if err != nil { return 0, err } return uint(v), nil } // GetUint return the uint value of a flag with the given name func (f *FlagSet) GetUint(name string) (uint, error) { val, err := f.getFlagType(name, "uint", uintConv) if err != nil { return 0, err } return val.(uint), nil } // UintVar defines a uint flag with specified name, default value, and usage string. // The argument p points to a uint variable in which to store the value of the flag. func (f *FlagSet) UintVar(p *uint, name string, value uint, usage string) { f.VarP(newUintValue(value, p), name, "", usage) } // UintVarP is like UintVar, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) UintVarP(p *uint, name, shorthand string, value uint, usage string) { f.VarP(newUintValue(value, p), name, shorthand, usage) } // UintVar defines a uint flag with specified name, default value, and usage string. // The argument p points to a uint variable in which to store the value of the flag. func UintVar(p *uint, name string, value uint, usage string) { CommandLine.VarP(newUintValue(value, p), name, "", usage) } // UintVarP is like UintVar, but accepts a shorthand letter that can be used after a single dash. func UintVarP(p *uint, name, shorthand string, value uint, usage string) { CommandLine.VarP(newUintValue(value, p), name, shorthand, usage) } // Uint defines a uint flag with specified name, default value, and usage string. // The return value is the address of a uint variable that stores the value of the flag. func (f *FlagSet) Uint(name string, value uint, usage string) *uint { p := new(uint) f.UintVarP(p, name, "", value, usage) return p } // UintP is like Uint, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) UintP(name, shorthand string, value uint, usage string) *uint { p := new(uint) f.UintVarP(p, name, shorthand, value, usage) return p } // Uint defines a uint flag with specified name, default value, and usage string. // The return value is the address of a uint variable that stores the value of the flag. func Uint(name string, value uint, usage string) *uint { return CommandLine.UintP(name, "", value, usage) } // UintP is like Uint, but accepts a shorthand letter that can be used after a single dash. func UintP(name, shorthand string, value uint, usage string) *uint { return CommandLine.UintP(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/uint16.go ================================================ package pflag import "strconv" // -- uint16 value type uint16Value uint16 func newUint16Value(val uint16, p *uint16) *uint16Value { *p = val return (*uint16Value)(p) } func (i *uint16Value) Set(s string) error { v, err := strconv.ParseUint(s, 0, 16) *i = uint16Value(v) return err } func (i *uint16Value) Type() string { return "uint16" } func (i *uint16Value) String() string { return strconv.FormatUint(uint64(*i), 10) } func uint16Conv(sval string) (interface{}, error) { v, err := strconv.ParseUint(sval, 0, 16) if err != nil { return 0, err } return uint16(v), nil } // GetUint16 return the uint16 value of a flag with the given name func (f *FlagSet) GetUint16(name string) (uint16, error) { val, err := f.getFlagType(name, "uint16", uint16Conv) if err != nil { return 0, err } return val.(uint16), nil } // Uint16Var defines a uint flag with specified name, default value, and usage string. // The argument p points to a uint variable in which to store the value of the flag. func (f *FlagSet) Uint16Var(p *uint16, name string, value uint16, usage string) { f.VarP(newUint16Value(value, p), name, "", usage) } // Uint16VarP is like Uint16Var, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Uint16VarP(p *uint16, name, shorthand string, value uint16, usage string) { f.VarP(newUint16Value(value, p), name, shorthand, usage) } // Uint16Var defines a uint flag with specified name, default value, and usage string. // The argument p points to a uint variable in which to store the value of the flag. func Uint16Var(p *uint16, name string, value uint16, usage string) { CommandLine.VarP(newUint16Value(value, p), name, "", usage) } // Uint16VarP is like Uint16Var, but accepts a shorthand letter that can be used after a single dash. func Uint16VarP(p *uint16, name, shorthand string, value uint16, usage string) { CommandLine.VarP(newUint16Value(value, p), name, shorthand, usage) } // Uint16 defines a uint flag with specified name, default value, and usage string. // The return value is the address of a uint variable that stores the value of the flag. func (f *FlagSet) Uint16(name string, value uint16, usage string) *uint16 { p := new(uint16) f.Uint16VarP(p, name, "", value, usage) return p } // Uint16P is like Uint16, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Uint16P(name, shorthand string, value uint16, usage string) *uint16 { p := new(uint16) f.Uint16VarP(p, name, shorthand, value, usage) return p } // Uint16 defines a uint flag with specified name, default value, and usage string. // The return value is the address of a uint variable that stores the value of the flag. func Uint16(name string, value uint16, usage string) *uint16 { return CommandLine.Uint16P(name, "", value, usage) } // Uint16P is like Uint16, but accepts a shorthand letter that can be used after a single dash. func Uint16P(name, shorthand string, value uint16, usage string) *uint16 { return CommandLine.Uint16P(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/uint32.go ================================================ package pflag import "strconv" // -- uint32 value type uint32Value uint32 func newUint32Value(val uint32, p *uint32) *uint32Value { *p = val return (*uint32Value)(p) } func (i *uint32Value) Set(s string) error { v, err := strconv.ParseUint(s, 0, 32) *i = uint32Value(v) return err } func (i *uint32Value) Type() string { return "uint32" } func (i *uint32Value) String() string { return strconv.FormatUint(uint64(*i), 10) } func uint32Conv(sval string) (interface{}, error) { v, err := strconv.ParseUint(sval, 0, 32) if err != nil { return 0, err } return uint32(v), nil } // GetUint32 return the uint32 value of a flag with the given name func (f *FlagSet) GetUint32(name string) (uint32, error) { val, err := f.getFlagType(name, "uint32", uint32Conv) if err != nil { return 0, err } return val.(uint32), nil } // Uint32Var defines a uint32 flag with specified name, default value, and usage string. // The argument p points to a uint32 variable in which to store the value of the flag. func (f *FlagSet) Uint32Var(p *uint32, name string, value uint32, usage string) { f.VarP(newUint32Value(value, p), name, "", usage) } // Uint32VarP is like Uint32Var, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Uint32VarP(p *uint32, name, shorthand string, value uint32, usage string) { f.VarP(newUint32Value(value, p), name, shorthand, usage) } // Uint32Var defines a uint32 flag with specified name, default value, and usage string. // The argument p points to a uint32 variable in which to store the value of the flag. func Uint32Var(p *uint32, name string, value uint32, usage string) { CommandLine.VarP(newUint32Value(value, p), name, "", usage) } // Uint32VarP is like Uint32Var, but accepts a shorthand letter that can be used after a single dash. func Uint32VarP(p *uint32, name, shorthand string, value uint32, usage string) { CommandLine.VarP(newUint32Value(value, p), name, shorthand, usage) } // Uint32 defines a uint32 flag with specified name, default value, and usage string. // The return value is the address of a uint32 variable that stores the value of the flag. func (f *FlagSet) Uint32(name string, value uint32, usage string) *uint32 { p := new(uint32) f.Uint32VarP(p, name, "", value, usage) return p } // Uint32P is like Uint32, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Uint32P(name, shorthand string, value uint32, usage string) *uint32 { p := new(uint32) f.Uint32VarP(p, name, shorthand, value, usage) return p } // Uint32 defines a uint32 flag with specified name, default value, and usage string. // The return value is the address of a uint32 variable that stores the value of the flag. func Uint32(name string, value uint32, usage string) *uint32 { return CommandLine.Uint32P(name, "", value, usage) } // Uint32P is like Uint32, but accepts a shorthand letter that can be used after a single dash. func Uint32P(name, shorthand string, value uint32, usage string) *uint32 { return CommandLine.Uint32P(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/uint64.go ================================================ package pflag import "strconv" // -- uint64 Value type uint64Value uint64 func newUint64Value(val uint64, p *uint64) *uint64Value { *p = val return (*uint64Value)(p) } func (i *uint64Value) Set(s string) error { v, err := strconv.ParseUint(s, 0, 64) *i = uint64Value(v) return err } func (i *uint64Value) Type() string { return "uint64" } func (i *uint64Value) String() string { return strconv.FormatUint(uint64(*i), 10) } func uint64Conv(sval string) (interface{}, error) { v, err := strconv.ParseUint(sval, 0, 64) if err != nil { return 0, err } return uint64(v), nil } // GetUint64 return the uint64 value of a flag with the given name func (f *FlagSet) GetUint64(name string) (uint64, error) { val, err := f.getFlagType(name, "uint64", uint64Conv) if err != nil { return 0, err } return val.(uint64), nil } // Uint64Var defines a uint64 flag with specified name, default value, and usage string. // The argument p points to a uint64 variable in which to store the value of the flag. func (f *FlagSet) Uint64Var(p *uint64, name string, value uint64, usage string) { f.VarP(newUint64Value(value, p), name, "", usage) } // Uint64VarP is like Uint64Var, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Uint64VarP(p *uint64, name, shorthand string, value uint64, usage string) { f.VarP(newUint64Value(value, p), name, shorthand, usage) } // Uint64Var defines a uint64 flag with specified name, default value, and usage string. // The argument p points to a uint64 variable in which to store the value of the flag. func Uint64Var(p *uint64, name string, value uint64, usage string) { CommandLine.VarP(newUint64Value(value, p), name, "", usage) } // Uint64VarP is like Uint64Var, but accepts a shorthand letter that can be used after a single dash. func Uint64VarP(p *uint64, name, shorthand string, value uint64, usage string) { CommandLine.VarP(newUint64Value(value, p), name, shorthand, usage) } // Uint64 defines a uint64 flag with specified name, default value, and usage string. // The return value is the address of a uint64 variable that stores the value of the flag. func (f *FlagSet) Uint64(name string, value uint64, usage string) *uint64 { p := new(uint64) f.Uint64VarP(p, name, "", value, usage) return p } // Uint64P is like Uint64, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Uint64P(name, shorthand string, value uint64, usage string) *uint64 { p := new(uint64) f.Uint64VarP(p, name, shorthand, value, usage) return p } // Uint64 defines a uint64 flag with specified name, default value, and usage string. // The return value is the address of a uint64 variable that stores the value of the flag. func Uint64(name string, value uint64, usage string) *uint64 { return CommandLine.Uint64P(name, "", value, usage) } // Uint64P is like Uint64, but accepts a shorthand letter that can be used after a single dash. func Uint64P(name, shorthand string, value uint64, usage string) *uint64 { return CommandLine.Uint64P(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/uint8.go ================================================ package pflag import "strconv" // -- uint8 Value type uint8Value uint8 func newUint8Value(val uint8, p *uint8) *uint8Value { *p = val return (*uint8Value)(p) } func (i *uint8Value) Set(s string) error { v, err := strconv.ParseUint(s, 0, 8) *i = uint8Value(v) return err } func (i *uint8Value) Type() string { return "uint8" } func (i *uint8Value) String() string { return strconv.FormatUint(uint64(*i), 10) } func uint8Conv(sval string) (interface{}, error) { v, err := strconv.ParseUint(sval, 0, 8) if err != nil { return 0, err } return uint8(v), nil } // GetUint8 return the uint8 value of a flag with the given name func (f *FlagSet) GetUint8(name string) (uint8, error) { val, err := f.getFlagType(name, "uint8", uint8Conv) if err != nil { return 0, err } return val.(uint8), nil } // Uint8Var defines a uint8 flag with specified name, default value, and usage string. // The argument p points to a uint8 variable in which to store the value of the flag. func (f *FlagSet) Uint8Var(p *uint8, name string, value uint8, usage string) { f.VarP(newUint8Value(value, p), name, "", usage) } // Uint8VarP is like Uint8Var, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Uint8VarP(p *uint8, name, shorthand string, value uint8, usage string) { f.VarP(newUint8Value(value, p), name, shorthand, usage) } // Uint8Var defines a uint8 flag with specified name, default value, and usage string. // The argument p points to a uint8 variable in which to store the value of the flag. func Uint8Var(p *uint8, name string, value uint8, usage string) { CommandLine.VarP(newUint8Value(value, p), name, "", usage) } // Uint8VarP is like Uint8Var, but accepts a shorthand letter that can be used after a single dash. func Uint8VarP(p *uint8, name, shorthand string, value uint8, usage string) { CommandLine.VarP(newUint8Value(value, p), name, shorthand, usage) } // Uint8 defines a uint8 flag with specified name, default value, and usage string. // The return value is the address of a uint8 variable that stores the value of the flag. func (f *FlagSet) Uint8(name string, value uint8, usage string) *uint8 { p := new(uint8) f.Uint8VarP(p, name, "", value, usage) return p } // Uint8P is like Uint8, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) Uint8P(name, shorthand string, value uint8, usage string) *uint8 { p := new(uint8) f.Uint8VarP(p, name, shorthand, value, usage) return p } // Uint8 defines a uint8 flag with specified name, default value, and usage string. // The return value is the address of a uint8 variable that stores the value of the flag. func Uint8(name string, value uint8, usage string) *uint8 { return CommandLine.Uint8P(name, "", value, usage) } // Uint8P is like Uint8, but accepts a shorthand letter that can be used after a single dash. func Uint8P(name, shorthand string, value uint8, usage string) *uint8 { return CommandLine.Uint8P(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/spf13/pflag/uint_slice.go ================================================ package pflag import ( "fmt" "strconv" "strings" ) // -- uintSlice Value type uintSliceValue struct { value *[]uint changed bool } func newUintSliceValue(val []uint, p *[]uint) *uintSliceValue { uisv := new(uintSliceValue) uisv.value = p *uisv.value = val return uisv } func (s *uintSliceValue) Set(val string) error { ss := strings.Split(val, ",") out := make([]uint, len(ss)) for i, d := range ss { u, err := strconv.ParseUint(d, 10, 0) if err != nil { return err } out[i] = uint(u) } if !s.changed { *s.value = out } else { *s.value = append(*s.value, out...) } s.changed = true return nil } func (s *uintSliceValue) Type() string { return "uintSlice" } func (s *uintSliceValue) String() string { out := make([]string, len(*s.value)) for i, d := range *s.value { out[i] = fmt.Sprintf("%d", d) } return "[" + strings.Join(out, ",") + "]" } func (s *uintSliceValue) fromString(val string) (uint, error) { t, err := strconv.ParseUint(val, 10, 0) if err != nil { return 0, err } return uint(t), nil } func (s *uintSliceValue) toString(val uint) string { return fmt.Sprintf("%d", val) } func (s *uintSliceValue) Append(val string) error { i, err := s.fromString(val) if err != nil { return err } *s.value = append(*s.value, i) return nil } func (s *uintSliceValue) Replace(val []string) error { out := make([]uint, len(val)) for i, d := range val { var err error out[i], err = s.fromString(d) if err != nil { return err } } *s.value = out return nil } func (s *uintSliceValue) GetSlice() []string { out := make([]string, len(*s.value)) for i, d := range *s.value { out[i] = s.toString(d) } return out } func uintSliceConv(val string) (interface{}, error) { val = strings.Trim(val, "[]") // Empty string would cause a slice with one (empty) entry if len(val) == 0 { return []uint{}, nil } ss := strings.Split(val, ",") out := make([]uint, len(ss)) for i, d := range ss { u, err := strconv.ParseUint(d, 10, 0) if err != nil { return nil, err } out[i] = uint(u) } return out, nil } // GetUintSlice returns the []uint value of a flag with the given name. func (f *FlagSet) GetUintSlice(name string) ([]uint, error) { val, err := f.getFlagType(name, "uintSlice", uintSliceConv) if err != nil { return []uint{}, err } return val.([]uint), nil } // UintSliceVar defines a uintSlice flag with specified name, default value, and usage string. // The argument p points to a []uint variable in which to store the value of the flag. func (f *FlagSet) UintSliceVar(p *[]uint, name string, value []uint, usage string) { f.VarP(newUintSliceValue(value, p), name, "", usage) } // UintSliceVarP is like UintSliceVar, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) UintSliceVarP(p *[]uint, name, shorthand string, value []uint, usage string) { f.VarP(newUintSliceValue(value, p), name, shorthand, usage) } // UintSliceVar defines a uint[] flag with specified name, default value, and usage string. // The argument p points to a uint[] variable in which to store the value of the flag. func UintSliceVar(p *[]uint, name string, value []uint, usage string) { CommandLine.VarP(newUintSliceValue(value, p), name, "", usage) } // UintSliceVarP is like the UintSliceVar, but accepts a shorthand letter that can be used after a single dash. func UintSliceVarP(p *[]uint, name, shorthand string, value []uint, usage string) { CommandLine.VarP(newUintSliceValue(value, p), name, shorthand, usage) } // UintSlice defines a []uint flag with specified name, default value, and usage string. // The return value is the address of a []uint variable that stores the value of the flag. func (f *FlagSet) UintSlice(name string, value []uint, usage string) *[]uint { p := []uint{} f.UintSliceVarP(&p, name, "", value, usage) return &p } // UintSliceP is like UintSlice, but accepts a shorthand letter that can be used after a single dash. func (f *FlagSet) UintSliceP(name, shorthand string, value []uint, usage string) *[]uint { p := []uint{} f.UintSliceVarP(&p, name, shorthand, value, usage) return &p } // UintSlice defines a []uint flag with specified name, default value, and usage string. // The return value is the address of a []uint variable that stores the value of the flag. func UintSlice(name string, value []uint, usage string) *[]uint { return CommandLine.UintSliceP(name, "", value, usage) } // UintSliceP is like UintSlice, but accepts a shorthand letter that can be used after a single dash. func UintSliceP(name, shorthand string, value []uint, usage string) *[]uint { return CommandLine.UintSliceP(name, shorthand, value, usage) } ================================================ FILE: vendor/github.com/stretchr/testify/LICENSE ================================================ MIT License Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/stretchr/testify/assert/assertion_compare.go ================================================ package assert import ( "bytes" "fmt" "reflect" "time" ) type CompareType int const ( compareLess CompareType = iota - 1 compareEqual compareGreater ) var ( intType = reflect.TypeOf(int(1)) int8Type = reflect.TypeOf(int8(1)) int16Type = reflect.TypeOf(int16(1)) int32Type = reflect.TypeOf(int32(1)) int64Type = reflect.TypeOf(int64(1)) uintType = reflect.TypeOf(uint(1)) uint8Type = reflect.TypeOf(uint8(1)) uint16Type = reflect.TypeOf(uint16(1)) uint32Type = reflect.TypeOf(uint32(1)) uint64Type = reflect.TypeOf(uint64(1)) uintptrType = reflect.TypeOf(uintptr(1)) float32Type = reflect.TypeOf(float32(1)) float64Type = reflect.TypeOf(float64(1)) stringType = reflect.TypeOf("") timeType = reflect.TypeOf(time.Time{}) bytesType = reflect.TypeOf([]byte{}) ) func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { obj1Value := reflect.ValueOf(obj1) obj2Value := reflect.ValueOf(obj2) // throughout this switch we try and avoid calling .Convert() if possible, // as this has a pretty big performance impact switch kind { case reflect.Int: { intobj1, ok := obj1.(int) if !ok { intobj1 = obj1Value.Convert(intType).Interface().(int) } intobj2, ok := obj2.(int) if !ok { intobj2 = obj2Value.Convert(intType).Interface().(int) } if intobj1 > intobj2 { return compareGreater, true } if intobj1 == intobj2 { return compareEqual, true } if intobj1 < intobj2 { return compareLess, true } } case reflect.Int8: { int8obj1, ok := obj1.(int8) if !ok { int8obj1 = obj1Value.Convert(int8Type).Interface().(int8) } int8obj2, ok := obj2.(int8) if !ok { int8obj2 = obj2Value.Convert(int8Type).Interface().(int8) } if int8obj1 > int8obj2 { return compareGreater, true } if int8obj1 == int8obj2 { return compareEqual, true } if int8obj1 < int8obj2 { return compareLess, true } } case reflect.Int16: { int16obj1, ok := obj1.(int16) if !ok { int16obj1 = obj1Value.Convert(int16Type).Interface().(int16) } int16obj2, ok := obj2.(int16) if !ok { int16obj2 = obj2Value.Convert(int16Type).Interface().(int16) } if int16obj1 > int16obj2 { return compareGreater, true } if int16obj1 == int16obj2 { return compareEqual, true } if int16obj1 < int16obj2 { return compareLess, true } } case reflect.Int32: { int32obj1, ok := obj1.(int32) if !ok { int32obj1 = obj1Value.Convert(int32Type).Interface().(int32) } int32obj2, ok := obj2.(int32) if !ok { int32obj2 = obj2Value.Convert(int32Type).Interface().(int32) } if int32obj1 > int32obj2 { return compareGreater, true } if int32obj1 == int32obj2 { return compareEqual, true } if int32obj1 < int32obj2 { return compareLess, true } } case reflect.Int64: { int64obj1, ok := obj1.(int64) if !ok { int64obj1 = obj1Value.Convert(int64Type).Interface().(int64) } int64obj2, ok := obj2.(int64) if !ok { int64obj2 = obj2Value.Convert(int64Type).Interface().(int64) } if int64obj1 > int64obj2 { return compareGreater, true } if int64obj1 == int64obj2 { return compareEqual, true } if int64obj1 < int64obj2 { return compareLess, true } } case reflect.Uint: { uintobj1, ok := obj1.(uint) if !ok { uintobj1 = obj1Value.Convert(uintType).Interface().(uint) } uintobj2, ok := obj2.(uint) if !ok { uintobj2 = obj2Value.Convert(uintType).Interface().(uint) } if uintobj1 > uintobj2 { return compareGreater, true } if uintobj1 == uintobj2 { return compareEqual, true } if uintobj1 < uintobj2 { return compareLess, true } } case reflect.Uint8: { uint8obj1, ok := obj1.(uint8) if !ok { uint8obj1 = obj1Value.Convert(uint8Type).Interface().(uint8) } uint8obj2, ok := obj2.(uint8) if !ok { uint8obj2 = obj2Value.Convert(uint8Type).Interface().(uint8) } if uint8obj1 > uint8obj2 { return compareGreater, true } if uint8obj1 == uint8obj2 { return compareEqual, true } if uint8obj1 < uint8obj2 { return compareLess, true } } case reflect.Uint16: { uint16obj1, ok := obj1.(uint16) if !ok { uint16obj1 = obj1Value.Convert(uint16Type).Interface().(uint16) } uint16obj2, ok := obj2.(uint16) if !ok { uint16obj2 = obj2Value.Convert(uint16Type).Interface().(uint16) } if uint16obj1 > uint16obj2 { return compareGreater, true } if uint16obj1 == uint16obj2 { return compareEqual, true } if uint16obj1 < uint16obj2 { return compareLess, true } } case reflect.Uint32: { uint32obj1, ok := obj1.(uint32) if !ok { uint32obj1 = obj1Value.Convert(uint32Type).Interface().(uint32) } uint32obj2, ok := obj2.(uint32) if !ok { uint32obj2 = obj2Value.Convert(uint32Type).Interface().(uint32) } if uint32obj1 > uint32obj2 { return compareGreater, true } if uint32obj1 == uint32obj2 { return compareEqual, true } if uint32obj1 < uint32obj2 { return compareLess, true } } case reflect.Uint64: { uint64obj1, ok := obj1.(uint64) if !ok { uint64obj1 = obj1Value.Convert(uint64Type).Interface().(uint64) } uint64obj2, ok := obj2.(uint64) if !ok { uint64obj2 = obj2Value.Convert(uint64Type).Interface().(uint64) } if uint64obj1 > uint64obj2 { return compareGreater, true } if uint64obj1 == uint64obj2 { return compareEqual, true } if uint64obj1 < uint64obj2 { return compareLess, true } } case reflect.Float32: { float32obj1, ok := obj1.(float32) if !ok { float32obj1 = obj1Value.Convert(float32Type).Interface().(float32) } float32obj2, ok := obj2.(float32) if !ok { float32obj2 = obj2Value.Convert(float32Type).Interface().(float32) } if float32obj1 > float32obj2 { return compareGreater, true } if float32obj1 == float32obj2 { return compareEqual, true } if float32obj1 < float32obj2 { return compareLess, true } } case reflect.Float64: { float64obj1, ok := obj1.(float64) if !ok { float64obj1 = obj1Value.Convert(float64Type).Interface().(float64) } float64obj2, ok := obj2.(float64) if !ok { float64obj2 = obj2Value.Convert(float64Type).Interface().(float64) } if float64obj1 > float64obj2 { return compareGreater, true } if float64obj1 == float64obj2 { return compareEqual, true } if float64obj1 < float64obj2 { return compareLess, true } } case reflect.String: { stringobj1, ok := obj1.(string) if !ok { stringobj1 = obj1Value.Convert(stringType).Interface().(string) } stringobj2, ok := obj2.(string) if !ok { stringobj2 = obj2Value.Convert(stringType).Interface().(string) } if stringobj1 > stringobj2 { return compareGreater, true } if stringobj1 == stringobj2 { return compareEqual, true } if stringobj1 < stringobj2 { return compareLess, true } } // Check for known struct types we can check for compare results. case reflect.Struct: { // All structs enter here. We're not interested in most types. if !obj1Value.CanConvert(timeType) { break } // time.Time can be compared! timeObj1, ok := obj1.(time.Time) if !ok { timeObj1 = obj1Value.Convert(timeType).Interface().(time.Time) } timeObj2, ok := obj2.(time.Time) if !ok { timeObj2 = obj2Value.Convert(timeType).Interface().(time.Time) } return compare(timeObj1.UnixNano(), timeObj2.UnixNano(), reflect.Int64) } case reflect.Slice: { // We only care about the []byte type. if !obj1Value.CanConvert(bytesType) { break } // []byte can be compared! bytesObj1, ok := obj1.([]byte) if !ok { bytesObj1 = obj1Value.Convert(bytesType).Interface().([]byte) } bytesObj2, ok := obj2.([]byte) if !ok { bytesObj2 = obj2Value.Convert(bytesType).Interface().([]byte) } return CompareType(bytes.Compare(bytesObj1, bytesObj2)), true } case reflect.Uintptr: { uintptrObj1, ok := obj1.(uintptr) if !ok { uintptrObj1 = obj1Value.Convert(uintptrType).Interface().(uintptr) } uintptrObj2, ok := obj2.(uintptr) if !ok { uintptrObj2 = obj2Value.Convert(uintptrType).Interface().(uintptr) } if uintptrObj1 > uintptrObj2 { return compareGreater, true } if uintptrObj1 == uintptrObj2 { return compareEqual, true } if uintptrObj1 < uintptrObj2 { return compareLess, true } } } return compareEqual, false } // Greater asserts that the first element is greater than the second // // assert.Greater(t, 2, 1) // assert.Greater(t, float64(2), float64(1)) // assert.Greater(t, "b", "a") func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return compareTwoValues(t, e1, e2, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) } // GreaterOrEqual asserts that the first element is greater than or equal to the second // // assert.GreaterOrEqual(t, 2, 1) // assert.GreaterOrEqual(t, 2, 2) // assert.GreaterOrEqual(t, "b", "a") // assert.GreaterOrEqual(t, "b", "b") func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return compareTwoValues(t, e1, e2, []CompareType{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) } // Less asserts that the first element is less than the second // // assert.Less(t, 1, 2) // assert.Less(t, float64(1), float64(2)) // assert.Less(t, "a", "b") func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return compareTwoValues(t, e1, e2, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) } // LessOrEqual asserts that the first element is less than or equal to the second // // assert.LessOrEqual(t, 1, 2) // assert.LessOrEqual(t, 2, 2) // assert.LessOrEqual(t, "a", "b") // assert.LessOrEqual(t, "b", "b") func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) } // Positive asserts that the specified element is positive // // assert.Positive(t, 1) // assert.Positive(t, 1.23) func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } zero := reflect.Zero(reflect.TypeOf(e)) return compareTwoValues(t, e, zero.Interface(), []CompareType{compareGreater}, "\"%v\" is not positive", msgAndArgs...) } // Negative asserts that the specified element is negative // // assert.Negative(t, -1) // assert.Negative(t, -1.23) func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } zero := reflect.Zero(reflect.TypeOf(e)) return compareTwoValues(t, e, zero.Interface(), []CompareType{compareLess}, "\"%v\" is not negative", msgAndArgs...) } func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } e1Kind := reflect.ValueOf(e1).Kind() e2Kind := reflect.ValueOf(e2).Kind() if e1Kind != e2Kind { return Fail(t, "Elements should be the same type", msgAndArgs...) } compareResult, isComparable := compare(e1, e2, e1Kind) if !isComparable { return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...) } if !containsValue(allowedComparesResults, compareResult) { return Fail(t, fmt.Sprintf(failMessage, e1, e2), msgAndArgs...) } return true } func containsValue(values []CompareType, value CompareType) bool { for _, v := range values { if v == value { return true } } return false } ================================================ FILE: vendor/github.com/stretchr/testify/assert/assertion_format.go ================================================ // Code generated with github.com/stretchr/testify/_codegen; DO NOT EDIT. package assert import ( http "net/http" url "net/url" time "time" ) // Conditionf uses a Comparison to assert a complex condition. func Conditionf(t TestingT, comp Comparison, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return Condition(t, comp, append([]interface{}{msg}, args...)...) } // Containsf asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // // assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") // assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") // assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return Contains(t, s, contains, append([]interface{}{msg}, args...)...) } // DirExistsf checks whether a directory exists in the given path. It also fails // if the path is a file rather a directory or there is an error checking whether it exists. func DirExistsf(t TestingT, path string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return DirExists(t, path, append([]interface{}{msg}, args...)...) } // ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified // listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, // the number of appearances of each of them in both lists should match. // // assert.ElementsMatchf(t, [1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return ElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...) } // Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // // assert.Emptyf(t, obj, "error message %s", "formatted") func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return Empty(t, object, append([]interface{}{msg}, args...)...) } // Equalf asserts that two objects are equal. // // assert.Equalf(t, 123, 123, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality // cannot be determined and will always fail. func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return Equal(t, expected, actual, append([]interface{}{msg}, args...)...) } // EqualErrorf asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() // assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return EqualError(t, theError, errString, append([]interface{}{msg}, args...)...) } // EqualExportedValuesf asserts that the types of two objects are equal and their public // fields are also equal. This is useful for comparing structs that have private fields // that could potentially differ. // // type S struct { // Exported int // notExported int // } // assert.EqualExportedValuesf(t, S{1, 2}, S{1, 3}, "error message %s", "formatted") => true // assert.EqualExportedValuesf(t, S{1, 2}, S{2, 3}, "error message %s", "formatted") => false func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return EqualExportedValues(t, expected, actual, append([]interface{}{msg}, args...)...) } // EqualValuesf asserts that two objects are equal or convertible to the same types // and equal. // // assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return EqualValues(t, expected, actual, append([]interface{}{msg}, args...)...) } // Errorf asserts that a function returned an error (i.e. not `nil`). // // actualObj, err := SomeFunction() // if assert.Errorf(t, err, "error message %s", "formatted") { // assert.Equal(t, expectedErrorf, err) // } func Errorf(t TestingT, err error, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return Error(t, err, append([]interface{}{msg}, args...)...) } // ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. // This is a wrapper for errors.As. func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return ErrorAs(t, err, target, append([]interface{}{msg}, args...)...) } // ErrorContainsf asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // // actualObj, err := SomeFunction() // assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return ErrorContains(t, theError, contains, append([]interface{}{msg}, args...)...) } // ErrorIsf asserts that at least one of the errors in err's chain matches target. // This is a wrapper for errors.Is. func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return ErrorIs(t, err, target, append([]interface{}{msg}, args...)...) } // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // // assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return Eventually(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...) } // EventuallyWithTf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. In contrast to Eventually, // it supplies a CollectT to the condition function, so that the condition // function can use the CollectT to call other assertions. // The condition is considered "met" if no errors are raised in a tick. // The supplied CollectT collects all errors from one tick (if there are any). // If the condition is not met before waitFor, the collected errors of // the last tick are copied to t. // // externalValue := false // go func() { // time.Sleep(8*time.Second) // externalValue = true // }() // assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") { // // add assertions as needed; any assertion failure will fail the current tick // assert.True(c, externalValue, "expected 'externalValue' to be true") // }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") func EventuallyWithTf(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return EventuallyWithT(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...) } // Exactlyf asserts that two objects are equal in value and type. // // assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return Exactly(t, expected, actual, append([]interface{}{msg}, args...)...) } // Failf reports a failure through func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return Fail(t, failureMessage, append([]interface{}{msg}, args...)...) } // FailNowf fails test func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return FailNow(t, failureMessage, append([]interface{}{msg}, args...)...) } // Falsef asserts that the specified value is false. // // assert.Falsef(t, myBool, "error message %s", "formatted") func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return False(t, value, append([]interface{}{msg}, args...)...) } // FileExistsf checks whether a file exists in the given path. It also fails if // the path points to a directory or there is an error when trying to check the file. func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return FileExists(t, path, append([]interface{}{msg}, args...)...) } // Greaterf asserts that the first element is greater than the second // // assert.Greaterf(t, 2, 1, "error message %s", "formatted") // assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") // assert.Greaterf(t, "b", "a", "error message %s", "formatted") func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return Greater(t, e1, e2, append([]interface{}{msg}, args...)...) } // GreaterOrEqualf asserts that the first element is greater than or equal to the second // // assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") // assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") // assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") // assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return GreaterOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...) } // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // // assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return HTTPBodyContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...) } // HTTPBodyNotContainsf asserts that a specified handler returns a // body that does not contain a string. // // assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return HTTPBodyNotContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...) } // HTTPErrorf asserts that a specified handler returns an error status code. // // assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return HTTPError(t, handler, method, url, values, append([]interface{}{msg}, args...)...) } // HTTPRedirectf asserts that a specified handler returns a redirect status code. // // assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return HTTPRedirect(t, handler, method, url, values, append([]interface{}{msg}, args...)...) } // HTTPStatusCodef asserts that a specified handler returns a specified status code. // // assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return HTTPStatusCode(t, handler, method, url, values, statuscode, append([]interface{}{msg}, args...)...) } // HTTPSuccessf asserts that a specified handler returns a success status code. // // assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return HTTPSuccess(t, handler, method, url, values, append([]interface{}{msg}, args...)...) } // Implementsf asserts that an object is implemented by the specified interface. // // assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return Implements(t, interfaceObject, object, append([]interface{}{msg}, args...)...) } // InDeltaf asserts that the two numerals are within delta of each other. // // assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return InDelta(t, expected, actual, delta, append([]interface{}{msg}, args...)...) } // InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. func InDeltaMapValuesf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return InDeltaMapValues(t, expected, actual, delta, append([]interface{}{msg}, args...)...) } // InDeltaSlicef is the same as InDelta, except it compares two slices. func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return InDeltaSlice(t, expected, actual, delta, append([]interface{}{msg}, args...)...) } // InEpsilonf asserts that expected and actual have a relative error less than epsilon func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return InEpsilon(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...) } // InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices. func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return InEpsilonSlice(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...) } // IsDecreasingf asserts that the collection is decreasing // // assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") // assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") // assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return IsDecreasing(t, object, append([]interface{}{msg}, args...)...) } // IsIncreasingf asserts that the collection is increasing // // assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") // assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") // assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return IsIncreasing(t, object, append([]interface{}{msg}, args...)...) } // IsNonDecreasingf asserts that the collection is not decreasing // // assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") // assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") // assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return IsNonDecreasing(t, object, append([]interface{}{msg}, args...)...) } // IsNonIncreasingf asserts that the collection is not increasing // // assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") // assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") // assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return IsNonIncreasing(t, object, append([]interface{}{msg}, args...)...) } // IsTypef asserts that the specified objects are of the same type. func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return IsType(t, expectedType, object, append([]interface{}{msg}, args...)...) } // JSONEqf asserts that two JSON strings are equivalent. // // assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return JSONEq(t, expected, actual, append([]interface{}{msg}, args...)...) } // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // // assert.Lenf(t, mySlice, 3, "error message %s", "formatted") func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return Len(t, object, length, append([]interface{}{msg}, args...)...) } // Lessf asserts that the first element is less than the second // // assert.Lessf(t, 1, 2, "error message %s", "formatted") // assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") // assert.Lessf(t, "a", "b", "error message %s", "formatted") func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return Less(t, e1, e2, append([]interface{}{msg}, args...)...) } // LessOrEqualf asserts that the first element is less than or equal to the second // // assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") // assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") // assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") // assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...) } // Negativef asserts that the specified element is negative // // assert.Negativef(t, -1, "error message %s", "formatted") // assert.Negativef(t, -1.23, "error message %s", "formatted") func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return Negative(t, e, append([]interface{}{msg}, args...)...) } // Neverf asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // // assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return Never(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...) } // Nilf asserts that the specified object is nil. // // assert.Nilf(t, err, "error message %s", "formatted") func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return Nil(t, object, append([]interface{}{msg}, args...)...) } // NoDirExistsf checks whether a directory does not exist in the given path. // It fails if the path points to an existing _directory_ only. func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return NoDirExists(t, path, append([]interface{}{msg}, args...)...) } // NoErrorf asserts that a function returned no error (i.e. `nil`). // // actualObj, err := SomeFunction() // if assert.NoErrorf(t, err, "error message %s", "formatted") { // assert.Equal(t, expectedObj, actualObj) // } func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return NoError(t, err, append([]interface{}{msg}, args...)...) } // NoFileExistsf checks whether a file does not exist in a given path. It fails // if the path points to an existing _file_ only. func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return NoFileExists(t, path, append([]interface{}{msg}, args...)...) } // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // // assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") // assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") // assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return NotContains(t, s, contains, append([]interface{}{msg}, args...)...) } // NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // // if assert.NotEmptyf(t, obj, "error message %s", "formatted") { // assert.Equal(t, "two", obj[1]) // } func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return NotEmpty(t, object, append([]interface{}{msg}, args...)...) } // NotEqualf asserts that the specified values are NOT equal. // // assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return NotEqual(t, expected, actual, append([]interface{}{msg}, args...)...) } // NotEqualValuesf asserts that two objects are not equal even when converted to the same type // // assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...) } // NotErrorIsf asserts that at none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return NotErrorIs(t, err, target, append([]interface{}{msg}, args...)...) } // NotImplementsf asserts that an object does not implement the specified interface. // // assert.NotImplementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func NotImplementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return NotImplements(t, interfaceObject, object, append([]interface{}{msg}, args...)...) } // NotNilf asserts that the specified object is not nil. // // assert.NotNilf(t, err, "error message %s", "formatted") func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return NotNil(t, object, append([]interface{}{msg}, args...)...) } // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. // // assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return NotPanics(t, f, append([]interface{}{msg}, args...)...) } // NotRegexpf asserts that a specified regexp does not match a string. // // assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") // assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return NotRegexp(t, rx, str, append([]interface{}{msg}, args...)...) } // NotSamef asserts that two pointers do not reference the same object. // // assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return NotSame(t, expected, actual, append([]interface{}{msg}, args...)...) } // NotSubsetf asserts that the specified list(array, slice...) or map does NOT // contain all elements given in the specified subset list(array, slice...) or // map. // // assert.NotSubsetf(t, [1, 3, 4], [1, 2], "error message %s", "formatted") // assert.NotSubsetf(t, {"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return NotSubset(t, list, subset, append([]interface{}{msg}, args...)...) } // NotZerof asserts that i is not the zero value for its type. func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return NotZero(t, i, append([]interface{}{msg}, args...)...) } // Panicsf asserts that the code inside the specified PanicTestFunc panics. // // assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return Panics(t, f, append([]interface{}{msg}, args...)...) } // PanicsWithErrorf asserts that the code inside the specified PanicTestFunc // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // // assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithErrorf(t TestingT, errString string, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return PanicsWithError(t, errString, f, append([]interface{}{msg}, args...)...) } // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // // assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...) } // Positivef asserts that the specified element is positive // // assert.Positivef(t, 1, "error message %s", "formatted") // assert.Positivef(t, 1.23, "error message %s", "formatted") func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return Positive(t, e, append([]interface{}{msg}, args...)...) } // Regexpf asserts that a specified regexp matches a string. // // assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") // assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return Regexp(t, rx, str, append([]interface{}{msg}, args...)...) } // Samef asserts that two pointers reference the same object. // // assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. func Samef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return Same(t, expected, actual, append([]interface{}{msg}, args...)...) } // Subsetf asserts that the specified list(array, slice...) or map contains all // elements given in the specified subset list(array, slice...) or map. // // assert.Subsetf(t, [1, 2, 3], [1, 2], "error message %s", "formatted") // assert.Subsetf(t, {"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return Subset(t, list, subset, append([]interface{}{msg}, args...)...) } // Truef asserts that the specified value is true. // // assert.Truef(t, myBool, "error message %s", "formatted") func Truef(t TestingT, value bool, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return True(t, value, append([]interface{}{msg}, args...)...) } // WithinDurationf asserts that the two times are within duration delta of each other. // // assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...) } // WithinRangef asserts that a time is within a time range (inclusive). // // assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return WithinRange(t, actual, start, end, append([]interface{}{msg}, args...)...) } // YAMLEqf asserts that two YAML strings are equivalent. func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return YAMLEq(t, expected, actual, append([]interface{}{msg}, args...)...) } // Zerof asserts that i is the zero value for its type. func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return Zero(t, i, append([]interface{}{msg}, args...)...) } ================================================ FILE: vendor/github.com/stretchr/testify/assert/assertion_format.go.tmpl ================================================ {{.CommentFormat}} func {{.DocInfo.Name}}f(t TestingT, {{.ParamsFormat}}) bool { if h, ok := t.(tHelper); ok { h.Helper() } return {{.DocInfo.Name}}(t, {{.ForwardedParamsFormat}}) } ================================================ FILE: vendor/github.com/stretchr/testify/assert/assertion_forward.go ================================================ // Code generated with github.com/stretchr/testify/_codegen; DO NOT EDIT. package assert import ( http "net/http" url "net/url" time "time" ) // Condition uses a Comparison to assert a complex condition. func (a *Assertions) Condition(comp Comparison, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Condition(a.t, comp, msgAndArgs...) } // Conditionf uses a Comparison to assert a complex condition. func (a *Assertions) Conditionf(comp Comparison, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Conditionf(a.t, comp, msg, args...) } // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // // a.Contains("Hello World", "World") // a.Contains(["Hello", "World"], "World") // a.Contains({"Hello": "World"}, "Hello") func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Contains(a.t, s, contains, msgAndArgs...) } // Containsf asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // // a.Containsf("Hello World", "World", "error message %s", "formatted") // a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") // a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Containsf(a.t, s, contains, msg, args...) } // DirExists checks whether a directory exists in the given path. It also fails // if the path is a file rather a directory or there is an error checking whether it exists. func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return DirExists(a.t, path, msgAndArgs...) } // DirExistsf checks whether a directory exists in the given path. It also fails // if the path is a file rather a directory or there is an error checking whether it exists. func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return DirExistsf(a.t, path, msg, args...) } // ElementsMatch asserts that the specified listA(array, slice...) is equal to specified // listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, // the number of appearances of each of them in both lists should match. // // a.ElementsMatch([1, 3, 2, 3], [1, 3, 3, 2]) func (a *Assertions) ElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return ElementsMatch(a.t, listA, listB, msgAndArgs...) } // ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified // listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, // the number of appearances of each of them in both lists should match. // // a.ElementsMatchf([1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return ElementsMatchf(a.t, listA, listB, msg, args...) } // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // // a.Empty(obj) func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Empty(a.t, object, msgAndArgs...) } // Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // // a.Emptyf(obj, "error message %s", "formatted") func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Emptyf(a.t, object, msg, args...) } // Equal asserts that two objects are equal. // // a.Equal(123, 123) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality // cannot be determined and will always fail. func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Equal(a.t, expected, actual, msgAndArgs...) } // EqualError asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() // a.EqualError(err, expectedErrorString) func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return EqualError(a.t, theError, errString, msgAndArgs...) } // EqualErrorf asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() // a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return EqualErrorf(a.t, theError, errString, msg, args...) } // EqualExportedValues asserts that the types of two objects are equal and their public // fields are also equal. This is useful for comparing structs that have private fields // that could potentially differ. // // type S struct { // Exported int // notExported int // } // a.EqualExportedValues(S{1, 2}, S{1, 3}) => true // a.EqualExportedValues(S{1, 2}, S{2, 3}) => false func (a *Assertions) EqualExportedValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return EqualExportedValues(a.t, expected, actual, msgAndArgs...) } // EqualExportedValuesf asserts that the types of two objects are equal and their public // fields are also equal. This is useful for comparing structs that have private fields // that could potentially differ. // // type S struct { // Exported int // notExported int // } // a.EqualExportedValuesf(S{1, 2}, S{1, 3}, "error message %s", "formatted") => true // a.EqualExportedValuesf(S{1, 2}, S{2, 3}, "error message %s", "formatted") => false func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return EqualExportedValuesf(a.t, expected, actual, msg, args...) } // EqualValues asserts that two objects are equal or convertible to the same types // and equal. // // a.EqualValues(uint32(123), int32(123)) func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return EqualValues(a.t, expected, actual, msgAndArgs...) } // EqualValuesf asserts that two objects are equal or convertible to the same types // and equal. // // a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return EqualValuesf(a.t, expected, actual, msg, args...) } // Equalf asserts that two objects are equal. // // a.Equalf(123, 123, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality // cannot be determined and will always fail. func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Equalf(a.t, expected, actual, msg, args...) } // Error asserts that a function returned an error (i.e. not `nil`). // // actualObj, err := SomeFunction() // if a.Error(err) { // assert.Equal(t, expectedError, err) // } func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Error(a.t, err, msgAndArgs...) } // ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. // This is a wrapper for errors.As. func (a *Assertions) ErrorAs(err error, target interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return ErrorAs(a.t, err, target, msgAndArgs...) } // ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. // This is a wrapper for errors.As. func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return ErrorAsf(a.t, err, target, msg, args...) } // ErrorContains asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // // actualObj, err := SomeFunction() // a.ErrorContains(err, expectedErrorSubString) func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return ErrorContains(a.t, theError, contains, msgAndArgs...) } // ErrorContainsf asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // // actualObj, err := SomeFunction() // a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") func (a *Assertions) ErrorContainsf(theError error, contains string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return ErrorContainsf(a.t, theError, contains, msg, args...) } // ErrorIs asserts that at least one of the errors in err's chain matches target. // This is a wrapper for errors.Is. func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return ErrorIs(a.t, err, target, msgAndArgs...) } // ErrorIsf asserts that at least one of the errors in err's chain matches target. // This is a wrapper for errors.Is. func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return ErrorIsf(a.t, err, target, msg, args...) } // Errorf asserts that a function returned an error (i.e. not `nil`). // // actualObj, err := SomeFunction() // if a.Errorf(err, "error message %s", "formatted") { // assert.Equal(t, expectedErrorf, err) // } func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Errorf(a.t, err, msg, args...) } // Eventually asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // // a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Eventually(a.t, condition, waitFor, tick, msgAndArgs...) } // EventuallyWithT asserts that given condition will be met in waitFor time, // periodically checking target function each tick. In contrast to Eventually, // it supplies a CollectT to the condition function, so that the condition // function can use the CollectT to call other assertions. // The condition is considered "met" if no errors are raised in a tick. // The supplied CollectT collects all errors from one tick (if there are any). // If the condition is not met before waitFor, the collected errors of // the last tick are copied to t. // // externalValue := false // go func() { // time.Sleep(8*time.Second) // externalValue = true // }() // a.EventuallyWithT(func(c *assert.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // assert.True(c, externalValue, "expected 'externalValue' to be true") // }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") func (a *Assertions) EventuallyWithT(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return EventuallyWithT(a.t, condition, waitFor, tick, msgAndArgs...) } // EventuallyWithTf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. In contrast to Eventually, // it supplies a CollectT to the condition function, so that the condition // function can use the CollectT to call other assertions. // The condition is considered "met" if no errors are raised in a tick. // The supplied CollectT collects all errors from one tick (if there are any). // If the condition is not met before waitFor, the collected errors of // the last tick are copied to t. // // externalValue := false // go func() { // time.Sleep(8*time.Second) // externalValue = true // }() // a.EventuallyWithTf(func(c *assert.CollectT, "error message %s", "formatted") { // // add assertions as needed; any assertion failure will fail the current tick // assert.True(c, externalValue, "expected 'externalValue' to be true") // }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") func (a *Assertions) EventuallyWithTf(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return EventuallyWithTf(a.t, condition, waitFor, tick, msg, args...) } // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // // a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Eventuallyf(a.t, condition, waitFor, tick, msg, args...) } // Exactly asserts that two objects are equal in value and type. // // a.Exactly(int32(123), int64(123)) func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Exactly(a.t, expected, actual, msgAndArgs...) } // Exactlyf asserts that two objects are equal in value and type. // // a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Exactlyf(a.t, expected, actual, msg, args...) } // Fail reports a failure through func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Fail(a.t, failureMessage, msgAndArgs...) } // FailNow fails test func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return FailNow(a.t, failureMessage, msgAndArgs...) } // FailNowf fails test func (a *Assertions) FailNowf(failureMessage string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return FailNowf(a.t, failureMessage, msg, args...) } // Failf reports a failure through func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Failf(a.t, failureMessage, msg, args...) } // False asserts that the specified value is false. // // a.False(myBool) func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return False(a.t, value, msgAndArgs...) } // Falsef asserts that the specified value is false. // // a.Falsef(myBool, "error message %s", "formatted") func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Falsef(a.t, value, msg, args...) } // FileExists checks whether a file exists in the given path. It also fails if // the path points to a directory or there is an error when trying to check the file. func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return FileExists(a.t, path, msgAndArgs...) } // FileExistsf checks whether a file exists in the given path. It also fails if // the path points to a directory or there is an error when trying to check the file. func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return FileExistsf(a.t, path, msg, args...) } // Greater asserts that the first element is greater than the second // // a.Greater(2, 1) // a.Greater(float64(2), float64(1)) // a.Greater("b", "a") func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Greater(a.t, e1, e2, msgAndArgs...) } // GreaterOrEqual asserts that the first element is greater than or equal to the second // // a.GreaterOrEqual(2, 1) // a.GreaterOrEqual(2, 2) // a.GreaterOrEqual("b", "a") // a.GreaterOrEqual("b", "b") func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return GreaterOrEqual(a.t, e1, e2, msgAndArgs...) } // GreaterOrEqualf asserts that the first element is greater than or equal to the second // // a.GreaterOrEqualf(2, 1, "error message %s", "formatted") // a.GreaterOrEqualf(2, 2, "error message %s", "formatted") // a.GreaterOrEqualf("b", "a", "error message %s", "formatted") // a.GreaterOrEqualf("b", "b", "error message %s", "formatted") func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return GreaterOrEqualf(a.t, e1, e2, msg, args...) } // Greaterf asserts that the first element is greater than the second // // a.Greaterf(2, 1, "error message %s", "formatted") // a.Greaterf(float64(2), float64(1), "error message %s", "formatted") // a.Greaterf("b", "a", "error message %s", "formatted") func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Greaterf(a.t, e1, e2, msg, args...) } // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // // a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return HTTPBodyContains(a.t, handler, method, url, values, str, msgAndArgs...) } // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // // a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return HTTPBodyContainsf(a.t, handler, method, url, values, str, msg, args...) } // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // // a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return HTTPBodyNotContains(a.t, handler, method, url, values, str, msgAndArgs...) } // HTTPBodyNotContainsf asserts that a specified handler returns a // body that does not contain a string. // // a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return HTTPBodyNotContainsf(a.t, handler, method, url, values, str, msg, args...) } // HTTPError asserts that a specified handler returns an error status code. // // a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return HTTPError(a.t, handler, method, url, values, msgAndArgs...) } // HTTPErrorf asserts that a specified handler returns an error status code. // // a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return HTTPErrorf(a.t, handler, method, url, values, msg, args...) } // HTTPRedirect asserts that a specified handler returns a redirect status code. // // a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return HTTPRedirect(a.t, handler, method, url, values, msgAndArgs...) } // HTTPRedirectf asserts that a specified handler returns a redirect status code. // // a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return HTTPRedirectf(a.t, handler, method, url, values, msg, args...) } // HTTPStatusCode asserts that a specified handler returns a specified status code. // // a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return HTTPStatusCode(a.t, handler, method, url, values, statuscode, msgAndArgs...) } // HTTPStatusCodef asserts that a specified handler returns a specified status code. // // a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return HTTPStatusCodef(a.t, handler, method, url, values, statuscode, msg, args...) } // HTTPSuccess asserts that a specified handler returns a success status code. // // a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return HTTPSuccess(a.t, handler, method, url, values, msgAndArgs...) } // HTTPSuccessf asserts that a specified handler returns a success status code. // // a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return HTTPSuccessf(a.t, handler, method, url, values, msg, args...) } // Implements asserts that an object is implemented by the specified interface. // // a.Implements((*MyInterface)(nil), new(MyObject)) func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Implements(a.t, interfaceObject, object, msgAndArgs...) } // Implementsf asserts that an object is implemented by the specified interface. // // a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Implementsf(a.t, interfaceObject, object, msg, args...) } // InDelta asserts that the two numerals are within delta of each other. // // a.InDelta(math.Pi, 22/7.0, 0.01) func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return InDelta(a.t, expected, actual, delta, msgAndArgs...) } // InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. func (a *Assertions) InDeltaMapValues(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return InDeltaMapValues(a.t, expected, actual, delta, msgAndArgs...) } // InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. func (a *Assertions) InDeltaMapValuesf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return InDeltaMapValuesf(a.t, expected, actual, delta, msg, args...) } // InDeltaSlice is the same as InDelta, except it compares two slices. func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...) } // InDeltaSlicef is the same as InDelta, except it compares two slices. func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return InDeltaSlicef(a.t, expected, actual, delta, msg, args...) } // InDeltaf asserts that the two numerals are within delta of each other. // // a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return InDeltaf(a.t, expected, actual, delta, msg, args...) } // InEpsilon asserts that expected and actual have a relative error less than epsilon func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...) } // InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return InEpsilonSlice(a.t, expected, actual, epsilon, msgAndArgs...) } // InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices. func (a *Assertions) InEpsilonSlicef(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return InEpsilonSlicef(a.t, expected, actual, epsilon, msg, args...) } // InEpsilonf asserts that expected and actual have a relative error less than epsilon func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return InEpsilonf(a.t, expected, actual, epsilon, msg, args...) } // IsDecreasing asserts that the collection is decreasing // // a.IsDecreasing([]int{2, 1, 0}) // a.IsDecreasing([]float{2, 1}) // a.IsDecreasing([]string{"b", "a"}) func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return IsDecreasing(a.t, object, msgAndArgs...) } // IsDecreasingf asserts that the collection is decreasing // // a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") // a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") // a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return IsDecreasingf(a.t, object, msg, args...) } // IsIncreasing asserts that the collection is increasing // // a.IsIncreasing([]int{1, 2, 3}) // a.IsIncreasing([]float{1, 2}) // a.IsIncreasing([]string{"a", "b"}) func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return IsIncreasing(a.t, object, msgAndArgs...) } // IsIncreasingf asserts that the collection is increasing // // a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") // a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") // a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return IsIncreasingf(a.t, object, msg, args...) } // IsNonDecreasing asserts that the collection is not decreasing // // a.IsNonDecreasing([]int{1, 1, 2}) // a.IsNonDecreasing([]float{1, 2}) // a.IsNonDecreasing([]string{"a", "b"}) func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return IsNonDecreasing(a.t, object, msgAndArgs...) } // IsNonDecreasingf asserts that the collection is not decreasing // // a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") // a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") // a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return IsNonDecreasingf(a.t, object, msg, args...) } // IsNonIncreasing asserts that the collection is not increasing // // a.IsNonIncreasing([]int{2, 1, 1}) // a.IsNonIncreasing([]float{2, 1}) // a.IsNonIncreasing([]string{"b", "a"}) func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return IsNonIncreasing(a.t, object, msgAndArgs...) } // IsNonIncreasingf asserts that the collection is not increasing // // a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") // a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") // a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return IsNonIncreasingf(a.t, object, msg, args...) } // IsType asserts that the specified objects are of the same type. func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return IsType(a.t, expectedType, object, msgAndArgs...) } // IsTypef asserts that the specified objects are of the same type. func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return IsTypef(a.t, expectedType, object, msg, args...) } // JSONEq asserts that two JSON strings are equivalent. // // a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return JSONEq(a.t, expected, actual, msgAndArgs...) } // JSONEqf asserts that two JSON strings are equivalent. // // a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return JSONEqf(a.t, expected, actual, msg, args...) } // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // // a.Len(mySlice, 3) func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Len(a.t, object, length, msgAndArgs...) } // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // // a.Lenf(mySlice, 3, "error message %s", "formatted") func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Lenf(a.t, object, length, msg, args...) } // Less asserts that the first element is less than the second // // a.Less(1, 2) // a.Less(float64(1), float64(2)) // a.Less("a", "b") func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Less(a.t, e1, e2, msgAndArgs...) } // LessOrEqual asserts that the first element is less than or equal to the second // // a.LessOrEqual(1, 2) // a.LessOrEqual(2, 2) // a.LessOrEqual("a", "b") // a.LessOrEqual("b", "b") func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return LessOrEqual(a.t, e1, e2, msgAndArgs...) } // LessOrEqualf asserts that the first element is less than or equal to the second // // a.LessOrEqualf(1, 2, "error message %s", "formatted") // a.LessOrEqualf(2, 2, "error message %s", "formatted") // a.LessOrEqualf("a", "b", "error message %s", "formatted") // a.LessOrEqualf("b", "b", "error message %s", "formatted") func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return LessOrEqualf(a.t, e1, e2, msg, args...) } // Lessf asserts that the first element is less than the second // // a.Lessf(1, 2, "error message %s", "formatted") // a.Lessf(float64(1), float64(2), "error message %s", "formatted") // a.Lessf("a", "b", "error message %s", "formatted") func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Lessf(a.t, e1, e2, msg, args...) } // Negative asserts that the specified element is negative // // a.Negative(-1) // a.Negative(-1.23) func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Negative(a.t, e, msgAndArgs...) } // Negativef asserts that the specified element is negative // // a.Negativef(-1, "error message %s", "formatted") // a.Negativef(-1.23, "error message %s", "formatted") func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Negativef(a.t, e, msg, args...) } // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // // a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Never(a.t, condition, waitFor, tick, msgAndArgs...) } // Neverf asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // // a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Neverf(a.t, condition, waitFor, tick, msg, args...) } // Nil asserts that the specified object is nil. // // a.Nil(err) func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Nil(a.t, object, msgAndArgs...) } // Nilf asserts that the specified object is nil. // // a.Nilf(err, "error message %s", "formatted") func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Nilf(a.t, object, msg, args...) } // NoDirExists checks whether a directory does not exist in the given path. // It fails if the path points to an existing _directory_ only. func (a *Assertions) NoDirExists(path string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NoDirExists(a.t, path, msgAndArgs...) } // NoDirExistsf checks whether a directory does not exist in the given path. // It fails if the path points to an existing _directory_ only. func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NoDirExistsf(a.t, path, msg, args...) } // NoError asserts that a function returned no error (i.e. `nil`). // // actualObj, err := SomeFunction() // if a.NoError(err) { // assert.Equal(t, expectedObj, actualObj) // } func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NoError(a.t, err, msgAndArgs...) } // NoErrorf asserts that a function returned no error (i.e. `nil`). // // actualObj, err := SomeFunction() // if a.NoErrorf(err, "error message %s", "formatted") { // assert.Equal(t, expectedObj, actualObj) // } func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NoErrorf(a.t, err, msg, args...) } // NoFileExists checks whether a file does not exist in a given path. It fails // if the path points to an existing _file_ only. func (a *Assertions) NoFileExists(path string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NoFileExists(a.t, path, msgAndArgs...) } // NoFileExistsf checks whether a file does not exist in a given path. It fails // if the path points to an existing _file_ only. func (a *Assertions) NoFileExistsf(path string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NoFileExistsf(a.t, path, msg, args...) } // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // // a.NotContains("Hello World", "Earth") // a.NotContains(["Hello", "World"], "Earth") // a.NotContains({"Hello": "World"}, "Earth") func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NotContains(a.t, s, contains, msgAndArgs...) } // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // // a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") // a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") // a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NotContainsf(a.t, s, contains, msg, args...) } // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // // if a.NotEmpty(obj) { // assert.Equal(t, "two", obj[1]) // } func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NotEmpty(a.t, object, msgAndArgs...) } // NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // // if a.NotEmptyf(obj, "error message %s", "formatted") { // assert.Equal(t, "two", obj[1]) // } func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NotEmptyf(a.t, object, msg, args...) } // NotEqual asserts that the specified values are NOT equal. // // a.NotEqual(obj1, obj2) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NotEqual(a.t, expected, actual, msgAndArgs...) } // NotEqualValues asserts that two objects are not equal even when converted to the same type // // a.NotEqualValues(obj1, obj2) func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NotEqualValues(a.t, expected, actual, msgAndArgs...) } // NotEqualValuesf asserts that two objects are not equal even when converted to the same type // // a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NotEqualValuesf(a.t, expected, actual, msg, args...) } // NotEqualf asserts that the specified values are NOT equal. // // a.NotEqualf(obj1, obj2, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NotEqualf(a.t, expected, actual, msg, args...) } // NotErrorIs asserts that at none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NotErrorIs(a.t, err, target, msgAndArgs...) } // NotErrorIsf asserts that at none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NotErrorIsf(a.t, err, target, msg, args...) } // NotImplements asserts that an object does not implement the specified interface. // // a.NotImplements((*MyInterface)(nil), new(MyObject)) func (a *Assertions) NotImplements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NotImplements(a.t, interfaceObject, object, msgAndArgs...) } // NotImplementsf asserts that an object does not implement the specified interface. // // a.NotImplementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func (a *Assertions) NotImplementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NotImplementsf(a.t, interfaceObject, object, msg, args...) } // NotNil asserts that the specified object is not nil. // // a.NotNil(err) func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NotNil(a.t, object, msgAndArgs...) } // NotNilf asserts that the specified object is not nil. // // a.NotNilf(err, "error message %s", "formatted") func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NotNilf(a.t, object, msg, args...) } // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. // // a.NotPanics(func(){ RemainCalm() }) func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NotPanics(a.t, f, msgAndArgs...) } // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. // // a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") func (a *Assertions) NotPanicsf(f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NotPanicsf(a.t, f, msg, args...) } // NotRegexp asserts that a specified regexp does not match a string. // // a.NotRegexp(regexp.MustCompile("starts"), "it's starting") // a.NotRegexp("^start", "it's not starting") func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NotRegexp(a.t, rx, str, msgAndArgs...) } // NotRegexpf asserts that a specified regexp does not match a string. // // a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") // a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NotRegexpf(a.t, rx, str, msg, args...) } // NotSame asserts that two pointers do not reference the same object. // // a.NotSame(ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. func (a *Assertions) NotSame(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NotSame(a.t, expected, actual, msgAndArgs...) } // NotSamef asserts that two pointers do not reference the same object. // // a.NotSamef(ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NotSamef(a.t, expected, actual, msg, args...) } // NotSubset asserts that the specified list(array, slice...) or map does NOT // contain all elements given in the specified subset list(array, slice...) or // map. // // a.NotSubset([1, 3, 4], [1, 2]) // a.NotSubset({"x": 1, "y": 2}, {"z": 3}) func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NotSubset(a.t, list, subset, msgAndArgs...) } // NotSubsetf asserts that the specified list(array, slice...) or map does NOT // contain all elements given in the specified subset list(array, slice...) or // map. // // a.NotSubsetf([1, 3, 4], [1, 2], "error message %s", "formatted") // a.NotSubsetf({"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NotSubsetf(a.t, list, subset, msg, args...) } // NotZero asserts that i is not the zero value for its type. func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NotZero(a.t, i, msgAndArgs...) } // NotZerof asserts that i is not the zero value for its type. func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return NotZerof(a.t, i, msg, args...) } // Panics asserts that the code inside the specified PanicTestFunc panics. // // a.Panics(func(){ GoCrazy() }) func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Panics(a.t, f, msgAndArgs...) } // PanicsWithError asserts that the code inside the specified PanicTestFunc // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // // a.PanicsWithError("crazy error", func(){ GoCrazy() }) func (a *Assertions) PanicsWithError(errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return PanicsWithError(a.t, errString, f, msgAndArgs...) } // PanicsWithErrorf asserts that the code inside the specified PanicTestFunc // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // // a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) PanicsWithErrorf(errString string, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return PanicsWithErrorf(a.t, errString, f, msg, args...) } // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // // a.PanicsWithValue("crazy error", func(){ GoCrazy() }) func (a *Assertions) PanicsWithValue(expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return PanicsWithValue(a.t, expected, f, msgAndArgs...) } // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // // a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) PanicsWithValuef(expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return PanicsWithValuef(a.t, expected, f, msg, args...) } // Panicsf asserts that the code inside the specified PanicTestFunc panics. // // a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Panicsf(a.t, f, msg, args...) } // Positive asserts that the specified element is positive // // a.Positive(1) // a.Positive(1.23) func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Positive(a.t, e, msgAndArgs...) } // Positivef asserts that the specified element is positive // // a.Positivef(1, "error message %s", "formatted") // a.Positivef(1.23, "error message %s", "formatted") func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Positivef(a.t, e, msg, args...) } // Regexp asserts that a specified regexp matches a string. // // a.Regexp(regexp.MustCompile("start"), "it's starting") // a.Regexp("start...$", "it's not starting") func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Regexp(a.t, rx, str, msgAndArgs...) } // Regexpf asserts that a specified regexp matches a string. // // a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") // a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Regexpf(a.t, rx, str, msg, args...) } // Same asserts that two pointers reference the same object. // // a.Same(ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. func (a *Assertions) Same(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Same(a.t, expected, actual, msgAndArgs...) } // Samef asserts that two pointers reference the same object. // // a.Samef(ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Samef(a.t, expected, actual, msg, args...) } // Subset asserts that the specified list(array, slice...) or map contains all // elements given in the specified subset list(array, slice...) or map. // // a.Subset([1, 2, 3], [1, 2]) // a.Subset({"x": 1, "y": 2}, {"x": 1}) func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Subset(a.t, list, subset, msgAndArgs...) } // Subsetf asserts that the specified list(array, slice...) or map contains all // elements given in the specified subset list(array, slice...) or map. // // a.Subsetf([1, 2, 3], [1, 2], "error message %s", "formatted") // a.Subsetf({"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Subsetf(a.t, list, subset, msg, args...) } // True asserts that the specified value is true. // // a.True(myBool) func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return True(a.t, value, msgAndArgs...) } // Truef asserts that the specified value is true. // // a.Truef(myBool, "error message %s", "formatted") func (a *Assertions) Truef(value bool, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Truef(a.t, value, msg, args...) } // WithinDuration asserts that the two times are within duration delta of each other. // // a.WithinDuration(time.Now(), time.Now(), 10*time.Second) func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return WithinDuration(a.t, expected, actual, delta, msgAndArgs...) } // WithinDurationf asserts that the two times are within duration delta of each other. // // a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return WithinDurationf(a.t, expected, actual, delta, msg, args...) } // WithinRange asserts that a time is within a time range (inclusive). // // a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return WithinRange(a.t, actual, start, end, msgAndArgs...) } // WithinRangef asserts that a time is within a time range (inclusive). // // a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return WithinRangef(a.t, actual, start, end, msg, args...) } // YAMLEq asserts that two YAML strings are equivalent. func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return YAMLEq(a.t, expected, actual, msgAndArgs...) } // YAMLEqf asserts that two YAML strings are equivalent. func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return YAMLEqf(a.t, expected, actual, msg, args...) } // Zero asserts that i is the zero value for its type. func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Zero(a.t, i, msgAndArgs...) } // Zerof asserts that i is the zero value for its type. func (a *Assertions) Zerof(i interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return Zerof(a.t, i, msg, args...) } ================================================ FILE: vendor/github.com/stretchr/testify/assert/assertion_forward.go.tmpl ================================================ {{.CommentWithoutT "a"}} func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } return {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) } ================================================ FILE: vendor/github.com/stretchr/testify/assert/assertion_order.go ================================================ package assert import ( "fmt" "reflect" ) // isOrdered checks that collection contains orderable elements. func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool { objKind := reflect.TypeOf(object).Kind() if objKind != reflect.Slice && objKind != reflect.Array { return false } objValue := reflect.ValueOf(object) objLen := objValue.Len() if objLen <= 1 { return true } value := objValue.Index(0) valueInterface := value.Interface() firstValueKind := value.Kind() for i := 1; i < objLen; i++ { prevValue := value prevValueInterface := valueInterface value = objValue.Index(i) valueInterface = value.Interface() compareResult, isComparable := compare(prevValueInterface, valueInterface, firstValueKind) if !isComparable { return Fail(t, fmt.Sprintf("Can not compare type \"%s\" and \"%s\"", reflect.TypeOf(value), reflect.TypeOf(prevValue)), msgAndArgs...) } if !containsValue(allowedComparesResults, compareResult) { return Fail(t, fmt.Sprintf(failMessage, prevValue, value), msgAndArgs...) } } return true } // IsIncreasing asserts that the collection is increasing // // assert.IsIncreasing(t, []int{1, 2, 3}) // assert.IsIncreasing(t, []float{1, 2}) // assert.IsIncreasing(t, []string{"a", "b"}) func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) } // IsNonIncreasing asserts that the collection is not increasing // // assert.IsNonIncreasing(t, []int{2, 1, 1}) // assert.IsNonIncreasing(t, []float{2, 1}) // assert.IsNonIncreasing(t, []string{"b", "a"}) func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) } // IsDecreasing asserts that the collection is decreasing // // assert.IsDecreasing(t, []int{2, 1, 0}) // assert.IsDecreasing(t, []float{2, 1}) // assert.IsDecreasing(t, []string{"b", "a"}) func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) } // IsNonDecreasing asserts that the collection is not decreasing // // assert.IsNonDecreasing(t, []int{1, 1, 2}) // assert.IsNonDecreasing(t, []float{1, 2}) // assert.IsNonDecreasing(t, []string{"a", "b"}) func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) } ================================================ FILE: vendor/github.com/stretchr/testify/assert/assertions.go ================================================ package assert import ( "bufio" "bytes" "encoding/json" "errors" "fmt" "math" "os" "reflect" "regexp" "runtime" "runtime/debug" "strings" "time" "unicode" "unicode/utf8" "github.com/davecgh/go-spew/spew" "github.com/pmezard/go-difflib/difflib" "gopkg.in/yaml.v3" ) //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_format.go.tmpl" // TestingT is an interface wrapper around *testing.T type TestingT interface { Errorf(format string, args ...interface{}) } // ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful // for table driven tests. type ComparisonAssertionFunc func(TestingT, interface{}, interface{}, ...interface{}) bool // ValueAssertionFunc is a common function prototype when validating a single value. Can be useful // for table driven tests. type ValueAssertionFunc func(TestingT, interface{}, ...interface{}) bool // BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful // for table driven tests. type BoolAssertionFunc func(TestingT, bool, ...interface{}) bool // ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful // for table driven tests. type ErrorAssertionFunc func(TestingT, error, ...interface{}) bool // Comparison is a custom function that returns true on success and false on failure type Comparison func() (success bool) /* Helper functions */ // ObjectsAreEqual determines if two objects are considered equal. // // This function does no assertion of any kind. func ObjectsAreEqual(expected, actual interface{}) bool { if expected == nil || actual == nil { return expected == actual } exp, ok := expected.([]byte) if !ok { return reflect.DeepEqual(expected, actual) } act, ok := actual.([]byte) if !ok { return false } if exp == nil || act == nil { return exp == nil && act == nil } return bytes.Equal(exp, act) } // copyExportedFields iterates downward through nested data structures and creates a copy // that only contains the exported struct fields. func copyExportedFields(expected interface{}) interface{} { if isNil(expected) { return expected } expectedType := reflect.TypeOf(expected) expectedKind := expectedType.Kind() expectedValue := reflect.ValueOf(expected) switch expectedKind { case reflect.Struct: result := reflect.New(expectedType).Elem() for i := 0; i < expectedType.NumField(); i++ { field := expectedType.Field(i) isExported := field.IsExported() if isExported { fieldValue := expectedValue.Field(i) if isNil(fieldValue) || isNil(fieldValue.Interface()) { continue } newValue := copyExportedFields(fieldValue.Interface()) result.Field(i).Set(reflect.ValueOf(newValue)) } } return result.Interface() case reflect.Ptr: result := reflect.New(expectedType.Elem()) unexportedRemoved := copyExportedFields(expectedValue.Elem().Interface()) result.Elem().Set(reflect.ValueOf(unexportedRemoved)) return result.Interface() case reflect.Array, reflect.Slice: var result reflect.Value if expectedKind == reflect.Array { result = reflect.New(reflect.ArrayOf(expectedValue.Len(), expectedType.Elem())).Elem() } else { result = reflect.MakeSlice(expectedType, expectedValue.Len(), expectedValue.Len()) } for i := 0; i < expectedValue.Len(); i++ { index := expectedValue.Index(i) if isNil(index) { continue } unexportedRemoved := copyExportedFields(index.Interface()) result.Index(i).Set(reflect.ValueOf(unexportedRemoved)) } return result.Interface() case reflect.Map: result := reflect.MakeMap(expectedType) for _, k := range expectedValue.MapKeys() { index := expectedValue.MapIndex(k) unexportedRemoved := copyExportedFields(index.Interface()) result.SetMapIndex(k, reflect.ValueOf(unexportedRemoved)) } return result.Interface() default: return expected } } // ObjectsExportedFieldsAreEqual determines if the exported (public) fields of two objects are // considered equal. This comparison of only exported fields is applied recursively to nested data // structures. // // This function does no assertion of any kind. // // Deprecated: Use [EqualExportedValues] instead. func ObjectsExportedFieldsAreEqual(expected, actual interface{}) bool { expectedCleaned := copyExportedFields(expected) actualCleaned := copyExportedFields(actual) return ObjectsAreEqualValues(expectedCleaned, actualCleaned) } // ObjectsAreEqualValues gets whether two objects are equal, or if their // values are equal. func ObjectsAreEqualValues(expected, actual interface{}) bool { if ObjectsAreEqual(expected, actual) { return true } expectedValue := reflect.ValueOf(expected) actualValue := reflect.ValueOf(actual) if !expectedValue.IsValid() || !actualValue.IsValid() { return false } expectedType := expectedValue.Type() actualType := actualValue.Type() if !expectedType.ConvertibleTo(actualType) { return false } if !isNumericType(expectedType) || !isNumericType(actualType) { // Attempt comparison after type conversion return reflect.DeepEqual( expectedValue.Convert(actualType).Interface(), actual, ) } // If BOTH values are numeric, there are chances of false positives due // to overflow or underflow. So, we need to make sure to always convert // the smaller type to a larger type before comparing. if expectedType.Size() >= actualType.Size() { return actualValue.Convert(expectedType).Interface() == expected } return expectedValue.Convert(actualType).Interface() == actual } // isNumericType returns true if the type is one of: // int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, // float32, float64, complex64, complex128 func isNumericType(t reflect.Type) bool { return t.Kind() >= reflect.Int && t.Kind() <= reflect.Complex128 } /* CallerInfo is necessary because the assert functions use the testing object internally, causing it to print the file:line of the assert method, rather than where the problem actually occurred in calling code.*/ // CallerInfo returns an array of strings containing the file and line number // of each stack frame leading from the current test to the assert call that // failed. func CallerInfo() []string { var pc uintptr var ok bool var file string var line int var name string callers := []string{} for i := 0; ; i++ { pc, file, line, ok = runtime.Caller(i) if !ok { // The breaks below failed to terminate the loop, and we ran off the // end of the call stack. break } // This is a huge edge case, but it will panic if this is the case, see #180 if file == "" { break } f := runtime.FuncForPC(pc) if f == nil { break } name = f.Name() // testing.tRunner is the standard library function that calls // tests. Subtests are called directly by tRunner, without going through // the Test/Benchmark/Example function that contains the t.Run calls, so // with subtests we should break when we hit tRunner, without adding it // to the list of callers. if name == "testing.tRunner" { break } parts := strings.Split(file, "/") if len(parts) > 1 { filename := parts[len(parts)-1] dir := parts[len(parts)-2] if (dir != "assert" && dir != "mock" && dir != "require") || filename == "mock_test.go" { callers = append(callers, fmt.Sprintf("%s:%d", file, line)) } } // Drop the package segments := strings.Split(name, ".") name = segments[len(segments)-1] if isTest(name, "Test") || isTest(name, "Benchmark") || isTest(name, "Example") { break } } return callers } // Stolen from the `go test` tool. // isTest tells whether name looks like a test (or benchmark, according to prefix). // It is a Test (say) if there is a character after Test that is not a lower-case letter. // We don't want TesticularCancer. func isTest(name, prefix string) bool { if !strings.HasPrefix(name, prefix) { return false } if len(name) == len(prefix) { // "Test" is ok return true } r, _ := utf8.DecodeRuneInString(name[len(prefix):]) return !unicode.IsLower(r) } func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { if len(msgAndArgs) == 0 || msgAndArgs == nil { return "" } if len(msgAndArgs) == 1 { msg := msgAndArgs[0] if msgAsStr, ok := msg.(string); ok { return msgAsStr } return fmt.Sprintf("%+v", msg) } if len(msgAndArgs) > 1 { return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...) } return "" } // Aligns the provided message so that all lines after the first line start at the same location as the first line. // Assumes that the first line starts at the correct location (after carriage return, tab, label, spacer and tab). // The longestLabelLen parameter specifies the length of the longest label in the output (required because this is the // basis on which the alignment occurs). func indentMessageLines(message string, longestLabelLen int) string { outBuf := new(bytes.Buffer) for i, scanner := 0, bufio.NewScanner(strings.NewReader(message)); scanner.Scan(); i++ { // no need to align first line because it starts at the correct location (after the label) if i != 0 { // append alignLen+1 spaces to align with "{{longestLabel}}:" before adding tab outBuf.WriteString("\n\t" + strings.Repeat(" ", longestLabelLen+1) + "\t") } outBuf.WriteString(scanner.Text()) } return outBuf.String() } type failNower interface { FailNow() } // FailNow fails test func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } Fail(t, failureMessage, msgAndArgs...) // We cannot extend TestingT with FailNow() and // maintain backwards compatibility, so we fallback // to panicking when FailNow is not available in // TestingT. // See issue #263 if t, ok := t.(failNower); ok { t.FailNow() } else { panic("test failed and t is missing `FailNow()`") } return false } // Fail reports a failure through func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } content := []labeledContent{ {"Error Trace", strings.Join(CallerInfo(), "\n\t\t\t")}, {"Error", failureMessage}, } // Add test name if the Go version supports it if n, ok := t.(interface { Name() string }); ok { content = append(content, labeledContent{"Test", n.Name()}) } message := messageFromMsgAndArgs(msgAndArgs...) if len(message) > 0 { content = append(content, labeledContent{"Messages", message}) } t.Errorf("\n%s", ""+labeledOutput(content...)) return false } type labeledContent struct { label string content string } // labeledOutput returns a string consisting of the provided labeledContent. Each labeled output is appended in the following manner: // // \t{{label}}:{{align_spaces}}\t{{content}}\n // // The initial carriage return is required to undo/erase any padding added by testing.T.Errorf. The "\t{{label}}:" is for the label. // If a label is shorter than the longest label provided, padding spaces are added to make all the labels match in length. Once this // alignment is achieved, "\t{{content}}\n" is added for the output. // // If the content of the labeledOutput contains line breaks, the subsequent lines are aligned so that they start at the same location as the first line. func labeledOutput(content ...labeledContent) string { longestLabel := 0 for _, v := range content { if len(v.label) > longestLabel { longestLabel = len(v.label) } } var output string for _, v := range content { output += "\t" + v.label + ":" + strings.Repeat(" ", longestLabel-len(v.label)) + "\t" + indentMessageLines(v.content, longestLabel) + "\n" } return output } // Implements asserts that an object is implemented by the specified interface. // // assert.Implements(t, (*MyInterface)(nil), new(MyObject)) func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } interfaceType := reflect.TypeOf(interfaceObject).Elem() if object == nil { return Fail(t, fmt.Sprintf("Cannot check if nil implements %v", interfaceType), msgAndArgs...) } if !reflect.TypeOf(object).Implements(interfaceType) { return Fail(t, fmt.Sprintf("%T must implement %v", object, interfaceType), msgAndArgs...) } return true } // NotImplements asserts that an object does not implement the specified interface. // // assert.NotImplements(t, (*MyInterface)(nil), new(MyObject)) func NotImplements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } interfaceType := reflect.TypeOf(interfaceObject).Elem() if object == nil { return Fail(t, fmt.Sprintf("Cannot check if nil does not implement %v", interfaceType), msgAndArgs...) } if reflect.TypeOf(object).Implements(interfaceType) { return Fail(t, fmt.Sprintf("%T implements %v", object, interfaceType), msgAndArgs...) } return true } // IsType asserts that the specified objects are of the same type. func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } if !ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) { return Fail(t, fmt.Sprintf("Object expected to be of type %v, but was %v", reflect.TypeOf(expectedType), reflect.TypeOf(object)), msgAndArgs...) } return true } // Equal asserts that two objects are equal. // // assert.Equal(t, 123, 123) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality // cannot be determined and will always fail. func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } if err := validateEqualArgs(expected, actual); err != nil { return Fail(t, fmt.Sprintf("Invalid operation: %#v == %#v (%s)", expected, actual, err), msgAndArgs...) } if !ObjectsAreEqual(expected, actual) { diff := diff(expected, actual) expected, actual = formatUnequalValues(expected, actual) return Fail(t, fmt.Sprintf("Not equal: \n"+ "expected: %s\n"+ "actual : %s%s", expected, actual, diff), msgAndArgs...) } return true } // validateEqualArgs checks whether provided arguments can be safely used in the // Equal/NotEqual functions. func validateEqualArgs(expected, actual interface{}) error { if expected == nil && actual == nil { return nil } if isFunction(expected) || isFunction(actual) { return errors.New("cannot take func type as argument") } return nil } // Same asserts that two pointers reference the same object. // // assert.Same(t, ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } if !samePointers(expected, actual) { return Fail(t, fmt.Sprintf("Not same: \n"+ "expected: %p %#v\n"+ "actual : %p %#v", expected, expected, actual, actual), msgAndArgs...) } return true } // NotSame asserts that two pointers do not reference the same object. // // assert.NotSame(t, ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } if samePointers(expected, actual) { return Fail(t, fmt.Sprintf( "Expected and actual point to the same object: %p %#v", expected, expected), msgAndArgs...) } return true } // samePointers compares two generic interface objects and returns whether // they point to the same object func samePointers(first, second interface{}) bool { firstPtr, secondPtr := reflect.ValueOf(first), reflect.ValueOf(second) if firstPtr.Kind() != reflect.Ptr || secondPtr.Kind() != reflect.Ptr { return false } firstType, secondType := reflect.TypeOf(first), reflect.TypeOf(second) if firstType != secondType { return false } // compare pointer addresses return first == second } // formatUnequalValues takes two values of arbitrary types and returns string // representations appropriate to be presented to the user. // // If the values are not of like type, the returned strings will be prefixed // with the type name, and the value will be enclosed in parentheses similar // to a type conversion in the Go grammar. func formatUnequalValues(expected, actual interface{}) (e string, a string) { if reflect.TypeOf(expected) != reflect.TypeOf(actual) { return fmt.Sprintf("%T(%s)", expected, truncatingFormat(expected)), fmt.Sprintf("%T(%s)", actual, truncatingFormat(actual)) } switch expected.(type) { case time.Duration: return fmt.Sprintf("%v", expected), fmt.Sprintf("%v", actual) } return truncatingFormat(expected), truncatingFormat(actual) } // truncatingFormat formats the data and truncates it if it's too long. // // This helps keep formatted error messages lines from exceeding the // bufio.MaxScanTokenSize max line length that the go testing framework imposes. func truncatingFormat(data interface{}) string { value := fmt.Sprintf("%#v", data) max := bufio.MaxScanTokenSize - 100 // Give us some space the type info too if needed. if len(value) > max { value = value[0:max] + "<... truncated>" } return value } // EqualValues asserts that two objects are equal or convertible to the same types // and equal. // // assert.EqualValues(t, uint32(123), int32(123)) func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } if !ObjectsAreEqualValues(expected, actual) { diff := diff(expected, actual) expected, actual = formatUnequalValues(expected, actual) return Fail(t, fmt.Sprintf("Not equal: \n"+ "expected: %s\n"+ "actual : %s%s", expected, actual, diff), msgAndArgs...) } return true } // EqualExportedValues asserts that the types of two objects are equal and their public // fields are also equal. This is useful for comparing structs that have private fields // that could potentially differ. // // type S struct { // Exported int // notExported int // } // assert.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true // assert.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false func EqualExportedValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } aType := reflect.TypeOf(expected) bType := reflect.TypeOf(actual) if aType != bType { return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...) } if aType.Kind() == reflect.Ptr { aType = aType.Elem() } if bType.Kind() == reflect.Ptr { bType = bType.Elem() } if aType.Kind() != reflect.Struct { return Fail(t, fmt.Sprintf("Types expected to both be struct or pointer to struct \n\t%v != %v", aType.Kind(), reflect.Struct), msgAndArgs...) } if bType.Kind() != reflect.Struct { return Fail(t, fmt.Sprintf("Types expected to both be struct or pointer to struct \n\t%v != %v", bType.Kind(), reflect.Struct), msgAndArgs...) } expected = copyExportedFields(expected) actual = copyExportedFields(actual) if !ObjectsAreEqualValues(expected, actual) { diff := diff(expected, actual) expected, actual = formatUnequalValues(expected, actual) return Fail(t, fmt.Sprintf("Not equal (comparing only exported fields): \n"+ "expected: %s\n"+ "actual : %s%s", expected, actual, diff), msgAndArgs...) } return true } // Exactly asserts that two objects are equal in value and type. // // assert.Exactly(t, int32(123), int64(123)) func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } aType := reflect.TypeOf(expected) bType := reflect.TypeOf(actual) if aType != bType { return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...) } return Equal(t, expected, actual, msgAndArgs...) } // NotNil asserts that the specified object is not nil. // // assert.NotNil(t, err) func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { if !isNil(object) { return true } if h, ok := t.(tHelper); ok { h.Helper() } return Fail(t, "Expected value not to be nil.", msgAndArgs...) } // isNil checks if a specified object is nil or not, without Failing. func isNil(object interface{}) bool { if object == nil { return true } value := reflect.ValueOf(object) switch value.Kind() { case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer: return value.IsNil() } return false } // Nil asserts that the specified object is nil. // // assert.Nil(t, err) func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { if isNil(object) { return true } if h, ok := t.(tHelper); ok { h.Helper() } return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...) } // isEmpty gets whether the specified object is considered empty or not. func isEmpty(object interface{}) bool { // get nil case out of the way if object == nil { return true } objValue := reflect.ValueOf(object) switch objValue.Kind() { // collection types are empty when they have no element case reflect.Chan, reflect.Map, reflect.Slice: return objValue.Len() == 0 // pointers are empty if nil or if the value they point to is empty case reflect.Ptr: if objValue.IsNil() { return true } deref := objValue.Elem().Interface() return isEmpty(deref) // for all other types, compare against the zero value // array types are empty when they match their zero-initialized state default: zero := reflect.Zero(objValue.Type()) return reflect.DeepEqual(object, zero.Interface()) } } // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // // assert.Empty(t, obj) func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { pass := isEmpty(object) if !pass { if h, ok := t.(tHelper); ok { h.Helper() } Fail(t, fmt.Sprintf("Should be empty, but was %v", object), msgAndArgs...) } return pass } // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // // if assert.NotEmpty(t, obj) { // assert.Equal(t, "two", obj[1]) // } func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { pass := !isEmpty(object) if !pass { if h, ok := t.(tHelper); ok { h.Helper() } Fail(t, fmt.Sprintf("Should NOT be empty, but was %v", object), msgAndArgs...) } return pass } // getLen tries to get the length of an object. // It returns (0, false) if impossible. func getLen(x interface{}) (length int, ok bool) { v := reflect.ValueOf(x) defer func() { ok = recover() == nil }() return v.Len(), true } // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // // assert.Len(t, mySlice, 3) func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } l, ok := getLen(object) if !ok { return Fail(t, fmt.Sprintf("\"%v\" could not be applied builtin len()", object), msgAndArgs...) } if l != length { return Fail(t, fmt.Sprintf("\"%v\" should have %d item(s), but has %d", object, length, l), msgAndArgs...) } return true } // True asserts that the specified value is true. // // assert.True(t, myBool) func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { if !value { if h, ok := t.(tHelper); ok { h.Helper() } return Fail(t, "Should be true", msgAndArgs...) } return true } // False asserts that the specified value is false. // // assert.False(t, myBool) func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { if value { if h, ok := t.(tHelper); ok { h.Helper() } return Fail(t, "Should be false", msgAndArgs...) } return true } // NotEqual asserts that the specified values are NOT equal. // // assert.NotEqual(t, obj1, obj2) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } if err := validateEqualArgs(expected, actual); err != nil { return Fail(t, fmt.Sprintf("Invalid operation: %#v != %#v (%s)", expected, actual, err), msgAndArgs...) } if ObjectsAreEqual(expected, actual) { return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...) } return true } // NotEqualValues asserts that two objects are not equal even when converted to the same type // // assert.NotEqualValues(t, obj1, obj2) func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } if ObjectsAreEqualValues(expected, actual) { return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...) } return true } // containsElement try loop over the list check if the list includes the element. // return (false, false) if impossible. // return (true, false) if element was not found. // return (true, true) if element was found. func containsElement(list interface{}, element interface{}) (ok, found bool) { listValue := reflect.ValueOf(list) listType := reflect.TypeOf(list) if listType == nil { return false, false } listKind := listType.Kind() defer func() { if e := recover(); e != nil { ok = false found = false } }() if listKind == reflect.String { elementValue := reflect.ValueOf(element) return true, strings.Contains(listValue.String(), elementValue.String()) } if listKind == reflect.Map { mapKeys := listValue.MapKeys() for i := 0; i < len(mapKeys); i++ { if ObjectsAreEqual(mapKeys[i].Interface(), element) { return true, true } } return true, false } for i := 0; i < listValue.Len(); i++ { if ObjectsAreEqual(listValue.Index(i).Interface(), element) { return true, true } } return true, false } // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // // assert.Contains(t, "Hello World", "World") // assert.Contains(t, ["Hello", "World"], "World") // assert.Contains(t, {"Hello": "World"}, "Hello") func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } ok, found := containsElement(s, contains) if !ok { return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...) } if !found { return Fail(t, fmt.Sprintf("%#v does not contain %#v", s, contains), msgAndArgs...) } return true } // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // // assert.NotContains(t, "Hello World", "Earth") // assert.NotContains(t, ["Hello", "World"], "Earth") // assert.NotContains(t, {"Hello": "World"}, "Earth") func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } ok, found := containsElement(s, contains) if !ok { return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...) } if found { return Fail(t, fmt.Sprintf("%#v should not contain %#v", s, contains), msgAndArgs...) } return true } // Subset asserts that the specified list(array, slice...) or map contains all // elements given in the specified subset list(array, slice...) or map. // // assert.Subset(t, [1, 2, 3], [1, 2]) // assert.Subset(t, {"x": 1, "y": 2}, {"x": 1}) func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { if h, ok := t.(tHelper); ok { h.Helper() } if subset == nil { return true // we consider nil to be equal to the nil set } listKind := reflect.TypeOf(list).Kind() if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) } subsetKind := reflect.TypeOf(subset).Kind() if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) } if subsetKind == reflect.Map && listKind == reflect.Map { subsetMap := reflect.ValueOf(subset) actualMap := reflect.ValueOf(list) for _, k := range subsetMap.MapKeys() { ev := subsetMap.MapIndex(k) av := actualMap.MapIndex(k) if !av.IsValid() { return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...) } if !ObjectsAreEqual(ev.Interface(), av.Interface()) { return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, subset), msgAndArgs...) } } return true } subsetList := reflect.ValueOf(subset) for i := 0; i < subsetList.Len(); i++ { element := subsetList.Index(i).Interface() ok, found := containsElement(list, element) if !ok { return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", list), msgAndArgs...) } if !found { return Fail(t, fmt.Sprintf("%#v does not contain %#v", list, element), msgAndArgs...) } } return true } // NotSubset asserts that the specified list(array, slice...) or map does NOT // contain all elements given in the specified subset list(array, slice...) or // map. // // assert.NotSubset(t, [1, 3, 4], [1, 2]) // assert.NotSubset(t, {"x": 1, "y": 2}, {"z": 3}) func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { if h, ok := t.(tHelper); ok { h.Helper() } if subset == nil { return Fail(t, "nil is the empty set which is a subset of every set", msgAndArgs...) } listKind := reflect.TypeOf(list).Kind() if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) } subsetKind := reflect.TypeOf(subset).Kind() if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) } if subsetKind == reflect.Map && listKind == reflect.Map { subsetMap := reflect.ValueOf(subset) actualMap := reflect.ValueOf(list) for _, k := range subsetMap.MapKeys() { ev := subsetMap.MapIndex(k) av := actualMap.MapIndex(k) if !av.IsValid() { return true } if !ObjectsAreEqual(ev.Interface(), av.Interface()) { return true } } return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...) } subsetList := reflect.ValueOf(subset) for i := 0; i < subsetList.Len(); i++ { element := subsetList.Index(i).Interface() ok, found := containsElement(list, element) if !ok { return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...) } if !found { return true } } return Fail(t, fmt.Sprintf("%q is a subset of %q", subset, list), msgAndArgs...) } // ElementsMatch asserts that the specified listA(array, slice...) is equal to specified // listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, // the number of appearances of each of them in both lists should match. // // assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2]) func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface{}) (ok bool) { if h, ok := t.(tHelper); ok { h.Helper() } if isEmpty(listA) && isEmpty(listB) { return true } if !isList(t, listA, msgAndArgs...) || !isList(t, listB, msgAndArgs...) { return false } extraA, extraB := diffLists(listA, listB) if len(extraA) == 0 && len(extraB) == 0 { return true } return Fail(t, formatListDiff(listA, listB, extraA, extraB), msgAndArgs...) } // isList checks that the provided value is array or slice. func isList(t TestingT, list interface{}, msgAndArgs ...interface{}) (ok bool) { kind := reflect.TypeOf(list).Kind() if kind != reflect.Array && kind != reflect.Slice { return Fail(t, fmt.Sprintf("%q has an unsupported type %s, expecting array or slice", list, kind), msgAndArgs...) } return true } // diffLists diffs two arrays/slices and returns slices of elements that are only in A and only in B. // If some element is present multiple times, each instance is counted separately (e.g. if something is 2x in A and // 5x in B, it will be 0x in extraA and 3x in extraB). The order of items in both lists is ignored. func diffLists(listA, listB interface{}) (extraA, extraB []interface{}) { aValue := reflect.ValueOf(listA) bValue := reflect.ValueOf(listB) aLen := aValue.Len() bLen := bValue.Len() // Mark indexes in bValue that we already used visited := make([]bool, bLen) for i := 0; i < aLen; i++ { element := aValue.Index(i).Interface() found := false for j := 0; j < bLen; j++ { if visited[j] { continue } if ObjectsAreEqual(bValue.Index(j).Interface(), element) { visited[j] = true found = true break } } if !found { extraA = append(extraA, element) } } for j := 0; j < bLen; j++ { if visited[j] { continue } extraB = append(extraB, bValue.Index(j).Interface()) } return } func formatListDiff(listA, listB interface{}, extraA, extraB []interface{}) string { var msg bytes.Buffer msg.WriteString("elements differ") if len(extraA) > 0 { msg.WriteString("\n\nextra elements in list A:\n") msg.WriteString(spewConfig.Sdump(extraA)) } if len(extraB) > 0 { msg.WriteString("\n\nextra elements in list B:\n") msg.WriteString(spewConfig.Sdump(extraB)) } msg.WriteString("\n\nlistA:\n") msg.WriteString(spewConfig.Sdump(listA)) msg.WriteString("\n\nlistB:\n") msg.WriteString(spewConfig.Sdump(listB)) return msg.String() } // Condition uses a Comparison to assert a complex condition. func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } result := comp() if !result { Fail(t, "Condition failed!", msgAndArgs...) } return result } // PanicTestFunc defines a func that should be passed to the assert.Panics and assert.NotPanics // methods, and represents a simple func that takes no arguments, and returns nothing. type PanicTestFunc func() // didPanic returns true if the function passed to it panics. Otherwise, it returns false. func didPanic(f PanicTestFunc) (didPanic bool, message interface{}, stack string) { didPanic = true defer func() { message = recover() if didPanic { stack = string(debug.Stack()) } }() // call the target function f() didPanic = false return } // Panics asserts that the code inside the specified PanicTestFunc panics. // // assert.Panics(t, func(){ GoCrazy() }) func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } if funcDidPanic, panicValue, _ := didPanic(f); !funcDidPanic { return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) } return true } // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // // assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } funcDidPanic, panicValue, panickedStack := didPanic(f) if !funcDidPanic { return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) } if panicValue != expected { return Fail(t, fmt.Sprintf("func %#v should panic with value:\t%#v\n\tPanic value:\t%#v\n\tPanic stack:\t%s", f, expected, panicValue, panickedStack), msgAndArgs...) } return true } // PanicsWithError asserts that the code inside the specified PanicTestFunc // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // // assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } funcDidPanic, panicValue, panickedStack := didPanic(f) if !funcDidPanic { return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) } panicErr, ok := panicValue.(error) if !ok || panicErr.Error() != errString { return Fail(t, fmt.Sprintf("func %#v should panic with error message:\t%#v\n\tPanic value:\t%#v\n\tPanic stack:\t%s", f, errString, panicValue, panickedStack), msgAndArgs...) } return true } // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. // // assert.NotPanics(t, func(){ RemainCalm() }) func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } if funcDidPanic, panicValue, panickedStack := didPanic(f); funcDidPanic { return Fail(t, fmt.Sprintf("func %#v should not panic\n\tPanic value:\t%v\n\tPanic stack:\t%s", f, panicValue, panickedStack), msgAndArgs...) } return true } // WithinDuration asserts that the two times are within duration delta of each other. // // assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } dt := expected.Sub(actual) if dt < -delta || dt > delta { return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...) } return true } // WithinRange asserts that a time is within a time range (inclusive). // // assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) func WithinRange(t TestingT, actual, start, end time.Time, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } if end.Before(start) { return Fail(t, "Start should be before end", msgAndArgs...) } if actual.Before(start) { return Fail(t, fmt.Sprintf("Time %v expected to be in time range %v to %v, but is before the range", actual, start, end), msgAndArgs...) } else if actual.After(end) { return Fail(t, fmt.Sprintf("Time %v expected to be in time range %v to %v, but is after the range", actual, start, end), msgAndArgs...) } return true } func toFloat(x interface{}) (float64, bool) { var xf float64 xok := true switch xn := x.(type) { case uint: xf = float64(xn) case uint8: xf = float64(xn) case uint16: xf = float64(xn) case uint32: xf = float64(xn) case uint64: xf = float64(xn) case int: xf = float64(xn) case int8: xf = float64(xn) case int16: xf = float64(xn) case int32: xf = float64(xn) case int64: xf = float64(xn) case float32: xf = float64(xn) case float64: xf = xn case time.Duration: xf = float64(xn) default: xok = false } return xf, xok } // InDelta asserts that the two numerals are within delta of each other. // // assert.InDelta(t, math.Pi, 22/7.0, 0.01) func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } af, aok := toFloat(expected) bf, bok := toFloat(actual) if !aok || !bok { return Fail(t, "Parameters must be numerical", msgAndArgs...) } if math.IsNaN(af) && math.IsNaN(bf) { return true } if math.IsNaN(af) { return Fail(t, "Expected must not be NaN", msgAndArgs...) } if math.IsNaN(bf) { return Fail(t, fmt.Sprintf("Expected %v with delta %v, but was NaN", expected, delta), msgAndArgs...) } dt := af - bf if dt < -delta || dt > delta { return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...) } return true } // InDeltaSlice is the same as InDelta, except it compares two slices. func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } if expected == nil || actual == nil || reflect.TypeOf(actual).Kind() != reflect.Slice || reflect.TypeOf(expected).Kind() != reflect.Slice { return Fail(t, "Parameters must be slice", msgAndArgs...) } actualSlice := reflect.ValueOf(actual) expectedSlice := reflect.ValueOf(expected) for i := 0; i < actualSlice.Len(); i++ { result := InDelta(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), delta, msgAndArgs...) if !result { return result } } return true } // InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. func InDeltaMapValues(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } if expected == nil || actual == nil || reflect.TypeOf(actual).Kind() != reflect.Map || reflect.TypeOf(expected).Kind() != reflect.Map { return Fail(t, "Arguments must be maps", msgAndArgs...) } expectedMap := reflect.ValueOf(expected) actualMap := reflect.ValueOf(actual) if expectedMap.Len() != actualMap.Len() { return Fail(t, "Arguments must have the same number of keys", msgAndArgs...) } for _, k := range expectedMap.MapKeys() { ev := expectedMap.MapIndex(k) av := actualMap.MapIndex(k) if !ev.IsValid() { return Fail(t, fmt.Sprintf("missing key %q in expected map", k), msgAndArgs...) } if !av.IsValid() { return Fail(t, fmt.Sprintf("missing key %q in actual map", k), msgAndArgs...) } if !InDelta( t, ev.Interface(), av.Interface(), delta, msgAndArgs..., ) { return false } } return true } func calcRelativeError(expected, actual interface{}) (float64, error) { af, aok := toFloat(expected) bf, bok := toFloat(actual) if !aok || !bok { return 0, fmt.Errorf("Parameters must be numerical") } if math.IsNaN(af) && math.IsNaN(bf) { return 0, nil } if math.IsNaN(af) { return 0, errors.New("expected value must not be NaN") } if af == 0 { return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error") } if math.IsNaN(bf) { return 0, errors.New("actual value must not be NaN") } return math.Abs(af-bf) / math.Abs(af), nil } // InEpsilon asserts that expected and actual have a relative error less than epsilon func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } if math.IsNaN(epsilon) { return Fail(t, "epsilon must not be NaN", msgAndArgs...) } actualEpsilon, err := calcRelativeError(expected, actual) if err != nil { return Fail(t, err.Error(), msgAndArgs...) } if actualEpsilon > epsilon { return Fail(t, fmt.Sprintf("Relative error is too high: %#v (expected)\n"+ " < %#v (actual)", epsilon, actualEpsilon), msgAndArgs...) } return true } // InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } if expected == nil || actual == nil { return Fail(t, "Parameters must be slice", msgAndArgs...) } expectedSlice := reflect.ValueOf(expected) actualSlice := reflect.ValueOf(actual) if expectedSlice.Type().Kind() != reflect.Slice { return Fail(t, "Expected value must be slice", msgAndArgs...) } expectedLen := expectedSlice.Len() if !IsType(t, expected, actual) || !Len(t, actual, expectedLen) { return false } for i := 0; i < expectedLen; i++ { if !InEpsilon(t, expectedSlice.Index(i).Interface(), actualSlice.Index(i).Interface(), epsilon, "at index %d", i) { return false } } return true } /* Errors */ // NoError asserts that a function returned no error (i.e. `nil`). // // actualObj, err := SomeFunction() // if assert.NoError(t, err) { // assert.Equal(t, expectedObj, actualObj) // } func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { if err != nil { if h, ok := t.(tHelper); ok { h.Helper() } return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...) } return true } // Error asserts that a function returned an error (i.e. not `nil`). // // actualObj, err := SomeFunction() // if assert.Error(t, err) { // assert.Equal(t, expectedError, err) // } func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { if err == nil { if h, ok := t.(tHelper); ok { h.Helper() } return Fail(t, "An error is expected but got nil.", msgAndArgs...) } return true } // EqualError asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() // assert.EqualError(t, err, expectedErrorString) func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } if !Error(t, theError, msgAndArgs...) { return false } expected := errString actual := theError.Error() // don't need to use deep equals here, we know they are both strings if expected != actual { return Fail(t, fmt.Sprintf("Error message not equal:\n"+ "expected: %q\n"+ "actual : %q", expected, actual), msgAndArgs...) } return true } // ErrorContains asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // // actualObj, err := SomeFunction() // assert.ErrorContains(t, err, expectedErrorSubString) func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } if !Error(t, theError, msgAndArgs...) { return false } actual := theError.Error() if !strings.Contains(actual, contains) { return Fail(t, fmt.Sprintf("Error %#v does not contain %#v", actual, contains), msgAndArgs...) } return true } // matchRegexp return true if a specified regexp matches a string. func matchRegexp(rx interface{}, str interface{}) bool { var r *regexp.Regexp if rr, ok := rx.(*regexp.Regexp); ok { r = rr } else { r = regexp.MustCompile(fmt.Sprint(rx)) } return (r.FindStringIndex(fmt.Sprint(str)) != nil) } // Regexp asserts that a specified regexp matches a string. // // assert.Regexp(t, regexp.MustCompile("start"), "it's starting") // assert.Regexp(t, "start...$", "it's not starting") func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } match := matchRegexp(rx, str) if !match { Fail(t, fmt.Sprintf("Expect \"%v\" to match \"%v\"", str, rx), msgAndArgs...) } return match } // NotRegexp asserts that a specified regexp does not match a string. // // assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") // assert.NotRegexp(t, "^start", "it's not starting") func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } match := matchRegexp(rx, str) if match { Fail(t, fmt.Sprintf("Expect \"%v\" to NOT match \"%v\"", str, rx), msgAndArgs...) } return !match } // Zero asserts that i is the zero value for its type. func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } if i != nil && !reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) { return Fail(t, fmt.Sprintf("Should be zero, but was %v", i), msgAndArgs...) } return true } // NotZero asserts that i is not the zero value for its type. func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } if i == nil || reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) { return Fail(t, fmt.Sprintf("Should not be zero, but was %v", i), msgAndArgs...) } return true } // FileExists checks whether a file exists in the given path. It also fails if // the path points to a directory or there is an error when trying to check the file. func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } info, err := os.Lstat(path) if err != nil { if os.IsNotExist(err) { return Fail(t, fmt.Sprintf("unable to find file %q", path), msgAndArgs...) } return Fail(t, fmt.Sprintf("error when running os.Lstat(%q): %s", path, err), msgAndArgs...) } if info.IsDir() { return Fail(t, fmt.Sprintf("%q is a directory", path), msgAndArgs...) } return true } // NoFileExists checks whether a file does not exist in a given path. It fails // if the path points to an existing _file_ only. func NoFileExists(t TestingT, path string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } info, err := os.Lstat(path) if err != nil { return true } if info.IsDir() { return true } return Fail(t, fmt.Sprintf("file %q exists", path), msgAndArgs...) } // DirExists checks whether a directory exists in the given path. It also fails // if the path is a file rather a directory or there is an error checking whether it exists. func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } info, err := os.Lstat(path) if err != nil { if os.IsNotExist(err) { return Fail(t, fmt.Sprintf("unable to find file %q", path), msgAndArgs...) } return Fail(t, fmt.Sprintf("error when running os.Lstat(%q): %s", path, err), msgAndArgs...) } if !info.IsDir() { return Fail(t, fmt.Sprintf("%q is a file", path), msgAndArgs...) } return true } // NoDirExists checks whether a directory does not exist in the given path. // It fails if the path points to an existing _directory_ only. func NoDirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } info, err := os.Lstat(path) if err != nil { if os.IsNotExist(err) { return true } return true } if !info.IsDir() { return true } return Fail(t, fmt.Sprintf("directory %q exists", path), msgAndArgs...) } // JSONEq asserts that two JSON strings are equivalent. // // assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } var expectedJSONAsInterface, actualJSONAsInterface interface{} if err := json.Unmarshal([]byte(expected), &expectedJSONAsInterface); err != nil { return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid json.\nJSON parsing error: '%s'", expected, err.Error()), msgAndArgs...) } if err := json.Unmarshal([]byte(actual), &actualJSONAsInterface); err != nil { return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid json.\nJSON parsing error: '%s'", actual, err.Error()), msgAndArgs...) } return Equal(t, expectedJSONAsInterface, actualJSONAsInterface, msgAndArgs...) } // YAMLEq asserts that two YAML strings are equivalent. func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } var expectedYAMLAsInterface, actualYAMLAsInterface interface{} if err := yaml.Unmarshal([]byte(expected), &expectedYAMLAsInterface); err != nil { return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid yaml.\nYAML parsing error: '%s'", expected, err.Error()), msgAndArgs...) } if err := yaml.Unmarshal([]byte(actual), &actualYAMLAsInterface); err != nil { return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid yaml.\nYAML error: '%s'", actual, err.Error()), msgAndArgs...) } return Equal(t, expectedYAMLAsInterface, actualYAMLAsInterface, msgAndArgs...) } func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) { t := reflect.TypeOf(v) k := t.Kind() if k == reflect.Ptr { t = t.Elem() k = t.Kind() } return t, k } // diff returns a diff of both values as long as both are of the same type and // are a struct, map, slice, array or string. Otherwise it returns an empty string. func diff(expected interface{}, actual interface{}) string { if expected == nil || actual == nil { return "" } et, ek := typeAndKind(expected) at, _ := typeAndKind(actual) if et != at { return "" } if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array && ek != reflect.String { return "" } var e, a string switch et { case reflect.TypeOf(""): e = reflect.ValueOf(expected).String() a = reflect.ValueOf(actual).String() case reflect.TypeOf(time.Time{}): e = spewConfigStringerEnabled.Sdump(expected) a = spewConfigStringerEnabled.Sdump(actual) default: e = spewConfig.Sdump(expected) a = spewConfig.Sdump(actual) } diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ A: difflib.SplitLines(e), B: difflib.SplitLines(a), FromFile: "Expected", FromDate: "", ToFile: "Actual", ToDate: "", Context: 1, }) return "\n\nDiff:\n" + diff } func isFunction(arg interface{}) bool { if arg == nil { return false } return reflect.TypeOf(arg).Kind() == reflect.Func } var spewConfig = spew.ConfigState{ Indent: " ", DisablePointerAddresses: true, DisableCapacities: true, SortKeys: true, DisableMethods: true, MaxDepth: 10, } var spewConfigStringerEnabled = spew.ConfigState{ Indent: " ", DisablePointerAddresses: true, DisableCapacities: true, SortKeys: true, MaxDepth: 10, } type tHelper interface { Helper() } // Eventually asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // // assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } ch := make(chan bool, 1) timer := time.NewTimer(waitFor) defer timer.Stop() ticker := time.NewTicker(tick) defer ticker.Stop() for tick := ticker.C; ; { select { case <-timer.C: return Fail(t, "Condition never satisfied", msgAndArgs...) case <-tick: tick = nil go func() { ch <- condition() }() case v := <-ch: if v { return true } tick = ticker.C } } } // CollectT implements the TestingT interface and collects all errors. type CollectT struct { errors []error } // Errorf collects the error. func (c *CollectT) Errorf(format string, args ...interface{}) { c.errors = append(c.errors, fmt.Errorf(format, args...)) } // FailNow panics. func (*CollectT) FailNow() { panic("Assertion failed") } // Deprecated: That was a method for internal usage that should not have been published. Now just panics. func (*CollectT) Reset() { panic("Reset() is deprecated") } // Deprecated: That was a method for internal usage that should not have been published. Now just panics. func (*CollectT) Copy(TestingT) { panic("Copy() is deprecated") } // EventuallyWithT asserts that given condition will be met in waitFor time, // periodically checking target function each tick. In contrast to Eventually, // it supplies a CollectT to the condition function, so that the condition // function can use the CollectT to call other assertions. // The condition is considered "met" if no errors are raised in a tick. // The supplied CollectT collects all errors from one tick (if there are any). // If the condition is not met before waitFor, the collected errors of // the last tick are copied to t. // // externalValue := false // go func() { // time.Sleep(8*time.Second) // externalValue = true // }() // assert.EventuallyWithT(t, func(c *assert.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // assert.True(c, externalValue, "expected 'externalValue' to be true") // }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } var lastFinishedTickErrs []error ch := make(chan []error, 1) timer := time.NewTimer(waitFor) defer timer.Stop() ticker := time.NewTicker(tick) defer ticker.Stop() for tick := ticker.C; ; { select { case <-timer.C: for _, err := range lastFinishedTickErrs { t.Errorf("%v", err) } return Fail(t, "Condition never satisfied", msgAndArgs...) case <-tick: tick = nil go func() { collect := new(CollectT) defer func() { ch <- collect.errors }() condition(collect) }() case errs := <-ch: if len(errs) == 0 { return true } // Keep the errors from the last ended condition, so that they can be copied to t if timeout is reached. lastFinishedTickErrs = errs tick = ticker.C } } } // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // // assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } ch := make(chan bool, 1) timer := time.NewTimer(waitFor) defer timer.Stop() ticker := time.NewTicker(tick) defer ticker.Stop() for tick := ticker.C; ; { select { case <-timer.C: return true case <-tick: tick = nil go func() { ch <- condition() }() case v := <-ch: if v { return Fail(t, "Condition satisfied", msgAndArgs...) } tick = ticker.C } } } // ErrorIs asserts that at least one of the errors in err's chain matches target. // This is a wrapper for errors.Is. func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } if errors.Is(err, target) { return true } var expectedText string if target != nil { expectedText = target.Error() } chain := buildErrorChainString(err) return Fail(t, fmt.Sprintf("Target error should be in err chain:\n"+ "expected: %q\n"+ "in chain: %s", expectedText, chain, ), msgAndArgs...) } // NotErrorIs asserts that at none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } if !errors.Is(err, target) { return true } var expectedText string if target != nil { expectedText = target.Error() } chain := buildErrorChainString(err) return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+ "found: %q\n"+ "in chain: %s", expectedText, chain, ), msgAndArgs...) } // ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. // This is a wrapper for errors.As. func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } if errors.As(err, target) { return true } chain := buildErrorChainString(err) return Fail(t, fmt.Sprintf("Should be in error chain:\n"+ "expected: %q\n"+ "in chain: %s", target, chain, ), msgAndArgs...) } func buildErrorChainString(err error) string { if err == nil { return "" } e := errors.Unwrap(err) chain := fmt.Sprintf("%q", err.Error()) for e != nil { chain += fmt.Sprintf("\n\t%q", e.Error()) e = errors.Unwrap(e) } return chain } ================================================ FILE: vendor/github.com/stretchr/testify/assert/doc.go ================================================ // Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. // // # Example Usage // // The following is a complete example using assert in a standard test function: // // import ( // "testing" // "github.com/stretchr/testify/assert" // ) // // func TestSomething(t *testing.T) { // // var a string = "Hello" // var b string = "Hello" // // assert.Equal(t, a, b, "The two words should be the same.") // // } // // if you assert many times, use the format below: // // import ( // "testing" // "github.com/stretchr/testify/assert" // ) // // func TestSomething(t *testing.T) { // assert := assert.New(t) // // var a string = "Hello" // var b string = "Hello" // // assert.Equal(a, b, "The two words should be the same.") // } // // # Assertions // // Assertions allow you to easily write test code, and are global funcs in the `assert` package. // All assertion functions take, as the first argument, the `*testing.T` object provided by the // testing framework. This allows the assertion funcs to write the failings and other details to // the correct place. // // Every assertion function also takes an optional string message as the final argument, // allowing custom error messages to be appended to the message the assertion method outputs. package assert ================================================ FILE: vendor/github.com/stretchr/testify/assert/errors.go ================================================ package assert import ( "errors" ) // AnError is an error instance useful for testing. If the code does not care // about error specifics, and only needs to return the error for example, this // error should be used to make the test code more readable. var AnError = errors.New("assert.AnError general error for testing") ================================================ FILE: vendor/github.com/stretchr/testify/assert/forward_assertions.go ================================================ package assert // Assertions provides assertion methods around the // TestingT interface. type Assertions struct { t TestingT } // New makes a new Assertions object for the specified TestingT. func New(t TestingT) *Assertions { return &Assertions{ t: t, } } //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_forward.go.tmpl -include-format-funcs" ================================================ FILE: vendor/github.com/stretchr/testify/assert/http_assertions.go ================================================ package assert import ( "fmt" "net/http" "net/http/httptest" "net/url" "strings" ) // httpCode is a helper that returns HTTP code of the response. It returns -1 and // an error if building a new request fails. func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (int, error) { w := httptest.NewRecorder() req, err := http.NewRequest(method, url, http.NoBody) if err != nil { return -1, err } req.URL.RawQuery = values.Encode() handler(w, req) return w.Code, nil } // HTTPSuccess asserts that a specified handler returns a success status code. // // assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } code, err := httpCode(handler, method, url, values) if err != nil { Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...) } isSuccessCode := code >= http.StatusOK && code <= http.StatusPartialContent if !isSuccessCode { Fail(t, fmt.Sprintf("Expected HTTP success status code for %q but received %d", url+"?"+values.Encode(), code), msgAndArgs...) } return isSuccessCode } // HTTPRedirect asserts that a specified handler returns a redirect status code. // // assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } code, err := httpCode(handler, method, url, values) if err != nil { Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...) } isRedirectCode := code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect if !isRedirectCode { Fail(t, fmt.Sprintf("Expected HTTP redirect status code for %q but received %d", url+"?"+values.Encode(), code), msgAndArgs...) } return isRedirectCode } // HTTPError asserts that a specified handler returns an error status code. // // assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } code, err := httpCode(handler, method, url, values) if err != nil { Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...) } isErrorCode := code >= http.StatusBadRequest if !isErrorCode { Fail(t, fmt.Sprintf("Expected HTTP error status code for %q but received %d", url+"?"+values.Encode(), code), msgAndArgs...) } return isErrorCode } // HTTPStatusCode asserts that a specified handler returns a specified status code. // // assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) // // Returns whether the assertion was successful (true) or not (false). func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } code, err := httpCode(handler, method, url, values) if err != nil { Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...) } successful := code == statuscode if !successful { Fail(t, fmt.Sprintf("Expected HTTP status code %d for %q but received %d", statuscode, url+"?"+values.Encode(), code), msgAndArgs...) } return successful } // HTTPBody is a helper that returns HTTP body of the response. It returns // empty string if building a new request fails. func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string { w := httptest.NewRecorder() if len(values) > 0 { url += "?" + values.Encode() } req, err := http.NewRequest(method, url, http.NoBody) if err != nil { return "" } handler(w, req) return w.Body.String() } // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // // assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } body := HTTPBody(handler, method, url, values) contains := strings.Contains(body, fmt.Sprint(str)) if !contains { Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body), msgAndArgs...) } return contains } // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // // assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } body := HTTPBody(handler, method, url, values) contains := strings.Contains(body, fmt.Sprint(str)) if contains { Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body), msgAndArgs...) } return !contains } ================================================ FILE: vendor/github.com/stretchr/testify/require/doc.go ================================================ // Package require implements the same assertions as the `assert` package but // stops test execution when a test fails. // // # Example Usage // // The following is a complete example using require in a standard test function: // // import ( // "testing" // "github.com/stretchr/testify/require" // ) // // func TestSomething(t *testing.T) { // // var a string = "Hello" // var b string = "Hello" // // require.Equal(t, a, b, "The two words should be the same.") // // } // // # Assertions // // The `require` package have same global functions as in the `assert` package, // but instead of returning a boolean result they call `t.FailNow()`. // // Every assertion function also takes an optional string message as the final argument, // allowing custom error messages to be appended to the message the assertion method outputs. package require ================================================ FILE: vendor/github.com/stretchr/testify/require/forward_requirements.go ================================================ package require // Assertions provides assertion methods around the // TestingT interface. type Assertions struct { t TestingT } // New makes a new Assertions object for the specified TestingT. func New(t TestingT) *Assertions { return &Assertions{ t: t, } } //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=require -template=require_forward.go.tmpl -include-format-funcs" ================================================ FILE: vendor/github.com/stretchr/testify/require/require.go ================================================ // Code generated with github.com/stretchr/testify/_codegen; DO NOT EDIT. package require import ( assert "github.com/stretchr/testify/assert" http "net/http" url "net/url" time "time" ) // Condition uses a Comparison to assert a complex condition. func Condition(t TestingT, comp assert.Comparison, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Condition(t, comp, msgAndArgs...) { return } t.FailNow() } // Conditionf uses a Comparison to assert a complex condition. func Conditionf(t TestingT, comp assert.Comparison, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Conditionf(t, comp, msg, args...) { return } t.FailNow() } // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // // assert.Contains(t, "Hello World", "World") // assert.Contains(t, ["Hello", "World"], "World") // assert.Contains(t, {"Hello": "World"}, "Hello") func Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Contains(t, s, contains, msgAndArgs...) { return } t.FailNow() } // Containsf asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // // assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") // assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") // assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Containsf(t, s, contains, msg, args...) { return } t.FailNow() } // DirExists checks whether a directory exists in the given path. It also fails // if the path is a file rather a directory or there is an error checking whether it exists. func DirExists(t TestingT, path string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.DirExists(t, path, msgAndArgs...) { return } t.FailNow() } // DirExistsf checks whether a directory exists in the given path. It also fails // if the path is a file rather a directory or there is an error checking whether it exists. func DirExistsf(t TestingT, path string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.DirExistsf(t, path, msg, args...) { return } t.FailNow() } // ElementsMatch asserts that the specified listA(array, slice...) is equal to specified // listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, // the number of appearances of each of them in both lists should match. // // assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2]) func ElementsMatch(t TestingT, listA interface{}, listB interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.ElementsMatch(t, listA, listB, msgAndArgs...) { return } t.FailNow() } // ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified // listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, // the number of appearances of each of them in both lists should match. // // assert.ElementsMatchf(t, [1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.ElementsMatchf(t, listA, listB, msg, args...) { return } t.FailNow() } // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // // assert.Empty(t, obj) func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Empty(t, object, msgAndArgs...) { return } t.FailNow() } // Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // // assert.Emptyf(t, obj, "error message %s", "formatted") func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Emptyf(t, object, msg, args...) { return } t.FailNow() } // Equal asserts that two objects are equal. // // assert.Equal(t, 123, 123) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality // cannot be determined and will always fail. func Equal(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Equal(t, expected, actual, msgAndArgs...) { return } t.FailNow() } // EqualError asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() // assert.EqualError(t, err, expectedErrorString) func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.EqualError(t, theError, errString, msgAndArgs...) { return } t.FailNow() } // EqualErrorf asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() // assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.EqualErrorf(t, theError, errString, msg, args...) { return } t.FailNow() } // EqualExportedValues asserts that the types of two objects are equal and their public // fields are also equal. This is useful for comparing structs that have private fields // that could potentially differ. // // type S struct { // Exported int // notExported int // } // assert.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true // assert.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false func EqualExportedValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.EqualExportedValues(t, expected, actual, msgAndArgs...) { return } t.FailNow() } // EqualExportedValuesf asserts that the types of two objects are equal and their public // fields are also equal. This is useful for comparing structs that have private fields // that could potentially differ. // // type S struct { // Exported int // notExported int // } // assert.EqualExportedValuesf(t, S{1, 2}, S{1, 3}, "error message %s", "formatted") => true // assert.EqualExportedValuesf(t, S{1, 2}, S{2, 3}, "error message %s", "formatted") => false func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.EqualExportedValuesf(t, expected, actual, msg, args...) { return } t.FailNow() } // EqualValues asserts that two objects are equal or convertible to the same types // and equal. // // assert.EqualValues(t, uint32(123), int32(123)) func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.EqualValues(t, expected, actual, msgAndArgs...) { return } t.FailNow() } // EqualValuesf asserts that two objects are equal or convertible to the same types // and equal. // // assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.EqualValuesf(t, expected, actual, msg, args...) { return } t.FailNow() } // Equalf asserts that two objects are equal. // // assert.Equalf(t, 123, 123, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality // cannot be determined and will always fail. func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Equalf(t, expected, actual, msg, args...) { return } t.FailNow() } // Error asserts that a function returned an error (i.e. not `nil`). // // actualObj, err := SomeFunction() // if assert.Error(t, err) { // assert.Equal(t, expectedError, err) // } func Error(t TestingT, err error, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Error(t, err, msgAndArgs...) { return } t.FailNow() } // ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. // This is a wrapper for errors.As. func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.ErrorAs(t, err, target, msgAndArgs...) { return } t.FailNow() } // ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. // This is a wrapper for errors.As. func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.ErrorAsf(t, err, target, msg, args...) { return } t.FailNow() } // ErrorContains asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // // actualObj, err := SomeFunction() // assert.ErrorContains(t, err, expectedErrorSubString) func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.ErrorContains(t, theError, contains, msgAndArgs...) { return } t.FailNow() } // ErrorContainsf asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // // actualObj, err := SomeFunction() // assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.ErrorContainsf(t, theError, contains, msg, args...) { return } t.FailNow() } // ErrorIs asserts that at least one of the errors in err's chain matches target. // This is a wrapper for errors.Is. func ErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.ErrorIs(t, err, target, msgAndArgs...) { return } t.FailNow() } // ErrorIsf asserts that at least one of the errors in err's chain matches target. // This is a wrapper for errors.Is. func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.ErrorIsf(t, err, target, msg, args...) { return } t.FailNow() } // Errorf asserts that a function returned an error (i.e. not `nil`). // // actualObj, err := SomeFunction() // if assert.Errorf(t, err, "error message %s", "formatted") { // assert.Equal(t, expectedErrorf, err) // } func Errorf(t TestingT, err error, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Errorf(t, err, msg, args...) { return } t.FailNow() } // Eventually asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // // assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Eventually(t, condition, waitFor, tick, msgAndArgs...) { return } t.FailNow() } // EventuallyWithT asserts that given condition will be met in waitFor time, // periodically checking target function each tick. In contrast to Eventually, // it supplies a CollectT to the condition function, so that the condition // function can use the CollectT to call other assertions. // The condition is considered "met" if no errors are raised in a tick. // The supplied CollectT collects all errors from one tick (if there are any). // If the condition is not met before waitFor, the collected errors of // the last tick are copied to t. // // externalValue := false // go func() { // time.Sleep(8*time.Second) // externalValue = true // }() // assert.EventuallyWithT(t, func(c *assert.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // assert.True(c, externalValue, "expected 'externalValue' to be true") // }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") func EventuallyWithT(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.EventuallyWithT(t, condition, waitFor, tick, msgAndArgs...) { return } t.FailNow() } // EventuallyWithTf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. In contrast to Eventually, // it supplies a CollectT to the condition function, so that the condition // function can use the CollectT to call other assertions. // The condition is considered "met" if no errors are raised in a tick. // The supplied CollectT collects all errors from one tick (if there are any). // If the condition is not met before waitFor, the collected errors of // the last tick are copied to t. // // externalValue := false // go func() { // time.Sleep(8*time.Second) // externalValue = true // }() // assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") { // // add assertions as needed; any assertion failure will fail the current tick // assert.True(c, externalValue, "expected 'externalValue' to be true") // }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") func EventuallyWithTf(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.EventuallyWithTf(t, condition, waitFor, tick, msg, args...) { return } t.FailNow() } // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // // assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Eventuallyf(t, condition, waitFor, tick, msg, args...) { return } t.FailNow() } // Exactly asserts that two objects are equal in value and type. // // assert.Exactly(t, int32(123), int64(123)) func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Exactly(t, expected, actual, msgAndArgs...) { return } t.FailNow() } // Exactlyf asserts that two objects are equal in value and type. // // assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Exactlyf(t, expected, actual, msg, args...) { return } t.FailNow() } // Fail reports a failure through func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Fail(t, failureMessage, msgAndArgs...) { return } t.FailNow() } // FailNow fails test func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.FailNow(t, failureMessage, msgAndArgs...) { return } t.FailNow() } // FailNowf fails test func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.FailNowf(t, failureMessage, msg, args...) { return } t.FailNow() } // Failf reports a failure through func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Failf(t, failureMessage, msg, args...) { return } t.FailNow() } // False asserts that the specified value is false. // // assert.False(t, myBool) func False(t TestingT, value bool, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.False(t, value, msgAndArgs...) { return } t.FailNow() } // Falsef asserts that the specified value is false. // // assert.Falsef(t, myBool, "error message %s", "formatted") func Falsef(t TestingT, value bool, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Falsef(t, value, msg, args...) { return } t.FailNow() } // FileExists checks whether a file exists in the given path. It also fails if // the path points to a directory or there is an error when trying to check the file. func FileExists(t TestingT, path string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.FileExists(t, path, msgAndArgs...) { return } t.FailNow() } // FileExistsf checks whether a file exists in the given path. It also fails if // the path points to a directory or there is an error when trying to check the file. func FileExistsf(t TestingT, path string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.FileExistsf(t, path, msg, args...) { return } t.FailNow() } // Greater asserts that the first element is greater than the second // // assert.Greater(t, 2, 1) // assert.Greater(t, float64(2), float64(1)) // assert.Greater(t, "b", "a") func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Greater(t, e1, e2, msgAndArgs...) { return } t.FailNow() } // GreaterOrEqual asserts that the first element is greater than or equal to the second // // assert.GreaterOrEqual(t, 2, 1) // assert.GreaterOrEqual(t, 2, 2) // assert.GreaterOrEqual(t, "b", "a") // assert.GreaterOrEqual(t, "b", "b") func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.GreaterOrEqual(t, e1, e2, msgAndArgs...) { return } t.FailNow() } // GreaterOrEqualf asserts that the first element is greater than or equal to the second // // assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") // assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") // assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") // assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.GreaterOrEqualf(t, e1, e2, msg, args...) { return } t.FailNow() } // Greaterf asserts that the first element is greater than the second // // assert.Greaterf(t, 2, 1, "error message %s", "formatted") // assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") // assert.Greaterf(t, "b", "a", "error message %s", "formatted") func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Greaterf(t, e1, e2, msg, args...) { return } t.FailNow() } // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // // assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.HTTPBodyContains(t, handler, method, url, values, str, msgAndArgs...) { return } t.FailNow() } // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // // assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.HTTPBodyContainsf(t, handler, method, url, values, str, msg, args...) { return } t.FailNow() } // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // // assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.HTTPBodyNotContains(t, handler, method, url, values, str, msgAndArgs...) { return } t.FailNow() } // HTTPBodyNotContainsf asserts that a specified handler returns a // body that does not contain a string. // // assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.HTTPBodyNotContainsf(t, handler, method, url, values, str, msg, args...) { return } t.FailNow() } // HTTPError asserts that a specified handler returns an error status code. // // assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.HTTPError(t, handler, method, url, values, msgAndArgs...) { return } t.FailNow() } // HTTPErrorf asserts that a specified handler returns an error status code. // // assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.HTTPErrorf(t, handler, method, url, values, msg, args...) { return } t.FailNow() } // HTTPRedirect asserts that a specified handler returns a redirect status code. // // assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.HTTPRedirect(t, handler, method, url, values, msgAndArgs...) { return } t.FailNow() } // HTTPRedirectf asserts that a specified handler returns a redirect status code. // // assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.HTTPRedirectf(t, handler, method, url, values, msg, args...) { return } t.FailNow() } // HTTPStatusCode asserts that a specified handler returns a specified status code. // // assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) // // Returns whether the assertion was successful (true) or not (false). func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.HTTPStatusCode(t, handler, method, url, values, statuscode, msgAndArgs...) { return } t.FailNow() } // HTTPStatusCodef asserts that a specified handler returns a specified status code. // // assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.HTTPStatusCodef(t, handler, method, url, values, statuscode, msg, args...) { return } t.FailNow() } // HTTPSuccess asserts that a specified handler returns a success status code. // // assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.HTTPSuccess(t, handler, method, url, values, msgAndArgs...) { return } t.FailNow() } // HTTPSuccessf asserts that a specified handler returns a success status code. // // assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.HTTPSuccessf(t, handler, method, url, values, msg, args...) { return } t.FailNow() } // Implements asserts that an object is implemented by the specified interface. // // assert.Implements(t, (*MyInterface)(nil), new(MyObject)) func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Implements(t, interfaceObject, object, msgAndArgs...) { return } t.FailNow() } // Implementsf asserts that an object is implemented by the specified interface. // // assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Implementsf(t, interfaceObject, object, msg, args...) { return } t.FailNow() } // InDelta asserts that the two numerals are within delta of each other. // // assert.InDelta(t, math.Pi, 22/7.0, 0.01) func InDelta(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.InDelta(t, expected, actual, delta, msgAndArgs...) { return } t.FailNow() } // InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. func InDeltaMapValues(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.InDeltaMapValues(t, expected, actual, delta, msgAndArgs...) { return } t.FailNow() } // InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. func InDeltaMapValuesf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.InDeltaMapValuesf(t, expected, actual, delta, msg, args...) { return } t.FailNow() } // InDeltaSlice is the same as InDelta, except it compares two slices. func InDeltaSlice(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.InDeltaSlice(t, expected, actual, delta, msgAndArgs...) { return } t.FailNow() } // InDeltaSlicef is the same as InDelta, except it compares two slices. func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.InDeltaSlicef(t, expected, actual, delta, msg, args...) { return } t.FailNow() } // InDeltaf asserts that the two numerals are within delta of each other. // // assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.InDeltaf(t, expected, actual, delta, msg, args...) { return } t.FailNow() } // InEpsilon asserts that expected and actual have a relative error less than epsilon func InEpsilon(t TestingT, expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.InEpsilon(t, expected, actual, epsilon, msgAndArgs...) { return } t.FailNow() } // InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. func InEpsilonSlice(t TestingT, expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.InEpsilonSlice(t, expected, actual, epsilon, msgAndArgs...) { return } t.FailNow() } // InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices. func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.InEpsilonSlicef(t, expected, actual, epsilon, msg, args...) { return } t.FailNow() } // InEpsilonf asserts that expected and actual have a relative error less than epsilon func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.InEpsilonf(t, expected, actual, epsilon, msg, args...) { return } t.FailNow() } // IsDecreasing asserts that the collection is decreasing // // assert.IsDecreasing(t, []int{2, 1, 0}) // assert.IsDecreasing(t, []float{2, 1}) // assert.IsDecreasing(t, []string{"b", "a"}) func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.IsDecreasing(t, object, msgAndArgs...) { return } t.FailNow() } // IsDecreasingf asserts that the collection is decreasing // // assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") // assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") // assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.IsDecreasingf(t, object, msg, args...) { return } t.FailNow() } // IsIncreasing asserts that the collection is increasing // // assert.IsIncreasing(t, []int{1, 2, 3}) // assert.IsIncreasing(t, []float{1, 2}) // assert.IsIncreasing(t, []string{"a", "b"}) func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.IsIncreasing(t, object, msgAndArgs...) { return } t.FailNow() } // IsIncreasingf asserts that the collection is increasing // // assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") // assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") // assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.IsIncreasingf(t, object, msg, args...) { return } t.FailNow() } // IsNonDecreasing asserts that the collection is not decreasing // // assert.IsNonDecreasing(t, []int{1, 1, 2}) // assert.IsNonDecreasing(t, []float{1, 2}) // assert.IsNonDecreasing(t, []string{"a", "b"}) func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.IsNonDecreasing(t, object, msgAndArgs...) { return } t.FailNow() } // IsNonDecreasingf asserts that the collection is not decreasing // // assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") // assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") // assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.IsNonDecreasingf(t, object, msg, args...) { return } t.FailNow() } // IsNonIncreasing asserts that the collection is not increasing // // assert.IsNonIncreasing(t, []int{2, 1, 1}) // assert.IsNonIncreasing(t, []float{2, 1}) // assert.IsNonIncreasing(t, []string{"b", "a"}) func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.IsNonIncreasing(t, object, msgAndArgs...) { return } t.FailNow() } // IsNonIncreasingf asserts that the collection is not increasing // // assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") // assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") // assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.IsNonIncreasingf(t, object, msg, args...) { return } t.FailNow() } // IsType asserts that the specified objects are of the same type. func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.IsType(t, expectedType, object, msgAndArgs...) { return } t.FailNow() } // IsTypef asserts that the specified objects are of the same type. func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.IsTypef(t, expectedType, object, msg, args...) { return } t.FailNow() } // JSONEq asserts that two JSON strings are equivalent. // // assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.JSONEq(t, expected, actual, msgAndArgs...) { return } t.FailNow() } // JSONEqf asserts that two JSON strings are equivalent. // // assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.JSONEqf(t, expected, actual, msg, args...) { return } t.FailNow() } // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // // assert.Len(t, mySlice, 3) func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Len(t, object, length, msgAndArgs...) { return } t.FailNow() } // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // // assert.Lenf(t, mySlice, 3, "error message %s", "formatted") func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Lenf(t, object, length, msg, args...) { return } t.FailNow() } // Less asserts that the first element is less than the second // // assert.Less(t, 1, 2) // assert.Less(t, float64(1), float64(2)) // assert.Less(t, "a", "b") func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Less(t, e1, e2, msgAndArgs...) { return } t.FailNow() } // LessOrEqual asserts that the first element is less than or equal to the second // // assert.LessOrEqual(t, 1, 2) // assert.LessOrEqual(t, 2, 2) // assert.LessOrEqual(t, "a", "b") // assert.LessOrEqual(t, "b", "b") func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.LessOrEqual(t, e1, e2, msgAndArgs...) { return } t.FailNow() } // LessOrEqualf asserts that the first element is less than or equal to the second // // assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") // assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") // assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") // assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.LessOrEqualf(t, e1, e2, msg, args...) { return } t.FailNow() } // Lessf asserts that the first element is less than the second // // assert.Lessf(t, 1, 2, "error message %s", "formatted") // assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") // assert.Lessf(t, "a", "b", "error message %s", "formatted") func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Lessf(t, e1, e2, msg, args...) { return } t.FailNow() } // Negative asserts that the specified element is negative // // assert.Negative(t, -1) // assert.Negative(t, -1.23) func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Negative(t, e, msgAndArgs...) { return } t.FailNow() } // Negativef asserts that the specified element is negative // // assert.Negativef(t, -1, "error message %s", "formatted") // assert.Negativef(t, -1.23, "error message %s", "formatted") func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Negativef(t, e, msg, args...) { return } t.FailNow() } // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // // assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Never(t, condition, waitFor, tick, msgAndArgs...) { return } t.FailNow() } // Neverf asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // // assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Neverf(t, condition, waitFor, tick, msg, args...) { return } t.FailNow() } // Nil asserts that the specified object is nil. // // assert.Nil(t, err) func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Nil(t, object, msgAndArgs...) { return } t.FailNow() } // Nilf asserts that the specified object is nil. // // assert.Nilf(t, err, "error message %s", "formatted") func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Nilf(t, object, msg, args...) { return } t.FailNow() } // NoDirExists checks whether a directory does not exist in the given path. // It fails if the path points to an existing _directory_ only. func NoDirExists(t TestingT, path string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NoDirExists(t, path, msgAndArgs...) { return } t.FailNow() } // NoDirExistsf checks whether a directory does not exist in the given path. // It fails if the path points to an existing _directory_ only. func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NoDirExistsf(t, path, msg, args...) { return } t.FailNow() } // NoError asserts that a function returned no error (i.e. `nil`). // // actualObj, err := SomeFunction() // if assert.NoError(t, err) { // assert.Equal(t, expectedObj, actualObj) // } func NoError(t TestingT, err error, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NoError(t, err, msgAndArgs...) { return } t.FailNow() } // NoErrorf asserts that a function returned no error (i.e. `nil`). // // actualObj, err := SomeFunction() // if assert.NoErrorf(t, err, "error message %s", "formatted") { // assert.Equal(t, expectedObj, actualObj) // } func NoErrorf(t TestingT, err error, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NoErrorf(t, err, msg, args...) { return } t.FailNow() } // NoFileExists checks whether a file does not exist in a given path. It fails // if the path points to an existing _file_ only. func NoFileExists(t TestingT, path string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NoFileExists(t, path, msgAndArgs...) { return } t.FailNow() } // NoFileExistsf checks whether a file does not exist in a given path. It fails // if the path points to an existing _file_ only. func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NoFileExistsf(t, path, msg, args...) { return } t.FailNow() } // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // // assert.NotContains(t, "Hello World", "Earth") // assert.NotContains(t, ["Hello", "World"], "Earth") // assert.NotContains(t, {"Hello": "World"}, "Earth") func NotContains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NotContains(t, s, contains, msgAndArgs...) { return } t.FailNow() } // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // // assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") // assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") // assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NotContainsf(t, s, contains, msg, args...) { return } t.FailNow() } // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // // if assert.NotEmpty(t, obj) { // assert.Equal(t, "two", obj[1]) // } func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NotEmpty(t, object, msgAndArgs...) { return } t.FailNow() } // NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // // if assert.NotEmptyf(t, obj, "error message %s", "formatted") { // assert.Equal(t, "two", obj[1]) // } func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NotEmptyf(t, object, msg, args...) { return } t.FailNow() } // NotEqual asserts that the specified values are NOT equal. // // assert.NotEqual(t, obj1, obj2) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). func NotEqual(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NotEqual(t, expected, actual, msgAndArgs...) { return } t.FailNow() } // NotEqualValues asserts that two objects are not equal even when converted to the same type // // assert.NotEqualValues(t, obj1, obj2) func NotEqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NotEqualValues(t, expected, actual, msgAndArgs...) { return } t.FailNow() } // NotEqualValuesf asserts that two objects are not equal even when converted to the same type // // assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NotEqualValuesf(t, expected, actual, msg, args...) { return } t.FailNow() } // NotEqualf asserts that the specified values are NOT equal. // // assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NotEqualf(t, expected, actual, msg, args...) { return } t.FailNow() } // NotErrorIs asserts that at none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func NotErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NotErrorIs(t, err, target, msgAndArgs...) { return } t.FailNow() } // NotErrorIsf asserts that at none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NotErrorIsf(t, err, target, msg, args...) { return } t.FailNow() } // NotImplements asserts that an object does not implement the specified interface. // // assert.NotImplements(t, (*MyInterface)(nil), new(MyObject)) func NotImplements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NotImplements(t, interfaceObject, object, msgAndArgs...) { return } t.FailNow() } // NotImplementsf asserts that an object does not implement the specified interface. // // assert.NotImplementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func NotImplementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NotImplementsf(t, interfaceObject, object, msg, args...) { return } t.FailNow() } // NotNil asserts that the specified object is not nil. // // assert.NotNil(t, err) func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NotNil(t, object, msgAndArgs...) { return } t.FailNow() } // NotNilf asserts that the specified object is not nil. // // assert.NotNilf(t, err, "error message %s", "formatted") func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NotNilf(t, object, msg, args...) { return } t.FailNow() } // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. // // assert.NotPanics(t, func(){ RemainCalm() }) func NotPanics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NotPanics(t, f, msgAndArgs...) { return } t.FailNow() } // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. // // assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") func NotPanicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NotPanicsf(t, f, msg, args...) { return } t.FailNow() } // NotRegexp asserts that a specified regexp does not match a string. // // assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") // assert.NotRegexp(t, "^start", "it's not starting") func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NotRegexp(t, rx, str, msgAndArgs...) { return } t.FailNow() } // NotRegexpf asserts that a specified regexp does not match a string. // // assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") // assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NotRegexpf(t, rx, str, msg, args...) { return } t.FailNow() } // NotSame asserts that two pointers do not reference the same object. // // assert.NotSame(t, ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. func NotSame(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NotSame(t, expected, actual, msgAndArgs...) { return } t.FailNow() } // NotSamef asserts that two pointers do not reference the same object. // // assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NotSamef(t, expected, actual, msg, args...) { return } t.FailNow() } // NotSubset asserts that the specified list(array, slice...) or map does NOT // contain all elements given in the specified subset list(array, slice...) or // map. // // assert.NotSubset(t, [1, 3, 4], [1, 2]) // assert.NotSubset(t, {"x": 1, "y": 2}, {"z": 3}) func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NotSubset(t, list, subset, msgAndArgs...) { return } t.FailNow() } // NotSubsetf asserts that the specified list(array, slice...) or map does NOT // contain all elements given in the specified subset list(array, slice...) or // map. // // assert.NotSubsetf(t, [1, 3, 4], [1, 2], "error message %s", "formatted") // assert.NotSubsetf(t, {"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NotSubsetf(t, list, subset, msg, args...) { return } t.FailNow() } // NotZero asserts that i is not the zero value for its type. func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NotZero(t, i, msgAndArgs...) { return } t.FailNow() } // NotZerof asserts that i is not the zero value for its type. func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.NotZerof(t, i, msg, args...) { return } t.FailNow() } // Panics asserts that the code inside the specified PanicTestFunc panics. // // assert.Panics(t, func(){ GoCrazy() }) func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Panics(t, f, msgAndArgs...) { return } t.FailNow() } // PanicsWithError asserts that the code inside the specified PanicTestFunc // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // // assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) func PanicsWithError(t TestingT, errString string, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.PanicsWithError(t, errString, f, msgAndArgs...) { return } t.FailNow() } // PanicsWithErrorf asserts that the code inside the specified PanicTestFunc // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // // assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithErrorf(t TestingT, errString string, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.PanicsWithErrorf(t, errString, f, msg, args...) { return } t.FailNow() } // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // // assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) func PanicsWithValue(t TestingT, expected interface{}, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.PanicsWithValue(t, expected, f, msgAndArgs...) { return } t.FailNow() } // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // // assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithValuef(t TestingT, expected interface{}, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.PanicsWithValuef(t, expected, f, msg, args...) { return } t.FailNow() } // Panicsf asserts that the code inside the specified PanicTestFunc panics. // // assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Panicsf(t, f, msg, args...) { return } t.FailNow() } // Positive asserts that the specified element is positive // // assert.Positive(t, 1) // assert.Positive(t, 1.23) func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Positive(t, e, msgAndArgs...) { return } t.FailNow() } // Positivef asserts that the specified element is positive // // assert.Positivef(t, 1, "error message %s", "formatted") // assert.Positivef(t, 1.23, "error message %s", "formatted") func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Positivef(t, e, msg, args...) { return } t.FailNow() } // Regexp asserts that a specified regexp matches a string. // // assert.Regexp(t, regexp.MustCompile("start"), "it's starting") // assert.Regexp(t, "start...$", "it's not starting") func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Regexp(t, rx, str, msgAndArgs...) { return } t.FailNow() } // Regexpf asserts that a specified regexp matches a string. // // assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") // assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Regexpf(t, rx, str, msg, args...) { return } t.FailNow() } // Same asserts that two pointers reference the same object. // // assert.Same(t, ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. func Same(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Same(t, expected, actual, msgAndArgs...) { return } t.FailNow() } // Samef asserts that two pointers reference the same object. // // assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. func Samef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Samef(t, expected, actual, msg, args...) { return } t.FailNow() } // Subset asserts that the specified list(array, slice...) or map contains all // elements given in the specified subset list(array, slice...) or map. // // assert.Subset(t, [1, 2, 3], [1, 2]) // assert.Subset(t, {"x": 1, "y": 2}, {"x": 1}) func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Subset(t, list, subset, msgAndArgs...) { return } t.FailNow() } // Subsetf asserts that the specified list(array, slice...) or map contains all // elements given in the specified subset list(array, slice...) or map. // // assert.Subsetf(t, [1, 2, 3], [1, 2], "error message %s", "formatted") // assert.Subsetf(t, {"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Subsetf(t, list, subset, msg, args...) { return } t.FailNow() } // True asserts that the specified value is true. // // assert.True(t, myBool) func True(t TestingT, value bool, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.True(t, value, msgAndArgs...) { return } t.FailNow() } // Truef asserts that the specified value is true. // // assert.Truef(t, myBool, "error message %s", "formatted") func Truef(t TestingT, value bool, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Truef(t, value, msg, args...) { return } t.FailNow() } // WithinDuration asserts that the two times are within duration delta of each other. // // assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) func WithinDuration(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.WithinDuration(t, expected, actual, delta, msgAndArgs...) { return } t.FailNow() } // WithinDurationf asserts that the two times are within duration delta of each other. // // assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.WithinDurationf(t, expected, actual, delta, msg, args...) { return } t.FailNow() } // WithinRange asserts that a time is within a time range (inclusive). // // assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) func WithinRange(t TestingT, actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.WithinRange(t, actual, start, end, msgAndArgs...) { return } t.FailNow() } // WithinRangef asserts that a time is within a time range (inclusive). // // assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.WithinRangef(t, actual, start, end, msg, args...) { return } t.FailNow() } // YAMLEq asserts that two YAML strings are equivalent. func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.YAMLEq(t, expected, actual, msgAndArgs...) { return } t.FailNow() } // YAMLEqf asserts that two YAML strings are equivalent. func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.YAMLEqf(t, expected, actual, msg, args...) { return } t.FailNow() } // Zero asserts that i is the zero value for its type. func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Zero(t, i, msgAndArgs...) { return } t.FailNow() } // Zerof asserts that i is the zero value for its type. func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.Zerof(t, i, msg, args...) { return } t.FailNow() } ================================================ FILE: vendor/github.com/stretchr/testify/require/require.go.tmpl ================================================ {{.Comment}} func {{.DocInfo.Name}}(t TestingT, {{.Params}}) { if h, ok := t.(tHelper); ok { h.Helper() } if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { return } t.FailNow() } ================================================ FILE: vendor/github.com/stretchr/testify/require/require_forward.go ================================================ // Code generated with github.com/stretchr/testify/_codegen; DO NOT EDIT. package require import ( assert "github.com/stretchr/testify/assert" http "net/http" url "net/url" time "time" ) // Condition uses a Comparison to assert a complex condition. func (a *Assertions) Condition(comp assert.Comparison, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Condition(a.t, comp, msgAndArgs...) } // Conditionf uses a Comparison to assert a complex condition. func (a *Assertions) Conditionf(comp assert.Comparison, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Conditionf(a.t, comp, msg, args...) } // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // // a.Contains("Hello World", "World") // a.Contains(["Hello", "World"], "World") // a.Contains({"Hello": "World"}, "Hello") func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Contains(a.t, s, contains, msgAndArgs...) } // Containsf asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // // a.Containsf("Hello World", "World", "error message %s", "formatted") // a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") // a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Containsf(a.t, s, contains, msg, args...) } // DirExists checks whether a directory exists in the given path. It also fails // if the path is a file rather a directory or there is an error checking whether it exists. func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } DirExists(a.t, path, msgAndArgs...) } // DirExistsf checks whether a directory exists in the given path. It also fails // if the path is a file rather a directory or there is an error checking whether it exists. func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } DirExistsf(a.t, path, msg, args...) } // ElementsMatch asserts that the specified listA(array, slice...) is equal to specified // listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, // the number of appearances of each of them in both lists should match. // // a.ElementsMatch([1, 3, 2, 3], [1, 3, 3, 2]) func (a *Assertions) ElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } ElementsMatch(a.t, listA, listB, msgAndArgs...) } // ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified // listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, // the number of appearances of each of them in both lists should match. // // a.ElementsMatchf([1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } ElementsMatchf(a.t, listA, listB, msg, args...) } // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // // a.Empty(obj) func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Empty(a.t, object, msgAndArgs...) } // Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // // a.Emptyf(obj, "error message %s", "formatted") func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Emptyf(a.t, object, msg, args...) } // Equal asserts that two objects are equal. // // a.Equal(123, 123) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality // cannot be determined and will always fail. func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Equal(a.t, expected, actual, msgAndArgs...) } // EqualError asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() // a.EqualError(err, expectedErrorString) func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } EqualError(a.t, theError, errString, msgAndArgs...) } // EqualErrorf asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // // actualObj, err := SomeFunction() // a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } EqualErrorf(a.t, theError, errString, msg, args...) } // EqualExportedValues asserts that the types of two objects are equal and their public // fields are also equal. This is useful for comparing structs that have private fields // that could potentially differ. // // type S struct { // Exported int // notExported int // } // a.EqualExportedValues(S{1, 2}, S{1, 3}) => true // a.EqualExportedValues(S{1, 2}, S{2, 3}) => false func (a *Assertions) EqualExportedValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } EqualExportedValues(a.t, expected, actual, msgAndArgs...) } // EqualExportedValuesf asserts that the types of two objects are equal and their public // fields are also equal. This is useful for comparing structs that have private fields // that could potentially differ. // // type S struct { // Exported int // notExported int // } // a.EqualExportedValuesf(S{1, 2}, S{1, 3}, "error message %s", "formatted") => true // a.EqualExportedValuesf(S{1, 2}, S{2, 3}, "error message %s", "formatted") => false func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } EqualExportedValuesf(a.t, expected, actual, msg, args...) } // EqualValues asserts that two objects are equal or convertible to the same types // and equal. // // a.EqualValues(uint32(123), int32(123)) func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } EqualValues(a.t, expected, actual, msgAndArgs...) } // EqualValuesf asserts that two objects are equal or convertible to the same types // and equal. // // a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } EqualValuesf(a.t, expected, actual, msg, args...) } // Equalf asserts that two objects are equal. // // a.Equalf(123, 123, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality // cannot be determined and will always fail. func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Equalf(a.t, expected, actual, msg, args...) } // Error asserts that a function returned an error (i.e. not `nil`). // // actualObj, err := SomeFunction() // if a.Error(err) { // assert.Equal(t, expectedError, err) // } func (a *Assertions) Error(err error, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Error(a.t, err, msgAndArgs...) } // ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. // This is a wrapper for errors.As. func (a *Assertions) ErrorAs(err error, target interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } ErrorAs(a.t, err, target, msgAndArgs...) } // ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. // This is a wrapper for errors.As. func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } ErrorAsf(a.t, err, target, msg, args...) } // ErrorContains asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // // actualObj, err := SomeFunction() // a.ErrorContains(err, expectedErrorSubString) func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } ErrorContains(a.t, theError, contains, msgAndArgs...) } // ErrorContainsf asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // // actualObj, err := SomeFunction() // a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") func (a *Assertions) ErrorContainsf(theError error, contains string, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } ErrorContainsf(a.t, theError, contains, msg, args...) } // ErrorIs asserts that at least one of the errors in err's chain matches target. // This is a wrapper for errors.Is. func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } ErrorIs(a.t, err, target, msgAndArgs...) } // ErrorIsf asserts that at least one of the errors in err's chain matches target. // This is a wrapper for errors.Is. func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } ErrorIsf(a.t, err, target, msg, args...) } // Errorf asserts that a function returned an error (i.e. not `nil`). // // actualObj, err := SomeFunction() // if a.Errorf(err, "error message %s", "formatted") { // assert.Equal(t, expectedErrorf, err) // } func (a *Assertions) Errorf(err error, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Errorf(a.t, err, msg, args...) } // Eventually asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // // a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Eventually(a.t, condition, waitFor, tick, msgAndArgs...) } // EventuallyWithT asserts that given condition will be met in waitFor time, // periodically checking target function each tick. In contrast to Eventually, // it supplies a CollectT to the condition function, so that the condition // function can use the CollectT to call other assertions. // The condition is considered "met" if no errors are raised in a tick. // The supplied CollectT collects all errors from one tick (if there are any). // If the condition is not met before waitFor, the collected errors of // the last tick are copied to t. // // externalValue := false // go func() { // time.Sleep(8*time.Second) // externalValue = true // }() // a.EventuallyWithT(func(c *assert.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // assert.True(c, externalValue, "expected 'externalValue' to be true") // }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") func (a *Assertions) EventuallyWithT(condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } EventuallyWithT(a.t, condition, waitFor, tick, msgAndArgs...) } // EventuallyWithTf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. In contrast to Eventually, // it supplies a CollectT to the condition function, so that the condition // function can use the CollectT to call other assertions. // The condition is considered "met" if no errors are raised in a tick. // The supplied CollectT collects all errors from one tick (if there are any). // If the condition is not met before waitFor, the collected errors of // the last tick are copied to t. // // externalValue := false // go func() { // time.Sleep(8*time.Second) // externalValue = true // }() // a.EventuallyWithTf(func(c *assert.CollectT, "error message %s", "formatted") { // // add assertions as needed; any assertion failure will fail the current tick // assert.True(c, externalValue, "expected 'externalValue' to be true") // }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") func (a *Assertions) EventuallyWithTf(condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } EventuallyWithTf(a.t, condition, waitFor, tick, msg, args...) } // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // // a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Eventuallyf(a.t, condition, waitFor, tick, msg, args...) } // Exactly asserts that two objects are equal in value and type. // // a.Exactly(int32(123), int64(123)) func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Exactly(a.t, expected, actual, msgAndArgs...) } // Exactlyf asserts that two objects are equal in value and type. // // a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Exactlyf(a.t, expected, actual, msg, args...) } // Fail reports a failure through func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Fail(a.t, failureMessage, msgAndArgs...) } // FailNow fails test func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } FailNow(a.t, failureMessage, msgAndArgs...) } // FailNowf fails test func (a *Assertions) FailNowf(failureMessage string, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } FailNowf(a.t, failureMessage, msg, args...) } // Failf reports a failure through func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Failf(a.t, failureMessage, msg, args...) } // False asserts that the specified value is false. // // a.False(myBool) func (a *Assertions) False(value bool, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } False(a.t, value, msgAndArgs...) } // Falsef asserts that the specified value is false. // // a.Falsef(myBool, "error message %s", "formatted") func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Falsef(a.t, value, msg, args...) } // FileExists checks whether a file exists in the given path. It also fails if // the path points to a directory or there is an error when trying to check the file. func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } FileExists(a.t, path, msgAndArgs...) } // FileExistsf checks whether a file exists in the given path. It also fails if // the path points to a directory or there is an error when trying to check the file. func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } FileExistsf(a.t, path, msg, args...) } // Greater asserts that the first element is greater than the second // // a.Greater(2, 1) // a.Greater(float64(2), float64(1)) // a.Greater("b", "a") func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Greater(a.t, e1, e2, msgAndArgs...) } // GreaterOrEqual asserts that the first element is greater than or equal to the second // // a.GreaterOrEqual(2, 1) // a.GreaterOrEqual(2, 2) // a.GreaterOrEqual("b", "a") // a.GreaterOrEqual("b", "b") func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } GreaterOrEqual(a.t, e1, e2, msgAndArgs...) } // GreaterOrEqualf asserts that the first element is greater than or equal to the second // // a.GreaterOrEqualf(2, 1, "error message %s", "formatted") // a.GreaterOrEqualf(2, 2, "error message %s", "formatted") // a.GreaterOrEqualf("b", "a", "error message %s", "formatted") // a.GreaterOrEqualf("b", "b", "error message %s", "formatted") func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } GreaterOrEqualf(a.t, e1, e2, msg, args...) } // Greaterf asserts that the first element is greater than the second // // a.Greaterf(2, 1, "error message %s", "formatted") // a.Greaterf(float64(2), float64(1), "error message %s", "formatted") // a.Greaterf("b", "a", "error message %s", "formatted") func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Greaterf(a.t, e1, e2, msg, args...) } // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // // a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } HTTPBodyContains(a.t, handler, method, url, values, str, msgAndArgs...) } // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // // a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } HTTPBodyContainsf(a.t, handler, method, url, values, str, msg, args...) } // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // // a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } HTTPBodyNotContains(a.t, handler, method, url, values, str, msgAndArgs...) } // HTTPBodyNotContainsf asserts that a specified handler returns a // body that does not contain a string. // // a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } HTTPBodyNotContainsf(a.t, handler, method, url, values, str, msg, args...) } // HTTPError asserts that a specified handler returns an error status code. // // a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } HTTPError(a.t, handler, method, url, values, msgAndArgs...) } // HTTPErrorf asserts that a specified handler returns an error status code. // // a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } HTTPErrorf(a.t, handler, method, url, values, msg, args...) } // HTTPRedirect asserts that a specified handler returns a redirect status code. // // a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } HTTPRedirect(a.t, handler, method, url, values, msgAndArgs...) } // HTTPRedirectf asserts that a specified handler returns a redirect status code. // // a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } HTTPRedirectf(a.t, handler, method, url, values, msg, args...) } // HTTPStatusCode asserts that a specified handler returns a specified status code. // // a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } HTTPStatusCode(a.t, handler, method, url, values, statuscode, msgAndArgs...) } // HTTPStatusCodef asserts that a specified handler returns a specified status code. // // a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } HTTPStatusCodef(a.t, handler, method, url, values, statuscode, msg, args...) } // HTTPSuccess asserts that a specified handler returns a success status code. // // a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } HTTPSuccess(a.t, handler, method, url, values, msgAndArgs...) } // HTTPSuccessf asserts that a specified handler returns a success status code. // // a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } HTTPSuccessf(a.t, handler, method, url, values, msg, args...) } // Implements asserts that an object is implemented by the specified interface. // // a.Implements((*MyInterface)(nil), new(MyObject)) func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Implements(a.t, interfaceObject, object, msgAndArgs...) } // Implementsf asserts that an object is implemented by the specified interface. // // a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Implementsf(a.t, interfaceObject, object, msg, args...) } // InDelta asserts that the two numerals are within delta of each other. // // a.InDelta(math.Pi, 22/7.0, 0.01) func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } InDelta(a.t, expected, actual, delta, msgAndArgs...) } // InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. func (a *Assertions) InDeltaMapValues(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } InDeltaMapValues(a.t, expected, actual, delta, msgAndArgs...) } // InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. func (a *Assertions) InDeltaMapValuesf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } InDeltaMapValuesf(a.t, expected, actual, delta, msg, args...) } // InDeltaSlice is the same as InDelta, except it compares two slices. func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...) } // InDeltaSlicef is the same as InDelta, except it compares two slices. func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } InDeltaSlicef(a.t, expected, actual, delta, msg, args...) } // InDeltaf asserts that the two numerals are within delta of each other. // // a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } InDeltaf(a.t, expected, actual, delta, msg, args...) } // InEpsilon asserts that expected and actual have a relative error less than epsilon func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...) } // InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } InEpsilonSlice(a.t, expected, actual, epsilon, msgAndArgs...) } // InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices. func (a *Assertions) InEpsilonSlicef(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } InEpsilonSlicef(a.t, expected, actual, epsilon, msg, args...) } // InEpsilonf asserts that expected and actual have a relative error less than epsilon func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } InEpsilonf(a.t, expected, actual, epsilon, msg, args...) } // IsDecreasing asserts that the collection is decreasing // // a.IsDecreasing([]int{2, 1, 0}) // a.IsDecreasing([]float{2, 1}) // a.IsDecreasing([]string{"b", "a"}) func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } IsDecreasing(a.t, object, msgAndArgs...) } // IsDecreasingf asserts that the collection is decreasing // // a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") // a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") // a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } IsDecreasingf(a.t, object, msg, args...) } // IsIncreasing asserts that the collection is increasing // // a.IsIncreasing([]int{1, 2, 3}) // a.IsIncreasing([]float{1, 2}) // a.IsIncreasing([]string{"a", "b"}) func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } IsIncreasing(a.t, object, msgAndArgs...) } // IsIncreasingf asserts that the collection is increasing // // a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") // a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") // a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } IsIncreasingf(a.t, object, msg, args...) } // IsNonDecreasing asserts that the collection is not decreasing // // a.IsNonDecreasing([]int{1, 1, 2}) // a.IsNonDecreasing([]float{1, 2}) // a.IsNonDecreasing([]string{"a", "b"}) func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } IsNonDecreasing(a.t, object, msgAndArgs...) } // IsNonDecreasingf asserts that the collection is not decreasing // // a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") // a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") // a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } IsNonDecreasingf(a.t, object, msg, args...) } // IsNonIncreasing asserts that the collection is not increasing // // a.IsNonIncreasing([]int{2, 1, 1}) // a.IsNonIncreasing([]float{2, 1}) // a.IsNonIncreasing([]string{"b", "a"}) func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } IsNonIncreasing(a.t, object, msgAndArgs...) } // IsNonIncreasingf asserts that the collection is not increasing // // a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") // a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") // a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } IsNonIncreasingf(a.t, object, msg, args...) } // IsType asserts that the specified objects are of the same type. func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } IsType(a.t, expectedType, object, msgAndArgs...) } // IsTypef asserts that the specified objects are of the same type. func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } IsTypef(a.t, expectedType, object, msg, args...) } // JSONEq asserts that two JSON strings are equivalent. // // a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } JSONEq(a.t, expected, actual, msgAndArgs...) } // JSONEqf asserts that two JSON strings are equivalent. // // a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } JSONEqf(a.t, expected, actual, msg, args...) } // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // // a.Len(mySlice, 3) func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Len(a.t, object, length, msgAndArgs...) } // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // // a.Lenf(mySlice, 3, "error message %s", "formatted") func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Lenf(a.t, object, length, msg, args...) } // Less asserts that the first element is less than the second // // a.Less(1, 2) // a.Less(float64(1), float64(2)) // a.Less("a", "b") func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Less(a.t, e1, e2, msgAndArgs...) } // LessOrEqual asserts that the first element is less than or equal to the second // // a.LessOrEqual(1, 2) // a.LessOrEqual(2, 2) // a.LessOrEqual("a", "b") // a.LessOrEqual("b", "b") func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } LessOrEqual(a.t, e1, e2, msgAndArgs...) } // LessOrEqualf asserts that the first element is less than or equal to the second // // a.LessOrEqualf(1, 2, "error message %s", "formatted") // a.LessOrEqualf(2, 2, "error message %s", "formatted") // a.LessOrEqualf("a", "b", "error message %s", "formatted") // a.LessOrEqualf("b", "b", "error message %s", "formatted") func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } LessOrEqualf(a.t, e1, e2, msg, args...) } // Lessf asserts that the first element is less than the second // // a.Lessf(1, 2, "error message %s", "formatted") // a.Lessf(float64(1), float64(2), "error message %s", "formatted") // a.Lessf("a", "b", "error message %s", "formatted") func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Lessf(a.t, e1, e2, msg, args...) } // Negative asserts that the specified element is negative // // a.Negative(-1) // a.Negative(-1.23) func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Negative(a.t, e, msgAndArgs...) } // Negativef asserts that the specified element is negative // // a.Negativef(-1, "error message %s", "formatted") // a.Negativef(-1.23, "error message %s", "formatted") func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Negativef(a.t, e, msg, args...) } // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // // a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Never(a.t, condition, waitFor, tick, msgAndArgs...) } // Neverf asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // // a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Neverf(a.t, condition, waitFor, tick, msg, args...) } // Nil asserts that the specified object is nil. // // a.Nil(err) func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Nil(a.t, object, msgAndArgs...) } // Nilf asserts that the specified object is nil. // // a.Nilf(err, "error message %s", "formatted") func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Nilf(a.t, object, msg, args...) } // NoDirExists checks whether a directory does not exist in the given path. // It fails if the path points to an existing _directory_ only. func (a *Assertions) NoDirExists(path string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NoDirExists(a.t, path, msgAndArgs...) } // NoDirExistsf checks whether a directory does not exist in the given path. // It fails if the path points to an existing _directory_ only. func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NoDirExistsf(a.t, path, msg, args...) } // NoError asserts that a function returned no error (i.e. `nil`). // // actualObj, err := SomeFunction() // if a.NoError(err) { // assert.Equal(t, expectedObj, actualObj) // } func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NoError(a.t, err, msgAndArgs...) } // NoErrorf asserts that a function returned no error (i.e. `nil`). // // actualObj, err := SomeFunction() // if a.NoErrorf(err, "error message %s", "formatted") { // assert.Equal(t, expectedObj, actualObj) // } func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NoErrorf(a.t, err, msg, args...) } // NoFileExists checks whether a file does not exist in a given path. It fails // if the path points to an existing _file_ only. func (a *Assertions) NoFileExists(path string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NoFileExists(a.t, path, msgAndArgs...) } // NoFileExistsf checks whether a file does not exist in a given path. It fails // if the path points to an existing _file_ only. func (a *Assertions) NoFileExistsf(path string, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NoFileExistsf(a.t, path, msg, args...) } // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // // a.NotContains("Hello World", "Earth") // a.NotContains(["Hello", "World"], "Earth") // a.NotContains({"Hello": "World"}, "Earth") func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NotContains(a.t, s, contains, msgAndArgs...) } // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // // a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") // a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") // a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NotContainsf(a.t, s, contains, msg, args...) } // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // // if a.NotEmpty(obj) { // assert.Equal(t, "two", obj[1]) // } func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NotEmpty(a.t, object, msgAndArgs...) } // NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // // if a.NotEmptyf(obj, "error message %s", "formatted") { // assert.Equal(t, "two", obj[1]) // } func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NotEmptyf(a.t, object, msg, args...) } // NotEqual asserts that the specified values are NOT equal. // // a.NotEqual(obj1, obj2) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NotEqual(a.t, expected, actual, msgAndArgs...) } // NotEqualValues asserts that two objects are not equal even when converted to the same type // // a.NotEqualValues(obj1, obj2) func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NotEqualValues(a.t, expected, actual, msgAndArgs...) } // NotEqualValuesf asserts that two objects are not equal even when converted to the same type // // a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NotEqualValuesf(a.t, expected, actual, msg, args...) } // NotEqualf asserts that the specified values are NOT equal. // // a.NotEqualf(obj1, obj2, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NotEqualf(a.t, expected, actual, msg, args...) } // NotErrorIs asserts that at none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NotErrorIs(a.t, err, target, msgAndArgs...) } // NotErrorIsf asserts that at none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NotErrorIsf(a.t, err, target, msg, args...) } // NotImplements asserts that an object does not implement the specified interface. // // a.NotImplements((*MyInterface)(nil), new(MyObject)) func (a *Assertions) NotImplements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NotImplements(a.t, interfaceObject, object, msgAndArgs...) } // NotImplementsf asserts that an object does not implement the specified interface. // // a.NotImplementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func (a *Assertions) NotImplementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NotImplementsf(a.t, interfaceObject, object, msg, args...) } // NotNil asserts that the specified object is not nil. // // a.NotNil(err) func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NotNil(a.t, object, msgAndArgs...) } // NotNilf asserts that the specified object is not nil. // // a.NotNilf(err, "error message %s", "formatted") func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NotNilf(a.t, object, msg, args...) } // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. // // a.NotPanics(func(){ RemainCalm() }) func (a *Assertions) NotPanics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NotPanics(a.t, f, msgAndArgs...) } // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. // // a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") func (a *Assertions) NotPanicsf(f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NotPanicsf(a.t, f, msg, args...) } // NotRegexp asserts that a specified regexp does not match a string. // // a.NotRegexp(regexp.MustCompile("starts"), "it's starting") // a.NotRegexp("^start", "it's not starting") func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NotRegexp(a.t, rx, str, msgAndArgs...) } // NotRegexpf asserts that a specified regexp does not match a string. // // a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") // a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NotRegexpf(a.t, rx, str, msg, args...) } // NotSame asserts that two pointers do not reference the same object. // // a.NotSame(ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. func (a *Assertions) NotSame(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NotSame(a.t, expected, actual, msgAndArgs...) } // NotSamef asserts that two pointers do not reference the same object. // // a.NotSamef(ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NotSamef(a.t, expected, actual, msg, args...) } // NotSubset asserts that the specified list(array, slice...) or map does NOT // contain all elements given in the specified subset list(array, slice...) or // map. // // a.NotSubset([1, 3, 4], [1, 2]) // a.NotSubset({"x": 1, "y": 2}, {"z": 3}) func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NotSubset(a.t, list, subset, msgAndArgs...) } // NotSubsetf asserts that the specified list(array, slice...) or map does NOT // contain all elements given in the specified subset list(array, slice...) or // map. // // a.NotSubsetf([1, 3, 4], [1, 2], "error message %s", "formatted") // a.NotSubsetf({"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NotSubsetf(a.t, list, subset, msg, args...) } // NotZero asserts that i is not the zero value for its type. func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NotZero(a.t, i, msgAndArgs...) } // NotZerof asserts that i is not the zero value for its type. func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } NotZerof(a.t, i, msg, args...) } // Panics asserts that the code inside the specified PanicTestFunc panics. // // a.Panics(func(){ GoCrazy() }) func (a *Assertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Panics(a.t, f, msgAndArgs...) } // PanicsWithError asserts that the code inside the specified PanicTestFunc // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // // a.PanicsWithError("crazy error", func(){ GoCrazy() }) func (a *Assertions) PanicsWithError(errString string, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } PanicsWithError(a.t, errString, f, msgAndArgs...) } // PanicsWithErrorf asserts that the code inside the specified PanicTestFunc // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // // a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) PanicsWithErrorf(errString string, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } PanicsWithErrorf(a.t, errString, f, msg, args...) } // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // // a.PanicsWithValue("crazy error", func(){ GoCrazy() }) func (a *Assertions) PanicsWithValue(expected interface{}, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } PanicsWithValue(a.t, expected, f, msgAndArgs...) } // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // // a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) PanicsWithValuef(expected interface{}, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } PanicsWithValuef(a.t, expected, f, msg, args...) } // Panicsf asserts that the code inside the specified PanicTestFunc panics. // // a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) Panicsf(f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Panicsf(a.t, f, msg, args...) } // Positive asserts that the specified element is positive // // a.Positive(1) // a.Positive(1.23) func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Positive(a.t, e, msgAndArgs...) } // Positivef asserts that the specified element is positive // // a.Positivef(1, "error message %s", "formatted") // a.Positivef(1.23, "error message %s", "formatted") func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Positivef(a.t, e, msg, args...) } // Regexp asserts that a specified regexp matches a string. // // a.Regexp(regexp.MustCompile("start"), "it's starting") // a.Regexp("start...$", "it's not starting") func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Regexp(a.t, rx, str, msgAndArgs...) } // Regexpf asserts that a specified regexp matches a string. // // a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") // a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Regexpf(a.t, rx, str, msg, args...) } // Same asserts that two pointers reference the same object. // // a.Same(ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. func (a *Assertions) Same(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Same(a.t, expected, actual, msgAndArgs...) } // Samef asserts that two pointers reference the same object. // // a.Samef(ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Samef(a.t, expected, actual, msg, args...) } // Subset asserts that the specified list(array, slice...) or map contains all // elements given in the specified subset list(array, slice...) or map. // // a.Subset([1, 2, 3], [1, 2]) // a.Subset({"x": 1, "y": 2}, {"x": 1}) func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Subset(a.t, list, subset, msgAndArgs...) } // Subsetf asserts that the specified list(array, slice...) or map contains all // elements given in the specified subset list(array, slice...) or map. // // a.Subsetf([1, 2, 3], [1, 2], "error message %s", "formatted") // a.Subsetf({"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Subsetf(a.t, list, subset, msg, args...) } // True asserts that the specified value is true. // // a.True(myBool) func (a *Assertions) True(value bool, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } True(a.t, value, msgAndArgs...) } // Truef asserts that the specified value is true. // // a.Truef(myBool, "error message %s", "formatted") func (a *Assertions) Truef(value bool, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Truef(a.t, value, msg, args...) } // WithinDuration asserts that the two times are within duration delta of each other. // // a.WithinDuration(time.Now(), time.Now(), 10*time.Second) func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } WithinDuration(a.t, expected, actual, delta, msgAndArgs...) } // WithinDurationf asserts that the two times are within duration delta of each other. // // a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } WithinDurationf(a.t, expected, actual, delta, msg, args...) } // WithinRange asserts that a time is within a time range (inclusive). // // a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } WithinRange(a.t, actual, start, end, msgAndArgs...) } // WithinRangef asserts that a time is within a time range (inclusive). // // a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } WithinRangef(a.t, actual, start, end, msg, args...) } // YAMLEq asserts that two YAML strings are equivalent. func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } YAMLEq(a.t, expected, actual, msgAndArgs...) } // YAMLEqf asserts that two YAML strings are equivalent. func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } YAMLEqf(a.t, expected, actual, msg, args...) } // Zero asserts that i is the zero value for its type. func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Zero(a.t, i, msgAndArgs...) } // Zerof asserts that i is the zero value for its type. func (a *Assertions) Zerof(i interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } Zerof(a.t, i, msg, args...) } ================================================ FILE: vendor/github.com/stretchr/testify/require/require_forward.go.tmpl ================================================ {{.CommentWithoutT "a"}} func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) { if h, ok := a.t.(tHelper); ok { h.Helper() } {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) } ================================================ FILE: vendor/github.com/stretchr/testify/require/requirements.go ================================================ package require // TestingT is an interface wrapper around *testing.T type TestingT interface { Errorf(format string, args ...interface{}) FailNow() } type tHelper interface { Helper() } // ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful // for table driven tests. type ComparisonAssertionFunc func(TestingT, interface{}, interface{}, ...interface{}) // ValueAssertionFunc is a common function prototype when validating a single value. Can be useful // for table driven tests. type ValueAssertionFunc func(TestingT, interface{}, ...interface{}) // BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful // for table driven tests. type BoolAssertionFunc func(TestingT, bool, ...interface{}) // ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful // for table driven tests. type ErrorAssertionFunc func(TestingT, error, ...interface{}) //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=require -template=require.go.tmpl -include-format-funcs" ================================================ FILE: vendor/github.com/syndtr/gocapability/LICENSE ================================================ Copyright 2013 Suryandaru Triandana All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/syndtr/gocapability/capability/capability.go ================================================ // Copyright (c) 2013, Suryandaru Triandana // All rights reserved. // // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Package capability provides utilities for manipulating POSIX capabilities. package capability type Capabilities interface { // Get check whether a capability present in the given // capabilities set. The 'which' value should be one of EFFECTIVE, // PERMITTED, INHERITABLE, BOUNDING or AMBIENT. Get(which CapType, what Cap) bool // Empty check whether all capability bits of the given capabilities // set are zero. The 'which' value should be one of EFFECTIVE, // PERMITTED, INHERITABLE, BOUNDING or AMBIENT. Empty(which CapType) bool // Full check whether all capability bits of the given capabilities // set are one. The 'which' value should be one of EFFECTIVE, // PERMITTED, INHERITABLE, BOUNDING or AMBIENT. Full(which CapType) bool // Set sets capabilities of the given capabilities sets. The // 'which' value should be one or combination (OR'ed) of EFFECTIVE, // PERMITTED, INHERITABLE, BOUNDING or AMBIENT. Set(which CapType, caps ...Cap) // Unset unsets capabilities of the given capabilities sets. The // 'which' value should be one or combination (OR'ed) of EFFECTIVE, // PERMITTED, INHERITABLE, BOUNDING or AMBIENT. Unset(which CapType, caps ...Cap) // Fill sets all bits of the given capabilities kind to one. The // 'kind' value should be one or combination (OR'ed) of CAPS, // BOUNDS or AMBS. Fill(kind CapType) // Clear sets all bits of the given capabilities kind to zero. The // 'kind' value should be one or combination (OR'ed) of CAPS, // BOUNDS or AMBS. Clear(kind CapType) // String return current capabilities state of the given capabilities // set as string. The 'which' value should be one of EFFECTIVE, // PERMITTED, INHERITABLE BOUNDING or AMBIENT StringCap(which CapType) string // String return current capabilities state as string. String() string // Load load actual capabilities value. This will overwrite all // outstanding changes. Load() error // Apply apply the capabilities settings, so all changes will take // effect. Apply(kind CapType) error } // NewPid initializes a new Capabilities object for given pid when // it is nonzero, or for the current process if pid is 0. // // Deprecated: Replace with NewPid2. For example, replace: // // c, err := NewPid(0) // if err != nil { // return err // } // // with: // // c, err := NewPid2(0) // if err != nil { // return err // } // err = c.Load() // if err != nil { // return err // } func NewPid(pid int) (Capabilities, error) { c, err := newPid(pid) if err != nil { return c, err } err = c.Load() return c, err } // NewPid2 initializes a new Capabilities object for given pid when // it is nonzero, or for the current process if pid is 0. This // does not load the process's current capabilities; to do that you // must call Load explicitly. func NewPid2(pid int) (Capabilities, error) { return newPid(pid) } // NewFile initializes a new Capabilities object for given file path. // // Deprecated: Replace with NewFile2. For example, replace: // // c, err := NewFile(path) // if err != nil { // return err // } // // with: // // c, err := NewFile2(path) // if err != nil { // return err // } // err = c.Load() // if err != nil { // return err // } func NewFile(path string) (Capabilities, error) { c, err := newFile(path) if err != nil { return c, err } err = c.Load() return c, err } // NewFile2 creates a new initialized Capabilities object for given // file path. This does not load the process's current capabilities; // to do that you must call Load explicitly. func NewFile2(path string) (Capabilities, error) { return newFile(path) } ================================================ FILE: vendor/github.com/syndtr/gocapability/capability/capability_linux.go ================================================ // Copyright (c) 2013, Suryandaru Triandana // All rights reserved. // // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package capability import ( "bufio" "errors" "fmt" "io" "os" "strings" "syscall" ) var errUnknownVers = errors.New("unknown capability version") const ( linuxCapVer1 = 0x19980330 linuxCapVer2 = 0x20071026 linuxCapVer3 = 0x20080522 ) var ( capVers uint32 capLastCap Cap ) func init() { var hdr capHeader capget(&hdr, nil) capVers = hdr.version if initLastCap() == nil { CAP_LAST_CAP = capLastCap if capLastCap > 31 { capUpperMask = (uint32(1) << (uint(capLastCap) - 31)) - 1 } else { capUpperMask = 0 } } } func initLastCap() error { if capLastCap != 0 { return nil } f, err := os.Open("/proc/sys/kernel/cap_last_cap") if err != nil { return err } defer f.Close() var b []byte = make([]byte, 11) _, err = f.Read(b) if err != nil { return err } fmt.Sscanf(string(b), "%d", &capLastCap) return nil } func mkStringCap(c Capabilities, which CapType) (ret string) { for i, first := Cap(0), true; i <= CAP_LAST_CAP; i++ { if !c.Get(which, i) { continue } if first { first = false } else { ret += ", " } ret += i.String() } return } func mkString(c Capabilities, max CapType) (ret string) { ret = "{" for i := CapType(1); i <= max; i <<= 1 { ret += " " + i.String() + "=\"" if c.Empty(i) { ret += "empty" } else if c.Full(i) { ret += "full" } else { ret += c.StringCap(i) } ret += "\"" } ret += " }" return } func newPid(pid int) (c Capabilities, err error) { switch capVers { case linuxCapVer1: p := new(capsV1) p.hdr.version = capVers p.hdr.pid = int32(pid) c = p case linuxCapVer2, linuxCapVer3: p := new(capsV3) p.hdr.version = capVers p.hdr.pid = int32(pid) c = p default: err = errUnknownVers return } return } type capsV1 struct { hdr capHeader data capData } func (c *capsV1) Get(which CapType, what Cap) bool { if what > 32 { return false } switch which { case EFFECTIVE: return (1< 32 { continue } if which&EFFECTIVE != 0 { c.data.effective |= 1 << uint(what) } if which&PERMITTED != 0 { c.data.permitted |= 1 << uint(what) } if which&INHERITABLE != 0 { c.data.inheritable |= 1 << uint(what) } } } func (c *capsV1) Unset(which CapType, caps ...Cap) { for _, what := range caps { if what > 32 { continue } if which&EFFECTIVE != 0 { c.data.effective &= ^(1 << uint(what)) } if which&PERMITTED != 0 { c.data.permitted &= ^(1 << uint(what)) } if which&INHERITABLE != 0 { c.data.inheritable &= ^(1 << uint(what)) } } } func (c *capsV1) Fill(kind CapType) { if kind&CAPS == CAPS { c.data.effective = 0x7fffffff c.data.permitted = 0x7fffffff c.data.inheritable = 0 } } func (c *capsV1) Clear(kind CapType) { if kind&CAPS == CAPS { c.data.effective = 0 c.data.permitted = 0 c.data.inheritable = 0 } } func (c *capsV1) StringCap(which CapType) (ret string) { return mkStringCap(c, which) } func (c *capsV1) String() (ret string) { return mkString(c, BOUNDING) } func (c *capsV1) Load() (err error) { return capget(&c.hdr, &c.data) } func (c *capsV1) Apply(kind CapType) error { if kind&CAPS == CAPS { return capset(&c.hdr, &c.data) } return nil } type capsV3 struct { hdr capHeader data [2]capData bounds [2]uint32 ambient [2]uint32 } func (c *capsV3) Get(which CapType, what Cap) bool { var i uint if what > 31 { i = uint(what) >> 5 what %= 32 } switch which { case EFFECTIVE: return (1< 31 { i = uint(what) >> 5 what %= 32 } if which&EFFECTIVE != 0 { c.data[i].effective |= 1 << uint(what) } if which&PERMITTED != 0 { c.data[i].permitted |= 1 << uint(what) } if which&INHERITABLE != 0 { c.data[i].inheritable |= 1 << uint(what) } if which&BOUNDING != 0 { c.bounds[i] |= 1 << uint(what) } if which&AMBIENT != 0 { c.ambient[i] |= 1 << uint(what) } } } func (c *capsV3) Unset(which CapType, caps ...Cap) { for _, what := range caps { var i uint if what > 31 { i = uint(what) >> 5 what %= 32 } if which&EFFECTIVE != 0 { c.data[i].effective &= ^(1 << uint(what)) } if which&PERMITTED != 0 { c.data[i].permitted &= ^(1 << uint(what)) } if which&INHERITABLE != 0 { c.data[i].inheritable &= ^(1 << uint(what)) } if which&BOUNDING != 0 { c.bounds[i] &= ^(1 << uint(what)) } if which&AMBIENT != 0 { c.ambient[i] &= ^(1 << uint(what)) } } } func (c *capsV3) Fill(kind CapType) { if kind&CAPS == CAPS { c.data[0].effective = 0xffffffff c.data[0].permitted = 0xffffffff c.data[0].inheritable = 0 c.data[1].effective = 0xffffffff c.data[1].permitted = 0xffffffff c.data[1].inheritable = 0 } if kind&BOUNDS == BOUNDS { c.bounds[0] = 0xffffffff c.bounds[1] = 0xffffffff } if kind&AMBS == AMBS { c.ambient[0] = 0xffffffff c.ambient[1] = 0xffffffff } } func (c *capsV3) Clear(kind CapType) { if kind&CAPS == CAPS { c.data[0].effective = 0 c.data[0].permitted = 0 c.data[0].inheritable = 0 c.data[1].effective = 0 c.data[1].permitted = 0 c.data[1].inheritable = 0 } if kind&BOUNDS == BOUNDS { c.bounds[0] = 0 c.bounds[1] = 0 } if kind&AMBS == AMBS { c.ambient[0] = 0 c.ambient[1] = 0 } } func (c *capsV3) StringCap(which CapType) (ret string) { return mkStringCap(c, which) } func (c *capsV3) String() (ret string) { return mkString(c, BOUNDING) } func (c *capsV3) Load() (err error) { err = capget(&c.hdr, &c.data[0]) if err != nil { return } var status_path string if c.hdr.pid == 0 { status_path = fmt.Sprintf("/proc/self/status") } else { status_path = fmt.Sprintf("/proc/%d/status", c.hdr.pid) } f, err := os.Open(status_path) if err != nil { return } b := bufio.NewReader(f) for { line, e := b.ReadString('\n') if e != nil { if e != io.EOF { err = e } break } if strings.HasPrefix(line, "CapB") { fmt.Sscanf(line[4:], "nd: %08x%08x", &c.bounds[1], &c.bounds[0]) continue } if strings.HasPrefix(line, "CapA") { fmt.Sscanf(line[4:], "mb: %08x%08x", &c.ambient[1], &c.ambient[0]) continue } } f.Close() return } func (c *capsV3) Apply(kind CapType) (err error) { if kind&BOUNDS == BOUNDS { var data [2]capData err = capget(&c.hdr, &data[0]) if err != nil { return } if (1< 31 { if c.data.version == 1 { return false } i = uint(what) >> 5 what %= 32 } switch which { case EFFECTIVE: return (1< 31 { if c.data.version == 1 { continue } i = uint(what) >> 5 what %= 32 } if which&EFFECTIVE != 0 { c.data.effective[i] |= 1 << uint(what) } if which&PERMITTED != 0 { c.data.data[i].permitted |= 1 << uint(what) } if which&INHERITABLE != 0 { c.data.data[i].inheritable |= 1 << uint(what) } } } func (c *capsFile) Unset(which CapType, caps ...Cap) { for _, what := range caps { var i uint if what > 31 { if c.data.version == 1 { continue } i = uint(what) >> 5 what %= 32 } if which&EFFECTIVE != 0 { c.data.effective[i] &= ^(1 << uint(what)) } if which&PERMITTED != 0 { c.data.data[i].permitted &= ^(1 << uint(what)) } if which&INHERITABLE != 0 { c.data.data[i].inheritable &= ^(1 << uint(what)) } } } func (c *capsFile) Fill(kind CapType) { if kind&CAPS == CAPS { c.data.effective[0] = 0xffffffff c.data.data[0].permitted = 0xffffffff c.data.data[0].inheritable = 0 if c.data.version == 2 { c.data.effective[1] = 0xffffffff c.data.data[1].permitted = 0xffffffff c.data.data[1].inheritable = 0 } } } func (c *capsFile) Clear(kind CapType) { if kind&CAPS == CAPS { c.data.effective[0] = 0 c.data.data[0].permitted = 0 c.data.data[0].inheritable = 0 if c.data.version == 2 { c.data.effective[1] = 0 c.data.data[1].permitted = 0 c.data.data[1].inheritable = 0 } } } func (c *capsFile) StringCap(which CapType) (ret string) { return mkStringCap(c, which) } func (c *capsFile) String() (ret string) { return mkString(c, INHERITABLE) } func (c *capsFile) Load() (err error) { return getVfsCap(c.path, &c.data) } func (c *capsFile) Apply(kind CapType) (err error) { if kind&CAPS == CAPS { return setVfsCap(c.path, &c.data) } return } ================================================ FILE: vendor/github.com/syndtr/gocapability/capability/capability_noop.go ================================================ // Copyright (c) 2013, Suryandaru Triandana // All rights reserved. // // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // +build !linux package capability import "errors" func newPid(pid int) (Capabilities, error) { return nil, errors.New("not supported") } func newFile(path string) (Capabilities, error) { return nil, errors.New("not supported") } ================================================ FILE: vendor/github.com/syndtr/gocapability/capability/enum.go ================================================ // Copyright (c) 2013, Suryandaru Triandana // All rights reserved. // // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package capability type CapType uint func (c CapType) String() string { switch c { case EFFECTIVE: return "effective" case PERMITTED: return "permitted" case INHERITABLE: return "inheritable" case BOUNDING: return "bounding" case CAPS: return "caps" case AMBIENT: return "ambient" } return "unknown" } const ( EFFECTIVE CapType = 1 << iota PERMITTED INHERITABLE BOUNDING AMBIENT CAPS = EFFECTIVE | PERMITTED | INHERITABLE BOUNDS = BOUNDING AMBS = AMBIENT ) //go:generate go run enumgen/gen.go type Cap int // POSIX-draft defined capabilities and Linux extensions. // // Defined in https://github.com/torvalds/linux/blob/master/include/uapi/linux/capability.h const ( // In a system with the [_POSIX_CHOWN_RESTRICTED] option defined, this // overrides the restriction of changing file ownership and group // ownership. CAP_CHOWN = Cap(0) // Override all DAC access, including ACL execute access if // [_POSIX_ACL] is defined. Excluding DAC access covered by // CAP_LINUX_IMMUTABLE. CAP_DAC_OVERRIDE = Cap(1) // Overrides all DAC restrictions regarding read and search on files // and directories, including ACL restrictions if [_POSIX_ACL] is // defined. Excluding DAC access covered by CAP_LINUX_IMMUTABLE. CAP_DAC_READ_SEARCH = Cap(2) // Overrides all restrictions about allowed operations on files, where // file owner ID must be equal to the user ID, except where CAP_FSETID // is applicable. It doesn't override MAC and DAC restrictions. CAP_FOWNER = Cap(3) // Overrides the following restrictions that the effective user ID // shall match the file owner ID when setting the S_ISUID and S_ISGID // bits on that file; that the effective group ID (or one of the // supplementary group IDs) shall match the file owner ID when setting // the S_ISGID bit on that file; that the S_ISUID and S_ISGID bits are // cleared on successful return from chown(2) (not implemented). CAP_FSETID = Cap(4) // Overrides the restriction that the real or effective user ID of a // process sending a signal must match the real or effective user ID // of the process receiving the signal. CAP_KILL = Cap(5) // Allows setgid(2) manipulation // Allows setgroups(2) // Allows forged gids on socket credentials passing. CAP_SETGID = Cap(6) // Allows set*uid(2) manipulation (including fsuid). // Allows forged pids on socket credentials passing. CAP_SETUID = Cap(7) // Linux-specific capabilities // Without VFS support for capabilities: // Transfer any capability in your permitted set to any pid, // remove any capability in your permitted set from any pid // With VFS support for capabilities (neither of above, but) // Add any capability from current's capability bounding set // to the current process' inheritable set // Allow taking bits out of capability bounding set // Allow modification of the securebits for a process CAP_SETPCAP = Cap(8) // Allow modification of S_IMMUTABLE and S_APPEND file attributes CAP_LINUX_IMMUTABLE = Cap(9) // Allows binding to TCP/UDP sockets below 1024 // Allows binding to ATM VCIs below 32 CAP_NET_BIND_SERVICE = Cap(10) // Allow broadcasting, listen to multicast CAP_NET_BROADCAST = Cap(11) // Allow interface configuration // Allow administration of IP firewall, masquerading and accounting // Allow setting debug option on sockets // Allow modification of routing tables // Allow setting arbitrary process / process group ownership on // sockets // Allow binding to any address for transparent proxying (also via NET_RAW) // Allow setting TOS (type of service) // Allow setting promiscuous mode // Allow clearing driver statistics // Allow multicasting // Allow read/write of device-specific registers // Allow activation of ATM control sockets CAP_NET_ADMIN = Cap(12) // Allow use of RAW sockets // Allow use of PACKET sockets // Allow binding to any address for transparent proxying (also via NET_ADMIN) CAP_NET_RAW = Cap(13) // Allow locking of shared memory segments // Allow mlock and mlockall (which doesn't really have anything to do // with IPC) CAP_IPC_LOCK = Cap(14) // Override IPC ownership checks CAP_IPC_OWNER = Cap(15) // Insert and remove kernel modules - modify kernel without limit CAP_SYS_MODULE = Cap(16) // Allow ioperm/iopl access // Allow sending USB messages to any device via /proc/bus/usb CAP_SYS_RAWIO = Cap(17) // Allow use of chroot() CAP_SYS_CHROOT = Cap(18) // Allow ptrace() of any process CAP_SYS_PTRACE = Cap(19) // Allow configuration of process accounting CAP_SYS_PACCT = Cap(20) // Allow configuration of the secure attention key // Allow administration of the random device // Allow examination and configuration of disk quotas // Allow setting the domainname // Allow setting the hostname // Allow calling bdflush() // Allow mount() and umount(), setting up new smb connection // Allow some autofs root ioctls // Allow nfsservctl // Allow VM86_REQUEST_IRQ // Allow to read/write pci config on alpha // Allow irix_prctl on mips (setstacksize) // Allow flushing all cache on m68k (sys_cacheflush) // Allow removing semaphores // Used instead of CAP_CHOWN to "chown" IPC message queues, semaphores // and shared memory // Allow locking/unlocking of shared memory segment // Allow turning swap on/off // Allow forged pids on socket credentials passing // Allow setting readahead and flushing buffers on block devices // Allow setting geometry in floppy driver // Allow turning DMA on/off in xd driver // Allow administration of md devices (mostly the above, but some // extra ioctls) // Allow tuning the ide driver // Allow access to the nvram device // Allow administration of apm_bios, serial and bttv (TV) device // Allow manufacturer commands in isdn CAPI support driver // Allow reading non-standardized portions of pci configuration space // Allow DDI debug ioctl on sbpcd driver // Allow setting up serial ports // Allow sending raw qic-117 commands // Allow enabling/disabling tagged queuing on SCSI controllers and sending // arbitrary SCSI commands // Allow setting encryption key on loopback filesystem // Allow setting zone reclaim policy // Allow everything under CAP_BPF and CAP_PERFMON for backward compatibility CAP_SYS_ADMIN = Cap(21) // Allow use of reboot() CAP_SYS_BOOT = Cap(22) // Allow raising priority and setting priority on other (different // UID) processes // Allow use of FIFO and round-robin (realtime) scheduling on own // processes and setting the scheduling algorithm used by another // process. // Allow setting cpu affinity on other processes CAP_SYS_NICE = Cap(23) // Override resource limits. Set resource limits. // Override quota limits. // Override reserved space on ext2 filesystem // Modify data journaling mode on ext3 filesystem (uses journaling // resources) // NOTE: ext2 honors fsuid when checking for resource overrides, so // you can override using fsuid too // Override size restrictions on IPC message queues // Allow more than 64hz interrupts from the real-time clock // Override max number of consoles on console allocation // Override max number of keymaps // Control memory reclaim behavior CAP_SYS_RESOURCE = Cap(24) // Allow manipulation of system clock // Allow irix_stime on mips // Allow setting the real-time clock CAP_SYS_TIME = Cap(25) // Allow configuration of tty devices // Allow vhangup() of tty CAP_SYS_TTY_CONFIG = Cap(26) // Allow the privileged aspects of mknod() CAP_MKNOD = Cap(27) // Allow taking of leases on files CAP_LEASE = Cap(28) CAP_AUDIT_WRITE = Cap(29) CAP_AUDIT_CONTROL = Cap(30) CAP_SETFCAP = Cap(31) // Override MAC access. // The base kernel enforces no MAC policy. // An LSM may enforce a MAC policy, and if it does and it chooses // to implement capability based overrides of that policy, this is // the capability it should use to do so. CAP_MAC_OVERRIDE = Cap(32) // Allow MAC configuration or state changes. // The base kernel requires no MAC configuration. // An LSM may enforce a MAC policy, and if it does and it chooses // to implement capability based checks on modifications to that // policy or the data required to maintain it, this is the // capability it should use to do so. CAP_MAC_ADMIN = Cap(33) // Allow configuring the kernel's syslog (printk behaviour) CAP_SYSLOG = Cap(34) // Allow triggering something that will wake the system CAP_WAKE_ALARM = Cap(35) // Allow preventing system suspends CAP_BLOCK_SUSPEND = Cap(36) // Allow reading the audit log via multicast netlink socket CAP_AUDIT_READ = Cap(37) // Allow system performance and observability privileged operations // using perf_events, i915_perf and other kernel subsystems CAP_PERFMON = Cap(38) // CAP_BPF allows the following BPF operations: // - Creating all types of BPF maps // - Advanced verifier features // - Indirect variable access // - Bounded loops // - BPF to BPF function calls // - Scalar precision tracking // - Larger complexity limits // - Dead code elimination // - And potentially other features // - Loading BPF Type Format (BTF) data // - Retrieve xlated and JITed code of BPF programs // - Use bpf_spin_lock() helper // // CAP_PERFMON relaxes the verifier checks further: // - BPF progs can use of pointer-to-integer conversions // - speculation attack hardening measures are bypassed // - bpf_probe_read to read arbitrary kernel memory is allowed // - bpf_trace_printk to print kernel memory is allowed // // CAP_SYS_ADMIN is required to use bpf_probe_write_user. // // CAP_SYS_ADMIN is required to iterate system wide loaded // programs, maps, links, BTFs and convert their IDs to file descriptors. // // CAP_PERFMON and CAP_BPF are required to load tracing programs. // CAP_NET_ADMIN and CAP_BPF are required to load networking programs. CAP_BPF = Cap(39) // Allow checkpoint/restore related operations. // Introduced in kernel 5.9 CAP_CHECKPOINT_RESTORE = Cap(40) ) var ( // Highest valid capability of the running kernel. CAP_LAST_CAP = Cap(63) capUpperMask = ^uint32(0) ) ================================================ FILE: vendor/github.com/syndtr/gocapability/capability/enum_gen.go ================================================ // generated file; DO NOT EDIT - use go generate in directory with source package capability func (c Cap) String() string { switch c { case CAP_CHOWN: return "chown" case CAP_DAC_OVERRIDE: return "dac_override" case CAP_DAC_READ_SEARCH: return "dac_read_search" case CAP_FOWNER: return "fowner" case CAP_FSETID: return "fsetid" case CAP_KILL: return "kill" case CAP_SETGID: return "setgid" case CAP_SETUID: return "setuid" case CAP_SETPCAP: return "setpcap" case CAP_LINUX_IMMUTABLE: return "linux_immutable" case CAP_NET_BIND_SERVICE: return "net_bind_service" case CAP_NET_BROADCAST: return "net_broadcast" case CAP_NET_ADMIN: return "net_admin" case CAP_NET_RAW: return "net_raw" case CAP_IPC_LOCK: return "ipc_lock" case CAP_IPC_OWNER: return "ipc_owner" case CAP_SYS_MODULE: return "sys_module" case CAP_SYS_RAWIO: return "sys_rawio" case CAP_SYS_CHROOT: return "sys_chroot" case CAP_SYS_PTRACE: return "sys_ptrace" case CAP_SYS_PACCT: return "sys_pacct" case CAP_SYS_ADMIN: return "sys_admin" case CAP_SYS_BOOT: return "sys_boot" case CAP_SYS_NICE: return "sys_nice" case CAP_SYS_RESOURCE: return "sys_resource" case CAP_SYS_TIME: return "sys_time" case CAP_SYS_TTY_CONFIG: return "sys_tty_config" case CAP_MKNOD: return "mknod" case CAP_LEASE: return "lease" case CAP_AUDIT_WRITE: return "audit_write" case CAP_AUDIT_CONTROL: return "audit_control" case CAP_SETFCAP: return "setfcap" case CAP_MAC_OVERRIDE: return "mac_override" case CAP_MAC_ADMIN: return "mac_admin" case CAP_SYSLOG: return "syslog" case CAP_WAKE_ALARM: return "wake_alarm" case CAP_BLOCK_SUSPEND: return "block_suspend" case CAP_AUDIT_READ: return "audit_read" case CAP_PERFMON: return "perfmon" case CAP_BPF: return "bpf" case CAP_CHECKPOINT_RESTORE: return "checkpoint_restore" } return "unknown" } // List returns list of all supported capabilities func List() []Cap { return []Cap{ CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_DAC_READ_SEARCH, CAP_FOWNER, CAP_FSETID, CAP_KILL, CAP_SETGID, CAP_SETUID, CAP_SETPCAP, CAP_LINUX_IMMUTABLE, CAP_NET_BIND_SERVICE, CAP_NET_BROADCAST, CAP_NET_ADMIN, CAP_NET_RAW, CAP_IPC_LOCK, CAP_IPC_OWNER, CAP_SYS_MODULE, CAP_SYS_RAWIO, CAP_SYS_CHROOT, CAP_SYS_PTRACE, CAP_SYS_PACCT, CAP_SYS_ADMIN, CAP_SYS_BOOT, CAP_SYS_NICE, CAP_SYS_RESOURCE, CAP_SYS_TIME, CAP_SYS_TTY_CONFIG, CAP_MKNOD, CAP_LEASE, CAP_AUDIT_WRITE, CAP_AUDIT_CONTROL, CAP_SETFCAP, CAP_MAC_OVERRIDE, CAP_MAC_ADMIN, CAP_SYSLOG, CAP_WAKE_ALARM, CAP_BLOCK_SUSPEND, CAP_AUDIT_READ, CAP_PERFMON, CAP_BPF, CAP_CHECKPOINT_RESTORE, } } ================================================ FILE: vendor/github.com/syndtr/gocapability/capability/syscall_linux.go ================================================ // Copyright (c) 2013, Suryandaru Triandana // All rights reserved. // // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package capability import ( "syscall" "unsafe" ) type capHeader struct { version uint32 pid int32 } type capData struct { effective uint32 permitted uint32 inheritable uint32 } func capget(hdr *capHeader, data *capData) (err error) { _, _, e1 := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(hdr)), uintptr(unsafe.Pointer(data)), 0) if e1 != 0 { err = e1 } return } func capset(hdr *capHeader, data *capData) (err error) { _, _, e1 := syscall.Syscall(syscall.SYS_CAPSET, uintptr(unsafe.Pointer(hdr)), uintptr(unsafe.Pointer(data)), 0) if e1 != 0 { err = e1 } return } // not yet in syscall const ( pr_CAP_AMBIENT = 47 pr_CAP_AMBIENT_IS_SET = uintptr(1) pr_CAP_AMBIENT_RAISE = uintptr(2) pr_CAP_AMBIENT_LOWER = uintptr(3) pr_CAP_AMBIENT_CLEAR_ALL = uintptr(4) ) func prctl(option int, arg2, arg3, arg4, arg5 uintptr) (err error) { _, _, e1 := syscall.Syscall6(syscall.SYS_PRCTL, uintptr(option), arg2, arg3, arg4, arg5, 0) if e1 != 0 { err = e1 } return } const ( vfsXattrName = "security.capability" vfsCapVerMask = 0xff000000 vfsCapVer1 = 0x01000000 vfsCapVer2 = 0x02000000 vfsCapFlagMask = ^vfsCapVerMask vfsCapFlageffective = 0x000001 vfscapDataSizeV1 = 4 * (1 + 2*1) vfscapDataSizeV2 = 4 * (1 + 2*2) ) type vfscapData struct { magic uint32 data [2]struct { permitted uint32 inheritable uint32 } effective [2]uint32 version int8 } var ( _vfsXattrName *byte ) func init() { _vfsXattrName, _ = syscall.BytePtrFromString(vfsXattrName) } func getVfsCap(path string, dest *vfscapData) (err error) { var _p0 *byte _p0, err = syscall.BytePtrFromString(path) if err != nil { return } r0, _, e1 := syscall.Syscall6(syscall.SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_vfsXattrName)), uintptr(unsafe.Pointer(dest)), vfscapDataSizeV2, 0, 0) if e1 != 0 { if e1 == syscall.ENODATA { dest.version = 2 return } err = e1 } switch dest.magic & vfsCapVerMask { case vfsCapVer1: dest.version = 1 if r0 != vfscapDataSizeV1 { return syscall.EINVAL } dest.data[1].permitted = 0 dest.data[1].inheritable = 0 case vfsCapVer2: dest.version = 2 if r0 != vfscapDataSizeV2 { return syscall.EINVAL } default: return syscall.EINVAL } if dest.magic&vfsCapFlageffective != 0 { dest.effective[0] = dest.data[0].permitted | dest.data[0].inheritable dest.effective[1] = dest.data[1].permitted | dest.data[1].inheritable } else { dest.effective[0] = 0 dest.effective[1] = 0 } return } func setVfsCap(path string, data *vfscapData) (err error) { var _p0 *byte _p0, err = syscall.BytePtrFromString(path) if err != nil { return } var size uintptr if data.version == 1 { data.magic = vfsCapVer1 size = vfscapDataSizeV1 } else if data.version == 2 { data.magic = vfsCapVer2 if data.effective[0] != 0 || data.effective[1] != 0 { data.magic |= vfsCapFlageffective } size = vfscapDataSizeV2 } else { return syscall.EINVAL } _, _, e1 := syscall.Syscall6(syscall.SYS_SETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_vfsXattrName)), uintptr(unsafe.Pointer(data)), size, 0, 0) if e1 != 0 { err = e1 } return } ================================================ FILE: vendor/github.com/temoto/robotstxt/.gitignore ================================================ *.cgo?.* *.o *.so *.sublime-* *.zip .DS_Store .idea/ .tags* _cgo_* _gofuzz/crashers/ _gofuzz/suppressions/ _obj _test coverage.txt robots.txt-check/robots.txt-check ================================================ FILE: vendor/github.com/temoto/robotstxt/.golangci.yml ================================================ linters: enable: - goconst - gofmt - gosec - maligned - prealloc - staticcheck disable: - deadcode - structcheck - varcheck linters-settings: gofmt: simplify: true govet: check-shadowing: true maligned: suggest-new: true ================================================ FILE: vendor/github.com/temoto/robotstxt/.travis.yml ================================================ cache: go: true directories: - $HOME/.cache - $HOME/bin - $HOME/gopath/pkg/mod language: go go: - 1.11 - 1.12 - 1.13 - 1.14 - 1.x - master install: true script: GO111MODULE=on go test -race matrix: include: - go: 1.x env: task=coverage script: GO111MODULE=on go test -race -covermode=atomic -coverprofile=coverage.txt after_success: bash <(curl -s https://codecov.io/bash) - go: 1.x env: task=bench script: GO111MODULE=on ./script/bench - go: 1.x install: curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $HOME/bin v1.19.1 env: task=clean script: GO111MODULE=on ./script/clean ================================================ FILE: vendor/github.com/temoto/robotstxt/LICENSE ================================================ The MIT License Copyright (c) 2010 Sergey Shepelev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/temoto/robotstxt/README.rst ================================================ What ==== This is a robots.txt exclusion protocol implementation for Go language (golang). Build ===== To build and run tests run `go test` in source directory. Contribute ========== Warm welcome. * If desired, add your name in README.rst, section Who. * Run `script/test && script/clean && echo ok` * You can ignore linter warnings, but everything else must pass. * Send your change as pull request or just a regular patch to current maintainer (see section Who). Thank you. Usage ===== As usual, no special installation is required, just import "github.com/temoto/robotstxt" run `go get` and you're ready. 1. Parse ^^^^^^^^ First of all, you need to parse robots.txt data. You can do it with functions `FromBytes(body []byte) (*RobotsData, error)` or same for `string`:: robots, err := robotstxt.FromBytes([]byte("User-agent: *\nDisallow:")) robots, err := robotstxt.FromString("User-agent: *\nDisallow:") As of 2012-10-03, `FromBytes` is the most efficient method, everything else is a wrapper for this core function. There are few convenient constructors for various purposes: * `FromResponse(*http.Response) (*RobotsData, error)` to init robots data from HTTP response. It *does not* call `response.Body.Close()`:: robots, err := robotstxt.FromResponse(resp) resp.Body.Close() if err != nil { log.Println("Error parsing robots.txt:", err.Error()) } * `FromStatusAndBytes(statusCode int, body []byte) (*RobotsData, error)` or `FromStatusAndString` if you prefer to read bytes (string) yourself. Passing status code applies following logic in line with Google's interpretation of robots.txt files: * status 2xx -> parse body with `FromBytes` and apply rules listed there. * status 4xx -> allow all (even 401/403, as recommended by Google). * other (5xx) -> disallow all, consider this a temporary unavailability. 2. Query ^^^^^^^^ Parsing robots.txt content builds a kind of logic database, which you can query with `(r *RobotsData) TestAgent(url, agent string) (bool)`. Explicit passing of agent is useful if you want to query for different agents. For single agent users there is an efficient option: `RobotsData.FindGroup(userAgent string)` returns a structure with `.Test(path string)` method and `.CrawlDelay time.Duration`. Simple query with explicit user agent. Each call will scan all rules. :: allow := robots.TestAgent("/", "FooBot") Or query several paths against same user agent for performance. :: group := robots.FindGroup("BarBot") group.Test("/") group.Test("/download.mp3") group.Test("/news/article-2012-1") Who === Honorable contributors (in undefined order): * Ilya Grigorik (igrigorik) * Martin Angers (PuerkitoBio) * Micha Gorelick (mynameisfiber) Initial commit and other: Sergey Shepelev temotor@gmail.com Flair ===== .. image:: https://travis-ci.org/temoto/robotstxt.svg?branch=master :target: https://travis-ci.org/temoto/robotstxt .. image:: https://codecov.io/gh/temoto/robotstxt/branch/master/graph/badge.svg :target: https://codecov.io/gh/temoto/robotstxt .. image:: https://goreportcard.com/badge/github.com/temoto/robotstxt :target: https://goreportcard.com/report/github.com/temoto/robotstxt ================================================ FILE: vendor/github.com/temoto/robotstxt/codecov.yml ================================================ codecov: token: 6bf9c7eb-69ff-4b74-8464-e2fb452d0f04 ================================================ FILE: vendor/github.com/temoto/robotstxt/fuzz.go ================================================ // +build gofuzz package robotstxt import "testing/quick" func Fuzz(data []byte) int { r, err := FromBytes(data) if err != nil { if r != nil { panic("r != nil on error") } return 0 } // FindGroup must never return nil f1 := func(agent string) bool { return r.FindGroup(agent) != nil } if err := quick.Check(f1, nil); err != nil { panic(err) } // just check TestAgent doesn't panic f2 := func(path, agent string) bool { r.TestAgent(path, agent); return true } if err := quick.Check(f2, nil); err != nil { panic(err) } return 1 } ================================================ FILE: vendor/github.com/temoto/robotstxt/parser.go ================================================ package robotstxt // Comments explaining the logic are taken from either the google's spec: // https://developers.google.com/webmasters/control-crawl-index/docs/robots_txt // // or the Wikipedia's entry on robots.txt: // http://en.wikipedia.org/wiki/Robots.txt import ( "fmt" "io" "math" "regexp" "strconv" "strings" "time" ) type lineType uint const ( lIgnore lineType = iota lUnknown lUserAgent lAllow lDisallow lCrawlDelay lSitemap lHost ) type parser struct { tokens []string pos int } type lineInfo struct { t lineType // Type of line key k string // String representation of the type of key vs string // String value of the key vf float64 // Float value of the key vr *regexp.Regexp // Regexp value of the key } func newParser(tokens []string) *parser { return &parser{tokens: tokens} } func parseGroupMap(groups map[string]*Group, agents []string, fun func(*Group)) { var g *Group for _, a := range agents { if g = groups[a]; g == nil { g = new(Group) groups[a] = g } fun(g) } } func (p *parser) parseAll() (groups map[string]*Group, host string, sitemaps []string, errs []error) { groups = make(map[string]*Group, 16) agents := make([]string, 0, 4) isEmptyGroup := true // Reset internal fields, tokens are assigned at creation time, never change p.pos = 0 for { if li, err := p.parseLine(); err != nil { if err == io.EOF { break } errs = append(errs, err) } else { switch li.t { case lUserAgent: // Two successive user-agent lines are part of the same group. if !isEmptyGroup { // End previous group agents = make([]string, 0, 4) } if len(agents) == 0 { isEmptyGroup = true } agents = append(agents, li.vs) case lDisallow: // Error if no current group if len(agents) == 0 { errs = append(errs, fmt.Errorf("Disallow before User-agent at token #%d.", p.pos)) } else { isEmptyGroup = false var r *rule if li.vr != nil { r = &rule{"", false, li.vr} } else { r = &rule{li.vs, false, nil} } parseGroupMap(groups, agents, func(g *Group) { g.rules = append(g.rules, r) }) } case lAllow: // Error if no current group if len(agents) == 0 { errs = append(errs, fmt.Errorf("Allow before User-agent at token #%d.", p.pos)) } else { isEmptyGroup = false var r *rule if li.vr != nil { r = &rule{"", true, li.vr} } else { r = &rule{li.vs, true, nil} } parseGroupMap(groups, agents, func(g *Group) { g.rules = append(g.rules, r) }) } case lHost: host = li.vs case lSitemap: sitemaps = append(sitemaps, li.vs) case lCrawlDelay: if len(agents) == 0 { errs = append(errs, fmt.Errorf("Crawl-delay before User-agent at token #%d.", p.pos)) } else { isEmptyGroup = false delay := time.Duration(li.vf * float64(time.Second)) parseGroupMap(groups, agents, func(g *Group) { g.CrawlDelay = delay }) } } } } return } func (p *parser) parseLine() (li *lineInfo, err error) { t1, ok1 := p.popToken() if !ok1 { // proper EOF return nil, io.EOF } t2, ok2 := p.peekToken() if !ok2 { // EOF, no value associated with the token, so ignore token and return return nil, io.EOF } // Helper closure for all string-based tokens, common behaviour: // - Consume t2 token // - If empty, return unknown line info // - Otherwise return the specified line info returnStringVal := func(t lineType) (*lineInfo, error) { p.popToken() if t2 != "" { return &lineInfo{t: t, k: t1, vs: t2}, nil } return &lineInfo{t: lIgnore}, nil } // Helper closure for all path tokens (allow/disallow), common behaviour: // - Consume t2 token // - If empty, return unknown line info // - Otherwise, normalize the path (add leading "/" if missing, remove trailing "*") // - Detect if wildcards are present, if so, compile into a regexp // - Return the specified line info returnPathVal := func(t lineType) (*lineInfo, error) { p.popToken() if t2 != "" { if !strings.HasPrefix(t2, "*") && !strings.HasPrefix(t2, "/") { t2 = "/" + t2 } t2 = strings.TrimRightFunc(t2, isAsterisk) // From google's spec: // Google, Bing, Yahoo, and Ask support a limited form of // "wildcards" for path values. These are: // * designates 0 or more instances of any valid character // $ designates the end of the URL if strings.ContainsAny(t2, "*$") { // Must compile a regexp, this is a pattern. // Escape string before compile. t2 = regexp.QuoteMeta(t2) t2 = strings.Replace(t2, `\*`, `.*`, -1) t2 = strings.Replace(t2, `\$`, `$`, -1) if r, e := regexp.Compile(t2); e != nil { return nil, e } else { return &lineInfo{t: t, k: t1, vr: r}, nil } } else { // Simple string path return &lineInfo{t: t, k: t1, vs: t2}, nil } } return &lineInfo{t: lIgnore}, nil } switch strings.ToLower(t1) { case tokEOL: // Don't consume t2 and continue parsing return &lineInfo{t: lIgnore}, nil case "user-agent", "useragent": // From google's spec: // Handling of elements with simple errors / typos (eg "useragent" // instead of "user-agent") is undefined and may be interpreted as correct // directives by some user-agents. // The user-agent is non-case-sensitive. t2 = strings.ToLower(t2) return returnStringVal(lUserAgent) case "disallow": // From google's spec: // When no path is specified, the directive is ignored (so an empty Disallow // CAN be an allow, since allow is the default. The actual result depends // on the other rules in the group). return returnPathVal(lDisallow) case "allow": // From google's spec: // When no path is specified, the directive is ignored. return returnPathVal(lAllow) case "host": // Host directive to specify main site mirror // Read more: https://help.yandex.com/webmaster/controlling-robot/robots-txt.xml#host return returnStringVal(lHost) case "sitemap": // Non-group field, applies to the host as a whole, not to a specific user-agent return returnStringVal(lSitemap) case "crawl-delay", "crawldelay": // From http://en.wikipedia.org/wiki/Robots_exclusion_standard#Nonstandard_extensions // Several major crawlers support a Crawl-delay parameter, set to the // number of seconds to wait between successive requests to the same server. p.popToken() if cd, e := strconv.ParseFloat(t2, 64); e != nil { return nil, e } else if cd < 0 || math.IsInf(cd, 0) || math.IsNaN(cd) { return nil, fmt.Errorf("Crawl-delay invalid value '%s'", t2) } else { return &lineInfo{t: lCrawlDelay, k: t1, vf: cd}, nil } } // Consume t2 token p.popToken() return &lineInfo{t: lUnknown, k: t1}, nil } func (p *parser) popToken() (tok string, ok bool) { tok, ok = p.peekToken() if !ok { return } p.pos++ return tok, true } func (p *parser) peekToken() (tok string, ok bool) { if p.pos >= len(p.tokens) { return "", false } return p.tokens[p.pos], true } func isAsterisk(r rune) bool { return r == '*' } ================================================ FILE: vendor/github.com/temoto/robotstxt/robotstxt.go ================================================ // Package robotstxt implements the robots.txt Exclusion Protocol // as specified in http://www.robotstxt.org/wc/robots.html // with various extensions. package robotstxt // Comments explaining the logic are taken from either the Google's spec: // https://developers.google.com/webmasters/control-crawl-index/docs/robots_txt import ( "bytes" "errors" "io/ioutil" "net/http" "regexp" "strconv" "strings" "time" ) type RobotsData struct { // private groups map[string]*Group allowAll bool disallowAll bool Host string Sitemaps []string } type Group struct { rules []*rule Agent string CrawlDelay time.Duration } type rule struct { path string allow bool pattern *regexp.Regexp } type ParseError struct { Errs []error } func newParseError(errs []error) *ParseError { return &ParseError{errs} } func (e ParseError) Error() string { var b bytes.Buffer b.WriteString("Parse error(s): " + "\n") for _, er := range e.Errs { b.WriteString(er.Error() + "\n") } return b.String() } var allowAll = &RobotsData{allowAll: true} var disallowAll = &RobotsData{disallowAll: true} var emptyGroup = &Group{} func FromStatusAndBytes(statusCode int, body []byte) (*RobotsData, error) { switch { case statusCode >= 200 && statusCode < 300: return FromBytes(body) // From https://developers.google.com/webmasters/control-crawl-index/docs/robots_txt // // Google treats all 4xx errors in the same way and assumes that no valid // robots.txt file exists. It is assumed that there are no restrictions. // This is a "full allow" for crawling. Note: this includes 401 // "Unauthorized" and 403 "Forbidden" HTTP result codes. case statusCode >= 400 && statusCode < 500: return allowAll, nil // From Google's spec: // Server errors (5xx) are seen as temporary errors that result in a "full // disallow" of crawling. case statusCode >= 500 && statusCode < 600: return disallowAll, nil } return nil, errors.New("Unexpected status: " + strconv.Itoa(statusCode)) } func FromStatusAndString(statusCode int, body string) (*RobotsData, error) { return FromStatusAndBytes(statusCode, []byte(body)) } func FromResponse(res *http.Response) (*RobotsData, error) { if res == nil { // Edge case, if res is nil, return nil data return nil, nil } buf, e := ioutil.ReadAll(res.Body) if e != nil { return nil, e } return FromStatusAndBytes(res.StatusCode, buf) } func FromBytes(body []byte) (r *RobotsData, err error) { var errs []error // special case (probably not worth optimization?) trimmed := bytes.TrimSpace(body) if len(trimmed) == 0 { return allowAll, nil } sc := newByteScanner("bytes", true) //sc.Quiet = !print_errors sc.feed(body, true) tokens := sc.scanAll() // special case worth optimization if len(tokens) == 0 { return allowAll, nil } r = &RobotsData{} parser := newParser(tokens) r.groups, r.Host, r.Sitemaps, errs = parser.parseAll() if len(errs) > 0 { return nil, newParseError(errs) } return r, nil } func FromString(body string) (r *RobotsData, err error) { return FromBytes([]byte(body)) } func (r *RobotsData) TestAgent(path, agent string) bool { if r.allowAll { return true } if r.disallowAll { return false } // Find a group of rules that applies to this agent // From Google's spec: // The user-agent is non-case-sensitive. g := r.FindGroup(agent) return g.Test(path) } // FindGroup searches block of declarations for specified user-agent. // From Google's spec: // Only one group of group-member records is valid for a particular crawler. // The crawler must determine the correct group of records by finding the group // with the most specific user-agent that still matches. All other groups of // records are ignored by the crawler. The user-agent is non-case-sensitive. // The order of the groups within the robots.txt file is irrelevant. func (r *RobotsData) FindGroup(agent string) (ret *Group) { var prefixLen int agent = strings.ToLower(agent) if ret = r.groups["*"]; ret != nil { // Weakest match possible prefixLen = 1 } for a, g := range r.groups { if a != "*" && strings.HasPrefix(agent, a) { if l := len(a); l > prefixLen { prefixLen = l ret = g } } } if ret == nil { return emptyGroup } return } func (g *Group) Test(path string) bool { if r := g.findRule(path); r != nil { return r.allow } // From Google's spec: // By default, there are no restrictions for crawling for the designated crawlers. return true } // From Google's spec: // The path value is used as a basis to determine whether or not a rule applies // to a specific URL on a site. With the exception of wildcards, the path is // used to match the beginning of a URL (and any valid URLs that start with the // same path). // // At a group-member level, in particular for allow and disallow directives, // the most specific rule based on the length of the [path] entry will trump // the less specific (shorter) rule. The order of precedence for rules with // wildcards is undefined. func (g *Group) findRule(path string) (ret *rule) { var prefixLen int for _, r := range g.rules { if r.pattern != nil { if r.pattern.MatchString(path) { // Consider this a match equal to the length of the pattern. // From Google's spec: // The order of precedence for rules with wildcards is undefined. if l := len(r.pattern.String()); l > prefixLen { prefixLen = l ret = r } } } else if r.path == "/" && prefixLen == 0 { // Weakest match possible prefixLen = 1 ret = r } else if strings.HasPrefix(path, r.path) { if l := len(r.path); l > prefixLen { prefixLen = l ret = r } } } return } ================================================ FILE: vendor/github.com/temoto/robotstxt/scanner.go ================================================ package robotstxt import ( "bytes" "fmt" "go/token" "os" "sync" "unicode/utf8" ) type byteScanner struct { pos token.Position buf []byte ErrorCount int ch rune Quiet bool keyTokenFound bool lastChunk bool } const tokEOL = "\n" var WhitespaceChars = []rune{' ', '\t', '\v'} var tokBuffers = sync.Pool{New: func() interface{} { return bytes.NewBuffer(make([]byte, 32)) }} func newByteScanner(srcname string, quiet bool) *byteScanner { return &byteScanner{ Quiet: quiet, ch: -1, pos: token.Position{Filename: srcname}, } } func (s *byteScanner) feed(input []byte, end bool) { s.buf = input s.pos.Offset = 0 s.pos.Line = 1 s.pos.Column = 1 s.lastChunk = end // Read first char into look-ahead buffer `s.ch`. if !s.nextChar() { return } // Skip UTF-8 byte order mark if s.ch == 65279 { s.nextChar() s.pos.Column = 1 } } func (s *byteScanner) GetPosition() token.Position { return s.pos } func (s *byteScanner) scan() string { // Note Offset > len, not >=, so we can scan last character. if s.lastChunk && s.pos.Offset > len(s.buf) { return "" } s.skipSpace() if s.ch == -1 { return "" } // EOL if s.isEol() { s.keyTokenFound = false // skip subsequent newline chars for s.ch != -1 && s.isEol() { s.nextChar() } // emit newline as separate token return tokEOL } // skip comments if s.ch == '#' { s.keyTokenFound = false s.skipUntilEol() if s.ch == -1 { return "" } // emit newline as separate token return tokEOL } // else we found something tok := tokBuffers.Get().(*bytes.Buffer) defer tokBuffers.Put(tok) tok.Reset() tok.WriteRune(s.ch) s.nextChar() for s.ch != -1 && !s.isSpace() && !s.isEol() { // Do not consider ":" to be a token separator if a first key token // has already been found on this line (avoid cutting an absolute URL // after the "http:") if s.ch == ':' && !s.keyTokenFound { s.nextChar() s.keyTokenFound = true break } tok.WriteRune(s.ch) s.nextChar() } return tok.String() } func (s *byteScanner) scanAll() []string { results := make([]string, 0, 64) // random guess of average tokens length for { token := s.scan() if token != "" { results = append(results, token) } else { break } } return results } func (s *byteScanner) error(pos token.Position, msg string) { s.ErrorCount++ if !s.Quiet { fmt.Fprintf(os.Stderr, "robotstxt from %s: %s\n", pos.String(), msg) } } func (s *byteScanner) isEol() bool { return s.ch == '\n' || s.ch == '\r' } func (s *byteScanner) isSpace() bool { for _, r := range WhitespaceChars { if s.ch == r { return true } } return false } func (s *byteScanner) skipSpace() { for s.ch != -1 && s.isSpace() { s.nextChar() } } func (s *byteScanner) skipUntilEol() { for s.ch != -1 && !s.isEol() { s.nextChar() } // skip subsequent newline chars for s.ch != -1 && s.isEol() { s.nextChar() } } // Reads next Unicode char. func (s *byteScanner) nextChar() bool { if s.pos.Offset >= len(s.buf) { s.ch = -1 return false } s.pos.Column++ if s.ch == '\n' { s.pos.Line++ s.pos.Column = 1 } r, w := rune(s.buf[s.pos.Offset]), 1 if r >= 0x80 { r, w = utf8.DecodeRune(s.buf[s.pos.Offset:]) if r == utf8.RuneError && w == 1 { s.error(s.pos, "illegal UTF-8 encoding") } } s.pos.Column++ s.pos.Offset += w s.ch = r return true } ================================================ FILE: vendor/github.com/ulikunitz/xz/.gitignore ================================================ # .gitignore TODO.html README.html lzma/writer.txt lzma/reader.txt cmd/gxz/gxz cmd/xb/xb # test executables *.test # profile files *.out # vim swap file .*.swp # executables on windows *.exe # default compression test file enwik8* ================================================ FILE: vendor/github.com/ulikunitz/xz/LICENSE ================================================ Copyright (c) 2014-2020 Ulrich Kunitz All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * My name, Ulrich Kunitz, may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/ulikunitz/xz/README.md ================================================ # Package xz This Go language package supports the reading and writing of xz compressed streams. It includes also a gxz command for compressing and decompressing data. The package is completely written in Go and doesn't have any dependency on any C code. The package is currently under development. There might be bugs and APIs are not considered stable. At this time the package cannot compete with the xz tool regarding compression speed and size. The algorithms there have been developed over a long time and are highly optimized. However there are a number of improvements planned and I'm very optimistic about parallel compression and decompression. Stay tuned! ## Using the API The following example program shows how to use the API. ```go package main import ( "bytes" "io" "log" "os" "github.com/ulikunitz/xz" ) func main() { const text = "The quick brown fox jumps over the lazy dog.\n" var buf bytes.Buffer // compress text w, err := xz.NewWriter(&buf) if err != nil { log.Fatalf("xz.NewWriter error %s", err) } if _, err := io.WriteString(w, text); err != nil { log.Fatalf("WriteString error %s", err) } if err := w.Close(); err != nil { log.Fatalf("w.Close error %s", err) } // decompress buffer and write output to stdout r, err := xz.NewReader(&buf) if err != nil { log.Fatalf("NewReader error %s", err) } if _, err = io.Copy(os.Stdout, r); err != nil { log.Fatalf("io.Copy error %s", err) } } ``` ## Using the gxz compression tool The package includes a gxz command line utility for compression and decompression. Use following command for installation: $ go get github.com/ulikunitz/xz/cmd/gxz To test it call the following command. $ gxz bigfile After some time a much smaller file bigfile.xz will replace bigfile. To decompress it use the following command. $ gxz -d bigfile.xz ================================================ FILE: vendor/github.com/ulikunitz/xz/TODO.md ================================================ # TODO list ## Release v0.5.x 1. Support check flag in gxz command. ## Release v0.6 1. Review encoder and check for lzma improvements under xz. 2. Fix binary tree matcher. 3. Compare compression ratio with xz tool using comparable parameters and optimize parameters 4. Do some optimizations - rename operation action and make it a simple type of size 8 - make maxMatches, wordSize parameters - stop searching after a certain length is found (parameter sweetLen) ## Release v0.7 1. Optimize code 2. Do statistical analysis to get linear presets. 3. Test sync.Pool compatability for xz and lzma Writer and Reader 3. Fuzz optimized code. ## Release v0.8 1. Support parallel go routines for writing and reading xz files. 2. Support a ReaderAt interface for xz files with small block sizes. 3. Improve compatibility between gxz and xz 4. Provide manual page for gxz ## Release v0.9 1. Improve documentation 2. Fuzz again ## Release v1.0 1. Full functioning gxz 2. Add godoc URL to README.md (godoc.org) 3. Resolve all issues. 4. Define release candidates. 5. Public announcement. ## Package lzma ### Release v0.6 - Rewrite Encoder into a simple greedy one-op-at-a-time encoder including + simple scan at the dictionary head for the same byte + use the killer byte (requiring matches to get longer, the first test should be the byte that would make the match longer) ## Optimizations - There may be a lot of false sharing in lzma.State; check whether this can be improved by reorganizing the internal structure of it. - Check whether batching encoding and decoding improves speed. ### DAG optimizations - Use full buffer to create minimal bit-length above range encoder. - Might be too slow (see v0.4) ### Different match finders - hashes with 2, 3 characters additional to 4 characters - binary trees with 2-7 characters (uint64 as key, use uint32 as pointers into a an array) - rb-trees with 2-7 characters (uint64 as key, use uint32 as pointers into an array with bit-steeling for the colors) ## Release Procedure - execute goch -l for all packages; probably with lower param like 0.5. - check orthography with gospell - Write release notes in doc/relnotes. - Update README.md - xb copyright . in xz directory to ensure all new files have Copyright header - VERSION= go generate github.com/ulikunitz/xz/... to update version files - Execute test for Linux/amd64, Linux/x86 and Windows/amd64. - Update TODO.md - write short log entry - git checkout master && git merge dev - git tag -a - git push ## Log ### 2020-02-24 Release v0.5.7 supports the check-ID None and fixes [issue #27](https://github.com/ulikunitz/xz/issues/27). ### 2019-02-20 Release v0.5.6 supports the go.mod file. ### 2018-10-28 Release v0.5.5 fixes issues #19 observing ErrLimit outputs. ### 2017-06-05 Release v0.5.4 fixes issues #15 of another problem with the padding size check for the xz block header. I removed the check completely. ### 2017-02-15 Release v0.5.3 fixes issue #12 regarding the decompression of an empty XZ stream. Many thanks to Tomasz Kłak, who reported the issue. ### 2016-12-02 Release v0.5.2 became necessary to allow the decoding of xz files with 4-byte padding in the block header. Many thanks to Greg, who reported the issue. ### 2016-07-23 Release v0.5.1 became necessary to fix problems with 32-bit platforms. Many thanks to Bruno Brigas, who reported the issue. ### 2016-07-04 Release v0.5 provides improvements to the compressor and provides support for the decompression of xz files with multiple xz streams. ### 2016-01-31 Another compression rate increase by checking the byte at length of the best match first, before checking the whole prefix. This makes the compressor even faster. We have now a large time budget to beat the compression ratio of the xz tool. For enwik8 we have now over 40 seconds to reduce the compressed file size for another 7 MiB. ### 2016-01-30 I simplified the encoder. Speed and compression rate increased dramatically. A high compression rate affects also the decompression speed. The approach with the buffer and optimizing for operation compression rate has not been successful. Going for the maximum length appears to be the best approach. ### 2016-01-28 The release v0.4 is ready. It provides a working xz implementation, which is rather slow, but works and is interoperable with the xz tool. It is an important milestone. ### 2016-01-10 I have the first working implementation of an xz reader and writer. I'm happy about reaching this milestone. ### 2015-12-02 I'm now ready to implement xz because, I have a working LZMA2 implementation. I decided today that v0.4 will use the slow encoder using the operations buffer to be able to go back, if I intend to do so. ### 2015-10-21 I have restarted the work on the library. While trying to implement LZMA2, I discovered that I need to resimplify the encoder and decoder functions. The option approach is too complicated. Using a limited byte writer and not caring for written bytes at all and not to try to handle uncompressed data simplifies the LZMA encoder and decoder much. Processing uncompressed data and handling limits is a feature of the LZMA2 format not of LZMA. I learned an interesting method from the LZO format. If the last copy is too far away they are moving the head one 2 bytes and not 1 byte to reduce processing times. ### 2015-08-26 I have now reimplemented the lzma package. The code is reasonably fast, but can still be optimized. The next step is to implement LZMA2 and then xz. ### 2015-07-05 Created release v0.3. The version is the foundation for a full xz implementation that is the target of v0.4. ### 2015-06-11 The gflag package has been developed because I couldn't use flag and pflag for a fully compatible support of gzip's and lzma's options. It seems to work now quite nicely. ### 2015-06-05 The overflow issue was interesting to research, however Henry S. Warren Jr. Hacker's Delight book was very helpful as usual and had the issue explained perfectly. Fefe's information on his website was based on the C FAQ and quite bad, because it didn't address the issue of -MININT == MININT. ### 2015-06-04 It has been a productive day. I improved the interface of lzma.Reader and lzma.Writer and fixed the error handling. ### 2015-06-01 By computing the bit length of the LZMA operations I was able to improve the greedy algorithm implementation. By using an 8 MByte buffer the compression rate was not as good as for xz but already better then gzip default. Compression is currently slow, but this is something we will be able to improve over time. ### 2015-05-26 Checked the license of ogier/pflag. The binary lzmago binary should include the license terms for the pflag library. I added the endorsement clause as used by Google for the Go sources the LICENSE file. ### 2015-05-22 The package lzb contains now the basic implementation for creating or reading LZMA byte streams. It allows the support for the implementation of the DAG-shortest-path algorithm for the compression function. ### 2015-04-23 Completed yesterday the lzbase classes. I'm a little bit concerned that using the components may require too much code, but on the other hand there is a lot of flexibility. ### 2015-04-22 Implemented Reader and Writer during the Bayern game against Porto. The second half gave me enough time. ### 2015-04-21 While showering today morning I discovered that the design for OpEncoder and OpDecoder doesn't work, because encoding/decoding might depend on the current status of the dictionary. This is not exactly the right way to start the day. Therefore we need to keep the Reader and Writer design. This time around we simplify it by ignoring size limits. These can be added by wrappers around the Reader and Writer interfaces. The Parameters type isn't needed anymore. However I will implement a ReaderState and WriterState type to use static typing to ensure the right State object is combined with the right lzbase.Reader and lzbase.Writer. As a start I have implemented ReaderState and WriterState to ensure that the state for reading is only used by readers and WriterState only used by Writers. ### 2015-04-20 Today I implemented the OpDecoder and tested OpEncoder and OpDecoder. ### 2015-04-08 Came up with a new simplified design for lzbase. I implemented already the type State that replaces OpCodec. ### 2015-04-06 The new lzma package is now fully usable and lzmago is using it now. The old lzma package has been completely removed. ### 2015-04-05 Implemented lzma.Reader and tested it. ### 2015-04-04 Implemented baseReader by adapting code form lzma.Reader. ### 2015-04-03 The opCodec has been copied yesterday to lzma2. opCodec has a high number of dependencies on other files in lzma2. Therefore I had to copy almost all files from lzma. ### 2015-03-31 Removed only a TODO item. However in Francesco Campoy's presentation "Go for Javaneros (Javaïstes?)" is the the idea that using an embedded field E, all the methods of E will be defined on T. If E is an interface T satisfies E. https://talks.golang.org/2014/go4java.slide#51 I have never used this, but it seems to be a cool idea. ### 2015-03-30 Finished the type writerDict and wrote a simple test. ### 2015-03-25 I started to implement the writerDict. ### 2015-03-24 After thinking long about the LZMA2 code and several false starts, I have now a plan to create a self-sufficient lzma2 package that supports the classic LZMA format as well as LZMA2. The core idea is to support a baseReader and baseWriter type that support the basic LZMA stream without any headers. Both types must support the reuse of dictionaries and the opCodec. ### 2015-01-10 1. Implemented simple lzmago tool 2. Tested tool against large 4.4G file - compression worked correctly; tested decompression with lzma - decompression hits a full buffer condition 3. Fixed a bug in the compressor and wrote a test for it 4. Executed full cycle for 4.4 GB file; performance can be improved ;-) ### 2015-01-11 - Release v0.2 because of the working LZMA encoder and decoder ================================================ FILE: vendor/github.com/ulikunitz/xz/bits.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package xz import ( "errors" "io" ) // putUint32LE puts the little-endian representation of x into the first // four bytes of p. func putUint32LE(p []byte, x uint32) { p[0] = byte(x) p[1] = byte(x >> 8) p[2] = byte(x >> 16) p[3] = byte(x >> 24) } // putUint64LE puts the little-endian representation of x into the first // eight bytes of p. func putUint64LE(p []byte, x uint64) { p[0] = byte(x) p[1] = byte(x >> 8) p[2] = byte(x >> 16) p[3] = byte(x >> 24) p[4] = byte(x >> 32) p[5] = byte(x >> 40) p[6] = byte(x >> 48) p[7] = byte(x >> 56) } // uint32LE converts a little endian representation to an uint32 value. func uint32LE(p []byte) uint32 { return uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24 } // putUvarint puts a uvarint representation of x into the byte slice. func putUvarint(p []byte, x uint64) int { i := 0 for x >= 0x80 { p[i] = byte(x) | 0x80 x >>= 7 i++ } p[i] = byte(x) return i + 1 } // errOverflow indicates an overflow of the 64-bit unsigned integer. var errOverflowU64 = errors.New("xz: uvarint overflows 64-bit unsigned integer") // readUvarint reads a uvarint from the given byte reader. func readUvarint(r io.ByteReader) (x uint64, n int, err error) { var s uint i := 0 for { b, err := r.ReadByte() if err != nil { return x, i, err } i++ if b < 0x80 { if i > 10 || i == 10 && b > 1 { return x, i, errOverflowU64 } return x | uint64(b)< 0 { k = 4 - k } return k } /*** Header ***/ // headerMagic stores the magic bytes for the header var headerMagic = []byte{0xfd, '7', 'z', 'X', 'Z', 0x00} // HeaderLen provides the length of the xz file header. const HeaderLen = 12 // Constants for the checksum methods supported by xz. const ( None byte = 0x0 CRC32 = 0x1 CRC64 = 0x4 SHA256 = 0xa ) // errInvalidFlags indicates that flags are invalid. var errInvalidFlags = errors.New("xz: invalid flags") // verifyFlags returns the error errInvalidFlags if the value is // invalid. func verifyFlags(flags byte) error { switch flags { case None, CRC32, CRC64, SHA256: return nil default: return errInvalidFlags } } // flagstrings maps flag values to strings. var flagstrings = map[byte]string{ None: "None", CRC32: "CRC-32", CRC64: "CRC-64", SHA256: "SHA-256", } // flagString returns the string representation for the given flags. func flagString(flags byte) string { s, ok := flagstrings[flags] if !ok { return "invalid" } return s } // newHashFunc returns a function that creates hash instances for the // hash method encoded in flags. func newHashFunc(flags byte) (newHash func() hash.Hash, err error) { switch flags { case None: newHash = newNoneHash case CRC32: newHash = newCRC32 case CRC64: newHash = newCRC64 case SHA256: newHash = sha256.New default: err = errInvalidFlags } return } // header provides the actual content of the xz file header: the flags. type header struct { flags byte } // Errors returned by readHeader. var errHeaderMagic = errors.New("xz: invalid header magic bytes") // ValidHeader checks whether data is a correct xz file header. The // length of data must be HeaderLen. func ValidHeader(data []byte) bool { var h header err := h.UnmarshalBinary(data) return err == nil } // String returns a string representation of the flags. func (h header) String() string { return flagString(h.flags) } // UnmarshalBinary reads header from the provided data slice. func (h *header) UnmarshalBinary(data []byte) error { // header length if len(data) != HeaderLen { return errors.New("xz: wrong file header length") } // magic header if !bytes.Equal(headerMagic, data[:6]) { return errHeaderMagic } // checksum crc := crc32.NewIEEE() crc.Write(data[6:8]) if uint32LE(data[8:]) != crc.Sum32() { return errors.New("xz: invalid checksum for file header") } // stream flags if data[6] != 0 { return errInvalidFlags } flags := data[7] if err := verifyFlags(flags); err != nil { return err } h.flags = flags return nil } // MarshalBinary generates the xz file header. func (h *header) MarshalBinary() (data []byte, err error) { if err = verifyFlags(h.flags); err != nil { return nil, err } data = make([]byte, 12) copy(data, headerMagic) data[7] = h.flags crc := crc32.NewIEEE() crc.Write(data[6:8]) putUint32LE(data[8:], crc.Sum32()) return data, nil } /*** Footer ***/ // footerLen defines the length of the footer. const footerLen = 12 // footerMagic contains the footer magic bytes. var footerMagic = []byte{'Y', 'Z'} // footer represents the content of the xz file footer. type footer struct { indexSize int64 flags byte } // String prints a string representation of the footer structure. func (f footer) String() string { return fmt.Sprintf("%s index size %d", flagString(f.flags), f.indexSize) } // Minimum and maximum for the size of the index (backward size). const ( minIndexSize = 4 maxIndexSize = (1 << 32) * 4 ) // MarshalBinary converts footer values into an xz file footer. Note // that the footer value is checked for correctness. func (f *footer) MarshalBinary() (data []byte, err error) { if err = verifyFlags(f.flags); err != nil { return nil, err } if !(minIndexSize <= f.indexSize && f.indexSize <= maxIndexSize) { return nil, errors.New("xz: index size out of range") } if f.indexSize%4 != 0 { return nil, errors.New( "xz: index size not aligned to four bytes") } data = make([]byte, footerLen) // backward size (index size) s := (f.indexSize / 4) - 1 putUint32LE(data[4:], uint32(s)) // flags data[9] = f.flags // footer magic copy(data[10:], footerMagic) // CRC-32 crc := crc32.NewIEEE() crc.Write(data[4:10]) putUint32LE(data, crc.Sum32()) return data, nil } // UnmarshalBinary sets the footer value by unmarshalling an xz file // footer. func (f *footer) UnmarshalBinary(data []byte) error { if len(data) != footerLen { return errors.New("xz: wrong footer length") } // magic bytes if !bytes.Equal(data[10:], footerMagic) { return errors.New("xz: footer magic invalid") } // CRC-32 crc := crc32.NewIEEE() crc.Write(data[4:10]) if uint32LE(data) != crc.Sum32() { return errors.New("xz: footer checksum error") } var g footer // backward size (index size) g.indexSize = (int64(uint32LE(data[4:])) + 1) * 4 // flags if data[8] != 0 { return errInvalidFlags } g.flags = data[9] if err := verifyFlags(g.flags); err != nil { return err } *f = g return nil } /*** Block Header ***/ // blockHeader represents the content of an xz block header. type blockHeader struct { compressedSize int64 uncompressedSize int64 filters []filter } // String converts the block header into a string. func (h blockHeader) String() string { var buf bytes.Buffer first := true if h.compressedSize >= 0 { fmt.Fprintf(&buf, "compressed size %d", h.compressedSize) first = false } if h.uncompressedSize >= 0 { if !first { buf.WriteString(" ") } fmt.Fprintf(&buf, "uncompressed size %d", h.uncompressedSize) first = false } for _, f := range h.filters { if !first { buf.WriteString(" ") } fmt.Fprintf(&buf, "filter %s", f) first = false } return buf.String() } // Masks for the block flags. const ( filterCountMask = 0x03 compressedSizePresent = 0x40 uncompressedSizePresent = 0x80 reservedBlockFlags = 0x3C ) // errIndexIndicator signals that an index indicator (0x00) has been found // instead of an expected block header indicator. var errIndexIndicator = errors.New("xz: found index indicator") // readBlockHeader reads the block header. func readBlockHeader(r io.Reader) (h *blockHeader, n int, err error) { var buf bytes.Buffer buf.Grow(20) // block header size z, err := io.CopyN(&buf, r, 1) n = int(z) if err != nil { return nil, n, err } s := buf.Bytes()[0] if s == 0 { return nil, n, errIndexIndicator } // read complete header headerLen := (int(s) + 1) * 4 buf.Grow(headerLen - 1) z, err = io.CopyN(&buf, r, int64(headerLen-1)) n += int(z) if err != nil { return nil, n, err } // unmarshal block header h = new(blockHeader) if err = h.UnmarshalBinary(buf.Bytes()); err != nil { return nil, n, err } return h, n, nil } // readSizeInBlockHeader reads the uncompressed or compressed size // fields in the block header. The present value informs the function // whether the respective field is actually present in the header. func readSizeInBlockHeader(r io.ByteReader, present bool) (n int64, err error) { if !present { return -1, nil } x, _, err := readUvarint(r) if err != nil { return 0, err } if x >= 1<<63 { return 0, errors.New("xz: size overflow in block header") } return int64(x), nil } // UnmarshalBinary unmarshals the block header. func (h *blockHeader) UnmarshalBinary(data []byte) error { // Check header length s := data[0] if data[0] == 0 { return errIndexIndicator } headerLen := (int(s) + 1) * 4 if len(data) != headerLen { return fmt.Errorf("xz: data length %d; want %d", len(data), headerLen) } n := headerLen - 4 // Check CRC-32 crc := crc32.NewIEEE() crc.Write(data[:n]) if crc.Sum32() != uint32LE(data[n:]) { return errors.New("xz: checksum error for block header") } // Block header flags flags := data[1] if flags&reservedBlockFlags != 0 { return errors.New("xz: reserved block header flags set") } r := bytes.NewReader(data[2:n]) // Compressed size var err error h.compressedSize, err = readSizeInBlockHeader( r, flags&compressedSizePresent != 0) if err != nil { return err } // Uncompressed size h.uncompressedSize, err = readSizeInBlockHeader( r, flags&uncompressedSizePresent != 0) if err != nil { return err } h.filters, err = readFilters(r, int(flags&filterCountMask)+1) if err != nil { return err } // Check padding // Since headerLen is a multiple of 4 we don't need to check // alignment. k := r.Len() // The standard spec says that the padding should have not more // than 3 bytes. However we found paddings of 4 or 5 in the // wild. See https://github.com/ulikunitz/xz/pull/11 and // https://github.com/ulikunitz/xz/issues/15 // // The only reasonable approach seems to be to ignore the // padding size. We still check that all padding bytes are zero. if !allZeros(data[n-k : n]) { return errPadding } return nil } // MarshalBinary marshals the binary header. func (h *blockHeader) MarshalBinary() (data []byte, err error) { if !(minFilters <= len(h.filters) && len(h.filters) <= maxFilters) { return nil, errors.New("xz: filter count wrong") } for i, f := range h.filters { if i < len(h.filters)-1 { if f.id() == lzmaFilterID { return nil, errors.New( "xz: LZMA2 filter is not the last") } } else { // last filter if f.id() != lzmaFilterID { return nil, errors.New("xz: " + "last filter must be the LZMA2 filter") } } } var buf bytes.Buffer // header size must set at the end buf.WriteByte(0) // flags flags := byte(len(h.filters) - 1) if h.compressedSize >= 0 { flags |= compressedSizePresent } if h.uncompressedSize >= 0 { flags |= uncompressedSizePresent } buf.WriteByte(flags) p := make([]byte, 10) if h.compressedSize >= 0 { k := putUvarint(p, uint64(h.compressedSize)) buf.Write(p[:k]) } if h.uncompressedSize >= 0 { k := putUvarint(p, uint64(h.uncompressedSize)) buf.Write(p[:k]) } for _, f := range h.filters { fp, err := f.MarshalBinary() if err != nil { return nil, err } buf.Write(fp) } // padding for i := padLen(int64(buf.Len())); i > 0; i-- { buf.WriteByte(0) } // crc place holder buf.Write(p[:4]) data = buf.Bytes() if len(data)%4 != 0 { panic("data length not aligned") } s := len(data)/4 - 1 if !(1 < s && s <= 255) { panic("wrong block header size") } data[0] = byte(s) crc := crc32.NewIEEE() crc.Write(data[:len(data)-4]) putUint32LE(data[len(data)-4:], crc.Sum32()) return data, nil } // Constants used for marshalling and unmarshalling filters in the xz // block header. const ( minFilters = 1 maxFilters = 4 minReservedID = 1 << 62 ) // filter represents a filter in the block header. type filter interface { id() uint64 UnmarshalBinary(data []byte) error MarshalBinary() (data []byte, err error) reader(r io.Reader, c *ReaderConfig) (fr io.Reader, err error) writeCloser(w io.WriteCloser, c *WriterConfig) (fw io.WriteCloser, err error) // filter must be last filter last() bool } // readFilter reads a block filter from the block header. At this point // in time only the LZMA2 filter is supported. func readFilter(r io.Reader) (f filter, err error) { br := lzma.ByteReader(r) // index id, _, err := readUvarint(br) if err != nil { return nil, err } var data []byte switch id { case lzmaFilterID: data = make([]byte, lzmaFilterLen) data[0] = lzmaFilterID if _, err = io.ReadFull(r, data[1:]); err != nil { return nil, err } f = new(lzmaFilter) default: if id >= minReservedID { return nil, errors.New( "xz: reserved filter id in block stream header") } return nil, errors.New("xz: invalid filter id") } if err = f.UnmarshalBinary(data); err != nil { return nil, err } return f, err } // readFilters reads count filters. At this point in time only the count // 1 is supported. func readFilters(r io.Reader, count int) (filters []filter, err error) { if count != 1 { return nil, errors.New("xz: unsupported filter count") } f, err := readFilter(r) if err != nil { return nil, err } return []filter{f}, err } // writeFilters writes the filters. func writeFilters(w io.Writer, filters []filter) (n int, err error) { for _, f := range filters { p, err := f.MarshalBinary() if err != nil { return n, err } k, err := w.Write(p) n += k if err != nil { return n, err } } return n, nil } /*** Index ***/ // record describes a block in the xz file index. type record struct { unpaddedSize int64 uncompressedSize int64 } // readRecord reads an index record. func readRecord(r io.ByteReader) (rec record, n int, err error) { u, k, err := readUvarint(r) n += k if err != nil { return rec, n, err } rec.unpaddedSize = int64(u) if rec.unpaddedSize < 0 { return rec, n, errors.New("xz: unpadded size negative") } u, k, err = readUvarint(r) n += k if err != nil { return rec, n, err } rec.uncompressedSize = int64(u) if rec.uncompressedSize < 0 { return rec, n, errors.New("xz: uncompressed size negative") } return rec, n, nil } // MarshalBinary converts an index record in its binary encoding. func (rec *record) MarshalBinary() (data []byte, err error) { // maximum length of a uvarint is 10 p := make([]byte, 20) n := putUvarint(p, uint64(rec.unpaddedSize)) n += putUvarint(p[n:], uint64(rec.uncompressedSize)) return p[:n], nil } // writeIndex writes the index, a sequence of records. func writeIndex(w io.Writer, index []record) (n int64, err error) { crc := crc32.NewIEEE() mw := io.MultiWriter(w, crc) // index indicator k, err := mw.Write([]byte{0}) n += int64(k) if err != nil { return n, err } // number of records p := make([]byte, 10) k = putUvarint(p, uint64(len(index))) k, err = mw.Write(p[:k]) n += int64(k) if err != nil { return n, err } // list of records for _, rec := range index { p, err := rec.MarshalBinary() if err != nil { return n, err } k, err = mw.Write(p) n += int64(k) if err != nil { return n, err } } // index padding k, err = mw.Write(make([]byte, padLen(int64(n)))) n += int64(k) if err != nil { return n, err } // crc32 checksum putUint32LE(p, crc.Sum32()) k, err = w.Write(p[:4]) n += int64(k) return n, err } // readIndexBody reads the index from the reader. It assumes that the // index indicator has already been read. func readIndexBody(r io.Reader) (records []record, n int64, err error) { crc := crc32.NewIEEE() // index indicator crc.Write([]byte{0}) br := lzma.ByteReader(io.TeeReader(r, crc)) // number of records u, k, err := readUvarint(br) n += int64(k) if err != nil { return nil, n, err } recLen := int(u) if recLen < 0 || uint64(recLen) != u { return nil, n, errors.New("xz: record number overflow") } // list of records records = make([]record, recLen) for i := range records { records[i], k, err = readRecord(br) n += int64(k) if err != nil { return nil, n, err } } p := make([]byte, padLen(int64(n+1)), 4) k, err = io.ReadFull(br.(io.Reader), p) n += int64(k) if err != nil { return nil, n, err } if !allZeros(p) { return nil, n, errors.New("xz: non-zero byte in index padding") } // crc32 s := crc.Sum32() p = p[:4] k, err = io.ReadFull(br.(io.Reader), p) n += int64(k) if err != nil { return records, n, err } if uint32LE(p) != s { return nil, n, errors.New("xz: wrong checksum for index") } return records, n, nil } ================================================ FILE: vendor/github.com/ulikunitz/xz/internal/hash/cyclic_poly.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package hash // CyclicPoly provides a cyclic polynomial rolling hash. type CyclicPoly struct { h uint64 p []uint64 i int } // ror rotates the unsigned 64-bit integer to right. The argument s must be // less than 64. func ror(x uint64, s uint) uint64 { return (x >> s) | (x << (64 - s)) } // NewCyclicPoly creates a new instance of the CyclicPoly structure. The // argument n gives the number of bytes for which a hash will be executed. // This number must be positive; the method panics if this isn't the case. func NewCyclicPoly(n int) *CyclicPoly { if n < 1 { panic("argument n must be positive") } return &CyclicPoly{p: make([]uint64, 0, n)} } // Len returns the length of the byte sequence for which a hash is generated. func (r *CyclicPoly) Len() int { return cap(r.p) } // RollByte hashes the next byte and returns a hash value. The complete becomes // available after at least Len() bytes have been hashed. func (r *CyclicPoly) RollByte(x byte) uint64 { y := hash[x] if len(r.p) < cap(r.p) { r.h = ror(r.h, 1) ^ y r.p = append(r.p, y) } else { r.h ^= ror(r.p[r.i], uint(cap(r.p)-1)) r.h = ror(r.h, 1) ^ y r.p[r.i] = y r.i = (r.i + 1) % cap(r.p) } return r.h } // Stores the hash for the individual bytes. var hash = [256]uint64{ 0x2e4fc3f904065142, 0xc790984cfbc99527, 0x879f95eb8c62f187, 0x3b61be86b5021ef2, 0x65a896a04196f0a5, 0xc5b307b80470b59e, 0xd3bff376a70df14b, 0xc332f04f0b3f1701, 0x753b5f0e9abf3e0d, 0xb41538fdfe66ef53, 0x1906a10c2c1c0208, 0xfb0c712a03421c0d, 0x38be311a65c9552b, 0xfee7ee4ca6445c7e, 0x71aadeded184f21e, 0xd73426fccda23b2d, 0x29773fb5fb9600b5, 0xce410261cd32981a, 0xfe2848b3c62dbc2d, 0x459eaaff6e43e11c, 0xc13e35fc9c73a887, 0xf30ed5c201e76dbc, 0xa5f10b3910482cea, 0x2945d59be02dfaad, 0x06ee334ff70571b5, 0xbabf9d8070f44380, 0xee3e2e9912ffd27c, 0x2a7118d1ea6b8ea7, 0x26183cb9f7b1664c, 0xea71dac7da068f21, 0xea92eca5bd1d0bb7, 0x415595862defcd75, 0x248a386023c60648, 0x9cf021ab284b3c8a, 0xfc9372df02870f6c, 0x2b92d693eeb3b3fc, 0x73e799d139dc6975, 0x7b15ae312486363c, 0xb70e5454a2239c80, 0x208e3fb31d3b2263, 0x01f563cabb930f44, 0x2ac4533d2a3240d8, 0x84231ed1064f6f7c, 0xa9f020977c2a6d19, 0x213c227271c20122, 0x09fe8a9a0a03d07a, 0x4236dc75bcaf910c, 0x460a8b2bead8f17e, 0xd9b27be1aa07055f, 0xd202d5dc4b11c33e, 0x70adb010543bea12, 0xcdae938f7ea6f579, 0x3f3d870208672f4d, 0x8e6ccbce9d349536, 0xe4c0871a389095ae, 0xf5f2a49152bca080, 0x9a43f9b97269934e, 0xc17b3753cb6f475c, 0xd56d941e8e206bd4, 0xac0a4f3e525eda00, 0xa06d5a011912a550, 0x5537ed19537ad1df, 0xa32fe713d611449d, 0x2a1d05b47c3b579f, 0x991d02dbd30a2a52, 0x39e91e7e28f93eb0, 0x40d06adb3e92c9ac, 0x9b9d3afde1c77c97, 0x9a3f3f41c02c616f, 0x22ecd4ba00f60c44, 0x0b63d5d801708420, 0x8f227ca8f37ffaec, 0x0256278670887c24, 0x107e14877dbf540b, 0x32c19f2786ac1c05, 0x1df5b12bb4bc9c61, 0xc0cac129d0d4c4e2, 0x9fdb52ee9800b001, 0x31f601d5d31c48c4, 0x72ff3c0928bcaec7, 0xd99264421147eb03, 0x535a2d6d38aefcfe, 0x6ba8b4454a916237, 0xfa39366eaae4719c, 0x10f00fd7bbb24b6f, 0x5bd23185c76c84d4, 0xb22c3d7e1b00d33f, 0x3efc20aa6bc830a8, 0xd61c2503fe639144, 0x30ce625441eb92d3, 0xe5d34cf359e93100, 0xa8e5aa13f2b9f7a5, 0x5c2b8d851ca254a6, 0x68fb6c5e8b0d5fdf, 0xc7ea4872c96b83ae, 0x6dd5d376f4392382, 0x1be88681aaa9792f, 0xfef465ee1b6c10d9, 0x1f98b65ed43fcb2e, 0x4d1ca11eb6e9a9c9, 0x7808e902b3857d0b, 0x171c9c4ea4607972, 0x58d66274850146df, 0x42b311c10d3981d1, 0x647fa8c621c41a4c, 0xf472771c66ddfedc, 0x338d27e3f847b46b, 0x6402ce3da97545ce, 0x5162db616fc38638, 0x9c83be97bc22a50e, 0x2d3d7478a78d5e72, 0xe621a9b938fd5397, 0x9454614eb0f81c45, 0x395fb6e742ed39b6, 0x77dd9179d06037bf, 0xc478d0fee4d2656d, 0x35d9d6cb772007af, 0x83a56e92c883f0f6, 0x27937453250c00a1, 0x27bd6ebc3a46a97d, 0x9f543bf784342d51, 0xd158f38c48b0ed52, 0x8dd8537c045f66b4, 0x846a57230226f6d5, 0x6b13939e0c4e7cdf, 0xfca25425d8176758, 0x92e5fc6cd52788e6, 0x9992e13d7a739170, 0x518246f7a199e8ea, 0xf104c2a71b9979c7, 0x86b3ffaabea4768f, 0x6388061cf3e351ad, 0x09d9b5295de5bbb5, 0x38bf1638c2599e92, 0x1d759846499e148d, 0x4c0ff015e5f96ef4, 0xa41a94cfa270f565, 0x42d76f9cb2326c0b, 0x0cf385dd3c9c23ba, 0x0508a6c7508d6e7a, 0x337523aabbe6cf8d, 0x646bb14001d42b12, 0xc178729d138adc74, 0xf900ef4491f24086, 0xee1a90d334bb5ac4, 0x9755c92247301a50, 0xb999bf7c4ff1b610, 0x6aeeb2f3b21e8fc9, 0x0fa8084cf91ac6ff, 0x10d226cf136e6189, 0xd302057a07d4fb21, 0x5f03800e20a0fcc3, 0x80118d4ae46bd210, 0x58ab61a522843733, 0x51edd575c5432a4b, 0x94ee6ff67f9197f7, 0x765669e0e5e8157b, 0xa5347830737132f0, 0x3ba485a69f01510c, 0x0b247d7b957a01c3, 0x1b3d63449fd807dc, 0x0fdc4721c30ad743, 0x8b535ed3829b2b14, 0xee41d0cad65d232c, 0xe6a99ed97a6a982f, 0x65ac6194c202003d, 0x692accf3a70573eb, 0xcc3c02c3e200d5af, 0x0d419e8b325914a3, 0x320f160f42c25e40, 0x00710d647a51fe7a, 0x3c947692330aed60, 0x9288aa280d355a7a, 0xa1806a9b791d1696, 0x5d60e38496763da1, 0x6c69e22e613fd0f4, 0x977fc2a5aadffb17, 0xfb7bd063fc5a94ba, 0x460c17992cbaece1, 0xf7822c5444d3297f, 0x344a9790c69b74aa, 0xb80a42e6cae09dce, 0x1b1361eaf2b1e757, 0xd84c1e758e236f01, 0x88e0b7be347627cc, 0x45246009b7a99490, 0x8011c6dd3fe50472, 0xc341d682bffb99d7, 0x2511be93808e2d15, 0xd5bc13d7fd739840, 0x2a3cd030679ae1ec, 0x8ad9898a4b9ee157, 0x3245fef0a8eaf521, 0x3d6d8dbbb427d2b0, 0x1ed146d8968b3981, 0x0c6a28bf7d45f3fc, 0x4a1fd3dbcee3c561, 0x4210ff6a476bf67e, 0xa559cce0d9199aac, 0xde39d47ef3723380, 0xe5b69d848ce42e35, 0xefa24296f8e79f52, 0x70190b59db9a5afc, 0x26f166cdb211e7bf, 0x4deaf2df3c6b8ef5, 0xf171dbdd670f1017, 0xb9059b05e9420d90, 0x2f0da855c9388754, 0x611d5e9ab77949cc, 0x2912038ac01163f4, 0x0231df50402b2fba, 0x45660fc4f3245f58, 0xb91cc97c7c8dac50, 0xb72d2aafe4953427, 0xfa6463f87e813d6b, 0x4515f7ee95d5c6a2, 0x1310e1c1a48d21c3, 0xad48a7810cdd8544, 0x4d5bdfefd5c9e631, 0xa43ed43f1fdcb7de, 0xe70cfc8fe1ee9626, 0xef4711b0d8dda442, 0xb80dd9bd4dab6c93, 0xa23be08d31ba4d93, 0x9b37db9d0335a39c, 0x494b6f870f5cfebc, 0x6d1b3c1149dda943, 0x372c943a518c1093, 0xad27af45e77c09c4, 0x3b6f92b646044604, 0xac2917909f5fcf4f, 0x2069a60e977e5557, 0x353a469e71014de5, 0x24be356281f55c15, 0x2b6d710ba8e9adea, 0x404ad1751c749c29, 0xed7311bf23d7f185, 0xba4f6976b4acc43e, 0x32d7198d2bc39000, 0xee667019014d6e01, 0x494ef3e128d14c83, 0x1f95a152baecd6be, 0x201648dff1f483a5, 0x68c28550c8384af6, 0x5fc834a6824a7f48, 0x7cd06cb7365eaf28, 0xd82bbd95e9b30909, 0x234f0d1694c53f6d, 0xd2fb7f4a96d83f4a, 0xff0d5da83acac05e, 0xf8f6b97f5585080a, 0x74236084be57b95b, 0xa25e40c03bbc36ad, 0x6b6e5c14ce88465b, 0x4378ffe93e1528c5, 0x94ca92a17118e2d2, } ================================================ FILE: vendor/github.com/ulikunitz/xz/internal/hash/doc.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. /* Package hash provides rolling hashes. Rolling hashes have to be used for maintaining the positions of n-byte sequences in the dictionary buffer. The package provides currently the Rabin-Karp rolling hash and a Cyclic Polynomial hash. Both support the Hashes method to be used with an interface. */ package hash ================================================ FILE: vendor/github.com/ulikunitz/xz/internal/hash/rabin_karp.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package hash // A is the default constant for Robin-Karp rolling hash. This is a random // prime. const A = 0x97b548add41d5da1 // RabinKarp supports the computation of a rolling hash. type RabinKarp struct { A uint64 // a^n aOldest uint64 h uint64 p []byte i int } // NewRabinKarp creates a new RabinKarp value. The argument n defines the // length of the byte sequence to be hashed. The default constant will will be // used. func NewRabinKarp(n int) *RabinKarp { return NewRabinKarpConst(n, A) } // NewRabinKarpConst creates a new RabinKarp value. The argument n defines the // length of the byte sequence to be hashed. The argument a provides the // constant used to compute the hash. func NewRabinKarpConst(n int, a uint64) *RabinKarp { if n <= 0 { panic("number of bytes n must be positive") } aOldest := uint64(1) // There are faster methods. For the small n required by the LZMA // compressor O(n) is sufficient. for i := 0; i < n; i++ { aOldest *= a } return &RabinKarp{ A: a, aOldest: aOldest, p: make([]byte, 0, n), } } // Len returns the length of the byte sequence. func (r *RabinKarp) Len() int { return cap(r.p) } // RollByte computes the hash after x has been added. func (r *RabinKarp) RollByte(x byte) uint64 { if len(r.p) < cap(r.p) { r.h += uint64(x) r.h *= r.A r.p = append(r.p, x) } else { r.h -= uint64(r.p[r.i]) * r.aOldest r.h += uint64(x) r.h *= r.A r.p[r.i] = x r.i = (r.i + 1) % cap(r.p) } return r.h } ================================================ FILE: vendor/github.com/ulikunitz/xz/internal/hash/roller.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package hash // Roller provides an interface for rolling hashes. The hash value will become // valid after hash has been called Len times. type Roller interface { Len() int RollByte(x byte) uint64 } // Hashes computes all hash values for the array p. Note that the state of the // roller is changed. func Hashes(r Roller, p []byte) []uint64 { n := r.Len() if len(p) < n { return nil } h := make([]uint64, len(p)-n+1) for i := 0; i < n-1; i++ { r.RollByte(p[i]) } for i := range h { h[i] = r.RollByte(p[i+n-1]) } return h } ================================================ FILE: vendor/github.com/ulikunitz/xz/internal/xlog/xlog.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package xlog provides a simple logging package that allows to disable // certain message categories. It defines a type, Logger, with multiple // methods for formatting output. The package has also a predefined // 'standard' Logger accessible through helper function Print[f|ln], // Fatal[f|ln], Panic[f|ln], Warn[f|ln], Print[f|ln] and Debug[f|ln] // that are easier to use then creating a Logger manually. That logger // writes to standard error and prints the date and time of each logged // message, which can be configured using the function SetFlags. // // The Fatal functions call os.Exit(1) after the message is output // unless not suppressed by the flags. The Panic functions call panic // after the writing the log message unless suppressed. package xlog import ( "fmt" "io" "os" "runtime" "sync" "time" ) // The flags define what information is prefixed to each log entry // generated by the Logger. The Lno* versions allow the suppression of // specific output. The bits are or'ed together to control what will be // printed. There is no control over the order of the items printed and // the format. The full format is: // // 2009-01-23 01:23:23.123123 /a/b/c/d.go:23: message // const ( Ldate = 1 << iota // the date: 2009-01-23 Ltime // the time: 01:23:23 Lmicroseconds // microsecond resolution: 01:23:23.123123 Llongfile // full file name and line number: /a/b/c/d.go:23 Lshortfile // final file name element and line number: d.go:23 Lnopanic // suppresses output from Panic[f|ln] but not the panic call Lnofatal // suppresses output from Fatal[f|ln] but not the exit Lnowarn // suppresses output from Warn[f|ln] Lnoprint // suppresses output from Print[f|ln] Lnodebug // suppresses output from Debug[f|ln] // initial values for the standard logger Lstdflags = Ldate | Ltime | Lnodebug ) // A Logger represents an active logging object that generates lines of // output to an io.Writer. Each logging operation if not suppressed // makes a single call to the Writer's Write method. A Logger can be // used simultaneously from multiple goroutines; it guarantees to // serialize access to the Writer. type Logger struct { mu sync.Mutex // ensures atomic writes; and protects the following // fields prefix string // prefix to write at beginning of each line flag int // properties out io.Writer // destination for output buf []byte // for accumulating text to write } // New creates a new Logger. The out argument sets the destination to // which the log output will be written. The prefix appears at the // beginning of each log line. The flag argument defines the logging // properties. func New(out io.Writer, prefix string, flag int) *Logger { return &Logger{out: out, prefix: prefix, flag: flag} } // std is the standard logger used by the package scope functions. var std = New(os.Stderr, "", Lstdflags) // itoa converts the integer to ASCII. A negative widths will avoid // zero-padding. The function supports only non-negative integers. func itoa(buf *[]byte, i int, wid int) { var u = uint(i) if u == 0 && wid <= 1 { *buf = append(*buf, '0') return } var b [32]byte bp := len(b) for ; u > 0 || wid > 0; u /= 10 { bp-- wid-- b[bp] = byte(u%10) + '0' } *buf = append(*buf, b[bp:]...) } // formatHeader puts the header into the buf field of the buffer. func (l *Logger) formatHeader(t time.Time, file string, line int) { l.buf = append(l.buf, l.prefix...) if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 { if l.flag&Ldate != 0 { year, month, day := t.Date() itoa(&l.buf, year, 4) l.buf = append(l.buf, '-') itoa(&l.buf, int(month), 2) l.buf = append(l.buf, '-') itoa(&l.buf, day, 2) l.buf = append(l.buf, ' ') } if l.flag&(Ltime|Lmicroseconds) != 0 { hour, min, sec := t.Clock() itoa(&l.buf, hour, 2) l.buf = append(l.buf, ':') itoa(&l.buf, min, 2) l.buf = append(l.buf, ':') itoa(&l.buf, sec, 2) if l.flag&Lmicroseconds != 0 { l.buf = append(l.buf, '.') itoa(&l.buf, t.Nanosecond()/1e3, 6) } l.buf = append(l.buf, ' ') } } if l.flag&(Lshortfile|Llongfile) != 0 { if l.flag&Lshortfile != 0 { short := file for i := len(file) - 1; i > 0; i-- { if file[i] == '/' { short = file[i+1:] break } } file = short } l.buf = append(l.buf, file...) l.buf = append(l.buf, ':') itoa(&l.buf, line, -1) l.buf = append(l.buf, ": "...) } } func (l *Logger) output(calldepth int, now time.Time, s string) error { var file string var line int if l.flag&(Lshortfile|Llongfile) != 0 { l.mu.Unlock() var ok bool _, file, line, ok = runtime.Caller(calldepth) if !ok { file = "???" line = 0 } l.mu.Lock() } l.buf = l.buf[:0] l.formatHeader(now, file, line) l.buf = append(l.buf, s...) if len(s) == 0 || s[len(s)-1] != '\n' { l.buf = append(l.buf, '\n') } _, err := l.out.Write(l.buf) return err } // Output writes the string s with the header controlled by the flags to // the l.out writer. A newline will be appended if s doesn't end in a // newline. Calldepth is used to recover the PC, although all current // calls of Output use the call depth 2. Access to the function is serialized. func (l *Logger) Output(calldepth, noflag int, v ...interface{}) error { now := time.Now() l.mu.Lock() defer l.mu.Unlock() if l.flag&noflag != 0 { return nil } s := fmt.Sprint(v...) return l.output(calldepth+1, now, s) } // Outputf works like output but formats the output like Printf. func (l *Logger) Outputf(calldepth int, noflag int, format string, v ...interface{}) error { now := time.Now() l.mu.Lock() defer l.mu.Unlock() if l.flag&noflag != 0 { return nil } s := fmt.Sprintf(format, v...) return l.output(calldepth+1, now, s) } // Outputln works like output but formats the output like Println. func (l *Logger) Outputln(calldepth int, noflag int, v ...interface{}) error { now := time.Now() l.mu.Lock() defer l.mu.Unlock() if l.flag&noflag != 0 { return nil } s := fmt.Sprintln(v...) return l.output(calldepth+1, now, s) } // Panic prints the message like Print and calls panic. The printing // might be suppressed by the flag Lnopanic. func (l *Logger) Panic(v ...interface{}) { l.Output(2, Lnopanic, v...) s := fmt.Sprint(v...) panic(s) } // Panic prints the message like Print and calls panic. The printing // might be suppressed by the flag Lnopanic. func Panic(v ...interface{}) { std.Output(2, Lnopanic, v...) s := fmt.Sprint(v...) panic(s) } // Panicf prints the message like Printf and calls panic. The printing // might be suppressed by the flag Lnopanic. func (l *Logger) Panicf(format string, v ...interface{}) { l.Outputf(2, Lnopanic, format, v...) s := fmt.Sprintf(format, v...) panic(s) } // Panicf prints the message like Printf and calls panic. The printing // might be suppressed by the flag Lnopanic. func Panicf(format string, v ...interface{}) { std.Outputf(2, Lnopanic, format, v...) s := fmt.Sprintf(format, v...) panic(s) } // Panicln prints the message like Println and calls panic. The printing // might be suppressed by the flag Lnopanic. func (l *Logger) Panicln(v ...interface{}) { l.Outputln(2, Lnopanic, v...) s := fmt.Sprintln(v...) panic(s) } // Panicln prints the message like Println and calls panic. The printing // might be suppressed by the flag Lnopanic. func Panicln(v ...interface{}) { std.Outputln(2, Lnopanic, v...) s := fmt.Sprintln(v...) panic(s) } // Fatal prints the message like Print and calls os.Exit(1). The // printing might be suppressed by the flag Lnofatal. func (l *Logger) Fatal(v ...interface{}) { l.Output(2, Lnofatal, v...) os.Exit(1) } // Fatal prints the message like Print and calls os.Exit(1). The // printing might be suppressed by the flag Lnofatal. func Fatal(v ...interface{}) { std.Output(2, Lnofatal, v...) os.Exit(1) } // Fatalf prints the message like Printf and calls os.Exit(1). The // printing might be suppressed by the flag Lnofatal. func (l *Logger) Fatalf(format string, v ...interface{}) { l.Outputf(2, Lnofatal, format, v...) os.Exit(1) } // Fatalf prints the message like Printf and calls os.Exit(1). The // printing might be suppressed by the flag Lnofatal. func Fatalf(format string, v ...interface{}) { std.Outputf(2, Lnofatal, format, v...) os.Exit(1) } // Fatalln prints the message like Println and calls os.Exit(1). The // printing might be suppressed by the flag Lnofatal. func (l *Logger) Fatalln(format string, v ...interface{}) { l.Outputln(2, Lnofatal, v...) os.Exit(1) } // Fatalln prints the message like Println and calls os.Exit(1). The // printing might be suppressed by the flag Lnofatal. func Fatalln(format string, v ...interface{}) { std.Outputln(2, Lnofatal, v...) os.Exit(1) } // Warn prints the message like Print. The printing might be suppressed // by the flag Lnowarn. func (l *Logger) Warn(v ...interface{}) { l.Output(2, Lnowarn, v...) } // Warn prints the message like Print. The printing might be suppressed // by the flag Lnowarn. func Warn(v ...interface{}) { std.Output(2, Lnowarn, v...) } // Warnf prints the message like Printf. The printing might be suppressed // by the flag Lnowarn. func (l *Logger) Warnf(format string, v ...interface{}) { l.Outputf(2, Lnowarn, format, v...) } // Warnf prints the message like Printf. The printing might be suppressed // by the flag Lnowarn. func Warnf(format string, v ...interface{}) { std.Outputf(2, Lnowarn, format, v...) } // Warnln prints the message like Println. The printing might be suppressed // by the flag Lnowarn. func (l *Logger) Warnln(v ...interface{}) { l.Outputln(2, Lnowarn, v...) } // Warnln prints the message like Println. The printing might be suppressed // by the flag Lnowarn. func Warnln(v ...interface{}) { std.Outputln(2, Lnowarn, v...) } // Print prints the message like fmt.Print. The printing might be suppressed // by the flag Lnoprint. func (l *Logger) Print(v ...interface{}) { l.Output(2, Lnoprint, v...) } // Print prints the message like fmt.Print. The printing might be suppressed // by the flag Lnoprint. func Print(v ...interface{}) { std.Output(2, Lnoprint, v...) } // Printf prints the message like fmt.Printf. The printing might be suppressed // by the flag Lnoprint. func (l *Logger) Printf(format string, v ...interface{}) { l.Outputf(2, Lnoprint, format, v...) } // Printf prints the message like fmt.Printf. The printing might be suppressed // by the flag Lnoprint. func Printf(format string, v ...interface{}) { std.Outputf(2, Lnoprint, format, v...) } // Println prints the message like fmt.Println. The printing might be // suppressed by the flag Lnoprint. func (l *Logger) Println(v ...interface{}) { l.Outputln(2, Lnoprint, v...) } // Println prints the message like fmt.Println. The printing might be // suppressed by the flag Lnoprint. func Println(v ...interface{}) { std.Outputln(2, Lnoprint, v...) } // Debug prints the message like Print. The printing might be suppressed // by the flag Lnodebug. func (l *Logger) Debug(v ...interface{}) { l.Output(2, Lnodebug, v...) } // Debug prints the message like Print. The printing might be suppressed // by the flag Lnodebug. func Debug(v ...interface{}) { std.Output(2, Lnodebug, v...) } // Debugf prints the message like Printf. The printing might be suppressed // by the flag Lnodebug. func (l *Logger) Debugf(format string, v ...interface{}) { l.Outputf(2, Lnodebug, format, v...) } // Debugf prints the message like Printf. The printing might be suppressed // by the flag Lnodebug. func Debugf(format string, v ...interface{}) { std.Outputf(2, Lnodebug, format, v...) } // Debugln prints the message like Println. The printing might be suppressed // by the flag Lnodebug. func (l *Logger) Debugln(v ...interface{}) { l.Outputln(2, Lnodebug, v...) } // Debugln prints the message like Println. The printing might be suppressed // by the flag Lnodebug. func Debugln(v ...interface{}) { std.Outputln(2, Lnodebug, v...) } // Flags returns the current flags used by the logger. func (l *Logger) Flags() int { l.mu.Lock() defer l.mu.Unlock() return l.flag } // Flags returns the current flags used by the standard logger. func Flags() int { return std.Flags() } // SetFlags sets the flags of the logger. func (l *Logger) SetFlags(flag int) { l.mu.Lock() defer l.mu.Unlock() l.flag = flag } // SetFlags sets the flags for the standard logger. func SetFlags(flag int) { std.SetFlags(flag) } // Prefix returns the prefix used by the logger. func (l *Logger) Prefix() string { l.mu.Lock() defer l.mu.Unlock() return l.prefix } // Prefix returns the prefix used by the standard logger of the package. func Prefix() string { return std.Prefix() } // SetPrefix sets the prefix for the logger. func (l *Logger) SetPrefix(prefix string) { l.mu.Lock() defer l.mu.Unlock() l.prefix = prefix } // SetPrefix sets the prefix of the standard logger of the package. func SetPrefix(prefix string) { std.SetPrefix(prefix) } // SetOutput sets the output of the logger. func (l *Logger) SetOutput(w io.Writer) { l.mu.Lock() defer l.mu.Unlock() l.out = w } // SetOutput sets the output for the standard logger of the package. func SetOutput(w io.Writer) { std.SetOutput(w) } ================================================ FILE: vendor/github.com/ulikunitz/xz/lzma/bintree.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package lzma import ( "bufio" "errors" "fmt" "io" "unicode" ) // node represents a node in the binary tree. type node struct { // x is the search value x uint32 // p parent node p uint32 // l left child l uint32 // r right child r uint32 } // wordLen is the number of bytes represented by the v field of a node. const wordLen = 4 // binTree supports the identification of the next operation based on a // binary tree. // // Nodes will be identified by their index into the ring buffer. type binTree struct { dict *encoderDict // ring buffer of nodes node []node // absolute offset of the entry for the next node. Position 4 // byte larger. hoff int64 // front position in the node ring buffer front uint32 // index of the root node root uint32 // current x value x uint32 // preallocated array data []byte } // null represents the nonexistent index. We can't use zero because it // would always exist or we would need to decrease the index for each // reference. const null uint32 = 1<<32 - 1 // newBinTree initializes the binTree structure. The capacity defines // the size of the buffer and defines the maximum distance for which // matches will be found. func newBinTree(capacity int) (t *binTree, err error) { if capacity < 1 { return nil, errors.New( "newBinTree: capacity must be larger than zero") } if int64(capacity) >= int64(null) { return nil, errors.New( "newBinTree: capacity must less 2^{32}-1") } t = &binTree{ node: make([]node, capacity), hoff: -int64(wordLen), root: null, data: make([]byte, maxMatchLen), } return t, nil } func (t *binTree) SetDict(d *encoderDict) { t.dict = d } // WriteByte writes a single byte into the binary tree. func (t *binTree) WriteByte(c byte) error { t.x = (t.x << 8) | uint32(c) t.hoff++ if t.hoff < 0 { return nil } v := t.front if int64(v) < t.hoff { // We are overwriting old nodes stored in the tree. t.remove(v) } t.node[v].x = t.x t.add(v) t.front++ if int64(t.front) >= int64(len(t.node)) { t.front = 0 } return nil } // Writes writes a sequence of bytes into the binTree structure. func (t *binTree) Write(p []byte) (n int, err error) { for _, c := range p { t.WriteByte(c) } return len(p), nil } // add puts the node v into the tree. The node must not be part of the // tree before. func (t *binTree) add(v uint32) { vn := &t.node[v] // Set left and right to null indices. vn.l, vn.r = null, null // If the binary tree is empty make v the root. if t.root == null { t.root = v vn.p = null return } x := vn.x p := t.root // Search for the right leave link and add the new node. for { pn := &t.node[p] if x <= pn.x { if pn.l == null { pn.l = v vn.p = p return } p = pn.l } else { if pn.r == null { pn.r = v vn.p = p return } p = pn.r } } } // parent returns the parent node index of v and the pointer to v value // in the parent. func (t *binTree) parent(v uint32) (p uint32, ptr *uint32) { if t.root == v { return null, &t.root } p = t.node[v].p if t.node[p].l == v { ptr = &t.node[p].l } else { ptr = &t.node[p].r } return } // Remove node v. func (t *binTree) remove(v uint32) { vn := &t.node[v] p, ptr := t.parent(v) l, r := vn.l, vn.r if l == null { // Move the right child up. *ptr = r if r != null { t.node[r].p = p } return } if r == null { // Move the left child up. *ptr = l t.node[l].p = p return } // Search the in-order predecessor u. un := &t.node[l] ur := un.r if ur == null { // In order predecessor is l. Move it up. un.r = r t.node[r].p = l un.p = p *ptr = l return } var u uint32 for { // Look for the max value in the tree where l is root. u = ur ur = t.node[u].r if ur == null { break } } // replace u with ul un = &t.node[u] ul := un.l up := un.p t.node[up].r = ul if ul != null { t.node[ul].p = up } // replace v by u un.l, un.r = l, r t.node[l].p = u t.node[r].p = u *ptr = u un.p = p } // search looks for the node that have the value x or for the nodes that // brace it. The node highest in the tree with the value x will be // returned. All other nodes with the same value live in left subtree of // the returned node. func (t *binTree) search(v uint32, x uint32) (a, b uint32) { a, b = null, null if v == null { return } for { vn := &t.node[v] if x <= vn.x { if x == vn.x { return v, v } b = v if vn.l == null { return } v = vn.l } else { a = v if vn.r == null { return } v = vn.r } } } // max returns the node with maximum value in the subtree with v as // root. func (t *binTree) max(v uint32) uint32 { if v == null { return null } for { r := t.node[v].r if r == null { return v } v = r } } // min returns the node with the minimum value in the subtree with v as // root. func (t *binTree) min(v uint32) uint32 { if v == null { return null } for { l := t.node[v].l if l == null { return v } v = l } } // pred returns the in-order predecessor of node v. func (t *binTree) pred(v uint32) uint32 { if v == null { return null } u := t.max(t.node[v].l) if u != null { return u } for { p := t.node[v].p if p == null { return null } if t.node[p].r == v { return p } v = p } } // succ returns the in-order successor of node v. func (t *binTree) succ(v uint32) uint32 { if v == null { return null } u := t.min(t.node[v].r) if u != null { return u } for { p := t.node[v].p if p == null { return null } if t.node[p].l == v { return p } v = p } } // xval converts the first four bytes of a into an 32-bit unsigned // integer in big-endian order. func xval(a []byte) uint32 { var x uint32 switch len(a) { default: x |= uint32(a[3]) fallthrough case 3: x |= uint32(a[2]) << 8 fallthrough case 2: x |= uint32(a[1]) << 16 fallthrough case 1: x |= uint32(a[0]) << 24 case 0: } return x } // dumpX converts value x into a four-letter string. func dumpX(x uint32) string { a := make([]byte, 4) for i := 0; i < 4; i++ { c := byte(x >> uint((3-i)*8)) if unicode.IsGraphic(rune(c)) { a[i] = c } else { a[i] = '.' } } return string(a) } // dumpNode writes a representation of the node v into the io.Writer. func (t *binTree) dumpNode(w io.Writer, v uint32, indent int) { if v == null { return } vn := &t.node[v] t.dumpNode(w, vn.r, indent+2) for i := 0; i < indent; i++ { fmt.Fprint(w, " ") } if vn.p == null { fmt.Fprintf(w, "node %d %q parent null\n", v, dumpX(vn.x)) } else { fmt.Fprintf(w, "node %d %q parent %d\n", v, dumpX(vn.x), vn.p) } t.dumpNode(w, vn.l, indent+2) } // dump prints a representation of the binary tree into the writer. func (t *binTree) dump(w io.Writer) error { bw := bufio.NewWriter(w) t.dumpNode(bw, t.root, 0) return bw.Flush() } func (t *binTree) distance(v uint32) int { dist := int(t.front) - int(v) if dist <= 0 { dist += len(t.node) } return dist } type matchParams struct { rep [4]uint32 // length when match will be accepted nAccept int // nodes to check check int // finish if length get shorter stopShorter bool } func (t *binTree) match(m match, distIter func() (int, bool), p matchParams, ) (r match, checked int, accepted bool) { buf := &t.dict.buf for { if checked >= p.check { return m, checked, true } dist, ok := distIter() if !ok { return m, checked, false } checked++ if m.n > 0 { i := buf.rear - dist + m.n - 1 if i < 0 { i += len(buf.data) } else if i >= len(buf.data) { i -= len(buf.data) } if buf.data[i] != t.data[m.n-1] { if p.stopShorter { return m, checked, false } continue } } n := buf.matchLen(dist, t.data) switch n { case 0: if p.stopShorter { return m, checked, false } continue case 1: if uint32(dist-minDistance) != p.rep[0] { continue } } if n < m.n || (n == m.n && int64(dist) >= m.distance) { continue } m = match{int64(dist), n} if n >= p.nAccept { return m, checked, true } } } func (t *binTree) NextOp(rep [4]uint32) operation { // retrieve maxMatchLen data n, _ := t.dict.buf.Peek(t.data[:maxMatchLen]) if n == 0 { panic("no data in buffer") } t.data = t.data[:n] var ( m match x, u, v uint32 iterPred, iterSucc func() (int, bool) ) p := matchParams{ rep: rep, nAccept: maxMatchLen, check: 32, } i := 4 iterSmall := func() (dist int, ok bool) { i-- if i <= 0 { return 0, false } return i, true } m, checked, accepted := t.match(m, iterSmall, p) if accepted { goto end } p.check -= checked x = xval(t.data) u, v = t.search(t.root, x) if u == v && len(t.data) == 4 { iter := func() (dist int, ok bool) { if u == null { return 0, false } dist = t.distance(u) u, v = t.search(t.node[u].l, x) if u != v { u = null } return dist, true } m, _, _ = t.match(m, iter, p) goto end } p.stopShorter = true iterSucc = func() (dist int, ok bool) { if v == null { return 0, false } dist = t.distance(v) v = t.succ(v) return dist, true } m, checked, accepted = t.match(m, iterSucc, p) if accepted { goto end } p.check -= checked iterPred = func() (dist int, ok bool) { if u == null { return 0, false } dist = t.distance(u) u = t.pred(u) return dist, true } m, _, _ = t.match(m, iterPred, p) end: if m.n == 0 { return lit{t.data[0]} } return m } ================================================ FILE: vendor/github.com/ulikunitz/xz/lzma/bitops.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package lzma /* Naming conventions follows the CodeReviewComments in the Go Wiki. */ // ntz32Const is used by the functions NTZ and NLZ. const ntz32Const = 0x04d7651f // ntz32Table is a helper table for de Bruijn algorithm by Danny Dubé. // See Henry S. Warren, Jr. "Hacker's Delight" section 5-1 figure 5-26. var ntz32Table = [32]int8{ 0, 1, 2, 24, 3, 19, 6, 25, 22, 4, 20, 10, 16, 7, 12, 26, 31, 23, 18, 5, 21, 9, 15, 11, 30, 17, 8, 14, 29, 13, 28, 27, } // ntz32 computes the number of trailing zeros for an unsigned 32-bit integer. func ntz32(x uint32) int { if x == 0 { return 32 } x = (x & -x) * ntz32Const return int(ntz32Table[x>>27]) } // nlz32 computes the number of leading zeros for an unsigned 32-bit integer. func nlz32(x uint32) int { // Smear left most bit to the right x |= x >> 1 x |= x >> 2 x |= x >> 4 x |= x >> 8 x |= x >> 16 // Use ntz mechanism to calculate nlz. x++ if x == 0 { return 0 } x *= ntz32Const return 32 - int(ntz32Table[x>>27]) } ================================================ FILE: vendor/github.com/ulikunitz/xz/lzma/breader.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package lzma import ( "errors" "io" ) // breader provides the ReadByte function for a Reader. It doesn't read // more data from the reader than absolutely necessary. type breader struct { io.Reader // helper slice to save allocations p []byte } // ByteReader converts an io.Reader into an io.ByteReader. func ByteReader(r io.Reader) io.ByteReader { br, ok := r.(io.ByteReader) if !ok { return &breader{r, make([]byte, 1)} } return br } // ReadByte read byte function. func (r *breader) ReadByte() (c byte, err error) { n, err := r.Reader.Read(r.p) if n < 1 { if err == nil { err = errors.New("breader.ReadByte: no data") } return 0, err } return r.p[0], nil } ================================================ FILE: vendor/github.com/ulikunitz/xz/lzma/buffer.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package lzma import ( "errors" ) // buffer provides a circular buffer of bytes. If the front index equals // the rear index the buffer is empty. As a consequence front cannot be // equal rear for a full buffer. So a full buffer has a length that is // one byte less the the length of the data slice. type buffer struct { data []byte front int rear int } // newBuffer creates a buffer with the given size. func newBuffer(size int) *buffer { return &buffer{data: make([]byte, size+1)} } // Cap returns the capacity of the buffer. func (b *buffer) Cap() int { return len(b.data) - 1 } // Resets the buffer. The front and rear index are set to zero. func (b *buffer) Reset() { b.front = 0 b.rear = 0 } // Buffered returns the number of bytes buffered. func (b *buffer) Buffered() int { delta := b.front - b.rear if delta < 0 { delta += len(b.data) } return delta } // Available returns the number of bytes available for writing. func (b *buffer) Available() int { delta := b.rear - 1 - b.front if delta < 0 { delta += len(b.data) } return delta } // addIndex adds a non-negative integer to the index i and returns the // resulting index. The function takes care of wrapping the index as // well as potential overflow situations. func (b *buffer) addIndex(i int, n int) int { // subtraction of len(b.data) prevents overflow i += n - len(b.data) if i < 0 { i += len(b.data) } return i } // Read reads bytes from the buffer into p and returns the number of // bytes read. The function never returns an error but might return less // data than requested. func (b *buffer) Read(p []byte) (n int, err error) { n, err = b.Peek(p) b.rear = b.addIndex(b.rear, n) return n, err } // Peek reads bytes from the buffer into p without changing the buffer. // Peek will never return an error but might return less data than // requested. func (b *buffer) Peek(p []byte) (n int, err error) { m := b.Buffered() n = len(p) if m < n { n = m p = p[:n] } k := copy(p, b.data[b.rear:]) if k < n { copy(p[k:], b.data) } return n, nil } // Discard skips the n next bytes to read from the buffer, returning the // bytes discarded. // // If Discards skips fewer than n bytes, it returns an error. func (b *buffer) Discard(n int) (discarded int, err error) { if n < 0 { return 0, errors.New("buffer.Discard: negative argument") } m := b.Buffered() if m < n { n = m err = errors.New( "buffer.Discard: discarded less bytes then requested") } b.rear = b.addIndex(b.rear, n) return n, err } // ErrNoSpace indicates that there is insufficient space for the Write // operation. var ErrNoSpace = errors.New("insufficient space") // Write puts data into the buffer. If less bytes are written than // requested ErrNoSpace is returned. func (b *buffer) Write(p []byte) (n int, err error) { m := b.Available() n = len(p) if m < n { n = m p = p[:m] err = ErrNoSpace } k := copy(b.data[b.front:], p) if k < n { copy(b.data, p[k:]) } b.front = b.addIndex(b.front, n) return n, err } // WriteByte writes a single byte into the buffer. The error ErrNoSpace // is returned if no single byte is available in the buffer for writing. func (b *buffer) WriteByte(c byte) error { if b.Available() < 1 { return ErrNoSpace } b.data[b.front] = c b.front = b.addIndex(b.front, 1) return nil } // prefixLen returns the length of the common prefix of a and b. func prefixLen(a, b []byte) int { if len(a) > len(b) { a, b = b, a } for i, c := range a { if b[i] != c { return i } } return len(a) } // matchLen returns the length of the common prefix for the given // distance from the rear and the byte slice p. func (b *buffer) matchLen(distance int, p []byte) int { var n int i := b.rear - distance if i < 0 { if n = prefixLen(p, b.data[len(b.data)+i:]); n < -i { return n } p = p[n:] i = 0 } n += prefixLen(p, b.data[i:]) return n } ================================================ FILE: vendor/github.com/ulikunitz/xz/lzma/bytewriter.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package lzma import ( "errors" "io" ) // ErrLimit indicates that the limit of the LimitedByteWriter has been // reached. var ErrLimit = errors.New("limit reached") // LimitedByteWriter provides a byte writer that can be written until a // limit is reached. The field N provides the number of remaining // bytes. type LimitedByteWriter struct { BW io.ByteWriter N int64 } // WriteByte writes a single byte to the limited byte writer. It returns // ErrLimit if the limit has been reached. If the byte is successfully // written the field N of the LimitedByteWriter will be decremented by // one. func (l *LimitedByteWriter) WriteByte(c byte) error { if l.N <= 0 { return ErrLimit } if err := l.BW.WriteByte(c); err != nil { return err } l.N-- return nil } ================================================ FILE: vendor/github.com/ulikunitz/xz/lzma/decoder.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package lzma import ( "errors" "fmt" "io" ) // decoder decodes a raw LZMA stream without any header. type decoder struct { // dictionary; the rear pointer of the buffer will be used for // reading the data. Dict *decoderDict // decoder state State *state // range decoder rd *rangeDecoder // start stores the head value of the dictionary for the LZMA // stream start int64 // size of uncompressed data size int64 // end-of-stream encountered eos bool // EOS marker found eosMarker bool } // newDecoder creates a new decoder instance. The parameter size provides // the expected byte size of the decompressed data. If the size is // unknown use a negative value. In that case the decoder will look for // a terminating end-of-stream marker. func newDecoder(br io.ByteReader, state *state, dict *decoderDict, size int64) (d *decoder, err error) { rd, err := newRangeDecoder(br) if err != nil { return nil, err } d = &decoder{ State: state, Dict: dict, rd: rd, size: size, start: dict.pos(), } return d, nil } // Reopen restarts the decoder with a new byte reader and a new size. Reopen // resets the Decompressed counter to zero. func (d *decoder) Reopen(br io.ByteReader, size int64) error { var err error if d.rd, err = newRangeDecoder(br); err != nil { return err } d.start = d.Dict.pos() d.size = size d.eos = false return nil } // decodeLiteral decodes a single literal from the LZMA stream. func (d *decoder) decodeLiteral() (op operation, err error) { litState := d.State.litState(d.Dict.byteAt(1), d.Dict.head) match := d.Dict.byteAt(int(d.State.rep[0]) + 1) s, err := d.State.litCodec.Decode(d.rd, d.State.state, match, litState) if err != nil { return nil, err } return lit{s}, nil } // errEOS indicates that an EOS marker has been found. var errEOS = errors.New("EOS marker found") // readOp decodes the next operation from the compressed stream. It // returns the operation. If an explicit end of stream marker is // identified the eos error is returned. func (d *decoder) readOp() (op operation, err error) { // Value of the end of stream (EOS) marker const eosDist = 1<<32 - 1 state, state2, posState := d.State.states(d.Dict.head) b, err := d.State.isMatch[state2].Decode(d.rd) if err != nil { return nil, err } if b == 0 { // literal op, err := d.decodeLiteral() if err != nil { return nil, err } d.State.updateStateLiteral() return op, nil } b, err = d.State.isRep[state].Decode(d.rd) if err != nil { return nil, err } if b == 0 { // simple match d.State.rep[3], d.State.rep[2], d.State.rep[1] = d.State.rep[2], d.State.rep[1], d.State.rep[0] d.State.updateStateMatch() // The length decoder returns the length offset. n, err := d.State.lenCodec.Decode(d.rd, posState) if err != nil { return nil, err } // The dist decoder returns the distance offset. The actual // distance is 1 higher. d.State.rep[0], err = d.State.distCodec.Decode(d.rd, n) if err != nil { return nil, err } if d.State.rep[0] == eosDist { d.eosMarker = true return nil, errEOS } op = match{n: int(n) + minMatchLen, distance: int64(d.State.rep[0]) + minDistance} return op, nil } b, err = d.State.isRepG0[state].Decode(d.rd) if err != nil { return nil, err } dist := d.State.rep[0] if b == 0 { // rep match 0 b, err = d.State.isRepG0Long[state2].Decode(d.rd) if err != nil { return nil, err } if b == 0 { d.State.updateStateShortRep() op = match{n: 1, distance: int64(dist) + minDistance} return op, nil } } else { b, err = d.State.isRepG1[state].Decode(d.rd) if err != nil { return nil, err } if b == 0 { dist = d.State.rep[1] } else { b, err = d.State.isRepG2[state].Decode(d.rd) if err != nil { return nil, err } if b == 0 { dist = d.State.rep[2] } else { dist = d.State.rep[3] d.State.rep[3] = d.State.rep[2] } d.State.rep[2] = d.State.rep[1] } d.State.rep[1] = d.State.rep[0] d.State.rep[0] = dist } n, err := d.State.repLenCodec.Decode(d.rd, posState) if err != nil { return nil, err } d.State.updateStateRep() op = match{n: int(n) + minMatchLen, distance: int64(dist) + minDistance} return op, nil } // apply takes the operation and transforms the decoder dictionary accordingly. func (d *decoder) apply(op operation) error { var err error switch x := op.(type) { case match: err = d.Dict.writeMatch(x.distance, x.n) case lit: err = d.Dict.WriteByte(x.b) default: panic("op is neither a match nor a literal") } return err } // decompress fills the dictionary unless no space for new data is // available. If the end of the LZMA stream has been reached io.EOF will // be returned. func (d *decoder) decompress() error { if d.eos { return io.EOF } for d.Dict.Available() >= maxMatchLen { op, err := d.readOp() switch err { case nil: break case errEOS: d.eos = true if !d.rd.possiblyAtEnd() { return errDataAfterEOS } if d.size >= 0 && d.size != d.Decompressed() { return errSize } return io.EOF case io.EOF: d.eos = true return io.ErrUnexpectedEOF default: return err } if err = d.apply(op); err != nil { return err } if d.size >= 0 && d.Decompressed() >= d.size { d.eos = true if d.Decompressed() > d.size { return errSize } if !d.rd.possiblyAtEnd() { switch _, err = d.readOp(); err { case nil: return errSize case io.EOF: return io.ErrUnexpectedEOF case errEOS: break default: return err } } return io.EOF } } return nil } // Errors that may be returned while decoding data. var ( errDataAfterEOS = errors.New("lzma: data after end of stream marker") errSize = errors.New("lzma: wrong uncompressed data size") ) // Read reads data from the buffer. If no more data is available io.EOF is // returned. func (d *decoder) Read(p []byte) (n int, err error) { var k int for { // Read of decoder dict never returns an error. k, err = d.Dict.Read(p[n:]) if err != nil { panic(fmt.Errorf("dictionary read error %s", err)) } if k == 0 && d.eos { return n, io.EOF } n += k if n >= len(p) { return n, nil } if err = d.decompress(); err != nil && err != io.EOF { return n, err } } } // Decompressed returns the number of bytes decompressed by the decoder. func (d *decoder) Decompressed() int64 { return d.Dict.pos() - d.start } ================================================ FILE: vendor/github.com/ulikunitz/xz/lzma/decoderdict.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package lzma import ( "errors" "fmt" ) // decoderDict provides the dictionary for the decoder. The whole // dictionary is used as reader buffer. type decoderDict struct { buf buffer head int64 } // newDecoderDict creates a new decoder dictionary. The whole dictionary // will be used as reader buffer. func newDecoderDict(dictCap int) (d *decoderDict, err error) { // lower limit supports easy test cases if !(1 <= dictCap && int64(dictCap) <= MaxDictCap) { return nil, errors.New("lzma: dictCap out of range") } d = &decoderDict{buf: *newBuffer(dictCap)} return d, nil } // Reset clears the dictionary. The read buffer is not changed, so the // buffered data can still be read. func (d *decoderDict) Reset() { d.head = 0 } // WriteByte writes a single byte into the dictionary. It is used to // write literals into the dictionary. func (d *decoderDict) WriteByte(c byte) error { if err := d.buf.WriteByte(c); err != nil { return err } d.head++ return nil } // pos returns the position of the dictionary head. func (d *decoderDict) pos() int64 { return d.head } // dictLen returns the actual length of the dictionary. func (d *decoderDict) dictLen() int { capacity := d.buf.Cap() if d.head >= int64(capacity) { return capacity } return int(d.head) } // byteAt returns a byte stored in the dictionary. If the distance is // non-positive or exceeds the current length of the dictionary the zero // byte is returned. func (d *decoderDict) byteAt(dist int) byte { if !(0 < dist && dist <= d.dictLen()) { return 0 } i := d.buf.front - dist if i < 0 { i += len(d.buf.data) } return d.buf.data[i] } // writeMatch writes the match at the top of the dictionary. The given // distance must point in the current dictionary and the length must not // exceed the maximum length 273 supported in LZMA. // // The error value ErrNoSpace indicates that no space is available in // the dictionary for writing. You need to read from the dictionary // first. func (d *decoderDict) writeMatch(dist int64, length int) error { if !(0 < dist && dist <= int64(d.dictLen())) { return errors.New("writeMatch: distance out of range") } if !(0 < length && length <= maxMatchLen) { return errors.New("writeMatch: length out of range") } if length > d.buf.Available() { return ErrNoSpace } d.head += int64(length) i := d.buf.front - int(dist) if i < 0 { i += len(d.buf.data) } for length > 0 { var p []byte if i >= d.buf.front { p = d.buf.data[i:] i = 0 } else { p = d.buf.data[i:d.buf.front] i = d.buf.front } if len(p) > length { p = p[:length] } if _, err := d.buf.Write(p); err != nil { panic(fmt.Errorf("d.buf.Write returned error %s", err)) } length -= len(p) } return nil } // Write writes the given bytes into the dictionary and advances the // head. func (d *decoderDict) Write(p []byte) (n int, err error) { n, err = d.buf.Write(p) d.head += int64(n) return n, err } // Available returns the number of available bytes for writing into the // decoder dictionary. func (d *decoderDict) Available() int { return d.buf.Available() } // Read reads data from the buffer contained in the decoder dictionary. func (d *decoderDict) Read(p []byte) (n int, err error) { return d.buf.Read(p) } // Buffered returns the number of bytes currently buffered in the // decoder dictionary. func (d *decoderDict) buffered() int { return d.buf.Buffered() } // Peek gets data from the buffer without advancing the rear index. func (d *decoderDict) peek(p []byte) (n int, err error) { return d.buf.Peek(p) } ================================================ FILE: vendor/github.com/ulikunitz/xz/lzma/directcodec.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package lzma import "fmt" // directCodec allows the encoding and decoding of values with a fixed number // of bits. The number of bits must be in the range [1,32]. type directCodec byte // makeDirectCodec creates a directCodec. The function panics if the number of // bits is not in the range [1,32]. func makeDirectCodec(bits int) directCodec { if !(1 <= bits && bits <= 32) { panic(fmt.Errorf("bits=%d out of range", bits)) } return directCodec(bits) } // Bits returns the number of bits supported by this codec. func (dc directCodec) Bits() int { return int(dc) } // Encode uses the range encoder to encode a value with the fixed number of // bits. The most-significant bit is encoded first. func (dc directCodec) Encode(e *rangeEncoder, v uint32) error { for i := int(dc) - 1; i >= 0; i-- { if err := e.DirectEncodeBit(v >> uint(i)); err != nil { return err } } return nil } // Decode uses the range decoder to decode a value with the given number of // given bits. The most-significant bit is decoded first. func (dc directCodec) Decode(d *rangeDecoder) (v uint32, err error) { for i := int(dc) - 1; i >= 0; i-- { x, err := d.DirectDecodeBit() if err != nil { return 0, err } v = (v << 1) | x } return v, nil } ================================================ FILE: vendor/github.com/ulikunitz/xz/lzma/distcodec.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package lzma // Constants used by the distance codec. const ( // minimum supported distance minDistance = 1 // maximum supported distance, value is used for the eos marker. maxDistance = 1 << 32 // number of the supported len states lenStates = 4 // start for the position models startPosModel = 4 // first index with align bits support endPosModel = 14 // bits for the position slots posSlotBits = 6 // number of align bits alignBits = 4 // maximum position slot maxPosSlot = 63 ) // distCodec provides encoding and decoding of distance values. type distCodec struct { posSlotCodecs [lenStates]treeCodec posModel [endPosModel - startPosModel]treeReverseCodec alignCodec treeReverseCodec } // deepcopy initializes dc as deep copy of the source. func (dc *distCodec) deepcopy(src *distCodec) { if dc == src { return } for i := range dc.posSlotCodecs { dc.posSlotCodecs[i].deepcopy(&src.posSlotCodecs[i]) } for i := range dc.posModel { dc.posModel[i].deepcopy(&src.posModel[i]) } dc.alignCodec.deepcopy(&src.alignCodec) } // distBits returns the number of bits required to encode dist. func distBits(dist uint32) int { if dist < startPosModel { return 6 } // slot s > 3, dist d // s = 2(bits(d)-1) + bit(d, bits(d)-2) // s>>1 = bits(d)-1 // bits(d) = 32-nlz32(d) // s>>1=31-nlz32(d) // n = 5 + (s>>1) = 36 - nlz32(d) return 36 - nlz32(dist) } // newDistCodec creates a new distance codec. func (dc *distCodec) init() { for i := range dc.posSlotCodecs { dc.posSlotCodecs[i] = makeTreeCodec(posSlotBits) } for i := range dc.posModel { posSlot := startPosModel + i bits := (posSlot >> 1) - 1 dc.posModel[i] = makeTreeReverseCodec(bits) } dc.alignCodec = makeTreeReverseCodec(alignBits) } // lenState converts the value l to a supported lenState value. func lenState(l uint32) uint32 { if l >= lenStates { l = lenStates - 1 } return l } // Encode encodes the distance using the parameter l. Dist can have values from // the full range of uint32 values. To get the distance offset the actual match // distance has to be decreased by 1. A distance offset of 0xffffffff (eos) // indicates the end of the stream. func (dc *distCodec) Encode(e *rangeEncoder, dist uint32, l uint32) (err error) { // Compute the posSlot using nlz32 var posSlot uint32 var bits uint32 if dist < startPosModel { posSlot = dist } else { bits = uint32(30 - nlz32(dist)) posSlot = startPosModel - 2 + (bits << 1) posSlot += (dist >> uint(bits)) & 1 } if err = dc.posSlotCodecs[lenState(l)].Encode(e, posSlot); err != nil { return } switch { case posSlot < startPosModel: return nil case posSlot < endPosModel: tc := &dc.posModel[posSlot-startPosModel] return tc.Encode(dist, e) } dic := directCodec(bits - alignBits) if err = dic.Encode(e, dist>>alignBits); err != nil { return } return dc.alignCodec.Encode(dist, e) } // Decode decodes the distance offset using the parameter l. The dist value // 0xffffffff (eos) indicates the end of the stream. Add one to the distance // offset to get the actual match distance. func (dc *distCodec) Decode(d *rangeDecoder, l uint32) (dist uint32, err error) { posSlot, err := dc.posSlotCodecs[lenState(l)].Decode(d) if err != nil { return } // posSlot equals distance if posSlot < startPosModel { return posSlot, nil } // posSlot uses the individual models bits := (posSlot >> 1) - 1 dist = (2 | (posSlot & 1)) << bits var u uint32 if posSlot < endPosModel { tc := &dc.posModel[posSlot-startPosModel] if u, err = tc.Decode(d); err != nil { return 0, err } dist += u return dist, nil } // posSlots use direct encoding and a single model for the four align // bits. dic := directCodec(bits - alignBits) if u, err = dic.Decode(d); err != nil { return 0, err } dist += u << alignBits if u, err = dc.alignCodec.Decode(d); err != nil { return 0, err } dist += u return dist, nil } ================================================ FILE: vendor/github.com/ulikunitz/xz/lzma/encoder.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package lzma import ( "fmt" "io" ) // opLenMargin provides the upper limit of the number of bytes required // to encode a single operation. const opLenMargin = 16 // compressFlags control the compression process. type compressFlags uint32 // Values for compressFlags. const ( // all data should be compressed, even if compression is not // optimal. all compressFlags = 1 << iota ) // encoderFlags provide the flags for an encoder. type encoderFlags uint32 // Flags for the encoder. const ( // eosMarker requests an EOS marker to be written. eosMarker encoderFlags = 1 << iota ) // Encoder compresses data buffered in the encoder dictionary and writes // it into a byte writer. type encoder struct { dict *encoderDict state *state re *rangeEncoder start int64 // generate eos marker marker bool limit bool margin int } // newEncoder creates a new encoder. If the byte writer must be // limited use LimitedByteWriter provided by this package. The flags // argument supports the eosMarker flag, controlling whether a // terminating end-of-stream marker must be written. func newEncoder(bw io.ByteWriter, state *state, dict *encoderDict, flags encoderFlags) (e *encoder, err error) { re, err := newRangeEncoder(bw) if err != nil { return nil, err } e = &encoder{ dict: dict, state: state, re: re, marker: flags&eosMarker != 0, start: dict.Pos(), margin: opLenMargin, } if e.marker { e.margin += 5 } return e, nil } // Write writes the bytes from p into the dictionary. If not enough // space is available the data in the dictionary buffer will be // compressed to make additional space available. If the limit of the // underlying writer has been reached ErrLimit will be returned. func (e *encoder) Write(p []byte) (n int, err error) { for { k, err := e.dict.Write(p[n:]) n += k if err == ErrNoSpace { if err = e.compress(0); err != nil { return n, err } continue } return n, err } } // Reopen reopens the encoder with a new byte writer. func (e *encoder) Reopen(bw io.ByteWriter) error { var err error if e.re, err = newRangeEncoder(bw); err != nil { return err } e.start = e.dict.Pos() e.limit = false return nil } // writeLiteral writes a literal into the LZMA stream func (e *encoder) writeLiteral(l lit) error { var err error state, state2, _ := e.state.states(e.dict.Pos()) if err = e.state.isMatch[state2].Encode(e.re, 0); err != nil { return err } litState := e.state.litState(e.dict.ByteAt(1), e.dict.Pos()) match := e.dict.ByteAt(int(e.state.rep[0]) + 1) err = e.state.litCodec.Encode(e.re, l.b, state, match, litState) if err != nil { return err } e.state.updateStateLiteral() return nil } // iverson implements the Iverson operator as proposed by Donald Knuth in his // book Concrete Mathematics. func iverson(ok bool) uint32 { if ok { return 1 } return 0 } // writeMatch writes a repetition operation into the operation stream func (e *encoder) writeMatch(m match) error { var err error if !(minDistance <= m.distance && m.distance <= maxDistance) { panic(fmt.Errorf("match distance %d out of range", m.distance)) } dist := uint32(m.distance - minDistance) if !(minMatchLen <= m.n && m.n <= maxMatchLen) && !(dist == e.state.rep[0] && m.n == 1) { panic(fmt.Errorf( "match length %d out of range; dist %d rep[0] %d", m.n, dist, e.state.rep[0])) } state, state2, posState := e.state.states(e.dict.Pos()) if err = e.state.isMatch[state2].Encode(e.re, 1); err != nil { return err } g := 0 for ; g < 4; g++ { if e.state.rep[g] == dist { break } } b := iverson(g < 4) if err = e.state.isRep[state].Encode(e.re, b); err != nil { return err } n := uint32(m.n - minMatchLen) if b == 0 { // simple match e.state.rep[3], e.state.rep[2], e.state.rep[1], e.state.rep[0] = e.state.rep[2], e.state.rep[1], e.state.rep[0], dist e.state.updateStateMatch() if err = e.state.lenCodec.Encode(e.re, n, posState); err != nil { return err } return e.state.distCodec.Encode(e.re, dist, n) } b = iverson(g != 0) if err = e.state.isRepG0[state].Encode(e.re, b); err != nil { return err } if b == 0 { // g == 0 b = iverson(m.n != 1) if err = e.state.isRepG0Long[state2].Encode(e.re, b); err != nil { return err } if b == 0 { e.state.updateStateShortRep() return nil } } else { // g in {1,2,3} b = iverson(g != 1) if err = e.state.isRepG1[state].Encode(e.re, b); err != nil { return err } if b == 1 { // g in {2,3} b = iverson(g != 2) err = e.state.isRepG2[state].Encode(e.re, b) if err != nil { return err } if b == 1 { e.state.rep[3] = e.state.rep[2] } e.state.rep[2] = e.state.rep[1] } e.state.rep[1] = e.state.rep[0] e.state.rep[0] = dist } e.state.updateStateRep() return e.state.repLenCodec.Encode(e.re, n, posState) } // writeOp writes a single operation to the range encoder. The function // checks whether there is enough space available to close the LZMA // stream. func (e *encoder) writeOp(op operation) error { if e.re.Available() < int64(e.margin) { return ErrLimit } switch x := op.(type) { case lit: return e.writeLiteral(x) case match: return e.writeMatch(x) default: panic("unexpected operation") } } // compress compressed data from the dictionary buffer. If the flag all // is set, all data in the dictionary buffer will be compressed. The // function returns ErrLimit if the underlying writer has reached its // limit. func (e *encoder) compress(flags compressFlags) error { n := 0 if flags&all == 0 { n = maxMatchLen - 1 } d := e.dict m := d.m for d.Buffered() > n { op := m.NextOp(e.state.rep) if err := e.writeOp(op); err != nil { return err } d.Discard(op.Len()) } return nil } // eosMatch is a pseudo operation that indicates the end of the stream. var eosMatch = match{distance: maxDistance, n: minMatchLen} // Close terminates the LZMA stream. If requested the end-of-stream // marker will be written. If the byte writer limit has been or will be // reached during compression of the remaining data in the buffer the // LZMA stream will be closed and data will remain in the buffer. func (e *encoder) Close() error { err := e.compress(all) if err != nil && err != ErrLimit { return err } if e.marker { if err := e.writeMatch(eosMatch); err != nil { return err } } err = e.re.Close() return err } // Compressed returns the number bytes of the input data that been // compressed. func (e *encoder) Compressed() int64 { return e.dict.Pos() - e.start } ================================================ FILE: vendor/github.com/ulikunitz/xz/lzma/encoderdict.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package lzma import ( "errors" "fmt" "io" ) // matcher is an interface that supports the identification of the next // operation. type matcher interface { io.Writer SetDict(d *encoderDict) NextOp(rep [4]uint32) operation } // encoderDict provides the dictionary of the encoder. It includes an // addtional buffer atop of the actual dictionary. type encoderDict struct { buf buffer m matcher head int64 capacity int // preallocated array data [maxMatchLen]byte } // newEncoderDict creates the encoder dictionary. The argument bufSize // defines the size of the additional buffer. func newEncoderDict(dictCap, bufSize int, m matcher) (d *encoderDict, err error) { if !(1 <= dictCap && int64(dictCap) <= MaxDictCap) { return nil, errors.New( "lzma: dictionary capacity out of range") } if bufSize < 1 { return nil, errors.New( "lzma: buffer size must be larger than zero") } d = &encoderDict{ buf: *newBuffer(dictCap + bufSize), capacity: dictCap, m: m, } m.SetDict(d) return d, nil } // Discard discards n bytes. Note that n must not be larger than // MaxMatchLen. func (d *encoderDict) Discard(n int) { p := d.data[:n] k, _ := d.buf.Read(p) if k < n { panic(fmt.Errorf("lzma: can't discard %d bytes", n)) } d.head += int64(n) d.m.Write(p) } // Len returns the data available in the encoder dictionary. func (d *encoderDict) Len() int { n := d.buf.Available() if int64(n) > d.head { return int(d.head) } return n } // DictLen returns the actual length of data in the dictionary. func (d *encoderDict) DictLen() int { if d.head < int64(d.capacity) { return int(d.head) } return d.capacity } // Available returns the number of bytes that can be written by a // following Write call. func (d *encoderDict) Available() int { return d.buf.Available() - d.DictLen() } // Write writes data into the dictionary buffer. Note that the position // of the dictionary head will not be moved. If there is not enough // space in the buffer ErrNoSpace will be returned. func (d *encoderDict) Write(p []byte) (n int, err error) { m := d.Available() if len(p) > m { p = p[:m] err = ErrNoSpace } var e error if n, e = d.buf.Write(p); e != nil { err = e } return n, err } // Pos returns the position of the head. func (d *encoderDict) Pos() int64 { return d.head } // ByteAt returns the byte at the given distance. func (d *encoderDict) ByteAt(distance int) byte { if !(0 < distance && distance <= d.Len()) { return 0 } i := d.buf.rear - distance if i < 0 { i += len(d.buf.data) } return d.buf.data[i] } // CopyN copies the last n bytes from the dictionary into the provided // writer. This is used for copying uncompressed data into an // uncompressed segment. func (d *encoderDict) CopyN(w io.Writer, n int) (written int, err error) { if n <= 0 { return 0, nil } m := d.Len() if n > m { n = m err = ErrNoSpace } i := d.buf.rear - n var e error if i < 0 { i += len(d.buf.data) if written, e = w.Write(d.buf.data[i:]); e != nil { return written, e } i = 0 } var k int k, e = w.Write(d.buf.data[i:d.buf.rear]) written += k if e != nil { err = e } return written, err } // Buffered returns the number of bytes in the buffer. func (d *encoderDict) Buffered() int { return d.buf.Buffered() } ================================================ FILE: vendor/github.com/ulikunitz/xz/lzma/hashtable.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package lzma import ( "errors" "fmt" "github.com/ulikunitz/xz/internal/hash" ) /* For compression we need to find byte sequences that match the byte * sequence at the dictionary head. A hash table is a simple method to * provide this capability. */ // maxMatches limits the number of matches requested from the Matches // function. This controls the speed of the overall encoding. const maxMatches = 16 // shortDists defines the number of short distances supported by the // implementation. const shortDists = 8 // The minimum is somehow arbitrary but the maximum is limited by the // memory requirements of the hash table. const ( minTableExponent = 9 maxTableExponent = 20 ) // newRoller contains the function used to create an instance of the // hash.Roller. var newRoller = func(n int) hash.Roller { return hash.NewCyclicPoly(n) } // hashTable stores the hash table including the rolling hash method. // // We implement chained hashing into a circular buffer. Each entry in // the circular buffer stores the delta distance to the next position with a // word that has the same hash value. type hashTable struct { dict *encoderDict // actual hash table t []int64 // circular list data with the offset to the next word data []uint32 front int // mask for computing the index for the hash table mask uint64 // hash offset; initial value is -int64(wordLen) hoff int64 // length of the hashed word wordLen int // hash roller for computing the hash values for the Write // method wr hash.Roller // hash roller for computing arbitrary hashes hr hash.Roller // preallocated slices p [maxMatches]int64 distances [maxMatches + shortDists]int } // hashTableExponent derives the hash table exponent from the dictionary // capacity. func hashTableExponent(n uint32) int { e := 30 - nlz32(n) switch { case e < minTableExponent: e = minTableExponent case e > maxTableExponent: e = maxTableExponent } return e } // newHashTable creates a new hash table for words of length wordLen func newHashTable(capacity int, wordLen int) (t *hashTable, err error) { if !(0 < capacity) { return nil, errors.New( "newHashTable: capacity must not be negative") } exp := hashTableExponent(uint32(capacity)) if !(1 <= wordLen && wordLen <= 4) { return nil, errors.New("newHashTable: " + "argument wordLen out of range") } n := 1 << uint(exp) if n <= 0 { panic("newHashTable: exponent is too large") } t = &hashTable{ t: make([]int64, n), data: make([]uint32, capacity), mask: (uint64(1) << uint(exp)) - 1, hoff: -int64(wordLen), wordLen: wordLen, wr: newRoller(wordLen), hr: newRoller(wordLen), } return t, nil } func (t *hashTable) SetDict(d *encoderDict) { t.dict = d } // buffered returns the number of bytes that are currently hashed. func (t *hashTable) buffered() int { n := t.hoff + 1 switch { case n <= 0: return 0 case n >= int64(len(t.data)): return len(t.data) } return int(n) } // addIndex adds n to an index ensuring that is stays inside the // circular buffer for the hash chain. func (t *hashTable) addIndex(i, n int) int { i += n - len(t.data) if i < 0 { i += len(t.data) } return i } // putDelta puts the delta instance at the current front of the circular // chain buffer. func (t *hashTable) putDelta(delta uint32) { t.data[t.front] = delta t.front = t.addIndex(t.front, 1) } // putEntry puts a new entry into the hash table. If there is already a // value stored it is moved into the circular chain buffer. func (t *hashTable) putEntry(h uint64, pos int64) { if pos < 0 { return } i := h & t.mask old := t.t[i] - 1 t.t[i] = pos + 1 var delta int64 if old >= 0 { delta = pos - old if delta > 1<<32-1 || delta > int64(t.buffered()) { delta = 0 } } t.putDelta(uint32(delta)) } // WriteByte converts a single byte into a hash and puts them into the hash // table. func (t *hashTable) WriteByte(b byte) error { h := t.wr.RollByte(b) t.hoff++ t.putEntry(h, t.hoff) return nil } // Write converts the bytes provided into hash tables and stores the // abbreviated offsets into the hash table. The method will never return an // error. func (t *hashTable) Write(p []byte) (n int, err error) { for _, b := range p { // WriteByte doesn't generate an error. t.WriteByte(b) } return len(p), nil } // getMatches the matches for a specific hash. The functions returns the // number of positions found. // // TODO: Make a getDistances because that we are actually interested in. func (t *hashTable) getMatches(h uint64, positions []int64) (n int) { if t.hoff < 0 || len(positions) == 0 { return 0 } buffered := t.buffered() tailPos := t.hoff + 1 - int64(buffered) rear := t.front - buffered if rear >= 0 { rear -= len(t.data) } // get the slot for the hash pos := t.t[h&t.mask] - 1 delta := pos - tailPos for { if delta < 0 { return n } positions[n] = tailPos + delta n++ if n >= len(positions) { return n } i := rear + int(delta) if i < 0 { i += len(t.data) } u := t.data[i] if u == 0 { return n } delta -= int64(u) } } // hash computes the rolling hash for the word stored in p. For correct // results its length must be equal to t.wordLen. func (t *hashTable) hash(p []byte) uint64 { var h uint64 for _, b := range p { h = t.hr.RollByte(b) } return h } // Matches fills the positions slice with potential matches. The // functions returns the number of positions filled into positions. The // byte slice p must have word length of the hash table. func (t *hashTable) Matches(p []byte, positions []int64) int { if len(p) != t.wordLen { panic(fmt.Errorf( "byte slice must have length %d", t.wordLen)) } h := t.hash(p) return t.getMatches(h, positions) } // NextOp identifies the next operation using the hash table. // // TODO: Use all repetitions to find matches. func (t *hashTable) NextOp(rep [4]uint32) operation { // get positions data := t.dict.data[:maxMatchLen] n, _ := t.dict.buf.Peek(data) data = data[:n] var p []int64 if n < t.wordLen { p = t.p[:0] } else { p = t.p[:maxMatches] n = t.Matches(data[:t.wordLen], p) p = p[:n] } // convert positions in potential distances head := t.dict.head dists := append(t.distances[:0], 1, 2, 3, 4, 5, 6, 7, 8) for _, pos := range p { dis := int(head - pos) if dis > shortDists { dists = append(dists, dis) } } // check distances var m match dictLen := t.dict.DictLen() for _, dist := range dists { if dist > dictLen { continue } // Here comes a trick. We are only interested in matches // that are longer than the matches we have been found // before. So before we test the whole byte sequence at // the given distance, we test the first byte that would // make the match longer. If it doesn't match the byte // to match, we don't to care any longer. i := t.dict.buf.rear - dist + m.n if i < 0 { i += len(t.dict.buf.data) } if t.dict.buf.data[i] != data[m.n] { // We can't get a longer match. Jump to the next // distance. continue } n := t.dict.buf.matchLen(dist, data) switch n { case 0: continue case 1: if uint32(dist-minDistance) != rep[0] { continue } } if n > m.n { m = match{int64(dist), n} if n == len(data) { // No better match will be found. break } } } if m.n == 0 { return lit{data[0]} } return m } ================================================ FILE: vendor/github.com/ulikunitz/xz/lzma/header.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package lzma import ( "errors" "fmt" ) // uint32LE reads an uint32 integer from a byte slice func uint32LE(b []byte) uint32 { x := uint32(b[3]) << 24 x |= uint32(b[2]) << 16 x |= uint32(b[1]) << 8 x |= uint32(b[0]) return x } // uint64LE converts the uint64 value stored as little endian to an uint64 // value. func uint64LE(b []byte) uint64 { x := uint64(b[7]) << 56 x |= uint64(b[6]) << 48 x |= uint64(b[5]) << 40 x |= uint64(b[4]) << 32 x |= uint64(b[3]) << 24 x |= uint64(b[2]) << 16 x |= uint64(b[1]) << 8 x |= uint64(b[0]) return x } // putUint32LE puts an uint32 integer into a byte slice that must have at least // a length of 4 bytes. func putUint32LE(b []byte, x uint32) { b[0] = byte(x) b[1] = byte(x >> 8) b[2] = byte(x >> 16) b[3] = byte(x >> 24) } // putUint64LE puts the uint64 value into the byte slice as little endian // value. The byte slice b must have at least place for 8 bytes. func putUint64LE(b []byte, x uint64) { b[0] = byte(x) b[1] = byte(x >> 8) b[2] = byte(x >> 16) b[3] = byte(x >> 24) b[4] = byte(x >> 32) b[5] = byte(x >> 40) b[6] = byte(x >> 48) b[7] = byte(x >> 56) } // noHeaderSize defines the value of the length field in the LZMA header. const noHeaderSize uint64 = 1<<64 - 1 // HeaderLen provides the length of the LZMA file header. const HeaderLen = 13 // header represents the header of an LZMA file. type header struct { properties Properties dictCap int // uncompressed size; negative value if no size is given size int64 } // marshalBinary marshals the header. func (h *header) marshalBinary() (data []byte, err error) { if err = h.properties.verify(); err != nil { return nil, err } if !(0 <= h.dictCap && int64(h.dictCap) <= MaxDictCap) { return nil, fmt.Errorf("lzma: DictCap %d out of range", h.dictCap) } data = make([]byte, 13) // property byte data[0] = h.properties.Code() // dictionary capacity putUint32LE(data[1:5], uint32(h.dictCap)) // uncompressed size var s uint64 if h.size > 0 { s = uint64(h.size) } else { s = noHeaderSize } putUint64LE(data[5:], s) return data, nil } // unmarshalBinary unmarshals the header. func (h *header) unmarshalBinary(data []byte) error { if len(data) != HeaderLen { return errors.New("lzma.unmarshalBinary: data has wrong length") } // properties var err error if h.properties, err = PropertiesForCode(data[0]); err != nil { return err } // dictionary capacity h.dictCap = int(uint32LE(data[1:])) if h.dictCap < 0 { return errors.New( "LZMA header: dictionary capacity exceeds maximum " + "integer") } // uncompressed size s := uint64LE(data[5:]) if s == noHeaderSize { h.size = -1 } else { h.size = int64(s) if h.size < 0 { return errors.New( "LZMA header: uncompressed size " + "out of int64 range") } } return nil } // validDictCap checks whether the dictionary capacity is correct. This // is used to weed out wrong file headers. func validDictCap(dictcap int) bool { if int64(dictcap) == MaxDictCap { return true } for n := uint(10); n < 32; n++ { if dictcap == 1<= 10 or 2^32-1. If // there is an explicit size it must not exceed 256 GiB. The length of // the data argument must be HeaderLen. func ValidHeader(data []byte) bool { var h header if err := h.unmarshalBinary(data); err != nil { return false } if !validDictCap(h.dictCap) { return false } return h.size < 0 || h.size <= 1<<38 } ================================================ FILE: vendor/github.com/ulikunitz/xz/lzma/header2.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package lzma import ( "errors" "fmt" "io" ) const ( // maximum size of compressed data in a chunk maxCompressed = 1 << 16 // maximum size of uncompressed data in a chunk maxUncompressed = 1 << 21 ) // chunkType represents the type of an LZMA2 chunk. Note that this // value is an internal representation and no actual encoding of a LZMA2 // chunk header. type chunkType byte // Possible values for the chunk type. const ( // end of stream cEOS chunkType = iota // uncompressed; reset dictionary cUD // uncompressed; no reset of dictionary cU // LZMA compressed; no reset cL // LZMA compressed; reset state cLR // LZMA compressed; reset state; new property value cLRN // LZMA compressed; reset state; new property value; reset dictionary cLRND ) // chunkTypeStrings provide a string representation for the chunk types. var chunkTypeStrings = [...]string{ cEOS: "EOS", cU: "U", cUD: "UD", cL: "L", cLR: "LR", cLRN: "LRN", cLRND: "LRND", } // String returns a string representation of the chunk type. func (c chunkType) String() string { if !(cEOS <= c && c <= cLRND) { return "unknown" } return chunkTypeStrings[c] } // Actual encodings for the chunk types in the value. Note that the high // uncompressed size bits are stored in the header byte additionally. const ( hEOS = 0 hUD = 1 hU = 2 hL = 1 << 7 hLR = 1<<7 | 1<<5 hLRN = 1<<7 | 1<<6 hLRND = 1<<7 | 1<<6 | 1<<5 ) // errHeaderByte indicates an unsupported value for the chunk header // byte. These bytes starts the variable-length chunk header. var errHeaderByte = errors.New("lzma: unsupported chunk header byte") // headerChunkType converts the header byte into a chunk type. It // ignores the uncompressed size bits in the chunk header byte. func headerChunkType(h byte) (c chunkType, err error) { if h&hL == 0 { // no compression switch h { case hEOS: c = cEOS case hUD: c = cUD case hU: c = cU default: return 0, errHeaderByte } return } switch h & hLRND { case hL: c = cL case hLR: c = cLR case hLRN: c = cLRN case hLRND: c = cLRND default: return 0, errHeaderByte } return } // uncompressedHeaderLen provides the length of an uncompressed header const uncompressedHeaderLen = 3 // headerLen returns the length of the LZMA2 header for a given chunk // type. func headerLen(c chunkType) int { switch c { case cEOS: return 1 case cU, cUD: return uncompressedHeaderLen case cL, cLR: return 5 case cLRN, cLRND: return 6 } panic(fmt.Errorf("unsupported chunk type %d", c)) } // chunkHeader represents the contents of a chunk header. type chunkHeader struct { ctype chunkType uncompressed uint32 compressed uint16 props Properties } // String returns a string representation of the chunk header. func (h *chunkHeader) String() string { return fmt.Sprintf("%s %d %d %s", h.ctype, h.uncompressed, h.compressed, &h.props) } // UnmarshalBinary reads the content of the chunk header from the data // slice. The slice must have the correct length. func (h *chunkHeader) UnmarshalBinary(data []byte) error { if len(data) == 0 { return errors.New("no data") } c, err := headerChunkType(data[0]) if err != nil { return err } n := headerLen(c) if len(data) < n { return errors.New("incomplete data") } if len(data) > n { return errors.New("invalid data length") } *h = chunkHeader{ctype: c} if c == cEOS { return nil } h.uncompressed = uint32(uint16BE(data[1:3])) if c <= cU { return nil } h.uncompressed |= uint32(data[0]&^hLRND) << 16 h.compressed = uint16BE(data[3:5]) if c <= cLR { return nil } h.props, err = PropertiesForCode(data[5]) return err } // MarshalBinary encodes the chunk header value. The function checks // whether the content of the chunk header is correct. func (h *chunkHeader) MarshalBinary() (data []byte, err error) { if h.ctype > cLRND { return nil, errors.New("invalid chunk type") } if err = h.props.verify(); err != nil { return nil, err } data = make([]byte, headerLen(h.ctype)) switch h.ctype { case cEOS: return data, nil case cUD: data[0] = hUD case cU: data[0] = hU case cL: data[0] = hL case cLR: data[0] = hLR case cLRN: data[0] = hLRN case cLRND: data[0] = hLRND } putUint16BE(data[1:3], uint16(h.uncompressed)) if h.ctype <= cU { return data, nil } data[0] |= byte(h.uncompressed>>16) &^ hLRND putUint16BE(data[3:5], h.compressed) if h.ctype <= cLR { return data, nil } data[5] = h.props.Code() return data, nil } // readChunkHeader reads the chunk header from the IO reader. func readChunkHeader(r io.Reader) (h *chunkHeader, err error) { p := make([]byte, 1, 6) if _, err = io.ReadFull(r, p); err != nil { return } c, err := headerChunkType(p[0]) if err != nil { return } p = p[:headerLen(c)] if _, err = io.ReadFull(r, p[1:]); err != nil { return } h = new(chunkHeader) if err = h.UnmarshalBinary(p); err != nil { return nil, err } return h, nil } // uint16BE converts a big-endian uint16 representation to an uint16 // value. func uint16BE(p []byte) uint16 { return uint16(p[0])<<8 | uint16(p[1]) } // putUint16BE puts the big-endian uint16 presentation into the given // slice. func putUint16BE(p []byte, x uint16) { p[0] = byte(x >> 8) p[1] = byte(x) } // chunkState is used to manage the state of the chunks type chunkState byte // start and stop define the initial and terminating state of the chunk // state const ( start chunkState = 'S' stop = 'T' ) // errors for the chunk state handling var ( errChunkType = errors.New("lzma: unexpected chunk type") errState = errors.New("lzma: wrong chunk state") ) // next transitions state based on chunk type input func (c *chunkState) next(ctype chunkType) error { switch *c { // start state case 'S': switch ctype { case cEOS: *c = 'T' case cUD: *c = 'R' case cLRND: *c = 'L' default: return errChunkType } // normal LZMA mode case 'L': switch ctype { case cEOS: *c = 'T' case cUD: *c = 'R' case cU: *c = 'U' case cL, cLR, cLRN, cLRND: break default: return errChunkType } // reset required case 'R': switch ctype { case cEOS: *c = 'T' case cUD, cU: break case cLRN, cLRND: *c = 'L' default: return errChunkType } // uncompressed case 'U': switch ctype { case cEOS: *c = 'T' case cUD: *c = 'R' case cU: break case cL, cLR, cLRN, cLRND: *c = 'L' default: return errChunkType } // terminal state case 'T': return errChunkType default: return errState } return nil } // defaultChunkType returns the default chunk type for each chunk state. func (c chunkState) defaultChunkType() chunkType { switch c { case 'S': return cLRND case 'L', 'U': return cL case 'R': return cLRN default: // no error return cEOS } } // maxDictCap defines the maximum dictionary capacity supported by the // LZMA2 dictionary capacity encoding. const maxDictCap = 1<<32 - 1 // maxDictCapCode defines the maximum dictionary capacity code. const maxDictCapCode = 40 // The function decodes the dictionary capacity byte, but doesn't change // for the correct range of the given byte. func decodeDictCap(c byte) int64 { return (2 | int64(c)&1) << (11 + (c>>1)&0x1f) } // DecodeDictCap decodes the encoded dictionary capacity. The function // returns an error if the code is out of range. func DecodeDictCap(c byte) (n int64, err error) { if c >= maxDictCapCode { if c == maxDictCapCode { return maxDictCap, nil } return 0, errors.New("lzma: invalid dictionary size code") } return decodeDictCap(c), nil } // EncodeDictCap encodes a dictionary capacity. The function returns the // code for the capacity that is greater or equal n. If n exceeds the // maximum support dictionary capacity, the maximum value is returned. func EncodeDictCap(n int64) byte { a, b := byte(0), byte(40) for a < b { c := a + (b-a)>>1 m := decodeDictCap(c) if n <= m { if n == m { return c } b = c } else { a = c + 1 } } return a } ================================================ FILE: vendor/github.com/ulikunitz/xz/lzma/lengthcodec.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package lzma import "errors" // maxPosBits defines the number of bits of the position value that are used to // to compute the posState value. The value is used to select the tree codec // for length encoding and decoding. const maxPosBits = 4 // minMatchLen and maxMatchLen give the minimum and maximum values for // encoding and decoding length values. minMatchLen is also used as base // for the encoded length values. const ( minMatchLen = 2 maxMatchLen = minMatchLen + 16 + 256 - 1 ) // lengthCodec support the encoding of the length value. type lengthCodec struct { choice [2]prob low [1 << maxPosBits]treeCodec mid [1 << maxPosBits]treeCodec high treeCodec } // deepcopy initializes the lc value as deep copy of the source value. func (lc *lengthCodec) deepcopy(src *lengthCodec) { if lc == src { return } lc.choice = src.choice for i := range lc.low { lc.low[i].deepcopy(&src.low[i]) } for i := range lc.mid { lc.mid[i].deepcopy(&src.mid[i]) } lc.high.deepcopy(&src.high) } // init initializes a new length codec. func (lc *lengthCodec) init() { for i := range lc.choice { lc.choice[i] = probInit } for i := range lc.low { lc.low[i] = makeTreeCodec(3) } for i := range lc.mid { lc.mid[i] = makeTreeCodec(3) } lc.high = makeTreeCodec(8) } // lBits gives the number of bits used for the encoding of the l value // provided to the range encoder. func lBits(l uint32) int { switch { case l < 8: return 4 case l < 16: return 5 default: return 10 } } // Encode encodes the length offset. The length offset l can be compute by // subtracting minMatchLen (2) from the actual length. // // l = length - minMatchLen // func (lc *lengthCodec) Encode(e *rangeEncoder, l uint32, posState uint32, ) (err error) { if l > maxMatchLen-minMatchLen { return errors.New("lengthCodec.Encode: l out of range") } if l < 8 { if err = lc.choice[0].Encode(e, 0); err != nil { return } return lc.low[posState].Encode(e, l) } if err = lc.choice[0].Encode(e, 1); err != nil { return } if l < 16 { if err = lc.choice[1].Encode(e, 0); err != nil { return } return lc.mid[posState].Encode(e, l-8) } if err = lc.choice[1].Encode(e, 1); err != nil { return } if err = lc.high.Encode(e, l-16); err != nil { return } return nil } // Decode reads the length offset. Add minMatchLen to compute the actual length // to the length offset l. func (lc *lengthCodec) Decode(d *rangeDecoder, posState uint32, ) (l uint32, err error) { var b uint32 if b, err = lc.choice[0].Decode(d); err != nil { return } if b == 0 { l, err = lc.low[posState].Decode(d) return } if b, err = lc.choice[1].Decode(d); err != nil { return } if b == 0 { l, err = lc.mid[posState].Decode(d) l += 8 return } l, err = lc.high.Decode(d) l += 16 return } ================================================ FILE: vendor/github.com/ulikunitz/xz/lzma/literalcodec.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package lzma // literalCodec supports the encoding of literal. It provides 768 probability // values per literal state. The upper 512 probabilities are used with the // context of a match bit. type literalCodec struct { probs []prob } // deepcopy initializes literal codec c as a deep copy of the source. func (c *literalCodec) deepcopy(src *literalCodec) { if c == src { return } c.probs = make([]prob, len(src.probs)) copy(c.probs, src.probs) } // init initializes the literal codec. func (c *literalCodec) init(lc, lp int) { switch { case !(minLC <= lc && lc <= maxLC): panic("lc out of range") case !(minLP <= lp && lp <= maxLP): panic("lp out of range") } c.probs = make([]prob, 0x300<= 7 { m := uint32(match) for { matchBit := (m >> 7) & 1 m <<= 1 bit := (r >> 7) & 1 r <<= 1 i := ((1 + matchBit) << 8) | symbol if err = probs[i].Encode(e, bit); err != nil { return } symbol = (symbol << 1) | bit if matchBit != bit { break } if symbol >= 0x100 { break } } } for symbol < 0x100 { bit := (r >> 7) & 1 r <<= 1 if err = probs[symbol].Encode(e, bit); err != nil { return } symbol = (symbol << 1) | bit } return nil } // Decode decodes a literal byte using the range decoder as well as the LZMA // state, a match byte, and the literal state. func (c *literalCodec) Decode(d *rangeDecoder, state uint32, match byte, litState uint32, ) (s byte, err error) { k := litState * 0x300 probs := c.probs[k : k+0x300] symbol := uint32(1) if state >= 7 { m := uint32(match) for { matchBit := (m >> 7) & 1 m <<= 1 i := ((1 + matchBit) << 8) | symbol bit, err := d.DecodeBit(&probs[i]) if err != nil { return 0, err } symbol = (symbol << 1) | bit if matchBit != bit { break } if symbol >= 0x100 { break } } } for symbol < 0x100 { bit, err := d.DecodeBit(&probs[symbol]) if err != nil { return 0, err } symbol = (symbol << 1) | bit } s = byte(symbol - 0x100) return s, nil } // minLC and maxLC define the range for LC values. const ( minLC = 0 maxLC = 8 ) // minLC and maxLC define the range for LP values. const ( minLP = 0 maxLP = 4 ) // minState and maxState define a range for the state values stored in // the State values. const ( minState = 0 maxState = 11 ) ================================================ FILE: vendor/github.com/ulikunitz/xz/lzma/matchalgorithm.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package lzma import "errors" // MatchAlgorithm identifies an algorithm to find matches in the // dictionary. type MatchAlgorithm byte // Supported matcher algorithms. const ( HashTable4 MatchAlgorithm = iota BinaryTree ) // maStrings are used by the String method. var maStrings = map[MatchAlgorithm]string{ HashTable4: "HashTable4", BinaryTree: "BinaryTree", } // String returns a string representation of the Matcher. func (a MatchAlgorithm) String() string { if s, ok := maStrings[a]; ok { return s } return "unknown" } var errUnsupportedMatchAlgorithm = errors.New( "lzma: unsupported match algorithm value") // verify checks whether the matcher value is supported. func (a MatchAlgorithm) verify() error { if _, ok := maStrings[a]; !ok { return errUnsupportedMatchAlgorithm } return nil } func (a MatchAlgorithm) new(dictCap int) (m matcher, err error) { switch a { case HashTable4: return newHashTable(dictCap, 4) case BinaryTree: return newBinTree(dictCap) } return nil, errUnsupportedMatchAlgorithm } ================================================ FILE: vendor/github.com/ulikunitz/xz/lzma/operation.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package lzma import ( "errors" "fmt" "unicode" ) // operation represents an operation on the dictionary during encoding or // decoding. type operation interface { Len() int } // rep represents a repetition at the given distance and the given length type match struct { // supports all possible distance values, including the eos marker distance int64 // length n int } // verify checks whether the match is valid. If that is not the case an // error is returned. func (m match) verify() error { if !(minDistance <= m.distance && m.distance <= maxDistance) { return errors.New("distance out of range") } if !(1 <= m.n && m.n <= maxMatchLen) { return errors.New("length out of range") } return nil } // l return the l-value for the match, which is the difference of length // n and 2. func (m match) l() uint32 { return uint32(m.n - minMatchLen) } // dist returns the dist value for the match, which is one less of the // distance stored in the match. func (m match) dist() uint32 { return uint32(m.distance - minDistance) } // Len returns the number of bytes matched. func (m match) Len() int { return m.n } // String returns a string representation for the repetition. func (m match) String() string { return fmt.Sprintf("M{%d,%d}", m.distance, m.n) } // lit represents a single byte literal. type lit struct { b byte } // Len returns 1 for the single byte literal. func (l lit) Len() int { return 1 } // String returns a string representation for the literal. func (l lit) String() string { var c byte if unicode.IsPrint(rune(l.b)) { c = l.b } else { c = '.' } return fmt.Sprintf("L{%c/%02x}", c, l.b) } ================================================ FILE: vendor/github.com/ulikunitz/xz/lzma/prob.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package lzma // movebits defines the number of bits used for the updates of probability // values. const movebits = 5 // probbits defines the number of bits of a probability value. const probbits = 11 // probInit defines 0.5 as initial value for prob values. const probInit prob = 1 << (probbits - 1) // Type prob represents probabilities. The type can also be used to encode and // decode single bits. type prob uint16 // Dec decreases the probability. The decrease is proportional to the // probability value. func (p *prob) dec() { *p -= *p >> movebits } // Inc increases the probability. The Increase is proportional to the // difference of 1 and the probability value. func (p *prob) inc() { *p += ((1 << probbits) - *p) >> movebits } // Computes the new bound for a given range using the probability value. func (p prob) bound(r uint32) uint32 { return (r >> probbits) * uint32(p) } // Bits returns 1. One is the number of bits that can be encoded or decoded // with a single prob value. func (p prob) Bits() int { return 1 } // Encode encodes the least-significant bit of v. Note that the p value will be // changed. func (p *prob) Encode(e *rangeEncoder, v uint32) error { return e.EncodeBit(v, p) } // Decode decodes a single bit. Note that the p value will change. func (p *prob) Decode(d *rangeDecoder) (v uint32, err error) { return d.DecodeBit(p) } ================================================ FILE: vendor/github.com/ulikunitz/xz/lzma/properties.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package lzma import ( "errors" "fmt" ) // maximum and minimum values for the LZMA properties. const ( minPB = 0 maxPB = 4 ) // maxPropertyCode is the possible maximum of a properties code byte. const maxPropertyCode = (maxPB+1)*(maxLP+1)*(maxLC+1) - 1 // Properties contains the parameters LC, LP and PB. The parameter LC // defines the number of literal context bits; parameter LP the number // of literal position bits and PB the number of position bits. type Properties struct { LC int LP int PB int } // String returns the properties in a string representation. func (p *Properties) String() string { return fmt.Sprintf("LC %d LP %d PB %d", p.LC, p.LP, p.PB) } // PropertiesForCode converts a properties code byte into a Properties value. func PropertiesForCode(code byte) (p Properties, err error) { if code > maxPropertyCode { return p, errors.New("lzma: invalid properties code") } p.LC = int(code % 9) code /= 9 p.LP = int(code % 5) code /= 5 p.PB = int(code % 5) return p, err } // verify checks the properties for correctness. func (p *Properties) verify() error { if p == nil { return errors.New("lzma: properties are nil") } if !(minLC <= p.LC && p.LC <= maxLC) { return errors.New("lzma: lc out of range") } if !(minLP <= p.LP && p.LP <= maxLP) { return errors.New("lzma: lp out of range") } if !(minPB <= p.PB && p.PB <= maxPB) { return errors.New("lzma: pb out of range") } return nil } // Code converts the properties to a byte. The function assumes that // the properties components are all in range. func (p Properties) Code() byte { return byte((p.PB*5+p.LP)*9 + p.LC) } ================================================ FILE: vendor/github.com/ulikunitz/xz/lzma/rangecodec.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package lzma import ( "errors" "io" ) // rangeEncoder implements range encoding of single bits. The low value can // overflow therefore we need uint64. The cache value is used to handle // overflows. type rangeEncoder struct { lbw *LimitedByteWriter nrange uint32 low uint64 cacheLen int64 cache byte } // maxInt64 provides the maximal value of the int64 type const maxInt64 = 1<<63 - 1 // newRangeEncoder creates a new range encoder. func newRangeEncoder(bw io.ByteWriter) (re *rangeEncoder, err error) { lbw, ok := bw.(*LimitedByteWriter) if !ok { lbw = &LimitedByteWriter{BW: bw, N: maxInt64} } return &rangeEncoder{ lbw: lbw, nrange: 0xffffffff, cacheLen: 1}, nil } // Available returns the number of bytes that still can be written. The // method takes the bytes that will be currently written by Close into // account. func (e *rangeEncoder) Available() int64 { return e.lbw.N - (e.cacheLen + 4) } // writeByte writes a single byte to the underlying writer. An error is // returned if the limit is reached. The written byte will be counted if // the underlying writer doesn't return an error. func (e *rangeEncoder) writeByte(c byte) error { if e.Available() < 1 { return ErrLimit } return e.lbw.WriteByte(c) } // DirectEncodeBit encodes the least-significant bit of b with probability 1/2. func (e *rangeEncoder) DirectEncodeBit(b uint32) error { e.nrange >>= 1 e.low += uint64(e.nrange) & (0 - (uint64(b) & 1)) // normalize const top = 1 << 24 if e.nrange >= top { return nil } e.nrange <<= 8 return e.shiftLow() } // EncodeBit encodes the least significant bit of b. The p value will be // updated by the function depending on the bit encoded. func (e *rangeEncoder) EncodeBit(b uint32, p *prob) error { bound := p.bound(e.nrange) if b&1 == 0 { e.nrange = bound p.inc() } else { e.low += uint64(bound) e.nrange -= bound p.dec() } // normalize const top = 1 << 24 if e.nrange >= top { return nil } e.nrange <<= 8 return e.shiftLow() } // Close writes a complete copy of the low value. func (e *rangeEncoder) Close() error { for i := 0; i < 5; i++ { if err := e.shiftLow(); err != nil { return err } } return nil } // shiftLow shifts the low value for 8 bit. The shifted byte is written into // the byte writer. The cache value is used to handle overflows. func (e *rangeEncoder) shiftLow() error { if uint32(e.low) < 0xff000000 || (e.low>>32) != 0 { tmp := e.cache for { err := e.writeByte(tmp + byte(e.low>>32)) if err != nil { return err } tmp = 0xff e.cacheLen-- if e.cacheLen <= 0 { if e.cacheLen < 0 { panic("negative cacheLen") } break } } e.cache = byte(uint32(e.low) >> 24) } e.cacheLen++ e.low = uint64(uint32(e.low) << 8) return nil } // rangeDecoder decodes single bits of the range encoding stream. type rangeDecoder struct { br io.ByteReader nrange uint32 code uint32 } // init initializes the range decoder, by reading from the byte reader. func (d *rangeDecoder) init() error { d.nrange = 0xffffffff d.code = 0 b, err := d.br.ReadByte() if err != nil { return err } if b != 0 { return errors.New("newRangeDecoder: first byte not zero") } for i := 0; i < 4; i++ { if err = d.updateCode(); err != nil { return err } } if d.code >= d.nrange { return errors.New("newRangeDecoder: d.code >= d.nrange") } return nil } // newRangeDecoder initializes a range decoder. It reads five bytes from the // reader and therefore may return an error. func newRangeDecoder(br io.ByteReader) (d *rangeDecoder, err error) { d = &rangeDecoder{br: br, nrange: 0xffffffff} b, err := d.br.ReadByte() if err != nil { return nil, err } if b != 0 { return nil, errors.New("newRangeDecoder: first byte not zero") } for i := 0; i < 4; i++ { if err = d.updateCode(); err != nil { return nil, err } } if d.code >= d.nrange { return nil, errors.New("newRangeDecoder: d.code >= d.nrange") } return d, nil } // possiblyAtEnd checks whether the decoder may be at the end of the stream. func (d *rangeDecoder) possiblyAtEnd() bool { return d.code == 0 } // DirectDecodeBit decodes a bit with probability 1/2. The return value b will // contain the bit at the least-significant position. All other bits will be // zero. func (d *rangeDecoder) DirectDecodeBit() (b uint32, err error) { d.nrange >>= 1 d.code -= d.nrange t := 0 - (d.code >> 31) d.code += d.nrange & t b = (t + 1) & 1 // d.code will stay less then d.nrange // normalize // assume d.code < d.nrange const top = 1 << 24 if d.nrange >= top { return b, nil } d.nrange <<= 8 // d.code < d.nrange will be maintained return b, d.updateCode() } // decodeBit decodes a single bit. The bit will be returned at the // least-significant position. All other bits will be zero. The probability // value will be updated. func (d *rangeDecoder) DecodeBit(p *prob) (b uint32, err error) { bound := p.bound(d.nrange) if d.code < bound { d.nrange = bound p.inc() b = 0 } else { d.code -= bound d.nrange -= bound p.dec() b = 1 } // normalize // assume d.code < d.nrange const top = 1 << 24 if d.nrange >= top { return b, nil } d.nrange <<= 8 // d.code < d.nrange will be maintained return b, d.updateCode() } // updateCode reads a new byte into the code. func (d *rangeDecoder) updateCode() error { b, err := d.br.ReadByte() if err != nil { return err } d.code = (d.code << 8) | uint32(b) return nil } ================================================ FILE: vendor/github.com/ulikunitz/xz/lzma/reader.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package lzma supports the decoding and encoding of LZMA streams. // Reader and Writer support the classic LZMA format. Reader2 and // Writer2 support the decoding and encoding of LZMA2 streams. // // The package is written completely in Go and doesn't rely on any external // library. package lzma import ( "errors" "io" ) // ReaderConfig stores the parameters for the reader of the classic LZMA // format. type ReaderConfig struct { DictCap int } // fill converts the zero values of the configuration to the default values. func (c *ReaderConfig) fill() { if c.DictCap == 0 { c.DictCap = 8 * 1024 * 1024 } } // Verify checks the reader configuration for errors. Zero values will // be replaced by default values. func (c *ReaderConfig) Verify() error { c.fill() if !(MinDictCap <= c.DictCap && int64(c.DictCap) <= MaxDictCap) { return errors.New("lzma: dictionary capacity is out of range") } return nil } // Reader provides a reader for LZMA files or streams. type Reader struct { lzma io.Reader h header d *decoder } // NewReader creates a new reader for an LZMA stream using the classic // format. NewReader reads and checks the header of the LZMA stream. func NewReader(lzma io.Reader) (r *Reader, err error) { return ReaderConfig{}.NewReader(lzma) } // NewReader creates a new reader for an LZMA stream in the classic // format. The function reads and verifies the the header of the LZMA // stream. func (c ReaderConfig) NewReader(lzma io.Reader) (r *Reader, err error) { if err = c.Verify(); err != nil { return nil, err } data := make([]byte, HeaderLen) if _, err := io.ReadFull(lzma, data); err != nil { if err == io.EOF { return nil, errors.New("lzma: unexpected EOF") } return nil, err } r = &Reader{lzma: lzma} if err = r.h.unmarshalBinary(data); err != nil { return nil, err } if r.h.dictCap < MinDictCap { return nil, errors.New("lzma: dictionary capacity too small") } dictCap := r.h.dictCap if c.DictCap > dictCap { dictCap = c.DictCap } state := newState(r.h.properties) dict, err := newDecoderDict(dictCap) if err != nil { return nil, err } r.d, err = newDecoder(ByteReader(lzma), state, dict, r.h.size) if err != nil { return nil, err } return r, nil } // EOSMarker indicates that an EOS marker has been encountered. func (r *Reader) EOSMarker() bool { return r.d.eosMarker } // Read returns uncompressed data. func (r *Reader) Read(p []byte) (n int, err error) { return r.d.Read(p) } ================================================ FILE: vendor/github.com/ulikunitz/xz/lzma/reader2.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package lzma import ( "errors" "io" "github.com/ulikunitz/xz/internal/xlog" ) // Reader2Config stores the parameters for the LZMA2 reader. // format. type Reader2Config struct { DictCap int } // fill converts the zero values of the configuration to the default values. func (c *Reader2Config) fill() { if c.DictCap == 0 { c.DictCap = 8 * 1024 * 1024 } } // Verify checks the reader configuration for errors. Zero configuration values // will be replaced by default values. func (c *Reader2Config) Verify() error { c.fill() if !(MinDictCap <= c.DictCap && int64(c.DictCap) <= MaxDictCap) { return errors.New("lzma: dictionary capacity is out of range") } return nil } // Reader2 supports the reading of LZMA2 chunk sequences. Note that the // first chunk should have a dictionary reset and the first compressed // chunk a properties reset. The chunk sequence may not be terminated by // an end-of-stream chunk. type Reader2 struct { r io.Reader err error dict *decoderDict ur *uncompressedReader decoder *decoder chunkReader io.Reader cstate chunkState ctype chunkType } // NewReader2 creates a reader for an LZMA2 chunk sequence. func NewReader2(lzma2 io.Reader) (r *Reader2, err error) { return Reader2Config{}.NewReader2(lzma2) } // NewReader2 creates an LZMA2 reader using the given configuration. func (c Reader2Config) NewReader2(lzma2 io.Reader) (r *Reader2, err error) { if err = c.Verify(); err != nil { return nil, err } r = &Reader2{r: lzma2, cstate: start} r.dict, err = newDecoderDict(c.DictCap) if err != nil { return nil, err } if err = r.startChunk(); err != nil { r.err = err } return r, nil } // uncompressed tests whether the chunk type specifies an uncompressed // chunk. func uncompressed(ctype chunkType) bool { return ctype == cU || ctype == cUD } // startChunk parses a new chunk. func (r *Reader2) startChunk() error { r.chunkReader = nil header, err := readChunkHeader(r.r) if err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } return err } xlog.Debugf("chunk header %v", header) if err = r.cstate.next(header.ctype); err != nil { return err } if r.cstate == stop { return io.EOF } if header.ctype == cUD || header.ctype == cLRND { r.dict.Reset() } size := int64(header.uncompressed) + 1 if uncompressed(header.ctype) { if r.ur != nil { r.ur.Reopen(r.r, size) } else { r.ur = newUncompressedReader(r.r, r.dict, size) } r.chunkReader = r.ur return nil } br := ByteReader(io.LimitReader(r.r, int64(header.compressed)+1)) if r.decoder == nil { state := newState(header.props) r.decoder, err = newDecoder(br, state, r.dict, size) if err != nil { return err } r.chunkReader = r.decoder return nil } switch header.ctype { case cLR: r.decoder.State.Reset() case cLRN, cLRND: r.decoder.State = newState(header.props) } err = r.decoder.Reopen(br, size) if err != nil { return err } r.chunkReader = r.decoder return nil } // Read reads data from the LZMA2 chunk sequence. func (r *Reader2) Read(p []byte) (n int, err error) { if r.err != nil { return 0, r.err } for n < len(p) { var k int k, err = r.chunkReader.Read(p[n:]) n += k if err != nil { if err == io.EOF { err = r.startChunk() if err == nil { continue } } r.err = err return n, err } if k == 0 { r.err = errors.New("lzma: Reader2 doesn't get data") return n, r.err } } return n, nil } // EOS returns whether the LZMA2 stream has been terminated by an // end-of-stream chunk. func (r *Reader2) EOS() bool { return r.cstate == stop } // uncompressedReader is used to read uncompressed chunks. type uncompressedReader struct { lr io.LimitedReader Dict *decoderDict eof bool err error } // newUncompressedReader initializes a new uncompressedReader. func newUncompressedReader(r io.Reader, dict *decoderDict, size int64) *uncompressedReader { ur := &uncompressedReader{ lr: io.LimitedReader{R: r, N: size}, Dict: dict, } return ur } // Reopen reinitializes an uncompressed reader. func (ur *uncompressedReader) Reopen(r io.Reader, size int64) { ur.err = nil ur.eof = false ur.lr = io.LimitedReader{R: r, N: size} } // fill reads uncompressed data into the dictionary. func (ur *uncompressedReader) fill() error { if !ur.eof { n, err := io.CopyN(ur.Dict, &ur.lr, int64(ur.Dict.Available())) if err != io.EOF { return err } ur.eof = true if n > 0 { return nil } } if ur.lr.N != 0 { return io.ErrUnexpectedEOF } return io.EOF } // Read reads uncompressed data from the limited reader. func (ur *uncompressedReader) Read(p []byte) (n int, err error) { if ur.err != nil { return 0, ur.err } for { var k int k, err = ur.Dict.Read(p[n:]) n += k if n >= len(p) { return n, nil } if err != nil { break } err = ur.fill() if err != nil { break } } ur.err = err return n, err } ================================================ FILE: vendor/github.com/ulikunitz/xz/lzma/state.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package lzma // states defines the overall state count const states = 12 // State maintains the full state of the operation encoding or decoding // process. type state struct { rep [4]uint32 isMatch [states << maxPosBits]prob isRepG0Long [states << maxPosBits]prob isRep [states]prob isRepG0 [states]prob isRepG1 [states]prob isRepG2 [states]prob litCodec literalCodec lenCodec lengthCodec repLenCodec lengthCodec distCodec distCodec state uint32 posBitMask uint32 Properties Properties } // initProbSlice initializes a slice of probabilities. func initProbSlice(p []prob) { for i := range p { p[i] = probInit } } // Reset sets all state information to the original values. func (s *state) Reset() { p := s.Properties *s = state{ Properties: p, // dict: s.dict, posBitMask: (uint32(1) << uint(p.PB)) - 1, } initProbSlice(s.isMatch[:]) initProbSlice(s.isRep[:]) initProbSlice(s.isRepG0[:]) initProbSlice(s.isRepG1[:]) initProbSlice(s.isRepG2[:]) initProbSlice(s.isRepG0Long[:]) s.litCodec.init(p.LC, p.LP) s.lenCodec.init() s.repLenCodec.init() s.distCodec.init() } // initState initializes the state. func initState(s *state, p Properties) { *s = state{Properties: p} s.Reset() } // newState creates a new state from the give Properties. func newState(p Properties) *state { s := &state{Properties: p} s.Reset() return s } // deepcopy initializes s as a deep copy of the source. func (s *state) deepcopy(src *state) { if s == src { return } s.rep = src.rep s.isMatch = src.isMatch s.isRepG0Long = src.isRepG0Long s.isRep = src.isRep s.isRepG0 = src.isRepG0 s.isRepG1 = src.isRepG1 s.isRepG2 = src.isRepG2 s.litCodec.deepcopy(&src.litCodec) s.lenCodec.deepcopy(&src.lenCodec) s.repLenCodec.deepcopy(&src.repLenCodec) s.distCodec.deepcopy(&src.distCodec) s.state = src.state s.posBitMask = src.posBitMask s.Properties = src.Properties } // cloneState creates a new clone of the give state. func cloneState(src *state) *state { s := new(state) s.deepcopy(src) return s } // updateStateLiteral updates the state for a literal. func (s *state) updateStateLiteral() { switch { case s.state < 4: s.state = 0 return case s.state < 10: s.state -= 3 return } s.state -= 6 } // updateStateMatch updates the state for a match. func (s *state) updateStateMatch() { if s.state < 7 { s.state = 7 } else { s.state = 10 } } // updateStateRep updates the state for a repetition. func (s *state) updateStateRep() { if s.state < 7 { s.state = 8 } else { s.state = 11 } } // updateStateShortRep updates the state for a short repetition. func (s *state) updateStateShortRep() { if s.state < 7 { s.state = 9 } else { s.state = 11 } } // states computes the states of the operation codec. func (s *state) states(dictHead int64) (state1, state2, posState uint32) { state1 = s.state posState = uint32(dictHead) & s.posBitMask state2 = (s.state << maxPosBits) | posState return } // litState computes the literal state. func (s *state) litState(prev byte, dictHead int64) uint32 { lp, lc := uint(s.Properties.LP), uint(s.Properties.LC) litState := ((uint32(dictHead) & ((1 << lp) - 1)) << lc) | (uint32(prev) >> (8 - lc)) return litState } ================================================ FILE: vendor/github.com/ulikunitz/xz/lzma/treecodecs.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package lzma // treeCodec encodes or decodes values with a fixed bit size. It is using a // tree of probability value. The root of the tree is the most-significant bit. type treeCodec struct { probTree } // makeTreeCodec makes a tree codec. The bits value must be inside the range // [1,32]. func makeTreeCodec(bits int) treeCodec { return treeCodec{makeProbTree(bits)} } // deepcopy initializes tc as a deep copy of the source. func (tc *treeCodec) deepcopy(src *treeCodec) { tc.probTree.deepcopy(&src.probTree) } // Encode uses the range encoder to encode a fixed-bit-size value. func (tc *treeCodec) Encode(e *rangeEncoder, v uint32) (err error) { m := uint32(1) for i := int(tc.bits) - 1; i >= 0; i-- { b := (v >> uint(i)) & 1 if err := e.EncodeBit(b, &tc.probs[m]); err != nil { return err } m = (m << 1) | b } return nil } // Decodes uses the range decoder to decode a fixed-bit-size value. Errors may // be caused by the range decoder. func (tc *treeCodec) Decode(d *rangeDecoder) (v uint32, err error) { m := uint32(1) for j := 0; j < int(tc.bits); j++ { b, err := d.DecodeBit(&tc.probs[m]) if err != nil { return 0, err } m = (m << 1) | b } return m - (1 << uint(tc.bits)), nil } // treeReverseCodec is another tree codec, where the least-significant bit is // the start of the probability tree. type treeReverseCodec struct { probTree } // deepcopy initializes the treeReverseCodec as a deep copy of the // source. func (tc *treeReverseCodec) deepcopy(src *treeReverseCodec) { tc.probTree.deepcopy(&src.probTree) } // makeTreeReverseCodec creates treeReverseCodec value. The bits argument must // be in the range [1,32]. func makeTreeReverseCodec(bits int) treeReverseCodec { return treeReverseCodec{makeProbTree(bits)} } // Encode uses range encoder to encode a fixed-bit-size value. The range // encoder may cause errors. func (tc *treeReverseCodec) Encode(v uint32, e *rangeEncoder) (err error) { m := uint32(1) for i := uint(0); i < uint(tc.bits); i++ { b := (v >> i) & 1 if err := e.EncodeBit(b, &tc.probs[m]); err != nil { return err } m = (m << 1) | b } return nil } // Decodes uses the range decoder to decode a fixed-bit-size value. Errors // returned by the range decoder will be returned. func (tc *treeReverseCodec) Decode(d *rangeDecoder) (v uint32, err error) { m := uint32(1) for j := uint(0); j < uint(tc.bits); j++ { b, err := d.DecodeBit(&tc.probs[m]) if err != nil { return 0, err } m = (m << 1) | b v |= b << j } return v, nil } // probTree stores enough probability values to be used by the treeEncode and // treeDecode methods of the range coder types. type probTree struct { probs []prob bits byte } // deepcopy initializes the probTree value as a deep copy of the source. func (t *probTree) deepcopy(src *probTree) { if t == src { return } t.probs = make([]prob, len(src.probs)) copy(t.probs, src.probs) t.bits = src.bits } // makeProbTree initializes a probTree structure. func makeProbTree(bits int) probTree { if !(1 <= bits && bits <= 32) { panic("bits outside of range [1,32]") } t := probTree{ bits: byte(bits), probs: make([]prob, 1< 0 { c.SizeInHeader = true } if !c.SizeInHeader { c.EOSMarker = true } } // Verify checks WriterConfig for errors. Verify will replace zero // values with default values. func (c *WriterConfig) Verify() error { c.fill() var err error if c == nil { return errors.New("lzma: WriterConfig is nil") } if c.Properties == nil { return errors.New("lzma: WriterConfig has no Properties set") } if err = c.Properties.verify(); err != nil { return err } if !(MinDictCap <= c.DictCap && int64(c.DictCap) <= MaxDictCap) { return errors.New("lzma: dictionary capacity is out of range") } if !(maxMatchLen <= c.BufSize) { return errors.New("lzma: lookahead buffer size too small") } if c.SizeInHeader { if c.Size < 0 { return errors.New("lzma: negative size not supported") } } else if !c.EOSMarker { return errors.New("lzma: EOS marker is required") } if err = c.Matcher.verify(); err != nil { return err } return nil } // header returns the header structure for this configuration. func (c *WriterConfig) header() header { h := header{ properties: *c.Properties, dictCap: c.DictCap, size: -1, } if c.SizeInHeader { h.size = c.Size } return h } // Writer writes an LZMA stream in the classic format. type Writer struct { h header bw io.ByteWriter buf *bufio.Writer e *encoder } // NewWriter creates a new LZMA writer for the classic format. The // method will write the header to the underlying stream. func (c WriterConfig) NewWriter(lzma io.Writer) (w *Writer, err error) { if err = c.Verify(); err != nil { return nil, err } w = &Writer{h: c.header()} var ok bool w.bw, ok = lzma.(io.ByteWriter) if !ok { w.buf = bufio.NewWriter(lzma) w.bw = w.buf } state := newState(w.h.properties) m, err := c.Matcher.new(w.h.dictCap) if err != nil { return nil, err } dict, err := newEncoderDict(w.h.dictCap, c.BufSize, m) if err != nil { return nil, err } var flags encoderFlags if c.EOSMarker { flags = eosMarker } if w.e, err = newEncoder(w.bw, state, dict, flags); err != nil { return nil, err } if err = w.writeHeader(); err != nil { return nil, err } return w, nil } // NewWriter creates a new LZMA writer using the classic format. The // function writes the header to the underlying stream. func NewWriter(lzma io.Writer) (w *Writer, err error) { return WriterConfig{}.NewWriter(lzma) } // writeHeader writes the LZMA header into the stream. func (w *Writer) writeHeader() error { data, err := w.h.marshalBinary() if err != nil { return err } _, err = w.bw.(io.Writer).Write(data) return err } // Write puts data into the Writer. func (w *Writer) Write(p []byte) (n int, err error) { if w.h.size >= 0 { m := w.h.size m -= w.e.Compressed() + int64(w.e.dict.Buffered()) if m < 0 { m = 0 } if m < int64(len(p)) { p = p[:m] err = ErrNoSpace } } var werr error if n, werr = w.e.Write(p); werr != nil { err = werr } return n, err } // Close closes the writer stream. It ensures that all data from the // buffer will be compressed and the LZMA stream will be finished. func (w *Writer) Close() error { if w.h.size >= 0 { n := w.e.Compressed() + int64(w.e.dict.Buffered()) if n != w.h.size { return errSize } } err := w.e.Close() if w.buf != nil { ferr := w.buf.Flush() if err == nil { err = ferr } } return err } ================================================ FILE: vendor/github.com/ulikunitz/xz/lzma/writer2.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package lzma import ( "bytes" "errors" "io" ) // Writer2Config is used to create a Writer2 using parameters. type Writer2Config struct { // The properties for the encoding. If the it is nil the value // {LC: 3, LP: 0, PB: 2} will be chosen. Properties *Properties // The capacity of the dictionary. If DictCap is zero, the value // 8 MiB will be chosen. DictCap int // Size of the lookahead buffer; value 0 indicates default size // 4096 BufSize int // Match algorithm Matcher MatchAlgorithm } // fill replaces zero values with default values. func (c *Writer2Config) fill() { if c.Properties == nil { c.Properties = &Properties{LC: 3, LP: 0, PB: 2} } if c.DictCap == 0 { c.DictCap = 8 * 1024 * 1024 } if c.BufSize == 0 { c.BufSize = 4096 } } // Verify checks the Writer2Config for correctness. Zero values will be // replaced by default values. func (c *Writer2Config) Verify() error { c.fill() var err error if c == nil { return errors.New("lzma: WriterConfig is nil") } if c.Properties == nil { return errors.New("lzma: WriterConfig has no Properties set") } if err = c.Properties.verify(); err != nil { return err } if !(MinDictCap <= c.DictCap && int64(c.DictCap) <= MaxDictCap) { return errors.New("lzma: dictionary capacity is out of range") } if !(maxMatchLen <= c.BufSize) { return errors.New("lzma: lookahead buffer size too small") } if c.Properties.LC+c.Properties.LP > 4 { return errors.New("lzma: sum of lc and lp exceeds 4") } if err = c.Matcher.verify(); err != nil { return err } return nil } // Writer2 supports the creation of an LZMA2 stream. But note that // written data is buffered, so call Flush or Close to write data to the // underlying writer. The Close method writes the end-of-stream marker // to the stream. So you may be able to concatenate the output of two // writers as long the output of the first writer has only been flushed // but not closed. // // Any change to the fields Properties, DictCap must be done before the // first call to Write, Flush or Close. type Writer2 struct { w io.Writer start *state encoder *encoder cstate chunkState ctype chunkType buf bytes.Buffer lbw LimitedByteWriter } // NewWriter2 creates an LZMA2 chunk sequence writer with the default // parameters and options. func NewWriter2(lzma2 io.Writer) (w *Writer2, err error) { return Writer2Config{}.NewWriter2(lzma2) } // NewWriter2 creates a new LZMA2 writer using the given configuration. func (c Writer2Config) NewWriter2(lzma2 io.Writer) (w *Writer2, err error) { if err = c.Verify(); err != nil { return nil, err } w = &Writer2{ w: lzma2, start: newState(*c.Properties), cstate: start, ctype: start.defaultChunkType(), } w.buf.Grow(maxCompressed) w.lbw = LimitedByteWriter{BW: &w.buf, N: maxCompressed} m, err := c.Matcher.new(c.DictCap) if err != nil { return nil, err } d, err := newEncoderDict(c.DictCap, c.BufSize, m) if err != nil { return nil, err } w.encoder, err = newEncoder(&w.lbw, cloneState(w.start), d, 0) if err != nil { return nil, err } return w, nil } // written returns the number of bytes written to the current chunk func (w *Writer2) written() int { if w.encoder == nil { return 0 } return int(w.encoder.Compressed()) + w.encoder.dict.Buffered() } // errClosed indicates that the writer is closed. var errClosed = errors.New("lzma: writer closed") // Writes data to LZMA2 stream. Note that written data will be buffered. // Use Flush or Close to ensure that data is written to the underlying // writer. func (w *Writer2) Write(p []byte) (n int, err error) { if w.cstate == stop { return 0, errClosed } for n < len(p) { m := maxUncompressed - w.written() if m <= 0 { panic("lzma: maxUncompressed reached") } var q []byte if n+m < len(p) { q = p[n : n+m] } else { q = p[n:] } k, err := w.encoder.Write(q) n += k if err != nil && err != ErrLimit { return n, err } if err == ErrLimit || k == m { if err = w.flushChunk(); err != nil { return n, err } } } return n, nil } // writeUncompressedChunk writes an uncompressed chunk to the LZMA2 // stream. func (w *Writer2) writeUncompressedChunk() error { u := w.encoder.Compressed() if u <= 0 { return errors.New("lzma: can't write empty uncompressed chunk") } if u > maxUncompressed { panic("overrun of uncompressed data limit") } switch w.ctype { case cLRND: w.ctype = cUD default: w.ctype = cU } w.encoder.state = w.start header := chunkHeader{ ctype: w.ctype, uncompressed: uint32(u - 1), } hdata, err := header.MarshalBinary() if err != nil { return err } if _, err = w.w.Write(hdata); err != nil { return err } _, err = w.encoder.dict.CopyN(w.w, int(u)) return err } // writeCompressedChunk writes a compressed chunk to the underlying // writer. func (w *Writer2) writeCompressedChunk() error { if w.ctype == cU || w.ctype == cUD { panic("chunk type uncompressed") } u := w.encoder.Compressed() if u <= 0 { return errors.New("writeCompressedChunk: empty chunk") } if u > maxUncompressed { panic("overrun of uncompressed data limit") } c := w.buf.Len() if c <= 0 { panic("no compressed data") } if c > maxCompressed { panic("overrun of compressed data limit") } header := chunkHeader{ ctype: w.ctype, uncompressed: uint32(u - 1), compressed: uint16(c - 1), props: w.encoder.state.Properties, } hdata, err := header.MarshalBinary() if err != nil { return err } if _, err = w.w.Write(hdata); err != nil { return err } _, err = io.Copy(w.w, &w.buf) return err } // writes a single chunk to the underlying writer. func (w *Writer2) writeChunk() error { u := int(uncompressedHeaderLen + w.encoder.Compressed()) c := headerLen(w.ctype) + w.buf.Len() if u < c { return w.writeUncompressedChunk() } return w.writeCompressedChunk() } // flushChunk terminates the current chunk. The encoder will be reset // to support the next chunk. func (w *Writer2) flushChunk() error { if w.written() == 0 { return nil } var err error if err = w.encoder.Close(); err != nil { return err } if err = w.writeChunk(); err != nil { return err } w.buf.Reset() w.lbw.N = maxCompressed if err = w.encoder.Reopen(&w.lbw); err != nil { return err } if err = w.cstate.next(w.ctype); err != nil { return err } w.ctype = w.cstate.defaultChunkType() w.start = cloneState(w.encoder.state) return nil } // Flush writes all buffered data out to the underlying stream. This // could result in multiple chunks to be created. func (w *Writer2) Flush() error { if w.cstate == stop { return errClosed } for w.written() > 0 { if err := w.flushChunk(); err != nil { return err } } return nil } // Close terminates the LZMA2 stream with an EOS chunk. func (w *Writer2) Close() error { if w.cstate == stop { return errClosed } if err := w.Flush(); err != nil { return nil } // write zero byte EOS chunk _, err := w.w.Write([]byte{0}) if err != nil { return err } w.cstate = stop return nil } ================================================ FILE: vendor/github.com/ulikunitz/xz/lzmafilter.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package xz import ( "errors" "fmt" "io" "github.com/ulikunitz/xz/lzma" ) // LZMA filter constants. const ( lzmaFilterID = 0x21 lzmaFilterLen = 3 ) // lzmaFilter declares the LZMA2 filter information stored in an xz // block header. type lzmaFilter struct { dictCap int64 } // String returns a representation of the LZMA filter. func (f lzmaFilter) String() string { return fmt.Sprintf("LZMA dict cap %#x", f.dictCap) } // id returns the ID for the LZMA2 filter. func (f lzmaFilter) id() uint64 { return lzmaFilterID } // MarshalBinary converts the lzmaFilter in its encoded representation. func (f lzmaFilter) MarshalBinary() (data []byte, err error) { c := lzma.EncodeDictCap(f.dictCap) return []byte{lzmaFilterID, 1, c}, nil } // UnmarshalBinary unmarshals the given data representation of the LZMA2 // filter. func (f *lzmaFilter) UnmarshalBinary(data []byte) error { if len(data) != lzmaFilterLen { return errors.New("xz: data for LZMA2 filter has wrong length") } if data[0] != lzmaFilterID { return errors.New("xz: wrong LZMA2 filter id") } if data[1] != 1 { return errors.New("xz: wrong LZMA2 filter size") } dc, err := lzma.DecodeDictCap(data[2]) if err != nil { return errors.New("xz: wrong LZMA2 dictionary size property") } f.dictCap = dc return nil } // reader creates a new reader for the LZMA2 filter. func (f lzmaFilter) reader(r io.Reader, c *ReaderConfig) (fr io.Reader, err error) { config := new(lzma.Reader2Config) if c != nil { config.DictCap = c.DictCap } dc := int(f.dictCap) if dc < 1 { return nil, errors.New("xz: LZMA2 filter parameter " + "dictionary capacity overflow") } if dc > config.DictCap { config.DictCap = dc } fr, err = config.NewReader2(r) if err != nil { return nil, err } return fr, nil } // writeCloser creates a io.WriteCloser for the LZMA2 filter. func (f lzmaFilter) writeCloser(w io.WriteCloser, c *WriterConfig, ) (fw io.WriteCloser, err error) { config := new(lzma.Writer2Config) if c != nil { *config = lzma.Writer2Config{ Properties: c.Properties, DictCap: c.DictCap, BufSize: c.BufSize, Matcher: c.Matcher, } } dc := int(f.dictCap) if dc < 1 { return nil, errors.New("xz: LZMA2 filter parameter " + "dictionary capacity overflow") } if dc > config.DictCap { config.DictCap = dc } fw, err = config.NewWriter2(w) if err != nil { return nil, err } return fw, nil } // last returns true, because an LZMA2 filter must be the last filter in // the filter list. func (f lzmaFilter) last() bool { return true } ================================================ FILE: vendor/github.com/ulikunitz/xz/make-docs ================================================ #!/bin/sh set -x pandoc -t html5 -f markdown -s --css=doc/md.css -o README.html README.md pandoc -t html5 -f markdown -s --css=doc/md.css -o TODO.html TODO.md ================================================ FILE: vendor/github.com/ulikunitz/xz/none-check.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package xz import "hash" type noneHash struct{} func (h noneHash) Write(p []byte) (n int, err error) { return len(p), nil } func (h noneHash) Sum(b []byte) []byte { return b } func (h noneHash) Reset() {} func (h noneHash) Size() int { return 0 } func (h noneHash) BlockSize() int { return 0 } func newNoneHash() hash.Hash { return &noneHash{} } ================================================ FILE: vendor/github.com/ulikunitz/xz/reader.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package xz supports the compression and decompression of xz files. It // supports version 1.0.4 of the specification without the non-LZMA2 // filters. See http://tukaani.org/xz/xz-file-format-1.0.4.txt package xz import ( "bytes" "errors" "fmt" "hash" "io" "github.com/ulikunitz/xz/internal/xlog" "github.com/ulikunitz/xz/lzma" ) // ReaderConfig defines the parameters for the xz reader. The // SingleStream parameter requests the reader to assume that the // underlying stream contains only a single stream. type ReaderConfig struct { DictCap int SingleStream bool } // fill replaces all zero values with their default values. func (c *ReaderConfig) fill() { if c.DictCap == 0 { c.DictCap = 8 * 1024 * 1024 } } // Verify checks the reader parameters for Validity. Zero values will be // replaced by default values. func (c *ReaderConfig) Verify() error { if c == nil { return errors.New("xz: reader parameters are nil") } lc := lzma.Reader2Config{DictCap: c.DictCap} if err := lc.Verify(); err != nil { return err } return nil } // Reader supports the reading of one or multiple xz streams. type Reader struct { ReaderConfig xz io.Reader sr *streamReader } // streamReader decodes a single xz stream type streamReader struct { ReaderConfig xz io.Reader br *blockReader newHash func() hash.Hash h header index []record } // NewReader creates a new xz reader using the default parameters. // The function reads and checks the header of the first XZ stream. The // reader will process multiple streams including padding. func NewReader(xz io.Reader) (r *Reader, err error) { return ReaderConfig{}.NewReader(xz) } // NewReader creates an xz stream reader. The created reader will be // able to process multiple streams and padding unless a SingleStream // has been set in the reader configuration c. func (c ReaderConfig) NewReader(xz io.Reader) (r *Reader, err error) { if err = c.Verify(); err != nil { return nil, err } r = &Reader{ ReaderConfig: c, xz: xz, } if r.sr, err = c.newStreamReader(xz); err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } return nil, err } return r, nil } var errUnexpectedData = errors.New("xz: unexpected data after stream") // Read reads uncompressed data from the stream. func (r *Reader) Read(p []byte) (n int, err error) { for n < len(p) { if r.sr == nil { if r.SingleStream { data := make([]byte, 1) _, err = io.ReadFull(r.xz, data) if err != io.EOF { return n, errUnexpectedData } return n, io.EOF } for { r.sr, err = r.ReaderConfig.newStreamReader(r.xz) if err != errPadding { break } } if err != nil { return n, err } } k, err := r.sr.Read(p[n:]) n += k if err != nil { if err == io.EOF { r.sr = nil continue } return n, err } } return n, nil } var errPadding = errors.New("xz: padding (4 zero bytes) encountered") // newStreamReader creates a new xz stream reader using the given configuration // parameters. NewReader reads and checks the header of the xz stream. func (c ReaderConfig) newStreamReader(xz io.Reader) (r *streamReader, err error) { if err = c.Verify(); err != nil { return nil, err } data := make([]byte, HeaderLen) if _, err := io.ReadFull(xz, data[:4]); err != nil { return nil, err } if bytes.Equal(data[:4], []byte{0, 0, 0, 0}) { return nil, errPadding } if _, err = io.ReadFull(xz, data[4:]); err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } return nil, err } r = &streamReader{ ReaderConfig: c, xz: xz, index: make([]record, 0, 4), } if err = r.h.UnmarshalBinary(data); err != nil { return nil, err } xlog.Debugf("xz header %s", r.h) if r.newHash, err = newHashFunc(r.h.flags); err != nil { return nil, err } return r, nil } // errIndex indicates an error with the xz file index. var errIndex = errors.New("xz: error in xz file index") // readTail reads the index body and the xz footer. func (r *streamReader) readTail() error { index, n, err := readIndexBody(r.xz) if err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } return err } if len(index) != len(r.index) { return fmt.Errorf("xz: index length is %d; want %d", len(index), len(r.index)) } for i, rec := range r.index { if rec != index[i] { return fmt.Errorf("xz: record %d is %v; want %v", i, rec, index[i]) } } p := make([]byte, footerLen) if _, err = io.ReadFull(r.xz, p); err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } return err } var f footer if err = f.UnmarshalBinary(p); err != nil { return err } xlog.Debugf("xz footer %s", f) if f.flags != r.h.flags { return errors.New("xz: footer flags incorrect") } if f.indexSize != int64(n)+1 { return errors.New("xz: index size in footer wrong") } return nil } // Read reads actual data from the xz stream. func (r *streamReader) Read(p []byte) (n int, err error) { for n < len(p) { if r.br == nil { bh, hlen, err := readBlockHeader(r.xz) if err != nil { if err == errIndexIndicator { if err = r.readTail(); err != nil { return n, err } return n, io.EOF } return n, err } xlog.Debugf("block %v", *bh) r.br, err = r.ReaderConfig.newBlockReader(r.xz, bh, hlen, r.newHash()) if err != nil { return n, err } } k, err := r.br.Read(p[n:]) n += k if err != nil { if err == io.EOF { r.index = append(r.index, r.br.record()) r.br = nil } else { return n, err } } } return n, nil } // countingReader is a reader that counts the bytes read. type countingReader struct { r io.Reader n int64 } // Read reads data from the wrapped reader and adds it to the n field. func (lr *countingReader) Read(p []byte) (n int, err error) { n, err = lr.r.Read(p) lr.n += int64(n) return n, err } // blockReader supports the reading of a block. type blockReader struct { lxz countingReader header *blockHeader headerLen int n int64 hash hash.Hash r io.Reader err error } // newBlockReader creates a new block reader. func (c *ReaderConfig) newBlockReader(xz io.Reader, h *blockHeader, hlen int, hash hash.Hash) (br *blockReader, err error) { br = &blockReader{ lxz: countingReader{r: xz}, header: h, headerLen: hlen, hash: hash, } fr, err := c.newFilterReader(&br.lxz, h.filters) if err != nil { return nil, err } if br.hash.Size() != 0 { br.r = io.TeeReader(fr, br.hash) } else { br.r = fr } return br, nil } // uncompressedSize returns the uncompressed size of the block. func (br *blockReader) uncompressedSize() int64 { return br.n } // compressedSize returns the compressed size of the block. func (br *blockReader) compressedSize() int64 { return br.lxz.n } // unpaddedSize computes the unpadded size for the block. func (br *blockReader) unpaddedSize() int64 { n := int64(br.headerLen) n += br.compressedSize() n += int64(br.hash.Size()) return n } // record returns the index record for the current block. func (br *blockReader) record() record { return record{br.unpaddedSize(), br.uncompressedSize()} } // errBlockSize indicates that the size of the block in the block header // is wrong. var errBlockSize = errors.New("xz: wrong uncompressed size for block") // Read reads data from the block. func (br *blockReader) Read(p []byte) (n int, err error) { n, err = br.r.Read(p) br.n += int64(n) u := br.header.uncompressedSize if u >= 0 && br.uncompressedSize() > u { return n, errors.New("xz: wrong uncompressed size for block") } c := br.header.compressedSize if c >= 0 && br.compressedSize() > c { return n, errors.New("xz: wrong compressed size for block") } if err != io.EOF { return n, err } if br.uncompressedSize() < u || br.compressedSize() < c { return n, io.ErrUnexpectedEOF } s := br.hash.Size() k := padLen(br.lxz.n) q := make([]byte, k+s, k+2*s) if _, err = io.ReadFull(br.lxz.r, q); err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } return n, err } if !allZeros(q[:k]) { return n, errors.New("xz: non-zero block padding") } checkSum := q[k:] computedSum := br.hash.Sum(checkSum[s:]) if !bytes.Equal(checkSum, computedSum) { return n, errors.New("xz: checksum error for block") } return n, io.EOF } func (c *ReaderConfig) newFilterReader(r io.Reader, f []filter) (fr io.Reader, err error) { if err = verifyFilters(f); err != nil { return nil, err } fr = r for i := len(f) - 1; i >= 0; i-- { fr, err = f[i].reader(fr, c) if err != nil { return nil, err } } return fr, nil } ================================================ FILE: vendor/github.com/ulikunitz/xz/writer.go ================================================ // Copyright 2014-2019 Ulrich Kunitz. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package xz import ( "errors" "hash" "io" "github.com/ulikunitz/xz/lzma" ) // WriterConfig describe the parameters for an xz writer. type WriterConfig struct { Properties *lzma.Properties DictCap int BufSize int BlockSize int64 // checksum method: CRC32, CRC64 or SHA256 (default: CRC64) CheckSum byte // Forces NoChecksum (default: false) NoCheckSum bool // match algorithm Matcher lzma.MatchAlgorithm } // fill replaces zero values with default values. func (c *WriterConfig) fill() { if c.Properties == nil { c.Properties = &lzma.Properties{LC: 3, LP: 0, PB: 2} } if c.DictCap == 0 { c.DictCap = 8 * 1024 * 1024 } if c.BufSize == 0 { c.BufSize = 4096 } if c.BlockSize == 0 { c.BlockSize = maxInt64 } if c.CheckSum == 0 { c.CheckSum = CRC64 } if c.NoCheckSum { c.CheckSum = None } } // Verify checks the configuration for errors. Zero values will be // replaced by default values. func (c *WriterConfig) Verify() error { if c == nil { return errors.New("xz: writer configuration is nil") } c.fill() lc := lzma.Writer2Config{ Properties: c.Properties, DictCap: c.DictCap, BufSize: c.BufSize, Matcher: c.Matcher, } if err := lc.Verify(); err != nil { return err } if c.BlockSize <= 0 { return errors.New("xz: block size out of range") } if err := verifyFlags(c.CheckSum); err != nil { return err } return nil } // filters creates the filter list for the given parameters. func (c *WriterConfig) filters() []filter { return []filter{&lzmaFilter{int64(c.DictCap)}} } // maxInt64 defines the maximum 64-bit signed integer. const maxInt64 = 1<<63 - 1 // verifyFilters checks the filter list for the length and the right // sequence of filters. func verifyFilters(f []filter) error { if len(f) == 0 { return errors.New("xz: no filters") } if len(f) > 4 { return errors.New("xz: more than four filters") } for _, g := range f[:len(f)-1] { if g.last() { return errors.New("xz: last filter is not last") } } if !f[len(f)-1].last() { return errors.New("xz: wrong last filter") } return nil } // newFilterWriteCloser converts a filter list into a WriteCloser that // can be used by a blockWriter. func (c *WriterConfig) newFilterWriteCloser(w io.Writer, f []filter) (fw io.WriteCloser, err error) { if err = verifyFilters(f); err != nil { return nil, err } fw = nopWriteCloser(w) for i := len(f) - 1; i >= 0; i-- { fw, err = f[i].writeCloser(fw, c) if err != nil { return nil, err } } return fw, nil } // nopWCloser implements a WriteCloser with a Close method not doing // anything. type nopWCloser struct { io.Writer } // Close returns nil and doesn't do anything else. func (c nopWCloser) Close() error { return nil } // nopWriteCloser converts the Writer into a WriteCloser with a Close // function that does nothing beside returning nil. func nopWriteCloser(w io.Writer) io.WriteCloser { return nopWCloser{w} } // Writer compresses data written to it. It is an io.WriteCloser. type Writer struct { WriterConfig xz io.Writer bw *blockWriter newHash func() hash.Hash h header index []record closed bool } // newBlockWriter creates a new block writer writes the header out. func (w *Writer) newBlockWriter() error { var err error w.bw, err = w.WriterConfig.newBlockWriter(w.xz, w.newHash()) if err != nil { return err } if err = w.bw.writeHeader(w.xz); err != nil { return err } return nil } // closeBlockWriter closes a block writer and records the sizes in the // index. func (w *Writer) closeBlockWriter() error { var err error if err = w.bw.Close(); err != nil { return err } w.index = append(w.index, w.bw.record()) return nil } // NewWriter creates a new xz writer using default parameters. func NewWriter(xz io.Writer) (w *Writer, err error) { return WriterConfig{}.NewWriter(xz) } // NewWriter creates a new Writer using the given configuration parameters. func (c WriterConfig) NewWriter(xz io.Writer) (w *Writer, err error) { if err = c.Verify(); err != nil { return nil, err } w = &Writer{ WriterConfig: c, xz: xz, h: header{c.CheckSum}, index: make([]record, 0, 4), } if w.newHash, err = newHashFunc(c.CheckSum); err != nil { return nil, err } data, err := w.h.MarshalBinary() if _, err = xz.Write(data); err != nil { return nil, err } if err = w.newBlockWriter(); err != nil { return nil, err } return w, nil } // Write compresses the uncompressed data provided. func (w *Writer) Write(p []byte) (n int, err error) { if w.closed { return 0, errClosed } for { k, err := w.bw.Write(p[n:]) n += k if err != errNoSpace { return n, err } if err = w.closeBlockWriter(); err != nil { return n, err } if err = w.newBlockWriter(); err != nil { return n, err } } } // Close closes the writer and adds the footer to the Writer. Close // doesn't close the underlying writer. func (w *Writer) Close() error { if w.closed { return errClosed } w.closed = true var err error if err = w.closeBlockWriter(); err != nil { return err } f := footer{flags: w.h.flags} if f.indexSize, err = writeIndex(w.xz, w.index); err != nil { return err } data, err := f.MarshalBinary() if err != nil { return err } if _, err = w.xz.Write(data); err != nil { return err } return nil } // countingWriter is a writer that counts all data written to it. type countingWriter struct { w io.Writer n int64 } // Write writes data to the countingWriter. func (cw *countingWriter) Write(p []byte) (n int, err error) { n, err = cw.w.Write(p) cw.n += int64(n) if err == nil && cw.n < 0 { return n, errors.New("xz: counter overflow") } return } // blockWriter is writes a single block. type blockWriter struct { cxz countingWriter // mw combines io.WriteCloser w and the hash. mw io.Writer w io.WriteCloser n int64 blockSize int64 closed bool headerLen int filters []filter hash hash.Hash } // newBlockWriter creates a new block writer. func (c *WriterConfig) newBlockWriter(xz io.Writer, hash hash.Hash) (bw *blockWriter, err error) { bw = &blockWriter{ cxz: countingWriter{w: xz}, blockSize: c.BlockSize, filters: c.filters(), hash: hash, } bw.w, err = c.newFilterWriteCloser(&bw.cxz, bw.filters) if err != nil { return nil, err } if bw.hash.Size() != 0 { bw.mw = io.MultiWriter(bw.w, bw.hash) } else { bw.mw = bw.w } return bw, nil } // writeHeader writes the header. If the function is called after Close // the commpressedSize and uncompressedSize fields will be filled. func (bw *blockWriter) writeHeader(w io.Writer) error { h := blockHeader{ compressedSize: -1, uncompressedSize: -1, filters: bw.filters, } if bw.closed { h.compressedSize = bw.compressedSize() h.uncompressedSize = bw.uncompressedSize() } data, err := h.MarshalBinary() if err != nil { return err } if _, err = w.Write(data); err != nil { return err } bw.headerLen = len(data) return nil } // compressed size returns the amount of data written to the underlying // stream. func (bw *blockWriter) compressedSize() int64 { return bw.cxz.n } // uncompressedSize returns the number of data written to the // blockWriter func (bw *blockWriter) uncompressedSize() int64 { return bw.n } // unpaddedSize returns the sum of the header length, the uncompressed // size of the block and the hash size. func (bw *blockWriter) unpaddedSize() int64 { if bw.headerLen <= 0 { panic("xz: block header not written") } n := int64(bw.headerLen) n += bw.compressedSize() n += int64(bw.hash.Size()) return n } // record returns the record for the current stream. Call Close before // calling this method. func (bw *blockWriter) record() record { return record{bw.unpaddedSize(), bw.uncompressedSize()} } var errClosed = errors.New("xz: writer already closed") var errNoSpace = errors.New("xz: no space") // Write writes uncompressed data to the block writer. func (bw *blockWriter) Write(p []byte) (n int, err error) { if bw.closed { return 0, errClosed } t := bw.blockSize - bw.n if int64(len(p)) > t { err = errNoSpace p = p[:t] } var werr error n, werr = bw.mw.Write(p) bw.n += int64(n) if werr != nil { return n, werr } return n, err } // Close closes the writer. func (bw *blockWriter) Close() error { if bw.closed { return errClosed } bw.closed = true if err := bw.w.Close(); err != nil { return err } s := bw.hash.Size() k := padLen(bw.cxz.n) p := make([]byte, k+s) bw.hash.Sum(p[k:k]) if _, err := bw.cxz.w.Write(p); err != nil { return err } return nil } ================================================ FILE: vendor/github.com/ulyssessouza/godotenv/.gitignore ================================================ .DS_Store ================================================ FILE: vendor/github.com/ulyssessouza/godotenv/LICENCE ================================================ Copyright (c) 2013 John Barton MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/ulyssessouza/godotenv/README.md ================================================ # GoDotEnv ![CI](https://github.com/joho/godotenv/workflows/CI/badge.svg) [![Go Report Card](https://goreportcard.com/badge/github.com/joho/godotenv)](https://goreportcard.com/report/github.com/joho/godotenv) A Go (golang) port of the Ruby dotenv project (which loads env vars from a .env file) From the original Library: > Storing configuration in the environment is one of the tenets of a twelve-factor app. Anything that is likely to change between deployment environments–such as resource handles for databases or credentials for external services–should be extracted from the code into environment variables. > > But it is not always practical to set environment variables on development machines or continuous integration servers where multiple projects are run. Dotenv load variables from a .env file into ENV when the environment is bootstrapped. It can be used as a library (for loading in env for your own daemons etc) or as a bin command. There is test coverage and CI for both linuxish and windows environments, but I make no guarantees about the bin version working on windows. ## Installation As a library ```shell go get github.com/joho/godotenv ``` or if you want to use it as a bin command ```shell go get github.com/joho/godotenv/cmd/godotenv ``` ## Usage Add your application configuration to your `.env` file in the root of your project: ```shell S3_BUCKET=YOURS3BUCKET SECRET_KEY=YOURSECRETKEYGOESHERE ``` Then in your Go app you can do something like ```go package main import ( "github.com/joho/godotenv" "log" "os" ) func main() { err := godotenv.Load() if err != nil { log.Fatal("Error loading .env file") } s3Bucket := os.Getenv("S3_BUCKET") secretKey := os.Getenv("SECRET_KEY") // now do something with s3 or whatever } ``` If you're even lazier than that, you can just take advantage of the autoload package which will read in `.env` on import ```go import _ "github.com/joho/godotenv/autoload" ``` While `.env` in the project root is the default, you don't have to be constrained, both examples below are 100% legit ```go _ = godotenv.Load("somerandomfile") _ = godotenv.Load("filenumberone.env", "filenumbertwo.env") ``` If you want to be really fancy with your env file you can do comments and exports (below is a valid env file) ```shell # I am a comment and that is OK SOME_VAR=someval FOO=BAR # comments at line end are OK too export BAR=BAZ ``` Or finally you can do YAML(ish) style ```yaml FOO: bar BAR: baz ``` as a final aside, if you don't want godotenv munging your env you can just get a map back instead ```go var myEnv map[string]string myEnv, err := godotenv.Read() s3Bucket := myEnv["S3_BUCKET"] ``` ... or from an `io.Reader` instead of a local file ```go reader := getRemoteFile() myEnv, err := godotenv.Parse(reader) ``` ... or from a `string` if you so desire ```go content := getRemoteFileContent() myEnv, err := godotenv.Unmarshal(content) ``` ### Precedence & Conventions Existing envs take precedence of envs that are loaded later. The [convention](https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use) for managing multiple environments (i.e. development, test, production) is to create an env named `{YOURAPP}_ENV` and load envs in this order: ```go env := os.Getenv("FOO_ENV") if "" == env { env = "development" } godotenv.Load(".env." + env + ".local") if "test" != env { godotenv.Load(".env.local") } godotenv.Load(".env." + env) godotenv.Load() // The Original .env ``` If you need to, you can also use `godotenv.Overload()` to defy this convention and overwrite existing envs instead of only supplanting them. Use with caution. ### Command Mode Assuming you've installed the command as above and you've got `$GOPATH/bin` in your `$PATH` ``` godotenv -f /some/path/to/.env some_command with some args ``` If you don't specify `-f` it will fall back on the default of loading `.env` in `PWD` ### Writing Env Files Godotenv can also write a map representing the environment to a correctly-formatted and escaped file ```go env, err := godotenv.Unmarshal("KEY=value") err := godotenv.Write(env, "./.env") ``` ... or to a string ```go env, err := godotenv.Unmarshal("KEY=value") content, err := godotenv.Marshal(env) ``` ## Contributing Contributions are most welcome! The parser itself is pretty stupidly naive and I wouldn't be surprised if it breaks with edge cases. *code changes without tests will not be accepted* 1. Fork it 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Added some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request ## Releases Releases should follow [Semver](http://semver.org/) though the first couple of releases are `v1` and `v1.1`. Use [annotated tags for all releases](https://github.com/joho/godotenv/issues/30). Example `git tag -a v1.2.1` ## CI Linux: [![Build Status](https://travis-ci.org/joho/godotenv.svg?branch=master)](https://travis-ci.org/joho/godotenv) Windows: [![Build status](https://ci.appveyor.com/api/projects/status/9v40vnfvvgde64u4)](https://ci.appveyor.com/project/joho/godotenv) ## Who? The original library [dotenv](https://github.com/bkeepers/dotenv) was written by [Brandon Keepers](http://opensoul.org/), and this port was done by [John Barton](https://johnbarton.co/) based off the tests/fixtures in the original library. ================================================ FILE: vendor/github.com/ulyssessouza/godotenv/godotenv.go ================================================ // Package godotenv is a go port of the ruby dotenv library (https://github.com/bkeepers/dotenv) // // Examples/readme can be found on the github page at https://github.com/joho/godotenv // // The TL;DR is that you make a .env file that looks something like // // SOME_ENV_VAR=somevalue // // and then in your go code you can call // // godotenv.Load() // // and all the env vars declared in .env will be available through os.Getenv("SOME_ENV_VAR") package godotenv import ( "bufio" "errors" "fmt" "io" "os" "os/exec" "regexp" "sort" "strconv" "strings" ) const doubleQuoteSpecialChars = "\\\n\r\"!$`" // LoadWithLookupFn will read your env file(s), lookup the inherited variables with `lookupFn` // // and load them into ENV for this process. // // Call this function as close as possible to the start of your program (ideally in main) // // If you call Load without any args it will default to loading .env in the current path // // You can otherwise tell it which files to load (there can be more than one) like // // godotenv.Load("fileone", "filetwo") // // It's important to note that it WILL NOT OVERRIDE an env variable that already exists - consider the .env file to set dev vars or sensible defaults func LoadWithLookupFn(lookupFn func(string) (string, bool), filenames ...string) (err error) { filenames = filenamesOrDefault(filenames) for _, filename := range filenames { err = loadFile(filename, false, lookupFn) if err != nil { return // return early on a spazout } } return } // Load will read your env file(s) and load them into ENV for this process. // // Call this function as close as possible to the start of your program (ideally in main) // // If you call Load without any args it will default to loading .env in the current path // // You can otherwise tell it which files to load (there can be more than one) like // // godotenv.Load("fileone", "filetwo") // // It's important to note that it WILL NOT OVERRIDE an env variable that already exists - consider the .env file to set dev vars or sensible defaults func Load(filenames ...string) (err error) { return LoadWithLookupFn(func(string) (string, bool){ return "", true }, filenames...) } // Overload will read your env file(s) and load them into ENV for this process. // // Call this function as close as possible to the start of your program (ideally in main) // // If you call Overload without any args it will default to loading .env in the current path // // You can otherwise tell it which files to load (there can be more than one) like // // godotenv.Overload("fileone", "filetwo") // // It's important to note this WILL OVERRIDE an env variable that already exists - consider the .env file to forcefilly set all vars. func Overload(filenames ...string) (err error) { filenames = filenamesOrDefault(filenames) for _, filename := range filenames { err = loadFile(filename, true, func(string) (string, bool) { return "", true }) if err != nil { return // return early on a spazout } } return } // Read all env (with same file loading semantics as Load) but return values as // a map rather than automatically writing values into env func Read(filenames ...string) (envMap map[string]string, err error) { filenames = filenamesOrDefault(filenames) envMap = make(map[string]string) for _, filename := range filenames { individualEnvMap, individualErr := readFile(filename, noLookupFn) if individualErr != nil { err = individualErr return // return early on a spazout } for key, value := range individualEnvMap { envMap[key] = value } } return } // ReadWithLookup all env (with same file loading semantics as Load) but return values as // a map rather than automatically writing values into env func ReadWithLookup(lookupFn func(string)(string, bool), filenames ...string) (envMap map[string]string, err error) { filenames = filenamesOrDefault(filenames) envMap = make(map[string]string) for _, filename := range filenames { individualEnvMap, individualErr := readFile(filename, lookupFn) if individualErr != nil { err = individualErr return // return early on a spazout } for key, value := range individualEnvMap { envMap[key] = value } } return } // ParseWithLookup reads an env file from io.Reader resolving variables with lookupFn, returning a map of keys and values. func ParseWithLookup(r io.Reader, lookupFn func(string)(string, bool)) (map[string]string, error) { envMap := make(map[string]string) var lines []string scanner := bufio.NewScanner(r) for scanner.Scan() { lines = append(lines, scanner.Text()) } if err := scanner.Err(); err != nil { return envMap, err } for _, fullLine := range lines { if !isIgnoredLine(fullLine) { var key, value string key, value, err := parseLine(fullLine, envMap, lookupFn) if err != nil { return envMap, err } if key == "" { continue } envMap[key] = value } } return envMap, nil } // Parse reads an env file from io.Reader, returning a map of keys and values. func Parse(r io.Reader) (map[string]string, error) { return ParseWithLookup(r, noLookupFn) } //Unmarshal reads an env file from a string, returning a map of keys and values. func Unmarshal(str string) (envMap map[string]string, err error) { return Parse(strings.NewReader(str)) } // Exec loads env vars from the specified filenames (empty map falls back to default) // then executes the cmd specified. // // Simply hooks up os.Stdin/err/out to the command and calls Run() // // If you want more fine grained control over your command it's recommended // that you use `Load()` or `Read()` and the `os/exec` package yourself. func Exec(filenames []string, cmd string, cmdArgs []string) error { Load(filenames...) command := exec.Command(cmd, cmdArgs...) command.Stdin = os.Stdin command.Stdout = os.Stdout command.Stderr = os.Stderr return command.Run() } // Write serializes the given environment and writes it to a file func Write(envMap map[string]string, filename string) error { content, err := Marshal(envMap) if err != nil { return err } file, err := os.Create(filename) if err != nil { return err } defer file.Close() _, err = file.WriteString(content + "\n") if err != nil { return err } file.Sync() return err } // Marshal outputs the given environment as a dotenv-formatted environment file. // Each line is in the format: KEY="VALUE" where VALUE is backslash-escaped. func Marshal(envMap map[string]string) (string, error) { lines := make([]string, 0, len(envMap)) for k, v := range envMap { if d, err := strconv.Atoi(v); err == nil { lines = append(lines, fmt.Sprintf(`%s=%d`, k, d)) } else { lines = append(lines, fmt.Sprintf(`%s="%s"`, k, doubleQuoteEscape(v))) } } sort.Strings(lines) return strings.Join(lines, "\n"), nil } func filenamesOrDefault(filenames []string) []string { if len(filenames) == 0 { return []string{".env"} } return filenames } func loadFile(filename string, overload bool, lookupFn func(string)(string, bool)) error { envMap, err := readFile(filename, lookupFn) if err != nil { return err } currentEnv := map[string]bool{} rawEnv := os.Environ() for _, rawEnvLine := range rawEnv { key := strings.Split(rawEnvLine, "=")[0] currentEnv[key] = true } for key, value := range envMap { if !currentEnv[key] || overload { os.Setenv(key, value) } } return nil } func readFile(filename string, lookupFn func(string)(string, bool)) (envMap map[string]string, err error) { file, err := os.Open(filename) if err != nil { return } defer file.Close() return ParseWithLookup(file, lookupFn) } var exportRegex = regexp.MustCompile(`^\s*(?:export\s+)?(.*?)\s*$`) func parseLine(line string, envMap map[string]string, lookupFn func(string)(string, bool)) (key string, value string, err error) { if len(line) == 0 { err = errors.New("zero length string") return } // ditch the comments (but keep quoted hashes) if strings.Contains(line, "#") { segmentsBetweenHashes := strings.Split(line, "#") quotesAreOpen := false var segmentsToKeep []string for _, segment := range segmentsBetweenHashes { if strings.Count(segment, "\"") == 1 || strings.Count(segment, "'") == 1 { if quotesAreOpen { quotesAreOpen = false segmentsToKeep = append(segmentsToKeep, segment) } else { quotesAreOpen = true } } if len(segmentsToKeep) == 0 || quotesAreOpen { segmentsToKeep = append(segmentsToKeep, segment) } } line = strings.Join(segmentsToKeep, "#") } firstEquals := strings.Index(line, "=") firstColon := strings.Index(line, ":") splitString := strings.SplitN(line, "=", 2) if firstColon != -1 && (firstColon < firstEquals || firstEquals == -1) { //this is a yaml-style line splitString = strings.SplitN(line, ":", 2) } // Parse the key key = strings.TrimSpace(strings.TrimPrefix(splitString[0], "export ")) key = exportRegex.ReplaceAllString(key, "$1") if err = validateVariableName(key); err != nil { return } // Environment inherited variable if firstEquals < 0 && firstColon < 0 { value = "" v, ok := lookupFn(strings.TrimSpace(key)) if ok { value = v } return } if len(splitString) != 2 { err = errors.New("Can't separate key from value") return } // Parse the value value = parseValue(splitString[1], envMap) return } func validateVariableName(key string) error { key = strings.TrimSpace(strings.TrimPrefix(key, "export ")) if !variableNameRegex.MatchString(key) { return fmt.Errorf("invalid variable name %q", key) } return nil } var ( singleQuotesRegex = regexp.MustCompile(`\A'(.*)'\z`) doubleQuotesRegex = regexp.MustCompile(`\A"(.*)"\z`) escapeRegex = regexp.MustCompile(`\\.`) unescapeCharsRegex = regexp.MustCompile(`\\([^$])`) variableNameRegex = regexp.MustCompile(`^[_\\.a-zA-Z0-9]+$`) noLookupFn = func(string)(string, bool) {return "", true} ) func parseValue(value string, envMap map[string]string) string { // trim value = strings.Trim(value, " ") // check if we've got quoted values or possible escapes if len(value) > 1 { singleQuotes := singleQuotesRegex.FindStringSubmatch(value) doubleQuotes := doubleQuotesRegex.FindStringSubmatch(value) if singleQuotes != nil || doubleQuotes != nil { // pull the quotes off the edges value = value[1 : len(value)-1] } if doubleQuotes != nil { // expand newlines value = escapeRegex.ReplaceAllStringFunc(value, func(match string) string { c := strings.TrimPrefix(match, `\`) switch c { case "n": return "\n" case "r": return "\r" default: return match } }) // unescape characters value = unescapeCharsRegex.ReplaceAllString(value, "$1") } if singleQuotes == nil { value = expandVariables(value, envMap) } } return value } var expandVarRegex = regexp.MustCompile(`(\\)?(\$)(\()?\{?([A-Z0-9_]+)?\}?`) func expandVariables(v string, m map[string]string) string { return expandVarRegex.ReplaceAllStringFunc(v, func(s string) string { submatch := expandVarRegex.FindStringSubmatch(s) if submatch == nil { return s } if submatch[1] == "\\" || submatch[2] == "(" { return submatch[0][1:] } else if submatch[4] != "" { return m[submatch[4]] } return s }) } func isIgnoredLine(line string) bool { trimmedLine := strings.TrimSpace(line) return len(trimmedLine) == 0 || strings.HasPrefix(trimmedLine, "#") } func doubleQuoteEscape(line string) string { for _, c := range doubleQuoteSpecialChars { toReplace := "\\" + string(c) if c == '\n' { toReplace = `\n` } if c == '\r' { toReplace = `\r` } line = strings.Replace(line, string(c), toReplace, -1) } return line } ================================================ FILE: vendor/github.com/ulyssessouza/godotenv/renovate.json ================================================ { "extends": [ "config:base" ] } ================================================ FILE: vendor/github.com/vbatts/tar-split/LICENSE ================================================ Copyright (c) 2015 Vincent Batts, Raleigh, NC, USA All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/github.com/vbatts/tar-split/archive/tar/common.go ================================================ // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package tar implements access to tar archives. // // Tape archives (tar) are a file format for storing a sequence of files that // can be read and written in a streaming manner. // This package aims to cover most variations of the format, // including those produced by GNU and BSD tar tools. package tar import ( "errors" "fmt" "math" "os" "path" "reflect" "strconv" "strings" "time" ) // BUG: Use of the Uid and Gid fields in Header could overflow on 32-bit // architectures. If a large value is encountered when decoding, the result // stored in Header will be the truncated version. var ( ErrHeader = errors.New("archive/tar: invalid tar header") ErrWriteTooLong = errors.New("archive/tar: write too long") ErrFieldTooLong = errors.New("archive/tar: header field too long") ErrWriteAfterClose = errors.New("archive/tar: write after close") errMissData = errors.New("archive/tar: sparse file references non-existent data") errUnrefData = errors.New("archive/tar: sparse file contains unreferenced data") errWriteHole = errors.New("archive/tar: write non-NUL byte in sparse hole") ) type headerError []string func (he headerError) Error() string { const prefix = "archive/tar: cannot encode header" var ss []string for _, s := range he { if s != "" { ss = append(ss, s) } } if len(ss) == 0 { return prefix } return fmt.Sprintf("%s: %v", prefix, strings.Join(ss, "; and ")) } // Type flags for Header.Typeflag. const ( // Type '0' indicates a regular file. TypeReg = '0' TypeRegA = '\x00' // Deprecated: Use TypeReg instead. // Type '1' to '6' are header-only flags and may not have a data body. TypeLink = '1' // Hard link TypeSymlink = '2' // Symbolic link TypeChar = '3' // Character device node TypeBlock = '4' // Block device node TypeDir = '5' // Directory TypeFifo = '6' // FIFO node // Type '7' is reserved. TypeCont = '7' // Type 'x' is used by the PAX format to store key-value records that // are only relevant to the next file. // This package transparently handles these types. TypeXHeader = 'x' // Type 'g' is used by the PAX format to store key-value records that // are relevant to all subsequent files. // This package only supports parsing and composing such headers, // but does not currently support persisting the global state across files. TypeXGlobalHeader = 'g' // Type 'S' indicates a sparse file in the GNU format. TypeGNUSparse = 'S' // Types 'L' and 'K' are used by the GNU format for a meta file // used to store the path or link name for the next file. // This package transparently handles these types. TypeGNULongName = 'L' TypeGNULongLink = 'K' ) // Keywords for PAX extended header records. const ( paxNone = "" // Indicates that no PAX key is suitable paxPath = "path" paxLinkpath = "linkpath" paxSize = "size" paxUid = "uid" paxGid = "gid" paxUname = "uname" paxGname = "gname" paxMtime = "mtime" paxAtime = "atime" paxCtime = "ctime" // Removed from later revision of PAX spec, but was valid paxCharset = "charset" // Currently unused paxComment = "comment" // Currently unused paxSchilyXattr = "SCHILY.xattr." // Keywords for GNU sparse files in a PAX extended header. paxGNUSparse = "GNU.sparse." paxGNUSparseNumBlocks = "GNU.sparse.numblocks" paxGNUSparseOffset = "GNU.sparse.offset" paxGNUSparseNumBytes = "GNU.sparse.numbytes" paxGNUSparseMap = "GNU.sparse.map" paxGNUSparseName = "GNU.sparse.name" paxGNUSparseMajor = "GNU.sparse.major" paxGNUSparseMinor = "GNU.sparse.minor" paxGNUSparseSize = "GNU.sparse.size" paxGNUSparseRealSize = "GNU.sparse.realsize" ) // basicKeys is a set of the PAX keys for which we have built-in support. // This does not contain "charset" or "comment", which are both PAX-specific, // so adding them as first-class features of Header is unlikely. // Users can use the PAXRecords field to set it themselves. var basicKeys = map[string]bool{ paxPath: true, paxLinkpath: true, paxSize: true, paxUid: true, paxGid: true, paxUname: true, paxGname: true, paxMtime: true, paxAtime: true, paxCtime: true, } // A Header represents a single header in a tar archive. // Some fields may not be populated. // // For forward compatibility, users that retrieve a Header from Reader.Next, // mutate it in some ways, and then pass it back to Writer.WriteHeader // should do so by creating a new Header and copying the fields // that they are interested in preserving. type Header struct { // Typeflag is the type of header entry. // The zero value is automatically promoted to either TypeReg or TypeDir // depending on the presence of a trailing slash in Name. Typeflag byte Name string // Name of file entry Linkname string // Target name of link (valid for TypeLink or TypeSymlink) Size int64 // Logical file size in bytes Mode int64 // Permission and mode bits Uid int // User ID of owner Gid int // Group ID of owner Uname string // User name of owner Gname string // Group name of owner // If the Format is unspecified, then Writer.WriteHeader rounds ModTime // to the nearest second and ignores the AccessTime and ChangeTime fields. // // To use AccessTime or ChangeTime, specify the Format as PAX or GNU. // To use sub-second resolution, specify the Format as PAX. ModTime time.Time // Modification time AccessTime time.Time // Access time (requires either PAX or GNU support) ChangeTime time.Time // Change time (requires either PAX or GNU support) Devmajor int64 // Major device number (valid for TypeChar or TypeBlock) Devminor int64 // Minor device number (valid for TypeChar or TypeBlock) // Xattrs stores extended attributes as PAX records under the // "SCHILY.xattr." namespace. // // The following are semantically equivalent: // h.Xattrs[key] = value // h.PAXRecords["SCHILY.xattr."+key] = value // // When Writer.WriteHeader is called, the contents of Xattrs will take // precedence over those in PAXRecords. // // Deprecated: Use PAXRecords instead. Xattrs map[string]string // PAXRecords is a map of PAX extended header records. // // User-defined records should have keys of the following form: // VENDOR.keyword // Where VENDOR is some namespace in all uppercase, and keyword may // not contain the '=' character (e.g., "GOLANG.pkg.version"). // The key and value should be non-empty UTF-8 strings. // // When Writer.WriteHeader is called, PAX records derived from the // other fields in Header take precedence over PAXRecords. PAXRecords map[string]string // Format specifies the format of the tar header. // // This is set by Reader.Next as a best-effort guess at the format. // Since the Reader liberally reads some non-compliant files, // it is possible for this to be FormatUnknown. // // If the format is unspecified when Writer.WriteHeader is called, // then it uses the first format (in the order of USTAR, PAX, GNU) // capable of encoding this Header (see Format). Format Format } // sparseEntry represents a Length-sized fragment at Offset in the file. type sparseEntry struct{ Offset, Length int64 } func (s sparseEntry) endOffset() int64 { return s.Offset + s.Length } // A sparse file can be represented as either a sparseDatas or a sparseHoles. // As long as the total size is known, they are equivalent and one can be // converted to the other form and back. The various tar formats with sparse // file support represent sparse files in the sparseDatas form. That is, they // specify the fragments in the file that has data, and treat everything else as // having zero bytes. As such, the encoding and decoding logic in this package // deals with sparseDatas. // // However, the external API uses sparseHoles instead of sparseDatas because the // zero value of sparseHoles logically represents a normal file (i.e., there are // no holes in it). On the other hand, the zero value of sparseDatas implies // that the file has no data in it, which is rather odd. // // As an example, if the underlying raw file contains the 10-byte data: // var compactFile = "abcdefgh" // // And the sparse map has the following entries: // var spd sparseDatas = []sparseEntry{ // {Offset: 2, Length: 5}, // Data fragment for 2..6 // {Offset: 18, Length: 3}, // Data fragment for 18..20 // } // var sph sparseHoles = []sparseEntry{ // {Offset: 0, Length: 2}, // Hole fragment for 0..1 // {Offset: 7, Length: 11}, // Hole fragment for 7..17 // {Offset: 21, Length: 4}, // Hole fragment for 21..24 // } // // Then the content of the resulting sparse file with a Header.Size of 25 is: // var sparseFile = "\x00"*2 + "abcde" + "\x00"*11 + "fgh" + "\x00"*4 type ( sparseDatas []sparseEntry sparseHoles []sparseEntry ) // validateSparseEntries reports whether sp is a valid sparse map. // It does not matter whether sp represents data fragments or hole fragments. func validateSparseEntries(sp []sparseEntry, size int64) bool { // Validate all sparse entries. These are the same checks as performed by // the BSD tar utility. if size < 0 { return false } var pre sparseEntry for _, cur := range sp { switch { case cur.Offset < 0 || cur.Length < 0: return false // Negative values are never okay case cur.Offset > math.MaxInt64-cur.Length: return false // Integer overflow with large length case cur.endOffset() > size: return false // Region extends beyond the actual size case pre.endOffset() > cur.Offset: return false // Regions cannot overlap and must be in order } pre = cur } return true } // alignSparseEntries mutates src and returns dst where each fragment's // starting offset is aligned up to the nearest block edge, and each // ending offset is aligned down to the nearest block edge. // // Even though the Go tar Reader and the BSD tar utility can handle entries // with arbitrary offsets and lengths, the GNU tar utility can only handle // offsets and lengths that are multiples of blockSize. func alignSparseEntries(src []sparseEntry, size int64) []sparseEntry { dst := src[:0] for _, s := range src { pos, end := s.Offset, s.endOffset() pos += blockPadding(+pos) // Round-up to nearest blockSize if end != size { end -= blockPadding(-end) // Round-down to nearest blockSize } if pos < end { dst = append(dst, sparseEntry{Offset: pos, Length: end - pos}) } } return dst } // invertSparseEntries converts a sparse map from one form to the other. // If the input is sparseHoles, then it will output sparseDatas and vice-versa. // The input must have been already validated. // // This function mutates src and returns a normalized map where: // * adjacent fragments are coalesced together // * only the last fragment may be empty // * the endOffset of the last fragment is the total size func invertSparseEntries(src []sparseEntry, size int64) []sparseEntry { dst := src[:0] var pre sparseEntry for _, cur := range src { if cur.Length == 0 { continue // Skip empty fragments } pre.Length = cur.Offset - pre.Offset if pre.Length > 0 { dst = append(dst, pre) // Only add non-empty fragments } pre.Offset = cur.endOffset() } pre.Length = size - pre.Offset // Possibly the only empty fragment return append(dst, pre) } // fileState tracks the number of logical (includes sparse holes) and physical // (actual in tar archive) bytes remaining for the current file. // // Invariant: LogicalRemaining >= PhysicalRemaining type fileState interface { LogicalRemaining() int64 PhysicalRemaining() int64 } // allowedFormats determines which formats can be used. // The value returned is the logical OR of multiple possible formats. // If the value is FormatUnknown, then the input Header cannot be encoded // and an error is returned explaining why. // // As a by-product of checking the fields, this function returns paxHdrs, which // contain all fields that could not be directly encoded. // A value receiver ensures that this method does not mutate the source Header. func (h Header) allowedFormats() (format Format, paxHdrs map[string]string, err error) { format = FormatUSTAR | FormatPAX | FormatGNU paxHdrs = make(map[string]string) var whyNoUSTAR, whyNoPAX, whyNoGNU string var preferPAX bool // Prefer PAX over USTAR verifyString := func(s string, size int, name, paxKey string) { // NUL-terminator is optional for path and linkpath. // Technically, it is required for uname and gname, // but neither GNU nor BSD tar checks for it. tooLong := len(s) > size allowLongGNU := paxKey == paxPath || paxKey == paxLinkpath if hasNUL(s) || (tooLong && !allowLongGNU) { whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%q", name, s) format.mustNotBe(FormatGNU) } if !isASCII(s) || tooLong { canSplitUSTAR := paxKey == paxPath if _, _, ok := splitUSTARPath(s); !canSplitUSTAR || !ok { whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%q", name, s) format.mustNotBe(FormatUSTAR) } if paxKey == paxNone { whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%q", name, s) format.mustNotBe(FormatPAX) } else { paxHdrs[paxKey] = s } } if v, ok := h.PAXRecords[paxKey]; ok && v == s { paxHdrs[paxKey] = v } } verifyNumeric := func(n int64, size int, name, paxKey string) { if !fitsInBase256(size, n) { whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%d", name, n) format.mustNotBe(FormatGNU) } if !fitsInOctal(size, n) { whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%d", name, n) format.mustNotBe(FormatUSTAR) if paxKey == paxNone { whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%d", name, n) format.mustNotBe(FormatPAX) } else { paxHdrs[paxKey] = strconv.FormatInt(n, 10) } } if v, ok := h.PAXRecords[paxKey]; ok && v == strconv.FormatInt(n, 10) { paxHdrs[paxKey] = v } } verifyTime := func(ts time.Time, size int, name, paxKey string) { if ts.IsZero() { return // Always okay } if !fitsInBase256(size, ts.Unix()) { whyNoGNU = fmt.Sprintf("GNU cannot encode %s=%v", name, ts) format.mustNotBe(FormatGNU) } isMtime := paxKey == paxMtime fitsOctal := fitsInOctal(size, ts.Unix()) if (isMtime && !fitsOctal) || !isMtime { whyNoUSTAR = fmt.Sprintf("USTAR cannot encode %s=%v", name, ts) format.mustNotBe(FormatUSTAR) } needsNano := ts.Nanosecond() != 0 if !isMtime || !fitsOctal || needsNano { preferPAX = true // USTAR may truncate sub-second measurements if paxKey == paxNone { whyNoPAX = fmt.Sprintf("PAX cannot encode %s=%v", name, ts) format.mustNotBe(FormatPAX) } else { paxHdrs[paxKey] = formatPAXTime(ts) } } if v, ok := h.PAXRecords[paxKey]; ok && v == formatPAXTime(ts) { paxHdrs[paxKey] = v } } // Check basic fields. var blk block v7 := blk.V7() ustar := blk.USTAR() gnu := blk.GNU() verifyString(h.Name, len(v7.Name()), "Name", paxPath) verifyString(h.Linkname, len(v7.LinkName()), "Linkname", paxLinkpath) verifyString(h.Uname, len(ustar.UserName()), "Uname", paxUname) verifyString(h.Gname, len(ustar.GroupName()), "Gname", paxGname) verifyNumeric(h.Mode, len(v7.Mode()), "Mode", paxNone) verifyNumeric(int64(h.Uid), len(v7.UID()), "Uid", paxUid) verifyNumeric(int64(h.Gid), len(v7.GID()), "Gid", paxGid) verifyNumeric(h.Size, len(v7.Size()), "Size", paxSize) verifyNumeric(h.Devmajor, len(ustar.DevMajor()), "Devmajor", paxNone) verifyNumeric(h.Devminor, len(ustar.DevMinor()), "Devminor", paxNone) verifyTime(h.ModTime, len(v7.ModTime()), "ModTime", paxMtime) verifyTime(h.AccessTime, len(gnu.AccessTime()), "AccessTime", paxAtime) verifyTime(h.ChangeTime, len(gnu.ChangeTime()), "ChangeTime", paxCtime) // Check for header-only types. var whyOnlyPAX, whyOnlyGNU string switch h.Typeflag { case TypeReg, TypeChar, TypeBlock, TypeFifo, TypeGNUSparse: // Exclude TypeLink and TypeSymlink, since they may reference directories. if strings.HasSuffix(h.Name, "/") { return FormatUnknown, nil, headerError{"filename may not have trailing slash"} } case TypeXHeader, TypeGNULongName, TypeGNULongLink: return FormatUnknown, nil, headerError{"cannot manually encode TypeXHeader, TypeGNULongName, or TypeGNULongLink headers"} case TypeXGlobalHeader: h2 := Header{Name: h.Name, Typeflag: h.Typeflag, Xattrs: h.Xattrs, PAXRecords: h.PAXRecords, Format: h.Format} if !reflect.DeepEqual(h, h2) { return FormatUnknown, nil, headerError{"only PAXRecords should be set for TypeXGlobalHeader"} } whyOnlyPAX = "only PAX supports TypeXGlobalHeader" format.mayOnlyBe(FormatPAX) } if !isHeaderOnlyType(h.Typeflag) && h.Size < 0 { return FormatUnknown, nil, headerError{"negative size on header-only type"} } // Check PAX records. if len(h.Xattrs) > 0 { for k, v := range h.Xattrs { paxHdrs[paxSchilyXattr+k] = v } whyOnlyPAX = "only PAX supports Xattrs" format.mayOnlyBe(FormatPAX) } if len(h.PAXRecords) > 0 { for k, v := range h.PAXRecords { switch _, exists := paxHdrs[k]; { case exists: continue // Do not overwrite existing records case h.Typeflag == TypeXGlobalHeader: paxHdrs[k] = v // Copy all records case !basicKeys[k] && !strings.HasPrefix(k, paxGNUSparse): paxHdrs[k] = v // Ignore local records that may conflict } } whyOnlyPAX = "only PAX supports PAXRecords" format.mayOnlyBe(FormatPAX) } for k, v := range paxHdrs { if !validPAXRecord(k, v) { return FormatUnknown, nil, headerError{fmt.Sprintf("invalid PAX record: %q", k+" = "+v)} } } // TODO(dsnet): Re-enable this when adding sparse support. // See https://golang.org/issue/22735 /* // Check sparse files. if len(h.SparseHoles) > 0 || h.Typeflag == TypeGNUSparse { if isHeaderOnlyType(h.Typeflag) { return FormatUnknown, nil, headerError{"header-only type cannot be sparse"} } if !validateSparseEntries(h.SparseHoles, h.Size) { return FormatUnknown, nil, headerError{"invalid sparse holes"} } if h.Typeflag == TypeGNUSparse { whyOnlyGNU = "only GNU supports TypeGNUSparse" format.mayOnlyBe(FormatGNU) } else { whyNoGNU = "GNU supports sparse files only with TypeGNUSparse" format.mustNotBe(FormatGNU) } whyNoUSTAR = "USTAR does not support sparse files" format.mustNotBe(FormatUSTAR) } */ // Check desired format. if wantFormat := h.Format; wantFormat != FormatUnknown { if wantFormat.has(FormatPAX) && !preferPAX { wantFormat.mayBe(FormatUSTAR) // PAX implies USTAR allowed too } format.mayOnlyBe(wantFormat) // Set union of formats allowed and format wanted } if format == FormatUnknown { switch h.Format { case FormatUSTAR: err = headerError{"Format specifies USTAR", whyNoUSTAR, whyOnlyPAX, whyOnlyGNU} case FormatPAX: err = headerError{"Format specifies PAX", whyNoPAX, whyOnlyGNU} case FormatGNU: err = headerError{"Format specifies GNU", whyNoGNU, whyOnlyPAX} default: err = headerError{whyNoUSTAR, whyNoPAX, whyNoGNU, whyOnlyPAX, whyOnlyGNU} } } return format, paxHdrs, err } // FileInfo returns an os.FileInfo for the Header. func (h *Header) FileInfo() os.FileInfo { return headerFileInfo{h} } // headerFileInfo implements os.FileInfo. type headerFileInfo struct { h *Header } func (fi headerFileInfo) Size() int64 { return fi.h.Size } func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() } func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime } func (fi headerFileInfo) Sys() interface{} { return fi.h } // Name returns the base name of the file. func (fi headerFileInfo) Name() string { if fi.IsDir() { return path.Base(path.Clean(fi.h.Name)) } return path.Base(fi.h.Name) } // Mode returns the permission and mode bits for the headerFileInfo. func (fi headerFileInfo) Mode() (mode os.FileMode) { // Set file permission bits. mode = os.FileMode(fi.h.Mode).Perm() // Set setuid, setgid and sticky bits. if fi.h.Mode&c_ISUID != 0 { mode |= os.ModeSetuid } if fi.h.Mode&c_ISGID != 0 { mode |= os.ModeSetgid } if fi.h.Mode&c_ISVTX != 0 { mode |= os.ModeSticky } // Set file mode bits; clear perm, setuid, setgid, and sticky bits. switch m := os.FileMode(fi.h.Mode) &^ 07777; m { case c_ISDIR: mode |= os.ModeDir case c_ISFIFO: mode |= os.ModeNamedPipe case c_ISLNK: mode |= os.ModeSymlink case c_ISBLK: mode |= os.ModeDevice case c_ISCHR: mode |= os.ModeDevice mode |= os.ModeCharDevice case c_ISSOCK: mode |= os.ModeSocket } switch fi.h.Typeflag { case TypeSymlink: mode |= os.ModeSymlink case TypeChar: mode |= os.ModeDevice mode |= os.ModeCharDevice case TypeBlock: mode |= os.ModeDevice case TypeDir: mode |= os.ModeDir case TypeFifo: mode |= os.ModeNamedPipe } return mode } // sysStat, if non-nil, populates h from system-dependent fields of fi. var sysStat func(fi os.FileInfo, h *Header) error const ( // Mode constants from the USTAR spec: // See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_06 c_ISUID = 04000 // Set uid c_ISGID = 02000 // Set gid c_ISVTX = 01000 // Save text (sticky bit) // Common Unix mode constants; these are not defined in any common tar standard. // Header.FileInfo understands these, but FileInfoHeader will never produce these. c_ISDIR = 040000 // Directory c_ISFIFO = 010000 // FIFO c_ISREG = 0100000 // Regular file c_ISLNK = 0120000 // Symbolic link c_ISBLK = 060000 // Block special file c_ISCHR = 020000 // Character special file c_ISSOCK = 0140000 // Socket ) // FileInfoHeader creates a partially-populated Header from fi. // If fi describes a symlink, FileInfoHeader records link as the link target. // If fi describes a directory, a slash is appended to the name. // // Since os.FileInfo's Name method only returns the base name of // the file it describes, it may be necessary to modify Header.Name // to provide the full path name of the file. func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) { if fi == nil { return nil, errors.New("archive/tar: FileInfo is nil") } fm := fi.Mode() h := &Header{ Name: fi.Name(), ModTime: fi.ModTime(), Mode: int64(fm.Perm()), // or'd with c_IS* constants later } switch { case fm.IsRegular(): h.Typeflag = TypeReg h.Size = fi.Size() case fi.IsDir(): h.Typeflag = TypeDir h.Name += "/" case fm&os.ModeSymlink != 0: h.Typeflag = TypeSymlink h.Linkname = link case fm&os.ModeDevice != 0: if fm&os.ModeCharDevice != 0 { h.Typeflag = TypeChar } else { h.Typeflag = TypeBlock } case fm&os.ModeNamedPipe != 0: h.Typeflag = TypeFifo case fm&os.ModeSocket != 0: return nil, fmt.Errorf("archive/tar: sockets not supported") default: return nil, fmt.Errorf("archive/tar: unknown file mode %v", fm) } if fm&os.ModeSetuid != 0 { h.Mode |= c_ISUID } if fm&os.ModeSetgid != 0 { h.Mode |= c_ISGID } if fm&os.ModeSticky != 0 { h.Mode |= c_ISVTX } // If possible, populate additional fields from OS-specific // FileInfo fields. if sys, ok := fi.Sys().(*Header); ok { // This FileInfo came from a Header (not the OS). Use the // original Header to populate all remaining fields. h.Uid = sys.Uid h.Gid = sys.Gid h.Uname = sys.Uname h.Gname = sys.Gname h.AccessTime = sys.AccessTime h.ChangeTime = sys.ChangeTime if sys.Xattrs != nil { h.Xattrs = make(map[string]string) for k, v := range sys.Xattrs { h.Xattrs[k] = v } } if sys.Typeflag == TypeLink { // hard link h.Typeflag = TypeLink h.Size = 0 h.Linkname = sys.Linkname } if sys.PAXRecords != nil { h.PAXRecords = make(map[string]string) for k, v := range sys.PAXRecords { h.PAXRecords[k] = v } } } if sysStat != nil { return h, sysStat(fi, h) } return h, nil } // isHeaderOnlyType checks if the given type flag is of the type that has no // data section even if a size is specified. func isHeaderOnlyType(flag byte) bool { switch flag { case TypeLink, TypeSymlink, TypeChar, TypeBlock, TypeDir, TypeFifo: return true default: return false } } func min(a, b int64) int64 { if a < b { return a } return b } ================================================ FILE: vendor/github.com/vbatts/tar-split/archive/tar/format.go ================================================ // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package tar import "strings" // Format represents the tar archive format. // // The original tar format was introduced in Unix V7. // Since then, there have been multiple competing formats attempting to // standardize or extend the V7 format to overcome its limitations. // The most common formats are the USTAR, PAX, and GNU formats, // each with their own advantages and limitations. // // The following table captures the capabilities of each format: // // | USTAR | PAX | GNU // ------------------+--------+-----------+---------- // Name | 256B | unlimited | unlimited // Linkname | 100B | unlimited | unlimited // Size | uint33 | unlimited | uint89 // Mode | uint21 | uint21 | uint57 // Uid/Gid | uint21 | unlimited | uint57 // Uname/Gname | 32B | unlimited | 32B // ModTime | uint33 | unlimited | int89 // AccessTime | n/a | unlimited | int89 // ChangeTime | n/a | unlimited | int89 // Devmajor/Devminor | uint21 | uint21 | uint57 // ------------------+--------+-----------+---------- // string encoding | ASCII | UTF-8 | binary // sub-second times | no | yes | no // sparse files | no | yes | yes // // The table's upper portion shows the Header fields, where each format reports // the maximum number of bytes allowed for each string field and // the integer type used to store each numeric field // (where timestamps are stored as the number of seconds since the Unix epoch). // // The table's lower portion shows specialized features of each format, // such as supported string encodings, support for sub-second timestamps, // or support for sparse files. // // The Writer currently provides no support for sparse files. type Format int // Constants to identify various tar formats. const ( // Deliberately hide the meaning of constants from public API. _ Format = (1 << iota) / 4 // Sequence of 0, 0, 1, 2, 4, 8, etc... // FormatUnknown indicates that the format is unknown. FormatUnknown // The format of the original Unix V7 tar tool prior to standardization. formatV7 // FormatUSTAR represents the USTAR header format defined in POSIX.1-1988. // // While this format is compatible with most tar readers, // the format has several limitations making it unsuitable for some usages. // Most notably, it cannot support sparse files, files larger than 8GiB, // filenames larger than 256 characters, and non-ASCII filenames. // // Reference: // http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_06 FormatUSTAR // FormatPAX represents the PAX header format defined in POSIX.1-2001. // // PAX extends USTAR by writing a special file with Typeflag TypeXHeader // preceding the original header. This file contains a set of key-value // records, which are used to overcome USTAR's shortcomings, in addition to // providing the ability to have sub-second resolution for timestamps. // // Some newer formats add their own extensions to PAX by defining their // own keys and assigning certain semantic meaning to the associated values. // For example, sparse file support in PAX is implemented using keys // defined by the GNU manual (e.g., "GNU.sparse.map"). // // Reference: // http://pubs.opengroup.org/onlinepubs/009695399/utilities/pax.html FormatPAX // FormatGNU represents the GNU header format. // // The GNU header format is older than the USTAR and PAX standards and // is not compatible with them. The GNU format supports // arbitrary file sizes, filenames of arbitrary encoding and length, // sparse files, and other features. // // It is recommended that PAX be chosen over GNU unless the target // application can only parse GNU formatted archives. // // Reference: // https://www.gnu.org/software/tar/manual/html_node/Standard.html FormatGNU // Schily's tar format, which is incompatible with USTAR. // This does not cover STAR extensions to the PAX format; these fall under // the PAX format. formatSTAR formatMax ) func (f Format) has(f2 Format) bool { return f&f2 != 0 } func (f *Format) mayBe(f2 Format) { *f |= f2 } func (f *Format) mayOnlyBe(f2 Format) { *f &= f2 } func (f *Format) mustNotBe(f2 Format) { *f &^= f2 } var formatNames = map[Format]string{ formatV7: "V7", FormatUSTAR: "USTAR", FormatPAX: "PAX", FormatGNU: "GNU", formatSTAR: "STAR", } func (f Format) String() string { var ss []string for f2 := Format(1); f2 < formatMax; f2 <<= 1 { if f.has(f2) { ss = append(ss, formatNames[f2]) } } switch len(ss) { case 0: return "" case 1: return ss[0] default: return "(" + strings.Join(ss, " | ") + ")" } } // Magics used to identify various formats. const ( magicGNU, versionGNU = "ustar ", " \x00" magicUSTAR, versionUSTAR = "ustar\x00", "00" trailerSTAR = "tar\x00" ) // Size constants from various tar specifications. const ( blockSize = 512 // Size of each block in a tar stream nameSize = 100 // Max length of the name field in USTAR format prefixSize = 155 // Max length of the prefix field in USTAR format ) // blockPadding computes the number of bytes needed to pad offset up to the // nearest block edge where 0 <= n < blockSize. func blockPadding(offset int64) (n int64) { return -offset & (blockSize - 1) } var zeroBlock block type block [blockSize]byte // Convert block to any number of formats. func (b *block) V7() *headerV7 { return (*headerV7)(b) } func (b *block) GNU() *headerGNU { return (*headerGNU)(b) } func (b *block) STAR() *headerSTAR { return (*headerSTAR)(b) } func (b *block) USTAR() *headerUSTAR { return (*headerUSTAR)(b) } func (b *block) Sparse() sparseArray { return (sparseArray)(b[:]) } // GetFormat checks that the block is a valid tar header based on the checksum. // It then attempts to guess the specific format based on magic values. // If the checksum fails, then FormatUnknown is returned. func (b *block) GetFormat() Format { // Verify checksum. var p parser value := p.parseOctal(b.V7().Chksum()) chksum1, chksum2 := b.ComputeChecksum() if p.err != nil || (value != chksum1 && value != chksum2) { return FormatUnknown } // Guess the magic values. magic := string(b.USTAR().Magic()) version := string(b.USTAR().Version()) trailer := string(b.STAR().Trailer()) switch { case magic == magicUSTAR && trailer == trailerSTAR: return formatSTAR case magic == magicUSTAR: return FormatUSTAR | FormatPAX case magic == magicGNU && version == versionGNU: return FormatGNU default: return formatV7 } } // SetFormat writes the magic values necessary for specified format // and then updates the checksum accordingly. func (b *block) SetFormat(format Format) { // Set the magic values. switch { case format.has(formatV7): // Do nothing. case format.has(FormatGNU): copy(b.GNU().Magic(), magicGNU) copy(b.GNU().Version(), versionGNU) case format.has(formatSTAR): copy(b.STAR().Magic(), magicUSTAR) copy(b.STAR().Version(), versionUSTAR) copy(b.STAR().Trailer(), trailerSTAR) case format.has(FormatUSTAR | FormatPAX): copy(b.USTAR().Magic(), magicUSTAR) copy(b.USTAR().Version(), versionUSTAR) default: panic("invalid format") } // Update checksum. // This field is special in that it is terminated by a NULL then space. var f formatter field := b.V7().Chksum() chksum, _ := b.ComputeChecksum() // Possible values are 256..128776 f.formatOctal(field[:7], chksum) // Never fails since 128776 < 262143 field[7] = ' ' } // ComputeChecksum computes the checksum for the header block. // POSIX specifies a sum of the unsigned byte values, but the Sun tar used // signed byte values. // We compute and return both. func (b *block) ComputeChecksum() (unsigned, signed int64) { for i, c := range b { if 148 <= i && i < 156 { c = ' ' // Treat the checksum field itself as all spaces. } unsigned += int64(c) signed += int64(int8(c)) } return unsigned, signed } // Reset clears the block with all zeros. func (b *block) Reset() { *b = block{} } type headerV7 [blockSize]byte func (h *headerV7) Name() []byte { return h[000:][:100] } func (h *headerV7) Mode() []byte { return h[100:][:8] } func (h *headerV7) UID() []byte { return h[108:][:8] } func (h *headerV7) GID() []byte { return h[116:][:8] } func (h *headerV7) Size() []byte { return h[124:][:12] } func (h *headerV7) ModTime() []byte { return h[136:][:12] } func (h *headerV7) Chksum() []byte { return h[148:][:8] } func (h *headerV7) TypeFlag() []byte { return h[156:][:1] } func (h *headerV7) LinkName() []byte { return h[157:][:100] } type headerGNU [blockSize]byte func (h *headerGNU) V7() *headerV7 { return (*headerV7)(h) } func (h *headerGNU) Magic() []byte { return h[257:][:6] } func (h *headerGNU) Version() []byte { return h[263:][:2] } func (h *headerGNU) UserName() []byte { return h[265:][:32] } func (h *headerGNU) GroupName() []byte { return h[297:][:32] } func (h *headerGNU) DevMajor() []byte { return h[329:][:8] } func (h *headerGNU) DevMinor() []byte { return h[337:][:8] } func (h *headerGNU) AccessTime() []byte { return h[345:][:12] } func (h *headerGNU) ChangeTime() []byte { return h[357:][:12] } func (h *headerGNU) Sparse() sparseArray { return (sparseArray)(h[386:][:24*4+1]) } func (h *headerGNU) RealSize() []byte { return h[483:][:12] } type headerSTAR [blockSize]byte func (h *headerSTAR) V7() *headerV7 { return (*headerV7)(h) } func (h *headerSTAR) Magic() []byte { return h[257:][:6] } func (h *headerSTAR) Version() []byte { return h[263:][:2] } func (h *headerSTAR) UserName() []byte { return h[265:][:32] } func (h *headerSTAR) GroupName() []byte { return h[297:][:32] } func (h *headerSTAR) DevMajor() []byte { return h[329:][:8] } func (h *headerSTAR) DevMinor() []byte { return h[337:][:8] } func (h *headerSTAR) Prefix() []byte { return h[345:][:131] } func (h *headerSTAR) AccessTime() []byte { return h[476:][:12] } func (h *headerSTAR) ChangeTime() []byte { return h[488:][:12] } func (h *headerSTAR) Trailer() []byte { return h[508:][:4] } type headerUSTAR [blockSize]byte func (h *headerUSTAR) V7() *headerV7 { return (*headerV7)(h) } func (h *headerUSTAR) Magic() []byte { return h[257:][:6] } func (h *headerUSTAR) Version() []byte { return h[263:][:2] } func (h *headerUSTAR) UserName() []byte { return h[265:][:32] } func (h *headerUSTAR) GroupName() []byte { return h[297:][:32] } func (h *headerUSTAR) DevMajor() []byte { return h[329:][:8] } func (h *headerUSTAR) DevMinor() []byte { return h[337:][:8] } func (h *headerUSTAR) Prefix() []byte { return h[345:][:155] } type sparseArray []byte func (s sparseArray) Entry(i int) sparseElem { return (sparseElem)(s[i*24:]) } func (s sparseArray) IsExtended() []byte { return s[24*s.MaxEntries():][:1] } func (s sparseArray) MaxEntries() int { return len(s) / 24 } type sparseElem []byte func (s sparseElem) Offset() []byte { return s[00:][:12] } func (s sparseElem) Length() []byte { return s[12:][:12] } ================================================ FILE: vendor/github.com/vbatts/tar-split/archive/tar/reader.go ================================================ // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package tar import ( "bytes" "io" "io/ioutil" "strconv" "strings" "time" ) // Reader provides sequential access to the contents of a tar archive. // Reader.Next advances to the next file in the archive (including the first), // and then Reader can be treated as an io.Reader to access the file's data. type Reader struct { r io.Reader pad int64 // Amount of padding (ignored) after current file entry curr fileReader // Reader for current file entry blk block // Buffer to use as temporary local storage // err is a persistent error. // It is only the responsibility of every exported method of Reader to // ensure that this error is sticky. err error RawAccounting bool // Whether to enable the access needed to reassemble the tar from raw bytes. Some performance/memory hit for this. rawBytes *bytes.Buffer // last raw bits } type fileReader interface { io.Reader fileState WriteTo(io.Writer) (int64, error) } // RawBytes accesses the raw bytes of the archive, apart from the file payload itself. // This includes the header and padding. // // # This call resets the current rawbytes buffer // // Only when RawAccounting is enabled, otherwise this returns nil func (tr *Reader) RawBytes() []byte { if !tr.RawAccounting { return nil } if tr.rawBytes == nil { tr.rawBytes = bytes.NewBuffer(nil) } defer tr.rawBytes.Reset() // if we've read them, then flush them. return tr.rawBytes.Bytes() } // NewReader creates a new Reader reading from r. func NewReader(r io.Reader) *Reader { return &Reader{r: r, curr: ®FileReader{r, 0}} } // Next advances to the next entry in the tar archive. // The Header.Size determines how many bytes can be read for the next file. // Any remaining data in the current file is automatically discarded. // // io.EOF is returned at the end of the input. func (tr *Reader) Next() (*Header, error) { if tr.err != nil { return nil, tr.err } hdr, err := tr.next() tr.err = err return hdr, err } func (tr *Reader) next() (*Header, error) { var paxHdrs map[string]string var gnuLongName, gnuLongLink string if tr.RawAccounting { if tr.rawBytes == nil { tr.rawBytes = bytes.NewBuffer(nil) } else { tr.rawBytes.Reset() } } // Externally, Next iterates through the tar archive as if it is a series of // files. Internally, the tar format often uses fake "files" to add meta // data that describes the next file. These meta data "files" should not // normally be visible to the outside. As such, this loop iterates through // one or more "header files" until it finds a "normal file". format := FormatUSTAR | FormatPAX | FormatGNU for { // Discard the remainder of the file and any padding. if err := discard(tr, tr.curr.PhysicalRemaining()); err != nil { return nil, err } n, err := tryReadFull(tr.r, tr.blk[:tr.pad]) if err != nil { return nil, err } if tr.RawAccounting { tr.rawBytes.Write(tr.blk[:n]) } tr.pad = 0 hdr, rawHdr, err := tr.readHeader() if err != nil { return nil, err } if err := tr.handleRegularFile(hdr); err != nil { return nil, err } format.mayOnlyBe(hdr.Format) // Check for PAX/GNU special headers and files. switch hdr.Typeflag { case TypeXHeader, TypeXGlobalHeader: format.mayOnlyBe(FormatPAX) paxHdrs, err = parsePAX(tr) if err != nil { return nil, err } if hdr.Typeflag == TypeXGlobalHeader { if err = mergePAX(hdr, paxHdrs); err != nil { return nil, err } return &Header{ Name: hdr.Name, Typeflag: hdr.Typeflag, Xattrs: hdr.Xattrs, PAXRecords: hdr.PAXRecords, Format: format, }, nil } continue // This is a meta header affecting the next header case TypeGNULongName, TypeGNULongLink: format.mayOnlyBe(FormatGNU) realname, err := ioutil.ReadAll(tr) if err != nil { return nil, err } if tr.RawAccounting { tr.rawBytes.Write(realname) } var p parser switch hdr.Typeflag { case TypeGNULongName: gnuLongName = p.parseString(realname) case TypeGNULongLink: gnuLongLink = p.parseString(realname) } continue // This is a meta header affecting the next header default: // The old GNU sparse format is handled here since it is technically // just a regular file with additional attributes. if err := mergePAX(hdr, paxHdrs); err != nil { return nil, err } if gnuLongName != "" { hdr.Name = gnuLongName } if gnuLongLink != "" { hdr.Linkname = gnuLongLink } if hdr.Typeflag == TypeRegA { if strings.HasSuffix(hdr.Name, "/") { hdr.Typeflag = TypeDir // Legacy archives use trailing slash for directories } else { hdr.Typeflag = TypeReg } } // The extended headers may have updated the size. // Thus, setup the regFileReader again after merging PAX headers. if err := tr.handleRegularFile(hdr); err != nil { return nil, err } // Sparse formats rely on being able to read from the logical data // section; there must be a preceding call to handleRegularFile. if err := tr.handleSparseFile(hdr, rawHdr); err != nil { return nil, err } // Set the final guess at the format. if format.has(FormatUSTAR) && format.has(FormatPAX) { format.mayOnlyBe(FormatUSTAR) } hdr.Format = format return hdr, nil // This is a file, so stop } } } // handleRegularFile sets up the current file reader and padding such that it // can only read the following logical data section. It will properly handle // special headers that contain no data section. func (tr *Reader) handleRegularFile(hdr *Header) error { nb := hdr.Size if isHeaderOnlyType(hdr.Typeflag) { nb = 0 } if nb < 0 { return ErrHeader } tr.pad = blockPadding(nb) tr.curr = ®FileReader{r: tr.r, nb: nb} return nil } // handleSparseFile checks if the current file is a sparse format of any type // and sets the curr reader appropriately. func (tr *Reader) handleSparseFile(hdr *Header, rawHdr *block) error { var spd sparseDatas var err error if hdr.Typeflag == TypeGNUSparse { spd, err = tr.readOldGNUSparseMap(hdr, rawHdr) } else { spd, err = tr.readGNUSparsePAXHeaders(hdr) } // If sp is non-nil, then this is a sparse file. // Note that it is possible for len(sp) == 0. if err == nil && spd != nil { if isHeaderOnlyType(hdr.Typeflag) || !validateSparseEntries(spd, hdr.Size) { return ErrHeader } sph := invertSparseEntries(spd, hdr.Size) tr.curr = &sparseFileReader{tr.curr, sph, 0} } return err } // readGNUSparsePAXHeaders checks the PAX headers for GNU sparse headers. // If they are found, then this function reads the sparse map and returns it. // This assumes that 0.0 headers have already been converted to 0.1 headers // by the PAX header parsing logic. func (tr *Reader) readGNUSparsePAXHeaders(hdr *Header) (sparseDatas, error) { // Identify the version of GNU headers. var is1x0 bool major, minor := hdr.PAXRecords[paxGNUSparseMajor], hdr.PAXRecords[paxGNUSparseMinor] switch { case major == "0" && (minor == "0" || minor == "1"): is1x0 = false case major == "1" && minor == "0": is1x0 = true case major != "" || minor != "": return nil, nil // Unknown GNU sparse PAX version case hdr.PAXRecords[paxGNUSparseMap] != "": is1x0 = false // 0.0 and 0.1 did not have explicit version records, so guess default: return nil, nil // Not a PAX format GNU sparse file. } hdr.Format.mayOnlyBe(FormatPAX) // Update hdr from GNU sparse PAX headers. if name := hdr.PAXRecords[paxGNUSparseName]; name != "" { hdr.Name = name } size := hdr.PAXRecords[paxGNUSparseSize] if size == "" { size = hdr.PAXRecords[paxGNUSparseRealSize] } if size != "" { n, err := strconv.ParseInt(size, 10, 64) if err != nil { return nil, ErrHeader } hdr.Size = n } // Read the sparse map according to the appropriate format. if is1x0 { return readGNUSparseMap1x0(tr.curr) } return readGNUSparseMap0x1(hdr.PAXRecords) } // mergePAX merges paxHdrs into hdr for all relevant fields of Header. func mergePAX(hdr *Header, paxHdrs map[string]string) (err error) { for k, v := range paxHdrs { if v == "" { continue // Keep the original USTAR value } var id64 int64 switch k { case paxPath: hdr.Name = v case paxLinkpath: hdr.Linkname = v case paxUname: hdr.Uname = v case paxGname: hdr.Gname = v case paxUid: id64, err = strconv.ParseInt(v, 10, 64) hdr.Uid = int(id64) // Integer overflow possible case paxGid: id64, err = strconv.ParseInt(v, 10, 64) hdr.Gid = int(id64) // Integer overflow possible case paxAtime: hdr.AccessTime, err = parsePAXTime(v) case paxMtime: hdr.ModTime, err = parsePAXTime(v) case paxCtime: hdr.ChangeTime, err = parsePAXTime(v) case paxSize: hdr.Size, err = strconv.ParseInt(v, 10, 64) default: if strings.HasPrefix(k, paxSchilyXattr) { if hdr.Xattrs == nil { hdr.Xattrs = make(map[string]string) } hdr.Xattrs[k[len(paxSchilyXattr):]] = v } } if err != nil { return ErrHeader } } hdr.PAXRecords = paxHdrs return nil } // parsePAX parses PAX headers. // If an extended header (type 'x') is invalid, ErrHeader is returned func parsePAX(r io.Reader) (map[string]string, error) { buf, err := ioutil.ReadAll(r) if err != nil { return nil, err } // leaving this function for io.Reader makes it more testable if tr, ok := r.(*Reader); ok && tr.RawAccounting { if _, err = tr.rawBytes.Write(buf); err != nil { return nil, err } } sbuf := string(buf) // For GNU PAX sparse format 0.0 support. // This function transforms the sparse format 0.0 headers into format 0.1 // headers since 0.0 headers were not PAX compliant. var sparseMap []string paxHdrs := make(map[string]string) for len(sbuf) > 0 { key, value, residual, err := parsePAXRecord(sbuf) if err != nil { return nil, ErrHeader } sbuf = residual switch key { case paxGNUSparseOffset, paxGNUSparseNumBytes: // Validate sparse header order and value. if (len(sparseMap)%2 == 0 && key != paxGNUSparseOffset) || (len(sparseMap)%2 == 1 && key != paxGNUSparseNumBytes) || strings.Contains(value, ",") { return nil, ErrHeader } sparseMap = append(sparseMap, value) default: paxHdrs[key] = value } } if len(sparseMap) > 0 { paxHdrs[paxGNUSparseMap] = strings.Join(sparseMap, ",") } return paxHdrs, nil } // readHeader reads the next block header and assumes that the underlying reader // is already aligned to a block boundary. It returns the raw block of the // header in case further processing is required. // // The err will be set to io.EOF only when one of the following occurs: // - Exactly 0 bytes are read and EOF is hit. // - Exactly 1 block of zeros is read and EOF is hit. // - At least 2 blocks of zeros are read. func (tr *Reader) readHeader() (*Header, *block, error) { // Two blocks of zero bytes marks the end of the archive. n, err := io.ReadFull(tr.r, tr.blk[:]) if tr.RawAccounting && (err == nil || err == io.EOF) { tr.rawBytes.Write(tr.blk[:n]) } if err != nil { return nil, nil, err // EOF is okay here; exactly 0 bytes read } if bytes.Equal(tr.blk[:], zeroBlock[:]) { n, err = io.ReadFull(tr.r, tr.blk[:]) if tr.RawAccounting && (err == nil || err == io.EOF) { tr.rawBytes.Write(tr.blk[:n]) } if err != nil { return nil, nil, err // EOF is okay here; exactly 1 block of zeros read } if bytes.Equal(tr.blk[:], zeroBlock[:]) { return nil, nil, io.EOF // normal EOF; exactly 2 block of zeros read } return nil, nil, ErrHeader // Zero block and then non-zero block } // Verify the header matches a known format. format := tr.blk.GetFormat() if format == FormatUnknown { return nil, nil, ErrHeader } var p parser hdr := new(Header) // Unpack the V7 header. v7 := tr.blk.V7() hdr.Typeflag = v7.TypeFlag()[0] hdr.Name = p.parseString(v7.Name()) hdr.Linkname = p.parseString(v7.LinkName()) hdr.Size = p.parseNumeric(v7.Size()) hdr.Mode = p.parseNumeric(v7.Mode()) hdr.Uid = int(p.parseNumeric(v7.UID())) hdr.Gid = int(p.parseNumeric(v7.GID())) hdr.ModTime = time.Unix(p.parseNumeric(v7.ModTime()), 0) // Unpack format specific fields. if format > formatV7 { ustar := tr.blk.USTAR() hdr.Uname = p.parseString(ustar.UserName()) hdr.Gname = p.parseString(ustar.GroupName()) hdr.Devmajor = p.parseNumeric(ustar.DevMajor()) hdr.Devminor = p.parseNumeric(ustar.DevMinor()) var prefix string switch { case format.has(FormatUSTAR | FormatPAX): hdr.Format = format ustar := tr.blk.USTAR() prefix = p.parseString(ustar.Prefix()) // For Format detection, check if block is properly formatted since // the parser is more liberal than what USTAR actually permits. notASCII := func(r rune) bool { return r >= 0x80 } if bytes.IndexFunc(tr.blk[:], notASCII) >= 0 { hdr.Format = FormatUnknown // Non-ASCII characters in block. } nul := func(b []byte) bool { return int(b[len(b)-1]) == 0 } if !(nul(v7.Size()) && nul(v7.Mode()) && nul(v7.UID()) && nul(v7.GID()) && nul(v7.ModTime()) && nul(ustar.DevMajor()) && nul(ustar.DevMinor())) { hdr.Format = FormatUnknown // Numeric fields must end in NUL } case format.has(formatSTAR): star := tr.blk.STAR() prefix = p.parseString(star.Prefix()) hdr.AccessTime = time.Unix(p.parseNumeric(star.AccessTime()), 0) hdr.ChangeTime = time.Unix(p.parseNumeric(star.ChangeTime()), 0) case format.has(FormatGNU): hdr.Format = format var p2 parser gnu := tr.blk.GNU() if b := gnu.AccessTime(); b[0] != 0 { hdr.AccessTime = time.Unix(p2.parseNumeric(b), 0) } if b := gnu.ChangeTime(); b[0] != 0 { hdr.ChangeTime = time.Unix(p2.parseNumeric(b), 0) } // Prior to Go1.8, the Writer had a bug where it would output // an invalid tar file in certain rare situations because the logic // incorrectly believed that the old GNU format had a prefix field. // This is wrong and leads to an output file that mangles the // atime and ctime fields, which are often left unused. // // In order to continue reading tar files created by former, buggy // versions of Go, we skeptically parse the atime and ctime fields. // If we are unable to parse them and the prefix field looks like // an ASCII string, then we fallback on the pre-Go1.8 behavior // of treating these fields as the USTAR prefix field. // // Note that this will not use the fallback logic for all possible // files generated by a pre-Go1.8 toolchain. If the generated file // happened to have a prefix field that parses as valid // atime and ctime fields (e.g., when they are valid octal strings), // then it is impossible to distinguish between an valid GNU file // and an invalid pre-Go1.8 file. // // See https://golang.org/issues/12594 // See https://golang.org/issues/21005 if p2.err != nil { hdr.AccessTime, hdr.ChangeTime = time.Time{}, time.Time{} ustar := tr.blk.USTAR() if s := p.parseString(ustar.Prefix()); isASCII(s) { prefix = s } hdr.Format = FormatUnknown // Buggy file is not GNU } } if len(prefix) > 0 { hdr.Name = prefix + "/" + hdr.Name } } return hdr, &tr.blk, p.err } // readOldGNUSparseMap reads the sparse map from the old GNU sparse format. // The sparse map is stored in the tar header if it's small enough. // If it's larger than four entries, then one or more extension headers are used // to store the rest of the sparse map. // // The Header.Size does not reflect the size of any extended headers used. // Thus, this function will read from the raw io.Reader to fetch extra headers. // This method mutates blk in the process. func (tr *Reader) readOldGNUSparseMap(hdr *Header, blk *block) (sparseDatas, error) { // Make sure that the input format is GNU. // Unfortunately, the STAR format also has a sparse header format that uses // the same type flag but has a completely different layout. if blk.GetFormat() != FormatGNU { return nil, ErrHeader } hdr.Format.mayOnlyBe(FormatGNU) var p parser hdr.Size = p.parseNumeric(blk.GNU().RealSize()) if p.err != nil { return nil, p.err } s := blk.GNU().Sparse() spd := make(sparseDatas, 0, s.MaxEntries()) for { for i := 0; i < s.MaxEntries(); i++ { // This termination condition is identical to GNU and BSD tar. if s.Entry(i).Offset()[0] == 0x00 { break // Don't return, need to process extended headers (even if empty) } offset := p.parseNumeric(s.Entry(i).Offset()) length := p.parseNumeric(s.Entry(i).Length()) if p.err != nil { return nil, p.err } spd = append(spd, sparseEntry{Offset: offset, Length: length}) } if s.IsExtended()[0] > 0 { // There are more entries. Read an extension header and parse its entries. if _, err := mustReadFull(tr.r, blk[:]); err != nil { return nil, err } if tr.RawAccounting { tr.rawBytes.Write(blk[:]) } s = blk.Sparse() continue } return spd, nil // Done } } // readGNUSparseMap1x0 reads the sparse map as stored in GNU's PAX sparse format // version 1.0. The format of the sparse map consists of a series of // newline-terminated numeric fields. The first field is the number of entries // and is always present. Following this are the entries, consisting of two // fields (offset, length). This function must stop reading at the end // boundary of the block containing the last newline. // // Note that the GNU manual says that numeric values should be encoded in octal // format. However, the GNU tar utility itself outputs these values in decimal. // As such, this library treats values as being encoded in decimal. func readGNUSparseMap1x0(r io.Reader) (sparseDatas, error) { var ( cntNewline int64 buf bytes.Buffer blk block ) // feedTokens copies data in blocks from r into buf until there are // at least cnt newlines in buf. It will not read more blocks than needed. feedTokens := func(n int64) error { for cntNewline < n { if _, err := mustReadFull(r, blk[:]); err != nil { return err } buf.Write(blk[:]) for _, c := range blk { if c == '\n' { cntNewline++ } } } return nil } // nextToken gets the next token delimited by a newline. This assumes that // at least one newline exists in the buffer. nextToken := func() string { cntNewline-- tok, _ := buf.ReadString('\n') return strings.TrimRight(tok, "\n") } // Parse for the number of entries. // Use integer overflow resistant math to check this. if err := feedTokens(1); err != nil { return nil, err } numEntries, err := strconv.ParseInt(nextToken(), 10, 0) // Intentionally parse as native int if err != nil || numEntries < 0 || int(2*numEntries) < int(numEntries) { return nil, ErrHeader } // Parse for all member entries. // numEntries is trusted after this since a potential attacker must have // committed resources proportional to what this library used. if err := feedTokens(2 * numEntries); err != nil { return nil, err } spd := make(sparseDatas, 0, numEntries) for i := int64(0); i < numEntries; i++ { offset, err1 := strconv.ParseInt(nextToken(), 10, 64) length, err2 := strconv.ParseInt(nextToken(), 10, 64) if err1 != nil || err2 != nil { return nil, ErrHeader } spd = append(spd, sparseEntry{Offset: offset, Length: length}) } return spd, nil } // readGNUSparseMap0x1 reads the sparse map as stored in GNU's PAX sparse format // version 0.1. The sparse map is stored in the PAX headers. func readGNUSparseMap0x1(paxHdrs map[string]string) (sparseDatas, error) { // Get number of entries. // Use integer overflow resistant math to check this. numEntriesStr := paxHdrs[paxGNUSparseNumBlocks] numEntries, err := strconv.ParseInt(numEntriesStr, 10, 0) // Intentionally parse as native int if err != nil || numEntries < 0 || int(2*numEntries) < int(numEntries) { return nil, ErrHeader } // There should be two numbers in sparseMap for each entry. sparseMap := strings.Split(paxHdrs[paxGNUSparseMap], ",") if len(sparseMap) == 1 && sparseMap[0] == "" { sparseMap = sparseMap[:0] } if int64(len(sparseMap)) != 2*numEntries { return nil, ErrHeader } // Loop through the entries in the sparse map. // numEntries is trusted now. spd := make(sparseDatas, 0, numEntries) for len(sparseMap) >= 2 { offset, err1 := strconv.ParseInt(sparseMap[0], 10, 64) length, err2 := strconv.ParseInt(sparseMap[1], 10, 64) if err1 != nil || err2 != nil { return nil, ErrHeader } spd = append(spd, sparseEntry{Offset: offset, Length: length}) sparseMap = sparseMap[2:] } return spd, nil } // Read reads from the current file in the tar archive. // It returns (0, io.EOF) when it reaches the end of that file, // until Next is called to advance to the next file. // // If the current file is sparse, then the regions marked as a hole // are read back as NUL-bytes. // // Calling Read on special types like TypeLink, TypeSymlink, TypeChar, // TypeBlock, TypeDir, and TypeFifo returns (0, io.EOF) regardless of what // the Header.Size claims. func (tr *Reader) Read(b []byte) (int, error) { if tr.err != nil { return 0, tr.err } n, err := tr.curr.Read(b) if err != nil && err != io.EOF { tr.err = err } return n, err } // writeTo writes the content of the current file to w. // The bytes written matches the number of remaining bytes in the current file. // // If the current file is sparse and w is an io.WriteSeeker, // then writeTo uses Seek to skip past holes defined in Header.SparseHoles, // assuming that skipped regions are filled with NULs. // This always writes the last byte to ensure w is the right size. // // TODO(dsnet): Re-export this when adding sparse file support. // See https://golang.org/issue/22735 func (tr *Reader) writeTo(w io.Writer) (int64, error) { if tr.err != nil { return 0, tr.err } n, err := tr.curr.WriteTo(w) if err != nil { tr.err = err } return n, err } // regFileReader is a fileReader for reading data from a regular file entry. type regFileReader struct { r io.Reader // Underlying Reader nb int64 // Number of remaining bytes to read } func (fr *regFileReader) Read(b []byte) (n int, err error) { if int64(len(b)) > fr.nb { b = b[:fr.nb] } if len(b) > 0 { n, err = fr.r.Read(b) fr.nb -= int64(n) } switch { case err == io.EOF && fr.nb > 0: return n, io.ErrUnexpectedEOF case err == nil && fr.nb == 0: return n, io.EOF default: return n, err } } func (fr *regFileReader) WriteTo(w io.Writer) (int64, error) { return io.Copy(w, struct{ io.Reader }{fr}) } func (fr regFileReader) LogicalRemaining() int64 { return fr.nb } func (fr regFileReader) PhysicalRemaining() int64 { return fr.nb } // sparseFileReader is a fileReader for reading data from a sparse file entry. type sparseFileReader struct { fr fileReader // Underlying fileReader sp sparseHoles // Normalized list of sparse holes pos int64 // Current position in sparse file } func (sr *sparseFileReader) Read(b []byte) (n int, err error) { finished := int64(len(b)) >= sr.LogicalRemaining() if finished { b = b[:sr.LogicalRemaining()] } b0 := b endPos := sr.pos + int64(len(b)) for endPos > sr.pos && err == nil { var nf int // Bytes read in fragment holeStart, holeEnd := sr.sp[0].Offset, sr.sp[0].endOffset() if sr.pos < holeStart { // In a data fragment bf := b[:min(int64(len(b)), holeStart-sr.pos)] nf, err = tryReadFull(sr.fr, bf) } else { // In a hole fragment bf := b[:min(int64(len(b)), holeEnd-sr.pos)] nf, err = tryReadFull(zeroReader{}, bf) } b = b[nf:] sr.pos += int64(nf) if sr.pos >= holeEnd && len(sr.sp) > 1 { sr.sp = sr.sp[1:] // Ensure last fragment always remains } } n = len(b0) - len(b) switch { case err == io.EOF: return n, errMissData // Less data in dense file than sparse file case err != nil: return n, err case sr.LogicalRemaining() == 0 && sr.PhysicalRemaining() > 0: return n, errUnrefData // More data in dense file than sparse file case finished: return n, io.EOF default: return n, nil } } func (sr *sparseFileReader) WriteTo(w io.Writer) (n int64, err error) { ws, ok := w.(io.WriteSeeker) if ok { if _, err := ws.Seek(0, io.SeekCurrent); err != nil { ok = false // Not all io.Seeker can really seek } } if !ok { return io.Copy(w, struct{ io.Reader }{sr}) } var writeLastByte bool pos0 := sr.pos for sr.LogicalRemaining() > 0 && !writeLastByte && err == nil { var nf int64 // Size of fragment holeStart, holeEnd := sr.sp[0].Offset, sr.sp[0].endOffset() if sr.pos < holeStart { // In a data fragment nf = holeStart - sr.pos nf, err = io.CopyN(ws, sr.fr, nf) } else { // In a hole fragment nf = holeEnd - sr.pos if sr.PhysicalRemaining() == 0 { writeLastByte = true nf-- } _, err = ws.Seek(nf, io.SeekCurrent) } sr.pos += nf if sr.pos >= holeEnd && len(sr.sp) > 1 { sr.sp = sr.sp[1:] // Ensure last fragment always remains } } // If the last fragment is a hole, then seek to 1-byte before EOF, and // write a single byte to ensure the file is the right size. if writeLastByte && err == nil { _, err = ws.Write([]byte{0}) sr.pos++ } n = sr.pos - pos0 switch { case err == io.EOF: return n, errMissData // Less data in dense file than sparse file case err != nil: return n, err case sr.LogicalRemaining() == 0 && sr.PhysicalRemaining() > 0: return n, errUnrefData // More data in dense file than sparse file default: return n, nil } } func (sr sparseFileReader) LogicalRemaining() int64 { return sr.sp[len(sr.sp)-1].endOffset() - sr.pos } func (sr sparseFileReader) PhysicalRemaining() int64 { return sr.fr.PhysicalRemaining() } type zeroReader struct{} func (zeroReader) Read(b []byte) (int, error) { for i := range b { b[i] = 0 } return len(b), nil } // mustReadFull is like io.ReadFull except it returns // io.ErrUnexpectedEOF when io.EOF is hit before len(b) bytes are read. func mustReadFull(r io.Reader, b []byte) (int, error) { n, err := tryReadFull(r, b) if err == io.EOF { err = io.ErrUnexpectedEOF } return n, err } // tryReadFull is like io.ReadFull except it returns // io.EOF when it is hit before len(b) bytes are read. func tryReadFull(r io.Reader, b []byte) (n int, err error) { for len(b) > n && err == nil { var nn int nn, err = r.Read(b[n:]) n += nn } if len(b) == n && err == io.EOF { err = nil } return n, err } // discard skips n bytes in r, reporting an error if unable to do so. func discard(tr *Reader, n int64) error { var seekSkipped, copySkipped int64 var err error r := tr.r if tr.RawAccounting { copySkipped, err = io.CopyN(tr.rawBytes, tr.r, n) goto out } // If possible, Seek to the last byte before the end of the data section. // Do this because Seek is often lazy about reporting errors; this will mask // the fact that the stream may be truncated. We can rely on the // io.CopyN done shortly afterwards to trigger any IO errors. if sr, ok := r.(io.Seeker); ok && n > 1 { // Not all io.Seeker can actually Seek. For example, os.Stdin implements // io.Seeker, but calling Seek always returns an error and performs // no action. Thus, we try an innocent seek to the current position // to see if Seek is really supported. pos1, err := sr.Seek(0, io.SeekCurrent) if pos1 >= 0 && err == nil { // Seek seems supported, so perform the real Seek. pos2, err := sr.Seek(n-1, io.SeekCurrent) if pos2 < 0 || err != nil { return err } seekSkipped = pos2 - pos1 } } copySkipped, err = io.CopyN(ioutil.Discard, r, n-seekSkipped) out: if err == io.EOF && seekSkipped+copySkipped < n { err = io.ErrUnexpectedEOF } return err } ================================================ FILE: vendor/github.com/vbatts/tar-split/archive/tar/stat_actime1.go ================================================ // Copyright 2012 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build linux dragonfly openbsd solaris package tar import ( "syscall" "time" ) func statAtime(st *syscall.Stat_t) time.Time { return time.Unix(st.Atim.Unix()) } func statCtime(st *syscall.Stat_t) time.Time { return time.Unix(st.Ctim.Unix()) } ================================================ FILE: vendor/github.com/vbatts/tar-split/archive/tar/stat_actime2.go ================================================ // Copyright 2012 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build darwin freebsd netbsd package tar import ( "syscall" "time" ) func statAtime(st *syscall.Stat_t) time.Time { return time.Unix(st.Atimespec.Unix()) } func statCtime(st *syscall.Stat_t) time.Time { return time.Unix(st.Ctimespec.Unix()) } ================================================ FILE: vendor/github.com/vbatts/tar-split/archive/tar/stat_unix.go ================================================ // Copyright 2012 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build linux darwin dragonfly freebsd openbsd netbsd solaris package tar import ( "os" "os/user" "runtime" "strconv" "sync" "syscall" ) func init() { sysStat = statUnix } // userMap and groupMap caches UID and GID lookups for performance reasons. // The downside is that renaming uname or gname by the OS never takes effect. var userMap, groupMap sync.Map // map[int]string func statUnix(fi os.FileInfo, h *Header) error { sys, ok := fi.Sys().(*syscall.Stat_t) if !ok { return nil } h.Uid = int(sys.Uid) h.Gid = int(sys.Gid) // Best effort at populating Uname and Gname. // The os/user functions may fail for any number of reasons // (not implemented on that platform, cgo not enabled, etc). if u, ok := userMap.Load(h.Uid); ok { h.Uname = u.(string) } else if u, err := user.LookupId(strconv.Itoa(h.Uid)); err == nil { h.Uname = u.Username userMap.Store(h.Uid, h.Uname) } if g, ok := groupMap.Load(h.Gid); ok { h.Gname = g.(string) } else if g, err := user.LookupGroupId(strconv.Itoa(h.Gid)); err == nil { h.Gname = g.Name groupMap.Store(h.Gid, h.Gname) } h.AccessTime = statAtime(sys) h.ChangeTime = statCtime(sys) // Best effort at populating Devmajor and Devminor. if h.Typeflag == TypeChar || h.Typeflag == TypeBlock { dev := uint64(sys.Rdev) // May be int32 or uint32 switch runtime.GOOS { case "linux": // Copied from golang.org/x/sys/unix/dev_linux.go. major := uint32((dev & 0x00000000000fff00) >> 8) major |= uint32((dev & 0xfffff00000000000) >> 32) minor := uint32((dev & 0x00000000000000ff) >> 0) minor |= uint32((dev & 0x00000ffffff00000) >> 12) h.Devmajor, h.Devminor = int64(major), int64(minor) case "darwin": // Copied from golang.org/x/sys/unix/dev_darwin.go. major := uint32((dev >> 24) & 0xff) minor := uint32(dev & 0xffffff) h.Devmajor, h.Devminor = int64(major), int64(minor) case "dragonfly": // Copied from golang.org/x/sys/unix/dev_dragonfly.go. major := uint32((dev >> 8) & 0xff) minor := uint32(dev & 0xffff00ff) h.Devmajor, h.Devminor = int64(major), int64(minor) case "freebsd": // Copied from golang.org/x/sys/unix/dev_freebsd.go. major := uint32((dev >> 8) & 0xff) minor := uint32(dev & 0xffff00ff) h.Devmajor, h.Devminor = int64(major), int64(minor) case "netbsd": // Copied from golang.org/x/sys/unix/dev_netbsd.go. major := uint32((dev & 0x000fff00) >> 8) minor := uint32((dev & 0x000000ff) >> 0) minor |= uint32((dev & 0xfff00000) >> 12) h.Devmajor, h.Devminor = int64(major), int64(minor) case "openbsd": // Copied from golang.org/x/sys/unix/dev_openbsd.go. major := uint32((dev & 0x0000ff00) >> 8) minor := uint32((dev & 0x000000ff) >> 0) minor |= uint32((dev & 0xffff0000) >> 8) h.Devmajor, h.Devminor = int64(major), int64(minor) default: // TODO: Implement solaris (see https://golang.org/issue/8106) } } return nil } ================================================ FILE: vendor/github.com/vbatts/tar-split/archive/tar/strconv.go ================================================ // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package tar import ( "bytes" "fmt" "strconv" "strings" "time" ) // hasNUL reports whether the NUL character exists within s. func hasNUL(s string) bool { return strings.IndexByte(s, 0) >= 0 } // isASCII reports whether the input is an ASCII C-style string. func isASCII(s string) bool { for _, c := range s { if c >= 0x80 || c == 0x00 { return false } } return true } // toASCII converts the input to an ASCII C-style string. // This a best effort conversion, so invalid characters are dropped. func toASCII(s string) string { if isASCII(s) { return s } b := make([]byte, 0, len(s)) for _, c := range s { if c < 0x80 && c != 0x00 { b = append(b, byte(c)) } } return string(b) } type parser struct { err error // Last error seen } type formatter struct { err error // Last error seen } // parseString parses bytes as a NUL-terminated C-style string. // If a NUL byte is not found then the whole slice is returned as a string. func (*parser) parseString(b []byte) string { if i := bytes.IndexByte(b, 0); i >= 0 { return string(b[:i]) } return string(b) } // formatString copies s into b, NUL-terminating if possible. func (f *formatter) formatString(b []byte, s string) { if len(s) > len(b) { f.err = ErrFieldTooLong } copy(b, s) if len(s) < len(b) { b[len(s)] = 0 } // Some buggy readers treat regular files with a trailing slash // in the V7 path field as a directory even though the full path // recorded elsewhere (e.g., via PAX record) contains no trailing slash. if len(s) > len(b) && b[len(b)-1] == '/' { n := len(strings.TrimRight(s[:len(b)], "/")) b[n] = 0 // Replace trailing slash with NUL terminator } } // fitsInBase256 reports whether x can be encoded into n bytes using base-256 // encoding. Unlike octal encoding, base-256 encoding does not require that the // string ends with a NUL character. Thus, all n bytes are available for output. // // If operating in binary mode, this assumes strict GNU binary mode; which means // that the first byte can only be either 0x80 or 0xff. Thus, the first byte is // equivalent to the sign bit in two's complement form. func fitsInBase256(n int, x int64) bool { binBits := uint(n-1) * 8 return n >= 9 || (x >= -1< 0 && b[0]&0x80 != 0 { // Handling negative numbers relies on the following identity: // -a-1 == ^a // // If the number is negative, we use an inversion mask to invert the // data bytes and treat the value as an unsigned number. var inv byte // 0x00 if positive or zero, 0xff if negative if b[0]&0x40 != 0 { inv = 0xff } var x uint64 for i, c := range b { c ^= inv // Inverts c only if inv is 0xff, otherwise does nothing if i == 0 { c &= 0x7f // Ignore signal bit in first byte } if (x >> 56) > 0 { p.err = ErrHeader // Integer overflow return 0 } x = x<<8 | uint64(c) } if (x >> 63) > 0 { p.err = ErrHeader // Integer overflow return 0 } if inv == 0xff { return ^int64(x) } return int64(x) } // Normal case is base-8 (octal) format. return p.parseOctal(b) } // formatNumeric encodes x into b using base-8 (octal) encoding if possible. // Otherwise it will attempt to use base-256 (binary) encoding. func (f *formatter) formatNumeric(b []byte, x int64) { if fitsInOctal(len(b), x) { f.formatOctal(b, x) return } if fitsInBase256(len(b), x) { for i := len(b) - 1; i >= 0; i-- { b[i] = byte(x) x >>= 8 } b[0] |= 0x80 // Highest bit indicates binary format return } f.formatOctal(b, 0) // Last resort, just write zero f.err = ErrFieldTooLong } func (p *parser) parseOctal(b []byte) int64 { // Because unused fields are filled with NULs, we need // to skip leading NULs. Fields may also be padded with // spaces or NULs. // So we remove leading and trailing NULs and spaces to // be sure. b = bytes.Trim(b, " \x00") if len(b) == 0 { return 0 } x, perr := strconv.ParseUint(p.parseString(b), 8, 64) if perr != nil { p.err = ErrHeader } return int64(x) } func (f *formatter) formatOctal(b []byte, x int64) { if !fitsInOctal(len(b), x) { x = 0 // Last resort, just write zero f.err = ErrFieldTooLong } s := strconv.FormatInt(x, 8) // Add leading zeros, but leave room for a NUL. if n := len(b) - len(s) - 1; n > 0 { s = strings.Repeat("0", n) + s } f.formatString(b, s) } // fitsInOctal reports whether the integer x fits in a field n-bytes long // using octal encoding with the appropriate NUL terminator. func fitsInOctal(n int, x int64) bool { octBits := uint(n-1) * 3 return x >= 0 && (n >= 22 || x < 1<= 0 { ss, sn = s[:pos], s[pos+1:] } // Parse the seconds. secs, err := strconv.ParseInt(ss, 10, 64) if err != nil { return time.Time{}, ErrHeader } if len(sn) == 0 { return time.Unix(secs, 0), nil // No sub-second values } // Parse the nanoseconds. if strings.Trim(sn, "0123456789") != "" { return time.Time{}, ErrHeader } if len(sn) < maxNanoSecondDigits { sn += strings.Repeat("0", maxNanoSecondDigits-len(sn)) // Right pad } else { sn = sn[:maxNanoSecondDigits] // Right truncate } nsecs, _ := strconv.ParseInt(sn, 10, 64) // Must succeed if len(ss) > 0 && ss[0] == '-' { return time.Unix(secs, -1*nsecs), nil // Negative correction } return time.Unix(secs, nsecs), nil } // formatPAXTime converts ts into a time of the form %d.%d as described in the // PAX specification. This function is capable of negative timestamps. func formatPAXTime(ts time.Time) (s string) { secs, nsecs := ts.Unix(), ts.Nanosecond() if nsecs == 0 { return strconv.FormatInt(secs, 10) } // If seconds is negative, then perform correction. sign := "" if secs < 0 { sign = "-" // Remember sign secs = -(secs + 1) // Add a second to secs nsecs = -(nsecs - 1E9) // Take that second away from nsecs } return strings.TrimRight(fmt.Sprintf("%s%d.%09d", sign, secs, nsecs), "0") } // parsePAXRecord parses the input PAX record string into a key-value pair. // If parsing is successful, it will slice off the currently read record and // return the remainder as r. func parsePAXRecord(s string) (k, v, r string, err error) { // The size field ends at the first space. sp := strings.IndexByte(s, ' ') if sp == -1 { return "", "", s, ErrHeader } // Parse the first token as a decimal integer. n, perr := strconv.ParseInt(s[:sp], 10, 0) // Intentionally parse as native int if perr != nil || n < 5 || int64(len(s)) < n { return "", "", s, ErrHeader } // Extract everything between the space and the final newline. rec, nl, rem := s[sp+1:n-1], s[n-1:n], s[n:] if nl != "\n" { return "", "", s, ErrHeader } // The first equals separates the key from the value. eq := strings.IndexByte(rec, '=') if eq == -1 { return "", "", s, ErrHeader } k, v = rec[:eq], rec[eq+1:] if !validPAXRecord(k, v) { return "", "", s, ErrHeader } return k, v, rem, nil } // formatPAXRecord formats a single PAX record, prefixing it with the // appropriate length. func formatPAXRecord(k, v string) (string, error) { if !validPAXRecord(k, v) { return "", ErrHeader } const padding = 3 // Extra padding for ' ', '=', and '\n' size := len(k) + len(v) + padding size += len(strconv.Itoa(size)) record := strconv.Itoa(size) + " " + k + "=" + v + "\n" // Final adjustment if adding size field increased the record size. if len(record) != size { size = len(record) record = strconv.Itoa(size) + " " + k + "=" + v + "\n" } return record, nil } // validPAXRecord reports whether the key-value pair is valid where each // record is formatted as: // "%d %s=%s\n" % (size, key, value) // // Keys and values should be UTF-8, but the number of bad writers out there // forces us to be a more liberal. // Thus, we only reject all keys with NUL, and only reject NULs in values // for the PAX version of the USTAR string fields. // The key must not contain an '=' character. func validPAXRecord(k, v string) bool { if k == "" || strings.IndexByte(k, '=') >= 0 { return false } switch k { case paxPath, paxLinkpath, paxUname, paxGname: return !hasNUL(v) default: return !hasNUL(k) } } ================================================ FILE: vendor/github.com/vbatts/tar-split/archive/tar/writer.go ================================================ // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package tar import ( "fmt" "io" "path" "sort" "strings" "time" ) // Writer provides sequential writing of a tar archive. // Write.WriteHeader begins a new file with the provided Header, // and then Writer can be treated as an io.Writer to supply that file's data. type Writer struct { w io.Writer pad int64 // Amount of padding to write after current file entry curr fileWriter // Writer for current file entry hdr Header // Shallow copy of Header that is safe for mutations blk block // Buffer to use as temporary local storage // err is a persistent error. // It is only the responsibility of every exported method of Writer to // ensure that this error is sticky. err error } // NewWriter creates a new Writer writing to w. func NewWriter(w io.Writer) *Writer { return &Writer{w: w, curr: ®FileWriter{w, 0}} } type fileWriter interface { io.Writer fileState ReadFrom(io.Reader) (int64, error) } // Flush finishes writing the current file's block padding. // The current file must be fully written before Flush can be called. // // This is unnecessary as the next call to WriteHeader or Close // will implicitly flush out the file's padding. func (tw *Writer) Flush() error { if tw.err != nil { return tw.err } if nb := tw.curr.LogicalRemaining(); nb > 0 { return fmt.Errorf("archive/tar: missed writing %d bytes", nb) } if _, tw.err = tw.w.Write(zeroBlock[:tw.pad]); tw.err != nil { return tw.err } tw.pad = 0 return nil } // WriteHeader writes hdr and prepares to accept the file's contents. // The Header.Size determines how many bytes can be written for the next file. // If the current file is not fully written, then this returns an error. // This implicitly flushes any padding necessary before writing the header. func (tw *Writer) WriteHeader(hdr *Header) error { if err := tw.Flush(); err != nil { return err } tw.hdr = *hdr // Shallow copy of Header // Avoid usage of the legacy TypeRegA flag, and automatically promote // it to use TypeReg or TypeDir. if tw.hdr.Typeflag == TypeRegA { if strings.HasSuffix(tw.hdr.Name, "/") { tw.hdr.Typeflag = TypeDir } else { tw.hdr.Typeflag = TypeReg } } // Round ModTime and ignore AccessTime and ChangeTime unless // the format is explicitly chosen. // This ensures nominal usage of WriteHeader (without specifying the format) // does not always result in the PAX format being chosen, which // causes a 1KiB increase to every header. if tw.hdr.Format == FormatUnknown { tw.hdr.ModTime = tw.hdr.ModTime.Round(time.Second) tw.hdr.AccessTime = time.Time{} tw.hdr.ChangeTime = time.Time{} } allowedFormats, paxHdrs, err := tw.hdr.allowedFormats() switch { case allowedFormats.has(FormatUSTAR): tw.err = tw.writeUSTARHeader(&tw.hdr) return tw.err case allowedFormats.has(FormatPAX): tw.err = tw.writePAXHeader(&tw.hdr, paxHdrs) return tw.err case allowedFormats.has(FormatGNU): tw.err = tw.writeGNUHeader(&tw.hdr) return tw.err default: return err // Non-fatal error } } func (tw *Writer) writeUSTARHeader(hdr *Header) error { // Check if we can use USTAR prefix/suffix splitting. var namePrefix string if prefix, suffix, ok := splitUSTARPath(hdr.Name); ok { namePrefix, hdr.Name = prefix, suffix } // Pack the main header. var f formatter blk := tw.templateV7Plus(hdr, f.formatString, f.formatOctal) f.formatString(blk.USTAR().Prefix(), namePrefix) blk.SetFormat(FormatUSTAR) if f.err != nil { return f.err // Should never happen since header is validated } return tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag) } func (tw *Writer) writePAXHeader(hdr *Header, paxHdrs map[string]string) error { realName, realSize := hdr.Name, hdr.Size // TODO(dsnet): Re-enable this when adding sparse support. // See https://golang.org/issue/22735 /* // Handle sparse files. var spd sparseDatas var spb []byte if len(hdr.SparseHoles) > 0 { sph := append([]sparseEntry{}, hdr.SparseHoles...) // Copy sparse map sph = alignSparseEntries(sph, hdr.Size) spd = invertSparseEntries(sph, hdr.Size) // Format the sparse map. hdr.Size = 0 // Replace with encoded size spb = append(strconv.AppendInt(spb, int64(len(spd)), 10), '\n') for _, s := range spd { hdr.Size += s.Length spb = append(strconv.AppendInt(spb, s.Offset, 10), '\n') spb = append(strconv.AppendInt(spb, s.Length, 10), '\n') } pad := blockPadding(int64(len(spb))) spb = append(spb, zeroBlock[:pad]...) hdr.Size += int64(len(spb)) // Accounts for encoded sparse map // Add and modify appropriate PAX records. dir, file := path.Split(realName) hdr.Name = path.Join(dir, "GNUSparseFile.0", file) paxHdrs[paxGNUSparseMajor] = "1" paxHdrs[paxGNUSparseMinor] = "0" paxHdrs[paxGNUSparseName] = realName paxHdrs[paxGNUSparseRealSize] = strconv.FormatInt(realSize, 10) paxHdrs[paxSize] = strconv.FormatInt(hdr.Size, 10) delete(paxHdrs, paxPath) // Recorded by paxGNUSparseName } */ _ = realSize // Write PAX records to the output. isGlobal := hdr.Typeflag == TypeXGlobalHeader if len(paxHdrs) > 0 || isGlobal { // Sort keys for deterministic ordering. var keys []string for k := range paxHdrs { keys = append(keys, k) } sort.Strings(keys) // Write each record to a buffer. var buf strings.Builder for _, k := range keys { rec, err := formatPAXRecord(k, paxHdrs[k]) if err != nil { return err } buf.WriteString(rec) } // Write the extended header file. var name string var flag byte if isGlobal { name = realName if name == "" { name = "GlobalHead.0.0" } flag = TypeXGlobalHeader } else { dir, file := path.Split(realName) name = path.Join(dir, "PaxHeaders.0", file) flag = TypeXHeader } data := buf.String() if err := tw.writeRawFile(name, data, flag, FormatPAX); err != nil || isGlobal { return err // Global headers return here } } // Pack the main header. var f formatter // Ignore errors since they are expected fmtStr := func(b []byte, s string) { f.formatString(b, toASCII(s)) } blk := tw.templateV7Plus(hdr, fmtStr, f.formatOctal) blk.SetFormat(FormatPAX) if err := tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag); err != nil { return err } // TODO(dsnet): Re-enable this when adding sparse support. // See https://golang.org/issue/22735 /* // Write the sparse map and setup the sparse writer if necessary. if len(spd) > 0 { // Use tw.curr since the sparse map is accounted for in hdr.Size. if _, err := tw.curr.Write(spb); err != nil { return err } tw.curr = &sparseFileWriter{tw.curr, spd, 0} } */ return nil } func (tw *Writer) writeGNUHeader(hdr *Header) error { // Use long-link files if Name or Linkname exceeds the field size. const longName = "././@LongLink" if len(hdr.Name) > nameSize { data := hdr.Name + "\x00" if err := tw.writeRawFile(longName, data, TypeGNULongName, FormatGNU); err != nil { return err } } if len(hdr.Linkname) > nameSize { data := hdr.Linkname + "\x00" if err := tw.writeRawFile(longName, data, TypeGNULongLink, FormatGNU); err != nil { return err } } // Pack the main header. var f formatter // Ignore errors since they are expected var spd sparseDatas var spb []byte blk := tw.templateV7Plus(hdr, f.formatString, f.formatNumeric) if !hdr.AccessTime.IsZero() { f.formatNumeric(blk.GNU().AccessTime(), hdr.AccessTime.Unix()) } if !hdr.ChangeTime.IsZero() { f.formatNumeric(blk.GNU().ChangeTime(), hdr.ChangeTime.Unix()) } // TODO(dsnet): Re-enable this when adding sparse support. // See https://golang.org/issue/22735 /* if hdr.Typeflag == TypeGNUSparse { sph := append([]sparseEntry{}, hdr.SparseHoles...) // Copy sparse map sph = alignSparseEntries(sph, hdr.Size) spd = invertSparseEntries(sph, hdr.Size) // Format the sparse map. formatSPD := func(sp sparseDatas, sa sparseArray) sparseDatas { for i := 0; len(sp) > 0 && i < sa.MaxEntries(); i++ { f.formatNumeric(sa.Entry(i).Offset(), sp[0].Offset) f.formatNumeric(sa.Entry(i).Length(), sp[0].Length) sp = sp[1:] } if len(sp) > 0 { sa.IsExtended()[0] = 1 } return sp } sp2 := formatSPD(spd, blk.GNU().Sparse()) for len(sp2) > 0 { var spHdr block sp2 = formatSPD(sp2, spHdr.Sparse()) spb = append(spb, spHdr[:]...) } // Update size fields in the header block. realSize := hdr.Size hdr.Size = 0 // Encoded size; does not account for encoded sparse map for _, s := range spd { hdr.Size += s.Length } copy(blk.V7().Size(), zeroBlock[:]) // Reset field f.formatNumeric(blk.V7().Size(), hdr.Size) f.formatNumeric(blk.GNU().RealSize(), realSize) } */ blk.SetFormat(FormatGNU) if err := tw.writeRawHeader(blk, hdr.Size, hdr.Typeflag); err != nil { return err } // Write the extended sparse map and setup the sparse writer if necessary. if len(spd) > 0 { // Use tw.w since the sparse map is not accounted for in hdr.Size. if _, err := tw.w.Write(spb); err != nil { return err } tw.curr = &sparseFileWriter{tw.curr, spd, 0} } return nil } type ( stringFormatter func([]byte, string) numberFormatter func([]byte, int64) ) // templateV7Plus fills out the V7 fields of a block using values from hdr. // It also fills out fields (uname, gname, devmajor, devminor) that are // shared in the USTAR, PAX, and GNU formats using the provided formatters. // // The block returned is only valid until the next call to // templateV7Plus or writeRawFile. func (tw *Writer) templateV7Plus(hdr *Header, fmtStr stringFormatter, fmtNum numberFormatter) *block { tw.blk.Reset() modTime := hdr.ModTime if modTime.IsZero() { modTime = time.Unix(0, 0) } v7 := tw.blk.V7() v7.TypeFlag()[0] = hdr.Typeflag fmtStr(v7.Name(), hdr.Name) fmtStr(v7.LinkName(), hdr.Linkname) fmtNum(v7.Mode(), hdr.Mode) fmtNum(v7.UID(), int64(hdr.Uid)) fmtNum(v7.GID(), int64(hdr.Gid)) fmtNum(v7.Size(), hdr.Size) fmtNum(v7.ModTime(), modTime.Unix()) ustar := tw.blk.USTAR() fmtStr(ustar.UserName(), hdr.Uname) fmtStr(ustar.GroupName(), hdr.Gname) fmtNum(ustar.DevMajor(), hdr.Devmajor) fmtNum(ustar.DevMinor(), hdr.Devminor) return &tw.blk } // writeRawFile writes a minimal file with the given name and flag type. // It uses format to encode the header format and will write data as the body. // It uses default values for all of the other fields (as BSD and GNU tar does). func (tw *Writer) writeRawFile(name, data string, flag byte, format Format) error { tw.blk.Reset() // Best effort for the filename. name = toASCII(name) if len(name) > nameSize { name = name[:nameSize] } name = strings.TrimRight(name, "/") var f formatter v7 := tw.blk.V7() v7.TypeFlag()[0] = flag f.formatString(v7.Name(), name) f.formatOctal(v7.Mode(), 0) f.formatOctal(v7.UID(), 0) f.formatOctal(v7.GID(), 0) f.formatOctal(v7.Size(), int64(len(data))) // Must be < 8GiB f.formatOctal(v7.ModTime(), 0) tw.blk.SetFormat(format) if f.err != nil { return f.err // Only occurs if size condition is violated } // Write the header and data. if err := tw.writeRawHeader(&tw.blk, int64(len(data)), flag); err != nil { return err } _, err := io.WriteString(tw, data) return err } // writeRawHeader writes the value of blk, regardless of its value. // It sets up the Writer such that it can accept a file of the given size. // If the flag is a special header-only flag, then the size is treated as zero. func (tw *Writer) writeRawHeader(blk *block, size int64, flag byte) error { if err := tw.Flush(); err != nil { return err } if _, err := tw.w.Write(blk[:]); err != nil { return err } if isHeaderOnlyType(flag) { size = 0 } tw.curr = ®FileWriter{tw.w, size} tw.pad = blockPadding(size) return nil } // splitUSTARPath splits a path according to USTAR prefix and suffix rules. // If the path is not splittable, then it will return ("", "", false). func splitUSTARPath(name string) (prefix, suffix string, ok bool) { length := len(name) if length <= nameSize || !isASCII(name) { return "", "", false } else if length > prefixSize+1 { length = prefixSize + 1 } else if name[length-1] == '/' { length-- } i := strings.LastIndex(name[:length], "/") nlen := len(name) - i - 1 // nlen is length of suffix plen := i // plen is length of prefix if i <= 0 || nlen > nameSize || nlen == 0 || plen > prefixSize { return "", "", false } return name[:i], name[i+1:], true } // Write writes to the current file in the tar archive. // Write returns the error ErrWriteTooLong if more than // Header.Size bytes are written after WriteHeader. // // Calling Write on special types like TypeLink, TypeSymlink, TypeChar, // TypeBlock, TypeDir, and TypeFifo returns (0, ErrWriteTooLong) regardless // of what the Header.Size claims. func (tw *Writer) Write(b []byte) (int, error) { if tw.err != nil { return 0, tw.err } n, err := tw.curr.Write(b) if err != nil && err != ErrWriteTooLong { tw.err = err } return n, err } // readFrom populates the content of the current file by reading from r. // The bytes read must match the number of remaining bytes in the current file. // // If the current file is sparse and r is an io.ReadSeeker, // then readFrom uses Seek to skip past holes defined in Header.SparseHoles, // assuming that skipped regions are all NULs. // This always reads the last byte to ensure r is the right size. // // TODO(dsnet): Re-export this when adding sparse file support. // See https://golang.org/issue/22735 func (tw *Writer) readFrom(r io.Reader) (int64, error) { if tw.err != nil { return 0, tw.err } n, err := tw.curr.ReadFrom(r) if err != nil && err != ErrWriteTooLong { tw.err = err } return n, err } // Close closes the tar archive by flushing the padding, and writing the footer. // If the current file (from a prior call to WriteHeader) is not fully written, // then this returns an error. func (tw *Writer) Close() error { if tw.err == ErrWriteAfterClose { return nil } if tw.err != nil { return tw.err } // Trailer: two zero blocks. err := tw.Flush() for i := 0; i < 2 && err == nil; i++ { _, err = tw.w.Write(zeroBlock[:]) } // Ensure all future actions are invalid. tw.err = ErrWriteAfterClose return err // Report IO errors } // regFileWriter is a fileWriter for writing data to a regular file entry. type regFileWriter struct { w io.Writer // Underlying Writer nb int64 // Number of remaining bytes to write } func (fw *regFileWriter) Write(b []byte) (n int, err error) { overwrite := int64(len(b)) > fw.nb if overwrite { b = b[:fw.nb] } if len(b) > 0 { n, err = fw.w.Write(b) fw.nb -= int64(n) } switch { case err != nil: return n, err case overwrite: return n, ErrWriteTooLong default: return n, nil } } func (fw *regFileWriter) ReadFrom(r io.Reader) (int64, error) { return io.Copy(struct{ io.Writer }{fw}, r) } func (fw regFileWriter) LogicalRemaining() int64 { return fw.nb } func (fw regFileWriter) PhysicalRemaining() int64 { return fw.nb } // sparseFileWriter is a fileWriter for writing data to a sparse file entry. type sparseFileWriter struct { fw fileWriter // Underlying fileWriter sp sparseDatas // Normalized list of data fragments pos int64 // Current position in sparse file } func (sw *sparseFileWriter) Write(b []byte) (n int, err error) { overwrite := int64(len(b)) > sw.LogicalRemaining() if overwrite { b = b[:sw.LogicalRemaining()] } b0 := b endPos := sw.pos + int64(len(b)) for endPos > sw.pos && err == nil { var nf int // Bytes written in fragment dataStart, dataEnd := sw.sp[0].Offset, sw.sp[0].endOffset() if sw.pos < dataStart { // In a hole fragment bf := b[:min(int64(len(b)), dataStart-sw.pos)] nf, err = zeroWriter{}.Write(bf) } else { // In a data fragment bf := b[:min(int64(len(b)), dataEnd-sw.pos)] nf, err = sw.fw.Write(bf) } b = b[nf:] sw.pos += int64(nf) if sw.pos >= dataEnd && len(sw.sp) > 1 { sw.sp = sw.sp[1:] // Ensure last fragment always remains } } n = len(b0) - len(b) switch { case err == ErrWriteTooLong: return n, errMissData // Not possible; implies bug in validation logic case err != nil: return n, err case sw.LogicalRemaining() == 0 && sw.PhysicalRemaining() > 0: return n, errUnrefData // Not possible; implies bug in validation logic case overwrite: return n, ErrWriteTooLong default: return n, nil } } func (sw *sparseFileWriter) ReadFrom(r io.Reader) (n int64, err error) { rs, ok := r.(io.ReadSeeker) if ok { if _, err := rs.Seek(0, io.SeekCurrent); err != nil { ok = false // Not all io.Seeker can really seek } } if !ok { return io.Copy(struct{ io.Writer }{sw}, r) } var readLastByte bool pos0 := sw.pos for sw.LogicalRemaining() > 0 && !readLastByte && err == nil { var nf int64 // Size of fragment dataStart, dataEnd := sw.sp[0].Offset, sw.sp[0].endOffset() if sw.pos < dataStart { // In a hole fragment nf = dataStart - sw.pos if sw.PhysicalRemaining() == 0 { readLastByte = true nf-- } _, err = rs.Seek(nf, io.SeekCurrent) } else { // In a data fragment nf = dataEnd - sw.pos nf, err = io.CopyN(sw.fw, rs, nf) } sw.pos += nf if sw.pos >= dataEnd && len(sw.sp) > 1 { sw.sp = sw.sp[1:] // Ensure last fragment always remains } } // If the last fragment is a hole, then seek to 1-byte before EOF, and // read a single byte to ensure the file is the right size. if readLastByte && err == nil { _, err = mustReadFull(rs, []byte{0}) sw.pos++ } n = sw.pos - pos0 switch { case err == io.EOF: return n, io.ErrUnexpectedEOF case err == ErrWriteTooLong: return n, errMissData // Not possible; implies bug in validation logic case err != nil: return n, err case sw.LogicalRemaining() == 0 && sw.PhysicalRemaining() > 0: return n, errUnrefData // Not possible; implies bug in validation logic default: return n, ensureEOF(rs) } } func (sw sparseFileWriter) LogicalRemaining() int64 { return sw.sp[len(sw.sp)-1].endOffset() - sw.pos } func (sw sparseFileWriter) PhysicalRemaining() int64 { return sw.fw.PhysicalRemaining() } // zeroWriter may only be written with NULs, otherwise it returns errWriteHole. type zeroWriter struct{} func (zeroWriter) Write(b []byte) (int, error) { for i, c := range b { if c != 0 { return i, errWriteHole } } return len(b), nil } // ensureEOF checks whether r is at EOF, reporting ErrWriteTooLong if not so. func ensureEOF(r io.Reader) error { n, err := tryReadFull(r, []byte{0}) switch { case n > 0: return ErrWriteTooLong case err == io.EOF: return nil default: return err } } ================================================ FILE: vendor/github.com/xlab/treeprint/LICENSE ================================================ The MIT License (MIT) Copyright © 2016 Maxim Kupriianov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/xlab/treeprint/README.md ================================================ treeprint [![GoDoc](https://godoc.org/github.com/xlab/treeprint?status.svg)](https://godoc.org/github.com/xlab/treeprint) ![test coverage](https://img.shields.io/badge/coverage-68.6%25-green.svg) ========= Package `treeprint` provides a simple ASCII tree composing tool. SYSTEME FIGURE If you are familiar with the [tree](http://mama.indstate.edu/users/ice/tree/) utility that is a recursive directory listing command that produces a depth indented listing of files, then you have the idea of what it would look like. On my system the command yields the following ``` $ tree . ├── LICENSE ├── README.md ├── treeprint.go └── treeprint_test.go 0 directories, 4 files ``` and I'd like to have the same format for my Go data structures when I print them. ## Installation ``` $ go get github.com/xlab/treeprint ``` ## Concept of work The general idea is that you initialise a new tree with `treeprint.New()` and then add nodes and branches into it. Use `AddNode()` when you want add a node on the same level as the target or use `AddBranch()` when you want to go a level deeper. So `tree.AddBranch().AddNode().AddNode()` would create a new level with two distinct nodes on it. So `tree.AddNode().AddNode()` is a flat thing and `tree.AddBranch().AddBranch().AddBranch()` is a high thing. Use `String()` or `Bytes()` on a branch to render a subtree, or use it on the root to print the whole tree. The utility will yield Unicode-friendly trees. The output is predictable and there is no platform-dependent exceptions, so if you have issues with displaying the tree in the console, all platform-related transformations can be done after the tree has been rendered: [an example](https://github.com/xlab/treeprint/issues/2#issuecomment-324944141) for Asian locales. ## Use cases ### When you want to render a complex data structure: ```go func main() { // to add a custom root name use `treeprint.NewWithRoot()` instead tree := treeprint.New() // create a new branch in the root one := tree.AddBranch("one") // add some nodes one.AddNode("subnode1").AddNode("subnode2") // create a new sub-branch one.AddBranch("two"). AddNode("subnode1").AddNode("subnode2"). // add some nodes AddBranch("three"). // add a new sub-branch AddNode("subnode1").AddNode("subnode2") // add some nodes too // add one more node that should surround the inner branch one.AddNode("subnode3") // add a new node to the root tree.AddNode("outernode") fmt.Println(tree.String()) } ``` Will give you: ``` . ├── one │   ├── subnode1 │   ├── subnode2 │   ├── two │   │   ├── subnode1 │   │   ├── subnode2 │   │   └── three │   │   ├── subnode1 │   │   └── subnode2 │   └── subnode3 └── outernode ``` ### Another case, when you have to make a tree where any leaf may have some meta-data (as `tree` is capable of it): ```go func main { // to add a custom root name use `treeprint.NewWithRoot()` instead tree := treeprint.New() tree.AddNode("Dockerfile") tree.AddNode("Makefile") tree.AddNode("aws.sh") tree.AddMetaBranch(" 204", "bin"). AddNode("dbmaker").AddNode("someserver").AddNode("testtool") tree.AddMetaBranch(" 374", "deploy"). AddNode("Makefile").AddNode("bootstrap.sh") tree.AddMetaNode("122K", "testtool.a") fmt.Println(tree.String()) } ``` Output: ``` . ├── Dockerfile ├── Makefile ├── aws.sh ├── [ 204] bin │   ├── dbmaker │   ├── someserver │   └── testtool ├── [ 374] deploy │   ├── Makefile │   └── bootstrap.sh └── [122K] testtool.a ``` ### Iterating over the tree nodes ```go tree := New() one := tree.AddBranch("one") one.AddNode("one-subnode1").AddNode("one-subnode2") one.AddBranch("two").AddNode("two-subnode1").AddNode("two-subnode2"). AddBranch("three").AddNode("three-subnode1").AddNode("three-subnode2") tree.AddNode("outernode") // if you need to iterate over the whole tree // call `VisitAll` from your top root node. tree.VisitAll(func(item *node) { if len(item.Nodes) > 0 { // branch nodes fmt.Println(item.Value) // will output one, two, three } else { // leaf nodes fmt.Println(item.Value) // will output one-*, two-*, three-* and outernode } }) ``` Yay! So it works. ## License MIT ================================================ FILE: vendor/github.com/xlab/treeprint/helpers.go ================================================ package treeprint import ( "reflect" "strings" ) func isEmpty(v *reflect.Value) bool { switch v.Kind() { case reflect.Array, reflect.Map, reflect.Slice, reflect.String: return v.Len() == 0 case reflect.Bool: return !v.Bool() case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return v.Int() == 0 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return v.Uint() == 0 case reflect.Float32, reflect.Float64: return v.Float() == 0 case reflect.Interface, reflect.Ptr: return v.IsNil() } return false } func tagSpec(tag string) (name string, omit bool) { parts := strings.Split(tag, ",") if len(parts) < 2 { return tag, false } if parts[1] == "omitempty" { return parts[0], true } return parts[0], false } func filterTags(tag reflect.StructTag) string { tags := strings.Split(string(tag), " ") filtered := make([]string, 0, len(tags)) for i := range tags { if strings.HasPrefix(tags[i], "tree:") { continue } filtered = append(filtered, tags[i]) } return strings.Join(filtered, " ") } ================================================ FILE: vendor/github.com/xlab/treeprint/struct.go ================================================ package treeprint import ( "fmt" "reflect" "strings" ) type StructTreeOption int const ( StructNameTree StructTreeOption = iota StructValueTree StructTagTree StructTypeTree StructTypeSizeTree ) func FromStruct(v interface{}, opt ...StructTreeOption) (Tree, error) { var treeOpt StructTreeOption if len(opt) > 0 { treeOpt = opt[0] } switch treeOpt { case StructNameTree: tree := New() err := nameTree(tree, v) return tree, err case StructValueTree: tree := New() err := valueTree(tree, v) return tree, err case StructTagTree: tree := New() err := tagTree(tree, v) return tree, err case StructTypeTree: tree := New() err := typeTree(tree, v) return tree, err case StructTypeSizeTree: tree := New() err := typeSizeTree(tree, v) return tree, err default: err := fmt.Errorf("treeprint: invalid StructTreeOption %v", treeOpt) return nil, err } } type FmtFunc func(name string, v interface{}) (string, bool) func FromStructWithMeta(v interface{}, fmtFunc FmtFunc) (Tree, error) { if fmtFunc == nil { tree := New() err := nameTree(tree, v) return tree, err } tree := New() err := metaTree(tree, v, fmtFunc) return tree, err } func Repr(v interface{}) string { tree := New() vType := reflect.TypeOf(v) vValue := reflect.ValueOf(v) _, val, isStruct := getValue(vType, &vValue) if !isStruct { return fmt.Sprintf("%+v", val.Interface()) } err := valueTree(tree, val.Interface()) if err != nil { return err.Error() } return tree.String() } func nameTree(tree Tree, v interface{}) error { typ, val, err := checkType(v) if err != nil { return err } fields := typ.NumField() for i := 0; i < fields; i++ { field := typ.Field(i) fieldValue := val.Field(i) name, skip, omit := getMeta(field.Name, field.Tag) if skip || omit && isEmpty(&fieldValue) { continue } typ, val, isStruct := getValue(field.Type, &fieldValue) if !isStruct { tree.AddNode(name) continue } else if subNum := typ.NumField(); subNum == 0 { tree.AddNode(name) continue } branch := tree.AddBranch(name) if err := nameTree(branch, val.Interface()); err != nil { err := fmt.Errorf("%v on struct branch %s", err, name) return err } } return nil } func getMeta(fieldName string, tag reflect.StructTag) (name string, skip, omit bool) { if tagStr := tag.Get("tree"); len(tagStr) > 0 { name, omit = tagSpec(tagStr) } if name == "-" { return fieldName, true, omit } if len(name) == 0 { name = fieldName } else if trimmed := strings.TrimSpace(name); len(trimmed) == 0 { name = fieldName } return } func valueTree(tree Tree, v interface{}) error { typ, val, err := checkType(v) if err != nil { return err } fields := typ.NumField() for i := 0; i < fields; i++ { field := typ.Field(i) fieldValue := val.Field(i) name, skip, omit := getMeta(field.Name, field.Tag) if skip || omit && isEmpty(&fieldValue) { continue } typ, val, isStruct := getValue(field.Type, &fieldValue) if !isStruct { tree.AddMetaNode(val.Interface(), name) continue } else if subNum := typ.NumField(); subNum == 0 { tree.AddMetaNode(val.Interface(), name) continue } branch := tree.AddBranch(name) if err := valueTree(branch, val.Interface()); err != nil { err := fmt.Errorf("%v on struct branch %s", err, name) return err } } return nil } func tagTree(tree Tree, v interface{}) error { typ, val, err := checkType(v) if err != nil { return err } fields := typ.NumField() for i := 0; i < fields; i++ { field := typ.Field(i) fieldValue := val.Field(i) name, skip, omit := getMeta(field.Name, field.Tag) if skip || omit && isEmpty(&fieldValue) { continue } filteredTag := filterTags(field.Tag) typ, val, isStruct := getValue(field.Type, &fieldValue) if !isStruct { tree.AddMetaNode(filteredTag, name) continue } else if subNum := typ.NumField(); subNum == 0 { tree.AddMetaNode(filteredTag, name) continue } branch := tree.AddMetaBranch(filteredTag, name) if err := tagTree(branch, val.Interface()); err != nil { err := fmt.Errorf("%v on struct branch %s", err, name) return err } } return nil } func typeTree(tree Tree, v interface{}) error { typ, val, err := checkType(v) if err != nil { return err } fields := typ.NumField() for i := 0; i < fields; i++ { field := typ.Field(i) fieldValue := val.Field(i) name, skip, omit := getMeta(field.Name, field.Tag) if skip || omit && isEmpty(&fieldValue) { continue } typ, val, isStruct := getValue(field.Type, &fieldValue) typename := fmt.Sprintf("%T", val.Interface()) if !isStruct { tree.AddMetaNode(typename, name) continue } else if subNum := typ.NumField(); subNum == 0 { tree.AddMetaNode(typename, name) continue } branch := tree.AddMetaBranch(typename, name) if err := typeTree(branch, val.Interface()); err != nil { err := fmt.Errorf("%v on struct branch %s", err, name) return err } } return nil } func typeSizeTree(tree Tree, v interface{}) error { typ, val, err := checkType(v) if err != nil { return err } fields := typ.NumField() for i := 0; i < fields; i++ { field := typ.Field(i) fieldValue := val.Field(i) name, skip, omit := getMeta(field.Name, field.Tag) if skip || omit && isEmpty(&fieldValue) { continue } typ, val, isStruct := getValue(field.Type, &fieldValue) typesize := typ.Size() if !isStruct { tree.AddMetaNode(typesize, name) continue } else if subNum := typ.NumField(); subNum == 0 { tree.AddMetaNode(typesize, name) continue } branch := tree.AddMetaBranch(typesize, name) if err := typeSizeTree(branch, val.Interface()); err != nil { err := fmt.Errorf("%v on struct branch %s", err, name) return err } } return nil } func metaTree(tree Tree, v interface{}, fmtFunc FmtFunc) error { typ, val, err := checkType(v) if err != nil { return err } fields := typ.NumField() for i := 0; i < fields; i++ { field := typ.Field(i) fieldValue := val.Field(i) name, skip, omit := getMeta(field.Name, field.Tag) if skip || omit && isEmpty(&fieldValue) { continue } typ, val, isStruct := getValue(field.Type, &fieldValue) formatted, show := fmtFunc(name, val.Interface()) if !isStruct { if show { tree.AddMetaNode(formatted, name) continue } tree.AddNode(name) continue } else if subNum := typ.NumField(); subNum == 0 { if show { tree.AddMetaNode(formatted, name) continue } tree.AddNode(name) continue } var branch Tree if show { branch = tree.AddMetaBranch(formatted, name) } else { branch = tree.AddBranch(name) } if err := metaTree(branch, val.Interface(), fmtFunc); err != nil { err := fmt.Errorf("%v on struct branch %s", err, name) return err } } return nil } func getValue(typ reflect.Type, val *reflect.Value) (reflect.Type, *reflect.Value, bool) { switch typ.Kind() { case reflect.Ptr: typ = typ.Elem() if typ.Kind() == reflect.Struct { elem := val.Elem() return typ, &elem, true } case reflect.Struct: return typ, val, true } return typ, val, false } func checkType(v interface{}) (reflect.Type, *reflect.Value, error) { typ := reflect.TypeOf(v) val := reflect.ValueOf(v) switch typ.Kind() { case reflect.Ptr: typ = typ.Elem() if typ.Kind() != reflect.Struct { err := fmt.Errorf("treeprint: %T is not a struct we could work with", v) return nil, nil, err } val = val.Elem() case reflect.Struct: default: err := fmt.Errorf("treeprint: %T is not a struct we could work with", v) return nil, nil, err } return typ, &val, nil } ================================================ FILE: vendor/github.com/xlab/treeprint/treeprint.go ================================================ // Package treeprint provides a simple ASCII tree composing tool. package treeprint import ( "bytes" "fmt" "io" "reflect" "strings" ) // Value defines any value type Value interface{} // MetaValue defines any meta value type MetaValue interface{} // NodeVisitor function type for iterating over nodes type NodeVisitor func(item *node) // Tree represents a tree structure with leaf-nodes and branch-nodes. type Tree interface { // AddNode adds a new node to a branch. AddNode(v Value) Tree // AddMetaNode adds a new node with meta value provided to a branch. AddMetaNode(meta MetaValue, v Value) Tree // AddBranch adds a new branch node (a level deeper). AddBranch(v Value) Tree // AddMetaBranch adds a new branch node (a level deeper) with meta value provided. AddMetaBranch(meta MetaValue, v Value) Tree // Branch converts a leaf-node to a branch-node, // applying this on a branch-node does no effect. Branch() Tree // FindByMeta finds a node whose meta value matches the provided one by reflect.DeepEqual, // returns nil if not found. FindByMeta(meta MetaValue) Tree // FindByValue finds a node whose value matches the provided one by reflect.DeepEqual, // returns nil if not found. FindByValue(value Value) Tree // returns the last node of a tree FindLastNode() Tree // String renders the tree or subtree as a string. String() string // Bytes renders the tree or subtree as byteslice. Bytes() []byte SetValue(value Value) SetMetaValue(meta MetaValue) // VisitAll iterates over the tree, branches and nodes. // If need to iterate over the whole tree, use the root node. // Note this method uses a breadth-first approach. VisitAll(fn NodeVisitor) } type node struct { Root *node Meta MetaValue Value Value Nodes []*node } func (n *node) FindLastNode() Tree { ns := n.Nodes if len(ns) == 0 { return nil } return ns[len(ns)-1] } func (n *node) AddNode(v Value) Tree { n.Nodes = append(n.Nodes, &node{ Root: n, Value: v, }) return n } func (n *node) AddMetaNode(meta MetaValue, v Value) Tree { n.Nodes = append(n.Nodes, &node{ Root: n, Meta: meta, Value: v, }) return n } func (n *node) AddBranch(v Value) Tree { branch := &node{ Root: n, Value: v, } n.Nodes = append(n.Nodes, branch) return branch } func (n *node) AddMetaBranch(meta MetaValue, v Value) Tree { branch := &node{ Root: n, Meta: meta, Value: v, } n.Nodes = append(n.Nodes, branch) return branch } func (n *node) Branch() Tree { n.Root = nil return n } func (n *node) FindByMeta(meta MetaValue) Tree { for _, node := range n.Nodes { if reflect.DeepEqual(node.Meta, meta) { return node } if v := node.FindByMeta(meta); v != nil { return v } } return nil } func (n *node) FindByValue(value Value) Tree { for _, node := range n.Nodes { if reflect.DeepEqual(node.Value, value) { return node } if v := node.FindByMeta(value); v != nil { return v } } return nil } func (n *node) Bytes() []byte { buf := new(bytes.Buffer) level := 0 var levelsEnded []int if n.Root == nil { if n.Meta != nil { buf.WriteString(fmt.Sprintf("[%v] %v", n.Meta, n.Value)) } else { buf.WriteString(fmt.Sprintf("%v", n.Value)) } buf.WriteByte('\n') } else { edge := EdgeTypeMid if len(n.Nodes) == 0 { edge = EdgeTypeEnd levelsEnded = append(levelsEnded, level) } printValues(buf, 0, levelsEnded, edge, n) } if len(n.Nodes) > 0 { printNodes(buf, level, levelsEnded, n.Nodes) } return buf.Bytes() } func (n *node) String() string { return string(n.Bytes()) } func (n *node) SetValue(value Value) { n.Value = value } func (n *node) SetMetaValue(meta MetaValue) { n.Meta = meta } func (n *node) VisitAll(fn NodeVisitor) { for _, node := range n.Nodes { fn(node) if len(node.Nodes) > 0 { node.VisitAll(fn) continue } } } func printNodes(wr io.Writer, level int, levelsEnded []int, nodes []*node) { for i, node := range nodes { edge := EdgeTypeMid if i == len(nodes)-1 { levelsEnded = append(levelsEnded, level) edge = EdgeTypeEnd } printValues(wr, level, levelsEnded, edge, node) if len(node.Nodes) > 0 { printNodes(wr, level+1, levelsEnded, node.Nodes) } } } func printValues(wr io.Writer, level int, levelsEnded []int, edge EdgeType, node *node) { for i := 0; i < level; i++ { if isEnded(levelsEnded, i) { fmt.Fprint(wr, strings.Repeat(" ", IndentSize+1)) continue } fmt.Fprintf(wr, "%s%s", EdgeTypeLink, strings.Repeat(" ", IndentSize)) } val := renderValue(level, node) meta := node.Meta if meta != nil { fmt.Fprintf(wr, "%s [%v] %v\n", edge, meta, val) return } fmt.Fprintf(wr, "%s %v\n", edge, val) } func isEnded(levelsEnded []int, level int) bool { for _, l := range levelsEnded { if l == level { return true } } return false } func renderValue(level int, node *node) Value { lines := strings.Split(fmt.Sprintf("%v", node.Value), "\n") // If value does not contain multiple lines, return itself. if len(lines) < 2 { return node.Value } // If value contains multiple lines, // generate a padding and prefix each line with it. pad := padding(level, node) for i := 1; i < len(lines); i++ { lines[i] = fmt.Sprintf("%s%s", pad, lines[i]) } return strings.Join(lines, "\n") } // padding returns a padding for the multiline values with correctly placed link edges. // It is generated by traversing the tree upwards (from leaf to the root of the tree) // and, on each level, checking if the node the last one of its siblings. // If a node is the last one, the padding on that level should be empty (there's nothing to link to below it). // If a node is not the last one, the padding on that level should be the link edge so the sibling below is correctly connected. func padding(level int, node *node) string { links := make([]string, level+1) for node.Root != nil { if isLast(node) { links[level] = strings.Repeat(" ", IndentSize+1) } else { links[level] = fmt.Sprintf("%s%s", EdgeTypeLink, strings.Repeat(" ", IndentSize)) } level-- node = node.Root } return strings.Join(links, "") } // isLast checks if the node is the last one in the slice of its parent children func isLast(n *node) bool { return n == n.Root.FindLastNode() } type EdgeType string var ( EdgeTypeLink EdgeType = "│" EdgeTypeMid EdgeType = "├──" EdgeTypeEnd EdgeType = "└──" ) // IndentSize is the number of spaces per tree level. var IndentSize = 3 // New Generates new tree func New() Tree { return &node{Value: "."} } // NewWithRoot Generates new tree with the given root value func NewWithRoot(root Value) Tree { return &node{Value: root} } ================================================ FILE: vendor/github.com/xrash/smetrics/.travis.yml ================================================ language: go go: - 1.11 - 1.12 - 1.13 - 1.14.x - master script: - cd tests && make ================================================ FILE: vendor/github.com/xrash/smetrics/LICENSE ================================================ Copyright (C) 2016 Felipe da Cunha Gonçalves All Rights Reserved. MIT LICENSE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: vendor/github.com/xrash/smetrics/README.md ================================================ [![Build Status](https://travis-ci.org/xrash/smetrics.svg?branch=master)](http://travis-ci.org/xrash/smetrics) # smetrics `smetrics` is "string metrics". Package smetrics provides a bunch of algorithms for calculating the distance between strings. There are implementations for calculating the popular Levenshtein distance (aka Edit Distance or Wagner-Fischer), as well as the Jaro distance, the Jaro-Winkler distance, and more. # How to import ```go import "github.com/xrash/smetrics" ``` # Documentation Go to [https://pkg.go.dev/github.com/xrash/smetrics](https://pkg.go.dev/github.com/xrash/smetrics) for complete documentation. # Example ```go package main import ( "github.com/xrash/smetrics" ) func main() { smetrics.WagnerFischer("POTATO", "POTATTO", 1, 1, 2) smetrics.WagnerFischer("MOUSE", "HOUSE", 2, 2, 4) smetrics.Ukkonen("POTATO", "POTATTO", 1, 1, 2) smetrics.Ukkonen("MOUSE", "HOUSE", 2, 2, 4) smetrics.Jaro("AL", "AL") smetrics.Jaro("MARTHA", "MARHTA") smetrics.JaroWinkler("AL", "AL", 0.7, 4) smetrics.JaroWinkler("MARTHA", "MARHTA", 0.7, 4) smetrics.Soundex("Euler") smetrics.Soundex("Ellery") smetrics.Hamming("aaa", "aaa") smetrics.Hamming("aaa", "aab") } ``` ================================================ FILE: vendor/github.com/xrash/smetrics/doc.go ================================================ /* Package smetrics provides a bunch of algorithms for calculating the distance between strings. There are implementations for calculating the popular Levenshtein distance (aka Edit Distance or Wagner-Fischer), as well as the Jaro distance, the Jaro-Winkler distance, and more. For the Levenshtein distance, you can use the functions WagnerFischer() and Ukkonen(). Read the documentation on these functions. For the Jaro and Jaro-Winkler algorithms, check the functions Jaro() and JaroWinkler(). Read the documentation on these functions. For the Soundex algorithm, check the function Soundex(). For the Hamming distance algorithm, check the function Hamming(). */ package smetrics ================================================ FILE: vendor/github.com/xrash/smetrics/hamming.go ================================================ package smetrics import ( "fmt" ) // The Hamming distance is the minimum number of substitutions required to change string A into string B. Both strings must have the same size. If the strings have different sizes, the function returns an error. func Hamming(a, b string) (int, error) { al := len(a) bl := len(b) if al != bl { return -1, fmt.Errorf("strings are not equal (len(a)=%d, len(b)=%d)", al, bl) } var difference = 0 for i := range a { if a[i] != b[i] { difference = difference + 1 } } return difference, nil } ================================================ FILE: vendor/github.com/xrash/smetrics/jaro-winkler.go ================================================ package smetrics import ( "math" ) // The Jaro-Winkler distance. The result is 1 for equal strings, and 0 for completely different strings. It is commonly used on Record Linkage stuff, thus it tries to be accurate for common typos when writing real names such as person names and street names. // Jaro-Winkler is a modification of the Jaro algorithm. It works by first running Jaro, then boosting the score of exact matches at the beginning of the strings. Because of that, it introduces two more parameters: the boostThreshold and the prefixSize. These are commonly set to 0.7 and 4, respectively. func JaroWinkler(a, b string, boostThreshold float64, prefixSize int) float64 { j := Jaro(a, b) if j <= boostThreshold { return j } prefixSize = int(math.Min(float64(len(a)), math.Min(float64(prefixSize), float64(len(b))))) var prefixMatch float64 for i := 0; i < prefixSize; i++ { if a[i] == b[i] { prefixMatch++ } else { break } } return j + 0.1*prefixMatch*(1.0-j) } ================================================ FILE: vendor/github.com/xrash/smetrics/jaro.go ================================================ package smetrics import ( "math" ) // The Jaro distance. The result is 1 for equal strings, and 0 for completely different strings. func Jaro(a, b string) float64 { // If both strings are zero-length, they are completely equal, // therefore return 1. if len(a) == 0 && len(b) == 0 { return 1 } // If one string is zero-length, strings are completely different, // therefore return 0. if len(a) == 0 || len(b) == 0 { return 0 } // Define the necessary variables for the algorithm. la := float64(len(a)) lb := float64(len(b)) matchRange := int(math.Max(0, math.Floor(math.Max(la, lb)/2.0)-1)) matchesA := make([]bool, len(a)) matchesB := make([]bool, len(b)) var matches float64 = 0 // Step 1: Matches // Loop through each character of the first string, // looking for a matching character in the second string. for i := 0; i < len(a); i++ { start := int(math.Max(0, float64(i-matchRange))) end := int(math.Min(lb-1, float64(i+matchRange))) for j := start; j <= end; j++ { if matchesB[j] { continue } if a[i] == b[j] { matchesA[i] = true matchesB[j] = true matches++ break } } } // If there are no matches, strings are completely different, // therefore return 0. if matches == 0 { return 0 } // Step 2: Transpositions // Loop through the matches' arrays, looking for // unaligned matches. Count the number of unaligned matches. unaligned := 0 j := 0 for i := 0; i < len(a); i++ { if !matchesA[i] { continue } for !matchesB[j] { j++ } if a[i] != b[j] { unaligned++ } j++ } // The number of unaligned matches divided by two, is the number of _transpositions_. transpositions := math.Floor(float64(unaligned / 2)) // Jaro distance is the average between these three numbers: // 1. matches / length of string A // 2. matches / length of string B // 3. (matches - transpositions/matches) // So, all that divided by three is the final result. return ((matches / la) + (matches / lb) + ((matches - transpositions) / matches)) / 3.0 } ================================================ FILE: vendor/github.com/xrash/smetrics/soundex.go ================================================ package smetrics import ( "strings" ) // The Soundex encoding. It is a phonetic algorithm that considers how the words sound in English. Soundex maps a string to a 4-byte code consisting of the first letter of the original string and three numbers. Strings that sound similar should map to the same code. func Soundex(s string) string { b := strings.Builder{} b.Grow(4) p := s[0] if p <= 'z' && p >= 'a' { p -= 32 // convert to uppercase } b.WriteByte(p) n := 0 for i := 1; i < len(s); i++ { c := s[i] if c <= 'z' && c >= 'a' { c -= 32 // convert to uppercase } else if c < 'A' || c > 'Z' { continue } if c == p { continue } p = c switch c { case 'B', 'P', 'F', 'V': c = '1' case 'C', 'S', 'K', 'G', 'J', 'Q', 'X', 'Z': c = '2' case 'D', 'T': c = '3' case 'L': c = '4' case 'M', 'N': c = '5' case 'R': c = '6' default: continue } b.WriteByte(c) n++ if n == 3 { break } } for i := n; i < 3; i++ { b.WriteByte('0') } return b.String() } ================================================ FILE: vendor/github.com/xrash/smetrics/ukkonen.go ================================================ package smetrics import ( "math" ) // The Ukkonen algorithm for calculating the Levenshtein distance. The algorithm is described in http://www.cs.helsinki.fi/u/ukkonen/InfCont85.PDF, or in docs/InfCont85.PDF. It runs on O(t . min(m, n)) where t is the actual distance between strings a and b. It needs O(min(t, m, n)) space. This function might be preferred over WagnerFischer() for *very* similar strings. But test it out yourself. // The first two parameters are the two strings to be compared. The last three parameters are the insertion cost, the deletion cost and the substitution cost. These are normally defined as 1, 1 and 2 respectively. func Ukkonen(a, b string, icost, dcost, scost int) int { var lowerCost int if icost < dcost && icost < scost { lowerCost = icost } else if dcost < scost { lowerCost = dcost } else { lowerCost = scost } infinite := math.MaxInt32 / 2 var r []int var k, kprime, p, t int var ins, del, sub int if len(a) > len(b) { t = (len(a) - len(b) + 1) * lowerCost } else { t = (len(b) - len(a) + 1) * lowerCost } for { if (t / lowerCost) < (len(b) - len(a)) { continue } // This is the right damn thing since the original Ukkonen // paper minimizes the expression result only, but the uncommented version // doesn't need to deal with floats so it's faster. // p = int(math.Floor(0.5*((float64(t)/float64(lowerCost)) - float64(len(b) - len(a))))) p = ((t / lowerCost) - (len(b) - len(a))) / 2 k = -p kprime = k rowlength := (len(b) - len(a)) + (2 * p) r = make([]int, rowlength+2) for i := 0; i < rowlength+2; i++ { r[i] = infinite } for i := 0; i <= len(a); i++ { for j := 0; j <= rowlength; j++ { if i == j+k && i == 0 { r[j] = 0 } else { if j-1 < 0 { ins = infinite } else { ins = r[j-1] + icost } del = r[j+1] + dcost sub = r[j] + scost if i-1 < 0 || i-1 >= len(a) || j+k-1 >= len(b) || j+k-1 < 0 { sub = infinite } else if a[i-1] == b[j+k-1] { sub = r[j] } if ins < del && ins < sub { r[j] = ins } else if del < sub { r[j] = del } else { r[j] = sub } } } k++ } if r[(len(b)-len(a))+(2*p)+kprime] <= t { break } else { t *= 2 } } return r[(len(b)-len(a))+(2*p)+kprime] } ================================================ FILE: vendor/github.com/xrash/smetrics/wagner-fischer.go ================================================ package smetrics // The Wagner-Fischer algorithm for calculating the Levenshtein distance. // The first two parameters are the two strings to be compared. The last three parameters are the insertion cost, the deletion cost and the substitution cost. These are normally defined as 1, 1 and 2 respectively. func WagnerFischer(a, b string, icost, dcost, scost int) int { // Allocate both rows. row1 := make([]int, len(b)+1) row2 := make([]int, len(b)+1) var tmp []int // Initialize the first row. for i := 1; i <= len(b); i++ { row1[i] = i * icost } // For each row... for i := 1; i <= len(a); i++ { row2[0] = i * dcost // For each column... for j := 1; j <= len(b); j++ { if a[i-1] == b[j-1] { row2[j] = row1[j-1] } else { ins := row2[j-1] + icost del := row1[j] + dcost sub := row1[j-1] + scost if ins < del && ins < sub { row2[j] = ins } else if del < sub { row2[j] = del } else { row2[j] = sub } } } // Swap the rows at the end of each row. tmp = row1 row1 = row2 row2 = tmp } // Because we swapped the rows, the final result is in row1 instead of row2. return row1[len(row1)-1] } ================================================ FILE: vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/client.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" import ( "context" "io" "net/http" "net/url" "strings" ) // DefaultClient is the default Client and is used by Get, Head, Post and PostForm. // Please be careful of intitialization order - for example, if you change // the global propagator, the DefaultClient might still be using the old one. var DefaultClient = &http.Client{Transport: NewTransport(http.DefaultTransport)} // Get is a convenient replacement for http.Get that adds a span around the request. func Get(ctx context.Context, targetURL string) (resp *http.Response, err error) { req, err := http.NewRequestWithContext(ctx, "GET", targetURL, nil) if err != nil { return nil, err } return DefaultClient.Do(req) } // Head is a convenient replacement for http.Head that adds a span around the request. func Head(ctx context.Context, targetURL string) (resp *http.Response, err error) { req, err := http.NewRequestWithContext(ctx, "HEAD", targetURL, nil) if err != nil { return nil, err } return DefaultClient.Do(req) } // Post is a convenient replacement for http.Post that adds a span around the request. func Post(ctx context.Context, targetURL, contentType string, body io.Reader) (resp *http.Response, err error) { req, err := http.NewRequestWithContext(ctx, "POST", targetURL, body) if err != nil { return nil, err } req.Header.Set("Content-Type", contentType) return DefaultClient.Do(req) } // PostForm is a convenient replacement for http.PostForm that adds a span around the request. func PostForm(ctx context.Context, targetURL string, data url.Values) (resp *http.Response, err error) { return Post(ctx, targetURL, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) } ================================================ FILE: vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/common.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" import ( "net/http" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" ) // Attribute keys that can be added to a span. const ( ReadBytesKey = attribute.Key("http.read_bytes") // if anything was read from the request body, the total number of bytes read ReadErrorKey = attribute.Key("http.read_error") // If an error occurred while reading a request, the string of the error (io.EOF is not recorded) WroteBytesKey = attribute.Key("http.wrote_bytes") // if anything was written to the response writer, the total number of bytes written WriteErrorKey = attribute.Key("http.write_error") // if an error occurred while writing a reply, the string of the error (io.EOF is not recorded) ) // Server HTTP metrics. const ( RequestCount = "http.server.request_count" // Incoming request count total RequestContentLength = "http.server.request_content_length" // Incoming request bytes total ResponseContentLength = "http.server.response_content_length" // Incoming response bytes total ServerLatency = "http.server.duration" // Incoming end to end duration, microseconds ) // Filter is a predicate used to determine whether a given http.request should // be traced. A Filter must return true if the request should be traced. type Filter func(*http.Request) bool func newTracer(tp trace.TracerProvider) trace.Tracer { return tp.Tracer(instrumentationName, trace.WithInstrumentationVersion(Version())) } ================================================ FILE: vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/config.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" import ( "context" "net/http" "net/http/httptrace" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" ) const ( instrumentationName = "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" ) // config represents the configuration options available for the http.Handler // and http.Transport types. type config struct { ServerName string Tracer trace.Tracer Meter metric.Meter Propagators propagation.TextMapPropagator SpanStartOptions []trace.SpanStartOption PublicEndpoint bool PublicEndpointFn func(*http.Request) bool ReadEvent bool WriteEvent bool Filters []Filter SpanNameFormatter func(string, *http.Request) string ClientTrace func(context.Context) *httptrace.ClientTrace TracerProvider trace.TracerProvider MeterProvider metric.MeterProvider } // Option interface used for setting optional config properties. type Option interface { apply(*config) } type optionFunc func(*config) func (o optionFunc) apply(c *config) { o(c) } // newConfig creates a new config struct and applies opts to it. func newConfig(opts ...Option) *config { c := &config{ Propagators: otel.GetTextMapPropagator(), MeterProvider: otel.GetMeterProvider(), } for _, opt := range opts { opt.apply(c) } // Tracer is only initialized if manually specified. Otherwise, can be passed with the tracing context. if c.TracerProvider != nil { c.Tracer = newTracer(c.TracerProvider) } c.Meter = c.MeterProvider.Meter( instrumentationName, metric.WithInstrumentationVersion(Version()), ) return c } // WithTracerProvider specifies a tracer provider to use for creating a tracer. // If none is specified, the global provider is used. func WithTracerProvider(provider trace.TracerProvider) Option { return optionFunc(func(cfg *config) { if provider != nil { cfg.TracerProvider = provider } }) } // WithMeterProvider specifies a meter provider to use for creating a meter. // If none is specified, the global provider is used. func WithMeterProvider(provider metric.MeterProvider) Option { return optionFunc(func(cfg *config) { if provider != nil { cfg.MeterProvider = provider } }) } // WithPublicEndpoint configures the Handler to link the span with an incoming // span context. If this option is not provided, then the association is a child // association instead of a link. func WithPublicEndpoint() Option { return optionFunc(func(c *config) { c.PublicEndpoint = true }) } // WithPublicEndpointFn runs with every request, and allows conditionnally // configuring the Handler to link the span with an incoming span context. If // this option is not provided or returns false, then the association is a // child association instead of a link. // Note: WithPublicEndpoint takes precedence over WithPublicEndpointFn. func WithPublicEndpointFn(fn func(*http.Request) bool) Option { return optionFunc(func(c *config) { c.PublicEndpointFn = fn }) } // WithPropagators configures specific propagators. If this // option isn't specified, then the global TextMapPropagator is used. func WithPropagators(ps propagation.TextMapPropagator) Option { return optionFunc(func(c *config) { if ps != nil { c.Propagators = ps } }) } // WithSpanOptions configures an additional set of // trace.SpanOptions, which are applied to each new span. func WithSpanOptions(opts ...trace.SpanStartOption) Option { return optionFunc(func(c *config) { c.SpanStartOptions = append(c.SpanStartOptions, opts...) }) } // WithFilter adds a filter to the list of filters used by the handler. // If any filter indicates to exclude a request then the request will not be // traced. All filters must allow a request to be traced for a Span to be created. // If no filters are provided then all requests are traced. // Filters will be invoked for each processed request, it is advised to make them // simple and fast. func WithFilter(f Filter) Option { return optionFunc(func(c *config) { c.Filters = append(c.Filters, f) }) } type event int // Different types of events that can be recorded, see WithMessageEvents. const ( ReadEvents event = iota WriteEvents ) // WithMessageEvents configures the Handler to record the specified events // (span.AddEvent) on spans. By default only summary attributes are added at the // end of the request. // // Valid events are: // - ReadEvents: Record the number of bytes read after every http.Request.Body.Read // using the ReadBytesKey // - WriteEvents: Record the number of bytes written after every http.ResponeWriter.Write // using the WriteBytesKey func WithMessageEvents(events ...event) Option { return optionFunc(func(c *config) { for _, e := range events { switch e { case ReadEvents: c.ReadEvent = true case WriteEvents: c.WriteEvent = true } } }) } // WithSpanNameFormatter takes a function that will be called on every // request and the returned string will become the Span Name. func WithSpanNameFormatter(f func(operation string, r *http.Request) string) Option { return optionFunc(func(c *config) { c.SpanNameFormatter = f }) } // WithClientTrace takes a function that returns client trace instance that will be // applied to the requests sent through the otelhttp Transport. func WithClientTrace(f func(context.Context) *httptrace.ClientTrace) Option { return optionFunc(func(c *config) { c.ClientTrace = f }) } // WithServerName returns an Option that sets the name of the (virtual) server // handling requests. func WithServerName(server string) Option { return optionFunc(func(c *config) { c.ServerName = server }) } ================================================ FILE: vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/doc.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package otelhttp provides an http.Handler and functions that are intended // to be used to add tracing by wrapping existing handlers (with Handler) and // routes WithRouteTag. package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" ================================================ FILE: vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/handler.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" import ( "io" "net/http" "time" "github.com/felixge/httpsnoop" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/propagation" semconv "go.opentelemetry.io/otel/semconv/v1.17.0" "go.opentelemetry.io/otel/trace" ) // middleware is an http middleware which wraps the next handler in a span. type middleware struct { operation string server string tracer trace.Tracer meter metric.Meter propagators propagation.TextMapPropagator spanStartOptions []trace.SpanStartOption readEvent bool writeEvent bool filters []Filter spanNameFormatter func(string, *http.Request) string counters map[string]metric.Int64Counter valueRecorders map[string]metric.Float64Histogram publicEndpoint bool publicEndpointFn func(*http.Request) bool } func defaultHandlerFormatter(operation string, _ *http.Request) string { return operation } // NewHandler wraps the passed handler in a span named after the operation and // enriches it with metrics. func NewHandler(handler http.Handler, operation string, opts ...Option) http.Handler { return NewMiddleware(operation, opts...)(handler) } // NewMiddleware returns a tracing and metrics instrumentation middleware. // The handler returned by the middleware wraps a handler // in a span named after the operation and enriches it with metrics. func NewMiddleware(operation string, opts ...Option) func(http.Handler) http.Handler { h := middleware{ operation: operation, } defaultOpts := []Option{ WithSpanOptions(trace.WithSpanKind(trace.SpanKindServer)), WithSpanNameFormatter(defaultHandlerFormatter), } c := newConfig(append(defaultOpts, opts...)...) h.configure(c) h.createMeasures() return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { h.serveHTTP(w, r, next) }) } } func (h *middleware) configure(c *config) { h.tracer = c.Tracer h.meter = c.Meter h.propagators = c.Propagators h.spanStartOptions = c.SpanStartOptions h.readEvent = c.ReadEvent h.writeEvent = c.WriteEvent h.filters = c.Filters h.spanNameFormatter = c.SpanNameFormatter h.publicEndpoint = c.PublicEndpoint h.publicEndpointFn = c.PublicEndpointFn h.server = c.ServerName } func handleErr(err error) { if err != nil { otel.Handle(err) } } func (h *middleware) createMeasures() { h.counters = make(map[string]metric.Int64Counter) h.valueRecorders = make(map[string]metric.Float64Histogram) requestBytesCounter, err := h.meter.Int64Counter(RequestContentLength) handleErr(err) responseBytesCounter, err := h.meter.Int64Counter(ResponseContentLength) handleErr(err) serverLatencyMeasure, err := h.meter.Float64Histogram(ServerLatency) handleErr(err) h.counters[RequestContentLength] = requestBytesCounter h.counters[ResponseContentLength] = responseBytesCounter h.valueRecorders[ServerLatency] = serverLatencyMeasure } // serveHTTP sets up tracing and calls the given next http.Handler with the span // context injected into the request context. func (h *middleware) serveHTTP(w http.ResponseWriter, r *http.Request, next http.Handler) { requestStartTime := time.Now() for _, f := range h.filters { if !f(r) { // Simply pass through to the handler if a filter rejects the request next.ServeHTTP(w, r) return } } ctx := h.propagators.Extract(r.Context(), propagation.HeaderCarrier(r.Header)) opts := []trace.SpanStartOption{ trace.WithAttributes(semconvutil.HTTPServerRequest(h.server, r)...), } if h.server != "" { hostAttr := semconv.NetHostName(h.server) opts = append(opts, trace.WithAttributes(hostAttr)) } opts = append(opts, h.spanStartOptions...) if h.publicEndpoint || (h.publicEndpointFn != nil && h.publicEndpointFn(r.WithContext(ctx))) { opts = append(opts, trace.WithNewRoot()) // Linking incoming span context if any for public endpoint. if s := trace.SpanContextFromContext(ctx); s.IsValid() && s.IsRemote() { opts = append(opts, trace.WithLinks(trace.Link{SpanContext: s})) } } tracer := h.tracer if tracer == nil { if span := trace.SpanFromContext(r.Context()); span.SpanContext().IsValid() { tracer = newTracer(span.TracerProvider()) } else { tracer = newTracer(otel.GetTracerProvider()) } } ctx, span := tracer.Start(ctx, h.spanNameFormatter(h.operation, r), opts...) defer span.End() readRecordFunc := func(int64) {} if h.readEvent { readRecordFunc = func(n int64) { span.AddEvent("read", trace.WithAttributes(ReadBytesKey.Int64(n))) } } var bw bodyWrapper // if request body is nil or NoBody, we don't want to mutate the body as it // will affect the identity of it in an unforeseeable way because we assert // ReadCloser fulfills a certain interface and it is indeed nil or NoBody. if r.Body != nil && r.Body != http.NoBody { bw.ReadCloser = r.Body bw.record = readRecordFunc r.Body = &bw } writeRecordFunc := func(int64) {} if h.writeEvent { writeRecordFunc = func(n int64) { span.AddEvent("write", trace.WithAttributes(WroteBytesKey.Int64(n))) } } rww := &respWriterWrapper{ ResponseWriter: w, record: writeRecordFunc, ctx: ctx, props: h.propagators, statusCode: http.StatusOK, // default status code in case the Handler doesn't write anything } // Wrap w to use our ResponseWriter methods while also exposing // other interfaces that w may implement (http.CloseNotifier, // http.Flusher, http.Hijacker, http.Pusher, io.ReaderFrom). w = httpsnoop.Wrap(w, httpsnoop.Hooks{ Header: func(httpsnoop.HeaderFunc) httpsnoop.HeaderFunc { return rww.Header }, Write: func(httpsnoop.WriteFunc) httpsnoop.WriteFunc { return rww.Write }, WriteHeader: func(httpsnoop.WriteHeaderFunc) httpsnoop.WriteHeaderFunc { return rww.WriteHeader }, }) labeler := &Labeler{} ctx = injectLabeler(ctx, labeler) next.ServeHTTP(w, r.WithContext(ctx)) setAfterServeAttributes(span, bw.read, rww.written, rww.statusCode, bw.err, rww.err) // Add metrics attributes := append(labeler.Get(), semconvutil.HTTPServerRequestMetrics(h.server, r)...) if rww.statusCode > 0 { attributes = append(attributes, semconv.HTTPStatusCode(rww.statusCode)) } o := metric.WithAttributes(attributes...) h.counters[RequestContentLength].Add(ctx, bw.read, o) h.counters[ResponseContentLength].Add(ctx, rww.written, o) // Use floating point division here for higher precision (instead of Millisecond method). elapsedTime := float64(time.Since(requestStartTime)) / float64(time.Millisecond) h.valueRecorders[ServerLatency].Record(ctx, elapsedTime, o) } func setAfterServeAttributes(span trace.Span, read, wrote int64, statusCode int, rerr, werr error) { attributes := []attribute.KeyValue{} // TODO: Consider adding an event after each read and write, possibly as an // option (defaulting to off), so as to not create needlessly verbose spans. if read > 0 { attributes = append(attributes, ReadBytesKey.Int64(read)) } if rerr != nil && rerr != io.EOF { attributes = append(attributes, ReadErrorKey.String(rerr.Error())) } if wrote > 0 { attributes = append(attributes, WroteBytesKey.Int64(wrote)) } if statusCode > 0 { attributes = append(attributes, semconv.HTTPStatusCode(statusCode)) } span.SetStatus(semconvutil.HTTPServerStatus(statusCode)) if werr != nil && werr != io.EOF { attributes = append(attributes, WriteErrorKey.String(werr.Error())) } span.SetAttributes(attributes...) } // WithRouteTag annotates spans and metrics with the provided route name // with HTTP route attribute. func WithRouteTag(route string, h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { attr := semconv.HTTPRouteKey.String(route) span := trace.SpanFromContext(r.Context()) span.SetAttributes(attr) labeler, _ := LabelerFromContext(r.Context()) labeler.Add(attr) h.ServeHTTP(w, r) }) } ================================================ FILE: vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil/gen.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package semconvutil // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil" // Generate semconvutil package: //go:generate gotmpl --body=../../../../../../internal/shared/semconvutil/httpconv_test.go.tmpl "--data={}" --out=httpconv_test.go //go:generate gotmpl --body=../../../../../../internal/shared/semconvutil/httpconv.go.tmpl "--data={}" --out=httpconv.go //go:generate gotmpl --body=../../../../../../internal/shared/semconvutil/netconv_test.go.tmpl "--data={}" --out=netconv_test.go //go:generate gotmpl --body=../../../../../../internal/shared/semconvutil/netconv.go.tmpl "--data={}" --out=netconv.go ================================================ FILE: vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil/httpconv.go ================================================ // Code created by gotmpl. DO NOT MODIFY. // source: internal/shared/semconvutil/httpconv.go.tmpl // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package semconvutil // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil" import ( "fmt" "net/http" "strings" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" semconv "go.opentelemetry.io/otel/semconv/v1.17.0" ) // HTTPClientResponse returns trace attributes for an HTTP response received by a // client from a server. It will return the following attributes if the related // values are defined in resp: "http.status.code", // "http.response_content_length". // // This does not add all OpenTelemetry required attributes for an HTTP event, // it assumes ClientRequest was used to create the span with a complete set of // attributes. If a complete set of attributes can be generated using the // request contained in resp. For example: // // append(HTTPClientResponse(resp), ClientRequest(resp.Request)...) func HTTPClientResponse(resp *http.Response) []attribute.KeyValue { return hc.ClientResponse(resp) } // HTTPClientRequest returns trace attributes for an HTTP request made by a client. // The following attributes are always returned: "http.url", "http.flavor", // "http.method", "net.peer.name". The following attributes are returned if the // related values are defined in req: "net.peer.port", "http.user_agent", // "http.request_content_length", "enduser.id". func HTTPClientRequest(req *http.Request) []attribute.KeyValue { return hc.ClientRequest(req) } // HTTPClientStatus returns a span status code and message for an HTTP status code // value received by a client. func HTTPClientStatus(code int) (codes.Code, string) { return hc.ClientStatus(code) } // HTTPServerRequest returns trace attributes for an HTTP request received by a // server. // // The server must be the primary server name if it is known. For example this // would be the ServerName directive // (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache // server, and the server_name directive // (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an // nginx server. More generically, the primary server name would be the host // header value that matches the default virtual host of an HTTP server. It // should include the host identifier and if a port is used to route to the // server that port identifier should be included as an appropriate port // suffix. // // If the primary server name is not known, server should be an empty string. // The req Host will be used to determine the server instead. // // The following attributes are always returned: "http.method", "http.scheme", // "http.flavor", "http.target", "net.host.name". The following attributes are // returned if they related values are defined in req: "net.host.port", // "net.sock.peer.addr", "net.sock.peer.port", "http.user_agent", "enduser.id", // "http.client_ip". func HTTPServerRequest(server string, req *http.Request) []attribute.KeyValue { return hc.ServerRequest(server, req) } // HTTPServerRequestMetrics returns metric attributes for an HTTP request received by a // server. // // The server must be the primary server name if it is known. For example this // would be the ServerName directive // (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache // server, and the server_name directive // (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an // nginx server. More generically, the primary server name would be the host // header value that matches the default virtual host of an HTTP server. It // should include the host identifier and if a port is used to route to the // server that port identifier should be included as an appropriate port // suffix. // // If the primary server name is not known, server should be an empty string. // The req Host will be used to determine the server instead. // // The following attributes are always returned: "http.method", "http.scheme", // "http.flavor", "net.host.name". The following attributes are // returned if they related values are defined in req: "net.host.port". func HTTPServerRequestMetrics(server string, req *http.Request) []attribute.KeyValue { return hc.ServerRequestMetrics(server, req) } // HTTPServerStatus returns a span status code and message for an HTTP status code // value returned by a server. Status codes in the 400-499 range are not // returned as errors. func HTTPServerStatus(code int) (codes.Code, string) { return hc.ServerStatus(code) } // HTTPRequestHeader returns the contents of h as attributes. // // Instrumentation should require an explicit configuration of which headers to // captured and then prune what they pass here. Including all headers can be a // security risk - explicit configuration helps avoid leaking sensitive // information. // // The User-Agent header is already captured in the http.user_agent attribute // from ClientRequest and ServerRequest. Instrumentation may provide an option // to capture that header here even though it is not recommended. Otherwise, // instrumentation should filter that out of what is passed. func HTTPRequestHeader(h http.Header) []attribute.KeyValue { return hc.RequestHeader(h) } // HTTPResponseHeader returns the contents of h as attributes. // // Instrumentation should require an explicit configuration of which headers to // captured and then prune what they pass here. Including all headers can be a // security risk - explicit configuration helps avoid leaking sensitive // information. // // The User-Agent header is already captured in the http.user_agent attribute // from ClientRequest and ServerRequest. Instrumentation may provide an option // to capture that header here even though it is not recommended. Otherwise, // instrumentation should filter that out of what is passed. func HTTPResponseHeader(h http.Header) []attribute.KeyValue { return hc.ResponseHeader(h) } // httpConv are the HTTP semantic convention attributes defined for a version // of the OpenTelemetry specification. type httpConv struct { NetConv *netConv EnduserIDKey attribute.Key HTTPClientIPKey attribute.Key HTTPFlavorKey attribute.Key HTTPMethodKey attribute.Key HTTPRequestContentLengthKey attribute.Key HTTPResponseContentLengthKey attribute.Key HTTPRouteKey attribute.Key HTTPSchemeHTTP attribute.KeyValue HTTPSchemeHTTPS attribute.KeyValue HTTPStatusCodeKey attribute.Key HTTPTargetKey attribute.Key HTTPURLKey attribute.Key HTTPUserAgentKey attribute.Key } var hc = &httpConv{ NetConv: nc, EnduserIDKey: semconv.EnduserIDKey, HTTPClientIPKey: semconv.HTTPClientIPKey, HTTPFlavorKey: semconv.HTTPFlavorKey, HTTPMethodKey: semconv.HTTPMethodKey, HTTPRequestContentLengthKey: semconv.HTTPRequestContentLengthKey, HTTPResponseContentLengthKey: semconv.HTTPResponseContentLengthKey, HTTPRouteKey: semconv.HTTPRouteKey, HTTPSchemeHTTP: semconv.HTTPSchemeHTTP, HTTPSchemeHTTPS: semconv.HTTPSchemeHTTPS, HTTPStatusCodeKey: semconv.HTTPStatusCodeKey, HTTPTargetKey: semconv.HTTPTargetKey, HTTPURLKey: semconv.HTTPURLKey, HTTPUserAgentKey: semconv.HTTPUserAgentKey, } // ClientResponse returns attributes for an HTTP response received by a client // from a server. The following attributes are returned if the related values // are defined in resp: "http.status.code", "http.response_content_length". // // This does not add all OpenTelemetry required attributes for an HTTP event, // it assumes ClientRequest was used to create the span with a complete set of // attributes. If a complete set of attributes can be generated using the // request contained in resp. For example: // // append(ClientResponse(resp), ClientRequest(resp.Request)...) func (c *httpConv) ClientResponse(resp *http.Response) []attribute.KeyValue { var n int if resp.StatusCode > 0 { n++ } if resp.ContentLength > 0 { n++ } attrs := make([]attribute.KeyValue, 0, n) if resp.StatusCode > 0 { attrs = append(attrs, c.HTTPStatusCodeKey.Int(resp.StatusCode)) } if resp.ContentLength > 0 { attrs = append(attrs, c.HTTPResponseContentLengthKey.Int(int(resp.ContentLength))) } return attrs } // ClientRequest returns attributes for an HTTP request made by a client. The // following attributes are always returned: "http.url", "http.flavor", // "http.method", "net.peer.name". The following attributes are returned if the // related values are defined in req: "net.peer.port", "http.user_agent", // "http.request_content_length", "enduser.id". func (c *httpConv) ClientRequest(req *http.Request) []attribute.KeyValue { n := 3 // URL, peer name, proto, and method. var h string if req.URL != nil { h = req.URL.Host } peer, p := firstHostPort(h, req.Header.Get("Host")) port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", p) if port > 0 { n++ } useragent := req.UserAgent() if useragent != "" { n++ } if req.ContentLength > 0 { n++ } userID, _, hasUserID := req.BasicAuth() if hasUserID { n++ } attrs := make([]attribute.KeyValue, 0, n) attrs = append(attrs, c.method(req.Method)) attrs = append(attrs, c.flavor(req.Proto)) var u string if req.URL != nil { // Remove any username/password info that may be in the URL. userinfo := req.URL.User req.URL.User = nil u = req.URL.String() // Restore any username/password info that was removed. req.URL.User = userinfo } attrs = append(attrs, c.HTTPURLKey.String(u)) attrs = append(attrs, c.NetConv.PeerName(peer)) if port > 0 { attrs = append(attrs, c.NetConv.PeerPort(port)) } if useragent != "" { attrs = append(attrs, c.HTTPUserAgentKey.String(useragent)) } if l := req.ContentLength; l > 0 { attrs = append(attrs, c.HTTPRequestContentLengthKey.Int64(l)) } if hasUserID { attrs = append(attrs, c.EnduserIDKey.String(userID)) } return attrs } // ServerRequest returns attributes for an HTTP request received by a server. // // The server must be the primary server name if it is known. For example this // would be the ServerName directive // (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache // server, and the server_name directive // (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an // nginx server. More generically, the primary server name would be the host // header value that matches the default virtual host of an HTTP server. It // should include the host identifier and if a port is used to route to the // server that port identifier should be included as an appropriate port // suffix. // // If the primary server name is not known, server should be an empty string. // The req Host will be used to determine the server instead. // // The following attributes are always returned: "http.method", "http.scheme", // "http.flavor", "http.target", "net.host.name". The following attributes are // returned if they related values are defined in req: "net.host.port", // "net.sock.peer.addr", "net.sock.peer.port", "http.user_agent", "enduser.id", // "http.client_ip". func (c *httpConv) ServerRequest(server string, req *http.Request) []attribute.KeyValue { // TODO: This currently does not add the specification required // `http.target` attribute. It has too high of a cardinality to safely be // added. An alternate should be added, or this comment removed, when it is // addressed by the specification. If it is ultimately decided to continue // not including the attribute, the HTTPTargetKey field of the httpConv // should be removed as well. n := 4 // Method, scheme, proto, and host name. var host string var p int if server == "" { host, p = splitHostPort(req.Host) } else { // Prioritize the primary server name. host, p = splitHostPort(server) if p < 0 { _, p = splitHostPort(req.Host) } } hostPort := requiredHTTPPort(req.TLS != nil, p) if hostPort > 0 { n++ } peer, peerPort := splitHostPort(req.RemoteAddr) if peer != "" { n++ if peerPort > 0 { n++ } } useragent := req.UserAgent() if useragent != "" { n++ } userID, _, hasUserID := req.BasicAuth() if hasUserID { n++ } clientIP := serverClientIP(req.Header.Get("X-Forwarded-For")) if clientIP != "" { n++ } attrs := make([]attribute.KeyValue, 0, n) attrs = append(attrs, c.method(req.Method)) attrs = append(attrs, c.scheme(req.TLS != nil)) attrs = append(attrs, c.flavor(req.Proto)) attrs = append(attrs, c.NetConv.HostName(host)) if hostPort > 0 { attrs = append(attrs, c.NetConv.HostPort(hostPort)) } if peer != "" { // The Go HTTP server sets RemoteAddr to "IP:port", this will not be a // file-path that would be interpreted with a sock family. attrs = append(attrs, c.NetConv.SockPeerAddr(peer)) if peerPort > 0 { attrs = append(attrs, c.NetConv.SockPeerPort(peerPort)) } } if useragent != "" { attrs = append(attrs, c.HTTPUserAgentKey.String(useragent)) } if hasUserID { attrs = append(attrs, c.EnduserIDKey.String(userID)) } if clientIP != "" { attrs = append(attrs, c.HTTPClientIPKey.String(clientIP)) } return attrs } // ServerRequestMetrics returns metric attributes for an HTTP request received // by a server. // // The server must be the primary server name if it is known. For example this // would be the ServerName directive // (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache // server, and the server_name directive // (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an // nginx server. More generically, the primary server name would be the host // header value that matches the default virtual host of an HTTP server. It // should include the host identifier and if a port is used to route to the // server that port identifier should be included as an appropriate port // suffix. // // If the primary server name is not known, server should be an empty string. // The req Host will be used to determine the server instead. // // The following attributes are always returned: "http.method", "http.scheme", // "http.flavor", "net.host.name". The following attributes are // returned if they related values are defined in req: "net.host.port". func (c *httpConv) ServerRequestMetrics(server string, req *http.Request) []attribute.KeyValue { // TODO: This currently does not add the specification required // `http.target` attribute. It has too high of a cardinality to safely be // added. An alternate should be added, or this comment removed, when it is // addressed by the specification. If it is ultimately decided to continue // not including the attribute, the HTTPTargetKey field of the httpConv // should be removed as well. n := 4 // Method, scheme, proto, and host name. var host string var p int if server == "" { host, p = splitHostPort(req.Host) } else { // Prioritize the primary server name. host, p = splitHostPort(server) if p < 0 { _, p = splitHostPort(req.Host) } } hostPort := requiredHTTPPort(req.TLS != nil, p) if hostPort > 0 { n++ } attrs := make([]attribute.KeyValue, 0, n) attrs = append(attrs, c.methodMetric(req.Method)) attrs = append(attrs, c.scheme(req.TLS != nil)) attrs = append(attrs, c.flavor(req.Proto)) attrs = append(attrs, c.NetConv.HostName(host)) if hostPort > 0 { attrs = append(attrs, c.NetConv.HostPort(hostPort)) } return attrs } func (c *httpConv) method(method string) attribute.KeyValue { if method == "" { return c.HTTPMethodKey.String(http.MethodGet) } return c.HTTPMethodKey.String(method) } func (c *httpConv) methodMetric(method string) attribute.KeyValue { method = strings.ToUpper(method) switch method { case http.MethodConnect, http.MethodDelete, http.MethodGet, http.MethodHead, http.MethodOptions, http.MethodPatch, http.MethodPost, http.MethodPut, http.MethodTrace: default: method = "_OTHER" } return c.HTTPMethodKey.String(method) } func (c *httpConv) scheme(https bool) attribute.KeyValue { // nolint:revive if https { return c.HTTPSchemeHTTPS } return c.HTTPSchemeHTTP } func (c *httpConv) flavor(proto string) attribute.KeyValue { switch proto { case "HTTP/1.0": return c.HTTPFlavorKey.String("1.0") case "HTTP/1.1": return c.HTTPFlavorKey.String("1.1") case "HTTP/2": return c.HTTPFlavorKey.String("2.0") case "HTTP/3": return c.HTTPFlavorKey.String("3.0") default: return c.HTTPFlavorKey.String(proto) } } func serverClientIP(xForwardedFor string) string { if idx := strings.Index(xForwardedFor, ","); idx >= 0 { xForwardedFor = xForwardedFor[:idx] } return xForwardedFor } func requiredHTTPPort(https bool, port int) int { // nolint:revive if https { if port > 0 && port != 443 { return port } } else { if port > 0 && port != 80 { return port } } return -1 } // Return the request host and port from the first non-empty source. func firstHostPort(source ...string) (host string, port int) { for _, hostport := range source { host, port = splitHostPort(hostport) if host != "" || port > 0 { break } } return } // RequestHeader returns the contents of h as OpenTelemetry attributes. func (c *httpConv) RequestHeader(h http.Header) []attribute.KeyValue { return c.header("http.request.header", h) } // ResponseHeader returns the contents of h as OpenTelemetry attributes. func (c *httpConv) ResponseHeader(h http.Header) []attribute.KeyValue { return c.header("http.response.header", h) } func (c *httpConv) header(prefix string, h http.Header) []attribute.KeyValue { key := func(k string) attribute.Key { k = strings.ToLower(k) k = strings.ReplaceAll(k, "-", "_") k = fmt.Sprintf("%s.%s", prefix, k) return attribute.Key(k) } attrs := make([]attribute.KeyValue, 0, len(h)) for k, v := range h { attrs = append(attrs, key(k).StringSlice(v)) } return attrs } // ClientStatus returns a span status code and message for an HTTP status code // value received by a client. func (c *httpConv) ClientStatus(code int) (codes.Code, string) { if code < 100 || code >= 600 { return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code) } if code >= 400 { return codes.Error, "" } return codes.Unset, "" } // ServerStatus returns a span status code and message for an HTTP status code // value returned by a server. Status codes in the 400-499 range are not // returned as errors. func (c *httpConv) ServerStatus(code int) (codes.Code, string) { if code < 100 || code >= 600 { return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code) } if code >= 500 { return codes.Error, "" } return codes.Unset, "" } ================================================ FILE: vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil/netconv.go ================================================ // Code created by gotmpl. DO NOT MODIFY. // source: internal/shared/semconvutil/netconv.go.tmpl // Copyright The OpenTelemetry Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package semconvutil // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil" import ( "net" "strconv" "strings" "go.opentelemetry.io/otel/attribute" semconv "go.opentelemetry.io/otel/semconv/v1.17.0" ) // NetTransport returns a trace attribute describing the transport protocol of the // passed network. See the net.Dial for information about acceptable network // values. func NetTransport(network string) attribute.KeyValue { return nc.Transport(network) } // NetClient returns trace attributes for a client network connection to address. // See net.Dial for information about acceptable address values, address should // be the same as the one used to create conn. If conn is nil, only network // peer attributes will be returned that describe address. Otherwise, the // socket level information about conn will also be included. func NetClient(address string, conn net.Conn) []attribute.KeyValue { return nc.Client(address, conn) } // NetServer returns trace attributes for a network listener listening at address. // See net.Listen for information about acceptable address values, address // should be the same as the one used to create ln. If ln is nil, only network // host attributes will be returned that describe address. Otherwise, the // socket level information about ln will also be included. func NetServer(address string, ln net.Listener) []attribute.KeyValue { return nc.Server(address, ln) } // netConv are the network semantic convention attributes defined for a version // of the OpenTelemetry specification. type netConv struct { NetHostNameKey attribute.Key NetHostPortKey attribute.Key NetPeerNameKey attribute.Key NetPeerPortKey attribute.Key NetSockFamilyKey attribute.Key NetSockPeerAddrKey attribute.Key NetSockPeerPortKey attribute.Key NetSockHostAddrKey attribute.Key NetSockHostPortKey attribute.Key NetTransportOther attribute.KeyValue NetTransportTCP attribute.KeyValue NetTransportUDP attribute.KeyValue NetTransportInProc attribute.KeyValue } var nc = &netConv{ NetHostNameKey: semconv.NetHostNameKey, NetHostPortKey: semconv.NetHostPortKey, NetPeerNameKey: semconv.NetPeerNameKey, NetPeerPortKey: semconv.NetPeerPortKey, NetSockFamilyKey: semconv.NetSockFamilyKey, NetSockPeerAddrKey: semconv.NetSockPeerAddrKey, NetSockPeerPortKey: semconv.NetSockPeerPortKey, NetSockHostAddrKey: semconv.NetSockHostAddrKey, NetSockHostPortKey: semconv.NetSockHostPortKey, NetTransportOther: semconv.NetTransportOther, NetTransportTCP: semconv.NetTransportTCP, NetTransportUDP: semconv.NetTransportUDP, NetTransportInProc: semconv.NetTransportInProc, } func (c *netConv) Transport(network string) attribute.KeyValue { switch network { case "tcp", "tcp4", "tcp6": return c.NetTransportTCP case "udp", "udp4", "udp6": return c.NetTransportUDP case "unix", "unixgram", "unixpacket": return c.NetTransportInProc default: // "ip:*", "ip4:*", and "ip6:*" all are considered other. return c.NetTransportOther } } // Host returns attributes for a network host address. func (c *netConv) Host(address string) []attribute.KeyValue { h, p := splitHostPort(address) var n int if h != "" { n++ if p > 0 { n++ } } if n == 0 { return nil } attrs := make([]attribute.KeyValue, 0, n) attrs = append(attrs, c.HostName(h)) if p > 0 { attrs = append(attrs, c.HostPort(int(p))) } return attrs } // Server returns attributes for a network listener listening at address. See // net.Listen for information about acceptable address values, address should // be the same as the one used to create ln. If ln is nil, only network host // attributes will be returned that describe address. Otherwise, the socket // level information about ln will also be included. func (c *netConv) Server(address string, ln net.Listener) []attribute.KeyValue { if ln == nil { return c.Host(address) } lAddr := ln.Addr() if lAddr == nil { return c.Host(address) } hostName, hostPort := splitHostPort(address) sockHostAddr, sockHostPort := splitHostPort(lAddr.String()) network := lAddr.Network() sockFamily := family(network, sockHostAddr) n := nonZeroStr(hostName, network, sockHostAddr, sockFamily) n += positiveInt(hostPort, sockHostPort) attr := make([]attribute.KeyValue, 0, n) if hostName != "" { attr = append(attr, c.HostName(hostName)) if hostPort > 0 { // Only if net.host.name is set should net.host.port be. attr = append(attr, c.HostPort(hostPort)) } } if network != "" { attr = append(attr, c.Transport(network)) } if sockFamily != "" { attr = append(attr, c.NetSockFamilyKey.String(sockFamily)) } if sockHostAddr != "" { attr = append(attr, c.NetSockHostAddrKey.String(sockHostAddr)) if sockHostPort > 0 { // Only if net.sock.host.addr is set should net.sock.host.port be. attr = append(attr, c.NetSockHostPortKey.Int(sockHostPort)) } } return attr } func (c *netConv) HostName(name string) attribute.KeyValue { return c.NetHostNameKey.String(name) } func (c *netConv) HostPort(port int) attribute.KeyValue { return c.NetHostPortKey.Int(port) } // Client returns attributes for a client network connection to address. See // net.Dial for information about acceptable address values, address should be // the same as the one used to create conn. If conn is nil, only network peer // attributes will be returned that describe address. Otherwise, the socket // level information about conn will also be included. func (c *netConv) Client(address string, conn net.Conn) []attribute.KeyValue { if conn == nil { return c.Peer(address) } lAddr, rAddr := conn.LocalAddr(), conn.RemoteAddr() var network string switch { case lAddr != nil: network = lAddr.Network() case rAddr != nil: network = rAddr.Network() default: return c.Peer(address) } peerName, peerPort := splitHostPort(address) var ( sockFamily string sockPeerAddr string sockPeerPort int sockHostAddr string sockHostPort int ) if lAddr != nil { sockHostAddr, sockHostPort = splitHostPort(lAddr.String()) } if rAddr != nil { sockPeerAddr, sockPeerPort = splitHostPort(rAddr.String()) } switch { case sockHostAddr != "": sockFamily = family(network, sockHostAddr) case sockPeerAddr != "": sockFamily = family(network, sockPeerAddr) } n := nonZeroStr(peerName, network, sockPeerAddr, sockHostAddr, sockFamily) n += positiveInt(peerPort, sockPeerPort, sockHostPort) attr := make([]attribute.KeyValue, 0, n) if peerName != "" { attr = append(attr, c.PeerName(peerName)) if peerPort > 0 { // Only if net.peer.name is set should net.peer.port be. attr = append(attr, c.PeerPort(peerPort)) } } if network != "" { attr = append(attr, c.Transport(network)) } if sockFamily != "" { attr = append(attr, c.NetSockFamilyKey.String(sockFamily)) } if sockPeerAddr != "" { attr = append(attr, c.NetSockPeerAddrKey.String(sockPeerAddr)) if sockPeerPort > 0 { // Only if net.sock.peer.addr is set should net.sock.peer.port be. attr = append(attr, c.NetSockPeerPortKey.Int(sockPeerPort)) } } if sockHostAddr != "" { attr = append(attr, c.NetSockHostAddrKey.String(sockHostAddr)) if sockHostPort > 0 { // Only if net.sock.host.addr is set should net.sock.host.port be. attr = append(attr, c.NetSockHostPortKey.Int(sockHostPort)) } } return attr } func family(network, address string) string { switch network { case "unix", "unixgram", "unixpacket": return "unix" default: if ip := net.ParseIP(address); ip != nil { if ip.To4() == nil { return "inet6" } return "inet" } } return "" } func nonZeroStr(strs ...string) int { var n int for _, str := range strs { if str != "" { n++ } } return n } func positiveInt(ints ...int) int { var n int for _, i := range ints { if i > 0 { n++ } } return n } // Peer returns attributes for a network peer address. func (c *netConv) Peer(address string) []attribute.KeyValue { h, p := splitHostPort(address) var n int if h != "" { n++ if p > 0 { n++ } } if n == 0 { return nil } attrs := make([]attribute.KeyValue, 0, n) attrs = append(attrs, c.PeerName(h)) if p > 0 { attrs = append(attrs, c.PeerPort(int(p))) } return attrs } func (c *netConv) PeerName(name string) attribute.KeyValue { return c.NetPeerNameKey.String(name) } func (c *netConv) PeerPort(port int) attribute.KeyValue { return c.NetPeerPortKey.Int(port) } func (c *netConv) SockPeerAddr(addr string) attribute.KeyValue { return c.NetSockPeerAddrKey.String(addr) } func (c *netConv) SockPeerPort(port int) attribute.KeyValue { return c.NetSockPeerPortKey.Int(port) } // splitHostPort splits a network address hostport of the form "host", // "host%zone", "[host]", "[host%zone], "host:port", "host%zone:port", // "[host]:port", "[host%zone]:port", or ":port" into host or host%zone and // port. // // An empty host is returned if it is not provided or unparsable. A negative // port is returned if it is not provided or unparsable. func splitHostPort(hostport string) (host string, port int) { port = -1 if strings.HasPrefix(hostport, "[") { addrEnd := strings.LastIndex(hostport, "]") if addrEnd < 0 { // Invalid hostport. return } if i := strings.LastIndex(hostport[addrEnd:], ":"); i < 0 { host = hostport[1:addrEnd] return } } else { if i := strings.LastIndex(hostport, ":"); i < 0 { host = hostport return } } host, pStr, err := net.SplitHostPort(hostport) if err != nil { return } p, err := strconv.ParseUint(pStr, 10, 16) if err != nil { return } return host, int(p) } ================================================ FILE: vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/labeler.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" import ( "context" "sync" "go.opentelemetry.io/otel/attribute" ) // Labeler is used to allow instrumented HTTP handlers to add custom attributes to // the metrics recorded by the net/http instrumentation. type Labeler struct { mu sync.Mutex attributes []attribute.KeyValue } // Add attributes to a Labeler. func (l *Labeler) Add(ls ...attribute.KeyValue) { l.mu.Lock() defer l.mu.Unlock() l.attributes = append(l.attributes, ls...) } // Get returns a copy of the attributes added to the Labeler. func (l *Labeler) Get() []attribute.KeyValue { l.mu.Lock() defer l.mu.Unlock() ret := make([]attribute.KeyValue, len(l.attributes)) copy(ret, l.attributes) return ret } type labelerContextKeyType int const lablelerContextKey labelerContextKeyType = 0 func injectLabeler(ctx context.Context, l *Labeler) context.Context { return context.WithValue(ctx, lablelerContextKey, l) } // LabelerFromContext retrieves a Labeler instance from the provided context if // one is available. If no Labeler was found in the provided context a new, empty // Labeler is returned and the second return value is false. In this case it is // safe to use the Labeler but any attributes added to it will not be used. func LabelerFromContext(ctx context.Context) (*Labeler, bool) { l, ok := ctx.Value(lablelerContextKey).(*Labeler) if !ok { l = &Labeler{} } return l, ok } ================================================ FILE: vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/transport.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" import ( "context" "io" "net/http" "net/http/httptrace" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" ) // Transport implements the http.RoundTripper interface and wraps // outbound HTTP(S) requests with a span. type Transport struct { rt http.RoundTripper tracer trace.Tracer propagators propagation.TextMapPropagator spanStartOptions []trace.SpanStartOption filters []Filter spanNameFormatter func(string, *http.Request) string clientTrace func(context.Context) *httptrace.ClientTrace } var _ http.RoundTripper = &Transport{} // NewTransport wraps the provided http.RoundTripper with one that // starts a span and injects the span context into the outbound request headers. // // If the provided http.RoundTripper is nil, http.DefaultTransport will be used // as the base http.RoundTripper. func NewTransport(base http.RoundTripper, opts ...Option) *Transport { if base == nil { base = http.DefaultTransport } t := Transport{ rt: base, } defaultOpts := []Option{ WithSpanOptions(trace.WithSpanKind(trace.SpanKindClient)), WithSpanNameFormatter(defaultTransportFormatter), } c := newConfig(append(defaultOpts, opts...)...) t.applyConfig(c) return &t } func (t *Transport) applyConfig(c *config) { t.tracer = c.Tracer t.propagators = c.Propagators t.spanStartOptions = c.SpanStartOptions t.filters = c.Filters t.spanNameFormatter = c.SpanNameFormatter t.clientTrace = c.ClientTrace } func defaultTransportFormatter(_ string, r *http.Request) string { return "HTTP " + r.Method } // RoundTrip creates a Span and propagates its context via the provided request's headers // before handing the request to the configured base RoundTripper. The created span will // end when the response body is closed or when a read from the body returns io.EOF. func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) { for _, f := range t.filters { if !f(r) { // Simply pass through to the base RoundTripper if a filter rejects the request return t.rt.RoundTrip(r) } } tracer := t.tracer if tracer == nil { if span := trace.SpanFromContext(r.Context()); span.SpanContext().IsValid() { tracer = newTracer(span.TracerProvider()) } else { tracer = newTracer(otel.GetTracerProvider()) } } opts := append([]trace.SpanStartOption{}, t.spanStartOptions...) // start with the configured options ctx, span := tracer.Start(r.Context(), t.spanNameFormatter("", r), opts...) if t.clientTrace != nil { ctx = httptrace.WithClientTrace(ctx, t.clientTrace(ctx)) } r = r.Clone(ctx) // According to RoundTripper spec, we shouldn't modify the origin request. span.SetAttributes(semconvutil.HTTPClientRequest(r)...) t.propagators.Inject(ctx, propagation.HeaderCarrier(r.Header)) res, err := t.rt.RoundTrip(r) if err != nil { span.RecordError(err) span.SetStatus(codes.Error, err.Error()) span.End() return res, err } span.SetAttributes(semconvutil.HTTPClientResponse(res)...) span.SetStatus(semconvutil.HTTPClientStatus(res.StatusCode)) res.Body = newWrappedBody(span, res.Body) return res, err } // newWrappedBody returns a new and appropriately scoped *wrappedBody as an // io.ReadCloser. If the passed body implements io.Writer, the returned value // will implement io.ReadWriteCloser. func newWrappedBody(span trace.Span, body io.ReadCloser) io.ReadCloser { // The successful protocol switch responses will have a body that // implement an io.ReadWriteCloser. Ensure this interface type continues // to be satisfied if that is the case. if _, ok := body.(io.ReadWriteCloser); ok { return &wrappedBody{span: span, body: body} } // Remove the implementation of the io.ReadWriteCloser and only implement // the io.ReadCloser. return struct{ io.ReadCloser }{&wrappedBody{span: span, body: body}} } // wrappedBody is the response body type returned by the transport // instrumentation to complete a span. Errors encountered when using the // response body are recorded in span tracking the response. // // The span tracking the response is ended when this body is closed. // // If the response body implements the io.Writer interface (i.e. for // successful protocol switches), the wrapped body also will. type wrappedBody struct { span trace.Span body io.ReadCloser } var _ io.ReadWriteCloser = &wrappedBody{} func (wb *wrappedBody) Write(p []byte) (int, error) { // This will not panic given the guard in newWrappedBody. n, err := wb.body.(io.Writer).Write(p) if err != nil { wb.span.RecordError(err) wb.span.SetStatus(codes.Error, err.Error()) } return n, err } func (wb *wrappedBody) Read(b []byte) (int, error) { n, err := wb.body.Read(b) switch err { case nil: // nothing to do here but fall through to the return case io.EOF: wb.span.End() default: wb.span.RecordError(err) wb.span.SetStatus(codes.Error, err.Error()) } return n, err } func (wb *wrappedBody) Close() error { wb.span.End() if wb.body != nil { return wb.body.Close() } return nil } ================================================ FILE: vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/version.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" // Version is the current release version of the otelhttp instrumentation. func Version() string { return "0.45.0" // This string is updated by the pre_release.sh script during release } // SemVersion is the semantic version to be supplied to tracer/meter creation. // // Deprecated: Use [Version] instead. func SemVersion() string { return Version() } ================================================ FILE: vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/wrap.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" import ( "context" "io" "net/http" "go.opentelemetry.io/otel/propagation" ) var _ io.ReadCloser = &bodyWrapper{} // bodyWrapper wraps a http.Request.Body (an io.ReadCloser) to track the number // of bytes read and the last error. type bodyWrapper struct { io.ReadCloser record func(n int64) // must not be nil read int64 err error } func (w *bodyWrapper) Read(b []byte) (int, error) { n, err := w.ReadCloser.Read(b) n1 := int64(n) w.read += n1 w.err = err w.record(n1) return n, err } func (w *bodyWrapper) Close() error { return w.ReadCloser.Close() } var _ http.ResponseWriter = &respWriterWrapper{} // respWriterWrapper wraps a http.ResponseWriter in order to track the number of // bytes written, the last error, and to catch the first written statusCode. // TODO: The wrapped http.ResponseWriter doesn't implement any of the optional // types (http.Hijacker, http.Pusher, http.CloseNotifier, http.Flusher, etc) // that may be useful when using it in real life situations. type respWriterWrapper struct { http.ResponseWriter record func(n int64) // must not be nil // used to inject the header ctx context.Context props propagation.TextMapPropagator written int64 statusCode int err error wroteHeader bool } func (w *respWriterWrapper) Header() http.Header { return w.ResponseWriter.Header() } func (w *respWriterWrapper) Write(p []byte) (int, error) { if !w.wroteHeader { w.WriteHeader(http.StatusOK) } n, err := w.ResponseWriter.Write(p) n1 := int64(n) w.record(n1) w.written += n1 w.err = err return n, err } // WriteHeader persists initial statusCode for span attribution. // All calls to WriteHeader will be propagated to the underlying ResponseWriter // and will persist the statusCode from the first call. // Blocking consecutive calls to WriteHeader alters expected behavior and will // remove warning logs from net/http where developers will notice incorrect handler implementations. func (w *respWriterWrapper) WriteHeader(statusCode int) { if !w.wroteHeader { w.wroteHeader = true w.statusCode = statusCode } w.ResponseWriter.WriteHeader(statusCode) } ================================================ FILE: vendor/go.opentelemetry.io/otel/.codespellignore ================================================ ot fo te collison consequentially ================================================ FILE: vendor/go.opentelemetry.io/otel/.codespellrc ================================================ # https://github.com/codespell-project/codespell [codespell] builtin = clear,rare,informal check-filenames = check-hidden = ignore-words = .codespellignore interactive = 1 skip = .git,go.mod,go.sum,semconv,venv,.tools uri-ignore-words-list = * write = ================================================ FILE: vendor/go.opentelemetry.io/otel/.gitattributes ================================================ * text=auto eol=lf *.{cmd,[cC][mM][dD]} text eol=crlf *.{bat,[bB][aA][tT]} text eol=crlf ================================================ FILE: vendor/go.opentelemetry.io/otel/.gitignore ================================================ .DS_Store Thumbs.db .tools/ venv/ .idea/ .vscode/ *.iml *.so coverage.* go.work go.work.sum gen/ /example/dice/dice /example/fib/fib /example/fib/traces.txt /example/jaeger/jaeger /example/namedtracer/namedtracer /example/opencensus/opencensus /example/passthrough/passthrough /example/prometheus/prometheus /example/zipkin/zipkin /example/otel-collector/otel-collector ================================================ FILE: vendor/go.opentelemetry.io/otel/.gitmodules ================================================ [submodule "opentelemetry-proto"] path = exporters/otlp/internal/opentelemetry-proto url = https://github.com/open-telemetry/opentelemetry-proto ================================================ FILE: vendor/go.opentelemetry.io/otel/.golangci.yml ================================================ # See https://github.com/golangci/golangci-lint#config-file run: issues-exit-code: 1 #Default tests: true #Default linters: # Disable everything by default so upgrades to not include new "default # enabled" linters. disable-all: true # Specifically enable linters we want to use. enable: - depguard - errcheck - godot - gofmt - goimports - gosimple - govet - ineffassign - misspell - revive - staticcheck - typecheck - unused issues: # Maximum issues count per one linter. # Set to 0 to disable. # Default: 50 # Setting to unlimited so the linter only is run once to debug all issues. max-issues-per-linter: 0 # Maximum count of issues with the same text. # Set to 0 to disable. # Default: 3 # Setting to unlimited so the linter only is run once to debug all issues. max-same-issues: 0 # Excluding configuration per-path, per-linter, per-text and per-source. exclude-rules: # TODO: Having appropriate comments for exported objects helps development, # even for objects in internal packages. Appropriate comments for all # exported objects should be added and this exclusion removed. - path: '.*internal/.*' text: "exported (method|function|type|const) (.+) should have comment or be unexported" linters: - revive # Yes, they are, but it's okay in a test. - path: _test\.go text: "exported func.*returns unexported type.*which can be annoying to use" linters: - revive # Example test functions should be treated like main. - path: example.*_test\.go text: "calls to (.+) only in main[(][)] or init[(][)] functions" linters: - revive include: # revive exported should have comment or be unexported. - EXC0012 # revive package comment should be of the form ... - EXC0013 linters-settings: depguard: rules: non-tests: files: - "!$test" - "!**/*test/*.go" - "!**/internal/matchers/*.go" deny: - pkg: "testing" - pkg: "github.com/stretchr/testify" - pkg: "crypto/md5" - pkg: "crypto/sha1" - pkg: "crypto/**/pkix" otlp-internal: files: - "!**/exporters/otlp/internal/**/*.go" deny: - pkg: "go.opentelemetry.io/otel/exporters/otlp/internal" desc: Do not use cross-module internal packages. otlptrace-internal: files: - "!**/exporters/otlp/otlptrace/*.go" - "!**/exporters/otlp/otlptrace/internal/**.go" deny: - pkg: "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal" desc: Do not use cross-module internal packages. otlpmetric-internal: files: - "!**/exporters/otlp/otlpmetric/internal/*.go" - "!**/exporters/otlp/otlpmetric/internal/**/*.go" deny: - pkg: "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal" desc: Do not use cross-module internal packages. otel-internal: files: - "**/sdk/*.go" - "**/sdk/**/*.go" - "**/exporters/*.go" - "**/exporters/**/*.go" - "**/schema/*.go" - "**/schema/**/*.go" - "**/metric/*.go" - "**/metric/**/*.go" - "**/bridge/*.go" - "**/bridge/**/*.go" - "**/example/*.go" - "**/example/**/*.go" - "**/trace/*.go" - "**/trace/**/*.go" deny: - pkg: "go.opentelemetry.io/otel/internal$" desc: Do not use cross-module internal packages. - pkg: "go.opentelemetry.io/otel/internal/attribute" desc: Do not use cross-module internal packages. - pkg: "go.opentelemetry.io/otel/internal/internaltest" desc: Do not use cross-module internal packages. - pkg: "go.opentelemetry.io/otel/internal/matchers" desc: Do not use cross-module internal packages. godot: exclude: # Exclude links. - '^ *\[[^]]+\]:' # Exclude sentence fragments for lists. - '^[ ]*[-•]' # Exclude sentences prefixing a list. - ':$' goimports: local-prefixes: go.opentelemetry.io misspell: locale: US ignore-words: - cancelled revive: # Sets the default failure confidence. # This means that linting errors with less than 0.8 confidence will be ignored. # Default: 0.8 confidence: 0.01 rules: # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#blank-imports - name: blank-imports disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#bool-literal-in-expr - name: bool-literal-in-expr disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#constant-logical-expr - name: constant-logical-expr disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#context-as-argument # TODO (#3372) re-enable linter when it is compatible. https://github.com/golangci/golangci-lint/issues/3280 - name: context-as-argument disabled: true arguments: allowTypesBefore: "*testing.T" # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#context-keys-type - name: context-keys-type disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#deep-exit - name: deep-exit disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#defer - name: defer disabled: false arguments: - ["call-chain", "loop"] # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#dot-imports - name: dot-imports disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#duplicated-imports - name: duplicated-imports disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#early-return - name: early-return disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#empty-block - name: empty-block disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#empty-lines - name: empty-lines disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-naming - name: error-naming disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-return - name: error-return disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-strings - name: error-strings disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#errorf - name: errorf disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#exported - name: exported disabled: false arguments: - "sayRepetitiveInsteadOfStutters" # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#flag-parameter - name: flag-parameter disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#identical-branches - name: identical-branches disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#if-return - name: if-return disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#increment-decrement - name: increment-decrement disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#indent-error-flow - name: indent-error-flow disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#import-shadowing - name: import-shadowing disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#package-comments - name: package-comments disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range - name: range disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range-val-in-closure - name: range-val-in-closure disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range-val-address - name: range-val-address disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#redefines-builtin-id - name: redefines-builtin-id disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#string-format - name: string-format disabled: false arguments: - - panic - '/^[^\n]*$/' - must not contain line breaks # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#struct-tag - name: struct-tag disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#superfluous-else - name: superfluous-else disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#time-equal - name: time-equal disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#var-naming - name: var-naming disabled: false arguments: - ["ID"] # AllowList - ["Otel", "Aws", "Gcp"] # DenyList # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#var-declaration - name: var-declaration disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unconditional-recursion - name: unconditional-recursion disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unexported-return - name: unexported-return disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unhandled-error - name: unhandled-error disabled: false arguments: - "fmt.Fprint" - "fmt.Fprintf" - "fmt.Fprintln" - "fmt.Print" - "fmt.Printf" - "fmt.Println" # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unnecessary-stmt - name: unnecessary-stmt disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#useless-break - name: useless-break disabled: false # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#waitgroup-by-value - name: waitgroup-by-value disabled: false ================================================ FILE: vendor/go.opentelemetry.io/otel/.lycheeignore ================================================ http://localhost http://jaeger-collector https://github.com/open-telemetry/opentelemetry-go/milestone/ https://github.com/open-telemetry/opentelemetry-go/projects file:///home/runner/work/opentelemetry-go/opentelemetry-go/libraries file:///home/runner/work/opentelemetry-go/opentelemetry-go/manual ================================================ FILE: vendor/go.opentelemetry.io/otel/.markdownlint.yaml ================================================ # Default state for all rules default: true # ul-style MD004: false # hard-tabs MD010: false # line-length MD013: false # no-duplicate-header MD024: siblings_only: true #single-title MD025: false # ol-prefix MD029: style: ordered # no-inline-html MD033: false # fenced-code-language MD040: false ================================================ FILE: vendor/go.opentelemetry.io/otel/CHANGELOG.md ================================================ # Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ## [1.19.0/0.42.0/0.0.7] 2023-09-28 This release contains the first stable release of the OpenTelemetry Go [metric SDK]. Our project stability guarantees now apply to the `go.opentelemetry.io/otel/sdk/metric` package. See our [versioning policy](VERSIONING.md) for more information about these stability guarantees. ### Added - Add the "Roll the dice" getting started application example in `go.opentelemetry.io/otel/example/dice`. (#4539) - The `WithWriter` and `WithPrettyPrint` options to `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric` to set a custom `io.Writer`, and allow displaying the output in human-readable JSON. (#4507) ### Changed - Allow '/' characters in metric instrument names. (#4501) - The exporter in `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric` does not prettify its output by default anymore. (#4507) - Upgrade `gopkg.io/yaml` from `v2` to `v3` in `go.opentelemetry.io/otel/schema`. (#4535) ### Fixed - In `go.opentelemetry.op/otel/exporters/prometheus`, don't try to create the Prometheus metric on every `Collect` if we know the scope is invalid. (#4499) ### Removed - Remove `"go.opentelemetry.io/otel/bridge/opencensus".NewMetricExporter`, which is replaced by `NewMetricProducer`. (#4566) ## [1.19.0-rc.1/0.42.0-rc.1] 2023-09-14 This is a release candidate for the v1.19.0/v0.42.0 release. That release is expected to include the `v1` release of the OpenTelemetry Go metric SDK and will provide stability guarantees of that SDK. See our [versioning policy](VERSIONING.md) for more information about these stability guarantees. ### Changed - Allow '/' characters in metric instrument names. (#4501) ### Fixed - In `go.opentelemetry.op/otel/exporters/prometheus`, don't try to create the prometheus metric on every `Collect` if we know the scope is invalid. (#4499) ## [1.18.0/0.41.0/0.0.6] 2023-09-12 This release drops the compatibility guarantee of [Go 1.19]. ### Added - Add `WithProducer` option in `go.opentelemetry.op/otel/exporters/prometheus` to restore the ability to register producers on the prometheus exporter's manual reader. (#4473) - Add `IgnoreValue` option in `go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest` to allow ignoring values when comparing metrics. (#4447) ### Changed - Use a `TestingT` interface instead of `*testing.T` struct in `go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest`. (#4483) ### Deprecated - The `NewMetricExporter` in `go.opentelemetry.io/otel/bridge/opencensus` was deprecated in `v0.35.0` (#3541). The deprecation notice format for the function has been corrected to trigger Go documentation and build tooling. (#4470) ### Removed - Removed the deprecated `go.opentelemetry.io/otel/exporters/jaeger` package. (#4467) - Removed the deprecated `go.opentelemetry.io/otel/example/jaeger` package. (#4467) - Removed the deprecated `go.opentelemetry.io/otel/sdk/metric/aggregation` package. (#4468) - Removed the deprecated internal packages in `go.opentelemetry.io/otel/exporters/otlp` and its sub-packages. (#4469) - Dropped guaranteed support for versions of Go less than 1.20. (#4481) ## [1.17.0/0.40.0/0.0.5] 2023-08-28 ### Added - Export the `ManualReader` struct in `go.opentelemetry.io/otel/sdk/metric`. (#4244) - Export the `PeriodicReader` struct in `go.opentelemetry.io/otel/sdk/metric`. (#4244) - Add support for exponential histogram aggregations. A histogram can be configured as an exponential histogram using a view with `"go.opentelemetry.io/otel/sdk/metric".ExponentialHistogram` as the aggregation. (#4245) - Export the `Exporter` struct in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc`. (#4272) - Export the `Exporter` struct in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. (#4272) - The exporters in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric` now support the `OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE` environment variable. (#4287) - Add `WithoutCounterSuffixes` option in `go.opentelemetry.io/otel/exporters/prometheus` to disable addition of `_total` suffixes. (#4306) - Add info and debug logging to the metric SDK in `go.opentelemetry.io/otel/sdk/metric`. (#4315) - The `go.opentelemetry.io/otel/semconv/v1.21.0` package. The package contains semantic conventions from the `v1.21.0` version of the OpenTelemetry Semantic Conventions. (#4362) - Accept 201 to 299 HTTP status as success in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp` and `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`. (#4365) - Document the `Temporality` and `Aggregation` methods of the `"go.opentelemetry.io/otel/sdk/metric".Exporter"` need to be concurrent safe. (#4381) - Expand the set of units supported by the Prometheus exporter, and don't add unit suffixes if they are already present in `go.opentelemetry.op/otel/exporters/prometheus` (#4374) - Move the `Aggregation` interface and its implementations from `go.opentelemetry.io/otel/sdk/metric/aggregation` to `go.opentelemetry.io/otel/sdk/metric`. (#4435) - The exporters in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric` now support the `OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION` environment variable. (#4437) - Add the `NewAllowKeysFilter` and `NewDenyKeysFilter` functions to `go.opentelemetry.io/otel/attribute` to allow convenient creation of allow-keys and deny-keys filters. (#4444) - Support Go 1.21. (#4463) ### Changed - Starting from `v1.21.0` of semantic conventions, `go.opentelemetry.io/otel/semconv/{version}/httpconv` and `go.opentelemetry.io/otel/semconv/{version}/netconv` packages will no longer be published. (#4145) - Log duplicate instrument conflict at a warning level instead of info in `go.opentelemetry.io/otel/sdk/metric`. (#4202) - Return an error on the creation of new instruments in `go.opentelemetry.io/otel/sdk/metric` if their name doesn't pass regexp validation. (#4210) - `NewManualReader` in `go.opentelemetry.io/otel/sdk/metric` returns `*ManualReader` instead of `Reader`. (#4244) - `NewPeriodicReader` in `go.opentelemetry.io/otel/sdk/metric` returns `*PeriodicReader` instead of `Reader`. (#4244) - Count the Collect time in the `PeriodicReader` timeout in `go.opentelemetry.io/otel/sdk/metric`. (#4221) - The function `New` in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` returns `*Exporter` instead of `"go.opentelemetry.io/otel/sdk/metric".Exporter`. (#4272) - The function `New` in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp` returns `*Exporter` instead of `"go.opentelemetry.io/otel/sdk/metric".Exporter`. (#4272) - If an attribute set is omitted from an async callback, the previous value will no longer be exported in `go.opentelemetry.io/otel/sdk/metric`. (#4290) - If an attribute set is observed multiple times in an async callback in `go.opentelemetry.io/otel/sdk/metric`, the values will be summed instead of the last observation winning. (#4289) - Allow the explicit bucket histogram aggregation to be used for the up-down counter, observable counter, observable up-down counter, and observable gauge in the `go.opentelemetry.io/otel/sdk/metric` package. (#4332) - Restrict `Meter`s in `go.opentelemetry.io/otel/sdk/metric` to only register and collect instruments it created. (#4333) - `PeriodicReader.Shutdown` and `PeriodicReader.ForceFlush` in `go.opentelemetry.io/otel/sdk/metric` now apply the periodic reader's timeout to the operation if the user provided context does not contain a deadline. (#4356, #4377) - Upgrade all use of `go.opentelemetry.io/otel/semconv` to use `v1.21.0`. (#4408) - Increase instrument name maximum length from 63 to 255 characters in `go.opentelemetry.io/otel/sdk/metric`. (#4434) - Add `go.opentelemetry.op/otel/sdk/metric.WithProducer` as an `Option` for `"go.opentelemetry.io/otel/sdk/metric".NewManualReader` and `"go.opentelemetry.io/otel/sdk/metric".NewPeriodicReader`. (#4346) ### Removed - Remove `Reader.RegisterProducer` in `go.opentelemetry.io/otel/metric`. Use the added `WithProducer` option instead. (#4346) - Remove `Reader.ForceFlush` in `go.opentelemetry.io/otel/metric`. Notice that `PeriodicReader.ForceFlush` is still available. (#4375) ### Fixed - Correctly format log messages from the `go.opentelemetry.io/otel/exporters/zipkin` exporter. (#4143) - Log an error for calls to `NewView` in `go.opentelemetry.io/otel/sdk/metric` that have empty criteria. (#4307) - Fix `"go.opentelemetry.io/otel/sdk/resource".WithHostID()` to not set an empty `host.id`. (#4317) - Use the instrument identifying fields to cache aggregators and determine duplicate instrument registrations in `go.opentelemetry.io/otel/sdk/metric`. (#4337) - Detect duplicate instruments for case-insensitive names in `go.opentelemetry.io/otel/sdk/metric`. (#4338) - The `ManualReader` will not panic if `AggregationSelector` returns `nil` in `go.opentelemetry.io/otel/sdk/metric`. (#4350) - If a `Reader`'s `AggregationSelector` returns `nil` or `DefaultAggregation` the pipeline will use the default aggregation. (#4350) - Log a suggested view that fixes instrument conflicts in `go.opentelemetry.io/otel/sdk/metric`. (#4349) - Fix possible panic, deadlock and race condition in batch span processor in `go.opentelemetry.io/otel/sdk/trace`. (#4353) - Improve context cancellation handling in batch span processor's `ForceFlush` in `go.opentelemetry.io/otel/sdk/trace`. (#4369) - Decouple `go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal` from `go.opentelemetry.io/otel/exporters/otlp/internal` using gotmpl. (#4397, #3846) - Decouple `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal` from `go.opentelemetry.io/otel/exporters/otlp/internal` and `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal` using gotmpl. (#4404, #3846) - Decouple `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal` from `go.opentelemetry.io/otel/exporters/otlp/internal` and `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal` using gotmpl. (#4407, #3846) - Decouple `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal` from `go.opentelemetry.io/otel/exporters/otlp/internal` and `go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal` using gotmpl. (#4400, #3846) - Decouple `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal` from `go.opentelemetry.io/otel/exporters/otlp/internal` and `go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal` using gotmpl. (#4401, #3846) - Do not block the metric SDK when OTLP metric exports are blocked in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` and `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. (#3925, #4395) - Do not append `_total` if the counter already has that suffix for the Prometheus exproter in `go.opentelemetry.io/otel/exporter/prometheus`. (#4373) - Fix resource detection data race in `go.opentelemetry.io/otel/sdk/resource`. (#4409) - Use the first-seen instrument name during instrument name conflicts in `go.opentelemetry.io/otel/sdk/metric`. (#4428) ### Deprecated - The `go.opentelemetry.io/otel/exporters/jaeger` package is deprecated. OpenTelemetry dropped support for Jaeger exporter in July 2023. Use `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp` or `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc` instead. (#4423) - The `go.opentelemetry.io/otel/example/jaeger` package is deprecated. (#4423) - The `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal` package is deprecated. (#4420) - The `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/oconf` package is deprecated. (#4420) - The `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/otest` package is deprecated. (#4420) - The `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/internal/transform` package is deprecated. (#4420) - The `go.opentelemetry.io/otel/exporters/otlp/internal` package is deprecated. (#4421) - The `go.opentelemetry.io/otel/exporters/otlp/internal/envconfig` package is deprecated. (#4421) - The `go.opentelemetry.io/otel/exporters/otlp/internal/retry` package is deprecated. (#4421) - The `go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal` package is deprecated. (#4425) - The `go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/envconfig` package is deprecated. (#4425) - The `go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlpconfig` package is deprecated. (#4425) - The `go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/otlptracetest` package is deprecated. (#4425) - The `go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/retry` package is deprecated. (#4425) - The `go.opentelemetry.io/otel/sdk/metric/aggregation` package is deprecated. Use the aggregation types added to `go.opentelemetry.io/otel/sdk/metric` instead. (#4435) ## [1.16.0/0.39.0] 2023-05-18 This release contains the first stable release of the OpenTelemetry Go [metric API]. Our project stability guarantees now apply to the `go.opentelemetry.io/otel/metric` package. See our [versioning policy](VERSIONING.md) for more information about these stability guarantees. ### Added - The `go.opentelemetry.io/otel/semconv/v1.19.0` package. The package contains semantic conventions from the `v1.19.0` version of the OpenTelemetry specification. (#3848) - The `go.opentelemetry.io/otel/semconv/v1.20.0` package. The package contains semantic conventions from the `v1.20.0` version of the OpenTelemetry specification. (#4078) - The Exponential Histogram data types in `go.opentelemetry.io/otel/sdk/metric/metricdata`. (#4165) - OTLP metrics exporter now supports the Exponential Histogram Data Type. (#4222) - Fix serialization of `time.Time` zero values in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` and `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp` packages. (#4271) ### Changed - Use `strings.Cut()` instead of `string.SplitN()` for better readability and memory use. (#4049) - `MeterProvider` returns noop meters once it has been shutdown. (#4154) ### Removed - The deprecated `go.opentelemetry.io/otel/metric/instrument` package is removed. Use `go.opentelemetry.io/otel/metric` instead. (#4055) ### Fixed - Fix build for BSD based systems in `go.opentelemetry.io/otel/sdk/resource`. (#4077) ## [1.16.0-rc.1/0.39.0-rc.1] 2023-05-03 This is a release candidate for the v1.16.0/v0.39.0 release. That release is expected to include the `v1` release of the OpenTelemetry Go metric API and will provide stability guarantees of that API. See our [versioning policy](VERSIONING.md) for more information about these stability guarantees. ### Added - Support global `MeterProvider` in `go.opentelemetry.io/otel`. (#4039) - Use `Meter` for a `metric.Meter` from the global `metric.MeterProvider`. - Use `GetMeterProivder` for a global `metric.MeterProvider`. - Use `SetMeterProivder` to set the global `metric.MeterProvider`. ### Changed - Move the `go.opentelemetry.io/otel/metric` module to the `stable-v1` module set. This stages the metric API to be released as a stable module. (#4038) ### Removed - The `go.opentelemetry.io/otel/metric/global` package is removed. Use `go.opentelemetry.io/otel` instead. (#4039) ## [1.15.1/0.38.1] 2023-05-02 ### Fixed - Remove unused imports from `sdk/resource/host_id_bsd.go` which caused build failures. (#4040, #4041) ## [1.15.0/0.38.0] 2023-04-27 ### Added - The `go.opentelemetry.io/otel/metric/embedded` package. (#3916) - The `Version` function to `go.opentelemetry.io/otel/sdk` to return the SDK version. (#3949) - Add a `WithNamespace` option to `go.opentelemetry.io/otel/exporters/prometheus` to allow users to prefix metrics with a namespace. (#3970) - The following configuration types were added to `go.opentelemetry.io/otel/metric/instrument` to be used in the configuration of measurement methods. (#3971) - The `AddConfig` used to hold configuration for addition measurements - `NewAddConfig` used to create a new `AddConfig` - `AddOption` used to configure an `AddConfig` - The `RecordConfig` used to hold configuration for recorded measurements - `NewRecordConfig` used to create a new `RecordConfig` - `RecordOption` used to configure a `RecordConfig` - The `ObserveConfig` used to hold configuration for observed measurements - `NewObserveConfig` used to create a new `ObserveConfig` - `ObserveOption` used to configure an `ObserveConfig` - `WithAttributeSet` and `WithAttributes` are added to `go.opentelemetry.io/otel/metric/instrument`. They return an option used during a measurement that defines the attribute Set associated with the measurement. (#3971) - The `Version` function to `go.opentelemetry.io/otel/exporters/otlp/otlpmetric` to return the OTLP metrics client version. (#3956) - The `Version` function to `go.opentelemetry.io/otel/exporters/otlp/otlptrace` to return the OTLP trace client version. (#3956) ### Changed - The `Extrema` in `go.opentelemetry.io/otel/sdk/metric/metricdata` is redefined with a generic argument of `[N int64 | float64]`. (#3870) - Update all exported interfaces from `go.opentelemetry.io/otel/metric` to embed their corresponding interface from `go.opentelemetry.io/otel/metric/embedded`. This adds an implementation requirement to set the interface default behavior for unimplemented methods. (#3916) - Move No-Op implementation from `go.opentelemetry.io/otel/metric` into its own package `go.opentelemetry.io/otel/metric/noop`. (#3941) - `metric.NewNoopMeterProvider` is replaced with `noop.NewMeterProvider` - Add all the methods from `"go.opentelemetry.io/otel/trace".SpanContext` to `bridgeSpanContext` by embedding `otel.SpanContext` in `bridgeSpanContext`. (#3966) - Wrap `UploadMetrics` error in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/` to improve error message when encountering generic grpc errors. (#3974) - The measurement methods for all instruments in `go.opentelemetry.io/otel/metric/instrument` accept an option instead of the variadic `"go.opentelemetry.io/otel/attribute".KeyValue`. (#3971) - The `Int64Counter.Add` method now accepts `...AddOption` - The `Float64Counter.Add` method now accepts `...AddOption` - The `Int64UpDownCounter.Add` method now accepts `...AddOption` - The `Float64UpDownCounter.Add` method now accepts `...AddOption` - The `Int64Histogram.Record` method now accepts `...RecordOption` - The `Float64Histogram.Record` method now accepts `...RecordOption` - The `Int64Observer.Observe` method now accepts `...ObserveOption` - The `Float64Observer.Observe` method now accepts `...ObserveOption` - The `Observer` methods in `go.opentelemetry.io/otel/metric` accept an option instead of the variadic `"go.opentelemetry.io/otel/attribute".KeyValue`. (#3971) - The `Observer.ObserveInt64` method now accepts `...ObserveOption` - The `Observer.ObserveFloat64` method now accepts `...ObserveOption` - Move global metric back to `go.opentelemetry.io/otel/metric/global` from `go.opentelemetry.io/otel`. (#3986) ### Fixed - `TracerProvider` allows calling `Tracer()` while it's shutting down. It used to deadlock. (#3924) - Use the SDK version for the Telemetry SDK resource detector in `go.opentelemetry.io/otel/sdk/resource`. (#3949) - Fix a data race in `SpanProcessor` returned by `NewSimpleSpanProcessor` in `go.opentelemetry.io/otel/sdk/trace`. (#3951) - Automatically figure out the default aggregation with `aggregation.Default`. (#3967) ### Deprecated - The `go.opentelemetry.io/otel/metric/instrument` package is deprecated. Use the equivalent types added to `go.opentelemetry.io/otel/metric` instead. (#4018) ## [1.15.0-rc.2/0.38.0-rc.2] 2023-03-23 This is a release candidate for the v1.15.0/v0.38.0 release. That release will include the `v1` release of the OpenTelemetry Go metric API and will provide stability guarantees of that API. See our [versioning policy](VERSIONING.md) for more information about these stability guarantees. ### Added - The `WithHostID` option to `go.opentelemetry.io/otel/sdk/resource`. (#3812) - The `WithoutTimestamps` option to `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric` to sets all timestamps to zero. (#3828) - The new `Exemplar` type is added to `go.opentelemetry.io/otel/sdk/metric/metricdata`. Both the `DataPoint` and `HistogramDataPoint` types from that package have a new field of `Exemplars` containing the sampled exemplars for their timeseries. (#3849) - Configuration for each metric instrument in `go.opentelemetry.io/otel/sdk/metric/instrument`. (#3895) - The internal logging introduces a warning level verbosity equal to `V(1)`. (#3900) - Added a log message warning about usage of `SimpleSpanProcessor` in production environments. (#3854) ### Changed - Optimize memory allocation when creation a new `Set` using `NewSet` or `NewSetWithFiltered` in `go.opentelemetry.io/otel/attribute`. (#3832) - Optimize memory allocation when creation new metric instruments in `go.opentelemetry.io/otel/sdk/metric`. (#3832) - Avoid creating new objects on all calls to `WithDeferredSetup` and `SkipContextSetup` in OpenTracing bridge. (#3833) - The `New` and `Detect` functions from `go.opentelemetry.io/otel/sdk/resource` return errors that wrap underlying errors instead of just containing the underlying error strings. (#3844) - Both the `Histogram` and `HistogramDataPoint` are redefined with a generic argument of `[N int64 | float64]` in `go.opentelemetry.io/otel/sdk/metric/metricdata`. (#3849) - The metric `Export` interface from `go.opentelemetry.io/otel/sdk/metric` accepts a `*ResourceMetrics` instead of `ResourceMetrics`. (#3853) - Rename `Asynchronous` to `Observable` in `go.opentelemetry.io/otel/metric/instrument`. (#3892) - Rename `Int64ObserverOption` to `Int64ObservableOption` in `go.opentelemetry.io/otel/metric/instrument`. (#3895) - Rename `Float64ObserverOption` to `Float64ObservableOption` in `go.opentelemetry.io/otel/metric/instrument`. (#3895) - The internal logging changes the verbosity level of info to `V(4)`, the verbosity level of debug to `V(8)`. (#3900) ### Fixed - `TracerProvider` consistently doesn't allow to register a `SpanProcessor` after shutdown. (#3845) ### Removed - The deprecated `go.opentelemetry.io/otel/metric/global` package is removed. (#3829) - The unneeded `Synchronous` interface in `go.opentelemetry.io/otel/metric/instrument` was removed. (#3892) - The `Float64ObserverConfig` and `NewFloat64ObserverConfig` in `go.opentelemetry.io/otel/sdk/metric/instrument`. Use the added `float64` instrument configuration instead. (#3895) - The `Int64ObserverConfig` and `NewInt64ObserverConfig` in `go.opentelemetry.io/otel/sdk/metric/instrument`. Use the added `int64` instrument configuration instead. (#3895) - The `NewNoopMeter` function in `go.opentelemetry.io/otel/metric`, use `NewMeterProvider().Meter("")` instead. (#3893) ## [1.15.0-rc.1/0.38.0-rc.1] 2023-03-01 This is a release candidate for the v1.15.0/v0.38.0 release. That release will include the `v1` release of the OpenTelemetry Go metric API and will provide stability guarantees of that API. See our [versioning policy](VERSIONING.md) for more information about these stability guarantees. This release drops the compatibility guarantee of [Go 1.18]. ### Added - Support global `MeterProvider` in `go.opentelemetry.io/otel`. (#3818) - Use `Meter` for a `metric.Meter` from the global `metric.MeterProvider`. - Use `GetMeterProivder` for a global `metric.MeterProvider`. - Use `SetMeterProivder` to set the global `metric.MeterProvider`. ### Changed - Dropped compatibility testing for [Go 1.18]. The project no longer guarantees support for this version of Go. (#3813) ### Fixed - Handle empty environment variable as it they were not set. (#3764) - Clarify the `httpconv` and `netconv` packages in `go.opentelemetry.io/otel/semconv/*` provide tracing semantic conventions. (#3823) - Fix race conditions in `go.opentelemetry.io/otel/exporters/metric/prometheus` that could cause a panic. (#3899) - Fix sending nil `scopeInfo` to metrics channel in `go.opentelemetry.io/otel/exporters/metric/prometheus` that could cause a panic in `github.com/prometheus/client_golang/prometheus`. (#3899) ### Deprecated - The `go.opentelemetry.io/otel/metric/global` package is deprecated. Use `go.opentelemetry.io/otel` instead. (#3818) ### Removed - The deprecated `go.opentelemetry.io/otel/metric/unit` package is removed. (#3814) ## [1.14.0/0.37.0/0.0.4] 2023-02-27 This release is the last to support [Go 1.18]. The next release will require at least [Go 1.19]. ### Added - The `event` type semantic conventions are added to `go.opentelemetry.io/otel/semconv/v1.17.0`. (#3697) - Support [Go 1.20]. (#3693) - The `go.opentelemetry.io/otel/semconv/v1.18.0` package. The package contains semantic conventions from the `v1.18.0` version of the OpenTelemetry specification. (#3719) - The following `const` renames from `go.opentelemetry.io/otel/semconv/v1.17.0` are included: - `OtelScopeNameKey` -> `OTelScopeNameKey` - `OtelScopeVersionKey` -> `OTelScopeVersionKey` - `OtelLibraryNameKey` -> `OTelLibraryNameKey` - `OtelLibraryVersionKey` -> `OTelLibraryVersionKey` - `OtelStatusCodeKey` -> `OTelStatusCodeKey` - `OtelStatusDescriptionKey` -> `OTelStatusDescriptionKey` - `OtelStatusCodeOk` -> `OTelStatusCodeOk` - `OtelStatusCodeError` -> `OTelStatusCodeError` - The following `func` renames from `go.opentelemetry.io/otel/semconv/v1.17.0` are included: - `OtelScopeName` -> `OTelScopeName` - `OtelScopeVersion` -> `OTelScopeVersion` - `OtelLibraryName` -> `OTelLibraryName` - `OtelLibraryVersion` -> `OTelLibraryVersion` - `OtelStatusDescription` -> `OTelStatusDescription` - A `IsSampled` method is added to the `SpanContext` implementation in `go.opentelemetry.io/otel/bridge/opentracing` to expose the span sampled state. See the [README](./bridge/opentracing/README.md) for more information. (#3570) - The `WithInstrumentationAttributes` option to `go.opentelemetry.io/otel/metric`. (#3738) - The `WithInstrumentationAttributes` option to `go.opentelemetry.io/otel/trace`. (#3739) - The following environment variables are supported by the periodic `Reader` in `go.opentelemetry.io/otel/sdk/metric`. (#3763) - `OTEL_METRIC_EXPORT_INTERVAL` sets the time between collections and exports. - `OTEL_METRIC_EXPORT_TIMEOUT` sets the timeout an export is attempted. ### Changed - Fall-back to `TextMapCarrier` when it's not `HttpHeader`s in `go.opentelemetry.io/otel/bridge/opentracing`. (#3679) - The `Collect` method of the `"go.opentelemetry.io/otel/sdk/metric".Reader` interface is updated to accept the `metricdata.ResourceMetrics` value the collection will be made into. This change is made to enable memory reuse by SDK users. (#3732) - The `WithUnit` option in `go.opentelemetry.io/otel/sdk/metric/instrument` is updated to accept a `string` for the unit value. (#3776) ### Fixed - Ensure `go.opentelemetry.io/otel` does not use generics. (#3723, #3725) - Multi-reader `MeterProvider`s now export metrics for all readers, instead of just the first reader. (#3720, #3724) - Remove use of deprecated `"math/rand".Seed` in `go.opentelemetry.io/otel/example/prometheus`. (#3733) - Do not silently drop unknown schema data with `Parse` in `go.opentelemetry.io/otel/schema/v1.1`. (#3743) - Data race issue in OTLP exporter retry mechanism. (#3755, #3756) - Wrapping empty errors when exporting in `go.opentelemetry.io/otel/sdk/metric`. (#3698, #3772) - Incorrect "all" and "resource" definition for schema files in `go.opentelemetry.io/otel/schema/v1.1`. (#3777) ### Deprecated - The `go.opentelemetry.io/otel/metric/unit` package is deprecated. Use the equivalent unit string instead. (#3776) - Use `"1"` instead of `unit.Dimensionless` - Use `"By"` instead of `unit.Bytes` - Use `"ms"` instead of `unit.Milliseconds` ## [1.13.0/0.36.0] 2023-02-07 ### Added - Attribute `KeyValue` creations functions to `go.opentelemetry.io/otel/semconv/v1.17.0` for all non-enum semantic conventions. These functions ensure semantic convention type correctness. (#3675) ### Fixed - Removed the `http.target` attribute from being added by `ServerRequest` in the following packages. (#3687) - `go.opentelemetry.io/otel/semconv/v1.13.0/httpconv` - `go.opentelemetry.io/otel/semconv/v1.14.0/httpconv` - `go.opentelemetry.io/otel/semconv/v1.15.0/httpconv` - `go.opentelemetry.io/otel/semconv/v1.16.0/httpconv` - `go.opentelemetry.io/otel/semconv/v1.17.0/httpconv` ### Removed - The deprecated `go.opentelemetry.io/otel/metric/instrument/asyncfloat64` package is removed. (#3631) - The deprecated `go.opentelemetry.io/otel/metric/instrument/asyncint64` package is removed. (#3631) - The deprecated `go.opentelemetry.io/otel/metric/instrument/syncfloat64` package is removed. (#3631) - The deprecated `go.opentelemetry.io/otel/metric/instrument/syncint64` package is removed. (#3631) ## [1.12.0/0.35.0] 2023-01-28 ### Added - The `WithInt64Callback` option to `go.opentelemetry.io/otel/metric/instrument`. This options is used to configure `int64` Observer callbacks during their creation. (#3507) - The `WithFloat64Callback` option to `go.opentelemetry.io/otel/metric/instrument`. This options is used to configure `float64` Observer callbacks during their creation. (#3507) - The `Producer` interface and `Reader.RegisterProducer(Producer)` to `go.opentelemetry.io/otel/sdk/metric`. These additions are used to enable external metric Producers. (#3524) - The `Callback` function type to `go.opentelemetry.io/otel/metric`. This new named function type is registered with a `Meter`. (#3564) - The `go.opentelemetry.io/otel/semconv/v1.13.0` package. The package contains semantic conventions from the `v1.13.0` version of the OpenTelemetry specification. (#3499) - The `EndUserAttributesFromHTTPRequest` function in `go.opentelemetry.io/otel/semconv/v1.12.0` is merged into `ClientRequest` and `ServerRequest` in `go.opentelemetry.io/otel/semconv/v1.13.0/httpconv`. - The `HTTPAttributesFromHTTPStatusCode` function in `go.opentelemetry.io/otel/semconv/v1.12.0` is merged into `ClientResponse` in `go.opentelemetry.io/otel/semconv/v1.13.0/httpconv`. - The `HTTPClientAttributesFromHTTPRequest` function in `go.opentelemetry.io/otel/semconv/v1.12.0` is replaced by `ClientRequest` in `go.opentelemetry.io/otel/semconv/v1.13.0/httpconv`. - The `HTTPServerAttributesFromHTTPRequest` function in `go.opentelemetry.io/otel/semconv/v1.12.0` is replaced by `ServerRequest` in `go.opentelemetry.io/otel/semconv/v1.13.0/httpconv`. - The `HTTPServerMetricAttributesFromHTTPRequest` function in `go.opentelemetry.io/otel/semconv/v1.12.0` is replaced by `ServerRequest` in `go.opentelemetry.io/otel/semconv/v1.13.0/httpconv`. - The `NetAttributesFromHTTPRequest` function in `go.opentelemetry.io/otel/semconv/v1.12.0` is split into `Transport` in `go.opentelemetry.io/otel/semconv/v1.13.0/netconv` and `ClientRequest` or `ServerRequest` in `go.opentelemetry.io/otel/semconv/v1.13.0/httpconv`. - The `SpanStatusFromHTTPStatusCode` function in `go.opentelemetry.io/otel/semconv/v1.12.0` is replaced by `ClientStatus` in `go.opentelemetry.io/otel/semconv/v1.13.0/httpconv`. - The `SpanStatusFromHTTPStatusCodeAndSpanKind` function in `go.opentelemetry.io/otel/semconv/v1.12.0` is split into `ClientStatus` and `ServerStatus` in `go.opentelemetry.io/otel/semconv/v1.13.0/httpconv`. - The `Client` function is included in `go.opentelemetry.io/otel/semconv/v1.13.0/netconv` to generate attributes for a `net.Conn`. - The `Server` function is included in `go.opentelemetry.io/otel/semconv/v1.13.0/netconv` to generate attributes for a `net.Listener`. - The `go.opentelemetry.io/otel/semconv/v1.14.0` package. The package contains semantic conventions from the `v1.14.0` version of the OpenTelemetry specification. (#3566) - The `go.opentelemetry.io/otel/semconv/v1.15.0` package. The package contains semantic conventions from the `v1.15.0` version of the OpenTelemetry specification. (#3578) - The `go.opentelemetry.io/otel/semconv/v1.16.0` package. The package contains semantic conventions from the `v1.16.0` version of the OpenTelemetry specification. (#3579) - Metric instruments to `go.opentelemetry.io/otel/metric/instrument`. These instruments are use as replacements of the deprecated `go.opentelemetry.io/otel/metric/instrument/{asyncfloat64,asyncint64,syncfloat64,syncint64}` packages.(#3575, #3586) - `Float64ObservableCounter` replaces the `asyncfloat64.Counter` - `Float64ObservableUpDownCounter` replaces the `asyncfloat64.UpDownCounter` - `Float64ObservableGauge` replaces the `asyncfloat64.Gauge` - `Int64ObservableCounter` replaces the `asyncint64.Counter` - `Int64ObservableUpDownCounter` replaces the `asyncint64.UpDownCounter` - `Int64ObservableGauge` replaces the `asyncint64.Gauge` - `Float64Counter` replaces the `syncfloat64.Counter` - `Float64UpDownCounter` replaces the `syncfloat64.UpDownCounter` - `Float64Histogram` replaces the `syncfloat64.Histogram` - `Int64Counter` replaces the `syncint64.Counter` - `Int64UpDownCounter` replaces the `syncint64.UpDownCounter` - `Int64Histogram` replaces the `syncint64.Histogram` - `NewTracerProvider` to `go.opentelemetry.io/otel/bridge/opentracing`. This is used to create `WrapperTracer` instances from a `TracerProvider`. (#3116) - The `Extrema` type to `go.opentelemetry.io/otel/sdk/metric/metricdata`. This type is used to represent min/max values and still be able to distinguish unset and zero values. (#3487) - The `go.opentelemetry.io/otel/semconv/v1.17.0` package. The package contains semantic conventions from the `v1.17.0` version of the OpenTelemetry specification. (#3599) ### Changed - Jaeger and Zipkin exporter use `github.com/go-logr/logr` as the logging interface, and add the `WithLogr` option. (#3497, #3500) - Instrument configuration in `go.opentelemetry.io/otel/metric/instrument` is split into specific options and configuration based on the instrument type. (#3507) - Use the added `Int64Option` type to configure instruments from `go.opentelemetry.io/otel/metric/instrument/syncint64`. - Use the added `Float64Option` type to configure instruments from `go.opentelemetry.io/otel/metric/instrument/syncfloat64`. - Use the added `Int64ObserverOption` type to configure instruments from `go.opentelemetry.io/otel/metric/instrument/asyncint64`. - Use the added `Float64ObserverOption` type to configure instruments from `go.opentelemetry.io/otel/metric/instrument/asyncfloat64`. - Return a `Registration` from the `RegisterCallback` method of a `Meter` in the `go.opentelemetry.io/otel/metric` package. This `Registration` can be used to unregister callbacks. (#3522) - Global error handler uses an atomic value instead of a mutex. (#3543) - Add `NewMetricProducer` to `go.opentelemetry.io/otel/bridge/opencensus`, which can be used to pass OpenCensus metrics to an OpenTelemetry Reader. (#3541) - Global logger uses an atomic value instead of a mutex. (#3545) - The `Shutdown` method of the `"go.opentelemetry.io/otel/sdk/trace".TracerProvider` releases all computational resources when called the first time. (#3551) - The `Sampler` returned from `TraceIDRatioBased` `go.opentelemetry.io/otel/sdk/trace` now uses the rightmost bits for sampling decisions. This fixes random sampling when using ID generators like `xray.IDGenerator` and increasing parity with other language implementations. (#3557) - Errors from `go.opentelemetry.io/otel/exporters/otlp/otlptrace` exporters are wrapped in errors identifying their signal name. Existing users of the exporters attempting to identify specific errors will need to use `errors.Unwrap()` to get the underlying error. (#3516) - Exporters from `go.opentelemetry.io/otel/exporters/otlp` will print the final retryable error message when attempts to retry time out. (#3514) - The instrument kind names in `go.opentelemetry.io/otel/sdk/metric` are updated to match the API. (#3562) - `InstrumentKindSyncCounter` is renamed to `InstrumentKindCounter` - `InstrumentKindSyncUpDownCounter` is renamed to `InstrumentKindUpDownCounter` - `InstrumentKindSyncHistogram` is renamed to `InstrumentKindHistogram` - `InstrumentKindAsyncCounter` is renamed to `InstrumentKindObservableCounter` - `InstrumentKindAsyncUpDownCounter` is renamed to `InstrumentKindObservableUpDownCounter` - `InstrumentKindAsyncGauge` is renamed to `InstrumentKindObservableGauge` - The `RegisterCallback` method of the `Meter` in `go.opentelemetry.io/otel/metric` changed. - The named `Callback` replaces the inline function parameter. (#3564) - `Callback` is required to return an error. (#3576) - `Callback` accepts the added `Observer` parameter added. This new parameter is used by `Callback` implementations to observe values for asynchronous instruments instead of calling the `Observe` method of the instrument directly. (#3584) - The slice of `instrument.Asynchronous` is now passed as a variadic argument. (#3587) - The exporter from `go.opentelemetry.io/otel/exporters/zipkin` is updated to use the `v1.16.0` version of semantic conventions. This means it no longer uses the removed `net.peer.ip` or `http.host` attributes to determine the remote endpoint. Instead it uses the `net.sock.peer` attributes. (#3581) - The `Min` and `Max` fields of the `HistogramDataPoint` in `go.opentelemetry.io/otel/sdk/metric/metricdata` are now defined with the added `Extrema` type instead of a `*float64`. (#3487) ### Fixed - Asynchronous instruments that use sum aggregators and attribute filters correctly add values from equivalent attribute sets that have been filtered. (#3439, #3549) - The `RegisterCallback` method of the `Meter` from `go.opentelemetry.io/otel/sdk/metric` only registers a callback for instruments created by that meter. Trying to register a callback with instruments from a different meter will result in an error being returned. (#3584) ### Deprecated - The `NewMetricExporter` in `go.opentelemetry.io/otel/bridge/opencensus` is deprecated. Use `NewMetricProducer` instead. (#3541) - The `go.opentelemetry.io/otel/metric/instrument/asyncfloat64` package is deprecated. Use the instruments from `go.opentelemetry.io/otel/metric/instrument` instead. (#3575) - The `go.opentelemetry.io/otel/metric/instrument/asyncint64` package is deprecated. Use the instruments from `go.opentelemetry.io/otel/metric/instrument` instead. (#3575) - The `go.opentelemetry.io/otel/metric/instrument/syncfloat64` package is deprecated. Use the instruments from `go.opentelemetry.io/otel/metric/instrument` instead. (#3575) - The `go.opentelemetry.io/otel/metric/instrument/syncint64` package is deprecated. Use the instruments from `go.opentelemetry.io/otel/metric/instrument` instead. (#3575) - The `NewWrappedTracerProvider` in `go.opentelemetry.io/otel/bridge/opentracing` is now deprecated. Use `NewTracerProvider` instead. (#3116) ### Removed - The deprecated `go.opentelemetry.io/otel/sdk/metric/view` package is removed. (#3520) - The `InstrumentProvider` from `go.opentelemetry.io/otel/sdk/metric/asyncint64` is removed. Use the new creation methods of the `Meter` in `go.opentelemetry.io/otel/sdk/metric` instead. (#3530) - The `Counter` method is replaced by `Meter.Int64ObservableCounter` - The `UpDownCounter` method is replaced by `Meter.Int64ObservableUpDownCounter` - The `Gauge` method is replaced by `Meter.Int64ObservableGauge` - The `InstrumentProvider` from `go.opentelemetry.io/otel/sdk/metric/asyncfloat64` is removed. Use the new creation methods of the `Meter` in `go.opentelemetry.io/otel/sdk/metric` instead. (#3530) - The `Counter` method is replaced by `Meter.Float64ObservableCounter` - The `UpDownCounter` method is replaced by `Meter.Float64ObservableUpDownCounter` - The `Gauge` method is replaced by `Meter.Float64ObservableGauge` - The `InstrumentProvider` from `go.opentelemetry.io/otel/sdk/metric/syncint64` is removed. Use the new creation methods of the `Meter` in `go.opentelemetry.io/otel/sdk/metric` instead. (#3530) - The `Counter` method is replaced by `Meter.Int64Counter` - The `UpDownCounter` method is replaced by `Meter.Int64UpDownCounter` - The `Histogram` method is replaced by `Meter.Int64Histogram` - The `InstrumentProvider` from `go.opentelemetry.io/otel/sdk/metric/syncfloat64` is removed. Use the new creation methods of the `Meter` in `go.opentelemetry.io/otel/sdk/metric` instead. (#3530) - The `Counter` method is replaced by `Meter.Float64Counter` - The `UpDownCounter` method is replaced by `Meter.Float64UpDownCounter` - The `Histogram` method is replaced by `Meter.Float64Histogram` ## [1.11.2/0.34.0] 2022-12-05 ### Added - The `WithView` `Option` is added to the `go.opentelemetry.io/otel/sdk/metric` package. This option is used to configure the view(s) a `MeterProvider` will use for all `Reader`s that are registered with it. (#3387) - Add Instrumentation Scope and Version as info metric and label in Prometheus exporter. This can be disabled using the `WithoutScopeInfo()` option added to that package.(#3273, #3357) - OTLP exporters now recognize: (#3363) - `OTEL_EXPORTER_OTLP_INSECURE` - `OTEL_EXPORTER_OTLP_TRACES_INSECURE` - `OTEL_EXPORTER_OTLP_METRICS_INSECURE` - `OTEL_EXPORTER_OTLP_CLIENT_KEY` - `OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY` - `OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY` - `OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE` - `OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE` - `OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE` - The `View` type and related `NewView` function to create a view according to the OpenTelemetry specification are added to `go.opentelemetry.io/otel/sdk/metric`. These additions are replacements for the `View` type and `New` function from `go.opentelemetry.io/otel/sdk/metric/view`. (#3459) - The `Instrument` and `InstrumentKind` type are added to `go.opentelemetry.io/otel/sdk/metric`. These additions are replacements for the `Instrument` and `InstrumentKind` types from `go.opentelemetry.io/otel/sdk/metric/view`. (#3459) - The `Stream` type is added to `go.opentelemetry.io/otel/sdk/metric` to define a metric data stream a view will produce. (#3459) - The `AssertHasAttributes` allows instrument authors to test that datapoints returned have appropriate attributes. (#3487) ### Changed - The `"go.opentelemetry.io/otel/sdk/metric".WithReader` option no longer accepts views to associate with the `Reader`. Instead, views are now registered directly with the `MeterProvider` via the new `WithView` option. The views registered with the `MeterProvider` apply to all `Reader`s. (#3387) - The `Temporality(view.InstrumentKind) metricdata.Temporality` and `Aggregation(view.InstrumentKind) aggregation.Aggregation` methods are added to the `"go.opentelemetry.io/otel/sdk/metric".Exporter` interface. (#3260) - The `Temporality(view.InstrumentKind) metricdata.Temporality` and `Aggregation(view.InstrumentKind) aggregation.Aggregation` methods are added to the `"go.opentelemetry.io/otel/exporters/otlp/otlpmetric".Client` interface. (#3260) - The `WithTemporalitySelector` and `WithAggregationSelector` `ReaderOption`s have been changed to `ManualReaderOption`s in the `go.opentelemetry.io/otel/sdk/metric` package. (#3260) - The periodic reader in the `go.opentelemetry.io/otel/sdk/metric` package now uses the temporality and aggregation selectors from its configured exporter instead of accepting them as options. (#3260) ### Fixed - The `go.opentelemetry.io/otel/exporters/prometheus` exporter fixes duplicated `_total` suffixes. (#3369) - Remove comparable requirement for `Reader`s. (#3387) - Cumulative metrics from the OpenCensus bridge (`go.opentelemetry.io/otel/bridge/opencensus`) are defined as monotonic sums, instead of non-monotonic. (#3389) - Asynchronous counters (`Counter` and `UpDownCounter`) from the metric SDK now produce delta sums when configured with delta temporality. (#3398) - Exported `Status` codes in the `go.opentelemetry.io/otel/exporters/zipkin` exporter are now exported as all upper case values. (#3340) - `Aggregation`s from `go.opentelemetry.io/otel/sdk/metric` with no data are not exported. (#3394, #3436) - Re-enabled Attribute Filters in the Metric SDK. (#3396) - Asynchronous callbacks are only called if they are registered with at least one instrument that does not use drop aggragation. (#3408) - Do not report empty partial-success responses in the `go.opentelemetry.io/otel/exporters/otlp` exporters. (#3438, #3432) - Handle partial success responses in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric` exporters. (#3162, #3440) - Prevent duplicate Prometheus description, unit, and type. (#3469) - Prevents panic when using incorrect `attribute.Value.As[Type]Slice()`. (#3489) ### Removed - The `go.opentelemetry.io/otel/exporters/otlp/otlpmetric.Client` interface is removed. (#3486) - The `go.opentelemetry.io/otel/exporters/otlp/otlpmetric.New` function is removed. Use the `otlpmetric[http|grpc].New` directly. (#3486) ### Deprecated - The `go.opentelemetry.io/otel/sdk/metric/view` package is deprecated. Use `Instrument`, `InstrumentKind`, `View`, and `NewView` in `go.opentelemetry.io/otel/sdk/metric` instead. (#3476) ## [1.11.1/0.33.0] 2022-10-19 ### Added - The Prometheus exporter in `go.opentelemetry.io/otel/exporters/prometheus` registers with a Prometheus registerer on creation. By default, it will register with the default Prometheus registerer. A non-default registerer can be used by passing the `WithRegisterer` option. (#3239) - Added the `WithAggregationSelector` option to the `go.opentelemetry.io/otel/exporters/prometheus` package to change the default `AggregationSelector` used. (#3341) - The Prometheus exporter in `go.opentelemetry.io/otel/exporters/prometheus` converts the `Resource` associated with metric exports into a `target_info` metric. (#3285) ### Changed - The `"go.opentelemetry.io/otel/exporters/prometheus".New` function is updated to return an error. It will return an error if the exporter fails to register with Prometheus. (#3239) ### Fixed - The URL-encoded values from the `OTEL_RESOURCE_ATTRIBUTES` environment variable are decoded. (#2963) - The `baggage.NewMember` function decodes the `value` parameter instead of directly using it. This fixes the implementation to be compliant with the W3C specification. (#3226) - Slice attributes of the `attribute` package are now comparable based on their value, not instance. (#3108 #3252) - The `Shutdown` and `ForceFlush` methods of the `"go.opentelemetry.io/otel/sdk/trace".TraceProvider` no longer return an error when no processor is registered. (#3268) - The Prometheus exporter in `go.opentelemetry.io/otel/exporters/prometheus` cumulatively sums histogram buckets. (#3281) - The sum of each histogram data point is now uniquely exported by the `go.opentelemetry.io/otel/exporters/otlpmetric` exporters. (#3284, #3293) - Recorded values for asynchronous counters (`Counter` and `UpDownCounter`) are interpreted as exact, not incremental, sum values by the metric SDK. (#3350, #3278) - `UpDownCounters` are now correctly output as Prometheus gauges in the `go.opentelemetry.io/otel/exporters/prometheus` exporter. (#3358) - The Prometheus exporter in `go.opentelemetry.io/otel/exporters/prometheus` no longer describes the metrics it will send to Prometheus on startup. Instead the exporter is defined as an "unchecked" collector for Prometheus. This fixes the `reader is not registered` warning currently emitted on startup. (#3291 #3342) - The `go.opentelemetry.io/otel/exporters/prometheus` exporter now correctly adds `_total` suffixes to counter metrics. (#3360) - The `go.opentelemetry.io/otel/exporters/prometheus` exporter now adds a unit suffix to metric names. This can be disabled using the `WithoutUnits()` option added to that package. (#3352) ## [1.11.0/0.32.3] 2022-10-12 ### Added - Add default User-Agent header to OTLP exporter requests (`go.opentelemetry.io/otel/exporters/otlptrace/otlptracegrpc` and `go.opentelemetry.io/otel/exporters/otlptrace/otlptracehttp`). (#3261) ### Changed - `span.SetStatus` has been updated such that calls that lower the status are now no-ops. (#3214) - Upgrade `golang.org/x/sys/unix` from `v0.0.0-20210423185535-09eb48e85fd7` to `v0.0.0-20220919091848-fb04ddd9f9c8`. This addresses [GO-2022-0493](https://pkg.go.dev/vuln/GO-2022-0493). (#3235) ## [0.32.2] Metric SDK (Alpha) - 2022-10-11 ### Added - Added an example of using metric views to customize instruments. (#3177) - Add default User-Agent header to OTLP exporter requests (`go.opentelemetry.io/otel/exporters/otlpmetric/otlpmetricgrpc` and `go.opentelemetry.io/otel/exporters/otlpmetric/otlpmetrichttp`). (#3261) ### Changed - Flush pending measurements with the `PeriodicReader` in the `go.opentelemetry.io/otel/sdk/metric` when `ForceFlush` or `Shutdown` are called. (#3220) - Update histogram default bounds to match the requirements of the latest specification. (#3222) - Encode the HTTP status code in the OpenTracing bridge (`go.opentelemetry.io/otel/bridge/opentracing`) as an integer. (#3265) ### Fixed - Use default view if instrument does not match any registered view of a reader. (#3224, #3237) - Return the same instrument every time a user makes the exact same instrument creation call. (#3229, #3251) - Return the existing instrument when a view transforms a creation call to match an existing instrument. (#3240, #3251) - Log a warning when a conflicting instrument (e.g. description, unit, data-type) is created instead of returning an error. (#3251) - The OpenCensus bridge no longer sends empty batches of metrics. (#3263) ## [0.32.1] Metric SDK (Alpha) - 2022-09-22 ### Changed - The Prometheus exporter sanitizes OpenTelemetry instrument names when exporting. Invalid characters are replaced with `_`. (#3212) ### Added - The metric portion of the OpenCensus bridge (`go.opentelemetry.io/otel/bridge/opencensus`) has been reintroduced. (#3192) - The OpenCensus bridge example (`go.opentelemetry.io/otel/example/opencensus`) has been reintroduced. (#3206) ### Fixed - Updated go.mods to point to valid versions of the sdk. (#3216) - Set the `MeterProvider` resource on all exported metric data. (#3218) ## [0.32.0] Revised Metric SDK (Alpha) - 2022-09-18 ### Changed - The metric SDK in `go.opentelemetry.io/otel/sdk/metric` is completely refactored to comply with the OpenTelemetry specification. Please see the package documentation for how the new SDK is initialized and configured. (#3175) - Update the minimum supported go version to go1.18. Removes support for go1.17 (#3179) ### Removed - The metric portion of the OpenCensus bridge (`go.opentelemetry.io/otel/bridge/opencensus`) has been removed. A new bridge compliant with the revised metric SDK will be added back in a future release. (#3175) - The `go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest` package is removed, see the new metric SDK. (#3175) - The `go.opentelemetry.io/otel/sdk/metric/aggregator/histogram` package is removed, see the new metric SDK. (#3175) - The `go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue` package is removed, see the new metric SDK. (#3175) - The `go.opentelemetry.io/otel/sdk/metric/aggregator/sum` package is removed, see the new metric SDK. (#3175) - The `go.opentelemetry.io/otel/sdk/metric/aggregator` package is removed, see the new metric SDK. (#3175) - The `go.opentelemetry.io/otel/sdk/metric/controller/basic` package is removed, see the new metric SDK. (#3175) - The `go.opentelemetry.io/otel/sdk/metric/controller/controllertest` package is removed, see the new metric SDK. (#3175) - The `go.opentelemetry.io/otel/sdk/metric/controller/time` package is removed, see the new metric SDK. (#3175) - The `go.opentelemetry.io/otel/sdk/metric/export/aggregation` package is removed, see the new metric SDK. (#3175) - The `go.opentelemetry.io/otel/sdk/metric/export` package is removed, see the new metric SDK. (#3175) - The `go.opentelemetry.io/otel/sdk/metric/metrictest` package is removed. A replacement package that supports the new metric SDK will be added back in a future release. (#3175) - The `go.opentelemetry.io/otel/sdk/metric/number` package is removed, see the new metric SDK. (#3175) - The `go.opentelemetry.io/otel/sdk/metric/processor/basic` package is removed, see the new metric SDK. (#3175) - The `go.opentelemetry.io/otel/sdk/metric/processor/processortest` package is removed, see the new metric SDK. (#3175) - The `go.opentelemetry.io/otel/sdk/metric/processor/reducer` package is removed, see the new metric SDK. (#3175) - The `go.opentelemetry.io/otel/sdk/metric/registry` package is removed, see the new metric SDK. (#3175) - The `go.opentelemetry.io/otel/sdk/metric/sdkapi` package is removed, see the new metric SDK. (#3175) - The `go.opentelemetry.io/otel/sdk/metric/selector/simple` package is removed, see the new metric SDK. (#3175) - The `"go.opentelemetry.io/otel/sdk/metric".ErrUninitializedInstrument` variable was removed. (#3175) - The `"go.opentelemetry.io/otel/sdk/metric".ErrBadInstrument` variable was removed. (#3175) - The `"go.opentelemetry.io/otel/sdk/metric".Accumulator` type was removed, see the `MeterProvider`in the new metric SDK. (#3175) - The `"go.opentelemetry.io/otel/sdk/metric".NewAccumulator` function was removed, see `NewMeterProvider`in the new metric SDK. (#3175) - The deprecated `"go.opentelemetry.io/otel/sdk/metric".AtomicFieldOffsets` function was removed. (#3175) ## [1.10.0] - 2022-09-09 ### Added - Support Go 1.19. (#3077) Include compatibility testing and document support. (#3077) - Support the OTLP ExportTracePartialSuccess response; these are passed to the registered error handler. (#3106) - Upgrade go.opentelemetry.io/proto/otlp from v0.18.0 to v0.19.0 (#3107) ### Changed - Fix misidentification of OpenTelemetry `SpanKind` in OpenTracing bridge (`go.opentelemetry.io/otel/bridge/opentracing`). (#3096) - Attempting to start a span with a nil `context` will no longer cause a panic. (#3110) - All exporters will be shutdown even if one reports an error (#3091) - Ensure valid UTF-8 when truncating over-length attribute values. (#3156) ## [1.9.0/0.0.3] - 2022-08-01 ### Added - Add support for Schema Files format 1.1.x (metric "split" transform) with the new `go.opentelemetry.io/otel/schema/v1.1` package. (#2999) - Add the `go.opentelemetry.io/otel/semconv/v1.11.0` package. The package contains semantic conventions from the `v1.11.0` version of the OpenTelemetry specification. (#3009) - Add the `go.opentelemetry.io/otel/semconv/v1.12.0` package. The package contains semantic conventions from the `v1.12.0` version of the OpenTelemetry specification. (#3010) - Add the `http.method` attribute to HTTP server metric from all `go.opentelemetry.io/otel/semconv/*` packages. (#3018) ### Fixed - Invalid warning for context setup being deferred in `go.opentelemetry.io/otel/bridge/opentracing` package. (#3029) ## [1.8.0/0.31.0] - 2022-07-08 ### Added - Add support for `opentracing.TextMap` format in the `Inject` and `Extract` methods of the `"go.opentelemetry.io/otel/bridge/opentracing".BridgeTracer` type. (#2911) ### Changed - The `crosslink` make target has been updated to use the `go.opentelemetry.io/build-tools/crosslink` package. (#2886) - In the `go.opentelemetry.io/otel/sdk/instrumentation` package rename `Library` to `Scope` and alias `Library` as `Scope` (#2976) - Move metric no-op implementation form `nonrecording` to `metric` package. (#2866) ### Removed - Support for go1.16. Support is now only for go1.17 and go1.18 (#2917) ### Deprecated - The `Library` struct in the `go.opentelemetry.io/otel/sdk/instrumentation` package is deprecated. Use the equivalent `Scope` struct instead. (#2977) - The `ReadOnlySpan.InstrumentationLibrary` method from the `go.opentelemetry.io/otel/sdk/trace` package is deprecated. Use the equivalent `ReadOnlySpan.InstrumentationScope` method instead. (#2977) ## [1.7.0/0.30.0] - 2022-04-28 ### Added - Add the `go.opentelemetry.io/otel/semconv/v1.8.0` package. The package contains semantic conventions from the `v1.8.0` version of the OpenTelemetry specification. (#2763) - Add the `go.opentelemetry.io/otel/semconv/v1.9.0` package. The package contains semantic conventions from the `v1.9.0` version of the OpenTelemetry specification. (#2792) - Add the `go.opentelemetry.io/otel/semconv/v1.10.0` package. The package contains semantic conventions from the `v1.10.0` version of the OpenTelemetry specification. (#2842) - Added an in-memory exporter to metrictest to aid testing with a full SDK. (#2776) ### Fixed - Globally delegated instruments are unwrapped before delegating asynchronous callbacks. (#2784) - Remove import of `testing` package in non-tests builds of the `go.opentelemetry.io/otel` package. (#2786) ### Changed - The `WithLabelEncoder` option from the `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric` package is renamed to `WithAttributeEncoder`. (#2790) - The `LabelFilterSelector` interface from `go.opentelemetry.io/otel/sdk/metric/processor/reducer` is renamed to `AttributeFilterSelector`. The method included in the renamed interface also changed from `LabelFilterFor` to `AttributeFilterFor`. (#2790) - The `Metadata.Labels` method from the `go.opentelemetry.io/otel/sdk/metric/export` package is renamed to `Metadata.Attributes`. Consequentially, the `Record` type from the same package also has had the embedded method renamed. (#2790) ### Deprecated - The `Iterator.Label` method in the `go.opentelemetry.io/otel/attribute` package is deprecated. Use the equivalent `Iterator.Attribute` method instead. (#2790) - The `Iterator.IndexedLabel` method in the `go.opentelemetry.io/otel/attribute` package is deprecated. Use the equivalent `Iterator.IndexedAttribute` method instead. (#2790) - The `MergeIterator.Label` method in the `go.opentelemetry.io/otel/attribute` package is deprecated. Use the equivalent `MergeIterator.Attribute` method instead. (#2790) ### Removed - Removed the `Batch` type from the `go.opentelemetry.io/otel/sdk/metric/metrictest` package. (#2864) - Removed the `Measurement` type from the `go.opentelemetry.io/otel/sdk/metric/metrictest` package. (#2864) ## [0.29.0] - 2022-04-11 ### Added - The metrics global package was added back into several test files. (#2764) - The `Meter` function is added back to the `go.opentelemetry.io/otel/metric/global` package. This function is a convenience function equivalent to calling `global.MeterProvider().Meter(...)`. (#2750) ### Removed - Removed module the `go.opentelemetry.io/otel/sdk/export/metric`. Use the `go.opentelemetry.io/otel/sdk/metric` module instead. (#2720) ### Changed - Don't panic anymore when setting a global MeterProvider to itself. (#2749) - Upgrade `go.opentelemetry.io/proto/otlp` in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric` from `v0.12.1` to `v0.15.0`. This replaces the use of the now deprecated `InstrumentationLibrary` and `InstrumentationLibraryMetrics` types and fields in the proto library with the equivalent `InstrumentationScope` and `ScopeMetrics`. (#2748) ## [1.6.3] - 2022-04-07 ### Fixed - Allow non-comparable global `MeterProvider`, `TracerProvider`, and `TextMapPropagator` types to be set. (#2772, #2773) ## [1.6.2] - 2022-04-06 ### Changed - Don't panic anymore when setting a global TracerProvider or TextMapPropagator to itself. (#2749) - Upgrade `go.opentelemetry.io/proto/otlp` in `go.opentelemetry.io/otel/exporters/otlp/otlptrace` from `v0.12.1` to `v0.15.0`. This replaces the use of the now deprecated `InstrumentationLibrary` and `InstrumentationLibrarySpans` types and fields in the proto library with the equivalent `InstrumentationScope` and `ScopeSpans`. (#2748) ## [1.6.1] - 2022-03-28 ### Fixed - The `go.opentelemetry.io/otel/schema/*` packages now use the correct schema URL for their `SchemaURL` constant. Instead of using `"https://opentelemetry.io/schemas/v"` they now use the correct URL without a `v` prefix, `"https://opentelemetry.io/schemas/"`. (#2743, #2744) ### Security - Upgrade `go.opentelemetry.io/proto/otlp` from `v0.12.0` to `v0.12.1`. This includes an indirect upgrade of `github.com/grpc-ecosystem/grpc-gateway` which resolves [a vulnerability](https://nvd.nist.gov/vuln/detail/CVE-2019-11254) from `gopkg.in/yaml.v2` in version `v2.2.3`. (#2724, #2728) ## [1.6.0/0.28.0] - 2022-03-23 ### ⚠️ Notice ⚠️ This update is a breaking change of the unstable Metrics API. Code instrumented with the `go.opentelemetry.io/otel/metric` will need to be modified. ### Added - Add metrics exponential histogram support. New mapping functions have been made available in `sdk/metric/aggregator/exponential/mapping` for other OpenTelemetry projects to take dependencies on. (#2502) - Add Go 1.18 to our compatibility tests. (#2679) - Allow configuring the Sampler with the `OTEL_TRACES_SAMPLER` and `OTEL_TRACES_SAMPLER_ARG` environment variables. (#2305, #2517) - Add the `metric/global` for obtaining and setting the global `MeterProvider`. (#2660) ### Changed - The metrics API has been significantly changed to match the revised OpenTelemetry specification. High-level changes include: - Synchronous and asynchronous instruments are now handled by independent `InstrumentProvider`s. These `InstrumentProvider`s are managed with a `Meter`. - Synchronous and asynchronous instruments are grouped into their own packages based on value types. - Asynchronous callbacks can now be registered with a `Meter`. Be sure to check out the metric module documentation for more information on how to use the revised API. (#2587, #2660) ### Fixed - Fallback to general attribute limits when span specific ones are not set in the environment. (#2675, #2677) ## [1.5.0] - 2022-03-16 ### Added - Log the Exporters configuration in the TracerProviders message. (#2578) - Added support to configure the span limits with environment variables. The following environment variables are supported. (#2606, #2637) - `OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT` - `OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT` - `OTEL_SPAN_EVENT_COUNT_LIMIT` - `OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT` - `OTEL_SPAN_LINK_COUNT_LIMIT` - `OTEL_LINK_ATTRIBUTE_COUNT_LIMIT` If the provided environment variables are invalid (negative), the default values would be used. - Rename the `gc` runtime name to `go` (#2560) - Add resource container ID detection. (#2418) - Add span attribute value length limit. The new `AttributeValueLengthLimit` field is added to the `"go.opentelemetry.io/otel/sdk/trace".SpanLimits` type to configure this limit for a `TracerProvider`. The default limit for this resource is "unlimited". (#2637) - Add the `WithRawSpanLimits` option to `go.opentelemetry.io/otel/sdk/trace`. This option replaces the `WithSpanLimits` option. Zero or negative values will not be changed to the default value like `WithSpanLimits` does. Setting a limit to zero will effectively disable the related resource it limits and setting to a negative value will mean that resource is unlimited. Consequentially, limits should be constructed using `NewSpanLimits` and updated accordingly. (#2637) ### Changed - Drop oldest tracestate `Member` when capacity is reached. (#2592) - Add event and link drop counts to the exported data from the `oltptrace` exporter. (#2601) - Unify path cleaning functionally in the `otlpmetric` and `otlptrace` configuration. (#2639) - Change the debug message from the `sdk/trace.BatchSpanProcessor` to reflect the count is cumulative. (#2640) - Introduce new internal `envconfig` package for OTLP exporters. (#2608) - If `http.Request.Host` is empty, fall back to use `URL.Host` when populating `http.host` in the `semconv` packages. (#2661) ### Fixed - Remove the OTLP trace exporter limit of SpanEvents when exporting. (#2616) - Default to port `4318` instead of `4317` for the `otlpmetrichttp` and `otlptracehttp` client. (#2614, #2625) - Unlimited span limits are now supported (negative values). (#2636, #2637) ### Deprecated - Deprecated `"go.opentelemetry.io/otel/sdk/trace".WithSpanLimits`. Use `WithRawSpanLimits` instead. That option allows setting unlimited and zero limits, this option does not. This option will be kept until the next major version incremented release. (#2637) ## [1.4.1] - 2022-02-16 ### Fixed - Fix race condition in reading the dropped spans number for the `BatchSpanProcessor`. (#2615) ## [1.4.0] - 2022-02-11 ### Added - Use `OTEL_EXPORTER_ZIPKIN_ENDPOINT` environment variable to specify zipkin collector endpoint. (#2490) - Log the configuration of `TracerProvider`s, and `Tracer`s for debugging. To enable use a logger with Verbosity (V level) `>=1`. (#2500) - Added support to configure the batch span-processor with environment variables. The following environment variables are used. (#2515) - `OTEL_BSP_SCHEDULE_DELAY` - `OTEL_BSP_EXPORT_TIMEOUT` - `OTEL_BSP_MAX_QUEUE_SIZE`. - `OTEL_BSP_MAX_EXPORT_BATCH_SIZE` ### Changed - Zipkin exporter exports `Resource` attributes in the `Tags` field. (#2589) ### Deprecated - Deprecate module the `go.opentelemetry.io/otel/sdk/export/metric`. Use the `go.opentelemetry.io/otel/sdk/metric` module instead. (#2382) - Deprecate `"go.opentelemetry.io/otel/sdk/metric".AtomicFieldOffsets`. (#2445) ### Fixed - Fixed the instrument kind for noop async instruments to correctly report an implementation. (#2461) - Fix UDP packets overflowing with Jaeger payloads. (#2489, #2512) - Change the `otlpmetric.Client` interface's `UploadMetrics` method to accept a single `ResourceMetrics` instead of a slice of them. (#2491) - Specify explicit buckets in Prometheus example, fixing issue where example only has `+inf` bucket. (#2419, #2493) - W3C baggage will now decode urlescaped values. (#2529) - Baggage members are now only validated once, when calling `NewMember` and not also when adding it to the baggage itself. (#2522) - The order attributes are dropped from spans in the `go.opentelemetry.io/otel/sdk/trace` package when capacity is reached is fixed to be in compliance with the OpenTelemetry specification. Instead of dropping the least-recently-used attribute, the last added attribute is dropped. This drop order still only applies to attributes with unique keys not already contained in the span. If an attribute is added with a key already contained in the span, that attribute is updated to the new value being added. (#2576) ### Removed - Updated `go.opentelemetry.io/proto/otlp` from `v0.11.0` to `v0.12.0`. This version removes a number of deprecated methods. (#2546) - [`Metric.GetIntGauge()`](https://pkg.go.dev/go.opentelemetry.io/proto/otlp@v0.11.0/metrics/v1#Metric.GetIntGauge) - [`Metric.GetIntHistogram()`](https://pkg.go.dev/go.opentelemetry.io/proto/otlp@v0.11.0/metrics/v1#Metric.GetIntHistogram) - [`Metric.GetIntSum()`](https://pkg.go.dev/go.opentelemetry.io/proto/otlp@v0.11.0/metrics/v1#Metric.GetIntSum) ## [1.3.0] - 2021-12-10 ### ⚠️ Notice ⚠️ We have updated the project minimum supported Go version to 1.16 ### Added - Added an internal Logger. This can be used by the SDK and API to provide users with feedback of the internal state. To enable verbose logs configure the logger which will print V(1) logs. For debugging information configure to print V(5) logs. (#2343) - Add the `WithRetry` `Option` and the `RetryConfig` type to the `go.opentelemetry.io/otel/exporter/otel/otlpmetric/otlpmetrichttp` package to specify retry behavior consistently. (#2425) - Add `SpanStatusFromHTTPStatusCodeAndSpanKind` to all `semconv` packages to return a span status code similar to `SpanStatusFromHTTPStatusCode`, but exclude `4XX` HTTP errors as span errors if the span is of server kind. (#2296) ### Changed - The `"go.opentelemetry.io/otel/exporter/otel/otlptrace/otlptracegrpc".Client` now uses the underlying gRPC `ClientConn` to handle name resolution, TCP connection establishment (with retries and backoff) and TLS handshakes, and handling errors on established connections by re-resolving the name and reconnecting. (#2329) - The `"go.opentelemetry.io/otel/exporter/otel/otlpmetric/otlpmetricgrpc".Client` now uses the underlying gRPC `ClientConn` to handle name resolution, TCP connection establishment (with retries and backoff) and TLS handshakes, and handling errors on established connections by re-resolving the name and reconnecting. (#2425) - The `"go.opentelemetry.io/otel/exporter/otel/otlpmetric/otlpmetricgrpc".RetrySettings` type is renamed to `RetryConfig`. (#2425) - The `go.opentelemetry.io/otel/exporter/otel/*` gRPC exporters now default to using the host's root CA set if none are provided by the user and `WithInsecure` is not specified. (#2432) - Change `resource.Default` to be evaluated the first time it is called, rather than on import. This allows the caller the option to update `OTEL_RESOURCE_ATTRIBUTES` first, such as with `os.Setenv`. (#2371) ### Fixed - The `go.opentelemetry.io/otel/exporter/otel/*` exporters are updated to handle per-signal and universal endpoints according to the OpenTelemetry specification. Any per-signal endpoint set via an `OTEL_EXPORTER_OTLP__ENDPOINT` environment variable is now used without modification of the path. When `OTEL_EXPORTER_OTLP_ENDPOINT` is set, if it contains a path, that path is used as a base path which per-signal paths are appended to. (#2433) - Basic metric controller updated to use sync.Map to avoid blocking calls (#2381) - The `go.opentelemetry.io/otel/exporter/jaeger` correctly sets the `otel.status_code` value to be a string of `ERROR` or `OK` instead of an integer code. (#2439, #2440) ### Deprecated - Deprecated the `"go.opentelemetry.io/otel/exporter/otel/otlpmetric/otlpmetrichttp".WithMaxAttempts` `Option`, use the new `WithRetry` `Option` instead. (#2425) - Deprecated the `"go.opentelemetry.io/otel/exporter/otel/otlpmetric/otlpmetrichttp".WithBackoff` `Option`, use the new `WithRetry` `Option` instead. (#2425) ### Removed - Remove the metric Processor's ability to convert cumulative to delta aggregation temporality. (#2350) - Remove the metric Bound Instruments interface and implementations. (#2399) - Remove the metric MinMaxSumCount kind aggregation and the corresponding OTLP export path. (#2423) - Metric SDK removes the "exact" aggregator for histogram instruments, as it performed a non-standard aggregation for OTLP export (creating repeated Gauge points) and worked its way into a number of confusing examples. (#2348) ## [1.2.0] - 2021-11-12 ### Changed - Metric SDK `export.ExportKind`, `export.ExportKindSelector` types have been renamed to `aggregation.Temporality` and `aggregation.TemporalitySelector` respectively to keep in line with current specification and protocol along with built-in selectors (e.g., `aggregation.CumulativeTemporalitySelector`, ...). (#2274) - The Metric `Exporter` interface now requires a `TemporalitySelector` method instead of an `ExportKindSelector`. (#2274) - Metrics API cleanup. The `metric/sdkapi` package has been created to relocate the API-to-SDK interface: - The following interface types simply moved from `metric` to `metric/sdkapi`: `Descriptor`, `MeterImpl`, `InstrumentImpl`, `SyncImpl`, `BoundSyncImpl`, `AsyncImpl`, `AsyncRunner`, `AsyncSingleRunner`, and `AsyncBatchRunner` - The following struct types moved and are replaced with type aliases, since they are exposed to the user: `Observation`, `Measurement`. - The No-op implementations of sync and async instruments are no longer exported, new functions `sdkapi.NewNoopAsyncInstrument()` and `sdkapi.NewNoopSyncInstrument()` are provided instead. (#2271) - Update the SDK `BatchSpanProcessor` to export all queued spans when `ForceFlush` is called. (#2080, #2335) ### Added - Add the `"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc".WithGRPCConn` option so the exporter can reuse an existing gRPC connection. (#2002) - Added a new `schema` module to help parse Schema Files in OTEP 0152 format. (#2267) - Added a new `MapCarrier` to the `go.opentelemetry.io/otel/propagation` package to hold propagated cross-cutting concerns as a `map[string]string` held in memory. (#2334) ## [1.1.0] - 2021-10-27 ### Added - Add the `"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc".WithGRPCConn` option so the exporter can reuse an existing gRPC connection. (#2002) - Add the `go.opentelemetry.io/otel/semconv/v1.7.0` package. The package contains semantic conventions from the `v1.7.0` version of the OpenTelemetry specification. (#2320) - Add the `go.opentelemetry.io/otel/semconv/v1.6.1` package. The package contains semantic conventions from the `v1.6.1` version of the OpenTelemetry specification. (#2321) - Add the `go.opentelemetry.io/otel/semconv/v1.5.0` package. The package contains semantic conventions from the `v1.5.0` version of the OpenTelemetry specification. (#2322) - When upgrading from the `semconv/v1.4.0` package note the following name changes: - `K8SReplicasetUIDKey` -> `K8SReplicaSetUIDKey` - `K8SReplicasetNameKey` -> `K8SReplicaSetNameKey` - `K8SStatefulsetUIDKey` -> `K8SStatefulSetUIDKey` - `k8SStatefulsetNameKey` -> `K8SStatefulSetNameKey` - `K8SDaemonsetUIDKey` -> `K8SDaemonSetUIDKey` - `K8SDaemonsetNameKey` -> `K8SDaemonSetNameKey` ### Changed - Links added to a span will be dropped by the SDK if they contain an invalid span context (#2275). ### Fixed - The `"go.opentelemetry.io/otel/semconv/v1.4.0".HTTPServerAttributesFromHTTPRequest` now correctly only sets the HTTP client IP attribute even if the connection was routed with proxies and there are multiple addresses in the `X-Forwarded-For` header. (#2282, #2284) - The `"go.opentelemetry.io/otel/semconv/v1.4.0".NetAttributesFromHTTPRequest` function correctly handles IPv6 addresses as IP addresses and sets the correct net peer IP instead of the net peer hostname attribute. (#2283, #2285) - The simple span processor shutdown method deterministically returns the exporter error status if it simultaneously finishes when the deadline is reached. (#2290, #2289) ## [1.0.1] - 2021-10-01 ### Fixed - json stdout exporter no longer crashes due to concurrency bug. (#2265) ## [Metrics 0.24.0] - 2021-10-01 ### Changed - NoopMeterProvider is now private and NewNoopMeterProvider must be used to obtain a noopMeterProvider. (#2237) - The Metric SDK `Export()` function takes a new two-level reader interface for iterating over results one instrumentation library at a time. (#2197) - The former `"go.opentelemetry.io/otel/sdk/export/metric".CheckpointSet` is renamed `Reader`. - The new interface is named `"go.opentelemetry.io/otel/sdk/export/metric".InstrumentationLibraryReader`. ## [1.0.0] - 2021-09-20 This is the first stable release for the project. This release includes an API and SDK for the tracing signal that will comply with the stability guarantees defined by the projects [versioning policy](./VERSIONING.md). ### Added - OTLP trace exporter now sets the `SchemaURL` field in the exported telemetry if the Tracer has `WithSchemaURL` option. (#2242) ### Fixed - Slice-valued attributes can correctly be used as map keys. (#2223) ### Removed - Removed the `"go.opentelemetry.io/otel/exporters/zipkin".WithSDKOptions` function. (#2248) - Removed the deprecated package `go.opentelemetry.io/otel/oteltest`. (#2234) - Removed the deprecated package `go.opentelemetry.io/otel/bridge/opencensus/utils`. (#2233) - Removed deprecated functions, types, and methods from `go.opentelemetry.io/otel/attribute` package. Use the typed functions and methods added to the package instead. (#2235) - The `Key.Array` method is removed. - The `Array` function is removed. - The `Any` function is removed. - The `ArrayValue` function is removed. - The `AsArray` function is removed. ## [1.0.0-RC3] - 2021-09-02 ### Added - Added `ErrorHandlerFunc` to use a function as an `"go.opentelemetry.io/otel".ErrorHandler`. (#2149) - Added `"go.opentelemetry.io/otel/trace".WithStackTrace` option to add a stack trace when using `span.RecordError` or when panic is handled in `span.End`. (#2163) - Added typed slice attribute types and functionality to the `go.opentelemetry.io/otel/attribute` package to replace the existing array type and functions. (#2162) - `BoolSlice`, `IntSlice`, `Int64Slice`, `Float64Slice`, and `StringSlice` replace the use of the `Array` function in the package. - Added the `go.opentelemetry.io/otel/example/fib` example package. Included is an example application that computes Fibonacci numbers. (#2203) ### Changed - Metric instruments have been renamed to match the (feature-frozen) metric API specification: - ValueRecorder becomes Histogram - ValueObserver becomes Gauge - SumObserver becomes CounterObserver - UpDownSumObserver becomes UpDownCounterObserver The API exported from this project is still considered experimental. (#2202) - Metric SDK/API implementation type `InstrumentKind` moves into `sdkapi` sub-package. (#2091) - The Metrics SDK export record no longer contains a Resource pointer, the SDK `"go.opentelemetry.io/otel/sdk/trace/export/metric".Exporter.Export()` function for push-based exporters now takes a single Resource argument, pull-based exporters use `"go.opentelemetry.io/otel/sdk/metric/controller/basic".Controller.Resource()`. (#2120) - The JSON output of the `go.opentelemetry.io/otel/exporters/stdout/stdouttrace` is harmonized now such that the output is "plain" JSON objects after each other of the form `{ ... } { ... } { ... }`. Earlier the JSON objects describing a span were wrapped in a slice for each `Exporter.ExportSpans` call, like `[ { ... } ][ { ... } { ... } ]`. Outputting JSON object directly after each other is consistent with JSON loggers, and a bit easier to parse and read. (#2196) - Update the `NewTracerConfig`, `NewSpanStartConfig`, `NewSpanEndConfig`, and `NewEventConfig` function in the `go.opentelemetry.io/otel/trace` package to return their respective configurations as structs instead of pointers to the struct. (#2212) ### Deprecated - The `go.opentelemetry.io/otel/bridge/opencensus/utils` package is deprecated. All functionality from this package now exists in the `go.opentelemetry.io/otel/bridge/opencensus` package. The functions from that package should be used instead. (#2166) - The `"go.opentelemetry.io/otel/attribute".Array` function and the related `ARRAY` value type is deprecated. Use the typed `*Slice` functions and types added to the package instead. (#2162) - The `"go.opentelemetry.io/otel/attribute".Any` function is deprecated. Use the typed functions instead. (#2181) - The `go.opentelemetry.io/otel/oteltest` package is deprecated. The `"go.opentelemetry.io/otel/sdk/trace/tracetest".SpanRecorder` can be registered with the default SDK (`go.opentelemetry.io/otel/sdk/trace`) as a `SpanProcessor` and used as a replacement for this deprecated package. (#2188) ### Removed - Removed metrics test package `go.opentelemetry.io/otel/sdk/export/metric/metrictest`. (#2105) ### Fixed - The `fromEnv` detector no longer throws an error when `OTEL_RESOURCE_ATTRIBUTES` environment variable is not set or empty. (#2138) - Setting the global `ErrorHandler` with `"go.opentelemetry.io/otel".SetErrorHandler` multiple times is now supported. (#2160, #2140) - The `"go.opentelemetry.io/otel/attribute".Any` function now supports `int32` values. (#2169) - Multiple calls to `"go.opentelemetry.io/otel/sdk/metric/controller/basic".WithResource()` are handled correctly, and when no resources are provided `"go.opentelemetry.io/otel/sdk/resource".Default()` is used. (#2120) - The `WithoutTimestamps` option for the `go.opentelemetry.io/otel/exporters/stdout/stdouttrace` exporter causes the exporter to correctly omit timestamps. (#2195) - Fixed typos in resources.go. (#2201) ## [1.0.0-RC2] - 2021-07-26 ### Added - Added `WithOSDescription` resource configuration option to set OS (Operating System) description resource attribute (`os.description`). (#1840) - Added `WithOS` resource configuration option to set all OS (Operating System) resource attributes at once. (#1840) - Added the `WithRetry` option to the `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp` package. This option is a replacement for the removed `WithMaxAttempts` and `WithBackoff` options. (#2095) - Added API `LinkFromContext` to return Link which encapsulates SpanContext from provided context and also encapsulates attributes. (#2115) - Added a new `Link` type under the SDK `otel/sdk/trace` package that counts the number of attributes that were dropped for surpassing the `AttributePerLinkCountLimit` configured in the Span's `SpanLimits`. This new type replaces the equal-named API `Link` type found in the `otel/trace` package for most usages within the SDK. For example, instances of this type are now returned by the `Links()` function of `ReadOnlySpan`s provided in places like the `OnEnd` function of `SpanProcessor` implementations. (#2118) - Added the `SpanRecorder` type to the `go.opentelemetry.io/otel/skd/trace/tracetest` package. This type can be used with the default SDK as a `SpanProcessor` during testing. (#2132) ### Changed - The `SpanModels` function is now exported from the `go.opentelemetry.io/otel/exporters/zipkin` package to convert OpenTelemetry spans into Zipkin model spans. (#2027) - Rename the `"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc".RetrySettings` to `RetryConfig`. (#2095) ### Deprecated - The `TextMapCarrier` and `TextMapPropagator` from the `go.opentelemetry.io/otel/oteltest` package and their associated creation functions (`TextMapCarrier`, `NewTextMapPropagator`) are deprecated. (#2114) - The `Harness` type from the `go.opentelemetry.io/otel/oteltest` package and its associated creation function, `NewHarness` are deprecated and will be removed in the next release. (#2123) - The `TraceStateFromKeyValues` function from the `go.opentelemetry.io/otel/oteltest` package is deprecated. Use the `trace.ParseTraceState` function instead. (#2122) ### Removed - Removed the deprecated package `go.opentelemetry.io/otel/exporters/trace/jaeger`. (#2020) - Removed the deprecated package `go.opentelemetry.io/otel/exporters/trace/zipkin`. (#2020) - Removed the `"go.opentelemetry.io/otel/sdk/resource".WithBuiltinDetectors` function. The explicit `With*` options for every built-in detector should be used instead. (#2026 #2097) - Removed the `WithMaxAttempts` and `WithBackoff` options from the `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp` package. The retry logic of the package has been updated to match the `otlptracegrpc` package and accordingly a `WithRetry` option is added that should be used instead. (#2095) - Removed `DroppedAttributeCount` field from `otel/trace.Link` struct. (#2118) ### Fixed - When using WithNewRoot, don't use the parent context for making sampling decisions. (#2032) - `oteltest.Tracer` now creates a valid `SpanContext` when using `WithNewRoot`. (#2073) - OS type detector now sets the correct `dragonflybsd` value for DragonFly BSD. (#2092) - The OTel span status is correctly transformed into the OTLP status in the `go.opentelemetry.io/otel/exporters/otlp/otlptrace` package. This fix will by default set the status to `Unset` if it is not explicitly set to `Ok` or `Error`. (#2099 #2102) - The `Inject` method for the `"go.opentelemetry.io/otel/propagation".TraceContext` type no longer injects empty `tracestate` values. (#2108) - Use `6831` as default Jaeger agent port instead of `6832`. (#2131) ## [Experimental Metrics v0.22.0] - 2021-07-19 ### Added - Adds HTTP support for OTLP metrics exporter. (#2022) ### Removed - Removed the deprecated package `go.opentelemetry.io/otel/exporters/metric/prometheus`. (#2020) ## [1.0.0-RC1] / 0.21.0 - 2021-06-18 With this release we are introducing a split in module versions. The tracing API and SDK are entering the `v1.0.0` Release Candidate phase with `v1.0.0-RC1` while the experimental metrics API and SDK continue with `v0.x` releases at `v0.21.0`. Modules at major version 1 or greater will not depend on modules with major version 0. ### Added - Adds `otlpgrpc.WithRetry`option for configuring the retry policy for transient errors on the otlp/gRPC exporter. (#1832) - The following status codes are defined as transient errors: | gRPC Status Code | Description | | ---------------- | ----------- | | 1 | Cancelled | | 4 | Deadline Exceeded | | 8 | Resource Exhausted | | 10 | Aborted | | 10 | Out of Range | | 14 | Unavailable | | 15 | Data Loss | - Added `Status` type to the `go.opentelemetry.io/otel/sdk/trace` package to represent the status of a span. (#1874) - Added `SpanStub` type and its associated functions to the `go.opentelemetry.io/otel/sdk/trace/tracetest` package. This type can be used as a testing replacement for the `SpanSnapshot` that was removed from the `go.opentelemetry.io/otel/sdk/trace` package. (#1873) - Adds support for scheme in `OTEL_EXPORTER_OTLP_ENDPOINT` according to the spec. (#1886) - Adds `trace.WithSchemaURL` option for configuring the tracer with a Schema URL. (#1889) - Added an example of using OpenTelemetry Go as a trace context forwarder. (#1912) - `ParseTraceState` is added to the `go.opentelemetry.io/otel/trace` package. It can be used to decode a `TraceState` from a `tracestate` header string value. (#1937) - Added `Len` method to the `TraceState` type in the `go.opentelemetry.io/otel/trace` package. This method returns the number of list-members the `TraceState` holds. (#1937) - Creates package `go.opentelemetry.io/otel/exporters/otlp/otlptrace` that defines a trace exporter that uses a `otlptrace.Client` to send data. Creates package `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc` implementing a gRPC `otlptrace.Client` and offers convenience functions, `NewExportPipeline` and `InstallNewPipeline`, to setup and install a `otlptrace.Exporter` in tracing .(#1922) - Added `Baggage`, `Member`, and `Property` types to the `go.opentelemetry.io/otel/baggage` package along with their related functions. (#1967) - Added `ContextWithBaggage`, `ContextWithoutBaggage`, and `FromContext` functions to the `go.opentelemetry.io/otel/baggage` package. These functions replace the `Set`, `Value`, `ContextWithValue`, `ContextWithoutValue`, and `ContextWithEmpty` functions from that package and directly work with the new `Baggage` type. (#1967) - The `OTEL_SERVICE_NAME` environment variable is the preferred source for `service.name`, used by the environment resource detector if a service name is present both there and in `OTEL_RESOURCE_ATTRIBUTES`. (#1969) - Creates package `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp` implementing an HTTP `otlptrace.Client` and offers convenience functions, `NewExportPipeline` and `InstallNewPipeline`, to setup and install a `otlptrace.Exporter` in tracing. (#1963) - Changes `go.opentelemetry.io/otel/sdk/resource.NewWithAttributes` to require a schema URL. The old function is still available as `resource.NewSchemaless`. This is a breaking change. (#1938) - Several builtin resource detectors now correctly populate the schema URL. (#1938) - Creates package `go.opentelemetry.io/otel/exporters/otlp/otlpmetric` that defines a metrics exporter that uses a `otlpmetric.Client` to send data. - Creates package `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` implementing a gRPC `otlpmetric.Client` and offers convenience functions, `New` and `NewUnstarted`, to create an `otlpmetric.Exporter`.(#1991) - Added `go.opentelemetry.io/otel/exporters/stdout/stdouttrace` exporter. (#2005) - Added `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric` exporter. (#2005) - Added a `TracerProvider()` method to the `"go.opentelemetry.io/otel/trace".Span` interface. This can be used to obtain a `TracerProvider` from a given span that utilizes the same trace processing pipeline. (#2009) ### Changed - Make `NewSplitDriver` from `go.opentelemetry.io/otel/exporters/otlp` take variadic arguments instead of a `SplitConfig` item. `NewSplitDriver` now automatically implements an internal `noopDriver` for `SplitConfig` fields that are not initialized. (#1798) - `resource.New()` now creates a Resource without builtin detectors. Previous behavior is now achieved by using `WithBuiltinDetectors` Option. (#1810) - Move the `Event` type from the `go.opentelemetry.io/otel` package to the `go.opentelemetry.io/otel/sdk/trace` package. (#1846) - CI builds validate against last two versions of Go, dropping 1.14 and adding 1.16. (#1865) - BatchSpanProcessor now report export failures when calling `ForceFlush()` method. (#1860) - `Set.Encoded(Encoder)` no longer caches the result of an encoding. (#1855) - Renamed `CloudZoneKey` to `CloudAvailabilityZoneKey` in Resource semantic conventions according to spec. (#1871) - The `StatusCode` and `StatusMessage` methods of the `ReadOnlySpan` interface and the `Span` produced by the `go.opentelemetry.io/otel/sdk/trace` package have been replaced with a single `Status` method. This method returns the status of a span using the new `Status` type. (#1874) - Updated `ExportSpans` method of the`SpanExporter` interface type to accept `ReadOnlySpan`s instead of the removed `SpanSnapshot`. This brings the export interface into compliance with the specification in that it now accepts an explicitly immutable type instead of just an implied one. (#1873) - Unembed `SpanContext` in `Link`. (#1877) - Generate Semantic conventions from the specification YAML. (#1891) - Spans created by the global `Tracer` obtained from `go.opentelemetry.io/otel`, prior to a functioning `TracerProvider` being set, now propagate the span context from their parent if one exists. (#1901) - The `"go.opentelemetry.io/otel".Tracer` function now accepts tracer options. (#1902) - Move the `go.opentelemetry.io/otel/unit` package to `go.opentelemetry.io/otel/metric/unit`. (#1903) - Changed `go.opentelemetry.io/otel/trace.TracerConfig` to conform to the [Contributing guidelines](CONTRIBUTING.md#config.) (#1921) - Changed `go.opentelemetry.io/otel/trace.SpanConfig` to conform to the [Contributing guidelines](CONTRIBUTING.md#config). (#1921) - Changed `span.End()` now only accepts Options that are allowed at `End()`. (#1921) - Changed `go.opentelemetry.io/otel/metric.InstrumentConfig` to conform to the [Contributing guidelines](CONTRIBUTING.md#config). (#1921) - Changed `go.opentelemetry.io/otel/metric.MeterConfig` to conform to the [Contributing guidelines](CONTRIBUTING.md#config). (#1921) - Refactored option types according to the contribution style guide. (#1882) - Move the `go.opentelemetry.io/otel/trace.TraceStateFromKeyValues` function to the `go.opentelemetry.io/otel/oteltest` package. This function is preserved for testing purposes where it may be useful to create a `TraceState` from `attribute.KeyValue`s, but it is not intended for production use. The new `ParseTraceState` function should be used to create a `TraceState`. (#1931) - Updated `MarshalJSON` method of the `go.opentelemetry.io/otel/trace.TraceState` type to marshal the type into the string representation of the `TraceState`. (#1931) - The `TraceState.Delete` method from the `go.opentelemetry.io/otel/trace` package no longer returns an error in addition to a `TraceState`. (#1931) - Updated `Get` method of the `TraceState` type from the `go.opentelemetry.io/otel/trace` package to accept a `string` instead of an `attribute.Key` type. (#1931) - Updated `Insert` method of the `TraceState` type from the `go.opentelemetry.io/otel/trace` package to accept a pair of `string`s instead of an `attribute.KeyValue` type. (#1931) - Updated `Delete` method of the `TraceState` type from the `go.opentelemetry.io/otel/trace` package to accept a `string` instead of an `attribute.Key` type. (#1931) - Renamed `NewExporter` to `New` in the `go.opentelemetry.io/otel/exporters/stdout` package. (#1985) - Renamed `NewExporter` to `New` in the `go.opentelemetry.io/otel/exporters/metric/prometheus` package. (#1985) - Renamed `NewExporter` to `New` in the `go.opentelemetry.io/otel/exporters/trace/jaeger` package. (#1985) - Renamed `NewExporter` to `New` in the `go.opentelemetry.io/otel/exporters/trace/zipkin` package. (#1985) - Renamed `NewExporter` to `New` in the `go.opentelemetry.io/otel/exporters/otlp` package. (#1985) - Renamed `NewUnstartedExporter` to `NewUnstarted` in the `go.opentelemetry.io/otel/exporters/otlp` package. (#1985) - The `go.opentelemetry.io/otel/semconv` package has been moved to `go.opentelemetry.io/otel/semconv/v1.4.0` to allow for multiple [telemetry schema](https://github.com/open-telemetry/oteps/blob/main/text/0152-telemetry-schemas.md) versions to be used concurrently. (#1987) - Metrics test helpers in `go.opentelemetry.io/otel/oteltest` have been moved to `go.opentelemetry.io/otel/metric/metrictest`. (#1988) ### Deprecated - The `go.opentelemetry.io/otel/exporters/metric/prometheus` is deprecated, use `go.opentelemetry.io/otel/exporters/prometheus` instead. (#1993) - The `go.opentelemetry.io/otel/exporters/trace/jaeger` is deprecated, use `go.opentelemetry.io/otel/exporters/jaeger` instead. (#1993) - The `go.opentelemetry.io/otel/exporters/trace/zipkin` is deprecated, use `go.opentelemetry.io/otel/exporters/zipkin` instead. (#1993) ### Removed - Removed `resource.WithoutBuiltin()`. Use `resource.New()`. (#1810) - Unexported types `resource.FromEnv`, `resource.Host`, and `resource.TelemetrySDK`, Use the corresponding `With*()` to use individually. (#1810) - Removed the `Tracer` and `IsRecording` method from the `ReadOnlySpan` in the `go.opentelemetry.io/otel/sdk/trace`. The `Tracer` method is not a required to be included in this interface and given the mutable nature of the tracer that is associated with a span, this method is not appropriate. The `IsRecording` method returns if the span is recording or not. A read-only span value does not need to know if updates to it will be recorded or not. By definition, it cannot be updated so there is no point in communicating if an update is recorded. (#1873) - Removed the `SpanSnapshot` type from the `go.opentelemetry.io/otel/sdk/trace` package. The use of this type has been replaced with the use of the explicitly immutable `ReadOnlySpan` type. When a concrete representation of a read-only span is needed for testing, the newly added `SpanStub` in the `go.opentelemetry.io/otel/sdk/trace/tracetest` package should be used. (#1873) - Removed the `Tracer` method from the `Span` interface in the `go.opentelemetry.io/otel/trace` package. Using the same tracer that created a span introduces the error where an instrumentation library's `Tracer` is used by other code instead of their own. The `"go.opentelemetry.io/otel".Tracer` function or a `TracerProvider` should be used to acquire a library specific `Tracer` instead. (#1900) - The `TracerProvider()` method on the `Span` interface may also be used to obtain a `TracerProvider` using the same trace processing pipeline. (#2009) - The `http.url` attribute generated by `HTTPClientAttributesFromHTTPRequest` will no longer include username or password information. (#1919) - Removed `IsEmpty` method of the `TraceState` type in the `go.opentelemetry.io/otel/trace` package in favor of using the added `TraceState.Len` method. (#1931) - Removed `Set`, `Value`, `ContextWithValue`, `ContextWithoutValue`, and `ContextWithEmpty` functions in the `go.opentelemetry.io/otel/baggage` package. Handling of baggage is now done using the added `Baggage` type and related context functions (`ContextWithBaggage`, `ContextWithoutBaggage`, and `FromContext`) in that package. (#1967) - The `InstallNewPipeline` and `NewExportPipeline` creation functions in all the exporters (prometheus, otlp, stdout, jaeger, and zipkin) have been removed. These functions were deemed premature attempts to provide convenience that did not achieve this aim. (#1985) - The `go.opentelemetry.io/otel/exporters/otlp` exporter has been removed. Use `go.opentelemetry.io/otel/exporters/otlp/otlptrace` instead. (#1990) - The `go.opentelemetry.io/otel/exporters/stdout` exporter has been removed. Use `go.opentelemetry.io/otel/exporters/stdout/stdouttrace` or `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric` instead. (#2005) ### Fixed - Only report errors from the `"go.opentelemetry.io/otel/sdk/resource".Environment` function when they are not `nil`. (#1850, #1851) - The `Shutdown` method of the simple `SpanProcessor` in the `go.opentelemetry.io/otel/sdk/trace` package now honors the context deadline or cancellation. (#1616, #1856) - BatchSpanProcessor now drops span batches that failed to be exported. (#1860) - Use `http://localhost:14268/api/traces` as default Jaeger collector endpoint instead of `http://localhost:14250`. (#1898) - Allow trailing and leading whitespace in the parsing of a `tracestate` header. (#1931) - Add logic to determine if the channel is closed to fix Jaeger exporter test panic with close closed channel. (#1870, #1973) - Avoid transport security when OTLP endpoint is a Unix socket. (#2001) ### Security ## [0.20.0] - 2021-04-23 ### Added - The OTLP exporter now has two new convenience functions, `NewExportPipeline` and `InstallNewPipeline`, setup and install the exporter in tracing and metrics pipelines. (#1373) - Adds semantic conventions for exceptions. (#1492) - Added Jaeger Environment variables: `OTEL_EXPORTER_JAEGER_AGENT_HOST`, `OTEL_EXPORTER_JAEGER_AGENT_PORT` These environment variables can be used to override Jaeger agent hostname and port (#1752) - Option `ExportTimeout` was added to batch span processor. (#1755) - `trace.TraceFlags` is now a defined type over `byte` and `WithSampled(bool) TraceFlags` and `IsSampled() bool` methods have been added to it. (#1770) - The `Event` and `Link` struct types from the `go.opentelemetry.io/otel` package now include a `DroppedAttributeCount` field to record the number of attributes that were not recorded due to configured limits being reached. (#1771) - The Jaeger exporter now reports dropped attributes for a Span event in the exported log. (#1771) - Adds test to check BatchSpanProcessor ignores `OnEnd` and `ForceFlush` post `Shutdown`. (#1772) - Extract resource attributes from the `OTEL_RESOURCE_ATTRIBUTES` environment variable and merge them with the `resource.Default` resource as well as resources provided to the `TracerProvider` and metric `Controller`. (#1785) - Added `WithOSType` resource configuration option to set OS (Operating System) type resource attribute (`os.type`). (#1788) - Added `WithProcess*` resource configuration options to set Process resource attributes. (#1788) - `process.pid` - `process.executable.name` - `process.executable.path` - `process.command_args` - `process.owner` - `process.runtime.name` - `process.runtime.version` - `process.runtime.description` - Adds `k8s.node.name` and `k8s.node.uid` attribute keys to the `semconv` package. (#1789) - Added support for configuring OTLP/HTTP and OTLP/gRPC Endpoints, TLS Certificates, Headers, Compression and Timeout via Environment Variables. (#1758, #1769 and #1811) - `OTEL_EXPORTER_OTLP_ENDPOINT` - `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` - `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` - `OTEL_EXPORTER_OTLP_HEADERS` - `OTEL_EXPORTER_OTLP_TRACES_HEADERS` - `OTEL_EXPORTER_OTLP_METRICS_HEADERS` - `OTEL_EXPORTER_OTLP_COMPRESSION` - `OTEL_EXPORTER_OTLP_TRACES_COMPRESSION` - `OTEL_EXPORTER_OTLP_METRICS_COMPRESSION` - `OTEL_EXPORTER_OTLP_TIMEOUT` - `OTEL_EXPORTER_OTLP_TRACES_TIMEOUT` - `OTEL_EXPORTER_OTLP_METRICS_TIMEOUT` - `OTEL_EXPORTER_OTLP_CERTIFICATE` - `OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE` - `OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE` - Adds `otlpgrpc.WithTimeout` option for configuring timeout to the otlp/gRPC exporter. (#1821) - Adds `jaeger.WithMaxPacketSize` option for configuring maximum UDP packet size used when connecting to the Jaeger agent. (#1853) ### Fixed - The `Span.IsRecording` implementation from `go.opentelemetry.io/otel/sdk/trace` always returns false when not being sampled. (#1750) - The Jaeger exporter now correctly sets tags for the Span status code and message. This means it uses the correct tag keys (`"otel.status_code"`, `"otel.status_description"`) and does not set the status message as a tag unless it is set on the span. (#1761) - The Jaeger exporter now correctly records Span event's names using the `"event"` key for a tag. Additionally, this tag is overridden, as specified in the OTel specification, if the event contains an attribute with that key. (#1768) - Zipkin Exporter: Ensure mapping between OTel and Zipkin span data complies with the specification. (#1688) - Fixed typo for default service name in Jaeger Exporter. (#1797) - Fix flaky OTLP for the reconnnection of the client connection. (#1527, #1814) - Fix Jaeger exporter dropping of span batches that exceed the UDP packet size limit. Instead, the exporter now splits the batch into smaller sendable batches. (#1828) ### Changed - Span `RecordError` now records an `exception` event to comply with the semantic convention specification. (#1492) - Jaeger exporter was updated to use thrift v0.14.1. (#1712) - Migrate from using internally built and maintained version of the OTLP to the one hosted at `go.opentelemetry.io/proto/otlp`. (#1713) - Migrate from using `github.com/gogo/protobuf` to `google.golang.org/protobuf` to match `go.opentelemetry.io/proto/otlp`. (#1713) - The storage of a local or remote Span in a `context.Context` using its SpanContext is unified to store just the current Span. The Span's SpanContext can now self-identify as being remote or not. This means that `"go.opentelemetry.io/otel/trace".ContextWithRemoteSpanContext` will now overwrite any existing current Span, not just existing remote Spans, and make it the current Span in a `context.Context`. (#1731) - Improve OTLP/gRPC exporter connection errors. (#1737) - Information about a parent span context in a `"go.opentelemetry.io/otel/export/trace".SpanSnapshot` is unified in a new `Parent` field. The existing `ParentSpanID` and `HasRemoteParent` fields are removed in favor of this. (#1748) - The `ParentContext` field of the `"go.opentelemetry.io/otel/sdk/trace".SamplingParameters` is updated to hold a `context.Context` containing the parent span. This changes it to make `SamplingParameters` conform with the OpenTelemetry specification. (#1749) - Updated Jaeger Environment Variables: `JAEGER_ENDPOINT`, `JAEGER_USER`, `JAEGER_PASSWORD` to `OTEL_EXPORTER_JAEGER_ENDPOINT`, `OTEL_EXPORTER_JAEGER_USER`, `OTEL_EXPORTER_JAEGER_PASSWORD` in compliance with OTel specification. (#1752) - Modify `BatchSpanProcessor.ForceFlush` to abort after timeout/cancellation. (#1757) - The `DroppedAttributeCount` field of the `Span` in the `go.opentelemetry.io/otel` package now only represents the number of attributes dropped for the span itself. It no longer is a conglomerate of itself, events, and link attributes that have been dropped. (#1771) - Make `ExportSpans` in Jaeger Exporter honor context deadline. (#1773) - Modify Zipkin Exporter default service name, use default resource's serviceName instead of empty. (#1777) - The `go.opentelemetry.io/otel/sdk/export/trace` package is merged into the `go.opentelemetry.io/otel/sdk/trace` package. (#1778) - The prometheus.InstallNewPipeline example is moved from comment to example test (#1796) - The convenience functions for the stdout exporter have been updated to return the `TracerProvider` implementation and enable the shutdown of the exporter. (#1800) - Replace the flush function returned from the Jaeger exporter's convenience creation functions (`InstallNewPipeline` and `NewExportPipeline`) with the `TracerProvider` implementation they create. This enables the caller to shutdown and flush using the related `TracerProvider` methods. (#1822) - Updated the Jaeger exporter to have a default endpoint, `http://localhost:14250`, for the collector. (#1824) - Changed the function `WithCollectorEndpoint` in the Jaeger exporter to no longer accept an endpoint as an argument. The endpoint can be passed with the `CollectorEndpointOption` using the `WithEndpoint` function or by setting the `OTEL_EXPORTER_JAEGER_ENDPOINT` environment variable value appropriately. (#1824) - The Jaeger exporter no longer batches exported spans itself, instead it relies on the SDK's `BatchSpanProcessor` for this functionality. (#1830) - The Jaeger exporter creation functions (`NewRawExporter`, `NewExportPipeline`, and `InstallNewPipeline`) no longer accept the removed `Option` type as a variadic argument. (#1830) ### Removed - Removed Jaeger Environment variables: `JAEGER_SERVICE_NAME`, `JAEGER_DISABLED`, `JAEGER_TAGS` These environment variables will no longer be used to override values of the Jaeger exporter (#1752) - No longer set the links for a `Span` in `go.opentelemetry.io/otel/sdk/trace` that is configured to be a new root. This is unspecified behavior that the OpenTelemetry community plans to standardize in the future. To prevent backwards incompatible changes when it is specified, these links are removed. (#1726) - Setting error status while recording error with Span from oteltest package. (#1729) - The concept of a remote and local Span stored in a context is unified to just the current Span. Because of this `"go.opentelemetry.io/otel/trace".RemoteSpanContextFromContext` is removed as it is no longer needed. Instead, `"go.opentelemetry.io/otel/trace".SpanContextFromContex` can be used to return the current Span. If needed, that Span's `SpanContext.IsRemote()` can then be used to determine if it is remote or not. (#1731) - The `HasRemoteParent` field of the `"go.opentelemetry.io/otel/sdk/trace".SamplingParameters` is removed. This field is redundant to the information returned from the `Remote` method of the `SpanContext` held in the `ParentContext` field. (#1749) - The `trace.FlagsDebug` and `trace.FlagsDeferred` constants have been removed and will be localized to the B3 propagator. (#1770) - Remove `Process` configuration, `WithProcessFromEnv` and `ProcessFromEnv`, and type from the Jaeger exporter package. The information that could be configured in the `Process` struct should be configured in a `Resource` instead. (#1776, #1804) - Remove the `WithDisabled` option from the Jaeger exporter. To disable the exporter unregister it from the `TracerProvider` or use a no-operation `TracerProvider`. (#1806) - Removed the functions `CollectorEndpointFromEnv` and `WithCollectorEndpointOptionFromEnv` from the Jaeger exporter. These functions for retrieving specific environment variable values are redundant of other internal functions and are not intended for end user use. (#1824) - Removed the Jaeger exporter `WithSDKOptions` `Option`. This option was used to set SDK options for the exporter creation convenience functions. These functions are provided as a way to easily setup or install the exporter with what are deemed reasonable SDK settings for common use cases. If the SDK needs to be configured differently, the `NewRawExporter` function and direct setup of the SDK with the desired settings should be used. (#1825) - The `WithBufferMaxCount` and `WithBatchMaxCount` `Option`s from the Jaeger exporter are removed. The exporter no longer batches exports, instead relying on the SDK's `BatchSpanProcessor` for this functionality. (#1830) - The Jaeger exporter `Option` type is removed. The type is no longer used by the exporter to configure anything. All the previous configurations these options provided were duplicates of SDK configuration. They have been removed in favor of using the SDK configuration and focuses the exporter configuration to be only about the endpoints it will send telemetry to. (#1830) ## [0.19.0] - 2021-03-18 ### Added - Added `Marshaler` config option to `otlphttp` to enable otlp over json or protobufs. (#1586) - A `ForceFlush` method to the `"go.opentelemetry.io/otel/sdk/trace".TracerProvider` to flush all registered `SpanProcessor`s. (#1608) - Added `WithSampler` and `WithSpanLimits` to tracer provider. (#1633, #1702) - `"go.opentelemetry.io/otel/trace".SpanContext` now has a `remote` property, and `IsRemote()` predicate, that is true when the `SpanContext` has been extracted from remote context data. (#1701) - A `Valid` method to the `"go.opentelemetry.io/otel/attribute".KeyValue` type. (#1703) ### Changed - `trace.SpanContext` is now immutable and has no exported fields. (#1573) - `trace.NewSpanContext()` can be used in conjunction with the `trace.SpanContextConfig` struct to initialize a new `SpanContext` where all values are known. - Update the `ForceFlush` method signature to the `"go.opentelemetry.io/otel/sdk/trace".SpanProcessor` to accept a `context.Context` and return an error. (#1608) - Update the `Shutdown` method to the `"go.opentelemetry.io/otel/sdk/trace".TracerProvider` return an error on shutdown failure. (#1608) - The SimpleSpanProcessor will now shut down the enclosed `SpanExporter` and gracefully ignore subsequent calls to `OnEnd` after `Shutdown` is called. (#1612) - `"go.opentelemetry.io/sdk/metric/controller.basic".WithPusher` is replaced with `WithExporter` to provide consistent naming across project. (#1656) - Added non-empty string check for trace `Attribute` keys. (#1659) - Add `description` to SpanStatus only when `StatusCode` is set to error. (#1662) - Jaeger exporter falls back to `resource.Default`'s `service.name` if the exported Span does not have one. (#1673) - Jaeger exporter populates Jaeger's Span Process from Resource. (#1673) - Renamed the `LabelSet` method of `"go.opentelemetry.io/otel/sdk/resource".Resource` to `Set`. (#1692) - Changed `WithSDK` to `WithSDKOptions` to accept variadic arguments of `TracerProviderOption` type in `go.opentelemetry.io/otel/exporters/trace/jaeger` package. (#1693) - Changed `WithSDK` to `WithSDKOptions` to accept variadic arguments of `TracerProviderOption` type in `go.opentelemetry.io/otel/exporters/trace/zipkin` package. (#1693) ### Removed - Removed `serviceName` parameter from Zipkin exporter and uses resource instead. (#1549) - Removed `WithConfig` from tracer provider to avoid overriding configuration. (#1633) - Removed the exported `SimpleSpanProcessor` and `BatchSpanProcessor` structs. These are now returned as a SpanProcessor interface from their respective constructors. (#1638) - Removed `WithRecord()` from `trace.SpanOption` when creating a span. (#1660) - Removed setting status to `Error` while recording an error as a span event in `RecordError`. (#1663) - Removed `jaeger.WithProcess` configuration option. (#1673) - Removed `ApplyConfig` method from `"go.opentelemetry.io/otel/sdk/trace".TracerProvider` and the now unneeded `Config` struct. (#1693) ### Fixed - Jaeger Exporter: Ensure mapping between OTEL and Jaeger span data complies with the specification. (#1626) - `SamplingResult.TraceState` is correctly propagated to a newly created span's `SpanContext`. (#1655) - The `otel-collector` example now correctly flushes metric events prior to shutting down the exporter. (#1678) - Do not set span status message in `SpanStatusFromHTTPStatusCode` if it can be inferred from `http.status_code`. (#1681) - Synchronization issues in global trace delegate implementation. (#1686) - Reduced excess memory usage by global `TracerProvider`. (#1687) ## [0.18.0] - 2021-03-03 ### Added - Added `resource.Default()` for use with meter and tracer providers. (#1507) - `AttributePerEventCountLimit` and `AttributePerLinkCountLimit` for `SpanLimits`. (#1535) - Added `Keys()` method to `propagation.TextMapCarrier` and `propagation.HeaderCarrier` to adapt `http.Header` to this interface. (#1544) - Added `code` attributes to `go.opentelemetry.io/otel/semconv` package. (#1558) - Compatibility testing suite in the CI system for the following systems. (#1567) | OS | Go Version | Architecture | | ------- | ---------- | ------------ | | Ubuntu | 1.15 | amd64 | | Ubuntu | 1.14 | amd64 | | Ubuntu | 1.15 | 386 | | Ubuntu | 1.14 | 386 | | MacOS | 1.15 | amd64 | | MacOS | 1.14 | amd64 | | Windows | 1.15 | amd64 | | Windows | 1.14 | amd64 | | Windows | 1.15 | 386 | | Windows | 1.14 | 386 | ### Changed - Replaced interface `oteltest.SpanRecorder` with its existing implementation `StandardSpanRecorder`. (#1542) - Default span limit values to 128. (#1535) - Rename `MaxEventsPerSpan`, `MaxAttributesPerSpan` and `MaxLinksPerSpan` to `EventCountLimit`, `AttributeCountLimit` and `LinkCountLimit`, and move these fields into `SpanLimits`. (#1535) - Renamed the `otel/label` package to `otel/attribute`. (#1541) - Vendor the Jaeger exporter's dependency on Apache Thrift. (#1551) - Parallelize the CI linting and testing. (#1567) - Stagger timestamps in exact aggregator tests. (#1569) - Changed all examples to use `WithBatchTimeout(5 * time.Second)` rather than `WithBatchTimeout(5)`. (#1621) - Prevent end-users from implementing some interfaces (#1575) ``` "otel/exporters/otlp/otlphttp".Option "otel/exporters/stdout".Option "otel/oteltest".Option "otel/trace".TracerOption "otel/trace".SpanOption "otel/trace".EventOption "otel/trace".LifeCycleOption "otel/trace".InstrumentationOption "otel/sdk/resource".Option "otel/sdk/trace".ParentBasedSamplerOption "otel/sdk/trace".ReadOnlySpan "otel/sdk/trace".ReadWriteSpan ``` ### Removed - Removed attempt to resample spans upon changing the span name with `span.SetName()`. (#1545) - The `test-benchmark` is no longer a dependency of the `precommit` make target. (#1567) - Removed the `test-386` make target. This was replaced with a full compatibility testing suite (i.e. multi OS/arch) in the CI system. (#1567) ### Fixed - The sequential timing check of timestamps in the stdout exporter are now setup explicitly to be sequential (#1571). (#1572) - Windows build of Jaeger tests now compiles with OS specific functions (#1576). (#1577) - The sequential timing check of timestamps of go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue are now setup explicitly to be sequential (#1578). (#1579) - Validate tracestate header keys with vendors according to the W3C TraceContext specification (#1475). (#1581) - The OTLP exporter includes related labels for translations of a GaugeArray (#1563). (#1570) ## [0.17.0] - 2021-02-12 ### Changed - Rename project default branch from `master` to `main`. (#1505) - Reverse order in which `Resource` attributes are merged, per change in spec. (#1501) - Add tooling to maintain "replace" directives in go.mod files automatically. (#1528) - Create new modules: otel/metric, otel/trace, otel/oteltest, otel/sdk/export/metric, otel/sdk/metric (#1528) - Move metric-related public global APIs from otel to otel/metric/global. (#1528) ## Fixed - Fixed otlpgrpc reconnection issue. - The example code in the README.md of `go.opentelemetry.io/otel/exporters/otlp` is moved to a compiled example test and used the new `WithAddress` instead of `WithEndpoint`. (#1513) - The otel-collector example now uses the default OTLP receiver port of the collector. ## [0.16.0] - 2021-01-13 ### Added - Add the `ReadOnlySpan` and `ReadWriteSpan` interfaces to provide better control for accessing span data. (#1360) - `NewGRPCDriver` function returns a `ProtocolDriver` that maintains a single gRPC connection to the collector. (#1369) - Added documentation about the project's versioning policy. (#1388) - Added `NewSplitDriver` for OTLP exporter that allows sending traces and metrics to different endpoints. (#1418) - Added codeql workflow to GitHub Actions (#1428) - Added Gosec workflow to GitHub Actions (#1429) - Add new HTTP driver for OTLP exporter in `exporters/otlp/otlphttp`. Currently it only supports the binary protobuf payloads. (#1420) - Add an OpenCensus exporter bridge. (#1444) ### Changed - Rename `internal/testing` to `internal/internaltest`. (#1449) - Rename `export.SpanData` to `export.SpanSnapshot` and use it only for exporting spans. (#1360) - Store the parent's full `SpanContext` rather than just its span ID in the `span` struct. (#1360) - Improve span duration accuracy. (#1360) - Migrated CI/CD from CircleCI to GitHub Actions (#1382) - Remove duplicate checkout from GitHub Actions workflow (#1407) - Metric `array` aggregator renamed `exact` to match its `aggregation.Kind` (#1412) - Metric `exact` aggregator includes per-point timestamps (#1412) - Metric stdout exporter uses MinMaxSumCount aggregator for ValueRecorder instruments (#1412) - `NewExporter` from `exporters/otlp` now takes a `ProtocolDriver` as a parameter. (#1369) - Many OTLP Exporter options became gRPC ProtocolDriver options. (#1369) - Unify endpoint API that related to OTel exporter. (#1401) - Optimize metric histogram aggregator to re-use its slice of buckets. (#1435) - Metric aggregator Count() and histogram Bucket.Counts are consistently `uint64`. (1430) - Histogram aggregator accepts functional options, uses default boundaries if none given. (#1434) - `SamplingResult` now passed a `Tracestate` from the parent `SpanContext` (#1432) - Moved gRPC driver for OTLP exporter to `exporters/otlp/otlpgrpc`. (#1420) - The `TraceContext` propagator now correctly propagates `TraceState` through the `SpanContext`. (#1447) - Metric Push and Pull Controller components are combined into a single "basic" Controller: - `WithExporter()` and `Start()` to configure Push behavior - `Start()` is optional; use `Collect()` and `ForEach()` for Pull behavior - `Start()` and `Stop()` accept Context. (#1378) - The `Event` type is moved from the `otel/sdk/export/trace` package to the `otel/trace` API package. (#1452) ### Removed - Remove `errUninitializedSpan` as its only usage is now obsolete. (#1360) - Remove Metric export functionality related to quantiles and summary data points: this is not specified (#1412) - Remove DDSketch metric aggregator; our intention is to re-introduce this as an option of the histogram aggregator after [new OTLP histogram data types](https://github.com/open-telemetry/opentelemetry-proto/pull/226) are released (#1412) ### Fixed - `BatchSpanProcessor.Shutdown()` will now shutdown underlying `export.SpanExporter`. (#1443) ## [0.15.0] - 2020-12-10 ### Added - The `WithIDGenerator` `TracerProviderOption` is added to the `go.opentelemetry.io/otel/trace` package to configure an `IDGenerator` for the `TracerProvider`. (#1363) ### Changed - The Zipkin exporter now uses the Span status code to determine. (#1328) - `NewExporter` and `Start` functions in `go.opentelemetry.io/otel/exporters/otlp` now receive `context.Context` as a first parameter. (#1357) - Move the OpenCensus example into `example` directory. (#1359) - Moved the SDK's `internal.IDGenerator` interface in to the `sdk/trace` package to enable support for externally-defined ID generators. (#1363) - Bump `github.com/google/go-cmp` from 0.5.3 to 0.5.4 (#1374) - Bump `github.com/golangci/golangci-lint` in `/internal/tools` (#1375) ### Fixed - Metric SDK `SumObserver` and `UpDownSumObserver` instruments correctness fixes. (#1381) ## [0.14.0] - 2020-11-19 ### Added - An `EventOption` and the related `NewEventConfig` function are added to the `go.opentelemetry.io/otel` package to configure Span events. (#1254) - A `TextMapPropagator` and associated `TextMapCarrier` are added to the `go.opentelemetry.io/otel/oteltest` package to test `TextMap` type propagators and their use. (#1259) - `SpanContextFromContext` returns `SpanContext` from context. (#1255) - `TraceState` has been added to `SpanContext`. (#1340) - `DeploymentEnvironmentKey` added to `go.opentelemetry.io/otel/semconv` package. (#1323) - Add an OpenCensus to OpenTelemetry tracing bridge. (#1305) - Add a parent context argument to `SpanProcessor.OnStart` to follow the specification. (#1333) - Add missing tests for `sdk/trace/attributes_map.go`. (#1337) ### Changed - Move the `go.opentelemetry.io/otel/api/trace` package into `go.opentelemetry.io/otel/trace` with the following changes. (#1229) (#1307) - `ID` has been renamed to `TraceID`. - `IDFromHex` has been renamed to `TraceIDFromHex`. - `EmptySpanContext` is removed. - Move the `go.opentelemetry.io/otel/api/trace/tracetest` package into `go.opentelemetry.io/otel/oteltest`. (#1229) - OTLP Exporter updates: - supports OTLP v0.6.0 (#1230, #1354) - supports configurable aggregation temporality (default: Cumulative, optional: Stateless). (#1296) - The Sampler is now called on local child spans. (#1233) - The `Kind` type from the `go.opentelemetry.io/otel/api/metric` package was renamed to `InstrumentKind` to more specifically describe what it is and avoid semantic ambiguity. (#1240) - The `MetricKind` method of the `Descriptor` type in the `go.opentelemetry.io/otel/api/metric` package was renamed to `Descriptor.InstrumentKind`. This matches the returned type and fixes misuse of the term metric. (#1240) - Move test harness from the `go.opentelemetry.io/otel/api/apitest` package into `go.opentelemetry.io/otel/oteltest`. (#1241) - Move the `go.opentelemetry.io/otel/api/metric/metrictest` package into `go.opentelemetry.io/oteltest` as part of #964. (#1252) - Move the `go.opentelemetry.io/otel/api/metric` package into `go.opentelemetry.io/otel/metric` as part of #1303. (#1321) - Move the `go.opentelemetry.io/otel/api/metric/registry` package into `go.opentelemetry.io/otel/metric/registry` as a part of #1303. (#1316) - Move the `Number` type (together with related functions) from `go.opentelemetry.io/otel/api/metric` package into `go.opentelemetry.io/otel/metric/number` as a part of #1303. (#1316) - The function signature of the Span `AddEvent` method in `go.opentelemetry.io/otel` is updated to no longer take an unused context and instead take a required name and a variable number of `EventOption`s. (#1254) - The function signature of the Span `RecordError` method in `go.opentelemetry.io/otel` is updated to no longer take an unused context and instead take a required error value and a variable number of `EventOption`s. (#1254) - Move the `go.opentelemetry.io/otel/api/global` package to `go.opentelemetry.io/otel`. (#1262) (#1330) - Move the `Version` function from `go.opentelemetry.io/otel/sdk` to `go.opentelemetry.io/otel`. (#1330) - Rename correlation context header from `"otcorrelations"` to `"baggage"` to match the OpenTelemetry specification. (#1267) - Fix `Code.UnmarshalJSON` to work with valid JSON only. (#1276) - The `resource.New()` method changes signature to support builtin attributes and functional options, including `telemetry.sdk.*` and `host.name` semantic conventions; the former method is renamed `resource.NewWithAttributes`. (#1235) - The Prometheus exporter now exports non-monotonic counters (i.e. `UpDownCounter`s) as gauges. (#1210) - Correct the `Span.End` method documentation in the `otel` API to state updates are not allowed on a span after it has ended. (#1310) - Updated span collection limits for attribute, event and link counts to 1000 (#1318) - Renamed `semconv.HTTPUrlKey` to `semconv.HTTPURLKey`. (#1338) ### Removed - The `ErrInvalidHexID`, `ErrInvalidTraceIDLength`, `ErrInvalidSpanIDLength`, `ErrInvalidSpanIDLength`, or `ErrNilSpanID` from the `go.opentelemetry.io/otel` package are unexported now. (#1243) - The `AddEventWithTimestamp` method on the `Span` interface in `go.opentelemetry.io/otel` is removed due to its redundancy. It is replaced by using the `AddEvent` method with a `WithTimestamp` option. (#1254) - The `MockSpan` and `MockTracer` types are removed from `go.opentelemetry.io/otel/oteltest`. `Tracer` and `Span` from the same module should be used in their place instead. (#1306) - `WorkerCount` option is removed from `go.opentelemetry.io/otel/exporters/otlp`. (#1350) - Remove the following labels types: INT32, UINT32, UINT64 and FLOAT32. (#1314) ### Fixed - Rename `MergeItererator` to `MergeIterator` in the `go.opentelemetry.io/otel/label` package. (#1244) - The `go.opentelemetry.io/otel/api/global` packages global TextMapPropagator now delegates functionality to a globally set delegate for all previously returned propagators. (#1258) - Fix condition in `label.Any`. (#1299) - Fix global `TracerProvider` to pass options to its configured provider. (#1329) - Fix missing handler for `ExactKind` aggregator in OTLP metrics transformer (#1309) ## [0.13.0] - 2020-10-08 ### Added - OTLP Metric exporter supports Histogram aggregation. (#1209) - The `Code` struct from the `go.opentelemetry.io/otel/codes` package now supports JSON marshaling and unmarshaling as well as implements the `Stringer` interface. (#1214) - A Baggage API to implement the OpenTelemetry specification. (#1217) - Add Shutdown method to sdk/trace/provider, shutdown processors in the order they were registered. (#1227) ### Changed - Set default propagator to no-op propagator. (#1184) - The `HTTPSupplier`, `HTTPExtractor`, `HTTPInjector`, and `HTTPPropagator` from the `go.opentelemetry.io/otel/api/propagation` package were replaced with unified `TextMapCarrier` and `TextMapPropagator` in the `go.opentelemetry.io/otel/propagation` package. (#1212) (#1325) - The `New` function from the `go.opentelemetry.io/otel/api/propagation` package was replaced with `NewCompositeTextMapPropagator` in the `go.opentelemetry.io/otel` package. (#1212) - The status codes of the `go.opentelemetry.io/otel/codes` package have been updated to match the latest OpenTelemetry specification. They now are `Unset`, `Error`, and `Ok`. They no longer track the gRPC codes. (#1214) - The `StatusCode` field of the `SpanData` struct in the `go.opentelemetry.io/otel/sdk/export/trace` package now uses the codes package from this package instead of the gRPC project. (#1214) - Move the `go.opentelemetry.io/otel/api/baggage` package into `go.opentelemetry.io/otel/baggage`. (#1217) (#1325) - A `Shutdown` method of `SpanProcessor` and all its implementations receives a context and returns an error. (#1264) ### Fixed - Copies of data from arrays and slices passed to `go.opentelemetry.io/otel/label.ArrayValue()` are now used in the returned `Value` instead of using the mutable data itself. (#1226) ### Removed - The `ExtractHTTP` and `InjectHTTP` functions from the `go.opentelemetry.io/otel/api/propagation` package were removed. (#1212) - The `Propagators` interface from the `go.opentelemetry.io/otel/api/propagation` package was removed to conform to the OpenTelemetry specification. The explicit `TextMapPropagator` type can be used in its place as this is the `Propagator` type the specification defines. (#1212) - The `SetAttribute` method of the `Span` from the `go.opentelemetry.io/otel/api/trace` package was removed given its redundancy with the `SetAttributes` method. (#1216) - The internal implementation of Baggage storage is removed in favor of using the new Baggage API functionality. (#1217) - Remove duplicate hostname key `HostHostNameKey` in Resource semantic conventions. (#1219) - Nested array/slice support has been removed. (#1226) ## [0.12.0] - 2020-09-24 ### Added - A `SpanConfigure` function in `go.opentelemetry.io/otel/api/trace` to create a new `SpanConfig` from `SpanOption`s. (#1108) - In the `go.opentelemetry.io/otel/api/trace` package, `NewTracerConfig` was added to construct new `TracerConfig`s. This addition was made to conform with our project option conventions. (#1155) - Instrumentation library information was added to the Zipkin exporter. (#1119) - The `SpanProcessor` interface now has a `ForceFlush()` method. (#1166) - More semantic conventions for k8s as resource attributes. (#1167) ### Changed - Add reconnecting udp connection type to Jaeger exporter. This change adds a new optional implementation of the udp conn interface used to detect changes to an agent's host dns record. It then adopts the new destination address to ensure the exporter doesn't get stuck. This change was ported from jaegertracing/jaeger-client-go#520. (#1063) - Replace `StartOption` and `EndOption` in `go.opentelemetry.io/otel/api/trace` with `SpanOption`. This change is matched by replacing the `StartConfig` and `EndConfig` with a unified `SpanConfig`. (#1108) - Replace the `LinkedTo` span option in `go.opentelemetry.io/otel/api/trace` with `WithLinks`. This is be more consistent with our other option patterns, i.e. passing the item to be configured directly instead of its component parts, and provides a cleaner function signature. (#1108) - The `go.opentelemetry.io/otel/api/trace` `TracerOption` was changed to an interface to conform to project option conventions. (#1109) - Move the `B3` and `TraceContext` from within the `go.opentelemetry.io/otel/api/trace` package to their own `go.opentelemetry.io/otel/propagators` package. This removal of the propagators is reflective of the OpenTelemetry specification for these propagators as well as cleans up the `go.opentelemetry.io/otel/api/trace` API. (#1118) - Rename Jaeger tags used for instrumentation library information to reflect changes in OpenTelemetry specification. (#1119) - Rename `ProbabilitySampler` to `TraceIDRatioBased` and change semantics to ignore parent span sampling status. (#1115) - Move `tools` package under `internal`. (#1141) - Move `go.opentelemetry.io/otel/api/correlation` package to `go.opentelemetry.io/otel/api/baggage`. (#1142) The `correlation.CorrelationContext` propagator has been renamed `baggage.Baggage`. Other exported functions and types are unchanged. - Rename `ParentOrElse` sampler to `ParentBased` and allow setting samplers depending on parent span. (#1153) - In the `go.opentelemetry.io/otel/api/trace` package, `SpanConfigure` was renamed to `NewSpanConfig`. (#1155) - Change `dependabot.yml` to add a `Skip Changelog` label to dependabot-sourced PRs. (#1161) - The [configuration style guide](https://github.com/open-telemetry/opentelemetry-go/blob/master/CONTRIBUTING.md#config) has been updated to recommend the use of `newConfig()` instead of `configure()`. (#1163) - The `otlp.Config` type has been unexported and changed to `otlp.config`, along with its initializer. (#1163) - Ensure exported interface types include parameter names and update the Style Guide to reflect this styling rule. (#1172) - Don't consider unset environment variable for resource detection to be an error. (#1170) - Rename `go.opentelemetry.io/otel/api/metric.ConfigureInstrument` to `NewInstrumentConfig` and `go.opentelemetry.io/otel/api/metric.ConfigureMeter` to `NewMeterConfig`. - ValueObserver instruments use LastValue aggregator by default. (#1165) - OTLP Metric exporter supports LastValue aggregation. (#1165) - Move the `go.opentelemetry.io/otel/api/unit` package to `go.opentelemetry.io/otel/unit`. (#1185) - Rename `Provider` to `MeterProvider` in the `go.opentelemetry.io/otel/api/metric` package. (#1190) - Rename `NoopProvider` to `NoopMeterProvider` in the `go.opentelemetry.io/otel/api/metric` package. (#1190) - Rename `NewProvider` to `NewMeterProvider` in the `go.opentelemetry.io/otel/api/metric/metrictest` package. (#1190) - Rename `Provider` to `MeterProvider` in the `go.opentelemetry.io/otel/api/metric/registry` package. (#1190) - Rename `NewProvider` to `NewMeterProvider` in the `go.opentelemetry.io/otel/api/metri/registryc` package. (#1190) - Rename `Provider` to `TracerProvider` in the `go.opentelemetry.io/otel/api/trace` package. (#1190) - Rename `NoopProvider` to `NoopTracerProvider` in the `go.opentelemetry.io/otel/api/trace` package. (#1190) - Rename `Provider` to `TracerProvider` in the `go.opentelemetry.io/otel/api/trace/tracetest` package. (#1190) - Rename `NewProvider` to `NewTracerProvider` in the `go.opentelemetry.io/otel/api/trace/tracetest` package. (#1190) - Rename `WrapperProvider` to `WrapperTracerProvider` in the `go.opentelemetry.io/otel/bridge/opentracing` package. (#1190) - Rename `NewWrapperProvider` to `NewWrapperTracerProvider` in the `go.opentelemetry.io/otel/bridge/opentracing` package. (#1190) - Rename `Provider` method of the pull controller to `MeterProvider` in the `go.opentelemetry.io/otel/sdk/metric/controller/pull` package. (#1190) - Rename `Provider` method of the push controller to `MeterProvider` in the `go.opentelemetry.io/otel/sdk/metric/controller/push` package. (#1190) - Rename `ProviderOptions` to `TracerProviderConfig` in the `go.opentelemetry.io/otel/sdk/trace` package. (#1190) - Rename `ProviderOption` to `TracerProviderOption` in the `go.opentelemetry.io/otel/sdk/trace` package. (#1190) - Rename `Provider` to `TracerProvider` in the `go.opentelemetry.io/otel/sdk/trace` package. (#1190) - Rename `NewProvider` to `NewTracerProvider` in the `go.opentelemetry.io/otel/sdk/trace` package. (#1190) - Renamed `SamplingDecision` values to comply with OpenTelemetry specification change. (#1192) - Renamed Zipkin attribute names from `ot.status_code & ot.status_description` to `otel.status_code & otel.status_description`. (#1201) - The default SDK now invokes registered `SpanProcessor`s in the order they were registered with the `TracerProvider`. (#1195) - Add test of spans being processed by the `SpanProcessor`s in the order they were registered. (#1203) ### Removed - Remove the B3 propagator from `go.opentelemetry.io/otel/propagators`. It is now located in the `go.opentelemetry.io/contrib/propagators/` module. (#1191) - Remove the semantic convention for HTTP status text, `HTTPStatusTextKey` from package `go.opentelemetry.io/otel/semconv`. (#1194) ### Fixed - Zipkin example no longer mentions `ParentSampler`, corrected to `ParentBased`. (#1171) - Fix missing shutdown processor in otel-collector example. (#1186) - Fix missing shutdown processor in basic and namedtracer examples. (#1197) ## [0.11.0] - 2020-08-24 ### Added - Support for exporting array-valued attributes via OTLP. (#992) - `Noop` and `InMemory` `SpanBatcher` implementations to help with testing integrations. (#994) - Support for filtering metric label sets. (#1047) - A dimensionality-reducing metric Processor. (#1057) - Integration tests for more OTel Collector Attribute types. (#1062) - A new `WithSpanProcessor` `ProviderOption` is added to the `go.opentelemetry.io/otel/sdk/trace` package to create a `Provider` and automatically register the `SpanProcessor`. (#1078) ### Changed - Rename `sdk/metric/processor/test` to `sdk/metric/processor/processortest`. (#1049) - Rename `sdk/metric/controller/test` to `sdk/metric/controller/controllertest`. (#1049) - Rename `api/testharness` to `api/apitest`. (#1049) - Rename `api/trace/testtrace` to `api/trace/tracetest`. (#1049) - Change Metric Processor to merge multiple observations. (#1024) - The `go.opentelemetry.io/otel/bridge/opentracing` bridge package has been made into its own module. This removes the package dependencies of this bridge from the rest of the OpenTelemetry based project. (#1038) - Renamed `go.opentelemetry.io/otel/api/standard` package to `go.opentelemetry.io/otel/semconv` to avoid the ambiguous and generic name `standard` and better describe the package as containing OpenTelemetry semantic conventions. (#1016) - The environment variable used for resource detection has been changed from `OTEL_RESOURCE_LABELS` to `OTEL_RESOURCE_ATTRIBUTES` (#1042) - Replace `WithSyncer` with `WithBatcher` in examples. (#1044) - Replace the `google.golang.org/grpc/codes` dependency in the API with an equivalent `go.opentelemetry.io/otel/codes` package. (#1046) - Merge the `go.opentelemetry.io/otel/api/label` and `go.opentelemetry.io/otel/api/kv` into the new `go.opentelemetry.io/otel/label` package. (#1060) - Unify Callback Function Naming. Rename `*Callback` with `*Func`. (#1061) - CI builds validate against last two versions of Go, dropping 1.13 and adding 1.15. (#1064) - The `go.opentelemetry.io/otel/sdk/export/trace` interfaces `SpanSyncer` and `SpanBatcher` have been replaced with a specification compliant `Exporter` interface. This interface still supports the export of `SpanData`, but only as a slice. Implementation are also required now to return any error from `ExportSpans` if one occurs as well as implement a `Shutdown` method for exporter clean-up. (#1078) - The `go.opentelemetry.io/otel/sdk/trace` `NewBatchSpanProcessor` function no longer returns an error. If a `nil` exporter is passed as an argument to this function, instead of it returning an error, it now returns a `BatchSpanProcessor` that handles the export of `SpanData` by not taking any action. (#1078) - The `go.opentelemetry.io/otel/sdk/trace` `NewProvider` function to create a `Provider` no longer returns an error, instead only a `*Provider`. This change is related to `NewBatchSpanProcessor` not returning an error which was the only error this function would return. (#1078) ### Removed - Duplicate, unused API sampler interface. (#999) Use the [`Sampler` interface](https://github.com/open-telemetry/opentelemetry-go/blob/v0.11.0/sdk/trace/sampling.go) provided by the SDK instead. - The `grpctrace` instrumentation was moved to the `go.opentelemetry.io/contrib` repository and out of this repository. This move includes moving the `grpc` example to the `go.opentelemetry.io/contrib` as well. (#1027) - The `WithSpan` method of the `Tracer` interface. The functionality this method provided was limited compared to what a user can provide themselves. It was removed with the understanding that if there is sufficient user need it can be added back based on actual user usage. (#1043) - The `RegisterSpanProcessor` and `UnregisterSpanProcessor` functions. These were holdovers from an approach prior to the TracerProvider design. They were not used anymore. (#1077) - The `oterror` package. (#1026) - The `othttp` and `httptrace` instrumentations were moved to `go.opentelemetry.io/contrib`. (#1032) ### Fixed - The `semconv.HTTPServerMetricAttributesFromHTTPRequest()` function no longer generates the high-cardinality `http.request.content.length` label. (#1031) - Correct instrumentation version tag in Jaeger exporter. (#1037) - The SDK span will now set an error event if the `End` method is called during a panic (i.e. it was deferred). (#1043) - Move internally generated protobuf code from the `go.opentelemetry.io/otel` to the OTLP exporter to reduce dependency overhead. (#1050) - The `otel-collector` example referenced outdated collector processors. (#1006) ## [0.10.0] - 2020-07-29 This release migrates the default OpenTelemetry SDK into its own Go module, decoupling the SDK from the API and reducing dependencies for instrumentation packages. ### Added - The Zipkin exporter now has `NewExportPipeline` and `InstallNewPipeline` constructor functions to match the common pattern. These function build a new exporter with default SDK options and register the exporter with the `global` package respectively. (#944) - Add propagator option for gRPC instrumentation. (#986) - The `testtrace` package now tracks the `trace.SpanKind` for each span. (#987) ### Changed - Replace the `RegisterGlobal` `Option` in the Jaeger exporter with an `InstallNewPipeline` constructor function. This matches the other exporter constructor patterns and will register a new exporter after building it with default configuration. (#944) - The trace (`go.opentelemetry.io/otel/exporters/trace/stdout`) and metric (`go.opentelemetry.io/otel/exporters/metric/stdout`) `stdout` exporters are now merged into a single exporter at `go.opentelemetry.io/otel/exporters/stdout`. This new exporter was made into its own Go module to follow the pattern of all exporters and decouple it from the `go.opentelemetry.io/otel` module. (#956, #963) - Move the `go.opentelemetry.io/otel/exporters/test` test package to `go.opentelemetry.io/otel/sdk/export/metric/metrictest`. (#962) - The `go.opentelemetry.io/otel/api/kv/value` package was merged into the parent `go.opentelemetry.io/otel/api/kv` package. (#968) - `value.Bool` was replaced with `kv.BoolValue`. - `value.Int64` was replaced with `kv.Int64Value`. - `value.Uint64` was replaced with `kv.Uint64Value`. - `value.Float64` was replaced with `kv.Float64Value`. - `value.Int32` was replaced with `kv.Int32Value`. - `value.Uint32` was replaced with `kv.Uint32Value`. - `value.Float32` was replaced with `kv.Float32Value`. - `value.String` was replaced with `kv.StringValue`. - `value.Int` was replaced with `kv.IntValue`. - `value.Uint` was replaced with `kv.UintValue`. - `value.Array` was replaced with `kv.ArrayValue`. - Rename `Infer` to `Any` in the `go.opentelemetry.io/otel/api/kv` package. (#972) - Change `othttp` to use the `httpsnoop` package to wrap the `ResponseWriter` so that optional interfaces (`http.Hijacker`, `http.Flusher`, etc.) that are implemented by the original `ResponseWriter`are also implemented by the wrapped `ResponseWriter`. (#979) - Rename `go.opentelemetry.io/otel/sdk/metric/aggregator/test` package to `go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest`. (#980) - Make the SDK into its own Go module called `go.opentelemetry.io/otel/sdk`. (#985) - Changed the default trace `Sampler` from `AlwaysOn` to `ParentOrElse(AlwaysOn)`. (#989) ### Removed - The `IndexedAttribute` function from the `go.opentelemetry.io/otel/api/label` package was removed in favor of `IndexedLabel` which it was synonymous with. (#970) ### Fixed - Bump github.com/golangci/golangci-lint from 1.28.3 to 1.29.0 in /tools. (#953) - Bump github.com/google/go-cmp from 0.5.0 to 0.5.1. (#957) - Use `global.Handle` for span export errors in the OTLP exporter. (#946) - Correct Go language formatting in the README documentation. (#961) - Remove default SDK dependencies from the `go.opentelemetry.io/otel/api` package. (#977) - Remove default SDK dependencies from the `go.opentelemetry.io/otel/instrumentation` package. (#983) - Move documented examples for `go.opentelemetry.io/otel/instrumentation/grpctrace` interceptors into Go example tests. (#984) ## [0.9.0] - 2020-07-20 ### Added - A new Resource Detector interface is included to allow resources to be automatically detected and included. (#939) - A Detector to automatically detect resources from an environment variable. (#939) - Github action to generate protobuf Go bindings locally in `internal/opentelemetry-proto-gen`. (#938) - OTLP .proto files from `open-telemetry/opentelemetry-proto` imported as a git submodule under `internal/opentelemetry-proto`. References to `github.com/open-telemetry/opentelemetry-proto` changed to `go.opentelemetry.io/otel/internal/opentelemetry-proto-gen`. (#942) ### Changed - Non-nil value `struct`s for key-value pairs will be marshalled using JSON rather than `Sprintf`. (#948) ### Removed - Removed dependency on `github.com/open-telemetry/opentelemetry-collector`. (#943) ## [0.8.0] - 2020-07-09 ### Added - The `B3Encoding` type to represent the B3 encoding(s) the B3 propagator can inject. A value for HTTP supported encodings (Multiple Header: `MultipleHeader`, Single Header: `SingleHeader`) are included. (#882) - The `FlagsDeferred` trace flag to indicate if the trace sampling decision has been deferred. (#882) - The `FlagsDebug` trace flag to indicate if the trace is a debug trace. (#882) - Add `peer.service` semantic attribute. (#898) - Add database-specific semantic attributes. (#899) - Add semantic convention for `faas.coldstart` and `container.id`. (#909) - Add http content size semantic conventions. (#905) - Include `http.request_content_length` in HTTP request basic attributes. (#905) - Add semantic conventions for operating system process resource attribute keys. (#919) - The Jaeger exporter now has a `WithBatchMaxCount` option to specify the maximum number of spans sent in a batch. (#931) ### Changed - Update `CONTRIBUTING.md` to ask for updates to `CHANGELOG.md` with each pull request. (#879) - Use lowercase header names for B3 Multiple Headers. (#881) - The B3 propagator `SingleHeader` field has been replaced with `InjectEncoding`. This new field can be set to combinations of the `B3Encoding` bitmasks and will inject trace information in these encodings. If no encoding is set, the propagator will default to `MultipleHeader` encoding. (#882) - The B3 propagator now extracts from either HTTP encoding of B3 (Single Header or Multiple Header) based on what is contained in the header. Preference is given to Single Header encoding with Multiple Header being the fallback if Single Header is not found or is invalid. This behavior change is made to dynamically support all correctly encoded traces received instead of having to guess the expected encoding prior to receiving. (#882) - Extend semantic conventions for RPC. (#900) - To match constant naming conventions in the `api/standard` package, the `FaaS*` key names are appended with a suffix of `Key`. (#920) - `"api/standard".FaaSName` -> `FaaSNameKey` - `"api/standard".FaaSID` -> `FaaSIDKey` - `"api/standard".FaaSVersion` -> `FaaSVersionKey` - `"api/standard".FaaSInstance` -> `FaaSInstanceKey` ### Removed - The `FlagsUnused` trace flag is removed. The purpose of this flag was to act as the inverse of `FlagsSampled`, the inverse of `FlagsSampled` is used instead. (#882) - The B3 header constants (`B3SingleHeader`, `B3DebugFlagHeader`, `B3TraceIDHeader`, `B3SpanIDHeader`, `B3SampledHeader`, `B3ParentSpanIDHeader`) are removed. If B3 header keys are needed [the authoritative OpenZipkin package constants](https://pkg.go.dev/github.com/openzipkin/zipkin-go@v0.2.2/propagation/b3?tab=doc#pkg-constants) should be used instead. (#882) ### Fixed - The B3 Single Header name is now correctly `b3` instead of the previous `X-B3`. (#881) - The B3 propagator now correctly supports sampling only values (`b3: 0`, `b3: 1`, or `b3: d`) for a Single B3 Header. (#882) - The B3 propagator now propagates the debug flag. This removes the behavior of changing the debug flag into a set sampling bit. Instead, this now follow the B3 specification and omits the `X-B3-Sampling` header. (#882) - The B3 propagator now tracks "unset" sampling state (meaning "defer the decision") and does not set the `X-B3-Sampling` header when injecting. (#882) - Bump github.com/itchyny/gojq from 0.10.3 to 0.10.4 in /tools. (#883) - Bump github.com/opentracing/opentracing-go from v1.1.1-0.20190913142402-a7454ce5950e to v1.2.0. (#885) - The tracing time conversion for OTLP spans is now correctly set to `UnixNano`. (#896) - Ensure span status is not set to `Unknown` when no HTTP status code is provided as it is assumed to be `200 OK`. (#908) - Ensure `httptrace.clientTracer` closes `http.headers` span. (#912) - Prometheus exporter will not apply stale updates or forget inactive metrics. (#903) - Add test for api.standard `HTTPClientAttributesFromHTTPRequest`. (#905) - Bump github.com/golangci/golangci-lint from 1.27.0 to 1.28.1 in /tools. (#901, #913) - Update otel-colector example to use the v0.5.0 collector. (#915) - The `grpctrace` instrumentation uses a span name conforming to the OpenTelemetry semantic conventions (does not contain a leading slash (`/`)). (#922) - The `grpctrace` instrumentation includes an `rpc.method` attribute now set to the gRPC method name. (#900, #922) - The `grpctrace` instrumentation `rpc.service` attribute now contains the package name if one exists. This is in accordance with OpenTelemetry semantic conventions. (#922) - Correlation Context extractor will no longer insert an empty map into the returned context when no valid values are extracted. (#923) - Bump google.golang.org/api from 0.28.0 to 0.29.0 in /exporters/trace/jaeger. (#925) - Bump github.com/itchyny/gojq from 0.10.4 to 0.11.0 in /tools. (#926) - Bump github.com/golangci/golangci-lint from 1.28.1 to 1.28.2 in /tools. (#930) ## [0.7.0] - 2020-06-26 This release implements the v0.5.0 version of the OpenTelemetry specification. ### Added - The othttp instrumentation now includes default metrics. (#861) - This CHANGELOG file to track all changes in the project going forward. - Support for array type attributes. (#798) - Apply transitive dependabot go.mod dependency updates as part of a new automatic Github workflow. (#844) - Timestamps are now passed to exporters for each export. (#835) - Add new `Accumulation` type to metric SDK to transport telemetry from `Accumulator`s to `Processor`s. This replaces the prior `Record` `struct` use for this purpose. (#835) - New dependabot integration to automate package upgrades. (#814) - `Meter` and `Tracer` implementations accept instrumentation version version as an optional argument. This instrumentation version is passed on to exporters. (#811) (#805) (#802) - The OTLP exporter includes the instrumentation version in telemetry it exports. (#811) - Environment variables for Jaeger exporter are supported. (#796) - New `aggregation.Kind` in the export metric API. (#808) - New example that uses OTLP and the collector. (#790) - Handle errors in the span `SetName` during span initialization. (#791) - Default service config to enable retries for retry-able failed requests in the OTLP exporter and an option to override this default. (#777) - New `go.opentelemetry.io/otel/api/oterror` package to uniformly support error handling and definitions for the project. (#778) - New `global` default implementation of the `go.opentelemetry.io/otel/api/oterror.Handler` interface to be used to handle errors prior to an user defined `Handler`. There is also functionality for the user to register their `Handler` as well as a convenience function `Handle` to handle an error with this global `Handler`(#778) - Options to specify propagators for httptrace and grpctrace instrumentation. (#784) - The required `application/json` header for the Zipkin exporter is included in all exports. (#774) - Integrate HTTP semantics helpers from the contrib repository into the `api/standard` package. #769 ### Changed - Rename `Integrator` to `Processor` in the metric SDK. (#863) - Rename `AggregationSelector` to `AggregatorSelector`. (#859) - Rename `SynchronizedCopy` to `SynchronizedMove`. (#858) - Rename `simple` integrator to `basic` integrator. (#857) - Merge otlp collector examples. (#841) - Change the metric SDK to support cumulative, delta, and pass-through exporters directly. With these changes, cumulative and delta specific exporters are able to request the correct kind of aggregation from the SDK. (#840) - The `Aggregator.Checkpoint` API is renamed to `SynchronizedCopy` and adds an argument, a different `Aggregator` into which the copy is stored. (#812) - The `export.Aggregator` contract is that `Update()` and `SynchronizedCopy()` are synchronized with each other. All the aggregation interfaces (`Sum`, `LastValue`, ...) are not meant to be synchronized, as the caller is expected to synchronize aggregators at a higher level after the `Accumulator`. Some of the `Aggregators` used unnecessary locking and that has been cleaned up. (#812) - Use of `metric.Number` was replaced by `int64` now that we use `sync.Mutex` in the `MinMaxSumCount` and `Histogram` `Aggregators`. (#812) - Replace `AlwaysParentSample` with `ParentSample(fallback)` to match the OpenTelemetry v0.5.0 specification. (#810) - Rename `sdk/export/metric/aggregator` to `sdk/export/metric/aggregation`. #808 - Send configured headers with every request in the OTLP exporter, instead of just on connection creation. (#806) - Update error handling for any one off error handlers, replacing, instead, with the `global.Handle` function. (#791) - Rename `plugin` directory to `instrumentation` to match the OpenTelemetry specification. (#779) - Makes the argument order to Histogram and DDSketch `New()` consistent. (#781) ### Removed - `Uint64NumberKind` and related functions from the API. (#864) - Context arguments from `Aggregator.Checkpoint` and `Integrator.Process` as they were unused. (#803) - `SpanID` is no longer included in parameters for sampling decision to match the OpenTelemetry specification. (#775) ### Fixed - Upgrade OTLP exporter to opentelemetry-proto matching the opentelemetry-collector v0.4.0 release. (#866) - Allow changes to `go.sum` and `go.mod` when running dependabot tidy-up. (#871) - Bump github.com/stretchr/testify from 1.4.0 to 1.6.1. (#824) - Bump github.com/prometheus/client_golang from 1.7.0 to 1.7.1 in /exporters/metric/prometheus. (#867) - Bump google.golang.org/grpc from 1.29.1 to 1.30.0 in /exporters/trace/jaeger. (#853) - Bump google.golang.org/grpc from 1.29.1 to 1.30.0 in /exporters/trace/zipkin. (#854) - Bumps github.com/golang/protobuf from 1.3.2 to 1.4.2 (#848) - Bump github.com/stretchr/testify from 1.4.0 to 1.6.1 in /exporters/otlp (#817) - Bump github.com/golangci/golangci-lint from 1.25.1 to 1.27.0 in /tools (#828) - Bump github.com/prometheus/client_golang from 1.5.0 to 1.7.0 in /exporters/metric/prometheus (#838) - Bump github.com/stretchr/testify from 1.4.0 to 1.6.1 in /exporters/trace/jaeger (#829) - Bump github.com/benbjohnson/clock from 1.0.0 to 1.0.3 (#815) - Bump github.com/stretchr/testify from 1.4.0 to 1.6.1 in /exporters/trace/zipkin (#823) - Bump github.com/itchyny/gojq from 0.10.1 to 0.10.3 in /tools (#830) - Bump github.com/stretchr/testify from 1.4.0 to 1.6.1 in /exporters/metric/prometheus (#822) - Bump google.golang.org/grpc from 1.27.1 to 1.29.1 in /exporters/trace/zipkin (#820) - Bump google.golang.org/grpc from 1.27.1 to 1.29.1 in /exporters/trace/jaeger (#831) - Bump github.com/google/go-cmp from 0.4.0 to 0.5.0 (#836) - Bump github.com/google/go-cmp from 0.4.0 to 0.5.0 in /exporters/trace/jaeger (#837) - Bump github.com/google/go-cmp from 0.4.0 to 0.5.0 in /exporters/otlp (#839) - Bump google.golang.org/api from 0.20.0 to 0.28.0 in /exporters/trace/jaeger (#843) - Set span status from HTTP status code in the othttp instrumentation. (#832) - Fixed typo in push controller comment. (#834) - The `Aggregator` testing has been updated and cleaned. (#812) - `metric.Number(0)` expressions are replaced by `0` where possible. (#812) - Fixed `global` `handler_test.go` test failure. #804 - Fixed `BatchSpanProcessor.Shutdown` to wait until all spans are processed. (#766) - Fixed OTLP example's accidental early close of exporter. (#807) - Ensure zipkin exporter reads and closes response body. (#788) - Update instrumentation to use `api/standard` keys instead of custom keys. (#782) - Clean up tools and RELEASING documentation. (#762) ## [0.6.0] - 2020-05-21 ### Added - Support for `Resource`s in the prometheus exporter. (#757) - New pull controller. (#751) - New `UpDownSumObserver` instrument. (#750) - OpenTelemetry collector demo. (#711) - New `SumObserver` instrument. (#747) - New `UpDownCounter` instrument. (#745) - New timeout `Option` and configuration function `WithTimeout` to the push controller. (#742) - New `api/standards` package to implement semantic conventions and standard key-value generation. (#731) ### Changed - Rename `Register*` functions in the metric API to `New*` for all `Observer` instruments. (#761) - Use `[]float64` for histogram boundaries, not `[]metric.Number`. (#758) - Change OTLP example to use exporter as a trace `Syncer` instead of as an unneeded `Batcher`. (#756) - Replace `WithResourceAttributes()` with `WithResource()` in the trace SDK. (#754) - The prometheus exporter now uses the new pull controller. (#751) - Rename `ScheduleDelayMillis` to `BatchTimeout` in the trace `BatchSpanProcessor`.(#752) - Support use of synchronous instruments in asynchronous callbacks (#725) - Move `Resource` from the `Export` method parameter into the metric export `Record`. (#739) - Rename `Observer` instrument to `ValueObserver`. (#734) - The push controller now has a method (`Provider()`) to return a `metric.Provider` instead of the old `Meter` method that acted as a `metric.Provider`. (#738) - Replace `Measure` instrument by `ValueRecorder` instrument. (#732) - Rename correlation context header from `"Correlation-Context"` to `"otcorrelations"` to match the OpenTelemetry specification. (#727) ### Fixed - Ensure gRPC `ClientStream` override methods do not panic in grpctrace package. (#755) - Disable parts of `BatchSpanProcessor` test until a fix is found. (#743) - Fix `string` case in `kv` `Infer` function. (#746) - Fix panic in grpctrace client interceptors. (#740) - Refactor the `api/metrics` push controller and add `CheckpointSet` synchronization. (#737) - Rewrite span batch process queue batching logic. (#719) - Remove the push controller named Meter map. (#738) - Fix Histogram aggregator initial state (fix #735). (#736) - Ensure golang alpine image is running `golang-1.14` for examples. (#733) - Added test for grpctrace `UnaryInterceptorClient`. (#695) - Rearrange `api/metric` code layout. (#724) ## [0.5.0] - 2020-05-13 ### Added - Batch `Observer` callback support. (#717) - Alias `api` types to root package of project. (#696) - Create basic `othttp.Transport` for simple client instrumentation. (#678) - `SetAttribute(string, interface{})` to the trace API. (#674) - Jaeger exporter option that allows user to specify custom http client. (#671) - `Stringer` and `Infer` methods to `key`s. (#662) ### Changed - Rename `NewKey` in the `kv` package to just `Key`. (#721) - Move `core` and `key` to `kv` package. (#720) - Make the metric API `Meter` a `struct` so the abstract `MeterImpl` can be passed and simplify implementation. (#709) - Rename SDK `Batcher` to `Integrator` to match draft OpenTelemetry SDK specification. (#710) - Rename SDK `Ungrouped` integrator to `simple.Integrator` to match draft OpenTelemetry SDK specification. (#710) - Rename SDK `SDK` `struct` to `Accumulator` to match draft OpenTelemetry SDK specification. (#710) - Move `Number` from `core` to `api/metric` package. (#706) - Move `SpanContext` from `core` to `trace` package. (#692) - Change traceparent header from `Traceparent` to `traceparent` to implement the W3C specification. (#681) ### Fixed - Update tooling to run generators in all submodules. (#705) - gRPC interceptor regexp to match methods without a service name. (#683) - Use a `const` for padding 64-bit B3 trace IDs. (#701) - Update `mockZipkin` listen address from `:0` to `127.0.0.1:0`. (#700) - Left-pad 64-bit B3 trace IDs with zero. (#698) - Propagate at least the first W3C tracestate header. (#694) - Remove internal `StateLocker` implementation. (#688) - Increase instance size CI system uses. (#690) - Add a `key` benchmark and use reflection in `key.Infer()`. (#679) - Fix internal `global` test by using `global.Meter` with `RecordBatch()`. (#680) - Reimplement histogram using mutex instead of `StateLocker`. (#669) - Switch `MinMaxSumCount` to a mutex lock implementation instead of `StateLocker`. (#667) - Update documentation to not include any references to `WithKeys`. (#672) - Correct misspelling. (#668) - Fix clobbering of the span context if extraction fails. (#656) - Bump `golangci-lint` and work around the corrupting bug. (#666) (#670) ## [0.4.3] - 2020-04-24 ### Added - `Dockerfile` and `docker-compose.yml` to run example code. (#635) - New `grpctrace` package that provides gRPC client and server interceptors for both unary and stream connections. (#621) - New `api/label` package, providing common label set implementation. (#651) - Support for JSON marshaling of `Resources`. (#654) - `TraceID` and `SpanID` implementations for `Stringer` interface. (#642) - `RemoteAddrKey` in the othttp plugin to include the HTTP client address in top-level spans. (#627) - `WithSpanFormatter` option to the othttp plugin. (#617) - Updated README to include section for compatible libraries and include reference to the contrib repository. (#612) - The prometheus exporter now supports exporting histograms. (#601) - A `String` method to the `Resource` to return a hashable identifier for a now unique resource. (#613) - An `Iter` method to the `Resource` to return an array `AttributeIterator`. (#613) - An `Equal` method to the `Resource` test the equivalence of resources. (#613) - An iterable structure (`AttributeIterator`) for `Resource` attributes. ### Changed - zipkin export's `NewExporter` now requires a `serviceName` argument to ensure this needed values is provided. (#644) - Pass `Resources` through the metrics export pipeline. (#659) ### Removed - `WithKeys` option from the metric API. (#639) ### Fixed - Use the `label.Set.Equivalent` value instead of an encoding in the batcher. (#658) - Correct typo `trace.Exporter` to `trace.SpanSyncer` in comments. (#653) - Use type names for return values in jaeger exporter. (#648) - Increase the visibility of the `api/key` package by updating comments and fixing usages locally. (#650) - `Checkpoint` only after `Update`; Keep records in the `sync.Map` longer. (#647) - Do not cache `reflect.ValueOf()` in metric Labels. (#649) - Batch metrics exported from the OTLP exporter based on `Resource` and labels. (#626) - Add error wrapping to the prometheus exporter. (#631) - Update the OTLP exporter batching of traces to use a unique `string` representation of an associated `Resource` as the batching key. (#623) - Update OTLP `SpanData` transform to only include the `ParentSpanID` if one exists. (#614) - Update `Resource` internal representation to uniquely and reliably identify resources. (#613) - Check return value from `CheckpointSet.ForEach` in prometheus exporter. (#622) - Ensure spans created by httptrace client tracer reflect operation structure. (#618) - Create a new recorder rather than reuse when multiple observations in same epoch for asynchronous instruments. #610 - The default port the OTLP exporter uses to connect to the OpenTelemetry collector is updated to match the one the collector listens on by default. (#611) ## [0.4.2] - 2020-03-31 ### Fixed - Fix `pre_release.sh` to update version in `sdk/opentelemetry.go`. (#607) - Fix time conversion from internal to OTLP in OTLP exporter. (#606) ## [0.4.1] - 2020-03-31 ### Fixed - Update `tag.sh` to create signed tags. (#604) ## [0.4.0] - 2020-03-30 ### Added - New API package `api/metric/registry` that exposes a `MeterImpl` wrapper for use by SDKs to generate unique instruments. (#580) - Script to verify examples after a new release. (#579) ### Removed - The dogstatsd exporter due to lack of support. This additionally removes support for statsd. (#591) - `LabelSet` from the metric API. This is replaced by a `[]core.KeyValue` slice. (#595) - `Labels` from the metric API's `Meter` interface. (#595) ### Changed - The metric `export.Labels` became an interface which the SDK implements and the `export` package provides a simple, immutable implementation of this interface intended for testing purposes. (#574) - Renamed `internal/metric.Meter` to `MeterImpl`. (#580) - Renamed `api/global/internal.obsImpl` to `asyncImpl`. (#580) ### Fixed - Corrected missing return in mock span. (#582) - Update License header for all source files to match CNCF guidelines and include a test to ensure it is present. (#586) (#596) - Update to v0.3.0 of the OTLP in the OTLP exporter. (#588) - Update pre-release script to be compatible between GNU and BSD based systems. (#592) - Add a `RecordBatch` benchmark. (#594) - Moved span transforms of the OTLP exporter to the internal package. (#593) - Build both go-1.13 and go-1.14 in circleci to test for all supported versions of Go. (#569) - Removed unneeded allocation on empty labels in OLTP exporter. (#597) - Update `BatchedSpanProcessor` to process the queue until no data but respect max batch size. (#599) - Update project documentation godoc.org links to pkg.go.dev. (#602) ## [0.3.0] - 2020-03-21 This is a first official beta release, which provides almost fully complete metrics, tracing, and context propagation functionality. There is still a possibility of breaking changes. ### Added - Add `Observer` metric instrument. (#474) - Add global `Propagators` functionality to enable deferred initialization for propagators registered before the first Meter SDK is installed. (#494) - Simplified export setup pipeline for the jaeger exporter to match other exporters. (#459) - The zipkin trace exporter. (#495) - The OTLP exporter to export metric and trace telemetry to the OpenTelemetry collector. (#497) (#544) (#545) - Add `StatusMessage` field to the trace `Span`. (#524) - Context propagation in OpenTracing bridge in terms of OpenTelemetry context propagation. (#525) - The `Resource` type was added to the SDK. (#528) - The global API now supports a `Tracer` and `Meter` function as shortcuts to getting a global `*Provider` and calling these methods directly. (#538) - The metric API now defines a generic `MeterImpl` interface to support general purpose `Meter` construction. Additionally, `SyncImpl` and `AsyncImpl` are added to support general purpose instrument construction. (#560) - A metric `Kind` is added to represent the `MeasureKind`, `ObserverKind`, and `CounterKind`. (#560) - Scripts to better automate the release process. (#576) ### Changed - Default to to use `AlwaysSampler` instead of `ProbabilitySampler` to match OpenTelemetry specification. (#506) - Renamed `AlwaysSampleSampler` to `AlwaysOnSampler` in the trace API. (#511) - Renamed `NeverSampleSampler` to `AlwaysOffSampler` in the trace API. (#511) - The `Status` field of the `Span` was changed to `StatusCode` to disambiguate with the added `StatusMessage`. (#524) - Updated the trace `Sampler` interface conform to the OpenTelemetry specification. (#531) - Rename metric API `Options` to `Config`. (#541) - Rename metric `Counter` aggregator to be `Sum`. (#541) - Unify metric options into `Option` from instrument specific options. (#541) - The trace API's `TraceProvider` now support `Resource`s. (#545) - Correct error in zipkin module name. (#548) - The jaeger trace exporter now supports `Resource`s. (#551) - Metric SDK now supports `Resource`s. The `WithResource` option was added to configure a `Resource` on creation and the `Resource` method was added to the metric `Descriptor` to return the associated `Resource`. (#552) - Replace `ErrNoLastValue` and `ErrEmptyDataSet` by `ErrNoData` in the metric SDK. (#557) - The stdout trace exporter now supports `Resource`s. (#558) - The metric `Descriptor` is now included at the API instead of the SDK. (#560) - Replace `Ordered` with an iterator in `export.Labels`. (#567) ### Removed - The vendor specific Stackdriver. It is now hosted on 3rd party vendor infrastructure. (#452) - The `Unregister` method for metric observers as it is not in the OpenTelemetry specification. (#560) - `GetDescriptor` from the metric SDK. (#575) - The `Gauge` instrument from the metric API. (#537) ### Fixed - Make histogram aggregator checkpoint consistent. (#438) - Update README with import instructions and how to build and test. (#505) - The default label encoding was updated to be unique. (#508) - Use `NewRoot` in the othttp plugin for public endpoints. (#513) - Fix data race in `BatchedSpanProcessor`. (#518) - Skip test-386 for Mac OS 10.15.x (Catalina and upwards). #521 - Use a variable-size array to represent ordered labels in maps. (#523) - Update the OTLP protobuf and update changed import path. (#532) - Use `StateLocker` implementation in `MinMaxSumCount`. (#546) - Eliminate goroutine leak in histogram stress test. (#547) - Update OTLP exporter with latest protobuf. (#550) - Add filters to the othttp plugin. (#556) - Provide an implementation of the `Header*` filters that do not depend on Go 1.14. (#565) - Encode labels once during checkpoint. The checkpoint function is executed in a single thread so we can do the encoding lazily before passing the encoded version of labels to the exporter. This is a cheap and quick way to avoid encoding the labels on every collection interval. (#572) - Run coverage over all packages in `COVERAGE_MOD_DIR`. (#573) ## [0.2.3] - 2020-03-04 ### Added - `RecordError` method on `Span`s in the trace API to Simplify adding error events to spans. (#473) - Configurable push frequency for exporters setup pipeline. (#504) ### Changed - Rename the `exporter` directory to `exporters`. The `go.opentelemetry.io/otel/exporter/trace/jaeger` package was mistakenly released with a `v1.0.0` tag instead of `v0.1.0`. This resulted in all subsequent releases not becoming the default latest. A consequence of this was that all `go get`s pulled in the incompatible `v0.1.0` release of that package when pulling in more recent packages from other otel packages. Renaming the `exporter` directory to `exporters` fixes this issue by renaming the package and therefore clearing any existing dependency tags. Consequentially, this action also renames *all* exporter packages. (#502) ### Removed - The `CorrelationContextHeader` constant in the `correlation` package is no longer exported. (#503) ## [0.2.2] - 2020-02-27 ### Added - `HTTPSupplier` interface in the propagation API to specify methods to retrieve and store a single value for a key to be associated with a carrier. (#467) - `HTTPExtractor` interface in the propagation API to extract information from an `HTTPSupplier` into a context. (#467) - `HTTPInjector` interface in the propagation API to inject information into an `HTTPSupplier.` (#467) - `Config` and configuring `Option` to the propagator API. (#467) - `Propagators` interface in the propagation API to contain the set of injectors and extractors for all supported carrier formats. (#467) - `HTTPPropagator` interface in the propagation API to inject and extract from an `HTTPSupplier.` (#467) - `WithInjectors` and `WithExtractors` functions to the propagator API to configure injectors and extractors to use. (#467) - `ExtractHTTP` and `InjectHTTP` functions to apply configured HTTP extractors and injectors to a passed context. (#467) - Histogram aggregator. (#433) - `DefaultPropagator` function and have it return `trace.TraceContext` as the default context propagator. (#456) - `AlwaysParentSample` sampler to the trace API. (#455) - `WithNewRoot` option function to the trace API to specify the created span should be considered a root span. (#451) ### Changed - Renamed `WithMap` to `ContextWithMap` in the correlation package. (#481) - Renamed `FromContext` to `MapFromContext` in the correlation package. (#481) - Move correlation context propagation to correlation package. (#479) - Do not default to putting remote span context into links. (#480) - `Tracer.WithSpan` updated to accept `StartOptions`. (#472) - Renamed `MetricKind` to `Kind` to not stutter in the type usage. (#432) - Renamed the `export` package to `metric` to match directory structure. (#432) - Rename the `api/distributedcontext` package to `api/correlation`. (#444) - Rename the `api/propagators` package to `api/propagation`. (#444) - Move the propagators from the `propagators` package into the `trace` API package. (#444) - Update `Float64Gauge`, `Int64Gauge`, `Float64Counter`, `Int64Counter`, `Float64Measure`, and `Int64Measure` metric methods to use value receivers instead of pointers. (#462) - Moved all dependencies of tools package to a tools directory. (#466) ### Removed - Binary propagators. (#467) - NOOP propagator. (#467) ### Fixed - Upgraded `github.com/golangci/golangci-lint` from `v1.21.0` to `v1.23.6` in `tools/`. (#492) - Fix a possible nil-dereference crash (#478) - Correct comments for `InstallNewPipeline` in the stdout exporter. (#483) - Correct comments for `InstallNewPipeline` in the dogstatsd exporter. (#484) - Correct comments for `InstallNewPipeline` in the prometheus exporter. (#482) - Initialize `onError` based on `Config` in prometheus exporter. (#486) - Correct module name in prometheus exporter README. (#475) - Removed tracer name prefix from span names. (#430) - Fix `aggregator_test.go` import package comment. (#431) - Improved detail in stdout exporter. (#436) - Fix a dependency issue (generate target should depend on stringer, not lint target) in Makefile. (#442) - Reorders the Makefile targets within `precommit` target so we generate files and build the code before doing linting, so we can get much nicer errors about syntax errors from the compiler. (#442) - Reword function documentation in gRPC plugin. (#446) - Send the `span.kind` tag to Jaeger from the jaeger exporter. (#441) - Fix `metadataSupplier` in the jaeger exporter to overwrite the header if existing instead of appending to it. (#441) - Upgraded to Go 1.13 in CI. (#465) - Correct opentelemetry.io URL in trace SDK documentation. (#464) - Refactored reference counting logic in SDK determination of stale records. (#468) - Add call to `runtime.Gosched` in instrument `acquireHandle` logic to not block the collector. (#469) ## [0.2.1.1] - 2020-01-13 ### Fixed - Use stateful batcher on Prometheus exporter fixing regression introduced in #395. (#428) ## [0.2.1] - 2020-01-08 ### Added - Global meter forwarding implementation. This enables deferred initialization for metric instruments registered before the first Meter SDK is installed. (#392) - Global trace forwarding implementation. This enables deferred initialization for tracers registered before the first Trace SDK is installed. (#406) - Standardize export pipeline creation in all exporters. (#395) - A testing, organization, and comments for 64-bit field alignment. (#418) - Script to tag all modules in the project. (#414) ### Changed - Renamed `propagation` package to `propagators`. (#362) - Renamed `B3Propagator` propagator to `B3`. (#362) - Renamed `TextFormatPropagator` propagator to `TextFormat`. (#362) - Renamed `BinaryPropagator` propagator to `Binary`. (#362) - Renamed `BinaryFormatPropagator` propagator to `BinaryFormat`. (#362) - Renamed `NoopTextFormatPropagator` propagator to `NoopTextFormat`. (#362) - Renamed `TraceContextPropagator` propagator to `TraceContext`. (#362) - Renamed `SpanOption` to `StartOption` in the trace API. (#369) - Renamed `StartOptions` to `StartConfig` in the trace API. (#369) - Renamed `EndOptions` to `EndConfig` in the trace API. (#369) - `Number` now has a pointer receiver for its methods. (#375) - Renamed `CurrentSpan` to `SpanFromContext` in the trace API. (#379) - Renamed `SetCurrentSpan` to `ContextWithSpan` in the trace API. (#379) - Renamed `Message` in Event to `Name` in the trace API. (#389) - Prometheus exporter no longer aggregates metrics, instead it only exports them. (#385) - Renamed `HandleImpl` to `BoundInstrumentImpl` in the metric API. (#400) - Renamed `Float64CounterHandle` to `Float64CounterBoundInstrument` in the metric API. (#400) - Renamed `Int64CounterHandle` to `Int64CounterBoundInstrument` in the metric API. (#400) - Renamed `Float64GaugeHandle` to `Float64GaugeBoundInstrument` in the metric API. (#400) - Renamed `Int64GaugeHandle` to `Int64GaugeBoundInstrument` in the metric API. (#400) - Renamed `Float64MeasureHandle` to `Float64MeasureBoundInstrument` in the metric API. (#400) - Renamed `Int64MeasureHandle` to `Int64MeasureBoundInstrument` in the metric API. (#400) - Renamed `Release` method for bound instruments in the metric API to `Unbind`. (#400) - Renamed `AcquireHandle` method for bound instruments in the metric API to `Bind`. (#400) - Renamed the `File` option in the stdout exporter to `Writer`. (#404) - Renamed all `Options` to `Config` for all metric exports where this wasn't already the case. ### Fixed - Aggregator import path corrected. (#421) - Correct links in README. (#368) - The README was updated to match latest code changes in its examples. (#374) - Don't capitalize error statements. (#375) - Fix ignored errors. (#375) - Fix ambiguous variable naming. (#375) - Removed unnecessary type casting. (#375) - Use named parameters. (#375) - Updated release schedule. (#378) - Correct http-stackdriver example module name. (#394) - Removed the `http.request` span in `httptrace` package. (#397) - Add comments in the metrics SDK (#399) - Initialize checkpoint when creating ddsketch aggregator to prevent panic when merging into a empty one. (#402) (#403) - Add documentation of compatible exporters in the README. (#405) - Typo fix. (#408) - Simplify span check logic in SDK tracer implementation. (#419) ## [0.2.0] - 2019-12-03 ### Added - Unary gRPC tracing example. (#351) - Prometheus exporter. (#334) - Dogstatsd metrics exporter. (#326) ### Changed - Rename `MaxSumCount` aggregation to `MinMaxSumCount` and add the `Min` interface for this aggregation. (#352) - Rename `GetMeter` to `Meter`. (#357) - Rename `HTTPTraceContextPropagator` to `TraceContextPropagator`. (#355) - Rename `HTTPB3Propagator` to `B3Propagator`. (#355) - Rename `HTTPTraceContextPropagator` to `TraceContextPropagator`. (#355) - Move `/global` package to `/api/global`. (#356) - Rename `GetTracer` to `Tracer`. (#347) ### Removed - `SetAttribute` from the `Span` interface in the trace API. (#361) - `AddLink` from the `Span` interface in the trace API. (#349) - `Link` from the `Span` interface in the trace API. (#349) ### Fixed - Exclude example directories from coverage report. (#365) - Lint make target now implements automatic fixes with `golangci-lint` before a second run to report the remaining issues. (#360) - Drop `GO111MODULE` environment variable in Makefile as Go 1.13 is the project specified minimum version and this is environment variable is not needed for that version of Go. (#359) - Run the race checker for all test. (#354) - Redundant commands in the Makefile are removed. (#354) - Split the `generate` and `lint` targets of the Makefile. (#354) - Renames `circle-ci` target to more generic `ci` in Makefile. (#354) - Add example Prometheus binary to gitignore. (#358) - Support negative numbers with the `MaxSumCount`. (#335) - Resolve race conditions in `push_test.go` identified in #339. (#340) - Use `/usr/bin/env bash` as a shebang in scripts rather than `/bin/bash`. (#336) - Trace benchmark now tests both `AlwaysSample` and `NeverSample`. Previously it was testing `AlwaysSample` twice. (#325) - Trace benchmark now uses a `[]byte` for `TraceID` to fix failing test. (#325) - Added a trace benchmark to test variadic functions in `setAttribute` vs `setAttributes` (#325) - The `defaultkeys` batcher was only using the encoded label set as its map key while building a checkpoint. This allowed distinct label sets through, but any metrics sharing a label set could be overwritten or merged incorrectly. This was corrected. (#333) ## [0.1.2] - 2019-11-18 ### Fixed - Optimized the `simplelru` map for attributes to reduce the number of allocations. (#328) - Removed unnecessary unslicing of parameters that are already a slice. (#324) ## [0.1.1] - 2019-11-18 This release contains a Metrics SDK with stdout exporter and supports basic aggregations such as counter, gauges, array, maxsumcount, and ddsketch. ### Added - Metrics stdout export pipeline. (#265) - Array aggregation for raw measure metrics. (#282) - The core.Value now have a `MarshalJSON` method. (#281) ### Removed - `WithService`, `WithResources`, and `WithComponent` methods of tracers. (#314) - Prefix slash in `Tracer.Start()` for the Jaeger example. (#292) ### Changed - Allocation in LabelSet construction to reduce GC overhead. (#318) - `trace.WithAttributes` to append values instead of replacing (#315) - Use a formula for tolerance in sampling tests. (#298) - Move export types into trace and metric-specific sub-directories. (#289) - `SpanKind` back to being based on an `int` type. (#288) ### Fixed - URL to OpenTelemetry website in README. (#323) - Name of othttp default tracer. (#321) - `ExportSpans` for the stackdriver exporter now handles `nil` context. (#294) - CI modules cache to correctly restore/save from/to the cache. (#316) - Fix metric SDK race condition between `LoadOrStore` and the assignment `rec.recorder = i.meter.exporter.AggregatorFor(rec)`. (#293) - README now reflects the new code structure introduced with these changes. (#291) - Make the basic example work. (#279) ## [0.1.0] - 2019-11-04 This is the first release of open-telemetry go library. It contains api and sdk for trace and meter. ### Added - Initial OpenTelemetry trace and metric API prototypes. - Initial OpenTelemetry trace, metric, and export SDK packages. - A wireframe bridge to support compatibility with OpenTracing. - Example code for a basic, http-stackdriver, http, jaeger, and named tracer setup. - Exporters for Jaeger, Stackdriver, and stdout. - Propagators for binary, B3, and trace-context protocols. - Project information and guidelines in the form of a README and CONTRIBUTING. - Tools to build the project and a Makefile to automate the process. - Apache-2.0 license. - CircleCI build CI manifest files. - CODEOWNERS file to track owners of this project. [Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.19.0...HEAD [1.19.0/0.42.0/0.0.7]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.19.0 [1.19.0-rc.1/0.42.0-rc.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.19.0-rc.1 [1.18.0/0.41.0/0.0.6]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.18.0 [1.17.0/0.40.0/0.0.5]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.17.0 [1.16.0/0.39.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.16.0 [1.16.0-rc.1/0.39.0-rc.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.16.0-rc.1 [1.15.1/0.38.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.15.1 [1.15.0/0.38.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.15.0 [1.15.0-rc.2/0.38.0-rc.2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.15.0-rc.2 [1.15.0-rc.1/0.38.0-rc.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.15.0-rc.1 [1.14.0/0.37.0/0.0.4]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.14.0 [1.13.0/0.36.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.13.0 [1.12.0/0.35.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.12.0 [1.11.2/0.34.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.11.2 [1.11.1/0.33.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.11.1 [1.11.0/0.32.3]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.11.0 [0.32.2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/sdk/metric/v0.32.2 [0.32.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/sdk/metric/v0.32.1 [0.32.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/sdk/metric/v0.32.0 [1.10.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.10.0 [1.9.0/0.0.3]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.9.0 [1.8.0/0.31.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.8.0 [1.7.0/0.30.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.7.0 [0.29.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/metric/v0.29.0 [1.6.3]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.6.3 [1.6.2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.6.2 [1.6.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.6.1 [1.6.0/0.28.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.6.0 [1.5.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.5.0 [1.4.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.4.1 [1.4.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.4.0 [1.3.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.3.0 [1.2.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.2.0 [1.1.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.1.0 [1.0.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.0.1 [Metrics 0.24.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/metric/v0.24.0 [1.0.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.0.0 [1.0.0-RC3]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.0.0-RC3 [1.0.0-RC2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.0.0-RC2 [Experimental Metrics v0.22.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/metric/v0.22.0 [1.0.0-RC1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.0.0-RC1 [0.20.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.20.0 [0.19.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.19.0 [0.18.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.18.0 [0.17.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.17.0 [0.16.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.16.0 [0.15.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.15.0 [0.14.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.14.0 [0.13.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.13.0 [0.12.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.12.0 [0.11.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.11.0 [0.10.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.10.0 [0.9.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.9.0 [0.8.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.8.0 [0.7.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.7.0 [0.6.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.6.0 [0.5.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.5.0 [0.4.3]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.4.3 [0.4.2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.4.2 [0.4.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.4.1 [0.4.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.4.0 [0.3.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.3.0 [0.2.3]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.2.3 [0.2.2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.2.2 [0.2.1.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.2.1.1 [0.2.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.2.1 [0.2.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.2.0 [0.1.2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.1.2 [0.1.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.1.1 [0.1.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.1.0 [Go 1.20]: https://go.dev/doc/go1.20 [Go 1.19]: https://go.dev/doc/go1.19 [Go 1.18]: https://go.dev/doc/go1.18 [Go 1.19]: https://go.dev/doc/go1.19 [metric API]:https://pkg.go.dev/go.opentelemetry.io/otel/metric [metric SDK]:https://pkg.go.dev/go.opentelemetry.io/otel/sdk/metric ================================================ FILE: vendor/go.opentelemetry.io/otel/CODEOWNERS ================================================ ##################################################### # # List of approvers for this repository # ##################################################### # # Learn about membership in OpenTelemetry community: # https://github.com/open-telemetry/community/blob/main/community-membership.md # # # Learn about CODEOWNERS file format: # https://help.github.com/en/articles/about-code-owners # * @MrAlias @Aneurysm9 @evantorrie @XSAM @dashpole @MadVikingGod @pellared @hanyuancheung @dmathieu CODEOWNERS @MrAlias @MadVikingGod @pellared ================================================ FILE: vendor/go.opentelemetry.io/otel/CONTRIBUTING.md ================================================ # Contributing to opentelemetry-go The Go special interest group (SIG) meets regularly. See the OpenTelemetry [community](https://github.com/open-telemetry/community#golang-sdk) repo for information on this and other language SIGs. See the [public meeting notes](https://docs.google.com/document/d/1E5e7Ld0NuU1iVvf-42tOBpu2VBBLYnh73GJuITGJTTU/edit) for a summary description of past meetings. To request edit access, join the meeting or get in touch on [Slack](https://cloud-native.slack.com/archives/C01NPAXACKT). ## Development You can view and edit the source code by cloning this repository: ```sh git clone https://github.com/open-telemetry/opentelemetry-go.git ``` Run `make test` to run the tests instead of `go test`. There are some generated files checked into the repo. To make sure that the generated files are up-to-date, run `make` (or `make precommit` - the `precommit` target is the default). The `precommit` target also fixes the formatting of the code and checks the status of the go module files. Additionally, there is a `codespell` target that checks for common typos in the code. It is not run by default, but you can run it manually with `make codespell`. It will set up a virtual environment in `venv` and install `codespell` there. If after running `make precommit` the output of `git status` contains `nothing to commit, working tree clean` then it means that everything is up-to-date and properly formatted. ## Pull Requests ### How to Send Pull Requests Everyone is welcome to contribute code to `opentelemetry-go` via GitHub pull requests (PRs). To create a new PR, fork the project in GitHub and clone the upstream repo: ```sh go get -d go.opentelemetry.io/otel ``` (This may print some warning about "build constraints exclude all Go files", just ignore it.) This will put the project in `${GOPATH}/src/go.opentelemetry.io/otel`. You can alternatively use `git` directly with: ```sh git clone https://github.com/open-telemetry/opentelemetry-go ``` (Note that `git clone` is *not* using the `go.opentelemetry.io/otel` name - that name is a kind of a redirector to GitHub that `go get` can understand, but `git` does not.) This would put the project in the `opentelemetry-go` directory in current working directory. Enter the newly created directory and add your fork as a new remote: ```sh git remote add git@github.com:/opentelemetry-go ``` Check out a new branch, make modifications, run linters and tests, update `CHANGELOG.md`, and push the branch to your fork: ```sh git checkout -b # edit files # update changelog make precommit git add -p git commit git push ``` Open a pull request against the main `opentelemetry-go` repo. Be sure to add the pull request ID to the entry you added to `CHANGELOG.md`. ### How to Receive Comments * If the PR is not ready for review, please put `[WIP]` in the title, tag it as `work-in-progress`, or mark it as [`draft`](https://github.blog/2019-02-14-introducing-draft-pull-requests/). * Make sure CLA is signed and CI is clear. ### How to Get PRs Merged A PR is considered **ready to merge** when: * It has received two qualified approvals[^1]. This is not enforced through automation, but needs to be validated by the maintainer merging. * The qualified approvals need to be from [Approver]s/[Maintainer]s affiliated with different companies. Two qualified approvals from [Approver]s or [Maintainer]s affiliated with the same company counts as a single qualified approval. * PRs introducing changes that have already been discussed and consensus reached only need one qualified approval. The discussion and resolution needs to be linked to the PR. * Trivial changes[^2] only need one qualified approval. * All feedback has been addressed. * All PR comments and suggestions are resolved. * All GitHub Pull Request reviews with a status of "Request changes" have been addressed. Another review by the objecting reviewer with a different status can be submitted to clear the original review, or the review can be dismissed by a [Maintainer] when the issues from the original review have been addressed. * Any comments or reviews that cannot be resolved between the PR author and reviewers can be submitted to the community [Approver]s and [Maintainer]s during the weekly SIG meeting. If consensus is reached among the [Approver]s and [Maintainer]s during the SIG meeting the objections to the PR may be dismissed or resolved or the PR closed by a [Maintainer]. * Any substantive changes to the PR require existing Approval reviews be cleared unless the approver explicitly states that their approval persists across changes. This includes changes resulting from other feedback. [Approver]s and [Maintainer]s can help in clearing reviews and they should be consulted if there are any questions. * The PR branch is up to date with the base branch it is merging into. * To ensure this does not block the PR, it should be configured to allow maintainers to update it. * It has been open for review for at least one working day. This gives people reasonable time to review. * Trivial changes[^2] do not have to wait for one day and may be merged with a single [Maintainer]'s approval. * All required GitHub workflows have succeeded. * Urgent fix can take exception as long as it has been actively communicated among [Maintainer]s. Any [Maintainer] can merge the PR once the above criteria have been met. [^1]: A qualified approval is a GitHub Pull Request review with "Approve" status from an OpenTelemetry Go [Approver] or [Maintainer]. [^2]: Trivial changes include: typo corrections, cosmetic non-substantive changes, documentation corrections or updates, dependency updates, etc. ## Design Choices As with other OpenTelemetry clients, opentelemetry-go follows the [OpenTelemetry Specification](https://opentelemetry.io/docs/specs/otel). It's especially valuable to read through the [library guidelines](https://opentelemetry.io/docs/specs/otel/library-guidelines). ### Focus on Capabilities, Not Structure Compliance OpenTelemetry is an evolving specification, one where the desires and use cases are clear, but the method to satisfy those uses cases are not. As such, Contributions should provide functionality and behavior that conforms to the specification, but the interface and structure is flexible. It is preferable to have contributions follow the idioms of the language rather than conform to specific API names or argument patterns in the spec. For a deeper discussion, see [this](https://github.com/open-telemetry/opentelemetry-specification/issues/165). ## Documentation Each (non-internal, non-test) package must be documented using [Go Doc Comments](https://go.dev/doc/comment), preferably in a `doc.go` file. Prefer using [Examples](https://pkg.go.dev/testing#hdr-Examples) instead of putting code snippets in Go doc comments. In some cases, you can even create [Testable Examples](https://go.dev/blog/examples). You can install and run a "local Go Doc site" in the following way: ```sh go install golang.org/x/pkgsite/cmd/pkgsite@latest pkgsite ``` [`go.opentelemetry.io/otel/metric`](https://pkg.go.dev/go.opentelemetry.io/otel/metric) is an example of a very well-documented package. ## Style Guide One of the primary goals of this project is that it is actually used by developers. With this goal in mind the project strives to build user-friendly and idiomatic Go code adhering to the Go community's best practices. For a non-comprehensive but foundational overview of these best practices the [Effective Go](https://golang.org/doc/effective_go.html) documentation is an excellent starting place. As a convenience for developers building this project the `make precommit` will format, lint, validate, and in some cases fix the changes you plan to submit. This check will need to pass for your changes to be able to be merged. In addition to idiomatic Go, the project has adopted certain standards for implementations of common patterns. These standards should be followed as a default, and if they are not followed documentation needs to be included as to the reasons why. ### Configuration When creating an instantiation function for a complex `type T struct`, it is useful to allow variable number of options to be applied. However, the strong type system of Go restricts the function design options. There are a few ways to solve this problem, but we have landed on the following design. #### `config` Configuration should be held in a `struct` named `config`, or prefixed with specific type name this Configuration applies to if there are multiple `config` in the package. This type must contain configuration options. ```go // config contains configuration options for a thing. type config struct { // options ... } ``` In general the `config` type will not need to be used externally to the package and should be unexported. If, however, it is expected that the user will likely want to build custom options for the configuration, the `config` should be exported. Please, include in the documentation for the `config` how the user can extend the configuration. It is important that internal `config` are not shared across package boundaries. Meaning a `config` from one package should not be directly used by another. The one exception is the API packages. The configs from the base API, eg. `go.opentelemetry.io/otel/trace.TracerConfig` and `go.opentelemetry.io/otel/metric.InstrumentConfig`, are intended to be consumed by the SDK therefore it is expected that these are exported. When a config is exported we want to maintain forward and backward compatibility, to achieve this no fields should be exported but should instead be accessed by methods. Optionally, it is common to include a `newConfig` function (with the same naming scheme). This function wraps any defaults setting and looping over all options to create a configured `config`. ```go // newConfig returns an appropriately configured config. func newConfig(options ...Option) config { // Set default values for config. config := config{/* […] */} for _, option := range options { config = option.apply(config) } // Perform any validation here. return config } ``` If validation of the `config` options is also performed this can return an error as well that is expected to be handled by the instantiation function or propagated to the user. Given the design goal of not having the user need to work with the `config`, the `newConfig` function should also be unexported. #### `Option` To set the value of the options a `config` contains, a corresponding `Option` interface type should be used. ```go type Option interface { apply(config) config } ``` Having `apply` unexported makes sure that it will not be used externally. Moreover, the interface becomes sealed so the user cannot easily implement the interface on its own. The `apply` method should return a modified version of the passed config. This approach, instead of passing a pointer, is used to prevent the config from being allocated to the heap. The name of the interface should be prefixed in the same way the corresponding `config` is (if at all). #### Options All user configurable options for a `config` must have a related unexported implementation of the `Option` interface and an exported configuration function that wraps this implementation. The wrapping function name should be prefixed with `With*` (or in the special case of a boolean options `Without*`) and should have the following function signature. ```go func With*(…) Option { … } ``` ##### `bool` Options ```go type defaultFalseOption bool func (o defaultFalseOption) apply(c config) config { c.Bool = bool(o) return c } // WithOption sets a T to have an option included. func WithOption() Option { return defaultFalseOption(true) } ``` ```go type defaultTrueOption bool func (o defaultTrueOption) apply(c config) config { c.Bool = bool(o) return c } // WithoutOption sets a T to have Bool option excluded. func WithoutOption() Option { return defaultTrueOption(false) } ``` ##### Declared Type Options ```go type myTypeOption struct { MyType MyType } func (o myTypeOption) apply(c config) config { c.MyType = o.MyType return c } // WithMyType sets T to have include MyType. func WithMyType(t MyType) Option { return myTypeOption{t} } ``` ##### Functional Options ```go type optionFunc func(config) config func (fn optionFunc) apply(c config) config { return fn(c) } // WithMyType sets t as MyType. func WithMyType(t MyType) Option { return optionFunc(func(c config) config { c.MyType = t return c }) } ``` #### Instantiation Using this configuration pattern to configure instantiation with a `NewT` function. ```go func NewT(options ...Option) T {…} ``` Any required parameters can be declared before the variadic `options`. #### Dealing with Overlap Sometimes there are multiple complex `struct` that share common configuration and also have distinct configuration. To avoid repeated portions of `config`s, a common `config` can be used with the union of options being handled with the `Option` interface. For example. ```go // config holds options for all animals. type config struct { Weight float64 Color string MaxAltitude float64 } // DogOption apply Dog specific options. type DogOption interface { applyDog(config) config } // BirdOption apply Bird specific options. type BirdOption interface { applyBird(config) config } // Option apply options for all animals. type Option interface { BirdOption DogOption } type weightOption float64 func (o weightOption) applyDog(c config) config { c.Weight = float64(o) return c } func (o weightOption) applyBird(c config) config { c.Weight = float64(o) return c } func WithWeight(w float64) Option { return weightOption(w) } type furColorOption string func (o furColorOption) applyDog(c config) config { c.Color = string(o) return c } func WithFurColor(c string) DogOption { return furColorOption(c) } type maxAltitudeOption float64 func (o maxAltitudeOption) applyBird(c config) config { c.MaxAltitude = float64(o) return c } func WithMaxAltitude(a float64) BirdOption { return maxAltitudeOption(a) } func NewDog(name string, o ...DogOption) Dog {…} func NewBird(name string, o ...BirdOption) Bird {…} ``` ### Interfaces To allow other developers to better comprehend the code, it is important to ensure it is sufficiently documented. One simple measure that contributes to this aim is self-documenting by naming method parameters. Therefore, where appropriate, methods of every exported interface type should have their parameters appropriately named. #### Interface Stability All exported stable interfaces that include the following warning in their documentation are allowed to be extended with additional methods. > Warning: methods may be added to this interface in minor releases. These interfaces are defined by the OpenTelemetry specification and will be updated as the specification evolves. Otherwise, stable interfaces MUST NOT be modified. #### How to Change Specification Interfaces When an API change must be made, we will update the SDK with the new method one release before the API change. This will allow the SDK one version before the API change to work seamlessly with the new API. If an incompatible version of the SDK is used with the new API the application will fail to compile. #### How Not to Change Specification Interfaces We have explored using a v2 of the API to change interfaces and found that there was no way to introduce a v2 and have it work seamlessly with the v1 of the API. Problems happened with libraries that upgraded to v2 when an application did not, and would not produce any telemetry. More detail of the approaches considered and their limitations can be found in the [Use a V2 API to evolve interfaces](https://github.com/open-telemetry/opentelemetry-go/issues/3920) issue. #### How to Change Other Interfaces If new functionality is needed for an interface that cannot be changed it MUST be added by including an additional interface. That added interface can be a simple interface for the specific functionality that you want to add or it can be a super-set of the original interface. For example, if you wanted to a `Close` method to the `Exporter` interface: ```go type Exporter interface { Export() } ``` A new interface, `Closer`, can be added: ```go type Closer interface { Close() } ``` Code that is passed the `Exporter` interface can now check to see if the passed value also satisfies the new interface. E.g. ```go func caller(e Exporter) { /* ... */ if c, ok := e.(Closer); ok { c.Close() } /* ... */ } ``` Alternatively, a new type that is the super-set of an `Exporter` can be created. ```go type ClosingExporter struct { Exporter Close() } ``` This new type can be used similar to the simple interface above in that a passed `Exporter` type can be asserted to satisfy the `ClosingExporter` type and the `Close` method called. This super-set approach can be useful if there is explicit behavior that needs to be coupled with the original type and passed as a unified type to a new function, but, because of this coupling, it also limits the applicability of the added functionality. If there exist other interfaces where this functionality should be added, each one will need their own super-set interfaces and will duplicate the pattern. For this reason, the simple targeted interface that defines the specific functionality should be preferred. ### Testing The tests should never leak goroutines. Use the term `ConcurrentSafe` in the test name when it aims to verify the absence of race conditions. ### Internal packages The use of internal packages should be scoped to a single module. A sub-module should never import from a parent internal package. This creates a coupling between the two modules where a user can upgrade the parent without the child and if the internal package API has changed it will fail to upgrade[^3]. There are two known exceptions to this rule: - `go.opentelemetry.io/otel/internal/global` - This package manages global state for all of opentelemetry-go. It needs to be a single package in order to ensure the uniqueness of the global state. - `go.opentelemetry.io/otel/internal/baggage` - This package provides values in a `context.Context` that need to be recognized by `go.opentelemetry.io/otel/baggage` and `go.opentelemetry.io/otel/bridge/opentracing` but remain private. If you have duplicate code in multiple modules, make that code into a Go template stored in `go.opentelemetry.io/otel/internal/shared` and use [gotmpl] to render the templates in the desired locations. See [#4404] for an example of this. [^3]: https://github.com/open-telemetry/opentelemetry-go/issues/3548 ## Approvers and Maintainers ### Approvers - [Evan Torrie](https://github.com/evantorrie), Verizon Media - [Sam Xie](https://github.com/XSAM), Cisco/AppDynamics - [David Ashpole](https://github.com/dashpole), Google - [Chester Cheung](https://github.com/hanyuancheung), Tencent - [Damien Mathieu](https://github.com/dmathieu), Elastic - [Anthony Mirabella](https://github.com/Aneurysm9), AWS ### Maintainers - [Aaron Clawson](https://github.com/MadVikingGod), LightStep - [Robert Pająk](https://github.com/pellared), Splunk - [Tyler Yahn](https://github.com/MrAlias), Splunk ### Emeritus - [Gustavo Silva Paiva](https://github.com/paivagustavo), LightStep - [Josh MacDonald](https://github.com/jmacd), LightStep ### Become an Approver or a Maintainer See the [community membership document in OpenTelemetry community repo](https://github.com/open-telemetry/community/blob/main/community-membership.md). [Approver]: #approvers [Maintainer]: #maintainers [gotmpl]: https://pkg.go.dev/go.opentelemetry.io/build-tools/gotmpl [#4404]: https://github.com/open-telemetry/opentelemetry-go/pull/4404 ================================================ FILE: vendor/go.opentelemetry.io/otel/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/go.opentelemetry.io/otel/Makefile ================================================ # Copyright The OpenTelemetry Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. TOOLS_MOD_DIR := ./internal/tools ALL_DOCS := $(shell find . -name '*.md' -type f | sort) ALL_GO_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {} \; | sort) OTEL_GO_MOD_DIRS := $(filter-out $(TOOLS_MOD_DIR), $(ALL_GO_MOD_DIRS)) ALL_COVERAGE_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {} \; | grep -E -v '^./example|^$(TOOLS_MOD_DIR)' | sort) GO = go TIMEOUT = 60 .DEFAULT_GOAL := precommit .PHONY: precommit ci precommit: generate dependabot-generate license-check misspell go-mod-tidy golangci-lint-fix test-default ci: generate dependabot-check license-check lint vanity-import-check build test-default check-clean-work-tree test-coverage # Tools TOOLS = $(CURDIR)/.tools $(TOOLS): @mkdir -p $@ $(TOOLS)/%: | $(TOOLS) cd $(TOOLS_MOD_DIR) && \ $(GO) build -o $@ $(PACKAGE) MULTIMOD = $(TOOLS)/multimod $(TOOLS)/multimod: PACKAGE=go.opentelemetry.io/build-tools/multimod SEMCONVGEN = $(TOOLS)/semconvgen $(TOOLS)/semconvgen: PACKAGE=go.opentelemetry.io/build-tools/semconvgen CROSSLINK = $(TOOLS)/crosslink $(TOOLS)/crosslink: PACKAGE=go.opentelemetry.io/build-tools/crosslink SEMCONVKIT = $(TOOLS)/semconvkit $(TOOLS)/semconvkit: PACKAGE=go.opentelemetry.io/otel/$(TOOLS_MOD_DIR)/semconvkit DBOTCONF = $(TOOLS)/dbotconf $(TOOLS)/dbotconf: PACKAGE=go.opentelemetry.io/build-tools/dbotconf GOLANGCI_LINT = $(TOOLS)/golangci-lint $(TOOLS)/golangci-lint: PACKAGE=github.com/golangci/golangci-lint/cmd/golangci-lint MISSPELL = $(TOOLS)/misspell $(TOOLS)/misspell: PACKAGE=github.com/client9/misspell/cmd/misspell GOCOVMERGE = $(TOOLS)/gocovmerge $(TOOLS)/gocovmerge: PACKAGE=github.com/wadey/gocovmerge STRINGER = $(TOOLS)/stringer $(TOOLS)/stringer: PACKAGE=golang.org/x/tools/cmd/stringer PORTO = $(TOOLS)/porto $(TOOLS)/porto: PACKAGE=github.com/jcchavezs/porto/cmd/porto GOJQ = $(TOOLS)/gojq $(TOOLS)/gojq: PACKAGE=github.com/itchyny/gojq/cmd/gojq GOTMPL = $(TOOLS)/gotmpl $(GOTMPL): PACKAGE=go.opentelemetry.io/build-tools/gotmpl GORELEASE = $(TOOLS)/gorelease $(GORELEASE): PACKAGE=golang.org/x/exp/cmd/gorelease .PHONY: tools tools: $(CROSSLINK) $(DBOTCONF) $(GOLANGCI_LINT) $(MISSPELL) $(GOCOVMERGE) $(STRINGER) $(PORTO) $(GOJQ) $(SEMCONVGEN) $(MULTIMOD) $(SEMCONVKIT) $(GOTMPL) $(GORELEASE) # Virtualized python tools via docker # The directory where the virtual environment is created. VENVDIR := venv # The directory where the python tools are installed. PYTOOLS := $(VENVDIR)/bin # The pip executable in the virtual environment. PIP := $(PYTOOLS)/pip # The directory in the docker image where the current directory is mounted. WORKDIR := /workdir # The python image to use for the virtual environment. PYTHONIMAGE := python:3.11.3-slim-bullseye # Run the python image with the current directory mounted. DOCKERPY := docker run --rm -v "$(CURDIR):$(WORKDIR)" -w $(WORKDIR) $(PYTHONIMAGE) # Create a virtual environment for Python tools. $(PYTOOLS): # The `--upgrade` flag is needed to ensure that the virtual environment is # created with the latest pip version. @$(DOCKERPY) bash -c "python3 -m venv $(VENVDIR) && $(PIP) install --upgrade pip" # Install python packages into the virtual environment. $(PYTOOLS)/%: | $(PYTOOLS) @$(DOCKERPY) $(PIP) install -r requirements.txt CODESPELL = $(PYTOOLS)/codespell $(CODESPELL): PACKAGE=codespell # Generate .PHONY: generate generate: go-generate vanity-import-fix .PHONY: go-generate go-generate: $(OTEL_GO_MOD_DIRS:%=go-generate/%) go-generate/%: DIR=$* go-generate/%: | $(STRINGER) $(GOTMPL) @echo "$(GO) generate $(DIR)/..." \ && cd $(DIR) \ && PATH="$(TOOLS):$${PATH}" $(GO) generate ./... .PHONY: vanity-import-fix vanity-import-fix: | $(PORTO) @$(PORTO) --include-internal -w . # Generate go.work file for local development. .PHONY: go-work go-work: | $(CROSSLINK) $(CROSSLINK) work --root=$(shell pwd) # Build .PHONY: build build: $(OTEL_GO_MOD_DIRS:%=build/%) $(OTEL_GO_MOD_DIRS:%=build-tests/%) build/%: DIR=$* build/%: @echo "$(GO) build $(DIR)/..." \ && cd $(DIR) \ && $(GO) build ./... build-tests/%: DIR=$* build-tests/%: @echo "$(GO) build tests $(DIR)/..." \ && cd $(DIR) \ && $(GO) list ./... \ | grep -v third_party \ | xargs $(GO) test -vet=off -run xxxxxMatchNothingxxxxx >/dev/null # Tests TEST_TARGETS := test-default test-bench test-short test-verbose test-race .PHONY: $(TEST_TARGETS) test test-default test-race: ARGS=-race test-bench: ARGS=-run=xxxxxMatchNothingxxxxx -test.benchtime=1ms -bench=. test-short: ARGS=-short test-verbose: ARGS=-v -race $(TEST_TARGETS): test test: $(OTEL_GO_MOD_DIRS:%=test/%) test/%: DIR=$* test/%: @echo "$(GO) test -timeout $(TIMEOUT)s $(ARGS) $(DIR)/..." \ && cd $(DIR) \ && $(GO) list ./... \ | grep -v third_party \ | xargs $(GO) test -timeout $(TIMEOUT)s $(ARGS) COVERAGE_MODE = atomic COVERAGE_PROFILE = coverage.out .PHONY: test-coverage test-coverage: | $(GOCOVMERGE) @set -e; \ printf "" > coverage.txt; \ for dir in $(ALL_COVERAGE_MOD_DIRS); do \ echo "$(GO) test -coverpkg=go.opentelemetry.io/otel/... -covermode=$(COVERAGE_MODE) -coverprofile="$(COVERAGE_PROFILE)" $${dir}/..."; \ (cd "$${dir}" && \ $(GO) list ./... \ | grep -v third_party \ | grep -v 'semconv/v.*' \ | xargs $(GO) test -coverpkg=./... -covermode=$(COVERAGE_MODE) -coverprofile="$(COVERAGE_PROFILE)" && \ $(GO) tool cover -html=coverage.out -o coverage.html); \ done; \ $(GOCOVMERGE) $$(find . -name coverage.out) > coverage.txt .PHONY: golangci-lint golangci-lint-fix golangci-lint-fix: ARGS=--fix golangci-lint-fix: golangci-lint golangci-lint: $(OTEL_GO_MOD_DIRS:%=golangci-lint/%) golangci-lint/%: DIR=$* golangci-lint/%: | $(GOLANGCI_LINT) @echo 'golangci-lint $(if $(ARGS),$(ARGS) ,)$(DIR)' \ && cd $(DIR) \ && $(GOLANGCI_LINT) run --allow-serial-runners $(ARGS) .PHONY: crosslink crosslink: | $(CROSSLINK) @echo "Updating intra-repository dependencies in all go modules" \ && $(CROSSLINK) --root=$(shell pwd) --prune .PHONY: go-mod-tidy go-mod-tidy: $(ALL_GO_MOD_DIRS:%=go-mod-tidy/%) go-mod-tidy/%: DIR=$* go-mod-tidy/%: | crosslink @echo "$(GO) mod tidy in $(DIR)" \ && cd $(DIR) \ && $(GO) mod tidy -compat=1.20 .PHONY: lint-modules lint-modules: go-mod-tidy .PHONY: lint lint: misspell lint-modules golangci-lint .PHONY: vanity-import-check vanity-import-check: | $(PORTO) @$(PORTO) --include-internal -l . || ( echo "(run: make vanity-import-fix)"; exit 1 ) .PHONY: misspell misspell: | $(MISSPELL) @$(MISSPELL) -w $(ALL_DOCS) .PHONY: codespell codespell: | $(CODESPELL) @$(DOCKERPY) $(CODESPELL) .PHONY: license-check license-check: @licRes=$$(for f in $$(find . -type f \( -iname '*.go' -o -iname '*.sh' \) ! -path '**/third_party/*' ! -path './.git/*' ) ; do \ awk '/Copyright The OpenTelemetry Authors|generated|GENERATED/ && NR<=4 { found=1; next } END { if (!found) print FILENAME }' $$f; \ done); \ if [ -n "$${licRes}" ]; then \ echo "license header checking failed:"; echo "$${licRes}"; \ exit 1; \ fi DEPENDABOT_CONFIG = .github/dependabot.yml .PHONY: dependabot-check dependabot-check: | $(DBOTCONF) @$(DBOTCONF) verify $(DEPENDABOT_CONFIG) || ( echo "(run: make dependabot-generate)"; exit 1 ) .PHONY: dependabot-generate dependabot-generate: | $(DBOTCONF) @$(DBOTCONF) generate > $(DEPENDABOT_CONFIG) .PHONY: check-clean-work-tree check-clean-work-tree: @if ! git diff --quiet; then \ echo; \ echo 'Working tree is not clean, did you forget to run "make precommit"?'; \ echo; \ git status; \ exit 1; \ fi SEMCONVPKG ?= "semconv/" .PHONY: semconv-generate semconv-generate: | $(SEMCONVGEN) $(SEMCONVKIT) [ "$(TAG)" ] || ( echo "TAG unset: missing opentelemetry semantic-conventions tag"; exit 1 ) [ "$(OTEL_SEMCONV_REPO)" ] || ( echo "OTEL_SEMCONV_REPO unset: missing path to opentelemetry semantic-conventions repo"; exit 1 ) $(SEMCONVGEN) -i "$(OTEL_SEMCONV_REPO)/model/." --only=span -p conventionType=trace -f trace.go -t "$(SEMCONVPKG)/template.j2" -s "$(TAG)" $(SEMCONVGEN) -i "$(OTEL_SEMCONV_REPO)/model/." --only=attribute_group -p conventionType=trace -f attribute_group.go -t "$(SEMCONVPKG)/template.j2" -s "$(TAG)" $(SEMCONVGEN) -i "$(OTEL_SEMCONV_REPO)/model/." --only=event -p conventionType=event -f event.go -t "$(SEMCONVPKG)/template.j2" -s "$(TAG)" $(SEMCONVGEN) -i "$(OTEL_SEMCONV_REPO)/model/." --only=resource -p conventionType=resource -f resource.go -t "$(SEMCONVPKG)/template.j2" -s "$(TAG)" $(SEMCONVKIT) -output "$(SEMCONVPKG)/$(TAG)" -tag "$(TAG)" .PHONY: gorelease gorelease: $(OTEL_GO_MOD_DIRS:%=gorelease/%) gorelease/%: DIR=$* gorelease/%:| $(GORELEASE) @echo "gorelease in $(DIR):" \ && cd $(DIR) \ && $(GORELEASE) \ || echo "" .PHONY: prerelease prerelease: | $(MULTIMOD) @[ "${MODSET}" ] || ( echo ">> env var MODSET is not set"; exit 1 ) $(MULTIMOD) verify && $(MULTIMOD) prerelease -m ${MODSET} COMMIT ?= "HEAD" .PHONY: add-tags add-tags: | $(MULTIMOD) @[ "${MODSET}" ] || ( echo ">> env var MODSET is not set"; exit 1 ) $(MULTIMOD) verify && $(MULTIMOD) tag -m ${MODSET} -c ${COMMIT} ================================================ FILE: vendor/go.opentelemetry.io/otel/README.md ================================================ # OpenTelemetry-Go [![CI](https://github.com/open-telemetry/opentelemetry-go/workflows/ci/badge.svg)](https://github.com/open-telemetry/opentelemetry-go/actions?query=workflow%3Aci+branch%3Amain) [![codecov.io](https://codecov.io/gh/open-telemetry/opentelemetry-go/coverage.svg?branch=main)](https://app.codecov.io/gh/open-telemetry/opentelemetry-go?branch=main) [![PkgGoDev](https://pkg.go.dev/badge/go.opentelemetry.io/otel)](https://pkg.go.dev/go.opentelemetry.io/otel) [![Go Report Card](https://goreportcard.com/badge/go.opentelemetry.io/otel)](https://goreportcard.com/report/go.opentelemetry.io/otel) [![Slack](https://img.shields.io/badge/slack-@cncf/otel--go-brightgreen.svg?logo=slack)](https://cloud-native.slack.com/archives/C01NPAXACKT) OpenTelemetry-Go is the [Go](https://golang.org/) implementation of [OpenTelemetry](https://opentelemetry.io/). It provides a set of APIs to directly measure performance and behavior of your software and send this data to observability platforms. ## Project Status | Signal | Status | Project | |---------|------------|-----------------------| | Traces | Stable | N/A | | Metrics | Mixed [1] | [Go: Metric SDK (GA)] | | Logs | Frozen [2] | N/A | [Go: Metric SDK (GA)]: https://github.com/orgs/open-telemetry/projects/34 - [1]: [Metrics API](https://pkg.go.dev/go.opentelemetry.io/otel/metric) is Stable. [Metrics SDK](https://pkg.go.dev/go.opentelemetry.io/otel/sdk/metric) is Beta. - [2]: The Logs signal development is halted for this project while we stabilize the Metrics SDK. No Logs Pull Requests are currently being accepted. Progress and status specific to this repository is tracked in our [project boards](https://github.com/open-telemetry/opentelemetry-go/projects) and [milestones](https://github.com/open-telemetry/opentelemetry-go/milestones). Project versioning information and stability guarantees can be found in the [versioning documentation](VERSIONING.md). ### Compatibility OpenTelemetry-Go ensures compatibility with the current supported versions of the [Go language](https://golang.org/doc/devel/release#policy): > Each major Go release is supported until there are two newer major releases. > For example, Go 1.5 was supported until the Go 1.7 release, and Go 1.6 was supported until the Go 1.8 release. For versions of Go that are no longer supported upstream, opentelemetry-go will stop ensuring compatibility with these versions in the following manner: - A minor release of opentelemetry-go will be made to add support for the new supported release of Go. - The following minor release of opentelemetry-go will remove compatibility testing for the oldest (now archived upstream) version of Go. This, and future, releases of opentelemetry-go may include features only supported by the currently supported versions of Go. Currently, this project supports the following environments. | OS | Go Version | Architecture | |---------|------------|--------------| | Ubuntu | 1.21 | amd64 | | Ubuntu | 1.20 | amd64 | | Ubuntu | 1.21 | 386 | | Ubuntu | 1.20 | 386 | | MacOS | 1.21 | amd64 | | MacOS | 1.20 | amd64 | | Windows | 1.21 | amd64 | | Windows | 1.20 | amd64 | | Windows | 1.21 | 386 | | Windows | 1.20 | 386 | While this project should work for other systems, no compatibility guarantees are made for those systems currently. ## Getting Started You can find a getting started guide on [opentelemetry.io](https://opentelemetry.io/docs/go/getting-started/). OpenTelemetry's goal is to provide a single set of APIs to capture distributed traces and metrics from your application and send them to an observability platform. This project allows you to do just that for applications written in Go. There are two steps to this process: instrument your application, and configure an exporter. ### Instrumentation To start capturing distributed traces and metric events from your application it first needs to be instrumented. The easiest way to do this is by using an instrumentation library for your code. Be sure to check out [the officially supported instrumentation libraries](https://github.com/open-telemetry/opentelemetry-go-contrib/tree/main/instrumentation). If you need to extend the telemetry an instrumentation library provides or want to build your own instrumentation for your application directly you will need to use the [Go otel](https://pkg.go.dev/go.opentelemetry.io/otel) package. The included [examples](./example/) are a good way to see some practical uses of this process. ### Export Now that your application is instrumented to collect telemetry, it needs an export pipeline to send that telemetry to an observability platform. All officially supported exporters for the OpenTelemetry project are contained in the [exporters directory](./exporters). | Exporter | Metrics | Traces | |---------------------------------------|:-------:|:------:| | [OTLP](./exporters/otlp/) | ✓ | ✓ | | [Prometheus](./exporters/prometheus/) | ✓ | | | [stdout](./exporters/stdout/) | ✓ | ✓ | | [Zipkin](./exporters/zipkin/) | | ✓ | ## Contributing See the [contributing documentation](CONTRIBUTING.md). ================================================ FILE: vendor/go.opentelemetry.io/otel/RELEASING.md ================================================ # Release Process ## Semantic Convention Generation New versions of the [OpenTelemetry Semantic Conventions] mean new versions of the `semconv` package need to be generated. The `semconv-generate` make target is used for this. 1. Checkout a local copy of the [OpenTelemetry Semantic Conventions] to the desired release tag. 2. Pull the latest `otel/semconvgen` image: `docker pull otel/semconvgen:latest` 3. Run the `make semconv-generate ...` target from this repository. For example, ```sh export TAG="v1.21.0" # Change to the release version you are generating. export OTEL_SEMCONV_REPO="/absolute/path/to/opentelemetry/semantic-conventions" docker pull otel/semconvgen:latest make semconv-generate # Uses the exported TAG and OTEL_SEMCONV_REPO. ``` This should create a new sub-package of [`semconv`](./semconv). Ensure things look correct before submitting a pull request to include the addition. ## Breaking changes validation You can run `make gorelease` that runs [gorelease](https://pkg.go.dev/golang.org/x/exp/cmd/gorelease) to ensure that there are no unwanted changes done in the public API. You can check/report problems with `gorelease` [here](https://golang.org/issues/26420). ## Pre-Release First, decide which module sets will be released and update their versions in `versions.yaml`. Commit this change to a new branch. Update go.mod for submodules to depend on the new release which will happen in the next step. 1. Run the `prerelease` make target. It creates a branch `prerelease__` that will contain all release changes. ``` make prerelease MODSET= ``` 2. Verify the changes. ``` git diff ...prerelease__ ``` This should have changed the version for all modules to be ``. If these changes look correct, merge them into your pre-release branch: ```go git merge prerelease__ ``` 3. Update the [Changelog](./CHANGELOG.md). - Make sure all relevant changes for this release are included and are in language that non-contributors to the project can understand. To verify this, you can look directly at the commits since the ``. ``` git --no-pager log --pretty=oneline "..HEAD" ``` - Move all the `Unreleased` changes into a new section following the title scheme (`[] - `). - Update all the appropriate links at the bottom. 4. Push the changes to upstream and create a Pull Request on GitHub. Be sure to include the curated changes from the [Changelog](./CHANGELOG.md) in the description. ## Tag Once the Pull Request with all the version changes has been approved and merged it is time to tag the merged commit. ***IMPORTANT***: It is critical you use the same tag that you used in the Pre-Release step! Failure to do so will leave things in a broken state. As long as you do not change `versions.yaml` between pre-release and this step, things should be fine. ***IMPORTANT***: [There is currently no way to remove an incorrectly tagged version of a Go module](https://github.com/golang/go/issues/34189). It is critical you make sure the version you push upstream is correct. [Failure to do so will lead to minor emergencies and tough to work around](https://github.com/open-telemetry/opentelemetry-go/issues/331). 1. For each module set that will be released, run the `add-tags` make target using the `` of the commit on the main branch for the merged Pull Request. ``` make add-tags MODSET= COMMIT= ``` It should only be necessary to provide an explicit `COMMIT` value if the current `HEAD` of your working directory is not the correct commit. 2. Push tags to the upstream remote (not your fork: `github.com/open-telemetry/opentelemetry-go.git`). Make sure you push all sub-modules as well. ``` git push upstream git push upstream ... ``` ## Release Finally create a Release for the new `` on GitHub. The release body should include all the release notes from the Changelog for this release. ## Verify Examples After releasing verify that examples build outside of the repository. ``` ./verify_examples.sh ``` The script copies examples into a different directory removes any `replace` declarations in `go.mod` and builds them. This ensures they build with the published release, not the local copy. ## Post-Release ### Contrib Repository Once verified be sure to [make a release for the `contrib` repository](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/RELEASING.md) that uses this release. ### Website Documentation Update the [Go instrumentation documentation] in the OpenTelemetry website under [content/en/docs/instrumentation/go]. Importantly, bump any package versions referenced to be the latest one you just released and ensure all code examples still compile and are accurate. [OpenTelemetry Semantic Conventions]: https://github.com/open-telemetry/semantic-conventions [Go instrumentation documentation]: https://opentelemetry.io/docs/instrumentation/go/ [content/en/docs/instrumentation/go]: https://github.com/open-telemetry/opentelemetry.io/tree/main/content/en/docs/instrumentation/go ### Demo Repository Bump the dependencies in the following Go services: - [`accountingservice`](https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/accountingservice) - [`checkoutservice`](https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/checkoutservice) - [`productcatalogservice`](https://github.com/open-telemetry/opentelemetry-demo/tree/main/src/productcatalogservice) ================================================ FILE: vendor/go.opentelemetry.io/otel/VERSIONING.md ================================================ # Versioning This document describes the versioning policy for this repository. This policy is designed so the following goals can be achieved. **Users are provided a codebase of value that is stable and secure.** ## Policy * Versioning of this project will be idiomatic of a Go project using [Go modules](https://github.com/golang/go/wiki/Modules). * [Semantic import versioning](https://github.com/golang/go/wiki/Modules#semantic-import-versioning) will be used. * Versions will comply with [semver 2.0](https://semver.org/spec/v2.0.0.html) with the following exceptions. * New methods may be added to exported API interfaces. All exported interfaces that fall within this exception will include the following paragraph in their public documentation. > Warning: methods may be added to this interface in minor releases. * If a module is version `v2` or higher, the major version of the module must be included as a `/vN` at the end of the module paths used in `go.mod` files (e.g., `module go.opentelemetry.io/otel/v2`, `require go.opentelemetry.io/otel/v2 v2.0.1`) and in the package import path (e.g., `import "go.opentelemetry.io/otel/v2/trace"`). This includes the paths used in `go get` commands (e.g., `go get go.opentelemetry.io/otel/v2@v2.0.1`. Note there is both a `/v2` and a `@v2.0.1` in that example. One way to think about it is that the module name now includes the `/v2`, so include `/v2` whenever you are using the module name). * If a module is version `v0` or `v1`, do not include the major version in either the module path or the import path. * Modules will be used to encapsulate signals and components. * Experimental modules still under active development will be versioned at `v0` to imply the stability guarantee defined by [semver](https://semver.org/spec/v2.0.0.html#spec-item-4). > Major version zero (0.y.z) is for initial development. Anything MAY > change at any time. The public API SHOULD NOT be considered stable. * Mature modules for which we guarantee a stable public API will be versioned with a major version greater than `v0`. * The decision to make a module stable will be made on a case-by-case basis by the maintainers of this project. * Experimental modules will start their versioning at `v0.0.0` and will increment their minor version when backwards incompatible changes are released and increment their patch version when backwards compatible changes are released. * All stable modules that use the same major version number will use the same entire version number. * Stable modules may be released with an incremented minor or patch version even though that module has not been changed, but rather so that it will remain at the same version as other stable modules that did undergo change. * When an experimental module becomes stable a new stable module version will be released and will include this now stable module. The new stable module version will be an increment of the minor version number and will be applied to all existing stable modules as well as the newly stable module being released. * Versioning of the associated [contrib repository](https://github.com/open-telemetry/opentelemetry-go-contrib) of this project will be idiomatic of a Go project using [Go modules](https://github.com/golang/go/wiki/Modules). * [Semantic import versioning](https://github.com/golang/go/wiki/Modules#semantic-import-versioning) will be used. * Versions will comply with [semver 2.0](https://semver.org/spec/v2.0.0.html). * If a module is version `v2` or higher, the major version of the module must be included as a `/vN` at the end of the module paths used in `go.mod` files (e.g., `module go.opentelemetry.io/contrib/instrumentation/host/v2`, `require go.opentelemetry.io/contrib/instrumentation/host/v2 v2.0.1`) and in the package import path (e.g., `import "go.opentelemetry.io/contrib/instrumentation/host/v2"`). This includes the paths used in `go get` commands (e.g., `go get go.opentelemetry.io/contrib/instrumentation/host/v2@v2.0.1`. Note there is both a `/v2` and a `@v2.0.1` in that example. One way to think about it is that the module name now includes the `/v2`, so include `/v2` whenever you are using the module name). * If a module is version `v0` or `v1`, do not include the major version in either the module path or the import path. * In addition to public APIs, telemetry produced by stable instrumentation will remain stable and backwards compatible. This is to avoid breaking alerts and dashboard. * Modules will be used to encapsulate instrumentation, detectors, exporters, propagators, and any other independent sets of related components. * Experimental modules still under active development will be versioned at `v0` to imply the stability guarantee defined by [semver](https://semver.org/spec/v2.0.0.html#spec-item-4). > Major version zero (0.y.z) is for initial development. Anything MAY > change at any time. The public API SHOULD NOT be considered stable. * Mature modules for which we guarantee a stable public API and telemetry will be versioned with a major version greater than `v0`. * Experimental modules will start their versioning at `v0.0.0` and will increment their minor version when backwards incompatible changes are released and increment their patch version when backwards compatible changes are released. * Stable contrib modules cannot depend on experimental modules from this project. * All stable contrib modules of the same major version with this project will use the same entire version as this project. * Stable modules may be released with an incremented minor or patch version even though that module's code has not been changed. Instead the only change that will have been included is to have updated that modules dependency on this project's stable APIs. * When an experimental module in contrib becomes stable a new stable module version will be released and will include this now stable module. The new stable module version will be an increment of the minor version number and will be applied to all existing stable contrib modules, this project's modules, and the newly stable module being released. * Contrib modules will be kept up to date with this project's releases. * Due to the dependency contrib modules will implicitly have on this project's modules the release of stable contrib modules to match the released version number will be staggered after this project's release. There is no explicit time guarantee for how long after this projects release the contrib release will be. Effort should be made to keep them as close in time as possible. * No additional stable release in this project can be made until the contrib repository has a matching stable release. * No release can be made in the contrib repository after this project's stable release except for a stable release of the contrib repository. * GitHub releases will be made for all releases. * Go modules will be made available at Go package mirrors. ## Example Versioning Lifecycle To better understand the implementation of the above policy the following example is provided. This project is simplified to include only the following modules and their versions: * `otel`: `v0.14.0` * `otel/trace`: `v0.14.0` * `otel/metric`: `v0.14.0` * `otel/baggage`: `v0.14.0` * `otel/sdk/trace`: `v0.14.0` * `otel/sdk/metric`: `v0.14.0` These modules have been developed to a point where the `otel/trace`, `otel/baggage`, and `otel/sdk/trace` modules have reached a point that they should be considered for a stable release. The `otel/metric` and `otel/sdk/metric` are still under active development and the `otel` module depends on both `otel/trace` and `otel/metric`. The `otel` package is refactored to remove its dependencies on `otel/metric` so it can be released as stable as well. With that done the following release candidates are made: * `otel`: `v1.0.0-RC1` * `otel/trace`: `v1.0.0-RC1` * `otel/baggage`: `v1.0.0-RC1` * `otel/sdk/trace`: `v1.0.0-RC1` The `otel/metric` and `otel/sdk/metric` modules remain at `v0.14.0`. A few minor issues are discovered in the `otel/trace` package. These issues are resolved with some minor, but backwards incompatible, changes and are released as a second release candidate: * `otel`: `v1.0.0-RC2` * `otel/trace`: `v1.0.0-RC2` * `otel/baggage`: `v1.0.0-RC2` * `otel/sdk/trace`: `v1.0.0-RC2` Notice that all module version numbers are incremented to adhere to our versioning policy. After these release candidates have been evaluated to satisfaction, they are released as version `v1.0.0`. * `otel`: `v1.0.0` * `otel/trace`: `v1.0.0` * `otel/baggage`: `v1.0.0` * `otel/sdk/trace`: `v1.0.0` Since both the `go` utility and the Go module system support [the semantic versioning definition of precedence](https://semver.org/spec/v2.0.0.html#spec-item-11), this release will correctly be interpreted as the successor to the previous release candidates. Active development of this project continues. The `otel/metric` module now has backwards incompatible changes to its API that need to be released and the `otel/baggage` module has a minor bug fix that needs to be released. The following release is made: * `otel`: `v1.0.1` * `otel/trace`: `v1.0.1` * `otel/metric`: `v0.15.0` * `otel/baggage`: `v1.0.1` * `otel/sdk/trace`: `v1.0.1` * `otel/sdk/metric`: `v0.15.0` Notice that, again, all stable module versions are incremented in unison and the `otel/sdk/metric` package, which depends on the `otel/metric` package, also bumped its version. This bump of the `otel/sdk/metric` package makes sense given their coupling, though it is not explicitly required by our versioning policy. As we progress, the `otel/metric` and `otel/sdk/metric` packages have reached a point where they should be evaluated for stability. The `otel` module is reintegrated with the `otel/metric` package and the following release is made: * `otel`: `v1.1.0-RC1` * `otel/trace`: `v1.1.0-RC1` * `otel/metric`: `v1.1.0-RC1` * `otel/baggage`: `v1.1.0-RC1` * `otel/sdk/trace`: `v1.1.0-RC1` * `otel/sdk/metric`: `v1.1.0-RC1` All the modules are evaluated and determined to a viable stable release. They are then released as version `v1.1.0` (the minor version is incremented to indicate the addition of new signal). * `otel`: `v1.1.0` * `otel/trace`: `v1.1.0` * `otel/metric`: `v1.1.0` * `otel/baggage`: `v1.1.0` * `otel/sdk/trace`: `v1.1.0` * `otel/sdk/metric`: `v1.1.0` ================================================ FILE: vendor/go.opentelemetry.io/otel/attribute/doc.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package attribute provides key and value attributes. package attribute // import "go.opentelemetry.io/otel/attribute" ================================================ FILE: vendor/go.opentelemetry.io/otel/attribute/encoder.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package attribute // import "go.opentelemetry.io/otel/attribute" import ( "bytes" "sync" "sync/atomic" ) type ( // Encoder is a mechanism for serializing an attribute set into a specific // string representation that supports caching, to avoid repeated // serialization. An example could be an exporter encoding the attribute // set into a wire representation. Encoder interface { // Encode returns the serialized encoding of the attribute set using // its Iterator. This result may be cached by a attribute.Set. Encode(iterator Iterator) string // ID returns a value that is unique for each class of attribute // encoder. Attribute encoders allocate these using `NewEncoderID`. ID() EncoderID } // EncoderID is used to identify distinct Encoder // implementations, for caching encoded results. EncoderID struct { value uint64 } // defaultAttrEncoder uses a sync.Pool of buffers to reduce the number of // allocations used in encoding attributes. This implementation encodes a // comma-separated list of key=value, with '/'-escaping of '=', ',', and // '\'. defaultAttrEncoder struct { // pool is a pool of attribute set builders. The buffers in this pool // grow to a size that most attribute encodings will not allocate new // memory. pool sync.Pool // *bytes.Buffer } ) // escapeChar is used to ensure uniqueness of the attribute encoding where // keys or values contain either '=' or ','. Since there is no parser needed // for this encoding and its only requirement is to be unique, this choice is // arbitrary. Users will see these in some exporters (e.g., stdout), so the // backslash ('\') is used as a conventional choice. const escapeChar = '\\' var ( _ Encoder = &defaultAttrEncoder{} // encoderIDCounter is for generating IDs for other attribute encoders. encoderIDCounter uint64 defaultEncoderOnce sync.Once defaultEncoderID = NewEncoderID() defaultEncoderInstance *defaultAttrEncoder ) // NewEncoderID returns a unique attribute encoder ID. It should be called // once per each type of attribute encoder. Preferably in init() or in var // definition. func NewEncoderID() EncoderID { return EncoderID{value: atomic.AddUint64(&encoderIDCounter, 1)} } // DefaultEncoder returns an attribute encoder that encodes attributes in such // a way that each escaped attribute's key is followed by an equal sign and // then by an escaped attribute's value. All key-value pairs are separated by // a comma. // // Escaping is done by prepending a backslash before either a backslash, equal // sign or a comma. func DefaultEncoder() Encoder { defaultEncoderOnce.Do(func() { defaultEncoderInstance = &defaultAttrEncoder{ pool: sync.Pool{ New: func() interface{} { return &bytes.Buffer{} }, }, } }) return defaultEncoderInstance } // Encode is a part of an implementation of the AttributeEncoder interface. func (d *defaultAttrEncoder) Encode(iter Iterator) string { buf := d.pool.Get().(*bytes.Buffer) defer d.pool.Put(buf) buf.Reset() for iter.Next() { i, keyValue := iter.IndexedAttribute() if i > 0 { _, _ = buf.WriteRune(',') } copyAndEscape(buf, string(keyValue.Key)) _, _ = buf.WriteRune('=') if keyValue.Value.Type() == STRING { copyAndEscape(buf, keyValue.Value.AsString()) } else { _, _ = buf.WriteString(keyValue.Value.Emit()) } } return buf.String() } // ID is a part of an implementation of the AttributeEncoder interface. func (*defaultAttrEncoder) ID() EncoderID { return defaultEncoderID } // copyAndEscape escapes `=`, `,` and its own escape character (`\`), // making the default encoding unique. func copyAndEscape(buf *bytes.Buffer, val string) { for _, ch := range val { switch ch { case '=', ',', escapeChar: _, _ = buf.WriteRune(escapeChar) } _, _ = buf.WriteRune(ch) } } // Valid returns true if this encoder ID was allocated by // `NewEncoderID`. Invalid encoder IDs will not be cached. func (id EncoderID) Valid() bool { return id.value != 0 } ================================================ FILE: vendor/go.opentelemetry.io/otel/attribute/filter.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package attribute // import "go.opentelemetry.io/otel/attribute" // Filter supports removing certain attributes from attribute sets. When // the filter returns true, the attribute will be kept in the filtered // attribute set. When the filter returns false, the attribute is excluded // from the filtered attribute set, and the attribute instead appears in // the removed list of excluded attributes. type Filter func(KeyValue) bool // NewAllowKeysFilter returns a Filter that only allows attributes with one of // the provided keys. // // If keys is empty a deny-all filter is returned. func NewAllowKeysFilter(keys ...Key) Filter { if len(keys) <= 0 { return func(kv KeyValue) bool { return false } } allowed := make(map[Key]struct{}) for _, k := range keys { allowed[k] = struct{}{} } return func(kv KeyValue) bool { _, ok := allowed[kv.Key] return ok } } // NewDenyKeysFilter returns a Filter that only allows attributes // that do not have one of the provided keys. // // If keys is empty an allow-all filter is returned. func NewDenyKeysFilter(keys ...Key) Filter { if len(keys) <= 0 { return func(kv KeyValue) bool { return true } } forbid := make(map[Key]struct{}) for _, k := range keys { forbid[k] = struct{}{} } return func(kv KeyValue) bool { _, ok := forbid[kv.Key] return !ok } } ================================================ FILE: vendor/go.opentelemetry.io/otel/attribute/iterator.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package attribute // import "go.opentelemetry.io/otel/attribute" // Iterator allows iterating over the set of attributes in order, sorted by // key. type Iterator struct { storage *Set idx int } // MergeIterator supports iterating over two sets of attributes while // eliminating duplicate values from the combined set. The first iterator // value takes precedence. type MergeIterator struct { one oneIterator two oneIterator current KeyValue } type oneIterator struct { iter Iterator done bool attr KeyValue } // Next moves the iterator to the next position. Returns false if there are no // more attributes. func (i *Iterator) Next() bool { i.idx++ return i.idx < i.Len() } // Label returns current KeyValue. Must be called only after Next returns // true. // // Deprecated: Use Attribute instead. func (i *Iterator) Label() KeyValue { return i.Attribute() } // Attribute returns the current KeyValue of the Iterator. It must be called // only after Next returns true. func (i *Iterator) Attribute() KeyValue { kv, _ := i.storage.Get(i.idx) return kv } // IndexedLabel returns current index and attribute. Must be called only // after Next returns true. // // Deprecated: Use IndexedAttribute instead. func (i *Iterator) IndexedLabel() (int, KeyValue) { return i.idx, i.Attribute() } // IndexedAttribute returns current index and attribute. Must be called only // after Next returns true. func (i *Iterator) IndexedAttribute() (int, KeyValue) { return i.idx, i.Attribute() } // Len returns a number of attributes in the iterated set. func (i *Iterator) Len() int { return i.storage.Len() } // ToSlice is a convenience function that creates a slice of attributes from // the passed iterator. The iterator is set up to start from the beginning // before creating the slice. func (i *Iterator) ToSlice() []KeyValue { l := i.Len() if l == 0 { return nil } i.idx = -1 slice := make([]KeyValue, 0, l) for i.Next() { slice = append(slice, i.Attribute()) } return slice } // NewMergeIterator returns a MergeIterator for merging two attribute sets. // Duplicates are resolved by taking the value from the first set. func NewMergeIterator(s1, s2 *Set) MergeIterator { mi := MergeIterator{ one: makeOne(s1.Iter()), two: makeOne(s2.Iter()), } return mi } func makeOne(iter Iterator) oneIterator { oi := oneIterator{ iter: iter, } oi.advance() return oi } func (oi *oneIterator) advance() { if oi.done = !oi.iter.Next(); !oi.done { oi.attr = oi.iter.Attribute() } } // Next returns true if there is another attribute available. func (m *MergeIterator) Next() bool { if m.one.done && m.two.done { return false } if m.one.done { m.current = m.two.attr m.two.advance() return true } if m.two.done { m.current = m.one.attr m.one.advance() return true } if m.one.attr.Key == m.two.attr.Key { m.current = m.one.attr // first iterator attribute value wins m.one.advance() m.two.advance() return true } if m.one.attr.Key < m.two.attr.Key { m.current = m.one.attr m.one.advance() return true } m.current = m.two.attr m.two.advance() return true } // Label returns the current value after Next() returns true. // // Deprecated: Use Attribute instead. func (m *MergeIterator) Label() KeyValue { return m.current } // Attribute returns the current value after Next() returns true. func (m *MergeIterator) Attribute() KeyValue { return m.current } ================================================ FILE: vendor/go.opentelemetry.io/otel/attribute/key.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package attribute // import "go.opentelemetry.io/otel/attribute" // Key represents the key part in key-value pairs. It's a string. The // allowed character set in the key depends on the use of the key. type Key string // Bool creates a KeyValue instance with a BOOL Value. // // If creating both a key and value at the same time, use the provided // convenience function instead -- Bool(name, value). func (k Key) Bool(v bool) KeyValue { return KeyValue{ Key: k, Value: BoolValue(v), } } // BoolSlice creates a KeyValue instance with a BOOLSLICE Value. // // If creating both a key and value at the same time, use the provided // convenience function instead -- BoolSlice(name, value). func (k Key) BoolSlice(v []bool) KeyValue { return KeyValue{ Key: k, Value: BoolSliceValue(v), } } // Int creates a KeyValue instance with an INT64 Value. // // If creating both a key and value at the same time, use the provided // convenience function instead -- Int(name, value). func (k Key) Int(v int) KeyValue { return KeyValue{ Key: k, Value: IntValue(v), } } // IntSlice creates a KeyValue instance with an INT64SLICE Value. // // If creating both a key and value at the same time, use the provided // convenience function instead -- IntSlice(name, value). func (k Key) IntSlice(v []int) KeyValue { return KeyValue{ Key: k, Value: IntSliceValue(v), } } // Int64 creates a KeyValue instance with an INT64 Value. // // If creating both a key and value at the same time, use the provided // convenience function instead -- Int64(name, value). func (k Key) Int64(v int64) KeyValue { return KeyValue{ Key: k, Value: Int64Value(v), } } // Int64Slice creates a KeyValue instance with an INT64SLICE Value. // // If creating both a key and value at the same time, use the provided // convenience function instead -- Int64Slice(name, value). func (k Key) Int64Slice(v []int64) KeyValue { return KeyValue{ Key: k, Value: Int64SliceValue(v), } } // Float64 creates a KeyValue instance with a FLOAT64 Value. // // If creating both a key and value at the same time, use the provided // convenience function instead -- Float64(name, value). func (k Key) Float64(v float64) KeyValue { return KeyValue{ Key: k, Value: Float64Value(v), } } // Float64Slice creates a KeyValue instance with a FLOAT64SLICE Value. // // If creating both a key and value at the same time, use the provided // convenience function instead -- Float64(name, value). func (k Key) Float64Slice(v []float64) KeyValue { return KeyValue{ Key: k, Value: Float64SliceValue(v), } } // String creates a KeyValue instance with a STRING Value. // // If creating both a key and value at the same time, use the provided // convenience function instead -- String(name, value). func (k Key) String(v string) KeyValue { return KeyValue{ Key: k, Value: StringValue(v), } } // StringSlice creates a KeyValue instance with a STRINGSLICE Value. // // If creating both a key and value at the same time, use the provided // convenience function instead -- StringSlice(name, value). func (k Key) StringSlice(v []string) KeyValue { return KeyValue{ Key: k, Value: StringSliceValue(v), } } // Defined returns true for non-empty keys. func (k Key) Defined() bool { return len(k) != 0 } ================================================ FILE: vendor/go.opentelemetry.io/otel/attribute/kv.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package attribute // import "go.opentelemetry.io/otel/attribute" import ( "fmt" ) // KeyValue holds a key and value pair. type KeyValue struct { Key Key Value Value } // Valid returns if kv is a valid OpenTelemetry attribute. func (kv KeyValue) Valid() bool { return kv.Key.Defined() && kv.Value.Type() != INVALID } // Bool creates a KeyValue with a BOOL Value type. func Bool(k string, v bool) KeyValue { return Key(k).Bool(v) } // BoolSlice creates a KeyValue with a BOOLSLICE Value type. func BoolSlice(k string, v []bool) KeyValue { return Key(k).BoolSlice(v) } // Int creates a KeyValue with an INT64 Value type. func Int(k string, v int) KeyValue { return Key(k).Int(v) } // IntSlice creates a KeyValue with an INT64SLICE Value type. func IntSlice(k string, v []int) KeyValue { return Key(k).IntSlice(v) } // Int64 creates a KeyValue with an INT64 Value type. func Int64(k string, v int64) KeyValue { return Key(k).Int64(v) } // Int64Slice creates a KeyValue with an INT64SLICE Value type. func Int64Slice(k string, v []int64) KeyValue { return Key(k).Int64Slice(v) } // Float64 creates a KeyValue with a FLOAT64 Value type. func Float64(k string, v float64) KeyValue { return Key(k).Float64(v) } // Float64Slice creates a KeyValue with a FLOAT64SLICE Value type. func Float64Slice(k string, v []float64) KeyValue { return Key(k).Float64Slice(v) } // String creates a KeyValue with a STRING Value type. func String(k, v string) KeyValue { return Key(k).String(v) } // StringSlice creates a KeyValue with a STRINGSLICE Value type. func StringSlice(k string, v []string) KeyValue { return Key(k).StringSlice(v) } // Stringer creates a new key-value pair with a passed name and a string // value generated by the passed Stringer interface. func Stringer(k string, v fmt.Stringer) KeyValue { return Key(k).String(v.String()) } ================================================ FILE: vendor/go.opentelemetry.io/otel/attribute/set.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package attribute // import "go.opentelemetry.io/otel/attribute" import ( "encoding/json" "reflect" "sort" "sync" ) type ( // Set is the representation for a distinct attribute set. It manages an // immutable set of attributes, with an internal cache for storing // attribute encodings. // // This type supports the Equivalent method of comparison using values of // type Distinct. Set struct { equivalent Distinct } // Distinct wraps a variable-size array of KeyValue, constructed with keys // in sorted order. This can be used as a map key or for equality checking // between Sets. Distinct struct { iface interface{} } // Sortable implements sort.Interface, used for sorting KeyValue. This is // an exported type to support a memory optimization. A pointer to one of // these is needed for the call to sort.Stable(), which the caller may // provide in order to avoid an allocation. See NewSetWithSortable(). Sortable []KeyValue ) var ( // keyValueType is used in computeDistinctReflect. keyValueType = reflect.TypeOf(KeyValue{}) // emptySet is returned for empty attribute sets. emptySet = &Set{ equivalent: Distinct{ iface: [0]KeyValue{}, }, } // sortables is a pool of Sortables used to create Sets with a user does // not provide one. sortables = sync.Pool{ New: func() interface{} { return new(Sortable) }, } ) // EmptySet returns a reference to a Set with no elements. // // This is a convenience provided for optimized calling utility. func EmptySet() *Set { return emptySet } // reflectValue abbreviates reflect.ValueOf(d). func (d Distinct) reflectValue() reflect.Value { return reflect.ValueOf(d.iface) } // Valid returns true if this value refers to a valid Set. func (d Distinct) Valid() bool { return d.iface != nil } // Len returns the number of attributes in this set. func (l *Set) Len() int { if l == nil || !l.equivalent.Valid() { return 0 } return l.equivalent.reflectValue().Len() } // Get returns the KeyValue at ordered position idx in this set. func (l *Set) Get(idx int) (KeyValue, bool) { if l == nil || !l.equivalent.Valid() { return KeyValue{}, false } value := l.equivalent.reflectValue() if idx >= 0 && idx < value.Len() { // Note: The Go compiler successfully avoids an allocation for // the interface{} conversion here: return value.Index(idx).Interface().(KeyValue), true } return KeyValue{}, false } // Value returns the value of a specified key in this set. func (l *Set) Value(k Key) (Value, bool) { if l == nil || !l.equivalent.Valid() { return Value{}, false } rValue := l.equivalent.reflectValue() vlen := rValue.Len() idx := sort.Search(vlen, func(idx int) bool { return rValue.Index(idx).Interface().(KeyValue).Key >= k }) if idx >= vlen { return Value{}, false } keyValue := rValue.Index(idx).Interface().(KeyValue) if k == keyValue.Key { return keyValue.Value, true } return Value{}, false } // HasValue tests whether a key is defined in this set. func (l *Set) HasValue(k Key) bool { if l == nil { return false } _, ok := l.Value(k) return ok } // Iter returns an iterator for visiting the attributes in this set. func (l *Set) Iter() Iterator { return Iterator{ storage: l, idx: -1, } } // ToSlice returns the set of attributes belonging to this set, sorted, where // keys appear no more than once. func (l *Set) ToSlice() []KeyValue { iter := l.Iter() return iter.ToSlice() } // Equivalent returns a value that may be used as a map key. The Distinct type // guarantees that the result will equal the equivalent. Distinct value of any // attribute set with the same elements as this, where sets are made unique by // choosing the last value in the input for any given key. func (l *Set) Equivalent() Distinct { if l == nil || !l.equivalent.Valid() { return emptySet.equivalent } return l.equivalent } // Equals returns true if the argument set is equivalent to this set. func (l *Set) Equals(o *Set) bool { return l.Equivalent() == o.Equivalent() } // Encoded returns the encoded form of this set, according to encoder. func (l *Set) Encoded(encoder Encoder) string { if l == nil || encoder == nil { return "" } return encoder.Encode(l.Iter()) } func empty() Set { return Set{ equivalent: emptySet.equivalent, } } // NewSet returns a new Set. See the documentation for // NewSetWithSortableFiltered for more details. // // Except for empty sets, this method adds an additional allocation compared // with calls that include a Sortable. func NewSet(kvs ...KeyValue) Set { // Check for empty set. if len(kvs) == 0 { return empty() } srt := sortables.Get().(*Sortable) s, _ := NewSetWithSortableFiltered(kvs, srt, nil) sortables.Put(srt) return s } // NewSetWithSortable returns a new Set. See the documentation for // NewSetWithSortableFiltered for more details. // // This call includes a Sortable option as a memory optimization. func NewSetWithSortable(kvs []KeyValue, tmp *Sortable) Set { // Check for empty set. if len(kvs) == 0 { return empty() } s, _ := NewSetWithSortableFiltered(kvs, tmp, nil) return s } // NewSetWithFiltered returns a new Set. See the documentation for // NewSetWithSortableFiltered for more details. // // This call includes a Filter to include/exclude attribute keys from the // return value. Excluded keys are returned as a slice of attribute values. func NewSetWithFiltered(kvs []KeyValue, filter Filter) (Set, []KeyValue) { // Check for empty set. if len(kvs) == 0 { return empty(), nil } srt := sortables.Get().(*Sortable) s, filtered := NewSetWithSortableFiltered(kvs, srt, filter) sortables.Put(srt) return s, filtered } // NewSetWithSortableFiltered returns a new Set. // // Duplicate keys are eliminated by taking the last value. This // re-orders the input slice so that unique last-values are contiguous // at the end of the slice. // // This ensures the following: // // - Last-value-wins semantics // - Caller sees the reordering, but doesn't lose values // - Repeated call preserve last-value wins. // // Note that methods are defined on Set, although this returns Set. Callers // can avoid memory allocations by: // // - allocating a Sortable for use as a temporary in this method // - allocating a Set for storing the return value of this constructor. // // The result maintains a cache of encoded attributes, by attribute.EncoderID. // This value should not be copied after its first use. // // The second []KeyValue return value is a list of attributes that were // excluded by the Filter (if non-nil). func NewSetWithSortableFiltered(kvs []KeyValue, tmp *Sortable, filter Filter) (Set, []KeyValue) { // Check for empty set. if len(kvs) == 0 { return empty(), nil } *tmp = kvs // Stable sort so the following de-duplication can implement // last-value-wins semantics. sort.Stable(tmp) *tmp = nil position := len(kvs) - 1 offset := position - 1 // The requirements stated above require that the stable // result be placed in the end of the input slice, while // overwritten values are swapped to the beginning. // // De-duplicate with last-value-wins semantics. Preserve // duplicate values at the beginning of the input slice. for ; offset >= 0; offset-- { if kvs[offset].Key == kvs[position].Key { continue } position-- kvs[offset], kvs[position] = kvs[position], kvs[offset] } if filter != nil { return filterSet(kvs[position:], filter) } return Set{ equivalent: computeDistinct(kvs[position:]), }, nil } // filterSet reorders kvs so that included keys are contiguous at the end of // the slice, while excluded keys precede the included keys. func filterSet(kvs []KeyValue, filter Filter) (Set, []KeyValue) { var excluded []KeyValue // Move attributes that do not match the filter so they're adjacent before // calling computeDistinct(). distinctPosition := len(kvs) // Swap indistinct keys forward and distinct keys toward the // end of the slice. offset := len(kvs) - 1 for ; offset >= 0; offset-- { if filter(kvs[offset]) { distinctPosition-- kvs[offset], kvs[distinctPosition] = kvs[distinctPosition], kvs[offset] continue } } excluded = kvs[:distinctPosition] return Set{ equivalent: computeDistinct(kvs[distinctPosition:]), }, excluded } // Filter returns a filtered copy of this Set. See the documentation for // NewSetWithSortableFiltered for more details. func (l *Set) Filter(re Filter) (Set, []KeyValue) { if re == nil { return Set{ equivalent: l.equivalent, }, nil } // Note: This could be refactored to avoid the temporary slice // allocation, if it proves to be expensive. return filterSet(l.ToSlice(), re) } // computeDistinct returns a Distinct using either the fixed- or // reflect-oriented code path, depending on the size of the input. The input // slice is assumed to already be sorted and de-duplicated. func computeDistinct(kvs []KeyValue) Distinct { iface := computeDistinctFixed(kvs) if iface == nil { iface = computeDistinctReflect(kvs) } return Distinct{ iface: iface, } } // computeDistinctFixed computes a Distinct for small slices. It returns nil // if the input is too large for this code path. func computeDistinctFixed(kvs []KeyValue) interface{} { switch len(kvs) { case 1: ptr := new([1]KeyValue) copy((*ptr)[:], kvs) return *ptr case 2: ptr := new([2]KeyValue) copy((*ptr)[:], kvs) return *ptr case 3: ptr := new([3]KeyValue) copy((*ptr)[:], kvs) return *ptr case 4: ptr := new([4]KeyValue) copy((*ptr)[:], kvs) return *ptr case 5: ptr := new([5]KeyValue) copy((*ptr)[:], kvs) return *ptr case 6: ptr := new([6]KeyValue) copy((*ptr)[:], kvs) return *ptr case 7: ptr := new([7]KeyValue) copy((*ptr)[:], kvs) return *ptr case 8: ptr := new([8]KeyValue) copy((*ptr)[:], kvs) return *ptr case 9: ptr := new([9]KeyValue) copy((*ptr)[:], kvs) return *ptr case 10: ptr := new([10]KeyValue) copy((*ptr)[:], kvs) return *ptr default: return nil } } // computeDistinctReflect computes a Distinct using reflection, works for any // size input. func computeDistinctReflect(kvs []KeyValue) interface{} { at := reflect.New(reflect.ArrayOf(len(kvs), keyValueType)).Elem() for i, keyValue := range kvs { *(at.Index(i).Addr().Interface().(*KeyValue)) = keyValue } return at.Interface() } // MarshalJSON returns the JSON encoding of the Set. func (l *Set) MarshalJSON() ([]byte, error) { return json.Marshal(l.equivalent.iface) } // MarshalLog is the marshaling function used by the logging system to represent this exporter. func (l Set) MarshalLog() interface{} { kvs := make(map[string]string) for _, kv := range l.ToSlice() { kvs[string(kv.Key)] = kv.Value.Emit() } return kvs } // Len implements sort.Interface. func (l *Sortable) Len() int { return len(*l) } // Swap implements sort.Interface. func (l *Sortable) Swap(i, j int) { (*l)[i], (*l)[j] = (*l)[j], (*l)[i] } // Less implements sort.Interface. func (l *Sortable) Less(i, j int) bool { return (*l)[i].Key < (*l)[j].Key } ================================================ FILE: vendor/go.opentelemetry.io/otel/attribute/type_string.go ================================================ // Code generated by "stringer -type=Type"; DO NOT EDIT. package attribute import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[INVALID-0] _ = x[BOOL-1] _ = x[INT64-2] _ = x[FLOAT64-3] _ = x[STRING-4] _ = x[BOOLSLICE-5] _ = x[INT64SLICE-6] _ = x[FLOAT64SLICE-7] _ = x[STRINGSLICE-8] } const _Type_name = "INVALIDBOOLINT64FLOAT64STRINGBOOLSLICEINT64SLICEFLOAT64SLICESTRINGSLICE" var _Type_index = [...]uint8{0, 7, 11, 16, 23, 29, 38, 48, 60, 71} func (i Type) String() string { if i < 0 || i >= Type(len(_Type_index)-1) { return "Type(" + strconv.FormatInt(int64(i), 10) + ")" } return _Type_name[_Type_index[i]:_Type_index[i+1]] } ================================================ FILE: vendor/go.opentelemetry.io/otel/attribute/value.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package attribute // import "go.opentelemetry.io/otel/attribute" import ( "encoding/json" "fmt" "reflect" "strconv" "go.opentelemetry.io/otel/internal" "go.opentelemetry.io/otel/internal/attribute" ) //go:generate stringer -type=Type // Type describes the type of the data Value holds. type Type int // nolint: revive // redefines builtin Type. // Value represents the value part in key-value pairs. type Value struct { vtype Type numeric uint64 stringly string slice interface{} } const ( // INVALID is used for a Value with no value set. INVALID Type = iota // BOOL is a boolean Type Value. BOOL // INT64 is a 64-bit signed integral Type Value. INT64 // FLOAT64 is a 64-bit floating point Type Value. FLOAT64 // STRING is a string Type Value. STRING // BOOLSLICE is a slice of booleans Type Value. BOOLSLICE // INT64SLICE is a slice of 64-bit signed integral numbers Type Value. INT64SLICE // FLOAT64SLICE is a slice of 64-bit floating point numbers Type Value. FLOAT64SLICE // STRINGSLICE is a slice of strings Type Value. STRINGSLICE ) // BoolValue creates a BOOL Value. func BoolValue(v bool) Value { return Value{ vtype: BOOL, numeric: internal.BoolToRaw(v), } } // BoolSliceValue creates a BOOLSLICE Value. func BoolSliceValue(v []bool) Value { return Value{vtype: BOOLSLICE, slice: attribute.BoolSliceValue(v)} } // IntValue creates an INT64 Value. func IntValue(v int) Value { return Int64Value(int64(v)) } // IntSliceValue creates an INTSLICE Value. func IntSliceValue(v []int) Value { var int64Val int64 cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(int64Val))) for i, val := range v { cp.Elem().Index(i).SetInt(int64(val)) } return Value{ vtype: INT64SLICE, slice: cp.Elem().Interface(), } } // Int64Value creates an INT64 Value. func Int64Value(v int64) Value { return Value{ vtype: INT64, numeric: internal.Int64ToRaw(v), } } // Int64SliceValue creates an INT64SLICE Value. func Int64SliceValue(v []int64) Value { return Value{vtype: INT64SLICE, slice: attribute.Int64SliceValue(v)} } // Float64Value creates a FLOAT64 Value. func Float64Value(v float64) Value { return Value{ vtype: FLOAT64, numeric: internal.Float64ToRaw(v), } } // Float64SliceValue creates a FLOAT64SLICE Value. func Float64SliceValue(v []float64) Value { return Value{vtype: FLOAT64SLICE, slice: attribute.Float64SliceValue(v)} } // StringValue creates a STRING Value. func StringValue(v string) Value { return Value{ vtype: STRING, stringly: v, } } // StringSliceValue creates a STRINGSLICE Value. func StringSliceValue(v []string) Value { return Value{vtype: STRINGSLICE, slice: attribute.StringSliceValue(v)} } // Type returns a type of the Value. func (v Value) Type() Type { return v.vtype } // AsBool returns the bool value. Make sure that the Value's type is // BOOL. func (v Value) AsBool() bool { return internal.RawToBool(v.numeric) } // AsBoolSlice returns the []bool value. Make sure that the Value's type is // BOOLSLICE. func (v Value) AsBoolSlice() []bool { if v.vtype != BOOLSLICE { return nil } return v.asBoolSlice() } func (v Value) asBoolSlice() []bool { return attribute.AsBoolSlice(v.slice) } // AsInt64 returns the int64 value. Make sure that the Value's type is // INT64. func (v Value) AsInt64() int64 { return internal.RawToInt64(v.numeric) } // AsInt64Slice returns the []int64 value. Make sure that the Value's type is // INT64SLICE. func (v Value) AsInt64Slice() []int64 { if v.vtype != INT64SLICE { return nil } return v.asInt64Slice() } func (v Value) asInt64Slice() []int64 { return attribute.AsInt64Slice(v.slice) } // AsFloat64 returns the float64 value. Make sure that the Value's // type is FLOAT64. func (v Value) AsFloat64() float64 { return internal.RawToFloat64(v.numeric) } // AsFloat64Slice returns the []float64 value. Make sure that the Value's type is // FLOAT64SLICE. func (v Value) AsFloat64Slice() []float64 { if v.vtype != FLOAT64SLICE { return nil } return v.asFloat64Slice() } func (v Value) asFloat64Slice() []float64 { return attribute.AsFloat64Slice(v.slice) } // AsString returns the string value. Make sure that the Value's type // is STRING. func (v Value) AsString() string { return v.stringly } // AsStringSlice returns the []string value. Make sure that the Value's type is // STRINGSLICE. func (v Value) AsStringSlice() []string { if v.vtype != STRINGSLICE { return nil } return v.asStringSlice() } func (v Value) asStringSlice() []string { return attribute.AsStringSlice(v.slice) } type unknownValueType struct{} // AsInterface returns Value's data as interface{}. func (v Value) AsInterface() interface{} { switch v.Type() { case BOOL: return v.AsBool() case BOOLSLICE: return v.asBoolSlice() case INT64: return v.AsInt64() case INT64SLICE: return v.asInt64Slice() case FLOAT64: return v.AsFloat64() case FLOAT64SLICE: return v.asFloat64Slice() case STRING: return v.stringly case STRINGSLICE: return v.asStringSlice() } return unknownValueType{} } // Emit returns a string representation of Value's data. func (v Value) Emit() string { switch v.Type() { case BOOLSLICE: return fmt.Sprint(v.asBoolSlice()) case BOOL: return strconv.FormatBool(v.AsBool()) case INT64SLICE: return fmt.Sprint(v.asInt64Slice()) case INT64: return strconv.FormatInt(v.AsInt64(), 10) case FLOAT64SLICE: return fmt.Sprint(v.asFloat64Slice()) case FLOAT64: return fmt.Sprint(v.AsFloat64()) case STRINGSLICE: return fmt.Sprint(v.asStringSlice()) case STRING: return v.stringly default: return "unknown" } } // MarshalJSON returns the JSON encoding of the Value. func (v Value) MarshalJSON() ([]byte, error) { var jsonVal struct { Type string Value interface{} } jsonVal.Type = v.Type().String() jsonVal.Value = v.AsInterface() return json.Marshal(jsonVal) } ================================================ FILE: vendor/go.opentelemetry.io/otel/baggage/baggage.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package baggage // import "go.opentelemetry.io/otel/baggage" import ( "errors" "fmt" "net/url" "regexp" "strings" "go.opentelemetry.io/otel/internal/baggage" ) const ( maxMembers = 180 maxBytesPerMembers = 4096 maxBytesPerBaggageString = 8192 listDelimiter = "," keyValueDelimiter = "=" propertyDelimiter = ";" keyDef = `([\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5a\x5e-\x7a\x7c\x7e]+)` valueDef = `([\x21\x23-\x2b\x2d-\x3a\x3c-\x5B\x5D-\x7e]*)` keyValueDef = `\s*` + keyDef + `\s*` + keyValueDelimiter + `\s*` + valueDef + `\s*` ) var ( keyRe = regexp.MustCompile(`^` + keyDef + `$`) valueRe = regexp.MustCompile(`^` + valueDef + `$`) propertyRe = regexp.MustCompile(`^(?:\s*` + keyDef + `\s*|` + keyValueDef + `)$`) ) var ( errInvalidKey = errors.New("invalid key") errInvalidValue = errors.New("invalid value") errInvalidProperty = errors.New("invalid baggage list-member property") errInvalidMember = errors.New("invalid baggage list-member") errMemberNumber = errors.New("too many list-members in baggage-string") errMemberBytes = errors.New("list-member too large") errBaggageBytes = errors.New("baggage-string too large") ) // Property is an additional metadata entry for a baggage list-member. type Property struct { key, value string // hasValue indicates if a zero-value value means the property does not // have a value or if it was the zero-value. hasValue bool } // NewKeyProperty returns a new Property for key. // // If key is invalid, an error will be returned. func NewKeyProperty(key string) (Property, error) { if !keyRe.MatchString(key) { return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidKey, key) } p := Property{key: key} return p, nil } // NewKeyValueProperty returns a new Property for key with value. // // If key or value are invalid, an error will be returned. func NewKeyValueProperty(key, value string) (Property, error) { if !keyRe.MatchString(key) { return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidKey, key) } if !valueRe.MatchString(value) { return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidValue, value) } p := Property{ key: key, value: value, hasValue: true, } return p, nil } func newInvalidProperty() Property { return Property{} } // parseProperty attempts to decode a Property from the passed string. It // returns an error if the input is invalid according to the W3C Baggage // specification. func parseProperty(property string) (Property, error) { if property == "" { return newInvalidProperty(), nil } match := propertyRe.FindStringSubmatch(property) if len(match) != 4 { return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidProperty, property) } var p Property if match[1] != "" { p.key = match[1] } else { p.key = match[2] p.value = match[3] p.hasValue = true } return p, nil } // validate ensures p conforms to the W3C Baggage specification, returning an // error otherwise. func (p Property) validate() error { errFunc := func(err error) error { return fmt.Errorf("invalid property: %w", err) } if !keyRe.MatchString(p.key) { return errFunc(fmt.Errorf("%w: %q", errInvalidKey, p.key)) } if p.hasValue && !valueRe.MatchString(p.value) { return errFunc(fmt.Errorf("%w: %q", errInvalidValue, p.value)) } if !p.hasValue && p.value != "" { return errFunc(errors.New("inconsistent value")) } return nil } // Key returns the Property key. func (p Property) Key() string { return p.key } // Value returns the Property value. Additionally, a boolean value is returned // indicating if the returned value is the empty if the Property has a value // that is empty or if the value is not set. func (p Property) Value() (string, bool) { return p.value, p.hasValue } // String encodes Property into a string compliant with the W3C Baggage // specification. func (p Property) String() string { if p.hasValue { return fmt.Sprintf("%s%s%v", p.key, keyValueDelimiter, p.value) } return p.key } type properties []Property func fromInternalProperties(iProps []baggage.Property) properties { if len(iProps) == 0 { return nil } props := make(properties, len(iProps)) for i, p := range iProps { props[i] = Property{ key: p.Key, value: p.Value, hasValue: p.HasValue, } } return props } func (p properties) asInternal() []baggage.Property { if len(p) == 0 { return nil } iProps := make([]baggage.Property, len(p)) for i, prop := range p { iProps[i] = baggage.Property{ Key: prop.key, Value: prop.value, HasValue: prop.hasValue, } } return iProps } func (p properties) Copy() properties { if len(p) == 0 { return nil } props := make(properties, len(p)) copy(props, p) return props } // validate ensures each Property in p conforms to the W3C Baggage // specification, returning an error otherwise. func (p properties) validate() error { for _, prop := range p { if err := prop.validate(); err != nil { return err } } return nil } // String encodes properties into a string compliant with the W3C Baggage // specification. func (p properties) String() string { props := make([]string, len(p)) for i, prop := range p { props[i] = prop.String() } return strings.Join(props, propertyDelimiter) } // Member is a list-member of a baggage-string as defined by the W3C Baggage // specification. type Member struct { key, value string properties properties // hasData indicates whether the created property contains data or not. // Properties that do not contain data are invalid with no other check // required. hasData bool } // NewMember returns a new Member from the passed arguments. The key will be // used directly while the value will be url decoded after validation. An error // is returned if the created Member would be invalid according to the W3C // Baggage specification. func NewMember(key, value string, props ...Property) (Member, error) { m := Member{ key: key, value: value, properties: properties(props).Copy(), hasData: true, } if err := m.validate(); err != nil { return newInvalidMember(), err } decodedValue, err := url.QueryUnescape(value) if err != nil { return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidValue, value) } m.value = decodedValue return m, nil } func newInvalidMember() Member { return Member{} } // parseMember attempts to decode a Member from the passed string. It returns // an error if the input is invalid according to the W3C Baggage // specification. func parseMember(member string) (Member, error) { if n := len(member); n > maxBytesPerMembers { return newInvalidMember(), fmt.Errorf("%w: %d", errMemberBytes, n) } var ( key, value string props properties ) keyValue, properties, found := strings.Cut(member, propertyDelimiter) if found { // Parse the member properties. for _, pStr := range strings.Split(properties, propertyDelimiter) { p, err := parseProperty(pStr) if err != nil { return newInvalidMember(), err } props = append(props, p) } } // Parse the member key/value pair. // Take into account a value can contain equal signs (=). k, v, found := strings.Cut(keyValue, keyValueDelimiter) if !found { return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidMember, member) } // "Leading and trailing whitespaces are allowed but MUST be trimmed // when converting the header into a data structure." key = strings.TrimSpace(k) var err error value, err = url.QueryUnescape(strings.TrimSpace(v)) if err != nil { return newInvalidMember(), fmt.Errorf("%w: %q", err, value) } if !keyRe.MatchString(key) { return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidKey, key) } if !valueRe.MatchString(value) { return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidValue, value) } return Member{key: key, value: value, properties: props, hasData: true}, nil } // validate ensures m conforms to the W3C Baggage specification. // A key is just an ASCII string, but a value must be URL encoded UTF-8, // returning an error otherwise. func (m Member) validate() error { if !m.hasData { return fmt.Errorf("%w: %q", errInvalidMember, m) } if !keyRe.MatchString(m.key) { return fmt.Errorf("%w: %q", errInvalidKey, m.key) } if !valueRe.MatchString(m.value) { return fmt.Errorf("%w: %q", errInvalidValue, m.value) } return m.properties.validate() } // Key returns the Member key. func (m Member) Key() string { return m.key } // Value returns the Member value. func (m Member) Value() string { return m.value } // Properties returns a copy of the Member properties. func (m Member) Properties() []Property { return m.properties.Copy() } // String encodes Member into a string compliant with the W3C Baggage // specification. func (m Member) String() string { // A key is just an ASCII string, but a value is URL encoded UTF-8. s := fmt.Sprintf("%s%s%s", m.key, keyValueDelimiter, url.QueryEscape(m.value)) if len(m.properties) > 0 { s = fmt.Sprintf("%s%s%s", s, propertyDelimiter, m.properties.String()) } return s } // Baggage is a list of baggage members representing the baggage-string as // defined by the W3C Baggage specification. type Baggage struct { //nolint:golint list baggage.List } // New returns a new valid Baggage. It returns an error if it results in a // Baggage exceeding limits set in that specification. // // It expects all the provided members to have already been validated. func New(members ...Member) (Baggage, error) { if len(members) == 0 { return Baggage{}, nil } b := make(baggage.List) for _, m := range members { if !m.hasData { return Baggage{}, errInvalidMember } // OpenTelemetry resolves duplicates by last-one-wins. b[m.key] = baggage.Item{ Value: m.value, Properties: m.properties.asInternal(), } } // Check member numbers after deduplication. if len(b) > maxMembers { return Baggage{}, errMemberNumber } bag := Baggage{b} if n := len(bag.String()); n > maxBytesPerBaggageString { return Baggage{}, fmt.Errorf("%w: %d", errBaggageBytes, n) } return bag, nil } // Parse attempts to decode a baggage-string from the passed string. It // returns an error if the input is invalid according to the W3C Baggage // specification. // // If there are duplicate list-members contained in baggage, the last one // defined (reading left-to-right) will be the only one kept. This diverges // from the W3C Baggage specification which allows duplicate list-members, but // conforms to the OpenTelemetry Baggage specification. func Parse(bStr string) (Baggage, error) { if bStr == "" { return Baggage{}, nil } if n := len(bStr); n > maxBytesPerBaggageString { return Baggage{}, fmt.Errorf("%w: %d", errBaggageBytes, n) } b := make(baggage.List) for _, memberStr := range strings.Split(bStr, listDelimiter) { m, err := parseMember(memberStr) if err != nil { return Baggage{}, err } // OpenTelemetry resolves duplicates by last-one-wins. b[m.key] = baggage.Item{ Value: m.value, Properties: m.properties.asInternal(), } } // OpenTelemetry does not allow for duplicate list-members, but the W3C // specification does. Now that we have deduplicated, ensure the baggage // does not exceed list-member limits. if len(b) > maxMembers { return Baggage{}, errMemberNumber } return Baggage{b}, nil } // Member returns the baggage list-member identified by key. // // If there is no list-member matching the passed key the returned Member will // be a zero-value Member. // The returned member is not validated, as we assume the validation happened // when it was added to the Baggage. func (b Baggage) Member(key string) Member { v, ok := b.list[key] if !ok { // We do not need to worry about distinguishing between the situation // where a zero-valued Member is included in the Baggage because a // zero-valued Member is invalid according to the W3C Baggage // specification (it has an empty key). return newInvalidMember() } return Member{ key: key, value: v.Value, properties: fromInternalProperties(v.Properties), hasData: true, } } // Members returns all the baggage list-members. // The order of the returned list-members does not have significance. // // The returned members are not validated, as we assume the validation happened // when they were added to the Baggage. func (b Baggage) Members() []Member { if len(b.list) == 0 { return nil } members := make([]Member, 0, len(b.list)) for k, v := range b.list { members = append(members, Member{ key: k, value: v.Value, properties: fromInternalProperties(v.Properties), hasData: true, }) } return members } // SetMember returns a copy the Baggage with the member included. If the // baggage contains a Member with the same key the existing Member is // replaced. // // If member is invalid according to the W3C Baggage specification, an error // is returned with the original Baggage. func (b Baggage) SetMember(member Member) (Baggage, error) { if !member.hasData { return b, errInvalidMember } n := len(b.list) if _, ok := b.list[member.key]; !ok { n++ } list := make(baggage.List, n) for k, v := range b.list { // Do not copy if we are just going to overwrite. if k == member.key { continue } list[k] = v } list[member.key] = baggage.Item{ Value: member.value, Properties: member.properties.asInternal(), } return Baggage{list: list}, nil } // DeleteMember returns a copy of the Baggage with the list-member identified // by key removed. func (b Baggage) DeleteMember(key string) Baggage { n := len(b.list) if _, ok := b.list[key]; ok { n-- } list := make(baggage.List, n) for k, v := range b.list { if k == key { continue } list[k] = v } return Baggage{list: list} } // Len returns the number of list-members in the Baggage. func (b Baggage) Len() int { return len(b.list) } // String encodes Baggage into a string compliant with the W3C Baggage // specification. The returned string will be invalid if the Baggage contains // any invalid list-members. func (b Baggage) String() string { members := make([]string, 0, len(b.list)) for k, v := range b.list { members = append(members, Member{ key: k, value: v.Value, properties: fromInternalProperties(v.Properties), }.String()) } return strings.Join(members, listDelimiter) } ================================================ FILE: vendor/go.opentelemetry.io/otel/baggage/context.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package baggage // import "go.opentelemetry.io/otel/baggage" import ( "context" "go.opentelemetry.io/otel/internal/baggage" ) // ContextWithBaggage returns a copy of parent with baggage. func ContextWithBaggage(parent context.Context, b Baggage) context.Context { // Delegate so any hooks for the OpenTracing bridge are handled. return baggage.ContextWithList(parent, b.list) } // ContextWithoutBaggage returns a copy of parent with no baggage. func ContextWithoutBaggage(parent context.Context) context.Context { // Delegate so any hooks for the OpenTracing bridge are handled. return baggage.ContextWithList(parent, nil) } // FromContext returns the baggage contained in ctx. func FromContext(ctx context.Context) Baggage { // Delegate so any hooks for the OpenTracing bridge are handled. return Baggage{list: baggage.ListFromContext(ctx)} } ================================================ FILE: vendor/go.opentelemetry.io/otel/baggage/doc.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /* Package baggage provides functionality for storing and retrieving baggage items in Go context. For propagating the baggage, see the go.opentelemetry.io/otel/propagation package. */ package baggage // import "go.opentelemetry.io/otel/baggage" ================================================ FILE: vendor/go.opentelemetry.io/otel/codes/codes.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package codes // import "go.opentelemetry.io/otel/codes" import ( "encoding/json" "fmt" "strconv" ) const ( // Unset is the default status code. Unset Code = 0 // Error indicates the operation contains an error. // // NOTE: The error code in OTLP is 2. // The value of this enum is only relevant to the internals // of the Go SDK. Error Code = 1 // Ok indicates operation has been validated by an Application developers // or Operator to have completed successfully, or contain no error. // // NOTE: The Ok code in OTLP is 1. // The value of this enum is only relevant to the internals // of the Go SDK. Ok Code = 2 maxCode = 3 ) // Code is an 32-bit representation of a status state. type Code uint32 var codeToStr = map[Code]string{ Unset: "Unset", Error: "Error", Ok: "Ok", } var strToCode = map[string]Code{ `"Unset"`: Unset, `"Error"`: Error, `"Ok"`: Ok, } // String returns the Code as a string. func (c Code) String() string { return codeToStr[c] } // UnmarshalJSON unmarshals b into the Code. // // This is based on the functionality in the gRPC codes package: // https://github.com/grpc/grpc-go/blob/bb64fee312b46ebee26be43364a7a966033521b1/codes/codes.go#L218-L244 func (c *Code) UnmarshalJSON(b []byte) error { // From json.Unmarshaler: By convention, to approximate the behavior of // Unmarshal itself, Unmarshalers implement UnmarshalJSON([]byte("null")) as // a no-op. if string(b) == "null" { return nil } if c == nil { return fmt.Errorf("nil receiver passed to UnmarshalJSON") } var x interface{} if err := json.Unmarshal(b, &x); err != nil { return err } switch x.(type) { case string: if jc, ok := strToCode[string(b)]; ok { *c = jc return nil } return fmt.Errorf("invalid code: %q", string(b)) case float64: if ci, err := strconv.ParseUint(string(b), 10, 32); err == nil { if ci >= maxCode { return fmt.Errorf("invalid code: %q", ci) } *c = Code(ci) return nil } return fmt.Errorf("invalid code: %q", string(b)) default: return fmt.Errorf("invalid code: %q", string(b)) } } // MarshalJSON returns c as the JSON encoding of c. func (c *Code) MarshalJSON() ([]byte, error) { if c == nil { return []byte("null"), nil } str, ok := codeToStr[*c] if !ok { return nil, fmt.Errorf("invalid code: %d", *c) } return []byte(fmt.Sprintf("%q", str)), nil } ================================================ FILE: vendor/go.opentelemetry.io/otel/codes/doc.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /* Package codes defines the canonical error codes used by OpenTelemetry. It conforms to [the OpenTelemetry specification](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/specification/trace/api.md#set-status). */ package codes // import "go.opentelemetry.io/otel/codes" ================================================ FILE: vendor/go.opentelemetry.io/otel/doc.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /* Package otel provides global access to the OpenTelemetry API. The subpackages of the otel package provide an implementation of the OpenTelemetry API. The provided API is used to instrument code and measure data about that code's performance and operation. The measured data, by default, is not processed or transmitted anywhere. An implementation of the OpenTelemetry SDK, like the default SDK implementation (go.opentelemetry.io/otel/sdk), and associated exporters are used to process and transport this data. To read the getting started guide, see https://opentelemetry.io/docs/go/getting-started/. To read more about tracing, see go.opentelemetry.io/otel/trace. To read more about metrics, see go.opentelemetry.io/otel/metric. To read more about propagation, see go.opentelemetry.io/otel/propagation and go.opentelemetry.io/otel/baggage. */ package otel // import "go.opentelemetry.io/otel" ================================================ FILE: vendor/go.opentelemetry.io/otel/error_handler.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package otel // import "go.opentelemetry.io/otel" // ErrorHandler handles irremediable events. type ErrorHandler interface { // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // Handle handles any error deemed irremediable by an OpenTelemetry // component. Handle(error) // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. } // ErrorHandlerFunc is a convenience adapter to allow the use of a function // as an ErrorHandler. type ErrorHandlerFunc func(error) var _ ErrorHandler = ErrorHandlerFunc(nil) // Handle handles the irremediable error by calling the ErrorHandlerFunc itself. func (f ErrorHandlerFunc) Handle(err error) { f(err) } ================================================ FILE: vendor/go.opentelemetry.io/otel/get_main_pkgs.sh ================================================ #!/usr/bin/env bash # Copyright The OpenTelemetry Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -euo pipefail top_dir='.' if [[ $# -gt 0 ]]; then top_dir="${1}" fi p=$(pwd) mod_dirs=() # Note `mapfile` does not exist in older bash versions: # https://stackoverflow.com/questions/41475261/need-alternative-to-readarray-mapfile-for-script-on-older-version-of-bash while IFS= read -r line; do mod_dirs+=("$line") done < <(find "${top_dir}" -type f -name 'go.mod' -exec dirname {} \; | sort) for mod_dir in "${mod_dirs[@]}"; do cd "${mod_dir}" while IFS= read -r line; do echo ".${line#${p}}" done < <(go list --find -f '{{.Name}}|{{.Dir}}' ./... | grep '^main|' | cut -f 2- -d '|') cd "${p}" done ================================================ FILE: vendor/go.opentelemetry.io/otel/handler.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package otel // import "go.opentelemetry.io/otel" import ( "go.opentelemetry.io/otel/internal/global" ) var ( // Compile-time check global.ErrDelegator implements ErrorHandler. _ ErrorHandler = (*global.ErrDelegator)(nil) // Compile-time check global.ErrLogger implements ErrorHandler. _ ErrorHandler = (*global.ErrLogger)(nil) ) // GetErrorHandler returns the global ErrorHandler instance. // // The default ErrorHandler instance returned will log all errors to STDERR // until an override ErrorHandler is set with SetErrorHandler. All // ErrorHandler returned prior to this will automatically forward errors to // the set instance instead of logging. // // Subsequent calls to SetErrorHandler after the first will not forward errors // to the new ErrorHandler for prior returned instances. func GetErrorHandler() ErrorHandler { return global.GetErrorHandler() } // SetErrorHandler sets the global ErrorHandler to h. // // The first time this is called all ErrorHandler previously returned from // GetErrorHandler will send errors to h instead of the default logging // ErrorHandler. Subsequent calls will set the global ErrorHandler, but not // delegate errors to h. func SetErrorHandler(h ErrorHandler) { global.SetErrorHandler(h) } // Handle is a convenience function for ErrorHandler().Handle(err). func Handle(err error) { global.Handle(err) } ================================================ FILE: vendor/go.opentelemetry.io/otel/internal/attribute/attribute.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /* Package attribute provide several helper functions for some commonly used logic of processing attributes. */ package attribute // import "go.opentelemetry.io/otel/internal/attribute" import ( "reflect" ) // BoolSliceValue converts a bool slice into an array with same elements as slice. func BoolSliceValue(v []bool) interface{} { var zero bool cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero))) copy(cp.Elem().Slice(0, len(v)).Interface().([]bool), v) return cp.Elem().Interface() } // Int64SliceValue converts an int64 slice into an array with same elements as slice. func Int64SliceValue(v []int64) interface{} { var zero int64 cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero))) copy(cp.Elem().Slice(0, len(v)).Interface().([]int64), v) return cp.Elem().Interface() } // Float64SliceValue converts a float64 slice into an array with same elements as slice. func Float64SliceValue(v []float64) interface{} { var zero float64 cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero))) copy(cp.Elem().Slice(0, len(v)).Interface().([]float64), v) return cp.Elem().Interface() } // StringSliceValue converts a string slice into an array with same elements as slice. func StringSliceValue(v []string) interface{} { var zero string cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeOf(zero))) copy(cp.Elem().Slice(0, len(v)).Interface().([]string), v) return cp.Elem().Interface() } // AsBoolSlice converts a bool array into a slice into with same elements as array. func AsBoolSlice(v interface{}) []bool { rv := reflect.ValueOf(v) if rv.Type().Kind() != reflect.Array { return nil } var zero bool correctLen := rv.Len() correctType := reflect.ArrayOf(correctLen, reflect.TypeOf(zero)) cpy := reflect.New(correctType) _ = reflect.Copy(cpy.Elem(), rv) return cpy.Elem().Slice(0, correctLen).Interface().([]bool) } // AsInt64Slice converts an int64 array into a slice into with same elements as array. func AsInt64Slice(v interface{}) []int64 { rv := reflect.ValueOf(v) if rv.Type().Kind() != reflect.Array { return nil } var zero int64 correctLen := rv.Len() correctType := reflect.ArrayOf(correctLen, reflect.TypeOf(zero)) cpy := reflect.New(correctType) _ = reflect.Copy(cpy.Elem(), rv) return cpy.Elem().Slice(0, correctLen).Interface().([]int64) } // AsFloat64Slice converts a float64 array into a slice into with same elements as array. func AsFloat64Slice(v interface{}) []float64 { rv := reflect.ValueOf(v) if rv.Type().Kind() != reflect.Array { return nil } var zero float64 correctLen := rv.Len() correctType := reflect.ArrayOf(correctLen, reflect.TypeOf(zero)) cpy := reflect.New(correctType) _ = reflect.Copy(cpy.Elem(), rv) return cpy.Elem().Slice(0, correctLen).Interface().([]float64) } // AsStringSlice converts a string array into a slice into with same elements as array. func AsStringSlice(v interface{}) []string { rv := reflect.ValueOf(v) if rv.Type().Kind() != reflect.Array { return nil } var zero string correctLen := rv.Len() correctType := reflect.ArrayOf(correctLen, reflect.TypeOf(zero)) cpy := reflect.New(correctType) _ = reflect.Copy(cpy.Elem(), rv) return cpy.Elem().Slice(0, correctLen).Interface().([]string) } ================================================ FILE: vendor/go.opentelemetry.io/otel/internal/baggage/baggage.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /* Package baggage provides base types and functionality to store and retrieve baggage in Go context. This package exists because the OpenTracing bridge to OpenTelemetry needs to synchronize state whenever baggage for a context is modified and that context contains an OpenTracing span. If it were not for this need this package would not need to exist and the `go.opentelemetry.io/otel/baggage` package would be the singular place where W3C baggage is handled. */ package baggage // import "go.opentelemetry.io/otel/internal/baggage" // List is the collection of baggage members. The W3C allows for duplicates, // but OpenTelemetry does not, therefore, this is represented as a map. type List map[string]Item // Item is the value and metadata properties part of a list-member. type Item struct { Value string Properties []Property } // Property is a metadata entry for a list-member. type Property struct { Key, Value string // HasValue indicates if a zero-value value means the property does not // have a value or if it was the zero-value. HasValue bool } ================================================ FILE: vendor/go.opentelemetry.io/otel/internal/baggage/context.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package baggage // import "go.opentelemetry.io/otel/internal/baggage" import "context" type baggageContextKeyType int const baggageKey baggageContextKeyType = iota // SetHookFunc is a callback called when storing baggage in the context. type SetHookFunc func(context.Context, List) context.Context // GetHookFunc is a callback called when getting baggage from the context. type GetHookFunc func(context.Context, List) List type baggageState struct { list List setHook SetHookFunc getHook GetHookFunc } // ContextWithSetHook returns a copy of parent with hook configured to be // invoked every time ContextWithBaggage is called. // // Passing nil SetHookFunc creates a context with no set hook to call. func ContextWithSetHook(parent context.Context, hook SetHookFunc) context.Context { var s baggageState if v, ok := parent.Value(baggageKey).(baggageState); ok { s = v } s.setHook = hook return context.WithValue(parent, baggageKey, s) } // ContextWithGetHook returns a copy of parent with hook configured to be // invoked every time FromContext is called. // // Passing nil GetHookFunc creates a context with no get hook to call. func ContextWithGetHook(parent context.Context, hook GetHookFunc) context.Context { var s baggageState if v, ok := parent.Value(baggageKey).(baggageState); ok { s = v } s.getHook = hook return context.WithValue(parent, baggageKey, s) } // ContextWithList returns a copy of parent with baggage. Passing nil list // returns a context without any baggage. func ContextWithList(parent context.Context, list List) context.Context { var s baggageState if v, ok := parent.Value(baggageKey).(baggageState); ok { s = v } s.list = list ctx := context.WithValue(parent, baggageKey, s) if s.setHook != nil { ctx = s.setHook(ctx, list) } return ctx } // ListFromContext returns the baggage contained in ctx. func ListFromContext(ctx context.Context) List { switch v := ctx.Value(baggageKey).(type) { case baggageState: if v.getHook != nil { return v.getHook(ctx, v.list) } return v.list default: return nil } } ================================================ FILE: vendor/go.opentelemetry.io/otel/internal/gen.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package internal // import "go.opentelemetry.io/otel/internal" //go:generate gotmpl --body=./shared/matchers/expectation.go.tmpl "--data={}" --out=matchers/expectation.go //go:generate gotmpl --body=./shared/matchers/expecter.go.tmpl "--data={}" --out=matchers/expecter.go //go:generate gotmpl --body=./shared/matchers/temporal_matcher.go.tmpl "--data={}" --out=matchers/temporal_matcher.go //go:generate gotmpl --body=./shared/internaltest/alignment.go.tmpl "--data={}" --out=internaltest/alignment.go //go:generate gotmpl --body=./shared/internaltest/env.go.tmpl "--data={}" --out=internaltest/env.go //go:generate gotmpl --body=./shared/internaltest/env_test.go.tmpl "--data={}" --out=internaltest/env_test.go //go:generate gotmpl --body=./shared/internaltest/errors.go.tmpl "--data={}" --out=internaltest/errors.go //go:generate gotmpl --body=./shared/internaltest/harness.go.tmpl "--data={\"matchersImportPath\": \"go.opentelemetry.io/otel/internal/matchers\"}" --out=internaltest/harness.go //go:generate gotmpl --body=./shared/internaltest/text_map_carrier.go.tmpl "--data={}" --out=internaltest/text_map_carrier.go //go:generate gotmpl --body=./shared/internaltest/text_map_carrier_test.go.tmpl "--data={}" --out=internaltest/text_map_carrier_test.go //go:generate gotmpl --body=./shared/internaltest/text_map_propagator.go.tmpl "--data={}" --out=internaltest/text_map_propagator.go //go:generate gotmpl --body=./shared/internaltest/text_map_propagator_test.go.tmpl "--data={}" --out=internaltest/text_map_propagator_test.go ================================================ FILE: vendor/go.opentelemetry.io/otel/internal/global/handler.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package global // import "go.opentelemetry.io/otel/internal/global" import ( "log" "os" "sync/atomic" ) var ( // GlobalErrorHandler provides an ErrorHandler that can be used // throughout an OpenTelemetry instrumented project. When a user // specified ErrorHandler is registered (`SetErrorHandler`) all calls to // `Handle` and will be delegated to the registered ErrorHandler. GlobalErrorHandler = defaultErrorHandler() // Compile-time check that delegator implements ErrorHandler. _ ErrorHandler = (*ErrDelegator)(nil) // Compile-time check that errLogger implements ErrorHandler. _ ErrorHandler = (*ErrLogger)(nil) ) // ErrorHandler handles irremediable events. type ErrorHandler interface { // Handle handles any error deemed irremediable by an OpenTelemetry // component. Handle(error) } type ErrDelegator struct { delegate atomic.Pointer[ErrorHandler] } func (d *ErrDelegator) Handle(err error) { d.getDelegate().Handle(err) } func (d *ErrDelegator) getDelegate() ErrorHandler { return *d.delegate.Load() } // setDelegate sets the ErrorHandler delegate. func (d *ErrDelegator) setDelegate(eh ErrorHandler) { d.delegate.Store(&eh) } func defaultErrorHandler() *ErrDelegator { d := &ErrDelegator{} d.setDelegate(&ErrLogger{l: log.New(os.Stderr, "", log.LstdFlags)}) return d } // ErrLogger logs errors if no delegate is set, otherwise they are delegated. type ErrLogger struct { l *log.Logger } // Handle logs err if no delegate is set, otherwise it is delegated. func (h *ErrLogger) Handle(err error) { h.l.Print(err) } // GetErrorHandler returns the global ErrorHandler instance. // // The default ErrorHandler instance returned will log all errors to STDERR // until an override ErrorHandler is set with SetErrorHandler. All // ErrorHandler returned prior to this will automatically forward errors to // the set instance instead of logging. // // Subsequent calls to SetErrorHandler after the first will not forward errors // to the new ErrorHandler for prior returned instances. func GetErrorHandler() ErrorHandler { return GlobalErrorHandler } // SetErrorHandler sets the global ErrorHandler to h. // // The first time this is called all ErrorHandler previously returned from // GetErrorHandler will send errors to h instead of the default logging // ErrorHandler. Subsequent calls will set the global ErrorHandler, but not // delegate errors to h. func SetErrorHandler(h ErrorHandler) { GlobalErrorHandler.setDelegate(h) } // Handle is a convenience function for ErrorHandler().Handle(err). func Handle(err error) { GetErrorHandler().Handle(err) } ================================================ FILE: vendor/go.opentelemetry.io/otel/internal/global/instruments.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package global // import "go.opentelemetry.io/otel/internal/global" import ( "context" "sync/atomic" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/embedded" ) // unwrapper unwraps to return the underlying instrument implementation. type unwrapper interface { Unwrap() metric.Observable } type afCounter struct { embedded.Float64ObservableCounter metric.Float64Observable name string opts []metric.Float64ObservableCounterOption delegate atomic.Value //metric.Float64ObservableCounter } var _ unwrapper = (*afCounter)(nil) var _ metric.Float64ObservableCounter = (*afCounter)(nil) func (i *afCounter) setDelegate(m metric.Meter) { ctr, err := m.Float64ObservableCounter(i.name, i.opts...) if err != nil { GetErrorHandler().Handle(err) return } i.delegate.Store(ctr) } func (i *afCounter) Unwrap() metric.Observable { if ctr := i.delegate.Load(); ctr != nil { return ctr.(metric.Float64ObservableCounter) } return nil } type afUpDownCounter struct { embedded.Float64ObservableUpDownCounter metric.Float64Observable name string opts []metric.Float64ObservableUpDownCounterOption delegate atomic.Value //metric.Float64ObservableUpDownCounter } var _ unwrapper = (*afUpDownCounter)(nil) var _ metric.Float64ObservableUpDownCounter = (*afUpDownCounter)(nil) func (i *afUpDownCounter) setDelegate(m metric.Meter) { ctr, err := m.Float64ObservableUpDownCounter(i.name, i.opts...) if err != nil { GetErrorHandler().Handle(err) return } i.delegate.Store(ctr) } func (i *afUpDownCounter) Unwrap() metric.Observable { if ctr := i.delegate.Load(); ctr != nil { return ctr.(metric.Float64ObservableUpDownCounter) } return nil } type afGauge struct { embedded.Float64ObservableGauge metric.Float64Observable name string opts []metric.Float64ObservableGaugeOption delegate atomic.Value //metric.Float64ObservableGauge } var _ unwrapper = (*afGauge)(nil) var _ metric.Float64ObservableGauge = (*afGauge)(nil) func (i *afGauge) setDelegate(m metric.Meter) { ctr, err := m.Float64ObservableGauge(i.name, i.opts...) if err != nil { GetErrorHandler().Handle(err) return } i.delegate.Store(ctr) } func (i *afGauge) Unwrap() metric.Observable { if ctr := i.delegate.Load(); ctr != nil { return ctr.(metric.Float64ObservableGauge) } return nil } type aiCounter struct { embedded.Int64ObservableCounter metric.Int64Observable name string opts []metric.Int64ObservableCounterOption delegate atomic.Value //metric.Int64ObservableCounter } var _ unwrapper = (*aiCounter)(nil) var _ metric.Int64ObservableCounter = (*aiCounter)(nil) func (i *aiCounter) setDelegate(m metric.Meter) { ctr, err := m.Int64ObservableCounter(i.name, i.opts...) if err != nil { GetErrorHandler().Handle(err) return } i.delegate.Store(ctr) } func (i *aiCounter) Unwrap() metric.Observable { if ctr := i.delegate.Load(); ctr != nil { return ctr.(metric.Int64ObservableCounter) } return nil } type aiUpDownCounter struct { embedded.Int64ObservableUpDownCounter metric.Int64Observable name string opts []metric.Int64ObservableUpDownCounterOption delegate atomic.Value //metric.Int64ObservableUpDownCounter } var _ unwrapper = (*aiUpDownCounter)(nil) var _ metric.Int64ObservableUpDownCounter = (*aiUpDownCounter)(nil) func (i *aiUpDownCounter) setDelegate(m metric.Meter) { ctr, err := m.Int64ObservableUpDownCounter(i.name, i.opts...) if err != nil { GetErrorHandler().Handle(err) return } i.delegate.Store(ctr) } func (i *aiUpDownCounter) Unwrap() metric.Observable { if ctr := i.delegate.Load(); ctr != nil { return ctr.(metric.Int64ObservableUpDownCounter) } return nil } type aiGauge struct { embedded.Int64ObservableGauge metric.Int64Observable name string opts []metric.Int64ObservableGaugeOption delegate atomic.Value //metric.Int64ObservableGauge } var _ unwrapper = (*aiGauge)(nil) var _ metric.Int64ObservableGauge = (*aiGauge)(nil) func (i *aiGauge) setDelegate(m metric.Meter) { ctr, err := m.Int64ObservableGauge(i.name, i.opts...) if err != nil { GetErrorHandler().Handle(err) return } i.delegate.Store(ctr) } func (i *aiGauge) Unwrap() metric.Observable { if ctr := i.delegate.Load(); ctr != nil { return ctr.(metric.Int64ObservableGauge) } return nil } // Sync Instruments. type sfCounter struct { embedded.Float64Counter name string opts []metric.Float64CounterOption delegate atomic.Value //metric.Float64Counter } var _ metric.Float64Counter = (*sfCounter)(nil) func (i *sfCounter) setDelegate(m metric.Meter) { ctr, err := m.Float64Counter(i.name, i.opts...) if err != nil { GetErrorHandler().Handle(err) return } i.delegate.Store(ctr) } func (i *sfCounter) Add(ctx context.Context, incr float64, opts ...metric.AddOption) { if ctr := i.delegate.Load(); ctr != nil { ctr.(metric.Float64Counter).Add(ctx, incr, opts...) } } type sfUpDownCounter struct { embedded.Float64UpDownCounter name string opts []metric.Float64UpDownCounterOption delegate atomic.Value //metric.Float64UpDownCounter } var _ metric.Float64UpDownCounter = (*sfUpDownCounter)(nil) func (i *sfUpDownCounter) setDelegate(m metric.Meter) { ctr, err := m.Float64UpDownCounter(i.name, i.opts...) if err != nil { GetErrorHandler().Handle(err) return } i.delegate.Store(ctr) } func (i *sfUpDownCounter) Add(ctx context.Context, incr float64, opts ...metric.AddOption) { if ctr := i.delegate.Load(); ctr != nil { ctr.(metric.Float64UpDownCounter).Add(ctx, incr, opts...) } } type sfHistogram struct { embedded.Float64Histogram name string opts []metric.Float64HistogramOption delegate atomic.Value //metric.Float64Histogram } var _ metric.Float64Histogram = (*sfHistogram)(nil) func (i *sfHistogram) setDelegate(m metric.Meter) { ctr, err := m.Float64Histogram(i.name, i.opts...) if err != nil { GetErrorHandler().Handle(err) return } i.delegate.Store(ctr) } func (i *sfHistogram) Record(ctx context.Context, x float64, opts ...metric.RecordOption) { if ctr := i.delegate.Load(); ctr != nil { ctr.(metric.Float64Histogram).Record(ctx, x, opts...) } } type siCounter struct { embedded.Int64Counter name string opts []metric.Int64CounterOption delegate atomic.Value //metric.Int64Counter } var _ metric.Int64Counter = (*siCounter)(nil) func (i *siCounter) setDelegate(m metric.Meter) { ctr, err := m.Int64Counter(i.name, i.opts...) if err != nil { GetErrorHandler().Handle(err) return } i.delegate.Store(ctr) } func (i *siCounter) Add(ctx context.Context, x int64, opts ...metric.AddOption) { if ctr := i.delegate.Load(); ctr != nil { ctr.(metric.Int64Counter).Add(ctx, x, opts...) } } type siUpDownCounter struct { embedded.Int64UpDownCounter name string opts []metric.Int64UpDownCounterOption delegate atomic.Value //metric.Int64UpDownCounter } var _ metric.Int64UpDownCounter = (*siUpDownCounter)(nil) func (i *siUpDownCounter) setDelegate(m metric.Meter) { ctr, err := m.Int64UpDownCounter(i.name, i.opts...) if err != nil { GetErrorHandler().Handle(err) return } i.delegate.Store(ctr) } func (i *siUpDownCounter) Add(ctx context.Context, x int64, opts ...metric.AddOption) { if ctr := i.delegate.Load(); ctr != nil { ctr.(metric.Int64UpDownCounter).Add(ctx, x, opts...) } } type siHistogram struct { embedded.Int64Histogram name string opts []metric.Int64HistogramOption delegate atomic.Value //metric.Int64Histogram } var _ metric.Int64Histogram = (*siHistogram)(nil) func (i *siHistogram) setDelegate(m metric.Meter) { ctr, err := m.Int64Histogram(i.name, i.opts...) if err != nil { GetErrorHandler().Handle(err) return } i.delegate.Store(ctr) } func (i *siHistogram) Record(ctx context.Context, x int64, opts ...metric.RecordOption) { if ctr := i.delegate.Load(); ctr != nil { ctr.(metric.Int64Histogram).Record(ctx, x, opts...) } } ================================================ FILE: vendor/go.opentelemetry.io/otel/internal/global/internal_logging.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package global // import "go.opentelemetry.io/otel/internal/global" import ( "log" "os" "sync/atomic" "github.com/go-logr/logr" "github.com/go-logr/stdr" ) // globalLogger is the logging interface used within the otel api and sdk provide details of the internals. // // The default logger uses stdr which is backed by the standard `log.Logger` // interface. This logger will only show messages at the Error Level. var globalLogger atomic.Pointer[logr.Logger] func init() { SetLogger(stdr.New(log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile))) } // SetLogger overrides the globalLogger with l. // // To see Warn messages use a logger with `l.V(1).Enabled() == true` // To see Info messages use a logger with `l.V(4).Enabled() == true` // To see Debug messages use a logger with `l.V(8).Enabled() == true`. func SetLogger(l logr.Logger) { globalLogger.Store(&l) } func getLogger() logr.Logger { return *globalLogger.Load() } // Info prints messages about the general state of the API or SDK. // This should usually be less than 5 messages a minute. func Info(msg string, keysAndValues ...interface{}) { getLogger().V(4).Info(msg, keysAndValues...) } // Error prints messages about exceptional states of the API or SDK. func Error(err error, msg string, keysAndValues ...interface{}) { getLogger().Error(err, msg, keysAndValues...) } // Debug prints messages about all internal changes in the API or SDK. func Debug(msg string, keysAndValues ...interface{}) { getLogger().V(8).Info(msg, keysAndValues...) } // Warn prints messages about warnings in the API or SDK. // Not an error but is likely more important than an informational event. func Warn(msg string, keysAndValues ...interface{}) { getLogger().V(1).Info(msg, keysAndValues...) } ================================================ FILE: vendor/go.opentelemetry.io/otel/internal/global/meter.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package global // import "go.opentelemetry.io/otel/internal/global" import ( "container/list" "sync" "sync/atomic" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/metric/embedded" ) // meterProvider is a placeholder for a configured SDK MeterProvider. // // All MeterProvider functionality is forwarded to a delegate once // configured. type meterProvider struct { embedded.MeterProvider mtx sync.Mutex meters map[il]*meter delegate metric.MeterProvider } // setDelegate configures p to delegate all MeterProvider functionality to // provider. // // All Meters provided prior to this function call are switched out to be // Meters provided by provider. All instruments and callbacks are recreated and // delegated. // // It is guaranteed by the caller that this happens only once. func (p *meterProvider) setDelegate(provider metric.MeterProvider) { p.mtx.Lock() defer p.mtx.Unlock() p.delegate = provider if len(p.meters) == 0 { return } for _, meter := range p.meters { meter.setDelegate(provider) } p.meters = nil } // Meter implements MeterProvider. func (p *meterProvider) Meter(name string, opts ...metric.MeterOption) metric.Meter { p.mtx.Lock() defer p.mtx.Unlock() if p.delegate != nil { return p.delegate.Meter(name, opts...) } // At this moment it is guaranteed that no sdk is installed, save the meter in the meters map. c := metric.NewMeterConfig(opts...) key := il{ name: name, version: c.InstrumentationVersion(), } if p.meters == nil { p.meters = make(map[il]*meter) } if val, ok := p.meters[key]; ok { return val } t := &meter{name: name, opts: opts} p.meters[key] = t return t } // meter is a placeholder for a metric.Meter. // // All Meter functionality is forwarded to a delegate once configured. // Otherwise, all functionality is forwarded to a NoopMeter. type meter struct { embedded.Meter name string opts []metric.MeterOption mtx sync.Mutex instruments []delegatedInstrument registry list.List delegate atomic.Value // metric.Meter } type delegatedInstrument interface { setDelegate(metric.Meter) } // setDelegate configures m to delegate all Meter functionality to Meters // created by provider. // // All subsequent calls to the Meter methods will be passed to the delegate. // // It is guaranteed by the caller that this happens only once. func (m *meter) setDelegate(provider metric.MeterProvider) { meter := provider.Meter(m.name, m.opts...) m.delegate.Store(meter) m.mtx.Lock() defer m.mtx.Unlock() for _, inst := range m.instruments { inst.setDelegate(meter) } for e := m.registry.Front(); e != nil; e = e.Next() { r := e.Value.(*registration) r.setDelegate(meter) m.registry.Remove(e) } m.instruments = nil m.registry.Init() } func (m *meter) Int64Counter(name string, options ...metric.Int64CounterOption) (metric.Int64Counter, error) { if del, ok := m.delegate.Load().(metric.Meter); ok { return del.Int64Counter(name, options...) } m.mtx.Lock() defer m.mtx.Unlock() i := &siCounter{name: name, opts: options} m.instruments = append(m.instruments, i) return i, nil } func (m *meter) Int64UpDownCounter(name string, options ...metric.Int64UpDownCounterOption) (metric.Int64UpDownCounter, error) { if del, ok := m.delegate.Load().(metric.Meter); ok { return del.Int64UpDownCounter(name, options...) } m.mtx.Lock() defer m.mtx.Unlock() i := &siUpDownCounter{name: name, opts: options} m.instruments = append(m.instruments, i) return i, nil } func (m *meter) Int64Histogram(name string, options ...metric.Int64HistogramOption) (metric.Int64Histogram, error) { if del, ok := m.delegate.Load().(metric.Meter); ok { return del.Int64Histogram(name, options...) } m.mtx.Lock() defer m.mtx.Unlock() i := &siHistogram{name: name, opts: options} m.instruments = append(m.instruments, i) return i, nil } func (m *meter) Int64ObservableCounter(name string, options ...metric.Int64ObservableCounterOption) (metric.Int64ObservableCounter, error) { if del, ok := m.delegate.Load().(metric.Meter); ok { return del.Int64ObservableCounter(name, options...) } m.mtx.Lock() defer m.mtx.Unlock() i := &aiCounter{name: name, opts: options} m.instruments = append(m.instruments, i) return i, nil } func (m *meter) Int64ObservableUpDownCounter(name string, options ...metric.Int64ObservableUpDownCounterOption) (metric.Int64ObservableUpDownCounter, error) { if del, ok := m.delegate.Load().(metric.Meter); ok { return del.Int64ObservableUpDownCounter(name, options...) } m.mtx.Lock() defer m.mtx.Unlock() i := &aiUpDownCounter{name: name, opts: options} m.instruments = append(m.instruments, i) return i, nil } func (m *meter) Int64ObservableGauge(name string, options ...metric.Int64ObservableGaugeOption) (metric.Int64ObservableGauge, error) { if del, ok := m.delegate.Load().(metric.Meter); ok { return del.Int64ObservableGauge(name, options...) } m.mtx.Lock() defer m.mtx.Unlock() i := &aiGauge{name: name, opts: options} m.instruments = append(m.instruments, i) return i, nil } func (m *meter) Float64Counter(name string, options ...metric.Float64CounterOption) (metric.Float64Counter, error) { if del, ok := m.delegate.Load().(metric.Meter); ok { return del.Float64Counter(name, options...) } m.mtx.Lock() defer m.mtx.Unlock() i := &sfCounter{name: name, opts: options} m.instruments = append(m.instruments, i) return i, nil } func (m *meter) Float64UpDownCounter(name string, options ...metric.Float64UpDownCounterOption) (metric.Float64UpDownCounter, error) { if del, ok := m.delegate.Load().(metric.Meter); ok { return del.Float64UpDownCounter(name, options...) } m.mtx.Lock() defer m.mtx.Unlock() i := &sfUpDownCounter{name: name, opts: options} m.instruments = append(m.instruments, i) return i, nil } func (m *meter) Float64Histogram(name string, options ...metric.Float64HistogramOption) (metric.Float64Histogram, error) { if del, ok := m.delegate.Load().(metric.Meter); ok { return del.Float64Histogram(name, options...) } m.mtx.Lock() defer m.mtx.Unlock() i := &sfHistogram{name: name, opts: options} m.instruments = append(m.instruments, i) return i, nil } func (m *meter) Float64ObservableCounter(name string, options ...metric.Float64ObservableCounterOption) (metric.Float64ObservableCounter, error) { if del, ok := m.delegate.Load().(metric.Meter); ok { return del.Float64ObservableCounter(name, options...) } m.mtx.Lock() defer m.mtx.Unlock() i := &afCounter{name: name, opts: options} m.instruments = append(m.instruments, i) return i, nil } func (m *meter) Float64ObservableUpDownCounter(name string, options ...metric.Float64ObservableUpDownCounterOption) (metric.Float64ObservableUpDownCounter, error) { if del, ok := m.delegate.Load().(metric.Meter); ok { return del.Float64ObservableUpDownCounter(name, options...) } m.mtx.Lock() defer m.mtx.Unlock() i := &afUpDownCounter{name: name, opts: options} m.instruments = append(m.instruments, i) return i, nil } func (m *meter) Float64ObservableGauge(name string, options ...metric.Float64ObservableGaugeOption) (metric.Float64ObservableGauge, error) { if del, ok := m.delegate.Load().(metric.Meter); ok { return del.Float64ObservableGauge(name, options...) } m.mtx.Lock() defer m.mtx.Unlock() i := &afGauge{name: name, opts: options} m.instruments = append(m.instruments, i) return i, nil } // RegisterCallback captures the function that will be called during Collect. func (m *meter) RegisterCallback(f metric.Callback, insts ...metric.Observable) (metric.Registration, error) { if del, ok := m.delegate.Load().(metric.Meter); ok { insts = unwrapInstruments(insts) return del.RegisterCallback(f, insts...) } m.mtx.Lock() defer m.mtx.Unlock() reg := ®istration{instruments: insts, function: f} e := m.registry.PushBack(reg) reg.unreg = func() error { m.mtx.Lock() _ = m.registry.Remove(e) m.mtx.Unlock() return nil } return reg, nil } type wrapped interface { unwrap() metric.Observable } func unwrapInstruments(instruments []metric.Observable) []metric.Observable { out := make([]metric.Observable, 0, len(instruments)) for _, inst := range instruments { if in, ok := inst.(wrapped); ok { out = append(out, in.unwrap()) } else { out = append(out, inst) } } return out } type registration struct { embedded.Registration instruments []metric.Observable function metric.Callback unreg func() error unregMu sync.Mutex } func (c *registration) setDelegate(m metric.Meter) { insts := unwrapInstruments(c.instruments) c.unregMu.Lock() defer c.unregMu.Unlock() if c.unreg == nil { // Unregister already called. return } reg, err := m.RegisterCallback(c.function, insts...) if err != nil { GetErrorHandler().Handle(err) } c.unreg = reg.Unregister } func (c *registration) Unregister() error { c.unregMu.Lock() defer c.unregMu.Unlock() if c.unreg == nil { // Unregister already called. return nil } var err error err, c.unreg = c.unreg(), nil return err } ================================================ FILE: vendor/go.opentelemetry.io/otel/internal/global/propagator.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package global // import "go.opentelemetry.io/otel/internal/global" import ( "context" "sync" "go.opentelemetry.io/otel/propagation" ) // textMapPropagator is a default TextMapPropagator that delegates calls to a // registered delegate if one is set, otherwise it defaults to delegating the // calls to a the default no-op propagation.TextMapPropagator. type textMapPropagator struct { mtx sync.Mutex once sync.Once delegate propagation.TextMapPropagator noop propagation.TextMapPropagator } // Compile-time guarantee that textMapPropagator implements the // propagation.TextMapPropagator interface. var _ propagation.TextMapPropagator = (*textMapPropagator)(nil) func newTextMapPropagator() *textMapPropagator { return &textMapPropagator{ noop: propagation.NewCompositeTextMapPropagator(), } } // SetDelegate sets a delegate propagation.TextMapPropagator that all calls are // forwarded to. Delegation can only be performed once, all subsequent calls // perform no delegation. func (p *textMapPropagator) SetDelegate(delegate propagation.TextMapPropagator) { if delegate == nil { return } p.mtx.Lock() p.once.Do(func() { p.delegate = delegate }) p.mtx.Unlock() } // effectiveDelegate returns the current delegate of p if one is set, // otherwise the default noop TextMapPropagator is returned. This method // can be called concurrently. func (p *textMapPropagator) effectiveDelegate() propagation.TextMapPropagator { p.mtx.Lock() defer p.mtx.Unlock() if p.delegate != nil { return p.delegate } return p.noop } // Inject set cross-cutting concerns from the Context into the carrier. func (p *textMapPropagator) Inject(ctx context.Context, carrier propagation.TextMapCarrier) { p.effectiveDelegate().Inject(ctx, carrier) } // Extract reads cross-cutting concerns from the carrier into a Context. func (p *textMapPropagator) Extract(ctx context.Context, carrier propagation.TextMapCarrier) context.Context { return p.effectiveDelegate().Extract(ctx, carrier) } // Fields returns the keys whose values are set with Inject. func (p *textMapPropagator) Fields() []string { return p.effectiveDelegate().Fields() } ================================================ FILE: vendor/go.opentelemetry.io/otel/internal/global/state.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package global // import "go.opentelemetry.io/otel/internal/global" import ( "errors" "sync" "sync/atomic" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/trace" ) type ( tracerProviderHolder struct { tp trace.TracerProvider } propagatorsHolder struct { tm propagation.TextMapPropagator } meterProviderHolder struct { mp metric.MeterProvider } ) var ( globalTracer = defaultTracerValue() globalPropagators = defaultPropagatorsValue() globalMeterProvider = defaultMeterProvider() delegateTraceOnce sync.Once delegateTextMapPropagatorOnce sync.Once delegateMeterOnce sync.Once ) // TracerProvider is the internal implementation for global.TracerProvider. func TracerProvider() trace.TracerProvider { return globalTracer.Load().(tracerProviderHolder).tp } // SetTracerProvider is the internal implementation for global.SetTracerProvider. func SetTracerProvider(tp trace.TracerProvider) { current := TracerProvider() if _, cOk := current.(*tracerProvider); cOk { if _, tpOk := tp.(*tracerProvider); tpOk && current == tp { // Do not assign the default delegating TracerProvider to delegate // to itself. Error( errors.New("no delegate configured in tracer provider"), "Setting tracer provider to it's current value. No delegate will be configured", ) return } } delegateTraceOnce.Do(func() { if def, ok := current.(*tracerProvider); ok { def.setDelegate(tp) } }) globalTracer.Store(tracerProviderHolder{tp: tp}) } // TextMapPropagator is the internal implementation for global.TextMapPropagator. func TextMapPropagator() propagation.TextMapPropagator { return globalPropagators.Load().(propagatorsHolder).tm } // SetTextMapPropagator is the internal implementation for global.SetTextMapPropagator. func SetTextMapPropagator(p propagation.TextMapPropagator) { current := TextMapPropagator() if _, cOk := current.(*textMapPropagator); cOk { if _, pOk := p.(*textMapPropagator); pOk && current == p { // Do not assign the default delegating TextMapPropagator to // delegate to itself. Error( errors.New("no delegate configured in text map propagator"), "Setting text map propagator to it's current value. No delegate will be configured", ) return } } // For the textMapPropagator already returned by TextMapPropagator // delegate to p. delegateTextMapPropagatorOnce.Do(func() { if def, ok := current.(*textMapPropagator); ok { def.SetDelegate(p) } }) // Return p when subsequent calls to TextMapPropagator are made. globalPropagators.Store(propagatorsHolder{tm: p}) } // MeterProvider is the internal implementation for global.MeterProvider. func MeterProvider() metric.MeterProvider { return globalMeterProvider.Load().(meterProviderHolder).mp } // SetMeterProvider is the internal implementation for global.SetMeterProvider. func SetMeterProvider(mp metric.MeterProvider) { current := MeterProvider() if _, cOk := current.(*meterProvider); cOk { if _, mpOk := mp.(*meterProvider); mpOk && current == mp { // Do not assign the default delegating MeterProvider to delegate // to itself. Error( errors.New("no delegate configured in meter provider"), "Setting meter provider to it's current value. No delegate will be configured", ) return } } delegateMeterOnce.Do(func() { if def, ok := current.(*meterProvider); ok { def.setDelegate(mp) } }) globalMeterProvider.Store(meterProviderHolder{mp: mp}) } func defaultTracerValue() *atomic.Value { v := &atomic.Value{} v.Store(tracerProviderHolder{tp: &tracerProvider{}}) return v } func defaultPropagatorsValue() *atomic.Value { v := &atomic.Value{} v.Store(propagatorsHolder{tm: newTextMapPropagator()}) return v } func defaultMeterProvider() *atomic.Value { v := &atomic.Value{} v.Store(meterProviderHolder{mp: &meterProvider{}}) return v } ================================================ FILE: vendor/go.opentelemetry.io/otel/internal/global/trace.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package global // import "go.opentelemetry.io/otel/internal/global" /* This file contains the forwarding implementation of the TracerProvider used as the default global instance. Prior to initialization of an SDK, Tracers returned by the global TracerProvider will provide no-op functionality. This means that all Span created prior to initialization are no-op Spans. Once an SDK has been initialized, all provided no-op Tracers are swapped for Tracers provided by the SDK defined TracerProvider. However, any Span started prior to this initialization does not change its behavior. Meaning, the Span remains a no-op Span. The implementation to track and swap Tracers locks all new Tracer creation until the swap is complete. This assumes that this operation is not performance-critical. If that assumption is incorrect, be sure to configure an SDK prior to any Tracer creation. */ import ( "context" "sync" "sync/atomic" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" ) // tracerProvider is a placeholder for a configured SDK TracerProvider. // // All TracerProvider functionality is forwarded to a delegate once // configured. type tracerProvider struct { mtx sync.Mutex tracers map[il]*tracer delegate trace.TracerProvider } // Compile-time guarantee that tracerProvider implements the TracerProvider // interface. var _ trace.TracerProvider = &tracerProvider{} // setDelegate configures p to delegate all TracerProvider functionality to // provider. // // All Tracers provided prior to this function call are switched out to be // Tracers provided by provider. // // It is guaranteed by the caller that this happens only once. func (p *tracerProvider) setDelegate(provider trace.TracerProvider) { p.mtx.Lock() defer p.mtx.Unlock() p.delegate = provider if len(p.tracers) == 0 { return } for _, t := range p.tracers { t.setDelegate(provider) } p.tracers = nil } // Tracer implements TracerProvider. func (p *tracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.Tracer { p.mtx.Lock() defer p.mtx.Unlock() if p.delegate != nil { return p.delegate.Tracer(name, opts...) } // At this moment it is guaranteed that no sdk is installed, save the tracer in the tracers map. c := trace.NewTracerConfig(opts...) key := il{ name: name, version: c.InstrumentationVersion(), } if p.tracers == nil { p.tracers = make(map[il]*tracer) } if val, ok := p.tracers[key]; ok { return val } t := &tracer{name: name, opts: opts, provider: p} p.tracers[key] = t return t } type il struct { name string version string } // tracer is a placeholder for a trace.Tracer. // // All Tracer functionality is forwarded to a delegate once configured. // Otherwise, all functionality is forwarded to a NoopTracer. type tracer struct { name string opts []trace.TracerOption provider *tracerProvider delegate atomic.Value } // Compile-time guarantee that tracer implements the trace.Tracer interface. var _ trace.Tracer = &tracer{} // setDelegate configures t to delegate all Tracer functionality to Tracers // created by provider. // // All subsequent calls to the Tracer methods will be passed to the delegate. // // It is guaranteed by the caller that this happens only once. func (t *tracer) setDelegate(provider trace.TracerProvider) { t.delegate.Store(provider.Tracer(t.name, t.opts...)) } // Start implements trace.Tracer by forwarding the call to t.delegate if // set, otherwise it forwards the call to a NoopTracer. func (t *tracer) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) { delegate := t.delegate.Load() if delegate != nil { return delegate.(trace.Tracer).Start(ctx, name, opts...) } s := nonRecordingSpan{sc: trace.SpanContextFromContext(ctx), tracer: t} ctx = trace.ContextWithSpan(ctx, s) return ctx, s } // nonRecordingSpan is a minimal implementation of a Span that wraps a // SpanContext. It performs no operations other than to return the wrapped // SpanContext. type nonRecordingSpan struct { sc trace.SpanContext tracer *tracer } var _ trace.Span = nonRecordingSpan{} // SpanContext returns the wrapped SpanContext. func (s nonRecordingSpan) SpanContext() trace.SpanContext { return s.sc } // IsRecording always returns false. func (nonRecordingSpan) IsRecording() bool { return false } // SetStatus does nothing. func (nonRecordingSpan) SetStatus(codes.Code, string) {} // SetError does nothing. func (nonRecordingSpan) SetError(bool) {} // SetAttributes does nothing. func (nonRecordingSpan) SetAttributes(...attribute.KeyValue) {} // End does nothing. func (nonRecordingSpan) End(...trace.SpanEndOption) {} // RecordError does nothing. func (nonRecordingSpan) RecordError(error, ...trace.EventOption) {} // AddEvent does nothing. func (nonRecordingSpan) AddEvent(string, ...trace.EventOption) {} // SetName does nothing. func (nonRecordingSpan) SetName(string) {} func (s nonRecordingSpan) TracerProvider() trace.TracerProvider { return s.tracer.provider } ================================================ FILE: vendor/go.opentelemetry.io/otel/internal/rawhelpers.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package internal // import "go.opentelemetry.io/otel/internal" import ( "math" "unsafe" ) func BoolToRaw(b bool) uint64 { // nolint:revive // b is not a control flag. if b { return 1 } return 0 } func RawToBool(r uint64) bool { return r != 0 } func Int64ToRaw(i int64) uint64 { return uint64(i) } func RawToInt64(r uint64) int64 { return int64(r) } func Float64ToRaw(f float64) uint64 { return math.Float64bits(f) } func RawToFloat64(r uint64) float64 { return math.Float64frombits(r) } func RawPtrToFloat64Ptr(r *uint64) *float64 { return (*float64)(unsafe.Pointer(r)) } func RawPtrToInt64Ptr(r *uint64) *int64 { return (*int64)(unsafe.Pointer(r)) } ================================================ FILE: vendor/go.opentelemetry.io/otel/internal_logging.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package otel // import "go.opentelemetry.io/otel" import ( "github.com/go-logr/logr" "go.opentelemetry.io/otel/internal/global" ) // SetLogger configures the logger used internally to opentelemetry. func SetLogger(logger logr.Logger) { global.SetLogger(logger) } ================================================ FILE: vendor/go.opentelemetry.io/otel/metric/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/go.opentelemetry.io/otel/metric/asyncfloat64.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package metric // import "go.opentelemetry.io/otel/metric" import ( "context" "go.opentelemetry.io/otel/metric/embedded" ) // Float64Observable describes a set of instruments used asynchronously to // record float64 measurements once per collection cycle. Observations of // these instruments are only made within a callback. // // Warning: Methods may be added to this interface in minor releases. type Float64Observable interface { Observable float64Observable() } // Float64ObservableCounter is an instrument used to asynchronously record // increasing float64 measurements once per collection cycle. Observations are // only made within a callback for this instrument. The value observed is // assumed the to be the cumulative sum of the count. // // Warning: Methods may be added to this interface in minor releases. See // package documentation on API implementation for information on how to set // default behavior for // unimplemented methods. type Float64ObservableCounter interface { // Users of the interface can ignore this. This embedded type is only used // by implementations of this interface. See the "API Implementations" // section of the package documentation for more information. embedded.Float64ObservableCounter Float64Observable } // Float64ObservableCounterConfig contains options for asynchronous counter // instruments that record int64 values. type Float64ObservableCounterConfig struct { description string unit string callbacks []Float64Callback } // NewFloat64ObservableCounterConfig returns a new // [Float64ObservableCounterConfig] with all opts applied. func NewFloat64ObservableCounterConfig(opts ...Float64ObservableCounterOption) Float64ObservableCounterConfig { var config Float64ObservableCounterConfig for _, o := range opts { config = o.applyFloat64ObservableCounter(config) } return config } // Description returns the configured description. func (c Float64ObservableCounterConfig) Description() string { return c.description } // Unit returns the configured unit. func (c Float64ObservableCounterConfig) Unit() string { return c.unit } // Callbacks returns the configured callbacks. func (c Float64ObservableCounterConfig) Callbacks() []Float64Callback { return c.callbacks } // Float64ObservableCounterOption applies options to a // [Float64ObservableCounterConfig]. See [Float64ObservableOption] and // [InstrumentOption] for other options that can be used as a // Float64ObservableCounterOption. type Float64ObservableCounterOption interface { applyFloat64ObservableCounter(Float64ObservableCounterConfig) Float64ObservableCounterConfig } // Float64ObservableUpDownCounter is an instrument used to asynchronously // record float64 measurements once per collection cycle. Observations are only // made within a callback for this instrument. The value observed is assumed // the to be the cumulative sum of the count. // // Warning: Methods may be added to this interface in minor releases. See // package documentation on API implementation for information on how to set // default behavior for unimplemented methods. type Float64ObservableUpDownCounter interface { // Users of the interface can ignore this. This embedded type is only used // by implementations of this interface. See the "API Implementations" // section of the package documentation for more information. embedded.Float64ObservableUpDownCounter Float64Observable } // Float64ObservableUpDownCounterConfig contains options for asynchronous // counter instruments that record int64 values. type Float64ObservableUpDownCounterConfig struct { description string unit string callbacks []Float64Callback } // NewFloat64ObservableUpDownCounterConfig returns a new // [Float64ObservableUpDownCounterConfig] with all opts applied. func NewFloat64ObservableUpDownCounterConfig(opts ...Float64ObservableUpDownCounterOption) Float64ObservableUpDownCounterConfig { var config Float64ObservableUpDownCounterConfig for _, o := range opts { config = o.applyFloat64ObservableUpDownCounter(config) } return config } // Description returns the configured description. func (c Float64ObservableUpDownCounterConfig) Description() string { return c.description } // Unit returns the configured unit. func (c Float64ObservableUpDownCounterConfig) Unit() string { return c.unit } // Callbacks returns the configured callbacks. func (c Float64ObservableUpDownCounterConfig) Callbacks() []Float64Callback { return c.callbacks } // Float64ObservableUpDownCounterOption applies options to a // [Float64ObservableUpDownCounterConfig]. See [Float64ObservableOption] and // [InstrumentOption] for other options that can be used as a // Float64ObservableUpDownCounterOption. type Float64ObservableUpDownCounterOption interface { applyFloat64ObservableUpDownCounter(Float64ObservableUpDownCounterConfig) Float64ObservableUpDownCounterConfig } // Float64ObservableGauge is an instrument used to asynchronously record // instantaneous float64 measurements once per collection cycle. Observations // are only made within a callback for this instrument. // // Warning: Methods may be added to this interface in minor releases. See // package documentation on API implementation for information on how to set // default behavior for unimplemented methods. type Float64ObservableGauge interface { // Users of the interface can ignore this. This embedded type is only used // by implementations of this interface. See the "API Implementations" // section of the package documentation for more information. embedded.Float64ObservableGauge Float64Observable } // Float64ObservableGaugeConfig contains options for asynchronous counter // instruments that record int64 values. type Float64ObservableGaugeConfig struct { description string unit string callbacks []Float64Callback } // NewFloat64ObservableGaugeConfig returns a new [Float64ObservableGaugeConfig] // with all opts applied. func NewFloat64ObservableGaugeConfig(opts ...Float64ObservableGaugeOption) Float64ObservableGaugeConfig { var config Float64ObservableGaugeConfig for _, o := range opts { config = o.applyFloat64ObservableGauge(config) } return config } // Description returns the configured description. func (c Float64ObservableGaugeConfig) Description() string { return c.description } // Unit returns the configured unit. func (c Float64ObservableGaugeConfig) Unit() string { return c.unit } // Callbacks returns the configured callbacks. func (c Float64ObservableGaugeConfig) Callbacks() []Float64Callback { return c.callbacks } // Float64ObservableGaugeOption applies options to a // [Float64ObservableGaugeConfig]. See [Float64ObservableOption] and // [InstrumentOption] for other options that can be used as a // Float64ObservableGaugeOption. type Float64ObservableGaugeOption interface { applyFloat64ObservableGauge(Float64ObservableGaugeConfig) Float64ObservableGaugeConfig } // Float64Observer is a recorder of float64 measurements. // // Warning: Methods may be added to this interface in minor releases. See // package documentation on API implementation for information on how to set // default behavior for unimplemented methods. type Float64Observer interface { // Users of the interface can ignore this. This embedded type is only used // by implementations of this interface. See the "API Implementations" // section of the package documentation for more information. embedded.Float64Observer // Observe records the float64 value. // // Use the WithAttributeSet (or, if performance is not a concern, // the WithAttributes) option to include measurement attributes. Observe(value float64, options ...ObserveOption) } // Float64Callback is a function registered with a Meter that makes // observations for a Float64Observerable instrument it is registered with. // Calls to the Float64Observer record measurement values for the // Float64Observable. // // The function needs to complete in a finite amount of time and the deadline // of the passed context is expected to be honored. // // The function needs to make unique observations across all registered // Float64Callbacks. Meaning, it should not report measurements with the same // attributes as another Float64Callbacks also registered for the same // instrument. // // The function needs to be concurrent safe. type Float64Callback func(context.Context, Float64Observer) error // Float64ObservableOption applies options to float64 Observer instruments. type Float64ObservableOption interface { Float64ObservableCounterOption Float64ObservableUpDownCounterOption Float64ObservableGaugeOption } type float64CallbackOpt struct { cback Float64Callback } func (o float64CallbackOpt) applyFloat64ObservableCounter(cfg Float64ObservableCounterConfig) Float64ObservableCounterConfig { cfg.callbacks = append(cfg.callbacks, o.cback) return cfg } func (o float64CallbackOpt) applyFloat64ObservableUpDownCounter(cfg Float64ObservableUpDownCounterConfig) Float64ObservableUpDownCounterConfig { cfg.callbacks = append(cfg.callbacks, o.cback) return cfg } func (o float64CallbackOpt) applyFloat64ObservableGauge(cfg Float64ObservableGaugeConfig) Float64ObservableGaugeConfig { cfg.callbacks = append(cfg.callbacks, o.cback) return cfg } // WithFloat64Callback adds callback to be called for an instrument. func WithFloat64Callback(callback Float64Callback) Float64ObservableOption { return float64CallbackOpt{callback} } ================================================ FILE: vendor/go.opentelemetry.io/otel/metric/asyncint64.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package metric // import "go.opentelemetry.io/otel/metric" import ( "context" "go.opentelemetry.io/otel/metric/embedded" ) // Int64Observable describes a set of instruments used asynchronously to record // int64 measurements once per collection cycle. Observations of these // instruments are only made within a callback. // // Warning: Methods may be added to this interface in minor releases. type Int64Observable interface { Observable int64Observable() } // Int64ObservableCounter is an instrument used to asynchronously record // increasing int64 measurements once per collection cycle. Observations are // only made within a callback for this instrument. The value observed is // assumed the to be the cumulative sum of the count. // // Warning: Methods may be added to this interface in minor releases. See // package documentation on API implementation for information on how to set // default behavior for unimplemented methods. type Int64ObservableCounter interface { // Users of the interface can ignore this. This embedded type is only used // by implementations of this interface. See the "API Implementations" // section of the package documentation for more information. embedded.Int64ObservableCounter Int64Observable } // Int64ObservableCounterConfig contains options for asynchronous counter // instruments that record int64 values. type Int64ObservableCounterConfig struct { description string unit string callbacks []Int64Callback } // NewInt64ObservableCounterConfig returns a new [Int64ObservableCounterConfig] // with all opts applied. func NewInt64ObservableCounterConfig(opts ...Int64ObservableCounterOption) Int64ObservableCounterConfig { var config Int64ObservableCounterConfig for _, o := range opts { config = o.applyInt64ObservableCounter(config) } return config } // Description returns the configured description. func (c Int64ObservableCounterConfig) Description() string { return c.description } // Unit returns the configured unit. func (c Int64ObservableCounterConfig) Unit() string { return c.unit } // Callbacks returns the configured callbacks. func (c Int64ObservableCounterConfig) Callbacks() []Int64Callback { return c.callbacks } // Int64ObservableCounterOption applies options to a // [Int64ObservableCounterConfig]. See [Int64ObservableOption] and // [InstrumentOption] for other options that can be used as an // Int64ObservableCounterOption. type Int64ObservableCounterOption interface { applyInt64ObservableCounter(Int64ObservableCounterConfig) Int64ObservableCounterConfig } // Int64ObservableUpDownCounter is an instrument used to asynchronously record // int64 measurements once per collection cycle. Observations are only made // within a callback for this instrument. The value observed is assumed the to // be the cumulative sum of the count. // // Warning: Methods may be added to this interface in minor releases. See // package documentation on API implementation for information on how to set // default behavior for unimplemented methods. type Int64ObservableUpDownCounter interface { // Users of the interface can ignore this. This embedded type is only used // by implementations of this interface. See the "API Implementations" // section of the package documentation for more information. embedded.Int64ObservableUpDownCounter Int64Observable } // Int64ObservableUpDownCounterConfig contains options for asynchronous counter // instruments that record int64 values. type Int64ObservableUpDownCounterConfig struct { description string unit string callbacks []Int64Callback } // NewInt64ObservableUpDownCounterConfig returns a new // [Int64ObservableUpDownCounterConfig] with all opts applied. func NewInt64ObservableUpDownCounterConfig(opts ...Int64ObservableUpDownCounterOption) Int64ObservableUpDownCounterConfig { var config Int64ObservableUpDownCounterConfig for _, o := range opts { config = o.applyInt64ObservableUpDownCounter(config) } return config } // Description returns the configured description. func (c Int64ObservableUpDownCounterConfig) Description() string { return c.description } // Unit returns the configured unit. func (c Int64ObservableUpDownCounterConfig) Unit() string { return c.unit } // Callbacks returns the configured callbacks. func (c Int64ObservableUpDownCounterConfig) Callbacks() []Int64Callback { return c.callbacks } // Int64ObservableUpDownCounterOption applies options to a // [Int64ObservableUpDownCounterConfig]. See [Int64ObservableOption] and // [InstrumentOption] for other options that can be used as an // Int64ObservableUpDownCounterOption. type Int64ObservableUpDownCounterOption interface { applyInt64ObservableUpDownCounter(Int64ObservableUpDownCounterConfig) Int64ObservableUpDownCounterConfig } // Int64ObservableGauge is an instrument used to asynchronously record // instantaneous int64 measurements once per collection cycle. Observations are // only made within a callback for this instrument. // // Warning: Methods may be added to this interface in minor releases. See // package documentation on API implementation for information on how to set // default behavior for unimplemented methods. type Int64ObservableGauge interface { // Users of the interface can ignore this. This embedded type is only used // by implementations of this interface. See the "API Implementations" // section of the package documentation for more information. embedded.Int64ObservableGauge Int64Observable } // Int64ObservableGaugeConfig contains options for asynchronous counter // instruments that record int64 values. type Int64ObservableGaugeConfig struct { description string unit string callbacks []Int64Callback } // NewInt64ObservableGaugeConfig returns a new [Int64ObservableGaugeConfig] // with all opts applied. func NewInt64ObservableGaugeConfig(opts ...Int64ObservableGaugeOption) Int64ObservableGaugeConfig { var config Int64ObservableGaugeConfig for _, o := range opts { config = o.applyInt64ObservableGauge(config) } return config } // Description returns the configured description. func (c Int64ObservableGaugeConfig) Description() string { return c.description } // Unit returns the configured unit. func (c Int64ObservableGaugeConfig) Unit() string { return c.unit } // Callbacks returns the configured callbacks. func (c Int64ObservableGaugeConfig) Callbacks() []Int64Callback { return c.callbacks } // Int64ObservableGaugeOption applies options to a // [Int64ObservableGaugeConfig]. See [Int64ObservableOption] and // [InstrumentOption] for other options that can be used as an // Int64ObservableGaugeOption. type Int64ObservableGaugeOption interface { applyInt64ObservableGauge(Int64ObservableGaugeConfig) Int64ObservableGaugeConfig } // Int64Observer is a recorder of int64 measurements. // // Warning: Methods may be added to this interface in minor releases. See // package documentation on API implementation for information on how to set // default behavior for unimplemented methods. type Int64Observer interface { // Users of the interface can ignore this. This embedded type is only used // by implementations of this interface. See the "API Implementations" // section of the package documentation for more information. embedded.Int64Observer // Observe records the int64 value. // // Use the WithAttributeSet (or, if performance is not a concern, // the WithAttributes) option to include measurement attributes. Observe(value int64, options ...ObserveOption) } // Int64Callback is a function registered with a Meter that makes observations // for an Int64Observerable instrument it is registered with. Calls to the // Int64Observer record measurement values for the Int64Observable. // // The function needs to complete in a finite amount of time and the deadline // of the passed context is expected to be honored. // // The function needs to make unique observations across all registered // Int64Callbacks. Meaning, it should not report measurements with the same // attributes as another Int64Callbacks also registered for the same // instrument. // // The function needs to be concurrent safe. type Int64Callback func(context.Context, Int64Observer) error // Int64ObservableOption applies options to int64 Observer instruments. type Int64ObservableOption interface { Int64ObservableCounterOption Int64ObservableUpDownCounterOption Int64ObservableGaugeOption } type int64CallbackOpt struct { cback Int64Callback } func (o int64CallbackOpt) applyInt64ObservableCounter(cfg Int64ObservableCounterConfig) Int64ObservableCounterConfig { cfg.callbacks = append(cfg.callbacks, o.cback) return cfg } func (o int64CallbackOpt) applyInt64ObservableUpDownCounter(cfg Int64ObservableUpDownCounterConfig) Int64ObservableUpDownCounterConfig { cfg.callbacks = append(cfg.callbacks, o.cback) return cfg } func (o int64CallbackOpt) applyInt64ObservableGauge(cfg Int64ObservableGaugeConfig) Int64ObservableGaugeConfig { cfg.callbacks = append(cfg.callbacks, o.cback) return cfg } // WithInt64Callback adds callback to be called for an instrument. func WithInt64Callback(callback Int64Callback) Int64ObservableOption { return int64CallbackOpt{callback} } ================================================ FILE: vendor/go.opentelemetry.io/otel/metric/config.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package metric // import "go.opentelemetry.io/otel/metric" import "go.opentelemetry.io/otel/attribute" // MeterConfig contains options for Meters. type MeterConfig struct { instrumentationVersion string schemaURL string attrs attribute.Set // Ensure forward compatibility by explicitly making this not comparable. noCmp [0]func() //nolint: unused // This is indeed used. } // InstrumentationVersion returns the version of the library providing // instrumentation. func (cfg MeterConfig) InstrumentationVersion() string { return cfg.instrumentationVersion } // InstrumentationAttributes returns the attributes associated with the library // providing instrumentation. func (cfg MeterConfig) InstrumentationAttributes() attribute.Set { return cfg.attrs } // SchemaURL is the schema_url of the library providing instrumentation. func (cfg MeterConfig) SchemaURL() string { return cfg.schemaURL } // MeterOption is an interface for applying Meter options. type MeterOption interface { // applyMeter is used to set a MeterOption value of a MeterConfig. applyMeter(MeterConfig) MeterConfig } // NewMeterConfig creates a new MeterConfig and applies // all the given options. func NewMeterConfig(opts ...MeterOption) MeterConfig { var config MeterConfig for _, o := range opts { config = o.applyMeter(config) } return config } type meterOptionFunc func(MeterConfig) MeterConfig func (fn meterOptionFunc) applyMeter(cfg MeterConfig) MeterConfig { return fn(cfg) } // WithInstrumentationVersion sets the instrumentation version. func WithInstrumentationVersion(version string) MeterOption { return meterOptionFunc(func(config MeterConfig) MeterConfig { config.instrumentationVersion = version return config }) } // WithInstrumentationAttributes sets the instrumentation attributes. // // The passed attributes will be de-duplicated. func WithInstrumentationAttributes(attr ...attribute.KeyValue) MeterOption { return meterOptionFunc(func(config MeterConfig) MeterConfig { config.attrs = attribute.NewSet(attr...) return config }) } // WithSchemaURL sets the schema URL. func WithSchemaURL(schemaURL string) MeterOption { return meterOptionFunc(func(config MeterConfig) MeterConfig { config.schemaURL = schemaURL return config }) } ================================================ FILE: vendor/go.opentelemetry.io/otel/metric/doc.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /* Package metric provides the OpenTelemetry API used to measure metrics about source code operation. This API is separate from its implementation so the instrumentation built from it is reusable. See [go.opentelemetry.io/otel/sdk/metric] for the official OpenTelemetry implementation of this API. All measurements made with this package are made via instruments. These instruments are created by a [Meter] which itself is created by a [MeterProvider]. Applications need to accept a [MeterProvider] implementation as a starting point when instrumenting. This can be done directly, or by using the OpenTelemetry global MeterProvider via [GetMeterProvider]. Using an appropriately named [Meter] from the accepted [MeterProvider], instrumentation can then be built from the [Meter]'s instruments. # Instruments Each instrument is designed to make measurements of a particular type. Broadly, all instruments fall into two overlapping logical categories: asynchronous or synchronous, and int64 or float64. All synchronous instruments ([Int64Counter], [Int64UpDownCounter], [Int64Histogram], [Float64Counter], [Float64UpDownCounter], and [Float64Histogram]) are used to measure the operation and performance of source code during the source code execution. These instruments only make measurements when the source code they instrument is run. All asynchronous instruments ([Int64ObservableCounter], [Int64ObservableUpDownCounter], [Int64ObservableGauge], [Float64ObservableCounter], [Float64ObservableUpDownCounter], and [Float64ObservableGauge]) are used to measure metrics outside of the execution of source code. They are said to make "observations" via a callback function called once every measurement collection cycle. Each instrument is also grouped by the value type it measures. Either int64 or float64. The value being measured will dictate which instrument in these categories to use. Outside of these two broad categories, instruments are described by the function they are designed to serve. All Counters ([Int64Counter], [Float64Counter], [Int64ObservableCounter], and [Float64ObservableCounter]) are designed to measure values that never decrease in value, but instead only incrementally increase in value. UpDownCounters ([Int64UpDownCounter], [Float64UpDownCounter], [Int64ObservableUpDownCounter], and [Float64ObservableUpDownCounter]) on the other hand, are designed to measure values that can increase and decrease. When more information needs to be conveyed about all the synchronous measurements made during a collection cycle, a Histogram ([Int64Histogram] and [Float64Histogram]) should be used. Finally, when just the most recent measurement needs to be conveyed about an asynchronous measurement, a Gauge ([Int64ObservableGauge] and [Float64ObservableGauge]) should be used. See the [OpenTelemetry documentation] for more information about instruments and their intended use. # Measurements Measurements are made by recording values and information about the values with an instrument. How these measurements are recorded depends on the instrument. Measurements for synchronous instruments ([Int64Counter], [Int64UpDownCounter], [Int64Histogram], [Float64Counter], [Float64UpDownCounter], and [Float64Histogram]) are recorded using the instrument methods directly. All counter instruments have an Add method that is used to measure an increment value, and all histogram instruments have a Record method to measure a data point. Asynchronous instruments ([Int64ObservableCounter], [Int64ObservableUpDownCounter], [Int64ObservableGauge], [Float64ObservableCounter], [Float64ObservableUpDownCounter], and [Float64ObservableGauge]) record measurements within a callback function. The callback is registered with the Meter which ensures the callback is called once per collection cycle. A callback can be registered two ways: during the instrument's creation using an option, or later using the RegisterCallback method of the [Meter] that created the instrument. If the following criteria are met, an option ([WithInt64Callback] or [WithFloat64Callback]) can be used during the asynchronous instrument's creation to register a callback ([Int64Callback] or [Float64Callback], respectively): - The measurement process is known when the instrument is created - Only that instrument will make a measurement within the callback - The callback never needs to be unregistered If the criteria are not met, use the RegisterCallback method of the [Meter] that created the instrument to register a [Callback]. # API Implementations This package does not conform to the standard Go versioning policy, all of its interfaces may have methods added to them without a package major version bump. This non-standard API evolution could surprise an uninformed implementation author. They could unknowingly build their implementation in a way that would result in a runtime panic for their users that update to the new API. The API is designed to help inform an instrumentation author about this non-standard API evolution. It requires them to choose a default behavior for unimplemented interface methods. There are three behavior choices they can make: - Compilation failure - Panic - Default to another implementation All interfaces in this API embed a corresponding interface from [go.opentelemetry.io/otel/metric/embedded]. If an author wants the default behavior of their implementations to be a compilation failure, signaling to their users they need to update to the latest version of that implementation, they need to embed the corresponding interface from [go.opentelemetry.io/otel/metric/embedded] in their implementation. For example, import "go.opentelemetry.io/otel/metric/embedded" type MeterProvider struct { embedded.MeterProvider // ... } If an author wants the default behavior of their implementations to a panic, they need to embed the API interface directly. import "go.opentelemetry.io/otel/metric" type MeterProvider struct { metric.MeterProvider // ... } This is not a recommended behavior as it could lead to publishing packages that contain runtime panics when users update other package that use newer versions of [go.opentelemetry.io/otel/metric]. Finally, an author can embed another implementation in theirs. The embedded implementation will be used for methods not defined by the author. For example, an author who want to default to silently dropping the call can use [go.opentelemetry.io/otel/metric/noop]: import "go.opentelemetry.io/otel/metric/noop" type MeterProvider struct { noop.MeterProvider // ... } It is strongly recommended that authors only embed [go.opentelemetry.io/otel/metric/noop] if they choose this default behavior. That implementation is the only one OpenTelemetry authors can guarantee will fully implement all the API interfaces when a user updates their API. [OpenTelemetry documentation]: https://opentelemetry.io/docs/concepts/signals/metrics/ [GetMeterProvider]: https://pkg.go.dev/go.opentelemetry.io/otel#GetMeterProvider */ package metric // import "go.opentelemetry.io/otel/metric" ================================================ FILE: vendor/go.opentelemetry.io/otel/metric/embedded/embedded.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package embedded provides interfaces embedded within the [OpenTelemetry // metric API]. // // Implementers of the [OpenTelemetry metric API] can embed the relevant type // from this package into their implementation directly. Doing so will result // in a compilation error for users when the [OpenTelemetry metric API] is // extended (which is something that can happen without a major version bump of // the API package). // // [OpenTelemetry metric API]: https://pkg.go.dev/go.opentelemetry.io/otel/metric package embedded // import "go.opentelemetry.io/otel/metric/embedded" // MeterProvider is embedded in // [go.opentelemetry.io/otel/metric.MeterProvider]. // // Embed this interface in your implementation of the // [go.opentelemetry.io/otel/metric.MeterProvider] if you want users to // experience a compilation error, signaling they need to update to your latest // implementation, when the [go.opentelemetry.io/otel/metric.MeterProvider] // interface is extended (which is something that can happen without a major // version bump of the API package). type MeterProvider interface{ meterProvider() } // Meter is embedded in [go.opentelemetry.io/otel/metric.Meter]. // // Embed this interface in your implementation of the // [go.opentelemetry.io/otel/metric.Meter] if you want users to experience a // compilation error, signaling they need to update to your latest // implementation, when the [go.opentelemetry.io/otel/metric.Meter] interface // is extended (which is something that can happen without a major version bump // of the API package). type Meter interface{ meter() } // Float64Observer is embedded in // [go.opentelemetry.io/otel/metric.Float64Observer]. // // Embed this interface in your implementation of the // [go.opentelemetry.io/otel/metric.Float64Observer] if you want // users to experience a compilation error, signaling they need to update to // your latest implementation, when the // [go.opentelemetry.io/otel/metric.Float64Observer] interface is // extended (which is something that can happen without a major version bump of // the API package). type Float64Observer interface{ float64Observer() } // Int64Observer is embedded in // [go.opentelemetry.io/otel/metric.Int64Observer]. // // Embed this interface in your implementation of the // [go.opentelemetry.io/otel/metric.Int64Observer] if you want users // to experience a compilation error, signaling they need to update to your // latest implementation, when the // [go.opentelemetry.io/otel/metric.Int64Observer] interface is // extended (which is something that can happen without a major version bump of // the API package). type Int64Observer interface{ int64Observer() } // Observer is embedded in [go.opentelemetry.io/otel/metric.Observer]. // // Embed this interface in your implementation of the // [go.opentelemetry.io/otel/metric.Observer] if you want users to experience a // compilation error, signaling they need to update to your latest // implementation, when the [go.opentelemetry.io/otel/metric.Observer] // interface is extended (which is something that can happen without a major // version bump of the API package). type Observer interface{ observer() } // Registration is embedded in [go.opentelemetry.io/otel/metric.Registration]. // // Embed this interface in your implementation of the // [go.opentelemetry.io/otel/metric.Registration] if you want users to // experience a compilation error, signaling they need to update to your latest // implementation, when the [go.opentelemetry.io/otel/metric.Registration] // interface is extended (which is something that can happen without a major // version bump of the API package). type Registration interface{ registration() } // Float64Counter is embedded in // [go.opentelemetry.io/otel/metric.Float64Counter]. // // Embed this interface in your implementation of the // [go.opentelemetry.io/otel/metric.Float64Counter] if you want // users to experience a compilation error, signaling they need to update to // your latest implementation, when the // [go.opentelemetry.io/otel/metric.Float64Counter] interface is // extended (which is something that can happen without a major version bump of // the API package). type Float64Counter interface{ float64Counter() } // Float64Histogram is embedded in // [go.opentelemetry.io/otel/metric.Float64Histogram]. // // Embed this interface in your implementation of the // [go.opentelemetry.io/otel/metric.Float64Histogram] if you want // users to experience a compilation error, signaling they need to update to // your latest implementation, when the // [go.opentelemetry.io/otel/metric.Float64Histogram] interface is // extended (which is something that can happen without a major version bump of // the API package). type Float64Histogram interface{ float64Histogram() } // Float64ObservableCounter is embedded in // [go.opentelemetry.io/otel/metric.Float64ObservableCounter]. // // Embed this interface in your implementation of the // [go.opentelemetry.io/otel/metric.Float64ObservableCounter] if you // want users to experience a compilation error, signaling they need to update // to your latest implementation, when the // [go.opentelemetry.io/otel/metric.Float64ObservableCounter] // interface is extended (which is something that can happen without a major // version bump of the API package). type Float64ObservableCounter interface{ float64ObservableCounter() } // Float64ObservableGauge is embedded in // [go.opentelemetry.io/otel/metric.Float64ObservableGauge]. // // Embed this interface in your implementation of the // [go.opentelemetry.io/otel/metric.Float64ObservableGauge] if you // want users to experience a compilation error, signaling they need to update // to your latest implementation, when the // [go.opentelemetry.io/otel/metric.Float64ObservableGauge] // interface is extended (which is something that can happen without a major // version bump of the API package). type Float64ObservableGauge interface{ float64ObservableGauge() } // Float64ObservableUpDownCounter is embedded in // [go.opentelemetry.io/otel/metric.Float64ObservableUpDownCounter]. // // Embed this interface in your implementation of the // [go.opentelemetry.io/otel/metric.Float64ObservableUpDownCounter] // if you want users to experience a compilation error, signaling they need to // update to your latest implementation, when the // [go.opentelemetry.io/otel/metric.Float64ObservableUpDownCounter] // interface is extended (which is something that can happen without a major // version bump of the API package). type Float64ObservableUpDownCounter interface{ float64ObservableUpDownCounter() } // Float64UpDownCounter is embedded in // [go.opentelemetry.io/otel/metric.Float64UpDownCounter]. // // Embed this interface in your implementation of the // [go.opentelemetry.io/otel/metric.Float64UpDownCounter] if you // want users to experience a compilation error, signaling they need to update // to your latest implementation, when the // [go.opentelemetry.io/otel/metric.Float64UpDownCounter] interface // is extended (which is something that can happen without a major version bump // of the API package). type Float64UpDownCounter interface{ float64UpDownCounter() } // Int64Counter is embedded in // [go.opentelemetry.io/otel/metric.Int64Counter]. // // Embed this interface in your implementation of the // [go.opentelemetry.io/otel/metric.Int64Counter] if you want users // to experience a compilation error, signaling they need to update to your // latest implementation, when the // [go.opentelemetry.io/otel/metric.Int64Counter] interface is // extended (which is something that can happen without a major version bump of // the API package). type Int64Counter interface{ int64Counter() } // Int64Histogram is embedded in // [go.opentelemetry.io/otel/metric.Int64Histogram]. // // Embed this interface in your implementation of the // [go.opentelemetry.io/otel/metric.Int64Histogram] if you want // users to experience a compilation error, signaling they need to update to // your latest implementation, when the // [go.opentelemetry.io/otel/metric.Int64Histogram] interface is // extended (which is something that can happen without a major version bump of // the API package). type Int64Histogram interface{ int64Histogram() } // Int64ObservableCounter is embedded in // [go.opentelemetry.io/otel/metric.Int64ObservableCounter]. // // Embed this interface in your implementation of the // [go.opentelemetry.io/otel/metric.Int64ObservableCounter] if you // want users to experience a compilation error, signaling they need to update // to your latest implementation, when the // [go.opentelemetry.io/otel/metric.Int64ObservableCounter] // interface is extended (which is something that can happen without a major // version bump of the API package). type Int64ObservableCounter interface{ int64ObservableCounter() } // Int64ObservableGauge is embedded in // [go.opentelemetry.io/otel/metric.Int64ObservableGauge]. // // Embed this interface in your implementation of the // [go.opentelemetry.io/otel/metric.Int64ObservableGauge] if you // want users to experience a compilation error, signaling they need to update // to your latest implementation, when the // [go.opentelemetry.io/otel/metric.Int64ObservableGauge] interface // is extended (which is something that can happen without a major version bump // of the API package). type Int64ObservableGauge interface{ int64ObservableGauge() } // Int64ObservableUpDownCounter is embedded in // [go.opentelemetry.io/otel/metric.Int64ObservableUpDownCounter]. // // Embed this interface in your implementation of the // [go.opentelemetry.io/otel/metric.Int64ObservableUpDownCounter] if // you want users to experience a compilation error, signaling they need to // update to your latest implementation, when the // [go.opentelemetry.io/otel/metric.Int64ObservableUpDownCounter] // interface is extended (which is something that can happen without a major // version bump of the API package). type Int64ObservableUpDownCounter interface{ int64ObservableUpDownCounter() } // Int64UpDownCounter is embedded in // [go.opentelemetry.io/otel/metric.Int64UpDownCounter]. // // Embed this interface in your implementation of the // [go.opentelemetry.io/otel/metric.Int64UpDownCounter] if you want // users to experience a compilation error, signaling they need to update to // your latest implementation, when the // [go.opentelemetry.io/otel/metric.Int64UpDownCounter] interface is // extended (which is something that can happen without a major version bump of // the API package). type Int64UpDownCounter interface{ int64UpDownCounter() } ================================================ FILE: vendor/go.opentelemetry.io/otel/metric/instrument.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package metric // import "go.opentelemetry.io/otel/metric" import "go.opentelemetry.io/otel/attribute" // Observable is used as a grouping mechanism for all instruments that are // updated within a Callback. type Observable interface { observable() } // InstrumentOption applies options to all instruments. type InstrumentOption interface { Int64CounterOption Int64UpDownCounterOption Int64HistogramOption Int64ObservableCounterOption Int64ObservableUpDownCounterOption Int64ObservableGaugeOption Float64CounterOption Float64UpDownCounterOption Float64HistogramOption Float64ObservableCounterOption Float64ObservableUpDownCounterOption Float64ObservableGaugeOption } type descOpt string func (o descOpt) applyFloat64Counter(c Float64CounterConfig) Float64CounterConfig { c.description = string(o) return c } func (o descOpt) applyFloat64UpDownCounter(c Float64UpDownCounterConfig) Float64UpDownCounterConfig { c.description = string(o) return c } func (o descOpt) applyFloat64Histogram(c Float64HistogramConfig) Float64HistogramConfig { c.description = string(o) return c } func (o descOpt) applyFloat64ObservableCounter(c Float64ObservableCounterConfig) Float64ObservableCounterConfig { c.description = string(o) return c } func (o descOpt) applyFloat64ObservableUpDownCounter(c Float64ObservableUpDownCounterConfig) Float64ObservableUpDownCounterConfig { c.description = string(o) return c } func (o descOpt) applyFloat64ObservableGauge(c Float64ObservableGaugeConfig) Float64ObservableGaugeConfig { c.description = string(o) return c } func (o descOpt) applyInt64Counter(c Int64CounterConfig) Int64CounterConfig { c.description = string(o) return c } func (o descOpt) applyInt64UpDownCounter(c Int64UpDownCounterConfig) Int64UpDownCounterConfig { c.description = string(o) return c } func (o descOpt) applyInt64Histogram(c Int64HistogramConfig) Int64HistogramConfig { c.description = string(o) return c } func (o descOpt) applyInt64ObservableCounter(c Int64ObservableCounterConfig) Int64ObservableCounterConfig { c.description = string(o) return c } func (o descOpt) applyInt64ObservableUpDownCounter(c Int64ObservableUpDownCounterConfig) Int64ObservableUpDownCounterConfig { c.description = string(o) return c } func (o descOpt) applyInt64ObservableGauge(c Int64ObservableGaugeConfig) Int64ObservableGaugeConfig { c.description = string(o) return c } // WithDescription sets the instrument description. func WithDescription(desc string) InstrumentOption { return descOpt(desc) } type unitOpt string func (o unitOpt) applyFloat64Counter(c Float64CounterConfig) Float64CounterConfig { c.unit = string(o) return c } func (o unitOpt) applyFloat64UpDownCounter(c Float64UpDownCounterConfig) Float64UpDownCounterConfig { c.unit = string(o) return c } func (o unitOpt) applyFloat64Histogram(c Float64HistogramConfig) Float64HistogramConfig { c.unit = string(o) return c } func (o unitOpt) applyFloat64ObservableCounter(c Float64ObservableCounterConfig) Float64ObservableCounterConfig { c.unit = string(o) return c } func (o unitOpt) applyFloat64ObservableUpDownCounter(c Float64ObservableUpDownCounterConfig) Float64ObservableUpDownCounterConfig { c.unit = string(o) return c } func (o unitOpt) applyFloat64ObservableGauge(c Float64ObservableGaugeConfig) Float64ObservableGaugeConfig { c.unit = string(o) return c } func (o unitOpt) applyInt64Counter(c Int64CounterConfig) Int64CounterConfig { c.unit = string(o) return c } func (o unitOpt) applyInt64UpDownCounter(c Int64UpDownCounterConfig) Int64UpDownCounterConfig { c.unit = string(o) return c } func (o unitOpt) applyInt64Histogram(c Int64HistogramConfig) Int64HistogramConfig { c.unit = string(o) return c } func (o unitOpt) applyInt64ObservableCounter(c Int64ObservableCounterConfig) Int64ObservableCounterConfig { c.unit = string(o) return c } func (o unitOpt) applyInt64ObservableUpDownCounter(c Int64ObservableUpDownCounterConfig) Int64ObservableUpDownCounterConfig { c.unit = string(o) return c } func (o unitOpt) applyInt64ObservableGauge(c Int64ObservableGaugeConfig) Int64ObservableGaugeConfig { c.unit = string(o) return c } // WithUnit sets the instrument unit. // // The unit u should be defined using the appropriate [UCUM](https://ucum.org) case-sensitive code. func WithUnit(u string) InstrumentOption { return unitOpt(u) } // AddOption applies options to an addition measurement. See // [MeasurementOption] for other options that can be used as an AddOption. type AddOption interface { applyAdd(AddConfig) AddConfig } // AddConfig contains options for an addition measurement. type AddConfig struct { attrs attribute.Set } // NewAddConfig returns a new [AddConfig] with all opts applied. func NewAddConfig(opts []AddOption) AddConfig { config := AddConfig{attrs: *attribute.EmptySet()} for _, o := range opts { config = o.applyAdd(config) } return config } // Attributes returns the configured attribute set. func (c AddConfig) Attributes() attribute.Set { return c.attrs } // RecordOption applies options to an addition measurement. See // [MeasurementOption] for other options that can be used as a RecordOption. type RecordOption interface { applyRecord(RecordConfig) RecordConfig } // RecordConfig contains options for a recorded measurement. type RecordConfig struct { attrs attribute.Set } // NewRecordConfig returns a new [RecordConfig] with all opts applied. func NewRecordConfig(opts []RecordOption) RecordConfig { config := RecordConfig{attrs: *attribute.EmptySet()} for _, o := range opts { config = o.applyRecord(config) } return config } // Attributes returns the configured attribute set. func (c RecordConfig) Attributes() attribute.Set { return c.attrs } // ObserveOption applies options to an addition measurement. See // [MeasurementOption] for other options that can be used as a ObserveOption. type ObserveOption interface { applyObserve(ObserveConfig) ObserveConfig } // ObserveConfig contains options for an observed measurement. type ObserveConfig struct { attrs attribute.Set } // NewObserveConfig returns a new [ObserveConfig] with all opts applied. func NewObserveConfig(opts []ObserveOption) ObserveConfig { config := ObserveConfig{attrs: *attribute.EmptySet()} for _, o := range opts { config = o.applyObserve(config) } return config } // Attributes returns the configured attribute set. func (c ObserveConfig) Attributes() attribute.Set { return c.attrs } // MeasurementOption applies options to all instrument measurement. type MeasurementOption interface { AddOption RecordOption ObserveOption } type attrOpt struct { set attribute.Set } // mergeSets returns the union of keys between a and b. Any duplicate keys will // use the value associated with b. func mergeSets(a, b attribute.Set) attribute.Set { // NewMergeIterator uses the first value for any duplicates. iter := attribute.NewMergeIterator(&b, &a) merged := make([]attribute.KeyValue, 0, a.Len()+b.Len()) for iter.Next() { merged = append(merged, iter.Attribute()) } return attribute.NewSet(merged...) } func (o attrOpt) applyAdd(c AddConfig) AddConfig { switch { case o.set.Len() == 0: case c.attrs.Len() == 0: c.attrs = o.set default: c.attrs = mergeSets(c.attrs, o.set) } return c } func (o attrOpt) applyRecord(c RecordConfig) RecordConfig { switch { case o.set.Len() == 0: case c.attrs.Len() == 0: c.attrs = o.set default: c.attrs = mergeSets(c.attrs, o.set) } return c } func (o attrOpt) applyObserve(c ObserveConfig) ObserveConfig { switch { case o.set.Len() == 0: case c.attrs.Len() == 0: c.attrs = o.set default: c.attrs = mergeSets(c.attrs, o.set) } return c } // WithAttributeSet sets the attribute Set associated with a measurement is // made with. // // If multiple WithAttributeSet or WithAttributes options are passed the // attributes will be merged together in the order they are passed. Attributes // with duplicate keys will use the last value passed. func WithAttributeSet(attributes attribute.Set) MeasurementOption { return attrOpt{set: attributes} } // WithAttributes converts attributes into an attribute Set and sets the Set to // be associated with a measurement. This is shorthand for: // // cp := make([]attribute.KeyValue, len(attributes)) // copy(cp, attributes) // WithAttributes(attribute.NewSet(cp...)) // // [attribute.NewSet] may modify the passed attributes so this will make a copy // of attributes before creating a set in order to ensure this function is // concurrent safe. This makes this option function less optimized in // comparison to [WithAttributeSet]. Therefore, [WithAttributeSet] should be // preferred for performance sensitive code. // // See [WithAttributeSet] for information about how multiple WithAttributes are // merged. func WithAttributes(attributes ...attribute.KeyValue) MeasurementOption { cp := make([]attribute.KeyValue, len(attributes)) copy(cp, attributes) return attrOpt{set: attribute.NewSet(cp...)} } ================================================ FILE: vendor/go.opentelemetry.io/otel/metric/meter.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package metric // import "go.opentelemetry.io/otel/metric" import ( "context" "go.opentelemetry.io/otel/metric/embedded" ) // MeterProvider provides access to named Meter instances, for instrumenting // an application or package. // // Warning: Methods may be added to this interface in minor releases. See // package documentation on API implementation for information on how to set // default behavior for unimplemented methods. type MeterProvider interface { // Users of the interface can ignore this. This embedded type is only used // by implementations of this interface. See the "API Implementations" // section of the package documentation for more information. embedded.MeterProvider // Meter returns a new Meter with the provided name and configuration. // // A Meter should be scoped at most to a single package. The name needs to // be unique so it does not collide with other names used by // an application, nor other applications. To achieve this, the import path // of the instrumentation package is recommended to be used as name. // // If the name is empty, then an implementation defined default name will // be used instead. Meter(name string, opts ...MeterOption) Meter } // Meter provides access to instrument instances for recording metrics. // // Warning: Methods may be added to this interface in minor releases. See // package documentation on API implementation for information on how to set // default behavior for unimplemented methods. type Meter interface { // Users of the interface can ignore this. This embedded type is only used // by implementations of this interface. See the "API Implementations" // section of the package documentation for more information. embedded.Meter // Int64Counter returns a new Int64Counter instrument identified by name // and configured with options. The instrument is used to synchronously // record increasing int64 measurements during a computational operation. Int64Counter(name string, options ...Int64CounterOption) (Int64Counter, error) // Int64UpDownCounter returns a new Int64UpDownCounter instrument // identified by name and configured with options. The instrument is used // to synchronously record int64 measurements during a computational // operation. Int64UpDownCounter(name string, options ...Int64UpDownCounterOption) (Int64UpDownCounter, error) // Int64Histogram returns a new Int64Histogram instrument identified by // name and configured with options. The instrument is used to // synchronously record the distribution of int64 measurements during a // computational operation. Int64Histogram(name string, options ...Int64HistogramOption) (Int64Histogram, error) // Int64ObservableCounter returns a new Int64ObservableCounter identified // by name and configured with options. The instrument is used to // asynchronously record increasing int64 measurements once per a // measurement collection cycle. // // Measurements for the returned instrument are made via a callback. Use // the WithInt64Callback option to register the callback here, or use the // RegisterCallback method of this Meter to register one later. See the // Measurements section of the package documentation for more information. Int64ObservableCounter(name string, options ...Int64ObservableCounterOption) (Int64ObservableCounter, error) // Int64ObservableUpDownCounter returns a new Int64ObservableUpDownCounter // instrument identified by name and configured with options. The // instrument is used to asynchronously record int64 measurements once per // a measurement collection cycle. // // Measurements for the returned instrument are made via a callback. Use // the WithInt64Callback option to register the callback here, or use the // RegisterCallback method of this Meter to register one later. See the // Measurements section of the package documentation for more information. Int64ObservableUpDownCounter(name string, options ...Int64ObservableUpDownCounterOption) (Int64ObservableUpDownCounter, error) // Int64ObservableGauge returns a new Int64ObservableGauge instrument // identified by name and configured with options. The instrument is used // to asynchronously record instantaneous int64 measurements once per a // measurement collection cycle. // // Measurements for the returned instrument are made via a callback. Use // the WithInt64Callback option to register the callback here, or use the // RegisterCallback method of this Meter to register one later. See the // Measurements section of the package documentation for more information. Int64ObservableGauge(name string, options ...Int64ObservableGaugeOption) (Int64ObservableGauge, error) // Float64Counter returns a new Float64Counter instrument identified by // name and configured with options. The instrument is used to // synchronously record increasing float64 measurements during a // computational operation. Float64Counter(name string, options ...Float64CounterOption) (Float64Counter, error) // Float64UpDownCounter returns a new Float64UpDownCounter instrument // identified by name and configured with options. The instrument is used // to synchronously record float64 measurements during a computational // operation. Float64UpDownCounter(name string, options ...Float64UpDownCounterOption) (Float64UpDownCounter, error) // Float64Histogram returns a new Float64Histogram instrument identified by // name and configured with options. The instrument is used to // synchronously record the distribution of float64 measurements during a // computational operation. Float64Histogram(name string, options ...Float64HistogramOption) (Float64Histogram, error) // Float64ObservableCounter returns a new Float64ObservableCounter // instrument identified by name and configured with options. The // instrument is used to asynchronously record increasing float64 // measurements once per a measurement collection cycle. // // Measurements for the returned instrument are made via a callback. Use // the WithFloat64Callback option to register the callback here, or use the // RegisterCallback method of this Meter to register one later. See the // Measurements section of the package documentation for more information. Float64ObservableCounter(name string, options ...Float64ObservableCounterOption) (Float64ObservableCounter, error) // Float64ObservableUpDownCounter returns a new // Float64ObservableUpDownCounter instrument identified by name and // configured with options. The instrument is used to asynchronously record // float64 measurements once per a measurement collection cycle. // // Measurements for the returned instrument are made via a callback. Use // the WithFloat64Callback option to register the callback here, or use the // RegisterCallback method of this Meter to register one later. See the // Measurements section of the package documentation for more information. Float64ObservableUpDownCounter(name string, options ...Float64ObservableUpDownCounterOption) (Float64ObservableUpDownCounter, error) // Float64ObservableGauge returns a new Float64ObservableGauge instrument // identified by name and configured with options. The instrument is used // to asynchronously record instantaneous float64 measurements once per a // measurement collection cycle. // // Measurements for the returned instrument are made via a callback. Use // the WithFloat64Callback option to register the callback here, or use the // RegisterCallback method of this Meter to register one later. See the // Measurements section of the package documentation for more information. Float64ObservableGauge(name string, options ...Float64ObservableGaugeOption) (Float64ObservableGauge, error) // RegisterCallback registers f to be called during the collection of a // measurement cycle. // // If Unregister of the returned Registration is called, f needs to be // unregistered and not called during collection. // // The instruments f is registered with are the only instruments that f may // observe values for. // // If no instruments are passed, f should not be registered nor called // during collection. // // The function f needs to be concurrent safe. RegisterCallback(f Callback, instruments ...Observable) (Registration, error) } // Callback is a function registered with a Meter that makes observations for // the set of instruments it is registered with. The Observer parameter is used // to record measurement observations for these instruments. // // The function needs to complete in a finite amount of time and the deadline // of the passed context is expected to be honored. // // The function needs to make unique observations across all registered // Callbacks. Meaning, it should not report measurements for an instrument with // the same attributes as another Callback will report. // // The function needs to be concurrent safe. type Callback func(context.Context, Observer) error // Observer records measurements for multiple instruments in a Callback. // // Warning: Methods may be added to this interface in minor releases. See // package documentation on API implementation for information on how to set // default behavior for unimplemented methods. type Observer interface { // Users of the interface can ignore this. This embedded type is only used // by implementations of this interface. See the "API Implementations" // section of the package documentation for more information. embedded.Observer // ObserveFloat64 records the float64 value for obsrv. ObserveFloat64(obsrv Float64Observable, value float64, opts ...ObserveOption) // ObserveInt64 records the int64 value for obsrv. ObserveInt64(obsrv Int64Observable, value int64, opts ...ObserveOption) } // Registration is an token representing the unique registration of a callback // for a set of instruments with a Meter. // // Warning: Methods may be added to this interface in minor releases. See // package documentation on API implementation for information on how to set // default behavior for unimplemented methods. type Registration interface { // Users of the interface can ignore this. This embedded type is only used // by implementations of this interface. See the "API Implementations" // section of the package documentation for more information. embedded.Registration // Unregister removes the callback registration from a Meter. // // This method needs to be idempotent and concurrent safe. Unregister() error } ================================================ FILE: vendor/go.opentelemetry.io/otel/metric/syncfloat64.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package metric // import "go.opentelemetry.io/otel/metric" import ( "context" "go.opentelemetry.io/otel/metric/embedded" ) // Float64Counter is an instrument that records increasing float64 values. // // Warning: Methods may be added to this interface in minor releases. See // package documentation on API implementation for information on how to set // default behavior for unimplemented methods. type Float64Counter interface { // Users of the interface can ignore this. This embedded type is only used // by implementations of this interface. See the "API Implementations" // section of the package documentation for more information. embedded.Float64Counter // Add records a change to the counter. // // Use the WithAttributeSet (or, if performance is not a concern, // the WithAttributes) option to include measurement attributes. Add(ctx context.Context, incr float64, options ...AddOption) } // Float64CounterConfig contains options for synchronous counter instruments that // record int64 values. type Float64CounterConfig struct { description string unit string } // NewFloat64CounterConfig returns a new [Float64CounterConfig] with all opts // applied. func NewFloat64CounterConfig(opts ...Float64CounterOption) Float64CounterConfig { var config Float64CounterConfig for _, o := range opts { config = o.applyFloat64Counter(config) } return config } // Description returns the configured description. func (c Float64CounterConfig) Description() string { return c.description } // Unit returns the configured unit. func (c Float64CounterConfig) Unit() string { return c.unit } // Float64CounterOption applies options to a [Float64CounterConfig]. See // [InstrumentOption] for other options that can be used as a // Float64CounterOption. type Float64CounterOption interface { applyFloat64Counter(Float64CounterConfig) Float64CounterConfig } // Float64UpDownCounter is an instrument that records increasing or decreasing // float64 values. // // Warning: Methods may be added to this interface in minor releases. See // package documentation on API implementation for information on how to set // default behavior for unimplemented methods. type Float64UpDownCounter interface { // Users of the interface can ignore this. This embedded type is only used // by implementations of this interface. See the "API Implementations" // section of the package documentation for more information. embedded.Float64UpDownCounter // Add records a change to the counter. // // Use the WithAttributeSet (or, if performance is not a concern, // the WithAttributes) option to include measurement attributes. Add(ctx context.Context, incr float64, options ...AddOption) } // Float64UpDownCounterConfig contains options for synchronous counter // instruments that record int64 values. type Float64UpDownCounterConfig struct { description string unit string } // NewFloat64UpDownCounterConfig returns a new [Float64UpDownCounterConfig] // with all opts applied. func NewFloat64UpDownCounterConfig(opts ...Float64UpDownCounterOption) Float64UpDownCounterConfig { var config Float64UpDownCounterConfig for _, o := range opts { config = o.applyFloat64UpDownCounter(config) } return config } // Description returns the configured description. func (c Float64UpDownCounterConfig) Description() string { return c.description } // Unit returns the configured unit. func (c Float64UpDownCounterConfig) Unit() string { return c.unit } // Float64UpDownCounterOption applies options to a // [Float64UpDownCounterConfig]. See [InstrumentOption] for other options that // can be used as a Float64UpDownCounterOption. type Float64UpDownCounterOption interface { applyFloat64UpDownCounter(Float64UpDownCounterConfig) Float64UpDownCounterConfig } // Float64Histogram is an instrument that records a distribution of float64 // values. // // Warning: Methods may be added to this interface in minor releases. See // package documentation on API implementation for information on how to set // default behavior for unimplemented methods. type Float64Histogram interface { // Users of the interface can ignore this. This embedded type is only used // by implementations of this interface. See the "API Implementations" // section of the package documentation for more information. embedded.Float64Histogram // Record adds an additional value to the distribution. // // Use the WithAttributeSet (or, if performance is not a concern, // the WithAttributes) option to include measurement attributes. Record(ctx context.Context, incr float64, options ...RecordOption) } // Float64HistogramConfig contains options for synchronous counter instruments // that record int64 values. type Float64HistogramConfig struct { description string unit string } // NewFloat64HistogramConfig returns a new [Float64HistogramConfig] with all // opts applied. func NewFloat64HistogramConfig(opts ...Float64HistogramOption) Float64HistogramConfig { var config Float64HistogramConfig for _, o := range opts { config = o.applyFloat64Histogram(config) } return config } // Description returns the configured description. func (c Float64HistogramConfig) Description() string { return c.description } // Unit returns the configured unit. func (c Float64HistogramConfig) Unit() string { return c.unit } // Float64HistogramOption applies options to a [Float64HistogramConfig]. See // [InstrumentOption] for other options that can be used as a // Float64HistogramOption. type Float64HistogramOption interface { applyFloat64Histogram(Float64HistogramConfig) Float64HistogramConfig } ================================================ FILE: vendor/go.opentelemetry.io/otel/metric/syncint64.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package metric // import "go.opentelemetry.io/otel/metric" import ( "context" "go.opentelemetry.io/otel/metric/embedded" ) // Int64Counter is an instrument that records increasing int64 values. // // Warning: Methods may be added to this interface in minor releases. See // package documentation on API implementation for information on how to set // default behavior for unimplemented methods. type Int64Counter interface { // Users of the interface can ignore this. This embedded type is only used // by implementations of this interface. See the "API Implementations" // section of the package documentation for more information. embedded.Int64Counter // Add records a change to the counter. // // Use the WithAttributeSet (or, if performance is not a concern, // the WithAttributes) option to include measurement attributes. Add(ctx context.Context, incr int64, options ...AddOption) } // Int64CounterConfig contains options for synchronous counter instruments that // record int64 values. type Int64CounterConfig struct { description string unit string } // NewInt64CounterConfig returns a new [Int64CounterConfig] with all opts // applied. func NewInt64CounterConfig(opts ...Int64CounterOption) Int64CounterConfig { var config Int64CounterConfig for _, o := range opts { config = o.applyInt64Counter(config) } return config } // Description returns the configured description. func (c Int64CounterConfig) Description() string { return c.description } // Unit returns the configured unit. func (c Int64CounterConfig) Unit() string { return c.unit } // Int64CounterOption applies options to a [Int64CounterConfig]. See // [InstrumentOption] for other options that can be used as an // Int64CounterOption. type Int64CounterOption interface { applyInt64Counter(Int64CounterConfig) Int64CounterConfig } // Int64UpDownCounter is an instrument that records increasing or decreasing // int64 values. // // Warning: Methods may be added to this interface in minor releases. See // package documentation on API implementation for information on how to set // default behavior for unimplemented methods. type Int64UpDownCounter interface { // Users of the interface can ignore this. This embedded type is only used // by implementations of this interface. See the "API Implementations" // section of the package documentation for more information. embedded.Int64UpDownCounter // Add records a change to the counter. // // Use the WithAttributeSet (or, if performance is not a concern, // the WithAttributes) option to include measurement attributes. Add(ctx context.Context, incr int64, options ...AddOption) } // Int64UpDownCounterConfig contains options for synchronous counter // instruments that record int64 values. type Int64UpDownCounterConfig struct { description string unit string } // NewInt64UpDownCounterConfig returns a new [Int64UpDownCounterConfig] with // all opts applied. func NewInt64UpDownCounterConfig(opts ...Int64UpDownCounterOption) Int64UpDownCounterConfig { var config Int64UpDownCounterConfig for _, o := range opts { config = o.applyInt64UpDownCounter(config) } return config } // Description returns the configured description. func (c Int64UpDownCounterConfig) Description() string { return c.description } // Unit returns the configured unit. func (c Int64UpDownCounterConfig) Unit() string { return c.unit } // Int64UpDownCounterOption applies options to a [Int64UpDownCounterConfig]. // See [InstrumentOption] for other options that can be used as an // Int64UpDownCounterOption. type Int64UpDownCounterOption interface { applyInt64UpDownCounter(Int64UpDownCounterConfig) Int64UpDownCounterConfig } // Int64Histogram is an instrument that records a distribution of int64 // values. // // Warning: Methods may be added to this interface in minor releases. See // package documentation on API implementation for information on how to set // default behavior for unimplemented methods. type Int64Histogram interface { // Users of the interface can ignore this. This embedded type is only used // by implementations of this interface. See the "API Implementations" // section of the package documentation for more information. embedded.Int64Histogram // Record adds an additional value to the distribution. // // Use the WithAttributeSet (or, if performance is not a concern, // the WithAttributes) option to include measurement attributes. Record(ctx context.Context, incr int64, options ...RecordOption) } // Int64HistogramConfig contains options for synchronous counter instruments // that record int64 values. type Int64HistogramConfig struct { description string unit string } // NewInt64HistogramConfig returns a new [Int64HistogramConfig] with all opts // applied. func NewInt64HistogramConfig(opts ...Int64HistogramOption) Int64HistogramConfig { var config Int64HistogramConfig for _, o := range opts { config = o.applyInt64Histogram(config) } return config } // Description returns the configured description. func (c Int64HistogramConfig) Description() string { return c.description } // Unit returns the configured unit. func (c Int64HistogramConfig) Unit() string { return c.unit } // Int64HistogramOption applies options to a [Int64HistogramConfig]. See // [InstrumentOption] for other options that can be used as an // Int64HistogramOption. type Int64HistogramOption interface { applyInt64Histogram(Int64HistogramConfig) Int64HistogramConfig } ================================================ FILE: vendor/go.opentelemetry.io/otel/metric.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package otel // import "go.opentelemetry.io/otel" import ( "go.opentelemetry.io/otel/internal/global" "go.opentelemetry.io/otel/metric" ) // Meter returns a Meter from the global MeterProvider. The name must be the // name of the library providing instrumentation. This name may be the same as // the instrumented code only if that code provides built-in instrumentation. // If the name is empty, then a implementation defined default name will be // used instead. // // If this is called before a global MeterProvider is registered the returned // Meter will be a No-op implementation of a Meter. When a global MeterProvider // is registered for the first time, the returned Meter, and all the // instruments it has created or will create, are recreated automatically from // the new MeterProvider. // // This is short for GetMeterProvider().Meter(name). func Meter(name string, opts ...metric.MeterOption) metric.Meter { return GetMeterProvider().Meter(name, opts...) } // GetMeterProvider returns the registered global meter provider. // // If no global GetMeterProvider has been registered, a No-op GetMeterProvider // implementation is returned. When a global GetMeterProvider is registered for // the first time, the returned GetMeterProvider, and all the Meters it has // created or will create, are recreated automatically from the new // GetMeterProvider. func GetMeterProvider() metric.MeterProvider { return global.MeterProvider() } // SetMeterProvider registers mp as the global MeterProvider. func SetMeterProvider(mp metric.MeterProvider) { global.SetMeterProvider(mp) } ================================================ FILE: vendor/go.opentelemetry.io/otel/propagation/baggage.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package propagation // import "go.opentelemetry.io/otel/propagation" import ( "context" "go.opentelemetry.io/otel/baggage" ) const baggageHeader = "baggage" // Baggage is a propagator that supports the W3C Baggage format. // // This propagates user-defined baggage associated with a trace. The complete // specification is defined at https://www.w3.org/TR/baggage/. type Baggage struct{} var _ TextMapPropagator = Baggage{} // Inject sets baggage key-values from ctx into the carrier. func (b Baggage) Inject(ctx context.Context, carrier TextMapCarrier) { bStr := baggage.FromContext(ctx).String() if bStr != "" { carrier.Set(baggageHeader, bStr) } } // Extract returns a copy of parent with the baggage from the carrier added. func (b Baggage) Extract(parent context.Context, carrier TextMapCarrier) context.Context { bStr := carrier.Get(baggageHeader) if bStr == "" { return parent } bag, err := baggage.Parse(bStr) if err != nil { return parent } return baggage.ContextWithBaggage(parent, bag) } // Fields returns the keys who's values are set with Inject. func (b Baggage) Fields() []string { return []string{baggageHeader} } ================================================ FILE: vendor/go.opentelemetry.io/otel/propagation/doc.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /* Package propagation contains OpenTelemetry context propagators. OpenTelemetry propagators are used to extract and inject context data from and into messages exchanged by applications. The propagator supported by this package is the W3C Trace Context encoding (https://www.w3.org/TR/trace-context/), and W3C Baggage (https://www.w3.org/TR/baggage/). */ package propagation // import "go.opentelemetry.io/otel/propagation" ================================================ FILE: vendor/go.opentelemetry.io/otel/propagation/propagation.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package propagation // import "go.opentelemetry.io/otel/propagation" import ( "context" "net/http" ) // TextMapCarrier is the storage medium used by a TextMapPropagator. type TextMapCarrier interface { // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // Get returns the value associated with the passed key. Get(key string) string // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // Set stores the key-value pair. Set(key string, value string) // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // Keys lists the keys stored in this carrier. Keys() []string // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. } // MapCarrier is a TextMapCarrier that uses a map held in memory as a storage // medium for propagated key-value pairs. type MapCarrier map[string]string // Compile time check that MapCarrier implements the TextMapCarrier. var _ TextMapCarrier = MapCarrier{} // Get returns the value associated with the passed key. func (c MapCarrier) Get(key string) string { return c[key] } // Set stores the key-value pair. func (c MapCarrier) Set(key, value string) { c[key] = value } // Keys lists the keys stored in this carrier. func (c MapCarrier) Keys() []string { keys := make([]string, 0, len(c)) for k := range c { keys = append(keys, k) } return keys } // HeaderCarrier adapts http.Header to satisfy the TextMapCarrier interface. type HeaderCarrier http.Header // Get returns the value associated with the passed key. func (hc HeaderCarrier) Get(key string) string { return http.Header(hc).Get(key) } // Set stores the key-value pair. func (hc HeaderCarrier) Set(key string, value string) { http.Header(hc).Set(key, value) } // Keys lists the keys stored in this carrier. func (hc HeaderCarrier) Keys() []string { keys := make([]string, 0, len(hc)) for k := range hc { keys = append(keys, k) } return keys } // TextMapPropagator propagates cross-cutting concerns as key-value text // pairs within a carrier that travels in-band across process boundaries. type TextMapPropagator interface { // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // Inject set cross-cutting concerns from the Context into the carrier. Inject(ctx context.Context, carrier TextMapCarrier) // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // Extract reads cross-cutting concerns from the carrier into a Context. Extract(ctx context.Context, carrier TextMapCarrier) context.Context // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. // Fields returns the keys whose values are set with Inject. Fields() []string // DO NOT CHANGE: any modification will not be backwards compatible and // must never be done outside of a new major release. } type compositeTextMapPropagator []TextMapPropagator func (p compositeTextMapPropagator) Inject(ctx context.Context, carrier TextMapCarrier) { for _, i := range p { i.Inject(ctx, carrier) } } func (p compositeTextMapPropagator) Extract(ctx context.Context, carrier TextMapCarrier) context.Context { for _, i := range p { ctx = i.Extract(ctx, carrier) } return ctx } func (p compositeTextMapPropagator) Fields() []string { unique := make(map[string]struct{}) for _, i := range p { for _, k := range i.Fields() { unique[k] = struct{}{} } } fields := make([]string, 0, len(unique)) for k := range unique { fields = append(fields, k) } return fields } // NewCompositeTextMapPropagator returns a unified TextMapPropagator from the // group of passed TextMapPropagator. This allows different cross-cutting // concerns to be propagates in a unified manner. // // The returned TextMapPropagator will inject and extract cross-cutting // concerns in the order the TextMapPropagators were provided. Additionally, // the Fields method will return a de-duplicated slice of the keys that are // set with the Inject method. func NewCompositeTextMapPropagator(p ...TextMapPropagator) TextMapPropagator { return compositeTextMapPropagator(p) } ================================================ FILE: vendor/go.opentelemetry.io/otel/propagation/trace_context.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package propagation // import "go.opentelemetry.io/otel/propagation" import ( "context" "encoding/hex" "fmt" "regexp" "go.opentelemetry.io/otel/trace" ) const ( supportedVersion = 0 maxVersion = 254 traceparentHeader = "traceparent" tracestateHeader = "tracestate" ) // TraceContext is a propagator that supports the W3C Trace Context format // (https://www.w3.org/TR/trace-context/) // // This propagator will propagate the traceparent and tracestate headers to // guarantee traces are not broken. It is up to the users of this propagator // to choose if they want to participate in a trace by modifying the // traceparent header and relevant parts of the tracestate header containing // their proprietary information. type TraceContext struct{} var _ TextMapPropagator = TraceContext{} var traceCtxRegExp = regexp.MustCompile("^(?P[0-9a-f]{2})-(?P[a-f0-9]{32})-(?P[a-f0-9]{16})-(?P[a-f0-9]{2})(?:-.*)?$") // Inject set tracecontext from the Context into the carrier. func (tc TraceContext) Inject(ctx context.Context, carrier TextMapCarrier) { sc := trace.SpanContextFromContext(ctx) if !sc.IsValid() { return } if ts := sc.TraceState().String(); ts != "" { carrier.Set(tracestateHeader, ts) } // Clear all flags other than the trace-context supported sampling bit. flags := sc.TraceFlags() & trace.FlagsSampled h := fmt.Sprintf("%.2x-%s-%s-%s", supportedVersion, sc.TraceID(), sc.SpanID(), flags) carrier.Set(traceparentHeader, h) } // Extract reads tracecontext from the carrier into a returned Context. // // The returned Context will be a copy of ctx and contain the extracted // tracecontext as the remote SpanContext. If the extracted tracecontext is // invalid, the passed ctx will be returned directly instead. func (tc TraceContext) Extract(ctx context.Context, carrier TextMapCarrier) context.Context { sc := tc.extract(carrier) if !sc.IsValid() { return ctx } return trace.ContextWithRemoteSpanContext(ctx, sc) } func (tc TraceContext) extract(carrier TextMapCarrier) trace.SpanContext { h := carrier.Get(traceparentHeader) if h == "" { return trace.SpanContext{} } matches := traceCtxRegExp.FindStringSubmatch(h) if len(matches) == 0 { return trace.SpanContext{} } if len(matches) < 5 { // four subgroups plus the overall match return trace.SpanContext{} } if len(matches[1]) != 2 { return trace.SpanContext{} } ver, err := hex.DecodeString(matches[1]) if err != nil { return trace.SpanContext{} } version := int(ver[0]) if version > maxVersion { return trace.SpanContext{} } if version == 0 && len(matches) != 5 { // four subgroups plus the overall match return trace.SpanContext{} } if len(matches[2]) != 32 { return trace.SpanContext{} } var scc trace.SpanContextConfig scc.TraceID, err = trace.TraceIDFromHex(matches[2][:32]) if err != nil { return trace.SpanContext{} } if len(matches[3]) != 16 { return trace.SpanContext{} } scc.SpanID, err = trace.SpanIDFromHex(matches[3]) if err != nil { return trace.SpanContext{} } if len(matches[4]) != 2 { return trace.SpanContext{} } opts, err := hex.DecodeString(matches[4]) if err != nil || len(opts) < 1 || (version == 0 && opts[0] > 2) { return trace.SpanContext{} } // Clear all flags other than the trace-context supported sampling bit. scc.TraceFlags = trace.TraceFlags(opts[0]) & trace.FlagsSampled // Ignore the error returned here. Failure to parse tracestate MUST NOT // affect the parsing of traceparent according to the W3C tracecontext // specification. scc.TraceState, _ = trace.ParseTraceState(carrier.Get(tracestateHeader)) scc.Remote = true sc := trace.NewSpanContext(scc) if !sc.IsValid() { return trace.SpanContext{} } return sc } // Fields returns the keys who's values are set with Inject. func (tc TraceContext) Fields() []string { return []string{traceparentHeader, tracestateHeader} } ================================================ FILE: vendor/go.opentelemetry.io/otel/propagation.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package otel // import "go.opentelemetry.io/otel" import ( "go.opentelemetry.io/otel/internal/global" "go.opentelemetry.io/otel/propagation" ) // GetTextMapPropagator returns the global TextMapPropagator. If none has been // set, a No-Op TextMapPropagator is returned. func GetTextMapPropagator() propagation.TextMapPropagator { return global.TextMapPropagator() } // SetTextMapPropagator sets propagator as the global TextMapPropagator. func SetTextMapPropagator(propagator propagation.TextMapPropagator) { global.SetTextMapPropagator(propagator) } ================================================ FILE: vendor/go.opentelemetry.io/otel/requirements.txt ================================================ codespell==2.2.5 ================================================ FILE: vendor/go.opentelemetry.io/otel/semconv/v1.17.0/doc.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package semconv implements OpenTelemetry semantic conventions. // // OpenTelemetry semantic conventions are agreed standardized naming // patterns for OpenTelemetry things. This package represents the conventions // as of the v1.17.0 version of the OpenTelemetry specification. package semconv // import "go.opentelemetry.io/otel/semconv/v1.17.0" ================================================ FILE: vendor/go.opentelemetry.io/otel/semconv/v1.17.0/event.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Code generated from semantic convention specification. DO NOT EDIT. package semconv // import "go.opentelemetry.io/otel/semconv/v1.17.0" import "go.opentelemetry.io/otel/attribute" // This semantic convention defines the attributes used to represent a feature // flag evaluation as an event. const ( // FeatureFlagKeyKey is the attribute Key conforming to the // "feature_flag.key" semantic conventions. It represents the unique // identifier of the feature flag. // // Type: string // RequirementLevel: Required // Stability: stable // Examples: 'logo-color' FeatureFlagKeyKey = attribute.Key("feature_flag.key") // FeatureFlagProviderNameKey is the attribute Key conforming to the // "feature_flag.provider_name" semantic conventions. It represents the // name of the service provider that performs the flag evaluation. // // Type: string // RequirementLevel: Recommended // Stability: stable // Examples: 'Flag Manager' FeatureFlagProviderNameKey = attribute.Key("feature_flag.provider_name") // FeatureFlagVariantKey is the attribute Key conforming to the // "feature_flag.variant" semantic conventions. It represents the sHOULD be // a semantic identifier for a value. If one is unavailable, a stringified // version of the value can be used. // // Type: string // RequirementLevel: Recommended // Stability: stable // Examples: 'red', 'true', 'on' // Note: A semantic identifier, commonly referred to as a variant, provides // a means // for referring to a value without including the value itself. This can // provide additional context for understanding the meaning behind a value. // For example, the variant `red` maybe be used for the value `#c05543`. // // A stringified version of the value can be used in situations where a // semantic identifier is unavailable. String representation of the value // should be determined by the implementer. FeatureFlagVariantKey = attribute.Key("feature_flag.variant") ) // FeatureFlagKey returns an attribute KeyValue conforming to the // "feature_flag.key" semantic conventions. It represents the unique identifier // of the feature flag. func FeatureFlagKey(val string) attribute.KeyValue { return FeatureFlagKeyKey.String(val) } // FeatureFlagProviderName returns an attribute KeyValue conforming to the // "feature_flag.provider_name" semantic conventions. It represents the name of // the service provider that performs the flag evaluation. func FeatureFlagProviderName(val string) attribute.KeyValue { return FeatureFlagProviderNameKey.String(val) } // FeatureFlagVariant returns an attribute KeyValue conforming to the // "feature_flag.variant" semantic conventions. It represents the sHOULD be a // semantic identifier for a value. If one is unavailable, a stringified // version of the value can be used. func FeatureFlagVariant(val string) attribute.KeyValue { return FeatureFlagVariantKey.String(val) } // RPC received/sent message. const ( // MessageTypeKey is the attribute Key conforming to the "message.type" // semantic conventions. It represents the whether this is a received or // sent message. // // Type: Enum // RequirementLevel: Optional // Stability: stable MessageTypeKey = attribute.Key("message.type") // MessageIDKey is the attribute Key conforming to the "message.id" // semantic conventions. It represents the mUST be calculated as two // different counters starting from `1` one for sent messages and one for // received message. // // Type: int // RequirementLevel: Optional // Stability: stable // Note: This way we guarantee that the values will be consistent between // different implementations. MessageIDKey = attribute.Key("message.id") // MessageCompressedSizeKey is the attribute Key conforming to the // "message.compressed_size" semantic conventions. It represents the // compressed size of the message in bytes. // // Type: int // RequirementLevel: Optional // Stability: stable MessageCompressedSizeKey = attribute.Key("message.compressed_size") // MessageUncompressedSizeKey is the attribute Key conforming to the // "message.uncompressed_size" semantic conventions. It represents the // uncompressed size of the message in bytes. // // Type: int // RequirementLevel: Optional // Stability: stable MessageUncompressedSizeKey = attribute.Key("message.uncompressed_size") ) var ( // sent MessageTypeSent = MessageTypeKey.String("SENT") // received MessageTypeReceived = MessageTypeKey.String("RECEIVED") ) // MessageID returns an attribute KeyValue conforming to the "message.id" // semantic conventions. It represents the mUST be calculated as two different // counters starting from `1` one for sent messages and one for received // message. func MessageID(val int) attribute.KeyValue { return MessageIDKey.Int(val) } // MessageCompressedSize returns an attribute KeyValue conforming to the // "message.compressed_size" semantic conventions. It represents the compressed // size of the message in bytes. func MessageCompressedSize(val int) attribute.KeyValue { return MessageCompressedSizeKey.Int(val) } // MessageUncompressedSize returns an attribute KeyValue conforming to the // "message.uncompressed_size" semantic conventions. It represents the // uncompressed size of the message in bytes. func MessageUncompressedSize(val int) attribute.KeyValue { return MessageUncompressedSizeKey.Int(val) } // The attributes used to report a single exception associated with a span. const ( // ExceptionEscapedKey is the attribute Key conforming to the // "exception.escaped" semantic conventions. It represents the sHOULD be // set to true if the exception event is recorded at a point where it is // known that the exception is escaping the scope of the span. // // Type: boolean // RequirementLevel: Optional // Stability: stable // Note: An exception is considered to have escaped (or left) the scope of // a span, // if that span is ended while the exception is still logically "in // flight". // This may be actually "in flight" in some languages (e.g. if the // exception // is passed to a Context manager's `__exit__` method in Python) but will // usually be caught at the point of recording the exception in most // languages. // // It is usually not possible to determine at the point where an exception // is thrown // whether it will escape the scope of a span. // However, it is trivial to know that an exception // will escape, if one checks for an active exception just before ending // the span, // as done in the [example above](#recording-an-exception). // // It follows that an exception may still escape the scope of the span // even if the `exception.escaped` attribute was not set or set to false, // since the event might have been recorded at a time where it was not // clear whether the exception will escape. ExceptionEscapedKey = attribute.Key("exception.escaped") ) // ExceptionEscaped returns an attribute KeyValue conforming to the // "exception.escaped" semantic conventions. It represents the sHOULD be set to // true if the exception event is recorded at a point where it is known that // the exception is escaping the scope of the span. func ExceptionEscaped(val bool) attribute.KeyValue { return ExceptionEscapedKey.Bool(val) } ================================================ FILE: vendor/go.opentelemetry.io/otel/semconv/v1.17.0/exception.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package semconv // import "go.opentelemetry.io/otel/semconv/v1.17.0" const ( // ExceptionEventName is the name of the Span event representing an exception. ExceptionEventName = "exception" ) ================================================ FILE: vendor/go.opentelemetry.io/otel/semconv/v1.17.0/http.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package semconv // import "go.opentelemetry.io/otel/semconv/v1.17.0" // HTTP scheme attributes. var ( HTTPSchemeHTTP = HTTPSchemeKey.String("http") HTTPSchemeHTTPS = HTTPSchemeKey.String("https") ) ================================================ FILE: vendor/go.opentelemetry.io/otel/semconv/v1.17.0/resource.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Code generated from semantic convention specification. DO NOT EDIT. package semconv // import "go.opentelemetry.io/otel/semconv/v1.17.0" import "go.opentelemetry.io/otel/attribute" // The web browser in which the application represented by the resource is // running. The `browser.*` attributes MUST be used only for resources that // represent applications running in a web browser (regardless of whether // running on a mobile or desktop device). const ( // BrowserBrandsKey is the attribute Key conforming to the "browser.brands" // semantic conventions. It represents the array of brand name and version // separated by a space // // Type: string[] // RequirementLevel: Optional // Stability: stable // Examples: ' Not A;Brand 99', 'Chromium 99', 'Chrome 99' // Note: This value is intended to be taken from the [UA client hints // API](https://wicg.github.io/ua-client-hints/#interface) // (`navigator.userAgentData.brands`). BrowserBrandsKey = attribute.Key("browser.brands") // BrowserPlatformKey is the attribute Key conforming to the // "browser.platform" semantic conventions. It represents the platform on // which the browser is running // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'Windows', 'macOS', 'Android' // Note: This value is intended to be taken from the [UA client hints // API](https://wicg.github.io/ua-client-hints/#interface) // (`navigator.userAgentData.platform`). If unavailable, the legacy // `navigator.platform` API SHOULD NOT be used instead and this attribute // SHOULD be left unset in order for the values to be consistent. // The list of possible values is defined in the [W3C User-Agent Client // Hints // specification](https://wicg.github.io/ua-client-hints/#sec-ch-ua-platform). // Note that some (but not all) of these values can overlap with values in // the [`os.type` and `os.name` attributes](./os.md). However, for // consistency, the values in the `browser.platform` attribute should // capture the exact value that the user agent provides. BrowserPlatformKey = attribute.Key("browser.platform") // BrowserMobileKey is the attribute Key conforming to the "browser.mobile" // semantic conventions. It represents a boolean that is true if the // browser is running on a mobile device // // Type: boolean // RequirementLevel: Optional // Stability: stable // Note: This value is intended to be taken from the [UA client hints // API](https://wicg.github.io/ua-client-hints/#interface) // (`navigator.userAgentData.mobile`). If unavailable, this attribute // SHOULD be left unset. BrowserMobileKey = attribute.Key("browser.mobile") // BrowserUserAgentKey is the attribute Key conforming to the // "browser.user_agent" semantic conventions. It represents the full // user-agent string provided by the browser // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) // AppleWebKit/537.36 (KHTML, ' // 'like Gecko) Chrome/95.0.4638.54 Safari/537.36' // Note: The user-agent value SHOULD be provided only from browsers that do // not have a mechanism to retrieve brands and platform individually from // the User-Agent Client Hints API. To retrieve the value, the legacy // `navigator.userAgent` API can be used. BrowserUserAgentKey = attribute.Key("browser.user_agent") // BrowserLanguageKey is the attribute Key conforming to the // "browser.language" semantic conventions. It represents the preferred // language of the user using the browser // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'en', 'en-US', 'fr', 'fr-FR' // Note: This value is intended to be taken from the Navigator API // `navigator.language`. BrowserLanguageKey = attribute.Key("browser.language") ) // BrowserBrands returns an attribute KeyValue conforming to the // "browser.brands" semantic conventions. It represents the array of brand name // and version separated by a space func BrowserBrands(val ...string) attribute.KeyValue { return BrowserBrandsKey.StringSlice(val) } // BrowserPlatform returns an attribute KeyValue conforming to the // "browser.platform" semantic conventions. It represents the platform on which // the browser is running func BrowserPlatform(val string) attribute.KeyValue { return BrowserPlatformKey.String(val) } // BrowserMobile returns an attribute KeyValue conforming to the // "browser.mobile" semantic conventions. It represents a boolean that is true // if the browser is running on a mobile device func BrowserMobile(val bool) attribute.KeyValue { return BrowserMobileKey.Bool(val) } // BrowserUserAgent returns an attribute KeyValue conforming to the // "browser.user_agent" semantic conventions. It represents the full user-agent // string provided by the browser func BrowserUserAgent(val string) attribute.KeyValue { return BrowserUserAgentKey.String(val) } // BrowserLanguage returns an attribute KeyValue conforming to the // "browser.language" semantic conventions. It represents the preferred // language of the user using the browser func BrowserLanguage(val string) attribute.KeyValue { return BrowserLanguageKey.String(val) } // A cloud environment (e.g. GCP, Azure, AWS) const ( // CloudProviderKey is the attribute Key conforming to the "cloud.provider" // semantic conventions. It represents the name of the cloud provider. // // Type: Enum // RequirementLevel: Optional // Stability: stable CloudProviderKey = attribute.Key("cloud.provider") // CloudAccountIDKey is the attribute Key conforming to the // "cloud.account.id" semantic conventions. It represents the cloud account // ID the resource is assigned to. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '111111111111', 'opentelemetry' CloudAccountIDKey = attribute.Key("cloud.account.id") // CloudRegionKey is the attribute Key conforming to the "cloud.region" // semantic conventions. It represents the geographical region the resource // is running. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'us-central1', 'us-east-1' // Note: Refer to your provider's docs to see the available regions, for // example [Alibaba Cloud // regions](https://www.alibabacloud.com/help/doc-detail/40654.htm), [AWS // regions](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/), // [Azure // regions](https://azure.microsoft.com/en-us/global-infrastructure/geographies/), // [Google Cloud regions](https://cloud.google.com/about/locations), or // [Tencent Cloud // regions](https://intl.cloud.tencent.com/document/product/213/6091). CloudRegionKey = attribute.Key("cloud.region") // CloudAvailabilityZoneKey is the attribute Key conforming to the // "cloud.availability_zone" semantic conventions. It represents the cloud // regions often have multiple, isolated locations known as zones to // increase availability. Availability zone represents the zone where the // resource is running. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'us-east-1c' // Note: Availability zones are called "zones" on Alibaba Cloud and Google // Cloud. CloudAvailabilityZoneKey = attribute.Key("cloud.availability_zone") // CloudPlatformKey is the attribute Key conforming to the "cloud.platform" // semantic conventions. It represents the cloud platform in use. // // Type: Enum // RequirementLevel: Optional // Stability: stable // Note: The prefix of the service SHOULD match the one specified in // `cloud.provider`. CloudPlatformKey = attribute.Key("cloud.platform") ) var ( // Alibaba Cloud CloudProviderAlibabaCloud = CloudProviderKey.String("alibaba_cloud") // Amazon Web Services CloudProviderAWS = CloudProviderKey.String("aws") // Microsoft Azure CloudProviderAzure = CloudProviderKey.String("azure") // Google Cloud Platform CloudProviderGCP = CloudProviderKey.String("gcp") // IBM Cloud CloudProviderIbmCloud = CloudProviderKey.String("ibm_cloud") // Tencent Cloud CloudProviderTencentCloud = CloudProviderKey.String("tencent_cloud") ) var ( // Alibaba Cloud Elastic Compute Service CloudPlatformAlibabaCloudECS = CloudPlatformKey.String("alibaba_cloud_ecs") // Alibaba Cloud Function Compute CloudPlatformAlibabaCloudFc = CloudPlatformKey.String("alibaba_cloud_fc") // Red Hat OpenShift on Alibaba Cloud CloudPlatformAlibabaCloudOpenshift = CloudPlatformKey.String("alibaba_cloud_openshift") // AWS Elastic Compute Cloud CloudPlatformAWSEC2 = CloudPlatformKey.String("aws_ec2") // AWS Elastic Container Service CloudPlatformAWSECS = CloudPlatformKey.String("aws_ecs") // AWS Elastic Kubernetes Service CloudPlatformAWSEKS = CloudPlatformKey.String("aws_eks") // AWS Lambda CloudPlatformAWSLambda = CloudPlatformKey.String("aws_lambda") // AWS Elastic Beanstalk CloudPlatformAWSElasticBeanstalk = CloudPlatformKey.String("aws_elastic_beanstalk") // AWS App Runner CloudPlatformAWSAppRunner = CloudPlatformKey.String("aws_app_runner") // Red Hat OpenShift on AWS (ROSA) CloudPlatformAWSOpenshift = CloudPlatformKey.String("aws_openshift") // Azure Virtual Machines CloudPlatformAzureVM = CloudPlatformKey.String("azure_vm") // Azure Container Instances CloudPlatformAzureContainerInstances = CloudPlatformKey.String("azure_container_instances") // Azure Kubernetes Service CloudPlatformAzureAKS = CloudPlatformKey.String("azure_aks") // Azure Functions CloudPlatformAzureFunctions = CloudPlatformKey.String("azure_functions") // Azure App Service CloudPlatformAzureAppService = CloudPlatformKey.String("azure_app_service") // Azure Red Hat OpenShift CloudPlatformAzureOpenshift = CloudPlatformKey.String("azure_openshift") // Google Cloud Compute Engine (GCE) CloudPlatformGCPComputeEngine = CloudPlatformKey.String("gcp_compute_engine") // Google Cloud Run CloudPlatformGCPCloudRun = CloudPlatformKey.String("gcp_cloud_run") // Google Cloud Kubernetes Engine (GKE) CloudPlatformGCPKubernetesEngine = CloudPlatformKey.String("gcp_kubernetes_engine") // Google Cloud Functions (GCF) CloudPlatformGCPCloudFunctions = CloudPlatformKey.String("gcp_cloud_functions") // Google Cloud App Engine (GAE) CloudPlatformGCPAppEngine = CloudPlatformKey.String("gcp_app_engine") // Red Hat OpenShift on Google Cloud CloudPlatformGoogleCloudOpenshift = CloudPlatformKey.String("google_cloud_openshift") // Red Hat OpenShift on IBM Cloud CloudPlatformIbmCloudOpenshift = CloudPlatformKey.String("ibm_cloud_openshift") // Tencent Cloud Cloud Virtual Machine (CVM) CloudPlatformTencentCloudCvm = CloudPlatformKey.String("tencent_cloud_cvm") // Tencent Cloud Elastic Kubernetes Service (EKS) CloudPlatformTencentCloudEKS = CloudPlatformKey.String("tencent_cloud_eks") // Tencent Cloud Serverless Cloud Function (SCF) CloudPlatformTencentCloudScf = CloudPlatformKey.String("tencent_cloud_scf") ) // CloudAccountID returns an attribute KeyValue conforming to the // "cloud.account.id" semantic conventions. It represents the cloud account ID // the resource is assigned to. func CloudAccountID(val string) attribute.KeyValue { return CloudAccountIDKey.String(val) } // CloudRegion returns an attribute KeyValue conforming to the // "cloud.region" semantic conventions. It represents the geographical region // the resource is running. func CloudRegion(val string) attribute.KeyValue { return CloudRegionKey.String(val) } // CloudAvailabilityZone returns an attribute KeyValue conforming to the // "cloud.availability_zone" semantic conventions. It represents the cloud // regions often have multiple, isolated locations known as zones to increase // availability. Availability zone represents the zone where the resource is // running. func CloudAvailabilityZone(val string) attribute.KeyValue { return CloudAvailabilityZoneKey.String(val) } // Resources used by AWS Elastic Container Service (ECS). const ( // AWSECSContainerARNKey is the attribute Key conforming to the // "aws.ecs.container.arn" semantic conventions. It represents the Amazon // Resource Name (ARN) of an [ECS container // instance](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ECS_instances.html). // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: // 'arn:aws:ecs:us-west-1:123456789123:container/32624152-9086-4f0e-acae-1a75b14fe4d9' AWSECSContainerARNKey = attribute.Key("aws.ecs.container.arn") // AWSECSClusterARNKey is the attribute Key conforming to the // "aws.ecs.cluster.arn" semantic conventions. It represents the ARN of an // [ECS // cluster](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/clusters.html). // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'arn:aws:ecs:us-west-2:123456789123:cluster/my-cluster' AWSECSClusterARNKey = attribute.Key("aws.ecs.cluster.arn") // AWSECSLaunchtypeKey is the attribute Key conforming to the // "aws.ecs.launchtype" semantic conventions. It represents the [launch // type](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/launch_types.html) // for an ECS task. // // Type: Enum // RequirementLevel: Optional // Stability: stable AWSECSLaunchtypeKey = attribute.Key("aws.ecs.launchtype") // AWSECSTaskARNKey is the attribute Key conforming to the // "aws.ecs.task.arn" semantic conventions. It represents the ARN of an // [ECS task // definition](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definitions.html). // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: // 'arn:aws:ecs:us-west-1:123456789123:task/10838bed-421f-43ef-870a-f43feacbbb5b' AWSECSTaskARNKey = attribute.Key("aws.ecs.task.arn") // AWSECSTaskFamilyKey is the attribute Key conforming to the // "aws.ecs.task.family" semantic conventions. It represents the task // definition family this task definition is a member of. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'opentelemetry-family' AWSECSTaskFamilyKey = attribute.Key("aws.ecs.task.family") // AWSECSTaskRevisionKey is the attribute Key conforming to the // "aws.ecs.task.revision" semantic conventions. It represents the revision // for this task definition. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '8', '26' AWSECSTaskRevisionKey = attribute.Key("aws.ecs.task.revision") ) var ( // ec2 AWSECSLaunchtypeEC2 = AWSECSLaunchtypeKey.String("ec2") // fargate AWSECSLaunchtypeFargate = AWSECSLaunchtypeKey.String("fargate") ) // AWSECSContainerARN returns an attribute KeyValue conforming to the // "aws.ecs.container.arn" semantic conventions. It represents the Amazon // Resource Name (ARN) of an [ECS container // instance](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ECS_instances.html). func AWSECSContainerARN(val string) attribute.KeyValue { return AWSECSContainerARNKey.String(val) } // AWSECSClusterARN returns an attribute KeyValue conforming to the // "aws.ecs.cluster.arn" semantic conventions. It represents the ARN of an [ECS // cluster](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/clusters.html). func AWSECSClusterARN(val string) attribute.KeyValue { return AWSECSClusterARNKey.String(val) } // AWSECSTaskARN returns an attribute KeyValue conforming to the // "aws.ecs.task.arn" semantic conventions. It represents the ARN of an [ECS // task // definition](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definitions.html). func AWSECSTaskARN(val string) attribute.KeyValue { return AWSECSTaskARNKey.String(val) } // AWSECSTaskFamily returns an attribute KeyValue conforming to the // "aws.ecs.task.family" semantic conventions. It represents the task // definition family this task definition is a member of. func AWSECSTaskFamily(val string) attribute.KeyValue { return AWSECSTaskFamilyKey.String(val) } // AWSECSTaskRevision returns an attribute KeyValue conforming to the // "aws.ecs.task.revision" semantic conventions. It represents the revision for // this task definition. func AWSECSTaskRevision(val string) attribute.KeyValue { return AWSECSTaskRevisionKey.String(val) } // Resources used by AWS Elastic Kubernetes Service (EKS). const ( // AWSEKSClusterARNKey is the attribute Key conforming to the // "aws.eks.cluster.arn" semantic conventions. It represents the ARN of an // EKS cluster. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'arn:aws:ecs:us-west-2:123456789123:cluster/my-cluster' AWSEKSClusterARNKey = attribute.Key("aws.eks.cluster.arn") ) // AWSEKSClusterARN returns an attribute KeyValue conforming to the // "aws.eks.cluster.arn" semantic conventions. It represents the ARN of an EKS // cluster. func AWSEKSClusterARN(val string) attribute.KeyValue { return AWSEKSClusterARNKey.String(val) } // Resources specific to Amazon Web Services. const ( // AWSLogGroupNamesKey is the attribute Key conforming to the // "aws.log.group.names" semantic conventions. It represents the name(s) of // the AWS log group(s) an application is writing to. // // Type: string[] // RequirementLevel: Optional // Stability: stable // Examples: '/aws/lambda/my-function', 'opentelemetry-service' // Note: Multiple log groups must be supported for cases like // multi-container applications, where a single application has sidecar // containers, and each write to their own log group. AWSLogGroupNamesKey = attribute.Key("aws.log.group.names") // AWSLogGroupARNsKey is the attribute Key conforming to the // "aws.log.group.arns" semantic conventions. It represents the Amazon // Resource Name(s) (ARN) of the AWS log group(s). // // Type: string[] // RequirementLevel: Optional // Stability: stable // Examples: // 'arn:aws:logs:us-west-1:123456789012:log-group:/aws/my/group:*' // Note: See the [log group ARN format // documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam-access-control-overview-cwl.html#CWL_ARN_Format). AWSLogGroupARNsKey = attribute.Key("aws.log.group.arns") // AWSLogStreamNamesKey is the attribute Key conforming to the // "aws.log.stream.names" semantic conventions. It represents the name(s) // of the AWS log stream(s) an application is writing to. // // Type: string[] // RequirementLevel: Optional // Stability: stable // Examples: 'logs/main/10838bed-421f-43ef-870a-f43feacbbb5b' AWSLogStreamNamesKey = attribute.Key("aws.log.stream.names") // AWSLogStreamARNsKey is the attribute Key conforming to the // "aws.log.stream.arns" semantic conventions. It represents the ARN(s) of // the AWS log stream(s). // // Type: string[] // RequirementLevel: Optional // Stability: stable // Examples: // 'arn:aws:logs:us-west-1:123456789012:log-group:/aws/my/group:log-stream:logs/main/10838bed-421f-43ef-870a-f43feacbbb5b' // Note: See the [log stream ARN format // documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam-access-control-overview-cwl.html#CWL_ARN_Format). // One log group can contain several log streams, so these ARNs necessarily // identify both a log group and a log stream. AWSLogStreamARNsKey = attribute.Key("aws.log.stream.arns") ) // AWSLogGroupNames returns an attribute KeyValue conforming to the // "aws.log.group.names" semantic conventions. It represents the name(s) of the // AWS log group(s) an application is writing to. func AWSLogGroupNames(val ...string) attribute.KeyValue { return AWSLogGroupNamesKey.StringSlice(val) } // AWSLogGroupARNs returns an attribute KeyValue conforming to the // "aws.log.group.arns" semantic conventions. It represents the Amazon Resource // Name(s) (ARN) of the AWS log group(s). func AWSLogGroupARNs(val ...string) attribute.KeyValue { return AWSLogGroupARNsKey.StringSlice(val) } // AWSLogStreamNames returns an attribute KeyValue conforming to the // "aws.log.stream.names" semantic conventions. It represents the name(s) of // the AWS log stream(s) an application is writing to. func AWSLogStreamNames(val ...string) attribute.KeyValue { return AWSLogStreamNamesKey.StringSlice(val) } // AWSLogStreamARNs returns an attribute KeyValue conforming to the // "aws.log.stream.arns" semantic conventions. It represents the ARN(s) of the // AWS log stream(s). func AWSLogStreamARNs(val ...string) attribute.KeyValue { return AWSLogStreamARNsKey.StringSlice(val) } // A container instance. const ( // ContainerNameKey is the attribute Key conforming to the "container.name" // semantic conventions. It represents the container name used by container // runtime. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'opentelemetry-autoconf' ContainerNameKey = attribute.Key("container.name") // ContainerIDKey is the attribute Key conforming to the "container.id" // semantic conventions. It represents the container ID. Usually a UUID, as // for example used to [identify Docker // containers](https://docs.docker.com/engine/reference/run/#container-identification). // The UUID might be abbreviated. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'a3bf90e006b2' ContainerIDKey = attribute.Key("container.id") // ContainerRuntimeKey is the attribute Key conforming to the // "container.runtime" semantic conventions. It represents the container // runtime managing this container. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'docker', 'containerd', 'rkt' ContainerRuntimeKey = attribute.Key("container.runtime") // ContainerImageNameKey is the attribute Key conforming to the // "container.image.name" semantic conventions. It represents the name of // the image the container was built on. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'gcr.io/opentelemetry/operator' ContainerImageNameKey = attribute.Key("container.image.name") // ContainerImageTagKey is the attribute Key conforming to the // "container.image.tag" semantic conventions. It represents the container // image tag. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '0.1' ContainerImageTagKey = attribute.Key("container.image.tag") ) // ContainerName returns an attribute KeyValue conforming to the // "container.name" semantic conventions. It represents the container name used // by container runtime. func ContainerName(val string) attribute.KeyValue { return ContainerNameKey.String(val) } // ContainerID returns an attribute KeyValue conforming to the // "container.id" semantic conventions. It represents the container ID. Usually // a UUID, as for example used to [identify Docker // containers](https://docs.docker.com/engine/reference/run/#container-identification). // The UUID might be abbreviated. func ContainerID(val string) attribute.KeyValue { return ContainerIDKey.String(val) } // ContainerRuntime returns an attribute KeyValue conforming to the // "container.runtime" semantic conventions. It represents the container // runtime managing this container. func ContainerRuntime(val string) attribute.KeyValue { return ContainerRuntimeKey.String(val) } // ContainerImageName returns an attribute KeyValue conforming to the // "container.image.name" semantic conventions. It represents the name of the // image the container was built on. func ContainerImageName(val string) attribute.KeyValue { return ContainerImageNameKey.String(val) } // ContainerImageTag returns an attribute KeyValue conforming to the // "container.image.tag" semantic conventions. It represents the container // image tag. func ContainerImageTag(val string) attribute.KeyValue { return ContainerImageTagKey.String(val) } // The software deployment. const ( // DeploymentEnvironmentKey is the attribute Key conforming to the // "deployment.environment" semantic conventions. It represents the name of // the [deployment // environment](https://en.wikipedia.org/wiki/Deployment_environment) (aka // deployment tier). // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'staging', 'production' DeploymentEnvironmentKey = attribute.Key("deployment.environment") ) // DeploymentEnvironment returns an attribute KeyValue conforming to the // "deployment.environment" semantic conventions. It represents the name of the // [deployment // environment](https://en.wikipedia.org/wiki/Deployment_environment) (aka // deployment tier). func DeploymentEnvironment(val string) attribute.KeyValue { return DeploymentEnvironmentKey.String(val) } // The device on which the process represented by this resource is running. const ( // DeviceIDKey is the attribute Key conforming to the "device.id" semantic // conventions. It represents a unique identifier representing the device // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '2ab2916d-a51f-4ac8-80ee-45ac31a28092' // Note: The device identifier MUST only be defined using the values // outlined below. This value is not an advertising identifier and MUST NOT // be used as such. On iOS (Swift or Objective-C), this value MUST be equal // to the [vendor // identifier](https://developer.apple.com/documentation/uikit/uidevice/1620059-identifierforvendor). // On Android (Java or Kotlin), this value MUST be equal to the Firebase // Installation ID or a globally unique UUID which is persisted across // sessions in your application. More information can be found // [here](https://developer.android.com/training/articles/user-data-ids) on // best practices and exact implementation details. Caution should be taken // when storing personal data or anything which can identify a user. GDPR // and data protection laws may apply, ensure you do your own due // diligence. DeviceIDKey = attribute.Key("device.id") // DeviceModelIdentifierKey is the attribute Key conforming to the // "device.model.identifier" semantic conventions. It represents the model // identifier for the device // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'iPhone3,4', 'SM-G920F' // Note: It's recommended this value represents a machine readable version // of the model identifier rather than the market or consumer-friendly name // of the device. DeviceModelIdentifierKey = attribute.Key("device.model.identifier") // DeviceModelNameKey is the attribute Key conforming to the // "device.model.name" semantic conventions. It represents the marketing // name for the device model // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'iPhone 6s Plus', 'Samsung Galaxy S6' // Note: It's recommended this value represents a human readable version of // the device model rather than a machine readable alternative. DeviceModelNameKey = attribute.Key("device.model.name") // DeviceManufacturerKey is the attribute Key conforming to the // "device.manufacturer" semantic conventions. It represents the name of // the device manufacturer // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'Apple', 'Samsung' // Note: The Android OS provides this field via // [Build](https://developer.android.com/reference/android/os/Build#MANUFACTURER). // iOS apps SHOULD hardcode the value `Apple`. DeviceManufacturerKey = attribute.Key("device.manufacturer") ) // DeviceID returns an attribute KeyValue conforming to the "device.id" // semantic conventions. It represents a unique identifier representing the // device func DeviceID(val string) attribute.KeyValue { return DeviceIDKey.String(val) } // DeviceModelIdentifier returns an attribute KeyValue conforming to the // "device.model.identifier" semantic conventions. It represents the model // identifier for the device func DeviceModelIdentifier(val string) attribute.KeyValue { return DeviceModelIdentifierKey.String(val) } // DeviceModelName returns an attribute KeyValue conforming to the // "device.model.name" semantic conventions. It represents the marketing name // for the device model func DeviceModelName(val string) attribute.KeyValue { return DeviceModelNameKey.String(val) } // DeviceManufacturer returns an attribute KeyValue conforming to the // "device.manufacturer" semantic conventions. It represents the name of the // device manufacturer func DeviceManufacturer(val string) attribute.KeyValue { return DeviceManufacturerKey.String(val) } // A serverless instance. const ( // FaaSNameKey is the attribute Key conforming to the "faas.name" semantic // conventions. It represents the name of the single function that this // runtime instance executes. // // Type: string // RequirementLevel: Required // Stability: stable // Examples: 'my-function', 'myazurefunctionapp/some-function-name' // Note: This is the name of the function as configured/deployed on the // FaaS // platform and is usually different from the name of the callback // function (which may be stored in the // [`code.namespace`/`code.function`](../../trace/semantic_conventions/span-general.md#source-code-attributes) // span attributes). // // For some cloud providers, the above definition is ambiguous. The // following // definition of function name MUST be used for this attribute // (and consequently the span name) for the listed cloud // providers/products: // // * **Azure:** The full name `/`, i.e., function app name // followed by a forward slash followed by the function name (this form // can also be seen in the resource JSON for the function). // This means that a span attribute MUST be used, as an Azure function // app can host multiple functions that would usually share // a TracerProvider (see also the `faas.id` attribute). FaaSNameKey = attribute.Key("faas.name") // FaaSIDKey is the attribute Key conforming to the "faas.id" semantic // conventions. It represents the unique ID of the single function that // this runtime instance executes. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'arn:aws:lambda:us-west-2:123456789012:function:my-function' // Note: On some cloud providers, it may not be possible to determine the // full ID at startup, // so consider setting `faas.id` as a span attribute instead. // // The exact value to use for `faas.id` depends on the cloud provider: // // * **AWS Lambda:** The function // [ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html). // Take care not to use the "invoked ARN" directly but replace any // [alias // suffix](https://docs.aws.amazon.com/lambda/latest/dg/configuration-aliases.html) // with the resolved function version, as the same runtime instance may // be invokable with // multiple different aliases. // * **GCP:** The [URI of the // resource](https://cloud.google.com/iam/docs/full-resource-names) // * **Azure:** The [Fully Qualified Resource // ID](https://docs.microsoft.com/en-us/rest/api/resources/resources/get-by-id) // of the invoked function, // *not* the function app, having the form // `/subscriptions//resourceGroups//providers/Microsoft.Web/sites//functions/`. // This means that a span attribute MUST be used, as an Azure function // app can host multiple functions that would usually share // a TracerProvider. FaaSIDKey = attribute.Key("faas.id") // FaaSVersionKey is the attribute Key conforming to the "faas.version" // semantic conventions. It represents the immutable version of the // function being executed. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '26', 'pinkfroid-00002' // Note: Depending on the cloud provider and platform, use: // // * **AWS Lambda:** The [function // version](https://docs.aws.amazon.com/lambda/latest/dg/configuration-versions.html) // (an integer represented as a decimal string). // * **Google Cloud Run:** The // [revision](https://cloud.google.com/run/docs/managing/revisions) // (i.e., the function name plus the revision suffix). // * **Google Cloud Functions:** The value of the // [`K_REVISION` environment // variable](https://cloud.google.com/functions/docs/env-var#runtime_environment_variables_set_automatically). // * **Azure Functions:** Not applicable. Do not set this attribute. FaaSVersionKey = attribute.Key("faas.version") // FaaSInstanceKey is the attribute Key conforming to the "faas.instance" // semantic conventions. It represents the execution environment ID as a // string, that will be potentially reused for other invocations to the // same function/function version. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '2021/06/28/[$LATEST]2f399eb14537447da05ab2a2e39309de' // Note: * **AWS Lambda:** Use the (full) log stream name. FaaSInstanceKey = attribute.Key("faas.instance") // FaaSMaxMemoryKey is the attribute Key conforming to the // "faas.max_memory" semantic conventions. It represents the amount of // memory available to the serverless function in MiB. // // Type: int // RequirementLevel: Optional // Stability: stable // Examples: 128 // Note: It's recommended to set this attribute since e.g. too little // memory can easily stop a Java AWS Lambda function from working // correctly. On AWS Lambda, the environment variable // `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` provides this information. FaaSMaxMemoryKey = attribute.Key("faas.max_memory") ) // FaaSName returns an attribute KeyValue conforming to the "faas.name" // semantic conventions. It represents the name of the single function that // this runtime instance executes. func FaaSName(val string) attribute.KeyValue { return FaaSNameKey.String(val) } // FaaSID returns an attribute KeyValue conforming to the "faas.id" semantic // conventions. It represents the unique ID of the single function that this // runtime instance executes. func FaaSID(val string) attribute.KeyValue { return FaaSIDKey.String(val) } // FaaSVersion returns an attribute KeyValue conforming to the // "faas.version" semantic conventions. It represents the immutable version of // the function being executed. func FaaSVersion(val string) attribute.KeyValue { return FaaSVersionKey.String(val) } // FaaSInstance returns an attribute KeyValue conforming to the // "faas.instance" semantic conventions. It represents the execution // environment ID as a string, that will be potentially reused for other // invocations to the same function/function version. func FaaSInstance(val string) attribute.KeyValue { return FaaSInstanceKey.String(val) } // FaaSMaxMemory returns an attribute KeyValue conforming to the // "faas.max_memory" semantic conventions. It represents the amount of memory // available to the serverless function in MiB. func FaaSMaxMemory(val int) attribute.KeyValue { return FaaSMaxMemoryKey.Int(val) } // A host is defined as a general computing instance. const ( // HostIDKey is the attribute Key conforming to the "host.id" semantic // conventions. It represents the unique host ID. For Cloud, this must be // the instance_id assigned by the cloud provider. For non-containerized // Linux systems, the `machine-id` located in `/etc/machine-id` or // `/var/lib/dbus/machine-id` may be used. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'fdbf79e8af94cb7f9e8df36789187052' HostIDKey = attribute.Key("host.id") // HostNameKey is the attribute Key conforming to the "host.name" semantic // conventions. It represents the name of the host. On Unix systems, it may // contain what the hostname command returns, or the fully qualified // hostname, or another name specified by the user. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'opentelemetry-test' HostNameKey = attribute.Key("host.name") // HostTypeKey is the attribute Key conforming to the "host.type" semantic // conventions. It represents the type of host. For Cloud, this must be the // machine type. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'n1-standard-1' HostTypeKey = attribute.Key("host.type") // HostArchKey is the attribute Key conforming to the "host.arch" semantic // conventions. It represents the CPU architecture the host system is // running on. // // Type: Enum // RequirementLevel: Optional // Stability: stable HostArchKey = attribute.Key("host.arch") // HostImageNameKey is the attribute Key conforming to the // "host.image.name" semantic conventions. It represents the name of the VM // image or OS install the host was instantiated from. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'infra-ami-eks-worker-node-7d4ec78312', 'CentOS-8-x86_64-1905' HostImageNameKey = attribute.Key("host.image.name") // HostImageIDKey is the attribute Key conforming to the "host.image.id" // semantic conventions. It represents the vM image ID. For Cloud, this // value is from the provider. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'ami-07b06b442921831e5' HostImageIDKey = attribute.Key("host.image.id") // HostImageVersionKey is the attribute Key conforming to the // "host.image.version" semantic conventions. It represents the version // string of the VM image as defined in [Version // Attributes](README.md#version-attributes). // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '0.1' HostImageVersionKey = attribute.Key("host.image.version") ) var ( // AMD64 HostArchAMD64 = HostArchKey.String("amd64") // ARM32 HostArchARM32 = HostArchKey.String("arm32") // ARM64 HostArchARM64 = HostArchKey.String("arm64") // Itanium HostArchIA64 = HostArchKey.String("ia64") // 32-bit PowerPC HostArchPPC32 = HostArchKey.String("ppc32") // 64-bit PowerPC HostArchPPC64 = HostArchKey.String("ppc64") // IBM z/Architecture HostArchS390x = HostArchKey.String("s390x") // 32-bit x86 HostArchX86 = HostArchKey.String("x86") ) // HostID returns an attribute KeyValue conforming to the "host.id" semantic // conventions. It represents the unique host ID. For Cloud, this must be the // instance_id assigned by the cloud provider. For non-containerized Linux // systems, the `machine-id` located in `/etc/machine-id` or // `/var/lib/dbus/machine-id` may be used. func HostID(val string) attribute.KeyValue { return HostIDKey.String(val) } // HostName returns an attribute KeyValue conforming to the "host.name" // semantic conventions. It represents the name of the host. On Unix systems, // it may contain what the hostname command returns, or the fully qualified // hostname, or another name specified by the user. func HostName(val string) attribute.KeyValue { return HostNameKey.String(val) } // HostType returns an attribute KeyValue conforming to the "host.type" // semantic conventions. It represents the type of host. For Cloud, this must // be the machine type. func HostType(val string) attribute.KeyValue { return HostTypeKey.String(val) } // HostImageName returns an attribute KeyValue conforming to the // "host.image.name" semantic conventions. It represents the name of the VM // image or OS install the host was instantiated from. func HostImageName(val string) attribute.KeyValue { return HostImageNameKey.String(val) } // HostImageID returns an attribute KeyValue conforming to the // "host.image.id" semantic conventions. It represents the vM image ID. For // Cloud, this value is from the provider. func HostImageID(val string) attribute.KeyValue { return HostImageIDKey.String(val) } // HostImageVersion returns an attribute KeyValue conforming to the // "host.image.version" semantic conventions. It represents the version string // of the VM image as defined in [Version // Attributes](README.md#version-attributes). func HostImageVersion(val string) attribute.KeyValue { return HostImageVersionKey.String(val) } // A Kubernetes Cluster. const ( // K8SClusterNameKey is the attribute Key conforming to the // "k8s.cluster.name" semantic conventions. It represents the name of the // cluster. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'opentelemetry-cluster' K8SClusterNameKey = attribute.Key("k8s.cluster.name") ) // K8SClusterName returns an attribute KeyValue conforming to the // "k8s.cluster.name" semantic conventions. It represents the name of the // cluster. func K8SClusterName(val string) attribute.KeyValue { return K8SClusterNameKey.String(val) } // A Kubernetes Node object. const ( // K8SNodeNameKey is the attribute Key conforming to the "k8s.node.name" // semantic conventions. It represents the name of the Node. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'node-1' K8SNodeNameKey = attribute.Key("k8s.node.name") // K8SNodeUIDKey is the attribute Key conforming to the "k8s.node.uid" // semantic conventions. It represents the UID of the Node. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '1eb3a0c6-0477-4080-a9cb-0cb7db65c6a2' K8SNodeUIDKey = attribute.Key("k8s.node.uid") ) // K8SNodeName returns an attribute KeyValue conforming to the // "k8s.node.name" semantic conventions. It represents the name of the Node. func K8SNodeName(val string) attribute.KeyValue { return K8SNodeNameKey.String(val) } // K8SNodeUID returns an attribute KeyValue conforming to the "k8s.node.uid" // semantic conventions. It represents the UID of the Node. func K8SNodeUID(val string) attribute.KeyValue { return K8SNodeUIDKey.String(val) } // A Kubernetes Namespace. const ( // K8SNamespaceNameKey is the attribute Key conforming to the // "k8s.namespace.name" semantic conventions. It represents the name of the // namespace that the pod is running in. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'default' K8SNamespaceNameKey = attribute.Key("k8s.namespace.name") ) // K8SNamespaceName returns an attribute KeyValue conforming to the // "k8s.namespace.name" semantic conventions. It represents the name of the // namespace that the pod is running in. func K8SNamespaceName(val string) attribute.KeyValue { return K8SNamespaceNameKey.String(val) } // A Kubernetes Pod object. const ( // K8SPodUIDKey is the attribute Key conforming to the "k8s.pod.uid" // semantic conventions. It represents the UID of the Pod. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SPodUIDKey = attribute.Key("k8s.pod.uid") // K8SPodNameKey is the attribute Key conforming to the "k8s.pod.name" // semantic conventions. It represents the name of the Pod. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'opentelemetry-pod-autoconf' K8SPodNameKey = attribute.Key("k8s.pod.name") ) // K8SPodUID returns an attribute KeyValue conforming to the "k8s.pod.uid" // semantic conventions. It represents the UID of the Pod. func K8SPodUID(val string) attribute.KeyValue { return K8SPodUIDKey.String(val) } // K8SPodName returns an attribute KeyValue conforming to the "k8s.pod.name" // semantic conventions. It represents the name of the Pod. func K8SPodName(val string) attribute.KeyValue { return K8SPodNameKey.String(val) } // A container in a // [PodTemplate](https://kubernetes.io/docs/concepts/workloads/pods/#pod-templates). const ( // K8SContainerNameKey is the attribute Key conforming to the // "k8s.container.name" semantic conventions. It represents the name of the // Container from Pod specification, must be unique within a Pod. Container // runtime usually uses different globally unique name (`container.name`). // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'redis' K8SContainerNameKey = attribute.Key("k8s.container.name") // K8SContainerRestartCountKey is the attribute Key conforming to the // "k8s.container.restart_count" semantic conventions. It represents the // number of times the container was restarted. This attribute can be used // to identify a particular container (running or stopped) within a // container spec. // // Type: int // RequirementLevel: Optional // Stability: stable // Examples: 0, 2 K8SContainerRestartCountKey = attribute.Key("k8s.container.restart_count") ) // K8SContainerName returns an attribute KeyValue conforming to the // "k8s.container.name" semantic conventions. It represents the name of the // Container from Pod specification, must be unique within a Pod. Container // runtime usually uses different globally unique name (`container.name`). func K8SContainerName(val string) attribute.KeyValue { return K8SContainerNameKey.String(val) } // K8SContainerRestartCount returns an attribute KeyValue conforming to the // "k8s.container.restart_count" semantic conventions. It represents the number // of times the container was restarted. This attribute can be used to identify // a particular container (running or stopped) within a container spec. func K8SContainerRestartCount(val int) attribute.KeyValue { return K8SContainerRestartCountKey.Int(val) } // A Kubernetes ReplicaSet object. const ( // K8SReplicaSetUIDKey is the attribute Key conforming to the // "k8s.replicaset.uid" semantic conventions. It represents the UID of the // ReplicaSet. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SReplicaSetUIDKey = attribute.Key("k8s.replicaset.uid") // K8SReplicaSetNameKey is the attribute Key conforming to the // "k8s.replicaset.name" semantic conventions. It represents the name of // the ReplicaSet. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'opentelemetry' K8SReplicaSetNameKey = attribute.Key("k8s.replicaset.name") ) // K8SReplicaSetUID returns an attribute KeyValue conforming to the // "k8s.replicaset.uid" semantic conventions. It represents the UID of the // ReplicaSet. func K8SReplicaSetUID(val string) attribute.KeyValue { return K8SReplicaSetUIDKey.String(val) } // K8SReplicaSetName returns an attribute KeyValue conforming to the // "k8s.replicaset.name" semantic conventions. It represents the name of the // ReplicaSet. func K8SReplicaSetName(val string) attribute.KeyValue { return K8SReplicaSetNameKey.String(val) } // A Kubernetes Deployment object. const ( // K8SDeploymentUIDKey is the attribute Key conforming to the // "k8s.deployment.uid" semantic conventions. It represents the UID of the // Deployment. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SDeploymentUIDKey = attribute.Key("k8s.deployment.uid") // K8SDeploymentNameKey is the attribute Key conforming to the // "k8s.deployment.name" semantic conventions. It represents the name of // the Deployment. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'opentelemetry' K8SDeploymentNameKey = attribute.Key("k8s.deployment.name") ) // K8SDeploymentUID returns an attribute KeyValue conforming to the // "k8s.deployment.uid" semantic conventions. It represents the UID of the // Deployment. func K8SDeploymentUID(val string) attribute.KeyValue { return K8SDeploymentUIDKey.String(val) } // K8SDeploymentName returns an attribute KeyValue conforming to the // "k8s.deployment.name" semantic conventions. It represents the name of the // Deployment. func K8SDeploymentName(val string) attribute.KeyValue { return K8SDeploymentNameKey.String(val) } // A Kubernetes StatefulSet object. const ( // K8SStatefulSetUIDKey is the attribute Key conforming to the // "k8s.statefulset.uid" semantic conventions. It represents the UID of the // StatefulSet. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SStatefulSetUIDKey = attribute.Key("k8s.statefulset.uid") // K8SStatefulSetNameKey is the attribute Key conforming to the // "k8s.statefulset.name" semantic conventions. It represents the name of // the StatefulSet. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'opentelemetry' K8SStatefulSetNameKey = attribute.Key("k8s.statefulset.name") ) // K8SStatefulSetUID returns an attribute KeyValue conforming to the // "k8s.statefulset.uid" semantic conventions. It represents the UID of the // StatefulSet. func K8SStatefulSetUID(val string) attribute.KeyValue { return K8SStatefulSetUIDKey.String(val) } // K8SStatefulSetName returns an attribute KeyValue conforming to the // "k8s.statefulset.name" semantic conventions. It represents the name of the // StatefulSet. func K8SStatefulSetName(val string) attribute.KeyValue { return K8SStatefulSetNameKey.String(val) } // A Kubernetes DaemonSet object. const ( // K8SDaemonSetUIDKey is the attribute Key conforming to the // "k8s.daemonset.uid" semantic conventions. It represents the UID of the // DaemonSet. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SDaemonSetUIDKey = attribute.Key("k8s.daemonset.uid") // K8SDaemonSetNameKey is the attribute Key conforming to the // "k8s.daemonset.name" semantic conventions. It represents the name of the // DaemonSet. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'opentelemetry' K8SDaemonSetNameKey = attribute.Key("k8s.daemonset.name") ) // K8SDaemonSetUID returns an attribute KeyValue conforming to the // "k8s.daemonset.uid" semantic conventions. It represents the UID of the // DaemonSet. func K8SDaemonSetUID(val string) attribute.KeyValue { return K8SDaemonSetUIDKey.String(val) } // K8SDaemonSetName returns an attribute KeyValue conforming to the // "k8s.daemonset.name" semantic conventions. It represents the name of the // DaemonSet. func K8SDaemonSetName(val string) attribute.KeyValue { return K8SDaemonSetNameKey.String(val) } // A Kubernetes Job object. const ( // K8SJobUIDKey is the attribute Key conforming to the "k8s.job.uid" // semantic conventions. It represents the UID of the Job. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SJobUIDKey = attribute.Key("k8s.job.uid") // K8SJobNameKey is the attribute Key conforming to the "k8s.job.name" // semantic conventions. It represents the name of the Job. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'opentelemetry' K8SJobNameKey = attribute.Key("k8s.job.name") ) // K8SJobUID returns an attribute KeyValue conforming to the "k8s.job.uid" // semantic conventions. It represents the UID of the Job. func K8SJobUID(val string) attribute.KeyValue { return K8SJobUIDKey.String(val) } // K8SJobName returns an attribute KeyValue conforming to the "k8s.job.name" // semantic conventions. It represents the name of the Job. func K8SJobName(val string) attribute.KeyValue { return K8SJobNameKey.String(val) } // A Kubernetes CronJob object. const ( // K8SCronJobUIDKey is the attribute Key conforming to the // "k8s.cronjob.uid" semantic conventions. It represents the UID of the // CronJob. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff' K8SCronJobUIDKey = attribute.Key("k8s.cronjob.uid") // K8SCronJobNameKey is the attribute Key conforming to the // "k8s.cronjob.name" semantic conventions. It represents the name of the // CronJob. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'opentelemetry' K8SCronJobNameKey = attribute.Key("k8s.cronjob.name") ) // K8SCronJobUID returns an attribute KeyValue conforming to the // "k8s.cronjob.uid" semantic conventions. It represents the UID of the // CronJob. func K8SCronJobUID(val string) attribute.KeyValue { return K8SCronJobUIDKey.String(val) } // K8SCronJobName returns an attribute KeyValue conforming to the // "k8s.cronjob.name" semantic conventions. It represents the name of the // CronJob. func K8SCronJobName(val string) attribute.KeyValue { return K8SCronJobNameKey.String(val) } // The operating system (OS) on which the process represented by this resource // is running. const ( // OSTypeKey is the attribute Key conforming to the "os.type" semantic // conventions. It represents the operating system type. // // Type: Enum // RequirementLevel: Required // Stability: stable OSTypeKey = attribute.Key("os.type") // OSDescriptionKey is the attribute Key conforming to the "os.description" // semantic conventions. It represents the human readable (not intended to // be parsed) OS version information, like e.g. reported by `ver` or // `lsb_release -a` commands. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'Microsoft Windows [Version 10.0.18363.778]', 'Ubuntu 18.04.1 // LTS' OSDescriptionKey = attribute.Key("os.description") // OSNameKey is the attribute Key conforming to the "os.name" semantic // conventions. It represents the human readable operating system name. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'iOS', 'Android', 'Ubuntu' OSNameKey = attribute.Key("os.name") // OSVersionKey is the attribute Key conforming to the "os.version" // semantic conventions. It represents the version string of the operating // system as defined in [Version // Attributes](../../resource/semantic_conventions/README.md#version-attributes). // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '14.2.1', '18.04.1' OSVersionKey = attribute.Key("os.version") ) var ( // Microsoft Windows OSTypeWindows = OSTypeKey.String("windows") // Linux OSTypeLinux = OSTypeKey.String("linux") // Apple Darwin OSTypeDarwin = OSTypeKey.String("darwin") // FreeBSD OSTypeFreeBSD = OSTypeKey.String("freebsd") // NetBSD OSTypeNetBSD = OSTypeKey.String("netbsd") // OpenBSD OSTypeOpenBSD = OSTypeKey.String("openbsd") // DragonFly BSD OSTypeDragonflyBSD = OSTypeKey.String("dragonflybsd") // HP-UX (Hewlett Packard Unix) OSTypeHPUX = OSTypeKey.String("hpux") // AIX (Advanced Interactive eXecutive) OSTypeAIX = OSTypeKey.String("aix") // SunOS, Oracle Solaris OSTypeSolaris = OSTypeKey.String("solaris") // IBM z/OS OSTypeZOS = OSTypeKey.String("z_os") ) // OSDescription returns an attribute KeyValue conforming to the // "os.description" semantic conventions. It represents the human readable (not // intended to be parsed) OS version information, like e.g. reported by `ver` // or `lsb_release -a` commands. func OSDescription(val string) attribute.KeyValue { return OSDescriptionKey.String(val) } // OSName returns an attribute KeyValue conforming to the "os.name" semantic // conventions. It represents the human readable operating system name. func OSName(val string) attribute.KeyValue { return OSNameKey.String(val) } // OSVersion returns an attribute KeyValue conforming to the "os.version" // semantic conventions. It represents the version string of the operating // system as defined in [Version // Attributes](../../resource/semantic_conventions/README.md#version-attributes). func OSVersion(val string) attribute.KeyValue { return OSVersionKey.String(val) } // An operating system process. const ( // ProcessPIDKey is the attribute Key conforming to the "process.pid" // semantic conventions. It represents the process identifier (PID). // // Type: int // RequirementLevel: Optional // Stability: stable // Examples: 1234 ProcessPIDKey = attribute.Key("process.pid") // ProcessParentPIDKey is the attribute Key conforming to the // "process.parent_pid" semantic conventions. It represents the parent // Process identifier (PID). // // Type: int // RequirementLevel: Optional // Stability: stable // Examples: 111 ProcessParentPIDKey = attribute.Key("process.parent_pid") // ProcessExecutableNameKey is the attribute Key conforming to the // "process.executable.name" semantic conventions. It represents the name // of the process executable. On Linux based systems, can be set to the // `Name` in `proc/[pid]/status`. On Windows, can be set to the base name // of `GetProcessImageFileNameW`. // // Type: string // RequirementLevel: ConditionallyRequired (See alternative attributes // below.) // Stability: stable // Examples: 'otelcol' ProcessExecutableNameKey = attribute.Key("process.executable.name") // ProcessExecutablePathKey is the attribute Key conforming to the // "process.executable.path" semantic conventions. It represents the full // path to the process executable. On Linux based systems, can be set to // the target of `proc/[pid]/exe`. On Windows, can be set to the result of // `GetProcessImageFileNameW`. // // Type: string // RequirementLevel: ConditionallyRequired (See alternative attributes // below.) // Stability: stable // Examples: '/usr/bin/cmd/otelcol' ProcessExecutablePathKey = attribute.Key("process.executable.path") // ProcessCommandKey is the attribute Key conforming to the // "process.command" semantic conventions. It represents the command used // to launch the process (i.e. the command name). On Linux based systems, // can be set to the zeroth string in `proc/[pid]/cmdline`. On Windows, can // be set to the first parameter extracted from `GetCommandLineW`. // // Type: string // RequirementLevel: ConditionallyRequired (See alternative attributes // below.) // Stability: stable // Examples: 'cmd/otelcol' ProcessCommandKey = attribute.Key("process.command") // ProcessCommandLineKey is the attribute Key conforming to the // "process.command_line" semantic conventions. It represents the full // command used to launch the process as a single string representing the // full command. On Windows, can be set to the result of `GetCommandLineW`. // Do not set this if you have to assemble it just for monitoring; use // `process.command_args` instead. // // Type: string // RequirementLevel: ConditionallyRequired (See alternative attributes // below.) // Stability: stable // Examples: 'C:\\cmd\\otecol --config="my directory\\config.yaml"' ProcessCommandLineKey = attribute.Key("process.command_line") // ProcessCommandArgsKey is the attribute Key conforming to the // "process.command_args" semantic conventions. It represents the all the // command arguments (including the command/executable itself) as received // by the process. On Linux-based systems (and some other Unixoid systems // supporting procfs), can be set according to the list of null-delimited // strings extracted from `proc/[pid]/cmdline`. For libc-based executables, // this would be the full argv vector passed to `main`. // // Type: string[] // RequirementLevel: ConditionallyRequired (See alternative attributes // below.) // Stability: stable // Examples: 'cmd/otecol', '--config=config.yaml' ProcessCommandArgsKey = attribute.Key("process.command_args") // ProcessOwnerKey is the attribute Key conforming to the "process.owner" // semantic conventions. It represents the username of the user that owns // the process. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'root' ProcessOwnerKey = attribute.Key("process.owner") ) // ProcessPID returns an attribute KeyValue conforming to the "process.pid" // semantic conventions. It represents the process identifier (PID). func ProcessPID(val int) attribute.KeyValue { return ProcessPIDKey.Int(val) } // ProcessParentPID returns an attribute KeyValue conforming to the // "process.parent_pid" semantic conventions. It represents the parent Process // identifier (PID). func ProcessParentPID(val int) attribute.KeyValue { return ProcessParentPIDKey.Int(val) } // ProcessExecutableName returns an attribute KeyValue conforming to the // "process.executable.name" semantic conventions. It represents the name of // the process executable. On Linux based systems, can be set to the `Name` in // `proc/[pid]/status`. On Windows, can be set to the base name of // `GetProcessImageFileNameW`. func ProcessExecutableName(val string) attribute.KeyValue { return ProcessExecutableNameKey.String(val) } // ProcessExecutablePath returns an attribute KeyValue conforming to the // "process.executable.path" semantic conventions. It represents the full path // to the process executable. On Linux based systems, can be set to the target // of `proc/[pid]/exe`. On Windows, can be set to the result of // `GetProcessImageFileNameW`. func ProcessExecutablePath(val string) attribute.KeyValue { return ProcessExecutablePathKey.String(val) } // ProcessCommand returns an attribute KeyValue conforming to the // "process.command" semantic conventions. It represents the command used to // launch the process (i.e. the command name). On Linux based systems, can be // set to the zeroth string in `proc/[pid]/cmdline`. On Windows, can be set to // the first parameter extracted from `GetCommandLineW`. func ProcessCommand(val string) attribute.KeyValue { return ProcessCommandKey.String(val) } // ProcessCommandLine returns an attribute KeyValue conforming to the // "process.command_line" semantic conventions. It represents the full command // used to launch the process as a single string representing the full command. // On Windows, can be set to the result of `GetCommandLineW`. Do not set this // if you have to assemble it just for monitoring; use `process.command_args` // instead. func ProcessCommandLine(val string) attribute.KeyValue { return ProcessCommandLineKey.String(val) } // ProcessCommandArgs returns an attribute KeyValue conforming to the // "process.command_args" semantic conventions. It represents the all the // command arguments (including the command/executable itself) as received by // the process. On Linux-based systems (and some other Unixoid systems // supporting procfs), can be set according to the list of null-delimited // strings extracted from `proc/[pid]/cmdline`. For libc-based executables, // this would be the full argv vector passed to `main`. func ProcessCommandArgs(val ...string) attribute.KeyValue { return ProcessCommandArgsKey.StringSlice(val) } // ProcessOwner returns an attribute KeyValue conforming to the // "process.owner" semantic conventions. It represents the username of the user // that owns the process. func ProcessOwner(val string) attribute.KeyValue { return ProcessOwnerKey.String(val) } // The single (language) runtime instance which is monitored. const ( // ProcessRuntimeNameKey is the attribute Key conforming to the // "process.runtime.name" semantic conventions. It represents the name of // the runtime of this process. For compiled native binaries, this SHOULD // be the name of the compiler. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'OpenJDK Runtime Environment' ProcessRuntimeNameKey = attribute.Key("process.runtime.name") // ProcessRuntimeVersionKey is the attribute Key conforming to the // "process.runtime.version" semantic conventions. It represents the // version of the runtime of this process, as returned by the runtime // without modification. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '14.0.2' ProcessRuntimeVersionKey = attribute.Key("process.runtime.version") // ProcessRuntimeDescriptionKey is the attribute Key conforming to the // "process.runtime.description" semantic conventions. It represents an // additional description about the runtime of the process, for example a // specific vendor customization of the runtime environment. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'Eclipse OpenJ9 Eclipse OpenJ9 VM openj9-0.21.0' ProcessRuntimeDescriptionKey = attribute.Key("process.runtime.description") ) // ProcessRuntimeName returns an attribute KeyValue conforming to the // "process.runtime.name" semantic conventions. It represents the name of the // runtime of this process. For compiled native binaries, this SHOULD be the // name of the compiler. func ProcessRuntimeName(val string) attribute.KeyValue { return ProcessRuntimeNameKey.String(val) } // ProcessRuntimeVersion returns an attribute KeyValue conforming to the // "process.runtime.version" semantic conventions. It represents the version of // the runtime of this process, as returned by the runtime without // modification. func ProcessRuntimeVersion(val string) attribute.KeyValue { return ProcessRuntimeVersionKey.String(val) } // ProcessRuntimeDescription returns an attribute KeyValue conforming to the // "process.runtime.description" semantic conventions. It represents an // additional description about the runtime of the process, for example a // specific vendor customization of the runtime environment. func ProcessRuntimeDescription(val string) attribute.KeyValue { return ProcessRuntimeDescriptionKey.String(val) } // A service instance. const ( // ServiceNameKey is the attribute Key conforming to the "service.name" // semantic conventions. It represents the logical name of the service. // // Type: string // RequirementLevel: Required // Stability: stable // Examples: 'shoppingcart' // Note: MUST be the same for all instances of horizontally scaled // services. If the value was not specified, SDKs MUST fallback to // `unknown_service:` concatenated with // [`process.executable.name`](process.md#process), e.g. // `unknown_service:bash`. If `process.executable.name` is not available, // the value MUST be set to `unknown_service`. ServiceNameKey = attribute.Key("service.name") // ServiceNamespaceKey is the attribute Key conforming to the // "service.namespace" semantic conventions. It represents a namespace for // `service.name`. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'Shop' // Note: A string value having a meaning that helps to distinguish a group // of services, for example the team name that owns a group of services. // `service.name` is expected to be unique within the same namespace. If // `service.namespace` is not specified in the Resource then `service.name` // is expected to be unique for all services that have no explicit // namespace defined (so the empty/unspecified namespace is simply one more // valid namespace). Zero-length namespace string is assumed equal to // unspecified namespace. ServiceNamespaceKey = attribute.Key("service.namespace") // ServiceInstanceIDKey is the attribute Key conforming to the // "service.instance.id" semantic conventions. It represents the string ID // of the service instance. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '627cc493-f310-47de-96bd-71410b7dec09' // Note: MUST be unique for each instance of the same // `service.namespace,service.name` pair (in other words // `service.namespace,service.name,service.instance.id` triplet MUST be // globally unique). The ID helps to distinguish instances of the same // service that exist at the same time (e.g. instances of a horizontally // scaled service). It is preferable for the ID to be persistent and stay // the same for the lifetime of the service instance, however it is // acceptable that the ID is ephemeral and changes during important // lifetime events for the service (e.g. service restarts). If the service // has no inherent unique ID that can be used as the value of this // attribute it is recommended to generate a random Version 1 or Version 4 // RFC 4122 UUID (services aiming for reproducible UUIDs may also use // Version 5, see RFC 4122 for more recommendations). ServiceInstanceIDKey = attribute.Key("service.instance.id") // ServiceVersionKey is the attribute Key conforming to the // "service.version" semantic conventions. It represents the version string // of the service API or implementation. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '2.0.0' ServiceVersionKey = attribute.Key("service.version") ) // ServiceName returns an attribute KeyValue conforming to the // "service.name" semantic conventions. It represents the logical name of the // service. func ServiceName(val string) attribute.KeyValue { return ServiceNameKey.String(val) } // ServiceNamespace returns an attribute KeyValue conforming to the // "service.namespace" semantic conventions. It represents a namespace for // `service.name`. func ServiceNamespace(val string) attribute.KeyValue { return ServiceNamespaceKey.String(val) } // ServiceInstanceID returns an attribute KeyValue conforming to the // "service.instance.id" semantic conventions. It represents the string ID of // the service instance. func ServiceInstanceID(val string) attribute.KeyValue { return ServiceInstanceIDKey.String(val) } // ServiceVersion returns an attribute KeyValue conforming to the // "service.version" semantic conventions. It represents the version string of // the service API or implementation. func ServiceVersion(val string) attribute.KeyValue { return ServiceVersionKey.String(val) } // The telemetry SDK used to capture data recorded by the instrumentation // libraries. const ( // TelemetrySDKNameKey is the attribute Key conforming to the // "telemetry.sdk.name" semantic conventions. It represents the name of the // telemetry SDK as defined above. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'opentelemetry' TelemetrySDKNameKey = attribute.Key("telemetry.sdk.name") // TelemetrySDKLanguageKey is the attribute Key conforming to the // "telemetry.sdk.language" semantic conventions. It represents the // language of the telemetry SDK. // // Type: Enum // RequirementLevel: Optional // Stability: stable TelemetrySDKLanguageKey = attribute.Key("telemetry.sdk.language") // TelemetrySDKVersionKey is the attribute Key conforming to the // "telemetry.sdk.version" semantic conventions. It represents the version // string of the telemetry SDK. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '1.2.3' TelemetrySDKVersionKey = attribute.Key("telemetry.sdk.version") // TelemetryAutoVersionKey is the attribute Key conforming to the // "telemetry.auto.version" semantic conventions. It represents the version // string of the auto instrumentation agent, if used. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '1.2.3' TelemetryAutoVersionKey = attribute.Key("telemetry.auto.version") ) var ( // cpp TelemetrySDKLanguageCPP = TelemetrySDKLanguageKey.String("cpp") // dotnet TelemetrySDKLanguageDotnet = TelemetrySDKLanguageKey.String("dotnet") // erlang TelemetrySDKLanguageErlang = TelemetrySDKLanguageKey.String("erlang") // go TelemetrySDKLanguageGo = TelemetrySDKLanguageKey.String("go") // java TelemetrySDKLanguageJava = TelemetrySDKLanguageKey.String("java") // nodejs TelemetrySDKLanguageNodejs = TelemetrySDKLanguageKey.String("nodejs") // php TelemetrySDKLanguagePHP = TelemetrySDKLanguageKey.String("php") // python TelemetrySDKLanguagePython = TelemetrySDKLanguageKey.String("python") // ruby TelemetrySDKLanguageRuby = TelemetrySDKLanguageKey.String("ruby") // webjs TelemetrySDKLanguageWebjs = TelemetrySDKLanguageKey.String("webjs") // swift TelemetrySDKLanguageSwift = TelemetrySDKLanguageKey.String("swift") ) // TelemetrySDKName returns an attribute KeyValue conforming to the // "telemetry.sdk.name" semantic conventions. It represents the name of the // telemetry SDK as defined above. func TelemetrySDKName(val string) attribute.KeyValue { return TelemetrySDKNameKey.String(val) } // TelemetrySDKVersion returns an attribute KeyValue conforming to the // "telemetry.sdk.version" semantic conventions. It represents the version // string of the telemetry SDK. func TelemetrySDKVersion(val string) attribute.KeyValue { return TelemetrySDKVersionKey.String(val) } // TelemetryAutoVersion returns an attribute KeyValue conforming to the // "telemetry.auto.version" semantic conventions. It represents the version // string of the auto instrumentation agent, if used. func TelemetryAutoVersion(val string) attribute.KeyValue { return TelemetryAutoVersionKey.String(val) } // Resource describing the packaged software running the application code. Web // engines are typically executed using process.runtime. const ( // WebEngineNameKey is the attribute Key conforming to the "webengine.name" // semantic conventions. It represents the name of the web engine. // // Type: string // RequirementLevel: Required // Stability: stable // Examples: 'WildFly' WebEngineNameKey = attribute.Key("webengine.name") // WebEngineVersionKey is the attribute Key conforming to the // "webengine.version" semantic conventions. It represents the version of // the web engine. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '21.0.0' WebEngineVersionKey = attribute.Key("webengine.version") // WebEngineDescriptionKey is the attribute Key conforming to the // "webengine.description" semantic conventions. It represents the // additional description of the web engine (e.g. detailed version and // edition information). // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'WildFly Full 21.0.0.Final (WildFly Core 13.0.1.Final) - // 2.2.2.Final' WebEngineDescriptionKey = attribute.Key("webengine.description") ) // WebEngineName returns an attribute KeyValue conforming to the // "webengine.name" semantic conventions. It represents the name of the web // engine. func WebEngineName(val string) attribute.KeyValue { return WebEngineNameKey.String(val) } // WebEngineVersion returns an attribute KeyValue conforming to the // "webengine.version" semantic conventions. It represents the version of the // web engine. func WebEngineVersion(val string) attribute.KeyValue { return WebEngineVersionKey.String(val) } // WebEngineDescription returns an attribute KeyValue conforming to the // "webengine.description" semantic conventions. It represents the additional // description of the web engine (e.g. detailed version and edition // information). func WebEngineDescription(val string) attribute.KeyValue { return WebEngineDescriptionKey.String(val) } // Attributes used by non-OTLP exporters to represent OpenTelemetry Scope's // concepts. const ( // OtelScopeNameKey is the attribute Key conforming to the // "otel.scope.name" semantic conventions. It represents the name of the // instrumentation scope - (`InstrumentationScope.Name` in OTLP). // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'io.opentelemetry.contrib.mongodb' OtelScopeNameKey = attribute.Key("otel.scope.name") // OtelScopeVersionKey is the attribute Key conforming to the // "otel.scope.version" semantic conventions. It represents the version of // the instrumentation scope - (`InstrumentationScope.Version` in OTLP). // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '1.0.0' OtelScopeVersionKey = attribute.Key("otel.scope.version") ) // OtelScopeName returns an attribute KeyValue conforming to the // "otel.scope.name" semantic conventions. It represents the name of the // instrumentation scope - (`InstrumentationScope.Name` in OTLP). func OtelScopeName(val string) attribute.KeyValue { return OtelScopeNameKey.String(val) } // OtelScopeVersion returns an attribute KeyValue conforming to the // "otel.scope.version" semantic conventions. It represents the version of the // instrumentation scope - (`InstrumentationScope.Version` in OTLP). func OtelScopeVersion(val string) attribute.KeyValue { return OtelScopeVersionKey.String(val) } // Span attributes used by non-OTLP exporters to represent OpenTelemetry // Scope's concepts. const ( // OtelLibraryNameKey is the attribute Key conforming to the // "otel.library.name" semantic conventions. It represents the deprecated, // use the `otel.scope.name` attribute. // // Type: string // RequirementLevel: Optional // Stability: deprecated // Examples: 'io.opentelemetry.contrib.mongodb' OtelLibraryNameKey = attribute.Key("otel.library.name") // OtelLibraryVersionKey is the attribute Key conforming to the // "otel.library.version" semantic conventions. It represents the // deprecated, use the `otel.scope.version` attribute. // // Type: string // RequirementLevel: Optional // Stability: deprecated // Examples: '1.0.0' OtelLibraryVersionKey = attribute.Key("otel.library.version") ) // OtelLibraryName returns an attribute KeyValue conforming to the // "otel.library.name" semantic conventions. It represents the deprecated, use // the `otel.scope.name` attribute. func OtelLibraryName(val string) attribute.KeyValue { return OtelLibraryNameKey.String(val) } // OtelLibraryVersion returns an attribute KeyValue conforming to the // "otel.library.version" semantic conventions. It represents the deprecated, // use the `otel.scope.version` attribute. func OtelLibraryVersion(val string) attribute.KeyValue { return OtelLibraryVersionKey.String(val) } ================================================ FILE: vendor/go.opentelemetry.io/otel/semconv/v1.17.0/schema.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package semconv // import "go.opentelemetry.io/otel/semconv/v1.17.0" // SchemaURL is the schema URL that matches the version of the semantic conventions // that this package defines. Semconv packages starting from v1.4.0 must declare // non-empty schema URL in the form https://opentelemetry.io/schemas/ const SchemaURL = "https://opentelemetry.io/schemas/1.17.0" ================================================ FILE: vendor/go.opentelemetry.io/otel/semconv/v1.17.0/trace.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Code generated from semantic convention specification. DO NOT EDIT. package semconv // import "go.opentelemetry.io/otel/semconv/v1.17.0" import "go.opentelemetry.io/otel/attribute" // The shared attributes used to report a single exception associated with a // span or log. const ( // ExceptionTypeKey is the attribute Key conforming to the "exception.type" // semantic conventions. It represents the type of the exception (its // fully-qualified class name, if applicable). The dynamic type of the // exception should be preferred over the static type in languages that // support it. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'java.net.ConnectException', 'OSError' ExceptionTypeKey = attribute.Key("exception.type") // ExceptionMessageKey is the attribute Key conforming to the // "exception.message" semantic conventions. It represents the exception // message. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'Division by zero', "Can't convert 'int' object to str // implicitly" ExceptionMessageKey = attribute.Key("exception.message") // ExceptionStacktraceKey is the attribute Key conforming to the // "exception.stacktrace" semantic conventions. It represents a stacktrace // as a string in the natural representation for the language runtime. The // representation is to be determined and documented by each language SIG. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'Exception in thread "main" java.lang.RuntimeException: Test // exception\\n at ' // 'com.example.GenerateTrace.methodB(GenerateTrace.java:13)\\n at ' // 'com.example.GenerateTrace.methodA(GenerateTrace.java:9)\\n at ' // 'com.example.GenerateTrace.main(GenerateTrace.java:5)' ExceptionStacktraceKey = attribute.Key("exception.stacktrace") ) // ExceptionType returns an attribute KeyValue conforming to the // "exception.type" semantic conventions. It represents the type of the // exception (its fully-qualified class name, if applicable). The dynamic type // of the exception should be preferred over the static type in languages that // support it. func ExceptionType(val string) attribute.KeyValue { return ExceptionTypeKey.String(val) } // ExceptionMessage returns an attribute KeyValue conforming to the // "exception.message" semantic conventions. It represents the exception // message. func ExceptionMessage(val string) attribute.KeyValue { return ExceptionMessageKey.String(val) } // ExceptionStacktrace returns an attribute KeyValue conforming to the // "exception.stacktrace" semantic conventions. It represents a stacktrace as a // string in the natural representation for the language runtime. The // representation is to be determined and documented by each language SIG. func ExceptionStacktrace(val string) attribute.KeyValue { return ExceptionStacktraceKey.String(val) } // Attributes for Events represented using Log Records. const ( // EventNameKey is the attribute Key conforming to the "event.name" // semantic conventions. It represents the name identifies the event. // // Type: string // RequirementLevel: Required // Stability: stable // Examples: 'click', 'exception' EventNameKey = attribute.Key("event.name") // EventDomainKey is the attribute Key conforming to the "event.domain" // semantic conventions. It represents the domain identifies the business // context for the events. // // Type: Enum // RequirementLevel: Required // Stability: stable // Note: Events across different domains may have same `event.name`, yet be // unrelated events. EventDomainKey = attribute.Key("event.domain") ) var ( // Events from browser apps EventDomainBrowser = EventDomainKey.String("browser") // Events from mobile apps EventDomainDevice = EventDomainKey.String("device") // Events from Kubernetes EventDomainK8S = EventDomainKey.String("k8s") ) // EventName returns an attribute KeyValue conforming to the "event.name" // semantic conventions. It represents the name identifies the event. func EventName(val string) attribute.KeyValue { return EventNameKey.String(val) } // Span attributes used by AWS Lambda (in addition to general `faas` // attributes). const ( // AWSLambdaInvokedARNKey is the attribute Key conforming to the // "aws.lambda.invoked_arn" semantic conventions. It represents the full // invoked ARN as provided on the `Context` passed to the function // (`Lambda-Runtime-Invoked-Function-ARN` header on the // `/runtime/invocation/next` applicable). // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'arn:aws:lambda:us-east-1:123456:function:myfunction:myalias' // Note: This may be different from `faas.id` if an alias is involved. AWSLambdaInvokedARNKey = attribute.Key("aws.lambda.invoked_arn") ) // AWSLambdaInvokedARN returns an attribute KeyValue conforming to the // "aws.lambda.invoked_arn" semantic conventions. It represents the full // invoked ARN as provided on the `Context` passed to the function // (`Lambda-Runtime-Invoked-Function-ARN` header on the // `/runtime/invocation/next` applicable). func AWSLambdaInvokedARN(val string) attribute.KeyValue { return AWSLambdaInvokedARNKey.String(val) } // Attributes for CloudEvents. CloudEvents is a specification on how to define // event data in a standard way. These attributes can be attached to spans when // performing operations with CloudEvents, regardless of the protocol being // used. const ( // CloudeventsEventIDKey is the attribute Key conforming to the // "cloudevents.event_id" semantic conventions. It represents the // [event_id](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#id) // uniquely identifies the event. // // Type: string // RequirementLevel: Required // Stability: stable // Examples: '123e4567-e89b-12d3-a456-426614174000', '0001' CloudeventsEventIDKey = attribute.Key("cloudevents.event_id") // CloudeventsEventSourceKey is the attribute Key conforming to the // "cloudevents.event_source" semantic conventions. It represents the // [source](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#source-1) // identifies the context in which an event happened. // // Type: string // RequirementLevel: Required // Stability: stable // Examples: 'https://github.com/cloudevents', // '/cloudevents/spec/pull/123', 'my-service' CloudeventsEventSourceKey = attribute.Key("cloudevents.event_source") // CloudeventsEventSpecVersionKey is the attribute Key conforming to the // "cloudevents.event_spec_version" semantic conventions. It represents the // [version of the CloudEvents // specification](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#specversion) // which the event uses. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '1.0' CloudeventsEventSpecVersionKey = attribute.Key("cloudevents.event_spec_version") // CloudeventsEventTypeKey is the attribute Key conforming to the // "cloudevents.event_type" semantic conventions. It represents the // [event_type](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#type) // contains a value describing the type of event related to the originating // occurrence. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'com.github.pull_request.opened', // 'com.example.object.deleted.v2' CloudeventsEventTypeKey = attribute.Key("cloudevents.event_type") // CloudeventsEventSubjectKey is the attribute Key conforming to the // "cloudevents.event_subject" semantic conventions. It represents the // [subject](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#subject) // of the event in the context of the event producer (identified by // source). // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'mynewfile.jpg' CloudeventsEventSubjectKey = attribute.Key("cloudevents.event_subject") ) // CloudeventsEventID returns an attribute KeyValue conforming to the // "cloudevents.event_id" semantic conventions. It represents the // [event_id](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#id) // uniquely identifies the event. func CloudeventsEventID(val string) attribute.KeyValue { return CloudeventsEventIDKey.String(val) } // CloudeventsEventSource returns an attribute KeyValue conforming to the // "cloudevents.event_source" semantic conventions. It represents the // [source](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#source-1) // identifies the context in which an event happened. func CloudeventsEventSource(val string) attribute.KeyValue { return CloudeventsEventSourceKey.String(val) } // CloudeventsEventSpecVersion returns an attribute KeyValue conforming to // the "cloudevents.event_spec_version" semantic conventions. It represents the // [version of the CloudEvents // specification](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#specversion) // which the event uses. func CloudeventsEventSpecVersion(val string) attribute.KeyValue { return CloudeventsEventSpecVersionKey.String(val) } // CloudeventsEventType returns an attribute KeyValue conforming to the // "cloudevents.event_type" semantic conventions. It represents the // [event_type](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#type) // contains a value describing the type of event related to the originating // occurrence. func CloudeventsEventType(val string) attribute.KeyValue { return CloudeventsEventTypeKey.String(val) } // CloudeventsEventSubject returns an attribute KeyValue conforming to the // "cloudevents.event_subject" semantic conventions. It represents the // [subject](https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md#subject) // of the event in the context of the event producer (identified by source). func CloudeventsEventSubject(val string) attribute.KeyValue { return CloudeventsEventSubjectKey.String(val) } // Semantic conventions for the OpenTracing Shim const ( // OpentracingRefTypeKey is the attribute Key conforming to the // "opentracing.ref_type" semantic conventions. It represents the // parent-child Reference type // // Type: Enum // RequirementLevel: Optional // Stability: stable // Note: The causal relationship between a child Span and a parent Span. OpentracingRefTypeKey = attribute.Key("opentracing.ref_type") ) var ( // The parent Span depends on the child Span in some capacity OpentracingRefTypeChildOf = OpentracingRefTypeKey.String("child_of") // The parent Span does not depend in any way on the result of the child Span OpentracingRefTypeFollowsFrom = OpentracingRefTypeKey.String("follows_from") ) // The attributes used to perform database client calls. const ( // DBSystemKey is the attribute Key conforming to the "db.system" semantic // conventions. It represents an identifier for the database management // system (DBMS) product being used. See below for a list of well-known // identifiers. // // Type: Enum // RequirementLevel: Required // Stability: stable DBSystemKey = attribute.Key("db.system") // DBConnectionStringKey is the attribute Key conforming to the // "db.connection_string" semantic conventions. It represents the // connection string used to connect to the database. It is recommended to // remove embedded credentials. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'Server=(localdb)\\v11.0;Integrated Security=true;' DBConnectionStringKey = attribute.Key("db.connection_string") // DBUserKey is the attribute Key conforming to the "db.user" semantic // conventions. It represents the username for accessing the database. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'readonly_user', 'reporting_user' DBUserKey = attribute.Key("db.user") // DBJDBCDriverClassnameKey is the attribute Key conforming to the // "db.jdbc.driver_classname" semantic conventions. It represents the // fully-qualified class name of the [Java Database Connectivity // (JDBC)](https://docs.oracle.com/javase/8/docs/technotes/guides/jdbc/) // driver used to connect. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'org.postgresql.Driver', // 'com.microsoft.sqlserver.jdbc.SQLServerDriver' DBJDBCDriverClassnameKey = attribute.Key("db.jdbc.driver_classname") // DBNameKey is the attribute Key conforming to the "db.name" semantic // conventions. It represents the this attribute is used to report the name // of the database being accessed. For commands that switch the database, // this should be set to the target database (even if the command fails). // // Type: string // RequirementLevel: ConditionallyRequired (If applicable.) // Stability: stable // Examples: 'customers', 'main' // Note: In some SQL databases, the database name to be used is called // "schema name". In case there are multiple layers that could be // considered for database name (e.g. Oracle instance name and schema // name), the database name to be used is the more specific layer (e.g. // Oracle schema name). DBNameKey = attribute.Key("db.name") // DBStatementKey is the attribute Key conforming to the "db.statement" // semantic conventions. It represents the database statement being // executed. // // Type: string // RequirementLevel: ConditionallyRequired (If applicable and not // explicitly disabled via instrumentation configuration.) // Stability: stable // Examples: 'SELECT * FROM wuser_table', 'SET mykey "WuValue"' // Note: The value may be sanitized to exclude sensitive information. DBStatementKey = attribute.Key("db.statement") // DBOperationKey is the attribute Key conforming to the "db.operation" // semantic conventions. It represents the name of the operation being // executed, e.g. the [MongoDB command // name](https://docs.mongodb.com/manual/reference/command/#database-operations) // such as `findAndModify`, or the SQL keyword. // // Type: string // RequirementLevel: ConditionallyRequired (If `db.statement` is not // applicable.) // Stability: stable // Examples: 'findAndModify', 'HMSET', 'SELECT' // Note: When setting this to an SQL keyword, it is not recommended to // attempt any client-side parsing of `db.statement` just to get this // property, but it should be set if the operation name is provided by the // library being instrumented. If the SQL statement has an ambiguous // operation, or performs more than one operation, this value may be // omitted. DBOperationKey = attribute.Key("db.operation") ) var ( // Some other SQL database. Fallback only. See notes DBSystemOtherSQL = DBSystemKey.String("other_sql") // Microsoft SQL Server DBSystemMSSQL = DBSystemKey.String("mssql") // MySQL DBSystemMySQL = DBSystemKey.String("mysql") // Oracle Database DBSystemOracle = DBSystemKey.String("oracle") // IBM DB2 DBSystemDB2 = DBSystemKey.String("db2") // PostgreSQL DBSystemPostgreSQL = DBSystemKey.String("postgresql") // Amazon Redshift DBSystemRedshift = DBSystemKey.String("redshift") // Apache Hive DBSystemHive = DBSystemKey.String("hive") // Cloudscape DBSystemCloudscape = DBSystemKey.String("cloudscape") // HyperSQL DataBase DBSystemHSQLDB = DBSystemKey.String("hsqldb") // Progress Database DBSystemProgress = DBSystemKey.String("progress") // SAP MaxDB DBSystemMaxDB = DBSystemKey.String("maxdb") // SAP HANA DBSystemHanaDB = DBSystemKey.String("hanadb") // Ingres DBSystemIngres = DBSystemKey.String("ingres") // FirstSQL DBSystemFirstSQL = DBSystemKey.String("firstsql") // EnterpriseDB DBSystemEDB = DBSystemKey.String("edb") // InterSystems Caché DBSystemCache = DBSystemKey.String("cache") // Adabas (Adaptable Database System) DBSystemAdabas = DBSystemKey.String("adabas") // Firebird DBSystemFirebird = DBSystemKey.String("firebird") // Apache Derby DBSystemDerby = DBSystemKey.String("derby") // FileMaker DBSystemFilemaker = DBSystemKey.String("filemaker") // Informix DBSystemInformix = DBSystemKey.String("informix") // InstantDB DBSystemInstantDB = DBSystemKey.String("instantdb") // InterBase DBSystemInterbase = DBSystemKey.String("interbase") // MariaDB DBSystemMariaDB = DBSystemKey.String("mariadb") // Netezza DBSystemNetezza = DBSystemKey.String("netezza") // Pervasive PSQL DBSystemPervasive = DBSystemKey.String("pervasive") // PointBase DBSystemPointbase = DBSystemKey.String("pointbase") // SQLite DBSystemSqlite = DBSystemKey.String("sqlite") // Sybase DBSystemSybase = DBSystemKey.String("sybase") // Teradata DBSystemTeradata = DBSystemKey.String("teradata") // Vertica DBSystemVertica = DBSystemKey.String("vertica") // H2 DBSystemH2 = DBSystemKey.String("h2") // ColdFusion IMQ DBSystemColdfusion = DBSystemKey.String("coldfusion") // Apache Cassandra DBSystemCassandra = DBSystemKey.String("cassandra") // Apache HBase DBSystemHBase = DBSystemKey.String("hbase") // MongoDB DBSystemMongoDB = DBSystemKey.String("mongodb") // Redis DBSystemRedis = DBSystemKey.String("redis") // Couchbase DBSystemCouchbase = DBSystemKey.String("couchbase") // CouchDB DBSystemCouchDB = DBSystemKey.String("couchdb") // Microsoft Azure Cosmos DB DBSystemCosmosDB = DBSystemKey.String("cosmosdb") // Amazon DynamoDB DBSystemDynamoDB = DBSystemKey.String("dynamodb") // Neo4j DBSystemNeo4j = DBSystemKey.String("neo4j") // Apache Geode DBSystemGeode = DBSystemKey.String("geode") // Elasticsearch DBSystemElasticsearch = DBSystemKey.String("elasticsearch") // Memcached DBSystemMemcached = DBSystemKey.String("memcached") // CockroachDB DBSystemCockroachdb = DBSystemKey.String("cockroachdb") // OpenSearch DBSystemOpensearch = DBSystemKey.String("opensearch") // ClickHouse DBSystemClickhouse = DBSystemKey.String("clickhouse") ) // DBConnectionString returns an attribute KeyValue conforming to the // "db.connection_string" semantic conventions. It represents the connection // string used to connect to the database. It is recommended to remove embedded // credentials. func DBConnectionString(val string) attribute.KeyValue { return DBConnectionStringKey.String(val) } // DBUser returns an attribute KeyValue conforming to the "db.user" semantic // conventions. It represents the username for accessing the database. func DBUser(val string) attribute.KeyValue { return DBUserKey.String(val) } // DBJDBCDriverClassname returns an attribute KeyValue conforming to the // "db.jdbc.driver_classname" semantic conventions. It represents the // fully-qualified class name of the [Java Database Connectivity // (JDBC)](https://docs.oracle.com/javase/8/docs/technotes/guides/jdbc/) driver // used to connect. func DBJDBCDriverClassname(val string) attribute.KeyValue { return DBJDBCDriverClassnameKey.String(val) } // DBName returns an attribute KeyValue conforming to the "db.name" semantic // conventions. It represents the this attribute is used to report the name of // the database being accessed. For commands that switch the database, this // should be set to the target database (even if the command fails). func DBName(val string) attribute.KeyValue { return DBNameKey.String(val) } // DBStatement returns an attribute KeyValue conforming to the // "db.statement" semantic conventions. It represents the database statement // being executed. func DBStatement(val string) attribute.KeyValue { return DBStatementKey.String(val) } // DBOperation returns an attribute KeyValue conforming to the // "db.operation" semantic conventions. It represents the name of the operation // being executed, e.g. the [MongoDB command // name](https://docs.mongodb.com/manual/reference/command/#database-operations) // such as `findAndModify`, or the SQL keyword. func DBOperation(val string) attribute.KeyValue { return DBOperationKey.String(val) } // Connection-level attributes for Microsoft SQL Server const ( // DBMSSQLInstanceNameKey is the attribute Key conforming to the // "db.mssql.instance_name" semantic conventions. It represents the // Microsoft SQL Server [instance // name](https://docs.microsoft.com/en-us/sql/connect/jdbc/building-the-connection-url?view=sql-server-ver15) // connecting to. This name is used to determine the port of a named // instance. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'MSSQLSERVER' // Note: If setting a `db.mssql.instance_name`, `net.peer.port` is no // longer required (but still recommended if non-standard). DBMSSQLInstanceNameKey = attribute.Key("db.mssql.instance_name") ) // DBMSSQLInstanceName returns an attribute KeyValue conforming to the // "db.mssql.instance_name" semantic conventions. It represents the Microsoft // SQL Server [instance // name](https://docs.microsoft.com/en-us/sql/connect/jdbc/building-the-connection-url?view=sql-server-ver15) // connecting to. This name is used to determine the port of a named instance. func DBMSSQLInstanceName(val string) attribute.KeyValue { return DBMSSQLInstanceNameKey.String(val) } // Call-level attributes for Cassandra const ( // DBCassandraPageSizeKey is the attribute Key conforming to the // "db.cassandra.page_size" semantic conventions. It represents the fetch // size used for paging, i.e. how many rows will be returned at once. // // Type: int // RequirementLevel: Optional // Stability: stable // Examples: 5000 DBCassandraPageSizeKey = attribute.Key("db.cassandra.page_size") // DBCassandraConsistencyLevelKey is the attribute Key conforming to the // "db.cassandra.consistency_level" semantic conventions. It represents the // consistency level of the query. Based on consistency values from // [CQL](https://docs.datastax.com/en/cassandra-oss/3.0/cassandra/dml/dmlConfigConsistency.html). // // Type: Enum // RequirementLevel: Optional // Stability: stable DBCassandraConsistencyLevelKey = attribute.Key("db.cassandra.consistency_level") // DBCassandraTableKey is the attribute Key conforming to the // "db.cassandra.table" semantic conventions. It represents the name of the // primary table that the operation is acting upon, including the keyspace // name (if applicable). // // Type: string // RequirementLevel: Recommended // Stability: stable // Examples: 'mytable' // Note: This mirrors the db.sql.table attribute but references cassandra // rather than sql. It is not recommended to attempt any client-side // parsing of `db.statement` just to get this property, but it should be // set if it is provided by the library being instrumented. If the // operation is acting upon an anonymous table, or more than one table, // this value MUST NOT be set. DBCassandraTableKey = attribute.Key("db.cassandra.table") // DBCassandraIdempotenceKey is the attribute Key conforming to the // "db.cassandra.idempotence" semantic conventions. It represents the // whether or not the query is idempotent. // // Type: boolean // RequirementLevel: Optional // Stability: stable DBCassandraIdempotenceKey = attribute.Key("db.cassandra.idempotence") // DBCassandraSpeculativeExecutionCountKey is the attribute Key conforming // to the "db.cassandra.speculative_execution_count" semantic conventions. // It represents the number of times a query was speculatively executed. // Not set or `0` if the query was not executed speculatively. // // Type: int // RequirementLevel: Optional // Stability: stable // Examples: 0, 2 DBCassandraSpeculativeExecutionCountKey = attribute.Key("db.cassandra.speculative_execution_count") // DBCassandraCoordinatorIDKey is the attribute Key conforming to the // "db.cassandra.coordinator.id" semantic conventions. It represents the ID // of the coordinating node for a query. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'be13faa2-8574-4d71-926d-27f16cf8a7af' DBCassandraCoordinatorIDKey = attribute.Key("db.cassandra.coordinator.id") // DBCassandraCoordinatorDCKey is the attribute Key conforming to the // "db.cassandra.coordinator.dc" semantic conventions. It represents the // data center of the coordinating node for a query. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'us-west-2' DBCassandraCoordinatorDCKey = attribute.Key("db.cassandra.coordinator.dc") ) var ( // all DBCassandraConsistencyLevelAll = DBCassandraConsistencyLevelKey.String("all") // each_quorum DBCassandraConsistencyLevelEachQuorum = DBCassandraConsistencyLevelKey.String("each_quorum") // quorum DBCassandraConsistencyLevelQuorum = DBCassandraConsistencyLevelKey.String("quorum") // local_quorum DBCassandraConsistencyLevelLocalQuorum = DBCassandraConsistencyLevelKey.String("local_quorum") // one DBCassandraConsistencyLevelOne = DBCassandraConsistencyLevelKey.String("one") // two DBCassandraConsistencyLevelTwo = DBCassandraConsistencyLevelKey.String("two") // three DBCassandraConsistencyLevelThree = DBCassandraConsistencyLevelKey.String("three") // local_one DBCassandraConsistencyLevelLocalOne = DBCassandraConsistencyLevelKey.String("local_one") // any DBCassandraConsistencyLevelAny = DBCassandraConsistencyLevelKey.String("any") // serial DBCassandraConsistencyLevelSerial = DBCassandraConsistencyLevelKey.String("serial") // local_serial DBCassandraConsistencyLevelLocalSerial = DBCassandraConsistencyLevelKey.String("local_serial") ) // DBCassandraPageSize returns an attribute KeyValue conforming to the // "db.cassandra.page_size" semantic conventions. It represents the fetch size // used for paging, i.e. how many rows will be returned at once. func DBCassandraPageSize(val int) attribute.KeyValue { return DBCassandraPageSizeKey.Int(val) } // DBCassandraTable returns an attribute KeyValue conforming to the // "db.cassandra.table" semantic conventions. It represents the name of the // primary table that the operation is acting upon, including the keyspace name // (if applicable). func DBCassandraTable(val string) attribute.KeyValue { return DBCassandraTableKey.String(val) } // DBCassandraIdempotence returns an attribute KeyValue conforming to the // "db.cassandra.idempotence" semantic conventions. It represents the whether // or not the query is idempotent. func DBCassandraIdempotence(val bool) attribute.KeyValue { return DBCassandraIdempotenceKey.Bool(val) } // DBCassandraSpeculativeExecutionCount returns an attribute KeyValue // conforming to the "db.cassandra.speculative_execution_count" semantic // conventions. It represents the number of times a query was speculatively // executed. Not set or `0` if the query was not executed speculatively. func DBCassandraSpeculativeExecutionCount(val int) attribute.KeyValue { return DBCassandraSpeculativeExecutionCountKey.Int(val) } // DBCassandraCoordinatorID returns an attribute KeyValue conforming to the // "db.cassandra.coordinator.id" semantic conventions. It represents the ID of // the coordinating node for a query. func DBCassandraCoordinatorID(val string) attribute.KeyValue { return DBCassandraCoordinatorIDKey.String(val) } // DBCassandraCoordinatorDC returns an attribute KeyValue conforming to the // "db.cassandra.coordinator.dc" semantic conventions. It represents the data // center of the coordinating node for a query. func DBCassandraCoordinatorDC(val string) attribute.KeyValue { return DBCassandraCoordinatorDCKey.String(val) } // Call-level attributes for Redis const ( // DBRedisDBIndexKey is the attribute Key conforming to the // "db.redis.database_index" semantic conventions. It represents the index // of the database being accessed as used in the [`SELECT` // command](https://redis.io/commands/select), provided as an integer. To // be used instead of the generic `db.name` attribute. // // Type: int // RequirementLevel: ConditionallyRequired (If other than the default // database (`0`).) // Stability: stable // Examples: 0, 1, 15 DBRedisDBIndexKey = attribute.Key("db.redis.database_index") ) // DBRedisDBIndex returns an attribute KeyValue conforming to the // "db.redis.database_index" semantic conventions. It represents the index of // the database being accessed as used in the [`SELECT` // command](https://redis.io/commands/select), provided as an integer. To be // used instead of the generic `db.name` attribute. func DBRedisDBIndex(val int) attribute.KeyValue { return DBRedisDBIndexKey.Int(val) } // Call-level attributes for MongoDB const ( // DBMongoDBCollectionKey is the attribute Key conforming to the // "db.mongodb.collection" semantic conventions. It represents the // collection being accessed within the database stated in `db.name`. // // Type: string // RequirementLevel: Required // Stability: stable // Examples: 'customers', 'products' DBMongoDBCollectionKey = attribute.Key("db.mongodb.collection") ) // DBMongoDBCollection returns an attribute KeyValue conforming to the // "db.mongodb.collection" semantic conventions. It represents the collection // being accessed within the database stated in `db.name`. func DBMongoDBCollection(val string) attribute.KeyValue { return DBMongoDBCollectionKey.String(val) } // Call-level attributes for SQL databases const ( // DBSQLTableKey is the attribute Key conforming to the "db.sql.table" // semantic conventions. It represents the name of the primary table that // the operation is acting upon, including the database name (if // applicable). // // Type: string // RequirementLevel: Recommended // Stability: stable // Examples: 'public.users', 'customers' // Note: It is not recommended to attempt any client-side parsing of // `db.statement` just to get this property, but it should be set if it is // provided by the library being instrumented. If the operation is acting // upon an anonymous table, or more than one table, this value MUST NOT be // set. DBSQLTableKey = attribute.Key("db.sql.table") ) // DBSQLTable returns an attribute KeyValue conforming to the "db.sql.table" // semantic conventions. It represents the name of the primary table that the // operation is acting upon, including the database name (if applicable). func DBSQLTable(val string) attribute.KeyValue { return DBSQLTableKey.String(val) } // Span attributes used by non-OTLP exporters to represent OpenTelemetry Span's // concepts. const ( // OtelStatusCodeKey is the attribute Key conforming to the // "otel.status_code" semantic conventions. It represents the name of the // code, either "OK" or "ERROR". MUST NOT be set if the status code is // UNSET. // // Type: Enum // RequirementLevel: Optional // Stability: stable OtelStatusCodeKey = attribute.Key("otel.status_code") // OtelStatusDescriptionKey is the attribute Key conforming to the // "otel.status_description" semantic conventions. It represents the // description of the Status if it has a value, otherwise not set. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'resource not found' OtelStatusDescriptionKey = attribute.Key("otel.status_description") ) var ( // The operation has been validated by an Application developer or Operator to have completed successfully OtelStatusCodeOk = OtelStatusCodeKey.String("OK") // The operation contains an error OtelStatusCodeError = OtelStatusCodeKey.String("ERROR") ) // OtelStatusDescription returns an attribute KeyValue conforming to the // "otel.status_description" semantic conventions. It represents the // description of the Status if it has a value, otherwise not set. func OtelStatusDescription(val string) attribute.KeyValue { return OtelStatusDescriptionKey.String(val) } // This semantic convention describes an instance of a function that runs // without provisioning or managing of servers (also known as serverless // functions or Function as a Service (FaaS)) with spans. const ( // FaaSTriggerKey is the attribute Key conforming to the "faas.trigger" // semantic conventions. It represents the type of the trigger which caused // this function execution. // // Type: Enum // RequirementLevel: Optional // Stability: stable // Note: For the server/consumer span on the incoming side, // `faas.trigger` MUST be set. // // Clients invoking FaaS instances usually cannot set `faas.trigger`, // since they would typically need to look in the payload to determine // the event type. If clients set it, it should be the same as the // trigger that corresponding incoming would have (i.e., this has // nothing to do with the underlying transport used to make the API // call to invoke the lambda, which is often HTTP). FaaSTriggerKey = attribute.Key("faas.trigger") // FaaSExecutionKey is the attribute Key conforming to the "faas.execution" // semantic conventions. It represents the execution ID of the current // function execution. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'af9d5aa4-a685-4c5f-a22b-444f80b3cc28' FaaSExecutionKey = attribute.Key("faas.execution") ) var ( // A response to some data source operation such as a database or filesystem read/write FaaSTriggerDatasource = FaaSTriggerKey.String("datasource") // To provide an answer to an inbound HTTP request FaaSTriggerHTTP = FaaSTriggerKey.String("http") // A function is set to be executed when messages are sent to a messaging system FaaSTriggerPubsub = FaaSTriggerKey.String("pubsub") // A function is scheduled to be executed regularly FaaSTriggerTimer = FaaSTriggerKey.String("timer") // If none of the others apply FaaSTriggerOther = FaaSTriggerKey.String("other") ) // FaaSExecution returns an attribute KeyValue conforming to the // "faas.execution" semantic conventions. It represents the execution ID of the // current function execution. func FaaSExecution(val string) attribute.KeyValue { return FaaSExecutionKey.String(val) } // Semantic Convention for FaaS triggered as a response to some data source // operation such as a database or filesystem read/write. const ( // FaaSDocumentCollectionKey is the attribute Key conforming to the // "faas.document.collection" semantic conventions. It represents the name // of the source on which the triggering operation was performed. For // example, in Cloud Storage or S3 corresponds to the bucket name, and in // Cosmos DB to the database name. // // Type: string // RequirementLevel: Required // Stability: stable // Examples: 'myBucketName', 'myDBName' FaaSDocumentCollectionKey = attribute.Key("faas.document.collection") // FaaSDocumentOperationKey is the attribute Key conforming to the // "faas.document.operation" semantic conventions. It represents the // describes the type of the operation that was performed on the data. // // Type: Enum // RequirementLevel: Required // Stability: stable FaaSDocumentOperationKey = attribute.Key("faas.document.operation") // FaaSDocumentTimeKey is the attribute Key conforming to the // "faas.document.time" semantic conventions. It represents a string // containing the time when the data was accessed in the [ISO // 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format // expressed in [UTC](https://www.w3.org/TR/NOTE-datetime). // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '2020-01-23T13:47:06Z' FaaSDocumentTimeKey = attribute.Key("faas.document.time") // FaaSDocumentNameKey is the attribute Key conforming to the // "faas.document.name" semantic conventions. It represents the document // name/table subjected to the operation. For example, in Cloud Storage or // S3 is the name of the file, and in Cosmos DB the table name. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'myFile.txt', 'myTableName' FaaSDocumentNameKey = attribute.Key("faas.document.name") ) var ( // When a new object is created FaaSDocumentOperationInsert = FaaSDocumentOperationKey.String("insert") // When an object is modified FaaSDocumentOperationEdit = FaaSDocumentOperationKey.String("edit") // When an object is deleted FaaSDocumentOperationDelete = FaaSDocumentOperationKey.String("delete") ) // FaaSDocumentCollection returns an attribute KeyValue conforming to the // "faas.document.collection" semantic conventions. It represents the name of // the source on which the triggering operation was performed. For example, in // Cloud Storage or S3 corresponds to the bucket name, and in Cosmos DB to the // database name. func FaaSDocumentCollection(val string) attribute.KeyValue { return FaaSDocumentCollectionKey.String(val) } // FaaSDocumentTime returns an attribute KeyValue conforming to the // "faas.document.time" semantic conventions. It represents a string containing // the time when the data was accessed in the [ISO // 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format // expressed in [UTC](https://www.w3.org/TR/NOTE-datetime). func FaaSDocumentTime(val string) attribute.KeyValue { return FaaSDocumentTimeKey.String(val) } // FaaSDocumentName returns an attribute KeyValue conforming to the // "faas.document.name" semantic conventions. It represents the document // name/table subjected to the operation. For example, in Cloud Storage or S3 // is the name of the file, and in Cosmos DB the table name. func FaaSDocumentName(val string) attribute.KeyValue { return FaaSDocumentNameKey.String(val) } // Semantic Convention for FaaS scheduled to be executed regularly. const ( // FaaSTimeKey is the attribute Key conforming to the "faas.time" semantic // conventions. It represents a string containing the function invocation // time in the [ISO // 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format // expressed in [UTC](https://www.w3.org/TR/NOTE-datetime). // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '2020-01-23T13:47:06Z' FaaSTimeKey = attribute.Key("faas.time") // FaaSCronKey is the attribute Key conforming to the "faas.cron" semantic // conventions. It represents a string containing the schedule period as // [Cron // Expression](https://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm). // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '0/5 * * * ? *' FaaSCronKey = attribute.Key("faas.cron") ) // FaaSTime returns an attribute KeyValue conforming to the "faas.time" // semantic conventions. It represents a string containing the function // invocation time in the [ISO // 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format // expressed in [UTC](https://www.w3.org/TR/NOTE-datetime). func FaaSTime(val string) attribute.KeyValue { return FaaSTimeKey.String(val) } // FaaSCron returns an attribute KeyValue conforming to the "faas.cron" // semantic conventions. It represents a string containing the schedule period // as [Cron // Expression](https://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm). func FaaSCron(val string) attribute.KeyValue { return FaaSCronKey.String(val) } // Contains additional attributes for incoming FaaS spans. const ( // FaaSColdstartKey is the attribute Key conforming to the "faas.coldstart" // semantic conventions. It represents a boolean that is true if the // serverless function is executed for the first time (aka cold-start). // // Type: boolean // RequirementLevel: Optional // Stability: stable FaaSColdstartKey = attribute.Key("faas.coldstart") ) // FaaSColdstart returns an attribute KeyValue conforming to the // "faas.coldstart" semantic conventions. It represents a boolean that is true // if the serverless function is executed for the first time (aka cold-start). func FaaSColdstart(val bool) attribute.KeyValue { return FaaSColdstartKey.Bool(val) } // Contains additional attributes for outgoing FaaS spans. const ( // FaaSInvokedNameKey is the attribute Key conforming to the // "faas.invoked_name" semantic conventions. It represents the name of the // invoked function. // // Type: string // RequirementLevel: Required // Stability: stable // Examples: 'my-function' // Note: SHOULD be equal to the `faas.name` resource attribute of the // invoked function. FaaSInvokedNameKey = attribute.Key("faas.invoked_name") // FaaSInvokedProviderKey is the attribute Key conforming to the // "faas.invoked_provider" semantic conventions. It represents the cloud // provider of the invoked function. // // Type: Enum // RequirementLevel: Required // Stability: stable // Note: SHOULD be equal to the `cloud.provider` resource attribute of the // invoked function. FaaSInvokedProviderKey = attribute.Key("faas.invoked_provider") // FaaSInvokedRegionKey is the attribute Key conforming to the // "faas.invoked_region" semantic conventions. It represents the cloud // region of the invoked function. // // Type: string // RequirementLevel: ConditionallyRequired (For some cloud providers, like // AWS or GCP, the region in which a function is hosted is essential to // uniquely identify the function and also part of its endpoint. Since it's // part of the endpoint being called, the region is always known to // clients. In these cases, `faas.invoked_region` MUST be set accordingly. // If the region is unknown to the client or not required for identifying // the invoked function, setting `faas.invoked_region` is optional.) // Stability: stable // Examples: 'eu-central-1' // Note: SHOULD be equal to the `cloud.region` resource attribute of the // invoked function. FaaSInvokedRegionKey = attribute.Key("faas.invoked_region") ) var ( // Alibaba Cloud FaaSInvokedProviderAlibabaCloud = FaaSInvokedProviderKey.String("alibaba_cloud") // Amazon Web Services FaaSInvokedProviderAWS = FaaSInvokedProviderKey.String("aws") // Microsoft Azure FaaSInvokedProviderAzure = FaaSInvokedProviderKey.String("azure") // Google Cloud Platform FaaSInvokedProviderGCP = FaaSInvokedProviderKey.String("gcp") // Tencent Cloud FaaSInvokedProviderTencentCloud = FaaSInvokedProviderKey.String("tencent_cloud") ) // FaaSInvokedName returns an attribute KeyValue conforming to the // "faas.invoked_name" semantic conventions. It represents the name of the // invoked function. func FaaSInvokedName(val string) attribute.KeyValue { return FaaSInvokedNameKey.String(val) } // FaaSInvokedRegion returns an attribute KeyValue conforming to the // "faas.invoked_region" semantic conventions. It represents the cloud region // of the invoked function. func FaaSInvokedRegion(val string) attribute.KeyValue { return FaaSInvokedRegionKey.String(val) } // These attributes may be used for any network related operation. const ( // NetTransportKey is the attribute Key conforming to the "net.transport" // semantic conventions. It represents the transport protocol used. See // note below. // // Type: Enum // RequirementLevel: Optional // Stability: stable NetTransportKey = attribute.Key("net.transport") // NetAppProtocolNameKey is the attribute Key conforming to the // "net.app.protocol.name" semantic conventions. It represents the // application layer protocol used. The value SHOULD be normalized to // lowercase. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'amqp', 'http', 'mqtt' NetAppProtocolNameKey = attribute.Key("net.app.protocol.name") // NetAppProtocolVersionKey is the attribute Key conforming to the // "net.app.protocol.version" semantic conventions. It represents the // version of the application layer protocol used. See note below. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '3.1.1' // Note: `net.app.protocol.version` refers to the version of the protocol // used and might be different from the protocol client's version. If the // HTTP client used has a version of `0.27.2`, but sends HTTP version // `1.1`, this attribute should be set to `1.1`. NetAppProtocolVersionKey = attribute.Key("net.app.protocol.version") // NetSockPeerNameKey is the attribute Key conforming to the // "net.sock.peer.name" semantic conventions. It represents the remote // socket peer name. // // Type: string // RequirementLevel: Recommended (If available and different from // `net.peer.name` and if `net.sock.peer.addr` is set.) // Stability: stable // Examples: 'proxy.example.com' NetSockPeerNameKey = attribute.Key("net.sock.peer.name") // NetSockPeerAddrKey is the attribute Key conforming to the // "net.sock.peer.addr" semantic conventions. It represents the remote // socket peer address: IPv4 or IPv6 for internet protocols, path for local // communication, // [etc](https://man7.org/linux/man-pages/man7/address_families.7.html). // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '127.0.0.1', '/tmp/mysql.sock' NetSockPeerAddrKey = attribute.Key("net.sock.peer.addr") // NetSockPeerPortKey is the attribute Key conforming to the // "net.sock.peer.port" semantic conventions. It represents the remote // socket peer port. // // Type: int // RequirementLevel: Recommended (If defined for the address family and if // different than `net.peer.port` and if `net.sock.peer.addr` is set.) // Stability: stable // Examples: 16456 NetSockPeerPortKey = attribute.Key("net.sock.peer.port") // NetSockFamilyKey is the attribute Key conforming to the // "net.sock.family" semantic conventions. It represents the protocol // [address // family](https://man7.org/linux/man-pages/man7/address_families.7.html) // which is used for communication. // // Type: Enum // RequirementLevel: ConditionallyRequired (If different than `inet` and if // any of `net.sock.peer.addr` or `net.sock.host.addr` are set. Consumers // of telemetry SHOULD accept both IPv4 and IPv6 formats for the address in // `net.sock.peer.addr` if `net.sock.family` is not set. This is to support // instrumentations that follow previous versions of this document.) // Stability: stable // Examples: 'inet6', 'bluetooth' NetSockFamilyKey = attribute.Key("net.sock.family") // NetPeerNameKey is the attribute Key conforming to the "net.peer.name" // semantic conventions. It represents the logical remote hostname, see // note below. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'example.com' // Note: `net.peer.name` SHOULD NOT be set if capturing it would require an // extra DNS lookup. NetPeerNameKey = attribute.Key("net.peer.name") // NetPeerPortKey is the attribute Key conforming to the "net.peer.port" // semantic conventions. It represents the logical remote port number // // Type: int // RequirementLevel: Optional // Stability: stable // Examples: 80, 8080, 443 NetPeerPortKey = attribute.Key("net.peer.port") // NetHostNameKey is the attribute Key conforming to the "net.host.name" // semantic conventions. It represents the logical local hostname or // similar, see note below. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'localhost' NetHostNameKey = attribute.Key("net.host.name") // NetHostPortKey is the attribute Key conforming to the "net.host.port" // semantic conventions. It represents the logical local port number, // preferably the one that the peer used to connect // // Type: int // RequirementLevel: Optional // Stability: stable // Examples: 8080 NetHostPortKey = attribute.Key("net.host.port") // NetSockHostAddrKey is the attribute Key conforming to the // "net.sock.host.addr" semantic conventions. It represents the local // socket address. Useful in case of a multi-IP host. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '192.168.0.1' NetSockHostAddrKey = attribute.Key("net.sock.host.addr") // NetSockHostPortKey is the attribute Key conforming to the // "net.sock.host.port" semantic conventions. It represents the local // socket port number. // // Type: int // RequirementLevel: Recommended (If defined for the address family and if // different than `net.host.port` and if `net.sock.host.addr` is set.) // Stability: stable // Examples: 35555 NetSockHostPortKey = attribute.Key("net.sock.host.port") // NetHostConnectionTypeKey is the attribute Key conforming to the // "net.host.connection.type" semantic conventions. It represents the // internet connection type currently being used by the host. // // Type: Enum // RequirementLevel: Optional // Stability: stable // Examples: 'wifi' NetHostConnectionTypeKey = attribute.Key("net.host.connection.type") // NetHostConnectionSubtypeKey is the attribute Key conforming to the // "net.host.connection.subtype" semantic conventions. It represents the // this describes more details regarding the connection.type. It may be the // type of cell technology connection, but it could be used for describing // details about a wifi connection. // // Type: Enum // RequirementLevel: Optional // Stability: stable // Examples: 'LTE' NetHostConnectionSubtypeKey = attribute.Key("net.host.connection.subtype") // NetHostCarrierNameKey is the attribute Key conforming to the // "net.host.carrier.name" semantic conventions. It represents the name of // the mobile carrier. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'sprint' NetHostCarrierNameKey = attribute.Key("net.host.carrier.name") // NetHostCarrierMccKey is the attribute Key conforming to the // "net.host.carrier.mcc" semantic conventions. It represents the mobile // carrier country code. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '310' NetHostCarrierMccKey = attribute.Key("net.host.carrier.mcc") // NetHostCarrierMncKey is the attribute Key conforming to the // "net.host.carrier.mnc" semantic conventions. It represents the mobile // carrier network code. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '001' NetHostCarrierMncKey = attribute.Key("net.host.carrier.mnc") // NetHostCarrierIccKey is the attribute Key conforming to the // "net.host.carrier.icc" semantic conventions. It represents the ISO // 3166-1 alpha-2 2-character country code associated with the mobile // carrier network. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'DE' NetHostCarrierIccKey = attribute.Key("net.host.carrier.icc") ) var ( // ip_tcp NetTransportTCP = NetTransportKey.String("ip_tcp") // ip_udp NetTransportUDP = NetTransportKey.String("ip_udp") // Named or anonymous pipe. See note below NetTransportPipe = NetTransportKey.String("pipe") // In-process communication NetTransportInProc = NetTransportKey.String("inproc") // Something else (non IP-based) NetTransportOther = NetTransportKey.String("other") ) var ( // IPv4 address NetSockFamilyInet = NetSockFamilyKey.String("inet") // IPv6 address NetSockFamilyInet6 = NetSockFamilyKey.String("inet6") // Unix domain socket path NetSockFamilyUnix = NetSockFamilyKey.String("unix") ) var ( // wifi NetHostConnectionTypeWifi = NetHostConnectionTypeKey.String("wifi") // wired NetHostConnectionTypeWired = NetHostConnectionTypeKey.String("wired") // cell NetHostConnectionTypeCell = NetHostConnectionTypeKey.String("cell") // unavailable NetHostConnectionTypeUnavailable = NetHostConnectionTypeKey.String("unavailable") // unknown NetHostConnectionTypeUnknown = NetHostConnectionTypeKey.String("unknown") ) var ( // GPRS NetHostConnectionSubtypeGprs = NetHostConnectionSubtypeKey.String("gprs") // EDGE NetHostConnectionSubtypeEdge = NetHostConnectionSubtypeKey.String("edge") // UMTS NetHostConnectionSubtypeUmts = NetHostConnectionSubtypeKey.String("umts") // CDMA NetHostConnectionSubtypeCdma = NetHostConnectionSubtypeKey.String("cdma") // EVDO Rel. 0 NetHostConnectionSubtypeEvdo0 = NetHostConnectionSubtypeKey.String("evdo_0") // EVDO Rev. A NetHostConnectionSubtypeEvdoA = NetHostConnectionSubtypeKey.String("evdo_a") // CDMA2000 1XRTT NetHostConnectionSubtypeCdma20001xrtt = NetHostConnectionSubtypeKey.String("cdma2000_1xrtt") // HSDPA NetHostConnectionSubtypeHsdpa = NetHostConnectionSubtypeKey.String("hsdpa") // HSUPA NetHostConnectionSubtypeHsupa = NetHostConnectionSubtypeKey.String("hsupa") // HSPA NetHostConnectionSubtypeHspa = NetHostConnectionSubtypeKey.String("hspa") // IDEN NetHostConnectionSubtypeIden = NetHostConnectionSubtypeKey.String("iden") // EVDO Rev. B NetHostConnectionSubtypeEvdoB = NetHostConnectionSubtypeKey.String("evdo_b") // LTE NetHostConnectionSubtypeLte = NetHostConnectionSubtypeKey.String("lte") // EHRPD NetHostConnectionSubtypeEhrpd = NetHostConnectionSubtypeKey.String("ehrpd") // HSPAP NetHostConnectionSubtypeHspap = NetHostConnectionSubtypeKey.String("hspap") // GSM NetHostConnectionSubtypeGsm = NetHostConnectionSubtypeKey.String("gsm") // TD-SCDMA NetHostConnectionSubtypeTdScdma = NetHostConnectionSubtypeKey.String("td_scdma") // IWLAN NetHostConnectionSubtypeIwlan = NetHostConnectionSubtypeKey.String("iwlan") // 5G NR (New Radio) NetHostConnectionSubtypeNr = NetHostConnectionSubtypeKey.String("nr") // 5G NRNSA (New Radio Non-Standalone) NetHostConnectionSubtypeNrnsa = NetHostConnectionSubtypeKey.String("nrnsa") // LTE CA NetHostConnectionSubtypeLteCa = NetHostConnectionSubtypeKey.String("lte_ca") ) // NetAppProtocolName returns an attribute KeyValue conforming to the // "net.app.protocol.name" semantic conventions. It represents the application // layer protocol used. The value SHOULD be normalized to lowercase. func NetAppProtocolName(val string) attribute.KeyValue { return NetAppProtocolNameKey.String(val) } // NetAppProtocolVersion returns an attribute KeyValue conforming to the // "net.app.protocol.version" semantic conventions. It represents the version // of the application layer protocol used. See note below. func NetAppProtocolVersion(val string) attribute.KeyValue { return NetAppProtocolVersionKey.String(val) } // NetSockPeerName returns an attribute KeyValue conforming to the // "net.sock.peer.name" semantic conventions. It represents the remote socket // peer name. func NetSockPeerName(val string) attribute.KeyValue { return NetSockPeerNameKey.String(val) } // NetSockPeerAddr returns an attribute KeyValue conforming to the // "net.sock.peer.addr" semantic conventions. It represents the remote socket // peer address: IPv4 or IPv6 for internet protocols, path for local // communication, // [etc](https://man7.org/linux/man-pages/man7/address_families.7.html). func NetSockPeerAddr(val string) attribute.KeyValue { return NetSockPeerAddrKey.String(val) } // NetSockPeerPort returns an attribute KeyValue conforming to the // "net.sock.peer.port" semantic conventions. It represents the remote socket // peer port. func NetSockPeerPort(val int) attribute.KeyValue { return NetSockPeerPortKey.Int(val) } // NetPeerName returns an attribute KeyValue conforming to the // "net.peer.name" semantic conventions. It represents the logical remote // hostname, see note below. func NetPeerName(val string) attribute.KeyValue { return NetPeerNameKey.String(val) } // NetPeerPort returns an attribute KeyValue conforming to the // "net.peer.port" semantic conventions. It represents the logical remote port // number func NetPeerPort(val int) attribute.KeyValue { return NetPeerPortKey.Int(val) } // NetHostName returns an attribute KeyValue conforming to the // "net.host.name" semantic conventions. It represents the logical local // hostname or similar, see note below. func NetHostName(val string) attribute.KeyValue { return NetHostNameKey.String(val) } // NetHostPort returns an attribute KeyValue conforming to the // "net.host.port" semantic conventions. It represents the logical local port // number, preferably the one that the peer used to connect func NetHostPort(val int) attribute.KeyValue { return NetHostPortKey.Int(val) } // NetSockHostAddr returns an attribute KeyValue conforming to the // "net.sock.host.addr" semantic conventions. It represents the local socket // address. Useful in case of a multi-IP host. func NetSockHostAddr(val string) attribute.KeyValue { return NetSockHostAddrKey.String(val) } // NetSockHostPort returns an attribute KeyValue conforming to the // "net.sock.host.port" semantic conventions. It represents the local socket // port number. func NetSockHostPort(val int) attribute.KeyValue { return NetSockHostPortKey.Int(val) } // NetHostCarrierName returns an attribute KeyValue conforming to the // "net.host.carrier.name" semantic conventions. It represents the name of the // mobile carrier. func NetHostCarrierName(val string) attribute.KeyValue { return NetHostCarrierNameKey.String(val) } // NetHostCarrierMcc returns an attribute KeyValue conforming to the // "net.host.carrier.mcc" semantic conventions. It represents the mobile // carrier country code. func NetHostCarrierMcc(val string) attribute.KeyValue { return NetHostCarrierMccKey.String(val) } // NetHostCarrierMnc returns an attribute KeyValue conforming to the // "net.host.carrier.mnc" semantic conventions. It represents the mobile // carrier network code. func NetHostCarrierMnc(val string) attribute.KeyValue { return NetHostCarrierMncKey.String(val) } // NetHostCarrierIcc returns an attribute KeyValue conforming to the // "net.host.carrier.icc" semantic conventions. It represents the ISO 3166-1 // alpha-2 2-character country code associated with the mobile carrier network. func NetHostCarrierIcc(val string) attribute.KeyValue { return NetHostCarrierIccKey.String(val) } // Operations that access some remote service. const ( // PeerServiceKey is the attribute Key conforming to the "peer.service" // semantic conventions. It represents the // [`service.name`](../../resource/semantic_conventions/README.md#service) // of the remote service. SHOULD be equal to the actual `service.name` // resource attribute of the remote service if any. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'AuthTokenCache' PeerServiceKey = attribute.Key("peer.service") ) // PeerService returns an attribute KeyValue conforming to the // "peer.service" semantic conventions. It represents the // [`service.name`](../../resource/semantic_conventions/README.md#service) of // the remote service. SHOULD be equal to the actual `service.name` resource // attribute of the remote service if any. func PeerService(val string) attribute.KeyValue { return PeerServiceKey.String(val) } // These attributes may be used for any operation with an authenticated and/or // authorized enduser. const ( // EnduserIDKey is the attribute Key conforming to the "enduser.id" // semantic conventions. It represents the username or client_id extracted // from the access token or // [Authorization](https://tools.ietf.org/html/rfc7235#section-4.2) header // in the inbound request from outside the system. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'username' EnduserIDKey = attribute.Key("enduser.id") // EnduserRoleKey is the attribute Key conforming to the "enduser.role" // semantic conventions. It represents the actual/assumed role the client // is making the request under extracted from token or application security // context. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'admin' EnduserRoleKey = attribute.Key("enduser.role") // EnduserScopeKey is the attribute Key conforming to the "enduser.scope" // semantic conventions. It represents the scopes or granted authorities // the client currently possesses extracted from token or application // security context. The value would come from the scope associated with an // [OAuth 2.0 Access // Token](https://tools.ietf.org/html/rfc6749#section-3.3) or an attribute // value in a [SAML 2.0 // Assertion](http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html). // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'read:message, write:files' EnduserScopeKey = attribute.Key("enduser.scope") ) // EnduserID returns an attribute KeyValue conforming to the "enduser.id" // semantic conventions. It represents the username or client_id extracted from // the access token or // [Authorization](https://tools.ietf.org/html/rfc7235#section-4.2) header in // the inbound request from outside the system. func EnduserID(val string) attribute.KeyValue { return EnduserIDKey.String(val) } // EnduserRole returns an attribute KeyValue conforming to the // "enduser.role" semantic conventions. It represents the actual/assumed role // the client is making the request under extracted from token or application // security context. func EnduserRole(val string) attribute.KeyValue { return EnduserRoleKey.String(val) } // EnduserScope returns an attribute KeyValue conforming to the // "enduser.scope" semantic conventions. It represents the scopes or granted // authorities the client currently possesses extracted from token or // application security context. The value would come from the scope associated // with an [OAuth 2.0 Access // Token](https://tools.ietf.org/html/rfc6749#section-3.3) or an attribute // value in a [SAML 2.0 // Assertion](http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html). func EnduserScope(val string) attribute.KeyValue { return EnduserScopeKey.String(val) } // These attributes may be used for any operation to store information about a // thread that started a span. const ( // ThreadIDKey is the attribute Key conforming to the "thread.id" semantic // conventions. It represents the current "managed" thread ID (as opposed // to OS thread ID). // // Type: int // RequirementLevel: Optional // Stability: stable // Examples: 42 ThreadIDKey = attribute.Key("thread.id") // ThreadNameKey is the attribute Key conforming to the "thread.name" // semantic conventions. It represents the current thread name. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'main' ThreadNameKey = attribute.Key("thread.name") ) // ThreadID returns an attribute KeyValue conforming to the "thread.id" // semantic conventions. It represents the current "managed" thread ID (as // opposed to OS thread ID). func ThreadID(val int) attribute.KeyValue { return ThreadIDKey.Int(val) } // ThreadName returns an attribute KeyValue conforming to the "thread.name" // semantic conventions. It represents the current thread name. func ThreadName(val string) attribute.KeyValue { return ThreadNameKey.String(val) } // These attributes allow to report this unit of code and therefore to provide // more context about the span. const ( // CodeFunctionKey is the attribute Key conforming to the "code.function" // semantic conventions. It represents the method or function name, or // equivalent (usually rightmost part of the code unit's name). // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'serveRequest' CodeFunctionKey = attribute.Key("code.function") // CodeNamespaceKey is the attribute Key conforming to the "code.namespace" // semantic conventions. It represents the "namespace" within which // `code.function` is defined. Usually the qualified class or module name, // such that `code.namespace` + some separator + `code.function` form a // unique identifier for the code unit. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'com.example.MyHTTPService' CodeNamespaceKey = attribute.Key("code.namespace") // CodeFilepathKey is the attribute Key conforming to the "code.filepath" // semantic conventions. It represents the source code file name that // identifies the code unit as uniquely as possible (preferably an absolute // file path). // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '/usr/local/MyApplication/content_root/app/index.php' CodeFilepathKey = attribute.Key("code.filepath") // CodeLineNumberKey is the attribute Key conforming to the "code.lineno" // semantic conventions. It represents the line number in `code.filepath` // best representing the operation. It SHOULD point within the code unit // named in `code.function`. // // Type: int // RequirementLevel: Optional // Stability: stable // Examples: 42 CodeLineNumberKey = attribute.Key("code.lineno") // CodeColumnKey is the attribute Key conforming to the "code.column" // semantic conventions. It represents the column number in `code.filepath` // best representing the operation. It SHOULD point within the code unit // named in `code.function`. // // Type: int // RequirementLevel: Optional // Stability: stable // Examples: 16 CodeColumnKey = attribute.Key("code.column") ) // CodeFunction returns an attribute KeyValue conforming to the // "code.function" semantic conventions. It represents the method or function // name, or equivalent (usually rightmost part of the code unit's name). func CodeFunction(val string) attribute.KeyValue { return CodeFunctionKey.String(val) } // CodeNamespace returns an attribute KeyValue conforming to the // "code.namespace" semantic conventions. It represents the "namespace" within // which `code.function` is defined. Usually the qualified class or module // name, such that `code.namespace` + some separator + `code.function` form a // unique identifier for the code unit. func CodeNamespace(val string) attribute.KeyValue { return CodeNamespaceKey.String(val) } // CodeFilepath returns an attribute KeyValue conforming to the // "code.filepath" semantic conventions. It represents the source code file // name that identifies the code unit as uniquely as possible (preferably an // absolute file path). func CodeFilepath(val string) attribute.KeyValue { return CodeFilepathKey.String(val) } // CodeLineNumber returns an attribute KeyValue conforming to the "code.lineno" // semantic conventions. It represents the line number in `code.filepath` best // representing the operation. It SHOULD point within the code unit named in // `code.function`. func CodeLineNumber(val int) attribute.KeyValue { return CodeLineNumberKey.Int(val) } // CodeColumn returns an attribute KeyValue conforming to the "code.column" // semantic conventions. It represents the column number in `code.filepath` // best representing the operation. It SHOULD point within the code unit named // in `code.function`. func CodeColumn(val int) attribute.KeyValue { return CodeColumnKey.Int(val) } // Semantic conventions for HTTP client and server Spans. const ( // HTTPMethodKey is the attribute Key conforming to the "http.method" // semantic conventions. It represents the hTTP request method. // // Type: string // RequirementLevel: Required // Stability: stable // Examples: 'GET', 'POST', 'HEAD' HTTPMethodKey = attribute.Key("http.method") // HTTPStatusCodeKey is the attribute Key conforming to the // "http.status_code" semantic conventions. It represents the [HTTP // response status code](https://tools.ietf.org/html/rfc7231#section-6). // // Type: int // RequirementLevel: ConditionallyRequired (If and only if one was // received/sent.) // Stability: stable // Examples: 200 HTTPStatusCodeKey = attribute.Key("http.status_code") // HTTPFlavorKey is the attribute Key conforming to the "http.flavor" // semantic conventions. It represents the kind of HTTP protocol used. // // Type: Enum // RequirementLevel: Optional // Stability: stable // Note: If `net.transport` is not specified, it can be assumed to be // `IP.TCP` except if `http.flavor` is `QUIC`, in which case `IP.UDP` is // assumed. HTTPFlavorKey = attribute.Key("http.flavor") // HTTPUserAgentKey is the attribute Key conforming to the // "http.user_agent" semantic conventions. It represents the value of the // [HTTP // User-Agent](https://www.rfc-editor.org/rfc/rfc9110.html#field.user-agent) // header sent by the client. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'CERN-LineMode/2.15 libwww/2.17b3' HTTPUserAgentKey = attribute.Key("http.user_agent") // HTTPRequestContentLengthKey is the attribute Key conforming to the // "http.request_content_length" semantic conventions. It represents the // size of the request payload body in bytes. This is the number of bytes // transferred excluding headers and is often, but not always, present as // the // [Content-Length](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length) // header. For requests using transport encoding, this should be the // compressed size. // // Type: int // RequirementLevel: Optional // Stability: stable // Examples: 3495 HTTPRequestContentLengthKey = attribute.Key("http.request_content_length") // HTTPResponseContentLengthKey is the attribute Key conforming to the // "http.response_content_length" semantic conventions. It represents the // size of the response payload body in bytes. This is the number of bytes // transferred excluding headers and is often, but not always, present as // the // [Content-Length](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length) // header. For requests using transport encoding, this should be the // compressed size. // // Type: int // RequirementLevel: Optional // Stability: stable // Examples: 3495 HTTPResponseContentLengthKey = attribute.Key("http.response_content_length") ) var ( // HTTP/1.0 HTTPFlavorHTTP10 = HTTPFlavorKey.String("1.0") // HTTP/1.1 HTTPFlavorHTTP11 = HTTPFlavorKey.String("1.1") // HTTP/2 HTTPFlavorHTTP20 = HTTPFlavorKey.String("2.0") // HTTP/3 HTTPFlavorHTTP30 = HTTPFlavorKey.String("3.0") // SPDY protocol HTTPFlavorSPDY = HTTPFlavorKey.String("SPDY") // QUIC protocol HTTPFlavorQUIC = HTTPFlavorKey.String("QUIC") ) // HTTPMethod returns an attribute KeyValue conforming to the "http.method" // semantic conventions. It represents the hTTP request method. func HTTPMethod(val string) attribute.KeyValue { return HTTPMethodKey.String(val) } // HTTPStatusCode returns an attribute KeyValue conforming to the // "http.status_code" semantic conventions. It represents the [HTTP response // status code](https://tools.ietf.org/html/rfc7231#section-6). func HTTPStatusCode(val int) attribute.KeyValue { return HTTPStatusCodeKey.Int(val) } // HTTPUserAgent returns an attribute KeyValue conforming to the // "http.user_agent" semantic conventions. It represents the value of the [HTTP // User-Agent](https://www.rfc-editor.org/rfc/rfc9110.html#field.user-agent) // header sent by the client. func HTTPUserAgent(val string) attribute.KeyValue { return HTTPUserAgentKey.String(val) } // HTTPRequestContentLength returns an attribute KeyValue conforming to the // "http.request_content_length" semantic conventions. It represents the size // of the request payload body in bytes. This is the number of bytes // transferred excluding headers and is often, but not always, present as the // [Content-Length](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length) // header. For requests using transport encoding, this should be the compressed // size. func HTTPRequestContentLength(val int) attribute.KeyValue { return HTTPRequestContentLengthKey.Int(val) } // HTTPResponseContentLength returns an attribute KeyValue conforming to the // "http.response_content_length" semantic conventions. It represents the size // of the response payload body in bytes. This is the number of bytes // transferred excluding headers and is often, but not always, present as the // [Content-Length](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length) // header. For requests using transport encoding, this should be the compressed // size. func HTTPResponseContentLength(val int) attribute.KeyValue { return HTTPResponseContentLengthKey.Int(val) } // Semantic Convention for HTTP Client const ( // HTTPURLKey is the attribute Key conforming to the "http.url" semantic // conventions. It represents the full HTTP request URL in the form // `scheme://host[:port]/path?query[#fragment]`. Usually the fragment is // not transmitted over HTTP, but if it is known, it should be included // nevertheless. // // Type: string // RequirementLevel: Required // Stability: stable // Examples: 'https://www.foo.bar/search?q=OpenTelemetry#SemConv' // Note: `http.url` MUST NOT contain credentials passed via URL in form of // `https://username:password@www.example.com/`. In such case the // attribute's value should be `https://www.example.com/`. HTTPURLKey = attribute.Key("http.url") // HTTPResendCountKey is the attribute Key conforming to the // "http.resend_count" semantic conventions. It represents the ordinal // number of request resending attempt (for any reason, including // redirects). // // Type: int // RequirementLevel: Recommended (if and only if request was retried.) // Stability: stable // Examples: 3 // Note: The resend count SHOULD be updated each time an HTTP request gets // resent by the client, regardless of what was the cause of the resending // (e.g. redirection, authorization failure, 503 Server Unavailable, // network issues, or any other). HTTPResendCountKey = attribute.Key("http.resend_count") ) // HTTPURL returns an attribute KeyValue conforming to the "http.url" // semantic conventions. It represents the full HTTP request URL in the form // `scheme://host[:port]/path?query[#fragment]`. Usually the fragment is not // transmitted over HTTP, but if it is known, it should be included // nevertheless. func HTTPURL(val string) attribute.KeyValue { return HTTPURLKey.String(val) } // HTTPResendCount returns an attribute KeyValue conforming to the // "http.resend_count" semantic conventions. It represents the ordinal number // of request resending attempt (for any reason, including redirects). func HTTPResendCount(val int) attribute.KeyValue { return HTTPResendCountKey.Int(val) } // Semantic Convention for HTTP Server const ( // HTTPSchemeKey is the attribute Key conforming to the "http.scheme" // semantic conventions. It represents the URI scheme identifying the used // protocol. // // Type: string // RequirementLevel: Required // Stability: stable // Examples: 'http', 'https' HTTPSchemeKey = attribute.Key("http.scheme") // HTTPTargetKey is the attribute Key conforming to the "http.target" // semantic conventions. It represents the full request target as passed in // a HTTP request line or equivalent. // // Type: string // RequirementLevel: Required // Stability: stable // Examples: '/path/12314/?q=ddds' HTTPTargetKey = attribute.Key("http.target") // HTTPRouteKey is the attribute Key conforming to the "http.route" // semantic conventions. It represents the matched route (path template in // the format used by the respective server framework). See note below // // Type: string // RequirementLevel: ConditionallyRequired (If and only if it's available) // Stability: stable // Examples: '/users/:userID?', '{controller}/{action}/{id?}' // Note: 'http.route' MUST NOT be populated when this is not supported by // the HTTP server framework as the route attribute should have // low-cardinality and the URI path can NOT substitute it. HTTPRouteKey = attribute.Key("http.route") // HTTPClientIPKey is the attribute Key conforming to the "http.client_ip" // semantic conventions. It represents the IP address of the original // client behind all proxies, if known (e.g. from // [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For)). // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '83.164.160.102' // Note: This is not necessarily the same as `net.sock.peer.addr`, which // would // identify the network-level peer, which may be a proxy. // // This attribute should be set when a source of information different // from the one used for `net.sock.peer.addr`, is available even if that // other // source just confirms the same value as `net.sock.peer.addr`. // Rationale: For `net.sock.peer.addr`, one typically does not know if it // comes from a proxy, reverse proxy, or the actual client. Setting // `http.client_ip` when it's the same as `net.sock.peer.addr` means that // one is at least somewhat confident that the address is not that of // the closest proxy. HTTPClientIPKey = attribute.Key("http.client_ip") ) // HTTPScheme returns an attribute KeyValue conforming to the "http.scheme" // semantic conventions. It represents the URI scheme identifying the used // protocol. func HTTPScheme(val string) attribute.KeyValue { return HTTPSchemeKey.String(val) } // HTTPTarget returns an attribute KeyValue conforming to the "http.target" // semantic conventions. It represents the full request target as passed in a // HTTP request line or equivalent. func HTTPTarget(val string) attribute.KeyValue { return HTTPTargetKey.String(val) } // HTTPRoute returns an attribute KeyValue conforming to the "http.route" // semantic conventions. It represents the matched route (path template in the // format used by the respective server framework). See note below func HTTPRoute(val string) attribute.KeyValue { return HTTPRouteKey.String(val) } // HTTPClientIP returns an attribute KeyValue conforming to the // "http.client_ip" semantic conventions. It represents the IP address of the // original client behind all proxies, if known (e.g. from // [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For)). func HTTPClientIP(val string) attribute.KeyValue { return HTTPClientIPKey.String(val) } // Attributes that exist for multiple DynamoDB request types. const ( // AWSDynamoDBTableNamesKey is the attribute Key conforming to the // "aws.dynamodb.table_names" semantic conventions. It represents the keys // in the `RequestItems` object field. // // Type: string[] // RequirementLevel: Optional // Stability: stable // Examples: 'Users', 'Cats' AWSDynamoDBTableNamesKey = attribute.Key("aws.dynamodb.table_names") // AWSDynamoDBConsumedCapacityKey is the attribute Key conforming to the // "aws.dynamodb.consumed_capacity" semantic conventions. It represents the // JSON-serialized value of each item in the `ConsumedCapacity` response // field. // // Type: string[] // RequirementLevel: Optional // Stability: stable // Examples: '{ "CapacityUnits": number, "GlobalSecondaryIndexes": { // "string" : { "CapacityUnits": number, "ReadCapacityUnits": number, // "WriteCapacityUnits": number } }, "LocalSecondaryIndexes": { "string" : // { "CapacityUnits": number, "ReadCapacityUnits": number, // "WriteCapacityUnits": number } }, "ReadCapacityUnits": number, "Table": // { "CapacityUnits": number, "ReadCapacityUnits": number, // "WriteCapacityUnits": number }, "TableName": "string", // "WriteCapacityUnits": number }' AWSDynamoDBConsumedCapacityKey = attribute.Key("aws.dynamodb.consumed_capacity") // AWSDynamoDBItemCollectionMetricsKey is the attribute Key conforming to // the "aws.dynamodb.item_collection_metrics" semantic conventions. It // represents the JSON-serialized value of the `ItemCollectionMetrics` // response field. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '{ "string" : [ { "ItemCollectionKey": { "string" : { "B": // blob, "BOOL": boolean, "BS": [ blob ], "L": [ "AttributeValue" ], "M": { // "string" : "AttributeValue" }, "N": "string", "NS": [ "string" ], // "NULL": boolean, "S": "string", "SS": [ "string" ] } }, // "SizeEstimateRangeGB": [ number ] } ] }' AWSDynamoDBItemCollectionMetricsKey = attribute.Key("aws.dynamodb.item_collection_metrics") // AWSDynamoDBProvisionedReadCapacityKey is the attribute Key conforming to // the "aws.dynamodb.provisioned_read_capacity" semantic conventions. It // represents the value of the `ProvisionedThroughput.ReadCapacityUnits` // request parameter. // // Type: double // RequirementLevel: Optional // Stability: stable // Examples: 1.0, 2.0 AWSDynamoDBProvisionedReadCapacityKey = attribute.Key("aws.dynamodb.provisioned_read_capacity") // AWSDynamoDBProvisionedWriteCapacityKey is the attribute Key conforming // to the "aws.dynamodb.provisioned_write_capacity" semantic conventions. // It represents the value of the // `ProvisionedThroughput.WriteCapacityUnits` request parameter. // // Type: double // RequirementLevel: Optional // Stability: stable // Examples: 1.0, 2.0 AWSDynamoDBProvisionedWriteCapacityKey = attribute.Key("aws.dynamodb.provisioned_write_capacity") // AWSDynamoDBConsistentReadKey is the attribute Key conforming to the // "aws.dynamodb.consistent_read" semantic conventions. It represents the // value of the `ConsistentRead` request parameter. // // Type: boolean // RequirementLevel: Optional // Stability: stable AWSDynamoDBConsistentReadKey = attribute.Key("aws.dynamodb.consistent_read") // AWSDynamoDBProjectionKey is the attribute Key conforming to the // "aws.dynamodb.projection" semantic conventions. It represents the value // of the `ProjectionExpression` request parameter. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'Title', 'Title, Price, Color', 'Title, Description, // RelatedItems, ProductReviews' AWSDynamoDBProjectionKey = attribute.Key("aws.dynamodb.projection") // AWSDynamoDBLimitKey is the attribute Key conforming to the // "aws.dynamodb.limit" semantic conventions. It represents the value of // the `Limit` request parameter. // // Type: int // RequirementLevel: Optional // Stability: stable // Examples: 10 AWSDynamoDBLimitKey = attribute.Key("aws.dynamodb.limit") // AWSDynamoDBAttributesToGetKey is the attribute Key conforming to the // "aws.dynamodb.attributes_to_get" semantic conventions. It represents the // value of the `AttributesToGet` request parameter. // // Type: string[] // RequirementLevel: Optional // Stability: stable // Examples: 'lives', 'id' AWSDynamoDBAttributesToGetKey = attribute.Key("aws.dynamodb.attributes_to_get") // AWSDynamoDBIndexNameKey is the attribute Key conforming to the // "aws.dynamodb.index_name" semantic conventions. It represents the value // of the `IndexName` request parameter. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'name_to_group' AWSDynamoDBIndexNameKey = attribute.Key("aws.dynamodb.index_name") // AWSDynamoDBSelectKey is the attribute Key conforming to the // "aws.dynamodb.select" semantic conventions. It represents the value of // the `Select` request parameter. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'ALL_ATTRIBUTES', 'COUNT' AWSDynamoDBSelectKey = attribute.Key("aws.dynamodb.select") ) // AWSDynamoDBTableNames returns an attribute KeyValue conforming to the // "aws.dynamodb.table_names" semantic conventions. It represents the keys in // the `RequestItems` object field. func AWSDynamoDBTableNames(val ...string) attribute.KeyValue { return AWSDynamoDBTableNamesKey.StringSlice(val) } // AWSDynamoDBConsumedCapacity returns an attribute KeyValue conforming to // the "aws.dynamodb.consumed_capacity" semantic conventions. It represents the // JSON-serialized value of each item in the `ConsumedCapacity` response field. func AWSDynamoDBConsumedCapacity(val ...string) attribute.KeyValue { return AWSDynamoDBConsumedCapacityKey.StringSlice(val) } // AWSDynamoDBItemCollectionMetrics returns an attribute KeyValue conforming // to the "aws.dynamodb.item_collection_metrics" semantic conventions. It // represents the JSON-serialized value of the `ItemCollectionMetrics` response // field. func AWSDynamoDBItemCollectionMetrics(val string) attribute.KeyValue { return AWSDynamoDBItemCollectionMetricsKey.String(val) } // AWSDynamoDBProvisionedReadCapacity returns an attribute KeyValue // conforming to the "aws.dynamodb.provisioned_read_capacity" semantic // conventions. It represents the value of the // `ProvisionedThroughput.ReadCapacityUnits` request parameter. func AWSDynamoDBProvisionedReadCapacity(val float64) attribute.KeyValue { return AWSDynamoDBProvisionedReadCapacityKey.Float64(val) } // AWSDynamoDBProvisionedWriteCapacity returns an attribute KeyValue // conforming to the "aws.dynamodb.provisioned_write_capacity" semantic // conventions. It represents the value of the // `ProvisionedThroughput.WriteCapacityUnits` request parameter. func AWSDynamoDBProvisionedWriteCapacity(val float64) attribute.KeyValue { return AWSDynamoDBProvisionedWriteCapacityKey.Float64(val) } // AWSDynamoDBConsistentRead returns an attribute KeyValue conforming to the // "aws.dynamodb.consistent_read" semantic conventions. It represents the value // of the `ConsistentRead` request parameter. func AWSDynamoDBConsistentRead(val bool) attribute.KeyValue { return AWSDynamoDBConsistentReadKey.Bool(val) } // AWSDynamoDBProjection returns an attribute KeyValue conforming to the // "aws.dynamodb.projection" semantic conventions. It represents the value of // the `ProjectionExpression` request parameter. func AWSDynamoDBProjection(val string) attribute.KeyValue { return AWSDynamoDBProjectionKey.String(val) } // AWSDynamoDBLimit returns an attribute KeyValue conforming to the // "aws.dynamodb.limit" semantic conventions. It represents the value of the // `Limit` request parameter. func AWSDynamoDBLimit(val int) attribute.KeyValue { return AWSDynamoDBLimitKey.Int(val) } // AWSDynamoDBAttributesToGet returns an attribute KeyValue conforming to // the "aws.dynamodb.attributes_to_get" semantic conventions. It represents the // value of the `AttributesToGet` request parameter. func AWSDynamoDBAttributesToGet(val ...string) attribute.KeyValue { return AWSDynamoDBAttributesToGetKey.StringSlice(val) } // AWSDynamoDBIndexName returns an attribute KeyValue conforming to the // "aws.dynamodb.index_name" semantic conventions. It represents the value of // the `IndexName` request parameter. func AWSDynamoDBIndexName(val string) attribute.KeyValue { return AWSDynamoDBIndexNameKey.String(val) } // AWSDynamoDBSelect returns an attribute KeyValue conforming to the // "aws.dynamodb.select" semantic conventions. It represents the value of the // `Select` request parameter. func AWSDynamoDBSelect(val string) attribute.KeyValue { return AWSDynamoDBSelectKey.String(val) } // DynamoDB.CreateTable const ( // AWSDynamoDBGlobalSecondaryIndexesKey is the attribute Key conforming to // the "aws.dynamodb.global_secondary_indexes" semantic conventions. It // represents the JSON-serialized value of each item of the // `GlobalSecondaryIndexes` request field // // Type: string[] // RequirementLevel: Optional // Stability: stable // Examples: '{ "IndexName": "string", "KeySchema": [ { "AttributeName": // "string", "KeyType": "string" } ], "Projection": { "NonKeyAttributes": [ // "string" ], "ProjectionType": "string" }, "ProvisionedThroughput": { // "ReadCapacityUnits": number, "WriteCapacityUnits": number } }' AWSDynamoDBGlobalSecondaryIndexesKey = attribute.Key("aws.dynamodb.global_secondary_indexes") // AWSDynamoDBLocalSecondaryIndexesKey is the attribute Key conforming to // the "aws.dynamodb.local_secondary_indexes" semantic conventions. It // represents the JSON-serialized value of each item of the // `LocalSecondaryIndexes` request field. // // Type: string[] // RequirementLevel: Optional // Stability: stable // Examples: '{ "IndexARN": "string", "IndexName": "string", // "IndexSizeBytes": number, "ItemCount": number, "KeySchema": [ { // "AttributeName": "string", "KeyType": "string" } ], "Projection": { // "NonKeyAttributes": [ "string" ], "ProjectionType": "string" } }' AWSDynamoDBLocalSecondaryIndexesKey = attribute.Key("aws.dynamodb.local_secondary_indexes") ) // AWSDynamoDBGlobalSecondaryIndexes returns an attribute KeyValue // conforming to the "aws.dynamodb.global_secondary_indexes" semantic // conventions. It represents the JSON-serialized value of each item of the // `GlobalSecondaryIndexes` request field func AWSDynamoDBGlobalSecondaryIndexes(val ...string) attribute.KeyValue { return AWSDynamoDBGlobalSecondaryIndexesKey.StringSlice(val) } // AWSDynamoDBLocalSecondaryIndexes returns an attribute KeyValue conforming // to the "aws.dynamodb.local_secondary_indexes" semantic conventions. It // represents the JSON-serialized value of each item of the // `LocalSecondaryIndexes` request field. func AWSDynamoDBLocalSecondaryIndexes(val ...string) attribute.KeyValue { return AWSDynamoDBLocalSecondaryIndexesKey.StringSlice(val) } // DynamoDB.ListTables const ( // AWSDynamoDBExclusiveStartTableKey is the attribute Key conforming to the // "aws.dynamodb.exclusive_start_table" semantic conventions. It represents // the value of the `ExclusiveStartTableName` request parameter. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'Users', 'CatsTable' AWSDynamoDBExclusiveStartTableKey = attribute.Key("aws.dynamodb.exclusive_start_table") // AWSDynamoDBTableCountKey is the attribute Key conforming to the // "aws.dynamodb.table_count" semantic conventions. It represents the the // number of items in the `TableNames` response parameter. // // Type: int // RequirementLevel: Optional // Stability: stable // Examples: 20 AWSDynamoDBTableCountKey = attribute.Key("aws.dynamodb.table_count") ) // AWSDynamoDBExclusiveStartTable returns an attribute KeyValue conforming // to the "aws.dynamodb.exclusive_start_table" semantic conventions. It // represents the value of the `ExclusiveStartTableName` request parameter. func AWSDynamoDBExclusiveStartTable(val string) attribute.KeyValue { return AWSDynamoDBExclusiveStartTableKey.String(val) } // AWSDynamoDBTableCount returns an attribute KeyValue conforming to the // "aws.dynamodb.table_count" semantic conventions. It represents the the // number of items in the `TableNames` response parameter. func AWSDynamoDBTableCount(val int) attribute.KeyValue { return AWSDynamoDBTableCountKey.Int(val) } // DynamoDB.Query const ( // AWSDynamoDBScanForwardKey is the attribute Key conforming to the // "aws.dynamodb.scan_forward" semantic conventions. It represents the // value of the `ScanIndexForward` request parameter. // // Type: boolean // RequirementLevel: Optional // Stability: stable AWSDynamoDBScanForwardKey = attribute.Key("aws.dynamodb.scan_forward") ) // AWSDynamoDBScanForward returns an attribute KeyValue conforming to the // "aws.dynamodb.scan_forward" semantic conventions. It represents the value of // the `ScanIndexForward` request parameter. func AWSDynamoDBScanForward(val bool) attribute.KeyValue { return AWSDynamoDBScanForwardKey.Bool(val) } // DynamoDB.Scan const ( // AWSDynamoDBSegmentKey is the attribute Key conforming to the // "aws.dynamodb.segment" semantic conventions. It represents the value of // the `Segment` request parameter. // // Type: int // RequirementLevel: Optional // Stability: stable // Examples: 10 AWSDynamoDBSegmentKey = attribute.Key("aws.dynamodb.segment") // AWSDynamoDBTotalSegmentsKey is the attribute Key conforming to the // "aws.dynamodb.total_segments" semantic conventions. It represents the // value of the `TotalSegments` request parameter. // // Type: int // RequirementLevel: Optional // Stability: stable // Examples: 100 AWSDynamoDBTotalSegmentsKey = attribute.Key("aws.dynamodb.total_segments") // AWSDynamoDBCountKey is the attribute Key conforming to the // "aws.dynamodb.count" semantic conventions. It represents the value of // the `Count` response parameter. // // Type: int // RequirementLevel: Optional // Stability: stable // Examples: 10 AWSDynamoDBCountKey = attribute.Key("aws.dynamodb.count") // AWSDynamoDBScannedCountKey is the attribute Key conforming to the // "aws.dynamodb.scanned_count" semantic conventions. It represents the // value of the `ScannedCount` response parameter. // // Type: int // RequirementLevel: Optional // Stability: stable // Examples: 50 AWSDynamoDBScannedCountKey = attribute.Key("aws.dynamodb.scanned_count") ) // AWSDynamoDBSegment returns an attribute KeyValue conforming to the // "aws.dynamodb.segment" semantic conventions. It represents the value of the // `Segment` request parameter. func AWSDynamoDBSegment(val int) attribute.KeyValue { return AWSDynamoDBSegmentKey.Int(val) } // AWSDynamoDBTotalSegments returns an attribute KeyValue conforming to the // "aws.dynamodb.total_segments" semantic conventions. It represents the value // of the `TotalSegments` request parameter. func AWSDynamoDBTotalSegments(val int) attribute.KeyValue { return AWSDynamoDBTotalSegmentsKey.Int(val) } // AWSDynamoDBCount returns an attribute KeyValue conforming to the // "aws.dynamodb.count" semantic conventions. It represents the value of the // `Count` response parameter. func AWSDynamoDBCount(val int) attribute.KeyValue { return AWSDynamoDBCountKey.Int(val) } // AWSDynamoDBScannedCount returns an attribute KeyValue conforming to the // "aws.dynamodb.scanned_count" semantic conventions. It represents the value // of the `ScannedCount` response parameter. func AWSDynamoDBScannedCount(val int) attribute.KeyValue { return AWSDynamoDBScannedCountKey.Int(val) } // DynamoDB.UpdateTable const ( // AWSDynamoDBAttributeDefinitionsKey is the attribute Key conforming to // the "aws.dynamodb.attribute_definitions" semantic conventions. It // represents the JSON-serialized value of each item in the // `AttributeDefinitions` request field. // // Type: string[] // RequirementLevel: Optional // Stability: stable // Examples: '{ "AttributeName": "string", "AttributeType": "string" }' AWSDynamoDBAttributeDefinitionsKey = attribute.Key("aws.dynamodb.attribute_definitions") // AWSDynamoDBGlobalSecondaryIndexUpdatesKey is the attribute Key // conforming to the "aws.dynamodb.global_secondary_index_updates" semantic // conventions. It represents the JSON-serialized value of each item in the // the `GlobalSecondaryIndexUpdates` request field. // // Type: string[] // RequirementLevel: Optional // Stability: stable // Examples: '{ "Create": { "IndexName": "string", "KeySchema": [ { // "AttributeName": "string", "KeyType": "string" } ], "Projection": { // "NonKeyAttributes": [ "string" ], "ProjectionType": "string" }, // "ProvisionedThroughput": { "ReadCapacityUnits": number, // "WriteCapacityUnits": number } }' AWSDynamoDBGlobalSecondaryIndexUpdatesKey = attribute.Key("aws.dynamodb.global_secondary_index_updates") ) // AWSDynamoDBAttributeDefinitions returns an attribute KeyValue conforming // to the "aws.dynamodb.attribute_definitions" semantic conventions. It // represents the JSON-serialized value of each item in the // `AttributeDefinitions` request field. func AWSDynamoDBAttributeDefinitions(val ...string) attribute.KeyValue { return AWSDynamoDBAttributeDefinitionsKey.StringSlice(val) } // AWSDynamoDBGlobalSecondaryIndexUpdates returns an attribute KeyValue // conforming to the "aws.dynamodb.global_secondary_index_updates" semantic // conventions. It represents the JSON-serialized value of each item in the the // `GlobalSecondaryIndexUpdates` request field. func AWSDynamoDBGlobalSecondaryIndexUpdates(val ...string) attribute.KeyValue { return AWSDynamoDBGlobalSecondaryIndexUpdatesKey.StringSlice(val) } // Semantic conventions to apply when instrumenting the GraphQL implementation. // They map GraphQL operations to attributes on a Span. const ( // GraphqlOperationNameKey is the attribute Key conforming to the // "graphql.operation.name" semantic conventions. It represents the name of // the operation being executed. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'findBookByID' GraphqlOperationNameKey = attribute.Key("graphql.operation.name") // GraphqlOperationTypeKey is the attribute Key conforming to the // "graphql.operation.type" semantic conventions. It represents the type of // the operation being executed. // // Type: Enum // RequirementLevel: Optional // Stability: stable // Examples: 'query', 'mutation', 'subscription' GraphqlOperationTypeKey = attribute.Key("graphql.operation.type") // GraphqlDocumentKey is the attribute Key conforming to the // "graphql.document" semantic conventions. It represents the GraphQL // document being executed. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'query findBookByID { bookByID(id: ?) { name } }' // Note: The value may be sanitized to exclude sensitive information. GraphqlDocumentKey = attribute.Key("graphql.document") ) var ( // GraphQL query GraphqlOperationTypeQuery = GraphqlOperationTypeKey.String("query") // GraphQL mutation GraphqlOperationTypeMutation = GraphqlOperationTypeKey.String("mutation") // GraphQL subscription GraphqlOperationTypeSubscription = GraphqlOperationTypeKey.String("subscription") ) // GraphqlOperationName returns an attribute KeyValue conforming to the // "graphql.operation.name" semantic conventions. It represents the name of the // operation being executed. func GraphqlOperationName(val string) attribute.KeyValue { return GraphqlOperationNameKey.String(val) } // GraphqlDocument returns an attribute KeyValue conforming to the // "graphql.document" semantic conventions. It represents the GraphQL document // being executed. func GraphqlDocument(val string) attribute.KeyValue { return GraphqlDocumentKey.String(val) } // Semantic convention describing per-message attributes populated on messaging // spans or links. const ( // MessagingMessageIDKey is the attribute Key conforming to the // "messaging.message.id" semantic conventions. It represents a value used // by the messaging system as an identifier for the message, represented as // a string. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '452a7c7c7c7048c2f887f61572b18fc2' MessagingMessageIDKey = attribute.Key("messaging.message.id") // MessagingMessageConversationIDKey is the attribute Key conforming to the // "messaging.message.conversation_id" semantic conventions. It represents // the [conversation ID](#conversations) identifying the conversation to // which the message belongs, represented as a string. Sometimes called // "Correlation ID". // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'MyConversationID' MessagingMessageConversationIDKey = attribute.Key("messaging.message.conversation_id") // MessagingMessagePayloadSizeBytesKey is the attribute Key conforming to // the "messaging.message.payload_size_bytes" semantic conventions. It // represents the (uncompressed) size of the message payload in bytes. Also // use this attribute if it is unknown whether the compressed or // uncompressed payload size is reported. // // Type: int // RequirementLevel: Optional // Stability: stable // Examples: 2738 MessagingMessagePayloadSizeBytesKey = attribute.Key("messaging.message.payload_size_bytes") // MessagingMessagePayloadCompressedSizeBytesKey is the attribute Key // conforming to the "messaging.message.payload_compressed_size_bytes" // semantic conventions. It represents the compressed size of the message // payload in bytes. // // Type: int // RequirementLevel: Optional // Stability: stable // Examples: 2048 MessagingMessagePayloadCompressedSizeBytesKey = attribute.Key("messaging.message.payload_compressed_size_bytes") ) // MessagingMessageID returns an attribute KeyValue conforming to the // "messaging.message.id" semantic conventions. It represents a value used by // the messaging system as an identifier for the message, represented as a // string. func MessagingMessageID(val string) attribute.KeyValue { return MessagingMessageIDKey.String(val) } // MessagingMessageConversationID returns an attribute KeyValue conforming // to the "messaging.message.conversation_id" semantic conventions. It // represents the [conversation ID](#conversations) identifying the // conversation to which the message belongs, represented as a string. // Sometimes called "Correlation ID". func MessagingMessageConversationID(val string) attribute.KeyValue { return MessagingMessageConversationIDKey.String(val) } // MessagingMessagePayloadSizeBytes returns an attribute KeyValue conforming // to the "messaging.message.payload_size_bytes" semantic conventions. It // represents the (uncompressed) size of the message payload in bytes. Also use // this attribute if it is unknown whether the compressed or uncompressed // payload size is reported. func MessagingMessagePayloadSizeBytes(val int) attribute.KeyValue { return MessagingMessagePayloadSizeBytesKey.Int(val) } // MessagingMessagePayloadCompressedSizeBytes returns an attribute KeyValue // conforming to the "messaging.message.payload_compressed_size_bytes" semantic // conventions. It represents the compressed size of the message payload in // bytes. func MessagingMessagePayloadCompressedSizeBytes(val int) attribute.KeyValue { return MessagingMessagePayloadCompressedSizeBytesKey.Int(val) } // Semantic convention for attributes that describe messaging destination on // broker const ( // MessagingDestinationNameKey is the attribute Key conforming to the // "messaging.destination.name" semantic conventions. It represents the // message destination name // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'MyQueue', 'MyTopic' // Note: Destination name SHOULD uniquely identify a specific queue, topic // or other entity within the broker. If // the broker does not have such notion, the destination name SHOULD // uniquely identify the broker. MessagingDestinationNameKey = attribute.Key("messaging.destination.name") // MessagingDestinationKindKey is the attribute Key conforming to the // "messaging.destination.kind" semantic conventions. It represents the // kind of message destination // // Type: Enum // RequirementLevel: Optional // Stability: stable MessagingDestinationKindKey = attribute.Key("messaging.destination.kind") // MessagingDestinationTemplateKey is the attribute Key conforming to the // "messaging.destination.template" semantic conventions. It represents the // low cardinality representation of the messaging destination name // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '/customers/{customerID}' // Note: Destination names could be constructed from templates. An example // would be a destination name involving a user name or product id. // Although the destination name in this case is of high cardinality, the // underlying template is of low cardinality and can be effectively used // for grouping and aggregation. MessagingDestinationTemplateKey = attribute.Key("messaging.destination.template") // MessagingDestinationTemporaryKey is the attribute Key conforming to the // "messaging.destination.temporary" semantic conventions. It represents a // boolean that is true if the message destination is temporary and might // not exist anymore after messages are processed. // // Type: boolean // RequirementLevel: Optional // Stability: stable MessagingDestinationTemporaryKey = attribute.Key("messaging.destination.temporary") // MessagingDestinationAnonymousKey is the attribute Key conforming to the // "messaging.destination.anonymous" semantic conventions. It represents a // boolean that is true if the message destination is anonymous (could be // unnamed or have auto-generated name). // // Type: boolean // RequirementLevel: Optional // Stability: stable MessagingDestinationAnonymousKey = attribute.Key("messaging.destination.anonymous") ) var ( // A message sent to a queue MessagingDestinationKindQueue = MessagingDestinationKindKey.String("queue") // A message sent to a topic MessagingDestinationKindTopic = MessagingDestinationKindKey.String("topic") ) // MessagingDestinationName returns an attribute KeyValue conforming to the // "messaging.destination.name" semantic conventions. It represents the message // destination name func MessagingDestinationName(val string) attribute.KeyValue { return MessagingDestinationNameKey.String(val) } // MessagingDestinationTemplate returns an attribute KeyValue conforming to // the "messaging.destination.template" semantic conventions. It represents the // low cardinality representation of the messaging destination name func MessagingDestinationTemplate(val string) attribute.KeyValue { return MessagingDestinationTemplateKey.String(val) } // MessagingDestinationTemporary returns an attribute KeyValue conforming to // the "messaging.destination.temporary" semantic conventions. It represents a // boolean that is true if the message destination is temporary and might not // exist anymore after messages are processed. func MessagingDestinationTemporary(val bool) attribute.KeyValue { return MessagingDestinationTemporaryKey.Bool(val) } // MessagingDestinationAnonymous returns an attribute KeyValue conforming to // the "messaging.destination.anonymous" semantic conventions. It represents a // boolean that is true if the message destination is anonymous (could be // unnamed or have auto-generated name). func MessagingDestinationAnonymous(val bool) attribute.KeyValue { return MessagingDestinationAnonymousKey.Bool(val) } // Semantic convention for attributes that describe messaging source on broker const ( // MessagingSourceNameKey is the attribute Key conforming to the // "messaging.source.name" semantic conventions. It represents the message // source name // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'MyQueue', 'MyTopic' // Note: Source name SHOULD uniquely identify a specific queue, topic, or // other entity within the broker. If // the broker does not have such notion, the source name SHOULD uniquely // identify the broker. MessagingSourceNameKey = attribute.Key("messaging.source.name") // MessagingSourceKindKey is the attribute Key conforming to the // "messaging.source.kind" semantic conventions. It represents the kind of // message source // // Type: Enum // RequirementLevel: Optional // Stability: stable MessagingSourceKindKey = attribute.Key("messaging.source.kind") // MessagingSourceTemplateKey is the attribute Key conforming to the // "messaging.source.template" semantic conventions. It represents the low // cardinality representation of the messaging source name // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '/customers/{customerID}' // Note: Source names could be constructed from templates. An example would // be a source name involving a user name or product id. Although the // source name in this case is of high cardinality, the underlying template // is of low cardinality and can be effectively used for grouping and // aggregation. MessagingSourceTemplateKey = attribute.Key("messaging.source.template") // MessagingSourceTemporaryKey is the attribute Key conforming to the // "messaging.source.temporary" semantic conventions. It represents a // boolean that is true if the message source is temporary and might not // exist anymore after messages are processed. // // Type: boolean // RequirementLevel: Optional // Stability: stable MessagingSourceTemporaryKey = attribute.Key("messaging.source.temporary") // MessagingSourceAnonymousKey is the attribute Key conforming to the // "messaging.source.anonymous" semantic conventions. It represents a // boolean that is true if the message source is anonymous (could be // unnamed or have auto-generated name). // // Type: boolean // RequirementLevel: Optional // Stability: stable MessagingSourceAnonymousKey = attribute.Key("messaging.source.anonymous") ) var ( // A message received from a queue MessagingSourceKindQueue = MessagingSourceKindKey.String("queue") // A message received from a topic MessagingSourceKindTopic = MessagingSourceKindKey.String("topic") ) // MessagingSourceName returns an attribute KeyValue conforming to the // "messaging.source.name" semantic conventions. It represents the message // source name func MessagingSourceName(val string) attribute.KeyValue { return MessagingSourceNameKey.String(val) } // MessagingSourceTemplate returns an attribute KeyValue conforming to the // "messaging.source.template" semantic conventions. It represents the low // cardinality representation of the messaging source name func MessagingSourceTemplate(val string) attribute.KeyValue { return MessagingSourceTemplateKey.String(val) } // MessagingSourceTemporary returns an attribute KeyValue conforming to the // "messaging.source.temporary" semantic conventions. It represents a boolean // that is true if the message source is temporary and might not exist anymore // after messages are processed. func MessagingSourceTemporary(val bool) attribute.KeyValue { return MessagingSourceTemporaryKey.Bool(val) } // MessagingSourceAnonymous returns an attribute KeyValue conforming to the // "messaging.source.anonymous" semantic conventions. It represents a boolean // that is true if the message source is anonymous (could be unnamed or have // auto-generated name). func MessagingSourceAnonymous(val bool) attribute.KeyValue { return MessagingSourceAnonymousKey.Bool(val) } // General attributes used in messaging systems. const ( // MessagingSystemKey is the attribute Key conforming to the // "messaging.system" semantic conventions. It represents a string // identifying the messaging system. // // Type: string // RequirementLevel: Required // Stability: stable // Examples: 'kafka', 'rabbitmq', 'rocketmq', 'activemq', 'AmazonSQS' MessagingSystemKey = attribute.Key("messaging.system") // MessagingOperationKey is the attribute Key conforming to the // "messaging.operation" semantic conventions. It represents a string // identifying the kind of messaging operation as defined in the [Operation // names](#operation-names) section above. // // Type: Enum // RequirementLevel: Required // Stability: stable // Note: If a custom value is used, it MUST be of low cardinality. MessagingOperationKey = attribute.Key("messaging.operation") // MessagingBatchMessageCountKey is the attribute Key conforming to the // "messaging.batch.message_count" semantic conventions. It represents the // number of messages sent, received, or processed in the scope of the // batching operation. // // Type: int // RequirementLevel: ConditionallyRequired (If the span describes an // operation on a batch of messages.) // Stability: stable // Examples: 0, 1, 2 // Note: Instrumentations SHOULD NOT set `messaging.batch.message_count` on // spans that operate with a single message. When a messaging client // library supports both batch and single-message API for the same // operation, instrumentations SHOULD use `messaging.batch.message_count` // for batching APIs and SHOULD NOT use it for single-message APIs. MessagingBatchMessageCountKey = attribute.Key("messaging.batch.message_count") ) var ( // publish MessagingOperationPublish = MessagingOperationKey.String("publish") // receive MessagingOperationReceive = MessagingOperationKey.String("receive") // process MessagingOperationProcess = MessagingOperationKey.String("process") ) // MessagingSystem returns an attribute KeyValue conforming to the // "messaging.system" semantic conventions. It represents a string identifying // the messaging system. func MessagingSystem(val string) attribute.KeyValue { return MessagingSystemKey.String(val) } // MessagingBatchMessageCount returns an attribute KeyValue conforming to // the "messaging.batch.message_count" semantic conventions. It represents the // number of messages sent, received, or processed in the scope of the batching // operation. func MessagingBatchMessageCount(val int) attribute.KeyValue { return MessagingBatchMessageCountKey.Int(val) } // Semantic convention for a consumer of messages received from a messaging // system const ( // MessagingConsumerIDKey is the attribute Key conforming to the // "messaging.consumer.id" semantic conventions. It represents the // identifier for the consumer receiving a message. For Kafka, set it to // `{messaging.kafka.consumer.group} - {messaging.kafka.client_id}`, if // both are present, or only `messaging.kafka.consumer.group`. For brokers, // such as RabbitMQ and Artemis, set it to the `client_id` of the client // consuming the message. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'mygroup - client-6' MessagingConsumerIDKey = attribute.Key("messaging.consumer.id") ) // MessagingConsumerID returns an attribute KeyValue conforming to the // "messaging.consumer.id" semantic conventions. It represents the identifier // for the consumer receiving a message. For Kafka, set it to // `{messaging.kafka.consumer.group} - {messaging.kafka.client_id}`, if both // are present, or only `messaging.kafka.consumer.group`. For brokers, such as // RabbitMQ and Artemis, set it to the `client_id` of the client consuming the // message. func MessagingConsumerID(val string) attribute.KeyValue { return MessagingConsumerIDKey.String(val) } // Attributes for RabbitMQ const ( // MessagingRabbitmqDestinationRoutingKeyKey is the attribute Key // conforming to the "messaging.rabbitmq.destination.routing_key" semantic // conventions. It represents the rabbitMQ message routing key. // // Type: string // RequirementLevel: ConditionallyRequired (If not empty.) // Stability: stable // Examples: 'myKey' MessagingRabbitmqDestinationRoutingKeyKey = attribute.Key("messaging.rabbitmq.destination.routing_key") ) // MessagingRabbitmqDestinationRoutingKey returns an attribute KeyValue // conforming to the "messaging.rabbitmq.destination.routing_key" semantic // conventions. It represents the rabbitMQ message routing key. func MessagingRabbitmqDestinationRoutingKey(val string) attribute.KeyValue { return MessagingRabbitmqDestinationRoutingKeyKey.String(val) } // Attributes for Apache Kafka const ( // MessagingKafkaMessageKeyKey is the attribute Key conforming to the // "messaging.kafka.message.key" semantic conventions. It represents the // message keys in Kafka are used for grouping alike messages to ensure // they're processed on the same partition. They differ from // `messaging.message.id` in that they're not unique. If the key is `null`, // the attribute MUST NOT be set. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'myKey' // Note: If the key type is not string, it's string representation has to // be supplied for the attribute. If the key has no unambiguous, canonical // string form, don't include its value. MessagingKafkaMessageKeyKey = attribute.Key("messaging.kafka.message.key") // MessagingKafkaConsumerGroupKey is the attribute Key conforming to the // "messaging.kafka.consumer.group" semantic conventions. It represents the // name of the Kafka Consumer Group that is handling the message. Only // applies to consumers, not producers. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'my-group' MessagingKafkaConsumerGroupKey = attribute.Key("messaging.kafka.consumer.group") // MessagingKafkaClientIDKey is the attribute Key conforming to the // "messaging.kafka.client_id" semantic conventions. It represents the // client ID for the Consumer or Producer that is handling the message. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'client-5' MessagingKafkaClientIDKey = attribute.Key("messaging.kafka.client_id") // MessagingKafkaDestinationPartitionKey is the attribute Key conforming to // the "messaging.kafka.destination.partition" semantic conventions. It // represents the partition the message is sent to. // // Type: int // RequirementLevel: Optional // Stability: stable // Examples: 2 MessagingKafkaDestinationPartitionKey = attribute.Key("messaging.kafka.destination.partition") // MessagingKafkaSourcePartitionKey is the attribute Key conforming to the // "messaging.kafka.source.partition" semantic conventions. It represents // the partition the message is received from. // // Type: int // RequirementLevel: Optional // Stability: stable // Examples: 2 MessagingKafkaSourcePartitionKey = attribute.Key("messaging.kafka.source.partition") // MessagingKafkaMessageOffsetKey is the attribute Key conforming to the // "messaging.kafka.message.offset" semantic conventions. It represents the // offset of a record in the corresponding Kafka partition. // // Type: int // RequirementLevel: Optional // Stability: stable // Examples: 42 MessagingKafkaMessageOffsetKey = attribute.Key("messaging.kafka.message.offset") // MessagingKafkaMessageTombstoneKey is the attribute Key conforming to the // "messaging.kafka.message.tombstone" semantic conventions. It represents // a boolean that is true if the message is a tombstone. // // Type: boolean // RequirementLevel: ConditionallyRequired (If value is `true`. When // missing, the value is assumed to be `false`.) // Stability: stable MessagingKafkaMessageTombstoneKey = attribute.Key("messaging.kafka.message.tombstone") ) // MessagingKafkaMessageKey returns an attribute KeyValue conforming to the // "messaging.kafka.message.key" semantic conventions. It represents the // message keys in Kafka are used for grouping alike messages to ensure they're // processed on the same partition. They differ from `messaging.message.id` in // that they're not unique. If the key is `null`, the attribute MUST NOT be // set. func MessagingKafkaMessageKey(val string) attribute.KeyValue { return MessagingKafkaMessageKeyKey.String(val) } // MessagingKafkaConsumerGroup returns an attribute KeyValue conforming to // the "messaging.kafka.consumer.group" semantic conventions. It represents the // name of the Kafka Consumer Group that is handling the message. Only applies // to consumers, not producers. func MessagingKafkaConsumerGroup(val string) attribute.KeyValue { return MessagingKafkaConsumerGroupKey.String(val) } // MessagingKafkaClientID returns an attribute KeyValue conforming to the // "messaging.kafka.client_id" semantic conventions. It represents the client // ID for the Consumer or Producer that is handling the message. func MessagingKafkaClientID(val string) attribute.KeyValue { return MessagingKafkaClientIDKey.String(val) } // MessagingKafkaDestinationPartition returns an attribute KeyValue // conforming to the "messaging.kafka.destination.partition" semantic // conventions. It represents the partition the message is sent to. func MessagingKafkaDestinationPartition(val int) attribute.KeyValue { return MessagingKafkaDestinationPartitionKey.Int(val) } // MessagingKafkaSourcePartition returns an attribute KeyValue conforming to // the "messaging.kafka.source.partition" semantic conventions. It represents // the partition the message is received from. func MessagingKafkaSourcePartition(val int) attribute.KeyValue { return MessagingKafkaSourcePartitionKey.Int(val) } // MessagingKafkaMessageOffset returns an attribute KeyValue conforming to // the "messaging.kafka.message.offset" semantic conventions. It represents the // offset of a record in the corresponding Kafka partition. func MessagingKafkaMessageOffset(val int) attribute.KeyValue { return MessagingKafkaMessageOffsetKey.Int(val) } // MessagingKafkaMessageTombstone returns an attribute KeyValue conforming // to the "messaging.kafka.message.tombstone" semantic conventions. It // represents a boolean that is true if the message is a tombstone. func MessagingKafkaMessageTombstone(val bool) attribute.KeyValue { return MessagingKafkaMessageTombstoneKey.Bool(val) } // Attributes for Apache RocketMQ const ( // MessagingRocketmqNamespaceKey is the attribute Key conforming to the // "messaging.rocketmq.namespace" semantic conventions. It represents the // namespace of RocketMQ resources, resources in different namespaces are // individual. // // Type: string // RequirementLevel: Required // Stability: stable // Examples: 'myNamespace' MessagingRocketmqNamespaceKey = attribute.Key("messaging.rocketmq.namespace") // MessagingRocketmqClientGroupKey is the attribute Key conforming to the // "messaging.rocketmq.client_group" semantic conventions. It represents // the name of the RocketMQ producer/consumer group that is handling the // message. The client type is identified by the SpanKind. // // Type: string // RequirementLevel: Required // Stability: stable // Examples: 'myConsumerGroup' MessagingRocketmqClientGroupKey = attribute.Key("messaging.rocketmq.client_group") // MessagingRocketmqClientIDKey is the attribute Key conforming to the // "messaging.rocketmq.client_id" semantic conventions. It represents the // unique identifier for each client. // // Type: string // RequirementLevel: Required // Stability: stable // Examples: 'myhost@8742@s8083jm' MessagingRocketmqClientIDKey = attribute.Key("messaging.rocketmq.client_id") // MessagingRocketmqMessageDeliveryTimestampKey is the attribute Key // conforming to the "messaging.rocketmq.message.delivery_timestamp" // semantic conventions. It represents the timestamp in milliseconds that // the delay message is expected to be delivered to consumer. // // Type: int // RequirementLevel: ConditionallyRequired (If the message type is delay // and delay time level is not specified.) // Stability: stable // Examples: 1665987217045 MessagingRocketmqMessageDeliveryTimestampKey = attribute.Key("messaging.rocketmq.message.delivery_timestamp") // MessagingRocketmqMessageDelayTimeLevelKey is the attribute Key // conforming to the "messaging.rocketmq.message.delay_time_level" semantic // conventions. It represents the delay time level for delay message, which // determines the message delay time. // // Type: int // RequirementLevel: ConditionallyRequired (If the message type is delay // and delivery timestamp is not specified.) // Stability: stable // Examples: 3 MessagingRocketmqMessageDelayTimeLevelKey = attribute.Key("messaging.rocketmq.message.delay_time_level") // MessagingRocketmqMessageGroupKey is the attribute Key conforming to the // "messaging.rocketmq.message.group" semantic conventions. It represents // the it is essential for FIFO message. Messages that belong to the same // message group are always processed one by one within the same consumer // group. // // Type: string // RequirementLevel: ConditionallyRequired (If the message type is FIFO.) // Stability: stable // Examples: 'myMessageGroup' MessagingRocketmqMessageGroupKey = attribute.Key("messaging.rocketmq.message.group") // MessagingRocketmqMessageTypeKey is the attribute Key conforming to the // "messaging.rocketmq.message.type" semantic conventions. It represents // the type of message. // // Type: Enum // RequirementLevel: Optional // Stability: stable MessagingRocketmqMessageTypeKey = attribute.Key("messaging.rocketmq.message.type") // MessagingRocketmqMessageTagKey is the attribute Key conforming to the // "messaging.rocketmq.message.tag" semantic conventions. It represents the // secondary classifier of message besides topic. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'tagA' MessagingRocketmqMessageTagKey = attribute.Key("messaging.rocketmq.message.tag") // MessagingRocketmqMessageKeysKey is the attribute Key conforming to the // "messaging.rocketmq.message.keys" semantic conventions. It represents // the key(s) of message, another way to mark message besides message id. // // Type: string[] // RequirementLevel: Optional // Stability: stable // Examples: 'keyA', 'keyB' MessagingRocketmqMessageKeysKey = attribute.Key("messaging.rocketmq.message.keys") // MessagingRocketmqConsumptionModelKey is the attribute Key conforming to // the "messaging.rocketmq.consumption_model" semantic conventions. It // represents the model of message consumption. This only applies to // consumer spans. // // Type: Enum // RequirementLevel: Optional // Stability: stable MessagingRocketmqConsumptionModelKey = attribute.Key("messaging.rocketmq.consumption_model") ) var ( // Normal message MessagingRocketmqMessageTypeNormal = MessagingRocketmqMessageTypeKey.String("normal") // FIFO message MessagingRocketmqMessageTypeFifo = MessagingRocketmqMessageTypeKey.String("fifo") // Delay message MessagingRocketmqMessageTypeDelay = MessagingRocketmqMessageTypeKey.String("delay") // Transaction message MessagingRocketmqMessageTypeTransaction = MessagingRocketmqMessageTypeKey.String("transaction") ) var ( // Clustering consumption model MessagingRocketmqConsumptionModelClustering = MessagingRocketmqConsumptionModelKey.String("clustering") // Broadcasting consumption model MessagingRocketmqConsumptionModelBroadcasting = MessagingRocketmqConsumptionModelKey.String("broadcasting") ) // MessagingRocketmqNamespace returns an attribute KeyValue conforming to // the "messaging.rocketmq.namespace" semantic conventions. It represents the // namespace of RocketMQ resources, resources in different namespaces are // individual. func MessagingRocketmqNamespace(val string) attribute.KeyValue { return MessagingRocketmqNamespaceKey.String(val) } // MessagingRocketmqClientGroup returns an attribute KeyValue conforming to // the "messaging.rocketmq.client_group" semantic conventions. It represents // the name of the RocketMQ producer/consumer group that is handling the // message. The client type is identified by the SpanKind. func MessagingRocketmqClientGroup(val string) attribute.KeyValue { return MessagingRocketmqClientGroupKey.String(val) } // MessagingRocketmqClientID returns an attribute KeyValue conforming to the // "messaging.rocketmq.client_id" semantic conventions. It represents the // unique identifier for each client. func MessagingRocketmqClientID(val string) attribute.KeyValue { return MessagingRocketmqClientIDKey.String(val) } // MessagingRocketmqMessageDeliveryTimestamp returns an attribute KeyValue // conforming to the "messaging.rocketmq.message.delivery_timestamp" semantic // conventions. It represents the timestamp in milliseconds that the delay // message is expected to be delivered to consumer. func MessagingRocketmqMessageDeliveryTimestamp(val int) attribute.KeyValue { return MessagingRocketmqMessageDeliveryTimestampKey.Int(val) } // MessagingRocketmqMessageDelayTimeLevel returns an attribute KeyValue // conforming to the "messaging.rocketmq.message.delay_time_level" semantic // conventions. It represents the delay time level for delay message, which // determines the message delay time. func MessagingRocketmqMessageDelayTimeLevel(val int) attribute.KeyValue { return MessagingRocketmqMessageDelayTimeLevelKey.Int(val) } // MessagingRocketmqMessageGroup returns an attribute KeyValue conforming to // the "messaging.rocketmq.message.group" semantic conventions. It represents // the it is essential for FIFO message. Messages that belong to the same // message group are always processed one by one within the same consumer // group. func MessagingRocketmqMessageGroup(val string) attribute.KeyValue { return MessagingRocketmqMessageGroupKey.String(val) } // MessagingRocketmqMessageTag returns an attribute KeyValue conforming to // the "messaging.rocketmq.message.tag" semantic conventions. It represents the // secondary classifier of message besides topic. func MessagingRocketmqMessageTag(val string) attribute.KeyValue { return MessagingRocketmqMessageTagKey.String(val) } // MessagingRocketmqMessageKeys returns an attribute KeyValue conforming to // the "messaging.rocketmq.message.keys" semantic conventions. It represents // the key(s) of message, another way to mark message besides message id. func MessagingRocketmqMessageKeys(val ...string) attribute.KeyValue { return MessagingRocketmqMessageKeysKey.StringSlice(val) } // Semantic conventions for remote procedure calls. const ( // RPCSystemKey is the attribute Key conforming to the "rpc.system" // semantic conventions. It represents a string identifying the remoting // system. See below for a list of well-known identifiers. // // Type: Enum // RequirementLevel: Required // Stability: stable RPCSystemKey = attribute.Key("rpc.system") // RPCServiceKey is the attribute Key conforming to the "rpc.service" // semantic conventions. It represents the full (logical) name of the // service being called, including its package name, if applicable. // // Type: string // RequirementLevel: Recommended // Stability: stable // Examples: 'myservice.EchoService' // Note: This is the logical name of the service from the RPC interface // perspective, which can be different from the name of any implementing // class. The `code.namespace` attribute may be used to store the latter // (despite the attribute name, it may include a class name; e.g., class // with method actually executing the call on the server side, RPC client // stub class on the client side). RPCServiceKey = attribute.Key("rpc.service") // RPCMethodKey is the attribute Key conforming to the "rpc.method" // semantic conventions. It represents the name of the (logical) method // being called, must be equal to the $method part in the span name. // // Type: string // RequirementLevel: Recommended // Stability: stable // Examples: 'exampleMethod' // Note: This is the logical name of the method from the RPC interface // perspective, which can be different from the name of any implementing // method/function. The `code.function` attribute may be used to store the // latter (e.g., method actually executing the call on the server side, RPC // client stub method on the client side). RPCMethodKey = attribute.Key("rpc.method") ) var ( // gRPC RPCSystemGRPC = RPCSystemKey.String("grpc") // Java RMI RPCSystemJavaRmi = RPCSystemKey.String("java_rmi") // .NET WCF RPCSystemDotnetWcf = RPCSystemKey.String("dotnet_wcf") // Apache Dubbo RPCSystemApacheDubbo = RPCSystemKey.String("apache_dubbo") ) // RPCService returns an attribute KeyValue conforming to the "rpc.service" // semantic conventions. It represents the full (logical) name of the service // being called, including its package name, if applicable. func RPCService(val string) attribute.KeyValue { return RPCServiceKey.String(val) } // RPCMethod returns an attribute KeyValue conforming to the "rpc.method" // semantic conventions. It represents the name of the (logical) method being // called, must be equal to the $method part in the span name. func RPCMethod(val string) attribute.KeyValue { return RPCMethodKey.String(val) } // Tech-specific attributes for gRPC. const ( // RPCGRPCStatusCodeKey is the attribute Key conforming to the // "rpc.grpc.status_code" semantic conventions. It represents the [numeric // status // code](https://github.com/grpc/grpc/blob/v1.33.2/doc/statuscodes.md) of // the gRPC request. // // Type: Enum // RequirementLevel: Required // Stability: stable RPCGRPCStatusCodeKey = attribute.Key("rpc.grpc.status_code") ) var ( // OK RPCGRPCStatusCodeOk = RPCGRPCStatusCodeKey.Int(0) // CANCELLED RPCGRPCStatusCodeCancelled = RPCGRPCStatusCodeKey.Int(1) // UNKNOWN RPCGRPCStatusCodeUnknown = RPCGRPCStatusCodeKey.Int(2) // INVALID_ARGUMENT RPCGRPCStatusCodeInvalidArgument = RPCGRPCStatusCodeKey.Int(3) // DEADLINE_EXCEEDED RPCGRPCStatusCodeDeadlineExceeded = RPCGRPCStatusCodeKey.Int(4) // NOT_FOUND RPCGRPCStatusCodeNotFound = RPCGRPCStatusCodeKey.Int(5) // ALREADY_EXISTS RPCGRPCStatusCodeAlreadyExists = RPCGRPCStatusCodeKey.Int(6) // PERMISSION_DENIED RPCGRPCStatusCodePermissionDenied = RPCGRPCStatusCodeKey.Int(7) // RESOURCE_EXHAUSTED RPCGRPCStatusCodeResourceExhausted = RPCGRPCStatusCodeKey.Int(8) // FAILED_PRECONDITION RPCGRPCStatusCodeFailedPrecondition = RPCGRPCStatusCodeKey.Int(9) // ABORTED RPCGRPCStatusCodeAborted = RPCGRPCStatusCodeKey.Int(10) // OUT_OF_RANGE RPCGRPCStatusCodeOutOfRange = RPCGRPCStatusCodeKey.Int(11) // UNIMPLEMENTED RPCGRPCStatusCodeUnimplemented = RPCGRPCStatusCodeKey.Int(12) // INTERNAL RPCGRPCStatusCodeInternal = RPCGRPCStatusCodeKey.Int(13) // UNAVAILABLE RPCGRPCStatusCodeUnavailable = RPCGRPCStatusCodeKey.Int(14) // DATA_LOSS RPCGRPCStatusCodeDataLoss = RPCGRPCStatusCodeKey.Int(15) // UNAUTHENTICATED RPCGRPCStatusCodeUnauthenticated = RPCGRPCStatusCodeKey.Int(16) ) // Tech-specific attributes for [JSON RPC](https://www.jsonrpc.org/). const ( // RPCJsonrpcVersionKey is the attribute Key conforming to the // "rpc.jsonrpc.version" semantic conventions. It represents the protocol // version as in `jsonrpc` property of request/response. Since JSON-RPC 1.0 // does not specify this, the value can be omitted. // // Type: string // RequirementLevel: ConditionallyRequired (If other than the default // version (`1.0`)) // Stability: stable // Examples: '2.0', '1.0' RPCJsonrpcVersionKey = attribute.Key("rpc.jsonrpc.version") // RPCJsonrpcRequestIDKey is the attribute Key conforming to the // "rpc.jsonrpc.request_id" semantic conventions. It represents the `id` // property of request or response. Since protocol allows id to be int, // string, `null` or missing (for notifications), value is expected to be // cast to string for simplicity. Use empty string in case of `null` value. // Omit entirely if this is a notification. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: '10', 'request-7', '' RPCJsonrpcRequestIDKey = attribute.Key("rpc.jsonrpc.request_id") // RPCJsonrpcErrorCodeKey is the attribute Key conforming to the // "rpc.jsonrpc.error_code" semantic conventions. It represents the // `error.code` property of response if it is an error response. // // Type: int // RequirementLevel: ConditionallyRequired (If response is not successful.) // Stability: stable // Examples: -32700, 100 RPCJsonrpcErrorCodeKey = attribute.Key("rpc.jsonrpc.error_code") // RPCJsonrpcErrorMessageKey is the attribute Key conforming to the // "rpc.jsonrpc.error_message" semantic conventions. It represents the // `error.message` property of response if it is an error response. // // Type: string // RequirementLevel: Optional // Stability: stable // Examples: 'Parse error', 'User already exists' RPCJsonrpcErrorMessageKey = attribute.Key("rpc.jsonrpc.error_message") ) // RPCJsonrpcVersion returns an attribute KeyValue conforming to the // "rpc.jsonrpc.version" semantic conventions. It represents the protocol // version as in `jsonrpc` property of request/response. Since JSON-RPC 1.0 // does not specify this, the value can be omitted. func RPCJsonrpcVersion(val string) attribute.KeyValue { return RPCJsonrpcVersionKey.String(val) } // RPCJsonrpcRequestID returns an attribute KeyValue conforming to the // "rpc.jsonrpc.request_id" semantic conventions. It represents the `id` // property of request or response. Since protocol allows id to be int, string, // `null` or missing (for notifications), value is expected to be cast to // string for simplicity. Use empty string in case of `null` value. Omit // entirely if this is a notification. func RPCJsonrpcRequestID(val string) attribute.KeyValue { return RPCJsonrpcRequestIDKey.String(val) } // RPCJsonrpcErrorCode returns an attribute KeyValue conforming to the // "rpc.jsonrpc.error_code" semantic conventions. It represents the // `error.code` property of response if it is an error response. func RPCJsonrpcErrorCode(val int) attribute.KeyValue { return RPCJsonrpcErrorCodeKey.Int(val) } // RPCJsonrpcErrorMessage returns an attribute KeyValue conforming to the // "rpc.jsonrpc.error_message" semantic conventions. It represents the // `error.message` property of response if it is an error response. func RPCJsonrpcErrorMessage(val string) attribute.KeyValue { return RPCJsonrpcErrorMessageKey.String(val) } ================================================ FILE: vendor/go.opentelemetry.io/otel/trace/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: vendor/go.opentelemetry.io/otel/trace/config.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package trace // import "go.opentelemetry.io/otel/trace" import ( "time" "go.opentelemetry.io/otel/attribute" ) // TracerConfig is a group of options for a Tracer. type TracerConfig struct { instrumentationVersion string // Schema URL of the telemetry emitted by the Tracer. schemaURL string attrs attribute.Set } // InstrumentationVersion returns the version of the library providing instrumentation. func (t *TracerConfig) InstrumentationVersion() string { return t.instrumentationVersion } // InstrumentationAttributes returns the attributes associated with the library // providing instrumentation. func (t *TracerConfig) InstrumentationAttributes() attribute.Set { return t.attrs } // SchemaURL returns the Schema URL of the telemetry emitted by the Tracer. func (t *TracerConfig) SchemaURL() string { return t.schemaURL } // NewTracerConfig applies all the options to a returned TracerConfig. func NewTracerConfig(options ...TracerOption) TracerConfig { var config TracerConfig for _, option := range options { config = option.apply(config) } return config } // TracerOption applies an option to a TracerConfig. type TracerOption interface { apply(TracerConfig) TracerConfig } type tracerOptionFunc func(TracerConfig) TracerConfig func (fn tracerOptionFunc) apply(cfg TracerConfig) TracerConfig { return fn(cfg) } // SpanConfig is a group of options for a Span. type SpanConfig struct { attributes []attribute.KeyValue timestamp time.Time links []Link newRoot bool spanKind SpanKind stackTrace bool } // Attributes describe the associated qualities of a Span. func (cfg *SpanConfig) Attributes() []attribute.KeyValue { return cfg.attributes } // Timestamp is a time in a Span life-cycle. func (cfg *SpanConfig) Timestamp() time.Time { return cfg.timestamp } // StackTrace checks whether stack trace capturing is enabled. func (cfg *SpanConfig) StackTrace() bool { return cfg.stackTrace } // Links are the associations a Span has with other Spans. func (cfg *SpanConfig) Links() []Link { return cfg.links } // NewRoot identifies a Span as the root Span for a new trace. This is // commonly used when an existing trace crosses trust boundaries and the // remote parent span context should be ignored for security. func (cfg *SpanConfig) NewRoot() bool { return cfg.newRoot } // SpanKind is the role a Span has in a trace. func (cfg *SpanConfig) SpanKind() SpanKind { return cfg.spanKind } // NewSpanStartConfig applies all the options to a returned SpanConfig. // No validation is performed on the returned SpanConfig (e.g. no uniqueness // checking or bounding of data), it is left to the SDK to perform this // action. func NewSpanStartConfig(options ...SpanStartOption) SpanConfig { var c SpanConfig for _, option := range options { c = option.applySpanStart(c) } return c } // NewSpanEndConfig applies all the options to a returned SpanConfig. // No validation is performed on the returned SpanConfig (e.g. no uniqueness // checking or bounding of data), it is left to the SDK to perform this // action. func NewSpanEndConfig(options ...SpanEndOption) SpanConfig { var c SpanConfig for _, option := range options { c = option.applySpanEnd(c) } return c } // SpanStartOption applies an option to a SpanConfig. These options are applicable // only when the span is created. type SpanStartOption interface { applySpanStart(SpanConfig) SpanConfig } type spanOptionFunc func(SpanConfig) SpanConfig func (fn spanOptionFunc) applySpanStart(cfg SpanConfig) SpanConfig { return fn(cfg) } // SpanEndOption applies an option to a SpanConfig. These options are // applicable only when the span is ended. type SpanEndOption interface { applySpanEnd(SpanConfig) SpanConfig } // EventConfig is a group of options for an Event. type EventConfig struct { attributes []attribute.KeyValue timestamp time.Time stackTrace bool } // Attributes describe the associated qualities of an Event. func (cfg *EventConfig) Attributes() []attribute.KeyValue { return cfg.attributes } // Timestamp is a time in an Event life-cycle. func (cfg *EventConfig) Timestamp() time.Time { return cfg.timestamp } // StackTrace checks whether stack trace capturing is enabled. func (cfg *EventConfig) StackTrace() bool { return cfg.stackTrace } // NewEventConfig applies all the EventOptions to a returned EventConfig. If no // timestamp option is passed, the returned EventConfig will have a Timestamp // set to the call time, otherwise no validation is performed on the returned // EventConfig. func NewEventConfig(options ...EventOption) EventConfig { var c EventConfig for _, option := range options { c = option.applyEvent(c) } if c.timestamp.IsZero() { c.timestamp = time.Now() } return c } // EventOption applies span event options to an EventConfig. type EventOption interface { applyEvent(EventConfig) EventConfig } // SpanOption are options that can be used at both the beginning and end of a span. type SpanOption interface { SpanStartOption SpanEndOption } // SpanStartEventOption are options that can be used at the start of a span, or with an event. type SpanStartEventOption interface { SpanStartOption EventOption } // SpanEndEventOption are options that can be used at the end of a span, or with an event. type SpanEndEventOption interface { SpanEndOption EventOption } type attributeOption []attribute.KeyValue func (o attributeOption) applySpan(c SpanConfig) SpanConfig { c.attributes = append(c.attributes, []attribute.KeyValue(o)...) return c } func (o attributeOption) applySpanStart(c SpanConfig) SpanConfig { return o.applySpan(c) } func (o attributeOption) applyEvent(c EventConfig) EventConfig { c.attributes = append(c.attributes, []attribute.KeyValue(o)...) return c } var _ SpanStartEventOption = attributeOption{} // WithAttributes adds the attributes related to a span life-cycle event. // These attributes are used to describe the work a Span represents when this // option is provided to a Span's start or end events. Otherwise, these // attributes provide additional information about the event being recorded // (e.g. error, state change, processing progress, system event). // // If multiple of these options are passed the attributes of each successive // option will extend the attributes instead of overwriting. There is no // guarantee of uniqueness in the resulting attributes. func WithAttributes(attributes ...attribute.KeyValue) SpanStartEventOption { return attributeOption(attributes) } // SpanEventOption are options that can be used with an event or a span. type SpanEventOption interface { SpanOption EventOption } type timestampOption time.Time func (o timestampOption) applySpan(c SpanConfig) SpanConfig { c.timestamp = time.Time(o) return c } func (o timestampOption) applySpanStart(c SpanConfig) SpanConfig { return o.applySpan(c) } func (o timestampOption) applySpanEnd(c SpanConfig) SpanConfig { return o.applySpan(c) } func (o timestampOption) applyEvent(c EventConfig) EventConfig { c.timestamp = time.Time(o) return c } var _ SpanEventOption = timestampOption{} // WithTimestamp sets the time of a Span or Event life-cycle moment (e.g. // started, stopped, errored). func WithTimestamp(t time.Time) SpanEventOption { return timestampOption(t) } type stackTraceOption bool func (o stackTraceOption) applyEvent(c EventConfig) EventConfig { c.stackTrace = bool(o) return c } func (o stackTraceOption) applySpan(c SpanConfig) SpanConfig { c.stackTrace = bool(o) return c } func (o stackTraceOption) applySpanEnd(c SpanConfig) SpanConfig { return o.applySpan(c) } // WithStackTrace sets the flag to capture the error with stack trace (e.g. true, false). func WithStackTrace(b bool) SpanEndEventOption { return stackTraceOption(b) } // WithLinks adds links to a Span. The links are added to the existing Span // links, i.e. this does not overwrite. Links with invalid span context are ignored. func WithLinks(links ...Link) SpanStartOption { return spanOptionFunc(func(cfg SpanConfig) SpanConfig { cfg.links = append(cfg.links, links...) return cfg }) } // WithNewRoot specifies that the Span should be treated as a root Span. Any // existing parent span context will be ignored when defining the Span's trace // identifiers. func WithNewRoot() SpanStartOption { return spanOptionFunc(func(cfg SpanConfig) SpanConfig { cfg.newRoot = true return cfg }) } // WithSpanKind sets the SpanKind of a Span. func WithSpanKind(kind SpanKind) SpanStartOption { return spanOptionFunc(func(cfg SpanConfig) SpanConfig { cfg.spanKind = kind return cfg }) } // WithInstrumentationVersion sets the instrumentation version. func WithInstrumentationVersion(version string) TracerOption { return tracerOptionFunc(func(cfg TracerConfig) TracerConfig { cfg.instrumentationVersion = version return cfg }) } // WithInstrumentationAttributes sets the instrumentation attributes. // // The passed attributes will be de-duplicated. func WithInstrumentationAttributes(attr ...attribute.KeyValue) TracerOption { return tracerOptionFunc(func(config TracerConfig) TracerConfig { config.attrs = attribute.NewSet(attr...) return config }) } // WithSchemaURL sets the schema URL for the Tracer. func WithSchemaURL(schemaURL string) TracerOption { return tracerOptionFunc(func(cfg TracerConfig) TracerConfig { cfg.schemaURL = schemaURL return cfg }) } ================================================ FILE: vendor/go.opentelemetry.io/otel/trace/context.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package trace // import "go.opentelemetry.io/otel/trace" import "context" type traceContextKeyType int const currentSpanKey traceContextKeyType = iota // ContextWithSpan returns a copy of parent with span set as the current Span. func ContextWithSpan(parent context.Context, span Span) context.Context { return context.WithValue(parent, currentSpanKey, span) } // ContextWithSpanContext returns a copy of parent with sc as the current // Span. The Span implementation that wraps sc is non-recording and performs // no operations other than to return sc as the SpanContext from the // SpanContext method. func ContextWithSpanContext(parent context.Context, sc SpanContext) context.Context { return ContextWithSpan(parent, nonRecordingSpan{sc: sc}) } // ContextWithRemoteSpanContext returns a copy of parent with rsc set explicly // as a remote SpanContext and as the current Span. The Span implementation // that wraps rsc is non-recording and performs no operations other than to // return rsc as the SpanContext from the SpanContext method. func ContextWithRemoteSpanContext(parent context.Context, rsc SpanContext) context.Context { return ContextWithSpanContext(parent, rsc.WithRemote(true)) } // SpanFromContext returns the current Span from ctx. // // If no Span is currently set in ctx an implementation of a Span that // performs no operations is returned. func SpanFromContext(ctx context.Context) Span { if ctx == nil { return noopSpan{} } if span, ok := ctx.Value(currentSpanKey).(Span); ok { return span } return noopSpan{} } // SpanContextFromContext returns the current Span's SpanContext. func SpanContextFromContext(ctx context.Context) SpanContext { return SpanFromContext(ctx).SpanContext() } ================================================ FILE: vendor/go.opentelemetry.io/otel/trace/doc.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /* Package trace provides an implementation of the tracing part of the OpenTelemetry API. To participate in distributed traces a Span needs to be created for the operation being performed as part of a traced workflow. In its simplest form: var tracer trace.Tracer func init() { tracer = otel.Tracer("instrumentation/package/name") } func operation(ctx context.Context) { var span trace.Span ctx, span = tracer.Start(ctx, "operation") defer span.End() // ... } A Tracer is unique to the instrumentation and is used to create Spans. Instrumentation should be designed to accept a TracerProvider from which it can create its own unique Tracer. Alternatively, the registered global TracerProvider from the go.opentelemetry.io/otel package can be used as a default. const ( name = "instrumentation/package/name" version = "0.1.0" ) type Instrumentation struct { tracer trace.Tracer } func NewInstrumentation(tp trace.TracerProvider) *Instrumentation { if tp == nil { tp = otel.TracerProvider() } return &Instrumentation{ tracer: tp.Tracer(name, trace.WithInstrumentationVersion(version)), } } func operation(ctx context.Context, inst *Instrumentation) { var span trace.Span ctx, span = inst.tracer.Start(ctx, "operation") defer span.End() // ... } */ package trace // import "go.opentelemetry.io/otel/trace" ================================================ FILE: vendor/go.opentelemetry.io/otel/trace/nonrecording.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package trace // import "go.opentelemetry.io/otel/trace" // nonRecordingSpan is a minimal implementation of a Span that wraps a // SpanContext. It performs no operations other than to return the wrapped // SpanContext. type nonRecordingSpan struct { noopSpan sc SpanContext } // SpanContext returns the wrapped SpanContext. func (s nonRecordingSpan) SpanContext() SpanContext { return s.sc } ================================================ FILE: vendor/go.opentelemetry.io/otel/trace/noop.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package trace // import "go.opentelemetry.io/otel/trace" import ( "context" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" ) // NewNoopTracerProvider returns an implementation of TracerProvider that // performs no operations. The Tracer and Spans created from the returned // TracerProvider also perform no operations. func NewNoopTracerProvider() TracerProvider { return noopTracerProvider{} } type noopTracerProvider struct{} var _ TracerProvider = noopTracerProvider{} // Tracer returns noop implementation of Tracer. func (p noopTracerProvider) Tracer(string, ...TracerOption) Tracer { return noopTracer{} } // noopTracer is an implementation of Tracer that performs no operations. type noopTracer struct{} var _ Tracer = noopTracer{} // Start carries forward a non-recording Span, if one is present in the context, otherwise it // creates a no-op Span. func (t noopTracer) Start(ctx context.Context, name string, _ ...SpanStartOption) (context.Context, Span) { span := SpanFromContext(ctx) if _, ok := span.(nonRecordingSpan); !ok { // span is likely already a noopSpan, but let's be sure span = noopSpan{} } return ContextWithSpan(ctx, span), span } // noopSpan is an implementation of Span that performs no operations. type noopSpan struct{} var _ Span = noopSpan{} // SpanContext returns an empty span context. func (noopSpan) SpanContext() SpanContext { return SpanContext{} } // IsRecording always returns false. func (noopSpan) IsRecording() bool { return false } // SetStatus does nothing. func (noopSpan) SetStatus(codes.Code, string) {} // SetError does nothing. func (noopSpan) SetError(bool) {} // SetAttributes does nothing. func (noopSpan) SetAttributes(...attribute.KeyValue) {} // End does nothing. func (noopSpan) End(...SpanEndOption) {} // RecordError does nothing. func (noopSpan) RecordError(error, ...EventOption) {} // AddEvent does nothing. func (noopSpan) AddEvent(string, ...EventOption) {} // SetName does nothing. func (noopSpan) SetName(string) {} // TracerProvider returns a no-op TracerProvider. func (noopSpan) TracerProvider() TracerProvider { return noopTracerProvider{} } ================================================ FILE: vendor/go.opentelemetry.io/otel/trace/trace.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package trace // import "go.opentelemetry.io/otel/trace" import ( "bytes" "context" "encoding/hex" "encoding/json" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" ) const ( // FlagsSampled is a bitmask with the sampled bit set. A SpanContext // with the sampling bit set means the span is sampled. FlagsSampled = TraceFlags(0x01) errInvalidHexID errorConst = "trace-id and span-id can only contain [0-9a-f] characters, all lowercase" errInvalidTraceIDLength errorConst = "hex encoded trace-id must have length equals to 32" errNilTraceID errorConst = "trace-id can't be all zero" errInvalidSpanIDLength errorConst = "hex encoded span-id must have length equals to 16" errNilSpanID errorConst = "span-id can't be all zero" ) type errorConst string func (e errorConst) Error() string { return string(e) } // TraceID is a unique identity of a trace. // nolint:revive // revive complains about stutter of `trace.TraceID`. type TraceID [16]byte var nilTraceID TraceID var _ json.Marshaler = nilTraceID // IsValid checks whether the trace TraceID is valid. A valid trace ID does // not consist of zeros only. func (t TraceID) IsValid() bool { return !bytes.Equal(t[:], nilTraceID[:]) } // MarshalJSON implements a custom marshal function to encode TraceID // as a hex string. func (t TraceID) MarshalJSON() ([]byte, error) { return json.Marshal(t.String()) } // String returns the hex string representation form of a TraceID. func (t TraceID) String() string { return hex.EncodeToString(t[:]) } // SpanID is a unique identity of a span in a trace. type SpanID [8]byte var nilSpanID SpanID var _ json.Marshaler = nilSpanID // IsValid checks whether the SpanID is valid. A valid SpanID does not consist // of zeros only. func (s SpanID) IsValid() bool { return !bytes.Equal(s[:], nilSpanID[:]) } // MarshalJSON implements a custom marshal function to encode SpanID // as a hex string. func (s SpanID) MarshalJSON() ([]byte, error) { return json.Marshal(s.String()) } // String returns the hex string representation form of a SpanID. func (s SpanID) String() string { return hex.EncodeToString(s[:]) } // TraceIDFromHex returns a TraceID from a hex string if it is compliant with // the W3C trace-context specification. See more at // https://www.w3.org/TR/trace-context/#trace-id // nolint:revive // revive complains about stutter of `trace.TraceIDFromHex`. func TraceIDFromHex(h string) (TraceID, error) { t := TraceID{} if len(h) != 32 { return t, errInvalidTraceIDLength } if err := decodeHex(h, t[:]); err != nil { return t, err } if !t.IsValid() { return t, errNilTraceID } return t, nil } // SpanIDFromHex returns a SpanID from a hex string if it is compliant // with the w3c trace-context specification. // See more at https://www.w3.org/TR/trace-context/#parent-id func SpanIDFromHex(h string) (SpanID, error) { s := SpanID{} if len(h) != 16 { return s, errInvalidSpanIDLength } if err := decodeHex(h, s[:]); err != nil { return s, err } if !s.IsValid() { return s, errNilSpanID } return s, nil } func decodeHex(h string, b []byte) error { for _, r := range h { switch { case 'a' <= r && r <= 'f': continue case '0' <= r && r <= '9': continue default: return errInvalidHexID } } decoded, err := hex.DecodeString(h) if err != nil { return err } copy(b, decoded) return nil } // TraceFlags contains flags that can be set on a SpanContext. type TraceFlags byte //nolint:revive // revive complains about stutter of `trace.TraceFlags`. // IsSampled returns if the sampling bit is set in the TraceFlags. func (tf TraceFlags) IsSampled() bool { return tf&FlagsSampled == FlagsSampled } // WithSampled sets the sampling bit in a new copy of the TraceFlags. func (tf TraceFlags) WithSampled(sampled bool) TraceFlags { // nolint:revive // sampled is not a control flag. if sampled { return tf | FlagsSampled } return tf &^ FlagsSampled } // MarshalJSON implements a custom marshal function to encode TraceFlags // as a hex string. func (tf TraceFlags) MarshalJSON() ([]byte, error) { return json.Marshal(tf.String()) } // String returns the hex string representation form of TraceFlags. func (tf TraceFlags) String() string { return hex.EncodeToString([]byte{byte(tf)}[:]) } // SpanContextConfig contains mutable fields usable for constructing // an immutable SpanContext. type SpanContextConfig struct { TraceID TraceID SpanID SpanID TraceFlags TraceFlags TraceState TraceState Remote bool } // NewSpanContext constructs a SpanContext using values from the provided // SpanContextConfig. func NewSpanContext(config SpanContextConfig) SpanContext { return SpanContext{ traceID: config.TraceID, spanID: config.SpanID, traceFlags: config.TraceFlags, traceState: config.TraceState, remote: config.Remote, } } // SpanContext contains identifying trace information about a Span. type SpanContext struct { traceID TraceID spanID SpanID traceFlags TraceFlags traceState TraceState remote bool } var _ json.Marshaler = SpanContext{} // IsValid returns if the SpanContext is valid. A valid span context has a // valid TraceID and SpanID. func (sc SpanContext) IsValid() bool { return sc.HasTraceID() && sc.HasSpanID() } // IsRemote indicates whether the SpanContext represents a remotely-created Span. func (sc SpanContext) IsRemote() bool { return sc.remote } // WithRemote returns a copy of sc with the Remote property set to remote. func (sc SpanContext) WithRemote(remote bool) SpanContext { return SpanContext{ traceID: sc.traceID, spanID: sc.spanID, traceFlags: sc.traceFlags, traceState: sc.traceState, remote: remote, } } // TraceID returns the TraceID from the SpanContext. func (sc SpanContext) TraceID() TraceID { return sc.traceID } // HasTraceID checks if the SpanContext has a valid TraceID. func (sc SpanContext) HasTraceID() bool { return sc.traceID.IsValid() } // WithTraceID returns a new SpanContext with the TraceID replaced. func (sc SpanContext) WithTraceID(traceID TraceID) SpanContext { return SpanContext{ traceID: traceID, spanID: sc.spanID, traceFlags: sc.traceFlags, traceState: sc.traceState, remote: sc.remote, } } // SpanID returns the SpanID from the SpanContext. func (sc SpanContext) SpanID() SpanID { return sc.spanID } // HasSpanID checks if the SpanContext has a valid SpanID. func (sc SpanContext) HasSpanID() bool { return sc.spanID.IsValid() } // WithSpanID returns a new SpanContext with the SpanID replaced. func (sc SpanContext) WithSpanID(spanID SpanID) SpanContext { return SpanContext{ traceID: sc.traceID, spanID: spanID, traceFlags: sc.traceFlags, traceState: sc.traceState, remote: sc.remote, } } // TraceFlags returns the flags from the SpanContext. func (sc SpanContext) TraceFlags() TraceFlags { return sc.traceFlags } // IsSampled returns if the sampling bit is set in the SpanContext's TraceFlags. func (sc SpanContext) IsSampled() bool { return sc.traceFlags.IsSampled() } // WithTraceFlags returns a new SpanContext with the TraceFlags replaced. func (sc SpanContext) WithTraceFlags(flags TraceFlags) SpanContext { return SpanContext{ traceID: sc.traceID, spanID: sc.spanID, traceFlags: flags, traceState: sc.traceState, remote: sc.remote, } } // TraceState returns the TraceState from the SpanContext. func (sc SpanContext) TraceState() TraceState { return sc.traceState } // WithTraceState returns a new SpanContext with the TraceState replaced. func (sc SpanContext) WithTraceState(state TraceState) SpanContext { return SpanContext{ traceID: sc.traceID, spanID: sc.spanID, traceFlags: sc.traceFlags, traceState: state, remote: sc.remote, } } // Equal is a predicate that determines whether two SpanContext values are equal. func (sc SpanContext) Equal(other SpanContext) bool { return sc.traceID == other.traceID && sc.spanID == other.spanID && sc.traceFlags == other.traceFlags && sc.traceState.String() == other.traceState.String() && sc.remote == other.remote } // MarshalJSON implements a custom marshal function to encode a SpanContext. func (sc SpanContext) MarshalJSON() ([]byte, error) { return json.Marshal(SpanContextConfig{ TraceID: sc.traceID, SpanID: sc.spanID, TraceFlags: sc.traceFlags, TraceState: sc.traceState, Remote: sc.remote, }) } // Span is the individual component of a trace. It represents a single named // and timed operation of a workflow that is traced. A Tracer is used to // create a Span and it is then up to the operation the Span represents to // properly end the Span when the operation itself ends. // // Warning: methods may be added to this interface in minor releases. type Span interface { // End completes the Span. The Span is considered complete and ready to be // delivered through the rest of the telemetry pipeline after this method // is called. Therefore, updates to the Span are not allowed after this // method has been called. End(options ...SpanEndOption) // AddEvent adds an event with the provided name and options. AddEvent(name string, options ...EventOption) // IsRecording returns the recording state of the Span. It will return // true if the Span is active and events can be recorded. IsRecording() bool // RecordError will record err as an exception span event for this span. An // additional call to SetStatus is required if the Status of the Span should // be set to Error, as this method does not change the Span status. If this // span is not being recorded or err is nil then this method does nothing. RecordError(err error, options ...EventOption) // SpanContext returns the SpanContext of the Span. The returned SpanContext // is usable even after the End method has been called for the Span. SpanContext() SpanContext // SetStatus sets the status of the Span in the form of a code and a // description, provided the status hasn't already been set to a higher // value before (OK > Error > Unset). The description is only included in a // status when the code is for an error. SetStatus(code codes.Code, description string) // SetName sets the Span name. SetName(name string) // SetAttributes sets kv as attributes of the Span. If a key from kv // already exists for an attribute of the Span it will be overwritten with // the value contained in kv. SetAttributes(kv ...attribute.KeyValue) // TracerProvider returns a TracerProvider that can be used to generate // additional Spans on the same telemetry pipeline as the current Span. TracerProvider() TracerProvider } // Link is the relationship between two Spans. The relationship can be within // the same Trace or across different Traces. // // For example, a Link is used in the following situations: // // 1. Batch Processing: A batch of operations may contain operations // associated with one or more traces/spans. Since there can only be one // parent SpanContext, a Link is used to keep reference to the // SpanContext of all operations in the batch. // 2. Public Endpoint: A SpanContext for an in incoming client request on a // public endpoint should be considered untrusted. In such a case, a new // trace with its own identity and sampling decision needs to be created, // but this new trace needs to be related to the original trace in some // form. A Link is used to keep reference to the original SpanContext and // track the relationship. type Link struct { // SpanContext of the linked Span. SpanContext SpanContext // Attributes describe the aspects of the link. Attributes []attribute.KeyValue } // LinkFromContext returns a link encapsulating the SpanContext in the provided ctx. func LinkFromContext(ctx context.Context, attrs ...attribute.KeyValue) Link { return Link{ SpanContext: SpanContextFromContext(ctx), Attributes: attrs, } } // SpanKind is the role a Span plays in a Trace. type SpanKind int // As a convenience, these match the proto definition, see // https://github.com/open-telemetry/opentelemetry-proto/blob/30d237e1ff3ab7aa50e0922b5bebdd93505090af/opentelemetry/proto/trace/v1/trace.proto#L101-L129 // // The unspecified value is not a valid `SpanKind`. Use `ValidateSpanKind()` // to coerce a span kind to a valid value. const ( // SpanKindUnspecified is an unspecified SpanKind and is not a valid // SpanKind. SpanKindUnspecified should be replaced with SpanKindInternal // if it is received. SpanKindUnspecified SpanKind = 0 // SpanKindInternal is a SpanKind for a Span that represents an internal // operation within an application. SpanKindInternal SpanKind = 1 // SpanKindServer is a SpanKind for a Span that represents the operation // of handling a request from a client. SpanKindServer SpanKind = 2 // SpanKindClient is a SpanKind for a Span that represents the operation // of client making a request to a server. SpanKindClient SpanKind = 3 // SpanKindProducer is a SpanKind for a Span that represents the operation // of a producer sending a message to a message broker. Unlike // SpanKindClient and SpanKindServer, there is often no direct // relationship between this kind of Span and a SpanKindConsumer kind. A // SpanKindProducer Span will end once the message is accepted by the // message broker which might not overlap with the processing of that // message. SpanKindProducer SpanKind = 4 // SpanKindConsumer is a SpanKind for a Span that represents the operation // of a consumer receiving a message from a message broker. Like // SpanKindProducer Spans, there is often no direct relationship between // this Span and the Span that produced the message. SpanKindConsumer SpanKind = 5 ) // ValidateSpanKind returns a valid span kind value. This will coerce // invalid values into the default value, SpanKindInternal. func ValidateSpanKind(spanKind SpanKind) SpanKind { switch spanKind { case SpanKindInternal, SpanKindServer, SpanKindClient, SpanKindProducer, SpanKindConsumer: // valid return spanKind default: return SpanKindInternal } } // String returns the specified name of the SpanKind in lower-case. func (sk SpanKind) String() string { switch sk { case SpanKindInternal: return "internal" case SpanKindServer: return "server" case SpanKindClient: return "client" case SpanKindProducer: return "producer" case SpanKindConsumer: return "consumer" default: return "unspecified" } } // Tracer is the creator of Spans. // // Warning: methods may be added to this interface in minor releases. type Tracer interface { // Start creates a span and a context.Context containing the newly-created span. // // If the context.Context provided in `ctx` contains a Span then the newly-created // Span will be a child of that span, otherwise it will be a root span. This behavior // can be overridden by providing `WithNewRoot()` as a SpanOption, causing the // newly-created Span to be a root span even if `ctx` contains a Span. // // When creating a Span it is recommended to provide all known span attributes using // the `WithAttributes()` SpanOption as samplers will only have access to the // attributes provided when a Span is created. // // Any Span that is created MUST also be ended. This is the responsibility of the user. // Implementations of this API may leak memory or other resources if Spans are not ended. Start(ctx context.Context, spanName string, opts ...SpanStartOption) (context.Context, Span) } // TracerProvider provides Tracers that are used by instrumentation code to // trace computational workflows. // // A TracerProvider is the collection destination of all Spans from Tracers it // provides, it represents a unique telemetry collection pipeline. How that // pipeline is defined, meaning how those Spans are collected, processed, and // where they are exported, depends on its implementation. Instrumentation // authors do not need to define this implementation, rather just use the // provided Tracers to instrument code. // // Commonly, instrumentation code will accept a TracerProvider implementation // at runtime from its users or it can simply use the globally registered one // (see https://pkg.go.dev/go.opentelemetry.io/otel#GetTracerProvider). // // Warning: methods may be added to this interface in minor releases. type TracerProvider interface { // Tracer returns a unique Tracer scoped to be used by instrumentation code // to trace computational workflows. The scope and identity of that // instrumentation code is uniquely defined by the name and options passed. // // The passed name needs to uniquely identify instrumentation code. // Therefore, it is recommended that name is the Go package name of the // library providing instrumentation (note: not the code being // instrumented). Instrumentation libraries can have multiple versions, // therefore, the WithInstrumentationVersion option should be used to // distinguish these different codebases. Additionally, instrumentation // libraries may sometimes use traces to communicate different domains of // workflow data (i.e. using spans to communicate workflow events only). If // this is the case, the WithScopeAttributes option should be used to // uniquely identify Tracers that handle the different domains of workflow // data. // // If the same name and options are passed multiple times, the same Tracer // will be returned (it is up to the implementation if this will be the // same underlying instance of that Tracer or not). It is not necessary to // call this multiple times with the same name and options to get an // up-to-date Tracer. All implementations will ensure any TracerProvider // configuration changes are propagated to all provided Tracers. // // If name is empty, then an implementation defined default name will be // used instead. // // This method is safe to call concurrently. Tracer(name string, options ...TracerOption) Tracer } ================================================ FILE: vendor/go.opentelemetry.io/otel/trace/tracestate.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package trace // import "go.opentelemetry.io/otel/trace" import ( "encoding/json" "fmt" "regexp" "strings" ) const ( maxListMembers = 32 listDelimiter = "," // based on the W3C Trace Context specification, see // https://www.w3.org/TR/trace-context-1/#tracestate-header noTenantKeyFormat = `[a-z][_0-9a-z\-\*\/]{0,255}` withTenantKeyFormat = `[a-z0-9][_0-9a-z\-\*\/]{0,240}@[a-z][_0-9a-z\-\*\/]{0,13}` valueFormat = `[\x20-\x2b\x2d-\x3c\x3e-\x7e]{0,255}[\x21-\x2b\x2d-\x3c\x3e-\x7e]` errInvalidKey errorConst = "invalid tracestate key" errInvalidValue errorConst = "invalid tracestate value" errInvalidMember errorConst = "invalid tracestate list-member" errMemberNumber errorConst = "too many list-members in tracestate" errDuplicate errorConst = "duplicate list-member in tracestate" ) var ( keyRe = regexp.MustCompile(`^((` + noTenantKeyFormat + `)|(` + withTenantKeyFormat + `))$`) valueRe = regexp.MustCompile(`^(` + valueFormat + `)$`) memberRe = regexp.MustCompile(`^\s*((` + noTenantKeyFormat + `)|(` + withTenantKeyFormat + `))=(` + valueFormat + `)\s*$`) ) type member struct { Key string Value string } func newMember(key, value string) (member, error) { if !keyRe.MatchString(key) { return member{}, fmt.Errorf("%w: %s", errInvalidKey, key) } if !valueRe.MatchString(value) { return member{}, fmt.Errorf("%w: %s", errInvalidValue, value) } return member{Key: key, Value: value}, nil } func parseMember(m string) (member, error) { matches := memberRe.FindStringSubmatch(m) if len(matches) != 5 { return member{}, fmt.Errorf("%w: %s", errInvalidMember, m) } return member{ Key: matches[1], Value: matches[4], }, nil } // String encodes member into a string compliant with the W3C Trace Context // specification. func (m member) String() string { return fmt.Sprintf("%s=%s", m.Key, m.Value) } // TraceState provides additional vendor-specific trace identification // information across different distributed tracing systems. It represents an // immutable list consisting of key/value pairs, each pair is referred to as a // list-member. // // TraceState conforms to the W3C Trace Context specification // (https://www.w3.org/TR/trace-context-1). All operations that create or copy // a TraceState do so by validating all input and will only produce TraceState // that conform to the specification. Specifically, this means that all // list-member's key/value pairs are valid, no duplicate list-members exist, // and the maximum number of list-members (32) is not exceeded. type TraceState struct { //nolint:revive // revive complains about stutter of `trace.TraceState` // list is the members in order. list []member } var _ json.Marshaler = TraceState{} // ParseTraceState attempts to decode a TraceState from the passed // string. It returns an error if the input is invalid according to the W3C // Trace Context specification. func ParseTraceState(tracestate string) (TraceState, error) { if tracestate == "" { return TraceState{}, nil } wrapErr := func(err error) error { return fmt.Errorf("failed to parse tracestate: %w", err) } var members []member found := make(map[string]struct{}) for _, memberStr := range strings.Split(tracestate, listDelimiter) { if len(memberStr) == 0 { continue } m, err := parseMember(memberStr) if err != nil { return TraceState{}, wrapErr(err) } if _, ok := found[m.Key]; ok { return TraceState{}, wrapErr(errDuplicate) } found[m.Key] = struct{}{} members = append(members, m) if n := len(members); n > maxListMembers { return TraceState{}, wrapErr(errMemberNumber) } } return TraceState{list: members}, nil } // MarshalJSON marshals the TraceState into JSON. func (ts TraceState) MarshalJSON() ([]byte, error) { return json.Marshal(ts.String()) } // String encodes the TraceState into a string compliant with the W3C // Trace Context specification. The returned string will be invalid if the // TraceState contains any invalid members. func (ts TraceState) String() string { members := make([]string, len(ts.list)) for i, m := range ts.list { members[i] = m.String() } return strings.Join(members, listDelimiter) } // Get returns the value paired with key from the corresponding TraceState // list-member if it exists, otherwise an empty string is returned. func (ts TraceState) Get(key string) string { for _, member := range ts.list { if member.Key == key { return member.Value } } return "" } // Insert adds a new list-member defined by the key/value pair to the // TraceState. If a list-member already exists for the given key, that // list-member's value is updated. The new or updated list-member is always // moved to the beginning of the TraceState as specified by the W3C Trace // Context specification. // // If key or value are invalid according to the W3C Trace Context // specification an error is returned with the original TraceState. // // If adding a new list-member means the TraceState would have more members // then is allowed, the new list-member will be inserted and the right-most // list-member will be dropped in the returned TraceState. func (ts TraceState) Insert(key, value string) (TraceState, error) { m, err := newMember(key, value) if err != nil { return ts, err } cTS := ts.Delete(key) if cTS.Len()+1 <= maxListMembers { cTS.list = append(cTS.list, member{}) } // When the number of members exceeds capacity, drop the "right-most". copy(cTS.list[1:], cTS.list) cTS.list[0] = m return cTS, nil } // Delete returns a copy of the TraceState with the list-member identified by // key removed. func (ts TraceState) Delete(key string) TraceState { members := make([]member, ts.Len()) copy(members, ts.list) for i, member := range ts.list { if member.Key == key { members = append(members[:i], members[i+1:]...) // TraceState should contain no duplicate members. break } } return TraceState{list: members} } // Len returns the number of list-members in the TraceState. func (ts TraceState) Len() int { return len(ts.list) } ================================================ FILE: vendor/go.opentelemetry.io/otel/trace.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package otel // import "go.opentelemetry.io/otel" import ( "go.opentelemetry.io/otel/internal/global" "go.opentelemetry.io/otel/trace" ) // Tracer creates a named tracer that implements Tracer interface. // If the name is an empty string then provider uses default name. // // This is short for GetTracerProvider().Tracer(name, opts...) func Tracer(name string, opts ...trace.TracerOption) trace.Tracer { return GetTracerProvider().Tracer(name, opts...) } // GetTracerProvider returns the registered global trace provider. // If none is registered then an instance of NoopTracerProvider is returned. // // Use the trace provider to create a named tracer. E.g. // // tracer := otel.GetTracerProvider().Tracer("example.com/foo") // // or // // tracer := otel.Tracer("example.com/foo") func GetTracerProvider() trace.TracerProvider { return global.TracerProvider() } // SetTracerProvider registers `tp` as the global trace provider. func SetTracerProvider(tp trace.TracerProvider) { global.SetTracerProvider(tp) } ================================================ FILE: vendor/go.opentelemetry.io/otel/verify_examples.sh ================================================ #!/bin/bash # Copyright The OpenTelemetry Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -euo pipefail cd $(dirname $0) TOOLS_DIR=$(pwd)/.tools if [ -z "${GOPATH}" ] ; then printf "GOPATH is not defined.\n" exit -1 fi if [ ! -d "${GOPATH}" ] ; then printf "GOPATH ${GOPATH} is invalid \n" exit -1 fi # Pre-requisites if ! git diff --quiet; then \ git status printf "\n\nError: working tree is not clean\n" exit -1 fi if [ "$(git tag --contains $(git log -1 --pretty=format:"%H"))" = "" ] ; then printf "$(git log -1)" printf "\n\nError: HEAD is not pointing to a tagged version" fi make ${TOOLS_DIR}/gojq DIR_TMP="${GOPATH}/src/oteltmp/" rm -rf $DIR_TMP mkdir -p $DIR_TMP printf "Copy examples to ${DIR_TMP}\n" cp -a ./example ${DIR_TMP} # Update go.mod files printf "Update go.mod: rename module and remove replace\n" PACKAGE_DIRS=$(find . -mindepth 2 -type f -name 'go.mod' -exec dirname {} \; | egrep 'example' | sed 's/^\.\///' | sort) for dir in $PACKAGE_DIRS; do printf " Update go.mod for $dir\n" (cd "${DIR_TMP}/${dir}" && \ # replaces is ("mod1" "mod2" …) replaces=($(go mod edit -json | ${TOOLS_DIR}/gojq '.Replace[].Old.Path')) && \ # strip double quotes replaces=("${replaces[@]%\"}") && \ replaces=("${replaces[@]#\"}") && \ # make an array (-dropreplace=mod1 -dropreplace=mod2 …) dropreplaces=("${replaces[@]/#/-dropreplace=}") && \ go mod edit -module "oteltmp/${dir}" "${dropreplaces[@]}" && \ go mod tidy) done printf "Update done:\n\n" # Build directories that contain main package. These directories are different than # directories that contain go.mod files. printf "Build examples:\n" EXAMPLES=$(./get_main_pkgs.sh ./example) for ex in $EXAMPLES; do printf " Build $ex in ${DIR_TMP}/${ex}\n" (cd "${DIR_TMP}/${ex}" && \ go build .) done # Cleanup printf "Remove copied files.\n" rm -rf $DIR_TMP ================================================ FILE: vendor/go.opentelemetry.io/otel/version.go ================================================ // Copyright The OpenTelemetry Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package otel // import "go.opentelemetry.io/otel" // Version is the current release version of OpenTelemetry in use. func Version() string { return "1.19.0" } ================================================ FILE: vendor/go.opentelemetry.io/otel/versions.yaml ================================================ # Copyright The OpenTelemetry Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. module-sets: stable-v1: version: v1.19.0 modules: - go.opentelemetry.io/otel - go.opentelemetry.io/otel/bridge/opentracing - go.opentelemetry.io/otel/bridge/opentracing/test - go.opentelemetry.io/otel/example/dice - go.opentelemetry.io/otel/example/fib - go.opentelemetry.io/otel/example/namedtracer - go.opentelemetry.io/otel/example/otel-collector - go.opentelemetry.io/otel/example/passthrough - go.opentelemetry.io/otel/example/zipkin - go.opentelemetry.io/otel/exporters/otlp/otlptrace - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp - go.opentelemetry.io/otel/exporters/stdout/stdouttrace - go.opentelemetry.io/otel/exporters/zipkin - go.opentelemetry.io/otel/metric - go.opentelemetry.io/otel/sdk - go.opentelemetry.io/otel/sdk/metric - go.opentelemetry.io/otel/trace experimental-metrics: version: v0.42.0 modules: - go.opentelemetry.io/otel/bridge/opencensus - go.opentelemetry.io/otel/bridge/opencensus/test - go.opentelemetry.io/otel/example/opencensus - go.opentelemetry.io/otel/example/prometheus - go.opentelemetry.io/otel/example/view - go.opentelemetry.io/otel/exporters/otlp/otlpmetric - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp - go.opentelemetry.io/otel/exporters/prometheus - go.opentelemetry.io/otel/exporters/stdout/stdoutmetric experimental-schema: version: v0.0.7 modules: - go.opentelemetry.io/otel/schema excluded-modules: - go.opentelemetry.io/otel/internal/tools ================================================ FILE: vendor/go.starlark.net/LICENSE ================================================ Copyright (c) 2017 The Bazel Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/go.starlark.net/internal/compile/compile.go ================================================ // Package compile defines the Starlark bytecode compiler. // It is an internal package of the Starlark interpreter and is not directly accessible to clients. // // The compiler generates byte code with optional uint32 operands for a // virtual machine with the following components: // - a program counter, which is an index into the byte code array. // - an operand stack, whose maximum size is computed for each function by the compiler. // - an stack of active iterators. // - an array of local variables. // The number of local variables and their indices are computed by the resolver. // Locals (possibly including parameters) that are shared with nested functions // are 'cells': their locals array slot will contain a value of type 'cell', // an indirect value in a box that is explicitly read/updated by instructions. // - an array of free variables, for nested functions. // Free variables are a subset of the ancestors' cell variables. // As with locals and cells, these are computed by the resolver. // - an array of global variables, shared among all functions in the same module. // All elements are initially nil. // - two maps of predeclared and universal identifiers. // // Each function has a line number table that maps each program counter // offset to a source position, including the column number. // // Operands, logically uint32s, are encoded using little-endian 7-bit // varints, the top bit indicating that more bytes follow. // package compile // import "go.starlark.net/internal/compile" import ( "bytes" "fmt" "log" "os" "path/filepath" "strconv" "sync" "go.starlark.net/resolve" "go.starlark.net/syntax" ) // Disassemble causes the assembly code for each function // to be printed to stderr as it is generated. var Disassemble = false const debug = false // make code generation verbose, for debugging the compiler // Increment this to force recompilation of saved bytecode files. const Version = 10 type Opcode uint8 // "x DUP x x" is a "stack picture" that describes the state of the // stack before and after execution of the instruction. // // OP indicates an immediate operand that is an index into the // specified table: locals, names, freevars, constants. const ( NOP Opcode = iota // - NOP - // stack operations DUP // x DUP x x DUP2 // x y DUP2 x y x y POP // x POP - EXCH // x y EXCH y x // binary comparisons // (order must match Token) LT GT GE LE EQL NEQ // binary arithmetic // (order must match Token) PLUS MINUS STAR SLASH SLASHSLASH PERCENT AMP PIPE CIRCUMFLEX LTLT GTGT IN // unary operators UPLUS // x UPLUS x UMINUS // x UMINUS -x TILDE // x TILDE ~x NONE // - NONE None TRUE // - TRUE True FALSE // - FALSE False MANDATORY // - MANDATORY Mandatory [sentinel value for required kwonly args] ITERPUSH // iterable ITERPUSH - [pushes the iterator stack] ITERPOP // - ITERPOP - [pops the iterator stack] NOT // value NOT bool RETURN // value RETURN - SETINDEX // a i new SETINDEX - INDEX // a i INDEX elem SETDICT // dict key value SETDICT - SETDICTUNIQ // dict key value SETDICTUNIQ - APPEND // list elem APPEND - SLICE // x lo hi step SLICE slice INPLACE_ADD // x y INPLACE_ADD z where z is x+y or x.extend(y) MAKEDICT // - MAKEDICT dict SETCELL // value cell SETCELL - CELL // cell CELL value // --- opcodes with an argument must go below this line --- // control flow JMP // - JMP - CJMP // cond CJMP - ITERJMP // - ITERJMP elem (and fall through) [acts on topmost iterator] // or: - ITERJMP - (and jump) CONSTANT // - CONSTANT value MAKETUPLE // x1 ... xn MAKETUPLE tuple MAKELIST // x1 ... xn MAKELIST list MAKEFUNC // defaults+freevars MAKEFUNC fn LOAD // from1 ... fromN module LOAD v1 ... vN SETLOCAL // value SETLOCAL - SETGLOBAL // value SETGLOBAL - LOCAL // - LOCAL value FREE // - FREE cell GLOBAL // - GLOBAL value PREDECLARED // - PREDECLARED value UNIVERSAL // - UNIVERSAL value ATTR // x ATTR y y = x.name SETFIELD // x y SETFIELD - x.name = y UNPACK // iterable UNPACK vn ... v1 // n>>8 is #positional args and n&0xff is #named args (pairs). CALL // fn positional named CALL result CALL_VAR // fn positional named *args CALL_VAR result CALL_KW // fn positional named **kwargs CALL_KW result CALL_VAR_KW // fn positional named *args **kwargs CALL_VAR_KW result OpcodeArgMin = JMP OpcodeMax = CALL_VAR_KW ) // TODO(adonovan): add dynamic checks for missing opcodes in the tables below. var opcodeNames = [...]string{ AMP: "amp", APPEND: "append", ATTR: "attr", CALL: "call", CALL_KW: "call_kw ", CALL_VAR: "call_var", CALL_VAR_KW: "call_var_kw", CELL: "cell", CIRCUMFLEX: "circumflex", CJMP: "cjmp", CONSTANT: "constant", DUP2: "dup2", DUP: "dup", EQL: "eql", EXCH: "exch", FALSE: "false", FREE: "free", GE: "ge", GLOBAL: "global", GT: "gt", GTGT: "gtgt", IN: "in", INDEX: "index", INPLACE_ADD: "inplace_add", ITERJMP: "iterjmp", ITERPOP: "iterpop", ITERPUSH: "iterpush", JMP: "jmp", LE: "le", LOAD: "load", LOCAL: "local", LT: "lt", LTLT: "ltlt", MAKEDICT: "makedict", MAKEFUNC: "makefunc", MAKELIST: "makelist", MAKETUPLE: "maketuple", MANDATORY: "mandatory", MINUS: "minus", NEQ: "neq", NONE: "none", NOP: "nop", NOT: "not", PERCENT: "percent", PIPE: "pipe", PLUS: "plus", POP: "pop", PREDECLARED: "predeclared", RETURN: "return", SETCELL: "setcell", SETDICT: "setdict", SETDICTUNIQ: "setdictuniq", SETFIELD: "setfield", SETGLOBAL: "setglobal", SETINDEX: "setindex", SETLOCAL: "setlocal", SLASH: "slash", SLASHSLASH: "slashslash", SLICE: "slice", STAR: "star", TILDE: "tilde", TRUE: "true", UMINUS: "uminus", UNIVERSAL: "universal", UNPACK: "unpack", UPLUS: "uplus", } const variableStackEffect = 0x7f // stackEffect records the effect on the size of the operand stack of // each kind of instruction. For some instructions this requires computation. var stackEffect = [...]int8{ AMP: -1, APPEND: -2, ATTR: 0, CALL: variableStackEffect, CALL_KW: variableStackEffect, CALL_VAR: variableStackEffect, CALL_VAR_KW: variableStackEffect, CELL: 0, CIRCUMFLEX: -1, CJMP: -1, CONSTANT: +1, DUP2: +2, DUP: +1, EQL: -1, FALSE: +1, FREE: +1, GE: -1, GLOBAL: +1, GT: -1, GTGT: -1, IN: -1, INDEX: -1, INPLACE_ADD: -1, ITERJMP: variableStackEffect, ITERPOP: 0, ITERPUSH: -1, JMP: 0, LE: -1, LOAD: -1, LOCAL: +1, LT: -1, LTLT: -1, MAKEDICT: +1, MAKEFUNC: 0, MAKELIST: variableStackEffect, MAKETUPLE: variableStackEffect, MANDATORY: +1, MINUS: -1, NEQ: -1, NONE: +1, NOP: 0, NOT: 0, PERCENT: -1, PIPE: -1, PLUS: -1, POP: -1, PREDECLARED: +1, RETURN: -1, SETCELL: -2, SETDICT: -3, SETDICTUNIQ: -3, SETFIELD: -2, SETGLOBAL: -1, SETINDEX: -3, SETLOCAL: -1, SLASH: -1, SLASHSLASH: -1, SLICE: -3, STAR: -1, TRUE: +1, UMINUS: 0, UNIVERSAL: +1, UNPACK: variableStackEffect, UPLUS: 0, } func (op Opcode) String() string { if op < OpcodeMax { if name := opcodeNames[op]; name != "" { return name } } return fmt.Sprintf("illegal op (%d)", op) } // A Program is a Starlark file in executable form. // // Programs are serialized by the Program.Encode method, // which must be updated whenever this declaration is changed. type Program struct { Loads []Binding // name (really, string) and position of each load stmt Names []string // names of attributes and predeclared variables Constants []interface{} // = string | int64 | float64 | *big.Int Functions []*Funcode Globals []Binding // for error messages and tracing Toplevel *Funcode // module initialization function } // A Funcode is the code of a compiled Starlark function. // // Funcodes are serialized by the encoder.function method, // which must be updated whenever this declaration is changed. type Funcode struct { Prog *Program Pos syntax.Position // position of def or lambda token Name string // name of this function Doc string // docstring of this function Code []byte // the byte code pclinetab []uint16 // mapping from pc to linenum Locals []Binding // locals, parameters first Cells []int // indices of Locals that require cells Freevars []Binding // for tracing MaxStack int NumParams int NumKwonlyParams int HasVarargs, HasKwargs bool // -- transient state -- lntOnce sync.Once lnt []pclinecol // decoded line number table } type pclinecol struct { pc uint32 line, col int32 } // A Binding is the name and position of a binding identifier. type Binding struct { Name string Pos syntax.Position } // A pcomp holds the compiler state for a Program. type pcomp struct { prog *Program // what we're building names map[string]uint32 constants map[interface{}]uint32 functions map[*Funcode]uint32 } // An fcomp holds the compiler state for a Funcode. type fcomp struct { fn *Funcode // what we're building pcomp *pcomp pos syntax.Position // current position of generated code loops []loop block *block } type loop struct { break_, continue_ *block } type block struct { insns []insn // If the last insn is a RETURN, jmp and cjmp are nil. // If the last insn is a CJMP or ITERJMP, // cjmp and jmp are the "true" and "false" successors. // Otherwise, jmp is the sole successor. jmp, cjmp *block initialstack int // for stack depth computation // Used during encoding index int // -1 => not encoded yet addr uint32 } type insn struct { op Opcode arg uint32 line, col int32 } // Position returns the source position for program counter pc. func (fn *Funcode) Position(pc uint32) syntax.Position { fn.lntOnce.Do(fn.decodeLNT) // Binary search to find last LNT entry not greater than pc. // To avoid dynamic dispatch, this is a specialization of // sort.Search using this predicate: // !(i < len(fn.lnt)-1 && fn.lnt[i+1].pc <= pc) n := len(fn.lnt) i, j := 0, n for i < j { h := int(uint(i+j) >> 1) if !(h >= n-1 || fn.lnt[h+1].pc > pc) { i = h + 1 } else { j = h } } var line, col int32 if i < n { line = fn.lnt[i].line col = fn.lnt[i].col } pos := fn.Pos // copy the (annoyingly inaccessible) filename pos.Col = col pos.Line = line return pos } // decodeLNT decodes the line number table and populates fn.lnt. // It is called at most once. func (fn *Funcode) decodeLNT() { // Conceptually the table contains rows of the form // (pc uint32, line int32, col int32), sorted by pc. // We use a delta encoding, since the differences // between successive pc, line, and column values // are typically small and positive (though line and // especially column differences may be negative). // The delta encoding starts from // {pc: 0, line: fn.Pos.Line, col: fn.Pos.Col}. // // Each entry is packed into one or more 16-bit values: // Δpc uint4 // Δline int5 // Δcol int6 // incomplete uint1 // The top 4 bits are the unsigned delta pc. // The next 5 bits are the signed line number delta. // The next 6 bits are the signed column number delta. // The bottom bit indicates that more rows follow because // one of the deltas was maxed out. // These field widths were chosen from a sample of real programs, // and allow >97% of rows to be encoded in a single uint16. fn.lnt = make([]pclinecol, 0, len(fn.pclinetab)) // a minor overapproximation entry := pclinecol{ pc: 0, line: fn.Pos.Line, col: fn.Pos.Col, } for _, x := range fn.pclinetab { entry.pc += uint32(x) >> 12 entry.line += int32((int16(x) << 4) >> (16 - 5)) // sign extend Δline entry.col += int32((int16(x) << 9) >> (16 - 6)) // sign extend Δcol if (x & 1) == 0 { fn.lnt = append(fn.lnt, entry) } } } // bindings converts resolve.Bindings to compiled form. func bindings(bindings []*resolve.Binding) []Binding { res := make([]Binding, len(bindings)) for i, bind := range bindings { res[i].Name = bind.First.Name res[i].Pos = bind.First.NamePos } return res } // Expr compiles an expression to a program whose toplevel function evaluates it. func Expr(expr syntax.Expr, name string, locals []*resolve.Binding) *Program { pos := syntax.Start(expr) stmts := []syntax.Stmt{&syntax.ReturnStmt{Result: expr}} return File(stmts, pos, name, locals, nil) } // File compiles the statements of a file into a program. func File(stmts []syntax.Stmt, pos syntax.Position, name string, locals, globals []*resolve.Binding) *Program { pcomp := &pcomp{ prog: &Program{ Globals: bindings(globals), }, names: make(map[string]uint32), constants: make(map[interface{}]uint32), functions: make(map[*Funcode]uint32), } pcomp.prog.Toplevel = pcomp.function(name, pos, stmts, locals, nil) return pcomp.prog } func (pcomp *pcomp) function(name string, pos syntax.Position, stmts []syntax.Stmt, locals, freevars []*resolve.Binding) *Funcode { fcomp := &fcomp{ pcomp: pcomp, pos: pos, fn: &Funcode{ Prog: pcomp.prog, Pos: pos, Name: name, Doc: docStringFromBody(stmts), Locals: bindings(locals), Freevars: bindings(freevars), }, } // Record indices of locals that require cells. for i, local := range locals { if local.Scope == resolve.Cell { fcomp.fn.Cells = append(fcomp.fn.Cells, i) } } if debug { fmt.Fprintf(os.Stderr, "start function(%s @ %s)\n", name, pos) } // Convert AST to a CFG of instructions. entry := fcomp.newBlock() fcomp.block = entry fcomp.stmts(stmts) if fcomp.block != nil { fcomp.emit(NONE) fcomp.emit(RETURN) } var oops bool // something bad happened setinitialstack := func(b *block, depth int) { if b.initialstack == -1 { b.initialstack = depth } else if b.initialstack != depth { fmt.Fprintf(os.Stderr, "%d: setinitialstack: depth mismatch: %d vs %d\n", b.index, b.initialstack, depth) oops = true } } // Linearize the CFG: // compute order, address, and initial // stack depth of each reachable block. var pc uint32 var blocks []*block var maxstack int var visit func(b *block) visit = func(b *block) { if b.index >= 0 { return // already visited } b.index = len(blocks) b.addr = pc blocks = append(blocks, b) stack := b.initialstack if debug { fmt.Fprintf(os.Stderr, "%s block %d: (stack = %d)\n", name, b.index, stack) } var cjmpAddr *uint32 var isiterjmp int for i, insn := range b.insns { pc++ // Compute size of argument. if insn.op >= OpcodeArgMin { switch insn.op { case ITERJMP: isiterjmp = 1 fallthrough case CJMP: cjmpAddr = &b.insns[i].arg pc += 4 default: pc += uint32(argLen(insn.arg)) } } // Compute effect on stack. se := insn.stackeffect() if debug { fmt.Fprintln(os.Stderr, "\t", insn.op, stack, stack+se) } stack += se if stack < 0 { fmt.Fprintf(os.Stderr, "After pc=%d: stack underflow\n", pc) oops = true } if stack+isiterjmp > maxstack { maxstack = stack + isiterjmp } } if debug { fmt.Fprintf(os.Stderr, "successors of block %d (start=%d):\n", b.addr, b.index) if b.jmp != nil { fmt.Fprintf(os.Stderr, "jmp to %d\n", b.jmp.index) } if b.cjmp != nil { fmt.Fprintf(os.Stderr, "cjmp to %d\n", b.cjmp.index) } } // Place the jmp block next. if b.jmp != nil { // jump threading (empty cycles are impossible) for b.jmp.insns == nil { b.jmp = b.jmp.jmp } setinitialstack(b.jmp, stack+isiterjmp) if b.jmp.index < 0 { // Successor is not yet visited: // place it next and fall through. visit(b.jmp) } else { // Successor already visited; // explicit backward jump required. pc += 5 } } // Then the cjmp block. if b.cjmp != nil { // jump threading (empty cycles are impossible) for b.cjmp.insns == nil { b.cjmp = b.cjmp.jmp } setinitialstack(b.cjmp, stack) visit(b.cjmp) // Patch the CJMP/ITERJMP, if present. if cjmpAddr != nil { *cjmpAddr = b.cjmp.addr } } } setinitialstack(entry, 0) visit(entry) fn := fcomp.fn fn.MaxStack = maxstack // Emit bytecode (and position table). if Disassemble { fmt.Fprintf(os.Stderr, "Function %s: (%d blocks, %d bytes)\n", name, len(blocks), pc) } fcomp.generate(blocks, pc) if debug { fmt.Fprintf(os.Stderr, "code=%d maxstack=%d\n", fn.Code, fn.MaxStack) } // Don't panic until we've completed printing of the function. if oops { panic("internal error") } if debug { fmt.Fprintf(os.Stderr, "end function(%s @ %s)\n", name, pos) } return fn } func docStringFromBody(body []syntax.Stmt) string { if len(body) == 0 { return "" } expr, ok := body[0].(*syntax.ExprStmt) if !ok { return "" } lit, ok := expr.X.(*syntax.Literal) if !ok { return "" } if lit.Token != syntax.STRING { return "" } return lit.Value.(string) } func (insn *insn) stackeffect() int { se := int(stackEffect[insn.op]) if se == variableStackEffect { arg := int(insn.arg) switch insn.op { case CALL, CALL_KW, CALL_VAR, CALL_VAR_KW: se = -int(2*(insn.arg&0xff) + insn.arg>>8) if insn.op != CALL { se-- } if insn.op == CALL_VAR_KW { se-- } case ITERJMP: // Stack effect differs by successor: // +1 for jmp/false/ok // 0 for cjmp/true/exhausted // Handled specially in caller. se = 0 case MAKELIST, MAKETUPLE: se = 1 - arg case UNPACK: se = arg - 1 default: panic(insn.op) } } return se } // generate emits the linear instruction stream from the CFG, // and builds the PC-to-line number table. func (fcomp *fcomp) generate(blocks []*block, codelen uint32) { code := make([]byte, 0, codelen) var pclinetab []uint16 prev := pclinecol{ pc: 0, line: fcomp.fn.Pos.Line, col: fcomp.fn.Pos.Col, } for _, b := range blocks { if Disassemble { fmt.Fprintf(os.Stderr, "%d:\n", b.index) } pc := b.addr for _, insn := range b.insns { if insn.line != 0 { // Instruction has a source position. Delta-encode it. // See Funcode.Position for the encoding. for { var incomplete uint16 // Δpc, uint4 deltapc := pc - prev.pc if deltapc > 0x0f { deltapc = 0x0f incomplete = 1 } prev.pc += deltapc // Δline, int5 deltaline, ok := clip(insn.line-prev.line, -0x10, 0x0f) if !ok { incomplete = 1 } prev.line += deltaline // Δcol, int6 deltacol, ok := clip(insn.col-prev.col, -0x20, 0x1f) if !ok { incomplete = 1 } prev.col += deltacol entry := uint16(deltapc<<12) | uint16(deltaline&0x1f)<<7 | uint16(deltacol&0x3f)<<1 | incomplete pclinetab = append(pclinetab, entry) if incomplete == 0 { break } } if Disassemble { fmt.Fprintf(os.Stderr, "\t\t\t\t\t; %s:%d:%d\n", filepath.Base(fcomp.fn.Pos.Filename()), insn.line, insn.col) } } if Disassemble { PrintOp(fcomp.fn, pc, insn.op, insn.arg) } code = append(code, byte(insn.op)) pc++ if insn.op >= OpcodeArgMin { if insn.op == CJMP || insn.op == ITERJMP { code = addUint32(code, insn.arg, 4) // pad arg to 4 bytes } else { code = addUint32(code, insn.arg, 0) } pc = uint32(len(code)) } } if b.jmp != nil && b.jmp.index != b.index+1 { addr := b.jmp.addr if Disassemble { fmt.Fprintf(os.Stderr, "\t%d\tjmp\t\t%d\t; block %d\n", pc, addr, b.jmp.index) } code = append(code, byte(JMP)) code = addUint32(code, addr, 4) } } if len(code) != int(codelen) { panic("internal error: wrong code length") } fcomp.fn.pclinetab = pclinetab fcomp.fn.Code = code } // clip returns the value nearest x in the range [min...max], // and whether it equals x. func clip(x, min, max int32) (int32, bool) { if x > max { return max, false } else if x < min { return min, false } else { return x, true } } // addUint32 encodes x as 7-bit little-endian varint. // TODO(adonovan): opt: steal top two bits of opcode // to encode the number of complete bytes that follow. func addUint32(code []byte, x uint32, min int) []byte { end := len(code) + min for x >= 0x80 { code = append(code, byte(x)|0x80) x >>= 7 } code = append(code, byte(x)) // Pad the operand with NOPs to exactly min bytes. for len(code) < end { code = append(code, byte(NOP)) } return code } func argLen(x uint32) int { n := 0 for x >= 0x80 { n++ x >>= 7 } return n + 1 } // PrintOp prints an instruction. // It is provided for debugging. func PrintOp(fn *Funcode, pc uint32, op Opcode, arg uint32) { if op < OpcodeArgMin { fmt.Fprintf(os.Stderr, "\t%d\t%s\n", pc, op) return } var comment string switch op { case CONSTANT: switch x := fn.Prog.Constants[arg].(type) { case string: comment = strconv.Quote(x) default: comment = fmt.Sprint(x) } case MAKEFUNC: comment = fn.Prog.Functions[arg].Name case SETLOCAL, LOCAL: comment = fn.Locals[arg].Name case SETGLOBAL, GLOBAL: comment = fn.Prog.Globals[arg].Name case ATTR, SETFIELD, PREDECLARED, UNIVERSAL: comment = fn.Prog.Names[arg] case FREE: comment = fn.Freevars[arg].Name case CALL, CALL_VAR, CALL_KW, CALL_VAR_KW: comment = fmt.Sprintf("%d pos, %d named", arg>>8, arg&0xff) default: // JMP, CJMP, ITERJMP, MAKETUPLE, MAKELIST, LOAD, UNPACK: // arg is just a number } var buf bytes.Buffer fmt.Fprintf(&buf, "\t%d\t%-10s\t%d", pc, op, arg) if comment != "" { fmt.Fprint(&buf, "\t; ", comment) } fmt.Fprintln(&buf) os.Stderr.Write(buf.Bytes()) } // newBlock returns a new block. func (fcomp) newBlock() *block { return &block{index: -1, initialstack: -1} } // emit emits an instruction to the current block. func (fcomp *fcomp) emit(op Opcode) { if op >= OpcodeArgMin { panic("missing arg: " + op.String()) } insn := insn{op: op, line: fcomp.pos.Line, col: fcomp.pos.Col} fcomp.block.insns = append(fcomp.block.insns, insn) fcomp.pos.Line = 0 fcomp.pos.Col = 0 } // emit1 emits an instruction with an immediate operand. func (fcomp *fcomp) emit1(op Opcode, arg uint32) { if op < OpcodeArgMin { panic("unwanted arg: " + op.String()) } insn := insn{op: op, arg: arg, line: fcomp.pos.Line, col: fcomp.pos.Col} fcomp.block.insns = append(fcomp.block.insns, insn) fcomp.pos.Line = 0 fcomp.pos.Col = 0 } // jump emits a jump to the specified block. // On return, the current block is unset. func (fcomp *fcomp) jump(b *block) { if b == fcomp.block { panic("self-jump") // unreachable: Starlark has no arbitrary looping constructs } fcomp.block.jmp = b fcomp.block = nil } // condjump emits a conditional jump (CJMP or ITERJMP) // to the specified true/false blocks. // (For ITERJMP, the cases are jmp/f/ok and cjmp/t/exhausted.) // On return, the current block is unset. func (fcomp *fcomp) condjump(op Opcode, t, f *block) { if !(op == CJMP || op == ITERJMP) { panic("not a conditional jump: " + op.String()) } fcomp.emit1(op, 0) // fill in address later fcomp.block.cjmp = t fcomp.jump(f) } // nameIndex returns the index of the specified name // within the name pool, adding it if necessary. func (pcomp *pcomp) nameIndex(name string) uint32 { index, ok := pcomp.names[name] if !ok { index = uint32(len(pcomp.prog.Names)) pcomp.names[name] = index pcomp.prog.Names = append(pcomp.prog.Names, name) } return index } // constantIndex returns the index of the specified constant // within the constant pool, adding it if necessary. func (pcomp *pcomp) constantIndex(v interface{}) uint32 { index, ok := pcomp.constants[v] if !ok { index = uint32(len(pcomp.prog.Constants)) pcomp.constants[v] = index pcomp.prog.Constants = append(pcomp.prog.Constants, v) } return index } // functionIndex returns the index of the specified function // AST the nestedfun pool, adding it if necessary. func (pcomp *pcomp) functionIndex(fn *Funcode) uint32 { index, ok := pcomp.functions[fn] if !ok { index = uint32(len(pcomp.prog.Functions)) pcomp.functions[fn] = index pcomp.prog.Functions = append(pcomp.prog.Functions, fn) } return index } // string emits code to push the specified string. func (fcomp *fcomp) string(s string) { fcomp.emit1(CONSTANT, fcomp.pcomp.constantIndex(s)) } // setPos sets the current source position. // It should be called prior to any operation that can fail dynamically. // All positions are assumed to belong to the same file. func (fcomp *fcomp) setPos(pos syntax.Position) { fcomp.pos = pos } // set emits code to store the top-of-stack value // to the specified local, cell, or global variable. func (fcomp *fcomp) set(id *syntax.Ident) { bind := id.Binding.(*resolve.Binding) switch bind.Scope { case resolve.Local: fcomp.emit1(SETLOCAL, uint32(bind.Index)) case resolve.Cell: // TODO(adonovan): opt: make a single op for LOCAL, SETCELL. fcomp.emit1(LOCAL, uint32(bind.Index)) fcomp.emit(SETCELL) case resolve.Global: fcomp.emit1(SETGLOBAL, uint32(bind.Index)) default: log.Panicf("%s: set(%s): not global/local/cell (%d)", id.NamePos, id.Name, bind.Scope) } } // lookup emits code to push the value of the specified variable. func (fcomp *fcomp) lookup(id *syntax.Ident) { bind := id.Binding.(*resolve.Binding) if bind.Scope != resolve.Universal { // (universal lookup can't fail) fcomp.setPos(id.NamePos) } switch bind.Scope { case resolve.Local: fcomp.emit1(LOCAL, uint32(bind.Index)) case resolve.Free: // TODO(adonovan): opt: make a single op for FREE, CELL. fcomp.emit1(FREE, uint32(bind.Index)) fcomp.emit(CELL) case resolve.Cell: // TODO(adonovan): opt: make a single op for LOCAL, CELL. fcomp.emit1(LOCAL, uint32(bind.Index)) fcomp.emit(CELL) case resolve.Global: fcomp.emit1(GLOBAL, uint32(bind.Index)) case resolve.Predeclared: fcomp.emit1(PREDECLARED, fcomp.pcomp.nameIndex(id.Name)) case resolve.Universal: fcomp.emit1(UNIVERSAL, fcomp.pcomp.nameIndex(id.Name)) default: log.Panicf("%s: compiler.lookup(%s): scope = %d", id.NamePos, id.Name, bind.Scope) } } func (fcomp *fcomp) stmts(stmts []syntax.Stmt) { for _, stmt := range stmts { fcomp.stmt(stmt) } } func (fcomp *fcomp) stmt(stmt syntax.Stmt) { switch stmt := stmt.(type) { case *syntax.ExprStmt: if _, ok := stmt.X.(*syntax.Literal); ok { // Opt: don't compile doc comments only to pop them. return } fcomp.expr(stmt.X) fcomp.emit(POP) case *syntax.BranchStmt: // Resolver invariant: break/continue appear only within loops. switch stmt.Token { case syntax.PASS: // no-op case syntax.BREAK: b := fcomp.loops[len(fcomp.loops)-1].break_ fcomp.jump(b) fcomp.block = fcomp.newBlock() // dead code case syntax.CONTINUE: b := fcomp.loops[len(fcomp.loops)-1].continue_ fcomp.jump(b) fcomp.block = fcomp.newBlock() // dead code } case *syntax.IfStmt: // Keep consistent with CondExpr. t := fcomp.newBlock() f := fcomp.newBlock() done := fcomp.newBlock() fcomp.ifelse(stmt.Cond, t, f) fcomp.block = t fcomp.stmts(stmt.True) fcomp.jump(done) fcomp.block = f fcomp.stmts(stmt.False) fcomp.jump(done) fcomp.block = done case *syntax.AssignStmt: switch stmt.Op { case syntax.EQ: // simple assignment: x = y fcomp.expr(stmt.RHS) fcomp.assign(stmt.OpPos, stmt.LHS) case syntax.PLUS_EQ, syntax.MINUS_EQ, syntax.STAR_EQ, syntax.SLASH_EQ, syntax.SLASHSLASH_EQ, syntax.PERCENT_EQ, syntax.AMP_EQ, syntax.PIPE_EQ, syntax.CIRCUMFLEX_EQ, syntax.LTLT_EQ, syntax.GTGT_EQ: // augmented assignment: x += y var set func() // Evaluate "address" of x exactly once to avoid duplicate side-effects. switch lhs := unparen(stmt.LHS).(type) { case *syntax.Ident: // x = ... fcomp.lookup(lhs) set = func() { fcomp.set(lhs) } case *syntax.IndexExpr: // x[y] = ... fcomp.expr(lhs.X) fcomp.expr(lhs.Y) fcomp.emit(DUP2) fcomp.setPos(lhs.Lbrack) fcomp.emit(INDEX) set = func() { fcomp.setPos(lhs.Lbrack) fcomp.emit(SETINDEX) } case *syntax.DotExpr: // x.f = ... fcomp.expr(lhs.X) fcomp.emit(DUP) name := fcomp.pcomp.nameIndex(lhs.Name.Name) fcomp.setPos(lhs.Dot) fcomp.emit1(ATTR, name) set = func() { fcomp.setPos(lhs.Dot) fcomp.emit1(SETFIELD, name) } default: panic(lhs) } fcomp.expr(stmt.RHS) if stmt.Op == syntax.PLUS_EQ { // Allow the runtime to optimize list += iterable. fcomp.setPos(stmt.OpPos) fcomp.emit(INPLACE_ADD) } else { fcomp.binop(stmt.OpPos, stmt.Op-syntax.PLUS_EQ+syntax.PLUS) } set() } case *syntax.DefStmt: fcomp.function(stmt.Function.(*resolve.Function)) fcomp.set(stmt.Name) case *syntax.ForStmt: // Keep consistent with ForClause. head := fcomp.newBlock() body := fcomp.newBlock() tail := fcomp.newBlock() fcomp.expr(stmt.X) fcomp.setPos(stmt.For) fcomp.emit(ITERPUSH) fcomp.jump(head) fcomp.block = head fcomp.condjump(ITERJMP, tail, body) fcomp.block = body fcomp.assign(stmt.For, stmt.Vars) fcomp.loops = append(fcomp.loops, loop{break_: tail, continue_: head}) fcomp.stmts(stmt.Body) fcomp.loops = fcomp.loops[:len(fcomp.loops)-1] fcomp.jump(head) fcomp.block = tail fcomp.emit(ITERPOP) case *syntax.WhileStmt: head := fcomp.newBlock() body := fcomp.newBlock() done := fcomp.newBlock() fcomp.jump(head) fcomp.block = head fcomp.ifelse(stmt.Cond, body, done) fcomp.block = body fcomp.loops = append(fcomp.loops, loop{break_: done, continue_: head}) fcomp.stmts(stmt.Body) fcomp.loops = fcomp.loops[:len(fcomp.loops)-1] fcomp.jump(head) fcomp.block = done case *syntax.ReturnStmt: if stmt.Result != nil { fcomp.expr(stmt.Result) } else { fcomp.emit(NONE) } fcomp.emit(RETURN) fcomp.block = fcomp.newBlock() // dead code case *syntax.LoadStmt: for i := range stmt.From { fcomp.string(stmt.From[i].Name) } module := stmt.Module.Value.(string) fcomp.pcomp.prog.Loads = append(fcomp.pcomp.prog.Loads, Binding{ Name: module, Pos: stmt.Module.TokenPos, }) fcomp.string(module) fcomp.setPos(stmt.Load) fcomp.emit1(LOAD, uint32(len(stmt.From))) for i := range stmt.To { fcomp.set(stmt.To[len(stmt.To)-1-i]) } default: start, _ := stmt.Span() log.Panicf("%s: exec: unexpected statement %T", start, stmt) } } // assign implements lhs = rhs for arbitrary expressions lhs. // RHS is on top of stack, consumed. func (fcomp *fcomp) assign(pos syntax.Position, lhs syntax.Expr) { switch lhs := lhs.(type) { case *syntax.ParenExpr: // (lhs) = rhs fcomp.assign(pos, lhs.X) case *syntax.Ident: // x = rhs fcomp.set(lhs) case *syntax.TupleExpr: // x, y = rhs fcomp.assignSequence(pos, lhs.List) case *syntax.ListExpr: // [x, y] = rhs fcomp.assignSequence(pos, lhs.List) case *syntax.IndexExpr: // x[y] = rhs fcomp.expr(lhs.X) fcomp.emit(EXCH) fcomp.expr(lhs.Y) fcomp.emit(EXCH) fcomp.setPos(lhs.Lbrack) fcomp.emit(SETINDEX) case *syntax.DotExpr: // x.f = rhs fcomp.expr(lhs.X) fcomp.emit(EXCH) fcomp.setPos(lhs.Dot) fcomp.emit1(SETFIELD, fcomp.pcomp.nameIndex(lhs.Name.Name)) default: panic(lhs) } } func (fcomp *fcomp) assignSequence(pos syntax.Position, lhs []syntax.Expr) { fcomp.setPos(pos) fcomp.emit1(UNPACK, uint32(len(lhs))) for i := range lhs { fcomp.assign(pos, lhs[i]) } } func (fcomp *fcomp) expr(e syntax.Expr) { switch e := e.(type) { case *syntax.ParenExpr: fcomp.expr(e.X) case *syntax.Ident: fcomp.lookup(e) case *syntax.Literal: // e.Value is int64, float64, *bigInt, or string. fcomp.emit1(CONSTANT, fcomp.pcomp.constantIndex(e.Value)) case *syntax.ListExpr: for _, x := range e.List { fcomp.expr(x) } fcomp.emit1(MAKELIST, uint32(len(e.List))) case *syntax.CondExpr: // Keep consistent with IfStmt. t := fcomp.newBlock() f := fcomp.newBlock() done := fcomp.newBlock() fcomp.ifelse(e.Cond, t, f) fcomp.block = t fcomp.expr(e.True) fcomp.jump(done) fcomp.block = f fcomp.expr(e.False) fcomp.jump(done) fcomp.block = done case *syntax.IndexExpr: fcomp.expr(e.X) fcomp.expr(e.Y) fcomp.setPos(e.Lbrack) fcomp.emit(INDEX) case *syntax.SliceExpr: fcomp.setPos(e.Lbrack) fcomp.expr(e.X) if e.Lo != nil { fcomp.expr(e.Lo) } else { fcomp.emit(NONE) } if e.Hi != nil { fcomp.expr(e.Hi) } else { fcomp.emit(NONE) } if e.Step != nil { fcomp.expr(e.Step) } else { fcomp.emit(NONE) } fcomp.emit(SLICE) case *syntax.Comprehension: if e.Curly { fcomp.emit(MAKEDICT) } else { fcomp.emit1(MAKELIST, 0) } fcomp.comprehension(e, 0) case *syntax.TupleExpr: fcomp.tuple(e.List) case *syntax.DictExpr: fcomp.emit(MAKEDICT) for _, entry := range e.List { entry := entry.(*syntax.DictEntry) fcomp.emit(DUP) fcomp.expr(entry.Key) fcomp.expr(entry.Value) fcomp.setPos(entry.Colon) fcomp.emit(SETDICTUNIQ) } case *syntax.UnaryExpr: fcomp.expr(e.X) fcomp.setPos(e.OpPos) switch e.Op { case syntax.MINUS: fcomp.emit(UMINUS) case syntax.PLUS: fcomp.emit(UPLUS) case syntax.NOT: fcomp.emit(NOT) case syntax.TILDE: fcomp.emit(TILDE) default: log.Panicf("%s: unexpected unary op: %s", e.OpPos, e.Op) } case *syntax.BinaryExpr: switch e.Op { // short-circuit operators // TODO(adonovan): use ifelse to simplify conditions. case syntax.OR: // x or y => if x then x else y done := fcomp.newBlock() y := fcomp.newBlock() fcomp.expr(e.X) fcomp.emit(DUP) fcomp.condjump(CJMP, done, y) fcomp.block = y fcomp.emit(POP) // discard X fcomp.expr(e.Y) fcomp.jump(done) fcomp.block = done case syntax.AND: // x and y => if x then y else x done := fcomp.newBlock() y := fcomp.newBlock() fcomp.expr(e.X) fcomp.emit(DUP) fcomp.condjump(CJMP, y, done) fcomp.block = y fcomp.emit(POP) // discard X fcomp.expr(e.Y) fcomp.jump(done) fcomp.block = done case syntax.PLUS: fcomp.plus(e) default: // all other strict binary operator (includes comparisons) fcomp.expr(e.X) fcomp.expr(e.Y) fcomp.binop(e.OpPos, e.Op) } case *syntax.DotExpr: fcomp.expr(e.X) fcomp.setPos(e.Dot) fcomp.emit1(ATTR, fcomp.pcomp.nameIndex(e.Name.Name)) case *syntax.CallExpr: fcomp.call(e) case *syntax.LambdaExpr: fcomp.function(e.Function.(*resolve.Function)) default: start, _ := e.Span() log.Panicf("%s: unexpected expr %T", start, e) } } type summand struct { x syntax.Expr plusPos syntax.Position } // plus emits optimized code for ((a+b)+...)+z that avoids naive // quadratic behavior for strings, tuples, and lists, // and folds together adjacent literals of the same type. func (fcomp *fcomp) plus(e *syntax.BinaryExpr) { // Gather all the right operands of the left tree of plusses. // A tree (((a+b)+c)+d) becomes args=[a +b +c +d]. args := make([]summand, 0, 2) // common case: 2 operands for plus := e; ; { args = append(args, summand{unparen(plus.Y), plus.OpPos}) left := unparen(plus.X) x, ok := left.(*syntax.BinaryExpr) if !ok || x.Op != syntax.PLUS { args = append(args, summand{x: left}) break } plus = x } // Reverse args to syntactic order. for i, n := 0, len(args)/2; i < n; i++ { j := len(args) - 1 - i args[i], args[j] = args[j], args[i] } // Fold sums of adjacent literals of the same type: ""+"", []+[], ()+(). out := args[:0] // compact in situ for i := 0; i < len(args); { j := i + 1 if code := addable(args[i].x); code != 0 { for j < len(args) && addable(args[j].x) == code { j++ } if j > i+1 { args[i].x = add(code, args[i:j]) } } out = append(out, args[i]) i = j } args = out // Emit code for an n-ary sum (n > 0). fcomp.expr(args[0].x) for _, summand := range args[1:] { fcomp.expr(summand.x) fcomp.setPos(summand.plusPos) fcomp.emit(PLUS) } // If len(args) > 2, use of an accumulator instead of a chain of // PLUS operations may be more efficient. // However, no gain was measured on a workload analogous to Bazel loading; // TODO(adonovan): opt: re-evaluate on a Bazel analysis-like workload. // // We cannot use a single n-ary SUM operation // a b c SUM<3> // because we need to report a distinct error for each // individual '+' operation, so three additional operations are // needed: // // ACCSTART => create buffer and append to it // ACCUM => append to buffer // ACCEND => get contents of buffer // // For string, list, and tuple values, the interpreter can // optimize these operations by using a mutable buffer. // For all other types, ACCSTART and ACCEND would behave like // the identity function and ACCUM behaves like PLUS. // ACCUM must correctly support user-defined operations // such as list+foo. // // fcomp.emit(ACCSTART) // for _, summand := range args[1:] { // fcomp.expr(summand.x) // fcomp.setPos(summand.plusPos) // fcomp.emit(ACCUM) // } // fcomp.emit(ACCEND) } // addable reports whether e is a statically addable // expression: a [s]tring, [l]ist, or [t]uple. func addable(e syntax.Expr) rune { switch e := e.(type) { case *syntax.Literal: // TODO(adonovan): opt: support INT/FLOAT/BIGINT constant folding. switch e.Token { case syntax.STRING: return 's' } case *syntax.ListExpr: return 'l' case *syntax.TupleExpr: return 't' } return 0 } // add returns an expression denoting the sum of args, // which are all addable values of the type indicated by code. // The resulting syntax is degenerate, lacking position, etc. func add(code rune, args []summand) syntax.Expr { switch code { case 's': var buf bytes.Buffer for _, arg := range args { buf.WriteString(arg.x.(*syntax.Literal).Value.(string)) } return &syntax.Literal{Token: syntax.STRING, Value: buf.String()} case 'l': var elems []syntax.Expr for _, arg := range args { elems = append(elems, arg.x.(*syntax.ListExpr).List...) } return &syntax.ListExpr{List: elems} case 't': var elems []syntax.Expr for _, arg := range args { elems = append(elems, arg.x.(*syntax.TupleExpr).List...) } return &syntax.TupleExpr{List: elems} } panic(code) } func unparen(e syntax.Expr) syntax.Expr { if p, ok := e.(*syntax.ParenExpr); ok { return unparen(p.X) } return e } func (fcomp *fcomp) binop(pos syntax.Position, op syntax.Token) { // TODO(adonovan): simplify by assuming syntax and compiler constants align. fcomp.setPos(pos) switch op { // arithmetic case syntax.PLUS: fcomp.emit(PLUS) case syntax.MINUS: fcomp.emit(MINUS) case syntax.STAR: fcomp.emit(STAR) case syntax.SLASH: fcomp.emit(SLASH) case syntax.SLASHSLASH: fcomp.emit(SLASHSLASH) case syntax.PERCENT: fcomp.emit(PERCENT) case syntax.AMP: fcomp.emit(AMP) case syntax.PIPE: fcomp.emit(PIPE) case syntax.CIRCUMFLEX: fcomp.emit(CIRCUMFLEX) case syntax.LTLT: fcomp.emit(LTLT) case syntax.GTGT: fcomp.emit(GTGT) case syntax.IN: fcomp.emit(IN) case syntax.NOT_IN: fcomp.emit(IN) fcomp.emit(NOT) // comparisons case syntax.EQL, syntax.NEQ, syntax.GT, syntax.LT, syntax.LE, syntax.GE: fcomp.emit(Opcode(op-syntax.EQL) + EQL) default: log.Panicf("%s: unexpected binary op: %s", pos, op) } } func (fcomp *fcomp) call(call *syntax.CallExpr) { // TODO(adonovan): opt: Use optimized path for calling methods // of built-ins: x.f(...) to avoid materializing a closure. // if dot, ok := call.Fcomp.(*syntax.DotExpr); ok { // fcomp.expr(dot.X) // fcomp.args(call) // fcomp.emit1(CALL_ATTR, fcomp.name(dot.Name.Name)) // return // } // usual case fcomp.expr(call.Fn) op, arg := fcomp.args(call) fcomp.setPos(call.Lparen) fcomp.emit1(op, arg) } // args emits code to push a tuple of positional arguments // and a tuple of named arguments containing alternating keys and values. // Either or both tuples may be empty (TODO(adonovan): optimize). func (fcomp *fcomp) args(call *syntax.CallExpr) (op Opcode, arg uint32) { var callmode int // Compute the number of each kind of parameter. var p, n int // number of positional, named arguments var varargs, kwargs syntax.Expr for _, arg := range call.Args { if binary, ok := arg.(*syntax.BinaryExpr); ok && binary.Op == syntax.EQ { // named argument (name, value) fcomp.string(binary.X.(*syntax.Ident).Name) fcomp.expr(binary.Y) n++ continue } if unary, ok := arg.(*syntax.UnaryExpr); ok { if unary.Op == syntax.STAR { callmode |= 1 varargs = unary.X continue } else if unary.Op == syntax.STARSTAR { callmode |= 2 kwargs = unary.X continue } } // positional argument fcomp.expr(arg) p++ } // Python2 and Python3 both permit named arguments // to appear both before and after a *args argument: // f(1, 2, x=3, *[4], y=5, **dict(z=6)) // // They also differ in their evaluation order: // Python2: 1 2 3 5 4 6 (*args and **kwargs evaluated last) // Python3: 1 2 4 3 5 6 (positional args evaluated before named args) // Starlark-in-Java historically used a third order: // Lexical: 1 2 3 4 5 6 (all args evaluated left-to-right) // // After discussion in github.com/bazelbuild/starlark#13, the // spec now requires Starlark to statically reject named // arguments after *args (e.g. y=5), and to use Python2-style // evaluation order. This is both easy to implement and // consistent with lexical order: // // f(1, 2, x=3, *[4], **dict(z=6)) # 1 2 3 4 6 // *args if varargs != nil { fcomp.expr(varargs) } // **kwargs if kwargs != nil { fcomp.expr(kwargs) } // TODO(adonovan): avoid this with a more flexible encoding. if p >= 256 || n >= 256 { // resolve already checked this; should be unreachable panic("too many arguments in call") } return CALL + Opcode(callmode), uint32(p<<8 | n) } func (fcomp *fcomp) tuple(elems []syntax.Expr) { for _, elem := range elems { fcomp.expr(elem) } fcomp.emit1(MAKETUPLE, uint32(len(elems))) } func (fcomp *fcomp) comprehension(comp *syntax.Comprehension, clauseIndex int) { if clauseIndex == len(comp.Clauses) { fcomp.emit(DUP) // accumulator if comp.Curly { // dict: {k:v for ...} // Parser ensures that body is of form k:v. // Python-style set comprehensions {body for vars in x} // are not supported. entry := comp.Body.(*syntax.DictEntry) fcomp.expr(entry.Key) fcomp.expr(entry.Value) fcomp.setPos(entry.Colon) fcomp.emit(SETDICT) } else { // list: [body for vars in x] fcomp.expr(comp.Body) fcomp.emit(APPEND) } return } clause := comp.Clauses[clauseIndex] switch clause := clause.(type) { case *syntax.IfClause: t := fcomp.newBlock() done := fcomp.newBlock() fcomp.ifelse(clause.Cond, t, done) fcomp.block = t fcomp.comprehension(comp, clauseIndex+1) fcomp.jump(done) fcomp.block = done return case *syntax.ForClause: // Keep consistent with ForStmt. head := fcomp.newBlock() body := fcomp.newBlock() tail := fcomp.newBlock() fcomp.expr(clause.X) fcomp.setPos(clause.For) fcomp.emit(ITERPUSH) fcomp.jump(head) fcomp.block = head fcomp.condjump(ITERJMP, tail, body) fcomp.block = body fcomp.assign(clause.For, clause.Vars) fcomp.comprehension(comp, clauseIndex+1) fcomp.jump(head) fcomp.block = tail fcomp.emit(ITERPOP) return } start, _ := clause.Span() log.Panicf("%s: unexpected comprehension clause %T", start, clause) } func (fcomp *fcomp) function(f *resolve.Function) { // Evaluation of the defaults may fail, so record the position. fcomp.setPos(f.Pos) // To reduce allocation, we emit a combined tuple // for the defaults and the freevars. // The function knows where to split it at run time. // Generate tuple of parameter defaults. For: // def f(p1, p2=dp2, p3=dp3, *, k1, k2=dk2, k3, **kwargs) // the tuple is: // (dp2, dp3, MANDATORY, dk2, MANDATORY). ndefaults := 0 seenStar := false for _, param := range f.Params { switch param := param.(type) { case *syntax.BinaryExpr: fcomp.expr(param.Y) ndefaults++ case *syntax.UnaryExpr: seenStar = true // * or *args (also **kwargs) case *syntax.Ident: if seenStar { fcomp.emit(MANDATORY) ndefaults++ } } } // Capture the cells of the function's // free variables from the lexical environment. for _, freevar := range f.FreeVars { // Don't call fcomp.lookup because we want // the cell itself, not its content. switch freevar.Scope { case resolve.Free: fcomp.emit1(FREE, uint32(freevar.Index)) case resolve.Cell: fcomp.emit1(LOCAL, uint32(freevar.Index)) } } fcomp.emit1(MAKETUPLE, uint32(ndefaults+len(f.FreeVars))) funcode := fcomp.pcomp.function(f.Name, f.Pos, f.Body, f.Locals, f.FreeVars) if debug { // TODO(adonovan): do compilations sequentially not as a tree, // to make the log easier to read. // Simplify by identifying Toplevel and functionIndex 0. fmt.Fprintf(os.Stderr, "resuming %s @ %s\n", fcomp.fn.Name, fcomp.pos) } // def f(a, *, b=1) has only 2 parameters. numParams := len(f.Params) if f.NumKwonlyParams > 0 && !f.HasVarargs { numParams-- } funcode.NumParams = numParams funcode.NumKwonlyParams = f.NumKwonlyParams funcode.HasVarargs = f.HasVarargs funcode.HasKwargs = f.HasKwargs fcomp.emit1(MAKEFUNC, fcomp.pcomp.functionIndex(funcode)) } // ifelse emits a Boolean control flow decision. // On return, the current block is unset. func (fcomp *fcomp) ifelse(cond syntax.Expr, t, f *block) { switch cond := cond.(type) { case *syntax.UnaryExpr: if cond.Op == syntax.NOT { // if not x then goto t else goto f // => // if x then goto f else goto t fcomp.ifelse(cond.X, f, t) return } case *syntax.BinaryExpr: switch cond.Op { case syntax.AND: // if x and y then goto t else goto f // => // if x then ifelse(y, t, f) else goto f fcomp.expr(cond.X) y := fcomp.newBlock() fcomp.condjump(CJMP, y, f) fcomp.block = y fcomp.ifelse(cond.Y, t, f) return case syntax.OR: // if x or y then goto t else goto f // => // if x then goto t else ifelse(y, t, f) fcomp.expr(cond.X) y := fcomp.newBlock() fcomp.condjump(CJMP, t, y) fcomp.block = y fcomp.ifelse(cond.Y, t, f) return case syntax.NOT_IN: // if x not in y then goto t else goto f // => // if x in y then goto f else goto t copy := *cond copy.Op = syntax.IN fcomp.expr(©) fcomp.condjump(CJMP, f, t) return } } // general case fcomp.expr(cond) fcomp.condjump(CJMP, t, f) } ================================================ FILE: vendor/go.starlark.net/internal/compile/serial.go ================================================ package compile // This file defines functions to read and write a compile.Program to a file. // // It is the client's responsibility to avoid version skew between the // compiler used to produce a file and the interpreter that consumes it. // The version number is provided as a constant. // Incompatible protocol changes should also increment the version number. // // Encoding // // Program: // "sky!" [4]byte # magic number // str uint32le # offset of section // version varint # must match Version // filename string // numloads varint // loads []Ident // numnames varint // names []string // numconsts varint // consts []Constant // numglobals varint // globals []Ident // toplevel Funcode // numfuncs varint // funcs []Funcode // []byte # concatenation of all referenced strings // EOF // // Funcode: // id Ident // code []byte // pclinetablen varint // pclinetab []varint // numlocals varint // locals []Ident // numcells varint // cells []int // numfreevars varint // freevar []Ident // maxstack varint // numparams varint // numkwonlyparams varint // hasvarargs varint (0 or 1) // haskwargs varint (0 or 1) // // Ident: // filename string // line, col varint // // Constant: # type data // type varint # 0=string string // data ... # 1=int varint // # 2=float varint (bits as uint64) // # 3=bigint string (decimal ASCII text) // // The encoding starts with a four-byte magic number. // The next four bytes are a little-endian uint32 // that provides the offset of the string section // at the end of the file, which contains the ordered // concatenation of all strings referenced by the // program. This design permits the decoder to read // the first and second parts of the file into different // memory allocations: the first (the encoded program) // is transient, but the second (the strings) persists // for the life of the Program. // // Within the encoded program, all strings are referred // to by their length. As the encoder and decoder process // the entire file sequentially, they are in lock step, // so the start offset of each string is implicit. // // Program.Code is represented as a []byte slice to permit // modification when breakpoints are set. All other strings // are represented as strings. They all (unsafely) share the // same backing byte slice. // // Aside from the str field, all integers are encoded as varints. import ( "encoding/binary" "fmt" "math" "math/big" debugpkg "runtime/debug" "unsafe" "go.starlark.net/syntax" ) const magic = "!sky" // Encode encodes a compiled Starlark program. func (prog *Program) Encode() []byte { var e encoder e.p = append(e.p, magic...) e.p = append(e.p, "????"...) // string data offset; filled in later e.int(Version) e.string(prog.Toplevel.Pos.Filename()) e.bindings(prog.Loads) e.int(len(prog.Names)) for _, name := range prog.Names { e.string(name) } e.int(len(prog.Constants)) for _, c := range prog.Constants { switch c := c.(type) { case string: e.int(0) e.string(c) case int64: e.int(1) e.int64(c) case float64: e.int(2) e.uint64(math.Float64bits(c)) case *big.Int: e.int(3) e.string(c.Text(10)) } } e.bindings(prog.Globals) e.function(prog.Toplevel) e.int(len(prog.Functions)) for _, fn := range prog.Functions { e.function(fn) } // Patch in the offset of the string data section. binary.LittleEndian.PutUint32(e.p[4:8], uint32(len(e.p))) return append(e.p, e.s...) } type encoder struct { p []byte // encoded program s []byte // strings tmp [binary.MaxVarintLen64]byte } func (e *encoder) int(x int) { e.int64(int64(x)) } func (e *encoder) int64(x int64) { n := binary.PutVarint(e.tmp[:], x) e.p = append(e.p, e.tmp[:n]...) } func (e *encoder) uint64(x uint64) { n := binary.PutUvarint(e.tmp[:], x) e.p = append(e.p, e.tmp[:n]...) } func (e *encoder) string(s string) { e.int(len(s)) e.s = append(e.s, s...) } func (e *encoder) bytes(b []byte) { e.int(len(b)) e.s = append(e.s, b...) } func (e *encoder) binding(bind Binding) { e.string(bind.Name) e.int(int(bind.Pos.Line)) e.int(int(bind.Pos.Col)) } func (e *encoder) bindings(binds []Binding) { e.int(len(binds)) for _, bind := range binds { e.binding(bind) } } func (e *encoder) function(fn *Funcode) { e.binding(Binding{fn.Name, fn.Pos}) e.string(fn.Doc) e.bytes(fn.Code) e.int(len(fn.pclinetab)) for _, x := range fn.pclinetab { e.int64(int64(x)) } e.bindings(fn.Locals) e.int(len(fn.Cells)) for _, index := range fn.Cells { e.int(index) } e.bindings(fn.Freevars) e.int(fn.MaxStack) e.int(fn.NumParams) e.int(fn.NumKwonlyParams) e.int(b2i(fn.HasVarargs)) e.int(b2i(fn.HasKwargs)) } func b2i(b bool) int { if b { return 1 } else { return 0 } } // DecodeProgram decodes a compiled Starlark program from data. func DecodeProgram(data []byte) (_ *Program, err error) { if len(data) < len(magic) { return nil, fmt.Errorf("not a compiled module: no magic number") } if got := string(data[:4]); got != magic { return nil, fmt.Errorf("not a compiled module: got magic number %q, want %q", got, magic) } defer func() { if x := recover(); x != nil { debugpkg.PrintStack() err = fmt.Errorf("internal error while decoding program: %v", x) } }() offset := binary.LittleEndian.Uint32(data[4:8]) d := decoder{ p: data[8:offset], s: append([]byte(nil), data[offset:]...), // allocate a copy, which will persist } if v := d.int(); v != Version { return nil, fmt.Errorf("version mismatch: read %d, want %d", v, Version) } filename := d.string() d.filename = &filename loads := d.bindings() names := make([]string, d.int()) for i := range names { names[i] = d.string() } // constants constants := make([]interface{}, d.int()) for i := range constants { var c interface{} switch d.int() { case 0: c = d.string() case 1: c = d.int64() case 2: c = math.Float64frombits(d.uint64()) case 3: c, _ = new(big.Int).SetString(d.string(), 10) } constants[i] = c } globals := d.bindings() toplevel := d.function() funcs := make([]*Funcode, d.int()) for i := range funcs { funcs[i] = d.function() } prog := &Program{ Loads: loads, Names: names, Constants: constants, Globals: globals, Functions: funcs, Toplevel: toplevel, } toplevel.Prog = prog for _, f := range funcs { f.Prog = prog } if len(d.p)+len(d.s) > 0 { return nil, fmt.Errorf("internal error: unconsumed data during decoding") } return prog, nil } type decoder struct { p []byte // encoded program s []byte // strings filename *string // (indirect to avoid keeping decoder live) } func (d *decoder) int() int { return int(d.int64()) } func (d *decoder) int64() int64 { x, len := binary.Varint(d.p[:]) d.p = d.p[len:] return x } func (d *decoder) uint64() uint64 { x, len := binary.Uvarint(d.p[:]) d.p = d.p[len:] return x } func (d *decoder) string() (s string) { if slice := d.bytes(); len(slice) > 0 { // Avoid a memory allocation for each string // by unsafely aliasing slice. type string struct { data *byte len int } ptr := (*string)(unsafe.Pointer(&s)) ptr.data = &slice[0] ptr.len = len(slice) } return s } func (d *decoder) bytes() []byte { len := d.int() r := d.s[:len:len] d.s = d.s[len:] return r } func (d *decoder) binding() Binding { name := d.string() line := int32(d.int()) col := int32(d.int()) return Binding{Name: name, Pos: syntax.MakePosition(d.filename, line, col)} } func (d *decoder) bindings() []Binding { bindings := make([]Binding, d.int()) for i := range bindings { bindings[i] = d.binding() } return bindings } func (d *decoder) ints() []int { ints := make([]int, d.int()) for i := range ints { ints[i] = d.int() } return ints } func (d *decoder) bool() bool { return d.int() != 0 } func (d *decoder) function() *Funcode { id := d.binding() doc := d.string() code := d.bytes() pclinetab := make([]uint16, d.int()) for i := range pclinetab { pclinetab[i] = uint16(d.int()) } locals := d.bindings() cells := d.ints() freevars := d.bindings() maxStack := d.int() numParams := d.int() numKwonlyParams := d.int() hasVarargs := d.int() != 0 hasKwargs := d.int() != 0 return &Funcode{ // Prog is filled in later. Pos: id.Pos, Name: id.Name, Doc: doc, Code: code, pclinetab: pclinetab, Locals: locals, Cells: cells, Freevars: freevars, MaxStack: maxStack, NumParams: numParams, NumKwonlyParams: numKwonlyParams, HasVarargs: hasVarargs, HasKwargs: hasKwargs, } } ================================================ FILE: vendor/go.starlark.net/internal/spell/spell.go ================================================ // Package spell file defines a simple spelling checker for use in attribute errors // such as "no such field .foo; did you mean .food?". package spell import ( "strings" "unicode" ) // Nearest returns the element of candidates // nearest to x using the Levenshtein metric, // or "" if none were promising. func Nearest(x string, candidates []string) string { // Ignore underscores and case when matching. fold := func(s string) string { return strings.Map(func(r rune) rune { if r == '_' { return -1 } return unicode.ToLower(r) }, s) } x = fold(x) var best string bestD := (len(x) + 1) / 2 // allow up to 50% typos for _, c := range candidates { d := levenshtein(x, fold(c), bestD) if d < bestD { bestD = d best = c } } return best } // levenshtein returns the non-negative Levenshtein edit distance // between the byte strings x and y. // // If the computed distance exceeds max, // the function may return early with an approximate value > max. func levenshtein(x, y string, max int) int { // This implementation is derived from one by Laurent Le Brun in // Bazel that uses the single-row space efficiency trick // described at bitbucket.org/clearer/iosifovich. // Let x be the shorter string. if len(x) > len(y) { x, y = y, x } // Remove common prefix. for i := 0; i < len(x); i++ { if x[i] != y[i] { x = x[i:] y = y[i:] break } } if x == "" { return len(y) } if d := abs(len(x) - len(y)); d > max { return d // excessive length divergence } row := make([]int, len(y)+1) for i := range row { row[i] = i } for i := 1; i <= len(x); i++ { row[0] = i best := i prev := i - 1 for j := 1; j <= len(y); j++ { a := prev + b2i(x[i-1] != y[j-1]) // substitution b := 1 + row[j-1] // deletion c := 1 + row[j] // insertion k := min(a, min(b, c)) prev, row[j] = row[j], k best = min(best, k) } if best > max { return best } } return row[len(y)] } func b2i(b bool) int { if b { return 1 } else { return 0 } } func min(x, y int) int { if x < y { return x } else { return y } } func abs(x int) int { if x >= 0 { return x } else { return -x } } ================================================ FILE: vendor/go.starlark.net/resolve/binding.go ================================================ // Copyright 2019 The Bazel Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package resolve import "go.starlark.net/syntax" // This file defines resolver data types saved in the syntax tree. // We cannot guarantee API stability for these types // as they are closely tied to the implementation. // A Binding contains resolver information about an identifer. // The resolver populates the Binding field of each syntax.Identifier. // The Binding ties together all identifiers that denote the same variable. type Binding struct { Scope Scope // Index records the index into the enclosing // - {DefStmt,File}.Locals, if Scope==Local // - DefStmt.FreeVars, if Scope==Free // - File.Globals, if Scope==Global. // It is zero if Scope is Predeclared, Universal, or Undefined. Index int First *syntax.Ident // first binding use (iff Scope==Local/Free/Global) } // The Scope of Binding indicates what kind of scope it has. type Scope uint8 const ( Undefined Scope = iota // name is not defined Local // name is local to its function or file Cell // name is function-local but shared with a nested function Free // name is cell of some enclosing function Global // name is global to module Predeclared // name is predeclared for this module (e.g. glob) Universal // name is universal (e.g. len) ) var scopeNames = [...]string{ Undefined: "undefined", Local: "local", Cell: "cell", Free: "free", Global: "global", Predeclared: "predeclared", Universal: "universal", } func (scope Scope) String() string { return scopeNames[scope] } // A Module contains resolver information about a file. // The resolver populates the Module field of each syntax.File. type Module struct { Locals []*Binding // the file's (comprehension-)local variables Globals []*Binding // the file's global variables } // A Function contains resolver information about a named or anonymous function. // The resolver populates the Function field of each syntax.DefStmt and syntax.LambdaExpr. type Function struct { Pos syntax.Position // of DEF or LAMBDA Name string // name of def, or "lambda" Params []syntax.Expr // param = ident | ident=expr | * | *ident | **ident Body []syntax.Stmt // contains synthetic 'return expr' for lambda HasVarargs bool // whether params includes *args (convenience) HasKwargs bool // whether params includes **kwargs (convenience) NumKwonlyParams int // number of keyword-only optional parameters Locals []*Binding // this function's local/cell variables, parameters first FreeVars []*Binding // enclosing cells to capture in closure } ================================================ FILE: vendor/go.starlark.net/resolve/resolve.go ================================================ // Copyright 2017 The Bazel Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package resolve defines a name-resolution pass for Starlark abstract // syntax trees. // // The resolver sets the Locals and FreeVars arrays of each DefStmt and // the LocalIndex field of each syntax.Ident that refers to a local or // free variable. It also sets the Locals array of a File for locals // bound by top-level comprehensions and load statements. // Identifiers for global variables do not get an index. package resolve // import "go.starlark.net/resolve" // All references to names are statically resolved. Names may be // predeclared, global, or local to a function or file. // File-local variables include those bound by top-level comprehensions // and by load statements. ("Top-level" means "outside of any function".) // The resolver maps each global name to a small integer and each local // name to a small integer; these integers enable a fast and compact // representation of globals and locals in the evaluator. // // As an optimization, the resolver classifies each predeclared name as // either universal (e.g. None, len) or per-module (e.g. glob in Bazel's // build language), enabling the evaluator to share the representation // of the universal environment across all modules. // // The lexical environment is a tree of blocks with the file block at // its root. The file's child blocks may be of two kinds: functions // and comprehensions, and these may have further children of either // kind. // // Python-style resolution requires multiple passes because a name is // determined to be local to a function only if the function contains a // "binding" use of it; similarly, a name is determined to be global (as // opposed to predeclared) if the module contains a top-level binding use. // Unlike ordinary top-level assignments, the bindings created by load // statements are local to the file block. // A non-binding use may lexically precede the binding to which it is resolved. // In the first pass, we inspect each function, recording in // 'uses' each identifier and the environment block in which it occurs. // If a use of a name is binding, such as a function parameter or // assignment, we add the name to the block's bindings mapping and add a // local variable to the enclosing function. // // As we finish resolving each function, we inspect all the uses within // that function and discard ones that were found to be function-local. The // remaining ones must be either free (local to some lexically enclosing // function), or top-level (global, predeclared, or file-local), but we cannot tell // which until we have finished inspecting the outermost enclosing // function. At that point, we can distinguish local from top-level names // (and this is when Python would compute free variables). // // However, Starlark additionally requires that all references to global // names are satisfied by some declaration in the current module; // Starlark permits a function to forward-reference a global or file-local // that has not // been declared yet so long as it is declared before the end of the // module. So, instead of re-resolving the unresolved references after // each top-level function, we defer this until the end of the module // and ensure that all such references are satisfied by some definition. // // At the end of the module, we visit each of the nested function blocks // in bottom-up order, doing a recursive lexical lookup for each // unresolved name. If the name is found to be local to some enclosing // function, we must create a DefStmt.FreeVar (capture) parameter for // each intervening function. We enter these synthetic bindings into // the bindings map so that we create at most one freevar per name. If // the name was not local, we check that it was defined at module level. // // We resolve all uses of locals in the module (due to load statements // and comprehensions) in a similar way and compute the file's set of // local variables. // // Starlark enforces that all global names are assigned at most once on // all control flow paths by forbidding if/else statements and loops at // top level. A global may be used before it is defined, leading to a // dynamic error. However, the AllowGlobalReassign flag (really: allow // top-level reassign) makes the resolver allow multiple to a variable // at top-level. It also allows if-, for-, and while-loops at top-level, // which in turn may make the evaluator dynamically assign multiple // values to a variable at top-level. (These two roles should be separated.) import ( "fmt" "log" "sort" "strings" "go.starlark.net/internal/spell" "go.starlark.net/syntax" ) const debug = false const doesnt = "this Starlark dialect does not " // global options // These features are either not standard Starlark (yet), or deprecated // features of the BUILD language, so we put them behind flags. var ( AllowNestedDef = false // allow def statements within function bodies AllowLambda = false // allow lambda expressions AllowFloat = false // allow floating point literals, the 'float' built-in, and x / y AllowSet = false // allow the 'set' built-in AllowGlobalReassign = false // allow reassignment to top-level names; also, allow if/for/while at top-level AllowRecursion = false // allow while statements and recursive functions AllowBitwise = true // obsolete; bitwise operations (&, |, ^, ~, <<, and >>) are always enabled LoadBindsGlobally = false // load creates global not file-local bindings (deprecated) ) // File resolves the specified file and records information about the // module in file.Module. // // The isPredeclared and isUniversal predicates report whether a name is // a pre-declared identifier (visible in the current module) or a // universal identifier (visible in every module). // Clients should typically pass predeclared.Has for the first and // starlark.Universe.Has for the second, where predeclared is the // module's StringDict of predeclared names and starlark.Universe is the // standard set of built-ins. // The isUniverse predicate is supplied a parameter to avoid a cyclic // dependency upon starlark.Universe, not because users should ever need // to redefine it. func File(file *syntax.File, isPredeclared, isUniversal func(name string) bool) error { return REPLChunk(file, nil, isPredeclared, isUniversal) } // REPLChunk is a generalization of the File function that supports a // non-empty initial global block, as occurs in a REPL. func REPLChunk(file *syntax.File, isGlobal, isPredeclared, isUniversal func(name string) bool) error { r := newResolver(isGlobal, isPredeclared, isUniversal) r.stmts(file.Stmts) r.env.resolveLocalUses() // At the end of the module, resolve all non-local variable references, // computing closures. // Function bodies may contain forward references to later global declarations. r.resolveNonLocalUses(r.env) file.Module = &Module{ Locals: r.moduleLocals, Globals: r.moduleGlobals, } if len(r.errors) > 0 { return r.errors } return nil } // Expr resolves the specified expression. // It returns the local variables bound within the expression. // // The isPredeclared and isUniversal predicates behave as for the File function. func Expr(expr syntax.Expr, isPredeclared, isUniversal func(name string) bool) ([]*Binding, error) { r := newResolver(nil, isPredeclared, isUniversal) r.expr(expr) r.env.resolveLocalUses() r.resolveNonLocalUses(r.env) // globals & universals if len(r.errors) > 0 { return nil, r.errors } return r.moduleLocals, nil } // An ErrorList is a non-empty list of resolver error messages. type ErrorList []Error // len > 0 func (e ErrorList) Error() string { return e[0].Error() } // An Error describes the nature and position of a resolver error. type Error struct { Pos syntax.Position Msg string } func (e Error) Error() string { return e.Pos.String() + ": " + e.Msg } func newResolver(isGlobal, isPredeclared, isUniversal func(name string) bool) *resolver { file := new(block) return &resolver{ file: file, env: file, isGlobal: isGlobal, isPredeclared: isPredeclared, isUniversal: isUniversal, globals: make(map[string]*Binding), predeclared: make(map[string]*Binding), } } type resolver struct { // env is the current local environment: // a linked list of blocks, innermost first. // The tail of the list is the file block. env *block file *block // file block (contains load bindings) // moduleLocals contains the local variables of the module // (due to load statements and comprehensions outside any function). // moduleGlobals contains the global variables of the module. moduleLocals []*Binding moduleGlobals []*Binding // globals maps each global name in the module to its binding. // predeclared does the same for predeclared and universal names. globals map[string]*Binding predeclared map[string]*Binding // These predicates report whether a name is // pre-declared, either in this module or universally, // or already declared in the module globals (as in a REPL). // isGlobal may be nil. isGlobal, isPredeclared, isUniversal func(name string) bool loops int // number of enclosing for loops errors ErrorList } // container returns the innermost enclosing "container" block: // a function (function != nil) or file (function == nil). // Container blocks accumulate local variable bindings. func (r *resolver) container() *block { for b := r.env; ; b = b.parent { if b.function != nil || b == r.file { return b } } } func (r *resolver) push(b *block) { r.env.children = append(r.env.children, b) b.parent = r.env r.env = b } func (r *resolver) pop() { r.env = r.env.parent } type block struct { parent *block // nil for file block // In the file (root) block, both these fields are nil. function *Function // only for function blocks comp *syntax.Comprehension // only for comprehension blocks // bindings maps a name to its binding. // A local binding has an index into its innermost enclosing container's locals array. // A free binding has an index into its innermost enclosing function's freevars array. bindings map[string]*Binding // children records the child blocks of the current one. children []*block // uses records all identifiers seen in this container (function or file), // and a reference to the environment in which they appear. // As we leave each container block, we resolve them, // so that only free and global ones remain. // At the end of each top-level function we compute closures. uses []use } func (b *block) bind(name string, bind *Binding) { if b.bindings == nil { b.bindings = make(map[string]*Binding) } b.bindings[name] = bind } func (b *block) String() string { if b.function != nil { return "function block at " + fmt.Sprint(b.function.Pos) } if b.comp != nil { return "comprehension block at " + fmt.Sprint(b.comp.Span()) } return "file block" } func (r *resolver) errorf(posn syntax.Position, format string, args ...interface{}) { r.errors = append(r.errors, Error{posn, fmt.Sprintf(format, args...)}) } // A use records an identifier and the environment in which it appears. type use struct { id *syntax.Ident env *block } // bind creates a binding for id: a global (not file-local) // binding at top-level, a local binding otherwise. // At top-level, it reports an error if a global or file-local // binding already exists, unless AllowGlobalReassign. // It sets id.Binding to the binding (whether old or new), // and returns whether a binding already existed. func (r *resolver) bind(id *syntax.Ident) bool { // Binding outside any local (comprehension/function) block? if r.env == r.file { bind, ok := r.file.bindings[id.Name] if !ok { bind, ok = r.globals[id.Name] if !ok { // first global binding of this name bind = &Binding{ First: id, Scope: Global, Index: len(r.moduleGlobals), } r.globals[id.Name] = bind r.moduleGlobals = append(r.moduleGlobals, bind) } } if ok && !AllowGlobalReassign { r.errorf(id.NamePos, "cannot reassign %s %s declared at %s", bind.Scope, id.Name, bind.First.NamePos) } id.Binding = bind return ok } return r.bindLocal(id) } func (r *resolver) bindLocal(id *syntax.Ident) bool { // Mark this name as local to current block. // Assign it a new local (positive) index in the current container. _, ok := r.env.bindings[id.Name] if !ok { var locals *[]*Binding if fn := r.container().function; fn != nil { locals = &fn.Locals } else { locals = &r.moduleLocals } bind := &Binding{ First: id, Scope: Local, Index: len(*locals), } r.env.bind(id.Name, bind) *locals = append(*locals, bind) } r.use(id) return ok } func (r *resolver) use(id *syntax.Ident) { use := use{id, r.env} // The spec says that if there is a global binding of a name // then all references to that name in that block refer to the // global, even if the use precedes the def---just as for locals. // For example, in this code, // // print(len); len=1; print(len) // // both occurrences of len refer to the len=1 binding, which // completely shadows the predeclared len function. // // The rationale for these semantics, which differ from Python, // is that the static meaning of len (a reference to a global) // does not change depending on where it appears in the file. // Of course, its dynamic meaning does change, from an error // into a valid reference, so it's not clear these semantics // have any practical advantage. // // In any case, the Bazel implementation lags behind the spec // and follows Python behavior, so the first use of len refers // to the predeclared function. This typically used in a BUILD // file that redefines a predeclared name half way through, // for example: // // proto_library(...) # built-in rule // load("myproto.bzl", "proto_library") // proto_library(...) # user-defined rule // // We will piggyback support for the legacy semantics on the // AllowGlobalReassign flag, which is loosely related and also // required for Bazel. if AllowGlobalReassign && r.env == r.file { r.useToplevel(use) return } b := r.container() b.uses = append(b.uses, use) } // useToplevel resolves use.id as a reference to a name visible at top-level. // The use.env field captures the original environment for error reporting. func (r *resolver) useToplevel(use use) (bind *Binding) { id := use.id if prev, ok := r.file.bindings[id.Name]; ok { // use of load-defined name in file block bind = prev } else if prev, ok := r.globals[id.Name]; ok { // use of global declared by module bind = prev } else if r.isGlobal != nil && r.isGlobal(id.Name) { // use of global defined in a previous REPL chunk bind = &Binding{ First: id, // wrong: this is not even a binding use Scope: Global, Index: len(r.moduleGlobals), } r.globals[id.Name] = bind r.moduleGlobals = append(r.moduleGlobals, bind) } else if prev, ok := r.predeclared[id.Name]; ok { // repeated use of predeclared or universal bind = prev } else if r.isPredeclared(id.Name) { // use of pre-declared name bind = &Binding{Scope: Predeclared} r.predeclared[id.Name] = bind // save it } else if r.isUniversal(id.Name) { // use of universal name if !AllowFloat && id.Name == "float" { r.errorf(id.NamePos, doesnt+"support floating point") } if !AllowSet && id.Name == "set" { r.errorf(id.NamePos, doesnt+"support sets") } bind = &Binding{Scope: Universal} r.predeclared[id.Name] = bind // save it } else { bind = &Binding{Scope: Undefined} var hint string if n := r.spellcheck(use); n != "" { hint = fmt.Sprintf(" (did you mean %s?)", n) } r.errorf(id.NamePos, "undefined: %s%s", id.Name, hint) } id.Binding = bind return bind } // spellcheck returns the most likely misspelling of // the name use.id in the environment use.env. func (r *resolver) spellcheck(use use) string { var names []string // locals for b := use.env; b != nil; b = b.parent { for name := range b.bindings { names = append(names, name) } } // globals // // We have no way to enumerate the sets whose membership // tests are isPredeclared, isUniverse, and isGlobal, // which includes prior names in the REPL session. for _, bind := range r.moduleGlobals { names = append(names, bind.First.Name) } sort.Strings(names) return spell.Nearest(use.id.Name, names) } // resolveLocalUses is called when leaving a container (function/module) // block. It resolves all uses of locals/cells within that block. func (b *block) resolveLocalUses() { unresolved := b.uses[:0] for _, use := range b.uses { if bind := lookupLocal(use); bind != nil && (bind.Scope == Local || bind.Scope == Cell) { use.id.Binding = bind } else { unresolved = append(unresolved, use) } } b.uses = unresolved } func (r *resolver) stmts(stmts []syntax.Stmt) { for _, stmt := range stmts { r.stmt(stmt) } } func (r *resolver) stmt(stmt syntax.Stmt) { switch stmt := stmt.(type) { case *syntax.ExprStmt: r.expr(stmt.X) case *syntax.BranchStmt: if r.loops == 0 && (stmt.Token == syntax.BREAK || stmt.Token == syntax.CONTINUE) { r.errorf(stmt.TokenPos, "%s not in a loop", stmt.Token) } case *syntax.IfStmt: if !AllowGlobalReassign && r.container().function == nil { r.errorf(stmt.If, "if statement not within a function") } r.expr(stmt.Cond) r.stmts(stmt.True) r.stmts(stmt.False) case *syntax.AssignStmt: r.expr(stmt.RHS) isAugmented := stmt.Op != syntax.EQ r.assign(stmt.LHS, isAugmented) case *syntax.DefStmt: if !AllowNestedDef && r.container().function != nil { r.errorf(stmt.Def, doesnt+"support nested def") } r.bind(stmt.Name) fn := &Function{ Name: stmt.Name.Name, Pos: stmt.Def, Params: stmt.Params, Body: stmt.Body, } stmt.Function = fn r.function(fn, stmt.Def) case *syntax.ForStmt: if !AllowGlobalReassign && r.container().function == nil { r.errorf(stmt.For, "for loop not within a function") } r.expr(stmt.X) const isAugmented = false r.assign(stmt.Vars, isAugmented) r.loops++ r.stmts(stmt.Body) r.loops-- case *syntax.WhileStmt: if !AllowRecursion { r.errorf(stmt.While, doesnt+"support while loops") } if !AllowGlobalReassign && r.container().function == nil { r.errorf(stmt.While, "while loop not within a function") } r.expr(stmt.Cond) r.loops++ r.stmts(stmt.Body) r.loops-- case *syntax.ReturnStmt: if r.container().function == nil { r.errorf(stmt.Return, "return statement not within a function") } if stmt.Result != nil { r.expr(stmt.Result) } case *syntax.LoadStmt: if r.container().function != nil { r.errorf(stmt.Load, "load statement within a function") } for i, from := range stmt.From { if from.Name == "" { r.errorf(from.NamePos, "load: empty identifier") continue } if from.Name[0] == '_' { r.errorf(from.NamePos, "load: names with leading underscores are not exported: %s", from.Name) } id := stmt.To[i] if LoadBindsGlobally { r.bind(id) } else if r.bindLocal(id) && !AllowGlobalReassign { // "Global" in AllowGlobalReassign is a misnomer for "toplevel". // Sadly we can't report the previous declaration // as id.Binding may not be set yet. r.errorf(id.NamePos, "cannot reassign top-level %s", id.Name) } } default: log.Panicf("unexpected stmt %T", stmt) } } func (r *resolver) assign(lhs syntax.Expr, isAugmented bool) { switch lhs := lhs.(type) { case *syntax.Ident: // x = ... r.bind(lhs) case *syntax.IndexExpr: // x[i] = ... r.expr(lhs.X) r.expr(lhs.Y) case *syntax.DotExpr: // x.f = ... r.expr(lhs.X) case *syntax.TupleExpr: // (x, y) = ... if len(lhs.List) == 0 { r.errorf(syntax.Start(lhs), "can't assign to ()") } if isAugmented { r.errorf(syntax.Start(lhs), "can't use tuple expression in augmented assignment") } for _, elem := range lhs.List { r.assign(elem, isAugmented) } case *syntax.ListExpr: // [x, y, z] = ... if len(lhs.List) == 0 { r.errorf(syntax.Start(lhs), "can't assign to []") } if isAugmented { r.errorf(syntax.Start(lhs), "can't use list expression in augmented assignment") } for _, elem := range lhs.List { r.assign(elem, isAugmented) } case *syntax.ParenExpr: r.assign(lhs.X, isAugmented) default: name := strings.ToLower(strings.TrimPrefix(fmt.Sprintf("%T", lhs), "*syntax.")) r.errorf(syntax.Start(lhs), "can't assign to %s", name) } } func (r *resolver) expr(e syntax.Expr) { switch e := e.(type) { case *syntax.Ident: r.use(e) case *syntax.Literal: if !AllowFloat && e.Token == syntax.FLOAT { r.errorf(e.TokenPos, doesnt+"support floating point") } case *syntax.ListExpr: for _, x := range e.List { r.expr(x) } case *syntax.CondExpr: r.expr(e.Cond) r.expr(e.True) r.expr(e.False) case *syntax.IndexExpr: r.expr(e.X) r.expr(e.Y) case *syntax.DictEntry: r.expr(e.Key) r.expr(e.Value) case *syntax.SliceExpr: r.expr(e.X) if e.Lo != nil { r.expr(e.Lo) } if e.Hi != nil { r.expr(e.Hi) } if e.Step != nil { r.expr(e.Step) } case *syntax.Comprehension: // The 'in' operand of the first clause (always a ForClause) // is resolved in the outer block; consider: [x for x in x]. clause := e.Clauses[0].(*syntax.ForClause) r.expr(clause.X) // A list/dict comprehension defines a new lexical block. // Locals defined within the block will be allotted // distinct slots in the locals array of the innermost // enclosing container (function/module) block. r.push(&block{comp: e}) const isAugmented = false r.assign(clause.Vars, isAugmented) for _, clause := range e.Clauses[1:] { switch clause := clause.(type) { case *syntax.IfClause: r.expr(clause.Cond) case *syntax.ForClause: r.assign(clause.Vars, isAugmented) r.expr(clause.X) } } r.expr(e.Body) // body may be *DictEntry r.pop() case *syntax.TupleExpr: for _, x := range e.List { r.expr(x) } case *syntax.DictExpr: for _, entry := range e.List { entry := entry.(*syntax.DictEntry) r.expr(entry.Key) r.expr(entry.Value) } case *syntax.UnaryExpr: r.expr(e.X) case *syntax.BinaryExpr: if !AllowFloat && e.Op == syntax.SLASH { r.errorf(e.OpPos, doesnt+"support floating point (use //)") } r.expr(e.X) r.expr(e.Y) case *syntax.DotExpr: r.expr(e.X) // ignore e.Name case *syntax.CallExpr: r.expr(e.Fn) var seenVarargs, seenKwargs bool var seenName map[string]bool var n, p int for _, arg := range e.Args { pos, _ := arg.Span() if unop, ok := arg.(*syntax.UnaryExpr); ok && unop.Op == syntax.STARSTAR { // **kwargs if seenKwargs { r.errorf(pos, "multiple **kwargs not allowed") } seenKwargs = true r.expr(arg) } else if ok && unop.Op == syntax.STAR { // *args if seenKwargs { r.errorf(pos, "*args may not follow **kwargs") } else if seenVarargs { r.errorf(pos, "multiple *args not allowed") } seenVarargs = true r.expr(arg) } else if binop, ok := arg.(*syntax.BinaryExpr); ok && binop.Op == syntax.EQ { // k=v n++ if seenKwargs { r.errorf(pos, "argument may not follow **kwargs") } x := binop.X.(*syntax.Ident) if seenName[x.Name] { r.errorf(x.NamePos, "keyword argument %s repeated", x.Name) } else { if seenName == nil { seenName = make(map[string]bool) } seenName[x.Name] = true } r.expr(binop.Y) } else { // positional argument p++ if seenVarargs { r.errorf(pos, "argument may not follow *args") } else if seenKwargs { r.errorf(pos, "argument may not follow **kwargs") } else if len(seenName) > 0 { r.errorf(pos, "positional argument may not follow named") } r.expr(arg) } } // Fail gracefully if compiler-imposed limit is exceeded. if p >= 256 { pos, _ := e.Span() r.errorf(pos, "%v positional arguments in call, limit is 255", p) } if n >= 256 { pos, _ := e.Span() r.errorf(pos, "%v keyword arguments in call, limit is 255", n) } case *syntax.LambdaExpr: if !AllowLambda { r.errorf(e.Lambda, doesnt+"support lambda") } fn := &Function{ Name: "lambda", Pos: e.Lambda, Params: e.Params, Body: []syntax.Stmt{&syntax.ReturnStmt{Result: e.Body}}, } e.Function = fn r.function(fn, e.Lambda) case *syntax.ParenExpr: r.expr(e.X) default: log.Panicf("unexpected expr %T", e) } } func (r *resolver) function(function *Function, pos syntax.Position) { // Resolve defaults in enclosing environment. for _, param := range function.Params { if binary, ok := param.(*syntax.BinaryExpr); ok { r.expr(binary.Y) } } // Enter function block. b := &block{function: function} r.push(b) var seenOptional bool var star *syntax.UnaryExpr // * or *args param var starStar *syntax.Ident // **kwargs ident var numKwonlyParams int for _, param := range function.Params { switch param := param.(type) { case *syntax.Ident: // e.g. x if starStar != nil { r.errorf(param.NamePos, "required parameter may not follow **%s", starStar.Name) } else if star != nil { numKwonlyParams++ } else if seenOptional { r.errorf(param.NamePos, "required parameter may not follow optional") } if r.bind(param) { r.errorf(param.NamePos, "duplicate parameter: %s", param.Name) } case *syntax.BinaryExpr: // e.g. y=dflt if starStar != nil { r.errorf(param.OpPos, "optional parameter may not follow **%s", starStar.Name) } else if star != nil { numKwonlyParams++ } if id := param.X.(*syntax.Ident); r.bind(id) { r.errorf(param.OpPos, "duplicate parameter: %s", id.Name) } seenOptional = true case *syntax.UnaryExpr: // * or *args or **kwargs if param.Op == syntax.STAR { if starStar != nil { r.errorf(param.OpPos, "* parameter may not follow **%s", starStar.Name) } else if star != nil { r.errorf(param.OpPos, "multiple * parameters not allowed") } else { star = param } } else { if starStar != nil { r.errorf(param.OpPos, "multiple ** parameters not allowed") } starStar = param.X.(*syntax.Ident) } } } // Bind the *args and **kwargs parameters at the end, // so that regular parameters a/b/c are contiguous and // there is no hole for the "*": // def f(a, b, *args, c=0, **kwargs) // def f(a, b, *, c=0, **kwargs) if star != nil { if id, _ := star.X.(*syntax.Ident); id != nil { // *args if r.bind(id) { r.errorf(id.NamePos, "duplicate parameter: %s", id.Name) } function.HasVarargs = true } else if numKwonlyParams == 0 { r.errorf(star.OpPos, "bare * must be followed by keyword-only parameters") } } if starStar != nil { if r.bind(starStar) { r.errorf(starStar.NamePos, "duplicate parameter: %s", starStar.Name) } function.HasKwargs = true } function.NumKwonlyParams = numKwonlyParams r.stmts(function.Body) // Resolve all uses of this function's local vars, // and keep just the remaining uses of free/global vars. b.resolveLocalUses() // Leave function block. r.pop() // References within the function body to globals are not // resolved until the end of the module. } func (r *resolver) resolveNonLocalUses(b *block) { // First resolve inner blocks. for _, child := range b.children { r.resolveNonLocalUses(child) } for _, use := range b.uses { use.id.Binding = r.lookupLexical(use, use.env) } } // lookupLocal looks up an identifier within its immediately enclosing function. func lookupLocal(use use) *Binding { for env := use.env; env != nil; env = env.parent { if bind, ok := env.bindings[use.id.Name]; ok { if bind.Scope == Free { // shouldn't exist till later log.Panicf("%s: internal error: %s, %v", use.id.NamePos, use.id.Name, bind) } return bind // found } if env.function != nil { break } } return nil // not found in this function } // lookupLexical looks up an identifier use.id within its lexically enclosing environment. // The use.env field captures the original environment for error reporting. func (r *resolver) lookupLexical(use use, env *block) (bind *Binding) { if debug { fmt.Printf("lookupLexical %s in %s = ...\n", use.id.Name, env) defer func() { fmt.Printf("= %v\n", bind) }() } // Is this the file block? if env == r.file { return r.useToplevel(use) // file-local, global, predeclared, or not found } // Defined in this block? bind, ok := env.bindings[use.id.Name] if !ok { // Defined in parent block? bind = r.lookupLexical(use, env.parent) if env.function != nil && (bind.Scope == Local || bind.Scope == Free || bind.Scope == Cell) { // Found in parent block, which belongs to enclosing function. // Add the parent's binding to the function's freevars, // and add a new 'free' binding to the inner function's block, // and turn the parent's local into cell. if bind.Scope == Local { bind.Scope = Cell } index := len(env.function.FreeVars) env.function.FreeVars = append(env.function.FreeVars, bind) bind = &Binding{ First: bind.First, Scope: Free, Index: index, } if debug { fmt.Printf("creating freevar %v in function at %s: %s\n", len(env.function.FreeVars), env.function.Pos, use.id.Name) } } // Memoize, to avoid duplicate free vars // and redundant global (failing) lookups. env.bind(use.id.Name, bind) } return bind } ================================================ FILE: vendor/go.starlark.net/starlark/debug.go ================================================ package starlark import "go.starlark.net/syntax" // This file defines an experimental API for the debugging tools. // Some of these declarations expose details of internal packages. // (The debugger makes liberal use of exported fields of unexported types.) // Breaking changes may occur without notice. // Local returns the value of the i'th local variable. // It may be nil if not yet assigned. // // Local may be called only for frames whose Callable is a *Function (a // function defined by Starlark source code), and only while the frame // is active; it will panic otherwise. // // This function is provided only for debugging tools. // // THIS API IS EXPERIMENTAL AND MAY CHANGE WITHOUT NOTICE. func (fr *frame) Local(i int) Value { return fr.locals[i] } // DebugFrame is the debugger API for a frame of the interpreter's call stack. // // Most applications have no need for this API; use CallFrame instead. // // Clients must not retain a DebugFrame nor call any of its methods once // the current built-in call has returned or execution has resumed // after a breakpoint as this may have unpredictable effects, including // but not limited to retention of object that would otherwise be garbage. type DebugFrame interface { Callable() Callable // returns the frame's function Local(i int) Value // returns the value of the (Starlark) frame's ith local variable Position() syntax.Position // returns the current position of execution in this frame } // DebugFrame returns the debugger interface for // the specified frame of the interpreter's call stack. // Frame numbering is as for Thread.CallFrame. // // This function is intended for use in debugging tools. // Most applications should have no need for it; use CallFrame instead. func (thread *Thread) DebugFrame(depth int) DebugFrame { return thread.frameAt(depth) } ================================================ FILE: vendor/go.starlark.net/starlark/empty.s ================================================ // The presence of this file allows the package to use the // "go:linkname" hack to call non-exported functions in the // Go runtime, such as hardware-accelerated string hashing. ================================================ FILE: vendor/go.starlark.net/starlark/eval.go ================================================ // Copyright 2017 The Bazel Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package starlark import ( "fmt" "io" "io/ioutil" "log" "math" "math/big" "sort" "strings" "time" "unicode" "unicode/utf8" "go.starlark.net/internal/compile" "go.starlark.net/internal/spell" "go.starlark.net/resolve" "go.starlark.net/syntax" ) // A Thread contains the state of a Starlark thread, // such as its call stack and thread-local storage. // The Thread is threaded throughout the evaluator. type Thread struct { // Name is an optional name that describes the thread, for debugging. Name string // stack is the stack of (internal) call frames. stack []*frame // Print is the client-supplied implementation of the Starlark // 'print' function. If nil, fmt.Fprintln(os.Stderr, msg) is // used instead. Print func(thread *Thread, msg string) // Load is the client-supplied implementation of module loading. // Repeated calls with the same module name must return the same // module environment or error. // The error message need not include the module name. // // See example_test.go for some example implementations of Load. Load func(thread *Thread, module string) (StringDict, error) // locals holds arbitrary "thread-local" Go values belonging to the client. // They are accessible to the client but not to any Starlark program. locals map[string]interface{} // proftime holds the accumulated execution time since the last profile event. proftime time.Duration } // SetLocal sets the thread-local value associated with the specified key. // It must not be called after execution begins. func (thread *Thread) SetLocal(key string, value interface{}) { if thread.locals == nil { thread.locals = make(map[string]interface{}) } thread.locals[key] = value } // Local returns the thread-local value associated with the specified key. func (thread *Thread) Local(key string) interface{} { return thread.locals[key] } // CallFrame returns a copy of the specified frame of the callstack. // It should only be used in built-ins called from Starlark code. // Depth 0 means the frame of the built-in itself, 1 is its caller, and so on. // // It is equivalent to CallStack().At(depth), but more efficient. func (thread *Thread) CallFrame(depth int) CallFrame { return thread.frameAt(depth).asCallFrame() } func (thread *Thread) frameAt(depth int) *frame { return thread.stack[len(thread.stack)-1-depth] } // CallStack returns a new slice containing the thread's stack of call frames. func (thread *Thread) CallStack() CallStack { frames := make([]CallFrame, len(thread.stack)) for i, fr := range thread.stack { frames[i] = fr.asCallFrame() } return frames } // CallStackDepth returns the number of frames in the current call stack. func (thread *Thread) CallStackDepth() int { return len(thread.stack) } // A StringDict is a mapping from names to values, and represents // an environment such as the global variables of a module. // It is not a true starlark.Value. type StringDict map[string]Value // Keys returns a new sorted slice of d's keys. func (d StringDict) Keys() []string { names := make([]string, 0, len(d)) for name := range d { names = append(names, name) } sort.Strings(names) return names } func (d StringDict) String() string { buf := new(strings.Builder) buf.WriteByte('{') sep := "" for _, name := range d.Keys() { buf.WriteString(sep) buf.WriteString(name) buf.WriteString(": ") writeValue(buf, d[name], nil) sep = ", " } buf.WriteByte('}') return buf.String() } func (d StringDict) Freeze() { for _, v := range d { v.Freeze() } } // Has reports whether the dictionary contains the specified key. func (d StringDict) Has(key string) bool { _, ok := d[key]; return ok } // A frame records a call to a Starlark function (including module toplevel) // or a built-in function or method. type frame struct { callable Callable // current function (or toplevel) or built-in pc uint32 // program counter (Starlark frames only) locals []Value // local variables (Starlark frames only) spanStart int64 // start time of current profiler span } // Position returns the source position of the current point of execution in this frame. func (fr *frame) Position() syntax.Position { switch c := fr.callable.(type) { case *Function: // Starlark function return c.funcode.Position(fr.pc) case callableWithPosition: // If a built-in Callable defines // a Position method, use it. return c.Position() } return syntax.MakePosition(&builtinFilename, 0, 0) } var builtinFilename = "" // Function returns the frame's function or built-in. func (fr *frame) Callable() Callable { return fr.callable } // A CallStack is a stack of call frames, outermost first. type CallStack []CallFrame // At returns a copy of the frame at depth i. // At(0) returns the topmost frame. func (stack CallStack) At(i int) CallFrame { return stack[len(stack)-1-i] } // Pop removes and returns the topmost frame. func (stack *CallStack) Pop() CallFrame { last := len(*stack) - 1 top := (*stack)[last] *stack = (*stack)[:last] return top } // String returns a user-friendly description of the stack. func (stack CallStack) String() string { out := new(strings.Builder) fmt.Fprintf(out, "Traceback (most recent call last):\n") for _, fr := range stack { fmt.Fprintf(out, " %s: in %s\n", fr.Pos, fr.Name) } return out.String() } // An EvalError is a Starlark evaluation error and // a copy of the thread's stack at the moment of the error. type EvalError struct { Msg string CallStack CallStack cause error } // A CallFrame represents the function name and current // position of execution of an enclosing call frame. type CallFrame struct { Name string Pos syntax.Position } func (fr *frame) asCallFrame() CallFrame { return CallFrame{ Name: fr.Callable().Name(), Pos: fr.Position(), } } func (thread *Thread) evalError(err error) *EvalError { return &EvalError{ Msg: err.Error(), CallStack: thread.CallStack(), cause: err, } } func (e *EvalError) Error() string { return e.Msg } // Backtrace returns a user-friendly error message describing the stack // of calls that led to this error. func (e *EvalError) Backtrace() string { return fmt.Sprintf("%sError: %s", e.CallStack, e.Msg) } func (e *EvalError) Unwrap() error { return e.cause } // A Program is a compiled Starlark program. // // Programs are immutable, and contain no Values. // A Program may be created by parsing a source file (see SourceProgram) // or by loading a previously saved compiled program (see CompiledProgram). type Program struct { compiled *compile.Program } // CompilerVersion is the version number of the protocol for compiled // files. Applications must not run programs compiled by one version // with an interpreter at another version, and should thus incorporate // the compiler version into the cache key when reusing compiled code. const CompilerVersion = compile.Version // Filename returns the name of the file from which this program was loaded. func (prog *Program) Filename() string { return prog.compiled.Toplevel.Pos.Filename() } func (prog *Program) String() string { return prog.Filename() } // NumLoads returns the number of load statements in the compiled program. func (prog *Program) NumLoads() int { return len(prog.compiled.Loads) } // Load(i) returns the name and position of the i'th module directly // loaded by this one, where 0 <= i < NumLoads(). // The name is unresolved---exactly as it appears in the source. func (prog *Program) Load(i int) (string, syntax.Position) { id := prog.compiled.Loads[i] return id.Name, id.Pos } // WriteTo writes the compiled module to the specified output stream. func (prog *Program) Write(out io.Writer) error { data := prog.compiled.Encode() _, err := out.Write(data) return err } // ExecFile parses, resolves, and executes a Starlark file in the // specified global environment, which may be modified during execution. // // Thread is the state associated with the Starlark thread. // // The filename and src parameters are as for syntax.Parse: // filename is the name of the file to execute, // and the name that appears in error messages; // src is an optional source of bytes to use // instead of filename. // // predeclared defines the predeclared names specific to this module. // Execution does not modify this dictionary, though it may mutate // its values. // // If ExecFile fails during evaluation, it returns an *EvalError // containing a backtrace. func ExecFile(thread *Thread, filename string, src interface{}, predeclared StringDict) (StringDict, error) { // Parse, resolve, and compile a Starlark source file. _, mod, err := SourceProgram(filename, src, predeclared.Has) if err != nil { return nil, err } g, err := mod.Init(thread, predeclared) g.Freeze() return g, err } // SourceProgram produces a new program by parsing, resolving, // and compiling a Starlark source file. // On success, it returns the parsed file and the compiled program. // The filename and src parameters are as for syntax.Parse. // // The isPredeclared predicate reports whether a name is // a pre-declared identifier of the current module. // Its typical value is predeclared.Has, // where predeclared is a StringDict of pre-declared values. func SourceProgram(filename string, src interface{}, isPredeclared func(string) bool) (*syntax.File, *Program, error) { f, err := syntax.Parse(filename, src, 0) if err != nil { return nil, nil, err } prog, err := FileProgram(f, isPredeclared) return f, prog, err } // FileProgram produces a new program by resolving, // and compiling the Starlark source file syntax tree. // On success, it returns the compiled program. // // Resolving a syntax tree mutates it. // Do not call FileProgram more than once on the same file. // // The isPredeclared predicate reports whether a name is // a pre-declared identifier of the current module. // Its typical value is predeclared.Has, // where predeclared is a StringDict of pre-declared values. func FileProgram(f *syntax.File, isPredeclared func(string) bool) (*Program, error) { if err := resolve.File(f, isPredeclared, Universe.Has); err != nil { return nil, err } var pos syntax.Position if len(f.Stmts) > 0 { pos = syntax.Start(f.Stmts[0]) } else { pos = syntax.MakePosition(&f.Path, 1, 1) } module := f.Module.(*resolve.Module) compiled := compile.File(f.Stmts, pos, "", module.Locals, module.Globals) return &Program{compiled}, nil } // CompiledProgram produces a new program from the representation // of a compiled program previously saved by Program.Write. func CompiledProgram(in io.Reader) (*Program, error) { data, err := ioutil.ReadAll(in) if err != nil { return nil, err } compiled, err := compile.DecodeProgram(data) if err != nil { return nil, err } return &Program{compiled}, nil } // Init creates a set of global variables for the program, // executes the toplevel code of the specified program, // and returns a new, unfrozen dictionary of the globals. func (prog *Program) Init(thread *Thread, predeclared StringDict) (StringDict, error) { toplevel := makeToplevelFunction(prog.compiled, predeclared) _, err := Call(thread, toplevel, nil, nil) // Convert the global environment to a map. // We return a (partial) map even in case of error. return toplevel.Globals(), err } // ExecREPLChunk compiles and executes file f in the specified thread // and global environment. This is a variant of ExecFile specialized to // the needs of a REPL, in which a sequence of input chunks, each // syntactically a File, manipulates the same set of module globals, // which are not frozen after execution. // // This function is intended to support only go.starlark.net/repl. // Its API stability is not guaranteed. func ExecREPLChunk(f *syntax.File, thread *Thread, globals StringDict) error { var predeclared StringDict // -- variant of FileProgram -- if err := resolve.REPLChunk(f, globals.Has, predeclared.Has, Universe.Has); err != nil { return err } var pos syntax.Position if len(f.Stmts) > 0 { pos = syntax.Start(f.Stmts[0]) } else { pos = syntax.MakePosition(&f.Path, 1, 1) } module := f.Module.(*resolve.Module) compiled := compile.File(f.Stmts, pos, "", module.Locals, module.Globals) prog := &Program{compiled} // -- variant of Program.Init -- toplevel := makeToplevelFunction(prog.compiled, predeclared) // Initialize module globals from parameter. for i, id := range prog.compiled.Globals { if v := globals[id.Name]; v != nil { toplevel.module.globals[i] = v } } _, err := Call(thread, toplevel, nil, nil) // Reflect changes to globals back to parameter, even after an error. for i, id := range prog.compiled.Globals { if v := toplevel.module.globals[i]; v != nil { globals[id.Name] = v } } return err } func makeToplevelFunction(prog *compile.Program, predeclared StringDict) *Function { // Create the Starlark value denoted by each program constant c. constants := make([]Value, len(prog.Constants)) for i, c := range prog.Constants { var v Value switch c := c.(type) { case int64: v = MakeInt64(c) case *big.Int: v = MakeBigInt(c) case string: v = String(c) case float64: v = Float(c) default: log.Panicf("unexpected constant %T: %v", c, c) } constants[i] = v } return &Function{ funcode: prog.Toplevel, module: &module{ program: prog, predeclared: predeclared, globals: make([]Value, len(prog.Globals)), constants: constants, }, } } // Eval parses, resolves, and evaluates an expression within the // specified (predeclared) environment. // // Evaluation cannot mutate the environment dictionary itself, // though it may modify variables reachable from the dictionary. // // The filename and src parameters are as for syntax.Parse. // // If Eval fails during evaluation, it returns an *EvalError // containing a backtrace. func Eval(thread *Thread, filename string, src interface{}, env StringDict) (Value, error) { expr, err := syntax.ParseExpr(filename, src, 0) if err != nil { return nil, err } f, err := makeExprFunc(expr, env) if err != nil { return nil, err } return Call(thread, f, nil, nil) } // EvalExpr resolves and evaluates an expression within the // specified (predeclared) environment. // Evaluating a comma-separated list of expressions yields a tuple value. // // Resolving an expression mutates it. // Do not call EvalExpr more than once for the same expression. // // Evaluation cannot mutate the environment dictionary itself, // though it may modify variables reachable from the dictionary. // // If Eval fails during evaluation, it returns an *EvalError // containing a backtrace. func EvalExpr(thread *Thread, expr syntax.Expr, env StringDict) (Value, error) { fn, err := makeExprFunc(expr, env) if err != nil { return nil, err } return Call(thread, fn, nil, nil) } // ExprFunc returns a no-argument function // that evaluates the expression whose source is src. func ExprFunc(filename string, src interface{}, env StringDict) (*Function, error) { expr, err := syntax.ParseExpr(filename, src, 0) if err != nil { return nil, err } return makeExprFunc(expr, env) } // makeExprFunc returns a no-argument function whose body is expr. func makeExprFunc(expr syntax.Expr, env StringDict) (*Function, error) { locals, err := resolve.Expr(expr, env.Has, Universe.Has) if err != nil { return nil, err } return makeToplevelFunction(compile.Expr(expr, "", locals), env), nil } // The following functions are primitive operations of the byte code interpreter. // list += iterable func listExtend(x *List, y Iterable) { if ylist, ok := y.(*List); ok { // fast path: list += list x.elems = append(x.elems, ylist.elems...) } else { iter := y.Iterate() defer iter.Done() var z Value for iter.Next(&z) { x.elems = append(x.elems, z) } } } // getAttr implements x.dot. func getAttr(x Value, name string) (Value, error) { hasAttr, ok := x.(HasAttrs) if !ok { return nil, fmt.Errorf("%s has no .%s field or method", x.Type(), name) } var errmsg string v, err := hasAttr.Attr(name) if err == nil { if v != nil { return v, nil // success } // (nil, nil) => generic error errmsg = fmt.Sprintf("%s has no .%s field or method", x.Type(), name) } else if nsa, ok := err.(NoSuchAttrError); ok { errmsg = string(nsa) } else { return nil, err // return error as is } // add spelling hint if n := spell.Nearest(name, hasAttr.AttrNames()); n != "" { errmsg = fmt.Sprintf("%s (did you mean .%s?)", errmsg, n) } return nil, fmt.Errorf("%s", errmsg) } // setField implements x.name = y. func setField(x Value, name string, y Value) error { if x, ok := x.(HasSetField); ok { err := x.SetField(name, y) if _, ok := err.(NoSuchAttrError); ok { // No such field: check spelling. if n := spell.Nearest(name, x.AttrNames()); n != "" { err = fmt.Errorf("%s (did you mean .%s?)", err, n) } } return err } return fmt.Errorf("can't assign to .%s field of %s", name, x.Type()) } // getIndex implements x[y]. func getIndex(x, y Value) (Value, error) { switch x := x.(type) { case Mapping: // dict z, found, err := x.Get(y) if err != nil { return nil, err } if !found { return nil, fmt.Errorf("key %v not in %s", y, x.Type()) } return z, nil case Indexable: // string, list, tuple n := x.Len() i, err := AsInt32(y) if err != nil { return nil, fmt.Errorf("%s index: %s", x.Type(), err) } origI := i if i < 0 { i += n } if i < 0 || i >= n { return nil, outOfRange(origI, n, x) } return x.Index(i), nil } return nil, fmt.Errorf("unhandled index operation %s[%s]", x.Type(), y.Type()) } func outOfRange(i, n int, x Value) error { if n == 0 { return fmt.Errorf("index %d out of range: empty %s", i, x.Type()) } else { return fmt.Errorf("%s index %d out of range [%d:%d]", x.Type(), i, -n, n-1) } } // setIndex implements x[y] = z. func setIndex(x, y, z Value) error { switch x := x.(type) { case HasSetKey: if err := x.SetKey(y, z); err != nil { return err } case HasSetIndex: n := x.Len() i, err := AsInt32(y) if err != nil { return err } origI := i if i < 0 { i += n } if i < 0 || i >= n { return outOfRange(origI, n, x) } return x.SetIndex(i, z) default: return fmt.Errorf("%s value does not support item assignment", x.Type()) } return nil } // Unary applies a unary operator (+, -, ~, not) to its operand. func Unary(op syntax.Token, x Value) (Value, error) { // The NOT operator is not customizable. if op == syntax.NOT { return !x.Truth(), nil } // Int, Float, and user-defined types if x, ok := x.(HasUnary); ok { // (nil, nil) => unhandled y, err := x.Unary(op) if y != nil || err != nil { return y, err } } return nil, fmt.Errorf("unknown unary op: %s %s", op, x.Type()) } // Binary applies a strict binary operator (not AND or OR) to its operands. // For equality tests or ordered comparisons, use Compare instead. func Binary(op syntax.Token, x, y Value) (Value, error) { switch op { case syntax.PLUS: switch x := x.(type) { case String: if y, ok := y.(String); ok { return x + y, nil } case Int: switch y := y.(type) { case Int: return x.Add(y), nil case Float: return x.Float() + y, nil } case Float: switch y := y.(type) { case Float: return x + y, nil case Int: return x + y.Float(), nil } case *List: if y, ok := y.(*List); ok { z := make([]Value, 0, x.Len()+y.Len()) z = append(z, x.elems...) z = append(z, y.elems...) return NewList(z), nil } case Tuple: if y, ok := y.(Tuple); ok { z := make(Tuple, 0, len(x)+len(y)) z = append(z, x...) z = append(z, y...) return z, nil } } case syntax.MINUS: switch x := x.(type) { case Int: switch y := y.(type) { case Int: return x.Sub(y), nil case Float: return x.Float() - y, nil } case Float: switch y := y.(type) { case Float: return x - y, nil case Int: return x - y.Float(), nil } } case syntax.STAR: switch x := x.(type) { case Int: switch y := y.(type) { case Int: return x.Mul(y), nil case Float: return x.Float() * y, nil case String: return stringRepeat(y, x) case *List: elems, err := tupleRepeat(Tuple(y.elems), x) if err != nil { return nil, err } return NewList(elems), nil case Tuple: return tupleRepeat(y, x) } case Float: switch y := y.(type) { case Float: return x * y, nil case Int: return x * y.Float(), nil } case String: if y, ok := y.(Int); ok { return stringRepeat(x, y) } case *List: if y, ok := y.(Int); ok { elems, err := tupleRepeat(Tuple(x.elems), y) if err != nil { return nil, err } return NewList(elems), nil } case Tuple: if y, ok := y.(Int); ok { return tupleRepeat(x, y) } } case syntax.SLASH: switch x := x.(type) { case Int: switch y := y.(type) { case Int: yf := y.Float() if yf == 0.0 { return nil, fmt.Errorf("real division by zero") } return x.Float() / yf, nil case Float: if y == 0.0 { return nil, fmt.Errorf("real division by zero") } return x.Float() / y, nil } case Float: switch y := y.(type) { case Float: if y == 0.0 { return nil, fmt.Errorf("real division by zero") } return x / y, nil case Int: yf := y.Float() if yf == 0.0 { return nil, fmt.Errorf("real division by zero") } return x / yf, nil } } case syntax.SLASHSLASH: switch x := x.(type) { case Int: switch y := y.(type) { case Int: if y.Sign() == 0 { return nil, fmt.Errorf("floored division by zero") } return x.Div(y), nil case Float: if y == 0.0 { return nil, fmt.Errorf("floored division by zero") } return floor((x.Float() / y)), nil } case Float: switch y := y.(type) { case Float: if y == 0.0 { return nil, fmt.Errorf("floored division by zero") } return floor(x / y), nil case Int: yf := y.Float() if yf == 0.0 { return nil, fmt.Errorf("floored division by zero") } return floor(x / yf), nil } } case syntax.PERCENT: switch x := x.(type) { case Int: switch y := y.(type) { case Int: if y.Sign() == 0 { return nil, fmt.Errorf("integer modulo by zero") } return x.Mod(y), nil case Float: if y == 0 { return nil, fmt.Errorf("float modulo by zero") } return x.Float().Mod(y), nil } case Float: switch y := y.(type) { case Float: if y == 0.0 { return nil, fmt.Errorf("float modulo by zero") } return Float(math.Mod(float64(x), float64(y))), nil case Int: if y.Sign() == 0 { return nil, fmt.Errorf("float modulo by zero") } return x.Mod(y.Float()), nil } case String: return interpolate(string(x), y) } case syntax.NOT_IN: z, err := Binary(syntax.IN, x, y) if err != nil { return nil, err } return !z.Truth(), nil case syntax.IN: switch y := y.(type) { case *List: for _, elem := range y.elems { if eq, err := Equal(elem, x); err != nil { return nil, err } else if eq { return True, nil } } return False, nil case Tuple: for _, elem := range y { if eq, err := Equal(elem, x); err != nil { return nil, err } else if eq { return True, nil } } return False, nil case Mapping: // e.g. dict // Ignore error from Get as we cannot distinguish true // errors (value cycle, type error) from "key not found". _, found, _ := y.Get(x) return Bool(found), nil case *Set: ok, err := y.Has(x) return Bool(ok), err case String: needle, ok := x.(String) if !ok { return nil, fmt.Errorf("'in ' requires string as left operand, not %s", x.Type()) } return Bool(strings.Contains(string(y), string(needle))), nil case rangeValue: i, err := NumberToInt(x) if err != nil { return nil, fmt.Errorf("'in ' requires integer as left operand, not %s", x.Type()) } return Bool(y.contains(i)), nil } case syntax.PIPE: switch x := x.(type) { case Int: if y, ok := y.(Int); ok { return x.Or(y), nil } case *Set: // union if y, ok := y.(*Set); ok { iter := Iterate(y) defer iter.Done() return x.Union(iter) } } case syntax.AMP: switch x := x.(type) { case Int: if y, ok := y.(Int); ok { return x.And(y), nil } case *Set: // intersection if y, ok := y.(*Set); ok { set := new(Set) if x.Len() > y.Len() { x, y = y, x // opt: range over smaller set } for _, xelem := range x.elems() { // Has, Insert cannot fail here. if found, _ := y.Has(xelem); found { set.Insert(xelem) } } return set, nil } } case syntax.CIRCUMFLEX: switch x := x.(type) { case Int: if y, ok := y.(Int); ok { return x.Xor(y), nil } case *Set: // symmetric difference if y, ok := y.(*Set); ok { set := new(Set) for _, xelem := range x.elems() { if found, _ := y.Has(xelem); !found { set.Insert(xelem) } } for _, yelem := range y.elems() { if found, _ := x.Has(yelem); !found { set.Insert(yelem) } } return set, nil } } case syntax.LTLT, syntax.GTGT: if x, ok := x.(Int); ok { y, err := AsInt32(y) if err != nil { return nil, err } if y < 0 { return nil, fmt.Errorf("negative shift count: %v", y) } if op == syntax.LTLT { if y >= 512 { return nil, fmt.Errorf("shift count too large: %v", y) } return x.Lsh(uint(y)), nil } else { return x.Rsh(uint(y)), nil } } default: // unknown operator goto unknown } // user-defined types // (nil, nil) => unhandled if x, ok := x.(HasBinary); ok { z, err := x.Binary(op, y, Left) if z != nil || err != nil { return z, err } } if y, ok := y.(HasBinary); ok { z, err := y.Binary(op, x, Right) if z != nil || err != nil { return z, err } } // unsupported operand types unknown: return nil, fmt.Errorf("unknown binary op: %s %s %s", x.Type(), op, y.Type()) } // It's always possible to overeat in small bites but we'll // try to stop someone swallowing the world in one gulp. const maxAlloc = 1 << 30 func tupleRepeat(elems Tuple, n Int) (Tuple, error) { if len(elems) == 0 { return nil, nil } i, err := AsInt32(n) if err != nil { return nil, fmt.Errorf("repeat count %s too large", n) } if i < 1 { return nil, nil } // Inv: i > 0, len > 0 sz := len(elems) * i if sz < 0 || sz >= maxAlloc { // sz < 0 => overflow return nil, fmt.Errorf("excessive repeat (%d elements)", sz) } res := make([]Value, sz) // copy elems into res, doubling each time x := copy(res, elems) for x < len(res) { copy(res[x:], res[:x]) x *= 2 } return res, nil } func stringRepeat(s String, n Int) (String, error) { if s == "" { return "", nil } i, err := AsInt32(n) if err != nil { return "", fmt.Errorf("repeat count %s too large", n) } if i < 1 { return "", nil } // Inv: i > 0, len > 0 sz := len(s) * i if sz < 0 || sz >= maxAlloc { // sz < 0 => overflow return "", fmt.Errorf("excessive repeat (%d elements)", sz) } return String(strings.Repeat(string(s), i)), nil } // Call calls the function fn with the specified positional and keyword arguments. func Call(thread *Thread, fn Value, args Tuple, kwargs []Tuple) (Value, error) { c, ok := fn.(Callable) if !ok { return nil, fmt.Errorf("invalid call of non-function (%s)", fn.Type()) } // Allocate and push a new frame. var fr *frame // Optimization: use slack portion of thread.stack // slice as a freelist of empty frames. if n := len(thread.stack); n < cap(thread.stack) { fr = thread.stack[n : n+1][0] } if fr == nil { fr = new(frame) } thread.stack = append(thread.stack, fr) // push fr.callable = c thread.beginProfSpan() result, err := c.CallInternal(thread, args, kwargs) thread.endProfSpan() // Sanity check: nil is not a valid Starlark value. if result == nil && err == nil { err = fmt.Errorf("internal error: nil (not None) returned from %s", fn) } // Always return an EvalError with an accurate frame. if err != nil { if _, ok := err.(*EvalError); !ok { err = thread.evalError(err) } } *fr = frame{} // clear out any references thread.stack = thread.stack[:len(thread.stack)-1] // pop return result, err } func slice(x, lo, hi, step_ Value) (Value, error) { sliceable, ok := x.(Sliceable) if !ok { return nil, fmt.Errorf("invalid slice operand %s", x.Type()) } n := sliceable.Len() step := 1 if step_ != None { var err error step, err = AsInt32(step_) if err != nil { return nil, fmt.Errorf("got %s for slice step, want int", step_.Type()) } if step == 0 { return nil, fmt.Errorf("zero is not a valid slice step") } } // TODO(adonovan): opt: preallocate result array. var start, end int if step > 0 { // positive stride // default indices are [0:n]. var err error start, end, err = indices(lo, hi, n) if err != nil { return nil, err } if end < start { end = start // => empty result } } else { // negative stride // default indices are effectively [n-1:-1], though to // get this effect using explicit indices requires // [n-1:-1-n:-1] because of the treatment of -ve values. start = n - 1 if err := asIndex(lo, n, &start); err != nil { return nil, fmt.Errorf("invalid start index: %s", err) } if start >= n { start = n - 1 } end = -1 if err := asIndex(hi, n, &end); err != nil { return nil, fmt.Errorf("invalid end index: %s", err) } if end < -1 { end = -1 } if start < end { start = end // => empty result } } return sliceable.Slice(start, end, step), nil } // From Hacker's Delight, section 2.8. func signum64(x int64) int { return int(uint64(x>>63) | uint64(-x)>>63) } func signum(x int) int { return signum64(int64(x)) } // indices converts start_ and end_ to indices in the range [0:len]. // The start index defaults to 0 and the end index defaults to len. // An index -len < i < 0 is treated like i+len. // All other indices outside the range are clamped to the nearest value in the range. // Beware: start may be greater than end. // This function is suitable only for slices with positive strides. func indices(start_, end_ Value, len int) (start, end int, err error) { start = 0 if err := asIndex(start_, len, &start); err != nil { return 0, 0, fmt.Errorf("invalid start index: %s", err) } // Clamp to [0:len]. if start < 0 { start = 0 } else if start > len { start = len } end = len if err := asIndex(end_, len, &end); err != nil { return 0, 0, fmt.Errorf("invalid end index: %s", err) } // Clamp to [0:len]. if end < 0 { end = 0 } else if end > len { end = len } return start, end, nil } // asIndex sets *result to the integer value of v, adding len to it // if it is negative. If v is nil or None, *result is unchanged. func asIndex(v Value, len int, result *int) error { if v != nil && v != None { var err error *result, err = AsInt32(v) if err != nil { return fmt.Errorf("got %s, want int", v.Type()) } if *result < 0 { *result += len } } return nil } // setArgs sets the values of the formal parameters of function fn in // based on the actual parameter values in args and kwargs. func setArgs(locals []Value, fn *Function, args Tuple, kwargs []Tuple) error { // This is the general schema of a function: // // def f(p1, p2=dp2, p3=dp3, *args, k1, k2=dk2, k3, **kwargs) // // The p parameters are non-kwonly, and may be specified positionally. // The k parameters are kwonly, and must be specified by name. // The defaults tuple is (dp2, dp3, mandatory, dk2, mandatory). // // Arguments are processed as follows: // - positional arguments are bound to a prefix of [p1, p2, p3]. // - surplus positional arguments are bound to *args. // - keyword arguments are bound to any of {p1, p2, p3, k1, k2, k3}; // duplicate bindings are rejected. // - surplus keyword arguments are bound to **kwargs. // - defaults are bound to each parameter from p2 to k3 if no value was set. // default values come from the tuple above. // It is an error if the tuple entry for an unset parameter is 'mandatory'. // Nullary function? if fn.NumParams() == 0 { if nactual := len(args) + len(kwargs); nactual > 0 { return fmt.Errorf("function %s accepts no arguments (%d given)", fn.Name(), nactual) } return nil } cond := func(x bool, y, z interface{}) interface{} { if x { return y } return z } // nparams is the number of ordinary parameters (sans *args and **kwargs). nparams := fn.NumParams() var kwdict *Dict if fn.HasKwargs() { nparams-- kwdict = new(Dict) locals[nparams] = kwdict } if fn.HasVarargs() { nparams-- } // nonkwonly is the number of non-kwonly parameters. nonkwonly := nparams - fn.NumKwonlyParams() // Too many positional args? n := len(args) if len(args) > nonkwonly { if !fn.HasVarargs() { return fmt.Errorf("function %s accepts %s%d positional argument%s (%d given)", fn.Name(), cond(len(fn.defaults) > fn.NumKwonlyParams(), "at most ", ""), nonkwonly, cond(nonkwonly == 1, "", "s"), len(args)) } n = nonkwonly } // Bind positional arguments to non-kwonly parameters. for i := 0; i < n; i++ { locals[i] = args[i] } // Bind surplus positional arguments to *args parameter. if fn.HasVarargs() { tuple := make(Tuple, len(args)-n) for i := n; i < len(args); i++ { tuple[i-n] = args[i] } locals[nparams] = tuple } // Bind keyword arguments to parameters. paramIdents := fn.funcode.Locals[:nparams] for _, pair := range kwargs { k, v := pair[0].(String), pair[1] if i := findParam(paramIdents, string(k)); i >= 0 { if locals[i] != nil { return fmt.Errorf("function %s got multiple values for parameter %s", fn.Name(), k) } locals[i] = v continue } if kwdict == nil { return fmt.Errorf("function %s got an unexpected keyword argument %s", fn.Name(), k) } oldlen := kwdict.Len() kwdict.SetKey(k, v) if kwdict.Len() == oldlen { return fmt.Errorf("function %s got multiple values for parameter %s", fn.Name(), k) } } // Are defaults required? if n < nparams || fn.NumKwonlyParams() > 0 { m := nparams - len(fn.defaults) // first default // Report errors for missing required arguments. var missing []string var i int for i = n; i < m; i++ { if locals[i] == nil { missing = append(missing, paramIdents[i].Name) } } // Bind default values to parameters. for ; i < nparams; i++ { if locals[i] == nil { dflt := fn.defaults[i-m] if _, ok := dflt.(mandatory); ok { missing = append(missing, paramIdents[i].Name) continue } locals[i] = dflt } } if missing != nil { return fmt.Errorf("function %s missing %d argument%s (%s)", fn.Name(), len(missing), cond(len(missing) > 1, "s", ""), strings.Join(missing, ", ")) } } return nil } func findParam(params []compile.Binding, name string) int { for i, param := range params { if param.Name == name { return i } } return -1 } // https://github.com/google/starlark-go/blob/master/doc/spec.md#string-interpolation func interpolate(format string, x Value) (Value, error) { buf := new(strings.Builder) index := 0 nargs := 1 if tuple, ok := x.(Tuple); ok { nargs = len(tuple) } for { i := strings.IndexByte(format, '%') if i < 0 { buf.WriteString(format) break } buf.WriteString(format[:i]) format = format[i+1:] if format != "" && format[0] == '%' { buf.WriteByte('%') format = format[1:] continue } var arg Value if format != "" && format[0] == '(' { // keyword argument: %(name)s. format = format[1:] j := strings.IndexByte(format, ')') if j < 0 { return nil, fmt.Errorf("incomplete format key") } key := format[:j] if dict, ok := x.(Mapping); !ok { return nil, fmt.Errorf("format requires a mapping") } else if v, found, _ := dict.Get(String(key)); found { arg = v } else { return nil, fmt.Errorf("key not found: %s", key) } format = format[j+1:] } else { // positional argument: %s. if index >= nargs { return nil, fmt.Errorf("not enough arguments for format string") } if tuple, ok := x.(Tuple); ok { arg = tuple[index] } else { arg = x } } // NOTE: Starlark does not support any of these optional Python features: // - optional conversion flags: [#0- +], etc. // - optional minimum field width (number or *). // - optional precision (.123 or *) // - optional length modifier // conversion type if format == "" { return nil, fmt.Errorf("incomplete format") } switch c := format[0]; c { case 's', 'r': if str, ok := AsString(arg); ok && c == 's' { buf.WriteString(str) } else { writeValue(buf, arg, nil) } case 'd', 'i', 'o', 'x', 'X': i, err := NumberToInt(arg) if err != nil { return nil, fmt.Errorf("%%%c format requires integer: %v", c, err) } switch c { case 'd', 'i': fmt.Fprintf(buf, "%d", i) case 'o': fmt.Fprintf(buf, "%o", i) case 'x': fmt.Fprintf(buf, "%x", i) case 'X': fmt.Fprintf(buf, "%X", i) } case 'e', 'f', 'g', 'E', 'F', 'G': f, ok := AsFloat(arg) if !ok { return nil, fmt.Errorf("%%%c format requires float, not %s", c, arg.Type()) } switch c { case 'e': fmt.Fprintf(buf, "%e", f) case 'f': fmt.Fprintf(buf, "%f", f) case 'g': fmt.Fprintf(buf, "%g", f) case 'E': fmt.Fprintf(buf, "%E", f) case 'F': fmt.Fprintf(buf, "%F", f) case 'G': fmt.Fprintf(buf, "%G", f) } case 'c': switch arg := arg.(type) { case Int: // chr(int) r, err := AsInt32(arg) if err != nil || r < 0 || r > unicode.MaxRune { return nil, fmt.Errorf("%%c format requires a valid Unicode code point, got %s", arg) } buf.WriteRune(rune(r)) case String: r, size := utf8.DecodeRuneInString(string(arg)) if size != len(arg) || len(arg) == 0 { return nil, fmt.Errorf("%%c format requires a single-character string") } buf.WriteRune(r) default: return nil, fmt.Errorf("%%c format requires int or single-character string, not %s", arg.Type()) } case '%': buf.WriteByte('%') default: return nil, fmt.Errorf("unknown conversion %%%c", c) } format = format[1:] index++ } if index < nargs { return nil, fmt.Errorf("too many arguments for format string") } return String(buf.String()), nil } ================================================ FILE: vendor/go.starlark.net/starlark/hashtable.go ================================================ // Copyright 2017 The Bazel Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package starlark import ( "fmt" _ "unsafe" // for go:linkname hack ) // hashtable is used to represent Starlark dict and set values. // It is a hash table whose key/value entries form a doubly-linked list // in the order the entries were inserted. type hashtable struct { table []bucket // len is zero or a power of two bucket0 [1]bucket // inline allocation for small maps. len uint32 itercount uint32 // number of active iterators (ignored if frozen) head *entry // insertion order doubly-linked list; may be nil tailLink **entry // address of nil link at end of list (perhaps &head) frozen bool } const bucketSize = 8 type bucket struct { entries [bucketSize]entry next *bucket // linked list of buckets } type entry struct { hash uint32 // nonzero => in use key, value Value next *entry // insertion order doubly-linked list; may be nil prevLink **entry // address of link to this entry (perhaps &head) } func (ht *hashtable) init(size int) { if size < 0 { panic("size < 0") } nb := 1 for overloaded(size, nb) { nb = nb << 1 } if nb < 2 { ht.table = ht.bucket0[:1] } else { ht.table = make([]bucket, nb) } ht.tailLink = &ht.head } func (ht *hashtable) freeze() { if !ht.frozen { ht.frozen = true for i := range ht.table { for p := &ht.table[i]; p != nil; p = p.next { for i := range p.entries { e := &p.entries[i] if e.hash != 0 { e.key.Freeze() e.value.Freeze() } } } } } } func (ht *hashtable) insert(k, v Value) error { if ht.frozen { return fmt.Errorf("cannot insert into frozen hash table") } if ht.itercount > 0 { return fmt.Errorf("cannot insert into hash table during iteration") } if ht.table == nil { ht.init(1) } h, err := k.Hash() if err != nil { return err } if h == 0 { h = 1 // zero is reserved } retry: var insert *entry // Inspect each bucket in the bucket list. p := &ht.table[h&(uint32(len(ht.table)-1))] for { for i := range p.entries { e := &p.entries[i] if e.hash != h { if e.hash == 0 { // Found empty entry; make a note. insert = e } continue } if eq, err := Equal(k, e.key); err != nil { return err // e.g. excessively recursive tuple } else if !eq { continue } // Key already present; update value. e.value = v return nil } if p.next == nil { break } p = p.next } // Key not found. p points to the last bucket. // Does the number of elements exceed the buckets' load factor? if overloaded(int(ht.len), len(ht.table)) { ht.grow() goto retry } if insert == nil { // No space in existing buckets. Add a new one to the bucket list. b := new(bucket) p.next = b insert = &b.entries[0] } // Insert key/value pair. insert.hash = h insert.key = k insert.value = v // Append entry to doubly-linked list. insert.prevLink = ht.tailLink *ht.tailLink = insert ht.tailLink = &insert.next ht.len++ return nil } func overloaded(elems, buckets int) bool { const loadFactor = 6.5 // just a guess return elems >= bucketSize && float64(elems) >= loadFactor*float64(buckets) } func (ht *hashtable) grow() { // Double the number of buckets and rehash. // TODO(adonovan): opt: // - avoid reentrant calls to ht.insert, and specialize it. // e.g. we know the calls to Equals will return false since // there are no duplicates among the old keys. // - saving the entire hash in the bucket would avoid the need to // recompute the hash. // - save the old buckets on a free list. ht.table = make([]bucket, len(ht.table)<<1) oldhead := ht.head ht.head = nil ht.tailLink = &ht.head ht.len = 0 for e := oldhead; e != nil; e = e.next { ht.insert(e.key, e.value) } ht.bucket0[0] = bucket{} // clear out unused initial bucket } func (ht *hashtable) lookup(k Value) (v Value, found bool, err error) { h, err := k.Hash() if err != nil { return nil, false, err // unhashable } if h == 0 { h = 1 // zero is reserved } if ht.table == nil { return None, false, nil // empty } // Inspect each bucket in the bucket list. for p := &ht.table[h&(uint32(len(ht.table)-1))]; p != nil; p = p.next { for i := range p.entries { e := &p.entries[i] if e.hash == h { if eq, err := Equal(k, e.key); err != nil { return nil, false, err // e.g. excessively recursive tuple } else if eq { return e.value, true, nil // found } } } } return None, false, nil // not found } // Items returns all the items in the map (as key/value pairs) in insertion order. func (ht *hashtable) items() []Tuple { items := make([]Tuple, 0, ht.len) array := make([]Value, ht.len*2) // allocate a single backing array for e := ht.head; e != nil; e = e.next { pair := Tuple(array[:2:2]) array = array[2:] pair[0] = e.key pair[1] = e.value items = append(items, pair) } return items } func (ht *hashtable) first() (Value, bool) { if ht.head != nil { return ht.head.key, true } return None, false } func (ht *hashtable) keys() []Value { keys := make([]Value, 0, ht.len) for e := ht.head; e != nil; e = e.next { keys = append(keys, e.key) } return keys } func (ht *hashtable) delete(k Value) (v Value, found bool, err error) { if ht.frozen { return nil, false, fmt.Errorf("cannot delete from frozen hash table") } if ht.itercount > 0 { return nil, false, fmt.Errorf("cannot delete from hash table during iteration") } if ht.table == nil { return None, false, nil // empty } h, err := k.Hash() if err != nil { return nil, false, err // unhashable } if h == 0 { h = 1 // zero is reserved } // Inspect each bucket in the bucket list. for p := &ht.table[h&(uint32(len(ht.table)-1))]; p != nil; p = p.next { for i := range p.entries { e := &p.entries[i] if e.hash == h { if eq, err := Equal(k, e.key); err != nil { return nil, false, err } else if eq { // Remove e from doubly-linked list. *e.prevLink = e.next if e.next == nil { ht.tailLink = e.prevLink // deletion of last entry } else { e.next.prevLink = e.prevLink } v := e.value *e = entry{} ht.len-- return v, true, nil // found } } } } // TODO(adonovan): opt: remove completely empty bucket from bucket list. return None, false, nil // not found } func (ht *hashtable) clear() error { if ht.frozen { return fmt.Errorf("cannot clear frozen hash table") } if ht.itercount > 0 { return fmt.Errorf("cannot clear hash table during iteration") } if ht.table != nil { for i := range ht.table { ht.table[i] = bucket{} } } ht.head = nil ht.tailLink = &ht.head ht.len = 0 return nil } // dump is provided as an aid to debugging. func (ht *hashtable) dump() { fmt.Printf("hashtable %p len=%d head=%p tailLink=%p", ht, ht.len, ht.head, ht.tailLink) if ht.tailLink != nil { fmt.Printf(" *tailLink=%p", *ht.tailLink) } fmt.Println() for j := range ht.table { fmt.Printf("bucket chain %d\n", j) for p := &ht.table[j]; p != nil; p = p.next { fmt.Printf("bucket %p\n", p) for i := range p.entries { e := &p.entries[i] fmt.Printf("\tentry %d @ %p hash=%d key=%v value=%v\n", i, e, e.hash, e.key, e.value) fmt.Printf("\t\tnext=%p &next=%p prev=%p", e.next, &e.next, e.prevLink) if e.prevLink != nil { fmt.Printf(" *prev=%p", *e.prevLink) } fmt.Println() } } } } func (ht *hashtable) iterate() *keyIterator { if !ht.frozen { ht.itercount++ } return &keyIterator{ht: ht, e: ht.head} } type keyIterator struct { ht *hashtable e *entry } func (it *keyIterator) Next(k *Value) bool { if it.e != nil { *k = it.e.key it.e = it.e.next return true } return false } func (it *keyIterator) Done() { if !it.ht.frozen { it.ht.itercount-- } } // hashString computes the hash of s. func hashString(s string) uint32 { if len(s) >= 12 { // Call the Go runtime's optimized hash implementation, // which uses the AESENC instruction on amd64 machines. return uint32(goStringHash(s, 0)) } return softHashString(s) } //go:linkname goStringHash runtime.stringHash func goStringHash(s string, seed uintptr) uintptr // softHashString computes the FNV hash of s in software. func softHashString(s string) uint32 { var h uint32 for i := 0; i < len(s); i++ { h ^= uint32(s[i]) h *= 16777619 } return h } ================================================ FILE: vendor/go.starlark.net/starlark/int.go ================================================ // Copyright 2017 The Bazel Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package starlark import ( "fmt" "math" "math/big" "strconv" "go.starlark.net/syntax" ) // Int is the type of a Starlark int. type Int struct { // We use only the signed 32 bit range of small to ensure // that small+small and small*small do not overflow. small int64 // minint32 <= small <= maxint32 big *big.Int // big != nil <=> value is not representable as int32 } // newBig allocates a new big.Int. func newBig(x int64) *big.Int { if 0 <= x && int64(big.Word(x)) == x { // x is guaranteed to fit into a single big.Word. // Most starlark ints are small, // but math/big assumes that since you've chosen to use math/big, // your big.Ints will probably grow, so it over-allocates. // Avoid that over-allocation by manually constructing a single-word slice. // See https://golang.org/cl/150999, which will hopefully land in Go 1.13. return new(big.Int).SetBits([]big.Word{big.Word(x)}) } return big.NewInt(x) } // MakeInt returns a Starlark int for the specified signed integer. func MakeInt(x int) Int { return MakeInt64(int64(x)) } // MakeInt64 returns a Starlark int for the specified int64. func MakeInt64(x int64) Int { if math.MinInt32 <= x && x <= math.MaxInt32 { return Int{small: x} } return Int{big: newBig(x)} } // MakeUint returns a Starlark int for the specified unsigned integer. func MakeUint(x uint) Int { return MakeUint64(uint64(x)) } // MakeUint64 returns a Starlark int for the specified uint64. func MakeUint64(x uint64) Int { if x <= math.MaxInt32 { return Int{small: int64(x)} } if uint64(big.Word(x)) == x { // See comment in newBig for an explanation of this optimization. return Int{big: new(big.Int).SetBits([]big.Word{big.Word(x)})} } return Int{big: new(big.Int).SetUint64(x)} } // MakeBigInt returns a Starlark int for the specified big.Int. // The caller must not subsequently modify x. func MakeBigInt(x *big.Int) Int { if n := x.BitLen(); n < 32 || n == 32 && x.Int64() == math.MinInt32 { return Int{small: x.Int64()} } return Int{big: x} } var ( zero, one = Int{small: 0}, Int{small: 1} oneBig = newBig(1) _ HasUnary = Int{} ) // Unary implements the operations +int, -int, and ~int. func (i Int) Unary(op syntax.Token) (Value, error) { switch op { case syntax.MINUS: return zero.Sub(i), nil case syntax.PLUS: return i, nil case syntax.TILDE: return i.Not(), nil } return nil, nil } // Int64 returns the value as an int64. // If it is not exactly representable the result is undefined and ok is false. func (i Int) Int64() (_ int64, ok bool) { if i.big != nil { x, acc := bigintToInt64(i.big) if acc != big.Exact { return // inexact } return x, true } return i.small, true } // BigInt returns the value as a big.Int. // The returned variable must not be modified by the client. func (i Int) BigInt() *big.Int { if i.big != nil { return i.big } return newBig(i.small) } // Uint64 returns the value as a uint64. // If it is not exactly representable the result is undefined and ok is false. func (i Int) Uint64() (_ uint64, ok bool) { if i.big != nil { x, acc := bigintToUint64(i.big) if acc != big.Exact { return // inexact } return x, true } if i.small < 0 { return // inexact } return uint64(i.small), true } // The math/big API should provide this function. func bigintToInt64(i *big.Int) (int64, big.Accuracy) { sign := i.Sign() if sign > 0 { if i.Cmp(maxint64) > 0 { return math.MaxInt64, big.Below } } else if sign < 0 { if i.Cmp(minint64) < 0 { return math.MinInt64, big.Above } } return i.Int64(), big.Exact } // The math/big API should provide this function. func bigintToUint64(i *big.Int) (uint64, big.Accuracy) { sign := i.Sign() if sign > 0 { if i.BitLen() > 64 { return math.MaxUint64, big.Below } } else if sign < 0 { return 0, big.Above } return i.Uint64(), big.Exact } var ( minint64 = new(big.Int).SetInt64(math.MinInt64) maxint64 = new(big.Int).SetInt64(math.MaxInt64) ) func (i Int) Format(s fmt.State, ch rune) { if i.big != nil { i.big.Format(s, ch) return } newBig(i.small).Format(s, ch) } func (i Int) String() string { if i.big != nil { return i.big.Text(10) } return strconv.FormatInt(i.small, 10) } func (i Int) Type() string { return "int" } func (i Int) Freeze() {} // immutable func (i Int) Truth() Bool { return i.Sign() != 0 } func (i Int) Hash() (uint32, error) { var lo big.Word if i.big != nil { lo = i.big.Bits()[0] } else { lo = big.Word(i.small) } return 12582917 * uint32(lo+3), nil } func (x Int) CompareSameType(op syntax.Token, v Value, depth int) (bool, error) { y := v.(Int) if x.big != nil || y.big != nil { return threeway(op, x.BigInt().Cmp(y.BigInt())), nil } return threeway(op, signum64(x.small-y.small)), nil } // Float returns the float value nearest i. func (i Int) Float() Float { if i.big != nil { f, _ := new(big.Float).SetInt(i.big).Float64() return Float(f) } return Float(i.small) } func (x Int) Sign() int { if x.big != nil { return x.big.Sign() } return signum64(x.small) } func (x Int) Add(y Int) Int { if x.big != nil || y.big != nil { return MakeBigInt(new(big.Int).Add(x.BigInt(), y.BigInt())) } return MakeInt64(x.small + y.small) } func (x Int) Sub(y Int) Int { if x.big != nil || y.big != nil { return MakeBigInt(new(big.Int).Sub(x.BigInt(), y.BigInt())) } return MakeInt64(x.small - y.small) } func (x Int) Mul(y Int) Int { if x.big != nil || y.big != nil { return MakeBigInt(new(big.Int).Mul(x.BigInt(), y.BigInt())) } return MakeInt64(x.small * y.small) } func (x Int) Or(y Int) Int { if x.big != nil || y.big != nil { return Int{big: new(big.Int).Or(x.BigInt(), y.BigInt())} } return Int{small: x.small | y.small} } func (x Int) And(y Int) Int { if x.big != nil || y.big != nil { return MakeBigInt(new(big.Int).And(x.BigInt(), y.BigInt())) } return Int{small: x.small & y.small} } func (x Int) Xor(y Int) Int { if x.big != nil || y.big != nil { return MakeBigInt(new(big.Int).Xor(x.BigInt(), y.BigInt())) } return Int{small: x.small ^ y.small} } func (x Int) Not() Int { if x.big != nil { return MakeBigInt(new(big.Int).Not(x.big)) } return Int{small: ^x.small} } func (x Int) Lsh(y uint) Int { return MakeBigInt(new(big.Int).Lsh(x.BigInt(), y)) } func (x Int) Rsh(y uint) Int { return MakeBigInt(new(big.Int).Rsh(x.BigInt(), y)) } // Precondition: y is nonzero. func (x Int) Div(y Int) Int { // http://python-history.blogspot.com/2010/08/why-pythons-integer-division-floors.html if x.big != nil || y.big != nil { xb, yb := x.BigInt(), y.BigInt() var quo, rem big.Int quo.QuoRem(xb, yb, &rem) if (xb.Sign() < 0) != (yb.Sign() < 0) && rem.Sign() != 0 { quo.Sub(&quo, oneBig) } return MakeBigInt(&quo) } quo := x.small / y.small rem := x.small % y.small if (x.small < 0) != (y.small < 0) && rem != 0 { quo -= 1 } return MakeInt64(quo) } // Precondition: y is nonzero. func (x Int) Mod(y Int) Int { if x.big != nil || y.big != nil { xb, yb := x.BigInt(), y.BigInt() var quo, rem big.Int quo.QuoRem(xb, yb, &rem) if (xb.Sign() < 0) != (yb.Sign() < 0) && rem.Sign() != 0 { rem.Add(&rem, yb) } return MakeBigInt(&rem) } rem := x.small % y.small if (x.small < 0) != (y.small < 0) && rem != 0 { rem += y.small } return Int{small: rem} } func (i Int) rational() *big.Rat { if i.big != nil { return new(big.Rat).SetInt(i.big) } return new(big.Rat).SetInt64(i.small) } // AsInt32 returns the value of x if is representable as an int32. func AsInt32(x Value) (int, error) { i, ok := x.(Int) if !ok { return 0, fmt.Errorf("got %s, want int", x.Type()) } if i.big != nil { return 0, fmt.Errorf("%s out of range", i) } return int(i.small), nil } // NumberToInt converts a number x to an integer value. // An int is returned unchanged, a float is truncated towards zero. // NumberToInt reports an error for all other values. func NumberToInt(x Value) (Int, error) { switch x := x.(type) { case Int: return x, nil case Float: f := float64(x) if math.IsInf(f, 0) { return zero, fmt.Errorf("cannot convert float infinity to integer") } else if math.IsNaN(f) { return zero, fmt.Errorf("cannot convert float NaN to integer") } return finiteFloatToInt(x), nil } return zero, fmt.Errorf("cannot convert %s to int", x.Type()) } // finiteFloatToInt converts f to an Int, truncating towards zero. // f must be finite. func finiteFloatToInt(f Float) Int { if math.MinInt64 <= f && f <= math.MaxInt64 { // small values return MakeInt64(int64(f)) } rat := f.rational() if rat == nil { panic(f) // non-finite } return MakeBigInt(new(big.Int).Div(rat.Num(), rat.Denom())) } ================================================ FILE: vendor/go.starlark.net/starlark/interp.go ================================================ package starlark // This file defines the bytecode interpreter. import ( "fmt" "os" "go.starlark.net/internal/compile" "go.starlark.net/internal/spell" "go.starlark.net/resolve" "go.starlark.net/syntax" ) const vmdebug = false // TODO(adonovan): use a bitfield of specific kinds of error. // TODO(adonovan): // - optimize position table. // - opt: record MaxIterStack during compilation and preallocate the stack. func (fn *Function) CallInternal(thread *Thread, args Tuple, kwargs []Tuple) (Value, error) { if !resolve.AllowRecursion { // detect recursion for _, fr := range thread.stack[:len(thread.stack)-1] { // We look for the same function code, // not function value, otherwise the user could // defeat the check by writing the Y combinator. if frfn, ok := fr.Callable().(*Function); ok && frfn.funcode == fn.funcode { return nil, fmt.Errorf("function %s called recursively", fn.Name()) } } } f := fn.funcode fr := thread.frameAt(0) // Allocate space for stack and locals. // Logically these do not escape from this frame // (See https://github.com/golang/go/issues/20533.) // // This heap allocation looks expensive, but I was unable to get // more than 1% real time improvement in a large alloc-heavy // benchmark (in which this alloc was 8% of alloc-bytes) // by allocating space for 8 Values in each frame, or // by allocating stack by slicing an array held by the Thread // that is expanded in chunks of min(k, nspace), for k=256 or 1024. nlocals := len(f.Locals) nspace := nlocals + f.MaxStack space := make([]Value, nspace) locals := space[:nlocals:nlocals] // local variables, starting with parameters stack := space[nlocals:] // operand stack // Digest arguments and set parameters. err := setArgs(locals, fn, args, kwargs) if err != nil { return nil, thread.evalError(err) } fr.locals = locals if vmdebug { fmt.Printf("Entering %s @ %s\n", f.Name, f.Position(0)) fmt.Printf("%d stack, %d locals\n", len(stack), len(locals)) defer fmt.Println("Leaving ", f.Name) } // Spill indicated locals to cells. // Each cell is a separate alloc to avoid spurious liveness. for _, index := range f.Cells { locals[index] = &cell{locals[index]} } // TODO(adonovan): add static check that beneath this point // - there is exactly one return statement // - there is no redefinition of 'err'. var iterstack []Iterator // stack of active iterators sp := 0 var pc uint32 var result Value code := f.Code loop: for { fr.pc = pc op := compile.Opcode(code[pc]) pc++ var arg uint32 if op >= compile.OpcodeArgMin { // TODO(adonovan): opt: profile this. // Perhaps compiling big endian would be less work to decode? for s := uint(0); ; s += 7 { b := code[pc] pc++ arg |= uint32(b&0x7f) << s if b < 0x80 { break } } } if vmdebug { fmt.Fprintln(os.Stderr, stack[:sp]) // very verbose! compile.PrintOp(f, fr.pc, op, arg) } switch op { case compile.NOP: // nop case compile.DUP: stack[sp] = stack[sp-1] sp++ case compile.DUP2: stack[sp] = stack[sp-2] stack[sp+1] = stack[sp-1] sp += 2 case compile.POP: sp-- case compile.EXCH: stack[sp-2], stack[sp-1] = stack[sp-1], stack[sp-2] case compile.EQL, compile.NEQ, compile.GT, compile.LT, compile.LE, compile.GE: op := syntax.Token(op-compile.EQL) + syntax.EQL y := stack[sp-1] x := stack[sp-2] sp -= 2 ok, err2 := Compare(op, x, y) if err2 != nil { err = err2 break loop } stack[sp] = Bool(ok) sp++ case compile.PLUS, compile.MINUS, compile.STAR, compile.SLASH, compile.SLASHSLASH, compile.PERCENT, compile.AMP, compile.PIPE, compile.CIRCUMFLEX, compile.LTLT, compile.GTGT, compile.IN: binop := syntax.Token(op-compile.PLUS) + syntax.PLUS if op == compile.IN { binop = syntax.IN // IN token is out of order } y := stack[sp-1] x := stack[sp-2] sp -= 2 z, err2 := Binary(binop, x, y) if err2 != nil { err = err2 break loop } stack[sp] = z sp++ case compile.UPLUS, compile.UMINUS, compile.TILDE: var unop syntax.Token if op == compile.TILDE { unop = syntax.TILDE } else { unop = syntax.Token(op-compile.UPLUS) + syntax.PLUS } x := stack[sp-1] y, err2 := Unary(unop, x) if err2 != nil { err = err2 break loop } stack[sp-1] = y case compile.INPLACE_ADD: y := stack[sp-1] x := stack[sp-2] sp -= 2 // It's possible that y is not Iterable but // nonetheless defines x+y, in which case we // should fall back to the general case. var z Value if xlist, ok := x.(*List); ok { if yiter, ok := y.(Iterable); ok { if err = xlist.checkMutable("apply += to"); err != nil { break loop } listExtend(xlist, yiter) z = xlist } } if z == nil { z, err = Binary(syntax.PLUS, x, y) if err != nil { break loop } } stack[sp] = z sp++ case compile.NONE: stack[sp] = None sp++ case compile.TRUE: stack[sp] = True sp++ case compile.FALSE: stack[sp] = False sp++ case compile.MANDATORY: stack[sp] = mandatory{} sp++ case compile.JMP: pc = arg case compile.CALL, compile.CALL_VAR, compile.CALL_KW, compile.CALL_VAR_KW: var kwargs Value if op == compile.CALL_KW || op == compile.CALL_VAR_KW { kwargs = stack[sp-1] sp-- } var args Value if op == compile.CALL_VAR || op == compile.CALL_VAR_KW { args = stack[sp-1] sp-- } // named args (pairs) var kvpairs []Tuple if nkvpairs := int(arg & 0xff); nkvpairs > 0 { kvpairs = make([]Tuple, 0, nkvpairs) kvpairsAlloc := make(Tuple, 2*nkvpairs) // allocate a single backing array sp -= 2 * nkvpairs for i := 0; i < nkvpairs; i++ { pair := kvpairsAlloc[:2:2] kvpairsAlloc = kvpairsAlloc[2:] pair[0] = stack[sp+2*i] // name pair[1] = stack[sp+2*i+1] // value kvpairs = append(kvpairs, pair) } } if kwargs != nil { // Add key/value items from **kwargs dictionary. dict, ok := kwargs.(IterableMapping) if !ok { err = fmt.Errorf("argument after ** must be a mapping, not %s", kwargs.Type()) break loop } items := dict.Items() for _, item := range items { if _, ok := item[0].(String); !ok { err = fmt.Errorf("keywords must be strings, not %s", item[0].Type()) break loop } } if len(kvpairs) == 0 { kvpairs = items } else { kvpairs = append(kvpairs, items...) } } // positional args var positional Tuple if npos := int(arg >> 8); npos > 0 { positional = make(Tuple, npos) sp -= npos copy(positional, stack[sp:]) } if args != nil { // Add elements from *args sequence. iter := Iterate(args) if iter == nil { err = fmt.Errorf("argument after * must be iterable, not %s", args.Type()) break loop } var elem Value for iter.Next(&elem) { positional = append(positional, elem) } iter.Done() } function := stack[sp-1] if vmdebug { fmt.Printf("VM call %s args=%s kwargs=%s @%s\n", function, positional, kvpairs, f.Position(fr.pc)) } thread.endProfSpan() z, err2 := Call(thread, function, positional, kvpairs) thread.beginProfSpan() if err2 != nil { err = err2 break loop } if vmdebug { fmt.Printf("Resuming %s @ %s\n", f.Name, f.Position(0)) } stack[sp-1] = z case compile.ITERPUSH: x := stack[sp-1] sp-- iter := Iterate(x) if iter == nil { err = fmt.Errorf("%s value is not iterable", x.Type()) break loop } iterstack = append(iterstack, iter) case compile.ITERJMP: iter := iterstack[len(iterstack)-1] if iter.Next(&stack[sp]) { sp++ } else { pc = arg } case compile.ITERPOP: n := len(iterstack) - 1 iterstack[n].Done() iterstack = iterstack[:n] case compile.NOT: stack[sp-1] = !stack[sp-1].Truth() case compile.RETURN: result = stack[sp-1] break loop case compile.SETINDEX: z := stack[sp-1] y := stack[sp-2] x := stack[sp-3] sp -= 3 err = setIndex(x, y, z) if err != nil { break loop } case compile.INDEX: y := stack[sp-1] x := stack[sp-2] sp -= 2 z, err2 := getIndex(x, y) if err2 != nil { err = err2 break loop } stack[sp] = z sp++ case compile.ATTR: x := stack[sp-1] name := f.Prog.Names[arg] y, err2 := getAttr(x, name) if err2 != nil { err = err2 break loop } stack[sp-1] = y case compile.SETFIELD: y := stack[sp-1] x := stack[sp-2] sp -= 2 name := f.Prog.Names[arg] if err2 := setField(x, name, y); err2 != nil { err = err2 break loop } case compile.MAKEDICT: stack[sp] = new(Dict) sp++ case compile.SETDICT, compile.SETDICTUNIQ: dict := stack[sp-3].(*Dict) k := stack[sp-2] v := stack[sp-1] sp -= 3 oldlen := dict.Len() if err2 := dict.SetKey(k, v); err2 != nil { err = err2 break loop } if op == compile.SETDICTUNIQ && dict.Len() == oldlen { err = fmt.Errorf("duplicate key: %v", k) break loop } case compile.APPEND: elem := stack[sp-1] list := stack[sp-2].(*List) sp -= 2 list.elems = append(list.elems, elem) case compile.SLICE: x := stack[sp-4] lo := stack[sp-3] hi := stack[sp-2] step := stack[sp-1] sp -= 4 res, err2 := slice(x, lo, hi, step) if err2 != nil { err = err2 break loop } stack[sp] = res sp++ case compile.UNPACK: n := int(arg) iterable := stack[sp-1] sp-- iter := Iterate(iterable) if iter == nil { err = fmt.Errorf("got %s in sequence assignment", iterable.Type()) break loop } i := 0 sp += n for i < n && iter.Next(&stack[sp-1-i]) { i++ } var dummy Value if iter.Next(&dummy) { // NB: Len may return -1 here in obscure cases. err = fmt.Errorf("too many values to unpack (got %d, want %d)", Len(iterable), n) break loop } iter.Done() if i < n { err = fmt.Errorf("too few values to unpack (got %d, want %d)", i, n) break loop } case compile.CJMP: if stack[sp-1].Truth() { pc = arg } sp-- case compile.CONSTANT: stack[sp] = fn.module.constants[arg] sp++ case compile.MAKETUPLE: n := int(arg) tuple := make(Tuple, n) sp -= n copy(tuple, stack[sp:]) stack[sp] = tuple sp++ case compile.MAKELIST: n := int(arg) elems := make([]Value, n) sp -= n copy(elems, stack[sp:]) stack[sp] = NewList(elems) sp++ case compile.MAKEFUNC: funcode := f.Prog.Functions[arg] tuple := stack[sp-1].(Tuple) n := len(tuple) - len(funcode.Freevars) defaults := tuple[:n:n] freevars := tuple[n:] stack[sp-1] = &Function{ funcode: funcode, module: fn.module, defaults: defaults, freevars: freevars, } case compile.LOAD: n := int(arg) module := string(stack[sp-1].(String)) sp-- if thread.Load == nil { err = fmt.Errorf("load not implemented by this application") break loop } thread.endProfSpan() dict, err2 := thread.Load(thread, module) thread.beginProfSpan() if err2 != nil { err = wrappedError{ msg: fmt.Sprintf("cannot load %s: %v", module, err2), cause: err2, } break loop } for i := 0; i < n; i++ { from := string(stack[sp-1-i].(String)) v, ok := dict[from] if !ok { err = fmt.Errorf("load: name %s not found in module %s", from, module) if n := spell.Nearest(from, dict.Keys()); n != "" { err = fmt.Errorf("%s (did you mean %s?)", err, n) } break loop } stack[sp-1-i] = v } case compile.SETLOCAL: locals[arg] = stack[sp-1] sp-- case compile.SETCELL: x := stack[sp-2] y := stack[sp-1] sp -= 2 y.(*cell).v = x case compile.SETGLOBAL: fn.module.globals[arg] = stack[sp-1] sp-- case compile.LOCAL: x := locals[arg] if x == nil { err = fmt.Errorf("local variable %s referenced before assignment", f.Locals[arg].Name) break loop } stack[sp] = x sp++ case compile.FREE: stack[sp] = fn.freevars[arg] sp++ case compile.CELL: x := stack[sp-1] stack[sp-1] = x.(*cell).v case compile.GLOBAL: x := fn.module.globals[arg] if x == nil { err = fmt.Errorf("global variable %s referenced before assignment", f.Prog.Globals[arg].Name) break loop } stack[sp] = x sp++ case compile.PREDECLARED: name := f.Prog.Names[arg] x := fn.module.predeclared[name] if x == nil { err = fmt.Errorf("internal error: predeclared variable %s is uninitialized", name) break loop } stack[sp] = x sp++ case compile.UNIVERSAL: stack[sp] = Universe[f.Prog.Names[arg]] sp++ default: err = fmt.Errorf("unimplemented: %s", op) break loop } } // ITERPOP the rest of the iterator stack. for _, iter := range iterstack { iter.Done() } fr.locals = nil return result, err } type wrappedError struct { msg string cause error } func (e wrappedError) Error() string { return e.msg } // Implements the xerrors.Wrapper interface // https://godoc.org/golang.org/x/xerrors#Wrapper func (e wrappedError) Unwrap() error { return e.cause } // mandatory is a sentinel value used in a function's defaults tuple // to indicate that a (keyword-only) parameter is mandatory. type mandatory struct{} func (mandatory) String() string { return "mandatory" } func (mandatory) Type() string { return "mandatory" } func (mandatory) Freeze() {} // immutable func (mandatory) Truth() Bool { return False } func (mandatory) Hash() (uint32, error) { return 0, nil } // A cell is a box containing a Value. // Local variables marked as cells hold their value indirectly // so that they may be shared by outer and inner nested functions. // Cells are always accessed using indirect CELL/SETCELL instructions. // The FreeVars tuple contains only cells. // The FREE instruction always yields a cell. type cell struct{ v Value } func (c *cell) String() string { return "cell" } func (c *cell) Type() string { return "cell" } func (c *cell) Freeze() { if c.v != nil { c.v.Freeze() } } func (c *cell) Truth() Bool { panic("unreachable") } func (c *cell) Hash() (uint32, error) { panic("unreachable") } ================================================ FILE: vendor/go.starlark.net/starlark/library.go ================================================ // Copyright 2017 The Bazel Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package starlark // This file defines the library of built-ins. // // Built-ins must explicitly check the "frozen" flag before updating // mutable types such as lists and dicts. import ( "errors" "fmt" "math/big" "os" "sort" "strconv" "strings" "unicode" "unicode/utf16" "unicode/utf8" "go.starlark.net/syntax" ) // Universe defines the set of universal built-ins, such as None, True, and len. // // The Go application may add or remove items from the // universe dictionary before Starlark evaluation begins. // All values in the dictionary must be immutable. // Starlark programs cannot modify the dictionary. var Universe StringDict func init() { // https://github.com/google/starlark-go/blob/master/doc/spec.md#built-in-constants-and-functions Universe = StringDict{ "None": None, "True": True, "False": False, "any": NewBuiltin("any", any), "all": NewBuiltin("all", all), "bool": NewBuiltin("bool", bool_), "chr": NewBuiltin("chr", chr), "dict": NewBuiltin("dict", dict), "dir": NewBuiltin("dir", dir), "enumerate": NewBuiltin("enumerate", enumerate), "fail": NewBuiltin("fail", fail), "float": NewBuiltin("float", float), // requires resolve.AllowFloat "getattr": NewBuiltin("getattr", getattr), "hasattr": NewBuiltin("hasattr", hasattr), "hash": NewBuiltin("hash", hash), "int": NewBuiltin("int", int_), "len": NewBuiltin("len", len_), "list": NewBuiltin("list", list), "max": NewBuiltin("max", minmax), "min": NewBuiltin("min", minmax), "ord": NewBuiltin("ord", ord), "print": NewBuiltin("print", print), "range": NewBuiltin("range", range_), "repr": NewBuiltin("repr", repr), "reversed": NewBuiltin("reversed", reversed), "set": NewBuiltin("set", set), // requires resolve.AllowSet "sorted": NewBuiltin("sorted", sorted), "str": NewBuiltin("str", str), "tuple": NewBuiltin("tuple", tuple), "type": NewBuiltin("type", type_), "zip": NewBuiltin("zip", zip), } } // methods of built-in types // https://github.com/google/starlark-go/blob/master/doc/spec.md#built-in-methods var ( dictMethods = map[string]*Builtin{ "clear": NewBuiltin("clear", dict_clear), "get": NewBuiltin("get", dict_get), "items": NewBuiltin("items", dict_items), "keys": NewBuiltin("keys", dict_keys), "pop": NewBuiltin("pop", dict_pop), "popitem": NewBuiltin("popitem", dict_popitem), "setdefault": NewBuiltin("setdefault", dict_setdefault), "update": NewBuiltin("update", dict_update), "values": NewBuiltin("values", dict_values), } listMethods = map[string]*Builtin{ "append": NewBuiltin("append", list_append), "clear": NewBuiltin("clear", list_clear), "extend": NewBuiltin("extend", list_extend), "index": NewBuiltin("index", list_index), "insert": NewBuiltin("insert", list_insert), "pop": NewBuiltin("pop", list_pop), "remove": NewBuiltin("remove", list_remove), } stringMethods = map[string]*Builtin{ "capitalize": NewBuiltin("capitalize", string_capitalize), "codepoint_ords": NewBuiltin("codepoint_ords", string_iterable), "codepoints": NewBuiltin("codepoints", string_iterable), // sic "count": NewBuiltin("count", string_count), "elem_ords": NewBuiltin("elem_ords", string_iterable), "elems": NewBuiltin("elems", string_iterable), // sic "endswith": NewBuiltin("endswith", string_startswith), // sic "find": NewBuiltin("find", string_find), "format": NewBuiltin("format", string_format), "index": NewBuiltin("index", string_index), "isalnum": NewBuiltin("isalnum", string_isalnum), "isalpha": NewBuiltin("isalpha", string_isalpha), "isdigit": NewBuiltin("isdigit", string_isdigit), "islower": NewBuiltin("islower", string_islower), "isspace": NewBuiltin("isspace", string_isspace), "istitle": NewBuiltin("istitle", string_istitle), "isupper": NewBuiltin("isupper", string_isupper), "join": NewBuiltin("join", string_join), "lower": NewBuiltin("lower", string_lower), "lstrip": NewBuiltin("lstrip", string_strip), // sic "partition": NewBuiltin("partition", string_partition), "replace": NewBuiltin("replace", string_replace), "rfind": NewBuiltin("rfind", string_rfind), "rindex": NewBuiltin("rindex", string_rindex), "rpartition": NewBuiltin("rpartition", string_partition), // sic "rsplit": NewBuiltin("rsplit", string_split), // sic "rstrip": NewBuiltin("rstrip", string_strip), // sic "split": NewBuiltin("split", string_split), "splitlines": NewBuiltin("splitlines", string_splitlines), "startswith": NewBuiltin("startswith", string_startswith), "strip": NewBuiltin("strip", string_strip), "title": NewBuiltin("title", string_title), "upper": NewBuiltin("upper", string_upper), } setMethods = map[string]*Builtin{ "union": NewBuiltin("union", set_union), } ) func builtinAttr(recv Value, name string, methods map[string]*Builtin) (Value, error) { b := methods[name] if b == nil { return nil, nil // no such method } return b.BindReceiver(recv), nil } func builtinAttrNames(methods map[string]*Builtin) []string { names := make([]string, 0, len(methods)) for name := range methods { names = append(names, name) } sort.Strings(names) return names } // ---- built-in functions ---- // https://github.com/google/starlark-go/blob/master/doc/spec.md#all func all(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { var iterable Iterable if err := UnpackPositionalArgs("all", args, kwargs, 1, &iterable); err != nil { return nil, err } iter := iterable.Iterate() defer iter.Done() var x Value for iter.Next(&x) { if !x.Truth() { return False, nil } } return True, nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#any func any(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { var iterable Iterable if err := UnpackPositionalArgs("any", args, kwargs, 1, &iterable); err != nil { return nil, err } iter := iterable.Iterate() defer iter.Done() var x Value for iter.Next(&x) { if x.Truth() { return True, nil } } return False, nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#bool func bool_(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { var x Value = False if err := UnpackPositionalArgs("bool", args, kwargs, 0, &x); err != nil { return nil, err } return x.Truth(), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#chr func chr(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if len(kwargs) > 0 { return nil, fmt.Errorf("chr does not accept keyword arguments") } if len(args) != 1 { return nil, fmt.Errorf("chr: got %d arguments, want 1", len(args)) } i, err := AsInt32(args[0]) if err != nil { return nil, fmt.Errorf("chr: got %s, want int", args[0].Type()) } if i < 0 { return nil, fmt.Errorf("chr: Unicode code point %d out of range (<0)", i) } if i > unicode.MaxRune { return nil, fmt.Errorf("chr: Unicode code point U+%X out of range (>0x10FFFF)", i) } return String(string(i)), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#dict func dict(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if len(args) > 1 { return nil, fmt.Errorf("dict: got %d arguments, want at most 1", len(args)) } dict := new(Dict) if err := updateDict(dict, args, kwargs); err != nil { return nil, fmt.Errorf("dict: %v", err) } return dict, nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#dir func dir(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if len(kwargs) > 0 { return nil, fmt.Errorf("dir does not accept keyword arguments") } if len(args) != 1 { return nil, fmt.Errorf("dir: got %d arguments, want 1", len(args)) } var names []string if x, ok := args[0].(HasAttrs); ok { names = x.AttrNames() } sort.Strings(names) elems := make([]Value, len(names)) for i, name := range names { elems[i] = String(name) } return NewList(elems), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#enumerate func enumerate(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { var iterable Iterable var start int if err := UnpackPositionalArgs("enumerate", args, kwargs, 1, &iterable, &start); err != nil { return nil, err } iter := iterable.Iterate() if iter == nil { return nil, fmt.Errorf("enumerate: got %s, want iterable", iterable.Type()) } defer iter.Done() var pairs []Value var x Value if n := Len(iterable); n >= 0 { // common case: known length pairs = make([]Value, 0, n) array := make(Tuple, 2*n) // allocate a single backing array for i := 0; iter.Next(&x); i++ { pair := array[:2:2] array = array[2:] pair[0] = MakeInt(start + i) pair[1] = x pairs = append(pairs, pair) } } else { // non-sequence (unknown length) for i := 0; iter.Next(&x); i++ { pair := Tuple{MakeInt(start + i), x} pairs = append(pairs, pair) } } return NewList(pairs), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#fail func fail(thread *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { sep := " " if err := UnpackArgs("fail", nil, kwargs, "sep?", &sep); err != nil { return nil, err } buf := new(strings.Builder) buf.WriteString("fail: ") for i, v := range args { if i > 0 { buf.WriteString(sep) } if s, ok := AsString(v); ok { buf.WriteString(s) } else { writeValue(buf, v, nil) } } return nil, errors.New(buf.String()) } func float(thread *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if len(kwargs) > 0 { return nil, fmt.Errorf("float does not accept keyword arguments") } if len(args) == 0 { return Float(0.0), nil } if len(args) != 1 { return nil, fmt.Errorf("float got %d arguments, wants 1", len(args)) } switch x := args[0].(type) { case Bool: if x { return Float(1.0), nil } else { return Float(0.0), nil } case Int: return x.Float(), nil case Float: return x, nil case String: f, err := strconv.ParseFloat(string(x), 64) if err != nil { return nil, nameErr(b, err) } return Float(f), nil default: return nil, fmt.Errorf("float got %s, want number or string", x.Type()) } } // https://github.com/google/starlark-go/blob/master/doc/spec.md#getattr func getattr(thread *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { var object, dflt Value var name string if err := UnpackPositionalArgs("getattr", args, kwargs, 2, &object, &name, &dflt); err != nil { return nil, err } if object, ok := object.(HasAttrs); ok { v, err := object.Attr(name) if err != nil { // An error could mean the field doesn't exist, // or it exists but could not be computed. if dflt != nil { return dflt, nil } return nil, nameErr(b, err) } if v != nil { return v, nil } // (nil, nil) => no such field } if dflt != nil { return dflt, nil } return nil, fmt.Errorf("getattr: %s has no .%s field or method", object.Type(), name) } // https://github.com/google/starlark-go/blob/master/doc/spec.md#hasattr func hasattr(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { var object Value var name string if err := UnpackPositionalArgs("hasattr", args, kwargs, 2, &object, &name); err != nil { return nil, err } if object, ok := object.(HasAttrs); ok { v, err := object.Attr(name) if err == nil { return Bool(v != nil), nil } // An error does not conclusively indicate presence or // absence of a field: it could occur while computing // the value of a present attribute, or it could be a // "no such attribute" error with details. for _, x := range object.AttrNames() { if x == name { return True, nil } } } return False, nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#hash func hash(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { var s string if err := UnpackPositionalArgs("hash", args, kwargs, 1, &s); err != nil { return nil, err } // The Starlark spec requires that the hash function be // deterministic across all runs, motivated by the need // for reproducibility of builds. Thus we cannot call // String.Hash, which uses the fastest implementation // available, because as varies across process restarts, // and may evolve with the implementation. return MakeInt(int(javaStringHash(s))), nil } // javaStringHash returns the same hash as would be produced by // java.lang.String.hashCode. This requires transcoding the string to // UTF-16; transcoding may introduce Unicode replacement characters // U+FFFD if s does not contain valid UTF-8. func javaStringHash(s string) (h int32) { for _, r := range s { if utf16.IsSurrogate(r) { c1, c2 := utf16.EncodeRune(r) h = 31*h + c1 h = 31*h + c2 } else { h = 31*h + r // r may be U+FFFD } } return h } // https://github.com/google/starlark-go/blob/master/doc/spec.md#int func int_(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { var x Value = zero var base Value if err := UnpackArgs("int", args, kwargs, "x", &x, "base?", &base); err != nil { return nil, err } // "If x is not a number or base is given, x must be a string." if s, ok := AsString(x); ok { b := 10 if base != nil { var err error b, err = AsInt32(base) if err != nil || b != 0 && (b < 2 || b > 36) { return nil, fmt.Errorf("int: base must be an integer >= 2 && <= 36") } } orig := s // save original for error message // remove sign var neg bool if s != "" { if s[0] == '+' { s = s[1:] } else if s[0] == '-' { neg = true s = s[1:] } } // remove base prefix baseprefix := 0 if len(s) > 1 && s[0] == '0' { if len(s) > 2 { switch s[1] { case 'o', 'O': s = s[2:] baseprefix = 8 case 'x', 'X': s = s[2:] baseprefix = 16 case 'b', 'B': s = s[2:] baseprefix = 2 } } // For automatic base detection, // a string starting with zero // must be all zeros. // Thus we reject int("0755", 0). if baseprefix == 0 && b == 0 { for i := 1; i < len(s); i++ { if s[i] != '0' { goto invalid } } return zero, nil } if b != 0 && baseprefix != 0 && baseprefix != b { // Explicit base doesn't match prefix, // e.g. int("0o755", 16). goto invalid } } // select base if b == 0 { if baseprefix != 0 { b = baseprefix } else { b = 10 } } // we explicitly handled sign above. // if a sign remains, it is invalid. if s != "" && (s[0] == '-' || s[0] == '+') { goto invalid } // s has no sign or base prefix. // // int(x) permits arbitrary precision, unlike the scanner. if i, ok := new(big.Int).SetString(s, b); ok { res := MakeBigInt(i) if neg { res = zero.Sub(res) } return res, nil } invalid: return nil, fmt.Errorf("int: invalid literal with base %d: %s", b, orig) } if base != nil { return nil, fmt.Errorf("int: can't convert non-string with explicit base") } if b, ok := x.(Bool); ok { if b { return one, nil } else { return zero, nil } } i, err := NumberToInt(x) if err != nil { return nil, fmt.Errorf("int: %s", err) } return i, nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#len func len_(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { var x Value if err := UnpackPositionalArgs("len", args, kwargs, 1, &x); err != nil { return nil, err } len := Len(x) if len < 0 { return nil, fmt.Errorf("len: value of type %s has no len", x.Type()) } return MakeInt(len), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#list func list(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { var iterable Iterable if err := UnpackPositionalArgs("list", args, kwargs, 0, &iterable); err != nil { return nil, err } var elems []Value if iterable != nil { iter := iterable.Iterate() defer iter.Done() if n := Len(iterable); n > 0 { elems = make([]Value, 0, n) // preallocate if length known } var x Value for iter.Next(&x) { elems = append(elems, x) } } return NewList(elems), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#min func minmax(thread *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if len(args) == 0 { return nil, fmt.Errorf("%s requires at least one positional argument", b.Name()) } var keyFunc Callable if err := UnpackArgs(b.Name(), nil, kwargs, "key?", &keyFunc); err != nil { return nil, err } var op syntax.Token if b.Name() == "max" { op = syntax.GT } else { op = syntax.LT } var iterable Value if len(args) == 1 { iterable = args[0] } else { iterable = args } iter := Iterate(iterable) if iter == nil { return nil, fmt.Errorf("%s: %s value is not iterable", b.Name(), iterable.Type()) } defer iter.Done() var extremum Value if !iter.Next(&extremum) { return nil, nameErr(b, "argument is an empty sequence") } var extremeKey Value var keyargs Tuple if keyFunc == nil { extremeKey = extremum } else { keyargs = Tuple{extremum} res, err := Call(thread, keyFunc, keyargs, nil) if err != nil { return nil, err // to preserve backtrace, don't modify error } extremeKey = res } var x Value for iter.Next(&x) { var key Value if keyFunc == nil { key = x } else { keyargs[0] = x res, err := Call(thread, keyFunc, keyargs, nil) if err != nil { return nil, err // to preserve backtrace, don't modify error } key = res } if ok, err := Compare(op, key, extremeKey); err != nil { return nil, nameErr(b, err) } else if ok { extremum = x extremeKey = key } } return extremum, nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#ord func ord(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if len(kwargs) > 0 { return nil, fmt.Errorf("ord does not accept keyword arguments") } if len(args) != 1 { return nil, fmt.Errorf("ord: got %d arguments, want 1", len(args)) } s, ok := AsString(args[0]) if !ok { return nil, fmt.Errorf("ord: got %s, want string", args[0].Type()) } r, sz := utf8.DecodeRuneInString(s) if sz == 0 || sz != len(s) { n := utf8.RuneCountInString(s) return nil, fmt.Errorf("ord: string encodes %d Unicode code points, want 1", n) } return MakeInt(int(r)), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#print func print(thread *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { sep := " " if err := UnpackArgs("print", nil, kwargs, "sep?", &sep); err != nil { return nil, err } buf := new(strings.Builder) for i, v := range args { if i > 0 { buf.WriteString(sep) } if s, ok := AsString(v); ok { buf.WriteString(s) } else { writeValue(buf, v, nil) } } s := buf.String() if thread.Print != nil { thread.Print(thread, s) } else { fmt.Fprintln(os.Stderr, s) } return None, nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#range func range_(thread *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { var start, stop, step int step = 1 if err := UnpackPositionalArgs("range", args, kwargs, 1, &start, &stop, &step); err != nil { return nil, err } // TODO(adonovan): analyze overflow/underflows cases for 32-bit implementations. if len(args) == 1 { // range(stop) start, stop = 0, start } if step == 0 { // we were given range(start, stop, 0) return nil, nameErr(b, "step argument must not be zero") } return rangeValue{start: start, stop: stop, step: step, len: rangeLen(start, stop, step)}, nil } // A rangeValue is a comparable, immutable, indexable sequence of integers // defined by the three parameters to a range(...) call. // Invariant: step != 0. type rangeValue struct{ start, stop, step, len int } var ( _ Indexable = rangeValue{} _ Sequence = rangeValue{} _ Comparable = rangeValue{} _ Sliceable = rangeValue{} ) func (r rangeValue) Len() int { return r.len } func (r rangeValue) Index(i int) Value { return MakeInt(r.start + i*r.step) } func (r rangeValue) Iterate() Iterator { return &rangeIterator{r, 0} } // rangeLen calculates the length of a range with the provided start, stop, and step. // caller must ensure that step is non-zero. func rangeLen(start, stop, step int) int { switch { case step > 0: if stop > start { return (stop-1-start)/step + 1 } case step < 0: if start > stop { return (start-1-stop)/-step + 1 } default: panic("rangeLen: zero step") } return 0 } func (r rangeValue) Slice(start, end, step int) Value { newStart := r.start + r.step*start newStop := r.start + r.step*end newStep := r.step * step return rangeValue{ start: newStart, stop: newStop, step: newStep, len: rangeLen(newStart, newStop, newStep), } } func (r rangeValue) Freeze() {} // immutable func (r rangeValue) String() string { if r.step != 1 { return fmt.Sprintf("range(%d, %d, %d)", r.start, r.stop, r.step) } else if r.start != 0 { return fmt.Sprintf("range(%d, %d)", r.start, r.stop) } else { return fmt.Sprintf("range(%d)", r.stop) } } func (r rangeValue) Type() string { return "range" } func (r rangeValue) Truth() Bool { return r.len > 0 } func (r rangeValue) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable: range") } func (x rangeValue) CompareSameType(op syntax.Token, y_ Value, depth int) (bool, error) { y := y_.(rangeValue) switch op { case syntax.EQL: return rangeEqual(x, y), nil case syntax.NEQ: return !rangeEqual(x, y), nil default: return false, fmt.Errorf("%s %s %s not implemented", x.Type(), op, y.Type()) } } func rangeEqual(x, y rangeValue) bool { // Two ranges compare equal if they denote the same sequence. if x.len != y.len { return false // sequences differ in length } if x.len == 0 { return true // both sequences are empty } if x.start != y.start { return false // first element differs } return x.len == 1 || x.step == y.step } func (r rangeValue) contains(x Int) bool { x32, err := AsInt32(x) if err != nil { return false // out of range } delta := x32 - r.start quo, rem := delta/r.step, delta%r.step return rem == 0 && 0 <= quo && quo < r.len } type rangeIterator struct { r rangeValue i int } func (it *rangeIterator) Next(p *Value) bool { if it.i < it.r.len { *p = it.r.Index(it.i) it.i++ return true } return false } func (*rangeIterator) Done() {} // https://github.com/google/starlark-go/blob/master/doc/spec.md#repr func repr(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { var x Value if err := UnpackPositionalArgs("repr", args, kwargs, 1, &x); err != nil { return nil, err } return String(x.String()), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#reversed func reversed(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { var iterable Iterable if err := UnpackPositionalArgs("reversed", args, kwargs, 1, &iterable); err != nil { return nil, err } iter := iterable.Iterate() defer iter.Done() var elems []Value if n := Len(args[0]); n >= 0 { elems = make([]Value, 0, n) // preallocate if length known } var x Value for iter.Next(&x) { elems = append(elems, x) } n := len(elems) for i := 0; i < n>>1; i++ { elems[i], elems[n-1-i] = elems[n-1-i], elems[i] } return NewList(elems), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#set func set(thread *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { var iterable Iterable if err := UnpackPositionalArgs("set", args, kwargs, 0, &iterable); err != nil { return nil, err } set := new(Set) if iterable != nil { iter := iterable.Iterate() defer iter.Done() var x Value for iter.Next(&x) { if err := set.Insert(x); err != nil { return nil, nameErr(b, err) } } } return set, nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#sorted func sorted(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { // Oddly, Python's sorted permits all arguments to be positional, thus so do we. var iterable Iterable var key Callable var reverse bool if err := UnpackArgs("sorted", args, kwargs, "iterable", &iterable, "key?", &key, "reverse?", &reverse, ); err != nil { return nil, err } iter := iterable.Iterate() defer iter.Done() var values []Value if n := Len(iterable); n > 0 { values = make(Tuple, 0, n) // preallocate if length is known } var x Value for iter.Next(&x) { values = append(values, x) } // Derive keys from values by applying key function. var keys []Value if key != nil { keys = make([]Value, len(values)) for i, v := range values { k, err := Call(thread, key, Tuple{v}, nil) if err != nil { return nil, err // to preserve backtrace, don't modify error } keys[i] = k } } slice := &sortSlice{keys: keys, values: values} if reverse { sort.Stable(sort.Reverse(slice)) } else { sort.Stable(slice) } return NewList(slice.values), slice.err } type sortSlice struct { keys []Value // nil => values[i] is key values []Value err error } func (s *sortSlice) Len() int { return len(s.values) } func (s *sortSlice) Less(i, j int) bool { keys := s.keys if s.keys == nil { keys = s.values } ok, err := Compare(syntax.LT, keys[i], keys[j]) if err != nil { s.err = err } return ok } func (s *sortSlice) Swap(i, j int) { if s.keys != nil { s.keys[i], s.keys[j] = s.keys[j], s.keys[i] } s.values[i], s.values[j] = s.values[j], s.values[i] } // https://github.com/google/starlark-go/blob/master/doc/spec.md#str func str(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if len(kwargs) > 0 { return nil, fmt.Errorf("str does not accept keyword arguments") } if len(args) != 1 { return nil, fmt.Errorf("str: got %d arguments, want exactly 1", len(args)) } x := args[0] if _, ok := AsString(x); !ok { x = String(x.String()) } return x, nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#tuple func tuple(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { var iterable Iterable if err := UnpackPositionalArgs("tuple", args, kwargs, 0, &iterable); err != nil { return nil, err } if len(args) == 0 { return Tuple(nil), nil } iter := iterable.Iterate() defer iter.Done() var elems Tuple if n := Len(iterable); n > 0 { elems = make(Tuple, 0, n) // preallocate if length is known } var x Value for iter.Next(&x) { elems = append(elems, x) } return elems, nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#type func type_(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if len(kwargs) > 0 { return nil, fmt.Errorf("type does not accept keyword arguments") } if len(args) != 1 { return nil, fmt.Errorf("type: got %d arguments, want exactly 1", len(args)) } return String(args[0].Type()), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#zip func zip(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if len(kwargs) > 0 { return nil, fmt.Errorf("zip does not accept keyword arguments") } rows, cols := 0, len(args) iters := make([]Iterator, cols) defer func() { for _, iter := range iters { if iter != nil { iter.Done() } } }() for i, seq := range args { it := Iterate(seq) if it == nil { return nil, fmt.Errorf("zip: argument #%d is not iterable: %s", i+1, seq.Type()) } iters[i] = it n := Len(seq) if i == 0 || n < rows { rows = n // possibly -1 } } var result []Value if rows >= 0 { // length known result = make([]Value, rows) array := make(Tuple, cols*rows) // allocate a single backing array for i := 0; i < rows; i++ { tuple := array[:cols:cols] array = array[cols:] for j, iter := range iters { iter.Next(&tuple[j]) } result[i] = tuple } } else { // length not known outer: for { tuple := make(Tuple, cols) for i, iter := range iters { if !iter.Next(&tuple[i]) { break outer } } result = append(result, tuple) } } return NewList(result), nil } // ---- methods of built-in types --- // https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·get func dict_get(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { var key, dflt Value if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &key, &dflt); err != nil { return nil, err } if v, ok, err := b.Receiver().(*Dict).Get(key); err != nil { return nil, nameErr(b, err) } else if ok { return v, nil } else if dflt != nil { return dflt, nil } return None, nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·clear func dict_clear(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { return nil, err } return None, b.Receiver().(*Dict).Clear() } // https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·items func dict_items(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { return nil, err } items := b.Receiver().(*Dict).Items() res := make([]Value, len(items)) for i, item := range items { res[i] = item // convert [2]Value to Value } return NewList(res), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·keys func dict_keys(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { return nil, err } return NewList(b.Receiver().(*Dict).Keys()), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·pop func dict_pop(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { var k, d Value if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &k, &d); err != nil { return nil, err } if v, found, err := b.Receiver().(*Dict).Delete(k); err != nil { return nil, nameErr(b, err) // dict is frozen or key is unhashable } else if found { return v, nil } else if d != nil { return d, nil } return nil, nameErr(b, "missing key") } // https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·popitem func dict_popitem(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { return nil, err } recv := b.Receiver().(*Dict) k, ok := recv.ht.first() if !ok { return nil, nameErr(b, "empty dict") } v, _, err := recv.Delete(k) if err != nil { return nil, nameErr(b, err) // dict is frozen } return Tuple{k, v}, nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·setdefault func dict_setdefault(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { var key, dflt Value = nil, None if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &key, &dflt); err != nil { return nil, err } dict := b.Receiver().(*Dict) if v, ok, err := dict.Get(key); err != nil { return nil, nameErr(b, err) } else if ok { return v, nil } else if err := dict.SetKey(key, dflt); err != nil { return nil, nameErr(b, err) } else { return dflt, nil } } // https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·update func dict_update(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if len(args) > 1 { return nil, fmt.Errorf("update: got %d arguments, want at most 1", len(args)) } if err := updateDict(b.Receiver().(*Dict), args, kwargs); err != nil { return nil, fmt.Errorf("update: %v", err) } return None, nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#dict·update func dict_values(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { return nil, err } items := b.Receiver().(*Dict).Items() res := make([]Value, len(items)) for i, item := range items { res[i] = item[1] } return NewList(res), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#list·append func list_append(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { var object Value if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &object); err != nil { return nil, err } recv := b.Receiver().(*List) if err := recv.checkMutable("append to"); err != nil { return nil, nameErr(b, err) } recv.elems = append(recv.elems, object) return None, nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#list·clear func list_clear(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { return nil, err } if err := b.Receiver().(*List).Clear(); err != nil { return nil, nameErr(b, err) } return None, nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#list·extend func list_extend(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { recv := b.Receiver().(*List) var iterable Iterable if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &iterable); err != nil { return nil, err } if err := recv.checkMutable("extend"); err != nil { return nil, nameErr(b, err) } listExtend(recv, iterable) return None, nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#list·index func list_index(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { var value, start_, end_ Value if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &value, &start_, &end_); err != nil { return nil, err } recv := b.Receiver().(*List) start, end, err := indices(start_, end_, recv.Len()) if err != nil { return nil, nameErr(b, err) } for i := start; i < end; i++ { if eq, err := Equal(recv.elems[i], value); err != nil { return nil, nameErr(b, err) } else if eq { return MakeInt(i), nil } } return nil, nameErr(b, "value not in list") } // https://github.com/google/starlark-go/blob/master/doc/spec.md#list·insert func list_insert(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { recv := b.Receiver().(*List) var index int var object Value if err := UnpackPositionalArgs(b.Name(), args, kwargs, 2, &index, &object); err != nil { return nil, err } if err := recv.checkMutable("insert into"); err != nil { return nil, nameErr(b, err) } if index < 0 { index += recv.Len() } if index >= recv.Len() { // end recv.elems = append(recv.elems, object) } else { if index < 0 { index = 0 // start } recv.elems = append(recv.elems, nil) copy(recv.elems[index+1:], recv.elems[index:]) // slide up one recv.elems[index] = object } return None, nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#list·remove func list_remove(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { recv := b.Receiver().(*List) var value Value if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &value); err != nil { return nil, err } if err := recv.checkMutable("remove from"); err != nil { return nil, nameErr(b, err) } for i, elem := range recv.elems { if eq, err := Equal(elem, value); err != nil { return nil, fmt.Errorf("remove: %v", err) } else if eq { recv.elems = append(recv.elems[:i], recv.elems[i+1:]...) return None, nil } } return nil, fmt.Errorf("remove: element not found") } // https://github.com/google/starlark-go/blob/master/doc/spec.md#list·pop func list_pop(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { recv := b.Receiver() list := recv.(*List) n := list.Len() i := n - 1 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0, &i); err != nil { return nil, err } origI := i if i < 0 { i += n } if i < 0 || i >= n { return nil, nameErr(b, outOfRange(origI, n, list)) } if err := list.checkMutable("pop from"); err != nil { return nil, nameErr(b, err) } res := list.elems[i] list.elems = append(list.elems[:i], list.elems[i+1:]...) return res, nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#string·capitalize func string_capitalize(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { return nil, err } s := string(b.Receiver().(String)) res := new(strings.Builder) res.Grow(len(s)) for i, r := range s { if i == 0 { r = unicode.ToTitle(r) } else { r = unicode.ToLower(r) } res.WriteRune(r) } return String(res.String()), nil } // string_iterable returns an unspecified iterable value whose iterator yields: // - elems: successive 1-byte substrings // - codepoints: successive substrings that encode a single Unicode code point. // - elem_ords: numeric values of successive bytes // - codepoint_ords: numeric values of successive Unicode code points func string_iterable(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { return nil, err } return stringIterable{ s: b.Receiver().(String), ords: b.Name()[len(b.Name())-2] == 'd', codepoints: b.Name()[0] == 'c', }, nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#string·count func string_count(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { var sub string var start_, end_ Value if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &sub, &start_, &end_); err != nil { return nil, err } recv := string(b.Receiver().(String)) start, end, err := indices(start_, end_, len(recv)) if err != nil { return nil, nameErr(b, err) } var slice string if start < end { slice = recv[start:end] } return MakeInt(strings.Count(slice, sub)), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#string·isalnum func string_isalnum(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { return nil, err } recv := string(b.Receiver().(String)) for _, r := range recv { if !unicode.IsLetter(r) && !unicode.IsDigit(r) { return False, nil } } return Bool(recv != ""), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#string·isalpha func string_isalpha(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { return nil, err } recv := string(b.Receiver().(String)) for _, r := range recv { if !unicode.IsLetter(r) { return False, nil } } return Bool(recv != ""), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#string·isdigit func string_isdigit(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { return nil, err } recv := string(b.Receiver().(String)) for _, r := range recv { if !unicode.IsDigit(r) { return False, nil } } return Bool(recv != ""), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#string·islower func string_islower(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { return nil, err } recv := string(b.Receiver().(String)) return Bool(isCasedString(recv) && recv == strings.ToLower(recv)), nil } // isCasedString reports whether its argument contains any cased code points. func isCasedString(s string) bool { for _, r := range s { if isCasedRune(r) { return true } } return false } func isCasedRune(r rune) bool { // It's unclear what the correct behavior is for a rune such as 'ffi', // a lowercase letter with no upper or title case and no SimpleFold. return 'a' <= r && r <= 'z' || 'A' <= r && r <= 'Z' || unicode.SimpleFold(r) != r } // https://github.com/google/starlark-go/blob/master/doc/spec.md#string·isspace func string_isspace(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { return nil, err } recv := string(b.Receiver().(String)) for _, r := range recv { if !unicode.IsSpace(r) { return False, nil } } return Bool(recv != ""), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#string·istitle func string_istitle(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { return nil, err } recv := string(b.Receiver().(String)) // Python semantics differ from x==strings.{To,}Title(x) in Go: // "uppercase characters may only follow uncased characters and // lowercase characters only cased ones." var cased, prevCased bool for _, r := range recv { if 'A' <= r && r <= 'Z' || unicode.IsTitle(r) { // e.g. "Dž" if prevCased { return False, nil } prevCased = true cased = true } else if unicode.IsLower(r) { if !prevCased { return False, nil } prevCased = true cased = true } else if unicode.IsUpper(r) { return False, nil } else { prevCased = false } } return Bool(cased), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#string·isupper func string_isupper(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { return nil, err } recv := string(b.Receiver().(String)) return Bool(isCasedString(recv) && recv == strings.ToUpper(recv)), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#string·find func string_find(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { return string_find_impl(b, args, kwargs, true, false) } // https://github.com/google/starlark-go/blob/master/doc/spec.md#string·format func string_format(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { format := string(b.Receiver().(String)) var auto, manual bool // kinds of positional indexing used buf := new(strings.Builder) index := 0 for { literal := format i := strings.IndexByte(format, '{') if i >= 0 { literal = format[:i] } // Replace "}}" with "}" in non-field portion, rejecting a lone '}'. for { j := strings.IndexByte(literal, '}') if j < 0 { buf.WriteString(literal) break } if len(literal) == j+1 || literal[j+1] != '}' { return nil, fmt.Errorf("format: single '}' in format") } buf.WriteString(literal[:j+1]) literal = literal[j+2:] } if i < 0 { break // end of format string } if i+1 < len(format) && format[i+1] == '{' { // "{{" means a literal '{' buf.WriteByte('{') format = format[i+2:] continue } format = format[i+1:] i = strings.IndexByte(format, '}') if i < 0 { return nil, fmt.Errorf("format: unmatched '{' in format") } var arg Value conv := "s" var spec string field := format[:i] format = format[i+1:] var name string if i := strings.IndexByte(field, '!'); i < 0 { // "name" or "name:spec" if i := strings.IndexByte(field, ':'); i < 0 { name = field } else { name = field[:i] spec = field[i+1:] } } else { // "name!conv" or "name!conv:spec" name = field[:i] field = field[i+1:] // "conv" or "conv:spec" if i := strings.IndexByte(field, ':'); i < 0 { conv = field } else { conv = field[:i] spec = field[i+1:] } } if name == "" { // "{}": automatic indexing if manual { return nil, fmt.Errorf("format: cannot switch from manual field specification to automatic field numbering") } auto = true if index >= len(args) { return nil, fmt.Errorf("format: tuple index out of range") } arg = args[index] index++ } else if num, ok := decimal(name); ok { // positional argument if auto { return nil, fmt.Errorf("format: cannot switch from automatic field numbering to manual field specification") } manual = true if num >= len(args) { return nil, fmt.Errorf("format: tuple index out of range") } else { arg = args[num] } } else { // keyword argument for _, kv := range kwargs { if string(kv[0].(String)) == name { arg = kv[1] break } } if arg == nil { // Starlark does not support Python's x.y or a[i] syntaxes, // or nested use of {...}. if strings.Contains(name, ".") { return nil, fmt.Errorf("format: attribute syntax x.y is not supported in replacement fields: %s", name) } if strings.Contains(name, "[") { return nil, fmt.Errorf("format: element syntax a[i] is not supported in replacement fields: %s", name) } if strings.Contains(name, "{") { return nil, fmt.Errorf("format: nested replacement fields not supported") } return nil, fmt.Errorf("format: keyword %s not found", name) } } if spec != "" { // Starlark does not support Python's format_spec features. return nil, fmt.Errorf("format spec features not supported in replacement fields: %s", spec) } switch conv { case "s": if str, ok := AsString(arg); ok { buf.WriteString(str) } else { writeValue(buf, arg, nil) } case "r": writeValue(buf, arg, nil) default: return nil, fmt.Errorf("format: unknown conversion %q", conv) } } return String(buf.String()), nil } // decimal interprets s as a sequence of decimal digits. func decimal(s string) (x int, ok bool) { n := len(s) for i := 0; i < n; i++ { digit := s[i] - '0' if digit > 9 { return 0, false } x = x*10 + int(digit) if x < 0 { return 0, false // underflow } } return x, true } // https://github.com/google/starlark-go/blob/master/doc/spec.md#string·index func string_index(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { return string_find_impl(b, args, kwargs, false, false) } // https://github.com/google/starlark-go/blob/master/doc/spec.md#string·join func string_join(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { recv := string(b.Receiver().(String)) var iterable Iterable if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &iterable); err != nil { return nil, err } iter := iterable.Iterate() defer iter.Done() buf := new(strings.Builder) var x Value for i := 0; iter.Next(&x); i++ { if i > 0 { buf.WriteString(recv) } s, ok := AsString(x) if !ok { return nil, fmt.Errorf("join: in list, want string, got %s", x.Type()) } buf.WriteString(s) } return String(buf.String()), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#string·lower func string_lower(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { return nil, err } return String(strings.ToLower(string(b.Receiver().(String)))), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#string·partition func string_partition(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { recv := string(b.Receiver().(String)) var sep string if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &sep); err != nil { return nil, err } if sep == "" { return nil, nameErr(b, "empty separator") } var i int if b.Name()[0] == 'p' { i = strings.Index(recv, sep) // partition } else { i = strings.LastIndex(recv, sep) // rpartition } tuple := make(Tuple, 0, 3) if i < 0 { if b.Name()[0] == 'p' { tuple = append(tuple, String(recv), String(""), String("")) } else { tuple = append(tuple, String(""), String(""), String(recv)) } } else { tuple = append(tuple, String(recv[:i]), String(sep), String(recv[i+len(sep):])) } return tuple, nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#string·replace func string_replace(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { recv := string(b.Receiver().(String)) var old, new string count := -1 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 2, &old, &new, &count); err != nil { return nil, err } return String(strings.Replace(recv, old, new, count)), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#string·rfind func string_rfind(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { return string_find_impl(b, args, kwargs, true, true) } // https://github.com/google/starlark-go/blob/master/doc/spec.md#string·rindex func string_rindex(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { return string_find_impl(b, args, kwargs, false, true) } // https://github.com/google/starlark-go/starlark/blob/master/doc/spec.md#string·startswith // https://github.com/google/starlark-go/starlark/blob/master/doc/spec.md#string·endswith func string_startswith(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { var x Value var start, end Value = None, None if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &x, &start, &end); err != nil { return nil, err } // compute effective substring. s := string(b.Receiver().(String)) if start, end, err := indices(start, end, len(s)); err != nil { return nil, nameErr(b, err) } else { if end < start { end = start // => empty result } s = s[start:end] } f := strings.HasPrefix if b.Name()[0] == 'e' { // endswith f = strings.HasSuffix } switch x := x.(type) { case Tuple: for i, x := range x { prefix, ok := AsString(x) if !ok { return nil, fmt.Errorf("%s: want string, got %s, for element %d", b.Name(), x.Type(), i) } if f(s, prefix) { return True, nil } } return False, nil case String: return Bool(f(s, string(x))), nil } return nil, fmt.Errorf("%s: got %s, want string or tuple of string", b.Name(), x.Type()) } // https://github.com/google/starlark-go/blob/master/doc/spec.md#string·strip // https://github.com/google/starlark-go/blob/master/doc/spec.md#string·lstrip // https://github.com/google/starlark-go/blob/master/doc/spec.md#string·rstrip func string_strip(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { var chars string if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0, &chars); err != nil { return nil, err } recv := string(b.Receiver().(String)) var s string switch b.Name()[0] { case 's': // strip if chars != "" { s = strings.Trim(recv, chars) } else { s = strings.TrimSpace(recv) } case 'l': // lstrip if chars != "" { s = strings.TrimLeft(recv, chars) } else { s = strings.TrimLeftFunc(recv, unicode.IsSpace) } case 'r': // rstrip if chars != "" { s = strings.TrimRight(recv, chars) } else { s = strings.TrimRightFunc(recv, unicode.IsSpace) } } return String(s), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#string·title func string_title(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { return nil, err } s := string(b.Receiver().(String)) // Python semantics differ from x==strings.{To,}Title(x) in Go: // "uppercase characters may only follow uncased characters and // lowercase characters only cased ones." buf := new(strings.Builder) buf.Grow(len(s)) var prevCased bool for _, r := range s { if prevCased { r = unicode.ToLower(r) } else { r = unicode.ToTitle(r) } prevCased = isCasedRune(r) buf.WriteRune(r) } return String(buf.String()), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#string·upper func string_upper(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil { return nil, err } return String(strings.ToUpper(string(b.Receiver().(String)))), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#string·split // https://github.com/google/starlark-go/blob/master/doc/spec.md#string·rsplit func string_split(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { recv := string(b.Receiver().(String)) var sep_ Value maxsplit := -1 if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0, &sep_, &maxsplit); err != nil { return nil, err } var res []string if sep_ == nil || sep_ == None { // special case: split on whitespace if maxsplit < 0 { res = strings.Fields(recv) } else if b.Name() == "split" { res = splitspace(recv, maxsplit) } else { // rsplit res = rsplitspace(recv, maxsplit) } } else if sep, ok := AsString(sep_); ok { if sep == "" { return nil, fmt.Errorf("split: empty separator") } // usual case: split on non-empty separator if maxsplit < 0 { res = strings.Split(recv, sep) } else if b.Name() == "split" { res = strings.SplitN(recv, sep, maxsplit+1) } else { // rsplit res = strings.Split(recv, sep) if excess := len(res) - maxsplit; excess > 0 { res[0] = strings.Join(res[:excess], sep) res = append(res[:1], res[excess:]...) } } } else { return nil, fmt.Errorf("split: got %s for separator, want string", sep_.Type()) } list := make([]Value, len(res)) for i, x := range res { list[i] = String(x) } return NewList(list), nil } // Precondition: max >= 0. func rsplitspace(s string, max int) []string { res := make([]string, 0, max+1) end := -1 // index of field end, or -1 in a region of spaces. for i := len(s); i > 0; { r, sz := utf8.DecodeLastRuneInString(s[:i]) if unicode.IsSpace(r) { if end >= 0 { if len(res) == max { break // let this field run to the start } res = append(res, s[i:end]) end = -1 } } else if end < 0 { end = i } i -= sz } if end >= 0 { res = append(res, s[:end]) } resLen := len(res) for i := 0; i < resLen/2; i++ { res[i], res[resLen-1-i] = res[resLen-1-i], res[i] } return res } // Precondition: max >= 0. func splitspace(s string, max int) []string { var res []string start := -1 // index of field start, or -1 in a region of spaces for i, r := range s { if unicode.IsSpace(r) { if start >= 0 { if len(res) == max { break // let this field run to the end } res = append(res, s[start:i]) start = -1 } } else if start == -1 { start = i } } if start >= 0 { res = append(res, s[start:]) } return res } // https://github.com/google/starlark-go/blob/master/doc/spec.md#string·splitlines func string_splitlines(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { var keepends bool if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0, &keepends); err != nil { return nil, err } var lines []string if s := string(b.Receiver().(String)); s != "" { // TODO(adonovan): handle CRLF correctly. if keepends { lines = strings.SplitAfter(s, "\n") } else { lines = strings.Split(s, "\n") } if strings.HasSuffix(s, "\n") { lines = lines[:len(lines)-1] } } list := make([]Value, len(lines)) for i, x := range lines { list[i] = String(x) } return NewList(list), nil } // https://github.com/google/starlark-go/blob/master/doc/spec.md#set·union. func set_union(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) { var iterable Iterable if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0, &iterable); err != nil { return nil, err } iter := iterable.Iterate() defer iter.Done() union, err := b.Receiver().(*Set).Union(iter) if err != nil { return nil, nameErr(b, err) } return union, nil } // Common implementation of string_{r}{find,index}. func string_find_impl(b *Builtin, args Tuple, kwargs []Tuple, allowError, last bool) (Value, error) { var sub string var start_, end_ Value if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &sub, &start_, &end_); err != nil { return nil, err } s := string(b.Receiver().(String)) start, end, err := indices(start_, end_, len(s)) if err != nil { return nil, nameErr(b, err) } var slice string if start < end { slice = s[start:end] } var i int if last { i = strings.LastIndex(slice, sub) } else { i = strings.Index(slice, sub) } if i < 0 { if !allowError { return nil, nameErr(b, "substring not found") } return MakeInt(-1), nil } return MakeInt(i + start), nil } // Common implementation of builtin dict function and dict.update method. // Precondition: len(updates) == 0 or 1. func updateDict(dict *Dict, updates Tuple, kwargs []Tuple) error { if len(updates) == 1 { switch updates := updates[0].(type) { case IterableMapping: // Iterate over dict's key/value pairs, not just keys. for _, item := range updates.Items() { if err := dict.SetKey(item[0], item[1]); err != nil { return err // dict is frozen } } default: // all other sequences iter := Iterate(updates) if iter == nil { return fmt.Errorf("got %s, want iterable", updates.Type()) } defer iter.Done() var pair Value for i := 0; iter.Next(&pair); i++ { iter2 := Iterate(pair) if iter2 == nil { return fmt.Errorf("dictionary update sequence element #%d is not iterable (%s)", i, pair.Type()) } defer iter2.Done() len := Len(pair) if len < 0 { return fmt.Errorf("dictionary update sequence element #%d has unknown length (%s)", i, pair.Type()) } else if len != 2 { return fmt.Errorf("dictionary update sequence element #%d has length %d, want 2", i, len) } var k, v Value iter2.Next(&k) iter2.Next(&v) if err := dict.SetKey(k, v); err != nil { return err } } } } // Then add the kwargs. before := dict.Len() for _, pair := range kwargs { if err := dict.SetKey(pair[0], pair[1]); err != nil { return err // dict is frozen } } // In the common case, each kwarg will add another dict entry. // If that's not so, check whether it is because there was a duplicate kwarg. if dict.Len() < before+len(kwargs) { keys := make(map[String]bool, len(kwargs)) for _, kv := range kwargs { k := kv[0].(String) if keys[k] { return fmt.Errorf("duplicate keyword arg: %v", k) } keys[k] = true } } return nil } // nameErr returns an error message of the form "name: msg" // where name is b.Name() and msg is a string or error. func nameErr(b *Builtin, msg interface{}) error { return fmt.Errorf("%s: %v", b.Name(), msg) } ================================================ FILE: vendor/go.starlark.net/starlark/profile.go ================================================ // Copyright 2019 The Bazel Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package starlark // This file defines a simple execution-time profiler for Starlark. // It measures the wall time spent executing Starlark code, and emits a // gzipped protocol message in pprof format (github.com/google/pprof). // // When profiling is enabled, the interpreter calls the profiler to // indicate the start and end of each "span" or time interval. A leaf // function (whether Go or Starlark) has a single span. A function that // calls another function has spans for each interval in which it is the // top of the stack. (A LOAD instruction also ends a span.) // // At the start of a span, the interpreter records the current time in // the thread's topmost frame. At the end of the span, it obtains the // time again and subtracts the span start time. The difference is added // to an accumulator variable in the thread. If the accumulator exceeds // some fixed quantum (10ms, say), the profiler records the current call // stack and sends it to the profiler goroutine, along with the number // of quanta, which are subtracted. For example, if the accumulator // holds 3ms and then a completed span adds 25ms to it, its value is 28ms, // which exceeeds 10ms. The profiler records a stack with the value 20ms // (2 quanta), and the accumulator is left with 8ms. // // The profiler goroutine converts the stacks into the pprof format and // emits a gzip-compressed protocol message to the designated output // file. We use a hand-written streaming proto encoder to avoid // dependencies on pprof and proto, and to avoid the need to // materialize the profile data structure in memory. // // A limitation of this profiler is that it measures wall time, which // does not necessarily correspond to CPU time. A CPU profiler requires // that only running (not runnable) threads are sampled; this is // commonly achieved by having the kernel deliver a (PROF) signal to an // arbitrary running thread, through setitimer(2). The CPU profiler in the // Go runtime uses this mechanism, but it is not possible for a Go // application to register a SIGPROF handler, nor is it possible for a // Go handler for some other signal to read the stack pointer of // the interrupted thread. // // Two caveats: // (1) it is tempting to send the leaf Frame directly to the profiler // goroutine instead of making a copy of the stack, since a Frame is a // spaghetti stack--a linked list. However, as soon as execution // resumes, the stack's Frame.pc values may be mutated, so Frames are // not safe to share with the asynchronous profiler goroutine. // (2) it is tempting to use Callables as keys in a map when tabulating // the pprof protocols's Function entities. However, we cannot assume // that Callables are valid map keys, and furthermore we must not // pin function values in memory indefinitely as this may cause lambda // values to keep their free variables live much longer than necessary. // TODO(adonovan): // - make Start/Stop fully thread-safe. // - fix the pc hack. // - experiment with other values of quantum. import ( "bufio" "bytes" "compress/gzip" "encoding/binary" "fmt" "io" "log" "reflect" "sync/atomic" "time" "unsafe" "go.starlark.net/syntax" ) // StartProfile enables time profiling of all Starlark threads, // and writes a profile in pprof format to w. // It must be followed by a call to StopProfiler to stop // the profiler and finalize the profile. // // StartProfile returns an error if profiling was already enabled. // // StartProfile must not be called concurrently with Starlark execution. func StartProfile(w io.Writer) error { if !atomic.CompareAndSwapUint32(&profiler.on, 0, 1) { return fmt.Errorf("profiler already running") } // TODO(adonovan): make the API fully concurrency-safe. // The main challenge is racy reads/writes of profiler.events, // and of send/close races on the channel it refers to. // It's easy to solve them with a mutex but harder to do // it efficiently. profiler.events = make(chan *profEvent, 1) profiler.done = make(chan error) go profile(w) return nil } // StopProfiler stops the profiler started by a prior call to // StartProfile and finalizes the profile. It returns an error if the // profile could not be completed. // // StopProfiler must not be called concurrently with Starlark execution. func StopProfile() error { // Terminate the profiler goroutine and get its result. close(profiler.events) err := <-profiler.done profiler.done = nil profiler.events = nil atomic.StoreUint32(&profiler.on, 0) return err } // globals var profiler struct { on uint32 // nonzero => profiler running events chan *profEvent // profile events from interpreter threads done chan error // indicates profiler goroutine is ready } func (thread *Thread) beginProfSpan() { if profiler.events == nil { return // profiling not enabled } thread.frameAt(0).spanStart = nanotime() } // TODO(adonovan): experiment with smaller values, // which trade space and time for greater precision. const quantum = 10 * time.Millisecond func (thread *Thread) endProfSpan() { if profiler.events == nil { return // profiling not enabled } // Add the span to the thread's accumulator. thread.proftime += time.Duration(nanotime() - thread.frameAt(0).spanStart) if thread.proftime < quantum { return } // Only record complete quanta. n := thread.proftime / quantum thread.proftime -= n * quantum // Copy the stack. // (We can't save thread.frame because its pc will change.) ev := &profEvent{ thread: thread, time: n * quantum, } ev.stack = ev.stackSpace[:0] for i := range thread.stack { fr := thread.frameAt(i) ev.stack = append(ev.stack, profFrame{ pos: fr.Position(), fn: fr.Callable(), pc: fr.pc, }) } profiler.events <- ev } type profEvent struct { thread *Thread // currently unused time time.Duration stack []profFrame stackSpace [8]profFrame // initial space for stack } type profFrame struct { fn Callable // don't hold this live for too long (prevents GC of lambdas) pc uint32 // program counter (Starlark frames only) pos syntax.Position // position of pc within this frame } // profile is the profiler goroutine. // It runs until StopProfiler is called. func profile(w io.Writer) { // Field numbers from pprof protocol. // See https://github.com/google/pprof/blob/master/proto/profile.proto const ( Profile_sample_type = 1 // repeated ValueType Profile_sample = 2 // repeated Sample Profile_mapping = 3 // repeated Mapping Profile_location = 4 // repeated Location Profile_function = 5 // repeated Function Profile_string_table = 6 // repeated string Profile_time_nanos = 9 // int64 Profile_duration_nanos = 10 // int64 Profile_period_type = 11 // ValueType Profile_period = 12 // int64 ValueType_type = 1 // int64 ValueType_unit = 2 // int64 Sample_location_id = 1 // repeated uint64 Sample_value = 2 // repeated int64 Sample_label = 3 // repeated Label Label_key = 1 // int64 Label_str = 2 // int64 Label_num = 3 // int64 Label_num_unit = 4 // int64 Location_id = 1 // uint64 Location_mapping_id = 2 // uint64 Location_address = 3 // uint64 Location_line = 4 // repeated Line Line_function_id = 1 // uint64 Line_line = 2 // int64 Function_id = 1 // uint64 Function_name = 2 // int64 Function_system_name = 3 // int64 Function_filename = 4 // int64 Function_start_line = 5 // int64 ) bufw := bufio.NewWriter(w) // write file in 4KB (not 240B flate-sized) chunks gz := gzip.NewWriter(bufw) enc := protoEncoder{w: gz} // strings stringIndex := make(map[string]int64) str := func(s string) int64 { i, ok := stringIndex[s] if !ok { i = int64(len(stringIndex)) enc.string(Profile_string_table, s) stringIndex[s] = i } return i } str("") // entry 0 // functions // // function returns the ID of a Callable for use in Line.FunctionId. // The ID is the same as the function's logical address, // which is supplied by the caller to avoid the need to recompute it. functionId := make(map[uintptr]uint64) function := func(fn Callable, addr uintptr) uint64 { id, ok := functionId[addr] if !ok { id = uint64(addr) var pos syntax.Position if fn, ok := fn.(callableWithPosition); ok { pos = fn.Position() } name := fn.Name() if name == "" { name = pos.Filename() } nameIndex := str(name) fun := new(bytes.Buffer) funenc := protoEncoder{w: fun} funenc.uint(Function_id, id) funenc.int(Function_name, nameIndex) funenc.int(Function_system_name, nameIndex) funenc.int(Function_filename, str(pos.Filename())) funenc.int(Function_start_line, int64(pos.Line)) enc.bytes(Profile_function, fun.Bytes()) functionId[addr] = id } return id } // locations // // location returns the ID of the location denoted by fr. // For Starlark frames, this is the Frame pc. locationId := make(map[uintptr]uint64) location := func(fr profFrame) uint64 { fnAddr := profFuncAddr(fr.fn) // For Starlark functions, the frame position // represents the current PC value. // Mix it into the low bits of the address. // This is super hacky and may result in collisions // in large functions or if functions are numerous. // TODO(adonovan): fix: try making this cleaner by treating // each bytecode segment as a Profile.Mapping. pcAddr := fnAddr if _, ok := fr.fn.(*Function); ok { pcAddr = (pcAddr << 16) ^ uintptr(fr.pc) } id, ok := locationId[pcAddr] if !ok { id = uint64(pcAddr) line := new(bytes.Buffer) lineenc := protoEncoder{w: line} lineenc.uint(Line_function_id, function(fr.fn, fnAddr)) lineenc.int(Line_line, int64(fr.pos.Line)) loc := new(bytes.Buffer) locenc := protoEncoder{w: loc} locenc.uint(Location_id, id) locenc.uint(Location_address, uint64(pcAddr)) locenc.bytes(Location_line, line.Bytes()) enc.bytes(Profile_location, loc.Bytes()) locationId[pcAddr] = id } return id } wallNanos := new(bytes.Buffer) wnenc := protoEncoder{w: wallNanos} wnenc.int(ValueType_type, str("wall")) wnenc.int(ValueType_unit, str("nanoseconds")) // informational fields of Profile enc.bytes(Profile_sample_type, wallNanos.Bytes()) enc.int(Profile_period, quantum.Nanoseconds()) // magnitude of sampling period enc.bytes(Profile_period_type, wallNanos.Bytes()) // dimension and unit of period enc.int(Profile_time_nanos, time.Now().UnixNano()) // start (real) time of profile startNano := nanotime() // Read profile events from the channel // until it is closed by StopProfiler. for e := range profiler.events { sample := new(bytes.Buffer) sampleenc := protoEncoder{w: sample} sampleenc.int(Sample_value, e.time.Nanoseconds()) // wall nanoseconds for _, fr := range e.stack { sampleenc.uint(Sample_location_id, location(fr)) } enc.bytes(Profile_sample, sample.Bytes()) } endNano := nanotime() enc.int(Profile_duration_nanos, endNano-startNano) err := gz.Close() // Close reports any prior write error if flushErr := bufw.Flush(); err == nil { err = flushErr } profiler.done <- err } // nanotime returns the time in nanoseconds since epoch. // It is implemented by runtime.nanotime using the linkname hack; // runtime.nanotime is defined for all OSs/ARCHS and uses the // monotonic system clock, which there is no portable way to access. // Should that function ever go away, these alternatives exist: // // // POSIX only. REALTIME not MONOTONIC. 17ns. // var tv syscall.Timeval // syscall.Gettimeofday(&tv) // can't fail // return tv.Nano() // // // Portable. REALTIME not MONOTONIC. 46ns. // return time.Now().Nanoseconds() // // // POSIX only. Adds a dependency. // import "golang.org/x/sys/unix" // var ts unix.Timespec // unix.ClockGettime(CLOCK_MONOTONIC, &ts) // can't fail // return unix.TimespecToNsec(ts) // //go:linkname nanotime runtime.nanotime func nanotime() int64 // profFuncAddr returns the canonical "address" // of a Callable for use by the profiler. func profFuncAddr(fn Callable) uintptr { switch fn := fn.(type) { case *Builtin: return reflect.ValueOf(fn.fn).Pointer() case *Function: return uintptr(unsafe.Pointer(fn.funcode)) } // User-defined callable types are typically of // of kind pointer-to-struct. Handle them specially. if v := reflect.ValueOf(fn); v.Type().Kind() == reflect.Ptr { return v.Pointer() } // Address zero is reserved by the protocol. // Use 1 for callables we don't recognize. log.Printf("Starlark profiler: no address for Callable %T", fn) return 1 } // We encode the protocol message by hand to avoid making // the interpreter depend on both github.com/google/pprof // and github.com/golang/protobuf. // // This also avoids the need to materialize a protocol message object // tree of unbounded size and serialize it all at the end. // The pprof format appears to have been designed to // permit streaming implementations such as this one. // // See https://developers.google.com/protocol-buffers/docs/encoding. type protoEncoder struct { w io.Writer // *bytes.Buffer or *gzip.Writer tmp [binary.MaxVarintLen64]byte } func (e *protoEncoder) uvarint(x uint64) { n := binary.PutUvarint(e.tmp[:], x) e.w.Write(e.tmp[:n]) } func (e *protoEncoder) tag(field, wire uint) { e.uvarint(uint64(field<<3 | wire)) } func (e *protoEncoder) string(field uint, s string) { e.tag(field, 2) // length-delimited e.uvarint(uint64(len(s))) io.WriteString(e.w, s) } func (e *protoEncoder) bytes(field uint, b []byte) { e.tag(field, 2) // length-delimited e.uvarint(uint64(len(b))) e.w.Write(b) } func (e *protoEncoder) uint(field uint, x uint64) { e.tag(field, 0) // varint e.uvarint(x) } func (e *protoEncoder) int(field uint, x int64) { e.tag(field, 0) // varint e.uvarint(uint64(x)) } ================================================ FILE: vendor/go.starlark.net/starlark/unpack.go ================================================ package starlark // This file defines the Unpack helper functions used by // built-in functions to interpret their call arguments. import ( "fmt" "log" "reflect" "strings" ) // UnpackArgs unpacks the positional and keyword arguments into the // supplied parameter variables. pairs is an alternating list of names // and pointers to variables. // // If the variable is a bool, int, string, *List, *Dict, Callable, // Iterable, or user-defined implementation of Value, // UnpackArgs performs the appropriate type check. // An int uses the AsInt32 check. // If the parameter name ends with "?", // it and all following parameters are optional. // // If the variable implements Value, UnpackArgs may call // its Type() method while constructing the error message. // // Beware: an optional *List, *Dict, Callable, Iterable, or Value variable that is // not assigned is not a valid Starlark Value, so the caller must // explicitly handle such cases by interpreting nil as None or some // computed default. func UnpackArgs(fnname string, args Tuple, kwargs []Tuple, pairs ...interface{}) error { nparams := len(pairs) / 2 var defined intset defined.init(nparams) paramName := func(x interface{}) string { // (no free variables) name := x.(string) if name[len(name)-1] == '?' { name = name[:len(name)-1] } return name } // positional arguments if len(args) > nparams { return fmt.Errorf("%s: got %d arguments, want at most %d", fnname, len(args), nparams) } for i, arg := range args { defined.set(i) if err := unpackOneArg(arg, pairs[2*i+1]); err != nil { name := paramName(pairs[2*i]) return fmt.Errorf("%s: for parameter %s: %s", fnname, name, err) } } // keyword arguments kwloop: for _, item := range kwargs { name, arg := item[0].(String), item[1] for i := 0; i < nparams; i++ { if paramName(pairs[2*i]) == string(name) { // found it if defined.set(i) { return fmt.Errorf("%s: got multiple values for keyword argument %s", fnname, name) } ptr := pairs[2*i+1] if err := unpackOneArg(arg, ptr); err != nil { return fmt.Errorf("%s: for parameter %s: %s", fnname, name, err) } continue kwloop } } return fmt.Errorf("%s: unexpected keyword argument %s", fnname, name) } // Check that all non-optional parameters are defined. // (We needn't check the first len(args).) for i := len(args); i < nparams; i++ { name := pairs[2*i].(string) if strings.HasSuffix(name, "?") { break // optional } if !defined.get(i) { return fmt.Errorf("%s: missing argument for %s", fnname, name) } } return nil } // UnpackPositionalArgs unpacks the positional arguments into // corresponding variables. Each element of vars is a pointer; see // UnpackArgs for allowed types and conversions. // // UnpackPositionalArgs reports an error if the number of arguments is // less than min or greater than len(vars), if kwargs is nonempty, or if // any conversion fails. func UnpackPositionalArgs(fnname string, args Tuple, kwargs []Tuple, min int, vars ...interface{}) error { if len(kwargs) > 0 { return fmt.Errorf("%s: unexpected keyword arguments", fnname) } max := len(vars) if len(args) < min { var atleast string if min < max { atleast = "at least " } return fmt.Errorf("%s: got %d arguments, want %s%d", fnname, len(args), atleast, min) } if len(args) > max { var atmost string if max > min { atmost = "at most " } return fmt.Errorf("%s: got %d arguments, want %s%d", fnname, len(args), atmost, max) } for i, arg := range args { if err := unpackOneArg(arg, vars[i]); err != nil { return fmt.Errorf("%s: for parameter %d: %s", fnname, i+1, err) } } return nil } func unpackOneArg(v Value, ptr interface{}) error { // On failure, don't clobber *ptr. switch ptr := ptr.(type) { case *Value: *ptr = v case *string: s, ok := AsString(v) if !ok { return fmt.Errorf("got %s, want string", v.Type()) } *ptr = s case *bool: b, ok := v.(Bool) if !ok { return fmt.Errorf("got %s, want bool", v.Type()) } *ptr = bool(b) case *int: i, err := AsInt32(v) if err != nil { return err } *ptr = i case **List: list, ok := v.(*List) if !ok { return fmt.Errorf("got %s, want list", v.Type()) } *ptr = list case **Dict: dict, ok := v.(*Dict) if !ok { return fmt.Errorf("got %s, want dict", v.Type()) } *ptr = dict case *Callable: f, ok := v.(Callable) if !ok { return fmt.Errorf("got %s, want callable", v.Type()) } *ptr = f case *Iterable: it, ok := v.(Iterable) if !ok { return fmt.Errorf("got %s, want iterable", v.Type()) } *ptr = it default: // v must have type *V, where V is some subtype of starlark.Value. ptrv := reflect.ValueOf(ptr) if ptrv.Kind() != reflect.Ptr { log.Panicf("internal error: not a pointer: %T", ptr) } paramVar := ptrv.Elem() if !reflect.TypeOf(v).AssignableTo(paramVar.Type()) { // The value is not assignable to the variable. // Detect a possible bug in the Go program that called Unpack: // If the variable *ptr is not a subtype of Value, // no value of v can possibly work. if !paramVar.Type().AssignableTo(reflect.TypeOf(new(Value)).Elem()) { log.Panicf("pointer element type does not implement Value: %T", ptr) } // Report Starlark dynamic type error. // // We prefer the Starlark Value.Type name over // its Go reflect.Type name, but calling the // Value.Type method on the variable is not safe // in general. If the variable is an interface, // the call will fail. Even if the variable has // a concrete type, it might not be safe to call // Type() on a zero instance. Thus we must use // recover. // Default to Go reflect.Type name paramType := paramVar.Type().String() // Attempt to call Value.Type method. func() { defer func() { recover() }() paramType = paramVar.MethodByName("Type").Call(nil)[0].String() }() return fmt.Errorf("got %s, want %s", v.Type(), paramType) } paramVar.Set(reflect.ValueOf(v)) } return nil } type intset struct { small uint64 // bitset, used if n < 64 large map[int]bool // set, used if n >= 64 } func (is *intset) init(n int) { if n >= 64 { is.large = make(map[int]bool) } } func (is *intset) set(i int) (prev bool) { if is.large == nil { prev = is.small&(1< Hash(x) == Hash(y). // Hash may fail if the value's type is not hashable, or if the value // contains a non-hashable value. The hash is used only by dictionaries and // is not exposed to the Starlark program. Hash() (uint32, error) } // A Comparable is a value that defines its own equivalence relation and // perhaps ordered comparisons. type Comparable interface { Value // CompareSameType compares one value to another of the same Type(). // The comparison operation must be one of EQL, NEQ, LT, LE, GT, or GE. // CompareSameType returns an error if an ordered comparison was // requested for a type that does not support it. // // Implementations that recursively compare subcomponents of // the value should use the CompareDepth function, not Compare, to // avoid infinite recursion on cyclic structures. // // The depth parameter is used to bound comparisons of cyclic // data structures. Implementations should decrement depth // before calling CompareDepth and should return an error if depth // < 1. // // Client code should not call this method. Instead, use the // standalone Compare or Equals functions, which are defined for // all pairs of operands. CompareSameType(op syntax.Token, y Value, depth int) (bool, error) } var ( _ Comparable = None _ Comparable = Int{} _ Comparable = False _ Comparable = Float(0) _ Comparable = String("") _ Comparable = (*Dict)(nil) _ Comparable = (*List)(nil) _ Comparable = Tuple(nil) _ Comparable = (*Set)(nil) ) // A Callable value f may be the operand of a function call, f(x). // // Clients should use the Call function, never the CallInternal method. type Callable interface { Value Name() string CallInternal(thread *Thread, args Tuple, kwargs []Tuple) (Value, error) } type callableWithPosition interface { Callable Position() syntax.Position } var ( _ Callable = (*Builtin)(nil) _ Callable = (*Function)(nil) _ callableWithPosition = (*Function)(nil) ) // An Iterable abstracts a sequence of values. // An iterable value may be iterated over by a 'for' loop or used where // any other Starlark iterable is allowed. Unlike a Sequence, the length // of an Iterable is not necessarily known in advance of iteration. type Iterable interface { Value Iterate() Iterator // must be followed by call to Iterator.Done } // A Sequence is a sequence of values of known length. type Sequence interface { Iterable Len() int } var ( _ Sequence = (*Dict)(nil) _ Sequence = (*Set)(nil) ) // An Indexable is a sequence of known length that supports efficient random access. // It is not necessarily iterable. type Indexable interface { Value Index(i int) Value // requires 0 <= i < Len() Len() int } // A Sliceable is a sequence that can be cut into pieces with the slice operator (x[i:j:step]). // // All native indexable objects are sliceable. // This is a separate interface for backwards-compatibility. type Sliceable interface { Indexable // For positive strides (step > 0), 0 <= start <= end <= n. // For negative strides (step < 0), -1 <= end <= start < n. // The caller must ensure that the start and end indices are valid // and that step is non-zero. Slice(start, end, step int) Value } // A HasSetIndex is an Indexable value whose elements may be assigned (x[i] = y). // // The implementation should not add Len to a negative index as the // evaluator does this before the call. type HasSetIndex interface { Indexable SetIndex(index int, v Value) error } var ( _ HasSetIndex = (*List)(nil) _ Indexable = Tuple(nil) _ Indexable = String("") _ Sliceable = Tuple(nil) _ Sliceable = String("") _ Sliceable = (*List)(nil) ) // An Iterator provides a sequence of values to the caller. // // The caller must call Done when the iterator is no longer needed. // Operations that modify a sequence will fail if it has active iterators. // // Example usage: // // iter := iterable.Iterator() // defer iter.Done() // var x Value // for iter.Next(&x) { // ... // } // type Iterator interface { // If the iterator is exhausted, Next returns false. // Otherwise it sets *p to the current element of the sequence, // advances the iterator, and returns true. Next(p *Value) bool Done() } // A Mapping is a mapping from keys to values, such as a dictionary. // // If a type satisfies both Mapping and Iterable, the iterator yields // the keys of the mapping. type Mapping interface { Value // Get returns the value corresponding to the specified key, // or !found if the mapping does not contain the key. // // Get also defines the behavior of "v in mapping". // The 'in' operator reports the 'found' component, ignoring errors. Get(Value) (v Value, found bool, err error) } // An IterableMapping is a mapping that supports key enumeration. type IterableMapping interface { Mapping Iterate() Iterator // see Iterable interface Items() []Tuple // a new slice containing all key/value pairs } var _ IterableMapping = (*Dict)(nil) // A HasSetKey supports map update using x[k]=v syntax, like a dictionary. type HasSetKey interface { Mapping SetKey(k, v Value) error } var _ HasSetKey = (*Dict)(nil) // A HasBinary value may be used as either operand of these binary operators: // + - * / // % in not in | & ^ << >> // // The Side argument indicates whether the receiver is the left or right operand. // // An implementation may decline to handle an operation by returning (nil, nil). // For this reason, clients should always call the standalone Binary(op, x, y) // function rather than calling the method directly. type HasBinary interface { Value Binary(op syntax.Token, y Value, side Side) (Value, error) } type Side bool const ( Left Side = false Right Side = true ) // A HasUnary value may be used as the operand of these unary operators: // + - ~ // // An implementation may decline to handle an operation by returning (nil, nil). // For this reason, clients should always call the standalone Unary(op, x) // function rather than calling the method directly. type HasUnary interface { Value Unary(op syntax.Token) (Value, error) } // A HasAttrs value has fields or methods that may be read by a dot expression (y = x.f). // Attribute names may be listed using the built-in 'dir' function. // // For implementation convenience, a result of (nil, nil) from Attr is // interpreted as a "no such field or method" error. Implementations are // free to return a more precise error. type HasAttrs interface { Value Attr(name string) (Value, error) // returns (nil, nil) if attribute not present AttrNames() []string // callers must not modify the result. } var ( _ HasAttrs = String("") _ HasAttrs = new(List) _ HasAttrs = new(Dict) _ HasAttrs = new(Set) ) // A HasSetField value has fields that may be written by a dot expression (x.f = y). // // An implementation of SetField may return a NoSuchAttrError, // in which case the runtime may augment the error message to // warn of possible misspelling. type HasSetField interface { HasAttrs SetField(name string, val Value) error } // A NoSuchAttrError may be returned by an implementation of // HasAttrs.Attr or HasSetField.SetField to indicate that no such field // exists. In that case the runtime may augment the error message to // warn of possible misspelling. type NoSuchAttrError string func (e NoSuchAttrError) Error() string { return string(e) } // NoneType is the type of None. Its only legal value is None. // (We represent it as a number, not struct{}, so that None may be constant.) type NoneType byte const None = NoneType(0) func (NoneType) String() string { return "None" } func (NoneType) Type() string { return "NoneType" } func (NoneType) Freeze() {} // immutable func (NoneType) Truth() Bool { return False } func (NoneType) Hash() (uint32, error) { return 0, nil } func (NoneType) CompareSameType(op syntax.Token, y Value, depth int) (bool, error) { return threeway(op, 0), nil } // Bool is the type of a Starlark bool. type Bool bool const ( False Bool = false True Bool = true ) func (b Bool) String() string { if b { return "True" } else { return "False" } } func (b Bool) Type() string { return "bool" } func (b Bool) Freeze() {} // immutable func (b Bool) Truth() Bool { return b } func (b Bool) Hash() (uint32, error) { return uint32(b2i(bool(b))), nil } func (x Bool) CompareSameType(op syntax.Token, y_ Value, depth int) (bool, error) { y := y_.(Bool) return threeway(op, b2i(bool(x))-b2i(bool(y))), nil } // Float is the type of a Starlark float. type Float float64 func (f Float) String() string { return strconv.FormatFloat(float64(f), 'g', 6, 64) } func (f Float) Type() string { return "float" } func (f Float) Freeze() {} // immutable func (f Float) Truth() Bool { return f != 0.0 } func (f Float) Hash() (uint32, error) { // Equal float and int values must yield the same hash. // TODO(adonovan): opt: if f is non-integral, and thus not equal // to any Int, we can avoid the Int conversion and use a cheaper hash. if isFinite(float64(f)) { return finiteFloatToInt(f).Hash() } return 1618033, nil // NaN, +/-Inf } func floor(f Float) Float { return Float(math.Floor(float64(f))) } // isFinite reports whether f represents a finite rational value. // It is equivalent to !math.IsNan(f) && !math.IsInf(f, 0). func isFinite(f float64) bool { return math.Abs(f) <= math.MaxFloat64 } func (x Float) CompareSameType(op syntax.Token, y_ Value, depth int) (bool, error) { y := y_.(Float) switch op { case syntax.EQL: return x == y, nil case syntax.NEQ: return x != y, nil case syntax.LE: return x <= y, nil case syntax.LT: return x < y, nil case syntax.GE: return x >= y, nil case syntax.GT: return x > y, nil } panic(op) } func (f Float) rational() *big.Rat { return new(big.Rat).SetFloat64(float64(f)) } // AsFloat returns the float64 value closest to x. // The f result is undefined if x is not a float or int. func AsFloat(x Value) (f float64, ok bool) { switch x := x.(type) { case Float: return float64(x), true case Int: return float64(x.Float()), true } return 0, false } func (x Float) Mod(y Float) Float { return Float(math.Mod(float64(x), float64(y))) } // Unary implements the operations +float and -float. func (f Float) Unary(op syntax.Token) (Value, error) { switch op { case syntax.MINUS: return -f, nil case syntax.PLUS: return +f, nil } return nil, nil } // String is the type of a Starlark string. // // A String encapsulates an an immutable sequence of bytes, // but strings are not directly iterable. Instead, iterate // over the result of calling one of these four methods: // codepoints, codepoint_ords, elems, elem_ords. // // Warning: the contract of the Value interface's String method is that // it returns the value printed in Starlark notation, // so s.String() or fmt.Sprintf("%s", s) returns a quoted string. // Use string(s) or s.GoString() or fmt.Sprintf("%#v", s) to obtain the raw contents // of a Starlark string as a Go string. type String string func (s String) String() string { return strconv.Quote(string(s)) } func (s String) GoString() string { return string(s) } func (s String) Type() string { return "string" } func (s String) Freeze() {} // immutable func (s String) Truth() Bool { return len(s) > 0 } func (s String) Hash() (uint32, error) { return hashString(string(s)), nil } func (s String) Len() int { return len(s) } // bytes func (s String) Index(i int) Value { return s[i : i+1] } func (s String) Slice(start, end, step int) Value { if step == 1 { return s[start:end] } sign := signum(step) var str []byte for i := start; signum(end-i) == sign; i += step { str = append(str, s[i]) } return String(str) } func (s String) Attr(name string) (Value, error) { return builtinAttr(s, name, stringMethods) } func (s String) AttrNames() []string { return builtinAttrNames(stringMethods) } func (x String) CompareSameType(op syntax.Token, y_ Value, depth int) (bool, error) { y := y_.(String) return threeway(op, strings.Compare(string(x), string(y))), nil } func AsString(x Value) (string, bool) { v, ok := x.(String); return string(v), ok } // A stringIterable is an iterable whose iterator yields a sequence of // either Unicode code points or elements (bytes), // either numerically or as successive substrings. type stringIterable struct { s String ords bool codepoints bool } var _ Iterable = (*stringIterable)(nil) func (si stringIterable) String() string { var etype string if si.codepoints { etype = "codepoint" } else { etype = "elem" } if si.ords { return si.s.String() + "." + etype + "_ords()" } else { return si.s.String() + "." + etype + "s()" } } func (si stringIterable) Type() string { if si.codepoints { return "codepoints" } else { return "elems" } } func (si stringIterable) Freeze() {} // immutable func (si stringIterable) Truth() Bool { return True } func (si stringIterable) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable: %s", si.Type()) } func (si stringIterable) Iterate() Iterator { return &stringIterator{si, 0} } type stringIterator struct { si stringIterable i int } func (it *stringIterator) Next(p *Value) bool { s := it.si.s[it.i:] if s == "" { return false } if it.si.codepoints { r, sz := utf8.DecodeRuneInString(string(s)) if !it.si.ords { *p = s[:sz] } else { *p = MakeInt(int(r)) } it.i += sz } else { b := int(s[0]) if !it.si.ords { *p = s[:1] } else { *p = MakeInt(b) } it.i += 1 } return true } func (*stringIterator) Done() {} // A Function is a function defined by a Starlark def statement or lambda expression. // The initialization behavior of a Starlark module is also represented by a Function. type Function struct { funcode *compile.Funcode module *module defaults Tuple freevars Tuple } // A module is the dynamic counterpart to a Program. // All functions in the same program share a module. type module struct { program *compile.Program predeclared StringDict globals []Value constants []Value } // makeGlobalDict returns a new, unfrozen StringDict containing all global // variables so far defined in the module. func (m *module) makeGlobalDict() StringDict { r := make(StringDict, len(m.program.Globals)) for i, id := range m.program.Globals { if v := m.globals[i]; v != nil { r[id.Name] = v } } return r } func (fn *Function) Name() string { return fn.funcode.Name } // "lambda" for anonymous functions func (fn *Function) Doc() string { return fn.funcode.Doc } func (fn *Function) Hash() (uint32, error) { return hashString(fn.funcode.Name), nil } func (fn *Function) Freeze() { fn.defaults.Freeze(); fn.freevars.Freeze() } func (fn *Function) String() string { return toString(fn) } func (fn *Function) Type() string { return "function" } func (fn *Function) Truth() Bool { return true } // Globals returns a new, unfrozen StringDict containing all global // variables so far defined in the function's module. func (fn *Function) Globals() StringDict { return fn.module.makeGlobalDict() } func (fn *Function) Position() syntax.Position { return fn.funcode.Pos } func (fn *Function) NumParams() int { return fn.funcode.NumParams } func (fn *Function) NumKwonlyParams() int { return fn.funcode.NumKwonlyParams } // Param returns the name and position of the ith parameter, // where 0 <= i < NumParams(). // The *args and **kwargs parameters are at the end // even if there were optional parameters after *args. func (fn *Function) Param(i int) (string, syntax.Position) { if i >= fn.NumParams() { panic(i) } id := fn.funcode.Locals[i] return id.Name, id.Pos } func (fn *Function) HasVarargs() bool { return fn.funcode.HasVarargs } func (fn *Function) HasKwargs() bool { return fn.funcode.HasKwargs } // A Builtin is a function implemented in Go. type Builtin struct { name string fn func(thread *Thread, fn *Builtin, args Tuple, kwargs []Tuple) (Value, error) recv Value // for bound methods (e.g. "".startswith) } func (b *Builtin) Name() string { return b.name } func (b *Builtin) Freeze() { if b.recv != nil { b.recv.Freeze() } } func (b *Builtin) Hash() (uint32, error) { h := hashString(b.name) if b.recv != nil { h ^= 5521 } return h, nil } func (b *Builtin) Receiver() Value { return b.recv } func (b *Builtin) String() string { return toString(b) } func (b *Builtin) Type() string { return "builtin_function_or_method" } func (b *Builtin) CallInternal(thread *Thread, args Tuple, kwargs []Tuple) (Value, error) { return b.fn(thread, b, args, kwargs) } func (b *Builtin) Truth() Bool { return true } // NewBuiltin returns a new 'builtin_function_or_method' value with the specified name // and implementation. It compares unequal with all other values. func NewBuiltin(name string, fn func(thread *Thread, fn *Builtin, args Tuple, kwargs []Tuple) (Value, error)) *Builtin { return &Builtin{name: name, fn: fn} } // BindReceiver returns a new Builtin value representing a method // closure, that is, a built-in function bound to a receiver value. // // In the example below, the value of f is the string.index // built-in method bound to the receiver value "abc": // // f = "abc".index; f("a"); f("b") // // In the common case, the receiver is bound only during the call, // but this still results in the creation of a temporary method closure: // // "abc".index("a") // func (b *Builtin) BindReceiver(recv Value) *Builtin { return &Builtin{name: b.name, fn: b.fn, recv: recv} } // A *Dict represents a Starlark dictionary. // The zero value of Dict is a valid empty dictionary. // If you know the exact final number of entries, // it is more efficient to call NewDict. type Dict struct { ht hashtable } // NewDict returns a set with initial space for // at least size insertions before rehashing. func NewDict(size int) *Dict { dict := new(Dict) dict.ht.init(size) return dict } func (d *Dict) Clear() error { return d.ht.clear() } func (d *Dict) Delete(k Value) (v Value, found bool, err error) { return d.ht.delete(k) } func (d *Dict) Get(k Value) (v Value, found bool, err error) { return d.ht.lookup(k) } func (d *Dict) Items() []Tuple { return d.ht.items() } func (d *Dict) Keys() []Value { return d.ht.keys() } func (d *Dict) Len() int { return int(d.ht.len) } func (d *Dict) Iterate() Iterator { return d.ht.iterate() } func (d *Dict) SetKey(k, v Value) error { return d.ht.insert(k, v) } func (d *Dict) String() string { return toString(d) } func (d *Dict) Type() string { return "dict" } func (d *Dict) Freeze() { d.ht.freeze() } func (d *Dict) Truth() Bool { return d.Len() > 0 } func (d *Dict) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable type: dict") } func (d *Dict) Attr(name string) (Value, error) { return builtinAttr(d, name, dictMethods) } func (d *Dict) AttrNames() []string { return builtinAttrNames(dictMethods) } func (x *Dict) CompareSameType(op syntax.Token, y_ Value, depth int) (bool, error) { y := y_.(*Dict) switch op { case syntax.EQL: ok, err := dictsEqual(x, y, depth) return ok, err case syntax.NEQ: ok, err := dictsEqual(x, y, depth) return !ok, err default: return false, fmt.Errorf("%s %s %s not implemented", x.Type(), op, y.Type()) } } func dictsEqual(x, y *Dict, depth int) (bool, error) { if x.Len() != y.Len() { return false, nil } for _, xitem := range x.Items() { key, xval := xitem[0], xitem[1] if yval, found, _ := y.Get(key); !found { return false, nil } else if eq, err := EqualDepth(xval, yval, depth-1); err != nil { return false, err } else if !eq { return false, nil } } return true, nil } // A *List represents a Starlark list value. type List struct { elems []Value frozen bool itercount uint32 // number of active iterators (ignored if frozen) } // NewList returns a list containing the specified elements. // Callers should not subsequently modify elems. func NewList(elems []Value) *List { return &List{elems: elems} } func (l *List) Freeze() { if !l.frozen { l.frozen = true for _, elem := range l.elems { elem.Freeze() } } } // checkMutable reports an error if the list should not be mutated. // verb+" list" should describe the operation. func (l *List) checkMutable(verb string) error { if l.frozen { return fmt.Errorf("cannot %s frozen list", verb) } if l.itercount > 0 { return fmt.Errorf("cannot %s list during iteration", verb) } return nil } func (l *List) String() string { return toString(l) } func (l *List) Type() string { return "list" } func (l *List) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable type: list") } func (l *List) Truth() Bool { return l.Len() > 0 } func (l *List) Len() int { return len(l.elems) } func (l *List) Index(i int) Value { return l.elems[i] } func (l *List) Slice(start, end, step int) Value { if step == 1 { elems := append([]Value{}, l.elems[start:end]...) return NewList(elems) } sign := signum(step) var list []Value for i := start; signum(end-i) == sign; i += step { list = append(list, l.elems[i]) } return NewList(list) } func (l *List) Attr(name string) (Value, error) { return builtinAttr(l, name, listMethods) } func (l *List) AttrNames() []string { return builtinAttrNames(listMethods) } func (l *List) Iterate() Iterator { if !l.frozen { l.itercount++ } return &listIterator{l: l} } func (x *List) CompareSameType(op syntax.Token, y_ Value, depth int) (bool, error) { y := y_.(*List) // It's tempting to check x == y as an optimization here, // but wrong because a list containing NaN is not equal to itself. return sliceCompare(op, x.elems, y.elems, depth) } func sliceCompare(op syntax.Token, x, y []Value, depth int) (bool, error) { // Fast path: check length. if len(x) != len(y) && (op == syntax.EQL || op == syntax.NEQ) { return op == syntax.NEQ, nil } // Find first element that is not equal in both lists. for i := 0; i < len(x) && i < len(y); i++ { if eq, err := EqualDepth(x[i], y[i], depth-1); err != nil { return false, err } else if !eq { switch op { case syntax.EQL: return false, nil case syntax.NEQ: return true, nil default: return CompareDepth(op, x[i], y[i], depth-1) } } } return threeway(op, len(x)-len(y)), nil } type listIterator struct { l *List i int } func (it *listIterator) Next(p *Value) bool { if it.i < it.l.Len() { *p = it.l.elems[it.i] it.i++ return true } return false } func (it *listIterator) Done() { if !it.l.frozen { it.l.itercount-- } } func (l *List) SetIndex(i int, v Value) error { if err := l.checkMutable("assign to element of"); err != nil { return err } l.elems[i] = v return nil } func (l *List) Append(v Value) error { if err := l.checkMutable("append to"); err != nil { return err } l.elems = append(l.elems, v) return nil } func (l *List) Clear() error { if err := l.checkMutable("clear"); err != nil { return err } for i := range l.elems { l.elems[i] = nil // aid GC } l.elems = l.elems[:0] return nil } // A Tuple represents a Starlark tuple value. type Tuple []Value func (t Tuple) Len() int { return len(t) } func (t Tuple) Index(i int) Value { return t[i] } func (t Tuple) Slice(start, end, step int) Value { if step == 1 { return t[start:end] } sign := signum(step) var tuple Tuple for i := start; signum(end-i) == sign; i += step { tuple = append(tuple, t[i]) } return tuple } func (t Tuple) Iterate() Iterator { return &tupleIterator{elems: t} } func (t Tuple) Freeze() { for _, elem := range t { elem.Freeze() } } func (t Tuple) String() string { return toString(t) } func (t Tuple) Type() string { return "tuple" } func (t Tuple) Truth() Bool { return len(t) > 0 } func (x Tuple) CompareSameType(op syntax.Token, y_ Value, depth int) (bool, error) { y := y_.(Tuple) return sliceCompare(op, x, y, depth) } func (t Tuple) Hash() (uint32, error) { // Use same algorithm as Python. var x, mult uint32 = 0x345678, 1000003 for _, elem := range t { y, err := elem.Hash() if err != nil { return 0, err } x = x ^ y*mult mult += 82520 + uint32(len(t)+len(t)) } return x, nil } type tupleIterator struct{ elems Tuple } func (it *tupleIterator) Next(p *Value) bool { if len(it.elems) > 0 { *p = it.elems[0] it.elems = it.elems[1:] return true } return false } func (it *tupleIterator) Done() {} // A Set represents a Starlark set value. // The zero value of Set is a valid empty set. // If you know the exact final number of elements, // it is more efficient to call NewSet. type Set struct { ht hashtable // values are all None } // NewSet returns a dictionary with initial space for // at least size insertions before rehashing. func NewSet(size int) *Set { set := new(Set) set.ht.init(size) return set } func (s *Set) Delete(k Value) (found bool, err error) { _, found, err = s.ht.delete(k); return } func (s *Set) Clear() error { return s.ht.clear() } func (s *Set) Has(k Value) (found bool, err error) { _, found, err = s.ht.lookup(k); return } func (s *Set) Insert(k Value) error { return s.ht.insert(k, None) } func (s *Set) Len() int { return int(s.ht.len) } func (s *Set) Iterate() Iterator { return s.ht.iterate() } func (s *Set) String() string { return toString(s) } func (s *Set) Type() string { return "set" } func (s *Set) elems() []Value { return s.ht.keys() } func (s *Set) Freeze() { s.ht.freeze() } func (s *Set) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable type: set") } func (s *Set) Truth() Bool { return s.Len() > 0 } func (s *Set) Attr(name string) (Value, error) { return builtinAttr(s, name, setMethods) } func (s *Set) AttrNames() []string { return builtinAttrNames(setMethods) } func (x *Set) CompareSameType(op syntax.Token, y_ Value, depth int) (bool, error) { y := y_.(*Set) switch op { case syntax.EQL: ok, err := setsEqual(x, y, depth) return ok, err case syntax.NEQ: ok, err := setsEqual(x, y, depth) return !ok, err default: return false, fmt.Errorf("%s %s %s not implemented", x.Type(), op, y.Type()) } } func setsEqual(x, y *Set, depth int) (bool, error) { if x.Len() != y.Len() { return false, nil } for _, elem := range x.elems() { if found, _ := y.Has(elem); !found { return false, nil } } return true, nil } func (s *Set) Union(iter Iterator) (Value, error) { set := new(Set) for _, elem := range s.elems() { set.Insert(elem) // can't fail } var x Value for iter.Next(&x) { if err := set.Insert(x); err != nil { return nil, err } } return set, nil } // toString returns the string form of value v. // It may be more efficient than v.String() for larger values. func toString(v Value) string { buf := new(strings.Builder) writeValue(buf, v, nil) return buf.String() } // writeValue writes x to out. // // path is used to detect cycles. // It contains the list of *List and *Dict values we're currently printing. // (These are the only potentially cyclic structures.) // Callers should generally pass nil for path. // It is safe to re-use the same path slice for multiple calls. func writeValue(out *strings.Builder, x Value, path []Value) { switch x := x.(type) { case nil: out.WriteString("") // indicates a bug case NoneType: out.WriteString("None") case Int: out.WriteString(x.String()) case Bool: if x { out.WriteString("True") } else { out.WriteString("False") } case String: fmt.Fprintf(out, "%q", string(x)) case *List: out.WriteByte('[') if pathContains(path, x) { out.WriteString("...") // list contains itself } else { for i, elem := range x.elems { if i > 0 { out.WriteString(", ") } writeValue(out, elem, append(path, x)) } } out.WriteByte(']') case Tuple: out.WriteByte('(') for i, elem := range x { if i > 0 { out.WriteString(", ") } writeValue(out, elem, path) } if len(x) == 1 { out.WriteByte(',') } out.WriteByte(')') case *Function: fmt.Fprintf(out, "", x.Name()) case *Builtin: if x.recv != nil { fmt.Fprintf(out, "", x.Name(), x.recv.Type()) } else { fmt.Fprintf(out, "", x.Name()) } case *Dict: out.WriteByte('{') if pathContains(path, x) { out.WriteString("...") // dict contains itself } else { sep := "" for _, item := range x.Items() { k, v := item[0], item[1] out.WriteString(sep) writeValue(out, k, path) out.WriteString(": ") writeValue(out, v, append(path, x)) // cycle check sep = ", " } } out.WriteByte('}') case *Set: out.WriteString("set([") for i, elem := range x.elems() { if i > 0 { out.WriteString(", ") } writeValue(out, elem, path) } out.WriteString("])") default: out.WriteString(x.String()) } } func pathContains(path []Value, x Value) bool { for _, y := range path { if x == y { return true } } return false } const maxdepth = 10 // Equal reports whether two Starlark values are equal. func Equal(x, y Value) (bool, error) { if x, ok := x.(String); ok { return x == y, nil // fast path for an important special case } return EqualDepth(x, y, maxdepth) } // EqualDepth reports whether two Starlark values are equal. // // Recursive comparisons by implementations of Value.CompareSameType // should use EqualDepth to prevent infinite recursion. func EqualDepth(x, y Value, depth int) (bool, error) { return CompareDepth(syntax.EQL, x, y, depth) } // Compare compares two Starlark values. // The comparison operation must be one of EQL, NEQ, LT, LE, GT, or GE. // Compare returns an error if an ordered comparison was // requested for a type that does not support it. // // Recursive comparisons by implementations of Value.CompareSameType // should use CompareDepth to prevent infinite recursion. func Compare(op syntax.Token, x, y Value) (bool, error) { return CompareDepth(op, x, y, maxdepth) } // CompareDepth compares two Starlark values. // The comparison operation must be one of EQL, NEQ, LT, LE, GT, or GE. // CompareDepth returns an error if an ordered comparison was // requested for a pair of values that do not support it. // // The depth parameter limits the maximum depth of recursion // in cyclic data structures. func CompareDepth(op syntax.Token, x, y Value, depth int) (bool, error) { if depth < 1 { return false, fmt.Errorf("comparison exceeded maximum recursion depth") } if sameType(x, y) { if xcomp, ok := x.(Comparable); ok { return xcomp.CompareSameType(op, y, depth) } // use identity comparison switch op { case syntax.EQL: return x == y, nil case syntax.NEQ: return x != y, nil } return false, fmt.Errorf("%s %s %s not implemented", x.Type(), op, y.Type()) } // different types // int/float ordered comparisons switch x := x.(type) { case Int: if y, ok := y.(Float); ok { if y != y { return false, nil // y is NaN } var cmp int if !math.IsInf(float64(y), 0) { cmp = x.rational().Cmp(y.rational()) // y is finite } else if y > 0 { cmp = -1 // y is +Inf } else { cmp = +1 // y is -Inf } return threeway(op, cmp), nil } case Float: if y, ok := y.(Int); ok { if x != x { return false, nil // x is NaN } var cmp int if !math.IsInf(float64(x), 0) { cmp = x.rational().Cmp(y.rational()) // x is finite } else if x > 0 { cmp = -1 // x is +Inf } else { cmp = +1 // x is -Inf } return threeway(op, cmp), nil } } // All other values of different types compare unequal. switch op { case syntax.EQL: return false, nil case syntax.NEQ: return true, nil } return false, fmt.Errorf("%s %s %s not implemented", x.Type(), op, y.Type()) } func sameType(x, y Value) bool { return reflect.TypeOf(x) == reflect.TypeOf(y) || x.Type() == y.Type() } // threeway interprets a three-way comparison value cmp (-1, 0, +1) // as a boolean comparison (e.g. x < y). func threeway(op syntax.Token, cmp int) bool { switch op { case syntax.EQL: return cmp == 0 case syntax.NEQ: return cmp != 0 case syntax.LE: return cmp <= 0 case syntax.LT: return cmp < 0 case syntax.GE: return cmp >= 0 case syntax.GT: return cmp > 0 } panic(op) } func b2i(b bool) int { if b { return 1 } else { return 0 } } // Len returns the length of a string or sequence value, // and -1 for all others. // // Warning: Len(x) >= 0 does not imply Iterate(x) != nil. // A string has a known length but is not directly iterable. func Len(x Value) int { switch x := x.(type) { case String: return x.Len() case Sequence: return x.Len() } return -1 } // Iterate return a new iterator for the value if iterable, nil otherwise. // If the result is non-nil, the caller must call Done when finished with it. // // Warning: Iterate(x) != nil does not imply Len(x) >= 0. // Some iterables may have unknown length. func Iterate(x Value) Iterator { if x, ok := x.(Iterable); ok { return x.Iterate() } return nil } ================================================ FILE: vendor/go.starlark.net/starlarkstruct/module.go ================================================ package starlarkstruct import ( "fmt" "go.starlark.net/starlark" ) // A Module is a named collection of values, // typically a suite of functions imported by a load statement. // // It differs from Struct primarily in that its string representation // does not enumerate its fields. type Module struct { Name string Members starlark.StringDict } var _ starlark.HasAttrs = (*Module)(nil) func (m *Module) Attr(name string) (starlark.Value, error) { return m.Members[name], nil } func (m *Module) AttrNames() []string { return m.Members.Keys() } func (m *Module) Freeze() { m.Members.Freeze() } func (m *Module) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable: %s", m.Type()) } func (m *Module) String() string { return fmt.Sprintf("", m.Name) } func (m *Module) Truth() starlark.Bool { return true } func (m *Module) Type() string { return "module" } // MakeModule may be used as the implementation of a Starlark built-in // function, module(name, **kwargs). It returns a new module with the // specified name and members. func MakeModule(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { var name string if err := starlark.UnpackPositionalArgs(b.Name(), args, nil, 1, &name); err != nil { return nil, err } members := make(starlark.StringDict, len(kwargs)) for _, kwarg := range kwargs { k := string(kwarg[0].(starlark.String)) members[k] = kwarg[1] } return &Module{name, members}, nil } ================================================ FILE: vendor/go.starlark.net/starlarkstruct/struct.go ================================================ // Copyright 2017 The Bazel Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package starlarkstruct defines the Starlark types 'struct' and // 'module', both optional language extensions. // package starlarkstruct // import "go.starlark.net/starlarkstruct" // It is tempting to introduce a variant of Struct that is a wrapper // around a Go struct value, for stronger typing guarantees and more // efficient and convenient field lookup. However: // 1) all fields of Starlark structs are optional, so we cannot represent // them using more specific types such as String, Int, *Depset, and // *File, as such types give no way to represent missing fields. // 2) the efficiency gain of direct struct field access is rather // marginal: finding the index of a field by binary searching on the // sorted list of field names is quite fast compared to the other // overheads. // 3) the gains in compactness and spatial locality are also rather // marginal: the array behind the []entry slice is (due to field name // strings) only a factor of 2 larger than the corresponding Go struct // would be, and, like the Go struct, requires only a single allocation. import ( "fmt" "sort" "strings" "go.starlark.net/starlark" "go.starlark.net/syntax" ) // Make is the implementation of a built-in function that instantiates // an immutable struct from the specified keyword arguments. // // An application can add 'struct' to the Starlark environment like so: // // globals := starlark.StringDict{ // "struct": starlark.NewBuiltin("struct", starlarkstruct.Make), // } // func Make(_ *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { if len(args) > 0 { return nil, fmt.Errorf("struct: unexpected positional arguments") } return FromKeywords(Default, kwargs), nil } // FromKeywords returns a new struct instance whose fields are specified by the // key/value pairs in kwargs. (Each kwargs[i][0] must be a starlark.String.) func FromKeywords(constructor starlark.Value, kwargs []starlark.Tuple) *Struct { if constructor == nil { panic("nil constructor") } s := &Struct{ constructor: constructor, entries: make(entries, 0, len(kwargs)), } for _, kwarg := range kwargs { k := string(kwarg[0].(starlark.String)) v := kwarg[1] s.entries = append(s.entries, entry{k, v}) } sort.Sort(s.entries) return s } // FromStringDict returns a whose elements are those of d. // The constructor parameter specifies the constructor; use Default for an ordinary struct. func FromStringDict(constructor starlark.Value, d starlark.StringDict) *Struct { if constructor == nil { panic("nil constructor") } s := &Struct{ constructor: constructor, entries: make(entries, 0, len(d)), } for k, v := range d { s.entries = append(s.entries, entry{k, v}) } sort.Sort(s.entries) return s } // Struct is an immutable Starlark type that maps field names to values. // It is not iterable and does not support len. // // A struct has a constructor, a distinct value that identifies a class // of structs, and which appears in the struct's string representation. // // Operations such as x+y fail if the constructors of the two operands // are not equal. // // The default constructor, Default, is the string "struct", but // clients may wish to 'brand' structs for their own purposes. // The constructor value appears in the printed form of the value, // and is accessible using the Constructor method. // // Use Attr to access its fields and AttrNames to enumerate them. type Struct struct { constructor starlark.Value entries entries // sorted by name } // Default is the default constructor for structs. // It is merely the string "struct". const Default = starlark.String("struct") type entries []entry func (a entries) Len() int { return len(a) } func (a entries) Less(i, j int) bool { return a[i].name < a[j].name } func (a entries) Swap(i, j int) { a[i], a[j] = a[j], a[i] } type entry struct { name string value starlark.Value } var ( _ starlark.HasAttrs = (*Struct)(nil) _ starlark.HasBinary = (*Struct)(nil) ) // ToStringDict adds a name/value entry to d for each field of the struct. func (s *Struct) ToStringDict(d starlark.StringDict) { for _, e := range s.entries { d[e.name] = e.value } } func (s *Struct) String() string { buf := new(strings.Builder) if s.constructor == Default { // NB: The Java implementation always prints struct // even for Bazel provider instances. buf.WriteString("struct") // avoid String()'s quotation } else { buf.WriteString(s.constructor.String()) } buf.WriteByte('(') for i, e := range s.entries { if i > 0 { buf.WriteString(", ") } buf.WriteString(e.name) buf.WriteString(" = ") buf.WriteString(e.value.String()) } buf.WriteByte(')') return buf.String() } // Constructor returns the constructor used to create this struct. func (s *Struct) Constructor() starlark.Value { return s.constructor } func (s *Struct) Type() string { return "struct" } func (s *Struct) Truth() starlark.Bool { return true } // even when empty func (s *Struct) Hash() (uint32, error) { // Same algorithm as Tuple.hash, but with different primes. var x, m uint32 = 8731, 9839 for _, e := range s.entries { namehash, _ := starlark.String(e.name).Hash() x = x ^ 3*namehash y, err := e.value.Hash() if err != nil { return 0, err } x = x ^ y*m m += 7349 } return x, nil } func (s *Struct) Freeze() { for _, e := range s.entries { e.value.Freeze() } } func (x *Struct) Binary(op syntax.Token, y starlark.Value, side starlark.Side) (starlark.Value, error) { if y, ok := y.(*Struct); ok && op == syntax.PLUS { if side == starlark.Right { x, y = y, x } if eq, err := starlark.Equal(x.constructor, y.constructor); err != nil { return nil, fmt.Errorf("in %s + %s: error comparing constructors: %v", x.constructor, y.constructor, err) } else if !eq { return nil, fmt.Errorf("cannot add structs of different constructors: %s + %s", x.constructor, y.constructor) } z := make(starlark.StringDict, x.len()+y.len()) for _, e := range x.entries { z[e.name] = e.value } for _, e := range y.entries { z[e.name] = e.value } return FromStringDict(x.constructor, z), nil } return nil, nil // unhandled } // Attr returns the value of the specified field. func (s *Struct) Attr(name string) (starlark.Value, error) { // Binary search the entries. // This implementation is a specialization of // sort.Search that avoids dynamic dispatch. n := len(s.entries) i, j := 0, n for i < j { h := int(uint(i+j) >> 1) if s.entries[h].name < name { i = h + 1 } else { j = h } } if i < n && s.entries[i].name == name { return s.entries[i].value, nil } var ctor string if s.constructor != Default { ctor = s.constructor.String() + " " } return nil, starlark.NoSuchAttrError( fmt.Sprintf("%sstruct has no .%s attribute", ctor, name)) } func (s *Struct) len() int { return len(s.entries) } // AttrNames returns a new sorted list of the struct fields. func (s *Struct) AttrNames() []string { names := make([]string, len(s.entries)) for i, e := range s.entries { names[i] = e.name } return names } func (x *Struct) CompareSameType(op syntax.Token, y_ starlark.Value, depth int) (bool, error) { y := y_.(*Struct) switch op { case syntax.EQL: return structsEqual(x, y, depth) case syntax.NEQ: eq, err := structsEqual(x, y, depth) return !eq, err default: return false, fmt.Errorf("%s %s %s not implemented", x.Type(), op, y.Type()) } } func structsEqual(x, y *Struct, depth int) (bool, error) { if x.len() != y.len() { return false, nil } if eq, err := starlark.Equal(x.constructor, y.constructor); err != nil { return false, fmt.Errorf("error comparing struct constructors %v and %v: %v", x.constructor, y.constructor, err) } else if !eq { return false, nil } for i, n := 0, x.len(); i < n; i++ { if x.entries[i].name != y.entries[i].name { return false, nil } else if eq, err := starlark.EqualDepth(x.entries[i].value, y.entries[i].value, depth-1); err != nil { return false, err } else if !eq { return false, nil } } return true, nil } ================================================ FILE: vendor/go.starlark.net/syntax/grammar.txt ================================================ Grammar of Starlark ================== File = {Statement | newline} eof . Statement = DefStmt | IfStmt | ForStmt | WhileStmt | SimpleStmt . DefStmt = 'def' identifier '(' [Parameters [',']] ')' ':' Suite . Parameters = Parameter {',' Parameter}. Parameter = identifier | identifier '=' Test | '*' | '*' identifier | '**' identifier . IfStmt = 'if' Test ':' Suite {'elif' Test ':' Suite} ['else' ':' Suite] . ForStmt = 'for' LoopVariables 'in' Expression ':' Suite . WhileStmt = 'while' Test ':' Suite . Suite = [newline indent {Statement} outdent] | SimpleStmt . SimpleStmt = SmallStmt {';' SmallStmt} [';'] '\n' . # NOTE: '\n' optional at EOF SmallStmt = ReturnStmt | BreakStmt | ContinueStmt | PassStmt | AssignStmt | ExprStmt | LoadStmt . ReturnStmt = 'return' [Expression] . BreakStmt = 'break' . ContinueStmt = 'continue' . PassStmt = 'pass' . AssignStmt = Expression ('=' | '+=' | '-=' | '*=' | '/=' | '//=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '>>=') Expression . ExprStmt = Expression . LoadStmt = 'load' '(' string {',' [identifier '='] string} [','] ')' . Test = LambdaExpr | IfExpr | PrimaryExpr | UnaryExpr | BinaryExpr . LambdaExpr = 'lambda' [Parameters] ':' Test . IfExpr = Test 'if' Test 'else' Test . PrimaryExpr = Operand | PrimaryExpr DotSuffix | PrimaryExpr CallSuffix | PrimaryExpr SliceSuffix . Operand = identifier | int | float | string | ListExpr | ListComp | DictExpr | DictComp | '(' [Expression [',']] ')' | ('-' | '+') PrimaryExpr . DotSuffix = '.' identifier . CallSuffix = '(' [Arguments [',']] ')' . SliceSuffix = '[' [Expression] [':' Test [':' Test]] ']' . Arguments = Argument {',' Argument} . Argument = Test | identifier '=' Test | '*' Test | '**' Test . ListExpr = '[' [Expression [',']] ']' . ListComp = '[' Test {CompClause} ']'. DictExpr = '{' [Entries [',']] '}' . DictComp = '{' Entry {CompClause} '}' . Entries = Entry {',' Entry} . Entry = Test ':' Test . CompClause = 'for' LoopVariables 'in' Test | 'if' Test . UnaryExpr = 'not' Test . BinaryExpr = Test {Binop Test} . Binop = 'or' | 'and' | '==' | '!=' | '<' | '>' | '<=' | '>=' | 'in' | 'not' 'in' | '|' | '^' | '&' | '-' | '+' | '*' | '%' | '/' | '//' . Expression = Test {',' Test} . # NOTE: trailing comma permitted only when within [...] or (...). LoopVariables = PrimaryExpr {',' PrimaryExpr} . # Notation (similar to Go spec): - lowercase and 'quoted' items are lexical tokens. - Capitalized names denote grammar productions. - (...) implies grouping - x | y means either x or y. - [x] means x is optional - {x} means x is repeated zero or more times - The end of each declaration is marked with a period. # Tokens - spaces: newline, eof, indent, outdent. - identifier. - literals: string, int, float. - plus all quoted tokens such as '+=', 'return'. # Notes: - Ambiguity is resolved using operator precedence. - The grammar does not enforce the legal order of params and args, nor that the first compclause must be a 'for'. TODO: - explain how the lexer generates indent, outdent, and newline tokens. - why is unary NOT separated from unary - and +? - the grammar is (mostly) in LL(1) style so, for example, dot expressions are formed suffixes, not complete expressions, which makes the spec harder to read. Reorganize into non-LL(1) form? ================================================ FILE: vendor/go.starlark.net/syntax/parse.go ================================================ // Copyright 2017 The Bazel Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package syntax // This file defines a recursive-descent parser for Starlark. // The LL(1) grammar of Starlark and the names of many productions follow Python 2.7. // // TODO(adonovan): use syntax.Error more systematically throughout the // package. Verify that error positions are correct using the // chunkedfile mechanism. import "log" // Enable this flag to print the token stream and log.Fatal on the first error. const debug = false // A Mode value is a set of flags (or 0) that controls optional parser functionality. type Mode uint const ( RetainComments Mode = 1 << iota // retain comments in AST; see Node.Comments ) // Parse parses the input data and returns the corresponding parse tree. // // If src != nil, ParseFile parses the source from src and the filename // is only used when recording position information. // The type of the argument for the src parameter must be string, // []byte, or io.Reader. // If src == nil, ParseFile parses the file specified by filename. func Parse(filename string, src interface{}, mode Mode) (f *File, err error) { in, err := newScanner(filename, src, mode&RetainComments != 0) if err != nil { return nil, err } p := parser{in: in} defer p.in.recover(&err) p.nextToken() // read first lookahead token f = p.parseFile() if f != nil { f.Path = filename } p.assignComments(f) return f, nil } // ParseCompoundStmt parses a single compound statement: // a blank line, a def, for, while, or if statement, or a // semicolon-separated list of simple statements followed // by a newline. These are the units on which the REPL operates. // ParseCompoundStmt does not consume any following input. // The parser calls the readline function each // time it needs a new line of input. func ParseCompoundStmt(filename string, readline func() ([]byte, error)) (f *File, err error) { in, err := newScanner(filename, readline, false) if err != nil { return nil, err } p := parser{in: in} defer p.in.recover(&err) p.nextToken() // read first lookahead token var stmts []Stmt switch p.tok { case DEF, IF, FOR, WHILE: stmts = p.parseStmt(stmts) case NEWLINE: // blank line default: stmts = p.parseSimpleStmt(stmts, false) // Require but don't consume newline, to avoid blocking again. if p.tok != NEWLINE { p.in.errorf(p.in.pos, "invalid syntax") } } return &File{Path: filename, Stmts: stmts}, nil } // ParseExpr parses a Starlark expression. // A comma-separated list of expressions is parsed as a tuple. // See Parse for explanation of parameters. func ParseExpr(filename string, src interface{}, mode Mode) (expr Expr, err error) { in, err := newScanner(filename, src, mode&RetainComments != 0) if err != nil { return nil, err } p := parser{in: in} defer p.in.recover(&err) p.nextToken() // read first lookahead token // Use parseExpr, not parseTest, to permit an unparenthesized tuple. expr = p.parseExpr(false) // A following newline (e.g. "f()\n") appears outside any brackets, // on a non-blank line, and thus results in a NEWLINE token. if p.tok == NEWLINE { p.nextToken() } if p.tok != EOF { p.in.errorf(p.in.pos, "got %#v after expression, want EOF", p.tok) } p.assignComments(expr) return expr, nil } type parser struct { in *scanner tok Token tokval tokenValue } // nextToken advances the scanner and returns the position of the // previous token. func (p *parser) nextToken() Position { oldpos := p.tokval.pos p.tok = p.in.nextToken(&p.tokval) // enable to see the token stream if debug { log.Printf("nextToken: %-20s%+v\n", p.tok, p.tokval.pos) } return oldpos } // file_input = (NEWLINE | stmt)* EOF func (p *parser) parseFile() *File { var stmts []Stmt for p.tok != EOF { if p.tok == NEWLINE { p.nextToken() continue } stmts = p.parseStmt(stmts) } return &File{Stmts: stmts} } func (p *parser) parseStmt(stmts []Stmt) []Stmt { if p.tok == DEF { return append(stmts, p.parseDefStmt()) } else if p.tok == IF { return append(stmts, p.parseIfStmt()) } else if p.tok == FOR { return append(stmts, p.parseForStmt()) } else if p.tok == WHILE { return append(stmts, p.parseWhileStmt()) } return p.parseSimpleStmt(stmts, true) } func (p *parser) parseDefStmt() Stmt { defpos := p.nextToken() // consume DEF id := p.parseIdent() p.consume(LPAREN) params := p.parseParams() p.consume(RPAREN) p.consume(COLON) body := p.parseSuite() return &DefStmt{ Def: defpos, Name: id, Params: params, Body: body, } } func (p *parser) parseIfStmt() Stmt { ifpos := p.nextToken() // consume IF cond := p.parseTest() p.consume(COLON) body := p.parseSuite() ifStmt := &IfStmt{ If: ifpos, Cond: cond, True: body, } tail := ifStmt for p.tok == ELIF { elifpos := p.nextToken() // consume ELIF cond := p.parseTest() p.consume(COLON) body := p.parseSuite() elif := &IfStmt{ If: elifpos, Cond: cond, True: body, } tail.ElsePos = elifpos tail.False = []Stmt{elif} tail = elif } if p.tok == ELSE { tail.ElsePos = p.nextToken() // consume ELSE p.consume(COLON) tail.False = p.parseSuite() } return ifStmt } func (p *parser) parseForStmt() Stmt { forpos := p.nextToken() // consume FOR vars := p.parseForLoopVariables() p.consume(IN) x := p.parseExpr(false) p.consume(COLON) body := p.parseSuite() return &ForStmt{ For: forpos, Vars: vars, X: x, Body: body, } } func (p *parser) parseWhileStmt() Stmt { whilepos := p.nextToken() // consume WHILE cond := p.parseTest() p.consume(COLON) body := p.parseSuite() return &WhileStmt{ While: whilepos, Cond: cond, Body: body, } } // Equivalent to 'exprlist' production in Python grammar. // // loop_variables = primary_with_suffix (COMMA primary_with_suffix)* COMMA? func (p *parser) parseForLoopVariables() Expr { // Avoid parseExpr because it would consume the IN token // following x in "for x in y: ...". v := p.parsePrimaryWithSuffix() if p.tok != COMMA { return v } list := []Expr{v} for p.tok == COMMA { p.nextToken() if terminatesExprList(p.tok) { break } list = append(list, p.parsePrimaryWithSuffix()) } return &TupleExpr{List: list} } // simple_stmt = small_stmt (SEMI small_stmt)* SEMI? NEWLINE // In REPL mode, it does not consume the NEWLINE. func (p *parser) parseSimpleStmt(stmts []Stmt, consumeNL bool) []Stmt { for { stmts = append(stmts, p.parseSmallStmt()) if p.tok != SEMI { break } p.nextToken() // consume SEMI if p.tok == NEWLINE || p.tok == EOF { break } } // EOF without NEWLINE occurs in `if x: pass`, for example. if p.tok != EOF && consumeNL { p.consume(NEWLINE) } return stmts } // small_stmt = RETURN expr? // | PASS | BREAK | CONTINUE // | LOAD ... // | expr ('=' | '+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '>>=') expr // assign // | expr func (p *parser) parseSmallStmt() Stmt { switch p.tok { case RETURN: pos := p.nextToken() // consume RETURN var result Expr if p.tok != EOF && p.tok != NEWLINE && p.tok != SEMI { result = p.parseExpr(false) } return &ReturnStmt{Return: pos, Result: result} case BREAK, CONTINUE, PASS: tok := p.tok pos := p.nextToken() // consume it return &BranchStmt{Token: tok, TokenPos: pos} case LOAD: return p.parseLoadStmt() } // Assignment x := p.parseExpr(false) switch p.tok { case EQ, PLUS_EQ, MINUS_EQ, STAR_EQ, SLASH_EQ, SLASHSLASH_EQ, PERCENT_EQ, AMP_EQ, PIPE_EQ, CIRCUMFLEX_EQ, LTLT_EQ, GTGT_EQ: op := p.tok pos := p.nextToken() // consume op rhs := p.parseExpr(false) return &AssignStmt{OpPos: pos, Op: op, LHS: x, RHS: rhs} } // Expression statement (e.g. function call, doc string). return &ExprStmt{X: x} } // stmt = LOAD '(' STRING {',' (IDENT '=')? STRING} [','] ')' func (p *parser) parseLoadStmt() *LoadStmt { loadPos := p.nextToken() // consume LOAD lparen := p.consume(LPAREN) if p.tok != STRING { p.in.errorf(p.in.pos, "first operand of load statement must be a string literal") } module := p.parsePrimary().(*Literal) var from, to []*Ident for p.tok != RPAREN && p.tok != EOF { p.consume(COMMA) if p.tok == RPAREN { break // allow trailing comma } switch p.tok { case STRING: // load("module", "id") // To name is same as original. lit := p.parsePrimary().(*Literal) id := &Ident{ NamePos: lit.TokenPos.add(`"`), Name: lit.Value.(string), } to = append(to, id) from = append(from, id) case IDENT: // load("module", to="from") id := p.parseIdent() to = append(to, id) if p.tok != EQ { p.in.errorf(p.in.pos, `load operand must be "%[1]s" or %[1]s="originalname" (want '=' after %[1]s)`, id.Name) } p.consume(EQ) if p.tok != STRING { p.in.errorf(p.in.pos, `original name of loaded symbol must be quoted: %s="originalname"`, id.Name) } lit := p.parsePrimary().(*Literal) from = append(from, &Ident{ NamePos: lit.TokenPos.add(`"`), Name: lit.Value.(string), }) case RPAREN: p.in.errorf(p.in.pos, "trailing comma in load statement") default: p.in.errorf(p.in.pos, `load operand must be "name" or localname="name" (got %#v)`, p.tok) } } rparen := p.consume(RPAREN) if len(to) == 0 { p.in.errorf(lparen, "load statement must import at least 1 symbol") } return &LoadStmt{ Load: loadPos, Module: module, To: to, From: from, Rparen: rparen, } } // suite is typically what follows a COLON (e.g. after DEF or FOR). // suite = simple_stmt | NEWLINE INDENT stmt+ OUTDENT func (p *parser) parseSuite() []Stmt { if p.tok == NEWLINE { p.nextToken() // consume NEWLINE p.consume(INDENT) var stmts []Stmt for p.tok != OUTDENT && p.tok != EOF { stmts = p.parseStmt(stmts) } p.consume(OUTDENT) return stmts } return p.parseSimpleStmt(nil, true) } func (p *parser) parseIdent() *Ident { if p.tok != IDENT { p.in.error(p.in.pos, "not an identifier") } id := &Ident{ NamePos: p.tokval.pos, Name: p.tokval.raw, } p.nextToken() return id } func (p *parser) consume(t Token) Position { if p.tok != t { p.in.errorf(p.in.pos, "got %#v, want %#v", p.tok, t) } return p.nextToken() } // params = (param COMMA)* param COMMA? // | // // param = IDENT // | IDENT EQ test // | STAR // | STAR IDENT // | STARSTAR IDENT // // parseParams parses a parameter list. The resulting expressions are of the form: // // *Ident x // *Binary{Op: EQ, X: *Ident, Y: Expr} x=y // *Unary{Op: STAR} * // *Unary{Op: STAR, X: *Ident} *args // *Unary{Op: STARSTAR, X: *Ident} **kwargs func (p *parser) parseParams() []Expr { var params []Expr for p.tok != RPAREN && p.tok != COLON && p.tok != EOF { if len(params) > 0 { p.consume(COMMA) } if p.tok == RPAREN { break } // * or *args or **kwargs if p.tok == STAR || p.tok == STARSTAR { op := p.tok pos := p.nextToken() var x Expr if op == STARSTAR || p.tok == IDENT { x = p.parseIdent() } params = append(params, &UnaryExpr{ OpPos: pos, Op: op, X: x, }) continue } // IDENT // IDENT = test id := p.parseIdent() if p.tok == EQ { // default value eq := p.nextToken() dflt := p.parseTest() params = append(params, &BinaryExpr{ X: id, OpPos: eq, Op: EQ, Y: dflt, }) continue } params = append(params, id) } return params } // parseExpr parses an expression, possible consisting of a // comma-separated list of 'test' expressions. // // In many cases we must use parseTest to avoid ambiguity such as // f(x, y) vs. f((x, y)). func (p *parser) parseExpr(inParens bool) Expr { x := p.parseTest() if p.tok != COMMA { return x } // tuple exprs := p.parseExprs([]Expr{x}, inParens) return &TupleExpr{List: exprs} } // parseExprs parses a comma-separated list of expressions, starting with the comma. // It is used to parse tuples and list elements. // expr_list = (',' expr)* ','? func (p *parser) parseExprs(exprs []Expr, allowTrailingComma bool) []Expr { for p.tok == COMMA { pos := p.nextToken() if terminatesExprList(p.tok) { if !allowTrailingComma { p.in.error(pos, "unparenthesized tuple with trailing comma") } break } exprs = append(exprs, p.parseTest()) } return exprs } // parseTest parses a 'test', a single-component expression. func (p *parser) parseTest() Expr { if p.tok == LAMBDA { return p.parseLambda(true) } x := p.parseTestPrec(0) // conditional expression (t IF cond ELSE f) if p.tok == IF { ifpos := p.nextToken() cond := p.parseTestPrec(0) if p.tok != ELSE { p.in.error(ifpos, "conditional expression without else clause") } elsepos := p.nextToken() else_ := p.parseTest() return &CondExpr{If: ifpos, Cond: cond, True: x, ElsePos: elsepos, False: else_} } return x } // parseTestNoCond parses a a single-component expression without // consuming a trailing 'if expr else expr'. func (p *parser) parseTestNoCond() Expr { if p.tok == LAMBDA { return p.parseLambda(false) } return p.parseTestPrec(0) } // parseLambda parses a lambda expression. // The allowCond flag allows the body to be an 'a if b else c' conditional. func (p *parser) parseLambda(allowCond bool) Expr { lambda := p.nextToken() var params []Expr if p.tok != COLON { params = p.parseParams() } p.consume(COLON) var body Expr if allowCond { body = p.parseTest() } else { body = p.parseTestNoCond() } return &LambdaExpr{ Lambda: lambda, Params: params, Body: body, } } func (p *parser) parseTestPrec(prec int) Expr { if prec >= len(preclevels) { return p.parsePrimaryWithSuffix() } // expr = NOT expr if p.tok == NOT && prec == int(precedence[NOT]) { pos := p.nextToken() x := p.parseTestPrec(prec) return &UnaryExpr{ OpPos: pos, Op: NOT, X: x, } } return p.parseBinopExpr(prec) } // expr = test (OP test)* // Uses precedence climbing; see http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm#climbing. func (p *parser) parseBinopExpr(prec int) Expr { x := p.parseTestPrec(prec + 1) for first := true; ; first = false { if p.tok == NOT { p.nextToken() // consume NOT // In this context, NOT must be followed by IN. // Replace NOT IN by a single NOT_IN token. if p.tok != IN { p.in.errorf(p.in.pos, "got %#v, want in", p.tok) } p.tok = NOT_IN } // Binary operator of specified precedence? opprec := int(precedence[p.tok]) if opprec < prec { return x } // Comparisons are non-associative. if !first && opprec == int(precedence[EQL]) { p.in.errorf(p.in.pos, "%s does not associate with %s (use parens)", x.(*BinaryExpr).Op, p.tok) } op := p.tok pos := p.nextToken() y := p.parseTestPrec(opprec + 1) x = &BinaryExpr{OpPos: pos, Op: op, X: x, Y: y} } } // precedence maps each operator to its precedence (0-7), or -1 for other tokens. var precedence [maxToken]int8 // preclevels groups operators of equal precedence. // Comparisons are nonassociative; other binary operators associate to the left. // Unary MINUS, unary PLUS, and TILDE have higher precedence so are handled in parsePrimary. // See https://github.com/google/starlark-go/blob/master/doc/spec.md#binary-operators var preclevels = [...][]Token{ {OR}, // or {AND}, // and {NOT}, // not (unary) {EQL, NEQ, LT, GT, LE, GE, IN, NOT_IN}, // == != < > <= >= in not in {PIPE}, // | {CIRCUMFLEX}, // ^ {AMP}, // & {LTLT, GTGT}, // << >> {MINUS, PLUS}, // - {STAR, PERCENT, SLASH, SLASHSLASH}, // * % / // } func init() { // populate precedence table for i := range precedence { precedence[i] = -1 } for level, tokens := range preclevels { for _, tok := range tokens { precedence[tok] = int8(level) } } } // primary_with_suffix = primary // | primary '.' IDENT // | primary slice_suffix // | primary call_suffix func (p *parser) parsePrimaryWithSuffix() Expr { x := p.parsePrimary() for { switch p.tok { case DOT: dot := p.nextToken() id := p.parseIdent() x = &DotExpr{Dot: dot, X: x, Name: id} case LBRACK: x = p.parseSliceSuffix(x) case LPAREN: x = p.parseCallSuffix(x) default: return x } } } // slice_suffix = '[' expr? ':' expr? ':' expr? ']' func (p *parser) parseSliceSuffix(x Expr) Expr { lbrack := p.nextToken() var lo, hi, step Expr if p.tok != COLON { y := p.parseExpr(false) // index x[y] if p.tok == RBRACK { rbrack := p.nextToken() return &IndexExpr{X: x, Lbrack: lbrack, Y: y, Rbrack: rbrack} } lo = y } // slice or substring x[lo:hi:step] if p.tok == COLON { p.nextToken() if p.tok != COLON && p.tok != RBRACK { hi = p.parseTest() } } if p.tok == COLON { p.nextToken() if p.tok != RBRACK { step = p.parseTest() } } rbrack := p.consume(RBRACK) return &SliceExpr{X: x, Lbrack: lbrack, Lo: lo, Hi: hi, Step: step, Rbrack: rbrack} } // call_suffix = '(' arg_list? ')' func (p *parser) parseCallSuffix(fn Expr) Expr { lparen := p.consume(LPAREN) var rparen Position var args []Expr if p.tok == RPAREN { rparen = p.nextToken() } else { args = p.parseArgs() rparen = p.consume(RPAREN) } return &CallExpr{Fn: fn, Lparen: lparen, Args: args, Rparen: rparen} } // parseArgs parses a list of actual parameter values (arguments). // It mirrors the structure of parseParams. // arg_list = ((arg COMMA)* arg COMMA?)? func (p *parser) parseArgs() []Expr { var args []Expr for p.tok != RPAREN && p.tok != EOF { if len(args) > 0 { p.consume(COMMA) } if p.tok == RPAREN { break } // *args or **kwargs if p.tok == STAR || p.tok == STARSTAR { op := p.tok pos := p.nextToken() x := p.parseTest() args = append(args, &UnaryExpr{ OpPos: pos, Op: op, X: x, }) continue } // We use a different strategy from Bazel here to stay within LL(1). // Instead of looking ahead two tokens (IDENT, EQ) we parse // 'test = test' then check that the first was an IDENT. x := p.parseTest() if p.tok == EQ { // name = value if _, ok := x.(*Ident); !ok { p.in.errorf(p.in.pos, "keyword argument must have form name=expr") } eq := p.nextToken() y := p.parseTest() x = &BinaryExpr{ X: x, OpPos: eq, Op: EQ, Y: y, } } args = append(args, x) } return args } // primary = IDENT // | INT | FLOAT // | STRING // | '[' ... // list literal or comprehension // | '{' ... // dict literal or comprehension // | '(' ... // tuple or parenthesized expression // | ('-'|'+'|'~') primary_with_suffix func (p *parser) parsePrimary() Expr { switch p.tok { case IDENT: return p.parseIdent() case INT, FLOAT, STRING: var val interface{} tok := p.tok switch tok { case INT: if p.tokval.bigInt != nil { val = p.tokval.bigInt } else { val = p.tokval.int } case FLOAT: val = p.tokval.float case STRING: val = p.tokval.string } raw := p.tokval.raw pos := p.nextToken() return &Literal{Token: tok, TokenPos: pos, Raw: raw, Value: val} case LBRACK: return p.parseList() case LBRACE: return p.parseDict() case LPAREN: lparen := p.nextToken() if p.tok == RPAREN { // empty tuple rparen := p.nextToken() return &TupleExpr{Lparen: lparen, Rparen: rparen} } e := p.parseExpr(true) // allow trailing comma rparen := p.consume(RPAREN) return &ParenExpr{ Lparen: lparen, X: e, Rparen: rparen, } case MINUS, PLUS, TILDE: // unary tok := p.tok pos := p.nextToken() x := p.parsePrimaryWithSuffix() return &UnaryExpr{ OpPos: pos, Op: tok, X: x, } } p.in.errorf(p.in.pos, "got %#v, want primary expression", p.tok) panic("unreachable") } // list = '[' ']' // | '[' expr ']' // | '[' expr expr_list ']' // | '[' expr (FOR loop_variables IN expr)+ ']' func (p *parser) parseList() Expr { lbrack := p.nextToken() if p.tok == RBRACK { // empty List rbrack := p.nextToken() return &ListExpr{Lbrack: lbrack, Rbrack: rbrack} } x := p.parseTest() if p.tok == FOR { // list comprehension return p.parseComprehensionSuffix(lbrack, x, RBRACK) } exprs := []Expr{x} if p.tok == COMMA { // multi-item list literal exprs = p.parseExprs(exprs, true) // allow trailing comma } rbrack := p.consume(RBRACK) return &ListExpr{Lbrack: lbrack, List: exprs, Rbrack: rbrack} } // dict = '{' '}' // | '{' dict_entry_list '}' // | '{' dict_entry FOR loop_variables IN expr '}' func (p *parser) parseDict() Expr { lbrace := p.nextToken() if p.tok == RBRACE { // empty dict rbrace := p.nextToken() return &DictExpr{Lbrace: lbrace, Rbrace: rbrace} } x := p.parseDictEntry() if p.tok == FOR { // dict comprehension return p.parseComprehensionSuffix(lbrace, x, RBRACE) } entries := []Expr{x} for p.tok == COMMA { p.nextToken() if p.tok == RBRACE { break } entries = append(entries, p.parseDictEntry()) } rbrace := p.consume(RBRACE) return &DictExpr{Lbrace: lbrace, List: entries, Rbrace: rbrace} } // dict_entry = test ':' test func (p *parser) parseDictEntry() *DictEntry { k := p.parseTest() colon := p.consume(COLON) v := p.parseTest() return &DictEntry{Key: k, Colon: colon, Value: v} } // comp_suffix = FOR loopvars IN expr comp_suffix // | IF expr comp_suffix // | ']' or ')' (end) // // There can be multiple FOR/IF clauses; the first is always a FOR. func (p *parser) parseComprehensionSuffix(lbrace Position, body Expr, endBrace Token) Expr { var clauses []Node for p.tok != endBrace { if p.tok == FOR { pos := p.nextToken() vars := p.parseForLoopVariables() in := p.consume(IN) // Following Python 3, the operand of IN cannot be: // - a conditional expression ('x if y else z'), // due to conflicts in Python grammar // ('if' is used by the comprehension); // - a lambda expression // - an unparenthesized tuple. x := p.parseTestPrec(0) clauses = append(clauses, &ForClause{For: pos, Vars: vars, In: in, X: x}) } else if p.tok == IF { pos := p.nextToken() cond := p.parseTestNoCond() clauses = append(clauses, &IfClause{If: pos, Cond: cond}) } else { p.in.errorf(p.in.pos, "got %#v, want '%s', for, or if", p.tok, endBrace) } } rbrace := p.nextToken() return &Comprehension{ Curly: endBrace == RBRACE, Lbrack: lbrace, Body: body, Clauses: clauses, Rbrack: rbrace, } } func terminatesExprList(tok Token) bool { switch tok { case EOF, NEWLINE, EQ, RBRACE, RBRACK, RPAREN, SEMI: return true } return false } // Comment assignment. // We build two lists of all subnodes, preorder and postorder. // The preorder list is ordered by start location, with outer nodes first. // The postorder list is ordered by end location, with outer nodes last. // We use the preorder list to assign each whole-line comment to the syntax // immediately following it, and we use the postorder list to assign each // end-of-line comment to the syntax immediately preceding it. // flattenAST returns the list of AST nodes, both in prefix order and in postfix // order. func flattenAST(root Node) (pre, post []Node) { stack := []Node{} Walk(root, func(n Node) bool { if n != nil { pre = append(pre, n) stack = append(stack, n) } else { post = append(post, stack[len(stack)-1]) stack = stack[:len(stack)-1] } return true }) return pre, post } // assignComments attaches comments to nearby syntax. func (p *parser) assignComments(n Node) { // Leave early if there are no comments if len(p.in.lineComments)+len(p.in.suffixComments) == 0 { return } pre, post := flattenAST(n) // Assign line comments to syntax immediately following. line := p.in.lineComments for _, x := range pre { start, _ := x.Span() switch x.(type) { case *File: continue } for len(line) > 0 && !start.isBefore(line[0].Start) { x.AllocComments() x.Comments().Before = append(x.Comments().Before, line[0]) line = line[1:] } } // Remaining line comments go at end of file. if len(line) > 0 { n.AllocComments() n.Comments().After = append(n.Comments().After, line...) } // Assign suffix comments to syntax immediately before. suffix := p.in.suffixComments for i := len(post) - 1; i >= 0; i-- { x := post[i] // Do not assign suffix comments to file switch x.(type) { case *File: continue } _, end := x.Span() if len(suffix) > 0 && end.isBefore(suffix[len(suffix)-1].Start) { x.AllocComments() x.Comments().Suffix = append(x.Comments().Suffix, suffix[len(suffix)-1]) suffix = suffix[:len(suffix)-1] } } } ================================================ FILE: vendor/go.starlark.net/syntax/quote.go ================================================ // Copyright 2017 The Bazel Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package syntax // Starlark quoted string utilities. import ( "fmt" "strconv" "strings" ) // unesc maps single-letter chars following \ to their actual values. var unesc = [256]byte{ 'a': '\a', 'b': '\b', 'f': '\f', 'n': '\n', 'r': '\r', 't': '\t', 'v': '\v', '\\': '\\', '\'': '\'', '"': '"', } // esc maps escape-worthy bytes to the char that should follow \. var esc = [256]byte{ '\a': 'a', '\b': 'b', '\f': 'f', '\n': 'n', '\r': 'r', '\t': 't', '\v': 'v', '\\': '\\', '\'': '\'', '"': '"', } // notEsc is a list of characters that can follow a \ in a string value // without having to escape the \. That is, since ( is in this list, we // quote the Go string "foo\\(bar" as the Python literal "foo\(bar". // This really does happen in BUILD files, especially in strings // being used as shell arguments containing regular expressions. const notEsc = " !#$%&()*+,-./:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~" // unquote unquotes the quoted string, returning the actual // string value, whether the original was triple-quoted, and // an error describing invalid input. func unquote(quoted string) (s string, triple bool, err error) { // Check for raw prefix: means don't interpret the inner \. raw := false if strings.HasPrefix(quoted, "r") { raw = true quoted = quoted[1:] } if len(quoted) < 2 { err = fmt.Errorf("string literal too short") return } if quoted[0] != '"' && quoted[0] != '\'' || quoted[0] != quoted[len(quoted)-1] { err = fmt.Errorf("string literal has invalid quotes") return } // Check for triple quoted string. quote := quoted[0] if len(quoted) >= 6 && quoted[1] == quote && quoted[2] == quote && quoted[:3] == quoted[len(quoted)-3:] { triple = true quoted = quoted[3 : len(quoted)-3] } else { quoted = quoted[1 : len(quoted)-1] } // Now quoted is the quoted data, but no quotes. // If we're in raw mode or there are no escapes or // carriage returns, we're done. var unquoteChars string if raw { unquoteChars = "\r" } else { unquoteChars = "\\\r" } if !strings.ContainsAny(quoted, unquoteChars) { s = quoted return } // Otherwise process quoted string. // Each iteration processes one escape sequence along with the // plain text leading up to it. buf := new(strings.Builder) for { // Remove prefix before escape sequence. i := strings.IndexAny(quoted, unquoteChars) if i < 0 { i = len(quoted) } buf.WriteString(quoted[:i]) quoted = quoted[i:] if len(quoted) == 0 { break } // Process carriage return. if quoted[0] == '\r' { buf.WriteByte('\n') if len(quoted) > 1 && quoted[1] == '\n' { quoted = quoted[2:] } else { quoted = quoted[1:] } continue } // Process escape sequence. if len(quoted) == 1 { err = fmt.Errorf(`truncated escape sequence \`) return } switch quoted[1] { default: // In Python, if \z (for some byte z) is not a known escape sequence // then it appears as literal text in the string. buf.WriteString(quoted[:2]) quoted = quoted[2:] case '\n': // Ignore the escape and the line break. quoted = quoted[2:] case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '\'', '"': // One-char escape buf.WriteByte(unesc[quoted[1]]) quoted = quoted[2:] case '0', '1', '2', '3', '4', '5', '6', '7': // Octal escape, up to 3 digits. n := int(quoted[1] - '0') quoted = quoted[2:] for i := 1; i < 3; i++ { if len(quoted) == 0 || quoted[0] < '0' || '7' < quoted[0] { break } n = n*8 + int(quoted[0]-'0') quoted = quoted[1:] } if n >= 256 { // NOTE: Python silently discards the high bit, // so that '\541' == '\141' == 'a'. // Let's see if we can avoid doing that in BUILD files. err = fmt.Errorf(`invalid escape sequence \%03o`, n) return } buf.WriteByte(byte(n)) case 'x': // Hexadecimal escape, exactly 2 digits. if len(quoted) < 4 { err = fmt.Errorf(`truncated escape sequence %s`, quoted) return } n, err1 := strconv.ParseUint(quoted[2:4], 16, 0) if err1 != nil { err = fmt.Errorf(`invalid escape sequence %s`, quoted[:4]) return } buf.WriteByte(byte(n)) quoted = quoted[4:] } } s = buf.String() return } // indexByte returns the index of the first instance of b in s, or else -1. func indexByte(s string, b byte) int { for i := 0; i < len(s); i++ { if s[i] == b { return i } } return -1 } // hex is a list of the hexadecimal digits, for use in quoting. // We always print lower-case hexadecimal. const hex = "0123456789abcdef" // quote returns the quoted form of the string value "x". // If triple is true, quote uses the triple-quoted form """x""". func quote(unquoted string, triple bool) string { q := `"` if triple { q = `"""` } buf := new(strings.Builder) buf.WriteString(q) for i := 0; i < len(unquoted); i++ { c := unquoted[i] if c == '"' && triple && (i+1 < len(unquoted) && unquoted[i+1] != '"' || i+2 < len(unquoted) && unquoted[i+2] != '"') { // Can pass up to two quotes through, because they are followed by a non-quote byte. buf.WriteByte(c) if i+1 < len(unquoted) && unquoted[i+1] == '"' { buf.WriteByte(c) i++ } continue } if triple && c == '\n' { // Can allow newline in triple-quoted string. buf.WriteByte(c) continue } if c == '\'' { // Can allow ' since we always use ". buf.WriteByte(c) continue } if c == '\\' { if i+1 < len(unquoted) && indexByte(notEsc, unquoted[i+1]) >= 0 { // Can pass \ through when followed by a byte that // known not to be a valid escape sequence and also // that does not trigger an escape sequence of its own. // Use this, because various BUILD files do. buf.WriteByte('\\') buf.WriteByte(unquoted[i+1]) i++ continue } } if esc[c] != 0 { buf.WriteByte('\\') buf.WriteByte(esc[c]) continue } if c < 0x20 || c >= 0x80 { // BUILD files are supposed to be Latin-1, so escape all control and high bytes. // I'd prefer to use \x here, but Blaze does not implement // \x in quoted strings (b/7272572). buf.WriteByte('\\') buf.WriteByte(hex[c>>6]) // actually octal but reusing hex digits 0-7. buf.WriteByte(hex[(c>>3)&7]) buf.WriteByte(hex[c&7]) /* buf.WriteByte('\\') buf.WriteByte('x') buf.WriteByte(hex[c>>4]) buf.WriteByte(hex[c&0xF]) */ continue } buf.WriteByte(c) continue } buf.WriteString(q) return buf.String() } ================================================ FILE: vendor/go.starlark.net/syntax/scan.go ================================================ // Copyright 2017 The Bazel Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package syntax // A lexical scanner for Starlark. import ( "fmt" "io" "io/ioutil" "log" "math/big" "os" "strconv" "strings" "unicode" "unicode/utf8" ) // A Token represents a Starlark lexical token. type Token int8 const ( ILLEGAL Token = iota EOF NEWLINE INDENT OUTDENT // Tokens with values IDENT // x INT // 123 FLOAT // 1.23e45 STRING // "foo" or 'foo' or '''foo''' or r'foo' or r"foo" // Punctuation PLUS // + MINUS // - STAR // * SLASH // / SLASHSLASH // // PERCENT // % AMP // & PIPE // | CIRCUMFLEX // ^ LTLT // << GTGT // >> TILDE // ~ DOT // . COMMA // , EQ // = SEMI // ; COLON // : LPAREN // ( RPAREN // ) LBRACK // [ RBRACK // ] LBRACE // { RBRACE // } LT // < GT // > GE // >= LE // <= EQL // == NEQ // != PLUS_EQ // += (keep order consistent with PLUS..GTGT) MINUS_EQ // -= STAR_EQ // *= SLASH_EQ // /= SLASHSLASH_EQ // //= PERCENT_EQ // %= AMP_EQ // &= PIPE_EQ // |= CIRCUMFLEX_EQ // ^= LTLT_EQ // <<= GTGT_EQ // >>= STARSTAR // ** // Keywords AND BREAK CONTINUE DEF ELIF ELSE FOR IF IN LAMBDA LOAD NOT NOT_IN // synthesized by parser from NOT IN OR PASS RETURN WHILE maxToken ) func (tok Token) String() string { return tokenNames[tok] } // GoString is like String but quotes punctuation tokens. // Use Sprintf("%#v", tok) when constructing error messages. func (tok Token) GoString() string { if tok >= PLUS && tok <= STARSTAR { return "'" + tokenNames[tok] + "'" } return tokenNames[tok] } var tokenNames = [...]string{ ILLEGAL: "illegal token", EOF: "end of file", NEWLINE: "newline", INDENT: "indent", OUTDENT: "outdent", IDENT: "identifier", INT: "int literal", FLOAT: "float literal", STRING: "string literal", PLUS: "+", MINUS: "-", STAR: "*", SLASH: "/", SLASHSLASH: "//", PERCENT: "%", AMP: "&", PIPE: "|", CIRCUMFLEX: "^", LTLT: "<<", GTGT: ">>", TILDE: "~", DOT: ".", COMMA: ",", EQ: "=", SEMI: ";", COLON: ":", LPAREN: "(", RPAREN: ")", LBRACK: "[", RBRACK: "]", LBRACE: "{", RBRACE: "}", LT: "<", GT: ">", GE: ">=", LE: "<=", EQL: "==", NEQ: "!=", PLUS_EQ: "+=", MINUS_EQ: "-=", STAR_EQ: "*=", SLASH_EQ: "/=", SLASHSLASH_EQ: "//=", PERCENT_EQ: "%=", AMP_EQ: "&=", PIPE_EQ: "|=", CIRCUMFLEX_EQ: "^=", LTLT_EQ: "<<=", GTGT_EQ: ">>=", STARSTAR: "**", AND: "and", BREAK: "break", CONTINUE: "continue", DEF: "def", ELIF: "elif", ELSE: "else", FOR: "for", IF: "if", IN: "in", LAMBDA: "lambda", LOAD: "load", NOT: "not", NOT_IN: "not in", OR: "or", PASS: "pass", RETURN: "return", WHILE: "while", } // A Position describes the location of a rune of input. type Position struct { file *string // filename (indirect for compactness) Line int32 // 1-based line number; 0 if line unknown Col int32 // 1-based column (rune) number; 0 if column unknown } // IsValid reports whether the position is valid. func (p Position) IsValid() bool { return p.file != nil } // Filename returns the name of the file containing this position. func (p Position) Filename() string { if p.file != nil { return *p.file } return "" } // MakePosition returns position with the specified components. func MakePosition(file *string, line, col int32) Position { return Position{file, line, col} } // add returns the position at the end of s, assuming it starts at p. func (p Position) add(s string) Position { if n := strings.Count(s, "\n"); n > 0 { p.Line += int32(n) s = s[strings.LastIndex(s, "\n")+1:] p.Col = 1 } p.Col += int32(utf8.RuneCountInString(s)) return p } func (p Position) String() string { file := p.Filename() if p.Line > 0 { if p.Col > 0 { return fmt.Sprintf("%s:%d:%d", file, p.Line, p.Col) } return fmt.Sprintf("%s:%d", file, p.Line) } return file } func (p Position) isBefore(q Position) bool { if p.Line != q.Line { return p.Line < q.Line } return p.Col < q.Col } // An scanner represents a single input file being parsed. type scanner struct { rest []byte // rest of input (in REPL, a line of input) token []byte // token being scanned pos Position // current input position depth int // nesting of [ ] { } ( ) indentstk []int // stack of indentation levels dents int // number of saved INDENT (>0) or OUTDENT (<0) tokens to return lineStart bool // after NEWLINE; convert spaces to indentation tokens keepComments bool // accumulate comments in slice lineComments []Comment // list of full line comments (if keepComments) suffixComments []Comment // list of suffix comments (if keepComments) readline func() ([]byte, error) // read next line of input (REPL only) } func newScanner(filename string, src interface{}, keepComments bool) (*scanner, error) { sc := &scanner{ pos: Position{file: &filename, Line: 1, Col: 1}, indentstk: make([]int, 1, 10), // []int{0} + spare capacity lineStart: true, keepComments: keepComments, } sc.readline, _ = src.(func() ([]byte, error)) // REPL only if sc.readline == nil { data, err := readSource(filename, src) if err != nil { return nil, err } sc.rest = data } return sc, nil } func readSource(filename string, src interface{}) ([]byte, error) { switch src := src.(type) { case string: return []byte(src), nil case []byte: return src, nil case io.Reader: data, err := ioutil.ReadAll(src) if err != nil { err = &os.PathError{Op: "read", Path: filename, Err: err} return nil, err } return data, nil case nil: return ioutil.ReadFile(filename) default: return nil, fmt.Errorf("invalid source: %T", src) } } // An Error describes the nature and position of a scanner or parser error. type Error struct { Pos Position Msg string } func (e Error) Error() string { return e.Pos.String() + ": " + e.Msg } // errorf is called to report an error. // errorf does not return: it panics. func (sc *scanner) error(pos Position, s string) { panic(Error{pos, s}) } func (sc *scanner) errorf(pos Position, format string, args ...interface{}) { sc.error(pos, fmt.Sprintf(format, args...)) } func (sc *scanner) recover(err *error) { // The scanner and parser panic both for routine errors like // syntax errors and for programmer bugs like array index // errors. Turn both into error returns. Catching bug panics // is especially important when processing many files. switch e := recover().(type) { case nil: // no panic case Error: *err = e default: *err = Error{sc.pos, fmt.Sprintf("internal error: %v", e)} if debug { log.Fatal(*err) } } } // eof reports whether the input has reached end of file. func (sc *scanner) eof() bool { return len(sc.rest) == 0 && !sc.readLine() } // readLine attempts to read another line of input. // Precondition: len(sc.rest)==0. func (sc *scanner) readLine() bool { if sc.readline != nil { var err error sc.rest, err = sc.readline() if err != nil { sc.errorf(sc.pos, "%v", err) // EOF or ErrInterrupt } return len(sc.rest) > 0 } return false } // peekRune returns the next rune in the input without consuming it. // Newlines in Unix, DOS, or Mac format are treated as one rune, '\n'. func (sc *scanner) peekRune() rune { // TODO(adonovan): opt: measure and perhaps inline eof. if sc.eof() { return 0 } // fast path: ASCII if b := sc.rest[0]; b < utf8.RuneSelf { if b == '\r' { return '\n' } return rune(b) } r, _ := utf8.DecodeRune(sc.rest) return r } // readRune consumes and returns the next rune in the input. // Newlines in Unix, DOS, or Mac format are treated as one rune, '\n'. func (sc *scanner) readRune() rune { // eof() has been inlined here, both to avoid a call // and to establish len(rest)>0 to avoid a bounds check. if len(sc.rest) == 0 { if !sc.readLine() { sc.error(sc.pos, "internal scanner error: readRune at EOF") } // Redundant, but eliminates the bounds-check below. if len(sc.rest) == 0 { return 0 } } // fast path: ASCII if b := sc.rest[0]; b < utf8.RuneSelf { r := rune(b) sc.rest = sc.rest[1:] if r == '\r' { if len(sc.rest) > 0 && sc.rest[0] == '\n' { sc.rest = sc.rest[1:] } r = '\n' } if r == '\n' { sc.pos.Line++ sc.pos.Col = 1 } else { sc.pos.Col++ } return r } r, size := utf8.DecodeRune(sc.rest) sc.rest = sc.rest[size:] sc.pos.Col++ return r } // tokenValue records the position and value associated with each token. type tokenValue struct { raw string // raw text of token int int64 // decoded int bigInt *big.Int // decoded integers > int64 float float64 // decoded float string string // decoded string pos Position // start position of token } // startToken marks the beginning of the next input token. // It must be followed by a call to endToken once the token has // been consumed using readRune. func (sc *scanner) startToken(val *tokenValue) { sc.token = sc.rest val.raw = "" val.pos = sc.pos } // endToken marks the end of an input token. // It records the actual token string in val.raw if the caller // has not done that already. func (sc *scanner) endToken(val *tokenValue) { if val.raw == "" { val.raw = string(sc.token[:len(sc.token)-len(sc.rest)]) } } // nextToken is called by the parser to obtain the next input token. // It returns the token value and sets val to the data associated with // the token. // // For all our input tokens, the associated data is val.pos (the // position where the token begins), val.raw (the input string // corresponding to the token). For string and int tokens, the string // and int fields additionally contain the token's interpreted value. func (sc *scanner) nextToken(val *tokenValue) Token { // The following distribution of tokens guides case ordering: // // COMMA 27 % // STRING 23 % // IDENT 15 % // EQL 11 % // LBRACK 5.5 % // RBRACK 5.5 % // NEWLINE 3 % // LPAREN 2.9 % // RPAREN 2.9 % // INT 2 % // others < 1 % // // Although NEWLINE tokens are infrequent, and lineStart is // usually (~97%) false on entry, skipped newlines account for // about 50% of all iterations of the 'start' loop. start: var c rune // Deal with leading spaces and indentation. blank := false savedLineStart := sc.lineStart if sc.lineStart { sc.lineStart = false col := 0 for { c = sc.peekRune() if c == ' ' { col++ sc.readRune() } else if c == '\t' { const tab = 8 col += int(tab - (sc.pos.Col-1)%tab) sc.readRune() } else { break } } // The third clause matches EOF. if c == '#' || c == '\n' || c == 0 { blank = true } // Compute indentation level for non-blank lines not // inside an expression. This is not the common case. if !blank && sc.depth == 0 { cur := sc.indentstk[len(sc.indentstk)-1] if col > cur { // indent sc.dents++ sc.indentstk = append(sc.indentstk, col) } else if col < cur { // outdent(s) for len(sc.indentstk) > 0 && col < sc.indentstk[len(sc.indentstk)-1] { sc.dents-- sc.indentstk = sc.indentstk[:len(sc.indentstk)-1] // pop } if col != sc.indentstk[len(sc.indentstk)-1] { sc.error(sc.pos, "unindent does not match any outer indentation level") } } } } // Return saved indentation tokens. if sc.dents != 0 { sc.startToken(val) sc.endToken(val) if sc.dents < 0 { sc.dents++ return OUTDENT } else { sc.dents-- return INDENT } } // start of line proper c = sc.peekRune() // Skip spaces. for c == ' ' || c == '\t' { sc.readRune() c = sc.peekRune() } // comment if c == '#' { if sc.keepComments { sc.startToken(val) } // Consume up to newline (included). for c != 0 && c != '\n' { sc.readRune() c = sc.peekRune() } if sc.keepComments { sc.endToken(val) if blank { sc.lineComments = append(sc.lineComments, Comment{val.pos, val.raw}) } else { sc.suffixComments = append(sc.suffixComments, Comment{val.pos, val.raw}) } } } // newline if c == '\n' { sc.lineStart = true // Ignore newlines within expressions (common case). if sc.depth > 0 { sc.readRune() goto start } // Ignore blank lines, except in the REPL, // where they emit OUTDENTs and NEWLINE. if blank { if sc.readline == nil { sc.readRune() goto start } else if len(sc.indentstk) > 1 { sc.dents = 1 - len(sc.indentstk) sc.indentstk = sc.indentstk[:1] goto start } } // At top-level (not in an expression). sc.startToken(val) sc.readRune() val.raw = "\n" return NEWLINE } // end of file if c == 0 { // Emit OUTDENTs for unfinished indentation, // preceded by a NEWLINE if we haven't just emitted one. if len(sc.indentstk) > 1 { if savedLineStart { sc.dents = 1 - len(sc.indentstk) sc.indentstk = sc.indentstk[:1] goto start } else { sc.lineStart = true sc.startToken(val) val.raw = "\n" return NEWLINE } } sc.startToken(val) sc.endToken(val) return EOF } // line continuation if c == '\\' { sc.readRune() if sc.peekRune() != '\n' { sc.errorf(sc.pos, "stray backslash in program") } sc.readRune() goto start } // start of the next token sc.startToken(val) // comma (common case) if c == ',' { sc.readRune() sc.endToken(val) return COMMA } // string literal if c == '"' || c == '\'' { return sc.scanString(val, c) } // identifier or keyword if isIdentStart(c) { // raw string literal if c == 'r' && len(sc.rest) > 1 && (sc.rest[1] == '"' || sc.rest[1] == '\'') { sc.readRune() c = sc.peekRune() return sc.scanString(val, c) } for isIdent(c) { sc.readRune() c = sc.peekRune() } sc.endToken(val) if k, ok := keywordToken[val.raw]; ok { return k } return IDENT } // brackets switch c { case '[', '(', '{': sc.depth++ sc.readRune() sc.endToken(val) switch c { case '[': return LBRACK case '(': return LPAREN case '{': return LBRACE } panic("unreachable") case ']', ')', '}': if sc.depth == 0 { sc.errorf(sc.pos, "unexpected %q", c) } else { sc.depth-- } sc.readRune() sc.endToken(val) switch c { case ']': return RBRACK case ')': return RPAREN case '}': return RBRACE } panic("unreachable") } // int or float literal, or period if isdigit(c) || c == '.' { return sc.scanNumber(val, c) } // other punctuation defer sc.endToken(val) switch c { case '=', '<', '>', '!', '+', '-', '%', '/', '&', '|', '^': // possibly followed by '=' start := sc.pos sc.readRune() if sc.peekRune() == '=' { sc.readRune() switch c { case '<': return LE case '>': return GE case '=': return EQL case '!': return NEQ case '+': return PLUS_EQ case '-': return MINUS_EQ case '/': return SLASH_EQ case '%': return PERCENT_EQ case '&': return AMP_EQ case '|': return PIPE_EQ case '^': return CIRCUMFLEX_EQ } } switch c { case '=': return EQ case '<': if sc.peekRune() == '<' { sc.readRune() if sc.peekRune() == '=' { sc.readRune() return LTLT_EQ } else { return LTLT } } return LT case '>': if sc.peekRune() == '>' { sc.readRune() if sc.peekRune() == '=' { sc.readRune() return GTGT_EQ } else { return GTGT } } return GT case '!': sc.error(start, "unexpected input character '!'") case '+': return PLUS case '-': return MINUS case '/': if sc.peekRune() == '/' { sc.readRune() if sc.peekRune() == '=' { sc.readRune() return SLASHSLASH_EQ } else { return SLASHSLASH } } return SLASH case '%': return PERCENT case '&': return AMP case '|': return PIPE case '^': return CIRCUMFLEX } panic("unreachable") case ':', ';', '~': // single-char tokens (except comma) sc.readRune() switch c { case ':': return COLON case ';': return SEMI case '~': return TILDE } panic("unreachable") case '*': // possibly followed by '*' or '=' sc.readRune() switch sc.peekRune() { case '*': sc.readRune() return STARSTAR case '=': sc.readRune() return STAR_EQ } return STAR } sc.errorf(sc.pos, "unexpected input character %#q", c) panic("unreachable") } func (sc *scanner) scanString(val *tokenValue, quote rune) Token { start := sc.pos triple := len(sc.rest) >= 3 && sc.rest[0] == byte(quote) && sc.rest[1] == byte(quote) && sc.rest[2] == byte(quote) sc.readRune() if !triple { // Precondition: startToken was already called. for { if sc.eof() { sc.error(val.pos, "unexpected EOF in string") } c := sc.readRune() if c == quote { break } if c == '\n' { sc.error(val.pos, "unexpected newline in string") } if c == '\\' { if sc.eof() { sc.error(val.pos, "unexpected EOF in string") } sc.readRune() } } sc.endToken(val) } else { // triple-quoted string literal sc.readRune() sc.readRune() // A triple-quoted string literal may span multiple // gulps of REPL input; it is the only such token. // Thus we must avoid {start,end}Token. raw := new(strings.Builder) // Copy the prefix, e.g. r''' or """ (see startToken). raw.Write(sc.token[:len(sc.token)-len(sc.rest)]) quoteCount := 0 for { if sc.eof() { sc.error(val.pos, "unexpected EOF in string") } c := sc.readRune() raw.WriteRune(c) if c == quote { quoteCount++ if quoteCount == 3 { break } } else { quoteCount = 0 } if c == '\\' { if sc.eof() { sc.error(val.pos, "unexpected EOF in string") } c = sc.readRune() raw.WriteRune(c) } } val.raw = raw.String() } s, _, err := unquote(val.raw) if err != nil { sc.error(start, err.Error()) } val.string = s return STRING } func (sc *scanner) scanNumber(val *tokenValue, c rune) Token { // https://github.com/google/starlark-go/blob/master/doc/spec.md#lexical-elements // // Python features not supported: // - integer literals of >64 bits of precision // - 123L or 123l long suffix // - traditional octal: 0755 // https://docs.python.org/2/reference/lexical_analysis.html#integer-and-long-integer-literals start := sc.pos fraction, exponent := false, false if c == '.' { // dot or start of fraction sc.readRune() c = sc.peekRune() if !isdigit(c) { sc.endToken(val) return DOT } fraction = true } else if c == '0' { // hex, octal, binary or float sc.readRune() c = sc.peekRune() if c == '.' { fraction = true } else if c == 'x' || c == 'X' { // hex sc.readRune() c = sc.peekRune() if !isxdigit(c) { sc.error(start, "invalid hex literal") } for isxdigit(c) { sc.readRune() c = sc.peekRune() } } else if c == 'o' || c == 'O' { // octal sc.readRune() c = sc.peekRune() if !isodigit(c) { sc.error(sc.pos, "invalid octal literal") } for isodigit(c) { sc.readRune() c = sc.peekRune() } } else if c == 'b' || c == 'B' { // binary sc.readRune() c = sc.peekRune() if !isbdigit(c) { sc.error(sc.pos, "invalid binary literal") } for isbdigit(c) { sc.readRune() c = sc.peekRune() } } else { // float (or obsolete octal "0755") allzeros, octal := true, true for isdigit(c) { if c != '0' { allzeros = false } if c > '7' { octal = false } sc.readRune() c = sc.peekRune() } if c == '.' { fraction = true } else if c == 'e' || c == 'E' { exponent = true } else if octal && !allzeros { sc.endToken(val) sc.errorf(sc.pos, "obsolete form of octal literal; use 0o%s", val.raw[1:]) } } } else { // decimal for isdigit(c) { sc.readRune() c = sc.peekRune() } if c == '.' { fraction = true } else if c == 'e' || c == 'E' { exponent = true } } if fraction { sc.readRune() // consume '.' c = sc.peekRune() for isdigit(c) { sc.readRune() c = sc.peekRune() } if c == 'e' || c == 'E' { exponent = true } } if exponent { sc.readRune() // consume [eE] c = sc.peekRune() if c == '+' || c == '-' { sc.readRune() c = sc.peekRune() if !isdigit(c) { sc.error(sc.pos, "invalid float literal") } } for isdigit(c) { sc.readRune() c = sc.peekRune() } } sc.endToken(val) if fraction || exponent { var err error val.float, err = strconv.ParseFloat(val.raw, 64) if err != nil { sc.error(sc.pos, "invalid float literal") } return FLOAT } else { var err error s := val.raw val.bigInt = nil if len(s) > 2 && s[0] == '0' && (s[1] == 'o' || s[1] == 'O') { val.int, err = strconv.ParseInt(s[2:], 8, 64) } else if len(s) > 2 && s[0] == '0' && (s[1] == 'b' || s[1] == 'B') { val.int, err = strconv.ParseInt(s[2:], 2, 64) } else { val.int, err = strconv.ParseInt(s, 0, 64) if err != nil { num := new(big.Int) var ok bool val.bigInt, ok = num.SetString(s, 0) if ok { err = nil } } } if err != nil { sc.error(start, "invalid int literal") } return INT } } // isIdent reports whether c is an identifier rune. func isIdent(c rune) bool { return isdigit(c) || isIdentStart(c) } func isIdentStart(c rune) bool { return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || c == '_' || unicode.IsLetter(c) } func isdigit(c rune) bool { return '0' <= c && c <= '9' } func isodigit(c rune) bool { return '0' <= c && c <= '7' } func isxdigit(c rune) bool { return isdigit(c) || 'A' <= c && c <= 'F' || 'a' <= c && c <= 'f' } func isbdigit(c rune) bool { return '0' == c || c == '1' } // keywordToken records the special tokens for // strings that should not be treated as ordinary identifiers. var keywordToken = map[string]Token{ "and": AND, "break": BREAK, "continue": CONTINUE, "def": DEF, "elif": ELIF, "else": ELSE, "for": FOR, "if": IF, "in": IN, "lambda": LAMBDA, "load": LOAD, "not": NOT, "or": OR, "pass": PASS, "return": RETURN, "while": WHILE, // reserved words: "as": ILLEGAL, // "assert": ILLEGAL, // heavily used by our tests "class": ILLEGAL, "del": ILLEGAL, "except": ILLEGAL, "finally": ILLEGAL, "from": ILLEGAL, "global": ILLEGAL, "import": ILLEGAL, "is": ILLEGAL, "nonlocal": ILLEGAL, "raise": ILLEGAL, "try": ILLEGAL, "with": ILLEGAL, "yield": ILLEGAL, } ================================================ FILE: vendor/go.starlark.net/syntax/syntax.go ================================================ // Copyright 2017 The Bazel Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package syntax provides a Starlark parser and abstract syntax tree. package syntax // import "go.starlark.net/syntax" // A Node is a node in a Starlark syntax tree. type Node interface { // Span returns the start and end position of the expression. Span() (start, end Position) // Comments returns the comments associated with this node. // It returns nil if RetainComments was not specified during parsing, // or if AllocComments was not called. Comments() *Comments // AllocComments allocates a new Comments node if there was none. // This makes possible to add new comments using Comments() method. AllocComments() } // A Comment represents a single # comment. type Comment struct { Start Position Text string // without trailing newline } // Comments collects the comments associated with an expression. type Comments struct { Before []Comment // whole-line comments before this expression Suffix []Comment // end-of-line comments after this expression (up to 1) // For top-level expressions only, After lists whole-line // comments following the expression. After []Comment } // A commentsRef is a possibly-nil reference to a set of comments. // A commentsRef is embedded in each type of syntax node, // and provides its Comments and AllocComments methods. type commentsRef struct{ ref *Comments } // Comments returns the comments associated with a syntax node, // or nil if AllocComments has not yet been called. func (cr commentsRef) Comments() *Comments { return cr.ref } // AllocComments enables comments to be associated with a syntax node. func (cr *commentsRef) AllocComments() { if cr.ref == nil { cr.ref = new(Comments) } } // Start returns the start position of the expression. func Start(n Node) Position { start, _ := n.Span() return start } // End returns the end position of the expression. func End(n Node) Position { _, end := n.Span() return end } // A File represents a Starlark file. type File struct { commentsRef Path string Stmts []Stmt Module interface{} // a *resolve.Module, set by resolver } func (x *File) Span() (start, end Position) { if len(x.Stmts) == 0 { return } start, _ = x.Stmts[0].Span() _, end = x.Stmts[len(x.Stmts)-1].Span() return start, end } // A Stmt is a Starlark statement. type Stmt interface { Node stmt() } func (*AssignStmt) stmt() {} func (*BranchStmt) stmt() {} func (*DefStmt) stmt() {} func (*ExprStmt) stmt() {} func (*ForStmt) stmt() {} func (*WhileStmt) stmt() {} func (*IfStmt) stmt() {} func (*LoadStmt) stmt() {} func (*ReturnStmt) stmt() {} // An AssignStmt represents an assignment: // x = 0 // x, y = y, x // x += 1 type AssignStmt struct { commentsRef OpPos Position Op Token // = EQ | {PLUS,MINUS,STAR,PERCENT}_EQ LHS Expr RHS Expr } func (x *AssignStmt) Span() (start, end Position) { start, _ = x.LHS.Span() _, end = x.RHS.Span() return } // A DefStmt represents a function definition. type DefStmt struct { commentsRef Def Position Name *Ident Params []Expr // param = ident | ident=expr | * | *ident | **ident Body []Stmt Function interface{} // a *resolve.Function, set by resolver } func (x *DefStmt) Span() (start, end Position) { _, end = x.Body[len(x.Body)-1].Span() return x.Def, end } // An ExprStmt is an expression evaluated for side effects. type ExprStmt struct { commentsRef X Expr } func (x *ExprStmt) Span() (start, end Position) { return x.X.Span() } // An IfStmt is a conditional: If Cond: True; else: False. // 'elseif' is desugared into a chain of IfStmts. type IfStmt struct { commentsRef If Position // IF or ELIF Cond Expr True []Stmt ElsePos Position // ELSE or ELIF False []Stmt // optional } func (x *IfStmt) Span() (start, end Position) { body := x.False if body == nil { body = x.True } _, end = body[len(body)-1].Span() return x.If, end } // A LoadStmt loads another module and binds names from it: // load(Module, "x", y="foo"). // // The AST is slightly unfaithful to the concrete syntax here because // Starlark's load statement, so that it can be implemented in Python, // binds some names (like y above) with an identifier and some (like x) // without. For consistency we create fake identifiers for all the // strings. type LoadStmt struct { commentsRef Load Position Module *Literal // a string From []*Ident // name defined in loading module To []*Ident // name in loaded module Rparen Position } func (x *LoadStmt) Span() (start, end Position) { return x.Load, x.Rparen } // ModuleName returns the name of the module loaded by this statement. func (x *LoadStmt) ModuleName() string { return x.Module.Value.(string) } // A BranchStmt changes the flow of control: break, continue, pass. type BranchStmt struct { commentsRef Token Token // = BREAK | CONTINUE | PASS TokenPos Position } func (x *BranchStmt) Span() (start, end Position) { return x.TokenPos, x.TokenPos.add(x.Token.String()) } // A ReturnStmt returns from a function. type ReturnStmt struct { commentsRef Return Position Result Expr // may be nil } func (x *ReturnStmt) Span() (start, end Position) { if x.Result == nil { return x.Return, x.Return.add("return") } _, end = x.Result.Span() return x.Return, end } // An Expr is a Starlark expression. type Expr interface { Node expr() } func (*BinaryExpr) expr() {} func (*CallExpr) expr() {} func (*Comprehension) expr() {} func (*CondExpr) expr() {} func (*DictEntry) expr() {} func (*DictExpr) expr() {} func (*DotExpr) expr() {} func (*Ident) expr() {} func (*IndexExpr) expr() {} func (*LambdaExpr) expr() {} func (*ListExpr) expr() {} func (*Literal) expr() {} func (*ParenExpr) expr() {} func (*SliceExpr) expr() {} func (*TupleExpr) expr() {} func (*UnaryExpr) expr() {} // An Ident represents an identifier. type Ident struct { commentsRef NamePos Position Name string Binding interface{} // a *resolver.Binding, set by resolver } func (x *Ident) Span() (start, end Position) { return x.NamePos, x.NamePos.add(x.Name) } // A Literal represents a literal string or number. type Literal struct { commentsRef Token Token // = STRING | INT TokenPos Position Raw string // uninterpreted text Value interface{} // = string | int64 | *big.Int } func (x *Literal) Span() (start, end Position) { return x.TokenPos, x.TokenPos.add(x.Raw) } // A ParenExpr represents a parenthesized expression: (X). type ParenExpr struct { commentsRef Lparen Position X Expr Rparen Position } func (x *ParenExpr) Span() (start, end Position) { return x.Lparen, x.Rparen.add(")") } // A CallExpr represents a function call expression: Fn(Args). type CallExpr struct { commentsRef Fn Expr Lparen Position Args []Expr // arg = expr | ident=expr | *expr | **expr Rparen Position } func (x *CallExpr) Span() (start, end Position) { start, _ = x.Fn.Span() return start, x.Rparen.add(")") } // A DotExpr represents a field or method selector: X.Name. type DotExpr struct { commentsRef X Expr Dot Position NamePos Position Name *Ident } func (x *DotExpr) Span() (start, end Position) { start, _ = x.X.Span() _, end = x.Name.Span() return } // A Comprehension represents a list or dict comprehension: // [Body for ... if ...] or {Body for ... if ...} type Comprehension struct { commentsRef Curly bool // {x:y for ...} or {x for ...}, not [x for ...] Lbrack Position Body Expr Clauses []Node // = *ForClause | *IfClause Rbrack Position } func (x *Comprehension) Span() (start, end Position) { return x.Lbrack, x.Rbrack.add("]") } // A ForStmt represents a loop: for Vars in X: Body. type ForStmt struct { commentsRef For Position Vars Expr // name, or tuple of names X Expr Body []Stmt } func (x *ForStmt) Span() (start, end Position) { _, end = x.Body[len(x.Body)-1].Span() return x.For, end } // A WhileStmt represents a while loop: while X: Body. type WhileStmt struct { commentsRef While Position Cond Expr Body []Stmt } func (x *WhileStmt) Span() (start, end Position) { _, end = x.Body[len(x.Body)-1].Span() return x.While, end } // A ForClause represents a for clause in a list comprehension: for Vars in X. type ForClause struct { commentsRef For Position Vars Expr // name, or tuple of names In Position X Expr } func (x *ForClause) Span() (start, end Position) { _, end = x.X.Span() return x.For, end } // An IfClause represents an if clause in a list comprehension: if Cond. type IfClause struct { commentsRef If Position Cond Expr } func (x *IfClause) Span() (start, end Position) { _, end = x.Cond.Span() return x.If, end } // A DictExpr represents a dictionary literal: { List }. type DictExpr struct { commentsRef Lbrace Position List []Expr // all *DictEntrys Rbrace Position } func (x *DictExpr) Span() (start, end Position) { return x.Lbrace, x.Rbrace.add("}") } // A DictEntry represents a dictionary entry: Key: Value. // Used only within a DictExpr. type DictEntry struct { commentsRef Key Expr Colon Position Value Expr } func (x *DictEntry) Span() (start, end Position) { start, _ = x.Key.Span() _, end = x.Value.Span() return start, end } // A LambdaExpr represents an inline function abstraction. // // Although they may be added in future, lambda expressions are not // currently part of the Starlark spec, so their use is controlled by the // resolver.AllowLambda flag. type LambdaExpr struct { commentsRef Lambda Position Params []Expr // param = ident | ident=expr | * | *ident | **ident Body Expr Function interface{} // a *resolve.Function, set by resolver } func (x *LambdaExpr) Span() (start, end Position) { _, end = x.Body.Span() return x.Lambda, end } // A ListExpr represents a list literal: [ List ]. type ListExpr struct { commentsRef Lbrack Position List []Expr Rbrack Position } func (x *ListExpr) Span() (start, end Position) { return x.Lbrack, x.Rbrack.add("]") } // CondExpr represents the conditional: X if COND else ELSE. type CondExpr struct { commentsRef If Position Cond Expr True Expr ElsePos Position False Expr } func (x *CondExpr) Span() (start, end Position) { start, _ = x.True.Span() _, end = x.False.Span() return start, end } // A TupleExpr represents a tuple literal: (List). type TupleExpr struct { commentsRef Lparen Position // optional (e.g. in x, y = 0, 1), but required if List is empty List []Expr Rparen Position } func (x *TupleExpr) Span() (start, end Position) { if x.Lparen.IsValid() { return x.Lparen, x.Rparen } else { return Start(x.List[0]), End(x.List[len(x.List)-1]) } } // A UnaryExpr represents a unary expression: Op X. // // As a special case, UnaryOp{Op:Star} may also represent // the star parameter in def f(*args) or def f(*, x). type UnaryExpr struct { commentsRef OpPos Position Op Token X Expr // may be nil if Op==STAR } func (x *UnaryExpr) Span() (start, end Position) { if x.X != nil { _, end = x.X.Span() } else { end = x.OpPos.add("*") } return x.OpPos, end } // A BinaryExpr represents a binary expression: X Op Y. // // As a special case, BinaryExpr{Op:EQ} may also // represent a named argument in a call f(k=v) // or a named parameter in a function declaration // def f(param=default). type BinaryExpr struct { commentsRef X Expr OpPos Position Op Token Y Expr } func (x *BinaryExpr) Span() (start, end Position) { start, _ = x.X.Span() _, end = x.Y.Span() return start, end } // A SliceExpr represents a slice or substring expression: X[Lo:Hi:Step]. type SliceExpr struct { commentsRef X Expr Lbrack Position Lo, Hi, Step Expr // all optional Rbrack Position } func (x *SliceExpr) Span() (start, end Position) { start, _ = x.X.Span() return start, x.Rbrack } // An IndexExpr represents an index expression: X[Y]. type IndexExpr struct { commentsRef X Expr Lbrack Position Y Expr Rbrack Position } func (x *IndexExpr) Span() (start, end Position) { start, _ = x.X.Span() return start, x.Rbrack } ================================================ FILE: vendor/go.starlark.net/syntax/walk.go ================================================ // Copyright 2017 The Bazel Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package syntax // Walk traverses a syntax tree in depth-first order. // It starts by calling f(n); n must not be nil. // If f returns true, Walk calls itself // recursively for each non-nil child of n. // Walk then calls f(nil). func Walk(n Node, f func(Node) bool) { if n == nil { panic("nil") } if !f(n) { return } // TODO(adonovan): opt: order cases using profile data. switch n := n.(type) { case *File: walkStmts(n.Stmts, f) case *ExprStmt: Walk(n.X, f) case *BranchStmt: // no-op case *IfStmt: Walk(n.Cond, f) walkStmts(n.True, f) walkStmts(n.False, f) case *AssignStmt: Walk(n.LHS, f) Walk(n.RHS, f) case *DefStmt: Walk(n.Name, f) for _, param := range n.Params { Walk(param, f) } walkStmts(n.Body, f) case *ForStmt: Walk(n.Vars, f) Walk(n.X, f) walkStmts(n.Body, f) case *ReturnStmt: if n.Result != nil { Walk(n.Result, f) } case *LoadStmt: Walk(n.Module, f) for _, from := range n.From { Walk(from, f) } for _, to := range n.To { Walk(to, f) } case *Ident, *Literal: // no-op case *ListExpr: for _, x := range n.List { Walk(x, f) } case *ParenExpr: Walk(n.X, f) case *CondExpr: Walk(n.Cond, f) Walk(n.True, f) Walk(n.False, f) case *IndexExpr: Walk(n.X, f) Walk(n.Y, f) case *DictEntry: Walk(n.Key, f) Walk(n.Value, f) case *SliceExpr: Walk(n.X, f) if n.Lo != nil { Walk(n.Lo, f) } if n.Hi != nil { Walk(n.Hi, f) } if n.Step != nil { Walk(n.Step, f) } case *Comprehension: Walk(n.Body, f) for _, clause := range n.Clauses { Walk(clause, f) } case *IfClause: Walk(n.Cond, f) case *ForClause: Walk(n.Vars, f) Walk(n.X, f) case *TupleExpr: for _, x := range n.List { Walk(x, f) } case *DictExpr: for _, entry := range n.List { entry := entry.(*DictEntry) Walk(entry.Key, f) Walk(entry.Value, f) } case *UnaryExpr: if n.X != nil { Walk(n.X, f) } case *BinaryExpr: Walk(n.X, f) Walk(n.Y, f) case *DotExpr: Walk(n.X, f) Walk(n.Name, f) case *CallExpr: Walk(n.Fn, f) for _, arg := range n.Args { Walk(arg, f) } case *LambdaExpr: for _, param := range n.Params { Walk(param, f) } Walk(n.Body, f) default: panic(n) } f(nil) } func walkStmts(stmts []Stmt, f func(Node) bool) { for _, stmt := range stmts { Walk(stmt, f) } } ================================================ FILE: vendor/golang.org/x/crypto/LICENSE ================================================ Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/golang.org/x/crypto/PATENTS ================================================ Additional IP Rights Grant (Patents) "This implementation" means the copyrightable works distributed by Google as part of the Go project. Google hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, transfer and otherwise run, modify and propagate the contents of this implementation of Go, where such license applies only to those patent claims, both currently owned or controlled by Google and acquired in the future, licensable by Google that are necessarily infringed by this implementation of Go. This grant does not include claims that would be infringed only as a consequence of further modification of this implementation. If you or your agent or exclusive licensee institute or order or agree to the institution of patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that this implementation of Go or any code incorporated within this implementation of Go constitutes direct or contributory patent infringement, or inducement of patent infringement, then any patent rights granted to you under this License for this implementation of Go shall terminate as of the date such litigation is filed. ================================================ FILE: vendor/golang.org/x/crypto/acme/acme.go ================================================ // Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package acme provides an implementation of the // Automatic Certificate Management Environment (ACME) spec, // most famously used by Let's Encrypt. // // The initial implementation of this package was based on an early version // of the spec. The current implementation supports only the modern // RFC 8555 but some of the old API surface remains for compatibility. // While code using the old API will still compile, it will return an error. // Note the deprecation comments to update your code. // // See https://tools.ietf.org/html/rfc8555 for the spec. // // Most common scenarios will want to use autocert subdirectory instead, // which provides automatic access to certificates from Let's Encrypt // and any other ACME-based CA. package acme import ( "context" "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/sha256" "crypto/tls" "crypto/x509" "crypto/x509/pkix" "encoding/asn1" "encoding/base64" "encoding/json" "errors" "fmt" "math/big" "net" "net/http" "strings" "sync" "time" ) const ( // LetsEncryptURL is the Directory endpoint of Let's Encrypt CA. LetsEncryptURL = "https://acme-v02.api.letsencrypt.org/directory" // ALPNProto is the ALPN protocol name used by a CA server when validating // tls-alpn-01 challenges. // // Package users must ensure their servers can negotiate the ACME ALPN in // order for tls-alpn-01 challenge verifications to succeed. // See the crypto/tls package's Config.NextProtos field. ALPNProto = "acme-tls/1" ) // idPeACMEIdentifier is the OID for the ACME extension for the TLS-ALPN challenge. // https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05#section-5.1 var idPeACMEIdentifier = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 31} const ( maxChainLen = 5 // max depth and breadth of a certificate chain maxCertSize = 1 << 20 // max size of a certificate, in DER bytes // Used for decoding certs from application/pem-certificate-chain response, // the default when in RFC mode. maxCertChainSize = maxCertSize * maxChainLen // Max number of collected nonces kept in memory. // Expect usual peak of 1 or 2. maxNonces = 100 ) // Client is an ACME client. // // The only required field is Key. An example of creating a client with a new key // is as follows: // // key, err := rsa.GenerateKey(rand.Reader, 2048) // if err != nil { // log.Fatal(err) // } // client := &Client{Key: key} type Client struct { // Key is the account key used to register with a CA and sign requests. // Key.Public() must return a *rsa.PublicKey or *ecdsa.PublicKey. // // The following algorithms are supported: // RS256, ES256, ES384 and ES512. // See RFC 7518 for more details about the algorithms. Key crypto.Signer // HTTPClient optionally specifies an HTTP client to use // instead of http.DefaultClient. HTTPClient *http.Client // DirectoryURL points to the CA directory endpoint. // If empty, LetsEncryptURL is used. // Mutating this value after a successful call of Client's Discover method // will have no effect. DirectoryURL string // RetryBackoff computes the duration after which the nth retry of a failed request // should occur. The value of n for the first call on failure is 1. // The values of r and resp are the request and response of the last failed attempt. // If the returned value is negative or zero, no more retries are done and an error // is returned to the caller of the original method. // // Requests which result in a 4xx client error are not retried, // except for 400 Bad Request due to "bad nonce" errors and 429 Too Many Requests. // // If RetryBackoff is nil, a truncated exponential backoff algorithm // with the ceiling of 10 seconds is used, where each subsequent retry n // is done after either ("Retry-After" + jitter) or (2^n seconds + jitter), // preferring the former if "Retry-After" header is found in the resp. // The jitter is a random value up to 1 second. RetryBackoff func(n int, r *http.Request, resp *http.Response) time.Duration // UserAgent is prepended to the User-Agent header sent to the ACME server, // which by default is this package's name and version. // // Reusable libraries and tools in particular should set this value to be // identifiable by the server, in case they are causing issues. UserAgent string cacheMu sync.Mutex dir *Directory // cached result of Client's Discover method // KID is the key identifier provided by the CA. If not provided it will be // retrieved from the CA by making a call to the registration endpoint. KID KeyID noncesMu sync.Mutex nonces map[string]struct{} // nonces collected from previous responses } // accountKID returns a key ID associated with c.Key, the account identity // provided by the CA during RFC based registration. // It assumes c.Discover has already been called. // // accountKID requires at most one network roundtrip. // It caches only successful result. // // When in pre-RFC mode or when c.getRegRFC responds with an error, accountKID // returns noKeyID. func (c *Client) accountKID(ctx context.Context) KeyID { c.cacheMu.Lock() defer c.cacheMu.Unlock() if c.KID != noKeyID { return c.KID } a, err := c.getRegRFC(ctx) if err != nil { return noKeyID } c.KID = KeyID(a.URI) return c.KID } var errPreRFC = errors.New("acme: server does not support the RFC 8555 version of ACME") // Discover performs ACME server discovery using c.DirectoryURL. // // It caches successful result. So, subsequent calls will not result in // a network round-trip. This also means mutating c.DirectoryURL after successful call // of this method will have no effect. func (c *Client) Discover(ctx context.Context) (Directory, error) { c.cacheMu.Lock() defer c.cacheMu.Unlock() if c.dir != nil { return *c.dir, nil } res, err := c.get(ctx, c.directoryURL(), wantStatus(http.StatusOK)) if err != nil { return Directory{}, err } defer res.Body.Close() c.addNonce(res.Header) var v struct { Reg string `json:"newAccount"` Authz string `json:"newAuthz"` Order string `json:"newOrder"` Revoke string `json:"revokeCert"` Nonce string `json:"newNonce"` KeyChange string `json:"keyChange"` Meta struct { Terms string `json:"termsOfService"` Website string `json:"website"` CAA []string `json:"caaIdentities"` ExternalAcct bool `json:"externalAccountRequired"` } } if err := json.NewDecoder(res.Body).Decode(&v); err != nil { return Directory{}, err } if v.Order == "" { return Directory{}, errPreRFC } c.dir = &Directory{ RegURL: v.Reg, AuthzURL: v.Authz, OrderURL: v.Order, RevokeURL: v.Revoke, NonceURL: v.Nonce, KeyChangeURL: v.KeyChange, Terms: v.Meta.Terms, Website: v.Meta.Website, CAA: v.Meta.CAA, ExternalAccountRequired: v.Meta.ExternalAcct, } return *c.dir, nil } func (c *Client) directoryURL() string { if c.DirectoryURL != "" { return c.DirectoryURL } return LetsEncryptURL } // CreateCert was part of the old version of ACME. It is incompatible with RFC 8555. // // Deprecated: this was for the pre-RFC 8555 version of ACME. Callers should use CreateOrderCert. func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration, bundle bool) (der [][]byte, certURL string, err error) { return nil, "", errPreRFC } // FetchCert retrieves already issued certificate from the given url, in DER format. // It retries the request until the certificate is successfully retrieved, // context is cancelled by the caller or an error response is received. // // If the bundle argument is true, the returned value also contains the CA (issuer) // certificate chain. // // FetchCert returns an error if the CA's response or chain was unreasonably large. // Callers are encouraged to parse the returned value to ensure the certificate is valid // and has expected features. func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]byte, error) { if _, err := c.Discover(ctx); err != nil { return nil, err } return c.fetchCertRFC(ctx, url, bundle) } // RevokeCert revokes a previously issued certificate cert, provided in DER format. // // The key argument, used to sign the request, must be authorized // to revoke the certificate. It's up to the CA to decide which keys are authorized. // For instance, the key pair of the certificate may be authorized. // If the key is nil, c.Key is used instead. func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte, reason CRLReasonCode) error { if _, err := c.Discover(ctx); err != nil { return err } return c.revokeCertRFC(ctx, key, cert, reason) } // AcceptTOS always returns true to indicate the acceptance of a CA's Terms of Service // during account registration. See Register method of Client for more details. func AcceptTOS(tosURL string) bool { return true } // Register creates a new account with the CA using c.Key. // It returns the registered account. The account acct is not modified. // // The registration may require the caller to agree to the CA's Terms of Service (TOS). // If so, and the account has not indicated the acceptance of the terms (see Account for details), // Register calls prompt with a TOS URL provided by the CA. Prompt should report // whether the caller agrees to the terms. To always accept the terms, the caller can use AcceptTOS. // // When interfacing with an RFC-compliant CA, non-RFC 8555 fields of acct are ignored // and prompt is called if Directory's Terms field is non-zero. // Also see Error's Instance field for when a CA requires already registered accounts to agree // to an updated Terms of Service. func (c *Client) Register(ctx context.Context, acct *Account, prompt func(tosURL string) bool) (*Account, error) { if c.Key == nil { return nil, errors.New("acme: client.Key must be set to Register") } if _, err := c.Discover(ctx); err != nil { return nil, err } return c.registerRFC(ctx, acct, prompt) } // GetReg retrieves an existing account associated with c.Key. // // The url argument is a legacy artifact of the pre-RFC 8555 API // and is ignored. func (c *Client) GetReg(ctx context.Context, url string) (*Account, error) { if _, err := c.Discover(ctx); err != nil { return nil, err } return c.getRegRFC(ctx) } // UpdateReg updates an existing registration. // It returns an updated account copy. The provided account is not modified. // // The account's URI is ignored and the account URL associated with // c.Key is used instead. func (c *Client) UpdateReg(ctx context.Context, acct *Account) (*Account, error) { if _, err := c.Discover(ctx); err != nil { return nil, err } return c.updateRegRFC(ctx, acct) } // AccountKeyRollover attempts to transition a client's account key to a new key. // On success client's Key is updated which is not concurrency safe. // On failure an error will be returned. // The new key is already registered with the ACME provider if the following is true: // - error is of type acme.Error // - StatusCode should be 409 (Conflict) // - Location header will have the KID of the associated account // // More about account key rollover can be found at // https://tools.ietf.org/html/rfc8555#section-7.3.5. func (c *Client) AccountKeyRollover(ctx context.Context, newKey crypto.Signer) error { return c.accountKeyRollover(ctx, newKey) } // Authorize performs the initial step in the pre-authorization flow, // as opposed to order-based flow. // The caller will then need to choose from and perform a set of returned // challenges using c.Accept in order to successfully complete authorization. // // Once complete, the caller can use AuthorizeOrder which the CA // should provision with the already satisfied authorization. // For pre-RFC CAs, the caller can proceed directly to requesting a certificate // using CreateCert method. // // If an authorization has been previously granted, the CA may return // a valid authorization which has its Status field set to StatusValid. // // More about pre-authorization can be found at // https://tools.ietf.org/html/rfc8555#section-7.4.1. func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization, error) { return c.authorize(ctx, "dns", domain) } // AuthorizeIP is the same as Authorize but requests IP address authorization. // Clients which successfully obtain such authorization may request to issue // a certificate for IP addresses. // // See the ACME spec extension for more details about IP address identifiers: // https://tools.ietf.org/html/draft-ietf-acme-ip. func (c *Client) AuthorizeIP(ctx context.Context, ipaddr string) (*Authorization, error) { return c.authorize(ctx, "ip", ipaddr) } func (c *Client) authorize(ctx context.Context, typ, val string) (*Authorization, error) { if _, err := c.Discover(ctx); err != nil { return nil, err } if c.dir.AuthzURL == "" { // Pre-Authorization is unsupported return nil, errPreAuthorizationNotSupported } type authzID struct { Type string `json:"type"` Value string `json:"value"` } req := struct { Resource string `json:"resource"` Identifier authzID `json:"identifier"` }{ Resource: "new-authz", Identifier: authzID{Type: typ, Value: val}, } res, err := c.post(ctx, nil, c.dir.AuthzURL, req, wantStatus(http.StatusCreated)) if err != nil { return nil, err } defer res.Body.Close() var v wireAuthz if err := json.NewDecoder(res.Body).Decode(&v); err != nil { return nil, fmt.Errorf("acme: invalid response: %v", err) } if v.Status != StatusPending && v.Status != StatusValid { return nil, fmt.Errorf("acme: unexpected status: %s", v.Status) } return v.authorization(res.Header.Get("Location")), nil } // GetAuthorization retrieves an authorization identified by the given URL. // // If a caller needs to poll an authorization until its status is final, // see the WaitAuthorization method. func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorization, error) { if _, err := c.Discover(ctx); err != nil { return nil, err } res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK)) if err != nil { return nil, err } defer res.Body.Close() var v wireAuthz if err := json.NewDecoder(res.Body).Decode(&v); err != nil { return nil, fmt.Errorf("acme: invalid response: %v", err) } return v.authorization(url), nil } // RevokeAuthorization relinquishes an existing authorization identified // by the given URL. // The url argument is an Authorization.URI value. // // If successful, the caller will be required to obtain a new authorization // using the Authorize or AuthorizeOrder methods before being able to request // a new certificate for the domain associated with the authorization. // // It does not revoke existing certificates. func (c *Client) RevokeAuthorization(ctx context.Context, url string) error { if _, err := c.Discover(ctx); err != nil { return err } req := struct { Resource string `json:"resource"` Status string `json:"status"` Delete bool `json:"delete"` }{ Resource: "authz", Status: "deactivated", Delete: true, } res, err := c.post(ctx, nil, url, req, wantStatus(http.StatusOK)) if err != nil { return err } defer res.Body.Close() return nil } // WaitAuthorization polls an authorization at the given URL // until it is in one of the final states, StatusValid or StatusInvalid, // the ACME CA responded with a 4xx error code, or the context is done. // // It returns a non-nil Authorization only if its Status is StatusValid. // In all other cases WaitAuthorization returns an error. // If the Status is StatusInvalid, the returned error is of type *AuthorizationError. func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorization, error) { if _, err := c.Discover(ctx); err != nil { return nil, err } for { res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted)) if err != nil { return nil, err } var raw wireAuthz err = json.NewDecoder(res.Body).Decode(&raw) res.Body.Close() switch { case err != nil: // Skip and retry. case raw.Status == StatusValid: return raw.authorization(url), nil case raw.Status == StatusInvalid: return nil, raw.error(url) } // Exponential backoff is implemented in c.get above. // This is just to prevent continuously hitting the CA // while waiting for a final authorization status. d := retryAfter(res.Header.Get("Retry-After")) if d == 0 { // Given that the fastest challenges TLS-ALPN and HTTP-01 // require a CA to make at least 1 network round trip // and most likely persist a challenge state, // this default delay seems reasonable. d = time.Second } t := time.NewTimer(d) select { case <-ctx.Done(): t.Stop() return nil, ctx.Err() case <-t.C: // Retry. } } } // GetChallenge retrieves the current status of an challenge. // // A client typically polls a challenge status using this method. func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, error) { if _, err := c.Discover(ctx); err != nil { return nil, err } res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted)) if err != nil { return nil, err } defer res.Body.Close() v := wireChallenge{URI: url} if err := json.NewDecoder(res.Body).Decode(&v); err != nil { return nil, fmt.Errorf("acme: invalid response: %v", err) } return v.challenge(), nil } // Accept informs the server that the client accepts one of its challenges // previously obtained with c.Authorize. // // The server will then perform the validation asynchronously. func (c *Client) Accept(ctx context.Context, chal *Challenge) (*Challenge, error) { if _, err := c.Discover(ctx); err != nil { return nil, err } payload := json.RawMessage("{}") if len(chal.Payload) != 0 { payload = chal.Payload } res, err := c.post(ctx, nil, chal.URI, payload, wantStatus( http.StatusOK, // according to the spec http.StatusAccepted, // Let's Encrypt: see https://goo.gl/WsJ7VT (acme-divergences.md) )) if err != nil { return nil, err } defer res.Body.Close() var v wireChallenge if err := json.NewDecoder(res.Body).Decode(&v); err != nil { return nil, fmt.Errorf("acme: invalid response: %v", err) } return v.challenge(), nil } // DNS01ChallengeRecord returns a DNS record value for a dns-01 challenge response. // A TXT record containing the returned value must be provisioned under // "_acme-challenge" name of the domain being validated. // // The token argument is a Challenge.Token value. func (c *Client) DNS01ChallengeRecord(token string) (string, error) { ka, err := keyAuth(c.Key.Public(), token) if err != nil { return "", err } b := sha256.Sum256([]byte(ka)) return base64.RawURLEncoding.EncodeToString(b[:]), nil } // HTTP01ChallengeResponse returns the response for an http-01 challenge. // Servers should respond with the value to HTTP requests at the URL path // provided by HTTP01ChallengePath to validate the challenge and prove control // over a domain name. // // The token argument is a Challenge.Token value. func (c *Client) HTTP01ChallengeResponse(token string) (string, error) { return keyAuth(c.Key.Public(), token) } // HTTP01ChallengePath returns the URL path at which the response for an http-01 challenge // should be provided by the servers. // The response value can be obtained with HTTP01ChallengeResponse. // // The token argument is a Challenge.Token value. func (c *Client) HTTP01ChallengePath(token string) string { return "/.well-known/acme-challenge/" + token } // TLSSNI01ChallengeCert creates a certificate for TLS-SNI-01 challenge response. // Always returns an error. // // Deprecated: This challenge type was only present in pre-standardized ACME // protocol drafts and is insecure for use in shared hosting environments. func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (tls.Certificate, string, error) { return tls.Certificate{}, "", errPreRFC } // TLSSNI02ChallengeCert creates a certificate for TLS-SNI-02 challenge response. // Always returns an error. // // Deprecated: This challenge type was only present in pre-standardized ACME // protocol drafts and is insecure for use in shared hosting environments. func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (tls.Certificate, string, error) { return tls.Certificate{}, "", errPreRFC } // TLSALPN01ChallengeCert creates a certificate for TLS-ALPN-01 challenge response. // Servers can present the certificate to validate the challenge and prove control // over an identifier (either a DNS name or the textual form of an IPv4 or IPv6 // address). For more details on TLS-ALPN-01 see // https://www.rfc-editor.org/rfc/rfc8737 and https://www.rfc-editor.org/rfc/rfc8738 // // The token argument is a Challenge.Token value. // If a WithKey option is provided, its private part signs the returned cert, // and the public part is used to specify the signee. // If no WithKey option is provided, a new ECDSA key is generated using P-256 curve. // // The returned certificate is valid for the next 24 hours and must be presented only when // the server name in the TLS ClientHello matches the identifier, and the special acme-tls/1 ALPN protocol // has been specified. // // Validation requests for IP address identifiers will use the reverse DNS form in the server name // in the TLS ClientHello since the SNI extension is not supported for IP addresses. // See RFC 8738 Section 6 for more information. func (c *Client) TLSALPN01ChallengeCert(token, identifier string, opt ...CertOption) (cert tls.Certificate, err error) { ka, err := keyAuth(c.Key.Public(), token) if err != nil { return tls.Certificate{}, err } shasum := sha256.Sum256([]byte(ka)) extValue, err := asn1.Marshal(shasum[:]) if err != nil { return tls.Certificate{}, err } acmeExtension := pkix.Extension{ Id: idPeACMEIdentifier, Critical: true, Value: extValue, } tmpl := defaultTLSChallengeCertTemplate() var newOpt []CertOption for _, o := range opt { switch o := o.(type) { case *certOptTemplate: t := *(*x509.Certificate)(o) // shallow copy is ok tmpl = &t default: newOpt = append(newOpt, o) } } tmpl.ExtraExtensions = append(tmpl.ExtraExtensions, acmeExtension) newOpt = append(newOpt, WithTemplate(tmpl)) return tlsChallengeCert(identifier, newOpt) } // popNonce returns a nonce value previously stored with c.addNonce // or fetches a fresh one from c.dir.NonceURL. // If NonceURL is empty, it first tries c.directoryURL() and, failing that, // the provided url. func (c *Client) popNonce(ctx context.Context, url string) (string, error) { c.noncesMu.Lock() defer c.noncesMu.Unlock() if len(c.nonces) == 0 { if c.dir != nil && c.dir.NonceURL != "" { return c.fetchNonce(ctx, c.dir.NonceURL) } dirURL := c.directoryURL() v, err := c.fetchNonce(ctx, dirURL) if err != nil && url != dirURL { v, err = c.fetchNonce(ctx, url) } return v, err } var nonce string for nonce = range c.nonces { delete(c.nonces, nonce) break } return nonce, nil } // clearNonces clears any stored nonces func (c *Client) clearNonces() { c.noncesMu.Lock() defer c.noncesMu.Unlock() c.nonces = make(map[string]struct{}) } // addNonce stores a nonce value found in h (if any) for future use. func (c *Client) addNonce(h http.Header) { v := nonceFromHeader(h) if v == "" { return } c.noncesMu.Lock() defer c.noncesMu.Unlock() if len(c.nonces) >= maxNonces { return } if c.nonces == nil { c.nonces = make(map[string]struct{}) } c.nonces[v] = struct{}{} } func (c *Client) fetchNonce(ctx context.Context, url string) (string, error) { r, err := http.NewRequestWithContext(ctx, "HEAD", url, nil) if err != nil { return "", err } resp, err := c.doNoRetry(ctx, r) if err != nil { return "", err } defer resp.Body.Close() nonce := nonceFromHeader(resp.Header) if nonce == "" { if resp.StatusCode > 299 { return "", responseError(resp) } return "", errors.New("acme: nonce not found") } return nonce, nil } func nonceFromHeader(h http.Header) string { return h.Get("Replay-Nonce") } // linkHeader returns URI-Reference values of all Link headers // with relation-type rel. // See https://tools.ietf.org/html/rfc5988#section-5 for details. func linkHeader(h http.Header, rel string) []string { var links []string for _, v := range h["Link"] { parts := strings.Split(v, ";") for _, p := range parts { p = strings.TrimSpace(p) if !strings.HasPrefix(p, "rel=") { continue } if v := strings.Trim(p[4:], `"`); v == rel { links = append(links, strings.Trim(parts[0], "<>")) } } } return links } // keyAuth generates a key authorization string for a given token. func keyAuth(pub crypto.PublicKey, token string) (string, error) { th, err := JWKThumbprint(pub) if err != nil { return "", err } return fmt.Sprintf("%s.%s", token, th), nil } // defaultTLSChallengeCertTemplate is a template used to create challenge certs for TLS challenges. func defaultTLSChallengeCertTemplate() *x509.Certificate { return &x509.Certificate{ SerialNumber: big.NewInt(1), NotBefore: time.Now(), NotAfter: time.Now().Add(24 * time.Hour), BasicConstraintsValid: true, KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, } } // tlsChallengeCert creates a temporary certificate for TLS-ALPN challenges // for the given identifier, using an auto-generated public/private key pair. // // If the provided identifier is a domain name, it will be used as a DNS type SAN and for the // subject common name. If the provided identifier is an IP address it will be used as an IP type // SAN. // // To create a cert with a custom key pair, specify WithKey option. func tlsChallengeCert(identifier string, opt []CertOption) (tls.Certificate, error) { var key crypto.Signer tmpl := defaultTLSChallengeCertTemplate() for _, o := range opt { switch o := o.(type) { case *certOptKey: if key != nil { return tls.Certificate{}, errors.New("acme: duplicate key option") } key = o.key case *certOptTemplate: t := *(*x509.Certificate)(o) // shallow copy is ok tmpl = &t default: // package's fault, if we let this happen: panic(fmt.Sprintf("unsupported option type %T", o)) } } if key == nil { var err error if key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader); err != nil { return tls.Certificate{}, err } } if ip := net.ParseIP(identifier); ip != nil { tmpl.IPAddresses = []net.IP{ip} } else { tmpl.DNSNames = []string{identifier} tmpl.Subject.CommonName = identifier } der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, key.Public(), key) if err != nil { return tls.Certificate{}, err } return tls.Certificate{ Certificate: [][]byte{der}, PrivateKey: key, }, nil } // timeNow is time.Now, except in tests which can mess with it. var timeNow = time.Now ================================================ FILE: vendor/golang.org/x/crypto/acme/autocert/autocert.go ================================================ // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package autocert provides automatic access to certificates from Let's Encrypt // and any other ACME-based CA. // // This package is a work in progress and makes no API stability promises. package autocert import ( "bytes" "context" "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/rsa" "crypto/tls" "crypto/x509" "crypto/x509/pkix" "encoding/pem" "errors" "fmt" "io" mathrand "math/rand" "net" "net/http" "path" "strings" "sync" "time" "golang.org/x/crypto/acme" "golang.org/x/net/idna" ) // DefaultACMEDirectory is the default ACME Directory URL used when the Manager's Client is nil. const DefaultACMEDirectory = "https://acme-v02.api.letsencrypt.org/directory" // createCertRetryAfter is how much time to wait before removing a failed state // entry due to an unsuccessful createCert call. // This is a variable instead of a const for testing. // TODO: Consider making it configurable or an exp backoff? var createCertRetryAfter = time.Minute // pseudoRand is safe for concurrent use. var pseudoRand *lockedMathRand var errPreRFC = errors.New("autocert: ACME server doesn't support RFC 8555") func init() { src := mathrand.NewSource(time.Now().UnixNano()) pseudoRand = &lockedMathRand{rnd: mathrand.New(src)} } // AcceptTOS is a Manager.Prompt function that always returns true to // indicate acceptance of the CA's Terms of Service during account // registration. func AcceptTOS(tosURL string) bool { return true } // HostPolicy specifies which host names the Manager is allowed to respond to. // It returns a non-nil error if the host should be rejected. // The returned error is accessible via tls.Conn.Handshake and its callers. // See Manager's HostPolicy field and GetCertificate method docs for more details. type HostPolicy func(ctx context.Context, host string) error // HostWhitelist returns a policy where only the specified host names are allowed. // Only exact matches are currently supported. Subdomains, regexp or wildcard // will not match. // // Note that all hosts will be converted to Punycode via idna.Lookup.ToASCII so that // Manager.GetCertificate can handle the Unicode IDN and mixedcase hosts correctly. // Invalid hosts will be silently ignored. func HostWhitelist(hosts ...string) HostPolicy { whitelist := make(map[string]bool, len(hosts)) for _, h := range hosts { if h, err := idna.Lookup.ToASCII(h); err == nil { whitelist[h] = true } } return func(_ context.Context, host string) error { if !whitelist[host] { return fmt.Errorf("acme/autocert: host %q not configured in HostWhitelist", host) } return nil } } // defaultHostPolicy is used when Manager.HostPolicy is not set. func defaultHostPolicy(context.Context, string) error { return nil } // Manager is a stateful certificate manager built on top of acme.Client. // It obtains and refreshes certificates automatically using "tls-alpn-01" // or "http-01" challenge types, as well as providing them to a TLS server // via tls.Config. // // You must specify a cache implementation, such as DirCache, // to reuse obtained certificates across program restarts. // Otherwise your server is very likely to exceed the certificate // issuer's request rate limits. type Manager struct { // Prompt specifies a callback function to conditionally accept a CA's Terms of Service (TOS). // The registration may require the caller to agree to the CA's TOS. // If so, Manager calls Prompt with a TOS URL provided by the CA. Prompt should report // whether the caller agrees to the terms. // // To always accept the terms, the callers can use AcceptTOS. Prompt func(tosURL string) bool // Cache optionally stores and retrieves previously-obtained certificates // and other state. If nil, certs will only be cached for the lifetime of // the Manager. Multiple Managers can share the same Cache. // // Using a persistent Cache, such as DirCache, is strongly recommended. Cache Cache // HostPolicy controls which domains the Manager will attempt // to retrieve new certificates for. It does not affect cached certs. // // If non-nil, HostPolicy is called before requesting a new cert. // If nil, all hosts are currently allowed. This is not recommended, // as it opens a potential attack where clients connect to a server // by IP address and pretend to be asking for an incorrect host name. // Manager will attempt to obtain a certificate for that host, incorrectly, // eventually reaching the CA's rate limit for certificate requests // and making it impossible to obtain actual certificates. // // See GetCertificate for more details. HostPolicy HostPolicy // RenewBefore optionally specifies how early certificates should // be renewed before they expire. // // If zero, they're renewed at the lesser of 30 days or // 1/3 of the certificate lifetime. RenewBefore time.Duration // Client is used to perform low-level operations, such as account registration // and requesting new certificates. // // If Client is nil, a zero-value acme.Client is used with DefaultACMEDirectory // as the directory endpoint. // If the Client.Key is nil, a new ECDSA P-256 key is generated and, // if Cache is not nil, stored in cache. // // Mutating the field after the first call of GetCertificate method will have no effect. Client *acme.Client // Email optionally specifies a contact email address. // This is used by CAs, such as Let's Encrypt, to notify about problems // with issued certificates. // // If the Client's account key is already registered, Email is not used. Email string // ForceRSA used to make the Manager generate RSA certificates. It is now ignored. // // Deprecated: the Manager will request the correct type of certificate based // on what each client supports. ForceRSA bool // ExtraExtensions are used when generating a new CSR (Certificate Request), // thus allowing customization of the resulting certificate. // For instance, TLS Feature Extension (RFC 7633) can be used // to prevent an OCSP downgrade attack. // // The field value is passed to crypto/x509.CreateCertificateRequest // in the template's ExtraExtensions field as is. ExtraExtensions []pkix.Extension // ExternalAccountBinding optionally represents an arbitrary binding to an // account of the CA to which the ACME server is tied. // See RFC 8555, Section 7.3.4 for more details. ExternalAccountBinding *acme.ExternalAccountBinding clientMu sync.Mutex client *acme.Client // initialized by acmeClient method stateMu sync.Mutex state map[certKey]*certState // renewal tracks the set of domains currently running renewal timers. renewalMu sync.Mutex renewal map[certKey]*domainRenewal // challengeMu guards tryHTTP01, certTokens and httpTokens. challengeMu sync.RWMutex // tryHTTP01 indicates whether the Manager should try "http-01" challenge type // during the authorization flow. tryHTTP01 bool // httpTokens contains response body values for http-01 challenges // and is keyed by the URL path at which a challenge response is expected // to be provisioned. // The entries are stored for the duration of the authorization flow. httpTokens map[string][]byte // certTokens contains temporary certificates for tls-alpn-01 challenges // and is keyed by the domain name which matches the ClientHello server name. // The entries are stored for the duration of the authorization flow. certTokens map[string]*tls.Certificate // nowFunc, if not nil, returns the current time. This may be set for // testing purposes. nowFunc func() time.Time } // certKey is the key by which certificates are tracked in state, renewal and cache. type certKey struct { domain string // without trailing dot isRSA bool // RSA cert for legacy clients (as opposed to default ECDSA) isToken bool // tls-based challenge token cert; key type is undefined regardless of isRSA } func (c certKey) String() string { if c.isToken { return c.domain + "+token" } if c.isRSA { return c.domain + "+rsa" } return c.domain } // TLSConfig creates a new TLS config suitable for net/http.Server servers, // supporting HTTP/2 and the tls-alpn-01 ACME challenge type. func (m *Manager) TLSConfig() *tls.Config { return &tls.Config{ GetCertificate: m.GetCertificate, NextProtos: []string{ "h2", "http/1.1", // enable HTTP/2 acme.ALPNProto, // enable tls-alpn ACME challenges }, } } // GetCertificate implements the tls.Config.GetCertificate hook. // It provides a TLS certificate for hello.ServerName host, including answering // tls-alpn-01 challenges. // All other fields of hello are ignored. // // If m.HostPolicy is non-nil, GetCertificate calls the policy before requesting // a new cert. A non-nil error returned from m.HostPolicy halts TLS negotiation. // The error is propagated back to the caller of GetCertificate and is user-visible. // This does not affect cached certs. See HostPolicy field description for more details. // // If GetCertificate is used directly, instead of via Manager.TLSConfig, package users will // also have to add acme.ALPNProto to NextProtos for tls-alpn-01, or use HTTPHandler for http-01. func (m *Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) { if m.Prompt == nil { return nil, errors.New("acme/autocert: Manager.Prompt not set") } name := hello.ServerName if name == "" { return nil, errors.New("acme/autocert: missing server name") } if !strings.Contains(strings.Trim(name, "."), ".") { return nil, errors.New("acme/autocert: server name component count invalid") } // Note that this conversion is necessary because some server names in the handshakes // started by some clients (such as cURL) are not converted to Punycode, which will // prevent us from obtaining certificates for them. In addition, we should also treat // example.com and EXAMPLE.COM as equivalent and return the same certificate for them. // Fortunately, this conversion also helped us deal with this kind of mixedcase problems. // // Due to the "σςΣ" problem (see https://unicode.org/faq/idn.html#22), we can't use // idna.Punycode.ToASCII (or just idna.ToASCII) here. name, err := idna.Lookup.ToASCII(name) if err != nil { return nil, errors.New("acme/autocert: server name contains invalid character") } // In the worst-case scenario, the timeout needs to account for caching, host policy, // domain ownership verification and certificate issuance. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) defer cancel() // Check whether this is a token cert requested for TLS-ALPN challenge. if wantsTokenCert(hello) { m.challengeMu.RLock() defer m.challengeMu.RUnlock() if cert := m.certTokens[name]; cert != nil { return cert, nil } if cert, err := m.cacheGet(ctx, certKey{domain: name, isToken: true}); err == nil { return cert, nil } // TODO: cache error results? return nil, fmt.Errorf("acme/autocert: no token cert for %q", name) } // regular domain if err := m.hostPolicy()(ctx, name); err != nil { return nil, err } ck := certKey{ domain: strings.TrimSuffix(name, "."), // golang.org/issue/18114 isRSA: !supportsECDSA(hello), } cert, err := m.cert(ctx, ck) if err == nil { return cert, nil } if err != ErrCacheMiss { return nil, err } // first-time cert, err = m.createCert(ctx, ck) if err != nil { return nil, err } m.cachePut(ctx, ck, cert) return cert, nil } // wantsTokenCert reports whether a TLS request with SNI is made by a CA server // for a challenge verification. func wantsTokenCert(hello *tls.ClientHelloInfo) bool { // tls-alpn-01 if len(hello.SupportedProtos) == 1 && hello.SupportedProtos[0] == acme.ALPNProto { return true } return false } func supportsECDSA(hello *tls.ClientHelloInfo) bool { // The "signature_algorithms" extension, if present, limits the key exchange // algorithms allowed by the cipher suites. See RFC 5246, section 7.4.1.4.1. if hello.SignatureSchemes != nil { ecdsaOK := false schemeLoop: for _, scheme := range hello.SignatureSchemes { const tlsECDSAWithSHA1 tls.SignatureScheme = 0x0203 // constant added in Go 1.10 switch scheme { case tlsECDSAWithSHA1, tls.ECDSAWithP256AndSHA256, tls.ECDSAWithP384AndSHA384, tls.ECDSAWithP521AndSHA512: ecdsaOK = true break schemeLoop } } if !ecdsaOK { return false } } if hello.SupportedCurves != nil { ecdsaOK := false for _, curve := range hello.SupportedCurves { if curve == tls.CurveP256 { ecdsaOK = true break } } if !ecdsaOK { return false } } for _, suite := range hello.CipherSuites { switch suite { case tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305: return true } } return false } // HTTPHandler configures the Manager to provision ACME "http-01" challenge responses. // It returns an http.Handler that responds to the challenges and must be // running on port 80. If it receives a request that is not an ACME challenge, // it delegates the request to the optional fallback handler. // // If fallback is nil, the returned handler redirects all GET and HEAD requests // to the default TLS port 443 with 302 Found status code, preserving the original // request path and query. It responds with 400 Bad Request to all other HTTP methods. // The fallback is not protected by the optional HostPolicy. // // Because the fallback handler is run with unencrypted port 80 requests, // the fallback should not serve TLS-only requests. // // If HTTPHandler is never called, the Manager will only use the "tls-alpn-01" // challenge for domain verification. func (m *Manager) HTTPHandler(fallback http.Handler) http.Handler { m.challengeMu.Lock() defer m.challengeMu.Unlock() m.tryHTTP01 = true if fallback == nil { fallback = http.HandlerFunc(handleHTTPRedirect) } return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if !strings.HasPrefix(r.URL.Path, "/.well-known/acme-challenge/") { fallback.ServeHTTP(w, r) return } // A reasonable context timeout for cache and host policy only, // because we don't wait for a new certificate issuance here. ctx, cancel := context.WithTimeout(r.Context(), time.Minute) defer cancel() if err := m.hostPolicy()(ctx, r.Host); err != nil { http.Error(w, err.Error(), http.StatusForbidden) return } data, err := m.httpToken(ctx, r.URL.Path) if err != nil { http.Error(w, err.Error(), http.StatusNotFound) return } w.Write(data) }) } func handleHTTPRedirect(w http.ResponseWriter, r *http.Request) { if r.Method != "GET" && r.Method != "HEAD" { http.Error(w, "Use HTTPS", http.StatusBadRequest) return } target := "https://" + stripPort(r.Host) + r.URL.RequestURI() http.Redirect(w, r, target, http.StatusFound) } func stripPort(hostport string) string { host, _, err := net.SplitHostPort(hostport) if err != nil { return hostport } return net.JoinHostPort(host, "443") } // cert returns an existing certificate either from m.state or cache. // If a certificate is found in cache but not in m.state, the latter will be filled // with the cached value. func (m *Manager) cert(ctx context.Context, ck certKey) (*tls.Certificate, error) { m.stateMu.Lock() if s, ok := m.state[ck]; ok { m.stateMu.Unlock() s.RLock() defer s.RUnlock() return s.tlscert() } defer m.stateMu.Unlock() cert, err := m.cacheGet(ctx, ck) if err != nil { return nil, err } signer, ok := cert.PrivateKey.(crypto.Signer) if !ok { return nil, errors.New("acme/autocert: private key cannot sign") } if m.state == nil { m.state = make(map[certKey]*certState) } s := &certState{ key: signer, cert: cert.Certificate, leaf: cert.Leaf, } m.state[ck] = s m.startRenew(ck, s.key, s.leaf.NotBefore, s.leaf.NotAfter) return cert, nil } // cacheGet always returns a valid certificate, or an error otherwise. // If a cached certificate exists but is not valid, ErrCacheMiss is returned. func (m *Manager) cacheGet(ctx context.Context, ck certKey) (*tls.Certificate, error) { if m.Cache == nil { return nil, ErrCacheMiss } data, err := m.Cache.Get(ctx, ck.String()) if err != nil { return nil, err } // private priv, pub := pem.Decode(data) if priv == nil || !strings.Contains(priv.Type, "PRIVATE") { return nil, ErrCacheMiss } privKey, err := parsePrivateKey(priv.Bytes) if err != nil { return nil, err } // public var pubDER [][]byte for len(pub) > 0 { var b *pem.Block b, pub = pem.Decode(pub) if b == nil { break } pubDER = append(pubDER, b.Bytes) } if len(pub) > 0 { // Leftover content not consumed by pem.Decode. Corrupt. Ignore. return nil, ErrCacheMiss } // verify and create TLS cert leaf, err := validCert(ck, pubDER, privKey, m.now()) if err != nil { return nil, ErrCacheMiss } tlscert := &tls.Certificate{ Certificate: pubDER, PrivateKey: privKey, Leaf: leaf, } return tlscert, nil } func (m *Manager) cachePut(ctx context.Context, ck certKey, tlscert *tls.Certificate) error { if m.Cache == nil { return nil } // contains PEM-encoded data var buf bytes.Buffer // private switch key := tlscert.PrivateKey.(type) { case *ecdsa.PrivateKey: if err := encodeECDSAKey(&buf, key); err != nil { return err } case *rsa.PrivateKey: b := x509.MarshalPKCS1PrivateKey(key) pb := &pem.Block{Type: "RSA PRIVATE KEY", Bytes: b} if err := pem.Encode(&buf, pb); err != nil { return err } default: return errors.New("acme/autocert: unknown private key type") } // public for _, b := range tlscert.Certificate { pb := &pem.Block{Type: "CERTIFICATE", Bytes: b} if err := pem.Encode(&buf, pb); err != nil { return err } } return m.Cache.Put(ctx, ck.String(), buf.Bytes()) } func encodeECDSAKey(w io.Writer, key *ecdsa.PrivateKey) error { b, err := x509.MarshalECPrivateKey(key) if err != nil { return err } pb := &pem.Block{Type: "EC PRIVATE KEY", Bytes: b} return pem.Encode(w, pb) } // createCert starts the domain ownership verification and returns a certificate // for that domain upon success. // // If the domain is already being verified, it waits for the existing verification to complete. // Either way, createCert blocks for the duration of the whole process. func (m *Manager) createCert(ctx context.Context, ck certKey) (*tls.Certificate, error) { // TODO: maybe rewrite this whole piece using sync.Once state, err := m.certState(ck) if err != nil { return nil, err } // state may exist if another goroutine is already working on it // in which case just wait for it to finish if !state.locked { state.RLock() defer state.RUnlock() return state.tlscert() } // We are the first; state is locked. // Unblock the readers when domain ownership is verified // and we got the cert or the process failed. defer state.Unlock() state.locked = false der, leaf, err := m.authorizedCert(ctx, state.key, ck) if err != nil { // Remove the failed state after some time, // making the manager call createCert again on the following TLS hello. didRemove := testDidRemoveState // The lifetime of this timer is untracked, so copy mutable local state to avoid races. time.AfterFunc(createCertRetryAfter, func() { defer didRemove(ck) m.stateMu.Lock() defer m.stateMu.Unlock() // Verify the state hasn't changed and it's still invalid // before deleting. s, ok := m.state[ck] if !ok { return } if _, err := validCert(ck, s.cert, s.key, m.now()); err == nil { return } delete(m.state, ck) }) return nil, err } state.cert = der state.leaf = leaf m.startRenew(ck, state.key, state.leaf.NotBefore, state.leaf.NotAfter) return state.tlscert() } // certState returns a new or existing certState. // If a new certState is returned, state.exist is false and the state is locked. // The returned error is non-nil only in the case where a new state could not be created. func (m *Manager) certState(ck certKey) (*certState, error) { m.stateMu.Lock() defer m.stateMu.Unlock() if m.state == nil { m.state = make(map[certKey]*certState) } // existing state if state, ok := m.state[ck]; ok { return state, nil } // new locked state var ( err error key crypto.Signer ) if ck.isRSA { key, err = rsa.GenerateKey(rand.Reader, 2048) } else { key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) } if err != nil { return nil, err } state := &certState{ key: key, locked: true, } state.Lock() // will be unlocked by m.certState caller m.state[ck] = state return state, nil } // authorizedCert starts the domain ownership verification process and requests a new cert upon success. // The key argument is the certificate private key. func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, ck certKey) (der [][]byte, leaf *x509.Certificate, err error) { csr, err := certRequest(key, ck.domain, m.ExtraExtensions) if err != nil { return nil, nil, err } client, err := m.acmeClient(ctx) if err != nil { return nil, nil, err } dir, err := client.Discover(ctx) if err != nil { return nil, nil, err } if dir.OrderURL == "" { return nil, nil, errPreRFC } o, err := m.verifyRFC(ctx, client, ck.domain) if err != nil { return nil, nil, err } chain, _, err := client.CreateOrderCert(ctx, o.FinalizeURL, csr, true) if err != nil { return nil, nil, err } leaf, err = validCert(ck, chain, key, m.now()) if err != nil { return nil, nil, err } return chain, leaf, nil } // verifyRFC runs the identifier (domain) order-based authorization flow for RFC compliant CAs // using each applicable ACME challenge type. func (m *Manager) verifyRFC(ctx context.Context, client *acme.Client, domain string) (*acme.Order, error) { // Try each supported challenge type starting with a new order each time. // The nextTyp index of the next challenge type to try is shared across // all order authorizations: if we've tried a challenge type once and it didn't work, // it will most likely not work on another order's authorization either. challengeTypes := m.supportedChallengeTypes() nextTyp := 0 // challengeTypes index AuthorizeOrderLoop: for { o, err := client.AuthorizeOrder(ctx, acme.DomainIDs(domain)) if err != nil { return nil, err } // Remove all hanging authorizations to reduce rate limit quotas // after we're done. defer func(urls []string) { go m.deactivatePendingAuthz(urls) }(o.AuthzURLs) // Check if there's actually anything we need to do. switch o.Status { case acme.StatusReady: // Already authorized. return o, nil case acme.StatusPending: // Continue normal Order-based flow. default: return nil, fmt.Errorf("acme/autocert: invalid new order status %q; order URL: %q", o.Status, o.URI) } // Satisfy all pending authorizations. for _, zurl := range o.AuthzURLs { z, err := client.GetAuthorization(ctx, zurl) if err != nil { return nil, err } if z.Status != acme.StatusPending { // We are interested only in pending authorizations. continue } // Pick the next preferred challenge. var chal *acme.Challenge for chal == nil && nextTyp < len(challengeTypes) { chal = pickChallenge(challengeTypes[nextTyp], z.Challenges) nextTyp++ } if chal == nil { return nil, fmt.Errorf("acme/autocert: unable to satisfy %q for domain %q: no viable challenge type found", z.URI, domain) } // Respond to the challenge and wait for validation result. cleanup, err := m.fulfill(ctx, client, chal, domain) if err != nil { continue AuthorizeOrderLoop } defer cleanup() if _, err := client.Accept(ctx, chal); err != nil { continue AuthorizeOrderLoop } if _, err := client.WaitAuthorization(ctx, z.URI); err != nil { continue AuthorizeOrderLoop } } // All authorizations are satisfied. // Wait for the CA to update the order status. o, err = client.WaitOrder(ctx, o.URI) if err != nil { continue AuthorizeOrderLoop } return o, nil } } func pickChallenge(typ string, chal []*acme.Challenge) *acme.Challenge { for _, c := range chal { if c.Type == typ { return c } } return nil } func (m *Manager) supportedChallengeTypes() []string { m.challengeMu.RLock() defer m.challengeMu.RUnlock() typ := []string{"tls-alpn-01"} if m.tryHTTP01 { typ = append(typ, "http-01") } return typ } // deactivatePendingAuthz relinquishes all authorizations identified by the elements // of the provided uri slice which are in "pending" state. // It ignores revocation errors. // // deactivatePendingAuthz takes no context argument and instead runs with its own // "detached" context because deactivations are done in a goroutine separate from // that of the main issuance or renewal flow. func (m *Manager) deactivatePendingAuthz(uri []string) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) defer cancel() client, err := m.acmeClient(ctx) if err != nil { return } for _, u := range uri { z, err := client.GetAuthorization(ctx, u) if err == nil && z.Status == acme.StatusPending { client.RevokeAuthorization(ctx, u) } } } // fulfill provisions a response to the challenge chal. // The cleanup is non-nil only if provisioning succeeded. func (m *Manager) fulfill(ctx context.Context, client *acme.Client, chal *acme.Challenge, domain string) (cleanup func(), err error) { switch chal.Type { case "tls-alpn-01": cert, err := client.TLSALPN01ChallengeCert(chal.Token, domain) if err != nil { return nil, err } m.putCertToken(ctx, domain, &cert) return func() { go m.deleteCertToken(domain) }, nil case "http-01": resp, err := client.HTTP01ChallengeResponse(chal.Token) if err != nil { return nil, err } p := client.HTTP01ChallengePath(chal.Token) m.putHTTPToken(ctx, p, resp) return func() { go m.deleteHTTPToken(p) }, nil } return nil, fmt.Errorf("acme/autocert: unknown challenge type %q", chal.Type) } // putCertToken stores the token certificate with the specified name // in both m.certTokens map and m.Cache. func (m *Manager) putCertToken(ctx context.Context, name string, cert *tls.Certificate) { m.challengeMu.Lock() defer m.challengeMu.Unlock() if m.certTokens == nil { m.certTokens = make(map[string]*tls.Certificate) } m.certTokens[name] = cert m.cachePut(ctx, certKey{domain: name, isToken: true}, cert) } // deleteCertToken removes the token certificate with the specified name // from both m.certTokens map and m.Cache. func (m *Manager) deleteCertToken(name string) { m.challengeMu.Lock() defer m.challengeMu.Unlock() delete(m.certTokens, name) if m.Cache != nil { ck := certKey{domain: name, isToken: true} m.Cache.Delete(context.Background(), ck.String()) } } // httpToken retrieves an existing http-01 token value from an in-memory map // or the optional cache. func (m *Manager) httpToken(ctx context.Context, tokenPath string) ([]byte, error) { m.challengeMu.RLock() defer m.challengeMu.RUnlock() if v, ok := m.httpTokens[tokenPath]; ok { return v, nil } if m.Cache == nil { return nil, fmt.Errorf("acme/autocert: no token at %q", tokenPath) } return m.Cache.Get(ctx, httpTokenCacheKey(tokenPath)) } // putHTTPToken stores an http-01 token value using tokenPath as key // in both in-memory map and the optional Cache. // // It ignores any error returned from Cache.Put. func (m *Manager) putHTTPToken(ctx context.Context, tokenPath, val string) { m.challengeMu.Lock() defer m.challengeMu.Unlock() if m.httpTokens == nil { m.httpTokens = make(map[string][]byte) } b := []byte(val) m.httpTokens[tokenPath] = b if m.Cache != nil { m.Cache.Put(ctx, httpTokenCacheKey(tokenPath), b) } } // deleteHTTPToken removes an http-01 token value from both in-memory map // and the optional Cache, ignoring any error returned from the latter. // // If m.Cache is non-nil, it blocks until Cache.Delete returns without a timeout. func (m *Manager) deleteHTTPToken(tokenPath string) { m.challengeMu.Lock() defer m.challengeMu.Unlock() delete(m.httpTokens, tokenPath) if m.Cache != nil { m.Cache.Delete(context.Background(), httpTokenCacheKey(tokenPath)) } } // httpTokenCacheKey returns a key at which an http-01 token value may be stored // in the Manager's optional Cache. func httpTokenCacheKey(tokenPath string) string { return path.Base(tokenPath) + "+http-01" } // startRenew starts a cert renewal timer loop, one per domain. // // The loop is scheduled in two cases: // - a cert was fetched from cache for the first time (wasn't in m.state) // - a new cert was created by m.createCert // // The key argument is a certificate private key. // The exp argument is the cert expiration time (NotAfter). func (m *Manager) startRenew(ck certKey, key crypto.Signer, notBefore, notAfter time.Time) { m.renewalMu.Lock() defer m.renewalMu.Unlock() if m.renewal[ck] != nil { // another goroutine is already on it return } if m.renewal == nil { m.renewal = make(map[certKey]*domainRenewal) } dr := &domainRenewal{m: m, ck: ck, key: key} m.renewal[ck] = dr dr.start(notBefore, notAfter) } // stopRenew stops all currently running cert renewal timers. // The timers are not restarted during the lifetime of the Manager. func (m *Manager) stopRenew() { m.renewalMu.Lock() defer m.renewalMu.Unlock() for name, dr := range m.renewal { delete(m.renewal, name) dr.stop() } } func (m *Manager) accountKey(ctx context.Context) (crypto.Signer, error) { const keyName = "acme_account+key" // Previous versions of autocert stored the value under a different key. const legacyKeyName = "acme_account.key" genKey := func() (*ecdsa.PrivateKey, error) { return ecdsa.GenerateKey(elliptic.P256(), rand.Reader) } if m.Cache == nil { return genKey() } data, err := m.Cache.Get(ctx, keyName) if err == ErrCacheMiss { data, err = m.Cache.Get(ctx, legacyKeyName) } if err == ErrCacheMiss { key, err := genKey() if err != nil { return nil, err } var buf bytes.Buffer if err := encodeECDSAKey(&buf, key); err != nil { return nil, err } if err := m.Cache.Put(ctx, keyName, buf.Bytes()); err != nil { return nil, err } return key, nil } if err != nil { return nil, err } priv, _ := pem.Decode(data) if priv == nil || !strings.Contains(priv.Type, "PRIVATE") { return nil, errors.New("acme/autocert: invalid account key found in cache") } return parsePrivateKey(priv.Bytes) } func (m *Manager) acmeClient(ctx context.Context) (*acme.Client, error) { m.clientMu.Lock() defer m.clientMu.Unlock() if m.client != nil { return m.client, nil } client := m.Client if client == nil { client = &acme.Client{DirectoryURL: DefaultACMEDirectory} } if client.Key == nil { var err error client.Key, err = m.accountKey(ctx) if err != nil { return nil, err } } if client.UserAgent == "" { client.UserAgent = "autocert" } var contact []string if m.Email != "" { contact = []string{"mailto:" + m.Email} } a := &acme.Account{Contact: contact, ExternalAccountBinding: m.ExternalAccountBinding} _, err := client.Register(ctx, a, m.Prompt) if err == nil || isAccountAlreadyExist(err) { m.client = client err = nil } return m.client, err } // isAccountAlreadyExist reports whether the err, as returned from acme.Client.Register, // indicates the account has already been registered. func isAccountAlreadyExist(err error) bool { if err == acme.ErrAccountAlreadyExists { return true } ae, ok := err.(*acme.Error) return ok && ae.StatusCode == http.StatusConflict } func (m *Manager) hostPolicy() HostPolicy { if m.HostPolicy != nil { return m.HostPolicy } return defaultHostPolicy } func (m *Manager) now() time.Time { if m.nowFunc != nil { return m.nowFunc() } return time.Now() } // certState is ready when its mutex is unlocked for reading. type certState struct { sync.RWMutex locked bool // locked for read/write key crypto.Signer // private key for cert cert [][]byte // DER encoding leaf *x509.Certificate // parsed cert[0]; always non-nil if cert != nil } // tlscert creates a tls.Certificate from s.key and s.cert. // Callers should wrap it in s.RLock() and s.RUnlock(). func (s *certState) tlscert() (*tls.Certificate, error) { if s.key == nil { return nil, errors.New("acme/autocert: missing signer") } if len(s.cert) == 0 { return nil, errors.New("acme/autocert: missing certificate") } return &tls.Certificate{ PrivateKey: s.key, Certificate: s.cert, Leaf: s.leaf, }, nil } // certRequest generates a CSR for the given common name. func certRequest(key crypto.Signer, name string, ext []pkix.Extension) ([]byte, error) { req := &x509.CertificateRequest{ Subject: pkix.Name{CommonName: name}, DNSNames: []string{name}, ExtraExtensions: ext, } return x509.CreateCertificateRequest(rand.Reader, req, key) } // Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates // PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys. // OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three. // // Inspired by parsePrivateKey in crypto/tls/tls.go. func parsePrivateKey(der []byte) (crypto.Signer, error) { if key, err := x509.ParsePKCS1PrivateKey(der); err == nil { return key, nil } if key, err := x509.ParsePKCS8PrivateKey(der); err == nil { switch key := key.(type) { case *rsa.PrivateKey: return key, nil case *ecdsa.PrivateKey: return key, nil default: return nil, errors.New("acme/autocert: unknown private key type in PKCS#8 wrapping") } } if key, err := x509.ParseECPrivateKey(der); err == nil { return key, nil } return nil, errors.New("acme/autocert: failed to parse private key") } // validCert parses a cert chain provided as der argument and verifies the leaf and der[0] // correspond to the private key, the domain and key type match, and expiration dates // are valid. It doesn't do any revocation checking. // // The returned value is the verified leaf cert. func validCert(ck certKey, der [][]byte, key crypto.Signer, now time.Time) (leaf *x509.Certificate, err error) { // parse public part(s) var n int for _, b := range der { n += len(b) } pub := make([]byte, n) n = 0 for _, b := range der { n += copy(pub[n:], b) } x509Cert, err := x509.ParseCertificates(pub) if err != nil || len(x509Cert) == 0 { return nil, errors.New("acme/autocert: no public key found") } // verify the leaf is not expired and matches the domain name leaf = x509Cert[0] if now.Before(leaf.NotBefore) { return nil, errors.New("acme/autocert: certificate is not valid yet") } if now.After(leaf.NotAfter) { return nil, errors.New("acme/autocert: expired certificate") } if err := leaf.VerifyHostname(ck.domain); err != nil { return nil, err } // renew certificates revoked by Let's Encrypt in January 2022 if isRevokedLetsEncrypt(leaf) { return nil, errors.New("acme/autocert: certificate was probably revoked by Let's Encrypt") } // ensure the leaf corresponds to the private key and matches the certKey type switch pub := leaf.PublicKey.(type) { case *rsa.PublicKey: prv, ok := key.(*rsa.PrivateKey) if !ok { return nil, errors.New("acme/autocert: private key type does not match public key type") } if pub.N.Cmp(prv.N) != 0 { return nil, errors.New("acme/autocert: private key does not match public key") } if !ck.isRSA && !ck.isToken { return nil, errors.New("acme/autocert: key type does not match expected value") } case *ecdsa.PublicKey: prv, ok := key.(*ecdsa.PrivateKey) if !ok { return nil, errors.New("acme/autocert: private key type does not match public key type") } if pub.X.Cmp(prv.X) != 0 || pub.Y.Cmp(prv.Y) != 0 { return nil, errors.New("acme/autocert: private key does not match public key") } if ck.isRSA && !ck.isToken { return nil, errors.New("acme/autocert: key type does not match expected value") } default: return nil, errors.New("acme/autocert: unknown public key algorithm") } return leaf, nil } // https://community.letsencrypt.org/t/2022-01-25-issue-with-tls-alpn-01-validation-method/170450 var letsEncryptFixDeployTime = time.Date(2022, time.January, 26, 00, 48, 0, 0, time.UTC) // isRevokedLetsEncrypt returns whether the certificate is likely to be part of // a batch of certificates revoked by Let's Encrypt in January 2022. This check // can be safely removed from May 2022. func isRevokedLetsEncrypt(cert *x509.Certificate) bool { O := cert.Issuer.Organization return len(O) == 1 && O[0] == "Let's Encrypt" && cert.NotBefore.Before(letsEncryptFixDeployTime) } type lockedMathRand struct { sync.Mutex rnd *mathrand.Rand } func (r *lockedMathRand) int63n(max int64) int64 { r.Lock() n := r.rnd.Int63n(max) r.Unlock() return n } // For easier testing. var ( // Called when a state is removed. testDidRemoveState = func(certKey) {} ) ================================================ FILE: vendor/golang.org/x/crypto/acme/autocert/cache.go ================================================ // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package autocert import ( "context" "errors" "os" "path/filepath" ) // ErrCacheMiss is returned when a certificate is not found in cache. var ErrCacheMiss = errors.New("acme/autocert: certificate cache miss") // Cache is used by Manager to store and retrieve previously obtained certificates // and other account data as opaque blobs. // // Cache implementations should not rely on the key naming pattern. Keys can // include any printable ASCII characters, except the following: \/:*?"<>| type Cache interface { // Get returns a certificate data for the specified key. // If there's no such key, Get returns ErrCacheMiss. Get(ctx context.Context, key string) ([]byte, error) // Put stores the data in the cache under the specified key. // Underlying implementations may use any data storage format, // as long as the reverse operation, Get, results in the original data. Put(ctx context.Context, key string, data []byte) error // Delete removes a certificate data from the cache under the specified key. // If there's no such key in the cache, Delete returns nil. Delete(ctx context.Context, key string) error } // DirCache implements Cache using a directory on the local filesystem. // If the directory does not exist, it will be created with 0700 permissions. type DirCache string // Get reads a certificate data from the specified file name. func (d DirCache) Get(ctx context.Context, name string) ([]byte, error) { name = filepath.Join(string(d), filepath.Clean("/"+name)) var ( data []byte err error done = make(chan struct{}) ) go func() { data, err = os.ReadFile(name) close(done) }() select { case <-ctx.Done(): return nil, ctx.Err() case <-done: } if os.IsNotExist(err) { return nil, ErrCacheMiss } return data, err } // Put writes the certificate data to the specified file name. // The file will be created with 0600 permissions. func (d DirCache) Put(ctx context.Context, name string, data []byte) error { if err := os.MkdirAll(string(d), 0700); err != nil { return err } done := make(chan struct{}) var err error go func() { defer close(done) var tmp string if tmp, err = d.writeTempFile(name, data); err != nil { return } defer os.Remove(tmp) select { case <-ctx.Done(): // Don't overwrite the file if the context was canceled. default: newName := filepath.Join(string(d), filepath.Clean("/"+name)) err = os.Rename(tmp, newName) } }() select { case <-ctx.Done(): return ctx.Err() case <-done: } return err } // Delete removes the specified file name. func (d DirCache) Delete(ctx context.Context, name string) error { name = filepath.Join(string(d), filepath.Clean("/"+name)) var ( err error done = make(chan struct{}) ) go func() { err = os.Remove(name) close(done) }() select { case <-ctx.Done(): return ctx.Err() case <-done: } if err != nil && !os.IsNotExist(err) { return err } return nil } // writeTempFile writes b to a temporary file, closes the file and returns its path. func (d DirCache) writeTempFile(prefix string, b []byte) (name string, reterr error) { // TempFile uses 0600 permissions f, err := os.CreateTemp(string(d), prefix) if err != nil { return "", err } defer func() { if reterr != nil { os.Remove(f.Name()) } }() if _, err := f.Write(b); err != nil { f.Close() return "", err } return f.Name(), f.Close() } ================================================ FILE: vendor/golang.org/x/crypto/acme/autocert/listener.go ================================================ // Copyright 2017 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package autocert import ( "crypto/tls" "log" "net" "os" "path/filepath" "time" ) // NewListener returns a net.Listener that listens on the standard TLS // port (443) on all interfaces and returns *tls.Conn connections with // LetsEncrypt certificates for the provided domain or domains. // // It enables one-line HTTPS servers: // // log.Fatal(http.Serve(autocert.NewListener("example.com"), handler)) // // NewListener is a convenience function for a common configuration. // More complex or custom configurations can use the autocert.Manager // type instead. // // Use of this function implies acceptance of the LetsEncrypt Terms of // Service. If domains is not empty, the provided domains are passed // to HostWhitelist. If domains is empty, the listener will do // LetsEncrypt challenges for any requested domain, which is not // recommended. // // Certificates are cached in a "golang-autocert" directory under an // operating system-specific cache or temp directory. This may not // be suitable for servers spanning multiple machines. // // The returned listener uses a *tls.Config that enables HTTP/2, and // should only be used with servers that support HTTP/2. // // The returned Listener also enables TCP keep-alives on the accepted // connections. The returned *tls.Conn are returned before their TLS // handshake has completed. func NewListener(domains ...string) net.Listener { m := &Manager{ Prompt: AcceptTOS, } if len(domains) > 0 { m.HostPolicy = HostWhitelist(domains...) } dir := cacheDir() if err := os.MkdirAll(dir, 0700); err != nil { log.Printf("warning: autocert.NewListener not using a cache: %v", err) } else { m.Cache = DirCache(dir) } return m.Listener() } // Listener listens on the standard TLS port (443) on all interfaces // and returns a net.Listener returning *tls.Conn connections. // // The returned listener uses a *tls.Config that enables HTTP/2, and // should only be used with servers that support HTTP/2. // // The returned Listener also enables TCP keep-alives on the accepted // connections. The returned *tls.Conn are returned before their TLS // handshake has completed. // // Unlike NewListener, it is the caller's responsibility to initialize // the Manager m's Prompt, Cache, HostPolicy, and other desired options. func (m *Manager) Listener() net.Listener { ln := &listener{ conf: m.TLSConfig(), } ln.tcpListener, ln.tcpListenErr = net.Listen("tcp", ":443") return ln } type listener struct { conf *tls.Config tcpListener net.Listener tcpListenErr error } func (ln *listener) Accept() (net.Conn, error) { if ln.tcpListenErr != nil { return nil, ln.tcpListenErr } conn, err := ln.tcpListener.Accept() if err != nil { return nil, err } tcpConn := conn.(*net.TCPConn) // Because Listener is a convenience function, help out with // this too. This is not possible for the caller to set once // we return a *tcp.Conn wrapping an inaccessible net.Conn. // If callers don't want this, they can do things the manual // way and tweak as needed. But this is what net/http does // itself, so copy that. If net/http changes, we can change // here too. tcpConn.SetKeepAlive(true) tcpConn.SetKeepAlivePeriod(3 * time.Minute) return tls.Server(tcpConn, ln.conf), nil } func (ln *listener) Addr() net.Addr { if ln.tcpListener != nil { return ln.tcpListener.Addr() } // net.Listen failed. Return something non-nil in case callers // call Addr before Accept: return &net.TCPAddr{IP: net.IP{0, 0, 0, 0}, Port: 443} } func (ln *listener) Close() error { if ln.tcpListenErr != nil { return ln.tcpListenErr } return ln.tcpListener.Close() } func cacheDir() string { const base = "golang-autocert" cache, err := os.UserCacheDir() if err != nil { // Fall back to the root directory. cache = "/.cache" } return filepath.Join(cache, base) } ================================================ FILE: vendor/golang.org/x/crypto/acme/autocert/renewal.go ================================================ // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package autocert import ( "context" "crypto" "sync" "time" ) // domainRenewal tracks the state used by the periodic timers // renewing a single domain's cert. type domainRenewal struct { m *Manager ck certKey key crypto.Signer timerMu sync.Mutex timer *time.Timer timerClose chan struct{} // if non-nil, renew closes this channel (and nils out the timer fields) instead of running } // start starts a cert renewal timer at the time // defined by the certificate expiration time exp. // // If the timer is already started, calling start is a noop. func (dr *domainRenewal) start(notBefore, notAfter time.Time) { dr.timerMu.Lock() defer dr.timerMu.Unlock() if dr.timer != nil { return } dr.timer = time.AfterFunc(dr.next(notBefore, notAfter), dr.renew) } // stop stops the cert renewal timer and waits for any in-flight calls to renew // to complete. If the timer is already stopped, calling stop is a noop. func (dr *domainRenewal) stop() { dr.timerMu.Lock() defer dr.timerMu.Unlock() for { if dr.timer == nil { return } if dr.timer.Stop() { dr.timer = nil return } else { // dr.timer fired, and we acquired dr.timerMu before the renew callback did. // (We know this because otherwise the renew callback would have reset dr.timer!) timerClose := make(chan struct{}) dr.timerClose = timerClose dr.timerMu.Unlock() <-timerClose dr.timerMu.Lock() } } } // renew is called periodically by a timer. // The first renew call is kicked off by dr.start. func (dr *domainRenewal) renew() { dr.timerMu.Lock() defer dr.timerMu.Unlock() if dr.timerClose != nil { close(dr.timerClose) dr.timer, dr.timerClose = nil, nil return } ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) defer cancel() // TODO: rotate dr.key at some point? next, err := dr.do(ctx) if err != nil { next = time.Hour / 2 next += time.Duration(pseudoRand.int63n(int64(next))) } testDidRenewLoop(next, err) dr.timer = time.AfterFunc(next, dr.renew) } // updateState locks and replaces the relevant Manager.state item with the given // state. It additionally updates dr.key with the given state's key. func (dr *domainRenewal) updateState(state *certState) { dr.m.stateMu.Lock() defer dr.m.stateMu.Unlock() dr.key = state.key dr.m.state[dr.ck] = state } // do is similar to Manager.createCert but it doesn't lock a Manager.state item. // Instead, it requests a new certificate independently and, upon success, // replaces dr.m.state item with a new one and updates cache for the given domain. // // It may lock and update the Manager.state if the expiration date of the currently // cached cert is far enough in the future. // // The returned value is a time interval after which the renewal should occur again. func (dr *domainRenewal) do(ctx context.Context) (time.Duration, error) { // a race is likely unavoidable in a distributed environment // but we try nonetheless if tlscert, err := dr.m.cacheGet(ctx, dr.ck); err == nil { next := dr.next(tlscert.Leaf.NotBefore, tlscert.Leaf.NotAfter) if next > 0 { signer, ok := tlscert.PrivateKey.(crypto.Signer) if ok { state := &certState{ key: signer, cert: tlscert.Certificate, leaf: tlscert.Leaf, } dr.updateState(state) return next, nil } } } der, leaf, err := dr.m.authorizedCert(ctx, dr.key, dr.ck) if err != nil { return 0, err } state := &certState{ key: dr.key, cert: der, leaf: leaf, } tlscert, err := state.tlscert() if err != nil { return 0, err } if err := dr.m.cachePut(ctx, dr.ck, tlscert); err != nil { return 0, err } dr.updateState(state) return dr.next(leaf.NotBefore, leaf.NotAfter), nil } // next returns the wait time before the next renewal should start. // If manager.RenewBefore is set, it uses that capped at 30 days, // otherwise it uses a default of 1/3 of the cert lifetime. // It builds in a jitter of 10% of the renew threshold, capped at 1 hour. func (dr *domainRenewal) next(notBefore, notAfter time.Time) time.Duration { threshold := min(notAfter.Sub(notBefore)/3, 30*24*time.Hour) if dr.m.RenewBefore > 0 { threshold = min(dr.m.RenewBefore, 30*24*time.Hour) } maxJitter := min(threshold/10, time.Hour) jitter := pseudoRand.int63n(int64(maxJitter)) renewAt := notAfter.Add(-(threshold - time.Duration(jitter))) renewWait := renewAt.Sub(dr.m.now()) return max(0, renewWait) } var testDidRenewLoop = func(next time.Duration, err error) {} ================================================ FILE: vendor/golang.org/x/crypto/acme/http.go ================================================ // Copyright 2018 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package acme import ( "bytes" "context" "crypto" "crypto/rand" "encoding/json" "errors" "fmt" "io" "math/big" "net/http" "runtime/debug" "strconv" "strings" "time" ) // retryTimer encapsulates common logic for retrying unsuccessful requests. // It is not safe for concurrent use. type retryTimer struct { // backoffFn provides backoff delay sequence for retries. // See Client.RetryBackoff doc comment. backoffFn func(n int, r *http.Request, res *http.Response) time.Duration // n is the current retry attempt. n int } func (t *retryTimer) inc() { t.n++ } // backoff pauses the current goroutine as described in Client.RetryBackoff. func (t *retryTimer) backoff(ctx context.Context, r *http.Request, res *http.Response) error { d := t.backoffFn(t.n, r, res) if d <= 0 { return fmt.Errorf("acme: no more retries for %s; tried %d time(s)", r.URL, t.n) } wakeup := time.NewTimer(d) defer wakeup.Stop() select { case <-ctx.Done(): return ctx.Err() case <-wakeup.C: return nil } } func (c *Client) retryTimer() *retryTimer { f := c.RetryBackoff if f == nil { f = defaultBackoff } return &retryTimer{backoffFn: f} } // defaultBackoff provides default Client.RetryBackoff implementation // using a truncated exponential backoff algorithm, // as described in Client.RetryBackoff. // // The n argument is always bounded between 1 and 30. // The returned value is always greater than 0. func defaultBackoff(n int, r *http.Request, res *http.Response) time.Duration { const maxVal = 10 * time.Second var jitter time.Duration if x, err := rand.Int(rand.Reader, big.NewInt(1000)); err == nil { // Set the minimum to 1ms to avoid a case where // an invalid Retry-After value is parsed into 0 below, // resulting in the 0 returned value which would unintentionally // stop the retries. jitter = (1 + time.Duration(x.Int64())) * time.Millisecond } if v, ok := res.Header["Retry-After"]; ok { return retryAfter(v[0]) + jitter } if n < 1 { n = 1 } if n > 30 { n = 30 } d := time.Duration(1<= 500 || code == http.StatusTooManyRequests } // responseError creates an error of Error type from resp. func responseError(resp *http.Response) error { // don't care if ReadAll returns an error: // json.Unmarshal will fail in that case anyway b, _ := io.ReadAll(resp.Body) e := &wireError{Status: resp.StatusCode} if err := json.Unmarshal(b, e); err != nil { // this is not a regular error response: // populate detail with anything we received, // e.Status will already contain HTTP response code value e.Detail = string(b) if e.Detail == "" { e.Detail = resp.Status } } return e.error(resp.Header) } ================================================ FILE: vendor/golang.org/x/crypto/acme/jws.go ================================================ // Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package acme import ( "crypto" "crypto/ecdsa" "crypto/hmac" "crypto/rand" "crypto/rsa" "crypto/sha256" _ "crypto/sha512" // need for EC keys "encoding/asn1" "encoding/base64" "encoding/json" "errors" "fmt" "math/big" ) // KeyID is the account key identity provided by a CA during registration. type KeyID string // noKeyID indicates that jwsEncodeJSON should compute and use JWK instead of a KID. // See jwsEncodeJSON for details. const noKeyID = KeyID("") // noPayload indicates jwsEncodeJSON will encode zero-length octet string // in a JWS request. This is called POST-as-GET in RFC 8555 and is used to make // authenticated GET requests via POSTing with an empty payload. // See https://tools.ietf.org/html/rfc8555#section-6.3 for more details. const noPayload = "" // noNonce indicates that the nonce should be omitted from the protected header. // See jwsEncodeJSON for details. const noNonce = "" // jsonWebSignature can be easily serialized into a JWS following // https://tools.ietf.org/html/rfc7515#section-3.2. type jsonWebSignature struct { Protected string `json:"protected"` Payload string `json:"payload"` Sig string `json:"signature"` } // jwsEncodeJSON signs claimset using provided key and a nonce. // The result is serialized in JSON format containing either kid or jwk // fields based on the provided KeyID value. // // The claimset is marshalled using json.Marshal unless it is a string. // In which case it is inserted directly into the message. // // If kid is non-empty, its quoted value is inserted in the protected header // as "kid" field value. Otherwise, JWK is computed using jwkEncode and inserted // as "jwk" field value. The "jwk" and "kid" fields are mutually exclusive. // // If nonce is non-empty, its quoted value is inserted in the protected header. // // See https://tools.ietf.org/html/rfc7515#section-7. func jwsEncodeJSON(claimset interface{}, key crypto.Signer, kid KeyID, nonce, url string) ([]byte, error) { if key == nil { return nil, errors.New("nil key") } alg, sha := jwsHasher(key.Public()) if alg == "" || !sha.Available() { return nil, ErrUnsupportedKey } headers := struct { Alg string `json:"alg"` KID string `json:"kid,omitempty"` JWK json.RawMessage `json:"jwk,omitempty"` Nonce string `json:"nonce,omitempty"` URL string `json:"url"` }{ Alg: alg, Nonce: nonce, URL: url, } switch kid { case noKeyID: jwk, err := jwkEncode(key.Public()) if err != nil { return nil, err } headers.JWK = json.RawMessage(jwk) default: headers.KID = string(kid) } phJSON, err := json.Marshal(headers) if err != nil { return nil, err } phead := base64.RawURLEncoding.EncodeToString(phJSON) var payload string if val, ok := claimset.(string); ok { payload = val } else { cs, err := json.Marshal(claimset) if err != nil { return nil, err } payload = base64.RawURLEncoding.EncodeToString(cs) } hash := sha.New() hash.Write([]byte(phead + "." + payload)) sig, err := jwsSign(key, sha, hash.Sum(nil)) if err != nil { return nil, err } enc := jsonWebSignature{ Protected: phead, Payload: payload, Sig: base64.RawURLEncoding.EncodeToString(sig), } return json.Marshal(&enc) } // jwsWithMAC creates and signs a JWS using the given key and the HS256 // algorithm. kid and url are included in the protected header. rawPayload // should not be base64-URL-encoded. func jwsWithMAC(key []byte, kid, url string, rawPayload []byte) (*jsonWebSignature, error) { if len(key) == 0 { return nil, errors.New("acme: cannot sign JWS with an empty MAC key") } header := struct { Algorithm string `json:"alg"` KID string `json:"kid"` URL string `json:"url,omitempty"` }{ // Only HMAC-SHA256 is supported. Algorithm: "HS256", KID: kid, URL: url, } rawProtected, err := json.Marshal(header) if err != nil { return nil, err } protected := base64.RawURLEncoding.EncodeToString(rawProtected) payload := base64.RawURLEncoding.EncodeToString(rawPayload) h := hmac.New(sha256.New, key) if _, err := h.Write([]byte(protected + "." + payload)); err != nil { return nil, err } mac := h.Sum(nil) return &jsonWebSignature{ Protected: protected, Payload: payload, Sig: base64.RawURLEncoding.EncodeToString(mac), }, nil } // jwkEncode encodes public part of an RSA or ECDSA key into a JWK. // The result is also suitable for creating a JWK thumbprint. // https://tools.ietf.org/html/rfc7517 func jwkEncode(pub crypto.PublicKey) (string, error) { switch pub := pub.(type) { case *rsa.PublicKey: // https://tools.ietf.org/html/rfc7518#section-6.3.1 n := pub.N e := big.NewInt(int64(pub.E)) // Field order is important. // See https://tools.ietf.org/html/rfc7638#section-3.3 for details. return fmt.Sprintf(`{"e":"%s","kty":"RSA","n":"%s"}`, base64.RawURLEncoding.EncodeToString(e.Bytes()), base64.RawURLEncoding.EncodeToString(n.Bytes()), ), nil case *ecdsa.PublicKey: // https://tools.ietf.org/html/rfc7518#section-6.2.1 p := pub.Curve.Params() n := p.BitSize / 8 if p.BitSize%8 != 0 { n++ } x := pub.X.Bytes() if n > len(x) { x = append(make([]byte, n-len(x)), x...) } y := pub.Y.Bytes() if n > len(y) { y = append(make([]byte, n-len(y)), y...) } // Field order is important. // See https://tools.ietf.org/html/rfc7638#section-3.3 for details. return fmt.Sprintf(`{"crv":"%s","kty":"EC","x":"%s","y":"%s"}`, p.Name, base64.RawURLEncoding.EncodeToString(x), base64.RawURLEncoding.EncodeToString(y), ), nil } return "", ErrUnsupportedKey } // jwsSign signs the digest using the given key. // The hash is unused for ECDSA keys. func jwsSign(key crypto.Signer, hash crypto.Hash, digest []byte) ([]byte, error) { switch pub := key.Public().(type) { case *rsa.PublicKey: return key.Sign(rand.Reader, digest, hash) case *ecdsa.PublicKey: sigASN1, err := key.Sign(rand.Reader, digest, hash) if err != nil { return nil, err } var rs struct{ R, S *big.Int } if _, err := asn1.Unmarshal(sigASN1, &rs); err != nil { return nil, err } rb, sb := rs.R.Bytes(), rs.S.Bytes() size := pub.Params().BitSize / 8 if size%8 > 0 { size++ } sig := make([]byte, size*2) copy(sig[size-len(rb):], rb) copy(sig[size*2-len(sb):], sb) return sig, nil } return nil, ErrUnsupportedKey } // jwsHasher indicates suitable JWS algorithm name and a hash function // to use for signing a digest with the provided key. // It returns ("", 0) if the key is not supported. func jwsHasher(pub crypto.PublicKey) (string, crypto.Hash) { switch pub := pub.(type) { case *rsa.PublicKey: return "RS256", crypto.SHA256 case *ecdsa.PublicKey: switch pub.Params().Name { case "P-256": return "ES256", crypto.SHA256 case "P-384": return "ES384", crypto.SHA384 case "P-521": return "ES512", crypto.SHA512 } } return "", 0 } // JWKThumbprint creates a JWK thumbprint out of pub // as specified in https://tools.ietf.org/html/rfc7638. func JWKThumbprint(pub crypto.PublicKey) (string, error) { jwk, err := jwkEncode(pub) if err != nil { return "", err } b := sha256.Sum256([]byte(jwk)) return base64.RawURLEncoding.EncodeToString(b[:]), nil } ================================================ FILE: vendor/golang.org/x/crypto/acme/rfc8555.go ================================================ // Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package acme import ( "context" "crypto" "encoding/base64" "encoding/json" "encoding/pem" "errors" "fmt" "io" "net/http" "time" ) // DeactivateReg permanently disables an existing account associated with c.Key. // A deactivated account can no longer request certificate issuance or access // resources related to the account, such as orders or authorizations. // // It only works with CAs implementing RFC 8555. func (c *Client) DeactivateReg(ctx context.Context) error { if _, err := c.Discover(ctx); err != nil { // required by c.accountKID return err } url := string(c.accountKID(ctx)) if url == "" { return ErrNoAccount } req := json.RawMessage(`{"status": "deactivated"}`) res, err := c.post(ctx, nil, url, req, wantStatus(http.StatusOK)) if err != nil { return err } res.Body.Close() return nil } // registerRFC is equivalent to c.Register but for CAs implementing RFC 8555. // It expects c.Discover to have already been called. func (c *Client) registerRFC(ctx context.Context, acct *Account, prompt func(tosURL string) bool) (*Account, error) { c.cacheMu.Lock() // guard c.kid access defer c.cacheMu.Unlock() req := struct { TermsAgreed bool `json:"termsOfServiceAgreed,omitempty"` Contact []string `json:"contact,omitempty"` ExternalAccountBinding *jsonWebSignature `json:"externalAccountBinding,omitempty"` }{ Contact: acct.Contact, } if c.dir.Terms != "" { req.TermsAgreed = prompt(c.dir.Terms) } // set 'externalAccountBinding' field if requested if acct.ExternalAccountBinding != nil { eabJWS, err := c.encodeExternalAccountBinding(acct.ExternalAccountBinding) if err != nil { return nil, fmt.Errorf("acme: failed to encode external account binding: %v", err) } req.ExternalAccountBinding = eabJWS } res, err := c.post(ctx, c.Key, c.dir.RegURL, req, wantStatus( http.StatusOK, // account with this key already registered http.StatusCreated, // new account created )) if err != nil { return nil, err } defer res.Body.Close() a, err := responseAccount(res) if err != nil { return nil, err } // Cache Account URL even if we return an error to the caller. // It is by all means a valid and usable "kid" value for future requests. c.KID = KeyID(a.URI) if res.StatusCode == http.StatusOK { return nil, ErrAccountAlreadyExists } return a, nil } // encodeExternalAccountBinding will encode an external account binding stanza // as described in https://tools.ietf.org/html/rfc8555#section-7.3.4. func (c *Client) encodeExternalAccountBinding(eab *ExternalAccountBinding) (*jsonWebSignature, error) { jwk, err := jwkEncode(c.Key.Public()) if err != nil { return nil, err } return jwsWithMAC(eab.Key, eab.KID, c.dir.RegURL, []byte(jwk)) } // updateRegRFC is equivalent to c.UpdateReg but for CAs implementing RFC 8555. // It expects c.Discover to have already been called. func (c *Client) updateRegRFC(ctx context.Context, a *Account) (*Account, error) { url := string(c.accountKID(ctx)) if url == "" { return nil, ErrNoAccount } req := struct { Contact []string `json:"contact,omitempty"` }{ Contact: a.Contact, } res, err := c.post(ctx, nil, url, req, wantStatus(http.StatusOK)) if err != nil { return nil, err } defer res.Body.Close() return responseAccount(res) } // getRegRFC is equivalent to c.GetReg but for CAs implementing RFC 8555. // It expects c.Discover to have already been called. func (c *Client) getRegRFC(ctx context.Context) (*Account, error) { req := json.RawMessage(`{"onlyReturnExisting": true}`) res, err := c.post(ctx, c.Key, c.dir.RegURL, req, wantStatus(http.StatusOK)) if e, ok := err.(*Error); ok && e.ProblemType == "urn:ietf:params:acme:error:accountDoesNotExist" { return nil, ErrNoAccount } if err != nil { return nil, err } defer res.Body.Close() return responseAccount(res) } func responseAccount(res *http.Response) (*Account, error) { var v struct { Status string Contact []string Orders string } if err := json.NewDecoder(res.Body).Decode(&v); err != nil { return nil, fmt.Errorf("acme: invalid account response: %v", err) } return &Account{ URI: res.Header.Get("Location"), Status: v.Status, Contact: v.Contact, OrdersURL: v.Orders, }, nil } // accountKeyRollover attempts to perform account key rollover. // On success it will change client.Key to the new key. func (c *Client) accountKeyRollover(ctx context.Context, newKey crypto.Signer) error { dir, err := c.Discover(ctx) // Also required by c.accountKID if err != nil { return err } kid := c.accountKID(ctx) if kid == noKeyID { return ErrNoAccount } oldKey, err := jwkEncode(c.Key.Public()) if err != nil { return err } payload := struct { Account string `json:"account"` OldKey json.RawMessage `json:"oldKey"` }{ Account: string(kid), OldKey: json.RawMessage(oldKey), } inner, err := jwsEncodeJSON(payload, newKey, noKeyID, noNonce, dir.KeyChangeURL) if err != nil { return err } res, err := c.post(ctx, nil, dir.KeyChangeURL, base64.RawURLEncoding.EncodeToString(inner), wantStatus(http.StatusOK)) if err != nil { return err } defer res.Body.Close() c.Key = newKey return nil } // AuthorizeOrder initiates the order-based application for certificate issuance, // as opposed to pre-authorization in Authorize. // It is only supported by CAs implementing RFC 8555. // // The caller then needs to fetch each authorization with GetAuthorization, // identify those with StatusPending status and fulfill a challenge using Accept. // Once all authorizations are satisfied, the caller will typically want to poll // order status using WaitOrder until it's in StatusReady state. // To finalize the order and obtain a certificate, the caller submits a CSR with CreateOrderCert. func (c *Client) AuthorizeOrder(ctx context.Context, id []AuthzID, opt ...OrderOption) (*Order, error) { dir, err := c.Discover(ctx) if err != nil { return nil, err } req := struct { Identifiers []wireAuthzID `json:"identifiers"` NotBefore string `json:"notBefore,omitempty"` NotAfter string `json:"notAfter,omitempty"` }{} for _, v := range id { req.Identifiers = append(req.Identifiers, wireAuthzID{ Type: v.Type, Value: v.Value, }) } for _, o := range opt { switch o := o.(type) { case orderNotBeforeOpt: req.NotBefore = time.Time(o).Format(time.RFC3339) case orderNotAfterOpt: req.NotAfter = time.Time(o).Format(time.RFC3339) default: // Package's fault if we let this happen. panic(fmt.Sprintf("unsupported order option type %T", o)) } } res, err := c.post(ctx, nil, dir.OrderURL, req, wantStatus(http.StatusCreated)) if err != nil { return nil, err } defer res.Body.Close() return responseOrder(res) } // GetOrder retrieves an order identified by the given URL. // For orders created with AuthorizeOrder, the url value is Order.URI. // // If a caller needs to poll an order until its status is final, // see the WaitOrder method. func (c *Client) GetOrder(ctx context.Context, url string) (*Order, error) { if _, err := c.Discover(ctx); err != nil { return nil, err } res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK)) if err != nil { return nil, err } defer res.Body.Close() return responseOrder(res) } // WaitOrder polls an order from the given URL until it is in one of the final states, // StatusReady, StatusValid or StatusInvalid, the CA responded with a non-retryable error // or the context is done. // // It returns a non-nil Order only if its Status is StatusReady or StatusValid. // In all other cases WaitOrder returns an error. // If the Status is StatusInvalid, the returned error is of type *OrderError. func (c *Client) WaitOrder(ctx context.Context, url string) (*Order, error) { if _, err := c.Discover(ctx); err != nil { return nil, err } for { res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK)) if err != nil { return nil, err } o, err := responseOrder(res) res.Body.Close() switch { case err != nil: // Skip and retry. case o.Status == StatusInvalid: return nil, &OrderError{OrderURL: o.URI, Status: o.Status, Problem: o.Error} case o.Status == StatusReady || o.Status == StatusValid: return o, nil } d := retryAfter(res.Header.Get("Retry-After")) if d == 0 { // Default retry-after. // Same reasoning as in WaitAuthorization. d = time.Second } t := time.NewTimer(d) select { case <-ctx.Done(): t.Stop() return nil, ctx.Err() case <-t.C: // Retry. } } } func responseOrder(res *http.Response) (*Order, error) { var v struct { Status string Expires time.Time Identifiers []wireAuthzID NotBefore time.Time NotAfter time.Time Error *wireError Authorizations []string Finalize string Certificate string } if err := json.NewDecoder(res.Body).Decode(&v); err != nil { return nil, fmt.Errorf("acme: error reading order: %v", err) } o := &Order{ URI: res.Header.Get("Location"), Status: v.Status, Expires: v.Expires, NotBefore: v.NotBefore, NotAfter: v.NotAfter, AuthzURLs: v.Authorizations, FinalizeURL: v.Finalize, CertURL: v.Certificate, } for _, id := range v.Identifiers { o.Identifiers = append(o.Identifiers, AuthzID{Type: id.Type, Value: id.Value}) } if v.Error != nil { o.Error = v.Error.error(nil /* headers */) } return o, nil } // CreateOrderCert submits the CSR (Certificate Signing Request) to a CA at the specified URL. // The URL is the FinalizeURL field of an Order created with AuthorizeOrder. // // If the bundle argument is true, the returned value also contain the CA (issuer) // certificate chain. Otherwise, only a leaf certificate is returned. // The returned URL can be used to re-fetch the certificate using FetchCert. // // This method is only supported by CAs implementing RFC 8555. See CreateCert for pre-RFC CAs. // // CreateOrderCert returns an error if the CA's response is unreasonably large. // Callers are encouraged to parse the returned value to ensure the certificate is valid and has the expected features. func (c *Client) CreateOrderCert(ctx context.Context, url string, csr []byte, bundle bool) (der [][]byte, certURL string, err error) { if _, err := c.Discover(ctx); err != nil { // required by c.accountKID return nil, "", err } // RFC describes this as "finalize order" request. req := struct { CSR string `json:"csr"` }{ CSR: base64.RawURLEncoding.EncodeToString(csr), } res, err := c.post(ctx, nil, url, req, wantStatus(http.StatusOK)) if err != nil { return nil, "", err } defer res.Body.Close() o, err := responseOrder(res) if err != nil { return nil, "", err } // Wait for CA to issue the cert if they haven't. if o.Status != StatusValid { o, err = c.WaitOrder(ctx, o.URI) } if err != nil { return nil, "", err } // The only acceptable status post finalize and WaitOrder is "valid". if o.Status != StatusValid { return nil, "", &OrderError{OrderURL: o.URI, Status: o.Status, Problem: o.Error} } crt, err := c.fetchCertRFC(ctx, o.CertURL, bundle) return crt, o.CertURL, err } // fetchCertRFC downloads issued certificate from the given URL. // It expects the CA to respond with PEM-encoded certificate chain. // // The URL argument is the CertURL field of Order. func (c *Client) fetchCertRFC(ctx context.Context, url string, bundle bool) ([][]byte, error) { res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK)) if err != nil { return nil, err } defer res.Body.Close() // Get all the bytes up to a sane maximum. // Account very roughly for base64 overhead. const max = maxCertChainSize + maxCertChainSize/33 b, err := io.ReadAll(io.LimitReader(res.Body, max+1)) if err != nil { return nil, fmt.Errorf("acme: fetch cert response stream: %v", err) } if len(b) > max { return nil, errors.New("acme: certificate chain is too big") } // Decode PEM chain. var chain [][]byte for { var p *pem.Block p, b = pem.Decode(b) if p == nil { break } if p.Type != "CERTIFICATE" { return nil, fmt.Errorf("acme: invalid PEM cert type %q", p.Type) } chain = append(chain, p.Bytes) if !bundle { return chain, nil } if len(chain) > maxChainLen { return nil, errors.New("acme: certificate chain is too long") } } if len(chain) == 0 { return nil, errors.New("acme: certificate chain is empty") } return chain, nil } // sends a cert revocation request in either JWK form when key is non-nil or KID form otherwise. func (c *Client) revokeCertRFC(ctx context.Context, key crypto.Signer, cert []byte, reason CRLReasonCode) error { req := &struct { Cert string `json:"certificate"` Reason int `json:"reason"` }{ Cert: base64.RawURLEncoding.EncodeToString(cert), Reason: int(reason), } res, err := c.post(ctx, key, c.dir.RevokeURL, req, wantStatus(http.StatusOK)) if err != nil { if isAlreadyRevoked(err) { // Assume it is not an error to revoke an already revoked cert. return nil } return err } defer res.Body.Close() return nil } func isAlreadyRevoked(err error) bool { e, ok := err.(*Error) return ok && e.ProblemType == "urn:ietf:params:acme:error:alreadyRevoked" } // ListCertAlternates retrieves any alternate certificate chain URLs for the // given certificate chain URL. These alternate URLs can be passed to FetchCert // in order to retrieve the alternate certificate chains. // // If there are no alternate issuer certificate chains, a nil slice will be // returned. func (c *Client) ListCertAlternates(ctx context.Context, url string) ([]string, error) { if _, err := c.Discover(ctx); err != nil { // required by c.accountKID return nil, err } res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK)) if err != nil { return nil, err } defer res.Body.Close() // We don't need the body but we need to discard it so we don't end up // preventing keep-alive if _, err := io.Copy(io.Discard, res.Body); err != nil { return nil, fmt.Errorf("acme: cert alternates response stream: %v", err) } alts := linkHeader(res.Header, "alternate") return alts, nil } ================================================ FILE: vendor/golang.org/x/crypto/acme/types.go ================================================ // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package acme import ( "crypto" "crypto/x509" "encoding/json" "errors" "fmt" "net/http" "strings" "time" ) // ACME status values of Account, Order, Authorization and Challenge objects. // See https://tools.ietf.org/html/rfc8555#section-7.1.6 for details. const ( StatusDeactivated = "deactivated" StatusExpired = "expired" StatusInvalid = "invalid" StatusPending = "pending" StatusProcessing = "processing" StatusReady = "ready" StatusRevoked = "revoked" StatusUnknown = "unknown" StatusValid = "valid" ) // CRLReasonCode identifies the reason for a certificate revocation. type CRLReasonCode int // CRL reason codes as defined in RFC 5280. const ( CRLReasonUnspecified CRLReasonCode = 0 CRLReasonKeyCompromise CRLReasonCode = 1 CRLReasonCACompromise CRLReasonCode = 2 CRLReasonAffiliationChanged CRLReasonCode = 3 CRLReasonSuperseded CRLReasonCode = 4 CRLReasonCessationOfOperation CRLReasonCode = 5 CRLReasonCertificateHold CRLReasonCode = 6 CRLReasonRemoveFromCRL CRLReasonCode = 8 CRLReasonPrivilegeWithdrawn CRLReasonCode = 9 CRLReasonAACompromise CRLReasonCode = 10 ) var ( // ErrUnsupportedKey is returned when an unsupported key type is encountered. ErrUnsupportedKey = errors.New("acme: unknown key type; only RSA and ECDSA are supported") // ErrAccountAlreadyExists indicates that the Client's key has already been registered // with the CA. It is returned by Register method. ErrAccountAlreadyExists = errors.New("acme: account already exists") // ErrNoAccount indicates that the Client's key has not been registered with the CA. ErrNoAccount = errors.New("acme: account does not exist") // errPreAuthorizationNotSupported indicates that the server does not // support pre-authorization of identifiers. errPreAuthorizationNotSupported = errors.New("acme: pre-authorization is not supported") ) // A Subproblem describes an ACME subproblem as reported in an Error. type Subproblem struct { // Type is a URI reference that identifies the problem type, // typically in a "urn:acme:error:xxx" form. Type string // Detail is a human-readable explanation specific to this occurrence of the problem. Detail string // Instance indicates a URL that the client should direct a human user to visit // in order for instructions on how to agree to the updated Terms of Service. // In such an event CA sets StatusCode to 403, Type to // "urn:ietf:params:acme:error:userActionRequired", and adds a Link header with relation // "terms-of-service" containing the latest TOS URL. Instance string // Identifier may contain the ACME identifier that the error is for. Identifier *AuthzID } func (sp Subproblem) String() string { str := fmt.Sprintf("%s: ", sp.Type) if sp.Identifier != nil { str += fmt.Sprintf("[%s: %s] ", sp.Identifier.Type, sp.Identifier.Value) } str += sp.Detail return str } // Error is an ACME error, defined in Problem Details for HTTP APIs doc // http://tools.ietf.org/html/draft-ietf-appsawg-http-problem. type Error struct { // StatusCode is The HTTP status code generated by the origin server. StatusCode int // ProblemType is a URI reference that identifies the problem type, // typically in a "urn:acme:error:xxx" form. ProblemType string // Detail is a human-readable explanation specific to this occurrence of the problem. Detail string // Instance indicates a URL that the client should direct a human user to visit // in order for instructions on how to agree to the updated Terms of Service. // In such an event CA sets StatusCode to 403, ProblemType to // "urn:ietf:params:acme:error:userActionRequired" and a Link header with relation // "terms-of-service" containing the latest TOS URL. Instance string // Header is the original server error response headers. // It may be nil. Header http.Header // Subproblems may contain more detailed information about the individual problems // that caused the error. This field is only sent by RFC 8555 compatible ACME // servers. Defined in RFC 8555 Section 6.7.1. Subproblems []Subproblem } func (e *Error) Error() string { str := fmt.Sprintf("%d %s: %s", e.StatusCode, e.ProblemType, e.Detail) if len(e.Subproblems) > 0 { str += fmt.Sprintf("; subproblems:") for _, sp := range e.Subproblems { str += fmt.Sprintf("\n\t%s", sp) } } return str } // AuthorizationError indicates that an authorization for an identifier // did not succeed. // It contains all errors from Challenge items of the failed Authorization. type AuthorizationError struct { // URI uniquely identifies the failed Authorization. URI string // Identifier is an AuthzID.Value of the failed Authorization. Identifier string // Errors is a collection of non-nil error values of Challenge items // of the failed Authorization. Errors []error } func (a *AuthorizationError) Error() string { e := make([]string, len(a.Errors)) for i, err := range a.Errors { e[i] = err.Error() } if a.Identifier != "" { return fmt.Sprintf("acme: authorization error for %s: %s", a.Identifier, strings.Join(e, "; ")) } return fmt.Sprintf("acme: authorization error: %s", strings.Join(e, "; ")) } // OrderError is returned from Client's order related methods. // It indicates the order is unusable and the clients should start over with // AuthorizeOrder. A Problem description may be provided with details on // what caused the order to become unusable. // // The clients can still fetch the order object from CA using GetOrder // to inspect its state. type OrderError struct { OrderURL string Status string // Problem is the error that occurred while processing the order. Problem *Error } func (oe *OrderError) Error() string { return fmt.Sprintf("acme: order %s status: %s", oe.OrderURL, oe.Status) } // RateLimit reports whether err represents a rate limit error and // any Retry-After duration returned by the server. // // See the following for more details on rate limiting: // https://tools.ietf.org/html/draft-ietf-acme-acme-05#section-5.6 func RateLimit(err error) (time.Duration, bool) { e, ok := err.(*Error) if !ok { return 0, false } // Some CA implementations may return incorrect values. // Use case-insensitive comparison. if !strings.HasSuffix(strings.ToLower(e.ProblemType), ":ratelimited") { return 0, false } if e.Header == nil { return 0, true } return retryAfter(e.Header.Get("Retry-After")), true } // Account is a user account. It is associated with a private key. // Non-RFC 8555 fields are empty when interfacing with a compliant CA. type Account struct { // URI is the account unique ID, which is also a URL used to retrieve // account data from the CA. // When interfacing with RFC 8555-compliant CAs, URI is the "kid" field // value in JWS signed requests. URI string // Contact is a slice of contact info used during registration. // See https://tools.ietf.org/html/rfc8555#section-7.3 for supported // formats. Contact []string // Status indicates current account status as returned by the CA. // Possible values are StatusValid, StatusDeactivated, and StatusRevoked. Status string // OrdersURL is a URL from which a list of orders submitted by this account // can be fetched. OrdersURL string // The terms user has agreed to. // A value not matching CurrentTerms indicates that the user hasn't agreed // to the actual Terms of Service of the CA. // // It is non-RFC 8555 compliant. Package users can store the ToS they agree to // during Client's Register call in the prompt callback function. AgreedTerms string // Actual terms of a CA. // // It is non-RFC 8555 compliant. Use Directory's Terms field. // When a CA updates their terms and requires an account agreement, // a URL at which instructions to do so is available in Error's Instance field. CurrentTerms string // Authz is the authorization URL used to initiate a new authz flow. // // It is non-RFC 8555 compliant. Use Directory's AuthzURL or OrderURL. Authz string // Authorizations is a URI from which a list of authorizations // granted to this account can be fetched via a GET request. // // It is non-RFC 8555 compliant and is obsoleted by OrdersURL. Authorizations string // Certificates is a URI from which a list of certificates // issued for this account can be fetched via a GET request. // // It is non-RFC 8555 compliant and is obsoleted by OrdersURL. Certificates string // ExternalAccountBinding represents an arbitrary binding to an account of // the CA which the ACME server is tied to. // See https://tools.ietf.org/html/rfc8555#section-7.3.4 for more details. ExternalAccountBinding *ExternalAccountBinding } // ExternalAccountBinding contains the data needed to form a request with // an external account binding. // See https://tools.ietf.org/html/rfc8555#section-7.3.4 for more details. type ExternalAccountBinding struct { // KID is the Key ID of the symmetric MAC key that the CA provides to // identify an external account from ACME. KID string // Key is the bytes of the symmetric key that the CA provides to identify // the account. Key must correspond to the KID. Key []byte } func (e *ExternalAccountBinding) String() string { return fmt.Sprintf("&{KID: %q, Key: redacted}", e.KID) } // Directory is ACME server discovery data. // See https://tools.ietf.org/html/rfc8555#section-7.1.1 for more details. type Directory struct { // NonceURL indicates an endpoint where to fetch fresh nonce values from. NonceURL string // RegURL is an account endpoint URL, allowing for creating new accounts. // Pre-RFC 8555 CAs also allow modifying existing accounts at this URL. RegURL string // OrderURL is used to initiate the certificate issuance flow // as described in RFC 8555. OrderURL string // AuthzURL is used to initiate identifier pre-authorization flow. // Empty string indicates the flow is unsupported by the CA. AuthzURL string // CertURL is a new certificate issuance endpoint URL. // It is non-RFC 8555 compliant and is obsoleted by OrderURL. CertURL string // RevokeURL is used to initiate a certificate revocation flow. RevokeURL string // KeyChangeURL allows to perform account key rollover flow. KeyChangeURL string // Terms is a URI identifying the current terms of service. Terms string // Website is an HTTP or HTTPS URL locating a website // providing more information about the ACME server. Website string // CAA consists of lowercase hostname elements, which the ACME server // recognises as referring to itself for the purposes of CAA record validation // as defined in RFC 6844. CAA []string // ExternalAccountRequired indicates that the CA requires for all account-related // requests to include external account binding information. ExternalAccountRequired bool } // Order represents a client's request for a certificate. // It tracks the request flow progress through to issuance. type Order struct { // URI uniquely identifies an order. URI string // Status represents the current status of the order. // It indicates which action the client should take. // // Possible values are StatusPending, StatusReady, StatusProcessing, StatusValid and StatusInvalid. // Pending means the CA does not believe that the client has fulfilled the requirements. // Ready indicates that the client has fulfilled all the requirements and can submit a CSR // to obtain a certificate. This is done with Client's CreateOrderCert. // Processing means the certificate is being issued. // Valid indicates the CA has issued the certificate. It can be downloaded // from the Order's CertURL. This is done with Client's FetchCert. // Invalid means the certificate will not be issued. Users should consider this order // abandoned. Status string // Expires is the timestamp after which CA considers this order invalid. Expires time.Time // Identifiers contains all identifier objects which the order pertains to. Identifiers []AuthzID // NotBefore is the requested value of the notBefore field in the certificate. NotBefore time.Time // NotAfter is the requested value of the notAfter field in the certificate. NotAfter time.Time // AuthzURLs represents authorizations to complete before a certificate // for identifiers specified in the order can be issued. // It also contains unexpired authorizations that the client has completed // in the past. // // Authorization objects can be fetched using Client's GetAuthorization method. // // The required authorizations are dictated by CA policies. // There may not be a 1:1 relationship between the identifiers and required authorizations. // Required authorizations can be identified by their StatusPending status. // // For orders in the StatusValid or StatusInvalid state these are the authorizations // which were completed. AuthzURLs []string // FinalizeURL is the endpoint at which a CSR is submitted to obtain a certificate // once all the authorizations are satisfied. FinalizeURL string // CertURL points to the certificate that has been issued in response to this order. CertURL string // The error that occurred while processing the order as received from a CA, if any. Error *Error } // OrderOption allows customizing Client.AuthorizeOrder call. type OrderOption interface { privateOrderOpt() } // WithOrderNotBefore sets order's NotBefore field. func WithOrderNotBefore(t time.Time) OrderOption { return orderNotBeforeOpt(t) } // WithOrderNotAfter sets order's NotAfter field. func WithOrderNotAfter(t time.Time) OrderOption { return orderNotAfterOpt(t) } type orderNotBeforeOpt time.Time func (orderNotBeforeOpt) privateOrderOpt() {} type orderNotAfterOpt time.Time func (orderNotAfterOpt) privateOrderOpt() {} // Authorization encodes an authorization response. type Authorization struct { // URI uniquely identifies a authorization. URI string // Status is the current status of an authorization. // Possible values are StatusPending, StatusValid, StatusInvalid, StatusDeactivated, // StatusExpired and StatusRevoked. Status string // Identifier is what the account is authorized to represent. Identifier AuthzID // The timestamp after which the CA considers the authorization invalid. Expires time.Time // Wildcard is true for authorizations of a wildcard domain name. Wildcard bool // Challenges that the client needs to fulfill in order to prove possession // of the identifier (for pending authorizations). // For valid authorizations, the challenge that was validated. // For invalid authorizations, the challenge that was attempted and failed. // // RFC 8555 compatible CAs require users to fuflfill only one of the challenges. Challenges []*Challenge // A collection of sets of challenges, each of which would be sufficient // to prove possession of the identifier. // Clients must complete a set of challenges that covers at least one set. // Challenges are identified by their indices in the challenges array. // If this field is empty, the client needs to complete all challenges. // // This field is unused in RFC 8555. Combinations [][]int } // AuthzID is an identifier that an account is authorized to represent. type AuthzID struct { Type string // The type of identifier, "dns" or "ip". Value string // The identifier itself, e.g. "example.org". } // DomainIDs creates a slice of AuthzID with "dns" identifier type. func DomainIDs(names ...string) []AuthzID { a := make([]AuthzID, len(names)) for i, v := range names { a[i] = AuthzID{Type: "dns", Value: v} } return a } // IPIDs creates a slice of AuthzID with "ip" identifier type. // Each element of addr is textual form of an address as defined // in RFC 1123 Section 2.1 for IPv4 and in RFC 5952 Section 4 for IPv6. func IPIDs(addr ...string) []AuthzID { a := make([]AuthzID, len(addr)) for i, v := range addr { a[i] = AuthzID{Type: "ip", Value: v} } return a } // wireAuthzID is ACME JSON representation of authorization identifier objects. type wireAuthzID struct { Type string `json:"type"` Value string `json:"value"` } // wireAuthz is ACME JSON representation of Authorization objects. type wireAuthz struct { Identifier wireAuthzID Status string Expires time.Time Wildcard bool Challenges []wireChallenge Combinations [][]int Error *wireError } func (z *wireAuthz) authorization(uri string) *Authorization { a := &Authorization{ URI: uri, Status: z.Status, Identifier: AuthzID{Type: z.Identifier.Type, Value: z.Identifier.Value}, Expires: z.Expires, Wildcard: z.Wildcard, Challenges: make([]*Challenge, len(z.Challenges)), Combinations: z.Combinations, // shallow copy } for i, v := range z.Challenges { a.Challenges[i] = v.challenge() } return a } func (z *wireAuthz) error(uri string) *AuthorizationError { err := &AuthorizationError{ URI: uri, Identifier: z.Identifier.Value, } if z.Error != nil { err.Errors = append(err.Errors, z.Error.error(nil)) } for _, raw := range z.Challenges { if raw.Error != nil { err.Errors = append(err.Errors, raw.Error.error(nil)) } } return err } // Challenge encodes a returned CA challenge. // Its Error field may be non-nil if the challenge is part of an Authorization // with StatusInvalid. type Challenge struct { // Type is the challenge type, e.g. "http-01", "tls-alpn-01", "dns-01". Type string // URI is where a challenge response can be posted to. URI string // Token is a random value that uniquely identifies the challenge. Token string // Status identifies the status of this challenge. // In RFC 8555, possible values are StatusPending, StatusProcessing, StatusValid, // and StatusInvalid. Status string // Validated is the time at which the CA validated this challenge. // Always zero value in pre-RFC 8555. Validated time.Time // Error indicates the reason for an authorization failure // when this challenge was used. // The type of a non-nil value is *Error. Error error // Payload is the JSON-formatted payload that the client sends // to the server to indicate it is ready to respond to the challenge. // When unset, it defaults to an empty JSON object: {}. // For most challenges, the client must not set Payload, // see https://tools.ietf.org/html/rfc8555#section-7.5.1. // Payload is used only for newer challenges (such as "device-attest-01") // where the client must send additional data for the server to validate // the challenge. Payload json.RawMessage } // wireChallenge is ACME JSON challenge representation. type wireChallenge struct { URL string `json:"url"` // RFC URI string `json:"uri"` // pre-RFC Type string Token string Status string Validated time.Time Error *wireError } func (c *wireChallenge) challenge() *Challenge { v := &Challenge{ URI: c.URL, Type: c.Type, Token: c.Token, Status: c.Status, } if v.URI == "" { v.URI = c.URI // c.URL was empty; use legacy } if v.Status == "" { v.Status = StatusPending } if c.Error != nil { v.Error = c.Error.error(nil) } return v } // wireError is a subset of fields of the Problem Details object // as described in https://tools.ietf.org/html/rfc7807#section-3.1. type wireError struct { Status int Type string Detail string Instance string Subproblems []Subproblem } func (e *wireError) error(h http.Header) *Error { err := &Error{ StatusCode: e.Status, ProblemType: e.Type, Detail: e.Detail, Instance: e.Instance, Header: h, Subproblems: e.Subproblems, } return err } // CertOption is an optional argument type for the TLS ChallengeCert methods for // customizing a temporary certificate for TLS-based challenges. type CertOption interface { privateCertOpt() } // WithKey creates an option holding a private/public key pair. // The private part signs a certificate, and the public part represents the signee. func WithKey(key crypto.Signer) CertOption { return &certOptKey{key} } type certOptKey struct { key crypto.Signer } func (*certOptKey) privateCertOpt() {} // WithTemplate creates an option for specifying a certificate template. // See x509.CreateCertificate for template usage details. // // In TLS ChallengeCert methods, the template is also used as parent, // resulting in a self-signed certificate. // The DNSNames or IPAddresses fields of t are always overwritten for tls-alpn challenge certs. func WithTemplate(t *x509.Certificate) CertOption { return (*certOptTemplate)(t) } type certOptTemplate x509.Certificate func (*certOptTemplate) privateCertOpt() {} ================================================ FILE: vendor/golang.org/x/mod/LICENSE ================================================ Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/golang.org/x/mod/PATENTS ================================================ Additional IP Rights Grant (Patents) "This implementation" means the copyrightable works distributed by Google as part of the Go project. Google hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, transfer and otherwise run, modify and propagate the contents of this implementation of Go, where such license applies only to those patent claims, both currently owned or controlled by Google and acquired in the future, licensable by Google that are necessarily infringed by this implementation of Go. This grant does not include claims that would be infringed only as a consequence of further modification of this implementation. If you or your agent or exclusive licensee institute or order or agree to the institution of patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that this implementation of Go or any code incorporated within this implementation of Go constitutes direct or contributory patent infringement, or inducement of patent infringement, then any patent rights granted to you under this License for this implementation of Go shall terminate as of the date such litigation is filed. ================================================ FILE: vendor/golang.org/x/mod/semver/semver.go ================================================ // Copyright 2018 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package semver implements comparison of semantic version strings. // In this package, semantic version strings must begin with a leading "v", // as in "v1.0.0". // // The general form of a semantic version string accepted by this package is // // vMAJOR[.MINOR[.PATCH[-PRERELEASE][+BUILD]]] // // where square brackets indicate optional parts of the syntax; // MAJOR, MINOR, and PATCH are decimal integers without extra leading zeros; // PRERELEASE and BUILD are each a series of non-empty dot-separated identifiers // using only alphanumeric characters and hyphens; and // all-numeric PRERELEASE identifiers must not have leading zeros. // // This package follows Semantic Versioning 2.0.0 (see semver.org) // with two exceptions. First, it requires the "v" prefix. Second, it recognizes // vMAJOR and vMAJOR.MINOR (with no prerelease or build suffixes) // as shorthands for vMAJOR.0.0 and vMAJOR.MINOR.0. package semver import ( "slices" "strings" ) // parsed returns the parsed form of a semantic version string. type parsed struct { major string minor string patch string short string prerelease string build string } // IsValid reports whether v is a valid semantic version string. func IsValid(v string) bool { _, ok := parse(v) return ok } // Canonical returns the canonical formatting of the semantic version v. // It fills in any missing .MINOR or .PATCH and discards build metadata. // Two semantic versions compare equal only if their canonical formattings // are identical strings. // The canonical invalid semantic version is the empty string. func Canonical(v string) string { p, ok := parse(v) if !ok { return "" } if p.build != "" { return v[:len(v)-len(p.build)] } if p.short != "" { return v + p.short } return v } // Major returns the major version prefix of the semantic version v. // For example, Major("v2.1.0") == "v2". // If v is an invalid semantic version string, Major returns the empty string. func Major(v string) string { pv, ok := parse(v) if !ok { return "" } return v[:1+len(pv.major)] } // MajorMinor returns the major.minor version prefix of the semantic version v. // For example, MajorMinor("v2.1.0") == "v2.1". // If v is an invalid semantic version string, MajorMinor returns the empty string. func MajorMinor(v string) string { pv, ok := parse(v) if !ok { return "" } i := 1 + len(pv.major) if j := i + 1 + len(pv.minor); j <= len(v) && v[i] == '.' && v[i+1:j] == pv.minor { return v[:j] } return v[:i] + "." + pv.minor } // Prerelease returns the prerelease suffix of the semantic version v. // For example, Prerelease("v2.1.0-pre+meta") == "-pre". // If v is an invalid semantic version string, Prerelease returns the empty string. func Prerelease(v string) string { pv, ok := parse(v) if !ok { return "" } return pv.prerelease } // Build returns the build suffix of the semantic version v. // For example, Build("v2.1.0+meta") == "+meta". // If v is an invalid semantic version string, Build returns the empty string. func Build(v string) string { pv, ok := parse(v) if !ok { return "" } return pv.build } // Compare returns an integer comparing two versions according to // semantic version precedence. // The result will be 0 if v == w, -1 if v < w, or +1 if v > w. // // An invalid semantic version string is considered less than a valid one. // All invalid semantic version strings compare equal to each other. func Compare(v, w string) int { pv, ok1 := parse(v) pw, ok2 := parse(w) if !ok1 && !ok2 { return 0 } if !ok1 { return -1 } if !ok2 { return +1 } if c := compareInt(pv.major, pw.major); c != 0 { return c } if c := compareInt(pv.minor, pw.minor); c != 0 { return c } if c := compareInt(pv.patch, pw.patch); c != 0 { return c } return comparePrerelease(pv.prerelease, pw.prerelease) } // Max canonicalizes its arguments and then returns the version string // that compares greater. // // Deprecated: use [Compare] instead. In most cases, returning a canonicalized // version is not expected or desired. func Max(v, w string) string { v = Canonical(v) w = Canonical(w) if Compare(v, w) > 0 { return v } return w } // ByVersion implements [sort.Interface] for sorting semantic version strings. type ByVersion []string func (vs ByVersion) Len() int { return len(vs) } func (vs ByVersion) Swap(i, j int) { vs[i], vs[j] = vs[j], vs[i] } func (vs ByVersion) Less(i, j int) bool { return compareVersion(vs[i], vs[j]) < 0 } // Sort sorts a list of semantic version strings using [Compare] and falls back // to use [strings.Compare] if both versions are considered equal. func Sort(list []string) { slices.SortFunc(list, compareVersion) } func compareVersion(a, b string) int { cmp := Compare(a, b) if cmp != 0 { return cmp } return strings.Compare(a, b) } func parse(v string) (p parsed, ok bool) { if v == "" || v[0] != 'v' { return } p.major, v, ok = parseInt(v[1:]) if !ok { return } if v == "" { p.minor = "0" p.patch = "0" p.short = ".0.0" return } if v[0] != '.' { ok = false return } p.minor, v, ok = parseInt(v[1:]) if !ok { return } if v == "" { p.patch = "0" p.short = ".0" return } if v[0] != '.' { ok = false return } p.patch, v, ok = parseInt(v[1:]) if !ok { return } if len(v) > 0 && v[0] == '-' { p.prerelease, v, ok = parsePrerelease(v) if !ok { return } } if len(v) > 0 && v[0] == '+' { p.build, v, ok = parseBuild(v) if !ok { return } } if v != "" { ok = false return } ok = true return } func parseInt(v string) (t, rest string, ok bool) { if v == "" { return } if v[0] < '0' || '9' < v[0] { return } i := 1 for i < len(v) && '0' <= v[i] && v[i] <= '9' { i++ } if v[0] == '0' && i != 1 { return } return v[:i], v[i:], true } func parsePrerelease(v string) (t, rest string, ok bool) { // "A pre-release version MAY be denoted by appending a hyphen and // a series of dot separated identifiers immediately following the patch version. // Identifiers MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-]. // Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes." if v == "" || v[0] != '-' { return } i := 1 start := 1 for i < len(v) && v[i] != '+' { if !isIdentChar(v[i]) && v[i] != '.' { return } if v[i] == '.' { if start == i || isBadNum(v[start:i]) { return } start = i + 1 } i++ } if start == i || isBadNum(v[start:i]) { return } return v[:i], v[i:], true } func parseBuild(v string) (t, rest string, ok bool) { if v == "" || v[0] != '+' { return } i := 1 start := 1 for i < len(v) { if !isIdentChar(v[i]) && v[i] != '.' { return } if v[i] == '.' { if start == i { return } start = i + 1 } i++ } if start == i { return } return v[:i], v[i:], true } func isIdentChar(c byte) bool { return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '-' } func isBadNum(v string) bool { i := 0 for i < len(v) && '0' <= v[i] && v[i] <= '9' { i++ } return i == len(v) && i > 1 && v[0] == '0' } func isNum(v string) bool { i := 0 for i < len(v) && '0' <= v[i] && v[i] <= '9' { i++ } return i == len(v) } func compareInt(x, y string) int { if x == y { return 0 } if len(x) < len(y) { return -1 } if len(x) > len(y) { return +1 } if x < y { return -1 } else { return +1 } } func comparePrerelease(x, y string) int { // "When major, minor, and patch are equal, a pre-release version has // lower precedence than a normal version. // Example: 1.0.0-alpha < 1.0.0. // Precedence for two pre-release versions with the same major, minor, // and patch version MUST be determined by comparing each dot separated // identifier from left to right until a difference is found as follows: // identifiers consisting of only digits are compared numerically and // identifiers with letters or hyphens are compared lexically in ASCII // sort order. Numeric identifiers always have lower precedence than // non-numeric identifiers. A larger set of pre-release fields has a // higher precedence than a smaller set, if all of the preceding // identifiers are equal. // Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < // 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0." if x == y { return 0 } if x == "" { return +1 } if y == "" { return -1 } for x != "" && y != "" { x = x[1:] // skip - or . y = y[1:] // skip - or . var dx, dy string dx, x = nextIdent(x) dy, y = nextIdent(y) if dx != dy { ix := isNum(dx) iy := isNum(dy) if ix != iy { if ix { return -1 } else { return +1 } } if ix { if len(dx) < len(dy) { return -1 } if len(dx) > len(dy) { return +1 } } if dx < dy { return -1 } else { return +1 } } } if x == "" { return -1 } else { return +1 } } func nextIdent(x string) (dx, rest string) { i := 0 for i < len(x) && x[i] != '.' { i++ } return x[:i], x[i:] } ================================================ FILE: vendor/golang.org/x/net/LICENSE ================================================ Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: vendor/golang.org/x/net/PATENTS ================================================ Additional IP Rights Grant (Patents) "This implementation" means the copyrightable works distributed by Google as part of the Go project. Google hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, transfer and otherwise run, modify and propagate the contents of this implementation of Go, where such license applies only to those patent claims, both currently owned or controlled by Google and acquired in the future, licensable by Google that are necessarily infringed by this implementation of Go. This grant does not include claims that would be infringed only as a consequence of further modification of this implementation. If you or your agent or exclusive licensee institute or order or agree to the institution of patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that this implementation of Go or any code incorporated within this implementation of Go constitutes direct or contributory patent infringement, or inducement of patent infringement, then any patent rights granted to you under this License for this implementation of Go shall terminate as of the date such litigation is filed. ================================================ FILE: vendor/golang.org/x/net/context/context.go ================================================ // Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package context has been superseded by the standard library [context] package. // // Deprecated: Use the standard library context package instead. package context import ( "context" // standard library's context, as of Go 1.7 "time" ) // A Context carries a deadline, a cancellation signal, and other values across // API boundaries. // // Context's methods may be called by multiple goroutines simultaneously. // //go:fix inline type Context = context.Context // Canceled is the error returned by [Context.Err] when the context is canceled // for some reason other than its deadline passing. // //go:fix inline var Canceled = context.Canceled // DeadlineExceeded is the error returned by [Context.Err] when the context is canceled // due to its deadline passing. // //go:fix inline var DeadlineExceeded = context.DeadlineExceeded // Background returns a non-nil, empty Context. It is never canceled, has no // values, and has no deadline. It is typically used by the main function, // initialization, and tests, and as the top-level Context for incoming // requests. // //go:fix inline func Background() Context { return context.Background() } // TODO returns a non-nil, empty Context. Code should use context.TODO when // it's unclear which Context to use or it is not yet available (because the // surrounding function has not yet been extended to accept a Context // parameter). // //go:fix inline func TODO() Context { return context.TODO() } // A CancelFunc tells an operation to abandon its work. // A CancelFunc does not wait for the work to stop. // A CancelFunc may be called by multiple goroutines simultaneously. // After the first call, subsequent calls to a CancelFunc do nothing. type CancelFunc = context.CancelFunc // WithCancel returns a derived context that points to the parent context // but has a new Done channel. The returned context's Done channel is closed // when the returned cancel function is called or when the parent context's // Done channel is closed, whichever happens first. // // Canceling this context releases resources associated with it, so code should // call cancel as soon as the operations running in this [Context] complete. // //go:fix inline func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { return context.WithCancel(parent) } // WithDeadline returns a derived context that points to the parent context // but has the deadline adjusted to be no later than d. If the parent's // deadline is already earlier than d, WithDeadline(parent, d) is semantically // equivalent to parent. The returned [Context.Done] channel is closed when // the deadline expires, when the returned cancel function is called, // or when the parent context's Done channel is closed, whichever happens first. // // Canceling this context releases resources associated with it, so code should // call cancel as soon as the operations running in this [Context] complete. // //go:fix inline func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) { return context.WithDeadline(parent, d) } // WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). // // Canceling this context releases resources associated with it, so code should // call cancel as soon as the operations running in this [Context] complete: // // func slowOperationWithTimeout(ctx context.Context) (Result, error) { // ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) // defer cancel() // releases resources if slowOperation completes before timeout elapses // return slowOperation(ctx) // } // //go:fix inline func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { return context.WithTimeout(parent, timeout) } // WithValue returns a derived context that points to the parent Context. // In the derived context, the value associated with key is val. // // Use context Values only for request-scoped data that transits processes and // APIs, not for passing optional parameters to functions. // // The provided key must be comparable and should not be of type // string or any other built-in type to avoid collisions between // packages using context. Users of WithValue should define their own // types for keys. To avoid allocating when assigning to an // interface{}, context keys often have concrete type // struct{}. Alternatively, exported context key variables' static // type should be a pointer or interface. // //go:fix inline func WithValue(parent Context, key, val interface{}) Context { return context.WithValue(parent, key, val) } ================================================ FILE: vendor/golang.org/x/net/html/atom/atom.go ================================================ // Copyright 2012 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package atom provides integer codes (also known as atoms) for a fixed set of // frequently occurring HTML strings: tag names and attribute keys such as "p" // and "id". // // Sharing an atom's name between all elements with the same tag can result in // fewer string allocations when tokenizing and parsing HTML. Integer // comparisons are also generally faster than string comparisons. // // The value of an atom's particular code is not guaranteed to stay the same // between versions of this package. Neither is any ordering guaranteed: // whether atom.H1 < atom.H2 may also change. The codes are not guaranteed to // be dense. The only guarantees are that e.g. looking up "div" will yield // atom.Div, calling atom.Div.String will return "div", and atom.Div != 0. package atom // import "golang.org/x/net/html/atom" // Atom is an integer code for a string. The zero value maps to "". type Atom uint32 // String returns the atom's name. func (a Atom) String() string { start := uint32(a >> 8) n := uint32(a & 0xff) if start+n > uint32(len(atomText)) { return "" } return atomText[start : start+n] } func (a Atom) string() string { return atomText[a>>8 : a>>8+a&0xff] } // fnv computes the FNV hash with an arbitrary starting value h. func fnv(h uint32, s []byte) uint32 { for i := range s { h ^= uint32(s[i]) h *= 16777619 } return h } func match(s string, t []byte) bool { for i, c := range t { if s[i] != c { return false } } return true } // Lookup returns the atom whose name is s. It returns zero if there is no // such atom. The lookup is case sensitive. func Lookup(s []byte) Atom { if len(s) == 0 || len(s) > maxAtomLen { return 0 } h := fnv(hash0, s) if a := table[h&uint32(len(table)-1)]; int(a&0xff) == len(s) && match(a.string(), s) { return a } if a := table[(h>>16)&uint32(len(table)-1)]; int(a&0xff) == len(s) && match(a.string(), s) { return a } return 0 } // String returns a string whose contents are equal to s. In that sense, it is // equivalent to string(s) but may be more efficient. func String(s []byte) string { if a := Lookup(s); a != 0 { return a.String() } return string(s) } ================================================ FILE: vendor/golang.org/x/net/html/atom/table.go ================================================ // Code generated by go generate gen.go; DO NOT EDIT. //go:generate go run gen.go package atom const ( A Atom = 0x1 Abbr Atom = 0x4 Accept Atom = 0x1a06 AcceptCharset Atom = 0x1a0e Accesskey Atom = 0x2c09 Acronym Atom = 0xaa07 Action Atom = 0x26506 Address Atom = 0x6f107 Align Atom = 0xb105 Allowfullscreen Atom = 0x3280f Allowpaymentrequest Atom = 0xc113 Allowusermedia Atom = 0xdd0e Alt Atom = 0xf303 Annotation Atom = 0x1c90a AnnotationXml Atom = 0x1c90e Applet Atom = 0x30806 Area Atom = 0x35004 Article Atom = 0x3f607 As Atom = 0x3c02 Aside Atom = 0x10705 Async Atom = 0xff05 Audio Atom = 0x11505 Autocomplete Atom = 0x26b0c Autofocus Atom = 0x12109 Autoplay Atom = 0x13c08 B Atom = 0x101 Base Atom = 0x3b04 Basefont Atom = 0x3b08 Bdi Atom = 0xba03 Bdo Atom = 0x14b03 Bgsound Atom = 0x15e07 Big Atom = 0x17003 Blink Atom = 0x17305 Blockquote Atom = 0x1870a Body Atom = 0x2804 Br Atom = 0x202 Button Atom = 0x19106 Canvas Atom = 0x10306 Caption Atom = 0x22407 Center Atom = 0x21306 Challenge Atom = 0x28e09 Charset Atom = 0x2107 Checked Atom = 0x5b507 Cite Atom = 0x19c04 Class Atom = 0x55805 Code Atom = 0x5ee04 Col Atom = 0x1ab03 Colgroup Atom = 0x1ab08 Color Atom = 0x1bf05 Cols Atom = 0x1c404 Colspan Atom = 0x1c407 Command Atom = 0x1d707 Content Atom = 0x57b07 Contenteditable Atom = 0x57b0f Contextmenu Atom = 0x37a0b Controls Atom = 0x1de08 Coords Atom = 0x1f006 Crossorigin Atom = 0x1fa0b Data Atom = 0x49904 Datalist Atom = 0x49908 Datetime Atom = 0x2ab08 Dd Atom = 0x2bf02 Default Atom = 0x10a07 Defer Atom = 0x5f005 Del Atom = 0x44c03 Desc Atom = 0x55504 Details Atom = 0x7207 Dfn Atom = 0x8703 Dialog Atom = 0xbb06 Dir Atom = 0x9303 Dirname Atom = 0x9307 Disabled Atom = 0x16408 Div Atom = 0x16b03 Dl Atom = 0x5d602 Download Atom = 0x45d08 Draggable Atom = 0x17a09 Dropzone Atom = 0x3ff08 Dt Atom = 0x64002 Em Atom = 0x6e02 Embed Atom = 0x6e05 Enctype Atom = 0x28007 Face Atom = 0x21104 Fieldset Atom = 0x21908 Figcaption Atom = 0x2210a Figure Atom = 0x23b06 Font Atom = 0x3f04 Footer Atom = 0xf606 For Atom = 0x24703 ForeignObject Atom = 0x2470d Foreignobject Atom = 0x2540d Form Atom = 0x26104 Formaction Atom = 0x2610a Formenctype Atom = 0x27c0b Formmethod Atom = 0x2970a Formnovalidate Atom = 0x2a10e Formtarget Atom = 0x2b30a Frame Atom = 0x8b05 Frameset Atom = 0x8b08 H1 Atom = 0x15c02 H2 Atom = 0x56102 H3 Atom = 0x2cd02 H4 Atom = 0x2fc02 H5 Atom = 0x33f02 H6 Atom = 0x34902 Head Atom = 0x32004 Header Atom = 0x32006 Headers Atom = 0x32007 Height Atom = 0x5206 Hgroup Atom = 0x64206 Hidden Atom = 0x2bd06 High Atom = 0x2ca04 Hr Atom = 0x15702 Href Atom = 0x2cf04 Hreflang Atom = 0x2cf08 Html Atom = 0x5604 HttpEquiv Atom = 0x2d70a I Atom = 0x601 Icon Atom = 0x57a04 Id Atom = 0x10902 Iframe Atom = 0x2eb06 Image Atom = 0x2f105 Img Atom = 0x2f603 Input Atom = 0x44505 Inputmode Atom = 0x44509 Ins Atom = 0x20303 Integrity Atom = 0x23209 Is Atom = 0x16502 Isindex Atom = 0x2fe07 Ismap Atom = 0x30505 Itemid Atom = 0x38506 Itemprop Atom = 0x19d08 Itemref Atom = 0x3c707 Itemscope Atom = 0x66f09 Itemtype Atom = 0x30e08 Kbd Atom = 0xb903 Keygen Atom = 0x3206 Keytype Atom = 0xd607 Kind Atom = 0x17704 Label Atom = 0x5905 Lang Atom = 0x2d304 Legend Atom = 0x18106 Li Atom = 0xb202 Link Atom = 0x17404 List Atom = 0x49d04 Listing Atom = 0x49d07 Loop Atom = 0x5d04 Low Atom = 0xc303 Main Atom = 0x1004 Malignmark Atom = 0xb00a Manifest Atom = 0x6d508 Map Atom = 0x30703 Mark Atom = 0xb604 Marquee Atom = 0x31607 Math Atom = 0x31d04 Max Atom = 0x33703 Maxlength Atom = 0x33709 Media Atom = 0xe605 Mediagroup Atom = 0xe60a Menu Atom = 0x38104 Menuitem Atom = 0x38108 Meta Atom = 0x4ac04 Meter Atom = 0x9805 Method Atom = 0x29b06 Mglyph Atom = 0x2f706 Mi Atom = 0x34102 Min Atom = 0x34103 Minlength Atom = 0x34109 Mn Atom = 0x2a402 Mo Atom = 0xa402 Ms Atom = 0x67202 Mtext Atom = 0x34b05 Multiple Atom = 0x35908 Muted Atom = 0x36105 Name Atom = 0x9604 Nav Atom = 0x1303 Nobr Atom = 0x3704 Noembed Atom = 0x6c07 Noframes Atom = 0x8908 Nomodule Atom = 0xa208 Nonce Atom = 0x1a605 Noscript Atom = 0x2c208 Novalidate Atom = 0x2a50a Object Atom = 0x25b06 Ol Atom = 0x13702 Onabort Atom = 0x19507 Onafterprint Atom = 0x2290c Onautocomplete Atom = 0x2690e Onautocompleteerror Atom = 0x26913 Onauxclick Atom = 0x6140a Onbeforeprint Atom = 0x69c0d Onbeforeunload Atom = 0x6e50e Onblur Atom = 0x1ea06 Oncancel Atom = 0x11908 Oncanplay Atom = 0x14d09 Oncanplaythrough Atom = 0x14d10 Onchange Atom = 0x41508 Onclick Atom = 0x2e407 Onclose Atom = 0x36607 Oncontextmenu Atom = 0x3780d Oncopy Atom = 0x38b06 Oncuechange Atom = 0x3910b Oncut Atom = 0x39c05 Ondblclick Atom = 0x3a10a Ondrag Atom = 0x3ab06 Ondragend Atom = 0x3ab09 Ondragenter Atom = 0x3b40b Ondragexit Atom = 0x3bf0a Ondragleave Atom = 0x3d90b Ondragover Atom = 0x3e40a Ondragstart Atom = 0x3ee0b Ondrop Atom = 0x3fd06 Ondurationchange Atom = 0x40d10 Onemptied Atom = 0x40409 Onended Atom = 0x41d07 Onerror Atom = 0x42407 Onfocus Atom = 0x42b07 Onhashchange Atom = 0x4370c Oninput Atom = 0x44307 Oninvalid Atom = 0x44f09 Onkeydown Atom = 0x45809 Onkeypress Atom = 0x4650a Onkeyup Atom = 0x47407 Onlanguagechange Atom = 0x48110 Onload Atom = 0x49106 Onloadeddata Atom = 0x4910c Onloadedmetadata Atom = 0x4a410 Onloadend Atom = 0x4ba09 Onloadstart Atom = 0x4c30b Onmessage Atom = 0x4ce09 Onmessageerror Atom = 0x4ce0e Onmousedown Atom = 0x4dc0b Onmouseenter Atom = 0x4e70c Onmouseleave Atom = 0x4f30c Onmousemove Atom = 0x4ff0b Onmouseout Atom = 0x50a0a Onmouseover Atom = 0x5170b Onmouseup Atom = 0x52209 Onmousewheel Atom = 0x5300c Onoffline Atom = 0x53c09 Ononline Atom = 0x54508 Onpagehide Atom = 0x54d0a Onpageshow Atom = 0x5630a Onpaste Atom = 0x56f07 Onpause Atom = 0x58a07 Onplay Atom = 0x59406 Onplaying Atom = 0x59409 Onpopstate Atom = 0x59d0a Onprogress Atom = 0x5a70a Onratechange Atom = 0x5bc0c Onrejectionhandled Atom = 0x5c812 Onreset Atom = 0x5da07 Onresize Atom = 0x5e108 Onscroll Atom = 0x5f508 Onsecuritypolicyviolation Atom = 0x5fd19 Onseeked Atom = 0x61e08 Onseeking Atom = 0x62609 Onselect Atom = 0x62f08 Onshow Atom = 0x63906 Onsort Atom = 0x64d06 Onstalled Atom = 0x65709 Onstorage Atom = 0x66009 Onsubmit Atom = 0x66908 Onsuspend Atom = 0x67909 Ontimeupdate Atom = 0x400c Ontoggle Atom = 0x68208 Onunhandledrejection Atom = 0x68a14 Onunload Atom = 0x6a908 Onvolumechange Atom = 0x6b10e Onwaiting Atom = 0x6bf09 Onwheel Atom = 0x6c807 Open Atom = 0x1a304 Optgroup Atom = 0x5f08 Optimum Atom = 0x6cf07 Option Atom = 0x6e106 Output Atom = 0x51106 P Atom = 0xc01 Param Atom = 0xc05 Pattern Atom = 0x6607 Picture Atom = 0x7b07 Ping Atom = 0xef04 Placeholder Atom = 0x1310b Plaintext Atom = 0x1b209 Playsinline Atom = 0x1400b Poster Atom = 0x64706 Pre Atom = 0x46a03 Preload Atom = 0x47a07 Progress Atom = 0x5a908 Prompt Atom = 0x52a06 Public Atom = 0x57606 Q Atom = 0xcf01 Radiogroup Atom = 0x30a Rb Atom = 0x3a02 Readonly Atom = 0x35108 Referrerpolicy Atom = 0x3cb0e Rel Atom = 0x47b03 Required Atom = 0x23f08 Reversed Atom = 0x8008 Rows Atom = 0x9c04 Rowspan Atom = 0x9c07 Rp Atom = 0x22f02 Rt Atom = 0x19a02 Rtc Atom = 0x19a03 Ruby Atom = 0xfb04 S Atom = 0x2501 Samp Atom = 0x7804 Sandbox Atom = 0x12907 Scope Atom = 0x67305 Scoped Atom = 0x67306 Script Atom = 0x2c406 Seamless Atom = 0x36b08 Search Atom = 0x55c06 Section Atom = 0x1e507 Select Atom = 0x63106 Selected Atom = 0x63108 Shape Atom = 0x1f505 Size Atom = 0x5e504 Sizes Atom = 0x5e505 Slot Atom = 0x20504 Small Atom = 0x32605 Sortable Atom = 0x64f08 Sorted Atom = 0x37206 Source Atom = 0x43106 Spacer Atom = 0x46e06 Span Atom = 0x9f04 Spellcheck Atom = 0x5b00a Src Atom = 0x5e903 Srcdoc Atom = 0x5e906 Srclang Atom = 0x6f707 Srcset Atom = 0x6fe06 Start Atom = 0x3f405 Step Atom = 0x57304 Strike Atom = 0xd206 Strong Atom = 0x6db06 Style Atom = 0x70405 Sub Atom = 0x66b03 Summary Atom = 0x70907 Sup Atom = 0x71003 Svg Atom = 0x71303 System Atom = 0x71606 Tabindex Atom = 0x4b208 Table Atom = 0x58505 Target Atom = 0x2b706 Tbody Atom = 0x2705 Td Atom = 0x9202 Template Atom = 0x71908 Textarea Atom = 0x34c08 Tfoot Atom = 0xf505 Th Atom = 0x15602 Thead Atom = 0x31f05 Time Atom = 0x4204 Title Atom = 0x11005 Tr Atom = 0xcc02 Track Atom = 0x1ba05 Translate Atom = 0x20809 Tt Atom = 0x6802 Type Atom = 0xd904 Typemustmatch Atom = 0x2830d U Atom = 0xb01 Ul Atom = 0xa702 Updateviacache Atom = 0x460e Usemap Atom = 0x58e06 Value Atom = 0x1505 Var Atom = 0x16d03 Video Atom = 0x2e005 Wbr Atom = 0x56c03 Width Atom = 0x63e05 Workertype Atom = 0x7210a Wrap Atom = 0x72b04 Xmp Atom = 0x12f03 ) const hash0 = 0x84f70e16 const maxAtomLen = 25 var table = [1 << 9]Atom{ 0x1: 0x3ff08, // dropzone 0x2: 0x3b08, // basefont 0x3: 0x23209, // integrity 0x4: 0x43106, // source 0x5: 0x2c09, // accesskey 0x6: 0x1a06, // accept 0x7: 0x6c807, // onwheel 0xb: 0x47407, // onkeyup 0xc: 0x32007, // headers 0xd: 0x67306, // scoped 0xe: 0x67909, // onsuspend 0xf: 0x8908, // noframes 0x10: 0x1fa0b, // crossorigin 0x11: 0x2e407, // onclick 0x12: 0x3f405, // start 0x13: 0x37a0b, // contextmenu 0x14: 0x5e903, // src 0x15: 0x1c404, // cols 0x16: 0xbb06, // dialog 0x17: 0x47a07, // preload 0x18: 0x3c707, // itemref 0x1b: 0x2f105, // image 0x1d: 0x4ba09, // onloadend 0x1e: 0x45d08, // download 0x1f: 0x46a03, // pre 0x23: 0x2970a, // formmethod 0x24: 0x71303, // svg 0x25: 0xcf01, // q 0x26: 0x64002, // dt 0x27: 0x1de08, // controls 0x2a: 0x2804, // body 0x2b: 0xd206, // strike 0x2c: 0x3910b, // oncuechange 0x2d: 0x4c30b, // onloadstart 0x2e: 0x2fe07, // isindex 0x2f: 0xb202, // li 0x30: 0x1400b, // playsinline 0x31: 0x34102, // mi 0x32: 0x30806, // applet 0x33: 0x4ce09, // onmessage 0x35: 0x13702, // ol 0x36: 0x1a304, // open 0x39: 0x14d09, // oncanplay 0x3a: 0x6bf09, // onwaiting 0x3b: 0x11908, // oncancel 0x3c: 0x6a908, // onunload 0x3e: 0x53c09, // onoffline 0x3f: 0x1a0e, // accept-charset 0x40: 0x32004, // head 0x42: 0x3ab09, // ondragend 0x43: 0x1310b, // placeholder 0x44: 0x2b30a, // formtarget 0x45: 0x2540d, // foreignobject 0x47: 0x400c, // ontimeupdate 0x48: 0xdd0e, // allowusermedia 0x4a: 0x69c0d, // onbeforeprint 0x4b: 0x5604, // html 0x4c: 0x9f04, // span 0x4d: 0x64206, // hgroup 0x4e: 0x16408, // disabled 0x4f: 0x4204, // time 0x51: 0x42b07, // onfocus 0x53: 0xb00a, // malignmark 0x55: 0x4650a, // onkeypress 0x56: 0x55805, // class 0x57: 0x1ab08, // colgroup 0x58: 0x33709, // maxlength 0x59: 0x5a908, // progress 0x5b: 0x70405, // style 0x5c: 0x2a10e, // formnovalidate 0x5e: 0x38b06, // oncopy 0x60: 0x26104, // form 0x61: 0xf606, // footer 0x64: 0x30a, // radiogroup 0x66: 0xfb04, // ruby 0x67: 0x4ff0b, // onmousemove 0x68: 0x19d08, // itemprop 0x69: 0x2d70a, // http-equiv 0x6a: 0x15602, // th 0x6c: 0x6e02, // em 0x6d: 0x38108, // menuitem 0x6e: 0x63106, // select 0x6f: 0x48110, // onlanguagechange 0x70: 0x31f05, // thead 0x71: 0x15c02, // h1 0x72: 0x5e906, // srcdoc 0x75: 0x9604, // name 0x76: 0x19106, // button 0x77: 0x55504, // desc 0x78: 0x17704, // kind 0x79: 0x1bf05, // color 0x7c: 0x58e06, // usemap 0x7d: 0x30e08, // itemtype 0x7f: 0x6d508, // manifest 0x81: 0x5300c, // onmousewheel 0x82: 0x4dc0b, // onmousedown 0x84: 0xc05, // param 0x85: 0x2e005, // video 0x86: 0x4910c, // onloadeddata 0x87: 0x6f107, // address 0x8c: 0xef04, // ping 0x8d: 0x24703, // for 0x8f: 0x62f08, // onselect 0x90: 0x30703, // map 0x92: 0xc01, // p 0x93: 0x8008, // reversed 0x94: 0x54d0a, // onpagehide 0x95: 0x3206, // keygen 0x96: 0x34109, // minlength 0x97: 0x3e40a, // ondragover 0x98: 0x42407, // onerror 0x9a: 0x2107, // charset 0x9b: 0x29b06, // method 0x9c: 0x101, // b 0x9d: 0x68208, // ontoggle 0x9e: 0x2bd06, // hidden 0xa0: 0x3f607, // article 0xa2: 0x63906, // onshow 0xa3: 0x64d06, // onsort 0xa5: 0x57b0f, // contenteditable 0xa6: 0x66908, // onsubmit 0xa8: 0x44f09, // oninvalid 0xaa: 0x202, // br 0xab: 0x10902, // id 0xac: 0x5d04, // loop 0xad: 0x5630a, // onpageshow 0xb0: 0x2cf04, // href 0xb2: 0x2210a, // figcaption 0xb3: 0x2690e, // onautocomplete 0xb4: 0x49106, // onload 0xb6: 0x9c04, // rows 0xb7: 0x1a605, // nonce 0xb8: 0x68a14, // onunhandledrejection 0xbb: 0x21306, // center 0xbc: 0x59406, // onplay 0xbd: 0x33f02, // h5 0xbe: 0x49d07, // listing 0xbf: 0x57606, // public 0xc2: 0x23b06, // figure 0xc3: 0x57a04, // icon 0xc4: 0x1ab03, // col 0xc5: 0x47b03, // rel 0xc6: 0xe605, // media 0xc7: 0x12109, // autofocus 0xc8: 0x19a02, // rt 0xca: 0x2d304, // lang 0xcc: 0x49908, // datalist 0xce: 0x2eb06, // iframe 0xcf: 0x36105, // muted 0xd0: 0x6140a, // onauxclick 0xd2: 0x3c02, // as 0xd6: 0x3fd06, // ondrop 0xd7: 0x1c90a, // annotation 0xd8: 0x21908, // fieldset 0xdb: 0x2cf08, // hreflang 0xdc: 0x4e70c, // onmouseenter 0xdd: 0x2a402, // mn 0xde: 0xe60a, // mediagroup 0xdf: 0x9805, // meter 0xe0: 0x56c03, // wbr 0xe2: 0x63e05, // width 0xe3: 0x2290c, // onafterprint 0xe4: 0x30505, // ismap 0xe5: 0x1505, // value 0xe7: 0x1303, // nav 0xe8: 0x54508, // ononline 0xe9: 0xb604, // mark 0xea: 0xc303, // low 0xeb: 0x3ee0b, // ondragstart 0xef: 0x12f03, // xmp 0xf0: 0x22407, // caption 0xf1: 0xd904, // type 0xf2: 0x70907, // summary 0xf3: 0x6802, // tt 0xf4: 0x20809, // translate 0xf5: 0x1870a, // blockquote 0xf8: 0x15702, // hr 0xfa: 0x2705, // tbody 0xfc: 0x7b07, // picture 0xfd: 0x5206, // height 0xfe: 0x19c04, // cite 0xff: 0x2501, // s 0x101: 0xff05, // async 0x102: 0x56f07, // onpaste 0x103: 0x19507, // onabort 0x104: 0x2b706, // target 0x105: 0x14b03, // bdo 0x106: 0x1f006, // coords 0x107: 0x5e108, // onresize 0x108: 0x71908, // template 0x10a: 0x3a02, // rb 0x10b: 0x2a50a, // novalidate 0x10c: 0x460e, // updateviacache 0x10d: 0x71003, // sup 0x10e: 0x6c07, // noembed 0x10f: 0x16b03, // div 0x110: 0x6f707, // srclang 0x111: 0x17a09, // draggable 0x112: 0x67305, // scope 0x113: 0x5905, // label 0x114: 0x22f02, // rp 0x115: 0x23f08, // required 0x116: 0x3780d, // oncontextmenu 0x117: 0x5e504, // size 0x118: 0x5b00a, // spellcheck 0x119: 0x3f04, // font 0x11a: 0x9c07, // rowspan 0x11b: 0x10a07, // default 0x11d: 0x44307, // oninput 0x11e: 0x38506, // itemid 0x11f: 0x5ee04, // code 0x120: 0xaa07, // acronym 0x121: 0x3b04, // base 0x125: 0x2470d, // foreignObject 0x126: 0x2ca04, // high 0x127: 0x3cb0e, // referrerpolicy 0x128: 0x33703, // max 0x129: 0x59d0a, // onpopstate 0x12a: 0x2fc02, // h4 0x12b: 0x4ac04, // meta 0x12c: 0x17305, // blink 0x12e: 0x5f508, // onscroll 0x12f: 0x59409, // onplaying 0x130: 0xc113, // allowpaymentrequest 0x131: 0x19a03, // rtc 0x132: 0x72b04, // wrap 0x134: 0x8b08, // frameset 0x135: 0x32605, // small 0x137: 0x32006, // header 0x138: 0x40409, // onemptied 0x139: 0x34902, // h6 0x13a: 0x35908, // multiple 0x13c: 0x52a06, // prompt 0x13f: 0x28e09, // challenge 0x141: 0x4370c, // onhashchange 0x142: 0x57b07, // content 0x143: 0x1c90e, // annotation-xml 0x144: 0x36607, // onclose 0x145: 0x14d10, // oncanplaythrough 0x148: 0x5170b, // onmouseover 0x149: 0x64f08, // sortable 0x14a: 0xa402, // mo 0x14b: 0x2cd02, // h3 0x14c: 0x2c406, // script 0x14d: 0x41d07, // onended 0x14f: 0x64706, // poster 0x150: 0x7210a, // workertype 0x153: 0x1f505, // shape 0x154: 0x4, // abbr 0x155: 0x1, // a 0x156: 0x2bf02, // dd 0x157: 0x71606, // system 0x158: 0x4ce0e, // onmessageerror 0x159: 0x36b08, // seamless 0x15a: 0x2610a, // formaction 0x15b: 0x6e106, // option 0x15c: 0x31d04, // math 0x15d: 0x62609, // onseeking 0x15e: 0x39c05, // oncut 0x15f: 0x44c03, // del 0x160: 0x11005, // title 0x161: 0x11505, // audio 0x162: 0x63108, // selected 0x165: 0x3b40b, // ondragenter 0x166: 0x46e06, // spacer 0x167: 0x4a410, // onloadedmetadata 0x168: 0x44505, // input 0x16a: 0x58505, // table 0x16b: 0x41508, // onchange 0x16e: 0x5f005, // defer 0x171: 0x50a0a, // onmouseout 0x172: 0x20504, // slot 0x175: 0x3704, // nobr 0x177: 0x1d707, // command 0x17a: 0x7207, // details 0x17b: 0x38104, // menu 0x17c: 0xb903, // kbd 0x17d: 0x57304, // step 0x17e: 0x20303, // ins 0x17f: 0x13c08, // autoplay 0x182: 0x34103, // min 0x183: 0x17404, // link 0x185: 0x40d10, // ondurationchange 0x186: 0x9202, // td 0x187: 0x8b05, // frame 0x18a: 0x2ab08, // datetime 0x18b: 0x44509, // inputmode 0x18c: 0x35108, // readonly 0x18d: 0x21104, // face 0x18f: 0x5e505, // sizes 0x191: 0x4b208, // tabindex 0x192: 0x6db06, // strong 0x193: 0xba03, // bdi 0x194: 0x6fe06, // srcset 0x196: 0x67202, // ms 0x197: 0x5b507, // checked 0x198: 0xb105, // align 0x199: 0x1e507, // section 0x19b: 0x6e05, // embed 0x19d: 0x15e07, // bgsound 0x1a2: 0x49d04, // list 0x1a3: 0x61e08, // onseeked 0x1a4: 0x66009, // onstorage 0x1a5: 0x2f603, // img 0x1a6: 0xf505, // tfoot 0x1a9: 0x26913, // onautocompleteerror 0x1aa: 0x5fd19, // onsecuritypolicyviolation 0x1ad: 0x9303, // dir 0x1ae: 0x9307, // dirname 0x1b0: 0x5a70a, // onprogress 0x1b2: 0x65709, // onstalled 0x1b5: 0x66f09, // itemscope 0x1b6: 0x49904, // data 0x1b7: 0x3d90b, // ondragleave 0x1b8: 0x56102, // h2 0x1b9: 0x2f706, // mglyph 0x1ba: 0x16502, // is 0x1bb: 0x6e50e, // onbeforeunload 0x1bc: 0x2830d, // typemustmatch 0x1bd: 0x3ab06, // ondrag 0x1be: 0x5da07, // onreset 0x1c0: 0x51106, // output 0x1c1: 0x12907, // sandbox 0x1c2: 0x1b209, // plaintext 0x1c4: 0x34c08, // textarea 0x1c7: 0xd607, // keytype 0x1c8: 0x34b05, // mtext 0x1c9: 0x6b10e, // onvolumechange 0x1ca: 0x1ea06, // onblur 0x1cb: 0x58a07, // onpause 0x1cd: 0x5bc0c, // onratechange 0x1ce: 0x10705, // aside 0x1cf: 0x6cf07, // optimum 0x1d1: 0x45809, // onkeydown 0x1d2: 0x1c407, // colspan 0x1d3: 0x1004, // main 0x1d4: 0x66b03, // sub 0x1d5: 0x25b06, // object 0x1d6: 0x55c06, // search 0x1d7: 0x37206, // sorted 0x1d8: 0x17003, // big 0x1d9: 0xb01, // u 0x1db: 0x26b0c, // autocomplete 0x1dc: 0xcc02, // tr 0x1dd: 0xf303, // alt 0x1df: 0x7804, // samp 0x1e0: 0x5c812, // onrejectionhandled 0x1e1: 0x4f30c, // onmouseleave 0x1e2: 0x28007, // enctype 0x1e3: 0xa208, // nomodule 0x1e5: 0x3280f, // allowfullscreen 0x1e6: 0x5f08, // optgroup 0x1e8: 0x27c0b, // formenctype 0x1e9: 0x18106, // legend 0x1ea: 0x10306, // canvas 0x1eb: 0x6607, // pattern 0x1ec: 0x2c208, // noscript 0x1ed: 0x601, // i 0x1ee: 0x5d602, // dl 0x1ef: 0xa702, // ul 0x1f2: 0x52209, // onmouseup 0x1f4: 0x1ba05, // track 0x1f7: 0x3a10a, // ondblclick 0x1f8: 0x3bf0a, // ondragexit 0x1fa: 0x8703, // dfn 0x1fc: 0x26506, // action 0x1fd: 0x35004, // area 0x1fe: 0x31607, // marquee 0x1ff: 0x16d03, // var } const atomText = "abbradiogrouparamainavalueaccept-charsetbodyaccesskeygenobrb" + "asefontimeupdateviacacheightmlabelooptgroupatternoembedetail" + "sampictureversedfnoframesetdirnameterowspanomoduleacronymali" + "gnmarkbdialogallowpaymentrequestrikeytypeallowusermediagroup" + "ingaltfooterubyasyncanvasidefaultitleaudioncancelautofocusan" + "dboxmplaceholderautoplaysinlinebdoncanplaythrough1bgsoundisa" + "bledivarbigblinkindraggablegendblockquotebuttonabortcitempro" + "penoncecolgrouplaintextrackcolorcolspannotation-xmlcommandco" + "ntrolsectionblurcoordshapecrossoriginslotranslatefacenterfie" + "ldsetfigcaptionafterprintegrityfigurequiredforeignObjectfore" + "ignobjectformactionautocompleteerrorformenctypemustmatchalle" + "ngeformmethodformnovalidatetimeformtargethiddenoscripthigh3h" + "reflanghttp-equivideonclickiframeimageimglyph4isindexismappl" + "etitemtypemarqueematheadersmallowfullscreenmaxlength5minleng" + "th6mtextareadonlymultiplemutedoncloseamlessortedoncontextmen" + "uitemidoncopyoncuechangeoncutondblclickondragendondragentero" + "ndragexitemreferrerpolicyondragleaveondragoverondragstarticl" + "eondropzonemptiedondurationchangeonendedonerroronfocusourceo" + "nhashchangeoninputmodeloninvalidonkeydownloadonkeypresspacer" + "onkeyupreloadonlanguagechangeonloadeddatalistingonloadedmeta" + "databindexonloadendonloadstartonmessageerroronmousedownonmou" + "seenteronmouseleaveonmousemoveonmouseoutputonmouseoveronmous" + "eupromptonmousewheelonofflineononlineonpagehidesclassearch2o" + "npageshowbronpastepublicontenteditableonpausemaponplayingonp" + "opstateonprogresspellcheckedonratechangeonrejectionhandledon" + "resetonresizesrcdocodeferonscrollonsecuritypolicyviolationau" + "xclickonseekedonseekingonselectedonshowidthgrouposteronsorta" + "bleonstalledonstorageonsubmitemscopedonsuspendontoggleonunha" + "ndledrejectionbeforeprintonunloadonvolumechangeonwaitingonwh" + "eeloptimumanifestrongoptionbeforeunloaddressrclangsrcsetstyl" + "esummarysupsvgsystemplateworkertypewrap" ================================================ FILE: vendor/golang.org/x/net/html/charset/charset.go ================================================ // Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package charset provides common text encodings for HTML documents. // // The mapping from encoding labels to encodings is defined at // https://encoding.spec.whatwg.org/. package charset // import "golang.org/x/net/html/charset" import ( "bytes" "fmt" "io" "mime" "strings" "unicode/utf8" "golang.org/x/net/html" "golang.org/x/text/encoding" "golang.org/x/text/encoding/charmap" "golang.org/x/text/encoding/htmlindex" "golang.org/x/text/transform" ) // Lookup returns the encoding with the specified label, and its canonical // name. It returns nil and the empty string if label is not one of the // standard encodings for HTML. Matching is case-insensitive and ignores // leading and trailing whitespace. Encoders will use HTML escape sequences for // runes that are not supported by the character set. func Lookup(label string) (e encoding.Encoding, name string) { e, err := htmlindex.Get(label) if err != nil { return nil, "" } name, _ = htmlindex.Name(e) return &htmlEncoding{e}, name } type htmlEncoding struct{ encoding.Encoding } func (h *htmlEncoding) NewEncoder() *encoding.Encoder { // HTML requires a non-terminating legacy encoder. We use HTML escapes to // substitute unsupported code points. return encoding.HTMLEscapeUnsupported(h.Encoding.NewEncoder()) } // DetermineEncoding determines the encoding of an HTML document by examining // up to the first 1024 bytes of content and the declared Content-Type. // // See http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#determining-the-character-encoding func DetermineEncoding(content []byte, contentType string) (e encoding.Encoding, name string, certain bool) { if len(content) > 1024 { content = content[:1024] } for _, b := range boms { if bytes.HasPrefix(content, b.bom) { e, name = Lookup(b.enc) return e, name, true } } if _, params, err := mime.ParseMediaType(contentType); err == nil { if cs, ok := params["charset"]; ok { if e, name = Lookup(cs); e != nil { return e, name, true } } } if len(content) > 0 { e, name = prescan(content) if e != nil { return e, name, false } } // Try to detect UTF-8. // First eliminate any partial rune at the end. for i := len(content) - 1; i >= 0 && i > len(content)-4; i-- { b := content[i] if b < 0x80 { break } if utf8.RuneStart(b) { content = content[:i] break } } hasHighBit := false for _, c := range content { if c >= 0x80 { hasHighBit = true break } } if hasHighBit && utf8.Valid(content) { return encoding.Nop, "utf-8", false } // TODO: change default depending on user's locale? return charmap.Windows1252, "windows-1252", false } // NewReader returns an io.Reader that converts the content of r to UTF-8. // It calls DetermineEncoding to find out what r's encoding is. func NewReader(r io.Reader, contentType string) (io.Reader, error) { preview := make([]byte, 1024) n, err := io.ReadFull(r, preview) switch { case err == io.ErrUnexpectedEOF: preview = preview[:n] r = bytes.NewReader(preview) case err != nil: return nil, err default: r = io.MultiReader(bytes.NewReader(preview), r) } if e, _, _ := DetermineEncoding(preview, contentType); e != encoding.Nop { r = transform.NewReader(r, e.NewDecoder()) } return r, nil } // NewReaderLabel returns a reader that converts from the specified charset to // UTF-8. It uses Lookup to find the encoding that corresponds to label, and // returns an error if Lookup returns nil. It is suitable for use as // encoding/xml.Decoder's CharsetReader function. func NewReaderLabel(label string, input io.Reader) (io.Reader, error) { e, _ := Lookup(label) if e == nil { return nil, fmt.Errorf("unsupported charset: %q", label) } return transform.NewReader(input, e.NewDecoder()), nil } func prescan(content []byte) (e encoding.Encoding, name string) { z := html.NewTokenizer(bytes.NewReader(content)) for { switch z.Next() { case html.ErrorToken: return nil, "" case html.StartTagToken, html.SelfClosingTagToken: tagName, hasAttr := z.TagName() if !bytes.Equal(tagName, []byte("meta")) { continue } attrList := make(map[string]bool) gotPragma := false const ( dontKnow = iota doNeedPragma doNotNeedPragma ) needPragma := dontKnow name = "" e = nil for hasAttr { var key, val []byte key, val, hasAttr = z.TagAttr() ks := string(key) if attrList[ks] { continue } attrList[ks] = true for i, c := range val { if 'A' <= c && c <= 'Z' { val[i] = c + 0x20 } } switch ks { case "http-equiv": if bytes.Equal(val, []byte("content-type")) { gotPragma = true } case "content": if e == nil { name = fromMetaElement(string(val)) if name != "" { e, name = Lookup(name) if e != nil { needPragma = doNeedPragma } } } case "charset": e, name = Lookup(string(val)) needPragma = doNotNeedPragma } } if needPragma == dontKnow || needPragma == doNeedPragma && !gotPragma { continue } if strings.HasPrefix(name, "utf-16") { name = "utf-8" e = encoding.Nop } if e != nil { return e, name } } } } func fromMetaElement(s string) string { for s != "" { csLoc := strings.Index(s, "charset") if csLoc == -1 { return "" } s = s[csLoc+len("charset"):] s = strings.TrimLeft(s, " \t\n\f\r") if !strings.HasPrefix(s, "=") { continue } s = s[1:] s = strings.TrimLeft(s, " \t\n\f\r") if s == "" { return "" } if q := s[0]; q == '"' || q == '\'' { s = s[1:] closeQuote := strings.IndexRune(s, rune(q)) if closeQuote == -1 { return "" } return s[:closeQuote] } end := strings.IndexAny(s, "; \t\n\f\r") if end == -1 { end = len(s) } return s[:end] } return "" } var boms = []struct { bom []byte enc string }{ {[]byte{0xfe, 0xff}, "utf-16be"}, {[]byte{0xff, 0xfe}, "utf-16le"}, {[]byte{0xef, 0xbb, 0xbf}, "utf-8"}, } ================================================ FILE: vendor/golang.org/x/net/html/const.go ================================================ // Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package html // Section 12.2.4.2 of the HTML5 specification says "The following elements // have varying levels of special parsing rules". // https://html.spec.whatwg.org/multipage/syntax.html#the-stack-of-open-elements var isSpecialElementMap = map[string]bool{ "address": true, "applet": true, "area": true, "article": true, "aside": true, "base": true, "basefont": true, "bgsound": true, "blockquote": true, "body": true, "br": true, "button": true, "caption": true, "center": true, "col": true, "colgroup": true, "dd": true, "details": true, "dir": true, "div": true, "dl": true, "dt": true, "embed": true, "fieldset": true, "figcaption": true, "figure": true, "footer": true, "form": true, "frame": true, "frameset": true, "h1": true, "h2": true, "h3": true, "h4": true, "h5": true, "h6": true, "head": true, "header": true, "hgroup": true, "hr": true, "html": true, "iframe": true, "img": true, "input": true, "keygen": true, // "keygen" has been removed from the spec, but are kept here for backwards compatibility. "li": true, "link": true, "listing": true, "main": true, "marquee": true, "menu": true, "meta": true, "nav": true, "noembed": true, "noframes": true, "noscript": true, "object": true, "ol": true, "p": true, "param": true, "plaintext": true, "pre": true, "script": true, "section": true, "select": true, "source": true, "style": true, "summary": true, "table": true, "tbody": true, "td": true, "template": true, "textarea": true, "tfoot": true, "th": true, "thead": true, "title": true, "tr": true, "track": true, "ul": true, "wbr": true, "xmp": true, } func isSpecialElement(element *Node) bool { switch element.Namespace { case "", "html": return isSpecialElementMap[element.Data] case "math": switch element.Data { case "mi", "mo", "mn", "ms", "mtext", "annotation-xml": return true } case "svg": switch element.Data { case "foreignObject", "desc", "title": return true } } return false } ================================================ FILE: vendor/golang.org/x/net/html/doc.go ================================================ // Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. /* Package html implements an HTML5-compliant tokenizer and parser. Tokenization is done by creating a Tokenizer for an io.Reader r. It is the caller's responsibility to ensure that r provides UTF-8 encoded HTML. z := html.NewTokenizer(r) Given a Tokenizer z, the HTML is tokenized by repeatedly calling z.Next(), which parses the next token and returns its type, or an error: for { tt := z.Next() if tt == html.ErrorToken { // ... return ... } // Process the current token. } There are two APIs for retrieving the current token. The high-level API is to call Token; the low-level API is to call Text or TagName / TagAttr. Both APIs allow optionally calling Raw after Next but before Token, Text, TagName, or TagAttr. In EBNF notation, the valid call sequence per token is: Next {Raw} [ Token | Text | TagName {TagAttr} ] Token returns an independent data structure that completely describes a token. Entities (such as "<") are unescaped, tag names and attribute keys are lower-cased, and attributes are collected into a []Attribute. For example: for { if z.Next() == html.ErrorToken { // Returning io.EOF indicates success. return z.Err() } emitToken(z.Token()) } The low-level API performs fewer allocations and copies, but the contents of the []byte values returned by Text, TagName and TagAttr may change on the next call to Next. For example, to extract an HTML page's anchor text: depth := 0 for { tt := z.Next() switch tt { case html.ErrorToken: return z.Err() case html.TextToken: if depth > 0 { // emitBytes should copy the []byte it receives, // if it doesn't process it immediately. emitBytes(z.Text()) } case html.StartTagToken, html.EndTagToken: tn, _ := z.TagName() if len(tn) == 1 && tn[0] == 'a' { if tt == html.StartTagToken { depth++ } else { depth-- } } } } Parsing is done by calling Parse with an io.Reader, which returns the root of the parse tree (the document element) as a *Node. It is the caller's responsibility to ensure that the Reader provides UTF-8 encoded HTML. For example, to process each anchor node in depth-first order: doc, err := html.Parse(r) if err != nil { // ... } for n := range doc.Descendants() { if n.Type == html.ElementNode && n.Data == "a" { // Do something with n... } } The relevant specifications include: https://html.spec.whatwg.org/multipage/syntax.html and https://html.spec.whatwg.org/multipage/syntax.html#tokenization # Security Considerations Care should be taken when parsing and interpreting HTML, whether full documents or fragments, within the framework of the HTML specification, especially with regard to untrusted inputs. This package provides both a tokenizer and a parser, which implement the tokenization, and tokenization and tree construction stages of the WHATWG HTML parsing specification respectively. While the tokenizer parses and normalizes individual HTML tokens, only the parser constructs the DOM tree from the tokenized HTML, as described in the tree construction stage of the specification, dynamically modifying or extending the document's DOM tree. If your use case requires semantically well-formed HTML documents, as defined by the WHATWG specification, the parser should be used rather than the tokenizer. In security contexts, if trust decisions are being made using the tokenized or parsed content, the input must be re-serialized (for instance by using Render or Token.String) in order for those trust decisions to hold, as the process of tokenization or parsing may alter the content. */ package html // import "golang.org/x/net/html" // The tokenization algorithm implemented by this package is not a line-by-line // transliteration of the relatively verbose state-machine in the WHATWG // specification. A more direct approach is used instead, where the program // counter implies the state, such as whether it is tokenizing a tag or a text // node. Specification compliance is verified by checking expected and actual // outputs over a test suite rather than aiming for algorithmic fidelity. // TODO(nigeltao): Does a DOM API belong in this package or a separate one? // TODO(nigeltao): How does parsing interact with a JavaScript engine? ================================================ FILE: vendor/golang.org/x/net/html/doctype.go ================================================ // Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package html import ( "strings" ) // parseDoctype parses the data from a DoctypeToken into a name, // public identifier, and system identifier. It returns a Node whose Type // is DoctypeNode, whose Data is the name, and which has attributes // named "system" and "public" for the two identifiers if they were present. // quirks is whether the document should be parsed in "quirks mode". func parseDoctype(s string) (n *Node, quirks bool) { n = &Node{Type: DoctypeNode} // Find the name. space := strings.IndexAny(s, whitespace) if space == -1 { space = len(s) } n.Data = s[:space] // The comparison to "html" is case-sensitive. if n.Data != "html" { quirks = true } n.Data = strings.ToLower(n.Data) s = strings.TrimLeft(s[space:], whitespace) if len(s) < 6 { // It can't start with "PUBLIC" or "SYSTEM". // Ignore the rest of the string. return n, quirks || s != "" } key := strings.ToLower(s[:6]) s = s[6:] for key == "public" || key == "system" { s = strings.TrimLeft(s, whitespace) if s == "" { break } quote := s[0] if quote != '"' && quote != '\'' { break } s = s[1:] q := strings.IndexRune(s, rune(quote)) var id string if q == -1 { id = s s = "" } else { id = s[:q] s = s[q+1:] } n.Attr = append(n.Attr, Attribute{Key: key, Val: id}) if key == "public" { key = "system" } else { key = "" } } if key != "" || s != "" { quirks = true } else if len(n.Attr) > 0 { if n.Attr[0].Key == "public" { public := strings.ToLower(n.Attr[0].Val) switch public { case "-//w3o//dtd w3 html strict 3.0//en//", "-/w3d/dtd html 4.0 transitional/en", "html": quirks = true default: for _, q := range quirkyIDs { if strings.HasPrefix(public, q) { quirks = true break } } } // The following two public IDs only cause quirks mode if there is no system ID. if len(n.Attr) == 1 && (strings.HasPrefix(public, "-//w3c//dtd html 4.01 frameset//") || strings.HasPrefix(public, "-//w3c//dtd html 4.01 transitional//")) { quirks = true } } if lastAttr := n.Attr[len(n.Attr)-1]; lastAttr.Key == "system" && strings.EqualFold(lastAttr.Val, "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") { quirks = true } } return n, quirks } // quirkyIDs is a list of public doctype identifiers that cause a document // to be interpreted in quirks mode. The identifiers should be in lower case. var quirkyIDs = []string{ "+//silmaril//dtd html pro v0r11 19970101//", "-//advasoft ltd//dtd html 3.0 aswedit + extensions//", "-//as//dtd html 3.0 aswedit + extensions//", "-//ietf//dtd html 2.0 level 1//", "-//ietf//dtd html 2.0 level 2//", "-//ietf//dtd html 2.0 strict level 1//", "-//ietf//dtd html 2.0 strict level 2//", "-//ietf//dtd html 2.0 strict//", "-//ietf//dtd html 2.0//", "-//ietf//dtd html 2.1e//", "-//ietf//dtd html 3.0//", "-//ietf//dtd html 3.2 final//", "-//ietf//dtd html 3.2//", "-//ietf//dtd html 3//", "-//ietf//dtd html level 0//", "-//ietf//dtd html level 1//", "-//ietf//dtd html level 2//", "-//ietf//dtd html level 3//", "-//ietf//dtd html strict level 0//", "-//ietf//dtd html strict level 1//", "-//ietf//dtd html strict level 2//", "-//ietf//dtd html strict level 3//", "-//ietf//dtd html strict//", "-//ietf//dtd html//", "-//metrius//dtd metrius presentational//", "-//microsoft//dtd internet explorer 2.0 html strict//", "-//microsoft//dtd internet explorer 2.0 html//", "-//microsoft//dtd internet explorer 2.0 tables//", "-//microsoft//dtd internet explorer 3.0 html strict//", "-//microsoft//dtd internet explorer 3.0 html//", "-//microsoft//dtd internet explorer 3.0 tables//", "-//netscape comm. corp.//dtd html//", "-//netscape comm. corp.//dtd strict html//", "-//o'reilly and associates//dtd html 2.0//", "-//o'reilly and associates//dtd html extended 1.0//", "-//o'reilly and associates//dtd html extended relaxed 1.0//", "-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//", "-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//", "-//spyglass//dtd html 2.0 extended//", "-//sq//dtd html 2.0 hotmetal + extensions//", "-//sun microsystems corp.//dtd hotjava html//", "-//sun microsystems corp.//dtd hotjava strict html//", "-//w3c//dtd html 3 1995-03-24//", "-//w3c//dtd html 3.2 draft//", "-//w3c//dtd html 3.2 final//", "-//w3c//dtd html 3.2//", "-//w3c//dtd html 3.2s draft//", "-//w3c//dtd html 4.0 frameset//", "-//w3c//dtd html 4.0 transitional//", "-//w3c//dtd html experimental 19960712//", "-//w3c//dtd html experimental 970421//", "-//w3c//dtd w3 html//", "-//w3o//dtd w3 html 3.0//", "-//webtechs//dtd mozilla html 2.0//", "-//webtechs//dtd mozilla html//", } ================================================ FILE: vendor/golang.org/x/net/html/entity.go ================================================ // Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package html // All entities that do not end with ';' are 6 or fewer bytes long. const longestEntityWithoutSemicolon = 6 // entity is a map from HTML entity names to their values. The semicolon matters: // https://html.spec.whatwg.org/multipage/syntax.html#named-character-references // lists both "amp" and "amp;" as two separate entries. // // Note that the HTML5 list is larger than the HTML4 list at // http://www.w3.org/TR/html4/sgml/entities.html var entity = map[string]rune{ "AElig;": '\U000000C6', "AMP;": '\U00000026', "Aacute;": '\U000000C1', "Abreve;": '\U00000102', "Acirc;": '\U000000C2', "Acy;": '\U00000410', "Afr;": '\U0001D504', "Agrave;": '\U000000C0', "Alpha;": '\U00000391', "Amacr;": '\U00000100', "And;": '\U00002A53', "Aogon;": '\U00000104', "Aopf;": '\U0001D538', "ApplyFunction;": '\U00002061', "Aring;": '\U000000C5', "Ascr;": '\U0001D49C', "Assign;": '\U00002254', "Atilde;": '\U000000C3', "Auml;": '\U000000C4', "Backslash;": '\U00002216', "Barv;": '\U00002AE7', "Barwed;": '\U00002306', "Bcy;": '\U00000411', "Because;": '\U00002235', "Bernoullis;": '\U0000212C', "Beta;": '\U00000392', "Bfr;": '\U0001D505', "Bopf;": '\U0001D539', "Breve;": '\U000002D8', "Bscr;": '\U0000212C', "Bumpeq;": '\U0000224E', "CHcy;": '\U00000427', "COPY;": '\U000000A9', "Cacute;": '\U00000106', "Cap;": '\U000022D2', "CapitalDifferentialD;": '\U00002145', "Cayleys;": '\U0000212D', "Ccaron;": '\U0000010C', "Ccedil;": '\U000000C7', "Ccirc;": '\U00000108', "Cconint;": '\U00002230', "Cdot;": '\U0000010A', "Cedilla;": '\U000000B8', "CenterDot;": '\U000000B7', "Cfr;": '\U0000212D', "Chi;": '\U000003A7', "CircleDot;": '\U00002299', "CircleMinus;": '\U00002296', "CirclePlus;": '\U00002295', "CircleTimes;": '\U00002297', "ClockwiseContourIntegral;": '\U00002232', "CloseCurlyDoubleQuote;": '\U0000201D', "CloseCurlyQuote;": '\U00002019', "Colon;": '\U00002237', "Colone;": '\U00002A74', "Congruent;": '\U00002261', "Conint;": '\U0000222F', "ContourIntegral;": '\U0000222E', "Copf;": '\U00002102', "Coproduct;": '\U00002210', "CounterClockwiseContourIntegral;": '\U00002233', "Cross;": '\U00002A2F', "Cscr;": '\U0001D49E', "Cup;": '\U000022D3', "CupCap;": '\U0000224D', "DD;": '\U00002145', "DDotrahd;": '\U00002911', "DJcy;": '\U00000402', "DScy;": '\U00000405', "DZcy;": '\U0000040F', "Dagger;": '\U00002021', "Darr;": '\U000021A1', "Dashv;": '\U00002AE4', "Dcaron;": '\U0000010E', "Dcy;": '\U00000414', "Del;": '\U00002207', "Delta;": '\U00000394', "Dfr;": '\U0001D507', "DiacriticalAcute;": '\U000000B4', "DiacriticalDot;": '\U000002D9', "DiacriticalDoubleAcute;": '\U000002DD', "DiacriticalGrave;": '\U00000060', "DiacriticalTilde;": '\U000002DC', "Diamond;": '\U000022C4', "DifferentialD;": '\U00002146', "Dopf;": '\U0001D53B', "Dot;": '\U000000A8', "DotDot;": '\U000020DC', "DotEqual;": '\U00002250', "DoubleContourIntegral;": '\U0000222F', "DoubleDot;": '\U000000A8', "DoubleDownArrow;": '\U000021D3', "DoubleLeftArrow;": '\U000021D0', "DoubleLeftRightArrow;": '\U000021D4', "DoubleLeftTee;": '\U00002AE4', "DoubleLongLeftArrow;": '\U000027F8', "DoubleLongLeftRightArrow;": '\U000027FA', "DoubleLongRightArrow;": '\U000027F9', "DoubleRightArrow;": '\U000021D2', "DoubleRightTee;": '\U000022A8', "DoubleUpArrow;": '\U000021D1', "DoubleUpDownArrow;": '\U000021D5', "DoubleVerticalBar;": '\U00002225', "DownArrow;": '\U00002193', "DownArrowBar;": '\U00002913', "DownArrowUpArrow;": '\U000021F5', "DownBreve;": '\U00000311', "DownLeftRightVector;": '\U00002950', "DownLeftTeeVector;": '\U0000295E', "DownLeftVector;": '\U000021BD', "DownLeftVectorBar;": '\U00002956', "DownRightTeeVector;": '\U0000295F', "DownRightVector;": '\U000021C1', "DownRightVectorBar;": '\U00002957', "DownTee;": '\U000022A4', "DownTeeArrow;": '\U000021A7', "Downarrow;": '\U000021D3', "Dscr;": '\U0001D49F', "Dstrok;": '\U00000110', "ENG;": '\U0000014A', "ETH;": '\U000000D0', "Eacute;": '\U000000C9', "Ecaron;": '\U0000011A', "Ecirc;": '\U000000CA', "Ecy;": '\U0000042D', "Edot;": '\U00000116', "Efr;": '\U0001D508', "Egrave;": '\U000000C8', "Element;": '\U00002208', "Emacr;": '\U00000112', "EmptySmallSquare;": '\U000025FB', "EmptyVerySmallSquare;": '\U000025AB', "Eogon;": '\U00000118', "Eopf;": '\U0001D53C', "Epsilon;": '\U00000395', "Equal;": '\U00002A75', "EqualTilde;": '\U00002242', "Equilibrium;": '\U000021CC', "Escr;": '\U00002130', "Esim;": '\U00002A73', "Eta;": '\U00000397', "Euml;": '\U000000CB', "Exists;": '\U00002203', "ExponentialE;": '\U00002147', "Fcy;": '\U00000424', "Ffr;": '\U0001D509', "FilledSmallSquare;": '\U000025FC', "FilledVerySmallSquare;": '\U000025AA', "Fopf;": '\U0001D53D', "ForAll;": '\U00002200', "Fouriertrf;": '\U00002131', "Fscr;": '\U00002131', "GJcy;": '\U00000403', "GT;": '\U0000003E', "Gamma;": '\U00000393', "Gammad;": '\U000003DC', "Gbreve;": '\U0000011E', "Gcedil;": '\U00000122', "Gcirc;": '\U0000011C', "Gcy;": '\U00000413', "Gdot;": '\U00000120', "Gfr;": '\U0001D50A', "Gg;": '\U000022D9', "Gopf;": '\U0001D53E', "GreaterEqual;": '\U00002265', "GreaterEqualLess;": '\U000022DB', "GreaterFullEqual;": '\U00002267', "GreaterGreater;": '\U00002AA2', "GreaterLess;": '\U00002277', "GreaterSlantEqual;": '\U00002A7E', "GreaterTilde;": '\U00002273', "Gscr;": '\U0001D4A2', "Gt;": '\U0000226B', "HARDcy;": '\U0000042A', "Hacek;": '\U000002C7', "Hat;": '\U0000005E', "Hcirc;": '\U00000124', "Hfr;": '\U0000210C', "HilbertSpace;": '\U0000210B', "Hopf;": '\U0000210D', "HorizontalLine;": '\U00002500', "Hscr;": '\U0000210B', "Hstrok;": '\U00000126', "HumpDownHump;": '\U0000224E', "HumpEqual;": '\U0000224F', "IEcy;": '\U00000415', "IJlig;": '\U00000132', "IOcy;": '\U00000401', "Iacute;": '\U000000CD', "Icirc;": '\U000000CE', "Icy;": '\U00000418', "Idot;": '\U00000130', "Ifr;": '\U00002111', "Igrave;": '\U000000CC', "Im;": '\U00002111', "Imacr;": '\U0000012A', "ImaginaryI;": '\U00002148', "Implies;": '\U000021D2', "Int;": '\U0000222C', "Integral;": '\U0000222B', "Intersection;": '\U000022C2', "InvisibleComma;": '\U00002063', "InvisibleTimes;": '\U00002062', "Iogon;": '\U0000012E', "Iopf;": '\U0001D540', "Iota;": '\U00000399', "Iscr;": '\U00002110', "Itilde;": '\U00000128', "Iukcy;": '\U00000406', "Iuml;": '\U000000CF', "Jcirc;": '\U00000134', "Jcy;": '\U00000419', "Jfr;": '\U0001D50D', "Jopf;": '\U0001D541', "Jscr;": '\U0001D4A5', "Jsercy;": '\U00000408', "Jukcy;": '\U00000404', "KHcy;": '\U00000425', "KJcy;": '\U0000040C', "Kappa;": '\U0000039A', "Kcedil;": '\U00000136', "Kcy;": '\U0000041A', "Kfr;": '\U0001D50E', "Kopf;": '\U0001D542', "Kscr;": '\U0001D4A6', "LJcy;": '\U00000409', "LT;": '\U0000003C', "Lacute;": '\U00000139', "Lambda;": '\U0000039B', "Lang;": '\U000027EA', "Laplacetrf;": '\U00002112', "Larr;": '\U0000219E', "Lcaron;": '\U0000013D', "Lcedil;": '\U0000013B', "Lcy;": '\U0000041B', "LeftAngleBracket;": '\U000027E8', "LeftArrow;": '\U00002190', "LeftArrowBar;": '\U000021E4', "LeftArrowRightArrow;": '\U000021C6', "LeftCeiling;": '\U00002308', "LeftDoubleBracket;": '\U000027E6', "LeftDownTeeVector;": '\U00002961', "LeftDownVector;": '\U000021C3', "LeftDownVectorBar;": '\U00002959', "LeftFloor;": '\U0000230A', "LeftRightArrow;": '\U00002194', "LeftRightVector;": '\U0000294E', "LeftTee;": '\U000022A3', "LeftTeeArrow;": '\U000021A4', "LeftTeeVector;": '\U0000295A', "LeftTriangle;": '\U000022B2', "LeftTriangleBar;": '\U000029CF', "LeftTriangleEqual;": '\U000022B4', "LeftUpDownVector;": '\U00002951', "LeftUpTeeVector;": '\U00002960', "LeftUpVector;": '\U000021BF', "LeftUpVectorBar;": '\U00002958', "LeftVector;": '\U000021BC', "LeftVectorBar;": '\U00002952', "Leftarrow;": '\U000021D0', "Leftrightarrow;": '\U000021D4', "LessEqualGreater;": '\U000022DA', "LessFullEqual;": '\U00002266', "LessGreater;": '\U00002276', "LessLess;": '\U00002AA1', "LessSlantEqual;": '\U00002A7D', "LessTilde;": '\U00002272', "Lfr;": '\U0001D50F', "Ll;": '\U000022D8', "Lleftarrow;": '\U000021DA', "Lmidot;": '\U0000013F', "LongLeftArrow;": '\U000027F5', "LongLeftRightArrow;": '\U000027F7', "LongRightArrow;": '\U000027F6', "Longleftarrow;": '\U000027F8', "Longleftrightarrow;": '\U000027FA', "Longrightarrow;": '\U000027F9', "Lopf;": '\U0001D543', "LowerLeftArrow;": '\U00002199', "LowerRightArrow;": '\U00002198', "Lscr;": '\U00002112', "Lsh;": '\U000021B0', "Lstrok;": '\U00000141', "Lt;": '\U0000226A', "Map;": '\U00002905', "Mcy;": '\U0000041C', "MediumSpace;": '\U0000205F', "Mellintrf;": '\U00002133', "Mfr;": '\U0001D510', "MinusPlus;": '\U00002213', "Mopf;": '\U0001D544', "Mscr;": '\U00002133', "Mu;": '\U0000039C', "NJcy;": '\U0000040A', "Nacute;": '\U00000143', "Ncaron;": '\U00000147', "Ncedil;": '\U00000145', "Ncy;": '\U0000041D', "NegativeMediumSpace;": '\U0000200B', "NegativeThickSpace;": '\U0000200B', "NegativeThinSpace;": '\U0000200B', "NegativeVeryThinSpace;": '\U0000200B', "NestedGreaterGreater;": '\U0000226B', "NestedLessLess;": '\U0000226A', "NewLine;": '\U0000000A', "Nfr;": '\U0001D511', "NoBreak;": '\U00002060', "NonBreakingSpace;": '\U000000A0', "Nopf;": '\U00002115', "Not;": '\U00002AEC', "NotCongruent;": '\U00002262', "NotCupCap;": '\U0000226D', "NotDoubleVerticalBar;": '\U00002226', "NotElement;": '\U00002209', "NotEqual;": '\U00002260', "NotExists;": '\U00002204', "NotGreater;": '\U0000226F', "NotGreaterEqual;": '\U00002271', "NotGreaterLess;": '\U00002279', "NotGreaterTilde;": '\U00002275', "NotLeftTriangle;": '\U000022EA', "NotLeftTriangleEqual;": '\U000022EC', "NotLess;": '\U0000226E', "NotLessEqual;": '\U00002270', "NotLessGreater;": '\U00002278', "NotLessTilde;": '\U00002274', "NotPrecedes;": '\U00002280', "NotPrecedesSlantEqual;": '\U000022E0', "NotReverseElement;": '\U0000220C', "NotRightTriangle;": '\U000022EB', "NotRightTriangleEqual;": '\U000022ED', "NotSquareSubsetEqual;": '\U000022E2', "NotSquareSupersetEqual;": '\U000022E3', "NotSubsetEqual;": '\U00002288', "NotSucceeds;": '\U00002281', "NotSucceedsSlantEqual;": '\U000022E1', "NotSupersetEqual;": '\U00002289', "NotTilde;": '\U00002241', "NotTildeEqual;": '\U00002244', "NotTildeFullEqual;": '\U00002247', "NotTildeTilde;": '\U00002249', "NotVerticalBar;": '\U00002224', "Nscr;": '\U0001D4A9', "Ntilde;": '\U000000D1', "Nu;": '\U0000039D', "OElig;": '\U00000152', "Oacute;": '\U000000D3', "Ocirc;": '\U000000D4', "Ocy;": '\U0000041E', "Odblac;": '\U00000150', "Ofr;": '\U0001D512', "Ograve;": '\U000000D2', "Omacr;": '\U0000014C', "Omega;": '\U000003A9', "Omicron;": '\U0000039F', "Oopf;": '\U0001D546', "OpenCurlyDoubleQuote;": '\U0000201C', "OpenCurlyQuote;": '\U00002018', "Or;": '\U00002A54', "Oscr;": '\U0001D4AA', "Oslash;": '\U000000D8', "Otilde;": '\U000000D5', "Otimes;": '\U00002A37', "Ouml;": '\U000000D6', "OverBar;": '\U0000203E', "OverBrace;": '\U000023DE', "OverBracket;": '\U000023B4', "OverParenthesis;": '\U000023DC', "PartialD;": '\U00002202', "Pcy;": '\U0000041F', "Pfr;": '\U0001D513', "Phi;": '\U000003A6', "Pi;": '\U000003A0', "PlusMinus;": '\U000000B1', "Poincareplane;": '\U0000210C', "Popf;": '\U00002119', "Pr;": '\U00002ABB', "Precedes;": '\U0000227A', "PrecedesEqual;": '\U00002AAF', "PrecedesSlantEqual;": '\U0000227C', "PrecedesTilde;": '\U0000227E', "Prime;": '\U00002033', "Product;": '\U0000220F', "Proportion;": '\U00002237', "Proportional;": '\U0000221D', "Pscr;": '\U0001D4AB', "Psi;": '\U000003A8', "QUOT;": '\U00000022', "Qfr;": '\U0001D514', "Qopf;": '\U0000211A', "Qscr;": '\U0001D4AC', "RBarr;": '\U00002910', "REG;": '\U000000AE', "Racute;": '\U00000154', "Rang;": '\U000027EB', "Rarr;": '\U000021A0', "Rarrtl;": '\U00002916', "Rcaron;": '\U00000158', "Rcedil;": '\U00000156', "Rcy;": '\U00000420', "Re;": '\U0000211C', "ReverseElement;": '\U0000220B', "ReverseEquilibrium;": '\U000021CB', "ReverseUpEquilibrium;": '\U0000296F', "Rfr;": '\U0000211C', "Rho;": '\U000003A1', "RightAngleBracket;": '\U000027E9', "RightArrow;": '\U00002192', "RightArrowBar;": '\U000021E5', "RightArrowLeftArrow;": '\U000021C4', "RightCeiling;": '\U00002309', "RightDoubleBracket;": '\U000027E7', "RightDownTeeVector;": '\U0000295D', "RightDownVector;": '\U000021C2', "RightDownVectorBar;": '\U00002955', "RightFloor;": '\U0000230B', "RightTee;": '\U000022A2', "RightTeeArrow;": '\U000021A6', "RightTeeVector;": '\U0000295B', "RightTriangle;": '\U000022B3', "RightTriangleBar;": '\U000029D0', "RightTriangleEqual;": '\U000022B5', "RightUpDownVector;": '\U0000294F', "RightUpTeeVector;": '\U0000295C', "RightUpVector;": '\U000021BE', "RightUpVectorBar;": '\U00002954', "RightVector;": '\U000021C0', "RightVectorBar;": '\U00002953', "Rightarrow;": '\U000021D2', "Ropf;": '\U0000211D', "RoundImplies;": '\U00002970', "Rrightarrow;": '\U000021DB', "Rscr;": '\U0000211B', "Rsh;": '\U000021B1', "RuleDelayed;": '\U000029F4', "SHCHcy;": '\U00000429', "SHcy;": '\U00000428', "SOFTcy;": '\U0000042C', "Sacute;": '\U0000015A', "Sc;": '\U00002ABC', "Scaron;": '\U00000160', "Scedil;": '\U0000015E', "Scirc;": '\U0000015C', "Scy;": '\U00000421', "Sfr;": '\U0001D516', "ShortDownArrow;": '\U00002193', "ShortLeftArrow;": '\U00002190', "ShortRightArrow;": '\U00002192', "ShortUpArrow;": '\U00002191', "Sigma;": '\U000003A3', "SmallCircle;": '\U00002218', "Sopf;": '\U0001D54A', "Sqrt;": '\U0000221A', "Square;": '\U000025A1', "SquareIntersection;": '\U00002293', "SquareSubset;": '\U0000228F', "SquareSubsetEqual;": '\U00002291', "SquareSuperset;": '\U00002290', "SquareSupersetEqual;": '\U00002292', "SquareUnion;": '\U00002294', "Sscr;": '\U0001D4AE', "Star;": '\U000022C6', "Sub;": '\U000022D0', "Subset;": '\U000022D0', "SubsetEqual;": '\U00002286', "Succeeds;": '\U0000227B', "SucceedsEqual;": '\U00002AB0', "SucceedsSlantEqual;": '\U0000227D', "SucceedsTilde;": '\U0000227F', "SuchThat;": '\U0000220B', "Sum;": '\U00002211', "Sup;": '\U000022D1', "Superset;": '\U00002283', "SupersetEqual;": '\U00002287', "Supset;": '\U000022D1', "THORN;": '\U000000DE', "TRADE;": '\U00002122', "TSHcy;": '\U0000040B', "TScy;": '\U00000426', "Tab;": '\U00000009', "Tau;": '\U000003A4', "Tcaron;": '\U00000164', "Tcedil;": '\U00000162', "Tcy;": '\U00000422', "Tfr;": '\U0001D517', "Therefore;": '\U00002234', "Theta;": '\U00000398', "ThinSpace;": '\U00002009', "Tilde;": '\U0000223C', "TildeEqual;": '\U00002243', "TildeFullEqual;": '\U00002245', "TildeTilde;": '\U00002248', "Topf;": '\U0001D54B', "TripleDot;": '\U000020DB', "Tscr;": '\U0001D4AF', "Tstrok;": '\U00000166', "Uacute;": '\U000000DA', "Uarr;": '\U0000219F', "Uarrocir;": '\U00002949', "Ubrcy;": '\U0000040E', "Ubreve;": '\U0000016C', "Ucirc;": '\U000000DB', "Ucy;": '\U00000423', "Udblac;": '\U00000170', "Ufr;": '\U0001D518', "Ugrave;": '\U000000D9', "Umacr;": '\U0000016A', "UnderBar;": '\U0000005F', "UnderBrace;": '\U000023DF', "UnderBracket;": '\U000023B5', "UnderParenthesis;": '\U000023DD', "Union;": '\U000022C3', "UnionPlus;": '\U0000228E', "Uogon;": '\U00000172', "Uopf;": '\U0001D54C', "UpArrow;": '\U00002191', "UpArrowBar;": '\U00002912', "UpArrowDownArrow;": '\U000021C5', "UpDownArrow;": '\U00002195', "UpEquilibrium;": '\U0000296E', "UpTee;": '\U000022A5', "UpTeeArrow;": '\U000021A5', "Uparrow;": '\U000021D1', "Updownarrow;": '\U000021D5', "UpperLeftArrow;": '\U00002196', "UpperRightArrow;": '\U00002197', "Upsi;": '\U000003D2', "Upsilon;": '\U000003A5', "Uring;": '\U0000016E', "Uscr;": '\U0001D4B0', "Utilde;": '\U00000168', "Uuml;": '\U000000DC', "VDash;": '\U000022AB', "Vbar;": '\U00002AEB', "Vcy;": '\U00000412', "Vdash;": '\U000022A9', "Vdashl;": '\U00002AE6', "Vee;": '\U000022C1', "Verbar;": '\U00002016', "Vert;": '\U00002016', "VerticalBar;": '\U00002223', "VerticalLine;": '\U0000007C', "VerticalSeparator;": '\U00002758', "VerticalTilde;": '\U00002240', "VeryThinSpace;": '\U0000200A', "Vfr;": '\U0001D519', "Vopf;": '\U0001D54D', "Vscr;": '\U0001D4B1', "Vvdash;": '\U000022AA', "Wcirc;": '\U00000174', "Wedge;": '\U000022C0', "Wfr;": '\U0001D51A', "Wopf;": '\U0001D54E', "Wscr;": '\U0001D4B2', "Xfr;": '\U0001D51B', "Xi;": '\U0000039E', "Xopf;": '\U0001D54F', "Xscr;": '\U0001D4B3', "YAcy;": '\U0000042F', "YIcy;": '\U00000407', "YUcy;": '\U0000042E', "Yacute;": '\U000000DD', "Ycirc;": '\U00000176', "Ycy;": '\U0000042B', "Yfr;": '\U0001D51C', "Yopf;": '\U0001D550', "Yscr;": '\U0001D4B4', "Yuml;": '\U00000178', "ZHcy;": '\U00000416', "Zacute;": '\U00000179', "Zcaron;": '\U0000017D', "Zcy;": '\U00000417', "Zdot;": '\U0000017B', "ZeroWidthSpace;": '\U0000200B', "Zeta;": '\U00000396', "Zfr;": '\U00002128', "Zopf;": '\U00002124', "Zscr;": '\U0001D4B5', "aacute;": '\U000000E1', "abreve;": '\U00000103', "ac;": '\U0000223E', "acd;": '\U0000223F', "acirc;": '\U000000E2', "acute;": '\U000000B4', "acy;": '\U00000430', "aelig;": '\U000000E6', "af;": '\U00002061', "afr;": '\U0001D51E', "agrave;": '\U000000E0', "alefsym;": '\U00002135', "aleph;": '\U00002135', "alpha;": '\U000003B1', "amacr;": '\U00000101', "amalg;": '\U00002A3F', "amp;": '\U00000026', "and;": '\U00002227', "andand;": '\U00002A55', "andd;": '\U00002A5C', "andslope;": '\U00002A58', "andv;": '\U00002A5A', "ang;": '\U00002220', "ange;": '\U000029A4', "angle;": '\U00002220', "angmsd;": '\U00002221', "angmsdaa;": '\U000029A8', "angmsdab;": '\U000029A9', "angmsdac;": '\U000029AA', "angmsdad;": '\U000029AB', "angmsdae;": '\U000029AC', "angmsdaf;": '\U000029AD', "angmsdag;": '\U000029AE', "angmsdah;": '\U000029AF', "angrt;": '\U0000221F', "angrtvb;": '\U000022BE', "angrtvbd;": '\U0000299D', "angsph;": '\U00002222', "angst;": '\U000000C5', "angzarr;": '\U0000237C', "aogon;": '\U00000105', "aopf;": '\U0001D552', "ap;": '\U00002248', "apE;": '\U00002A70', "apacir;": '\U00002A6F', "ape;": '\U0000224A', "apid;": '\U0000224B', "apos;": '\U00000027', "approx;": '\U00002248', "approxeq;": '\U0000224A', "aring;": '\U000000E5', "ascr;": '\U0001D4B6', "ast;": '\U0000002A', "asymp;": '\U00002248', "asympeq;": '\U0000224D', "atilde;": '\U000000E3', "auml;": '\U000000E4', "awconint;": '\U00002233', "awint;": '\U00002A11', "bNot;": '\U00002AED', "backcong;": '\U0000224C', "backepsilon;": '\U000003F6', "backprime;": '\U00002035', "backsim;": '\U0000223D', "backsimeq;": '\U000022CD', "barvee;": '\U000022BD', "barwed;": '\U00002305', "barwedge;": '\U00002305', "bbrk;": '\U000023B5', "bbrktbrk;": '\U000023B6', "bcong;": '\U0000224C', "bcy;": '\U00000431', "bdquo;": '\U0000201E', "becaus;": '\U00002235', "because;": '\U00002235', "bemptyv;": '\U000029B0', "bepsi;": '\U000003F6', "bernou;": '\U0000212C', "beta;": '\U000003B2', "beth;": '\U00002136', "between;": '\U0000226C', "bfr;": '\U0001D51F', "bigcap;": '\U000022C2', "bigcirc;": '\U000025EF', "bigcup;": '\U000022C3', "bigodot;": '\U00002A00', "bigoplus;": '\U00002A01', "bigotimes;": '\U00002A02', "bigsqcup;": '\U00002A06', "bigstar;": '\U00002605', "bigtriangledown;": '\U000025BD', "bigtriangleup;": '\U000025B3', "biguplus;": '\U00002A04', "bigvee;": '\U000022C1', "bigwedge;": '\U000022C0', "bkarow;": '\U0000290D', "blacklozenge;": '\U000029EB', "blacksquare;": '\U000025AA', "blacktriangle;": '\U000025B4', "blacktriangledown;": '\U000025BE', "blacktriangleleft;": '\U000025C2', "blacktriangleright;": '\U000025B8', "blank;": '\U00002423', "blk12;": '\U00002592', "blk14;": '\U00002591', "blk34;": '\U00002593', "block;": '\U00002588', "bnot;": '\U00002310', "bopf;": '\U0001D553', "bot;": '\U000022A5', "bottom;": '\U000022A5', "bowtie;": '\U000022C8', "boxDL;": '\U00002557', "boxDR;": '\U00002554', "boxDl;": '\U00002556', "boxDr;": '\U00002553', "boxH;": '\U00002550', "boxHD;": '\U00002566', "boxHU;": '\U00002569', "boxHd;": '\U00002564', "boxHu;": '\U00002567', "boxUL;": '\U0000255D', "boxUR;": '\U0000255A', "boxUl;": '\U0000255C', "boxUr;": '\U00002559', "boxV;": '\U00002551', "boxVH;": '\U0000256C', "boxVL;": '\U00002563', "boxVR;": '\U00002560', "boxVh;": '\U0000256B', "boxVl;": '\U00002562', "boxVr;": '\U0000255F', "boxbox;": '\U000029C9', "boxdL;": '\U00002555', "boxdR;": '\U00002552', "boxdl;": '\U00002510', "boxdr;": '\U0000250C', "boxh;": '\U00002500', "boxhD;": '\U00002565', "boxhU;": '\U00002568', "boxhd;": '\U0000252C', "boxhu;": '\U00002534', "boxminus;": '\U0000229F', "boxplus;": '\U0000229E', "boxtimes;": '\U000022A0', "boxuL;": '\U0000255B', "boxuR;": '\U00002558', "boxul;": '\U00002518', "boxur;": '\U00002514', "boxv;": '\U00002502', "boxvH;": '\U0000256A', "boxvL;": '\U00002561', "boxvR;": '\U0000255E', "boxvh;": '\U0000253C', "boxvl;": '\U00002524', "boxvr;": '\U0000251C', "bprime;": '\U00002035', "breve;": '\U000002D8', "brvbar;": '\U000000A6', "bscr;": '\U0001D4B7', "bsemi;": '\U0000204F', "bsim;": '\U0000223D', "bsime;": '\U000022CD', "bsol;": '\U0000005C', "bsolb;": '\U000029C5', "bsolhsub;": '\U000027C8', "bull;": '\U00002022', "bullet;": '\U00002022', "bump;": '\U0000224E', "bumpE;": '\U00002AAE', "bumpe;": '\U0000224F', "bumpeq;": '\U0000224F', "cacute;": '\U00000107', "cap;": '\U00002229', "capand;": '\U00002A44', "capbrcup;": '\U00002A49', "capcap;": '\U00002A4B', "capcup;": '\U00002A47', "capdot;": '\U00002A40', "caret;": '\U00002041', "caron;": '\U000002C7', "ccaps;": '\U00002A4D', "ccaron;": '\U0000010D', "ccedil;": '\U000000E7', "ccirc;": '\U00000109', "ccups;": '\U00002A4C', "ccupssm;": '\U00002A50', "cdot;": '\U0000010B', "cedil;": '\U000000B8', "cemptyv;": '\U000029B2', "cent;": '\U000000A2', "centerdot;": '\U000000B7', "cfr;": '\U0001D520', "chcy;": '\U00000447', "check;": '\U00002713', "checkmark;": '\U00002713', "chi;": '\U000003C7', "cir;": '\U000025CB', "cirE;": '\U000029C3', "circ;": '\U000002C6', "circeq;": '\U00002257', "circlearrowleft;": '\U000021BA', "circlearrowright;": '\U000021BB', "circledR;": '\U000000AE', "circledS;": '\U000024C8', "circledast;": '\U0000229B', "circledcirc;": '\U0000229A', "circleddash;": '\U0000229D', "cire;": '\U00002257', "cirfnint;": '\U00002A10', "cirmid;": '\U00002AEF', "cirscir;": '\U000029C2', "clubs;": '\U00002663', "clubsuit;": '\U00002663', "colon;": '\U0000003A', "colone;": '\U00002254', "coloneq;": '\U00002254', "comma;": '\U0000002C', "commat;": '\U00000040', "comp;": '\U00002201', "compfn;": '\U00002218', "complement;": '\U00002201', "complexes;": '\U00002102', "cong;": '\U00002245', "congdot;": '\U00002A6D', "conint;": '\U0000222E', "copf;": '\U0001D554', "coprod;": '\U00002210', "copy;": '\U000000A9', "copysr;": '\U00002117', "crarr;": '\U000021B5', "cross;": '\U00002717', "cscr;": '\U0001D4B8', "csub;": '\U00002ACF', "csube;": '\U00002AD1', "csup;": '\U00002AD0', "csupe;": '\U00002AD2', "ctdot;": '\U000022EF', "cudarrl;": '\U00002938', "cudarrr;": '\U00002935', "cuepr;": '\U000022DE', "cuesc;": '\U000022DF', "cularr;": '\U000021B6', "cularrp;": '\U0000293D', "cup;": '\U0000222A', "cupbrcap;": '\U00002A48', "cupcap;": '\U00002A46', "cupcup;": '\U00002A4A', "cupdot;": '\U0000228D', "cupor;": '\U00002A45', "curarr;": '\U000021B7', "curarrm;": '\U0000293C', "curlyeqprec;": '\U000022DE', "curlyeqsucc;": '\U000022DF', "curlyvee;": '\U000022CE', "curlywedge;": '\U000022CF', "curren;": '\U000000A4', "curvearrowleft;": '\U000021B6', "curvearrowright;": '\U000021B7', "cuvee;": '\U000022CE', "cuwed;": '\U000022CF', "cwconint;": '\U00002232', "cwint;": '\U00002231', "cylcty;": '\U0000232D', "dArr;": '\U000021D3', "dHar;": '\U00002965', "dagger;": '\U00002020', "daleth;": '\U00002138', "darr;": '\U00002193', "dash;": '\U00002010', "dashv;": '\U000022A3', "dbkarow;": '\U0000290F', "dblac;": '\U000002DD', "dcaron;": '\U0000010F', "dcy;": '\U00000434', "dd;": '\U00002146', "ddagger;": '\U00002021', "ddarr;": '\U000021CA', "ddotseq;": '\U00002A77', "deg;": '\U000000B0', "delta;": '\U000003B4', "demptyv;": '\U000029B1', "dfisht;": '\U0000297F', "dfr;": '\U0001D521', "dharl;": '\U000021C3', "dharr;": '\U000021C2', "diam;": '\U000022C4', "diamond;": '\U000022C4', "diamondsuit;": '\U00002666', "diams;": '\U00002666', "die;": '\U000000A8', "digamma;": '\U000003DD', "disin;": '\U000022F2', "div;": '\U000000F7', "divide;": '\U000000F7', "divideontimes;": '\U000022C7', "divonx;": '\U000022C7', "djcy;": '\U00000452', "dlcorn;": '\U0000231E', "dlcrop;": '\U0000230D', "dollar;": '\U00000024', "dopf;": '\U0001D555', "dot;": '\U000002D9', "doteq;": '\U00002250', "doteqdot;": '\U00002251', "dotminus;": '\U00002238', "dotplus;": '\U00002214', "dotsquare;": '\U000022A1', "doublebarwedge;": '\U00002306', "downarrow;": '\U00002193', "downdownarrows;": '\U000021CA', "downharpoonleft;": '\U000021C3', "downharpoonright;": '\U000021C2', "drbkarow;": '\U00002910', "drcorn;": '\U0000231F', "drcrop;": '\U0000230C', "dscr;": '\U0001D4B9', "dscy;": '\U00000455', "dsol;": '\U000029F6', "dstrok;": '\U00000111', "dtdot;": '\U000022F1', "dtri;": '\U000025BF', "dtrif;": '\U000025BE', "duarr;": '\U000021F5', "duhar;": '\U0000296F', "dwangle;": '\U000029A6', "dzcy;": '\U0000045F', "dzigrarr;": '\U000027FF', "eDDot;": '\U00002A77', "eDot;": '\U00002251', "eacute;": '\U000000E9', "easter;": '\U00002A6E', "ecaron;": '\U0000011B', "ecir;": '\U00002256', "ecirc;": '\U000000EA', "ecolon;": '\U00002255', "ecy;": '\U0000044D', "edot;": '\U00000117', "ee;": '\U00002147', "efDot;": '\U00002252', "efr;": '\U0001D522', "eg;": '\U00002A9A', "egrave;": '\U000000E8', "egs;": '\U00002A96', "egsdot;": '\U00002A98', "el;": '\U00002A99', "elinters;": '\U000023E7', "ell;": '\U00002113', "els;": '\U00002A95', "elsdot;": '\U00002A97', "emacr;": '\U00000113', "empty;": '\U00002205', "emptyset;": '\U00002205', "emptyv;": '\U00002205', "emsp;": '\U00002003', "emsp13;": '\U00002004', "emsp14;": '\U00002005', "eng;": '\U0000014B', "ensp;": '\U00002002', "eogon;": '\U00000119', "eopf;": '\U0001D556', "epar;": '\U000022D5', "eparsl;": '\U000029E3', "eplus;": '\U00002A71', "epsi;": '\U000003B5', "epsilon;": '\U000003B5', "epsiv;": '\U000003F5', "eqcirc;": '\U00002256', "eqcolon;": '\U00002255', "eqsim;": '\U00002242', "eqslantgtr;": '\U00002A96', "eqslantless;": '\U00002A95', "equals;": '\U0000003D', "equest;": '\U0000225F', "equiv;": '\U00002261', "equivDD;": '\U00002A78', "eqvparsl;": '\U000029E5', "erDot;": '\U00002253', "erarr;": '\U00002971', "escr;": '\U0000212F', "esdot;": '\U00002250', "esim;": '\U00002242', "eta;": '\U000003B7', "eth;": '\U000000F0', "euml;": '\U000000EB', "euro;": '\U000020AC', "excl;": '\U00000021', "exist;": '\U00002203', "expectation;": '\U00002130', "exponentiale;": '\U00002147', "fallingdotseq;": '\U00002252', "fcy;": '\U00000444', "female;": '\U00002640', "ffilig;": '\U0000FB03', "fflig;": '\U0000FB00', "ffllig;": '\U0000FB04', "ffr;": '\U0001D523', "filig;": '\U0000FB01', "flat;": '\U0000266D', "fllig;": '\U0000FB02', "fltns;": '\U000025B1', "fnof;": '\U00000192', "fopf;": '\U0001D557', "forall;": '\U00002200', "fork;": '\U000022D4', "forkv;": '\U00002AD9', "fpartint;": '\U00002A0D', "frac12;": '\U000000BD', "frac13;": '\U00002153', "frac14;": '\U000000BC', "frac15;": '\U00002155', "frac16;": '\U00002159', "frac18;": '\U0000215B', "frac23;": '\U00002154', "frac25;": '\U00002156', "frac34;": '\U000000BE', "frac35;": '\U00002157', "frac38;": '\U0000215C', "frac45;": '\U00002158', "frac56;": '\U0000215A', "frac58;": '\U0000215D', "frac78;": '\U0000215E', "frasl;": '\U00002044', "frown;": '\U00002322', "fscr;": '\U0001D4BB', "gE;": '\U00002267', "gEl;": '\U00002A8C', "gacute;": '\U000001F5', "gamma;": '\U000003B3', "gammad;": '\U000003DD', "gap;": '\U00002A86', "gbreve;": '\U0000011F', "gcirc;": '\U0000011D', "gcy;": '\U00000433', "gdot;": '\U00000121', "ge;": '\U00002265', "gel;": '\U000022DB', "geq;": '\U00002265', "geqq;": '\U00002267', "geqslant;": '\U00002A7E', "ges;": '\U00002A7E', "gescc;": '\U00002AA9', "gesdot;": '\U00002A80', "gesdoto;": '\U00002A82', "gesdotol;": '\U00002A84', "gesles;": '\U00002A94', "gfr;": '\U0001D524', "gg;": '\U0000226B', "ggg;": '\U000022D9', "gimel;": '\U00002137', "gjcy;": '\U00000453', "gl;": '\U00002277', "glE;": '\U00002A92', "gla;": '\U00002AA5', "glj;": '\U00002AA4', "gnE;": '\U00002269', "gnap;": '\U00002A8A', "gnapprox;": '\U00002A8A', "gne;": '\U00002A88', "gneq;": '\U00002A88', "gneqq;": '\U00002269', "gnsim;": '\U000022E7', "gopf;": '\U0001D558', "grave;": '\U00000060', "gscr;": '\U0000210A', "gsim;": '\U00002273', "gsime;": '\U00002A8E', "gsiml;": '\U00002A90', "gt;": '\U0000003E', "gtcc;": '\U00002AA7', "gtcir;": '\U00002A7A', "gtdot;": '\U000022D7', "gtlPar;": '\U00002995', "gtquest;": '\U00002A7C', "gtrapprox;": '\U00002A86', "gtrarr;": '\U00002978', "gtrdot;": '\U000022D7', "gtreqless;": '\U000022DB', "gtreqqless;": '\U00002A8C', "gtrless;": '\U00002277', "gtrsim;": '\U00002273', "hArr;": '\U000021D4', "hairsp;": '\U0000200A', "half;": '\U000000BD', "hamilt;": '\U0000210B', "hardcy;": '\U0000044A', "harr;": '\U00002194', "harrcir;": '\U00002948', "harrw;": '\U000021AD', "hbar;": '\U0000210F', "hcirc;": '\U00000125', "hearts;": '\U00002665', "heartsuit;": '\U00002665', "hellip;": '\U00002026', "hercon;": '\U000022B9', "hfr;": '\U0001D525', "hksearow;": '\U00002925', "hkswarow;": '\U00002926', "hoarr;": '\U000021FF', "homtht;": '\U0000223B', "hookleftarrow;": '\U000021A9', "hookrightarrow;": '\U000021AA', "hopf;": '\U0001D559', "horbar;": '\U00002015', "hscr;": '\U0001D4BD', "hslash;": '\U0000210F', "hstrok;": '\U00000127', "hybull;": '\U00002043', "hyphen;": '\U00002010', "iacute;": '\U000000ED', "ic;": '\U00002063', "icirc;": '\U000000EE', "icy;": '\U00000438', "iecy;": '\U00000435', "iexcl;": '\U000000A1', "iff;": '\U000021D4', "ifr;": '\U0001D526', "igrave;": '\U000000EC', "ii;": '\U00002148', "iiiint;": '\U00002A0C', "iiint;": '\U0000222D', "iinfin;": '\U000029DC', "iiota;": '\U00002129', "ijlig;": '\U00000133', "imacr;": '\U0000012B', "image;": '\U00002111', "imagline;": '\U00002110', "imagpart;": '\U00002111', "imath;": '\U00000131', "imof;": '\U000022B7', "imped;": '\U000001B5', "in;": '\U00002208', "incare;": '\U00002105', "infin;": '\U0000221E', "infintie;": '\U000029DD', "inodot;": '\U00000131', "int;": '\U0000222B', "intcal;": '\U000022BA', "integers;": '\U00002124', "intercal;": '\U000022BA', "intlarhk;": '\U00002A17', "intprod;": '\U00002A3C', "iocy;": '\U00000451', "iogon;": '\U0000012F', "iopf;": '\U0001D55A', "iota;": '\U000003B9', "iprod;": '\U00002A3C', "iquest;": '\U000000BF', "iscr;": '\U0001D4BE', "isin;": '\U00002208', "isinE;": '\U000022F9', "isindot;": '\U000022F5', "isins;": '\U000022F4', "isinsv;": '\U000022F3', "isinv;": '\U00002208', "it;": '\U00002062', "itilde;": '\U00000129', "iukcy;": '\U00000456', "iuml;": '\U000000EF', "jcirc;": '\U00000135', "jcy;": '\U00000439', "jfr;": '\U0001D527', "jmath;": '\U00000237', "jopf;": '\U0001D55B', "jscr;": '\U0001D4BF', "jsercy;": '\U00000458', "jukcy;": '\U00000454', "kappa;": '\U000003BA', "kappav;": '\U000003F0', "kcedil;": '\U00000137', "kcy;": '\U0000043A', "kfr;": '\U0001D528', "kgreen;": '\U00000138', "khcy;": '\U00000445', "kjcy;": '\U0000045C', "kopf;": '\U0001D55C', "kscr;": '\U0001D4C0', "lAarr;": '\U000021DA', "lArr;": '\U000021D0', "lAtail;": '\U0000291B', "lBarr;": '\U0000290E', "lE;": '\U00002266', "lEg;": '\U00002A8B', "lHar;": '\U00002962', "lacute;": '\U0000013A', "laemptyv;": '\U000029B4', "lagran;": '\U00002112', "lambda;": '\U000003BB', "lang;": '\U000027E8', "langd;": '\U00002991', "langle;": '\U000027E8', "lap;": '\U00002A85', "laquo;": '\U000000AB', "larr;": '\U00002190', "larrb;": '\U000021E4', "larrbfs;": '\U0000291F', "larrfs;": '\U0000291D', "larrhk;": '\U000021A9', "larrlp;": '\U000021AB', "larrpl;": '\U00002939', "larrsim;": '\U00002973', "larrtl;": '\U000021A2', "lat;": '\U00002AAB', "latail;": '\U00002919', "late;": '\U00002AAD', "lbarr;": '\U0000290C', "lbbrk;": '\U00002772', "lbrace;": '\U0000007B', "lbrack;": '\U0000005B', "lbrke;": '\U0000298B', "lbrksld;": '\U0000298F', "lbrkslu;": '\U0000298D', "lcaron;": '\U0000013E', "lcedil;": '\U0000013C', "lceil;": '\U00002308', "lcub;": '\U0000007B', "lcy;": '\U0000043B', "ldca;": '\U00002936', "ldquo;": '\U0000201C', "ldquor;": '\U0000201E', "ldrdhar;": '\U00002967', "ldrushar;": '\U0000294B', "ldsh;": '\U000021B2', "le;": '\U00002264', "leftarrow;": '\U00002190', "leftarrowtail;": '\U000021A2', "leftharpoondown;": '\U000021BD', "leftharpoonup;": '\U000021BC', "leftleftarrows;": '\U000021C7', "leftrightarrow;": '\U00002194', "leftrightarrows;": '\U000021C6', "leftrightharpoons;": '\U000021CB', "leftrightsquigarrow;": '\U000021AD', "leftthreetimes;": '\U000022CB', "leg;": '\U000022DA', "leq;": '\U00002264', "leqq;": '\U00002266', "leqslant;": '\U00002A7D', "les;": '\U00002A7D', "lescc;": '\U00002AA8', "lesdot;": '\U00002A7F', "lesdoto;": '\U00002A81', "lesdotor;": '\U00002A83', "lesges;": '\U00002A93', "lessapprox;": '\U00002A85', "lessdot;": '\U000022D6', "lesseqgtr;": '\U000022DA', "lesseqqgtr;": '\U00002A8B', "lessgtr;": '\U00002276', "lesssim;": '\U00002272', "lfisht;": '\U0000297C', "lfloor;": '\U0000230A', "lfr;": '\U0001D529', "lg;": '\U00002276', "lgE;": '\U00002A91', "lhard;": '\U000021BD', "lharu;": '\U000021BC', "lharul;": '\U0000296A', "lhblk;": '\U00002584', "ljcy;": '\U00000459', "ll;": '\U0000226A', "llarr;": '\U000021C7', "llcorner;": '\U0000231E', "llhard;": '\U0000296B', "lltri;": '\U000025FA', "lmidot;": '\U00000140', "lmoust;": '\U000023B0', "lmoustache;": '\U000023B0', "lnE;": '\U00002268', "lnap;": '\U00002A89', "lnapprox;": '\U00002A89', "lne;": '\U00002A87', "lneq;": '\U00002A87', "lneqq;": '\U00002268', "lnsim;": '\U000022E6', "loang;": '\U000027EC', "loarr;": '\U000021FD', "lobrk;": '\U000027E6', "longleftarrow;": '\U000027F5', "longleftrightarrow;": '\U000027F7', "longmapsto;": '\U000027FC', "longrightarrow;": '\U000027F6', "looparrowleft;": '\U000021AB', "looparrowright;": '\U000021AC', "lopar;": '\U00002985', "lopf;": '\U0001D55D', "loplus;": '\U00002A2D', "lotimes;": '\U00002A34', "lowast;": '\U00002217', "lowbar;": '\U0000005F', "loz;": '\U000025CA', "lozenge;": '\U000025CA', "lozf;": '\U000029EB', "lpar;": '\U00000028', "lparlt;": '\U00002993', "lrarr;": '\U000021C6', "lrcorner;": '\U0000231F', "lrhar;": '\U000021CB', "lrhard;": '\U0000296D', "lrm;": '\U0000200E', "lrtri;": '\U000022BF', "lsaquo;": '\U00002039', "lscr;": '\U0001D4C1', "lsh;": '\U000021B0', "lsim;": '\U00002272', "lsime;": '\U00002A8D', "lsimg;": '\U00002A8F', "lsqb;": '\U0000005B', "lsquo;": '\U00002018', "lsquor;": '\U0000201A', "lstrok;": '\U00000142', "lt;": '\U0000003C', "ltcc;": '\U00002AA6', "ltcir;": '\U00002A79', "ltdot;": '\U000022D6', "lthree;": '\U000022CB', "ltimes;": '\U000022C9', "ltlarr;": '\U00002976', "ltquest;": '\U00002A7B', "ltrPar;": '\U00002996', "ltri;": '\U000025C3', "ltrie;": '\U000022B4', "ltrif;": '\U000025C2', "lurdshar;": '\U0000294A', "luruhar;": '\U00002966', "mDDot;": '\U0000223A', "macr;": '\U000000AF', "male;": '\U00002642', "malt;": '\U00002720', "maltese;": '\U00002720', "map;": '\U000021A6', "mapsto;": '\U000021A6', "mapstodown;": '\U000021A7', "mapstoleft;": '\U000021A4', "mapstoup;": '\U000021A5', "marker;": '\U000025AE', "mcomma;": '\U00002A29', "mcy;": '\U0000043C', "mdash;": '\U00002014', "measuredangle;": '\U00002221', "mfr;": '\U0001D52A', "mho;": '\U00002127', "micro;": '\U000000B5', "mid;": '\U00002223', "midast;": '\U0000002A', "midcir;": '\U00002AF0', "middot;": '\U000000B7', "minus;": '\U00002212', "minusb;": '\U0000229F', "minusd;": '\U00002238', "minusdu;": '\U00002A2A', "mlcp;": '\U00002ADB', "mldr;": '\U00002026', "mnplus;": '\U00002213', "models;": '\U000022A7', "mopf;": '\U0001D55E', "mp;": '\U00002213', "mscr;": '\U0001D4C2', "mstpos;": '\U0000223E', "mu;": '\U000003BC', "multimap;": '\U000022B8', "mumap;": '\U000022B8', "nLeftarrow;": '\U000021CD', "nLeftrightarrow;": '\U000021CE', "nRightarrow;": '\U000021CF', "nVDash;": '\U000022AF', "nVdash;": '\U000022AE', "nabla;": '\U00002207', "nacute;": '\U00000144', "nap;": '\U00002249', "napos;": '\U00000149', "napprox;": '\U00002249', "natur;": '\U0000266E', "natural;": '\U0000266E', "naturals;": '\U00002115', "nbsp;": '\U000000A0', "ncap;": '\U00002A43', "ncaron;": '\U00000148', "ncedil;": '\U00000146', "ncong;": '\U00002247', "ncup;": '\U00002A42', "ncy;": '\U0000043D', "ndash;": '\U00002013', "ne;": '\U00002260', "neArr;": '\U000021D7', "nearhk;": '\U00002924', "nearr;": '\U00002197', "nearrow;": '\U00002197', "nequiv;": '\U00002262', "nesear;": '\U00002928', "nexist;": '\U00002204', "nexists;": '\U00002204', "nfr;": '\U0001D52B', "nge;": '\U00002271', "ngeq;": '\U00002271', "ngsim;": '\U00002275', "ngt;": '\U0000226F', "ngtr;": '\U0000226F', "nhArr;": '\U000021CE', "nharr;": '\U000021AE', "nhpar;": '\U00002AF2', "ni;": '\U0000220B', "nis;": '\U000022FC', "nisd;": '\U000022FA', "niv;": '\U0000220B', "njcy;": '\U0000045A', "nlArr;": '\U000021CD', "nlarr;": '\U0000219A', "nldr;": '\U00002025', "nle;": '\U00002270', "nleftarrow;": '\U0000219A', "nleftrightarrow;": '\U000021AE', "nleq;": '\U00002270', "nless;": '\U0000226E', "nlsim;": '\U00002274', "nlt;": '\U0000226E', "nltri;": '\U000022EA', "nltrie;": '\U000022EC', "nmid;": '\U00002224', "nopf;": '\U0001D55F', "not;": '\U000000AC', "notin;": '\U00002209', "notinva;": '\U00002209', "notinvb;": '\U000022F7', "notinvc;": '\U000022F6', "notni;": '\U0000220C', "notniva;": '\U0000220C', "notnivb;": '\U000022FE', "notnivc;": '\U000022FD', "npar;": '\U00002226', "nparallel;": '\U00002226', "npolint;": '\U00002A14', "npr;": '\U00002280', "nprcue;": '\U000022E0', "nprec;": '\U00002280', "nrArr;": '\U000021CF', "nrarr;": '\U0000219B', "nrightarrow;": '\U0000219B', "nrtri;": '\U000022EB', "nrtrie;": '\U000022ED', "nsc;": '\U00002281', "nsccue;": '\U000022E1', "nscr;": '\U0001D4C3', "nshortmid;": '\U00002224', "nshortparallel;": '\U00002226', "nsim;": '\U00002241', "nsime;": '\U00002244', "nsimeq;": '\U00002244', "nsmid;": '\U00002224', "nspar;": '\U00002226', "nsqsube;": '\U000022E2', "nsqsupe;": '\U000022E3', "nsub;": '\U00002284', "nsube;": '\U00002288', "nsubseteq;": '\U00002288', "nsucc;": '\U00002281', "nsup;": '\U00002285', "nsupe;": '\U00002289', "nsupseteq;": '\U00002289', "ntgl;": '\U00002279', "ntilde;": '\U000000F1', "ntlg;": '\U00002278', "ntriangleleft;": '\U000022EA', "ntrianglelefteq;": '\U000022EC', "ntriangleright;": '\U000022EB', "ntrianglerighteq;": '\U000022ED', "nu;": '\U000003BD', "num;": '\U00000023', "numero;": '\U00002116', "numsp;": '\U00002007', "nvDash;": '\U000022AD', "nvHarr;": '\U00002904', "nvdash;": '\U000022AC', "nvinfin;": '\U000029DE', "nvlArr;": '\U00002902', "nvrArr;": '\U00002903', "nwArr;": '\U000021D6', "nwarhk;": '\U00002923', "nwarr;": '\U00002196', "nwarrow;": '\U00002196', "nwnear;": '\U00002927', "oS;": '\U000024C8', "oacute;": '\U000000F3', "oast;": '\U0000229B', "ocir;": '\U0000229A', "ocirc;": '\U000000F4', "ocy;": '\U0000043E', "odash;": '\U0000229D', "odblac;": '\U00000151', "odiv;": '\U00002A38', "odot;": '\U00002299', "odsold;": '\U000029BC', "oelig;": '\U00000153', "ofcir;": '\U000029BF', "ofr;": '\U0001D52C', "ogon;": '\U000002DB', "ograve;": '\U000000F2', "ogt;": '\U000029C1', "ohbar;": '\U000029B5', "ohm;": '\U000003A9', "oint;": '\U0000222E', "olarr;": '\U000021BA', "olcir;": '\U000029BE', "olcross;": '\U000029BB', "oline;": '\U0000203E', "olt;": '\U000029C0', "omacr;": '\U0000014D', "omega;": '\U000003C9', "omicron;": '\U000003BF', "omid;": '\U000029B6', "ominus;": '\U00002296', "oopf;": '\U0001D560', "opar;": '\U000029B7', "operp;": '\U000029B9', "oplus;": '\U00002295', "or;": '\U00002228', "orarr;": '\U000021BB', "ord;": '\U00002A5D', "order;": '\U00002134', "orderof;": '\U00002134', "ordf;": '\U000000AA', "ordm;": '\U000000BA', "origof;": '\U000022B6', "oror;": '\U00002A56', "orslope;": '\U00002A57', "orv;": '\U00002A5B', "oscr;": '\U00002134', "oslash;": '\U000000F8', "osol;": '\U00002298', "otilde;": '\U000000F5', "otimes;": '\U00002297', "otimesas;": '\U00002A36', "ouml;": '\U000000F6', "ovbar;": '\U0000233D', "par;": '\U00002225', "para;": '\U000000B6', "parallel;": '\U00002225', "parsim;": '\U00002AF3', "parsl;": '\U00002AFD', "part;": '\U00002202', "pcy;": '\U0000043F', "percnt;": '\U00000025', "period;": '\U0000002E', "permil;": '\U00002030', "perp;": '\U000022A5', "pertenk;": '\U00002031', "pfr;": '\U0001D52D', "phi;": '\U000003C6', "phiv;": '\U000003D5', "phmmat;": '\U00002133', "phone;": '\U0000260E', "pi;": '\U000003C0', "pitchfork;": '\U000022D4', "piv;": '\U000003D6', "planck;": '\U0000210F', "planckh;": '\U0000210E', "plankv;": '\U0000210F', "plus;": '\U0000002B', "plusacir;": '\U00002A23', "plusb;": '\U0000229E', "pluscir;": '\U00002A22', "plusdo;": '\U00002214', "plusdu;": '\U00002A25', "pluse;": '\U00002A72', "plusmn;": '\U000000B1', "plussim;": '\U00002A26', "plustwo;": '\U00002A27', "pm;": '\U000000B1', "pointint;": '\U00002A15', "popf;": '\U0001D561', "pound;": '\U000000A3', "pr;": '\U0000227A', "prE;": '\U00002AB3', "prap;": '\U00002AB7', "prcue;": '\U0000227C', "pre;": '\U00002AAF', "prec;": '\U0000227A', "precapprox;": '\U00002AB7', "preccurlyeq;": '\U0000227C', "preceq;": '\U00002AAF', "precnapprox;": '\U00002AB9', "precneqq;": '\U00002AB5', "precnsim;": '\U000022E8', "precsim;": '\U0000227E', "prime;": '\U00002032', "primes;": '\U00002119', "prnE;": '\U00002AB5', "prnap;": '\U00002AB9', "prnsim;": '\U000022E8', "prod;": '\U0000220F', "profalar;": '\U0000232E', "profline;": '\U00002312', "profsurf;": '\U00002313', "prop;": '\U0000221D', "propto;": '\U0000221D', "prsim;": '\U0000227E', "prurel;": '\U000022B0', "pscr;": '\U0001D4C5', "psi;": '\U000003C8', "puncsp;": '\U00002008', "qfr;": '\U0001D52E', "qint;": '\U00002A0C', "qopf;": '\U0001D562', "qprime;": '\U00002057', "qscr;": '\U0001D4C6', "quaternions;": '\U0000210D', "quatint;": '\U00002A16', "quest;": '\U0000003F', "questeq;": '\U0000225F', "quot;": '\U00000022', "rAarr;": '\U000021DB', "rArr;": '\U000021D2', "rAtail;": '\U0000291C', "rBarr;": '\U0000290F', "rHar;": '\U00002964', "racute;": '\U00000155', "radic;": '\U0000221A', "raemptyv;": '\U000029B3', "rang;": '\U000027E9', "rangd;": '\U00002992', "range;": '\U000029A5', "rangle;": '\U000027E9', "raquo;": '\U000000BB', "rarr;": '\U00002192', "rarrap;": '\U00002975', "rarrb;": '\U000021E5', "rarrbfs;": '\U00002920', "rarrc;": '\U00002933', "rarrfs;": '\U0000291E', "rarrhk;": '\U000021AA', "rarrlp;": '\U000021AC', "rarrpl;": '\U00002945', "rarrsim;": '\U00002974', "rarrtl;": '\U000021A3', "rarrw;": '\U0000219D', "ratail;": '\U0000291A', "ratio;": '\U00002236', "rationals;": '\U0000211A', "rbarr;": '\U0000290D', "rbbrk;": '\U00002773', "rbrace;": '\U0000007D', "rbrack;": '\U0000005D', "rbrke;": '\U0000298C', "rbrksld;": '\U0000298E', "rbrkslu;": '\U00002990', "rcaron;": '\U00000159', "rcedil;": '\U00000157', "rceil;": '\U00002309', "rcub;": '\U0000007D', "rcy;": '\U00000440', "rdca;": '\U00002937', "rdldhar;": '\U00002969', "rdquo;": '\U0000201D', "rdquor;": '\U0000201D', "rdsh;": '\U000021B3', "real;": '\U0000211C', "realine;": '\U0000211B', "realpart;": '\U0000211C', "reals;": '\U0000211D', "rect;": '\U000025AD', "reg;": '\U000000AE', "rfisht;": '\U0000297D', "rfloor;": '\U0000230B', "rfr;": '\U0001D52F', "rhard;": '\U000021C1', "rharu;": '\U000021C0', "rharul;": '\U0000296C', "rho;": '\U000003C1', "rhov;": '\U000003F1', "rightarrow;": '\U00002192', "rightarrowtail;": '\U000021A3', "rightharpoondown;": '\U000021C1', "rightharpoonup;": '\U000021C0', "rightleftarrows;": '\U000021C4', "rightleftharpoons;": '\U000021CC', "rightrightarrows;": '\U000021C9', "rightsquigarrow;": '\U0000219D', "rightthreetimes;": '\U000022CC', "ring;": '\U000002DA', "risingdotseq;": '\U00002253', "rlarr;": '\U000021C4', "rlhar;": '\U000021CC', "rlm;": '\U0000200F', "rmoust;": '\U000023B1', "rmoustache;": '\U000023B1', "rnmid;": '\U00002AEE', "roang;": '\U000027ED', "roarr;": '\U000021FE', "robrk;": '\U000027E7', "ropar;": '\U00002986', "ropf;": '\U0001D563', "roplus;": '\U00002A2E', "rotimes;": '\U00002A35', "rpar;": '\U00000029', "rpargt;": '\U00002994', "rppolint;": '\U00002A12', "rrarr;": '\U000021C9', "rsaquo;": '\U0000203A', "rscr;": '\U0001D4C7', "rsh;": '\U000021B1', "rsqb;": '\U0000005D', "rsquo;": '\U00002019', "rsquor;": '\U00002019', "rthree;": '\U000022CC', "rtimes;": '\U000022CA', "rtri;": '\U000025B9', "rtrie;": '\U000022B5', "rtrif;": '\U000025B8', "rtriltri;": '\U000029CE', "ruluhar;": '\U00002968', "rx;": '\U0000211E', "sacute;": '\U0000015B', "sbquo;": '\U0000201A', "sc;": '\U0000227B', "scE;": '\U00002AB4', "scap;": '\U00002AB8', "scaron;": '\U00000161', "sccue;": '\U0000227D', "sce;": '\U00002AB0', "scedil;": '\U0000015F', "scirc;": '\U0000015D', "scnE;": '\U00002AB6', "scnap;": '\U00002ABA', "scnsim;": '\U000022E9', "scpolint;": '\U00002A13', "scsim;": '\U0000227F', "scy;": '\U00000441', "sdot;": '\U000022C5', "sdotb;": '\U000022A1', "sdote;": '\U00002A66', "seArr;": '\U000021D8', "searhk;": '\U00002925', "searr;": '\U00002198', "searrow;": '\U00002198', "sect;": '\U000000A7', "semi;": '\U0000003B', "seswar;": '\U00002929', "setminus;": '\U00002216', "setmn;": '\U00002216', "sext;": '\U00002736', "sfr;": '\U0001D530', "sfrown;": '\U00002322', "sharp;": '\U0000266F', "shchcy;": '\U00000449', "shcy;": '\U00000448', "shortmid;": '\U00002223', "shortparallel;": '\U00002225', "shy;": '\U000000AD', "sigma;": '\U000003C3', "sigmaf;": '\U000003C2', "sigmav;": '\U000003C2', "sim;": '\U0000223C', "simdot;": '\U00002A6A', "sime;": '\U00002243', "simeq;": '\U00002243', "simg;": '\U00002A9E', "simgE;": '\U00002AA0', "siml;": '\U00002A9D', "simlE;": '\U00002A9F', "simne;": '\U00002246', "simplus;": '\U00002A24', "simrarr;": '\U00002972', "slarr;": '\U00002190', "smallsetminus;": '\U00002216', "smashp;": '\U00002A33', "smeparsl;": '\U000029E4', "smid;": '\U00002223', "smile;": '\U00002323', "smt;": '\U00002AAA', "smte;": '\U00002AAC', "softcy;": '\U0000044C', "sol;": '\U0000002F', "solb;": '\U000029C4', "solbar;": '\U0000233F', "sopf;": '\U0001D564', "spades;": '\U00002660', "spadesuit;": '\U00002660', "spar;": '\U00002225', "sqcap;": '\U00002293', "sqcup;": '\U00002294', "sqsub;": '\U0000228F', "sqsube;": '\U00002291', "sqsubset;": '\U0000228F', "sqsubseteq;": '\U00002291', "sqsup;": '\U00002290', "sqsupe;": '\U00002292', "sqsupset;": '\U00002290', "sqsupseteq;": '\U00002292', "squ;": '\U000025A1', "square;": '\U000025A1', "squarf;": '\U000025AA', "squf;": '\U000025AA', "srarr;": '\U00002192', "sscr;": '\U0001D4C8', "ssetmn;": '\U00002216', "ssmile;": '\U00002323', "sstarf;": '\U000022C6', "star;": '\U00002606', "starf;": '\U00002605', "straightepsilon;": '\U000003F5', "straightphi;": '\U000003D5', "strns;": '\U000000AF', "sub;": '\U00002282', "subE;": '\U00002AC5', "subdot;": '\U00002ABD', "sube;": '\U00002286', "subedot;": '\U00002AC3', "submult;": '\U00002AC1', "subnE;": '\U00002ACB', "subne;": '\U0000228A', "subplus;": '\U00002ABF', "subrarr;": '\U00002979', "subset;": '\U00002282', "subseteq;": '\U00002286', "subseteqq;": '\U00002AC5', "subsetneq;": '\U0000228A', "subsetneqq;": '\U00002ACB', "subsim;": '\U00002AC7', "subsub;": '\U00002AD5', "subsup;": '\U00002AD3', "succ;": '\U0000227B', "succapprox;": '\U00002AB8', "succcurlyeq;": '\U0000227D', "succeq;": '\U00002AB0', "succnapprox;": '\U00002ABA', "succneqq;": '\U00002AB6', "succnsim;": '\U000022E9', "succsim;": '\U0000227F', "sum;": '\U00002211', "sung;": '\U0000266A', "sup;": '\U00002283', "sup1;": '\U000000B9', "sup2;": '\U000000B2', "sup3;": '\U000000B3', "supE;": '\U00002AC6', "supdot;": '\U00002ABE', "supdsub;": '\U00002AD8', "supe;": '\U00002287', "supedot;": '\U00002AC4', "suphsol;": '\U000027C9', "suphsub;": '\U00002AD7', "suplarr;": '\U0000297B', "supmult;": '\U00002AC2', "supnE;": '\U00002ACC', "supne;": '\U0000228B', "supplus;": '\U00002AC0', "supset;": '\U00002283', "supseteq;": '\U00002287', "supseteqq;": '\U00002AC6', "supsetneq;": '\U0000228B', "supsetneqq;": '\U00002ACC', "supsim;": '\U00002AC8', "supsub;": '\U00002AD4', "supsup;": '\U00002AD6', "swArr;": '\U000021D9', "swarhk;": '\U00002926', "swarr;": '\U00002199', "swarrow;": '\U00002199', "swnwar;": '\U0000292A', "szlig;": '\U000000DF', "target;": '\U00002316', "tau;": '\U000003C4', "tbrk;": '\U000023B4', "tcaron;": '\U00000165', "tcedil;": '\U00000163', "tcy;": '\U00000442', "tdot;": '\U000020DB', "telrec;": '\U00002315', "tfr;": '\U0001D531', "there4;": '\U00002234', "therefore;": '\U00002234', "theta;": '\U000003B8', "thetasym;": '\U000003D1', "thetav;": '\U000003D1', "thickapprox;": '\U00002248', "thicksim;": '\U0000223C', "thinsp;": '\U00002009', "thkap;": '\U00002248', "thksim;": '\U0000223C', "thorn;": '\U000000FE', "tilde;": '\U000002DC', "times;": '\U000000D7', "timesb;": '\U000022A0', "timesbar;": '\U00002A31', "timesd;": '\U00002A30', "tint;": '\U0000222D', "toea;": '\U00002928', "top;": '\U000022A4', "topbot;": '\U00002336', "topcir;": '\U00002AF1', "topf;": '\U0001D565', "topfork;": '\U00002ADA', "tosa;": '\U00002929', "tprime;": '\U00002034', "trade;": '\U00002122', "triangle;": '\U000025B5', "triangledown;": '\U000025BF', "triangleleft;": '\U000025C3', "trianglelefteq;": '\U000022B4', "triangleq;": '\U0000225C', "triangleright;": '\U000025B9', "trianglerighteq;": '\U000022B5', "tridot;": '\U000025EC', "trie;": '\U0000225C', "triminus;": '\U00002A3A', "triplus;": '\U00002A39', "trisb;": '\U000029CD', "tritime;": '\U00002A3B', "trpezium;": '\U000023E2', "tscr;": '\U0001D4C9', "tscy;": '\U00000446', "tshcy;": '\U0000045B', "tstrok;": '\U00000167', "twixt;": '\U0000226C', "twoheadleftarrow;": '\U0000219E', "twoheadrightarrow;": '\U000021A0', "uArr;": '\U000021D1', "uHar;": '\U00002963', "uacute;": '\U000000FA', "uarr;": '\U00002191', "ubrcy;": '\U0000045E', "ubreve;": '\U0000016D', "ucirc;": '\U000000FB', "ucy;": '\U00000443', "udarr;": '\U000021C5', "udblac;": '\U00000171', "udhar;": '\U0000296E', "ufisht;": '\U0000297E', "ufr;": '\U0001D532', "ugrave;": '\U000000F9', "uharl;": '\U000021BF', "uharr;": '\U000021BE', "uhblk;": '\U00002580', "ulcorn;": '\U0000231C', "ulcorner;": '\U0000231C', "ulcrop;": '\U0000230F', "ultri;": '\U000025F8', "umacr;": '\U0000016B', "uml;": '\U000000A8', "uogon;": '\U00000173', "uopf;": '\U0001D566', "uparrow;": '\U00002191', "updownarrow;": '\U00002195', "upharpoonleft;": '\U000021BF', "upharpoonright;": '\U000021BE', "uplus;": '\U0000228E', "upsi;": '\U000003C5', "upsih;": '\U000003D2', "upsilon;": '\U000003C5', "upuparrows;": '\U000021C8', "urcorn;": '\U0000231D', "urcorner;": '\U0000231D', "urcrop;": '\U0000230E', "uring;": '\U0000016F', "urtri;": '\U000025F9', "uscr;": '\U0001D4CA', "utdot;": '\U000022F0', "utilde;": '\U00000169', "utri;": '\U000025B5', "utrif;": '\U000025B4', "uuarr;": '\U000021C8', "uuml;": '\U000000FC', "uwangle;": '\U000029A7', "vArr;": '\U000021D5', "vBar;": '\U00002AE8', "vBarv;": '\U00002AE9', "vDash;": '\U000022A8', "vangrt;": '\U0000299C', "varepsilon;": '\U000003F5', "varkappa;": '\U000003F0', "varnothing;": '\U00002205', "varphi;": '\U000003D5', "varpi;": '\U000003D6', "varpropto;": '\U0000221D', "varr;": '\U00002195', "varrho;": '\U000003F1', "varsigma;": '\U000003C2', "vartheta;": '\U000003D1', "vartriangleleft;": '\U000022B2', "vartriangleright;": '\U000022B3', "vcy;": '\U00000432', "vdash;": '\U000022A2', "vee;": '\U00002228', "veebar;": '\U000022BB', "veeeq;": '\U0000225A', "vellip;": '\U000022EE', "verbar;": '\U0000007C', "vert;": '\U0000007C', "vfr;": '\U0001D533', "vltri;": '\U000022B2', "vopf;": '\U0001D567', "vprop;": '\U0000221D', "vrtri;": '\U000022B3', "vscr;": '\U0001D4CB', "vzigzag;": '\U0000299A', "wcirc;": '\U00000175', "wedbar;": '\U00002A5F', "wedge;": '\U00002227', "wedgeq;": '\U00002259', "weierp;": '\U00002118', "wfr;": '\U0001D534', "wopf;": '\U0001D568', "wp;": '\U00002118', "wr;": '\U00002240', "wreath;": '\U00002240', "wscr;": '\U0001D4CC', "xcap;": '\U000022C2', "xcirc;": '\U000025EF', "xcup;": '\U000022C3', "xdtri;": '\U000025BD', "xfr;": '\U0001D535', "xhArr;": '\U000027FA', "xharr;": '\U000027F7', "xi;": '\U000003BE', "xlArr;": '\U000027F8', "xlarr;": '\U000027F5', "xmap;": '\U000027FC', "xnis;": '\U000022FB', "xodot;": '\U00002A00', "xopf;": '\U0001D569', "xoplus;": '\U00002A01', "xotime;": '\U00002A02', "xrArr;": '\U000027F9', "xrarr;": '\U000027F6', "xscr;": '\U0001D4CD', "xsqcup;": '\U00002A06', "xuplus;": '\U00002A04', "xutri;": '\U000025B3', "xvee;": '\U000022C1', "xwedge;": '\U000022C0', "yacute;": '\U000000FD', "yacy;": '\U0000044F', "ycirc;": '\U00000177', "ycy;": '\U0000044B', "yen;": '\U000000A5', "yfr;": '\U0001D536', "yicy;": '\U00000457', "yopf;": '\U0001D56A', "yscr;": '\U0001D4CE', "yucy;": '\U0000044E', "yuml;": '\U000000FF', "zacute;": '\U0000017A', "zcaron;": '\U0000017E', "zcy;": '\U00000437', "zdot;": '\U0000017C', "zeetrf;": '\U00002128', "zeta;": '\U000003B6', "zfr;": '\U0001D537', "zhcy;": '\U00000436', "zigrarr;": '\U000021DD', "zopf;": '\U0001D56B', "zscr;": '\U0001D4CF', "zwj;": '\U0000200D', "zwnj;": '\U0000200C', "AElig": '\U000000C6', "AMP": '\U00000026', "Aacute": '\U000000C1', "Acirc": '\U000000C2', "Agrave": '\U000000C0', "Aring": '\U000000C5', "Atilde": '\U000000C3', "Auml": '\U000000C4', "COPY": '\U000000A9', "Ccedil": '\U000000C7', "ETH": '\U000000D0', "Eacute": '\U000000C9', "Ecirc": '\U000000CA', "Egrave": '\U000000C8', "Euml": '\U000000CB', "GT": '\U0000003E', "Iacute": '\U000000CD', "Icirc": '\U000000CE', "Igrave": '\U000000CC', "Iuml": '\U000000CF', "LT": '\U0000003C', "Ntilde": '\U000000D1', "Oacute": '\U000000D3', "Ocirc": '\U000000D4', "Ograve": '\U000000D2', "Oslash": '\U000000D8', "Otilde": '\U000000D5', "Ouml": '\U000000D6', "QUOT": '\U00000022', "REG": '\U000000AE', "THORN": '\U000000DE', "Uacute": '\U000000DA', "Ucirc": '\U000000DB', "Ugrave": '\U000000D9', "Uuml": '\U000000DC', "Yacute": '\U000000DD', "aacute": '\U000000E1', "acirc": '\U000000E2', "acute": '\U000000B4', "aelig": '\U000000E6', "agrave": '\U000000E0', "amp": '\U00000026', "aring": '\U000000E5', "atilde": '\U000000E3', "auml": '\U000000E4', "brvbar": '\U000000A6', "ccedil": '\U000000E7', "cedil": '\U000000B8', "cent": '\U000000A2', "copy": '\U000000A9', "curren": '\U000000A4', "deg": '\U000000B0', "divide": '\U000000F7', "eacute": '\U000000E9', "ecirc": '\U000000EA', "egrave": '\U000000E8', "eth": '\U000000F0', "euml": '\U000000EB', "frac12": '\U000000BD', "frac14": '\U000000BC', "frac34": '\U000000BE', "gt": '\U0000003E', "iacute": '\U000000ED', "icirc": '\U000000EE', "iexcl": '\U000000A1', "igrave": '\U000000EC', "iquest": '\U000000BF', "iuml": '\U000000EF', "laquo": '\U000000AB', "lt": '\U0000003C', "macr": '\U000000AF', "micro": '\U000000B5', "middot": '\U000000B7', "nbsp": '\U000000A0', "not": '\U000000AC', "ntilde": '\U000000F1', "oacute": '\U000000F3', "ocirc": '\U000000F4', "ograve": '\U000000F2', "ordf": '\U000000AA', "ordm": '\U000000BA', "oslash": '\U000000F8', "otilde": '\U000000F5', "ouml": '\U000000F6', "para": '\U000000B6', "plusmn": '\U000000B1', "pound": '\U000000A3', "quot": '\U00000022', "raquo": '\U000000BB', "reg": '\U000000AE', "sect": '\U000000A7', "shy": '\U000000AD', "sup1": '\U000000B9', "sup2": '\U000000B2', "sup3": '\U000000B3', "szlig": '\U000000DF', "thorn": '\U000000FE', "times": '\U000000D7', "uacute": '\U000000FA', "ucirc": '\U000000FB', "ugrave": '\U000000F9', "uml": '\U000000A8', "uuml": '\U000000FC', "yacute": '\U000000FD', "yen": '\U000000A5', "yuml": '\U000000FF', } // HTML entities that are two unicode codepoints. var entity2 = map[string][2]rune{ // TODO(nigeltao): Handle replacements that are wider than their names. // "nLt;": {'\u226A', '\u20D2'}, // "nGt;": {'\u226B', '\u20D2'}, "NotEqualTilde;": {'\u2242', '\u0338'}, "NotGreaterFullEqual;": {'\u2267', '\u0338'}, "NotGreaterGreater;": {'\u226B', '\u0338'}, "NotGreaterSlantEqual;": {'\u2A7E', '\u0338'}, "NotHumpDownHump;": {'\u224E', '\u0338'}, "NotHumpEqual;": {'\u224F', '\u0338'}, "NotLeftTriangleBar;": {'\u29CF', '\u0338'}, "NotLessLess;": {'\u226A', '\u0338'}, "NotLessSlantEqual;": {'\u2A7D', '\u0338'}, "NotNestedGreaterGreater;": {'\u2AA2', '\u0338'}, "NotNestedLessLess;": {'\u2AA1', '\u0338'}, "NotPrecedesEqual;": {'\u2AAF', '\u0338'}, "NotRightTriangleBar;": {'\u29D0', '\u0338'}, "NotSquareSubset;": {'\u228F', '\u0338'}, "NotSquareSuperset;": {'\u2290', '\u0338'}, "NotSubset;": {'\u2282', '\u20D2'}, "NotSucceedsEqual;": {'\u2AB0', '\u0338'}, "NotSucceedsTilde;": {'\u227F', '\u0338'}, "NotSuperset;": {'\u2283', '\u20D2'}, "ThickSpace;": {'\u205F', '\u200A'}, "acE;": {'\u223E', '\u0333'}, "bne;": {'\u003D', '\u20E5'}, "bnequiv;": {'\u2261', '\u20E5'}, "caps;": {'\u2229', '\uFE00'}, "cups;": {'\u222A', '\uFE00'}, "fjlig;": {'\u0066', '\u006A'}, "gesl;": {'\u22DB', '\uFE00'}, "gvertneqq;": {'\u2269', '\uFE00'}, "gvnE;": {'\u2269', '\uFE00'}, "lates;": {'\u2AAD', '\uFE00'}, "lesg;": {'\u22DA', '\uFE00'}, "lvertneqq;": {'\u2268', '\uFE00'}, "lvnE;": {'\u2268', '\uFE00'}, "nGg;": {'\u22D9', '\u0338'}, "nGtv;": {'\u226B', '\u0338'}, "nLl;": {'\u22D8', '\u0338'}, "nLtv;": {'\u226A', '\u0338'}, "nang;": {'\u2220', '\u20D2'}, "napE;": {'\u2A70', '\u0338'}, "napid;": {'\u224B', '\u0338'}, "nbump;": {'\u224E', '\u0338'}, "nbumpe;": {'\u224F', '\u0338'}, "ncongdot;": {'\u2A6D', '\u0338'}, "nedot;": {'\u2250', '\u0338'}, "nesim;": {'\u2242', '\u0338'}, "ngE;": {'\u2267', '\u0338'}, "ngeqq;": {'\u2267', '\u0338'}, "ngeqslant;": {'\u2A7E', '\u0338'}, "nges;": {'\u2A7E', '\u0338'}, "nlE;": {'\u2266', '\u0338'}, "nleqq;": {'\u2266', '\u0338'}, "nleqslant;": {'\u2A7D', '\u0338'}, "nles;": {'\u2A7D', '\u0338'}, "notinE;": {'\u22F9', '\u0338'}, "notindot;": {'\u22F5', '\u0338'}, "nparsl;": {'\u2AFD', '\u20E5'}, "npart;": {'\u2202', '\u0338'}, "npre;": {'\u2AAF', '\u0338'}, "npreceq;": {'\u2AAF', '\u0338'}, "nrarrc;": {'\u2933', '\u0338'}, "nrarrw;": {'\u219D', '\u0338'}, "nsce;": {'\u2AB0', '\u0338'}, "nsubE;": {'\u2AC5', '\u0338'}, "nsubset;": {'\u2282', '\u20D2'}, "nsubseteqq;": {'\u2AC5', '\u0338'}, "nsucceq;": {'\u2AB0', '\u0338'}, "nsupE;": {'\u2AC6', '\u0338'}, "nsupset;": {'\u2283', '\u20D2'}, "nsupseteqq;": {'\u2AC6', '\u0338'}, "nvap;": {'\u224D', '\u20D2'}, "nvge;": {'\u2265', '\u20D2'}, "nvgt;": {'\u003E', '\u20D2'}, "nvle;": {'\u2264', '\u20D2'}, "nvlt;": {'\u003C', '\u20D2'}, "nvltrie;": {'\u22B4', '\u20D2'}, "nvrtrie;": {'\u22B5', '\u20D2'}, "nvsim;": {'\u223C', '\u20D2'}, "race;": {'\u223D', '\u0331'}, "smtes;": {'\u2AAC', '\uFE00'}, "sqcaps;": {'\u2293', '\uFE00'}, "sqcups;": {'\u2294', '\uFE00'}, "varsubsetneq;": {'\u228A', '\uFE00'}, "varsubsetneqq;": {'\u2ACB', '\uFE00'}, "varsupsetneq;": {'\u228B', '\uFE00'}, "varsupsetneqq;": {'\u2ACC', '\uFE00'}, "vnsub;": {'\u2282', '\u20D2'}, "vnsup;": {'\u2283', '\u20D2'}, "vsubnE;": {'\u2ACB', '\uFE00'}, "vsubne;": {'\u228A', '\uFE00'}, "vsupnE;": {'\u2ACC', '\uFE00'}, "vsupne;": {'\u228B', '\uFE00'}, } ================================================ FILE: vendor/golang.org/x/net/html/escape.go ================================================ // Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package html import ( "bytes" "strings" "unicode/utf8" ) // These replacements permit compatibility with old numeric entities that // assumed Windows-1252 encoding. // https://html.spec.whatwg.org/multipage/syntax.html#consume-a-character-reference var replacementTable = [...]rune{ '\u20AC', // First entry is what 0x80 should be replaced with. '\u0081', '\u201A', '\u0192', '\u201E', '\u2026', '\u2020', '\u2021', '\u02C6', '\u2030', '\u0160', '\u2039', '\u0152', '\u008D', '\u017D', '\u008F', '\u0090', '\u2018', '\u2019', '\u201C', '\u201D', '\u2022', '\u2013', '\u2014', '\u02DC', '\u2122', '\u0161', '\u203A', '\u0153', '\u009D', '\u017E', '\u0178', // Last entry is 0x9F. // 0x00->'\uFFFD' is handled programmatically. // 0x0D->'\u000D' is a no-op. } // unescapeEntity reads an entity like "<" from b[src:] and writes the // corresponding "<" to b[dst:], returning the incremented dst and src cursors. // Precondition: b[src] == '&' && dst <= src. // attribute should be true if parsing an attribute value. func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { // https://html.spec.whatwg.org/multipage/syntax.html#consume-a-character-reference // i starts at 1 because we already know that s[0] == '&'. i, s := 1, b[src:] if len(s) <= 1 { b[dst] = b[src] return dst + 1, src + 1 } if s[i] == '#' { if len(s) <= 3 { // We need to have at least "&#.". b[dst] = b[src] return dst + 1, src + 1 } i++ c := s[i] hex := false if c == 'x' || c == 'X' { hex = true i++ } x := '\x00' for i < len(s) { c = s[i] i++ if hex { if '0' <= c && c <= '9' { x = 16*x + rune(c) - '0' continue } else if 'a' <= c && c <= 'f' { x = 16*x + rune(c) - 'a' + 10 continue } else if 'A' <= c && c <= 'F' { x = 16*x + rune(c) - 'A' + 10 continue } } else if '0' <= c && c <= '9' { x = 10*x + rune(c) - '0' continue } if c != ';' { i-- } break } if i <= 3 { // No characters matched. b[dst] = b[src] return dst + 1, src + 1 } if 0x80 <= x && x <= 0x9F { // Replace characters from Windows-1252 with UTF-8 equivalents. x = replacementTable[x-0x80] } else if x == 0 || (0xD800 <= x && x <= 0xDFFF) || x > 0x10FFFF { // Replace invalid characters with the replacement character. x = '\uFFFD' } return dst + utf8.EncodeRune(b[dst:], x), src + i } // Consume the maximum number of characters possible, with the // consumed characters matching one of the named references. for i < len(s) { c := s[i] i++ // Lower-cased characters are more common in entities, so we check for them first. if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' { continue } if c != ';' { i-- } break } entityName := string(s[1:i]) if entityName == "" { // No-op. } else if attribute && entityName[len(entityName)-1] != ';' && len(s) > i && s[i] == '=' { // No-op. } else if x := entity[entityName]; x != 0 { return dst + utf8.EncodeRune(b[dst:], x), src + i } else if x := entity2[entityName]; x[0] != 0 { dst1 := dst + utf8.EncodeRune(b[dst:], x[0]) return dst1 + utf8.EncodeRune(b[dst1:], x[1]), src + i } else if !attribute { maxLen := len(entityName) - 1 if maxLen > longestEntityWithoutSemicolon { maxLen = longestEntityWithoutSemicolon } for j := maxLen; j > 1; j-- { if x := entity[entityName[:j]]; x != 0 { return dst + utf8.EncodeRune(b[dst:], x), src + j + 1 } } } dst1, src1 = dst+i, src+i copy(b[dst:dst1], b[src:src1]) return dst1, src1 } // unescape unescapes b's entities in-place, so that "a<b" becomes "a' byte that, per above, we'd like to avoid escaping unless we have to. // // Studying the summary table (and T actions in its '>' column) closely, we // only need to escape in states 43, 44, 49, 51 and 52. State 43 is at the // start of the comment data. State 52 is after a '!'. The other three states // are after a '-'. // // Our algorithm is thus to escape every '&' and to escape '>' if and only if: // - The '>' is after a '!' or '-' (in the unescaped data) or // - The '>' is at the start of the comment data (after the opening ""); err != nil { return err } return nil case DoctypeNode: if _, err := w.WriteString("') case RawNode: _, err := w.WriteString(n.Data) return err default: return errors.New("html: unknown node type") } // Render the opening tag. if err := w.WriteByte('<'); err != nil { return err } if _, err := w.WriteString(n.Data); err != nil { return err } for _, a := range n.Attr { if err := w.WriteByte(' '); err != nil { return err } if a.Namespace != "" { if _, err := w.WriteString(a.Namespace); err != nil { return err } if err := w.WriteByte(':'); err != nil { return err } } if _, err := w.WriteString(a.Key); err != nil { return err } if _, err := w.WriteString(`="`); err != nil { return err } if err := escape(w, a.Val); err != nil { return err } if err := w.WriteByte('"'); err != nil { return err } } if voidElements[n.Data] { if n.FirstChild != nil { return fmt.Errorf("html: void element <%s> has child nodes", n.Data) } _, err := w.WriteString("/>") return err } if err := w.WriteByte('>'); err != nil { return err } // Add initial newline where there is danger of a newline being ignored. if c := n.FirstChild; c != nil && c.Type == TextNode && strings.HasPrefix(c.Data, "\n") { switch n.Data { case "pre", "listing", "textarea": if err := w.WriteByte('\n'); err != nil { return err } } } // Render any child nodes if childTextNodesAreLiteral(n) { for c := n.FirstChild; c != nil; c = c.NextSibling { if c.Type == TextNode { if _, err := w.WriteString(c.Data); err != nil { return err } } else { if err := render1(w, c); err != nil { return err } } } if n.Data == "plaintext" { // Don't render anything else. must be the // last element in the file, with no closing tag. return plaintextAbort } } else { for c := n.FirstChild; c != nil; c = c.NextSibling { if err := render1(w, c); err != nil { return err } } } // Render the </xxx> closing tag. if _, err := w.WriteString("</"); err != nil { return err } if _, err := w.WriteString(n.Data); err != nil { return err } return w.WriteByte('>') } func childTextNodesAreLiteral(n *Node) bool { // Per WHATWG HTML 13.3, if the parent of the current node is a style, // script, xmp, iframe, noembed, noframes, or plaintext element, and the // current node is a text node, append the value of the node's data // literally. The specification is not explicit about it, but we only // enforce this if we are in the HTML namespace (i.e. when the namespace is // ""). // NOTE: we also always include noscript elements, although the // specification states that they should only be rendered as such if // scripting is enabled for the node (which is not something we track). if n.Namespace != "" { return false } switch n.Data { case "iframe", "noembed", "noframes", "noscript", "plaintext", "script", "style", "xmp": return true default: return false } } // writeQuoted writes s to w surrounded by quotes. Normally it will use double // quotes, but if s contains a double quote, it will use single quotes. // It is used for writing the identifiers in a doctype declaration. // In valid HTML, they can't contain both types of quotes. func writeQuoted(w writer, s string) error { var q byte = '"' if strings.Contains(s, `"`) { q = '\'' } if err := w.WriteByte(q); err != nil { return err } if _, err := w.WriteString(s); err != nil { return err } if err := w.WriteByte(q); err != nil { return err } return nil } // Section 12.1.2, "Elements", gives this list of void elements. Void elements // are those that can't have any contents. var voidElements = map[string]bool{ "area": true, "base": true, "br": true, "col": true, "embed": true, "hr": true, "img": true, "input": true, "keygen": true, // "keygen" has been removed from the spec, but are kept here for backwards compatibility. "link": true, "meta": true, "param": true, "source": true, "track": true, "wbr": true, } ================================================ FILE: vendor/golang.org/x/net/html/token.go ================================================ // Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package html import ( "bytes" "errors" "io" "strconv" "strings" "golang.org/x/net/html/atom" ) // A TokenType is the type of a Token. type TokenType uint32 const ( // ErrorToken means that an error occurred during tokenization. ErrorToken TokenType = iota // TextToken means a text node. TextToken // A StartTagToken looks like <a>. StartTagToken // An EndTagToken looks like </a>. EndTagToken // A SelfClosingTagToken tag looks like <br/>. SelfClosingTagToken // A CommentToken looks like <!--x-->. CommentToken // A DoctypeToken looks like <!DOCTYPE x> DoctypeToken ) // ErrBufferExceeded means that the buffering limit was exceeded. var ErrBufferExceeded = errors.New("max buffer exceeded") // String returns a string representation of the TokenType. func (t TokenType) String() string { switch t { case ErrorToken: return "Error" case TextToken: return "Text" case StartTagToken: return "StartTag" case EndTagToken: return "EndTag" case SelfClosingTagToken: return "SelfClosingTag" case CommentToken: return "Comment" case DoctypeToken: return "Doctype" } return "Invalid(" + strconv.Itoa(int(t)) + ")" } // An Attribute is an attribute namespace-key-value triple. Namespace is // non-empty for foreign attributes like xlink, Key is alphabetic (and hence // does not contain escapable characters like '&', '<' or '>'), and Val is // unescaped (it looks like "a<b" rather than "a&lt;b"). // // Namespace is only used by the parser, not the tokenizer. type Attribute struct { Namespace, Key, Val string } // A Token consists of a TokenType and some Data (tag name for start and end // tags, content for text, comments and doctypes). A tag Token may also contain // a slice of Attributes. Data is unescaped for all Tokens (it looks like "a<b" // rather than "a&lt;b"). For tag Tokens, DataAtom is the atom for Data, or // zero if Data is not a known tag name. type Token struct { Type TokenType DataAtom atom.Atom Data string Attr []Attribute } // tagString returns a string representation of a tag Token's Data and Attr. func (t Token) tagString() string { if len(t.Attr) == 0 { return t.Data } buf := bytes.NewBufferString(t.Data) for _, a := range t.Attr { buf.WriteByte(' ') buf.WriteString(a.Key) buf.WriteString(`="`) escape(buf, a.Val) buf.WriteByte('"') } return buf.String() } // String returns a string representation of the Token. func (t Token) String() string { switch t.Type { case ErrorToken: return "" case TextToken: return EscapeString(t.Data) case StartTagToken: return "<" + t.tagString() + ">" case EndTagToken: return "</" + t.tagString() + ">" case SelfClosingTagToken: return "<" + t.tagString() + "/>" case CommentToken: return "<!--" + escapeCommentString(t.Data) + "-->" case DoctypeToken: return "<!DOCTYPE " + EscapeString(t.Data) + ">" } return "Invalid(" + strconv.Itoa(int(t.Type)) + ")" } // span is a range of bytes in a Tokenizer's buffer. The start is inclusive, // the end is exclusive. type span struct { start, end int } // A Tokenizer returns a stream of HTML Tokens. type Tokenizer struct { // r is the source of the HTML text. r io.Reader // tt is the TokenType of the current token. tt TokenType // err is the first error encountered during tokenization. It is possible // for tt != Error && err != nil to hold: this means that Next returned a // valid token but the subsequent Next call will return an error token. // For example, if the HTML text input was just "plain", then the first // Next call would set z.err to io.EOF but return a TextToken, and all // subsequent Next calls would return an ErrorToken. // err is never reset. Once it becomes non-nil, it stays non-nil. err error // readErr is the error returned by the io.Reader r. It is separate from // err because it is valid for an io.Reader to return (n int, err1 error) // such that n > 0 && err1 != nil, and callers should always process the // n > 0 bytes before considering the error err1. readErr error // buf[raw.start:raw.end] holds the raw bytes of the current token. // buf[raw.end:] is buffered input that will yield future tokens. raw span buf []byte // maxBuf limits the data buffered in buf. A value of 0 means unlimited. maxBuf int // buf[data.start:data.end] holds the raw bytes of the current token's data: // a text token's text, a tag token's tag name, etc. data span // pendingAttr is the attribute key and value currently being tokenized. // When complete, pendingAttr is pushed onto attr. nAttrReturned is // incremented on each call to TagAttr. pendingAttr [2]span attr [][2]span nAttrReturned int // rawTag is the "script" in "</script>" that closes the next token. If // non-empty, the subsequent call to Next will return a raw or RCDATA text // token: one that treats "<p>" as text instead of an element. // rawTag's contents are lower-cased. rawTag string // textIsRaw is whether the current text token's data is not escaped. textIsRaw bool // convertNUL is whether NUL bytes in the current token's data should // be converted into \ufffd replacement characters. convertNUL bool // allowCDATA is whether CDATA sections are allowed in the current context. allowCDATA bool } // AllowCDATA sets whether or not the tokenizer recognizes <![CDATA[foo]]> as // the text "foo". The default value is false, which means to recognize it as // a bogus comment "<!-- [CDATA[foo]] -->" instead. // // Strictly speaking, an HTML5 compliant tokenizer should allow CDATA if and // only if tokenizing foreign content, such as MathML and SVG. However, // tracking foreign-contentness is difficult to do purely in the tokenizer, // as opposed to the parser, due to HTML integration points: an <svg> element // can contain a <foreignObject> that is foreign-to-SVG but not foreign-to- // HTML. For strict compliance with the HTML5 tokenization algorithm, it is the // responsibility of the user of a tokenizer to call AllowCDATA as appropriate. // In practice, if using the tokenizer without caring whether MathML or SVG // CDATA is text or comments, such as tokenizing HTML to find all the anchor // text, it is acceptable to ignore this responsibility. func (z *Tokenizer) AllowCDATA(allowCDATA bool) { z.allowCDATA = allowCDATA } // NextIsNotRawText instructs the tokenizer that the next token should not be // considered as 'raw text'. Some elements, such as script and title elements, // normally require the next token after the opening tag to be 'raw text' that // has no child elements. For example, tokenizing "<title>a<b>c</b>d</title>" // yields a start tag token for "<title>", a text token for "a<b>c</b>d", and // an end tag token for "</title>". There are no distinct start tag or end tag // tokens for the "<b>" and "</b>". // // This tokenizer implementation will generally look for raw text at the right // times. Strictly speaking, an HTML5 compliant tokenizer should not look for // raw text if in foreign content: <title> generally needs raw text, but a // <title> inside an <svg> does not. Another example is that a <textarea> // generally needs raw text, but a <textarea> is not allowed as an immediate // child of a <select>; in normal parsing, a <textarea> implies </select>, but // one cannot close the implicit element when parsing a <select>'s InnerHTML. // Similarly to AllowCDATA, tracking the correct moment to override raw-text- // ness is difficult to do purely in the tokenizer, as opposed to the parser. // For strict compliance with the HTML5 tokenization algorithm, it is the // responsibility of the user of a tokenizer to call NextIsNotRawText as // appropriate. In practice, like AllowCDATA, it is acceptable to ignore this // responsibility for basic usage. // // Note that this 'raw text' concept is different from the one offered by the // Tokenizer.Raw method. func (z *Tokenizer) NextIsNotRawText() { z.rawTag = "" } // Err returns the error associated with the most recent ErrorToken token. // This is typically io.EOF, meaning the end of tokenization. func (z *Tokenizer) Err() error { if z.tt != ErrorToken { return nil } return z.err } // readByte returns the next byte from the input stream, doing a buffered read // from z.r into z.buf if necessary. z.buf[z.raw.start:z.raw.end] remains a contiguous byte // slice that holds all the bytes read so far for the current token. // It sets z.err if the underlying reader returns an error. // Pre-condition: z.err == nil. func (z *Tokenizer) readByte() byte { if z.raw.end >= len(z.buf) { // Our buffer is exhausted and we have to read from z.r. Check if the // previous read resulted in an error. if z.readErr != nil { z.err = z.readErr return 0 } // We copy z.buf[z.raw.start:z.raw.end] to the beginning of z.buf. If the length // z.raw.end - z.raw.start is more than half the capacity of z.buf, then we // allocate a new buffer before the copy. c := cap(z.buf) d := z.raw.end - z.raw.start var buf1 []byte if 2*d > c { buf1 = make([]byte, d, 2*c) } else { buf1 = z.buf[:d] } copy(buf1, z.buf[z.raw.start:z.raw.end]) if x := z.raw.start; x != 0 { // Adjust the data/attr spans to refer to the same contents after the copy. z.data.start -= x z.data.end -= x z.pendingAttr[0].start -= x z.pendingAttr[0].end -= x z.pendingAttr[1].start -= x z.pendingAttr[1].end -= x for i := range z.attr { z.attr[i][0].start -= x z.attr[i][0].end -= x z.attr[i][1].start -= x z.attr[i][1].end -= x } } z.raw.start, z.raw.end, z.buf = 0, d, buf1[:d] // Now that we have copied the live bytes to the start of the buffer, // we read from z.r into the remainder. var n int n, z.readErr = readAtLeastOneByte(z.r, buf1[d:cap(buf1)]) if n == 0 { z.err = z.readErr return 0 } z.buf = buf1[:d+n] } x := z.buf[z.raw.end] z.raw.end++ if z.maxBuf > 0 && z.raw.end-z.raw.start >= z.maxBuf { z.err = ErrBufferExceeded return 0 } return x } // Buffered returns a slice containing data buffered but not yet tokenized. func (z *Tokenizer) Buffered() []byte { return z.buf[z.raw.end:] } // readAtLeastOneByte wraps an io.Reader so that reading cannot return (0, nil). // It returns io.ErrNoProgress if the underlying r.Read method returns (0, nil) // too many times in succession. func readAtLeastOneByte(r io.Reader, b []byte) (int, error) { for i := 0; i < 100; i++ { if n, err := r.Read(b); n != 0 || err != nil { return n, err } } return 0, io.ErrNoProgress } // skipWhiteSpace skips past any white space. func (z *Tokenizer) skipWhiteSpace() { if z.err != nil { return } for { c := z.readByte() if z.err != nil { return } switch c { case ' ', '\n', '\r', '\t', '\f': // No-op. default: z.raw.end-- return } } } // readRawOrRCDATA reads until the next "</foo>", where "foo" is z.rawTag and // is typically something like "script" or "textarea". func (z *Tokenizer) readRawOrRCDATA() { if z.rawTag == "script" { z.readScript() z.textIsRaw = true z.rawTag = "" return } loop: for { c := z.readByte() if z.err != nil { break loop } if c != '<' { continue loop } c = z.readByte() if z.err != nil { break loop } if c != '/' { z.raw.end-- continue loop } if z.readRawEndTag() || z.err != nil { break loop } } z.data.end = z.raw.end // A textarea's or title's RCDATA can contain escaped entities. z.textIsRaw = z.rawTag != "textarea" && z.rawTag != "title" z.rawTag = "" } // readRawEndTag attempts to read a tag like "</foo>", where "foo" is z.rawTag. // If it succeeds, it backs up the input position to reconsume the tag and // returns true. Otherwise it returns false. The opening "</" has already been // consumed. func (z *Tokenizer) readRawEndTag() bool { for i := 0; i < len(z.rawTag); i++ { c := z.readByte() if z.err != nil { return false } if c != z.rawTag[i] && c != z.rawTag[i]-('a'-'A') { z.raw.end-- return false } } c := z.readByte() if z.err != nil { return false } switch c { case ' ', '\n', '\r', '\t', '\f', '/', '>': // The 3 is 2 for the leading "</" plus 1 for the trailing character c. z.raw.end -= 3 + len(z.rawTag) return true } z.raw.end-- return false } // readScript reads until the next </script> tag, following the byzantine // rules for escaping/hiding the closing tag. func (z *Tokenizer) readScript() { defer func() { z.data.end = z.raw.end }() var c byte scriptData: c = z.readByte() if z.err != nil { return } if c == '<' { goto scriptDataLessThanSign } goto scriptData scriptDataLessThanSign: c = z.readByte() if z.err != nil { return } switch c { case '/': goto scriptDataEndTagOpen case '!': goto scriptDataEscapeStart } z.raw.end-- goto scriptData scriptDataEndTagOpen: if z.readRawEndTag() || z.err != nil { return } goto scriptData scriptDataEscapeStart: c = z.readByte() if z.err != nil { return } if c == '-' { goto scriptDataEscapeStartDash } z.raw.end-- goto scriptData scriptDataEscapeStartDash: c = z.readByte() if z.err != nil { return } if c == '-' { goto scriptDataEscapedDashDash } z.raw.end-- goto scriptData scriptDataEscaped: c = z.readByte() if z.err != nil { return } switch c { case '-': goto scriptDataEscapedDash case '<': goto scriptDataEscapedLessThanSign } goto scriptDataEscaped scriptDataEscapedDash: c = z.readByte() if z.err != nil { return } switch c { case '-': goto scriptDataEscapedDashDash case '<': goto scriptDataEscapedLessThanSign } goto scriptDataEscaped scriptDataEscapedDashDash: c = z.readByte() if z.err != nil { return } switch c { case '-': goto scriptDataEscapedDashDash case '<': goto scriptDataEscapedLessThanSign case '>': goto scriptData } goto scriptDataEscaped scriptDataEscapedLessThanSign: c = z.readByte() if z.err != nil { return } if c == '/' { goto scriptDataEscapedEndTagOpen } if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' { goto scriptDataDoubleEscapeStart } z.raw.end-- goto scriptData scriptDataEscapedEndTagOpen: if z.readRawEndTag() || z.err != nil { return } goto scriptDataEscaped scriptDataDoubleEscapeStart: z.raw.end-- for i := 0; i < len("script"); i++ { c = z.readByte() if z.err != nil { return } if c != "script"[i] && c != "SCRIPT"[i] { z.raw.end-- goto scriptDataEscaped } } c = z.readByte() if z.err != nil { return } switch c { case ' ', '\n', '\r', '\t', '\f', '/', '>': goto scriptDataDoubleEscaped } z.raw.end-- goto scriptDataEscaped scriptDataDoubleEscaped: c = z.readByte() if z.err != nil { return } switch c { case '-': goto scriptDataDoubleEscapedDash case '<': goto scriptDataDoubleEscapedLessThanSign } goto scriptDataDoubleEscaped scriptDataDoubleEscapedDash: c = z.readByte() if z.err != nil { return } switch c { case '-': goto scriptDataDoubleEscapedDashDash case '<': goto scriptDataDoubleEscapedLessThanSign } goto scriptDataDoubleEscaped scriptDataDoubleEscapedDashDash: c = z.readByte() if z.err != nil { return } switch c { case '-': goto scriptDataDoubleEscapedDashDash case '<': goto scriptDataDoubleEscapedLessThanSign case '>': goto scriptData } goto scriptDataDoubleEscaped scriptDataDoubleEscapedLessThanSign: c = z.readByte() if z.err != nil { return } if c == '/' { goto scriptDataDoubleEscapeEnd } z.raw.end-- goto scriptDataDoubleEscaped scriptDataDoubleEscapeEnd: if z.readRawEndTag() { z.raw.end += len("</script>") goto scriptDataEscaped } if z.err != nil { return } goto scriptDataDoubleEscaped } // readComment reads the next comment token starting with "<!--". The opening // "<!--" has already been consumed. func (z *Tokenizer) readComment() { // When modifying this function, consider manually increasing the // maxSuffixLen constant in func TestComments, from 6 to e.g. 9 or more. // That increase should only be temporary, not committed, as it // exponentially affects the test running time. z.data.start = z.raw.end defer func() { if z.data.end < z.data.start { // It's a comment with no data, like <!-->. z.data.end = z.data.start } }() var dashCount int beginning := true for { c := z.readByte() if z.err != nil { z.data.end = z.calculateAbruptCommentDataEnd() return } switch c { case '-': dashCount++ continue case '>': if dashCount >= 2 || beginning { z.data.end = z.raw.end - len("-->") return } case '!': if dashCount >= 2 { c = z.readByte() if z.err != nil { z.data.end = z.calculateAbruptCommentDataEnd() return } else if c == '>' { z.data.end = z.raw.end - len("--!>") return } else if c == '-' { dashCount = 1 beginning = false continue } } } dashCount = 0 beginning = false } } func (z *Tokenizer) calculateAbruptCommentDataEnd() int { raw := z.Raw() const prefixLen = len("<!--") if len(raw) >= prefixLen { raw = raw[prefixLen:] if hasSuffix(raw, "--!") { return z.raw.end - 3 } else if hasSuffix(raw, "--") { return z.raw.end - 2 } else if hasSuffix(raw, "-") { return z.raw.end - 1 } } return z.raw.end } func hasSuffix(b []byte, suffix string) bool { if len(b) < len(suffix) { return false } b = b[len(b)-len(suffix):] for i := range b { if b[i] != suffix[i] { return false } } return true } // readUntilCloseAngle reads until the next ">". func (z *Tokenizer) readUntilCloseAngle() { z.data.start = z.raw.end for { c := z.readByte() if z.err != nil { z.data.end = z.raw.end return } if c == '>' { z.data.end = z.raw.end - len(">") return } } } // readMarkupDeclaration reads the next token starting with "<!". It might be // a "<!--comment-->", a "<!DOCTYPE foo>", a "<![CDATA[section]]>" or // "<!a bogus comment". The opening "<!" has already been consumed. func (z *Tokenizer) readMarkupDeclaration() TokenType { z.data.start = z.raw.end var c [2]byte for i := 0; i < 2; i++ { c[i] = z.readByte() if z.err != nil { z.data.end = z.raw.end return CommentToken } } if c[0] == '-' && c[1] == '-' { z.readComment() return CommentToken } z.raw.end -= 2 if z.readDoctype() { return DoctypeToken } if z.allowCDATA && z.readCDATA() { z.convertNUL = true return TextToken } // It's a bogus comment. z.readUntilCloseAngle() return CommentToken } // readDoctype attempts to read a doctype declaration and returns true if // successful. The opening "<!" has already been consumed. func (z *Tokenizer) readDoctype() bool { const s = "DOCTYPE" for i := 0; i < len(s); i++ { c := z.readByte() if z.err != nil { z.data.end = z.raw.end return false } if c != s[i] && c != s[i]+('a'-'A') { // Back up to read the fragment of "DOCTYPE" again. z.raw.end = z.data.start return false } } if z.skipWhiteSpace(); z.err != nil { z.data.start = z.raw.end z.data.end = z.raw.end return true } z.readUntilCloseAngle() return true } // readCDATA attempts to read a CDATA section and returns true if // successful. The opening "<!" has already been consumed. func (z *Tokenizer) readCDATA() bool { const s = "[CDATA[" for i := 0; i < len(s); i++ { c := z.readByte() if z.err != nil { z.data.end = z.raw.end return false } if c != s[i] { // Back up to read the fragment of "[CDATA[" again. z.raw.end = z.data.start return false } } z.data.start = z.raw.end brackets := 0 for { c := z.readByte() if z.err != nil { z.data.end = z.raw.end return true } switch c { case ']': brackets++ case '>': if brackets >= 2 { z.data.end = z.raw.end - len("]]>") return true } brackets = 0 default: brackets = 0 } } } // startTagIn returns whether the start tag in z.buf[z.data.start:z.data.end] // case-insensitively matches any element of ss. func (z *Tokenizer) startTagIn(ss ...string) bool { loop: for _, s := range ss { if z.data.end-z.data.start != len(s) { continue loop } for i := 0; i < len(s); i++ { c := z.buf[z.data.start+i] if 'A' <= c && c <= 'Z' { c += 'a' - 'A' } if c != s[i] { continue loop } } return true } return false } // readStartTag reads the next start tag token. The opening "<a" has already // been consumed, where 'a' means anything in [A-Za-z]. func (z *Tokenizer) readStartTag() TokenType { z.readTag(true) if z.err != nil { return ErrorToken } // Several tags flag the tokenizer's next token as raw. c, raw := z.buf[z.data.start], false if 'A' <= c && c <= 'Z' { c += 'a' - 'A' } switch c { case 'i': raw = z.startTagIn("iframe") case 'n': raw = z.startTagIn("noembed", "noframes", "noscript") case 'p': raw = z.startTagIn("plaintext") case 's': raw = z.startTagIn("script", "style") case 't': raw = z.startTagIn("textarea", "title") case 'x': raw = z.startTagIn("xmp") } if raw { z.rawTag = strings.ToLower(string(z.buf[z.data.start:z.data.end])) } // Look for a self-closing token (e.g. <br/>). // // Originally, we did this by just checking that the last character of the // tag (ignoring the closing bracket) was a solidus (/) character, but this // is not always accurate. // // We need to be careful that we don't misinterpret a non-self-closing tag // as self-closing, as can happen if the tag contains unquoted attribute // values (i.e. <p a=/>). // // To avoid this, we check that the last non-bracket character of the tag // (z.raw.end-2) isn't the same character as the last non-quote character of // the last attribute of the tag (z.pendingAttr[1].end-1), if the tag has // attributes. nAttrs := len(z.attr) if z.err == nil && z.buf[z.raw.end-2] == '/' && (nAttrs == 0 || z.raw.end-2 != z.attr[nAttrs-1][1].end-1) { return SelfClosingTagToken } return StartTagToken } // readTag reads the next tag token and its attributes. If saveAttr, those // attributes are saved in z.attr, otherwise z.attr is set to an empty slice. // The opening "<a" or "</a" has already been consumed, where 'a' means anything // in [A-Za-z]. func (z *Tokenizer) readTag(saveAttr bool) { z.attr = z.attr[:0] z.nAttrReturned = 0 // Read the tag name and attribute key/value pairs. z.readTagName() if z.skipWhiteSpace(); z.err != nil { return } for { c := z.readByte() if z.err != nil || c == '>' { break } z.raw.end-- z.readTagAttrKey() z.readTagAttrVal() // Save pendingAttr if saveAttr and that attribute has a non-empty key. if saveAttr && z.pendingAttr[0].start != z.pendingAttr[0].end { z.attr = append(z.attr, z.pendingAttr) } if z.skipWhiteSpace(); z.err != nil { break } } } // readTagName sets z.data to the "div" in "<div k=v>". The reader (z.raw.end) // is positioned such that the first byte of the tag name (the "d" in "<div") // has already been consumed. func (z *Tokenizer) readTagName() { z.data.start = z.raw.end - 1 for { c := z.readByte() if z.err != nil { z.data.end = z.raw.end return } switch c { case ' ', '\n', '\r', '\t', '\f': z.data.end = z.raw.end - 1 return case '/', '>': z.raw.end-- z.data.end = z.raw.end return } } } // readTagAttrKey sets z.pendingAttr[0] to the "k" in "<div k=v>". // Precondition: z.err == nil. func (z *Tokenizer) readTagAttrKey() { z.pendingAttr[0].start = z.raw.end for { c := z.readByte() if z.err != nil { z.pendingAttr[0].end = z.raw.end return } switch c { case '=': if z.pendingAttr[0].start+1 == z.raw.end { // WHATWG 13.2.5.32, if we see an equals sign before the attribute name // begins, we treat it as a character in the attribute name and continue. continue } fallthrough case ' ', '\n', '\r', '\t', '\f', '/', '>': // WHATWG 13.2.5.33 Attribute name state // We need to reconsume the char in the after attribute name state to support the / character z.raw.end-- z.pendingAttr[0].end = z.raw.end return } } } // readTagAttrVal sets z.pendingAttr[1] to the "v" in "<div k=v>". func (z *Tokenizer) readTagAttrVal() { z.pendingAttr[1].start = z.raw.end z.pendingAttr[1].end = z.raw.end if z.skipWhiteSpace(); z.err != nil { return } c := z.readByte() if z.err != nil { return } if c == '/' { // WHATWG 13.2.5.34 After attribute name state // U+002F SOLIDUS (/) - Switch to the self-closing start tag state. return } if c != '=' { z.raw.end-- return } if z.skipWhiteSpace(); z.err != nil { return } quote := z.readByte() if z.err != nil { return } switch quote { case '>': z.raw.end-- return case '\'', '"': z.pendingAttr[1].start = z.raw.end for { c := z.readByte() if z.err != nil { z.pendingAttr[1].end = z.raw.end return } if c == quote { z.pendingAttr[1].end = z.raw.end - 1 return } } default: z.pendingAttr[1].start = z.raw.end - 1 for { c := z.readByte() if z.err != nil { z.pendingAttr[1].end = z.raw.end return } switch c { case ' ', '\n', '\r', '\t', '\f': z.pendingAttr[1].end = z.raw.end - 1 return case '>': z.raw.end-- z.pendingAttr[1].end = z.raw.end return } } } } // Next scans the next token and returns its type. func (z *Tokenizer) Next() TokenType { z.raw.start = z.raw.end z.data.start = z.raw.end z.data.end = z.raw.end if z.err != nil { z.tt = ErrorToken return z.tt } if z.rawTag != "" { if z.rawTag == "plaintext" { // Read everything up to EOF. for z.err == nil { z.readByte() } z.data.end = z.raw.end z.textIsRaw = true } else { z.readRawOrRCDATA() } if z.data.end > z.data.start { z.tt = TextToken z.convertNUL = true return z.tt } } z.textIsRaw = false z.convertNUL = false loop: for { c := z.readByte() if z.err != nil { break loop } if c != '<' { continue loop } // Check if the '<' we have just read is part of a tag, comment // or doctype. If not, it's part of the accumulated text token. c = z.readByte() if z.err != nil { break loop } var tokenType TokenType switch { case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z': tokenType = StartTagToken case c == '/': tokenType = EndTagToken case c == '!' || c == '?': // We use CommentToken to mean any of "<!--actual comments-->", // "<!DOCTYPE declarations>" and "<?xml processing instructions?>". tokenType = CommentToken default: // Reconsume the current character. z.raw.end-- continue } // We have a non-text token, but we might have accumulated some text // before that. If so, we return the text first, and return the non- // text token on the subsequent call to Next. if x := z.raw.end - len("<a"); z.raw.start < x { z.raw.end = x z.data.end = x z.tt = TextToken return z.tt } switch tokenType { case StartTagToken: z.tt = z.readStartTag() return z.tt case EndTagToken: c = z.readByte() if z.err != nil { break loop } if c == '>' { // "</>" does not generate a token at all. Generate an empty comment // to allow passthrough clients to pick up the data using Raw. // Reset the tokenizer state and start again. z.tt = CommentToken return z.tt } if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' { z.readTag(false) if z.err != nil { z.tt = ErrorToken } else { z.tt = EndTagToken } return z.tt } z.raw.end-- z.readUntilCloseAngle() z.tt = CommentToken return z.tt case CommentToken: if c == '!' { z.tt = z.readMarkupDeclaration() return z.tt } z.raw.end-- z.readUntilCloseAngle() z.tt = CommentToken return z.tt } } if z.raw.start < z.raw.end { z.data.end = z.raw.end z.tt = TextToken return z.tt } z.tt = ErrorToken return z.tt } // Raw returns the unmodified text of the current token. Calling Next, Token, // Text, TagName or TagAttr may change the contents of the returned slice. // // The token stream's raw bytes partition the byte stream (up until an // ErrorToken). There are no overlaps or gaps between two consecutive token's // raw bytes. One implication is that the byte offset of the current token is // the sum of the lengths of all previous tokens' raw bytes. func (z *Tokenizer) Raw() []byte { return z.buf[z.raw.start:z.raw.end] } // convertNewlines converts "\r" and "\r\n" in s to "\n". // The conversion happens in place, but the resulting slice may be shorter. func convertNewlines(s []byte) []byte { for i, c := range s { if c != '\r' { continue } src := i + 1 if src >= len(s) || s[src] != '\n' { s[i] = '\n' continue } dst := i for src < len(s) { if s[src] == '\r' { if src+1 < len(s) && s[src+1] == '\n' { src++ } s[dst] = '\n' } else { s[dst] = s[src] } src++ dst++ } return s[:dst] } return s } var ( nul = []byte("\x00") replacement = []byte("\ufffd") ) // Text returns the unescaped text of a text, comment or doctype token. The // contents of the returned slice may change on the next call to Next. func (z *Tokenizer) Text() []byte { switch z.tt { case TextToken, CommentToken, DoctypeToken: s := z.buf[z.data.start:z.data.end] z.data.start = z.raw.end z.data.end = z.raw.end s = convertNewlines(s) if (z.convertNUL || z.tt == CommentToken) && bytes.Contains(s, nul) { s = bytes.Replace(s, nul, replacement, -1) } if !z.textIsRaw { s = unescape(s, false) } return s } return nil } // TagName returns the lower-cased name of a tag token (the `img` out of // `<IMG SRC="foo">`) and whether the tag has attributes. // The contents of the returned slice may change on the next call to Next. func (z *Tokenizer) TagName() (name []byte, hasAttr bool) { if z.data.start < z.data.end { switch z.tt { case StartTagToken, EndTagToken, SelfClosingTagToken: s := z.buf[z.data.start:z.data.end] z.data.start = z.raw.end z.data.end = z.raw.end return lower(s), z.nAttrReturned < len(z.attr) } } return nil, false } // TagAttr returns the lower-cased key and unescaped value of the next unparsed // attribute for the current tag token and whether there are more attributes. // The contents of the returned slices may change on the next call to Next. func (z *Tokenizer) TagAttr() (key, val []byte, moreAttr bool) { if z.nAttrReturned < len(z.attr) { switch z.tt { case StartTagToken, SelfClosingTagToken: x := z.attr[z.nAttrReturned] z.nAttrReturned++ key = z.buf[x[0].start:x[0].end] val = z.buf[x[1].start:x[1].end] return lower(key), unescape(convertNewlines(val), true), z.nAttrReturned < len(z.attr) } } return nil, nil, false } // Token returns the current Token. The result's Data and Attr values remain // valid after subsequent Next calls. func (z *Tokenizer) Token() Token { t := Token{Type: z.tt} switch z.tt { case TextToken, CommentToken, DoctypeToken: t.Data = string(z.Text()) case StartTagToken, SelfClosingTagToken, EndTagToken: name, moreAttr := z.TagName() for moreAttr { var key, val []byte key, val, moreAttr = z.TagAttr() t.Attr = append(t.Attr, Attribute{"", atom.String(key), string(val)}) } if a := atom.Lookup(name); a != 0 { t.DataAtom, t.Data = a, a.String() } else { t.DataAtom, t.Data = 0, string(name) } } return t } // SetMaxBuf sets a limit on the amount of data buffered during tokenization. // A value of 0 means unlimited. func (z *Tokenizer) SetMaxBuf(n int) { z.maxBuf = n } // NewTokenizer returns a new HTML Tokenizer for the given Reader. // The input is assumed to be UTF-8 encoded. func NewTokenizer(r io.Reader) *Tokenizer { return NewTokenizerFragment(r, "") } // NewTokenizerFragment returns a new HTML Tokenizer for the given Reader, for // tokenizing an existing element's InnerHTML fragment. contextTag is that // element's tag, such as "div" or "iframe". // // For example, how the InnerHTML "a<b" is tokenized depends on whether it is // for a <p> tag or a <script> tag. // // The input is assumed to be UTF-8 encoded. func NewTokenizerFragment(r io.Reader, contextTag string) *Tokenizer { z := &Tokenizer{ r: r, buf: make([]byte, 0, 4096), } if contextTag != "" { switch s := strings.ToLower(contextTag); s { case "iframe", "noembed", "noframes", "noscript", "plaintext", "script", "style", "title", "textarea", "xmp": z.rawTag = s } } return z } ================================================ FILE: vendor/golang.org/x/net/http/httpguts/guts.go ================================================ // Copyright 2018 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package httpguts provides functions implementing various details // of the HTTP specification. // // This package is shared by the standard library (which vendors it) // and x/net/http2. It comes with no API stability promise. package httpguts import ( "net/textproto" "strings" ) // ValidTrailerHeader reports whether name is a valid header field name to appear // in trailers. // See RFC 7230, Section 4.1.2 func ValidTrailerHeader(name string) bool { name = textproto.CanonicalMIMEHeaderKey(name) if strings.HasPrefix(name, "If-") || badTrailer[name] { return false } return true } var badTrailer = map[string]bool{ "Authorization": true, "Cache-Control": true, "Connection": true, "Content-Encoding": true, "Content-Length": true, "Content-Range": true, "Content-Type": true, "Expect": true, "Host": true, "Keep-Alive": true, "Max-Forwards": true, "Pragma": true, "Proxy-Authenticate": true, "Proxy-Authorization": true, "Proxy-Connection": true, "Range": true, "Realm": true, "Te": true, "Trailer": true, "Transfer-Encoding": true, "Www-Authenticate": true, } ================================================ FILE: vendor/golang.org/x/net/http/httpguts/httplex.go ================================================ // Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package httpguts import ( "net" "strings" "unicode/utf8" "golang.org/x/net/idna" ) var isTokenTable = [256]bool{ '!': true, '#': true, '$': true, '%': true, '&': true, '\'': true, '*': true, '+': true, '-': true, '.': true, '0': true, '1': true, '2': true, '3': true, '4': true, '5': true, '6': true, '7': true, '8': true, '9': true, 'A': true, 'B': true, 'C': true, 'D': true, 'E': true, 'F': true, 'G': true, 'H': true, 'I': true, 'J': true, 'K': true, 'L': true, 'M': true, 'N': true, 'O': true, 'P': true, 'Q': true, 'R': true, 'S': true, 'T': true, 'U': true, 'W': true, 'V': true, 'X': true, 'Y': true, 'Z': true, '^': true, '_': true, '`': true, 'a': true, 'b': true, 'c': true, 'd': true, 'e': true, 'f': true, 'g': true, 'h': true, 'i': true, 'j': true, 'k': true, 'l': true, 'm': true, 'n': true, 'o': true, 'p': true, 'q': true, 'r': true, 's': true, 't': true, 'u': true, 'v': true, 'w': true, 'x': true, 'y': true, 'z': true, '|': true, '~': true, } func IsTokenRune(r rune) bool { return r < utf8.RuneSelf && isTokenTable[byte(r)] } // HeaderValuesContainsToken reports whether any string in values // contains the provided token, ASCII case-insensitively. func HeaderValuesContainsToken(values []string, token string) bool { for _, v := range values { if headerValueContainsToken(v, token) { return true } } return false } // isOWS reports whether b is an optional whitespace byte, as defined // by RFC 7230 section 3.2.3. func isOWS(b byte) bool { return b == ' ' || b == '\t' } // trimOWS returns x with all optional whitespace removes from the // beginning and end. func trimOWS(x string) string { // TODO: consider using strings.Trim(x, " \t") instead, // if and when it's fast enough. See issue 10292. // But this ASCII-only code will probably always beat UTF-8 // aware code. for len(x) > 0 && isOWS(x[0]) { x = x[1:] } for len(x) > 0 && isOWS(x[len(x)-1]) { x = x[:len(x)-1] } return x } // headerValueContainsToken reports whether v (assumed to be a // 0#element, in the ABNF extension described in RFC 7230 section 7) // contains token amongst its comma-separated tokens, ASCII // case-insensitively. func headerValueContainsToken(v string, token string) bool { for comma := strings.IndexByte(v, ','); comma != -1; comma = strings.IndexByte(v, ',') { if tokenEqual(trimOWS(v[:comma]), token) { return true } v = v[comma+1:] } return tokenEqual(trimOWS(v), token) } // lowerASCII returns the ASCII lowercase version of b. func lowerASCII(b byte) byte { if 'A' <= b && b <= 'Z' { return b + ('a' - 'A') } return b } // tokenEqual reports whether t1 and t2 are equal, ASCII case-insensitively. func tokenEqual(t1, t2 string) bool { if len(t1) != len(t2) { return false } for i, b := range t1 { if b >= utf8.RuneSelf { // No UTF-8 or non-ASCII allowed in tokens. return false } if lowerASCII(byte(b)) != lowerASCII(t2[i]) { return false } } return true } // isLWS reports whether b is linear white space, according // to http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2 // // LWS = [CRLF] 1*( SP | HT ) func isLWS(b byte) bool { return b == ' ' || b == '\t' } // isCTL reports whether b is a control byte, according // to http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2 // // CTL = <any US-ASCII control character // (octets 0 - 31) and DEL (127)> func isCTL(b byte) bool { const del = 0x7f // a CTL return b < ' ' || b == del } // ValidHeaderFieldName reports whether v is a valid HTTP/1.x header name. // HTTP/2 imposes the additional restriction that uppercase ASCII // letters are not allowed. // // RFC 7230 says: // // header-field = field-name ":" OWS field-value OWS // field-name = token // token = 1*tchar // tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / // "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA func ValidHeaderFieldName(v string) bool { if len(v) == 0 { return false } for i := 0; i < len(v); i++ { if !isTokenTable[v[i]] { return false } } return true } // ValidHostHeader reports whether h is a valid host header. func ValidHostHeader(h string) bool { // The latest spec is actually this: // // http://tools.ietf.org/html/rfc7230#section-5.4 // Host = uri-host [ ":" port ] // // Where uri-host is: // http://tools.ietf.org/html/rfc3986#section-3.2.2 // // But we're going to be much more lenient for now and just // search for any byte that's not a valid byte in any of those // expressions. for i := 0; i < len(h); i++ { if !validHostByte[h[i]] { return false } } return true } // See the validHostHeader comment. var validHostByte = [256]bool{ '0': true, '1': true, '2': true, '3': true, '4': true, '5': true, '6': true, '7': true, '8': true, '9': true, 'a': true, 'b': true, 'c': true, 'd': true, 'e': true, 'f': true, 'g': true, 'h': true, 'i': true, 'j': true, 'k': true, 'l': true, 'm': true, 'n': true, 'o': true, 'p': true, 'q': true, 'r': true, 's': true, 't': true, 'u': true, 'v': true, 'w': true, 'x': true, 'y': true, 'z': true, 'A': true, 'B': true, 'C': true, 'D': true, 'E': true, 'F': true, 'G': true, 'H': true, 'I': true, 'J': true, 'K': true, 'L': true, 'M': true, 'N': true, 'O': true, 'P': true, 'Q': true, 'R': true, 'S': true, 'T': true, 'U': true, 'V': true, 'W': true, 'X': true, 'Y': true, 'Z': true, '!': true, // sub-delims '$': true, // sub-delims '%': true, // pct-encoded (and used in IPv6 zones) '&': true, // sub-delims '(': true, // sub-delims ')': true, // sub-delims '*': true, // sub-delims '+': true, // sub-delims ',': true, // sub-delims '-': true, // unreserved '.': true, // unreserved ':': true, // IPv6address + Host expression's optional port ';': true, // sub-delims '=': true, // sub-delims '[': true, '\'': true, // sub-delims ']': true, '_': true, // unreserved '~': true, // unreserved } // ValidHeaderFieldValue reports whether v is a valid "field-value" according to // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 : // // message-header = field-name ":" [ field-value ] // field-value = *( field-content | LWS ) // field-content = <the OCTETs making up the field-value // and consisting of either *TEXT or combinations // of token, separators, and quoted-string> // // http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2 : // // TEXT = <any OCTET except CTLs, // but including LWS> // LWS = [CRLF] 1*( SP | HT ) // CTL = <any US-ASCII control character // (octets 0 - 31) and DEL (127)> // // RFC 7230 says: // // field-value = *( field-content / obs-fold ) // obj-fold = N/A to http2, and deprecated // field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] // field-vchar = VCHAR / obs-text // obs-text = %x80-FF // VCHAR = "any visible [USASCII] character" // // http2 further says: "Similarly, HTTP/2 allows header field values // that are not valid. While most of the values that can be encoded // will not alter header field parsing, carriage return (CR, ASCII // 0xd), line feed (LF, ASCII 0xa), and the zero character (NUL, ASCII // 0x0) might be exploited by an attacker if they are translated // verbatim. Any request or response that contains a character not // permitted in a header field value MUST be treated as malformed // (Section 8.1.2.6). Valid characters are defined by the // field-content ABNF rule in Section 3.2 of [RFC7230]." // // This function does not (yet?) properly handle the rejection of // strings that begin or end with SP or HTAB. func ValidHeaderFieldValue(v string) bool { for i := 0; i < len(v); i++ { b := v[i] if isCTL(b) && !isLWS(b) { return false } } return true } func isASCII(s string) bool { for i := 0; i < len(s); i++ { if s[i] >= utf8.RuneSelf { return false } } return true } // PunycodeHostPort returns the IDNA Punycode version // of the provided "host" or "host:port" string. func PunycodeHostPort(v string) (string, error) { if isASCII(v) { return v, nil } host, port, err := net.SplitHostPort(v) if err != nil { // The input 'v' argument was just a "host" argument, // without a port. This error should not be returned // to the caller. host = v port = "" } host, err = idna.ToASCII(host) if err != nil { // Non-UTF-8? Not representable in Punycode, in any // case. return "", err } if port == "" { return host, nil } return net.JoinHostPort(host, port), nil } ================================================ FILE: vendor/golang.org/x/net/http2/.gitignore ================================================ *~ h2i/h2i ================================================ FILE: vendor/golang.org/x/net/http2/ascii.go ================================================ // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package http2 import "strings" // The HTTP protocols are defined in terms of ASCII, not Unicode. This file // contains helper functions which may use Unicode-aware functions which would // otherwise be unsafe and could introduce vulnerabilities if used improperly. // asciiEqualFold is strings.EqualFold, ASCII only. It reports whether s and t // are equal, ASCII-case-insensitively. func asciiEqualFold(s, t string) bool { if len(s) != len(t) { return false } for i := 0; i < len(s); i++ { if lower(s[i]) != lower(t[i]) { return false } } return true } // lower returns the ASCII lowercase version of b. func lower(b byte) byte { if 'A' <= b && b <= 'Z' { return b + ('a' - 'A') } return b } // isASCIIPrint returns whether s is ASCII and printable according to // https://tools.ietf.org/html/rfc20#section-4.2. func isASCIIPrint(s string) bool { for i := 0; i < len(s); i++ { if s[i] < ' ' || s[i] > '~' { return false } } return true } // asciiToLower returns the lowercase version of s if s is ASCII and printable, // and whether or not it was. func asciiToLower(s string) (lower string, ok bool) { if !isASCIIPrint(s) { return "", false } return strings.ToLower(s), true } ================================================ FILE: vendor/golang.org/x/net/http2/ciphers.go ================================================ // Copyright 2017 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package http2 // A list of the possible cipher suite ids. Taken from // https://www.iana.org/assignments/tls-parameters/tls-parameters.txt const ( cipher_TLS_NULL_WITH_NULL_NULL uint16 = 0x0000 cipher_TLS_RSA_WITH_NULL_MD5 uint16 = 0x0001 cipher_TLS_RSA_WITH_NULL_SHA uint16 = 0x0002 cipher_TLS_RSA_EXPORT_WITH_RC4_40_MD5 uint16 = 0x0003 cipher_TLS_RSA_WITH_RC4_128_MD5 uint16 = 0x0004 cipher_TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005 cipher_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 uint16 = 0x0006 cipher_TLS_RSA_WITH_IDEA_CBC_SHA uint16 = 0x0007 cipher_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x0008 cipher_TLS_RSA_WITH_DES_CBC_SHA uint16 = 0x0009 cipher_TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000A cipher_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x000B cipher_TLS_DH_DSS_WITH_DES_CBC_SHA uint16 = 0x000C cipher_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA uint16 = 0x000D cipher_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x000E cipher_TLS_DH_RSA_WITH_DES_CBC_SHA uint16 = 0x000F cipher_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x0010 cipher_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x0011 cipher_TLS_DHE_DSS_WITH_DES_CBC_SHA uint16 = 0x0012 cipher_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA uint16 = 0x0013 cipher_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x0014 cipher_TLS_DHE_RSA_WITH_DES_CBC_SHA uint16 = 0x0015 cipher_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x0016 cipher_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 uint16 = 0x0017 cipher_TLS_DH_anon_WITH_RC4_128_MD5 uint16 = 0x0018 cipher_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x0019 cipher_TLS_DH_anon_WITH_DES_CBC_SHA uint16 = 0x001A cipher_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA uint16 = 0x001B // Reserved uint16 = 0x001C-1D cipher_TLS_KRB5_WITH_DES_CBC_SHA uint16 = 0x001E cipher_TLS_KRB5_WITH_3DES_EDE_CBC_SHA uint16 = 0x001F cipher_TLS_KRB5_WITH_RC4_128_SHA uint16 = 0x0020 cipher_TLS_KRB5_WITH_IDEA_CBC_SHA uint16 = 0x0021 cipher_TLS_KRB5_WITH_DES_CBC_MD5 uint16 = 0x0022 cipher_TLS_KRB5_WITH_3DES_EDE_CBC_MD5 uint16 = 0x0023 cipher_TLS_KRB5_WITH_RC4_128_MD5 uint16 = 0x0024 cipher_TLS_KRB5_WITH_IDEA_CBC_MD5 uint16 = 0x0025 cipher_TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA uint16 = 0x0026 cipher_TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA uint16 = 0x0027 cipher_TLS_KRB5_EXPORT_WITH_RC4_40_SHA uint16 = 0x0028 cipher_TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 uint16 = 0x0029 cipher_TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5 uint16 = 0x002A cipher_TLS_KRB5_EXPORT_WITH_RC4_40_MD5 uint16 = 0x002B cipher_TLS_PSK_WITH_NULL_SHA uint16 = 0x002C cipher_TLS_DHE_PSK_WITH_NULL_SHA uint16 = 0x002D cipher_TLS_RSA_PSK_WITH_NULL_SHA uint16 = 0x002E cipher_TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002F cipher_TLS_DH_DSS_WITH_AES_128_CBC_SHA uint16 = 0x0030 cipher_TLS_DH_RSA_WITH_AES_128_CBC_SHA uint16 = 0x0031 cipher_TLS_DHE_DSS_WITH_AES_128_CBC_SHA uint16 = 0x0032 cipher_TLS_DHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0x0033 cipher_TLS_DH_anon_WITH_AES_128_CBC_SHA uint16 = 0x0034 cipher_TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035 cipher_TLS_DH_DSS_WITH_AES_256_CBC_SHA uint16 = 0x0036 cipher_TLS_DH_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0037 cipher_TLS_DHE_DSS_WITH_AES_256_CBC_SHA uint16 = 0x0038 cipher_TLS_DHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0039 cipher_TLS_DH_anon_WITH_AES_256_CBC_SHA uint16 = 0x003A cipher_TLS_RSA_WITH_NULL_SHA256 uint16 = 0x003B cipher_TLS_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x003C cipher_TLS_RSA_WITH_AES_256_CBC_SHA256 uint16 = 0x003D cipher_TLS_DH_DSS_WITH_AES_128_CBC_SHA256 uint16 = 0x003E cipher_TLS_DH_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x003F cipher_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 uint16 = 0x0040 cipher_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0041 cipher_TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0042 cipher_TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0043 cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0044 cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0045 cipher_TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0046 // Reserved uint16 = 0x0047-4F // Reserved uint16 = 0x0050-58 // Reserved uint16 = 0x0059-5C // Unassigned uint16 = 0x005D-5F // Reserved uint16 = 0x0060-66 cipher_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x0067 cipher_TLS_DH_DSS_WITH_AES_256_CBC_SHA256 uint16 = 0x0068 cipher_TLS_DH_RSA_WITH_AES_256_CBC_SHA256 uint16 = 0x0069 cipher_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 uint16 = 0x006A cipher_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 uint16 = 0x006B cipher_TLS_DH_anon_WITH_AES_128_CBC_SHA256 uint16 = 0x006C cipher_TLS_DH_anon_WITH_AES_256_CBC_SHA256 uint16 = 0x006D // Unassigned uint16 = 0x006E-83 cipher_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0084 cipher_TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0085 cipher_TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0086 cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0087 cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0088 cipher_TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0089 cipher_TLS_PSK_WITH_RC4_128_SHA uint16 = 0x008A cipher_TLS_PSK_WITH_3DES_EDE_CBC_SHA uint16 = 0x008B cipher_TLS_PSK_WITH_AES_128_CBC_SHA uint16 = 0x008C cipher_TLS_PSK_WITH_AES_256_CBC_SHA uint16 = 0x008D cipher_TLS_DHE_PSK_WITH_RC4_128_SHA uint16 = 0x008E cipher_TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA uint16 = 0x008F cipher_TLS_DHE_PSK_WITH_AES_128_CBC_SHA uint16 = 0x0090 cipher_TLS_DHE_PSK_WITH_AES_256_CBC_SHA uint16 = 0x0091 cipher_TLS_RSA_PSK_WITH_RC4_128_SHA uint16 = 0x0092 cipher_TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA uint16 = 0x0093 cipher_TLS_RSA_PSK_WITH_AES_128_CBC_SHA uint16 = 0x0094 cipher_TLS_RSA_PSK_WITH_AES_256_CBC_SHA uint16 = 0x0095 cipher_TLS_RSA_WITH_SEED_CBC_SHA uint16 = 0x0096 cipher_TLS_DH_DSS_WITH_SEED_CBC_SHA uint16 = 0x0097 cipher_TLS_DH_RSA_WITH_SEED_CBC_SHA uint16 = 0x0098 cipher_TLS_DHE_DSS_WITH_SEED_CBC_SHA uint16 = 0x0099 cipher_TLS_DHE_RSA_WITH_SEED_CBC_SHA uint16 = 0x009A cipher_TLS_DH_anon_WITH_SEED_CBC_SHA uint16 = 0x009B cipher_TLS_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x009C cipher_TLS_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x009D cipher_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x009E cipher_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x009F cipher_TLS_DH_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x00A0 cipher_TLS_DH_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x00A1 cipher_TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 uint16 = 0x00A2 cipher_TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 uint16 = 0x00A3 cipher_TLS_DH_DSS_WITH_AES_128_GCM_SHA256 uint16 = 0x00A4 cipher_TLS_DH_DSS_WITH_AES_256_GCM_SHA384 uint16 = 0x00A5 cipher_TLS_DH_anon_WITH_AES_128_GCM_SHA256 uint16 = 0x00A6 cipher_TLS_DH_anon_WITH_AES_256_GCM_SHA384 uint16 = 0x00A7 cipher_TLS_PSK_WITH_AES_128_GCM_SHA256 uint16 = 0x00A8 cipher_TLS_PSK_WITH_AES_256_GCM_SHA384 uint16 = 0x00A9 cipher_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 uint16 = 0x00AA cipher_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 uint16 = 0x00AB cipher_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 uint16 = 0x00AC cipher_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 uint16 = 0x00AD cipher_TLS_PSK_WITH_AES_128_CBC_SHA256 uint16 = 0x00AE cipher_TLS_PSK_WITH_AES_256_CBC_SHA384 uint16 = 0x00AF cipher_TLS_PSK_WITH_NULL_SHA256 uint16 = 0x00B0 cipher_TLS_PSK_WITH_NULL_SHA384 uint16 = 0x00B1 cipher_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 uint16 = 0x00B2 cipher_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 uint16 = 0x00B3 cipher_TLS_DHE_PSK_WITH_NULL_SHA256 uint16 = 0x00B4 cipher_TLS_DHE_PSK_WITH_NULL_SHA384 uint16 = 0x00B5 cipher_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 uint16 = 0x00B6 cipher_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 uint16 = 0x00B7 cipher_TLS_RSA_PSK_WITH_NULL_SHA256 uint16 = 0x00B8 cipher_TLS_RSA_PSK_WITH_NULL_SHA384 uint16 = 0x00B9 cipher_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BA cipher_TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BB cipher_TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BC cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BD cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BE cipher_TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BF cipher_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C0 cipher_TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C1 cipher_TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C2 cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C3 cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C4 cipher_TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C5 // Unassigned uint16 = 0x00C6-FE cipher_TLS_EMPTY_RENEGOTIATION_INFO_SCSV uint16 = 0x00FF // Unassigned uint16 = 0x01-55,* cipher_TLS_FALLBACK_SCSV uint16 = 0x5600 // Unassigned uint16 = 0x5601 - 0xC000 cipher_TLS_ECDH_ECDSA_WITH_NULL_SHA uint16 = 0xC001 cipher_TLS_ECDH_ECDSA_WITH_RC4_128_SHA uint16 = 0xC002 cipher_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC003 cipher_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xC004 cipher_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xC005 cipher_TLS_ECDHE_ECDSA_WITH_NULL_SHA uint16 = 0xC006 cipher_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA uint16 = 0xC007 cipher_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC008 cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xC009 cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xC00A cipher_TLS_ECDH_RSA_WITH_NULL_SHA uint16 = 0xC00B cipher_TLS_ECDH_RSA_WITH_RC4_128_SHA uint16 = 0xC00C cipher_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC00D cipher_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA uint16 = 0xC00E cipher_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA uint16 = 0xC00F cipher_TLS_ECDHE_RSA_WITH_NULL_SHA uint16 = 0xC010 cipher_TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xC011 cipher_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC012 cipher_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xC013 cipher_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xC014 cipher_TLS_ECDH_anon_WITH_NULL_SHA uint16 = 0xC015 cipher_TLS_ECDH_anon_WITH_RC4_128_SHA uint16 = 0xC016 cipher_TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA uint16 = 0xC017 cipher_TLS_ECDH_anon_WITH_AES_128_CBC_SHA uint16 = 0xC018 cipher_TLS_ECDH_anon_WITH_AES_256_CBC_SHA uint16 = 0xC019 cipher_TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC01A cipher_TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC01B cipher_TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA uint16 = 0xC01C cipher_TLS_SRP_SHA_WITH_AES_128_CBC_SHA uint16 = 0xC01D cipher_TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA uint16 = 0xC01E cipher_TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA uint16 = 0xC01F cipher_TLS_SRP_SHA_WITH_AES_256_CBC_SHA uint16 = 0xC020 cipher_TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA uint16 = 0xC021 cipher_TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA uint16 = 0xC022 cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 uint16 = 0xC023 cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 uint16 = 0xC024 cipher_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 uint16 = 0xC025 cipher_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 uint16 = 0xC026 cipher_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0xC027 cipher_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 uint16 = 0xC028 cipher_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0xC029 cipher_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 uint16 = 0xC02A cipher_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xC02B cipher_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xC02C cipher_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xC02D cipher_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xC02E cipher_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xC02F cipher_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xC030 cipher_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xC031 cipher_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xC032 cipher_TLS_ECDHE_PSK_WITH_RC4_128_SHA uint16 = 0xC033 cipher_TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA uint16 = 0xC034 cipher_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA uint16 = 0xC035 cipher_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA uint16 = 0xC036 cipher_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 uint16 = 0xC037 cipher_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 uint16 = 0xC038 cipher_TLS_ECDHE_PSK_WITH_NULL_SHA uint16 = 0xC039 cipher_TLS_ECDHE_PSK_WITH_NULL_SHA256 uint16 = 0xC03A cipher_TLS_ECDHE_PSK_WITH_NULL_SHA384 uint16 = 0xC03B cipher_TLS_RSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC03C cipher_TLS_RSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC03D cipher_TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC03E cipher_TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC03F cipher_TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC040 cipher_TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC041 cipher_TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC042 cipher_TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC043 cipher_TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC044 cipher_TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC045 cipher_TLS_DH_anon_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC046 cipher_TLS_DH_anon_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC047 cipher_TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC048 cipher_TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC049 cipher_TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC04A cipher_TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC04B cipher_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC04C cipher_TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC04D cipher_TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC04E cipher_TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC04F cipher_TLS_RSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC050 cipher_TLS_RSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC051 cipher_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC052 cipher_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC053 cipher_TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC054 cipher_TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC055 cipher_TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC056 cipher_TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC057 cipher_TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC058 cipher_TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC059 cipher_TLS_DH_anon_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC05A cipher_TLS_DH_anon_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC05B cipher_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC05C cipher_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC05D cipher_TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC05E cipher_TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC05F cipher_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC060 cipher_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC061 cipher_TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC062 cipher_TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC063 cipher_TLS_PSK_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC064 cipher_TLS_PSK_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC065 cipher_TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC066 cipher_TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC067 cipher_TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC068 cipher_TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC069 cipher_TLS_PSK_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC06A cipher_TLS_PSK_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC06B cipher_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC06C cipher_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC06D cipher_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC06E cipher_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC06F cipher_TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC070 cipher_TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC071 cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC072 cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC073 cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC074 cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC075 cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC076 cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC077 cipher_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC078 cipher_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC079 cipher_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC07A cipher_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC07B cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC07C cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC07D cipher_TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC07E cipher_TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC07F cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC080 cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC081 cipher_TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC082 cipher_TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC083 cipher_TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC084 cipher_TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC085 cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC086 cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC087 cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC088 cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC089 cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC08A cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC08B cipher_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC08C cipher_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC08D cipher_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC08E cipher_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC08F cipher_TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC090 cipher_TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC091 cipher_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC092 cipher_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC093 cipher_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC094 cipher_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC095 cipher_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC096 cipher_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC097 cipher_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC098 cipher_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC099 cipher_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC09A cipher_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC09B cipher_TLS_RSA_WITH_AES_128_CCM uint16 = 0xC09C cipher_TLS_RSA_WITH_AES_256_CCM uint16 = 0xC09D cipher_TLS_DHE_RSA_WITH_AES_128_CCM uint16 = 0xC09E cipher_TLS_DHE_RSA_WITH_AES_256_CCM uint16 = 0xC09F cipher_TLS_RSA_WITH_AES_128_CCM_8 uint16 = 0xC0A0 cipher_TLS_RSA_WITH_AES_256_CCM_8 uint16 = 0xC0A1 cipher_TLS_DHE_RSA_WITH_AES_128_CCM_8 uint16 = 0xC0A2 cipher_TLS_DHE_RSA_WITH_AES_256_CCM_8 uint16 = 0xC0A3 cipher_TLS_PSK_WITH_AES_128_CCM uint16 = 0xC0A4 cipher_TLS_PSK_WITH_AES_256_CCM uint16 = 0xC0A5 cipher_TLS_DHE_PSK_WITH_AES_128_CCM uint16 = 0xC0A6 cipher_TLS_DHE_PSK_WITH_AES_256_CCM uint16 = 0xC0A7 cipher_TLS_PSK_WITH_AES_128_CCM_8 uint16 = 0xC0A8 cipher_TLS_PSK_WITH_AES_256_CCM_8 uint16 = 0xC0A9 cipher_TLS_PSK_DHE_WITH_AES_128_CCM_8 uint16 = 0xC0AA cipher_TLS_PSK_DHE_WITH_AES_256_CCM_8 uint16 = 0xC0AB cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CCM uint16 = 0xC0AC cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CCM uint16 = 0xC0AD cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 uint16 = 0xC0AE cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 uint16 = 0xC0AF // Unassigned uint16 = 0xC0B0-FF // Unassigned uint16 = 0xC1-CB,* // Unassigned uint16 = 0xCC00-A7 cipher_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCA8 cipher_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCA9 cipher_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCAA cipher_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCAB cipher_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCAC cipher_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCAD cipher_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCAE ) // isBadCipher reports whether the cipher is blacklisted by the HTTP/2 spec. // References: // https://tools.ietf.org/html/rfc7540#appendix-A // Reject cipher suites from Appendix A. // "This list includes those cipher suites that do not // offer an ephemeral key exchange and those that are // based on the TLS null, stream or block cipher type" func isBadCipher(cipher uint16) bool { switch cipher { case cipher_TLS_NULL_WITH_NULL_NULL, cipher_TLS_RSA_WITH_NULL_MD5, cipher_TLS_RSA_WITH_NULL_SHA, cipher_TLS_RSA_EXPORT_WITH_RC4_40_MD5, cipher_TLS_RSA_WITH_RC4_128_MD5, cipher_TLS_RSA_WITH_RC4_128_SHA, cipher_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5, cipher_TLS_RSA_WITH_IDEA_CBC_SHA, cipher_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA, cipher_TLS_RSA_WITH_DES_CBC_SHA, cipher_TLS_RSA_WITH_3DES_EDE_CBC_SHA, cipher_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA, cipher_TLS_DH_DSS_WITH_DES_CBC_SHA, cipher_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA, cipher_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA, cipher_TLS_DH_RSA_WITH_DES_CBC_SHA, cipher_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA, cipher_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, cipher_TLS_DHE_DSS_WITH_DES_CBC_SHA, cipher_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, cipher_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, cipher_TLS_DHE_RSA_WITH_DES_CBC_SHA, cipher_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, cipher_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5, cipher_TLS_DH_anon_WITH_RC4_128_MD5, cipher_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA, cipher_TLS_DH_anon_WITH_DES_CBC_SHA, cipher_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, cipher_TLS_KRB5_WITH_DES_CBC_SHA, cipher_TLS_KRB5_WITH_3DES_EDE_CBC_SHA, cipher_TLS_KRB5_WITH_RC4_128_SHA, cipher_TLS_KRB5_WITH_IDEA_CBC_SHA, cipher_TLS_KRB5_WITH_DES_CBC_MD5, cipher_TLS_KRB5_WITH_3DES_EDE_CBC_MD5, cipher_TLS_KRB5_WITH_RC4_128_MD5, cipher_TLS_KRB5_WITH_IDEA_CBC_MD5, cipher_TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA, cipher_TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA, cipher_TLS_KRB5_EXPORT_WITH_RC4_40_SHA, cipher_TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5, cipher_TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5, cipher_TLS_KRB5_EXPORT_WITH_RC4_40_MD5, cipher_TLS_PSK_WITH_NULL_SHA, cipher_TLS_DHE_PSK_WITH_NULL_SHA, cipher_TLS_RSA_PSK_WITH_NULL_SHA, cipher_TLS_RSA_WITH_AES_128_CBC_SHA, cipher_TLS_DH_DSS_WITH_AES_128_CBC_SHA, cipher_TLS_DH_RSA_WITH_AES_128_CBC_SHA, cipher_TLS_DHE_DSS_WITH_AES_128_CBC_SHA, cipher_TLS_DHE_RSA_WITH_AES_128_CBC_SHA, cipher_TLS_DH_anon_WITH_AES_128_CBC_SHA, cipher_TLS_RSA_WITH_AES_256_CBC_SHA, cipher_TLS_DH_DSS_WITH_AES_256_CBC_SHA, cipher_TLS_DH_RSA_WITH_AES_256_CBC_SHA, cipher_TLS_DHE_DSS_WITH_AES_256_CBC_SHA, cipher_TLS_DHE_RSA_WITH_AES_256_CBC_SHA, cipher_TLS_DH_anon_WITH_AES_256_CBC_SHA, cipher_TLS_RSA_WITH_NULL_SHA256, cipher_TLS_RSA_WITH_AES_128_CBC_SHA256, cipher_TLS_RSA_WITH_AES_256_CBC_SHA256, cipher_TLS_DH_DSS_WITH_AES_128_CBC_SHA256, cipher_TLS_DH_RSA_WITH_AES_128_CBC_SHA256, cipher_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, cipher_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, cipher_TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA, cipher_TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA, cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA, cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, cipher_TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA, cipher_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, cipher_TLS_DH_DSS_WITH_AES_256_CBC_SHA256, cipher_TLS_DH_RSA_WITH_AES_256_CBC_SHA256, cipher_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, cipher_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, cipher_TLS_DH_anon_WITH_AES_128_CBC_SHA256, cipher_TLS_DH_anon_WITH_AES_256_CBC_SHA256, cipher_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, cipher_TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA, cipher_TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA, cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA, cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, cipher_TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA, cipher_TLS_PSK_WITH_RC4_128_SHA, cipher_TLS_PSK_WITH_3DES_EDE_CBC_SHA, cipher_TLS_PSK_WITH_AES_128_CBC_SHA, cipher_TLS_PSK_WITH_AES_256_CBC_SHA, cipher_TLS_DHE_PSK_WITH_RC4_128_SHA, cipher_TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, cipher_TLS_DHE_PSK_WITH_AES_128_CBC_SHA, cipher_TLS_DHE_PSK_WITH_AES_256_CBC_SHA, cipher_TLS_RSA_PSK_WITH_RC4_128_SHA, cipher_TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, cipher_TLS_RSA_PSK_WITH_AES_128_CBC_SHA, cipher_TLS_RSA_PSK_WITH_AES_256_CBC_SHA, cipher_TLS_RSA_WITH_SEED_CBC_SHA, cipher_TLS_DH_DSS_WITH_SEED_CBC_SHA, cipher_TLS_DH_RSA_WITH_SEED_CBC_SHA, cipher_TLS_DHE_DSS_WITH_SEED_CBC_SHA, cipher_TLS_DHE_RSA_WITH_SEED_CBC_SHA, cipher_TLS_DH_anon_WITH_SEED_CBC_SHA, cipher_TLS_RSA_WITH_AES_128_GCM_SHA256, cipher_TLS_RSA_WITH_AES_256_GCM_SHA384, cipher_TLS_DH_RSA_WITH_AES_128_GCM_SHA256, cipher_TLS_DH_RSA_WITH_AES_256_GCM_SHA384, cipher_TLS_DH_DSS_WITH_AES_128_GCM_SHA256, cipher_TLS_DH_DSS_WITH_AES_256_GCM_SHA384, cipher_TLS_DH_anon_WITH_AES_128_GCM_SHA256, cipher_TLS_DH_anon_WITH_AES_256_GCM_SHA384, cipher_TLS_PSK_WITH_AES_128_GCM_SHA256, cipher_TLS_PSK_WITH_AES_256_GCM_SHA384, cipher_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256, cipher_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384, cipher_TLS_PSK_WITH_AES_128_CBC_SHA256, cipher_TLS_PSK_WITH_AES_256_CBC_SHA384, cipher_TLS_PSK_WITH_NULL_SHA256, cipher_TLS_PSK_WITH_NULL_SHA384, cipher_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256, cipher_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, cipher_TLS_DHE_PSK_WITH_NULL_SHA256, cipher_TLS_DHE_PSK_WITH_NULL_SHA384, cipher_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256, cipher_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384, cipher_TLS_RSA_PSK_WITH_NULL_SHA256, cipher_TLS_RSA_PSK_WITH_NULL_SHA384, cipher_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256, cipher_TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256, cipher_TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256, cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256, cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, cipher_TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256, cipher_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256, cipher_TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256, cipher_TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256, cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256, cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256, cipher_TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256, cipher_TLS_EMPTY_RENEGOTIATION_INFO_SCSV, cipher_TLS_ECDH_ECDSA_WITH_NULL_SHA, cipher_TLS_ECDH_ECDSA_WITH_RC4_128_SHA, cipher_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, cipher_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, cipher_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, cipher_TLS_ECDHE_ECDSA_WITH_NULL_SHA, cipher_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, cipher_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, cipher_TLS_ECDH_RSA_WITH_NULL_SHA, cipher_TLS_ECDH_RSA_WITH_RC4_128_SHA, cipher_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, cipher_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, cipher_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, cipher_TLS_ECDHE_RSA_WITH_NULL_SHA, cipher_TLS_ECDHE_RSA_WITH_RC4_128_SHA, cipher_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, cipher_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, cipher_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, cipher_TLS_ECDH_anon_WITH_NULL_SHA, cipher_TLS_ECDH_anon_WITH_RC4_128_SHA, cipher_TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA, cipher_TLS_ECDH_anon_WITH_AES_128_CBC_SHA, cipher_TLS_ECDH_anon_WITH_AES_256_CBC_SHA, cipher_TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA, cipher_TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA, cipher_TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA, cipher_TLS_SRP_SHA_WITH_AES_128_CBC_SHA, cipher_TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA, cipher_TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA, cipher_TLS_SRP_SHA_WITH_AES_256_CBC_SHA, cipher_TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA, cipher_TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA, cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, cipher_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, cipher_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, cipher_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, cipher_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, cipher_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, cipher_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, cipher_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, cipher_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, cipher_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, cipher_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, cipher_TLS_ECDHE_PSK_WITH_RC4_128_SHA, cipher_TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA, cipher_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, cipher_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, cipher_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, cipher_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, cipher_TLS_ECDHE_PSK_WITH_NULL_SHA, cipher_TLS_ECDHE_PSK_WITH_NULL_SHA256, cipher_TLS_ECDHE_PSK_WITH_NULL_SHA384, cipher_TLS_RSA_WITH_ARIA_128_CBC_SHA256, cipher_TLS_RSA_WITH_ARIA_256_CBC_SHA384, cipher_TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256, cipher_TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384, cipher_TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256, cipher_TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384, cipher_TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256, cipher_TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384, cipher_TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256, cipher_TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384, cipher_TLS_DH_anon_WITH_ARIA_128_CBC_SHA256, cipher_TLS_DH_anon_WITH_ARIA_256_CBC_SHA384, cipher_TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256, cipher_TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384, cipher_TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256, cipher_TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384, cipher_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256, cipher_TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384, cipher_TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256, cipher_TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384, cipher_TLS_RSA_WITH_ARIA_128_GCM_SHA256, cipher_TLS_RSA_WITH_ARIA_256_GCM_SHA384, cipher_TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256, cipher_TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384, cipher_TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256, cipher_TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384, cipher_TLS_DH_anon_WITH_ARIA_128_GCM_SHA256, cipher_TLS_DH_anon_WITH_ARIA_256_GCM_SHA384, cipher_TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256, cipher_TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384, cipher_TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256, cipher_TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384, cipher_TLS_PSK_WITH_ARIA_128_CBC_SHA256, cipher_TLS_PSK_WITH_ARIA_256_CBC_SHA384, cipher_TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256, cipher_TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384, cipher_TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256, cipher_TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384, cipher_TLS_PSK_WITH_ARIA_128_GCM_SHA256, cipher_TLS_PSK_WITH_ARIA_256_GCM_SHA384, cipher_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256, cipher_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384, cipher_TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256, cipher_TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384, cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256, cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384, cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256, cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384, cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384, cipher_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256, cipher_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384, cipher_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256, cipher_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384, cipher_TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256, cipher_TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384, cipher_TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256, cipher_TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384, cipher_TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256, cipher_TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384, cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256, cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384, cipher_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256, cipher_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384, cipher_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256, cipher_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384, cipher_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256, cipher_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384, cipher_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256, cipher_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384, cipher_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, cipher_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, cipher_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256, cipher_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384, cipher_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256, cipher_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384, cipher_TLS_RSA_WITH_AES_128_CCM, cipher_TLS_RSA_WITH_AES_256_CCM, cipher_TLS_RSA_WITH_AES_128_CCM_8, cipher_TLS_RSA_WITH_AES_256_CCM_8, cipher_TLS_PSK_WITH_AES_128_CCM, cipher_TLS_PSK_WITH_AES_256_CCM, cipher_TLS_PSK_WITH_AES_128_CCM_8, cipher_TLS_PSK_WITH_AES_256_CCM_8: return true default: return false } } ================================================ FILE: vendor/golang.org/x/net/http2/client_conn_pool.go ================================================ // Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Transport code's client connection pooling. package http2 import ( "context" "errors" "net" "net/http" "sync" ) // ClientConnPool manages a pool of HTTP/2 client connections. type ClientConnPool interface { // GetClientConn returns a specific HTTP/2 connection (usually // a TLS-TCP connection) to an HTTP/2 server. On success, the // returned ClientConn accounts for the upcoming RoundTrip // call, so the caller should not omit it. If the caller needs // to, ClientConn.RoundTrip can be called with a bogus // new(http.Request) to release the stream reservation. GetClientConn(req *http.Request, addr string) (*ClientConn, error) MarkDead(*ClientConn) } // clientConnPoolIdleCloser is the interface implemented by ClientConnPool // implementations which can close their idle connections. type clientConnPoolIdleCloser interface { ClientConnPool closeIdleConnections() } var ( _ clientConnPoolIdleCloser = (*clientConnPool)(nil) _ clientConnPoolIdleCloser = noDialClientConnPool{} ) // TODO: use singleflight for dialing and addConnCalls? type clientConnPool struct { t *Transport mu sync.Mutex // TODO: maybe switch to RWMutex // TODO: add support for sharing conns based on cert names // (e.g. share conn for googleapis.com and appspot.com) conns map[string][]*ClientConn // key is host:port dialing map[string]*dialCall // currently in-flight dials keys map[*ClientConn][]string addConnCalls map[string]*addConnCall // in-flight addConnIfNeeded calls } func (p *clientConnPool) GetClientConn(req *http.Request, addr string) (*ClientConn, error) { return p.getClientConn(req, addr, dialOnMiss) } const ( dialOnMiss = true noDialOnMiss = false ) func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMiss bool) (*ClientConn, error) { // TODO(dneil): Dial a new connection when t.DisableKeepAlives is set? if isConnectionCloseRequest(req) && dialOnMiss { // It gets its own connection. traceGetConn(req, addr) const singleUse = true cc, err := p.t.dialClientConn(req.Context(), addr, singleUse) if err != nil { return nil, err } return cc, nil } for { p.mu.Lock() for _, cc := range p.conns[addr] { if cc.ReserveNewRequest() { // When a connection is presented to us by the net/http package, // the GetConn hook has already been called. // Don't call it a second time here. if !cc.getConnCalled { traceGetConn(req, addr) } cc.getConnCalled = false p.mu.Unlock() return cc, nil } } if !dialOnMiss { p.mu.Unlock() return nil, ErrNoCachedConn } traceGetConn(req, addr) call := p.getStartDialLocked(req.Context(), addr) p.mu.Unlock() <-call.done if shouldRetryDial(call, req) { continue } cc, err := call.res, call.err if err != nil { return nil, err } if cc.ReserveNewRequest() { return cc, nil } } } // dialCall is an in-flight Transport dial call to a host. type dialCall struct { _ incomparable p *clientConnPool // the context associated with the request // that created this dialCall ctx context.Context done chan struct{} // closed when done res *ClientConn // valid after done is closed err error // valid after done is closed } // requires p.mu is held. func (p *clientConnPool) getStartDialLocked(ctx context.Context, addr string) *dialCall { if call, ok := p.dialing[addr]; ok { // A dial is already in-flight. Don't start another. return call } call := &dialCall{p: p, done: make(chan struct{}), ctx: ctx} if p.dialing == nil { p.dialing = make(map[string]*dialCall) } p.dialing[addr] = call go call.dial(call.ctx, addr) return call } // run in its own goroutine. func (c *dialCall) dial(ctx context.Context, addr string) { const singleUse = false // shared conn c.res, c.err = c.p.t.dialClientConn(ctx, addr, singleUse) c.p.mu.Lock() delete(c.p.dialing, addr) if c.err == nil { c.p.addConnLocked(addr, c.res) } c.p.mu.Unlock() close(c.done) } // addConnIfNeeded makes a NewClientConn out of c if a connection for key doesn't // already exist. It coalesces concurrent calls with the same key. // This is used by the http1 Transport code when it creates a new connection. Because // the http1 Transport doesn't de-dup TCP dials to outbound hosts (because it doesn't know // the protocol), it can get into a situation where it has multiple TLS connections. // This code decides which ones live or die. // The return value used is whether c was used. // c is never closed. func (p *clientConnPool) addConnIfNeeded(key string, t *Transport, c net.Conn) (used bool, err error) { p.mu.Lock() for _, cc := range p.conns[key] { if cc.CanTakeNewRequest() { p.mu.Unlock() return false, nil } } call, dup := p.addConnCalls[key] if !dup { if p.addConnCalls == nil { p.addConnCalls = make(map[string]*addConnCall) } call = &addConnCall{ p: p, done: make(chan struct{}), } p.addConnCalls[key] = call go call.run(t, key, c) } p.mu.Unlock() <-call.done if call.err != nil { return false, call.err } return !dup, nil } type addConnCall struct { _ incomparable p *clientConnPool done chan struct{} // closed when done err error } func (c *addConnCall) run(t *Transport, key string, nc net.Conn) { cc, err := t.NewClientConn(nc) p := c.p p.mu.Lock() if err != nil { c.err = err } else { cc.getConnCalled = true // already called by the net/http package p.addConnLocked(key, cc) } delete(p.addConnCalls, key) p.mu.Unlock() close(c.done) } // p.mu must be held func (p *clientConnPool) addConnLocked(key string, cc *ClientConn) { for _, v := range p.conns[key] { if v == cc { return } } if p.conns == nil { p.conns = make(map[string][]*ClientConn) } if p.keys == nil { p.keys = make(map[*ClientConn][]string) } p.conns[key] = append(p.conns[key], cc) p.keys[cc] = append(p.keys[cc], key) } func (p *clientConnPool) MarkDead(cc *ClientConn) { p.mu.Lock() defer p.mu.Unlock() for _, key := range p.keys[cc] { vv, ok := p.conns[key] if !ok { continue } newList := filterOutClientConn(vv, cc) if len(newList) > 0 { p.conns[key] = newList } else { delete(p.conns, key) } } delete(p.keys, cc) } func (p *clientConnPool) closeIdleConnections() { p.mu.Lock() defer p.mu.Unlock() // TODO: don't close a cc if it was just added to the pool // milliseconds ago and has never been used. There's currently // a small race window with the HTTP/1 Transport's integration // where it can add an idle conn just before using it, and // somebody else can concurrently call CloseIdleConns and // break some caller's RoundTrip. for _, vv := range p.conns { for _, cc := range vv { cc.closeIfIdle() } } } func filterOutClientConn(in []*ClientConn, exclude *ClientConn) []*ClientConn { out := in[:0] for _, v := range in { if v != exclude { out = append(out, v) } } // If we filtered it out, zero out the last item to prevent // the GC from seeing it. if len(in) != len(out) { in[len(in)-1] = nil } return out } // noDialClientConnPool is an implementation of http2.ClientConnPool // which never dials. We let the HTTP/1.1 client dial and use its TLS // connection instead. type noDialClientConnPool struct{ *clientConnPool } func (p noDialClientConnPool) GetClientConn(req *http.Request, addr string) (*ClientConn, error) { return p.getClientConn(req, addr, noDialOnMiss) } // shouldRetryDial reports whether the current request should // retry dialing after the call finished unsuccessfully, for example // if the dial was canceled because of a context cancellation or // deadline expiry. func shouldRetryDial(call *dialCall, req *http.Request) bool { if call.err == nil { // No error, no need to retry return false } if call.ctx == req.Context() { // If the call has the same context as the request, the dial // should not be retried, since any cancellation will have come // from this request. return false } if !errors.Is(call.err, context.Canceled) && !errors.Is(call.err, context.DeadlineExceeded) { // If the call error is not because of a context cancellation or a deadline expiry, // the dial should not be retried. return false } // Only retry if the error is a context cancellation error or deadline expiry // and the context associated with the call was canceled or expired. return call.ctx.Err() != nil } ================================================ FILE: vendor/golang.org/x/net/http2/config.go ================================================ // Copyright 2024 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package http2 import ( "math" "net/http" "time" ) // http2Config is a package-internal version of net/http.HTTP2Config. // // http.HTTP2Config was added in Go 1.24. // When running with a version of net/http that includes HTTP2Config, // we merge the configuration with the fields in Transport or Server // to produce an http2Config. // // Zero valued fields in http2Config are interpreted as in the // net/http.HTTPConfig documentation. // // Precedence order for reconciling configurations is: // // - Use the net/http.{Server,Transport}.HTTP2Config value, when non-zero. // - Otherwise use the http2.{Server.Transport} value. // - If the resulting value is zero or out of range, use a default. type http2Config struct { MaxConcurrentStreams uint32 StrictMaxConcurrentRequests bool MaxDecoderHeaderTableSize uint32 MaxEncoderHeaderTableSize uint32 MaxReadFrameSize uint32 MaxUploadBufferPerConnection int32 MaxUploadBufferPerStream int32 SendPingTimeout time.Duration PingTimeout time.Duration WriteByteTimeout time.Duration PermitProhibitedCipherSuites bool CountError func(errType string) } // configFromServer merges configuration settings from // net/http.Server.HTTP2Config and http2.Server. func configFromServer(h1 *http.Server, h2 *Server) http2Config { conf := http2Config{ MaxConcurrentStreams: h2.MaxConcurrentStreams, MaxEncoderHeaderTableSize: h2.MaxEncoderHeaderTableSize, MaxDecoderHeaderTableSize: h2.MaxDecoderHeaderTableSize, MaxReadFrameSize: h2.MaxReadFrameSize, MaxUploadBufferPerConnection: h2.MaxUploadBufferPerConnection, MaxUploadBufferPerStream: h2.MaxUploadBufferPerStream, SendPingTimeout: h2.ReadIdleTimeout, PingTimeout: h2.PingTimeout, WriteByteTimeout: h2.WriteByteTimeout, PermitProhibitedCipherSuites: h2.PermitProhibitedCipherSuites, CountError: h2.CountError, } fillNetHTTPConfig(&conf, h1.HTTP2) setConfigDefaults(&conf, true) return conf } // configFromTransport merges configuration settings from h2 and h2.t1.HTTP2 // (the net/http Transport). func configFromTransport(h2 *Transport) http2Config { conf := http2Config{ StrictMaxConcurrentRequests: h2.StrictMaxConcurrentStreams, MaxEncoderHeaderTableSize: h2.MaxEncoderHeaderTableSize, MaxDecoderHeaderTableSize: h2.MaxDecoderHeaderTableSize, MaxReadFrameSize: h2.MaxReadFrameSize, SendPingTimeout: h2.ReadIdleTimeout, PingTimeout: h2.PingTimeout, WriteByteTimeout: h2.WriteByteTimeout, } // Unlike most config fields, where out-of-range values revert to the default, // Transport.MaxReadFrameSize clips. if conf.MaxReadFrameSize < minMaxFrameSize { conf.MaxReadFrameSize = minMaxFrameSize } else if conf.MaxReadFrameSize > maxFrameSize { conf.MaxReadFrameSize = maxFrameSize } if h2.t1 != nil { fillNetHTTPConfig(&conf, h2.t1.HTTP2) } setConfigDefaults(&conf, false) return conf } func setDefault[T ~int | ~int32 | ~uint32 | ~int64](v *T, minval, maxval, defval T) { if *v < minval || *v > maxval { *v = defval } } func setConfigDefaults(conf *http2Config, server bool) { setDefault(&conf.MaxConcurrentStreams, 1, math.MaxUint32, defaultMaxStreams) setDefault(&conf.MaxEncoderHeaderTableSize, 1, math.MaxUint32, initialHeaderTableSize) setDefault(&conf.MaxDecoderHeaderTableSize, 1, math.MaxUint32, initialHeaderTableSize) if server { setDefault(&conf.MaxUploadBufferPerConnection, initialWindowSize, math.MaxInt32, 1<<20) } else { setDefault(&conf.MaxUploadBufferPerConnection, initialWindowSize, math.MaxInt32, transportDefaultConnFlow) } if server { setDefault(&conf.MaxUploadBufferPerStream, 1, math.MaxInt32, 1<<20) } else { setDefault(&conf.MaxUploadBufferPerStream, 1, math.MaxInt32, transportDefaultStreamFlow) } setDefault(&conf.MaxReadFrameSize, minMaxFrameSize, maxFrameSize, defaultMaxReadFrameSize) setDefault(&conf.PingTimeout, 1, math.MaxInt64, 15*time.Second) } // adjustHTTP1MaxHeaderSize converts a limit in bytes on the size of an HTTP/1 header // to an HTTP/2 MAX_HEADER_LIST_SIZE value. func adjustHTTP1MaxHeaderSize(n int64) int64 { // http2's count is in a slightly different unit and includes 32 bytes per pair. // So, take the net/http.Server value and pad it up a bit, assuming 10 headers. const perFieldOverhead = 32 // per http2 spec const typicalHeaders = 10 // conservative return n + typicalHeaders*perFieldOverhead } func fillNetHTTPConfig(conf *http2Config, h2 *http.HTTP2Config) { if h2 == nil { return } if h2.MaxConcurrentStreams != 0 { conf.MaxConcurrentStreams = uint32(h2.MaxConcurrentStreams) } if http2ConfigStrictMaxConcurrentRequests(h2) { conf.StrictMaxConcurrentRequests = true } if h2.MaxEncoderHeaderTableSize != 0 { conf.MaxEncoderHeaderTableSize = uint32(h2.MaxEncoderHeaderTableSize) } if h2.MaxDecoderHeaderTableSize != 0 { conf.MaxDecoderHeaderTableSize = uint32(h2.MaxDecoderHeaderTableSize) } if h2.MaxConcurrentStreams != 0 { conf.MaxConcurrentStreams = uint32(h2.MaxConcurrentStreams) } if h2.MaxReadFrameSize != 0 { conf.MaxReadFrameSize = uint32(h2.MaxReadFrameSize) } if h2.MaxReceiveBufferPerConnection != 0 { conf.MaxUploadBufferPerConnection = int32(h2.MaxReceiveBufferPerConnection) } if h2.MaxReceiveBufferPerStream != 0 { conf.MaxUploadBufferPerStream = int32(h2.MaxReceiveBufferPerStream) } if h2.SendPingTimeout != 0 { conf.SendPingTimeout = h2.SendPingTimeout } if h2.PingTimeout != 0 { conf.PingTimeout = h2.PingTimeout } if h2.WriteByteTimeout != 0 { conf.WriteByteTimeout = h2.WriteByteTimeout } if h2.PermitProhibitedCipherSuites { conf.PermitProhibitedCipherSuites = true } if h2.CountError != nil { conf.CountError = h2.CountError } } ================================================ FILE: vendor/golang.org/x/net/http2/config_go125.go ================================================ // Copyright 2025 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build !go1.26 package http2 import ( "net/http" ) func http2ConfigStrictMaxConcurrentRequests(h2 *http.HTTP2Config) bool { return false } ================================================ FILE: vendor/golang.org/x/net/http2/config_go126.go ================================================ // Copyright 2025 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build go1.26 package http2 import ( "net/http" ) func http2ConfigStrictMaxConcurrentRequests(h2 *http.HTTP2Config) bool { return h2.StrictMaxConcurrentRequests } ================================================ FILE: vendor/golang.org/x/net/http2/databuffer.go ================================================ // Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package http2 import ( "errors" "fmt" "sync" ) // Buffer chunks are allocated from a pool to reduce pressure on GC. // The maximum wasted space per dataBuffer is 2x the largest size class, // which happens when the dataBuffer has multiple chunks and there is // one unread byte in both the first and last chunks. We use a few size // classes to minimize overheads for servers that typically receive very // small request bodies. // // TODO: Benchmark to determine if the pools are necessary. The GC may have // improved enough that we can instead allocate chunks like this: // make([]byte, max(16<<10, expectedBytesRemaining)) var dataChunkPools = [...]sync.Pool{ {New: func() interface{} { return new([1 << 10]byte) }}, {New: func() interface{} { return new([2 << 10]byte) }}, {New: func() interface{} { return new([4 << 10]byte) }}, {New: func() interface{} { return new([8 << 10]byte) }}, {New: func() interface{} { return new([16 << 10]byte) }}, } func getDataBufferChunk(size int64) []byte { switch { case size <= 1<<10: return dataChunkPools[0].Get().(*[1 << 10]byte)[:] case size <= 2<<10: return dataChunkPools[1].Get().(*[2 << 10]byte)[:] case size <= 4<<10: return dataChunkPools[2].Get().(*[4 << 10]byte)[:] case size <= 8<<10: return dataChunkPools[3].Get().(*[8 << 10]byte)[:] default: return dataChunkPools[4].Get().(*[16 << 10]byte)[:] } } func putDataBufferChunk(p []byte) { switch len(p) { case 1 << 10: dataChunkPools[0].Put((*[1 << 10]byte)(p)) case 2 << 10: dataChunkPools[1].Put((*[2 << 10]byte)(p)) case 4 << 10: dataChunkPools[2].Put((*[4 << 10]byte)(p)) case 8 << 10: dataChunkPools[3].Put((*[8 << 10]byte)(p)) case 16 << 10: dataChunkPools[4].Put((*[16 << 10]byte)(p)) default: panic(fmt.Sprintf("unexpected buffer len=%v", len(p))) } } // dataBuffer is an io.ReadWriter backed by a list of data chunks. // Each dataBuffer is used to read DATA frames on a single stream. // The buffer is divided into chunks so the server can limit the // total memory used by a single connection without limiting the // request body size on any single stream. type dataBuffer struct { chunks [][]byte r int // next byte to read is chunks[0][r] w int // next byte to write is chunks[len(chunks)-1][w] size int // total buffered bytes expected int64 // we expect at least this many bytes in future Write calls (ignored if <= 0) } var errReadEmpty = errors.New("read from empty dataBuffer") // Read copies bytes from the buffer into p. // It is an error to read when no data is available. func (b *dataBuffer) Read(p []byte) (int, error) { if b.size == 0 { return 0, errReadEmpty } var ntotal int for len(p) > 0 && b.size > 0 { readFrom := b.bytesFromFirstChunk() n := copy(p, readFrom) p = p[n:] ntotal += n b.r += n b.size -= n // If the first chunk has been consumed, advance to the next chunk. if b.r == len(b.chunks[0]) { putDataBufferChunk(b.chunks[0]) end := len(b.chunks) - 1 copy(b.chunks[:end], b.chunks[1:]) b.chunks[end] = nil b.chunks = b.chunks[:end] b.r = 0 } } return ntotal, nil } func (b *dataBuffer) bytesFromFirstChunk() []byte { if len(b.chunks) == 1 { return b.chunks[0][b.r:b.w] } return b.chunks[0][b.r:] } // Len returns the number of bytes of the unread portion of the buffer. func (b *dataBuffer) Len() int { return b.size } // Write appends p to the buffer. func (b *dataBuffer) Write(p []byte) (int, error) { ntotal := len(p) for len(p) > 0 { // If the last chunk is empty, allocate a new chunk. Try to allocate // enough to fully copy p plus any additional bytes we expect to // receive. However, this may allocate less than len(p). want := int64(len(p)) if b.expected > want { want = b.expected } chunk := b.lastChunkOrAlloc(want) n := copy(chunk[b.w:], p) p = p[n:] b.w += n b.size += n b.expected -= int64(n) } return ntotal, nil } func (b *dataBuffer) lastChunkOrAlloc(want int64) []byte { if len(b.chunks) != 0 { last := b.chunks[len(b.chunks)-1] if b.w < len(last) { return last } } chunk := getDataBufferChunk(want) b.chunks = append(b.chunks, chunk) b.w = 0 return chunk } ================================================ FILE: vendor/golang.org/x/net/http2/errors.go ================================================ // Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package http2 import ( "errors" "fmt" ) // An ErrCode is an unsigned 32-bit error code as defined in the HTTP/2 spec. type ErrCode uint32 const ( ErrCodeNo ErrCode = 0x0 ErrCodeProtocol ErrCode = 0x1 ErrCodeInternal ErrCode = 0x2 ErrCodeFlowControl ErrCode = 0x3 ErrCodeSettingsTimeout ErrCode = 0x4 ErrCodeStreamClosed ErrCode = 0x5 ErrCodeFrameSize ErrCode = 0x6 ErrCodeRefusedStream ErrCode = 0x7 ErrCodeCancel ErrCode = 0x8 ErrCodeCompression ErrCode = 0x9 ErrCodeConnect ErrCode = 0xa ErrCodeEnhanceYourCalm ErrCode = 0xb ErrCodeInadequateSecurity ErrCode = 0xc ErrCodeHTTP11Required ErrCode = 0xd ) var errCodeName = map[ErrCode]string{ ErrCodeNo: "NO_ERROR", ErrCodeProtocol: "PROTOCOL_ERROR", ErrCodeInternal: "INTERNAL_ERROR", ErrCodeFlowControl: "FLOW_CONTROL_ERROR", ErrCodeSettingsTimeout: "SETTINGS_TIMEOUT", ErrCodeStreamClosed: "STREAM_CLOSED", ErrCodeFrameSize: "FRAME_SIZE_ERROR", ErrCodeRefusedStream: "REFUSED_STREAM", ErrCodeCancel: "CANCEL", ErrCodeCompression: "COMPRESSION_ERROR", ErrCodeConnect: "CONNECT_ERROR", ErrCodeEnhanceYourCalm: "ENHANCE_YOUR_CALM", ErrCodeInadequateSecurity: "INADEQUATE_SECURITY", ErrCodeHTTP11Required: "HTTP_1_1_REQUIRED", } func (e ErrCode) String() string { if s, ok := errCodeName[e]; ok { return s } return fmt.Sprintf("unknown error code 0x%x", uint32(e)) } func (e ErrCode) stringToken() string { if s, ok := errCodeName[e]; ok { return s } return fmt.Sprintf("ERR_UNKNOWN_%d", uint32(e)) } // ConnectionError is an error that results in the termination of the // entire connection. type ConnectionError ErrCode func (e ConnectionError) Error() string { return fmt.Sprintf("connection error: %s", ErrCode(e)) } // StreamError is an error that only affects one stream within an // HTTP/2 connection. type StreamError struct { StreamID uint32 Code ErrCode Cause error // optional additional detail } // errFromPeer is a sentinel error value for StreamError.Cause to // indicate that the StreamError was sent from the peer over the wire // and wasn't locally generated in the Transport. var errFromPeer = errors.New("received from peer") func streamError(id uint32, code ErrCode) StreamError { return StreamError{StreamID: id, Code: code} } func (e StreamError) Error() string { if e.Cause != nil { return fmt.Sprintf("stream error: stream ID %d; %v; %v", e.StreamID, e.Code, e.Cause) } return fmt.Sprintf("stream error: stream ID %d; %v", e.StreamID, e.Code) } // 6.9.1 The Flow Control Window // "If a sender receives a WINDOW_UPDATE that causes a flow control // window to exceed this maximum it MUST terminate either the stream // or the connection, as appropriate. For streams, [...]; for the // connection, a GOAWAY frame with a FLOW_CONTROL_ERROR code." type goAwayFlowError struct{} func (goAwayFlowError) Error() string { return "connection exceeded flow control window size" } // connError represents an HTTP/2 ConnectionError error code, along // with a string (for debugging) explaining why. // // Errors of this type are only returned by the frame parser functions // and converted into ConnectionError(Code), after stashing away // the Reason into the Framer's errDetail field, accessible via // the (*Framer).ErrorDetail method. type connError struct { Code ErrCode // the ConnectionError error code Reason string // additional reason } func (e connError) Error() string { return fmt.Sprintf("http2: connection error: %v: %v", e.Code, e.Reason) } type pseudoHeaderError string func (e pseudoHeaderError) Error() string { return fmt.Sprintf("invalid pseudo-header %q", string(e)) } type duplicatePseudoHeaderError string func (e duplicatePseudoHeaderError) Error() string { return fmt.Sprintf("duplicate pseudo-header %q", string(e)) } type headerFieldNameError string func (e headerFieldNameError) Error() string { return fmt.Sprintf("invalid header field name %q", string(e)) } type headerFieldValueError string func (e headerFieldValueError) Error() string { return fmt.Sprintf("invalid header field value for %q", string(e)) } var ( errMixPseudoHeaderTypes = errors.New("mix of request and response pseudo headers") errPseudoAfterRegular = errors.New("pseudo header field after regular") ) ================================================ FILE: vendor/golang.org/x/net/http2/flow.go ================================================ // Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Flow control package http2 // inflowMinRefresh is the minimum number of bytes we'll send for a // flow control window update. const inflowMinRefresh = 4 << 10 // inflow accounts for an inbound flow control window. // It tracks both the latest window sent to the peer (used for enforcement) // and the accumulated unsent window. type inflow struct { avail int32 unsent int32 } // init sets the initial window. func (f *inflow) init(n int32) { f.avail = n } // add adds n bytes to the window, with a maximum window size of max, // indicating that the peer can now send us more data. // For example, the user read from a {Request,Response} body and consumed // some of the buffered data, so the peer can now send more. // It returns the number of bytes to send in a WINDOW_UPDATE frame to the peer. // Window updates are accumulated and sent when the unsent capacity // is at least inflowMinRefresh or will at least double the peer's available window. func (f *inflow) add(n int) (connAdd int32) { if n < 0 { panic("negative update") } unsent := int64(f.unsent) + int64(n) // "A sender MUST NOT allow a flow-control window to exceed 2^31-1 octets." // RFC 7540 Section 6.9.1. const maxWindow = 1<<31 - 1 if unsent+int64(f.avail) > maxWindow { panic("flow control update exceeds maximum window size") } f.unsent = int32(unsent) if f.unsent < inflowMinRefresh && f.unsent < f.avail { // If there aren't at least inflowMinRefresh bytes of window to send, // and this update won't at least double the window, buffer the update for later. return 0 } f.avail += f.unsent f.unsent = 0 return int32(unsent) } // take attempts to take n bytes from the peer's flow control window. // It reports whether the window has available capacity. func (f *inflow) take(n uint32) bool { if n > uint32(f.avail) { return false } f.avail -= int32(n) return true } // takeInflows attempts to take n bytes from two inflows, // typically connection-level and stream-level flows. // It reports whether both windows have available capacity. func takeInflows(f1, f2 *inflow, n uint32) bool { if n > uint32(f1.avail) || n > uint32(f2.avail) { return false } f1.avail -= int32(n) f2.avail -= int32(n) return true } // outflow is the outbound flow control window's size. type outflow struct { _ incomparable // n is the number of DATA bytes we're allowed to send. // An outflow is kept both on a conn and a per-stream. n int32 // conn points to the shared connection-level outflow that is // shared by all streams on that conn. It is nil for the outflow // that's on the conn directly. conn *outflow } func (f *outflow) setConnFlow(cf *outflow) { f.conn = cf } func (f *outflow) available() int32 { n := f.n if f.conn != nil && f.conn.n < n { n = f.conn.n } return n } func (f *outflow) take(n int32) { if n > f.available() { panic("internal error: took too much") } f.n -= n if f.conn != nil { f.conn.n -= n } } // add adds n bytes (positive or negative) to the flow control window. // It returns false if the sum would exceed 2^31-1. func (f *outflow) add(n int32) bool { sum := f.n + n if (sum > n) == (f.n > 0) { f.n = sum return true } return false } ================================================ FILE: vendor/golang.org/x/net/http2/frame.go ================================================ // Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package http2 import ( "bytes" "encoding/binary" "errors" "fmt" "io" "log" "strings" "sync" "golang.org/x/net/http/httpguts" "golang.org/x/net/http2/hpack" ) const frameHeaderLen = 9 var padZeros = make([]byte, 255) // zeros for padding // A FrameType is a registered frame type as defined in // https://httpwg.org/specs/rfc7540.html#rfc.section.11.2 type FrameType uint8 const ( FrameData FrameType = 0x0 FrameHeaders FrameType = 0x1 FramePriority FrameType = 0x2 FrameRSTStream FrameType = 0x3 FrameSettings FrameType = 0x4 FramePushPromise FrameType = 0x5 FramePing FrameType = 0x6 FrameGoAway FrameType = 0x7 FrameWindowUpdate FrameType = 0x8 FrameContinuation FrameType = 0x9 ) var frameNames = [...]string{ FrameData: "DATA", FrameHeaders: "HEADERS", FramePriority: "PRIORITY", FrameRSTStream: "RST_STREAM", FrameSettings: "SETTINGS", FramePushPromise: "PUSH_PROMISE", FramePing: "PING", FrameGoAway: "GOAWAY", FrameWindowUpdate: "WINDOW_UPDATE", FrameContinuation: "CONTINUATION", } func (t FrameType) String() string { if int(t) < len(frameNames) { return frameNames[t] } return fmt.Sprintf("UNKNOWN_FRAME_TYPE_%d", t) } // Flags is a bitmask of HTTP/2 flags. // The meaning of flags varies depending on the frame type. type Flags uint8 // Has reports whether f contains all (0 or more) flags in v. func (f Flags) Has(v Flags) bool { return (f & v) == v } // Frame-specific FrameHeader flag bits. const ( // Data Frame FlagDataEndStream Flags = 0x1 FlagDataPadded Flags = 0x8 // Headers Frame FlagHeadersEndStream Flags = 0x1 FlagHeadersEndHeaders Flags = 0x4 FlagHeadersPadded Flags = 0x8 FlagHeadersPriority Flags = 0x20 // Settings Frame FlagSettingsAck Flags = 0x1 // Ping Frame FlagPingAck Flags = 0x1 // Continuation Frame FlagContinuationEndHeaders Flags = 0x4 FlagPushPromiseEndHeaders Flags = 0x4 FlagPushPromisePadded Flags = 0x8 ) var flagName = map[FrameType]map[Flags]string{ FrameData: { FlagDataEndStream: "END_STREAM", FlagDataPadded: "PADDED", }, FrameHeaders: { FlagHeadersEndStream: "END_STREAM", FlagHeadersEndHeaders: "END_HEADERS", FlagHeadersPadded: "PADDED", FlagHeadersPriority: "PRIORITY", }, FrameSettings: { FlagSettingsAck: "ACK", }, FramePing: { FlagPingAck: "ACK", }, FrameContinuation: { FlagContinuationEndHeaders: "END_HEADERS", }, FramePushPromise: { FlagPushPromiseEndHeaders: "END_HEADERS", FlagPushPromisePadded: "PADDED", }, } // a frameParser parses a frame given its FrameHeader and payload // bytes. The length of payload will always equal fh.Length (which // might be 0). type frameParser func(fc *frameCache, fh FrameHeader, countError func(string), payload []byte) (Frame, error) var frameParsers = [...]frameParser{ FrameData: parseDataFrame, FrameHeaders: parseHeadersFrame, FramePriority: parsePriorityFrame, FrameRSTStream: parseRSTStreamFrame, FrameSettings: parseSettingsFrame, FramePushPromise: parsePushPromise, FramePing: parsePingFrame, FrameGoAway: parseGoAwayFrame, FrameWindowUpdate: parseWindowUpdateFrame, FrameContinuation: parseContinuationFrame, } func typeFrameParser(t FrameType) frameParser { if int(t) < len(frameParsers) { return frameParsers[t] } return parseUnknownFrame } // A FrameHeader is the 9 byte header of all HTTP/2 frames. // // See https://httpwg.org/specs/rfc7540.html#FrameHeader type FrameHeader struct { valid bool // caller can access []byte fields in the Frame // Type is the 1 byte frame type. There are ten standard frame // types, but extension frame types may be written by WriteRawFrame // and will be returned by ReadFrame (as UnknownFrame). Type FrameType // Flags are the 1 byte of 8 potential bit flags per frame. // They are specific to the frame type. Flags Flags // Length is the length of the frame, not including the 9 byte header. // The maximum size is one byte less than 16MB (uint24), but only // frames up to 16KB are allowed without peer agreement. Length uint32 // StreamID is which stream this frame is for. Certain frames // are not stream-specific, in which case this field is 0. StreamID uint32 } // Header returns h. It exists so FrameHeaders can be embedded in other // specific frame types and implement the Frame interface. func (h FrameHeader) Header() FrameHeader { return h } func (h FrameHeader) String() string { var buf bytes.Buffer buf.WriteString("[FrameHeader ") h.writeDebug(&buf) buf.WriteByte(']') return buf.String() } func (h FrameHeader) writeDebug(buf *bytes.Buffer) { buf.WriteString(h.Type.String()) if h.Flags != 0 { buf.WriteString(" flags=") set := 0 for i := uint8(0); i < 8; i++ { if h.Flags&(1<<i) == 0 { continue } set++ if set > 1 { buf.WriteByte('|') } name := flagName[h.Type][Flags(1<<i)] if name != "" { buf.WriteString(name) } else { fmt.Fprintf(buf, "0x%x", 1<<i) } } } if h.StreamID != 0 { fmt.Fprintf(buf, " stream=%d", h.StreamID) } fmt.Fprintf(buf, " len=%d", h.Length) } func (h *FrameHeader) checkValid() { if !h.valid { panic("Frame accessor called on non-owned Frame") } } func (h *FrameHeader) invalidate() { h.valid = false } // frame header bytes. // Used only by ReadFrameHeader. var fhBytes = sync.Pool{ New: func() interface{} { buf := make([]byte, frameHeaderLen) return &buf }, } func invalidHTTP1LookingFrameHeader() FrameHeader { fh, _ := readFrameHeader(make([]byte, frameHeaderLen), strings.NewReader("HTTP/1.1 ")) return fh } // ReadFrameHeader reads 9 bytes from r and returns a FrameHeader. // Most users should use Framer.ReadFrame instead. func ReadFrameHeader(r io.Reader) (FrameHeader, error) { bufp := fhBytes.Get().(*[]byte) defer fhBytes.Put(bufp) return readFrameHeader(*bufp, r) } func readFrameHeader(buf []byte, r io.Reader) (FrameHeader, error) { _, err := io.ReadFull(r, buf[:frameHeaderLen]) if err != nil { return FrameHeader{}, err } return FrameHeader{ Length: (uint32(buf[0])<<16 | uint32(buf[1])<<8 | uint32(buf[2])), Type: FrameType(buf[3]), Flags: Flags(buf[4]), StreamID: binary.BigEndian.Uint32(buf[5:]) & (1<<31 - 1), valid: true, }, nil } // A Frame is the base interface implemented by all frame types. // Callers will generally type-assert the specific frame type: // *HeadersFrame, *SettingsFrame, *WindowUpdateFrame, etc. // // Frames are only valid until the next call to Framer.ReadFrame. type Frame interface { Header() FrameHeader // invalidate is called by Framer.ReadFrame to make this // frame's buffers as being invalid, since the subsequent // frame will reuse them. invalidate() } // A Framer reads and writes Frames. type Framer struct { r io.Reader lastFrame Frame errDetail error // countError is a non-nil func that's called on a frame parse // error with some unique error path token. It's initialized // from Transport.CountError or Server.CountError. countError func(errToken string) // lastHeaderStream is non-zero if the last frame was an // unfinished HEADERS/CONTINUATION. lastHeaderStream uint32 // lastFrameType holds the type of the last frame for verifying frame order. lastFrameType FrameType maxReadSize uint32 headerBuf [frameHeaderLen]byte // TODO: let getReadBuf be configurable, and use a less memory-pinning // allocator in server.go to minimize memory pinned for many idle conns. // Will probably also need to make frame invalidation have a hook too. getReadBuf func(size uint32) []byte readBuf []byte // cache for default getReadBuf maxWriteSize uint32 // zero means unlimited; TODO: implement w io.Writer wbuf []byte // AllowIllegalWrites permits the Framer's Write methods to // write frames that do not conform to the HTTP/2 spec. This // permits using the Framer to test other HTTP/2 // implementations' conformance to the spec. // If false, the Write methods will prefer to return an error // rather than comply. AllowIllegalWrites bool // AllowIllegalReads permits the Framer's ReadFrame method // to return non-compliant frames or frame orders. // This is for testing and permits using the Framer to test // other HTTP/2 implementations' conformance to the spec. // It is not compatible with ReadMetaHeaders. AllowIllegalReads bool // ReadMetaHeaders if non-nil causes ReadFrame to merge // HEADERS and CONTINUATION frames together and return // MetaHeadersFrame instead. ReadMetaHeaders *hpack.Decoder // MaxHeaderListSize is the http2 MAX_HEADER_LIST_SIZE. // It's used only if ReadMetaHeaders is set; 0 means a sane default // (currently 16MB) // If the limit is hit, MetaHeadersFrame.Truncated is set true. MaxHeaderListSize uint32 // TODO: track which type of frame & with which flags was sent // last. Then return an error (unless AllowIllegalWrites) if // we're in the middle of a header block and a // non-Continuation or Continuation on a different stream is // attempted to be written. logReads, logWrites bool debugFramer *Framer // only use for logging written writes debugFramerBuf *bytes.Buffer debugReadLoggerf func(string, ...interface{}) debugWriteLoggerf func(string, ...interface{}) frameCache *frameCache // nil if frames aren't reused (default) } func (fr *Framer) maxHeaderListSize() uint32 { if fr.MaxHeaderListSize == 0 { return 16 << 20 // sane default, per docs } return fr.MaxHeaderListSize } func (f *Framer) startWrite(ftype FrameType, flags Flags, streamID uint32) { // Write the FrameHeader. f.wbuf = append(f.wbuf[:0], 0, // 3 bytes of length, filled in endWrite 0, 0, byte(ftype), byte(flags), byte(streamID>>24), byte(streamID>>16), byte(streamID>>8), byte(streamID)) } func (f *Framer) endWrite() error { // Now that we know the final size, fill in the FrameHeader in // the space previously reserved for it. Abuse append. length := len(f.wbuf) - frameHeaderLen if length >= (1 << 24) { return ErrFrameTooLarge } _ = append(f.wbuf[:0], byte(length>>16), byte(length>>8), byte(length)) if f.logWrites { f.logWrite() } n, err := f.w.Write(f.wbuf) if err == nil && n != len(f.wbuf) { err = io.ErrShortWrite } return err } func (f *Framer) logWrite() { if f.debugFramer == nil { f.debugFramerBuf = new(bytes.Buffer) f.debugFramer = NewFramer(nil, f.debugFramerBuf) f.debugFramer.logReads = false // we log it ourselves, saying "wrote" below // Let us read anything, even if we accidentally wrote it // in the wrong order: f.debugFramer.AllowIllegalReads = true } f.debugFramerBuf.Write(f.wbuf) fr, err := f.debugFramer.ReadFrame() if err != nil { f.debugWriteLoggerf("http2: Framer %p: failed to decode just-written frame", f) return } f.debugWriteLoggerf("http2: Framer %p: wrote %v", f, summarizeFrame(fr)) } func (f *Framer) writeByte(v byte) { f.wbuf = append(f.wbuf, v) } func (f *Framer) writeBytes(v []byte) { f.wbuf = append(f.wbuf, v...) } func (f *Framer) writeUint16(v uint16) { f.wbuf = append(f.wbuf, byte(v>>8), byte(v)) } func (f *Framer) writeUint32(v uint32) { f.wbuf = append(f.wbuf, byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) } const ( minMaxFrameSize = 1 << 14 maxFrameSize = 1<<24 - 1 ) // SetReuseFrames allows the Framer to reuse Frames. // If called on a Framer, Frames returned by calls to ReadFrame are only // valid until the next call to ReadFrame. func (fr *Framer) SetReuseFrames() { if fr.frameCache != nil { return } fr.frameCache = &frameCache{} } type frameCache struct { dataFrame DataFrame } func (fc *frameCache) getDataFrame() *DataFrame { if fc == nil { return &DataFrame{} } return &fc.dataFrame } // NewFramer returns a Framer that writes frames to w and reads them from r. func NewFramer(w io.Writer, r io.Reader) *Framer { fr := &Framer{ w: w, r: r, countError: func(string) {}, logReads: logFrameReads, logWrites: logFrameWrites, debugReadLoggerf: log.Printf, debugWriteLoggerf: log.Printf, } fr.getReadBuf = func(size uint32) []byte { if cap(fr.readBuf) >= int(size) { return fr.readBuf[:size] } fr.readBuf = make([]byte, size) return fr.readBuf } fr.SetMaxReadFrameSize(maxFrameSize) return fr } // SetMaxReadFrameSize sets the maximum size of a frame // that will be read by a subsequent call to ReadFrame. // It is the caller's responsibility to advertise this // limit with a SETTINGS frame. func (fr *Framer) SetMaxReadFrameSize(v uint32) { if v > maxFrameSize { v = maxFrameSize } fr.maxReadSize = v } // ErrorDetail returns a more detailed error of the last error // returned by Framer.ReadFrame. For instance, if ReadFrame // returns a StreamError with code PROTOCOL_ERROR, ErrorDetail // will say exactly what was invalid. ErrorDetail is not guaranteed // to return a non-nil value and like the rest of the http2 package, // its return value is not protected by an API compatibility promise. // ErrorDetail is reset after the next call to ReadFrame. func (fr *Framer) ErrorDetail() error { return fr.errDetail } // ErrFrameTooLarge is returned from Framer.ReadFrame when the peer // sends a frame that is larger than declared with SetMaxReadFrameSize. var ErrFrameTooLarge = errors.New("http2: frame too large") // terminalReadFrameError reports whether err is an unrecoverable // error from ReadFrame and no other frames should be read. func terminalReadFrameError(err error) bool { if _, ok := err.(StreamError); ok { return false } return err != nil } // ReadFrameHeader reads the header of the next frame. // It reads the 9-byte fixed frame header, and does not read any portion of the // frame payload. The caller is responsible for consuming the payload, either // with ReadFrameForHeader or directly from the Framer's io.Reader. // // If the frame is larger than previously set with SetMaxReadFrameSize, it // returns the frame header and ErrFrameTooLarge. // // If the returned FrameHeader.StreamID is non-zero, it indicates the stream // responsible for the error. func (fr *Framer) ReadFrameHeader() (FrameHeader, error) { fr.errDetail = nil fh, err := readFrameHeader(fr.headerBuf[:], fr.r) if err != nil { return fh, err } if fh.Length > fr.maxReadSize { if fh == invalidHTTP1LookingFrameHeader() { return fh, fmt.Errorf("http2: failed reading the frame payload: %w, note that the frame header looked like an HTTP/1.1 header", ErrFrameTooLarge) } return fh, ErrFrameTooLarge } if err := fr.checkFrameOrder(fh); err != nil { return fh, err } return fh, nil } // ReadFrameForHeader reads the payload for the frame with the given FrameHeader. // // It behaves identically to ReadFrame, other than not checking the maximum // frame size. func (fr *Framer) ReadFrameForHeader(fh FrameHeader) (Frame, error) { if fr.lastFrame != nil { fr.lastFrame.invalidate() } payload := fr.getReadBuf(fh.Length) if _, err := io.ReadFull(fr.r, payload); err != nil { if fh == invalidHTTP1LookingFrameHeader() { return nil, fmt.Errorf("http2: failed reading the frame payload: %w, note that the frame header looked like an HTTP/1.1 header", err) } return nil, err } f, err := typeFrameParser(fh.Type)(fr.frameCache, fh, fr.countError, payload) if err != nil { if ce, ok := err.(connError); ok { return nil, fr.connError(ce.Code, ce.Reason) } return nil, err } fr.lastFrame = f if fr.logReads { fr.debugReadLoggerf("http2: Framer %p: read %v", fr, summarizeFrame(f)) } if fh.Type == FrameHeaders && fr.ReadMetaHeaders != nil { return fr.readMetaFrame(f.(*HeadersFrame)) } return f, nil } // ReadFrame reads a single frame. The returned Frame is only valid // until the next call to ReadFrame or ReadFrameBodyForHeader. // // If the frame is larger than previously set with SetMaxReadFrameSize, the // returned error is ErrFrameTooLarge. Other errors may be of type // ConnectionError, StreamError, or anything else from the underlying // reader. // // If ReadFrame returns an error and a non-nil Frame, the Frame's StreamID // indicates the stream responsible for the error. func (fr *Framer) ReadFrame() (Frame, error) { fh, err := fr.ReadFrameHeader() if err != nil { return nil, err } return fr.ReadFrameForHeader(fh) } // connError returns ConnectionError(code) but first // stashes away a public reason to the caller can optionally relay it // to the peer before hanging up on them. This might help others debug // their implementations. func (fr *Framer) connError(code ErrCode, reason string) error { fr.errDetail = errors.New(reason) return ConnectionError(code) } // checkFrameOrder reports an error if f is an invalid frame to return // next from ReadFrame. Mostly it checks whether HEADERS and // CONTINUATION frames are contiguous. func (fr *Framer) checkFrameOrder(fh FrameHeader) error { lastType := fr.lastFrameType fr.lastFrameType = fh.Type if fr.AllowIllegalReads { return nil } if fr.lastHeaderStream != 0 { if fh.Type != FrameContinuation { return fr.connError(ErrCodeProtocol, fmt.Sprintf("got %s for stream %d; expected CONTINUATION following %s for stream %d", fh.Type, fh.StreamID, lastType, fr.lastHeaderStream)) } if fh.StreamID != fr.lastHeaderStream { return fr.connError(ErrCodeProtocol, fmt.Sprintf("got CONTINUATION for stream %d; expected stream %d", fh.StreamID, fr.lastHeaderStream)) } } else if fh.Type == FrameContinuation { return fr.connError(ErrCodeProtocol, fmt.Sprintf("unexpected CONTINUATION for stream %d", fh.StreamID)) } switch fh.Type { case FrameHeaders, FrameContinuation: if fh.Flags.Has(FlagHeadersEndHeaders) { fr.lastHeaderStream = 0 } else { fr.lastHeaderStream = fh.StreamID } } return nil } // A DataFrame conveys arbitrary, variable-length sequences of octets // associated with a stream. // See https://httpwg.org/specs/rfc7540.html#rfc.section.6.1 type DataFrame struct { FrameHeader data []byte } func (f *DataFrame) StreamEnded() bool { return f.FrameHeader.Flags.Has(FlagDataEndStream) } // Data returns the frame's data octets, not including any padding // size byte or padding suffix bytes. // The caller must not retain the returned memory past the next // call to ReadFrame. func (f *DataFrame) Data() []byte { f.checkValid() return f.data } func parseDataFrame(fc *frameCache, fh FrameHeader, countError func(string), payload []byte) (Frame, error) { if fh.StreamID == 0 { // DATA frames MUST be associated with a stream. If a // DATA frame is received whose stream identifier // field is 0x0, the recipient MUST respond with a // connection error (Section 5.4.1) of type // PROTOCOL_ERROR. countError("frame_data_stream_0") return nil, connError{ErrCodeProtocol, "DATA frame with stream ID 0"} } f := fc.getDataFrame() f.FrameHeader = fh var padSize byte if fh.Flags.Has(FlagDataPadded) { var err error payload, padSize, err = readByte(payload) if err != nil { countError("frame_data_pad_byte_short") return nil, err } } if int(padSize) > len(payload) { // If the length of the padding is greater than the // length of the frame payload, the recipient MUST // treat this as a connection error. // Filed: https://github.com/http2/http2-spec/issues/610 countError("frame_data_pad_too_big") return nil, connError{ErrCodeProtocol, "pad size larger than data payload"} } f.data = payload[:len(payload)-int(padSize)] return f, nil } var ( errStreamID = errors.New("invalid stream ID") errDepStreamID = errors.New("invalid dependent stream ID") errPadLength = errors.New("pad length too large") errPadBytes = errors.New("padding bytes must all be zeros unless AllowIllegalWrites is enabled") ) func validStreamIDOrZero(streamID uint32) bool { return streamID&(1<<31) == 0 } func validStreamID(streamID uint32) bool { return streamID != 0 && streamID&(1<<31) == 0 } // WriteData writes a DATA frame. // // It will perform exactly one Write to the underlying Writer. // It is the caller's responsibility not to violate the maximum frame size // and to not call other Write methods concurrently. func (f *Framer) WriteData(streamID uint32, endStream bool, data []byte) error { return f.WriteDataPadded(streamID, endStream, data, nil) } // WriteDataPadded writes a DATA frame with optional padding. // // If pad is nil, the padding bit is not sent. // The length of pad must not exceed 255 bytes. // The bytes of pad must all be zero, unless f.AllowIllegalWrites is set. // // It will perform exactly one Write to the underlying Writer. // It is the caller's responsibility not to violate the maximum frame size // and to not call other Write methods concurrently. func (f *Framer) WriteDataPadded(streamID uint32, endStream bool, data, pad []byte) error { if err := f.startWriteDataPadded(streamID, endStream, data, pad); err != nil { return err } return f.endWrite() } // startWriteDataPadded is WriteDataPadded, but only writes the frame to the Framer's internal buffer. // The caller should call endWrite to flush the frame to the underlying writer. func (f *Framer) startWriteDataPadded(streamID uint32, endStream bool, data, pad []byte) error { if !validStreamID(streamID) && !f.AllowIllegalWrites { return errStreamID } if len(pad) > 0 { if len(pad) > 255 { return errPadLength } if !f.AllowIllegalWrites { for _, b := range pad { if b != 0 { // "Padding octets MUST be set to zero when sending." return errPadBytes } } } } var flags Flags if endStream { flags |= FlagDataEndStream } if pad != nil { flags |= FlagDataPadded } f.startWrite(FrameData, flags, streamID) if pad != nil { f.wbuf = append(f.wbuf, byte(len(pad))) } f.wbuf = append(f.wbuf, data...) f.wbuf = append(f.wbuf, pad...) return nil } // A SettingsFrame conveys configuration parameters that affect how // endpoints communicate, such as preferences and constraints on peer // behavior. // // See https://httpwg.org/specs/rfc7540.html#SETTINGS type SettingsFrame struct { FrameHeader p []byte } func parseSettingsFrame(_ *frameCache, fh FrameHeader, countError func(string), p []byte) (Frame, error) { if fh.Flags.Has(FlagSettingsAck) && fh.Length > 0 { // When this (ACK 0x1) bit is set, the payload of the // SETTINGS frame MUST be empty. Receipt of a // SETTINGS frame with the ACK flag set and a length // field value other than 0 MUST be treated as a // connection error (Section 5.4.1) of type // FRAME_SIZE_ERROR. countError("frame_settings_ack_with_length") return nil, ConnectionError(ErrCodeFrameSize) } if fh.StreamID != 0 { // SETTINGS frames always apply to a connection, // never a single stream. The stream identifier for a // SETTINGS frame MUST be zero (0x0). If an endpoint // receives a SETTINGS frame whose stream identifier // field is anything other than 0x0, the endpoint MUST // respond with a connection error (Section 5.4.1) of // type PROTOCOL_ERROR. countError("frame_settings_has_stream") return nil, ConnectionError(ErrCodeProtocol) } if len(p)%6 != 0 { countError("frame_settings_mod_6") // Expecting even number of 6 byte settings. return nil, ConnectionError(ErrCodeFrameSize) } f := &SettingsFrame{FrameHeader: fh, p: p} if v, ok := f.Value(SettingInitialWindowSize); ok && v > (1<<31)-1 { countError("frame_settings_window_size_too_big") // Values above the maximum flow control window size of 2^31 - 1 MUST // be treated as a connection error (Section 5.4.1) of type // FLOW_CONTROL_ERROR. return nil, ConnectionError(ErrCodeFlowControl) } return f, nil } func (f *SettingsFrame) IsAck() bool { return f.FrameHeader.Flags.Has(FlagSettingsAck) } func (f *SettingsFrame) Value(id SettingID) (v uint32, ok bool) { f.checkValid() for i := 0; i < f.NumSettings(); i++ { if s := f.Setting(i); s.ID == id { return s.Val, true } } return 0, false } // Setting returns the setting from the frame at the given 0-based index. // The index must be >= 0 and less than f.NumSettings(). func (f *SettingsFrame) Setting(i int) Setting { buf := f.p return Setting{ ID: SettingID(binary.BigEndian.Uint16(buf[i*6 : i*6+2])), Val: binary.BigEndian.Uint32(buf[i*6+2 : i*6+6]), } } func (f *SettingsFrame) NumSettings() int { return len(f.p) / 6 } // HasDuplicates reports whether f contains any duplicate setting IDs. func (f *SettingsFrame) HasDuplicates() bool { num := f.NumSettings() if num == 0 { return false } // If it's small enough (the common case), just do the n^2 // thing and avoid a map allocation. if num < 10 { for i := 0; i < num; i++ { idi := f.Setting(i).ID for j := i + 1; j < num; j++ { idj := f.Setting(j).ID if idi == idj { return true } } } return false } seen := map[SettingID]bool{} for i := 0; i < num; i++ { id := f.Setting(i).ID if seen[id] { return true } seen[id] = true } return false } // ForeachSetting runs fn for each setting. // It stops and returns the first error. func (f *SettingsFrame) ForeachSetting(fn func(Setting) error) error { f.checkValid() for i := 0; i < f.NumSettings(); i++ { if err := fn(f.Setting(i)); err != nil { return err } } return nil } // WriteSettings writes a SETTINGS frame with zero or more settings // specified and the ACK bit not set. // // It will perform exactly one Write to the underlying Writer. // It is the caller's responsibility to not call other Write methods concurrently. func (f *Framer) WriteSettings(settings ...Setting) error { f.startWrite(FrameSettings, 0, 0) for _, s := range settings { f.writeUint16(uint16(s.ID)) f.writeUint32(s.Val) } return f.endWrite() } // WriteSettingsAck writes an empty SETTINGS frame with the ACK bit set. // // It will perform exactly one Write to the underlying Writer. // It is the caller's responsibility to not call other Write methods concurrently. func (f *Framer) WriteSettingsAck() error { f.startWrite(FrameSettings, FlagSettingsAck, 0) return f.endWrite() } // A PingFrame is a mechanism for measuring a minimal round trip time // from the sender, as well as determining whether an idle connection // is still functional. // See https://httpwg.org/specs/rfc7540.html#rfc.section.6.7 type PingFrame struct { FrameHeader Data [8]byte } func (f *PingFrame) IsAck() bool { return f.Flags.Has(FlagPingAck) } func parsePingFrame(_ *frameCache, fh FrameHeader, countError func(string), payload []byte) (Frame, error) { if len(payload) != 8 { countError("frame_ping_length") return nil, ConnectionError(ErrCodeFrameSize) } if fh.StreamID != 0 { countError("frame_ping_has_stream") return nil, ConnectionError(ErrCodeProtocol) } f := &PingFrame{FrameHeader: fh} copy(f.Data[:], payload) return f, nil } func (f *Framer) WritePing(ack bool, data [8]byte) error { var flags Flags if ack { flags = FlagPingAck } f.startWrite(FramePing, flags, 0) f.writeBytes(data[:]) return f.endWrite() } // A GoAwayFrame informs the remote peer to stop creating streams on this connection. // See https://httpwg.org/specs/rfc7540.html#rfc.section.6.8 type GoAwayFrame struct { FrameHeader LastStreamID uint32 ErrCode ErrCode debugData []byte } // DebugData returns any debug data in the GOAWAY frame. Its contents // are not defined. // The caller must not retain the returned memory past the next // call to ReadFrame. func (f *GoAwayFrame) DebugData() []byte { f.checkValid() return f.debugData } func parseGoAwayFrame(_ *frameCache, fh FrameHeader, countError func(string), p []byte) (Frame, error) { if fh.StreamID != 0 { countError("frame_goaway_has_stream") return nil, ConnectionError(ErrCodeProtocol) } if len(p) < 8 { countError("frame_goaway_short") return nil, ConnectionError(ErrCodeFrameSize) } return &GoAwayFrame{ FrameHeader: fh, LastStreamID: binary.BigEndian.Uint32(p[:4]) & (1<<31 - 1), ErrCode: ErrCode(binary.BigEndian.Uint32(p[4:8])), debugData: p[8:], }, nil } func (f *Framer) WriteGoAway(maxStreamID uint32, code ErrCode, debugData []byte) error { f.startWrite(FrameGoAway, 0, 0) f.writeUint32(maxStreamID & (1<<31 - 1)) f.writeUint32(uint32(code)) f.writeBytes(debugData) return f.endWrite() } // An UnknownFrame is the frame type returned when the frame type is unknown // or no specific frame type parser exists. type UnknownFrame struct { FrameHeader p []byte } // Payload returns the frame's payload (after the header). It is not // valid to call this method after a subsequent call to // Framer.ReadFrame, nor is it valid to retain the returned slice. // The memory is owned by the Framer and is invalidated when the next // frame is read. func (f *UnknownFrame) Payload() []byte { f.checkValid() return f.p } func parseUnknownFrame(_ *frameCache, fh FrameHeader, countError func(string), p []byte) (Frame, error) { return &UnknownFrame{fh, p}, nil } // A WindowUpdateFrame is used to implement flow control. // See https://httpwg.org/specs/rfc7540.html#rfc.section.6.9 type WindowUpdateFrame struct { FrameHeader Increment uint32 // never read with high bit set } func parseWindowUpdateFrame(_ *frameCache, fh FrameHeader, countError func(string), p []byte) (Frame, error) { if len(p) != 4 { countError("frame_windowupdate_bad_len") return nil, ConnectionError(ErrCodeFrameSize) } inc := binary.BigEndian.Uint32(p[:4]) & 0x7fffffff // mask off high reserved bit if inc == 0 { // A receiver MUST treat the receipt of a // WINDOW_UPDATE frame with an flow control window // increment of 0 as a stream error (Section 5.4.2) of // type PROTOCOL_ERROR; errors on the connection flow // control window MUST be treated as a connection // error (Section 5.4.1). if fh.StreamID == 0 { countError("frame_windowupdate_zero_inc_conn") return nil, ConnectionError(ErrCodeProtocol) } countError("frame_windowupdate_zero_inc_stream") return nil, streamError(fh.StreamID, ErrCodeProtocol) } return &WindowUpdateFrame{ FrameHeader: fh, Increment: inc, }, nil } // WriteWindowUpdate writes a WINDOW_UPDATE frame. // The increment value must be between 1 and 2,147,483,647, inclusive. // If the Stream ID is zero, the window update applies to the // connection as a whole. func (f *Framer) WriteWindowUpdate(streamID, incr uint32) error { // "The legal range for the increment to the flow control window is 1 to 2^31-1 (2,147,483,647) octets." if (incr < 1 || incr > 2147483647) && !f.AllowIllegalWrites { return errors.New("illegal window increment value") } f.startWrite(FrameWindowUpdate, 0, streamID) f.writeUint32(incr) return f.endWrite() } // A HeadersFrame is used to open a stream and additionally carries a // header block fragment. type HeadersFrame struct { FrameHeader // Priority is set if FlagHeadersPriority is set in the FrameHeader. Priority PriorityParam headerFragBuf []byte // not owned } func (f *HeadersFrame) HeaderBlockFragment() []byte { f.checkValid() return f.headerFragBuf } func (f *HeadersFrame) HeadersEnded() bool { return f.FrameHeader.Flags.Has(FlagHeadersEndHeaders) } func (f *HeadersFrame) StreamEnded() bool { return f.FrameHeader.Flags.Has(FlagHeadersEndStream) } func (f *HeadersFrame) HasPriority() bool { return f.FrameHeader.Flags.Has(FlagHeadersPriority) } func parseHeadersFrame(_ *frameCache, fh FrameHeader, countError func(string), p []byte) (_ Frame, err error) { hf := &HeadersFrame{ FrameHeader: fh, } if fh.StreamID == 0 { // HEADERS frames MUST be associated with a stream. If a HEADERS frame // is received whose stream identifier field is 0x0, the recipient MUST // respond with a connection error (Section 5.4.1) of type // PROTOCOL_ERROR. countError("frame_headers_zero_stream") return nil, connError{ErrCodeProtocol, "HEADERS frame with stream ID 0"} } var padLength uint8 if fh.Flags.Has(FlagHeadersPadded) { if p, padLength, err = readByte(p); err != nil { countError("frame_headers_pad_short") return } } if fh.Flags.Has(FlagHeadersPriority) { var v uint32 p, v, err = readUint32(p) if err != nil { countError("frame_headers_prio_short") return nil, err } hf.Priority.StreamDep = v & 0x7fffffff hf.Priority.Exclusive = (v != hf.Priority.StreamDep) // high bit was set p, hf.Priority.Weight, err = readByte(p) if err != nil { countError("frame_headers_prio_weight_short") return nil, err } } if len(p)-int(padLength) < 0 { countError("frame_headers_pad_too_big") return nil, streamError(fh.StreamID, ErrCodeProtocol) } hf.headerFragBuf = p[:len(p)-int(padLength)] return hf, nil } // HeadersFrameParam are the parameters for writing a HEADERS frame. type HeadersFrameParam struct { // StreamID is the required Stream ID to initiate. StreamID uint32 // BlockFragment is part (or all) of a Header Block. BlockFragment []byte // EndStream indicates that the header block is the last that // the endpoint will send for the identified stream. Setting // this flag causes the stream to enter one of "half closed" // states. EndStream bool // EndHeaders indicates that this frame contains an entire // header block and is not followed by any // CONTINUATION frames. EndHeaders bool // PadLength is the optional number of bytes of zeros to add // to this frame. PadLength uint8 // Priority, if non-zero, includes stream priority information // in the HEADER frame. Priority PriorityParam } // WriteHeaders writes a single HEADERS frame. // // This is a low-level header writing method. Encoding headers and // splitting them into any necessary CONTINUATION frames is handled // elsewhere. // // It will perform exactly one Write to the underlying Writer. // It is the caller's responsibility to not call other Write methods concurrently. func (f *Framer) WriteHeaders(p HeadersFrameParam) error { if !validStreamID(p.StreamID) && !f.AllowIllegalWrites { return errStreamID } var flags Flags if p.PadLength != 0 { flags |= FlagHeadersPadded } if p.EndStream { flags |= FlagHeadersEndStream } if p.EndHeaders { flags |= FlagHeadersEndHeaders } if !p.Priority.IsZero() { flags |= FlagHeadersPriority } f.startWrite(FrameHeaders, flags, p.StreamID) if p.PadLength != 0 { f.writeByte(p.PadLength) } if !p.Priority.IsZero() { v := p.Priority.StreamDep if !validStreamIDOrZero(v) && !f.AllowIllegalWrites { return errDepStreamID } if p.Priority.Exclusive { v |= 1 << 31 } f.writeUint32(v) f.writeByte(p.Priority.Weight) } f.wbuf = append(f.wbuf, p.BlockFragment...) f.wbuf = append(f.wbuf, padZeros[:p.PadLength]...) return f.endWrite() } // A PriorityFrame specifies the sender-advised priority of a stream. // See https://httpwg.org/specs/rfc7540.html#rfc.section.6.3 type PriorityFrame struct { FrameHeader PriorityParam } var defaultRFC9218Priority = PriorityParam{ incremental: 0, urgency: 3, } // Note that HTTP/2 has had two different prioritization schemes, and // PriorityParam struct below is a superset of both schemes. The exported // symbols are from RFC 7540 and the non-exported ones are from RFC 9218. // PriorityParam are the stream prioritization parameters. type PriorityParam struct { // StreamDep is a 31-bit stream identifier for the // stream that this stream depends on. Zero means no // dependency. StreamDep uint32 // Exclusive is whether the dependency is exclusive. Exclusive bool // Weight is the stream's zero-indexed weight. It should be // set together with StreamDep, or neither should be set. Per // the spec, "Add one to the value to obtain a weight between // 1 and 256." Weight uint8 // "The urgency (u) parameter value is Integer (see Section 3.3.1 of // [STRUCTURED-FIELDS]), between 0 and 7 inclusive, in descending order of // priority. The default is 3." urgency uint8 // "The incremental (i) parameter value is Boolean (see Section 3.3.6 of // [STRUCTURED-FIELDS]). It indicates if an HTTP response can be processed // incrementally, i.e., provide some meaningful output as chunks of the // response arrive." // // We use uint8 (i.e. 0 is false, 1 is true) instead of bool so we can // avoid unnecessary type conversions and because either type takes 1 byte. incremental uint8 } func (p PriorityParam) IsZero() bool { return p == PriorityParam{} } func parsePriorityFrame(_ *frameCache, fh FrameHeader, countError func(string), payload []byte) (Frame, error) { if fh.StreamID == 0 { countError("frame_priority_zero_stream") return nil, connError{ErrCodeProtocol, "PRIORITY frame with stream ID 0"} } if len(payload) != 5 { countError("frame_priority_bad_length") return nil, connError{ErrCodeFrameSize, fmt.Sprintf("PRIORITY frame payload size was %d; want 5", len(payload))} } v := binary.BigEndian.Uint32(payload[:4]) streamID := v & 0x7fffffff // mask off high bit return &PriorityFrame{ FrameHeader: fh, PriorityParam: PriorityParam{ Weight: payload[4], StreamDep: streamID, Exclusive: streamID != v, // was high bit set? }, }, nil } // WritePriority writes a PRIORITY frame. // // It will perform exactly one Write to the underlying Writer. // It is the caller's responsibility to not call other Write methods concurrently. func (f *Framer) WritePriority(streamID uint32, p PriorityParam) error { if !validStreamID(streamID) && !f.AllowIllegalWrites { return errStreamID } if !validStreamIDOrZero(p.StreamDep) { return errDepStreamID } f.startWrite(FramePriority, 0, streamID) v := p.StreamDep if p.Exclusive { v |= 1 << 31 } f.writeUint32(v) f.writeByte(p.Weight) return f.endWrite() } // A RSTStreamFrame allows for abnormal termination of a stream. // See https://httpwg.org/specs/rfc7540.html#rfc.section.6.4 type RSTStreamFrame struct { FrameHeader ErrCode ErrCode } func parseRSTStreamFrame(_ *frameCache, fh FrameHeader, countError func(string), p []byte) (Frame, error) { if len(p) != 4 { countError("frame_rststream_bad_len") return nil, ConnectionError(ErrCodeFrameSize) } if fh.StreamID == 0 { countError("frame_rststream_zero_stream") return nil, ConnectionError(ErrCodeProtocol) } return &RSTStreamFrame{fh, ErrCode(binary.BigEndian.Uint32(p[:4]))}, nil } // WriteRSTStream writes a RST_STREAM frame. // // It will perform exactly one Write to the underlying Writer. // It is the caller's responsibility to not call other Write methods concurrently. func (f *Framer) WriteRSTStream(streamID uint32, code ErrCode) error { if !validStreamID(streamID) && !f.AllowIllegalWrites { return errStreamID } f.startWrite(FrameRSTStream, 0, streamID) f.writeUint32(uint32(code)) return f.endWrite() } // A ContinuationFrame is used to continue a sequence of header block fragments. // See https://httpwg.org/specs/rfc7540.html#rfc.section.6.10 type ContinuationFrame struct { FrameHeader headerFragBuf []byte } func parseContinuationFrame(_ *frameCache, fh FrameHeader, countError func(string), p []byte) (Frame, error) { if fh.StreamID == 0 { countError("frame_continuation_zero_stream") return nil, connError{ErrCodeProtocol, "CONTINUATION frame with stream ID 0"} } return &ContinuationFrame{fh, p}, nil } func (f *ContinuationFrame) HeaderBlockFragment() []byte { f.checkValid() return f.headerFragBuf } func (f *ContinuationFrame) HeadersEnded() bool { return f.FrameHeader.Flags.Has(FlagContinuationEndHeaders) } // WriteContinuation writes a CONTINUATION frame. // // It will perform exactly one Write to the underlying Writer. // It is the caller's responsibility to not call other Write methods concurrently. func (f *Framer) WriteContinuation(streamID uint32, endHeaders bool, headerBlockFragment []byte) error { if !validStreamID(streamID) && !f.AllowIllegalWrites { return errStreamID } var flags Flags if endHeaders { flags |= FlagContinuationEndHeaders } f.startWrite(FrameContinuation, flags, streamID) f.wbuf = append(f.wbuf, headerBlockFragment...) return f.endWrite() } // A PushPromiseFrame is used to initiate a server stream. // See https://httpwg.org/specs/rfc7540.html#rfc.section.6.6 type PushPromiseFrame struct { FrameHeader PromiseID uint32 headerFragBuf []byte // not owned } func (f *PushPromiseFrame) HeaderBlockFragment() []byte { f.checkValid() return f.headerFragBuf } func (f *PushPromiseFrame) HeadersEnded() bool { return f.FrameHeader.Flags.Has(FlagPushPromiseEndHeaders) } func parsePushPromise(_ *frameCache, fh FrameHeader, countError func(string), p []byte) (_ Frame, err error) { pp := &PushPromiseFrame{ FrameHeader: fh, } if pp.StreamID == 0 { // PUSH_PROMISE frames MUST be associated with an existing, // peer-initiated stream. The stream identifier of a // PUSH_PROMISE frame indicates the stream it is associated // with. If the stream identifier field specifies the value // 0x0, a recipient MUST respond with a connection error // (Section 5.4.1) of type PROTOCOL_ERROR. countError("frame_pushpromise_zero_stream") return nil, ConnectionError(ErrCodeProtocol) } // The PUSH_PROMISE frame includes optional padding. // Padding fields and flags are identical to those defined for DATA frames var padLength uint8 if fh.Flags.Has(FlagPushPromisePadded) { if p, padLength, err = readByte(p); err != nil { countError("frame_pushpromise_pad_short") return } } p, pp.PromiseID, err = readUint32(p) if err != nil { countError("frame_pushpromise_promiseid_short") return } pp.PromiseID = pp.PromiseID & (1<<31 - 1) if int(padLength) > len(p) { // like the DATA frame, error out if padding is longer than the body. countError("frame_pushpromise_pad_too_big") return nil, ConnectionError(ErrCodeProtocol) } pp.headerFragBuf = p[:len(p)-int(padLength)] return pp, nil } // PushPromiseParam are the parameters for writing a PUSH_PROMISE frame. type PushPromiseParam struct { // StreamID is the required Stream ID to initiate. StreamID uint32 // PromiseID is the required Stream ID which this // Push Promises PromiseID uint32 // BlockFragment is part (or all) of a Header Block. BlockFragment []byte // EndHeaders indicates that this frame contains an entire // header block and is not followed by any // CONTINUATION frames. EndHeaders bool // PadLength is the optional number of bytes of zeros to add // to this frame. PadLength uint8 } // WritePushPromise writes a single PushPromise Frame. // // As with Header Frames, This is the low level call for writing // individual frames. Continuation frames are handled elsewhere. // // It will perform exactly one Write to the underlying Writer. // It is the caller's responsibility to not call other Write methods concurrently. func (f *Framer) WritePushPromise(p PushPromiseParam) error { if !validStreamID(p.StreamID) && !f.AllowIllegalWrites { return errStreamID } var flags Flags if p.PadLength != 0 { flags |= FlagPushPromisePadded } if p.EndHeaders { flags |= FlagPushPromiseEndHeaders } f.startWrite(FramePushPromise, flags, p.StreamID) if p.PadLength != 0 { f.writeByte(p.PadLength) } if !validStreamID(p.PromiseID) && !f.AllowIllegalWrites { return errStreamID } f.writeUint32(p.PromiseID) f.wbuf = append(f.wbuf, p.BlockFragment...) f.wbuf = append(f.wbuf, padZeros[:p.PadLength]...) return f.endWrite() } // WriteRawFrame writes a raw frame. This can be used to write // extension frames unknown to this package. func (f *Framer) WriteRawFrame(t FrameType, flags Flags, streamID uint32, payload []byte) error { f.startWrite(t, flags, streamID) f.writeBytes(payload) return f.endWrite() } func readByte(p []byte) (remain []byte, b byte, err error) { if len(p) == 0 { return nil, 0, io.ErrUnexpectedEOF } return p[1:], p[0], nil } func readUint32(p []byte) (remain []byte, v uint32, err error) { if len(p) < 4 { return nil, 0, io.ErrUnexpectedEOF } return p[4:], binary.BigEndian.Uint32(p[:4]), nil } type streamEnder interface { StreamEnded() bool } type headersEnder interface { HeadersEnded() bool } type headersOrContinuation interface { headersEnder HeaderBlockFragment() []byte } // A MetaHeadersFrame is the representation of one HEADERS frame and // zero or more contiguous CONTINUATION frames and the decoding of // their HPACK-encoded contents. // // This type of frame does not appear on the wire and is only returned // by the Framer when Framer.ReadMetaHeaders is set. type MetaHeadersFrame struct { *HeadersFrame // Fields are the fields contained in the HEADERS and // CONTINUATION frames. The underlying slice is owned by the // Framer and must not be retained after the next call to // ReadFrame. // // Fields are guaranteed to be in the correct http2 order and // not have unknown pseudo header fields or invalid header // field names or values. Required pseudo header fields may be // missing, however. Use the MetaHeadersFrame.Pseudo accessor // method access pseudo headers. Fields []hpack.HeaderField // Truncated is whether the max header list size limit was hit // and Fields is incomplete. The hpack decoder state is still // valid, however. Truncated bool } // PseudoValue returns the given pseudo header field's value. // The provided pseudo field should not contain the leading colon. func (mh *MetaHeadersFrame) PseudoValue(pseudo string) string { for _, hf := range mh.Fields { if !hf.IsPseudo() { return "" } if hf.Name[1:] == pseudo { return hf.Value } } return "" } // RegularFields returns the regular (non-pseudo) header fields of mh. // The caller does not own the returned slice. func (mh *MetaHeadersFrame) RegularFields() []hpack.HeaderField { for i, hf := range mh.Fields { if !hf.IsPseudo() { return mh.Fields[i:] } } return nil } // PseudoFields returns the pseudo header fields of mh. // The caller does not own the returned slice. func (mh *MetaHeadersFrame) PseudoFields() []hpack.HeaderField { for i, hf := range mh.Fields { if !hf.IsPseudo() { return mh.Fields[:i] } } return mh.Fields } func (mh *MetaHeadersFrame) checkPseudos() error { var isRequest, isResponse bool pf := mh.PseudoFields() for i, hf := range pf { switch hf.Name { case ":method", ":path", ":scheme", ":authority", ":protocol": isRequest = true case ":status": isResponse = true default: return pseudoHeaderError(hf.Name) } // Check for duplicates. // This would be a bad algorithm, but N is 5. // And this doesn't allocate. for _, hf2 := range pf[:i] { if hf.Name == hf2.Name { return duplicatePseudoHeaderError(hf.Name) } } } if isRequest && isResponse { return errMixPseudoHeaderTypes } return nil } func (fr *Framer) maxHeaderStringLen() int { v := int(fr.maxHeaderListSize()) if v < 0 { // If maxHeaderListSize overflows an int, use no limit (0). return 0 } return v } // readMetaFrame returns 0 or more CONTINUATION frames from fr and // merge them into the provided hf and returns a MetaHeadersFrame // with the decoded hpack values. func (fr *Framer) readMetaFrame(hf *HeadersFrame) (Frame, error) { if fr.AllowIllegalReads { return nil, errors.New("illegal use of AllowIllegalReads with ReadMetaHeaders") } mh := &MetaHeadersFrame{ HeadersFrame: hf, } var remainSize = fr.maxHeaderListSize() var sawRegular bool var invalid error // pseudo header field errors hdec := fr.ReadMetaHeaders hdec.SetEmitEnabled(true) hdec.SetMaxStringLength(fr.maxHeaderStringLen()) hdec.SetEmitFunc(func(hf hpack.HeaderField) { if VerboseLogs && fr.logReads { fr.debugReadLoggerf("http2: decoded hpack field %+v", hf) } if !httpguts.ValidHeaderFieldValue(hf.Value) { // Don't include the value in the error, because it may be sensitive. invalid = headerFieldValueError(hf.Name) } isPseudo := strings.HasPrefix(hf.Name, ":") if isPseudo { if sawRegular { invalid = errPseudoAfterRegular } } else { sawRegular = true if !validWireHeaderFieldName(hf.Name) { invalid = headerFieldNameError(hf.Name) } } if invalid != nil { hdec.SetEmitEnabled(false) return } size := hf.Size() if size > remainSize { hdec.SetEmitEnabled(false) mh.Truncated = true remainSize = 0 return } remainSize -= size mh.Fields = append(mh.Fields, hf) }) // Lose reference to MetaHeadersFrame: defer hdec.SetEmitFunc(func(hf hpack.HeaderField) {}) var hc headersOrContinuation = hf for { frag := hc.HeaderBlockFragment() // Avoid parsing large amounts of headers that we will then discard. // If the sender exceeds the max header list size by too much, // skip parsing the fragment and close the connection. // // "Too much" is either any CONTINUATION frame after we've already // exceeded the max header list size (in which case remainSize is 0), // or a frame whose encoded size is more than twice the remaining // header list bytes we're willing to accept. if int64(len(frag)) > int64(2*remainSize) { if VerboseLogs { log.Printf("http2: header list too large") } // It would be nice to send a RST_STREAM before sending the GOAWAY, // but the structure of the server's frame writer makes this difficult. return mh, ConnectionError(ErrCodeProtocol) } // Also close the connection after any CONTINUATION frame following an // invalid header, since we stop tracking the size of the headers after // an invalid one. if invalid != nil { if VerboseLogs { log.Printf("http2: invalid header: %v", invalid) } // It would be nice to send a RST_STREAM before sending the GOAWAY, // but the structure of the server's frame writer makes this difficult. return mh, ConnectionError(ErrCodeProtocol) } if _, err := hdec.Write(frag); err != nil { return mh, ConnectionError(ErrCodeCompression) } if hc.HeadersEnded() { break } if f, err := fr.ReadFrame(); err != nil { return nil, err } else { hc = f.(*ContinuationFrame) // guaranteed by checkFrameOrder } } mh.HeadersFrame.headerFragBuf = nil mh.HeadersFrame.invalidate() if err := hdec.Close(); err != nil { return mh, ConnectionError(ErrCodeCompression) } if invalid != nil { fr.errDetail = invalid if VerboseLogs { log.Printf("http2: invalid header: %v", invalid) } return nil, StreamError{mh.StreamID, ErrCodeProtocol, invalid} } if err := mh.checkPseudos(); err != nil { fr.errDetail = err if VerboseLogs { log.Printf("http2: invalid pseudo headers: %v", err) } return nil, StreamError{mh.StreamID, ErrCodeProtocol, err} } return mh, nil } func summarizeFrame(f Frame) string { var buf bytes.Buffer f.Header().writeDebug(&buf) switch f := f.(type) { case *SettingsFrame: n := 0 f.ForeachSetting(func(s Setting) error { n++ if n == 1 { buf.WriteString(", settings:") } fmt.Fprintf(&buf, " %v=%v,", s.ID, s.Val) return nil }) if n > 0 { buf.Truncate(buf.Len() - 1) // remove trailing comma } case *DataFrame: data := f.Data() const max = 256 if len(data) > max { data = data[:max] } fmt.Fprintf(&buf, " data=%q", data) if len(f.Data()) > max { fmt.Fprintf(&buf, " (%d bytes omitted)", len(f.Data())-max) } case *WindowUpdateFrame: if f.StreamID == 0 { buf.WriteString(" (conn)") } fmt.Fprintf(&buf, " incr=%v", f.Increment) case *PingFrame: fmt.Fprintf(&buf, " ping=%q", f.Data[:]) case *GoAwayFrame: fmt.Fprintf(&buf, " LastStreamID=%v ErrCode=%v Debug=%q", f.LastStreamID, f.ErrCode, f.debugData) case *RSTStreamFrame: fmt.Fprintf(&buf, " ErrCode=%v", f.ErrCode) } return buf.String() } ================================================ FILE: vendor/golang.org/x/net/http2/gotrack.go ================================================ // Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Defensive debug-only utility to track that functions run on the // goroutine that they're supposed to. package http2 import ( "bytes" "errors" "fmt" "os" "runtime" "strconv" "sync" "sync/atomic" ) var DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1" // Setting DebugGoroutines to false during a test to disable goroutine debugging // results in race detector complaints when a test leaves goroutines running before // returning. Tests shouldn't do this, of course, but when they do it generally shows // up as infrequent, hard-to-debug flakes. (See #66519.) // // Disable goroutine debugging during individual tests with an atomic bool. // (Note that it's safe to enable/disable debugging mid-test, so the actual race condition // here is harmless.) var disableDebugGoroutines atomic.Bool type goroutineLock uint64 func newGoroutineLock() goroutineLock { if !DebugGoroutines || disableDebugGoroutines.Load() { return 0 } return goroutineLock(curGoroutineID()) } func (g goroutineLock) check() { if !DebugGoroutines || disableDebugGoroutines.Load() { return } if curGoroutineID() != uint64(g) { panic("running on the wrong goroutine") } } func (g goroutineLock) checkNotOn() { if !DebugGoroutines || disableDebugGoroutines.Load() { return } if curGoroutineID() == uint64(g) { panic("running on the wrong goroutine") } } var goroutineSpace = []byte("goroutine ") func curGoroutineID() uint64 { bp := littleBuf.Get().(*[]byte) defer littleBuf.Put(bp) b := *bp b = b[:runtime.Stack(b, false)] // Parse the 4707 out of "goroutine 4707 [" b = bytes.TrimPrefix(b, goroutineSpace) i := bytes.IndexByte(b, ' ') if i < 0 { panic(fmt.Sprintf("No space found in %q", b)) } b = b[:i] n, err := parseUintBytes(b, 10, 64) if err != nil { panic(fmt.Sprintf("Failed to parse goroutine ID out of %q: %v", b, err)) } return n } var littleBuf = sync.Pool{ New: func() interface{} { buf := make([]byte, 64) return &buf }, } // parseUintBytes is like strconv.ParseUint, but using a []byte. func parseUintBytes(s []byte, base int, bitSize int) (n uint64, err error) { var cutoff, maxVal uint64 if bitSize == 0 { bitSize = int(strconv.IntSize) } s0 := s switch { case len(s) < 1: err = strconv.ErrSyntax goto Error case 2 <= base && base <= 36: // valid base; nothing to do case base == 0: // Look for octal, hex prefix. switch { case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'): base = 16 s = s[2:] if len(s) < 1 { err = strconv.ErrSyntax goto Error } case s[0] == '0': base = 8 default: base = 10 } default: err = errors.New("invalid base " + strconv.Itoa(base)) goto Error } n = 0 cutoff = cutoff64(base) maxVal = 1<<uint(bitSize) - 1 for i := 0; i < len(s); i++ { var v byte d := s[i] switch { case '0' <= d && d <= '9': v = d - '0' case 'a' <= d && d <= 'z': v = d - 'a' + 10 case 'A' <= d && d <= 'Z': v = d - 'A' + 10 default: n = 0 err = strconv.ErrSyntax goto Error } if int(v) >= base { n = 0 err = strconv.ErrSyntax goto Error } if n >= cutoff { // n*base overflows n = 1<<64 - 1 err = strconv.ErrRange goto Error } n *= uint64(base) n1 := n + uint64(v) if n1 < n || n1 > maxVal { // n+v overflows n = 1<<64 - 1 err = strconv.ErrRange goto Error } n = n1 } return n, nil Error: return n, &strconv.NumError{Func: "ParseUint", Num: string(s0), Err: err} } // Return the first number n such that n*base >= 1<<64. func cutoff64(base int) uint64 { if base < 2 { return 0 } return (1<<64-1)/uint64(base) + 1 } ================================================ FILE: vendor/golang.org/x/net/http2/hpack/encode.go ================================================ // Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package hpack import ( "io" ) const ( uint32Max = ^uint32(0) initialHeaderTableSize = 4096 ) type Encoder struct { dynTab dynamicTable // minSize is the minimum table size set by // SetMaxDynamicTableSize after the previous Header Table Size // Update. minSize uint32 // maxSizeLimit is the maximum table size this encoder // supports. This will protect the encoder from too large // size. maxSizeLimit uint32 // tableSizeUpdate indicates whether "Header Table Size // Update" is required. tableSizeUpdate bool w io.Writer buf []byte } // NewEncoder returns a new Encoder which performs HPACK encoding. An // encoded data is written to w. func NewEncoder(w io.Writer) *Encoder { e := &Encoder{ minSize: uint32Max, maxSizeLimit: initialHeaderTableSize, tableSizeUpdate: false, w: w, } e.dynTab.table.init() e.dynTab.setMaxSize(initialHeaderTableSize) return e } // WriteField encodes f into a single Write to e's underlying Writer. // This function may also produce bytes for "Header Table Size Update" // if necessary. If produced, it is done before encoding f. func (e *Encoder) WriteField(f HeaderField) error { e.buf = e.buf[:0] if e.tableSizeUpdate { e.tableSizeUpdate = false if e.minSize < e.dynTab.maxSize { e.buf = appendTableSize(e.buf, e.minSize) } e.minSize = uint32Max e.buf = appendTableSize(e.buf, e.dynTab.maxSize) } idx, nameValueMatch := e.searchTable(f) if nameValueMatch { e.buf = appendIndexed(e.buf, idx) } else { indexing := e.shouldIndex(f) if indexing { e.dynTab.add(f) } if idx == 0 { e.buf = appendNewName(e.buf, f, indexing) } else { e.buf = appendIndexedName(e.buf, f, idx, indexing) } } n, err := e.w.Write(e.buf) if err == nil && n != len(e.buf) { err = io.ErrShortWrite } return err } // searchTable searches f in both stable and dynamic header tables. // The static header table is searched first. Only when there is no // exact match for both name and value, the dynamic header table is // then searched. If there is no match, i is 0. If both name and value // match, i is the matched index and nameValueMatch becomes true. If // only name matches, i points to that index and nameValueMatch // becomes false. func (e *Encoder) searchTable(f HeaderField) (i uint64, nameValueMatch bool) { i, nameValueMatch = staticTable.search(f) if nameValueMatch { return i, true } j, nameValueMatch := e.dynTab.table.search(f) if nameValueMatch || (i == 0 && j != 0) { return j + uint64(staticTable.len()), nameValueMatch } return i, false } // SetMaxDynamicTableSize changes the dynamic header table size to v. // The actual size is bounded by the value passed to // SetMaxDynamicTableSizeLimit. func (e *Encoder) SetMaxDynamicTableSize(v uint32) { if v > e.maxSizeLimit { v = e.maxSizeLimit } if v < e.minSize { e.minSize = v } e.tableSizeUpdate = true e.dynTab.setMaxSize(v) } // MaxDynamicTableSize returns the current dynamic header table size. func (e *Encoder) MaxDynamicTableSize() (v uint32) { return e.dynTab.maxSize } // SetMaxDynamicTableSizeLimit changes the maximum value that can be // specified in SetMaxDynamicTableSize to v. By default, it is set to // 4096, which is the same size of the default dynamic header table // size described in HPACK specification. If the current maximum // dynamic header table size is strictly greater than v, "Header Table // Size Update" will be done in the next WriteField call and the // maximum dynamic header table size is truncated to v. func (e *Encoder) SetMaxDynamicTableSizeLimit(v uint32) { e.maxSizeLimit = v if e.dynTab.maxSize > v { e.tableSizeUpdate = true e.dynTab.setMaxSize(v) } } // shouldIndex reports whether f should be indexed. func (e *Encoder) shouldIndex(f HeaderField) bool { return !f.Sensitive && f.Size() <= e.dynTab.maxSize } // appendIndexed appends index i, as encoded in "Indexed Header Field" // representation, to dst and returns the extended buffer. func appendIndexed(dst []byte, i uint64) []byte { first := len(dst) dst = appendVarInt(dst, 7, i) dst[first] |= 0x80 return dst } // appendNewName appends f, as encoded in one of "Literal Header field // - New Name" representation variants, to dst and returns the // extended buffer. // // If f.Sensitive is true, "Never Indexed" representation is used. If // f.Sensitive is false and indexing is true, "Incremental Indexing" // representation is used. func appendNewName(dst []byte, f HeaderField, indexing bool) []byte { dst = append(dst, encodeTypeByte(indexing, f.Sensitive)) dst = appendHpackString(dst, f.Name) return appendHpackString(dst, f.Value) } // appendIndexedName appends f and index i referring indexed name // entry, as encoded in one of "Literal Header field - Indexed Name" // representation variants, to dst and returns the extended buffer. // // If f.Sensitive is true, "Never Indexed" representation is used. If // f.Sensitive is false and indexing is true, "Incremental Indexing" // representation is used. func appendIndexedName(dst []byte, f HeaderField, i uint64, indexing bool) []byte { first := len(dst) var n byte if indexing { n = 6 } else { n = 4 } dst = appendVarInt(dst, n, i) dst[first] |= encodeTypeByte(indexing, f.Sensitive) return appendHpackString(dst, f.Value) } // appendTableSize appends v, as encoded in "Header Table Size Update" // representation, to dst and returns the extended buffer. func appendTableSize(dst []byte, v uint32) []byte { first := len(dst) dst = appendVarInt(dst, 5, uint64(v)) dst[first] |= 0x20 return dst } // appendVarInt appends i, as encoded in variable integer form using n // bit prefix, to dst and returns the extended buffer. // // See // https://httpwg.org/specs/rfc7541.html#integer.representation func appendVarInt(dst []byte, n byte, i uint64) []byte { k := uint64((1 << n) - 1) if i < k { return append(dst, byte(i)) } dst = append(dst, byte(k)) i -= k for ; i >= 128; i >>= 7 { dst = append(dst, byte(0x80|(i&0x7f))) } return append(dst, byte(i)) } // appendHpackString appends s, as encoded in "String Literal" // representation, to dst and returns the extended buffer. // // s will be encoded in Huffman codes only when it produces strictly // shorter byte string. func appendHpackString(dst []byte, s string) []byte { huffmanLength := HuffmanEncodeLength(s) if huffmanLength < uint64(len(s)) { first := len(dst) dst = appendVarInt(dst, 7, huffmanLength) dst = AppendHuffmanString(dst, s) dst[first] |= 0x80 } else { dst = appendVarInt(dst, 7, uint64(len(s))) dst = append(dst, s...) } return dst } // encodeTypeByte returns type byte. If sensitive is true, type byte // for "Never Indexed" representation is returned. If sensitive is // false and indexing is true, type byte for "Incremental Indexing" // representation is returned. Otherwise, type byte for "Without // Indexing" is returned. func encodeTypeByte(indexing, sensitive bool) byte { if sensitive { return 0x10 } if indexing { return 0x40 } return 0 } ================================================ FILE: vendor/golang.org/x/net/http2/hpack/hpack.go ================================================ // Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package hpack implements HPACK, a compression format for // efficiently representing HTTP header fields in the context of HTTP/2. // // See http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-09 package hpack import ( "bytes" "errors" "fmt" ) // A DecodingError is something the spec defines as a decoding error. type DecodingError struct { Err error } func (de DecodingError) Error() string { return fmt.Sprintf("decoding error: %v", de.Err) } // An InvalidIndexError is returned when an encoder references a table // entry before the static table or after the end of the dynamic table. type InvalidIndexError int func (e InvalidIndexError) Error() string { return fmt.Sprintf("invalid indexed representation index %d", int(e)) } // A HeaderField is a name-value pair. Both the name and value are // treated as opaque sequences of octets. type HeaderField struct { Name, Value string // Sensitive means that this header field should never be // indexed. Sensitive bool } // IsPseudo reports whether the header field is an http2 pseudo header. // That is, it reports whether it starts with a colon. // It is not otherwise guaranteed to be a valid pseudo header field, // though. func (hf HeaderField) IsPseudo() bool { return len(hf.Name) != 0 && hf.Name[0] == ':' } func (hf HeaderField) String() string { var suffix string if hf.Sensitive { suffix = " (sensitive)" } return fmt.Sprintf("header field %q = %q%s", hf.Name, hf.Value, suffix) } // Size returns the size of an entry per RFC 7541 section 4.1. func (hf HeaderField) Size() uint32 { // https://httpwg.org/specs/rfc7541.html#rfc.section.4.1 // "The size of the dynamic table is the sum of the size of // its entries. The size of an entry is the sum of its name's // length in octets (as defined in Section 5.2), its value's // length in octets (see Section 5.2), plus 32. The size of // an entry is calculated using the length of the name and // value without any Huffman encoding applied." // This can overflow if somebody makes a large HeaderField // Name and/or Value by hand, but we don't care, because that // won't happen on the wire because the encoding doesn't allow // it. return uint32(len(hf.Name) + len(hf.Value) + 32) } // A Decoder is the decoding context for incremental processing of // header blocks. type Decoder struct { dynTab dynamicTable emit func(f HeaderField) emitEnabled bool // whether calls to emit are enabled maxStrLen int // 0 means unlimited // buf is the unparsed buffer. It's only written to // saveBuf if it was truncated in the middle of a header // block. Because it's usually not owned, we can only // process it under Write. buf []byte // not owned; only valid during Write // saveBuf is previous data passed to Write which we weren't able // to fully parse before. Unlike buf, we own this data. saveBuf bytes.Buffer firstField bool // processing the first field of the header block } // NewDecoder returns a new decoder with the provided maximum dynamic // table size. The emitFunc will be called for each valid field // parsed, in the same goroutine as calls to Write, before Write returns. func NewDecoder(maxDynamicTableSize uint32, emitFunc func(f HeaderField)) *Decoder { d := &Decoder{ emit: emitFunc, emitEnabled: true, firstField: true, } d.dynTab.table.init() d.dynTab.allowedMaxSize = maxDynamicTableSize d.dynTab.setMaxSize(maxDynamicTableSize) return d } // ErrStringLength is returned by Decoder.Write when the max string length // (as configured by Decoder.SetMaxStringLength) would be violated. var ErrStringLength = errors.New("hpack: string too long") // SetMaxStringLength sets the maximum size of a HeaderField name or // value string. If a string exceeds this length (even after any // decompression), Write will return ErrStringLength. // A value of 0 means unlimited and is the default from NewDecoder. func (d *Decoder) SetMaxStringLength(n int) { d.maxStrLen = n } // SetEmitFunc changes the callback used when new header fields // are decoded. // It must be non-nil. It does not affect EmitEnabled. func (d *Decoder) SetEmitFunc(emitFunc func(f HeaderField)) { d.emit = emitFunc } // SetEmitEnabled controls whether the emitFunc provided to NewDecoder // should be called. The default is true. // // This facility exists to let servers enforce MAX_HEADER_LIST_SIZE // while still decoding and keeping in-sync with decoder state, but // without doing unnecessary decompression or generating unnecessary // garbage for header fields past the limit. func (d *Decoder) SetEmitEnabled(v bool) { d.emitEnabled = v } // EmitEnabled reports whether calls to the emitFunc provided to NewDecoder // are currently enabled. The default is true. func (d *Decoder) EmitEnabled() bool { return d.emitEnabled } // TODO: add method *Decoder.Reset(maxSize, emitFunc) to let callers re-use Decoders and their // underlying buffers for garbage reasons. func (d *Decoder) SetMaxDynamicTableSize(v uint32) { d.dynTab.setMaxSize(v) } // SetAllowedMaxDynamicTableSize sets the upper bound that the encoded // stream (via dynamic table size updates) may set the maximum size // to. func (d *Decoder) SetAllowedMaxDynamicTableSize(v uint32) { d.dynTab.allowedMaxSize = v } type dynamicTable struct { // https://httpwg.org/specs/rfc7541.html#rfc.section.2.3.2 table headerFieldTable size uint32 // in bytes maxSize uint32 // current maxSize allowedMaxSize uint32 // maxSize may go up to this, inclusive } func (dt *dynamicTable) setMaxSize(v uint32) { dt.maxSize = v dt.evict() } func (dt *dynamicTable) add(f HeaderField) { dt.table.addEntry(f) dt.size += f.Size() dt.evict() } // If we're too big, evict old stuff. func (dt *dynamicTable) evict() { var n int for dt.size > dt.maxSize && n < dt.table.len() { dt.size -= dt.table.ents[n].Size() n++ } dt.table.evictOldest(n) } func (d *Decoder) maxTableIndex() int { // This should never overflow. RFC 7540 Section 6.5.2 limits the size of // the dynamic table to 2^32 bytes, where each entry will occupy more than // one byte. Further, the staticTable has a fixed, small length. return d.dynTab.table.len() + staticTable.len() } func (d *Decoder) at(i uint64) (hf HeaderField, ok bool) { // See Section 2.3.3. if i == 0 { return } if i <= uint64(staticTable.len()) { return staticTable.ents[i-1], true } if i > uint64(d.maxTableIndex()) { return } // In the dynamic table, newer entries have lower indices. // However, dt.ents[0] is the oldest entry. Hence, dt.ents is // the reversed dynamic table. dt := d.dynTab.table return dt.ents[dt.len()-(int(i)-staticTable.len())], true } // DecodeFull decodes an entire block. // // TODO: remove this method and make it incremental later? This is // easier for debugging now. func (d *Decoder) DecodeFull(p []byte) ([]HeaderField, error) { var hf []HeaderField saveFunc := d.emit defer func() { d.emit = saveFunc }() d.emit = func(f HeaderField) { hf = append(hf, f) } if _, err := d.Write(p); err != nil { return nil, err } if err := d.Close(); err != nil { return nil, err } return hf, nil } // Close declares that the decoding is complete and resets the Decoder // to be reused again for a new header block. If there is any remaining // data in the decoder's buffer, Close returns an error. func (d *Decoder) Close() error { if d.saveBuf.Len() > 0 { d.saveBuf.Reset() return DecodingError{errors.New("truncated headers")} } d.firstField = true return nil } func (d *Decoder) Write(p []byte) (n int, err error) { if len(p) == 0 { // Prevent state machine CPU attacks (making us redo // work up to the point of finding out we don't have // enough data) return } // Only copy the data if we have to. Optimistically assume // that p will contain a complete header block. if d.saveBuf.Len() == 0 { d.buf = p } else { d.saveBuf.Write(p) d.buf = d.saveBuf.Bytes() d.saveBuf.Reset() } for len(d.buf) > 0 { err = d.parseHeaderFieldRepr() if err == errNeedMore { // Extra paranoia, making sure saveBuf won't // get too large. All the varint and string // reading code earlier should already catch // overlong things and return ErrStringLength, // but keep this as a last resort. const varIntOverhead = 8 // conservative if d.maxStrLen != 0 && int64(len(d.buf)) > 2*(int64(d.maxStrLen)+varIntOverhead) { return 0, ErrStringLength } d.saveBuf.Write(d.buf) return len(p), nil } d.firstField = false if err != nil { break } } return len(p), err } // errNeedMore is an internal sentinel error value that means the // buffer is truncated and we need to read more data before we can // continue parsing. var errNeedMore = errors.New("need more data") type indexType int const ( indexedTrue indexType = iota indexedFalse indexedNever ) func (v indexType) indexed() bool { return v == indexedTrue } func (v indexType) sensitive() bool { return v == indexedNever } // returns errNeedMore if there isn't enough data available. // any other error is fatal. // consumes d.buf iff it returns nil. // precondition: must be called with len(d.buf) > 0 func (d *Decoder) parseHeaderFieldRepr() error { b := d.buf[0] switch { case b&128 != 0: // Indexed representation. // High bit set? // https://httpwg.org/specs/rfc7541.html#rfc.section.6.1 return d.parseFieldIndexed() case b&192 == 64: // 6.2.1 Literal Header Field with Incremental Indexing // 0b10xxxxxx: top two bits are 10 // https://httpwg.org/specs/rfc7541.html#rfc.section.6.2.1 return d.parseFieldLiteral(6, indexedTrue) case b&240 == 0: // 6.2.2 Literal Header Field without Indexing // 0b0000xxxx: top four bits are 0000 // https://httpwg.org/specs/rfc7541.html#rfc.section.6.2.2 return d.parseFieldLiteral(4, indexedFalse) case b&240 == 16: // 6.2.3 Literal Header Field never Indexed // 0b0001xxxx: top four bits are 0001 // https://httpwg.org/specs/rfc7541.html#rfc.section.6.2.3 return d.parseFieldLiteral(4, indexedNever) case b&224 == 32: // 6.3 Dynamic Table Size Update // Top three bits are '001'. // https://httpwg.org/specs/rfc7541.html#rfc.section.6.3 return d.parseDynamicTableSizeUpdate() } return DecodingError{errors.New("invalid encoding")} } // (same invariants and behavior as parseHeaderFieldRepr) func (d *Decoder) parseFieldIndexed() error { buf := d.buf idx, buf, err := readVarInt(7, buf) if err != nil { return err } hf, ok := d.at(idx) if !ok { return DecodingError{InvalidIndexError(idx)} } d.buf = buf return d.callEmit(HeaderField{Name: hf.Name, Value: hf.Value}) } // (same invariants and behavior as parseHeaderFieldRepr) func (d *Decoder) parseFieldLiteral(n uint8, it indexType) error { buf := d.buf nameIdx, buf, err := readVarInt(n, buf) if err != nil { return err } var hf HeaderField wantStr := d.emitEnabled || it.indexed() var undecodedName undecodedString if nameIdx > 0 { ihf, ok := d.at(nameIdx) if !ok { return DecodingError{InvalidIndexError(nameIdx)} } hf.Name = ihf.Name } else { undecodedName, buf, err = d.readString(buf) if err != nil { return err } } undecodedValue, buf, err := d.readString(buf) if err != nil { return err } if wantStr { if nameIdx <= 0 { hf.Name, err = d.decodeString(undecodedName) if err != nil { return err } } hf.Value, err = d.decodeString(undecodedValue) if err != nil { return err } } d.buf = buf if it.indexed() { d.dynTab.add(hf) } hf.Sensitive = it.sensitive() return d.callEmit(hf) } func (d *Decoder) callEmit(hf HeaderField) error { if d.maxStrLen != 0 { if len(hf.Name) > d.maxStrLen || len(hf.Value) > d.maxStrLen { return ErrStringLength } } if d.emitEnabled { d.emit(hf) } return nil } // (same invariants and behavior as parseHeaderFieldRepr) func (d *Decoder) parseDynamicTableSizeUpdate() error { // RFC 7541, sec 4.2: This dynamic table size update MUST occur at the // beginning of the first header block following the change to the dynamic table size. if !d.firstField && d.dynTab.size > 0 { return DecodingError{errors.New("dynamic table size update MUST occur at the beginning of a header block")} } buf := d.buf size, buf, err := readVarInt(5, buf) if err != nil { return err } if size > uint64(d.dynTab.allowedMaxSize) { return DecodingError{errors.New("dynamic table size update too large")} } d.dynTab.setMaxSize(uint32(size)) d.buf = buf return nil } var errVarintOverflow = DecodingError{errors.New("varint integer overflow")} // readVarInt reads an unsigned variable length integer off the // beginning of p. n is the parameter as described in // https://httpwg.org/specs/rfc7541.html#rfc.section.5.1. // // n must always be between 1 and 8. // // The returned remain buffer is either a smaller suffix of p, or err != nil. // The error is errNeedMore if p doesn't contain a complete integer. func readVarInt(n byte, p []byte) (i uint64, remain []byte, err error) { if n < 1 || n > 8 { panic("bad n") } if len(p) == 0 { return 0, p, errNeedMore } i = uint64(p[0]) if n < 8 { i &= (1 << uint64(n)) - 1 } if i < (1<<uint64(n))-1 { return i, p[1:], nil } origP := p p = p[1:] var m uint64 for len(p) > 0 { b := p[0] p = p[1:] i += uint64(b&127) << m if b&128 == 0 { return i, p, nil } m += 7 if m >= 63 { // TODO: proper overflow check. making this up. return 0, origP, errVarintOverflow } } return 0, origP, errNeedMore } // readString reads an hpack string from p. // // It returns a reference to the encoded string data to permit deferring decode costs // until after the caller verifies all data is present. func (d *Decoder) readString(p []byte) (u undecodedString, remain []byte, err error) { if len(p) == 0 { return u, p, errNeedMore } isHuff := p[0]&128 != 0 strLen, p, err := readVarInt(7, p) if err != nil { return u, p, err } if d.maxStrLen != 0 && strLen > uint64(d.maxStrLen) { // Returning an error here means Huffman decoding errors // for non-indexed strings past the maximum string length // are ignored, but the server is returning an error anyway // and because the string is not indexed the error will not // affect the decoding state. return u, nil, ErrStringLength } if uint64(len(p)) < strLen { return u, p, errNeedMore } u.isHuff = isHuff u.b = p[:strLen] return u, p[strLen:], nil } type undecodedString struct { isHuff bool b []byte } func (d *Decoder) decodeString(u undecodedString) (string, error) { if !u.isHuff { return string(u.b), nil } buf := bufPool.Get().(*bytes.Buffer) buf.Reset() // don't trust others var s string err := huffmanDecode(buf, d.maxStrLen, u.b) if err == nil { s = buf.String() } buf.Reset() // be nice to GC bufPool.Put(buf) return s, err } ================================================ FILE: vendor/golang.org/x/net/http2/hpack/huffman.go ================================================ // Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package hpack import ( "bytes" "errors" "io" "sync" ) var bufPool = sync.Pool{ New: func() interface{} { return new(bytes.Buffer) }, } // HuffmanDecode decodes the string in v and writes the expanded // result to w, returning the number of bytes written to w and the // Write call's return value. At most one Write call is made. func HuffmanDecode(w io.Writer, v []byte) (int, error) { buf := bufPool.Get().(*bytes.Buffer) buf.Reset() defer bufPool.Put(buf) if err := huffmanDecode(buf, 0, v); err != nil { return 0, err } return w.Write(buf.Bytes()) } // HuffmanDecodeToString decodes the string in v. func HuffmanDecodeToString(v []byte) (string, error) { buf := bufPool.Get().(*bytes.Buffer) buf.Reset() defer bufPool.Put(buf) if err := huffmanDecode(buf, 0, v); err != nil { return "", err } return buf.String(), nil } // ErrInvalidHuffman is returned for errors found decoding // Huffman-encoded strings. var ErrInvalidHuffman = errors.New("hpack: invalid Huffman-encoded data") // huffmanDecode decodes v to buf. // If maxLen is greater than 0, attempts to write more to buf than // maxLen bytes will return ErrStringLength. func huffmanDecode(buf *bytes.Buffer, maxLen int, v []byte) error { rootHuffmanNode := getRootHuffmanNode() n := rootHuffmanNode // cur is the bit buffer that has not been fed into n. // cbits is the number of low order bits in cur that are valid. // sbits is the number of bits of the symbol prefix being decoded. cur, cbits, sbits := uint(0), uint8(0), uint8(0) for _, b := range v { cur = cur<<8 | uint(b) cbits += 8 sbits += 8 for cbits >= 8 { idx := byte(cur >> (cbits - 8)) n = n.children[idx] if n == nil { return ErrInvalidHuffman } if n.children == nil { if maxLen != 0 && buf.Len() == maxLen { return ErrStringLength } buf.WriteByte(n.sym) cbits -= n.codeLen n = rootHuffmanNode sbits = cbits } else { cbits -= 8 } } } for cbits > 0 { n = n.children[byte(cur<<(8-cbits))] if n == nil { return ErrInvalidHuffman } if n.children != nil || n.codeLen > cbits { break } if maxLen != 0 && buf.Len() == maxLen { return ErrStringLength } buf.WriteByte(n.sym) cbits -= n.codeLen n = rootHuffmanNode sbits = cbits } if sbits > 7 { // Either there was an incomplete symbol, or overlong padding. // Both are decoding errors per RFC 7541 section 5.2. return ErrInvalidHuffman } if mask := uint(1<<cbits - 1); cur&mask != mask { // Trailing bits must be a prefix of EOS per RFC 7541 section 5.2. return ErrInvalidHuffman } return nil } // incomparable is a zero-width, non-comparable type. Adding it to a struct // makes that struct also non-comparable, and generally doesn't add // any size (as long as it's first). type incomparable [0]func() type node struct { _ incomparable // children is non-nil for internal nodes children *[256]*node // The following are only valid if children is nil: codeLen uint8 // number of bits that led to the output of sym sym byte // output symbol } func newInternalNode() *node { return &node{children: new([256]*node)} } var ( buildRootOnce sync.Once lazyRootHuffmanNode *node ) func getRootHuffmanNode() *node { buildRootOnce.Do(buildRootHuffmanNode) return lazyRootHuffmanNode } func buildRootHuffmanNode() { if len(huffmanCodes) != 256 { panic("unexpected size") } lazyRootHuffmanNode = newInternalNode() // allocate a leaf node for each of the 256 symbols leaves := new([256]node) for sym, code := range huffmanCodes { codeLen := huffmanCodeLen[sym] cur := lazyRootHuffmanNode for codeLen > 8 { codeLen -= 8 i := uint8(code >> codeLen) if cur.children[i] == nil { cur.children[i] = newInternalNode() } cur = cur.children[i] } shift := 8 - codeLen start, end := int(uint8(code<<shift)), int(1<<shift) leaves[sym].sym = byte(sym) leaves[sym].codeLen = codeLen for i := start; i < start+end; i++ { cur.children[i] = &leaves[sym] } } } // AppendHuffmanString appends s, as encoded in Huffman codes, to dst // and returns the extended buffer. func AppendHuffmanString(dst []byte, s string) []byte { // This relies on the maximum huffman code length being 30 (See tables.go huffmanCodeLen array) // So if a uint64 buffer has less than 32 valid bits can always accommodate another huffmanCode. var ( x uint64 // buffer n uint // number valid of bits present in x ) for i := 0; i < len(s); i++ { c := s[i] n += uint(huffmanCodeLen[c]) x <<= huffmanCodeLen[c] % 64 x |= uint64(huffmanCodes[c]) if n >= 32 { n %= 32 // Normally would be -= 32 but %= 32 informs compiler 0 <= n <= 31 for upcoming shift y := uint32(x >> n) // Compiler doesn't combine memory writes if y isn't uint32 dst = append(dst, byte(y>>24), byte(y>>16), byte(y>>8), byte(y)) } } // Add padding bits if necessary if over := n % 8; over > 0 { const ( eosCode = 0x3fffffff eosNBits = 30 eosPadByte = eosCode >> (eosNBits - 8) ) pad := 8 - over x = (x << pad) | (eosPadByte >> over) n += pad // 8 now divides into n exactly } // n in (0, 8, 16, 24, 32) switch n / 8 { case 0: return dst case 1: return append(dst, byte(x)) case 2: y := uint16(x) return append(dst, byte(y>>8), byte(y)) case 3: y := uint16(x >> 8) return append(dst, byte(y>>8), byte(y), byte(x)) } // case 4: y := uint32(x) return append(dst, byte(y>>24), byte(y>>16), byte(y>>8), byte(y)) } // HuffmanEncodeLength returns the number of bytes required to encode // s in Huffman codes. The result is round up to byte boundary. func HuffmanEncodeLength(s string) uint64 { n := uint64(0) for i := 0; i < len(s); i++ { n += uint64(huffmanCodeLen[s[i]]) } return (n + 7) / 8 } ================================================ FILE: vendor/golang.org/x/net/http2/hpack/static_table.go ================================================ // go generate gen.go // Code generated by the command above; DO NOT EDIT. package hpack var staticTable = &headerFieldTable{ evictCount: 0, byName: map[string]uint64{ ":authority": 1, ":method": 3, ":path": 5, ":scheme": 7, ":status": 14, "accept-charset": 15, "accept-encoding": 16, "accept-language": 17, "accept-ranges": 18, "accept": 19, "access-control-allow-origin": 20, "age": 21, "allow": 22, "authorization": 23, "cache-control": 24, "content-disposition": 25, "content-encoding": 26, "content-language": 27, "content-length": 28, "content-location": 29, "content-range": 30, "content-type": 31, "cookie": 32, "date": 33, "etag": 34, "expect": 35, "expires": 36, "from": 37, "host": 38, "if-match": 39, "if-modified-since": 40, "if-none-match": 41, "if-range": 42, "if-unmodified-since": 43, "last-modified": 44, "link": 45, "location": 46, "max-forwards": 47, "proxy-authenticate": 48, "proxy-authorization": 49, "range": 50, "referer": 51, "refresh": 52, "retry-after": 53, "server": 54, "set-cookie": 55, "strict-transport-security": 56, "transfer-encoding": 57, "user-agent": 58, "vary": 59, "via": 60, "www-authenticate": 61, }, byNameValue: map[pairNameValue]uint64{ {name: ":authority", value: ""}: 1, {name: ":method", value: "GET"}: 2, {name: ":method", value: "POST"}: 3, {name: ":path", value: "/"}: 4, {name: ":path", value: "/index.html"}: 5, {name: ":scheme", value: "http"}: 6, {name: ":scheme", value: "https"}: 7, {name: ":status", value: "200"}: 8, {name: ":status", value: "204"}: 9, {name: ":status", value: "206"}: 10, {name: ":status", value: "304"}: 11, {name: ":status", value: "400"}: 12, {name: ":status", value: "404"}: 13, {name: ":status", value: "500"}: 14, {name: "accept-charset", value: ""}: 15, {name: "accept-encoding", value: "gzip, deflate"}: 16, {name: "accept-language", value: ""}: 17, {name: "accept-ranges", value: ""}: 18, {name: "accept", value: ""}: 19, {name: "access-control-allow-origin", value: ""}: 20, {name: "age", value: ""}: 21, {name: "allow", value: ""}: 22, {name: "authorization", value: ""}: 23, {name: "cache-control", value: ""}: 24, {name: "content-disposition", value: ""}: 25, {name: "content-encoding", value: ""}: 26, {name: "content-language", value: ""}: 27, {name: "content-length", value: ""}: 28, {name: "content-location", value: ""}: 29, {name: "content-range", value: ""}: 30, {name: "content-type", value: ""}: 31, {name: "cookie", value: ""}: 32, {name: "date", value: ""}: 33, {name: "etag", value: ""}: 34, {name: "expect", value: ""}: 35, {name: "expires", value: ""}: 36, {name: "from", value: ""}: 37, {name: "host", value: ""}: 38, {name: "if-match", value: ""}: 39, {name: "if-modified-since", value: ""}: 40, {name: "if-none-match", value: ""}: 41, {name: "if-range", value: ""}: 42, {name: "if-unmodified-since", value: ""}: 43, {name: "last-modified", value: ""}: 44, {name: "link", value: ""}: 45, {name: "location", value: ""}: 46, {name: "max-forwards", value: ""}: 47, {name: "proxy-authenticate", value: ""}: 48, {name: "proxy-authorization", value: ""}: 49, {name: "range", value: ""}: 50, {name: "referer", value: ""}: 51, {name: "refresh", value: ""}: 52, {name: "retry-after", value: ""}: 53, {name: "server", value: ""}: 54, {name: "set-cookie", value: ""}: 55, {name: "strict-transport-security", value: ""}: 56, {name: "transfer-encoding", value: ""}: 57, {name: "user-agent", value: ""}: 58, {name: "vary", value: ""}: 59, {name: "via", value: ""}: 60, {name: "www-authenticate", value: ""}: 61, }, ents: []HeaderField{ {Name: ":authority", Value: "", Sensitive: false}, {Name: ":method", Value: "GET", Sensitive: false}, {Name: ":method", Value: "POST", Sensitive: false}, {Name: ":path", Value: "/", Sensitive: false}, {Name: ":path", Value: "/index.html", Sensitive: false}, {Name: ":scheme", Value: "http", Sensitive: false}, {Name: ":scheme", Value: "https", Sensitive: false}, {Name: ":status", Value: "200", Sensitive: false}, {Name: ":status", Value: "204", Sensitive: false}, {Name: ":status", Value: "206", Sensitive: false}, {Name: ":status", Value: "304", Sensitive: false}, {Name: ":status", Value: "400", Sensitive: false}, {Name: ":status", Value: "404", Sensitive: false}, {Name: ":status", Value: "500", Sensitive: false}, {Name: "accept-charset", Value: "", Sensitive: false}, {Name: "accept-encoding", Value: "gzip, deflate", Sensitive: false}, {Name: "accept-language", Value: "", Sensitive: false}, {Name: "accept-ranges", Value: "", Sensitive: false}, {Name: "accept", Value: "", Sensitive: false}, {Name: "access-control-allow-origin", Value: "", Sensitive: false}, {Name: "age", Value: "", Sensitive: false}, {Name: "allow", Value: "", Sensitive: false}, {Name: "authorization", Value: "", Sensitive: false}, {Name: "cache-control", Value: "", Sensitive: false}, {Name: "content-disposition", Value: "", Sensitive: false}, {Name: "content-encoding", Value: "", Sensitive: false}, {Name: "content-language", Value: "", Sensitive: false}, {Name: "content-length", Value: "", Sensitive: false}, {Name: "content-location", Value: "", Sensitive: false}, {Name: "content-range", Value: "", Sensitive: false}, {Name: "content-type", Value: "", Sensitive: false}, {Name: "cookie", Value: "", Sensitive: false}, {Name: "date", Value: "", Sensitive: false}, {Name: "etag", Value: "", Sensitive: false}, {Name: "expect", Value: "", Sensitive: false}, {Name: "expires", Value: "", Sensitive: false}, {Name: "from", Value: "", Sensitive: false}, {Name: "host", Value: "", Sensitive: false}, {Name: "if-match", Value: "", Sensitive: false}, {Name: "if-modified-since", Value: "", Sensitive: false}, {Name: "if-none-match", Value: "", Sensitive: false}, {Name: "if-range", Value: "", Sensitive: false}, {Name: "if-unmodified-since", Value: "", Sensitive: false}, {Name: "last-modified", Value: "", Sensitive: false}, {Name: "link", Value: "", Sensitive: false}, {Name: "location", Value: "", Sensitive: false}, {Name: "max-forwards", Value: "", Sensitive: false}, {Name: "proxy-authenticate", Value: "", Sensitive: false}, {Name: "proxy-authorization", Value: "", Sensitive: false}, {Name: "range", Value: "", Sensitive: false}, {Name: "referer", Value: "", Sensitive: false}, {Name: "refresh", Value: "", Sensitive: false}, {Name: "retry-after", Value: "", Sensitive: false}, {Name: "server", Value: "", Sensitive: false}, {Name: "set-cookie", Value: "", Sensitive: false}, {Name: "strict-transport-security", Value: "", Sensitive: false}, {Name: "transfer-encoding", Value: "", Sensitive: false}, {Name: "user-agent", Value: "", Sensitive: false}, {Name: "vary", Value: "", Sensitive: false}, {Name: "via", Value: "", Sensitive: false}, {Name: "www-authenticate", Value: "", Sensitive: false}, }, } ================================================ FILE: vendor/golang.org/x/net/http2/hpack/tables.go ================================================ // Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package hpack import ( "fmt" ) // headerFieldTable implements a list of HeaderFields. // This is used to implement the static and dynamic tables. type headerFieldTable struct { // For static tables, entries are never evicted. // // For dynamic tables, entries are evicted from ents[0] and added to the end. // Each entry has a unique id that starts at one and increments for each // entry that is added. This unique id is stable across evictions, meaning // it can be used as a pointer to a specific entry. As in hpack, unique ids // are 1-based. The unique id for ents[k] is k + evictCount + 1. // // Zero is not a valid unique id. // // evictCount should not overflow in any remotely practical situation. In // practice, we will have one dynamic table per HTTP/2 connection. If we // assume a very powerful server that handles 1M QPS per connection and each // request adds (then evicts) 100 entries from the table, it would still take // 2M years for evictCount to overflow. ents []HeaderField evictCount uint64 // byName maps a HeaderField name to the unique id of the newest entry with // the same name. See above for a definition of "unique id". byName map[string]uint64 // byNameValue maps a HeaderField name/value pair to the unique id of the newest // entry with the same name and value. See above for a definition of "unique id". byNameValue map[pairNameValue]uint64 } type pairNameValue struct { name, value string } func (t *headerFieldTable) init() { t.byName = make(map[string]uint64) t.byNameValue = make(map[pairNameValue]uint64) } // len reports the number of entries in the table. func (t *headerFieldTable) len() int { return len(t.ents) } // addEntry adds a new entry. func (t *headerFieldTable) addEntry(f HeaderField) { id := uint64(t.len()) + t.evictCount + 1 t.byName[f.Name] = id t.byNameValue[pairNameValue{f.Name, f.Value}] = id t.ents = append(t.ents, f) } // evictOldest evicts the n oldest entries in the table. func (t *headerFieldTable) evictOldest(n int) { if n > t.len() { panic(fmt.Sprintf("evictOldest(%v) on table with %v entries", n, t.len())) } for k := 0; k < n; k++ { f := t.ents[k] id := t.evictCount + uint64(k) + 1 if t.byName[f.Name] == id { delete(t.byName, f.Name) } if p := (pairNameValue{f.Name, f.Value}); t.byNameValue[p] == id { delete(t.byNameValue, p) } } copy(t.ents, t.ents[n:]) for k := t.len() - n; k < t.len(); k++ { t.ents[k] = HeaderField{} // so strings can be garbage collected } t.ents = t.ents[:t.len()-n] if t.evictCount+uint64(n) < t.evictCount { panic("evictCount overflow") } t.evictCount += uint64(n) } // search finds f in the table. If there is no match, i is 0. // If both name and value match, i is the matched index and nameValueMatch // becomes true. If only name matches, i points to that index and // nameValueMatch becomes false. // // The returned index is a 1-based HPACK index. For dynamic tables, HPACK says // that index 1 should be the newest entry, but t.ents[0] is the oldest entry, // meaning t.ents is reversed for dynamic tables. Hence, when t is a dynamic // table, the return value i actually refers to the entry t.ents[t.len()-i]. // // All tables are assumed to be a dynamic tables except for the global staticTable. // // See Section 2.3.3. func (t *headerFieldTable) search(f HeaderField) (i uint64, nameValueMatch bool) { if !f.Sensitive { if id := t.byNameValue[pairNameValue{f.Name, f.Value}]; id != 0 { return t.idToIndex(id), true } } if id := t.byName[f.Name]; id != 0 { return t.idToIndex(id), false } return 0, false } // idToIndex converts a unique id to an HPACK index. // See Section 2.3.3. func (t *headerFieldTable) idToIndex(id uint64) uint64 { if id <= t.evictCount { panic(fmt.Sprintf("id (%v) <= evictCount (%v)", id, t.evictCount)) } k := id - t.evictCount - 1 // convert id to an index t.ents[k] if t != staticTable { return uint64(t.len()) - k // dynamic table } return k + 1 } var huffmanCodes = [256]uint32{ 0x1ff8, 0x7fffd8, 0xfffffe2, 0xfffffe3, 0xfffffe4, 0xfffffe5, 0xfffffe6, 0xfffffe7, 0xfffffe8, 0xffffea, 0x3ffffffc, 0xfffffe9, 0xfffffea, 0x3ffffffd, 0xfffffeb, 0xfffffec, 0xfffffed, 0xfffffee, 0xfffffef, 0xffffff0, 0xffffff1, 0xffffff2, 0x3ffffffe, 0xffffff3, 0xffffff4, 0xffffff5, 0xffffff6, 0xffffff7, 0xffffff8, 0xffffff9, 0xffffffa, 0xffffffb, 0x14, 0x3f8, 0x3f9, 0xffa, 0x1ff9, 0x15, 0xf8, 0x7fa, 0x3fa, 0x3fb, 0xf9, 0x7fb, 0xfa, 0x16, 0x17, 0x18, 0x0, 0x1, 0x2, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x5c, 0xfb, 0x7ffc, 0x20, 0xffb, 0x3fc, 0x1ffa, 0x21, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0xfc, 0x73, 0xfd, 0x1ffb, 0x7fff0, 0x1ffc, 0x3ffc, 0x22, 0x7ffd, 0x3, 0x23, 0x4, 0x24, 0x5, 0x25, 0x26, 0x27, 0x6, 0x74, 0x75, 0x28, 0x29, 0x2a, 0x7, 0x2b, 0x76, 0x2c, 0x8, 0x9, 0x2d, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7ffe, 0x7fc, 0x3ffd, 0x1ffd, 0xffffffc, 0xfffe6, 0x3fffd2, 0xfffe7, 0xfffe8, 0x3fffd3, 0x3fffd4, 0x3fffd5, 0x7fffd9, 0x3fffd6, 0x7fffda, 0x7fffdb, 0x7fffdc, 0x7fffdd, 0x7fffde, 0xffffeb, 0x7fffdf, 0xffffec, 0xffffed, 0x3fffd7, 0x7fffe0, 0xffffee, 0x7fffe1, 0x7fffe2, 0x7fffe3, 0x7fffe4, 0x1fffdc, 0x3fffd8, 0x7fffe5, 0x3fffd9, 0x7fffe6, 0x7fffe7, 0xffffef, 0x3fffda, 0x1fffdd, 0xfffe9, 0x3fffdb, 0x3fffdc, 0x7fffe8, 0x7fffe9, 0x1fffde, 0x7fffea, 0x3fffdd, 0x3fffde, 0xfffff0, 0x1fffdf, 0x3fffdf, 0x7fffeb, 0x7fffec, 0x1fffe0, 0x1fffe1, 0x3fffe0, 0x1fffe2, 0x7fffed, 0x3fffe1, 0x7fffee, 0x7fffef, 0xfffea, 0x3fffe2, 0x3fffe3, 0x3fffe4, 0x7ffff0, 0x3fffe5, 0x3fffe6, 0x7ffff1, 0x3ffffe0, 0x3ffffe1, 0xfffeb, 0x7fff1, 0x3fffe7, 0x7ffff2, 0x3fffe8, 0x1ffffec, 0x3ffffe2, 0x3ffffe3, 0x3ffffe4, 0x7ffffde, 0x7ffffdf, 0x3ffffe5, 0xfffff1, 0x1ffffed, 0x7fff2, 0x1fffe3, 0x3ffffe6, 0x7ffffe0, 0x7ffffe1, 0x3ffffe7, 0x7ffffe2, 0xfffff2, 0x1fffe4, 0x1fffe5, 0x3ffffe8, 0x3ffffe9, 0xffffffd, 0x7ffffe3, 0x7ffffe4, 0x7ffffe5, 0xfffec, 0xfffff3, 0xfffed, 0x1fffe6, 0x3fffe9, 0x1fffe7, 0x1fffe8, 0x7ffff3, 0x3fffea, 0x3fffeb, 0x1ffffee, 0x1ffffef, 0xfffff4, 0xfffff5, 0x3ffffea, 0x7ffff4, 0x3ffffeb, 0x7ffffe6, 0x3ffffec, 0x3ffffed, 0x7ffffe7, 0x7ffffe8, 0x7ffffe9, 0x7ffffea, 0x7ffffeb, 0xffffffe, 0x7ffffec, 0x7ffffed, 0x7ffffee, 0x7ffffef, 0x7fffff0, 0x3ffffee, } var huffmanCodeLen = [256]uint8{ 13, 23, 28, 28, 28, 28, 28, 28, 28, 24, 30, 28, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 28, 6, 10, 10, 12, 13, 6, 8, 11, 10, 10, 8, 11, 8, 6, 6, 6, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 8, 15, 6, 12, 10, 13, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 7, 8, 13, 19, 13, 14, 6, 15, 5, 6, 5, 6, 5, 6, 6, 6, 5, 7, 7, 6, 6, 6, 5, 6, 7, 6, 5, 5, 6, 7, 7, 7, 7, 7, 15, 11, 14, 13, 28, 20, 22, 20, 20, 22, 22, 22, 23, 22, 23, 23, 23, 23, 23, 24, 23, 24, 24, 22, 23, 24, 23, 23, 23, 23, 21, 22, 23, 22, 23, 23, 24, 22, 21, 20, 22, 22, 23, 23, 21, 23, 22, 22, 24, 21, 22, 23, 23, 21, 21, 22, 21, 23, 22, 23, 23, 20, 22, 22, 22, 23, 22, 22, 23, 26, 26, 20, 19, 22, 23, 22, 25, 26, 26, 26, 27, 27, 26, 24, 25, 19, 21, 26, 27, 27, 26, 27, 24, 21, 21, 26, 26, 28, 27, 27, 27, 20, 24, 20, 21, 22, 21, 21, 23, 22, 22, 25, 25, 24, 24, 26, 23, 26, 27, 26, 26, 27, 27, 27, 27, 27, 28, 27, 27, 27, 27, 27, 26, } ================================================ FILE: vendor/golang.org/x/net/http2/http2.go ================================================ // Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package http2 implements the HTTP/2 protocol. // // This package is low-level and intended to be used directly by very // few people. Most users will use it indirectly through the automatic // use by the net/http package (from Go 1.6 and later). // For use in earlier Go versions see ConfigureServer. (Transport support // requires Go 1.6 or later) // // See https://http2.github.io/ for more information on HTTP/2. package http2 // import "golang.org/x/net/http2" import ( "bufio" "crypto/tls" "errors" "fmt" "net" "net/http" "os" "sort" "strconv" "strings" "sync" "time" "golang.org/x/net/http/httpguts" ) var ( VerboseLogs bool logFrameWrites bool logFrameReads bool // Enabling extended CONNECT by causes browsers to attempt to use // WebSockets-over-HTTP/2. This results in problems when the server's websocket // package doesn't support extended CONNECT. // // Disable extended CONNECT by default for now. // // Issue #71128. disableExtendedConnectProtocol = true ) func init() { e := os.Getenv("GODEBUG") if strings.Contains(e, "http2debug=1") { VerboseLogs = true } if strings.Contains(e, "http2debug=2") { VerboseLogs = true logFrameWrites = true logFrameReads = true } if strings.Contains(e, "http2xconnect=1") { disableExtendedConnectProtocol = false } } const ( // ClientPreface is the string that must be sent by new // connections from clients. ClientPreface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" // SETTINGS_MAX_FRAME_SIZE default // https://httpwg.org/specs/rfc7540.html#rfc.section.6.5.2 initialMaxFrameSize = 16384 // NextProtoTLS is the NPN/ALPN protocol negotiated during // HTTP/2's TLS setup. NextProtoTLS = "h2" // https://httpwg.org/specs/rfc7540.html#SettingValues initialHeaderTableSize = 4096 initialWindowSize = 65535 // 6.9.2 Initial Flow Control Window Size defaultMaxReadFrameSize = 1 << 20 ) var ( clientPreface = []byte(ClientPreface) ) type streamState int // HTTP/2 stream states. // // See http://tools.ietf.org/html/rfc7540#section-5.1. // // For simplicity, the server code merges "reserved (local)" into // "half-closed (remote)". This is one less state transition to track. // The only downside is that we send PUSH_PROMISEs slightly less // liberally than allowable. More discussion here: // https://lists.w3.org/Archives/Public/ietf-http-wg/2016JulSep/0599.html // // "reserved (remote)" is omitted since the client code does not // support server push. const ( stateIdle streamState = iota stateOpen stateHalfClosedLocal stateHalfClosedRemote stateClosed ) var stateName = [...]string{ stateIdle: "Idle", stateOpen: "Open", stateHalfClosedLocal: "HalfClosedLocal", stateHalfClosedRemote: "HalfClosedRemote", stateClosed: "Closed", } func (st streamState) String() string { return stateName[st] } // Setting is a setting parameter: which setting it is, and its value. type Setting struct { // ID is which setting is being set. // See https://httpwg.org/specs/rfc7540.html#SettingFormat ID SettingID // Val is the value. Val uint32 } func (s Setting) String() string { return fmt.Sprintf("[%v = %d]", s.ID, s.Val) } // Valid reports whether the setting is valid. func (s Setting) Valid() error { // Limits and error codes from 6.5.2 Defined SETTINGS Parameters switch s.ID { case SettingEnablePush: if s.Val != 1 && s.Val != 0 { return ConnectionError(ErrCodeProtocol) } case SettingInitialWindowSize: if s.Val > 1<<31-1 { return ConnectionError(ErrCodeFlowControl) } case SettingMaxFrameSize: if s.Val < 16384 || s.Val > 1<<24-1 { return ConnectionError(ErrCodeProtocol) } case SettingEnableConnectProtocol: if s.Val != 1 && s.Val != 0 { return ConnectionError(ErrCodeProtocol) } } return nil } // A SettingID is an HTTP/2 setting as defined in // https://httpwg.org/specs/rfc7540.html#iana-settings type SettingID uint16 const ( SettingHeaderTableSize SettingID = 0x1 SettingEnablePush SettingID = 0x2 SettingMaxConcurrentStreams SettingID = 0x3 SettingInitialWindowSize SettingID = 0x4 SettingMaxFrameSize SettingID = 0x5 SettingMaxHeaderListSize SettingID = 0x6 SettingEnableConnectProtocol SettingID = 0x8 ) var settingName = map[SettingID]string{ SettingHeaderTableSize: "HEADER_TABLE_SIZE", SettingEnablePush: "ENABLE_PUSH", SettingMaxConcurrentStreams: "MAX_CONCURRENT_STREAMS", SettingInitialWindowSize: "INITIAL_WINDOW_SIZE", SettingMaxFrameSize: "MAX_FRAME_SIZE", SettingMaxHeaderListSize: "MAX_HEADER_LIST_SIZE", SettingEnableConnectProtocol: "ENABLE_CONNECT_PROTOCOL", } func (s SettingID) String() string { if v, ok := settingName[s]; ok { return v } return fmt.Sprintf("UNKNOWN_SETTING_%d", uint16(s)) } // validWireHeaderFieldName reports whether v is a valid header field // name (key). See httpguts.ValidHeaderName for the base rules. // // Further, http2 says: // // "Just as in HTTP/1.x, header field names are strings of ASCII // characters that are compared in a case-insensitive // fashion. However, header field names MUST be converted to // lowercase prior to their encoding in HTTP/2. " func validWireHeaderFieldName(v string) bool { if len(v) == 0 { return false } for _, r := range v { if !httpguts.IsTokenRune(r) { return false } if 'A' <= r && r <= 'Z' { return false } } return true } func httpCodeString(code int) string { switch code { case 200: return "200" case 404: return "404" } return strconv.Itoa(code) } // from pkg io type stringWriter interface { WriteString(s string) (n int, err error) } // A closeWaiter is like a sync.WaitGroup but only goes 1 to 0 (open to closed). type closeWaiter chan struct{} // Init makes a closeWaiter usable. // It exists because so a closeWaiter value can be placed inside a // larger struct and have the Mutex and Cond's memory in the same // allocation. func (cw *closeWaiter) Init() { *cw = make(chan struct{}) } // Close marks the closeWaiter as closed and unblocks any waiters. func (cw closeWaiter) Close() { close(cw) } // Wait waits for the closeWaiter to become closed. func (cw closeWaiter) Wait() { <-cw } // bufferedWriter is a buffered writer that writes to w. // Its buffered writer is lazily allocated as needed, to minimize // idle memory usage with many connections. type bufferedWriter struct { _ incomparable conn net.Conn // immutable bw *bufio.Writer // non-nil when data is buffered byteTimeout time.Duration // immutable, WriteByteTimeout } func newBufferedWriter(conn net.Conn, timeout time.Duration) *bufferedWriter { return &bufferedWriter{ conn: conn, byteTimeout: timeout, } } // bufWriterPoolBufferSize is the size of bufio.Writer's // buffers created using bufWriterPool. // // TODO: pick a less arbitrary value? this is a bit under // (3 x typical 1500 byte MTU) at least. Other than that, // not much thought went into it. const bufWriterPoolBufferSize = 4 << 10 var bufWriterPool = sync.Pool{ New: func() interface{} { return bufio.NewWriterSize(nil, bufWriterPoolBufferSize) }, } func (w *bufferedWriter) Available() int { if w.bw == nil { return bufWriterPoolBufferSize } return w.bw.Available() } func (w *bufferedWriter) Write(p []byte) (n int, err error) { if w.bw == nil { bw := bufWriterPool.Get().(*bufio.Writer) bw.Reset((*bufferedWriterTimeoutWriter)(w)) w.bw = bw } return w.bw.Write(p) } func (w *bufferedWriter) Flush() error { bw := w.bw if bw == nil { return nil } err := bw.Flush() bw.Reset(nil) bufWriterPool.Put(bw) w.bw = nil return err } type bufferedWriterTimeoutWriter bufferedWriter func (w *bufferedWriterTimeoutWriter) Write(p []byte) (n int, err error) { return writeWithByteTimeout(w.conn, w.byteTimeout, p) } // writeWithByteTimeout writes to conn. // If more than timeout passes without any bytes being written to the connection, // the write fails. func writeWithByteTimeout(conn net.Conn, timeout time.Duration, p []byte) (n int, err error) { if timeout <= 0 { return conn.Write(p) } for { conn.SetWriteDeadline(time.Now().Add(timeout)) nn, err := conn.Write(p[n:]) n += nn if n == len(p) || nn == 0 || !errors.Is(err, os.ErrDeadlineExceeded) { // Either we finished the write, made no progress, or hit the deadline. // Whichever it is, we're done now. conn.SetWriteDeadline(time.Time{}) return n, err } } } func mustUint31(v int32) uint32 { if v < 0 || v > 2147483647 { panic("out of range") } return uint32(v) } // bodyAllowedForStatus reports whether a given response status code // permits a body. See RFC 7230, section 3.3. func bodyAllowedForStatus(status int) bool { switch { case status >= 100 && status <= 199: return false case status == 204: return false case status == 304: return false } return true } type httpError struct { _ incomparable msg string timeout bool } func (e *httpError) Error() string { return e.msg } func (e *httpError) Timeout() bool { return e.timeout } func (e *httpError) Temporary() bool { return true } var errTimeout error = &httpError{msg: "http2: timeout awaiting response headers", timeout: true} type connectionStater interface { ConnectionState() tls.ConnectionState } var sorterPool = sync.Pool{New: func() interface{} { return new(sorter) }} type sorter struct { v []string // owned by sorter } func (s *sorter) Len() int { return len(s.v) } func (s *sorter) Swap(i, j int) { s.v[i], s.v[j] = s.v[j], s.v[i] } func (s *sorter) Less(i, j int) bool { return s.v[i] < s.v[j] } // Keys returns the sorted keys of h. // // The returned slice is only valid until s used again or returned to // its pool. func (s *sorter) Keys(h http.Header) []string { keys := s.v[:0] for k := range h { keys = append(keys, k) } s.v = keys sort.Sort(s) return keys } func (s *sorter) SortStrings(ss []string) { // Our sorter works on s.v, which sorter owns, so // stash it away while we sort the user's buffer. save := s.v s.v = ss sort.Sort(s) s.v = save } // incomparable is a zero-width, non-comparable type. Adding it to a struct // makes that struct also non-comparable, and generally doesn't add // any size (as long as it's first). type incomparable [0]func() ================================================ FILE: vendor/golang.org/x/net/http2/pipe.go ================================================ // Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package http2 import ( "errors" "io" "sync" ) // pipe is a goroutine-safe io.Reader/io.Writer pair. It's like // io.Pipe except there are no PipeReader/PipeWriter halves, and the // underlying buffer is an interface. (io.Pipe is always unbuffered) type pipe struct { mu sync.Mutex c sync.Cond // c.L lazily initialized to &p.mu b pipeBuffer // nil when done reading unread int // bytes unread when done err error // read error once empty. non-nil means closed. breakErr error // immediate read error (caller doesn't see rest of b) donec chan struct{} // closed on error readFn func() // optional code to run in Read before error } type pipeBuffer interface { Len() int io.Writer io.Reader } // setBuffer initializes the pipe buffer. // It has no effect if the pipe is already closed. func (p *pipe) setBuffer(b pipeBuffer) { p.mu.Lock() defer p.mu.Unlock() if p.err != nil || p.breakErr != nil { return } p.b = b } func (p *pipe) Len() int { p.mu.Lock() defer p.mu.Unlock() if p.b == nil { return p.unread } return p.b.Len() } // Read waits until data is available and copies bytes // from the buffer into p. func (p *pipe) Read(d []byte) (n int, err error) { p.mu.Lock() defer p.mu.Unlock() if p.c.L == nil { p.c.L = &p.mu } for { if p.breakErr != nil { return 0, p.breakErr } if p.b != nil && p.b.Len() > 0 { return p.b.Read(d) } if p.err != nil { if p.readFn != nil { p.readFn() // e.g. copy trailers p.readFn = nil // not sticky like p.err } p.b = nil return 0, p.err } p.c.Wait() } } var ( errClosedPipeWrite = errors.New("write on closed buffer") errUninitializedPipeWrite = errors.New("write on uninitialized buffer") ) // Write copies bytes from p into the buffer and wakes a reader. // It is an error to write more data than the buffer can hold. func (p *pipe) Write(d []byte) (n int, err error) { p.mu.Lock() defer p.mu.Unlock() if p.c.L == nil { p.c.L = &p.mu } defer p.c.Signal() if p.err != nil || p.breakErr != nil { return 0, errClosedPipeWrite } // pipe.setBuffer is never invoked, leaving the buffer uninitialized. // We shouldn't try to write to an uninitialized pipe, // but returning an error is better than panicking. if p.b == nil { return 0, errUninitializedPipeWrite } return p.b.Write(d) } // CloseWithError causes the next Read (waking up a current blocked // Read if needed) to return the provided err after all data has been // read. // // The error must be non-nil. func (p *pipe) CloseWithError(err error) { p.closeWithError(&p.err, err, nil) } // BreakWithError causes the next Read (waking up a current blocked // Read if needed) to return the provided err immediately, without // waiting for unread data. func (p *pipe) BreakWithError(err error) { p.closeWithError(&p.breakErr, err, nil) } // closeWithErrorAndCode is like CloseWithError but also sets some code to run // in the caller's goroutine before returning the error. func (p *pipe) closeWithErrorAndCode(err error, fn func()) { p.closeWithError(&p.err, err, fn) } func (p *pipe) closeWithError(dst *error, err error, fn func()) { if err == nil { panic("err must be non-nil") } p.mu.Lock() defer p.mu.Unlock() if p.c.L == nil { p.c.L = &p.mu } defer p.c.Signal() if *dst != nil { // Already been done. return } p.readFn = fn if dst == &p.breakErr { if p.b != nil { p.unread += p.b.Len() } p.b = nil } *dst = err p.closeDoneLocked() } // requires p.mu be held. func (p *pipe) closeDoneLocked() { if p.donec == nil { return } // Close if unclosed. This isn't racy since we always // hold p.mu while closing. select { case <-p.donec: default: close(p.donec) } } // Err returns the error (if any) first set by BreakWithError or CloseWithError. func (p *pipe) Err() error { p.mu.Lock() defer p.mu.Unlock() if p.breakErr != nil { return p.breakErr } return p.err } // Done returns a channel which is closed if and when this pipe is closed // with CloseWithError. func (p *pipe) Done() <-chan struct{} { p.mu.Lock() defer p.mu.Unlock() if p.donec == nil { p.donec = make(chan struct{}) if p.err != nil || p.breakErr != nil { // Already hit an error. p.closeDoneLocked() } } return p.donec } ================================================ FILE: vendor/golang.org/x/net/http2/server.go ================================================ [File too large to display: 103.9 KB] ================================================ FILE: vendor/golang.org/x/net/http2/transport.go ================================================ // Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Transport code. package http2 import ( "bufio" "bytes" "compress/flate" "compress/gzip" "context" "crypto/rand" "crypto/tls" "errors" "fmt" "io" "io/fs" "log" "math" "math/bits" mathrand "math/rand" "net" "net/http" "net/http/httptrace" "net/textproto" "strconv" "strings" "sync" "sync/atomic" "time" "golang.org/x/net/http/httpguts" "golang.org/x/net/http2/hpack" "golang.org/x/net/idna" "golang.org/x/net/internal/httpcommon" ) const ( // transportDefaultConnFlow is how many connection-level flow control // tokens we give the server at start-up, past the default 64k. transportDefaultConnFlow = 1 << 30 // transportDefaultStreamFlow is how many stream-level flow // control tokens we announce to the peer, and how many bytes // we buffer per stream. transportDefaultStreamFlow = 4 << 20 defaultUserAgent = "Go-http-client/2.0" // initialMaxConcurrentStreams is a connections maxConcurrentStreams until // it's received servers initial SETTINGS frame, which corresponds with the // spec's minimum recommended value. initialMaxConcurrentStreams = 100 // defaultMaxConcurrentStreams is a connections default maxConcurrentStreams // if the server doesn't include one in its initial SETTINGS frame. defaultMaxConcurrentStreams = 1000 ) // Transport is an HTTP/2 Transport. // // A Transport internally caches connections to servers. It is safe // for concurrent use by multiple goroutines. type Transport struct { // DialTLSContext specifies an optional dial function with context for // creating TLS connections for requests. // // If DialTLSContext and DialTLS is nil, tls.Dial is used. // // If the returned net.Conn has a ConnectionState method like tls.Conn, // it will be used to set http.Response.TLS. DialTLSContext func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) // DialTLS specifies an optional dial function for creating // TLS connections for requests. // // If DialTLSContext and DialTLS is nil, tls.Dial is used. // // Deprecated: Use DialTLSContext instead, which allows the transport // to cancel dials as soon as they are no longer needed. // If both are set, DialTLSContext takes priority. DialTLS func(network, addr string, cfg *tls.Config) (net.Conn, error) // TLSClientConfig specifies the TLS configuration to use with // tls.Client. If nil, the default configuration is used. TLSClientConfig *tls.Config // ConnPool optionally specifies an alternate connection pool to use. // If nil, the default is used. ConnPool ClientConnPool // DisableCompression, if true, prevents the Transport from // requesting compression with an "Accept-Encoding: gzip" // request header when the Request contains no existing // Accept-Encoding value. If the Transport requests gzip on // its own and gets a gzipped response, it's transparently // decoded in the Response.Body. However, if the user // explicitly requested gzip it is not automatically // uncompressed. DisableCompression bool // AllowHTTP, if true, permits HTTP/2 requests using the insecure, // plain-text "http" scheme. Note that this does not enable h2c support. AllowHTTP bool // MaxHeaderListSize is the http2 SETTINGS_MAX_HEADER_LIST_SIZE to // send in the initial settings frame. It is how many bytes // of response headers are allowed. Unlike the http2 spec, zero here // means to use a default limit (currently 10MB). If you actually // want to advertise an unlimited value to the peer, Transport // interprets the highest possible value here (0xffffffff or 1<<32-1) // to mean no limit. MaxHeaderListSize uint32 // MaxReadFrameSize is the http2 SETTINGS_MAX_FRAME_SIZE to send in the // initial settings frame. It is the size in bytes of the largest frame // payload that the sender is willing to receive. If 0, no setting is // sent, and the value is provided by the peer, which should be 16384 // according to the spec: // https://datatracker.ietf.org/doc/html/rfc7540#section-6.5.2. // Values are bounded in the range 16k to 16M. MaxReadFrameSize uint32 // MaxDecoderHeaderTableSize optionally specifies the http2 // SETTINGS_HEADER_TABLE_SIZE to send in the initial settings frame. It // informs the remote endpoint of the maximum size of the header compression // table used to decode header blocks, in octets. If zero, the default value // of 4096 is used. MaxDecoderHeaderTableSize uint32 // MaxEncoderHeaderTableSize optionally specifies an upper limit for the // header compression table used for encoding request headers. Received // SETTINGS_HEADER_TABLE_SIZE settings are capped at this limit. If zero, // the default value of 4096 is used. MaxEncoderHeaderTableSize uint32 // StrictMaxConcurrentStreams controls whether the server's // SETTINGS_MAX_CONCURRENT_STREAMS should be respected // globally. If false, new TCP connections are created to the // server as needed to keep each under the per-connection // SETTINGS_MAX_CONCURRENT_STREAMS limit. If true, the // server's SETTINGS_MAX_CONCURRENT_STREAMS is interpreted as // a global limit and callers of RoundTrip block when needed, // waiting for their turn. StrictMaxConcurrentStreams bool // IdleConnTimeout is the maximum amount of time an idle // (keep-alive) connection will remain idle before closing // itself. // Zero means no limit. IdleConnTimeout time.Duration // ReadIdleTimeout is the timeout after which a health check using ping // frame will be carried out if no frame is received on the connection. // Note that a ping response will is considered a received frame, so if // there is no other traffic on the connection, the health check will // be performed every ReadIdleTimeout interval. // If zero, no health check is performed. ReadIdleTimeout time.Duration // PingTimeout is the timeout after which the connection will be closed // if a response to Ping is not received. // Defaults to 15s. PingTimeout time.Duration // WriteByteTimeout is the timeout after which the connection will be // closed no data can be written to it. The timeout begins when data is // available to write, and is extended whenever any bytes are written. WriteByteTimeout time.Duration // CountError, if non-nil, is called on HTTP/2 transport errors. // It's intended to increment a metric for monitoring, such // as an expvar or Prometheus metric. // The errType consists of only ASCII word characters. CountError func(errType string) // t1, if non-nil, is the standard library Transport using // this transport. Its settings are used (but not its // RoundTrip method, etc). t1 *http.Transport connPoolOnce sync.Once connPoolOrDef ClientConnPool // non-nil version of ConnPool *transportTestHooks } // Hook points used for testing. // Outside of tests, t.transportTestHooks is nil and these all have minimal implementations. // Inside tests, see the testSyncHooks function docs. type transportTestHooks struct { newclientconn func(*ClientConn) } func (t *Transport) maxHeaderListSize() uint32 { n := int64(t.MaxHeaderListSize) if t.t1 != nil && t.t1.MaxResponseHeaderBytes != 0 { n = t.t1.MaxResponseHeaderBytes if n > 0 { n = adjustHTTP1MaxHeaderSize(n) } } if n <= 0 { return 10 << 20 } if n >= 0xffffffff { return 0 } return uint32(n) } func (t *Transport) disableCompression() bool { return t.DisableCompression || (t.t1 != nil && t.t1.DisableCompression) } // ConfigureTransport configures a net/http HTTP/1 Transport to use HTTP/2. // It returns an error if t1 has already been HTTP/2-enabled. // // Use ConfigureTransports instead to configure the HTTP/2 Transport. func ConfigureTransport(t1 *http.Transport) error { _, err := ConfigureTransports(t1) return err } // ConfigureTransports configures a net/http HTTP/1 Transport to use HTTP/2. // It returns a new HTTP/2 Transport for further configuration. // It returns an error if t1 has already been HTTP/2-enabled. func ConfigureTransports(t1 *http.Transport) (*Transport, error) { return configureTransports(t1) } func configureTransports(t1 *http.Transport) (*Transport, error) { connPool := new(clientConnPool) t2 := &Transport{ ConnPool: noDialClientConnPool{connPool}, t1: t1, } connPool.t = t2 if err := registerHTTPSProtocol(t1, noDialH2RoundTripper{t2}); err != nil { return nil, err } if t1.TLSClientConfig == nil { t1.TLSClientConfig = new(tls.Config) } if !strSliceContains(t1.TLSClientConfig.NextProtos, "h2") { t1.TLSClientConfig.NextProtos = append([]string{"h2"}, t1.TLSClientConfig.NextProtos...) } if !strSliceContains(t1.TLSClientConfig.NextProtos, "http/1.1") { t1.TLSClientConfig.NextProtos = append(t1.TLSClientConfig.NextProtos, "http/1.1") } upgradeFn := func(scheme, authority string, c net.Conn) http.RoundTripper { addr := authorityAddr(scheme, authority) if used, err := connPool.addConnIfNeeded(addr, t2, c); err != nil { go c.Close() return erringRoundTripper{err} } else if !used { // Turns out we don't need this c. // For example, two goroutines made requests to the same host // at the same time, both kicking off TCP dials. (since protocol // was unknown) go c.Close() } if scheme == "http" { return (*unencryptedTransport)(t2) } return t2 } if t1.TLSNextProto == nil { t1.TLSNextProto = make(map[string]func(string, *tls.Conn) http.RoundTripper) } t1.TLSNextProto[NextProtoTLS] = func(authority string, c *tls.Conn) http.RoundTripper { return upgradeFn("https", authority, c) } // The "unencrypted_http2" TLSNextProto key is used to pass off non-TLS HTTP/2 conns. t1.TLSNextProto[nextProtoUnencryptedHTTP2] = func(authority string, c *tls.Conn) http.RoundTripper { nc, err := unencryptedNetConnFromTLSConn(c) if err != nil { go c.Close() return erringRoundTripper{err} } return upgradeFn("http", authority, nc) } return t2, nil } // unencryptedTransport is a Transport with a RoundTrip method that // always permits http:// URLs. type unencryptedTransport Transport func (t *unencryptedTransport) RoundTrip(req *http.Request) (*http.Response, error) { return (*Transport)(t).RoundTripOpt(req, RoundTripOpt{allowHTTP: true}) } func (t *Transport) connPool() ClientConnPool { t.connPoolOnce.Do(t.initConnPool) return t.connPoolOrDef } func (t *Transport) initConnPool() { if t.ConnPool != nil { t.connPoolOrDef = t.ConnPool } else { t.connPoolOrDef = &clientConnPool{t: t} } } // ClientConn is the state of a single HTTP/2 client connection to an // HTTP/2 server. type ClientConn struct { t *Transport tconn net.Conn // usually *tls.Conn, except specialized impls tlsState *tls.ConnectionState // nil only for specialized impls atomicReused uint32 // whether conn is being reused; atomic singleUse bool // whether being used for a single http.Request getConnCalled bool // used by clientConnPool // readLoop goroutine fields: readerDone chan struct{} // closed on error readerErr error // set before readerDone is closed idleTimeout time.Duration // or 0 for never idleTimer *time.Timer mu sync.Mutex // guards following cond *sync.Cond // hold mu; broadcast on flow/closed changes flow outflow // our conn-level flow control quota (cs.outflow is per stream) inflow inflow // peer's conn-level flow control doNotReuse bool // whether conn is marked to not be reused for any future requests closing bool closed bool closedOnIdle bool // true if conn was closed for idleness seenSettings bool // true if we've seen a settings frame, false otherwise seenSettingsChan chan struct{} // closed when seenSettings is true or frame reading fails wantSettingsAck bool // we sent a SETTINGS frame and haven't heard back goAway *GoAwayFrame // if non-nil, the GoAwayFrame we received goAwayDebug string // goAway frame's debug data, retained as a string streams map[uint32]*clientStream // client-initiated streamsReserved int // incr by ReserveNewRequest; decr on RoundTrip nextStreamID uint32 pendingRequests int // requests blocked and waiting to be sent because len(streams) == maxConcurrentStreams pings map[[8]byte]chan struct{} // in flight ping data to notification channel br *bufio.Reader lastActive time.Time lastIdle time.Time // time last idle // Settings from peer: (also guarded by wmu) maxFrameSize uint32 maxConcurrentStreams uint32 peerMaxHeaderListSize uint64 peerMaxHeaderTableSize uint32 initialWindowSize uint32 initialStreamRecvWindowSize int32 readIdleTimeout time.Duration pingTimeout time.Duration extendedConnectAllowed bool strictMaxConcurrentStreams bool // rstStreamPingsBlocked works around an unfortunate gRPC behavior. // gRPC strictly limits the number of PING frames that it will receive. // The default is two pings per two hours, but the limit resets every time // the gRPC endpoint sends a HEADERS or DATA frame. See golang/go#70575. // // rstStreamPingsBlocked is set after receiving a response to a PING frame // bundled with an RST_STREAM (see pendingResets below), and cleared after // receiving a HEADERS or DATA frame. rstStreamPingsBlocked bool // pendingResets is the number of RST_STREAM frames we have sent to the peer, // without confirming that the peer has received them. When we send a RST_STREAM, // we bundle it with a PING frame, unless a PING is already in flight. We count // the reset stream against the connection's concurrency limit until we get // a PING response. This limits the number of requests we'll try to send to a // completely unresponsive connection. pendingResets int // reqHeaderMu is a 1-element semaphore channel controlling access to sending new requests. // Write to reqHeaderMu to lock it, read from it to unlock. // Lock reqmu BEFORE mu or wmu. reqHeaderMu chan struct{} // wmu is held while writing. // Acquire BEFORE mu when holding both, to avoid blocking mu on network writes. // Only acquire both at the same time when changing peer settings. wmu sync.Mutex bw *bufio.Writer fr *Framer werr error // first write error that has occurred hbuf bytes.Buffer // HPACK encoder writes into this henc *hpack.Encoder } // clientStream is the state for a single HTTP/2 stream. One of these // is created for each Transport.RoundTrip call. type clientStream struct { cc *ClientConn // Fields of Request that we may access even after the response body is closed. ctx context.Context reqCancel <-chan struct{} trace *httptrace.ClientTrace // or nil ID uint32 bufPipe pipe // buffered pipe with the flow-controlled response payload requestedGzip bool isHead bool abortOnce sync.Once abort chan struct{} // closed to signal stream should end immediately abortErr error // set if abort is closed peerClosed chan struct{} // closed when the peer sends an END_STREAM flag donec chan struct{} // closed after the stream is in the closed state on100 chan struct{} // buffered; written to if a 100 is received respHeaderRecv chan struct{} // closed when headers are received res *http.Response // set if respHeaderRecv is closed flow outflow // guarded by cc.mu inflow inflow // guarded by cc.mu bytesRemain int64 // -1 means unknown; owned by transportResponseBody.Read readErr error // sticky read error; owned by transportResponseBody.Read reqBody io.ReadCloser reqBodyContentLength int64 // -1 means unknown reqBodyClosed chan struct{} // guarded by cc.mu; non-nil on Close, closed when done // owned by writeRequest: sentEndStream bool // sent an END_STREAM flag to the peer sentHeaders bool // owned by clientConnReadLoop: firstByte bool // got the first response byte pastHeaders bool // got first MetaHeadersFrame (actual headers) pastTrailers bool // got optional second MetaHeadersFrame (trailers) readClosed bool // peer sent an END_STREAM flag readAborted bool // read loop reset the stream totalHeaderSize int64 // total size of 1xx headers seen trailer http.Header // accumulated trailers resTrailer *http.Header // client's Response.Trailer } var got1xxFuncForTests func(int, textproto.MIMEHeader) error // get1xxTraceFunc returns the value of request's httptrace.ClientTrace.Got1xxResponse func, // if any. It returns nil if not set or if the Go version is too old. func (cs *clientStream) get1xxTraceFunc() func(int, textproto.MIMEHeader) error { if fn := got1xxFuncForTests; fn != nil { return fn } return traceGot1xxResponseFunc(cs.trace) } func (cs *clientStream) abortStream(err error) { cs.cc.mu.Lock() defer cs.cc.mu.Unlock() cs.abortStreamLocked(err) } func (cs *clientStream) abortStreamLocked(err error) { cs.abortOnce.Do(func() { cs.abortErr = err close(cs.abort) }) if cs.reqBody != nil { cs.closeReqBodyLocked() } // TODO(dneil): Clean up tests where cs.cc.cond is nil. if cs.cc.cond != nil { // Wake up writeRequestBody if it is waiting on flow control. cs.cc.cond.Broadcast() } } func (cs *clientStream) abortRequestBodyWrite() { cc := cs.cc cc.mu.Lock() defer cc.mu.Unlock() if cs.reqBody != nil && cs.reqBodyClosed == nil { cs.closeReqBodyLocked() cc.cond.Broadcast() } } func (cs *clientStream) closeReqBodyLocked() { if cs.reqBodyClosed != nil { return } cs.reqBodyClosed = make(chan struct{}) reqBodyClosed := cs.reqBodyClosed go func() { cs.reqBody.Close() close(reqBodyClosed) }() } type stickyErrWriter struct { conn net.Conn timeout time.Duration err *error } func (sew stickyErrWriter) Write(p []byte) (n int, err error) { if *sew.err != nil { return 0, *sew.err } n, err = writeWithByteTimeout(sew.conn, sew.timeout, p) *sew.err = err return n, err } // noCachedConnError is the concrete type of ErrNoCachedConn, which // needs to be detected by net/http regardless of whether it's its // bundled version (in h2_bundle.go with a rewritten type name) or // from a user's x/net/http2. As such, as it has a unique method name // (IsHTTP2NoCachedConnError) that net/http sniffs for via func // isNoCachedConnError. type noCachedConnError struct{} func (noCachedConnError) IsHTTP2NoCachedConnError() {} func (noCachedConnError) Error() string { return "http2: no cached connection was available" } // isNoCachedConnError reports whether err is of type noCachedConnError // or its equivalent renamed type in net/http2's h2_bundle.go. Both types // may coexist in the same running program. func isNoCachedConnError(err error) bool { _, ok := err.(interface{ IsHTTP2NoCachedConnError() }) return ok } var ErrNoCachedConn error = noCachedConnError{} // RoundTripOpt are options for the Transport.RoundTripOpt method. type RoundTripOpt struct { // OnlyCachedConn controls whether RoundTripOpt may // create a new TCP connection. If set true and // no cached connection is available, RoundTripOpt // will return ErrNoCachedConn. OnlyCachedConn bool allowHTTP bool // allow http:// URLs } func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { return t.RoundTripOpt(req, RoundTripOpt{}) } // authorityAddr returns a given authority (a host/IP, or host:port / ip:port) // and returns a host:port. The port 443 is added if needed. func authorityAddr(scheme string, authority string) (addr string) { host, port, err := net.SplitHostPort(authority) if err != nil { // authority didn't have a port host = authority port = "" } if port == "" { // authority's port was empty port = "443" if scheme == "http" { port = "80" } } if a, err := idna.ToASCII(host); err == nil { host = a } // IPv6 address literal, without a port: if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") { return host + ":" + port } return net.JoinHostPort(host, port) } // RoundTripOpt is like RoundTrip, but takes options. func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Response, error) { switch req.URL.Scheme { case "https": // Always okay. case "http": if !t.AllowHTTP && !opt.allowHTTP { return nil, errors.New("http2: unencrypted HTTP/2 not enabled") } default: return nil, errors.New("http2: unsupported scheme") } addr := authorityAddr(req.URL.Scheme, req.URL.Host) for retry := 0; ; retry++ { cc, err := t.connPool().GetClientConn(req, addr) if err != nil { t.vlogf("http2: Transport failed to get client conn for %s: %v", addr, err) return nil, err } reused := !atomic.CompareAndSwapUint32(&cc.atomicReused, 0, 1) traceGotConn(req, cc, reused) res, err := cc.RoundTrip(req) if err != nil && retry <= 6 { roundTripErr := err if req, err = shouldRetryRequest(req, err); err == nil { // After the first retry, do exponential backoff with 10% jitter. if retry == 0 { t.vlogf("RoundTrip retrying after failure: %v", roundTripErr) continue } backoff := float64(uint(1) << (uint(retry) - 1)) backoff += backoff * (0.1 * mathrand.Float64()) d := time.Second * time.Duration(backoff) tm := time.NewTimer(d) select { case <-tm.C: t.vlogf("RoundTrip retrying after failure: %v", roundTripErr) continue case <-req.Context().Done(): tm.Stop() err = req.Context().Err() } } } if err == errClientConnNotEstablished { // This ClientConn was created recently, // this is the first request to use it, // and the connection is closed and not usable. // // In this state, cc.idleTimer will remove the conn from the pool // when it fires. Stop the timer and remove it here so future requests // won't try to use this connection. // // If the timer has already fired and we're racing it, the redundant // call to MarkDead is harmless. if cc.idleTimer != nil { cc.idleTimer.Stop() } t.connPool().MarkDead(cc) } if err != nil { t.vlogf("RoundTrip failure: %v", err) return nil, err } return res, nil } } // CloseIdleConnections closes any connections which were previously // connected from previous requests but are now sitting idle. // It does not interrupt any connections currently in use. func (t *Transport) CloseIdleConnections() { if cp, ok := t.connPool().(clientConnPoolIdleCloser); ok { cp.closeIdleConnections() } } var ( errClientConnClosed = errors.New("http2: client conn is closed") errClientConnUnusable = errors.New("http2: client conn not usable") errClientConnNotEstablished = errors.New("http2: client conn could not be established") errClientConnGotGoAway = errors.New("http2: Transport received Server's graceful shutdown GOAWAY") errClientConnForceClosed = errors.New("http2: client connection force closed via ClientConn.Close") ) // shouldRetryRequest is called by RoundTrip when a request fails to get // response headers. It is always called with a non-nil error. // It returns either a request to retry (either the same request, or a // modified clone), or an error if the request can't be replayed. func shouldRetryRequest(req *http.Request, err error) (*http.Request, error) { if !canRetryError(err) { return nil, err } // If the Body is nil (or http.NoBody), it's safe to reuse // this request and its Body. if req.Body == nil || req.Body == http.NoBody { return req, nil } // If the request body can be reset back to its original // state via the optional req.GetBody, do that. if req.GetBody != nil { body, err := req.GetBody() if err != nil { return nil, err } newReq := *req newReq.Body = body return &newReq, nil } // The Request.Body can't reset back to the beginning, but we // don't seem to have started to read from it yet, so reuse // the request directly. if err == errClientConnUnusable { return req, nil } return nil, fmt.Errorf("http2: Transport: cannot retry err [%v] after Request.Body was written; define Request.GetBody to avoid this error", err) } func canRetryError(err error) bool { if err == errClientConnUnusable || err == errClientConnGotGoAway { return true } if se, ok := err.(StreamError); ok { if se.Code == ErrCodeProtocol && se.Cause == errFromPeer { // See golang/go#47635, golang/go#42777 return true } return se.Code == ErrCodeRefusedStream } return false } func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse bool) (*ClientConn, error) { if t.transportTestHooks != nil { return t.newClientConn(nil, singleUse) } host, _, err := net.SplitHostPort(addr) if err != nil { return nil, err } tconn, err := t.dialTLS(ctx, "tcp", addr, t.newTLSConfig(host)) if err != nil { return nil, err } return t.newClientConn(tconn, singleUse) } func (t *Transport) newTLSConfig(host string) *tls.Config { cfg := new(tls.Config) if t.TLSClientConfig != nil { *cfg = *t.TLSClientConfig.Clone() } if !strSliceContains(cfg.NextProtos, NextProtoTLS) { cfg.NextProtos = append([]string{NextProtoTLS}, cfg.NextProtos...) } if cfg.ServerName == "" { cfg.ServerName = host } return cfg } func (t *Transport) dialTLS(ctx context.Context, network, addr string, tlsCfg *tls.Config) (net.Conn, error) { if t.DialTLSContext != nil { return t.DialTLSContext(ctx, network, addr, tlsCfg) } else if t.DialTLS != nil { return t.DialTLS(network, addr, tlsCfg) } tlsCn, err := t.dialTLSWithContext(ctx, network, addr, tlsCfg) if err != nil { return nil, err } state := tlsCn.ConnectionState() if p := state.NegotiatedProtocol; p != NextProtoTLS { return nil, fmt.Errorf("http2: unexpected ALPN protocol %q; want %q", p, NextProtoTLS) } if !state.NegotiatedProtocolIsMutual { return nil, errors.New("http2: could not negotiate protocol mutually") } return tlsCn, nil } // disableKeepAlives reports whether connections should be closed as // soon as possible after handling the first request. func (t *Transport) disableKeepAlives() bool { return t.t1 != nil && t.t1.DisableKeepAlives } func (t *Transport) expectContinueTimeout() time.Duration { if t.t1 == nil { return 0 } return t.t1.ExpectContinueTimeout } func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) { return t.newClientConn(c, t.disableKeepAlives()) } func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, error) { conf := configFromTransport(t) cc := &ClientConn{ t: t, tconn: c, readerDone: make(chan struct{}), nextStreamID: 1, maxFrameSize: 16 << 10, // spec default initialWindowSize: 65535, // spec default initialStreamRecvWindowSize: conf.MaxUploadBufferPerStream, maxConcurrentStreams: initialMaxConcurrentStreams, // "infinite", per spec. Use a smaller value until we have received server settings. strictMaxConcurrentStreams: conf.StrictMaxConcurrentRequests, peerMaxHeaderListSize: 0xffffffffffffffff, // "infinite", per spec. Use 2^64-1 instead. streams: make(map[uint32]*clientStream), singleUse: singleUse, seenSettingsChan: make(chan struct{}), wantSettingsAck: true, readIdleTimeout: conf.SendPingTimeout, pingTimeout: conf.PingTimeout, pings: make(map[[8]byte]chan struct{}), reqHeaderMu: make(chan struct{}, 1), lastActive: time.Now(), } if t.transportTestHooks != nil { t.transportTestHooks.newclientconn(cc) c = cc.tconn } if VerboseLogs { t.vlogf("http2: Transport creating client conn %p to %v", cc, c.RemoteAddr()) } cc.cond = sync.NewCond(&cc.mu) cc.flow.add(int32(initialWindowSize)) // TODO: adjust this writer size to account for frame size + // MTU + crypto/tls record padding. cc.bw = bufio.NewWriter(stickyErrWriter{ conn: c, timeout: conf.WriteByteTimeout, err: &cc.werr, }) cc.br = bufio.NewReader(c) cc.fr = NewFramer(cc.bw, cc.br) cc.fr.SetMaxReadFrameSize(conf.MaxReadFrameSize) if t.CountError != nil { cc.fr.countError = t.CountError } maxHeaderTableSize := conf.MaxDecoderHeaderTableSize cc.fr.ReadMetaHeaders = hpack.NewDecoder(maxHeaderTableSize, nil) cc.fr.MaxHeaderListSize = t.maxHeaderListSize() cc.henc = hpack.NewEncoder(&cc.hbuf) cc.henc.SetMaxDynamicTableSizeLimit(conf.MaxEncoderHeaderTableSize) cc.peerMaxHeaderTableSize = initialHeaderTableSize if cs, ok := c.(connectionStater); ok { state := cs.ConnectionState() cc.tlsState = &state } initialSettings := []Setting{ {ID: SettingEnablePush, Val: 0}, {ID: SettingInitialWindowSize, Val: uint32(cc.initialStreamRecvWindowSize)}, } initialSettings = append(initialSettings, Setting{ID: SettingMaxFrameSize, Val: conf.MaxReadFrameSize}) if max := t.maxHeaderListSize(); max != 0 { initialSettings = append(initialSettings, Setting{ID: SettingMaxHeaderListSize, Val: max}) } if maxHeaderTableSize != initialHeaderTableSize { initialSettings = append(initialSettings, Setting{ID: SettingHeaderTableSize, Val: maxHeaderTableSize}) } cc.bw.Write(clientPreface) cc.fr.WriteSettings(initialSettings...) cc.fr.WriteWindowUpdate(0, uint32(conf.MaxUploadBufferPerConnection)) cc.inflow.init(conf.MaxUploadBufferPerConnection + initialWindowSize) cc.bw.Flush() if cc.werr != nil { cc.Close() return nil, cc.werr } // Start the idle timer after the connection is fully initialized. if d := t.idleConnTimeout(); d != 0 { cc.idleTimeout = d cc.idleTimer = time.AfterFunc(d, cc.onIdleTimeout) } go cc.readLoop() return cc, nil } func (cc *ClientConn) healthCheck() { pingTimeout := cc.pingTimeout // We don't need to periodically ping in the health check, because the readLoop of ClientConn will // trigger the healthCheck again if there is no frame received. ctx, cancel := context.WithTimeout(context.Background(), pingTimeout) defer cancel() cc.vlogf("http2: Transport sending health check") err := cc.Ping(ctx) if err != nil { cc.vlogf("http2: Transport health check failure: %v", err) cc.closeForLostPing() } else { cc.vlogf("http2: Transport health check success") } } // SetDoNotReuse marks cc as not reusable for future HTTP requests. func (cc *ClientConn) SetDoNotReuse() { cc.mu.Lock() defer cc.mu.Unlock() cc.doNotReuse = true } func (cc *ClientConn) setGoAway(f *GoAwayFrame) { cc.mu.Lock() defer cc.mu.Unlock() old := cc.goAway cc.goAway = f // Merge the previous and current GoAway error frames. if cc.goAwayDebug == "" { cc.goAwayDebug = string(f.DebugData()) } if old != nil && old.ErrCode != ErrCodeNo { cc.goAway.ErrCode = old.ErrCode } last := f.LastStreamID for streamID, cs := range cc.streams { if streamID <= last { // The server's GOAWAY indicates that it received this stream. // It will either finish processing it, or close the connection // without doing so. Either way, leave the stream alone for now. continue } if streamID == 1 && cc.goAway.ErrCode != ErrCodeNo { // Don't retry the first stream on a connection if we get a non-NO error. // If the server is sending an error on a new connection, // retrying the request on a new one probably isn't going to work. cs.abortStreamLocked(fmt.Errorf("http2: Transport received GOAWAY from server ErrCode:%v", cc.goAway.ErrCode)) } else { // Aborting the stream with errClentConnGotGoAway indicates that // the request should be retried on a new connection. cs.abortStreamLocked(errClientConnGotGoAway) } } } // CanTakeNewRequest reports whether the connection can take a new request, // meaning it has not been closed or received or sent a GOAWAY. // // If the caller is going to immediately make a new request on this // connection, use ReserveNewRequest instead. func (cc *ClientConn) CanTakeNewRequest() bool { cc.mu.Lock() defer cc.mu.Unlock() return cc.canTakeNewRequestLocked() } // ReserveNewRequest is like CanTakeNewRequest but also reserves a // concurrent stream in cc. The reservation is decremented on the // next call to RoundTrip. func (cc *ClientConn) ReserveNewRequest() bool { cc.mu.Lock() defer cc.mu.Unlock() if st := cc.idleStateLocked(); !st.canTakeNewRequest { return false } cc.streamsReserved++ return true } // ClientConnState describes the state of a ClientConn. type ClientConnState struct { // Closed is whether the connection is closed. Closed bool // Closing is whether the connection is in the process of // closing. It may be closing due to shutdown, being a // single-use connection, being marked as DoNotReuse, or // having received a GOAWAY frame. Closing bool // StreamsActive is how many streams are active. StreamsActive int // StreamsReserved is how many streams have been reserved via // ClientConn.ReserveNewRequest. StreamsReserved int // StreamsPending is how many requests have been sent in excess // of the peer's advertised MaxConcurrentStreams setting and // are waiting for other streams to complete. StreamsPending int // MaxConcurrentStreams is how many concurrent streams the // peer advertised as acceptable. Zero means no SETTINGS // frame has been received yet. MaxConcurrentStreams uint32 // LastIdle, if non-zero, is when the connection last // transitioned to idle state. LastIdle time.Time } // State returns a snapshot of cc's state. func (cc *ClientConn) State() ClientConnState { cc.wmu.Lock() maxConcurrent := cc.maxConcurrentStreams if !cc.seenSettings { maxConcurrent = 0 } cc.wmu.Unlock() cc.mu.Lock() defer cc.mu.Unlock() return ClientConnState{ Closed: cc.closed, Closing: cc.closing || cc.singleUse || cc.doNotReuse || cc.goAway != nil, StreamsActive: len(cc.streams) + cc.pendingResets, StreamsReserved: cc.streamsReserved, StreamsPending: cc.pendingRequests, LastIdle: cc.lastIdle, MaxConcurrentStreams: maxConcurrent, } } // clientConnIdleState describes the suitability of a client // connection to initiate a new RoundTrip request. type clientConnIdleState struct { canTakeNewRequest bool } func (cc *ClientConn) idleState() clientConnIdleState { cc.mu.Lock() defer cc.mu.Unlock() return cc.idleStateLocked() } func (cc *ClientConn) idleStateLocked() (st clientConnIdleState) { if cc.singleUse && cc.nextStreamID > 1 { return } var maxConcurrentOkay bool if cc.strictMaxConcurrentStreams { // We'll tell the caller we can take a new request to // prevent the caller from dialing a new TCP // connection, but then we'll block later before // writing it. maxConcurrentOkay = true } else { // We can take a new request if the total of // - active streams; // - reservation slots for new streams; and // - streams for which we have sent a RST_STREAM and a PING, // but received no subsequent frame // is less than the concurrency limit. maxConcurrentOkay = cc.currentRequestCountLocked() < int(cc.maxConcurrentStreams) } st.canTakeNewRequest = cc.goAway == nil && !cc.closed && !cc.closing && maxConcurrentOkay && !cc.doNotReuse && int64(cc.nextStreamID)+2*int64(cc.pendingRequests) < math.MaxInt32 && !cc.tooIdleLocked() // If this connection has never been used for a request and is closed, // then let it take a request (which will fail). // If the conn was closed for idleness, we're racing the idle timer; // don't try to use the conn. (Issue #70515.) // // This avoids a situation where an error early in a connection's lifetime // goes unreported. if cc.nextStreamID == 1 && cc.streamsReserved == 0 && cc.closed && !cc.closedOnIdle { st.canTakeNewRequest = true } return } // currentRequestCountLocked reports the number of concurrency slots currently in use, // including active streams, reserved slots, and reset streams waiting for acknowledgement. func (cc *ClientConn) currentRequestCountLocked() int { return len(cc.streams) + cc.streamsReserved + cc.pendingResets } func (cc *ClientConn) canTakeNewRequestLocked() bool { st := cc.idleStateLocked() return st.canTakeNewRequest } // tooIdleLocked reports whether this connection has been been sitting idle // for too much wall time. func (cc *ClientConn) tooIdleLocked() bool { // The Round(0) strips the monontonic clock reading so the // times are compared based on their wall time. We don't want // to reuse a connection that's been sitting idle during // VM/laptop suspend if monotonic time was also frozen. return cc.idleTimeout != 0 && !cc.lastIdle.IsZero() && time.Since(cc.lastIdle.Round(0)) > cc.idleTimeout } // onIdleTimeout is called from a time.AfterFunc goroutine. It will // only be called when we're idle, but because we're coming from a new // goroutine, there could be a new request coming in at the same time, // so this simply calls the synchronized closeIfIdle to shut down this // connection. The timer could just call closeIfIdle, but this is more // clear. func (cc *ClientConn) onIdleTimeout() { cc.closeIfIdle() } func (cc *ClientConn) closeConn() { t := time.AfterFunc(250*time.Millisecond, cc.forceCloseConn) defer t.Stop() cc.tconn.Close() } // A tls.Conn.Close can hang for a long time if the peer is unresponsive. // Try to shut it down more aggressively. func (cc *ClientConn) forceCloseConn() { tc, ok := cc.tconn.(*tls.Conn) if !ok { return } if nc := tc.NetConn(); nc != nil { nc.Close() } } func (cc *ClientConn) closeIfIdle() { cc.mu.Lock() if len(cc.streams) > 0 || cc.streamsReserved > 0 { cc.mu.Unlock() return } cc.closed = true cc.closedOnIdle = true nextID := cc.nextStreamID // TODO: do clients send GOAWAY too? maybe? Just Close: cc.mu.Unlock() if VerboseLogs { cc.vlogf("http2: Transport closing idle conn %p (forSingleUse=%v, maxStream=%v)", cc, cc.singleUse, nextID-2) } cc.closeConn() } func (cc *ClientConn) isDoNotReuseAndIdle() bool { cc.mu.Lock() defer cc.mu.Unlock() return cc.doNotReuse && len(cc.streams) == 0 } var shutdownEnterWaitStateHook = func() {} // Shutdown gracefully closes the client connection, waiting for running streams to complete. func (cc *ClientConn) Shutdown(ctx context.Context) error { if err := cc.sendGoAway(); err != nil { return err } // Wait for all in-flight streams to complete or connection to close done := make(chan struct{}) cancelled := false // guarded by cc.mu go func() { cc.mu.Lock() defer cc.mu.Unlock() for { if len(cc.streams) == 0 || cc.closed { cc.closed = true close(done) break } if cancelled { break } cc.cond.Wait() } }() shutdownEnterWaitStateHook() select { case <-done: cc.closeConn() return nil case <-ctx.Done(): cc.mu.Lock() // Free the goroutine above cancelled = true cc.cond.Broadcast() cc.mu.Unlock() return ctx.Err() } } func (cc *ClientConn) sendGoAway() error { cc.mu.Lock() closing := cc.closing cc.closing = true maxStreamID := cc.nextStreamID cc.mu.Unlock() if closing { // GOAWAY sent already return nil } cc.wmu.Lock() defer cc.wmu.Unlock() // Send a graceful shutdown frame to server if err := cc.fr.WriteGoAway(maxStreamID, ErrCodeNo, nil); err != nil { return err } if err := cc.bw.Flush(); err != nil { return err } // Prevent new requests return nil } // closes the client connection immediately. In-flight requests are interrupted. // err is sent to streams. func (cc *ClientConn) closeForError(err error) { cc.mu.Lock() cc.closed = true for _, cs := range cc.streams { cs.abortStreamLocked(err) } cc.cond.Broadcast() cc.mu.Unlock() cc.closeConn() } // Close closes the client connection immediately. // // In-flight requests are interrupted. For a graceful shutdown, use Shutdown instead. func (cc *ClientConn) Close() error { cc.closeForError(errClientConnForceClosed) return nil } // closes the client connection immediately. In-flight requests are interrupted. func (cc *ClientConn) closeForLostPing() { err := errors.New("http2: client connection lost") if f := cc.t.CountError; f != nil { f("conn_close_lost_ping") } cc.closeForError(err) } // errRequestCanceled is a copy of net/http's errRequestCanceled because it's not // exported. At least they'll be DeepEqual for h1-vs-h2 comparisons tests. var errRequestCanceled = errors.New("net/http: request canceled") func (cc *ClientConn) responseHeaderTimeout() time.Duration { if cc.t.t1 != nil { return cc.t.t1.ResponseHeaderTimeout } // No way to do this (yet?) with just an http2.Transport. Probably // no need. Request.Cancel this is the new way. We only need to support // this for compatibility with the old http.Transport fields when // we're doing transparent http2. return 0 } // actualContentLength returns a sanitized version of // req.ContentLength, where 0 actually means zero (not unknown) and -1 // means unknown. func actualContentLength(req *http.Request) int64 { if req.Body == nil || req.Body == http.NoBody { return 0 } if req.ContentLength != 0 { return req.ContentLength } return -1 } func (cc *ClientConn) decrStreamReservations() { cc.mu.Lock() defer cc.mu.Unlock() cc.decrStreamReservationsLocked() } func (cc *ClientConn) decrStreamReservationsLocked() { if cc.streamsReserved > 0 { cc.streamsReserved-- } } func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) { return cc.roundTrip(req, nil) } func (cc *ClientConn) roundTrip(req *http.Request, streamf func(*clientStream)) (*http.Response, error) { ctx := req.Context() cs := &clientStream{ cc: cc, ctx: ctx, reqCancel: req.Cancel, isHead: req.Method == "HEAD", reqBody: req.Body, reqBodyContentLength: actualContentLength(req), trace: httptrace.ContextClientTrace(ctx), peerClosed: make(chan struct{}), abort: make(chan struct{}), respHeaderRecv: make(chan struct{}), donec: make(chan struct{}), } cs.requestedGzip = httpcommon.IsRequestGzip(req.Method, req.Header, cc.t.disableCompression()) go cs.doRequest(req, streamf) waitDone := func() error { select { case <-cs.donec: return nil case <-ctx.Done(): return ctx.Err() case <-cs.reqCancel: return errRequestCanceled } } handleResponseHeaders := func() (*http.Response, error) { res := cs.res if res.StatusCode > 299 { // On error or status code 3xx, 4xx, 5xx, etc abort any // ongoing write, assuming that the server doesn't care // about our request body. If the server replied with 1xx or // 2xx, however, then assume the server DOES potentially // want our body (e.g. full-duplex streaming: // golang.org/issue/13444). If it turns out the server // doesn't, they'll RST_STREAM us soon enough. This is a // heuristic to avoid adding knobs to Transport. Hopefully // we can keep it. cs.abortRequestBodyWrite() } res.Request = req res.TLS = cc.tlsState if res.Body == noBody && actualContentLength(req) == 0 { // If there isn't a request or response body still being // written, then wait for the stream to be closed before // RoundTrip returns. if err := waitDone(); err != nil { return nil, err } } return res, nil } cancelRequest := func(cs *clientStream, err error) error { cs.cc.mu.Lock() bodyClosed := cs.reqBodyClosed cs.cc.mu.Unlock() // Wait for the request body to be closed. // // If nothing closed the body before now, abortStreamLocked // will have started a goroutine to close it. // // Closing the body before returning avoids a race condition // with net/http checking its readTrackingBody to see if the // body was read from or closed. See golang/go#60041. // // The body is closed in a separate goroutine without the // connection mutex held, but dropping the mutex before waiting // will keep us from holding it indefinitely if the body // close is slow for some reason. if bodyClosed != nil { <-bodyClosed } return err } for { select { case <-cs.respHeaderRecv: return handleResponseHeaders() case <-cs.abort: select { case <-cs.respHeaderRecv: // If both cs.respHeaderRecv and cs.abort are signaling, // pick respHeaderRecv. The server probably wrote the // response and immediately reset the stream. // golang.org/issue/49645 return handleResponseHeaders() default: waitDone() return nil, cs.abortErr } case <-ctx.Done(): err := ctx.Err() cs.abortStream(err) return nil, cancelRequest(cs, err) case <-cs.reqCancel: cs.abortStream(errRequestCanceled) return nil, cancelRequest(cs, errRequestCanceled) } } } // doRequest runs for the duration of the request lifetime. // // It sends the request and performs post-request cleanup (closing Request.Body, etc.). func (cs *clientStream) doRequest(req *http.Request, streamf func(*clientStream)) { err := cs.writeRequest(req, streamf) cs.cleanupWriteRequest(err) } var errExtendedConnectNotSupported = errors.New("net/http: extended connect not supported by peer") // writeRequest sends a request. // // It returns nil after the request is written, the response read, // and the request stream is half-closed by the peer. // // It returns non-nil if the request ends otherwise. // If the returned error is StreamError, the error Code may be used in resetting the stream. func (cs *clientStream) writeRequest(req *http.Request, streamf func(*clientStream)) (err error) { cc := cs.cc ctx := cs.ctx // wait for setting frames to be received, a server can change this value later, // but we just wait for the first settings frame var isExtendedConnect bool if req.Method == "CONNECT" && req.Header.Get(":protocol") != "" { isExtendedConnect = true } // Acquire the new-request lock by writing to reqHeaderMu. // This lock guards the critical section covering allocating a new stream ID // (requires mu) and creating the stream (requires wmu). if cc.reqHeaderMu == nil { panic("RoundTrip on uninitialized ClientConn") // for tests } if isExtendedConnect { select { case <-cs.reqCancel: return errRequestCanceled case <-ctx.Done(): return ctx.Err() case <-cc.seenSettingsChan: if !cc.extendedConnectAllowed { return errExtendedConnectNotSupported } } } select { case cc.reqHeaderMu <- struct{}{}: case <-cs.reqCancel: return errRequestCanceled case <-ctx.Done(): return ctx.Err() } cc.mu.Lock() if cc.idleTimer != nil { cc.idleTimer.Stop() } cc.decrStreamReservationsLocked() if err := cc.awaitOpenSlotForStreamLocked(cs); err != nil { cc.mu.Unlock() <-cc.reqHeaderMu return err } cc.addStreamLocked(cs) // assigns stream ID if isConnectionCloseRequest(req) { cc.doNotReuse = true } cc.mu.Unlock() if streamf != nil { streamf(cs) } continueTimeout := cc.t.expectContinueTimeout() if continueTimeout != 0 { if !httpguts.HeaderValuesContainsToken(req.Header["Expect"], "100-continue") { continueTimeout = 0 } else { cs.on100 = make(chan struct{}, 1) } } // Past this point (where we send request headers), it is possible for // RoundTrip to return successfully. Since the RoundTrip contract permits // the caller to "mutate or reuse" the Request after closing the Response's Body, // we must take care when referencing the Request from here on. err = cs.encodeAndWriteHeaders(req) <-cc.reqHeaderMu if err != nil { return err } hasBody := cs.reqBodyContentLength != 0 if !hasBody { cs.sentEndStream = true } else { if continueTimeout != 0 { traceWait100Continue(cs.trace) timer := time.NewTimer(continueTimeout) select { case <-timer.C: err = nil case <-cs.on100: err = nil case <-cs.abort: err = cs.abortErr case <-ctx.Done(): err = ctx.Err() case <-cs.reqCancel: err = errRequestCanceled } timer.Stop() if err != nil { traceWroteRequest(cs.trace, err) return err } } if err = cs.writeRequestBody(req); err != nil { if err != errStopReqBodyWrite { traceWroteRequest(cs.trace, err) return err } } else { cs.sentEndStream = true } } traceWroteRequest(cs.trace, err) var respHeaderTimer <-chan time.Time var respHeaderRecv chan struct{} if d := cc.responseHeaderTimeout(); d != 0 { timer := time.NewTimer(d) defer timer.Stop() respHeaderTimer = timer.C respHeaderRecv = cs.respHeaderRecv } // Wait until the peer half-closes its end of the stream, // or until the request is aborted (via context, error, or otherwise), // whichever comes first. for { select { case <-cs.peerClosed: return nil case <-respHeaderTimer: return errTimeout case <-respHeaderRecv: respHeaderRecv = nil respHeaderTimer = nil // keep waiting for END_STREAM case <-cs.abort: return cs.abortErr case <-ctx.Done(): return ctx.Err() case <-cs.reqCancel: return errRequestCanceled } } } func (cs *clientStream) encodeAndWriteHeaders(req *http.Request) error { cc := cs.cc ctx := cs.ctx cc.wmu.Lock() defer cc.wmu.Unlock() // If the request was canceled while waiting for cc.mu, just quit. select { case <-cs.abort: return cs.abortErr case <-ctx.Done(): return ctx.Err() case <-cs.reqCancel: return errRequestCanceled default: } // Encode headers. // // we send: HEADERS{1}, CONTINUATION{0,} + DATA{0,} (DATA is // sent by writeRequestBody below, along with any Trailers, // again in form HEADERS{1}, CONTINUATION{0,}) cc.hbuf.Reset() res, err := encodeRequestHeaders(req, cs.requestedGzip, cc.peerMaxHeaderListSize, func(name, value string) { cc.writeHeader(name, value) }) if err != nil { return fmt.Errorf("http2: %w", err) } hdrs := cc.hbuf.Bytes() // Write the request. endStream := !res.HasBody && !res.HasTrailers cs.sentHeaders = true err = cc.writeHeaders(cs.ID, endStream, int(cc.maxFrameSize), hdrs) traceWroteHeaders(cs.trace) return err } func encodeRequestHeaders(req *http.Request, addGzipHeader bool, peerMaxHeaderListSize uint64, headerf func(name, value string)) (httpcommon.EncodeHeadersResult, error) { return httpcommon.EncodeHeaders(req.Context(), httpcommon.EncodeHeadersParam{ Request: httpcommon.Request{ Header: req.Header, Trailer: req.Trailer, URL: req.URL, Host: req.Host, Method: req.Method, ActualContentLength: actualContentLength(req), }, AddGzipHeader: addGzipHeader, PeerMaxHeaderListSize: peerMaxHeaderListSize, DefaultUserAgent: defaultUserAgent, }, headerf) } // cleanupWriteRequest performs post-request tasks. // // If err (the result of writeRequest) is non-nil and the stream is not closed, // cleanupWriteRequest will send a reset to the peer. func (cs *clientStream) cleanupWriteRequest(err error) { cc := cs.cc if cs.ID == 0 { // We were canceled before creating the stream, so return our reservation. cc.decrStreamReservations() } // TODO: write h12Compare test showing whether // Request.Body is closed by the Transport, // and in multiple cases: server replies <=299 and >299 // while still writing request body cc.mu.Lock() mustCloseBody := false if cs.reqBody != nil && cs.reqBodyClosed == nil { mustCloseBody = true cs.reqBodyClosed = make(chan struct{}) } bodyClosed := cs.reqBodyClosed closeOnIdle := cc.singleUse || cc.doNotReuse || cc.t.disableKeepAlives() || cc.goAway != nil cc.mu.Unlock() if mustCloseBody { cs.reqBody.Close() close(bodyClosed) } if bodyClosed != nil { <-bodyClosed } if err != nil && cs.sentEndStream { // If the connection is closed immediately after the response is read, // we may be aborted before finishing up here. If the stream was closed // cleanly on both sides, there is no error. select { case <-cs.peerClosed: err = nil default: } } if err != nil { cs.abortStream(err) // possibly redundant, but harmless if cs.sentHeaders { if se, ok := err.(StreamError); ok { if se.Cause != errFromPeer { cc.writeStreamReset(cs.ID, se.Code, false, err) } } else { // We're cancelling an in-flight request. // // This could be due to the server becoming unresponsive. // To avoid sending too many requests on a dead connection, // we let the request continue to consume a concurrency slot // until we can confirm the server is still responding. // We do this by sending a PING frame along with the RST_STREAM // (unless a ping is already in flight). // // For simplicity, we don't bother tracking the PING payload: // We reset cc.pendingResets any time we receive a PING ACK. // // We skip this if the conn is going to be closed on idle, // because it's short lived and will probably be closed before // we get the ping response. ping := false if !closeOnIdle { cc.mu.Lock() // rstStreamPingsBlocked works around a gRPC behavior: // see comment on the field for details. if !cc.rstStreamPingsBlocked { if cc.pendingResets == 0 { ping = true } cc.pendingResets++ } cc.mu.Unlock() } cc.writeStreamReset(cs.ID, ErrCodeCancel, ping, err) } } cs.bufPipe.CloseWithError(err) // no-op if already closed } else { if cs.sentHeaders && !cs.sentEndStream { cc.writeStreamReset(cs.ID, ErrCodeNo, false, nil) } cs.bufPipe.CloseWithError(errRequestCanceled) } if cs.ID != 0 { cc.forgetStreamID(cs.ID) } cc.wmu.Lock() werr := cc.werr cc.wmu.Unlock() if werr != nil { cc.Close() } close(cs.donec) } // awaitOpenSlotForStreamLocked waits until len(streams) < maxConcurrentStreams. // Must hold cc.mu. func (cc *ClientConn) awaitOpenSlotForStreamLocked(cs *clientStream) error { for { if cc.closed && cc.nextStreamID == 1 && cc.streamsReserved == 0 { // This is the very first request sent to this connection. // Return a fatal error which aborts the retry loop. return errClientConnNotEstablished } cc.lastActive = time.Now() if cc.closed || !cc.canTakeNewRequestLocked() { return errClientConnUnusable } cc.lastIdle = time.Time{} if cc.currentRequestCountLocked() < int(cc.maxConcurrentStreams) { return nil } cc.pendingRequests++ cc.cond.Wait() cc.pendingRequests-- select { case <-cs.abort: return cs.abortErr default: } } } // requires cc.wmu be held func (cc *ClientConn) writeHeaders(streamID uint32, endStream bool, maxFrameSize int, hdrs []byte) error { first := true // first frame written (HEADERS is first, then CONTINUATION) for len(hdrs) > 0 && cc.werr == nil { chunk := hdrs if len(chunk) > maxFrameSize { chunk = chunk[:maxFrameSize] } hdrs = hdrs[len(chunk):] endHeaders := len(hdrs) == 0 if first { cc.fr.WriteHeaders(HeadersFrameParam{ StreamID: streamID, BlockFragment: chunk, EndStream: endStream, EndHeaders: endHeaders, }) first = false } else { cc.fr.WriteContinuation(streamID, endHeaders, chunk) } } cc.bw.Flush() return cc.werr } // internal error values; they don't escape to callers var ( // abort request body write; don't send cancel errStopReqBodyWrite = errors.New("http2: aborting request body write") // abort request body write, but send stream reset of cancel. errStopReqBodyWriteAndCancel = errors.New("http2: canceling request") errReqBodyTooLong = errors.New("http2: request body larger than specified content length") ) // frameScratchBufferLen returns the length of a buffer to use for // outgoing request bodies to read/write to/from. // // It returns max(1, min(peer's advertised max frame size, // Request.ContentLength+1, 512KB)). func (cs *clientStream) frameScratchBufferLen(maxFrameSize int) int { const max = 512 << 10 n := int64(maxFrameSize) if n > max { n = max } if cl := cs.reqBodyContentLength; cl != -1 && cl+1 < n { // Add an extra byte past the declared content-length to // give the caller's Request.Body io.Reader a chance to // give us more bytes than they declared, so we can catch it // early. n = cl + 1 } if n < 1 { return 1 } return int(n) // doesn't truncate; max is 512K } // Seven bufPools manage different frame sizes. This helps to avoid scenarios where long-running // streaming requests using small frame sizes occupy large buffers initially allocated for prior // requests needing big buffers. The size ranges are as follows: // {0 KB, 16 KB], {16 KB, 32 KB], {32 KB, 64 KB], {64 KB, 128 KB], {128 KB, 256 KB], // {256 KB, 512 KB], {512 KB, infinity} // In practice, the maximum scratch buffer size should not exceed 512 KB due to // frameScratchBufferLen(maxFrameSize), thus the "infinity pool" should never be used. // It exists mainly as a safety measure, for potential future increases in max buffer size. var bufPools [7]sync.Pool // of *[]byte func bufPoolIndex(size int) int { if size <= 16384 { return 0 } size -= 1 bits := bits.Len(uint(size)) index := bits - 14 if index >= len(bufPools) { return len(bufPools) - 1 } return index } func (cs *clientStream) writeRequestBody(req *http.Request) (err error) { cc := cs.cc body := cs.reqBody sentEnd := false // whether we sent the final DATA frame w/ END_STREAM hasTrailers := req.Trailer != nil remainLen := cs.reqBodyContentLength hasContentLen := remainLen != -1 cc.mu.Lock() maxFrameSize := int(cc.maxFrameSize) cc.mu.Unlock() // Scratch buffer for reading into & writing from. scratchLen := cs.frameScratchBufferLen(maxFrameSize) var buf []byte index := bufPoolIndex(scratchLen) if bp, ok := bufPools[index].Get().(*[]byte); ok && len(*bp) >= scratchLen { defer bufPools[index].Put(bp) buf = *bp } else { buf = make([]byte, scratchLen) defer bufPools[index].Put(&buf) } var sawEOF bool for !sawEOF { n, err := body.Read(buf) if hasContentLen { remainLen -= int64(n) if remainLen == 0 && err == nil { // The request body's Content-Length was predeclared and // we just finished reading it all, but the underlying io.Reader // returned the final chunk with a nil error (which is one of // the two valid things a Reader can do at EOF). Because we'd prefer // to send the END_STREAM bit early, double-check that we're actually // at EOF. Subsequent reads should return (0, EOF) at this point. // If either value is different, we return an error in one of two ways below. var scratch [1]byte var n1 int n1, err = body.Read(scratch[:]) remainLen -= int64(n1) } if remainLen < 0 { err = errReqBodyTooLong return err } } if err != nil { cc.mu.Lock() bodyClosed := cs.reqBodyClosed != nil cc.mu.Unlock() switch { case bodyClosed: return errStopReqBodyWrite case err == io.EOF: sawEOF = true err = nil default: return err } } remain := buf[:n] for len(remain) > 0 && err == nil { var allowed int32 allowed, err = cs.awaitFlowControl(len(remain)) if err != nil { return err } cc.wmu.Lock() data := remain[:allowed] remain = remain[allowed:] sentEnd = sawEOF && len(remain) == 0 && !hasTrailers err = cc.fr.WriteData(cs.ID, sentEnd, data) if err == nil { // TODO(bradfitz): this flush is for latency, not bandwidth. // Most requests won't need this. Make this opt-in or // opt-out? Use some heuristic on the body type? Nagel-like // timers? Based on 'n'? Only last chunk of this for loop, // unless flow control tokens are low? For now, always. // If we change this, see comment below. err = cc.bw.Flush() } cc.wmu.Unlock() } if err != nil { return err } } if sentEnd { // Already sent END_STREAM (which implies we have no // trailers) and flushed, because currently all // WriteData frames above get a flush. So we're done. return nil } // Since the RoundTrip contract permits the caller to "mutate or reuse" // a request after the Response's Body is closed, verify that this hasn't // happened before accessing the trailers. cc.mu.Lock() trailer := req.Trailer err = cs.abortErr cc.mu.Unlock() if err != nil { return err } cc.wmu.Lock() defer cc.wmu.Unlock() var trls []byte if len(trailer) > 0 { trls, err = cc.encodeTrailers(trailer) if err != nil { return err } } // Two ways to send END_STREAM: either with trailers, or // with an empty DATA frame. if len(trls) > 0 { err = cc.writeHeaders(cs.ID, true, maxFrameSize, trls) } else { err = cc.fr.WriteData(cs.ID, true, nil) } if ferr := cc.bw.Flush(); ferr != nil && err == nil { err = ferr } return err } // awaitFlowControl waits for [1, min(maxBytes, cc.cs.maxFrameSize)] flow // control tokens from the server. // It returns either the non-zero number of tokens taken or an error // if the stream is dead. func (cs *clientStream) awaitFlowControl(maxBytes int) (taken int32, err error) { cc := cs.cc ctx := cs.ctx cc.mu.Lock() defer cc.mu.Unlock() for { if cc.closed { return 0, errClientConnClosed } if cs.reqBodyClosed != nil { return 0, errStopReqBodyWrite } select { case <-cs.abort: return 0, cs.abortErr case <-ctx.Done(): return 0, ctx.Err() case <-cs.reqCancel: return 0, errRequestCanceled default: } if a := cs.flow.available(); a > 0 { take := a if int(take) > maxBytes { take = int32(maxBytes) // can't truncate int; take is int32 } if take > int32(cc.maxFrameSize) { take = int32(cc.maxFrameSize) } cs.flow.take(take) return take, nil } cc.cond.Wait() } } // requires cc.wmu be held. func (cc *ClientConn) encodeTrailers(trailer http.Header) ([]byte, error) { cc.hbuf.Reset() hlSize := uint64(0) for k, vv := range trailer { for _, v := range vv { hf := hpack.HeaderField{Name: k, Value: v} hlSize += uint64(hf.Size()) } } if hlSize > cc.peerMaxHeaderListSize { return nil, errRequestHeaderListSize } for k, vv := range trailer { lowKey, ascii := httpcommon.LowerHeader(k) if !ascii { // Skip writing invalid headers. Per RFC 7540, Section 8.1.2, header // field names have to be ASCII characters (just as in HTTP/1.x). continue } // Transfer-Encoding, etc.. have already been filtered at the // start of RoundTrip for _, v := range vv { cc.writeHeader(lowKey, v) } } return cc.hbuf.Bytes(), nil } func (cc *ClientConn) writeHeader(name, value string) { if VerboseLogs { log.Printf("http2: Transport encoding header %q = %q", name, value) } cc.henc.WriteField(hpack.HeaderField{Name: name, Value: value}) } type resAndError struct { _ incomparable res *http.Response err error } // requires cc.mu be held. func (cc *ClientConn) addStreamLocked(cs *clientStream) { cs.flow.add(int32(cc.initialWindowSize)) cs.flow.setConnFlow(&cc.flow) cs.inflow.init(cc.initialStreamRecvWindowSize) cs.ID = cc.nextStreamID cc.nextStreamID += 2 cc.streams[cs.ID] = cs if cs.ID == 0 { panic("assigned stream ID 0") } } func (cc *ClientConn) forgetStreamID(id uint32) { cc.mu.Lock() slen := len(cc.streams) delete(cc.streams, id) if len(cc.streams) != slen-1 { panic("forgetting unknown stream id") } cc.lastActive = time.Now() if len(cc.streams) == 0 && cc.idleTimer != nil { cc.idleTimer.Reset(cc.idleTimeout) cc.lastIdle = time.Now() } // Wake up writeRequestBody via clientStream.awaitFlowControl and // wake up RoundTrip if there is a pending request. cc.cond.Broadcast() closeOnIdle := cc.singleUse || cc.doNotReuse || cc.t.disableKeepAlives() || cc.goAway != nil if closeOnIdle && cc.streamsReserved == 0 && len(cc.streams) == 0 { if VerboseLogs { cc.vlogf("http2: Transport closing idle conn %p (forSingleUse=%v, maxStream=%v)", cc, cc.singleUse, cc.nextStreamID-2) } cc.closed = true defer cc.closeConn() } cc.mu.Unlock() } // clientConnReadLoop is the state owned by the clientConn's frame-reading readLoop. type clientConnReadLoop struct { _ incomparable cc *ClientConn } // readLoop runs in its own goroutine and reads and dispatches frames. func (cc *ClientConn) readLoop() { rl := &clientConnReadLoop{cc: cc} defer rl.cleanup() cc.readerErr = rl.run() if ce, ok := cc.readerErr.(ConnectionError); ok { cc.wmu.Lock() cc.fr.WriteGoAway(0, ErrCode(ce), nil) cc.wmu.Unlock() } } // GoAwayError is returned by the Transport when the server closes the // TCP connection after sending a GOAWAY frame. type GoAwayError struct { LastStreamID uint32 ErrCode ErrCode DebugData string } func (e GoAwayError) Error() string { return fmt.Sprintf("http2: server sent GOAWAY and closed the connection; LastStreamID=%v, ErrCode=%v, debug=%q", e.LastStreamID, e.ErrCode, e.DebugData) } func isEOFOrNetReadError(err error) bool { if err == io.EOF { return true } ne, ok := err.(*net.OpError) return ok && ne.Op == "read" } func (rl *clientConnReadLoop) cleanup() { cc := rl.cc defer cc.closeConn() defer close(cc.readerDone) if cc.idleTimer != nil { cc.idleTimer.Stop() } // Close any response bodies if the server closes prematurely. // TODO: also do this if we've written the headers but not // gotten a response yet. err := cc.readerErr cc.mu.Lock() if cc.goAway != nil && isEOFOrNetReadError(err) { err = GoAwayError{ LastStreamID: cc.goAway.LastStreamID, ErrCode: cc.goAway.ErrCode, DebugData: cc.goAwayDebug, } } else if err == io.EOF { err = io.ErrUnexpectedEOF } cc.closed = true // If the connection has never been used, and has been open for only a short time, // leave it in the connection pool for a little while. // // This avoids a situation where new connections are constantly created, // added to the pool, fail, and are removed from the pool, without any error // being surfaced to the user. unusedWaitTime := 5 * time.Second if cc.idleTimeout > 0 && unusedWaitTime > cc.idleTimeout { unusedWaitTime = cc.idleTimeout } idleTime := time.Now().Sub(cc.lastActive) if atomic.LoadUint32(&cc.atomicReused) == 0 && idleTime < unusedWaitTime && !cc.closedOnIdle { cc.idleTimer = time.AfterFunc(unusedWaitTime-idleTime, func() { cc.t.connPool().MarkDead(cc) }) } else { cc.mu.Unlock() // avoid any deadlocks in MarkDead cc.t.connPool().MarkDead(cc) cc.mu.Lock() } for _, cs := range cc.streams { select { case <-cs.peerClosed: // The server closed the stream before closing the conn, // so no need to interrupt it. default: cs.abortStreamLocked(err) } } cc.cond.Broadcast() cc.mu.Unlock() if !cc.seenSettings { // If we have a pending request that wants extended CONNECT, // let it continue and fail with the connection error. cc.extendedConnectAllowed = true close(cc.seenSettingsChan) } } // countReadFrameError calls Transport.CountError with a string // representing err. func (cc *ClientConn) countReadFrameError(err error) { f := cc.t.CountError if f == nil || err == nil { return } if ce, ok := err.(ConnectionError); ok { errCode := ErrCode(ce) f(fmt.Sprintf("read_frame_conn_error_%s", errCode.stringToken())) return } if errors.Is(err, io.EOF) { f("read_frame_eof") return } if errors.Is(err, io.ErrUnexpectedEOF) { f("read_frame_unexpected_eof") return } if errors.Is(err, ErrFrameTooLarge) { f("read_frame_too_large") return } f("read_frame_other") } func (rl *clientConnReadLoop) run() error { cc := rl.cc gotSettings := false readIdleTimeout := cc.readIdleTimeout var t *time.Timer if readIdleTimeout != 0 { t = time.AfterFunc(readIdleTimeout, cc.healthCheck) } for { f, err := cc.fr.ReadFrame() if t != nil { t.Reset(readIdleTimeout) } if err != nil { cc.vlogf("http2: Transport readFrame error on conn %p: (%T) %v", cc, err, err) } if se, ok := err.(StreamError); ok { if cs := rl.streamByID(se.StreamID, notHeaderOrDataFrame); cs != nil { if se.Cause == nil { se.Cause = cc.fr.errDetail } rl.endStreamError(cs, se) } continue } else if err != nil { cc.countReadFrameError(err) return err } if VerboseLogs { cc.vlogf("http2: Transport received %s", summarizeFrame(f)) } if !gotSettings { if _, ok := f.(*SettingsFrame); !ok { cc.logf("protocol error: received %T before a SETTINGS frame", f) return ConnectionError(ErrCodeProtocol) } gotSettings = true } switch f := f.(type) { case *MetaHeadersFrame: err = rl.processHeaders(f) case *DataFrame: err = rl.processData(f) case *GoAwayFrame: err = rl.processGoAway(f) case *RSTStreamFrame: err = rl.processResetStream(f) case *SettingsFrame: err = rl.processSettings(f) case *PushPromiseFrame: err = rl.processPushPromise(f) case *WindowUpdateFrame: err = rl.processWindowUpdate(f) case *PingFrame: err = rl.processPing(f) default: cc.logf("Transport: unhandled response frame type %T", f) } if err != nil { if VerboseLogs { cc.vlogf("http2: Transport conn %p received error from processing frame %v: %v", cc, summarizeFrame(f), err) } return err } } } func (rl *clientConnReadLoop) processHeaders(f *MetaHeadersFrame) error { cs := rl.streamByID(f.StreamID, headerOrDataFrame) if cs == nil { // We'd get here if we canceled a request while the // server had its response still in flight. So if this // was just something we canceled, ignore it. return nil } if cs.readClosed { rl.endStreamError(cs, StreamError{ StreamID: f.StreamID, Code: ErrCodeProtocol, Cause: errors.New("protocol error: headers after END_STREAM"), }) return nil } if !cs.firstByte { if cs.trace != nil { // TODO(bradfitz): move first response byte earlier, // when we first read the 9 byte header, not waiting // until all the HEADERS+CONTINUATION frames have been // merged. This works for now. traceFirstResponseByte(cs.trace) } cs.firstByte = true } if !cs.pastHeaders { cs.pastHeaders = true } else { return rl.processTrailers(cs, f) } res, err := rl.handleResponse(cs, f) if err != nil { if _, ok := err.(ConnectionError); ok { return err } // Any other error type is a stream error. rl.endStreamError(cs, StreamError{ StreamID: f.StreamID, Code: ErrCodeProtocol, Cause: err, }) return nil // return nil from process* funcs to keep conn alive } if res == nil { // (nil, nil) special case. See handleResponse docs. return nil } cs.resTrailer = &res.Trailer cs.res = res close(cs.respHeaderRecv) if f.StreamEnded() { rl.endStream(cs) } return nil } // may return error types nil, or ConnectionError. Any other error value // is a StreamError of type ErrCodeProtocol. The returned error in that case // is the detail. // // As a special case, handleResponse may return (nil, nil) to skip the // frame (currently only used for 1xx responses). func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFrame) (*http.Response, error) { if f.Truncated { return nil, errResponseHeaderListSize } status := f.PseudoValue("status") if status == "" { return nil, errors.New("malformed response from server: missing status pseudo header") } statusCode, err := strconv.Atoi(status) if err != nil { return nil, errors.New("malformed response from server: malformed non-numeric status pseudo header") } regularFields := f.RegularFields() strs := make([]string, len(regularFields)) header := make(http.Header, len(regularFields)) res := &http.Response{ Proto: "HTTP/2.0", ProtoMajor: 2, Header: header, StatusCode: statusCode, Status: status + " " + http.StatusText(statusCode), } for _, hf := range regularFields { key := httpcommon.CanonicalHeader(hf.Name) if key == "Trailer" { t := res.Trailer if t == nil { t = make(http.Header) res.Trailer = t } foreachHeaderElement(hf.Value, func(v string) { t[httpcommon.CanonicalHeader(v)] = nil }) } else { vv := header[key] if vv == nil && len(strs) > 0 { // More than likely this will be a single-element key. // Most headers aren't multi-valued. // Set the capacity on strs[0] to 1, so any future append // won't extend the slice into the other strings. vv, strs = strs[:1:1], strs[1:] vv[0] = hf.Value header[key] = vv } else { header[key] = append(vv, hf.Value) } } } if statusCode >= 100 && statusCode <= 199 { if f.StreamEnded() { return nil, errors.New("1xx informational response with END_STREAM flag") } if fn := cs.get1xxTraceFunc(); fn != nil { // If the 1xx response is being delivered to the user, // then they're responsible for limiting the number // of responses. if err := fn(statusCode, textproto.MIMEHeader(header)); err != nil { return nil, err } } else { // If the user didn't examine the 1xx response, then we // limit the size of all 1xx headers. // // This differs a bit from the HTTP/1 implementation, which // limits the size of all 1xx headers plus the final response. // Use the larger limit of MaxHeaderListSize and // net/http.Transport.MaxResponseHeaderBytes. limit := int64(cs.cc.t.maxHeaderListSize()) if t1 := cs.cc.t.t1; t1 != nil && t1.MaxResponseHeaderBytes > limit { limit = t1.MaxResponseHeaderBytes } for _, h := range f.Fields { cs.totalHeaderSize += int64(h.Size()) } if cs.totalHeaderSize > limit { if VerboseLogs { log.Printf("http2: 1xx informational responses too large") } return nil, errors.New("header list too large") } } if statusCode == 100 { traceGot100Continue(cs.trace) select { case cs.on100 <- struct{}{}: default: } } cs.pastHeaders = false // do it all again return nil, nil } res.ContentLength = -1 if clens := res.Header["Content-Length"]; len(clens) == 1 { if cl, err := strconv.ParseUint(clens[0], 10, 63); err == nil { res.ContentLength = int64(cl) } else { // TODO: care? unlike http/1, it won't mess up our framing, so it's // more safe smuggling-wise to ignore. } } else if len(clens) > 1 { // TODO: care? unlike http/1, it won't mess up our framing, so it's // more safe smuggling-wise to ignore. } else if f.StreamEnded() && !cs.isHead { res.ContentLength = 0 } if cs.isHead { res.Body = noBody return res, nil } if f.StreamEnded() { if res.ContentLength > 0 { res.Body = missingBody{} } else { res.Body = noBody } return res, nil } cs.bufPipe.setBuffer(&dataBuffer{expected: res.ContentLength}) cs.bytesRemain = res.ContentLength res.Body = transportResponseBody{cs} if cs.requestedGzip && asciiEqualFold(res.Header.Get("Content-Encoding"), "gzip") { res.Header.Del("Content-Encoding") res.Header.Del("Content-Length") res.ContentLength = -1 res.Body = &gzipReader{body: res.Body} res.Uncompressed = true } return res, nil } func (rl *clientConnReadLoop) processTrailers(cs *clientStream, f *MetaHeadersFrame) error { if cs.pastTrailers { // Too many HEADERS frames for this stream. return ConnectionError(ErrCodeProtocol) } cs.pastTrailers = true if !f.StreamEnded() { // We expect that any headers for trailers also // has END_STREAM. return ConnectionError(ErrCodeProtocol) } if len(f.PseudoFields()) > 0 { // No pseudo header fields are defined for trailers. // TODO: ConnectionError might be overly harsh? Check. return ConnectionError(ErrCodeProtocol) } trailer := make(http.Header) for _, hf := range f.RegularFields() { key := httpcommon.CanonicalHeader(hf.Name) trailer[key] = append(trailer[key], hf.Value) } cs.trailer = trailer rl.endStream(cs) return nil } // transportResponseBody is the concrete type of Transport.RoundTrip's // Response.Body. It is an io.ReadCloser. type transportResponseBody struct { cs *clientStream } func (b transportResponseBody) Read(p []byte) (n int, err error) { cs := b.cs cc := cs.cc if cs.readErr != nil { return 0, cs.readErr } n, err = b.cs.bufPipe.Read(p) if cs.bytesRemain != -1 { if int64(n) > cs.bytesRemain { n = int(cs.bytesRemain) if err == nil { err = errors.New("net/http: server replied with more than declared Content-Length; truncated") cs.abortStream(err) } cs.readErr = err return int(cs.bytesRemain), err } cs.bytesRemain -= int64(n) if err == io.EOF && cs.bytesRemain > 0 { err = io.ErrUnexpectedEOF cs.readErr = err return n, err } } if n == 0 { // No flow control tokens to send back. return } cc.mu.Lock() connAdd := cc.inflow.add(n) var streamAdd int32 if err == nil { // No need to refresh if the stream is over or failed. streamAdd = cs.inflow.add(n) } cc.mu.Unlock() if connAdd != 0 || streamAdd != 0 { cc.wmu.Lock() defer cc.wmu.Unlock() if connAdd != 0 { cc.fr.WriteWindowUpdate(0, mustUint31(connAdd)) } if streamAdd != 0 { cc.fr.WriteWindowUpdate(cs.ID, mustUint31(streamAdd)) } cc.bw.Flush() } return } var errClosedResponseBody = errors.New("http2: response body closed") func (b transportResponseBody) Close() error { cs := b.cs cc := cs.cc cs.bufPipe.BreakWithError(errClosedResponseBody) cs.abortStream(errClosedResponseBody) unread := cs.bufPipe.Len() if unread > 0 { cc.mu.Lock() // Return connection-level flow control. connAdd := cc.inflow.add(unread) cc.mu.Unlock() // TODO(dneil): Acquiring this mutex can block indefinitely. // Move flow control return to a goroutine? cc.wmu.Lock() // Return connection-level flow control. if connAdd > 0 { cc.fr.WriteWindowUpdate(0, uint32(connAdd)) } cc.bw.Flush() cc.wmu.Unlock() } select { case <-cs.donec: case <-cs.ctx.Done(): // See golang/go#49366: The net/http package can cancel the // request context after the response body is fully read. // Don't treat this as an error. return nil case <-cs.reqCancel: return errRequestCanceled } return nil } func (rl *clientConnReadLoop) processData(f *DataFrame) error { cc := rl.cc cs := rl.streamByID(f.StreamID, headerOrDataFrame) data := f.Data() if cs == nil { cc.mu.Lock() neverSent := cc.nextStreamID cc.mu.Unlock() if f.StreamID >= neverSent { // We never asked for this. cc.logf("http2: Transport received unsolicited DATA frame; closing connection") return ConnectionError(ErrCodeProtocol) } // We probably did ask for this, but canceled. Just ignore it. // TODO: be stricter here? only silently ignore things which // we canceled, but not things which were closed normally // by the peer? Tough without accumulating too much state. // But at least return their flow control: if f.Length > 0 { cc.mu.Lock() ok := cc.inflow.take(f.Length) connAdd := cc.inflow.add(int(f.Length)) cc.mu.Unlock() if !ok { return ConnectionError(ErrCodeFlowControl) } if connAdd > 0 { cc.wmu.Lock() cc.fr.WriteWindowUpdate(0, uint32(connAdd)) cc.bw.Flush() cc.wmu.Unlock() } } return nil } if cs.readClosed { cc.logf("protocol error: received DATA after END_STREAM") rl.endStreamError(cs, StreamError{ StreamID: f.StreamID, Code: ErrCodeProtocol, }) return nil } if !cs.pastHeaders { cc.logf("protocol error: received DATA before a HEADERS frame") rl.endStreamError(cs, StreamError{ StreamID: f.StreamID, Code: ErrCodeProtocol, }) return nil } if f.Length > 0 { if cs.isHead && len(data) > 0 { cc.logf("protocol error: received DATA on a HEAD request") rl.endStreamError(cs, StreamError{ StreamID: f.StreamID, Code: ErrCodeProtocol, }) return nil } // Check connection-level flow control. cc.mu.Lock() if !takeInflows(&cc.inflow, &cs.inflow, f.Length) { cc.mu.Unlock() return ConnectionError(ErrCodeFlowControl) } // Return any padded flow control now, since we won't // refund it later on body reads. var refund int if pad := int(f.Length) - len(data); pad > 0 { refund += pad } didReset := false var err error if len(data) > 0 { if _, err = cs.bufPipe.Write(data); err != nil { // Return len(data) now if the stream is already closed, // since data will never be read. didReset = true refund += len(data) } } sendConn := cc.inflow.add(refund) var sendStream int32 if !didReset { sendStream = cs.inflow.add(refund) } cc.mu.Unlock() if sendConn > 0 || sendStream > 0 { cc.wmu.Lock() if sendConn > 0 { cc.fr.WriteWindowUpdate(0, uint32(sendConn)) } if sendStream > 0 { cc.fr.WriteWindowUpdate(cs.ID, uint32(sendStream)) } cc.bw.Flush() cc.wmu.Unlock() } if err != nil { rl.endStreamError(cs, err) return nil } } if f.StreamEnded() { rl.endStream(cs) } return nil } func (rl *clientConnReadLoop) endStream(cs *clientStream) { // TODO: check that any declared content-length matches, like // server.go's (*stream).endStream method. if !cs.readClosed { cs.readClosed = true // Close cs.bufPipe and cs.peerClosed with cc.mu held to avoid a // race condition: The caller can read io.EOF from Response.Body // and close the body before we close cs.peerClosed, causing // cleanupWriteRequest to send a RST_STREAM. rl.cc.mu.Lock() defer rl.cc.mu.Unlock() cs.bufPipe.closeWithErrorAndCode(io.EOF, cs.copyTrailers) close(cs.peerClosed) } } func (rl *clientConnReadLoop) endStreamError(cs *clientStream, err error) { cs.readAborted = true cs.abortStream(err) } // Constants passed to streamByID for documentation purposes. const ( headerOrDataFrame = true notHeaderOrDataFrame = false ) // streamByID returns the stream with the given id, or nil if no stream has that id. // If headerOrData is true, it clears rst.StreamPingsBlocked. func (rl *clientConnReadLoop) streamByID(id uint32, headerOrData bool) *clientStream { rl.cc.mu.Lock() defer rl.cc.mu.Unlock() if headerOrData { // Work around an unfortunate gRPC behavior. // See comment on ClientConn.rstStreamPingsBlocked for details. rl.cc.rstStreamPingsBlocked = false } cs := rl.cc.streams[id] if cs != nil && !cs.readAborted { return cs } return nil } func (cs *clientStream) copyTrailers() { for k, vv := range cs.trailer { t := cs.resTrailer if *t == nil { *t = make(http.Header) } (*t)[k] = vv } } func (rl *clientConnReadLoop) processGoAway(f *GoAwayFrame) error { cc := rl.cc cc.t.connPool().MarkDead(cc) if f.ErrCode != 0 { // TODO: deal with GOAWAY more. particularly the error code cc.vlogf("transport got GOAWAY with error code = %v", f.ErrCode) if fn := cc.t.CountError; fn != nil { fn("recv_goaway_" + f.ErrCode.stringToken()) } } cc.setGoAway(f) return nil } func (rl *clientConnReadLoop) processSettings(f *SettingsFrame) error { cc := rl.cc // Locking both mu and wmu here allows frame encoding to read settings with only wmu held. // Acquiring wmu when f.IsAck() is unnecessary, but convenient and mostly harmless. cc.wmu.Lock() defer cc.wmu.Unlock() if err := rl.processSettingsNoWrite(f); err != nil { return err } if !f.IsAck() { cc.fr.WriteSettingsAck() cc.bw.Flush() } return nil } func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error { cc := rl.cc cc.mu.Lock() defer cc.mu.Unlock() if f.IsAck() { if cc.wantSettingsAck { cc.wantSettingsAck = false return nil } return ConnectionError(ErrCodeProtocol) } var seenMaxConcurrentStreams bool err := f.ForeachSetting(func(s Setting) error { switch s.ID { case SettingMaxFrameSize: cc.maxFrameSize = s.Val case SettingMaxConcurrentStreams: cc.maxConcurrentStreams = s.Val seenMaxConcurrentStreams = true case SettingMaxHeaderListSize: cc.peerMaxHeaderListSize = uint64(s.Val) case SettingInitialWindowSize: // Values above the maximum flow-control // window size of 2^31-1 MUST be treated as a // connection error (Section 5.4.1) of type // FLOW_CONTROL_ERROR. if s.Val > math.MaxInt32 { return ConnectionError(ErrCodeFlowControl) } // Adjust flow control of currently-open // frames by the difference of the old initial // window size and this one. delta := int32(s.Val) - int32(cc.initialWindowSize) for _, cs := range cc.streams { cs.flow.add(delta) } cc.cond.Broadcast() cc.initialWindowSize = s.Val case SettingHeaderTableSize: cc.henc.SetMaxDynamicTableSize(s.Val) cc.peerMaxHeaderTableSize = s.Val case SettingEnableConnectProtocol: if err := s.Valid(); err != nil { return err } // If the peer wants to send us SETTINGS_ENABLE_CONNECT_PROTOCOL, // we require that it do so in the first SETTINGS frame. // // When we attempt to use extended CONNECT, we wait for the first // SETTINGS frame to see if the server supports it. If we let the // server enable the feature with a later SETTINGS frame, then // users will see inconsistent results depending on whether we've // seen that frame or not. if !cc.seenSettings { cc.extendedConnectAllowed = s.Val == 1 } default: cc.vlogf("Unhandled Setting: %v", s) } return nil }) if err != nil { return err } if !cc.seenSettings { if !seenMaxConcurrentStreams { // This was the servers initial SETTINGS frame and it // didn't contain a MAX_CONCURRENT_STREAMS field so // increase the number of concurrent streams this // connection can establish to our default. cc.maxConcurrentStreams = defaultMaxConcurrentStreams } close(cc.seenSettingsChan) cc.seenSettings = true } return nil } func (rl *clientConnReadLoop) processWindowUpdate(f *WindowUpdateFrame) error { cc := rl.cc cs := rl.streamByID(f.StreamID, notHeaderOrDataFrame) if f.StreamID != 0 && cs == nil { return nil } cc.mu.Lock() defer cc.mu.Unlock() fl := &cc.flow if cs != nil { fl = &cs.flow } if !fl.add(int32(f.Increment)) { // For stream, the sender sends RST_STREAM with an error code of FLOW_CONTROL_ERROR if cs != nil { rl.endStreamError(cs, StreamError{ StreamID: f.StreamID, Code: ErrCodeFlowControl, }) return nil } return ConnectionError(ErrCodeFlowControl) } cc.cond.Broadcast() return nil } func (rl *clientConnReadLoop) processResetStream(f *RSTStreamFrame) error { cs := rl.streamByID(f.StreamID, notHeaderOrDataFrame) if cs == nil { // TODO: return error if server tries to RST_STREAM an idle stream return nil } serr := streamError(cs.ID, f.ErrCode) serr.Cause = errFromPeer if f.ErrCode == ErrCodeProtocol { rl.cc.SetDoNotReuse() } if fn := cs.cc.t.CountError; fn != nil { fn("recv_rststream_" + f.ErrCode.stringToken()) } cs.abortStream(serr) cs.bufPipe.CloseWithError(serr) return nil } // Ping sends a PING frame to the server and waits for the ack. func (cc *ClientConn) Ping(ctx context.Context) error { c := make(chan struct{}) // Generate a random payload var p [8]byte for { if _, err := rand.Read(p[:]); err != nil { return err } cc.mu.Lock() // check for dup before insert if _, found := cc.pings[p]; !found { cc.pings[p] = c cc.mu.Unlock() break } cc.mu.Unlock() } var pingError error errc := make(chan struct{}) go func() { cc.wmu.Lock() defer cc.wmu.Unlock() if pingError = cc.fr.WritePing(false, p); pingError != nil { close(errc) return } if pingError = cc.bw.Flush(); pingError != nil { close(errc) return } }() select { case <-c: return nil case <-errc: return pingError case <-ctx.Done(): return ctx.Err() case <-cc.readerDone: // connection closed return cc.readerErr } } func (rl *clientConnReadLoop) processPing(f *PingFrame) error { if f.IsAck() { cc := rl.cc cc.mu.Lock() defer cc.mu.Unlock() // If ack, notify listener if any if c, ok := cc.pings[f.Data]; ok { close(c) delete(cc.pings, f.Data) } if cc.pendingResets > 0 { // See clientStream.cleanupWriteRequest. cc.pendingResets = 0 cc.rstStreamPingsBlocked = true cc.cond.Broadcast() } return nil } cc := rl.cc cc.wmu.Lock() defer cc.wmu.Unlock() if err := cc.fr.WritePing(true, f.Data); err != nil { return err } return cc.bw.Flush() } func (rl *clientConnReadLoop) processPushPromise(f *PushPromiseFrame) error { // We told the peer we don't want them. // Spec says: // "PUSH_PROMISE MUST NOT be sent if the SETTINGS_ENABLE_PUSH // setting of the peer endpoint is set to 0. An endpoint that // has set this setting and has received acknowledgement MUST // treat the receipt of a PUSH_PROMISE frame as a connection // error (Section 5.4.1) of type PROTOCOL_ERROR." return ConnectionError(ErrCodeProtocol) } // writeStreamReset sends a RST_STREAM frame. // When ping is true, it also sends a PING frame with a random payload. func (cc *ClientConn) writeStreamReset(streamID uint32, code ErrCode, ping bool, err error) { // TODO: map err to more interesting error codes, once the // HTTP community comes up with some. But currently for // RST_STREAM there's no equivalent to GOAWAY frame's debug // data, and the error codes are all pretty vague ("cancel"). cc.wmu.Lock() cc.fr.WriteRSTStream(streamID, code) if ping { var payload [8]byte rand.Read(payload[:]) cc.fr.WritePing(false, payload) } cc.bw.Flush() cc.wmu.Unlock() } var ( errResponseHeaderListSize = errors.New("http2: response header list larger than advertised limit") errRequestHeaderListSize = httpcommon.ErrRequestHeaderListSize ) func (cc *ClientConn) logf(format string, args ...interface{}) { cc.t.logf(format, args...) } func (cc *ClientConn) vlogf(format string, args ...interface{}) { cc.t.vlogf(format, args...) } func (t *Transport) vlogf(format string, args ...interface{}) { if VerboseLogs { t.logf(format, args...) } } func (t *Transport) logf(format string, args ...interface{}) { log.Printf(format, args...) } var noBody io.ReadCloser = noBodyReader{} type noBodyReader struct{} func (noBodyReader) Close() error { return nil } func (noBodyReader) Read([]byte) (int, error) { return 0, io.EOF } type missingBody struct{} func (missingBody) Close() error { return nil } func (missingBody) Read([]byte) (int, error) { return 0, io.ErrUnexpectedEOF } func strSliceContains(ss []string, s string) bool { for _, v := range ss { if v == s { return true } } return false } type erringRoundTripper struct{ err error } func (rt erringRoundTripper) RoundTripErr() error { return rt.err } func (rt erringRoundTripper) RoundTrip(*http.Request) (*http.Response, error) { return nil, rt.err } var errConcurrentReadOnResBody = errors.New("http2: concurrent read on response body") // gzipReader wraps a response body so it can lazily // get gzip.Reader from the pool on the first call to Read. // After Close is called it puts gzip.Reader to the pool immediately // if there is no Read in progress or later when Read completes. type gzipReader struct { _ incomparable body io.ReadCloser // underlying Response.Body mu sync.Mutex // guards zr and zerr zr *gzip.Reader // stores gzip reader from the pool between reads zerr error // sticky gzip reader init error or sentinel value to detect concurrent read and read after close } type eofReader struct{} func (eofReader) Read([]byte) (int, error) { return 0, io.EOF } func (eofReader) ReadByte() (byte, error) { return 0, io.EOF } var gzipPool = sync.Pool{New: func() any { return new(gzip.Reader) }} // gzipPoolGet gets a gzip.Reader from the pool and resets it to read from r. func gzipPoolGet(r io.Reader) (*gzip.Reader, error) { zr := gzipPool.Get().(*gzip.Reader) if err := zr.Reset(r); err != nil { gzipPoolPut(zr) return nil, err } return zr, nil } // gzipPoolPut puts a gzip.Reader back into the pool. func gzipPoolPut(zr *gzip.Reader) { // Reset will allocate bufio.Reader if we pass it anything // other than a flate.Reader, so ensure that it's getting one. var r flate.Reader = eofReader{} zr.Reset(r) gzipPool.Put(zr) } // acquire returns a gzip.Reader for reading response body. // The reader must be released after use. func (gz *gzipReader) acquire() (*gzip.Reader, error) { gz.mu.Lock() defer gz.mu.Unlock() if gz.zerr != nil { return nil, gz.zerr } if gz.zr == nil { gz.zr, gz.zerr = gzipPoolGet(gz.body) if gz.zerr != nil { return nil, gz.zerr } } ret := gz.zr gz.zr, gz.zerr = nil, errConcurrentReadOnResBody return ret, nil } // release returns the gzip.Reader to the pool if Close was called during Read. func (gz *gzipReader) release(zr *gzip.Reader) { gz.mu.Lock() defer gz.mu.Unlock() if gz.zerr == errConcurrentReadOnResBody { gz.zr, gz.zerr = zr, nil } else { // fs.ErrClosed gzipPoolPut(zr) } } // close returns the gzip.Reader to the pool immediately or // signals release to do so after Read completes. func (gz *gzipReader) close() { gz.mu.Lock() defer gz.mu.Unlock() if gz.zerr == nil && gz.zr != nil { gzipPoolPut(gz.zr) gz.zr = nil } gz.zerr = fs.ErrClosed } func (gz *gzipReader) Read(p []byte) (n int, err error) { zr, err := gz.acquire() if err != nil { return 0, err } defer gz.release(zr) return zr.Read(p) } func (gz *gzipReader) Close() error { gz.close() return gz.body.Close() } type errorReader struct{ err error } func (r errorReader) Read(p []byte) (int, error) { return 0, r.err } // isConnectionCloseRequest reports whether req should use its own // connection for a single request and then close the connection. func isConnectionCloseRequest(req *http.Request) bool { return req.Close || httpguts.HeaderValuesContainsToken(req.Header["Connection"], "close") } // registerHTTPSProtocol calls Transport.RegisterProtocol but // converting panics into errors. func registerHTTPSProtocol(t *http.Transport, rt noDialH2RoundTripper) (err error) { defer func() { if e := recover(); e != nil { err = fmt.Errorf("%v", e) } }() t.RegisterProtocol("https", rt) return nil } // noDialH2RoundTripper is a RoundTripper which only tries to complete the request // if there's already has a cached connection to the host. // (The field is exported so it can be accessed via reflect from net/http; tested // by TestNoDialH2RoundTripperType) type noDialH2RoundTripper struct{ *Transport } func (rt noDialH2RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { res, err := rt.Transport.RoundTrip(req) if isNoCachedConnError(err) { return nil, http.ErrSkipAltProtocol } return res, err } func (t *Transport) idleConnTimeout() time.Duration { // to keep things backwards compatible, we use non-zero values of // IdleConnTimeout, followed by using the IdleConnTimeout on the underlying // http1 transport, followed by 0 if t.IdleConnTimeout != 0 { return t.IdleConnTimeout } if t.t1 != nil { return t.t1.IdleConnTimeout } return 0 } func traceGetConn(req *http.Request, hostPort string) { trace := httptrace.ContextClientTrace(req.Context()) if trace == nil || trace.GetConn == nil { return } trace.GetConn(hostPort) } func traceGotConn(req *http.Request, cc *ClientConn, reused bool) { trace := httptrace.ContextClientTrace(req.Context()) if trace == nil || trace.GotConn == nil { return } ci := httptrace.GotConnInfo{Conn: cc.tconn} ci.Reused = reused cc.mu.Lock() ci.WasIdle = len(cc.streams) == 0 && reused if ci.WasIdle && !cc.lastActive.IsZero() { ci.IdleTime = time.Since(cc.lastActive) } cc.mu.Unlock() trace.GotConn(ci) } func traceWroteHeaders(trace *httptrace.ClientTrace) { if trace != nil && trace.WroteHeaders != nil { trace.WroteHeaders() } } func traceGot100Continue(trace *httptrace.ClientTrace) { if trace != nil && trace.Got100Continue != nil { trace.Got100Continue() } } func traceWait100Continue(trace *httptrace.ClientTrace) { if trace != nil && trace.Wait100Continue != nil { trace.Wait100Continue() } } func traceWroteRequest(trace *httptrace.ClientTrace, err error) { if trace != nil && trace.WroteRequest != nil { trace.WroteRequest(httptrace.WroteRequestInfo{Err: err}) } } func traceFirstResponseByte(trace *httptrace.ClientTrace) { if trace != nil && trace.GotFirstResponseByte != nil { trace.GotFirstResponseByte() } } func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error { if trace != nil { return trace.Got1xxResponse } return nil } // dialTLSWithContext uses tls.Dialer, added in Go 1.15, to open a TLS // connection. func (t *Transport) dialTLSWithContext(ctx context.Context, network, addr string, cfg *tls.Config) (*tls.Conn, error) { dialer := &tls.Dialer{ Config: cfg, } cn, err := dialer.DialContext(ctx, network, addr) if err != nil { return nil, err } tlsCn := cn.(*tls.Conn) // DialContext comment promises this will always succeed return tlsCn, nil } ================================================ FILE: vendor/golang.org/x/net/http2/unencrypted.go ================================================ // Copyright 2024 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package http2 import ( "crypto/tls" "errors" "net" ) const nextProtoUnencryptedHTTP2 = "unencrypted_http2" // unencryptedNetConnFromTLSConn retrieves a net.Conn wrapped in a *tls.Conn. // // TLSNextProto functions accept a *tls.Conn. // // When passing an unencrypted HTTP/2 connection to a TLSNextProto function, // we pass a *tls.Conn with an underlying net.Conn containing the unencrypted connection. // To be extra careful about mistakes (accidentally dropping TLS encryption in a place // where we want it), the tls.Conn contains a net.Conn with an UnencryptedNetConn method // that returns the actual connection we want to use. func unencryptedNetConnFromTLSConn(tc *tls.Conn) (net.Conn, error) { conner, ok := tc.NetConn().(interface { UnencryptedNetConn() net.Conn }) if !ok { return nil, errors.New("http2: TLS conn unexpectedly found in unencrypted handoff") } return conner.UnencryptedNetConn(), nil } ================================================ FILE: vendor/golang.org/x/net/http2/write.go ================================================ [File too large to display: 11.0 KB] ================================================ FILE: vendor/golang.org/x/net/http2/writesched.go ================================================ [File too large to display: 9.1 KB] ================================================ FILE: vendor/golang.org/x/net/http2/writesched_priority_rfc7540.go ================================================ [File too large to display: 13.7 KB] ================================================ FILE: vendor/golang.org/x/net/http2/writesched_priority_rfc9218.go ================================================ [File too large to display: 5.7 KB] ================================================ FILE: vendor/golang.org/x/net/http2/writesched_random.go ================================================ // Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package http2 import "math" // NewRandomWriteScheduler constructs a WriteScheduler that ignores HTTP/2 // priorities. Control frames like SETTINGS and PING are written before DATA // frames, but if no control frames are queued and multiple streams have queued // HEADERS or DATA frames, Pop selects a ready stream arbitrarily. func NewRandomWriteScheduler() WriteScheduler { return &randomWriteScheduler{sq: make(map[uint32]*writeQueue)} } type randomWriteScheduler struct { // zero are frames not associated with a specific stream. zero writeQueue // sq contains the stream-specific queues, keyed by stream ID. // When a stream is idle, closed, or emptied, it's deleted // from the map. sq map[uint32]*writeQueue // pool of empty queues for reuse. queuePool writeQueuePool } func (ws *randomWriteScheduler) OpenStream(streamID uint32, options OpenStreamOptions) { // no-op: idle streams are not tracked } func (ws *randomWriteScheduler) CloseStream(streamID uint32) { q, ok := ws.sq[streamID] if !ok { return } delete(ws.sq, streamID) ws.queuePool.put(q) } func (ws *randomWriteScheduler) AdjustStream(streamID uint32, priority PriorityParam) { // no-op: priorities are ignored } func (ws *randomWriteScheduler) Push(wr FrameWriteRequest) { if wr.isControl() { ws.zero.push(wr) return } id := wr.StreamID() q, ok := ws.sq[id] if !ok { q = ws.queuePool.get() ws.sq[id] = q } q.push(wr) } func (ws *randomWriteScheduler) Pop() (FrameWriteRequest, bool) { // Control and RST_STREAM frames first. if !ws.zero.empty() { return ws.zero.shift(), true } // Iterate over all non-idle streams until finding one that can be consumed. for streamID, q := range ws.sq { if wr, ok := q.consume(math.MaxInt32); ok { if q.empty() { delete(ws.sq, streamID) ws.queuePool.put(q) } return wr, true } } return FrameWriteRequest{}, false } ================================================ FILE: vendor/golang.org/x/net/http2/writesched_roundrobin.go ================================================ // Copyright 2023 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package http2 import ( "fmt" "math" ) type roundRobinWriteScheduler struct { // control contains control frames (SETTINGS, PING, etc.). control writeQueue // streams maps stream ID to a queue. streams map[uint32]*writeQueue // stream queues are stored in a circular linked list. // head is the next stream to write, or nil if there are no streams open. head *writeQueue // pool of empty queues for reuse. queuePool writeQueuePool } // newRoundRobinWriteScheduler constructs a new write scheduler. // The round robin scheduler prioritizes control frames // like SETTINGS and PING over DATA frames. // When there are no control frames to send, it performs a round-robin // selection from the ready streams. func newRoundRobinWriteScheduler() WriteScheduler { ws := &roundRobinWriteScheduler{ streams: make(map[uint32]*writeQueue), } return ws } func (ws *roundRobinWriteScheduler) OpenStream(streamID uint32, options OpenStreamOptions) { if ws.streams[streamID] != nil { panic(fmt.Errorf("stream %d already opened", streamID)) } q := ws.queuePool.get() ws.streams[streamID] = q if ws.head == nil { ws.head = q q.next = q q.prev = q } else { // Queues are stored in a ring. // Insert the new stream before ws.head, putting it at the end of the list. q.prev = ws.head.prev q.next = ws.head q.prev.next = q q.next.prev = q } } func (ws *roundRobinWriteScheduler) CloseStream(streamID uint32) { q := ws.streams[streamID] if q == nil { return } if q.next == q { // This was the only open stream. ws.head = nil } else { q.prev.next = q.next q.next.prev = q.prev if ws.head == q { ws.head = q.next } } delete(ws.streams, streamID) ws.queuePool.put(q) } func (ws *roundRobinWriteScheduler) AdjustStream(streamID uint32, priority PriorityParam) {} func (ws *roundRobinWriteScheduler) Push(wr FrameWriteRequest) { if wr.isControl() { ws.control.push(wr) return } q := ws.streams[wr.StreamID()] if q == nil { // This is a closed stream. // wr should not be a HEADERS or DATA frame. // We push the request onto the control queue. if wr.DataSize() > 0 { panic("add DATA on non-open stream") } ws.control.push(wr) return } q.push(wr) } func (ws *roundRobinWriteScheduler) Pop() (FrameWriteRequest, bool) { // Control and RST_STREAM frames first. if !ws.control.empty() { return ws.control.shift(), true } if ws.head == nil { return FrameWriteRequest{}, false } q := ws.head for { if wr, ok := q.consume(math.MaxInt32); ok { ws.head = q.next return wr, true } q = q.next if q == ws.head { break } } return FrameWriteRequest{}, false } ================================================ FILE: vendor/golang.org/x/net/idna/go118.go ================================================ [File too large to display: 401 B] ================================================ FILE: vendor/golang.org/x/net/idna/idna10.0.0.go ================================================ [File too large to display: 20.9 KB] ================================================ FILE: vendor/golang.org/x/net/idna/idna9.0.0.go ================================================ [File too large to display: 19.2 KB] ================================================ FILE: vendor/golang.org/x/net/idna/pre_go118.go ================================================ [File too large to display: 305 B] ================================================ FILE: vendor/golang.org/x/net/idna/punycode.go ================================================ [File too large to display: 4.6 KB] ================================================ FILE: vendor/golang.org/x/net/idna/tables10.0.0.go ================================================ [File too large to display: 267.2 KB] ================================================ FILE: vendor/golang.org/x/net/idna/tables11.0.0.go ================================================ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.13 && !go1.14 package idna // UnicodeVersion is the Unicode version from which the tables in this package are derived. const UnicodeVersion = "11.0.0" var mappings string = "" + // Size: 8175 bytes "\x00\x01 \x03 ̈\x01a\x03 ̄\x012\x013\x03 ́\x03 ̧\x011\x01o\x051⁄4\x051⁄2" + "\x053⁄4\x03i̇\x03l·\x03ʼn\x01s\x03dž\x03ⱥ\x03ⱦ\x01h\x01j\x01r\x01w\x01y" + "\x03 ̆\x03 ̇\x03 ̊\x03 ̨\x03 ̃\x03 ̋\x01l\x01x\x04̈́\x03 ι\x01;\x05 ̈́" + "\x04եւ\x04اٴ\x04وٴ\x04ۇٴ\x04يٴ\x06क़\x06ख़\x06ग़\x06ज़\x06ड़\x06ढ़\x06फ़" + "\x06य़\x06ড়\x06ঢ়\x06য়\x06ਲ਼\x06ਸ਼\x06ਖ਼\x06ਗ਼\x06ਜ਼\x06ਫ਼\x06ଡ଼\x06ଢ଼" + "\x06ํา\x06ໍາ\x06ຫນ\x06ຫມ\x06གྷ\x06ཌྷ\x06དྷ\x06བྷ\x06ཛྷ\x06ཀྵ\x06ཱི\x06ཱུ" + "\x06ྲྀ\x09ྲཱྀ\x06ླྀ\x09ླཱྀ\x06ཱྀ\x06ྒྷ\x06ྜྷ\x06ྡྷ\x06ྦྷ\x06ྫྷ\x06ྐྵ\x02" + "в\x02д\x02о\x02с\x02т\x02ъ\x02ѣ\x02æ\x01b\x01d\x01e\x02ǝ\x01g\x01i\x01k" + "\x01m\x01n\x02ȣ\x01p\x01t\x01u\x02ɐ\x02ɑ\x02ə\x02ɛ\x02ɜ\x02ŋ\x02ɔ\x02ɯ" + "\x01v\x02β\x02γ\x02δ\x02φ\x02χ\x02ρ\x02н\x02ɒ\x01c\x02ɕ\x02ð\x01f\x02ɟ" + "\x02ɡ\x02ɥ\x02ɨ\x02ɩ\x02ɪ\x02ʝ\x02ɭ\x02ʟ\x02ɱ\x02ɰ\x02ɲ\x02ɳ\x02ɴ\x02ɵ" + "\x02ɸ\x02ʂ\x02ʃ\x02ƫ\x02ʉ\x02ʊ\x02ʋ\x02ʌ\x01z\x02ʐ\x02ʑ\x02ʒ\x02θ\x02ss" + "\x02ά\x02έ\x02ή\x02ί\x02ό\x02ύ\x02ώ\x05ἀι\x05ἁι\x05ἂι\x05ἃι\x05ἄι\x05ἅι" + "\x05ἆι\x05ἇι\x05ἠι\x05ἡι\x05ἢι\x05ἣι\x05ἤι\x05ἥι\x05ἦι\x05ἧι\x05ὠι\x05ὡι" + "\x05ὢι\x05ὣι\x05ὤι\x05ὥι\x05ὦι\x05ὧι\x05ὰι\x04αι\x04άι\x05ᾶι\x02ι\x05 ̈͂" + "\x05ὴι\x04ηι\x04ήι\x05ῆι\x05 ̓̀\x05 ̓́\x05 ̓͂\x02ΐ\x05 ̔̀\x05 ̔́\x05 ̔͂" + "\x02ΰ\x05 ̈̀\x01`\x05ὼι\x04ωι\x04ώι\x05ῶι\x06′′\x09′′′\x06‵‵\x09‵‵‵\x02!" + "!\x02??\x02?!\x02!?\x0c′′′′\x010\x014\x015\x016\x017\x018\x019\x01+\x01=" + "\x01(\x01)\x02rs\x02ħ\x02no\x01q\x02sm\x02tm\x02ω\x02å\x02א\x02ב\x02ג" + "\x02ד\x02π\x051⁄7\x051⁄9\x061⁄10\x051⁄3\x052⁄3\x051⁄5\x052⁄5\x053⁄5\x054" + "⁄5\x051⁄6\x055⁄6\x051⁄8\x053⁄8\x055⁄8\x057⁄8\x041⁄\x02ii\x02iv\x02vi" + "\x04viii\x02ix\x02xi\x050⁄3\x06∫∫\x09∫∫∫\x06∮∮\x09∮∮∮\x0210\x0211\x0212" + "\x0213\x0214\x0215\x0216\x0217\x0218\x0219\x0220\x04(10)\x04(11)\x04(12)" + "\x04(13)\x04(14)\x04(15)\x04(16)\x04(17)\x04(18)\x04(19)\x04(20)\x0c∫∫∫∫" + "\x02==\x05⫝̸\x02ɫ\x02ɽ\x02ȿ\x02ɀ\x01.\x04 ゙\x04 ゚\x06より\x06コト\x05(ᄀ)\x05" + "(ᄂ)\x05(ᄃ)\x05(ᄅ)\x05(ᄆ)\x05(ᄇ)\x05(ᄉ)\x05(ᄋ)\x05(ᄌ)\x05(ᄎ)\x05(ᄏ)\x05(ᄐ" + ")\x05(ᄑ)\x05(ᄒ)\x05(가)\x05(나)\x05(다)\x05(라)\x05(마)\x05(바)\x05(사)\x05(아)" + "\x05(자)\x05(차)\x05(카)\x05(타)\x05(파)\x05(하)\x05(주)\x08(오전)\x08(오후)\x05(一)" + "\x05(二)\x05(三)\x05(四)\x05(五)\x05(六)\x05(七)\x05(八)\x05(九)\x05(十)\x05(月)" + "\x05(火)\x05(水)\x05(木)\x05(金)\x05(土)\x05(日)\x05(株)\x05(有)\x05(社)\x05(名)" + "\x05(特)\x05(財)\x05(祝)\x05(労)\x05(代)\x05(呼)\x05(学)\x05(監)\x05(企)\x05(資)" + "\x05(協)\x05(祭)\x05(休)\x05(自)\x05(至)\x0221\x0222\x0223\x0224\x0225\x0226" + "\x0227\x0228\x0229\x0230\x0231\x0232\x0233\x0234\x0235\x06참고\x06주의\x0236" + "\x0237\x0238\x0239\x0240\x0241\x0242\x0243\x0244\x0245\x0246\x0247\x0248" + "\x0249\x0250\x041月\x042月\x043月\x044月\x045月\x046月\x047月\x048月\x049月\x0510" + "月\x0511月\x0512月\x02hg\x02ev\x0cアパート\x0cアルファ\x0cアンペア\x09アール\x0cイニング\x09" + "インチ\x09ウォン\x0fエスクード\x0cエーカー\x09オンス\x09オーム\x09カイリ\x0cカラット\x0cカロリー\x09ガロ" + "ン\x09ガンマ\x06ギガ\x09ギニー\x0cキュリー\x0cギルダー\x06キロ\x0fキログラム\x12キロメートル\x0fキロワッ" + "ト\x09グラム\x0fグラムトン\x0fクルゼイロ\x0cクローネ\x09ケース\x09コルナ\x09コーポ\x0cサイクル\x0fサンチ" + "ーム\x0cシリング\x09センチ\x09セント\x09ダース\x06デシ\x06ドル\x06トン\x06ナノ\x09ノット\x09ハイツ" + "\x0fパーセント\x09パーツ\x0cバーレル\x0fピアストル\x09ピクル\x06ピコ\x06ビル\x0fファラッド\x0cフィート" + "\x0fブッシェル\x09フラン\x0fヘクタール\x06ペソ\x09ペニヒ\x09ヘルツ\x09ペンス\x09ページ\x09ベータ\x0cポイ" + "ント\x09ボルト\x06ホン\x09ポンド\x09ホール\x09ホーン\x0cマイクロ\x09マイル\x09マッハ\x09マルク\x0fマ" + "ンション\x0cミクロン\x06ミリ\x0fミリバール\x06メガ\x0cメガトン\x0cメートル\x09ヤード\x09ヤール\x09ユアン" + "\x0cリットル\x06リラ\x09ルピー\x0cルーブル\x06レム\x0fレントゲン\x09ワット\x040点\x041点\x042点" + "\x043点\x044点\x045点\x046点\x047点\x048点\x049点\x0510点\x0511点\x0512点\x0513点" + "\x0514点\x0515点\x0516点\x0517点\x0518点\x0519点\x0520点\x0521点\x0522点\x0523点" + "\x0524点\x02da\x02au\x02ov\x02pc\x02dm\x02iu\x06平成\x06昭和\x06大正\x06明治\x0c株" + "式会社\x02pa\x02na\x02ma\x02ka\x02kb\x02mb\x02gb\x04kcal\x02pf\x02nf\x02m" + "g\x02kg\x02hz\x02ml\x02dl\x02kl\x02fm\x02nm\x02mm\x02cm\x02km\x02m2\x02m" + "3\x05m∕s\x06m∕s2\x07rad∕s\x08rad∕s2\x02ps\x02ns\x02ms\x02pv\x02nv\x02mv" + "\x02kv\x02pw\x02nw\x02mw\x02kw\x02bq\x02cc\x02cd\x06c∕kg\x02db\x02gy\x02" + "ha\x02hp\x02in\x02kk\x02kt\x02lm\x02ln\x02lx\x02ph\x02pr\x02sr\x02sv\x02" + "wb\x05v∕m\x05a∕m\x041日\x042日\x043日\x044日\x045日\x046日\x047日\x048日\x049日" + "\x0510日\x0511日\x0512日\x0513日\x0514日\x0515日\x0516日\x0517日\x0518日\x0519日" + "\x0520日\x0521日\x0522日\x0523日\x0524日\x0525日\x0526日\x0527日\x0528日\x0529日" + "\x0530日\x0531日\x02ь\x02ɦ\x02ɬ\x02ʞ\x02ʇ\x02œ\x04𤋮\x04𢡊\x04𢡄\x04𣏕\x04𥉉" + "\x04𥳐\x04𧻓\x02ff\x02fi\x02fl\x02st\x04մն\x04մե\x04մի\x04վն\x04մխ\x04יִ" + "\x04ײַ\x02ע\x02ה\x02כ\x02ל\x02ם\x02ר\x02ת\x04שׁ\x04שׂ\x06שּׁ\x06שּׂ\x04א" + "ַ\x04אָ\x04אּ\x04בּ\x04גּ\x04דּ\x04הּ\x04וּ\x04זּ\x04טּ\x04יּ\x04ךּ\x04" + "כּ\x04לּ\x04מּ\x04נּ\x04סּ\x04ףּ\x04פּ\x04צּ\x04קּ\x04רּ\x04שּ\x04תּ" + "\x04וֹ\x04בֿ\x04כֿ\x04פֿ\x04אל\x02ٱ\x02ٻ\x02پ\x02ڀ\x02ٺ\x02ٿ\x02ٹ\x02ڤ" + "\x02ڦ\x02ڄ\x02ڃ\x02چ\x02ڇ\x02ڍ\x02ڌ\x02ڎ\x02ڈ\x02ژ\x02ڑ\x02ک\x02گ\x02ڳ" + "\x02ڱ\x02ں\x02ڻ\x02ۀ\x02ہ\x02ھ\x02ے\x02ۓ\x02ڭ\x02ۇ\x02ۆ\x02ۈ\x02ۋ\x02ۅ" + "\x02ۉ\x02ې\x02ى\x04ئا\x04ئە\x04ئو\x04ئۇ\x04ئۆ\x04ئۈ\x04ئې\x04ئى\x02ی\x04" + "ئج\x04ئح\x04ئم\x04ئي\x04بج\x04بح\x04بخ\x04بم\x04بى\x04بي\x04تج\x04تح" + "\x04تخ\x04تم\x04تى\x04تي\x04ثج\x04ثم\x04ثى\x04ثي\x04جح\x04جم\x04حج\x04حم" + "\x04خج\x04خح\x04خم\x04سج\x04سح\x04سخ\x04سم\x04صح\x04صم\x04ضج\x04ضح\x04ضخ" + "\x04ضم\x04طح\x04طم\x04ظم\x04عج\x04عم\x04غج\x04غم\x04فج\x04فح\x04فخ\x04فم" + "\x04فى\x04في\x04قح\x04قم\x04قى\x04قي\x04كا\x04كج\x04كح\x04كخ\x04كل\x04كم" + "\x04كى\x04كي\x04لج\x04لح\x04لخ\x04لم\x04لى\x04لي\x04مج\x04مح\x04مخ\x04مم" + "\x04مى\x04مي\x04نج\x04نح\x04نخ\x04نم\x04نى\x04ني\x04هج\x04هم\x04هى\x04هي" + "\x04يج\x04يح\x04يخ\x04يم\x04يى\x04يي\x04ذٰ\x04رٰ\x04ىٰ\x05 ٌّ\x05 ٍّ\x05" + " َّ\x05 ُّ\x05 ِّ\x05 ّٰ\x04ئر\x04ئز\x04ئن\x04بر\x04بز\x04بن\x04تر\x04تز" + "\x04تن\x04ثر\x04ثز\x04ثن\x04ما\x04نر\x04نز\x04نن\x04ير\x04يز\x04ين\x04ئخ" + "\x04ئه\x04به\x04ته\x04صخ\x04له\x04نه\x04هٰ\x04يه\x04ثه\x04سه\x04شم\x04شه" + "\x06ـَّ\x06ـُّ\x06ـِّ\x04طى\x04طي\x04عى\x04عي\x04غى\x04غي\x04سى\x04سي" + "\x04شى\x04شي\x04حى\x04حي\x04جى\x04جي\x04خى\x04خي\x04صى\x04صي\x04ضى\x04ضي" + "\x04شج\x04شح\x04شخ\x04شر\x04سر\x04صر\x04ضر\x04اً\x06تجم\x06تحج\x06تحم" + "\x06تخم\x06تمج\x06تمح\x06تمخ\x06جمح\x06حمي\x06حمى\x06سحج\x06سجح\x06سجى" + "\x06سمح\x06سمج\x06سمم\x06صحح\x06صمم\x06شحم\x06شجي\x06شمخ\x06شمم\x06ضحى" + "\x06ضخم\x06طمح\x06طمم\x06طمي\x06عجم\x06عمم\x06عمى\x06غمم\x06غمي\x06غمى" + "\x06فخم\x06قمح\x06قمم\x06لحم\x06لحي\x06لحى\x06لجج\x06لخم\x06لمح\x06محج" + "\x06محم\x06محي\x06مجح\x06مجم\x06مخج\x06مخم\x06مجخ\x06همج\x06همم\x06نحم" + "\x06نحى\x06نجم\x06نجى\x06نمي\x06نمى\x06يمم\x06بخي\x06تجي\x06تجى\x06تخي" + "\x06تخى\x06تمي\x06تمى\x06جمي\x06جحى\x06جمى\x06سخى\x06صحي\x06شحي\x06ضحي" + "\x06لجي\x06لمي\x06يحي\x06يجي\x06يمي\x06ممي\x06قمي\x06نحي\x06عمي\x06كمي" + "\x06نجح\x06مخي\x06لجم\x06كمم\x06جحي\x06حجي\x06مجي\x06فمي\x06بحي\x06سخي" + "\x06نجي\x06صلے\x06قلے\x08الله\x08اكبر\x08محمد\x08صلعم\x08رسول\x08عليه" + "\x08وسلم\x06صلى!صلى الله عليه وسلم\x0fجل جلاله\x08ریال\x01,\x01:\x01!" + "\x01?\x01_\x01{\x01}\x01[\x01]\x01#\x01&\x01*\x01-\x01<\x01>\x01\\\x01$" + "\x01%\x01@\x04ـً\x04ـَ\x04ـُ\x04ـِ\x04ـّ\x04ـْ\x02ء\x02آ\x02أ\x02ؤ\x02إ" + "\x02ئ\x02ا\x02ب\x02ة\x02ت\x02ث\x02ج\x02ح\x02خ\x02د\x02ذ\x02ر\x02ز\x02س" + "\x02ش\x02ص\x02ض\x02ط\x02ظ\x02ع\x02غ\x02ف\x02ق\x02ك\x02ل\x02م\x02ن\x02ه" + "\x02و\x02ي\x04لآ\x04لأ\x04لإ\x04لا\x01\x22\x01'\x01/\x01^\x01|\x01~\x02¢" + "\x02£\x02¬\x02¦\x02¥\x08𝅗𝅥\x08𝅘𝅥\x0c𝅘𝅥𝅮\x0c𝅘𝅥𝅯\x0c𝅘𝅥𝅰\x0c𝅘𝅥𝅱\x0c𝅘𝅥𝅲\x08𝆹" + "𝅥\x08𝆺𝅥\x0c𝆹𝅥𝅮\x0c𝆺𝅥𝅮\x0c𝆹𝅥𝅯\x0c𝆺𝅥𝅯\x02ı\x02ȷ\x02α\x02ε\x02ζ\x02η\x02" + "κ\x02λ\x02μ\x02ν\x02ξ\x02ο\x02σ\x02τ\x02υ\x02ψ\x03∇\x03∂\x02ϝ\x02ٮ\x02ڡ" + "\x02ٯ\x020,\x021,\x022,\x023,\x024,\x025,\x026,\x027,\x028,\x029,\x03(a)" + "\x03(b)\x03(c)\x03(d)\x03(e)\x03(f)\x03(g)\x03(h)\x03(i)\x03(j)\x03(k)" + "\x03(l)\x03(m)\x03(n)\x03(o)\x03(p)\x03(q)\x03(r)\x03(s)\x03(t)\x03(u)" + "\x03(v)\x03(w)\x03(x)\x03(y)\x03(z)\x07〔s〕\x02wz\x02hv\x02sd\x03ppv\x02w" + "c\x02mc\x02md\x02dj\x06ほか\x06ココ\x03サ\x03手\x03字\x03双\x03デ\x03二\x03多\x03解" + "\x03天\x03交\x03映\x03無\x03料\x03前\x03後\x03再\x03新\x03初\x03終\x03生\x03販\x03声" + "\x03吹\x03演\x03投\x03捕\x03一\x03三\x03遊\x03左\x03中\x03右\x03指\x03走\x03打\x03禁" + "\x03空\x03合\x03満\x03有\x03月\x03申\x03割\x03営\x03配\x09〔本〕\x09〔三〕\x09〔二〕\x09〔安" + "〕\x09〔点〕\x09〔打〕\x09〔盗〕\x09〔勝〕\x09〔敗〕\x03得\x03可\x03丽\x03丸\x03乁\x03你\x03" + "侮\x03侻\x03倂\x03偺\x03備\x03僧\x03像\x03㒞\x03免\x03兔\x03兤\x03具\x03㒹\x03內\x03" + "冗\x03冤\x03仌\x03冬\x03况\x03凵\x03刃\x03㓟\x03刻\x03剆\x03剷\x03㔕\x03勇\x03勉\x03" + "勤\x03勺\x03包\x03匆\x03北\x03卉\x03卑\x03博\x03即\x03卽\x03卿\x03灰\x03及\x03叟\x03" + "叫\x03叱\x03吆\x03咞\x03吸\x03呈\x03周\x03咢\x03哶\x03唐\x03啓\x03啣\x03善\x03喙\x03" + "喫\x03喳\x03嗂\x03圖\x03嘆\x03圗\x03噑\x03噴\x03切\x03壮\x03城\x03埴\x03堍\x03型\x03" + "堲\x03報\x03墬\x03売\x03壷\x03夆\x03夢\x03奢\x03姬\x03娛\x03娧\x03姘\x03婦\x03㛮\x03" + "嬈\x03嬾\x03寃\x03寘\x03寧\x03寳\x03寿\x03将\x03尢\x03㞁\x03屠\x03屮\x03峀\x03岍\x03" + "嵃\x03嵮\x03嵫\x03嵼\x03巡\x03巢\x03㠯\x03巽\x03帨\x03帽\x03幩\x03㡢\x03㡼\x03庰\x03" + "庳\x03庶\x03廊\x03廾\x03舁\x03弢\x03㣇\x03形\x03彫\x03㣣\x03徚\x03忍\x03志\x03忹\x03" + "悁\x03㤺\x03㤜\x03悔\x03惇\x03慈\x03慌\x03慎\x03慺\x03憎\x03憲\x03憤\x03憯\x03懞\x03" + "懲\x03懶\x03成\x03戛\x03扝\x03抱\x03拔\x03捐\x03挽\x03拼\x03捨\x03掃\x03揤\x03搢\x03" + "揅\x03掩\x03㨮\x03摩\x03摾\x03撝\x03摷\x03㩬\x03敏\x03敬\x03旣\x03書\x03晉\x03㬙\x03" + "暑\x03㬈\x03㫤\x03冒\x03冕\x03最\x03暜\x03肭\x03䏙\x03朗\x03望\x03朡\x03杞\x03杓\x03" + "㭉\x03柺\x03枅\x03桒\x03梅\x03梎\x03栟\x03椔\x03㮝\x03楂\x03榣\x03槪\x03檨\x03櫛\x03" + "㰘\x03次\x03歔\x03㱎\x03歲\x03殟\x03殺\x03殻\x03汎\x03沿\x03泍\x03汧\x03洖\x03派\x03" + "海\x03流\x03浩\x03浸\x03涅\x03洴\x03港\x03湮\x03㴳\x03滋\x03滇\x03淹\x03潮\x03濆\x03" + "瀹\x03瀞\x03瀛\x03㶖\x03灊\x03災\x03灷\x03炭\x03煅\x03熜\x03爨\x03爵\x03牐\x03犀\x03" + "犕\x03獺\x03王\x03㺬\x03玥\x03㺸\x03瑇\x03瑜\x03瑱\x03璅\x03瓊\x03㼛\x03甤\x03甾\x03" + "異\x03瘐\x03㿼\x03䀈\x03直\x03眞\x03真\x03睊\x03䀹\x03瞋\x03䁆\x03䂖\x03硎\x03碌\x03" + "磌\x03䃣\x03祖\x03福\x03秫\x03䄯\x03穀\x03穊\x03穏\x03䈂\x03篆\x03築\x03䈧\x03糒\x03" + "䊠\x03糨\x03糣\x03紀\x03絣\x03䌁\x03緇\x03縂\x03繅\x03䌴\x03䍙\x03罺\x03羕\x03翺\x03" + "者\x03聠\x03聰\x03䏕\x03育\x03脃\x03䐋\x03脾\x03媵\x03舄\x03辞\x03䑫\x03芑\x03芋\x03" + "芝\x03劳\x03花\x03芳\x03芽\x03苦\x03若\x03茝\x03荣\x03莭\x03茣\x03莽\x03菧\x03著\x03" + "荓\x03菊\x03菌\x03菜\x03䔫\x03蓱\x03蓳\x03蔖\x03蕤\x03䕝\x03䕡\x03䕫\x03虐\x03虜\x03" + "虧\x03虩\x03蚩\x03蚈\x03蜎\x03蛢\x03蝹\x03蜨\x03蝫\x03螆\x03蟡\x03蠁\x03䗹\x03衠\x03" + "衣\x03裗\x03裞\x03䘵\x03裺\x03㒻\x03䚾\x03䛇\x03誠\x03諭\x03變\x03豕\x03貫\x03賁\x03" + "贛\x03起\x03跋\x03趼\x03跰\x03軔\x03輸\x03邔\x03郱\x03鄑\x03鄛\x03鈸\x03鋗\x03鋘\x03" + "鉼\x03鏹\x03鐕\x03開\x03䦕\x03閷\x03䧦\x03雃\x03嶲\x03霣\x03䩮\x03䩶\x03韠\x03䪲\x03" + "頋\x03頩\x03飢\x03䬳\x03餩\x03馧\x03駂\x03駾\x03䯎\x03鬒\x03鱀\x03鳽\x03䳎\x03䳭\x03" + "鵧\x03䳸\x03麻\x03䵖\x03黹\x03黾\x03鼅\x03鼏\x03鼖\x03鼻" var xorData string = "" + // Size: 4855 bytes "\x02\x0c\x09\x02\xb0\xec\x02\xad\xd8\x02\xad\xd9\x02\x06\x07\x02\x0f\x12" + "\x02\x0f\x1f\x02\x0f\x1d\x02\x01\x13\x02\x0f\x16\x02\x0f\x0b\x02\x0f3" + "\x02\x0f7\x02\x0f?\x02\x0f/\x02\x0f*\x02\x0c&\x02\x0c*\x02\x0c;\x02\x0c9" + "\x02\x0c%\x02\xab\xed\x02\xab\xe2\x02\xab\xe3\x02\xa9\xe0\x02\xa9\xe1" + "\x02\xa9\xe6\x02\xa3\xcb\x02\xa3\xc8\x02\xa3\xc9\x02\x01#\x02\x01\x08" + "\x02\x0e>\x02\x0e'\x02\x0f\x03\x02\x03\x0d\x02\x03\x09\x02\x03\x17\x02" + "\x03\x0e\x02\x02\x03\x02\x011\x02\x01\x00\x02\x01\x10\x02\x03<\x02\x07" + "\x0d\x02\x02\x0c\x02\x0c0\x02\x01\x03\x02\x01\x01\x02\x01 \x02\x01\x22" + "\x02\x01)\x02\x01\x0a\x02\x01\x0c\x02\x02\x06\x02\x02\x02\x02\x03\x10" + "\x03\x037 \x03\x0b+\x03\x02\x01\x04\x02\x01\x02\x02\x019\x02\x03\x1c\x02" + "\x02$\x03\x80p$\x02\x03:\x02\x03\x0a\x03\xc1r.\x03\xc1r,\x03\xc1r\x02" + "\x02\x02:\x02\x02>\x02\x02,\x02\x02\x10\x02\x02\x00\x03\xc1s<\x03\xc1s*" + "\x03\xc2L$\x03\xc2L;\x02\x09)\x02\x0a\x19\x03\x83\xab\xe3\x03\x83\xab" + "\xf2\x03 4\xe0\x03\x81\xab\xea\x03\x81\xab\xf3\x03 4\xef\x03\x96\xe1\xcd" + "\x03\x84\xe5\xc3\x02\x0d\x11\x03\x8b\xec\xcb\x03\x94\xec\xcf\x03\x9a\xec" + "\xc2\x03\x8b\xec\xdb\x03\x94\xec\xdf\x03\x9a\xec\xd2\x03\x01\x0c!\x03" + "\x01\x0c#\x03ʠ\x9d\x03ʣ\x9c\x03ʢ\x9f\x03ʥ\x9e\x03ʤ\x91\x03ʧ\x90\x03ʦ\x93" + "\x03ʩ\x92\x03ʨ\x95\x03\xca\xf3\xb5\x03\xca\xf0\xb4\x03\xca\xf1\xb7\x03" + "\xca\xf6\xb6\x03\xca\xf7\x89\x03\xca\xf4\x88\x03\xca\xf5\x8b\x03\xca\xfa" + "\x8a\x03\xca\xfb\x8d\x03\xca\xf8\x8c\x03\xca\xf9\x8f\x03\xca\xfe\x8e\x03" + "\xca\xff\x81\x03\xca\xfc\x80\x03\xca\xfd\x83\x03\xca\xe2\x82\x03\xca\xe3" + "\x85\x03\xca\xe0\x84\x03\xca\xe1\x87\x03\xca\xe6\x86\x03\xca\xe7\x99\x03" + "\xca\xe4\x98\x03\xca\xe5\x9b\x03\xca\xea\x9a\x03\xca\xeb\x9d\x03\xca\xe8" + "\x9c\x03ؓ\x89\x03ߔ\x8b\x02\x010\x03\x03\x04\x1e\x03\x04\x15\x12\x03\x0b" + "\x05,\x03\x06\x04\x00\x03\x06\x04)\x03\x06\x044\x03\x06\x04<\x03\x06\x05" + "\x1d\x03\x06\x06\x00\x03\x06\x06\x0a\x03\x06\x06'\x03\x06\x062\x03\x0786" + "\x03\x079/\x03\x079 \x03\x07:\x0e\x03\x07:\x1b\x03\x07:%\x03\x07;/\x03" + "\x07;%\x03\x074\x11\x03\x076\x09\x03\x077*\x03\x070\x01\x03\x070\x0f\x03" + "\x070.\x03\x071\x16\x03\x071\x04\x03\x0710\x03\x072\x18\x03\x072-\x03" + "\x073\x14\x03\x073>\x03\x07'\x09\x03\x07 \x00\x03\x07\x1f\x0b\x03\x07" + "\x18#\x03\x07\x18(\x03\x07\x186\x03\x07\x18\x03\x03\x07\x19\x16\x03\x07" + "\x116\x03\x07\x12'\x03\x07\x13\x10\x03\x07\x0c&\x03\x07\x0c\x08\x03\x07" + "\x0c\x13\x03\x07\x0d\x02\x03\x07\x0d\x1c\x03\x07\x0b5\x03\x07\x0b\x0a" + "\x03\x07\x0b\x01\x03\x07\x0b\x0f\x03\x07\x05\x00\x03\x07\x05\x09\x03\x07" + "\x05\x0b\x03\x07\x07\x01\x03\x07\x07\x08\x03\x07\x00<\x03\x07\x00+\x03" + "\x07\x01)\x03\x07\x01\x1b\x03\x07\x01\x08\x03\x07\x03?\x03\x0445\x03\x04" + "4\x08\x03\x0454\x03\x04)/\x03\x04)5\x03\x04+\x05\x03\x04+\x14\x03\x04+ " + "\x03\x04+<\x03\x04*&\x03\x04*\x22\x03\x04&8\x03\x04!\x01\x03\x04!\x22" + "\x03\x04\x11+\x03\x04\x10.\x03\x04\x104\x03\x04\x13=\x03\x04\x12\x04\x03" + "\x04\x12\x0a\x03\x04\x0d\x1d\x03\x04\x0d\x07\x03\x04\x0d \x03\x05<>\x03" + "\x055<\x03\x055!\x03\x055#\x03\x055&\x03\x054\x1d\x03\x054\x02\x03\x054" + "\x07\x03\x0571\x03\x053\x1a\x03\x053\x16\x03\x05.<\x03\x05.\x07\x03\x05)" + ":\x03\x05)<\x03\x05)\x0c\x03\x05)\x15\x03\x05+-\x03\x05+5\x03\x05$\x1e" + "\x03\x05$\x14\x03\x05'\x04\x03\x05'\x14\x03\x05&\x02\x03\x05\x226\x03" + "\x05\x22\x0c\x03\x05\x22\x1c\x03\x05\x19\x0a\x03\x05\x1b\x09\x03\x05\x1b" + "\x0c\x03\x05\x14\x07\x03\x05\x16?\x03\x05\x16\x0c\x03\x05\x0c\x05\x03" + "\x05\x0e\x0f\x03\x05\x01\x0e\x03\x05\x00(\x03\x05\x030\x03\x05\x03\x06" + "\x03\x0a==\x03\x0a=1\x03\x0a=,\x03\x0a=\x0c\x03\x0a??\x03\x0a<\x08\x03" + "\x0a9!\x03\x0a9)\x03\x0a97\x03\x0a99\x03\x0a6\x0a\x03\x0a6\x1c\x03\x0a6" + "\x17\x03\x0a7'\x03\x0a78\x03\x0a73\x03\x0a'\x01\x03\x0a'&\x03\x0a\x1f" + "\x0e\x03\x0a\x1f\x03\x03\x0a\x1f3\x03\x0a\x1b/\x03\x0a\x18\x19\x03\x0a" + "\x19\x01\x03\x0a\x16\x14\x03\x0a\x0e\x22\x03\x0a\x0f\x10\x03\x0a\x0f\x02" + "\x03\x0a\x0f \x03\x0a\x0c\x04\x03\x0a\x0b>\x03\x0a\x0b+\x03\x0a\x08/\x03" + "\x0a\x046\x03\x0a\x05\x14\x03\x0a\x00\x04\x03\x0a\x00\x10\x03\x0a\x00" + "\x14\x03\x0b<3\x03\x0b;*\x03\x0b9\x22\x03\x0b9)\x03\x0b97\x03\x0b+\x10" + "\x03\x0b((\x03\x0b&5\x03\x0b$\x1c\x03\x0b$\x12\x03\x0b%\x04\x03\x0b#<" + "\x03\x0b#0\x03\x0b#\x0d\x03\x0b#\x19\x03\x0b!:\x03\x0b!\x1f\x03\x0b!\x00" + "\x03\x0b\x1e5\x03\x0b\x1c\x1d\x03\x0b\x1d-\x03\x0b\x1d(\x03\x0b\x18.\x03" + "\x0b\x18 \x03\x0b\x18\x16\x03\x0b\x14\x13\x03\x0b\x15$\x03\x0b\x15\x22" + "\x03\x0b\x12\x1b\x03\x0b\x12\x10\x03\x0b\x132\x03\x0b\x13=\x03\x0b\x12" + "\x18\x03\x0b\x0c&\x03\x0b\x061\x03\x0b\x06:\x03\x0b\x05#\x03\x0b\x05<" + "\x03\x0b\x04\x0b\x03\x0b\x04\x04\x03\x0b\x04\x1b\x03\x0b\x042\x03\x0b" + "\x041\x03\x0b\x03\x03\x03\x0b\x03\x1d\x03\x0b\x03/\x03\x0b\x03+\x03\x0b" + "\x02\x1b\x03\x0b\x02\x00\x03\x0b\x01\x1e\x03\x0b\x01\x08\x03\x0b\x015" + "\x03\x06\x0d9\x03\x06\x0d=\x03\x06\x0d?\x03\x02\x001\x03\x02\x003\x03" + "\x02\x02\x19\x03\x02\x006\x03\x02\x02\x1b\x03\x02\x004\x03\x02\x00<\x03" + "\x02\x02\x0a\x03\x02\x02\x0e\x03\x02\x01\x1a\x03\x02\x01\x07\x03\x02\x01" + "\x05\x03\x02\x01\x0b\x03\x02\x01%\x03\x02\x01\x0c\x03\x02\x01\x04\x03" + "\x02\x01\x1c\x03\x02\x00.\x03\x02\x002\x03\x02\x00>\x03\x02\x00\x12\x03" + "\x02\x00\x16\x03\x02\x011\x03\x02\x013\x03\x02\x02 \x03\x02\x02%\x03\x02" + "\x02$\x03\x02\x028\x03\x02\x02;\x03\x02\x024\x03\x02\x012\x03\x02\x022" + "\x03\x02\x02/\x03\x02\x01,\x03\x02\x01\x13\x03\x02\x01\x16\x03\x02\x01" + "\x11\x03\x02\x01\x1e\x03\x02\x01\x15\x03\x02\x01\x17\x03\x02\x01\x0f\x03" + "\x02\x01\x08\x03\x02\x00?\x03\x02\x03\x07\x03\x02\x03\x0d\x03\x02\x03" + "\x13\x03\x02\x03\x1d\x03\x02\x03\x1f\x03\x02\x00\x03\x03\x02\x00\x0d\x03" + "\x02\x00\x01\x03\x02\x00\x1b\x03\x02\x00\x19\x03\x02\x00\x18\x03\x02\x00" + "\x13\x03\x02\x00/\x03\x07>\x12\x03\x07<\x1f\x03\x07>\x1d\x03\x06\x1d\x0e" + "\x03\x07>\x1c\x03\x07>:\x03\x07>\x13\x03\x04\x12+\x03\x07?\x03\x03\x07>" + "\x02\x03\x06\x224\x03\x06\x1a.\x03\x07<%\x03\x06\x1c\x0b\x03\x0609\x03" + "\x05\x1f\x01\x03\x04'\x08\x03\x93\xfd\xf5\x03\x02\x0d \x03\x02\x0d#\x03" + "\x02\x0d!\x03\x02\x0d&\x03\x02\x0d\x22\x03\x02\x0d/\x03\x02\x0d,\x03\x02" + "\x0d$\x03\x02\x0d'\x03\x02\x0d%\x03\x02\x0d;\x03\x02\x0d=\x03\x02\x0d?" + "\x03\x099.\x03\x08\x0b7\x03\x08\x02\x14\x03\x08\x14\x0d\x03\x08.:\x03" + "\x089'\x03\x0f\x0b\x18\x03\x0f\x1c1\x03\x0f\x17&\x03\x0f9\x1f\x03\x0f0" + "\x0c\x03\x0e\x0a9\x03\x0e\x056\x03\x0e\x1c#\x03\x0f\x13\x0e\x03\x072\x00" + "\x03\x070\x0d\x03\x072\x0b\x03\x06\x11\x18\x03\x070\x10\x03\x06\x0f(\x03" + "\x072\x05\x03\x06\x0f,\x03\x073\x15\x03\x06\x07\x08\x03\x05\x16\x02\x03" + "\x04\x0b \x03\x05:8\x03\x05\x16%\x03\x0a\x0d\x1f\x03\x06\x16\x10\x03\x05" + "\x1d5\x03\x05*;\x03\x05\x16\x1b\x03\x04.-\x03\x06\x1a\x19\x03\x04\x03," + "\x03\x0b87\x03\x04/\x0a\x03\x06\x00,\x03\x04-\x01\x03\x04\x1e-\x03\x06/(" + "\x03\x0a\x0b5\x03\x06\x0e7\x03\x06\x07.\x03\x0597\x03\x0a*%\x03\x0760" + "\x03\x06\x0c;\x03\x05'\x00\x03\x072.\x03\x072\x08\x03\x06=\x01\x03\x06" + "\x05\x1b\x03\x06\x06\x12\x03\x06$=\x03\x06'\x0d\x03\x04\x11\x0f\x03\x076" + ",\x03\x06\x07;\x03\x06.,\x03\x86\xf9\xea\x03\x8f\xff\xeb\x02\x092\x02" + "\x095\x02\x094\x02\x09;\x02\x09>\x02\x098\x02\x09*\x02\x09/\x02\x09,\x02" + "\x09%\x02\x09&\x02\x09#\x02\x09 \x02\x08!\x02\x08%\x02\x08$\x02\x08+\x02" + "\x08.\x02\x08*\x02\x08&\x02\x088\x02\x08>\x02\x084\x02\x086\x02\x080\x02" + "\x08\x10\x02\x08\x17\x02\x08\x12\x02\x08\x1d\x02\x08\x1f\x02\x08\x13\x02" + "\x08\x15\x02\x08\x14\x02\x08\x0c\x03\x8b\xfd\xd0\x03\x81\xec\xc6\x03\x87" + "\xe0\x8a\x03-2\xe3\x03\x80\xef\xe4\x03-2\xea\x03\x88\xe6\xeb\x03\x8e\xe6" + "\xe8\x03\x84\xe6\xe9\x03\x97\xe6\xee\x03-2\xf9\x03-2\xf6\x03\x8e\xe3\xad" + "\x03\x80\xe3\x92\x03\x88\xe3\x90\x03\x8e\xe3\x90\x03\x80\xe3\x97\x03\x88" + "\xe3\x95\x03\x88\xfe\xcb\x03\x8e\xfe\xca\x03\x84\xfe\xcd\x03\x91\xef\xc9" + "\x03-2\xc1\x03-2\xc0\x03-2\xcb\x03\x88@\x09\x03\x8e@\x08\x03\x8f\xe0\xf5" + "\x03\x8e\xe6\xf9\x03\x8e\xe0\xfa\x03\x93\xff\xf4\x03\x84\xee\xd3\x03\x0b" + "(\x04\x023 \x021;\x02\x01*\x03\x0b#\x10\x03\x0b 0\x03\x0b!\x10\x03\x0b!0" + "\x03\x07\x15\x08\x03\x09?5\x03\x07\x1f\x08\x03\x07\x17\x0b\x03\x09\x1f" + "\x15\x03\x0b\x1c7\x03\x0a+#\x03\x06\x1a\x1b\x03\x06\x1a\x14\x03\x0a\x01" + "\x18\x03\x06#\x1b\x03\x0a2\x0c\x03\x0a\x01\x04\x03\x09#;\x03\x08='\x03" + "\x08\x1a\x0a\x03\x07</\x03\x07:+\x03\x07\x07*\x03\x06&\x1c\x03\x09\x0c" + "\x16\x03\x09\x10\x0e\x03\x08'\x0f\x03\x08+\x09\x03\x074%\x03\x06!3\x03" + "\x06\x03+\x03\x0b\x1e\x19\x03\x0a))\x03\x09\x08\x19\x03\x08,\x05\x03\x07" + "<2\x03\x06\x1c>\x03\x0a\x111\x03\x09\x1b\x09\x03\x073.\x03\x07\x01\x00" + "\x03\x09/,\x03\x07#>\x03\x07\x048\x03\x0a\x1f\x22\x03\x098>\x03\x09\x11" + "\x00\x03\x08/\x17\x03\x06'\x22\x03\x0b\x1a+\x03\x0a\x22\x19\x03\x0a/1" + "\x03\x0974\x03\x09\x0f\x22\x03\x08,\x22\x03\x08?\x14\x03\x07$5\x03\x07<3" + "\x03\x07=*\x03\x07\x13\x18\x03\x068\x0a\x03\x06\x09\x16\x03\x06\x13\x00" + "\x03\x08\x067\x03\x08\x01\x03\x03\x08\x12\x1d\x03\x07+7\x03\x06(;\x03" + "\x06\x1c?\x03\x07\x0e\x17\x03\x0a\x06\x1d\x03\x0a\x19\x07\x03\x08\x14$" + "\x03\x07$;\x03\x08,$\x03\x08\x06\x0d\x03\x07\x16\x0a\x03\x06>>\x03\x0a" + "\x06\x12\x03\x0a\x14)\x03\x09\x0d\x1f\x03\x09\x12\x17\x03\x09\x19\x01" + "\x03\x08\x11 \x03\x08\x1d'\x03\x06<\x1a\x03\x0a.\x00\x03\x07'\x18\x03" + "\x0a\x22\x08\x03\x08\x0d\x0a\x03\x08\x13)\x03\x07*)\x03\x06<,\x03\x07" + "\x0b\x1a\x03\x09.\x14\x03\x09\x0d\x1e\x03\x07\x0e#\x03\x0b\x1d'\x03\x0a" + "\x0a8\x03\x09%2\x03\x08+&\x03\x080\x12\x03\x0a)4\x03\x08\x06\x1f\x03\x0b" + "\x1b\x1a\x03\x0a\x1b\x0f\x03\x0b\x1d*\x03\x09\x16$\x03\x090\x11\x03\x08" + "\x11\x08\x03\x0a*(\x03\x0a\x042\x03\x089,\x03\x074'\x03\x07\x0f\x05\x03" + "\x09\x0b\x0a\x03\x07\x1b\x01\x03\x09\x17:\x03\x09.\x0d\x03\x07.\x11\x03" + "\x09+\x15\x03\x080\x13\x03\x0b\x1f\x19\x03\x0a \x11\x03\x0a\x220\x03\x09" + "\x07;\x03\x08\x16\x1c\x03\x07,\x13\x03\x07\x0e/\x03\x06\x221\x03\x0a." + "\x0a\x03\x0a7\x02\x03\x0a\x032\x03\x0a\x1d.\x03\x091\x06\x03\x09\x19:" + "\x03\x08\x02/\x03\x060+\x03\x06\x0f-\x03\x06\x1c\x1f\x03\x06\x1d\x07\x03" + "\x0a,\x11\x03\x09=\x0d\x03\x09\x0b;\x03\x07\x1b/\x03\x0a\x1f:\x03\x09 " + "\x1f\x03\x09.\x10\x03\x094\x0b\x03\x09\x1a1\x03\x08#\x1a\x03\x084\x1d" + "\x03\x08\x01\x1f\x03\x08\x11\x22\x03\x07'8\x03\x07\x1a>\x03\x0757\x03" + "\x06&9\x03\x06+\x11\x03\x0a.\x0b\x03\x0a,>\x03\x0a4#\x03\x08%\x17\x03" + "\x07\x05\x22\x03\x07\x0c\x0b\x03\x0a\x1d+\x03\x0a\x19\x16\x03\x09+\x1f" + "\x03\x09\x08\x0b\x03\x08\x16\x18\x03\x08+\x12\x03\x0b\x1d\x0c\x03\x0a=" + "\x10\x03\x0a\x09\x0d\x03\x0a\x10\x11\x03\x09&0\x03\x08(\x1f\x03\x087\x07" + "\x03\x08\x185\x03\x07'6\x03\x06.\x05\x03\x06=\x04\x03\x06;;\x03\x06\x06," + "\x03\x0b\x18>\x03\x08\x00\x18\x03\x06 \x03\x03\x06<\x00\x03\x09%\x18\x03" + "\x0b\x1c<\x03\x0a%!\x03\x0a\x09\x12\x03\x0a\x16\x02\x03\x090'\x03\x09" + "\x0e=\x03\x08 \x0e\x03\x08>\x03\x03\x074>\x03\x06&?\x03\x06\x19\x09\x03" + "\x06?(\x03\x0a-\x0e\x03\x09:3\x03\x098:\x03\x09\x12\x0b\x03\x09\x1d\x17" + "\x03\x087\x05\x03\x082\x14\x03\x08\x06%\x03\x08\x13\x1f\x03\x06\x06\x0e" + "\x03\x0a\x22<\x03\x09/<\x03\x06>+\x03\x0a'?\x03\x0a\x13\x0c\x03\x09\x10<" + "\x03\x07\x1b=\x03\x0a\x19\x13\x03\x09\x22\x1d\x03\x09\x07\x0d\x03\x08)" + "\x1c\x03\x06=\x1a\x03\x0a/4\x03\x0a7\x11\x03\x0a\x16:\x03\x09?3\x03\x09:" + "/\x03\x09\x05\x0a\x03\x09\x14\x06\x03\x087\x22\x03\x080\x07\x03\x08\x1a" + "\x1f\x03\x07\x04(\x03\x07\x04\x09\x03\x06 %\x03\x06<\x08\x03\x0a+\x14" + "\x03\x09\x1d\x16\x03\x0a70\x03\x08 >\x03\x0857\x03\x070\x0a\x03\x06=\x12" + "\x03\x06\x16%\x03\x06\x1d,\x03\x099#\x03\x09\x10>\x03\x07 \x1e\x03\x08" + "\x0c<\x03\x08\x0b\x18\x03\x08\x15+\x03\x08,:\x03\x08%\x22\x03\x07\x0a$" + "\x03\x0b\x1c=\x03\x07+\x08\x03\x0a/\x05\x03\x0a \x07\x03\x0a\x12'\x03" + "\x09#\x11\x03\x08\x1b\x15\x03\x0a\x06\x01\x03\x09\x1c\x1b\x03\x0922\x03" + "\x07\x14<\x03\x07\x09\x04\x03\x061\x04\x03\x07\x0e\x01\x03\x0a\x13\x18" + "\x03\x0a-\x0c\x03\x0a?\x0d\x03\x0a\x09\x0a\x03\x091&\x03\x0a/\x0b\x03" + "\x08$<\x03\x083\x1d\x03\x08\x0c$\x03\x08\x0d\x07\x03\x08\x0d?\x03\x08" + "\x0e\x14\x03\x065\x0a\x03\x08\x1a#\x03\x08\x16#\x03\x0702\x03\x07\x03" + "\x1a\x03\x06(\x1d\x03\x06+\x1b\x03\x06\x0b\x05\x03\x06\x0b\x17\x03\x06" + "\x0c\x04\x03\x06\x1e\x19\x03\x06+0\x03\x062\x18\x03\x0b\x16\x1e\x03\x0a+" + "\x16\x03\x0a-?\x03\x0a#:\x03\x0a#\x10\x03\x0a%$\x03\x0a>+\x03\x0a01\x03" + "\x0a1\x10\x03\x0a\x099\x03\x0a\x0a\x12\x03\x0a\x19\x1f\x03\x0a\x19\x12" + "\x03\x09*)\x03\x09-\x16\x03\x09.1\x03\x09.2\x03\x09<\x0e\x03\x09> \x03" + "\x093\x12\x03\x09\x0b\x01\x03\x09\x1c2\x03\x09\x11\x1c\x03\x09\x15%\x03" + "\x08,&\x03\x08!\x22\x03\x089(\x03\x08\x0b\x1a\x03\x08\x0d2\x03\x08\x0c" + "\x04\x03\x08\x0c\x06\x03\x08\x0c\x1f\x03\x08\x0c\x0c\x03\x08\x0f\x1f\x03" + "\x08\x0f\x1d\x03\x08\x00\x14\x03\x08\x03\x14\x03\x08\x06\x16\x03\x08\x1e" + "#\x03\x08\x11\x11\x03\x08\x10\x18\x03\x08\x14(\x03\x07)\x1e\x03\x07.1" + "\x03\x07 $\x03\x07 '\x03\x078\x08\x03\x07\x0d0\x03\x07\x0f7\x03\x07\x05#" + "\x03\x07\x05\x1a\x03\x07\x1a7\x03\x07\x1d-\x03\x07\x17\x10\x03\x06)\x1f" + "\x03\x062\x0b\x03\x066\x16\x03\x06\x09\x11\x03\x09(\x1e\x03\x07!5\x03" + "\x0b\x11\x16\x03\x0a/\x04\x03\x0a,\x1a\x03\x0b\x173\x03\x0a,1\x03\x0a/5" + "\x03\x0a\x221\x03\x0a\x22\x0d\x03\x0a?%\x03\x0a<,\x03\x0a?#\x03\x0a>\x19" + "\x03\x0a\x08&\x03\x0a\x0b\x0e\x03\x0a\x0c:\x03\x0a\x0c+\x03\x0a\x03\x22" + "\x03\x0a\x06)\x03\x0a\x11\x10\x03\x0a\x11\x1a\x03\x0a\x17-\x03\x0a\x14(" + "\x03\x09)\x1e\x03\x09/\x09\x03\x09.\x00\x03\x09,\x07\x03\x09/*\x03\x09-9" + "\x03\x09\x228\x03\x09%\x09\x03\x09:\x12\x03\x09;\x1d\x03\x09?\x06\x03" + "\x093%\x03\x096\x05\x03\x096\x08\x03\x097\x02\x03\x09\x07,\x03\x09\x04," + "\x03\x09\x1f\x16\x03\x09\x11\x03\x03\x09\x11\x12\x03\x09\x168\x03\x08*" + "\x05\x03\x08/2\x03\x084:\x03\x08\x22+\x03\x08 0\x03\x08&\x0a\x03\x08;" + "\x10\x03\x08>$\x03\x08>\x18\x03\x0829\x03\x082:\x03\x081,\x03\x081<\x03" + "\x081\x1c\x03\x087#\x03\x087*\x03\x08\x09'\x03\x08\x00\x1d\x03\x08\x05-" + "\x03\x08\x1f4\x03\x08\x1d\x04\x03\x08\x16\x0f\x03\x07*7\x03\x07'!\x03" + "\x07%\x1b\x03\x077\x0c\x03\x07\x0c1\x03\x07\x0c.\x03\x07\x00\x06\x03\x07" + "\x01\x02\x03\x07\x010\x03\x07\x06=\x03\x07\x01\x03\x03\x07\x01\x13\x03" + "\x07\x06\x06\x03\x07\x05\x0a\x03\x07\x1f\x09\x03\x07\x17:\x03\x06*1\x03" + "\x06-\x1d\x03\x06\x223\x03\x062:\x03\x060$\x03\x066\x1e\x03\x064\x12\x03" + "\x0645\x03\x06\x0b\x00\x03\x06\x0b7\x03\x06\x07\x1f\x03\x06\x15\x12\x03" + "\x0c\x05\x0f\x03\x0b+\x0b\x03\x0b+-\x03\x06\x16\x1b\x03\x06\x15\x17\x03" + "\x89\xca\xea\x03\x89\xca\xe8\x03\x0c8\x10\x03\x0c8\x01\x03\x0c8\x0f\x03" + "\x0d8%\x03\x0d8!\x03\x0c8-\x03\x0c8/\x03\x0c8+\x03\x0c87\x03\x0c85\x03" + "\x0c9\x09\x03\x0c9\x0d\x03\x0c9\x0f\x03\x0c9\x0b\x03\xcfu\x0c\x03\xcfu" + "\x0f\x03\xcfu\x0e\x03\xcfu\x09\x03\x0c9\x10\x03\x0d9\x0c\x03\xcf`;\x03" + "\xcf`>\x03\xcf`9\x03\xcf`8\x03\xcf`7\x03\xcf`*\x03\xcf`-\x03\xcf`,\x03" + "\x0d\x1b\x1a\x03\x0d\x1b&\x03\x0c=.\x03\x0c=%\x03\x0c>\x1e\x03\x0c>\x14" + "\x03\x0c?\x06\x03\x0c?\x0b\x03\x0c?\x0c\x03\x0c?\x0d\x03\x0c?\x02\x03" + "\x0c>\x0f\x03\x0c>\x08\x03\x0c>\x09\x03\x0c>,\x03\x0c>\x0c\x03\x0c?\x13" + "\x03\x0c?\x16\x03\x0c?\x15\x03\x0c?\x1c\x03\x0c?\x1f\x03\x0c?\x1d\x03" + "\x0c?\x1a\x03\x0c?\x17\x03\x0c?\x08\x03\x0c?\x09\x03\x0c?\x0e\x03\x0c?" + "\x04\x03\x0c?\x05\x03\x0c<?\x03\x0c=\x00\x03\x0c=\x06\x03\x0c=\x05\x03" + "\x0c=\x0c\x03\x0c=\x0f\x03\x0c=\x0d\x03\x0c=\x0b\x03\x0c=\x07\x03\x0c=" + "\x19\x03\x0c=\x15\x03\x0c=\x11\x03\x0c=1\x03\x0c=3\x03\x0c=0\x03\x0c=>" + "\x03\x0c=2\x03\x0c=6\x03\x0c<\x07\x03\x0c<\x05\x03\x0e:!\x03\x0e:#\x03" + "\x0e8\x09\x03\x0e:&\x03\x0e8\x0b\x03\x0e:$\x03\x0e:,\x03\x0e8\x1a\x03" + "\x0e8\x1e\x03\x0e:*\x03\x0e:7\x03\x0e:5\x03\x0e:;\x03\x0e:\x15\x03\x0e:<" + "\x03\x0e:4\x03\x0e:'\x03\x0e:-\x03\x0e:%\x03\x0e:?\x03\x0e:=\x03\x0e:)" + "\x03\x0e:/\x03\xcfs'\x03\x0d=\x0f\x03\x0d+*\x03\x0d99\x03\x0d9;\x03\x0d9" + "?\x03\x0d)\x0d\x03\x0d(%\x02\x01\x18\x02\x01(\x02\x01\x1e\x03\x0f$!\x03" + "\x0f87\x03\x0f4\x0e\x03\x0f5\x1d\x03\x06'\x03\x03\x0f\x08\x18\x03\x0f" + "\x0d\x1b\x03\x0e2=\x03\x0e;\x08\x03\x0e:\x0b\x03\x0e\x06$\x03\x0e\x0d)" + "\x03\x0e\x16\x1f\x03\x0e\x16\x1b\x03\x0d$\x0a\x03\x05,\x1d\x03\x0d. \x03" + "\x0d.#\x03\x0c(/\x03\x09%\x02\x03\x0d90\x03\x0d\x0e4\x03\x0d\x0d\x0f\x03" + "\x0c#\x00\x03\x0c,\x1e\x03\x0c2\x0e\x03\x0c\x01\x17\x03\x0c\x09:\x03\x0e" + "\x173\x03\x0c\x08\x03\x03\x0c\x11\x07\x03\x0c\x10\x18\x03\x0c\x1f\x1c" + "\x03\x0c\x19\x0e\x03\x0c\x1a\x1f\x03\x0f0>\x03\x0b->\x03\x0b<+\x03\x0b8" + "\x13\x03\x0b\x043\x03\x0b\x14\x03\x03\x0b\x16%\x03\x0d\x22&\x03\x0b\x1a" + "\x1a\x03\x0b\x1a\x04\x03\x0a%9\x03\x0a&2\x03\x0a&0\x03\x0a!\x1a\x03\x0a!" + "7\x03\x0a5\x10\x03\x0a=4\x03\x0a?\x0e\x03\x0a>\x10\x03\x0a\x00 \x03\x0a" + "\x0f:\x03\x0a\x0f9\x03\x0a\x0b\x0a\x03\x0a\x17%\x03\x0a\x1b-\x03\x09-" + "\x1a\x03\x09,4\x03\x09.,\x03\x09)\x09\x03\x096!\x03\x091\x1f\x03\x093" + "\x16\x03\x0c+\x1f\x03\x098 \x03\x098=\x03\x0c(\x1a\x03\x0c(\x16\x03\x09" + "\x0a+\x03\x09\x16\x12\x03\x09\x13\x0e\x03\x09\x153\x03\x08)!\x03\x09\x1a" + "\x01\x03\x09\x18\x01\x03\x08%#\x03\x08>\x22\x03\x08\x05%\x03\x08\x02*" + "\x03\x08\x15;\x03\x08\x1b7\x03\x0f\x07\x1d\x03\x0f\x04\x03\x03\x070\x0c" + "\x03\x07;\x0b\x03\x07\x08\x17\x03\x07\x12\x06\x03\x06/-\x03\x0671\x03" + "\x065+\x03\x06>7\x03\x06\x049\x03\x05+\x1e\x03\x05,\x17\x03\x05 \x1d\x03" + "\x05\x22\x05\x03\x050\x1d" // lookup returns the trie value for the first UTF-8 encoding in s and // the width in bytes of this encoding. The size will be 0 if s does not // hold enough bytes to complete the encoding. len(s) must be greater than 0. func (t *idnaTrie) lookup(s []byte) (v uint16, sz int) { c0 := s[0] switch { case c0 < 0x80: // is ASCII return idnaValues[c0], 1 case c0 < 0xC2: return 0, 1 // Illegal UTF-8: not a starter, not ASCII. case c0 < 0xE0: // 2-byte UTF-8 if len(s) < 2 { return 0, 0 } i := idnaIndex[c0] c1 := s[1] if c1 < 0x80 || 0xC0 <= c1 { return 0, 1 // Illegal UTF-8: not a continuation byte. } return t.lookupValue(uint32(i), c1), 2 case c0 < 0xF0: // 3-byte UTF-8 if len(s) < 3 { return 0, 0 } i := idnaIndex[c0] c1 := s[1] if c1 < 0x80 || 0xC0 <= c1 { return 0, 1 // Illegal UTF-8: not a continuation byte. } o := uint32(i)<<6 + uint32(c1) i = idnaIndex[o] c2 := s[2] if c2 < 0x80 || 0xC0 <= c2 { return 0, 2 // Illegal UTF-8: not a continuation byte. } return t.lookupValue(uint32(i), c2), 3 case c0 < 0xF8: // 4-byte UTF-8 if len(s) < 4 { return 0, 0 } i := idnaIndex[c0] c1 := s[1] if c1 < 0x80 || 0xC0 <= c1 { return 0, 1 // Illegal UTF-8: not a continuation byte. } o := uint32(i)<<6 + uint32(c1) i = idnaIndex[o] c2 := s[2] if c2 < 0x80 || 0xC0 <= c2 { return 0, 2 // Illegal UTF-8: not a continuation byte. } o = uint32(i)<<6 + uint32(c2) i = idnaIndex[o] c3 := s[3] if c3 < 0x80 || 0xC0 <= c3 { return 0, 3 // Illegal UTF-8: not a continuation byte. } return t.lookupValue(uint32(i), c3), 4 } // Illegal rune return 0, 1 } // lookupUnsafe returns the trie value for the first UTF-8 encoding in s. // s must start with a full and valid UTF-8 encoded rune. func (t *idnaTrie) lookupUnsafe(s []byte) uint16 { c0 := s[0] if c0 < 0x80 { // is ASCII return idnaValues[c0] } i := idnaIndex[c0] if c0 < 0xE0 { // 2-byte UTF-8 return t.lookupValue(uint32(i), s[1]) } i = idnaIndex[uint32(i)<<6+uint32(s[1])] if c0 < 0xF0 { // 3-byte UTF-8 return t.lookupValue(uint32(i), s[2]) } i = idnaIndex[uint32(i)<<6+uint32(s[2])] if c0 < 0xF8 { // 4-byte UTF-8 return t.lookupValue(uint32(i), s[3]) } return 0 } // lookupString returns the trie value for the first UTF-8 encoding in s and // the width in bytes of this encoding. The size will be 0 if s does not // hold enough bytes to complete the encoding. len(s) must be greater than 0. func (t *idnaTrie) lookupString(s string) (v uint16, sz int) { c0 := s[0] switch { case c0 < 0x80: // is ASCII return idnaValues[c0], 1 case c0 < 0xC2: return 0, 1 // Illegal UTF-8: not a starter, not ASCII. case c0 < 0xE0: // 2-byte UTF-8 if len(s) < 2 { return 0, 0 } i := idnaIndex[c0] c1 := s[1] if c1 < 0x80 || 0xC0 <= c1 { return 0, 1 // Illegal UTF-8: not a continuation byte. } return t.lookupValue(uint32(i), c1), 2 case c0 < 0xF0: // 3-byte UTF-8 if len(s) < 3 { return 0, 0 } i := idnaIndex[c0] c1 := s[1] if c1 < 0x80 || 0xC0 <= c1 { return 0, 1 // Illegal UTF-8: not a continuation byte. } o := uint32(i)<<6 + uint32(c1) i = idnaIndex[o] c2 := s[2] if c2 < 0x80 || 0xC0 <= c2 { return 0, 2 // Illegal UTF-8: not a continuation byte. } return t.lookupValue(uint32(i), c2), 3 case c0 < 0xF8: // 4-byte UTF-8 if len(s) < 4 { return 0, 0 } i := idnaIndex[c0] c1 := s[1] if c1 < 0x80 || 0xC0 <= c1 { return 0, 1 // Illegal UTF-8: not a continuation byte. } o := uint32(i)<<6 + uint32(c1) i = idnaIndex[o] c2 := s[2] if c2 < 0x80 || 0xC0 <= c2 { return 0, 2 // Illegal UTF-8: not a continuation byte. } o = uint32(i)<<6 + uint32(c2) i = idnaIndex[o] c3 := s[3] if c3 < 0x80 || 0xC0 <= c3 { return 0, 3 // Illegal UTF-8: not a continuation byte. } return t.lookupValue(uint32(i), c3), 4 } // Illegal rune return 0, 1 } // lookupStringUnsafe returns the trie value for the first UTF-8 encoding in s. // s must start with a full and valid UTF-8 encoded rune. func (t *idnaTrie) lookupStringUnsafe(s string) uint16 { c0 := s[0] if c0 < 0x80 { // is ASCII return idnaValues[c0] } i := idnaIndex[c0] if c0 < 0xE0 { // 2-byte UTF-8 return t.lookupValue(uint32(i), s[1]) } i = idnaIndex[uint32(i)<<6+uint32(s[1])] if c0 < 0xF0 { // 3-byte UTF-8 return t.lookupValue(uint32(i), s[2]) } i = idnaIndex[uint32(i)<<6+uint32(s[2])] if c0 < 0xF8 { // 4-byte UTF-8 return t.lookupValue(uint32(i), s[3]) } return 0 } // idnaTrie. Total size: 29404 bytes (28.71 KiB). Checksum: 848c45acb5f7991c. type idnaTrie struct{} func newIdnaTrie(i int) *idnaTrie { return &idnaTrie{} } // lookupValue determines the type of block n and looks up the value for b. func (t *idnaTrie) lookupValue(n uint32, b byte) uint16 { switch { case n < 125: return uint16(idnaValues[n<<6+uint32(b)]) default: n -= 125 return uint16(idnaSparse.lookup(n, b)) } } // idnaValues: 127 blocks, 8128 entries, 16256 bytes // The third block is the zero block. var idnaValues = [8128]uint16{ // Block 0x0, offset 0x0 0x00: 0x0080, 0x01: 0x0080, 0x02: 0x0080, 0x03: 0x0080, 0x04: 0x0080, 0x05: 0x0080, 0x06: 0x0080, 0x07: 0x0080, 0x08: 0x0080, 0x09: 0x0080, 0x0a: 0x0080, 0x0b: 0x0080, 0x0c: 0x0080, 0x0d: 0x0080, 0x0e: 0x0080, 0x0f: 0x0080, 0x10: 0x0080, 0x11: 0x0080, 0x12: 0x0080, 0x13: 0x0080, 0x14: 0x0080, 0x15: 0x0080, 0x16: 0x0080, 0x17: 0x0080, 0x18: 0x0080, 0x19: 0x0080, 0x1a: 0x0080, 0x1b: 0x0080, 0x1c: 0x0080, 0x1d: 0x0080, 0x1e: 0x0080, 0x1f: 0x0080, 0x20: 0x0080, 0x21: 0x0080, 0x22: 0x0080, 0x23: 0x0080, 0x24: 0x0080, 0x25: 0x0080, 0x26: 0x0080, 0x27: 0x0080, 0x28: 0x0080, 0x29: 0x0080, 0x2a: 0x0080, 0x2b: 0x0080, 0x2c: 0x0080, 0x2d: 0x0008, 0x2e: 0x0008, 0x2f: 0x0080, 0x30: 0x0008, 0x31: 0x0008, 0x32: 0x0008, 0x33: 0x0008, 0x34: 0x0008, 0x35: 0x0008, 0x36: 0x0008, 0x37: 0x0008, 0x38: 0x0008, 0x39: 0x0008, 0x3a: 0x0080, 0x3b: 0x0080, 0x3c: 0x0080, 0x3d: 0x0080, 0x3e: 0x0080, 0x3f: 0x0080, // Block 0x1, offset 0x40 0x40: 0x0080, 0x41: 0xe105, 0x42: 0xe105, 0x43: 0xe105, 0x44: 0xe105, 0x45: 0xe105, 0x46: 0xe105, 0x47: 0xe105, 0x48: 0xe105, 0x49: 0xe105, 0x4a: 0xe105, 0x4b: 0xe105, 0x4c: 0xe105, 0x4d: 0xe105, 0x4e: 0xe105, 0x4f: 0xe105, 0x50: 0xe105, 0x51: 0xe105, 0x52: 0xe105, 0x53: 0xe105, 0x54: 0xe105, 0x55: 0xe105, 0x56: 0xe105, 0x57: 0xe105, 0x58: 0xe105, 0x59: 0xe105, 0x5a: 0xe105, 0x5b: 0x0080, 0x5c: 0x0080, 0x5d: 0x0080, 0x5e: 0x0080, 0x5f: 0x0080, 0x60: 0x0080, 0x61: 0x0008, 0x62: 0x0008, 0x63: 0x0008, 0x64: 0x0008, 0x65: 0x0008, 0x66: 0x0008, 0x67: 0x0008, 0x68: 0x0008, 0x69: 0x0008, 0x6a: 0x0008, 0x6b: 0x0008, 0x6c: 0x0008, 0x6d: 0x0008, 0x6e: 0x0008, 0x6f: 0x0008, 0x70: 0x0008, 0x71: 0x0008, 0x72: 0x0008, 0x73: 0x0008, 0x74: 0x0008, 0x75: 0x0008, 0x76: 0x0008, 0x77: 0x0008, 0x78: 0x0008, 0x79: 0x0008, 0x7a: 0x0008, 0x7b: 0x0080, 0x7c: 0x0080, 0x7d: 0x0080, 0x7e: 0x0080, 0x7f: 0x0080, // Block 0x2, offset 0x80 // Block 0x3, offset 0xc0 0xc0: 0x0040, 0xc1: 0x0040, 0xc2: 0x0040, 0xc3: 0x0040, 0xc4: 0x0040, 0xc5: 0x0040, 0xc6: 0x0040, 0xc7: 0x0040, 0xc8: 0x0040, 0xc9: 0x0040, 0xca: 0x0040, 0xcb: 0x0040, 0xcc: 0x0040, 0xcd: 0x0040, 0xce: 0x0040, 0xcf: 0x0040, 0xd0: 0x0040, 0xd1: 0x0040, 0xd2: 0x0040, 0xd3: 0x0040, 0xd4: 0x0040, 0xd5: 0x0040, 0xd6: 0x0040, 0xd7: 0x0040, 0xd8: 0x0040, 0xd9: 0x0040, 0xda: 0x0040, 0xdb: 0x0040, 0xdc: 0x0040, 0xdd: 0x0040, 0xde: 0x0040, 0xdf: 0x0040, 0xe0: 0x000a, 0xe1: 0x0018, 0xe2: 0x0018, 0xe3: 0x0018, 0xe4: 0x0018, 0xe5: 0x0018, 0xe6: 0x0018, 0xe7: 0x0018, 0xe8: 0x001a, 0xe9: 0x0018, 0xea: 0x0039, 0xeb: 0x0018, 0xec: 0x0018, 0xed: 0x03c0, 0xee: 0x0018, 0xef: 0x004a, 0xf0: 0x0018, 0xf1: 0x0018, 0xf2: 0x0069, 0xf3: 0x0079, 0xf4: 0x008a, 0xf5: 0x0005, 0xf6: 0x0018, 0xf7: 0x0008, 0xf8: 0x00aa, 0xf9: 0x00c9, 0xfa: 0x00d9, 0xfb: 0x0018, 0xfc: 0x00e9, 0xfd: 0x0119, 0xfe: 0x0149, 0xff: 0x0018, // Block 0x4, offset 0x100 0x100: 0xe00d, 0x101: 0x0008, 0x102: 0xe00d, 0x103: 0x0008, 0x104: 0xe00d, 0x105: 0x0008, 0x106: 0xe00d, 0x107: 0x0008, 0x108: 0xe00d, 0x109: 0x0008, 0x10a: 0xe00d, 0x10b: 0x0008, 0x10c: 0xe00d, 0x10d: 0x0008, 0x10e: 0xe00d, 0x10f: 0x0008, 0x110: 0xe00d, 0x111: 0x0008, 0x112: 0xe00d, 0x113: 0x0008, 0x114: 0xe00d, 0x115: 0x0008, 0x116: 0xe00d, 0x117: 0x0008, 0x118: 0xe00d, 0x119: 0x0008, 0x11a: 0xe00d, 0x11b: 0x0008, 0x11c: 0xe00d, 0x11d: 0x0008, 0x11e: 0xe00d, 0x11f: 0x0008, 0x120: 0xe00d, 0x121: 0x0008, 0x122: 0xe00d, 0x123: 0x0008, 0x124: 0xe00d, 0x125: 0x0008, 0x126: 0xe00d, 0x127: 0x0008, 0x128: 0xe00d, 0x129: 0x0008, 0x12a: 0xe00d, 0x12b: 0x0008, 0x12c: 0xe00d, 0x12d: 0x0008, 0x12e: 0xe00d, 0x12f: 0x0008, 0x130: 0x0179, 0x131: 0x0008, 0x132: 0x0035, 0x133: 0x004d, 0x134: 0xe00d, 0x135: 0x0008, 0x136: 0xe00d, 0x137: 0x0008, 0x138: 0x0008, 0x139: 0xe01d, 0x13a: 0x0008, 0x13b: 0xe03d, 0x13c: 0x0008, 0x13d: 0xe01d, 0x13e: 0x0008, 0x13f: 0x0199, // Block 0x5, offset 0x140 0x140: 0x0199, 0x141: 0xe01d, 0x142: 0x0008, 0x143: 0xe03d, 0x144: 0x0008, 0x145: 0xe01d, 0x146: 0x0008, 0x147: 0xe07d, 0x148: 0x0008, 0x149: 0x01b9, 0x14a: 0xe00d, 0x14b: 0x0008, 0x14c: 0xe00d, 0x14d: 0x0008, 0x14e: 0xe00d, 0x14f: 0x0008, 0x150: 0xe00d, 0x151: 0x0008, 0x152: 0xe00d, 0x153: 0x0008, 0x154: 0xe00d, 0x155: 0x0008, 0x156: 0xe00d, 0x157: 0x0008, 0x158: 0xe00d, 0x159: 0x0008, 0x15a: 0xe00d, 0x15b: 0x0008, 0x15c: 0xe00d, 0x15d: 0x0008, 0x15e: 0xe00d, 0x15f: 0x0008, 0x160: 0xe00d, 0x161: 0x0008, 0x162: 0xe00d, 0x163: 0x0008, 0x164: 0xe00d, 0x165: 0x0008, 0x166: 0xe00d, 0x167: 0x0008, 0x168: 0xe00d, 0x169: 0x0008, 0x16a: 0xe00d, 0x16b: 0x0008, 0x16c: 0xe00d, 0x16d: 0x0008, 0x16e: 0xe00d, 0x16f: 0x0008, 0x170: 0xe00d, 0x171: 0x0008, 0x172: 0xe00d, 0x173: 0x0008, 0x174: 0xe00d, 0x175: 0x0008, 0x176: 0xe00d, 0x177: 0x0008, 0x178: 0x0065, 0x179: 0xe01d, 0x17a: 0x0008, 0x17b: 0xe03d, 0x17c: 0x0008, 0x17d: 0xe01d, 0x17e: 0x0008, 0x17f: 0x01d9, // Block 0x6, offset 0x180 0x180: 0x0008, 0x181: 0x007d, 0x182: 0xe00d, 0x183: 0x0008, 0x184: 0xe00d, 0x185: 0x0008, 0x186: 0x007d, 0x187: 0xe07d, 0x188: 0x0008, 0x189: 0x0095, 0x18a: 0x00ad, 0x18b: 0xe03d, 0x18c: 0x0008, 0x18d: 0x0008, 0x18e: 0x00c5, 0x18f: 0x00dd, 0x190: 0x00f5, 0x191: 0xe01d, 0x192: 0x0008, 0x193: 0x010d, 0x194: 0x0125, 0x195: 0x0008, 0x196: 0x013d, 0x197: 0x013d, 0x198: 0xe00d, 0x199: 0x0008, 0x19a: 0x0008, 0x19b: 0x0008, 0x19c: 0x010d, 0x19d: 0x0155, 0x19e: 0x0008, 0x19f: 0x016d, 0x1a0: 0xe00d, 0x1a1: 0x0008, 0x1a2: 0xe00d, 0x1a3: 0x0008, 0x1a4: 0xe00d, 0x1a5: 0x0008, 0x1a6: 0x0185, 0x1a7: 0xe07d, 0x1a8: 0x0008, 0x1a9: 0x019d, 0x1aa: 0x0008, 0x1ab: 0x0008, 0x1ac: 0xe00d, 0x1ad: 0x0008, 0x1ae: 0x0185, 0x1af: 0xe0fd, 0x1b0: 0x0008, 0x1b1: 0x01b5, 0x1b2: 0x01cd, 0x1b3: 0xe03d, 0x1b4: 0x0008, 0x1b5: 0xe01d, 0x1b6: 0x0008, 0x1b7: 0x01e5, 0x1b8: 0xe00d, 0x1b9: 0x0008, 0x1ba: 0x0008, 0x1bb: 0x0008, 0x1bc: 0xe00d, 0x1bd: 0x0008, 0x1be: 0x0008, 0x1bf: 0x0008, // Block 0x7, offset 0x1c0 0x1c0: 0x0008, 0x1c1: 0x0008, 0x1c2: 0x0008, 0x1c3: 0x0008, 0x1c4: 0x01e9, 0x1c5: 0x01e9, 0x1c6: 0x01e9, 0x1c7: 0x01fd, 0x1c8: 0x0215, 0x1c9: 0x022d, 0x1ca: 0x0245, 0x1cb: 0x025d, 0x1cc: 0x0275, 0x1cd: 0xe01d, 0x1ce: 0x0008, 0x1cf: 0xe0fd, 0x1d0: 0x0008, 0x1d1: 0xe01d, 0x1d2: 0x0008, 0x1d3: 0xe03d, 0x1d4: 0x0008, 0x1d5: 0xe01d, 0x1d6: 0x0008, 0x1d7: 0xe07d, 0x1d8: 0x0008, 0x1d9: 0xe01d, 0x1da: 0x0008, 0x1db: 0xe03d, 0x1dc: 0x0008, 0x1dd: 0x0008, 0x1de: 0xe00d, 0x1df: 0x0008, 0x1e0: 0xe00d, 0x1e1: 0x0008, 0x1e2: 0xe00d, 0x1e3: 0x0008, 0x1e4: 0xe00d, 0x1e5: 0x0008, 0x1e6: 0xe00d, 0x1e7: 0x0008, 0x1e8: 0xe00d, 0x1e9: 0x0008, 0x1ea: 0xe00d, 0x1eb: 0x0008, 0x1ec: 0xe00d, 0x1ed: 0x0008, 0x1ee: 0xe00d, 0x1ef: 0x0008, 0x1f0: 0x0008, 0x1f1: 0x028d, 0x1f2: 0x02a5, 0x1f3: 0x02bd, 0x1f4: 0xe00d, 0x1f5: 0x0008, 0x1f6: 0x02d5, 0x1f7: 0x02ed, 0x1f8: 0xe00d, 0x1f9: 0x0008, 0x1fa: 0xe00d, 0x1fb: 0x0008, 0x1fc: 0xe00d, 0x1fd: 0x0008, 0x1fe: 0xe00d, 0x1ff: 0x0008, // Block 0x8, offset 0x200 0x200: 0xe00d, 0x201: 0x0008, 0x202: 0xe00d, 0x203: 0x0008, 0x204: 0xe00d, 0x205: 0x0008, 0x206: 0xe00d, 0x207: 0x0008, 0x208: 0xe00d, 0x209: 0x0008, 0x20a: 0xe00d, 0x20b: 0x0008, 0x20c: 0xe00d, 0x20d: 0x0008, 0x20e: 0xe00d, 0x20f: 0x0008, 0x210: 0xe00d, 0x211: 0x0008, 0x212: 0xe00d, 0x213: 0x0008, 0x214: 0xe00d, 0x215: 0x0008, 0x216: 0xe00d, 0x217: 0x0008, 0x218: 0xe00d, 0x219: 0x0008, 0x21a: 0xe00d, 0x21b: 0x0008, 0x21c: 0xe00d, 0x21d: 0x0008, 0x21e: 0xe00d, 0x21f: 0x0008, 0x220: 0x0305, 0x221: 0x0008, 0x222: 0xe00d, 0x223: 0x0008, 0x224: 0xe00d, 0x225: 0x0008, 0x226: 0xe00d, 0x227: 0x0008, 0x228: 0xe00d, 0x229: 0x0008, 0x22a: 0xe00d, 0x22b: 0x0008, 0x22c: 0xe00d, 0x22d: 0x0008, 0x22e: 0xe00d, 0x22f: 0x0008, 0x230: 0xe00d, 0x231: 0x0008, 0x232: 0xe00d, 0x233: 0x0008, 0x234: 0x0008, 0x235: 0x0008, 0x236: 0x0008, 0x237: 0x0008, 0x238: 0x0008, 0x239: 0x0008, 0x23a: 0x0209, 0x23b: 0xe03d, 0x23c: 0x0008, 0x23d: 0x031d, 0x23e: 0x0229, 0x23f: 0x0008, // Block 0x9, offset 0x240 0x240: 0x0008, 0x241: 0x0008, 0x242: 0x0018, 0x243: 0x0018, 0x244: 0x0018, 0x245: 0x0018, 0x246: 0x0008, 0x247: 0x0008, 0x248: 0x0008, 0x249: 0x0008, 0x24a: 0x0008, 0x24b: 0x0008, 0x24c: 0x0008, 0x24d: 0x0008, 0x24e: 0x0008, 0x24f: 0x0008, 0x250: 0x0008, 0x251: 0x0008, 0x252: 0x0018, 0x253: 0x0018, 0x254: 0x0018, 0x255: 0x0018, 0x256: 0x0018, 0x257: 0x0018, 0x258: 0x029a, 0x259: 0x02ba, 0x25a: 0x02da, 0x25b: 0x02fa, 0x25c: 0x031a, 0x25d: 0x033a, 0x25e: 0x0018, 0x25f: 0x0018, 0x260: 0x03ad, 0x261: 0x0359, 0x262: 0x01d9, 0x263: 0x0369, 0x264: 0x03c5, 0x265: 0x0018, 0x266: 0x0018, 0x267: 0x0018, 0x268: 0x0018, 0x269: 0x0018, 0x26a: 0x0018, 0x26b: 0x0018, 0x26c: 0x0008, 0x26d: 0x0018, 0x26e: 0x0008, 0x26f: 0x0018, 0x270: 0x0018, 0x271: 0x0018, 0x272: 0x0018, 0x273: 0x0018, 0x274: 0x0018, 0x275: 0x0018, 0x276: 0x0018, 0x277: 0x0018, 0x278: 0x0018, 0x279: 0x0018, 0x27a: 0x0018, 0x27b: 0x0018, 0x27c: 0x0018, 0x27d: 0x0018, 0x27e: 0x0018, 0x27f: 0x0018, // Block 0xa, offset 0x280 0x280: 0x03dd, 0x281: 0x03dd, 0x282: 0x3308, 0x283: 0x03f5, 0x284: 0x0379, 0x285: 0x040d, 0x286: 0x3308, 0x287: 0x3308, 0x288: 0x3308, 0x289: 0x3308, 0x28a: 0x3308, 0x28b: 0x3308, 0x28c: 0x3308, 0x28d: 0x3308, 0x28e: 0x3308, 0x28f: 0x33c0, 0x290: 0x3308, 0x291: 0x3308, 0x292: 0x3308, 0x293: 0x3308, 0x294: 0x3308, 0x295: 0x3308, 0x296: 0x3308, 0x297: 0x3308, 0x298: 0x3308, 0x299: 0x3308, 0x29a: 0x3308, 0x29b: 0x3308, 0x29c: 0x3308, 0x29d: 0x3308, 0x29e: 0x3308, 0x29f: 0x3308, 0x2a0: 0x3308, 0x2a1: 0x3308, 0x2a2: 0x3308, 0x2a3: 0x3308, 0x2a4: 0x3308, 0x2a5: 0x3308, 0x2a6: 0x3308, 0x2a7: 0x3308, 0x2a8: 0x3308, 0x2a9: 0x3308, 0x2aa: 0x3308, 0x2ab: 0x3308, 0x2ac: 0x3308, 0x2ad: 0x3308, 0x2ae: 0x3308, 0x2af: 0x3308, 0x2b0: 0xe00d, 0x2b1: 0x0008, 0x2b2: 0xe00d, 0x2b3: 0x0008, 0x2b4: 0x0425, 0x2b5: 0x0008, 0x2b6: 0xe00d, 0x2b7: 0x0008, 0x2b8: 0x0040, 0x2b9: 0x0040, 0x2ba: 0x03a2, 0x2bb: 0x0008, 0x2bc: 0x0008, 0x2bd: 0x0008, 0x2be: 0x03c2, 0x2bf: 0x043d, // Block 0xb, offset 0x2c0 0x2c0: 0x0040, 0x2c1: 0x0040, 0x2c2: 0x0040, 0x2c3: 0x0040, 0x2c4: 0x008a, 0x2c5: 0x03d2, 0x2c6: 0xe155, 0x2c7: 0x0455, 0x2c8: 0xe12d, 0x2c9: 0xe13d, 0x2ca: 0xe12d, 0x2cb: 0x0040, 0x2cc: 0x03dd, 0x2cd: 0x0040, 0x2ce: 0x046d, 0x2cf: 0x0485, 0x2d0: 0x0008, 0x2d1: 0xe105, 0x2d2: 0xe105, 0x2d3: 0xe105, 0x2d4: 0xe105, 0x2d5: 0xe105, 0x2d6: 0xe105, 0x2d7: 0xe105, 0x2d8: 0xe105, 0x2d9: 0xe105, 0x2da: 0xe105, 0x2db: 0xe105, 0x2dc: 0xe105, 0x2dd: 0xe105, 0x2de: 0xe105, 0x2df: 0xe105, 0x2e0: 0x049d, 0x2e1: 0x049d, 0x2e2: 0x0040, 0x2e3: 0x049d, 0x2e4: 0x049d, 0x2e5: 0x049d, 0x2e6: 0x049d, 0x2e7: 0x049d, 0x2e8: 0x049d, 0x2e9: 0x049d, 0x2ea: 0x049d, 0x2eb: 0x049d, 0x2ec: 0x0008, 0x2ed: 0x0008, 0x2ee: 0x0008, 0x2ef: 0x0008, 0x2f0: 0x0008, 0x2f1: 0x0008, 0x2f2: 0x0008, 0x2f3: 0x0008, 0x2f4: 0x0008, 0x2f5: 0x0008, 0x2f6: 0x0008, 0x2f7: 0x0008, 0x2f8: 0x0008, 0x2f9: 0x0008, 0x2fa: 0x0008, 0x2fb: 0x0008, 0x2fc: 0x0008, 0x2fd: 0x0008, 0x2fe: 0x0008, 0x2ff: 0x0008, // Block 0xc, offset 0x300 0x300: 0x0008, 0x301: 0x0008, 0x302: 0xe00f, 0x303: 0x0008, 0x304: 0x0008, 0x305: 0x0008, 0x306: 0x0008, 0x307: 0x0008, 0x308: 0x0008, 0x309: 0x0008, 0x30a: 0x0008, 0x30b: 0x0008, 0x30c: 0x0008, 0x30d: 0x0008, 0x30e: 0x0008, 0x30f: 0xe0c5, 0x310: 0x04b5, 0x311: 0x04cd, 0x312: 0xe0bd, 0x313: 0xe0f5, 0x314: 0xe0fd, 0x315: 0xe09d, 0x316: 0xe0b5, 0x317: 0x0008, 0x318: 0xe00d, 0x319: 0x0008, 0x31a: 0xe00d, 0x31b: 0x0008, 0x31c: 0xe00d, 0x31d: 0x0008, 0x31e: 0xe00d, 0x31f: 0x0008, 0x320: 0xe00d, 0x321: 0x0008, 0x322: 0xe00d, 0x323: 0x0008, 0x324: 0xe00d, 0x325: 0x0008, 0x326: 0xe00d, 0x327: 0x0008, 0x328: 0xe00d, 0x329: 0x0008, 0x32a: 0xe00d, 0x32b: 0x0008, 0x32c: 0xe00d, 0x32d: 0x0008, 0x32e: 0xe00d, 0x32f: 0x0008, 0x330: 0x04e5, 0x331: 0xe185, 0x332: 0xe18d, 0x333: 0x0008, 0x334: 0x04fd, 0x335: 0x03dd, 0x336: 0x0018, 0x337: 0xe07d, 0x338: 0x0008, 0x339: 0xe1d5, 0x33a: 0xe00d, 0x33b: 0x0008, 0x33c: 0x0008, 0x33d: 0x0515, 0x33e: 0x052d, 0x33f: 0x052d, // Block 0xd, offset 0x340 0x340: 0x0008, 0x341: 0x0008, 0x342: 0x0008, 0x343: 0x0008, 0x344: 0x0008, 0x345: 0x0008, 0x346: 0x0008, 0x347: 0x0008, 0x348: 0x0008, 0x349: 0x0008, 0x34a: 0x0008, 0x34b: 0x0008, 0x34c: 0x0008, 0x34d: 0x0008, 0x34e: 0x0008, 0x34f: 0x0008, 0x350: 0x0008, 0x351: 0x0008, 0x352: 0x0008, 0x353: 0x0008, 0x354: 0x0008, 0x355: 0x0008, 0x356: 0x0008, 0x357: 0x0008, 0x358: 0x0008, 0x359: 0x0008, 0x35a: 0x0008, 0x35b: 0x0008, 0x35c: 0x0008, 0x35d: 0x0008, 0x35e: 0x0008, 0x35f: 0x0008, 0x360: 0xe00d, 0x361: 0x0008, 0x362: 0xe00d, 0x363: 0x0008, 0x364: 0xe00d, 0x365: 0x0008, 0x366: 0xe00d, 0x367: 0x0008, 0x368: 0xe00d, 0x369: 0x0008, 0x36a: 0xe00d, 0x36b: 0x0008, 0x36c: 0xe00d, 0x36d: 0x0008, 0x36e: 0xe00d, 0x36f: 0x0008, 0x370: 0xe00d, 0x371: 0x0008, 0x372: 0xe00d, 0x373: 0x0008, 0x374: 0xe00d, 0x375: 0x0008, 0x376: 0xe00d, 0x377: 0x0008, 0x378: 0xe00d, 0x379: 0x0008, 0x37a: 0xe00d, 0x37b: 0x0008, 0x37c: 0xe00d, 0x37d: 0x0008, 0x37e: 0xe00d, 0x37f: 0x0008, // Block 0xe, offset 0x380 0x380: 0xe00d, 0x381: 0x0008, 0x382: 0x0018, 0x383: 0x3308, 0x384: 0x3308, 0x385: 0x3308, 0x386: 0x3308, 0x387: 0x3308, 0x388: 0x3318, 0x389: 0x3318, 0x38a: 0xe00d, 0x38b: 0x0008, 0x38c: 0xe00d, 0x38d: 0x0008, 0x38e: 0xe00d, 0x38f: 0x0008, 0x390: 0xe00d, 0x391: 0x0008, 0x392: 0xe00d, 0x393: 0x0008, 0x394: 0xe00d, 0x395: 0x0008, 0x396: 0xe00d, 0x397: 0x0008, 0x398: 0xe00d, 0x399: 0x0008, 0x39a: 0xe00d, 0x39b: 0x0008, 0x39c: 0xe00d, 0x39d: 0x0008, 0x39e: 0xe00d, 0x39f: 0x0008, 0x3a0: 0xe00d, 0x3a1: 0x0008, 0x3a2: 0xe00d, 0x3a3: 0x0008, 0x3a4: 0xe00d, 0x3a5: 0x0008, 0x3a6: 0xe00d, 0x3a7: 0x0008, 0x3a8: 0xe00d, 0x3a9: 0x0008, 0x3aa: 0xe00d, 0x3ab: 0x0008, 0x3ac: 0xe00d, 0x3ad: 0x0008, 0x3ae: 0xe00d, 0x3af: 0x0008, 0x3b0: 0xe00d, 0x3b1: 0x0008, 0x3b2: 0xe00d, 0x3b3: 0x0008, 0x3b4: 0xe00d, 0x3b5: 0x0008, 0x3b6: 0xe00d, 0x3b7: 0x0008, 0x3b8: 0xe00d, 0x3b9: 0x0008, 0x3ba: 0xe00d, 0x3bb: 0x0008, 0x3bc: 0xe00d, 0x3bd: 0x0008, 0x3be: 0xe00d, 0x3bf: 0x0008, // Block 0xf, offset 0x3c0 0x3c0: 0x0040, 0x3c1: 0xe01d, 0x3c2: 0x0008, 0x3c3: 0xe03d, 0x3c4: 0x0008, 0x3c5: 0xe01d, 0x3c6: 0x0008, 0x3c7: 0xe07d, 0x3c8: 0x0008, 0x3c9: 0xe01d, 0x3ca: 0x0008, 0x3cb: 0xe03d, 0x3cc: 0x0008, 0x3cd: 0xe01d, 0x3ce: 0x0008, 0x3cf: 0x0008, 0x3d0: 0xe00d, 0x3d1: 0x0008, 0x3d2: 0xe00d, 0x3d3: 0x0008, 0x3d4: 0xe00d, 0x3d5: 0x0008, 0x3d6: 0xe00d, 0x3d7: 0x0008, 0x3d8: 0xe00d, 0x3d9: 0x0008, 0x3da: 0xe00d, 0x3db: 0x0008, 0x3dc: 0xe00d, 0x3dd: 0x0008, 0x3de: 0xe00d, 0x3df: 0x0008, 0x3e0: 0xe00d, 0x3e1: 0x0008, 0x3e2: 0xe00d, 0x3e3: 0x0008, 0x3e4: 0xe00d, 0x3e5: 0x0008, 0x3e6: 0xe00d, 0x3e7: 0x0008, 0x3e8: 0xe00d, 0x3e9: 0x0008, 0x3ea: 0xe00d, 0x3eb: 0x0008, 0x3ec: 0xe00d, 0x3ed: 0x0008, 0x3ee: 0xe00d, 0x3ef: 0x0008, 0x3f0: 0xe00d, 0x3f1: 0x0008, 0x3f2: 0xe00d, 0x3f3: 0x0008, 0x3f4: 0xe00d, 0x3f5: 0x0008, 0x3f6: 0xe00d, 0x3f7: 0x0008, 0x3f8: 0xe00d, 0x3f9: 0x0008, 0x3fa: 0xe00d, 0x3fb: 0x0008, 0x3fc: 0xe00d, 0x3fd: 0x0008, 0x3fe: 0xe00d, 0x3ff: 0x0008, // Block 0x10, offset 0x400 0x400: 0xe00d, 0x401: 0x0008, 0x402: 0xe00d, 0x403: 0x0008, 0x404: 0xe00d, 0x405: 0x0008, 0x406: 0xe00d, 0x407: 0x0008, 0x408: 0xe00d, 0x409: 0x0008, 0x40a: 0xe00d, 0x40b: 0x0008, 0x40c: 0xe00d, 0x40d: 0x0008, 0x40e: 0xe00d, 0x40f: 0x0008, 0x410: 0xe00d, 0x411: 0x0008, 0x412: 0xe00d, 0x413: 0x0008, 0x414: 0xe00d, 0x415: 0x0008, 0x416: 0xe00d, 0x417: 0x0008, 0x418: 0xe00d, 0x419: 0x0008, 0x41a: 0xe00d, 0x41b: 0x0008, 0x41c: 0xe00d, 0x41d: 0x0008, 0x41e: 0xe00d, 0x41f: 0x0008, 0x420: 0xe00d, 0x421: 0x0008, 0x422: 0xe00d, 0x423: 0x0008, 0x424: 0xe00d, 0x425: 0x0008, 0x426: 0xe00d, 0x427: 0x0008, 0x428: 0xe00d, 0x429: 0x0008, 0x42a: 0xe00d, 0x42b: 0x0008, 0x42c: 0xe00d, 0x42d: 0x0008, 0x42e: 0xe00d, 0x42f: 0x0008, 0x430: 0x0040, 0x431: 0x03f5, 0x432: 0x03f5, 0x433: 0x03f5, 0x434: 0x03f5, 0x435: 0x03f5, 0x436: 0x03f5, 0x437: 0x03f5, 0x438: 0x03f5, 0x439: 0x03f5, 0x43a: 0x03f5, 0x43b: 0x03f5, 0x43c: 0x03f5, 0x43d: 0x03f5, 0x43e: 0x03f5, 0x43f: 0x03f5, // Block 0x11, offset 0x440 0x440: 0x0840, 0x441: 0x0840, 0x442: 0x0840, 0x443: 0x0840, 0x444: 0x0840, 0x445: 0x0840, 0x446: 0x0018, 0x447: 0x0018, 0x448: 0x0818, 0x449: 0x0018, 0x44a: 0x0018, 0x44b: 0x0818, 0x44c: 0x0018, 0x44d: 0x0818, 0x44e: 0x0018, 0x44f: 0x0018, 0x450: 0x3308, 0x451: 0x3308, 0x452: 0x3308, 0x453: 0x3308, 0x454: 0x3308, 0x455: 0x3308, 0x456: 0x3308, 0x457: 0x3308, 0x458: 0x3308, 0x459: 0x3308, 0x45a: 0x3308, 0x45b: 0x0818, 0x45c: 0x0b40, 0x45d: 0x0040, 0x45e: 0x0818, 0x45f: 0x0818, 0x460: 0x0a08, 0x461: 0x0808, 0x462: 0x0c08, 0x463: 0x0c08, 0x464: 0x0c08, 0x465: 0x0c08, 0x466: 0x0a08, 0x467: 0x0c08, 0x468: 0x0a08, 0x469: 0x0c08, 0x46a: 0x0a08, 0x46b: 0x0a08, 0x46c: 0x0a08, 0x46d: 0x0a08, 0x46e: 0x0a08, 0x46f: 0x0c08, 0x470: 0x0c08, 0x471: 0x0c08, 0x472: 0x0c08, 0x473: 0x0a08, 0x474: 0x0a08, 0x475: 0x0a08, 0x476: 0x0a08, 0x477: 0x0a08, 0x478: 0x0a08, 0x479: 0x0a08, 0x47a: 0x0a08, 0x47b: 0x0a08, 0x47c: 0x0a08, 0x47d: 0x0a08, 0x47e: 0x0a08, 0x47f: 0x0a08, // Block 0x12, offset 0x480 0x480: 0x0818, 0x481: 0x0a08, 0x482: 0x0a08, 0x483: 0x0a08, 0x484: 0x0a08, 0x485: 0x0a08, 0x486: 0x0a08, 0x487: 0x0a08, 0x488: 0x0c08, 0x489: 0x0a08, 0x48a: 0x0a08, 0x48b: 0x3308, 0x48c: 0x3308, 0x48d: 0x3308, 0x48e: 0x3308, 0x48f: 0x3308, 0x490: 0x3308, 0x491: 0x3308, 0x492: 0x3308, 0x493: 0x3308, 0x494: 0x3308, 0x495: 0x3308, 0x496: 0x3308, 0x497: 0x3308, 0x498: 0x3308, 0x499: 0x3308, 0x49a: 0x3308, 0x49b: 0x3308, 0x49c: 0x3308, 0x49d: 0x3308, 0x49e: 0x3308, 0x49f: 0x3308, 0x4a0: 0x0808, 0x4a1: 0x0808, 0x4a2: 0x0808, 0x4a3: 0x0808, 0x4a4: 0x0808, 0x4a5: 0x0808, 0x4a6: 0x0808, 0x4a7: 0x0808, 0x4a8: 0x0808, 0x4a9: 0x0808, 0x4aa: 0x0018, 0x4ab: 0x0818, 0x4ac: 0x0818, 0x4ad: 0x0818, 0x4ae: 0x0a08, 0x4af: 0x0a08, 0x4b0: 0x3308, 0x4b1: 0x0c08, 0x4b2: 0x0c08, 0x4b3: 0x0c08, 0x4b4: 0x0808, 0x4b5: 0x0429, 0x4b6: 0x0451, 0x4b7: 0x0479, 0x4b8: 0x04a1, 0x4b9: 0x0a08, 0x4ba: 0x0a08, 0x4bb: 0x0a08, 0x4bc: 0x0a08, 0x4bd: 0x0a08, 0x4be: 0x0a08, 0x4bf: 0x0a08, // Block 0x13, offset 0x4c0 0x4c0: 0x0c08, 0x4c1: 0x0a08, 0x4c2: 0x0a08, 0x4c3: 0x0c08, 0x4c4: 0x0c08, 0x4c5: 0x0c08, 0x4c6: 0x0c08, 0x4c7: 0x0c08, 0x4c8: 0x0c08, 0x4c9: 0x0c08, 0x4ca: 0x0c08, 0x4cb: 0x0c08, 0x4cc: 0x0a08, 0x4cd: 0x0c08, 0x4ce: 0x0a08, 0x4cf: 0x0c08, 0x4d0: 0x0a08, 0x4d1: 0x0a08, 0x4d2: 0x0c08, 0x4d3: 0x0c08, 0x4d4: 0x0818, 0x4d5: 0x0c08, 0x4d6: 0x3308, 0x4d7: 0x3308, 0x4d8: 0x3308, 0x4d9: 0x3308, 0x4da: 0x3308, 0x4db: 0x3308, 0x4dc: 0x3308, 0x4dd: 0x0840, 0x4de: 0x0018, 0x4df: 0x3308, 0x4e0: 0x3308, 0x4e1: 0x3308, 0x4e2: 0x3308, 0x4e3: 0x3308, 0x4e4: 0x3308, 0x4e5: 0x0808, 0x4e6: 0x0808, 0x4e7: 0x3308, 0x4e8: 0x3308, 0x4e9: 0x0018, 0x4ea: 0x3308, 0x4eb: 0x3308, 0x4ec: 0x3308, 0x4ed: 0x3308, 0x4ee: 0x0c08, 0x4ef: 0x0c08, 0x4f0: 0x0008, 0x4f1: 0x0008, 0x4f2: 0x0008, 0x4f3: 0x0008, 0x4f4: 0x0008, 0x4f5: 0x0008, 0x4f6: 0x0008, 0x4f7: 0x0008, 0x4f8: 0x0008, 0x4f9: 0x0008, 0x4fa: 0x0a08, 0x4fb: 0x0a08, 0x4fc: 0x0a08, 0x4fd: 0x0808, 0x4fe: 0x0808, 0x4ff: 0x0a08, // Block 0x14, offset 0x500 0x500: 0x0818, 0x501: 0x0818, 0x502: 0x0818, 0x503: 0x0818, 0x504: 0x0818, 0x505: 0x0818, 0x506: 0x0818, 0x507: 0x0818, 0x508: 0x0818, 0x509: 0x0818, 0x50a: 0x0818, 0x50b: 0x0818, 0x50c: 0x0818, 0x50d: 0x0818, 0x50e: 0x0040, 0x50f: 0x0b40, 0x510: 0x0c08, 0x511: 0x3308, 0x512: 0x0a08, 0x513: 0x0a08, 0x514: 0x0a08, 0x515: 0x0c08, 0x516: 0x0c08, 0x517: 0x0c08, 0x518: 0x0c08, 0x519: 0x0c08, 0x51a: 0x0a08, 0x51b: 0x0a08, 0x51c: 0x0a08, 0x51d: 0x0a08, 0x51e: 0x0c08, 0x51f: 0x0a08, 0x520: 0x0a08, 0x521: 0x0a08, 0x522: 0x0a08, 0x523: 0x0a08, 0x524: 0x0a08, 0x525: 0x0a08, 0x526: 0x0a08, 0x527: 0x0a08, 0x528: 0x0c08, 0x529: 0x0a08, 0x52a: 0x0c08, 0x52b: 0x0a08, 0x52c: 0x0c08, 0x52d: 0x0a08, 0x52e: 0x0a08, 0x52f: 0x0c08, 0x530: 0x3308, 0x531: 0x3308, 0x532: 0x3308, 0x533: 0x3308, 0x534: 0x3308, 0x535: 0x3308, 0x536: 0x3308, 0x537: 0x3308, 0x538: 0x3308, 0x539: 0x3308, 0x53a: 0x3308, 0x53b: 0x3308, 0x53c: 0x3308, 0x53d: 0x3308, 0x53e: 0x3308, 0x53f: 0x3308, // Block 0x15, offset 0x540 0x540: 0x0c08, 0x541: 0x0a08, 0x542: 0x0a08, 0x543: 0x0a08, 0x544: 0x0a08, 0x545: 0x0a08, 0x546: 0x0c08, 0x547: 0x0c08, 0x548: 0x0a08, 0x549: 0x0c08, 0x54a: 0x0a08, 0x54b: 0x0a08, 0x54c: 0x0a08, 0x54d: 0x0a08, 0x54e: 0x0a08, 0x54f: 0x0a08, 0x550: 0x0a08, 0x551: 0x0a08, 0x552: 0x0a08, 0x553: 0x0a08, 0x554: 0x0c08, 0x555: 0x0a08, 0x556: 0x0808, 0x557: 0x0808, 0x558: 0x0808, 0x559: 0x3308, 0x55a: 0x3308, 0x55b: 0x3308, 0x55c: 0x0040, 0x55d: 0x0040, 0x55e: 0x0818, 0x55f: 0x0040, 0x560: 0x0a08, 0x561: 0x0808, 0x562: 0x0a08, 0x563: 0x0a08, 0x564: 0x0a08, 0x565: 0x0a08, 0x566: 0x0808, 0x567: 0x0c08, 0x568: 0x0a08, 0x569: 0x0c08, 0x56a: 0x0c08, 0x56b: 0x0040, 0x56c: 0x0040, 0x56d: 0x0040, 0x56e: 0x0040, 0x56f: 0x0040, 0x570: 0x0040, 0x571: 0x0040, 0x572: 0x0040, 0x573: 0x0040, 0x574: 0x0040, 0x575: 0x0040, 0x576: 0x0040, 0x577: 0x0040, 0x578: 0x0040, 0x579: 0x0040, 0x57a: 0x0040, 0x57b: 0x0040, 0x57c: 0x0040, 0x57d: 0x0040, 0x57e: 0x0040, 0x57f: 0x0040, // Block 0x16, offset 0x580 0x580: 0x3008, 0x581: 0x3308, 0x582: 0x3308, 0x583: 0x3308, 0x584: 0x3308, 0x585: 0x3308, 0x586: 0x3308, 0x587: 0x3308, 0x588: 0x3308, 0x589: 0x3008, 0x58a: 0x3008, 0x58b: 0x3008, 0x58c: 0x3008, 0x58d: 0x3b08, 0x58e: 0x3008, 0x58f: 0x3008, 0x590: 0x0008, 0x591: 0x3308, 0x592: 0x3308, 0x593: 0x3308, 0x594: 0x3308, 0x595: 0x3308, 0x596: 0x3308, 0x597: 0x3308, 0x598: 0x04c9, 0x599: 0x0501, 0x59a: 0x0539, 0x59b: 0x0571, 0x59c: 0x05a9, 0x59d: 0x05e1, 0x59e: 0x0619, 0x59f: 0x0651, 0x5a0: 0x0008, 0x5a1: 0x0008, 0x5a2: 0x3308, 0x5a3: 0x3308, 0x5a4: 0x0018, 0x5a5: 0x0018, 0x5a6: 0x0008, 0x5a7: 0x0008, 0x5a8: 0x0008, 0x5a9: 0x0008, 0x5aa: 0x0008, 0x5ab: 0x0008, 0x5ac: 0x0008, 0x5ad: 0x0008, 0x5ae: 0x0008, 0x5af: 0x0008, 0x5b0: 0x0018, 0x5b1: 0x0008, 0x5b2: 0x0008, 0x5b3: 0x0008, 0x5b4: 0x0008, 0x5b5: 0x0008, 0x5b6: 0x0008, 0x5b7: 0x0008, 0x5b8: 0x0008, 0x5b9: 0x0008, 0x5ba: 0x0008, 0x5bb: 0x0008, 0x5bc: 0x0008, 0x5bd: 0x0008, 0x5be: 0x0008, 0x5bf: 0x0008, // Block 0x17, offset 0x5c0 0x5c0: 0x0008, 0x5c1: 0x3308, 0x5c2: 0x3008, 0x5c3: 0x3008, 0x5c4: 0x0040, 0x5c5: 0x0008, 0x5c6: 0x0008, 0x5c7: 0x0008, 0x5c8: 0x0008, 0x5c9: 0x0008, 0x5ca: 0x0008, 0x5cb: 0x0008, 0x5cc: 0x0008, 0x5cd: 0x0040, 0x5ce: 0x0040, 0x5cf: 0x0008, 0x5d0: 0x0008, 0x5d1: 0x0040, 0x5d2: 0x0040, 0x5d3: 0x0008, 0x5d4: 0x0008, 0x5d5: 0x0008, 0x5d6: 0x0008, 0x5d7: 0x0008, 0x5d8: 0x0008, 0x5d9: 0x0008, 0x5da: 0x0008, 0x5db: 0x0008, 0x5dc: 0x0008, 0x5dd: 0x0008, 0x5de: 0x0008, 0x5df: 0x0008, 0x5e0: 0x0008, 0x5e1: 0x0008, 0x5e2: 0x0008, 0x5e3: 0x0008, 0x5e4: 0x0008, 0x5e5: 0x0008, 0x5e6: 0x0008, 0x5e7: 0x0008, 0x5e8: 0x0008, 0x5e9: 0x0040, 0x5ea: 0x0008, 0x5eb: 0x0008, 0x5ec: 0x0008, 0x5ed: 0x0008, 0x5ee: 0x0008, 0x5ef: 0x0008, 0x5f0: 0x0008, 0x5f1: 0x0040, 0x5f2: 0x0008, 0x5f3: 0x0040, 0x5f4: 0x0040, 0x5f5: 0x0040, 0x5f6: 0x0008, 0x5f7: 0x0008, 0x5f8: 0x0008, 0x5f9: 0x0008, 0x5fa: 0x0040, 0x5fb: 0x0040, 0x5fc: 0x3308, 0x5fd: 0x0008, 0x5fe: 0x3008, 0x5ff: 0x3008, // Block 0x18, offset 0x600 0x600: 0x3008, 0x601: 0x3308, 0x602: 0x3308, 0x603: 0x3308, 0x604: 0x3308, 0x605: 0x0040, 0x606: 0x0040, 0x607: 0x3008, 0x608: 0x3008, 0x609: 0x0040, 0x60a: 0x0040, 0x60b: 0x3008, 0x60c: 0x3008, 0x60d: 0x3b08, 0x60e: 0x0008, 0x60f: 0x0040, 0x610: 0x0040, 0x611: 0x0040, 0x612: 0x0040, 0x613: 0x0040, 0x614: 0x0040, 0x615: 0x0040, 0x616: 0x0040, 0x617: 0x3008, 0x618: 0x0040, 0x619: 0x0040, 0x61a: 0x0040, 0x61b: 0x0040, 0x61c: 0x0689, 0x61d: 0x06c1, 0x61e: 0x0040, 0x61f: 0x06f9, 0x620: 0x0008, 0x621: 0x0008, 0x622: 0x3308, 0x623: 0x3308, 0x624: 0x0040, 0x625: 0x0040, 0x626: 0x0008, 0x627: 0x0008, 0x628: 0x0008, 0x629: 0x0008, 0x62a: 0x0008, 0x62b: 0x0008, 0x62c: 0x0008, 0x62d: 0x0008, 0x62e: 0x0008, 0x62f: 0x0008, 0x630: 0x0008, 0x631: 0x0008, 0x632: 0x0018, 0x633: 0x0018, 0x634: 0x0018, 0x635: 0x0018, 0x636: 0x0018, 0x637: 0x0018, 0x638: 0x0018, 0x639: 0x0018, 0x63a: 0x0018, 0x63b: 0x0018, 0x63c: 0x0008, 0x63d: 0x0018, 0x63e: 0x3308, 0x63f: 0x0040, // Block 0x19, offset 0x640 0x640: 0x0040, 0x641: 0x3308, 0x642: 0x3308, 0x643: 0x3008, 0x644: 0x0040, 0x645: 0x0008, 0x646: 0x0008, 0x647: 0x0008, 0x648: 0x0008, 0x649: 0x0008, 0x64a: 0x0008, 0x64b: 0x0040, 0x64c: 0x0040, 0x64d: 0x0040, 0x64e: 0x0040, 0x64f: 0x0008, 0x650: 0x0008, 0x651: 0x0040, 0x652: 0x0040, 0x653: 0x0008, 0x654: 0x0008, 0x655: 0x0008, 0x656: 0x0008, 0x657: 0x0008, 0x658: 0x0008, 0x659: 0x0008, 0x65a: 0x0008, 0x65b: 0x0008, 0x65c: 0x0008, 0x65d: 0x0008, 0x65e: 0x0008, 0x65f: 0x0008, 0x660: 0x0008, 0x661: 0x0008, 0x662: 0x0008, 0x663: 0x0008, 0x664: 0x0008, 0x665: 0x0008, 0x666: 0x0008, 0x667: 0x0008, 0x668: 0x0008, 0x669: 0x0040, 0x66a: 0x0008, 0x66b: 0x0008, 0x66c: 0x0008, 0x66d: 0x0008, 0x66e: 0x0008, 0x66f: 0x0008, 0x670: 0x0008, 0x671: 0x0040, 0x672: 0x0008, 0x673: 0x0731, 0x674: 0x0040, 0x675: 0x0008, 0x676: 0x0769, 0x677: 0x0040, 0x678: 0x0008, 0x679: 0x0008, 0x67a: 0x0040, 0x67b: 0x0040, 0x67c: 0x3308, 0x67d: 0x0040, 0x67e: 0x3008, 0x67f: 0x3008, // Block 0x1a, offset 0x680 0x680: 0x3008, 0x681: 0x3308, 0x682: 0x3308, 0x683: 0x0040, 0x684: 0x0040, 0x685: 0x0040, 0x686: 0x0040, 0x687: 0x3308, 0x688: 0x3308, 0x689: 0x0040, 0x68a: 0x0040, 0x68b: 0x3308, 0x68c: 0x3308, 0x68d: 0x3b08, 0x68e: 0x0040, 0x68f: 0x0040, 0x690: 0x0040, 0x691: 0x3308, 0x692: 0x0040, 0x693: 0x0040, 0x694: 0x0040, 0x695: 0x0040, 0x696: 0x0040, 0x697: 0x0040, 0x698: 0x0040, 0x699: 0x07a1, 0x69a: 0x07d9, 0x69b: 0x0811, 0x69c: 0x0008, 0x69d: 0x0040, 0x69e: 0x0849, 0x69f: 0x0040, 0x6a0: 0x0040, 0x6a1: 0x0040, 0x6a2: 0x0040, 0x6a3: 0x0040, 0x6a4: 0x0040, 0x6a5: 0x0040, 0x6a6: 0x0008, 0x6a7: 0x0008, 0x6a8: 0x0008, 0x6a9: 0x0008, 0x6aa: 0x0008, 0x6ab: 0x0008, 0x6ac: 0x0008, 0x6ad: 0x0008, 0x6ae: 0x0008, 0x6af: 0x0008, 0x6b0: 0x3308, 0x6b1: 0x3308, 0x6b2: 0x0008, 0x6b3: 0x0008, 0x6b4: 0x0008, 0x6b5: 0x3308, 0x6b6: 0x0018, 0x6b7: 0x0040, 0x6b8: 0x0040, 0x6b9: 0x0040, 0x6ba: 0x0040, 0x6bb: 0x0040, 0x6bc: 0x0040, 0x6bd: 0x0040, 0x6be: 0x0040, 0x6bf: 0x0040, // Block 0x1b, offset 0x6c0 0x6c0: 0x0040, 0x6c1: 0x3308, 0x6c2: 0x3308, 0x6c3: 0x3008, 0x6c4: 0x0040, 0x6c5: 0x0008, 0x6c6: 0x0008, 0x6c7: 0x0008, 0x6c8: 0x0008, 0x6c9: 0x0008, 0x6ca: 0x0008, 0x6cb: 0x0008, 0x6cc: 0x0008, 0x6cd: 0x0008, 0x6ce: 0x0040, 0x6cf: 0x0008, 0x6d0: 0x0008, 0x6d1: 0x0008, 0x6d2: 0x0040, 0x6d3: 0x0008, 0x6d4: 0x0008, 0x6d5: 0x0008, 0x6d6: 0x0008, 0x6d7: 0x0008, 0x6d8: 0x0008, 0x6d9: 0x0008, 0x6da: 0x0008, 0x6db: 0x0008, 0x6dc: 0x0008, 0x6dd: 0x0008, 0x6de: 0x0008, 0x6df: 0x0008, 0x6e0: 0x0008, 0x6e1: 0x0008, 0x6e2: 0x0008, 0x6e3: 0x0008, 0x6e4: 0x0008, 0x6e5: 0x0008, 0x6e6: 0x0008, 0x6e7: 0x0008, 0x6e8: 0x0008, 0x6e9: 0x0040, 0x6ea: 0x0008, 0x6eb: 0x0008, 0x6ec: 0x0008, 0x6ed: 0x0008, 0x6ee: 0x0008, 0x6ef: 0x0008, 0x6f0: 0x0008, 0x6f1: 0x0040, 0x6f2: 0x0008, 0x6f3: 0x0008, 0x6f4: 0x0040, 0x6f5: 0x0008, 0x6f6: 0x0008, 0x6f7: 0x0008, 0x6f8: 0x0008, 0x6f9: 0x0008, 0x6fa: 0x0040, 0x6fb: 0x0040, 0x6fc: 0x3308, 0x6fd: 0x0008, 0x6fe: 0x3008, 0x6ff: 0x3008, // Block 0x1c, offset 0x700 0x700: 0x3008, 0x701: 0x3308, 0x702: 0x3308, 0x703: 0x3308, 0x704: 0x3308, 0x705: 0x3308, 0x706: 0x0040, 0x707: 0x3308, 0x708: 0x3308, 0x709: 0x3008, 0x70a: 0x0040, 0x70b: 0x3008, 0x70c: 0x3008, 0x70d: 0x3b08, 0x70e: 0x0040, 0x70f: 0x0040, 0x710: 0x0008, 0x711: 0x0040, 0x712: 0x0040, 0x713: 0x0040, 0x714: 0x0040, 0x715: 0x0040, 0x716: 0x0040, 0x717: 0x0040, 0x718: 0x0040, 0x719: 0x0040, 0x71a: 0x0040, 0x71b: 0x0040, 0x71c: 0x0040, 0x71d: 0x0040, 0x71e: 0x0040, 0x71f: 0x0040, 0x720: 0x0008, 0x721: 0x0008, 0x722: 0x3308, 0x723: 0x3308, 0x724: 0x0040, 0x725: 0x0040, 0x726: 0x0008, 0x727: 0x0008, 0x728: 0x0008, 0x729: 0x0008, 0x72a: 0x0008, 0x72b: 0x0008, 0x72c: 0x0008, 0x72d: 0x0008, 0x72e: 0x0008, 0x72f: 0x0008, 0x730: 0x0018, 0x731: 0x0018, 0x732: 0x0040, 0x733: 0x0040, 0x734: 0x0040, 0x735: 0x0040, 0x736: 0x0040, 0x737: 0x0040, 0x738: 0x0040, 0x739: 0x0008, 0x73a: 0x3308, 0x73b: 0x3308, 0x73c: 0x3308, 0x73d: 0x3308, 0x73e: 0x3308, 0x73f: 0x3308, // Block 0x1d, offset 0x740 0x740: 0x0040, 0x741: 0x3308, 0x742: 0x3008, 0x743: 0x3008, 0x744: 0x0040, 0x745: 0x0008, 0x746: 0x0008, 0x747: 0x0008, 0x748: 0x0008, 0x749: 0x0008, 0x74a: 0x0008, 0x74b: 0x0008, 0x74c: 0x0008, 0x74d: 0x0040, 0x74e: 0x0040, 0x74f: 0x0008, 0x750: 0x0008, 0x751: 0x0040, 0x752: 0x0040, 0x753: 0x0008, 0x754: 0x0008, 0x755: 0x0008, 0x756: 0x0008, 0x757: 0x0008, 0x758: 0x0008, 0x759: 0x0008, 0x75a: 0x0008, 0x75b: 0x0008, 0x75c: 0x0008, 0x75d: 0x0008, 0x75e: 0x0008, 0x75f: 0x0008, 0x760: 0x0008, 0x761: 0x0008, 0x762: 0x0008, 0x763: 0x0008, 0x764: 0x0008, 0x765: 0x0008, 0x766: 0x0008, 0x767: 0x0008, 0x768: 0x0008, 0x769: 0x0040, 0x76a: 0x0008, 0x76b: 0x0008, 0x76c: 0x0008, 0x76d: 0x0008, 0x76e: 0x0008, 0x76f: 0x0008, 0x770: 0x0008, 0x771: 0x0040, 0x772: 0x0008, 0x773: 0x0008, 0x774: 0x0040, 0x775: 0x0008, 0x776: 0x0008, 0x777: 0x0008, 0x778: 0x0008, 0x779: 0x0008, 0x77a: 0x0040, 0x77b: 0x0040, 0x77c: 0x3308, 0x77d: 0x0008, 0x77e: 0x3008, 0x77f: 0x3308, // Block 0x1e, offset 0x780 0x780: 0x3008, 0x781: 0x3308, 0x782: 0x3308, 0x783: 0x3308, 0x784: 0x3308, 0x785: 0x0040, 0x786: 0x0040, 0x787: 0x3008, 0x788: 0x3008, 0x789: 0x0040, 0x78a: 0x0040, 0x78b: 0x3008, 0x78c: 0x3008, 0x78d: 0x3b08, 0x78e: 0x0040, 0x78f: 0x0040, 0x790: 0x0040, 0x791: 0x0040, 0x792: 0x0040, 0x793: 0x0040, 0x794: 0x0040, 0x795: 0x0040, 0x796: 0x3308, 0x797: 0x3008, 0x798: 0x0040, 0x799: 0x0040, 0x79a: 0x0040, 0x79b: 0x0040, 0x79c: 0x0881, 0x79d: 0x08b9, 0x79e: 0x0040, 0x79f: 0x0008, 0x7a0: 0x0008, 0x7a1: 0x0008, 0x7a2: 0x3308, 0x7a3: 0x3308, 0x7a4: 0x0040, 0x7a5: 0x0040, 0x7a6: 0x0008, 0x7a7: 0x0008, 0x7a8: 0x0008, 0x7a9: 0x0008, 0x7aa: 0x0008, 0x7ab: 0x0008, 0x7ac: 0x0008, 0x7ad: 0x0008, 0x7ae: 0x0008, 0x7af: 0x0008, 0x7b0: 0x0018, 0x7b1: 0x0008, 0x7b2: 0x0018, 0x7b3: 0x0018, 0x7b4: 0x0018, 0x7b5: 0x0018, 0x7b6: 0x0018, 0x7b7: 0x0018, 0x7b8: 0x0040, 0x7b9: 0x0040, 0x7ba: 0x0040, 0x7bb: 0x0040, 0x7bc: 0x0040, 0x7bd: 0x0040, 0x7be: 0x0040, 0x7bf: 0x0040, // Block 0x1f, offset 0x7c0 0x7c0: 0x0040, 0x7c1: 0x0040, 0x7c2: 0x3308, 0x7c3: 0x0008, 0x7c4: 0x0040, 0x7c5: 0x0008, 0x7c6: 0x0008, 0x7c7: 0x0008, 0x7c8: 0x0008, 0x7c9: 0x0008, 0x7ca: 0x0008, 0x7cb: 0x0040, 0x7cc: 0x0040, 0x7cd: 0x0040, 0x7ce: 0x0008, 0x7cf: 0x0008, 0x7d0: 0x0008, 0x7d1: 0x0040, 0x7d2: 0x0008, 0x7d3: 0x0008, 0x7d4: 0x0008, 0x7d5: 0x0008, 0x7d6: 0x0040, 0x7d7: 0x0040, 0x7d8: 0x0040, 0x7d9: 0x0008, 0x7da: 0x0008, 0x7db: 0x0040, 0x7dc: 0x0008, 0x7dd: 0x0040, 0x7de: 0x0008, 0x7df: 0x0008, 0x7e0: 0x0040, 0x7e1: 0x0040, 0x7e2: 0x0040, 0x7e3: 0x0008, 0x7e4: 0x0008, 0x7e5: 0x0040, 0x7e6: 0x0040, 0x7e7: 0x0040, 0x7e8: 0x0008, 0x7e9: 0x0008, 0x7ea: 0x0008, 0x7eb: 0x0040, 0x7ec: 0x0040, 0x7ed: 0x0040, 0x7ee: 0x0008, 0x7ef: 0x0008, 0x7f0: 0x0008, 0x7f1: 0x0008, 0x7f2: 0x0008, 0x7f3: 0x0008, 0x7f4: 0x0008, 0x7f5: 0x0008, 0x7f6: 0x0008, 0x7f7: 0x0008, 0x7f8: 0x0008, 0x7f9: 0x0008, 0x7fa: 0x0040, 0x7fb: 0x0040, 0x7fc: 0x0040, 0x7fd: 0x0040, 0x7fe: 0x3008, 0x7ff: 0x3008, // Block 0x20, offset 0x800 0x800: 0x3308, 0x801: 0x3008, 0x802: 0x3008, 0x803: 0x3008, 0x804: 0x3008, 0x805: 0x0040, 0x806: 0x3308, 0x807: 0x3308, 0x808: 0x3308, 0x809: 0x0040, 0x80a: 0x3308, 0x80b: 0x3308, 0x80c: 0x3308, 0x80d: 0x3b08, 0x80e: 0x0040, 0x80f: 0x0040, 0x810: 0x0040, 0x811: 0x0040, 0x812: 0x0040, 0x813: 0x0040, 0x814: 0x0040, 0x815: 0x3308, 0x816: 0x3308, 0x817: 0x0040, 0x818: 0x0008, 0x819: 0x0008, 0x81a: 0x0008, 0x81b: 0x0040, 0x81c: 0x0040, 0x81d: 0x0040, 0x81e: 0x0040, 0x81f: 0x0040, 0x820: 0x0008, 0x821: 0x0008, 0x822: 0x3308, 0x823: 0x3308, 0x824: 0x0040, 0x825: 0x0040, 0x826: 0x0008, 0x827: 0x0008, 0x828: 0x0008, 0x829: 0x0008, 0x82a: 0x0008, 0x82b: 0x0008, 0x82c: 0x0008, 0x82d: 0x0008, 0x82e: 0x0008, 0x82f: 0x0008, 0x830: 0x0040, 0x831: 0x0040, 0x832: 0x0040, 0x833: 0x0040, 0x834: 0x0040, 0x835: 0x0040, 0x836: 0x0040, 0x837: 0x0040, 0x838: 0x0018, 0x839: 0x0018, 0x83a: 0x0018, 0x83b: 0x0018, 0x83c: 0x0018, 0x83d: 0x0018, 0x83e: 0x0018, 0x83f: 0x0018, // Block 0x21, offset 0x840 0x840: 0x0008, 0x841: 0x3308, 0x842: 0x3008, 0x843: 0x3008, 0x844: 0x0018, 0x845: 0x0008, 0x846: 0x0008, 0x847: 0x0008, 0x848: 0x0008, 0x849: 0x0008, 0x84a: 0x0008, 0x84b: 0x0008, 0x84c: 0x0008, 0x84d: 0x0040, 0x84e: 0x0008, 0x84f: 0x0008, 0x850: 0x0008, 0x851: 0x0040, 0x852: 0x0008, 0x853: 0x0008, 0x854: 0x0008, 0x855: 0x0008, 0x856: 0x0008, 0x857: 0x0008, 0x858: 0x0008, 0x859: 0x0008, 0x85a: 0x0008, 0x85b: 0x0008, 0x85c: 0x0008, 0x85d: 0x0008, 0x85e: 0x0008, 0x85f: 0x0008, 0x860: 0x0008, 0x861: 0x0008, 0x862: 0x0008, 0x863: 0x0008, 0x864: 0x0008, 0x865: 0x0008, 0x866: 0x0008, 0x867: 0x0008, 0x868: 0x0008, 0x869: 0x0040, 0x86a: 0x0008, 0x86b: 0x0008, 0x86c: 0x0008, 0x86d: 0x0008, 0x86e: 0x0008, 0x86f: 0x0008, 0x870: 0x0008, 0x871: 0x0008, 0x872: 0x0008, 0x873: 0x0008, 0x874: 0x0040, 0x875: 0x0008, 0x876: 0x0008, 0x877: 0x0008, 0x878: 0x0008, 0x879: 0x0008, 0x87a: 0x0040, 0x87b: 0x0040, 0x87c: 0x3308, 0x87d: 0x0008, 0x87e: 0x3008, 0x87f: 0x3308, // Block 0x22, offset 0x880 0x880: 0x3008, 0x881: 0x3008, 0x882: 0x3008, 0x883: 0x3008, 0x884: 0x3008, 0x885: 0x0040, 0x886: 0x3308, 0x887: 0x3008, 0x888: 0x3008, 0x889: 0x0040, 0x88a: 0x3008, 0x88b: 0x3008, 0x88c: 0x3308, 0x88d: 0x3b08, 0x88e: 0x0040, 0x88f: 0x0040, 0x890: 0x0040, 0x891: 0x0040, 0x892: 0x0040, 0x893: 0x0040, 0x894: 0x0040, 0x895: 0x3008, 0x896: 0x3008, 0x897: 0x0040, 0x898: 0x0040, 0x899: 0x0040, 0x89a: 0x0040, 0x89b: 0x0040, 0x89c: 0x0040, 0x89d: 0x0040, 0x89e: 0x0008, 0x89f: 0x0040, 0x8a0: 0x0008, 0x8a1: 0x0008, 0x8a2: 0x3308, 0x8a3: 0x3308, 0x8a4: 0x0040, 0x8a5: 0x0040, 0x8a6: 0x0008, 0x8a7: 0x0008, 0x8a8: 0x0008, 0x8a9: 0x0008, 0x8aa: 0x0008, 0x8ab: 0x0008, 0x8ac: 0x0008, 0x8ad: 0x0008, 0x8ae: 0x0008, 0x8af: 0x0008, 0x8b0: 0x0040, 0x8b1: 0x0008, 0x8b2: 0x0008, 0x8b3: 0x0040, 0x8b4: 0x0040, 0x8b5: 0x0040, 0x8b6: 0x0040, 0x8b7: 0x0040, 0x8b8: 0x0040, 0x8b9: 0x0040, 0x8ba: 0x0040, 0x8bb: 0x0040, 0x8bc: 0x0040, 0x8bd: 0x0040, 0x8be: 0x0040, 0x8bf: 0x0040, // Block 0x23, offset 0x8c0 0x8c0: 0x3008, 0x8c1: 0x3308, 0x8c2: 0x3308, 0x8c3: 0x3308, 0x8c4: 0x3308, 0x8c5: 0x0040, 0x8c6: 0x3008, 0x8c7: 0x3008, 0x8c8: 0x3008, 0x8c9: 0x0040, 0x8ca: 0x3008, 0x8cb: 0x3008, 0x8cc: 0x3008, 0x8cd: 0x3b08, 0x8ce: 0x0008, 0x8cf: 0x0018, 0x8d0: 0x0040, 0x8d1: 0x0040, 0x8d2: 0x0040, 0x8d3: 0x0040, 0x8d4: 0x0008, 0x8d5: 0x0008, 0x8d6: 0x0008, 0x8d7: 0x3008, 0x8d8: 0x0018, 0x8d9: 0x0018, 0x8da: 0x0018, 0x8db: 0x0018, 0x8dc: 0x0018, 0x8dd: 0x0018, 0x8de: 0x0018, 0x8df: 0x0008, 0x8e0: 0x0008, 0x8e1: 0x0008, 0x8e2: 0x3308, 0x8e3: 0x3308, 0x8e4: 0x0040, 0x8e5: 0x0040, 0x8e6: 0x0008, 0x8e7: 0x0008, 0x8e8: 0x0008, 0x8e9: 0x0008, 0x8ea: 0x0008, 0x8eb: 0x0008, 0x8ec: 0x0008, 0x8ed: 0x0008, 0x8ee: 0x0008, 0x8ef: 0x0008, 0x8f0: 0x0018, 0x8f1: 0x0018, 0x8f2: 0x0018, 0x8f3: 0x0018, 0x8f4: 0x0018, 0x8f5: 0x0018, 0x8f6: 0x0018, 0x8f7: 0x0018, 0x8f8: 0x0018, 0x8f9: 0x0018, 0x8fa: 0x0008, 0x8fb: 0x0008, 0x8fc: 0x0008, 0x8fd: 0x0008, 0x8fe: 0x0008, 0x8ff: 0x0008, // Block 0x24, offset 0x900 0x900: 0x0040, 0x901: 0x0008, 0x902: 0x0008, 0x903: 0x0040, 0x904: 0x0008, 0x905: 0x0040, 0x906: 0x0040, 0x907: 0x0008, 0x908: 0x0008, 0x909: 0x0040, 0x90a: 0x0008, 0x90b: 0x0040, 0x90c: 0x0040, 0x90d: 0x0008, 0x90e: 0x0040, 0x90f: 0x0040, 0x910: 0x0040, 0x911: 0x0040, 0x912: 0x0040, 0x913: 0x0040, 0x914: 0x0008, 0x915: 0x0008, 0x916: 0x0008, 0x917: 0x0008, 0x918: 0x0040, 0x919: 0x0008, 0x91a: 0x0008, 0x91b: 0x0008, 0x91c: 0x0008, 0x91d: 0x0008, 0x91e: 0x0008, 0x91f: 0x0008, 0x920: 0x0040, 0x921: 0x0008, 0x922: 0x0008, 0x923: 0x0008, 0x924: 0x0040, 0x925: 0x0008, 0x926: 0x0040, 0x927: 0x0008, 0x928: 0x0040, 0x929: 0x0040, 0x92a: 0x0008, 0x92b: 0x0008, 0x92c: 0x0040, 0x92d: 0x0008, 0x92e: 0x0008, 0x92f: 0x0008, 0x930: 0x0008, 0x931: 0x3308, 0x932: 0x0008, 0x933: 0x0929, 0x934: 0x3308, 0x935: 0x3308, 0x936: 0x3308, 0x937: 0x3308, 0x938: 0x3308, 0x939: 0x3308, 0x93a: 0x0040, 0x93b: 0x3308, 0x93c: 0x3308, 0x93d: 0x0008, 0x93e: 0x0040, 0x93f: 0x0040, // Block 0x25, offset 0x940 0x940: 0x0008, 0x941: 0x0008, 0x942: 0x0008, 0x943: 0x09d1, 0x944: 0x0008, 0x945: 0x0008, 0x946: 0x0008, 0x947: 0x0008, 0x948: 0x0040, 0x949: 0x0008, 0x94a: 0x0008, 0x94b: 0x0008, 0x94c: 0x0008, 0x94d: 0x0a09, 0x94e: 0x0008, 0x94f: 0x0008, 0x950: 0x0008, 0x951: 0x0008, 0x952: 0x0a41, 0x953: 0x0008, 0x954: 0x0008, 0x955: 0x0008, 0x956: 0x0008, 0x957: 0x0a79, 0x958: 0x0008, 0x959: 0x0008, 0x95a: 0x0008, 0x95b: 0x0008, 0x95c: 0x0ab1, 0x95d: 0x0008, 0x95e: 0x0008, 0x95f: 0x0008, 0x960: 0x0008, 0x961: 0x0008, 0x962: 0x0008, 0x963: 0x0008, 0x964: 0x0008, 0x965: 0x0008, 0x966: 0x0008, 0x967: 0x0008, 0x968: 0x0008, 0x969: 0x0ae9, 0x96a: 0x0008, 0x96b: 0x0008, 0x96c: 0x0008, 0x96d: 0x0040, 0x96e: 0x0040, 0x96f: 0x0040, 0x970: 0x0040, 0x971: 0x3308, 0x972: 0x3308, 0x973: 0x0b21, 0x974: 0x3308, 0x975: 0x0b59, 0x976: 0x0b91, 0x977: 0x0bc9, 0x978: 0x0c19, 0x979: 0x0c51, 0x97a: 0x3308, 0x97b: 0x3308, 0x97c: 0x3308, 0x97d: 0x3308, 0x97e: 0x3308, 0x97f: 0x3008, // Block 0x26, offset 0x980 0x980: 0x3308, 0x981: 0x0ca1, 0x982: 0x3308, 0x983: 0x3308, 0x984: 0x3b08, 0x985: 0x0018, 0x986: 0x3308, 0x987: 0x3308, 0x988: 0x0008, 0x989: 0x0008, 0x98a: 0x0008, 0x98b: 0x0008, 0x98c: 0x0008, 0x98d: 0x3308, 0x98e: 0x3308, 0x98f: 0x3308, 0x990: 0x3308, 0x991: 0x3308, 0x992: 0x3308, 0x993: 0x0cd9, 0x994: 0x3308, 0x995: 0x3308, 0x996: 0x3308, 0x997: 0x3308, 0x998: 0x0040, 0x999: 0x3308, 0x99a: 0x3308, 0x99b: 0x3308, 0x99c: 0x3308, 0x99d: 0x0d11, 0x99e: 0x3308, 0x99f: 0x3308, 0x9a0: 0x3308, 0x9a1: 0x3308, 0x9a2: 0x0d49, 0x9a3: 0x3308, 0x9a4: 0x3308, 0x9a5: 0x3308, 0x9a6: 0x3308, 0x9a7: 0x0d81, 0x9a8: 0x3308, 0x9a9: 0x3308, 0x9aa: 0x3308, 0x9ab: 0x3308, 0x9ac: 0x0db9, 0x9ad: 0x3308, 0x9ae: 0x3308, 0x9af: 0x3308, 0x9b0: 0x3308, 0x9b1: 0x3308, 0x9b2: 0x3308, 0x9b3: 0x3308, 0x9b4: 0x3308, 0x9b5: 0x3308, 0x9b6: 0x3308, 0x9b7: 0x3308, 0x9b8: 0x3308, 0x9b9: 0x0df1, 0x9ba: 0x3308, 0x9bb: 0x3308, 0x9bc: 0x3308, 0x9bd: 0x0040, 0x9be: 0x0018, 0x9bf: 0x0018, // Block 0x27, offset 0x9c0 0x9c0: 0x0008, 0x9c1: 0x0008, 0x9c2: 0x0008, 0x9c3: 0x0008, 0x9c4: 0x0008, 0x9c5: 0x0008, 0x9c6: 0x0008, 0x9c7: 0x0008, 0x9c8: 0x0008, 0x9c9: 0x0008, 0x9ca: 0x0008, 0x9cb: 0x0008, 0x9cc: 0x0008, 0x9cd: 0x0008, 0x9ce: 0x0008, 0x9cf: 0x0008, 0x9d0: 0x0008, 0x9d1: 0x0008, 0x9d2: 0x0008, 0x9d3: 0x0008, 0x9d4: 0x0008, 0x9d5: 0x0008, 0x9d6: 0x0008, 0x9d7: 0x0008, 0x9d8: 0x0008, 0x9d9: 0x0008, 0x9da: 0x0008, 0x9db: 0x0008, 0x9dc: 0x0008, 0x9dd: 0x0008, 0x9de: 0x0008, 0x9df: 0x0008, 0x9e0: 0x0008, 0x9e1: 0x0008, 0x9e2: 0x0008, 0x9e3: 0x0008, 0x9e4: 0x0008, 0x9e5: 0x0008, 0x9e6: 0x0008, 0x9e7: 0x0008, 0x9e8: 0x0008, 0x9e9: 0x0008, 0x9ea: 0x0008, 0x9eb: 0x0008, 0x9ec: 0x0039, 0x9ed: 0x0ed1, 0x9ee: 0x0ee9, 0x9ef: 0x0008, 0x9f0: 0x0ef9, 0x9f1: 0x0f09, 0x9f2: 0x0f19, 0x9f3: 0x0f31, 0x9f4: 0x0249, 0x9f5: 0x0f41, 0x9f6: 0x0259, 0x9f7: 0x0f51, 0x9f8: 0x0359, 0x9f9: 0x0f61, 0x9fa: 0x0f71, 0x9fb: 0x0008, 0x9fc: 0x00d9, 0x9fd: 0x0f81, 0x9fe: 0x0f99, 0x9ff: 0x0269, // Block 0x28, offset 0xa00 0xa00: 0x0fa9, 0xa01: 0x0fb9, 0xa02: 0x0279, 0xa03: 0x0039, 0xa04: 0x0fc9, 0xa05: 0x0fe1, 0xa06: 0x059d, 0xa07: 0x0ee9, 0xa08: 0x0ef9, 0xa09: 0x0f09, 0xa0a: 0x0ff9, 0xa0b: 0x1011, 0xa0c: 0x1029, 0xa0d: 0x0f31, 0xa0e: 0x0008, 0xa0f: 0x0f51, 0xa10: 0x0f61, 0xa11: 0x1041, 0xa12: 0x00d9, 0xa13: 0x1059, 0xa14: 0x05b5, 0xa15: 0x05b5, 0xa16: 0x0f99, 0xa17: 0x0fa9, 0xa18: 0x0fb9, 0xa19: 0x059d, 0xa1a: 0x1071, 0xa1b: 0x1089, 0xa1c: 0x05cd, 0xa1d: 0x1099, 0xa1e: 0x10b1, 0xa1f: 0x10c9, 0xa20: 0x10e1, 0xa21: 0x10f9, 0xa22: 0x0f41, 0xa23: 0x0269, 0xa24: 0x0fb9, 0xa25: 0x1089, 0xa26: 0x1099, 0xa27: 0x10b1, 0xa28: 0x1111, 0xa29: 0x10e1, 0xa2a: 0x10f9, 0xa2b: 0x0008, 0xa2c: 0x0008, 0xa2d: 0x0008, 0xa2e: 0x0008, 0xa2f: 0x0008, 0xa30: 0x0008, 0xa31: 0x0008, 0xa32: 0x0008, 0xa33: 0x0008, 0xa34: 0x0008, 0xa35: 0x0008, 0xa36: 0x0008, 0xa37: 0x0008, 0xa38: 0x1129, 0xa39: 0x0008, 0xa3a: 0x0008, 0xa3b: 0x0008, 0xa3c: 0x0008, 0xa3d: 0x0008, 0xa3e: 0x0008, 0xa3f: 0x0008, // Block 0x29, offset 0xa40 0xa40: 0x0008, 0xa41: 0x0008, 0xa42: 0x0008, 0xa43: 0x0008, 0xa44: 0x0008, 0xa45: 0x0008, 0xa46: 0x0008, 0xa47: 0x0008, 0xa48: 0x0008, 0xa49: 0x0008, 0xa4a: 0x0008, 0xa4b: 0x0008, 0xa4c: 0x0008, 0xa4d: 0x0008, 0xa4e: 0x0008, 0xa4f: 0x0008, 0xa50: 0x0008, 0xa51: 0x0008, 0xa52: 0x0008, 0xa53: 0x0008, 0xa54: 0x0008, 0xa55: 0x0008, 0xa56: 0x0008, 0xa57: 0x0008, 0xa58: 0x0008, 0xa59: 0x0008, 0xa5a: 0x0008, 0xa5b: 0x1141, 0xa5c: 0x1159, 0xa5d: 0x1169, 0xa5e: 0x1181, 0xa5f: 0x1029, 0xa60: 0x1199, 0xa61: 0x11a9, 0xa62: 0x11c1, 0xa63: 0x11d9, 0xa64: 0x11f1, 0xa65: 0x1209, 0xa66: 0x1221, 0xa67: 0x05e5, 0xa68: 0x1239, 0xa69: 0x1251, 0xa6a: 0xe17d, 0xa6b: 0x1269, 0xa6c: 0x1281, 0xa6d: 0x1299, 0xa6e: 0x12b1, 0xa6f: 0x12c9, 0xa70: 0x12e1, 0xa71: 0x12f9, 0xa72: 0x1311, 0xa73: 0x1329, 0xa74: 0x1341, 0xa75: 0x1359, 0xa76: 0x1371, 0xa77: 0x1389, 0xa78: 0x05fd, 0xa79: 0x13a1, 0xa7a: 0x13b9, 0xa7b: 0x13d1, 0xa7c: 0x13e1, 0xa7d: 0x13f9, 0xa7e: 0x1411, 0xa7f: 0x1429, // Block 0x2a, offset 0xa80 0xa80: 0xe00d, 0xa81: 0x0008, 0xa82: 0xe00d, 0xa83: 0x0008, 0xa84: 0xe00d, 0xa85: 0x0008, 0xa86: 0xe00d, 0xa87: 0x0008, 0xa88: 0xe00d, 0xa89: 0x0008, 0xa8a: 0xe00d, 0xa8b: 0x0008, 0xa8c: 0xe00d, 0xa8d: 0x0008, 0xa8e: 0xe00d, 0xa8f: 0x0008, 0xa90: 0xe00d, 0xa91: 0x0008, 0xa92: 0xe00d, 0xa93: 0x0008, 0xa94: 0xe00d, 0xa95: 0x0008, 0xa96: 0xe00d, 0xa97: 0x0008, 0xa98: 0xe00d, 0xa99: 0x0008, 0xa9a: 0xe00d, 0xa9b: 0x0008, 0xa9c: 0xe00d, 0xa9d: 0x0008, 0xa9e: 0xe00d, 0xa9f: 0x0008, 0xaa0: 0xe00d, 0xaa1: 0x0008, 0xaa2: 0xe00d, 0xaa3: 0x0008, 0xaa4: 0xe00d, 0xaa5: 0x0008, 0xaa6: 0xe00d, 0xaa7: 0x0008, 0xaa8: 0xe00d, 0xaa9: 0x0008, 0xaaa: 0xe00d, 0xaab: 0x0008, 0xaac: 0xe00d, 0xaad: 0x0008, 0xaae: 0xe00d, 0xaaf: 0x0008, 0xab0: 0xe00d, 0xab1: 0x0008, 0xab2: 0xe00d, 0xab3: 0x0008, 0xab4: 0xe00d, 0xab5: 0x0008, 0xab6: 0xe00d, 0xab7: 0x0008, 0xab8: 0xe00d, 0xab9: 0x0008, 0xaba: 0xe00d, 0xabb: 0x0008, 0xabc: 0xe00d, 0xabd: 0x0008, 0xabe: 0xe00d, 0xabf: 0x0008, // Block 0x2b, offset 0xac0 0xac0: 0xe00d, 0xac1: 0x0008, 0xac2: 0xe00d, 0xac3: 0x0008, 0xac4: 0xe00d, 0xac5: 0x0008, 0xac6: 0xe00d, 0xac7: 0x0008, 0xac8: 0xe00d, 0xac9: 0x0008, 0xaca: 0xe00d, 0xacb: 0x0008, 0xacc: 0xe00d, 0xacd: 0x0008, 0xace: 0xe00d, 0xacf: 0x0008, 0xad0: 0xe00d, 0xad1: 0x0008, 0xad2: 0xe00d, 0xad3: 0x0008, 0xad4: 0xe00d, 0xad5: 0x0008, 0xad6: 0x0008, 0xad7: 0x0008, 0xad8: 0x0008, 0xad9: 0x0008, 0xada: 0x0615, 0xadb: 0x0635, 0xadc: 0x0008, 0xadd: 0x0008, 0xade: 0x1441, 0xadf: 0x0008, 0xae0: 0xe00d, 0xae1: 0x0008, 0xae2: 0xe00d, 0xae3: 0x0008, 0xae4: 0xe00d, 0xae5: 0x0008, 0xae6: 0xe00d, 0xae7: 0x0008, 0xae8: 0xe00d, 0xae9: 0x0008, 0xaea: 0xe00d, 0xaeb: 0x0008, 0xaec: 0xe00d, 0xaed: 0x0008, 0xaee: 0xe00d, 0xaef: 0x0008, 0xaf0: 0xe00d, 0xaf1: 0x0008, 0xaf2: 0xe00d, 0xaf3: 0x0008, 0xaf4: 0xe00d, 0xaf5: 0x0008, 0xaf6: 0xe00d, 0xaf7: 0x0008, 0xaf8: 0xe00d, 0xaf9: 0x0008, 0xafa: 0xe00d, 0xafb: 0x0008, 0xafc: 0xe00d, 0xafd: 0x0008, 0xafe: 0xe00d, 0xaff: 0x0008, // Block 0x2c, offset 0xb00 0xb00: 0x0008, 0xb01: 0x0008, 0xb02: 0x0008, 0xb03: 0x0008, 0xb04: 0x0008, 0xb05: 0x0008, 0xb06: 0x0040, 0xb07: 0x0040, 0xb08: 0xe045, 0xb09: 0xe045, 0xb0a: 0xe045, 0xb0b: 0xe045, 0xb0c: 0xe045, 0xb0d: 0xe045, 0xb0e: 0x0040, 0xb0f: 0x0040, 0xb10: 0x0008, 0xb11: 0x0008, 0xb12: 0x0008, 0xb13: 0x0008, 0xb14: 0x0008, 0xb15: 0x0008, 0xb16: 0x0008, 0xb17: 0x0008, 0xb18: 0x0040, 0xb19: 0xe045, 0xb1a: 0x0040, 0xb1b: 0xe045, 0xb1c: 0x0040, 0xb1d: 0xe045, 0xb1e: 0x0040, 0xb1f: 0xe045, 0xb20: 0x0008, 0xb21: 0x0008, 0xb22: 0x0008, 0xb23: 0x0008, 0xb24: 0x0008, 0xb25: 0x0008, 0xb26: 0x0008, 0xb27: 0x0008, 0xb28: 0xe045, 0xb29: 0xe045, 0xb2a: 0xe045, 0xb2b: 0xe045, 0xb2c: 0xe045, 0xb2d: 0xe045, 0xb2e: 0xe045, 0xb2f: 0xe045, 0xb30: 0x0008, 0xb31: 0x1459, 0xb32: 0x0008, 0xb33: 0x1471, 0xb34: 0x0008, 0xb35: 0x1489, 0xb36: 0x0008, 0xb37: 0x14a1, 0xb38: 0x0008, 0xb39: 0x14b9, 0xb3a: 0x0008, 0xb3b: 0x14d1, 0xb3c: 0x0008, 0xb3d: 0x14e9, 0xb3e: 0x0040, 0xb3f: 0x0040, // Block 0x2d, offset 0xb40 0xb40: 0x1501, 0xb41: 0x1531, 0xb42: 0x1561, 0xb43: 0x1591, 0xb44: 0x15c1, 0xb45: 0x15f1, 0xb46: 0x1621, 0xb47: 0x1651, 0xb48: 0x1501, 0xb49: 0x1531, 0xb4a: 0x1561, 0xb4b: 0x1591, 0xb4c: 0x15c1, 0xb4d: 0x15f1, 0xb4e: 0x1621, 0xb4f: 0x1651, 0xb50: 0x1681, 0xb51: 0x16b1, 0xb52: 0x16e1, 0xb53: 0x1711, 0xb54: 0x1741, 0xb55: 0x1771, 0xb56: 0x17a1, 0xb57: 0x17d1, 0xb58: 0x1681, 0xb59: 0x16b1, 0xb5a: 0x16e1, 0xb5b: 0x1711, 0xb5c: 0x1741, 0xb5d: 0x1771, 0xb5e: 0x17a1, 0xb5f: 0x17d1, 0xb60: 0x1801, 0xb61: 0x1831, 0xb62: 0x1861, 0xb63: 0x1891, 0xb64: 0x18c1, 0xb65: 0x18f1, 0xb66: 0x1921, 0xb67: 0x1951, 0xb68: 0x1801, 0xb69: 0x1831, 0xb6a: 0x1861, 0xb6b: 0x1891, 0xb6c: 0x18c1, 0xb6d: 0x18f1, 0xb6e: 0x1921, 0xb6f: 0x1951, 0xb70: 0x0008, 0xb71: 0x0008, 0xb72: 0x1981, 0xb73: 0x19b1, 0xb74: 0x19d9, 0xb75: 0x0040, 0xb76: 0x0008, 0xb77: 0x1a01, 0xb78: 0xe045, 0xb79: 0xe045, 0xb7a: 0x064d, 0xb7b: 0x1459, 0xb7c: 0x19b1, 0xb7d: 0x0666, 0xb7e: 0x1a31, 0xb7f: 0x0686, // Block 0x2e, offset 0xb80 0xb80: 0x06a6, 0xb81: 0x1a4a, 0xb82: 0x1a79, 0xb83: 0x1aa9, 0xb84: 0x1ad1, 0xb85: 0x0040, 0xb86: 0x0008, 0xb87: 0x1af9, 0xb88: 0x06c5, 0xb89: 0x1471, 0xb8a: 0x06dd, 0xb8b: 0x1489, 0xb8c: 0x1aa9, 0xb8d: 0x1b2a, 0xb8e: 0x1b5a, 0xb8f: 0x1b8a, 0xb90: 0x0008, 0xb91: 0x0008, 0xb92: 0x0008, 0xb93: 0x1bb9, 0xb94: 0x0040, 0xb95: 0x0040, 0xb96: 0x0008, 0xb97: 0x0008, 0xb98: 0xe045, 0xb99: 0xe045, 0xb9a: 0x06f5, 0xb9b: 0x14a1, 0xb9c: 0x0040, 0xb9d: 0x1bd2, 0xb9e: 0x1c02, 0xb9f: 0x1c32, 0xba0: 0x0008, 0xba1: 0x0008, 0xba2: 0x0008, 0xba3: 0x1c61, 0xba4: 0x0008, 0xba5: 0x0008, 0xba6: 0x0008, 0xba7: 0x0008, 0xba8: 0xe045, 0xba9: 0xe045, 0xbaa: 0x070d, 0xbab: 0x14d1, 0xbac: 0xe04d, 0xbad: 0x1c7a, 0xbae: 0x03d2, 0xbaf: 0x1caa, 0xbb0: 0x0040, 0xbb1: 0x0040, 0xbb2: 0x1cb9, 0xbb3: 0x1ce9, 0xbb4: 0x1d11, 0xbb5: 0x0040, 0xbb6: 0x0008, 0xbb7: 0x1d39, 0xbb8: 0x0725, 0xbb9: 0x14b9, 0xbba: 0x0515, 0xbbb: 0x14e9, 0xbbc: 0x1ce9, 0xbbd: 0x073e, 0xbbe: 0x075e, 0xbbf: 0x0040, // Block 0x2f, offset 0xbc0 0xbc0: 0x000a, 0xbc1: 0x000a, 0xbc2: 0x000a, 0xbc3: 0x000a, 0xbc4: 0x000a, 0xbc5: 0x000a, 0xbc6: 0x000a, 0xbc7: 0x000a, 0xbc8: 0x000a, 0xbc9: 0x000a, 0xbca: 0x000a, 0xbcb: 0x03c0, 0xbcc: 0x0003, 0xbcd: 0x0003, 0xbce: 0x0340, 0xbcf: 0x0b40, 0xbd0: 0x0018, 0xbd1: 0xe00d, 0xbd2: 0x0018, 0xbd3: 0x0018, 0xbd4: 0x0018, 0xbd5: 0x0018, 0xbd6: 0x0018, 0xbd7: 0x077e, 0xbd8: 0x0018, 0xbd9: 0x0018, 0xbda: 0x0018, 0xbdb: 0x0018, 0xbdc: 0x0018, 0xbdd: 0x0018, 0xbde: 0x0018, 0xbdf: 0x0018, 0xbe0: 0x0018, 0xbe1: 0x0018, 0xbe2: 0x0018, 0xbe3: 0x0018, 0xbe4: 0x0040, 0xbe5: 0x0040, 0xbe6: 0x0040, 0xbe7: 0x0018, 0xbe8: 0x0040, 0xbe9: 0x0040, 0xbea: 0x0340, 0xbeb: 0x0340, 0xbec: 0x0340, 0xbed: 0x0340, 0xbee: 0x0340, 0xbef: 0x000a, 0xbf0: 0x0018, 0xbf1: 0x0018, 0xbf2: 0x0018, 0xbf3: 0x1d69, 0xbf4: 0x1da1, 0xbf5: 0x0018, 0xbf6: 0x1df1, 0xbf7: 0x1e29, 0xbf8: 0x0018, 0xbf9: 0x0018, 0xbfa: 0x0018, 0xbfb: 0x0018, 0xbfc: 0x1e7a, 0xbfd: 0x0018, 0xbfe: 0x079e, 0xbff: 0x0018, // Block 0x30, offset 0xc00 0xc00: 0x0018, 0xc01: 0x0018, 0xc02: 0x0018, 0xc03: 0x0018, 0xc04: 0x0018, 0xc05: 0x0018, 0xc06: 0x0018, 0xc07: 0x1e92, 0xc08: 0x1eaa, 0xc09: 0x1ec2, 0xc0a: 0x0018, 0xc0b: 0x0018, 0xc0c: 0x0018, 0xc0d: 0x0018, 0xc0e: 0x0018, 0xc0f: 0x0018, 0xc10: 0x0018, 0xc11: 0x0018, 0xc12: 0x0018, 0xc13: 0x0018, 0xc14: 0x0018, 0xc15: 0x0018, 0xc16: 0x0018, 0xc17: 0x1ed9, 0xc18: 0x0018, 0xc19: 0x0018, 0xc1a: 0x0018, 0xc1b: 0x0018, 0xc1c: 0x0018, 0xc1d: 0x0018, 0xc1e: 0x0018, 0xc1f: 0x000a, 0xc20: 0x03c0, 0xc21: 0x0340, 0xc22: 0x0340, 0xc23: 0x0340, 0xc24: 0x03c0, 0xc25: 0x0040, 0xc26: 0x0040, 0xc27: 0x0040, 0xc28: 0x0040, 0xc29: 0x0040, 0xc2a: 0x0340, 0xc2b: 0x0340, 0xc2c: 0x0340, 0xc2d: 0x0340, 0xc2e: 0x0340, 0xc2f: 0x0340, 0xc30: 0x1f41, 0xc31: 0x0f41, 0xc32: 0x0040, 0xc33: 0x0040, 0xc34: 0x1f51, 0xc35: 0x1f61, 0xc36: 0x1f71, 0xc37: 0x1f81, 0xc38: 0x1f91, 0xc39: 0x1fa1, 0xc3a: 0x1fb2, 0xc3b: 0x07bd, 0xc3c: 0x1fc2, 0xc3d: 0x1fd2, 0xc3e: 0x1fe2, 0xc3f: 0x0f71, // Block 0x31, offset 0xc40 0xc40: 0x1f41, 0xc41: 0x00c9, 0xc42: 0x0069, 0xc43: 0x0079, 0xc44: 0x1f51, 0xc45: 0x1f61, 0xc46: 0x1f71, 0xc47: 0x1f81, 0xc48: 0x1f91, 0xc49: 0x1fa1, 0xc4a: 0x1fb2, 0xc4b: 0x07d5, 0xc4c: 0x1fc2, 0xc4d: 0x1fd2, 0xc4e: 0x1fe2, 0xc4f: 0x0040, 0xc50: 0x0039, 0xc51: 0x0f09, 0xc52: 0x00d9, 0xc53: 0x0369, 0xc54: 0x0ff9, 0xc55: 0x0249, 0xc56: 0x0f51, 0xc57: 0x0359, 0xc58: 0x0f61, 0xc59: 0x0f71, 0xc5a: 0x0f99, 0xc5b: 0x01d9, 0xc5c: 0x0fa9, 0xc5d: 0x0040, 0xc5e: 0x0040, 0xc5f: 0x0040, 0xc60: 0x0018, 0xc61: 0x0018, 0xc62: 0x0018, 0xc63: 0x0018, 0xc64: 0x0018, 0xc65: 0x0018, 0xc66: 0x0018, 0xc67: 0x0018, 0xc68: 0x1ff1, 0xc69: 0x0018, 0xc6a: 0x0018, 0xc6b: 0x0018, 0xc6c: 0x0018, 0xc6d: 0x0018, 0xc6e: 0x0018, 0xc6f: 0x0018, 0xc70: 0x0018, 0xc71: 0x0018, 0xc72: 0x0018, 0xc73: 0x0018, 0xc74: 0x0018, 0xc75: 0x0018, 0xc76: 0x0018, 0xc77: 0x0018, 0xc78: 0x0018, 0xc79: 0x0018, 0xc7a: 0x0018, 0xc7b: 0x0018, 0xc7c: 0x0018, 0xc7d: 0x0018, 0xc7e: 0x0018, 0xc7f: 0x0018, // Block 0x32, offset 0xc80 0xc80: 0x07ee, 0xc81: 0x080e, 0xc82: 0x1159, 0xc83: 0x082d, 0xc84: 0x0018, 0xc85: 0x084e, 0xc86: 0x086e, 0xc87: 0x1011, 0xc88: 0x0018, 0xc89: 0x088d, 0xc8a: 0x0f31, 0xc8b: 0x0249, 0xc8c: 0x0249, 0xc8d: 0x0249, 0xc8e: 0x0249, 0xc8f: 0x2009, 0xc90: 0x0f41, 0xc91: 0x0f41, 0xc92: 0x0359, 0xc93: 0x0359, 0xc94: 0x0018, 0xc95: 0x0f71, 0xc96: 0x2021, 0xc97: 0x0018, 0xc98: 0x0018, 0xc99: 0x0f99, 0xc9a: 0x2039, 0xc9b: 0x0269, 0xc9c: 0x0269, 0xc9d: 0x0269, 0xc9e: 0x0018, 0xc9f: 0x0018, 0xca0: 0x2049, 0xca1: 0x08ad, 0xca2: 0x2061, 0xca3: 0x0018, 0xca4: 0x13d1, 0xca5: 0x0018, 0xca6: 0x2079, 0xca7: 0x0018, 0xca8: 0x13d1, 0xca9: 0x0018, 0xcaa: 0x0f51, 0xcab: 0x2091, 0xcac: 0x0ee9, 0xcad: 0x1159, 0xcae: 0x0018, 0xcaf: 0x0f09, 0xcb0: 0x0f09, 0xcb1: 0x1199, 0xcb2: 0x0040, 0xcb3: 0x0f61, 0xcb4: 0x00d9, 0xcb5: 0x20a9, 0xcb6: 0x20c1, 0xcb7: 0x20d9, 0xcb8: 0x20f1, 0xcb9: 0x0f41, 0xcba: 0x0018, 0xcbb: 0x08cd, 0xcbc: 0x2109, 0xcbd: 0x10b1, 0xcbe: 0x10b1, 0xcbf: 0x2109, // Block 0x33, offset 0xcc0 0xcc0: 0x08ed, 0xcc1: 0x0018, 0xcc2: 0x0018, 0xcc3: 0x0018, 0xcc4: 0x0018, 0xcc5: 0x0ef9, 0xcc6: 0x0ef9, 0xcc7: 0x0f09, 0xcc8: 0x0f41, 0xcc9: 0x0259, 0xcca: 0x0018, 0xccb: 0x0018, 0xccc: 0x0018, 0xccd: 0x0018, 0xcce: 0x0008, 0xccf: 0x0018, 0xcd0: 0x2121, 0xcd1: 0x2151, 0xcd2: 0x2181, 0xcd3: 0x21b9, 0xcd4: 0x21e9, 0xcd5: 0x2219, 0xcd6: 0x2249, 0xcd7: 0x2279, 0xcd8: 0x22a9, 0xcd9: 0x22d9, 0xcda: 0x2309, 0xcdb: 0x2339, 0xcdc: 0x2369, 0xcdd: 0x2399, 0xcde: 0x23c9, 0xcdf: 0x23f9, 0xce0: 0x0f41, 0xce1: 0x2421, 0xce2: 0x0905, 0xce3: 0x2439, 0xce4: 0x1089, 0xce5: 0x2451, 0xce6: 0x0925, 0xce7: 0x2469, 0xce8: 0x2491, 0xce9: 0x0369, 0xcea: 0x24a9, 0xceb: 0x0945, 0xcec: 0x0359, 0xced: 0x1159, 0xcee: 0x0ef9, 0xcef: 0x0f61, 0xcf0: 0x0f41, 0xcf1: 0x2421, 0xcf2: 0x0965, 0xcf3: 0x2439, 0xcf4: 0x1089, 0xcf5: 0x2451, 0xcf6: 0x0985, 0xcf7: 0x2469, 0xcf8: 0x2491, 0xcf9: 0x0369, 0xcfa: 0x24a9, 0xcfb: 0x09a5, 0xcfc: 0x0359, 0xcfd: 0x1159, 0xcfe: 0x0ef9, 0xcff: 0x0f61, // Block 0x34, offset 0xd00 0xd00: 0x0018, 0xd01: 0x0018, 0xd02: 0x0018, 0xd03: 0x0018, 0xd04: 0x0018, 0xd05: 0x0018, 0xd06: 0x0018, 0xd07: 0x0018, 0xd08: 0x0018, 0xd09: 0x0018, 0xd0a: 0x0018, 0xd0b: 0x0040, 0xd0c: 0x0040, 0xd0d: 0x0040, 0xd0e: 0x0040, 0xd0f: 0x0040, 0xd10: 0x0040, 0xd11: 0x0040, 0xd12: 0x0040, 0xd13: 0x0040, 0xd14: 0x0040, 0xd15: 0x0040, 0xd16: 0x0040, 0xd17: 0x0040, 0xd18: 0x0040, 0xd19: 0x0040, 0xd1a: 0x0040, 0xd1b: 0x0040, 0xd1c: 0x0040, 0xd1d: 0x0040, 0xd1e: 0x0040, 0xd1f: 0x0040, 0xd20: 0x00c9, 0xd21: 0x0069, 0xd22: 0x0079, 0xd23: 0x1f51, 0xd24: 0x1f61, 0xd25: 0x1f71, 0xd26: 0x1f81, 0xd27: 0x1f91, 0xd28: 0x1fa1, 0xd29: 0x2601, 0xd2a: 0x2619, 0xd2b: 0x2631, 0xd2c: 0x2649, 0xd2d: 0x2661, 0xd2e: 0x2679, 0xd2f: 0x2691, 0xd30: 0x26a9, 0xd31: 0x26c1, 0xd32: 0x26d9, 0xd33: 0x26f1, 0xd34: 0x0a06, 0xd35: 0x0a26, 0xd36: 0x0a46, 0xd37: 0x0a66, 0xd38: 0x0a86, 0xd39: 0x0aa6, 0xd3a: 0x0ac6, 0xd3b: 0x0ae6, 0xd3c: 0x0b06, 0xd3d: 0x270a, 0xd3e: 0x2732, 0xd3f: 0x275a, // Block 0x35, offset 0xd40 0xd40: 0x2782, 0xd41: 0x27aa, 0xd42: 0x27d2, 0xd43: 0x27fa, 0xd44: 0x2822, 0xd45: 0x284a, 0xd46: 0x2872, 0xd47: 0x289a, 0xd48: 0x0040, 0xd49: 0x0040, 0xd4a: 0x0040, 0xd4b: 0x0040, 0xd4c: 0x0040, 0xd4d: 0x0040, 0xd4e: 0x0040, 0xd4f: 0x0040, 0xd50: 0x0040, 0xd51: 0x0040, 0xd52: 0x0040, 0xd53: 0x0040, 0xd54: 0x0040, 0xd55: 0x0040, 0xd56: 0x0040, 0xd57: 0x0040, 0xd58: 0x0040, 0xd59: 0x0040, 0xd5a: 0x0040, 0xd5b: 0x0040, 0xd5c: 0x0b26, 0xd5d: 0x0b46, 0xd5e: 0x0b66, 0xd5f: 0x0b86, 0xd60: 0x0ba6, 0xd61: 0x0bc6, 0xd62: 0x0be6, 0xd63: 0x0c06, 0xd64: 0x0c26, 0xd65: 0x0c46, 0xd66: 0x0c66, 0xd67: 0x0c86, 0xd68: 0x0ca6, 0xd69: 0x0cc6, 0xd6a: 0x0ce6, 0xd6b: 0x0d06, 0xd6c: 0x0d26, 0xd6d: 0x0d46, 0xd6e: 0x0d66, 0xd6f: 0x0d86, 0xd70: 0x0da6, 0xd71: 0x0dc6, 0xd72: 0x0de6, 0xd73: 0x0e06, 0xd74: 0x0e26, 0xd75: 0x0e46, 0xd76: 0x0039, 0xd77: 0x0ee9, 0xd78: 0x1159, 0xd79: 0x0ef9, 0xd7a: 0x0f09, 0xd7b: 0x1199, 0xd7c: 0x0f31, 0xd7d: 0x0249, 0xd7e: 0x0f41, 0xd7f: 0x0259, // Block 0x36, offset 0xd80 0xd80: 0x0f51, 0xd81: 0x0359, 0xd82: 0x0f61, 0xd83: 0x0f71, 0xd84: 0x00d9, 0xd85: 0x0f99, 0xd86: 0x2039, 0xd87: 0x0269, 0xd88: 0x01d9, 0xd89: 0x0fa9, 0xd8a: 0x0fb9, 0xd8b: 0x1089, 0xd8c: 0x0279, 0xd8d: 0x0369, 0xd8e: 0x0289, 0xd8f: 0x13d1, 0xd90: 0x0039, 0xd91: 0x0ee9, 0xd92: 0x1159, 0xd93: 0x0ef9, 0xd94: 0x0f09, 0xd95: 0x1199, 0xd96: 0x0f31, 0xd97: 0x0249, 0xd98: 0x0f41, 0xd99: 0x0259, 0xd9a: 0x0f51, 0xd9b: 0x0359, 0xd9c: 0x0f61, 0xd9d: 0x0f71, 0xd9e: 0x00d9, 0xd9f: 0x0f99, 0xda0: 0x2039, 0xda1: 0x0269, 0xda2: 0x01d9, 0xda3: 0x0fa9, 0xda4: 0x0fb9, 0xda5: 0x1089, 0xda6: 0x0279, 0xda7: 0x0369, 0xda8: 0x0289, 0xda9: 0x13d1, 0xdaa: 0x1f41, 0xdab: 0x0018, 0xdac: 0x0018, 0xdad: 0x0018, 0xdae: 0x0018, 0xdaf: 0x0018, 0xdb0: 0x0018, 0xdb1: 0x0018, 0xdb2: 0x0018, 0xdb3: 0x0018, 0xdb4: 0x0018, 0xdb5: 0x0018, 0xdb6: 0x0018, 0xdb7: 0x0018, 0xdb8: 0x0018, 0xdb9: 0x0018, 0xdba: 0x0018, 0xdbb: 0x0018, 0xdbc: 0x0018, 0xdbd: 0x0018, 0xdbe: 0x0018, 0xdbf: 0x0018, // Block 0x37, offset 0xdc0 0xdc0: 0x0008, 0xdc1: 0x0008, 0xdc2: 0x0008, 0xdc3: 0x0008, 0xdc4: 0x0008, 0xdc5: 0x0008, 0xdc6: 0x0008, 0xdc7: 0x0008, 0xdc8: 0x0008, 0xdc9: 0x0008, 0xdca: 0x0008, 0xdcb: 0x0008, 0xdcc: 0x0008, 0xdcd: 0x0008, 0xdce: 0x0008, 0xdcf: 0x0008, 0xdd0: 0x0008, 0xdd1: 0x0008, 0xdd2: 0x0008, 0xdd3: 0x0008, 0xdd4: 0x0008, 0xdd5: 0x0008, 0xdd6: 0x0008, 0xdd7: 0x0008, 0xdd8: 0x0008, 0xdd9: 0x0008, 0xdda: 0x0008, 0xddb: 0x0008, 0xddc: 0x0008, 0xddd: 0x0008, 0xdde: 0x0008, 0xddf: 0x0040, 0xde0: 0xe00d, 0xde1: 0x0008, 0xde2: 0x2971, 0xde3: 0x0ebd, 0xde4: 0x2989, 0xde5: 0x0008, 0xde6: 0x0008, 0xde7: 0xe07d, 0xde8: 0x0008, 0xde9: 0xe01d, 0xdea: 0x0008, 0xdeb: 0xe03d, 0xdec: 0x0008, 0xded: 0x0fe1, 0xdee: 0x1281, 0xdef: 0x0fc9, 0xdf0: 0x1141, 0xdf1: 0x0008, 0xdf2: 0xe00d, 0xdf3: 0x0008, 0xdf4: 0x0008, 0xdf5: 0xe01d, 0xdf6: 0x0008, 0xdf7: 0x0008, 0xdf8: 0x0008, 0xdf9: 0x0008, 0xdfa: 0x0008, 0xdfb: 0x0008, 0xdfc: 0x0259, 0xdfd: 0x1089, 0xdfe: 0x29a1, 0xdff: 0x29b9, // Block 0x38, offset 0xe00 0xe00: 0xe00d, 0xe01: 0x0008, 0xe02: 0xe00d, 0xe03: 0x0008, 0xe04: 0xe00d, 0xe05: 0x0008, 0xe06: 0xe00d, 0xe07: 0x0008, 0xe08: 0xe00d, 0xe09: 0x0008, 0xe0a: 0xe00d, 0xe0b: 0x0008, 0xe0c: 0xe00d, 0xe0d: 0x0008, 0xe0e: 0xe00d, 0xe0f: 0x0008, 0xe10: 0xe00d, 0xe11: 0x0008, 0xe12: 0xe00d, 0xe13: 0x0008, 0xe14: 0xe00d, 0xe15: 0x0008, 0xe16: 0xe00d, 0xe17: 0x0008, 0xe18: 0xe00d, 0xe19: 0x0008, 0xe1a: 0xe00d, 0xe1b: 0x0008, 0xe1c: 0xe00d, 0xe1d: 0x0008, 0xe1e: 0xe00d, 0xe1f: 0x0008, 0xe20: 0xe00d, 0xe21: 0x0008, 0xe22: 0xe00d, 0xe23: 0x0008, 0xe24: 0x0008, 0xe25: 0x0018, 0xe26: 0x0018, 0xe27: 0x0018, 0xe28: 0x0018, 0xe29: 0x0018, 0xe2a: 0x0018, 0xe2b: 0xe03d, 0xe2c: 0x0008, 0xe2d: 0xe01d, 0xe2e: 0x0008, 0xe2f: 0x3308, 0xe30: 0x3308, 0xe31: 0x3308, 0xe32: 0xe00d, 0xe33: 0x0008, 0xe34: 0x0040, 0xe35: 0x0040, 0xe36: 0x0040, 0xe37: 0x0040, 0xe38: 0x0040, 0xe39: 0x0018, 0xe3a: 0x0018, 0xe3b: 0x0018, 0xe3c: 0x0018, 0xe3d: 0x0018, 0xe3e: 0x0018, 0xe3f: 0x0018, // Block 0x39, offset 0xe40 0xe40: 0x26fd, 0xe41: 0x271d, 0xe42: 0x273d, 0xe43: 0x275d, 0xe44: 0x277d, 0xe45: 0x279d, 0xe46: 0x27bd, 0xe47: 0x27dd, 0xe48: 0x27fd, 0xe49: 0x281d, 0xe4a: 0x283d, 0xe4b: 0x285d, 0xe4c: 0x287d, 0xe4d: 0x289d, 0xe4e: 0x28bd, 0xe4f: 0x28dd, 0xe50: 0x28fd, 0xe51: 0x291d, 0xe52: 0x293d, 0xe53: 0x295d, 0xe54: 0x297d, 0xe55: 0x299d, 0xe56: 0x0040, 0xe57: 0x0040, 0xe58: 0x0040, 0xe59: 0x0040, 0xe5a: 0x0040, 0xe5b: 0x0040, 0xe5c: 0x0040, 0xe5d: 0x0040, 0xe5e: 0x0040, 0xe5f: 0x0040, 0xe60: 0x0040, 0xe61: 0x0040, 0xe62: 0x0040, 0xe63: 0x0040, 0xe64: 0x0040, 0xe65: 0x0040, 0xe66: 0x0040, 0xe67: 0x0040, 0xe68: 0x0040, 0xe69: 0x0040, 0xe6a: 0x0040, 0xe6b: 0x0040, 0xe6c: 0x0040, 0xe6d: 0x0040, 0xe6e: 0x0040, 0xe6f: 0x0040, 0xe70: 0x0040, 0xe71: 0x0040, 0xe72: 0x0040, 0xe73: 0x0040, 0xe74: 0x0040, 0xe75: 0x0040, 0xe76: 0x0040, 0xe77: 0x0040, 0xe78: 0x0040, 0xe79: 0x0040, 0xe7a: 0x0040, 0xe7b: 0x0040, 0xe7c: 0x0040, 0xe7d: 0x0040, 0xe7e: 0x0040, 0xe7f: 0x0040, // Block 0x3a, offset 0xe80 0xe80: 0x000a, 0xe81: 0x0018, 0xe82: 0x29d1, 0xe83: 0x0018, 0xe84: 0x0018, 0xe85: 0x0008, 0xe86: 0x0008, 0xe87: 0x0008, 0xe88: 0x0018, 0xe89: 0x0018, 0xe8a: 0x0018, 0xe8b: 0x0018, 0xe8c: 0x0018, 0xe8d: 0x0018, 0xe8e: 0x0018, 0xe8f: 0x0018, 0xe90: 0x0018, 0xe91: 0x0018, 0xe92: 0x0018, 0xe93: 0x0018, 0xe94: 0x0018, 0xe95: 0x0018, 0xe96: 0x0018, 0xe97: 0x0018, 0xe98: 0x0018, 0xe99: 0x0018, 0xe9a: 0x0018, 0xe9b: 0x0018, 0xe9c: 0x0018, 0xe9d: 0x0018, 0xe9e: 0x0018, 0xe9f: 0x0018, 0xea0: 0x0018, 0xea1: 0x0018, 0xea2: 0x0018, 0xea3: 0x0018, 0xea4: 0x0018, 0xea5: 0x0018, 0xea6: 0x0018, 0xea7: 0x0018, 0xea8: 0x0018, 0xea9: 0x0018, 0xeaa: 0x3308, 0xeab: 0x3308, 0xeac: 0x3308, 0xead: 0x3308, 0xeae: 0x3018, 0xeaf: 0x3018, 0xeb0: 0x0018, 0xeb1: 0x0018, 0xeb2: 0x0018, 0xeb3: 0x0018, 0xeb4: 0x0018, 0xeb5: 0x0018, 0xeb6: 0xe125, 0xeb7: 0x0018, 0xeb8: 0x29bd, 0xeb9: 0x29dd, 0xeba: 0x29fd, 0xebb: 0x0018, 0xebc: 0x0008, 0xebd: 0x0018, 0xebe: 0x0018, 0xebf: 0x0018, // Block 0x3b, offset 0xec0 0xec0: 0x2b3d, 0xec1: 0x2b5d, 0xec2: 0x2b7d, 0xec3: 0x2b9d, 0xec4: 0x2bbd, 0xec5: 0x2bdd, 0xec6: 0x2bdd, 0xec7: 0x2bdd, 0xec8: 0x2bfd, 0xec9: 0x2bfd, 0xeca: 0x2bfd, 0xecb: 0x2bfd, 0xecc: 0x2c1d, 0xecd: 0x2c1d, 0xece: 0x2c1d, 0xecf: 0x2c3d, 0xed0: 0x2c5d, 0xed1: 0x2c5d, 0xed2: 0x2a7d, 0xed3: 0x2a7d, 0xed4: 0x2c5d, 0xed5: 0x2c5d, 0xed6: 0x2c7d, 0xed7: 0x2c7d, 0xed8: 0x2c5d, 0xed9: 0x2c5d, 0xeda: 0x2a7d, 0xedb: 0x2a7d, 0xedc: 0x2c5d, 0xedd: 0x2c5d, 0xede: 0x2c3d, 0xedf: 0x2c3d, 0xee0: 0x2c9d, 0xee1: 0x2c9d, 0xee2: 0x2cbd, 0xee3: 0x2cbd, 0xee4: 0x0040, 0xee5: 0x2cdd, 0xee6: 0x2cfd, 0xee7: 0x2d1d, 0xee8: 0x2d1d, 0xee9: 0x2d3d, 0xeea: 0x2d5d, 0xeeb: 0x2d7d, 0xeec: 0x2d9d, 0xeed: 0x2dbd, 0xeee: 0x2ddd, 0xeef: 0x2dfd, 0xef0: 0x2e1d, 0xef1: 0x2e3d, 0xef2: 0x2e3d, 0xef3: 0x2e5d, 0xef4: 0x2e7d, 0xef5: 0x2e7d, 0xef6: 0x2e9d, 0xef7: 0x2ebd, 0xef8: 0x2e5d, 0xef9: 0x2edd, 0xefa: 0x2efd, 0xefb: 0x2edd, 0xefc: 0x2e5d, 0xefd: 0x2f1d, 0xefe: 0x2f3d, 0xeff: 0x2f5d, // Block 0x3c, offset 0xf00 0xf00: 0x2f7d, 0xf01: 0x2f9d, 0xf02: 0x2cfd, 0xf03: 0x2cdd, 0xf04: 0x2fbd, 0xf05: 0x2fdd, 0xf06: 0x2ffd, 0xf07: 0x301d, 0xf08: 0x303d, 0xf09: 0x305d, 0xf0a: 0x307d, 0xf0b: 0x309d, 0xf0c: 0x30bd, 0xf0d: 0x30dd, 0xf0e: 0x30fd, 0xf0f: 0x0040, 0xf10: 0x0018, 0xf11: 0x0018, 0xf12: 0x311d, 0xf13: 0x313d, 0xf14: 0x315d, 0xf15: 0x317d, 0xf16: 0x319d, 0xf17: 0x31bd, 0xf18: 0x31dd, 0xf19: 0x31fd, 0xf1a: 0x321d, 0xf1b: 0x323d, 0xf1c: 0x315d, 0xf1d: 0x325d, 0xf1e: 0x327d, 0xf1f: 0x329d, 0xf20: 0x0008, 0xf21: 0x0008, 0xf22: 0x0008, 0xf23: 0x0008, 0xf24: 0x0008, 0xf25: 0x0008, 0xf26: 0x0008, 0xf27: 0x0008, 0xf28: 0x0008, 0xf29: 0x0008, 0xf2a: 0x0008, 0xf2b: 0x0008, 0xf2c: 0x0008, 0xf2d: 0x0008, 0xf2e: 0x0008, 0xf2f: 0x0008, 0xf30: 0x0008, 0xf31: 0x0008, 0xf32: 0x0008, 0xf33: 0x0008, 0xf34: 0x0008, 0xf35: 0x0008, 0xf36: 0x0008, 0xf37: 0x0008, 0xf38: 0x0008, 0xf39: 0x0008, 0xf3a: 0x0008, 0xf3b: 0x0040, 0xf3c: 0x0040, 0xf3d: 0x0040, 0xf3e: 0x0040, 0xf3f: 0x0040, // Block 0x3d, offset 0xf40 0xf40: 0x36a2, 0xf41: 0x36d2, 0xf42: 0x3702, 0xf43: 0x3732, 0xf44: 0x32bd, 0xf45: 0x32dd, 0xf46: 0x32fd, 0xf47: 0x331d, 0xf48: 0x0018, 0xf49: 0x0018, 0xf4a: 0x0018, 0xf4b: 0x0018, 0xf4c: 0x0018, 0xf4d: 0x0018, 0xf4e: 0x0018, 0xf4f: 0x0018, 0xf50: 0x333d, 0xf51: 0x3761, 0xf52: 0x3779, 0xf53: 0x3791, 0xf54: 0x37a9, 0xf55: 0x37c1, 0xf56: 0x37d9, 0xf57: 0x37f1, 0xf58: 0x3809, 0xf59: 0x3821, 0xf5a: 0x3839, 0xf5b: 0x3851, 0xf5c: 0x3869, 0xf5d: 0x3881, 0xf5e: 0x3899, 0xf5f: 0x38b1, 0xf60: 0x335d, 0xf61: 0x337d, 0xf62: 0x339d, 0xf63: 0x33bd, 0xf64: 0x33dd, 0xf65: 0x33dd, 0xf66: 0x33fd, 0xf67: 0x341d, 0xf68: 0x343d, 0xf69: 0x345d, 0xf6a: 0x347d, 0xf6b: 0x349d, 0xf6c: 0x34bd, 0xf6d: 0x34dd, 0xf6e: 0x34fd, 0xf6f: 0x351d, 0xf70: 0x353d, 0xf71: 0x355d, 0xf72: 0x357d, 0xf73: 0x359d, 0xf74: 0x35bd, 0xf75: 0x35dd, 0xf76: 0x35fd, 0xf77: 0x361d, 0xf78: 0x363d, 0xf79: 0x365d, 0xf7a: 0x367d, 0xf7b: 0x369d, 0xf7c: 0x38c9, 0xf7d: 0x3901, 0xf7e: 0x36bd, 0xf7f: 0x0018, // Block 0x3e, offset 0xf80 0xf80: 0x36dd, 0xf81: 0x36fd, 0xf82: 0x371d, 0xf83: 0x373d, 0xf84: 0x375d, 0xf85: 0x377d, 0xf86: 0x379d, 0xf87: 0x37bd, 0xf88: 0x37dd, 0xf89: 0x37fd, 0xf8a: 0x381d, 0xf8b: 0x383d, 0xf8c: 0x385d, 0xf8d: 0x387d, 0xf8e: 0x389d, 0xf8f: 0x38bd, 0xf90: 0x38dd, 0xf91: 0x38fd, 0xf92: 0x391d, 0xf93: 0x393d, 0xf94: 0x395d, 0xf95: 0x397d, 0xf96: 0x399d, 0xf97: 0x39bd, 0xf98: 0x39dd, 0xf99: 0x39fd, 0xf9a: 0x3a1d, 0xf9b: 0x3a3d, 0xf9c: 0x3a5d, 0xf9d: 0x3a7d, 0xf9e: 0x3a9d, 0xf9f: 0x3abd, 0xfa0: 0x3add, 0xfa1: 0x3afd, 0xfa2: 0x3b1d, 0xfa3: 0x3b3d, 0xfa4: 0x3b5d, 0xfa5: 0x3b7d, 0xfa6: 0x127d, 0xfa7: 0x3b9d, 0xfa8: 0x3bbd, 0xfa9: 0x3bdd, 0xfaa: 0x3bfd, 0xfab: 0x3c1d, 0xfac: 0x3c3d, 0xfad: 0x3c5d, 0xfae: 0x239d, 0xfaf: 0x3c7d, 0xfb0: 0x3c9d, 0xfb1: 0x3939, 0xfb2: 0x3951, 0xfb3: 0x3969, 0xfb4: 0x3981, 0xfb5: 0x3999, 0xfb6: 0x39b1, 0xfb7: 0x39c9, 0xfb8: 0x39e1, 0xfb9: 0x39f9, 0xfba: 0x3a11, 0xfbb: 0x3a29, 0xfbc: 0x3a41, 0xfbd: 0x3a59, 0xfbe: 0x3a71, 0xfbf: 0x3a89, // Block 0x3f, offset 0xfc0 0xfc0: 0x3aa1, 0xfc1: 0x3ac9, 0xfc2: 0x3af1, 0xfc3: 0x3b19, 0xfc4: 0x3b41, 0xfc5: 0x3b69, 0xfc6: 0x3b91, 0xfc7: 0x3bb9, 0xfc8: 0x3be1, 0xfc9: 0x3c09, 0xfca: 0x3c39, 0xfcb: 0x3c69, 0xfcc: 0x3c99, 0xfcd: 0x3cbd, 0xfce: 0x3cb1, 0xfcf: 0x3cdd, 0xfd0: 0x3cfd, 0xfd1: 0x3d15, 0xfd2: 0x3d2d, 0xfd3: 0x3d45, 0xfd4: 0x3d5d, 0xfd5: 0x3d5d, 0xfd6: 0x3d45, 0xfd7: 0x3d75, 0xfd8: 0x07bd, 0xfd9: 0x3d8d, 0xfda: 0x3da5, 0xfdb: 0x3dbd, 0xfdc: 0x3dd5, 0xfdd: 0x3ded, 0xfde: 0x3e05, 0xfdf: 0x3e1d, 0xfe0: 0x3e35, 0xfe1: 0x3e4d, 0xfe2: 0x3e65, 0xfe3: 0x3e7d, 0xfe4: 0x3e95, 0xfe5: 0x3e95, 0xfe6: 0x3ead, 0xfe7: 0x3ead, 0xfe8: 0x3ec5, 0xfe9: 0x3ec5, 0xfea: 0x3edd, 0xfeb: 0x3ef5, 0xfec: 0x3f0d, 0xfed: 0x3f25, 0xfee: 0x3f3d, 0xfef: 0x3f3d, 0xff0: 0x3f55, 0xff1: 0x3f55, 0xff2: 0x3f55, 0xff3: 0x3f6d, 0xff4: 0x3f85, 0xff5: 0x3f9d, 0xff6: 0x3fb5, 0xff7: 0x3f9d, 0xff8: 0x3fcd, 0xff9: 0x3fe5, 0xffa: 0x3f6d, 0xffb: 0x3ffd, 0xffc: 0x4015, 0xffd: 0x4015, 0xffe: 0x4015, 0xfff: 0x0040, // Block 0x40, offset 0x1000 0x1000: 0x3cc9, 0x1001: 0x3d31, 0x1002: 0x3d99, 0x1003: 0x3e01, 0x1004: 0x3e51, 0x1005: 0x3eb9, 0x1006: 0x3f09, 0x1007: 0x3f59, 0x1008: 0x3fd9, 0x1009: 0x4041, 0x100a: 0x4091, 0x100b: 0x40e1, 0x100c: 0x4131, 0x100d: 0x4199, 0x100e: 0x4201, 0x100f: 0x4251, 0x1010: 0x42a1, 0x1011: 0x42d9, 0x1012: 0x4329, 0x1013: 0x4391, 0x1014: 0x43f9, 0x1015: 0x4431, 0x1016: 0x44b1, 0x1017: 0x4549, 0x1018: 0x45c9, 0x1019: 0x4619, 0x101a: 0x4699, 0x101b: 0x4719, 0x101c: 0x4781, 0x101d: 0x47d1, 0x101e: 0x4821, 0x101f: 0x4871, 0x1020: 0x48d9, 0x1021: 0x4959, 0x1022: 0x49c1, 0x1023: 0x4a11, 0x1024: 0x4a61, 0x1025: 0x4ab1, 0x1026: 0x4ae9, 0x1027: 0x4b21, 0x1028: 0x4b59, 0x1029: 0x4b91, 0x102a: 0x4be1, 0x102b: 0x4c31, 0x102c: 0x4cb1, 0x102d: 0x4d01, 0x102e: 0x4d69, 0x102f: 0x4de9, 0x1030: 0x4e39, 0x1031: 0x4e71, 0x1032: 0x4ea9, 0x1033: 0x4f29, 0x1034: 0x4f91, 0x1035: 0x5011, 0x1036: 0x5061, 0x1037: 0x50e1, 0x1038: 0x5119, 0x1039: 0x5169, 0x103a: 0x51b9, 0x103b: 0x5209, 0x103c: 0x5259, 0x103d: 0x52a9, 0x103e: 0x5311, 0x103f: 0x5361, // Block 0x41, offset 0x1040 0x1040: 0x5399, 0x1041: 0x53e9, 0x1042: 0x5439, 0x1043: 0x5489, 0x1044: 0x54f1, 0x1045: 0x5541, 0x1046: 0x5591, 0x1047: 0x55e1, 0x1048: 0x5661, 0x1049: 0x56c9, 0x104a: 0x5701, 0x104b: 0x5781, 0x104c: 0x57b9, 0x104d: 0x5821, 0x104e: 0x5889, 0x104f: 0x58d9, 0x1050: 0x5929, 0x1051: 0x5979, 0x1052: 0x59e1, 0x1053: 0x5a19, 0x1054: 0x5a69, 0x1055: 0x5ad1, 0x1056: 0x5b09, 0x1057: 0x5b89, 0x1058: 0x5bd9, 0x1059: 0x5c01, 0x105a: 0x5c29, 0x105b: 0x5c51, 0x105c: 0x5c79, 0x105d: 0x5ca1, 0x105e: 0x5cc9, 0x105f: 0x5cf1, 0x1060: 0x5d19, 0x1061: 0x5d41, 0x1062: 0x5d69, 0x1063: 0x5d99, 0x1064: 0x5dc9, 0x1065: 0x5df9, 0x1066: 0x5e29, 0x1067: 0x5e59, 0x1068: 0x5e89, 0x1069: 0x5eb9, 0x106a: 0x5ee9, 0x106b: 0x5f19, 0x106c: 0x5f49, 0x106d: 0x5f79, 0x106e: 0x5fa9, 0x106f: 0x5fd9, 0x1070: 0x6009, 0x1071: 0x402d, 0x1072: 0x6039, 0x1073: 0x6051, 0x1074: 0x404d, 0x1075: 0x6069, 0x1076: 0x6081, 0x1077: 0x6099, 0x1078: 0x406d, 0x1079: 0x406d, 0x107a: 0x60b1, 0x107b: 0x60c9, 0x107c: 0x6101, 0x107d: 0x6139, 0x107e: 0x6171, 0x107f: 0x61a9, // Block 0x42, offset 0x1080 0x1080: 0x6211, 0x1081: 0x6229, 0x1082: 0x408d, 0x1083: 0x6241, 0x1084: 0x6259, 0x1085: 0x6271, 0x1086: 0x6289, 0x1087: 0x62a1, 0x1088: 0x40ad, 0x1089: 0x62b9, 0x108a: 0x62e1, 0x108b: 0x62f9, 0x108c: 0x40cd, 0x108d: 0x40cd, 0x108e: 0x6311, 0x108f: 0x6329, 0x1090: 0x6341, 0x1091: 0x40ed, 0x1092: 0x410d, 0x1093: 0x412d, 0x1094: 0x414d, 0x1095: 0x416d, 0x1096: 0x6359, 0x1097: 0x6371, 0x1098: 0x6389, 0x1099: 0x63a1, 0x109a: 0x63b9, 0x109b: 0x418d, 0x109c: 0x63d1, 0x109d: 0x63e9, 0x109e: 0x6401, 0x109f: 0x41ad, 0x10a0: 0x41cd, 0x10a1: 0x6419, 0x10a2: 0x41ed, 0x10a3: 0x420d, 0x10a4: 0x422d, 0x10a5: 0x6431, 0x10a6: 0x424d, 0x10a7: 0x6449, 0x10a8: 0x6479, 0x10a9: 0x6211, 0x10aa: 0x426d, 0x10ab: 0x428d, 0x10ac: 0x42ad, 0x10ad: 0x42cd, 0x10ae: 0x64b1, 0x10af: 0x64f1, 0x10b0: 0x6539, 0x10b1: 0x6551, 0x10b2: 0x42ed, 0x10b3: 0x6569, 0x10b4: 0x6581, 0x10b5: 0x6599, 0x10b6: 0x430d, 0x10b7: 0x65b1, 0x10b8: 0x65c9, 0x10b9: 0x65b1, 0x10ba: 0x65e1, 0x10bb: 0x65f9, 0x10bc: 0x432d, 0x10bd: 0x6611, 0x10be: 0x6629, 0x10bf: 0x6611, // Block 0x43, offset 0x10c0 0x10c0: 0x434d, 0x10c1: 0x436d, 0x10c2: 0x0040, 0x10c3: 0x6641, 0x10c4: 0x6659, 0x10c5: 0x6671, 0x10c6: 0x6689, 0x10c7: 0x0040, 0x10c8: 0x66c1, 0x10c9: 0x66d9, 0x10ca: 0x66f1, 0x10cb: 0x6709, 0x10cc: 0x6721, 0x10cd: 0x6739, 0x10ce: 0x6401, 0x10cf: 0x6751, 0x10d0: 0x6769, 0x10d1: 0x6781, 0x10d2: 0x438d, 0x10d3: 0x6799, 0x10d4: 0x6289, 0x10d5: 0x43ad, 0x10d6: 0x43cd, 0x10d7: 0x67b1, 0x10d8: 0x0040, 0x10d9: 0x43ed, 0x10da: 0x67c9, 0x10db: 0x67e1, 0x10dc: 0x67f9, 0x10dd: 0x6811, 0x10de: 0x6829, 0x10df: 0x6859, 0x10e0: 0x6889, 0x10e1: 0x68b1, 0x10e2: 0x68d9, 0x10e3: 0x6901, 0x10e4: 0x6929, 0x10e5: 0x6951, 0x10e6: 0x6979, 0x10e7: 0x69a1, 0x10e8: 0x69c9, 0x10e9: 0x69f1, 0x10ea: 0x6a21, 0x10eb: 0x6a51, 0x10ec: 0x6a81, 0x10ed: 0x6ab1, 0x10ee: 0x6ae1, 0x10ef: 0x6b11, 0x10f0: 0x6b41, 0x10f1: 0x6b71, 0x10f2: 0x6ba1, 0x10f3: 0x6bd1, 0x10f4: 0x6c01, 0x10f5: 0x6c31, 0x10f6: 0x6c61, 0x10f7: 0x6c91, 0x10f8: 0x6cc1, 0x10f9: 0x6cf1, 0x10fa: 0x6d21, 0x10fb: 0x6d51, 0x10fc: 0x6d81, 0x10fd: 0x6db1, 0x10fe: 0x6de1, 0x10ff: 0x440d, // Block 0x44, offset 0x1100 0x1100: 0xe00d, 0x1101: 0x0008, 0x1102: 0xe00d, 0x1103: 0x0008, 0x1104: 0xe00d, 0x1105: 0x0008, 0x1106: 0xe00d, 0x1107: 0x0008, 0x1108: 0xe00d, 0x1109: 0x0008, 0x110a: 0xe00d, 0x110b: 0x0008, 0x110c: 0xe00d, 0x110d: 0x0008, 0x110e: 0xe00d, 0x110f: 0x0008, 0x1110: 0xe00d, 0x1111: 0x0008, 0x1112: 0xe00d, 0x1113: 0x0008, 0x1114: 0xe00d, 0x1115: 0x0008, 0x1116: 0xe00d, 0x1117: 0x0008, 0x1118: 0xe00d, 0x1119: 0x0008, 0x111a: 0xe00d, 0x111b: 0x0008, 0x111c: 0xe00d, 0x111d: 0x0008, 0x111e: 0xe00d, 0x111f: 0x0008, 0x1120: 0xe00d, 0x1121: 0x0008, 0x1122: 0xe00d, 0x1123: 0x0008, 0x1124: 0xe00d, 0x1125: 0x0008, 0x1126: 0xe00d, 0x1127: 0x0008, 0x1128: 0xe00d, 0x1129: 0x0008, 0x112a: 0xe00d, 0x112b: 0x0008, 0x112c: 0xe00d, 0x112d: 0x0008, 0x112e: 0x0008, 0x112f: 0x3308, 0x1130: 0x3318, 0x1131: 0x3318, 0x1132: 0x3318, 0x1133: 0x0018, 0x1134: 0x3308, 0x1135: 0x3308, 0x1136: 0x3308, 0x1137: 0x3308, 0x1138: 0x3308, 0x1139: 0x3308, 0x113a: 0x3308, 0x113b: 0x3308, 0x113c: 0x3308, 0x113d: 0x3308, 0x113e: 0x0018, 0x113f: 0x0008, // Block 0x45, offset 0x1140 0x1140: 0xe00d, 0x1141: 0x0008, 0x1142: 0xe00d, 0x1143: 0x0008, 0x1144: 0xe00d, 0x1145: 0x0008, 0x1146: 0xe00d, 0x1147: 0x0008, 0x1148: 0xe00d, 0x1149: 0x0008, 0x114a: 0xe00d, 0x114b: 0x0008, 0x114c: 0xe00d, 0x114d: 0x0008, 0x114e: 0xe00d, 0x114f: 0x0008, 0x1150: 0xe00d, 0x1151: 0x0008, 0x1152: 0xe00d, 0x1153: 0x0008, 0x1154: 0xe00d, 0x1155: 0x0008, 0x1156: 0xe00d, 0x1157: 0x0008, 0x1158: 0xe00d, 0x1159: 0x0008, 0x115a: 0xe00d, 0x115b: 0x0008, 0x115c: 0x0ea1, 0x115d: 0x6e11, 0x115e: 0x3308, 0x115f: 0x3308, 0x1160: 0x0008, 0x1161: 0x0008, 0x1162: 0x0008, 0x1163: 0x0008, 0x1164: 0x0008, 0x1165: 0x0008, 0x1166: 0x0008, 0x1167: 0x0008, 0x1168: 0x0008, 0x1169: 0x0008, 0x116a: 0x0008, 0x116b: 0x0008, 0x116c: 0x0008, 0x116d: 0x0008, 0x116e: 0x0008, 0x116f: 0x0008, 0x1170: 0x0008, 0x1171: 0x0008, 0x1172: 0x0008, 0x1173: 0x0008, 0x1174: 0x0008, 0x1175: 0x0008, 0x1176: 0x0008, 0x1177: 0x0008, 0x1178: 0x0008, 0x1179: 0x0008, 0x117a: 0x0008, 0x117b: 0x0008, 0x117c: 0x0008, 0x117d: 0x0008, 0x117e: 0x0008, 0x117f: 0x0008, // Block 0x46, offset 0x1180 0x1180: 0x0018, 0x1181: 0x0018, 0x1182: 0x0018, 0x1183: 0x0018, 0x1184: 0x0018, 0x1185: 0x0018, 0x1186: 0x0018, 0x1187: 0x0018, 0x1188: 0x0018, 0x1189: 0x0018, 0x118a: 0x0018, 0x118b: 0x0018, 0x118c: 0x0018, 0x118d: 0x0018, 0x118e: 0x0018, 0x118f: 0x0018, 0x1190: 0x0018, 0x1191: 0x0018, 0x1192: 0x0018, 0x1193: 0x0018, 0x1194: 0x0018, 0x1195: 0x0018, 0x1196: 0x0018, 0x1197: 0x0008, 0x1198: 0x0008, 0x1199: 0x0008, 0x119a: 0x0008, 0x119b: 0x0008, 0x119c: 0x0008, 0x119d: 0x0008, 0x119e: 0x0008, 0x119f: 0x0008, 0x11a0: 0x0018, 0x11a1: 0x0018, 0x11a2: 0xe00d, 0x11a3: 0x0008, 0x11a4: 0xe00d, 0x11a5: 0x0008, 0x11a6: 0xe00d, 0x11a7: 0x0008, 0x11a8: 0xe00d, 0x11a9: 0x0008, 0x11aa: 0xe00d, 0x11ab: 0x0008, 0x11ac: 0xe00d, 0x11ad: 0x0008, 0x11ae: 0xe00d, 0x11af: 0x0008, 0x11b0: 0x0008, 0x11b1: 0x0008, 0x11b2: 0xe00d, 0x11b3: 0x0008, 0x11b4: 0xe00d, 0x11b5: 0x0008, 0x11b6: 0xe00d, 0x11b7: 0x0008, 0x11b8: 0xe00d, 0x11b9: 0x0008, 0x11ba: 0xe00d, 0x11bb: 0x0008, 0x11bc: 0xe00d, 0x11bd: 0x0008, 0x11be: 0xe00d, 0x11bf: 0x0008, // Block 0x47, offset 0x11c0 0x11c0: 0xe00d, 0x11c1: 0x0008, 0x11c2: 0xe00d, 0x11c3: 0x0008, 0x11c4: 0xe00d, 0x11c5: 0x0008, 0x11c6: 0xe00d, 0x11c7: 0x0008, 0x11c8: 0xe00d, 0x11c9: 0x0008, 0x11ca: 0xe00d, 0x11cb: 0x0008, 0x11cc: 0xe00d, 0x11cd: 0x0008, 0x11ce: 0xe00d, 0x11cf: 0x0008, 0x11d0: 0xe00d, 0x11d1: 0x0008, 0x11d2: 0xe00d, 0x11d3: 0x0008, 0x11d4: 0xe00d, 0x11d5: 0x0008, 0x11d6: 0xe00d, 0x11d7: 0x0008, 0x11d8: 0xe00d, 0x11d9: 0x0008, 0x11da: 0xe00d, 0x11db: 0x0008, 0x11dc: 0xe00d, 0x11dd: 0x0008, 0x11de: 0xe00d, 0x11df: 0x0008, 0x11e0: 0xe00d, 0x11e1: 0x0008, 0x11e2: 0xe00d, 0x11e3: 0x0008, 0x11e4: 0xe00d, 0x11e5: 0x0008, 0x11e6: 0xe00d, 0x11e7: 0x0008, 0x11e8: 0xe00d, 0x11e9: 0x0008, 0x11ea: 0xe00d, 0x11eb: 0x0008, 0x11ec: 0xe00d, 0x11ed: 0x0008, 0x11ee: 0xe00d, 0x11ef: 0x0008, 0x11f0: 0xe0fd, 0x11f1: 0x0008, 0x11f2: 0x0008, 0x11f3: 0x0008, 0x11f4: 0x0008, 0x11f5: 0x0008, 0x11f6: 0x0008, 0x11f7: 0x0008, 0x11f8: 0x0008, 0x11f9: 0xe01d, 0x11fa: 0x0008, 0x11fb: 0xe03d, 0x11fc: 0x0008, 0x11fd: 0x442d, 0x11fe: 0xe00d, 0x11ff: 0x0008, // Block 0x48, offset 0x1200 0x1200: 0xe00d, 0x1201: 0x0008, 0x1202: 0xe00d, 0x1203: 0x0008, 0x1204: 0xe00d, 0x1205: 0x0008, 0x1206: 0xe00d, 0x1207: 0x0008, 0x1208: 0x0008, 0x1209: 0x0018, 0x120a: 0x0018, 0x120b: 0xe03d, 0x120c: 0x0008, 0x120d: 0x11d9, 0x120e: 0x0008, 0x120f: 0x0008, 0x1210: 0xe00d, 0x1211: 0x0008, 0x1212: 0xe00d, 0x1213: 0x0008, 0x1214: 0x0008, 0x1215: 0x0008, 0x1216: 0xe00d, 0x1217: 0x0008, 0x1218: 0xe00d, 0x1219: 0x0008, 0x121a: 0xe00d, 0x121b: 0x0008, 0x121c: 0xe00d, 0x121d: 0x0008, 0x121e: 0xe00d, 0x121f: 0x0008, 0x1220: 0xe00d, 0x1221: 0x0008, 0x1222: 0xe00d, 0x1223: 0x0008, 0x1224: 0xe00d, 0x1225: 0x0008, 0x1226: 0xe00d, 0x1227: 0x0008, 0x1228: 0xe00d, 0x1229: 0x0008, 0x122a: 0x6e29, 0x122b: 0x1029, 0x122c: 0x11c1, 0x122d: 0x6e41, 0x122e: 0x1221, 0x122f: 0x0008, 0x1230: 0x6e59, 0x1231: 0x6e71, 0x1232: 0x1239, 0x1233: 0x444d, 0x1234: 0xe00d, 0x1235: 0x0008, 0x1236: 0xe00d, 0x1237: 0x0008, 0x1238: 0x0040, 0x1239: 0x0008, 0x123a: 0x0040, 0x123b: 0x0040, 0x123c: 0x0040, 0x123d: 0x0040, 0x123e: 0x0040, 0x123f: 0x0040, // Block 0x49, offset 0x1240 0x1240: 0x64d5, 0x1241: 0x64f5, 0x1242: 0x6515, 0x1243: 0x6535, 0x1244: 0x6555, 0x1245: 0x6575, 0x1246: 0x6595, 0x1247: 0x65b5, 0x1248: 0x65d5, 0x1249: 0x65f5, 0x124a: 0x6615, 0x124b: 0x6635, 0x124c: 0x6655, 0x124d: 0x6675, 0x124e: 0x0008, 0x124f: 0x0008, 0x1250: 0x6695, 0x1251: 0x0008, 0x1252: 0x66b5, 0x1253: 0x0008, 0x1254: 0x0008, 0x1255: 0x66d5, 0x1256: 0x66f5, 0x1257: 0x6715, 0x1258: 0x6735, 0x1259: 0x6755, 0x125a: 0x6775, 0x125b: 0x6795, 0x125c: 0x67b5, 0x125d: 0x67d5, 0x125e: 0x67f5, 0x125f: 0x0008, 0x1260: 0x6815, 0x1261: 0x0008, 0x1262: 0x6835, 0x1263: 0x0008, 0x1264: 0x0008, 0x1265: 0x6855, 0x1266: 0x6875, 0x1267: 0x0008, 0x1268: 0x0008, 0x1269: 0x0008, 0x126a: 0x6895, 0x126b: 0x68b5, 0x126c: 0x68d5, 0x126d: 0x68f5, 0x126e: 0x6915, 0x126f: 0x6935, 0x1270: 0x6955, 0x1271: 0x6975, 0x1272: 0x6995, 0x1273: 0x69b5, 0x1274: 0x69d5, 0x1275: 0x69f5, 0x1276: 0x6a15, 0x1277: 0x6a35, 0x1278: 0x6a55, 0x1279: 0x6a75, 0x127a: 0x6a95, 0x127b: 0x6ab5, 0x127c: 0x6ad5, 0x127d: 0x6af5, 0x127e: 0x6b15, 0x127f: 0x6b35, // Block 0x4a, offset 0x1280 0x1280: 0x7a95, 0x1281: 0x7ab5, 0x1282: 0x7ad5, 0x1283: 0x7af5, 0x1284: 0x7b15, 0x1285: 0x7b35, 0x1286: 0x7b55, 0x1287: 0x7b75, 0x1288: 0x7b95, 0x1289: 0x7bb5, 0x128a: 0x7bd5, 0x128b: 0x7bf5, 0x128c: 0x7c15, 0x128d: 0x7c35, 0x128e: 0x7c55, 0x128f: 0x6ec9, 0x1290: 0x6ef1, 0x1291: 0x6f19, 0x1292: 0x7c75, 0x1293: 0x7c95, 0x1294: 0x7cb5, 0x1295: 0x6f41, 0x1296: 0x6f69, 0x1297: 0x6f91, 0x1298: 0x7cd5, 0x1299: 0x7cf5, 0x129a: 0x0040, 0x129b: 0x0040, 0x129c: 0x0040, 0x129d: 0x0040, 0x129e: 0x0040, 0x129f: 0x0040, 0x12a0: 0x0040, 0x12a1: 0x0040, 0x12a2: 0x0040, 0x12a3: 0x0040, 0x12a4: 0x0040, 0x12a5: 0x0040, 0x12a6: 0x0040, 0x12a7: 0x0040, 0x12a8: 0x0040, 0x12a9: 0x0040, 0x12aa: 0x0040, 0x12ab: 0x0040, 0x12ac: 0x0040, 0x12ad: 0x0040, 0x12ae: 0x0040, 0x12af: 0x0040, 0x12b0: 0x0040, 0x12b1: 0x0040, 0x12b2: 0x0040, 0x12b3: 0x0040, 0x12b4: 0x0040, 0x12b5: 0x0040, 0x12b6: 0x0040, 0x12b7: 0x0040, 0x12b8: 0x0040, 0x12b9: 0x0040, 0x12ba: 0x0040, 0x12bb: 0x0040, 0x12bc: 0x0040, 0x12bd: 0x0040, 0x12be: 0x0040, 0x12bf: 0x0040, // Block 0x4b, offset 0x12c0 0x12c0: 0x6fb9, 0x12c1: 0x6fd1, 0x12c2: 0x6fe9, 0x12c3: 0x7d15, 0x12c4: 0x7d35, 0x12c5: 0x7001, 0x12c6: 0x7001, 0x12c7: 0x0040, 0x12c8: 0x0040, 0x12c9: 0x0040, 0x12ca: 0x0040, 0x12cb: 0x0040, 0x12cc: 0x0040, 0x12cd: 0x0040, 0x12ce: 0x0040, 0x12cf: 0x0040, 0x12d0: 0x0040, 0x12d1: 0x0040, 0x12d2: 0x0040, 0x12d3: 0x7019, 0x12d4: 0x7041, 0x12d5: 0x7069, 0x12d6: 0x7091, 0x12d7: 0x70b9, 0x12d8: 0x0040, 0x12d9: 0x0040, 0x12da: 0x0040, 0x12db: 0x0040, 0x12dc: 0x0040, 0x12dd: 0x70e1, 0x12de: 0x3308, 0x12df: 0x7109, 0x12e0: 0x7131, 0x12e1: 0x20a9, 0x12e2: 0x20f1, 0x12e3: 0x7149, 0x12e4: 0x7161, 0x12e5: 0x7179, 0x12e6: 0x7191, 0x12e7: 0x71a9, 0x12e8: 0x71c1, 0x12e9: 0x1fb2, 0x12ea: 0x71d9, 0x12eb: 0x7201, 0x12ec: 0x7229, 0x12ed: 0x7261, 0x12ee: 0x7299, 0x12ef: 0x72c1, 0x12f0: 0x72e9, 0x12f1: 0x7311, 0x12f2: 0x7339, 0x12f3: 0x7361, 0x12f4: 0x7389, 0x12f5: 0x73b1, 0x12f6: 0x73d9, 0x12f7: 0x0040, 0x12f8: 0x7401, 0x12f9: 0x7429, 0x12fa: 0x7451, 0x12fb: 0x7479, 0x12fc: 0x74a1, 0x12fd: 0x0040, 0x12fe: 0x74c9, 0x12ff: 0x0040, // Block 0x4c, offset 0x1300 0x1300: 0x74f1, 0x1301: 0x7519, 0x1302: 0x0040, 0x1303: 0x7541, 0x1304: 0x7569, 0x1305: 0x0040, 0x1306: 0x7591, 0x1307: 0x75b9, 0x1308: 0x75e1, 0x1309: 0x7609, 0x130a: 0x7631, 0x130b: 0x7659, 0x130c: 0x7681, 0x130d: 0x76a9, 0x130e: 0x76d1, 0x130f: 0x76f9, 0x1310: 0x7721, 0x1311: 0x7721, 0x1312: 0x7739, 0x1313: 0x7739, 0x1314: 0x7739, 0x1315: 0x7739, 0x1316: 0x7751, 0x1317: 0x7751, 0x1318: 0x7751, 0x1319: 0x7751, 0x131a: 0x7769, 0x131b: 0x7769, 0x131c: 0x7769, 0x131d: 0x7769, 0x131e: 0x7781, 0x131f: 0x7781, 0x1320: 0x7781, 0x1321: 0x7781, 0x1322: 0x7799, 0x1323: 0x7799, 0x1324: 0x7799, 0x1325: 0x7799, 0x1326: 0x77b1, 0x1327: 0x77b1, 0x1328: 0x77b1, 0x1329: 0x77b1, 0x132a: 0x77c9, 0x132b: 0x77c9, 0x132c: 0x77c9, 0x132d: 0x77c9, 0x132e: 0x77e1, 0x132f: 0x77e1, 0x1330: 0x77e1, 0x1331: 0x77e1, 0x1332: 0x77f9, 0x1333: 0x77f9, 0x1334: 0x77f9, 0x1335: 0x77f9, 0x1336: 0x7811, 0x1337: 0x7811, 0x1338: 0x7811, 0x1339: 0x7811, 0x133a: 0x7829, 0x133b: 0x7829, 0x133c: 0x7829, 0x133d: 0x7829, 0x133e: 0x7841, 0x133f: 0x7841, // Block 0x4d, offset 0x1340 0x1340: 0x7841, 0x1341: 0x7841, 0x1342: 0x7859, 0x1343: 0x7859, 0x1344: 0x7871, 0x1345: 0x7871, 0x1346: 0x7889, 0x1347: 0x7889, 0x1348: 0x78a1, 0x1349: 0x78a1, 0x134a: 0x78b9, 0x134b: 0x78b9, 0x134c: 0x78d1, 0x134d: 0x78d1, 0x134e: 0x78e9, 0x134f: 0x78e9, 0x1350: 0x78e9, 0x1351: 0x78e9, 0x1352: 0x7901, 0x1353: 0x7901, 0x1354: 0x7901, 0x1355: 0x7901, 0x1356: 0x7919, 0x1357: 0x7919, 0x1358: 0x7919, 0x1359: 0x7919, 0x135a: 0x7931, 0x135b: 0x7931, 0x135c: 0x7931, 0x135d: 0x7931, 0x135e: 0x7949, 0x135f: 0x7949, 0x1360: 0x7961, 0x1361: 0x7961, 0x1362: 0x7961, 0x1363: 0x7961, 0x1364: 0x7979, 0x1365: 0x7979, 0x1366: 0x7991, 0x1367: 0x7991, 0x1368: 0x7991, 0x1369: 0x7991, 0x136a: 0x79a9, 0x136b: 0x79a9, 0x136c: 0x79a9, 0x136d: 0x79a9, 0x136e: 0x79c1, 0x136f: 0x79c1, 0x1370: 0x79d9, 0x1371: 0x79d9, 0x1372: 0x0818, 0x1373: 0x0818, 0x1374: 0x0818, 0x1375: 0x0818, 0x1376: 0x0818, 0x1377: 0x0818, 0x1378: 0x0818, 0x1379: 0x0818, 0x137a: 0x0818, 0x137b: 0x0818, 0x137c: 0x0818, 0x137d: 0x0818, 0x137e: 0x0818, 0x137f: 0x0818, // Block 0x4e, offset 0x1380 0x1380: 0x0818, 0x1381: 0x0818, 0x1382: 0x0040, 0x1383: 0x0040, 0x1384: 0x0040, 0x1385: 0x0040, 0x1386: 0x0040, 0x1387: 0x0040, 0x1388: 0x0040, 0x1389: 0x0040, 0x138a: 0x0040, 0x138b: 0x0040, 0x138c: 0x0040, 0x138d: 0x0040, 0x138e: 0x0040, 0x138f: 0x0040, 0x1390: 0x0040, 0x1391: 0x0040, 0x1392: 0x0040, 0x1393: 0x79f1, 0x1394: 0x79f1, 0x1395: 0x79f1, 0x1396: 0x79f1, 0x1397: 0x7a09, 0x1398: 0x7a09, 0x1399: 0x7a21, 0x139a: 0x7a21, 0x139b: 0x7a39, 0x139c: 0x7a39, 0x139d: 0x0479, 0x139e: 0x7a51, 0x139f: 0x7a51, 0x13a0: 0x7a69, 0x13a1: 0x7a69, 0x13a2: 0x7a81, 0x13a3: 0x7a81, 0x13a4: 0x7a99, 0x13a5: 0x7a99, 0x13a6: 0x7a99, 0x13a7: 0x7a99, 0x13a8: 0x7ab1, 0x13a9: 0x7ab1, 0x13aa: 0x7ac9, 0x13ab: 0x7ac9, 0x13ac: 0x7af1, 0x13ad: 0x7af1, 0x13ae: 0x7b19, 0x13af: 0x7b19, 0x13b0: 0x7b41, 0x13b1: 0x7b41, 0x13b2: 0x7b69, 0x13b3: 0x7b69, 0x13b4: 0x7b91, 0x13b5: 0x7b91, 0x13b6: 0x7bb9, 0x13b7: 0x7bb9, 0x13b8: 0x7bb9, 0x13b9: 0x7be1, 0x13ba: 0x7be1, 0x13bb: 0x7be1, 0x13bc: 0x7c09, 0x13bd: 0x7c09, 0x13be: 0x7c09, 0x13bf: 0x7c09, // Block 0x4f, offset 0x13c0 0x13c0: 0x85f9, 0x13c1: 0x8621, 0x13c2: 0x8649, 0x13c3: 0x8671, 0x13c4: 0x8699, 0x13c5: 0x86c1, 0x13c6: 0x86e9, 0x13c7: 0x8711, 0x13c8: 0x8739, 0x13c9: 0x8761, 0x13ca: 0x8789, 0x13cb: 0x87b1, 0x13cc: 0x87d9, 0x13cd: 0x8801, 0x13ce: 0x8829, 0x13cf: 0x8851, 0x13d0: 0x8879, 0x13d1: 0x88a1, 0x13d2: 0x88c9, 0x13d3: 0x88f1, 0x13d4: 0x8919, 0x13d5: 0x8941, 0x13d6: 0x8969, 0x13d7: 0x8991, 0x13d8: 0x89b9, 0x13d9: 0x89e1, 0x13da: 0x8a09, 0x13db: 0x8a31, 0x13dc: 0x8a59, 0x13dd: 0x8a81, 0x13de: 0x8aaa, 0x13df: 0x8ada, 0x13e0: 0x8b0a, 0x13e1: 0x8b3a, 0x13e2: 0x8b6a, 0x13e3: 0x8b9a, 0x13e4: 0x8bc9, 0x13e5: 0x8bf1, 0x13e6: 0x7c71, 0x13e7: 0x8c19, 0x13e8: 0x7be1, 0x13e9: 0x7c99, 0x13ea: 0x8c41, 0x13eb: 0x8c69, 0x13ec: 0x7d39, 0x13ed: 0x8c91, 0x13ee: 0x7d61, 0x13ef: 0x7d89, 0x13f0: 0x8cb9, 0x13f1: 0x8ce1, 0x13f2: 0x7e29, 0x13f3: 0x8d09, 0x13f4: 0x7e51, 0x13f5: 0x7e79, 0x13f6: 0x8d31, 0x13f7: 0x8d59, 0x13f8: 0x7ec9, 0x13f9: 0x8d81, 0x13fa: 0x7ef1, 0x13fb: 0x7f19, 0x13fc: 0x83a1, 0x13fd: 0x83c9, 0x13fe: 0x8441, 0x13ff: 0x8469, // Block 0x50, offset 0x1400 0x1400: 0x8491, 0x1401: 0x8531, 0x1402: 0x8559, 0x1403: 0x8581, 0x1404: 0x85a9, 0x1405: 0x8649, 0x1406: 0x8671, 0x1407: 0x8699, 0x1408: 0x8da9, 0x1409: 0x8739, 0x140a: 0x8dd1, 0x140b: 0x8df9, 0x140c: 0x8829, 0x140d: 0x8e21, 0x140e: 0x8851, 0x140f: 0x8879, 0x1410: 0x8a81, 0x1411: 0x8e49, 0x1412: 0x8e71, 0x1413: 0x89b9, 0x1414: 0x8e99, 0x1415: 0x89e1, 0x1416: 0x8a09, 0x1417: 0x7c21, 0x1418: 0x7c49, 0x1419: 0x8ec1, 0x141a: 0x7c71, 0x141b: 0x8ee9, 0x141c: 0x7cc1, 0x141d: 0x7ce9, 0x141e: 0x7d11, 0x141f: 0x7d39, 0x1420: 0x8f11, 0x1421: 0x7db1, 0x1422: 0x7dd9, 0x1423: 0x7e01, 0x1424: 0x7e29, 0x1425: 0x8f39, 0x1426: 0x7ec9, 0x1427: 0x7f41, 0x1428: 0x7f69, 0x1429: 0x7f91, 0x142a: 0x7fb9, 0x142b: 0x7fe1, 0x142c: 0x8031, 0x142d: 0x8059, 0x142e: 0x8081, 0x142f: 0x80a9, 0x1430: 0x80d1, 0x1431: 0x80f9, 0x1432: 0x8f61, 0x1433: 0x8121, 0x1434: 0x8149, 0x1435: 0x8171, 0x1436: 0x8199, 0x1437: 0x81c1, 0x1438: 0x81e9, 0x1439: 0x8239, 0x143a: 0x8261, 0x143b: 0x8289, 0x143c: 0x82b1, 0x143d: 0x82d9, 0x143e: 0x8301, 0x143f: 0x8329, // Block 0x51, offset 0x1440 0x1440: 0x8351, 0x1441: 0x8379, 0x1442: 0x83f1, 0x1443: 0x8419, 0x1444: 0x84b9, 0x1445: 0x84e1, 0x1446: 0x8509, 0x1447: 0x8531, 0x1448: 0x8559, 0x1449: 0x85d1, 0x144a: 0x85f9, 0x144b: 0x8621, 0x144c: 0x8649, 0x144d: 0x8f89, 0x144e: 0x86c1, 0x144f: 0x86e9, 0x1450: 0x8711, 0x1451: 0x8739, 0x1452: 0x87b1, 0x1453: 0x87d9, 0x1454: 0x8801, 0x1455: 0x8829, 0x1456: 0x8fb1, 0x1457: 0x88a1, 0x1458: 0x88c9, 0x1459: 0x8fd9, 0x145a: 0x8941, 0x145b: 0x8969, 0x145c: 0x8991, 0x145d: 0x89b9, 0x145e: 0x9001, 0x145f: 0x7c71, 0x1460: 0x8ee9, 0x1461: 0x7d39, 0x1462: 0x8f11, 0x1463: 0x7e29, 0x1464: 0x8f39, 0x1465: 0x7ec9, 0x1466: 0x9029, 0x1467: 0x80d1, 0x1468: 0x9051, 0x1469: 0x9079, 0x146a: 0x90a1, 0x146b: 0x8531, 0x146c: 0x8559, 0x146d: 0x8649, 0x146e: 0x8829, 0x146f: 0x8fb1, 0x1470: 0x89b9, 0x1471: 0x9001, 0x1472: 0x90c9, 0x1473: 0x9101, 0x1474: 0x9139, 0x1475: 0x9171, 0x1476: 0x9199, 0x1477: 0x91c1, 0x1478: 0x91e9, 0x1479: 0x9211, 0x147a: 0x9239, 0x147b: 0x9261, 0x147c: 0x9289, 0x147d: 0x92b1, 0x147e: 0x92d9, 0x147f: 0x9301, // Block 0x52, offset 0x1480 0x1480: 0x9329, 0x1481: 0x9351, 0x1482: 0x9379, 0x1483: 0x93a1, 0x1484: 0x93c9, 0x1485: 0x93f1, 0x1486: 0x9419, 0x1487: 0x9441, 0x1488: 0x9469, 0x1489: 0x9491, 0x148a: 0x94b9, 0x148b: 0x94e1, 0x148c: 0x9079, 0x148d: 0x9509, 0x148e: 0x9531, 0x148f: 0x9559, 0x1490: 0x9581, 0x1491: 0x9171, 0x1492: 0x9199, 0x1493: 0x91c1, 0x1494: 0x91e9, 0x1495: 0x9211, 0x1496: 0x9239, 0x1497: 0x9261, 0x1498: 0x9289, 0x1499: 0x92b1, 0x149a: 0x92d9, 0x149b: 0x9301, 0x149c: 0x9329, 0x149d: 0x9351, 0x149e: 0x9379, 0x149f: 0x93a1, 0x14a0: 0x93c9, 0x14a1: 0x93f1, 0x14a2: 0x9419, 0x14a3: 0x9441, 0x14a4: 0x9469, 0x14a5: 0x9491, 0x14a6: 0x94b9, 0x14a7: 0x94e1, 0x14a8: 0x9079, 0x14a9: 0x9509, 0x14aa: 0x9531, 0x14ab: 0x9559, 0x14ac: 0x9581, 0x14ad: 0x9491, 0x14ae: 0x94b9, 0x14af: 0x94e1, 0x14b0: 0x9079, 0x14b1: 0x9051, 0x14b2: 0x90a1, 0x14b3: 0x8211, 0x14b4: 0x8059, 0x14b5: 0x8081, 0x14b6: 0x80a9, 0x14b7: 0x9491, 0x14b8: 0x94b9, 0x14b9: 0x94e1, 0x14ba: 0x8211, 0x14bb: 0x8239, 0x14bc: 0x95a9, 0x14bd: 0x95a9, 0x14be: 0x0018, 0x14bf: 0x0018, // Block 0x53, offset 0x14c0 0x14c0: 0x0040, 0x14c1: 0x0040, 0x14c2: 0x0040, 0x14c3: 0x0040, 0x14c4: 0x0040, 0x14c5: 0x0040, 0x14c6: 0x0040, 0x14c7: 0x0040, 0x14c8: 0x0040, 0x14c9: 0x0040, 0x14ca: 0x0040, 0x14cb: 0x0040, 0x14cc: 0x0040, 0x14cd: 0x0040, 0x14ce: 0x0040, 0x14cf: 0x0040, 0x14d0: 0x95d1, 0x14d1: 0x9609, 0x14d2: 0x9609, 0x14d3: 0x9641, 0x14d4: 0x9679, 0x14d5: 0x96b1, 0x14d6: 0x96e9, 0x14d7: 0x9721, 0x14d8: 0x9759, 0x14d9: 0x9759, 0x14da: 0x9791, 0x14db: 0x97c9, 0x14dc: 0x9801, 0x14dd: 0x9839, 0x14de: 0x9871, 0x14df: 0x98a9, 0x14e0: 0x98a9, 0x14e1: 0x98e1, 0x14e2: 0x9919, 0x14e3: 0x9919, 0x14e4: 0x9951, 0x14e5: 0x9951, 0x14e6: 0x9989, 0x14e7: 0x99c1, 0x14e8: 0x99c1, 0x14e9: 0x99f9, 0x14ea: 0x9a31, 0x14eb: 0x9a31, 0x14ec: 0x9a69, 0x14ed: 0x9a69, 0x14ee: 0x9aa1, 0x14ef: 0x9ad9, 0x14f0: 0x9ad9, 0x14f1: 0x9b11, 0x14f2: 0x9b11, 0x14f3: 0x9b49, 0x14f4: 0x9b81, 0x14f5: 0x9bb9, 0x14f6: 0x9bf1, 0x14f7: 0x9bf1, 0x14f8: 0x9c29, 0x14f9: 0x9c61, 0x14fa: 0x9c99, 0x14fb: 0x9cd1, 0x14fc: 0x9d09, 0x14fd: 0x9d09, 0x14fe: 0x9d41, 0x14ff: 0x9d79, // Block 0x54, offset 0x1500 0x1500: 0xa949, 0x1501: 0xa981, 0x1502: 0xa9b9, 0x1503: 0xa8a1, 0x1504: 0x9bb9, 0x1505: 0x9989, 0x1506: 0xa9f1, 0x1507: 0xaa29, 0x1508: 0x0040, 0x1509: 0x0040, 0x150a: 0x0040, 0x150b: 0x0040, 0x150c: 0x0040, 0x150d: 0x0040, 0x150e: 0x0040, 0x150f: 0x0040, 0x1510: 0x0040, 0x1511: 0x0040, 0x1512: 0x0040, 0x1513: 0x0040, 0x1514: 0x0040, 0x1515: 0x0040, 0x1516: 0x0040, 0x1517: 0x0040, 0x1518: 0x0040, 0x1519: 0x0040, 0x151a: 0x0040, 0x151b: 0x0040, 0x151c: 0x0040, 0x151d: 0x0040, 0x151e: 0x0040, 0x151f: 0x0040, 0x1520: 0x0040, 0x1521: 0x0040, 0x1522: 0x0040, 0x1523: 0x0040, 0x1524: 0x0040, 0x1525: 0x0040, 0x1526: 0x0040, 0x1527: 0x0040, 0x1528: 0x0040, 0x1529: 0x0040, 0x152a: 0x0040, 0x152b: 0x0040, 0x152c: 0x0040, 0x152d: 0x0040, 0x152e: 0x0040, 0x152f: 0x0040, 0x1530: 0xaa61, 0x1531: 0xaa99, 0x1532: 0xaad1, 0x1533: 0xab19, 0x1534: 0xab61, 0x1535: 0xaba9, 0x1536: 0xabf1, 0x1537: 0xac39, 0x1538: 0xac81, 0x1539: 0xacc9, 0x153a: 0xad02, 0x153b: 0xae12, 0x153c: 0xae91, 0x153d: 0x0018, 0x153e: 0x0040, 0x153f: 0x0040, // Block 0x55, offset 0x1540 0x1540: 0x33c0, 0x1541: 0x33c0, 0x1542: 0x33c0, 0x1543: 0x33c0, 0x1544: 0x33c0, 0x1545: 0x33c0, 0x1546: 0x33c0, 0x1547: 0x33c0, 0x1548: 0x33c0, 0x1549: 0x33c0, 0x154a: 0x33c0, 0x154b: 0x33c0, 0x154c: 0x33c0, 0x154d: 0x33c0, 0x154e: 0x33c0, 0x154f: 0x33c0, 0x1550: 0xaeda, 0x1551: 0x7d55, 0x1552: 0x0040, 0x1553: 0xaeea, 0x1554: 0x03c2, 0x1555: 0xaefa, 0x1556: 0xaf0a, 0x1557: 0x7d75, 0x1558: 0x7d95, 0x1559: 0x0040, 0x155a: 0x0040, 0x155b: 0x0040, 0x155c: 0x0040, 0x155d: 0x0040, 0x155e: 0x0040, 0x155f: 0x0040, 0x1560: 0x3308, 0x1561: 0x3308, 0x1562: 0x3308, 0x1563: 0x3308, 0x1564: 0x3308, 0x1565: 0x3308, 0x1566: 0x3308, 0x1567: 0x3308, 0x1568: 0x3308, 0x1569: 0x3308, 0x156a: 0x3308, 0x156b: 0x3308, 0x156c: 0x3308, 0x156d: 0x3308, 0x156e: 0x3308, 0x156f: 0x3308, 0x1570: 0x0040, 0x1571: 0x7db5, 0x1572: 0x7dd5, 0x1573: 0xaf1a, 0x1574: 0xaf1a, 0x1575: 0x1fd2, 0x1576: 0x1fe2, 0x1577: 0xaf2a, 0x1578: 0xaf3a, 0x1579: 0x7df5, 0x157a: 0x7e15, 0x157b: 0x7e35, 0x157c: 0x7df5, 0x157d: 0x7e55, 0x157e: 0x7e75, 0x157f: 0x7e55, // Block 0x56, offset 0x1580 0x1580: 0x7e95, 0x1581: 0x7eb5, 0x1582: 0x7ed5, 0x1583: 0x7eb5, 0x1584: 0x7ef5, 0x1585: 0x0018, 0x1586: 0x0018, 0x1587: 0xaf4a, 0x1588: 0xaf5a, 0x1589: 0x7f16, 0x158a: 0x7f36, 0x158b: 0x7f56, 0x158c: 0x7f76, 0x158d: 0xaf1a, 0x158e: 0xaf1a, 0x158f: 0xaf1a, 0x1590: 0xaeda, 0x1591: 0x7f95, 0x1592: 0x0040, 0x1593: 0x0040, 0x1594: 0x03c2, 0x1595: 0xaeea, 0x1596: 0xaf0a, 0x1597: 0xaefa, 0x1598: 0x7fb5, 0x1599: 0x1fd2, 0x159a: 0x1fe2, 0x159b: 0xaf2a, 0x159c: 0xaf3a, 0x159d: 0x7e95, 0x159e: 0x7ef5, 0x159f: 0xaf6a, 0x15a0: 0xaf7a, 0x15a1: 0xaf8a, 0x15a2: 0x1fb2, 0x15a3: 0xaf99, 0x15a4: 0xafaa, 0x15a5: 0xafba, 0x15a6: 0x1fc2, 0x15a7: 0x0040, 0x15a8: 0xafca, 0x15a9: 0xafda, 0x15aa: 0xafea, 0x15ab: 0xaffa, 0x15ac: 0x0040, 0x15ad: 0x0040, 0x15ae: 0x0040, 0x15af: 0x0040, 0x15b0: 0x7fd6, 0x15b1: 0xb009, 0x15b2: 0x7ff6, 0x15b3: 0x0808, 0x15b4: 0x8016, 0x15b5: 0x0040, 0x15b6: 0x8036, 0x15b7: 0xb031, 0x15b8: 0x8056, 0x15b9: 0xb059, 0x15ba: 0x8076, 0x15bb: 0xb081, 0x15bc: 0x8096, 0x15bd: 0xb0a9, 0x15be: 0x80b6, 0x15bf: 0xb0d1, // Block 0x57, offset 0x15c0 0x15c0: 0xb0f9, 0x15c1: 0xb111, 0x15c2: 0xb111, 0x15c3: 0xb129, 0x15c4: 0xb129, 0x15c5: 0xb141, 0x15c6: 0xb141, 0x15c7: 0xb159, 0x15c8: 0xb159, 0x15c9: 0xb171, 0x15ca: 0xb171, 0x15cb: 0xb171, 0x15cc: 0xb171, 0x15cd: 0xb189, 0x15ce: 0xb189, 0x15cf: 0xb1a1, 0x15d0: 0xb1a1, 0x15d1: 0xb1a1, 0x15d2: 0xb1a1, 0x15d3: 0xb1b9, 0x15d4: 0xb1b9, 0x15d5: 0xb1d1, 0x15d6: 0xb1d1, 0x15d7: 0xb1d1, 0x15d8: 0xb1d1, 0x15d9: 0xb1e9, 0x15da: 0xb1e9, 0x15db: 0xb1e9, 0x15dc: 0xb1e9, 0x15dd: 0xb201, 0x15de: 0xb201, 0x15df: 0xb201, 0x15e0: 0xb201, 0x15e1: 0xb219, 0x15e2: 0xb219, 0x15e3: 0xb219, 0x15e4: 0xb219, 0x15e5: 0xb231, 0x15e6: 0xb231, 0x15e7: 0xb231, 0x15e8: 0xb231, 0x15e9: 0xb249, 0x15ea: 0xb249, 0x15eb: 0xb261, 0x15ec: 0xb261, 0x15ed: 0xb279, 0x15ee: 0xb279, 0x15ef: 0xb291, 0x15f0: 0xb291, 0x15f1: 0xb2a9, 0x15f2: 0xb2a9, 0x15f3: 0xb2a9, 0x15f4: 0xb2a9, 0x15f5: 0xb2c1, 0x15f6: 0xb2c1, 0x15f7: 0xb2c1, 0x15f8: 0xb2c1, 0x15f9: 0xb2d9, 0x15fa: 0xb2d9, 0x15fb: 0xb2d9, 0x15fc: 0xb2d9, 0x15fd: 0xb2f1, 0x15fe: 0xb2f1, 0x15ff: 0xb2f1, // Block 0x58, offset 0x1600 0x1600: 0xb2f1, 0x1601: 0xb309, 0x1602: 0xb309, 0x1603: 0xb309, 0x1604: 0xb309, 0x1605: 0xb321, 0x1606: 0xb321, 0x1607: 0xb321, 0x1608: 0xb321, 0x1609: 0xb339, 0x160a: 0xb339, 0x160b: 0xb339, 0x160c: 0xb339, 0x160d: 0xb351, 0x160e: 0xb351, 0x160f: 0xb351, 0x1610: 0xb351, 0x1611: 0xb369, 0x1612: 0xb369, 0x1613: 0xb369, 0x1614: 0xb369, 0x1615: 0xb381, 0x1616: 0xb381, 0x1617: 0xb381, 0x1618: 0xb381, 0x1619: 0xb399, 0x161a: 0xb399, 0x161b: 0xb399, 0x161c: 0xb399, 0x161d: 0xb3b1, 0x161e: 0xb3b1, 0x161f: 0xb3b1, 0x1620: 0xb3b1, 0x1621: 0xb3c9, 0x1622: 0xb3c9, 0x1623: 0xb3c9, 0x1624: 0xb3c9, 0x1625: 0xb3e1, 0x1626: 0xb3e1, 0x1627: 0xb3e1, 0x1628: 0xb3e1, 0x1629: 0xb3f9, 0x162a: 0xb3f9, 0x162b: 0xb3f9, 0x162c: 0xb3f9, 0x162d: 0xb411, 0x162e: 0xb411, 0x162f: 0x7ab1, 0x1630: 0x7ab1, 0x1631: 0xb429, 0x1632: 0xb429, 0x1633: 0xb429, 0x1634: 0xb429, 0x1635: 0xb441, 0x1636: 0xb441, 0x1637: 0xb469, 0x1638: 0xb469, 0x1639: 0xb491, 0x163a: 0xb491, 0x163b: 0xb4b9, 0x163c: 0xb4b9, 0x163d: 0x0040, 0x163e: 0x0040, 0x163f: 0x03c0, // Block 0x59, offset 0x1640 0x1640: 0x0040, 0x1641: 0xaefa, 0x1642: 0xb4e2, 0x1643: 0xaf6a, 0x1644: 0xafda, 0x1645: 0xafea, 0x1646: 0xaf7a, 0x1647: 0xb4f2, 0x1648: 0x1fd2, 0x1649: 0x1fe2, 0x164a: 0xaf8a, 0x164b: 0x1fb2, 0x164c: 0xaeda, 0x164d: 0xaf99, 0x164e: 0x29d1, 0x164f: 0xb502, 0x1650: 0x1f41, 0x1651: 0x00c9, 0x1652: 0x0069, 0x1653: 0x0079, 0x1654: 0x1f51, 0x1655: 0x1f61, 0x1656: 0x1f71, 0x1657: 0x1f81, 0x1658: 0x1f91, 0x1659: 0x1fa1, 0x165a: 0xaeea, 0x165b: 0x03c2, 0x165c: 0xafaa, 0x165d: 0x1fc2, 0x165e: 0xafba, 0x165f: 0xaf0a, 0x1660: 0xaffa, 0x1661: 0x0039, 0x1662: 0x0ee9, 0x1663: 0x1159, 0x1664: 0x0ef9, 0x1665: 0x0f09, 0x1666: 0x1199, 0x1667: 0x0f31, 0x1668: 0x0249, 0x1669: 0x0f41, 0x166a: 0x0259, 0x166b: 0x0f51, 0x166c: 0x0359, 0x166d: 0x0f61, 0x166e: 0x0f71, 0x166f: 0x00d9, 0x1670: 0x0f99, 0x1671: 0x2039, 0x1672: 0x0269, 0x1673: 0x01d9, 0x1674: 0x0fa9, 0x1675: 0x0fb9, 0x1676: 0x1089, 0x1677: 0x0279, 0x1678: 0x0369, 0x1679: 0x0289, 0x167a: 0x13d1, 0x167b: 0xaf4a, 0x167c: 0xafca, 0x167d: 0xaf5a, 0x167e: 0xb512, 0x167f: 0xaf1a, // Block 0x5a, offset 0x1680 0x1680: 0x1caa, 0x1681: 0x0039, 0x1682: 0x0ee9, 0x1683: 0x1159, 0x1684: 0x0ef9, 0x1685: 0x0f09, 0x1686: 0x1199, 0x1687: 0x0f31, 0x1688: 0x0249, 0x1689: 0x0f41, 0x168a: 0x0259, 0x168b: 0x0f51, 0x168c: 0x0359, 0x168d: 0x0f61, 0x168e: 0x0f71, 0x168f: 0x00d9, 0x1690: 0x0f99, 0x1691: 0x2039, 0x1692: 0x0269, 0x1693: 0x01d9, 0x1694: 0x0fa9, 0x1695: 0x0fb9, 0x1696: 0x1089, 0x1697: 0x0279, 0x1698: 0x0369, 0x1699: 0x0289, 0x169a: 0x13d1, 0x169b: 0xaf2a, 0x169c: 0xb522, 0x169d: 0xaf3a, 0x169e: 0xb532, 0x169f: 0x80d5, 0x16a0: 0x80f5, 0x16a1: 0x29d1, 0x16a2: 0x8115, 0x16a3: 0x8115, 0x16a4: 0x8135, 0x16a5: 0x8155, 0x16a6: 0x8175, 0x16a7: 0x8195, 0x16a8: 0x81b5, 0x16a9: 0x81d5, 0x16aa: 0x81f5, 0x16ab: 0x8215, 0x16ac: 0x8235, 0x16ad: 0x8255, 0x16ae: 0x8275, 0x16af: 0x8295, 0x16b0: 0x82b5, 0x16b1: 0x82d5, 0x16b2: 0x82f5, 0x16b3: 0x8315, 0x16b4: 0x8335, 0x16b5: 0x8355, 0x16b6: 0x8375, 0x16b7: 0x8395, 0x16b8: 0x83b5, 0x16b9: 0x83d5, 0x16ba: 0x83f5, 0x16bb: 0x8415, 0x16bc: 0x81b5, 0x16bd: 0x8435, 0x16be: 0x8455, 0x16bf: 0x8215, // Block 0x5b, offset 0x16c0 0x16c0: 0x8475, 0x16c1: 0x8495, 0x16c2: 0x84b5, 0x16c3: 0x84d5, 0x16c4: 0x84f5, 0x16c5: 0x8515, 0x16c6: 0x8535, 0x16c7: 0x8555, 0x16c8: 0x84d5, 0x16c9: 0x8575, 0x16ca: 0x84d5, 0x16cb: 0x8595, 0x16cc: 0x8595, 0x16cd: 0x85b5, 0x16ce: 0x85b5, 0x16cf: 0x85d5, 0x16d0: 0x8515, 0x16d1: 0x85f5, 0x16d2: 0x8615, 0x16d3: 0x85f5, 0x16d4: 0x8635, 0x16d5: 0x8615, 0x16d6: 0x8655, 0x16d7: 0x8655, 0x16d8: 0x8675, 0x16d9: 0x8675, 0x16da: 0x8695, 0x16db: 0x8695, 0x16dc: 0x8615, 0x16dd: 0x8115, 0x16de: 0x86b5, 0x16df: 0x86d5, 0x16e0: 0x0040, 0x16e1: 0x86f5, 0x16e2: 0x8715, 0x16e3: 0x8735, 0x16e4: 0x8755, 0x16e5: 0x8735, 0x16e6: 0x8775, 0x16e7: 0x8795, 0x16e8: 0x87b5, 0x16e9: 0x87b5, 0x16ea: 0x87d5, 0x16eb: 0x87d5, 0x16ec: 0x87f5, 0x16ed: 0x87f5, 0x16ee: 0x87d5, 0x16ef: 0x87d5, 0x16f0: 0x8815, 0x16f1: 0x8835, 0x16f2: 0x8855, 0x16f3: 0x8875, 0x16f4: 0x8895, 0x16f5: 0x88b5, 0x16f6: 0x88b5, 0x16f7: 0x88b5, 0x16f8: 0x88d5, 0x16f9: 0x88d5, 0x16fa: 0x88d5, 0x16fb: 0x88d5, 0x16fc: 0x87b5, 0x16fd: 0x87b5, 0x16fe: 0x87b5, 0x16ff: 0x0040, // Block 0x5c, offset 0x1700 0x1700: 0x0040, 0x1701: 0x0040, 0x1702: 0x8715, 0x1703: 0x86f5, 0x1704: 0x88f5, 0x1705: 0x86f5, 0x1706: 0x8715, 0x1707: 0x86f5, 0x1708: 0x0040, 0x1709: 0x0040, 0x170a: 0x8915, 0x170b: 0x8715, 0x170c: 0x8935, 0x170d: 0x88f5, 0x170e: 0x8935, 0x170f: 0x8715, 0x1710: 0x0040, 0x1711: 0x0040, 0x1712: 0x8955, 0x1713: 0x8975, 0x1714: 0x8875, 0x1715: 0x8935, 0x1716: 0x88f5, 0x1717: 0x8935, 0x1718: 0x0040, 0x1719: 0x0040, 0x171a: 0x8995, 0x171b: 0x89b5, 0x171c: 0x8995, 0x171d: 0x0040, 0x171e: 0x0040, 0x171f: 0x0040, 0x1720: 0xb541, 0x1721: 0xb559, 0x1722: 0xb571, 0x1723: 0x89d6, 0x1724: 0xb589, 0x1725: 0xb5a1, 0x1726: 0x89f5, 0x1727: 0x0040, 0x1728: 0x8a15, 0x1729: 0x8a35, 0x172a: 0x8a55, 0x172b: 0x8a35, 0x172c: 0x8a75, 0x172d: 0x8a95, 0x172e: 0x8ab5, 0x172f: 0x0040, 0x1730: 0x0040, 0x1731: 0x0040, 0x1732: 0x0040, 0x1733: 0x0040, 0x1734: 0x0040, 0x1735: 0x0040, 0x1736: 0x0040, 0x1737: 0x0040, 0x1738: 0x0040, 0x1739: 0x0340, 0x173a: 0x0340, 0x173b: 0x0340, 0x173c: 0x0040, 0x173d: 0x0040, 0x173e: 0x0040, 0x173f: 0x0040, // Block 0x5d, offset 0x1740 0x1740: 0x0a08, 0x1741: 0x0a08, 0x1742: 0x0a08, 0x1743: 0x0a08, 0x1744: 0x0a08, 0x1745: 0x0c08, 0x1746: 0x0808, 0x1747: 0x0c08, 0x1748: 0x0818, 0x1749: 0x0c08, 0x174a: 0x0c08, 0x174b: 0x0808, 0x174c: 0x0808, 0x174d: 0x0908, 0x174e: 0x0c08, 0x174f: 0x0c08, 0x1750: 0x0c08, 0x1751: 0x0c08, 0x1752: 0x0c08, 0x1753: 0x0a08, 0x1754: 0x0a08, 0x1755: 0x0a08, 0x1756: 0x0a08, 0x1757: 0x0908, 0x1758: 0x0a08, 0x1759: 0x0a08, 0x175a: 0x0a08, 0x175b: 0x0a08, 0x175c: 0x0a08, 0x175d: 0x0c08, 0x175e: 0x0a08, 0x175f: 0x0a08, 0x1760: 0x0a08, 0x1761: 0x0c08, 0x1762: 0x0808, 0x1763: 0x0808, 0x1764: 0x0c08, 0x1765: 0x3308, 0x1766: 0x3308, 0x1767: 0x0040, 0x1768: 0x0040, 0x1769: 0x0040, 0x176a: 0x0040, 0x176b: 0x0a18, 0x176c: 0x0a18, 0x176d: 0x0a18, 0x176e: 0x0a18, 0x176f: 0x0c18, 0x1770: 0x0818, 0x1771: 0x0818, 0x1772: 0x0818, 0x1773: 0x0818, 0x1774: 0x0818, 0x1775: 0x0818, 0x1776: 0x0818, 0x1777: 0x0040, 0x1778: 0x0040, 0x1779: 0x0040, 0x177a: 0x0040, 0x177b: 0x0040, 0x177c: 0x0040, 0x177d: 0x0040, 0x177e: 0x0040, 0x177f: 0x0040, // Block 0x5e, offset 0x1780 0x1780: 0x0a08, 0x1781: 0x0c08, 0x1782: 0x0a08, 0x1783: 0x0c08, 0x1784: 0x0c08, 0x1785: 0x0c08, 0x1786: 0x0a08, 0x1787: 0x0a08, 0x1788: 0x0a08, 0x1789: 0x0c08, 0x178a: 0x0a08, 0x178b: 0x0a08, 0x178c: 0x0c08, 0x178d: 0x0a08, 0x178e: 0x0c08, 0x178f: 0x0c08, 0x1790: 0x0a08, 0x1791: 0x0c08, 0x1792: 0x0040, 0x1793: 0x0040, 0x1794: 0x0040, 0x1795: 0x0040, 0x1796: 0x0040, 0x1797: 0x0040, 0x1798: 0x0040, 0x1799: 0x0818, 0x179a: 0x0818, 0x179b: 0x0818, 0x179c: 0x0818, 0x179d: 0x0040, 0x179e: 0x0040, 0x179f: 0x0040, 0x17a0: 0x0040, 0x17a1: 0x0040, 0x17a2: 0x0040, 0x17a3: 0x0040, 0x17a4: 0x0040, 0x17a5: 0x0040, 0x17a6: 0x0040, 0x17a7: 0x0040, 0x17a8: 0x0040, 0x17a9: 0x0c18, 0x17aa: 0x0c18, 0x17ab: 0x0c18, 0x17ac: 0x0c18, 0x17ad: 0x0a18, 0x17ae: 0x0a18, 0x17af: 0x0818, 0x17b0: 0x0040, 0x17b1: 0x0040, 0x17b2: 0x0040, 0x17b3: 0x0040, 0x17b4: 0x0040, 0x17b5: 0x0040, 0x17b6: 0x0040, 0x17b7: 0x0040, 0x17b8: 0x0040, 0x17b9: 0x0040, 0x17ba: 0x0040, 0x17bb: 0x0040, 0x17bc: 0x0040, 0x17bd: 0x0040, 0x17be: 0x0040, 0x17bf: 0x0040, // Block 0x5f, offset 0x17c0 0x17c0: 0x3308, 0x17c1: 0x3308, 0x17c2: 0x3008, 0x17c3: 0x3008, 0x17c4: 0x0040, 0x17c5: 0x0008, 0x17c6: 0x0008, 0x17c7: 0x0008, 0x17c8: 0x0008, 0x17c9: 0x0008, 0x17ca: 0x0008, 0x17cb: 0x0008, 0x17cc: 0x0008, 0x17cd: 0x0040, 0x17ce: 0x0040, 0x17cf: 0x0008, 0x17d0: 0x0008, 0x17d1: 0x0040, 0x17d2: 0x0040, 0x17d3: 0x0008, 0x17d4: 0x0008, 0x17d5: 0x0008, 0x17d6: 0x0008, 0x17d7: 0x0008, 0x17d8: 0x0008, 0x17d9: 0x0008, 0x17da: 0x0008, 0x17db: 0x0008, 0x17dc: 0x0008, 0x17dd: 0x0008, 0x17de: 0x0008, 0x17df: 0x0008, 0x17e0: 0x0008, 0x17e1: 0x0008, 0x17e2: 0x0008, 0x17e3: 0x0008, 0x17e4: 0x0008, 0x17e5: 0x0008, 0x17e6: 0x0008, 0x17e7: 0x0008, 0x17e8: 0x0008, 0x17e9: 0x0040, 0x17ea: 0x0008, 0x17eb: 0x0008, 0x17ec: 0x0008, 0x17ed: 0x0008, 0x17ee: 0x0008, 0x17ef: 0x0008, 0x17f0: 0x0008, 0x17f1: 0x0040, 0x17f2: 0x0008, 0x17f3: 0x0008, 0x17f4: 0x0040, 0x17f5: 0x0008, 0x17f6: 0x0008, 0x17f7: 0x0008, 0x17f8: 0x0008, 0x17f9: 0x0008, 0x17fa: 0x0040, 0x17fb: 0x3308, 0x17fc: 0x3308, 0x17fd: 0x0008, 0x17fe: 0x3008, 0x17ff: 0x3008, // Block 0x60, offset 0x1800 0x1800: 0x3308, 0x1801: 0x3008, 0x1802: 0x3008, 0x1803: 0x3008, 0x1804: 0x3008, 0x1805: 0x0040, 0x1806: 0x0040, 0x1807: 0x3008, 0x1808: 0x3008, 0x1809: 0x0040, 0x180a: 0x0040, 0x180b: 0x3008, 0x180c: 0x3008, 0x180d: 0x3808, 0x180e: 0x0040, 0x180f: 0x0040, 0x1810: 0x0008, 0x1811: 0x0040, 0x1812: 0x0040, 0x1813: 0x0040, 0x1814: 0x0040, 0x1815: 0x0040, 0x1816: 0x0040, 0x1817: 0x3008, 0x1818: 0x0040, 0x1819: 0x0040, 0x181a: 0x0040, 0x181b: 0x0040, 0x181c: 0x0040, 0x181d: 0x0008, 0x181e: 0x0008, 0x181f: 0x0008, 0x1820: 0x0008, 0x1821: 0x0008, 0x1822: 0x3008, 0x1823: 0x3008, 0x1824: 0x0040, 0x1825: 0x0040, 0x1826: 0x3308, 0x1827: 0x3308, 0x1828: 0x3308, 0x1829: 0x3308, 0x182a: 0x3308, 0x182b: 0x3308, 0x182c: 0x3308, 0x182d: 0x0040, 0x182e: 0x0040, 0x182f: 0x0040, 0x1830: 0x3308, 0x1831: 0x3308, 0x1832: 0x3308, 0x1833: 0x3308, 0x1834: 0x3308, 0x1835: 0x0040, 0x1836: 0x0040, 0x1837: 0x0040, 0x1838: 0x0040, 0x1839: 0x0040, 0x183a: 0x0040, 0x183b: 0x0040, 0x183c: 0x0040, 0x183d: 0x0040, 0x183e: 0x0040, 0x183f: 0x0040, // Block 0x61, offset 0x1840 0x1840: 0x0039, 0x1841: 0x0ee9, 0x1842: 0x1159, 0x1843: 0x0ef9, 0x1844: 0x0f09, 0x1845: 0x1199, 0x1846: 0x0f31, 0x1847: 0x0249, 0x1848: 0x0f41, 0x1849: 0x0259, 0x184a: 0x0f51, 0x184b: 0x0359, 0x184c: 0x0f61, 0x184d: 0x0f71, 0x184e: 0x00d9, 0x184f: 0x0f99, 0x1850: 0x2039, 0x1851: 0x0269, 0x1852: 0x01d9, 0x1853: 0x0fa9, 0x1854: 0x0fb9, 0x1855: 0x1089, 0x1856: 0x0279, 0x1857: 0x0369, 0x1858: 0x0289, 0x1859: 0x13d1, 0x185a: 0x0039, 0x185b: 0x0ee9, 0x185c: 0x1159, 0x185d: 0x0ef9, 0x185e: 0x0f09, 0x185f: 0x1199, 0x1860: 0x0f31, 0x1861: 0x0249, 0x1862: 0x0f41, 0x1863: 0x0259, 0x1864: 0x0f51, 0x1865: 0x0359, 0x1866: 0x0f61, 0x1867: 0x0f71, 0x1868: 0x00d9, 0x1869: 0x0f99, 0x186a: 0x2039, 0x186b: 0x0269, 0x186c: 0x01d9, 0x186d: 0x0fa9, 0x186e: 0x0fb9, 0x186f: 0x1089, 0x1870: 0x0279, 0x1871: 0x0369, 0x1872: 0x0289, 0x1873: 0x13d1, 0x1874: 0x0039, 0x1875: 0x0ee9, 0x1876: 0x1159, 0x1877: 0x0ef9, 0x1878: 0x0f09, 0x1879: 0x1199, 0x187a: 0x0f31, 0x187b: 0x0249, 0x187c: 0x0f41, 0x187d: 0x0259, 0x187e: 0x0f51, 0x187f: 0x0359, // Block 0x62, offset 0x1880 0x1880: 0x0f61, 0x1881: 0x0f71, 0x1882: 0x00d9, 0x1883: 0x0f99, 0x1884: 0x2039, 0x1885: 0x0269, 0x1886: 0x01d9, 0x1887: 0x0fa9, 0x1888: 0x0fb9, 0x1889: 0x1089, 0x188a: 0x0279, 0x188b: 0x0369, 0x188c: 0x0289, 0x188d: 0x13d1, 0x188e: 0x0039, 0x188f: 0x0ee9, 0x1890: 0x1159, 0x1891: 0x0ef9, 0x1892: 0x0f09, 0x1893: 0x1199, 0x1894: 0x0f31, 0x1895: 0x0040, 0x1896: 0x0f41, 0x1897: 0x0259, 0x1898: 0x0f51, 0x1899: 0x0359, 0x189a: 0x0f61, 0x189b: 0x0f71, 0x189c: 0x00d9, 0x189d: 0x0f99, 0x189e: 0x2039, 0x189f: 0x0269, 0x18a0: 0x01d9, 0x18a1: 0x0fa9, 0x18a2: 0x0fb9, 0x18a3: 0x1089, 0x18a4: 0x0279, 0x18a5: 0x0369, 0x18a6: 0x0289, 0x18a7: 0x13d1, 0x18a8: 0x0039, 0x18a9: 0x0ee9, 0x18aa: 0x1159, 0x18ab: 0x0ef9, 0x18ac: 0x0f09, 0x18ad: 0x1199, 0x18ae: 0x0f31, 0x18af: 0x0249, 0x18b0: 0x0f41, 0x18b1: 0x0259, 0x18b2: 0x0f51, 0x18b3: 0x0359, 0x18b4: 0x0f61, 0x18b5: 0x0f71, 0x18b6: 0x00d9, 0x18b7: 0x0f99, 0x18b8: 0x2039, 0x18b9: 0x0269, 0x18ba: 0x01d9, 0x18bb: 0x0fa9, 0x18bc: 0x0fb9, 0x18bd: 0x1089, 0x18be: 0x0279, 0x18bf: 0x0369, // Block 0x63, offset 0x18c0 0x18c0: 0x0289, 0x18c1: 0x13d1, 0x18c2: 0x0039, 0x18c3: 0x0ee9, 0x18c4: 0x1159, 0x18c5: 0x0ef9, 0x18c6: 0x0f09, 0x18c7: 0x1199, 0x18c8: 0x0f31, 0x18c9: 0x0249, 0x18ca: 0x0f41, 0x18cb: 0x0259, 0x18cc: 0x0f51, 0x18cd: 0x0359, 0x18ce: 0x0f61, 0x18cf: 0x0f71, 0x18d0: 0x00d9, 0x18d1: 0x0f99, 0x18d2: 0x2039, 0x18d3: 0x0269, 0x18d4: 0x01d9, 0x18d5: 0x0fa9, 0x18d6: 0x0fb9, 0x18d7: 0x1089, 0x18d8: 0x0279, 0x18d9: 0x0369, 0x18da: 0x0289, 0x18db: 0x13d1, 0x18dc: 0x0039, 0x18dd: 0x0040, 0x18de: 0x1159, 0x18df: 0x0ef9, 0x18e0: 0x0040, 0x18e1: 0x0040, 0x18e2: 0x0f31, 0x18e3: 0x0040, 0x18e4: 0x0040, 0x18e5: 0x0259, 0x18e6: 0x0f51, 0x18e7: 0x0040, 0x18e8: 0x0040, 0x18e9: 0x0f71, 0x18ea: 0x00d9, 0x18eb: 0x0f99, 0x18ec: 0x2039, 0x18ed: 0x0040, 0x18ee: 0x01d9, 0x18ef: 0x0fa9, 0x18f0: 0x0fb9, 0x18f1: 0x1089, 0x18f2: 0x0279, 0x18f3: 0x0369, 0x18f4: 0x0289, 0x18f5: 0x13d1, 0x18f6: 0x0039, 0x18f7: 0x0ee9, 0x18f8: 0x1159, 0x18f9: 0x0ef9, 0x18fa: 0x0040, 0x18fb: 0x1199, 0x18fc: 0x0040, 0x18fd: 0x0249, 0x18fe: 0x0f41, 0x18ff: 0x0259, // Block 0x64, offset 0x1900 0x1900: 0x0f51, 0x1901: 0x0359, 0x1902: 0x0f61, 0x1903: 0x0f71, 0x1904: 0x0040, 0x1905: 0x0f99, 0x1906: 0x2039, 0x1907: 0x0269, 0x1908: 0x01d9, 0x1909: 0x0fa9, 0x190a: 0x0fb9, 0x190b: 0x1089, 0x190c: 0x0279, 0x190d: 0x0369, 0x190e: 0x0289, 0x190f: 0x13d1, 0x1910: 0x0039, 0x1911: 0x0ee9, 0x1912: 0x1159, 0x1913: 0x0ef9, 0x1914: 0x0f09, 0x1915: 0x1199, 0x1916: 0x0f31, 0x1917: 0x0249, 0x1918: 0x0f41, 0x1919: 0x0259, 0x191a: 0x0f51, 0x191b: 0x0359, 0x191c: 0x0f61, 0x191d: 0x0f71, 0x191e: 0x00d9, 0x191f: 0x0f99, 0x1920: 0x2039, 0x1921: 0x0269, 0x1922: 0x01d9, 0x1923: 0x0fa9, 0x1924: 0x0fb9, 0x1925: 0x1089, 0x1926: 0x0279, 0x1927: 0x0369, 0x1928: 0x0289, 0x1929: 0x13d1, 0x192a: 0x0039, 0x192b: 0x0ee9, 0x192c: 0x1159, 0x192d: 0x0ef9, 0x192e: 0x0f09, 0x192f: 0x1199, 0x1930: 0x0f31, 0x1931: 0x0249, 0x1932: 0x0f41, 0x1933: 0x0259, 0x1934: 0x0f51, 0x1935: 0x0359, 0x1936: 0x0f61, 0x1937: 0x0f71, 0x1938: 0x00d9, 0x1939: 0x0f99, 0x193a: 0x2039, 0x193b: 0x0269, 0x193c: 0x01d9, 0x193d: 0x0fa9, 0x193e: 0x0fb9, 0x193f: 0x1089, // Block 0x65, offset 0x1940 0x1940: 0x0279, 0x1941: 0x0369, 0x1942: 0x0289, 0x1943: 0x13d1, 0x1944: 0x0039, 0x1945: 0x0ee9, 0x1946: 0x0040, 0x1947: 0x0ef9, 0x1948: 0x0f09, 0x1949: 0x1199, 0x194a: 0x0f31, 0x194b: 0x0040, 0x194c: 0x0040, 0x194d: 0x0259, 0x194e: 0x0f51, 0x194f: 0x0359, 0x1950: 0x0f61, 0x1951: 0x0f71, 0x1952: 0x00d9, 0x1953: 0x0f99, 0x1954: 0x2039, 0x1955: 0x0040, 0x1956: 0x01d9, 0x1957: 0x0fa9, 0x1958: 0x0fb9, 0x1959: 0x1089, 0x195a: 0x0279, 0x195b: 0x0369, 0x195c: 0x0289, 0x195d: 0x0040, 0x195e: 0x0039, 0x195f: 0x0ee9, 0x1960: 0x1159, 0x1961: 0x0ef9, 0x1962: 0x0f09, 0x1963: 0x1199, 0x1964: 0x0f31, 0x1965: 0x0249, 0x1966: 0x0f41, 0x1967: 0x0259, 0x1968: 0x0f51, 0x1969: 0x0359, 0x196a: 0x0f61, 0x196b: 0x0f71, 0x196c: 0x00d9, 0x196d: 0x0f99, 0x196e: 0x2039, 0x196f: 0x0269, 0x1970: 0x01d9, 0x1971: 0x0fa9, 0x1972: 0x0fb9, 0x1973: 0x1089, 0x1974: 0x0279, 0x1975: 0x0369, 0x1976: 0x0289, 0x1977: 0x13d1, 0x1978: 0x0039, 0x1979: 0x0ee9, 0x197a: 0x0040, 0x197b: 0x0ef9, 0x197c: 0x0f09, 0x197d: 0x1199, 0x197e: 0x0f31, 0x197f: 0x0040, // Block 0x66, offset 0x1980 0x1980: 0x0f41, 0x1981: 0x0259, 0x1982: 0x0f51, 0x1983: 0x0359, 0x1984: 0x0f61, 0x1985: 0x0040, 0x1986: 0x00d9, 0x1987: 0x0040, 0x1988: 0x0040, 0x1989: 0x0040, 0x198a: 0x01d9, 0x198b: 0x0fa9, 0x198c: 0x0fb9, 0x198d: 0x1089, 0x198e: 0x0279, 0x198f: 0x0369, 0x1990: 0x0289, 0x1991: 0x0040, 0x1992: 0x0039, 0x1993: 0x0ee9, 0x1994: 0x1159, 0x1995: 0x0ef9, 0x1996: 0x0f09, 0x1997: 0x1199, 0x1998: 0x0f31, 0x1999: 0x0249, 0x199a: 0x0f41, 0x199b: 0x0259, 0x199c: 0x0f51, 0x199d: 0x0359, 0x199e: 0x0f61, 0x199f: 0x0f71, 0x19a0: 0x00d9, 0x19a1: 0x0f99, 0x19a2: 0x2039, 0x19a3: 0x0269, 0x19a4: 0x01d9, 0x19a5: 0x0fa9, 0x19a6: 0x0fb9, 0x19a7: 0x1089, 0x19a8: 0x0279, 0x19a9: 0x0369, 0x19aa: 0x0289, 0x19ab: 0x13d1, 0x19ac: 0x0039, 0x19ad: 0x0ee9, 0x19ae: 0x1159, 0x19af: 0x0ef9, 0x19b0: 0x0f09, 0x19b1: 0x1199, 0x19b2: 0x0f31, 0x19b3: 0x0249, 0x19b4: 0x0f41, 0x19b5: 0x0259, 0x19b6: 0x0f51, 0x19b7: 0x0359, 0x19b8: 0x0f61, 0x19b9: 0x0f71, 0x19ba: 0x00d9, 0x19bb: 0x0f99, 0x19bc: 0x2039, 0x19bd: 0x0269, 0x19be: 0x01d9, 0x19bf: 0x0fa9, // Block 0x67, offset 0x19c0 0x19c0: 0x0fb9, 0x19c1: 0x1089, 0x19c2: 0x0279, 0x19c3: 0x0369, 0x19c4: 0x0289, 0x19c5: 0x13d1, 0x19c6: 0x0039, 0x19c7: 0x0ee9, 0x19c8: 0x1159, 0x19c9: 0x0ef9, 0x19ca: 0x0f09, 0x19cb: 0x1199, 0x19cc: 0x0f31, 0x19cd: 0x0249, 0x19ce: 0x0f41, 0x19cf: 0x0259, 0x19d0: 0x0f51, 0x19d1: 0x0359, 0x19d2: 0x0f61, 0x19d3: 0x0f71, 0x19d4: 0x00d9, 0x19d5: 0x0f99, 0x19d6: 0x2039, 0x19d7: 0x0269, 0x19d8: 0x01d9, 0x19d9: 0x0fa9, 0x19da: 0x0fb9, 0x19db: 0x1089, 0x19dc: 0x0279, 0x19dd: 0x0369, 0x19de: 0x0289, 0x19df: 0x13d1, 0x19e0: 0x0039, 0x19e1: 0x0ee9, 0x19e2: 0x1159, 0x19e3: 0x0ef9, 0x19e4: 0x0f09, 0x19e5: 0x1199, 0x19e6: 0x0f31, 0x19e7: 0x0249, 0x19e8: 0x0f41, 0x19e9: 0x0259, 0x19ea: 0x0f51, 0x19eb: 0x0359, 0x19ec: 0x0f61, 0x19ed: 0x0f71, 0x19ee: 0x00d9, 0x19ef: 0x0f99, 0x19f0: 0x2039, 0x19f1: 0x0269, 0x19f2: 0x01d9, 0x19f3: 0x0fa9, 0x19f4: 0x0fb9, 0x19f5: 0x1089, 0x19f6: 0x0279, 0x19f7: 0x0369, 0x19f8: 0x0289, 0x19f9: 0x13d1, 0x19fa: 0x0039, 0x19fb: 0x0ee9, 0x19fc: 0x1159, 0x19fd: 0x0ef9, 0x19fe: 0x0f09, 0x19ff: 0x1199, // Block 0x68, offset 0x1a00 0x1a00: 0x0f31, 0x1a01: 0x0249, 0x1a02: 0x0f41, 0x1a03: 0x0259, 0x1a04: 0x0f51, 0x1a05: 0x0359, 0x1a06: 0x0f61, 0x1a07: 0x0f71, 0x1a08: 0x00d9, 0x1a09: 0x0f99, 0x1a0a: 0x2039, 0x1a0b: 0x0269, 0x1a0c: 0x01d9, 0x1a0d: 0x0fa9, 0x1a0e: 0x0fb9, 0x1a0f: 0x1089, 0x1a10: 0x0279, 0x1a11: 0x0369, 0x1a12: 0x0289, 0x1a13: 0x13d1, 0x1a14: 0x0039, 0x1a15: 0x0ee9, 0x1a16: 0x1159, 0x1a17: 0x0ef9, 0x1a18: 0x0f09, 0x1a19: 0x1199, 0x1a1a: 0x0f31, 0x1a1b: 0x0249, 0x1a1c: 0x0f41, 0x1a1d: 0x0259, 0x1a1e: 0x0f51, 0x1a1f: 0x0359, 0x1a20: 0x0f61, 0x1a21: 0x0f71, 0x1a22: 0x00d9, 0x1a23: 0x0f99, 0x1a24: 0x2039, 0x1a25: 0x0269, 0x1a26: 0x01d9, 0x1a27: 0x0fa9, 0x1a28: 0x0fb9, 0x1a29: 0x1089, 0x1a2a: 0x0279, 0x1a2b: 0x0369, 0x1a2c: 0x0289, 0x1a2d: 0x13d1, 0x1a2e: 0x0039, 0x1a2f: 0x0ee9, 0x1a30: 0x1159, 0x1a31: 0x0ef9, 0x1a32: 0x0f09, 0x1a33: 0x1199, 0x1a34: 0x0f31, 0x1a35: 0x0249, 0x1a36: 0x0f41, 0x1a37: 0x0259, 0x1a38: 0x0f51, 0x1a39: 0x0359, 0x1a3a: 0x0f61, 0x1a3b: 0x0f71, 0x1a3c: 0x00d9, 0x1a3d: 0x0f99, 0x1a3e: 0x2039, 0x1a3f: 0x0269, // Block 0x69, offset 0x1a40 0x1a40: 0x01d9, 0x1a41: 0x0fa9, 0x1a42: 0x0fb9, 0x1a43: 0x1089, 0x1a44: 0x0279, 0x1a45: 0x0369, 0x1a46: 0x0289, 0x1a47: 0x13d1, 0x1a48: 0x0039, 0x1a49: 0x0ee9, 0x1a4a: 0x1159, 0x1a4b: 0x0ef9, 0x1a4c: 0x0f09, 0x1a4d: 0x1199, 0x1a4e: 0x0f31, 0x1a4f: 0x0249, 0x1a50: 0x0f41, 0x1a51: 0x0259, 0x1a52: 0x0f51, 0x1a53: 0x0359, 0x1a54: 0x0f61, 0x1a55: 0x0f71, 0x1a56: 0x00d9, 0x1a57: 0x0f99, 0x1a58: 0x2039, 0x1a59: 0x0269, 0x1a5a: 0x01d9, 0x1a5b: 0x0fa9, 0x1a5c: 0x0fb9, 0x1a5d: 0x1089, 0x1a5e: 0x0279, 0x1a5f: 0x0369, 0x1a60: 0x0289, 0x1a61: 0x13d1, 0x1a62: 0x0039, 0x1a63: 0x0ee9, 0x1a64: 0x1159, 0x1a65: 0x0ef9, 0x1a66: 0x0f09, 0x1a67: 0x1199, 0x1a68: 0x0f31, 0x1a69: 0x0249, 0x1a6a: 0x0f41, 0x1a6b: 0x0259, 0x1a6c: 0x0f51, 0x1a6d: 0x0359, 0x1a6e: 0x0f61, 0x1a6f: 0x0f71, 0x1a70: 0x00d9, 0x1a71: 0x0f99, 0x1a72: 0x2039, 0x1a73: 0x0269, 0x1a74: 0x01d9, 0x1a75: 0x0fa9, 0x1a76: 0x0fb9, 0x1a77: 0x1089, 0x1a78: 0x0279, 0x1a79: 0x0369, 0x1a7a: 0x0289, 0x1a7b: 0x13d1, 0x1a7c: 0x0039, 0x1a7d: 0x0ee9, 0x1a7e: 0x1159, 0x1a7f: 0x0ef9, // Block 0x6a, offset 0x1a80 0x1a80: 0x0f09, 0x1a81: 0x1199, 0x1a82: 0x0f31, 0x1a83: 0x0249, 0x1a84: 0x0f41, 0x1a85: 0x0259, 0x1a86: 0x0f51, 0x1a87: 0x0359, 0x1a88: 0x0f61, 0x1a89: 0x0f71, 0x1a8a: 0x00d9, 0x1a8b: 0x0f99, 0x1a8c: 0x2039, 0x1a8d: 0x0269, 0x1a8e: 0x01d9, 0x1a8f: 0x0fa9, 0x1a90: 0x0fb9, 0x1a91: 0x1089, 0x1a92: 0x0279, 0x1a93: 0x0369, 0x1a94: 0x0289, 0x1a95: 0x13d1, 0x1a96: 0x0039, 0x1a97: 0x0ee9, 0x1a98: 0x1159, 0x1a99: 0x0ef9, 0x1a9a: 0x0f09, 0x1a9b: 0x1199, 0x1a9c: 0x0f31, 0x1a9d: 0x0249, 0x1a9e: 0x0f41, 0x1a9f: 0x0259, 0x1aa0: 0x0f51, 0x1aa1: 0x0359, 0x1aa2: 0x0f61, 0x1aa3: 0x0f71, 0x1aa4: 0x00d9, 0x1aa5: 0x0f99, 0x1aa6: 0x2039, 0x1aa7: 0x0269, 0x1aa8: 0x01d9, 0x1aa9: 0x0fa9, 0x1aaa: 0x0fb9, 0x1aab: 0x1089, 0x1aac: 0x0279, 0x1aad: 0x0369, 0x1aae: 0x0289, 0x1aaf: 0x13d1, 0x1ab0: 0x0039, 0x1ab1: 0x0ee9, 0x1ab2: 0x1159, 0x1ab3: 0x0ef9, 0x1ab4: 0x0f09, 0x1ab5: 0x1199, 0x1ab6: 0x0f31, 0x1ab7: 0x0249, 0x1ab8: 0x0f41, 0x1ab9: 0x0259, 0x1aba: 0x0f51, 0x1abb: 0x0359, 0x1abc: 0x0f61, 0x1abd: 0x0f71, 0x1abe: 0x00d9, 0x1abf: 0x0f99, // Block 0x6b, offset 0x1ac0 0x1ac0: 0x2039, 0x1ac1: 0x0269, 0x1ac2: 0x01d9, 0x1ac3: 0x0fa9, 0x1ac4: 0x0fb9, 0x1ac5: 0x1089, 0x1ac6: 0x0279, 0x1ac7: 0x0369, 0x1ac8: 0x0289, 0x1ac9: 0x13d1, 0x1aca: 0x0039, 0x1acb: 0x0ee9, 0x1acc: 0x1159, 0x1acd: 0x0ef9, 0x1ace: 0x0f09, 0x1acf: 0x1199, 0x1ad0: 0x0f31, 0x1ad1: 0x0249, 0x1ad2: 0x0f41, 0x1ad3: 0x0259, 0x1ad4: 0x0f51, 0x1ad5: 0x0359, 0x1ad6: 0x0f61, 0x1ad7: 0x0f71, 0x1ad8: 0x00d9, 0x1ad9: 0x0f99, 0x1ada: 0x2039, 0x1adb: 0x0269, 0x1adc: 0x01d9, 0x1add: 0x0fa9, 0x1ade: 0x0fb9, 0x1adf: 0x1089, 0x1ae0: 0x0279, 0x1ae1: 0x0369, 0x1ae2: 0x0289, 0x1ae3: 0x13d1, 0x1ae4: 0xba81, 0x1ae5: 0xba99, 0x1ae6: 0x0040, 0x1ae7: 0x0040, 0x1ae8: 0xbab1, 0x1ae9: 0x1099, 0x1aea: 0x10b1, 0x1aeb: 0x10c9, 0x1aec: 0xbac9, 0x1aed: 0xbae1, 0x1aee: 0xbaf9, 0x1aef: 0x1429, 0x1af0: 0x1a31, 0x1af1: 0xbb11, 0x1af2: 0xbb29, 0x1af3: 0xbb41, 0x1af4: 0xbb59, 0x1af5: 0xbb71, 0x1af6: 0xbb89, 0x1af7: 0x2109, 0x1af8: 0x1111, 0x1af9: 0x1429, 0x1afa: 0xbba1, 0x1afb: 0xbbb9, 0x1afc: 0xbbd1, 0x1afd: 0x10e1, 0x1afe: 0x10f9, 0x1aff: 0xbbe9, // Block 0x6c, offset 0x1b00 0x1b00: 0x2079, 0x1b01: 0xbc01, 0x1b02: 0xbab1, 0x1b03: 0x1099, 0x1b04: 0x10b1, 0x1b05: 0x10c9, 0x1b06: 0xbac9, 0x1b07: 0xbae1, 0x1b08: 0xbaf9, 0x1b09: 0x1429, 0x1b0a: 0x1a31, 0x1b0b: 0xbb11, 0x1b0c: 0xbb29, 0x1b0d: 0xbb41, 0x1b0e: 0xbb59, 0x1b0f: 0xbb71, 0x1b10: 0xbb89, 0x1b11: 0x2109, 0x1b12: 0x1111, 0x1b13: 0xbba1, 0x1b14: 0xbba1, 0x1b15: 0xbbb9, 0x1b16: 0xbbd1, 0x1b17: 0x10e1, 0x1b18: 0x10f9, 0x1b19: 0xbbe9, 0x1b1a: 0x2079, 0x1b1b: 0xbc21, 0x1b1c: 0xbac9, 0x1b1d: 0x1429, 0x1b1e: 0xbb11, 0x1b1f: 0x10e1, 0x1b20: 0x1111, 0x1b21: 0x2109, 0x1b22: 0xbab1, 0x1b23: 0x1099, 0x1b24: 0x10b1, 0x1b25: 0x10c9, 0x1b26: 0xbac9, 0x1b27: 0xbae1, 0x1b28: 0xbaf9, 0x1b29: 0x1429, 0x1b2a: 0x1a31, 0x1b2b: 0xbb11, 0x1b2c: 0xbb29, 0x1b2d: 0xbb41, 0x1b2e: 0xbb59, 0x1b2f: 0xbb71, 0x1b30: 0xbb89, 0x1b31: 0x2109, 0x1b32: 0x1111, 0x1b33: 0x1429, 0x1b34: 0xbba1, 0x1b35: 0xbbb9, 0x1b36: 0xbbd1, 0x1b37: 0x10e1, 0x1b38: 0x10f9, 0x1b39: 0xbbe9, 0x1b3a: 0x2079, 0x1b3b: 0xbc01, 0x1b3c: 0xbab1, 0x1b3d: 0x1099, 0x1b3e: 0x10b1, 0x1b3f: 0x10c9, // Block 0x6d, offset 0x1b40 0x1b40: 0xbac9, 0x1b41: 0xbae1, 0x1b42: 0xbaf9, 0x1b43: 0x1429, 0x1b44: 0x1a31, 0x1b45: 0xbb11, 0x1b46: 0xbb29, 0x1b47: 0xbb41, 0x1b48: 0xbb59, 0x1b49: 0xbb71, 0x1b4a: 0xbb89, 0x1b4b: 0x2109, 0x1b4c: 0x1111, 0x1b4d: 0xbba1, 0x1b4e: 0xbba1, 0x1b4f: 0xbbb9, 0x1b50: 0xbbd1, 0x1b51: 0x10e1, 0x1b52: 0x10f9, 0x1b53: 0xbbe9, 0x1b54: 0x2079, 0x1b55: 0xbc21, 0x1b56: 0xbac9, 0x1b57: 0x1429, 0x1b58: 0xbb11, 0x1b59: 0x10e1, 0x1b5a: 0x1111, 0x1b5b: 0x2109, 0x1b5c: 0xbab1, 0x1b5d: 0x1099, 0x1b5e: 0x10b1, 0x1b5f: 0x10c9, 0x1b60: 0xbac9, 0x1b61: 0xbae1, 0x1b62: 0xbaf9, 0x1b63: 0x1429, 0x1b64: 0x1a31, 0x1b65: 0xbb11, 0x1b66: 0xbb29, 0x1b67: 0xbb41, 0x1b68: 0xbb59, 0x1b69: 0xbb71, 0x1b6a: 0xbb89, 0x1b6b: 0x2109, 0x1b6c: 0x1111, 0x1b6d: 0x1429, 0x1b6e: 0xbba1, 0x1b6f: 0xbbb9, 0x1b70: 0xbbd1, 0x1b71: 0x10e1, 0x1b72: 0x10f9, 0x1b73: 0xbbe9, 0x1b74: 0x2079, 0x1b75: 0xbc01, 0x1b76: 0xbab1, 0x1b77: 0x1099, 0x1b78: 0x10b1, 0x1b79: 0x10c9, 0x1b7a: 0xbac9, 0x1b7b: 0xbae1, 0x1b7c: 0xbaf9, 0x1b7d: 0x1429, 0x1b7e: 0x1a31, 0x1b7f: 0xbb11, // Block 0x6e, offset 0x1b80 0x1b80: 0xbb29, 0x1b81: 0xbb41, 0x1b82: 0xbb59, 0x1b83: 0xbb71, 0x1b84: 0xbb89, 0x1b85: 0x2109, 0x1b86: 0x1111, 0x1b87: 0xbba1, 0x1b88: 0xbba1, 0x1b89: 0xbbb9, 0x1b8a: 0xbbd1, 0x1b8b: 0x10e1, 0x1b8c: 0x10f9, 0x1b8d: 0xbbe9, 0x1b8e: 0x2079, 0x1b8f: 0xbc21, 0x1b90: 0xbac9, 0x1b91: 0x1429, 0x1b92: 0xbb11, 0x1b93: 0x10e1, 0x1b94: 0x1111, 0x1b95: 0x2109, 0x1b96: 0xbab1, 0x1b97: 0x1099, 0x1b98: 0x10b1, 0x1b99: 0x10c9, 0x1b9a: 0xbac9, 0x1b9b: 0xbae1, 0x1b9c: 0xbaf9, 0x1b9d: 0x1429, 0x1b9e: 0x1a31, 0x1b9f: 0xbb11, 0x1ba0: 0xbb29, 0x1ba1: 0xbb41, 0x1ba2: 0xbb59, 0x1ba3: 0xbb71, 0x1ba4: 0xbb89, 0x1ba5: 0x2109, 0x1ba6: 0x1111, 0x1ba7: 0x1429, 0x1ba8: 0xbba1, 0x1ba9: 0xbbb9, 0x1baa: 0xbbd1, 0x1bab: 0x10e1, 0x1bac: 0x10f9, 0x1bad: 0xbbe9, 0x1bae: 0x2079, 0x1baf: 0xbc01, 0x1bb0: 0xbab1, 0x1bb1: 0x1099, 0x1bb2: 0x10b1, 0x1bb3: 0x10c9, 0x1bb4: 0xbac9, 0x1bb5: 0xbae1, 0x1bb6: 0xbaf9, 0x1bb7: 0x1429, 0x1bb8: 0x1a31, 0x1bb9: 0xbb11, 0x1bba: 0xbb29, 0x1bbb: 0xbb41, 0x1bbc: 0xbb59, 0x1bbd: 0xbb71, 0x1bbe: 0xbb89, 0x1bbf: 0x2109, // Block 0x6f, offset 0x1bc0 0x1bc0: 0x1111, 0x1bc1: 0xbba1, 0x1bc2: 0xbba1, 0x1bc3: 0xbbb9, 0x1bc4: 0xbbd1, 0x1bc5: 0x10e1, 0x1bc6: 0x10f9, 0x1bc7: 0xbbe9, 0x1bc8: 0x2079, 0x1bc9: 0xbc21, 0x1bca: 0xbac9, 0x1bcb: 0x1429, 0x1bcc: 0xbb11, 0x1bcd: 0x10e1, 0x1bce: 0x1111, 0x1bcf: 0x2109, 0x1bd0: 0xbab1, 0x1bd1: 0x1099, 0x1bd2: 0x10b1, 0x1bd3: 0x10c9, 0x1bd4: 0xbac9, 0x1bd5: 0xbae1, 0x1bd6: 0xbaf9, 0x1bd7: 0x1429, 0x1bd8: 0x1a31, 0x1bd9: 0xbb11, 0x1bda: 0xbb29, 0x1bdb: 0xbb41, 0x1bdc: 0xbb59, 0x1bdd: 0xbb71, 0x1bde: 0xbb89, 0x1bdf: 0x2109, 0x1be0: 0x1111, 0x1be1: 0x1429, 0x1be2: 0xbba1, 0x1be3: 0xbbb9, 0x1be4: 0xbbd1, 0x1be5: 0x10e1, 0x1be6: 0x10f9, 0x1be7: 0xbbe9, 0x1be8: 0x2079, 0x1be9: 0xbc01, 0x1bea: 0xbab1, 0x1beb: 0x1099, 0x1bec: 0x10b1, 0x1bed: 0x10c9, 0x1bee: 0xbac9, 0x1bef: 0xbae1, 0x1bf0: 0xbaf9, 0x1bf1: 0x1429, 0x1bf2: 0x1a31, 0x1bf3: 0xbb11, 0x1bf4: 0xbb29, 0x1bf5: 0xbb41, 0x1bf6: 0xbb59, 0x1bf7: 0xbb71, 0x1bf8: 0xbb89, 0x1bf9: 0x2109, 0x1bfa: 0x1111, 0x1bfb: 0xbba1, 0x1bfc: 0xbba1, 0x1bfd: 0xbbb9, 0x1bfe: 0xbbd1, 0x1bff: 0x10e1, // Block 0x70, offset 0x1c00 0x1c00: 0x10f9, 0x1c01: 0xbbe9, 0x1c02: 0x2079, 0x1c03: 0xbc21, 0x1c04: 0xbac9, 0x1c05: 0x1429, 0x1c06: 0xbb11, 0x1c07: 0x10e1, 0x1c08: 0x1111, 0x1c09: 0x2109, 0x1c0a: 0xbc41, 0x1c0b: 0xbc41, 0x1c0c: 0x0040, 0x1c0d: 0x0040, 0x1c0e: 0x1f41, 0x1c0f: 0x00c9, 0x1c10: 0x0069, 0x1c11: 0x0079, 0x1c12: 0x1f51, 0x1c13: 0x1f61, 0x1c14: 0x1f71, 0x1c15: 0x1f81, 0x1c16: 0x1f91, 0x1c17: 0x1fa1, 0x1c18: 0x1f41, 0x1c19: 0x00c9, 0x1c1a: 0x0069, 0x1c1b: 0x0079, 0x1c1c: 0x1f51, 0x1c1d: 0x1f61, 0x1c1e: 0x1f71, 0x1c1f: 0x1f81, 0x1c20: 0x1f91, 0x1c21: 0x1fa1, 0x1c22: 0x1f41, 0x1c23: 0x00c9, 0x1c24: 0x0069, 0x1c25: 0x0079, 0x1c26: 0x1f51, 0x1c27: 0x1f61, 0x1c28: 0x1f71, 0x1c29: 0x1f81, 0x1c2a: 0x1f91, 0x1c2b: 0x1fa1, 0x1c2c: 0x1f41, 0x1c2d: 0x00c9, 0x1c2e: 0x0069, 0x1c2f: 0x0079, 0x1c30: 0x1f51, 0x1c31: 0x1f61, 0x1c32: 0x1f71, 0x1c33: 0x1f81, 0x1c34: 0x1f91, 0x1c35: 0x1fa1, 0x1c36: 0x1f41, 0x1c37: 0x00c9, 0x1c38: 0x0069, 0x1c39: 0x0079, 0x1c3a: 0x1f51, 0x1c3b: 0x1f61, 0x1c3c: 0x1f71, 0x1c3d: 0x1f81, 0x1c3e: 0x1f91, 0x1c3f: 0x1fa1, // Block 0x71, offset 0x1c40 0x1c40: 0xe115, 0x1c41: 0xe115, 0x1c42: 0xe135, 0x1c43: 0xe135, 0x1c44: 0xe115, 0x1c45: 0xe115, 0x1c46: 0xe175, 0x1c47: 0xe175, 0x1c48: 0xe115, 0x1c49: 0xe115, 0x1c4a: 0xe135, 0x1c4b: 0xe135, 0x1c4c: 0xe115, 0x1c4d: 0xe115, 0x1c4e: 0xe1f5, 0x1c4f: 0xe1f5, 0x1c50: 0xe115, 0x1c51: 0xe115, 0x1c52: 0xe135, 0x1c53: 0xe135, 0x1c54: 0xe115, 0x1c55: 0xe115, 0x1c56: 0xe175, 0x1c57: 0xe175, 0x1c58: 0xe115, 0x1c59: 0xe115, 0x1c5a: 0xe135, 0x1c5b: 0xe135, 0x1c5c: 0xe115, 0x1c5d: 0xe115, 0x1c5e: 0x8b05, 0x1c5f: 0x8b05, 0x1c60: 0x04b5, 0x1c61: 0x04b5, 0x1c62: 0x0a08, 0x1c63: 0x0a08, 0x1c64: 0x0a08, 0x1c65: 0x0a08, 0x1c66: 0x0a08, 0x1c67: 0x0a08, 0x1c68: 0x0a08, 0x1c69: 0x0a08, 0x1c6a: 0x0a08, 0x1c6b: 0x0a08, 0x1c6c: 0x0a08, 0x1c6d: 0x0a08, 0x1c6e: 0x0a08, 0x1c6f: 0x0a08, 0x1c70: 0x0a08, 0x1c71: 0x0a08, 0x1c72: 0x0a08, 0x1c73: 0x0a08, 0x1c74: 0x0a08, 0x1c75: 0x0a08, 0x1c76: 0x0a08, 0x1c77: 0x0a08, 0x1c78: 0x0a08, 0x1c79: 0x0a08, 0x1c7a: 0x0a08, 0x1c7b: 0x0a08, 0x1c7c: 0x0a08, 0x1c7d: 0x0a08, 0x1c7e: 0x0a08, 0x1c7f: 0x0a08, // Block 0x72, offset 0x1c80 0x1c80: 0xb189, 0x1c81: 0xb1a1, 0x1c82: 0xb201, 0x1c83: 0xb249, 0x1c84: 0x0040, 0x1c85: 0xb411, 0x1c86: 0xb291, 0x1c87: 0xb219, 0x1c88: 0xb309, 0x1c89: 0xb429, 0x1c8a: 0xb399, 0x1c8b: 0xb3b1, 0x1c8c: 0xb3c9, 0x1c8d: 0xb3e1, 0x1c8e: 0xb2a9, 0x1c8f: 0xb339, 0x1c90: 0xb369, 0x1c91: 0xb2d9, 0x1c92: 0xb381, 0x1c93: 0xb279, 0x1c94: 0xb2c1, 0x1c95: 0xb1d1, 0x1c96: 0xb1e9, 0x1c97: 0xb231, 0x1c98: 0xb261, 0x1c99: 0xb2f1, 0x1c9a: 0xb321, 0x1c9b: 0xb351, 0x1c9c: 0xbc59, 0x1c9d: 0x7949, 0x1c9e: 0xbc71, 0x1c9f: 0xbc89, 0x1ca0: 0x0040, 0x1ca1: 0xb1a1, 0x1ca2: 0xb201, 0x1ca3: 0x0040, 0x1ca4: 0xb3f9, 0x1ca5: 0x0040, 0x1ca6: 0x0040, 0x1ca7: 0xb219, 0x1ca8: 0x0040, 0x1ca9: 0xb429, 0x1caa: 0xb399, 0x1cab: 0xb3b1, 0x1cac: 0xb3c9, 0x1cad: 0xb3e1, 0x1cae: 0xb2a9, 0x1caf: 0xb339, 0x1cb0: 0xb369, 0x1cb1: 0xb2d9, 0x1cb2: 0xb381, 0x1cb3: 0x0040, 0x1cb4: 0xb2c1, 0x1cb5: 0xb1d1, 0x1cb6: 0xb1e9, 0x1cb7: 0xb231, 0x1cb8: 0x0040, 0x1cb9: 0xb2f1, 0x1cba: 0x0040, 0x1cbb: 0xb351, 0x1cbc: 0x0040, 0x1cbd: 0x0040, 0x1cbe: 0x0040, 0x1cbf: 0x0040, // Block 0x73, offset 0x1cc0 0x1cc0: 0x0040, 0x1cc1: 0x0040, 0x1cc2: 0xb201, 0x1cc3: 0x0040, 0x1cc4: 0x0040, 0x1cc5: 0x0040, 0x1cc6: 0x0040, 0x1cc7: 0xb219, 0x1cc8: 0x0040, 0x1cc9: 0xb429, 0x1cca: 0x0040, 0x1ccb: 0xb3b1, 0x1ccc: 0x0040, 0x1ccd: 0xb3e1, 0x1cce: 0xb2a9, 0x1ccf: 0xb339, 0x1cd0: 0x0040, 0x1cd1: 0xb2d9, 0x1cd2: 0xb381, 0x1cd3: 0x0040, 0x1cd4: 0xb2c1, 0x1cd5: 0x0040, 0x1cd6: 0x0040, 0x1cd7: 0xb231, 0x1cd8: 0x0040, 0x1cd9: 0xb2f1, 0x1cda: 0x0040, 0x1cdb: 0xb351, 0x1cdc: 0x0040, 0x1cdd: 0x7949, 0x1cde: 0x0040, 0x1cdf: 0xbc89, 0x1ce0: 0x0040, 0x1ce1: 0xb1a1, 0x1ce2: 0xb201, 0x1ce3: 0x0040, 0x1ce4: 0xb3f9, 0x1ce5: 0x0040, 0x1ce6: 0x0040, 0x1ce7: 0xb219, 0x1ce8: 0xb309, 0x1ce9: 0xb429, 0x1cea: 0xb399, 0x1ceb: 0x0040, 0x1cec: 0xb3c9, 0x1ced: 0xb3e1, 0x1cee: 0xb2a9, 0x1cef: 0xb339, 0x1cf0: 0xb369, 0x1cf1: 0xb2d9, 0x1cf2: 0xb381, 0x1cf3: 0x0040, 0x1cf4: 0xb2c1, 0x1cf5: 0xb1d1, 0x1cf6: 0xb1e9, 0x1cf7: 0xb231, 0x1cf8: 0x0040, 0x1cf9: 0xb2f1, 0x1cfa: 0xb321, 0x1cfb: 0xb351, 0x1cfc: 0xbc59, 0x1cfd: 0x0040, 0x1cfe: 0xbc71, 0x1cff: 0x0040, // Block 0x74, offset 0x1d00 0x1d00: 0xb189, 0x1d01: 0xb1a1, 0x1d02: 0xb201, 0x1d03: 0xb249, 0x1d04: 0xb3f9, 0x1d05: 0xb411, 0x1d06: 0xb291, 0x1d07: 0xb219, 0x1d08: 0xb309, 0x1d09: 0xb429, 0x1d0a: 0x0040, 0x1d0b: 0xb3b1, 0x1d0c: 0xb3c9, 0x1d0d: 0xb3e1, 0x1d0e: 0xb2a9, 0x1d0f: 0xb339, 0x1d10: 0xb369, 0x1d11: 0xb2d9, 0x1d12: 0xb381, 0x1d13: 0xb279, 0x1d14: 0xb2c1, 0x1d15: 0xb1d1, 0x1d16: 0xb1e9, 0x1d17: 0xb231, 0x1d18: 0xb261, 0x1d19: 0xb2f1, 0x1d1a: 0xb321, 0x1d1b: 0xb351, 0x1d1c: 0x0040, 0x1d1d: 0x0040, 0x1d1e: 0x0040, 0x1d1f: 0x0040, 0x1d20: 0x0040, 0x1d21: 0xb1a1, 0x1d22: 0xb201, 0x1d23: 0xb249, 0x1d24: 0x0040, 0x1d25: 0xb411, 0x1d26: 0xb291, 0x1d27: 0xb219, 0x1d28: 0xb309, 0x1d29: 0xb429, 0x1d2a: 0x0040, 0x1d2b: 0xb3b1, 0x1d2c: 0xb3c9, 0x1d2d: 0xb3e1, 0x1d2e: 0xb2a9, 0x1d2f: 0xb339, 0x1d30: 0xb369, 0x1d31: 0xb2d9, 0x1d32: 0xb381, 0x1d33: 0xb279, 0x1d34: 0xb2c1, 0x1d35: 0xb1d1, 0x1d36: 0xb1e9, 0x1d37: 0xb231, 0x1d38: 0xb261, 0x1d39: 0xb2f1, 0x1d3a: 0xb321, 0x1d3b: 0xb351, 0x1d3c: 0x0040, 0x1d3d: 0x0040, 0x1d3e: 0x0040, 0x1d3f: 0x0040, // Block 0x75, offset 0x1d40 0x1d40: 0x0040, 0x1d41: 0xbca2, 0x1d42: 0xbcba, 0x1d43: 0xbcd2, 0x1d44: 0xbcea, 0x1d45: 0xbd02, 0x1d46: 0xbd1a, 0x1d47: 0xbd32, 0x1d48: 0xbd4a, 0x1d49: 0xbd62, 0x1d4a: 0xbd7a, 0x1d4b: 0x0018, 0x1d4c: 0x0018, 0x1d4d: 0x0040, 0x1d4e: 0x0040, 0x1d4f: 0x0040, 0x1d50: 0xbd92, 0x1d51: 0xbdb2, 0x1d52: 0xbdd2, 0x1d53: 0xbdf2, 0x1d54: 0xbe12, 0x1d55: 0xbe32, 0x1d56: 0xbe52, 0x1d57: 0xbe72, 0x1d58: 0xbe92, 0x1d59: 0xbeb2, 0x1d5a: 0xbed2, 0x1d5b: 0xbef2, 0x1d5c: 0xbf12, 0x1d5d: 0xbf32, 0x1d5e: 0xbf52, 0x1d5f: 0xbf72, 0x1d60: 0xbf92, 0x1d61: 0xbfb2, 0x1d62: 0xbfd2, 0x1d63: 0xbff2, 0x1d64: 0xc012, 0x1d65: 0xc032, 0x1d66: 0xc052, 0x1d67: 0xc072, 0x1d68: 0xc092, 0x1d69: 0xc0b2, 0x1d6a: 0xc0d1, 0x1d6b: 0x1159, 0x1d6c: 0x0269, 0x1d6d: 0x6671, 0x1d6e: 0xc111, 0x1d6f: 0x0018, 0x1d70: 0x0039, 0x1d71: 0x0ee9, 0x1d72: 0x1159, 0x1d73: 0x0ef9, 0x1d74: 0x0f09, 0x1d75: 0x1199, 0x1d76: 0x0f31, 0x1d77: 0x0249, 0x1d78: 0x0f41, 0x1d79: 0x0259, 0x1d7a: 0x0f51, 0x1d7b: 0x0359, 0x1d7c: 0x0f61, 0x1d7d: 0x0f71, 0x1d7e: 0x00d9, 0x1d7f: 0x0f99, // Block 0x76, offset 0x1d80 0x1d80: 0x2039, 0x1d81: 0x0269, 0x1d82: 0x01d9, 0x1d83: 0x0fa9, 0x1d84: 0x0fb9, 0x1d85: 0x1089, 0x1d86: 0x0279, 0x1d87: 0x0369, 0x1d88: 0x0289, 0x1d89: 0x13d1, 0x1d8a: 0xc129, 0x1d8b: 0x65b1, 0x1d8c: 0xc141, 0x1d8d: 0x1441, 0x1d8e: 0xc159, 0x1d8f: 0xc179, 0x1d90: 0x0018, 0x1d91: 0x0018, 0x1d92: 0x0018, 0x1d93: 0x0018, 0x1d94: 0x0018, 0x1d95: 0x0018, 0x1d96: 0x0018, 0x1d97: 0x0018, 0x1d98: 0x0018, 0x1d99: 0x0018, 0x1d9a: 0x0018, 0x1d9b: 0x0018, 0x1d9c: 0x0018, 0x1d9d: 0x0018, 0x1d9e: 0x0018, 0x1d9f: 0x0018, 0x1da0: 0x0018, 0x1da1: 0x0018, 0x1da2: 0x0018, 0x1da3: 0x0018, 0x1da4: 0x0018, 0x1da5: 0x0018, 0x1da6: 0x0018, 0x1da7: 0x0018, 0x1da8: 0x0018, 0x1da9: 0x0018, 0x1daa: 0xc191, 0x1dab: 0xc1a9, 0x1dac: 0x0040, 0x1dad: 0x0040, 0x1dae: 0x0040, 0x1daf: 0x0040, 0x1db0: 0x0018, 0x1db1: 0x0018, 0x1db2: 0x0018, 0x1db3: 0x0018, 0x1db4: 0x0018, 0x1db5: 0x0018, 0x1db6: 0x0018, 0x1db7: 0x0018, 0x1db8: 0x0018, 0x1db9: 0x0018, 0x1dba: 0x0018, 0x1dbb: 0x0018, 0x1dbc: 0x0018, 0x1dbd: 0x0018, 0x1dbe: 0x0018, 0x1dbf: 0x0018, // Block 0x77, offset 0x1dc0 0x1dc0: 0xc1d9, 0x1dc1: 0xc211, 0x1dc2: 0xc249, 0x1dc3: 0x0040, 0x1dc4: 0x0040, 0x1dc5: 0x0040, 0x1dc6: 0x0040, 0x1dc7: 0x0040, 0x1dc8: 0x0040, 0x1dc9: 0x0040, 0x1dca: 0x0040, 0x1dcb: 0x0040, 0x1dcc: 0x0040, 0x1dcd: 0x0040, 0x1dce: 0x0040, 0x1dcf: 0x0040, 0x1dd0: 0xc269, 0x1dd1: 0xc289, 0x1dd2: 0xc2a9, 0x1dd3: 0xc2c9, 0x1dd4: 0xc2e9, 0x1dd5: 0xc309, 0x1dd6: 0xc329, 0x1dd7: 0xc349, 0x1dd8: 0xc369, 0x1dd9: 0xc389, 0x1dda: 0xc3a9, 0x1ddb: 0xc3c9, 0x1ddc: 0xc3e9, 0x1ddd: 0xc409, 0x1dde: 0xc429, 0x1ddf: 0xc449, 0x1de0: 0xc469, 0x1de1: 0xc489, 0x1de2: 0xc4a9, 0x1de3: 0xc4c9, 0x1de4: 0xc4e9, 0x1de5: 0xc509, 0x1de6: 0xc529, 0x1de7: 0xc549, 0x1de8: 0xc569, 0x1de9: 0xc589, 0x1dea: 0xc5a9, 0x1deb: 0xc5c9, 0x1dec: 0xc5e9, 0x1ded: 0xc609, 0x1dee: 0xc629, 0x1def: 0xc649, 0x1df0: 0xc669, 0x1df1: 0xc689, 0x1df2: 0xc6a9, 0x1df3: 0xc6c9, 0x1df4: 0xc6e9, 0x1df5: 0xc709, 0x1df6: 0xc729, 0x1df7: 0xc749, 0x1df8: 0xc769, 0x1df9: 0xc789, 0x1dfa: 0xc7a9, 0x1dfb: 0xc7c9, 0x1dfc: 0x0040, 0x1dfd: 0x0040, 0x1dfe: 0x0040, 0x1dff: 0x0040, // Block 0x78, offset 0x1e00 0x1e00: 0xcaf9, 0x1e01: 0xcb19, 0x1e02: 0xcb39, 0x1e03: 0x8b1d, 0x1e04: 0xcb59, 0x1e05: 0xcb79, 0x1e06: 0xcb99, 0x1e07: 0xcbb9, 0x1e08: 0xcbd9, 0x1e09: 0xcbf9, 0x1e0a: 0xcc19, 0x1e0b: 0xcc39, 0x1e0c: 0xcc59, 0x1e0d: 0x8b3d, 0x1e0e: 0xcc79, 0x1e0f: 0xcc99, 0x1e10: 0xccb9, 0x1e11: 0xccd9, 0x1e12: 0x8b5d, 0x1e13: 0xccf9, 0x1e14: 0xcd19, 0x1e15: 0xc429, 0x1e16: 0x8b7d, 0x1e17: 0xcd39, 0x1e18: 0xcd59, 0x1e19: 0xcd79, 0x1e1a: 0xcd99, 0x1e1b: 0xcdb9, 0x1e1c: 0x8b9d, 0x1e1d: 0xcdd9, 0x1e1e: 0xcdf9, 0x1e1f: 0xce19, 0x1e20: 0xce39, 0x1e21: 0xce59, 0x1e22: 0xc789, 0x1e23: 0xce79, 0x1e24: 0xce99, 0x1e25: 0xceb9, 0x1e26: 0xced9, 0x1e27: 0xcef9, 0x1e28: 0xcf19, 0x1e29: 0xcf39, 0x1e2a: 0xcf59, 0x1e2b: 0xcf79, 0x1e2c: 0xcf99, 0x1e2d: 0xcfb9, 0x1e2e: 0xcfd9, 0x1e2f: 0xcff9, 0x1e30: 0xd019, 0x1e31: 0xd039, 0x1e32: 0xd039, 0x1e33: 0xd039, 0x1e34: 0x8bbd, 0x1e35: 0xd059, 0x1e36: 0xd079, 0x1e37: 0xd099, 0x1e38: 0x8bdd, 0x1e39: 0xd0b9, 0x1e3a: 0xd0d9, 0x1e3b: 0xd0f9, 0x1e3c: 0xd119, 0x1e3d: 0xd139, 0x1e3e: 0xd159, 0x1e3f: 0xd179, // Block 0x79, offset 0x1e40 0x1e40: 0xd199, 0x1e41: 0xd1b9, 0x1e42: 0xd1d9, 0x1e43: 0xd1f9, 0x1e44: 0xd219, 0x1e45: 0xd239, 0x1e46: 0xd239, 0x1e47: 0xd259, 0x1e48: 0xd279, 0x1e49: 0xd299, 0x1e4a: 0xd2b9, 0x1e4b: 0xd2d9, 0x1e4c: 0xd2f9, 0x1e4d: 0xd319, 0x1e4e: 0xd339, 0x1e4f: 0xd359, 0x1e50: 0xd379, 0x1e51: 0xd399, 0x1e52: 0xd3b9, 0x1e53: 0xd3d9, 0x1e54: 0xd3f9, 0x1e55: 0xd419, 0x1e56: 0xd439, 0x1e57: 0xd459, 0x1e58: 0xd479, 0x1e59: 0x8bfd, 0x1e5a: 0xd499, 0x1e5b: 0xd4b9, 0x1e5c: 0xd4d9, 0x1e5d: 0xc309, 0x1e5e: 0xd4f9, 0x1e5f: 0xd519, 0x1e60: 0x8c1d, 0x1e61: 0x8c3d, 0x1e62: 0xd539, 0x1e63: 0xd559, 0x1e64: 0xd579, 0x1e65: 0xd599, 0x1e66: 0xd5b9, 0x1e67: 0xd5d9, 0x1e68: 0x2040, 0x1e69: 0xd5f9, 0x1e6a: 0xd619, 0x1e6b: 0xd619, 0x1e6c: 0x8c5d, 0x1e6d: 0xd639, 0x1e6e: 0xd659, 0x1e6f: 0xd679, 0x1e70: 0xd699, 0x1e71: 0x8c7d, 0x1e72: 0xd6b9, 0x1e73: 0xd6d9, 0x1e74: 0x2040, 0x1e75: 0xd6f9, 0x1e76: 0xd719, 0x1e77: 0xd739, 0x1e78: 0xd759, 0x1e79: 0xd779, 0x1e7a: 0xd799, 0x1e7b: 0x8c9d, 0x1e7c: 0xd7b9, 0x1e7d: 0x8cbd, 0x1e7e: 0xd7d9, 0x1e7f: 0xd7f9, // Block 0x7a, offset 0x1e80 0x1e80: 0xd819, 0x1e81: 0xd839, 0x1e82: 0xd859, 0x1e83: 0xd879, 0x1e84: 0xd899, 0x1e85: 0xd8b9, 0x1e86: 0xd8d9, 0x1e87: 0xd8f9, 0x1e88: 0xd919, 0x1e89: 0x8cdd, 0x1e8a: 0xd939, 0x1e8b: 0xd959, 0x1e8c: 0xd979, 0x1e8d: 0xd999, 0x1e8e: 0xd9b9, 0x1e8f: 0x8cfd, 0x1e90: 0xd9d9, 0x1e91: 0x8d1d, 0x1e92: 0x8d3d, 0x1e93: 0xd9f9, 0x1e94: 0xda19, 0x1e95: 0xda19, 0x1e96: 0xda39, 0x1e97: 0x8d5d, 0x1e98: 0x8d7d, 0x1e99: 0xda59, 0x1e9a: 0xda79, 0x1e9b: 0xda99, 0x1e9c: 0xdab9, 0x1e9d: 0xdad9, 0x1e9e: 0xdaf9, 0x1e9f: 0xdb19, 0x1ea0: 0xdb39, 0x1ea1: 0xdb59, 0x1ea2: 0xdb79, 0x1ea3: 0xdb99, 0x1ea4: 0x8d9d, 0x1ea5: 0xdbb9, 0x1ea6: 0xdbd9, 0x1ea7: 0xdbf9, 0x1ea8: 0xdc19, 0x1ea9: 0xdbf9, 0x1eaa: 0xdc39, 0x1eab: 0xdc59, 0x1eac: 0xdc79, 0x1ead: 0xdc99, 0x1eae: 0xdcb9, 0x1eaf: 0xdcd9, 0x1eb0: 0xdcf9, 0x1eb1: 0xdd19, 0x1eb2: 0xdd39, 0x1eb3: 0xdd59, 0x1eb4: 0xdd79, 0x1eb5: 0xdd99, 0x1eb6: 0xddb9, 0x1eb7: 0xddd9, 0x1eb8: 0x8dbd, 0x1eb9: 0xddf9, 0x1eba: 0xde19, 0x1ebb: 0xde39, 0x1ebc: 0xde59, 0x1ebd: 0xde79, 0x1ebe: 0x8ddd, 0x1ebf: 0xde99, // Block 0x7b, offset 0x1ec0 0x1ec0: 0xe599, 0x1ec1: 0xe5b9, 0x1ec2: 0xe5d9, 0x1ec3: 0xe5f9, 0x1ec4: 0xe619, 0x1ec5: 0xe639, 0x1ec6: 0x8efd, 0x1ec7: 0xe659, 0x1ec8: 0xe679, 0x1ec9: 0xe699, 0x1eca: 0xe6b9, 0x1ecb: 0xe6d9, 0x1ecc: 0xe6f9, 0x1ecd: 0x8f1d, 0x1ece: 0xe719, 0x1ecf: 0xe739, 0x1ed0: 0x8f3d, 0x1ed1: 0x8f5d, 0x1ed2: 0xe759, 0x1ed3: 0xe779, 0x1ed4: 0xe799, 0x1ed5: 0xe7b9, 0x1ed6: 0xe7d9, 0x1ed7: 0xe7f9, 0x1ed8: 0xe819, 0x1ed9: 0xe839, 0x1eda: 0xe859, 0x1edb: 0x8f7d, 0x1edc: 0xe879, 0x1edd: 0x8f9d, 0x1ede: 0xe899, 0x1edf: 0x2040, 0x1ee0: 0xe8b9, 0x1ee1: 0xe8d9, 0x1ee2: 0xe8f9, 0x1ee3: 0x8fbd, 0x1ee4: 0xe919, 0x1ee5: 0xe939, 0x1ee6: 0x8fdd, 0x1ee7: 0x8ffd, 0x1ee8: 0xe959, 0x1ee9: 0xe979, 0x1eea: 0xe999, 0x1eeb: 0xe9b9, 0x1eec: 0xe9d9, 0x1eed: 0xe9d9, 0x1eee: 0xe9f9, 0x1eef: 0xea19, 0x1ef0: 0xea39, 0x1ef1: 0xea59, 0x1ef2: 0xea79, 0x1ef3: 0xea99, 0x1ef4: 0xeab9, 0x1ef5: 0x901d, 0x1ef6: 0xead9, 0x1ef7: 0x903d, 0x1ef8: 0xeaf9, 0x1ef9: 0x905d, 0x1efa: 0xeb19, 0x1efb: 0x907d, 0x1efc: 0x909d, 0x1efd: 0x90bd, 0x1efe: 0xeb39, 0x1eff: 0xeb59, // Block 0x7c, offset 0x1f00 0x1f00: 0xeb79, 0x1f01: 0x90dd, 0x1f02: 0x90fd, 0x1f03: 0x911d, 0x1f04: 0x913d, 0x1f05: 0xeb99, 0x1f06: 0xebb9, 0x1f07: 0xebb9, 0x1f08: 0xebd9, 0x1f09: 0xebf9, 0x1f0a: 0xec19, 0x1f0b: 0xec39, 0x1f0c: 0xec59, 0x1f0d: 0x915d, 0x1f0e: 0xec79, 0x1f0f: 0xec99, 0x1f10: 0xecb9, 0x1f11: 0xecd9, 0x1f12: 0x917d, 0x1f13: 0xecf9, 0x1f14: 0x919d, 0x1f15: 0x91bd, 0x1f16: 0xed19, 0x1f17: 0xed39, 0x1f18: 0xed59, 0x1f19: 0xed79, 0x1f1a: 0xed99, 0x1f1b: 0xedb9, 0x1f1c: 0x91dd, 0x1f1d: 0x91fd, 0x1f1e: 0x921d, 0x1f1f: 0x2040, 0x1f20: 0xedd9, 0x1f21: 0x923d, 0x1f22: 0xedf9, 0x1f23: 0xee19, 0x1f24: 0xee39, 0x1f25: 0x925d, 0x1f26: 0xee59, 0x1f27: 0xee79, 0x1f28: 0xee99, 0x1f29: 0xeeb9, 0x1f2a: 0xeed9, 0x1f2b: 0x927d, 0x1f2c: 0xeef9, 0x1f2d: 0xef19, 0x1f2e: 0xef39, 0x1f2f: 0xef59, 0x1f30: 0xef79, 0x1f31: 0xef99, 0x1f32: 0x929d, 0x1f33: 0x92bd, 0x1f34: 0xefb9, 0x1f35: 0x92dd, 0x1f36: 0xefd9, 0x1f37: 0x92fd, 0x1f38: 0xeff9, 0x1f39: 0xf019, 0x1f3a: 0xf039, 0x1f3b: 0x931d, 0x1f3c: 0x933d, 0x1f3d: 0xf059, 0x1f3e: 0x935d, 0x1f3f: 0xf079, // Block 0x7d, offset 0x1f40 0x1f40: 0xf6b9, 0x1f41: 0xf6d9, 0x1f42: 0xf6f9, 0x1f43: 0xf719, 0x1f44: 0xf739, 0x1f45: 0x951d, 0x1f46: 0xf759, 0x1f47: 0xf779, 0x1f48: 0xf799, 0x1f49: 0xf7b9, 0x1f4a: 0xf7d9, 0x1f4b: 0x953d, 0x1f4c: 0x955d, 0x1f4d: 0xf7f9, 0x1f4e: 0xf819, 0x1f4f: 0xf839, 0x1f50: 0xf859, 0x1f51: 0xf879, 0x1f52: 0xf899, 0x1f53: 0x957d, 0x1f54: 0xf8b9, 0x1f55: 0xf8d9, 0x1f56: 0xf8f9, 0x1f57: 0xf919, 0x1f58: 0x959d, 0x1f59: 0x95bd, 0x1f5a: 0xf939, 0x1f5b: 0xf959, 0x1f5c: 0xf979, 0x1f5d: 0x95dd, 0x1f5e: 0xf999, 0x1f5f: 0xf9b9, 0x1f60: 0x6815, 0x1f61: 0x95fd, 0x1f62: 0xf9d9, 0x1f63: 0xf9f9, 0x1f64: 0xfa19, 0x1f65: 0x961d, 0x1f66: 0xfa39, 0x1f67: 0xfa59, 0x1f68: 0xfa79, 0x1f69: 0xfa99, 0x1f6a: 0xfab9, 0x1f6b: 0xfad9, 0x1f6c: 0xfaf9, 0x1f6d: 0x963d, 0x1f6e: 0xfb19, 0x1f6f: 0xfb39, 0x1f70: 0xfb59, 0x1f71: 0x965d, 0x1f72: 0xfb79, 0x1f73: 0xfb99, 0x1f74: 0xfbb9, 0x1f75: 0xfbd9, 0x1f76: 0x7b35, 0x1f77: 0x967d, 0x1f78: 0xfbf9, 0x1f79: 0xfc19, 0x1f7a: 0xfc39, 0x1f7b: 0x969d, 0x1f7c: 0xfc59, 0x1f7d: 0x96bd, 0x1f7e: 0xfc79, 0x1f7f: 0xfc79, // Block 0x7e, offset 0x1f80 0x1f80: 0xfc99, 0x1f81: 0x96dd, 0x1f82: 0xfcb9, 0x1f83: 0xfcd9, 0x1f84: 0xfcf9, 0x1f85: 0xfd19, 0x1f86: 0xfd39, 0x1f87: 0xfd59, 0x1f88: 0xfd79, 0x1f89: 0x96fd, 0x1f8a: 0xfd99, 0x1f8b: 0xfdb9, 0x1f8c: 0xfdd9, 0x1f8d: 0xfdf9, 0x1f8e: 0xfe19, 0x1f8f: 0xfe39, 0x1f90: 0x971d, 0x1f91: 0xfe59, 0x1f92: 0x973d, 0x1f93: 0x975d, 0x1f94: 0x977d, 0x1f95: 0xfe79, 0x1f96: 0xfe99, 0x1f97: 0xfeb9, 0x1f98: 0xfed9, 0x1f99: 0xfef9, 0x1f9a: 0xff19, 0x1f9b: 0xff39, 0x1f9c: 0xff59, 0x1f9d: 0x979d, 0x1f9e: 0x0040, 0x1f9f: 0x0040, 0x1fa0: 0x0040, 0x1fa1: 0x0040, 0x1fa2: 0x0040, 0x1fa3: 0x0040, 0x1fa4: 0x0040, 0x1fa5: 0x0040, 0x1fa6: 0x0040, 0x1fa7: 0x0040, 0x1fa8: 0x0040, 0x1fa9: 0x0040, 0x1faa: 0x0040, 0x1fab: 0x0040, 0x1fac: 0x0040, 0x1fad: 0x0040, 0x1fae: 0x0040, 0x1faf: 0x0040, 0x1fb0: 0x0040, 0x1fb1: 0x0040, 0x1fb2: 0x0040, 0x1fb3: 0x0040, 0x1fb4: 0x0040, 0x1fb5: 0x0040, 0x1fb6: 0x0040, 0x1fb7: 0x0040, 0x1fb8: 0x0040, 0x1fb9: 0x0040, 0x1fba: 0x0040, 0x1fbb: 0x0040, 0x1fbc: 0x0040, 0x1fbd: 0x0040, 0x1fbe: 0x0040, 0x1fbf: 0x0040, } // idnaIndex: 36 blocks, 2304 entries, 4608 bytes // Block 0 is the zero block. var idnaIndex = [2304]uint16{ // Block 0x0, offset 0x0 // Block 0x1, offset 0x40 // Block 0x2, offset 0x80 // Block 0x3, offset 0xc0 0xc2: 0x01, 0xc3: 0x7d, 0xc4: 0x02, 0xc5: 0x03, 0xc6: 0x04, 0xc7: 0x05, 0xc8: 0x06, 0xc9: 0x7e, 0xca: 0x7f, 0xcb: 0x07, 0xcc: 0x80, 0xcd: 0x08, 0xce: 0x09, 0xcf: 0x0a, 0xd0: 0x81, 0xd1: 0x0b, 0xd2: 0x0c, 0xd3: 0x0d, 0xd4: 0x0e, 0xd5: 0x82, 0xd6: 0x83, 0xd7: 0x84, 0xd8: 0x0f, 0xd9: 0x10, 0xda: 0x85, 0xdb: 0x11, 0xdc: 0x12, 0xdd: 0x86, 0xde: 0x87, 0xdf: 0x88, 0xe0: 0x02, 0xe1: 0x03, 0xe2: 0x04, 0xe3: 0x05, 0xe4: 0x06, 0xe5: 0x07, 0xe6: 0x07, 0xe7: 0x07, 0xe8: 0x07, 0xe9: 0x08, 0xea: 0x09, 0xeb: 0x07, 0xec: 0x07, 0xed: 0x0a, 0xee: 0x0b, 0xef: 0x0c, 0xf0: 0x1d, 0xf1: 0x1e, 0xf2: 0x1e, 0xf3: 0x20, 0xf4: 0x21, // Block 0x4, offset 0x100 0x120: 0x89, 0x121: 0x13, 0x122: 0x8a, 0x123: 0x8b, 0x124: 0x8c, 0x125: 0x14, 0x126: 0x15, 0x127: 0x16, 0x128: 0x17, 0x129: 0x18, 0x12a: 0x19, 0x12b: 0x1a, 0x12c: 0x1b, 0x12d: 0x1c, 0x12e: 0x1d, 0x12f: 0x8d, 0x130: 0x8e, 0x131: 0x1e, 0x132: 0x1f, 0x133: 0x20, 0x134: 0x8f, 0x135: 0x21, 0x136: 0x90, 0x137: 0x91, 0x138: 0x92, 0x139: 0x93, 0x13a: 0x22, 0x13b: 0x94, 0x13c: 0x95, 0x13d: 0x23, 0x13e: 0x24, 0x13f: 0x96, // Block 0x5, offset 0x140 0x140: 0x97, 0x141: 0x98, 0x142: 0x99, 0x143: 0x9a, 0x144: 0x9b, 0x145: 0x9c, 0x146: 0x9d, 0x147: 0x9e, 0x148: 0x9f, 0x149: 0xa0, 0x14a: 0xa1, 0x14b: 0xa2, 0x14c: 0xa3, 0x14d: 0xa4, 0x14e: 0xa5, 0x14f: 0xa6, 0x150: 0xa7, 0x151: 0x9f, 0x152: 0x9f, 0x153: 0x9f, 0x154: 0x9f, 0x155: 0x9f, 0x156: 0x9f, 0x157: 0x9f, 0x158: 0x9f, 0x159: 0xa8, 0x15a: 0xa9, 0x15b: 0xaa, 0x15c: 0xab, 0x15d: 0xac, 0x15e: 0xad, 0x15f: 0xae, 0x160: 0xaf, 0x161: 0xb0, 0x162: 0xb1, 0x163: 0xb2, 0x164: 0xb3, 0x165: 0xb4, 0x166: 0xb5, 0x167: 0xb6, 0x168: 0xb7, 0x169: 0xb8, 0x16a: 0xb9, 0x16b: 0xba, 0x16c: 0xbb, 0x16d: 0xbc, 0x16e: 0xbd, 0x16f: 0xbe, 0x170: 0xbf, 0x171: 0xc0, 0x172: 0xc1, 0x173: 0xc2, 0x174: 0x25, 0x175: 0x26, 0x176: 0x27, 0x177: 0xc3, 0x178: 0x28, 0x179: 0x28, 0x17a: 0x29, 0x17b: 0x28, 0x17c: 0xc4, 0x17d: 0x2a, 0x17e: 0x2b, 0x17f: 0x2c, // Block 0x6, offset 0x180 0x180: 0x2d, 0x181: 0x2e, 0x182: 0x2f, 0x183: 0xc5, 0x184: 0x30, 0x185: 0x31, 0x186: 0xc6, 0x187: 0x9b, 0x188: 0xc7, 0x189: 0xc8, 0x18a: 0x9b, 0x18b: 0x9b, 0x18c: 0xc9, 0x18d: 0x9b, 0x18e: 0x9b, 0x18f: 0x9b, 0x190: 0xca, 0x191: 0x32, 0x192: 0x33, 0x193: 0x34, 0x194: 0x9b, 0x195: 0x9b, 0x196: 0x9b, 0x197: 0x9b, 0x198: 0x9b, 0x199: 0x9b, 0x19a: 0x9b, 0x19b: 0x9b, 0x19c: 0x9b, 0x19d: 0x9b, 0x19e: 0x9b, 0x19f: 0x9b, 0x1a0: 0x9b, 0x1a1: 0x9b, 0x1a2: 0x9b, 0x1a3: 0x9b, 0x1a4: 0x9b, 0x1a5: 0x9b, 0x1a6: 0x9b, 0x1a7: 0x9b, 0x1a8: 0xcb, 0x1a9: 0xcc, 0x1aa: 0x9b, 0x1ab: 0xcd, 0x1ac: 0x9b, 0x1ad: 0xce, 0x1ae: 0xcf, 0x1af: 0xd0, 0x1b0: 0xd1, 0x1b1: 0x35, 0x1b2: 0x28, 0x1b3: 0x36, 0x1b4: 0xd2, 0x1b5: 0xd3, 0x1b6: 0xd4, 0x1b7: 0xd5, 0x1b8: 0xd6, 0x1b9: 0xd7, 0x1ba: 0xd8, 0x1bb: 0xd9, 0x1bc: 0xda, 0x1bd: 0xdb, 0x1be: 0xdc, 0x1bf: 0x37, // Block 0x7, offset 0x1c0 0x1c0: 0x38, 0x1c1: 0xdd, 0x1c2: 0xde, 0x1c3: 0xdf, 0x1c4: 0xe0, 0x1c5: 0x39, 0x1c6: 0x3a, 0x1c7: 0xe1, 0x1c8: 0xe2, 0x1c9: 0x3b, 0x1ca: 0x3c, 0x1cb: 0x3d, 0x1cc: 0x3e, 0x1cd: 0x3f, 0x1ce: 0x40, 0x1cf: 0x41, 0x1d0: 0x9f, 0x1d1: 0x9f, 0x1d2: 0x9f, 0x1d3: 0x9f, 0x1d4: 0x9f, 0x1d5: 0x9f, 0x1d6: 0x9f, 0x1d7: 0x9f, 0x1d8: 0x9f, 0x1d9: 0x9f, 0x1da: 0x9f, 0x1db: 0x9f, 0x1dc: 0x9f, 0x1dd: 0x9f, 0x1de: 0x9f, 0x1df: 0x9f, 0x1e0: 0x9f, 0x1e1: 0x9f, 0x1e2: 0x9f, 0x1e3: 0x9f, 0x1e4: 0x9f, 0x1e5: 0x9f, 0x1e6: 0x9f, 0x1e7: 0x9f, 0x1e8: 0x9f, 0x1e9: 0x9f, 0x1ea: 0x9f, 0x1eb: 0x9f, 0x1ec: 0x9f, 0x1ed: 0x9f, 0x1ee: 0x9f, 0x1ef: 0x9f, 0x1f0: 0x9f, 0x1f1: 0x9f, 0x1f2: 0x9f, 0x1f3: 0x9f, 0x1f4: 0x9f, 0x1f5: 0x9f, 0x1f6: 0x9f, 0x1f7: 0x9f, 0x1f8: 0x9f, 0x1f9: 0x9f, 0x1fa: 0x9f, 0x1fb: 0x9f, 0x1fc: 0x9f, 0x1fd: 0x9f, 0x1fe: 0x9f, 0x1ff: 0x9f, // Block 0x8, offset 0x200 0x200: 0x9f, 0x201: 0x9f, 0x202: 0x9f, 0x203: 0x9f, 0x204: 0x9f, 0x205: 0x9f, 0x206: 0x9f, 0x207: 0x9f, 0x208: 0x9f, 0x209: 0x9f, 0x20a: 0x9f, 0x20b: 0x9f, 0x20c: 0x9f, 0x20d: 0x9f, 0x20e: 0x9f, 0x20f: 0x9f, 0x210: 0x9f, 0x211: 0x9f, 0x212: 0x9f, 0x213: 0x9f, 0x214: 0x9f, 0x215: 0x9f, 0x216: 0x9f, 0x217: 0x9f, 0x218: 0x9f, 0x219: 0x9f, 0x21a: 0x9f, 0x21b: 0x9f, 0x21c: 0x9f, 0x21d: 0x9f, 0x21e: 0x9f, 0x21f: 0x9f, 0x220: 0x9f, 0x221: 0x9f, 0x222: 0x9f, 0x223: 0x9f, 0x224: 0x9f, 0x225: 0x9f, 0x226: 0x9f, 0x227: 0x9f, 0x228: 0x9f, 0x229: 0x9f, 0x22a: 0x9f, 0x22b: 0x9f, 0x22c: 0x9f, 0x22d: 0x9f, 0x22e: 0x9f, 0x22f: 0x9f, 0x230: 0x9f, 0x231: 0x9f, 0x232: 0x9f, 0x233: 0x9f, 0x234: 0x9f, 0x235: 0x9f, 0x236: 0xb2, 0x237: 0x9b, 0x238: 0x9f, 0x239: 0x9f, 0x23a: 0x9f, 0x23b: 0x9f, 0x23c: 0x9f, 0x23d: 0x9f, 0x23e: 0x9f, 0x23f: 0x9f, // Block 0x9, offset 0x240 0x240: 0x9f, 0x241: 0x9f, 0x242: 0x9f, 0x243: 0x9f, 0x244: 0x9f, 0x245: 0x9f, 0x246: 0x9f, 0x247: 0x9f, 0x248: 0x9f, 0x249: 0x9f, 0x24a: 0x9f, 0x24b: 0x9f, 0x24c: 0x9f, 0x24d: 0x9f, 0x24e: 0x9f, 0x24f: 0x9f, 0x250: 0x9f, 0x251: 0x9f, 0x252: 0x9f, 0x253: 0x9f, 0x254: 0x9f, 0x255: 0x9f, 0x256: 0x9f, 0x257: 0x9f, 0x258: 0x9f, 0x259: 0x9f, 0x25a: 0x9f, 0x25b: 0x9f, 0x25c: 0x9f, 0x25d: 0x9f, 0x25e: 0x9f, 0x25f: 0x9f, 0x260: 0x9f, 0x261: 0x9f, 0x262: 0x9f, 0x263: 0x9f, 0x264: 0x9f, 0x265: 0x9f, 0x266: 0x9f, 0x267: 0x9f, 0x268: 0x9f, 0x269: 0x9f, 0x26a: 0x9f, 0x26b: 0x9f, 0x26c: 0x9f, 0x26d: 0x9f, 0x26e: 0x9f, 0x26f: 0x9f, 0x270: 0x9f, 0x271: 0x9f, 0x272: 0x9f, 0x273: 0x9f, 0x274: 0x9f, 0x275: 0x9f, 0x276: 0x9f, 0x277: 0x9f, 0x278: 0x9f, 0x279: 0x9f, 0x27a: 0x9f, 0x27b: 0x9f, 0x27c: 0x9f, 0x27d: 0x9f, 0x27e: 0x9f, 0x27f: 0x9f, // Block 0xa, offset 0x280 0x280: 0x9f, 0x281: 0x9f, 0x282: 0x9f, 0x283: 0x9f, 0x284: 0x9f, 0x285: 0x9f, 0x286: 0x9f, 0x287: 0x9f, 0x288: 0x9f, 0x289: 0x9f, 0x28a: 0x9f, 0x28b: 0x9f, 0x28c: 0x9f, 0x28d: 0x9f, 0x28e: 0x9f, 0x28f: 0x9f, 0x290: 0x9f, 0x291: 0x9f, 0x292: 0x9f, 0x293: 0x9f, 0x294: 0x9f, 0x295: 0x9f, 0x296: 0x9f, 0x297: 0x9f, 0x298: 0x9f, 0x299: 0x9f, 0x29a: 0x9f, 0x29b: 0x9f, 0x29c: 0x9f, 0x29d: 0x9f, 0x29e: 0x9f, 0x29f: 0x9f, 0x2a0: 0x9f, 0x2a1: 0x9f, 0x2a2: 0x9f, 0x2a3: 0x9f, 0x2a4: 0x9f, 0x2a5: 0x9f, 0x2a6: 0x9f, 0x2a7: 0x9f, 0x2a8: 0x9f, 0x2a9: 0x9f, 0x2aa: 0x9f, 0x2ab: 0x9f, 0x2ac: 0x9f, 0x2ad: 0x9f, 0x2ae: 0x9f, 0x2af: 0x9f, 0x2b0: 0x9f, 0x2b1: 0x9f, 0x2b2: 0x9f, 0x2b3: 0x9f, 0x2b4: 0x9f, 0x2b5: 0x9f, 0x2b6: 0x9f, 0x2b7: 0x9f, 0x2b8: 0x9f, 0x2b9: 0x9f, 0x2ba: 0x9f, 0x2bb: 0x9f, 0x2bc: 0x9f, 0x2bd: 0x9f, 0x2be: 0x9f, 0x2bf: 0xe3, // Block 0xb, offset 0x2c0 0x2c0: 0x9f, 0x2c1: 0x9f, 0x2c2: 0x9f, 0x2c3: 0x9f, 0x2c4: 0x9f, 0x2c5: 0x9f, 0x2c6: 0x9f, 0x2c7: 0x9f, 0x2c8: 0x9f, 0x2c9: 0x9f, 0x2ca: 0x9f, 0x2cb: 0x9f, 0x2cc: 0x9f, 0x2cd: 0x9f, 0x2ce: 0x9f, 0x2cf: 0x9f, 0x2d0: 0x9f, 0x2d1: 0x9f, 0x2d2: 0xe4, 0x2d3: 0xe5, 0x2d4: 0x9f, 0x2d5: 0x9f, 0x2d6: 0x9f, 0x2d7: 0x9f, 0x2d8: 0xe6, 0x2d9: 0x42, 0x2da: 0x43, 0x2db: 0xe7, 0x2dc: 0x44, 0x2dd: 0x45, 0x2de: 0x46, 0x2df: 0xe8, 0x2e0: 0xe9, 0x2e1: 0xea, 0x2e2: 0xeb, 0x2e3: 0xec, 0x2e4: 0xed, 0x2e5: 0xee, 0x2e6: 0xef, 0x2e7: 0xf0, 0x2e8: 0xf1, 0x2e9: 0xf2, 0x2ea: 0xf3, 0x2eb: 0xf4, 0x2ec: 0xf5, 0x2ed: 0xf6, 0x2ee: 0xf7, 0x2ef: 0xf8, 0x2f0: 0x9f, 0x2f1: 0x9f, 0x2f2: 0x9f, 0x2f3: 0x9f, 0x2f4: 0x9f, 0x2f5: 0x9f, 0x2f6: 0x9f, 0x2f7: 0x9f, 0x2f8: 0x9f, 0x2f9: 0x9f, 0x2fa: 0x9f, 0x2fb: 0x9f, 0x2fc: 0x9f, 0x2fd: 0x9f, 0x2fe: 0x9f, 0x2ff: 0x9f, // Block 0xc, offset 0x300 0x300: 0x9f, 0x301: 0x9f, 0x302: 0x9f, 0x303: 0x9f, 0x304: 0x9f, 0x305: 0x9f, 0x306: 0x9f, 0x307: 0x9f, 0x308: 0x9f, 0x309: 0x9f, 0x30a: 0x9f, 0x30b: 0x9f, 0x30c: 0x9f, 0x30d: 0x9f, 0x30e: 0x9f, 0x30f: 0x9f, 0x310: 0x9f, 0x311: 0x9f, 0x312: 0x9f, 0x313: 0x9f, 0x314: 0x9f, 0x315: 0x9f, 0x316: 0x9f, 0x317: 0x9f, 0x318: 0x9f, 0x319: 0x9f, 0x31a: 0x9f, 0x31b: 0x9f, 0x31c: 0x9f, 0x31d: 0x9f, 0x31e: 0xf9, 0x31f: 0xfa, // Block 0xd, offset 0x340 0x340: 0xba, 0x341: 0xba, 0x342: 0xba, 0x343: 0xba, 0x344: 0xba, 0x345: 0xba, 0x346: 0xba, 0x347: 0xba, 0x348: 0xba, 0x349: 0xba, 0x34a: 0xba, 0x34b: 0xba, 0x34c: 0xba, 0x34d: 0xba, 0x34e: 0xba, 0x34f: 0xba, 0x350: 0xba, 0x351: 0xba, 0x352: 0xba, 0x353: 0xba, 0x354: 0xba, 0x355: 0xba, 0x356: 0xba, 0x357: 0xba, 0x358: 0xba, 0x359: 0xba, 0x35a: 0xba, 0x35b: 0xba, 0x35c: 0xba, 0x35d: 0xba, 0x35e: 0xba, 0x35f: 0xba, 0x360: 0xba, 0x361: 0xba, 0x362: 0xba, 0x363: 0xba, 0x364: 0xba, 0x365: 0xba, 0x366: 0xba, 0x367: 0xba, 0x368: 0xba, 0x369: 0xba, 0x36a: 0xba, 0x36b: 0xba, 0x36c: 0xba, 0x36d: 0xba, 0x36e: 0xba, 0x36f: 0xba, 0x370: 0xba, 0x371: 0xba, 0x372: 0xba, 0x373: 0xba, 0x374: 0xba, 0x375: 0xba, 0x376: 0xba, 0x377: 0xba, 0x378: 0xba, 0x379: 0xba, 0x37a: 0xba, 0x37b: 0xba, 0x37c: 0xba, 0x37d: 0xba, 0x37e: 0xba, 0x37f: 0xba, // Block 0xe, offset 0x380 0x380: 0xba, 0x381: 0xba, 0x382: 0xba, 0x383: 0xba, 0x384: 0xba, 0x385: 0xba, 0x386: 0xba, 0x387: 0xba, 0x388: 0xba, 0x389: 0xba, 0x38a: 0xba, 0x38b: 0xba, 0x38c: 0xba, 0x38d: 0xba, 0x38e: 0xba, 0x38f: 0xba, 0x390: 0xba, 0x391: 0xba, 0x392: 0xba, 0x393: 0xba, 0x394: 0xba, 0x395: 0xba, 0x396: 0xba, 0x397: 0xba, 0x398: 0xba, 0x399: 0xba, 0x39a: 0xba, 0x39b: 0xba, 0x39c: 0xba, 0x39d: 0xba, 0x39e: 0xba, 0x39f: 0xba, 0x3a0: 0xba, 0x3a1: 0xba, 0x3a2: 0xba, 0x3a3: 0xba, 0x3a4: 0xfb, 0x3a5: 0xfc, 0x3a6: 0xfd, 0x3a7: 0xfe, 0x3a8: 0x47, 0x3a9: 0xff, 0x3aa: 0x100, 0x3ab: 0x48, 0x3ac: 0x49, 0x3ad: 0x4a, 0x3ae: 0x4b, 0x3af: 0x4c, 0x3b0: 0x101, 0x3b1: 0x4d, 0x3b2: 0x4e, 0x3b3: 0x4f, 0x3b4: 0x50, 0x3b5: 0x51, 0x3b6: 0x102, 0x3b7: 0x52, 0x3b8: 0x53, 0x3b9: 0x54, 0x3ba: 0x55, 0x3bb: 0x56, 0x3bc: 0x57, 0x3bd: 0x58, 0x3be: 0x59, 0x3bf: 0x5a, // Block 0xf, offset 0x3c0 0x3c0: 0x103, 0x3c1: 0x104, 0x3c2: 0x9f, 0x3c3: 0x105, 0x3c4: 0x106, 0x3c5: 0x9b, 0x3c6: 0x107, 0x3c7: 0x108, 0x3c8: 0xba, 0x3c9: 0xba, 0x3ca: 0x109, 0x3cb: 0x10a, 0x3cc: 0x10b, 0x3cd: 0x10c, 0x3ce: 0x10d, 0x3cf: 0x10e, 0x3d0: 0x10f, 0x3d1: 0x9f, 0x3d2: 0x110, 0x3d3: 0x111, 0x3d4: 0x112, 0x3d5: 0x113, 0x3d6: 0xba, 0x3d7: 0xba, 0x3d8: 0x9f, 0x3d9: 0x9f, 0x3da: 0x9f, 0x3db: 0x9f, 0x3dc: 0x114, 0x3dd: 0x115, 0x3de: 0xba, 0x3df: 0xba, 0x3e0: 0x116, 0x3e1: 0x117, 0x3e2: 0x118, 0x3e3: 0x119, 0x3e4: 0x11a, 0x3e5: 0xba, 0x3e6: 0x11b, 0x3e7: 0x11c, 0x3e8: 0x11d, 0x3e9: 0x11e, 0x3ea: 0x11f, 0x3eb: 0x5b, 0x3ec: 0x120, 0x3ed: 0x121, 0x3ee: 0x5c, 0x3ef: 0xba, 0x3f0: 0x122, 0x3f1: 0x123, 0x3f2: 0x124, 0x3f3: 0x125, 0x3f4: 0x126, 0x3f5: 0xba, 0x3f6: 0xba, 0x3f7: 0xba, 0x3f8: 0xba, 0x3f9: 0x127, 0x3fa: 0xba, 0x3fb: 0xba, 0x3fc: 0x128, 0x3fd: 0x129, 0x3fe: 0xba, 0x3ff: 0xba, // Block 0x10, offset 0x400 0x400: 0x12a, 0x401: 0x12b, 0x402: 0x12c, 0x403: 0x12d, 0x404: 0x12e, 0x405: 0x12f, 0x406: 0x130, 0x407: 0x131, 0x408: 0x132, 0x409: 0xba, 0x40a: 0x133, 0x40b: 0x134, 0x40c: 0x5d, 0x40d: 0x5e, 0x40e: 0xba, 0x40f: 0xba, 0x410: 0x135, 0x411: 0x136, 0x412: 0x137, 0x413: 0x138, 0x414: 0xba, 0x415: 0xba, 0x416: 0x139, 0x417: 0x13a, 0x418: 0x13b, 0x419: 0x13c, 0x41a: 0x13d, 0x41b: 0x13e, 0x41c: 0x13f, 0x41d: 0xba, 0x41e: 0xba, 0x41f: 0xba, 0x420: 0x140, 0x421: 0xba, 0x422: 0x141, 0x423: 0x142, 0x424: 0xba, 0x425: 0xba, 0x426: 0xba, 0x427: 0xba, 0x428: 0x143, 0x429: 0x144, 0x42a: 0x145, 0x42b: 0x146, 0x42c: 0xba, 0x42d: 0xba, 0x42e: 0xba, 0x42f: 0xba, 0x430: 0x147, 0x431: 0x148, 0x432: 0x149, 0x433: 0xba, 0x434: 0x14a, 0x435: 0x14b, 0x436: 0x14c, 0x437: 0xba, 0x438: 0xba, 0x439: 0xba, 0x43a: 0xba, 0x43b: 0x14d, 0x43c: 0xba, 0x43d: 0xba, 0x43e: 0xba, 0x43f: 0xba, // Block 0x11, offset 0x440 0x440: 0x9f, 0x441: 0x9f, 0x442: 0x9f, 0x443: 0x9f, 0x444: 0x9f, 0x445: 0x9f, 0x446: 0x9f, 0x447: 0x9f, 0x448: 0x9f, 0x449: 0x9f, 0x44a: 0x9f, 0x44b: 0x9f, 0x44c: 0x9f, 0x44d: 0x9f, 0x44e: 0x14e, 0x44f: 0xba, 0x450: 0x9b, 0x451: 0x14f, 0x452: 0x9f, 0x453: 0x9f, 0x454: 0x9f, 0x455: 0x150, 0x456: 0xba, 0x457: 0xba, 0x458: 0xba, 0x459: 0xba, 0x45a: 0xba, 0x45b: 0xba, 0x45c: 0xba, 0x45d: 0xba, 0x45e: 0xba, 0x45f: 0xba, 0x460: 0xba, 0x461: 0xba, 0x462: 0xba, 0x463: 0xba, 0x464: 0xba, 0x465: 0xba, 0x466: 0xba, 0x467: 0xba, 0x468: 0xba, 0x469: 0xba, 0x46a: 0xba, 0x46b: 0xba, 0x46c: 0xba, 0x46d: 0xba, 0x46e: 0xba, 0x46f: 0xba, 0x470: 0xba, 0x471: 0xba, 0x472: 0xba, 0x473: 0xba, 0x474: 0xba, 0x475: 0xba, 0x476: 0xba, 0x477: 0xba, 0x478: 0xba, 0x479: 0xba, 0x47a: 0xba, 0x47b: 0xba, 0x47c: 0xba, 0x47d: 0xba, 0x47e: 0xba, 0x47f: 0xba, // Block 0x12, offset 0x480 0x480: 0x9f, 0x481: 0x9f, 0x482: 0x9f, 0x483: 0x9f, 0x484: 0x9f, 0x485: 0x9f, 0x486: 0x9f, 0x487: 0x9f, 0x488: 0x9f, 0x489: 0x9f, 0x48a: 0x9f, 0x48b: 0x9f, 0x48c: 0x9f, 0x48d: 0x9f, 0x48e: 0x9f, 0x48f: 0x9f, 0x490: 0x151, 0x491: 0xba, 0x492: 0xba, 0x493: 0xba, 0x494: 0xba, 0x495: 0xba, 0x496: 0xba, 0x497: 0xba, 0x498: 0xba, 0x499: 0xba, 0x49a: 0xba, 0x49b: 0xba, 0x49c: 0xba, 0x49d: 0xba, 0x49e: 0xba, 0x49f: 0xba, 0x4a0: 0xba, 0x4a1: 0xba, 0x4a2: 0xba, 0x4a3: 0xba, 0x4a4: 0xba, 0x4a5: 0xba, 0x4a6: 0xba, 0x4a7: 0xba, 0x4a8: 0xba, 0x4a9: 0xba, 0x4aa: 0xba, 0x4ab: 0xba, 0x4ac: 0xba, 0x4ad: 0xba, 0x4ae: 0xba, 0x4af: 0xba, 0x4b0: 0xba, 0x4b1: 0xba, 0x4b2: 0xba, 0x4b3: 0xba, 0x4b4: 0xba, 0x4b5: 0xba, 0x4b6: 0xba, 0x4b7: 0xba, 0x4b8: 0xba, 0x4b9: 0xba, 0x4ba: 0xba, 0x4bb: 0xba, 0x4bc: 0xba, 0x4bd: 0xba, 0x4be: 0xba, 0x4bf: 0xba, // Block 0x13, offset 0x4c0 0x4c0: 0xba, 0x4c1: 0xba, 0x4c2: 0xba, 0x4c3: 0xba, 0x4c4: 0xba, 0x4c5: 0xba, 0x4c6: 0xba, 0x4c7: 0xba, 0x4c8: 0xba, 0x4c9: 0xba, 0x4ca: 0xba, 0x4cb: 0xba, 0x4cc: 0xba, 0x4cd: 0xba, 0x4ce: 0xba, 0x4cf: 0xba, 0x4d0: 0x9f, 0x4d1: 0x9f, 0x4d2: 0x9f, 0x4d3: 0x9f, 0x4d4: 0x9f, 0x4d5: 0x9f, 0x4d6: 0x9f, 0x4d7: 0x9f, 0x4d8: 0x9f, 0x4d9: 0x152, 0x4da: 0xba, 0x4db: 0xba, 0x4dc: 0xba, 0x4dd: 0xba, 0x4de: 0xba, 0x4df: 0xba, 0x4e0: 0xba, 0x4e1: 0xba, 0x4e2: 0xba, 0x4e3: 0xba, 0x4e4: 0xba, 0x4e5: 0xba, 0x4e6: 0xba, 0x4e7: 0xba, 0x4e8: 0xba, 0x4e9: 0xba, 0x4ea: 0xba, 0x4eb: 0xba, 0x4ec: 0xba, 0x4ed: 0xba, 0x4ee: 0xba, 0x4ef: 0xba, 0x4f0: 0xba, 0x4f1: 0xba, 0x4f2: 0xba, 0x4f3: 0xba, 0x4f4: 0xba, 0x4f5: 0xba, 0x4f6: 0xba, 0x4f7: 0xba, 0x4f8: 0xba, 0x4f9: 0xba, 0x4fa: 0xba, 0x4fb: 0xba, 0x4fc: 0xba, 0x4fd: 0xba, 0x4fe: 0xba, 0x4ff: 0xba, // Block 0x14, offset 0x500 0x500: 0xba, 0x501: 0xba, 0x502: 0xba, 0x503: 0xba, 0x504: 0xba, 0x505: 0xba, 0x506: 0xba, 0x507: 0xba, 0x508: 0xba, 0x509: 0xba, 0x50a: 0xba, 0x50b: 0xba, 0x50c: 0xba, 0x50d: 0xba, 0x50e: 0xba, 0x50f: 0xba, 0x510: 0xba, 0x511: 0xba, 0x512: 0xba, 0x513: 0xba, 0x514: 0xba, 0x515: 0xba, 0x516: 0xba, 0x517: 0xba, 0x518: 0xba, 0x519: 0xba, 0x51a: 0xba, 0x51b: 0xba, 0x51c: 0xba, 0x51d: 0xba, 0x51e: 0xba, 0x51f: 0xba, 0x520: 0x9f, 0x521: 0x9f, 0x522: 0x9f, 0x523: 0x9f, 0x524: 0x9f, 0x525: 0x9f, 0x526: 0x9f, 0x527: 0x9f, 0x528: 0x146, 0x529: 0x153, 0x52a: 0xba, 0x52b: 0x154, 0x52c: 0x155, 0x52d: 0x156, 0x52e: 0x157, 0x52f: 0xba, 0x530: 0xba, 0x531: 0xba, 0x532: 0xba, 0x533: 0xba, 0x534: 0xba, 0x535: 0xba, 0x536: 0xba, 0x537: 0xba, 0x538: 0xba, 0x539: 0x158, 0x53a: 0x159, 0x53b: 0xba, 0x53c: 0x9f, 0x53d: 0x15a, 0x53e: 0x15b, 0x53f: 0x15c, // Block 0x15, offset 0x540 0x540: 0x9f, 0x541: 0x9f, 0x542: 0x9f, 0x543: 0x9f, 0x544: 0x9f, 0x545: 0x9f, 0x546: 0x9f, 0x547: 0x9f, 0x548: 0x9f, 0x549: 0x9f, 0x54a: 0x9f, 0x54b: 0x9f, 0x54c: 0x9f, 0x54d: 0x9f, 0x54e: 0x9f, 0x54f: 0x9f, 0x550: 0x9f, 0x551: 0x9f, 0x552: 0x9f, 0x553: 0x9f, 0x554: 0x9f, 0x555: 0x9f, 0x556: 0x9f, 0x557: 0x9f, 0x558: 0x9f, 0x559: 0x9f, 0x55a: 0x9f, 0x55b: 0x9f, 0x55c: 0x9f, 0x55d: 0x9f, 0x55e: 0x9f, 0x55f: 0x15d, 0x560: 0x9f, 0x561: 0x9f, 0x562: 0x9f, 0x563: 0x9f, 0x564: 0x9f, 0x565: 0x9f, 0x566: 0x9f, 0x567: 0x9f, 0x568: 0x9f, 0x569: 0x9f, 0x56a: 0x9f, 0x56b: 0x15e, 0x56c: 0xba, 0x56d: 0xba, 0x56e: 0xba, 0x56f: 0xba, 0x570: 0xba, 0x571: 0xba, 0x572: 0xba, 0x573: 0xba, 0x574: 0xba, 0x575: 0xba, 0x576: 0xba, 0x577: 0xba, 0x578: 0xba, 0x579: 0xba, 0x57a: 0xba, 0x57b: 0xba, 0x57c: 0xba, 0x57d: 0xba, 0x57e: 0xba, 0x57f: 0xba, // Block 0x16, offset 0x580 0x580: 0x9f, 0x581: 0x9f, 0x582: 0x9f, 0x583: 0x9f, 0x584: 0x15f, 0x585: 0x160, 0x586: 0x9f, 0x587: 0x9f, 0x588: 0x9f, 0x589: 0x9f, 0x58a: 0x9f, 0x58b: 0x161, 0x58c: 0xba, 0x58d: 0xba, 0x58e: 0xba, 0x58f: 0xba, 0x590: 0xba, 0x591: 0xba, 0x592: 0xba, 0x593: 0xba, 0x594: 0xba, 0x595: 0xba, 0x596: 0xba, 0x597: 0xba, 0x598: 0xba, 0x599: 0xba, 0x59a: 0xba, 0x59b: 0xba, 0x59c: 0xba, 0x59d: 0xba, 0x59e: 0xba, 0x59f: 0xba, 0x5a0: 0xba, 0x5a1: 0xba, 0x5a2: 0xba, 0x5a3: 0xba, 0x5a4: 0xba, 0x5a5: 0xba, 0x5a6: 0xba, 0x5a7: 0xba, 0x5a8: 0xba, 0x5a9: 0xba, 0x5aa: 0xba, 0x5ab: 0xba, 0x5ac: 0xba, 0x5ad: 0xba, 0x5ae: 0xba, 0x5af: 0xba, 0x5b0: 0x9f, 0x5b1: 0x162, 0x5b2: 0x163, 0x5b3: 0xba, 0x5b4: 0xba, 0x5b5: 0xba, 0x5b6: 0xba, 0x5b7: 0xba, 0x5b8: 0xba, 0x5b9: 0xba, 0x5ba: 0xba, 0x5bb: 0xba, 0x5bc: 0xba, 0x5bd: 0xba, 0x5be: 0xba, 0x5bf: 0xba, // Block 0x17, offset 0x5c0 0x5c0: 0x9b, 0x5c1: 0x9b, 0x5c2: 0x9b, 0x5c3: 0x164, 0x5c4: 0x165, 0x5c5: 0x166, 0x5c6: 0x167, 0x5c7: 0x168, 0x5c8: 0x9b, 0x5c9: 0x169, 0x5ca: 0xba, 0x5cb: 0x16a, 0x5cc: 0x9b, 0x5cd: 0x16b, 0x5ce: 0xba, 0x5cf: 0xba, 0x5d0: 0x5f, 0x5d1: 0x60, 0x5d2: 0x61, 0x5d3: 0x62, 0x5d4: 0x63, 0x5d5: 0x64, 0x5d6: 0x65, 0x5d7: 0x66, 0x5d8: 0x67, 0x5d9: 0x68, 0x5da: 0x69, 0x5db: 0x6a, 0x5dc: 0x6b, 0x5dd: 0x6c, 0x5de: 0x6d, 0x5df: 0x6e, 0x5e0: 0x9b, 0x5e1: 0x9b, 0x5e2: 0x9b, 0x5e3: 0x9b, 0x5e4: 0x9b, 0x5e5: 0x9b, 0x5e6: 0x9b, 0x5e7: 0x9b, 0x5e8: 0x16c, 0x5e9: 0x16d, 0x5ea: 0x16e, 0x5eb: 0xba, 0x5ec: 0xba, 0x5ed: 0xba, 0x5ee: 0xba, 0x5ef: 0xba, 0x5f0: 0xba, 0x5f1: 0xba, 0x5f2: 0xba, 0x5f3: 0xba, 0x5f4: 0xba, 0x5f5: 0xba, 0x5f6: 0xba, 0x5f7: 0xba, 0x5f8: 0xba, 0x5f9: 0xba, 0x5fa: 0xba, 0x5fb: 0xba, 0x5fc: 0xba, 0x5fd: 0xba, 0x5fe: 0xba, 0x5ff: 0xba, // Block 0x18, offset 0x600 0x600: 0x16f, 0x601: 0xba, 0x602: 0xba, 0x603: 0xba, 0x604: 0xba, 0x605: 0xba, 0x606: 0xba, 0x607: 0xba, 0x608: 0xba, 0x609: 0xba, 0x60a: 0xba, 0x60b: 0xba, 0x60c: 0xba, 0x60d: 0xba, 0x60e: 0xba, 0x60f: 0xba, 0x610: 0xba, 0x611: 0xba, 0x612: 0xba, 0x613: 0xba, 0x614: 0xba, 0x615: 0xba, 0x616: 0xba, 0x617: 0xba, 0x618: 0xba, 0x619: 0xba, 0x61a: 0xba, 0x61b: 0xba, 0x61c: 0xba, 0x61d: 0xba, 0x61e: 0xba, 0x61f: 0xba, 0x620: 0x122, 0x621: 0x122, 0x622: 0x122, 0x623: 0x170, 0x624: 0x6f, 0x625: 0x171, 0x626: 0xba, 0x627: 0xba, 0x628: 0xba, 0x629: 0xba, 0x62a: 0xba, 0x62b: 0xba, 0x62c: 0xba, 0x62d: 0xba, 0x62e: 0xba, 0x62f: 0xba, 0x630: 0xba, 0x631: 0x172, 0x632: 0x173, 0x633: 0xba, 0x634: 0xba, 0x635: 0xba, 0x636: 0xba, 0x637: 0xba, 0x638: 0x70, 0x639: 0x71, 0x63a: 0x72, 0x63b: 0x174, 0x63c: 0xba, 0x63d: 0xba, 0x63e: 0xba, 0x63f: 0xba, // Block 0x19, offset 0x640 0x640: 0x175, 0x641: 0x9b, 0x642: 0x176, 0x643: 0x177, 0x644: 0x73, 0x645: 0x74, 0x646: 0x178, 0x647: 0x179, 0x648: 0x75, 0x649: 0x17a, 0x64a: 0xba, 0x64b: 0xba, 0x64c: 0x9b, 0x64d: 0x9b, 0x64e: 0x9b, 0x64f: 0x9b, 0x650: 0x9b, 0x651: 0x9b, 0x652: 0x9b, 0x653: 0x9b, 0x654: 0x9b, 0x655: 0x9b, 0x656: 0x9b, 0x657: 0x9b, 0x658: 0x9b, 0x659: 0x9b, 0x65a: 0x9b, 0x65b: 0x17b, 0x65c: 0x9b, 0x65d: 0x17c, 0x65e: 0x9b, 0x65f: 0x17d, 0x660: 0x17e, 0x661: 0x17f, 0x662: 0x180, 0x663: 0xba, 0x664: 0x181, 0x665: 0x182, 0x666: 0x183, 0x667: 0x184, 0x668: 0xba, 0x669: 0x185, 0x66a: 0xba, 0x66b: 0xba, 0x66c: 0xba, 0x66d: 0xba, 0x66e: 0xba, 0x66f: 0xba, 0x670: 0xba, 0x671: 0xba, 0x672: 0xba, 0x673: 0xba, 0x674: 0xba, 0x675: 0xba, 0x676: 0xba, 0x677: 0xba, 0x678: 0xba, 0x679: 0xba, 0x67a: 0xba, 0x67b: 0xba, 0x67c: 0xba, 0x67d: 0xba, 0x67e: 0xba, 0x67f: 0xba, // Block 0x1a, offset 0x680 0x680: 0x9f, 0x681: 0x9f, 0x682: 0x9f, 0x683: 0x9f, 0x684: 0x9f, 0x685: 0x9f, 0x686: 0x9f, 0x687: 0x9f, 0x688: 0x9f, 0x689: 0x9f, 0x68a: 0x9f, 0x68b: 0x9f, 0x68c: 0x9f, 0x68d: 0x9f, 0x68e: 0x9f, 0x68f: 0x9f, 0x690: 0x9f, 0x691: 0x9f, 0x692: 0x9f, 0x693: 0x9f, 0x694: 0x9f, 0x695: 0x9f, 0x696: 0x9f, 0x697: 0x9f, 0x698: 0x9f, 0x699: 0x9f, 0x69a: 0x9f, 0x69b: 0x186, 0x69c: 0x9f, 0x69d: 0x9f, 0x69e: 0x9f, 0x69f: 0x9f, 0x6a0: 0x9f, 0x6a1: 0x9f, 0x6a2: 0x9f, 0x6a3: 0x9f, 0x6a4: 0x9f, 0x6a5: 0x9f, 0x6a6: 0x9f, 0x6a7: 0x9f, 0x6a8: 0x9f, 0x6a9: 0x9f, 0x6aa: 0x9f, 0x6ab: 0x9f, 0x6ac: 0x9f, 0x6ad: 0x9f, 0x6ae: 0x9f, 0x6af: 0x9f, 0x6b0: 0x9f, 0x6b1: 0x9f, 0x6b2: 0x9f, 0x6b3: 0x9f, 0x6b4: 0x9f, 0x6b5: 0x9f, 0x6b6: 0x9f, 0x6b7: 0x9f, 0x6b8: 0x9f, 0x6b9: 0x9f, 0x6ba: 0x9f, 0x6bb: 0x9f, 0x6bc: 0x9f, 0x6bd: 0x9f, 0x6be: 0x9f, 0x6bf: 0x9f, // Block 0x1b, offset 0x6c0 0x6c0: 0x9f, 0x6c1: 0x9f, 0x6c2: 0x9f, 0x6c3: 0x9f, 0x6c4: 0x9f, 0x6c5: 0x9f, 0x6c6: 0x9f, 0x6c7: 0x9f, 0x6c8: 0x9f, 0x6c9: 0x9f, 0x6ca: 0x9f, 0x6cb: 0x9f, 0x6cc: 0x9f, 0x6cd: 0x9f, 0x6ce: 0x9f, 0x6cf: 0x9f, 0x6d0: 0x9f, 0x6d1: 0x9f, 0x6d2: 0x9f, 0x6d3: 0x9f, 0x6d4: 0x9f, 0x6d5: 0x9f, 0x6d6: 0x9f, 0x6d7: 0x9f, 0x6d8: 0x9f, 0x6d9: 0x9f, 0x6da: 0x9f, 0x6db: 0x9f, 0x6dc: 0x187, 0x6dd: 0x9f, 0x6de: 0x9f, 0x6df: 0x9f, 0x6e0: 0x188, 0x6e1: 0x9f, 0x6e2: 0x9f, 0x6e3: 0x9f, 0x6e4: 0x9f, 0x6e5: 0x9f, 0x6e6: 0x9f, 0x6e7: 0x9f, 0x6e8: 0x9f, 0x6e9: 0x9f, 0x6ea: 0x9f, 0x6eb: 0x9f, 0x6ec: 0x9f, 0x6ed: 0x9f, 0x6ee: 0x9f, 0x6ef: 0x9f, 0x6f0: 0x9f, 0x6f1: 0x9f, 0x6f2: 0x9f, 0x6f3: 0x9f, 0x6f4: 0x9f, 0x6f5: 0x9f, 0x6f6: 0x9f, 0x6f7: 0x9f, 0x6f8: 0x9f, 0x6f9: 0x9f, 0x6fa: 0x9f, 0x6fb: 0x9f, 0x6fc: 0x9f, 0x6fd: 0x9f, 0x6fe: 0x9f, 0x6ff: 0x9f, // Block 0x1c, offset 0x700 0x700: 0x9f, 0x701: 0x9f, 0x702: 0x9f, 0x703: 0x9f, 0x704: 0x9f, 0x705: 0x9f, 0x706: 0x9f, 0x707: 0x9f, 0x708: 0x9f, 0x709: 0x9f, 0x70a: 0x9f, 0x70b: 0x9f, 0x70c: 0x9f, 0x70d: 0x9f, 0x70e: 0x9f, 0x70f: 0x9f, 0x710: 0x9f, 0x711: 0x9f, 0x712: 0x9f, 0x713: 0x9f, 0x714: 0x9f, 0x715: 0x9f, 0x716: 0x9f, 0x717: 0x9f, 0x718: 0x9f, 0x719: 0x9f, 0x71a: 0x9f, 0x71b: 0x9f, 0x71c: 0x9f, 0x71d: 0x9f, 0x71e: 0x9f, 0x71f: 0x9f, 0x720: 0x9f, 0x721: 0x9f, 0x722: 0x9f, 0x723: 0x9f, 0x724: 0x9f, 0x725: 0x9f, 0x726: 0x9f, 0x727: 0x9f, 0x728: 0x9f, 0x729: 0x9f, 0x72a: 0x9f, 0x72b: 0x9f, 0x72c: 0x9f, 0x72d: 0x9f, 0x72e: 0x9f, 0x72f: 0x9f, 0x730: 0x9f, 0x731: 0x9f, 0x732: 0x9f, 0x733: 0x9f, 0x734: 0x9f, 0x735: 0x9f, 0x736: 0x9f, 0x737: 0x9f, 0x738: 0x9f, 0x739: 0x9f, 0x73a: 0x189, 0x73b: 0x9f, 0x73c: 0x9f, 0x73d: 0x9f, 0x73e: 0x9f, 0x73f: 0x9f, // Block 0x1d, offset 0x740 0x740: 0x9f, 0x741: 0x9f, 0x742: 0x9f, 0x743: 0x9f, 0x744: 0x9f, 0x745: 0x9f, 0x746: 0x9f, 0x747: 0x9f, 0x748: 0x9f, 0x749: 0x9f, 0x74a: 0x9f, 0x74b: 0x9f, 0x74c: 0x9f, 0x74d: 0x9f, 0x74e: 0x9f, 0x74f: 0x9f, 0x750: 0x9f, 0x751: 0x9f, 0x752: 0x9f, 0x753: 0x9f, 0x754: 0x9f, 0x755: 0x9f, 0x756: 0x9f, 0x757: 0x9f, 0x758: 0x9f, 0x759: 0x9f, 0x75a: 0x9f, 0x75b: 0x9f, 0x75c: 0x9f, 0x75d: 0x9f, 0x75e: 0x9f, 0x75f: 0x9f, 0x760: 0x9f, 0x761: 0x9f, 0x762: 0x9f, 0x763: 0x9f, 0x764: 0x9f, 0x765: 0x9f, 0x766: 0x9f, 0x767: 0x9f, 0x768: 0x9f, 0x769: 0x9f, 0x76a: 0x9f, 0x76b: 0x9f, 0x76c: 0x9f, 0x76d: 0x9f, 0x76e: 0x9f, 0x76f: 0x18a, 0x770: 0xba, 0x771: 0xba, 0x772: 0xba, 0x773: 0xba, 0x774: 0xba, 0x775: 0xba, 0x776: 0xba, 0x777: 0xba, 0x778: 0xba, 0x779: 0xba, 0x77a: 0xba, 0x77b: 0xba, 0x77c: 0xba, 0x77d: 0xba, 0x77e: 0xba, 0x77f: 0xba, // Block 0x1e, offset 0x780 0x780: 0xba, 0x781: 0xba, 0x782: 0xba, 0x783: 0xba, 0x784: 0xba, 0x785: 0xba, 0x786: 0xba, 0x787: 0xba, 0x788: 0xba, 0x789: 0xba, 0x78a: 0xba, 0x78b: 0xba, 0x78c: 0xba, 0x78d: 0xba, 0x78e: 0xba, 0x78f: 0xba, 0x790: 0xba, 0x791: 0xba, 0x792: 0xba, 0x793: 0xba, 0x794: 0xba, 0x795: 0xba, 0x796: 0xba, 0x797: 0xba, 0x798: 0xba, 0x799: 0xba, 0x79a: 0xba, 0x79b: 0xba, 0x79c: 0xba, 0x79d: 0xba, 0x79e: 0xba, 0x79f: 0xba, 0x7a0: 0x76, 0x7a1: 0x77, 0x7a2: 0x78, 0x7a3: 0x18b, 0x7a4: 0x79, 0x7a5: 0x7a, 0x7a6: 0x18c, 0x7a7: 0x7b, 0x7a8: 0x7c, 0x7a9: 0xba, 0x7aa: 0xba, 0x7ab: 0xba, 0x7ac: 0xba, 0x7ad: 0xba, 0x7ae: 0xba, 0x7af: 0xba, 0x7b0: 0xba, 0x7b1: 0xba, 0x7b2: 0xba, 0x7b3: 0xba, 0x7b4: 0xba, 0x7b5: 0xba, 0x7b6: 0xba, 0x7b7: 0xba, 0x7b8: 0xba, 0x7b9: 0xba, 0x7ba: 0xba, 0x7bb: 0xba, 0x7bc: 0xba, 0x7bd: 0xba, 0x7be: 0xba, 0x7bf: 0xba, // Block 0x1f, offset 0x7c0 0x7d0: 0x0d, 0x7d1: 0x0e, 0x7d2: 0x0f, 0x7d3: 0x10, 0x7d4: 0x11, 0x7d5: 0x0b, 0x7d6: 0x12, 0x7d7: 0x07, 0x7d8: 0x13, 0x7d9: 0x0b, 0x7da: 0x0b, 0x7db: 0x14, 0x7dc: 0x0b, 0x7dd: 0x15, 0x7de: 0x16, 0x7df: 0x17, 0x7e0: 0x07, 0x7e1: 0x07, 0x7e2: 0x07, 0x7e3: 0x07, 0x7e4: 0x07, 0x7e5: 0x07, 0x7e6: 0x07, 0x7e7: 0x07, 0x7e8: 0x07, 0x7e9: 0x07, 0x7ea: 0x18, 0x7eb: 0x19, 0x7ec: 0x1a, 0x7ed: 0x07, 0x7ee: 0x1b, 0x7ef: 0x1c, 0x7f0: 0x0b, 0x7f1: 0x0b, 0x7f2: 0x0b, 0x7f3: 0x0b, 0x7f4: 0x0b, 0x7f5: 0x0b, 0x7f6: 0x0b, 0x7f7: 0x0b, 0x7f8: 0x0b, 0x7f9: 0x0b, 0x7fa: 0x0b, 0x7fb: 0x0b, 0x7fc: 0x0b, 0x7fd: 0x0b, 0x7fe: 0x0b, 0x7ff: 0x0b, // Block 0x20, offset 0x800 0x800: 0x0b, 0x801: 0x0b, 0x802: 0x0b, 0x803: 0x0b, 0x804: 0x0b, 0x805: 0x0b, 0x806: 0x0b, 0x807: 0x0b, 0x808: 0x0b, 0x809: 0x0b, 0x80a: 0x0b, 0x80b: 0x0b, 0x80c: 0x0b, 0x80d: 0x0b, 0x80e: 0x0b, 0x80f: 0x0b, 0x810: 0x0b, 0x811: 0x0b, 0x812: 0x0b, 0x813: 0x0b, 0x814: 0x0b, 0x815: 0x0b, 0x816: 0x0b, 0x817: 0x0b, 0x818: 0x0b, 0x819: 0x0b, 0x81a: 0x0b, 0x81b: 0x0b, 0x81c: 0x0b, 0x81d: 0x0b, 0x81e: 0x0b, 0x81f: 0x0b, 0x820: 0x0b, 0x821: 0x0b, 0x822: 0x0b, 0x823: 0x0b, 0x824: 0x0b, 0x825: 0x0b, 0x826: 0x0b, 0x827: 0x0b, 0x828: 0x0b, 0x829: 0x0b, 0x82a: 0x0b, 0x82b: 0x0b, 0x82c: 0x0b, 0x82d: 0x0b, 0x82e: 0x0b, 0x82f: 0x0b, 0x830: 0x0b, 0x831: 0x0b, 0x832: 0x0b, 0x833: 0x0b, 0x834: 0x0b, 0x835: 0x0b, 0x836: 0x0b, 0x837: 0x0b, 0x838: 0x0b, 0x839: 0x0b, 0x83a: 0x0b, 0x83b: 0x0b, 0x83c: 0x0b, 0x83d: 0x0b, 0x83e: 0x0b, 0x83f: 0x0b, // Block 0x21, offset 0x840 0x840: 0x18d, 0x841: 0x18e, 0x842: 0xba, 0x843: 0xba, 0x844: 0x18f, 0x845: 0x18f, 0x846: 0x18f, 0x847: 0x190, 0x848: 0xba, 0x849: 0xba, 0x84a: 0xba, 0x84b: 0xba, 0x84c: 0xba, 0x84d: 0xba, 0x84e: 0xba, 0x84f: 0xba, 0x850: 0xba, 0x851: 0xba, 0x852: 0xba, 0x853: 0xba, 0x854: 0xba, 0x855: 0xba, 0x856: 0xba, 0x857: 0xba, 0x858: 0xba, 0x859: 0xba, 0x85a: 0xba, 0x85b: 0xba, 0x85c: 0xba, 0x85d: 0xba, 0x85e: 0xba, 0x85f: 0xba, 0x860: 0xba, 0x861: 0xba, 0x862: 0xba, 0x863: 0xba, 0x864: 0xba, 0x865: 0xba, 0x866: 0xba, 0x867: 0xba, 0x868: 0xba, 0x869: 0xba, 0x86a: 0xba, 0x86b: 0xba, 0x86c: 0xba, 0x86d: 0xba, 0x86e: 0xba, 0x86f: 0xba, 0x870: 0xba, 0x871: 0xba, 0x872: 0xba, 0x873: 0xba, 0x874: 0xba, 0x875: 0xba, 0x876: 0xba, 0x877: 0xba, 0x878: 0xba, 0x879: 0xba, 0x87a: 0xba, 0x87b: 0xba, 0x87c: 0xba, 0x87d: 0xba, 0x87e: 0xba, 0x87f: 0xba, // Block 0x22, offset 0x880 0x880: 0x0b, 0x881: 0x0b, 0x882: 0x0b, 0x883: 0x0b, 0x884: 0x0b, 0x885: 0x0b, 0x886: 0x0b, 0x887: 0x0b, 0x888: 0x0b, 0x889: 0x0b, 0x88a: 0x0b, 0x88b: 0x0b, 0x88c: 0x0b, 0x88d: 0x0b, 0x88e: 0x0b, 0x88f: 0x0b, 0x890: 0x0b, 0x891: 0x0b, 0x892: 0x0b, 0x893: 0x0b, 0x894: 0x0b, 0x895: 0x0b, 0x896: 0x0b, 0x897: 0x0b, 0x898: 0x0b, 0x899: 0x0b, 0x89a: 0x0b, 0x89b: 0x0b, 0x89c: 0x0b, 0x89d: 0x0b, 0x89e: 0x0b, 0x89f: 0x0b, 0x8a0: 0x1f, 0x8a1: 0x0b, 0x8a2: 0x0b, 0x8a3: 0x0b, 0x8a4: 0x0b, 0x8a5: 0x0b, 0x8a6: 0x0b, 0x8a7: 0x0b, 0x8a8: 0x0b, 0x8a9: 0x0b, 0x8aa: 0x0b, 0x8ab: 0x0b, 0x8ac: 0x0b, 0x8ad: 0x0b, 0x8ae: 0x0b, 0x8af: 0x0b, 0x8b0: 0x0b, 0x8b1: 0x0b, 0x8b2: 0x0b, 0x8b3: 0x0b, 0x8b4: 0x0b, 0x8b5: 0x0b, 0x8b6: 0x0b, 0x8b7: 0x0b, 0x8b8: 0x0b, 0x8b9: 0x0b, 0x8ba: 0x0b, 0x8bb: 0x0b, 0x8bc: 0x0b, 0x8bd: 0x0b, 0x8be: 0x0b, 0x8bf: 0x0b, // Block 0x23, offset 0x8c0 0x8c0: 0x0b, 0x8c1: 0x0b, 0x8c2: 0x0b, 0x8c3: 0x0b, 0x8c4: 0x0b, 0x8c5: 0x0b, 0x8c6: 0x0b, 0x8c7: 0x0b, 0x8c8: 0x0b, 0x8c9: 0x0b, 0x8ca: 0x0b, 0x8cb: 0x0b, 0x8cc: 0x0b, 0x8cd: 0x0b, 0x8ce: 0x0b, 0x8cf: 0x0b, } // idnaSparseOffset: 276 entries, 552 bytes var idnaSparseOffset = []uint16{0x0, 0x8, 0x19, 0x25, 0x27, 0x2c, 0x33, 0x3e, 0x4a, 0x4e, 0x5d, 0x62, 0x6c, 0x78, 0x86, 0x8b, 0x94, 0xa4, 0xb2, 0xbe, 0xca, 0xdb, 0xe5, 0xec, 0xf9, 0x10a, 0x111, 0x11c, 0x12b, 0x139, 0x143, 0x145, 0x14a, 0x14d, 0x150, 0x152, 0x15e, 0x169, 0x171, 0x177, 0x17d, 0x182, 0x187, 0x18a, 0x18e, 0x194, 0x199, 0x1a5, 0x1af, 0x1b5, 0x1c6, 0x1d0, 0x1d3, 0x1db, 0x1de, 0x1eb, 0x1f3, 0x1f7, 0x1fe, 0x206, 0x216, 0x222, 0x224, 0x22e, 0x23a, 0x246, 0x252, 0x25a, 0x25f, 0x269, 0x27a, 0x27e, 0x289, 0x28d, 0x296, 0x29e, 0x2a4, 0x2a9, 0x2ac, 0x2b0, 0x2b6, 0x2ba, 0x2be, 0x2c2, 0x2c7, 0x2cd, 0x2d5, 0x2dc, 0x2e7, 0x2f1, 0x2f5, 0x2f8, 0x2fe, 0x302, 0x304, 0x307, 0x309, 0x30c, 0x316, 0x319, 0x328, 0x32c, 0x331, 0x334, 0x338, 0x33d, 0x342, 0x348, 0x34e, 0x35d, 0x363, 0x367, 0x376, 0x37b, 0x383, 0x38d, 0x398, 0x3a0, 0x3b1, 0x3ba, 0x3ca, 0x3d7, 0x3e1, 0x3e6, 0x3f3, 0x3f7, 0x3fc, 0x3fe, 0x402, 0x404, 0x408, 0x411, 0x417, 0x41b, 0x42b, 0x435, 0x43a, 0x43d, 0x443, 0x44a, 0x44f, 0x453, 0x459, 0x45e, 0x467, 0x46c, 0x472, 0x479, 0x480, 0x487, 0x48b, 0x490, 0x493, 0x498, 0x4a4, 0x4aa, 0x4af, 0x4b6, 0x4be, 0x4c3, 0x4c7, 0x4d7, 0x4de, 0x4e2, 0x4e6, 0x4ed, 0x4ef, 0x4f2, 0x4f5, 0x4f9, 0x502, 0x506, 0x50e, 0x516, 0x51c, 0x525, 0x531, 0x538, 0x541, 0x54b, 0x552, 0x560, 0x56d, 0x57a, 0x583, 0x587, 0x596, 0x59e, 0x5a9, 0x5b2, 0x5b8, 0x5c0, 0x5c9, 0x5d3, 0x5d6, 0x5e2, 0x5eb, 0x5ee, 0x5f3, 0x5fe, 0x607, 0x613, 0x616, 0x620, 0x629, 0x635, 0x642, 0x64f, 0x65d, 0x664, 0x667, 0x66c, 0x66f, 0x672, 0x675, 0x67c, 0x683, 0x687, 0x692, 0x695, 0x698, 0x69b, 0x6a1, 0x6a6, 0x6aa, 0x6ad, 0x6b0, 0x6b3, 0x6b6, 0x6b9, 0x6be, 0x6c8, 0x6cb, 0x6cf, 0x6de, 0x6ea, 0x6ee, 0x6f3, 0x6f7, 0x6fc, 0x700, 0x705, 0x70e, 0x719, 0x71f, 0x727, 0x72a, 0x72d, 0x731, 0x735, 0x73b, 0x741, 0x746, 0x749, 0x759, 0x760, 0x763, 0x766, 0x76a, 0x770, 0x775, 0x77a, 0x782, 0x787, 0x78b, 0x78f, 0x792, 0x795, 0x799, 0x79d, 0x7a0, 0x7b0, 0x7c1, 0x7c6, 0x7c8, 0x7ca} // idnaSparseValues: 1997 entries, 7988 bytes var idnaSparseValues = [1997]valueRange{ // Block 0x0, offset 0x0 {value: 0x0000, lo: 0x07}, {value: 0xe105, lo: 0x80, hi: 0x96}, {value: 0x0018, lo: 0x97, hi: 0x97}, {value: 0xe105, lo: 0x98, hi: 0x9e}, {value: 0x001f, lo: 0x9f, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xb6}, {value: 0x0018, lo: 0xb7, hi: 0xb7}, {value: 0x0008, lo: 0xb8, hi: 0xbf}, // Block 0x1, offset 0x8 {value: 0x0000, lo: 0x10}, {value: 0x0008, lo: 0x80, hi: 0x80}, {value: 0xe01d, lo: 0x81, hi: 0x81}, {value: 0x0008, lo: 0x82, hi: 0x82}, {value: 0x0335, lo: 0x83, hi: 0x83}, {value: 0x034d, lo: 0x84, hi: 0x84}, {value: 0x0365, lo: 0x85, hi: 0x85}, {value: 0xe00d, lo: 0x86, hi: 0x86}, {value: 0x0008, lo: 0x87, hi: 0x87}, {value: 0xe00d, lo: 0x88, hi: 0x88}, {value: 0x0008, lo: 0x89, hi: 0x89}, {value: 0xe00d, lo: 0x8a, hi: 0x8a}, {value: 0x0008, lo: 0x8b, hi: 0x8b}, {value: 0xe00d, lo: 0x8c, hi: 0x8c}, {value: 0x0008, lo: 0x8d, hi: 0x8d}, {value: 0xe00d, lo: 0x8e, hi: 0x8e}, {value: 0x0008, lo: 0x8f, hi: 0xbf}, // Block 0x2, offset 0x19 {value: 0x0000, lo: 0x0b}, {value: 0x0008, lo: 0x80, hi: 0xaf}, {value: 0x0249, lo: 0xb0, hi: 0xb0}, {value: 0x037d, lo: 0xb1, hi: 0xb1}, {value: 0x0259, lo: 0xb2, hi: 0xb2}, {value: 0x0269, lo: 0xb3, hi: 0xb3}, {value: 0x034d, lo: 0xb4, hi: 0xb4}, {value: 0x0395, lo: 0xb5, hi: 0xb5}, {value: 0xe1bd, lo: 0xb6, hi: 0xb6}, {value: 0x0279, lo: 0xb7, hi: 0xb7}, {value: 0x0289, lo: 0xb8, hi: 0xb8}, {value: 0x0008, lo: 0xb9, hi: 0xbf}, // Block 0x3, offset 0x25 {value: 0x0000, lo: 0x01}, {value: 0x3308, lo: 0x80, hi: 0xbf}, // Block 0x4, offset 0x27 {value: 0x0000, lo: 0x04}, {value: 0x03f5, lo: 0x80, hi: 0x8f}, {value: 0xe105, lo: 0x90, hi: 0x9f}, {value: 0x049d, lo: 0xa0, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xbf}, // Block 0x5, offset 0x2c {value: 0x0000, lo: 0x06}, {value: 0xe185, lo: 0x80, hi: 0x8f}, {value: 0x0545, lo: 0x90, hi: 0x96}, {value: 0x0040, lo: 0x97, hi: 0x98}, {value: 0x0008, lo: 0x99, hi: 0x99}, {value: 0x0018, lo: 0x9a, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xbf}, // Block 0x6, offset 0x33 {value: 0x0000, lo: 0x0a}, {value: 0x0008, lo: 0x80, hi: 0x86}, {value: 0x0401, lo: 0x87, hi: 0x87}, {value: 0x0008, lo: 0x88, hi: 0x88}, {value: 0x0018, lo: 0x89, hi: 0x8a}, {value: 0x0040, lo: 0x8b, hi: 0x8c}, {value: 0x0018, lo: 0x8d, hi: 0x8f}, {value: 0x0040, lo: 0x90, hi: 0x90}, {value: 0x3308, lo: 0x91, hi: 0xbd}, {value: 0x0818, lo: 0xbe, hi: 0xbe}, {value: 0x3308, lo: 0xbf, hi: 0xbf}, // Block 0x7, offset 0x3e {value: 0x0000, lo: 0x0b}, {value: 0x0818, lo: 0x80, hi: 0x80}, {value: 0x3308, lo: 0x81, hi: 0x82}, {value: 0x0818, lo: 0x83, hi: 0x83}, {value: 0x3308, lo: 0x84, hi: 0x85}, {value: 0x0818, lo: 0x86, hi: 0x86}, {value: 0x3308, lo: 0x87, hi: 0x87}, {value: 0x0040, lo: 0x88, hi: 0x8f}, {value: 0x0808, lo: 0x90, hi: 0xaa}, {value: 0x0040, lo: 0xab, hi: 0xae}, {value: 0x0808, lo: 0xaf, hi: 0xb4}, {value: 0x0040, lo: 0xb5, hi: 0xbf}, // Block 0x8, offset 0x4a {value: 0x0000, lo: 0x03}, {value: 0x0a08, lo: 0x80, hi: 0x87}, {value: 0x0c08, lo: 0x88, hi: 0x99}, {value: 0x0a08, lo: 0x9a, hi: 0xbf}, // Block 0x9, offset 0x4e {value: 0x0000, lo: 0x0e}, {value: 0x3308, lo: 0x80, hi: 0x8a}, {value: 0x0040, lo: 0x8b, hi: 0x8c}, {value: 0x0c08, lo: 0x8d, hi: 0x8d}, {value: 0x0a08, lo: 0x8e, hi: 0x98}, {value: 0x0c08, lo: 0x99, hi: 0x9b}, {value: 0x0a08, lo: 0x9c, hi: 0xaa}, {value: 0x0c08, lo: 0xab, hi: 0xac}, {value: 0x0a08, lo: 0xad, hi: 0xb0}, {value: 0x0c08, lo: 0xb1, hi: 0xb1}, {value: 0x0a08, lo: 0xb2, hi: 0xb2}, {value: 0x0c08, lo: 0xb3, hi: 0xb4}, {value: 0x0a08, lo: 0xb5, hi: 0xb7}, {value: 0x0c08, lo: 0xb8, hi: 0xb9}, {value: 0x0a08, lo: 0xba, hi: 0xbf}, // Block 0xa, offset 0x5d {value: 0x0000, lo: 0x04}, {value: 0x0808, lo: 0x80, hi: 0xa5}, {value: 0x3308, lo: 0xa6, hi: 0xb0}, {value: 0x0808, lo: 0xb1, hi: 0xb1}, {value: 0x0040, lo: 0xb2, hi: 0xbf}, // Block 0xb, offset 0x62 {value: 0x0000, lo: 0x09}, {value: 0x0808, lo: 0x80, hi: 0x89}, {value: 0x0a08, lo: 0x8a, hi: 0xaa}, {value: 0x3308, lo: 0xab, hi: 0xb3}, {value: 0x0808, lo: 0xb4, hi: 0xb5}, {value: 0x0018, lo: 0xb6, hi: 0xb9}, {value: 0x0818, lo: 0xba, hi: 0xba}, {value: 0x0040, lo: 0xbb, hi: 0xbc}, {value: 0x3308, lo: 0xbd, hi: 0xbd}, {value: 0x0818, lo: 0xbe, hi: 0xbf}, // Block 0xc, offset 0x6c {value: 0x0000, lo: 0x0b}, {value: 0x0808, lo: 0x80, hi: 0x95}, {value: 0x3308, lo: 0x96, hi: 0x99}, {value: 0x0808, lo: 0x9a, hi: 0x9a}, {value: 0x3308, lo: 0x9b, hi: 0xa3}, {value: 0x0808, lo: 0xa4, hi: 0xa4}, {value: 0x3308, lo: 0xa5, hi: 0xa7}, {value: 0x0808, lo: 0xa8, hi: 0xa8}, {value: 0x3308, lo: 0xa9, hi: 0xad}, {value: 0x0040, lo: 0xae, hi: 0xaf}, {value: 0x0818, lo: 0xb0, hi: 0xbe}, {value: 0x0040, lo: 0xbf, hi: 0xbf}, // Block 0xd, offset 0x78 {value: 0x0000, lo: 0x0d}, {value: 0x0040, lo: 0x80, hi: 0x9f}, {value: 0x0a08, lo: 0xa0, hi: 0xa9}, {value: 0x0c08, lo: 0xaa, hi: 0xac}, {value: 0x0808, lo: 0xad, hi: 0xad}, {value: 0x0c08, lo: 0xae, hi: 0xae}, {value: 0x0a08, lo: 0xaf, hi: 0xb0}, {value: 0x0c08, lo: 0xb1, hi: 0xb2}, {value: 0x0a08, lo: 0xb3, hi: 0xb4}, {value: 0x0040, lo: 0xb5, hi: 0xb5}, {value: 0x0a08, lo: 0xb6, hi: 0xb8}, {value: 0x0c08, lo: 0xb9, hi: 0xb9}, {value: 0x0a08, lo: 0xba, hi: 0xbd}, {value: 0x0040, lo: 0xbe, hi: 0xbf}, // Block 0xe, offset 0x86 {value: 0x0000, lo: 0x04}, {value: 0x0040, lo: 0x80, hi: 0x92}, {value: 0x3308, lo: 0x93, hi: 0xa1}, {value: 0x0840, lo: 0xa2, hi: 0xa2}, {value: 0x3308, lo: 0xa3, hi: 0xbf}, // Block 0xf, offset 0x8b {value: 0x0000, lo: 0x08}, {value: 0x3308, lo: 0x80, hi: 0x82}, {value: 0x3008, lo: 0x83, hi: 0x83}, {value: 0x0008, lo: 0x84, hi: 0xb9}, {value: 0x3308, lo: 0xba, hi: 0xba}, {value: 0x3008, lo: 0xbb, hi: 0xbb}, {value: 0x3308, lo: 0xbc, hi: 0xbc}, {value: 0x0008, lo: 0xbd, hi: 0xbd}, {value: 0x3008, lo: 0xbe, hi: 0xbf}, // Block 0x10, offset 0x94 {value: 0x0000, lo: 0x0f}, {value: 0x3308, lo: 0x80, hi: 0x80}, {value: 0x3008, lo: 0x81, hi: 0x82}, {value: 0x0040, lo: 0x83, hi: 0x85}, {value: 0x3008, lo: 0x86, hi: 0x88}, {value: 0x0040, lo: 0x89, hi: 0x89}, {value: 0x3008, lo: 0x8a, hi: 0x8c}, {value: 0x3b08, lo: 0x8d, hi: 0x8d}, {value: 0x0040, lo: 0x8e, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x90}, {value: 0x0040, lo: 0x91, hi: 0x96}, {value: 0x3008, lo: 0x97, hi: 0x97}, {value: 0x0040, lo: 0x98, hi: 0xa5}, {value: 0x0008, lo: 0xa6, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xba}, {value: 0x0040, lo: 0xbb, hi: 0xbf}, // Block 0x11, offset 0xa4 {value: 0x0000, lo: 0x0d}, {value: 0x3308, lo: 0x80, hi: 0x80}, {value: 0x3008, lo: 0x81, hi: 0x83}, {value: 0x3308, lo: 0x84, hi: 0x84}, {value: 0x0008, lo: 0x85, hi: 0x8c}, {value: 0x0040, lo: 0x8d, hi: 0x8d}, {value: 0x0008, lo: 0x8e, hi: 0x90}, {value: 0x0040, lo: 0x91, hi: 0x91}, {value: 0x0008, lo: 0x92, hi: 0xa8}, {value: 0x0040, lo: 0xa9, hi: 0xa9}, {value: 0x0008, lo: 0xaa, hi: 0xb9}, {value: 0x0040, lo: 0xba, hi: 0xbc}, {value: 0x0008, lo: 0xbd, hi: 0xbd}, {value: 0x3308, lo: 0xbe, hi: 0xbf}, // Block 0x12, offset 0xb2 {value: 0x0000, lo: 0x0b}, {value: 0x3308, lo: 0x80, hi: 0x81}, {value: 0x3008, lo: 0x82, hi: 0x83}, {value: 0x0040, lo: 0x84, hi: 0x84}, {value: 0x0008, lo: 0x85, hi: 0x8c}, {value: 0x0040, lo: 0x8d, hi: 0x8d}, {value: 0x0008, lo: 0x8e, hi: 0x90}, {value: 0x0040, lo: 0x91, hi: 0x91}, {value: 0x0008, lo: 0x92, hi: 0xba}, {value: 0x3b08, lo: 0xbb, hi: 0xbc}, {value: 0x0008, lo: 0xbd, hi: 0xbd}, {value: 0x3008, lo: 0xbe, hi: 0xbf}, // Block 0x13, offset 0xbe {value: 0x0000, lo: 0x0b}, {value: 0x0040, lo: 0x80, hi: 0x81}, {value: 0x3008, lo: 0x82, hi: 0x83}, {value: 0x0040, lo: 0x84, hi: 0x84}, {value: 0x0008, lo: 0x85, hi: 0x96}, {value: 0x0040, lo: 0x97, hi: 0x99}, {value: 0x0008, lo: 0x9a, hi: 0xb1}, {value: 0x0040, lo: 0xb2, hi: 0xb2}, {value: 0x0008, lo: 0xb3, hi: 0xbb}, {value: 0x0040, lo: 0xbc, hi: 0xbc}, {value: 0x0008, lo: 0xbd, hi: 0xbd}, {value: 0x0040, lo: 0xbe, hi: 0xbf}, // Block 0x14, offset 0xca {value: 0x0000, lo: 0x10}, {value: 0x0008, lo: 0x80, hi: 0x86}, {value: 0x0040, lo: 0x87, hi: 0x89}, {value: 0x3b08, lo: 0x8a, hi: 0x8a}, {value: 0x0040, lo: 0x8b, hi: 0x8e}, {value: 0x3008, lo: 0x8f, hi: 0x91}, {value: 0x3308, lo: 0x92, hi: 0x94}, {value: 0x0040, lo: 0x95, hi: 0x95}, {value: 0x3308, lo: 0x96, hi: 0x96}, {value: 0x0040, lo: 0x97, hi: 0x97}, {value: 0x3008, lo: 0x98, hi: 0x9f}, {value: 0x0040, lo: 0xa0, hi: 0xa5}, {value: 0x0008, lo: 0xa6, hi: 0xaf}, {value: 0x0040, lo: 0xb0, hi: 0xb1}, {value: 0x3008, lo: 0xb2, hi: 0xb3}, {value: 0x0018, lo: 0xb4, hi: 0xb4}, {value: 0x0040, lo: 0xb5, hi: 0xbf}, // Block 0x15, offset 0xdb {value: 0x0000, lo: 0x09}, {value: 0x0040, lo: 0x80, hi: 0x80}, {value: 0x0008, lo: 0x81, hi: 0xb0}, {value: 0x3308, lo: 0xb1, hi: 0xb1}, {value: 0x0008, lo: 0xb2, hi: 0xb2}, {value: 0x08f1, lo: 0xb3, hi: 0xb3}, {value: 0x3308, lo: 0xb4, hi: 0xb9}, {value: 0x3b08, lo: 0xba, hi: 0xba}, {value: 0x0040, lo: 0xbb, hi: 0xbe}, {value: 0x0018, lo: 0xbf, hi: 0xbf}, // Block 0x16, offset 0xe5 {value: 0x0000, lo: 0x06}, {value: 0x0008, lo: 0x80, hi: 0x86}, {value: 0x3308, lo: 0x87, hi: 0x8e}, {value: 0x0018, lo: 0x8f, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0018, lo: 0x9a, hi: 0x9b}, {value: 0x0040, lo: 0x9c, hi: 0xbf}, // Block 0x17, offset 0xec {value: 0x0000, lo: 0x0c}, {value: 0x0008, lo: 0x80, hi: 0x84}, {value: 0x0040, lo: 0x85, hi: 0x85}, {value: 0x0008, lo: 0x86, hi: 0x86}, {value: 0x0040, lo: 0x87, hi: 0x87}, {value: 0x3308, lo: 0x88, hi: 0x8d}, {value: 0x0040, lo: 0x8e, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9b}, {value: 0x0961, lo: 0x9c, hi: 0x9c}, {value: 0x0999, lo: 0x9d, hi: 0x9d}, {value: 0x0008, lo: 0x9e, hi: 0x9f}, {value: 0x0040, lo: 0xa0, hi: 0xbf}, // Block 0x18, offset 0xf9 {value: 0x0000, lo: 0x10}, {value: 0x0008, lo: 0x80, hi: 0x80}, {value: 0x0018, lo: 0x81, hi: 0x8a}, {value: 0x0008, lo: 0x8b, hi: 0x8b}, {value: 0xe03d, lo: 0x8c, hi: 0x8c}, {value: 0x0018, lo: 0x8d, hi: 0x97}, {value: 0x3308, lo: 0x98, hi: 0x99}, {value: 0x0018, lo: 0x9a, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa9}, {value: 0x0018, lo: 0xaa, hi: 0xb4}, {value: 0x3308, lo: 0xb5, hi: 0xb5}, {value: 0x0018, lo: 0xb6, hi: 0xb6}, {value: 0x3308, lo: 0xb7, hi: 0xb7}, {value: 0x0018, lo: 0xb8, hi: 0xb8}, {value: 0x3308, lo: 0xb9, hi: 0xb9}, {value: 0x0018, lo: 0xba, hi: 0xbd}, {value: 0x3008, lo: 0xbe, hi: 0xbf}, // Block 0x19, offset 0x10a {value: 0x0000, lo: 0x06}, {value: 0x0018, lo: 0x80, hi: 0x85}, {value: 0x3308, lo: 0x86, hi: 0x86}, {value: 0x0018, lo: 0x87, hi: 0x8c}, {value: 0x0040, lo: 0x8d, hi: 0x8d}, {value: 0x0018, lo: 0x8e, hi: 0x9a}, {value: 0x0040, lo: 0x9b, hi: 0xbf}, // Block 0x1a, offset 0x111 {value: 0x0000, lo: 0x0a}, {value: 0x0008, lo: 0x80, hi: 0xaa}, {value: 0x3008, lo: 0xab, hi: 0xac}, {value: 0x3308, lo: 0xad, hi: 0xb0}, {value: 0x3008, lo: 0xb1, hi: 0xb1}, {value: 0x3308, lo: 0xb2, hi: 0xb7}, {value: 0x3008, lo: 0xb8, hi: 0xb8}, {value: 0x3b08, lo: 0xb9, hi: 0xba}, {value: 0x3008, lo: 0xbb, hi: 0xbc}, {value: 0x3308, lo: 0xbd, hi: 0xbe}, {value: 0x0008, lo: 0xbf, hi: 0xbf}, // Block 0x1b, offset 0x11c {value: 0x0000, lo: 0x0e}, {value: 0x0008, lo: 0x80, hi: 0x89}, {value: 0x0018, lo: 0x8a, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x95}, {value: 0x3008, lo: 0x96, hi: 0x97}, {value: 0x3308, lo: 0x98, hi: 0x99}, {value: 0x0008, lo: 0x9a, hi: 0x9d}, {value: 0x3308, lo: 0x9e, hi: 0xa0}, {value: 0x0008, lo: 0xa1, hi: 0xa1}, {value: 0x3008, lo: 0xa2, hi: 0xa4}, {value: 0x0008, lo: 0xa5, hi: 0xa6}, {value: 0x3008, lo: 0xa7, hi: 0xad}, {value: 0x0008, lo: 0xae, hi: 0xb0}, {value: 0x3308, lo: 0xb1, hi: 0xb4}, {value: 0x0008, lo: 0xb5, hi: 0xbf}, // Block 0x1c, offset 0x12b {value: 0x0000, lo: 0x0d}, {value: 0x0008, lo: 0x80, hi: 0x81}, {value: 0x3308, lo: 0x82, hi: 0x82}, {value: 0x3008, lo: 0x83, hi: 0x84}, {value: 0x3308, lo: 0x85, hi: 0x86}, {value: 0x3008, lo: 0x87, hi: 0x8c}, {value: 0x3308, lo: 0x8d, hi: 0x8d}, {value: 0x0008, lo: 0x8e, hi: 0x8e}, {value: 0x3008, lo: 0x8f, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x3008, lo: 0x9a, hi: 0x9c}, {value: 0x3308, lo: 0x9d, hi: 0x9d}, {value: 0x0018, lo: 0x9e, hi: 0x9f}, {value: 0x0040, lo: 0xa0, hi: 0xbf}, // Block 0x1d, offset 0x139 {value: 0x0000, lo: 0x09}, {value: 0x0040, lo: 0x80, hi: 0x86}, {value: 0x055d, lo: 0x87, hi: 0x87}, {value: 0x0040, lo: 0x88, hi: 0x8c}, {value: 0x055d, lo: 0x8d, hi: 0x8d}, {value: 0x0040, lo: 0x8e, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0xba}, {value: 0x0018, lo: 0xbb, hi: 0xbb}, {value: 0xe105, lo: 0xbc, hi: 0xbc}, {value: 0x0008, lo: 0xbd, hi: 0xbf}, // Block 0x1e, offset 0x143 {value: 0x0000, lo: 0x01}, {value: 0x0018, lo: 0x80, hi: 0xbf}, // Block 0x1f, offset 0x145 {value: 0x0000, lo: 0x04}, {value: 0x0018, lo: 0x80, hi: 0x9e}, {value: 0x0040, lo: 0x9f, hi: 0xa0}, {value: 0x2018, lo: 0xa1, hi: 0xb5}, {value: 0x0018, lo: 0xb6, hi: 0xbf}, // Block 0x20, offset 0x14a {value: 0x0000, lo: 0x02}, {value: 0x0018, lo: 0x80, hi: 0xa7}, {value: 0x2018, lo: 0xa8, hi: 0xbf}, // Block 0x21, offset 0x14d {value: 0x0000, lo: 0x02}, {value: 0x2018, lo: 0x80, hi: 0x82}, {value: 0x0018, lo: 0x83, hi: 0xbf}, // Block 0x22, offset 0x150 {value: 0x0000, lo: 0x01}, {value: 0x0008, lo: 0x80, hi: 0xbf}, // Block 0x23, offset 0x152 {value: 0x0000, lo: 0x0b}, {value: 0x0008, lo: 0x80, hi: 0x88}, {value: 0x0040, lo: 0x89, hi: 0x89}, {value: 0x0008, lo: 0x8a, hi: 0x8d}, {value: 0x0040, lo: 0x8e, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x96}, {value: 0x0040, lo: 0x97, hi: 0x97}, {value: 0x0008, lo: 0x98, hi: 0x98}, {value: 0x0040, lo: 0x99, hi: 0x99}, {value: 0x0008, lo: 0x9a, hi: 0x9d}, {value: 0x0040, lo: 0x9e, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xbf}, // Block 0x24, offset 0x15e {value: 0x0000, lo: 0x0a}, {value: 0x0008, lo: 0x80, hi: 0x88}, {value: 0x0040, lo: 0x89, hi: 0x89}, {value: 0x0008, lo: 0x8a, hi: 0x8d}, {value: 0x0040, lo: 0x8e, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0xb0}, {value: 0x0040, lo: 0xb1, hi: 0xb1}, {value: 0x0008, lo: 0xb2, hi: 0xb5}, {value: 0x0040, lo: 0xb6, hi: 0xb7}, {value: 0x0008, lo: 0xb8, hi: 0xbe}, {value: 0x0040, lo: 0xbf, hi: 0xbf}, // Block 0x25, offset 0x169 {value: 0x0000, lo: 0x07}, {value: 0x0008, lo: 0x80, hi: 0x80}, {value: 0x0040, lo: 0x81, hi: 0x81}, {value: 0x0008, lo: 0x82, hi: 0x85}, {value: 0x0040, lo: 0x86, hi: 0x87}, {value: 0x0008, lo: 0x88, hi: 0x96}, {value: 0x0040, lo: 0x97, hi: 0x97}, {value: 0x0008, lo: 0x98, hi: 0xbf}, // Block 0x26, offset 0x171 {value: 0x0000, lo: 0x05}, {value: 0x0008, lo: 0x80, hi: 0x90}, {value: 0x0040, lo: 0x91, hi: 0x91}, {value: 0x0008, lo: 0x92, hi: 0x95}, {value: 0x0040, lo: 0x96, hi: 0x97}, {value: 0x0008, lo: 0x98, hi: 0xbf}, // Block 0x27, offset 0x177 {value: 0x0000, lo: 0x05}, {value: 0x0008, lo: 0x80, hi: 0x9a}, {value: 0x0040, lo: 0x9b, hi: 0x9c}, {value: 0x3308, lo: 0x9d, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xbc}, {value: 0x0040, lo: 0xbd, hi: 0xbf}, // Block 0x28, offset 0x17d {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0x8f}, {value: 0x0018, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xbf}, // Block 0x29, offset 0x182 {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0xb5}, {value: 0x0040, lo: 0xb6, hi: 0xb7}, {value: 0xe045, lo: 0xb8, hi: 0xbd}, {value: 0x0040, lo: 0xbe, hi: 0xbf}, // Block 0x2a, offset 0x187 {value: 0x0000, lo: 0x02}, {value: 0x0018, lo: 0x80, hi: 0x80}, {value: 0x0008, lo: 0x81, hi: 0xbf}, // Block 0x2b, offset 0x18a {value: 0x0000, lo: 0x03}, {value: 0x0008, lo: 0x80, hi: 0xac}, {value: 0x0018, lo: 0xad, hi: 0xae}, {value: 0x0008, lo: 0xaf, hi: 0xbf}, // Block 0x2c, offset 0x18e {value: 0x0000, lo: 0x05}, {value: 0x0040, lo: 0x80, hi: 0x80}, {value: 0x0008, lo: 0x81, hi: 0x9a}, {value: 0x0018, lo: 0x9b, hi: 0x9c}, {value: 0x0040, lo: 0x9d, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xbf}, // Block 0x2d, offset 0x194 {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0xaa}, {value: 0x0018, lo: 0xab, hi: 0xb0}, {value: 0x0008, lo: 0xb1, hi: 0xb8}, {value: 0x0040, lo: 0xb9, hi: 0xbf}, // Block 0x2e, offset 0x199 {value: 0x0000, lo: 0x0b}, {value: 0x0008, lo: 0x80, hi: 0x8c}, {value: 0x0040, lo: 0x8d, hi: 0x8d}, {value: 0x0008, lo: 0x8e, hi: 0x91}, {value: 0x3308, lo: 0x92, hi: 0x93}, {value: 0x3b08, lo: 0x94, hi: 0x94}, {value: 0x0040, lo: 0x95, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xb1}, {value: 0x3308, lo: 0xb2, hi: 0xb3}, {value: 0x3b08, lo: 0xb4, hi: 0xb4}, {value: 0x0018, lo: 0xb5, hi: 0xb6}, {value: 0x0040, lo: 0xb7, hi: 0xbf}, // Block 0x2f, offset 0x1a5 {value: 0x0000, lo: 0x09}, {value: 0x0008, lo: 0x80, hi: 0x91}, {value: 0x3308, lo: 0x92, hi: 0x93}, {value: 0x0040, lo: 0x94, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xac}, {value: 0x0040, lo: 0xad, hi: 0xad}, {value: 0x0008, lo: 0xae, hi: 0xb0}, {value: 0x0040, lo: 0xb1, hi: 0xb1}, {value: 0x3308, lo: 0xb2, hi: 0xb3}, {value: 0x0040, lo: 0xb4, hi: 0xbf}, // Block 0x30, offset 0x1af {value: 0x0000, lo: 0x05}, {value: 0x0008, lo: 0x80, hi: 0xb3}, {value: 0x3340, lo: 0xb4, hi: 0xb5}, {value: 0x3008, lo: 0xb6, hi: 0xb6}, {value: 0x3308, lo: 0xb7, hi: 0xbd}, {value: 0x3008, lo: 0xbe, hi: 0xbf}, // Block 0x31, offset 0x1b5 {value: 0x0000, lo: 0x10}, {value: 0x3008, lo: 0x80, hi: 0x85}, {value: 0x3308, lo: 0x86, hi: 0x86}, {value: 0x3008, lo: 0x87, hi: 0x88}, {value: 0x3308, lo: 0x89, hi: 0x91}, {value: 0x3b08, lo: 0x92, hi: 0x92}, {value: 0x3308, lo: 0x93, hi: 0x93}, {value: 0x0018, lo: 0x94, hi: 0x96}, {value: 0x0008, lo: 0x97, hi: 0x97}, {value: 0x0018, lo: 0x98, hi: 0x9b}, {value: 0x0008, lo: 0x9c, hi: 0x9c}, {value: 0x3308, lo: 0x9d, hi: 0x9d}, {value: 0x0040, lo: 0x9e, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa9}, {value: 0x0040, lo: 0xaa, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xb9}, {value: 0x0040, lo: 0xba, hi: 0xbf}, // Block 0x32, offset 0x1c6 {value: 0x0000, lo: 0x09}, {value: 0x0018, lo: 0x80, hi: 0x85}, {value: 0x0040, lo: 0x86, hi: 0x86}, {value: 0x0218, lo: 0x87, hi: 0x87}, {value: 0x0018, lo: 0x88, hi: 0x8a}, {value: 0x33c0, lo: 0x8b, hi: 0x8d}, {value: 0x0040, lo: 0x8e, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9f}, {value: 0x0208, lo: 0xa0, hi: 0xbf}, // Block 0x33, offset 0x1d0 {value: 0x0000, lo: 0x02}, {value: 0x0208, lo: 0x80, hi: 0xb8}, {value: 0x0040, lo: 0xb9, hi: 0xbf}, // Block 0x34, offset 0x1d3 {value: 0x0000, lo: 0x07}, {value: 0x0008, lo: 0x80, hi: 0x84}, {value: 0x3308, lo: 0x85, hi: 0x86}, {value: 0x0208, lo: 0x87, hi: 0xa8}, {value: 0x3308, lo: 0xa9, hi: 0xa9}, {value: 0x0208, lo: 0xaa, hi: 0xaa}, {value: 0x0040, lo: 0xab, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xbf}, // Block 0x35, offset 0x1db {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0xb5}, {value: 0x0040, lo: 0xb6, hi: 0xbf}, // Block 0x36, offset 0x1de {value: 0x0000, lo: 0x0c}, {value: 0x0008, lo: 0x80, hi: 0x9e}, {value: 0x0040, lo: 0x9f, hi: 0x9f}, {value: 0x3308, lo: 0xa0, hi: 0xa2}, {value: 0x3008, lo: 0xa3, hi: 0xa6}, {value: 0x3308, lo: 0xa7, hi: 0xa8}, {value: 0x3008, lo: 0xa9, hi: 0xab}, {value: 0x0040, lo: 0xac, hi: 0xaf}, {value: 0x3008, lo: 0xb0, hi: 0xb1}, {value: 0x3308, lo: 0xb2, hi: 0xb2}, {value: 0x3008, lo: 0xb3, hi: 0xb8}, {value: 0x3308, lo: 0xb9, hi: 0xbb}, {value: 0x0040, lo: 0xbc, hi: 0xbf}, // Block 0x37, offset 0x1eb {value: 0x0000, lo: 0x07}, {value: 0x0018, lo: 0x80, hi: 0x80}, {value: 0x0040, lo: 0x81, hi: 0x83}, {value: 0x0018, lo: 0x84, hi: 0x85}, {value: 0x0008, lo: 0x86, hi: 0xad}, {value: 0x0040, lo: 0xae, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xb4}, {value: 0x0040, lo: 0xb5, hi: 0xbf}, // Block 0x38, offset 0x1f3 {value: 0x0000, lo: 0x03}, {value: 0x0008, lo: 0x80, hi: 0xab}, {value: 0x0040, lo: 0xac, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xbf}, // Block 0x39, offset 0x1f7 {value: 0x0000, lo: 0x06}, {value: 0x0008, lo: 0x80, hi: 0x89}, {value: 0x0040, lo: 0x8a, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0028, lo: 0x9a, hi: 0x9a}, {value: 0x0040, lo: 0x9b, hi: 0x9d}, {value: 0x0018, lo: 0x9e, hi: 0xbf}, // Block 0x3a, offset 0x1fe {value: 0x0000, lo: 0x07}, {value: 0x0008, lo: 0x80, hi: 0x96}, {value: 0x3308, lo: 0x97, hi: 0x98}, {value: 0x3008, lo: 0x99, hi: 0x9a}, {value: 0x3308, lo: 0x9b, hi: 0x9b}, {value: 0x0040, lo: 0x9c, hi: 0x9d}, {value: 0x0018, lo: 0x9e, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xbf}, // Block 0x3b, offset 0x206 {value: 0x0000, lo: 0x0f}, {value: 0x0008, lo: 0x80, hi: 0x94}, {value: 0x3008, lo: 0x95, hi: 0x95}, {value: 0x3308, lo: 0x96, hi: 0x96}, {value: 0x3008, lo: 0x97, hi: 0x97}, {value: 0x3308, lo: 0x98, hi: 0x9e}, {value: 0x0040, lo: 0x9f, hi: 0x9f}, {value: 0x3b08, lo: 0xa0, hi: 0xa0}, {value: 0x3008, lo: 0xa1, hi: 0xa1}, {value: 0x3308, lo: 0xa2, hi: 0xa2}, {value: 0x3008, lo: 0xa3, hi: 0xa4}, {value: 0x3308, lo: 0xa5, hi: 0xac}, {value: 0x3008, lo: 0xad, hi: 0xb2}, {value: 0x3308, lo: 0xb3, hi: 0xbc}, {value: 0x0040, lo: 0xbd, hi: 0xbe}, {value: 0x3308, lo: 0xbf, hi: 0xbf}, // Block 0x3c, offset 0x216 {value: 0x0000, lo: 0x0b}, {value: 0x0008, lo: 0x80, hi: 0x89}, {value: 0x0040, lo: 0x8a, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xa6}, {value: 0x0008, lo: 0xa7, hi: 0xa7}, {value: 0x0018, lo: 0xa8, hi: 0xad}, {value: 0x0040, lo: 0xae, hi: 0xaf}, {value: 0x3308, lo: 0xb0, hi: 0xbd}, {value: 0x3318, lo: 0xbe, hi: 0xbe}, {value: 0x0040, lo: 0xbf, hi: 0xbf}, // Block 0x3d, offset 0x222 {value: 0x0000, lo: 0x01}, {value: 0x0040, lo: 0x80, hi: 0xbf}, // Block 0x3e, offset 0x224 {value: 0x0000, lo: 0x09}, {value: 0x3308, lo: 0x80, hi: 0x83}, {value: 0x3008, lo: 0x84, hi: 0x84}, {value: 0x0008, lo: 0x85, hi: 0xb3}, {value: 0x3308, lo: 0xb4, hi: 0xb4}, {value: 0x3008, lo: 0xb5, hi: 0xb5}, {value: 0x3308, lo: 0xb6, hi: 0xba}, {value: 0x3008, lo: 0xbb, hi: 0xbb}, {value: 0x3308, lo: 0xbc, hi: 0xbc}, {value: 0x3008, lo: 0xbd, hi: 0xbf}, // Block 0x3f, offset 0x22e {value: 0x0000, lo: 0x0b}, {value: 0x3008, lo: 0x80, hi: 0x81}, {value: 0x3308, lo: 0x82, hi: 0x82}, {value: 0x3008, lo: 0x83, hi: 0x83}, {value: 0x3808, lo: 0x84, hi: 0x84}, {value: 0x0008, lo: 0x85, hi: 0x8b}, {value: 0x0040, lo: 0x8c, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0018, lo: 0x9a, hi: 0xaa}, {value: 0x3308, lo: 0xab, hi: 0xb3}, {value: 0x0018, lo: 0xb4, hi: 0xbc}, {value: 0x0040, lo: 0xbd, hi: 0xbf}, // Block 0x40, offset 0x23a {value: 0x0000, lo: 0x0b}, {value: 0x3308, lo: 0x80, hi: 0x81}, {value: 0x3008, lo: 0x82, hi: 0x82}, {value: 0x0008, lo: 0x83, hi: 0xa0}, {value: 0x3008, lo: 0xa1, hi: 0xa1}, {value: 0x3308, lo: 0xa2, hi: 0xa5}, {value: 0x3008, lo: 0xa6, hi: 0xa7}, {value: 0x3308, lo: 0xa8, hi: 0xa9}, {value: 0x3808, lo: 0xaa, hi: 0xaa}, {value: 0x3b08, lo: 0xab, hi: 0xab}, {value: 0x3308, lo: 0xac, hi: 0xad}, {value: 0x0008, lo: 0xae, hi: 0xbf}, // Block 0x41, offset 0x246 {value: 0x0000, lo: 0x0b}, {value: 0x0008, lo: 0x80, hi: 0xa5}, {value: 0x3308, lo: 0xa6, hi: 0xa6}, {value: 0x3008, lo: 0xa7, hi: 0xa7}, {value: 0x3308, lo: 0xa8, hi: 0xa9}, {value: 0x3008, lo: 0xaa, hi: 0xac}, {value: 0x3308, lo: 0xad, hi: 0xad}, {value: 0x3008, lo: 0xae, hi: 0xae}, {value: 0x3308, lo: 0xaf, hi: 0xb1}, {value: 0x3808, lo: 0xb2, hi: 0xb3}, {value: 0x0040, lo: 0xb4, hi: 0xbb}, {value: 0x0018, lo: 0xbc, hi: 0xbf}, // Block 0x42, offset 0x252 {value: 0x0000, lo: 0x07}, {value: 0x0008, lo: 0x80, hi: 0xa3}, {value: 0x3008, lo: 0xa4, hi: 0xab}, {value: 0x3308, lo: 0xac, hi: 0xb3}, {value: 0x3008, lo: 0xb4, hi: 0xb5}, {value: 0x3308, lo: 0xb6, hi: 0xb7}, {value: 0x0040, lo: 0xb8, hi: 0xba}, {value: 0x0018, lo: 0xbb, hi: 0xbf}, // Block 0x43, offset 0x25a {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0x89}, {value: 0x0040, lo: 0x8a, hi: 0x8c}, {value: 0x0008, lo: 0x8d, hi: 0xbd}, {value: 0x0018, lo: 0xbe, hi: 0xbf}, // Block 0x44, offset 0x25f {value: 0x0000, lo: 0x09}, {value: 0x0e29, lo: 0x80, hi: 0x80}, {value: 0x0e41, lo: 0x81, hi: 0x81}, {value: 0x0e59, lo: 0x82, hi: 0x82}, {value: 0x0e71, lo: 0x83, hi: 0x83}, {value: 0x0e89, lo: 0x84, hi: 0x85}, {value: 0x0ea1, lo: 0x86, hi: 0x86}, {value: 0x0eb9, lo: 0x87, hi: 0x87}, {value: 0x057d, lo: 0x88, hi: 0x88}, {value: 0x0040, lo: 0x89, hi: 0xbf}, // Block 0x45, offset 0x269 {value: 0x0000, lo: 0x10}, {value: 0x0018, lo: 0x80, hi: 0x87}, {value: 0x0040, lo: 0x88, hi: 0x8f}, {value: 0x3308, lo: 0x90, hi: 0x92}, {value: 0x0018, lo: 0x93, hi: 0x93}, {value: 0x3308, lo: 0x94, hi: 0xa0}, {value: 0x3008, lo: 0xa1, hi: 0xa1}, {value: 0x3308, lo: 0xa2, hi: 0xa8}, {value: 0x0008, lo: 0xa9, hi: 0xac}, {value: 0x3308, lo: 0xad, hi: 0xad}, {value: 0x0008, lo: 0xae, hi: 0xb1}, {value: 0x3008, lo: 0xb2, hi: 0xb3}, {value: 0x3308, lo: 0xb4, hi: 0xb4}, {value: 0x0008, lo: 0xb5, hi: 0xb6}, {value: 0x3008, lo: 0xb7, hi: 0xb7}, {value: 0x3308, lo: 0xb8, hi: 0xb9}, {value: 0x0040, lo: 0xba, hi: 0xbf}, // Block 0x46, offset 0x27a {value: 0x0000, lo: 0x03}, {value: 0x3308, lo: 0x80, hi: 0xb9}, {value: 0x0040, lo: 0xba, hi: 0xba}, {value: 0x3308, lo: 0xbb, hi: 0xbf}, // Block 0x47, offset 0x27e {value: 0x0000, lo: 0x0a}, {value: 0x0008, lo: 0x80, hi: 0x87}, {value: 0xe045, lo: 0x88, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x95}, {value: 0x0040, lo: 0x96, hi: 0x97}, {value: 0xe045, lo: 0x98, hi: 0x9d}, {value: 0x0040, lo: 0x9e, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa7}, {value: 0xe045, lo: 0xa8, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xb7}, {value: 0xe045, lo: 0xb8, hi: 0xbf}, // Block 0x48, offset 0x289 {value: 0x0000, lo: 0x03}, {value: 0x0040, lo: 0x80, hi: 0x8f}, {value: 0x3318, lo: 0x90, hi: 0xb0}, {value: 0x0040, lo: 0xb1, hi: 0xbf}, // Block 0x49, offset 0x28d {value: 0x0000, lo: 0x08}, {value: 0x0018, lo: 0x80, hi: 0x82}, {value: 0x0040, lo: 0x83, hi: 0x83}, {value: 0x0008, lo: 0x84, hi: 0x84}, {value: 0x0018, lo: 0x85, hi: 0x88}, {value: 0x24c1, lo: 0x89, hi: 0x89}, {value: 0x0018, lo: 0x8a, hi: 0x8b}, {value: 0x0040, lo: 0x8c, hi: 0x8f}, {value: 0x0018, lo: 0x90, hi: 0xbf}, // Block 0x4a, offset 0x296 {value: 0x0000, lo: 0x07}, {value: 0x0018, lo: 0x80, hi: 0xab}, {value: 0x24f1, lo: 0xac, hi: 0xac}, {value: 0x2529, lo: 0xad, hi: 0xad}, {value: 0x0018, lo: 0xae, hi: 0xae}, {value: 0x2579, lo: 0xaf, hi: 0xaf}, {value: 0x25b1, lo: 0xb0, hi: 0xb0}, {value: 0x0018, lo: 0xb1, hi: 0xbf}, // Block 0x4b, offset 0x29e {value: 0x0000, lo: 0x05}, {value: 0x0018, lo: 0x80, hi: 0x9f}, {value: 0x0080, lo: 0xa0, hi: 0xa0}, {value: 0x0018, lo: 0xa1, hi: 0xad}, {value: 0x0080, lo: 0xae, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xbf}, // Block 0x4c, offset 0x2a4 {value: 0x0000, lo: 0x04}, {value: 0x0018, lo: 0x80, hi: 0xa8}, {value: 0x09c5, lo: 0xa9, hi: 0xa9}, {value: 0x09e5, lo: 0xaa, hi: 0xaa}, {value: 0x0018, lo: 0xab, hi: 0xbf}, // Block 0x4d, offset 0x2a9 {value: 0x0000, lo: 0x02}, {value: 0x0018, lo: 0x80, hi: 0xa6}, {value: 0x0040, lo: 0xa7, hi: 0xbf}, // Block 0x4e, offset 0x2ac {value: 0x0000, lo: 0x03}, {value: 0x0018, lo: 0x80, hi: 0x8b}, {value: 0x28c1, lo: 0x8c, hi: 0x8c}, {value: 0x0018, lo: 0x8d, hi: 0xbf}, // Block 0x4f, offset 0x2b0 {value: 0x0000, lo: 0x05}, {value: 0x0018, lo: 0x80, hi: 0xb3}, {value: 0x0e66, lo: 0xb4, hi: 0xb4}, {value: 0x292a, lo: 0xb5, hi: 0xb5}, {value: 0x0e86, lo: 0xb6, hi: 0xb6}, {value: 0x0018, lo: 0xb7, hi: 0xbf}, // Block 0x50, offset 0x2b6 {value: 0x0000, lo: 0x03}, {value: 0x0018, lo: 0x80, hi: 0x9b}, {value: 0x2941, lo: 0x9c, hi: 0x9c}, {value: 0x0018, lo: 0x9d, hi: 0xbf}, // Block 0x51, offset 0x2ba {value: 0x0000, lo: 0x03}, {value: 0x0018, lo: 0x80, hi: 0xb3}, {value: 0x0040, lo: 0xb4, hi: 0xb5}, {value: 0x0018, lo: 0xb6, hi: 0xbf}, // Block 0x52, offset 0x2be {value: 0x0000, lo: 0x03}, {value: 0x0018, lo: 0x80, hi: 0x95}, {value: 0x0040, lo: 0x96, hi: 0x97}, {value: 0x0018, lo: 0x98, hi: 0xbf}, // Block 0x53, offset 0x2c2 {value: 0x0000, lo: 0x04}, {value: 0x0018, lo: 0x80, hi: 0x88}, {value: 0x0040, lo: 0x89, hi: 0x89}, {value: 0x0018, lo: 0x8a, hi: 0xbe}, {value: 0x0040, lo: 0xbf, hi: 0xbf}, // Block 0x54, offset 0x2c7 {value: 0x0000, lo: 0x05}, {value: 0xe185, lo: 0x80, hi: 0x8f}, {value: 0x03f5, lo: 0x90, hi: 0x9f}, {value: 0x0ea5, lo: 0xa0, hi: 0xae}, {value: 0x0040, lo: 0xaf, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xbf}, // Block 0x55, offset 0x2cd {value: 0x0000, lo: 0x07}, {value: 0x0008, lo: 0x80, hi: 0xa5}, {value: 0x0040, lo: 0xa6, hi: 0xa6}, {value: 0x0008, lo: 0xa7, hi: 0xa7}, {value: 0x0040, lo: 0xa8, hi: 0xac}, {value: 0x0008, lo: 0xad, hi: 0xad}, {value: 0x0040, lo: 0xae, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xbf}, // Block 0x56, offset 0x2d5 {value: 0x0000, lo: 0x06}, {value: 0x0008, lo: 0x80, hi: 0xa7}, {value: 0x0040, lo: 0xa8, hi: 0xae}, {value: 0xe075, lo: 0xaf, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xb0}, {value: 0x0040, lo: 0xb1, hi: 0xbe}, {value: 0x3b08, lo: 0xbf, hi: 0xbf}, // Block 0x57, offset 0x2dc {value: 0x0000, lo: 0x0a}, {value: 0x0008, lo: 0x80, hi: 0x96}, {value: 0x0040, lo: 0x97, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa6}, {value: 0x0040, lo: 0xa7, hi: 0xa7}, {value: 0x0008, lo: 0xa8, hi: 0xae}, {value: 0x0040, lo: 0xaf, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xb6}, {value: 0x0040, lo: 0xb7, hi: 0xb7}, {value: 0x0008, lo: 0xb8, hi: 0xbe}, {value: 0x0040, lo: 0xbf, hi: 0xbf}, // Block 0x58, offset 0x2e7 {value: 0x0000, lo: 0x09}, {value: 0x0008, lo: 0x80, hi: 0x86}, {value: 0x0040, lo: 0x87, hi: 0x87}, {value: 0x0008, lo: 0x88, hi: 0x8e}, {value: 0x0040, lo: 0x8f, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x96}, {value: 0x0040, lo: 0x97, hi: 0x97}, {value: 0x0008, lo: 0x98, hi: 0x9e}, {value: 0x0040, lo: 0x9f, hi: 0x9f}, {value: 0x3308, lo: 0xa0, hi: 0xbf}, // Block 0x59, offset 0x2f1 {value: 0x0000, lo: 0x03}, {value: 0x0018, lo: 0x80, hi: 0xae}, {value: 0x0008, lo: 0xaf, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xbf}, // Block 0x5a, offset 0x2f5 {value: 0x0000, lo: 0x02}, {value: 0x0018, lo: 0x80, hi: 0x8e}, {value: 0x0040, lo: 0x8f, hi: 0xbf}, // Block 0x5b, offset 0x2f8 {value: 0x0000, lo: 0x05}, {value: 0x0018, lo: 0x80, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9a}, {value: 0x0018, lo: 0x9b, hi: 0x9e}, {value: 0x0edd, lo: 0x9f, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xbf}, // Block 0x5c, offset 0x2fe {value: 0x0000, lo: 0x03}, {value: 0x0018, lo: 0x80, hi: 0xb2}, {value: 0x0efd, lo: 0xb3, hi: 0xb3}, {value: 0x0040, lo: 0xb4, hi: 0xbf}, // Block 0x5d, offset 0x302 {value: 0x0020, lo: 0x01}, {value: 0x0f1d, lo: 0x80, hi: 0xbf}, // Block 0x5e, offset 0x304 {value: 0x0020, lo: 0x02}, {value: 0x171d, lo: 0x80, hi: 0x8f}, {value: 0x18fd, lo: 0x90, hi: 0xbf}, // Block 0x5f, offset 0x307 {value: 0x0020, lo: 0x01}, {value: 0x1efd, lo: 0x80, hi: 0xbf}, // Block 0x60, offset 0x309 {value: 0x0000, lo: 0x02}, {value: 0x0040, lo: 0x80, hi: 0x80}, {value: 0x0008, lo: 0x81, hi: 0xbf}, // Block 0x61, offset 0x30c {value: 0x0000, lo: 0x09}, {value: 0x0008, lo: 0x80, hi: 0x96}, {value: 0x0040, lo: 0x97, hi: 0x98}, {value: 0x3308, lo: 0x99, hi: 0x9a}, {value: 0x29e2, lo: 0x9b, hi: 0x9b}, {value: 0x2a0a, lo: 0x9c, hi: 0x9c}, {value: 0x0008, lo: 0x9d, hi: 0x9e}, {value: 0x2a31, lo: 0x9f, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xa0}, {value: 0x0008, lo: 0xa1, hi: 0xbf}, // Block 0x62, offset 0x316 {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0xbe}, {value: 0x2a69, lo: 0xbf, hi: 0xbf}, // Block 0x63, offset 0x319 {value: 0x0000, lo: 0x0e}, {value: 0x0040, lo: 0x80, hi: 0x84}, {value: 0x0008, lo: 0x85, hi: 0xaf}, {value: 0x0040, lo: 0xb0, hi: 0xb0}, {value: 0x2a1d, lo: 0xb1, hi: 0xb1}, {value: 0x2a3d, lo: 0xb2, hi: 0xb2}, {value: 0x2a5d, lo: 0xb3, hi: 0xb3}, {value: 0x2a7d, lo: 0xb4, hi: 0xb4}, {value: 0x2a5d, lo: 0xb5, hi: 0xb5}, {value: 0x2a9d, lo: 0xb6, hi: 0xb6}, {value: 0x2abd, lo: 0xb7, hi: 0xb7}, {value: 0x2add, lo: 0xb8, hi: 0xb9}, {value: 0x2afd, lo: 0xba, hi: 0xbb}, {value: 0x2b1d, lo: 0xbc, hi: 0xbd}, {value: 0x2afd, lo: 0xbe, hi: 0xbf}, // Block 0x64, offset 0x328 {value: 0x0000, lo: 0x03}, {value: 0x0018, lo: 0x80, hi: 0xa3}, {value: 0x0040, lo: 0xa4, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xbf}, // Block 0x65, offset 0x32c {value: 0x0030, lo: 0x04}, {value: 0x2aa2, lo: 0x80, hi: 0x9d}, {value: 0x305a, lo: 0x9e, hi: 0x9e}, {value: 0x0040, lo: 0x9f, hi: 0x9f}, {value: 0x30a2, lo: 0xa0, hi: 0xbf}, // Block 0x66, offset 0x331 {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0xaf}, {value: 0x0040, lo: 0xb0, hi: 0xbf}, // Block 0x67, offset 0x334 {value: 0x0000, lo: 0x03}, {value: 0x0008, lo: 0x80, hi: 0x8c}, {value: 0x0040, lo: 0x8d, hi: 0x8f}, {value: 0x0018, lo: 0x90, hi: 0xbf}, // Block 0x68, offset 0x338 {value: 0x0000, lo: 0x04}, {value: 0x0018, lo: 0x80, hi: 0x86}, {value: 0x0040, lo: 0x87, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0xbd}, {value: 0x0018, lo: 0xbe, hi: 0xbf}, // Block 0x69, offset 0x33d {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0x8c}, {value: 0x0018, lo: 0x8d, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0xab}, {value: 0x0040, lo: 0xac, hi: 0xbf}, // Block 0x6a, offset 0x342 {value: 0x0000, lo: 0x05}, {value: 0x0008, lo: 0x80, hi: 0xa5}, {value: 0x0018, lo: 0xa6, hi: 0xaf}, {value: 0x3308, lo: 0xb0, hi: 0xb1}, {value: 0x0018, lo: 0xb2, hi: 0xb7}, {value: 0x0040, lo: 0xb8, hi: 0xbf}, // Block 0x6b, offset 0x348 {value: 0x0000, lo: 0x05}, {value: 0x0040, lo: 0x80, hi: 0xb6}, {value: 0x0008, lo: 0xb7, hi: 0xb7}, {value: 0x2009, lo: 0xb8, hi: 0xb8}, {value: 0x6e89, lo: 0xb9, hi: 0xb9}, {value: 0x0008, lo: 0xba, hi: 0xbf}, // Block 0x6c, offset 0x34e {value: 0x0000, lo: 0x0e}, {value: 0x0008, lo: 0x80, hi: 0x81}, {value: 0x3308, lo: 0x82, hi: 0x82}, {value: 0x0008, lo: 0x83, hi: 0x85}, {value: 0x3b08, lo: 0x86, hi: 0x86}, {value: 0x0008, lo: 0x87, hi: 0x8a}, {value: 0x3308, lo: 0x8b, hi: 0x8b}, {value: 0x0008, lo: 0x8c, hi: 0xa2}, {value: 0x3008, lo: 0xa3, hi: 0xa4}, {value: 0x3308, lo: 0xa5, hi: 0xa6}, {value: 0x3008, lo: 0xa7, hi: 0xa7}, {value: 0x0018, lo: 0xa8, hi: 0xab}, {value: 0x0040, lo: 0xac, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xb9}, {value: 0x0040, lo: 0xba, hi: 0xbf}, // Block 0x6d, offset 0x35d {value: 0x0000, lo: 0x05}, {value: 0x0208, lo: 0x80, hi: 0xb1}, {value: 0x0108, lo: 0xb2, hi: 0xb2}, {value: 0x0008, lo: 0xb3, hi: 0xb3}, {value: 0x0018, lo: 0xb4, hi: 0xb7}, {value: 0x0040, lo: 0xb8, hi: 0xbf}, // Block 0x6e, offset 0x363 {value: 0x0000, lo: 0x03}, {value: 0x3008, lo: 0x80, hi: 0x81}, {value: 0x0008, lo: 0x82, hi: 0xb3}, {value: 0x3008, lo: 0xb4, hi: 0xbf}, // Block 0x6f, offset 0x367 {value: 0x0000, lo: 0x0e}, {value: 0x3008, lo: 0x80, hi: 0x83}, {value: 0x3b08, lo: 0x84, hi: 0x84}, {value: 0x3308, lo: 0x85, hi: 0x85}, {value: 0x0040, lo: 0x86, hi: 0x8d}, {value: 0x0018, lo: 0x8e, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9f}, {value: 0x3308, lo: 0xa0, hi: 0xb1}, {value: 0x0008, lo: 0xb2, hi: 0xb7}, {value: 0x0018, lo: 0xb8, hi: 0xba}, {value: 0x0008, lo: 0xbb, hi: 0xbb}, {value: 0x0018, lo: 0xbc, hi: 0xbc}, {value: 0x0008, lo: 0xbd, hi: 0xbe}, {value: 0x3308, lo: 0xbf, hi: 0xbf}, // Block 0x70, offset 0x376 {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0xa5}, {value: 0x3308, lo: 0xa6, hi: 0xad}, {value: 0x0018, lo: 0xae, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xbf}, // Block 0x71, offset 0x37b {value: 0x0000, lo: 0x07}, {value: 0x0008, lo: 0x80, hi: 0x86}, {value: 0x3308, lo: 0x87, hi: 0x91}, {value: 0x3008, lo: 0x92, hi: 0x92}, {value: 0x3808, lo: 0x93, hi: 0x93}, {value: 0x0040, lo: 0x94, hi: 0x9e}, {value: 0x0018, lo: 0x9f, hi: 0xbc}, {value: 0x0040, lo: 0xbd, hi: 0xbf}, // Block 0x72, offset 0x383 {value: 0x0000, lo: 0x09}, {value: 0x3308, lo: 0x80, hi: 0x82}, {value: 0x3008, lo: 0x83, hi: 0x83}, {value: 0x0008, lo: 0x84, hi: 0xb2}, {value: 0x3308, lo: 0xb3, hi: 0xb3}, {value: 0x3008, lo: 0xb4, hi: 0xb5}, {value: 0x3308, lo: 0xb6, hi: 0xb9}, {value: 0x3008, lo: 0xba, hi: 0xbb}, {value: 0x3308, lo: 0xbc, hi: 0xbc}, {value: 0x3008, lo: 0xbd, hi: 0xbf}, // Block 0x73, offset 0x38d {value: 0x0000, lo: 0x0a}, {value: 0x3808, lo: 0x80, hi: 0x80}, {value: 0x0018, lo: 0x81, hi: 0x8d}, {value: 0x0040, lo: 0x8e, hi: 0x8e}, {value: 0x0008, lo: 0x8f, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9d}, {value: 0x0018, lo: 0x9e, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa4}, {value: 0x3308, lo: 0xa5, hi: 0xa5}, {value: 0x0008, lo: 0xa6, hi: 0xbe}, {value: 0x0040, lo: 0xbf, hi: 0xbf}, // Block 0x74, offset 0x398 {value: 0x0000, lo: 0x07}, {value: 0x0008, lo: 0x80, hi: 0xa8}, {value: 0x3308, lo: 0xa9, hi: 0xae}, {value: 0x3008, lo: 0xaf, hi: 0xb0}, {value: 0x3308, lo: 0xb1, hi: 0xb2}, {value: 0x3008, lo: 0xb3, hi: 0xb4}, {value: 0x3308, lo: 0xb5, hi: 0xb6}, {value: 0x0040, lo: 0xb7, hi: 0xbf}, // Block 0x75, offset 0x3a0 {value: 0x0000, lo: 0x10}, {value: 0x0008, lo: 0x80, hi: 0x82}, {value: 0x3308, lo: 0x83, hi: 0x83}, {value: 0x0008, lo: 0x84, hi: 0x8b}, {value: 0x3308, lo: 0x8c, hi: 0x8c}, {value: 0x3008, lo: 0x8d, hi: 0x8d}, {value: 0x0040, lo: 0x8e, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9b}, {value: 0x0018, lo: 0x9c, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xb6}, {value: 0x0018, lo: 0xb7, hi: 0xb9}, {value: 0x0008, lo: 0xba, hi: 0xba}, {value: 0x3008, lo: 0xbb, hi: 0xbb}, {value: 0x3308, lo: 0xbc, hi: 0xbc}, {value: 0x3008, lo: 0xbd, hi: 0xbd}, {value: 0x0008, lo: 0xbe, hi: 0xbf}, // Block 0x76, offset 0x3b1 {value: 0x0000, lo: 0x08}, {value: 0x0008, lo: 0x80, hi: 0xaf}, {value: 0x3308, lo: 0xb0, hi: 0xb0}, {value: 0x0008, lo: 0xb1, hi: 0xb1}, {value: 0x3308, lo: 0xb2, hi: 0xb4}, {value: 0x0008, lo: 0xb5, hi: 0xb6}, {value: 0x3308, lo: 0xb7, hi: 0xb8}, {value: 0x0008, lo: 0xb9, hi: 0xbd}, {value: 0x3308, lo: 0xbe, hi: 0xbf}, // Block 0x77, offset 0x3ba {value: 0x0000, lo: 0x0f}, {value: 0x0008, lo: 0x80, hi: 0x80}, {value: 0x3308, lo: 0x81, hi: 0x81}, {value: 0x0008, lo: 0x82, hi: 0x82}, {value: 0x0040, lo: 0x83, hi: 0x9a}, {value: 0x0008, lo: 0x9b, hi: 0x9d}, {value: 0x0018, lo: 0x9e, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xaa}, {value: 0x3008, lo: 0xab, hi: 0xab}, {value: 0x3308, lo: 0xac, hi: 0xad}, {value: 0x3008, lo: 0xae, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xb1}, {value: 0x0008, lo: 0xb2, hi: 0xb4}, {value: 0x3008, lo: 0xb5, hi: 0xb5}, {value: 0x3b08, lo: 0xb6, hi: 0xb6}, {value: 0x0040, lo: 0xb7, hi: 0xbf}, // Block 0x78, offset 0x3ca {value: 0x0000, lo: 0x0c}, {value: 0x0040, lo: 0x80, hi: 0x80}, {value: 0x0008, lo: 0x81, hi: 0x86}, {value: 0x0040, lo: 0x87, hi: 0x88}, {value: 0x0008, lo: 0x89, hi: 0x8e}, {value: 0x0040, lo: 0x8f, hi: 0x90}, {value: 0x0008, lo: 0x91, hi: 0x96}, {value: 0x0040, lo: 0x97, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa6}, {value: 0x0040, lo: 0xa7, hi: 0xa7}, {value: 0x0008, lo: 0xa8, hi: 0xae}, {value: 0x0040, lo: 0xaf, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xbf}, // Block 0x79, offset 0x3d7 {value: 0x0000, lo: 0x09}, {value: 0x0008, lo: 0x80, hi: 0x9a}, {value: 0x0018, lo: 0x9b, hi: 0x9b}, {value: 0x4465, lo: 0x9c, hi: 0x9c}, {value: 0x447d, lo: 0x9d, hi: 0x9d}, {value: 0x2971, lo: 0x9e, hi: 0x9e}, {value: 0xe06d, lo: 0x9f, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa5}, {value: 0x0040, lo: 0xa6, hi: 0xaf}, {value: 0x4495, lo: 0xb0, hi: 0xbf}, // Block 0x7a, offset 0x3e1 {value: 0x0000, lo: 0x04}, {value: 0x44b5, lo: 0x80, hi: 0x8f}, {value: 0x44d5, lo: 0x90, hi: 0x9f}, {value: 0x44f5, lo: 0xa0, hi: 0xaf}, {value: 0x44d5, lo: 0xb0, hi: 0xbf}, // Block 0x7b, offset 0x3e6 {value: 0x0000, lo: 0x0c}, {value: 0x0008, lo: 0x80, hi: 0xa2}, {value: 0x3008, lo: 0xa3, hi: 0xa4}, {value: 0x3308, lo: 0xa5, hi: 0xa5}, {value: 0x3008, lo: 0xa6, hi: 0xa7}, {value: 0x3308, lo: 0xa8, hi: 0xa8}, {value: 0x3008, lo: 0xa9, hi: 0xaa}, {value: 0x0018, lo: 0xab, hi: 0xab}, {value: 0x3008, lo: 0xac, hi: 0xac}, {value: 0x3b08, lo: 0xad, hi: 0xad}, {value: 0x0040, lo: 0xae, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xb9}, {value: 0x0040, lo: 0xba, hi: 0xbf}, // Block 0x7c, offset 0x3f3 {value: 0x0000, lo: 0x03}, {value: 0x0008, lo: 0x80, hi: 0xa3}, {value: 0x0040, lo: 0xa4, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xbf}, // Block 0x7d, offset 0x3f7 {value: 0x0000, lo: 0x04}, {value: 0x0018, lo: 0x80, hi: 0x86}, {value: 0x0040, lo: 0x87, hi: 0x8a}, {value: 0x0018, lo: 0x8b, hi: 0xbb}, {value: 0x0040, lo: 0xbc, hi: 0xbf}, // Block 0x7e, offset 0x3fc {value: 0x0020, lo: 0x01}, {value: 0x4515, lo: 0x80, hi: 0xbf}, // Block 0x7f, offset 0x3fe {value: 0x0020, lo: 0x03}, {value: 0x4d15, lo: 0x80, hi: 0x94}, {value: 0x4ad5, lo: 0x95, hi: 0x95}, {value: 0x4fb5, lo: 0x96, hi: 0xbf}, // Block 0x80, offset 0x402 {value: 0x0020, lo: 0x01}, {value: 0x54f5, lo: 0x80, hi: 0xbf}, // Block 0x81, offset 0x404 {value: 0x0020, lo: 0x03}, {value: 0x5cf5, lo: 0x80, hi: 0x84}, {value: 0x5655, lo: 0x85, hi: 0x85}, {value: 0x5d95, lo: 0x86, hi: 0xbf}, // Block 0x82, offset 0x408 {value: 0x0020, lo: 0x08}, {value: 0x6b55, lo: 0x80, hi: 0x8f}, {value: 0x6d15, lo: 0x90, hi: 0x90}, {value: 0x6d55, lo: 0x91, hi: 0xab}, {value: 0x6ea1, lo: 0xac, hi: 0xac}, {value: 0x70b5, lo: 0xad, hi: 0xad}, {value: 0x0040, lo: 0xae, hi: 0xae}, {value: 0x0040, lo: 0xaf, hi: 0xaf}, {value: 0x70d5, lo: 0xb0, hi: 0xbf}, // Block 0x83, offset 0x411 {value: 0x0020, lo: 0x05}, {value: 0x72d5, lo: 0x80, hi: 0xad}, {value: 0x6535, lo: 0xae, hi: 0xae}, {value: 0x7895, lo: 0xaf, hi: 0xb5}, {value: 0x6f55, lo: 0xb6, hi: 0xb6}, {value: 0x7975, lo: 0xb7, hi: 0xbf}, // Block 0x84, offset 0x417 {value: 0x0028, lo: 0x03}, {value: 0x7c21, lo: 0x80, hi: 0x82}, {value: 0x7be1, lo: 0x83, hi: 0x83}, {value: 0x7c99, lo: 0x84, hi: 0xbf}, // Block 0x85, offset 0x41b {value: 0x0038, lo: 0x0f}, {value: 0x9db1, lo: 0x80, hi: 0x83}, {value: 0x9e59, lo: 0x84, hi: 0x85}, {value: 0x9e91, lo: 0x86, hi: 0x87}, {value: 0x9ec9, lo: 0x88, hi: 0x8f}, {value: 0x0040, lo: 0x90, hi: 0x90}, {value: 0x0040, lo: 0x91, hi: 0x91}, {value: 0xa089, lo: 0x92, hi: 0x97}, {value: 0xa1a1, lo: 0x98, hi: 0x9c}, {value: 0xa281, lo: 0x9d, hi: 0xb3}, {value: 0x9d41, lo: 0xb4, hi: 0xb4}, {value: 0x9db1, lo: 0xb5, hi: 0xb5}, {value: 0xa789, lo: 0xb6, hi: 0xbb}, {value: 0xa869, lo: 0xbc, hi: 0xbc}, {value: 0xa7f9, lo: 0xbd, hi: 0xbd}, {value: 0xa8d9, lo: 0xbe, hi: 0xbf}, // Block 0x86, offset 0x42b {value: 0x0000, lo: 0x09}, {value: 0x0008, lo: 0x80, hi: 0x8b}, {value: 0x0040, lo: 0x8c, hi: 0x8c}, {value: 0x0008, lo: 0x8d, hi: 0xa6}, {value: 0x0040, lo: 0xa7, hi: 0xa7}, {value: 0x0008, lo: 0xa8, hi: 0xba}, {value: 0x0040, lo: 0xbb, hi: 0xbb}, {value: 0x0008, lo: 0xbc, hi: 0xbd}, {value: 0x0040, lo: 0xbe, hi: 0xbe}, {value: 0x0008, lo: 0xbf, hi: 0xbf}, // Block 0x87, offset 0x435 {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0x8d}, {value: 0x0040, lo: 0x8e, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x9d}, {value: 0x0040, lo: 0x9e, hi: 0xbf}, // Block 0x88, offset 0x43a {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0xba}, {value: 0x0040, lo: 0xbb, hi: 0xbf}, // Block 0x89, offset 0x43d {value: 0x0000, lo: 0x05}, {value: 0x0018, lo: 0x80, hi: 0x82}, {value: 0x0040, lo: 0x83, hi: 0x86}, {value: 0x0018, lo: 0x87, hi: 0xb3}, {value: 0x0040, lo: 0xb4, hi: 0xb6}, {value: 0x0018, lo: 0xb7, hi: 0xbf}, // Block 0x8a, offset 0x443 {value: 0x0000, lo: 0x06}, {value: 0x0018, lo: 0x80, hi: 0x8e}, {value: 0x0040, lo: 0x8f, hi: 0x8f}, {value: 0x0018, lo: 0x90, hi: 0x9b}, {value: 0x0040, lo: 0x9c, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xa0}, {value: 0x0040, lo: 0xa1, hi: 0xbf}, // Block 0x8b, offset 0x44a {value: 0x0000, lo: 0x04}, {value: 0x0040, lo: 0x80, hi: 0x8f}, {value: 0x0018, lo: 0x90, hi: 0xbc}, {value: 0x3308, lo: 0xbd, hi: 0xbd}, {value: 0x0040, lo: 0xbe, hi: 0xbf}, // Block 0x8c, offset 0x44f {value: 0x0000, lo: 0x03}, {value: 0x0008, lo: 0x80, hi: 0x9c}, {value: 0x0040, lo: 0x9d, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xbf}, // Block 0x8d, offset 0x453 {value: 0x0000, lo: 0x05}, {value: 0x0008, lo: 0x80, hi: 0x90}, {value: 0x0040, lo: 0x91, hi: 0x9f}, {value: 0x3308, lo: 0xa0, hi: 0xa0}, {value: 0x0018, lo: 0xa1, hi: 0xbb}, {value: 0x0040, lo: 0xbc, hi: 0xbf}, // Block 0x8e, offset 0x459 {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xa3}, {value: 0x0040, lo: 0xa4, hi: 0xac}, {value: 0x0008, lo: 0xad, hi: 0xbf}, // Block 0x8f, offset 0x45e {value: 0x0000, lo: 0x08}, {value: 0x0008, lo: 0x80, hi: 0x80}, {value: 0x0018, lo: 0x81, hi: 0x81}, {value: 0x0008, lo: 0x82, hi: 0x89}, {value: 0x0018, lo: 0x8a, hi: 0x8a}, {value: 0x0040, lo: 0x8b, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0xb5}, {value: 0x3308, lo: 0xb6, hi: 0xba}, {value: 0x0040, lo: 0xbb, hi: 0xbf}, // Block 0x90, offset 0x467 {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0x9d}, {value: 0x0040, lo: 0x9e, hi: 0x9e}, {value: 0x0018, lo: 0x9f, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xbf}, // Block 0x91, offset 0x46c {value: 0x0000, lo: 0x05}, {value: 0x0008, lo: 0x80, hi: 0x83}, {value: 0x0040, lo: 0x84, hi: 0x87}, {value: 0x0008, lo: 0x88, hi: 0x8f}, {value: 0x0018, lo: 0x90, hi: 0x95}, {value: 0x0040, lo: 0x96, hi: 0xbf}, // Block 0x92, offset 0x472 {value: 0x0000, lo: 0x06}, {value: 0xe145, lo: 0x80, hi: 0x87}, {value: 0xe1c5, lo: 0x88, hi: 0x8f}, {value: 0xe145, lo: 0x90, hi: 0x97}, {value: 0x8ad5, lo: 0x98, hi: 0x9f}, {value: 0x8aed, lo: 0xa0, hi: 0xa7}, {value: 0x0008, lo: 0xa8, hi: 0xbf}, // Block 0x93, offset 0x479 {value: 0x0000, lo: 0x06}, {value: 0x0008, lo: 0x80, hi: 0x9d}, {value: 0x0040, lo: 0x9e, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa9}, {value: 0x0040, lo: 0xaa, hi: 0xaf}, {value: 0x8aed, lo: 0xb0, hi: 0xb7}, {value: 0x8ad5, lo: 0xb8, hi: 0xbf}, // Block 0x94, offset 0x480 {value: 0x0000, lo: 0x06}, {value: 0xe145, lo: 0x80, hi: 0x87}, {value: 0xe1c5, lo: 0x88, hi: 0x8f}, {value: 0xe145, lo: 0x90, hi: 0x93}, {value: 0x0040, lo: 0x94, hi: 0x97}, {value: 0x0008, lo: 0x98, hi: 0xbb}, {value: 0x0040, lo: 0xbc, hi: 0xbf}, // Block 0x95, offset 0x487 {value: 0x0000, lo: 0x03}, {value: 0x0008, lo: 0x80, hi: 0xa7}, {value: 0x0040, lo: 0xa8, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xbf}, // Block 0x96, offset 0x48b {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0xa3}, {value: 0x0040, lo: 0xa4, hi: 0xae}, {value: 0x0018, lo: 0xaf, hi: 0xaf}, {value: 0x0040, lo: 0xb0, hi: 0xbf}, // Block 0x97, offset 0x490 {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0xb6}, {value: 0x0040, lo: 0xb7, hi: 0xbf}, // Block 0x98, offset 0x493 {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0x95}, {value: 0x0040, lo: 0x96, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa7}, {value: 0x0040, lo: 0xa8, hi: 0xbf}, // Block 0x99, offset 0x498 {value: 0x0000, lo: 0x0b}, {value: 0x0808, lo: 0x80, hi: 0x85}, {value: 0x0040, lo: 0x86, hi: 0x87}, {value: 0x0808, lo: 0x88, hi: 0x88}, {value: 0x0040, lo: 0x89, hi: 0x89}, {value: 0x0808, lo: 0x8a, hi: 0xb5}, {value: 0x0040, lo: 0xb6, hi: 0xb6}, {value: 0x0808, lo: 0xb7, hi: 0xb8}, {value: 0x0040, lo: 0xb9, hi: 0xbb}, {value: 0x0808, lo: 0xbc, hi: 0xbc}, {value: 0x0040, lo: 0xbd, hi: 0xbe}, {value: 0x0808, lo: 0xbf, hi: 0xbf}, // Block 0x9a, offset 0x4a4 {value: 0x0000, lo: 0x05}, {value: 0x0808, lo: 0x80, hi: 0x95}, {value: 0x0040, lo: 0x96, hi: 0x96}, {value: 0x0818, lo: 0x97, hi: 0x9f}, {value: 0x0808, lo: 0xa0, hi: 0xb6}, {value: 0x0818, lo: 0xb7, hi: 0xbf}, // Block 0x9b, offset 0x4aa {value: 0x0000, lo: 0x04}, {value: 0x0808, lo: 0x80, hi: 0x9e}, {value: 0x0040, lo: 0x9f, hi: 0xa6}, {value: 0x0818, lo: 0xa7, hi: 0xaf}, {value: 0x0040, lo: 0xb0, hi: 0xbf}, // Block 0x9c, offset 0x4af {value: 0x0000, lo: 0x06}, {value: 0x0040, lo: 0x80, hi: 0x9f}, {value: 0x0808, lo: 0xa0, hi: 0xb2}, {value: 0x0040, lo: 0xb3, hi: 0xb3}, {value: 0x0808, lo: 0xb4, hi: 0xb5}, {value: 0x0040, lo: 0xb6, hi: 0xba}, {value: 0x0818, lo: 0xbb, hi: 0xbf}, // Block 0x9d, offset 0x4b6 {value: 0x0000, lo: 0x07}, {value: 0x0808, lo: 0x80, hi: 0x95}, {value: 0x0818, lo: 0x96, hi: 0x9b}, {value: 0x0040, lo: 0x9c, hi: 0x9e}, {value: 0x0018, lo: 0x9f, hi: 0x9f}, {value: 0x0808, lo: 0xa0, hi: 0xb9}, {value: 0x0040, lo: 0xba, hi: 0xbe}, {value: 0x0818, lo: 0xbf, hi: 0xbf}, // Block 0x9e, offset 0x4be {value: 0x0000, lo: 0x04}, {value: 0x0808, lo: 0x80, hi: 0xb7}, {value: 0x0040, lo: 0xb8, hi: 0xbb}, {value: 0x0818, lo: 0xbc, hi: 0xbd}, {value: 0x0808, lo: 0xbe, hi: 0xbf}, // Block 0x9f, offset 0x4c3 {value: 0x0000, lo: 0x03}, {value: 0x0818, lo: 0x80, hi: 0x8f}, {value: 0x0040, lo: 0x90, hi: 0x91}, {value: 0x0818, lo: 0x92, hi: 0xbf}, // Block 0xa0, offset 0x4c7 {value: 0x0000, lo: 0x0f}, {value: 0x0808, lo: 0x80, hi: 0x80}, {value: 0x3308, lo: 0x81, hi: 0x83}, {value: 0x0040, lo: 0x84, hi: 0x84}, {value: 0x3308, lo: 0x85, hi: 0x86}, {value: 0x0040, lo: 0x87, hi: 0x8b}, {value: 0x3308, lo: 0x8c, hi: 0x8f}, {value: 0x0808, lo: 0x90, hi: 0x93}, {value: 0x0040, lo: 0x94, hi: 0x94}, {value: 0x0808, lo: 0x95, hi: 0x97}, {value: 0x0040, lo: 0x98, hi: 0x98}, {value: 0x0808, lo: 0x99, hi: 0xb5}, {value: 0x0040, lo: 0xb6, hi: 0xb7}, {value: 0x3308, lo: 0xb8, hi: 0xba}, {value: 0x0040, lo: 0xbb, hi: 0xbe}, {value: 0x3b08, lo: 0xbf, hi: 0xbf}, // Block 0xa1, offset 0x4d7 {value: 0x0000, lo: 0x06}, {value: 0x0818, lo: 0x80, hi: 0x88}, {value: 0x0040, lo: 0x89, hi: 0x8f}, {value: 0x0818, lo: 0x90, hi: 0x98}, {value: 0x0040, lo: 0x99, hi: 0x9f}, {value: 0x0808, lo: 0xa0, hi: 0xbc}, {value: 0x0818, lo: 0xbd, hi: 0xbf}, // Block 0xa2, offset 0x4de {value: 0x0000, lo: 0x03}, {value: 0x0808, lo: 0x80, hi: 0x9c}, {value: 0x0818, lo: 0x9d, hi: 0x9f}, {value: 0x0040, lo: 0xa0, hi: 0xbf}, // Block 0xa3, offset 0x4e2 {value: 0x0000, lo: 0x03}, {value: 0x0808, lo: 0x80, hi: 0xb5}, {value: 0x0040, lo: 0xb6, hi: 0xb8}, {value: 0x0018, lo: 0xb9, hi: 0xbf}, // Block 0xa4, offset 0x4e6 {value: 0x0000, lo: 0x06}, {value: 0x0808, lo: 0x80, hi: 0x95}, {value: 0x0040, lo: 0x96, hi: 0x97}, {value: 0x0818, lo: 0x98, hi: 0x9f}, {value: 0x0808, lo: 0xa0, hi: 0xb2}, {value: 0x0040, lo: 0xb3, hi: 0xb7}, {value: 0x0818, lo: 0xb8, hi: 0xbf}, // Block 0xa5, offset 0x4ed {value: 0x0000, lo: 0x01}, {value: 0x0808, lo: 0x80, hi: 0xbf}, // Block 0xa6, offset 0x4ef {value: 0x0000, lo: 0x02}, {value: 0x0808, lo: 0x80, hi: 0x88}, {value: 0x0040, lo: 0x89, hi: 0xbf}, // Block 0xa7, offset 0x4f2 {value: 0x0000, lo: 0x02}, {value: 0x03dd, lo: 0x80, hi: 0xb2}, {value: 0x0040, lo: 0xb3, hi: 0xbf}, // Block 0xa8, offset 0x4f5 {value: 0x0000, lo: 0x03}, {value: 0x0808, lo: 0x80, hi: 0xb2}, {value: 0x0040, lo: 0xb3, hi: 0xb9}, {value: 0x0818, lo: 0xba, hi: 0xbf}, // Block 0xa9, offset 0x4f9 {value: 0x0000, lo: 0x08}, {value: 0x0908, lo: 0x80, hi: 0x80}, {value: 0x0a08, lo: 0x81, hi: 0xa1}, {value: 0x0c08, lo: 0xa2, hi: 0xa2}, {value: 0x0a08, lo: 0xa3, hi: 0xa3}, {value: 0x3308, lo: 0xa4, hi: 0xa7}, {value: 0x0040, lo: 0xa8, hi: 0xaf}, {value: 0x0808, lo: 0xb0, hi: 0xb9}, {value: 0x0040, lo: 0xba, hi: 0xbf}, // Block 0xaa, offset 0x502 {value: 0x0000, lo: 0x03}, {value: 0x0040, lo: 0x80, hi: 0x9f}, {value: 0x0818, lo: 0xa0, hi: 0xbe}, {value: 0x0040, lo: 0xbf, hi: 0xbf}, // Block 0xab, offset 0x506 {value: 0x0000, lo: 0x07}, {value: 0x0808, lo: 0x80, hi: 0x9c}, {value: 0x0818, lo: 0x9d, hi: 0xa6}, {value: 0x0808, lo: 0xa7, hi: 0xa7}, {value: 0x0040, lo: 0xa8, hi: 0xaf}, {value: 0x0a08, lo: 0xb0, hi: 0xb2}, {value: 0x0c08, lo: 0xb3, hi: 0xb3}, {value: 0x0a08, lo: 0xb4, hi: 0xbf}, // Block 0xac, offset 0x50e {value: 0x0000, lo: 0x07}, {value: 0x0a08, lo: 0x80, hi: 0x84}, {value: 0x0808, lo: 0x85, hi: 0x85}, {value: 0x3308, lo: 0x86, hi: 0x90}, {value: 0x0a18, lo: 0x91, hi: 0x93}, {value: 0x0c18, lo: 0x94, hi: 0x94}, {value: 0x0818, lo: 0x95, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0xbf}, // Block 0xad, offset 0x516 {value: 0x0000, lo: 0x05}, {value: 0x3008, lo: 0x80, hi: 0x80}, {value: 0x3308, lo: 0x81, hi: 0x81}, {value: 0x3008, lo: 0x82, hi: 0x82}, {value: 0x0008, lo: 0x83, hi: 0xb7}, {value: 0x3308, lo: 0xb8, hi: 0xbf}, // Block 0xae, offset 0x51c {value: 0x0000, lo: 0x08}, {value: 0x3308, lo: 0x80, hi: 0x85}, {value: 0x3b08, lo: 0x86, hi: 0x86}, {value: 0x0018, lo: 0x87, hi: 0x8d}, {value: 0x0040, lo: 0x8e, hi: 0x91}, {value: 0x0018, lo: 0x92, hi: 0xa5}, {value: 0x0008, lo: 0xa6, hi: 0xaf}, {value: 0x0040, lo: 0xb0, hi: 0xbe}, {value: 0x3b08, lo: 0xbf, hi: 0xbf}, // Block 0xaf, offset 0x525 {value: 0x0000, lo: 0x0b}, {value: 0x3308, lo: 0x80, hi: 0x81}, {value: 0x3008, lo: 0x82, hi: 0x82}, {value: 0x0008, lo: 0x83, hi: 0xaf}, {value: 0x3008, lo: 0xb0, hi: 0xb2}, {value: 0x3308, lo: 0xb3, hi: 0xb6}, {value: 0x3008, lo: 0xb7, hi: 0xb8}, {value: 0x3b08, lo: 0xb9, hi: 0xb9}, {value: 0x3308, lo: 0xba, hi: 0xba}, {value: 0x0018, lo: 0xbb, hi: 0xbc}, {value: 0x0040, lo: 0xbd, hi: 0xbd}, {value: 0x0018, lo: 0xbe, hi: 0xbf}, // Block 0xb0, offset 0x531 {value: 0x0000, lo: 0x06}, {value: 0x0018, lo: 0x80, hi: 0x81}, {value: 0x0040, lo: 0x82, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0xa8}, {value: 0x0040, lo: 0xa9, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xb9}, {value: 0x0040, lo: 0xba, hi: 0xbf}, // Block 0xb1, offset 0x538 {value: 0x0000, lo: 0x08}, {value: 0x3308, lo: 0x80, hi: 0x82}, {value: 0x0008, lo: 0x83, hi: 0xa6}, {value: 0x3308, lo: 0xa7, hi: 0xab}, {value: 0x3008, lo: 0xac, hi: 0xac}, {value: 0x3308, lo: 0xad, hi: 0xb2}, {value: 0x3b08, lo: 0xb3, hi: 0xb4}, {value: 0x0040, lo: 0xb5, hi: 0xb5}, {value: 0x0008, lo: 0xb6, hi: 0xbf}, // Block 0xb2, offset 0x541 {value: 0x0000, lo: 0x09}, {value: 0x0018, lo: 0x80, hi: 0x83}, {value: 0x0008, lo: 0x84, hi: 0x84}, {value: 0x3008, lo: 0x85, hi: 0x86}, {value: 0x0040, lo: 0x87, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0xb2}, {value: 0x3308, lo: 0xb3, hi: 0xb3}, {value: 0x0018, lo: 0xb4, hi: 0xb5}, {value: 0x0008, lo: 0xb6, hi: 0xb6}, {value: 0x0040, lo: 0xb7, hi: 0xbf}, // Block 0xb3, offset 0x54b {value: 0x0000, lo: 0x06}, {value: 0x3308, lo: 0x80, hi: 0x81}, {value: 0x3008, lo: 0x82, hi: 0x82}, {value: 0x0008, lo: 0x83, hi: 0xb2}, {value: 0x3008, lo: 0xb3, hi: 0xb5}, {value: 0x3308, lo: 0xb6, hi: 0xbe}, {value: 0x3008, lo: 0xbf, hi: 0xbf}, // Block 0xb4, offset 0x552 {value: 0x0000, lo: 0x0d}, {value: 0x3808, lo: 0x80, hi: 0x80}, {value: 0x0008, lo: 0x81, hi: 0x84}, {value: 0x0018, lo: 0x85, hi: 0x88}, {value: 0x3308, lo: 0x89, hi: 0x8c}, {value: 0x0018, lo: 0x8d, hi: 0x8d}, {value: 0x0040, lo: 0x8e, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x9a}, {value: 0x0018, lo: 0x9b, hi: 0x9b}, {value: 0x0008, lo: 0x9c, hi: 0x9c}, {value: 0x0018, lo: 0x9d, hi: 0x9f}, {value: 0x0040, lo: 0xa0, hi: 0xa0}, {value: 0x0018, lo: 0xa1, hi: 0xb4}, {value: 0x0040, lo: 0xb5, hi: 0xbf}, // Block 0xb5, offset 0x560 {value: 0x0000, lo: 0x0c}, {value: 0x0008, lo: 0x80, hi: 0x91}, {value: 0x0040, lo: 0x92, hi: 0x92}, {value: 0x0008, lo: 0x93, hi: 0xab}, {value: 0x3008, lo: 0xac, hi: 0xae}, {value: 0x3308, lo: 0xaf, hi: 0xb1}, {value: 0x3008, lo: 0xb2, hi: 0xb3}, {value: 0x3308, lo: 0xb4, hi: 0xb4}, {value: 0x3808, lo: 0xb5, hi: 0xb5}, {value: 0x3308, lo: 0xb6, hi: 0xb7}, {value: 0x0018, lo: 0xb8, hi: 0xbd}, {value: 0x3308, lo: 0xbe, hi: 0xbe}, {value: 0x0040, lo: 0xbf, hi: 0xbf}, // Block 0xb6, offset 0x56d {value: 0x0000, lo: 0x0c}, {value: 0x0008, lo: 0x80, hi: 0x86}, {value: 0x0040, lo: 0x87, hi: 0x87}, {value: 0x0008, lo: 0x88, hi: 0x88}, {value: 0x0040, lo: 0x89, hi: 0x89}, {value: 0x0008, lo: 0x8a, hi: 0x8d}, {value: 0x0040, lo: 0x8e, hi: 0x8e}, {value: 0x0008, lo: 0x8f, hi: 0x9d}, {value: 0x0040, lo: 0x9e, hi: 0x9e}, {value: 0x0008, lo: 0x9f, hi: 0xa8}, {value: 0x0018, lo: 0xa9, hi: 0xa9}, {value: 0x0040, lo: 0xaa, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xbf}, // Block 0xb7, offset 0x57a {value: 0x0000, lo: 0x08}, {value: 0x0008, lo: 0x80, hi: 0x9e}, {value: 0x3308, lo: 0x9f, hi: 0x9f}, {value: 0x3008, lo: 0xa0, hi: 0xa2}, {value: 0x3308, lo: 0xa3, hi: 0xa9}, {value: 0x3b08, lo: 0xaa, hi: 0xaa}, {value: 0x0040, lo: 0xab, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xb9}, {value: 0x0040, lo: 0xba, hi: 0xbf}, // Block 0xb8, offset 0x583 {value: 0x0000, lo: 0x03}, {value: 0x0008, lo: 0x80, hi: 0xb4}, {value: 0x3008, lo: 0xb5, hi: 0xb7}, {value: 0x3308, lo: 0xb8, hi: 0xbf}, // Block 0xb9, offset 0x587 {value: 0x0000, lo: 0x0e}, {value: 0x3008, lo: 0x80, hi: 0x81}, {value: 0x3b08, lo: 0x82, hi: 0x82}, {value: 0x3308, lo: 0x83, hi: 0x84}, {value: 0x3008, lo: 0x85, hi: 0x85}, {value: 0x3308, lo: 0x86, hi: 0x86}, {value: 0x0008, lo: 0x87, hi: 0x8a}, {value: 0x0018, lo: 0x8b, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9a}, {value: 0x0018, lo: 0x9b, hi: 0x9b}, {value: 0x0040, lo: 0x9c, hi: 0x9c}, {value: 0x0018, lo: 0x9d, hi: 0x9d}, {value: 0x3308, lo: 0x9e, hi: 0x9e}, {value: 0x0040, lo: 0x9f, hi: 0xbf}, // Block 0xba, offset 0x596 {value: 0x0000, lo: 0x07}, {value: 0x0008, lo: 0x80, hi: 0xaf}, {value: 0x3008, lo: 0xb0, hi: 0xb2}, {value: 0x3308, lo: 0xb3, hi: 0xb8}, {value: 0x3008, lo: 0xb9, hi: 0xb9}, {value: 0x3308, lo: 0xba, hi: 0xba}, {value: 0x3008, lo: 0xbb, hi: 0xbe}, {value: 0x3308, lo: 0xbf, hi: 0xbf}, // Block 0xbb, offset 0x59e {value: 0x0000, lo: 0x0a}, {value: 0x3308, lo: 0x80, hi: 0x80}, {value: 0x3008, lo: 0x81, hi: 0x81}, {value: 0x3b08, lo: 0x82, hi: 0x82}, {value: 0x3308, lo: 0x83, hi: 0x83}, {value: 0x0008, lo: 0x84, hi: 0x85}, {value: 0x0018, lo: 0x86, hi: 0x86}, {value: 0x0008, lo: 0x87, hi: 0x87}, {value: 0x0040, lo: 0x88, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0xbf}, // Block 0xbc, offset 0x5a9 {value: 0x0000, lo: 0x08}, {value: 0x0008, lo: 0x80, hi: 0xae}, {value: 0x3008, lo: 0xaf, hi: 0xb1}, {value: 0x3308, lo: 0xb2, hi: 0xb5}, {value: 0x0040, lo: 0xb6, hi: 0xb7}, {value: 0x3008, lo: 0xb8, hi: 0xbb}, {value: 0x3308, lo: 0xbc, hi: 0xbd}, {value: 0x3008, lo: 0xbe, hi: 0xbe}, {value: 0x3b08, lo: 0xbf, hi: 0xbf}, // Block 0xbd, offset 0x5b2 {value: 0x0000, lo: 0x05}, {value: 0x3308, lo: 0x80, hi: 0x80}, {value: 0x0018, lo: 0x81, hi: 0x97}, {value: 0x0008, lo: 0x98, hi: 0x9b}, {value: 0x3308, lo: 0x9c, hi: 0x9d}, {value: 0x0040, lo: 0x9e, hi: 0xbf}, // Block 0xbe, offset 0x5b8 {value: 0x0000, lo: 0x07}, {value: 0x0008, lo: 0x80, hi: 0xaf}, {value: 0x3008, lo: 0xb0, hi: 0xb2}, {value: 0x3308, lo: 0xb3, hi: 0xba}, {value: 0x3008, lo: 0xbb, hi: 0xbc}, {value: 0x3308, lo: 0xbd, hi: 0xbd}, {value: 0x3008, lo: 0xbe, hi: 0xbe}, {value: 0x3b08, lo: 0xbf, hi: 0xbf}, // Block 0xbf, offset 0x5c0 {value: 0x0000, lo: 0x08}, {value: 0x3308, lo: 0x80, hi: 0x80}, {value: 0x0018, lo: 0x81, hi: 0x83}, {value: 0x0008, lo: 0x84, hi: 0x84}, {value: 0x0040, lo: 0x85, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xac}, {value: 0x0040, lo: 0xad, hi: 0xbf}, // Block 0xc0, offset 0x5c9 {value: 0x0000, lo: 0x09}, {value: 0x0008, lo: 0x80, hi: 0xaa}, {value: 0x3308, lo: 0xab, hi: 0xab}, {value: 0x3008, lo: 0xac, hi: 0xac}, {value: 0x3308, lo: 0xad, hi: 0xad}, {value: 0x3008, lo: 0xae, hi: 0xaf}, {value: 0x3308, lo: 0xb0, hi: 0xb5}, {value: 0x3808, lo: 0xb6, hi: 0xb6}, {value: 0x3308, lo: 0xb7, hi: 0xb7}, {value: 0x0040, lo: 0xb8, hi: 0xbf}, // Block 0xc1, offset 0x5d3 {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0x89}, {value: 0x0040, lo: 0x8a, hi: 0xbf}, // Block 0xc2, offset 0x5d6 {value: 0x0000, lo: 0x0b}, {value: 0x0008, lo: 0x80, hi: 0x9a}, {value: 0x0040, lo: 0x9b, hi: 0x9c}, {value: 0x3308, lo: 0x9d, hi: 0x9f}, {value: 0x3008, lo: 0xa0, hi: 0xa1}, {value: 0x3308, lo: 0xa2, hi: 0xa5}, {value: 0x3008, lo: 0xa6, hi: 0xa6}, {value: 0x3308, lo: 0xa7, hi: 0xaa}, {value: 0x3b08, lo: 0xab, hi: 0xab}, {value: 0x0040, lo: 0xac, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xb9}, {value: 0x0018, lo: 0xba, hi: 0xbf}, // Block 0xc3, offset 0x5e2 {value: 0x0000, lo: 0x08}, {value: 0x0008, lo: 0x80, hi: 0xab}, {value: 0x3008, lo: 0xac, hi: 0xae}, {value: 0x3308, lo: 0xaf, hi: 0xb7}, {value: 0x3008, lo: 0xb8, hi: 0xb8}, {value: 0x3b08, lo: 0xb9, hi: 0xb9}, {value: 0x3308, lo: 0xba, hi: 0xba}, {value: 0x0018, lo: 0xbb, hi: 0xbb}, {value: 0x0040, lo: 0xbc, hi: 0xbf}, // Block 0xc4, offset 0x5eb {value: 0x0000, lo: 0x02}, {value: 0x0040, lo: 0x80, hi: 0x9f}, {value: 0x049d, lo: 0xa0, hi: 0xbf}, // Block 0xc5, offset 0x5ee {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0xa9}, {value: 0x0018, lo: 0xaa, hi: 0xb2}, {value: 0x0040, lo: 0xb3, hi: 0xbe}, {value: 0x0008, lo: 0xbf, hi: 0xbf}, // Block 0xc6, offset 0x5f3 {value: 0x0000, lo: 0x0a}, {value: 0x0008, lo: 0x80, hi: 0x80}, {value: 0x3308, lo: 0x81, hi: 0x8a}, {value: 0x0008, lo: 0x8b, hi: 0xb2}, {value: 0x3308, lo: 0xb3, hi: 0xb3}, {value: 0x3b08, lo: 0xb4, hi: 0xb4}, {value: 0x3308, lo: 0xb5, hi: 0xb8}, {value: 0x3008, lo: 0xb9, hi: 0xb9}, {value: 0x0008, lo: 0xba, hi: 0xba}, {value: 0x3308, lo: 0xbb, hi: 0xbe}, {value: 0x0018, lo: 0xbf, hi: 0xbf}, // Block 0xc7, offset 0x5fe {value: 0x0000, lo: 0x08}, {value: 0x0018, lo: 0x80, hi: 0x86}, {value: 0x3b08, lo: 0x87, hi: 0x87}, {value: 0x0040, lo: 0x88, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x90}, {value: 0x3308, lo: 0x91, hi: 0x96}, {value: 0x3008, lo: 0x97, hi: 0x98}, {value: 0x3308, lo: 0x99, hi: 0x9b}, {value: 0x0008, lo: 0x9c, hi: 0xbf}, // Block 0xc8, offset 0x607 {value: 0x0000, lo: 0x0b}, {value: 0x0008, lo: 0x80, hi: 0x83}, {value: 0x0040, lo: 0x84, hi: 0x85}, {value: 0x0008, lo: 0x86, hi: 0x89}, {value: 0x3308, lo: 0x8a, hi: 0x96}, {value: 0x3008, lo: 0x97, hi: 0x97}, {value: 0x3308, lo: 0x98, hi: 0x98}, {value: 0x3b08, lo: 0x99, hi: 0x99}, {value: 0x0018, lo: 0x9a, hi: 0x9c}, {value: 0x0008, lo: 0x9d, hi: 0x9d}, {value: 0x0018, lo: 0x9e, hi: 0xa2}, {value: 0x0040, lo: 0xa3, hi: 0xbf}, // Block 0xc9, offset 0x613 {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0xb8}, {value: 0x0040, lo: 0xb9, hi: 0xbf}, // Block 0xca, offset 0x616 {value: 0x0000, lo: 0x09}, {value: 0x0008, lo: 0x80, hi: 0x88}, {value: 0x0040, lo: 0x89, hi: 0x89}, {value: 0x0008, lo: 0x8a, hi: 0xae}, {value: 0x3008, lo: 0xaf, hi: 0xaf}, {value: 0x3308, lo: 0xb0, hi: 0xb6}, {value: 0x0040, lo: 0xb7, hi: 0xb7}, {value: 0x3308, lo: 0xb8, hi: 0xbd}, {value: 0x3008, lo: 0xbe, hi: 0xbe}, {value: 0x3b08, lo: 0xbf, hi: 0xbf}, // Block 0xcb, offset 0x620 {value: 0x0000, lo: 0x08}, {value: 0x0008, lo: 0x80, hi: 0x80}, {value: 0x0018, lo: 0x81, hi: 0x85}, {value: 0x0040, lo: 0x86, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0018, lo: 0x9a, hi: 0xac}, {value: 0x0040, lo: 0xad, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xb1}, {value: 0x0008, lo: 0xb2, hi: 0xbf}, // Block 0xcc, offset 0x629 {value: 0x0000, lo: 0x0b}, {value: 0x0008, lo: 0x80, hi: 0x8f}, {value: 0x0040, lo: 0x90, hi: 0x91}, {value: 0x3308, lo: 0x92, hi: 0xa7}, {value: 0x0040, lo: 0xa8, hi: 0xa8}, {value: 0x3008, lo: 0xa9, hi: 0xa9}, {value: 0x3308, lo: 0xaa, hi: 0xb0}, {value: 0x3008, lo: 0xb1, hi: 0xb1}, {value: 0x3308, lo: 0xb2, hi: 0xb3}, {value: 0x3008, lo: 0xb4, hi: 0xb4}, {value: 0x3308, lo: 0xb5, hi: 0xb6}, {value: 0x0040, lo: 0xb7, hi: 0xbf}, // Block 0xcd, offset 0x635 {value: 0x0000, lo: 0x0c}, {value: 0x0008, lo: 0x80, hi: 0x86}, {value: 0x0040, lo: 0x87, hi: 0x87}, {value: 0x0008, lo: 0x88, hi: 0x89}, {value: 0x0040, lo: 0x8a, hi: 0x8a}, {value: 0x0008, lo: 0x8b, hi: 0xb0}, {value: 0x3308, lo: 0xb1, hi: 0xb6}, {value: 0x0040, lo: 0xb7, hi: 0xb9}, {value: 0x3308, lo: 0xba, hi: 0xba}, {value: 0x0040, lo: 0xbb, hi: 0xbb}, {value: 0x3308, lo: 0xbc, hi: 0xbd}, {value: 0x0040, lo: 0xbe, hi: 0xbe}, {value: 0x3308, lo: 0xbf, hi: 0xbf}, // Block 0xce, offset 0x642 {value: 0x0000, lo: 0x0c}, {value: 0x3308, lo: 0x80, hi: 0x83}, {value: 0x3b08, lo: 0x84, hi: 0x85}, {value: 0x0008, lo: 0x86, hi: 0x86}, {value: 0x3308, lo: 0x87, hi: 0x87}, {value: 0x0040, lo: 0x88, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa5}, {value: 0x0040, lo: 0xa6, hi: 0xa6}, {value: 0x0008, lo: 0xa7, hi: 0xa8}, {value: 0x0040, lo: 0xa9, hi: 0xa9}, {value: 0x0008, lo: 0xaa, hi: 0xbf}, // Block 0xcf, offset 0x64f {value: 0x0000, lo: 0x0d}, {value: 0x0008, lo: 0x80, hi: 0x89}, {value: 0x3008, lo: 0x8a, hi: 0x8e}, {value: 0x0040, lo: 0x8f, hi: 0x8f}, {value: 0x3308, lo: 0x90, hi: 0x91}, {value: 0x0040, lo: 0x92, hi: 0x92}, {value: 0x3008, lo: 0x93, hi: 0x94}, {value: 0x3308, lo: 0x95, hi: 0x95}, {value: 0x3008, lo: 0x96, hi: 0x96}, {value: 0x3b08, lo: 0x97, hi: 0x97}, {value: 0x0008, lo: 0x98, hi: 0x98}, {value: 0x0040, lo: 0x99, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa9}, {value: 0x0040, lo: 0xaa, hi: 0xbf}, // Block 0xd0, offset 0x65d {value: 0x0000, lo: 0x06}, {value: 0x0040, lo: 0x80, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xb2}, {value: 0x3308, lo: 0xb3, hi: 0xb4}, {value: 0x3008, lo: 0xb5, hi: 0xb6}, {value: 0x0018, lo: 0xb7, hi: 0xb8}, {value: 0x0040, lo: 0xb9, hi: 0xbf}, // Block 0xd1, offset 0x664 {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0xbf}, // Block 0xd2, offset 0x667 {value: 0x0000, lo: 0x04}, {value: 0x0018, lo: 0x80, hi: 0xae}, {value: 0x0040, lo: 0xaf, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xb4}, {value: 0x0040, lo: 0xb5, hi: 0xbf}, // Block 0xd3, offset 0x66c {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0x83}, {value: 0x0040, lo: 0x84, hi: 0xbf}, // Block 0xd4, offset 0x66f {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0xae}, {value: 0x0040, lo: 0xaf, hi: 0xbf}, // Block 0xd5, offset 0x672 {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0x86}, {value: 0x0040, lo: 0x87, hi: 0xbf}, // Block 0xd6, offset 0x675 {value: 0x0000, lo: 0x06}, {value: 0x0008, lo: 0x80, hi: 0x9e}, {value: 0x0040, lo: 0x9f, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa9}, {value: 0x0040, lo: 0xaa, hi: 0xad}, {value: 0x0018, lo: 0xae, hi: 0xaf}, {value: 0x0040, lo: 0xb0, hi: 0xbf}, // Block 0xd7, offset 0x67c {value: 0x0000, lo: 0x06}, {value: 0x0040, lo: 0x80, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0xad}, {value: 0x0040, lo: 0xae, hi: 0xaf}, {value: 0x3308, lo: 0xb0, hi: 0xb4}, {value: 0x0018, lo: 0xb5, hi: 0xb5}, {value: 0x0040, lo: 0xb6, hi: 0xbf}, // Block 0xd8, offset 0x683 {value: 0x0000, lo: 0x03}, {value: 0x0008, lo: 0x80, hi: 0xaf}, {value: 0x3308, lo: 0xb0, hi: 0xb6}, {value: 0x0018, lo: 0xb7, hi: 0xbf}, // Block 0xd9, offset 0x687 {value: 0x0000, lo: 0x0a}, {value: 0x0008, lo: 0x80, hi: 0x83}, {value: 0x0018, lo: 0x84, hi: 0x85}, {value: 0x0040, lo: 0x86, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9a}, {value: 0x0018, lo: 0x9b, hi: 0xa1}, {value: 0x0040, lo: 0xa2, hi: 0xa2}, {value: 0x0008, lo: 0xa3, hi: 0xb7}, {value: 0x0040, lo: 0xb8, hi: 0xbc}, {value: 0x0008, lo: 0xbd, hi: 0xbf}, // Block 0xda, offset 0x692 {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0x8f}, {value: 0x0040, lo: 0x90, hi: 0xbf}, // Block 0xdb, offset 0x695 {value: 0x0000, lo: 0x02}, {value: 0x0040, lo: 0x80, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xbf}, // Block 0xdc, offset 0x698 {value: 0x0000, lo: 0x02}, {value: 0x0018, lo: 0x80, hi: 0x9a}, {value: 0x0040, lo: 0x9b, hi: 0xbf}, // Block 0xdd, offset 0x69b {value: 0x0000, lo: 0x05}, {value: 0x0008, lo: 0x80, hi: 0x84}, {value: 0x0040, lo: 0x85, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x90}, {value: 0x3008, lo: 0x91, hi: 0xbe}, {value: 0x0040, lo: 0xbf, hi: 0xbf}, // Block 0xde, offset 0x6a1 {value: 0x0000, lo: 0x04}, {value: 0x0040, lo: 0x80, hi: 0x8e}, {value: 0x3308, lo: 0x8f, hi: 0x92}, {value: 0x0008, lo: 0x93, hi: 0x9f}, {value: 0x0040, lo: 0xa0, hi: 0xbf}, // Block 0xdf, offset 0x6a6 {value: 0x0000, lo: 0x03}, {value: 0x0040, lo: 0x80, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa1}, {value: 0x0040, lo: 0xa2, hi: 0xbf}, // Block 0xe0, offset 0x6aa {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0xb1}, {value: 0x0040, lo: 0xb2, hi: 0xbf}, // Block 0xe1, offset 0x6ad {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0xb2}, {value: 0x0040, lo: 0xb3, hi: 0xbf}, // Block 0xe2, offset 0x6b0 {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0x9e}, {value: 0x0040, lo: 0x9f, hi: 0xbf}, // Block 0xe3, offset 0x6b3 {value: 0x0000, lo: 0x02}, {value: 0x0040, lo: 0x80, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xbf}, // Block 0xe4, offset 0x6b6 {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0xbb}, {value: 0x0040, lo: 0xbc, hi: 0xbf}, // Block 0xe5, offset 0x6b9 {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0xaa}, {value: 0x0040, lo: 0xab, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xbc}, {value: 0x0040, lo: 0xbd, hi: 0xbf}, // Block 0xe6, offset 0x6be {value: 0x0000, lo: 0x09}, {value: 0x0008, lo: 0x80, hi: 0x88}, {value: 0x0040, lo: 0x89, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9b}, {value: 0x0018, lo: 0x9c, hi: 0x9c}, {value: 0x3308, lo: 0x9d, hi: 0x9e}, {value: 0x0018, lo: 0x9f, hi: 0x9f}, {value: 0x03c0, lo: 0xa0, hi: 0xa3}, {value: 0x0040, lo: 0xa4, hi: 0xbf}, // Block 0xe7, offset 0x6c8 {value: 0x0000, lo: 0x02}, {value: 0x0018, lo: 0x80, hi: 0xb5}, {value: 0x0040, lo: 0xb6, hi: 0xbf}, // Block 0xe8, offset 0x6cb {value: 0x0000, lo: 0x03}, {value: 0x0018, lo: 0x80, hi: 0xa6}, {value: 0x0040, lo: 0xa7, hi: 0xa8}, {value: 0x0018, lo: 0xa9, hi: 0xbf}, // Block 0xe9, offset 0x6cf {value: 0x0000, lo: 0x0e}, {value: 0x0018, lo: 0x80, hi: 0x9d}, {value: 0xb5b9, lo: 0x9e, hi: 0x9e}, {value: 0xb601, lo: 0x9f, hi: 0x9f}, {value: 0xb649, lo: 0xa0, hi: 0xa0}, {value: 0xb6b1, lo: 0xa1, hi: 0xa1}, {value: 0xb719, lo: 0xa2, hi: 0xa2}, {value: 0xb781, lo: 0xa3, hi: 0xa3}, {value: 0xb7e9, lo: 0xa4, hi: 0xa4}, {value: 0x3018, lo: 0xa5, hi: 0xa6}, {value: 0x3318, lo: 0xa7, hi: 0xa9}, {value: 0x0018, lo: 0xaa, hi: 0xac}, {value: 0x3018, lo: 0xad, hi: 0xb2}, {value: 0x0340, lo: 0xb3, hi: 0xba}, {value: 0x3318, lo: 0xbb, hi: 0xbf}, // Block 0xea, offset 0x6de {value: 0x0000, lo: 0x0b}, {value: 0x3318, lo: 0x80, hi: 0x82}, {value: 0x0018, lo: 0x83, hi: 0x84}, {value: 0x3318, lo: 0x85, hi: 0x8b}, {value: 0x0018, lo: 0x8c, hi: 0xa9}, {value: 0x3318, lo: 0xaa, hi: 0xad}, {value: 0x0018, lo: 0xae, hi: 0xba}, {value: 0xb851, lo: 0xbb, hi: 0xbb}, {value: 0xb899, lo: 0xbc, hi: 0xbc}, {value: 0xb8e1, lo: 0xbd, hi: 0xbd}, {value: 0xb949, lo: 0xbe, hi: 0xbe}, {value: 0xb9b1, lo: 0xbf, hi: 0xbf}, // Block 0xeb, offset 0x6ea {value: 0x0000, lo: 0x03}, {value: 0xba19, lo: 0x80, hi: 0x80}, {value: 0x0018, lo: 0x81, hi: 0xa8}, {value: 0x0040, lo: 0xa9, hi: 0xbf}, // Block 0xec, offset 0x6ee {value: 0x0000, lo: 0x04}, {value: 0x0018, lo: 0x80, hi: 0x81}, {value: 0x3318, lo: 0x82, hi: 0x84}, {value: 0x0018, lo: 0x85, hi: 0x85}, {value: 0x0040, lo: 0x86, hi: 0xbf}, // Block 0xed, offset 0x6f3 {value: 0x0000, lo: 0x03}, {value: 0x0040, lo: 0x80, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xb3}, {value: 0x0040, lo: 0xb4, hi: 0xbf}, // Block 0xee, offset 0x6f7 {value: 0x0000, lo: 0x04}, {value: 0x0018, lo: 0x80, hi: 0x96}, {value: 0x0040, lo: 0x97, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xb8}, {value: 0x0040, lo: 0xb9, hi: 0xbf}, // Block 0xef, offset 0x6fc {value: 0x0000, lo: 0x03}, {value: 0x3308, lo: 0x80, hi: 0xb6}, {value: 0x0018, lo: 0xb7, hi: 0xba}, {value: 0x3308, lo: 0xbb, hi: 0xbf}, // Block 0xf0, offset 0x700 {value: 0x0000, lo: 0x04}, {value: 0x3308, lo: 0x80, hi: 0xac}, {value: 0x0018, lo: 0xad, hi: 0xb4}, {value: 0x3308, lo: 0xb5, hi: 0xb5}, {value: 0x0018, lo: 0xb6, hi: 0xbf}, // Block 0xf1, offset 0x705 {value: 0x0000, lo: 0x08}, {value: 0x0018, lo: 0x80, hi: 0x83}, {value: 0x3308, lo: 0x84, hi: 0x84}, {value: 0x0018, lo: 0x85, hi: 0x8b}, {value: 0x0040, lo: 0x8c, hi: 0x9a}, {value: 0x3308, lo: 0x9b, hi: 0x9f}, {value: 0x0040, lo: 0xa0, hi: 0xa0}, {value: 0x3308, lo: 0xa1, hi: 0xaf}, {value: 0x0040, lo: 0xb0, hi: 0xbf}, // Block 0xf2, offset 0x70e {value: 0x0000, lo: 0x0a}, {value: 0x3308, lo: 0x80, hi: 0x86}, {value: 0x0040, lo: 0x87, hi: 0x87}, {value: 0x3308, lo: 0x88, hi: 0x98}, {value: 0x0040, lo: 0x99, hi: 0x9a}, {value: 0x3308, lo: 0x9b, hi: 0xa1}, {value: 0x0040, lo: 0xa2, hi: 0xa2}, {value: 0x3308, lo: 0xa3, hi: 0xa4}, {value: 0x0040, lo: 0xa5, hi: 0xa5}, {value: 0x3308, lo: 0xa6, hi: 0xaa}, {value: 0x0040, lo: 0xab, hi: 0xbf}, // Block 0xf3, offset 0x719 {value: 0x0000, lo: 0x05}, {value: 0x0808, lo: 0x80, hi: 0x84}, {value: 0x0040, lo: 0x85, hi: 0x86}, {value: 0x0818, lo: 0x87, hi: 0x8f}, {value: 0x3308, lo: 0x90, hi: 0x96}, {value: 0x0040, lo: 0x97, hi: 0xbf}, // Block 0xf4, offset 0x71f {value: 0x0000, lo: 0x07}, {value: 0x0a08, lo: 0x80, hi: 0x83}, {value: 0x3308, lo: 0x84, hi: 0x8a}, {value: 0x0040, lo: 0x8b, hi: 0x8f}, {value: 0x0808, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9d}, {value: 0x0818, lo: 0x9e, hi: 0x9f}, {value: 0x0040, lo: 0xa0, hi: 0xbf}, // Block 0xf5, offset 0x727 {value: 0x0000, lo: 0x02}, {value: 0x0040, lo: 0x80, hi: 0xb0}, {value: 0x0818, lo: 0xb1, hi: 0xbf}, // Block 0xf6, offset 0x72a {value: 0x0000, lo: 0x02}, {value: 0x0818, lo: 0x80, hi: 0xb4}, {value: 0x0040, lo: 0xb5, hi: 0xbf}, // Block 0xf7, offset 0x72d {value: 0x0000, lo: 0x03}, {value: 0x0040, lo: 0x80, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xb1}, {value: 0x0040, lo: 0xb2, hi: 0xbf}, // Block 0xf8, offset 0x731 {value: 0x0000, lo: 0x03}, {value: 0x0018, lo: 0x80, hi: 0xab}, {value: 0x0040, lo: 0xac, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xbf}, // Block 0xf9, offset 0x735 {value: 0x0000, lo: 0x05}, {value: 0x0018, lo: 0x80, hi: 0x93}, {value: 0x0040, lo: 0x94, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xae}, {value: 0x0040, lo: 0xaf, hi: 0xb0}, {value: 0x0018, lo: 0xb1, hi: 0xbf}, // Block 0xfa, offset 0x73b {value: 0x0000, lo: 0x05}, {value: 0x0040, lo: 0x80, hi: 0x80}, {value: 0x0018, lo: 0x81, hi: 0x8f}, {value: 0x0040, lo: 0x90, hi: 0x90}, {value: 0x0018, lo: 0x91, hi: 0xb5}, {value: 0x0040, lo: 0xb6, hi: 0xbf}, // Block 0xfb, offset 0x741 {value: 0x0000, lo: 0x04}, {value: 0x0018, lo: 0x80, hi: 0x8f}, {value: 0xc1c1, lo: 0x90, hi: 0x90}, {value: 0x0018, lo: 0x91, hi: 0xac}, {value: 0x0040, lo: 0xad, hi: 0xbf}, // Block 0xfc, offset 0x746 {value: 0x0000, lo: 0x02}, {value: 0x0040, lo: 0x80, hi: 0xa5}, {value: 0x0018, lo: 0xa6, hi: 0xbf}, // Block 0xfd, offset 0x749 {value: 0x0000, lo: 0x0f}, {value: 0xc7e9, lo: 0x80, hi: 0x80}, {value: 0xc839, lo: 0x81, hi: 0x81}, {value: 0xc889, lo: 0x82, hi: 0x82}, {value: 0xc8d9, lo: 0x83, hi: 0x83}, {value: 0xc929, lo: 0x84, hi: 0x84}, {value: 0xc979, lo: 0x85, hi: 0x85}, {value: 0xc9c9, lo: 0x86, hi: 0x86}, {value: 0xca19, lo: 0x87, hi: 0x87}, {value: 0xca69, lo: 0x88, hi: 0x88}, {value: 0x0040, lo: 0x89, hi: 0x8f}, {value: 0xcab9, lo: 0x90, hi: 0x90}, {value: 0xcad9, lo: 0x91, hi: 0x91}, {value: 0x0040, lo: 0x92, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xa5}, {value: 0x0040, lo: 0xa6, hi: 0xbf}, // Block 0xfe, offset 0x759 {value: 0x0000, lo: 0x06}, {value: 0x0018, lo: 0x80, hi: 0x94}, {value: 0x0040, lo: 0x95, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xac}, {value: 0x0040, lo: 0xad, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xb9}, {value: 0x0040, lo: 0xba, hi: 0xbf}, // Block 0xff, offset 0x760 {value: 0x0000, lo: 0x02}, {value: 0x0018, lo: 0x80, hi: 0xb3}, {value: 0x0040, lo: 0xb4, hi: 0xbf}, // Block 0x100, offset 0x763 {value: 0x0000, lo: 0x02}, {value: 0x0018, lo: 0x80, hi: 0x98}, {value: 0x0040, lo: 0x99, hi: 0xbf}, // Block 0x101, offset 0x766 {value: 0x0000, lo: 0x03}, {value: 0x0018, lo: 0x80, hi: 0x8b}, {value: 0x0040, lo: 0x8c, hi: 0x8f}, {value: 0x0018, lo: 0x90, hi: 0xbf}, // Block 0x102, offset 0x76a {value: 0x0000, lo: 0x05}, {value: 0x0018, lo: 0x80, hi: 0x87}, {value: 0x0040, lo: 0x88, hi: 0x8f}, {value: 0x0018, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xbf}, // Block 0x103, offset 0x770 {value: 0x0000, lo: 0x04}, {value: 0x0018, lo: 0x80, hi: 0x87}, {value: 0x0040, lo: 0x88, hi: 0x8f}, {value: 0x0018, lo: 0x90, hi: 0xad}, {value: 0x0040, lo: 0xae, hi: 0xbf}, // Block 0x104, offset 0x775 {value: 0x0000, lo: 0x04}, {value: 0x0018, lo: 0x80, hi: 0x8b}, {value: 0x0040, lo: 0x8c, hi: 0x8f}, {value: 0x0018, lo: 0x90, hi: 0xbe}, {value: 0x0040, lo: 0xbf, hi: 0xbf}, // Block 0x105, offset 0x77a {value: 0x0000, lo: 0x07}, {value: 0x0018, lo: 0x80, hi: 0xb0}, {value: 0x0040, lo: 0xb1, hi: 0xb2}, {value: 0x0018, lo: 0xb3, hi: 0xb6}, {value: 0x0040, lo: 0xb7, hi: 0xb9}, {value: 0x0018, lo: 0xba, hi: 0xba}, {value: 0x0040, lo: 0xbb, hi: 0xbb}, {value: 0x0018, lo: 0xbc, hi: 0xbf}, // Block 0x106, offset 0x782 {value: 0x0000, lo: 0x04}, {value: 0x0018, lo: 0x80, hi: 0xa2}, {value: 0x0040, lo: 0xa3, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xb9}, {value: 0x0040, lo: 0xba, hi: 0xbf}, // Block 0x107, offset 0x787 {value: 0x0000, lo: 0x03}, {value: 0x0018, lo: 0x80, hi: 0x82}, {value: 0x0040, lo: 0x83, hi: 0x8f}, {value: 0x0018, lo: 0x90, hi: 0xbf}, // Block 0x108, offset 0x78b {value: 0x0000, lo: 0x03}, {value: 0x0040, lo: 0x80, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xad}, {value: 0x0040, lo: 0xae, hi: 0xbf}, // Block 0x109, offset 0x78f {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0x96}, {value: 0x0040, lo: 0x97, hi: 0xbf}, // Block 0x10a, offset 0x792 {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0xb4}, {value: 0x0040, lo: 0xb5, hi: 0xbf}, // Block 0x10b, offset 0x795 {value: 0x0000, lo: 0x03}, {value: 0x0008, lo: 0x80, hi: 0x9d}, {value: 0x0040, lo: 0x9e, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xbf}, // Block 0x10c, offset 0x799 {value: 0x0000, lo: 0x03}, {value: 0x0008, lo: 0x80, hi: 0xa1}, {value: 0x0040, lo: 0xa2, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xbf}, // Block 0x10d, offset 0x79d {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0xa0}, {value: 0x0040, lo: 0xa1, hi: 0xbf}, // Block 0x10e, offset 0x7a0 {value: 0x0020, lo: 0x0f}, {value: 0xdeb9, lo: 0x80, hi: 0x89}, {value: 0x8dfd, lo: 0x8a, hi: 0x8a}, {value: 0xdff9, lo: 0x8b, hi: 0x9c}, {value: 0x8e1d, lo: 0x9d, hi: 0x9d}, {value: 0xe239, lo: 0x9e, hi: 0xa2}, {value: 0x8e3d, lo: 0xa3, hi: 0xa3}, {value: 0xe2d9, lo: 0xa4, hi: 0xab}, {value: 0x7ed5, lo: 0xac, hi: 0xac}, {value: 0xe3d9, lo: 0xad, hi: 0xaf}, {value: 0x8e5d, lo: 0xb0, hi: 0xb0}, {value: 0xe439, lo: 0xb1, hi: 0xb6}, {value: 0x8e7d, lo: 0xb7, hi: 0xb9}, {value: 0xe4f9, lo: 0xba, hi: 0xba}, {value: 0x8edd, lo: 0xbb, hi: 0xbb}, {value: 0xe519, lo: 0xbc, hi: 0xbf}, // Block 0x10f, offset 0x7b0 {value: 0x0020, lo: 0x10}, {value: 0x937d, lo: 0x80, hi: 0x80}, {value: 0xf099, lo: 0x81, hi: 0x86}, {value: 0x939d, lo: 0x87, hi: 0x8a}, {value: 0xd9f9, lo: 0x8b, hi: 0x8b}, {value: 0xf159, lo: 0x8c, hi: 0x96}, {value: 0x941d, lo: 0x97, hi: 0x97}, {value: 0xf2b9, lo: 0x98, hi: 0xa3}, {value: 0x943d, lo: 0xa4, hi: 0xa6}, {value: 0xf439, lo: 0xa7, hi: 0xaa}, {value: 0x949d, lo: 0xab, hi: 0xab}, {value: 0xf4b9, lo: 0xac, hi: 0xac}, {value: 0x94bd, lo: 0xad, hi: 0xad}, {value: 0xf4d9, lo: 0xae, hi: 0xaf}, {value: 0x94dd, lo: 0xb0, hi: 0xb1}, {value: 0xf519, lo: 0xb2, hi: 0xbe}, {value: 0x2040, lo: 0xbf, hi: 0xbf}, // Block 0x110, offset 0x7c1 {value: 0x0000, lo: 0x04}, {value: 0x0040, lo: 0x80, hi: 0x80}, {value: 0x0340, lo: 0x81, hi: 0x81}, {value: 0x0040, lo: 0x82, hi: 0x9f}, {value: 0x0340, lo: 0xa0, hi: 0xbf}, // Block 0x111, offset 0x7c6 {value: 0x0000, lo: 0x01}, {value: 0x0340, lo: 0x80, hi: 0xbf}, // Block 0x112, offset 0x7c8 {value: 0x0000, lo: 0x01}, {value: 0x33c0, lo: 0x80, hi: 0xbf}, // Block 0x113, offset 0x7ca {value: 0x0000, lo: 0x02}, {value: 0x33c0, lo: 0x80, hi: 0xaf}, {value: 0x0040, lo: 0xb0, hi: 0xbf}, } // Total table size 42466 bytes (41KiB); checksum: 355A58A4 ================================================ FILE: vendor/golang.org/x/net/idna/tables12.0.0.go ================================================ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.14 && !go1.16 package idna // UnicodeVersion is the Unicode version from which the tables in this package are derived. const UnicodeVersion = "12.0.0" var mappings string = "" + // Size: 8178 bytes "\x00\x01 \x03 ̈\x01a\x03 ̄\x012\x013\x03 ́\x03 ̧\x011\x01o\x051⁄4\x051⁄2" + "\x053⁄4\x03i̇\x03l·\x03ʼn\x01s\x03dž\x03ⱥ\x03ⱦ\x01h\x01j\x01r\x01w\x01y" + "\x03 ̆\x03 ̇\x03 ̊\x03 ̨\x03 ̃\x03 ̋\x01l\x01x\x04̈́\x03 ι\x01;\x05 ̈́" + "\x04եւ\x04اٴ\x04وٴ\x04ۇٴ\x04يٴ\x06क़\x06ख़\x06ग़\x06ज़\x06ड़\x06ढ़\x06फ़" + "\x06य़\x06ড়\x06ঢ়\x06য়\x06ਲ਼\x06ਸ਼\x06ਖ਼\x06ਗ਼\x06ਜ਼\x06ਫ਼\x06ଡ଼\x06ଢ଼" + "\x06ํา\x06ໍາ\x06ຫນ\x06ຫມ\x06གྷ\x06ཌྷ\x06དྷ\x06བྷ\x06ཛྷ\x06ཀྵ\x06ཱི\x06ཱུ" + "\x06ྲྀ\x09ྲཱྀ\x06ླྀ\x09ླཱྀ\x06ཱྀ\x06ྒྷ\x06ྜྷ\x06ྡྷ\x06ྦྷ\x06ྫྷ\x06ྐྵ\x02" + "в\x02д\x02о\x02с\x02т\x02ъ\x02ѣ\x02æ\x01b\x01d\x01e\x02ǝ\x01g\x01i\x01k" + "\x01m\x01n\x02ȣ\x01p\x01t\x01u\x02ɐ\x02ɑ\x02ə\x02ɛ\x02ɜ\x02ŋ\x02ɔ\x02ɯ" + "\x01v\x02β\x02γ\x02δ\x02φ\x02χ\x02ρ\x02н\x02ɒ\x01c\x02ɕ\x02ð\x01f\x02ɟ" + "\x02ɡ\x02ɥ\x02ɨ\x02ɩ\x02ɪ\x02ʝ\x02ɭ\x02ʟ\x02ɱ\x02ɰ\x02ɲ\x02ɳ\x02ɴ\x02ɵ" + "\x02ɸ\x02ʂ\x02ʃ\x02ƫ\x02ʉ\x02ʊ\x02ʋ\x02ʌ\x01z\x02ʐ\x02ʑ\x02ʒ\x02θ\x02ss" + "\x02ά\x02έ\x02ή\x02ί\x02ό\x02ύ\x02ώ\x05ἀι\x05ἁι\x05ἂι\x05ἃι\x05ἄι\x05ἅι" + "\x05ἆι\x05ἇι\x05ἠι\x05ἡι\x05ἢι\x05ἣι\x05ἤι\x05ἥι\x05ἦι\x05ἧι\x05ὠι\x05ὡι" + "\x05ὢι\x05ὣι\x05ὤι\x05ὥι\x05ὦι\x05ὧι\x05ὰι\x04αι\x04άι\x05ᾶι\x02ι\x05 ̈͂" + "\x05ὴι\x04ηι\x04ήι\x05ῆι\x05 ̓̀\x05 ̓́\x05 ̓͂\x02ΐ\x05 ̔̀\x05 ̔́\x05 ̔͂" + "\x02ΰ\x05 ̈̀\x01`\x05ὼι\x04ωι\x04ώι\x05ῶι\x06′′\x09′′′\x06‵‵\x09‵‵‵\x02!" + "!\x02??\x02?!\x02!?\x0c′′′′\x010\x014\x015\x016\x017\x018\x019\x01+\x01=" + "\x01(\x01)\x02rs\x02ħ\x02no\x01q\x02sm\x02tm\x02ω\x02å\x02א\x02ב\x02ג" + "\x02ד\x02π\x051⁄7\x051⁄9\x061⁄10\x051⁄3\x052⁄3\x051⁄5\x052⁄5\x053⁄5\x054" + "⁄5\x051⁄6\x055⁄6\x051⁄8\x053⁄8\x055⁄8\x057⁄8\x041⁄\x02ii\x02iv\x02vi" + "\x04viii\x02ix\x02xi\x050⁄3\x06∫∫\x09∫∫∫\x06∮∮\x09∮∮∮\x0210\x0211\x0212" + "\x0213\x0214\x0215\x0216\x0217\x0218\x0219\x0220\x04(10)\x04(11)\x04(12)" + "\x04(13)\x04(14)\x04(15)\x04(16)\x04(17)\x04(18)\x04(19)\x04(20)\x0c∫∫∫∫" + "\x02==\x05⫝̸\x02ɫ\x02ɽ\x02ȿ\x02ɀ\x01.\x04 ゙\x04 ゚\x06より\x06コト\x05(ᄀ)\x05" + "(ᄂ)\x05(ᄃ)\x05(ᄅ)\x05(ᄆ)\x05(ᄇ)\x05(ᄉ)\x05(ᄋ)\x05(ᄌ)\x05(ᄎ)\x05(ᄏ)\x05(ᄐ" + ")\x05(ᄑ)\x05(ᄒ)\x05(가)\x05(나)\x05(다)\x05(라)\x05(마)\x05(바)\x05(사)\x05(아)" + "\x05(자)\x05(차)\x05(카)\x05(타)\x05(파)\x05(하)\x05(주)\x08(오전)\x08(오후)\x05(一)" + "\x05(二)\x05(三)\x05(四)\x05(五)\x05(六)\x05(七)\x05(八)\x05(九)\x05(十)\x05(月)" + "\x05(火)\x05(水)\x05(木)\x05(金)\x05(土)\x05(日)\x05(株)\x05(有)\x05(社)\x05(名)" + "\x05(特)\x05(財)\x05(祝)\x05(労)\x05(代)\x05(呼)\x05(学)\x05(監)\x05(企)\x05(資)" + "\x05(協)\x05(祭)\x05(休)\x05(自)\x05(至)\x0221\x0222\x0223\x0224\x0225\x0226" + "\x0227\x0228\x0229\x0230\x0231\x0232\x0233\x0234\x0235\x06참고\x06주의\x0236" + "\x0237\x0238\x0239\x0240\x0241\x0242\x0243\x0244\x0245\x0246\x0247\x0248" + "\x0249\x0250\x041月\x042月\x043月\x044月\x045月\x046月\x047月\x048月\x049月\x0510" + "月\x0511月\x0512月\x02hg\x02ev\x0cアパート\x0cアルファ\x0cアンペア\x09アール\x0cイニング\x09" + "インチ\x09ウォン\x0fエスクード\x0cエーカー\x09オンス\x09オーム\x09カイリ\x0cカラット\x0cカロリー\x09ガロ" + "ン\x09ガンマ\x06ギガ\x09ギニー\x0cキュリー\x0cギルダー\x06キロ\x0fキログラム\x12キロメートル\x0fキロワッ" + "ト\x09グラム\x0fグラムトン\x0fクルゼイロ\x0cクローネ\x09ケース\x09コルナ\x09コーポ\x0cサイクル\x0fサンチ" + "ーム\x0cシリング\x09センチ\x09セント\x09ダース\x06デシ\x06ドル\x06トン\x06ナノ\x09ノット\x09ハイツ" + "\x0fパーセント\x09パーツ\x0cバーレル\x0fピアストル\x09ピクル\x06ピコ\x06ビル\x0fファラッド\x0cフィート" + "\x0fブッシェル\x09フラン\x0fヘクタール\x06ペソ\x09ペニヒ\x09ヘルツ\x09ペンス\x09ページ\x09ベータ\x0cポイ" + "ント\x09ボルト\x06ホン\x09ポンド\x09ホール\x09ホーン\x0cマイクロ\x09マイル\x09マッハ\x09マルク\x0fマ" + "ンション\x0cミクロン\x06ミリ\x0fミリバール\x06メガ\x0cメガトン\x0cメートル\x09ヤード\x09ヤール\x09ユアン" + "\x0cリットル\x06リラ\x09ルピー\x0cルーブル\x06レム\x0fレントゲン\x09ワット\x040点\x041点\x042点" + "\x043点\x044点\x045点\x046点\x047点\x048点\x049点\x0510点\x0511点\x0512点\x0513点" + "\x0514点\x0515点\x0516点\x0517点\x0518点\x0519点\x0520点\x0521点\x0522点\x0523点" + "\x0524点\x02da\x02au\x02ov\x02pc\x02dm\x02iu\x06平成\x06昭和\x06大正\x06明治\x0c株" + "式会社\x02pa\x02na\x02ma\x02ka\x02kb\x02mb\x02gb\x04kcal\x02pf\x02nf\x02m" + "g\x02kg\x02hz\x02ml\x02dl\x02kl\x02fm\x02nm\x02mm\x02cm\x02km\x02m2\x02m" + "3\x05m∕s\x06m∕s2\x07rad∕s\x08rad∕s2\x02ps\x02ns\x02ms\x02pv\x02nv\x02mv" + "\x02kv\x02pw\x02nw\x02mw\x02kw\x02bq\x02cc\x02cd\x06c∕kg\x02db\x02gy\x02" + "ha\x02hp\x02in\x02kk\x02kt\x02lm\x02ln\x02lx\x02ph\x02pr\x02sr\x02sv\x02" + "wb\x05v∕m\x05a∕m\x041日\x042日\x043日\x044日\x045日\x046日\x047日\x048日\x049日" + "\x0510日\x0511日\x0512日\x0513日\x0514日\x0515日\x0516日\x0517日\x0518日\x0519日" + "\x0520日\x0521日\x0522日\x0523日\x0524日\x0525日\x0526日\x0527日\x0528日\x0529日" + "\x0530日\x0531日\x02ь\x02ɦ\x02ɬ\x02ʞ\x02ʇ\x02œ\x04𤋮\x04𢡊\x04𢡄\x04𣏕\x04𥉉" + "\x04𥳐\x04𧻓\x02ff\x02fi\x02fl\x02st\x04մն\x04մե\x04մի\x04վն\x04մխ\x04יִ" + "\x04ײַ\x02ע\x02ה\x02כ\x02ל\x02ם\x02ר\x02ת\x04שׁ\x04שׂ\x06שּׁ\x06שּׂ\x04א" + "ַ\x04אָ\x04אּ\x04בּ\x04גּ\x04דּ\x04הּ\x04וּ\x04זּ\x04טּ\x04יּ\x04ךּ\x04" + "כּ\x04לּ\x04מּ\x04נּ\x04סּ\x04ףּ\x04פּ\x04צּ\x04קּ\x04רּ\x04שּ\x04תּ" + "\x04וֹ\x04בֿ\x04כֿ\x04פֿ\x04אל\x02ٱ\x02ٻ\x02پ\x02ڀ\x02ٺ\x02ٿ\x02ٹ\x02ڤ" + "\x02ڦ\x02ڄ\x02ڃ\x02چ\x02ڇ\x02ڍ\x02ڌ\x02ڎ\x02ڈ\x02ژ\x02ڑ\x02ک\x02گ\x02ڳ" + "\x02ڱ\x02ں\x02ڻ\x02ۀ\x02ہ\x02ھ\x02ے\x02ۓ\x02ڭ\x02ۇ\x02ۆ\x02ۈ\x02ۋ\x02ۅ" + "\x02ۉ\x02ې\x02ى\x04ئا\x04ئە\x04ئو\x04ئۇ\x04ئۆ\x04ئۈ\x04ئې\x04ئى\x02ی\x04" + "ئج\x04ئح\x04ئم\x04ئي\x04بج\x04بح\x04بخ\x04بم\x04بى\x04بي\x04تج\x04تح" + "\x04تخ\x04تم\x04تى\x04تي\x04ثج\x04ثم\x04ثى\x04ثي\x04جح\x04جم\x04حج\x04حم" + "\x04خج\x04خح\x04خم\x04سج\x04سح\x04سخ\x04سم\x04صح\x04صم\x04ضج\x04ضح\x04ضخ" + "\x04ضم\x04طح\x04طم\x04ظم\x04عج\x04عم\x04غج\x04غم\x04فج\x04فح\x04فخ\x04فم" + "\x04فى\x04في\x04قح\x04قم\x04قى\x04قي\x04كا\x04كج\x04كح\x04كخ\x04كل\x04كم" + "\x04كى\x04كي\x04لج\x04لح\x04لخ\x04لم\x04لى\x04لي\x04مج\x04مح\x04مخ\x04مم" + "\x04مى\x04مي\x04نج\x04نح\x04نخ\x04نم\x04نى\x04ني\x04هج\x04هم\x04هى\x04هي" + "\x04يج\x04يح\x04يخ\x04يم\x04يى\x04يي\x04ذٰ\x04رٰ\x04ىٰ\x05 ٌّ\x05 ٍّ\x05" + " َّ\x05 ُّ\x05 ِّ\x05 ّٰ\x04ئر\x04ئز\x04ئن\x04بر\x04بز\x04بن\x04تر\x04تز" + "\x04تن\x04ثر\x04ثز\x04ثن\x04ما\x04نر\x04نز\x04نن\x04ير\x04يز\x04ين\x04ئخ" + "\x04ئه\x04به\x04ته\x04صخ\x04له\x04نه\x04هٰ\x04يه\x04ثه\x04سه\x04شم\x04شه" + "\x06ـَّ\x06ـُّ\x06ـِّ\x04طى\x04طي\x04عى\x04عي\x04غى\x04غي\x04سى\x04سي" + "\x04شى\x04شي\x04حى\x04حي\x04جى\x04جي\x04خى\x04خي\x04صى\x04صي\x04ضى\x04ضي" + "\x04شج\x04شح\x04شخ\x04شر\x04سر\x04صر\x04ضر\x04اً\x06تجم\x06تحج\x06تحم" + "\x06تخم\x06تمج\x06تمح\x06تمخ\x06جمح\x06حمي\x06حمى\x06سحج\x06سجح\x06سجى" + "\x06سمح\x06سمج\x06سمم\x06صحح\x06صمم\x06شحم\x06شجي\x06شمخ\x06شمم\x06ضحى" + "\x06ضخم\x06طمح\x06طمم\x06طمي\x06عجم\x06عمم\x06عمى\x06غمم\x06غمي\x06غمى" + "\x06فخم\x06قمح\x06قمم\x06لحم\x06لحي\x06لحى\x06لجج\x06لخم\x06لمح\x06محج" + "\x06محم\x06محي\x06مجح\x06مجم\x06مخج\x06مخم\x06مجخ\x06همج\x06همم\x06نحم" + "\x06نحى\x06نجم\x06نجى\x06نمي\x06نمى\x06يمم\x06بخي\x06تجي\x06تجى\x06تخي" + "\x06تخى\x06تمي\x06تمى\x06جمي\x06جحى\x06جمى\x06سخى\x06صحي\x06شحي\x06ضحي" + "\x06لجي\x06لمي\x06يحي\x06يجي\x06يمي\x06ممي\x06قمي\x06نحي\x06عمي\x06كمي" + "\x06نجح\x06مخي\x06لجم\x06كمم\x06جحي\x06حجي\x06مجي\x06فمي\x06بحي\x06سخي" + "\x06نجي\x06صلے\x06قلے\x08الله\x08اكبر\x08محمد\x08صلعم\x08رسول\x08عليه" + "\x08وسلم\x06صلى!صلى الله عليه وسلم\x0fجل جلاله\x08ریال\x01,\x01:\x01!" + "\x01?\x01_\x01{\x01}\x01[\x01]\x01#\x01&\x01*\x01-\x01<\x01>\x01\\\x01$" + "\x01%\x01@\x04ـً\x04ـَ\x04ـُ\x04ـِ\x04ـّ\x04ـْ\x02ء\x02آ\x02أ\x02ؤ\x02إ" + "\x02ئ\x02ا\x02ب\x02ة\x02ت\x02ث\x02ج\x02ح\x02خ\x02د\x02ذ\x02ر\x02ز\x02س" + "\x02ش\x02ص\x02ض\x02ط\x02ظ\x02ع\x02غ\x02ف\x02ق\x02ك\x02ل\x02م\x02ن\x02ه" + "\x02و\x02ي\x04لآ\x04لأ\x04لإ\x04لا\x01\x22\x01'\x01/\x01^\x01|\x01~\x02¢" + "\x02£\x02¬\x02¦\x02¥\x08𝅗𝅥\x08𝅘𝅥\x0c𝅘𝅥𝅮\x0c𝅘𝅥𝅯\x0c𝅘𝅥𝅰\x0c𝅘𝅥𝅱\x0c𝅘𝅥𝅲\x08𝆹" + "𝅥\x08𝆺𝅥\x0c𝆹𝅥𝅮\x0c𝆺𝅥𝅮\x0c𝆹𝅥𝅯\x0c𝆺𝅥𝅯\x02ı\x02ȷ\x02α\x02ε\x02ζ\x02η\x02" + "κ\x02λ\x02μ\x02ν\x02ξ\x02ο\x02σ\x02τ\x02υ\x02ψ\x03∇\x03∂\x02ϝ\x02ٮ\x02ڡ" + "\x02ٯ\x020,\x021,\x022,\x023,\x024,\x025,\x026,\x027,\x028,\x029,\x03(a)" + "\x03(b)\x03(c)\x03(d)\x03(e)\x03(f)\x03(g)\x03(h)\x03(i)\x03(j)\x03(k)" + "\x03(l)\x03(m)\x03(n)\x03(o)\x03(p)\x03(q)\x03(r)\x03(s)\x03(t)\x03(u)" + "\x03(v)\x03(w)\x03(x)\x03(y)\x03(z)\x07〔s〕\x02wz\x02hv\x02sd\x03ppv\x02w" + "c\x02mc\x02md\x02mr\x02dj\x06ほか\x06ココ\x03サ\x03手\x03字\x03双\x03デ\x03二\x03多" + "\x03解\x03天\x03交\x03映\x03無\x03料\x03前\x03後\x03再\x03新\x03初\x03終\x03生\x03販" + "\x03声\x03吹\x03演\x03投\x03捕\x03一\x03三\x03遊\x03左\x03中\x03右\x03指\x03走\x03打" + "\x03禁\x03空\x03合\x03満\x03有\x03月\x03申\x03割\x03営\x03配\x09〔本〕\x09〔三〕\x09〔二〕" + "\x09〔安〕\x09〔点〕\x09〔打〕\x09〔盗〕\x09〔勝〕\x09〔敗〕\x03得\x03可\x03丽\x03丸\x03乁\x03你" + "\x03侮\x03侻\x03倂\x03偺\x03備\x03僧\x03像\x03㒞\x03免\x03兔\x03兤\x03具\x03㒹\x03內" + "\x03冗\x03冤\x03仌\x03冬\x03况\x03凵\x03刃\x03㓟\x03刻\x03剆\x03剷\x03㔕\x03勇\x03勉" + "\x03勤\x03勺\x03包\x03匆\x03北\x03卉\x03卑\x03博\x03即\x03卽\x03卿\x03灰\x03及\x03叟" + "\x03叫\x03叱\x03吆\x03咞\x03吸\x03呈\x03周\x03咢\x03哶\x03唐\x03啓\x03啣\x03善\x03喙" + "\x03喫\x03喳\x03嗂\x03圖\x03嘆\x03圗\x03噑\x03噴\x03切\x03壮\x03城\x03埴\x03堍\x03型" + "\x03堲\x03報\x03墬\x03売\x03壷\x03夆\x03夢\x03奢\x03姬\x03娛\x03娧\x03姘\x03婦\x03㛮" + "\x03嬈\x03嬾\x03寃\x03寘\x03寧\x03寳\x03寿\x03将\x03尢\x03㞁\x03屠\x03屮\x03峀\x03岍" + "\x03嵃\x03嵮\x03嵫\x03嵼\x03巡\x03巢\x03㠯\x03巽\x03帨\x03帽\x03幩\x03㡢\x03㡼\x03庰" + "\x03庳\x03庶\x03廊\x03廾\x03舁\x03弢\x03㣇\x03形\x03彫\x03㣣\x03徚\x03忍\x03志\x03忹" + "\x03悁\x03㤺\x03㤜\x03悔\x03惇\x03慈\x03慌\x03慎\x03慺\x03憎\x03憲\x03憤\x03憯\x03懞" + "\x03懲\x03懶\x03成\x03戛\x03扝\x03抱\x03拔\x03捐\x03挽\x03拼\x03捨\x03掃\x03揤\x03搢" + "\x03揅\x03掩\x03㨮\x03摩\x03摾\x03撝\x03摷\x03㩬\x03敏\x03敬\x03旣\x03書\x03晉\x03㬙" + "\x03暑\x03㬈\x03㫤\x03冒\x03冕\x03最\x03暜\x03肭\x03䏙\x03朗\x03望\x03朡\x03杞\x03杓" + "\x03㭉\x03柺\x03枅\x03桒\x03梅\x03梎\x03栟\x03椔\x03㮝\x03楂\x03榣\x03槪\x03檨\x03櫛" + "\x03㰘\x03次\x03歔\x03㱎\x03歲\x03殟\x03殺\x03殻\x03汎\x03沿\x03泍\x03汧\x03洖\x03派" + "\x03海\x03流\x03浩\x03浸\x03涅\x03洴\x03港\x03湮\x03㴳\x03滋\x03滇\x03淹\x03潮\x03濆" + "\x03瀹\x03瀞\x03瀛\x03㶖\x03灊\x03災\x03灷\x03炭\x03煅\x03熜\x03爨\x03爵\x03牐\x03犀" + "\x03犕\x03獺\x03王\x03㺬\x03玥\x03㺸\x03瑇\x03瑜\x03瑱\x03璅\x03瓊\x03㼛\x03甤\x03甾" + "\x03異\x03瘐\x03㿼\x03䀈\x03直\x03眞\x03真\x03睊\x03䀹\x03瞋\x03䁆\x03䂖\x03硎\x03碌" + "\x03磌\x03䃣\x03祖\x03福\x03秫\x03䄯\x03穀\x03穊\x03穏\x03䈂\x03篆\x03築\x03䈧\x03糒" + "\x03䊠\x03糨\x03糣\x03紀\x03絣\x03䌁\x03緇\x03縂\x03繅\x03䌴\x03䍙\x03罺\x03羕\x03翺" + "\x03者\x03聠\x03聰\x03䏕\x03育\x03脃\x03䐋\x03脾\x03媵\x03舄\x03辞\x03䑫\x03芑\x03芋" + "\x03芝\x03劳\x03花\x03芳\x03芽\x03苦\x03若\x03茝\x03荣\x03莭\x03茣\x03莽\x03菧\x03著" + "\x03荓\x03菊\x03菌\x03菜\x03䔫\x03蓱\x03蓳\x03蔖\x03蕤\x03䕝\x03䕡\x03䕫\x03虐\x03虜" + "\x03虧\x03虩\x03蚩\x03蚈\x03蜎\x03蛢\x03蝹\x03蜨\x03蝫\x03螆\x03蟡\x03蠁\x03䗹\x03衠" + "\x03衣\x03裗\x03裞\x03䘵\x03裺\x03㒻\x03䚾\x03䛇\x03誠\x03諭\x03變\x03豕\x03貫\x03賁" + "\x03贛\x03起\x03跋\x03趼\x03跰\x03軔\x03輸\x03邔\x03郱\x03鄑\x03鄛\x03鈸\x03鋗\x03鋘" + "\x03鉼\x03鏹\x03鐕\x03開\x03䦕\x03閷\x03䧦\x03雃\x03嶲\x03霣\x03䩮\x03䩶\x03韠\x03䪲" + "\x03頋\x03頩\x03飢\x03䬳\x03餩\x03馧\x03駂\x03駾\x03䯎\x03鬒\x03鱀\x03鳽\x03䳎\x03䳭" + "\x03鵧\x03䳸\x03麻\x03䵖\x03黹\x03黾\x03鼅\x03鼏\x03鼖\x03鼻" var xorData string = "" + // Size: 4862 bytes "\x02\x0c\x09\x02\xb0\xec\x02\xad\xd8\x02\xad\xd9\x02\x06\x07\x02\x0f\x12" + "\x02\x0f\x1f\x02\x0f\x1d\x02\x01\x13\x02\x0f\x16\x02\x0f\x0b\x02\x0f3" + "\x02\x0f7\x02\x0f?\x02\x0f/\x02\x0f*\x02\x0c&\x02\x0c*\x02\x0c;\x02\x0c9" + "\x02\x0c%\x02\xab\xed\x02\xab\xe2\x02\xab\xe3\x02\xa9\xe0\x02\xa9\xe1" + "\x02\xa9\xe6\x02\xa3\xcb\x02\xa3\xc8\x02\xa3\xc9\x02\x01#\x02\x01\x08" + "\x02\x0e>\x02\x0e'\x02\x0f\x03\x02\x03\x0d\x02\x03\x09\x02\x03\x17\x02" + "\x03\x0e\x02\x02\x03\x02\x011\x02\x01\x00\x02\x01\x10\x02\x03<\x02\x07" + "\x0d\x02\x02\x0c\x02\x0c0\x02\x01\x03\x02\x01\x01\x02\x01 \x02\x01\x22" + "\x02\x01)\x02\x01\x0a\x02\x01\x0c\x02\x02\x06\x02\x02\x02\x02\x03\x10" + "\x03\x037 \x03\x0b+\x03\x021\x00\x02\x01\x04\x02\x01\x02\x02\x019\x02" + "\x03\x1c\x02\x02$\x03\x80p$\x02\x03:\x02\x03\x0a\x03\xc1r.\x03\xc1r,\x03" + "\xc1r\x02\x02\x02:\x02\x02>\x02\x02,\x02\x02\x10\x02\x02\x00\x03\xc1s<" + "\x03\xc1s*\x03\xc2L$\x03\xc2L;\x02\x09)\x02\x0a\x19\x03\x83\xab\xe3\x03" + "\x83\xab\xf2\x03 4\xe0\x03\x81\xab\xea\x03\x81\xab\xf3\x03 4\xef\x03\x96" + "\xe1\xcd\x03\x84\xe5\xc3\x02\x0d\x11\x03\x8b\xec\xcb\x03\x94\xec\xcf\x03" + "\x9a\xec\xc2\x03\x8b\xec\xdb\x03\x94\xec\xdf\x03\x9a\xec\xd2\x03\x01\x0c" + "!\x03\x01\x0c#\x03ʠ\x9d\x03ʣ\x9c\x03ʢ\x9f\x03ʥ\x9e\x03ʤ\x91\x03ʧ\x90\x03" + "ʦ\x93\x03ʩ\x92\x03ʨ\x95\x03\xca\xf3\xb5\x03\xca\xf0\xb4\x03\xca\xf1\xb7" + "\x03\xca\xf6\xb6\x03\xca\xf7\x89\x03\xca\xf4\x88\x03\xca\xf5\x8b\x03\xca" + "\xfa\x8a\x03\xca\xfb\x8d\x03\xca\xf8\x8c\x03\xca\xf9\x8f\x03\xca\xfe\x8e" + "\x03\xca\xff\x81\x03\xca\xfc\x80\x03\xca\xfd\x83\x03\xca\xe2\x82\x03\xca" + "\xe3\x85\x03\xca\xe0\x84\x03\xca\xe1\x87\x03\xca\xe6\x86\x03\xca\xe7\x99" + "\x03\xca\xe4\x98\x03\xca\xe5\x9b\x03\xca\xea\x9a\x03\xca\xeb\x9d\x03\xca" + "\xe8\x9c\x03ؓ\x89\x03ߔ\x8b\x02\x010\x03\x03\x04\x1e\x03\x04\x15\x12\x03" + "\x0b\x05,\x03\x06\x04\x00\x03\x06\x04)\x03\x06\x044\x03\x06\x04<\x03\x06" + "\x05\x1d\x03\x06\x06\x00\x03\x06\x06\x0a\x03\x06\x06'\x03\x06\x062\x03" + "\x0786\x03\x079/\x03\x079 \x03\x07:\x0e\x03\x07:\x1b\x03\x07:%\x03\x07;/" + "\x03\x07;%\x03\x074\x11\x03\x076\x09\x03\x077*\x03\x070\x01\x03\x070\x0f" + "\x03\x070.\x03\x071\x16\x03\x071\x04\x03\x0710\x03\x072\x18\x03\x072-" + "\x03\x073\x14\x03\x073>\x03\x07'\x09\x03\x07 \x00\x03\x07\x1f\x0b\x03" + "\x07\x18#\x03\x07\x18(\x03\x07\x186\x03\x07\x18\x03\x03\x07\x19\x16\x03" + "\x07\x116\x03\x07\x12'\x03\x07\x13\x10\x03\x07\x0c&\x03\x07\x0c\x08\x03" + "\x07\x0c\x13\x03\x07\x0d\x02\x03\x07\x0d\x1c\x03\x07\x0b5\x03\x07\x0b" + "\x0a\x03\x07\x0b\x01\x03\x07\x0b\x0f\x03\x07\x05\x00\x03\x07\x05\x09\x03" + "\x07\x05\x0b\x03\x07\x07\x01\x03\x07\x07\x08\x03\x07\x00<\x03\x07\x00+" + "\x03\x07\x01)\x03\x07\x01\x1b\x03\x07\x01\x08\x03\x07\x03?\x03\x0445\x03" + "\x044\x08\x03\x0454\x03\x04)/\x03\x04)5\x03\x04+\x05\x03\x04+\x14\x03" + "\x04+ \x03\x04+<\x03\x04*&\x03\x04*\x22\x03\x04&8\x03\x04!\x01\x03\x04!" + "\x22\x03\x04\x11+\x03\x04\x10.\x03\x04\x104\x03\x04\x13=\x03\x04\x12\x04" + "\x03\x04\x12\x0a\x03\x04\x0d\x1d\x03\x04\x0d\x07\x03\x04\x0d \x03\x05<>" + "\x03\x055<\x03\x055!\x03\x055#\x03\x055&\x03\x054\x1d\x03\x054\x02\x03" + "\x054\x07\x03\x0571\x03\x053\x1a\x03\x053\x16\x03\x05.<\x03\x05.\x07\x03" + "\x05):\x03\x05)<\x03\x05)\x0c\x03\x05)\x15\x03\x05+-\x03\x05+5\x03\x05$" + "\x1e\x03\x05$\x14\x03\x05'\x04\x03\x05'\x14\x03\x05&\x02\x03\x05\x226" + "\x03\x05\x22\x0c\x03\x05\x22\x1c\x03\x05\x19\x0a\x03\x05\x1b\x09\x03\x05" + "\x1b\x0c\x03\x05\x14\x07\x03\x05\x16?\x03\x05\x16\x0c\x03\x05\x0c\x05" + "\x03\x05\x0e\x0f\x03\x05\x01\x0e\x03\x05\x00(\x03\x05\x030\x03\x05\x03" + "\x06\x03\x0a==\x03\x0a=1\x03\x0a=,\x03\x0a=\x0c\x03\x0a??\x03\x0a<\x08" + "\x03\x0a9!\x03\x0a9)\x03\x0a97\x03\x0a99\x03\x0a6\x0a\x03\x0a6\x1c\x03" + "\x0a6\x17\x03\x0a7'\x03\x0a78\x03\x0a73\x03\x0a'\x01\x03\x0a'&\x03\x0a" + "\x1f\x0e\x03\x0a\x1f\x03\x03\x0a\x1f3\x03\x0a\x1b/\x03\x0a\x18\x19\x03" + "\x0a\x19\x01\x03\x0a\x16\x14\x03\x0a\x0e\x22\x03\x0a\x0f\x10\x03\x0a\x0f" + "\x02\x03\x0a\x0f \x03\x0a\x0c\x04\x03\x0a\x0b>\x03\x0a\x0b+\x03\x0a\x08/" + "\x03\x0a\x046\x03\x0a\x05\x14\x03\x0a\x00\x04\x03\x0a\x00\x10\x03\x0a" + "\x00\x14\x03\x0b<3\x03\x0b;*\x03\x0b9\x22\x03\x0b9)\x03\x0b97\x03\x0b+" + "\x10\x03\x0b((\x03\x0b&5\x03\x0b$\x1c\x03\x0b$\x12\x03\x0b%\x04\x03\x0b#" + "<\x03\x0b#0\x03\x0b#\x0d\x03\x0b#\x19\x03\x0b!:\x03\x0b!\x1f\x03\x0b!" + "\x00\x03\x0b\x1e5\x03\x0b\x1c\x1d\x03\x0b\x1d-\x03\x0b\x1d(\x03\x0b\x18." + "\x03\x0b\x18 \x03\x0b\x18\x16\x03\x0b\x14\x13\x03\x0b\x15$\x03\x0b\x15" + "\x22\x03\x0b\x12\x1b\x03\x0b\x12\x10\x03\x0b\x132\x03\x0b\x13=\x03\x0b" + "\x12\x18\x03\x0b\x0c&\x03\x0b\x061\x03\x0b\x06:\x03\x0b\x05#\x03\x0b\x05" + "<\x03\x0b\x04\x0b\x03\x0b\x04\x04\x03\x0b\x04\x1b\x03\x0b\x042\x03\x0b" + "\x041\x03\x0b\x03\x03\x03\x0b\x03\x1d\x03\x0b\x03/\x03\x0b\x03+\x03\x0b" + "\x02\x1b\x03\x0b\x02\x00\x03\x0b\x01\x1e\x03\x0b\x01\x08\x03\x0b\x015" + "\x03\x06\x0d9\x03\x06\x0d=\x03\x06\x0d?\x03\x02\x001\x03\x02\x003\x03" + "\x02\x02\x19\x03\x02\x006\x03\x02\x02\x1b\x03\x02\x004\x03\x02\x00<\x03" + "\x02\x02\x0a\x03\x02\x02\x0e\x03\x02\x01\x1a\x03\x02\x01\x07\x03\x02\x01" + "\x05\x03\x02\x01\x0b\x03\x02\x01%\x03\x02\x01\x0c\x03\x02\x01\x04\x03" + "\x02\x01\x1c\x03\x02\x00.\x03\x02\x002\x03\x02\x00>\x03\x02\x00\x12\x03" + "\x02\x00\x16\x03\x02\x011\x03\x02\x013\x03\x02\x02 \x03\x02\x02%\x03\x02" + "\x02$\x03\x02\x028\x03\x02\x02;\x03\x02\x024\x03\x02\x012\x03\x02\x022" + "\x03\x02\x02/\x03\x02\x01,\x03\x02\x01\x13\x03\x02\x01\x16\x03\x02\x01" + "\x11\x03\x02\x01\x1e\x03\x02\x01\x15\x03\x02\x01\x17\x03\x02\x01\x0f\x03" + "\x02\x01\x08\x03\x02\x00?\x03\x02\x03\x07\x03\x02\x03\x0d\x03\x02\x03" + "\x13\x03\x02\x03\x1d\x03\x02\x03\x1f\x03\x02\x00\x03\x03\x02\x00\x0d\x03" + "\x02\x00\x01\x03\x02\x00\x1b\x03\x02\x00\x19\x03\x02\x00\x18\x03\x02\x00" + "\x13\x03\x02\x00/\x03\x07>\x12\x03\x07<\x1f\x03\x07>\x1d\x03\x06\x1d\x0e" + "\x03\x07>\x1c\x03\x07>:\x03\x07>\x13\x03\x04\x12+\x03\x07?\x03\x03\x07>" + "\x02\x03\x06\x224\x03\x06\x1a.\x03\x07<%\x03\x06\x1c\x0b\x03\x0609\x03" + "\x05\x1f\x01\x03\x04'\x08\x03\x93\xfd\xf5\x03\x02\x0d \x03\x02\x0d#\x03" + "\x02\x0d!\x03\x02\x0d&\x03\x02\x0d\x22\x03\x02\x0d/\x03\x02\x0d,\x03\x02" + "\x0d$\x03\x02\x0d'\x03\x02\x0d%\x03\x02\x0d;\x03\x02\x0d=\x03\x02\x0d?" + "\x03\x099.\x03\x08\x0b7\x03\x08\x02\x14\x03\x08\x14\x0d\x03\x08.:\x03" + "\x089'\x03\x0f\x0b\x18\x03\x0f\x1c1\x03\x0f\x17&\x03\x0f9\x1f\x03\x0f0" + "\x0c\x03\x0e\x0a9\x03\x0e\x056\x03\x0e\x1c#\x03\x0f\x13\x0e\x03\x072\x00" + "\x03\x070\x0d\x03\x072\x0b\x03\x06\x11\x18\x03\x070\x10\x03\x06\x0f(\x03" + "\x072\x05\x03\x06\x0f,\x03\x073\x15\x03\x06\x07\x08\x03\x05\x16\x02\x03" + "\x04\x0b \x03\x05:8\x03\x05\x16%\x03\x0a\x0d\x1f\x03\x06\x16\x10\x03\x05" + "\x1d5\x03\x05*;\x03\x05\x16\x1b\x03\x04.-\x03\x06\x1a\x19\x03\x04\x03," + "\x03\x0b87\x03\x04/\x0a\x03\x06\x00,\x03\x04-\x01\x03\x04\x1e-\x03\x06/(" + "\x03\x0a\x0b5\x03\x06\x0e7\x03\x06\x07.\x03\x0597\x03\x0a*%\x03\x0760" + "\x03\x06\x0c;\x03\x05'\x00\x03\x072.\x03\x072\x08\x03\x06=\x01\x03\x06" + "\x05\x1b\x03\x06\x06\x12\x03\x06$=\x03\x06'\x0d\x03\x04\x11\x0f\x03\x076" + ",\x03\x06\x07;\x03\x06.,\x03\x86\xf9\xea\x03\x8f\xff\xeb\x02\x092\x02" + "\x095\x02\x094\x02\x09;\x02\x09>\x02\x098\x02\x09*\x02\x09/\x02\x09,\x02" + "\x09%\x02\x09&\x02\x09#\x02\x09 \x02\x08!\x02\x08%\x02\x08$\x02\x08+\x02" + "\x08.\x02\x08*\x02\x08&\x02\x088\x02\x08>\x02\x084\x02\x086\x02\x080\x02" + "\x08\x10\x02\x08\x17\x02\x08\x12\x02\x08\x1d\x02\x08\x1f\x02\x08\x13\x02" + "\x08\x15\x02\x08\x14\x02\x08\x0c\x03\x8b\xfd\xd0\x03\x81\xec\xc6\x03\x87" + "\xe0\x8a\x03-2\xe3\x03\x80\xef\xe4\x03-2\xea\x03\x88\xe6\xeb\x03\x8e\xe6" + "\xe8\x03\x84\xe6\xe9\x03\x97\xe6\xee\x03-2\xf9\x03-2\xf6\x03\x8e\xe3\xad" + "\x03\x80\xe3\x92\x03\x88\xe3\x90\x03\x8e\xe3\x90\x03\x80\xe3\x97\x03\x88" + "\xe3\x95\x03\x88\xfe\xcb\x03\x8e\xfe\xca\x03\x84\xfe\xcd\x03\x91\xef\xc9" + "\x03-2\xc1\x03-2\xc0\x03-2\xcb\x03\x88@\x09\x03\x8e@\x08\x03\x8f\xe0\xf5" + "\x03\x8e\xe6\xf9\x03\x8e\xe0\xfa\x03\x93\xff\xf4\x03\x84\xee\xd3\x03\x0b" + "(\x04\x023 \x03\x0b)\x08\x021;\x02\x01*\x03\x0b#\x10\x03\x0b 0\x03\x0b!" + "\x10\x03\x0b!0\x03\x07\x15\x08\x03\x09?5\x03\x07\x1f\x08\x03\x07\x17\x0b" + "\x03\x09\x1f\x15\x03\x0b\x1c7\x03\x0a+#\x03\x06\x1a\x1b\x03\x06\x1a\x14" + "\x03\x0a\x01\x18\x03\x06#\x1b\x03\x0a2\x0c\x03\x0a\x01\x04\x03\x09#;\x03" + "\x08='\x03\x08\x1a\x0a\x03\x07</\x03\x07:+\x03\x07\x07*\x03\x06&\x1c\x03" + "\x09\x0c\x16\x03\x09\x10\x0e\x03\x08'\x0f\x03\x08+\x09\x03\x074%\x03\x06" + "!3\x03\x06\x03+\x03\x0b\x1e\x19\x03\x0a))\x03\x09\x08\x19\x03\x08,\x05" + "\x03\x07<2\x03\x06\x1c>\x03\x0a\x111\x03\x09\x1b\x09\x03\x073.\x03\x07" + "\x01\x00\x03\x09/,\x03\x07#>\x03\x07\x048\x03\x0a\x1f\x22\x03\x098>\x03" + "\x09\x11\x00\x03\x08/\x17\x03\x06'\x22\x03\x0b\x1a+\x03\x0a\x22\x19\x03" + "\x0a/1\x03\x0974\x03\x09\x0f\x22\x03\x08,\x22\x03\x08?\x14\x03\x07$5\x03" + "\x07<3\x03\x07=*\x03\x07\x13\x18\x03\x068\x0a\x03\x06\x09\x16\x03\x06" + "\x13\x00\x03\x08\x067\x03\x08\x01\x03\x03\x08\x12\x1d\x03\x07+7\x03\x06(" + ";\x03\x06\x1c?\x03\x07\x0e\x17\x03\x0a\x06\x1d\x03\x0a\x19\x07\x03\x08" + "\x14$\x03\x07$;\x03\x08,$\x03\x08\x06\x0d\x03\x07\x16\x0a\x03\x06>>\x03" + "\x0a\x06\x12\x03\x0a\x14)\x03\x09\x0d\x1f\x03\x09\x12\x17\x03\x09\x19" + "\x01\x03\x08\x11 \x03\x08\x1d'\x03\x06<\x1a\x03\x0a.\x00\x03\x07'\x18" + "\x03\x0a\x22\x08\x03\x08\x0d\x0a\x03\x08\x13)\x03\x07*)\x03\x06<,\x03" + "\x07\x0b\x1a\x03\x09.\x14\x03\x09\x0d\x1e\x03\x07\x0e#\x03\x0b\x1d'\x03" + "\x0a\x0a8\x03\x09%2\x03\x08+&\x03\x080\x12\x03\x0a)4\x03\x08\x06\x1f\x03" + "\x0b\x1b\x1a\x03\x0a\x1b\x0f\x03\x0b\x1d*\x03\x09\x16$\x03\x090\x11\x03" + "\x08\x11\x08\x03\x0a*(\x03\x0a\x042\x03\x089,\x03\x074'\x03\x07\x0f\x05" + "\x03\x09\x0b\x0a\x03\x07\x1b\x01\x03\x09\x17:\x03\x09.\x0d\x03\x07.\x11" + "\x03\x09+\x15\x03\x080\x13\x03\x0b\x1f\x19\x03\x0a \x11\x03\x0a\x220\x03" + "\x09\x07;\x03\x08\x16\x1c\x03\x07,\x13\x03\x07\x0e/\x03\x06\x221\x03\x0a" + ".\x0a\x03\x0a7\x02\x03\x0a\x032\x03\x0a\x1d.\x03\x091\x06\x03\x09\x19:" + "\x03\x08\x02/\x03\x060+\x03\x06\x0f-\x03\x06\x1c\x1f\x03\x06\x1d\x07\x03" + "\x0a,\x11\x03\x09=\x0d\x03\x09\x0b;\x03\x07\x1b/\x03\x0a\x1f:\x03\x09 " + "\x1f\x03\x09.\x10\x03\x094\x0b\x03\x09\x1a1\x03\x08#\x1a\x03\x084\x1d" + "\x03\x08\x01\x1f\x03\x08\x11\x22\x03\x07'8\x03\x07\x1a>\x03\x0757\x03" + "\x06&9\x03\x06+\x11\x03\x0a.\x0b\x03\x0a,>\x03\x0a4#\x03\x08%\x17\x03" + "\x07\x05\x22\x03\x07\x0c\x0b\x03\x0a\x1d+\x03\x0a\x19\x16\x03\x09+\x1f" + "\x03\x09\x08\x0b\x03\x08\x16\x18\x03\x08+\x12\x03\x0b\x1d\x0c\x03\x0a=" + "\x10\x03\x0a\x09\x0d\x03\x0a\x10\x11\x03\x09&0\x03\x08(\x1f\x03\x087\x07" + "\x03\x08\x185\x03\x07'6\x03\x06.\x05\x03\x06=\x04\x03\x06;;\x03\x06\x06," + "\x03\x0b\x18>\x03\x08\x00\x18\x03\x06 \x03\x03\x06<\x00\x03\x09%\x18\x03" + "\x0b\x1c<\x03\x0a%!\x03\x0a\x09\x12\x03\x0a\x16\x02\x03\x090'\x03\x09" + "\x0e=\x03\x08 \x0e\x03\x08>\x03\x03\x074>\x03\x06&?\x03\x06\x19\x09\x03" + "\x06?(\x03\x0a-\x0e\x03\x09:3\x03\x098:\x03\x09\x12\x0b\x03\x09\x1d\x17" + "\x03\x087\x05\x03\x082\x14\x03\x08\x06%\x03\x08\x13\x1f\x03\x06\x06\x0e" + "\x03\x0a\x22<\x03\x09/<\x03\x06>+\x03\x0a'?\x03\x0a\x13\x0c\x03\x09\x10<" + "\x03\x07\x1b=\x03\x0a\x19\x13\x03\x09\x22\x1d\x03\x09\x07\x0d\x03\x08)" + "\x1c\x03\x06=\x1a\x03\x0a/4\x03\x0a7\x11\x03\x0a\x16:\x03\x09?3\x03\x09:" + "/\x03\x09\x05\x0a\x03\x09\x14\x06\x03\x087\x22\x03\x080\x07\x03\x08\x1a" + "\x1f\x03\x07\x04(\x03\x07\x04\x09\x03\x06 %\x03\x06<\x08\x03\x0a+\x14" + "\x03\x09\x1d\x16\x03\x0a70\x03\x08 >\x03\x0857\x03\x070\x0a\x03\x06=\x12" + "\x03\x06\x16%\x03\x06\x1d,\x03\x099#\x03\x09\x10>\x03\x07 \x1e\x03\x08" + "\x0c<\x03\x08\x0b\x18\x03\x08\x15+\x03\x08,:\x03\x08%\x22\x03\x07\x0a$" + "\x03\x0b\x1c=\x03\x07+\x08\x03\x0a/\x05\x03\x0a \x07\x03\x0a\x12'\x03" + "\x09#\x11\x03\x08\x1b\x15\x03\x0a\x06\x01\x03\x09\x1c\x1b\x03\x0922\x03" + "\x07\x14<\x03\x07\x09\x04\x03\x061\x04\x03\x07\x0e\x01\x03\x0a\x13\x18" + "\x03\x0a-\x0c\x03\x0a?\x0d\x03\x0a\x09\x0a\x03\x091&\x03\x0a/\x0b\x03" + "\x08$<\x03\x083\x1d\x03\x08\x0c$\x03\x08\x0d\x07\x03\x08\x0d?\x03\x08" + "\x0e\x14\x03\x065\x0a\x03\x08\x1a#\x03\x08\x16#\x03\x0702\x03\x07\x03" + "\x1a\x03\x06(\x1d\x03\x06+\x1b\x03\x06\x0b\x05\x03\x06\x0b\x17\x03\x06" + "\x0c\x04\x03\x06\x1e\x19\x03\x06+0\x03\x062\x18\x03\x0b\x16\x1e\x03\x0a+" + "\x16\x03\x0a-?\x03\x0a#:\x03\x0a#\x10\x03\x0a%$\x03\x0a>+\x03\x0a01\x03" + "\x0a1\x10\x03\x0a\x099\x03\x0a\x0a\x12\x03\x0a\x19\x1f\x03\x0a\x19\x12" + "\x03\x09*)\x03\x09-\x16\x03\x09.1\x03\x09.2\x03\x09<\x0e\x03\x09> \x03" + "\x093\x12\x03\x09\x0b\x01\x03\x09\x1c2\x03\x09\x11\x1c\x03\x09\x15%\x03" + "\x08,&\x03\x08!\x22\x03\x089(\x03\x08\x0b\x1a\x03\x08\x0d2\x03\x08\x0c" + "\x04\x03\x08\x0c\x06\x03\x08\x0c\x1f\x03\x08\x0c\x0c\x03\x08\x0f\x1f\x03" + "\x08\x0f\x1d\x03\x08\x00\x14\x03\x08\x03\x14\x03\x08\x06\x16\x03\x08\x1e" + "#\x03\x08\x11\x11\x03\x08\x10\x18\x03\x08\x14(\x03\x07)\x1e\x03\x07.1" + "\x03\x07 $\x03\x07 '\x03\x078\x08\x03\x07\x0d0\x03\x07\x0f7\x03\x07\x05#" + "\x03\x07\x05\x1a\x03\x07\x1a7\x03\x07\x1d-\x03\x07\x17\x10\x03\x06)\x1f" + "\x03\x062\x0b\x03\x066\x16\x03\x06\x09\x11\x03\x09(\x1e\x03\x07!5\x03" + "\x0b\x11\x16\x03\x0a/\x04\x03\x0a,\x1a\x03\x0b\x173\x03\x0a,1\x03\x0a/5" + "\x03\x0a\x221\x03\x0a\x22\x0d\x03\x0a?%\x03\x0a<,\x03\x0a?#\x03\x0a>\x19" + "\x03\x0a\x08&\x03\x0a\x0b\x0e\x03\x0a\x0c:\x03\x0a\x0c+\x03\x0a\x03\x22" + "\x03\x0a\x06)\x03\x0a\x11\x10\x03\x0a\x11\x1a\x03\x0a\x17-\x03\x0a\x14(" + "\x03\x09)\x1e\x03\x09/\x09\x03\x09.\x00\x03\x09,\x07\x03\x09/*\x03\x09-9" + "\x03\x09\x228\x03\x09%\x09\x03\x09:\x12\x03\x09;\x1d\x03\x09?\x06\x03" + "\x093%\x03\x096\x05\x03\x096\x08\x03\x097\x02\x03\x09\x07,\x03\x09\x04," + "\x03\x09\x1f\x16\x03\x09\x11\x03\x03\x09\x11\x12\x03\x09\x168\x03\x08*" + "\x05\x03\x08/2\x03\x084:\x03\x08\x22+\x03\x08 0\x03\x08&\x0a\x03\x08;" + "\x10\x03\x08>$\x03\x08>\x18\x03\x0829\x03\x082:\x03\x081,\x03\x081<\x03" + "\x081\x1c\x03\x087#\x03\x087*\x03\x08\x09'\x03\x08\x00\x1d\x03\x08\x05-" + "\x03\x08\x1f4\x03\x08\x1d\x04\x03\x08\x16\x0f\x03\x07*7\x03\x07'!\x03" + "\x07%\x1b\x03\x077\x0c\x03\x07\x0c1\x03\x07\x0c.\x03\x07\x00\x06\x03\x07" + "\x01\x02\x03\x07\x010\x03\x07\x06=\x03\x07\x01\x03\x03\x07\x01\x13\x03" + "\x07\x06\x06\x03\x07\x05\x0a\x03\x07\x1f\x09\x03\x07\x17:\x03\x06*1\x03" + "\x06-\x1d\x03\x06\x223\x03\x062:\x03\x060$\x03\x066\x1e\x03\x064\x12\x03" + "\x0645\x03\x06\x0b\x00\x03\x06\x0b7\x03\x06\x07\x1f\x03\x06\x15\x12\x03" + "\x0c\x05\x0f\x03\x0b+\x0b\x03\x0b+-\x03\x06\x16\x1b\x03\x06\x15\x17\x03" + "\x89\xca\xea\x03\x89\xca\xe8\x03\x0c8\x10\x03\x0c8\x01\x03\x0c8\x0f\x03" + "\x0d8%\x03\x0d8!\x03\x0c8-\x03\x0c8/\x03\x0c8+\x03\x0c87\x03\x0c85\x03" + "\x0c9\x09\x03\x0c9\x0d\x03\x0c9\x0f\x03\x0c9\x0b\x03\xcfu\x0c\x03\xcfu" + "\x0f\x03\xcfu\x0e\x03\xcfu\x09\x03\x0c9\x10\x03\x0d9\x0c\x03\xcf`;\x03" + "\xcf`>\x03\xcf`9\x03\xcf`8\x03\xcf`7\x03\xcf`*\x03\xcf`-\x03\xcf`,\x03" + "\x0d\x1b\x1a\x03\x0d\x1b&\x03\x0c=.\x03\x0c=%\x03\x0c>\x1e\x03\x0c>\x14" + "\x03\x0c?\x06\x03\x0c?\x0b\x03\x0c?\x0c\x03\x0c?\x0d\x03\x0c?\x02\x03" + "\x0c>\x0f\x03\x0c>\x08\x03\x0c>\x09\x03\x0c>,\x03\x0c>\x0c\x03\x0c?\x13" + "\x03\x0c?\x16\x03\x0c?\x15\x03\x0c?\x1c\x03\x0c?\x1f\x03\x0c?\x1d\x03" + "\x0c?\x1a\x03\x0c?\x17\x03\x0c?\x08\x03\x0c?\x09\x03\x0c?\x0e\x03\x0c?" + "\x04\x03\x0c?\x05\x03\x0c<?\x03\x0c=\x00\x03\x0c=\x06\x03\x0c=\x05\x03" + "\x0c=\x0c\x03\x0c=\x0f\x03\x0c=\x0d\x03\x0c=\x0b\x03\x0c=\x07\x03\x0c=" + "\x19\x03\x0c=\x15\x03\x0c=\x11\x03\x0c=1\x03\x0c=3\x03\x0c=0\x03\x0c=>" + "\x03\x0c=2\x03\x0c=6\x03\x0c<\x07\x03\x0c<\x05\x03\x0e:!\x03\x0e:#\x03" + "\x0e8\x09\x03\x0e:&\x03\x0e8\x0b\x03\x0e:$\x03\x0e:,\x03\x0e8\x1a\x03" + "\x0e8\x1e\x03\x0e:*\x03\x0e:7\x03\x0e:5\x03\x0e:;\x03\x0e:\x15\x03\x0e:<" + "\x03\x0e:4\x03\x0e:'\x03\x0e:-\x03\x0e:%\x03\x0e:?\x03\x0e:=\x03\x0e:)" + "\x03\x0e:/\x03\xcfs'\x03\x0d=\x0f\x03\x0d+*\x03\x0d99\x03\x0d9;\x03\x0d9" + "?\x03\x0d)\x0d\x03\x0d(%\x02\x01\x18\x02\x01(\x02\x01\x1e\x03\x0f$!\x03" + "\x0f87\x03\x0f4\x0e\x03\x0f5\x1d\x03\x06'\x03\x03\x0f\x08\x18\x03\x0f" + "\x0d\x1b\x03\x0e2=\x03\x0e;\x08\x03\x0e:\x0b\x03\x0e\x06$\x03\x0e\x0d)" + "\x03\x0e\x16\x1f\x03\x0e\x16\x1b\x03\x0d$\x0a\x03\x05,\x1d\x03\x0d. \x03" + "\x0d.#\x03\x0c(/\x03\x09%\x02\x03\x0d90\x03\x0d\x0e4\x03\x0d\x0d\x0f\x03" + "\x0c#\x00\x03\x0c,\x1e\x03\x0c2\x0e\x03\x0c\x01\x17\x03\x0c\x09:\x03\x0e" + "\x173\x03\x0c\x08\x03\x03\x0c\x11\x07\x03\x0c\x10\x18\x03\x0c\x1f\x1c" + "\x03\x0c\x19\x0e\x03\x0c\x1a\x1f\x03\x0f0>\x03\x0b->\x03\x0b<+\x03\x0b8" + "\x13\x03\x0b\x043\x03\x0b\x14\x03\x03\x0b\x16%\x03\x0d\x22&\x03\x0b\x1a" + "\x1a\x03\x0b\x1a\x04\x03\x0a%9\x03\x0a&2\x03\x0a&0\x03\x0a!\x1a\x03\x0a!" + "7\x03\x0a5\x10\x03\x0a=4\x03\x0a?\x0e\x03\x0a>\x10\x03\x0a\x00 \x03\x0a" + "\x0f:\x03\x0a\x0f9\x03\x0a\x0b\x0a\x03\x0a\x17%\x03\x0a\x1b-\x03\x09-" + "\x1a\x03\x09,4\x03\x09.,\x03\x09)\x09\x03\x096!\x03\x091\x1f\x03\x093" + "\x16\x03\x0c+\x1f\x03\x098 \x03\x098=\x03\x0c(\x1a\x03\x0c(\x16\x03\x09" + "\x0a+\x03\x09\x16\x12\x03\x09\x13\x0e\x03\x09\x153\x03\x08)!\x03\x09\x1a" + "\x01\x03\x09\x18\x01\x03\x08%#\x03\x08>\x22\x03\x08\x05%\x03\x08\x02*" + "\x03\x08\x15;\x03\x08\x1b7\x03\x0f\x07\x1d\x03\x0f\x04\x03\x03\x070\x0c" + "\x03\x07;\x0b\x03\x07\x08\x17\x03\x07\x12\x06\x03\x06/-\x03\x0671\x03" + "\x065+\x03\x06>7\x03\x06\x049\x03\x05+\x1e\x03\x05,\x17\x03\x05 \x1d\x03" + "\x05\x22\x05\x03\x050\x1d" // lookup returns the trie value for the first UTF-8 encoding in s and // the width in bytes of this encoding. The size will be 0 if s does not // hold enough bytes to complete the encoding. len(s) must be greater than 0. func (t *idnaTrie) lookup(s []byte) (v uint16, sz int) { c0 := s[0] switch { case c0 < 0x80: // is ASCII return idnaValues[c0], 1 case c0 < 0xC2: return 0, 1 // Illegal UTF-8: not a starter, not ASCII. case c0 < 0xE0: // 2-byte UTF-8 if len(s) < 2 { return 0, 0 } i := idnaIndex[c0] c1 := s[1] if c1 < 0x80 || 0xC0 <= c1 { return 0, 1 // Illegal UTF-8: not a continuation byte. } return t.lookupValue(uint32(i), c1), 2 case c0 < 0xF0: // 3-byte UTF-8 if len(s) < 3 { return 0, 0 } i := idnaIndex[c0] c1 := s[1] if c1 < 0x80 || 0xC0 <= c1 { return 0, 1 // Illegal UTF-8: not a continuation byte. } o := uint32(i)<<6 + uint32(c1) i = idnaIndex[o] c2 := s[2] if c2 < 0x80 || 0xC0 <= c2 { return 0, 2 // Illegal UTF-8: not a continuation byte. } return t.lookupValue(uint32(i), c2), 3 case c0 < 0xF8: // 4-byte UTF-8 if len(s) < 4 { return 0, 0 } i := idnaIndex[c0] c1 := s[1] if c1 < 0x80 || 0xC0 <= c1 { return 0, 1 // Illegal UTF-8: not a continuation byte. } o := uint32(i)<<6 + uint32(c1) i = idnaIndex[o] c2 := s[2] if c2 < 0x80 || 0xC0 <= c2 { return 0, 2 // Illegal UTF-8: not a continuation byte. } o = uint32(i)<<6 + uint32(c2) i = idnaIndex[o] c3 := s[3] if c3 < 0x80 || 0xC0 <= c3 { return 0, 3 // Illegal UTF-8: not a continuation byte. } return t.lookupValue(uint32(i), c3), 4 } // Illegal rune return 0, 1 } // lookupUnsafe returns the trie value for the first UTF-8 encoding in s. // s must start with a full and valid UTF-8 encoded rune. func (t *idnaTrie) lookupUnsafe(s []byte) uint16 { c0 := s[0] if c0 < 0x80 { // is ASCII return idnaValues[c0] } i := idnaIndex[c0] if c0 < 0xE0 { // 2-byte UTF-8 return t.lookupValue(uint32(i), s[1]) } i = idnaIndex[uint32(i)<<6+uint32(s[1])] if c0 < 0xF0 { // 3-byte UTF-8 return t.lookupValue(uint32(i), s[2]) } i = idnaIndex[uint32(i)<<6+uint32(s[2])] if c0 < 0xF8 { // 4-byte UTF-8 return t.lookupValue(uint32(i), s[3]) } return 0 } // lookupString returns the trie value for the first UTF-8 encoding in s and // the width in bytes of this encoding. The size will be 0 if s does not // hold enough bytes to complete the encoding. len(s) must be greater than 0. func (t *idnaTrie) lookupString(s string) (v uint16, sz int) { c0 := s[0] switch { case c0 < 0x80: // is ASCII return idnaValues[c0], 1 case c0 < 0xC2: return 0, 1 // Illegal UTF-8: not a starter, not ASCII. case c0 < 0xE0: // 2-byte UTF-8 if len(s) < 2 { return 0, 0 } i := idnaIndex[c0] c1 := s[1] if c1 < 0x80 || 0xC0 <= c1 { return 0, 1 // Illegal UTF-8: not a continuation byte. } return t.lookupValue(uint32(i), c1), 2 case c0 < 0xF0: // 3-byte UTF-8 if len(s) < 3 { return 0, 0 } i := idnaIndex[c0] c1 := s[1] if c1 < 0x80 || 0xC0 <= c1 { return 0, 1 // Illegal UTF-8: not a continuation byte. } o := uint32(i)<<6 + uint32(c1) i = idnaIndex[o] c2 := s[2] if c2 < 0x80 || 0xC0 <= c2 { return 0, 2 // Illegal UTF-8: not a continuation byte. } return t.lookupValue(uint32(i), c2), 3 case c0 < 0xF8: // 4-byte UTF-8 if len(s) < 4 { return 0, 0 } i := idnaIndex[c0] c1 := s[1] if c1 < 0x80 || 0xC0 <= c1 { return 0, 1 // Illegal UTF-8: not a continuation byte. } o := uint32(i)<<6 + uint32(c1) i = idnaIndex[o] c2 := s[2] if c2 < 0x80 || 0xC0 <= c2 { return 0, 2 // Illegal UTF-8: not a continuation byte. } o = uint32(i)<<6 + uint32(c2) i = idnaIndex[o] c3 := s[3] if c3 < 0x80 || 0xC0 <= c3 { return 0, 3 // Illegal UTF-8: not a continuation byte. } return t.lookupValue(uint32(i), c3), 4 } // Illegal rune return 0, 1 } // lookupStringUnsafe returns the trie value for the first UTF-8 encoding in s. // s must start with a full and valid UTF-8 encoded rune. func (t *idnaTrie) lookupStringUnsafe(s string) uint16 { c0 := s[0] if c0 < 0x80 { // is ASCII return idnaValues[c0] } i := idnaIndex[c0] if c0 < 0xE0 { // 2-byte UTF-8 return t.lookupValue(uint32(i), s[1]) } i = idnaIndex[uint32(i)<<6+uint32(s[1])] if c0 < 0xF0 { // 3-byte UTF-8 return t.lookupValue(uint32(i), s[2]) } i = idnaIndex[uint32(i)<<6+uint32(s[2])] if c0 < 0xF8 { // 4-byte UTF-8 return t.lookupValue(uint32(i), s[3]) } return 0 } // idnaTrie. Total size: 29708 bytes (29.01 KiB). Checksum: c3ecc76d8fffa6e6. type idnaTrie struct{} func newIdnaTrie(i int) *idnaTrie { return &idnaTrie{} } // lookupValue determines the type of block n and looks up the value for b. func (t *idnaTrie) lookupValue(n uint32, b byte) uint16 { switch { case n < 125: return uint16(idnaValues[n<<6+uint32(b)]) default: n -= 125 return uint16(idnaSparse.lookup(n, b)) } } // idnaValues: 127 blocks, 8128 entries, 16256 bytes // The third block is the zero block. var idnaValues = [8128]uint16{ // Block 0x0, offset 0x0 0x00: 0x0080, 0x01: 0x0080, 0x02: 0x0080, 0x03: 0x0080, 0x04: 0x0080, 0x05: 0x0080, 0x06: 0x0080, 0x07: 0x0080, 0x08: 0x0080, 0x09: 0x0080, 0x0a: 0x0080, 0x0b: 0x0080, 0x0c: 0x0080, 0x0d: 0x0080, 0x0e: 0x0080, 0x0f: 0x0080, 0x10: 0x0080, 0x11: 0x0080, 0x12: 0x0080, 0x13: 0x0080, 0x14: 0x0080, 0x15: 0x0080, 0x16: 0x0080, 0x17: 0x0080, 0x18: 0x0080, 0x19: 0x0080, 0x1a: 0x0080, 0x1b: 0x0080, 0x1c: 0x0080, 0x1d: 0x0080, 0x1e: 0x0080, 0x1f: 0x0080, 0x20: 0x0080, 0x21: 0x0080, 0x22: 0x0080, 0x23: 0x0080, 0x24: 0x0080, 0x25: 0x0080, 0x26: 0x0080, 0x27: 0x0080, 0x28: 0x0080, 0x29: 0x0080, 0x2a: 0x0080, 0x2b: 0x0080, 0x2c: 0x0080, 0x2d: 0x0008, 0x2e: 0x0008, 0x2f: 0x0080, 0x30: 0x0008, 0x31: 0x0008, 0x32: 0x0008, 0x33: 0x0008, 0x34: 0x0008, 0x35: 0x0008, 0x36: 0x0008, 0x37: 0x0008, 0x38: 0x0008, 0x39: 0x0008, 0x3a: 0x0080, 0x3b: 0x0080, 0x3c: 0x0080, 0x3d: 0x0080, 0x3e: 0x0080, 0x3f: 0x0080, // Block 0x1, offset 0x40 0x40: 0x0080, 0x41: 0xe105, 0x42: 0xe105, 0x43: 0xe105, 0x44: 0xe105, 0x45: 0xe105, 0x46: 0xe105, 0x47: 0xe105, 0x48: 0xe105, 0x49: 0xe105, 0x4a: 0xe105, 0x4b: 0xe105, 0x4c: 0xe105, 0x4d: 0xe105, 0x4e: 0xe105, 0x4f: 0xe105, 0x50: 0xe105, 0x51: 0xe105, 0x52: 0xe105, 0x53: 0xe105, 0x54: 0xe105, 0x55: 0xe105, 0x56: 0xe105, 0x57: 0xe105, 0x58: 0xe105, 0x59: 0xe105, 0x5a: 0xe105, 0x5b: 0x0080, 0x5c: 0x0080, 0x5d: 0x0080, 0x5e: 0x0080, 0x5f: 0x0080, 0x60: 0x0080, 0x61: 0x0008, 0x62: 0x0008, 0x63: 0x0008, 0x64: 0x0008, 0x65: 0x0008, 0x66: 0x0008, 0x67: 0x0008, 0x68: 0x0008, 0x69: 0x0008, 0x6a: 0x0008, 0x6b: 0x0008, 0x6c: 0x0008, 0x6d: 0x0008, 0x6e: 0x0008, 0x6f: 0x0008, 0x70: 0x0008, 0x71: 0x0008, 0x72: 0x0008, 0x73: 0x0008, 0x74: 0x0008, 0x75: 0x0008, 0x76: 0x0008, 0x77: 0x0008, 0x78: 0x0008, 0x79: 0x0008, 0x7a: 0x0008, 0x7b: 0x0080, 0x7c: 0x0080, 0x7d: 0x0080, 0x7e: 0x0080, 0x7f: 0x0080, // Block 0x2, offset 0x80 // Block 0x3, offset 0xc0 0xc0: 0x0040, 0xc1: 0x0040, 0xc2: 0x0040, 0xc3: 0x0040, 0xc4: 0x0040, 0xc5: 0x0040, 0xc6: 0x0040, 0xc7: 0x0040, 0xc8: 0x0040, 0xc9: 0x0040, 0xca: 0x0040, 0xcb: 0x0040, 0xcc: 0x0040, 0xcd: 0x0040, 0xce: 0x0040, 0xcf: 0x0040, 0xd0: 0x0040, 0xd1: 0x0040, 0xd2: 0x0040, 0xd3: 0x0040, 0xd4: 0x0040, 0xd5: 0x0040, 0xd6: 0x0040, 0xd7: 0x0040, 0xd8: 0x0040, 0xd9: 0x0040, 0xda: 0x0040, 0xdb: 0x0040, 0xdc: 0x0040, 0xdd: 0x0040, 0xde: 0x0040, 0xdf: 0x0040, 0xe0: 0x000a, 0xe1: 0x0018, 0xe2: 0x0018, 0xe3: 0x0018, 0xe4: 0x0018, 0xe5: 0x0018, 0xe6: 0x0018, 0xe7: 0x0018, 0xe8: 0x001a, 0xe9: 0x0018, 0xea: 0x0039, 0xeb: 0x0018, 0xec: 0x0018, 0xed: 0x03c0, 0xee: 0x0018, 0xef: 0x004a, 0xf0: 0x0018, 0xf1: 0x0018, 0xf2: 0x0069, 0xf3: 0x0079, 0xf4: 0x008a, 0xf5: 0x0005, 0xf6: 0x0018, 0xf7: 0x0008, 0xf8: 0x00aa, 0xf9: 0x00c9, 0xfa: 0x00d9, 0xfb: 0x0018, 0xfc: 0x00e9, 0xfd: 0x0119, 0xfe: 0x0149, 0xff: 0x0018, // Block 0x4, offset 0x100 0x100: 0xe00d, 0x101: 0x0008, 0x102: 0xe00d, 0x103: 0x0008, 0x104: 0xe00d, 0x105: 0x0008, 0x106: 0xe00d, 0x107: 0x0008, 0x108: 0xe00d, 0x109: 0x0008, 0x10a: 0xe00d, 0x10b: 0x0008, 0x10c: 0xe00d, 0x10d: 0x0008, 0x10e: 0xe00d, 0x10f: 0x0008, 0x110: 0xe00d, 0x111: 0x0008, 0x112: 0xe00d, 0x113: 0x0008, 0x114: 0xe00d, 0x115: 0x0008, 0x116: 0xe00d, 0x117: 0x0008, 0x118: 0xe00d, 0x119: 0x0008, 0x11a: 0xe00d, 0x11b: 0x0008, 0x11c: 0xe00d, 0x11d: 0x0008, 0x11e: 0xe00d, 0x11f: 0x0008, 0x120: 0xe00d, 0x121: 0x0008, 0x122: 0xe00d, 0x123: 0x0008, 0x124: 0xe00d, 0x125: 0x0008, 0x126: 0xe00d, 0x127: 0x0008, 0x128: 0xe00d, 0x129: 0x0008, 0x12a: 0xe00d, 0x12b: 0x0008, 0x12c: 0xe00d, 0x12d: 0x0008, 0x12e: 0xe00d, 0x12f: 0x0008, 0x130: 0x0179, 0x131: 0x0008, 0x132: 0x0035, 0x133: 0x004d, 0x134: 0xe00d, 0x135: 0x0008, 0x136: 0xe00d, 0x137: 0x0008, 0x138: 0x0008, 0x139: 0xe01d, 0x13a: 0x0008, 0x13b: 0xe03d, 0x13c: 0x0008, 0x13d: 0xe01d, 0x13e: 0x0008, 0x13f: 0x0199, // Block 0x5, offset 0x140 0x140: 0x0199, 0x141: 0xe01d, 0x142: 0x0008, 0x143: 0xe03d, 0x144: 0x0008, 0x145: 0xe01d, 0x146: 0x0008, 0x147: 0xe07d, 0x148: 0x0008, 0x149: 0x01b9, 0x14a: 0xe00d, 0x14b: 0x0008, 0x14c: 0xe00d, 0x14d: 0x0008, 0x14e: 0xe00d, 0x14f: 0x0008, 0x150: 0xe00d, 0x151: 0x0008, 0x152: 0xe00d, 0x153: 0x0008, 0x154: 0xe00d, 0x155: 0x0008, 0x156: 0xe00d, 0x157: 0x0008, 0x158: 0xe00d, 0x159: 0x0008, 0x15a: 0xe00d, 0x15b: 0x0008, 0x15c: 0xe00d, 0x15d: 0x0008, 0x15e: 0xe00d, 0x15f: 0x0008, 0x160: 0xe00d, 0x161: 0x0008, 0x162: 0xe00d, 0x163: 0x0008, 0x164: 0xe00d, 0x165: 0x0008, 0x166: 0xe00d, 0x167: 0x0008, 0x168: 0xe00d, 0x169: 0x0008, 0x16a: 0xe00d, 0x16b: 0x0008, 0x16c: 0xe00d, 0x16d: 0x0008, 0x16e: 0xe00d, 0x16f: 0x0008, 0x170: 0xe00d, 0x171: 0x0008, 0x172: 0xe00d, 0x173: 0x0008, 0x174: 0xe00d, 0x175: 0x0008, 0x176: 0xe00d, 0x177: 0x0008, 0x178: 0x0065, 0x179: 0xe01d, 0x17a: 0x0008, 0x17b: 0xe03d, 0x17c: 0x0008, 0x17d: 0xe01d, 0x17e: 0x0008, 0x17f: 0x01d9, // Block 0x6, offset 0x180 0x180: 0x0008, 0x181: 0x007d, 0x182: 0xe00d, 0x183: 0x0008, 0x184: 0xe00d, 0x185: 0x0008, 0x186: 0x007d, 0x187: 0xe07d, 0x188: 0x0008, 0x189: 0x0095, 0x18a: 0x00ad, 0x18b: 0xe03d, 0x18c: 0x0008, 0x18d: 0x0008, 0x18e: 0x00c5, 0x18f: 0x00dd, 0x190: 0x00f5, 0x191: 0xe01d, 0x192: 0x0008, 0x193: 0x010d, 0x194: 0x0125, 0x195: 0x0008, 0x196: 0x013d, 0x197: 0x013d, 0x198: 0xe00d, 0x199: 0x0008, 0x19a: 0x0008, 0x19b: 0x0008, 0x19c: 0x010d, 0x19d: 0x0155, 0x19e: 0x0008, 0x19f: 0x016d, 0x1a0: 0xe00d, 0x1a1: 0x0008, 0x1a2: 0xe00d, 0x1a3: 0x0008, 0x1a4: 0xe00d, 0x1a5: 0x0008, 0x1a6: 0x0185, 0x1a7: 0xe07d, 0x1a8: 0x0008, 0x1a9: 0x019d, 0x1aa: 0x0008, 0x1ab: 0x0008, 0x1ac: 0xe00d, 0x1ad: 0x0008, 0x1ae: 0x0185, 0x1af: 0xe0fd, 0x1b0: 0x0008, 0x1b1: 0x01b5, 0x1b2: 0x01cd, 0x1b3: 0xe03d, 0x1b4: 0x0008, 0x1b5: 0xe01d, 0x1b6: 0x0008, 0x1b7: 0x01e5, 0x1b8: 0xe00d, 0x1b9: 0x0008, 0x1ba: 0x0008, 0x1bb: 0x0008, 0x1bc: 0xe00d, 0x1bd: 0x0008, 0x1be: 0x0008, 0x1bf: 0x0008, // Block 0x7, offset 0x1c0 0x1c0: 0x0008, 0x1c1: 0x0008, 0x1c2: 0x0008, 0x1c3: 0x0008, 0x1c4: 0x01e9, 0x1c5: 0x01e9, 0x1c6: 0x01e9, 0x1c7: 0x01fd, 0x1c8: 0x0215, 0x1c9: 0x022d, 0x1ca: 0x0245, 0x1cb: 0x025d, 0x1cc: 0x0275, 0x1cd: 0xe01d, 0x1ce: 0x0008, 0x1cf: 0xe0fd, 0x1d0: 0x0008, 0x1d1: 0xe01d, 0x1d2: 0x0008, 0x1d3: 0xe03d, 0x1d4: 0x0008, 0x1d5: 0xe01d, 0x1d6: 0x0008, 0x1d7: 0xe07d, 0x1d8: 0x0008, 0x1d9: 0xe01d, 0x1da: 0x0008, 0x1db: 0xe03d, 0x1dc: 0x0008, 0x1dd: 0x0008, 0x1de: 0xe00d, 0x1df: 0x0008, 0x1e0: 0xe00d, 0x1e1: 0x0008, 0x1e2: 0xe00d, 0x1e3: 0x0008, 0x1e4: 0xe00d, 0x1e5: 0x0008, 0x1e6: 0xe00d, 0x1e7: 0x0008, 0x1e8: 0xe00d, 0x1e9: 0x0008, 0x1ea: 0xe00d, 0x1eb: 0x0008, 0x1ec: 0xe00d, 0x1ed: 0x0008, 0x1ee: 0xe00d, 0x1ef: 0x0008, 0x1f0: 0x0008, 0x1f1: 0x028d, 0x1f2: 0x02a5, 0x1f3: 0x02bd, 0x1f4: 0xe00d, 0x1f5: 0x0008, 0x1f6: 0x02d5, 0x1f7: 0x02ed, 0x1f8: 0xe00d, 0x1f9: 0x0008, 0x1fa: 0xe00d, 0x1fb: 0x0008, 0x1fc: 0xe00d, 0x1fd: 0x0008, 0x1fe: 0xe00d, 0x1ff: 0x0008, // Block 0x8, offset 0x200 0x200: 0xe00d, 0x201: 0x0008, 0x202: 0xe00d, 0x203: 0x0008, 0x204: 0xe00d, 0x205: 0x0008, 0x206: 0xe00d, 0x207: 0x0008, 0x208: 0xe00d, 0x209: 0x0008, 0x20a: 0xe00d, 0x20b: 0x0008, 0x20c: 0xe00d, 0x20d: 0x0008, 0x20e: 0xe00d, 0x20f: 0x0008, 0x210: 0xe00d, 0x211: 0x0008, 0x212: 0xe00d, 0x213: 0x0008, 0x214: 0xe00d, 0x215: 0x0008, 0x216: 0xe00d, 0x217: 0x0008, 0x218: 0xe00d, 0x219: 0x0008, 0x21a: 0xe00d, 0x21b: 0x0008, 0x21c: 0xe00d, 0x21d: 0x0008, 0x21e: 0xe00d, 0x21f: 0x0008, 0x220: 0x0305, 0x221: 0x0008, 0x222: 0xe00d, 0x223: 0x0008, 0x224: 0xe00d, 0x225: 0x0008, 0x226: 0xe00d, 0x227: 0x0008, 0x228: 0xe00d, 0x229: 0x0008, 0x22a: 0xe00d, 0x22b: 0x0008, 0x22c: 0xe00d, 0x22d: 0x0008, 0x22e: 0xe00d, 0x22f: 0x0008, 0x230: 0xe00d, 0x231: 0x0008, 0x232: 0xe00d, 0x233: 0x0008, 0x234: 0x0008, 0x235: 0x0008, 0x236: 0x0008, 0x237: 0x0008, 0x238: 0x0008, 0x239: 0x0008, 0x23a: 0x0209, 0x23b: 0xe03d, 0x23c: 0x0008, 0x23d: 0x031d, 0x23e: 0x0229, 0x23f: 0x0008, // Block 0x9, offset 0x240 0x240: 0x0008, 0x241: 0x0008, 0x242: 0x0018, 0x243: 0x0018, 0x244: 0x0018, 0x245: 0x0018, 0x246: 0x0008, 0x247: 0x0008, 0x248: 0x0008, 0x249: 0x0008, 0x24a: 0x0008, 0x24b: 0x0008, 0x24c: 0x0008, 0x24d: 0x0008, 0x24e: 0x0008, 0x24f: 0x0008, 0x250: 0x0008, 0x251: 0x0008, 0x252: 0x0018, 0x253: 0x0018, 0x254: 0x0018, 0x255: 0x0018, 0x256: 0x0018, 0x257: 0x0018, 0x258: 0x029a, 0x259: 0x02ba, 0x25a: 0x02da, 0x25b: 0x02fa, 0x25c: 0x031a, 0x25d: 0x033a, 0x25e: 0x0018, 0x25f: 0x0018, 0x260: 0x03ad, 0x261: 0x0359, 0x262: 0x01d9, 0x263: 0x0369, 0x264: 0x03c5, 0x265: 0x0018, 0x266: 0x0018, 0x267: 0x0018, 0x268: 0x0018, 0x269: 0x0018, 0x26a: 0x0018, 0x26b: 0x0018, 0x26c: 0x0008, 0x26d: 0x0018, 0x26e: 0x0008, 0x26f: 0x0018, 0x270: 0x0018, 0x271: 0x0018, 0x272: 0x0018, 0x273: 0x0018, 0x274: 0x0018, 0x275: 0x0018, 0x276: 0x0018, 0x277: 0x0018, 0x278: 0x0018, 0x279: 0x0018, 0x27a: 0x0018, 0x27b: 0x0018, 0x27c: 0x0018, 0x27d: 0x0018, 0x27e: 0x0018, 0x27f: 0x0018, // Block 0xa, offset 0x280 0x280: 0x03dd, 0x281: 0x03dd, 0x282: 0x3308, 0x283: 0x03f5, 0x284: 0x0379, 0x285: 0x040d, 0x286: 0x3308, 0x287: 0x3308, 0x288: 0x3308, 0x289: 0x3308, 0x28a: 0x3308, 0x28b: 0x3308, 0x28c: 0x3308, 0x28d: 0x3308, 0x28e: 0x3308, 0x28f: 0x33c0, 0x290: 0x3308, 0x291: 0x3308, 0x292: 0x3308, 0x293: 0x3308, 0x294: 0x3308, 0x295: 0x3308, 0x296: 0x3308, 0x297: 0x3308, 0x298: 0x3308, 0x299: 0x3308, 0x29a: 0x3308, 0x29b: 0x3308, 0x29c: 0x3308, 0x29d: 0x3308, 0x29e: 0x3308, 0x29f: 0x3308, 0x2a0: 0x3308, 0x2a1: 0x3308, 0x2a2: 0x3308, 0x2a3: 0x3308, 0x2a4: 0x3308, 0x2a5: 0x3308, 0x2a6: 0x3308, 0x2a7: 0x3308, 0x2a8: 0x3308, 0x2a9: 0x3308, 0x2aa: 0x3308, 0x2ab: 0x3308, 0x2ac: 0x3308, 0x2ad: 0x3308, 0x2ae: 0x3308, 0x2af: 0x3308, 0x2b0: 0xe00d, 0x2b1: 0x0008, 0x2b2: 0xe00d, 0x2b3: 0x0008, 0x2b4: 0x0425, 0x2b5: 0x0008, 0x2b6: 0xe00d, 0x2b7: 0x0008, 0x2b8: 0x0040, 0x2b9: 0x0040, 0x2ba: 0x03a2, 0x2bb: 0x0008, 0x2bc: 0x0008, 0x2bd: 0x0008, 0x2be: 0x03c2, 0x2bf: 0x043d, // Block 0xb, offset 0x2c0 0x2c0: 0x0040, 0x2c1: 0x0040, 0x2c2: 0x0040, 0x2c3: 0x0040, 0x2c4: 0x008a, 0x2c5: 0x03d2, 0x2c6: 0xe155, 0x2c7: 0x0455, 0x2c8: 0xe12d, 0x2c9: 0xe13d, 0x2ca: 0xe12d, 0x2cb: 0x0040, 0x2cc: 0x03dd, 0x2cd: 0x0040, 0x2ce: 0x046d, 0x2cf: 0x0485, 0x2d0: 0x0008, 0x2d1: 0xe105, 0x2d2: 0xe105, 0x2d3: 0xe105, 0x2d4: 0xe105, 0x2d5: 0xe105, 0x2d6: 0xe105, 0x2d7: 0xe105, 0x2d8: 0xe105, 0x2d9: 0xe105, 0x2da: 0xe105, 0x2db: 0xe105, 0x2dc: 0xe105, 0x2dd: 0xe105, 0x2de: 0xe105, 0x2df: 0xe105, 0x2e0: 0x049d, 0x2e1: 0x049d, 0x2e2: 0x0040, 0x2e3: 0x049d, 0x2e4: 0x049d, 0x2e5: 0x049d, 0x2e6: 0x049d, 0x2e7: 0x049d, 0x2e8: 0x049d, 0x2e9: 0x049d, 0x2ea: 0x049d, 0x2eb: 0x049d, 0x2ec: 0x0008, 0x2ed: 0x0008, 0x2ee: 0x0008, 0x2ef: 0x0008, 0x2f0: 0x0008, 0x2f1: 0x0008, 0x2f2: 0x0008, 0x2f3: 0x0008, 0x2f4: 0x0008, 0x2f5: 0x0008, 0x2f6: 0x0008, 0x2f7: 0x0008, 0x2f8: 0x0008, 0x2f9: 0x0008, 0x2fa: 0x0008, 0x2fb: 0x0008, 0x2fc: 0x0008, 0x2fd: 0x0008, 0x2fe: 0x0008, 0x2ff: 0x0008, // Block 0xc, offset 0x300 0x300: 0x0008, 0x301: 0x0008, 0x302: 0xe00f, 0x303: 0x0008, 0x304: 0x0008, 0x305: 0x0008, 0x306: 0x0008, 0x307: 0x0008, 0x308: 0x0008, 0x309: 0x0008, 0x30a: 0x0008, 0x30b: 0x0008, 0x30c: 0x0008, 0x30d: 0x0008, 0x30e: 0x0008, 0x30f: 0xe0c5, 0x310: 0x04b5, 0x311: 0x04cd, 0x312: 0xe0bd, 0x313: 0xe0f5, 0x314: 0xe0fd, 0x315: 0xe09d, 0x316: 0xe0b5, 0x317: 0x0008, 0x318: 0xe00d, 0x319: 0x0008, 0x31a: 0xe00d, 0x31b: 0x0008, 0x31c: 0xe00d, 0x31d: 0x0008, 0x31e: 0xe00d, 0x31f: 0x0008, 0x320: 0xe00d, 0x321: 0x0008, 0x322: 0xe00d, 0x323: 0x0008, 0x324: 0xe00d, 0x325: 0x0008, 0x326: 0xe00d, 0x327: 0x0008, 0x328: 0xe00d, 0x329: 0x0008, 0x32a: 0xe00d, 0x32b: 0x0008, 0x32c: 0xe00d, 0x32d: 0x0008, 0x32e: 0xe00d, 0x32f: 0x0008, 0x330: 0x04e5, 0x331: 0xe185, 0x332: 0xe18d, 0x333: 0x0008, 0x334: 0x04fd, 0x335: 0x03dd, 0x336: 0x0018, 0x337: 0xe07d, 0x338: 0x0008, 0x339: 0xe1d5, 0x33a: 0xe00d, 0x33b: 0x0008, 0x33c: 0x0008, 0x33d: 0x0515, 0x33e: 0x052d, 0x33f: 0x052d, // Block 0xd, offset 0x340 0x340: 0x0008, 0x341: 0x0008, 0x342: 0x0008, 0x343: 0x0008, 0x344: 0x0008, 0x345: 0x0008, 0x346: 0x0008, 0x347: 0x0008, 0x348: 0x0008, 0x349: 0x0008, 0x34a: 0x0008, 0x34b: 0x0008, 0x34c: 0x0008, 0x34d: 0x0008, 0x34e: 0x0008, 0x34f: 0x0008, 0x350: 0x0008, 0x351: 0x0008, 0x352: 0x0008, 0x353: 0x0008, 0x354: 0x0008, 0x355: 0x0008, 0x356: 0x0008, 0x357: 0x0008, 0x358: 0x0008, 0x359: 0x0008, 0x35a: 0x0008, 0x35b: 0x0008, 0x35c: 0x0008, 0x35d: 0x0008, 0x35e: 0x0008, 0x35f: 0x0008, 0x360: 0xe00d, 0x361: 0x0008, 0x362: 0xe00d, 0x363: 0x0008, 0x364: 0xe00d, 0x365: 0x0008, 0x366: 0xe00d, 0x367: 0x0008, 0x368: 0xe00d, 0x369: 0x0008, 0x36a: 0xe00d, 0x36b: 0x0008, 0x36c: 0xe00d, 0x36d: 0x0008, 0x36e: 0xe00d, 0x36f: 0x0008, 0x370: 0xe00d, 0x371: 0x0008, 0x372: 0xe00d, 0x373: 0x0008, 0x374: 0xe00d, 0x375: 0x0008, 0x376: 0xe00d, 0x377: 0x0008, 0x378: 0xe00d, 0x379: 0x0008, 0x37a: 0xe00d, 0x37b: 0x0008, 0x37c: 0xe00d, 0x37d: 0x0008, 0x37e: 0xe00d, 0x37f: 0x0008, // Block 0xe, offset 0x380 0x380: 0xe00d, 0x381: 0x0008, 0x382: 0x0018, 0x383: 0x3308, 0x384: 0x3308, 0x385: 0x3308, 0x386: 0x3308, 0x387: 0x3308, 0x388: 0x3318, 0x389: 0x3318, 0x38a: 0xe00d, 0x38b: 0x0008, 0x38c: 0xe00d, 0x38d: 0x0008, 0x38e: 0xe00d, 0x38f: 0x0008, 0x390: 0xe00d, 0x391: 0x0008, 0x392: 0xe00d, 0x393: 0x0008, 0x394: 0xe00d, 0x395: 0x0008, 0x396: 0xe00d, 0x397: 0x0008, 0x398: 0xe00d, 0x399: 0x0008, 0x39a: 0xe00d, 0x39b: 0x0008, 0x39c: 0xe00d, 0x39d: 0x0008, 0x39e: 0xe00d, 0x39f: 0x0008, 0x3a0: 0xe00d, 0x3a1: 0x0008, 0x3a2: 0xe00d, 0x3a3: 0x0008, 0x3a4: 0xe00d, 0x3a5: 0x0008, 0x3a6: 0xe00d, 0x3a7: 0x0008, 0x3a8: 0xe00d, 0x3a9: 0x0008, 0x3aa: 0xe00d, 0x3ab: 0x0008, 0x3ac: 0xe00d, 0x3ad: 0x0008, 0x3ae: 0xe00d, 0x3af: 0x0008, 0x3b0: 0xe00d, 0x3b1: 0x0008, 0x3b2: 0xe00d, 0x3b3: 0x0008, 0x3b4: 0xe00d, 0x3b5: 0x0008, 0x3b6: 0xe00d, 0x3b7: 0x0008, 0x3b8: 0xe00d, 0x3b9: 0x0008, 0x3ba: 0xe00d, 0x3bb: 0x0008, 0x3bc: 0xe00d, 0x3bd: 0x0008, 0x3be: 0xe00d, 0x3bf: 0x0008, // Block 0xf, offset 0x3c0 0x3c0: 0x0040, 0x3c1: 0xe01d, 0x3c2: 0x0008, 0x3c3: 0xe03d, 0x3c4: 0x0008, 0x3c5: 0xe01d, 0x3c6: 0x0008, 0x3c7: 0xe07d, 0x3c8: 0x0008, 0x3c9: 0xe01d, 0x3ca: 0x0008, 0x3cb: 0xe03d, 0x3cc: 0x0008, 0x3cd: 0xe01d, 0x3ce: 0x0008, 0x3cf: 0x0008, 0x3d0: 0xe00d, 0x3d1: 0x0008, 0x3d2: 0xe00d, 0x3d3: 0x0008, 0x3d4: 0xe00d, 0x3d5: 0x0008, 0x3d6: 0xe00d, 0x3d7: 0x0008, 0x3d8: 0xe00d, 0x3d9: 0x0008, 0x3da: 0xe00d, 0x3db: 0x0008, 0x3dc: 0xe00d, 0x3dd: 0x0008, 0x3de: 0xe00d, 0x3df: 0x0008, 0x3e0: 0xe00d, 0x3e1: 0x0008, 0x3e2: 0xe00d, 0x3e3: 0x0008, 0x3e4: 0xe00d, 0x3e5: 0x0008, 0x3e6: 0xe00d, 0x3e7: 0x0008, 0x3e8: 0xe00d, 0x3e9: 0x0008, 0x3ea: 0xe00d, 0x3eb: 0x0008, 0x3ec: 0xe00d, 0x3ed: 0x0008, 0x3ee: 0xe00d, 0x3ef: 0x0008, 0x3f0: 0xe00d, 0x3f1: 0x0008, 0x3f2: 0xe00d, 0x3f3: 0x0008, 0x3f4: 0xe00d, 0x3f5: 0x0008, 0x3f6: 0xe00d, 0x3f7: 0x0008, 0x3f8: 0xe00d, 0x3f9: 0x0008, 0x3fa: 0xe00d, 0x3fb: 0x0008, 0x3fc: 0xe00d, 0x3fd: 0x0008, 0x3fe: 0xe00d, 0x3ff: 0x0008, // Block 0x10, offset 0x400 0x400: 0xe00d, 0x401: 0x0008, 0x402: 0xe00d, 0x403: 0x0008, 0x404: 0xe00d, 0x405: 0x0008, 0x406: 0xe00d, 0x407: 0x0008, 0x408: 0xe00d, 0x409: 0x0008, 0x40a: 0xe00d, 0x40b: 0x0008, 0x40c: 0xe00d, 0x40d: 0x0008, 0x40e: 0xe00d, 0x40f: 0x0008, 0x410: 0xe00d, 0x411: 0x0008, 0x412: 0xe00d, 0x413: 0x0008, 0x414: 0xe00d, 0x415: 0x0008, 0x416: 0xe00d, 0x417: 0x0008, 0x418: 0xe00d, 0x419: 0x0008, 0x41a: 0xe00d, 0x41b: 0x0008, 0x41c: 0xe00d, 0x41d: 0x0008, 0x41e: 0xe00d, 0x41f: 0x0008, 0x420: 0xe00d, 0x421: 0x0008, 0x422: 0xe00d, 0x423: 0x0008, 0x424: 0xe00d, 0x425: 0x0008, 0x426: 0xe00d, 0x427: 0x0008, 0x428: 0xe00d, 0x429: 0x0008, 0x42a: 0xe00d, 0x42b: 0x0008, 0x42c: 0xe00d, 0x42d: 0x0008, 0x42e: 0xe00d, 0x42f: 0x0008, 0x430: 0x0040, 0x431: 0x03f5, 0x432: 0x03f5, 0x433: 0x03f5, 0x434: 0x03f5, 0x435: 0x03f5, 0x436: 0x03f5, 0x437: 0x03f5, 0x438: 0x03f5, 0x439: 0x03f5, 0x43a: 0x03f5, 0x43b: 0x03f5, 0x43c: 0x03f5, 0x43d: 0x03f5, 0x43e: 0x03f5, 0x43f: 0x03f5, // Block 0x11, offset 0x440 0x440: 0x0840, 0x441: 0x0840, 0x442: 0x0840, 0x443: 0x0840, 0x444: 0x0840, 0x445: 0x0840, 0x446: 0x0018, 0x447: 0x0018, 0x448: 0x0818, 0x449: 0x0018, 0x44a: 0x0018, 0x44b: 0x0818, 0x44c: 0x0018, 0x44d: 0x0818, 0x44e: 0x0018, 0x44f: 0x0018, 0x450: 0x3308, 0x451: 0x3308, 0x452: 0x3308, 0x453: 0x3308, 0x454: 0x3308, 0x455: 0x3308, 0x456: 0x3308, 0x457: 0x3308, 0x458: 0x3308, 0x459: 0x3308, 0x45a: 0x3308, 0x45b: 0x0818, 0x45c: 0x0b40, 0x45d: 0x0040, 0x45e: 0x0818, 0x45f: 0x0818, 0x460: 0x0a08, 0x461: 0x0808, 0x462: 0x0c08, 0x463: 0x0c08, 0x464: 0x0c08, 0x465: 0x0c08, 0x466: 0x0a08, 0x467: 0x0c08, 0x468: 0x0a08, 0x469: 0x0c08, 0x46a: 0x0a08, 0x46b: 0x0a08, 0x46c: 0x0a08, 0x46d: 0x0a08, 0x46e: 0x0a08, 0x46f: 0x0c08, 0x470: 0x0c08, 0x471: 0x0c08, 0x472: 0x0c08, 0x473: 0x0a08, 0x474: 0x0a08, 0x475: 0x0a08, 0x476: 0x0a08, 0x477: 0x0a08, 0x478: 0x0a08, 0x479: 0x0a08, 0x47a: 0x0a08, 0x47b: 0x0a08, 0x47c: 0x0a08, 0x47d: 0x0a08, 0x47e: 0x0a08, 0x47f: 0x0a08, // Block 0x12, offset 0x480 0x480: 0x0818, 0x481: 0x0a08, 0x482: 0x0a08, 0x483: 0x0a08, 0x484: 0x0a08, 0x485: 0x0a08, 0x486: 0x0a08, 0x487: 0x0a08, 0x488: 0x0c08, 0x489: 0x0a08, 0x48a: 0x0a08, 0x48b: 0x3308, 0x48c: 0x3308, 0x48d: 0x3308, 0x48e: 0x3308, 0x48f: 0x3308, 0x490: 0x3308, 0x491: 0x3308, 0x492: 0x3308, 0x493: 0x3308, 0x494: 0x3308, 0x495: 0x3308, 0x496: 0x3308, 0x497: 0x3308, 0x498: 0x3308, 0x499: 0x3308, 0x49a: 0x3308, 0x49b: 0x3308, 0x49c: 0x3308, 0x49d: 0x3308, 0x49e: 0x3308, 0x49f: 0x3308, 0x4a0: 0x0808, 0x4a1: 0x0808, 0x4a2: 0x0808, 0x4a3: 0x0808, 0x4a4: 0x0808, 0x4a5: 0x0808, 0x4a6: 0x0808, 0x4a7: 0x0808, 0x4a8: 0x0808, 0x4a9: 0x0808, 0x4aa: 0x0018, 0x4ab: 0x0818, 0x4ac: 0x0818, 0x4ad: 0x0818, 0x4ae: 0x0a08, 0x4af: 0x0a08, 0x4b0: 0x3308, 0x4b1: 0x0c08, 0x4b2: 0x0c08, 0x4b3: 0x0c08, 0x4b4: 0x0808, 0x4b5: 0x0429, 0x4b6: 0x0451, 0x4b7: 0x0479, 0x4b8: 0x04a1, 0x4b9: 0x0a08, 0x4ba: 0x0a08, 0x4bb: 0x0a08, 0x4bc: 0x0a08, 0x4bd: 0x0a08, 0x4be: 0x0a08, 0x4bf: 0x0a08, // Block 0x13, offset 0x4c0 0x4c0: 0x0c08, 0x4c1: 0x0a08, 0x4c2: 0x0a08, 0x4c3: 0x0c08, 0x4c4: 0x0c08, 0x4c5: 0x0c08, 0x4c6: 0x0c08, 0x4c7: 0x0c08, 0x4c8: 0x0c08, 0x4c9: 0x0c08, 0x4ca: 0x0c08, 0x4cb: 0x0c08, 0x4cc: 0x0a08, 0x4cd: 0x0c08, 0x4ce: 0x0a08, 0x4cf: 0x0c08, 0x4d0: 0x0a08, 0x4d1: 0x0a08, 0x4d2: 0x0c08, 0x4d3: 0x0c08, 0x4d4: 0x0818, 0x4d5: 0x0c08, 0x4d6: 0x3308, 0x4d7: 0x3308, 0x4d8: 0x3308, 0x4d9: 0x3308, 0x4da: 0x3308, 0x4db: 0x3308, 0x4dc: 0x3308, 0x4dd: 0x0840, 0x4de: 0x0018, 0x4df: 0x3308, 0x4e0: 0x3308, 0x4e1: 0x3308, 0x4e2: 0x3308, 0x4e3: 0x3308, 0x4e4: 0x3308, 0x4e5: 0x0808, 0x4e6: 0x0808, 0x4e7: 0x3308, 0x4e8: 0x3308, 0x4e9: 0x0018, 0x4ea: 0x3308, 0x4eb: 0x3308, 0x4ec: 0x3308, 0x4ed: 0x3308, 0x4ee: 0x0c08, 0x4ef: 0x0c08, 0x4f0: 0x0008, 0x4f1: 0x0008, 0x4f2: 0x0008, 0x4f3: 0x0008, 0x4f4: 0x0008, 0x4f5: 0x0008, 0x4f6: 0x0008, 0x4f7: 0x0008, 0x4f8: 0x0008, 0x4f9: 0x0008, 0x4fa: 0x0a08, 0x4fb: 0x0a08, 0x4fc: 0x0a08, 0x4fd: 0x0808, 0x4fe: 0x0808, 0x4ff: 0x0a08, // Block 0x14, offset 0x500 0x500: 0x0818, 0x501: 0x0818, 0x502: 0x0818, 0x503: 0x0818, 0x504: 0x0818, 0x505: 0x0818, 0x506: 0x0818, 0x507: 0x0818, 0x508: 0x0818, 0x509: 0x0818, 0x50a: 0x0818, 0x50b: 0x0818, 0x50c: 0x0818, 0x50d: 0x0818, 0x50e: 0x0040, 0x50f: 0x0b40, 0x510: 0x0c08, 0x511: 0x3308, 0x512: 0x0a08, 0x513: 0x0a08, 0x514: 0x0a08, 0x515: 0x0c08, 0x516: 0x0c08, 0x517: 0x0c08, 0x518: 0x0c08, 0x519: 0x0c08, 0x51a: 0x0a08, 0x51b: 0x0a08, 0x51c: 0x0a08, 0x51d: 0x0a08, 0x51e: 0x0c08, 0x51f: 0x0a08, 0x520: 0x0a08, 0x521: 0x0a08, 0x522: 0x0a08, 0x523: 0x0a08, 0x524: 0x0a08, 0x525: 0x0a08, 0x526: 0x0a08, 0x527: 0x0a08, 0x528: 0x0c08, 0x529: 0x0a08, 0x52a: 0x0c08, 0x52b: 0x0a08, 0x52c: 0x0c08, 0x52d: 0x0a08, 0x52e: 0x0a08, 0x52f: 0x0c08, 0x530: 0x3308, 0x531: 0x3308, 0x532: 0x3308, 0x533: 0x3308, 0x534: 0x3308, 0x535: 0x3308, 0x536: 0x3308, 0x537: 0x3308, 0x538: 0x3308, 0x539: 0x3308, 0x53a: 0x3308, 0x53b: 0x3308, 0x53c: 0x3308, 0x53d: 0x3308, 0x53e: 0x3308, 0x53f: 0x3308, // Block 0x15, offset 0x540 0x540: 0x0c08, 0x541: 0x0a08, 0x542: 0x0a08, 0x543: 0x0a08, 0x544: 0x0a08, 0x545: 0x0a08, 0x546: 0x0c08, 0x547: 0x0c08, 0x548: 0x0a08, 0x549: 0x0c08, 0x54a: 0x0a08, 0x54b: 0x0a08, 0x54c: 0x0a08, 0x54d: 0x0a08, 0x54e: 0x0a08, 0x54f: 0x0a08, 0x550: 0x0a08, 0x551: 0x0a08, 0x552: 0x0a08, 0x553: 0x0a08, 0x554: 0x0c08, 0x555: 0x0a08, 0x556: 0x0808, 0x557: 0x0808, 0x558: 0x0808, 0x559: 0x3308, 0x55a: 0x3308, 0x55b: 0x3308, 0x55c: 0x0040, 0x55d: 0x0040, 0x55e: 0x0818, 0x55f: 0x0040, 0x560: 0x0a08, 0x561: 0x0808, 0x562: 0x0a08, 0x563: 0x0a08, 0x564: 0x0a08, 0x565: 0x0a08, 0x566: 0x0808, 0x567: 0x0c08, 0x568: 0x0a08, 0x569: 0x0c08, 0x56a: 0x0c08, 0x56b: 0x0040, 0x56c: 0x0040, 0x56d: 0x0040, 0x56e: 0x0040, 0x56f: 0x0040, 0x570: 0x0040, 0x571: 0x0040, 0x572: 0x0040, 0x573: 0x0040, 0x574: 0x0040, 0x575: 0x0040, 0x576: 0x0040, 0x577: 0x0040, 0x578: 0x0040, 0x579: 0x0040, 0x57a: 0x0040, 0x57b: 0x0040, 0x57c: 0x0040, 0x57d: 0x0040, 0x57e: 0x0040, 0x57f: 0x0040, // Block 0x16, offset 0x580 0x580: 0x3008, 0x581: 0x3308, 0x582: 0x3308, 0x583: 0x3308, 0x584: 0x3308, 0x585: 0x3308, 0x586: 0x3308, 0x587: 0x3308, 0x588: 0x3308, 0x589: 0x3008, 0x58a: 0x3008, 0x58b: 0x3008, 0x58c: 0x3008, 0x58d: 0x3b08, 0x58e: 0x3008, 0x58f: 0x3008, 0x590: 0x0008, 0x591: 0x3308, 0x592: 0x3308, 0x593: 0x3308, 0x594: 0x3308, 0x595: 0x3308, 0x596: 0x3308, 0x597: 0x3308, 0x598: 0x04c9, 0x599: 0x0501, 0x59a: 0x0539, 0x59b: 0x0571, 0x59c: 0x05a9, 0x59d: 0x05e1, 0x59e: 0x0619, 0x59f: 0x0651, 0x5a0: 0x0008, 0x5a1: 0x0008, 0x5a2: 0x3308, 0x5a3: 0x3308, 0x5a4: 0x0018, 0x5a5: 0x0018, 0x5a6: 0x0008, 0x5a7: 0x0008, 0x5a8: 0x0008, 0x5a9: 0x0008, 0x5aa: 0x0008, 0x5ab: 0x0008, 0x5ac: 0x0008, 0x5ad: 0x0008, 0x5ae: 0x0008, 0x5af: 0x0008, 0x5b0: 0x0018, 0x5b1: 0x0008, 0x5b2: 0x0008, 0x5b3: 0x0008, 0x5b4: 0x0008, 0x5b5: 0x0008, 0x5b6: 0x0008, 0x5b7: 0x0008, 0x5b8: 0x0008, 0x5b9: 0x0008, 0x5ba: 0x0008, 0x5bb: 0x0008, 0x5bc: 0x0008, 0x5bd: 0x0008, 0x5be: 0x0008, 0x5bf: 0x0008, // Block 0x17, offset 0x5c0 0x5c0: 0x0008, 0x5c1: 0x3308, 0x5c2: 0x3008, 0x5c3: 0x3008, 0x5c4: 0x0040, 0x5c5: 0x0008, 0x5c6: 0x0008, 0x5c7: 0x0008, 0x5c8: 0x0008, 0x5c9: 0x0008, 0x5ca: 0x0008, 0x5cb: 0x0008, 0x5cc: 0x0008, 0x5cd: 0x0040, 0x5ce: 0x0040, 0x5cf: 0x0008, 0x5d0: 0x0008, 0x5d1: 0x0040, 0x5d2: 0x0040, 0x5d3: 0x0008, 0x5d4: 0x0008, 0x5d5: 0x0008, 0x5d6: 0x0008, 0x5d7: 0x0008, 0x5d8: 0x0008, 0x5d9: 0x0008, 0x5da: 0x0008, 0x5db: 0x0008, 0x5dc: 0x0008, 0x5dd: 0x0008, 0x5de: 0x0008, 0x5df: 0x0008, 0x5e0: 0x0008, 0x5e1: 0x0008, 0x5e2: 0x0008, 0x5e3: 0x0008, 0x5e4: 0x0008, 0x5e5: 0x0008, 0x5e6: 0x0008, 0x5e7: 0x0008, 0x5e8: 0x0008, 0x5e9: 0x0040, 0x5ea: 0x0008, 0x5eb: 0x0008, 0x5ec: 0x0008, 0x5ed: 0x0008, 0x5ee: 0x0008, 0x5ef: 0x0008, 0x5f0: 0x0008, 0x5f1: 0x0040, 0x5f2: 0x0008, 0x5f3: 0x0040, 0x5f4: 0x0040, 0x5f5: 0x0040, 0x5f6: 0x0008, 0x5f7: 0x0008, 0x5f8: 0x0008, 0x5f9: 0x0008, 0x5fa: 0x0040, 0x5fb: 0x0040, 0x5fc: 0x3308, 0x5fd: 0x0008, 0x5fe: 0x3008, 0x5ff: 0x3008, // Block 0x18, offset 0x600 0x600: 0x3008, 0x601: 0x3308, 0x602: 0x3308, 0x603: 0x3308, 0x604: 0x3308, 0x605: 0x0040, 0x606: 0x0040, 0x607: 0x3008, 0x608: 0x3008, 0x609: 0x0040, 0x60a: 0x0040, 0x60b: 0x3008, 0x60c: 0x3008, 0x60d: 0x3b08, 0x60e: 0x0008, 0x60f: 0x0040, 0x610: 0x0040, 0x611: 0x0040, 0x612: 0x0040, 0x613: 0x0040, 0x614: 0x0040, 0x615: 0x0040, 0x616: 0x0040, 0x617: 0x3008, 0x618: 0x0040, 0x619: 0x0040, 0x61a: 0x0040, 0x61b: 0x0040, 0x61c: 0x0689, 0x61d: 0x06c1, 0x61e: 0x0040, 0x61f: 0x06f9, 0x620: 0x0008, 0x621: 0x0008, 0x622: 0x3308, 0x623: 0x3308, 0x624: 0x0040, 0x625: 0x0040, 0x626: 0x0008, 0x627: 0x0008, 0x628: 0x0008, 0x629: 0x0008, 0x62a: 0x0008, 0x62b: 0x0008, 0x62c: 0x0008, 0x62d: 0x0008, 0x62e: 0x0008, 0x62f: 0x0008, 0x630: 0x0008, 0x631: 0x0008, 0x632: 0x0018, 0x633: 0x0018, 0x634: 0x0018, 0x635: 0x0018, 0x636: 0x0018, 0x637: 0x0018, 0x638: 0x0018, 0x639: 0x0018, 0x63a: 0x0018, 0x63b: 0x0018, 0x63c: 0x0008, 0x63d: 0x0018, 0x63e: 0x3308, 0x63f: 0x0040, // Block 0x19, offset 0x640 0x640: 0x0040, 0x641: 0x3308, 0x642: 0x3308, 0x643: 0x3008, 0x644: 0x0040, 0x645: 0x0008, 0x646: 0x0008, 0x647: 0x0008, 0x648: 0x0008, 0x649: 0x0008, 0x64a: 0x0008, 0x64b: 0x0040, 0x64c: 0x0040, 0x64d: 0x0040, 0x64e: 0x0040, 0x64f: 0x0008, 0x650: 0x0008, 0x651: 0x0040, 0x652: 0x0040, 0x653: 0x0008, 0x654: 0x0008, 0x655: 0x0008, 0x656: 0x0008, 0x657: 0x0008, 0x658: 0x0008, 0x659: 0x0008, 0x65a: 0x0008, 0x65b: 0x0008, 0x65c: 0x0008, 0x65d: 0x0008, 0x65e: 0x0008, 0x65f: 0x0008, 0x660: 0x0008, 0x661: 0x0008, 0x662: 0x0008, 0x663: 0x0008, 0x664: 0x0008, 0x665: 0x0008, 0x666: 0x0008, 0x667: 0x0008, 0x668: 0x0008, 0x669: 0x0040, 0x66a: 0x0008, 0x66b: 0x0008, 0x66c: 0x0008, 0x66d: 0x0008, 0x66e: 0x0008, 0x66f: 0x0008, 0x670: 0x0008, 0x671: 0x0040, 0x672: 0x0008, 0x673: 0x0731, 0x674: 0x0040, 0x675: 0x0008, 0x676: 0x0769, 0x677: 0x0040, 0x678: 0x0008, 0x679: 0x0008, 0x67a: 0x0040, 0x67b: 0x0040, 0x67c: 0x3308, 0x67d: 0x0040, 0x67e: 0x3008, 0x67f: 0x3008, // Block 0x1a, offset 0x680 0x680: 0x3008, 0x681: 0x3308, 0x682: 0x3308, 0x683: 0x0040, 0x684: 0x0040, 0x685: 0x0040, 0x686: 0x0040, 0x687: 0x3308, 0x688: 0x3308, 0x689: 0x0040, 0x68a: 0x0040, 0x68b: 0x3308, 0x68c: 0x3308, 0x68d: 0x3b08, 0x68e: 0x0040, 0x68f: 0x0040, 0x690: 0x0040, 0x691: 0x3308, 0x692: 0x0040, 0x693: 0x0040, 0x694: 0x0040, 0x695: 0x0040, 0x696: 0x0040, 0x697: 0x0040, 0x698: 0x0040, 0x699: 0x07a1, 0x69a: 0x07d9, 0x69b: 0x0811, 0x69c: 0x0008, 0x69d: 0x0040, 0x69e: 0x0849, 0x69f: 0x0040, 0x6a0: 0x0040, 0x6a1: 0x0040, 0x6a2: 0x0040, 0x6a3: 0x0040, 0x6a4: 0x0040, 0x6a5: 0x0040, 0x6a6: 0x0008, 0x6a7: 0x0008, 0x6a8: 0x0008, 0x6a9: 0x0008, 0x6aa: 0x0008, 0x6ab: 0x0008, 0x6ac: 0x0008, 0x6ad: 0x0008, 0x6ae: 0x0008, 0x6af: 0x0008, 0x6b0: 0x3308, 0x6b1: 0x3308, 0x6b2: 0x0008, 0x6b3: 0x0008, 0x6b4: 0x0008, 0x6b5: 0x3308, 0x6b6: 0x0018, 0x6b7: 0x0040, 0x6b8: 0x0040, 0x6b9: 0x0040, 0x6ba: 0x0040, 0x6bb: 0x0040, 0x6bc: 0x0040, 0x6bd: 0x0040, 0x6be: 0x0040, 0x6bf: 0x0040, // Block 0x1b, offset 0x6c0 0x6c0: 0x0040, 0x6c1: 0x3308, 0x6c2: 0x3308, 0x6c3: 0x3008, 0x6c4: 0x0040, 0x6c5: 0x0008, 0x6c6: 0x0008, 0x6c7: 0x0008, 0x6c8: 0x0008, 0x6c9: 0x0008, 0x6ca: 0x0008, 0x6cb: 0x0008, 0x6cc: 0x0008, 0x6cd: 0x0008, 0x6ce: 0x0040, 0x6cf: 0x0008, 0x6d0: 0x0008, 0x6d1: 0x0008, 0x6d2: 0x0040, 0x6d3: 0x0008, 0x6d4: 0x0008, 0x6d5: 0x0008, 0x6d6: 0x0008, 0x6d7: 0x0008, 0x6d8: 0x0008, 0x6d9: 0x0008, 0x6da: 0x0008, 0x6db: 0x0008, 0x6dc: 0x0008, 0x6dd: 0x0008, 0x6de: 0x0008, 0x6df: 0x0008, 0x6e0: 0x0008, 0x6e1: 0x0008, 0x6e2: 0x0008, 0x6e3: 0x0008, 0x6e4: 0x0008, 0x6e5: 0x0008, 0x6e6: 0x0008, 0x6e7: 0x0008, 0x6e8: 0x0008, 0x6e9: 0x0040, 0x6ea: 0x0008, 0x6eb: 0x0008, 0x6ec: 0x0008, 0x6ed: 0x0008, 0x6ee: 0x0008, 0x6ef: 0x0008, 0x6f0: 0x0008, 0x6f1: 0x0040, 0x6f2: 0x0008, 0x6f3: 0x0008, 0x6f4: 0x0040, 0x6f5: 0x0008, 0x6f6: 0x0008, 0x6f7: 0x0008, 0x6f8: 0x0008, 0x6f9: 0x0008, 0x6fa: 0x0040, 0x6fb: 0x0040, 0x6fc: 0x3308, 0x6fd: 0x0008, 0x6fe: 0x3008, 0x6ff: 0x3008, // Block 0x1c, offset 0x700 0x700: 0x3008, 0x701: 0x3308, 0x702: 0x3308, 0x703: 0x3308, 0x704: 0x3308, 0x705: 0x3308, 0x706: 0x0040, 0x707: 0x3308, 0x708: 0x3308, 0x709: 0x3008, 0x70a: 0x0040, 0x70b: 0x3008, 0x70c: 0x3008, 0x70d: 0x3b08, 0x70e: 0x0040, 0x70f: 0x0040, 0x710: 0x0008, 0x711: 0x0040, 0x712: 0x0040, 0x713: 0x0040, 0x714: 0x0040, 0x715: 0x0040, 0x716: 0x0040, 0x717: 0x0040, 0x718: 0x0040, 0x719: 0x0040, 0x71a: 0x0040, 0x71b: 0x0040, 0x71c: 0x0040, 0x71d: 0x0040, 0x71e: 0x0040, 0x71f: 0x0040, 0x720: 0x0008, 0x721: 0x0008, 0x722: 0x3308, 0x723: 0x3308, 0x724: 0x0040, 0x725: 0x0040, 0x726: 0x0008, 0x727: 0x0008, 0x728: 0x0008, 0x729: 0x0008, 0x72a: 0x0008, 0x72b: 0x0008, 0x72c: 0x0008, 0x72d: 0x0008, 0x72e: 0x0008, 0x72f: 0x0008, 0x730: 0x0018, 0x731: 0x0018, 0x732: 0x0040, 0x733: 0x0040, 0x734: 0x0040, 0x735: 0x0040, 0x736: 0x0040, 0x737: 0x0040, 0x738: 0x0040, 0x739: 0x0008, 0x73a: 0x3308, 0x73b: 0x3308, 0x73c: 0x3308, 0x73d: 0x3308, 0x73e: 0x3308, 0x73f: 0x3308, // Block 0x1d, offset 0x740 0x740: 0x0040, 0x741: 0x3308, 0x742: 0x3008, 0x743: 0x3008, 0x744: 0x0040, 0x745: 0x0008, 0x746: 0x0008, 0x747: 0x0008, 0x748: 0x0008, 0x749: 0x0008, 0x74a: 0x0008, 0x74b: 0x0008, 0x74c: 0x0008, 0x74d: 0x0040, 0x74e: 0x0040, 0x74f: 0x0008, 0x750: 0x0008, 0x751: 0x0040, 0x752: 0x0040, 0x753: 0x0008, 0x754: 0x0008, 0x755: 0x0008, 0x756: 0x0008, 0x757: 0x0008, 0x758: 0x0008, 0x759: 0x0008, 0x75a: 0x0008, 0x75b: 0x0008, 0x75c: 0x0008, 0x75d: 0x0008, 0x75e: 0x0008, 0x75f: 0x0008, 0x760: 0x0008, 0x761: 0x0008, 0x762: 0x0008, 0x763: 0x0008, 0x764: 0x0008, 0x765: 0x0008, 0x766: 0x0008, 0x767: 0x0008, 0x768: 0x0008, 0x769: 0x0040, 0x76a: 0x0008, 0x76b: 0x0008, 0x76c: 0x0008, 0x76d: 0x0008, 0x76e: 0x0008, 0x76f: 0x0008, 0x770: 0x0008, 0x771: 0x0040, 0x772: 0x0008, 0x773: 0x0008, 0x774: 0x0040, 0x775: 0x0008, 0x776: 0x0008, 0x777: 0x0008, 0x778: 0x0008, 0x779: 0x0008, 0x77a: 0x0040, 0x77b: 0x0040, 0x77c: 0x3308, 0x77d: 0x0008, 0x77e: 0x3008, 0x77f: 0x3308, // Block 0x1e, offset 0x780 0x780: 0x3008, 0x781: 0x3308, 0x782: 0x3308, 0x783: 0x3308, 0x784: 0x3308, 0x785: 0x0040, 0x786: 0x0040, 0x787: 0x3008, 0x788: 0x3008, 0x789: 0x0040, 0x78a: 0x0040, 0x78b: 0x3008, 0x78c: 0x3008, 0x78d: 0x3b08, 0x78e: 0x0040, 0x78f: 0x0040, 0x790: 0x0040, 0x791: 0x0040, 0x792: 0x0040, 0x793: 0x0040, 0x794: 0x0040, 0x795: 0x0040, 0x796: 0x3308, 0x797: 0x3008, 0x798: 0x0040, 0x799: 0x0040, 0x79a: 0x0040, 0x79b: 0x0040, 0x79c: 0x0881, 0x79d: 0x08b9, 0x79e: 0x0040, 0x79f: 0x0008, 0x7a0: 0x0008, 0x7a1: 0x0008, 0x7a2: 0x3308, 0x7a3: 0x3308, 0x7a4: 0x0040, 0x7a5: 0x0040, 0x7a6: 0x0008, 0x7a7: 0x0008, 0x7a8: 0x0008, 0x7a9: 0x0008, 0x7aa: 0x0008, 0x7ab: 0x0008, 0x7ac: 0x0008, 0x7ad: 0x0008, 0x7ae: 0x0008, 0x7af: 0x0008, 0x7b0: 0x0018, 0x7b1: 0x0008, 0x7b2: 0x0018, 0x7b3: 0x0018, 0x7b4: 0x0018, 0x7b5: 0x0018, 0x7b6: 0x0018, 0x7b7: 0x0018, 0x7b8: 0x0040, 0x7b9: 0x0040, 0x7ba: 0x0040, 0x7bb: 0x0040, 0x7bc: 0x0040, 0x7bd: 0x0040, 0x7be: 0x0040, 0x7bf: 0x0040, // Block 0x1f, offset 0x7c0 0x7c0: 0x0040, 0x7c1: 0x0040, 0x7c2: 0x3308, 0x7c3: 0x0008, 0x7c4: 0x0040, 0x7c5: 0x0008, 0x7c6: 0x0008, 0x7c7: 0x0008, 0x7c8: 0x0008, 0x7c9: 0x0008, 0x7ca: 0x0008, 0x7cb: 0x0040, 0x7cc: 0x0040, 0x7cd: 0x0040, 0x7ce: 0x0008, 0x7cf: 0x0008, 0x7d0: 0x0008, 0x7d1: 0x0040, 0x7d2: 0x0008, 0x7d3: 0x0008, 0x7d4: 0x0008, 0x7d5: 0x0008, 0x7d6: 0x0040, 0x7d7: 0x0040, 0x7d8: 0x0040, 0x7d9: 0x0008, 0x7da: 0x0008, 0x7db: 0x0040, 0x7dc: 0x0008, 0x7dd: 0x0040, 0x7de: 0x0008, 0x7df: 0x0008, 0x7e0: 0x0040, 0x7e1: 0x0040, 0x7e2: 0x0040, 0x7e3: 0x0008, 0x7e4: 0x0008, 0x7e5: 0x0040, 0x7e6: 0x0040, 0x7e7: 0x0040, 0x7e8: 0x0008, 0x7e9: 0x0008, 0x7ea: 0x0008, 0x7eb: 0x0040, 0x7ec: 0x0040, 0x7ed: 0x0040, 0x7ee: 0x0008, 0x7ef: 0x0008, 0x7f0: 0x0008, 0x7f1: 0x0008, 0x7f2: 0x0008, 0x7f3: 0x0008, 0x7f4: 0x0008, 0x7f5: 0x0008, 0x7f6: 0x0008, 0x7f7: 0x0008, 0x7f8: 0x0008, 0x7f9: 0x0008, 0x7fa: 0x0040, 0x7fb: 0x0040, 0x7fc: 0x0040, 0x7fd: 0x0040, 0x7fe: 0x3008, 0x7ff: 0x3008, // Block 0x20, offset 0x800 0x800: 0x3308, 0x801: 0x3008, 0x802: 0x3008, 0x803: 0x3008, 0x804: 0x3008, 0x805: 0x0040, 0x806: 0x3308, 0x807: 0x3308, 0x808: 0x3308, 0x809: 0x0040, 0x80a: 0x3308, 0x80b: 0x3308, 0x80c: 0x3308, 0x80d: 0x3b08, 0x80e: 0x0040, 0x80f: 0x0040, 0x810: 0x0040, 0x811: 0x0040, 0x812: 0x0040, 0x813: 0x0040, 0x814: 0x0040, 0x815: 0x3308, 0x816: 0x3308, 0x817: 0x0040, 0x818: 0x0008, 0x819: 0x0008, 0x81a: 0x0008, 0x81b: 0x0040, 0x81c: 0x0040, 0x81d: 0x0040, 0x81e: 0x0040, 0x81f: 0x0040, 0x820: 0x0008, 0x821: 0x0008, 0x822: 0x3308, 0x823: 0x3308, 0x824: 0x0040, 0x825: 0x0040, 0x826: 0x0008, 0x827: 0x0008, 0x828: 0x0008, 0x829: 0x0008, 0x82a: 0x0008, 0x82b: 0x0008, 0x82c: 0x0008, 0x82d: 0x0008, 0x82e: 0x0008, 0x82f: 0x0008, 0x830: 0x0040, 0x831: 0x0040, 0x832: 0x0040, 0x833: 0x0040, 0x834: 0x0040, 0x835: 0x0040, 0x836: 0x0040, 0x837: 0x0018, 0x838: 0x0018, 0x839: 0x0018, 0x83a: 0x0018, 0x83b: 0x0018, 0x83c: 0x0018, 0x83d: 0x0018, 0x83e: 0x0018, 0x83f: 0x0018, // Block 0x21, offset 0x840 0x840: 0x0008, 0x841: 0x3308, 0x842: 0x3008, 0x843: 0x3008, 0x844: 0x0018, 0x845: 0x0008, 0x846: 0x0008, 0x847: 0x0008, 0x848: 0x0008, 0x849: 0x0008, 0x84a: 0x0008, 0x84b: 0x0008, 0x84c: 0x0008, 0x84d: 0x0040, 0x84e: 0x0008, 0x84f: 0x0008, 0x850: 0x0008, 0x851: 0x0040, 0x852: 0x0008, 0x853: 0x0008, 0x854: 0x0008, 0x855: 0x0008, 0x856: 0x0008, 0x857: 0x0008, 0x858: 0x0008, 0x859: 0x0008, 0x85a: 0x0008, 0x85b: 0x0008, 0x85c: 0x0008, 0x85d: 0x0008, 0x85e: 0x0008, 0x85f: 0x0008, 0x860: 0x0008, 0x861: 0x0008, 0x862: 0x0008, 0x863: 0x0008, 0x864: 0x0008, 0x865: 0x0008, 0x866: 0x0008, 0x867: 0x0008, 0x868: 0x0008, 0x869: 0x0040, 0x86a: 0x0008, 0x86b: 0x0008, 0x86c: 0x0008, 0x86d: 0x0008, 0x86e: 0x0008, 0x86f: 0x0008, 0x870: 0x0008, 0x871: 0x0008, 0x872: 0x0008, 0x873: 0x0008, 0x874: 0x0040, 0x875: 0x0008, 0x876: 0x0008, 0x877: 0x0008, 0x878: 0x0008, 0x879: 0x0008, 0x87a: 0x0040, 0x87b: 0x0040, 0x87c: 0x3308, 0x87d: 0x0008, 0x87e: 0x3008, 0x87f: 0x3308, // Block 0x22, offset 0x880 0x880: 0x3008, 0x881: 0x3008, 0x882: 0x3008, 0x883: 0x3008, 0x884: 0x3008, 0x885: 0x0040, 0x886: 0x3308, 0x887: 0x3008, 0x888: 0x3008, 0x889: 0x0040, 0x88a: 0x3008, 0x88b: 0x3008, 0x88c: 0x3308, 0x88d: 0x3b08, 0x88e: 0x0040, 0x88f: 0x0040, 0x890: 0x0040, 0x891: 0x0040, 0x892: 0x0040, 0x893: 0x0040, 0x894: 0x0040, 0x895: 0x3008, 0x896: 0x3008, 0x897: 0x0040, 0x898: 0x0040, 0x899: 0x0040, 0x89a: 0x0040, 0x89b: 0x0040, 0x89c: 0x0040, 0x89d: 0x0040, 0x89e: 0x0008, 0x89f: 0x0040, 0x8a0: 0x0008, 0x8a1: 0x0008, 0x8a2: 0x3308, 0x8a3: 0x3308, 0x8a4: 0x0040, 0x8a5: 0x0040, 0x8a6: 0x0008, 0x8a7: 0x0008, 0x8a8: 0x0008, 0x8a9: 0x0008, 0x8aa: 0x0008, 0x8ab: 0x0008, 0x8ac: 0x0008, 0x8ad: 0x0008, 0x8ae: 0x0008, 0x8af: 0x0008, 0x8b0: 0x0040, 0x8b1: 0x0008, 0x8b2: 0x0008, 0x8b3: 0x0040, 0x8b4: 0x0040, 0x8b5: 0x0040, 0x8b6: 0x0040, 0x8b7: 0x0040, 0x8b8: 0x0040, 0x8b9: 0x0040, 0x8ba: 0x0040, 0x8bb: 0x0040, 0x8bc: 0x0040, 0x8bd: 0x0040, 0x8be: 0x0040, 0x8bf: 0x0040, // Block 0x23, offset 0x8c0 0x8c0: 0x3008, 0x8c1: 0x3308, 0x8c2: 0x3308, 0x8c3: 0x3308, 0x8c4: 0x3308, 0x8c5: 0x0040, 0x8c6: 0x3008, 0x8c7: 0x3008, 0x8c8: 0x3008, 0x8c9: 0x0040, 0x8ca: 0x3008, 0x8cb: 0x3008, 0x8cc: 0x3008, 0x8cd: 0x3b08, 0x8ce: 0x0008, 0x8cf: 0x0018, 0x8d0: 0x0040, 0x8d1: 0x0040, 0x8d2: 0x0040, 0x8d3: 0x0040, 0x8d4: 0x0008, 0x8d5: 0x0008, 0x8d6: 0x0008, 0x8d7: 0x3008, 0x8d8: 0x0018, 0x8d9: 0x0018, 0x8da: 0x0018, 0x8db: 0x0018, 0x8dc: 0x0018, 0x8dd: 0x0018, 0x8de: 0x0018, 0x8df: 0x0008, 0x8e0: 0x0008, 0x8e1: 0x0008, 0x8e2: 0x3308, 0x8e3: 0x3308, 0x8e4: 0x0040, 0x8e5: 0x0040, 0x8e6: 0x0008, 0x8e7: 0x0008, 0x8e8: 0x0008, 0x8e9: 0x0008, 0x8ea: 0x0008, 0x8eb: 0x0008, 0x8ec: 0x0008, 0x8ed: 0x0008, 0x8ee: 0x0008, 0x8ef: 0x0008, 0x8f0: 0x0018, 0x8f1: 0x0018, 0x8f2: 0x0018, 0x8f3: 0x0018, 0x8f4: 0x0018, 0x8f5: 0x0018, 0x8f6: 0x0018, 0x8f7: 0x0018, 0x8f8: 0x0018, 0x8f9: 0x0018, 0x8fa: 0x0008, 0x8fb: 0x0008, 0x8fc: 0x0008, 0x8fd: 0x0008, 0x8fe: 0x0008, 0x8ff: 0x0008, // Block 0x24, offset 0x900 0x900: 0x0040, 0x901: 0x0008, 0x902: 0x0008, 0x903: 0x0040, 0x904: 0x0008, 0x905: 0x0040, 0x906: 0x0008, 0x907: 0x0008, 0x908: 0x0008, 0x909: 0x0008, 0x90a: 0x0008, 0x90b: 0x0040, 0x90c: 0x0008, 0x90d: 0x0008, 0x90e: 0x0008, 0x90f: 0x0008, 0x910: 0x0008, 0x911: 0x0008, 0x912: 0x0008, 0x913: 0x0008, 0x914: 0x0008, 0x915: 0x0008, 0x916: 0x0008, 0x917: 0x0008, 0x918: 0x0008, 0x919: 0x0008, 0x91a: 0x0008, 0x91b: 0x0008, 0x91c: 0x0008, 0x91d: 0x0008, 0x91e: 0x0008, 0x91f: 0x0008, 0x920: 0x0008, 0x921: 0x0008, 0x922: 0x0008, 0x923: 0x0008, 0x924: 0x0040, 0x925: 0x0008, 0x926: 0x0040, 0x927: 0x0008, 0x928: 0x0008, 0x929: 0x0008, 0x92a: 0x0008, 0x92b: 0x0008, 0x92c: 0x0008, 0x92d: 0x0008, 0x92e: 0x0008, 0x92f: 0x0008, 0x930: 0x0008, 0x931: 0x3308, 0x932: 0x0008, 0x933: 0x0929, 0x934: 0x3308, 0x935: 0x3308, 0x936: 0x3308, 0x937: 0x3308, 0x938: 0x3308, 0x939: 0x3308, 0x93a: 0x3b08, 0x93b: 0x3308, 0x93c: 0x3308, 0x93d: 0x0008, 0x93e: 0x0040, 0x93f: 0x0040, // Block 0x25, offset 0x940 0x940: 0x0008, 0x941: 0x0008, 0x942: 0x0008, 0x943: 0x09d1, 0x944: 0x0008, 0x945: 0x0008, 0x946: 0x0008, 0x947: 0x0008, 0x948: 0x0040, 0x949: 0x0008, 0x94a: 0x0008, 0x94b: 0x0008, 0x94c: 0x0008, 0x94d: 0x0a09, 0x94e: 0x0008, 0x94f: 0x0008, 0x950: 0x0008, 0x951: 0x0008, 0x952: 0x0a41, 0x953: 0x0008, 0x954: 0x0008, 0x955: 0x0008, 0x956: 0x0008, 0x957: 0x0a79, 0x958: 0x0008, 0x959: 0x0008, 0x95a: 0x0008, 0x95b: 0x0008, 0x95c: 0x0ab1, 0x95d: 0x0008, 0x95e: 0x0008, 0x95f: 0x0008, 0x960: 0x0008, 0x961: 0x0008, 0x962: 0x0008, 0x963: 0x0008, 0x964: 0x0008, 0x965: 0x0008, 0x966: 0x0008, 0x967: 0x0008, 0x968: 0x0008, 0x969: 0x0ae9, 0x96a: 0x0008, 0x96b: 0x0008, 0x96c: 0x0008, 0x96d: 0x0040, 0x96e: 0x0040, 0x96f: 0x0040, 0x970: 0x0040, 0x971: 0x3308, 0x972: 0x3308, 0x973: 0x0b21, 0x974: 0x3308, 0x975: 0x0b59, 0x976: 0x0b91, 0x977: 0x0bc9, 0x978: 0x0c19, 0x979: 0x0c51, 0x97a: 0x3308, 0x97b: 0x3308, 0x97c: 0x3308, 0x97d: 0x3308, 0x97e: 0x3308, 0x97f: 0x3008, // Block 0x26, offset 0x980 0x980: 0x3308, 0x981: 0x0ca1, 0x982: 0x3308, 0x983: 0x3308, 0x984: 0x3b08, 0x985: 0x0018, 0x986: 0x3308, 0x987: 0x3308, 0x988: 0x0008, 0x989: 0x0008, 0x98a: 0x0008, 0x98b: 0x0008, 0x98c: 0x0008, 0x98d: 0x3308, 0x98e: 0x3308, 0x98f: 0x3308, 0x990: 0x3308, 0x991: 0x3308, 0x992: 0x3308, 0x993: 0x0cd9, 0x994: 0x3308, 0x995: 0x3308, 0x996: 0x3308, 0x997: 0x3308, 0x998: 0x0040, 0x999: 0x3308, 0x99a: 0x3308, 0x99b: 0x3308, 0x99c: 0x3308, 0x99d: 0x0d11, 0x99e: 0x3308, 0x99f: 0x3308, 0x9a0: 0x3308, 0x9a1: 0x3308, 0x9a2: 0x0d49, 0x9a3: 0x3308, 0x9a4: 0x3308, 0x9a5: 0x3308, 0x9a6: 0x3308, 0x9a7: 0x0d81, 0x9a8: 0x3308, 0x9a9: 0x3308, 0x9aa: 0x3308, 0x9ab: 0x3308, 0x9ac: 0x0db9, 0x9ad: 0x3308, 0x9ae: 0x3308, 0x9af: 0x3308, 0x9b0: 0x3308, 0x9b1: 0x3308, 0x9b2: 0x3308, 0x9b3: 0x3308, 0x9b4: 0x3308, 0x9b5: 0x3308, 0x9b6: 0x3308, 0x9b7: 0x3308, 0x9b8: 0x3308, 0x9b9: 0x0df1, 0x9ba: 0x3308, 0x9bb: 0x3308, 0x9bc: 0x3308, 0x9bd: 0x0040, 0x9be: 0x0018, 0x9bf: 0x0018, // Block 0x27, offset 0x9c0 0x9c0: 0x0008, 0x9c1: 0x0008, 0x9c2: 0x0008, 0x9c3: 0x0008, 0x9c4: 0x0008, 0x9c5: 0x0008, 0x9c6: 0x0008, 0x9c7: 0x0008, 0x9c8: 0x0008, 0x9c9: 0x0008, 0x9ca: 0x0008, 0x9cb: 0x0008, 0x9cc: 0x0008, 0x9cd: 0x0008, 0x9ce: 0x0008, 0x9cf: 0x0008, 0x9d0: 0x0008, 0x9d1: 0x0008, 0x9d2: 0x0008, 0x9d3: 0x0008, 0x9d4: 0x0008, 0x9d5: 0x0008, 0x9d6: 0x0008, 0x9d7: 0x0008, 0x9d8: 0x0008, 0x9d9: 0x0008, 0x9da: 0x0008, 0x9db: 0x0008, 0x9dc: 0x0008, 0x9dd: 0x0008, 0x9de: 0x0008, 0x9df: 0x0008, 0x9e0: 0x0008, 0x9e1: 0x0008, 0x9e2: 0x0008, 0x9e3: 0x0008, 0x9e4: 0x0008, 0x9e5: 0x0008, 0x9e6: 0x0008, 0x9e7: 0x0008, 0x9e8: 0x0008, 0x9e9: 0x0008, 0x9ea: 0x0008, 0x9eb: 0x0008, 0x9ec: 0x0039, 0x9ed: 0x0ed1, 0x9ee: 0x0ee9, 0x9ef: 0x0008, 0x9f0: 0x0ef9, 0x9f1: 0x0f09, 0x9f2: 0x0f19, 0x9f3: 0x0f31, 0x9f4: 0x0249, 0x9f5: 0x0f41, 0x9f6: 0x0259, 0x9f7: 0x0f51, 0x9f8: 0x0359, 0x9f9: 0x0f61, 0x9fa: 0x0f71, 0x9fb: 0x0008, 0x9fc: 0x00d9, 0x9fd: 0x0f81, 0x9fe: 0x0f99, 0x9ff: 0x0269, // Block 0x28, offset 0xa00 0xa00: 0x0fa9, 0xa01: 0x0fb9, 0xa02: 0x0279, 0xa03: 0x0039, 0xa04: 0x0fc9, 0xa05: 0x0fe1, 0xa06: 0x05b5, 0xa07: 0x0ee9, 0xa08: 0x0ef9, 0xa09: 0x0f09, 0xa0a: 0x0ff9, 0xa0b: 0x1011, 0xa0c: 0x1029, 0xa0d: 0x0f31, 0xa0e: 0x0008, 0xa0f: 0x0f51, 0xa10: 0x0f61, 0xa11: 0x1041, 0xa12: 0x00d9, 0xa13: 0x1059, 0xa14: 0x05cd, 0xa15: 0x05cd, 0xa16: 0x0f99, 0xa17: 0x0fa9, 0xa18: 0x0fb9, 0xa19: 0x05b5, 0xa1a: 0x1071, 0xa1b: 0x1089, 0xa1c: 0x05e5, 0xa1d: 0x1099, 0xa1e: 0x10b1, 0xa1f: 0x10c9, 0xa20: 0x10e1, 0xa21: 0x10f9, 0xa22: 0x0f41, 0xa23: 0x0269, 0xa24: 0x0fb9, 0xa25: 0x1089, 0xa26: 0x1099, 0xa27: 0x10b1, 0xa28: 0x1111, 0xa29: 0x10e1, 0xa2a: 0x10f9, 0xa2b: 0x0008, 0xa2c: 0x0008, 0xa2d: 0x0008, 0xa2e: 0x0008, 0xa2f: 0x0008, 0xa30: 0x0008, 0xa31: 0x0008, 0xa32: 0x0008, 0xa33: 0x0008, 0xa34: 0x0008, 0xa35: 0x0008, 0xa36: 0x0008, 0xa37: 0x0008, 0xa38: 0x1129, 0xa39: 0x0008, 0xa3a: 0x0008, 0xa3b: 0x0008, 0xa3c: 0x0008, 0xa3d: 0x0008, 0xa3e: 0x0008, 0xa3f: 0x0008, // Block 0x29, offset 0xa40 0xa40: 0x0008, 0xa41: 0x0008, 0xa42: 0x0008, 0xa43: 0x0008, 0xa44: 0x0008, 0xa45: 0x0008, 0xa46: 0x0008, 0xa47: 0x0008, 0xa48: 0x0008, 0xa49: 0x0008, 0xa4a: 0x0008, 0xa4b: 0x0008, 0xa4c: 0x0008, 0xa4d: 0x0008, 0xa4e: 0x0008, 0xa4f: 0x0008, 0xa50: 0x0008, 0xa51: 0x0008, 0xa52: 0x0008, 0xa53: 0x0008, 0xa54: 0x0008, 0xa55: 0x0008, 0xa56: 0x0008, 0xa57: 0x0008, 0xa58: 0x0008, 0xa59: 0x0008, 0xa5a: 0x0008, 0xa5b: 0x1141, 0xa5c: 0x1159, 0xa5d: 0x1169, 0xa5e: 0x1181, 0xa5f: 0x1029, 0xa60: 0x1199, 0xa61: 0x11a9, 0xa62: 0x11c1, 0xa63: 0x11d9, 0xa64: 0x11f1, 0xa65: 0x1209, 0xa66: 0x1221, 0xa67: 0x05fd, 0xa68: 0x1239, 0xa69: 0x1251, 0xa6a: 0xe17d, 0xa6b: 0x1269, 0xa6c: 0x1281, 0xa6d: 0x1299, 0xa6e: 0x12b1, 0xa6f: 0x12c9, 0xa70: 0x12e1, 0xa71: 0x12f9, 0xa72: 0x1311, 0xa73: 0x1329, 0xa74: 0x1341, 0xa75: 0x1359, 0xa76: 0x1371, 0xa77: 0x1389, 0xa78: 0x0615, 0xa79: 0x13a1, 0xa7a: 0x13b9, 0xa7b: 0x13d1, 0xa7c: 0x13e1, 0xa7d: 0x13f9, 0xa7e: 0x1411, 0xa7f: 0x1429, // Block 0x2a, offset 0xa80 0xa80: 0xe00d, 0xa81: 0x0008, 0xa82: 0xe00d, 0xa83: 0x0008, 0xa84: 0xe00d, 0xa85: 0x0008, 0xa86: 0xe00d, 0xa87: 0x0008, 0xa88: 0xe00d, 0xa89: 0x0008, 0xa8a: 0xe00d, 0xa8b: 0x0008, 0xa8c: 0xe00d, 0xa8d: 0x0008, 0xa8e: 0xe00d, 0xa8f: 0x0008, 0xa90: 0xe00d, 0xa91: 0x0008, 0xa92: 0xe00d, 0xa93: 0x0008, 0xa94: 0xe00d, 0xa95: 0x0008, 0xa96: 0xe00d, 0xa97: 0x0008, 0xa98: 0xe00d, 0xa99: 0x0008, 0xa9a: 0xe00d, 0xa9b: 0x0008, 0xa9c: 0xe00d, 0xa9d: 0x0008, 0xa9e: 0xe00d, 0xa9f: 0x0008, 0xaa0: 0xe00d, 0xaa1: 0x0008, 0xaa2: 0xe00d, 0xaa3: 0x0008, 0xaa4: 0xe00d, 0xaa5: 0x0008, 0xaa6: 0xe00d, 0xaa7: 0x0008, 0xaa8: 0xe00d, 0xaa9: 0x0008, 0xaaa: 0xe00d, 0xaab: 0x0008, 0xaac: 0xe00d, 0xaad: 0x0008, 0xaae: 0xe00d, 0xaaf: 0x0008, 0xab0: 0xe00d, 0xab1: 0x0008, 0xab2: 0xe00d, 0xab3: 0x0008, 0xab4: 0xe00d, 0xab5: 0x0008, 0xab6: 0xe00d, 0xab7: 0x0008, 0xab8: 0xe00d, 0xab9: 0x0008, 0xaba: 0xe00d, 0xabb: 0x0008, 0xabc: 0xe00d, 0xabd: 0x0008, 0xabe: 0xe00d, 0xabf: 0x0008, // Block 0x2b, offset 0xac0 0xac0: 0xe00d, 0xac1: 0x0008, 0xac2: 0xe00d, 0xac3: 0x0008, 0xac4: 0xe00d, 0xac5: 0x0008, 0xac6: 0xe00d, 0xac7: 0x0008, 0xac8: 0xe00d, 0xac9: 0x0008, 0xaca: 0xe00d, 0xacb: 0x0008, 0xacc: 0xe00d, 0xacd: 0x0008, 0xace: 0xe00d, 0xacf: 0x0008, 0xad0: 0xe00d, 0xad1: 0x0008, 0xad2: 0xe00d, 0xad3: 0x0008, 0xad4: 0xe00d, 0xad5: 0x0008, 0xad6: 0x0008, 0xad7: 0x0008, 0xad8: 0x0008, 0xad9: 0x0008, 0xada: 0x062d, 0xadb: 0x064d, 0xadc: 0x0008, 0xadd: 0x0008, 0xade: 0x1441, 0xadf: 0x0008, 0xae0: 0xe00d, 0xae1: 0x0008, 0xae2: 0xe00d, 0xae3: 0x0008, 0xae4: 0xe00d, 0xae5: 0x0008, 0xae6: 0xe00d, 0xae7: 0x0008, 0xae8: 0xe00d, 0xae9: 0x0008, 0xaea: 0xe00d, 0xaeb: 0x0008, 0xaec: 0xe00d, 0xaed: 0x0008, 0xaee: 0xe00d, 0xaef: 0x0008, 0xaf0: 0xe00d, 0xaf1: 0x0008, 0xaf2: 0xe00d, 0xaf3: 0x0008, 0xaf4: 0xe00d, 0xaf5: 0x0008, 0xaf6: 0xe00d, 0xaf7: 0x0008, 0xaf8: 0xe00d, 0xaf9: 0x0008, 0xafa: 0xe00d, 0xafb: 0x0008, 0xafc: 0xe00d, 0xafd: 0x0008, 0xafe: 0xe00d, 0xaff: 0x0008, // Block 0x2c, offset 0xb00 0xb00: 0x0008, 0xb01: 0x0008, 0xb02: 0x0008, 0xb03: 0x0008, 0xb04: 0x0008, 0xb05: 0x0008, 0xb06: 0x0040, 0xb07: 0x0040, 0xb08: 0xe045, 0xb09: 0xe045, 0xb0a: 0xe045, 0xb0b: 0xe045, 0xb0c: 0xe045, 0xb0d: 0xe045, 0xb0e: 0x0040, 0xb0f: 0x0040, 0xb10: 0x0008, 0xb11: 0x0008, 0xb12: 0x0008, 0xb13: 0x0008, 0xb14: 0x0008, 0xb15: 0x0008, 0xb16: 0x0008, 0xb17: 0x0008, 0xb18: 0x0040, 0xb19: 0xe045, 0xb1a: 0x0040, 0xb1b: 0xe045, 0xb1c: 0x0040, 0xb1d: 0xe045, 0xb1e: 0x0040, 0xb1f: 0xe045, 0xb20: 0x0008, 0xb21: 0x0008, 0xb22: 0x0008, 0xb23: 0x0008, 0xb24: 0x0008, 0xb25: 0x0008, 0xb26: 0x0008, 0xb27: 0x0008, 0xb28: 0xe045, 0xb29: 0xe045, 0xb2a: 0xe045, 0xb2b: 0xe045, 0xb2c: 0xe045, 0xb2d: 0xe045, 0xb2e: 0xe045, 0xb2f: 0xe045, 0xb30: 0x0008, 0xb31: 0x1459, 0xb32: 0x0008, 0xb33: 0x1471, 0xb34: 0x0008, 0xb35: 0x1489, 0xb36: 0x0008, 0xb37: 0x14a1, 0xb38: 0x0008, 0xb39: 0x14b9, 0xb3a: 0x0008, 0xb3b: 0x14d1, 0xb3c: 0x0008, 0xb3d: 0x14e9, 0xb3e: 0x0040, 0xb3f: 0x0040, // Block 0x2d, offset 0xb40 0xb40: 0x1501, 0xb41: 0x1531, 0xb42: 0x1561, 0xb43: 0x1591, 0xb44: 0x15c1, 0xb45: 0x15f1, 0xb46: 0x1621, 0xb47: 0x1651, 0xb48: 0x1501, 0xb49: 0x1531, 0xb4a: 0x1561, 0xb4b: 0x1591, 0xb4c: 0x15c1, 0xb4d: 0x15f1, 0xb4e: 0x1621, 0xb4f: 0x1651, 0xb50: 0x1681, 0xb51: 0x16b1, 0xb52: 0x16e1, 0xb53: 0x1711, 0xb54: 0x1741, 0xb55: 0x1771, 0xb56: 0x17a1, 0xb57: 0x17d1, 0xb58: 0x1681, 0xb59: 0x16b1, 0xb5a: 0x16e1, 0xb5b: 0x1711, 0xb5c: 0x1741, 0xb5d: 0x1771, 0xb5e: 0x17a1, 0xb5f: 0x17d1, 0xb60: 0x1801, 0xb61: 0x1831, 0xb62: 0x1861, 0xb63: 0x1891, 0xb64: 0x18c1, 0xb65: 0x18f1, 0xb66: 0x1921, 0xb67: 0x1951, 0xb68: 0x1801, 0xb69: 0x1831, 0xb6a: 0x1861, 0xb6b: 0x1891, 0xb6c: 0x18c1, 0xb6d: 0x18f1, 0xb6e: 0x1921, 0xb6f: 0x1951, 0xb70: 0x0008, 0xb71: 0x0008, 0xb72: 0x1981, 0xb73: 0x19b1, 0xb74: 0x19d9, 0xb75: 0x0040, 0xb76: 0x0008, 0xb77: 0x1a01, 0xb78: 0xe045, 0xb79: 0xe045, 0xb7a: 0x0665, 0xb7b: 0x1459, 0xb7c: 0x19b1, 0xb7d: 0x067e, 0xb7e: 0x1a31, 0xb7f: 0x069e, // Block 0x2e, offset 0xb80 0xb80: 0x06be, 0xb81: 0x1a4a, 0xb82: 0x1a79, 0xb83: 0x1aa9, 0xb84: 0x1ad1, 0xb85: 0x0040, 0xb86: 0x0008, 0xb87: 0x1af9, 0xb88: 0x06dd, 0xb89: 0x1471, 0xb8a: 0x06f5, 0xb8b: 0x1489, 0xb8c: 0x1aa9, 0xb8d: 0x1b2a, 0xb8e: 0x1b5a, 0xb8f: 0x1b8a, 0xb90: 0x0008, 0xb91: 0x0008, 0xb92: 0x0008, 0xb93: 0x1bb9, 0xb94: 0x0040, 0xb95: 0x0040, 0xb96: 0x0008, 0xb97: 0x0008, 0xb98: 0xe045, 0xb99: 0xe045, 0xb9a: 0x070d, 0xb9b: 0x14a1, 0xb9c: 0x0040, 0xb9d: 0x1bd2, 0xb9e: 0x1c02, 0xb9f: 0x1c32, 0xba0: 0x0008, 0xba1: 0x0008, 0xba2: 0x0008, 0xba3: 0x1c61, 0xba4: 0x0008, 0xba5: 0x0008, 0xba6: 0x0008, 0xba7: 0x0008, 0xba8: 0xe045, 0xba9: 0xe045, 0xbaa: 0x0725, 0xbab: 0x14d1, 0xbac: 0xe04d, 0xbad: 0x1c7a, 0xbae: 0x03d2, 0xbaf: 0x1caa, 0xbb0: 0x0040, 0xbb1: 0x0040, 0xbb2: 0x1cb9, 0xbb3: 0x1ce9, 0xbb4: 0x1d11, 0xbb5: 0x0040, 0xbb6: 0x0008, 0xbb7: 0x1d39, 0xbb8: 0x073d, 0xbb9: 0x14b9, 0xbba: 0x0515, 0xbbb: 0x14e9, 0xbbc: 0x1ce9, 0xbbd: 0x0756, 0xbbe: 0x0776, 0xbbf: 0x0040, // Block 0x2f, offset 0xbc0 0xbc0: 0x000a, 0xbc1: 0x000a, 0xbc2: 0x000a, 0xbc3: 0x000a, 0xbc4: 0x000a, 0xbc5: 0x000a, 0xbc6: 0x000a, 0xbc7: 0x000a, 0xbc8: 0x000a, 0xbc9: 0x000a, 0xbca: 0x000a, 0xbcb: 0x03c0, 0xbcc: 0x0003, 0xbcd: 0x0003, 0xbce: 0x0340, 0xbcf: 0x0b40, 0xbd0: 0x0018, 0xbd1: 0xe00d, 0xbd2: 0x0018, 0xbd3: 0x0018, 0xbd4: 0x0018, 0xbd5: 0x0018, 0xbd6: 0x0018, 0xbd7: 0x0796, 0xbd8: 0x0018, 0xbd9: 0x0018, 0xbda: 0x0018, 0xbdb: 0x0018, 0xbdc: 0x0018, 0xbdd: 0x0018, 0xbde: 0x0018, 0xbdf: 0x0018, 0xbe0: 0x0018, 0xbe1: 0x0018, 0xbe2: 0x0018, 0xbe3: 0x0018, 0xbe4: 0x0040, 0xbe5: 0x0040, 0xbe6: 0x0040, 0xbe7: 0x0018, 0xbe8: 0x0040, 0xbe9: 0x0040, 0xbea: 0x0340, 0xbeb: 0x0340, 0xbec: 0x0340, 0xbed: 0x0340, 0xbee: 0x0340, 0xbef: 0x000a, 0xbf0: 0x0018, 0xbf1: 0x0018, 0xbf2: 0x0018, 0xbf3: 0x1d69, 0xbf4: 0x1da1, 0xbf5: 0x0018, 0xbf6: 0x1df1, 0xbf7: 0x1e29, 0xbf8: 0x0018, 0xbf9: 0x0018, 0xbfa: 0x0018, 0xbfb: 0x0018, 0xbfc: 0x1e7a, 0xbfd: 0x0018, 0xbfe: 0x07b6, 0xbff: 0x0018, // Block 0x30, offset 0xc00 0xc00: 0x0018, 0xc01: 0x0018, 0xc02: 0x0018, 0xc03: 0x0018, 0xc04: 0x0018, 0xc05: 0x0018, 0xc06: 0x0018, 0xc07: 0x1e92, 0xc08: 0x1eaa, 0xc09: 0x1ec2, 0xc0a: 0x0018, 0xc0b: 0x0018, 0xc0c: 0x0018, 0xc0d: 0x0018, 0xc0e: 0x0018, 0xc0f: 0x0018, 0xc10: 0x0018, 0xc11: 0x0018, 0xc12: 0x0018, 0xc13: 0x0018, 0xc14: 0x0018, 0xc15: 0x0018, 0xc16: 0x0018, 0xc17: 0x1ed9, 0xc18: 0x0018, 0xc19: 0x0018, 0xc1a: 0x0018, 0xc1b: 0x0018, 0xc1c: 0x0018, 0xc1d: 0x0018, 0xc1e: 0x0018, 0xc1f: 0x000a, 0xc20: 0x03c0, 0xc21: 0x0340, 0xc22: 0x0340, 0xc23: 0x0340, 0xc24: 0x03c0, 0xc25: 0x0040, 0xc26: 0x0040, 0xc27: 0x0040, 0xc28: 0x0040, 0xc29: 0x0040, 0xc2a: 0x0340, 0xc2b: 0x0340, 0xc2c: 0x0340, 0xc2d: 0x0340, 0xc2e: 0x0340, 0xc2f: 0x0340, 0xc30: 0x1f41, 0xc31: 0x0f41, 0xc32: 0x0040, 0xc33: 0x0040, 0xc34: 0x1f51, 0xc35: 0x1f61, 0xc36: 0x1f71, 0xc37: 0x1f81, 0xc38: 0x1f91, 0xc39: 0x1fa1, 0xc3a: 0x1fb2, 0xc3b: 0x07d5, 0xc3c: 0x1fc2, 0xc3d: 0x1fd2, 0xc3e: 0x1fe2, 0xc3f: 0x0f71, // Block 0x31, offset 0xc40 0xc40: 0x1f41, 0xc41: 0x00c9, 0xc42: 0x0069, 0xc43: 0x0079, 0xc44: 0x1f51, 0xc45: 0x1f61, 0xc46: 0x1f71, 0xc47: 0x1f81, 0xc48: 0x1f91, 0xc49: 0x1fa1, 0xc4a: 0x1fb2, 0xc4b: 0x07ed, 0xc4c: 0x1fc2, 0xc4d: 0x1fd2, 0xc4e: 0x1fe2, 0xc4f: 0x0040, 0xc50: 0x0039, 0xc51: 0x0f09, 0xc52: 0x00d9, 0xc53: 0x0369, 0xc54: 0x0ff9, 0xc55: 0x0249, 0xc56: 0x0f51, 0xc57: 0x0359, 0xc58: 0x0f61, 0xc59: 0x0f71, 0xc5a: 0x0f99, 0xc5b: 0x01d9, 0xc5c: 0x0fa9, 0xc5d: 0x0040, 0xc5e: 0x0040, 0xc5f: 0x0040, 0xc60: 0x0018, 0xc61: 0x0018, 0xc62: 0x0018, 0xc63: 0x0018, 0xc64: 0x0018, 0xc65: 0x0018, 0xc66: 0x0018, 0xc67: 0x0018, 0xc68: 0x1ff1, 0xc69: 0x0018, 0xc6a: 0x0018, 0xc6b: 0x0018, 0xc6c: 0x0018, 0xc6d: 0x0018, 0xc6e: 0x0018, 0xc6f: 0x0018, 0xc70: 0x0018, 0xc71: 0x0018, 0xc72: 0x0018, 0xc73: 0x0018, 0xc74: 0x0018, 0xc75: 0x0018, 0xc76: 0x0018, 0xc77: 0x0018, 0xc78: 0x0018, 0xc79: 0x0018, 0xc7a: 0x0018, 0xc7b: 0x0018, 0xc7c: 0x0018, 0xc7d: 0x0018, 0xc7e: 0x0018, 0xc7f: 0x0018, // Block 0x32, offset 0xc80 0xc80: 0x0806, 0xc81: 0x0826, 0xc82: 0x1159, 0xc83: 0x0845, 0xc84: 0x0018, 0xc85: 0x0866, 0xc86: 0x0886, 0xc87: 0x1011, 0xc88: 0x0018, 0xc89: 0x08a5, 0xc8a: 0x0f31, 0xc8b: 0x0249, 0xc8c: 0x0249, 0xc8d: 0x0249, 0xc8e: 0x0249, 0xc8f: 0x2009, 0xc90: 0x0f41, 0xc91: 0x0f41, 0xc92: 0x0359, 0xc93: 0x0359, 0xc94: 0x0018, 0xc95: 0x0f71, 0xc96: 0x2021, 0xc97: 0x0018, 0xc98: 0x0018, 0xc99: 0x0f99, 0xc9a: 0x2039, 0xc9b: 0x0269, 0xc9c: 0x0269, 0xc9d: 0x0269, 0xc9e: 0x0018, 0xc9f: 0x0018, 0xca0: 0x2049, 0xca1: 0x08c5, 0xca2: 0x2061, 0xca3: 0x0018, 0xca4: 0x13d1, 0xca5: 0x0018, 0xca6: 0x2079, 0xca7: 0x0018, 0xca8: 0x13d1, 0xca9: 0x0018, 0xcaa: 0x0f51, 0xcab: 0x2091, 0xcac: 0x0ee9, 0xcad: 0x1159, 0xcae: 0x0018, 0xcaf: 0x0f09, 0xcb0: 0x0f09, 0xcb1: 0x1199, 0xcb2: 0x0040, 0xcb3: 0x0f61, 0xcb4: 0x00d9, 0xcb5: 0x20a9, 0xcb6: 0x20c1, 0xcb7: 0x20d9, 0xcb8: 0x20f1, 0xcb9: 0x0f41, 0xcba: 0x0018, 0xcbb: 0x08e5, 0xcbc: 0x2109, 0xcbd: 0x10b1, 0xcbe: 0x10b1, 0xcbf: 0x2109, // Block 0x33, offset 0xcc0 0xcc0: 0x0905, 0xcc1: 0x0018, 0xcc2: 0x0018, 0xcc3: 0x0018, 0xcc4: 0x0018, 0xcc5: 0x0ef9, 0xcc6: 0x0ef9, 0xcc7: 0x0f09, 0xcc8: 0x0f41, 0xcc9: 0x0259, 0xcca: 0x0018, 0xccb: 0x0018, 0xccc: 0x0018, 0xccd: 0x0018, 0xcce: 0x0008, 0xccf: 0x0018, 0xcd0: 0x2121, 0xcd1: 0x2151, 0xcd2: 0x2181, 0xcd3: 0x21b9, 0xcd4: 0x21e9, 0xcd5: 0x2219, 0xcd6: 0x2249, 0xcd7: 0x2279, 0xcd8: 0x22a9, 0xcd9: 0x22d9, 0xcda: 0x2309, 0xcdb: 0x2339, 0xcdc: 0x2369, 0xcdd: 0x2399, 0xcde: 0x23c9, 0xcdf: 0x23f9, 0xce0: 0x0f41, 0xce1: 0x2421, 0xce2: 0x091d, 0xce3: 0x2439, 0xce4: 0x1089, 0xce5: 0x2451, 0xce6: 0x093d, 0xce7: 0x2469, 0xce8: 0x2491, 0xce9: 0x0369, 0xcea: 0x24a9, 0xceb: 0x095d, 0xcec: 0x0359, 0xced: 0x1159, 0xcee: 0x0ef9, 0xcef: 0x0f61, 0xcf0: 0x0f41, 0xcf1: 0x2421, 0xcf2: 0x097d, 0xcf3: 0x2439, 0xcf4: 0x1089, 0xcf5: 0x2451, 0xcf6: 0x099d, 0xcf7: 0x2469, 0xcf8: 0x2491, 0xcf9: 0x0369, 0xcfa: 0x24a9, 0xcfb: 0x09bd, 0xcfc: 0x0359, 0xcfd: 0x1159, 0xcfe: 0x0ef9, 0xcff: 0x0f61, // Block 0x34, offset 0xd00 0xd00: 0x0018, 0xd01: 0x0018, 0xd02: 0x0018, 0xd03: 0x0018, 0xd04: 0x0018, 0xd05: 0x0018, 0xd06: 0x0018, 0xd07: 0x0018, 0xd08: 0x0018, 0xd09: 0x0018, 0xd0a: 0x0018, 0xd0b: 0x0040, 0xd0c: 0x0040, 0xd0d: 0x0040, 0xd0e: 0x0040, 0xd0f: 0x0040, 0xd10: 0x0040, 0xd11: 0x0040, 0xd12: 0x0040, 0xd13: 0x0040, 0xd14: 0x0040, 0xd15: 0x0040, 0xd16: 0x0040, 0xd17: 0x0040, 0xd18: 0x0040, 0xd19: 0x0040, 0xd1a: 0x0040, 0xd1b: 0x0040, 0xd1c: 0x0040, 0xd1d: 0x0040, 0xd1e: 0x0040, 0xd1f: 0x0040, 0xd20: 0x00c9, 0xd21: 0x0069, 0xd22: 0x0079, 0xd23: 0x1f51, 0xd24: 0x1f61, 0xd25: 0x1f71, 0xd26: 0x1f81, 0xd27: 0x1f91, 0xd28: 0x1fa1, 0xd29: 0x2601, 0xd2a: 0x2619, 0xd2b: 0x2631, 0xd2c: 0x2649, 0xd2d: 0x2661, 0xd2e: 0x2679, 0xd2f: 0x2691, 0xd30: 0x26a9, 0xd31: 0x26c1, 0xd32: 0x26d9, 0xd33: 0x26f1, 0xd34: 0x0a1e, 0xd35: 0x0a3e, 0xd36: 0x0a5e, 0xd37: 0x0a7e, 0xd38: 0x0a9e, 0xd39: 0x0abe, 0xd3a: 0x0ade, 0xd3b: 0x0afe, 0xd3c: 0x0b1e, 0xd3d: 0x270a, 0xd3e: 0x2732, 0xd3f: 0x275a, // Block 0x35, offset 0xd40 0xd40: 0x2782, 0xd41: 0x27aa, 0xd42: 0x27d2, 0xd43: 0x27fa, 0xd44: 0x2822, 0xd45: 0x284a, 0xd46: 0x2872, 0xd47: 0x289a, 0xd48: 0x0040, 0xd49: 0x0040, 0xd4a: 0x0040, 0xd4b: 0x0040, 0xd4c: 0x0040, 0xd4d: 0x0040, 0xd4e: 0x0040, 0xd4f: 0x0040, 0xd50: 0x0040, 0xd51: 0x0040, 0xd52: 0x0040, 0xd53: 0x0040, 0xd54: 0x0040, 0xd55: 0x0040, 0xd56: 0x0040, 0xd57: 0x0040, 0xd58: 0x0040, 0xd59: 0x0040, 0xd5a: 0x0040, 0xd5b: 0x0040, 0xd5c: 0x0b3e, 0xd5d: 0x0b5e, 0xd5e: 0x0b7e, 0xd5f: 0x0b9e, 0xd60: 0x0bbe, 0xd61: 0x0bde, 0xd62: 0x0bfe, 0xd63: 0x0c1e, 0xd64: 0x0c3e, 0xd65: 0x0c5e, 0xd66: 0x0c7e, 0xd67: 0x0c9e, 0xd68: 0x0cbe, 0xd69: 0x0cde, 0xd6a: 0x0cfe, 0xd6b: 0x0d1e, 0xd6c: 0x0d3e, 0xd6d: 0x0d5e, 0xd6e: 0x0d7e, 0xd6f: 0x0d9e, 0xd70: 0x0dbe, 0xd71: 0x0dde, 0xd72: 0x0dfe, 0xd73: 0x0e1e, 0xd74: 0x0e3e, 0xd75: 0x0e5e, 0xd76: 0x0039, 0xd77: 0x0ee9, 0xd78: 0x1159, 0xd79: 0x0ef9, 0xd7a: 0x0f09, 0xd7b: 0x1199, 0xd7c: 0x0f31, 0xd7d: 0x0249, 0xd7e: 0x0f41, 0xd7f: 0x0259, // Block 0x36, offset 0xd80 0xd80: 0x0f51, 0xd81: 0x0359, 0xd82: 0x0f61, 0xd83: 0x0f71, 0xd84: 0x00d9, 0xd85: 0x0f99, 0xd86: 0x2039, 0xd87: 0x0269, 0xd88: 0x01d9, 0xd89: 0x0fa9, 0xd8a: 0x0fb9, 0xd8b: 0x1089, 0xd8c: 0x0279, 0xd8d: 0x0369, 0xd8e: 0x0289, 0xd8f: 0x13d1, 0xd90: 0x0039, 0xd91: 0x0ee9, 0xd92: 0x1159, 0xd93: 0x0ef9, 0xd94: 0x0f09, 0xd95: 0x1199, 0xd96: 0x0f31, 0xd97: 0x0249, 0xd98: 0x0f41, 0xd99: 0x0259, 0xd9a: 0x0f51, 0xd9b: 0x0359, 0xd9c: 0x0f61, 0xd9d: 0x0f71, 0xd9e: 0x00d9, 0xd9f: 0x0f99, 0xda0: 0x2039, 0xda1: 0x0269, 0xda2: 0x01d9, 0xda3: 0x0fa9, 0xda4: 0x0fb9, 0xda5: 0x1089, 0xda6: 0x0279, 0xda7: 0x0369, 0xda8: 0x0289, 0xda9: 0x13d1, 0xdaa: 0x1f41, 0xdab: 0x0018, 0xdac: 0x0018, 0xdad: 0x0018, 0xdae: 0x0018, 0xdaf: 0x0018, 0xdb0: 0x0018, 0xdb1: 0x0018, 0xdb2: 0x0018, 0xdb3: 0x0018, 0xdb4: 0x0018, 0xdb5: 0x0018, 0xdb6: 0x0018, 0xdb7: 0x0018, 0xdb8: 0x0018, 0xdb9: 0x0018, 0xdba: 0x0018, 0xdbb: 0x0018, 0xdbc: 0x0018, 0xdbd: 0x0018, 0xdbe: 0x0018, 0xdbf: 0x0018, // Block 0x37, offset 0xdc0 0xdc0: 0x0008, 0xdc1: 0x0008, 0xdc2: 0x0008, 0xdc3: 0x0008, 0xdc4: 0x0008, 0xdc5: 0x0008, 0xdc6: 0x0008, 0xdc7: 0x0008, 0xdc8: 0x0008, 0xdc9: 0x0008, 0xdca: 0x0008, 0xdcb: 0x0008, 0xdcc: 0x0008, 0xdcd: 0x0008, 0xdce: 0x0008, 0xdcf: 0x0008, 0xdd0: 0x0008, 0xdd1: 0x0008, 0xdd2: 0x0008, 0xdd3: 0x0008, 0xdd4: 0x0008, 0xdd5: 0x0008, 0xdd6: 0x0008, 0xdd7: 0x0008, 0xdd8: 0x0008, 0xdd9: 0x0008, 0xdda: 0x0008, 0xddb: 0x0008, 0xddc: 0x0008, 0xddd: 0x0008, 0xdde: 0x0008, 0xddf: 0x0040, 0xde0: 0xe00d, 0xde1: 0x0008, 0xde2: 0x2971, 0xde3: 0x0ed5, 0xde4: 0x2989, 0xde5: 0x0008, 0xde6: 0x0008, 0xde7: 0xe07d, 0xde8: 0x0008, 0xde9: 0xe01d, 0xdea: 0x0008, 0xdeb: 0xe03d, 0xdec: 0x0008, 0xded: 0x0fe1, 0xdee: 0x1281, 0xdef: 0x0fc9, 0xdf0: 0x1141, 0xdf1: 0x0008, 0xdf2: 0xe00d, 0xdf3: 0x0008, 0xdf4: 0x0008, 0xdf5: 0xe01d, 0xdf6: 0x0008, 0xdf7: 0x0008, 0xdf8: 0x0008, 0xdf9: 0x0008, 0xdfa: 0x0008, 0xdfb: 0x0008, 0xdfc: 0x0259, 0xdfd: 0x1089, 0xdfe: 0x29a1, 0xdff: 0x29b9, // Block 0x38, offset 0xe00 0xe00: 0xe00d, 0xe01: 0x0008, 0xe02: 0xe00d, 0xe03: 0x0008, 0xe04: 0xe00d, 0xe05: 0x0008, 0xe06: 0xe00d, 0xe07: 0x0008, 0xe08: 0xe00d, 0xe09: 0x0008, 0xe0a: 0xe00d, 0xe0b: 0x0008, 0xe0c: 0xe00d, 0xe0d: 0x0008, 0xe0e: 0xe00d, 0xe0f: 0x0008, 0xe10: 0xe00d, 0xe11: 0x0008, 0xe12: 0xe00d, 0xe13: 0x0008, 0xe14: 0xe00d, 0xe15: 0x0008, 0xe16: 0xe00d, 0xe17: 0x0008, 0xe18: 0xe00d, 0xe19: 0x0008, 0xe1a: 0xe00d, 0xe1b: 0x0008, 0xe1c: 0xe00d, 0xe1d: 0x0008, 0xe1e: 0xe00d, 0xe1f: 0x0008, 0xe20: 0xe00d, 0xe21: 0x0008, 0xe22: 0xe00d, 0xe23: 0x0008, 0xe24: 0x0008, 0xe25: 0x0018, 0xe26: 0x0018, 0xe27: 0x0018, 0xe28: 0x0018, 0xe29: 0x0018, 0xe2a: 0x0018, 0xe2b: 0xe03d, 0xe2c: 0x0008, 0xe2d: 0xe01d, 0xe2e: 0x0008, 0xe2f: 0x3308, 0xe30: 0x3308, 0xe31: 0x3308, 0xe32: 0xe00d, 0xe33: 0x0008, 0xe34: 0x0040, 0xe35: 0x0040, 0xe36: 0x0040, 0xe37: 0x0040, 0xe38: 0x0040, 0xe39: 0x0018, 0xe3a: 0x0018, 0xe3b: 0x0018, 0xe3c: 0x0018, 0xe3d: 0x0018, 0xe3e: 0x0018, 0xe3f: 0x0018, // Block 0x39, offset 0xe40 0xe40: 0x2715, 0xe41: 0x2735, 0xe42: 0x2755, 0xe43: 0x2775, 0xe44: 0x2795, 0xe45: 0x27b5, 0xe46: 0x27d5, 0xe47: 0x27f5, 0xe48: 0x2815, 0xe49: 0x2835, 0xe4a: 0x2855, 0xe4b: 0x2875, 0xe4c: 0x2895, 0xe4d: 0x28b5, 0xe4e: 0x28d5, 0xe4f: 0x28f5, 0xe50: 0x2915, 0xe51: 0x2935, 0xe52: 0x2955, 0xe53: 0x2975, 0xe54: 0x2995, 0xe55: 0x29b5, 0xe56: 0x0040, 0xe57: 0x0040, 0xe58: 0x0040, 0xe59: 0x0040, 0xe5a: 0x0040, 0xe5b: 0x0040, 0xe5c: 0x0040, 0xe5d: 0x0040, 0xe5e: 0x0040, 0xe5f: 0x0040, 0xe60: 0x0040, 0xe61: 0x0040, 0xe62: 0x0040, 0xe63: 0x0040, 0xe64: 0x0040, 0xe65: 0x0040, 0xe66: 0x0040, 0xe67: 0x0040, 0xe68: 0x0040, 0xe69: 0x0040, 0xe6a: 0x0040, 0xe6b: 0x0040, 0xe6c: 0x0040, 0xe6d: 0x0040, 0xe6e: 0x0040, 0xe6f: 0x0040, 0xe70: 0x0040, 0xe71: 0x0040, 0xe72: 0x0040, 0xe73: 0x0040, 0xe74: 0x0040, 0xe75: 0x0040, 0xe76: 0x0040, 0xe77: 0x0040, 0xe78: 0x0040, 0xe79: 0x0040, 0xe7a: 0x0040, 0xe7b: 0x0040, 0xe7c: 0x0040, 0xe7d: 0x0040, 0xe7e: 0x0040, 0xe7f: 0x0040, // Block 0x3a, offset 0xe80 0xe80: 0x000a, 0xe81: 0x0018, 0xe82: 0x29d1, 0xe83: 0x0018, 0xe84: 0x0018, 0xe85: 0x0008, 0xe86: 0x0008, 0xe87: 0x0008, 0xe88: 0x0018, 0xe89: 0x0018, 0xe8a: 0x0018, 0xe8b: 0x0018, 0xe8c: 0x0018, 0xe8d: 0x0018, 0xe8e: 0x0018, 0xe8f: 0x0018, 0xe90: 0x0018, 0xe91: 0x0018, 0xe92: 0x0018, 0xe93: 0x0018, 0xe94: 0x0018, 0xe95: 0x0018, 0xe96: 0x0018, 0xe97: 0x0018, 0xe98: 0x0018, 0xe99: 0x0018, 0xe9a: 0x0018, 0xe9b: 0x0018, 0xe9c: 0x0018, 0xe9d: 0x0018, 0xe9e: 0x0018, 0xe9f: 0x0018, 0xea0: 0x0018, 0xea1: 0x0018, 0xea2: 0x0018, 0xea3: 0x0018, 0xea4: 0x0018, 0xea5: 0x0018, 0xea6: 0x0018, 0xea7: 0x0018, 0xea8: 0x0018, 0xea9: 0x0018, 0xeaa: 0x3308, 0xeab: 0x3308, 0xeac: 0x3308, 0xead: 0x3308, 0xeae: 0x3018, 0xeaf: 0x3018, 0xeb0: 0x0018, 0xeb1: 0x0018, 0xeb2: 0x0018, 0xeb3: 0x0018, 0xeb4: 0x0018, 0xeb5: 0x0018, 0xeb6: 0xe125, 0xeb7: 0x0018, 0xeb8: 0x29d5, 0xeb9: 0x29f5, 0xeba: 0x2a15, 0xebb: 0x0018, 0xebc: 0x0008, 0xebd: 0x0018, 0xebe: 0x0018, 0xebf: 0x0018, // Block 0x3b, offset 0xec0 0xec0: 0x2b55, 0xec1: 0x2b75, 0xec2: 0x2b95, 0xec3: 0x2bb5, 0xec4: 0x2bd5, 0xec5: 0x2bf5, 0xec6: 0x2bf5, 0xec7: 0x2bf5, 0xec8: 0x2c15, 0xec9: 0x2c15, 0xeca: 0x2c15, 0xecb: 0x2c15, 0xecc: 0x2c35, 0xecd: 0x2c35, 0xece: 0x2c35, 0xecf: 0x2c55, 0xed0: 0x2c75, 0xed1: 0x2c75, 0xed2: 0x2a95, 0xed3: 0x2a95, 0xed4: 0x2c75, 0xed5: 0x2c75, 0xed6: 0x2c95, 0xed7: 0x2c95, 0xed8: 0x2c75, 0xed9: 0x2c75, 0xeda: 0x2a95, 0xedb: 0x2a95, 0xedc: 0x2c75, 0xedd: 0x2c75, 0xede: 0x2c55, 0xedf: 0x2c55, 0xee0: 0x2cb5, 0xee1: 0x2cb5, 0xee2: 0x2cd5, 0xee3: 0x2cd5, 0xee4: 0x0040, 0xee5: 0x2cf5, 0xee6: 0x2d15, 0xee7: 0x2d35, 0xee8: 0x2d35, 0xee9: 0x2d55, 0xeea: 0x2d75, 0xeeb: 0x2d95, 0xeec: 0x2db5, 0xeed: 0x2dd5, 0xeee: 0x2df5, 0xeef: 0x2e15, 0xef0: 0x2e35, 0xef1: 0x2e55, 0xef2: 0x2e55, 0xef3: 0x2e75, 0xef4: 0x2e95, 0xef5: 0x2e95, 0xef6: 0x2eb5, 0xef7: 0x2ed5, 0xef8: 0x2e75, 0xef9: 0x2ef5, 0xefa: 0x2f15, 0xefb: 0x2ef5, 0xefc: 0x2e75, 0xefd: 0x2f35, 0xefe: 0x2f55, 0xeff: 0x2f75, // Block 0x3c, offset 0xf00 0xf00: 0x2f95, 0xf01: 0x2fb5, 0xf02: 0x2d15, 0xf03: 0x2cf5, 0xf04: 0x2fd5, 0xf05: 0x2ff5, 0xf06: 0x3015, 0xf07: 0x3035, 0xf08: 0x3055, 0xf09: 0x3075, 0xf0a: 0x3095, 0xf0b: 0x30b5, 0xf0c: 0x30d5, 0xf0d: 0x30f5, 0xf0e: 0x3115, 0xf0f: 0x0040, 0xf10: 0x0018, 0xf11: 0x0018, 0xf12: 0x3135, 0xf13: 0x3155, 0xf14: 0x3175, 0xf15: 0x3195, 0xf16: 0x31b5, 0xf17: 0x31d5, 0xf18: 0x31f5, 0xf19: 0x3215, 0xf1a: 0x3235, 0xf1b: 0x3255, 0xf1c: 0x3175, 0xf1d: 0x3275, 0xf1e: 0x3295, 0xf1f: 0x32b5, 0xf20: 0x0008, 0xf21: 0x0008, 0xf22: 0x0008, 0xf23: 0x0008, 0xf24: 0x0008, 0xf25: 0x0008, 0xf26: 0x0008, 0xf27: 0x0008, 0xf28: 0x0008, 0xf29: 0x0008, 0xf2a: 0x0008, 0xf2b: 0x0008, 0xf2c: 0x0008, 0xf2d: 0x0008, 0xf2e: 0x0008, 0xf2f: 0x0008, 0xf30: 0x0008, 0xf31: 0x0008, 0xf32: 0x0008, 0xf33: 0x0008, 0xf34: 0x0008, 0xf35: 0x0008, 0xf36: 0x0008, 0xf37: 0x0008, 0xf38: 0x0008, 0xf39: 0x0008, 0xf3a: 0x0008, 0xf3b: 0x0040, 0xf3c: 0x0040, 0xf3d: 0x0040, 0xf3e: 0x0040, 0xf3f: 0x0040, // Block 0x3d, offset 0xf40 0xf40: 0x36a2, 0xf41: 0x36d2, 0xf42: 0x3702, 0xf43: 0x3732, 0xf44: 0x32d5, 0xf45: 0x32f5, 0xf46: 0x3315, 0xf47: 0x3335, 0xf48: 0x0018, 0xf49: 0x0018, 0xf4a: 0x0018, 0xf4b: 0x0018, 0xf4c: 0x0018, 0xf4d: 0x0018, 0xf4e: 0x0018, 0xf4f: 0x0018, 0xf50: 0x3355, 0xf51: 0x3761, 0xf52: 0x3779, 0xf53: 0x3791, 0xf54: 0x37a9, 0xf55: 0x37c1, 0xf56: 0x37d9, 0xf57: 0x37f1, 0xf58: 0x3809, 0xf59: 0x3821, 0xf5a: 0x3839, 0xf5b: 0x3851, 0xf5c: 0x3869, 0xf5d: 0x3881, 0xf5e: 0x3899, 0xf5f: 0x38b1, 0xf60: 0x3375, 0xf61: 0x3395, 0xf62: 0x33b5, 0xf63: 0x33d5, 0xf64: 0x33f5, 0xf65: 0x33f5, 0xf66: 0x3415, 0xf67: 0x3435, 0xf68: 0x3455, 0xf69: 0x3475, 0xf6a: 0x3495, 0xf6b: 0x34b5, 0xf6c: 0x34d5, 0xf6d: 0x34f5, 0xf6e: 0x3515, 0xf6f: 0x3535, 0xf70: 0x3555, 0xf71: 0x3575, 0xf72: 0x3595, 0xf73: 0x35b5, 0xf74: 0x35d5, 0xf75: 0x35f5, 0xf76: 0x3615, 0xf77: 0x3635, 0xf78: 0x3655, 0xf79: 0x3675, 0xf7a: 0x3695, 0xf7b: 0x36b5, 0xf7c: 0x38c9, 0xf7d: 0x3901, 0xf7e: 0x36d5, 0xf7f: 0x0018, // Block 0x3e, offset 0xf80 0xf80: 0x36f5, 0xf81: 0x3715, 0xf82: 0x3735, 0xf83: 0x3755, 0xf84: 0x3775, 0xf85: 0x3795, 0xf86: 0x37b5, 0xf87: 0x37d5, 0xf88: 0x37f5, 0xf89: 0x3815, 0xf8a: 0x3835, 0xf8b: 0x3855, 0xf8c: 0x3875, 0xf8d: 0x3895, 0xf8e: 0x38b5, 0xf8f: 0x38d5, 0xf90: 0x38f5, 0xf91: 0x3915, 0xf92: 0x3935, 0xf93: 0x3955, 0xf94: 0x3975, 0xf95: 0x3995, 0xf96: 0x39b5, 0xf97: 0x39d5, 0xf98: 0x39f5, 0xf99: 0x3a15, 0xf9a: 0x3a35, 0xf9b: 0x3a55, 0xf9c: 0x3a75, 0xf9d: 0x3a95, 0xf9e: 0x3ab5, 0xf9f: 0x3ad5, 0xfa0: 0x3af5, 0xfa1: 0x3b15, 0xfa2: 0x3b35, 0xfa3: 0x3b55, 0xfa4: 0x3b75, 0xfa5: 0x3b95, 0xfa6: 0x1295, 0xfa7: 0x3bb5, 0xfa8: 0x3bd5, 0xfa9: 0x3bf5, 0xfaa: 0x3c15, 0xfab: 0x3c35, 0xfac: 0x3c55, 0xfad: 0x3c75, 0xfae: 0x23b5, 0xfaf: 0x3c95, 0xfb0: 0x3cb5, 0xfb1: 0x3939, 0xfb2: 0x3951, 0xfb3: 0x3969, 0xfb4: 0x3981, 0xfb5: 0x3999, 0xfb6: 0x39b1, 0xfb7: 0x39c9, 0xfb8: 0x39e1, 0xfb9: 0x39f9, 0xfba: 0x3a11, 0xfbb: 0x3a29, 0xfbc: 0x3a41, 0xfbd: 0x3a59, 0xfbe: 0x3a71, 0xfbf: 0x3a89, // Block 0x3f, offset 0xfc0 0xfc0: 0x3aa1, 0xfc1: 0x3ac9, 0xfc2: 0x3af1, 0xfc3: 0x3b19, 0xfc4: 0x3b41, 0xfc5: 0x3b69, 0xfc6: 0x3b91, 0xfc7: 0x3bb9, 0xfc8: 0x3be1, 0xfc9: 0x3c09, 0xfca: 0x3c39, 0xfcb: 0x3c69, 0xfcc: 0x3c99, 0xfcd: 0x3cd5, 0xfce: 0x3cb1, 0xfcf: 0x3cf5, 0xfd0: 0x3d15, 0xfd1: 0x3d2d, 0xfd2: 0x3d45, 0xfd3: 0x3d5d, 0xfd4: 0x3d75, 0xfd5: 0x3d75, 0xfd6: 0x3d5d, 0xfd7: 0x3d8d, 0xfd8: 0x07d5, 0xfd9: 0x3da5, 0xfda: 0x3dbd, 0xfdb: 0x3dd5, 0xfdc: 0x3ded, 0xfdd: 0x3e05, 0xfde: 0x3e1d, 0xfdf: 0x3e35, 0xfe0: 0x3e4d, 0xfe1: 0x3e65, 0xfe2: 0x3e7d, 0xfe3: 0x3e95, 0xfe4: 0x3ead, 0xfe5: 0x3ead, 0xfe6: 0x3ec5, 0xfe7: 0x3ec5, 0xfe8: 0x3edd, 0xfe9: 0x3edd, 0xfea: 0x3ef5, 0xfeb: 0x3f0d, 0xfec: 0x3f25, 0xfed: 0x3f3d, 0xfee: 0x3f55, 0xfef: 0x3f55, 0xff0: 0x3f6d, 0xff1: 0x3f6d, 0xff2: 0x3f6d, 0xff3: 0x3f85, 0xff4: 0x3f9d, 0xff5: 0x3fb5, 0xff6: 0x3fcd, 0xff7: 0x3fb5, 0xff8: 0x3fe5, 0xff9: 0x3ffd, 0xffa: 0x3f85, 0xffb: 0x4015, 0xffc: 0x402d, 0xffd: 0x402d, 0xffe: 0x402d, 0xfff: 0x0040, // Block 0x40, offset 0x1000 0x1000: 0x3cc9, 0x1001: 0x3d31, 0x1002: 0x3d99, 0x1003: 0x3e01, 0x1004: 0x3e51, 0x1005: 0x3eb9, 0x1006: 0x3f09, 0x1007: 0x3f59, 0x1008: 0x3fd9, 0x1009: 0x4041, 0x100a: 0x4091, 0x100b: 0x40e1, 0x100c: 0x4131, 0x100d: 0x4199, 0x100e: 0x4201, 0x100f: 0x4251, 0x1010: 0x42a1, 0x1011: 0x42d9, 0x1012: 0x4329, 0x1013: 0x4391, 0x1014: 0x43f9, 0x1015: 0x4431, 0x1016: 0x44b1, 0x1017: 0x4549, 0x1018: 0x45c9, 0x1019: 0x4619, 0x101a: 0x4699, 0x101b: 0x4719, 0x101c: 0x4781, 0x101d: 0x47d1, 0x101e: 0x4821, 0x101f: 0x4871, 0x1020: 0x48d9, 0x1021: 0x4959, 0x1022: 0x49c1, 0x1023: 0x4a11, 0x1024: 0x4a61, 0x1025: 0x4ab1, 0x1026: 0x4ae9, 0x1027: 0x4b21, 0x1028: 0x4b59, 0x1029: 0x4b91, 0x102a: 0x4be1, 0x102b: 0x4c31, 0x102c: 0x4cb1, 0x102d: 0x4d01, 0x102e: 0x4d69, 0x102f: 0x4de9, 0x1030: 0x4e39, 0x1031: 0x4e71, 0x1032: 0x4ea9, 0x1033: 0x4f29, 0x1034: 0x4f91, 0x1035: 0x5011, 0x1036: 0x5061, 0x1037: 0x50e1, 0x1038: 0x5119, 0x1039: 0x5169, 0x103a: 0x51b9, 0x103b: 0x5209, 0x103c: 0x5259, 0x103d: 0x52a9, 0x103e: 0x5311, 0x103f: 0x5361, // Block 0x41, offset 0x1040 0x1040: 0x5399, 0x1041: 0x53e9, 0x1042: 0x5439, 0x1043: 0x5489, 0x1044: 0x54f1, 0x1045: 0x5541, 0x1046: 0x5591, 0x1047: 0x55e1, 0x1048: 0x5661, 0x1049: 0x56c9, 0x104a: 0x5701, 0x104b: 0x5781, 0x104c: 0x57b9, 0x104d: 0x5821, 0x104e: 0x5889, 0x104f: 0x58d9, 0x1050: 0x5929, 0x1051: 0x5979, 0x1052: 0x59e1, 0x1053: 0x5a19, 0x1054: 0x5a69, 0x1055: 0x5ad1, 0x1056: 0x5b09, 0x1057: 0x5b89, 0x1058: 0x5bd9, 0x1059: 0x5c01, 0x105a: 0x5c29, 0x105b: 0x5c51, 0x105c: 0x5c79, 0x105d: 0x5ca1, 0x105e: 0x5cc9, 0x105f: 0x5cf1, 0x1060: 0x5d19, 0x1061: 0x5d41, 0x1062: 0x5d69, 0x1063: 0x5d99, 0x1064: 0x5dc9, 0x1065: 0x5df9, 0x1066: 0x5e29, 0x1067: 0x5e59, 0x1068: 0x5e89, 0x1069: 0x5eb9, 0x106a: 0x5ee9, 0x106b: 0x5f19, 0x106c: 0x5f49, 0x106d: 0x5f79, 0x106e: 0x5fa9, 0x106f: 0x5fd9, 0x1070: 0x6009, 0x1071: 0x4045, 0x1072: 0x6039, 0x1073: 0x6051, 0x1074: 0x4065, 0x1075: 0x6069, 0x1076: 0x6081, 0x1077: 0x6099, 0x1078: 0x4085, 0x1079: 0x4085, 0x107a: 0x60b1, 0x107b: 0x60c9, 0x107c: 0x6101, 0x107d: 0x6139, 0x107e: 0x6171, 0x107f: 0x61a9, // Block 0x42, offset 0x1080 0x1080: 0x6211, 0x1081: 0x6229, 0x1082: 0x40a5, 0x1083: 0x6241, 0x1084: 0x6259, 0x1085: 0x6271, 0x1086: 0x6289, 0x1087: 0x62a1, 0x1088: 0x40c5, 0x1089: 0x62b9, 0x108a: 0x62e1, 0x108b: 0x62f9, 0x108c: 0x40e5, 0x108d: 0x40e5, 0x108e: 0x6311, 0x108f: 0x6329, 0x1090: 0x6341, 0x1091: 0x4105, 0x1092: 0x4125, 0x1093: 0x4145, 0x1094: 0x4165, 0x1095: 0x4185, 0x1096: 0x6359, 0x1097: 0x6371, 0x1098: 0x6389, 0x1099: 0x63a1, 0x109a: 0x63b9, 0x109b: 0x41a5, 0x109c: 0x63d1, 0x109d: 0x63e9, 0x109e: 0x6401, 0x109f: 0x41c5, 0x10a0: 0x41e5, 0x10a1: 0x6419, 0x10a2: 0x4205, 0x10a3: 0x4225, 0x10a4: 0x4245, 0x10a5: 0x6431, 0x10a6: 0x4265, 0x10a7: 0x6449, 0x10a8: 0x6479, 0x10a9: 0x6211, 0x10aa: 0x4285, 0x10ab: 0x42a5, 0x10ac: 0x42c5, 0x10ad: 0x42e5, 0x10ae: 0x64b1, 0x10af: 0x64f1, 0x10b0: 0x6539, 0x10b1: 0x6551, 0x10b2: 0x4305, 0x10b3: 0x6569, 0x10b4: 0x6581, 0x10b5: 0x6599, 0x10b6: 0x4325, 0x10b7: 0x65b1, 0x10b8: 0x65c9, 0x10b9: 0x65b1, 0x10ba: 0x65e1, 0x10bb: 0x65f9, 0x10bc: 0x4345, 0x10bd: 0x6611, 0x10be: 0x6629, 0x10bf: 0x6611, // Block 0x43, offset 0x10c0 0x10c0: 0x4365, 0x10c1: 0x4385, 0x10c2: 0x0040, 0x10c3: 0x6641, 0x10c4: 0x6659, 0x10c5: 0x6671, 0x10c6: 0x6689, 0x10c7: 0x0040, 0x10c8: 0x66c1, 0x10c9: 0x66d9, 0x10ca: 0x66f1, 0x10cb: 0x6709, 0x10cc: 0x6721, 0x10cd: 0x6739, 0x10ce: 0x6401, 0x10cf: 0x6751, 0x10d0: 0x6769, 0x10d1: 0x6781, 0x10d2: 0x43a5, 0x10d3: 0x6799, 0x10d4: 0x6289, 0x10d5: 0x43c5, 0x10d6: 0x43e5, 0x10d7: 0x67b1, 0x10d8: 0x0040, 0x10d9: 0x4405, 0x10da: 0x67c9, 0x10db: 0x67e1, 0x10dc: 0x67f9, 0x10dd: 0x6811, 0x10de: 0x6829, 0x10df: 0x6859, 0x10e0: 0x6889, 0x10e1: 0x68b1, 0x10e2: 0x68d9, 0x10e3: 0x6901, 0x10e4: 0x6929, 0x10e5: 0x6951, 0x10e6: 0x6979, 0x10e7: 0x69a1, 0x10e8: 0x69c9, 0x10e9: 0x69f1, 0x10ea: 0x6a21, 0x10eb: 0x6a51, 0x10ec: 0x6a81, 0x10ed: 0x6ab1, 0x10ee: 0x6ae1, 0x10ef: 0x6b11, 0x10f0: 0x6b41, 0x10f1: 0x6b71, 0x10f2: 0x6ba1, 0x10f3: 0x6bd1, 0x10f4: 0x6c01, 0x10f5: 0x6c31, 0x10f6: 0x6c61, 0x10f7: 0x6c91, 0x10f8: 0x6cc1, 0x10f9: 0x6cf1, 0x10fa: 0x6d21, 0x10fb: 0x6d51, 0x10fc: 0x6d81, 0x10fd: 0x6db1, 0x10fe: 0x6de1, 0x10ff: 0x4425, // Block 0x44, offset 0x1100 0x1100: 0xe00d, 0x1101: 0x0008, 0x1102: 0xe00d, 0x1103: 0x0008, 0x1104: 0xe00d, 0x1105: 0x0008, 0x1106: 0xe00d, 0x1107: 0x0008, 0x1108: 0xe00d, 0x1109: 0x0008, 0x110a: 0xe00d, 0x110b: 0x0008, 0x110c: 0xe00d, 0x110d: 0x0008, 0x110e: 0xe00d, 0x110f: 0x0008, 0x1110: 0xe00d, 0x1111: 0x0008, 0x1112: 0xe00d, 0x1113: 0x0008, 0x1114: 0xe00d, 0x1115: 0x0008, 0x1116: 0xe00d, 0x1117: 0x0008, 0x1118: 0xe00d, 0x1119: 0x0008, 0x111a: 0xe00d, 0x111b: 0x0008, 0x111c: 0xe00d, 0x111d: 0x0008, 0x111e: 0xe00d, 0x111f: 0x0008, 0x1120: 0xe00d, 0x1121: 0x0008, 0x1122: 0xe00d, 0x1123: 0x0008, 0x1124: 0xe00d, 0x1125: 0x0008, 0x1126: 0xe00d, 0x1127: 0x0008, 0x1128: 0xe00d, 0x1129: 0x0008, 0x112a: 0xe00d, 0x112b: 0x0008, 0x112c: 0xe00d, 0x112d: 0x0008, 0x112e: 0x0008, 0x112f: 0x3308, 0x1130: 0x3318, 0x1131: 0x3318, 0x1132: 0x3318, 0x1133: 0x0018, 0x1134: 0x3308, 0x1135: 0x3308, 0x1136: 0x3308, 0x1137: 0x3308, 0x1138: 0x3308, 0x1139: 0x3308, 0x113a: 0x3308, 0x113b: 0x3308, 0x113c: 0x3308, 0x113d: 0x3308, 0x113e: 0x0018, 0x113f: 0x0008, // Block 0x45, offset 0x1140 0x1140: 0xe00d, 0x1141: 0x0008, 0x1142: 0xe00d, 0x1143: 0x0008, 0x1144: 0xe00d, 0x1145: 0x0008, 0x1146: 0xe00d, 0x1147: 0x0008, 0x1148: 0xe00d, 0x1149: 0x0008, 0x114a: 0xe00d, 0x114b: 0x0008, 0x114c: 0xe00d, 0x114d: 0x0008, 0x114e: 0xe00d, 0x114f: 0x0008, 0x1150: 0xe00d, 0x1151: 0x0008, 0x1152: 0xe00d, 0x1153: 0x0008, 0x1154: 0xe00d, 0x1155: 0x0008, 0x1156: 0xe00d, 0x1157: 0x0008, 0x1158: 0xe00d, 0x1159: 0x0008, 0x115a: 0xe00d, 0x115b: 0x0008, 0x115c: 0x0ea1, 0x115d: 0x6e11, 0x115e: 0x3308, 0x115f: 0x3308, 0x1160: 0x0008, 0x1161: 0x0008, 0x1162: 0x0008, 0x1163: 0x0008, 0x1164: 0x0008, 0x1165: 0x0008, 0x1166: 0x0008, 0x1167: 0x0008, 0x1168: 0x0008, 0x1169: 0x0008, 0x116a: 0x0008, 0x116b: 0x0008, 0x116c: 0x0008, 0x116d: 0x0008, 0x116e: 0x0008, 0x116f: 0x0008, 0x1170: 0x0008, 0x1171: 0x0008, 0x1172: 0x0008, 0x1173: 0x0008, 0x1174: 0x0008, 0x1175: 0x0008, 0x1176: 0x0008, 0x1177: 0x0008, 0x1178: 0x0008, 0x1179: 0x0008, 0x117a: 0x0008, 0x117b: 0x0008, 0x117c: 0x0008, 0x117d: 0x0008, 0x117e: 0x0008, 0x117f: 0x0008, // Block 0x46, offset 0x1180 0x1180: 0x0018, 0x1181: 0x0018, 0x1182: 0x0018, 0x1183: 0x0018, 0x1184: 0x0018, 0x1185: 0x0018, 0x1186: 0x0018, 0x1187: 0x0018, 0x1188: 0x0018, 0x1189: 0x0018, 0x118a: 0x0018, 0x118b: 0x0018, 0x118c: 0x0018, 0x118d: 0x0018, 0x118e: 0x0018, 0x118f: 0x0018, 0x1190: 0x0018, 0x1191: 0x0018, 0x1192: 0x0018, 0x1193: 0x0018, 0x1194: 0x0018, 0x1195: 0x0018, 0x1196: 0x0018, 0x1197: 0x0008, 0x1198: 0x0008, 0x1199: 0x0008, 0x119a: 0x0008, 0x119b: 0x0008, 0x119c: 0x0008, 0x119d: 0x0008, 0x119e: 0x0008, 0x119f: 0x0008, 0x11a0: 0x0018, 0x11a1: 0x0018, 0x11a2: 0xe00d, 0x11a3: 0x0008, 0x11a4: 0xe00d, 0x11a5: 0x0008, 0x11a6: 0xe00d, 0x11a7: 0x0008, 0x11a8: 0xe00d, 0x11a9: 0x0008, 0x11aa: 0xe00d, 0x11ab: 0x0008, 0x11ac: 0xe00d, 0x11ad: 0x0008, 0x11ae: 0xe00d, 0x11af: 0x0008, 0x11b0: 0x0008, 0x11b1: 0x0008, 0x11b2: 0xe00d, 0x11b3: 0x0008, 0x11b4: 0xe00d, 0x11b5: 0x0008, 0x11b6: 0xe00d, 0x11b7: 0x0008, 0x11b8: 0xe00d, 0x11b9: 0x0008, 0x11ba: 0xe00d, 0x11bb: 0x0008, 0x11bc: 0xe00d, 0x11bd: 0x0008, 0x11be: 0xe00d, 0x11bf: 0x0008, // Block 0x47, offset 0x11c0 0x11c0: 0xe00d, 0x11c1: 0x0008, 0x11c2: 0xe00d, 0x11c3: 0x0008, 0x11c4: 0xe00d, 0x11c5: 0x0008, 0x11c6: 0xe00d, 0x11c7: 0x0008, 0x11c8: 0xe00d, 0x11c9: 0x0008, 0x11ca: 0xe00d, 0x11cb: 0x0008, 0x11cc: 0xe00d, 0x11cd: 0x0008, 0x11ce: 0xe00d, 0x11cf: 0x0008, 0x11d0: 0xe00d, 0x11d1: 0x0008, 0x11d2: 0xe00d, 0x11d3: 0x0008, 0x11d4: 0xe00d, 0x11d5: 0x0008, 0x11d6: 0xe00d, 0x11d7: 0x0008, 0x11d8: 0xe00d, 0x11d9: 0x0008, 0x11da: 0xe00d, 0x11db: 0x0008, 0x11dc: 0xe00d, 0x11dd: 0x0008, 0x11de: 0xe00d, 0x11df: 0x0008, 0x11e0: 0xe00d, 0x11e1: 0x0008, 0x11e2: 0xe00d, 0x11e3: 0x0008, 0x11e4: 0xe00d, 0x11e5: 0x0008, 0x11e6: 0xe00d, 0x11e7: 0x0008, 0x11e8: 0xe00d, 0x11e9: 0x0008, 0x11ea: 0xe00d, 0x11eb: 0x0008, 0x11ec: 0xe00d, 0x11ed: 0x0008, 0x11ee: 0xe00d, 0x11ef: 0x0008, 0x11f0: 0xe0fd, 0x11f1: 0x0008, 0x11f2: 0x0008, 0x11f3: 0x0008, 0x11f4: 0x0008, 0x11f5: 0x0008, 0x11f6: 0x0008, 0x11f7: 0x0008, 0x11f8: 0x0008, 0x11f9: 0xe01d, 0x11fa: 0x0008, 0x11fb: 0xe03d, 0x11fc: 0x0008, 0x11fd: 0x4445, 0x11fe: 0xe00d, 0x11ff: 0x0008, // Block 0x48, offset 0x1200 0x1200: 0xe00d, 0x1201: 0x0008, 0x1202: 0xe00d, 0x1203: 0x0008, 0x1204: 0xe00d, 0x1205: 0x0008, 0x1206: 0xe00d, 0x1207: 0x0008, 0x1208: 0x0008, 0x1209: 0x0018, 0x120a: 0x0018, 0x120b: 0xe03d, 0x120c: 0x0008, 0x120d: 0x11d9, 0x120e: 0x0008, 0x120f: 0x0008, 0x1210: 0xe00d, 0x1211: 0x0008, 0x1212: 0xe00d, 0x1213: 0x0008, 0x1214: 0x0008, 0x1215: 0x0008, 0x1216: 0xe00d, 0x1217: 0x0008, 0x1218: 0xe00d, 0x1219: 0x0008, 0x121a: 0xe00d, 0x121b: 0x0008, 0x121c: 0xe00d, 0x121d: 0x0008, 0x121e: 0xe00d, 0x121f: 0x0008, 0x1220: 0xe00d, 0x1221: 0x0008, 0x1222: 0xe00d, 0x1223: 0x0008, 0x1224: 0xe00d, 0x1225: 0x0008, 0x1226: 0xe00d, 0x1227: 0x0008, 0x1228: 0xe00d, 0x1229: 0x0008, 0x122a: 0x6e29, 0x122b: 0x1029, 0x122c: 0x11c1, 0x122d: 0x6e41, 0x122e: 0x1221, 0x122f: 0x0008, 0x1230: 0x6e59, 0x1231: 0x6e71, 0x1232: 0x1239, 0x1233: 0x4465, 0x1234: 0xe00d, 0x1235: 0x0008, 0x1236: 0xe00d, 0x1237: 0x0008, 0x1238: 0xe00d, 0x1239: 0x0008, 0x123a: 0xe00d, 0x123b: 0x0008, 0x123c: 0xe00d, 0x123d: 0x0008, 0x123e: 0xe00d, 0x123f: 0x0008, // Block 0x49, offset 0x1240 0x1240: 0x650d, 0x1241: 0x652d, 0x1242: 0x654d, 0x1243: 0x656d, 0x1244: 0x658d, 0x1245: 0x65ad, 0x1246: 0x65cd, 0x1247: 0x65ed, 0x1248: 0x660d, 0x1249: 0x662d, 0x124a: 0x664d, 0x124b: 0x666d, 0x124c: 0x668d, 0x124d: 0x66ad, 0x124e: 0x0008, 0x124f: 0x0008, 0x1250: 0x66cd, 0x1251: 0x0008, 0x1252: 0x66ed, 0x1253: 0x0008, 0x1254: 0x0008, 0x1255: 0x670d, 0x1256: 0x672d, 0x1257: 0x674d, 0x1258: 0x676d, 0x1259: 0x678d, 0x125a: 0x67ad, 0x125b: 0x67cd, 0x125c: 0x67ed, 0x125d: 0x680d, 0x125e: 0x682d, 0x125f: 0x0008, 0x1260: 0x684d, 0x1261: 0x0008, 0x1262: 0x686d, 0x1263: 0x0008, 0x1264: 0x0008, 0x1265: 0x688d, 0x1266: 0x68ad, 0x1267: 0x0008, 0x1268: 0x0008, 0x1269: 0x0008, 0x126a: 0x68cd, 0x126b: 0x68ed, 0x126c: 0x690d, 0x126d: 0x692d, 0x126e: 0x694d, 0x126f: 0x696d, 0x1270: 0x698d, 0x1271: 0x69ad, 0x1272: 0x69cd, 0x1273: 0x69ed, 0x1274: 0x6a0d, 0x1275: 0x6a2d, 0x1276: 0x6a4d, 0x1277: 0x6a6d, 0x1278: 0x6a8d, 0x1279: 0x6aad, 0x127a: 0x6acd, 0x127b: 0x6aed, 0x127c: 0x6b0d, 0x127d: 0x6b2d, 0x127e: 0x6b4d, 0x127f: 0x6b6d, // Block 0x4a, offset 0x1280 0x1280: 0x7acd, 0x1281: 0x7aed, 0x1282: 0x7b0d, 0x1283: 0x7b2d, 0x1284: 0x7b4d, 0x1285: 0x7b6d, 0x1286: 0x7b8d, 0x1287: 0x7bad, 0x1288: 0x7bcd, 0x1289: 0x7bed, 0x128a: 0x7c0d, 0x128b: 0x7c2d, 0x128c: 0x7c4d, 0x128d: 0x7c6d, 0x128e: 0x7c8d, 0x128f: 0x6ec9, 0x1290: 0x6ef1, 0x1291: 0x6f19, 0x1292: 0x7cad, 0x1293: 0x7ccd, 0x1294: 0x7ced, 0x1295: 0x6f41, 0x1296: 0x6f69, 0x1297: 0x6f91, 0x1298: 0x7d0d, 0x1299: 0x7d2d, 0x129a: 0x0040, 0x129b: 0x0040, 0x129c: 0x0040, 0x129d: 0x0040, 0x129e: 0x0040, 0x129f: 0x0040, 0x12a0: 0x0040, 0x12a1: 0x0040, 0x12a2: 0x0040, 0x12a3: 0x0040, 0x12a4: 0x0040, 0x12a5: 0x0040, 0x12a6: 0x0040, 0x12a7: 0x0040, 0x12a8: 0x0040, 0x12a9: 0x0040, 0x12aa: 0x0040, 0x12ab: 0x0040, 0x12ac: 0x0040, 0x12ad: 0x0040, 0x12ae: 0x0040, 0x12af: 0x0040, 0x12b0: 0x0040, 0x12b1: 0x0040, 0x12b2: 0x0040, 0x12b3: 0x0040, 0x12b4: 0x0040, 0x12b5: 0x0040, 0x12b6: 0x0040, 0x12b7: 0x0040, 0x12b8: 0x0040, 0x12b9: 0x0040, 0x12ba: 0x0040, 0x12bb: 0x0040, 0x12bc: 0x0040, 0x12bd: 0x0040, 0x12be: 0x0040, 0x12bf: 0x0040, // Block 0x4b, offset 0x12c0 0x12c0: 0x6fb9, 0x12c1: 0x6fd1, 0x12c2: 0x6fe9, 0x12c3: 0x7d4d, 0x12c4: 0x7d6d, 0x12c5: 0x7001, 0x12c6: 0x7001, 0x12c7: 0x0040, 0x12c8: 0x0040, 0x12c9: 0x0040, 0x12ca: 0x0040, 0x12cb: 0x0040, 0x12cc: 0x0040, 0x12cd: 0x0040, 0x12ce: 0x0040, 0x12cf: 0x0040, 0x12d0: 0x0040, 0x12d1: 0x0040, 0x12d2: 0x0040, 0x12d3: 0x7019, 0x12d4: 0x7041, 0x12d5: 0x7069, 0x12d6: 0x7091, 0x12d7: 0x70b9, 0x12d8: 0x0040, 0x12d9: 0x0040, 0x12da: 0x0040, 0x12db: 0x0040, 0x12dc: 0x0040, 0x12dd: 0x70e1, 0x12de: 0x3308, 0x12df: 0x7109, 0x12e0: 0x7131, 0x12e1: 0x20a9, 0x12e2: 0x20f1, 0x12e3: 0x7149, 0x12e4: 0x7161, 0x12e5: 0x7179, 0x12e6: 0x7191, 0x12e7: 0x71a9, 0x12e8: 0x71c1, 0x12e9: 0x1fb2, 0x12ea: 0x71d9, 0x12eb: 0x7201, 0x12ec: 0x7229, 0x12ed: 0x7261, 0x12ee: 0x7299, 0x12ef: 0x72c1, 0x12f0: 0x72e9, 0x12f1: 0x7311, 0x12f2: 0x7339, 0x12f3: 0x7361, 0x12f4: 0x7389, 0x12f5: 0x73b1, 0x12f6: 0x73d9, 0x12f7: 0x0040, 0x12f8: 0x7401, 0x12f9: 0x7429, 0x12fa: 0x7451, 0x12fb: 0x7479, 0x12fc: 0x74a1, 0x12fd: 0x0040, 0x12fe: 0x74c9, 0x12ff: 0x0040, // Block 0x4c, offset 0x1300 0x1300: 0x74f1, 0x1301: 0x7519, 0x1302: 0x0040, 0x1303: 0x7541, 0x1304: 0x7569, 0x1305: 0x0040, 0x1306: 0x7591, 0x1307: 0x75b9, 0x1308: 0x75e1, 0x1309: 0x7609, 0x130a: 0x7631, 0x130b: 0x7659, 0x130c: 0x7681, 0x130d: 0x76a9, 0x130e: 0x76d1, 0x130f: 0x76f9, 0x1310: 0x7721, 0x1311: 0x7721, 0x1312: 0x7739, 0x1313: 0x7739, 0x1314: 0x7739, 0x1315: 0x7739, 0x1316: 0x7751, 0x1317: 0x7751, 0x1318: 0x7751, 0x1319: 0x7751, 0x131a: 0x7769, 0x131b: 0x7769, 0x131c: 0x7769, 0x131d: 0x7769, 0x131e: 0x7781, 0x131f: 0x7781, 0x1320: 0x7781, 0x1321: 0x7781, 0x1322: 0x7799, 0x1323: 0x7799, 0x1324: 0x7799, 0x1325: 0x7799, 0x1326: 0x77b1, 0x1327: 0x77b1, 0x1328: 0x77b1, 0x1329: 0x77b1, 0x132a: 0x77c9, 0x132b: 0x77c9, 0x132c: 0x77c9, 0x132d: 0x77c9, 0x132e: 0x77e1, 0x132f: 0x77e1, 0x1330: 0x77e1, 0x1331: 0x77e1, 0x1332: 0x77f9, 0x1333: 0x77f9, 0x1334: 0x77f9, 0x1335: 0x77f9, 0x1336: 0x7811, 0x1337: 0x7811, 0x1338: 0x7811, 0x1339: 0x7811, 0x133a: 0x7829, 0x133b: 0x7829, 0x133c: 0x7829, 0x133d: 0x7829, 0x133e: 0x7841, 0x133f: 0x7841, // Block 0x4d, offset 0x1340 0x1340: 0x7841, 0x1341: 0x7841, 0x1342: 0x7859, 0x1343: 0x7859, 0x1344: 0x7871, 0x1345: 0x7871, 0x1346: 0x7889, 0x1347: 0x7889, 0x1348: 0x78a1, 0x1349: 0x78a1, 0x134a: 0x78b9, 0x134b: 0x78b9, 0x134c: 0x78d1, 0x134d: 0x78d1, 0x134e: 0x78e9, 0x134f: 0x78e9, 0x1350: 0x78e9, 0x1351: 0x78e9, 0x1352: 0x7901, 0x1353: 0x7901, 0x1354: 0x7901, 0x1355: 0x7901, 0x1356: 0x7919, 0x1357: 0x7919, 0x1358: 0x7919, 0x1359: 0x7919, 0x135a: 0x7931, 0x135b: 0x7931, 0x135c: 0x7931, 0x135d: 0x7931, 0x135e: 0x7949, 0x135f: 0x7949, 0x1360: 0x7961, 0x1361: 0x7961, 0x1362: 0x7961, 0x1363: 0x7961, 0x1364: 0x7979, 0x1365: 0x7979, 0x1366: 0x7991, 0x1367: 0x7991, 0x1368: 0x7991, 0x1369: 0x7991, 0x136a: 0x79a9, 0x136b: 0x79a9, 0x136c: 0x79a9, 0x136d: 0x79a9, 0x136e: 0x79c1, 0x136f: 0x79c1, 0x1370: 0x79d9, 0x1371: 0x79d9, 0x1372: 0x0818, 0x1373: 0x0818, 0x1374: 0x0818, 0x1375: 0x0818, 0x1376: 0x0818, 0x1377: 0x0818, 0x1378: 0x0818, 0x1379: 0x0818, 0x137a: 0x0818, 0x137b: 0x0818, 0x137c: 0x0818, 0x137d: 0x0818, 0x137e: 0x0818, 0x137f: 0x0818, // Block 0x4e, offset 0x1380 0x1380: 0x0818, 0x1381: 0x0818, 0x1382: 0x0040, 0x1383: 0x0040, 0x1384: 0x0040, 0x1385: 0x0040, 0x1386: 0x0040, 0x1387: 0x0040, 0x1388: 0x0040, 0x1389: 0x0040, 0x138a: 0x0040, 0x138b: 0x0040, 0x138c: 0x0040, 0x138d: 0x0040, 0x138e: 0x0040, 0x138f: 0x0040, 0x1390: 0x0040, 0x1391: 0x0040, 0x1392: 0x0040, 0x1393: 0x79f1, 0x1394: 0x79f1, 0x1395: 0x79f1, 0x1396: 0x79f1, 0x1397: 0x7a09, 0x1398: 0x7a09, 0x1399: 0x7a21, 0x139a: 0x7a21, 0x139b: 0x7a39, 0x139c: 0x7a39, 0x139d: 0x0479, 0x139e: 0x7a51, 0x139f: 0x7a51, 0x13a0: 0x7a69, 0x13a1: 0x7a69, 0x13a2: 0x7a81, 0x13a3: 0x7a81, 0x13a4: 0x7a99, 0x13a5: 0x7a99, 0x13a6: 0x7a99, 0x13a7: 0x7a99, 0x13a8: 0x7ab1, 0x13a9: 0x7ab1, 0x13aa: 0x7ac9, 0x13ab: 0x7ac9, 0x13ac: 0x7af1, 0x13ad: 0x7af1, 0x13ae: 0x7b19, 0x13af: 0x7b19, 0x13b0: 0x7b41, 0x13b1: 0x7b41, 0x13b2: 0x7b69, 0x13b3: 0x7b69, 0x13b4: 0x7b91, 0x13b5: 0x7b91, 0x13b6: 0x7bb9, 0x13b7: 0x7bb9, 0x13b8: 0x7bb9, 0x13b9: 0x7be1, 0x13ba: 0x7be1, 0x13bb: 0x7be1, 0x13bc: 0x7c09, 0x13bd: 0x7c09, 0x13be: 0x7c09, 0x13bf: 0x7c09, // Block 0x4f, offset 0x13c0 0x13c0: 0x85f9, 0x13c1: 0x8621, 0x13c2: 0x8649, 0x13c3: 0x8671, 0x13c4: 0x8699, 0x13c5: 0x86c1, 0x13c6: 0x86e9, 0x13c7: 0x8711, 0x13c8: 0x8739, 0x13c9: 0x8761, 0x13ca: 0x8789, 0x13cb: 0x87b1, 0x13cc: 0x87d9, 0x13cd: 0x8801, 0x13ce: 0x8829, 0x13cf: 0x8851, 0x13d0: 0x8879, 0x13d1: 0x88a1, 0x13d2: 0x88c9, 0x13d3: 0x88f1, 0x13d4: 0x8919, 0x13d5: 0x8941, 0x13d6: 0x8969, 0x13d7: 0x8991, 0x13d8: 0x89b9, 0x13d9: 0x89e1, 0x13da: 0x8a09, 0x13db: 0x8a31, 0x13dc: 0x8a59, 0x13dd: 0x8a81, 0x13de: 0x8aaa, 0x13df: 0x8ada, 0x13e0: 0x8b0a, 0x13e1: 0x8b3a, 0x13e2: 0x8b6a, 0x13e3: 0x8b9a, 0x13e4: 0x8bc9, 0x13e5: 0x8bf1, 0x13e6: 0x7c71, 0x13e7: 0x8c19, 0x13e8: 0x7be1, 0x13e9: 0x7c99, 0x13ea: 0x8c41, 0x13eb: 0x8c69, 0x13ec: 0x7d39, 0x13ed: 0x8c91, 0x13ee: 0x7d61, 0x13ef: 0x7d89, 0x13f0: 0x8cb9, 0x13f1: 0x8ce1, 0x13f2: 0x7e29, 0x13f3: 0x8d09, 0x13f4: 0x7e51, 0x13f5: 0x7e79, 0x13f6: 0x8d31, 0x13f7: 0x8d59, 0x13f8: 0x7ec9, 0x13f9: 0x8d81, 0x13fa: 0x7ef1, 0x13fb: 0x7f19, 0x13fc: 0x83a1, 0x13fd: 0x83c9, 0x13fe: 0x8441, 0x13ff: 0x8469, // Block 0x50, offset 0x1400 0x1400: 0x8491, 0x1401: 0x8531, 0x1402: 0x8559, 0x1403: 0x8581, 0x1404: 0x85a9, 0x1405: 0x8649, 0x1406: 0x8671, 0x1407: 0x8699, 0x1408: 0x8da9, 0x1409: 0x8739, 0x140a: 0x8dd1, 0x140b: 0x8df9, 0x140c: 0x8829, 0x140d: 0x8e21, 0x140e: 0x8851, 0x140f: 0x8879, 0x1410: 0x8a81, 0x1411: 0x8e49, 0x1412: 0x8e71, 0x1413: 0x89b9, 0x1414: 0x8e99, 0x1415: 0x89e1, 0x1416: 0x8a09, 0x1417: 0x7c21, 0x1418: 0x7c49, 0x1419: 0x8ec1, 0x141a: 0x7c71, 0x141b: 0x8ee9, 0x141c: 0x7cc1, 0x141d: 0x7ce9, 0x141e: 0x7d11, 0x141f: 0x7d39, 0x1420: 0x8f11, 0x1421: 0x7db1, 0x1422: 0x7dd9, 0x1423: 0x7e01, 0x1424: 0x7e29, 0x1425: 0x8f39, 0x1426: 0x7ec9, 0x1427: 0x7f41, 0x1428: 0x7f69, 0x1429: 0x7f91, 0x142a: 0x7fb9, 0x142b: 0x7fe1, 0x142c: 0x8031, 0x142d: 0x8059, 0x142e: 0x8081, 0x142f: 0x80a9, 0x1430: 0x80d1, 0x1431: 0x80f9, 0x1432: 0x8f61, 0x1433: 0x8121, 0x1434: 0x8149, 0x1435: 0x8171, 0x1436: 0x8199, 0x1437: 0x81c1, 0x1438: 0x81e9, 0x1439: 0x8239, 0x143a: 0x8261, 0x143b: 0x8289, 0x143c: 0x82b1, 0x143d: 0x82d9, 0x143e: 0x8301, 0x143f: 0x8329, // Block 0x51, offset 0x1440 0x1440: 0x8351, 0x1441: 0x8379, 0x1442: 0x83f1, 0x1443: 0x8419, 0x1444: 0x84b9, 0x1445: 0x84e1, 0x1446: 0x8509, 0x1447: 0x8531, 0x1448: 0x8559, 0x1449: 0x85d1, 0x144a: 0x85f9, 0x144b: 0x8621, 0x144c: 0x8649, 0x144d: 0x8f89, 0x144e: 0x86c1, 0x144f: 0x86e9, 0x1450: 0x8711, 0x1451: 0x8739, 0x1452: 0x87b1, 0x1453: 0x87d9, 0x1454: 0x8801, 0x1455: 0x8829, 0x1456: 0x8fb1, 0x1457: 0x88a1, 0x1458: 0x88c9, 0x1459: 0x8fd9, 0x145a: 0x8941, 0x145b: 0x8969, 0x145c: 0x8991, 0x145d: 0x89b9, 0x145e: 0x9001, 0x145f: 0x7c71, 0x1460: 0x8ee9, 0x1461: 0x7d39, 0x1462: 0x8f11, 0x1463: 0x7e29, 0x1464: 0x8f39, 0x1465: 0x7ec9, 0x1466: 0x9029, 0x1467: 0x80d1, 0x1468: 0x9051, 0x1469: 0x9079, 0x146a: 0x90a1, 0x146b: 0x8531, 0x146c: 0x8559, 0x146d: 0x8649, 0x146e: 0x8829, 0x146f: 0x8fb1, 0x1470: 0x89b9, 0x1471: 0x9001, 0x1472: 0x90c9, 0x1473: 0x9101, 0x1474: 0x9139, 0x1475: 0x9171, 0x1476: 0x9199, 0x1477: 0x91c1, 0x1478: 0x91e9, 0x1479: 0x9211, 0x147a: 0x9239, 0x147b: 0x9261, 0x147c: 0x9289, 0x147d: 0x92b1, 0x147e: 0x92d9, 0x147f: 0x9301, // Block 0x52, offset 0x1480 0x1480: 0x9329, 0x1481: 0x9351, 0x1482: 0x9379, 0x1483: 0x93a1, 0x1484: 0x93c9, 0x1485: 0x93f1, 0x1486: 0x9419, 0x1487: 0x9441, 0x1488: 0x9469, 0x1489: 0x9491, 0x148a: 0x94b9, 0x148b: 0x94e1, 0x148c: 0x9079, 0x148d: 0x9509, 0x148e: 0x9531, 0x148f: 0x9559, 0x1490: 0x9581, 0x1491: 0x9171, 0x1492: 0x9199, 0x1493: 0x91c1, 0x1494: 0x91e9, 0x1495: 0x9211, 0x1496: 0x9239, 0x1497: 0x9261, 0x1498: 0x9289, 0x1499: 0x92b1, 0x149a: 0x92d9, 0x149b: 0x9301, 0x149c: 0x9329, 0x149d: 0x9351, 0x149e: 0x9379, 0x149f: 0x93a1, 0x14a0: 0x93c9, 0x14a1: 0x93f1, 0x14a2: 0x9419, 0x14a3: 0x9441, 0x14a4: 0x9469, 0x14a5: 0x9491, 0x14a6: 0x94b9, 0x14a7: 0x94e1, 0x14a8: 0x9079, 0x14a9: 0x9509, 0x14aa: 0x9531, 0x14ab: 0x9559, 0x14ac: 0x9581, 0x14ad: 0x9491, 0x14ae: 0x94b9, 0x14af: 0x94e1, 0x14b0: 0x9079, 0x14b1: 0x9051, 0x14b2: 0x90a1, 0x14b3: 0x8211, 0x14b4: 0x8059, 0x14b5: 0x8081, 0x14b6: 0x80a9, 0x14b7: 0x9491, 0x14b8: 0x94b9, 0x14b9: 0x94e1, 0x14ba: 0x8211, 0x14bb: 0x8239, 0x14bc: 0x95a9, 0x14bd: 0x95a9, 0x14be: 0x0018, 0x14bf: 0x0018, // Block 0x53, offset 0x14c0 0x14c0: 0x0040, 0x14c1: 0x0040, 0x14c2: 0x0040, 0x14c3: 0x0040, 0x14c4: 0x0040, 0x14c5: 0x0040, 0x14c6: 0x0040, 0x14c7: 0x0040, 0x14c8: 0x0040, 0x14c9: 0x0040, 0x14ca: 0x0040, 0x14cb: 0x0040, 0x14cc: 0x0040, 0x14cd: 0x0040, 0x14ce: 0x0040, 0x14cf: 0x0040, 0x14d0: 0x95d1, 0x14d1: 0x9609, 0x14d2: 0x9609, 0x14d3: 0x9641, 0x14d4: 0x9679, 0x14d5: 0x96b1, 0x14d6: 0x96e9, 0x14d7: 0x9721, 0x14d8: 0x9759, 0x14d9: 0x9759, 0x14da: 0x9791, 0x14db: 0x97c9, 0x14dc: 0x9801, 0x14dd: 0x9839, 0x14de: 0x9871, 0x14df: 0x98a9, 0x14e0: 0x98a9, 0x14e1: 0x98e1, 0x14e2: 0x9919, 0x14e3: 0x9919, 0x14e4: 0x9951, 0x14e5: 0x9951, 0x14e6: 0x9989, 0x14e7: 0x99c1, 0x14e8: 0x99c1, 0x14e9: 0x99f9, 0x14ea: 0x9a31, 0x14eb: 0x9a31, 0x14ec: 0x9a69, 0x14ed: 0x9a69, 0x14ee: 0x9aa1, 0x14ef: 0x9ad9, 0x14f0: 0x9ad9, 0x14f1: 0x9b11, 0x14f2: 0x9b11, 0x14f3: 0x9b49, 0x14f4: 0x9b81, 0x14f5: 0x9bb9, 0x14f6: 0x9bf1, 0x14f7: 0x9bf1, 0x14f8: 0x9c29, 0x14f9: 0x9c61, 0x14fa: 0x9c99, 0x14fb: 0x9cd1, 0x14fc: 0x9d09, 0x14fd: 0x9d09, 0x14fe: 0x9d41, 0x14ff: 0x9d79, // Block 0x54, offset 0x1500 0x1500: 0xa949, 0x1501: 0xa981, 0x1502: 0xa9b9, 0x1503: 0xa8a1, 0x1504: 0x9bb9, 0x1505: 0x9989, 0x1506: 0xa9f1, 0x1507: 0xaa29, 0x1508: 0x0040, 0x1509: 0x0040, 0x150a: 0x0040, 0x150b: 0x0040, 0x150c: 0x0040, 0x150d: 0x0040, 0x150e: 0x0040, 0x150f: 0x0040, 0x1510: 0x0040, 0x1511: 0x0040, 0x1512: 0x0040, 0x1513: 0x0040, 0x1514: 0x0040, 0x1515: 0x0040, 0x1516: 0x0040, 0x1517: 0x0040, 0x1518: 0x0040, 0x1519: 0x0040, 0x151a: 0x0040, 0x151b: 0x0040, 0x151c: 0x0040, 0x151d: 0x0040, 0x151e: 0x0040, 0x151f: 0x0040, 0x1520: 0x0040, 0x1521: 0x0040, 0x1522: 0x0040, 0x1523: 0x0040, 0x1524: 0x0040, 0x1525: 0x0040, 0x1526: 0x0040, 0x1527: 0x0040, 0x1528: 0x0040, 0x1529: 0x0040, 0x152a: 0x0040, 0x152b: 0x0040, 0x152c: 0x0040, 0x152d: 0x0040, 0x152e: 0x0040, 0x152f: 0x0040, 0x1530: 0xaa61, 0x1531: 0xaa99, 0x1532: 0xaad1, 0x1533: 0xab19, 0x1534: 0xab61, 0x1535: 0xaba9, 0x1536: 0xabf1, 0x1537: 0xac39, 0x1538: 0xac81, 0x1539: 0xacc9, 0x153a: 0xad02, 0x153b: 0xae12, 0x153c: 0xae91, 0x153d: 0x0018, 0x153e: 0x0040, 0x153f: 0x0040, // Block 0x55, offset 0x1540 0x1540: 0x33c0, 0x1541: 0x33c0, 0x1542: 0x33c0, 0x1543: 0x33c0, 0x1544: 0x33c0, 0x1545: 0x33c0, 0x1546: 0x33c0, 0x1547: 0x33c0, 0x1548: 0x33c0, 0x1549: 0x33c0, 0x154a: 0x33c0, 0x154b: 0x33c0, 0x154c: 0x33c0, 0x154d: 0x33c0, 0x154e: 0x33c0, 0x154f: 0x33c0, 0x1550: 0xaeda, 0x1551: 0x7d8d, 0x1552: 0x0040, 0x1553: 0xaeea, 0x1554: 0x03c2, 0x1555: 0xaefa, 0x1556: 0xaf0a, 0x1557: 0x7dad, 0x1558: 0x7dcd, 0x1559: 0x0040, 0x155a: 0x0040, 0x155b: 0x0040, 0x155c: 0x0040, 0x155d: 0x0040, 0x155e: 0x0040, 0x155f: 0x0040, 0x1560: 0x3308, 0x1561: 0x3308, 0x1562: 0x3308, 0x1563: 0x3308, 0x1564: 0x3308, 0x1565: 0x3308, 0x1566: 0x3308, 0x1567: 0x3308, 0x1568: 0x3308, 0x1569: 0x3308, 0x156a: 0x3308, 0x156b: 0x3308, 0x156c: 0x3308, 0x156d: 0x3308, 0x156e: 0x3308, 0x156f: 0x3308, 0x1570: 0x0040, 0x1571: 0x7ded, 0x1572: 0x7e0d, 0x1573: 0xaf1a, 0x1574: 0xaf1a, 0x1575: 0x1fd2, 0x1576: 0x1fe2, 0x1577: 0xaf2a, 0x1578: 0xaf3a, 0x1579: 0x7e2d, 0x157a: 0x7e4d, 0x157b: 0x7e6d, 0x157c: 0x7e2d, 0x157d: 0x7e8d, 0x157e: 0x7ead, 0x157f: 0x7e8d, // Block 0x56, offset 0x1580 0x1580: 0x7ecd, 0x1581: 0x7eed, 0x1582: 0x7f0d, 0x1583: 0x7eed, 0x1584: 0x7f2d, 0x1585: 0x0018, 0x1586: 0x0018, 0x1587: 0xaf4a, 0x1588: 0xaf5a, 0x1589: 0x7f4e, 0x158a: 0x7f6e, 0x158b: 0x7f8e, 0x158c: 0x7fae, 0x158d: 0xaf1a, 0x158e: 0xaf1a, 0x158f: 0xaf1a, 0x1590: 0xaeda, 0x1591: 0x7fcd, 0x1592: 0x0040, 0x1593: 0x0040, 0x1594: 0x03c2, 0x1595: 0xaeea, 0x1596: 0xaf0a, 0x1597: 0xaefa, 0x1598: 0x7fed, 0x1599: 0x1fd2, 0x159a: 0x1fe2, 0x159b: 0xaf2a, 0x159c: 0xaf3a, 0x159d: 0x7ecd, 0x159e: 0x7f2d, 0x159f: 0xaf6a, 0x15a0: 0xaf7a, 0x15a1: 0xaf8a, 0x15a2: 0x1fb2, 0x15a3: 0xaf99, 0x15a4: 0xafaa, 0x15a5: 0xafba, 0x15a6: 0x1fc2, 0x15a7: 0x0040, 0x15a8: 0xafca, 0x15a9: 0xafda, 0x15aa: 0xafea, 0x15ab: 0xaffa, 0x15ac: 0x0040, 0x15ad: 0x0040, 0x15ae: 0x0040, 0x15af: 0x0040, 0x15b0: 0x800e, 0x15b1: 0xb009, 0x15b2: 0x802e, 0x15b3: 0x0808, 0x15b4: 0x804e, 0x15b5: 0x0040, 0x15b6: 0x806e, 0x15b7: 0xb031, 0x15b8: 0x808e, 0x15b9: 0xb059, 0x15ba: 0x80ae, 0x15bb: 0xb081, 0x15bc: 0x80ce, 0x15bd: 0xb0a9, 0x15be: 0x80ee, 0x15bf: 0xb0d1, // Block 0x57, offset 0x15c0 0x15c0: 0xb0f9, 0x15c1: 0xb111, 0x15c2: 0xb111, 0x15c3: 0xb129, 0x15c4: 0xb129, 0x15c5: 0xb141, 0x15c6: 0xb141, 0x15c7: 0xb159, 0x15c8: 0xb159, 0x15c9: 0xb171, 0x15ca: 0xb171, 0x15cb: 0xb171, 0x15cc: 0xb171, 0x15cd: 0xb189, 0x15ce: 0xb189, 0x15cf: 0xb1a1, 0x15d0: 0xb1a1, 0x15d1: 0xb1a1, 0x15d2: 0xb1a1, 0x15d3: 0xb1b9, 0x15d4: 0xb1b9, 0x15d5: 0xb1d1, 0x15d6: 0xb1d1, 0x15d7: 0xb1d1, 0x15d8: 0xb1d1, 0x15d9: 0xb1e9, 0x15da: 0xb1e9, 0x15db: 0xb1e9, 0x15dc: 0xb1e9, 0x15dd: 0xb201, 0x15de: 0xb201, 0x15df: 0xb201, 0x15e0: 0xb201, 0x15e1: 0xb219, 0x15e2: 0xb219, 0x15e3: 0xb219, 0x15e4: 0xb219, 0x15e5: 0xb231, 0x15e6: 0xb231, 0x15e7: 0xb231, 0x15e8: 0xb231, 0x15e9: 0xb249, 0x15ea: 0xb249, 0x15eb: 0xb261, 0x15ec: 0xb261, 0x15ed: 0xb279, 0x15ee: 0xb279, 0x15ef: 0xb291, 0x15f0: 0xb291, 0x15f1: 0xb2a9, 0x15f2: 0xb2a9, 0x15f3: 0xb2a9, 0x15f4: 0xb2a9, 0x15f5: 0xb2c1, 0x15f6: 0xb2c1, 0x15f7: 0xb2c1, 0x15f8: 0xb2c1, 0x15f9: 0xb2d9, 0x15fa: 0xb2d9, 0x15fb: 0xb2d9, 0x15fc: 0xb2d9, 0x15fd: 0xb2f1, 0x15fe: 0xb2f1, 0x15ff: 0xb2f1, // Block 0x58, offset 0x1600 0x1600: 0xb2f1, 0x1601: 0xb309, 0x1602: 0xb309, 0x1603: 0xb309, 0x1604: 0xb309, 0x1605: 0xb321, 0x1606: 0xb321, 0x1607: 0xb321, 0x1608: 0xb321, 0x1609: 0xb339, 0x160a: 0xb339, 0x160b: 0xb339, 0x160c: 0xb339, 0x160d: 0xb351, 0x160e: 0xb351, 0x160f: 0xb351, 0x1610: 0xb351, 0x1611: 0xb369, 0x1612: 0xb369, 0x1613: 0xb369, 0x1614: 0xb369, 0x1615: 0xb381, 0x1616: 0xb381, 0x1617: 0xb381, 0x1618: 0xb381, 0x1619: 0xb399, 0x161a: 0xb399, 0x161b: 0xb399, 0x161c: 0xb399, 0x161d: 0xb3b1, 0x161e: 0xb3b1, 0x161f: 0xb3b1, 0x1620: 0xb3b1, 0x1621: 0xb3c9, 0x1622: 0xb3c9, 0x1623: 0xb3c9, 0x1624: 0xb3c9, 0x1625: 0xb3e1, 0x1626: 0xb3e1, 0x1627: 0xb3e1, 0x1628: 0xb3e1, 0x1629: 0xb3f9, 0x162a: 0xb3f9, 0x162b: 0xb3f9, 0x162c: 0xb3f9, 0x162d: 0xb411, 0x162e: 0xb411, 0x162f: 0x7ab1, 0x1630: 0x7ab1, 0x1631: 0xb429, 0x1632: 0xb429, 0x1633: 0xb429, 0x1634: 0xb429, 0x1635: 0xb441, 0x1636: 0xb441, 0x1637: 0xb469, 0x1638: 0xb469, 0x1639: 0xb491, 0x163a: 0xb491, 0x163b: 0xb4b9, 0x163c: 0xb4b9, 0x163d: 0x0040, 0x163e: 0x0040, 0x163f: 0x03c0, // Block 0x59, offset 0x1640 0x1640: 0x0040, 0x1641: 0xaefa, 0x1642: 0xb4e2, 0x1643: 0xaf6a, 0x1644: 0xafda, 0x1645: 0xafea, 0x1646: 0xaf7a, 0x1647: 0xb4f2, 0x1648: 0x1fd2, 0x1649: 0x1fe2, 0x164a: 0xaf8a, 0x164b: 0x1fb2, 0x164c: 0xaeda, 0x164d: 0xaf99, 0x164e: 0x29d1, 0x164f: 0xb502, 0x1650: 0x1f41, 0x1651: 0x00c9, 0x1652: 0x0069, 0x1653: 0x0079, 0x1654: 0x1f51, 0x1655: 0x1f61, 0x1656: 0x1f71, 0x1657: 0x1f81, 0x1658: 0x1f91, 0x1659: 0x1fa1, 0x165a: 0xaeea, 0x165b: 0x03c2, 0x165c: 0xafaa, 0x165d: 0x1fc2, 0x165e: 0xafba, 0x165f: 0xaf0a, 0x1660: 0xaffa, 0x1661: 0x0039, 0x1662: 0x0ee9, 0x1663: 0x1159, 0x1664: 0x0ef9, 0x1665: 0x0f09, 0x1666: 0x1199, 0x1667: 0x0f31, 0x1668: 0x0249, 0x1669: 0x0f41, 0x166a: 0x0259, 0x166b: 0x0f51, 0x166c: 0x0359, 0x166d: 0x0f61, 0x166e: 0x0f71, 0x166f: 0x00d9, 0x1670: 0x0f99, 0x1671: 0x2039, 0x1672: 0x0269, 0x1673: 0x01d9, 0x1674: 0x0fa9, 0x1675: 0x0fb9, 0x1676: 0x1089, 0x1677: 0x0279, 0x1678: 0x0369, 0x1679: 0x0289, 0x167a: 0x13d1, 0x167b: 0xaf4a, 0x167c: 0xafca, 0x167d: 0xaf5a, 0x167e: 0xb512, 0x167f: 0xaf1a, // Block 0x5a, offset 0x1680 0x1680: 0x1caa, 0x1681: 0x0039, 0x1682: 0x0ee9, 0x1683: 0x1159, 0x1684: 0x0ef9, 0x1685: 0x0f09, 0x1686: 0x1199, 0x1687: 0x0f31, 0x1688: 0x0249, 0x1689: 0x0f41, 0x168a: 0x0259, 0x168b: 0x0f51, 0x168c: 0x0359, 0x168d: 0x0f61, 0x168e: 0x0f71, 0x168f: 0x00d9, 0x1690: 0x0f99, 0x1691: 0x2039, 0x1692: 0x0269, 0x1693: 0x01d9, 0x1694: 0x0fa9, 0x1695: 0x0fb9, 0x1696: 0x1089, 0x1697: 0x0279, 0x1698: 0x0369, 0x1699: 0x0289, 0x169a: 0x13d1, 0x169b: 0xaf2a, 0x169c: 0xb522, 0x169d: 0xaf3a, 0x169e: 0xb532, 0x169f: 0x810d, 0x16a0: 0x812d, 0x16a1: 0x29d1, 0x16a2: 0x814d, 0x16a3: 0x814d, 0x16a4: 0x816d, 0x16a5: 0x818d, 0x16a6: 0x81ad, 0x16a7: 0x81cd, 0x16a8: 0x81ed, 0x16a9: 0x820d, 0x16aa: 0x822d, 0x16ab: 0x824d, 0x16ac: 0x826d, 0x16ad: 0x828d, 0x16ae: 0x82ad, 0x16af: 0x82cd, 0x16b0: 0x82ed, 0x16b1: 0x830d, 0x16b2: 0x832d, 0x16b3: 0x834d, 0x16b4: 0x836d, 0x16b5: 0x838d, 0x16b6: 0x83ad, 0x16b7: 0x83cd, 0x16b8: 0x83ed, 0x16b9: 0x840d, 0x16ba: 0x842d, 0x16bb: 0x844d, 0x16bc: 0x81ed, 0x16bd: 0x846d, 0x16be: 0x848d, 0x16bf: 0x824d, // Block 0x5b, offset 0x16c0 0x16c0: 0x84ad, 0x16c1: 0x84cd, 0x16c2: 0x84ed, 0x16c3: 0x850d, 0x16c4: 0x852d, 0x16c5: 0x854d, 0x16c6: 0x856d, 0x16c7: 0x858d, 0x16c8: 0x850d, 0x16c9: 0x85ad, 0x16ca: 0x850d, 0x16cb: 0x85cd, 0x16cc: 0x85cd, 0x16cd: 0x85ed, 0x16ce: 0x85ed, 0x16cf: 0x860d, 0x16d0: 0x854d, 0x16d1: 0x862d, 0x16d2: 0x864d, 0x16d3: 0x862d, 0x16d4: 0x866d, 0x16d5: 0x864d, 0x16d6: 0x868d, 0x16d7: 0x868d, 0x16d8: 0x86ad, 0x16d9: 0x86ad, 0x16da: 0x86cd, 0x16db: 0x86cd, 0x16dc: 0x864d, 0x16dd: 0x814d, 0x16de: 0x86ed, 0x16df: 0x870d, 0x16e0: 0x0040, 0x16e1: 0x872d, 0x16e2: 0x874d, 0x16e3: 0x876d, 0x16e4: 0x878d, 0x16e5: 0x876d, 0x16e6: 0x87ad, 0x16e7: 0x87cd, 0x16e8: 0x87ed, 0x16e9: 0x87ed, 0x16ea: 0x880d, 0x16eb: 0x880d, 0x16ec: 0x882d, 0x16ed: 0x882d, 0x16ee: 0x880d, 0x16ef: 0x880d, 0x16f0: 0x884d, 0x16f1: 0x886d, 0x16f2: 0x888d, 0x16f3: 0x88ad, 0x16f4: 0x88cd, 0x16f5: 0x88ed, 0x16f6: 0x88ed, 0x16f7: 0x88ed, 0x16f8: 0x890d, 0x16f9: 0x890d, 0x16fa: 0x890d, 0x16fb: 0x890d, 0x16fc: 0x87ed, 0x16fd: 0x87ed, 0x16fe: 0x87ed, 0x16ff: 0x0040, // Block 0x5c, offset 0x1700 0x1700: 0x0040, 0x1701: 0x0040, 0x1702: 0x874d, 0x1703: 0x872d, 0x1704: 0x892d, 0x1705: 0x872d, 0x1706: 0x874d, 0x1707: 0x872d, 0x1708: 0x0040, 0x1709: 0x0040, 0x170a: 0x894d, 0x170b: 0x874d, 0x170c: 0x896d, 0x170d: 0x892d, 0x170e: 0x896d, 0x170f: 0x874d, 0x1710: 0x0040, 0x1711: 0x0040, 0x1712: 0x898d, 0x1713: 0x89ad, 0x1714: 0x88ad, 0x1715: 0x896d, 0x1716: 0x892d, 0x1717: 0x896d, 0x1718: 0x0040, 0x1719: 0x0040, 0x171a: 0x89cd, 0x171b: 0x89ed, 0x171c: 0x89cd, 0x171d: 0x0040, 0x171e: 0x0040, 0x171f: 0x0040, 0x1720: 0xb541, 0x1721: 0xb559, 0x1722: 0xb571, 0x1723: 0x8a0e, 0x1724: 0xb589, 0x1725: 0xb5a1, 0x1726: 0x8a2d, 0x1727: 0x0040, 0x1728: 0x8a4d, 0x1729: 0x8a6d, 0x172a: 0x8a8d, 0x172b: 0x8a6d, 0x172c: 0x8aad, 0x172d: 0x8acd, 0x172e: 0x8aed, 0x172f: 0x0040, 0x1730: 0x0040, 0x1731: 0x0040, 0x1732: 0x0040, 0x1733: 0x0040, 0x1734: 0x0040, 0x1735: 0x0040, 0x1736: 0x0040, 0x1737: 0x0040, 0x1738: 0x0040, 0x1739: 0x0340, 0x173a: 0x0340, 0x173b: 0x0340, 0x173c: 0x0040, 0x173d: 0x0040, 0x173e: 0x0040, 0x173f: 0x0040, // Block 0x5d, offset 0x1740 0x1740: 0x0a08, 0x1741: 0x0a08, 0x1742: 0x0a08, 0x1743: 0x0a08, 0x1744: 0x0a08, 0x1745: 0x0c08, 0x1746: 0x0808, 0x1747: 0x0c08, 0x1748: 0x0818, 0x1749: 0x0c08, 0x174a: 0x0c08, 0x174b: 0x0808, 0x174c: 0x0808, 0x174d: 0x0908, 0x174e: 0x0c08, 0x174f: 0x0c08, 0x1750: 0x0c08, 0x1751: 0x0c08, 0x1752: 0x0c08, 0x1753: 0x0a08, 0x1754: 0x0a08, 0x1755: 0x0a08, 0x1756: 0x0a08, 0x1757: 0x0908, 0x1758: 0x0a08, 0x1759: 0x0a08, 0x175a: 0x0a08, 0x175b: 0x0a08, 0x175c: 0x0a08, 0x175d: 0x0c08, 0x175e: 0x0a08, 0x175f: 0x0a08, 0x1760: 0x0a08, 0x1761: 0x0c08, 0x1762: 0x0808, 0x1763: 0x0808, 0x1764: 0x0c08, 0x1765: 0x3308, 0x1766: 0x3308, 0x1767: 0x0040, 0x1768: 0x0040, 0x1769: 0x0040, 0x176a: 0x0040, 0x176b: 0x0a18, 0x176c: 0x0a18, 0x176d: 0x0a18, 0x176e: 0x0a18, 0x176f: 0x0c18, 0x1770: 0x0818, 0x1771: 0x0818, 0x1772: 0x0818, 0x1773: 0x0818, 0x1774: 0x0818, 0x1775: 0x0818, 0x1776: 0x0818, 0x1777: 0x0040, 0x1778: 0x0040, 0x1779: 0x0040, 0x177a: 0x0040, 0x177b: 0x0040, 0x177c: 0x0040, 0x177d: 0x0040, 0x177e: 0x0040, 0x177f: 0x0040, // Block 0x5e, offset 0x1780 0x1780: 0x0a08, 0x1781: 0x0c08, 0x1782: 0x0a08, 0x1783: 0x0c08, 0x1784: 0x0c08, 0x1785: 0x0c08, 0x1786: 0x0a08, 0x1787: 0x0a08, 0x1788: 0x0a08, 0x1789: 0x0c08, 0x178a: 0x0a08, 0x178b: 0x0a08, 0x178c: 0x0c08, 0x178d: 0x0a08, 0x178e: 0x0c08, 0x178f: 0x0c08, 0x1790: 0x0a08, 0x1791: 0x0c08, 0x1792: 0x0040, 0x1793: 0x0040, 0x1794: 0x0040, 0x1795: 0x0040, 0x1796: 0x0040, 0x1797: 0x0040, 0x1798: 0x0040, 0x1799: 0x0818, 0x179a: 0x0818, 0x179b: 0x0818, 0x179c: 0x0818, 0x179d: 0x0040, 0x179e: 0x0040, 0x179f: 0x0040, 0x17a0: 0x0040, 0x17a1: 0x0040, 0x17a2: 0x0040, 0x17a3: 0x0040, 0x17a4: 0x0040, 0x17a5: 0x0040, 0x17a6: 0x0040, 0x17a7: 0x0040, 0x17a8: 0x0040, 0x17a9: 0x0c18, 0x17aa: 0x0c18, 0x17ab: 0x0c18, 0x17ac: 0x0c18, 0x17ad: 0x0a18, 0x17ae: 0x0a18, 0x17af: 0x0818, 0x17b0: 0x0040, 0x17b1: 0x0040, 0x17b2: 0x0040, 0x17b3: 0x0040, 0x17b4: 0x0040, 0x17b5: 0x0040, 0x17b6: 0x0040, 0x17b7: 0x0040, 0x17b8: 0x0040, 0x17b9: 0x0040, 0x17ba: 0x0040, 0x17bb: 0x0040, 0x17bc: 0x0040, 0x17bd: 0x0040, 0x17be: 0x0040, 0x17bf: 0x0040, // Block 0x5f, offset 0x17c0 0x17c0: 0x3308, 0x17c1: 0x3308, 0x17c2: 0x3008, 0x17c3: 0x3008, 0x17c4: 0x0040, 0x17c5: 0x0008, 0x17c6: 0x0008, 0x17c7: 0x0008, 0x17c8: 0x0008, 0x17c9: 0x0008, 0x17ca: 0x0008, 0x17cb: 0x0008, 0x17cc: 0x0008, 0x17cd: 0x0040, 0x17ce: 0x0040, 0x17cf: 0x0008, 0x17d0: 0x0008, 0x17d1: 0x0040, 0x17d2: 0x0040, 0x17d3: 0x0008, 0x17d4: 0x0008, 0x17d5: 0x0008, 0x17d6: 0x0008, 0x17d7: 0x0008, 0x17d8: 0x0008, 0x17d9: 0x0008, 0x17da: 0x0008, 0x17db: 0x0008, 0x17dc: 0x0008, 0x17dd: 0x0008, 0x17de: 0x0008, 0x17df: 0x0008, 0x17e0: 0x0008, 0x17e1: 0x0008, 0x17e2: 0x0008, 0x17e3: 0x0008, 0x17e4: 0x0008, 0x17e5: 0x0008, 0x17e6: 0x0008, 0x17e7: 0x0008, 0x17e8: 0x0008, 0x17e9: 0x0040, 0x17ea: 0x0008, 0x17eb: 0x0008, 0x17ec: 0x0008, 0x17ed: 0x0008, 0x17ee: 0x0008, 0x17ef: 0x0008, 0x17f0: 0x0008, 0x17f1: 0x0040, 0x17f2: 0x0008, 0x17f3: 0x0008, 0x17f4: 0x0040, 0x17f5: 0x0008, 0x17f6: 0x0008, 0x17f7: 0x0008, 0x17f8: 0x0008, 0x17f9: 0x0008, 0x17fa: 0x0040, 0x17fb: 0x3308, 0x17fc: 0x3308, 0x17fd: 0x0008, 0x17fe: 0x3008, 0x17ff: 0x3008, // Block 0x60, offset 0x1800 0x1800: 0x3308, 0x1801: 0x3008, 0x1802: 0x3008, 0x1803: 0x3008, 0x1804: 0x3008, 0x1805: 0x0040, 0x1806: 0x0040, 0x1807: 0x3008, 0x1808: 0x3008, 0x1809: 0x0040, 0x180a: 0x0040, 0x180b: 0x3008, 0x180c: 0x3008, 0x180d: 0x3808, 0x180e: 0x0040, 0x180f: 0x0040, 0x1810: 0x0008, 0x1811: 0x0040, 0x1812: 0x0040, 0x1813: 0x0040, 0x1814: 0x0040, 0x1815: 0x0040, 0x1816: 0x0040, 0x1817: 0x3008, 0x1818: 0x0040, 0x1819: 0x0040, 0x181a: 0x0040, 0x181b: 0x0040, 0x181c: 0x0040, 0x181d: 0x0008, 0x181e: 0x0008, 0x181f: 0x0008, 0x1820: 0x0008, 0x1821: 0x0008, 0x1822: 0x3008, 0x1823: 0x3008, 0x1824: 0x0040, 0x1825: 0x0040, 0x1826: 0x3308, 0x1827: 0x3308, 0x1828: 0x3308, 0x1829: 0x3308, 0x182a: 0x3308, 0x182b: 0x3308, 0x182c: 0x3308, 0x182d: 0x0040, 0x182e: 0x0040, 0x182f: 0x0040, 0x1830: 0x3308, 0x1831: 0x3308, 0x1832: 0x3308, 0x1833: 0x3308, 0x1834: 0x3308, 0x1835: 0x0040, 0x1836: 0x0040, 0x1837: 0x0040, 0x1838: 0x0040, 0x1839: 0x0040, 0x183a: 0x0040, 0x183b: 0x0040, 0x183c: 0x0040, 0x183d: 0x0040, 0x183e: 0x0040, 0x183f: 0x0040, // Block 0x61, offset 0x1840 0x1840: 0x0039, 0x1841: 0x0ee9, 0x1842: 0x1159, 0x1843: 0x0ef9, 0x1844: 0x0f09, 0x1845: 0x1199, 0x1846: 0x0f31, 0x1847: 0x0249, 0x1848: 0x0f41, 0x1849: 0x0259, 0x184a: 0x0f51, 0x184b: 0x0359, 0x184c: 0x0f61, 0x184d: 0x0f71, 0x184e: 0x00d9, 0x184f: 0x0f99, 0x1850: 0x2039, 0x1851: 0x0269, 0x1852: 0x01d9, 0x1853: 0x0fa9, 0x1854: 0x0fb9, 0x1855: 0x1089, 0x1856: 0x0279, 0x1857: 0x0369, 0x1858: 0x0289, 0x1859: 0x13d1, 0x185a: 0x0039, 0x185b: 0x0ee9, 0x185c: 0x1159, 0x185d: 0x0ef9, 0x185e: 0x0f09, 0x185f: 0x1199, 0x1860: 0x0f31, 0x1861: 0x0249, 0x1862: 0x0f41, 0x1863: 0x0259, 0x1864: 0x0f51, 0x1865: 0x0359, 0x1866: 0x0f61, 0x1867: 0x0f71, 0x1868: 0x00d9, 0x1869: 0x0f99, 0x186a: 0x2039, 0x186b: 0x0269, 0x186c: 0x01d9, 0x186d: 0x0fa9, 0x186e: 0x0fb9, 0x186f: 0x1089, 0x1870: 0x0279, 0x1871: 0x0369, 0x1872: 0x0289, 0x1873: 0x13d1, 0x1874: 0x0039, 0x1875: 0x0ee9, 0x1876: 0x1159, 0x1877: 0x0ef9, 0x1878: 0x0f09, 0x1879: 0x1199, 0x187a: 0x0f31, 0x187b: 0x0249, 0x187c: 0x0f41, 0x187d: 0x0259, 0x187e: 0x0f51, 0x187f: 0x0359, // Block 0x62, offset 0x1880 0x1880: 0x0f61, 0x1881: 0x0f71, 0x1882: 0x00d9, 0x1883: 0x0f99, 0x1884: 0x2039, 0x1885: 0x0269, 0x1886: 0x01d9, 0x1887: 0x0fa9, 0x1888: 0x0fb9, 0x1889: 0x1089, 0x188a: 0x0279, 0x188b: 0x0369, 0x188c: 0x0289, 0x188d: 0x13d1, 0x188e: 0x0039, 0x188f: 0x0ee9, 0x1890: 0x1159, 0x1891: 0x0ef9, 0x1892: 0x0f09, 0x1893: 0x1199, 0x1894: 0x0f31, 0x1895: 0x0040, 0x1896: 0x0f41, 0x1897: 0x0259, 0x1898: 0x0f51, 0x1899: 0x0359, 0x189a: 0x0f61, 0x189b: 0x0f71, 0x189c: 0x00d9, 0x189d: 0x0f99, 0x189e: 0x2039, 0x189f: 0x0269, 0x18a0: 0x01d9, 0x18a1: 0x0fa9, 0x18a2: 0x0fb9, 0x18a3: 0x1089, 0x18a4: 0x0279, 0x18a5: 0x0369, 0x18a6: 0x0289, 0x18a7: 0x13d1, 0x18a8: 0x0039, 0x18a9: 0x0ee9, 0x18aa: 0x1159, 0x18ab: 0x0ef9, 0x18ac: 0x0f09, 0x18ad: 0x1199, 0x18ae: 0x0f31, 0x18af: 0x0249, 0x18b0: 0x0f41, 0x18b1: 0x0259, 0x18b2: 0x0f51, 0x18b3: 0x0359, 0x18b4: 0x0f61, 0x18b5: 0x0f71, 0x18b6: 0x00d9, 0x18b7: 0x0f99, 0x18b8: 0x2039, 0x18b9: 0x0269, 0x18ba: 0x01d9, 0x18bb: 0x0fa9, 0x18bc: 0x0fb9, 0x18bd: 0x1089, 0x18be: 0x0279, 0x18bf: 0x0369, // Block 0x63, offset 0x18c0 0x18c0: 0x0289, 0x18c1: 0x13d1, 0x18c2: 0x0039, 0x18c3: 0x0ee9, 0x18c4: 0x1159, 0x18c5: 0x0ef9, 0x18c6: 0x0f09, 0x18c7: 0x1199, 0x18c8: 0x0f31, 0x18c9: 0x0249, 0x18ca: 0x0f41, 0x18cb: 0x0259, 0x18cc: 0x0f51, 0x18cd: 0x0359, 0x18ce: 0x0f61, 0x18cf: 0x0f71, 0x18d0: 0x00d9, 0x18d1: 0x0f99, 0x18d2: 0x2039, 0x18d3: 0x0269, 0x18d4: 0x01d9, 0x18d5: 0x0fa9, 0x18d6: 0x0fb9, 0x18d7: 0x1089, 0x18d8: 0x0279, 0x18d9: 0x0369, 0x18da: 0x0289, 0x18db: 0x13d1, 0x18dc: 0x0039, 0x18dd: 0x0040, 0x18de: 0x1159, 0x18df: 0x0ef9, 0x18e0: 0x0040, 0x18e1: 0x0040, 0x18e2: 0x0f31, 0x18e3: 0x0040, 0x18e4: 0x0040, 0x18e5: 0x0259, 0x18e6: 0x0f51, 0x18e7: 0x0040, 0x18e8: 0x0040, 0x18e9: 0x0f71, 0x18ea: 0x00d9, 0x18eb: 0x0f99, 0x18ec: 0x2039, 0x18ed: 0x0040, 0x18ee: 0x01d9, 0x18ef: 0x0fa9, 0x18f0: 0x0fb9, 0x18f1: 0x1089, 0x18f2: 0x0279, 0x18f3: 0x0369, 0x18f4: 0x0289, 0x18f5: 0x13d1, 0x18f6: 0x0039, 0x18f7: 0x0ee9, 0x18f8: 0x1159, 0x18f9: 0x0ef9, 0x18fa: 0x0040, 0x18fb: 0x1199, 0x18fc: 0x0040, 0x18fd: 0x0249, 0x18fe: 0x0f41, 0x18ff: 0x0259, // Block 0x64, offset 0x1900 0x1900: 0x0f51, 0x1901: 0x0359, 0x1902: 0x0f61, 0x1903: 0x0f71, 0x1904: 0x0040, 0x1905: 0x0f99, 0x1906: 0x2039, 0x1907: 0x0269, 0x1908: 0x01d9, 0x1909: 0x0fa9, 0x190a: 0x0fb9, 0x190b: 0x1089, 0x190c: 0x0279, 0x190d: 0x0369, 0x190e: 0x0289, 0x190f: 0x13d1, 0x1910: 0x0039, 0x1911: 0x0ee9, 0x1912: 0x1159, 0x1913: 0x0ef9, 0x1914: 0x0f09, 0x1915: 0x1199, 0x1916: 0x0f31, 0x1917: 0x0249, 0x1918: 0x0f41, 0x1919: 0x0259, 0x191a: 0x0f51, 0x191b: 0x0359, 0x191c: 0x0f61, 0x191d: 0x0f71, 0x191e: 0x00d9, 0x191f: 0x0f99, 0x1920: 0x2039, 0x1921: 0x0269, 0x1922: 0x01d9, 0x1923: 0x0fa9, 0x1924: 0x0fb9, 0x1925: 0x1089, 0x1926: 0x0279, 0x1927: 0x0369, 0x1928: 0x0289, 0x1929: 0x13d1, 0x192a: 0x0039, 0x192b: 0x0ee9, 0x192c: 0x1159, 0x192d: 0x0ef9, 0x192e: 0x0f09, 0x192f: 0x1199, 0x1930: 0x0f31, 0x1931: 0x0249, 0x1932: 0x0f41, 0x1933: 0x0259, 0x1934: 0x0f51, 0x1935: 0x0359, 0x1936: 0x0f61, 0x1937: 0x0f71, 0x1938: 0x00d9, 0x1939: 0x0f99, 0x193a: 0x2039, 0x193b: 0x0269, 0x193c: 0x01d9, 0x193d: 0x0fa9, 0x193e: 0x0fb9, 0x193f: 0x1089, // Block 0x65, offset 0x1940 0x1940: 0x0279, 0x1941: 0x0369, 0x1942: 0x0289, 0x1943: 0x13d1, 0x1944: 0x0039, 0x1945: 0x0ee9, 0x1946: 0x0040, 0x1947: 0x0ef9, 0x1948: 0x0f09, 0x1949: 0x1199, 0x194a: 0x0f31, 0x194b: 0x0040, 0x194c: 0x0040, 0x194d: 0x0259, 0x194e: 0x0f51, 0x194f: 0x0359, 0x1950: 0x0f61, 0x1951: 0x0f71, 0x1952: 0x00d9, 0x1953: 0x0f99, 0x1954: 0x2039, 0x1955: 0x0040, 0x1956: 0x01d9, 0x1957: 0x0fa9, 0x1958: 0x0fb9, 0x1959: 0x1089, 0x195a: 0x0279, 0x195b: 0x0369, 0x195c: 0x0289, 0x195d: 0x0040, 0x195e: 0x0039, 0x195f: 0x0ee9, 0x1960: 0x1159, 0x1961: 0x0ef9, 0x1962: 0x0f09, 0x1963: 0x1199, 0x1964: 0x0f31, 0x1965: 0x0249, 0x1966: 0x0f41, 0x1967: 0x0259, 0x1968: 0x0f51, 0x1969: 0x0359, 0x196a: 0x0f61, 0x196b: 0x0f71, 0x196c: 0x00d9, 0x196d: 0x0f99, 0x196e: 0x2039, 0x196f: 0x0269, 0x1970: 0x01d9, 0x1971: 0x0fa9, 0x1972: 0x0fb9, 0x1973: 0x1089, 0x1974: 0x0279, 0x1975: 0x0369, 0x1976: 0x0289, 0x1977: 0x13d1, 0x1978: 0x0039, 0x1979: 0x0ee9, 0x197a: 0x0040, 0x197b: 0x0ef9, 0x197c: 0x0f09, 0x197d: 0x1199, 0x197e: 0x0f31, 0x197f: 0x0040, // Block 0x66, offset 0x1980 0x1980: 0x0f41, 0x1981: 0x0259, 0x1982: 0x0f51, 0x1983: 0x0359, 0x1984: 0x0f61, 0x1985: 0x0040, 0x1986: 0x00d9, 0x1987: 0x0040, 0x1988: 0x0040, 0x1989: 0x0040, 0x198a: 0x01d9, 0x198b: 0x0fa9, 0x198c: 0x0fb9, 0x198d: 0x1089, 0x198e: 0x0279, 0x198f: 0x0369, 0x1990: 0x0289, 0x1991: 0x0040, 0x1992: 0x0039, 0x1993: 0x0ee9, 0x1994: 0x1159, 0x1995: 0x0ef9, 0x1996: 0x0f09, 0x1997: 0x1199, 0x1998: 0x0f31, 0x1999: 0x0249, 0x199a: 0x0f41, 0x199b: 0x0259, 0x199c: 0x0f51, 0x199d: 0x0359, 0x199e: 0x0f61, 0x199f: 0x0f71, 0x19a0: 0x00d9, 0x19a1: 0x0f99, 0x19a2: 0x2039, 0x19a3: 0x0269, 0x19a4: 0x01d9, 0x19a5: 0x0fa9, 0x19a6: 0x0fb9, 0x19a7: 0x1089, 0x19a8: 0x0279, 0x19a9: 0x0369, 0x19aa: 0x0289, 0x19ab: 0x13d1, 0x19ac: 0x0039, 0x19ad: 0x0ee9, 0x19ae: 0x1159, 0x19af: 0x0ef9, 0x19b0: 0x0f09, 0x19b1: 0x1199, 0x19b2: 0x0f31, 0x19b3: 0x0249, 0x19b4: 0x0f41, 0x19b5: 0x0259, 0x19b6: 0x0f51, 0x19b7: 0x0359, 0x19b8: 0x0f61, 0x19b9: 0x0f71, 0x19ba: 0x00d9, 0x19bb: 0x0f99, 0x19bc: 0x2039, 0x19bd: 0x0269, 0x19be: 0x01d9, 0x19bf: 0x0fa9, // Block 0x67, offset 0x19c0 0x19c0: 0x0fb9, 0x19c1: 0x1089, 0x19c2: 0x0279, 0x19c3: 0x0369, 0x19c4: 0x0289, 0x19c5: 0x13d1, 0x19c6: 0x0039, 0x19c7: 0x0ee9, 0x19c8: 0x1159, 0x19c9: 0x0ef9, 0x19ca: 0x0f09, 0x19cb: 0x1199, 0x19cc: 0x0f31, 0x19cd: 0x0249, 0x19ce: 0x0f41, 0x19cf: 0x0259, 0x19d0: 0x0f51, 0x19d1: 0x0359, 0x19d2: 0x0f61, 0x19d3: 0x0f71, 0x19d4: 0x00d9, 0x19d5: 0x0f99, 0x19d6: 0x2039, 0x19d7: 0x0269, 0x19d8: 0x01d9, 0x19d9: 0x0fa9, 0x19da: 0x0fb9, 0x19db: 0x1089, 0x19dc: 0x0279, 0x19dd: 0x0369, 0x19de: 0x0289, 0x19df: 0x13d1, 0x19e0: 0x0039, 0x19e1: 0x0ee9, 0x19e2: 0x1159, 0x19e3: 0x0ef9, 0x19e4: 0x0f09, 0x19e5: 0x1199, 0x19e6: 0x0f31, 0x19e7: 0x0249, 0x19e8: 0x0f41, 0x19e9: 0x0259, 0x19ea: 0x0f51, 0x19eb: 0x0359, 0x19ec: 0x0f61, 0x19ed: 0x0f71, 0x19ee: 0x00d9, 0x19ef: 0x0f99, 0x19f0: 0x2039, 0x19f1: 0x0269, 0x19f2: 0x01d9, 0x19f3: 0x0fa9, 0x19f4: 0x0fb9, 0x19f5: 0x1089, 0x19f6: 0x0279, 0x19f7: 0x0369, 0x19f8: 0x0289, 0x19f9: 0x13d1, 0x19fa: 0x0039, 0x19fb: 0x0ee9, 0x19fc: 0x1159, 0x19fd: 0x0ef9, 0x19fe: 0x0f09, 0x19ff: 0x1199, // Block 0x68, offset 0x1a00 0x1a00: 0x0f31, 0x1a01: 0x0249, 0x1a02: 0x0f41, 0x1a03: 0x0259, 0x1a04: 0x0f51, 0x1a05: 0x0359, 0x1a06: 0x0f61, 0x1a07: 0x0f71, 0x1a08: 0x00d9, 0x1a09: 0x0f99, 0x1a0a: 0x2039, 0x1a0b: 0x0269, 0x1a0c: 0x01d9, 0x1a0d: 0x0fa9, 0x1a0e: 0x0fb9, 0x1a0f: 0x1089, 0x1a10: 0x0279, 0x1a11: 0x0369, 0x1a12: 0x0289, 0x1a13: 0x13d1, 0x1a14: 0x0039, 0x1a15: 0x0ee9, 0x1a16: 0x1159, 0x1a17: 0x0ef9, 0x1a18: 0x0f09, 0x1a19: 0x1199, 0x1a1a: 0x0f31, 0x1a1b: 0x0249, 0x1a1c: 0x0f41, 0x1a1d: 0x0259, 0x1a1e: 0x0f51, 0x1a1f: 0x0359, 0x1a20: 0x0f61, 0x1a21: 0x0f71, 0x1a22: 0x00d9, 0x1a23: 0x0f99, 0x1a24: 0x2039, 0x1a25: 0x0269, 0x1a26: 0x01d9, 0x1a27: 0x0fa9, 0x1a28: 0x0fb9, 0x1a29: 0x1089, 0x1a2a: 0x0279, 0x1a2b: 0x0369, 0x1a2c: 0x0289, 0x1a2d: 0x13d1, 0x1a2e: 0x0039, 0x1a2f: 0x0ee9, 0x1a30: 0x1159, 0x1a31: 0x0ef9, 0x1a32: 0x0f09, 0x1a33: 0x1199, 0x1a34: 0x0f31, 0x1a35: 0x0249, 0x1a36: 0x0f41, 0x1a37: 0x0259, 0x1a38: 0x0f51, 0x1a39: 0x0359, 0x1a3a: 0x0f61, 0x1a3b: 0x0f71, 0x1a3c: 0x00d9, 0x1a3d: 0x0f99, 0x1a3e: 0x2039, 0x1a3f: 0x0269, // Block 0x69, offset 0x1a40 0x1a40: 0x01d9, 0x1a41: 0x0fa9, 0x1a42: 0x0fb9, 0x1a43: 0x1089, 0x1a44: 0x0279, 0x1a45: 0x0369, 0x1a46: 0x0289, 0x1a47: 0x13d1, 0x1a48: 0x0039, 0x1a49: 0x0ee9, 0x1a4a: 0x1159, 0x1a4b: 0x0ef9, 0x1a4c: 0x0f09, 0x1a4d: 0x1199, 0x1a4e: 0x0f31, 0x1a4f: 0x0249, 0x1a50: 0x0f41, 0x1a51: 0x0259, 0x1a52: 0x0f51, 0x1a53: 0x0359, 0x1a54: 0x0f61, 0x1a55: 0x0f71, 0x1a56: 0x00d9, 0x1a57: 0x0f99, 0x1a58: 0x2039, 0x1a59: 0x0269, 0x1a5a: 0x01d9, 0x1a5b: 0x0fa9, 0x1a5c: 0x0fb9, 0x1a5d: 0x1089, 0x1a5e: 0x0279, 0x1a5f: 0x0369, 0x1a60: 0x0289, 0x1a61: 0x13d1, 0x1a62: 0x0039, 0x1a63: 0x0ee9, 0x1a64: 0x1159, 0x1a65: 0x0ef9, 0x1a66: 0x0f09, 0x1a67: 0x1199, 0x1a68: 0x0f31, 0x1a69: 0x0249, 0x1a6a: 0x0f41, 0x1a6b: 0x0259, 0x1a6c: 0x0f51, 0x1a6d: 0x0359, 0x1a6e: 0x0f61, 0x1a6f: 0x0f71, 0x1a70: 0x00d9, 0x1a71: 0x0f99, 0x1a72: 0x2039, 0x1a73: 0x0269, 0x1a74: 0x01d9, 0x1a75: 0x0fa9, 0x1a76: 0x0fb9, 0x1a77: 0x1089, 0x1a78: 0x0279, 0x1a79: 0x0369, 0x1a7a: 0x0289, 0x1a7b: 0x13d1, 0x1a7c: 0x0039, 0x1a7d: 0x0ee9, 0x1a7e: 0x1159, 0x1a7f: 0x0ef9, // Block 0x6a, offset 0x1a80 0x1a80: 0x0f09, 0x1a81: 0x1199, 0x1a82: 0x0f31, 0x1a83: 0x0249, 0x1a84: 0x0f41, 0x1a85: 0x0259, 0x1a86: 0x0f51, 0x1a87: 0x0359, 0x1a88: 0x0f61, 0x1a89: 0x0f71, 0x1a8a: 0x00d9, 0x1a8b: 0x0f99, 0x1a8c: 0x2039, 0x1a8d: 0x0269, 0x1a8e: 0x01d9, 0x1a8f: 0x0fa9, 0x1a90: 0x0fb9, 0x1a91: 0x1089, 0x1a92: 0x0279, 0x1a93: 0x0369, 0x1a94: 0x0289, 0x1a95: 0x13d1, 0x1a96: 0x0039, 0x1a97: 0x0ee9, 0x1a98: 0x1159, 0x1a99: 0x0ef9, 0x1a9a: 0x0f09, 0x1a9b: 0x1199, 0x1a9c: 0x0f31, 0x1a9d: 0x0249, 0x1a9e: 0x0f41, 0x1a9f: 0x0259, 0x1aa0: 0x0f51, 0x1aa1: 0x0359, 0x1aa2: 0x0f61, 0x1aa3: 0x0f71, 0x1aa4: 0x00d9, 0x1aa5: 0x0f99, 0x1aa6: 0x2039, 0x1aa7: 0x0269, 0x1aa8: 0x01d9, 0x1aa9: 0x0fa9, 0x1aaa: 0x0fb9, 0x1aab: 0x1089, 0x1aac: 0x0279, 0x1aad: 0x0369, 0x1aae: 0x0289, 0x1aaf: 0x13d1, 0x1ab0: 0x0039, 0x1ab1: 0x0ee9, 0x1ab2: 0x1159, 0x1ab3: 0x0ef9, 0x1ab4: 0x0f09, 0x1ab5: 0x1199, 0x1ab6: 0x0f31, 0x1ab7: 0x0249, 0x1ab8: 0x0f41, 0x1ab9: 0x0259, 0x1aba: 0x0f51, 0x1abb: 0x0359, 0x1abc: 0x0f61, 0x1abd: 0x0f71, 0x1abe: 0x00d9, 0x1abf: 0x0f99, // Block 0x6b, offset 0x1ac0 0x1ac0: 0x2039, 0x1ac1: 0x0269, 0x1ac2: 0x01d9, 0x1ac3: 0x0fa9, 0x1ac4: 0x0fb9, 0x1ac5: 0x1089, 0x1ac6: 0x0279, 0x1ac7: 0x0369, 0x1ac8: 0x0289, 0x1ac9: 0x13d1, 0x1aca: 0x0039, 0x1acb: 0x0ee9, 0x1acc: 0x1159, 0x1acd: 0x0ef9, 0x1ace: 0x0f09, 0x1acf: 0x1199, 0x1ad0: 0x0f31, 0x1ad1: 0x0249, 0x1ad2: 0x0f41, 0x1ad3: 0x0259, 0x1ad4: 0x0f51, 0x1ad5: 0x0359, 0x1ad6: 0x0f61, 0x1ad7: 0x0f71, 0x1ad8: 0x00d9, 0x1ad9: 0x0f99, 0x1ada: 0x2039, 0x1adb: 0x0269, 0x1adc: 0x01d9, 0x1add: 0x0fa9, 0x1ade: 0x0fb9, 0x1adf: 0x1089, 0x1ae0: 0x0279, 0x1ae1: 0x0369, 0x1ae2: 0x0289, 0x1ae3: 0x13d1, 0x1ae4: 0xba81, 0x1ae5: 0xba99, 0x1ae6: 0x0040, 0x1ae7: 0x0040, 0x1ae8: 0xbab1, 0x1ae9: 0x1099, 0x1aea: 0x10b1, 0x1aeb: 0x10c9, 0x1aec: 0xbac9, 0x1aed: 0xbae1, 0x1aee: 0xbaf9, 0x1aef: 0x1429, 0x1af0: 0x1a31, 0x1af1: 0xbb11, 0x1af2: 0xbb29, 0x1af3: 0xbb41, 0x1af4: 0xbb59, 0x1af5: 0xbb71, 0x1af6: 0xbb89, 0x1af7: 0x2109, 0x1af8: 0x1111, 0x1af9: 0x1429, 0x1afa: 0xbba1, 0x1afb: 0xbbb9, 0x1afc: 0xbbd1, 0x1afd: 0x10e1, 0x1afe: 0x10f9, 0x1aff: 0xbbe9, // Block 0x6c, offset 0x1b00 0x1b00: 0x2079, 0x1b01: 0xbc01, 0x1b02: 0xbab1, 0x1b03: 0x1099, 0x1b04: 0x10b1, 0x1b05: 0x10c9, 0x1b06: 0xbac9, 0x1b07: 0xbae1, 0x1b08: 0xbaf9, 0x1b09: 0x1429, 0x1b0a: 0x1a31, 0x1b0b: 0xbb11, 0x1b0c: 0xbb29, 0x1b0d: 0xbb41, 0x1b0e: 0xbb59, 0x1b0f: 0xbb71, 0x1b10: 0xbb89, 0x1b11: 0x2109, 0x1b12: 0x1111, 0x1b13: 0xbba1, 0x1b14: 0xbba1, 0x1b15: 0xbbb9, 0x1b16: 0xbbd1, 0x1b17: 0x10e1, 0x1b18: 0x10f9, 0x1b19: 0xbbe9, 0x1b1a: 0x2079, 0x1b1b: 0xbc21, 0x1b1c: 0xbac9, 0x1b1d: 0x1429, 0x1b1e: 0xbb11, 0x1b1f: 0x10e1, 0x1b20: 0x1111, 0x1b21: 0x2109, 0x1b22: 0xbab1, 0x1b23: 0x1099, 0x1b24: 0x10b1, 0x1b25: 0x10c9, 0x1b26: 0xbac9, 0x1b27: 0xbae1, 0x1b28: 0xbaf9, 0x1b29: 0x1429, 0x1b2a: 0x1a31, 0x1b2b: 0xbb11, 0x1b2c: 0xbb29, 0x1b2d: 0xbb41, 0x1b2e: 0xbb59, 0x1b2f: 0xbb71, 0x1b30: 0xbb89, 0x1b31: 0x2109, 0x1b32: 0x1111, 0x1b33: 0x1429, 0x1b34: 0xbba1, 0x1b35: 0xbbb9, 0x1b36: 0xbbd1, 0x1b37: 0x10e1, 0x1b38: 0x10f9, 0x1b39: 0xbbe9, 0x1b3a: 0x2079, 0x1b3b: 0xbc01, 0x1b3c: 0xbab1, 0x1b3d: 0x1099, 0x1b3e: 0x10b1, 0x1b3f: 0x10c9, // Block 0x6d, offset 0x1b40 0x1b40: 0xbac9, 0x1b41: 0xbae1, 0x1b42: 0xbaf9, 0x1b43: 0x1429, 0x1b44: 0x1a31, 0x1b45: 0xbb11, 0x1b46: 0xbb29, 0x1b47: 0xbb41, 0x1b48: 0xbb59, 0x1b49: 0xbb71, 0x1b4a: 0xbb89, 0x1b4b: 0x2109, 0x1b4c: 0x1111, 0x1b4d: 0xbba1, 0x1b4e: 0xbba1, 0x1b4f: 0xbbb9, 0x1b50: 0xbbd1, 0x1b51: 0x10e1, 0x1b52: 0x10f9, 0x1b53: 0xbbe9, 0x1b54: 0x2079, 0x1b55: 0xbc21, 0x1b56: 0xbac9, 0x1b57: 0x1429, 0x1b58: 0xbb11, 0x1b59: 0x10e1, 0x1b5a: 0x1111, 0x1b5b: 0x2109, 0x1b5c: 0xbab1, 0x1b5d: 0x1099, 0x1b5e: 0x10b1, 0x1b5f: 0x10c9, 0x1b60: 0xbac9, 0x1b61: 0xbae1, 0x1b62: 0xbaf9, 0x1b63: 0x1429, 0x1b64: 0x1a31, 0x1b65: 0xbb11, 0x1b66: 0xbb29, 0x1b67: 0xbb41, 0x1b68: 0xbb59, 0x1b69: 0xbb71, 0x1b6a: 0xbb89, 0x1b6b: 0x2109, 0x1b6c: 0x1111, 0x1b6d: 0x1429, 0x1b6e: 0xbba1, 0x1b6f: 0xbbb9, 0x1b70: 0xbbd1, 0x1b71: 0x10e1, 0x1b72: 0x10f9, 0x1b73: 0xbbe9, 0x1b74: 0x2079, 0x1b75: 0xbc01, 0x1b76: 0xbab1, 0x1b77: 0x1099, 0x1b78: 0x10b1, 0x1b79: 0x10c9, 0x1b7a: 0xbac9, 0x1b7b: 0xbae1, 0x1b7c: 0xbaf9, 0x1b7d: 0x1429, 0x1b7e: 0x1a31, 0x1b7f: 0xbb11, // Block 0x6e, offset 0x1b80 0x1b80: 0xbb29, 0x1b81: 0xbb41, 0x1b82: 0xbb59, 0x1b83: 0xbb71, 0x1b84: 0xbb89, 0x1b85: 0x2109, 0x1b86: 0x1111, 0x1b87: 0xbba1, 0x1b88: 0xbba1, 0x1b89: 0xbbb9, 0x1b8a: 0xbbd1, 0x1b8b: 0x10e1, 0x1b8c: 0x10f9, 0x1b8d: 0xbbe9, 0x1b8e: 0x2079, 0x1b8f: 0xbc21, 0x1b90: 0xbac9, 0x1b91: 0x1429, 0x1b92: 0xbb11, 0x1b93: 0x10e1, 0x1b94: 0x1111, 0x1b95: 0x2109, 0x1b96: 0xbab1, 0x1b97: 0x1099, 0x1b98: 0x10b1, 0x1b99: 0x10c9, 0x1b9a: 0xbac9, 0x1b9b: 0xbae1, 0x1b9c: 0xbaf9, 0x1b9d: 0x1429, 0x1b9e: 0x1a31, 0x1b9f: 0xbb11, 0x1ba0: 0xbb29, 0x1ba1: 0xbb41, 0x1ba2: 0xbb59, 0x1ba3: 0xbb71, 0x1ba4: 0xbb89, 0x1ba5: 0x2109, 0x1ba6: 0x1111, 0x1ba7: 0x1429, 0x1ba8: 0xbba1, 0x1ba9: 0xbbb9, 0x1baa: 0xbbd1, 0x1bab: 0x10e1, 0x1bac: 0x10f9, 0x1bad: 0xbbe9, 0x1bae: 0x2079, 0x1baf: 0xbc01, 0x1bb0: 0xbab1, 0x1bb1: 0x1099, 0x1bb2: 0x10b1, 0x1bb3: 0x10c9, 0x1bb4: 0xbac9, 0x1bb5: 0xbae1, 0x1bb6: 0xbaf9, 0x1bb7: 0x1429, 0x1bb8: 0x1a31, 0x1bb9: 0xbb11, 0x1bba: 0xbb29, 0x1bbb: 0xbb41, 0x1bbc: 0xbb59, 0x1bbd: 0xbb71, 0x1bbe: 0xbb89, 0x1bbf: 0x2109, // Block 0x6f, offset 0x1bc0 0x1bc0: 0x1111, 0x1bc1: 0xbba1, 0x1bc2: 0xbba1, 0x1bc3: 0xbbb9, 0x1bc4: 0xbbd1, 0x1bc5: 0x10e1, 0x1bc6: 0x10f9, 0x1bc7: 0xbbe9, 0x1bc8: 0x2079, 0x1bc9: 0xbc21, 0x1bca: 0xbac9, 0x1bcb: 0x1429, 0x1bcc: 0xbb11, 0x1bcd: 0x10e1, 0x1bce: 0x1111, 0x1bcf: 0x2109, 0x1bd0: 0xbab1, 0x1bd1: 0x1099, 0x1bd2: 0x10b1, 0x1bd3: 0x10c9, 0x1bd4: 0xbac9, 0x1bd5: 0xbae1, 0x1bd6: 0xbaf9, 0x1bd7: 0x1429, 0x1bd8: 0x1a31, 0x1bd9: 0xbb11, 0x1bda: 0xbb29, 0x1bdb: 0xbb41, 0x1bdc: 0xbb59, 0x1bdd: 0xbb71, 0x1bde: 0xbb89, 0x1bdf: 0x2109, 0x1be0: 0x1111, 0x1be1: 0x1429, 0x1be2: 0xbba1, 0x1be3: 0xbbb9, 0x1be4: 0xbbd1, 0x1be5: 0x10e1, 0x1be6: 0x10f9, 0x1be7: 0xbbe9, 0x1be8: 0x2079, 0x1be9: 0xbc01, 0x1bea: 0xbab1, 0x1beb: 0x1099, 0x1bec: 0x10b1, 0x1bed: 0x10c9, 0x1bee: 0xbac9, 0x1bef: 0xbae1, 0x1bf0: 0xbaf9, 0x1bf1: 0x1429, 0x1bf2: 0x1a31, 0x1bf3: 0xbb11, 0x1bf4: 0xbb29, 0x1bf5: 0xbb41, 0x1bf6: 0xbb59, 0x1bf7: 0xbb71, 0x1bf8: 0xbb89, 0x1bf9: 0x2109, 0x1bfa: 0x1111, 0x1bfb: 0xbba1, 0x1bfc: 0xbba1, 0x1bfd: 0xbbb9, 0x1bfe: 0xbbd1, 0x1bff: 0x10e1, // Block 0x70, offset 0x1c00 0x1c00: 0x10f9, 0x1c01: 0xbbe9, 0x1c02: 0x2079, 0x1c03: 0xbc21, 0x1c04: 0xbac9, 0x1c05: 0x1429, 0x1c06: 0xbb11, 0x1c07: 0x10e1, 0x1c08: 0x1111, 0x1c09: 0x2109, 0x1c0a: 0xbc41, 0x1c0b: 0xbc41, 0x1c0c: 0x0040, 0x1c0d: 0x0040, 0x1c0e: 0x1f41, 0x1c0f: 0x00c9, 0x1c10: 0x0069, 0x1c11: 0x0079, 0x1c12: 0x1f51, 0x1c13: 0x1f61, 0x1c14: 0x1f71, 0x1c15: 0x1f81, 0x1c16: 0x1f91, 0x1c17: 0x1fa1, 0x1c18: 0x1f41, 0x1c19: 0x00c9, 0x1c1a: 0x0069, 0x1c1b: 0x0079, 0x1c1c: 0x1f51, 0x1c1d: 0x1f61, 0x1c1e: 0x1f71, 0x1c1f: 0x1f81, 0x1c20: 0x1f91, 0x1c21: 0x1fa1, 0x1c22: 0x1f41, 0x1c23: 0x00c9, 0x1c24: 0x0069, 0x1c25: 0x0079, 0x1c26: 0x1f51, 0x1c27: 0x1f61, 0x1c28: 0x1f71, 0x1c29: 0x1f81, 0x1c2a: 0x1f91, 0x1c2b: 0x1fa1, 0x1c2c: 0x1f41, 0x1c2d: 0x00c9, 0x1c2e: 0x0069, 0x1c2f: 0x0079, 0x1c30: 0x1f51, 0x1c31: 0x1f61, 0x1c32: 0x1f71, 0x1c33: 0x1f81, 0x1c34: 0x1f91, 0x1c35: 0x1fa1, 0x1c36: 0x1f41, 0x1c37: 0x00c9, 0x1c38: 0x0069, 0x1c39: 0x0079, 0x1c3a: 0x1f51, 0x1c3b: 0x1f61, 0x1c3c: 0x1f71, 0x1c3d: 0x1f81, 0x1c3e: 0x1f91, 0x1c3f: 0x1fa1, // Block 0x71, offset 0x1c40 0x1c40: 0xe115, 0x1c41: 0xe115, 0x1c42: 0xe135, 0x1c43: 0xe135, 0x1c44: 0xe115, 0x1c45: 0xe115, 0x1c46: 0xe175, 0x1c47: 0xe175, 0x1c48: 0xe115, 0x1c49: 0xe115, 0x1c4a: 0xe135, 0x1c4b: 0xe135, 0x1c4c: 0xe115, 0x1c4d: 0xe115, 0x1c4e: 0xe1f5, 0x1c4f: 0xe1f5, 0x1c50: 0xe115, 0x1c51: 0xe115, 0x1c52: 0xe135, 0x1c53: 0xe135, 0x1c54: 0xe115, 0x1c55: 0xe115, 0x1c56: 0xe175, 0x1c57: 0xe175, 0x1c58: 0xe115, 0x1c59: 0xe115, 0x1c5a: 0xe135, 0x1c5b: 0xe135, 0x1c5c: 0xe115, 0x1c5d: 0xe115, 0x1c5e: 0x8b3d, 0x1c5f: 0x8b3d, 0x1c60: 0x04b5, 0x1c61: 0x04b5, 0x1c62: 0x0a08, 0x1c63: 0x0a08, 0x1c64: 0x0a08, 0x1c65: 0x0a08, 0x1c66: 0x0a08, 0x1c67: 0x0a08, 0x1c68: 0x0a08, 0x1c69: 0x0a08, 0x1c6a: 0x0a08, 0x1c6b: 0x0a08, 0x1c6c: 0x0a08, 0x1c6d: 0x0a08, 0x1c6e: 0x0a08, 0x1c6f: 0x0a08, 0x1c70: 0x0a08, 0x1c71: 0x0a08, 0x1c72: 0x0a08, 0x1c73: 0x0a08, 0x1c74: 0x0a08, 0x1c75: 0x0a08, 0x1c76: 0x0a08, 0x1c77: 0x0a08, 0x1c78: 0x0a08, 0x1c79: 0x0a08, 0x1c7a: 0x0a08, 0x1c7b: 0x0a08, 0x1c7c: 0x0a08, 0x1c7d: 0x0a08, 0x1c7e: 0x0a08, 0x1c7f: 0x0a08, // Block 0x72, offset 0x1c80 0x1c80: 0xb189, 0x1c81: 0xb1a1, 0x1c82: 0xb201, 0x1c83: 0xb249, 0x1c84: 0x0040, 0x1c85: 0xb411, 0x1c86: 0xb291, 0x1c87: 0xb219, 0x1c88: 0xb309, 0x1c89: 0xb429, 0x1c8a: 0xb399, 0x1c8b: 0xb3b1, 0x1c8c: 0xb3c9, 0x1c8d: 0xb3e1, 0x1c8e: 0xb2a9, 0x1c8f: 0xb339, 0x1c90: 0xb369, 0x1c91: 0xb2d9, 0x1c92: 0xb381, 0x1c93: 0xb279, 0x1c94: 0xb2c1, 0x1c95: 0xb1d1, 0x1c96: 0xb1e9, 0x1c97: 0xb231, 0x1c98: 0xb261, 0x1c99: 0xb2f1, 0x1c9a: 0xb321, 0x1c9b: 0xb351, 0x1c9c: 0xbc59, 0x1c9d: 0x7949, 0x1c9e: 0xbc71, 0x1c9f: 0xbc89, 0x1ca0: 0x0040, 0x1ca1: 0xb1a1, 0x1ca2: 0xb201, 0x1ca3: 0x0040, 0x1ca4: 0xb3f9, 0x1ca5: 0x0040, 0x1ca6: 0x0040, 0x1ca7: 0xb219, 0x1ca8: 0x0040, 0x1ca9: 0xb429, 0x1caa: 0xb399, 0x1cab: 0xb3b1, 0x1cac: 0xb3c9, 0x1cad: 0xb3e1, 0x1cae: 0xb2a9, 0x1caf: 0xb339, 0x1cb0: 0xb369, 0x1cb1: 0xb2d9, 0x1cb2: 0xb381, 0x1cb3: 0x0040, 0x1cb4: 0xb2c1, 0x1cb5: 0xb1d1, 0x1cb6: 0xb1e9, 0x1cb7: 0xb231, 0x1cb8: 0x0040, 0x1cb9: 0xb2f1, 0x1cba: 0x0040, 0x1cbb: 0xb351, 0x1cbc: 0x0040, 0x1cbd: 0x0040, 0x1cbe: 0x0040, 0x1cbf: 0x0040, // Block 0x73, offset 0x1cc0 0x1cc0: 0x0040, 0x1cc1: 0x0040, 0x1cc2: 0xb201, 0x1cc3: 0x0040, 0x1cc4: 0x0040, 0x1cc5: 0x0040, 0x1cc6: 0x0040, 0x1cc7: 0xb219, 0x1cc8: 0x0040, 0x1cc9: 0xb429, 0x1cca: 0x0040, 0x1ccb: 0xb3b1, 0x1ccc: 0x0040, 0x1ccd: 0xb3e1, 0x1cce: 0xb2a9, 0x1ccf: 0xb339, 0x1cd0: 0x0040, 0x1cd1: 0xb2d9, 0x1cd2: 0xb381, 0x1cd3: 0x0040, 0x1cd4: 0xb2c1, 0x1cd5: 0x0040, 0x1cd6: 0x0040, 0x1cd7: 0xb231, 0x1cd8: 0x0040, 0x1cd9: 0xb2f1, 0x1cda: 0x0040, 0x1cdb: 0xb351, 0x1cdc: 0x0040, 0x1cdd: 0x7949, 0x1cde: 0x0040, 0x1cdf: 0xbc89, 0x1ce0: 0x0040, 0x1ce1: 0xb1a1, 0x1ce2: 0xb201, 0x1ce3: 0x0040, 0x1ce4: 0xb3f9, 0x1ce5: 0x0040, 0x1ce6: 0x0040, 0x1ce7: 0xb219, 0x1ce8: 0xb309, 0x1ce9: 0xb429, 0x1cea: 0xb399, 0x1ceb: 0x0040, 0x1cec: 0xb3c9, 0x1ced: 0xb3e1, 0x1cee: 0xb2a9, 0x1cef: 0xb339, 0x1cf0: 0xb369, 0x1cf1: 0xb2d9, 0x1cf2: 0xb381, 0x1cf3: 0x0040, 0x1cf4: 0xb2c1, 0x1cf5: 0xb1d1, 0x1cf6: 0xb1e9, 0x1cf7: 0xb231, 0x1cf8: 0x0040, 0x1cf9: 0xb2f1, 0x1cfa: 0xb321, 0x1cfb: 0xb351, 0x1cfc: 0xbc59, 0x1cfd: 0x0040, 0x1cfe: 0xbc71, 0x1cff: 0x0040, // Block 0x74, offset 0x1d00 0x1d00: 0xb189, 0x1d01: 0xb1a1, 0x1d02: 0xb201, 0x1d03: 0xb249, 0x1d04: 0xb3f9, 0x1d05: 0xb411, 0x1d06: 0xb291, 0x1d07: 0xb219, 0x1d08: 0xb309, 0x1d09: 0xb429, 0x1d0a: 0x0040, 0x1d0b: 0xb3b1, 0x1d0c: 0xb3c9, 0x1d0d: 0xb3e1, 0x1d0e: 0xb2a9, 0x1d0f: 0xb339, 0x1d10: 0xb369, 0x1d11: 0xb2d9, 0x1d12: 0xb381, 0x1d13: 0xb279, 0x1d14: 0xb2c1, 0x1d15: 0xb1d1, 0x1d16: 0xb1e9, 0x1d17: 0xb231, 0x1d18: 0xb261, 0x1d19: 0xb2f1, 0x1d1a: 0xb321, 0x1d1b: 0xb351, 0x1d1c: 0x0040, 0x1d1d: 0x0040, 0x1d1e: 0x0040, 0x1d1f: 0x0040, 0x1d20: 0x0040, 0x1d21: 0xb1a1, 0x1d22: 0xb201, 0x1d23: 0xb249, 0x1d24: 0x0040, 0x1d25: 0xb411, 0x1d26: 0xb291, 0x1d27: 0xb219, 0x1d28: 0xb309, 0x1d29: 0xb429, 0x1d2a: 0x0040, 0x1d2b: 0xb3b1, 0x1d2c: 0xb3c9, 0x1d2d: 0xb3e1, 0x1d2e: 0xb2a9, 0x1d2f: 0xb339, 0x1d30: 0xb369, 0x1d31: 0xb2d9, 0x1d32: 0xb381, 0x1d33: 0xb279, 0x1d34: 0xb2c1, 0x1d35: 0xb1d1, 0x1d36: 0xb1e9, 0x1d37: 0xb231, 0x1d38: 0xb261, 0x1d39: 0xb2f1, 0x1d3a: 0xb321, 0x1d3b: 0xb351, 0x1d3c: 0x0040, 0x1d3d: 0x0040, 0x1d3e: 0x0040, 0x1d3f: 0x0040, // Block 0x75, offset 0x1d40 0x1d40: 0x0040, 0x1d41: 0xbca2, 0x1d42: 0xbcba, 0x1d43: 0xbcd2, 0x1d44: 0xbcea, 0x1d45: 0xbd02, 0x1d46: 0xbd1a, 0x1d47: 0xbd32, 0x1d48: 0xbd4a, 0x1d49: 0xbd62, 0x1d4a: 0xbd7a, 0x1d4b: 0x0018, 0x1d4c: 0x0018, 0x1d4d: 0x0040, 0x1d4e: 0x0040, 0x1d4f: 0x0040, 0x1d50: 0xbd92, 0x1d51: 0xbdb2, 0x1d52: 0xbdd2, 0x1d53: 0xbdf2, 0x1d54: 0xbe12, 0x1d55: 0xbe32, 0x1d56: 0xbe52, 0x1d57: 0xbe72, 0x1d58: 0xbe92, 0x1d59: 0xbeb2, 0x1d5a: 0xbed2, 0x1d5b: 0xbef2, 0x1d5c: 0xbf12, 0x1d5d: 0xbf32, 0x1d5e: 0xbf52, 0x1d5f: 0xbf72, 0x1d60: 0xbf92, 0x1d61: 0xbfb2, 0x1d62: 0xbfd2, 0x1d63: 0xbff2, 0x1d64: 0xc012, 0x1d65: 0xc032, 0x1d66: 0xc052, 0x1d67: 0xc072, 0x1d68: 0xc092, 0x1d69: 0xc0b2, 0x1d6a: 0xc0d1, 0x1d6b: 0x1159, 0x1d6c: 0x0269, 0x1d6d: 0x6671, 0x1d6e: 0xc111, 0x1d6f: 0x0018, 0x1d70: 0x0039, 0x1d71: 0x0ee9, 0x1d72: 0x1159, 0x1d73: 0x0ef9, 0x1d74: 0x0f09, 0x1d75: 0x1199, 0x1d76: 0x0f31, 0x1d77: 0x0249, 0x1d78: 0x0f41, 0x1d79: 0x0259, 0x1d7a: 0x0f51, 0x1d7b: 0x0359, 0x1d7c: 0x0f61, 0x1d7d: 0x0f71, 0x1d7e: 0x00d9, 0x1d7f: 0x0f99, // Block 0x76, offset 0x1d80 0x1d80: 0x2039, 0x1d81: 0x0269, 0x1d82: 0x01d9, 0x1d83: 0x0fa9, 0x1d84: 0x0fb9, 0x1d85: 0x1089, 0x1d86: 0x0279, 0x1d87: 0x0369, 0x1d88: 0x0289, 0x1d89: 0x13d1, 0x1d8a: 0xc129, 0x1d8b: 0x65b1, 0x1d8c: 0xc141, 0x1d8d: 0x1441, 0x1d8e: 0xc159, 0x1d8f: 0xc179, 0x1d90: 0x0018, 0x1d91: 0x0018, 0x1d92: 0x0018, 0x1d93: 0x0018, 0x1d94: 0x0018, 0x1d95: 0x0018, 0x1d96: 0x0018, 0x1d97: 0x0018, 0x1d98: 0x0018, 0x1d99: 0x0018, 0x1d9a: 0x0018, 0x1d9b: 0x0018, 0x1d9c: 0x0018, 0x1d9d: 0x0018, 0x1d9e: 0x0018, 0x1d9f: 0x0018, 0x1da0: 0x0018, 0x1da1: 0x0018, 0x1da2: 0x0018, 0x1da3: 0x0018, 0x1da4: 0x0018, 0x1da5: 0x0018, 0x1da6: 0x0018, 0x1da7: 0x0018, 0x1da8: 0x0018, 0x1da9: 0x0018, 0x1daa: 0xc191, 0x1dab: 0xc1a9, 0x1dac: 0xc1c1, 0x1dad: 0x0040, 0x1dae: 0x0040, 0x1daf: 0x0040, 0x1db0: 0x0018, 0x1db1: 0x0018, 0x1db2: 0x0018, 0x1db3: 0x0018, 0x1db4: 0x0018, 0x1db5: 0x0018, 0x1db6: 0x0018, 0x1db7: 0x0018, 0x1db8: 0x0018, 0x1db9: 0x0018, 0x1dba: 0x0018, 0x1dbb: 0x0018, 0x1dbc: 0x0018, 0x1dbd: 0x0018, 0x1dbe: 0x0018, 0x1dbf: 0x0018, // Block 0x77, offset 0x1dc0 0x1dc0: 0xc1f1, 0x1dc1: 0xc229, 0x1dc2: 0xc261, 0x1dc3: 0x0040, 0x1dc4: 0x0040, 0x1dc5: 0x0040, 0x1dc6: 0x0040, 0x1dc7: 0x0040, 0x1dc8: 0x0040, 0x1dc9: 0x0040, 0x1dca: 0x0040, 0x1dcb: 0x0040, 0x1dcc: 0x0040, 0x1dcd: 0x0040, 0x1dce: 0x0040, 0x1dcf: 0x0040, 0x1dd0: 0xc281, 0x1dd1: 0xc2a1, 0x1dd2: 0xc2c1, 0x1dd3: 0xc2e1, 0x1dd4: 0xc301, 0x1dd5: 0xc321, 0x1dd6: 0xc341, 0x1dd7: 0xc361, 0x1dd8: 0xc381, 0x1dd9: 0xc3a1, 0x1dda: 0xc3c1, 0x1ddb: 0xc3e1, 0x1ddc: 0xc401, 0x1ddd: 0xc421, 0x1dde: 0xc441, 0x1ddf: 0xc461, 0x1de0: 0xc481, 0x1de1: 0xc4a1, 0x1de2: 0xc4c1, 0x1de3: 0xc4e1, 0x1de4: 0xc501, 0x1de5: 0xc521, 0x1de6: 0xc541, 0x1de7: 0xc561, 0x1de8: 0xc581, 0x1de9: 0xc5a1, 0x1dea: 0xc5c1, 0x1deb: 0xc5e1, 0x1dec: 0xc601, 0x1ded: 0xc621, 0x1dee: 0xc641, 0x1def: 0xc661, 0x1df0: 0xc681, 0x1df1: 0xc6a1, 0x1df2: 0xc6c1, 0x1df3: 0xc6e1, 0x1df4: 0xc701, 0x1df5: 0xc721, 0x1df6: 0xc741, 0x1df7: 0xc761, 0x1df8: 0xc781, 0x1df9: 0xc7a1, 0x1dfa: 0xc7c1, 0x1dfb: 0xc7e1, 0x1dfc: 0x0040, 0x1dfd: 0x0040, 0x1dfe: 0x0040, 0x1dff: 0x0040, // Block 0x78, offset 0x1e00 0x1e00: 0xcb11, 0x1e01: 0xcb31, 0x1e02: 0xcb51, 0x1e03: 0x8b55, 0x1e04: 0xcb71, 0x1e05: 0xcb91, 0x1e06: 0xcbb1, 0x1e07: 0xcbd1, 0x1e08: 0xcbf1, 0x1e09: 0xcc11, 0x1e0a: 0xcc31, 0x1e0b: 0xcc51, 0x1e0c: 0xcc71, 0x1e0d: 0x8b75, 0x1e0e: 0xcc91, 0x1e0f: 0xccb1, 0x1e10: 0xccd1, 0x1e11: 0xccf1, 0x1e12: 0x8b95, 0x1e13: 0xcd11, 0x1e14: 0xcd31, 0x1e15: 0xc441, 0x1e16: 0x8bb5, 0x1e17: 0xcd51, 0x1e18: 0xcd71, 0x1e19: 0xcd91, 0x1e1a: 0xcdb1, 0x1e1b: 0xcdd1, 0x1e1c: 0x8bd5, 0x1e1d: 0xcdf1, 0x1e1e: 0xce11, 0x1e1f: 0xce31, 0x1e20: 0xce51, 0x1e21: 0xce71, 0x1e22: 0xc7a1, 0x1e23: 0xce91, 0x1e24: 0xceb1, 0x1e25: 0xced1, 0x1e26: 0xcef1, 0x1e27: 0xcf11, 0x1e28: 0xcf31, 0x1e29: 0xcf51, 0x1e2a: 0xcf71, 0x1e2b: 0xcf91, 0x1e2c: 0xcfb1, 0x1e2d: 0xcfd1, 0x1e2e: 0xcff1, 0x1e2f: 0xd011, 0x1e30: 0xd031, 0x1e31: 0xd051, 0x1e32: 0xd051, 0x1e33: 0xd051, 0x1e34: 0x8bf5, 0x1e35: 0xd071, 0x1e36: 0xd091, 0x1e37: 0xd0b1, 0x1e38: 0x8c15, 0x1e39: 0xd0d1, 0x1e3a: 0xd0f1, 0x1e3b: 0xd111, 0x1e3c: 0xd131, 0x1e3d: 0xd151, 0x1e3e: 0xd171, 0x1e3f: 0xd191, // Block 0x79, offset 0x1e40 0x1e40: 0xd1b1, 0x1e41: 0xd1d1, 0x1e42: 0xd1f1, 0x1e43: 0xd211, 0x1e44: 0xd231, 0x1e45: 0xd251, 0x1e46: 0xd251, 0x1e47: 0xd271, 0x1e48: 0xd291, 0x1e49: 0xd2b1, 0x1e4a: 0xd2d1, 0x1e4b: 0xd2f1, 0x1e4c: 0xd311, 0x1e4d: 0xd331, 0x1e4e: 0xd351, 0x1e4f: 0xd371, 0x1e50: 0xd391, 0x1e51: 0xd3b1, 0x1e52: 0xd3d1, 0x1e53: 0xd3f1, 0x1e54: 0xd411, 0x1e55: 0xd431, 0x1e56: 0xd451, 0x1e57: 0xd471, 0x1e58: 0xd491, 0x1e59: 0x8c35, 0x1e5a: 0xd4b1, 0x1e5b: 0xd4d1, 0x1e5c: 0xd4f1, 0x1e5d: 0xc321, 0x1e5e: 0xd511, 0x1e5f: 0xd531, 0x1e60: 0x8c55, 0x1e61: 0x8c75, 0x1e62: 0xd551, 0x1e63: 0xd571, 0x1e64: 0xd591, 0x1e65: 0xd5b1, 0x1e66: 0xd5d1, 0x1e67: 0xd5f1, 0x1e68: 0x2040, 0x1e69: 0xd611, 0x1e6a: 0xd631, 0x1e6b: 0xd631, 0x1e6c: 0x8c95, 0x1e6d: 0xd651, 0x1e6e: 0xd671, 0x1e6f: 0xd691, 0x1e70: 0xd6b1, 0x1e71: 0x8cb5, 0x1e72: 0xd6d1, 0x1e73: 0xd6f1, 0x1e74: 0x2040, 0x1e75: 0xd711, 0x1e76: 0xd731, 0x1e77: 0xd751, 0x1e78: 0xd771, 0x1e79: 0xd791, 0x1e7a: 0xd7b1, 0x1e7b: 0x8cd5, 0x1e7c: 0xd7d1, 0x1e7d: 0x8cf5, 0x1e7e: 0xd7f1, 0x1e7f: 0xd811, // Block 0x7a, offset 0x1e80 0x1e80: 0xd831, 0x1e81: 0xd851, 0x1e82: 0xd871, 0x1e83: 0xd891, 0x1e84: 0xd8b1, 0x1e85: 0xd8d1, 0x1e86: 0xd8f1, 0x1e87: 0xd911, 0x1e88: 0xd931, 0x1e89: 0x8d15, 0x1e8a: 0xd951, 0x1e8b: 0xd971, 0x1e8c: 0xd991, 0x1e8d: 0xd9b1, 0x1e8e: 0xd9d1, 0x1e8f: 0x8d35, 0x1e90: 0xd9f1, 0x1e91: 0x8d55, 0x1e92: 0x8d75, 0x1e93: 0xda11, 0x1e94: 0xda31, 0x1e95: 0xda31, 0x1e96: 0xda51, 0x1e97: 0x8d95, 0x1e98: 0x8db5, 0x1e99: 0xda71, 0x1e9a: 0xda91, 0x1e9b: 0xdab1, 0x1e9c: 0xdad1, 0x1e9d: 0xdaf1, 0x1e9e: 0xdb11, 0x1e9f: 0xdb31, 0x1ea0: 0xdb51, 0x1ea1: 0xdb71, 0x1ea2: 0xdb91, 0x1ea3: 0xdbb1, 0x1ea4: 0x8dd5, 0x1ea5: 0xdbd1, 0x1ea6: 0xdbf1, 0x1ea7: 0xdc11, 0x1ea8: 0xdc31, 0x1ea9: 0xdc11, 0x1eaa: 0xdc51, 0x1eab: 0xdc71, 0x1eac: 0xdc91, 0x1ead: 0xdcb1, 0x1eae: 0xdcd1, 0x1eaf: 0xdcf1, 0x1eb0: 0xdd11, 0x1eb1: 0xdd31, 0x1eb2: 0xdd51, 0x1eb3: 0xdd71, 0x1eb4: 0xdd91, 0x1eb5: 0xddb1, 0x1eb6: 0xddd1, 0x1eb7: 0xddf1, 0x1eb8: 0x8df5, 0x1eb9: 0xde11, 0x1eba: 0xde31, 0x1ebb: 0xde51, 0x1ebc: 0xde71, 0x1ebd: 0xde91, 0x1ebe: 0x8e15, 0x1ebf: 0xdeb1, // Block 0x7b, offset 0x1ec0 0x1ec0: 0xe5b1, 0x1ec1: 0xe5d1, 0x1ec2: 0xe5f1, 0x1ec3: 0xe611, 0x1ec4: 0xe631, 0x1ec5: 0xe651, 0x1ec6: 0x8f35, 0x1ec7: 0xe671, 0x1ec8: 0xe691, 0x1ec9: 0xe6b1, 0x1eca: 0xe6d1, 0x1ecb: 0xe6f1, 0x1ecc: 0xe711, 0x1ecd: 0x8f55, 0x1ece: 0xe731, 0x1ecf: 0xe751, 0x1ed0: 0x8f75, 0x1ed1: 0x8f95, 0x1ed2: 0xe771, 0x1ed3: 0xe791, 0x1ed4: 0xe7b1, 0x1ed5: 0xe7d1, 0x1ed6: 0xe7f1, 0x1ed7: 0xe811, 0x1ed8: 0xe831, 0x1ed9: 0xe851, 0x1eda: 0xe871, 0x1edb: 0x8fb5, 0x1edc: 0xe891, 0x1edd: 0x8fd5, 0x1ede: 0xe8b1, 0x1edf: 0x2040, 0x1ee0: 0xe8d1, 0x1ee1: 0xe8f1, 0x1ee2: 0xe911, 0x1ee3: 0x8ff5, 0x1ee4: 0xe931, 0x1ee5: 0xe951, 0x1ee6: 0x9015, 0x1ee7: 0x9035, 0x1ee8: 0xe971, 0x1ee9: 0xe991, 0x1eea: 0xe9b1, 0x1eeb: 0xe9d1, 0x1eec: 0xe9f1, 0x1eed: 0xe9f1, 0x1eee: 0xea11, 0x1eef: 0xea31, 0x1ef0: 0xea51, 0x1ef1: 0xea71, 0x1ef2: 0xea91, 0x1ef3: 0xeab1, 0x1ef4: 0xead1, 0x1ef5: 0x9055, 0x1ef6: 0xeaf1, 0x1ef7: 0x9075, 0x1ef8: 0xeb11, 0x1ef9: 0x9095, 0x1efa: 0xeb31, 0x1efb: 0x90b5, 0x1efc: 0x90d5, 0x1efd: 0x90f5, 0x1efe: 0xeb51, 0x1eff: 0xeb71, // Block 0x7c, offset 0x1f00 0x1f00: 0xeb91, 0x1f01: 0x9115, 0x1f02: 0x9135, 0x1f03: 0x9155, 0x1f04: 0x9175, 0x1f05: 0xebb1, 0x1f06: 0xebd1, 0x1f07: 0xebd1, 0x1f08: 0xebf1, 0x1f09: 0xec11, 0x1f0a: 0xec31, 0x1f0b: 0xec51, 0x1f0c: 0xec71, 0x1f0d: 0x9195, 0x1f0e: 0xec91, 0x1f0f: 0xecb1, 0x1f10: 0xecd1, 0x1f11: 0xecf1, 0x1f12: 0x91b5, 0x1f13: 0xed11, 0x1f14: 0x91d5, 0x1f15: 0x91f5, 0x1f16: 0xed31, 0x1f17: 0xed51, 0x1f18: 0xed71, 0x1f19: 0xed91, 0x1f1a: 0xedb1, 0x1f1b: 0xedd1, 0x1f1c: 0x9215, 0x1f1d: 0x9235, 0x1f1e: 0x9255, 0x1f1f: 0x2040, 0x1f20: 0xedf1, 0x1f21: 0x9275, 0x1f22: 0xee11, 0x1f23: 0xee31, 0x1f24: 0xee51, 0x1f25: 0x9295, 0x1f26: 0xee71, 0x1f27: 0xee91, 0x1f28: 0xeeb1, 0x1f29: 0xeed1, 0x1f2a: 0xeef1, 0x1f2b: 0x92b5, 0x1f2c: 0xef11, 0x1f2d: 0xef31, 0x1f2e: 0xef51, 0x1f2f: 0xef71, 0x1f30: 0xef91, 0x1f31: 0xefb1, 0x1f32: 0x92d5, 0x1f33: 0x92f5, 0x1f34: 0xefd1, 0x1f35: 0x9315, 0x1f36: 0xeff1, 0x1f37: 0x9335, 0x1f38: 0xf011, 0x1f39: 0xf031, 0x1f3a: 0xf051, 0x1f3b: 0x9355, 0x1f3c: 0x9375, 0x1f3d: 0xf071, 0x1f3e: 0x9395, 0x1f3f: 0xf091, // Block 0x7d, offset 0x1f40 0x1f40: 0xf6d1, 0x1f41: 0xf6f1, 0x1f42: 0xf711, 0x1f43: 0xf731, 0x1f44: 0xf751, 0x1f45: 0x9555, 0x1f46: 0xf771, 0x1f47: 0xf791, 0x1f48: 0xf7b1, 0x1f49: 0xf7d1, 0x1f4a: 0xf7f1, 0x1f4b: 0x9575, 0x1f4c: 0x9595, 0x1f4d: 0xf811, 0x1f4e: 0xf831, 0x1f4f: 0xf851, 0x1f50: 0xf871, 0x1f51: 0xf891, 0x1f52: 0xf8b1, 0x1f53: 0x95b5, 0x1f54: 0xf8d1, 0x1f55: 0xf8f1, 0x1f56: 0xf911, 0x1f57: 0xf931, 0x1f58: 0x95d5, 0x1f59: 0x95f5, 0x1f5a: 0xf951, 0x1f5b: 0xf971, 0x1f5c: 0xf991, 0x1f5d: 0x9615, 0x1f5e: 0xf9b1, 0x1f5f: 0xf9d1, 0x1f60: 0x684d, 0x1f61: 0x9635, 0x1f62: 0xf9f1, 0x1f63: 0xfa11, 0x1f64: 0xfa31, 0x1f65: 0x9655, 0x1f66: 0xfa51, 0x1f67: 0xfa71, 0x1f68: 0xfa91, 0x1f69: 0xfab1, 0x1f6a: 0xfad1, 0x1f6b: 0xfaf1, 0x1f6c: 0xfb11, 0x1f6d: 0x9675, 0x1f6e: 0xfb31, 0x1f6f: 0xfb51, 0x1f70: 0xfb71, 0x1f71: 0x9695, 0x1f72: 0xfb91, 0x1f73: 0xfbb1, 0x1f74: 0xfbd1, 0x1f75: 0xfbf1, 0x1f76: 0x7b6d, 0x1f77: 0x96b5, 0x1f78: 0xfc11, 0x1f79: 0xfc31, 0x1f7a: 0xfc51, 0x1f7b: 0x96d5, 0x1f7c: 0xfc71, 0x1f7d: 0x96f5, 0x1f7e: 0xfc91, 0x1f7f: 0xfc91, // Block 0x7e, offset 0x1f80 0x1f80: 0xfcb1, 0x1f81: 0x9715, 0x1f82: 0xfcd1, 0x1f83: 0xfcf1, 0x1f84: 0xfd11, 0x1f85: 0xfd31, 0x1f86: 0xfd51, 0x1f87: 0xfd71, 0x1f88: 0xfd91, 0x1f89: 0x9735, 0x1f8a: 0xfdb1, 0x1f8b: 0xfdd1, 0x1f8c: 0xfdf1, 0x1f8d: 0xfe11, 0x1f8e: 0xfe31, 0x1f8f: 0xfe51, 0x1f90: 0x9755, 0x1f91: 0xfe71, 0x1f92: 0x9775, 0x1f93: 0x9795, 0x1f94: 0x97b5, 0x1f95: 0xfe91, 0x1f96: 0xfeb1, 0x1f97: 0xfed1, 0x1f98: 0xfef1, 0x1f99: 0xff11, 0x1f9a: 0xff31, 0x1f9b: 0xff51, 0x1f9c: 0xff71, 0x1f9d: 0x97d5, 0x1f9e: 0x0040, 0x1f9f: 0x0040, 0x1fa0: 0x0040, 0x1fa1: 0x0040, 0x1fa2: 0x0040, 0x1fa3: 0x0040, 0x1fa4: 0x0040, 0x1fa5: 0x0040, 0x1fa6: 0x0040, 0x1fa7: 0x0040, 0x1fa8: 0x0040, 0x1fa9: 0x0040, 0x1faa: 0x0040, 0x1fab: 0x0040, 0x1fac: 0x0040, 0x1fad: 0x0040, 0x1fae: 0x0040, 0x1faf: 0x0040, 0x1fb0: 0x0040, 0x1fb1: 0x0040, 0x1fb2: 0x0040, 0x1fb3: 0x0040, 0x1fb4: 0x0040, 0x1fb5: 0x0040, 0x1fb6: 0x0040, 0x1fb7: 0x0040, 0x1fb8: 0x0040, 0x1fb9: 0x0040, 0x1fba: 0x0040, 0x1fbb: 0x0040, 0x1fbc: 0x0040, 0x1fbd: 0x0040, 0x1fbe: 0x0040, 0x1fbf: 0x0040, } // idnaIndex: 36 blocks, 2304 entries, 4608 bytes // Block 0 is the zero block. var idnaIndex = [2304]uint16{ // Block 0x0, offset 0x0 // Block 0x1, offset 0x40 // Block 0x2, offset 0x80 // Block 0x3, offset 0xc0 0xc2: 0x01, 0xc3: 0x7d, 0xc4: 0x02, 0xc5: 0x03, 0xc6: 0x04, 0xc7: 0x05, 0xc8: 0x06, 0xc9: 0x7e, 0xca: 0x7f, 0xcb: 0x07, 0xcc: 0x80, 0xcd: 0x08, 0xce: 0x09, 0xcf: 0x0a, 0xd0: 0x81, 0xd1: 0x0b, 0xd2: 0x0c, 0xd3: 0x0d, 0xd4: 0x0e, 0xd5: 0x82, 0xd6: 0x83, 0xd7: 0x84, 0xd8: 0x0f, 0xd9: 0x10, 0xda: 0x85, 0xdb: 0x11, 0xdc: 0x12, 0xdd: 0x86, 0xde: 0x87, 0xdf: 0x88, 0xe0: 0x02, 0xe1: 0x03, 0xe2: 0x04, 0xe3: 0x05, 0xe4: 0x06, 0xe5: 0x07, 0xe6: 0x07, 0xe7: 0x07, 0xe8: 0x07, 0xe9: 0x08, 0xea: 0x09, 0xeb: 0x07, 0xec: 0x07, 0xed: 0x0a, 0xee: 0x0b, 0xef: 0x0c, 0xf0: 0x1d, 0xf1: 0x1e, 0xf2: 0x1e, 0xf3: 0x20, 0xf4: 0x21, // Block 0x4, offset 0x100 0x120: 0x89, 0x121: 0x13, 0x122: 0x8a, 0x123: 0x8b, 0x124: 0x8c, 0x125: 0x14, 0x126: 0x15, 0x127: 0x16, 0x128: 0x17, 0x129: 0x18, 0x12a: 0x19, 0x12b: 0x1a, 0x12c: 0x1b, 0x12d: 0x1c, 0x12e: 0x1d, 0x12f: 0x8d, 0x130: 0x8e, 0x131: 0x1e, 0x132: 0x1f, 0x133: 0x20, 0x134: 0x8f, 0x135: 0x21, 0x136: 0x90, 0x137: 0x91, 0x138: 0x92, 0x139: 0x93, 0x13a: 0x22, 0x13b: 0x94, 0x13c: 0x95, 0x13d: 0x23, 0x13e: 0x24, 0x13f: 0x96, // Block 0x5, offset 0x140 0x140: 0x97, 0x141: 0x98, 0x142: 0x99, 0x143: 0x9a, 0x144: 0x9b, 0x145: 0x9c, 0x146: 0x9d, 0x147: 0x9e, 0x148: 0x9f, 0x149: 0xa0, 0x14a: 0xa1, 0x14b: 0xa2, 0x14c: 0xa3, 0x14d: 0xa4, 0x14e: 0xa5, 0x14f: 0xa6, 0x150: 0xa7, 0x151: 0x9f, 0x152: 0x9f, 0x153: 0x9f, 0x154: 0x9f, 0x155: 0x9f, 0x156: 0x9f, 0x157: 0x9f, 0x158: 0x9f, 0x159: 0xa8, 0x15a: 0xa9, 0x15b: 0xaa, 0x15c: 0xab, 0x15d: 0xac, 0x15e: 0xad, 0x15f: 0xae, 0x160: 0xaf, 0x161: 0xb0, 0x162: 0xb1, 0x163: 0xb2, 0x164: 0xb3, 0x165: 0xb4, 0x166: 0xb5, 0x167: 0xb6, 0x168: 0xb7, 0x169: 0xb8, 0x16a: 0xb9, 0x16b: 0xba, 0x16c: 0xbb, 0x16d: 0xbc, 0x16e: 0xbd, 0x16f: 0xbe, 0x170: 0xbf, 0x171: 0xc0, 0x172: 0xc1, 0x173: 0xc2, 0x174: 0x25, 0x175: 0x26, 0x176: 0x27, 0x177: 0xc3, 0x178: 0x28, 0x179: 0x28, 0x17a: 0x29, 0x17b: 0x28, 0x17c: 0xc4, 0x17d: 0x2a, 0x17e: 0x2b, 0x17f: 0x2c, // Block 0x6, offset 0x180 0x180: 0x2d, 0x181: 0x2e, 0x182: 0x2f, 0x183: 0xc5, 0x184: 0x30, 0x185: 0x31, 0x186: 0xc6, 0x187: 0x9b, 0x188: 0xc7, 0x189: 0xc8, 0x18a: 0x9b, 0x18b: 0x9b, 0x18c: 0xc9, 0x18d: 0x9b, 0x18e: 0x9b, 0x18f: 0x9b, 0x190: 0xca, 0x191: 0x32, 0x192: 0x33, 0x193: 0x34, 0x194: 0x9b, 0x195: 0x9b, 0x196: 0x9b, 0x197: 0x9b, 0x198: 0x9b, 0x199: 0x9b, 0x19a: 0x9b, 0x19b: 0x9b, 0x19c: 0x9b, 0x19d: 0x9b, 0x19e: 0x9b, 0x19f: 0x9b, 0x1a0: 0x9b, 0x1a1: 0x9b, 0x1a2: 0x9b, 0x1a3: 0x9b, 0x1a4: 0x9b, 0x1a5: 0x9b, 0x1a6: 0x9b, 0x1a7: 0x9b, 0x1a8: 0xcb, 0x1a9: 0xcc, 0x1aa: 0x9b, 0x1ab: 0xcd, 0x1ac: 0x9b, 0x1ad: 0xce, 0x1ae: 0xcf, 0x1af: 0x9b, 0x1b0: 0xd0, 0x1b1: 0x35, 0x1b2: 0x28, 0x1b3: 0x36, 0x1b4: 0xd1, 0x1b5: 0xd2, 0x1b6: 0xd3, 0x1b7: 0xd4, 0x1b8: 0xd5, 0x1b9: 0xd6, 0x1ba: 0xd7, 0x1bb: 0xd8, 0x1bc: 0xd9, 0x1bd: 0xda, 0x1be: 0xdb, 0x1bf: 0x37, // Block 0x7, offset 0x1c0 0x1c0: 0x38, 0x1c1: 0xdc, 0x1c2: 0xdd, 0x1c3: 0xde, 0x1c4: 0xdf, 0x1c5: 0x39, 0x1c6: 0x3a, 0x1c7: 0xe0, 0x1c8: 0xe1, 0x1c9: 0x3b, 0x1ca: 0x3c, 0x1cb: 0x3d, 0x1cc: 0x3e, 0x1cd: 0x3f, 0x1ce: 0x40, 0x1cf: 0x41, 0x1d0: 0x9f, 0x1d1: 0x9f, 0x1d2: 0x9f, 0x1d3: 0x9f, 0x1d4: 0x9f, 0x1d5: 0x9f, 0x1d6: 0x9f, 0x1d7: 0x9f, 0x1d8: 0x9f, 0x1d9: 0x9f, 0x1da: 0x9f, 0x1db: 0x9f, 0x1dc: 0x9f, 0x1dd: 0x9f, 0x1de: 0x9f, 0x1df: 0x9f, 0x1e0: 0x9f, 0x1e1: 0x9f, 0x1e2: 0x9f, 0x1e3: 0x9f, 0x1e4: 0x9f, 0x1e5: 0x9f, 0x1e6: 0x9f, 0x1e7: 0x9f, 0x1e8: 0x9f, 0x1e9: 0x9f, 0x1ea: 0x9f, 0x1eb: 0x9f, 0x1ec: 0x9f, 0x1ed: 0x9f, 0x1ee: 0x9f, 0x1ef: 0x9f, 0x1f0: 0x9f, 0x1f1: 0x9f, 0x1f2: 0x9f, 0x1f3: 0x9f, 0x1f4: 0x9f, 0x1f5: 0x9f, 0x1f6: 0x9f, 0x1f7: 0x9f, 0x1f8: 0x9f, 0x1f9: 0x9f, 0x1fa: 0x9f, 0x1fb: 0x9f, 0x1fc: 0x9f, 0x1fd: 0x9f, 0x1fe: 0x9f, 0x1ff: 0x9f, // Block 0x8, offset 0x200 0x200: 0x9f, 0x201: 0x9f, 0x202: 0x9f, 0x203: 0x9f, 0x204: 0x9f, 0x205: 0x9f, 0x206: 0x9f, 0x207: 0x9f, 0x208: 0x9f, 0x209: 0x9f, 0x20a: 0x9f, 0x20b: 0x9f, 0x20c: 0x9f, 0x20d: 0x9f, 0x20e: 0x9f, 0x20f: 0x9f, 0x210: 0x9f, 0x211: 0x9f, 0x212: 0x9f, 0x213: 0x9f, 0x214: 0x9f, 0x215: 0x9f, 0x216: 0x9f, 0x217: 0x9f, 0x218: 0x9f, 0x219: 0x9f, 0x21a: 0x9f, 0x21b: 0x9f, 0x21c: 0x9f, 0x21d: 0x9f, 0x21e: 0x9f, 0x21f: 0x9f, 0x220: 0x9f, 0x221: 0x9f, 0x222: 0x9f, 0x223: 0x9f, 0x224: 0x9f, 0x225: 0x9f, 0x226: 0x9f, 0x227: 0x9f, 0x228: 0x9f, 0x229: 0x9f, 0x22a: 0x9f, 0x22b: 0x9f, 0x22c: 0x9f, 0x22d: 0x9f, 0x22e: 0x9f, 0x22f: 0x9f, 0x230: 0x9f, 0x231: 0x9f, 0x232: 0x9f, 0x233: 0x9f, 0x234: 0x9f, 0x235: 0x9f, 0x236: 0xb2, 0x237: 0x9b, 0x238: 0x9f, 0x239: 0x9f, 0x23a: 0x9f, 0x23b: 0x9f, 0x23c: 0x9f, 0x23d: 0x9f, 0x23e: 0x9f, 0x23f: 0x9f, // Block 0x9, offset 0x240 0x240: 0x9f, 0x241: 0x9f, 0x242: 0x9f, 0x243: 0x9f, 0x244: 0x9f, 0x245: 0x9f, 0x246: 0x9f, 0x247: 0x9f, 0x248: 0x9f, 0x249: 0x9f, 0x24a: 0x9f, 0x24b: 0x9f, 0x24c: 0x9f, 0x24d: 0x9f, 0x24e: 0x9f, 0x24f: 0x9f, 0x250: 0x9f, 0x251: 0x9f, 0x252: 0x9f, 0x253: 0x9f, 0x254: 0x9f, 0x255: 0x9f, 0x256: 0x9f, 0x257: 0x9f, 0x258: 0x9f, 0x259: 0x9f, 0x25a: 0x9f, 0x25b: 0x9f, 0x25c: 0x9f, 0x25d: 0x9f, 0x25e: 0x9f, 0x25f: 0x9f, 0x260: 0x9f, 0x261: 0x9f, 0x262: 0x9f, 0x263: 0x9f, 0x264: 0x9f, 0x265: 0x9f, 0x266: 0x9f, 0x267: 0x9f, 0x268: 0x9f, 0x269: 0x9f, 0x26a: 0x9f, 0x26b: 0x9f, 0x26c: 0x9f, 0x26d: 0x9f, 0x26e: 0x9f, 0x26f: 0x9f, 0x270: 0x9f, 0x271: 0x9f, 0x272: 0x9f, 0x273: 0x9f, 0x274: 0x9f, 0x275: 0x9f, 0x276: 0x9f, 0x277: 0x9f, 0x278: 0x9f, 0x279: 0x9f, 0x27a: 0x9f, 0x27b: 0x9f, 0x27c: 0x9f, 0x27d: 0x9f, 0x27e: 0x9f, 0x27f: 0x9f, // Block 0xa, offset 0x280 0x280: 0x9f, 0x281: 0x9f, 0x282: 0x9f, 0x283: 0x9f, 0x284: 0x9f, 0x285: 0x9f, 0x286: 0x9f, 0x287: 0x9f, 0x288: 0x9f, 0x289: 0x9f, 0x28a: 0x9f, 0x28b: 0x9f, 0x28c: 0x9f, 0x28d: 0x9f, 0x28e: 0x9f, 0x28f: 0x9f, 0x290: 0x9f, 0x291: 0x9f, 0x292: 0x9f, 0x293: 0x9f, 0x294: 0x9f, 0x295: 0x9f, 0x296: 0x9f, 0x297: 0x9f, 0x298: 0x9f, 0x299: 0x9f, 0x29a: 0x9f, 0x29b: 0x9f, 0x29c: 0x9f, 0x29d: 0x9f, 0x29e: 0x9f, 0x29f: 0x9f, 0x2a0: 0x9f, 0x2a1: 0x9f, 0x2a2: 0x9f, 0x2a3: 0x9f, 0x2a4: 0x9f, 0x2a5: 0x9f, 0x2a6: 0x9f, 0x2a7: 0x9f, 0x2a8: 0x9f, 0x2a9: 0x9f, 0x2aa: 0x9f, 0x2ab: 0x9f, 0x2ac: 0x9f, 0x2ad: 0x9f, 0x2ae: 0x9f, 0x2af: 0x9f, 0x2b0: 0x9f, 0x2b1: 0x9f, 0x2b2: 0x9f, 0x2b3: 0x9f, 0x2b4: 0x9f, 0x2b5: 0x9f, 0x2b6: 0x9f, 0x2b7: 0x9f, 0x2b8: 0x9f, 0x2b9: 0x9f, 0x2ba: 0x9f, 0x2bb: 0x9f, 0x2bc: 0x9f, 0x2bd: 0x9f, 0x2be: 0x9f, 0x2bf: 0xe2, // Block 0xb, offset 0x2c0 0x2c0: 0x9f, 0x2c1: 0x9f, 0x2c2: 0x9f, 0x2c3: 0x9f, 0x2c4: 0x9f, 0x2c5: 0x9f, 0x2c6: 0x9f, 0x2c7: 0x9f, 0x2c8: 0x9f, 0x2c9: 0x9f, 0x2ca: 0x9f, 0x2cb: 0x9f, 0x2cc: 0x9f, 0x2cd: 0x9f, 0x2ce: 0x9f, 0x2cf: 0x9f, 0x2d0: 0x9f, 0x2d1: 0x9f, 0x2d2: 0xe3, 0x2d3: 0xe4, 0x2d4: 0x9f, 0x2d5: 0x9f, 0x2d6: 0x9f, 0x2d7: 0x9f, 0x2d8: 0xe5, 0x2d9: 0x42, 0x2da: 0x43, 0x2db: 0xe6, 0x2dc: 0x44, 0x2dd: 0x45, 0x2de: 0x46, 0x2df: 0xe7, 0x2e0: 0xe8, 0x2e1: 0xe9, 0x2e2: 0xea, 0x2e3: 0xeb, 0x2e4: 0xec, 0x2e5: 0xed, 0x2e6: 0xee, 0x2e7: 0xef, 0x2e8: 0xf0, 0x2e9: 0xf1, 0x2ea: 0xf2, 0x2eb: 0xf3, 0x2ec: 0xf4, 0x2ed: 0xf5, 0x2ee: 0xf6, 0x2ef: 0xf7, 0x2f0: 0x9f, 0x2f1: 0x9f, 0x2f2: 0x9f, 0x2f3: 0x9f, 0x2f4: 0x9f, 0x2f5: 0x9f, 0x2f6: 0x9f, 0x2f7: 0x9f, 0x2f8: 0x9f, 0x2f9: 0x9f, 0x2fa: 0x9f, 0x2fb: 0x9f, 0x2fc: 0x9f, 0x2fd: 0x9f, 0x2fe: 0x9f, 0x2ff: 0x9f, // Block 0xc, offset 0x300 0x300: 0x9f, 0x301: 0x9f, 0x302: 0x9f, 0x303: 0x9f, 0x304: 0x9f, 0x305: 0x9f, 0x306: 0x9f, 0x307: 0x9f, 0x308: 0x9f, 0x309: 0x9f, 0x30a: 0x9f, 0x30b: 0x9f, 0x30c: 0x9f, 0x30d: 0x9f, 0x30e: 0x9f, 0x30f: 0x9f, 0x310: 0x9f, 0x311: 0x9f, 0x312: 0x9f, 0x313: 0x9f, 0x314: 0x9f, 0x315: 0x9f, 0x316: 0x9f, 0x317: 0x9f, 0x318: 0x9f, 0x319: 0x9f, 0x31a: 0x9f, 0x31b: 0x9f, 0x31c: 0x9f, 0x31d: 0x9f, 0x31e: 0xf8, 0x31f: 0xf9, // Block 0xd, offset 0x340 0x340: 0xba, 0x341: 0xba, 0x342: 0xba, 0x343: 0xba, 0x344: 0xba, 0x345: 0xba, 0x346: 0xba, 0x347: 0xba, 0x348: 0xba, 0x349: 0xba, 0x34a: 0xba, 0x34b: 0xba, 0x34c: 0xba, 0x34d: 0xba, 0x34e: 0xba, 0x34f: 0xba, 0x350: 0xba, 0x351: 0xba, 0x352: 0xba, 0x353: 0xba, 0x354: 0xba, 0x355: 0xba, 0x356: 0xba, 0x357: 0xba, 0x358: 0xba, 0x359: 0xba, 0x35a: 0xba, 0x35b: 0xba, 0x35c: 0xba, 0x35d: 0xba, 0x35e: 0xba, 0x35f: 0xba, 0x360: 0xba, 0x361: 0xba, 0x362: 0xba, 0x363: 0xba, 0x364: 0xba, 0x365: 0xba, 0x366: 0xba, 0x367: 0xba, 0x368: 0xba, 0x369: 0xba, 0x36a: 0xba, 0x36b: 0xba, 0x36c: 0xba, 0x36d: 0xba, 0x36e: 0xba, 0x36f: 0xba, 0x370: 0xba, 0x371: 0xba, 0x372: 0xba, 0x373: 0xba, 0x374: 0xba, 0x375: 0xba, 0x376: 0xba, 0x377: 0xba, 0x378: 0xba, 0x379: 0xba, 0x37a: 0xba, 0x37b: 0xba, 0x37c: 0xba, 0x37d: 0xba, 0x37e: 0xba, 0x37f: 0xba, // Block 0xe, offset 0x380 0x380: 0xba, 0x381: 0xba, 0x382: 0xba, 0x383: 0xba, 0x384: 0xba, 0x385: 0xba, 0x386: 0xba, 0x387: 0xba, 0x388: 0xba, 0x389: 0xba, 0x38a: 0xba, 0x38b: 0xba, 0x38c: 0xba, 0x38d: 0xba, 0x38e: 0xba, 0x38f: 0xba, 0x390: 0xba, 0x391: 0xba, 0x392: 0xba, 0x393: 0xba, 0x394: 0xba, 0x395: 0xba, 0x396: 0xba, 0x397: 0xba, 0x398: 0xba, 0x399: 0xba, 0x39a: 0xba, 0x39b: 0xba, 0x39c: 0xba, 0x39d: 0xba, 0x39e: 0xba, 0x39f: 0xba, 0x3a0: 0xba, 0x3a1: 0xba, 0x3a2: 0xba, 0x3a3: 0xba, 0x3a4: 0xfa, 0x3a5: 0xfb, 0x3a6: 0xfc, 0x3a7: 0xfd, 0x3a8: 0x47, 0x3a9: 0xfe, 0x3aa: 0xff, 0x3ab: 0x48, 0x3ac: 0x49, 0x3ad: 0x4a, 0x3ae: 0x4b, 0x3af: 0x4c, 0x3b0: 0x100, 0x3b1: 0x4d, 0x3b2: 0x4e, 0x3b3: 0x4f, 0x3b4: 0x50, 0x3b5: 0x51, 0x3b6: 0x101, 0x3b7: 0x52, 0x3b8: 0x53, 0x3b9: 0x54, 0x3ba: 0x55, 0x3bb: 0x56, 0x3bc: 0x57, 0x3bd: 0x58, 0x3be: 0x59, 0x3bf: 0x5a, // Block 0xf, offset 0x3c0 0x3c0: 0x102, 0x3c1: 0x103, 0x3c2: 0x9f, 0x3c3: 0x104, 0x3c4: 0x105, 0x3c5: 0x9b, 0x3c6: 0x106, 0x3c7: 0x107, 0x3c8: 0xba, 0x3c9: 0xba, 0x3ca: 0x108, 0x3cb: 0x109, 0x3cc: 0x10a, 0x3cd: 0x10b, 0x3ce: 0x10c, 0x3cf: 0x10d, 0x3d0: 0x10e, 0x3d1: 0x9f, 0x3d2: 0x10f, 0x3d3: 0x110, 0x3d4: 0x111, 0x3d5: 0x112, 0x3d6: 0xba, 0x3d7: 0xba, 0x3d8: 0x9f, 0x3d9: 0x9f, 0x3da: 0x9f, 0x3db: 0x9f, 0x3dc: 0x113, 0x3dd: 0x114, 0x3de: 0xba, 0x3df: 0xba, 0x3e0: 0x115, 0x3e1: 0x116, 0x3e2: 0x117, 0x3e3: 0x118, 0x3e4: 0x119, 0x3e5: 0xba, 0x3e6: 0x11a, 0x3e7: 0x11b, 0x3e8: 0x11c, 0x3e9: 0x11d, 0x3ea: 0x11e, 0x3eb: 0x5b, 0x3ec: 0x11f, 0x3ed: 0x120, 0x3ee: 0x5c, 0x3ef: 0xba, 0x3f0: 0x121, 0x3f1: 0x122, 0x3f2: 0x123, 0x3f3: 0x124, 0x3f4: 0x125, 0x3f5: 0xba, 0x3f6: 0xba, 0x3f7: 0xba, 0x3f8: 0xba, 0x3f9: 0x126, 0x3fa: 0xba, 0x3fb: 0xba, 0x3fc: 0x127, 0x3fd: 0x128, 0x3fe: 0xba, 0x3ff: 0x129, // Block 0x10, offset 0x400 0x400: 0x12a, 0x401: 0x12b, 0x402: 0x12c, 0x403: 0x12d, 0x404: 0x12e, 0x405: 0x12f, 0x406: 0x130, 0x407: 0x131, 0x408: 0x132, 0x409: 0xba, 0x40a: 0x133, 0x40b: 0x134, 0x40c: 0x5d, 0x40d: 0x5e, 0x40e: 0xba, 0x40f: 0xba, 0x410: 0x135, 0x411: 0x136, 0x412: 0x137, 0x413: 0x138, 0x414: 0xba, 0x415: 0xba, 0x416: 0x139, 0x417: 0x13a, 0x418: 0x13b, 0x419: 0x13c, 0x41a: 0x13d, 0x41b: 0x13e, 0x41c: 0x13f, 0x41d: 0xba, 0x41e: 0xba, 0x41f: 0xba, 0x420: 0x140, 0x421: 0xba, 0x422: 0x141, 0x423: 0x142, 0x424: 0xba, 0x425: 0xba, 0x426: 0x143, 0x427: 0x144, 0x428: 0x145, 0x429: 0x146, 0x42a: 0x147, 0x42b: 0x148, 0x42c: 0xba, 0x42d: 0xba, 0x42e: 0xba, 0x42f: 0xba, 0x430: 0x149, 0x431: 0x14a, 0x432: 0x14b, 0x433: 0xba, 0x434: 0x14c, 0x435: 0x14d, 0x436: 0x14e, 0x437: 0xba, 0x438: 0xba, 0x439: 0xba, 0x43a: 0xba, 0x43b: 0x14f, 0x43c: 0xba, 0x43d: 0xba, 0x43e: 0xba, 0x43f: 0x150, // Block 0x11, offset 0x440 0x440: 0x9f, 0x441: 0x9f, 0x442: 0x9f, 0x443: 0x9f, 0x444: 0x9f, 0x445: 0x9f, 0x446: 0x9f, 0x447: 0x9f, 0x448: 0x9f, 0x449: 0x9f, 0x44a: 0x9f, 0x44b: 0x9f, 0x44c: 0x9f, 0x44d: 0x9f, 0x44e: 0x151, 0x44f: 0xba, 0x450: 0x9b, 0x451: 0x152, 0x452: 0x9f, 0x453: 0x9f, 0x454: 0x9f, 0x455: 0x153, 0x456: 0xba, 0x457: 0xba, 0x458: 0xba, 0x459: 0xba, 0x45a: 0xba, 0x45b: 0xba, 0x45c: 0xba, 0x45d: 0xba, 0x45e: 0xba, 0x45f: 0xba, 0x460: 0xba, 0x461: 0xba, 0x462: 0xba, 0x463: 0xba, 0x464: 0xba, 0x465: 0xba, 0x466: 0xba, 0x467: 0xba, 0x468: 0xba, 0x469: 0xba, 0x46a: 0xba, 0x46b: 0xba, 0x46c: 0xba, 0x46d: 0xba, 0x46e: 0xba, 0x46f: 0xba, 0x470: 0xba, 0x471: 0xba, 0x472: 0xba, 0x473: 0xba, 0x474: 0xba, 0x475: 0xba, 0x476: 0xba, 0x477: 0xba, 0x478: 0xba, 0x479: 0xba, 0x47a: 0xba, 0x47b: 0xba, 0x47c: 0xba, 0x47d: 0xba, 0x47e: 0xba, 0x47f: 0xba, // Block 0x12, offset 0x480 0x480: 0x9f, 0x481: 0x9f, 0x482: 0x9f, 0x483: 0x9f, 0x484: 0x9f, 0x485: 0x9f, 0x486: 0x9f, 0x487: 0x9f, 0x488: 0x9f, 0x489: 0x9f, 0x48a: 0x9f, 0x48b: 0x9f, 0x48c: 0x9f, 0x48d: 0x9f, 0x48e: 0x9f, 0x48f: 0x9f, 0x490: 0x154, 0x491: 0xba, 0x492: 0xba, 0x493: 0xba, 0x494: 0xba, 0x495: 0xba, 0x496: 0xba, 0x497: 0xba, 0x498: 0xba, 0x499: 0xba, 0x49a: 0xba, 0x49b: 0xba, 0x49c: 0xba, 0x49d: 0xba, 0x49e: 0xba, 0x49f: 0xba, 0x4a0: 0xba, 0x4a1: 0xba, 0x4a2: 0xba, 0x4a3: 0xba, 0x4a4: 0xba, 0x4a5: 0xba, 0x4a6: 0xba, 0x4a7: 0xba, 0x4a8: 0xba, 0x4a9: 0xba, 0x4aa: 0xba, 0x4ab: 0xba, 0x4ac: 0xba, 0x4ad: 0xba, 0x4ae: 0xba, 0x4af: 0xba, 0x4b0: 0xba, 0x4b1: 0xba, 0x4b2: 0xba, 0x4b3: 0xba, 0x4b4: 0xba, 0x4b5: 0xba, 0x4b6: 0xba, 0x4b7: 0xba, 0x4b8: 0xba, 0x4b9: 0xba, 0x4ba: 0xba, 0x4bb: 0xba, 0x4bc: 0xba, 0x4bd: 0xba, 0x4be: 0xba, 0x4bf: 0xba, // Block 0x13, offset 0x4c0 0x4c0: 0xba, 0x4c1: 0xba, 0x4c2: 0xba, 0x4c3: 0xba, 0x4c4: 0xba, 0x4c5: 0xba, 0x4c6: 0xba, 0x4c7: 0xba, 0x4c8: 0xba, 0x4c9: 0xba, 0x4ca: 0xba, 0x4cb: 0xba, 0x4cc: 0xba, 0x4cd: 0xba, 0x4ce: 0xba, 0x4cf: 0xba, 0x4d0: 0x9f, 0x4d1: 0x9f, 0x4d2: 0x9f, 0x4d3: 0x9f, 0x4d4: 0x9f, 0x4d5: 0x9f, 0x4d6: 0x9f, 0x4d7: 0x9f, 0x4d8: 0x9f, 0x4d9: 0x155, 0x4da: 0xba, 0x4db: 0xba, 0x4dc: 0xba, 0x4dd: 0xba, 0x4de: 0xba, 0x4df: 0xba, 0x4e0: 0xba, 0x4e1: 0xba, 0x4e2: 0xba, 0x4e3: 0xba, 0x4e4: 0xba, 0x4e5: 0xba, 0x4e6: 0xba, 0x4e7: 0xba, 0x4e8: 0xba, 0x4e9: 0xba, 0x4ea: 0xba, 0x4eb: 0xba, 0x4ec: 0xba, 0x4ed: 0xba, 0x4ee: 0xba, 0x4ef: 0xba, 0x4f0: 0xba, 0x4f1: 0xba, 0x4f2: 0xba, 0x4f3: 0xba, 0x4f4: 0xba, 0x4f5: 0xba, 0x4f6: 0xba, 0x4f7: 0xba, 0x4f8: 0xba, 0x4f9: 0xba, 0x4fa: 0xba, 0x4fb: 0xba, 0x4fc: 0xba, 0x4fd: 0xba, 0x4fe: 0xba, 0x4ff: 0xba, // Block 0x14, offset 0x500 0x500: 0xba, 0x501: 0xba, 0x502: 0xba, 0x503: 0xba, 0x504: 0xba, 0x505: 0xba, 0x506: 0xba, 0x507: 0xba, 0x508: 0xba, 0x509: 0xba, 0x50a: 0xba, 0x50b: 0xba, 0x50c: 0xba, 0x50d: 0xba, 0x50e: 0xba, 0x50f: 0xba, 0x510: 0xba, 0x511: 0xba, 0x512: 0xba, 0x513: 0xba, 0x514: 0xba, 0x515: 0xba, 0x516: 0xba, 0x517: 0xba, 0x518: 0xba, 0x519: 0xba, 0x51a: 0xba, 0x51b: 0xba, 0x51c: 0xba, 0x51d: 0xba, 0x51e: 0xba, 0x51f: 0xba, 0x520: 0x9f, 0x521: 0x9f, 0x522: 0x9f, 0x523: 0x9f, 0x524: 0x9f, 0x525: 0x9f, 0x526: 0x9f, 0x527: 0x9f, 0x528: 0x148, 0x529: 0x156, 0x52a: 0xba, 0x52b: 0x157, 0x52c: 0x158, 0x52d: 0x159, 0x52e: 0x15a, 0x52f: 0xba, 0x530: 0xba, 0x531: 0xba, 0x532: 0xba, 0x533: 0xba, 0x534: 0xba, 0x535: 0xba, 0x536: 0xba, 0x537: 0xba, 0x538: 0xba, 0x539: 0x15b, 0x53a: 0x15c, 0x53b: 0xba, 0x53c: 0x9f, 0x53d: 0x15d, 0x53e: 0x15e, 0x53f: 0x15f, // Block 0x15, offset 0x540 0x540: 0x9f, 0x541: 0x9f, 0x542: 0x9f, 0x543: 0x9f, 0x544: 0x9f, 0x545: 0x9f, 0x546: 0x9f, 0x547: 0x9f, 0x548: 0x9f, 0x549: 0x9f, 0x54a: 0x9f, 0x54b: 0x9f, 0x54c: 0x9f, 0x54d: 0x9f, 0x54e: 0x9f, 0x54f: 0x9f, 0x550: 0x9f, 0x551: 0x9f, 0x552: 0x9f, 0x553: 0x9f, 0x554: 0x9f, 0x555: 0x9f, 0x556: 0x9f, 0x557: 0x9f, 0x558: 0x9f, 0x559: 0x9f, 0x55a: 0x9f, 0x55b: 0x9f, 0x55c: 0x9f, 0x55d: 0x9f, 0x55e: 0x9f, 0x55f: 0x160, 0x560: 0x9f, 0x561: 0x9f, 0x562: 0x9f, 0x563: 0x9f, 0x564: 0x9f, 0x565: 0x9f, 0x566: 0x9f, 0x567: 0x9f, 0x568: 0x9f, 0x569: 0x9f, 0x56a: 0x9f, 0x56b: 0x161, 0x56c: 0xba, 0x56d: 0xba, 0x56e: 0xba, 0x56f: 0xba, 0x570: 0xba, 0x571: 0xba, 0x572: 0xba, 0x573: 0xba, 0x574: 0xba, 0x575: 0xba, 0x576: 0xba, 0x577: 0xba, 0x578: 0xba, 0x579: 0xba, 0x57a: 0xba, 0x57b: 0xba, 0x57c: 0xba, 0x57d: 0xba, 0x57e: 0xba, 0x57f: 0xba, // Block 0x16, offset 0x580 0x580: 0x9f, 0x581: 0x9f, 0x582: 0x9f, 0x583: 0x9f, 0x584: 0x162, 0x585: 0x163, 0x586: 0x9f, 0x587: 0x9f, 0x588: 0x9f, 0x589: 0x9f, 0x58a: 0x9f, 0x58b: 0x164, 0x58c: 0xba, 0x58d: 0xba, 0x58e: 0xba, 0x58f: 0xba, 0x590: 0xba, 0x591: 0xba, 0x592: 0xba, 0x593: 0xba, 0x594: 0xba, 0x595: 0xba, 0x596: 0xba, 0x597: 0xba, 0x598: 0xba, 0x599: 0xba, 0x59a: 0xba, 0x59b: 0xba, 0x59c: 0xba, 0x59d: 0xba, 0x59e: 0xba, 0x59f: 0xba, 0x5a0: 0xba, 0x5a1: 0xba, 0x5a2: 0xba, 0x5a3: 0xba, 0x5a4: 0xba, 0x5a5: 0xba, 0x5a6: 0xba, 0x5a7: 0xba, 0x5a8: 0xba, 0x5a9: 0xba, 0x5aa: 0xba, 0x5ab: 0xba, 0x5ac: 0xba, 0x5ad: 0xba, 0x5ae: 0xba, 0x5af: 0xba, 0x5b0: 0x9f, 0x5b1: 0x165, 0x5b2: 0x166, 0x5b3: 0xba, 0x5b4: 0xba, 0x5b5: 0xba, 0x5b6: 0xba, 0x5b7: 0xba, 0x5b8: 0xba, 0x5b9: 0xba, 0x5ba: 0xba, 0x5bb: 0xba, 0x5bc: 0xba, 0x5bd: 0xba, 0x5be: 0xba, 0x5bf: 0xba, // Block 0x17, offset 0x5c0 0x5c0: 0x9b, 0x5c1: 0x9b, 0x5c2: 0x9b, 0x5c3: 0x167, 0x5c4: 0x168, 0x5c5: 0x169, 0x5c6: 0x16a, 0x5c7: 0x16b, 0x5c8: 0x9b, 0x5c9: 0x16c, 0x5ca: 0xba, 0x5cb: 0x16d, 0x5cc: 0x9b, 0x5cd: 0x16e, 0x5ce: 0xba, 0x5cf: 0xba, 0x5d0: 0x5f, 0x5d1: 0x60, 0x5d2: 0x61, 0x5d3: 0x62, 0x5d4: 0x63, 0x5d5: 0x64, 0x5d6: 0x65, 0x5d7: 0x66, 0x5d8: 0x67, 0x5d9: 0x68, 0x5da: 0x69, 0x5db: 0x6a, 0x5dc: 0x6b, 0x5dd: 0x6c, 0x5de: 0x6d, 0x5df: 0x6e, 0x5e0: 0x9b, 0x5e1: 0x9b, 0x5e2: 0x9b, 0x5e3: 0x9b, 0x5e4: 0x9b, 0x5e5: 0x9b, 0x5e6: 0x9b, 0x5e7: 0x9b, 0x5e8: 0x16f, 0x5e9: 0x170, 0x5ea: 0x171, 0x5eb: 0xba, 0x5ec: 0xba, 0x5ed: 0xba, 0x5ee: 0xba, 0x5ef: 0xba, 0x5f0: 0xba, 0x5f1: 0xba, 0x5f2: 0xba, 0x5f3: 0xba, 0x5f4: 0xba, 0x5f5: 0xba, 0x5f6: 0xba, 0x5f7: 0xba, 0x5f8: 0xba, 0x5f9: 0xba, 0x5fa: 0xba, 0x5fb: 0xba, 0x5fc: 0xba, 0x5fd: 0xba, 0x5fe: 0xba, 0x5ff: 0xba, // Block 0x18, offset 0x600 0x600: 0x172, 0x601: 0xba, 0x602: 0xba, 0x603: 0xba, 0x604: 0x173, 0x605: 0x174, 0x606: 0xba, 0x607: 0xba, 0x608: 0xba, 0x609: 0xba, 0x60a: 0xba, 0x60b: 0x175, 0x60c: 0xba, 0x60d: 0xba, 0x60e: 0xba, 0x60f: 0xba, 0x610: 0xba, 0x611: 0xba, 0x612: 0xba, 0x613: 0xba, 0x614: 0xba, 0x615: 0xba, 0x616: 0xba, 0x617: 0xba, 0x618: 0xba, 0x619: 0xba, 0x61a: 0xba, 0x61b: 0xba, 0x61c: 0xba, 0x61d: 0xba, 0x61e: 0xba, 0x61f: 0xba, 0x620: 0x121, 0x621: 0x121, 0x622: 0x121, 0x623: 0x176, 0x624: 0x6f, 0x625: 0x177, 0x626: 0xba, 0x627: 0xba, 0x628: 0xba, 0x629: 0xba, 0x62a: 0xba, 0x62b: 0xba, 0x62c: 0xba, 0x62d: 0xba, 0x62e: 0xba, 0x62f: 0xba, 0x630: 0xba, 0x631: 0x178, 0x632: 0x179, 0x633: 0xba, 0x634: 0x17a, 0x635: 0xba, 0x636: 0xba, 0x637: 0xba, 0x638: 0x70, 0x639: 0x71, 0x63a: 0x72, 0x63b: 0x17b, 0x63c: 0xba, 0x63d: 0xba, 0x63e: 0xba, 0x63f: 0xba, // Block 0x19, offset 0x640 0x640: 0x17c, 0x641: 0x9b, 0x642: 0x17d, 0x643: 0x17e, 0x644: 0x73, 0x645: 0x74, 0x646: 0x17f, 0x647: 0x180, 0x648: 0x75, 0x649: 0x181, 0x64a: 0xba, 0x64b: 0xba, 0x64c: 0x9b, 0x64d: 0x9b, 0x64e: 0x9b, 0x64f: 0x9b, 0x650: 0x9b, 0x651: 0x9b, 0x652: 0x9b, 0x653: 0x9b, 0x654: 0x9b, 0x655: 0x9b, 0x656: 0x9b, 0x657: 0x9b, 0x658: 0x9b, 0x659: 0x9b, 0x65a: 0x9b, 0x65b: 0x182, 0x65c: 0x9b, 0x65d: 0x183, 0x65e: 0x9b, 0x65f: 0x184, 0x660: 0x185, 0x661: 0x186, 0x662: 0x187, 0x663: 0xba, 0x664: 0x188, 0x665: 0x189, 0x666: 0x18a, 0x667: 0x18b, 0x668: 0x9b, 0x669: 0x18c, 0x66a: 0x18d, 0x66b: 0xba, 0x66c: 0xba, 0x66d: 0xba, 0x66e: 0xba, 0x66f: 0xba, 0x670: 0xba, 0x671: 0xba, 0x672: 0xba, 0x673: 0xba, 0x674: 0xba, 0x675: 0xba, 0x676: 0xba, 0x677: 0xba, 0x678: 0xba, 0x679: 0xba, 0x67a: 0xba, 0x67b: 0xba, 0x67c: 0xba, 0x67d: 0xba, 0x67e: 0xba, 0x67f: 0xba, // Block 0x1a, offset 0x680 0x680: 0x9f, 0x681: 0x9f, 0x682: 0x9f, 0x683: 0x9f, 0x684: 0x9f, 0x685: 0x9f, 0x686: 0x9f, 0x687: 0x9f, 0x688: 0x9f, 0x689: 0x9f, 0x68a: 0x9f, 0x68b: 0x9f, 0x68c: 0x9f, 0x68d: 0x9f, 0x68e: 0x9f, 0x68f: 0x9f, 0x690: 0x9f, 0x691: 0x9f, 0x692: 0x9f, 0x693: 0x9f, 0x694: 0x9f, 0x695: 0x9f, 0x696: 0x9f, 0x697: 0x9f, 0x698: 0x9f, 0x699: 0x9f, 0x69a: 0x9f, 0x69b: 0x18e, 0x69c: 0x9f, 0x69d: 0x9f, 0x69e: 0x9f, 0x69f: 0x9f, 0x6a0: 0x9f, 0x6a1: 0x9f, 0x6a2: 0x9f, 0x6a3: 0x9f, 0x6a4: 0x9f, 0x6a5: 0x9f, 0x6a6: 0x9f, 0x6a7: 0x9f, 0x6a8: 0x9f, 0x6a9: 0x9f, 0x6aa: 0x9f, 0x6ab: 0x9f, 0x6ac: 0x9f, 0x6ad: 0x9f, 0x6ae: 0x9f, 0x6af: 0x9f, 0x6b0: 0x9f, 0x6b1: 0x9f, 0x6b2: 0x9f, 0x6b3: 0x9f, 0x6b4: 0x9f, 0x6b5: 0x9f, 0x6b6: 0x9f, 0x6b7: 0x9f, 0x6b8: 0x9f, 0x6b9: 0x9f, 0x6ba: 0x9f, 0x6bb: 0x9f, 0x6bc: 0x9f, 0x6bd: 0x9f, 0x6be: 0x9f, 0x6bf: 0x9f, // Block 0x1b, offset 0x6c0 0x6c0: 0x9f, 0x6c1: 0x9f, 0x6c2: 0x9f, 0x6c3: 0x9f, 0x6c4: 0x9f, 0x6c5: 0x9f, 0x6c6: 0x9f, 0x6c7: 0x9f, 0x6c8: 0x9f, 0x6c9: 0x9f, 0x6ca: 0x9f, 0x6cb: 0x9f, 0x6cc: 0x9f, 0x6cd: 0x9f, 0x6ce: 0x9f, 0x6cf: 0x9f, 0x6d0: 0x9f, 0x6d1: 0x9f, 0x6d2: 0x9f, 0x6d3: 0x9f, 0x6d4: 0x9f, 0x6d5: 0x9f, 0x6d6: 0x9f, 0x6d7: 0x9f, 0x6d8: 0x9f, 0x6d9: 0x9f, 0x6da: 0x9f, 0x6db: 0x9f, 0x6dc: 0x18f, 0x6dd: 0x9f, 0x6de: 0x9f, 0x6df: 0x9f, 0x6e0: 0x190, 0x6e1: 0x9f, 0x6e2: 0x9f, 0x6e3: 0x9f, 0x6e4: 0x9f, 0x6e5: 0x9f, 0x6e6: 0x9f, 0x6e7: 0x9f, 0x6e8: 0x9f, 0x6e9: 0x9f, 0x6ea: 0x9f, 0x6eb: 0x9f, 0x6ec: 0x9f, 0x6ed: 0x9f, 0x6ee: 0x9f, 0x6ef: 0x9f, 0x6f0: 0x9f, 0x6f1: 0x9f, 0x6f2: 0x9f, 0x6f3: 0x9f, 0x6f4: 0x9f, 0x6f5: 0x9f, 0x6f6: 0x9f, 0x6f7: 0x9f, 0x6f8: 0x9f, 0x6f9: 0x9f, 0x6fa: 0x9f, 0x6fb: 0x9f, 0x6fc: 0x9f, 0x6fd: 0x9f, 0x6fe: 0x9f, 0x6ff: 0x9f, // Block 0x1c, offset 0x700 0x700: 0x9f, 0x701: 0x9f, 0x702: 0x9f, 0x703: 0x9f, 0x704: 0x9f, 0x705: 0x9f, 0x706: 0x9f, 0x707: 0x9f, 0x708: 0x9f, 0x709: 0x9f, 0x70a: 0x9f, 0x70b: 0x9f, 0x70c: 0x9f, 0x70d: 0x9f, 0x70e: 0x9f, 0x70f: 0x9f, 0x710: 0x9f, 0x711: 0x9f, 0x712: 0x9f, 0x713: 0x9f, 0x714: 0x9f, 0x715: 0x9f, 0x716: 0x9f, 0x717: 0x9f, 0x718: 0x9f, 0x719: 0x9f, 0x71a: 0x9f, 0x71b: 0x9f, 0x71c: 0x9f, 0x71d: 0x9f, 0x71e: 0x9f, 0x71f: 0x9f, 0x720: 0x9f, 0x721: 0x9f, 0x722: 0x9f, 0x723: 0x9f, 0x724: 0x9f, 0x725: 0x9f, 0x726: 0x9f, 0x727: 0x9f, 0x728: 0x9f, 0x729: 0x9f, 0x72a: 0x9f, 0x72b: 0x9f, 0x72c: 0x9f, 0x72d: 0x9f, 0x72e: 0x9f, 0x72f: 0x9f, 0x730: 0x9f, 0x731: 0x9f, 0x732: 0x9f, 0x733: 0x9f, 0x734: 0x9f, 0x735: 0x9f, 0x736: 0x9f, 0x737: 0x9f, 0x738: 0x9f, 0x739: 0x9f, 0x73a: 0x191, 0x73b: 0x9f, 0x73c: 0x9f, 0x73d: 0x9f, 0x73e: 0x9f, 0x73f: 0x9f, // Block 0x1d, offset 0x740 0x740: 0x9f, 0x741: 0x9f, 0x742: 0x9f, 0x743: 0x9f, 0x744: 0x9f, 0x745: 0x9f, 0x746: 0x9f, 0x747: 0x9f, 0x748: 0x9f, 0x749: 0x9f, 0x74a: 0x9f, 0x74b: 0x9f, 0x74c: 0x9f, 0x74d: 0x9f, 0x74e: 0x9f, 0x74f: 0x9f, 0x750: 0x9f, 0x751: 0x9f, 0x752: 0x9f, 0x753: 0x9f, 0x754: 0x9f, 0x755: 0x9f, 0x756: 0x9f, 0x757: 0x9f, 0x758: 0x9f, 0x759: 0x9f, 0x75a: 0x9f, 0x75b: 0x9f, 0x75c: 0x9f, 0x75d: 0x9f, 0x75e: 0x9f, 0x75f: 0x9f, 0x760: 0x9f, 0x761: 0x9f, 0x762: 0x9f, 0x763: 0x9f, 0x764: 0x9f, 0x765: 0x9f, 0x766: 0x9f, 0x767: 0x9f, 0x768: 0x9f, 0x769: 0x9f, 0x76a: 0x9f, 0x76b: 0x9f, 0x76c: 0x9f, 0x76d: 0x9f, 0x76e: 0x9f, 0x76f: 0x192, 0x770: 0xba, 0x771: 0xba, 0x772: 0xba, 0x773: 0xba, 0x774: 0xba, 0x775: 0xba, 0x776: 0xba, 0x777: 0xba, 0x778: 0xba, 0x779: 0xba, 0x77a: 0xba, 0x77b: 0xba, 0x77c: 0xba, 0x77d: 0xba, 0x77e: 0xba, 0x77f: 0xba, // Block 0x1e, offset 0x780 0x780: 0xba, 0x781: 0xba, 0x782: 0xba, 0x783: 0xba, 0x784: 0xba, 0x785: 0xba, 0x786: 0xba, 0x787: 0xba, 0x788: 0xba, 0x789: 0xba, 0x78a: 0xba, 0x78b: 0xba, 0x78c: 0xba, 0x78d: 0xba, 0x78e: 0xba, 0x78f: 0xba, 0x790: 0xba, 0x791: 0xba, 0x792: 0xba, 0x793: 0xba, 0x794: 0xba, 0x795: 0xba, 0x796: 0xba, 0x797: 0xba, 0x798: 0xba, 0x799: 0xba, 0x79a: 0xba, 0x79b: 0xba, 0x79c: 0xba, 0x79d: 0xba, 0x79e: 0xba, 0x79f: 0xba, 0x7a0: 0x76, 0x7a1: 0x77, 0x7a2: 0x78, 0x7a3: 0x193, 0x7a4: 0x79, 0x7a5: 0x7a, 0x7a6: 0x194, 0x7a7: 0x7b, 0x7a8: 0x7c, 0x7a9: 0xba, 0x7aa: 0xba, 0x7ab: 0xba, 0x7ac: 0xba, 0x7ad: 0xba, 0x7ae: 0xba, 0x7af: 0xba, 0x7b0: 0xba, 0x7b1: 0xba, 0x7b2: 0xba, 0x7b3: 0xba, 0x7b4: 0xba, 0x7b5: 0xba, 0x7b6: 0xba, 0x7b7: 0xba, 0x7b8: 0xba, 0x7b9: 0xba, 0x7ba: 0xba, 0x7bb: 0xba, 0x7bc: 0xba, 0x7bd: 0xba, 0x7be: 0xba, 0x7bf: 0xba, // Block 0x1f, offset 0x7c0 0x7d0: 0x0d, 0x7d1: 0x0e, 0x7d2: 0x0f, 0x7d3: 0x10, 0x7d4: 0x11, 0x7d5: 0x0b, 0x7d6: 0x12, 0x7d7: 0x07, 0x7d8: 0x13, 0x7d9: 0x0b, 0x7da: 0x0b, 0x7db: 0x14, 0x7dc: 0x0b, 0x7dd: 0x15, 0x7de: 0x16, 0x7df: 0x17, 0x7e0: 0x07, 0x7e1: 0x07, 0x7e2: 0x07, 0x7e3: 0x07, 0x7e4: 0x07, 0x7e5: 0x07, 0x7e6: 0x07, 0x7e7: 0x07, 0x7e8: 0x07, 0x7e9: 0x07, 0x7ea: 0x18, 0x7eb: 0x19, 0x7ec: 0x1a, 0x7ed: 0x07, 0x7ee: 0x1b, 0x7ef: 0x1c, 0x7f0: 0x0b, 0x7f1: 0x0b, 0x7f2: 0x0b, 0x7f3: 0x0b, 0x7f4: 0x0b, 0x7f5: 0x0b, 0x7f6: 0x0b, 0x7f7: 0x0b, 0x7f8: 0x0b, 0x7f9: 0x0b, 0x7fa: 0x0b, 0x7fb: 0x0b, 0x7fc: 0x0b, 0x7fd: 0x0b, 0x7fe: 0x0b, 0x7ff: 0x0b, // Block 0x20, offset 0x800 0x800: 0x0b, 0x801: 0x0b, 0x802: 0x0b, 0x803: 0x0b, 0x804: 0x0b, 0x805: 0x0b, 0x806: 0x0b, 0x807: 0x0b, 0x808: 0x0b, 0x809: 0x0b, 0x80a: 0x0b, 0x80b: 0x0b, 0x80c: 0x0b, 0x80d: 0x0b, 0x80e: 0x0b, 0x80f: 0x0b, 0x810: 0x0b, 0x811: 0x0b, 0x812: 0x0b, 0x813: 0x0b, 0x814: 0x0b, 0x815: 0x0b, 0x816: 0x0b, 0x817: 0x0b, 0x818: 0x0b, 0x819: 0x0b, 0x81a: 0x0b, 0x81b: 0x0b, 0x81c: 0x0b, 0x81d: 0x0b, 0x81e: 0x0b, 0x81f: 0x0b, 0x820: 0x0b, 0x821: 0x0b, 0x822: 0x0b, 0x823: 0x0b, 0x824: 0x0b, 0x825: 0x0b, 0x826: 0x0b, 0x827: 0x0b, 0x828: 0x0b, 0x829: 0x0b, 0x82a: 0x0b, 0x82b: 0x0b, 0x82c: 0x0b, 0x82d: 0x0b, 0x82e: 0x0b, 0x82f: 0x0b, 0x830: 0x0b, 0x831: 0x0b, 0x832: 0x0b, 0x833: 0x0b, 0x834: 0x0b, 0x835: 0x0b, 0x836: 0x0b, 0x837: 0x0b, 0x838: 0x0b, 0x839: 0x0b, 0x83a: 0x0b, 0x83b: 0x0b, 0x83c: 0x0b, 0x83d: 0x0b, 0x83e: 0x0b, 0x83f: 0x0b, // Block 0x21, offset 0x840 0x840: 0x195, 0x841: 0x196, 0x842: 0xba, 0x843: 0xba, 0x844: 0x197, 0x845: 0x197, 0x846: 0x197, 0x847: 0x198, 0x848: 0xba, 0x849: 0xba, 0x84a: 0xba, 0x84b: 0xba, 0x84c: 0xba, 0x84d: 0xba, 0x84e: 0xba, 0x84f: 0xba, 0x850: 0xba, 0x851: 0xba, 0x852: 0xba, 0x853: 0xba, 0x854: 0xba, 0x855: 0xba, 0x856: 0xba, 0x857: 0xba, 0x858: 0xba, 0x859: 0xba, 0x85a: 0xba, 0x85b: 0xba, 0x85c: 0xba, 0x85d: 0xba, 0x85e: 0xba, 0x85f: 0xba, 0x860: 0xba, 0x861: 0xba, 0x862: 0xba, 0x863: 0xba, 0x864: 0xba, 0x865: 0xba, 0x866: 0xba, 0x867: 0xba, 0x868: 0xba, 0x869: 0xba, 0x86a: 0xba, 0x86b: 0xba, 0x86c: 0xba, 0x86d: 0xba, 0x86e: 0xba, 0x86f: 0xba, 0x870: 0xba, 0x871: 0xba, 0x872: 0xba, 0x873: 0xba, 0x874: 0xba, 0x875: 0xba, 0x876: 0xba, 0x877: 0xba, 0x878: 0xba, 0x879: 0xba, 0x87a: 0xba, 0x87b: 0xba, 0x87c: 0xba, 0x87d: 0xba, 0x87e: 0xba, 0x87f: 0xba, // Block 0x22, offset 0x880 0x880: 0x0b, 0x881: 0x0b, 0x882: 0x0b, 0x883: 0x0b, 0x884: 0x0b, 0x885: 0x0b, 0x886: 0x0b, 0x887: 0x0b, 0x888: 0x0b, 0x889: 0x0b, 0x88a: 0x0b, 0x88b: 0x0b, 0x88c: 0x0b, 0x88d: 0x0b, 0x88e: 0x0b, 0x88f: 0x0b, 0x890: 0x0b, 0x891: 0x0b, 0x892: 0x0b, 0x893: 0x0b, 0x894: 0x0b, 0x895: 0x0b, 0x896: 0x0b, 0x897: 0x0b, 0x898: 0x0b, 0x899: 0x0b, 0x89a: 0x0b, 0x89b: 0x0b, 0x89c: 0x0b, 0x89d: 0x0b, 0x89e: 0x0b, 0x89f: 0x0b, 0x8a0: 0x1f, 0x8a1: 0x0b, 0x8a2: 0x0b, 0x8a3: 0x0b, 0x8a4: 0x0b, 0x8a5: 0x0b, 0x8a6: 0x0b, 0x8a7: 0x0b, 0x8a8: 0x0b, 0x8a9: 0x0b, 0x8aa: 0x0b, 0x8ab: 0x0b, 0x8ac: 0x0b, 0x8ad: 0x0b, 0x8ae: 0x0b, 0x8af: 0x0b, 0x8b0: 0x0b, 0x8b1: 0x0b, 0x8b2: 0x0b, 0x8b3: 0x0b, 0x8b4: 0x0b, 0x8b5: 0x0b, 0x8b6: 0x0b, 0x8b7: 0x0b, 0x8b8: 0x0b, 0x8b9: 0x0b, 0x8ba: 0x0b, 0x8bb: 0x0b, 0x8bc: 0x0b, 0x8bd: 0x0b, 0x8be: 0x0b, 0x8bf: 0x0b, // Block 0x23, offset 0x8c0 0x8c0: 0x0b, 0x8c1: 0x0b, 0x8c2: 0x0b, 0x8c3: 0x0b, 0x8c4: 0x0b, 0x8c5: 0x0b, 0x8c6: 0x0b, 0x8c7: 0x0b, 0x8c8: 0x0b, 0x8c9: 0x0b, 0x8ca: 0x0b, 0x8cb: 0x0b, 0x8cc: 0x0b, 0x8cd: 0x0b, 0x8ce: 0x0b, 0x8cf: 0x0b, } // idnaSparseOffset: 284 entries, 568 bytes var idnaSparseOffset = []uint16{0x0, 0x8, 0x19, 0x25, 0x27, 0x2c, 0x33, 0x3e, 0x4a, 0x4e, 0x5d, 0x62, 0x6c, 0x78, 0x86, 0x8b, 0x94, 0xa4, 0xb2, 0xbe, 0xca, 0xdb, 0xe5, 0xec, 0xf9, 0x10a, 0x111, 0x11c, 0x12b, 0x139, 0x143, 0x145, 0x14a, 0x14d, 0x150, 0x152, 0x15e, 0x169, 0x171, 0x177, 0x17d, 0x182, 0x187, 0x18a, 0x18e, 0x194, 0x199, 0x1a5, 0x1af, 0x1b5, 0x1c6, 0x1d0, 0x1d3, 0x1db, 0x1de, 0x1eb, 0x1f3, 0x1f7, 0x1fe, 0x206, 0x216, 0x222, 0x224, 0x22e, 0x23a, 0x246, 0x252, 0x25a, 0x25f, 0x26c, 0x27d, 0x281, 0x28c, 0x290, 0x299, 0x2a1, 0x2a7, 0x2ac, 0x2af, 0x2b3, 0x2b9, 0x2bd, 0x2c1, 0x2c5, 0x2cb, 0x2d3, 0x2da, 0x2e5, 0x2ef, 0x2f3, 0x2f6, 0x2fc, 0x300, 0x302, 0x305, 0x307, 0x30a, 0x314, 0x317, 0x326, 0x32a, 0x32f, 0x332, 0x336, 0x33b, 0x340, 0x346, 0x352, 0x361, 0x367, 0x36b, 0x37a, 0x37f, 0x387, 0x391, 0x39c, 0x3a4, 0x3b5, 0x3be, 0x3ce, 0x3db, 0x3e5, 0x3ea, 0x3f7, 0x3fb, 0x400, 0x402, 0x406, 0x408, 0x40c, 0x415, 0x41b, 0x41f, 0x42f, 0x439, 0x43e, 0x441, 0x447, 0x44e, 0x453, 0x457, 0x45d, 0x462, 0x46b, 0x470, 0x476, 0x47d, 0x484, 0x48b, 0x48f, 0x494, 0x497, 0x49c, 0x4a8, 0x4ae, 0x4b3, 0x4ba, 0x4c2, 0x4c7, 0x4cb, 0x4db, 0x4e2, 0x4e6, 0x4ea, 0x4f1, 0x4f3, 0x4f6, 0x4f9, 0x4fd, 0x506, 0x50a, 0x512, 0x51a, 0x51e, 0x524, 0x52d, 0x539, 0x540, 0x549, 0x553, 0x55a, 0x568, 0x575, 0x582, 0x58b, 0x58f, 0x59f, 0x5a7, 0x5b2, 0x5bb, 0x5c1, 0x5c9, 0x5d2, 0x5dd, 0x5e0, 0x5ec, 0x5f5, 0x5f8, 0x5fd, 0x602, 0x60f, 0x61a, 0x623, 0x62d, 0x630, 0x63a, 0x643, 0x64f, 0x65c, 0x669, 0x677, 0x67e, 0x682, 0x685, 0x68a, 0x68d, 0x692, 0x695, 0x69c, 0x6a3, 0x6a7, 0x6b2, 0x6b5, 0x6b8, 0x6bb, 0x6c1, 0x6c7, 0x6cd, 0x6d0, 0x6d3, 0x6d6, 0x6dd, 0x6e0, 0x6e5, 0x6ef, 0x6f2, 0x6f6, 0x705, 0x711, 0x715, 0x71a, 0x71e, 0x723, 0x727, 0x72c, 0x735, 0x740, 0x746, 0x74c, 0x752, 0x758, 0x761, 0x764, 0x767, 0x76b, 0x76f, 0x773, 0x779, 0x77f, 0x784, 0x787, 0x797, 0x79e, 0x7a1, 0x7a6, 0x7aa, 0x7b0, 0x7b5, 0x7b9, 0x7bf, 0x7c5, 0x7c9, 0x7d2, 0x7d7, 0x7da, 0x7dd, 0x7e1, 0x7e5, 0x7e8, 0x7f8, 0x809, 0x80e, 0x810, 0x812} // idnaSparseValues: 2069 entries, 8276 bytes var idnaSparseValues = [2069]valueRange{ // Block 0x0, offset 0x0 {value: 0x0000, lo: 0x07}, {value: 0xe105, lo: 0x80, hi: 0x96}, {value: 0x0018, lo: 0x97, hi: 0x97}, {value: 0xe105, lo: 0x98, hi: 0x9e}, {value: 0x001f, lo: 0x9f, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xb6}, {value: 0x0018, lo: 0xb7, hi: 0xb7}, {value: 0x0008, lo: 0xb8, hi: 0xbf}, // Block 0x1, offset 0x8 {value: 0x0000, lo: 0x10}, {value: 0x0008, lo: 0x80, hi: 0x80}, {value: 0xe01d, lo: 0x81, hi: 0x81}, {value: 0x0008, lo: 0x82, hi: 0x82}, {value: 0x0335, lo: 0x83, hi: 0x83}, {value: 0x034d, lo: 0x84, hi: 0x84}, {value: 0x0365, lo: 0x85, hi: 0x85}, {value: 0xe00d, lo: 0x86, hi: 0x86}, {value: 0x0008, lo: 0x87, hi: 0x87}, {value: 0xe00d, lo: 0x88, hi: 0x88}, {value: 0x0008, lo: 0x89, hi: 0x89}, {value: 0xe00d, lo: 0x8a, hi: 0x8a}, {value: 0x0008, lo: 0x8b, hi: 0x8b}, {value: 0xe00d, lo: 0x8c, hi: 0x8c}, {value: 0x0008, lo: 0x8d, hi: 0x8d}, {value: 0xe00d, lo: 0x8e, hi: 0x8e}, {value: 0x0008, lo: 0x8f, hi: 0xbf}, // Block 0x2, offset 0x19 {value: 0x0000, lo: 0x0b}, {value: 0x0008, lo: 0x80, hi: 0xaf}, {value: 0x0249, lo: 0xb0, hi: 0xb0}, {value: 0x037d, lo: 0xb1, hi: 0xb1}, {value: 0x0259, lo: 0xb2, hi: 0xb2}, {value: 0x0269, lo: 0xb3, hi: 0xb3}, {value: 0x034d, lo: 0xb4, hi: 0xb4}, {value: 0x0395, lo: 0xb5, hi: 0xb5}, {value: 0xe1bd, lo: 0xb6, hi: 0xb6}, {value: 0x0279, lo: 0xb7, hi: 0xb7}, {value: 0x0289, lo: 0xb8, hi: 0xb8}, {value: 0x0008, lo: 0xb9, hi: 0xbf}, // Block 0x3, offset 0x25 {value: 0x0000, lo: 0x01}, {value: 0x3308, lo: 0x80, hi: 0xbf}, // Block 0x4, offset 0x27 {value: 0x0000, lo: 0x04}, {value: 0x03f5, lo: 0x80, hi: 0x8f}, {value: 0xe105, lo: 0x90, hi: 0x9f}, {value: 0x049d, lo: 0xa0, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xbf}, // Block 0x5, offset 0x2c {value: 0x0000, lo: 0x06}, {value: 0xe185, lo: 0x80, hi: 0x8f}, {value: 0x0545, lo: 0x90, hi: 0x96}, {value: 0x0040, lo: 0x97, hi: 0x98}, {value: 0x0008, lo: 0x99, hi: 0x99}, {value: 0x0018, lo: 0x9a, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xbf}, // Block 0x6, offset 0x33 {value: 0x0000, lo: 0x0a}, {value: 0x0008, lo: 0x80, hi: 0x86}, {value: 0x0401, lo: 0x87, hi: 0x87}, {value: 0x0008, lo: 0x88, hi: 0x88}, {value: 0x0018, lo: 0x89, hi: 0x8a}, {value: 0x0040, lo: 0x8b, hi: 0x8c}, {value: 0x0018, lo: 0x8d, hi: 0x8f}, {value: 0x0040, lo: 0x90, hi: 0x90}, {value: 0x3308, lo: 0x91, hi: 0xbd}, {value: 0x0818, lo: 0xbe, hi: 0xbe}, {value: 0x3308, lo: 0xbf, hi: 0xbf}, // Block 0x7, offset 0x3e {value: 0x0000, lo: 0x0b}, {value: 0x0818, lo: 0x80, hi: 0x80}, {value: 0x3308, lo: 0x81, hi: 0x82}, {value: 0x0818, lo: 0x83, hi: 0x83}, {value: 0x3308, lo: 0x84, hi: 0x85}, {value: 0x0818, lo: 0x86, hi: 0x86}, {value: 0x3308, lo: 0x87, hi: 0x87}, {value: 0x0040, lo: 0x88, hi: 0x8f}, {value: 0x0808, lo: 0x90, hi: 0xaa}, {value: 0x0040, lo: 0xab, hi: 0xae}, {value: 0x0808, lo: 0xaf, hi: 0xb4}, {value: 0x0040, lo: 0xb5, hi: 0xbf}, // Block 0x8, offset 0x4a {value: 0x0000, lo: 0x03}, {value: 0x0a08, lo: 0x80, hi: 0x87}, {value: 0x0c08, lo: 0x88, hi: 0x99}, {value: 0x0a08, lo: 0x9a, hi: 0xbf}, // Block 0x9, offset 0x4e {value: 0x0000, lo: 0x0e}, {value: 0x3308, lo: 0x80, hi: 0x8a}, {value: 0x0040, lo: 0x8b, hi: 0x8c}, {value: 0x0c08, lo: 0x8d, hi: 0x8d}, {value: 0x0a08, lo: 0x8e, hi: 0x98}, {value: 0x0c08, lo: 0x99, hi: 0x9b}, {value: 0x0a08, lo: 0x9c, hi: 0xaa}, {value: 0x0c08, lo: 0xab, hi: 0xac}, {value: 0x0a08, lo: 0xad, hi: 0xb0}, {value: 0x0c08, lo: 0xb1, hi: 0xb1}, {value: 0x0a08, lo: 0xb2, hi: 0xb2}, {value: 0x0c08, lo: 0xb3, hi: 0xb4}, {value: 0x0a08, lo: 0xb5, hi: 0xb7}, {value: 0x0c08, lo: 0xb8, hi: 0xb9}, {value: 0x0a08, lo: 0xba, hi: 0xbf}, // Block 0xa, offset 0x5d {value: 0x0000, lo: 0x04}, {value: 0x0808, lo: 0x80, hi: 0xa5}, {value: 0x3308, lo: 0xa6, hi: 0xb0}, {value: 0x0808, lo: 0xb1, hi: 0xb1}, {value: 0x0040, lo: 0xb2, hi: 0xbf}, // Block 0xb, offset 0x62 {value: 0x0000, lo: 0x09}, {value: 0x0808, lo: 0x80, hi: 0x89}, {value: 0x0a08, lo: 0x8a, hi: 0xaa}, {value: 0x3308, lo: 0xab, hi: 0xb3}, {value: 0x0808, lo: 0xb4, hi: 0xb5}, {value: 0x0018, lo: 0xb6, hi: 0xb9}, {value: 0x0818, lo: 0xba, hi: 0xba}, {value: 0x0040, lo: 0xbb, hi: 0xbc}, {value: 0x3308, lo: 0xbd, hi: 0xbd}, {value: 0x0818, lo: 0xbe, hi: 0xbf}, // Block 0xc, offset 0x6c {value: 0x0000, lo: 0x0b}, {value: 0x0808, lo: 0x80, hi: 0x95}, {value: 0x3308, lo: 0x96, hi: 0x99}, {value: 0x0808, lo: 0x9a, hi: 0x9a}, {value: 0x3308, lo: 0x9b, hi: 0xa3}, {value: 0x0808, lo: 0xa4, hi: 0xa4}, {value: 0x3308, lo: 0xa5, hi: 0xa7}, {value: 0x0808, lo: 0xa8, hi: 0xa8}, {value: 0x3308, lo: 0xa9, hi: 0xad}, {value: 0x0040, lo: 0xae, hi: 0xaf}, {value: 0x0818, lo: 0xb0, hi: 0xbe}, {value: 0x0040, lo: 0xbf, hi: 0xbf}, // Block 0xd, offset 0x78 {value: 0x0000, lo: 0x0d}, {value: 0x0040, lo: 0x80, hi: 0x9f}, {value: 0x0a08, lo: 0xa0, hi: 0xa9}, {value: 0x0c08, lo: 0xaa, hi: 0xac}, {value: 0x0808, lo: 0xad, hi: 0xad}, {value: 0x0c08, lo: 0xae, hi: 0xae}, {value: 0x0a08, lo: 0xaf, hi: 0xb0}, {value: 0x0c08, lo: 0xb1, hi: 0xb2}, {value: 0x0a08, lo: 0xb3, hi: 0xb4}, {value: 0x0040, lo: 0xb5, hi: 0xb5}, {value: 0x0a08, lo: 0xb6, hi: 0xb8}, {value: 0x0c08, lo: 0xb9, hi: 0xb9}, {value: 0x0a08, lo: 0xba, hi: 0xbd}, {value: 0x0040, lo: 0xbe, hi: 0xbf}, // Block 0xe, offset 0x86 {value: 0x0000, lo: 0x04}, {value: 0x0040, lo: 0x80, hi: 0x92}, {value: 0x3308, lo: 0x93, hi: 0xa1}, {value: 0x0840, lo: 0xa2, hi: 0xa2}, {value: 0x3308, lo: 0xa3, hi: 0xbf}, // Block 0xf, offset 0x8b {value: 0x0000, lo: 0x08}, {value: 0x3308, lo: 0x80, hi: 0x82}, {value: 0x3008, lo: 0x83, hi: 0x83}, {value: 0x0008, lo: 0x84, hi: 0xb9}, {value: 0x3308, lo: 0xba, hi: 0xba}, {value: 0x3008, lo: 0xbb, hi: 0xbb}, {value: 0x3308, lo: 0xbc, hi: 0xbc}, {value: 0x0008, lo: 0xbd, hi: 0xbd}, {value: 0x3008, lo: 0xbe, hi: 0xbf}, // Block 0x10, offset 0x94 {value: 0x0000, lo: 0x0f}, {value: 0x3308, lo: 0x80, hi: 0x80}, {value: 0x3008, lo: 0x81, hi: 0x82}, {value: 0x0040, lo: 0x83, hi: 0x85}, {value: 0x3008, lo: 0x86, hi: 0x88}, {value: 0x0040, lo: 0x89, hi: 0x89}, {value: 0x3008, lo: 0x8a, hi: 0x8c}, {value: 0x3b08, lo: 0x8d, hi: 0x8d}, {value: 0x0040, lo: 0x8e, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x90}, {value: 0x0040, lo: 0x91, hi: 0x96}, {value: 0x3008, lo: 0x97, hi: 0x97}, {value: 0x0040, lo: 0x98, hi: 0xa5}, {value: 0x0008, lo: 0xa6, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xba}, {value: 0x0040, lo: 0xbb, hi: 0xbf}, // Block 0x11, offset 0xa4 {value: 0x0000, lo: 0x0d}, {value: 0x3308, lo: 0x80, hi: 0x80}, {value: 0x3008, lo: 0x81, hi: 0x83}, {value: 0x3308, lo: 0x84, hi: 0x84}, {value: 0x0008, lo: 0x85, hi: 0x8c}, {value: 0x0040, lo: 0x8d, hi: 0x8d}, {value: 0x0008, lo: 0x8e, hi: 0x90}, {value: 0x0040, lo: 0x91, hi: 0x91}, {value: 0x0008, lo: 0x92, hi: 0xa8}, {value: 0x0040, lo: 0xa9, hi: 0xa9}, {value: 0x0008, lo: 0xaa, hi: 0xb9}, {value: 0x0040, lo: 0xba, hi: 0xbc}, {value: 0x0008, lo: 0xbd, hi: 0xbd}, {value: 0x3308, lo: 0xbe, hi: 0xbf}, // Block 0x12, offset 0xb2 {value: 0x0000, lo: 0x0b}, {value: 0x3308, lo: 0x80, hi: 0x81}, {value: 0x3008, lo: 0x82, hi: 0x83}, {value: 0x0040, lo: 0x84, hi: 0x84}, {value: 0x0008, lo: 0x85, hi: 0x8c}, {value: 0x0040, lo: 0x8d, hi: 0x8d}, {value: 0x0008, lo: 0x8e, hi: 0x90}, {value: 0x0040, lo: 0x91, hi: 0x91}, {value: 0x0008, lo: 0x92, hi: 0xba}, {value: 0x3b08, lo: 0xbb, hi: 0xbc}, {value: 0x0008, lo: 0xbd, hi: 0xbd}, {value: 0x3008, lo: 0xbe, hi: 0xbf}, // Block 0x13, offset 0xbe {value: 0x0000, lo: 0x0b}, {value: 0x0040, lo: 0x80, hi: 0x81}, {value: 0x3008, lo: 0x82, hi: 0x83}, {value: 0x0040, lo: 0x84, hi: 0x84}, {value: 0x0008, lo: 0x85, hi: 0x96}, {value: 0x0040, lo: 0x97, hi: 0x99}, {value: 0x0008, lo: 0x9a, hi: 0xb1}, {value: 0x0040, lo: 0xb2, hi: 0xb2}, {value: 0x0008, lo: 0xb3, hi: 0xbb}, {value: 0x0040, lo: 0xbc, hi: 0xbc}, {value: 0x0008, lo: 0xbd, hi: 0xbd}, {value: 0x0040, lo: 0xbe, hi: 0xbf}, // Block 0x14, offset 0xca {value: 0x0000, lo: 0x10}, {value: 0x0008, lo: 0x80, hi: 0x86}, {value: 0x0040, lo: 0x87, hi: 0x89}, {value: 0x3b08, lo: 0x8a, hi: 0x8a}, {value: 0x0040, lo: 0x8b, hi: 0x8e}, {value: 0x3008, lo: 0x8f, hi: 0x91}, {value: 0x3308, lo: 0x92, hi: 0x94}, {value: 0x0040, lo: 0x95, hi: 0x95}, {value: 0x3308, lo: 0x96, hi: 0x96}, {value: 0x0040, lo: 0x97, hi: 0x97}, {value: 0x3008, lo: 0x98, hi: 0x9f}, {value: 0x0040, lo: 0xa0, hi: 0xa5}, {value: 0x0008, lo: 0xa6, hi: 0xaf}, {value: 0x0040, lo: 0xb0, hi: 0xb1}, {value: 0x3008, lo: 0xb2, hi: 0xb3}, {value: 0x0018, lo: 0xb4, hi: 0xb4}, {value: 0x0040, lo: 0xb5, hi: 0xbf}, // Block 0x15, offset 0xdb {value: 0x0000, lo: 0x09}, {value: 0x0040, lo: 0x80, hi: 0x80}, {value: 0x0008, lo: 0x81, hi: 0xb0}, {value: 0x3308, lo: 0xb1, hi: 0xb1}, {value: 0x0008, lo: 0xb2, hi: 0xb2}, {value: 0x08f1, lo: 0xb3, hi: 0xb3}, {value: 0x3308, lo: 0xb4, hi: 0xb9}, {value: 0x3b08, lo: 0xba, hi: 0xba}, {value: 0x0040, lo: 0xbb, hi: 0xbe}, {value: 0x0018, lo: 0xbf, hi: 0xbf}, // Block 0x16, offset 0xe5 {value: 0x0000, lo: 0x06}, {value: 0x0008, lo: 0x80, hi: 0x86}, {value: 0x3308, lo: 0x87, hi: 0x8e}, {value: 0x0018, lo: 0x8f, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0018, lo: 0x9a, hi: 0x9b}, {value: 0x0040, lo: 0x9c, hi: 0xbf}, // Block 0x17, offset 0xec {value: 0x0000, lo: 0x0c}, {value: 0x0008, lo: 0x80, hi: 0x84}, {value: 0x0040, lo: 0x85, hi: 0x85}, {value: 0x0008, lo: 0x86, hi: 0x86}, {value: 0x0040, lo: 0x87, hi: 0x87}, {value: 0x3308, lo: 0x88, hi: 0x8d}, {value: 0x0040, lo: 0x8e, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9b}, {value: 0x0961, lo: 0x9c, hi: 0x9c}, {value: 0x0999, lo: 0x9d, hi: 0x9d}, {value: 0x0008, lo: 0x9e, hi: 0x9f}, {value: 0x0040, lo: 0xa0, hi: 0xbf}, // Block 0x18, offset 0xf9 {value: 0x0000, lo: 0x10}, {value: 0x0008, lo: 0x80, hi: 0x80}, {value: 0x0018, lo: 0x81, hi: 0x8a}, {value: 0x0008, lo: 0x8b, hi: 0x8b}, {value: 0xe03d, lo: 0x8c, hi: 0x8c}, {value: 0x0018, lo: 0x8d, hi: 0x97}, {value: 0x3308, lo: 0x98, hi: 0x99}, {value: 0x0018, lo: 0x9a, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa9}, {value: 0x0018, lo: 0xaa, hi: 0xb4}, {value: 0x3308, lo: 0xb5, hi: 0xb5}, {value: 0x0018, lo: 0xb6, hi: 0xb6}, {value: 0x3308, lo: 0xb7, hi: 0xb7}, {value: 0x0018, lo: 0xb8, hi: 0xb8}, {value: 0x3308, lo: 0xb9, hi: 0xb9}, {value: 0x0018, lo: 0xba, hi: 0xbd}, {value: 0x3008, lo: 0xbe, hi: 0xbf}, // Block 0x19, offset 0x10a {value: 0x0000, lo: 0x06}, {value: 0x0018, lo: 0x80, hi: 0x85}, {value: 0x3308, lo: 0x86, hi: 0x86}, {value: 0x0018, lo: 0x87, hi: 0x8c}, {value: 0x0040, lo: 0x8d, hi: 0x8d}, {value: 0x0018, lo: 0x8e, hi: 0x9a}, {value: 0x0040, lo: 0x9b, hi: 0xbf}, // Block 0x1a, offset 0x111 {value: 0x0000, lo: 0x0a}, {value: 0x0008, lo: 0x80, hi: 0xaa}, {value: 0x3008, lo: 0xab, hi: 0xac}, {value: 0x3308, lo: 0xad, hi: 0xb0}, {value: 0x3008, lo: 0xb1, hi: 0xb1}, {value: 0x3308, lo: 0xb2, hi: 0xb7}, {value: 0x3008, lo: 0xb8, hi: 0xb8}, {value: 0x3b08, lo: 0xb9, hi: 0xba}, {value: 0x3008, lo: 0xbb, hi: 0xbc}, {value: 0x3308, lo: 0xbd, hi: 0xbe}, {value: 0x0008, lo: 0xbf, hi: 0xbf}, // Block 0x1b, offset 0x11c {value: 0x0000, lo: 0x0e}, {value: 0x0008, lo: 0x80, hi: 0x89}, {value: 0x0018, lo: 0x8a, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x95}, {value: 0x3008, lo: 0x96, hi: 0x97}, {value: 0x3308, lo: 0x98, hi: 0x99}, {value: 0x0008, lo: 0x9a, hi: 0x9d}, {value: 0x3308, lo: 0x9e, hi: 0xa0}, {value: 0x0008, lo: 0xa1, hi: 0xa1}, {value: 0x3008, lo: 0xa2, hi: 0xa4}, {value: 0x0008, lo: 0xa5, hi: 0xa6}, {value: 0x3008, lo: 0xa7, hi: 0xad}, {value: 0x0008, lo: 0xae, hi: 0xb0}, {value: 0x3308, lo: 0xb1, hi: 0xb4}, {value: 0x0008, lo: 0xb5, hi: 0xbf}, // Block 0x1c, offset 0x12b {value: 0x0000, lo: 0x0d}, {value: 0x0008, lo: 0x80, hi: 0x81}, {value: 0x3308, lo: 0x82, hi: 0x82}, {value: 0x3008, lo: 0x83, hi: 0x84}, {value: 0x3308, lo: 0x85, hi: 0x86}, {value: 0x3008, lo: 0x87, hi: 0x8c}, {value: 0x3308, lo: 0x8d, hi: 0x8d}, {value: 0x0008, lo: 0x8e, hi: 0x8e}, {value: 0x3008, lo: 0x8f, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x3008, lo: 0x9a, hi: 0x9c}, {value: 0x3308, lo: 0x9d, hi: 0x9d}, {value: 0x0018, lo: 0x9e, hi: 0x9f}, {value: 0x0040, lo: 0xa0, hi: 0xbf}, // Block 0x1d, offset 0x139 {value: 0x0000, lo: 0x09}, {value: 0x0040, lo: 0x80, hi: 0x86}, {value: 0x055d, lo: 0x87, hi: 0x87}, {value: 0x0040, lo: 0x88, hi: 0x8c}, {value: 0x055d, lo: 0x8d, hi: 0x8d}, {value: 0x0040, lo: 0x8e, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0xba}, {value: 0x0018, lo: 0xbb, hi: 0xbb}, {value: 0xe105, lo: 0xbc, hi: 0xbc}, {value: 0x0008, lo: 0xbd, hi: 0xbf}, // Block 0x1e, offset 0x143 {value: 0x0000, lo: 0x01}, {value: 0x0018, lo: 0x80, hi: 0xbf}, // Block 0x1f, offset 0x145 {value: 0x0000, lo: 0x04}, {value: 0x0018, lo: 0x80, hi: 0x9e}, {value: 0x0040, lo: 0x9f, hi: 0xa0}, {value: 0x2018, lo: 0xa1, hi: 0xb5}, {value: 0x0018, lo: 0xb6, hi: 0xbf}, // Block 0x20, offset 0x14a {value: 0x0000, lo: 0x02}, {value: 0x0018, lo: 0x80, hi: 0xa7}, {value: 0x2018, lo: 0xa8, hi: 0xbf}, // Block 0x21, offset 0x14d {value: 0x0000, lo: 0x02}, {value: 0x2018, lo: 0x80, hi: 0x82}, {value: 0x0018, lo: 0x83, hi: 0xbf}, // Block 0x22, offset 0x150 {value: 0x0000, lo: 0x01}, {value: 0x0008, lo: 0x80, hi: 0xbf}, // Block 0x23, offset 0x152 {value: 0x0000, lo: 0x0b}, {value: 0x0008, lo: 0x80, hi: 0x88}, {value: 0x0040, lo: 0x89, hi: 0x89}, {value: 0x0008, lo: 0x8a, hi: 0x8d}, {value: 0x0040, lo: 0x8e, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x96}, {value: 0x0040, lo: 0x97, hi: 0x97}, {value: 0x0008, lo: 0x98, hi: 0x98}, {value: 0x0040, lo: 0x99, hi: 0x99}, {value: 0x0008, lo: 0x9a, hi: 0x9d}, {value: 0x0040, lo: 0x9e, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xbf}, // Block 0x24, offset 0x15e {value: 0x0000, lo: 0x0a}, {value: 0x0008, lo: 0x80, hi: 0x88}, {value: 0x0040, lo: 0x89, hi: 0x89}, {value: 0x0008, lo: 0x8a, hi: 0x8d}, {value: 0x0040, lo: 0x8e, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0xb0}, {value: 0x0040, lo: 0xb1, hi: 0xb1}, {value: 0x0008, lo: 0xb2, hi: 0xb5}, {value: 0x0040, lo: 0xb6, hi: 0xb7}, {value: 0x0008, lo: 0xb8, hi: 0xbe}, {value: 0x0040, lo: 0xbf, hi: 0xbf}, // Block 0x25, offset 0x169 {value: 0x0000, lo: 0x07}, {value: 0x0008, lo: 0x80, hi: 0x80}, {value: 0x0040, lo: 0x81, hi: 0x81}, {value: 0x0008, lo: 0x82, hi: 0x85}, {value: 0x0040, lo: 0x86, hi: 0x87}, {value: 0x0008, lo: 0x88, hi: 0x96}, {value: 0x0040, lo: 0x97, hi: 0x97}, {value: 0x0008, lo: 0x98, hi: 0xbf}, // Block 0x26, offset 0x171 {value: 0x0000, lo: 0x05}, {value: 0x0008, lo: 0x80, hi: 0x90}, {value: 0x0040, lo: 0x91, hi: 0x91}, {value: 0x0008, lo: 0x92, hi: 0x95}, {value: 0x0040, lo: 0x96, hi: 0x97}, {value: 0x0008, lo: 0x98, hi: 0xbf}, // Block 0x27, offset 0x177 {value: 0x0000, lo: 0x05}, {value: 0x0008, lo: 0x80, hi: 0x9a}, {value: 0x0040, lo: 0x9b, hi: 0x9c}, {value: 0x3308, lo: 0x9d, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xbc}, {value: 0x0040, lo: 0xbd, hi: 0xbf}, // Block 0x28, offset 0x17d {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0x8f}, {value: 0x0018, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xbf}, // Block 0x29, offset 0x182 {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0xb5}, {value: 0x0040, lo: 0xb6, hi: 0xb7}, {value: 0xe045, lo: 0xb8, hi: 0xbd}, {value: 0x0040, lo: 0xbe, hi: 0xbf}, // Block 0x2a, offset 0x187 {value: 0x0000, lo: 0x02}, {value: 0x0018, lo: 0x80, hi: 0x80}, {value: 0x0008, lo: 0x81, hi: 0xbf}, // Block 0x2b, offset 0x18a {value: 0x0000, lo: 0x03}, {value: 0x0008, lo: 0x80, hi: 0xac}, {value: 0x0018, lo: 0xad, hi: 0xae}, {value: 0x0008, lo: 0xaf, hi: 0xbf}, // Block 0x2c, offset 0x18e {value: 0x0000, lo: 0x05}, {value: 0x0040, lo: 0x80, hi: 0x80}, {value: 0x0008, lo: 0x81, hi: 0x9a}, {value: 0x0018, lo: 0x9b, hi: 0x9c}, {value: 0x0040, lo: 0x9d, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xbf}, // Block 0x2d, offset 0x194 {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0xaa}, {value: 0x0018, lo: 0xab, hi: 0xb0}, {value: 0x0008, lo: 0xb1, hi: 0xb8}, {value: 0x0040, lo: 0xb9, hi: 0xbf}, // Block 0x2e, offset 0x199 {value: 0x0000, lo: 0x0b}, {value: 0x0008, lo: 0x80, hi: 0x8c}, {value: 0x0040, lo: 0x8d, hi: 0x8d}, {value: 0x0008, lo: 0x8e, hi: 0x91}, {value: 0x3308, lo: 0x92, hi: 0x93}, {value: 0x3b08, lo: 0x94, hi: 0x94}, {value: 0x0040, lo: 0x95, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xb1}, {value: 0x3308, lo: 0xb2, hi: 0xb3}, {value: 0x3b08, lo: 0xb4, hi: 0xb4}, {value: 0x0018, lo: 0xb5, hi: 0xb6}, {value: 0x0040, lo: 0xb7, hi: 0xbf}, // Block 0x2f, offset 0x1a5 {value: 0x0000, lo: 0x09}, {value: 0x0008, lo: 0x80, hi: 0x91}, {value: 0x3308, lo: 0x92, hi: 0x93}, {value: 0x0040, lo: 0x94, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xac}, {value: 0x0040, lo: 0xad, hi: 0xad}, {value: 0x0008, lo: 0xae, hi: 0xb0}, {value: 0x0040, lo: 0xb1, hi: 0xb1}, {value: 0x3308, lo: 0xb2, hi: 0xb3}, {value: 0x0040, lo: 0xb4, hi: 0xbf}, // Block 0x30, offset 0x1af {value: 0x0000, lo: 0x05}, {value: 0x0008, lo: 0x80, hi: 0xb3}, {value: 0x3340, lo: 0xb4, hi: 0xb5}, {value: 0x3008, lo: 0xb6, hi: 0xb6}, {value: 0x3308, lo: 0xb7, hi: 0xbd}, {value: 0x3008, lo: 0xbe, hi: 0xbf}, // Block 0x31, offset 0x1b5 {value: 0x0000, lo: 0x10}, {value: 0x3008, lo: 0x80, hi: 0x85}, {value: 0x3308, lo: 0x86, hi: 0x86}, {value: 0x3008, lo: 0x87, hi: 0x88}, {value: 0x3308, lo: 0x89, hi: 0x91}, {value: 0x3b08, lo: 0x92, hi: 0x92}, {value: 0x3308, lo: 0x93, hi: 0x93}, {value: 0x0018, lo: 0x94, hi: 0x96}, {value: 0x0008, lo: 0x97, hi: 0x97}, {value: 0x0018, lo: 0x98, hi: 0x9b}, {value: 0x0008, lo: 0x9c, hi: 0x9c}, {value: 0x3308, lo: 0x9d, hi: 0x9d}, {value: 0x0040, lo: 0x9e, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa9}, {value: 0x0040, lo: 0xaa, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xb9}, {value: 0x0040, lo: 0xba, hi: 0xbf}, // Block 0x32, offset 0x1c6 {value: 0x0000, lo: 0x09}, {value: 0x0018, lo: 0x80, hi: 0x85}, {value: 0x0040, lo: 0x86, hi: 0x86}, {value: 0x0218, lo: 0x87, hi: 0x87}, {value: 0x0018, lo: 0x88, hi: 0x8a}, {value: 0x33c0, lo: 0x8b, hi: 0x8d}, {value: 0x0040, lo: 0x8e, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9f}, {value: 0x0208, lo: 0xa0, hi: 0xbf}, // Block 0x33, offset 0x1d0 {value: 0x0000, lo: 0x02}, {value: 0x0208, lo: 0x80, hi: 0xb8}, {value: 0x0040, lo: 0xb9, hi: 0xbf}, // Block 0x34, offset 0x1d3 {value: 0x0000, lo: 0x07}, {value: 0x0008, lo: 0x80, hi: 0x84}, {value: 0x3308, lo: 0x85, hi: 0x86}, {value: 0x0208, lo: 0x87, hi: 0xa8}, {value: 0x3308, lo: 0xa9, hi: 0xa9}, {value: 0x0208, lo: 0xaa, hi: 0xaa}, {value: 0x0040, lo: 0xab, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xbf}, // Block 0x35, offset 0x1db {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0xb5}, {value: 0x0040, lo: 0xb6, hi: 0xbf}, // Block 0x36, offset 0x1de {value: 0x0000, lo: 0x0c}, {value: 0x0008, lo: 0x80, hi: 0x9e}, {value: 0x0040, lo: 0x9f, hi: 0x9f}, {value: 0x3308, lo: 0xa0, hi: 0xa2}, {value: 0x3008, lo: 0xa3, hi: 0xa6}, {value: 0x3308, lo: 0xa7, hi: 0xa8}, {value: 0x3008, lo: 0xa9, hi: 0xab}, {value: 0x0040, lo: 0xac, hi: 0xaf}, {value: 0x3008, lo: 0xb0, hi: 0xb1}, {value: 0x3308, lo: 0xb2, hi: 0xb2}, {value: 0x3008, lo: 0xb3, hi: 0xb8}, {value: 0x3308, lo: 0xb9, hi: 0xbb}, {value: 0x0040, lo: 0xbc, hi: 0xbf}, // Block 0x37, offset 0x1eb {value: 0x0000, lo: 0x07}, {value: 0x0018, lo: 0x80, hi: 0x80}, {value: 0x0040, lo: 0x81, hi: 0x83}, {value: 0x0018, lo: 0x84, hi: 0x85}, {value: 0x0008, lo: 0x86, hi: 0xad}, {value: 0x0040, lo: 0xae, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xb4}, {value: 0x0040, lo: 0xb5, hi: 0xbf}, // Block 0x38, offset 0x1f3 {value: 0x0000, lo: 0x03}, {value: 0x0008, lo: 0x80, hi: 0xab}, {value: 0x0040, lo: 0xac, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xbf}, // Block 0x39, offset 0x1f7 {value: 0x0000, lo: 0x06}, {value: 0x0008, lo: 0x80, hi: 0x89}, {value: 0x0040, lo: 0x8a, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0028, lo: 0x9a, hi: 0x9a}, {value: 0x0040, lo: 0x9b, hi: 0x9d}, {value: 0x0018, lo: 0x9e, hi: 0xbf}, // Block 0x3a, offset 0x1fe {value: 0x0000, lo: 0x07}, {value: 0x0008, lo: 0x80, hi: 0x96}, {value: 0x3308, lo: 0x97, hi: 0x98}, {value: 0x3008, lo: 0x99, hi: 0x9a}, {value: 0x3308, lo: 0x9b, hi: 0x9b}, {value: 0x0040, lo: 0x9c, hi: 0x9d}, {value: 0x0018, lo: 0x9e, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xbf}, // Block 0x3b, offset 0x206 {value: 0x0000, lo: 0x0f}, {value: 0x0008, lo: 0x80, hi: 0x94}, {value: 0x3008, lo: 0x95, hi: 0x95}, {value: 0x3308, lo: 0x96, hi: 0x96}, {value: 0x3008, lo: 0x97, hi: 0x97}, {value: 0x3308, lo: 0x98, hi: 0x9e}, {value: 0x0040, lo: 0x9f, hi: 0x9f}, {value: 0x3b08, lo: 0xa0, hi: 0xa0}, {value: 0x3008, lo: 0xa1, hi: 0xa1}, {value: 0x3308, lo: 0xa2, hi: 0xa2}, {value: 0x3008, lo: 0xa3, hi: 0xa4}, {value: 0x3308, lo: 0xa5, hi: 0xac}, {value: 0x3008, lo: 0xad, hi: 0xb2}, {value: 0x3308, lo: 0xb3, hi: 0xbc}, {value: 0x0040, lo: 0xbd, hi: 0xbe}, {value: 0x3308, lo: 0xbf, hi: 0xbf}, // Block 0x3c, offset 0x216 {value: 0x0000, lo: 0x0b}, {value: 0x0008, lo: 0x80, hi: 0x89}, {value: 0x0040, lo: 0x8a, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xa6}, {value: 0x0008, lo: 0xa7, hi: 0xa7}, {value: 0x0018, lo: 0xa8, hi: 0xad}, {value: 0x0040, lo: 0xae, hi: 0xaf}, {value: 0x3308, lo: 0xb0, hi: 0xbd}, {value: 0x3318, lo: 0xbe, hi: 0xbe}, {value: 0x0040, lo: 0xbf, hi: 0xbf}, // Block 0x3d, offset 0x222 {value: 0x0000, lo: 0x01}, {value: 0x0040, lo: 0x80, hi: 0xbf}, // Block 0x3e, offset 0x224 {value: 0x0000, lo: 0x09}, {value: 0x3308, lo: 0x80, hi: 0x83}, {value: 0x3008, lo: 0x84, hi: 0x84}, {value: 0x0008, lo: 0x85, hi: 0xb3}, {value: 0x3308, lo: 0xb4, hi: 0xb4}, {value: 0x3008, lo: 0xb5, hi: 0xb5}, {value: 0x3308, lo: 0xb6, hi: 0xba}, {value: 0x3008, lo: 0xbb, hi: 0xbb}, {value: 0x3308, lo: 0xbc, hi: 0xbc}, {value: 0x3008, lo: 0xbd, hi: 0xbf}, // Block 0x3f, offset 0x22e {value: 0x0000, lo: 0x0b}, {value: 0x3008, lo: 0x80, hi: 0x81}, {value: 0x3308, lo: 0x82, hi: 0x82}, {value: 0x3008, lo: 0x83, hi: 0x83}, {value: 0x3808, lo: 0x84, hi: 0x84}, {value: 0x0008, lo: 0x85, hi: 0x8b}, {value: 0x0040, lo: 0x8c, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0018, lo: 0x9a, hi: 0xaa}, {value: 0x3308, lo: 0xab, hi: 0xb3}, {value: 0x0018, lo: 0xb4, hi: 0xbc}, {value: 0x0040, lo: 0xbd, hi: 0xbf}, // Block 0x40, offset 0x23a {value: 0x0000, lo: 0x0b}, {value: 0x3308, lo: 0x80, hi: 0x81}, {value: 0x3008, lo: 0x82, hi: 0x82}, {value: 0x0008, lo: 0x83, hi: 0xa0}, {value: 0x3008, lo: 0xa1, hi: 0xa1}, {value: 0x3308, lo: 0xa2, hi: 0xa5}, {value: 0x3008, lo: 0xa6, hi: 0xa7}, {value: 0x3308, lo: 0xa8, hi: 0xa9}, {value: 0x3808, lo: 0xaa, hi: 0xaa}, {value: 0x3b08, lo: 0xab, hi: 0xab}, {value: 0x3308, lo: 0xac, hi: 0xad}, {value: 0x0008, lo: 0xae, hi: 0xbf}, // Block 0x41, offset 0x246 {value: 0x0000, lo: 0x0b}, {value: 0x0008, lo: 0x80, hi: 0xa5}, {value: 0x3308, lo: 0xa6, hi: 0xa6}, {value: 0x3008, lo: 0xa7, hi: 0xa7}, {value: 0x3308, lo: 0xa8, hi: 0xa9}, {value: 0x3008, lo: 0xaa, hi: 0xac}, {value: 0x3308, lo: 0xad, hi: 0xad}, {value: 0x3008, lo: 0xae, hi: 0xae}, {value: 0x3308, lo: 0xaf, hi: 0xb1}, {value: 0x3808, lo: 0xb2, hi: 0xb3}, {value: 0x0040, lo: 0xb4, hi: 0xbb}, {value: 0x0018, lo: 0xbc, hi: 0xbf}, // Block 0x42, offset 0x252 {value: 0x0000, lo: 0x07}, {value: 0x0008, lo: 0x80, hi: 0xa3}, {value: 0x3008, lo: 0xa4, hi: 0xab}, {value: 0x3308, lo: 0xac, hi: 0xb3}, {value: 0x3008, lo: 0xb4, hi: 0xb5}, {value: 0x3308, lo: 0xb6, hi: 0xb7}, {value: 0x0040, lo: 0xb8, hi: 0xba}, {value: 0x0018, lo: 0xbb, hi: 0xbf}, // Block 0x43, offset 0x25a {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0x89}, {value: 0x0040, lo: 0x8a, hi: 0x8c}, {value: 0x0008, lo: 0x8d, hi: 0xbd}, {value: 0x0018, lo: 0xbe, hi: 0xbf}, // Block 0x44, offset 0x25f {value: 0x0000, lo: 0x0c}, {value: 0x0e29, lo: 0x80, hi: 0x80}, {value: 0x0e41, lo: 0x81, hi: 0x81}, {value: 0x0e59, lo: 0x82, hi: 0x82}, {value: 0x0e71, lo: 0x83, hi: 0x83}, {value: 0x0e89, lo: 0x84, hi: 0x85}, {value: 0x0ea1, lo: 0x86, hi: 0x86}, {value: 0x0eb9, lo: 0x87, hi: 0x87}, {value: 0x057d, lo: 0x88, hi: 0x88}, {value: 0x0040, lo: 0x89, hi: 0x8f}, {value: 0x059d, lo: 0x90, hi: 0xba}, {value: 0x0040, lo: 0xbb, hi: 0xbc}, {value: 0x059d, lo: 0xbd, hi: 0xbf}, // Block 0x45, offset 0x26c {value: 0x0000, lo: 0x10}, {value: 0x0018, lo: 0x80, hi: 0x87}, {value: 0x0040, lo: 0x88, hi: 0x8f}, {value: 0x3308, lo: 0x90, hi: 0x92}, {value: 0x0018, lo: 0x93, hi: 0x93}, {value: 0x3308, lo: 0x94, hi: 0xa0}, {value: 0x3008, lo: 0xa1, hi: 0xa1}, {value: 0x3308, lo: 0xa2, hi: 0xa8}, {value: 0x0008, lo: 0xa9, hi: 0xac}, {value: 0x3308, lo: 0xad, hi: 0xad}, {value: 0x0008, lo: 0xae, hi: 0xb3}, {value: 0x3308, lo: 0xb4, hi: 0xb4}, {value: 0x0008, lo: 0xb5, hi: 0xb6}, {value: 0x3008, lo: 0xb7, hi: 0xb7}, {value: 0x3308, lo: 0xb8, hi: 0xb9}, {value: 0x0008, lo: 0xba, hi: 0xba}, {value: 0x0040, lo: 0xbb, hi: 0xbf}, // Block 0x46, offset 0x27d {value: 0x0000, lo: 0x03}, {value: 0x3308, lo: 0x80, hi: 0xb9}, {value: 0x0040, lo: 0xba, hi: 0xba}, {value: 0x3308, lo: 0xbb, hi: 0xbf}, // Block 0x47, offset 0x281 {value: 0x0000, lo: 0x0a}, {value: 0x0008, lo: 0x80, hi: 0x87}, {value: 0xe045, lo: 0x88, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x95}, {value: 0x0040, lo: 0x96, hi: 0x97}, {value: 0xe045, lo: 0x98, hi: 0x9d}, {value: 0x0040, lo: 0x9e, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa7}, {value: 0xe045, lo: 0xa8, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xb7}, {value: 0xe045, lo: 0xb8, hi: 0xbf}, // Block 0x48, offset 0x28c {value: 0x0000, lo: 0x03}, {value: 0x0040, lo: 0x80, hi: 0x8f}, {value: 0x3318, lo: 0x90, hi: 0xb0}, {value: 0x0040, lo: 0xb1, hi: 0xbf}, // Block 0x49, offset 0x290 {value: 0x0000, lo: 0x08}, {value: 0x0018, lo: 0x80, hi: 0x82}, {value: 0x0040, lo: 0x83, hi: 0x83}, {value: 0x0008, lo: 0x84, hi: 0x84}, {value: 0x0018, lo: 0x85, hi: 0x88}, {value: 0x24c1, lo: 0x89, hi: 0x89}, {value: 0x0018, lo: 0x8a, hi: 0x8b}, {value: 0x0040, lo: 0x8c, hi: 0x8f}, {value: 0x0018, lo: 0x90, hi: 0xbf}, // Block 0x4a, offset 0x299 {value: 0x0000, lo: 0x07}, {value: 0x0018, lo: 0x80, hi: 0xab}, {value: 0x24f1, lo: 0xac, hi: 0xac}, {value: 0x2529, lo: 0xad, hi: 0xad}, {value: 0x0018, lo: 0xae, hi: 0xae}, {value: 0x2579, lo: 0xaf, hi: 0xaf}, {value: 0x25b1, lo: 0xb0, hi: 0xb0}, {value: 0x0018, lo: 0xb1, hi: 0xbf}, // Block 0x4b, offset 0x2a1 {value: 0x0000, lo: 0x05}, {value: 0x0018, lo: 0x80, hi: 0x9f}, {value: 0x0080, lo: 0xa0, hi: 0xa0}, {value: 0x0018, lo: 0xa1, hi: 0xad}, {value: 0x0080, lo: 0xae, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xbf}, // Block 0x4c, offset 0x2a7 {value: 0x0000, lo: 0x04}, {value: 0x0018, lo: 0x80, hi: 0xa8}, {value: 0x09dd, lo: 0xa9, hi: 0xa9}, {value: 0x09fd, lo: 0xaa, hi: 0xaa}, {value: 0x0018, lo: 0xab, hi: 0xbf}, // Block 0x4d, offset 0x2ac {value: 0x0000, lo: 0x02}, {value: 0x0018, lo: 0x80, hi: 0xa6}, {value: 0x0040, lo: 0xa7, hi: 0xbf}, // Block 0x4e, offset 0x2af {value: 0x0000, lo: 0x03}, {value: 0x0018, lo: 0x80, hi: 0x8b}, {value: 0x28c1, lo: 0x8c, hi: 0x8c}, {value: 0x0018, lo: 0x8d, hi: 0xbf}, // Block 0x4f, offset 0x2b3 {value: 0x0000, lo: 0x05}, {value: 0x0018, lo: 0x80, hi: 0xb3}, {value: 0x0e7e, lo: 0xb4, hi: 0xb4}, {value: 0x292a, lo: 0xb5, hi: 0xb5}, {value: 0x0e9e, lo: 0xb6, hi: 0xb6}, {value: 0x0018, lo: 0xb7, hi: 0xbf}, // Block 0x50, offset 0x2b9 {value: 0x0000, lo: 0x03}, {value: 0x0018, lo: 0x80, hi: 0x9b}, {value: 0x2941, lo: 0x9c, hi: 0x9c}, {value: 0x0018, lo: 0x9d, hi: 0xbf}, // Block 0x51, offset 0x2bd {value: 0x0000, lo: 0x03}, {value: 0x0018, lo: 0x80, hi: 0xb3}, {value: 0x0040, lo: 0xb4, hi: 0xb5}, {value: 0x0018, lo: 0xb6, hi: 0xbf}, // Block 0x52, offset 0x2c1 {value: 0x0000, lo: 0x03}, {value: 0x0018, lo: 0x80, hi: 0x95}, {value: 0x0040, lo: 0x96, hi: 0x97}, {value: 0x0018, lo: 0x98, hi: 0xbf}, // Block 0x53, offset 0x2c5 {value: 0x0000, lo: 0x05}, {value: 0xe185, lo: 0x80, hi: 0x8f}, {value: 0x03f5, lo: 0x90, hi: 0x9f}, {value: 0x0ebd, lo: 0xa0, hi: 0xae}, {value: 0x0040, lo: 0xaf, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xbf}, // Block 0x54, offset 0x2cb {value: 0x0000, lo: 0x07}, {value: 0x0008, lo: 0x80, hi: 0xa5}, {value: 0x0040, lo: 0xa6, hi: 0xa6}, {value: 0x0008, lo: 0xa7, hi: 0xa7}, {value: 0x0040, lo: 0xa8, hi: 0xac}, {value: 0x0008, lo: 0xad, hi: 0xad}, {value: 0x0040, lo: 0xae, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xbf}, // Block 0x55, offset 0x2d3 {value: 0x0000, lo: 0x06}, {value: 0x0008, lo: 0x80, hi: 0xa7}, {value: 0x0040, lo: 0xa8, hi: 0xae}, {value: 0xe075, lo: 0xaf, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xb0}, {value: 0x0040, lo: 0xb1, hi: 0xbe}, {value: 0x3b08, lo: 0xbf, hi: 0xbf}, // Block 0x56, offset 0x2da {value: 0x0000, lo: 0x0a}, {value: 0x0008, lo: 0x80, hi: 0x96}, {value: 0x0040, lo: 0x97, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa6}, {value: 0x0040, lo: 0xa7, hi: 0xa7}, {value: 0x0008, lo: 0xa8, hi: 0xae}, {value: 0x0040, lo: 0xaf, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xb6}, {value: 0x0040, lo: 0xb7, hi: 0xb7}, {value: 0x0008, lo: 0xb8, hi: 0xbe}, {value: 0x0040, lo: 0xbf, hi: 0xbf}, // Block 0x57, offset 0x2e5 {value: 0x0000, lo: 0x09}, {value: 0x0008, lo: 0x80, hi: 0x86}, {value: 0x0040, lo: 0x87, hi: 0x87}, {value: 0x0008, lo: 0x88, hi: 0x8e}, {value: 0x0040, lo: 0x8f, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x96}, {value: 0x0040, lo: 0x97, hi: 0x97}, {value: 0x0008, lo: 0x98, hi: 0x9e}, {value: 0x0040, lo: 0x9f, hi: 0x9f}, {value: 0x3308, lo: 0xa0, hi: 0xbf}, // Block 0x58, offset 0x2ef {value: 0x0000, lo: 0x03}, {value: 0x0018, lo: 0x80, hi: 0xae}, {value: 0x0008, lo: 0xaf, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xbf}, // Block 0x59, offset 0x2f3 {value: 0x0000, lo: 0x02}, {value: 0x0018, lo: 0x80, hi: 0x8f}, {value: 0x0040, lo: 0x90, hi: 0xbf}, // Block 0x5a, offset 0x2f6 {value: 0x0000, lo: 0x05}, {value: 0x0018, lo: 0x80, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9a}, {value: 0x0018, lo: 0x9b, hi: 0x9e}, {value: 0x0ef5, lo: 0x9f, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xbf}, // Block 0x5b, offset 0x2fc {value: 0x0000, lo: 0x03}, {value: 0x0018, lo: 0x80, hi: 0xb2}, {value: 0x0f15, lo: 0xb3, hi: 0xb3}, {value: 0x0040, lo: 0xb4, hi: 0xbf}, // Block 0x5c, offset 0x300 {value: 0x0020, lo: 0x01}, {value: 0x0f35, lo: 0x80, hi: 0xbf}, // Block 0x5d, offset 0x302 {value: 0x0020, lo: 0x02}, {value: 0x1735, lo: 0x80, hi: 0x8f}, {value: 0x1915, lo: 0x90, hi: 0xbf}, // Block 0x5e, offset 0x305 {value: 0x0020, lo: 0x01}, {value: 0x1f15, lo: 0x80, hi: 0xbf}, // Block 0x5f, offset 0x307 {value: 0x0000, lo: 0x02}, {value: 0x0040, lo: 0x80, hi: 0x80}, {value: 0x0008, lo: 0x81, hi: 0xbf}, // Block 0x60, offset 0x30a {value: 0x0000, lo: 0x09}, {value: 0x0008, lo: 0x80, hi: 0x96}, {value: 0x0040, lo: 0x97, hi: 0x98}, {value: 0x3308, lo: 0x99, hi: 0x9a}, {value: 0x29e2, lo: 0x9b, hi: 0x9b}, {value: 0x2a0a, lo: 0x9c, hi: 0x9c}, {value: 0x0008, lo: 0x9d, hi: 0x9e}, {value: 0x2a31, lo: 0x9f, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xa0}, {value: 0x0008, lo: 0xa1, hi: 0xbf}, // Block 0x61, offset 0x314 {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0xbe}, {value: 0x2a69, lo: 0xbf, hi: 0xbf}, // Block 0x62, offset 0x317 {value: 0x0000, lo: 0x0e}, {value: 0x0040, lo: 0x80, hi: 0x84}, {value: 0x0008, lo: 0x85, hi: 0xaf}, {value: 0x0040, lo: 0xb0, hi: 0xb0}, {value: 0x2a35, lo: 0xb1, hi: 0xb1}, {value: 0x2a55, lo: 0xb2, hi: 0xb2}, {value: 0x2a75, lo: 0xb3, hi: 0xb3}, {value: 0x2a95, lo: 0xb4, hi: 0xb4}, {value: 0x2a75, lo: 0xb5, hi: 0xb5}, {value: 0x2ab5, lo: 0xb6, hi: 0xb6}, {value: 0x2ad5, lo: 0xb7, hi: 0xb7}, {value: 0x2af5, lo: 0xb8, hi: 0xb9}, {value: 0x2b15, lo: 0xba, hi: 0xbb}, {value: 0x2b35, lo: 0xbc, hi: 0xbd}, {value: 0x2b15, lo: 0xbe, hi: 0xbf}, // Block 0x63, offset 0x326 {value: 0x0000, lo: 0x03}, {value: 0x0018, lo: 0x80, hi: 0xa3}, {value: 0x0040, lo: 0xa4, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xbf}, // Block 0x64, offset 0x32a {value: 0x0030, lo: 0x04}, {value: 0x2aa2, lo: 0x80, hi: 0x9d}, {value: 0x305a, lo: 0x9e, hi: 0x9e}, {value: 0x0040, lo: 0x9f, hi: 0x9f}, {value: 0x30a2, lo: 0xa0, hi: 0xbf}, // Block 0x65, offset 0x32f {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0xaf}, {value: 0x0040, lo: 0xb0, hi: 0xbf}, // Block 0x66, offset 0x332 {value: 0x0000, lo: 0x03}, {value: 0x0008, lo: 0x80, hi: 0x8c}, {value: 0x0040, lo: 0x8d, hi: 0x8f}, {value: 0x0018, lo: 0x90, hi: 0xbf}, // Block 0x67, offset 0x336 {value: 0x0000, lo: 0x04}, {value: 0x0018, lo: 0x80, hi: 0x86}, {value: 0x0040, lo: 0x87, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0xbd}, {value: 0x0018, lo: 0xbe, hi: 0xbf}, // Block 0x68, offset 0x33b {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0x8c}, {value: 0x0018, lo: 0x8d, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0xab}, {value: 0x0040, lo: 0xac, hi: 0xbf}, // Block 0x69, offset 0x340 {value: 0x0000, lo: 0x05}, {value: 0x0008, lo: 0x80, hi: 0xa5}, {value: 0x0018, lo: 0xa6, hi: 0xaf}, {value: 0x3308, lo: 0xb0, hi: 0xb1}, {value: 0x0018, lo: 0xb2, hi: 0xb7}, {value: 0x0040, lo: 0xb8, hi: 0xbf}, // Block 0x6a, offset 0x346 {value: 0x0000, lo: 0x0b}, {value: 0x0040, lo: 0x80, hi: 0x81}, {value: 0xe00d, lo: 0x82, hi: 0x82}, {value: 0x0008, lo: 0x83, hi: 0x83}, {value: 0x03f5, lo: 0x84, hi: 0x84}, {value: 0x1329, lo: 0x85, hi: 0x85}, {value: 0x447d, lo: 0x86, hi: 0x86}, {value: 0x0040, lo: 0x87, hi: 0xb6}, {value: 0x0008, lo: 0xb7, hi: 0xb7}, {value: 0x2009, lo: 0xb8, hi: 0xb8}, {value: 0x6e89, lo: 0xb9, hi: 0xb9}, {value: 0x0008, lo: 0xba, hi: 0xbf}, // Block 0x6b, offset 0x352 {value: 0x0000, lo: 0x0e}, {value: 0x0008, lo: 0x80, hi: 0x81}, {value: 0x3308, lo: 0x82, hi: 0x82}, {value: 0x0008, lo: 0x83, hi: 0x85}, {value: 0x3b08, lo: 0x86, hi: 0x86}, {value: 0x0008, lo: 0x87, hi: 0x8a}, {value: 0x3308, lo: 0x8b, hi: 0x8b}, {value: 0x0008, lo: 0x8c, hi: 0xa2}, {value: 0x3008, lo: 0xa3, hi: 0xa4}, {value: 0x3308, lo: 0xa5, hi: 0xa6}, {value: 0x3008, lo: 0xa7, hi: 0xa7}, {value: 0x0018, lo: 0xa8, hi: 0xab}, {value: 0x0040, lo: 0xac, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xb9}, {value: 0x0040, lo: 0xba, hi: 0xbf}, // Block 0x6c, offset 0x361 {value: 0x0000, lo: 0x05}, {value: 0x0208, lo: 0x80, hi: 0xb1}, {value: 0x0108, lo: 0xb2, hi: 0xb2}, {value: 0x0008, lo: 0xb3, hi: 0xb3}, {value: 0x0018, lo: 0xb4, hi: 0xb7}, {value: 0x0040, lo: 0xb8, hi: 0xbf}, // Block 0x6d, offset 0x367 {value: 0x0000, lo: 0x03}, {value: 0x3008, lo: 0x80, hi: 0x81}, {value: 0x0008, lo: 0x82, hi: 0xb3}, {value: 0x3008, lo: 0xb4, hi: 0xbf}, // Block 0x6e, offset 0x36b {value: 0x0000, lo: 0x0e}, {value: 0x3008, lo: 0x80, hi: 0x83}, {value: 0x3b08, lo: 0x84, hi: 0x84}, {value: 0x3308, lo: 0x85, hi: 0x85}, {value: 0x0040, lo: 0x86, hi: 0x8d}, {value: 0x0018, lo: 0x8e, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9f}, {value: 0x3308, lo: 0xa0, hi: 0xb1}, {value: 0x0008, lo: 0xb2, hi: 0xb7}, {value: 0x0018, lo: 0xb8, hi: 0xba}, {value: 0x0008, lo: 0xbb, hi: 0xbb}, {value: 0x0018, lo: 0xbc, hi: 0xbc}, {value: 0x0008, lo: 0xbd, hi: 0xbe}, {value: 0x3308, lo: 0xbf, hi: 0xbf}, // Block 0x6f, offset 0x37a {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0xa5}, {value: 0x3308, lo: 0xa6, hi: 0xad}, {value: 0x0018, lo: 0xae, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xbf}, // Block 0x70, offset 0x37f {value: 0x0000, lo: 0x07}, {value: 0x0008, lo: 0x80, hi: 0x86}, {value: 0x3308, lo: 0x87, hi: 0x91}, {value: 0x3008, lo: 0x92, hi: 0x92}, {value: 0x3808, lo: 0x93, hi: 0x93}, {value: 0x0040, lo: 0x94, hi: 0x9e}, {value: 0x0018, lo: 0x9f, hi: 0xbc}, {value: 0x0040, lo: 0xbd, hi: 0xbf}, // Block 0x71, offset 0x387 {value: 0x0000, lo: 0x09}, {value: 0x3308, lo: 0x80, hi: 0x82}, {value: 0x3008, lo: 0x83, hi: 0x83}, {value: 0x0008, lo: 0x84, hi: 0xb2}, {value: 0x3308, lo: 0xb3, hi: 0xb3}, {value: 0x3008, lo: 0xb4, hi: 0xb5}, {value: 0x3308, lo: 0xb6, hi: 0xb9}, {value: 0x3008, lo: 0xba, hi: 0xbb}, {value: 0x3308, lo: 0xbc, hi: 0xbd}, {value: 0x3008, lo: 0xbe, hi: 0xbf}, // Block 0x72, offset 0x391 {value: 0x0000, lo: 0x0a}, {value: 0x3808, lo: 0x80, hi: 0x80}, {value: 0x0018, lo: 0x81, hi: 0x8d}, {value: 0x0040, lo: 0x8e, hi: 0x8e}, {value: 0x0008, lo: 0x8f, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9d}, {value: 0x0018, lo: 0x9e, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa4}, {value: 0x3308, lo: 0xa5, hi: 0xa5}, {value: 0x0008, lo: 0xa6, hi: 0xbe}, {value: 0x0040, lo: 0xbf, hi: 0xbf}, // Block 0x73, offset 0x39c {value: 0x0000, lo: 0x07}, {value: 0x0008, lo: 0x80, hi: 0xa8}, {value: 0x3308, lo: 0xa9, hi: 0xae}, {value: 0x3008, lo: 0xaf, hi: 0xb0}, {value: 0x3308, lo: 0xb1, hi: 0xb2}, {value: 0x3008, lo: 0xb3, hi: 0xb4}, {value: 0x3308, lo: 0xb5, hi: 0xb6}, {value: 0x0040, lo: 0xb7, hi: 0xbf}, // Block 0x74, offset 0x3a4 {value: 0x0000, lo: 0x10}, {value: 0x0008, lo: 0x80, hi: 0x82}, {value: 0x3308, lo: 0x83, hi: 0x83}, {value: 0x0008, lo: 0x84, hi: 0x8b}, {value: 0x3308, lo: 0x8c, hi: 0x8c}, {value: 0x3008, lo: 0x8d, hi: 0x8d}, {value: 0x0040, lo: 0x8e, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9b}, {value: 0x0018, lo: 0x9c, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xb6}, {value: 0x0018, lo: 0xb7, hi: 0xb9}, {value: 0x0008, lo: 0xba, hi: 0xba}, {value: 0x3008, lo: 0xbb, hi: 0xbb}, {value: 0x3308, lo: 0xbc, hi: 0xbc}, {value: 0x3008, lo: 0xbd, hi: 0xbd}, {value: 0x0008, lo: 0xbe, hi: 0xbf}, // Block 0x75, offset 0x3b5 {value: 0x0000, lo: 0x08}, {value: 0x0008, lo: 0x80, hi: 0xaf}, {value: 0x3308, lo: 0xb0, hi: 0xb0}, {value: 0x0008, lo: 0xb1, hi: 0xb1}, {value: 0x3308, lo: 0xb2, hi: 0xb4}, {value: 0x0008, lo: 0xb5, hi: 0xb6}, {value: 0x3308, lo: 0xb7, hi: 0xb8}, {value: 0x0008, lo: 0xb9, hi: 0xbd}, {value: 0x3308, lo: 0xbe, hi: 0xbf}, // Block 0x76, offset 0x3be {value: 0x0000, lo: 0x0f}, {value: 0x0008, lo: 0x80, hi: 0x80}, {value: 0x3308, lo: 0x81, hi: 0x81}, {value: 0x0008, lo: 0x82, hi: 0x82}, {value: 0x0040, lo: 0x83, hi: 0x9a}, {value: 0x0008, lo: 0x9b, hi: 0x9d}, {value: 0x0018, lo: 0x9e, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xaa}, {value: 0x3008, lo: 0xab, hi: 0xab}, {value: 0x3308, lo: 0xac, hi: 0xad}, {value: 0x3008, lo: 0xae, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xb1}, {value: 0x0008, lo: 0xb2, hi: 0xb4}, {value: 0x3008, lo: 0xb5, hi: 0xb5}, {value: 0x3b08, lo: 0xb6, hi: 0xb6}, {value: 0x0040, lo: 0xb7, hi: 0xbf}, // Block 0x77, offset 0x3ce {value: 0x0000, lo: 0x0c}, {value: 0x0040, lo: 0x80, hi: 0x80}, {value: 0x0008, lo: 0x81, hi: 0x86}, {value: 0x0040, lo: 0x87, hi: 0x88}, {value: 0x0008, lo: 0x89, hi: 0x8e}, {value: 0x0040, lo: 0x8f, hi: 0x90}, {value: 0x0008, lo: 0x91, hi: 0x96}, {value: 0x0040, lo: 0x97, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa6}, {value: 0x0040, lo: 0xa7, hi: 0xa7}, {value: 0x0008, lo: 0xa8, hi: 0xae}, {value: 0x0040, lo: 0xaf, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xbf}, // Block 0x78, offset 0x3db {value: 0x0000, lo: 0x09}, {value: 0x0008, lo: 0x80, hi: 0x9a}, {value: 0x0018, lo: 0x9b, hi: 0x9b}, {value: 0x449d, lo: 0x9c, hi: 0x9c}, {value: 0x44b5, lo: 0x9d, hi: 0x9d}, {value: 0x2971, lo: 0x9e, hi: 0x9e}, {value: 0xe06d, lo: 0x9f, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa7}, {value: 0x0040, lo: 0xa8, hi: 0xaf}, {value: 0x44cd, lo: 0xb0, hi: 0xbf}, // Block 0x79, offset 0x3e5 {value: 0x0000, lo: 0x04}, {value: 0x44ed, lo: 0x80, hi: 0x8f}, {value: 0x450d, lo: 0x90, hi: 0x9f}, {value: 0x452d, lo: 0xa0, hi: 0xaf}, {value: 0x450d, lo: 0xb0, hi: 0xbf}, // Block 0x7a, offset 0x3ea {value: 0x0000, lo: 0x0c}, {value: 0x0008, lo: 0x80, hi: 0xa2}, {value: 0x3008, lo: 0xa3, hi: 0xa4}, {value: 0x3308, lo: 0xa5, hi: 0xa5}, {value: 0x3008, lo: 0xa6, hi: 0xa7}, {value: 0x3308, lo: 0xa8, hi: 0xa8}, {value: 0x3008, lo: 0xa9, hi: 0xaa}, {value: 0x0018, lo: 0xab, hi: 0xab}, {value: 0x3008, lo: 0xac, hi: 0xac}, {value: 0x3b08, lo: 0xad, hi: 0xad}, {value: 0x0040, lo: 0xae, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xb9}, {value: 0x0040, lo: 0xba, hi: 0xbf}, // Block 0x7b, offset 0x3f7 {value: 0x0000, lo: 0x03}, {value: 0x0008, lo: 0x80, hi: 0xa3}, {value: 0x0040, lo: 0xa4, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xbf}, // Block 0x7c, offset 0x3fb {value: 0x0000, lo: 0x04}, {value: 0x0018, lo: 0x80, hi: 0x86}, {value: 0x0040, lo: 0x87, hi: 0x8a}, {value: 0x0018, lo: 0x8b, hi: 0xbb}, {value: 0x0040, lo: 0xbc, hi: 0xbf}, // Block 0x7d, offset 0x400 {value: 0x0020, lo: 0x01}, {value: 0x454d, lo: 0x80, hi: 0xbf}, // Block 0x7e, offset 0x402 {value: 0x0020, lo: 0x03}, {value: 0x4d4d, lo: 0x80, hi: 0x94}, {value: 0x4b0d, lo: 0x95, hi: 0x95}, {value: 0x4fed, lo: 0x96, hi: 0xbf}, // Block 0x7f, offset 0x406 {value: 0x0020, lo: 0x01}, {value: 0x552d, lo: 0x80, hi: 0xbf}, // Block 0x80, offset 0x408 {value: 0x0020, lo: 0x03}, {value: 0x5d2d, lo: 0x80, hi: 0x84}, {value: 0x568d, lo: 0x85, hi: 0x85}, {value: 0x5dcd, lo: 0x86, hi: 0xbf}, // Block 0x81, offset 0x40c {value: 0x0020, lo: 0x08}, {value: 0x6b8d, lo: 0x80, hi: 0x8f}, {value: 0x6d4d, lo: 0x90, hi: 0x90}, {value: 0x6d8d, lo: 0x91, hi: 0xab}, {value: 0x6ea1, lo: 0xac, hi: 0xac}, {value: 0x70ed, lo: 0xad, hi: 0xad}, {value: 0x0040, lo: 0xae, hi: 0xae}, {value: 0x0040, lo: 0xaf, hi: 0xaf}, {value: 0x710d, lo: 0xb0, hi: 0xbf}, // Block 0x82, offset 0x415 {value: 0x0020, lo: 0x05}, {value: 0x730d, lo: 0x80, hi: 0xad}, {value: 0x656d, lo: 0xae, hi: 0xae}, {value: 0x78cd, lo: 0xaf, hi: 0xb5}, {value: 0x6f8d, lo: 0xb6, hi: 0xb6}, {value: 0x79ad, lo: 0xb7, hi: 0xbf}, // Block 0x83, offset 0x41b {value: 0x0028, lo: 0x03}, {value: 0x7c21, lo: 0x80, hi: 0x82}, {value: 0x7be1, lo: 0x83, hi: 0x83}, {value: 0x7c99, lo: 0x84, hi: 0xbf}, // Block 0x84, offset 0x41f {value: 0x0038, lo: 0x0f}, {value: 0x9db1, lo: 0x80, hi: 0x83}, {value: 0x9e59, lo: 0x84, hi: 0x85}, {value: 0x9e91, lo: 0x86, hi: 0x87}, {value: 0x9ec9, lo: 0x88, hi: 0x8f}, {value: 0x0040, lo: 0x90, hi: 0x90}, {value: 0x0040, lo: 0x91, hi: 0x91}, {value: 0xa089, lo: 0x92, hi: 0x97}, {value: 0xa1a1, lo: 0x98, hi: 0x9c}, {value: 0xa281, lo: 0x9d, hi: 0xb3}, {value: 0x9d41, lo: 0xb4, hi: 0xb4}, {value: 0x9db1, lo: 0xb5, hi: 0xb5}, {value: 0xa789, lo: 0xb6, hi: 0xbb}, {value: 0xa869, lo: 0xbc, hi: 0xbc}, {value: 0xa7f9, lo: 0xbd, hi: 0xbd}, {value: 0xa8d9, lo: 0xbe, hi: 0xbf}, // Block 0x85, offset 0x42f {value: 0x0000, lo: 0x09}, {value: 0x0008, lo: 0x80, hi: 0x8b}, {value: 0x0040, lo: 0x8c, hi: 0x8c}, {value: 0x0008, lo: 0x8d, hi: 0xa6}, {value: 0x0040, lo: 0xa7, hi: 0xa7}, {value: 0x0008, lo: 0xa8, hi: 0xba}, {value: 0x0040, lo: 0xbb, hi: 0xbb}, {value: 0x0008, lo: 0xbc, hi: 0xbd}, {value: 0x0040, lo: 0xbe, hi: 0xbe}, {value: 0x0008, lo: 0xbf, hi: 0xbf}, // Block 0x86, offset 0x439 {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0x8d}, {value: 0x0040, lo: 0x8e, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x9d}, {value: 0x0040, lo: 0x9e, hi: 0xbf}, // Block 0x87, offset 0x43e {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0xba}, {value: 0x0040, lo: 0xbb, hi: 0xbf}, // Block 0x88, offset 0x441 {value: 0x0000, lo: 0x05}, {value: 0x0018, lo: 0x80, hi: 0x82}, {value: 0x0040, lo: 0x83, hi: 0x86}, {value: 0x0018, lo: 0x87, hi: 0xb3}, {value: 0x0040, lo: 0xb4, hi: 0xb6}, {value: 0x0018, lo: 0xb7, hi: 0xbf}, // Block 0x89, offset 0x447 {value: 0x0000, lo: 0x06}, {value: 0x0018, lo: 0x80, hi: 0x8e}, {value: 0x0040, lo: 0x8f, hi: 0x8f}, {value: 0x0018, lo: 0x90, hi: 0x9b}, {value: 0x0040, lo: 0x9c, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xa0}, {value: 0x0040, lo: 0xa1, hi: 0xbf}, // Block 0x8a, offset 0x44e {value: 0x0000, lo: 0x04}, {value: 0x0040, lo: 0x80, hi: 0x8f}, {value: 0x0018, lo: 0x90, hi: 0xbc}, {value: 0x3308, lo: 0xbd, hi: 0xbd}, {value: 0x0040, lo: 0xbe, hi: 0xbf}, // Block 0x8b, offset 0x453 {value: 0x0000, lo: 0x03}, {value: 0x0008, lo: 0x80, hi: 0x9c}, {value: 0x0040, lo: 0x9d, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xbf}, // Block 0x8c, offset 0x457 {value: 0x0000, lo: 0x05}, {value: 0x0008, lo: 0x80, hi: 0x90}, {value: 0x0040, lo: 0x91, hi: 0x9f}, {value: 0x3308, lo: 0xa0, hi: 0xa0}, {value: 0x0018, lo: 0xa1, hi: 0xbb}, {value: 0x0040, lo: 0xbc, hi: 0xbf}, // Block 0x8d, offset 0x45d {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xa3}, {value: 0x0040, lo: 0xa4, hi: 0xac}, {value: 0x0008, lo: 0xad, hi: 0xbf}, // Block 0x8e, offset 0x462 {value: 0x0000, lo: 0x08}, {value: 0x0008, lo: 0x80, hi: 0x80}, {value: 0x0018, lo: 0x81, hi: 0x81}, {value: 0x0008, lo: 0x82, hi: 0x89}, {value: 0x0018, lo: 0x8a, hi: 0x8a}, {value: 0x0040, lo: 0x8b, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0xb5}, {value: 0x3308, lo: 0xb6, hi: 0xba}, {value: 0x0040, lo: 0xbb, hi: 0xbf}, // Block 0x8f, offset 0x46b {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0x9d}, {value: 0x0040, lo: 0x9e, hi: 0x9e}, {value: 0x0018, lo: 0x9f, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xbf}, // Block 0x90, offset 0x470 {value: 0x0000, lo: 0x05}, {value: 0x0008, lo: 0x80, hi: 0x83}, {value: 0x0040, lo: 0x84, hi: 0x87}, {value: 0x0008, lo: 0x88, hi: 0x8f}, {value: 0x0018, lo: 0x90, hi: 0x95}, {value: 0x0040, lo: 0x96, hi: 0xbf}, // Block 0x91, offset 0x476 {value: 0x0000, lo: 0x06}, {value: 0xe145, lo: 0x80, hi: 0x87}, {value: 0xe1c5, lo: 0x88, hi: 0x8f}, {value: 0xe145, lo: 0x90, hi: 0x97}, {value: 0x8b0d, lo: 0x98, hi: 0x9f}, {value: 0x8b25, lo: 0xa0, hi: 0xa7}, {value: 0x0008, lo: 0xa8, hi: 0xbf}, // Block 0x92, offset 0x47d {value: 0x0000, lo: 0x06}, {value: 0x0008, lo: 0x80, hi: 0x9d}, {value: 0x0040, lo: 0x9e, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa9}, {value: 0x0040, lo: 0xaa, hi: 0xaf}, {value: 0x8b25, lo: 0xb0, hi: 0xb7}, {value: 0x8b0d, lo: 0xb8, hi: 0xbf}, // Block 0x93, offset 0x484 {value: 0x0000, lo: 0x06}, {value: 0xe145, lo: 0x80, hi: 0x87}, {value: 0xe1c5, lo: 0x88, hi: 0x8f}, {value: 0xe145, lo: 0x90, hi: 0x93}, {value: 0x0040, lo: 0x94, hi: 0x97}, {value: 0x0008, lo: 0x98, hi: 0xbb}, {value: 0x0040, lo: 0xbc, hi: 0xbf}, // Block 0x94, offset 0x48b {value: 0x0000, lo: 0x03}, {value: 0x0008, lo: 0x80, hi: 0xa7}, {value: 0x0040, lo: 0xa8, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xbf}, // Block 0x95, offset 0x48f {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0xa3}, {value: 0x0040, lo: 0xa4, hi: 0xae}, {value: 0x0018, lo: 0xaf, hi: 0xaf}, {value: 0x0040, lo: 0xb0, hi: 0xbf}, // Block 0x96, offset 0x494 {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0xb6}, {value: 0x0040, lo: 0xb7, hi: 0xbf}, // Block 0x97, offset 0x497 {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0x95}, {value: 0x0040, lo: 0x96, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa7}, {value: 0x0040, lo: 0xa8, hi: 0xbf}, // Block 0x98, offset 0x49c {value: 0x0000, lo: 0x0b}, {value: 0x0808, lo: 0x80, hi: 0x85}, {value: 0x0040, lo: 0x86, hi: 0x87}, {value: 0x0808, lo: 0x88, hi: 0x88}, {value: 0x0040, lo: 0x89, hi: 0x89}, {value: 0x0808, lo: 0x8a, hi: 0xb5}, {value: 0x0040, lo: 0xb6, hi: 0xb6}, {value: 0x0808, lo: 0xb7, hi: 0xb8}, {value: 0x0040, lo: 0xb9, hi: 0xbb}, {value: 0x0808, lo: 0xbc, hi: 0xbc}, {value: 0x0040, lo: 0xbd, hi: 0xbe}, {value: 0x0808, lo: 0xbf, hi: 0xbf}, // Block 0x99, offset 0x4a8 {value: 0x0000, lo: 0x05}, {value: 0x0808, lo: 0x80, hi: 0x95}, {value: 0x0040, lo: 0x96, hi: 0x96}, {value: 0x0818, lo: 0x97, hi: 0x9f}, {value: 0x0808, lo: 0xa0, hi: 0xb6}, {value: 0x0818, lo: 0xb7, hi: 0xbf}, // Block 0x9a, offset 0x4ae {value: 0x0000, lo: 0x04}, {value: 0x0808, lo: 0x80, hi: 0x9e}, {value: 0x0040, lo: 0x9f, hi: 0xa6}, {value: 0x0818, lo: 0xa7, hi: 0xaf}, {value: 0x0040, lo: 0xb0, hi: 0xbf}, // Block 0x9b, offset 0x4b3 {value: 0x0000, lo: 0x06}, {value: 0x0040, lo: 0x80, hi: 0x9f}, {value: 0x0808, lo: 0xa0, hi: 0xb2}, {value: 0x0040, lo: 0xb3, hi: 0xb3}, {value: 0x0808, lo: 0xb4, hi: 0xb5}, {value: 0x0040, lo: 0xb6, hi: 0xba}, {value: 0x0818, lo: 0xbb, hi: 0xbf}, // Block 0x9c, offset 0x4ba {value: 0x0000, lo: 0x07}, {value: 0x0808, lo: 0x80, hi: 0x95}, {value: 0x0818, lo: 0x96, hi: 0x9b}, {value: 0x0040, lo: 0x9c, hi: 0x9e}, {value: 0x0018, lo: 0x9f, hi: 0x9f}, {value: 0x0808, lo: 0xa0, hi: 0xb9}, {value: 0x0040, lo: 0xba, hi: 0xbe}, {value: 0x0818, lo: 0xbf, hi: 0xbf}, // Block 0x9d, offset 0x4c2 {value: 0x0000, lo: 0x04}, {value: 0x0808, lo: 0x80, hi: 0xb7}, {value: 0x0040, lo: 0xb8, hi: 0xbb}, {value: 0x0818, lo: 0xbc, hi: 0xbd}, {value: 0x0808, lo: 0xbe, hi: 0xbf}, // Block 0x9e, offset 0x4c7 {value: 0x0000, lo: 0x03}, {value: 0x0818, lo: 0x80, hi: 0x8f}, {value: 0x0040, lo: 0x90, hi: 0x91}, {value: 0x0818, lo: 0x92, hi: 0xbf}, // Block 0x9f, offset 0x4cb {value: 0x0000, lo: 0x0f}, {value: 0x0808, lo: 0x80, hi: 0x80}, {value: 0x3308, lo: 0x81, hi: 0x83}, {value: 0x0040, lo: 0x84, hi: 0x84}, {value: 0x3308, lo: 0x85, hi: 0x86}, {value: 0x0040, lo: 0x87, hi: 0x8b}, {value: 0x3308, lo: 0x8c, hi: 0x8f}, {value: 0x0808, lo: 0x90, hi: 0x93}, {value: 0x0040, lo: 0x94, hi: 0x94}, {value: 0x0808, lo: 0x95, hi: 0x97}, {value: 0x0040, lo: 0x98, hi: 0x98}, {value: 0x0808, lo: 0x99, hi: 0xb5}, {value: 0x0040, lo: 0xb6, hi: 0xb7}, {value: 0x3308, lo: 0xb8, hi: 0xba}, {value: 0x0040, lo: 0xbb, hi: 0xbe}, {value: 0x3b08, lo: 0xbf, hi: 0xbf}, // Block 0xa0, offset 0x4db {value: 0x0000, lo: 0x06}, {value: 0x0818, lo: 0x80, hi: 0x88}, {value: 0x0040, lo: 0x89, hi: 0x8f}, {value: 0x0818, lo: 0x90, hi: 0x98}, {value: 0x0040, lo: 0x99, hi: 0x9f}, {value: 0x0808, lo: 0xa0, hi: 0xbc}, {value: 0x0818, lo: 0xbd, hi: 0xbf}, // Block 0xa1, offset 0x4e2 {value: 0x0000, lo: 0x03}, {value: 0x0808, lo: 0x80, hi: 0x9c}, {value: 0x0818, lo: 0x9d, hi: 0x9f}, {value: 0x0040, lo: 0xa0, hi: 0xbf}, // Block 0xa2, offset 0x4e6 {value: 0x0000, lo: 0x03}, {value: 0x0808, lo: 0x80, hi: 0xb5}, {value: 0x0040, lo: 0xb6, hi: 0xb8}, {value: 0x0018, lo: 0xb9, hi: 0xbf}, // Block 0xa3, offset 0x4ea {value: 0x0000, lo: 0x06}, {value: 0x0808, lo: 0x80, hi: 0x95}, {value: 0x0040, lo: 0x96, hi: 0x97}, {value: 0x0818, lo: 0x98, hi: 0x9f}, {value: 0x0808, lo: 0xa0, hi: 0xb2}, {value: 0x0040, lo: 0xb3, hi: 0xb7}, {value: 0x0818, lo: 0xb8, hi: 0xbf}, // Block 0xa4, offset 0x4f1 {value: 0x0000, lo: 0x01}, {value: 0x0808, lo: 0x80, hi: 0xbf}, // Block 0xa5, offset 0x4f3 {value: 0x0000, lo: 0x02}, {value: 0x0808, lo: 0x80, hi: 0x88}, {value: 0x0040, lo: 0x89, hi: 0xbf}, // Block 0xa6, offset 0x4f6 {value: 0x0000, lo: 0x02}, {value: 0x03dd, lo: 0x80, hi: 0xb2}, {value: 0x0040, lo: 0xb3, hi: 0xbf}, // Block 0xa7, offset 0x4f9 {value: 0x0000, lo: 0x03}, {value: 0x0808, lo: 0x80, hi: 0xb2}, {value: 0x0040, lo: 0xb3, hi: 0xb9}, {value: 0x0818, lo: 0xba, hi: 0xbf}, // Block 0xa8, offset 0x4fd {value: 0x0000, lo: 0x08}, {value: 0x0908, lo: 0x80, hi: 0x80}, {value: 0x0a08, lo: 0x81, hi: 0xa1}, {value: 0x0c08, lo: 0xa2, hi: 0xa2}, {value: 0x0a08, lo: 0xa3, hi: 0xa3}, {value: 0x3308, lo: 0xa4, hi: 0xa7}, {value: 0x0040, lo: 0xa8, hi: 0xaf}, {value: 0x0808, lo: 0xb0, hi: 0xb9}, {value: 0x0040, lo: 0xba, hi: 0xbf}, // Block 0xa9, offset 0x506 {value: 0x0000, lo: 0x03}, {value: 0x0040, lo: 0x80, hi: 0x9f}, {value: 0x0818, lo: 0xa0, hi: 0xbe}, {value: 0x0040, lo: 0xbf, hi: 0xbf}, // Block 0xaa, offset 0x50a {value: 0x0000, lo: 0x07}, {value: 0x0808, lo: 0x80, hi: 0x9c}, {value: 0x0818, lo: 0x9d, hi: 0xa6}, {value: 0x0808, lo: 0xa7, hi: 0xa7}, {value: 0x0040, lo: 0xa8, hi: 0xaf}, {value: 0x0a08, lo: 0xb0, hi: 0xb2}, {value: 0x0c08, lo: 0xb3, hi: 0xb3}, {value: 0x0a08, lo: 0xb4, hi: 0xbf}, // Block 0xab, offset 0x512 {value: 0x0000, lo: 0x07}, {value: 0x0a08, lo: 0x80, hi: 0x84}, {value: 0x0808, lo: 0x85, hi: 0x85}, {value: 0x3308, lo: 0x86, hi: 0x90}, {value: 0x0a18, lo: 0x91, hi: 0x93}, {value: 0x0c18, lo: 0x94, hi: 0x94}, {value: 0x0818, lo: 0x95, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0xbf}, // Block 0xac, offset 0x51a {value: 0x0000, lo: 0x03}, {value: 0x0040, lo: 0x80, hi: 0x9f}, {value: 0x0808, lo: 0xa0, hi: 0xb6}, {value: 0x0040, lo: 0xb7, hi: 0xbf}, // Block 0xad, offset 0x51e {value: 0x0000, lo: 0x05}, {value: 0x3008, lo: 0x80, hi: 0x80}, {value: 0x3308, lo: 0x81, hi: 0x81}, {value: 0x3008, lo: 0x82, hi: 0x82}, {value: 0x0008, lo: 0x83, hi: 0xb7}, {value: 0x3308, lo: 0xb8, hi: 0xbf}, // Block 0xae, offset 0x524 {value: 0x0000, lo: 0x08}, {value: 0x3308, lo: 0x80, hi: 0x85}, {value: 0x3b08, lo: 0x86, hi: 0x86}, {value: 0x0018, lo: 0x87, hi: 0x8d}, {value: 0x0040, lo: 0x8e, hi: 0x91}, {value: 0x0018, lo: 0x92, hi: 0xa5}, {value: 0x0008, lo: 0xa6, hi: 0xaf}, {value: 0x0040, lo: 0xb0, hi: 0xbe}, {value: 0x3b08, lo: 0xbf, hi: 0xbf}, // Block 0xaf, offset 0x52d {value: 0x0000, lo: 0x0b}, {value: 0x3308, lo: 0x80, hi: 0x81}, {value: 0x3008, lo: 0x82, hi: 0x82}, {value: 0x0008, lo: 0x83, hi: 0xaf}, {value: 0x3008, lo: 0xb0, hi: 0xb2}, {value: 0x3308, lo: 0xb3, hi: 0xb6}, {value: 0x3008, lo: 0xb7, hi: 0xb8}, {value: 0x3b08, lo: 0xb9, hi: 0xb9}, {value: 0x3308, lo: 0xba, hi: 0xba}, {value: 0x0018, lo: 0xbb, hi: 0xbc}, {value: 0x0040, lo: 0xbd, hi: 0xbd}, {value: 0x0018, lo: 0xbe, hi: 0xbf}, // Block 0xb0, offset 0x539 {value: 0x0000, lo: 0x06}, {value: 0x0018, lo: 0x80, hi: 0x81}, {value: 0x0040, lo: 0x82, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0xa8}, {value: 0x0040, lo: 0xa9, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xb9}, {value: 0x0040, lo: 0xba, hi: 0xbf}, // Block 0xb1, offset 0x540 {value: 0x0000, lo: 0x08}, {value: 0x3308, lo: 0x80, hi: 0x82}, {value: 0x0008, lo: 0x83, hi: 0xa6}, {value: 0x3308, lo: 0xa7, hi: 0xab}, {value: 0x3008, lo: 0xac, hi: 0xac}, {value: 0x3308, lo: 0xad, hi: 0xb2}, {value: 0x3b08, lo: 0xb3, hi: 0xb4}, {value: 0x0040, lo: 0xb5, hi: 0xb5}, {value: 0x0008, lo: 0xb6, hi: 0xbf}, // Block 0xb2, offset 0x549 {value: 0x0000, lo: 0x09}, {value: 0x0018, lo: 0x80, hi: 0x83}, {value: 0x0008, lo: 0x84, hi: 0x84}, {value: 0x3008, lo: 0x85, hi: 0x86}, {value: 0x0040, lo: 0x87, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0xb2}, {value: 0x3308, lo: 0xb3, hi: 0xb3}, {value: 0x0018, lo: 0xb4, hi: 0xb5}, {value: 0x0008, lo: 0xb6, hi: 0xb6}, {value: 0x0040, lo: 0xb7, hi: 0xbf}, // Block 0xb3, offset 0x553 {value: 0x0000, lo: 0x06}, {value: 0x3308, lo: 0x80, hi: 0x81}, {value: 0x3008, lo: 0x82, hi: 0x82}, {value: 0x0008, lo: 0x83, hi: 0xb2}, {value: 0x3008, lo: 0xb3, hi: 0xb5}, {value: 0x3308, lo: 0xb6, hi: 0xbe}, {value: 0x3008, lo: 0xbf, hi: 0xbf}, // Block 0xb4, offset 0x55a {value: 0x0000, lo: 0x0d}, {value: 0x3808, lo: 0x80, hi: 0x80}, {value: 0x0008, lo: 0x81, hi: 0x84}, {value: 0x0018, lo: 0x85, hi: 0x88}, {value: 0x3308, lo: 0x89, hi: 0x8c}, {value: 0x0018, lo: 0x8d, hi: 0x8d}, {value: 0x0040, lo: 0x8e, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x9a}, {value: 0x0018, lo: 0x9b, hi: 0x9b}, {value: 0x0008, lo: 0x9c, hi: 0x9c}, {value: 0x0018, lo: 0x9d, hi: 0x9f}, {value: 0x0040, lo: 0xa0, hi: 0xa0}, {value: 0x0018, lo: 0xa1, hi: 0xb4}, {value: 0x0040, lo: 0xb5, hi: 0xbf}, // Block 0xb5, offset 0x568 {value: 0x0000, lo: 0x0c}, {value: 0x0008, lo: 0x80, hi: 0x91}, {value: 0x0040, lo: 0x92, hi: 0x92}, {value: 0x0008, lo: 0x93, hi: 0xab}, {value: 0x3008, lo: 0xac, hi: 0xae}, {value: 0x3308, lo: 0xaf, hi: 0xb1}, {value: 0x3008, lo: 0xb2, hi: 0xb3}, {value: 0x3308, lo: 0xb4, hi: 0xb4}, {value: 0x3808, lo: 0xb5, hi: 0xb5}, {value: 0x3308, lo: 0xb6, hi: 0xb7}, {value: 0x0018, lo: 0xb8, hi: 0xbd}, {value: 0x3308, lo: 0xbe, hi: 0xbe}, {value: 0x0040, lo: 0xbf, hi: 0xbf}, // Block 0xb6, offset 0x575 {value: 0x0000, lo: 0x0c}, {value: 0x0008, lo: 0x80, hi: 0x86}, {value: 0x0040, lo: 0x87, hi: 0x87}, {value: 0x0008, lo: 0x88, hi: 0x88}, {value: 0x0040, lo: 0x89, hi: 0x89}, {value: 0x0008, lo: 0x8a, hi: 0x8d}, {value: 0x0040, lo: 0x8e, hi: 0x8e}, {value: 0x0008, lo: 0x8f, hi: 0x9d}, {value: 0x0040, lo: 0x9e, hi: 0x9e}, {value: 0x0008, lo: 0x9f, hi: 0xa8}, {value: 0x0018, lo: 0xa9, hi: 0xa9}, {value: 0x0040, lo: 0xaa, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xbf}, // Block 0xb7, offset 0x582 {value: 0x0000, lo: 0x08}, {value: 0x0008, lo: 0x80, hi: 0x9e}, {value: 0x3308, lo: 0x9f, hi: 0x9f}, {value: 0x3008, lo: 0xa0, hi: 0xa2}, {value: 0x3308, lo: 0xa3, hi: 0xa9}, {value: 0x3b08, lo: 0xaa, hi: 0xaa}, {value: 0x0040, lo: 0xab, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xb9}, {value: 0x0040, lo: 0xba, hi: 0xbf}, // Block 0xb8, offset 0x58b {value: 0x0000, lo: 0x03}, {value: 0x0008, lo: 0x80, hi: 0xb4}, {value: 0x3008, lo: 0xb5, hi: 0xb7}, {value: 0x3308, lo: 0xb8, hi: 0xbf}, // Block 0xb9, offset 0x58f {value: 0x0000, lo: 0x0f}, {value: 0x3008, lo: 0x80, hi: 0x81}, {value: 0x3b08, lo: 0x82, hi: 0x82}, {value: 0x3308, lo: 0x83, hi: 0x84}, {value: 0x3008, lo: 0x85, hi: 0x85}, {value: 0x3308, lo: 0x86, hi: 0x86}, {value: 0x0008, lo: 0x87, hi: 0x8a}, {value: 0x0018, lo: 0x8b, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9a}, {value: 0x0018, lo: 0x9b, hi: 0x9b}, {value: 0x0040, lo: 0x9c, hi: 0x9c}, {value: 0x0018, lo: 0x9d, hi: 0x9d}, {value: 0x3308, lo: 0x9e, hi: 0x9e}, {value: 0x0008, lo: 0x9f, hi: 0x9f}, {value: 0x0040, lo: 0xa0, hi: 0xbf}, // Block 0xba, offset 0x59f {value: 0x0000, lo: 0x07}, {value: 0x0008, lo: 0x80, hi: 0xaf}, {value: 0x3008, lo: 0xb0, hi: 0xb2}, {value: 0x3308, lo: 0xb3, hi: 0xb8}, {value: 0x3008, lo: 0xb9, hi: 0xb9}, {value: 0x3308, lo: 0xba, hi: 0xba}, {value: 0x3008, lo: 0xbb, hi: 0xbe}, {value: 0x3308, lo: 0xbf, hi: 0xbf}, // Block 0xbb, offset 0x5a7 {value: 0x0000, lo: 0x0a}, {value: 0x3308, lo: 0x80, hi: 0x80}, {value: 0x3008, lo: 0x81, hi: 0x81}, {value: 0x3b08, lo: 0x82, hi: 0x82}, {value: 0x3308, lo: 0x83, hi: 0x83}, {value: 0x0008, lo: 0x84, hi: 0x85}, {value: 0x0018, lo: 0x86, hi: 0x86}, {value: 0x0008, lo: 0x87, hi: 0x87}, {value: 0x0040, lo: 0x88, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0xbf}, // Block 0xbc, offset 0x5b2 {value: 0x0000, lo: 0x08}, {value: 0x0008, lo: 0x80, hi: 0xae}, {value: 0x3008, lo: 0xaf, hi: 0xb1}, {value: 0x3308, lo: 0xb2, hi: 0xb5}, {value: 0x0040, lo: 0xb6, hi: 0xb7}, {value: 0x3008, lo: 0xb8, hi: 0xbb}, {value: 0x3308, lo: 0xbc, hi: 0xbd}, {value: 0x3008, lo: 0xbe, hi: 0xbe}, {value: 0x3b08, lo: 0xbf, hi: 0xbf}, // Block 0xbd, offset 0x5bb {value: 0x0000, lo: 0x05}, {value: 0x3308, lo: 0x80, hi: 0x80}, {value: 0x0018, lo: 0x81, hi: 0x97}, {value: 0x0008, lo: 0x98, hi: 0x9b}, {value: 0x3308, lo: 0x9c, hi: 0x9d}, {value: 0x0040, lo: 0x9e, hi: 0xbf}, // Block 0xbe, offset 0x5c1 {value: 0x0000, lo: 0x07}, {value: 0x0008, lo: 0x80, hi: 0xaf}, {value: 0x3008, lo: 0xb0, hi: 0xb2}, {value: 0x3308, lo: 0xb3, hi: 0xba}, {value: 0x3008, lo: 0xbb, hi: 0xbc}, {value: 0x3308, lo: 0xbd, hi: 0xbd}, {value: 0x3008, lo: 0xbe, hi: 0xbe}, {value: 0x3b08, lo: 0xbf, hi: 0xbf}, // Block 0xbf, offset 0x5c9 {value: 0x0000, lo: 0x08}, {value: 0x3308, lo: 0x80, hi: 0x80}, {value: 0x0018, lo: 0x81, hi: 0x83}, {value: 0x0008, lo: 0x84, hi: 0x84}, {value: 0x0040, lo: 0x85, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xac}, {value: 0x0040, lo: 0xad, hi: 0xbf}, // Block 0xc0, offset 0x5d2 {value: 0x0000, lo: 0x0a}, {value: 0x0008, lo: 0x80, hi: 0xaa}, {value: 0x3308, lo: 0xab, hi: 0xab}, {value: 0x3008, lo: 0xac, hi: 0xac}, {value: 0x3308, lo: 0xad, hi: 0xad}, {value: 0x3008, lo: 0xae, hi: 0xaf}, {value: 0x3308, lo: 0xb0, hi: 0xb5}, {value: 0x3808, lo: 0xb6, hi: 0xb6}, {value: 0x3308, lo: 0xb7, hi: 0xb7}, {value: 0x0008, lo: 0xb8, hi: 0xb8}, {value: 0x0040, lo: 0xb9, hi: 0xbf}, // Block 0xc1, offset 0x5dd {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0x89}, {value: 0x0040, lo: 0x8a, hi: 0xbf}, // Block 0xc2, offset 0x5e0 {value: 0x0000, lo: 0x0b}, {value: 0x0008, lo: 0x80, hi: 0x9a}, {value: 0x0040, lo: 0x9b, hi: 0x9c}, {value: 0x3308, lo: 0x9d, hi: 0x9f}, {value: 0x3008, lo: 0xa0, hi: 0xa1}, {value: 0x3308, lo: 0xa2, hi: 0xa5}, {value: 0x3008, lo: 0xa6, hi: 0xa6}, {value: 0x3308, lo: 0xa7, hi: 0xaa}, {value: 0x3b08, lo: 0xab, hi: 0xab}, {value: 0x0040, lo: 0xac, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xb9}, {value: 0x0018, lo: 0xba, hi: 0xbf}, // Block 0xc3, offset 0x5ec {value: 0x0000, lo: 0x08}, {value: 0x0008, lo: 0x80, hi: 0xab}, {value: 0x3008, lo: 0xac, hi: 0xae}, {value: 0x3308, lo: 0xaf, hi: 0xb7}, {value: 0x3008, lo: 0xb8, hi: 0xb8}, {value: 0x3b08, lo: 0xb9, hi: 0xb9}, {value: 0x3308, lo: 0xba, hi: 0xba}, {value: 0x0018, lo: 0xbb, hi: 0xbb}, {value: 0x0040, lo: 0xbc, hi: 0xbf}, // Block 0xc4, offset 0x5f5 {value: 0x0000, lo: 0x02}, {value: 0x0040, lo: 0x80, hi: 0x9f}, {value: 0x049d, lo: 0xa0, hi: 0xbf}, // Block 0xc5, offset 0x5f8 {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0xa9}, {value: 0x0018, lo: 0xaa, hi: 0xb2}, {value: 0x0040, lo: 0xb3, hi: 0xbe}, {value: 0x0008, lo: 0xbf, hi: 0xbf}, // Block 0xc6, offset 0x5fd {value: 0x0000, lo: 0x04}, {value: 0x0040, lo: 0x80, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa7}, {value: 0x0040, lo: 0xa8, hi: 0xa9}, {value: 0x0008, lo: 0xaa, hi: 0xbf}, // Block 0xc7, offset 0x602 {value: 0x0000, lo: 0x0c}, {value: 0x0008, lo: 0x80, hi: 0x90}, {value: 0x3008, lo: 0x91, hi: 0x93}, {value: 0x3308, lo: 0x94, hi: 0x97}, {value: 0x0040, lo: 0x98, hi: 0x99}, {value: 0x3308, lo: 0x9a, hi: 0x9b}, {value: 0x3008, lo: 0x9c, hi: 0x9f}, {value: 0x3b08, lo: 0xa0, hi: 0xa0}, {value: 0x0008, lo: 0xa1, hi: 0xa1}, {value: 0x0018, lo: 0xa2, hi: 0xa2}, {value: 0x0008, lo: 0xa3, hi: 0xa3}, {value: 0x3008, lo: 0xa4, hi: 0xa4}, {value: 0x0040, lo: 0xa5, hi: 0xbf}, // Block 0xc8, offset 0x60f {value: 0x0000, lo: 0x0a}, {value: 0x0008, lo: 0x80, hi: 0x80}, {value: 0x3308, lo: 0x81, hi: 0x8a}, {value: 0x0008, lo: 0x8b, hi: 0xb2}, {value: 0x3308, lo: 0xb3, hi: 0xb3}, {value: 0x3b08, lo: 0xb4, hi: 0xb4}, {value: 0x3308, lo: 0xb5, hi: 0xb8}, {value: 0x3008, lo: 0xb9, hi: 0xb9}, {value: 0x0008, lo: 0xba, hi: 0xba}, {value: 0x3308, lo: 0xbb, hi: 0xbe}, {value: 0x0018, lo: 0xbf, hi: 0xbf}, // Block 0xc9, offset 0x61a {value: 0x0000, lo: 0x08}, {value: 0x0018, lo: 0x80, hi: 0x86}, {value: 0x3b08, lo: 0x87, hi: 0x87}, {value: 0x0040, lo: 0x88, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x90}, {value: 0x3308, lo: 0x91, hi: 0x96}, {value: 0x3008, lo: 0x97, hi: 0x98}, {value: 0x3308, lo: 0x99, hi: 0x9b}, {value: 0x0008, lo: 0x9c, hi: 0xbf}, // Block 0xca, offset 0x623 {value: 0x0000, lo: 0x09}, {value: 0x0008, lo: 0x80, hi: 0x89}, {value: 0x3308, lo: 0x8a, hi: 0x96}, {value: 0x3008, lo: 0x97, hi: 0x97}, {value: 0x3308, lo: 0x98, hi: 0x98}, {value: 0x3b08, lo: 0x99, hi: 0x99}, {value: 0x0018, lo: 0x9a, hi: 0x9c}, {value: 0x0008, lo: 0x9d, hi: 0x9d}, {value: 0x0018, lo: 0x9e, hi: 0xa2}, {value: 0x0040, lo: 0xa3, hi: 0xbf}, // Block 0xcb, offset 0x62d {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0xb8}, {value: 0x0040, lo: 0xb9, hi: 0xbf}, // Block 0xcc, offset 0x630 {value: 0x0000, lo: 0x09}, {value: 0x0008, lo: 0x80, hi: 0x88}, {value: 0x0040, lo: 0x89, hi: 0x89}, {value: 0x0008, lo: 0x8a, hi: 0xae}, {value: 0x3008, lo: 0xaf, hi: 0xaf}, {value: 0x3308, lo: 0xb0, hi: 0xb6}, {value: 0x0040, lo: 0xb7, hi: 0xb7}, {value: 0x3308, lo: 0xb8, hi: 0xbd}, {value: 0x3008, lo: 0xbe, hi: 0xbe}, {value: 0x3b08, lo: 0xbf, hi: 0xbf}, // Block 0xcd, offset 0x63a {value: 0x0000, lo: 0x08}, {value: 0x0008, lo: 0x80, hi: 0x80}, {value: 0x0018, lo: 0x81, hi: 0x85}, {value: 0x0040, lo: 0x86, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0018, lo: 0x9a, hi: 0xac}, {value: 0x0040, lo: 0xad, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xb1}, {value: 0x0008, lo: 0xb2, hi: 0xbf}, // Block 0xce, offset 0x643 {value: 0x0000, lo: 0x0b}, {value: 0x0008, lo: 0x80, hi: 0x8f}, {value: 0x0040, lo: 0x90, hi: 0x91}, {value: 0x3308, lo: 0x92, hi: 0xa7}, {value: 0x0040, lo: 0xa8, hi: 0xa8}, {value: 0x3008, lo: 0xa9, hi: 0xa9}, {value: 0x3308, lo: 0xaa, hi: 0xb0}, {value: 0x3008, lo: 0xb1, hi: 0xb1}, {value: 0x3308, lo: 0xb2, hi: 0xb3}, {value: 0x3008, lo: 0xb4, hi: 0xb4}, {value: 0x3308, lo: 0xb5, hi: 0xb6}, {value: 0x0040, lo: 0xb7, hi: 0xbf}, // Block 0xcf, offset 0x64f {value: 0x0000, lo: 0x0c}, {value: 0x0008, lo: 0x80, hi: 0x86}, {value: 0x0040, lo: 0x87, hi: 0x87}, {value: 0x0008, lo: 0x88, hi: 0x89}, {value: 0x0040, lo: 0x8a, hi: 0x8a}, {value: 0x0008, lo: 0x8b, hi: 0xb0}, {value: 0x3308, lo: 0xb1, hi: 0xb6}, {value: 0x0040, lo: 0xb7, hi: 0xb9}, {value: 0x3308, lo: 0xba, hi: 0xba}, {value: 0x0040, lo: 0xbb, hi: 0xbb}, {value: 0x3308, lo: 0xbc, hi: 0xbd}, {value: 0x0040, lo: 0xbe, hi: 0xbe}, {value: 0x3308, lo: 0xbf, hi: 0xbf}, // Block 0xd0, offset 0x65c {value: 0x0000, lo: 0x0c}, {value: 0x3308, lo: 0x80, hi: 0x83}, {value: 0x3b08, lo: 0x84, hi: 0x85}, {value: 0x0008, lo: 0x86, hi: 0x86}, {value: 0x3308, lo: 0x87, hi: 0x87}, {value: 0x0040, lo: 0x88, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa5}, {value: 0x0040, lo: 0xa6, hi: 0xa6}, {value: 0x0008, lo: 0xa7, hi: 0xa8}, {value: 0x0040, lo: 0xa9, hi: 0xa9}, {value: 0x0008, lo: 0xaa, hi: 0xbf}, // Block 0xd1, offset 0x669 {value: 0x0000, lo: 0x0d}, {value: 0x0008, lo: 0x80, hi: 0x89}, {value: 0x3008, lo: 0x8a, hi: 0x8e}, {value: 0x0040, lo: 0x8f, hi: 0x8f}, {value: 0x3308, lo: 0x90, hi: 0x91}, {value: 0x0040, lo: 0x92, hi: 0x92}, {value: 0x3008, lo: 0x93, hi: 0x94}, {value: 0x3308, lo: 0x95, hi: 0x95}, {value: 0x3008, lo: 0x96, hi: 0x96}, {value: 0x3b08, lo: 0x97, hi: 0x97}, {value: 0x0008, lo: 0x98, hi: 0x98}, {value: 0x0040, lo: 0x99, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa9}, {value: 0x0040, lo: 0xaa, hi: 0xbf}, // Block 0xd2, offset 0x677 {value: 0x0000, lo: 0x06}, {value: 0x0040, lo: 0x80, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xb2}, {value: 0x3308, lo: 0xb3, hi: 0xb4}, {value: 0x3008, lo: 0xb5, hi: 0xb6}, {value: 0x0018, lo: 0xb7, hi: 0xb8}, {value: 0x0040, lo: 0xb9, hi: 0xbf}, // Block 0xd3, offset 0x67e {value: 0x0000, lo: 0x03}, {value: 0x0018, lo: 0x80, hi: 0xb1}, {value: 0x0040, lo: 0xb2, hi: 0xbe}, {value: 0x0018, lo: 0xbf, hi: 0xbf}, // Block 0xd4, offset 0x682 {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0xbf}, // Block 0xd5, offset 0x685 {value: 0x0000, lo: 0x04}, {value: 0x0018, lo: 0x80, hi: 0xae}, {value: 0x0040, lo: 0xaf, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xb4}, {value: 0x0040, lo: 0xb5, hi: 0xbf}, // Block 0xd6, offset 0x68a {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0x83}, {value: 0x0040, lo: 0x84, hi: 0xbf}, // Block 0xd7, offset 0x68d {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0xae}, {value: 0x0040, lo: 0xaf, hi: 0xaf}, {value: 0x0340, lo: 0xb0, hi: 0xb8}, {value: 0x0040, lo: 0xb9, hi: 0xbf}, // Block 0xd8, offset 0x692 {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0x86}, {value: 0x0040, lo: 0x87, hi: 0xbf}, // Block 0xd9, offset 0x695 {value: 0x0000, lo: 0x06}, {value: 0x0008, lo: 0x80, hi: 0x9e}, {value: 0x0040, lo: 0x9f, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa9}, {value: 0x0040, lo: 0xaa, hi: 0xad}, {value: 0x0018, lo: 0xae, hi: 0xaf}, {value: 0x0040, lo: 0xb0, hi: 0xbf}, // Block 0xda, offset 0x69c {value: 0x0000, lo: 0x06}, {value: 0x0040, lo: 0x80, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0xad}, {value: 0x0040, lo: 0xae, hi: 0xaf}, {value: 0x3308, lo: 0xb0, hi: 0xb4}, {value: 0x0018, lo: 0xb5, hi: 0xb5}, {value: 0x0040, lo: 0xb6, hi: 0xbf}, // Block 0xdb, offset 0x6a3 {value: 0x0000, lo: 0x03}, {value: 0x0008, lo: 0x80, hi: 0xaf}, {value: 0x3308, lo: 0xb0, hi: 0xb6}, {value: 0x0018, lo: 0xb7, hi: 0xbf}, // Block 0xdc, offset 0x6a7 {value: 0x0000, lo: 0x0a}, {value: 0x0008, lo: 0x80, hi: 0x83}, {value: 0x0018, lo: 0x84, hi: 0x85}, {value: 0x0040, lo: 0x86, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9a}, {value: 0x0018, lo: 0x9b, hi: 0xa1}, {value: 0x0040, lo: 0xa2, hi: 0xa2}, {value: 0x0008, lo: 0xa3, hi: 0xb7}, {value: 0x0040, lo: 0xb8, hi: 0xbc}, {value: 0x0008, lo: 0xbd, hi: 0xbf}, // Block 0xdd, offset 0x6b2 {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0x8f}, {value: 0x0040, lo: 0x90, hi: 0xbf}, // Block 0xde, offset 0x6b5 {value: 0x0000, lo: 0x02}, {value: 0xe105, lo: 0x80, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xbf}, // Block 0xdf, offset 0x6b8 {value: 0x0000, lo: 0x02}, {value: 0x0018, lo: 0x80, hi: 0x9a}, {value: 0x0040, lo: 0x9b, hi: 0xbf}, // Block 0xe0, offset 0x6bb {value: 0x0000, lo: 0x05}, {value: 0x0008, lo: 0x80, hi: 0x8a}, {value: 0x0040, lo: 0x8b, hi: 0x8e}, {value: 0x3308, lo: 0x8f, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x90}, {value: 0x3008, lo: 0x91, hi: 0xbf}, // Block 0xe1, offset 0x6c1 {value: 0x0000, lo: 0x05}, {value: 0x3008, lo: 0x80, hi: 0x87}, {value: 0x0040, lo: 0x88, hi: 0x8e}, {value: 0x3308, lo: 0x8f, hi: 0x92}, {value: 0x0008, lo: 0x93, hi: 0x9f}, {value: 0x0040, lo: 0xa0, hi: 0xbf}, // Block 0xe2, offset 0x6c7 {value: 0x0000, lo: 0x05}, {value: 0x0040, lo: 0x80, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xa1}, {value: 0x0018, lo: 0xa2, hi: 0xa2}, {value: 0x0008, lo: 0xa3, hi: 0xa3}, {value: 0x0040, lo: 0xa4, hi: 0xbf}, // Block 0xe3, offset 0x6cd {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0xb7}, {value: 0x0040, lo: 0xb8, hi: 0xbf}, // Block 0xe4, offset 0x6d0 {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0xb2}, {value: 0x0040, lo: 0xb3, hi: 0xbf}, // Block 0xe5, offset 0x6d3 {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0x9e}, {value: 0x0040, lo: 0x9f, hi: 0xbf}, // Block 0xe6, offset 0x6d6 {value: 0x0000, lo: 0x06}, {value: 0x0040, lo: 0x80, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x92}, {value: 0x0040, lo: 0x93, hi: 0xa3}, {value: 0x0008, lo: 0xa4, hi: 0xa7}, {value: 0x0040, lo: 0xa8, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xbf}, // Block 0xe7, offset 0x6dd {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0xbb}, {value: 0x0040, lo: 0xbc, hi: 0xbf}, // Block 0xe8, offset 0x6e0 {value: 0x0000, lo: 0x04}, {value: 0x0008, lo: 0x80, hi: 0xaa}, {value: 0x0040, lo: 0xab, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xbc}, {value: 0x0040, lo: 0xbd, hi: 0xbf}, // Block 0xe9, offset 0x6e5 {value: 0x0000, lo: 0x09}, {value: 0x0008, lo: 0x80, hi: 0x88}, {value: 0x0040, lo: 0x89, hi: 0x8f}, {value: 0x0008, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9b}, {value: 0x0018, lo: 0x9c, hi: 0x9c}, {value: 0x3308, lo: 0x9d, hi: 0x9e}, {value: 0x0018, lo: 0x9f, hi: 0x9f}, {value: 0x03c0, lo: 0xa0, hi: 0xa3}, {value: 0x0040, lo: 0xa4, hi: 0xbf}, // Block 0xea, offset 0x6ef {value: 0x0000, lo: 0x02}, {value: 0x0018, lo: 0x80, hi: 0xb5}, {value: 0x0040, lo: 0xb6, hi: 0xbf}, // Block 0xeb, offset 0x6f2 {value: 0x0000, lo: 0x03}, {value: 0x0018, lo: 0x80, hi: 0xa6}, {value: 0x0040, lo: 0xa7, hi: 0xa8}, {value: 0x0018, lo: 0xa9, hi: 0xbf}, // Block 0xec, offset 0x6f6 {value: 0x0000, lo: 0x0e}, {value: 0x0018, lo: 0x80, hi: 0x9d}, {value: 0xb5b9, lo: 0x9e, hi: 0x9e}, {value: 0xb601, lo: 0x9f, hi: 0x9f}, {value: 0xb649, lo: 0xa0, hi: 0xa0}, {value: 0xb6b1, lo: 0xa1, hi: 0xa1}, {value: 0xb719, lo: 0xa2, hi: 0xa2}, {value: 0xb781, lo: 0xa3, hi: 0xa3}, {value: 0xb7e9, lo: 0xa4, hi: 0xa4}, {value: 0x3018, lo: 0xa5, hi: 0xa6}, {value: 0x3318, lo: 0xa7, hi: 0xa9}, {value: 0x0018, lo: 0xaa, hi: 0xac}, {value: 0x3018, lo: 0xad, hi: 0xb2}, {value: 0x0340, lo: 0xb3, hi: 0xba}, {value: 0x3318, lo: 0xbb, hi: 0xbf}, // Block 0xed, offset 0x705 {value: 0x0000, lo: 0x0b}, {value: 0x3318, lo: 0x80, hi: 0x82}, {value: 0x0018, lo: 0x83, hi: 0x84}, {value: 0x3318, lo: 0x85, hi: 0x8b}, {value: 0x0018, lo: 0x8c, hi: 0xa9}, {value: 0x3318, lo: 0xaa, hi: 0xad}, {value: 0x0018, lo: 0xae, hi: 0xba}, {value: 0xb851, lo: 0xbb, hi: 0xbb}, {value: 0xb899, lo: 0xbc, hi: 0xbc}, {value: 0xb8e1, lo: 0xbd, hi: 0xbd}, {value: 0xb949, lo: 0xbe, hi: 0xbe}, {value: 0xb9b1, lo: 0xbf, hi: 0xbf}, // Block 0xee, offset 0x711 {value: 0x0000, lo: 0x03}, {value: 0xba19, lo: 0x80, hi: 0x80}, {value: 0x0018, lo: 0x81, hi: 0xa8}, {value: 0x0040, lo: 0xa9, hi: 0xbf}, // Block 0xef, offset 0x715 {value: 0x0000, lo: 0x04}, {value: 0x0018, lo: 0x80, hi: 0x81}, {value: 0x3318, lo: 0x82, hi: 0x84}, {value: 0x0018, lo: 0x85, hi: 0x85}, {value: 0x0040, lo: 0x86, hi: 0xbf}, // Block 0xf0, offset 0x71a {value: 0x0000, lo: 0x03}, {value: 0x0040, lo: 0x80, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xb3}, {value: 0x0040, lo: 0xb4, hi: 0xbf}, // Block 0xf1, offset 0x71e {value: 0x0000, lo: 0x04}, {value: 0x0018, lo: 0x80, hi: 0x96}, {value: 0x0040, lo: 0x97, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xb8}, {value: 0x0040, lo: 0xb9, hi: 0xbf}, // Block 0xf2, offset 0x723 {value: 0x0000, lo: 0x03}, {value: 0x3308, lo: 0x80, hi: 0xb6}, {value: 0x0018, lo: 0xb7, hi: 0xba}, {value: 0x3308, lo: 0xbb, hi: 0xbf}, // Block 0xf3, offset 0x727 {value: 0x0000, lo: 0x04}, {value: 0x3308, lo: 0x80, hi: 0xac}, {value: 0x0018, lo: 0xad, hi: 0xb4}, {value: 0x3308, lo: 0xb5, hi: 0xb5}, {value: 0x0018, lo: 0xb6, hi: 0xbf}, // Block 0xf4, offset 0x72c {value: 0x0000, lo: 0x08}, {value: 0x0018, lo: 0x80, hi: 0x83}, {value: 0x3308, lo: 0x84, hi: 0x84}, {value: 0x0018, lo: 0x85, hi: 0x8b}, {value: 0x0040, lo: 0x8c, hi: 0x9a}, {value: 0x3308, lo: 0x9b, hi: 0x9f}, {value: 0x0040, lo: 0xa0, hi: 0xa0}, {value: 0x3308, lo: 0xa1, hi: 0xaf}, {value: 0x0040, lo: 0xb0, hi: 0xbf}, // Block 0xf5, offset 0x735 {value: 0x0000, lo: 0x0a}, {value: 0x3308, lo: 0x80, hi: 0x86}, {value: 0x0040, lo: 0x87, hi: 0x87}, {value: 0x3308, lo: 0x88, hi: 0x98}, {value: 0x0040, lo: 0x99, hi: 0x9a}, {value: 0x3308, lo: 0x9b, hi: 0xa1}, {value: 0x0040, lo: 0xa2, hi: 0xa2}, {value: 0x3308, lo: 0xa3, hi: 0xa4}, {value: 0x0040, lo: 0xa5, hi: 0xa5}, {value: 0x3308, lo: 0xa6, hi: 0xaa}, {value: 0x0040, lo: 0xab, hi: 0xbf}, // Block 0xf6, offset 0x740 {value: 0x0000, lo: 0x05}, {value: 0x0008, lo: 0x80, hi: 0xac}, {value: 0x0040, lo: 0xad, hi: 0xaf}, {value: 0x3308, lo: 0xb0, hi: 0xb6}, {value: 0x0008, lo: 0xb7, hi: 0xbd}, {value: 0x0040, lo: 0xbe, hi: 0xbf}, // Block 0xf7, offset 0x746 {value: 0x0000, lo: 0x05}, {value: 0x0008, lo: 0x80, hi: 0x89}, {value: 0x0040, lo: 0x8a, hi: 0x8d}, {value: 0x0008, lo: 0x8e, hi: 0x8e}, {value: 0x0018, lo: 0x8f, hi: 0x8f}, {value: 0x0040, lo: 0x90, hi: 0xbf}, // Block 0xf8, offset 0x74c {value: 0x0000, lo: 0x05}, {value: 0x0008, lo: 0x80, hi: 0xab}, {value: 0x3308, lo: 0xac, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xb9}, {value: 0x0040, lo: 0xba, hi: 0xbe}, {value: 0x0018, lo: 0xbf, hi: 0xbf}, // Block 0xf9, offset 0x752 {value: 0x0000, lo: 0x05}, {value: 0x0808, lo: 0x80, hi: 0x84}, {value: 0x0040, lo: 0x85, hi: 0x86}, {value: 0x0818, lo: 0x87, hi: 0x8f}, {value: 0x3308, lo: 0x90, hi: 0x96}, {value: 0x0040, lo: 0x97, hi: 0xbf}, // Block 0xfa, offset 0x758 {value: 0x0000, lo: 0x08}, {value: 0x0a08, lo: 0x80, hi: 0x83}, {value: 0x3308, lo: 0x84, hi: 0x8a}, {value: 0x0b08, lo: 0x8b, hi: 0x8b}, {value: 0x0040, lo: 0x8c, hi: 0x8f}, {value: 0x0808, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9d}, {value: 0x0818, lo: 0x9e, hi: 0x9f}, {value: 0x0040, lo: 0xa0, hi: 0xbf}, // Block 0xfb, offset 0x761 {value: 0x0000, lo: 0x02}, {value: 0x0040, lo: 0x80, hi: 0xb0}, {value: 0x0818, lo: 0xb1, hi: 0xbf}, // Block 0xfc, offset 0x764 {value: 0x0000, lo: 0x02}, {value: 0x0818, lo: 0x80, hi: 0xb4}, {value: 0x0040, lo: 0xb5, hi: 0xbf}, // Block 0xfd, offset 0x767 {value: 0x0000, lo: 0x03}, {value: 0x0040, lo: 0x80, hi: 0x80}, {value: 0x0818, lo: 0x81, hi: 0xbd}, {value: 0x0040, lo: 0xbe, hi: 0xbf}, // Block 0xfe, offset 0x76b {value: 0x0000, lo: 0x03}, {value: 0x0040, lo: 0x80, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xb1}, {value: 0x0040, lo: 0xb2, hi: 0xbf}, // Block 0xff, offset 0x76f {value: 0x0000, lo: 0x03}, {value: 0x0018, lo: 0x80, hi: 0xab}, {value: 0x0040, lo: 0xac, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xbf}, // Block 0x100, offset 0x773 {value: 0x0000, lo: 0x05}, {value: 0x0018, lo: 0x80, hi: 0x93}, {value: 0x0040, lo: 0x94, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xae}, {value: 0x0040, lo: 0xaf, hi: 0xb0}, {value: 0x0018, lo: 0xb1, hi: 0xbf}, // Block 0x101, offset 0x779 {value: 0x0000, lo: 0x05}, {value: 0x0040, lo: 0x80, hi: 0x80}, {value: 0x0018, lo: 0x81, hi: 0x8f}, {value: 0x0040, lo: 0x90, hi: 0x90}, {value: 0x0018, lo: 0x91, hi: 0xb5}, {value: 0x0040, lo: 0xb6, hi: 0xbf}, // Block 0x102, offset 0x77f {value: 0x0000, lo: 0x04}, {value: 0x0018, lo: 0x80, hi: 0x8f}, {value: 0xc1d9, lo: 0x90, hi: 0x90}, {value: 0x0018, lo: 0x91, hi: 0xac}, {value: 0x0040, lo: 0xad, hi: 0xbf}, // Block 0x103, offset 0x784 {value: 0x0000, lo: 0x02}, {value: 0x0040, lo: 0x80, hi: 0xa5}, {value: 0x0018, lo: 0xa6, hi: 0xbf}, // Block 0x104, offset 0x787 {value: 0x0000, lo: 0x0f}, {value: 0xc801, lo: 0x80, hi: 0x80}, {value: 0xc851, lo: 0x81, hi: 0x81}, {value: 0xc8a1, lo: 0x82, hi: 0x82}, {value: 0xc8f1, lo: 0x83, hi: 0x83}, {value: 0xc941, lo: 0x84, hi: 0x84}, {value: 0xc991, lo: 0x85, hi: 0x85}, {value: 0xc9e1, lo: 0x86, hi: 0x86}, {value: 0xca31, lo: 0x87, hi: 0x87}, {value: 0xca81, lo: 0x88, hi: 0x88}, {value: 0x0040, lo: 0x89, hi: 0x8f}, {value: 0xcad1, lo: 0x90, hi: 0x90}, {value: 0xcaf1, lo: 0x91, hi: 0x91}, {value: 0x0040, lo: 0x92, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xa5}, {value: 0x0040, lo: 0xa6, hi: 0xbf}, // Block 0x105, offset 0x797 {value: 0x0000, lo: 0x06}, {value: 0x0018, lo: 0x80, hi: 0x95}, {value: 0x0040, lo: 0x96, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xac}, {value: 0x0040, lo: 0xad, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xba}, {value: 0x0040, lo: 0xbb, hi: 0xbf}, // Block 0x106, offset 0x79e {value: 0x0000, lo: 0x02}, {value: 0x0018, lo: 0x80, hi: 0xb3}, {value: 0x0040, lo: 0xb4, hi: 0xbf}, // Block 0x107, offset 0x7a1 {value: 0x0000, lo: 0x04}, {value: 0x0018, lo: 0x80, hi: 0x98}, {value: 0x0040, lo: 0x99, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xab}, {value: 0x0040, lo: 0xac, hi: 0xbf}, // Block 0x108, offset 0x7a6 {value: 0x0000, lo: 0x03}, {value: 0x0018, lo: 0x80, hi: 0x8b}, {value: 0x0040, lo: 0x8c, hi: 0x8f}, {value: 0x0018, lo: 0x90, hi: 0xbf}, // Block 0x109, offset 0x7aa {value: 0x0000, lo: 0x05}, {value: 0x0018, lo: 0x80, hi: 0x87}, {value: 0x0040, lo: 0x88, hi: 0x8f}, {value: 0x0018, lo: 0x90, hi: 0x99}, {value: 0x0040, lo: 0x9a, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xbf}, // Block 0x10a, offset 0x7b0 {value: 0x0000, lo: 0x04}, {value: 0x0018, lo: 0x80, hi: 0x87}, {value: 0x0040, lo: 0x88, hi: 0x8f}, {value: 0x0018, lo: 0x90, hi: 0xad}, {value: 0x0040, lo: 0xae, hi: 0xbf}, // Block 0x10b, offset 0x7b5 {value: 0x0000, lo: 0x03}, {value: 0x0018, lo: 0x80, hi: 0x8b}, {value: 0x0040, lo: 0x8c, hi: 0x8c}, {value: 0x0018, lo: 0x8d, hi: 0xbf}, // Block 0x10c, offset 0x7b9 {value: 0x0000, lo: 0x05}, {value: 0x0018, lo: 0x80, hi: 0xb1}, {value: 0x0040, lo: 0xb2, hi: 0xb2}, {value: 0x0018, lo: 0xb3, hi: 0xb6}, {value: 0x0040, lo: 0xb7, hi: 0xb9}, {value: 0x0018, lo: 0xba, hi: 0xbf}, // Block 0x10d, offset 0x7bf {value: 0x0000, lo: 0x05}, {value: 0x0018, lo: 0x80, hi: 0xa2}, {value: 0x0040, lo: 0xa3, hi: 0xa4}, {value: 0x0018, lo: 0xa5, hi: 0xaa}, {value: 0x0040, lo: 0xab, hi: 0xad}, {value: 0x0018, lo: 0xae, hi: 0xbf}, // Block 0x10e, offset 0x7c5 {value: 0x0000, lo: 0x03}, {value: 0x0018, lo: 0x80, hi: 0x8a}, {value: 0x0040, lo: 0x8b, hi: 0x8c}, {value: 0x0018, lo: 0x8d, hi: 0xbf}, // Block 0x10f, offset 0x7c9 {value: 0x0000, lo: 0x08}, {value: 0x0018, lo: 0x80, hi: 0x93}, {value: 0x0040, lo: 0x94, hi: 0x9f}, {value: 0x0018, lo: 0xa0, hi: 0xad}, {value: 0x0040, lo: 0xae, hi: 0xaf}, {value: 0x0018, lo: 0xb0, hi: 0xb3}, {value: 0x0040, lo: 0xb4, hi: 0xb7}, {value: 0x0018, lo: 0xb8, hi: 0xba}, {value: 0x0040, lo: 0xbb, hi: 0xbf}, // Block 0x110, offset 0x7d2 {value: 0x0000, lo: 0x04}, {value: 0x0018, lo: 0x80, hi: 0x82}, {value: 0x0040, lo: 0x83, hi: 0x8f}, {value: 0x0018, lo: 0x90, hi: 0x95}, {value: 0x0040, lo: 0x96, hi: 0xbf}, // Block 0x111, offset 0x7d7 {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0x96}, {value: 0x0040, lo: 0x97, hi: 0xbf}, // Block 0x112, offset 0x7da {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0xb4}, {value: 0x0040, lo: 0xb5, hi: 0xbf}, // Block 0x113, offset 0x7dd {value: 0x0000, lo: 0x03}, {value: 0x0008, lo: 0x80, hi: 0x9d}, {value: 0x0040, lo: 0x9e, hi: 0x9f}, {value: 0x0008, lo: 0xa0, hi: 0xbf}, // Block 0x114, offset 0x7e1 {value: 0x0000, lo: 0x03}, {value: 0x0008, lo: 0x80, hi: 0xa1}, {value: 0x0040, lo: 0xa2, hi: 0xaf}, {value: 0x0008, lo: 0xb0, hi: 0xbf}, // Block 0x115, offset 0x7e5 {value: 0x0000, lo: 0x02}, {value: 0x0008, lo: 0x80, hi: 0xa0}, {value: 0x0040, lo: 0xa1, hi: 0xbf}, // Block 0x116, offset 0x7e8 {value: 0x0020, lo: 0x0f}, {value: 0xded1, lo: 0x80, hi: 0x89}, {value: 0x8e35, lo: 0x8a, hi: 0x8a}, {value: 0xe011, lo: 0x8b, hi: 0x9c}, {value: 0x8e55, lo: 0x9d, hi: 0x9d}, {value: 0xe251, lo: 0x9e, hi: 0xa2}, {value: 0x8e75, lo: 0xa3, hi: 0xa3}, {value: 0xe2f1, lo: 0xa4, hi: 0xab}, {value: 0x7f0d, lo: 0xac, hi: 0xac}, {value: 0xe3f1, lo: 0xad, hi: 0xaf}, {value: 0x8e95, lo: 0xb0, hi: 0xb0}, {value: 0xe451, lo: 0xb1, hi: 0xb6}, {value: 0x8eb5, lo: 0xb7, hi: 0xb9}, {value: 0xe511, lo: 0xba, hi: 0xba}, {value: 0x8f15, lo: 0xbb, hi: 0xbb}, {value: 0xe531, lo: 0xbc, hi: 0xbf}, // Block 0x117, offset 0x7f8 {value: 0x0020, lo: 0x10}, {value: 0x93b5, lo: 0x80, hi: 0x80}, {value: 0xf0b1, lo: 0x81, hi: 0x86}, {value: 0x93d5, lo: 0x87, hi: 0x8a}, {value: 0xda11, lo: 0x8b, hi: 0x8b}, {value: 0xf171, lo: 0x8c, hi: 0x96}, {value: 0x9455, lo: 0x97, hi: 0x97}, {value: 0xf2d1, lo: 0x98, hi: 0xa3}, {value: 0x9475, lo: 0xa4, hi: 0xa6}, {value: 0xf451, lo: 0xa7, hi: 0xaa}, {value: 0x94d5, lo: 0xab, hi: 0xab}, {value: 0xf4d1, lo: 0xac, hi: 0xac}, {value: 0x94f5, lo: 0xad, hi: 0xad}, {value: 0xf4f1, lo: 0xae, hi: 0xaf}, {value: 0x9515, lo: 0xb0, hi: 0xb1}, {value: 0xf531, lo: 0xb2, hi: 0xbe}, {value: 0x2040, lo: 0xbf, hi: 0xbf}, // Block 0x118, offset 0x809 {value: 0x0000, lo: 0x04}, {value: 0x0040, lo: 0x80, hi: 0x80}, {value: 0x0340, lo: 0x81, hi: 0x81}, {value: 0x0040, lo: 0x82, hi: 0x9f}, {value: 0x0340, lo: 0xa0, hi: 0xbf}, // Block 0x119, offset 0x80e {value: 0x0000, lo: 0x01}, {value: 0x0340, lo: 0x80, hi: 0xbf}, // Block 0x11a, offset 0x810 {value: 0x0000, lo: 0x01}, {value: 0x33c0, lo: 0x80, hi: 0xbf}, // Block 0x11b, offset 0x812 {value: 0x0000, lo: 0x02}, {value: 0x33c0, lo: 0x80, hi: 0xaf}, {value: 0x0040, lo: 0xb0, hi: 0xbf}, } // Total table size 42780 bytes (41KiB); checksum: 29936AB9 ================================================ FILE: vendor/golang.org/x/net/idna/tables13.0.0.go ================================================ [File too large to display: 284.3 KB] ================================================ FILE: vendor/golang.org/x/net/idna/tables15.0.0.go ================================================ [File too large to display: 297.4 KB] ================================================ FILE: vendor/golang.org/x/net/idna/tables9.0.0.go ================================================ [File too large to display: 263.4 KB] ================================================ FILE: vendor/golang.org/x/net/idna/trie.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/golang.org/x/net/idna/trie12.0.0.go ================================================ [File too large to display: 852 B] ================================================ FILE: vendor/golang.org/x/net/idna/trie13.0.0.go ================================================ [File too large to display: 872 B] ================================================ FILE: vendor/golang.org/x/net/idna/trieval.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/golang.org/x/net/internal/httpcommon/ascii.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/golang.org/x/net/internal/httpcommon/headermap.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/golang.org/x/net/internal/httpcommon/request.go ================================================ [File too large to display: 13.8 KB] ================================================ FILE: vendor/golang.org/x/net/internal/socks/client.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/golang.org/x/net/internal/socks/socks.go ================================================ [File too large to display: 9.0 KB] ================================================ FILE: vendor/golang.org/x/net/proxy/dial.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/golang.org/x/net/proxy/direct.go ================================================ [File too large to display: 832 B] ================================================ FILE: vendor/golang.org/x/net/proxy/per_host.go ================================================ [File too large to display: 4.1 KB] ================================================ FILE: vendor/golang.org/x/net/proxy/proxy.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/golang.org/x/net/proxy/socks5.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/golang.org/x/oauth2/.travis.yml ================================================ [File too large to display: 262 B] ================================================ FILE: vendor/golang.org/x/oauth2/CONTRIBUTING.md ================================================ [File too large to display: 924 B] ================================================ FILE: vendor/golang.org/x/oauth2/LICENSE ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/golang.org/x/oauth2/README.md ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/golang.org/x/oauth2/internal/client_appengine.go ================================================ [File too large to display: 322 B] ================================================ FILE: vendor/golang.org/x/oauth2/internal/doc.go ================================================ [File too large to display: 243 B] ================================================ FILE: vendor/golang.org/x/oauth2/internal/oauth2.go ================================================ [File too large to display: 1.0 KB] ================================================ FILE: vendor/golang.org/x/oauth2/internal/token.go ================================================ [File too large to display: 10.0 KB] ================================================ FILE: vendor/golang.org/x/oauth2/internal/transport.go ================================================ [File too large to display: 923 B] ================================================ FILE: vendor/golang.org/x/oauth2/oauth2.go ================================================ [File too large to display: 13.2 KB] ================================================ FILE: vendor/golang.org/x/oauth2/token.go ================================================ [File too large to display: 5.8 KB] ================================================ FILE: vendor/golang.org/x/oauth2/transport.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/golang.org/x/sync/LICENSE ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/golang.org/x/sync/PATENTS ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/golang.org/x/sync/errgroup/errgroup.go ================================================ [File too large to display: 3.9 KB] ================================================ FILE: vendor/golang.org/x/sys/LICENSE ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/golang.org/x/sys/PATENTS ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/golang.org/x/sys/execabs/execabs.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/golang.org/x/sys/execabs/execabs_go118.go ================================================ [File too large to display: 331 B] ================================================ FILE: vendor/golang.org/x/sys/execabs/execabs_go119.go ================================================ [File too large to display: 376 B] ================================================ FILE: vendor/golang.org/x/sys/plan9/asm.s ================================================ [File too large to display: 214 B] ================================================ FILE: vendor/golang.org/x/sys/plan9/asm_plan9_386.s ================================================ [File too large to display: 702 B] ================================================ FILE: vendor/golang.org/x/sys/plan9/asm_plan9_amd64.s ================================================ [File too large to display: 704 B] ================================================ FILE: vendor/golang.org/x/sys/plan9/asm_plan9_arm.s ================================================ [File too large to display: 644 B] ================================================ FILE: vendor/golang.org/x/sys/plan9/const_plan9.go ================================================ [File too large to display: 1004 B] ================================================ FILE: vendor/golang.org/x/sys/plan9/dir_plan9.go ================================================ [File too large to display: 5.6 KB] ================================================ FILE: vendor/golang.org/x/sys/plan9/env_plan9.go ================================================ [File too large to display: 555 B] ================================================ FILE: vendor/golang.org/x/sys/plan9/errors_plan9.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/golang.org/x/sys/plan9/mkall.sh ================================================ [File too large to display: 4.4 KB] ================================================ FILE: vendor/golang.org/x/sys/plan9/mkerrors.sh ================================================ [File too large to display: 5.9 KB] ================================================ FILE: vendor/golang.org/x/sys/plan9/mksysnum_plan9.sh ================================================ [File too large to display: 459 B] ================================================ FILE: vendor/golang.org/x/sys/plan9/pwd_plan9.go ================================================ [File too large to display: 355 B] ================================================ FILE: vendor/golang.org/x/sys/plan9/race.go ================================================ [File too large to display: 587 B] ================================================ FILE: vendor/golang.org/x/sys/plan9/race0.go ================================================ [File too large to display: 450 B] ================================================ FILE: vendor/golang.org/x/sys/plan9/str.go ================================================ [File too large to display: 500 B] ================================================ FILE: vendor/golang.org/x/sys/plan9/syscall.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/golang.org/x/sys/plan9/syscall_plan9.go ================================================ [File too large to display: 7.0 KB] ================================================ FILE: vendor/golang.org/x/sys/plan9/zsyscall_plan9_386.go ================================================ [File too large to display: 6.2 KB] ================================================ FILE: vendor/golang.org/x/sys/plan9/zsyscall_plan9_amd64.go ================================================ [File too large to display: 6.2 KB] ================================================ FILE: vendor/golang.org/x/sys/plan9/zsyscall_plan9_arm.go ================================================ [File too large to display: 6.2 KB] ================================================ FILE: vendor/golang.org/x/sys/plan9/zsysnum_plan9.go ================================================ [File too large to display: 1.0 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/.gitignore ================================================ [File too large to display: 16 B] ================================================ FILE: vendor/golang.org/x/sys/unix/README.md ================================================ [File too large to display: 8.5 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/affinity_linux.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/aliases.go ================================================ [File too large to display: 385 B] ================================================ FILE: vendor/golang.org/x/sys/unix/asm_aix_ppc64.s ================================================ [File too large to display: 407 B] ================================================ FILE: vendor/golang.org/x/sys/unix/asm_bsd_386.s ================================================ [File too large to display: 696 B] ================================================ FILE: vendor/golang.org/x/sys/unix/asm_bsd_amd64.s ================================================ [File too large to display: 722 B] ================================================ FILE: vendor/golang.org/x/sys/unix/asm_bsd_arm.s ================================================ [File too large to display: 686 B] ================================================ FILE: vendor/golang.org/x/sys/unix/asm_bsd_arm64.s ================================================ [File too large to display: 709 B] ================================================ FILE: vendor/golang.org/x/sys/unix/asm_bsd_ppc64.s ================================================ [File too large to display: 716 B] ================================================ FILE: vendor/golang.org/x/sys/unix/asm_bsd_riscv64.s ================================================ [File too large to display: 711 B] ================================================ FILE: vendor/golang.org/x/sys/unix/asm_linux_386.s ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/asm_linux_amd64.s ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/asm_linux_arm.s ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/asm_linux_arm64.s ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/asm_linux_loong64.s ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/asm_linux_mips64x.s ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/asm_linux_mipsx.s ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/asm_linux_ppc64x.s ================================================ [File too large to display: 909 B] ================================================ FILE: vendor/golang.org/x/sys/unix/asm_linux_riscv64.s ================================================ [File too large to display: 1.0 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/asm_linux_s390x.s ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/asm_openbsd_mips64.s ================================================ [File too large to display: 677 B] ================================================ FILE: vendor/golang.org/x/sys/unix/asm_solaris_amd64.s ================================================ [File too large to display: 423 B] ================================================ FILE: vendor/golang.org/x/sys/unix/asm_zos_s390x.s ================================================ [File too large to display: 11.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/auxv.go ================================================ [File too large to display: 1020 B] ================================================ FILE: vendor/golang.org/x/sys/unix/auxv_unsupported.go ================================================ [File too large to display: 370 B] ================================================ FILE: vendor/golang.org/x/sys/unix/bluetooth_linux.go ================================================ [File too large to display: 680 B] ================================================ FILE: vendor/golang.org/x/sys/unix/bpxsvc_zos.go ================================================ [File too large to display: 19.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/bpxsvc_zos.s ================================================ [File too large to display: 8.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/cap_freebsd.go ================================================ [File too large to display: 5.0 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/constants.go ================================================ [File too large to display: 318 B] ================================================ FILE: vendor/golang.org/x/sys/unix/dev_aix_ppc.go ================================================ [File too large to display: 739 B] ================================================ FILE: vendor/golang.org/x/sys/unix/dev_aix_ppc64.go ================================================ [File too large to display: 858 B] ================================================ FILE: vendor/golang.org/x/sys/unix/dev_darwin.go ================================================ [File too large to display: 747 B] ================================================ FILE: vendor/golang.org/x/sys/unix/dev_dragonfly.go ================================================ [File too large to display: 1.0 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/dev_freebsd.go ================================================ [File too large to display: 1013 B] ================================================ FILE: vendor/golang.org/x/sys/unix/dev_linux.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/dev_netbsd.go ================================================ [File too large to display: 913 B] ================================================ FILE: vendor/golang.org/x/sys/unix/dev_openbsd.go ================================================ [File too large to display: 918 B] ================================================ FILE: vendor/golang.org/x/sys/unix/dev_zos.go ================================================ [File too large to display: 830 B] ================================================ FILE: vendor/golang.org/x/sys/unix/dirent.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/endian_big.go ================================================ [File too large to display: 330 B] ================================================ FILE: vendor/golang.org/x/sys/unix/endian_little.go ================================================ [File too large to display: 358 B] ================================================ FILE: vendor/golang.org/x/sys/unix/env_unix.go ================================================ [File too large to display: 645 B] ================================================ FILE: vendor/golang.org/x/sys/unix/fcntl.go ================================================ [File too large to display: 1.0 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/fcntl_darwin.go ================================================ [File too large to display: 806 B] ================================================ FILE: vendor/golang.org/x/sys/unix/fcntl_linux_32bit.go ================================================ [File too large to display: 436 B] ================================================ FILE: vendor/golang.org/x/sys/unix/fdset.go ================================================ [File too large to display: 753 B] ================================================ FILE: vendor/golang.org/x/sys/unix/gccgo.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/gccgo_c.c ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/gccgo_linux_amd64.go ================================================ [File too large to display: 436 B] ================================================ FILE: vendor/golang.org/x/sys/unix/ifreq_linux.go ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ioctl_linux.go ================================================ [File too large to display: 11.3 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ioctl_signed.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ioctl_unsigned.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ioctl_zos.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/mkall.sh ================================================ [File too large to display: 8.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/mkerrors.sh ================================================ [File too large to display: 20.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/mmap_nomremap.go ================================================ [File too large to display: 343 B] ================================================ FILE: vendor/golang.org/x/sys/unix/mremap.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/pagesize_unix.go ================================================ [File too large to display: 396 B] ================================================ FILE: vendor/golang.org/x/sys/unix/pledge_openbsd.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ptrace_darwin.go ================================================ [File too large to display: 315 B] ================================================ FILE: vendor/golang.org/x/sys/unix/ptrace_ios.go ================================================ [File too large to display: 284 B] ================================================ FILE: vendor/golang.org/x/sys/unix/race.go ================================================ [File too large to display: 629 B] ================================================ FILE: vendor/golang.org/x/sys/unix/race0.go ================================================ [File too large to display: 553 B] ================================================ FILE: vendor/golang.org/x/sys/unix/readdirent_getdents.go ================================================ [File too large to display: 401 B] ================================================ FILE: vendor/golang.org/x/sys/unix/readdirent_getdirentries.go ================================================ [File too large to display: 705 B] ================================================ FILE: vendor/golang.org/x/sys/unix/sockcmsg_dragonfly.go ================================================ [File too large to display: 544 B] ================================================ FILE: vendor/golang.org/x/sys/unix/sockcmsg_linux.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/sockcmsg_unix.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/sockcmsg_unix_other.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/sockcmsg_zos.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/symaddr_zos_s390x.s ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall.go ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_aix.go ================================================ [File too large to display: 16.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_aix_ppc.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_aix_ppc64.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_bsd.go ================================================ [File too large to display: 15.0 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_darwin.go ================================================ [File too large to display: 24.6 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_darwin_amd64.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_darwin_arm64.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_darwin_libSystem.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_dragonfly.go ================================================ [File too large to display: 11.4 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_dragonfly_amd64.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_freebsd.go ================================================ [File too large to display: 15.6 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_freebsd_386.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_freebsd_amd64.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_freebsd_arm.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_freebsd_arm64.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_freebsd_riscv64.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_hurd.go ================================================ [File too large to display: 651 B] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_hurd_386.go ================================================ [File too large to display: 447 B] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_illumos.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_linux.go ================================================ [File too large to display: 79.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_linux_386.go ================================================ [File too large to display: 8.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_linux_alarm.go ================================================ [File too large to display: 440 B] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_linux_amd64.go ================================================ [File too large to display: 5.3 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_linux_amd64_gc.go ================================================ [File too large to display: 290 B] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_linux_arm.go ================================================ [File too large to display: 6.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_linux_arm64.go ================================================ [File too large to display: 6.0 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_linux_gc.go ================================================ [File too large to display: 495 B] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_linux_gc_386.go ================================================ [File too large to display: 585 B] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_linux_gc_arm.go ================================================ [File too large to display: 411 B] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_linux_gccgo_386.go ================================================ [File too large to display: 986 B] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_linux_gccgo_arm.go ================================================ [File too large to display: 596 B] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_linux_loong64.go ================================================ [File too large to display: 6.7 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_linux_mips64x.go ================================================ [File too large to display: 5.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_linux_mipsx.go ================================================ [File too large to display: 5.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_linux_ppc.go ================================================ [File too large to display: 6.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_linux_ppc64x.go ================================================ [File too large to display: 5.0 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_linux_riscv64.go ================================================ [File too large to display: 6.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_linux_s390x.go ================================================ [File too large to display: 9.3 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_linux_sparc64.go ================================================ [File too large to display: 4.4 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_netbsd.go ================================================ [File too large to display: 12.7 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_netbsd_386.go ================================================ [File too large to display: 819 B] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_netbsd_amd64.go ================================================ [File too large to display: 814 B] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_netbsd_arm.go ================================================ [File too large to display: 819 B] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_netbsd_arm64.go ================================================ [File too large to display: 814 B] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_openbsd.go ================================================ [File too large to display: 11.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_openbsd_386.go ================================================ [File too large to display: 1000 B] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_openbsd_amd64.go ================================================ [File too large to display: 990 B] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_openbsd_arm.go ================================================ [File too large to display: 1000 B] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_openbsd_arm64.go ================================================ [File too large to display: 990 B] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_openbsd_libc.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_openbsd_mips64.go ================================================ [File too large to display: 955 B] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_openbsd_ppc64.go ================================================ [File too large to display: 990 B] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_openbsd_riscv64.go ================================================ [File too large to display: 994 B] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_solaris.go ================================================ [File too large to display: 33.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_solaris_amd64.go ================================================ [File too large to display: 593 B] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_unix.go ================================================ [File too large to display: 16.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_unix_gc.go ================================================ [File too large to display: 640 B] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_unix_gc_ppc64x.go ================================================ [File too large to display: 786 B] ================================================ FILE: vendor/golang.org/x/sys/unix/syscall_zos_s390x.go ================================================ [File too large to display: 86.7 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/sysvshm_linux.go ================================================ [File too large to display: 521 B] ================================================ FILE: vendor/golang.org/x/sys/unix/sysvshm_unix.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/sysvshm_unix_other.go ================================================ [File too large to display: 407 B] ================================================ FILE: vendor/golang.org/x/sys/unix/timestruct.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/unveil_openbsd.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/vgetrandom_linux.go ================================================ [File too large to display: 345 B] ================================================ FILE: vendor/golang.org/x/sys/unix/vgetrandom_unsupported.go ================================================ [File too large to display: 292 B] ================================================ FILE: vendor/golang.org/x/sys/unix/xattr_bsd.go ================================================ [File too large to display: 5.7 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_aix_ppc.go ================================================ [File too large to display: 52.4 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_aix_ppc64.go ================================================ [File too large to display: 52.7 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go ================================================ [File too large to display: 88.0 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go ================================================ [File too large to display: 88.0 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_dragonfly_amd64.go ================================================ [File too large to display: 70.7 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_freebsd_386.go ================================================ [File too large to display: 79.6 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_freebsd_amd64.go ================================================ [File too large to display: 79.4 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go ================================================ [File too large to display: 79.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_freebsd_arm64.go ================================================ [File too large to display: 79.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_freebsd_riscv64.go ================================================ [File too large to display: 83.7 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_linux.go ================================================ [File too large to display: 210.7 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_linux_386.go ================================================ [File too large to display: 35.6 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go ================================================ [File too large to display: 35.6 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_linux_arm.go ================================================ [File too large to display: 35.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go ================================================ [File too large to display: 35.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go ================================================ [File too large to display: 35.3 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_linux_mips.go ================================================ [File too large to display: 36.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go ================================================ [File too large to display: 36.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go ================================================ [File too large to display: 36.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go ================================================ [File too large to display: 36.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go ================================================ [File too large to display: 38.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go ================================================ [File too large to display: 38.4 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go ================================================ [File too large to display: 38.4 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go ================================================ [File too large to display: 35.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go ================================================ [File too large to display: 38.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go ================================================ [File too large to display: 40.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_netbsd_386.go ================================================ [File too large to display: 72.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_netbsd_amd64.go ================================================ [File too large to display: 72.3 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_netbsd_arm.go ================================================ [File too large to display: 71.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_netbsd_arm64.go ================================================ [File too large to display: 72.3 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_openbsd_386.go ================================================ [File too large to display: 78.5 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_openbsd_amd64.go ================================================ [File too large to display: 78.6 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_openbsd_arm.go ================================================ [File too large to display: 78.5 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_openbsd_arm64.go ================================================ [File too large to display: 78.6 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_openbsd_mips64.go ================================================ [File too large to display: 78.5 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_openbsd_ppc64.go ================================================ [File too large to display: 78.5 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_openbsd_riscv64.go ================================================ [File too large to display: 78.5 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_solaris_amd64.go ================================================ [File too large to display: 59.0 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zerrors_zos_s390x.go ================================================ [File too large to display: 39.5 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zptrace_armnn_linux.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zptrace_linux_arm64.go ================================================ [File too large to display: 721 B] ================================================ FILE: vendor/golang.org/x/sys/unix/zptrace_mipsnn_linux.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zptrace_mipsnnle_linux.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zptrace_x86_linux.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsymaddr_zos_s390x.s ================================================ [File too large to display: 8.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_aix_ppc.go ================================================ [File too large to display: 35.7 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64.go ================================================ [File too large to display: 30.0 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64_gc.go ================================================ [File too large to display: 42.4 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_aix_ppc64_gccgo.go ================================================ [File too large to display: 30.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go ================================================ [File too large to display: 71.7 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s ================================================ [File too large to display: 31.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go ================================================ [File too large to display: 71.7 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s ================================================ [File too large to display: 31.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_dragonfly_amd64.go ================================================ [File too large to display: 38.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_freebsd_386.go ================================================ [File too large to display: 45.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_freebsd_amd64.go ================================================ [File too large to display: 44.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm.go ================================================ [File too large to display: 45.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_freebsd_arm64.go ================================================ [File too large to display: 44.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_freebsd_riscv64.go ================================================ [File too large to display: 44.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_illumos_amd64.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_linux.go ================================================ [File too large to display: 55.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_linux_386.go ================================================ [File too large to display: 12.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_linux_amd64.go ================================================ [File too large to display: 16.5 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_linux_arm.go ================================================ [File too large to display: 15.6 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_linux_arm64.go ================================================ [File too large to display: 14.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_linux_loong64.go ================================================ [File too large to display: 12.6 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_linux_mips.go ================================================ [File too large to display: 16.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_linux_mips64.go ================================================ [File too large to display: 16.3 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_linux_mips64le.go ================================================ [File too large to display: 16.0 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_linux_mipsle.go ================================================ [File too large to display: 16.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_linux_ppc.go ================================================ [File too large to display: 16.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64.go ================================================ [File too large to display: 17.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_linux_ppc64le.go ================================================ [File too large to display: 17.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_linux_riscv64.go ================================================ [File too large to display: 14.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_linux_s390x.go ================================================ [File too large to display: 12.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_linux_sparc64.go ================================================ [File too large to display: 16.4 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_netbsd_386.go ================================================ [File too large to display: 44.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_netbsd_amd64.go ================================================ [File too large to display: 43.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm.go ================================================ [File too large to display: 44.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_netbsd_arm64.go ================================================ [File too large to display: 43.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go ================================================ [File too large to display: 58.3 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s ================================================ [File too large to display: 26.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go ================================================ [File too large to display: 58.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s ================================================ [File too large to display: 26.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go ================================================ [File too large to display: 58.3 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s ================================================ [File too large to display: 26.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go ================================================ [File too large to display: 58.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s ================================================ [File too large to display: 26.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go ================================================ [File too large to display: 58.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s ================================================ [File too large to display: 26.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go ================================================ [File too large to display: 58.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s ================================================ [File too large to display: 27.7 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go ================================================ [File too large to display: 58.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s ================================================ [File too large to display: 26.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go ================================================ [File too large to display: 63.4 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsyscall_zos_s390x.go ================================================ [File too large to display: 88.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysctl_openbsd_386.go ================================================ [File too large to display: 11.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysctl_openbsd_amd64.go ================================================ [File too large to display: 11.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysctl_openbsd_arm.go ================================================ [File too large to display: 11.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysctl_openbsd_arm64.go ================================================ [File too large to display: 11.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysctl_openbsd_mips64.go ================================================ [File too large to display: 11.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysctl_openbsd_ppc64.go ================================================ [File too large to display: 11.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysctl_openbsd_riscv64.go ================================================ [File too large to display: 12.0 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_darwin_amd64.go ================================================ [File too large to display: 17.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_darwin_arm64.go ================================================ [File too large to display: 17.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_dragonfly_amd64.go ================================================ [File too large to display: 27.6 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_freebsd_386.go ================================================ [File too large to display: 36.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_freebsd_amd64.go ================================================ [File too large to display: 36.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm.go ================================================ [File too large to display: 36.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_freebsd_arm64.go ================================================ [File too large to display: 36.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_freebsd_riscv64.go ================================================ [File too large to display: 36.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_linux_386.go ================================================ [File too large to display: 18.0 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go ================================================ [File too large to display: 13.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go ================================================ [File too large to display: 16.6 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go ================================================ [File too large to display: 11.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go ================================================ [File too large to display: 11.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go ================================================ [File too large to display: 17.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go ================================================ [File too large to display: 13.3 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go ================================================ [File too large to display: 13.3 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go ================================================ [File too large to display: 17.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go ================================================ [File too large to display: 17.6 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go ================================================ [File too large to display: 14.5 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go ================================================ [File too large to display: 14.5 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go ================================================ [File too large to display: 11.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go ================================================ [File too large to display: 13.3 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go ================================================ [File too large to display: 13.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_netbsd_386.go ================================================ [File too large to display: 25.7 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_netbsd_amd64.go ================================================ [File too large to display: 25.7 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_netbsd_arm.go ================================================ [File too large to display: 25.7 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_netbsd_arm64.go ================================================ [File too large to display: 25.7 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_openbsd_386.go ================================================ [File too large to display: 17.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_openbsd_amd64.go ================================================ [File too large to display: 17.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_openbsd_arm.go ================================================ [File too large to display: 17.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_openbsd_arm64.go ================================================ [File too large to display: 17.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_openbsd_mips64.go ================================================ [File too large to display: 18.0 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_openbsd_ppc64.go ================================================ [File too large to display: 17.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_openbsd_riscv64.go ================================================ [File too large to display: 17.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/zsysnum_zos_s390x.go ================================================ [File too large to display: 146.6 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_aix_ppc.go ================================================ [File too large to display: 4.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_aix_ppc64.go ================================================ [File too large to display: 5.0 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go ================================================ [File too large to display: 13.6 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go ================================================ [File too large to display: 13.6 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_dragonfly_amd64.go ================================================ [File too large to display: 6.9 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go ================================================ [File too large to display: 9.6 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go ================================================ [File too large to display: 9.7 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go ================================================ [File too large to display: 9.5 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go ================================================ [File too large to display: 9.4 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go ================================================ [File too large to display: 9.5 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_linux.go ================================================ [File too large to display: 261.7 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_linux_386.go ================================================ [File too large to display: 12.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_linux_amd64.go ================================================ [File too large to display: 13.0 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_linux_arm.go ================================================ [File too large to display: 12.7 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_linux_arm64.go ================================================ [File too large to display: 12.6 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_linux_loong64.go ================================================ [File too large to display: 12.6 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_linux_mips.go ================================================ [File too large to display: 12.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_linux_mips64.go ================================================ [File too large to display: 12.7 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_linux_mips64le.go ================================================ [File too large to display: 12.7 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_linux_mipsle.go ================================================ [File too large to display: 12.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_linux_ppc.go ================================================ [File too large to display: 13.0 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_linux_ppc64.go ================================================ [File too large to display: 12.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_linux_ppc64le.go ================================================ [File too large to display: 12.8 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go ================================================ [File too large to display: 15.4 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_linux_s390x.go ================================================ [File too large to display: 13.1 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_linux_sparc64.go ================================================ [File too large to display: 12.7 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_netbsd_386.go ================================================ [File too large to display: 9.5 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_netbsd_amd64.go ================================================ [File too large to display: 9.6 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_netbsd_arm.go ================================================ [File too large to display: 9.6 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_netbsd_arm64.go ================================================ [File too large to display: 9.6 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_openbsd_386.go ================================================ [File too large to display: 9.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_openbsd_amd64.go ================================================ [File too large to display: 9.2 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_openbsd_arm.go ================================================ [File too large to display: 9.4 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_openbsd_arm64.go ================================================ [File too large to display: 9.3 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_openbsd_mips64.go ================================================ [File too large to display: 9.3 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_openbsd_ppc64.go ================================================ [File too large to display: 9.3 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_openbsd_riscv64.go ================================================ [File too large to display: 9.3 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_solaris_amd64.go ================================================ [File too large to display: 7.5 KB] ================================================ FILE: vendor/golang.org/x/sys/unix/ztypes_zos_s390x.go ================================================ [File too large to display: 8.7 KB] ================================================ FILE: vendor/golang.org/x/sys/windows/aliases.go ================================================ [File too large to display: 281 B] ================================================ FILE: vendor/golang.org/x/sys/windows/dll_windows.go ================================================ [File too large to display: 12.1 KB] ================================================ FILE: vendor/golang.org/x/sys/windows/env_windows.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/golang.org/x/sys/windows/eventlog.go ================================================ [File too large to display: 825 B] ================================================ FILE: vendor/golang.org/x/sys/windows/exec_windows.go ================================================ [File too large to display: 7.3 KB] ================================================ FILE: vendor/golang.org/x/sys/windows/memory_windows.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/golang.org/x/sys/windows/mkerrors.bash ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/golang.org/x/sys/windows/mkknownfolderids.bash ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/golang.org/x/sys/windows/mksyscall.go ================================================ [File too large to display: 367 B] ================================================ FILE: vendor/golang.org/x/sys/windows/race.go ================================================ [File too large to display: 591 B] ================================================ FILE: vendor/golang.org/x/sys/windows/race0.go ================================================ [File too large to display: 454 B] ================================================ FILE: vendor/golang.org/x/sys/windows/security_windows.go ================================================ [File too large to display: 54.5 KB] ================================================ FILE: vendor/golang.org/x/sys/windows/service.go ================================================ [File too large to display: 10.3 KB] ================================================ FILE: vendor/golang.org/x/sys/windows/setupapi_windows.go ================================================ [File too large to display: 67.2 KB] ================================================ FILE: vendor/golang.org/x/sys/windows/str.go ================================================ [File too large to display: 504 B] ================================================ FILE: vendor/golang.org/x/sys/windows/syscall.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/golang.org/x/sys/windows/syscall_windows.go ================================================ [File too large to display: 85.8 KB] ================================================ FILE: vendor/golang.org/x/sys/windows/types_windows.go ================================================ [File too large to display: 121.2 KB] ================================================ FILE: vendor/golang.org/x/sys/windows/types_windows_386.go ================================================ [File too large to display: 875 B] ================================================ FILE: vendor/golang.org/x/sys/windows/types_windows_amd64.go ================================================ [File too large to display: 817 B] ================================================ FILE: vendor/golang.org/x/sys/windows/types_windows_arm.go ================================================ [File too large to display: 875 B] ================================================ FILE: vendor/golang.org/x/sys/windows/types_windows_arm64.go ================================================ [File too large to display: 817 B] ================================================ FILE: vendor/golang.org/x/sys/windows/zerrors_windows.go ================================================ [File too large to display: 923.3 KB] ================================================ FILE: vendor/golang.org/x/sys/windows/zknownfolderids_windows.go ================================================ [File too large to display: 18.7 KB] ================================================ FILE: vendor/golang.org/x/sys/windows/zsyscall_windows.go ================================================ [File too large to display: 201.0 KB] ================================================ FILE: vendor/golang.org/x/term/CONTRIBUTING.md ================================================ [File too large to display: 913 B] ================================================ FILE: vendor/golang.org/x/term/LICENSE ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/golang.org/x/term/PATENTS ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/golang.org/x/term/README.md ================================================ [File too large to display: 588 B] ================================================ FILE: vendor/golang.org/x/term/codereview.cfg ================================================ [File too large to display: 21 B] ================================================ FILE: vendor/golang.org/x/term/term.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/golang.org/x/term/term_plan9.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/golang.org/x/term/term_unix.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/golang.org/x/term/term_unix_bsd.go ================================================ [File too large to display: 349 B] ================================================ FILE: vendor/golang.org/x/term/term_unix_other.go ================================================ [File too large to display: 324 B] ================================================ FILE: vendor/golang.org/x/term/term_unsupported.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/golang.org/x/term/term_windows.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/golang.org/x/term/terminal.go ================================================ [File too large to display: 24.6 KB] ================================================ FILE: vendor/golang.org/x/text/LICENSE ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/golang.org/x/text/PATENTS ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/golang.org/x/text/encoding/charmap/charmap.go ================================================ [File too large to display: 6.4 KB] ================================================ FILE: vendor/golang.org/x/text/encoding/charmap/tables.go ================================================ [File too large to display: 488.6 KB] ================================================ FILE: vendor/golang.org/x/text/encoding/encoding.go ================================================ [File too large to display: 9.5 KB] ================================================ FILE: vendor/golang.org/x/text/encoding/htmlindex/htmlindex.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/golang.org/x/text/encoding/htmlindex/map.go ================================================ [File too large to display: 4.0 KB] ================================================ FILE: vendor/golang.org/x/text/encoding/htmlindex/tables.go ================================================ [File too large to display: 9.8 KB] ================================================ FILE: vendor/golang.org/x/text/encoding/internal/identifier/identifier.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/golang.org/x/text/encoding/internal/identifier/mib.go ================================================ [File too large to display: 49.0 KB] ================================================ FILE: vendor/golang.org/x/text/encoding/internal/internal.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/golang.org/x/text/encoding/japanese/all.go ================================================ [File too large to display: 337 B] ================================================ FILE: vendor/golang.org/x/text/encoding/japanese/eucjp.go ================================================ [File too large to display: 4.8 KB] ================================================ FILE: vendor/golang.org/x/text/encoding/japanese/iso2022jp.go ================================================ [File too large to display: 6.6 KB] ================================================ FILE: vendor/golang.org/x/text/encoding/japanese/shiftjis.go ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/golang.org/x/text/encoding/japanese/tables.go ================================================ [File too large to display: 798.9 KB] ================================================ FILE: vendor/golang.org/x/text/encoding/korean/euckr.go ================================================ [File too large to display: 3.9 KB] ================================================ FILE: vendor/golang.org/x/text/encoding/korean/tables.go ================================================ [File too large to display: 666.2 KB] ================================================ FILE: vendor/golang.org/x/text/encoding/simplifiedchinese/all.go ================================================ [File too large to display: 342 B] ================================================ FILE: vendor/golang.org/x/text/encoding/simplifiedchinese/gbk.go ================================================ [File too large to display: 6.2 KB] ================================================ FILE: vendor/golang.org/x/text/encoding/simplifiedchinese/hzgb2312.go ================================================ [File too large to display: 5.0 KB] ================================================ FILE: vendor/golang.org/x/text/encoding/simplifiedchinese/tables.go ================================================ [File too large to display: 858.4 KB] ================================================ FILE: vendor/golang.org/x/text/encoding/traditionalchinese/big5.go ================================================ [File too large to display: 4.5 KB] ================================================ FILE: vendor/golang.org/x/text/encoding/traditionalchinese/tables.go ================================================ [File too large to display: 800.6 KB] ================================================ FILE: vendor/golang.org/x/text/encoding/unicode/override.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/golang.org/x/text/encoding/unicode/unicode.go ================================================ [File too large to display: 14.7 KB] ================================================ FILE: vendor/golang.org/x/text/internal/language/common.go ================================================ [File too large to display: 334 B] ================================================ FILE: vendor/golang.org/x/text/internal/language/compact/compact.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/golang.org/x/text/internal/language/compact/language.go ================================================ [File too large to display: 7.3 KB] ================================================ FILE: vendor/golang.org/x/text/internal/language/compact/parents.go ================================================ [File too large to display: 6.7 KB] ================================================ FILE: vendor/golang.org/x/text/internal/language/compact/tables.go ================================================ [File too large to display: 31.4 KB] ================================================ FILE: vendor/golang.org/x/text/internal/language/compact/tags.go ================================================ [File too large to display: 5.5 KB] ================================================ FILE: vendor/golang.org/x/text/internal/language/compact.go ================================================ [File too large to display: 853 B] ================================================ FILE: vendor/golang.org/x/text/internal/language/compose.go ================================================ [File too large to display: 3.8 KB] ================================================ FILE: vendor/golang.org/x/text/internal/language/coverage.go ================================================ [File too large to display: 730 B] ================================================ FILE: vendor/golang.org/x/text/internal/language/language.go ================================================ [File too large to display: 16.8 KB] ================================================ FILE: vendor/golang.org/x/text/internal/language/lookup.go ================================================ [File too large to display: 12.1 KB] ================================================ FILE: vendor/golang.org/x/text/internal/language/match.go ================================================ [File too large to display: 5.7 KB] ================================================ FILE: vendor/golang.org/x/text/internal/language/parse.go ================================================ [File too large to display: 14.9 KB] ================================================ FILE: vendor/golang.org/x/text/internal/language/tables.go ================================================ [File too large to display: 153.0 KB] ================================================ FILE: vendor/golang.org/x/text/internal/language/tags.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/golang.org/x/text/internal/tag/tag.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/golang.org/x/text/internal/utf8internal/utf8internal.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/golang.org/x/text/language/coverage.go ================================================ [File too large to display: 4.8 KB] ================================================ FILE: vendor/golang.org/x/text/language/doc.go ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/golang.org/x/text/language/language.go ================================================ [File too large to display: 18.9 KB] ================================================ FILE: vendor/golang.org/x/text/language/match.go ================================================ [File too large to display: 25.1 KB] ================================================ FILE: vendor/golang.org/x/text/language/parse.go ================================================ [File too large to display: 7.5 KB] ================================================ FILE: vendor/golang.org/x/text/language/tables.go ================================================ [File too large to display: 14.2 KB] ================================================ FILE: vendor/golang.org/x/text/language/tags.go ================================================ [File too large to display: 5.4 KB] ================================================ FILE: vendor/golang.org/x/text/runes/cond.go ================================================ [File too large to display: 5.2 KB] ================================================ FILE: vendor/golang.org/x/text/runes/runes.go ================================================ [File too large to display: 8.5 KB] ================================================ FILE: vendor/golang.org/x/text/secure/bidirule/bidirule.go ================================================ [File too large to display: 9.3 KB] ================================================ FILE: vendor/golang.org/x/text/secure/bidirule/bidirule10.0.0.go ================================================ [File too large to display: 323 B] ================================================ FILE: vendor/golang.org/x/text/secure/bidirule/bidirule9.0.0.go ================================================ [File too large to display: 358 B] ================================================ FILE: vendor/golang.org/x/text/transform/transform.go ================================================ [File too large to display: 21.7 KB] ================================================ FILE: vendor/golang.org/x/text/unicode/bidi/bidi.go ================================================ [File too large to display: 10.3 KB] ================================================ FILE: vendor/golang.org/x/text/unicode/bidi/bracket.go ================================================ [File too large to display: 11.2 KB] ================================================ FILE: vendor/golang.org/x/text/unicode/bidi/core.go ================================================ [File too large to display: 29.3 KB] ================================================ FILE: vendor/golang.org/x/text/unicode/bidi/prop.go ================================================ [File too large to display: 5.7 KB] ================================================ FILE: vendor/golang.org/x/text/unicode/bidi/tables10.0.0.go ================================================ [File too large to display: 110.9 KB] ================================================ FILE: vendor/golang.org/x/text/unicode/bidi/tables11.0.0.go ================================================ [File too large to display: 116.6 KB] ================================================ FILE: vendor/golang.org/x/text/unicode/bidi/tables12.0.0.go ================================================ [File too large to display: 118.9 KB] ================================================ FILE: vendor/golang.org/x/text/unicode/bidi/tables13.0.0.go ================================================ [File too large to display: 120.9 KB] ================================================ FILE: vendor/golang.org/x/text/unicode/bidi/tables15.0.0.go ================================================ [File too large to display: 127.4 KB] ================================================ FILE: vendor/golang.org/x/text/unicode/bidi/tables9.0.0.go ================================================ [File too large to display: 109.2 KB] ================================================ FILE: vendor/golang.org/x/text/unicode/bidi/trieval.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/golang.org/x/text/unicode/norm/composition.go ================================================ [File too large to display: 14.1 KB] ================================================ FILE: vendor/golang.org/x/text/unicode/norm/forminfo.go ================================================ [File too large to display: 8.7 KB] ================================================ FILE: vendor/golang.org/x/text/unicode/norm/input.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/golang.org/x/text/unicode/norm/iter.go ================================================ [File too large to display: 11.0 KB] ================================================ FILE: vendor/golang.org/x/text/unicode/norm/normalize.go ================================================ [File too large to display: 14.9 KB] ================================================ FILE: vendor/golang.org/x/text/unicode/norm/readwriter.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/golang.org/x/text/unicode/norm/tables10.0.0.go ================================================ [File too large to display: 374.3 KB] ================================================ FILE: vendor/golang.org/x/text/unicode/norm/tables11.0.0.go ================================================ [File too large to display: 376.2 KB] ================================================ FILE: vendor/golang.org/x/text/unicode/norm/tables12.0.0.go ================================================ [File too large to display: 376.8 KB] ================================================ FILE: vendor/golang.org/x/text/unicode/norm/tables13.0.0.go ================================================ [File too large to display: 378.6 KB] ================================================ FILE: vendor/golang.org/x/text/unicode/norm/tables15.0.0.go ================================================ [File too large to display: 385.8 KB] ================================================ FILE: vendor/golang.org/x/text/unicode/norm/tables9.0.0.go ================================================ [File too large to display: 372.5 KB] ================================================ FILE: vendor/golang.org/x/text/unicode/norm/transform.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/golang.org/x/text/unicode/norm/trie.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/golang.org/x/time/AUTHORS ================================================ [File too large to display: 173 B] ================================================ FILE: vendor/golang.org/x/time/CONTRIBUTORS ================================================ [File too large to display: 170 B] ================================================ FILE: vendor/golang.org/x/time/LICENSE ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/golang.org/x/time/PATENTS ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/golang.org/x/time/rate/rate.go ================================================ [File too large to display: 11.6 KB] ================================================ FILE: vendor/golang.org/x/tools/LICENSE ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/golang.org/x/tools/PATENTS ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/golang.org/x/tools/cmd/stringer/stringer.go ================================================ [File too large to display: 22.5 KB] ================================================ FILE: vendor/golang.org/x/tools/go/ast/edge/edge.go ================================================ [File too large to display: 9.4 KB] ================================================ FILE: vendor/golang.org/x/tools/go/ast/inspector/cursor.go ================================================ [File too large to display: 14.5 KB] ================================================ FILE: vendor/golang.org/x/tools/go/ast/inspector/inspector.go ================================================ [File too large to display: 9.6 KB] ================================================ FILE: vendor/golang.org/x/tools/go/ast/inspector/iter.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/golang.org/x/tools/go/ast/inspector/typeof.go ================================================ [File too large to display: 4.8 KB] ================================================ FILE: vendor/golang.org/x/tools/go/ast/inspector/walk.go ================================================ [File too large to display: 7.7 KB] ================================================ FILE: vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go ================================================ [File too large to display: 9.0 KB] ================================================ FILE: vendor/golang.org/x/tools/go/gcexportdata/importer.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/golang.org/x/tools/go/packages/doc.go ================================================ [File too large to display: 12.1 KB] ================================================ FILE: vendor/golang.org/x/tools/go/packages/external.go ================================================ [File too large to display: 5.2 KB] ================================================ FILE: vendor/golang.org/x/tools/go/packages/golist.go ================================================ [File too large to display: 37.0 KB] ================================================ FILE: vendor/golang.org/x/tools/go/packages/golist_overlay.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/golang.org/x/tools/go/packages/loadmode_string.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/golang.org/x/tools/go/packages/packages.go ================================================ [File too large to display: 49.5 KB] ================================================ FILE: vendor/golang.org/x/tools/go/packages/visit.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/golang.org/x/tools/go/types/objectpath/objectpath.go ================================================ [File too large to display: 25.3 KB] ================================================ FILE: vendor/golang.org/x/tools/go/types/typeutil/callee.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/golang.org/x/tools/go/types/typeutil/imports.go ================================================ [File too large to display: 883 B] ================================================ FILE: vendor/golang.org/x/tools/go/types/typeutil/map.go ================================================ [File too large to display: 11.6 KB] ================================================ FILE: vendor/golang.org/x/tools/go/types/typeutil/methodsetcache.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/golang.org/x/tools/go/types/typeutil/ui.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/aliases/aliases.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/aliases/aliases_go122.go ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/event/core/event.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/event/core/export.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/event/core/fast.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/event/doc.go ================================================ [File too large to display: 298 B] ================================================ FILE: vendor/golang.org/x/tools/internal/event/event.go ================================================ [File too large to display: 4.4 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/event/keys/keys.go ================================================ [File too large to display: 15.5 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/event/keys/standard.go ================================================ [File too large to display: 961 B] ================================================ FILE: vendor/golang.org/x/tools/internal/event/keys/util.go ================================================ [File too large to display: 488 B] ================================================ FILE: vendor/golang.org/x/tools/internal/event/label/label.go ================================================ [File too large to display: 5.5 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/gcimporter/bimport.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/gcimporter/exportdata.go ================================================ [File too large to display: 12.1 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/gcimporter/gcimporter.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/gcimporter/iexport.go ================================================ [File too large to display: 42.3 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/gcimporter/iimport.go ================================================ [File too large to display: 28.1 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/gcimporter/predeclared.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/gcimporter/support.go ================================================ [File too large to display: 697 B] ================================================ FILE: vendor/golang.org/x/tools/internal/gcimporter/ureader_yes.go ================================================ [File too large to display: 19.1 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/gocommand/invoke.go ================================================ [File too large to display: 17.0 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/gocommand/invoke_notunix.go ================================================ [File too large to display: 377 B] ================================================ FILE: vendor/golang.org/x/tools/internal/gocommand/invoke_unix.go ================================================ [File too large to display: 361 B] ================================================ FILE: vendor/golang.org/x/tools/internal/gocommand/vendor.go ================================================ [File too large to display: 5.1 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/gocommand/version.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/packagesinternal/packages.go ================================================ [File too large to display: 788 B] ================================================ FILE: vendor/golang.org/x/tools/internal/pkgbits/codes.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/pkgbits/decoder.go ================================================ [File too large to display: 13.4 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/pkgbits/doc.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/pkgbits/encoder.go ================================================ [File too large to display: 9.6 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/pkgbits/flags.go ================================================ [File too large to display: 253 B] ================================================ FILE: vendor/golang.org/x/tools/internal/pkgbits/reloc.go ================================================ [File too large to display: 835 B] ================================================ FILE: vendor/golang.org/x/tools/internal/pkgbits/support.go ================================================ [File too large to display: 336 B] ================================================ FILE: vendor/golang.org/x/tools/internal/pkgbits/sync.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/pkgbits/syncmarker_string.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/pkgbits/version.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/stdlib/deps.go ================================================ [File too large to display: 18.2 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/stdlib/import.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/stdlib/manifest.go ================================================ [File too large to display: 684.0 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/stdlib/stdlib.go ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/typeparams/common.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/typeparams/coretype.go ================================================ [File too large to display: 5.4 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/typeparams/free.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/typeparams/normalize.go ================================================ [File too large to display: 6.7 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/typeparams/termlist.go ================================================ [File too large to display: 4.0 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/typeparams/typeterm.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/typesinternal/classify_call.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/typesinternal/element.go ================================================ [File too large to display: 3.9 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/typesinternal/errorcode.go ================================================ [File too large to display: 34.0 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/typesinternal/errorcode_string.go ================================================ [File too large to display: 7.5 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/typesinternal/fx.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/typesinternal/isnamed.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/typesinternal/qualifier.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/typesinternal/recv.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/typesinternal/toonew.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/typesinternal/types.go ================================================ [File too large to display: 6.1 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/typesinternal/varkind.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/typesinternal/zerovalue.go ================================================ [File too large to display: 10.3 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/versions/features.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/versions/gover.go ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/versions/types.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/golang.org/x/tools/internal/versions/versions.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/google.golang.org/appengine/LICENSE ================================================ [File too large to display: 11.1 KB] ================================================ FILE: vendor/google.golang.org/appengine/internal/api.go ================================================ [File too large to display: 17.1 KB] ================================================ FILE: vendor/google.golang.org/appengine/internal/api_classic.go ================================================ [File too large to display: 3.9 KB] ================================================ FILE: vendor/google.golang.org/appengine/internal/api_common.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/google.golang.org/appengine/internal/app_id.go ================================================ [File too large to display: 679 B] ================================================ FILE: vendor/google.golang.org/appengine/internal/base/api_base.pb.go ================================================ [File too large to display: 10.9 KB] ================================================ FILE: vendor/google.golang.org/appengine/internal/base/api_base.proto ================================================ [File too large to display: 503 B] ================================================ FILE: vendor/google.golang.org/appengine/internal/datastore/datastore_v3.pb.go ================================================ [File too large to display: 159.2 KB] ================================================ FILE: vendor/google.golang.org/appengine/internal/datastore/datastore_v3.proto ================================================ [File too large to display: 10.8 KB] ================================================ FILE: vendor/google.golang.org/appengine/internal/identity.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/google.golang.org/appengine/internal/identity_classic.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/google.golang.org/appengine/internal/identity_flex.go ================================================ [File too large to display: 238 B] ================================================ FILE: vendor/google.golang.org/appengine/internal/identity_vm.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/google.golang.org/appengine/internal/internal.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/google.golang.org/appengine/internal/log/log_service.pb.go ================================================ [File too large to display: 48.0 KB] ================================================ FILE: vendor/google.golang.org/appengine/internal/log/log_service.proto ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/google.golang.org/appengine/internal/main.go ================================================ [File too large to display: 290 B] ================================================ FILE: vendor/google.golang.org/appengine/internal/main_common.go ================================================ [File too large to display: 294 B] ================================================ FILE: vendor/google.golang.org/appengine/internal/main_vm.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/google.golang.org/appengine/internal/metadata.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/google.golang.org/appengine/internal/net.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/google.golang.org/appengine/internal/regen.sh ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/google.golang.org/appengine/internal/remote_api/remote_api.pb.go ================================================ [File too large to display: 13.1 KB] ================================================ FILE: vendor/google.golang.org/appengine/internal/remote_api/remote_api.proto ================================================ [File too large to display: 934 B] ================================================ FILE: vendor/google.golang.org/appengine/internal/transaction.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/google.golang.org/appengine/internal/urlfetch/urlfetch_service.pb.go ================================================ [File too large to display: 21.7 KB] ================================================ FILE: vendor/google.golang.org/appengine/internal/urlfetch/urlfetch_service.proto ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/google.golang.org/appengine/urlfetch/urlfetch.go ================================================ [File too large to display: 6.0 KB] ================================================ FILE: vendor/google.golang.org/protobuf/LICENSE ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/google.golang.org/protobuf/PATENTS ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/google.golang.org/protobuf/encoding/prototext/decode.go ================================================ [File too large to display: 20.8 KB] ================================================ FILE: vendor/google.golang.org/protobuf/encoding/prototext/doc.go ================================================ [File too large to display: 276 B] ================================================ FILE: vendor/google.golang.org/protobuf/encoding/prototext/encode.go ================================================ [File too large to display: 11.2 KB] ================================================ FILE: vendor/google.golang.org/protobuf/encoding/protowire/wire.go ================================================ [File too large to display: 13.1 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/descfmt/stringer.go ================================================ [File too large to display: 12.0 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/descopts/options.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/detrand/rand.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/editiondefaults/defaults.go ================================================ [File too large to display: 349 B] ================================================ FILE: vendor/google.golang.org/protobuf/internal/editiondefaults/editions_defaults.binpb ================================================ [File too large to display: 63 B] ================================================ FILE: vendor/google.golang.org/protobuf/internal/encoding/defval/default.go ================================================ [File too large to display: 6.4 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/encoding/messageset/messageset.go ================================================ [File too large to display: 7.4 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go ================================================ [File too large to display: 7.2 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/encoding/text/decode.go ================================================ [File too large to display: 16.6 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/encoding/text/decode_number.go ================================================ [File too large to display: 4.4 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/encoding/text/decode_string.go ================================================ [File too large to display: 4.8 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/encoding/text/decode_token.go ================================================ [File too large to display: 9.2 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/encoding/text/doc.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/encoding/text/encode.go ================================================ [File too large to display: 7.1 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/errors/errors.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/errors/is_go112.go ================================================ [File too large to display: 778 B] ================================================ FILE: vendor/google.golang.org/protobuf/internal/errors/is_go113.go ================================================ [File too large to display: 315 B] ================================================ FILE: vendor/google.golang.org/protobuf/internal/filedesc/build.go ================================================ [File too large to display: 5.0 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/filedesc/desc.go ================================================ [File too large to display: 25.9 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/filedesc/desc_init.go ================================================ [File too large to display: 14.6 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go ================================================ [File too large to display: 22.3 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/filedesc/desc_list.go ================================================ [File too large to display: 14.8 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/filedesc/desc_list_gen.go ================================================ [File too large to display: 8.4 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/filedesc/editions.go ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/filedesc/placeholder.go ================================================ [File too large to display: 6.9 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/filetype/build.go ================================================ [File too large to display: 10.5 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/flags/flags.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/flags/proto_legacy_disable.go ================================================ [File too large to display: 249 B] ================================================ FILE: vendor/google.golang.org/protobuf/internal/flags/proto_legacy_enable.go ================================================ [File too large to display: 246 B] ================================================ FILE: vendor/google.golang.org/protobuf/internal/genid/any_gen.go ================================================ [File too large to display: 1020 B] ================================================ FILE: vendor/google.golang.org/protobuf/internal/genid/api_gen.go ================================================ [File too large to display: 4.6 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go ================================================ [File too large to display: 67.8 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/genid/doc.go ================================================ [File too large to display: 419 B] ================================================ FILE: vendor/google.golang.org/protobuf/internal/genid/duration_gen.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/genid/empty_gen.go ================================================ [File too large to display: 549 B] ================================================ FILE: vendor/google.golang.org/protobuf/internal/genid/field_mask_gen.go ================================================ [File too large to display: 901 B] ================================================ FILE: vendor/google.golang.org/protobuf/internal/genid/go_features_gen.go ================================================ [File too large to display: 1.0 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/genid/goname.go ================================================ [File too large to display: 692 B] ================================================ FILE: vendor/google.golang.org/protobuf/internal/genid/map_entry.go ================================================ [File too large to display: 550 B] ================================================ FILE: vendor/google.golang.org/protobuf/internal/genid/source_context_gen.go ================================================ [File too large to display: 970 B] ================================================ FILE: vendor/google.golang.org/protobuf/internal/genid/struct_gen.go ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/genid/timestamp_gen.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/genid/type_gen.go ================================================ [File too large to display: 9.2 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/genid/wrappers.go ================================================ [File too large to display: 451 B] ================================================ FILE: vendor/google.golang.org/protobuf/internal/genid/wrappers_gen.go ================================================ [File too large to display: 5.0 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/api_export.go ================================================ [File too large to display: 4.9 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/checkinit.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/codec_extension.go ================================================ [File too large to display: 5.8 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/codec_field.go ================================================ [File too large to display: 24.6 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/codec_gen.go ================================================ [File too large to display: 162.8 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/codec_map.go ================================================ [File too large to display: 10.2 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/codec_map_go111.go ================================================ [File too large to display: 739 B] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/codec_map_go112.go ================================================ [File too large to display: 300 B] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/codec_message.go ================================================ [File too large to display: 7.1 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/codec_messageset.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/codec_reflect.go ================================================ [File too large to display: 5.3 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/codec_tables.go ================================================ [File too large to display: 16.3 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/codec_unsafe.go ================================================ [File too large to display: 518 B] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/convert.go ================================================ [File too large to display: 15.5 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/convert_list.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/convert_map.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/decode.go ================================================ [File too large to display: 7.4 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/encode.go ================================================ [File too large to display: 5.1 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/enum.go ================================================ [File too large to display: 594 B] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/extension.go ================================================ [File too large to display: 4.6 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/legacy_enum.go ================================================ [File too large to display: 6.7 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/legacy_export.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/legacy_extension.go ================================================ [File too large to display: 7.5 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/legacy_file.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/legacy_message.go ================================================ [File too large to display: 18.6 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/merge.go ================================================ [File too large to display: 4.5 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/merge_gen.go ================================================ [File too large to display: 4.5 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/message.go ================================================ [File too large to display: 8.5 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/message_reflect.go ================================================ [File too large to display: 13.6 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/message_reflect_field.go ================================================ [File too large to display: 15.1 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/message_reflect_gen.go ================================================ [File too large to display: 7.8 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/pointer_reflect.go ================================================ [File too large to display: 7.1 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/pointer_unsafe.go ================================================ [File too large to display: 7.6 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/validate.go ================================================ [File too large to display: 15.1 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/impl/weak.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/order/order.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/order/range.go ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/pragma/pragma.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/set/ints.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/strs/strings.go ================================================ [File too large to display: 5.3 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/strs/strings_pure.go ================================================ [File too large to display: 616 B] ================================================ FILE: vendor/google.golang.org/protobuf/internal/strs/strings_unsafe_go120.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/strs/strings_unsafe_go121.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/google.golang.org/protobuf/internal/version/version.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/google.golang.org/protobuf/proto/checkinit.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/google.golang.org/protobuf/proto/decode.go ================================================ [File too large to display: 8.5 KB] ================================================ FILE: vendor/google.golang.org/protobuf/proto/decode_gen.go ================================================ [File too large to display: 14.6 KB] ================================================ FILE: vendor/google.golang.org/protobuf/proto/doc.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/google.golang.org/protobuf/proto/encode.go ================================================ [File too large to display: 9.9 KB] ================================================ FILE: vendor/google.golang.org/protobuf/proto/encode_gen.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/google.golang.org/protobuf/proto/equal.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/google.golang.org/protobuf/proto/extension.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/google.golang.org/protobuf/proto/merge.go ================================================ [File too large to display: 4.4 KB] ================================================ FILE: vendor/google.golang.org/protobuf/proto/messageset.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/google.golang.org/protobuf/proto/proto.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/google.golang.org/protobuf/proto/proto_methods.go ================================================ [File too large to display: 523 B] ================================================ FILE: vendor/google.golang.org/protobuf/proto/proto_reflect.go ================================================ [File too large to display: 509 B] ================================================ FILE: vendor/google.golang.org/protobuf/proto/reset.go ================================================ [File too large to display: 1018 B] ================================================ FILE: vendor/google.golang.org/protobuf/proto/size.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/google.golang.org/protobuf/proto/size_gen.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/google.golang.org/protobuf/proto/wrappers.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/google.golang.org/protobuf/reflect/protodesc/desc.go ================================================ [File too large to display: 10.8 KB] ================================================ FILE: vendor/google.golang.org/protobuf/reflect/protodesc/desc_init.go ================================================ [File too large to display: 11.6 KB] ================================================ FILE: vendor/google.golang.org/protobuf/reflect/protodesc/desc_resolve.go ================================================ [File too large to display: 9.9 KB] ================================================ FILE: vendor/google.golang.org/protobuf/reflect/protodesc/desc_validate.go ================================================ [File too large to display: 16.2 KB] ================================================ FILE: vendor/google.golang.org/protobuf/reflect/protodesc/editions.go ================================================ [File too large to display: 5.0 KB] ================================================ FILE: vendor/google.golang.org/protobuf/reflect/protodesc/proto.go ================================================ [File too large to display: 9.5 KB] ================================================ FILE: vendor/google.golang.org/protobuf/reflect/protoreflect/methods.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/google.golang.org/protobuf/reflect/protoreflect/proto.go ================================================ [File too large to display: 16.9 KB] ================================================ FILE: vendor/google.golang.org/protobuf/reflect/protoreflect/source.go ================================================ [File too large to display: 3.9 KB] ================================================ FILE: vendor/google.golang.org/protobuf/reflect/protoreflect/source_gen.go ================================================ [File too large to display: 14.3 KB] ================================================ FILE: vendor/google.golang.org/protobuf/reflect/protoreflect/type.go ================================================ [File too large to display: 25.9 KB] ================================================ FILE: vendor/google.golang.org/protobuf/reflect/protoreflect/value.go ================================================ [File too large to display: 11.0 KB] ================================================ FILE: vendor/google.golang.org/protobuf/reflect/protoreflect/value_equal.go ================================================ [File too large to display: 4.0 KB] ================================================ FILE: vendor/google.golang.org/protobuf/reflect/protoreflect/value_pure.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/google.golang.org/protobuf/reflect/protoreflect/value_union.go ================================================ [File too large to display: 12.9 KB] ================================================ FILE: vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe_go120.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe_go121.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/google.golang.org/protobuf/reflect/protoregistry/registry.go ================================================ [File too large to display: 24.7 KB] ================================================ FILE: vendor/google.golang.org/protobuf/runtime/protoiface/legacy.go ================================================ [File too large to display: 321 B] ================================================ FILE: vendor/google.golang.org/protobuf/runtime/protoiface/methods.go ================================================ [File too large to display: 4.9 KB] ================================================ FILE: vendor/google.golang.org/protobuf/runtime/protoimpl/impl.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/google.golang.org/protobuf/runtime/protoimpl/version.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go ================================================ [File too large to display: 233.7 KB] ================================================ FILE: vendor/google.golang.org/protobuf/types/gofeaturespb/go_features.pb.go ================================================ [File too large to display: 7.3 KB] ================================================ FILE: vendor/google.golang.org/protobuf/types/gofeaturespb/go_features.proto ================================================ [File too large to display: 867 B] ================================================ FILE: vendor/google.golang.org/protobuf/types/known/anypb/any.pb.go ================================================ [File too large to display: 17.2 KB] ================================================ FILE: vendor/google.golang.org/protobuf/types/known/durationpb/duration.pb.go ================================================ [File too large to display: 13.5 KB] ================================================ FILE: vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go ================================================ [File too large to display: 14.4 KB] ================================================ FILE: vendor/gopkg.in/inf.v0/LICENSE ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/gopkg.in/inf.v0/dec.go ================================================ [File too large to display: 16.2 KB] ================================================ FILE: vendor/gopkg.in/inf.v0/rounder.go ================================================ [File too large to display: 3.8 KB] ================================================ FILE: vendor/gopkg.in/yaml.v2/.travis.yml ================================================ [File too large to display: 223 B] ================================================ FILE: vendor/gopkg.in/yaml.v2/LICENSE ================================================ [File too large to display: 11.1 KB] ================================================ FILE: vendor/gopkg.in/yaml.v2/LICENSE.libyaml ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/gopkg.in/yaml.v2/NOTICE ================================================ [File too large to display: 560 B] ================================================ FILE: vendor/gopkg.in/yaml.v2/README.md ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/gopkg.in/yaml.v2/apic.go ================================================ [File too large to display: 20.8 KB] ================================================ FILE: vendor/gopkg.in/yaml.v2/decode.go ================================================ [File too large to display: 19.5 KB] ================================================ FILE: vendor/gopkg.in/yaml.v2/emitterc.go ================================================ [File too large to display: 44.3 KB] ================================================ FILE: vendor/gopkg.in/yaml.v2/encode.go ================================================ [File too large to display: 9.6 KB] ================================================ FILE: vendor/gopkg.in/yaml.v2/parserc.go ================================================ [File too large to display: 33.9 KB] ================================================ FILE: vendor/gopkg.in/yaml.v2/readerc.go ================================================ [File too large to display: 12.6 KB] ================================================ FILE: vendor/gopkg.in/yaml.v2/resolve.go ================================================ [File too large to display: 6.7 KB] ================================================ FILE: vendor/gopkg.in/yaml.v2/scannerc.go ================================================ [File too large to display: 75.8 KB] ================================================ FILE: vendor/gopkg.in/yaml.v2/sorter.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/gopkg.in/yaml.v2/writerc.go ================================================ [File too large to display: 668 B] ================================================ FILE: vendor/gopkg.in/yaml.v2/yaml.go ================================================ [File too large to display: 13.6 KB] ================================================ FILE: vendor/gopkg.in/yaml.v2/yamlh.go ================================================ [File too large to display: 25.6 KB] ================================================ FILE: vendor/gopkg.in/yaml.v2/yamlprivateh.go ================================================ [File too large to display: 4.8 KB] ================================================ FILE: vendor/gopkg.in/yaml.v3/LICENSE ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/gopkg.in/yaml.v3/NOTICE ================================================ [File too large to display: 560 B] ================================================ FILE: vendor/gopkg.in/yaml.v3/README.md ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/gopkg.in/yaml.v3/apic.go ================================================ [File too large to display: 21.5 KB] ================================================ FILE: vendor/gopkg.in/yaml.v3/decode.go ================================================ [File too large to display: 24.4 KB] ================================================ FILE: vendor/gopkg.in/yaml.v3/emitterc.go ================================================ [File too large to display: 53.9 KB] ================================================ FILE: vendor/gopkg.in/yaml.v3/encode.go ================================================ [File too large to display: 14.4 KB] ================================================ FILE: vendor/gopkg.in/yaml.v3/parserc.go ================================================ [File too large to display: 39.9 KB] ================================================ FILE: vendor/gopkg.in/yaml.v3/readerc.go ================================================ [File too large to display: 13.8 KB] ================================================ FILE: vendor/gopkg.in/yaml.v3/resolve.go ================================================ [File too large to display: 8.3 KB] ================================================ FILE: vendor/gopkg.in/yaml.v3/scannerc.go ================================================ [File too large to display: 85.9 KB] ================================================ FILE: vendor/gopkg.in/yaml.v3/sorter.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/gopkg.in/yaml.v3/writerc.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/gopkg.in/yaml.v3/yaml.go ================================================ [File too large to display: 19.6 KB] ================================================ FILE: vendor/gopkg.in/yaml.v3/yamlh.go ================================================ [File too large to display: 28.4 KB] ================================================ FILE: vendor/gopkg.in/yaml.v3/yamlprivateh.go ================================================ [File too large to display: 6.0 KB] ================================================ FILE: vendor/k8s.io/api/LICENSE ================================================ [File too large to display: 11.1 KB] ================================================ FILE: vendor/k8s.io/api/admissionregistration/v1/doc.go ================================================ [File too large to display: 1.0 KB] ================================================ FILE: vendor/k8s.io/api/admissionregistration/v1/generated.pb.go ================================================ [File too large to display: 94.6 KB] ================================================ FILE: vendor/k8s.io/api/admissionregistration/v1/generated.proto ================================================ [File too large to display: 24.4 KB] ================================================ FILE: vendor/k8s.io/api/admissionregistration/v1/register.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/api/admissionregistration/v1/types.go ================================================ [File too large to display: 29.9 KB] ================================================ FILE: vendor/k8s.io/api/admissionregistration/v1/types_swagger_doc_generated.go ================================================ [File too large to display: 22.7 KB] ================================================ FILE: vendor/k8s.io/api/admissionregistration/v1/zz_generated.deepcopy.go ================================================ [File too large to display: 12.0 KB] ================================================ FILE: vendor/k8s.io/api/admissionregistration/v1alpha1/doc.go ================================================ [File too large to display: 825 B] ================================================ FILE: vendor/k8s.io/api/admissionregistration/v1alpha1/generated.pb.go ================================================ [File too large to display: 109.1 KB] ================================================ FILE: vendor/k8s.io/api/admissionregistration/v1alpha1/generated.proto ================================================ [File too large to display: 25.7 KB] ================================================ FILE: vendor/k8s.io/api/admissionregistration/v1alpha1/register.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/api/admissionregistration/v1alpha1/types.go ================================================ [File too large to display: 29.0 KB] ================================================ FILE: vendor/k8s.io/api/admissionregistration/v1alpha1/types_swagger_doc_generated.go ================================================ [File too large to display: 23.2 KB] ================================================ FILE: vendor/k8s.io/api/admissionregistration/v1alpha1/zz_generated.deepcopy.go ================================================ [File too large to display: 13.1 KB] ================================================ FILE: vendor/k8s.io/api/admissionregistration/v1beta1/doc.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/k8s.io/api/admissionregistration/v1beta1/generated.pb.go ================================================ [File too large to display: 81.8 KB] ================================================ FILE: vendor/k8s.io/api/admissionregistration/v1beta1/generated.proto ================================================ [File too large to display: 22.6 KB] ================================================ FILE: vendor/k8s.io/api/admissionregistration/v1beta1/register.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/api/admissionregistration/v1beta1/types.go ================================================ [File too large to display: 29.1 KB] ================================================ FILE: vendor/k8s.io/api/admissionregistration/v1beta1/types_swagger_doc_generated.go ================================================ [File too large to display: 21.0 KB] ================================================ FILE: vendor/k8s.io/api/admissionregistration/v1beta1/zz_generated.deepcopy.go ================================================ [File too large to display: 10.5 KB] ================================================ FILE: vendor/k8s.io/api/admissionregistration/v1beta1/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 7.3 KB] ================================================ FILE: vendor/k8s.io/api/apidiscovery/v2beta1/doc.go ================================================ [File too large to display: 788 B] ================================================ FILE: vendor/k8s.io/api/apidiscovery/v2beta1/generated.pb.go ================================================ [File too large to display: 45.1 KB] ================================================ FILE: vendor/k8s.io/api/apidiscovery/v2beta1/generated.proto ================================================ [File too large to display: 7.8 KB] ================================================ FILE: vendor/k8s.io/api/apidiscovery/v2beta1/register.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/api/apidiscovery/v2beta1/types.go ================================================ [File too large to display: 9.6 KB] ================================================ FILE: vendor/k8s.io/api/apidiscovery/v2beta1/zz_generated.deepcopy.go ================================================ [File too large to display: 5.4 KB] ================================================ FILE: vendor/k8s.io/api/apidiscovery/v2beta1/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/api/apiserverinternal/v1alpha1/doc.go ================================================ [File too large to display: 862 B] ================================================ FILE: vendor/k8s.io/api/apiserverinternal/v1alpha1/generated.pb.go ================================================ [File too large to display: 44.0 KB] ================================================ FILE: vendor/k8s.io/api/apiserverinternal/v1alpha1/generated.proto ================================================ [File too large to display: 4.1 KB] ================================================ FILE: vendor/k8s.io/api/apiserverinternal/v1alpha1/register.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/api/apiserverinternal/v1alpha1/types.go ================================================ [File too large to display: 5.2 KB] ================================================ FILE: vendor/k8s.io/api/apiserverinternal/v1alpha1/types_swagger_doc_generated.go ================================================ [File too large to display: 4.4 KB] ================================================ FILE: vendor/k8s.io/api/apiserverinternal/v1alpha1/zz_generated.deepcopy.go ================================================ [File too large to display: 5.1 KB] ================================================ FILE: vendor/k8s.io/api/apps/v1/doc.go ================================================ [File too large to display: 697 B] ================================================ FILE: vendor/k8s.io/api/apps/v1/generated.pb.go ================================================ [File too large to display: 217.2 KB] ================================================ FILE: vendor/k8s.io/api/apps/v1/generated.proto ================================================ [File too large to display: 34.5 KB] ================================================ FILE: vendor/k8s.io/api/apps/v1/register.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/api/apps/v1/types.go ================================================ [File too large to display: 48.4 KB] ================================================ FILE: vendor/k8s.io/api/apps/v1/types_swagger_doc_generated.go ================================================ [File too large to display: 32.5 KB] ================================================ FILE: vendor/k8s.io/api/apps/v1/zz_generated.deepcopy.go ================================================ [File too large to display: 23.8 KB] ================================================ FILE: vendor/k8s.io/api/apps/v1beta1/doc.go ================================================ [File too large to display: 745 B] ================================================ FILE: vendor/k8s.io/api/apps/v1beta1/generated.pb.go ================================================ [File too large to display: 169.4 KB] ================================================ FILE: vendor/k8s.io/api/apps/v1beta1/generated.proto ================================================ [File too large to display: 24.0 KB] ================================================ FILE: vendor/k8s.io/api/apps/v1beta1/register.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/api/apps/v1beta1/types.go ================================================ [File too large to display: 35.0 KB] ================================================ FILE: vendor/k8s.io/api/apps/v1beta1/types_swagger_doc_generated.go ================================================ [File too large to display: 23.1 KB] ================================================ FILE: vendor/k8s.io/api/apps/v1beta1/zz_generated.deepcopy.go ================================================ [File too large to display: 18.5 KB] ================================================ FILE: vendor/k8s.io/api/apps/v1beta1/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 13.0 KB] ================================================ FILE: vendor/k8s.io/api/apps/v1beta2/doc.go ================================================ [File too large to display: 745 B] ================================================ FILE: vendor/k8s.io/api/apps/v1beta2/generated.pb.go ================================================ [File too large to display: 235.7 KB] ================================================ FILE: vendor/k8s.io/api/apps/v1beta2/generated.proto ================================================ [File too large to display: 36.4 KB] ================================================ FILE: vendor/k8s.io/api/apps/v1beta2/register.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/api/apps/v1beta2/types.go ================================================ [File too large to display: 52.0 KB] ================================================ FILE: vendor/k8s.io/api/apps/v1beta2/types_swagger_doc_generated.go ================================================ [File too large to display: 34.3 KB] ================================================ FILE: vendor/k8s.io/api/apps/v1beta2/zz_generated.deepcopy.go ================================================ [File too large to display: 25.5 KB] ================================================ FILE: vendor/k8s.io/api/apps/v1beta2/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 17.5 KB] ================================================ FILE: vendor/k8s.io/api/authentication/v1/doc.go ================================================ [File too large to display: 743 B] ================================================ FILE: vendor/k8s.io/api/authentication/v1/generated.pb.go ================================================ [File too large to display: 63.0 KB] ================================================ FILE: vendor/k8s.io/api/authentication/v1/generated.proto ================================================ [File too large to display: 6.7 KB] ================================================ FILE: vendor/k8s.io/api/authentication/v1/register.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/api/authentication/v1/types.go ================================================ [File too large to display: 8.6 KB] ================================================ FILE: vendor/k8s.io/api/authentication/v1/types_swagger_doc_generated.go ================================================ [File too large to display: 6.6 KB] ================================================ FILE: vendor/k8s.io/api/authentication/v1/zz_generated.deepcopy.go ================================================ [File too large to display: 6.7 KB] ================================================ FILE: vendor/k8s.io/api/authentication/v1alpha1/doc.go ================================================ [File too large to display: 793 B] ================================================ FILE: vendor/k8s.io/api/authentication/v1alpha1/generated.pb.go ================================================ [File too large to display: 14.9 KB] ================================================ FILE: vendor/k8s.io/api/authentication/v1alpha1/generated.proto ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/api/authentication/v1alpha1/register.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/api/authentication/v1alpha1/types.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/api/authentication/v1alpha1/types_swagger_doc_generated.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/api/authentication/v1alpha1/zz_generated.deepcopy.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/k8s.io/api/authentication/v1alpha1/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/api/authentication/v1beta1/doc.go ================================================ [File too large to display: 791 B] ================================================ FILE: vendor/k8s.io/api/authentication/v1beta1/generated.pb.go ================================================ [File too large to display: 47.6 KB] ================================================ FILE: vendor/k8s.io/api/authentication/v1beta1/generated.proto ================================================ [File too large to display: 5.2 KB] ================================================ FILE: vendor/k8s.io/api/authentication/v1beta1/register.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/api/authentication/v1beta1/types.go ================================================ [File too large to display: 6.1 KB] ================================================ FILE: vendor/k8s.io/api/authentication/v1beta1/types_swagger_doc_generated.go ================================================ [File too large to display: 5.1 KB] ================================================ FILE: vendor/k8s.io/api/authentication/v1beta1/zz_generated.deepcopy.go ================================================ [File too large to display: 5.4 KB] ================================================ FILE: vendor/k8s.io/api/authentication/v1beta1/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/k8s.io/api/authorization/v1/doc.go ================================================ [File too large to display: 742 B] ================================================ FILE: vendor/k8s.io/api/authorization/v1/generated.pb.go ================================================ [File too large to display: 101.0 KB] ================================================ FILE: vendor/k8s.io/api/authorization/v1/generated.proto ================================================ [File too large to display: 11.9 KB] ================================================ FILE: vendor/k8s.io/api/authorization/v1/register.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/api/authorization/v1/types.go ================================================ [File too large to display: 14.2 KB] ================================================ FILE: vendor/k8s.io/api/authorization/v1/types_swagger_doc_generated.go ================================================ [File too large to display: 11.9 KB] ================================================ FILE: vendor/k8s.io/api/authorization/v1/zz_generated.deepcopy.go ================================================ [File too large to display: 11.2 KB] ================================================ FILE: vendor/k8s.io/api/authorization/v1beta1/doc.go ================================================ [File too large to display: 790 B] ================================================ FILE: vendor/k8s.io/api/authorization/v1beta1/generated.pb.go ================================================ [File too large to display: 101.1 KB] ================================================ FILE: vendor/k8s.io/api/authorization/v1beta1/generated.proto ================================================ [File too large to display: 11.9 KB] ================================================ FILE: vendor/k8s.io/api/authorization/v1beta1/register.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/api/authorization/v1beta1/types.go ================================================ [File too large to display: 14.9 KB] ================================================ FILE: vendor/k8s.io/api/authorization/v1beta1/types_swagger_doc_generated.go ================================================ [File too large to display: 11.9 KB] ================================================ FILE: vendor/k8s.io/api/authorization/v1beta1/zz_generated.deepcopy.go ================================================ [File too large to display: 11.2 KB] ================================================ FILE: vendor/k8s.io/api/authorization/v1beta1/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 7.1 KB] ================================================ FILE: vendor/k8s.io/api/autoscaling/v1/doc.go ================================================ [File too large to display: 704 B] ================================================ FILE: vendor/k8s.io/api/autoscaling/v1/generated.pb.go ================================================ [File too large to display: 153.1 KB] ================================================ FILE: vendor/k8s.io/api/autoscaling/v1/generated.proto ================================================ [File too large to display: 22.0 KB] ================================================ FILE: vendor/k8s.io/api/autoscaling/v1/register.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/api/autoscaling/v1/types.go ================================================ [File too large to display: 27.7 KB] ================================================ FILE: vendor/k8s.io/api/autoscaling/v1/types_swagger_doc_generated.go ================================================ [File too large to display: 21.4 KB] ================================================ FILE: vendor/k8s.io/api/autoscaling/v1/zz_generated.deepcopy.go ================================================ [File too large to display: 16.8 KB] ================================================ FILE: vendor/k8s.io/api/autoscaling/v2/doc.go ================================================ [File too large to display: 704 B] ================================================ FILE: vendor/k8s.io/api/autoscaling/v2/generated.pb.go ================================================ [File too large to display: 163.0 KB] ================================================ FILE: vendor/k8s.io/api/autoscaling/v2/generated.proto ================================================ [File too large to display: 21.3 KB] ================================================ FILE: vendor/k8s.io/api/autoscaling/v2/register.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/api/autoscaling/v2/types.go ================================================ [File too large to display: 28.4 KB] ================================================ FILE: vendor/k8s.io/api/autoscaling/v2/types_swagger_doc_generated.go ================================================ [File too large to display: 21.3 KB] ================================================ FILE: vendor/k8s.io/api/autoscaling/v2/zz_generated.deepcopy.go ================================================ [File too large to display: 17.8 KB] ================================================ FILE: vendor/k8s.io/api/autoscaling/v2beta1/doc.go ================================================ [File too large to display: 752 B] ================================================ FILE: vendor/k8s.io/api/autoscaling/v2beta1/generated.pb.go ================================================ [File too large to display: 143.1 KB] ================================================ FILE: vendor/k8s.io/api/autoscaling/v2beta1/generated.proto ================================================ [File too large to display: 21.2 KB] ================================================ FILE: vendor/k8s.io/api/autoscaling/v2beta1/register.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/api/autoscaling/v2beta1/types.go ================================================ [File too large to display: 26.8 KB] ================================================ FILE: vendor/k8s.io/api/autoscaling/v2beta1/types_swagger_doc_generated.go ================================================ [File too large to display: 20.4 KB] ================================================ FILE: vendor/k8s.io/api/autoscaling/v2beta1/zz_generated.deepcopy.go ================================================ [File too large to display: 15.4 KB] ================================================ FILE: vendor/k8s.io/api/autoscaling/v2beta1/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 3.9 KB] ================================================ FILE: vendor/k8s.io/api/autoscaling/v2beta2/doc.go ================================================ [File too large to display: 752 B] ================================================ FILE: vendor/k8s.io/api/autoscaling/v2beta2/generated.pb.go ================================================ [File too large to display: 163.2 KB] ================================================ FILE: vendor/k8s.io/api/autoscaling/v2beta2/generated.proto ================================================ [File too large to display: 21.0 KB] ================================================ FILE: vendor/k8s.io/api/autoscaling/v2beta2/register.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/api/autoscaling/v2beta2/types.go ================================================ [File too large to display: 28.3 KB] ================================================ FILE: vendor/k8s.io/api/autoscaling/v2beta2/types_swagger_doc_generated.go ================================================ [File too large to display: 21.0 KB] ================================================ FILE: vendor/k8s.io/api/autoscaling/v2beta2/zz_generated.deepcopy.go ================================================ [File too large to display: 17.8 KB] ================================================ FILE: vendor/k8s.io/api/autoscaling/v2beta2/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/k8s.io/api/batch/v1/doc.go ================================================ [File too large to display: 698 B] ================================================ FILE: vendor/k8s.io/api/batch/v1/generated.pb.go ================================================ [File too large to display: 116.6 KB] ================================================ FILE: vendor/k8s.io/api/batch/v1/generated.proto ================================================ [File too large to display: 21.1 KB] ================================================ FILE: vendor/k8s.io/api/batch/v1/register.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/api/batch/v1/types.go ================================================ [File too large to display: 28.7 KB] ================================================ FILE: vendor/k8s.io/api/batch/v1/types_swagger_doc_generated.go ================================================ [File too large to display: 20.0 KB] ================================================ FILE: vendor/k8s.io/api/batch/v1/zz_generated.deepcopy.go ================================================ [File too large to display: 13.4 KB] ================================================ FILE: vendor/k8s.io/api/batch/v1beta1/doc.go ================================================ [File too large to display: 746 B] ================================================ FILE: vendor/k8s.io/api/batch/v1beta1/generated.pb.go ================================================ [File too large to display: 40.3 KB] ================================================ FILE: vendor/k8s.io/api/batch/v1beta1/generated.proto ================================================ [File too large to display: 5.7 KB] ================================================ FILE: vendor/k8s.io/api/batch/v1beta1/register.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/api/batch/v1beta1/types.go ================================================ [File too large to display: 7.4 KB] ================================================ FILE: vendor/k8s.io/api/batch/v1beta1/types_swagger_doc_generated.go ================================================ [File too large to display: 5.6 KB] ================================================ FILE: vendor/k8s.io/api/batch/v1beta1/zz_generated.deepcopy.go ================================================ [File too large to display: 4.8 KB] ================================================ FILE: vendor/k8s.io/api/batch/v1beta1/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 3.8 KB] ================================================ FILE: vendor/k8s.io/api/certificates/v1/doc.go ================================================ [File too large to display: 740 B] ================================================ FILE: vendor/k8s.io/api/certificates/v1/generated.pb.go ================================================ [File too large to display: 53.8 KB] ================================================ FILE: vendor/k8s.io/api/certificates/v1/generated.proto ================================================ [File too large to display: 11.6 KB] ================================================ FILE: vendor/k8s.io/api/certificates/v1/register.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/api/certificates/v1/types.go ================================================ [File too large to display: 15.5 KB] ================================================ FILE: vendor/k8s.io/api/certificates/v1/types_swagger_doc_generated.go ================================================ [File too large to display: 10.9 KB] ================================================ FILE: vendor/k8s.io/api/certificates/v1/zz_generated.deepcopy.go ================================================ [File too large to display: 6.0 KB] ================================================ FILE: vendor/k8s.io/api/certificates/v1alpha1/doc.go ================================================ [File too large to display: 790 B] ================================================ FILE: vendor/k8s.io/api/certificates/v1alpha1/generated.pb.go ================================================ [File too large to display: 21.6 KB] ================================================ FILE: vendor/k8s.io/api/certificates/v1alpha1/generated.proto ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/k8s.io/api/certificates/v1alpha1/register.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/api/certificates/v1alpha1/types.go ================================================ [File too large to display: 4.3 KB] ================================================ FILE: vendor/k8s.io/api/certificates/v1alpha1/types_swagger_doc_generated.go ================================================ [File too large to display: 4.3 KB] ================================================ FILE: vendor/k8s.io/api/certificates/v1alpha1/zz_generated.deepcopy.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/api/certificates/v1alpha1/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/api/certificates/v1beta1/doc.go ================================================ [File too large to display: 788 B] ================================================ FILE: vendor/k8s.io/api/certificates/v1beta1/generated.pb.go ================================================ [File too large to display: 54.0 KB] ================================================ FILE: vendor/k8s.io/api/certificates/v1beta1/generated.proto ================================================ [File too large to display: 6.7 KB] ================================================ FILE: vendor/k8s.io/api/certificates/v1beta1/register.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/api/certificates/v1beta1/types.go ================================================ [File too large to display: 10.4 KB] ================================================ FILE: vendor/k8s.io/api/certificates/v1beta1/types_swagger_doc_generated.go ================================================ [File too large to display: 5.9 KB] ================================================ FILE: vendor/k8s.io/api/certificates/v1beta1/zz_generated.deepcopy.go ================================================ [File too large to display: 6.1 KB] ================================================ FILE: vendor/k8s.io/api/certificates/v1beta1/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 4.0 KB] ================================================ FILE: vendor/k8s.io/api/coordination/v1/doc.go ================================================ [File too large to display: 740 B] ================================================ FILE: vendor/k8s.io/api/coordination/v1/generated.pb.go ================================================ [File too large to display: 24.1 KB] ================================================ FILE: vendor/k8s.io/api/coordination/v1/generated.proto ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/api/coordination/v1/register.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/api/coordination/v1/types.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/k8s.io/api/coordination/v1/types_swagger_doc_generated.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/api/coordination/v1/zz_generated.deepcopy.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/k8s.io/api/coordination/v1beta1/doc.go ================================================ [File too large to display: 788 B] ================================================ FILE: vendor/k8s.io/api/coordination/v1beta1/generated.pb.go ================================================ [File too large to display: 24.2 KB] ================================================ FILE: vendor/k8s.io/api/coordination/v1beta1/generated.proto ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/api/coordination/v1beta1/register.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/api/coordination/v1beta1/types.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/api/coordination/v1beta1/types_swagger_doc_generated.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/api/coordination/v1beta1/zz_generated.deepcopy.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/k8s.io/api/coordination/v1beta1/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 3.8 KB] ================================================ FILE: vendor/k8s.io/api/core/v1/annotation_key_constants.go ================================================ [File too large to display: 8.8 KB] ================================================ FILE: vendor/k8s.io/api/core/v1/doc.go ================================================ [File too large to display: 746 B] ================================================ FILE: vendor/k8s.io/api/core/v1/generated.pb.go ================================================ [File too large to display: 1.7 MB] ================================================ FILE: vendor/k8s.io/api/core/v1/generated.proto ================================================ [File too large to display: 255.9 KB] ================================================ FILE: vendor/k8s.io/api/core/v1/lifecycle.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/api/core/v1/objectreference.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/k8s.io/api/core/v1/register.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/api/core/v1/resource.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/api/core/v1/taint.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/k8s.io/api/core/v1/toleration.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/api/core/v1/types.go ================================================ [File too large to display: 356.2 KB] ================================================ FILE: vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go ================================================ [File too large to display: 234.8 KB] ================================================ FILE: vendor/k8s.io/api/core/v1/well_known_labels.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/k8s.io/api/core/v1/well_known_taints.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/api/core/v1/zz_generated.deepcopy.go ================================================ [File too large to display: 171.9 KB] ================================================ FILE: vendor/k8s.io/api/discovery/v1/doc.go ================================================ [File too large to display: 733 B] ================================================ FILE: vendor/k8s.io/api/discovery/v1/generated.pb.go ================================================ [File too large to display: 55.7 KB] ================================================ FILE: vendor/k8s.io/api/discovery/v1/generated.proto ================================================ [File too large to display: 8.0 KB] ================================================ FILE: vendor/k8s.io/api/discovery/v1/register.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/api/discovery/v1/types.go ================================================ [File too large to display: 9.2 KB] ================================================ FILE: vendor/k8s.io/api/discovery/v1/types_swagger_doc_generated.go ================================================ [File too large to display: 7.9 KB] ================================================ FILE: vendor/k8s.io/api/discovery/v1/well_known_labels.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/k8s.io/api/discovery/v1/zz_generated.deepcopy.go ================================================ [File too large to display: 6.6 KB] ================================================ FILE: vendor/k8s.io/api/discovery/v1beta1/doc.go ================================================ [File too large to display: 781 B] ================================================ FILE: vendor/k8s.io/api/discovery/v1beta1/generated.pb.go ================================================ [File too large to display: 54.3 KB] ================================================ FILE: vendor/k8s.io/api/discovery/v1beta1/generated.proto ================================================ [File too large to display: 8.0 KB] ================================================ FILE: vendor/k8s.io/api/discovery/v1beta1/register.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/api/discovery/v1beta1/types.go ================================================ [File too large to display: 9.5 KB] ================================================ FILE: vendor/k8s.io/api/discovery/v1beta1/types_swagger_doc_generated.go ================================================ [File too large to display: 7.8 KB] ================================================ FILE: vendor/k8s.io/api/discovery/v1beta1/well_known_labels.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/k8s.io/api/discovery/v1beta1/zz_generated.deepcopy.go ================================================ [File too large to display: 6.5 KB] ================================================ FILE: vendor/k8s.io/api/discovery/v1beta1/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 3.8 KB] ================================================ FILE: vendor/k8s.io/api/events/v1/doc.go ================================================ [File too large to display: 728 B] ================================================ FILE: vendor/k8s.io/api/events/v1/generated.pb.go ================================================ [File too large to display: 35.7 KB] ================================================ FILE: vendor/k8s.io/api/events/v1/generated.proto ================================================ [File too large to display: 5.5 KB] ================================================ FILE: vendor/k8s.io/api/events/v1/register.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/api/events/v1/types.go ================================================ [File too large to display: 6.1 KB] ================================================ FILE: vendor/k8s.io/api/events/v1/types_swagger_doc_generated.go ================================================ [File too large to display: 5.3 KB] ================================================ FILE: vendor/k8s.io/api/events/v1/zz_generated.deepcopy.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/k8s.io/api/events/v1beta1/doc.go ================================================ [File too large to display: 776 B] ================================================ FILE: vendor/k8s.io/api/events/v1beta1/generated.pb.go ================================================ [File too large to display: 35.8 KB] ================================================ FILE: vendor/k8s.io/api/events/v1beta1/generated.proto ================================================ [File too large to display: 5.2 KB] ================================================ FILE: vendor/k8s.io/api/events/v1beta1/register.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/api/events/v1beta1/types.go ================================================ [File too large to display: 6.1 KB] ================================================ FILE: vendor/k8s.io/api/events/v1beta1/types_swagger_doc_generated.go ================================================ [File too large to display: 4.9 KB] ================================================ FILE: vendor/k8s.io/api/events/v1beta1/zz_generated.deepcopy.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/k8s.io/api/events/v1beta1/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/k8s.io/api/extensions/v1beta1/doc.go ================================================ [File too large to display: 751 B] ================================================ FILE: vendor/k8s.io/api/extensions/v1beta1/generated.pb.go ================================================ [File too large to display: 298.9 KB] ================================================ FILE: vendor/k8s.io/api/extensions/v1beta1/generated.proto ================================================ [File too large to display: 45.6 KB] ================================================ FILE: vendor/k8s.io/api/extensions/v1beta1/register.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/api/extensions/v1beta1/types.go ================================================ [File too large to display: 62.9 KB] ================================================ FILE: vendor/k8s.io/api/extensions/v1beta1/types_swagger_doc_generated.go ================================================ [File too large to display: 43.0 KB] ================================================ FILE: vendor/k8s.io/api/extensions/v1beta1/zz_generated.deepcopy.go ================================================ [File too large to display: 33.9 KB] ================================================ FILE: vendor/k8s.io/api/extensions/v1beta1/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 18.2 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1alpha1/doc.go ================================================ [File too large to display: 896 B] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1alpha1/generated.pb.go ================================================ [File too large to display: 136.0 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1alpha1/generated.proto ================================================ [File too large to display: 19.4 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1alpha1/register.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1alpha1/types.go ================================================ [File too large to display: 26.9 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1alpha1/types_swagger_doc_generated.go ================================================ [File too large to display: 19.3 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1alpha1/zz_generated.deepcopy.go ================================================ [File too large to display: 16.2 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1alpha1/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 7.1 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1beta1/doc.go ================================================ [File too large to display: 893 B] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1beta1/generated.pb.go ================================================ [File too large to display: 136.0 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1beta1/generated.proto ================================================ [File too large to display: 19.4 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1beta1/register.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1beta1/types.go ================================================ [File too large to display: 28.7 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1beta1/types_swagger_doc_generated.go ================================================ [File too large to display: 19.3 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1beta1/zz_generated.deepcopy.go ================================================ [File too large to display: 16.2 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1beta1/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 7.1 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1beta2/doc.go ================================================ [File too large to display: 893 B] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1beta2/generated.pb.go ================================================ [File too large to display: 136.0 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1beta2/generated.proto ================================================ [File too large to display: 19.4 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1beta2/register.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1beta2/types.go ================================================ [File too large to display: 28.7 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1beta2/types_swagger_doc_generated.go ================================================ [File too large to display: 19.3 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1beta2/zz_generated.deepcopy.go ================================================ [File too large to display: 16.2 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1beta2/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 7.1 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1beta3/doc.go ================================================ [File too large to display: 892 B] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1beta3/generated.pb.go ================================================ [File too large to display: 136.0 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1beta3/generated.proto ================================================ [File too large to display: 19.5 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1beta3/register.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1beta3/types.go ================================================ [File too large to display: 28.5 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1beta3/types_swagger_doc_generated.go ================================================ [File too large to display: 19.7 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1beta3/zz_generated.deepcopy.go ================================================ [File too large to display: 16.2 KB] ================================================ FILE: vendor/k8s.io/api/flowcontrol/v1beta3/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 5.2 KB] ================================================ FILE: vendor/k8s.io/api/networking/v1/doc.go ================================================ [File too large to display: 735 B] ================================================ FILE: vendor/k8s.io/api/networking/v1/generated.pb.go ================================================ [File too large to display: 163.9 KB] ================================================ FILE: vendor/k8s.io/api/networking/v1/generated.proto ================================================ [File too large to display: 25.2 KB] ================================================ FILE: vendor/k8s.io/api/networking/v1/register.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/api/networking/v1/types.go ================================================ [File too large to display: 31.7 KB] ================================================ FILE: vendor/k8s.io/api/networking/v1/types_swagger_doc_generated.go ================================================ [File too large to display: 24.2 KB] ================================================ FILE: vendor/k8s.io/api/networking/v1/well_known_annotations.go ================================================ [File too large to display: 951 B] ================================================ FILE: vendor/k8s.io/api/networking/v1/zz_generated.deepcopy.go ================================================ [File too large to display: 20.7 KB] ================================================ FILE: vendor/k8s.io/api/networking/v1alpha1/doc.go ================================================ [File too large to display: 785 B] ================================================ FILE: vendor/k8s.io/api/networking/v1alpha1/generated.pb.go ================================================ [File too large to display: 45.5 KB] ================================================ FILE: vendor/k8s.io/api/networking/v1alpha1/generated.proto ================================================ [File too large to display: 6.0 KB] ================================================ FILE: vendor/k8s.io/api/networking/v1alpha1/register.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/api/networking/v1alpha1/types.go ================================================ [File too large to display: 7.0 KB] ================================================ FILE: vendor/k8s.io/api/networking/v1alpha1/types_swagger_doc_generated.go ================================================ [File too large to display: 6.0 KB] ================================================ FILE: vendor/k8s.io/api/networking/v1alpha1/well_known_labels.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/k8s.io/api/networking/v1alpha1/zz_generated.deepcopy.go ================================================ [File too large to display: 5.6 KB] ================================================ FILE: vendor/k8s.io/api/networking/v1alpha1/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 5.1 KB] ================================================ FILE: vendor/k8s.io/api/networking/v1beta1/doc.go ================================================ [File too large to display: 783 B] ================================================ FILE: vendor/k8s.io/api/networking/v1beta1/generated.pb.go ================================================ [File too large to display: 101.6 KB] ================================================ FILE: vendor/k8s.io/api/networking/v1beta1/generated.proto ================================================ [File too large to display: 14.9 KB] ================================================ FILE: vendor/k8s.io/api/networking/v1beta1/register.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/api/networking/v1beta1/types.go ================================================ [File too large to display: 19.6 KB] ================================================ FILE: vendor/k8s.io/api/networking/v1beta1/types_swagger_doc_generated.go ================================================ [File too large to display: 14.3 KB] ================================================ FILE: vendor/k8s.io/api/networking/v1beta1/well_known_annotations.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/k8s.io/api/networking/v1beta1/zz_generated.deepcopy.go ================================================ [File too large to display: 12.4 KB] ================================================ FILE: vendor/k8s.io/api/networking/v1beta1/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 6.9 KB] ================================================ FILE: vendor/k8s.io/api/node/v1/doc.go ================================================ [File too large to display: 724 B] ================================================ FILE: vendor/k8s.io/api/node/v1/generated.pb.go ================================================ [File too large to display: 36.2 KB] ================================================ FILE: vendor/k8s.io/api/node/v1/generated.proto ================================================ [File too large to display: 4.4 KB] ================================================ FILE: vendor/k8s.io/api/node/v1/register.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/api/node/v1/types.go ================================================ [File too large to display: 4.7 KB] ================================================ FILE: vendor/k8s.io/api/node/v1/types_swagger_doc_generated.go ================================================ [File too large to display: 4.3 KB] ================================================ FILE: vendor/k8s.io/api/node/v1/zz_generated.deepcopy.go ================================================ [File too large to display: 4.0 KB] ================================================ FILE: vendor/k8s.io/api/node/v1alpha1/doc.go ================================================ [File too large to display: 736 B] ================================================ FILE: vendor/k8s.io/api/node/v1alpha1/generated.pb.go ================================================ [File too large to display: 40.3 KB] ================================================ FILE: vendor/k8s.io/api/node/v1alpha1/generated.proto ================================================ [File too large to display: 5.0 KB] ================================================ FILE: vendor/k8s.io/api/node/v1alpha1/register.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/api/node/v1alpha1/types.go ================================================ [File too large to display: 5.3 KB] ================================================ FILE: vendor/k8s.io/api/node/v1alpha1/types_swagger_doc_generated.go ================================================ [File too large to display: 5.0 KB] ================================================ FILE: vendor/k8s.io/api/node/v1alpha1/zz_generated.deepcopy.go ================================================ [File too large to display: 4.5 KB] ================================================ FILE: vendor/k8s.io/api/node/v1beta1/doc.go ================================================ [File too large to display: 772 B] ================================================ FILE: vendor/k8s.io/api/node/v1beta1/generated.pb.go ================================================ [File too large to display: 36.3 KB] ================================================ FILE: vendor/k8s.io/api/node/v1beta1/generated.proto ================================================ [File too large to display: 4.4 KB] ================================================ FILE: vendor/k8s.io/api/node/v1beta1/register.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/api/node/v1beta1/types.go ================================================ [File too large to display: 4.9 KB] ================================================ FILE: vendor/k8s.io/api/node/v1beta1/types_swagger_doc_generated.go ================================================ [File too large to display: 4.4 KB] ================================================ FILE: vendor/k8s.io/api/node/v1beta1/zz_generated.deepcopy.go ================================================ [File too large to display: 4.0 KB] ================================================ FILE: vendor/k8s.io/api/node/v1beta1/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/k8s.io/api/policy/v1/doc.go ================================================ [File too large to display: 871 B] ================================================ FILE: vendor/k8s.io/api/policy/v1/generated.pb.go ================================================ [File too large to display: 45.2 KB] ================================================ FILE: vendor/k8s.io/api/policy/v1/generated.proto ================================================ [File too large to display: 8.0 KB] ================================================ FILE: vendor/k8s.io/api/policy/v1/register.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/api/policy/v1/types.go ================================================ [File too large to display: 10.6 KB] ================================================ FILE: vendor/k8s.io/api/policy/v1/types_swagger_doc_generated.go ================================================ [File too large to display: 7.6 KB] ================================================ FILE: vendor/k8s.io/api/policy/v1/zz_generated.deepcopy.go ================================================ [File too large to display: 5.4 KB] ================================================ FILE: vendor/k8s.io/api/policy/v1beta1/doc.go ================================================ [File too large to display: 919 B] ================================================ FILE: vendor/k8s.io/api/policy/v1beta1/generated.pb.go ================================================ [File too large to display: 142.5 KB] ================================================ FILE: vendor/k8s.io/api/policy/v1beta1/generated.proto ================================================ [File too large to display: 19.6 KB] ================================================ FILE: vendor/k8s.io/api/policy/v1beta1/register.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/api/policy/v1beta1/types.go ================================================ [File too large to display: 30.7 KB] ================================================ FILE: vendor/k8s.io/api/policy/v1beta1/types_swagger_doc_generated.go ================================================ [File too large to display: 19.3 KB] ================================================ FILE: vendor/k8s.io/api/policy/v1beta1/zz_generated.deepcopy.go ================================================ [File too large to display: 16.3 KB] ================================================ FILE: vendor/k8s.io/api/policy/v1beta1/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 7.3 KB] ================================================ FILE: vendor/k8s.io/api/rbac/v1/doc.go ================================================ [File too large to display: 738 B] ================================================ FILE: vendor/k8s.io/api/rbac/v1/generated.pb.go ================================================ [File too large to display: 77.6 KB] ================================================ FILE: vendor/k8s.io/api/rbac/v1/generated.proto ================================================ [File too large to display: 7.6 KB] ================================================ FILE: vendor/k8s.io/api/rbac/v1/register.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/api/rbac/v1/types.go ================================================ [File too large to display: 10.1 KB] ================================================ FILE: vendor/k8s.io/api/rbac/v1/types_swagger_doc_generated.go ================================================ [File too large to display: 7.8 KB] ================================================ FILE: vendor/k8s.io/api/rbac/v1/zz_generated.deepcopy.go ================================================ [File too large to display: 10.6 KB] ================================================ FILE: vendor/k8s.io/api/rbac/v1alpha1/doc.go ================================================ [File too large to display: 750 B] ================================================ FILE: vendor/k8s.io/api/rbac/v1alpha1/generated.pb.go ================================================ [File too large to display: 77.9 KB] ================================================ FILE: vendor/k8s.io/api/rbac/v1alpha1/generated.proto ================================================ [File too large to display: 8.5 KB] ================================================ FILE: vendor/k8s.io/api/rbac/v1alpha1/register.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/api/rbac/v1alpha1/types.go ================================================ [File too large to display: 11.0 KB] ================================================ FILE: vendor/k8s.io/api/rbac/v1alpha1/types_swagger_doc_generated.go ================================================ [File too large to display: 8.7 KB] ================================================ FILE: vendor/k8s.io/api/rbac/v1alpha1/zz_generated.deepcopy.go ================================================ [File too large to display: 10.6 KB] ================================================ FILE: vendor/k8s.io/api/rbac/v1beta1/doc.go ================================================ [File too large to display: 786 B] ================================================ FILE: vendor/k8s.io/api/rbac/v1beta1/generated.pb.go ================================================ [File too large to display: 77.7 KB] ================================================ FILE: vendor/k8s.io/api/rbac/v1beta1/generated.proto ================================================ [File too large to display: 8.6 KB] ================================================ FILE: vendor/k8s.io/api/rbac/v1beta1/register.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/api/rbac/v1beta1/types.go ================================================ [File too large to display: 12.9 KB] ================================================ FILE: vendor/k8s.io/api/rbac/v1beta1/types_swagger_doc_generated.go ================================================ [File too large to display: 8.8 KB] ================================================ FILE: vendor/k8s.io/api/rbac/v1beta1/zz_generated.deepcopy.go ================================================ [File too large to display: 10.6 KB] ================================================ FILE: vendor/k8s.io/api/rbac/v1beta1/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 13.1 KB] ================================================ FILE: vendor/k8s.io/api/resource/v1alpha2/doc.go ================================================ [File too large to display: 809 B] ================================================ FILE: vendor/k8s.io/api/resource/v1alpha2/generated.pb.go ================================================ [File too large to display: 120.2 KB] ================================================ FILE: vendor/k8s.io/api/resource/v1alpha2/generated.proto ================================================ [File too large to display: 14.4 KB] ================================================ FILE: vendor/k8s.io/api/resource/v1alpha2/register.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/api/resource/v1alpha2/types.go ================================================ [File too large to display: 18.9 KB] ================================================ FILE: vendor/k8s.io/api/resource/v1alpha2/types_swagger_doc_generated.go ================================================ [File too large to display: 14.4 KB] ================================================ FILE: vendor/k8s.io/api/resource/v1alpha2/zz_generated.deepcopy.go ================================================ [File too large to display: 14.9 KB] ================================================ FILE: vendor/k8s.io/api/scheduling/v1/doc.go ================================================ [File too large to display: 736 B] ================================================ FILE: vendor/k8s.io/api/scheduling/v1/generated.pb.go ================================================ [File too large to display: 19.0 KB] ================================================ FILE: vendor/k8s.io/api/scheduling/v1/generated.proto ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/k8s.io/api/scheduling/v1/register.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/api/scheduling/v1/types.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/api/scheduling/v1/types_swagger_doc_generated.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/k8s.io/api/scheduling/v1/zz_generated.deepcopy.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/api/scheduling/v1alpha1/doc.go ================================================ [File too large to display: 748 B] ================================================ FILE: vendor/k8s.io/api/scheduling/v1alpha1/generated.pb.go ================================================ [File too large to display: 19.0 KB] ================================================ FILE: vendor/k8s.io/api/scheduling/v1alpha1/generated.proto ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/k8s.io/api/scheduling/v1alpha1/register.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/api/scheduling/v1alpha1/types.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/api/scheduling/v1alpha1/types_swagger_doc_generated.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/api/scheduling/v1alpha1/zz_generated.deepcopy.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/api/scheduling/v1beta1/doc.go ================================================ [File too large to display: 784 B] ================================================ FILE: vendor/k8s.io/api/scheduling/v1beta1/generated.pb.go ================================================ [File too large to display: 19.0 KB] ================================================ FILE: vendor/k8s.io/api/scheduling/v1beta1/generated.proto ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/k8s.io/api/scheduling/v1beta1/register.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/api/scheduling/v1beta1/types.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/k8s.io/api/scheduling/v1beta1/types_swagger_doc_generated.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/api/scheduling/v1beta1/zz_generated.deepcopy.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/api/scheduling/v1beta1/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 3.9 KB] ================================================ FILE: vendor/k8s.io/api/storage/v1/doc.go ================================================ [File too large to display: 729 B] ================================================ FILE: vendor/k8s.io/api/storage/v1/generated.pb.go ================================================ [File too large to display: 133.9 KB] ================================================ FILE: vendor/k8s.io/api/storage/v1/generated.proto ================================================ [File too large to display: 24.7 KB] ================================================ FILE: vendor/k8s.io/api/storage/v1/register.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/api/storage/v1/types.go ================================================ [File too large to display: 31.4 KB] ================================================ FILE: vendor/k8s.io/api/storage/v1/types_swagger_doc_generated.go ================================================ [File too large to display: 23.1 KB] ================================================ FILE: vendor/k8s.io/api/storage/v1/zz_generated.deepcopy.go ================================================ [File too large to display: 17.1 KB] ================================================ FILE: vendor/k8s.io/api/storage/v1alpha1/doc.go ================================================ [File too large to display: 779 B] ================================================ FILE: vendor/k8s.io/api/storage/v1alpha1/generated.pb.go ================================================ [File too large to display: 61.1 KB] ================================================ FILE: vendor/k8s.io/api/storage/v1alpha1/generated.proto ================================================ [File too large to display: 9.4 KB] ================================================ FILE: vendor/k8s.io/api/storage/v1alpha1/register.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/api/storage/v1alpha1/types.go ================================================ [File too large to display: 11.1 KB] ================================================ FILE: vendor/k8s.io/api/storage/v1alpha1/types_swagger_doc_generated.go ================================================ [File too large to display: 8.7 KB] ================================================ FILE: vendor/k8s.io/api/storage/v1alpha1/zz_generated.deepcopy.go ================================================ [File too large to display: 7.4 KB] ================================================ FILE: vendor/k8s.io/api/storage/v1alpha1/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 7.0 KB] ================================================ FILE: vendor/k8s.io/api/storage/v1beta1/doc.go ================================================ [File too large to display: 777 B] ================================================ FILE: vendor/k8s.io/api/storage/v1beta1/generated.pb.go ================================================ [File too large to display: 134.1 KB] ================================================ FILE: vendor/k8s.io/api/storage/v1beta1/generated.proto ================================================ [File too large to display: 24.9 KB] ================================================ FILE: vendor/k8s.io/api/storage/v1beta1/register.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/api/storage/v1beta1/types.go ================================================ [File too large to display: 33.1 KB] ================================================ FILE: vendor/k8s.io/api/storage/v1beta1/types_swagger_doc_generated.go ================================================ [File too large to display: 23.4 KB] ================================================ FILE: vendor/k8s.io/api/storage/v1beta1/zz_generated.deepcopy.go ================================================ [File too large to display: 17.1 KB] ================================================ FILE: vendor/k8s.io/api/storage/v1beta1/zz_generated.prerelease-lifecycle.go ================================================ [File too large to display: 16.2 KB] ================================================ FILE: vendor/k8s.io/apimachinery/LICENSE ================================================ [File too large to display: 11.1 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/equality/semantic.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/errors/OWNERS ================================================ [File too large to display: 256 B] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/errors/doc.go ================================================ [File too large to display: 707 B] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/errors/errors.go ================================================ [File too large to display: 30.5 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/meta/OWNERS ================================================ [File too large to display: 212 B] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/meta/conditions.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/meta/doc.go ================================================ [File too large to display: 740 B] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/meta/errors.go ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/meta/firsthit_restmapper.go ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/meta/help.go ================================================ [File too large to display: 6.9 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/meta/interfaces.go ================================================ [File too large to display: 5.5 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/meta/lazy.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/meta/meta.go ================================================ [File too large to display: 16.5 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/meta/multirestmapper.go ================================================ [File too large to display: 5.8 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/meta/priority.go ================================================ [File too large to display: 7.6 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/meta/restmapper.go ================================================ [File too large to display: 16.1 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/resource/OWNERS ================================================ [File too large to display: 179 B] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/resource/amount.go ================================================ [File too large to display: 8.6 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/resource/generated.pb.go ================================================ [File too large to display: 4.7 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/resource/generated.proto ================================================ [File too large to display: 3.9 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/resource/math.go ================================================ [File too large to display: 7.3 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/resource/quantity.go ================================================ [File too large to display: 22.9 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/resource/quantity_proto.go ================================================ [File too large to display: 6.0 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/resource/scale_int.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/resource/suffix.go ================================================ [File too large to display: 5.2 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/resource/zz_generated.deepcopy.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/validation/doc.go ================================================ [File too large to display: 711 B] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/validation/generic.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/api/validation/objectmeta.go ================================================ [File too large to display: 12.0 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/OWNERS ================================================ [File too large to display: 223 B] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/controller_ref.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/conversion.go ================================================ [File too large to display: 8.8 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/deepcopy.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/doc.go ================================================ [File too large to display: 773 B] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/duration.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/generated.pb.go ================================================ [File too large to display: 281.6 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto ================================================ [File too large to display: 53.3 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/group_version.go ================================================ [File too large to display: 4.8 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/helpers.go ================================================ [File too large to display: 9.6 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/labels.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/meta.go ================================================ [File too large to display: 7.7 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/micro_time.go ================================================ [File too large to display: 4.7 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/micro_time_fuzz.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/micro_time_proto.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/register.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/time.go ================================================ [File too large to display: 4.7 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/time_fuzz.go ================================================ [File too large to display: 1.0 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/time_proto.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types.go ================================================ [File too large to display: 77.5 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types_swagger_doc_generated.go ================================================ [File too large to display: 49.2 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/helpers.go ================================================ [File too large to display: 16.3 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured.go ================================================ [File too large to display: 13.5 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured_list.go ================================================ [File too large to display: 6.0 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructuredscheme/scheme.go ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/zz_generated.deepcopy.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/validation/validation.go ================================================ [File too large to display: 13.5 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/watch.go ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/zz_generated.conversion.go ================================================ [File too large to display: 21.6 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/zz_generated.deepcopy.go ================================================ [File too large to display: 32.2 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/apis/meta/v1/zz_generated.defaults.go ================================================ [File too large to display: 1007 B] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/conversion/converter.go ================================================ [File too large to display: 7.4 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/conversion/deep_equal.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/conversion/doc.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/conversion/helper.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/conversion/queryparams/convert.go ================================================ [File too large to display: 4.9 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/conversion/queryparams/doc.go ================================================ [File too large to display: 747 B] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/fields/doc.go ================================================ [File too large to display: 736 B] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/fields/fields.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/fields/requirements.go ================================================ [File too large to display: 974 B] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/fields/selector.go ================================================ [File too large to display: 12.3 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/labels/doc.go ================================================ [File too large to display: 736 B] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/labels/labels.go ================================================ [File too large to display: 4.5 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/labels/selector.go ================================================ [File too large to display: 31.8 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/labels/zz_generated.deepcopy.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/allocator.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/codec.go ================================================ [File too large to display: 12.2 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/codec_check.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/conversion.go ================================================ [File too large to display: 5.2 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/converter.go ================================================ [File too large to display: 24.9 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/doc.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/embedded.go ================================================ [File too large to display: 4.3 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/error.go ================================================ [File too large to display: 4.7 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/extension.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/generated.pb.go ================================================ [File too large to display: 20.7 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/generated.proto ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/helper.go ================================================ [File too large to display: 7.6 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/interfaces.go ================================================ [File too large to display: 18.6 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/mapper.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/negotiate.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/register.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/schema/generated.pb.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/schema/generated.proto ================================================ [File too large to display: 829 B] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/schema/group_version.go ================================================ [File too large to display: 10.2 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/schema/interfaces.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/scheme.go ================================================ [File too large to display: 25.2 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/scheme_builder.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/serializer/codec_factory.go ================================================ [File too large to display: 12.6 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/serializer/json/json.go ================================================ [File too large to display: 12.0 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/serializer/json/meta.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/serializer/negotiated_codec.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/serializer/protobuf/doc.go ================================================ [File too large to display: 730 B] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/serializer/protobuf/protobuf.go ================================================ [File too large to display: 17.8 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/serializer/recognizer/recognizer.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/serializer/streaming/streaming.go ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/serializer/versioning/versioning.go ================================================ [File too large to display: 9.8 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/swagger_doc_generator.go ================================================ [File too large to display: 7.0 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/types.go ================================================ [File too large to display: 4.7 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/types_proto.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/runtime/zz_generated.deepcopy.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/selection/operator.go ================================================ [File too large to display: 1.0 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/types/doc.go ================================================ [File too large to display: 705 B] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/types/namespacedname.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/types/nodename.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/types/patch.go ================================================ [File too large to display: 1.0 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/types/uid.go ================================================ [File too large to display: 825 B] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/duration/duration.go ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/errors/doc.go ================================================ [File too large to display: 714 B] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/errors/errors.go ================================================ [File too large to display: 6.3 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/framer/framer.go ================================================ [File too large to display: 4.8 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/httpstream/doc.go ================================================ [File too large to display: 758 B] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/httpstream/httpstream.go ================================================ [File too large to display: 5.8 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/connection.go ================================================ [File too large to display: 6.7 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/roundtripper.go ================================================ [File too large to display: 11.4 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/upgrade.go ================================================ [File too large to display: 4.0 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/intstr/generated.pb.go ================================================ [File too large to display: 9.7 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/intstr/generated.proto ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/intstr/instr_fuzz.go ================================================ [File too large to display: 1.0 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/intstr/intstr.go ================================================ [File too large to display: 7.1 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/json/json.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/managedfields/endpoints.yaml ================================================ [File too large to display: 183.9 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/managedfields/extract.go ================================================ [File too large to display: 4.4 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/managedfields/fieldmanager.go ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/managedfields/gvkparser.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/atmostevery.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/buildmanagerinfo.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/capmanagers.go ================================================ [File too large to display: 4.6 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/conflict.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/fieldmanager.go ================================================ [File too large to display: 7.2 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/fields.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/lastapplied.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/lastappliedmanager.go ================================================ [File too large to display: 5.9 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/lastappliedupdater.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/managedfields.go ================================================ [File too large to display: 8.1 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/managedfieldsupdater.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/manager.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/pathelement.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/skipnonapplied.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/stripmeta.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/structuredmerge.go ================================================ [File too large to display: 7.0 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/typeconverter.go ================================================ [File too large to display: 5.4 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/managedfields/internal/versionconverter.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/managedfields/node.yaml ================================================ [File too large to display: 10.8 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/managedfields/pod.yaml ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/managedfields/scalehandler.go ================================================ [File too large to display: 5.5 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/managedfields/typeconverter.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/naming/from_stack.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/net/http.go ================================================ [File too large to display: 20.8 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/net/interface.go ================================================ [File too large to display: 14.7 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/net/port_range.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/net/port_split.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/net/util.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/remotecommand/constants.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/runtime/runtime.go ================================================ [File too large to display: 5.4 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/sets/byte.go ================================================ [File too large to display: 3.9 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/sets/doc.go ================================================ [File too large to display: 724 B] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/sets/empty.go ================================================ [File too large to display: 789 B] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/sets/int.go ================================================ [File too large to display: 3.8 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/sets/int32.go ================================================ [File too large to display: 3.9 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/sets/int64.go ================================================ [File too large to display: 3.9 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/sets/ordered.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/sets/set.go ================================================ [File too large to display: 6.0 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/sets/string.go ================================================ [File too large to display: 4.0 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/validation/field/errors.go ================================================ [File too large to display: 11.1 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/validation/field/path.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/validation/validation.go ================================================ [File too large to display: 18.4 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/wait/backoff.go ================================================ [File too large to display: 16.5 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/wait/delay.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/wait/doc.go ================================================ [File too large to display: 717 B] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/wait/error.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/wait/loop.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/wait/poll.go ================================================ [File too large to display: 14.0 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/wait/timer.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/wait/wait.go ================================================ [File too large to display: 7.1 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/util/yaml/decoder.go ================================================ [File too large to display: 10.2 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/version/doc.go ================================================ [File too large to display: 743 B] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/version/helpers.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/version/types.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/watch/doc.go ================================================ [File too large to display: 746 B] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/watch/filter.go ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/watch/mux.go ================================================ [File too large to display: 9.4 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/watch/streamwatcher.go ================================================ [File too large to display: 3.9 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/watch/watch.go ================================================ [File too large to display: 7.2 KB] ================================================ FILE: vendor/k8s.io/apimachinery/pkg/watch/zz_generated.deepcopy.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/k8s.io/apimachinery/third_party/forked/golang/LICENSE ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/apimachinery/third_party/forked/golang/PATENTS ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/k8s.io/apimachinery/third_party/forked/golang/netutil/addr.go ================================================ [File too large to display: 723 B] ================================================ FILE: vendor/k8s.io/apimachinery/third_party/forked/golang/reflect/deep_equal.go ================================================ [File too large to display: 10.8 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/LICENSE ================================================ [File too large to display: 11.1 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/genericclioptions/builder_flags.go ================================================ [File too large to display: 6.7 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/genericclioptions/builder_flags_fake.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/genericclioptions/client_config.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/genericclioptions/command_headers.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/genericclioptions/config_flags.go ================================================ [File too large to display: 16.3 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/genericclioptions/config_flags_fake.go ================================================ [File too large to display: 3.9 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/genericclioptions/doc.go ================================================ [File too large to display: 837 B] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/genericclioptions/filename_flags.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/genericclioptions/io_options.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/genericclioptions/json_yaml_flags.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/genericclioptions/jsonpath_flags.go ================================================ [File too large to display: 4.3 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/genericclioptions/kube_template_flags.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/genericclioptions/name_flags.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/genericclioptions/print_flags.go ================================================ [File too large to display: 5.6 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/genericclioptions/record_flags.go ================================================ [File too large to display: 5.5 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/genericclioptions/template_flags.go ================================================ [File too large to display: 4.4 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/printers/discard.go ================================================ [File too large to display: 843 B] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/printers/doc.go ================================================ [File too large to display: 736 B] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/printers/interface.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/printers/json.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/printers/jsonpath.go ================================================ [File too large to display: 4.3 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/printers/managedfields.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/printers/name.go ================================================ [File too large to display: 3.9 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/printers/sourcechecker.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/printers/tableprinter.go ================================================ [File too large to display: 16.7 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/printers/tabwriter.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/printers/template.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/printers/terminal.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/printers/typesetter.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/printers/warningprinter.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/printers/yaml.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/resource/builder.go ================================================ [File too large to display: 36.9 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/resource/client.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/resource/crd_finder.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/resource/doc.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/resource/fake.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/resource/fallback_query_param_verifier.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/resource/helper.go ================================================ [File too large to display: 10.5 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/resource/interfaces.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/resource/kustomizevisitor.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/resource/mapper.go ================================================ [File too large to display: 4.8 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/resource/metadata_decoder.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/resource/query_param_verifier.go ================================================ [File too large to display: 5.2 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/resource/query_param_verifier_v3.go ================================================ [File too large to display: 4.9 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/resource/result.go ================================================ [File too large to display: 7.3 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/resource/scheme.go ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/resource/selector.go ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/k8s.io/cli-runtime/pkg/resource/visitor.go ================================================ [File too large to display: 20.7 KB] ================================================ FILE: vendor/k8s.io/client-go/LICENSE ================================================ [File too large to display: 11.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1/matchcondition.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1/mutatingwebhook.go ================================================ [File too large to display: 8.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1/mutatingwebhookconfiguration.go ================================================ [File too large to display: 13.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1/rule.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1/rulewithoperations.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1/servicereference.go ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1/validatingwebhook.go ================================================ [File too large to display: 7.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1/validatingwebhookconfiguration.go ================================================ [File too large to display: 13.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1/webhookclientconfig.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/auditannotation.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/expressionwarning.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/matchcondition.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/matchresources.go ================================================ [File too large to display: 4.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/namedrulewithoperations.go ================================================ [File too large to display: 4.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/paramkind.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/paramref.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/typechecking.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/validatingadmissionpolicy.go ================================================ [File too large to display: 13.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/validatingadmissionpolicybinding.go ================================================ [File too large to display: 13.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/validatingadmissionpolicybindingspec.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/validatingadmissionpolicyspec.go ================================================ [File too large to display: 5.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/validatingadmissionpolicystatus.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/validation.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1/matchcondition.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1/mutatingwebhook.go ================================================ [File too large to display: 8.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1/mutatingwebhookconfiguration.go ================================================ [File too large to display: 13.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1/servicereference.go ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1/validatingwebhook.go ================================================ [File too large to display: 7.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1/validatingwebhookconfiguration.go ================================================ [File too large to display: 13.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1/webhookclientconfig.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apiserverinternal/v1alpha1/serverstorageversion.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apiserverinternal/v1alpha1/storageversion.go ================================================ [File too large to display: 12.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apiserverinternal/v1alpha1/storageversioncondition.go ================================================ [File too large to display: 4.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apiserverinternal/v1alpha1/storageversionstatus.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1/controllerrevision.go ================================================ [File too large to display: 12.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1/daemonset.go ================================================ [File too large to display: 12.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1/daemonsetcondition.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1/daemonsetspec.go ================================================ [File too large to display: 3.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1/daemonsetstatus.go ================================================ [File too large to display: 6.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1/daemonsetupdatestrategy.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1/deployment.go ================================================ [File too large to display: 12.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1/deploymentcondition.go ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1/deploymentspec.go ================================================ [File too large to display: 5.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1/deploymentstatus.go ================================================ [File too large to display: 5.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1/deploymentstrategy.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1/replicaset.go ================================================ [File too large to display: 12.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1/replicasetcondition.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1/replicasetspec.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1/replicasetstatus.go ================================================ [File too large to display: 4.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1/rollingupdatedaemonset.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1/rollingupdatedeployment.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1/rollingupdatestatefulsetstrategy.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1/statefulset.go ================================================ [File too large to display: 12.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1/statefulsetcondition.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1/statefulsetordinals.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1/statefulsetpersistentvolumeclaimretentionpolicy.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1/statefulsetspec.go ================================================ [File too large to display: 8.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1/statefulsetstatus.go ================================================ [File too large to display: 6.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1/statefulsetupdatestrategy.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta1/controllerrevision.go ================================================ [File too large to display: 12.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta1/deployment.go ================================================ [File too large to display: 12.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta1/deploymentcondition.go ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta1/deploymentspec.go ================================================ [File too large to display: 5.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta1/deploymentstatus.go ================================================ [File too large to display: 5.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta1/deploymentstrategy.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta1/rollbackconfig.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta1/rollingupdatedeployment.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta1/rollingupdatestatefulsetstrategy.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta1/statefulset.go ================================================ [File too large to display: 12.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta1/statefulsetcondition.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta1/statefulsetordinals.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta1/statefulsetpersistentvolumeclaimretentionpolicy.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta1/statefulsetspec.go ================================================ [File too large to display: 8.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta1/statefulsetstatus.go ================================================ [File too large to display: 6.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta1/statefulsetupdatestrategy.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/controllerrevision.go ================================================ [File too large to display: 12.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/daemonset.go ================================================ [File too large to display: 12.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/daemonsetcondition.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/daemonsetspec.go ================================================ [File too large to display: 3.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/daemonsetstatus.go ================================================ [File too large to display: 6.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/daemonsetupdatestrategy.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/deployment.go ================================================ [File too large to display: 12.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/deploymentcondition.go ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/deploymentspec.go ================================================ [File too large to display: 5.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/deploymentstatus.go ================================================ [File too large to display: 5.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/deploymentstrategy.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/replicaset.go ================================================ [File too large to display: 12.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/replicasetcondition.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/replicasetspec.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/replicasetstatus.go ================================================ [File too large to display: 4.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/rollingupdatedaemonset.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/rollingupdatedeployment.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/rollingupdatestatefulsetstrategy.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/scale.go ================================================ [File too large to display: 9.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/statefulset.go ================================================ [File too large to display: 12.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/statefulsetcondition.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/statefulsetordinals.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/statefulsetpersistentvolumeclaimretentionpolicy.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/statefulsetspec.go ================================================ [File too large to display: 8.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/statefulsetstatus.go ================================================ [File too large to display: 6.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/apps/v1beta2/statefulsetupdatestrategy.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v1/crossversionobjectreference.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v1/horizontalpodautoscaler.go ================================================ [File too large to display: 13.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v1/horizontalpodautoscalerspec.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v1/horizontalpodautoscalerstatus.go ================================================ [File too large to display: 4.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v1/scale.go ================================================ [File too large to display: 9.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v1/scalespec.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v1/scalestatus.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2/containerresourcemetricsource.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2/containerresourcemetricstatus.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2/crossversionobjectreference.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2/externalmetricsource.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2/externalmetricstatus.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2/horizontalpodautoscaler.go ================================================ [File too large to display: 13.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2/horizontalpodautoscalerbehavior.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2/horizontalpodautoscalercondition.go ================================================ [File too large to display: 4.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2/horizontalpodautoscalerspec.go ================================================ [File too large to display: 4.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2/horizontalpodautoscalerstatus.go ================================================ [File too large to display: 5.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2/hpascalingpolicy.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2/hpascalingrules.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2/metricidentifier.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2/metricspec.go ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2/metricstatus.go ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2/metrictarget.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2/metricvaluestatus.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2/objectmetricsource.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2/objectmetricstatus.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2/podsmetricsource.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2/podsmetricstatus.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2/resourcemetricsource.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2/resourcemetricstatus.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta1/containerresourcemetricsource.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta1/containerresourcemetricstatus.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta1/crossversionobjectreference.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta1/externalmetricsource.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta1/externalmetricstatus.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta1/horizontalpodautoscaler.go ================================================ [File too large to display: 13.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta1/horizontalpodautoscalercondition.go ================================================ [File too large to display: 4.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta1/horizontalpodautoscalerspec.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta1/horizontalpodautoscalerstatus.go ================================================ [File too large to display: 5.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta1/metricspec.go ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta1/metricstatus.go ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta1/objectmetricsource.go ================================================ [File too large to display: 3.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta1/objectmetricstatus.go ================================================ [File too large to display: 3.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta1/podsmetricsource.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta1/podsmetricstatus.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta1/resourcemetricsource.go ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta1/resourcemetricstatus.go ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta2/containerresourcemetricsource.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta2/containerresourcemetricstatus.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta2/crossversionobjectreference.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta2/externalmetricsource.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta2/externalmetricstatus.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta2/horizontalpodautoscaler.go ================================================ [File too large to display: 13.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta2/horizontalpodautoscalerbehavior.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta2/horizontalpodautoscalercondition.go ================================================ [File too large to display: 4.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta2/horizontalpodautoscalerspec.go ================================================ [File too large to display: 4.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta2/horizontalpodautoscalerstatus.go ================================================ [File too large to display: 5.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta2/hpascalingpolicy.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta2/hpascalingrules.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta2/metricidentifier.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta2/metricspec.go ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta2/metricstatus.go ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta2/metrictarget.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta2/metricvaluestatus.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta2/objectmetricsource.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta2/objectmetricstatus.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta2/podsmetricsource.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta2/podsmetricstatus.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta2/resourcemetricsource.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/autoscaling/v2beta2/resourcemetricstatus.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/batch/v1/cronjob.go ================================================ [File too large to display: 11.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/batch/v1/cronjobspec.go ================================================ [File too large to display: 5.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/batch/v1/cronjobstatus.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/batch/v1/job.go ================================================ [File too large to display: 11.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/batch/v1/jobcondition.go ================================================ [File too large to display: 4.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/batch/v1/jobspec.go ================================================ [File too large to display: 6.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/batch/v1/jobstatus.go ================================================ [File too large to display: 5.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/batch/v1/jobtemplatespec.go ================================================ [File too large to display: 8.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/batch/v1/podfailurepolicy.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/batch/v1/podfailurepolicyonexitcodesrequirement.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/batch/v1/podfailurepolicyonpodconditionspattern.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/batch/v1/podfailurepolicyrule.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/batch/v1/uncountedterminatedpods.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/batch/v1beta1/cronjob.go ================================================ [File too large to display: 11.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/batch/v1beta1/cronjobspec.go ================================================ [File too large to display: 5.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/batch/v1beta1/cronjobstatus.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/batch/v1beta1/jobtemplatespec.go ================================================ [File too large to display: 8.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/certificates/v1/certificatesigningrequest.go ================================================ [File too large to display: 13.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/certificates/v1/certificatesigningrequestcondition.go ================================================ [File too large to display: 4.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/certificates/v1/certificatesigningrequestspec.go ================================================ [File too large to display: 5.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/certificates/v1/certificatesigningrequeststatus.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/certificates/v1alpha1/clustertrustbundle.go ================================================ [File too large to display: 12.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/certificates/v1alpha1/clustertrustbundlespec.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/certificates/v1beta1/certificatesigningrequest.go ================================================ [File too large to display: 13.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/certificates/v1beta1/certificatesigningrequestcondition.go ================================================ [File too large to display: 4.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/certificates/v1beta1/certificatesigningrequestspec.go ================================================ [File too large to display: 5.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/certificates/v1beta1/certificatesigningrequeststatus.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/coordination/v1/lease.go ================================================ [File too large to display: 11.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/coordination/v1/leasespec.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/coordination/v1beta1/lease.go ================================================ [File too large to display: 11.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/coordination/v1beta1/leasespec.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/affinity.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/attachedvolume.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/awselasticblockstorevolumesource.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/azurediskvolumesource.go ================================================ [File too large to display: 4.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/azurefilepersistentvolumesource.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/azurefilevolumesource.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/capabilities.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/cephfspersistentvolumesource.go ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/cephfsvolumesource.go ================================================ [File too large to display: 4.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/cinderpersistentvolumesource.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/cindervolumesource.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/claimsource.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/clientipconfig.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/componentcondition.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/componentstatus.go ================================================ [File too large to display: 12.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/configmap.go ================================================ [File too large to display: 12.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/configmapenvsource.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/configmapkeyselector.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/configmapnodeconfigsource.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/configmapprojection.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/configmapvolumesource.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/container.go ================================================ [File too large to display: 13.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/containerimage.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/containerport.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/containerresizepolicy.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/containerstate.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/containerstaterunning.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/containerstateterminated.go ================================================ [File too large to display: 4.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/containerstatewaiting.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/containerstatus.go ================================================ [File too large to display: 6.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/csipersistentvolumesource.go ================================================ [File too large to display: 7.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/csivolumesource.go ================================================ [File too large to display: 3.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/daemonendpoint.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/downwardapiprojection.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/downwardapivolumefile.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/downwardapivolumesource.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/emptydirvolumesource.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/endpointaddress.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/endpointport.go ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/endpoints.go ================================================ [File too large to display: 11.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/endpointsubset.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/envfromsource.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/envvar.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/envvarsource.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/ephemeralcontainer.go ================================================ [File too large to display: 12.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/ephemeralcontainercommon.go ================================================ [File too large to display: 14.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/ephemeralvolumesource.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/event.go ================================================ [File too large to display: 17.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/eventseries.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/eventsource.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/execaction.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/fcvolumesource.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/flexpersistentvolumesource.go ================================================ [File too large to display: 3.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/flexvolumesource.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/flockervolumesource.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/gcepersistentdiskvolumesource.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/gitrepovolumesource.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/glusterfspersistentvolumesource.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/glusterfsvolumesource.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/grpcaction.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/hostalias.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/hostpathvolumesource.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/httpgetaction.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/httpheader.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/iscsipersistentvolumesource.go ================================================ [File too large to display: 7.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/iscsivolumesource.go ================================================ [File too large to display: 6.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/keytopath.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/lifecycle.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/lifecyclehandler.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/limitrange.go ================================================ [File too large to display: 11.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/limitrangeitem.go ================================================ [File too large to display: 4.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/limitrangespec.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/loadbalanceringress.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/loadbalancerstatus.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/localobjectreference.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/localvolumesource.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/namespace.go ================================================ [File too large to display: 12.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/namespacecondition.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/namespacespec.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/namespacestatus.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/nfsvolumesource.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/node.go ================================================ [File too large to display: 11.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/nodeaddress.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/nodeaffinity.go ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/nodecondition.go ================================================ [File too large to display: 4.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/nodeconfigsource.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/nodeconfigstatus.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/nodedaemonendpoints.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/nodeselector.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/nodeselectorrequirement.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/nodeselectorterm.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/nodespec.go ================================================ [File too large to display: 4.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/nodestatus.go ================================================ [File too large to display: 7.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/nodesysteminfo.go ================================================ [File too large to display: 6.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/objectfieldselector.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/objectreference.go ================================================ [File too large to display: 4.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/persistentvolume.go ================================================ [File too large to display: 12.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/persistentvolumeclaim.go ================================================ [File too large to display: 13.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/persistentvolumeclaimcondition.go ================================================ [File too large to display: 4.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/persistentvolumeclaimspec.go ================================================ [File too large to display: 5.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/persistentvolumeclaimstatus.go ================================================ [File too large to display: 4.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/persistentvolumeclaimtemplate.go ================================================ [File too large to display: 9.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/persistentvolumeclaimvolumesource.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/persistentvolumesource.go ================================================ [File too large to display: 13.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/persistentvolumespec.go ================================================ [File too large to display: 15.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/persistentvolumestatus.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/photonpersistentdiskvolumesource.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/pod.go ================================================ [File too large to display: 11.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/podaffinity.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/podaffinityterm.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/podantiaffinity.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/podcondition.go ================================================ [File too large to display: 4.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/poddnsconfig.go ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/poddnsconfigoption.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/podip.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/podos.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/podreadinessgate.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/podresourceclaim.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/podschedulinggate.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/podsecuritycontext.go ================================================ [File too large to display: 6.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/podspec.go ================================================ [File too large to display: 23.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/podstatus.go ================================================ [File too large to display: 9.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/podtemplate.go ================================================ [File too large to display: 11.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/podtemplatespec.go ================================================ [File too large to display: 8.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/portstatus.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/portworxvolumesource.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/preferredschedulingterm.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/probe.go ================================================ [File too large to display: 5.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/probehandler.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/projectedvolumesource.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/quobytevolumesource.go ================================================ [File too large to display: 3.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/rbdpersistentvolumesource.go ================================================ [File too large to display: 5.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/rbdvolumesource.go ================================================ [File too large to display: 5.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/replicationcontroller.go ================================================ [File too large to display: 13.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/replicationcontrollercondition.go ================================================ [File too large to display: 3.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/replicationcontrollerspec.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/replicationcontrollerstatus.go ================================================ [File too large to display: 4.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/resourceclaim.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/resourcefieldselector.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/resourcequota.go ================================================ [File too large to display: 12.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/resourcequotaspec.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/resourcequotastatus.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/resourcerequirements.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/scaleiopersistentvolumesource.go ================================================ [File too large to display: 6.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/scaleiovolumesource.go ================================================ [File too large to display: 6.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/scopedresourceselectorrequirement.go ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/scopeselector.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/seccompprofile.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/secret.go ================================================ [File too large to display: 13.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/secretenvsource.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/secretkeyselector.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/secretprojection.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/secretreference.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/secretvolumesource.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/securitycontext.go ================================================ [File too large to display: 7.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/selinuxoptions.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/service.go ================================================ [File too large to display: 11.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/serviceaccount.go ================================================ [File too large to display: 13.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/serviceaccounttokenprojection.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/serviceport.go ================================================ [File too large to display: 3.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/servicespec.go ================================================ [File too large to display: 12.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/servicestatus.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/sessionaffinityconfig.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/storageospersistentvolumesource.go ================================================ [File too large to display: 3.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/storageosvolumesource.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/sysctl.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/taint.go ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/tcpsocketaction.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/toleration.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/topologyselectorlabelrequirement.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/topologyselectorterm.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/topologyspreadconstraint.go ================================================ [File too large to display: 5.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/typedlocalobjectreference.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/typedobjectreference.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/volume.go ================================================ [File too large to display: 13.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/volumedevice.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/volumemount.go ================================================ [File too large to display: 3.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/volumenodeaffinity.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/volumeprojection.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/volumesource.go ================================================ [File too large to display: 16.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/vspherevirtualdiskvolumesource.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/weightedpodaffinityterm.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/core/v1/windowssecuritycontextoptions.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/discovery/v1/endpoint.go ================================================ [File too large to display: 5.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/discovery/v1/endpointconditions.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/discovery/v1/endpointhints.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/discovery/v1/endpointport.go ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/discovery/v1/endpointslice.go ================================================ [File too large to display: 13.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/discovery/v1/forzone.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/discovery/v1beta1/endpoint.go ================================================ [File too large to display: 4.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/discovery/v1beta1/endpointconditions.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/discovery/v1beta1/endpointhints.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/discovery/v1beta1/endpointport.go ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/discovery/v1beta1/endpointslice.go ================================================ [File too large to display: 13.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/discovery/v1beta1/forzone.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/events/v1/event.go ================================================ [File too large to display: 18.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/events/v1/eventseries.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/events/v1beta1/event.go ================================================ [File too large to display: 18.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/events/v1beta1/eventseries.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/daemonset.go ================================================ [File too large to display: 12.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/daemonsetcondition.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/daemonsetspec.go ================================================ [File too large to display: 4.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/daemonsetstatus.go ================================================ [File too large to display: 6.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/daemonsetupdatestrategy.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/deployment.go ================================================ [File too large to display: 12.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/deploymentcondition.go ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/deploymentspec.go ================================================ [File too large to display: 5.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/deploymentstatus.go ================================================ [File too large to display: 5.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/deploymentstrategy.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/httpingresspath.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/httpingressrulevalue.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/ingress.go ================================================ [File too large to display: 12.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/ingressbackend.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/ingressloadbalanceringress.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/ingressloadbalancerstatus.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/ingressportstatus.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/ingressrule.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/ingressrulevalue.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/ingressspec.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/ingressstatus.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/ingresstls.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/ipblock.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/networkpolicy.go ================================================ [File too large to display: 12.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/networkpolicyegressrule.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/networkpolicyingressrule.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/networkpolicypeer.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/networkpolicyport.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/networkpolicyspec.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/networkpolicystatus.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/replicaset.go ================================================ [File too large to display: 12.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/replicasetcondition.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/replicasetspec.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/replicasetstatus.go ================================================ [File too large to display: 4.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/rollbackconfig.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/rollingupdatedaemonset.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/rollingupdatedeployment.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/extensions/v1beta1/scale.go ================================================ [File too large to display: 9.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1alpha1/flowdistinguishermethod.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1alpha1/flowschema.go ================================================ [File too large to display: 12.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1alpha1/flowschemacondition.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1alpha1/flowschemaspec.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1alpha1/flowschemastatus.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1alpha1/groupsubject.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1alpha1/limitedprioritylevelconfiguration.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1alpha1/limitresponse.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1alpha1/nonresourcepolicyrule.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1alpha1/policyruleswithsubjects.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1alpha1/prioritylevelconfiguration.go ================================================ [File too large to display: 13.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1alpha1/prioritylevelconfigurationcondition.go ================================================ [File too large to display: 4.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1alpha1/prioritylevelconfigurationreference.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1alpha1/prioritylevelconfigurationspec.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1alpha1/prioritylevelconfigurationstatus.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1alpha1/queuingconfiguration.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1alpha1/resourcepolicyrule.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1alpha1/serviceaccountsubject.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1alpha1/subject.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1alpha1/usersubject.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta1/flowdistinguishermethod.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta1/flowschema.go ================================================ [File too large to display: 12.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta1/flowschemacondition.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta1/flowschemaspec.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta1/flowschemastatus.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta1/groupsubject.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta1/limitedprioritylevelconfiguration.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta1/limitresponse.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta1/nonresourcepolicyrule.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta1/policyruleswithsubjects.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta1/prioritylevelconfiguration.go ================================================ [File too large to display: 13.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta1/prioritylevelconfigurationcondition.go ================================================ [File too large to display: 4.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta1/prioritylevelconfigurationreference.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta1/prioritylevelconfigurationspec.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta1/prioritylevelconfigurationstatus.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta1/queuingconfiguration.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta1/resourcepolicyrule.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta1/serviceaccountsubject.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta1/subject.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta1/usersubject.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta2/flowdistinguishermethod.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta2/flowschema.go ================================================ [File too large to display: 12.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta2/flowschemacondition.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta2/flowschemaspec.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta2/flowschemastatus.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta2/groupsubject.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta2/limitedprioritylevelconfiguration.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta2/limitresponse.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta2/nonresourcepolicyrule.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta2/policyruleswithsubjects.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta2/prioritylevelconfiguration.go ================================================ [File too large to display: 13.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta2/prioritylevelconfigurationcondition.go ================================================ [File too large to display: 4.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta2/prioritylevelconfigurationreference.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta2/prioritylevelconfigurationspec.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta2/prioritylevelconfigurationstatus.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta2/queuingconfiguration.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta2/resourcepolicyrule.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta2/serviceaccountsubject.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta2/subject.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta2/usersubject.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/flowdistinguishermethod.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/flowschema.go ================================================ [File too large to display: 12.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/flowschemacondition.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/flowschemaspec.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/flowschemastatus.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/groupsubject.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/limitedprioritylevelconfiguration.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/limitresponse.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/nonresourcepolicyrule.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/policyruleswithsubjects.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/prioritylevelconfiguration.go ================================================ [File too large to display: 13.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/prioritylevelconfigurationcondition.go ================================================ [File too large to display: 4.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/prioritylevelconfigurationreference.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/prioritylevelconfigurationspec.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/prioritylevelconfigurationstatus.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/queuingconfiguration.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/resourcepolicyrule.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/serviceaccountsubject.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/subject.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/flowcontrol/v1beta3/usersubject.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/internal/internal.go ================================================ [File too large to display: 298.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/meta/v1/condition.go ================================================ [File too large to display: 3.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/meta/v1/deleteoptions.go ================================================ [File too large to display: 4.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/meta/v1/labelselector.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/meta/v1/labelselectorrequirement.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/meta/v1/managedfieldsentry.go ================================================ [File too large to display: 4.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/meta/v1/objectmeta.go ================================================ [File too large to display: 8.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/meta/v1/ownerreference.go ================================================ [File too large to display: 3.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/meta/v1/preconditions.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/meta/v1/typemeta.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/meta/v1/unstructured.go ================================================ [File too large to display: 4.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1/httpingresspath.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1/httpingressrulevalue.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1/ingress.go ================================================ [File too large to display: 12.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1/ingressbackend.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1/ingressclass.go ================================================ [File too large to display: 11.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1/ingressclassparametersreference.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1/ingressclassspec.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1/ingressloadbalanceringress.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1/ingressloadbalancerstatus.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1/ingressportstatus.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1/ingressrule.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1/ingressrulevalue.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1/ingressservicebackend.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1/ingressspec.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1/ingressstatus.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1/ingresstls.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1/ipblock.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1/networkpolicy.go ================================================ [File too large to display: 12.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1/networkpolicyegressrule.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1/networkpolicyingressrule.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1/networkpolicypeer.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1/networkpolicyport.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1/networkpolicyspec.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1/networkpolicystatus.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1/servicebackendport.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1alpha1/clustercidr.go ================================================ [File too large to display: 11.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1alpha1/clustercidrspec.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1alpha1/ipaddress.go ================================================ [File too large to display: 11.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1alpha1/ipaddressspec.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1alpha1/parentreference.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1beta1/httpingresspath.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1beta1/httpingressrulevalue.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1beta1/ingress.go ================================================ [File too large to display: 12.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1beta1/ingressbackend.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1beta1/ingressclass.go ================================================ [File too large to display: 11.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1beta1/ingressclassparametersreference.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1beta1/ingressclassspec.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1beta1/ingressloadbalanceringress.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1beta1/ingressloadbalancerstatus.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1beta1/ingressportstatus.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1beta1/ingressrule.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1beta1/ingressrulevalue.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1beta1/ingressspec.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1beta1/ingressstatus.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/networking/v1beta1/ingresstls.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/node/v1/overhead.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/node/v1/runtimeclass.go ================================================ [File too large to display: 12.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/node/v1/scheduling.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/node/v1alpha1/overhead.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/node/v1alpha1/runtimeclass.go ================================================ [File too large to display: 11.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/node/v1alpha1/runtimeclassspec.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/node/v1alpha1/scheduling.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/node/v1beta1/overhead.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/node/v1beta1/runtimeclass.go ================================================ [File too large to display: 12.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/node/v1beta1/scheduling.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/policy/v1/eviction.go ================================================ [File too large to display: 11.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/policy/v1/poddisruptionbudget.go ================================================ [File too large to display: 12.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/policy/v1/poddisruptionbudgetspec.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/policy/v1/poddisruptionbudgetstatus.go ================================================ [File too large to display: 5.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/policy/v1beta1/allowedcsidriver.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/policy/v1beta1/allowedflexvolume.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/policy/v1beta1/allowedhostpath.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/policy/v1beta1/eviction.go ================================================ [File too large to display: 11.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/policy/v1beta1/fsgroupstrategyoptions.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/policy/v1beta1/hostportrange.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/policy/v1beta1/idrange.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/policy/v1beta1/poddisruptionbudget.go ================================================ [File too large to display: 12.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/policy/v1beta1/poddisruptionbudgetspec.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/policy/v1beta1/poddisruptionbudgetstatus.go ================================================ [File too large to display: 5.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/policy/v1beta1/podsecuritypolicy.go ================================================ [File too large to display: 12.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/policy/v1beta1/podsecuritypolicyspec.go ================================================ [File too large to display: 16.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/policy/v1beta1/runasgroupstrategyoptions.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/policy/v1beta1/runasuserstrategyoptions.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/policy/v1beta1/runtimeclassstrategyoptions.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/policy/v1beta1/selinuxstrategyoptions.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/policy/v1beta1/supplementalgroupsstrategyoptions.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/rbac/v1/aggregationrule.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/rbac/v1/clusterrole.go ================================================ [File too large to display: 12.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/rbac/v1/clusterrolebinding.go ================================================ [File too large to display: 12.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/rbac/v1/policyrule.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/rbac/v1/role.go ================================================ [File too large to display: 11.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/rbac/v1/rolebinding.go ================================================ [File too large to display: 12.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/rbac/v1/roleref.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/rbac/v1/subject.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/rbac/v1alpha1/aggregationrule.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/rbac/v1alpha1/clusterrole.go ================================================ [File too large to display: 12.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/rbac/v1alpha1/clusterrolebinding.go ================================================ [File too large to display: 12.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/rbac/v1alpha1/policyrule.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/rbac/v1alpha1/role.go ================================================ [File too large to display: 11.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/rbac/v1alpha1/rolebinding.go ================================================ [File too large to display: 12.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/rbac/v1alpha1/roleref.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/rbac/v1alpha1/subject.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/rbac/v1beta1/aggregationrule.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/rbac/v1beta1/clusterrole.go ================================================ [File too large to display: 12.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/rbac/v1beta1/clusterrolebinding.go ================================================ [File too large to display: 12.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/rbac/v1beta1/policyrule.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/rbac/v1beta1/role.go ================================================ [File too large to display: 11.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/rbac/v1beta1/rolebinding.go ================================================ [File too large to display: 12.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/rbac/v1beta1/roleref.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/rbac/v1beta1/subject.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/allocationresult.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/podschedulingcontext.go ================================================ [File too large to display: 13.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/podschedulingcontextspec.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/podschedulingcontextstatus.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/resourceclaim.go ================================================ [File too large to display: 12.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/resourceclaimconsumerreference.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/resourceclaimparametersreference.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/resourceclaimschedulingstatus.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/resourceclaimspec.go ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/resourceclaimstatus.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/resourceclaimtemplate.go ================================================ [File too large to display: 12.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/resourceclaimtemplatespec.go ================================================ [File too large to display: 9.2 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/resourceclass.go ================================================ [File too large to display: 13.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/resourceclassparametersreference.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/resource/v1alpha2/resourcehandle.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/scheduling/v1/priorityclass.go ================================================ [File too large to display: 13.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/scheduling/v1alpha1/priorityclass.go ================================================ [File too large to display: 13.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/scheduling/v1beta1/priorityclass.go ================================================ [File too large to display: 13.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1/csidriver.go ================================================ [File too large to display: 11.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1/csidriverspec.go ================================================ [File too large to display: 5.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1/csinode.go ================================================ [File too large to display: 11.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1/csinodedriver.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1/csinodespec.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1/csistoragecapacity.go ================================================ [File too large to display: 14.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1/storageclass.go ================================================ [File too large to display: 15.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1/tokenrequest.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1/volumeattachment.go ================================================ [File too large to display: 12.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1/volumeattachmentsource.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1/volumeattachmentspec.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1/volumeattachmentstatus.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1/volumeerror.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1/volumenoderesources.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1alpha1/csistoragecapacity.go ================================================ [File too large to display: 14.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1alpha1/volumeattachment.go ================================================ [File too large to display: 12.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1alpha1/volumeattachmentsource.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1alpha1/volumeattachmentspec.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1alpha1/volumeattachmentstatus.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1alpha1/volumeerror.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1beta1/csidriver.go ================================================ [File too large to display: 11.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1beta1/csidriverspec.go ================================================ [File too large to display: 5.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1beta1/csinode.go ================================================ [File too large to display: 11.4 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1beta1/csinodedriver.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1beta1/csinodespec.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1beta1/csistoragecapacity.go ================================================ [File too large to display: 14.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1beta1/storageclass.go ================================================ [File too large to display: 15.8 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1beta1/tokenrequest.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1beta1/volumeattachment.go ================================================ [File too large to display: 12.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1beta1/volumeattachmentsource.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1beta1/volumeattachmentspec.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1beta1/volumeattachmentstatus.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1beta1/volumeerror.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/applyconfigurations/storage/v1beta1/volumenoderesources.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/client-go/discovery/aggregated_discovery.go ================================================ [File too large to display: 5.7 KB] ================================================ FILE: vendor/k8s.io/client-go/discovery/cached/disk/cached_discovery.go ================================================ [File too large to display: 10.8 KB] ================================================ FILE: vendor/k8s.io/client-go/discovery/cached/disk/round_tripper.go ================================================ [File too large to display: 3.9 KB] ================================================ FILE: vendor/k8s.io/client-go/discovery/cached/memory/memcache.go ================================================ [File too large to display: 10.0 KB] ================================================ FILE: vendor/k8s.io/client-go/discovery/discovery_client.go ================================================ [File too large to display: 27.7 KB] ================================================ FILE: vendor/k8s.io/client-go/discovery/doc.go ================================================ [File too large to display: 731 B] ================================================ FILE: vendor/k8s.io/client-go/discovery/helper.go ================================================ [File too large to display: 5.3 KB] ================================================ FILE: vendor/k8s.io/client-go/dynamic/interface.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/k8s.io/client-go/dynamic/scheme.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/k8s.io/client-go/dynamic/simple.go ================================================ [File too large to display: 13.2 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/clientset.go ================================================ [File too large to display: 30.2 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/doc.go ================================================ [File too large to display: 679 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/import.go ================================================ [File too large to display: 699 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/scheme/doc.go ================================================ [File too large to display: 706 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/scheme/register.go ================================================ [File too large to display: 5.6 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1/admissionregistration_client.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1/doc.go ================================================ [File too large to display: 687 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1/generated_expansion.go ================================================ [File too large to display: 738 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1/mutatingwebhookconfiguration.go ================================================ [File too large to display: 8.3 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1/validatingwebhookconfiguration.go ================================================ [File too large to display: 8.5 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1/admissionregistration_client.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1/doc.go ================================================ [File too large to display: 693 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1/generated_expansion.go ================================================ [File too large to display: 743 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1/validatingadmissionpolicy.go ================================================ [File too large to display: 10.3 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1/validatingadmissionpolicybinding.go ================================================ [File too large to display: 8.7 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1beta1/admissionregistration_client.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1beta1/doc.go ================================================ [File too large to display: 692 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1beta1/generated_expansion.go ================================================ [File too large to display: 743 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1beta1/mutatingwebhookconfiguration.go ================================================ [File too large to display: 8.4 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/admissionregistration/v1beta1/validatingwebhookconfiguration.go ================================================ [File too large to display: 8.5 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/apiserverinternal/v1alpha1/apiserverinternal_client.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/apiserverinternal/v1alpha1/doc.go ================================================ [File too large to display: 693 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/apiserverinternal/v1alpha1/generated_expansion.go ================================================ [File too large to display: 672 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/apiserverinternal/v1alpha1/storageversion.go ================================================ [File too large to display: 9.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/apps/v1/apps_client.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/apps/v1/controllerrevision.go ================================================ [File too large to display: 7.6 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/apps/v1/daemonset.go ================================================ [File too large to display: 8.6 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/apps/v1/deployment.go ================================================ [File too large to display: 11.2 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/apps/v1/doc.go ================================================ [File too large to display: 687 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/apps/v1/generated_expansion.go ================================================ [File too large to display: 822 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/apps/v1/replicaset.go ================================================ [File too large to display: 11.2 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/apps/v1/statefulset.go ================================================ [File too large to display: 11.3 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/apps/v1beta1/apps_client.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/apps/v1beta1/controllerrevision.go ================================================ [File too large to display: 7.7 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/apps/v1beta1/deployment.go ================================================ [File too large to display: 8.8 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/apps/v1beta1/doc.go ================================================ [File too large to display: 692 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/apps/v1beta1/generated_expansion.go ================================================ [File too large to display: 752 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/apps/v1beta1/statefulset.go ================================================ [File too large to display: 8.9 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/apps/v1beta2/apps_client.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/apps/v1beta2/controllerrevision.go ================================================ [File too large to display: 7.7 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/apps/v1beta2/daemonset.go ================================================ [File too large to display: 8.7 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/apps/v1beta2/deployment.go ================================================ [File too large to display: 8.8 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/apps/v1beta2/doc.go ================================================ [File too large to display: 692 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/apps/v1beta2/generated_expansion.go ================================================ [File too large to display: 827 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/apps/v1beta2/replicaset.go ================================================ [File too large to display: 8.8 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/apps/v1beta2/statefulset.go ================================================ [File too large to display: 11.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authentication/v1/authentication_client.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authentication/v1/doc.go ================================================ [File too large to display: 687 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authentication/v1/generated_expansion.go ================================================ [File too large to display: 663 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authentication/v1/tokenreview.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authentication/v1alpha1/authentication_client.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authentication/v1alpha1/doc.go ================================================ [File too large to display: 693 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authentication/v1alpha1/generated_expansion.go ================================================ [File too large to display: 675 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authentication/v1alpha1/selfsubjectreview.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authentication/v1beta1/authentication_client.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authentication/v1beta1/doc.go ================================================ [File too large to display: 692 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authentication/v1beta1/generated_expansion.go ================================================ [File too large to display: 713 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authentication/v1beta1/selfsubjectreview.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authentication/v1beta1/tokenreview.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authorization/v1/authorization_client.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authorization/v1/doc.go ================================================ [File too large to display: 687 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authorization/v1/generated_expansion.go ================================================ [File too large to display: 824 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authorization/v1/localsubjectaccessreview.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authorization/v1/selfsubjectaccessreview.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authorization/v1/selfsubjectrulesreview.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authorization/v1/subjectaccessreview.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authorization/v1beta1/authorization_client.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authorization/v1beta1/doc.go ================================================ [File too large to display: 692 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authorization/v1beta1/generated_expansion.go ================================================ [File too large to display: 829 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authorization/v1beta1/localsubjectaccessreview.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authorization/v1beta1/selfsubjectaccessreview.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authorization/v1beta1/selfsubjectrulesreview.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/authorization/v1beta1/subjectaccessreview.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/autoscaling/v1/autoscaling_client.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/autoscaling/v1/doc.go ================================================ [File too large to display: 687 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/autoscaling/v1/generated_expansion.go ================================================ [File too large to display: 675 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/autoscaling/v1/horizontalpodautoscaler.go ================================================ [File too large to display: 10.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/autoscaling/v2/autoscaling_client.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/autoscaling/v2/doc.go ================================================ [File too large to display: 687 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/autoscaling/v2/generated_expansion.go ================================================ [File too large to display: 675 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/autoscaling/v2/horizontalpodautoscaler.go ================================================ [File too large to display: 10.0 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/autoscaling/v2beta1/autoscaling_client.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/autoscaling/v2beta1/doc.go ================================================ [File too large to display: 692 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/autoscaling/v2beta1/generated_expansion.go ================================================ [File too large to display: 680 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/autoscaling/v2beta1/horizontalpodautoscaler.go ================================================ [File too large to display: 10.2 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/autoscaling/v2beta2/autoscaling_client.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/autoscaling/v2beta2/doc.go ================================================ [File too large to display: 692 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/autoscaling/v2beta2/generated_expansion.go ================================================ [File too large to display: 680 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/autoscaling/v2beta2/horizontalpodautoscaler.go ================================================ [File too large to display: 10.2 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/batch/v1/batch_client.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/batch/v1/cronjob.go ================================================ [File too large to display: 8.4 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/batch/v1/doc.go ================================================ [File too large to display: 687 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/batch/v1/generated_expansion.go ================================================ [File too large to display: 690 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/batch/v1/job.go ================================================ [File too large to display: 7.9 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/batch/v1beta1/batch_client.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/batch/v1beta1/cronjob.go ================================================ [File too large to display: 8.5 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/batch/v1beta1/doc.go ================================================ [File too large to display: 692 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/batch/v1beta1/generated_expansion.go ================================================ [File too large to display: 664 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/certificates/v1/certificates_client.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/certificates/v1/certificatesigningrequest.go ================================================ [File too large to display: 11.0 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/certificates/v1/doc.go ================================================ [File too large to display: 687 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/certificates/v1/generated_expansion.go ================================================ [File too large to display: 677 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/certificates/v1alpha1/certificates_client.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/certificates/v1alpha1/clustertrustbundle.go ================================================ [File too large to display: 7.5 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/certificates/v1alpha1/doc.go ================================================ [File too large to display: 693 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/certificates/v1alpha1/generated_expansion.go ================================================ [File too large to display: 676 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/certificates/v1beta1/certificates_client.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/certificates/v1beta1/certificatesigningrequest.go ================================================ [File too large to display: 10.2 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/certificates/v1beta1/certificatesigningrequest_expansion.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/certificates/v1beta1/doc.go ================================================ [File too large to display: 692 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/certificates/v1beta1/generated_expansion.go ================================================ [File too large to display: 629 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/coordination/v1/coordination_client.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/coordination/v1/doc.go ================================================ [File too large to display: 687 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/coordination/v1/generated_expansion.go ================================================ [File too large to display: 657 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/coordination/v1/lease.go ================================================ [File too large to display: 6.6 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/coordination/v1beta1/coordination_client.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/coordination/v1beta1/doc.go ================================================ [File too large to display: 692 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/coordination/v1beta1/generated_expansion.go ================================================ [File too large to display: 662 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/coordination/v1beta1/lease.go ================================================ [File too large to display: 6.7 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/core/v1/componentstatus.go ================================================ [File too large to display: 7.2 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/core/v1/configmap.go ================================================ [File too large to display: 6.9 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/core/v1/core_client.go ================================================ [File too large to display: 4.8 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/core/v1/doc.go ================================================ [File too large to display: 687 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/core/v1/endpoints.go ================================================ [File too large to display: 6.9 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/core/v1/event.go ================================================ [File too large to display: 6.6 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/core/v1/event_expansion.go ================================================ [File too large to display: 6.8 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/core/v1/generated_expansion.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/core/v1/limitrange.go ================================================ [File too large to display: 7.0 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/core/v1/namespace.go ================================================ [File too large to display: 7.7 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/core/v1/namespace_expansion.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/core/v1/node.go ================================================ [File too large to display: 7.8 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/core/v1/node_expansion.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/core/v1/persistentvolume.go ================================================ [File too large to display: 9.0 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/core/v1/persistentvolumeclaim.go ================================================ [File too large to display: 9.8 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/core/v1/pod.go ================================================ [File too large to display: 8.6 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/core/v1/pod_expansion.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/core/v1/podtemplate.go ================================================ [File too large to display: 7.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/core/v1/replicationcontroller.go ================================================ [File too large to display: 11.3 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/core/v1/resourcequota.go ================================================ [File too large to display: 9.0 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/core/v1/secret.go ================================================ [File too large to display: 6.7 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/core/v1/service.go ================================================ [File too large to display: 7.8 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/core/v1/service_expansion.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/core/v1/serviceaccount.go ================================================ [File too large to display: 8.2 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/discovery/v1/discovery_client.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/discovery/v1/doc.go ================================================ [File too large to display: 687 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/discovery/v1/endpointslice.go ================================================ [File too large to display: 7.3 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/discovery/v1/generated_expansion.go ================================================ [File too large to display: 665 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/discovery/v1beta1/discovery_client.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/discovery/v1beta1/doc.go ================================================ [File too large to display: 692 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/discovery/v1beta1/endpointslice.go ================================================ [File too large to display: 7.3 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/discovery/v1beta1/generated_expansion.go ================================================ [File too large to display: 670 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/events/v1/doc.go ================================================ [File too large to display: 687 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/events/v1/event.go ================================================ [File too large to display: 6.6 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/events/v1/events_client.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/events/v1/generated_expansion.go ================================================ [File too large to display: 657 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/events/v1beta1/doc.go ================================================ [File too large to display: 692 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/events/v1beta1/event.go ================================================ [File too large to display: 6.6 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/events/v1beta1/event_expansion.go ================================================ [File too large to display: 3.9 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/events/v1beta1/events_client.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/events/v1beta1/generated_expansion.go ================================================ [File too large to display: 629 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/extensions/v1beta1/daemonset.go ================================================ [File too large to display: 8.7 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/extensions/v1beta1/deployment.go ================================================ [File too large to display: 11.0 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/extensions/v1beta1/deployment_expansion.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/extensions/v1beta1/doc.go ================================================ [File too large to display: 692 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/extensions/v1beta1/extensions_client.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/extensions/v1beta1/generated_expansion.go ================================================ [File too large to display: 780 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/extensions/v1beta1/ingress.go ================================================ [File too large to display: 8.5 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/extensions/v1beta1/networkpolicy.go ================================================ [File too large to display: 9.2 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/extensions/v1beta1/replicaset.go ================================================ [File too large to display: 11.0 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1alpha1/doc.go ================================================ [File too large to display: 693 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1alpha1/flowcontrol_client.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1alpha1/flowschema.go ================================================ [File too large to display: 8.6 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1alpha1/generated_expansion.go ================================================ [File too large to display: 722 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1alpha1/prioritylevelconfiguration.go ================================================ [File too large to display: 10.3 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta1/doc.go ================================================ [File too large to display: 692 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta1/flowcontrol_client.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta1/flowschema.go ================================================ [File too large to display: 8.6 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta1/generated_expansion.go ================================================ [File too large to display: 721 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta1/prioritylevelconfiguration.go ================================================ [File too large to display: 10.3 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta2/doc.go ================================================ [File too large to display: 692 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta2/flowcontrol_client.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta2/flowschema.go ================================================ [File too large to display: 8.6 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta2/generated_expansion.go ================================================ [File too large to display: 721 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta2/prioritylevelconfiguration.go ================================================ [File too large to display: 10.3 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta3/doc.go ================================================ [File too large to display: 692 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta3/flowcontrol_client.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta3/flowschema.go ================================================ [File too large to display: 8.6 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta3/generated_expansion.go ================================================ [File too large to display: 721 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/flowcontrol/v1beta3/prioritylevelconfiguration.go ================================================ [File too large to display: 10.3 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/networking/v1/doc.go ================================================ [File too large to display: 687 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/networking/v1/generated_expansion.go ================================================ [File too large to display: 740 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/networking/v1/ingress.go ================================================ [File too large to display: 8.4 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/networking/v1/ingressclass.go ================================================ [File too large to display: 7.0 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/networking/v1/networking_client.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/networking/v1/networkpolicy.go ================================================ [File too large to display: 9.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/networking/v1alpha1/clustercidr.go ================================================ [File too large to display: 7.0 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/networking/v1alpha1/doc.go ================================================ [File too large to display: 693 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/networking/v1alpha1/generated_expansion.go ================================================ [File too large to display: 706 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/networking/v1alpha1/ipaddress.go ================================================ [File too large to display: 6.8 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/networking/v1alpha1/networking_client.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/networking/v1beta1/doc.go ================================================ [File too large to display: 692 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/networking/v1beta1/generated_expansion.go ================================================ [File too large to display: 704 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/networking/v1beta1/ingress.go ================================================ [File too large to display: 8.5 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/networking/v1beta1/ingressclass.go ================================================ [File too large to display: 7.0 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/networking/v1beta1/networking_client.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/node/v1/doc.go ================================================ [File too large to display: 687 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/node/v1/generated_expansion.go ================================================ [File too large to display: 664 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/node/v1/node_client.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/node/v1/runtimeclass.go ================================================ [File too large to display: 6.9 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/node/v1alpha1/doc.go ================================================ [File too large to display: 693 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/node/v1alpha1/generated_expansion.go ================================================ [File too large to display: 670 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/node/v1alpha1/node_client.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/node/v1alpha1/runtimeclass.go ================================================ [File too large to display: 7.0 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/node/v1beta1/doc.go ================================================ [File too large to display: 692 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/node/v1beta1/generated_expansion.go ================================================ [File too large to display: 669 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/node/v1beta1/node_client.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/node/v1beta1/runtimeclass.go ================================================ [File too large to display: 7.0 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/policy/v1/doc.go ================================================ [File too large to display: 687 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/policy/v1/eviction.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/policy/v1/eviction_expansion.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/policy/v1/generated_expansion.go ================================================ [File too large to display: 671 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/policy/v1/poddisruptionbudget.go ================================================ [File too large to display: 9.7 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/policy/v1/policy_client.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/policy/v1beta1/doc.go ================================================ [File too large to display: 692 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/policy/v1beta1/eviction.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/policy/v1beta1/eviction_expansion.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/policy/v1beta1/generated_expansion.go ================================================ [File too large to display: 721 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/policy/v1beta1/poddisruptionbudget.go ================================================ [File too large to display: 9.7 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/policy/v1beta1/podsecuritypolicy.go ================================================ [File too large to display: 7.4 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/policy/v1beta1/policy_client.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/rbac/v1/clusterrole.go ================================================ [File too large to display: 6.8 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/rbac/v1/clusterrolebinding.go ================================================ [File too large to display: 7.4 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/rbac/v1/doc.go ================================================ [File too large to display: 687 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/rbac/v1/generated_expansion.go ================================================ [File too large to display: 780 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/rbac/v1/rbac_client.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/rbac/v1/role.go ================================================ [File too large to display: 6.5 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/rbac/v1/rolebinding.go ================================================ [File too large to display: 7.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/rbac/v1alpha1/clusterrole.go ================================================ [File too large to display: 6.9 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/rbac/v1alpha1/clusterrolebinding.go ================================================ [File too large to display: 7.5 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/rbac/v1alpha1/doc.go ================================================ [File too large to display: 693 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/rbac/v1alpha1/generated_expansion.go ================================================ [File too large to display: 786 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/rbac/v1alpha1/rbac_client.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/rbac/v1alpha1/role.go ================================================ [File too large to display: 6.6 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/rbac/v1alpha1/rolebinding.go ================================================ [File too large to display: 7.2 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/rbac/v1beta1/clusterrole.go ================================================ [File too large to display: 6.9 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/rbac/v1beta1/clusterrolebinding.go ================================================ [File too large to display: 7.5 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/rbac/v1beta1/doc.go ================================================ [File too large to display: 692 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/rbac/v1beta1/generated_expansion.go ================================================ [File too large to display: 785 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/rbac/v1beta1/rbac_client.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/rbac/v1beta1/role.go ================================================ [File too large to display: 6.6 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/rbac/v1beta1/rolebinding.go ================================================ [File too large to display: 7.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/resource/v1alpha2/doc.go ================================================ [File too large to display: 693 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/resource/v1alpha2/generated_expansion.go ================================================ [File too large to display: 809 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/resource/v1alpha2/podschedulingcontext.go ================================================ [File too large to display: 9.9 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/resource/v1alpha2/resource_client.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/resource/v1alpha2/resourceclaim.go ================================================ [File too large to display: 9.2 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/resource/v1alpha2/resourceclaimtemplate.go ================================================ [File too large to display: 8.0 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/resource/v1alpha2/resourceclass.go ================================================ [File too large to display: 7.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/scheduling/v1/doc.go ================================================ [File too large to display: 687 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/scheduling/v1/generated_expansion.go ================================================ [File too large to display: 665 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/scheduling/v1/priorityclass.go ================================================ [File too large to display: 7.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/scheduling/v1/scheduling_client.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/scheduling/v1alpha1/doc.go ================================================ [File too large to display: 693 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/scheduling/v1alpha1/generated_expansion.go ================================================ [File too large to display: 671 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/scheduling/v1alpha1/priorityclass.go ================================================ [File too large to display: 7.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/scheduling/v1alpha1/scheduling_client.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/scheduling/v1beta1/doc.go ================================================ [File too large to display: 692 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/scheduling/v1beta1/generated_expansion.go ================================================ [File too large to display: 670 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/scheduling/v1beta1/priorityclass.go ================================================ [File too large to display: 7.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/scheduling/v1beta1/scheduling_client.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/storage/v1/csidriver.go ================================================ [File too large to display: 6.7 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/storage/v1/csinode.go ================================================ [File too large to display: 6.5 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/storage/v1/csistoragecapacity.go ================================================ [File too large to display: 7.7 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/storage/v1/doc.go ================================================ [File too large to display: 687 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/storage/v1/generated_expansion.go ================================================ [File too large to display: 826 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/storage/v1/storage_client.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/storage/v1/storageclass.go ================================================ [File too large to display: 7.0 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/storage/v1/volumeattachment.go ================================================ [File too large to display: 9.1 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/storage/v1alpha1/csistoragecapacity.go ================================================ [File too large to display: 7.8 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/storage/v1alpha1/doc.go ================================================ [File too large to display: 693 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/storage/v1alpha1/generated_expansion.go ================================================ [File too large to display: 720 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/storage/v1alpha1/storage_client.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/storage/v1alpha1/volumeattachment.go ================================================ [File too large to display: 9.2 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/storage/v1beta1/csidriver.go ================================================ [File too large to display: 6.7 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/storage/v1beta1/csinode.go ================================================ [File too large to display: 6.6 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/storage/v1beta1/csistoragecapacity.go ================================================ [File too large to display: 7.7 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/storage/v1beta1/doc.go ================================================ [File too large to display: 692 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/storage/v1beta1/generated_expansion.go ================================================ [File too large to display: 831 B] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/storage/v1beta1/storage_client.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/storage/v1beta1/storageclass.go ================================================ [File too large to display: 7.0 KB] ================================================ FILE: vendor/k8s.io/client-go/kubernetes/typed/storage/v1beta1/volumeattachment.go ================================================ [File too large to display: 9.2 KB] ================================================ FILE: vendor/k8s.io/client-go/openapi/OWNERS ================================================ [File too large to display: 75 B] ================================================ FILE: vendor/k8s.io/client-go/openapi/cached/client.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/k8s.io/client-go/openapi/cached/groupversion.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/k8s.io/client-go/openapi/client.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/client-go/openapi/groupversion.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/openapi3/root.go ================================================ [File too large to display: 5.7 KB] ================================================ FILE: vendor/k8s.io/client-go/pkg/apis/clientauthentication/OWNERS ================================================ [File too large to display: 211 B] ================================================ FILE: vendor/k8s.io/client-go/pkg/apis/clientauthentication/doc.go ================================================ [File too large to display: 732 B] ================================================ FILE: vendor/k8s.io/client-go/pkg/apis/clientauthentication/install/install.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/k8s.io/client-go/pkg/apis/clientauthentication/register.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/client-go/pkg/apis/clientauthentication/types.go ================================================ [File too large to display: 5.0 KB] ================================================ FILE: vendor/k8s.io/client-go/pkg/apis/clientauthentication/v1/doc.go ================================================ [File too large to display: 844 B] ================================================ FILE: vendor/k8s.io/client-go/pkg/apis/clientauthentication/v1/register.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/client-go/pkg/apis/clientauthentication/v1/types.go ================================================ [File too large to display: 5.5 KB] ================================================ FILE: vendor/k8s.io/client-go/pkg/apis/clientauthentication/v1/zz_generated.conversion.go ================================================ [File too large to display: 10.3 KB] ================================================ FILE: vendor/k8s.io/client-go/pkg/apis/clientauthentication/v1/zz_generated.deepcopy.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/k8s.io/client-go/pkg/apis/clientauthentication/v1/zz_generated.defaults.go ================================================ [File too large to display: 1007 B] ================================================ FILE: vendor/k8s.io/client-go/pkg/apis/clientauthentication/v1beta1/doc.go ================================================ [File too large to display: 854 B] ================================================ FILE: vendor/k8s.io/client-go/pkg/apis/clientauthentication/v1beta1/register.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/client-go/pkg/apis/clientauthentication/v1beta1/types.go ================================================ [File too large to display: 5.5 KB] ================================================ FILE: vendor/k8s.io/client-go/pkg/apis/clientauthentication/v1beta1/zz_generated.conversion.go ================================================ [File too large to display: 10.5 KB] ================================================ FILE: vendor/k8s.io/client-go/pkg/apis/clientauthentication/v1beta1/zz_generated.deepcopy.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/k8s.io/client-go/pkg/apis/clientauthentication/v1beta1/zz_generated.defaults.go ================================================ [File too large to display: 1012 B] ================================================ FILE: vendor/k8s.io/client-go/pkg/apis/clientauthentication/zz_generated.deepcopy.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/k8s.io/client-go/pkg/version/base.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/k8s.io/client-go/pkg/version/doc.go ================================================ [File too large to display: 755 B] ================================================ FILE: vendor/k8s.io/client-go/pkg/version/version.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/k8s.io/client-go/plugin/pkg/client/auth/exec/exec.go ================================================ [File too large to display: 16.2 KB] ================================================ FILE: vendor/k8s.io/client-go/plugin/pkg/client/auth/exec/metrics.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/k8s.io/client-go/rest/OWNERS ================================================ [File too large to display: 200 B] ================================================ FILE: vendor/k8s.io/client-go/rest/client.go ================================================ [File too large to display: 6.6 KB] ================================================ FILE: vendor/k8s.io/client-go/rest/config.go ================================================ [File too large to display: 22.3 KB] ================================================ FILE: vendor/k8s.io/client-go/rest/exec.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/k8s.io/client-go/rest/plugin.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/rest/request.go ================================================ [File too large to display: 43.9 KB] ================================================ FILE: vendor/k8s.io/client-go/rest/transport.go ================================================ [File too large to display: 4.7 KB] ================================================ FILE: vendor/k8s.io/client-go/rest/url_utils.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/k8s.io/client-go/rest/urlbackoff.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/k8s.io/client-go/rest/warnings.go ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/k8s.io/client-go/rest/watch/decoder.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/rest/watch/encoder.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/k8s.io/client-go/rest/with_retry.go ================================================ [File too large to display: 12.7 KB] ================================================ FILE: vendor/k8s.io/client-go/rest/zz_generated.deepcopy.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/client-go/restmapper/category_expansion.go ================================================ [File too large to display: 3.9 KB] ================================================ FILE: vendor/k8s.io/client-go/restmapper/discovery.go ================================================ [File too large to display: 10.4 KB] ================================================ FILE: vendor/k8s.io/client-go/restmapper/shortcut.go ================================================ [File too large to display: 7.0 KB] ================================================ FILE: vendor/k8s.io/client-go/third_party/forked/golang/LICENSE ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/client-go/third_party/forked/golang/PATENTS ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/k8s.io/client-go/third_party/forked/golang/template/exec.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/client-go/third_party/forked/golang/template/funcs.go ================================================ [File too large to display: 4.1 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/auth/OWNERS ================================================ [File too large to display: 170 B] ================================================ FILE: vendor/k8s.io/client-go/tools/auth/clientauth.go ================================================ [File too large to display: 3.8 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/clientcmd/api/doc.go ================================================ [File too large to display: 613 B] ================================================ FILE: vendor/k8s.io/client-go/tools/clientcmd/api/helpers.go ================================================ [File too large to display: 6.8 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/clientcmd/api/latest/latest.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/clientcmd/api/register.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/clientcmd/api/types.go ================================================ [File too large to display: 16.9 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/clientcmd/api/v1/conversion.go ================================================ [File too large to display: 5.4 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/clientcmd/api/v1/defaults.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/clientcmd/api/v1/doc.go ================================================ [File too large to display: 699 B] ================================================ FILE: vendor/k8s.io/client-go/tools/clientcmd/api/v1/register.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/clientcmd/api/v1/types.go ================================================ [File too large to display: 12.5 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/clientcmd/api/v1/zz_generated.conversion.go ================================================ [File too large to display: 20.2 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/clientcmd/api/v1/zz_generated.deepcopy.go ================================================ [File too large to display: 9.2 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/clientcmd/api/v1/zz_generated.defaults.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/clientcmd/api/zz_generated.deepcopy.go ================================================ [File too large to display: 8.2 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/clientcmd/auth_loaders.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/clientcmd/client_config.go ================================================ [File too large to display: 25.6 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/clientcmd/config.go ================================================ [File too large to display: 15.0 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/clientcmd/doc.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/clientcmd/flag.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/clientcmd/helpers.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/clientcmd/loader.go ================================================ [File too large to display: 20.5 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/clientcmd/merged_client_builder.go ================================================ [File too large to display: 5.8 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/clientcmd/overrides.go ================================================ [File too large to display: 12.5 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/clientcmd/validation.go ================================================ [File too large to display: 13.4 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/metrics/OWNERS ================================================ [File too large to display: 90 B] ================================================ FILE: vendor/k8s.io/client-go/tools/metrics/metrics.go ================================================ [File too large to display: 4.9 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/reference/ref.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/remotecommand/doc.go ================================================ [File too large to display: 801 B] ================================================ FILE: vendor/k8s.io/client-go/tools/remotecommand/errorstream.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/remotecommand/reader.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/remotecommand/remotecommand.go ================================================ [File too large to display: 5.8 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/remotecommand/resize.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/remotecommand/v1.go ================================================ [File too large to display: 4.4 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/remotecommand/v2.go ================================================ [File too large to display: 5.3 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/remotecommand/v3.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/client-go/tools/remotecommand/v4.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/client-go/transport/OWNERS ================================================ [File too large to display: 135 B] ================================================ FILE: vendor/k8s.io/client-go/transport/cache.go ================================================ [File too large to display: 4.9 KB] ================================================ FILE: vendor/k8s.io/client-go/transport/cache_go118.go ================================================ [File too large to display: 781 B] ================================================ FILE: vendor/k8s.io/client-go/transport/cert_rotation.go ================================================ [File too large to display: 4.3 KB] ================================================ FILE: vendor/k8s.io/client-go/transport/config.go ================================================ [File too large to display: 6.0 KB] ================================================ FILE: vendor/k8s.io/client-go/transport/round_trippers.go ================================================ [File too large to display: 20.7 KB] ================================================ FILE: vendor/k8s.io/client-go/transport/spdy/spdy.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/client-go/transport/token_source.go ================================================ [File too large to display: 5.1 KB] ================================================ FILE: vendor/k8s.io/client-go/transport/transport.go ================================================ [File too large to display: 9.9 KB] ================================================ FILE: vendor/k8s.io/client-go/util/cert/OWNERS ================================================ [File too large to display: 166 B] ================================================ FILE: vendor/k8s.io/client-go/util/cert/cert.go ================================================ [File too large to display: 6.8 KB] ================================================ FILE: vendor/k8s.io/client-go/util/cert/csr.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/util/cert/io.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/k8s.io/client-go/util/cert/pem.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/k8s.io/client-go/util/cert/server_inspection.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/k8s.io/client-go/util/connrotation/connrotation.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/k8s.io/client-go/util/exec/exec.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/client-go/util/flowcontrol/backoff.go ================================================ [File too large to display: 5.0 KB] ================================================ FILE: vendor/k8s.io/client-go/util/flowcontrol/throttle.go ================================================ [File too large to display: 5.4 KB] ================================================ FILE: vendor/k8s.io/client-go/util/homedir/homedir.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/k8s.io/client-go/util/jsonpath/doc.go ================================================ [File too large to display: 832 B] ================================================ FILE: vendor/k8s.io/client-go/util/jsonpath/jsonpath.go ================================================ [File too large to display: 14.8 KB] ================================================ FILE: vendor/k8s.io/client-go/util/jsonpath/node.go ================================================ [File too large to display: 5.1 KB] ================================================ FILE: vendor/k8s.io/client-go/util/jsonpath/parser.go ================================================ [File too large to display: 11.5 KB] ================================================ FILE: vendor/k8s.io/client-go/util/keyutil/OWNERS ================================================ [File too large to display: 115 B] ================================================ FILE: vendor/k8s.io/client-go/util/keyutil/key.go ================================================ [File too large to display: 9.6 KB] ================================================ FILE: vendor/k8s.io/client-go/util/workqueue/default_rate_limiters.go ================================================ [File too large to display: 6.3 KB] ================================================ FILE: vendor/k8s.io/client-go/util/workqueue/delaying_queue.go ================================================ [File too large to display: 9.5 KB] ================================================ FILE: vendor/k8s.io/client-go/util/workqueue/doc.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/k8s.io/client-go/util/workqueue/metrics.go ================================================ [File too large to display: 6.7 KB] ================================================ FILE: vendor/k8s.io/client-go/util/workqueue/parallelizer.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/client-go/util/workqueue/queue.go ================================================ [File too large to display: 8.1 KB] ================================================ FILE: vendor/k8s.io/client-go/util/workqueue/rate_limiting_queue.go ================================================ [File too large to display: 4.4 KB] ================================================ FILE: vendor/k8s.io/klog/v2/.gitignore ================================================ [File too large to display: 215 B] ================================================ FILE: vendor/k8s.io/klog/v2/CONTRIBUTING.md ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/k8s.io/klog/v2/LICENSE ================================================ [File too large to display: 10.0 KB] ================================================ FILE: vendor/k8s.io/klog/v2/OWNERS ================================================ [File too large to display: 217 B] ================================================ FILE: vendor/k8s.io/klog/v2/README.md ================================================ [File too large to display: 5.1 KB] ================================================ FILE: vendor/k8s.io/klog/v2/RELEASE.md ================================================ [File too large to display: 508 B] ================================================ FILE: vendor/k8s.io/klog/v2/SECURITY.md ================================================ [File too large to display: 1.0 KB] ================================================ FILE: vendor/k8s.io/klog/v2/SECURITY_CONTACTS ================================================ [File too large to display: 585 B] ================================================ FILE: vendor/k8s.io/klog/v2/code-of-conduct.md ================================================ [File too large to display: 148 B] ================================================ FILE: vendor/k8s.io/klog/v2/contextual.go ================================================ [File too large to display: 7.1 KB] ================================================ FILE: vendor/k8s.io/klog/v2/exit.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/klog/v2/imports.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/k8s.io/klog/v2/internal/buffer/buffer.go ================================================ [File too large to display: 4.9 KB] ================================================ FILE: vendor/k8s.io/klog/v2/internal/clock/README.md ================================================ [File too large to display: 243 B] ================================================ FILE: vendor/k8s.io/klog/v2/internal/clock/clock.go ================================================ [File too large to display: 4.9 KB] ================================================ FILE: vendor/k8s.io/klog/v2/internal/dbg/dbg.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/k8s.io/klog/v2/internal/serialize/keyvalues.go ================================================ [File too large to display: 10.0 KB] ================================================ FILE: vendor/k8s.io/klog/v2/internal/severity/severity.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/klog/v2/k8s_references.go ================================================ [File too large to display: 5.3 KB] ================================================ FILE: vendor/k8s.io/klog/v2/klog.go ================================================ [File too large to display: 56.7 KB] ================================================ FILE: vendor/k8s.io/klog/v2/klog_file.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/k8s.io/klog/v2/klog_file_others.go ================================================ [File too large to display: 241 B] ================================================ FILE: vendor/k8s.io/klog/v2/klog_file_windows.go ================================================ [File too large to display: 717 B] ================================================ FILE: vendor/k8s.io/klog/v2/klogr.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/LICENSE ================================================ [File too large to display: 11.1 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/builder3/util/util.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/cached/cache.go ================================================ [File too large to display: 8.5 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/common/common.go ================================================ [File too large to display: 12.4 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/common/doc.go ================================================ [File too large to display: 687 B] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/common/interfaces.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/handler3/handler.go ================================================ [File too large to display: 9.4 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/internal/flags.go ================================================ [File too large to display: 863 B] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/internal/serialization.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json/AUTHORS ================================================ [File too large to display: 174 B] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json/CONTRIBUTORS ================================================ [File too large to display: 171 B] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json/LICENSE ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json/README.md ================================================ [File too large to display: 19.5 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json/arshal.go ================================================ [File too large to display: 22.7 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json/arshal_any.go ================================================ [File too large to display: 5.9 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json/arshal_default.go ================================================ [File too large to display: 44.9 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json/arshal_funcs.go ================================================ [File too large to display: 13.9 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json/arshal_inlined.go ================================================ [File too large to display: 6.5 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json/arshal_methods.go ================================================ [File too large to display: 9.0 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json/arshal_time.go ================================================ [File too large to display: 8.0 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json/decode.go ================================================ [File too large to display: 50.8 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json/doc.go ================================================ [File too large to display: 9.8 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json/encode.go ================================================ [File too large to display: 36.6 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json/errors.go ================================================ [File too large to display: 4.5 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json/fields.go ================================================ [File too large to display: 18.7 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json/fold.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json/intern.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json/pools.go ================================================ [File too large to display: 5.9 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json/state.go ================================================ [File too large to display: 23.4 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json/token.go ================================================ [File too large to display: 14.8 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/internal/third_party/go-json-experiment/json/value.go ================================================ [File too large to display: 12.3 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/openapiconv/convert.go ================================================ [File too large to display: 9.6 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/schemaconv/openapi.go ================================================ [File too large to display: 7.2 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/schemaconv/proto_models.go ================================================ [File too large to display: 4.5 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/schemaconv/smd.go ================================================ [File too large to display: 8.9 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/schemamutation/walker.go ================================================ [File too large to display: 11.7 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/spec3/component.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/spec3/encoding.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/spec3/example.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/spec3/external_documentation.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/spec3/fuzz.go ================================================ [File too large to display: 5.6 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/spec3/header.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/spec3/media_type.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/spec3/operation.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/spec3/parameter.go ================================================ [File too large to display: 4.1 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/spec3/path.go ================================================ [File too large to display: 5.9 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/spec3/request_body.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/spec3/response.go ================================================ [File too large to display: 8.9 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/spec3/security_scheme.go ================================================ [File too large to display: 4.0 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/spec3/server.go ================================================ [File too large to display: 3.8 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/spec3/spec.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/util/proto/OWNERS ================================================ [File too large to display: 22 B] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/util/proto/doc.go ================================================ [File too large to display: 761 B] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/util/proto/document.go ================================================ [File too large to display: 10.7 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/util/proto/document_v3.go ================================================ [File too large to display: 8.4 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/util/proto/openapi.go ================================================ [File too large to display: 6.3 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/validation/spec/.gitignore ================================================ [File too large to display: 25 B] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/validation/spec/LICENSE ================================================ [File too large to display: 11.1 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/validation/spec/contact_info.go ================================================ [File too large to display: 874 B] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/validation/spec/external_docs.go ================================================ [File too large to display: 918 B] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/validation/spec/fuzz.go ================================================ [File too large to display: 10.6 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/validation/spec/gnostic.go ================================================ [File too large to display: 32.2 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/validation/spec/header.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/validation/spec/info.go ================================================ [File too large to display: 5.5 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/validation/spec/items.go ================================================ [File too large to display: 6.0 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/validation/spec/license.go ================================================ [File too large to display: 817 B] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/validation/spec/operation.go ================================================ [File too large to display: 5.0 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/validation/spec/parameter.go ================================================ [File too large to display: 6.4 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/validation/spec/path_item.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/validation/spec/paths.go ================================================ [File too large to display: 3.9 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/validation/spec/ref.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/validation/spec/response.go ================================================ [File too large to display: 3.6 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/validation/spec/responses.go ================================================ [File too large to display: 5.5 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/validation/spec/schema.go ================================================ [File too large to display: 19.6 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/validation/spec/security_scheme.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/validation/spec/swagger.go ================================================ [File too large to display: 12.0 KB] ================================================ FILE: vendor/k8s.io/kube-openapi/pkg/validation/spec/tag.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/k8s.io/utils/LICENSE ================================================ [File too large to display: 11.1 KB] ================================================ FILE: vendor/k8s.io/utils/clock/README.md ================================================ [File too large to display: 108 B] ================================================ FILE: vendor/k8s.io/utils/clock/clock.go ================================================ [File too large to display: 4.9 KB] ================================================ FILE: vendor/k8s.io/utils/clock/testing/fake_clock.go ================================================ [File too large to display: 8.7 KB] ================================================ FILE: vendor/k8s.io/utils/clock/testing/simple_interval_clock.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/k8s.io/utils/integer/integer.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/utils/internal/third_party/forked/golang/LICENSE ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/utils/internal/third_party/forked/golang/PATENTS ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/k8s.io/utils/internal/third_party/forked/golang/net/ip.go ================================================ [File too large to display: 5.2 KB] ================================================ FILE: vendor/k8s.io/utils/internal/third_party/forked/golang/net/parse.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/k8s.io/utils/net/ipfamily.go ================================================ [File too large to display: 5.1 KB] ================================================ FILE: vendor/k8s.io/utils/net/ipnet.go ================================================ [File too large to display: 5.0 KB] ================================================ FILE: vendor/k8s.io/utils/net/net.go ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/k8s.io/utils/net/parse.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/k8s.io/utils/net/port.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/k8s.io/utils/pointer/OWNERS ================================================ [File too large to display: 141 B] ================================================ FILE: vendor/k8s.io/utils/pointer/README.md ================================================ [File too large to display: 78 B] ================================================ FILE: vendor/k8s.io/utils/pointer/pointer.go ================================================ [File too large to display: 9.4 KB] ================================================ FILE: vendor/k8s.io/utils/strings/slices/slices.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/modules.txt ================================================ [File too large to display: 36.4 KB] ================================================ FILE: vendor/sigs.k8s.io/json/CONTRIBUTING.md ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/sigs.k8s.io/json/LICENSE ================================================ [File too large to display: 12.6 KB] ================================================ FILE: vendor/sigs.k8s.io/json/Makefile ================================================ [File too large to display: 710 B] ================================================ FILE: vendor/sigs.k8s.io/json/OWNERS ================================================ [File too large to display: 99 B] ================================================ FILE: vendor/sigs.k8s.io/json/README.md ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/sigs.k8s.io/json/SECURITY.md ================================================ [File too large to display: 1.0 KB] ================================================ FILE: vendor/sigs.k8s.io/json/SECURITY_CONTACTS ================================================ [File too large to display: 543 B] ================================================ FILE: vendor/sigs.k8s.io/json/code-of-conduct.md ================================================ [File too large to display: 148 B] ================================================ FILE: vendor/sigs.k8s.io/json/doc.go ================================================ [File too large to display: 613 B] ================================================ FILE: vendor/sigs.k8s.io/json/internal/golang/encoding/json/decode.go ================================================ [File too large to display: 39.4 KB] ================================================ FILE: vendor/sigs.k8s.io/json/internal/golang/encoding/json/encode.go ================================================ [File too large to display: 38.5 KB] ================================================ FILE: vendor/sigs.k8s.io/json/internal/golang/encoding/json/fold.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/sigs.k8s.io/json/internal/golang/encoding/json/fuzz.go ================================================ [File too large to display: 725 B] ================================================ FILE: vendor/sigs.k8s.io/json/internal/golang/encoding/json/indent.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/sigs.k8s.io/json/internal/golang/encoding/json/kubernetes_patch.go ================================================ [File too large to display: 4.5 KB] ================================================ FILE: vendor/sigs.k8s.io/json/internal/golang/encoding/json/scanner.go ================================================ [File too large to display: 16.1 KB] ================================================ FILE: vendor/sigs.k8s.io/json/internal/golang/encoding/json/stream.go ================================================ [File too large to display: 12.9 KB] ================================================ FILE: vendor/sigs.k8s.io/json/internal/golang/encoding/json/tables.go ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/sigs.k8s.io/json/internal/golang/encoding/json/tags.go ================================================ [File too large to display: 972 B] ================================================ FILE: vendor/sigs.k8s.io/json/json.go ================================================ [File too large to display: 6.0 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/LICENSE ================================================ [File too large to display: 11.1 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/annotations/annotations.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/annotations/doc.go ================================================ [File too large to display: 207 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/fieldspec/doc.go ================================================ [File too large to display: 196 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/fieldspec/fieldspec.go ================================================ [File too large to display: 5.5 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/filtersutil/setters.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/fsslice/doc.go ================================================ [File too large to display: 209 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/fsslice/fsslice.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/iampolicygenerator/doc.go ================================================ [File too large to display: 237 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/iampolicygenerator/iampolicygenerator.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/imagetag/doc.go ================================================ [File too large to display: 486 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/imagetag/imagetag.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/imagetag/legacy.go ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/imagetag/updater.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/labels/doc.go ================================================ [File too large to display: 192 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/labels/labels.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/nameref/doc.go ================================================ [File too large to display: 202 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/nameref/nameref.go ================================================ [File too large to display: 12.7 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/nameref/seqfilter.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/namespace/doc.go ================================================ [File too large to display: 320 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/namespace/namespace.go ================================================ [File too large to display: 7.4 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/patchjson6902/doc.go ================================================ [File too large to display: 208 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/patchjson6902/patchjson6902.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/patchstrategicmerge/doc.go ================================================ [File too large to display: 233 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/patchstrategicmerge/patchstrategicmerge.go ================================================ [File too large to display: 747 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/prefix/doc.go ================================================ [File too large to display: 191 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/prefix/prefix.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/refvar/doc.go ================================================ [File too large to display: 245 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/refvar/expand.go ================================================ [File too large to display: 4.6 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/refvar/refvar.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/replacement/doc.go ================================================ [File too large to display: 303 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/replacement/replacement.go ================================================ [File too large to display: 7.1 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/replicacount/doc.go ================================================ [File too large to display: 209 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/replicacount/replicacount.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/suffix/doc.go ================================================ [File too large to display: 191 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/suffix/suffix.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/filters/valueadd/valueadd.go ================================================ [File too large to display: 5.2 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/hasher/hasher.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/ifc/ifc.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/image/image.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/accumulator/loadconfigfromcrds.go ================================================ [File too large to display: 4.9 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/accumulator/namereferencetransformer.go ================================================ [File too large to display: 4.9 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/accumulator/refvartransformer.go ================================================ [File too large to display: 1.5 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/accumulator/resaccumulator.go ================================================ [File too large to display: 5.2 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/builtins/AnnotationsTransformer.go ================================================ [File too large to display: 1.0 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/builtins/ConfigMapGenerator.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/builtins/HashTransformer.go ================================================ [File too large to display: 869 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/builtins/HelmChartInflationGenerator.go ================================================ [File too large to display: 8.9 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/builtins/IAMPolicyGenerator.go ================================================ [File too large to display: 860 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/builtins/ImageTagTransformer.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/builtins/LabelTransformer.go ================================================ [File too large to display: 989 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/builtins/NamespaceTransformer.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/builtins/PatchJson6902Transformer.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/builtins/PatchStrategicMergeTransformer.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/builtins/PatchTransformer.go ================================================ [File too large to display: 4.1 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/builtins/PrefixTransformer.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/builtins/ReplacementTransformer.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/builtins/ReplicaCountTransformer.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/builtins/SecretGenerator.go ================================================ [File too large to display: 1.0 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/builtins/SortOrderTransformer.go ================================================ [File too large to display: 6.7 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/builtins/SuffixTransformer.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/builtins/ValueAddTransformer.go ================================================ [File too large to display: 4.0 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/builtins/doc.go ================================================ [File too large to display: 361 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/generators/configmap.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/generators/secret.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/generators/utils.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/git/cloner.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/git/gitrunner.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/git/repospec.go ================================================ [File too large to display: 13.8 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/kusterr/yamlformaterror.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig/doc.go ================================================ [File too large to display: 412 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig/loaddefaultconfig.go ================================================ [File too large to display: 948 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig/namebackreferences.go ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig/transformerconfig.go ================================================ [File too large to display: 5.4 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers/builtinplugintype_string.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers/builtins.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/plugins/execplugin/execplugin.go ================================================ [File too large to display: 4.6 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/plugins/fnplugin/fnplugin.go ================================================ [File too large to display: 5.2 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/plugins/loader/loader.go ================================================ [File too large to display: 10.5 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/plugins/utils/utils.go ================================================ [File too large to display: 6.0 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/target/errmissingkustomization.go ================================================ [File too large to display: 910 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/target/kusttarget.go ================================================ [File too large to display: 16.3 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/target/kusttarget_configplugin.go ================================================ [File too large to display: 13.3 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/target/multitransformer.go ================================================ [File too large to display: 1.0 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/utils/annotations.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/utils/errtimeout.go ================================================ [File too large to display: 529 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/utils/makeResIds.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/utils/stringslice.go ================================================ [File too large to display: 1.0 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/utils/timedcall.go ================================================ [File too large to display: 535 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/internal/validate/fieldvalidator.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/konfig/builtinpluginconsts/commonannotations.go ================================================ [File too large to display: 915 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/konfig/builtinpluginconsts/commonlabels.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/konfig/builtinpluginconsts/defaultconfig.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/konfig/builtinpluginconsts/doc.go ================================================ [File too large to display: 330 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/konfig/builtinpluginconsts/images.go ================================================ [File too large to display: 377 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/konfig/builtinpluginconsts/metadatalabels.go ================================================ [File too large to display: 964 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/konfig/builtinpluginconsts/nameprefix.go ================================================ [File too large to display: 183 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/konfig/builtinpluginconsts/namereference.go ================================================ [File too large to display: 14.3 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/konfig/builtinpluginconsts/namespace.go ================================================ [File too large to display: 437 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/konfig/builtinpluginconsts/namesuffix.go ================================================ [File too large to display: 183 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/konfig/builtinpluginconsts/replicas.go ================================================ [File too large to display: 391 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/konfig/builtinpluginconsts/templatelabels.go ================================================ [File too large to display: 190 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/konfig/builtinpluginconsts/varreference.go ================================================ [File too large to display: 4.8 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/konfig/doc.go ================================================ [File too large to display: 262 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/konfig/general.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/konfig/plugins.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/krusty/doc.go ================================================ [File too large to display: 390 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/krusty/kustomizer.go ================================================ [File too large to display: 5.1 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/krusty/options.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/kv/kv.go ================================================ [File too large to display: 5.0 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/loader/errors.go ================================================ [File too large to display: 250 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/loader/fileloader.go ================================================ [File too large to display: 9.7 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/loader/loader.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/loader/loadrestrictions.go ================================================ [File too large to display: 780 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/provenance/provenance.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/provider/depprovider.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/resmap/factory.go ================================================ [File too large to display: 3.8 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/resmap/resmap.go ================================================ [File too large to display: 11.0 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/resmap/reswrangler.go ================================================ [File too large to display: 18.7 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/resource/doc.go ================================================ [File too large to display: 168 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/resource/factory.go ================================================ [File too large to display: 7.9 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/resource/idset.go ================================================ [File too large to display: 528 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/resource/origin.go ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/resource/resource.go ================================================ [File too large to display: 14.4 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/builtinpluginloadingoptions_string.go ================================================ [File too large to display: 905 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/configmapargs.go ================================================ [File too large to display: 302 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/doc.go ================================================ [File too large to display: 394 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/erronlybuiltinpluginsallowed.go ================================================ [File too large to display: 622 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/errunabletofind.go ================================================ [File too large to display: 727 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/fieldspec.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/generationbehavior.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/generatorargs.go ================================================ [File too large to display: 1.0 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/generatoroptions.go ================================================ [File too large to display: 2.4 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/helmchartargs.go ================================================ [File too large to display: 7.6 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/iampolicygenerator.go ================================================ [File too large to display: 1.0 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/image.go ================================================ [File too large to display: 950 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/kustomization.go ================================================ [File too large to display: 13.6 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/kvpairsources.go ================================================ [File too large to display: 1.4 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/labels.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/loadrestrictions.go ================================================ [File too large to display: 610 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/loadrestrictions_string.go ================================================ [File too large to display: 819 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/objectmeta.go ================================================ [File too large to display: 591 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/pair.go ================================================ [File too large to display: 175 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/patch.go ================================================ [File too large to display: 1.0 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/patchstrategicmerge.go ================================================ [File too large to display: 341 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/pluginconfig.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/pluginrestrictions.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/pluginrestrictions_string.go ================================================ [File too large to display: 859 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/replacement.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/replacementfield.go ================================================ [File too large to display: 261 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/replica.go ================================================ [File too large to display: 619 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/secretargs.go ================================================ [File too large to display: 625 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/selector.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/sortoptions.go ================================================ [File too large to display: 1008 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/typemeta.go ================================================ [File too large to display: 391 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/api/types/var.go ================================================ [File too large to display: 5.5 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/LICENSE ================================================ [File too large to display: 11.1 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/comments/comments.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/errors/errors.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/ext/ext.go ================================================ [File too large to display: 273 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/fieldmeta/fieldmeta.go ================================================ [File too large to display: 7.1 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/filesys/confirmeddir.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/filesys/doc.go ================================================ [File too large to display: 253 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/filesys/file.go ================================================ [File too large to display: 238 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/filesys/fileinfo.go ================================================ [File too large to display: 862 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/filesys/fileondisk.go ================================================ [File too large to display: 715 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/filesys/filesystem.go ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/filesys/fsnode.go ================================================ [File too large to display: 15.4 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/filesys/fsondisk.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/filesys/fsondisk_unix.go ================================================ [File too large to display: 245 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/filesys/fsondisk_windows.go ================================================ [File too large to display: 323 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/filesys/util.go ================================================ [File too large to display: 4.3 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/container/container.go ================================================ [File too large to display: 6.4 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/exec/doc.go ================================================ [File too large to display: 154 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/exec/exec.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil/doc.go ================================================ [File too large to display: 180 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil/functiontypes.go ================================================ [File too large to display: 8.9 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil/runtimeutil.go ================================================ [File too large to display: 7.5 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil/types.go ================================================ [File too large to display: 160 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/starlark/context.go ================================================ [File too large to display: 1.6 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/starlark/doc.go ================================================ [File too large to display: 1.9 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/fn/runtime/starlark/starlark.go ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/go-yaml/yaml/LICENSE ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/go-yaml/yaml/NOTICE ================================================ [File too large to display: 560 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/go-yaml/yaml/README.md ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/go-yaml/yaml/apic.go ================================================ [File too large to display: 21.5 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/go-yaml/yaml/decode.go ================================================ [File too large to display: 24.4 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/go-yaml/yaml/emitterc.go ================================================ [File too large to display: 54.3 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/go-yaml/yaml/encode.go ================================================ [File too large to display: 14.4 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/go-yaml/yaml/parserc.go ================================================ [File too large to display: 39.9 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/go-yaml/yaml/readerc.go ================================================ [File too large to display: 13.8 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/go-yaml/yaml/resolve.go ================================================ [File too large to display: 8.3 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/go-yaml/yaml/scannerc.go ================================================ [File too large to display: 85.9 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/go-yaml/yaml/sorter.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/go-yaml/yaml/writerc.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/go-yaml/yaml/yaml.go ================================================ [File too large to display: 20.0 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/go-yaml/yaml/yamlh.go ================================================ [File too large to display: 28.5 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/go-yaml/yaml/yamlprivateh.go ================================================ [File too large to display: 6.0 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/qri-io/starlib/util/LICENSE ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/qri-io/starlib/util/doc.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/internal/forked/github.com/qri-io/starlib/util/util.go ================================================ [File too large to display: 6.6 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/kio/byteio_reader.go ================================================ [File too large to display: 10.6 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/kio/byteio_writer.go ================================================ [File too large to display: 6.2 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/kio/doc.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/kio/filters/filters.go ================================================ [File too large to display: 5.5 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/kio/filters/fmtr.go ================================================ [File too large to display: 8.8 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/kio/filters/grep.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/kio/filters/local.go ================================================ [File too large to display: 1.0 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/kio/filters/merge.go ================================================ [File too large to display: 2.2 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/kio/filters/merge3.go ================================================ [File too large to display: 9.0 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/kio/filters/modify.go ================================================ [File too large to display: 98 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/kio/filters/stripcomments.go ================================================ [File too large to display: 630 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/kio/ignorefilesmatcher.go ================================================ [File too large to display: 3.3 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/kio/kio.go ================================================ [File too large to display: 14.7 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/kio/kioutil/kioutil.go ================================================ [File too large to display: 11.2 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/kio/pkgio_reader.go ================================================ [File too large to display: 11.2 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/kio/pkgio_writer.go ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/kio/tree.go ================================================ [File too large to display: 13.7 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/openapi/Makefile ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/openapi/README.md ================================================ [File too large to display: 2.7 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/openapi/kubernetesapi/openapiinfo.go ================================================ [File too large to display: 421 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/openapi/kubernetesapi/v1_21_2/swagger.go ================================================ [File too large to display: 1.2 MB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/openapi/kubernetesapi/v1_21_2/swagger.pb ================================================ [File too large to display: 3.3 MB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/openapi/kustomizationapi/swagger.go ================================================ [File too large to display: 7.8 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/openapi/kustomizationapi/swagger.json ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/openapi/openapi.go ================================================ [File too large to display: 28.3 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/order/syncorder.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/resid/gvk.go ================================================ [File too large to display: 5.8 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/resid/resid.go ================================================ [File too large to display: 3.7 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/runfn/runfn.go ================================================ [File too large to display: 15.4 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/sets/string.go ================================================ [File too large to display: 1017 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/sets/stringlist.go ================================================ [File too large to display: 714 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/sliceutil/slice.go ================================================ [File too large to display: 501 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/utils/pathsplitter.go ================================================ [File too large to display: 2.0 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/alias.go ================================================ [File too large to display: 3.1 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/compatibility.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/const.go ================================================ [File too large to display: 724 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/datamap.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/doc.go ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/filters.go ================================================ [File too large to display: 4.0 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/fns.go ================================================ [File too large to display: 24.4 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/internal/k8sgen/pkg/labels/copied.deepcopy.go ================================================ [File too large to display: 1.3 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/internal/k8sgen/pkg/labels/labels.go ================================================ [File too large to display: 4.8 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/internal/k8sgen/pkg/labels/selector.go ================================================ [File too large to display: 28.1 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/internal/k8sgen/pkg/selection/operator.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/internal/k8sgen/pkg/util/errors/errors.go ================================================ [File too large to display: 6.5 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/internal/k8sgen/pkg/util/sets/empty.go ================================================ [File too large to display: 940 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/internal/k8sgen/pkg/util/sets/string.go ================================================ [File too large to display: 4.8 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/internal/k8sgen/pkg/util/validation/field/errors.go ================================================ [File too large to display: 9.9 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/internal/k8sgen/pkg/util/validation/field/path.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/internal/k8sgen/pkg/util/validation/validation.go ================================================ [File too large to display: 18.3 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/kfns.go ================================================ [File too large to display: 3.9 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/mapnode.go ================================================ [File too large to display: 774 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/match.go ================================================ [File too large to display: 8.2 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/merge2/merge2.go ================================================ [File too large to display: 4.8 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/merge2/smpdirective.go ================================================ [File too large to display: 3.0 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/merge2/smpdirective_string.go ================================================ [File too large to display: 735 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/merge3/merge3.go ================================================ [File too large to display: 1.1 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/merge3/visitor.go ================================================ [File too large to display: 4.9 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/order.go ================================================ [File too large to display: 3.5 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/rnode.go ================================================ [File too large to display: 36.6 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/schema/schema.go ================================================ [File too large to display: 970 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/types.go ================================================ [File too large to display: 7.0 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/util.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/associative_sequence.go ================================================ [File too large to display: 10.6 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/map.go ================================================ [File too large to display: 4.5 KB] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/nonassociative_sequence.go ================================================ [File too large to display: 334 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/scalar.go ================================================ [File too large to display: 283 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/visitor.go ================================================ [File too large to display: 679 B] ================================================ FILE: vendor/sigs.k8s.io/kustomize/kyaml/yaml/walk/walk.go ================================================ [File too large to display: 4.2 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/LICENSE ================================================ [File too large to display: 11.1 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/doc.go ================================================ [File too large to display: 866 B] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/element.go ================================================ [File too large to display: 7.9 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/fromvalue.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/managers.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/path.go ================================================ [File too large to display: 2.8 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/pathelementmap.go ================================================ [File too large to display: 2.6 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/serialize-pe.go ================================================ [File too large to display: 4.5 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/serialize.go ================================================ [File too large to display: 5.8 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/fieldpath/set.go ================================================ [File too large to display: 13.9 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/merge/conflict.go ================================================ [File too large to display: 2.9 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/merge/update.go ================================================ [File too large to display: 13.7 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/schema/doc.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/schema/elements.go ================================================ [File too large to display: 12.7 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/schema/equals.go ================================================ [File too large to display: 4.3 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/schema/schemaschema.go ================================================ [File too large to display: 3.4 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/typed/doc.go ================================================ [File too large to display: 661 B] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/typed/helpers.go ================================================ [File too large to display: 7.7 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/typed/merge.go ================================================ [File too large to display: 10.6 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/typed/parser.go ================================================ [File too large to display: 4.6 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/typed/reconcile_schema.go ================================================ [File too large to display: 8.3 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/typed/remove.go ================================================ [File too large to display: 4.8 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/typed/tofieldset.go ================================================ [File too large to display: 4.0 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/typed/typed.go ================================================ [File too large to display: 9.4 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/typed/union.go ================================================ [File too large to display: 6.3 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/typed/validate.go ================================================ [File too large to display: 5.2 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/value/allocator.go ================================================ [File too large to display: 6.4 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/value/doc.go ================================================ [File too large to display: 841 B] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/value/fields.go ================================================ [File too large to display: 2.1 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/value/jsontagutil.go ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/value/list.go ================================================ [File too large to display: 4.6 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/value/listreflect.go ================================================ [File too large to display: 2.3 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/value/listunstructured.go ================================================ [File too large to display: 1.7 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/value/map.go ================================================ [File too large to display: 8.7 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/value/mapreflect.go ================================================ [File too large to display: 5.3 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/value/mapunstructured.go ================================================ [File too large to display: 4.6 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/value/reflectcache.go ================================================ [File too large to display: 14.6 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/value/scalar.go ================================================ [File too large to display: 1.2 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/value/structreflect.go ================================================ [File too large to display: 6.7 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/value/value.go ================================================ [File too large to display: 8.7 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/value/valuereflect.go ================================================ [File too large to display: 7.6 KB] ================================================ FILE: vendor/sigs.k8s.io/structured-merge-diff/v4/value/valueunstructured.go ================================================ [File too large to display: 4.1 KB] ================================================ FILE: vendor/sigs.k8s.io/yaml/.gitignore ================================================ [File too large to display: 262 B] ================================================ FILE: vendor/sigs.k8s.io/yaml/.travis.yml ================================================ [File too large to display: 326 B] ================================================ FILE: vendor/sigs.k8s.io/yaml/CONTRIBUTING.md ================================================ [File too large to display: 1.8 KB] ================================================ FILE: vendor/sigs.k8s.io/yaml/LICENSE ================================================ [File too large to display: 2.5 KB] ================================================ FILE: vendor/sigs.k8s.io/yaml/OWNERS ================================================ [File too large to display: 336 B] ================================================ FILE: vendor/sigs.k8s.io/yaml/README.md ================================================ [File too large to display: 3.2 KB] ================================================ FILE: vendor/sigs.k8s.io/yaml/RELEASE.md ================================================ [File too large to display: 516 B] ================================================ FILE: vendor/sigs.k8s.io/yaml/SECURITY_CONTACTS ================================================ [File too large to display: 621 B] ================================================ FILE: vendor/sigs.k8s.io/yaml/code-of-conduct.md ================================================ [File too large to display: 148 B] ================================================ FILE: vendor/sigs.k8s.io/yaml/fields.go ================================================ [File too large to display: 12.4 KB] ================================================ FILE: vendor/sigs.k8s.io/yaml/yaml.go ================================================ [File too large to display: 11.7 KB] ================================================ FILE: vendor/sigs.k8s.io/yaml/yaml_go110.go ================================================ [File too large to display: 373 B]